diff --git a/configure.REMOVED.git-id b/configure.REMOVED.git-id index 121c0a2234..13e74fea8c 100644 --- a/configure.REMOVED.git-id +++ b/configure.REMOVED.git-id @@ -1 +1 @@ -13c4421b7a5ecebbb847a299f306f96912b3ce36 \ No newline at end of file +5b34281abf0849c98261c0cfdd0d23e326e8bcd9 \ No newline at end of file diff --git a/configure.ac.REMOVED.git-id b/configure.ac.REMOVED.git-id index 4c7aed2f8e..43281d14d4 100644 --- a/configure.ac.REMOVED.git-id +++ b/configure.ac.REMOVED.git-id @@ -1 +1 @@ -5deb9ecc7ea035f4dbe596933c91280f63a5abd8 \ No newline at end of file +f6abc4fd387f6974da69a91114d1ccdd791df5f0 \ No newline at end of file diff --git a/external/api-snapshot/profiles/monodroid/System.Data.cs.REMOVED.git-id b/external/api-snapshot/profiles/monodroid/System.Data.cs.REMOVED.git-id index ad558307a0..7de0209462 100644 --- a/external/api-snapshot/profiles/monodroid/System.Data.cs.REMOVED.git-id +++ b/external/api-snapshot/profiles/monodroid/System.Data.cs.REMOVED.git-id @@ -1 +1 @@ -16c694a0175ec7eb00563855963fdf1d5a0b987c \ No newline at end of file +b803f86ef29cfff6559c08f5186f8bdf441b8d4b \ No newline at end of file diff --git a/external/api-snapshot/profiles/monodroid/System.IO.Compression.cs b/external/api-snapshot/profiles/monodroid/System.IO.Compression.cs index a366f791f0..6d2b1792b0 100644 --- a/external/api-snapshot/profiles/monodroid/System.IO.Compression.cs +++ b/external/api-snapshot/profiles/monodroid/System.IO.Compression.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. [assembly:System.Reflection.AssemblyVersionAttribute("2.0.5.0")] +[assembly:System.CLSCompliantAttribute(true)] [assembly:System.Diagnostics.DebuggableAttribute((System.Diagnostics.DebuggableAttribute.DebuggingModes)(2))] [assembly:System.Reflection.AssemblyCompanyAttribute("Mono development team")] [assembly:System.Reflection.AssemblyCopyrightAttribute("(c) Various Mono authors")] @@ -39,6 +40,8 @@ namespace System.IO.Compression internal ZipArchiveEntry() { } public System.IO.Compression.ZipArchive Archive { get { throw null; } } public long CompressedLength { get { throw null; } } + [System.CLSCompliantAttribute(false)] + public uint Crc32 { get { throw null; } } public int ExternalAttributes { get { throw null; } set { } } public string FullName { get { throw null; } } public System.DateTimeOffset LastWriteTime { get { throw null; } set { } } diff --git a/external/api-snapshot/profiles/monodroid/System.Numerics.cs b/external/api-snapshot/profiles/monodroid/System.Numerics.cs index 9a5c30d4fd..d8e1f95bf7 100644 --- a/external/api-snapshot/profiles/monodroid/System.Numerics.cs +++ b/external/api-snapshot/profiles/monodroid/System.Numerics.cs @@ -22,6 +22,7 @@ [assembly:System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.RequestMinimum, SkipVerification=true)] namespace System.Numerics { + [System.Runtime.CompilerServices.IsReadOnlyAttribute] [System.SerializableAttribute] [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct BigInteger : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable @@ -32,7 +33,7 @@ namespace System.Numerics public BigInteger(double value) { throw null;} public BigInteger(int value) { throw null;} public BigInteger(long value) { throw null;} - public BigInteger(System.ReadOnlySpan value) { throw null;} + public BigInteger(System.ReadOnlySpan value, bool isUnsigned=false, bool isBigEndian=false) { throw null;} public BigInteger(float value) { throw null;} [System.CLSCompliantAttribute(false)] public BigInteger(uint value) { throw null;} @@ -61,7 +62,7 @@ namespace System.Numerics public override bool Equals(object obj) { throw null; } [System.CLSCompliantAttribute(false)] public bool Equals(ulong other) { throw null; } - public int GetByteCount() { throw null; } + public int GetByteCount(bool isUnsigned=false) { throw null; } public override int GetHashCode() { throw null; } public static System.Numerics.BigInteger GreatestCommonDivisor(System.Numerics.BigInteger left, System.Numerics.BigInteger right) { throw null; } public static double Log(System.Numerics.BigInteger value) { throw null; } @@ -168,14 +169,17 @@ namespace System.Numerics public static System.Numerics.BigInteger Remainder(System.Numerics.BigInteger dividend, System.Numerics.BigInteger divisor) { throw null; } public static System.Numerics.BigInteger Subtract(System.Numerics.BigInteger left, System.Numerics.BigInteger right) { throw null; } public byte[] ToByteArray() { throw null; } + public byte[] ToByteArray(bool isUnsigned=false, bool isBigEndian=false) { throw null; } public override string ToString() { throw null; } public string ToString(System.IFormatProvider provider) { throw null; } public string ToString(string format) { throw null; } public string ToString(string format, System.IFormatProvider provider) { throw null; } - public static bool TryParse(System.ReadOnlySpan value, out System.Numerics.BigInteger result, System.Globalization.NumberStyles style=(System.Globalization.NumberStyles)(7), System.IFormatProvider provider=null) { result = default(System.Numerics.BigInteger); throw null; } + public bool TryFormat(System.Span destination, out int charsWritten, System.ReadOnlySpan format=default(System.ReadOnlySpan), System.IFormatProvider provider=null) { charsWritten = default(int); throw null; } + public static bool TryParse(System.ReadOnlySpan value, System.Globalization.NumberStyles style, System.IFormatProvider provider, out System.Numerics.BigInteger result) { result = default(System.Numerics.BigInteger); throw null; } + public static bool TryParse(System.ReadOnlySpan value, out System.Numerics.BigInteger result) { result = default(System.Numerics.BigInteger); throw null; } public static bool TryParse(string value, System.Globalization.NumberStyles style, System.IFormatProvider provider, out System.Numerics.BigInteger result) { result = default(System.Numerics.BigInteger); throw null; } public static bool TryParse(string value, out System.Numerics.BigInteger result) { result = default(System.Numerics.BigInteger); throw null; } - public bool TryWriteBytes(System.Span destination, out int bytesWritten) { bytesWritten = default(int); throw null; } + public bool TryWriteBytes(System.Span destination, out int bytesWritten, bool isUnsigned=false, bool isBigEndian=false) { bytesWritten = default(int); throw null; } } [System.SerializableAttribute] [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] diff --git a/external/api-snapshot/profiles/monodroid/System.cs.REMOVED.git-id b/external/api-snapshot/profiles/monodroid/System.cs.REMOVED.git-id index eaa6b5c82d..ae21c21d0e 100644 --- a/external/api-snapshot/profiles/monodroid/System.cs.REMOVED.git-id +++ b/external/api-snapshot/profiles/monodroid/System.cs.REMOVED.git-id @@ -1 +1 @@ -b197c51abc3c082dbda5c8adc28f62bae9c30032 \ No newline at end of file +6ee3ca8968f7c2c5a13fde0d89329b4efbbd7f4f \ No newline at end of file diff --git a/external/api-snapshot/profiles/monodroid/mscorlib.cs.REMOVED.git-id b/external/api-snapshot/profiles/monodroid/mscorlib.cs.REMOVED.git-id index 5da68e0840..ae0fdf8021 100644 --- a/external/api-snapshot/profiles/monodroid/mscorlib.cs.REMOVED.git-id +++ b/external/api-snapshot/profiles/monodroid/mscorlib.cs.REMOVED.git-id @@ -1 +1 @@ -f3174d13fff775c60031f68c928469a31d188436 \ No newline at end of file +e2fe033997a386b136170ffee03d043df47c88df \ No newline at end of file diff --git a/external/api-snapshot/profiles/monotouch/System.Data.cs.REMOVED.git-id b/external/api-snapshot/profiles/monotouch/System.Data.cs.REMOVED.git-id index ad558307a0..7de0209462 100644 --- a/external/api-snapshot/profiles/monotouch/System.Data.cs.REMOVED.git-id +++ b/external/api-snapshot/profiles/monotouch/System.Data.cs.REMOVED.git-id @@ -1 +1 @@ -16c694a0175ec7eb00563855963fdf1d5a0b987c \ No newline at end of file +b803f86ef29cfff6559c08f5186f8bdf441b8d4b \ No newline at end of file diff --git a/external/api-snapshot/profiles/monotouch/System.IO.Compression.cs b/external/api-snapshot/profiles/monotouch/System.IO.Compression.cs index a366f791f0..6d2b1792b0 100644 --- a/external/api-snapshot/profiles/monotouch/System.IO.Compression.cs +++ b/external/api-snapshot/profiles/monotouch/System.IO.Compression.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. [assembly:System.Reflection.AssemblyVersionAttribute("2.0.5.0")] +[assembly:System.CLSCompliantAttribute(true)] [assembly:System.Diagnostics.DebuggableAttribute((System.Diagnostics.DebuggableAttribute.DebuggingModes)(2))] [assembly:System.Reflection.AssemblyCompanyAttribute("Mono development team")] [assembly:System.Reflection.AssemblyCopyrightAttribute("(c) Various Mono authors")] @@ -39,6 +40,8 @@ namespace System.IO.Compression internal ZipArchiveEntry() { } public System.IO.Compression.ZipArchive Archive { get { throw null; } } public long CompressedLength { get { throw null; } } + [System.CLSCompliantAttribute(false)] + public uint Crc32 { get { throw null; } } public int ExternalAttributes { get { throw null; } set { } } public string FullName { get { throw null; } } public System.DateTimeOffset LastWriteTime { get { throw null; } set { } } diff --git a/external/api-snapshot/profiles/monotouch/System.Numerics.cs b/external/api-snapshot/profiles/monotouch/System.Numerics.cs index 9a5c30d4fd..d8e1f95bf7 100644 --- a/external/api-snapshot/profiles/monotouch/System.Numerics.cs +++ b/external/api-snapshot/profiles/monotouch/System.Numerics.cs @@ -22,6 +22,7 @@ [assembly:System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.RequestMinimum, SkipVerification=true)] namespace System.Numerics { + [System.Runtime.CompilerServices.IsReadOnlyAttribute] [System.SerializableAttribute] [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct BigInteger : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable @@ -32,7 +33,7 @@ namespace System.Numerics public BigInteger(double value) { throw null;} public BigInteger(int value) { throw null;} public BigInteger(long value) { throw null;} - public BigInteger(System.ReadOnlySpan value) { throw null;} + public BigInteger(System.ReadOnlySpan value, bool isUnsigned=false, bool isBigEndian=false) { throw null;} public BigInteger(float value) { throw null;} [System.CLSCompliantAttribute(false)] public BigInteger(uint value) { throw null;} @@ -61,7 +62,7 @@ namespace System.Numerics public override bool Equals(object obj) { throw null; } [System.CLSCompliantAttribute(false)] public bool Equals(ulong other) { throw null; } - public int GetByteCount() { throw null; } + public int GetByteCount(bool isUnsigned=false) { throw null; } public override int GetHashCode() { throw null; } public static System.Numerics.BigInteger GreatestCommonDivisor(System.Numerics.BigInteger left, System.Numerics.BigInteger right) { throw null; } public static double Log(System.Numerics.BigInteger value) { throw null; } @@ -168,14 +169,17 @@ namespace System.Numerics public static System.Numerics.BigInteger Remainder(System.Numerics.BigInteger dividend, System.Numerics.BigInteger divisor) { throw null; } public static System.Numerics.BigInteger Subtract(System.Numerics.BigInteger left, System.Numerics.BigInteger right) { throw null; } public byte[] ToByteArray() { throw null; } + public byte[] ToByteArray(bool isUnsigned=false, bool isBigEndian=false) { throw null; } public override string ToString() { throw null; } public string ToString(System.IFormatProvider provider) { throw null; } public string ToString(string format) { throw null; } public string ToString(string format, System.IFormatProvider provider) { throw null; } - public static bool TryParse(System.ReadOnlySpan value, out System.Numerics.BigInteger result, System.Globalization.NumberStyles style=(System.Globalization.NumberStyles)(7), System.IFormatProvider provider=null) { result = default(System.Numerics.BigInteger); throw null; } + public bool TryFormat(System.Span destination, out int charsWritten, System.ReadOnlySpan format=default(System.ReadOnlySpan), System.IFormatProvider provider=null) { charsWritten = default(int); throw null; } + public static bool TryParse(System.ReadOnlySpan value, System.Globalization.NumberStyles style, System.IFormatProvider provider, out System.Numerics.BigInteger result) { result = default(System.Numerics.BigInteger); throw null; } + public static bool TryParse(System.ReadOnlySpan value, out System.Numerics.BigInteger result) { result = default(System.Numerics.BigInteger); throw null; } public static bool TryParse(string value, System.Globalization.NumberStyles style, System.IFormatProvider provider, out System.Numerics.BigInteger result) { result = default(System.Numerics.BigInteger); throw null; } public static bool TryParse(string value, out System.Numerics.BigInteger result) { result = default(System.Numerics.BigInteger); throw null; } - public bool TryWriteBytes(System.Span destination, out int bytesWritten) { bytesWritten = default(int); throw null; } + public bool TryWriteBytes(System.Span destination, out int bytesWritten, bool isUnsigned=false, bool isBigEndian=false) { bytesWritten = default(int); throw null; } } [System.SerializableAttribute] [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] diff --git a/external/api-snapshot/profiles/monotouch/System.cs.REMOVED.git-id b/external/api-snapshot/profiles/monotouch/System.cs.REMOVED.git-id index e20600a9e5..718de2e02d 100644 --- a/external/api-snapshot/profiles/monotouch/System.cs.REMOVED.git-id +++ b/external/api-snapshot/profiles/monotouch/System.cs.REMOVED.git-id @@ -1 +1 @@ -4752cd1a3db92b01de458920358a6c4513933104 \ No newline at end of file +0aabd5e9b1f4b5f354b3f274cad6983bfa8434e8 \ No newline at end of file diff --git a/external/api-snapshot/profiles/monotouch/mscorlib.cs.REMOVED.git-id b/external/api-snapshot/profiles/monotouch/mscorlib.cs.REMOVED.git-id index 5938d03e1c..f46e01aa27 100644 --- a/external/api-snapshot/profiles/monotouch/mscorlib.cs.REMOVED.git-id +++ b/external/api-snapshot/profiles/monotouch/mscorlib.cs.REMOVED.git-id @@ -1 +1 @@ -4106d7c02b3c5d2831e4f06e674c5197fba1d6d4 \ No newline at end of file +caea80eb9a8aeae8d816300ba49e3e58a189a287 \ No newline at end of file diff --git a/external/api-snapshot/profiles/net_4_x/System.Data.cs.REMOVED.git-id b/external/api-snapshot/profiles/net_4_x/System.Data.cs.REMOVED.git-id index f9b7f10a80..b51c311406 100644 --- a/external/api-snapshot/profiles/net_4_x/System.Data.cs.REMOVED.git-id +++ b/external/api-snapshot/profiles/net_4_x/System.Data.cs.REMOVED.git-id @@ -1 +1 @@ -c52ad21527ca17216e4412a711bd166d404dc32a \ No newline at end of file +1f8d4c968568f6efbbb436418f27bf2ed01bc649 \ No newline at end of file diff --git a/external/api-snapshot/profiles/net_4_x/System.Drawing.cs.REMOVED.git-id b/external/api-snapshot/profiles/net_4_x/System.Drawing.cs.REMOVED.git-id index 124e5bb9db..a0c5cce6c2 100644 --- a/external/api-snapshot/profiles/net_4_x/System.Drawing.cs.REMOVED.git-id +++ b/external/api-snapshot/profiles/net_4_x/System.Drawing.cs.REMOVED.git-id @@ -1 +1 @@ -1daaa964a2b17770072422022f356fc5dd99ef2a \ No newline at end of file +234aaf94d293014332715fe43d020e87b2c24a7c \ No newline at end of file diff --git a/external/api-snapshot/profiles/net_4_x/System.IO.Compression.cs b/external/api-snapshot/profiles/net_4_x/System.IO.Compression.cs index db98763e5b..57e0339514 100644 --- a/external/api-snapshot/profiles/net_4_x/System.IO.Compression.cs +++ b/external/api-snapshot/profiles/net_4_x/System.IO.Compression.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. [assembly:System.Reflection.AssemblyVersionAttribute("4.0.0.0")] +[assembly:System.CLSCompliantAttribute(true)] [assembly:System.Diagnostics.DebuggableAttribute((System.Diagnostics.DebuggableAttribute.DebuggingModes)(2))] [assembly:System.Reflection.AssemblyCompanyAttribute("Mono development team")] [assembly:System.Reflection.AssemblyCopyrightAttribute("(c) Various Mono authors")] @@ -39,6 +40,8 @@ namespace System.IO.Compression internal ZipArchiveEntry() { } public System.IO.Compression.ZipArchive Archive { get { throw null; } } public long CompressedLength { get { throw null; } } + [System.CLSCompliantAttribute(false)] + public uint Crc32 { get { throw null; } } public int ExternalAttributes { get { throw null; } set { } } public string FullName { get { throw null; } } public System.DateTimeOffset LastWriteTime { get { throw null; } set { } } diff --git a/external/api-snapshot/profiles/net_4_x/System.Numerics.cs b/external/api-snapshot/profiles/net_4_x/System.Numerics.cs index a6501e37e6..8619abb7bb 100644 --- a/external/api-snapshot/profiles/net_4_x/System.Numerics.cs +++ b/external/api-snapshot/profiles/net_4_x/System.Numerics.cs @@ -22,6 +22,7 @@ [assembly:System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.RequestMinimum, SkipVerification=true)] namespace System.Numerics { + [System.Runtime.CompilerServices.IsReadOnlyAttribute] [System.SerializableAttribute] [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct BigInteger : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable @@ -32,7 +33,7 @@ namespace System.Numerics public BigInteger(double value) { throw null;} public BigInteger(int value) { throw null;} public BigInteger(long value) { throw null;} - public BigInteger(System.ReadOnlySpan value) { throw null;} + public BigInteger(System.ReadOnlySpan value, bool isUnsigned=false, bool isBigEndian=false) { throw null;} public BigInteger(float value) { throw null;} [System.CLSCompliantAttribute(false)] public BigInteger(uint value) { throw null;} @@ -61,7 +62,7 @@ namespace System.Numerics public override bool Equals(object obj) { throw null; } [System.CLSCompliantAttribute(false)] public bool Equals(ulong other) { throw null; } - public int GetByteCount() { throw null; } + public int GetByteCount(bool isUnsigned=false) { throw null; } public override int GetHashCode() { throw null; } public static System.Numerics.BigInteger GreatestCommonDivisor(System.Numerics.BigInteger left, System.Numerics.BigInteger right) { throw null; } public static double Log(System.Numerics.BigInteger value) { throw null; } @@ -168,14 +169,17 @@ namespace System.Numerics public static System.Numerics.BigInteger Remainder(System.Numerics.BigInteger dividend, System.Numerics.BigInteger divisor) { throw null; } public static System.Numerics.BigInteger Subtract(System.Numerics.BigInteger left, System.Numerics.BigInteger right) { throw null; } public byte[] ToByteArray() { throw null; } + public byte[] ToByteArray(bool isUnsigned=false, bool isBigEndian=false) { throw null; } public override string ToString() { throw null; } public string ToString(System.IFormatProvider provider) { throw null; } public string ToString(string format) { throw null; } public string ToString(string format, System.IFormatProvider provider) { throw null; } - public static bool TryParse(System.ReadOnlySpan value, out System.Numerics.BigInteger result, System.Globalization.NumberStyles style=(System.Globalization.NumberStyles)(7), System.IFormatProvider provider=null) { result = default(System.Numerics.BigInteger); throw null; } + public bool TryFormat(System.Span destination, out int charsWritten, System.ReadOnlySpan format=default(System.ReadOnlySpan), System.IFormatProvider provider=null) { charsWritten = default(int); throw null; } + public static bool TryParse(System.ReadOnlySpan value, System.Globalization.NumberStyles style, System.IFormatProvider provider, out System.Numerics.BigInteger result) { result = default(System.Numerics.BigInteger); throw null; } + public static bool TryParse(System.ReadOnlySpan value, out System.Numerics.BigInteger result) { result = default(System.Numerics.BigInteger); throw null; } public static bool TryParse(string value, System.Globalization.NumberStyles style, System.IFormatProvider provider, out System.Numerics.BigInteger result) { result = default(System.Numerics.BigInteger); throw null; } public static bool TryParse(string value, out System.Numerics.BigInteger result) { result = default(System.Numerics.BigInteger); throw null; } - public bool TryWriteBytes(System.Span destination, out int bytesWritten) { bytesWritten = default(int); throw null; } + public bool TryWriteBytes(System.Span destination, out int bytesWritten, bool isUnsigned=false, bool isBigEndian=false) { bytesWritten = default(int); throw null; } } [System.SerializableAttribute] [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] diff --git a/external/api-snapshot/profiles/net_4_x/System.Security.cs b/external/api-snapshot/profiles/net_4_x/System.Security.cs index b3844fb730..ddf912d812 100644 --- a/external/api-snapshot/profiles/net_4_x/System.Security.cs +++ b/external/api-snapshot/profiles/net_4_x/System.Security.cs @@ -474,6 +474,14 @@ namespace System.Security.Cryptography.Xml public override System.Xml.XmlElement GetXml() { throw null; } public override void LoadXml(System.Xml.XmlElement value) { } } + [System.SerializableAttribute] + public partial class CryptoSignedXmlRecursionException : System.Xml.XmlException + { + public CryptoSignedXmlRecursionException() { } + protected CryptoSignedXmlRecursionException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + public CryptoSignedXmlRecursionException(string message) { } + public CryptoSignedXmlRecursionException(string message, System.Exception inner) { } + } public partial class DataObject { public DataObject() { } diff --git a/external/api-snapshot/profiles/net_4_x/System.cs.REMOVED.git-id b/external/api-snapshot/profiles/net_4_x/System.cs.REMOVED.git-id index 9cd13c854c..7b895cf724 100644 --- a/external/api-snapshot/profiles/net_4_x/System.cs.REMOVED.git-id +++ b/external/api-snapshot/profiles/net_4_x/System.cs.REMOVED.git-id @@ -1 +1 @@ -59368e66d91c68d9e8972da9211f67ce3484cfa8 \ No newline at end of file +f41727750b395e4019243785bcefced55377f2f3 \ No newline at end of file diff --git a/external/api-snapshot/profiles/net_4_x/mscorlib.cs.REMOVED.git-id b/external/api-snapshot/profiles/net_4_x/mscorlib.cs.REMOVED.git-id index 58f2b20662..61748bdbab 100644 --- a/external/api-snapshot/profiles/net_4_x/mscorlib.cs.REMOVED.git-id +++ b/external/api-snapshot/profiles/net_4_x/mscorlib.cs.REMOVED.git-id @@ -1 +1 @@ -44f15a763ed2458ee79e0b610407d136b27f1825 \ No newline at end of file +9a68bbd21ee9196ff71f50bc108c1324eae4f4f9 \ No newline at end of file diff --git a/external/corefx/BuildToolsVersion.txt b/external/corefx/BuildToolsVersion.txt index aba00a2c97..889d549558 100644 --- a/external/corefx/BuildToolsVersion.txt +++ b/external/corefx/BuildToolsVersion.txt @@ -1 +1 @@ -2.0.0-prerelease-02118-01 +2.1.0-prerelease-02419-02 diff --git a/external/corefx/CODE_OF_CONDUCT.md b/external/corefx/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..0fd65e978f --- /dev/null +++ b/external/corefx/CODE_OF_CONDUCT.md @@ -0,0 +1,5 @@ +# Code of Conduct + +This project has adopted the code of conduct defined by the Contributor Covenant +to clarify expected behavior in our community. +For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct). diff --git a/external/corefx/Documentation/api-guidelines/README.md b/external/corefx/Documentation/api-guidelines/README.md new file mode 100644 index 0000000000..ffb84286fb --- /dev/null +++ b/external/corefx/Documentation/api-guidelines/README.md @@ -0,0 +1,13 @@ +# API Design Guidelines + +The guidelines in this folder represent work in progress API design guidelines. +The official guidelines can be found in the [documentation][docs] and as an +actual [book]. + +## Process + +To submit new proposals for design guidelines, simply create a PR adding or +modifying an existing file. + +[docs]: https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/ +[book]: https://amazon.com/dp/0321545613 diff --git a/external/corefx/Documentation/api-guidelines/System.Memory.md b/external/corefx/Documentation/api-guidelines/System.Memory.md new file mode 100644 index 0000000000..894b8ca77d --- /dev/null +++ b/external/corefx/Documentation/api-guidelines/System.Memory.md @@ -0,0 +1,54 @@ +# System.Memory Design Guidelines + +`System.Memory` is a collection of types and features that make working with +buffers and raw memory more efficient while remaining type safe. The feature +specs can be found here: + +* [`Span`](https://github.com/dotnet/corefxlab/blob/master/docs/specs/span.md) +* [`Memory`](https://github.com/dotnet/corefxlab/blob/master/docs/specs/memory.md) + +## Overview + +* `ReadOnlySpan` is effectively the universal receiver, in that `T[]`, `T*`, + `Memory`, `ReadOnlyMemory`, `Span`, `ArraySegment` can all be + converted to it. So if you can declare your API to accept a `ReadOnlySpan` + and behave efficiently, that's best, as any of these inputs can be used with + your method. +* Similarly for `Span`, if you need write access in the implementation. +* It allows building safe public APIs that can operate on unmanaged memory + without forcing all consumers to use pointers (and thus becoming unsafe). The + implementation can still extract a raw pointer, therefore getting equivalent + performance if necessary. +* It's generally best for a synchronous method to accept `Span` or + `ReadOnlySpan`. However, since `ReadOnlySpan`/`Span` are stack-only + [1], this may be too limiting for the implementation. In particular, if the + implementation needs to be able to store the argument for later usage, such as + with an asynchronous method or an iterator, `ReadOnlySpan`/`Span` is + inappropriate. `ReadOnlyMemory`/`Memory` should be used in such + situations. + + +[1] *stack-only* isn't the best way to put it. Strictly speaking, these types + are called `ref`-like types. These types must be structs, cannot be fields + in classes, cannot be boxed, and cannot be used to instantiate generic + types. Value types containing fields of `ref`-like types must themselves be + `ref`-like types. + +## Guidance + +* **DO NOT** use pointers for methods operating on buffers. Instead, use + appropriate type from below. In performance critical code where bounds + checking is unacceptable, the method's implementation can still pin the span + and get the raw pointer if necessary. The key is that you don't spread the + pointer through the public API. + - Synchronous, read-only access needed: `ReadOnlySpan` + - Synchronous, writable access needed: `Span` + - Asynchronous, read-only access needed: `ReadOnlyMemory` + - Asynchronous, writable access needed: `Memory` +* **CONSIDER** using `stackalloc` with `Span` when you need small temporary + storage but you need to avoid allocations and associated life-time management. +* **AVOID** providing overloads for both `ReadOnlySpan` and `Span` as `Span` + can be implicitly converted to `ReadOnlySpan`. +* **AVOID** providing overloads for both `ReadOnlySpan`/`Span` as well as + pointers and arrays as those can be implicitly converted to + `ReadOnlySpan`/`Span`. \ No newline at end of file diff --git a/external/corefx/Documentation/architecture/globalization-invariant-mode.md b/external/corefx/Documentation/architecture/globalization-invariant-mode.md index d81acf066d..6d568ef1cb 100644 --- a/external/corefx/Documentation/architecture/globalization-invariant-mode.md +++ b/external/corefx/Documentation/architecture/globalization-invariant-mode.md @@ -98,18 +98,32 @@ When running on Linux, ICU is used to get the time zone display name. In invaria ## Enabling the invariant mode -Applications can enable the invariant mode by setting the config switch System.Globalization.Invariant to true, in the `runtimeconfig.json` file, as you can see in the following example: +Applications can enable the invariant mode by either of the following: -```json -{ - "runtimeOptions": { - "configProperties": { - "System.Globalization.Invariant": true - }, +1. in project file: + + ```xml + + + + ``` + +2. in `runtimeconfig.json` file: + + ```json + { + "runtimeOptions": { + "configProperties": { + "System.Globalization.Invariant": true + } + } } -} -``` - + ``` + +3. setting environment variable value `DOTNET_SYSTEM_GLOBALIZATION_INVARIANT` to `true` or `1`. + +Note: value set in project file or `runtimeconfig.json` has higher priority than the environment variable. + ## APP behavior with and without the invariant config switch - If the invariant config switch is not set or it is set false diff --git a/external/corefx/Documentation/building/advanced-inner-loop-testing.md b/external/corefx/Documentation/building/advanced-inner-loop-testing.md new file mode 100644 index 0000000000..a503f1e135 --- /dev/null +++ b/external/corefx/Documentation/building/advanced-inner-loop-testing.md @@ -0,0 +1,68 @@ +# Advanced scenario - Build and run application code with csc/vbc and CoreRun + + __Don't consider using this tutorial for anything else than inner-loop testing of corefx/coreclr binaries. Prefer using the official .NET Core SDK: https://docs.microsoft.com/en-us/dotnet/core/tools/?tabs=netcore2x__ + +This tutorial describes how to build and run application code that targets self-compiled .NET Core binaries without using Visual Studio, the .NET Core SDK Host (`dotnet.exe`) or a project file (e.g. `csproj`). Follow these steps to quickly validate changes you've made in the product e.g. by running benchmarks or tests on it. + +If you are on Windows you currently need to use the `Developer Command Prompt for VS 2017` to build corefx/coreclr! For the sake of completeness, we have placed our repositories under `d:\git\`. + +## Compile corefx with self-compiled coreclr binaries +If you've made changes to coreclr make sure to also build it and pass its binaries to corefx. +``` +coreclr\build -release +corefx\build -release -- /p:CoreCLROverridePath=d:\git\coreclr\bin\Product\Windows_NT.x64.Release\ +``` + +## Compile corefx with pre-compiled coreclr binaries +If you haven't made any changes to coreclr you're fine with just building corefx. This automatically picks pre-compiled coreclr binaries from MyGet. +``` +corefx\build -release +``` + +## Create and prepare your application +We will build a sample application which outputs `Hello World!` to the console. + +1. Create an application directory (in our example under `d:\git\`): +``` +mkdir core-demo +cd core-demo +``` +2. Save the following C# code to a file called `Program.cs` into your application folder: +```csharp +using System; + +public class Program +{ + public static void Main() + { + Console.WriteLine("Hello World!"); + } +} +``` + +3. Copy the just built corefx assemblies into your application directory. When using Visual Studio or the .NET Core SDK Host (`dotnet.exe`) you usually compile against *reference assemblies*. For simplicity we compile against the same assembly set that we use during run time. +``` +xcopy ..\corefx\bin\testhost\netcoreapp-Windows_NT-Release-x64\shared\Microsoft.NETCore.App\9.9.9 runtime /e /i /y /s +``` + +You don't need all the assemblies that are built by corefx but copying the entire directory makes it easier if you want to reference additional ones. At a minimum, this app will need the following assemblies to run: + +- CoreClr assemblies: `clrjit.dll`, `CoreRun.exe`, `coreclr.dll`, `System.Private.CoreLib.dll`. For more information about the CoreClr parts, visit [Using your build](https://github.com/dotnet/coreclr/blob/master/Documentation/workflow/UsingYourBuild.md) +- CoreFx assemblies: `System.Runtime.dll`, `System.Runtime.Extensions.dll`, `System.Runtime.InteropServices.dll`, `System.Text.Encoding.Extensions.dll`, `System.Threading.dll` + +## Compile your application +Use the C# Compiler (`csc`) to compile your C# code (`Program.cs`) against the copied assemblies. For our Hello World example we need to compile our application code against `System.Private.Corelib.dll`, `System.Runtime.dll`, `System.Runtime.Extensions.dll` and `System.Console.dll`. As described above these assemblies have dependencies on two other assemblies: `System.Text.Encoding.Extensions.dll` and `System.Threading.dll`. +``` +.\runtime\corerun ..\corefx\tools\csc.dll /noconfig /r:runtime\System.Private.Corelib.dll /r:runtime\System.Runtime.dll /r:runtime\System.Runtime.Extensions.dll /r:runtime\System.Console.dll /out:runtime\Program.dll Program.cs +``` + +If you want to compile Visual Basic code simply replace `csc.dll` with `vbc.dll`. + +## Run your application +`Corerun.exe` is part of the coreclr binaries and is best described as the host of your .NET Core application. Find more information at [Using CoreRun](https://github.com/dotnet/coreclr/blob/master/Documentation/workflow/UsingCoreRun.md). +``` +cd runtime +.\corerun Program.dll +``` + +> Hello World! diff --git a/external/corefx/Documentation/building/code-coverage.md b/external/corefx/Documentation/building/code-coverage.md index 73e5f6a61c..3f6e50edcf 100644 --- a/external/corefx/Documentation/building/code-coverage.md +++ b/external/corefx/Documentation/building/code-coverage.md @@ -78,4 +78,7 @@ Some of the libraries for which contracts and tests live in the corefx repo are 1. Follow the steps outlined at [Testing with Private CoreClr Bits](https://github.com/dotnet/corefx/blob/master/Documentation/project-docs/developer-guide.md#testing-with-private-coreclr-bits). Make sure to include the optional steps listed as being required for code coverage. 2. Add /p:CodeCoverageAssemblies="System.Private.CoreLib" to the previously discussed msbuild command, e.g. msbuild /t:BuildAndTest /p:Coverage=true /p:CodeCoverageAssemblies="System.Private.CoreLib" +Note that you will also want to copy the System.Private.CoreLib.pdb along with the System.Private.CoreLib.dll. As of 10/2017 this PDB must be a windows PDB (Hopefully by early 2018 OpenCOver will directly support portable PDBs. +You can determine if it is a windows PDB by doing 'more System.Private.CoreLib.pdb. If it begins with 'Microsoft C/C++ MSF 7.00' it is a windows PDB) If you need a windows PDB the Pdb2Pdb tool will convert (or you can do a msbuild /t:rebuild /p:DebugType=full in the src\mscorlib) + The resulting code coverage report should now also include details for System.Private.CoreLib. diff --git a/external/corefx/Documentation/building/unix-instructions.md b/external/corefx/Documentation/building/unix-instructions.md index 813576ddf2..83e4befec1 100644 --- a/external/corefx/Documentation/building/unix-instructions.md +++ b/external/corefx/Documentation/building/unix-instructions.md @@ -19,6 +19,10 @@ For more information about the different options when building, run `build.sh -? ### Linux +First, the package lists might need to be updated + +`sudo apt-get update` + #### Native build For Ubuntu 14.04, the following packages should be installed to build the native diff --git a/external/corefx/Documentation/coding-guidelines/project-guidelines.md b/external/corefx/Documentation/coding-guidelines/project-guidelines.md index c3aba1d2c0..446c015de4 100644 --- a/external/corefx/Documentation/coding-guidelines/project-guidelines.md +++ b/external/corefx/Documentation/coding-guidelines/project-guidelines.md @@ -88,6 +88,22 @@ All supported targets with unique windows/unix build for netcoreapp: ``` +### Placeholder build configurations +Placeholder build configurations can be added to the `` property to indicate the build system that the specific project is inbox in that framework and that build configuration needs to be ignored. + +Placeholder build configurations start with _ prefix. + +Example: +When we have a project that has a `netstandard` build configuration that means that this project is compatible with any build configuration. So if we do a vertical build for `netfx` this project will be built as part of the vertical because `netfx` is compatible with `netstandard`. This means that in the runtime and testhost binaries the netstandard implementation will be included, and we will test against those assets instead of testing against the framework inbox asset. In order to tell the build system to not include this project as part of the `netfx` vertical we need to add a placeholder configuration: +``` + + + netstandard; + _netfx; + + +``` + ## Options for building A full or individual project build is centered around BuildConfiguration and will be setup in one of the following ways: diff --git a/external/corefx/Documentation/debugging/unix-instructions.md b/external/corefx/Documentation/debugging/unix-instructions.md index b088094881..3bb0f6f810 100644 --- a/external/corefx/Documentation/debugging/unix-instructions.md +++ b/external/corefx/Documentation/debugging/unix-instructions.md @@ -6,40 +6,61 @@ CoreFX can be debugged on unix using both lldb and visual studio code ## Using lldb and SOS - Run the test using msbuild at least once with `/t:BuildAndTest`. -- Install version 3.9 of lldb and launch lldb with dotnet as the process and arguments matching the arguments used when running the test through msbuild. +- [Install version 3.9 of lldb](https://github.com/dotnet/coreclr/blob/master/Documentation/building/debugging-instructions.md#debugging-core-dumps-with-lldb) and launch lldb with dotnet as the process and arguments matching the arguments used when running the test through msbuild. - Load the sos plugin using `plugin load libsosplugin.so`. - Type `soshelp` to get help. You can now use all sos commands like `bpmd`. +You may need to supply a path to load SOS. It can be found next to libcoreclr.so. For example: +``` +(lldb) plugin load libsosplugin.so +error: no such file +(lldb) image list libcoreclr.so +[ 0] ..... /home/dan/dotnet/shared/Microsoft.NETCoreApp/2.0.4/libcoreclr.so +(lldb) plugin load /home/dan/dotnet/shared/Microsoft.NETCoreApp/2.0.4/libcoreclr.so +``` + ## Debugging core dumps with lldb It is also possible to debug .NET Core crash dumps using lldb and SOS. In order to do this, you need all of the following: -- A machine whose environment matches the one used to produce the crash dumps. For crash dumps occurring on CI machines, you can either - - Log onto a matching machine in the pool. - - Create a new VM from the matching image. +- You will find the dump url to download the crash dump file in the test logs, something similar to: +
+2017-10-10 21:17:48,020: INFO: proc(54): run_and_log_output: Output: dumplingid:  eefcb1cc36977ccf86f457ee28a33a7b4cc24e13
+2017-10-10 21:17:48,020: INFO: proc(54): run_and_log_output: Output: https://dumpling.azurewebsites.net/api/dumplings/archived/eefcb1cc36977ccf86f457ee28a33a7b4cc24e13
+
- The crash dump file. We have a service called "Dumpling" which collects, uploads, and archives crash dump files during all of our CI jobs and official builds. -- Matching runtime bits from the crash. To get these, you should either: +- On Linux, there is an utility called `createdump` (see [doc](https://github.com/dotnet/coreclr/blob/master/Documentation/botr/xplat-minidump-generation.md "doc")) that can be setup to generate core dumps when a managed app throws an unhandled exception or faults. +- Matching coreclr/corefx runtime bits from the crash. To get these, you should either: - Download the matching Jenkins archive onto your repro machine. - - Check out the corefx repository at the appropriate commit and re-build the necessary portions. -- lldb version 3.9. -- libsosplugin.so built against a matching version of lldb. The official libsosplugin.so is now built against 3.9. -- Symbols for libcoreclr.so. libcoreclr.so.dbg should be copied to your "runtime" folder. To get this file, you can: - - Build coreclr at the matching commit. In order to determine which commit was used to build a version of libcoreclr.so, run the following command: - `strings libcoreclr.so | grep "@(#)"` - - You can also download the matching "symbols" nuget package from myget.org. You want the same package version that is used to build corefx. There is a "Download Symbols" button in the myget UI for this purpose. + - Check out the coreclr and corefx repositories at the appropriate commit and re-build the necessary portions. + - You can also download the matching "symbols" nuget package from myget.org. There is a "Download Symbols" button in the myget UI for this purpose. +- lldb version 3.9. The SOS plugin (i.e. libsosplugin.so) provided is now built for lldb 3.9. In order to install lldb 3.9 just run the following commands: +``` +~$ echo "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.9 main" | sudo tee /etc/apt/sources.list.d/llvm.list +~$ wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - +~$ sudo apt-get update +~$ sudo apt-get install lldb-3.9 +``` Once you have everything listed above, you are ready to start debugging. You need to specify an extra parameter to lldb in order for it to correctly resolve the symbols for libcoreclr.so. Use a command like this: ``` -lldb -O "settings set target.exec-search-paths " --core +lldb-3.9 -O "settings set target.exec-search-paths " -o "plugin load " --core ``` - ``: The path containing libcoreclr.so.dbg, as well as the rest of the runtime and framework assemblies. - ``: The path to the core dump you are attempting to debug. -- ``: The path to the dotnet executable, potentially in the `` folder. +- ``: The path to the dotnet or corerun executable, potentially in the `` folder. +- ``: The path to libsosplugin.so, should be in the `` folder. lldb should start debugging successfully at this point. You should see stacktraces with resolved symbols for libcoreclr.so. At this point, you can run `plugin load `, and begin using SOS commands, as above. +##### Example + +``` +lldb-3.9 -O "settings set target.exec-search-paths /home/parallels/Downloads/System.Drawing.Common.Tests/home/helixbot/dotnetbuild/work/2a74cf82-3018-4e08-9e9a-744bb492869e/Payload/shared/Microsoft.NETCore.App/9.9.9/" -o "plugin load /home/parallels/Downloads/System.Drawing.Common.Tests/home/helixbot/dotnetbuild/work/2a74cf82-3018-4e08-9e9a-744bb492869e/Payload/shared/Microsoft.NETCore.App/9.9.9/libsosplugin.so" --core /home/parallels/Downloads/System.Drawing.Common.Tests/home/helixbot/dotnetbuild/work/2a74cf82-3018-4e08-9e9a-744bb492869e/Work/f6414a62-9b41-4144-baed-756321e3e075/Unzip/core /home/parallels/Downloads/System.Drawing.Common.Tests/home/helixbot/dotnetbuild/work/2a74cf82-3018-4e08-9e9a-744bb492869e/Payload/shared/Microsoft.NETCore.App/9.9.9/dotnet +``` + ## Using Visual Studio Code - Install [Visual Studio Code](https://code.visualstudio.com/) diff --git a/external/corefx/Documentation/project-docs/benchmarking.md b/external/corefx/Documentation/project-docs/benchmarking.md new file mode 100644 index 0000000000..6a691829c8 --- /dev/null +++ b/external/corefx/Documentation/project-docs/benchmarking.md @@ -0,0 +1,146 @@ +# Benchmarking .NET Core 2.0 / 2.1 applications + +We recommend using [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) as it allows specifying custom SDK paths and measuring performance not just in-proc but also out-of-proc as a dedicated executable. + +``` + + + +``` + +## Defining your benchmark + +See [BenchmarkDotNet](http://benchmarkdotnet.org/Guides/GettingStarted.htm) documentation -- minimally you need to adorn a public method with the `[Benchmark]` attribute but there are many other ways to customize what is done such as using parameter sets or setup/cleanup methods. Of course, you'll want to bracket just the relevant code in your benchmark, ensure there are sufficient iterations that you minimise noise, as well as leaving the machine otherwise idle while you measure. + +# Benchmarking .NET Core 2.0 applications +For benchmarking .NET Core 2.0 applications you only need the .NET Core 2.0 SDK installed: https://www.microsoft.com/net/download/windows. Make sure that your `TargetFramework` property in your csproj is set to `netcoreapp2.0` and follow the official BenchmarkDotNet instructions: http://benchmarkdotnet.org. + +# Benchmarking .NET Core 2.1 applications +Make sure to download the .NET Core 2.1 SDK zip archive (https://github.com/dotnet/core-setup#daily-builds) and extract it somewhere locally, e.g.: `C:\dotnet-nightly\`. + +For the sake of this tutorial we won't modify the `PATH` variable and instead always explicitly call the `dotnet.exe` from the downloaded SDK folder. + +The shared framework is a set of assemblies that are packed into a `netcoreapp` Nuget package which is used when you set your `TargetFramework` to `netcoreappX.X`. You can either decide to use your local self-compiled shared framework package or use the one which is bundled with the .NET Core 2.1 SDK. + +## Alternative 1 - Using the shared framework from the .NET Core 2.1 SDK +Follow the instructions described here https://github.com/dotnet/corefx/blob/master/Documentation/project-docs/dogfooding.md#advanced-scenario---using-a-nightly-build-of-microsoftnetcoreapp and skip the last part which calls the `dotnet.exe` to run the application. + +Add a benchmark class, configure it either with a manual configuration or by attributing it and pass the class type to the BenchmarkRunner: + +```csharp +[MemoryDiagnoser] +// ... +public class Benchmark +{ + // Benchmark code ... +} + +public class Program +{ + public static void Main() + { + BenchmarkRunner.Run(); + } +} +``` + +## Alternative 2 - Using your self-compiled shared framework +Follow the instructions described here https://github.com/dotnet/corefx/blob/master/Documentation/project-docs/dogfooding.md#more-advanced-scenario---using-your-local-corefx-build and skip the last part which calls the `dotnet.exe` to run the application. +Make sure to build your local corefx repository in RELEASE mode `.\build -release`! You currently need to have a self-contained application to inject your local shared framework package. + +Currently there is no straightforward way to run your BenchmarkDotNet application in a dedicated process, therefore we are using the InProcess switch `[InProcess]`: + +```csharp +[InProcess] +public class Benchmark +{ + // Benchmark code ... +} + +public class Program +{ + public static void Main() + { + BenchmarkRunner.Run(); + } +} +``` + +# Benchmark multiple or custom .NET Core 2.x SDKs +Follow the instructions described here https://github.com/dotnet/corefx/blob/master/Documentation/project-docs/dogfooding.md#advanced-scenario---using-a-nightly-build-of-microsoftnetcoreapp and skip the last part which calls the `dotnet.exe` to run the application. + +Whenever you want to benchmark an application simultaneously with one or multiple different .NET Core run time framework versions, you want to create a manual BenchmarkDotNet configuration file. Add the desired amount of Jobs and `NetCoreAppSettings` to specify the `targetFrameworkMoniker`, `runtimeFrameworkVersion` and `customDotNetCliPath`: + +```csharp +using BenchmarkDotNet.Columns; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Exporters; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Loggers; +using BenchmarkDotNet.Toolchains.CsProj; +using BenchmarkDotNet.Toolchains.DotNetCli; + +public class MainConfig : ManualConfig +{ + public MainConfig() + { + // Job #1 + Add(Job.Default + .With(Runtime.Core) + .With(CsProjCoreToolchain.From(new NetCoreAppSettings( + targetFrameworkMoniker: "netcoreapp2.1", + runtimeFrameworkVersion: "2.1.0-preview1-25919-02", // <-- Adjust version here + customDotNetCliPath: @"C:\dotnet-nightly\dotnet.exe", // <-- Adjust path here + name: "Core 2.1.0-preview")))); + + // Job #2 which could be in-process (see Alternative #2) + // ... + + // Job #3 which could be .NET Core 2.0 + // ... + + // Add whatever jobs you need + Add(DefaultColumnProviders.Instance); + Add(MarkdownExporter.GitHub); + Add(new ConsoleLogger()); + Add(new HtmlExporter()); + Add(MemoryDiagnoser.Default); + } +} +``` + +In your application entry point pass the configuration to the BenchmarkRunner: +```csharp +public class Benchmark +{ + // Benchmark code ... +} + +public class Program +{ + public static void Main() + { + BenchmarkRunner.Run(new MainConfig()); + } +} +``` + +# Running the benchmark + +To get valid results make sure to run your project in RELEASE configuration: + +``` +cd "path/to/your/benchmark/project" +"C:\dotnet-nightly\dotnet.exe" run -c Release +``` + +# Reporting results + +Often in a Github Pull Request or issue you will want to share performance results to justify a change. If you add the `MarkdownExporter` job in the configuration (as you can see in Alternative 3), BenchmarkDotNet will have created a Markdown (*.md) file in the `BenchmarkDotNet.Artifacts` folder which you can paste in, along with the code you benchmarked. + +# References +- [BenchmarkDotNet](http://benchmarkdotnet.org/) +- [BenchmarkDotNet Github](https://github.com/dotnet/BenchmarkDotNet) +- [.NET Core SDK](https://github.com/dotnet/core-setup) diff --git a/external/corefx/Documentation/project-docs/dogfooding.md b/external/corefx/Documentation/project-docs/dogfooding.md index b5c284bb98..9199f86de6 100644 --- a/external/corefx/Documentation/project-docs/dogfooding.md +++ b/external/corefx/Documentation/project-docs/dogfooding.md @@ -1,32 +1,54 @@ -# How to get up and running on .NET Core 2.0 +# How to get up and running on .NET Core This document provides the steps necessary to consume a nightly build of -.NET Core 2.0 runtime and SDK. +.NET Core runtime and SDK. Please note that these steps are likely to change as we're simplifying this experience. Make sure to consult this document often. ## Install prerequisites -1. Acquire the latest nightly .NET Core SDK 2.0 +1. Acquire the latest nightly .NET Core SDK by downloading the zip or tarball listed in https://github.com/dotnet/cli/blob/master/README.md#installers-and-binaries (for example, https://dotnetcli.blob.core.windows.net/dotnet/Sdk/master/dotnet-sdk-latest-win-x64.zip ) into a new folder. -- [Win 64-bit Latest Zip](https://dotnetcli.azureedge.net/dotnet/Sdk/master/dotnet-dev-win-x64.latest.zip) [Installer](https://dotnetcli.azureedge.net/dotnet/Sdk/master/dotnet-dev-win-x64.latest.exe) -- [macOS 64-bit Latest Tar](https://dotnetcli.azureedge.net/dotnet/Sdk/master/dotnet-dev-osx-x64.latest.tar.gz) [Installer](https://dotnetcli.azureedge.net/dotnet/Sdk/master/dotnet-dev-osx-x64.latest.pkg) -- [Others](https://github.com/dotnet/cli/blob/master/README.md#installers-and-binaries) +2. By default, the dotnet CLI will use the globally installed SDK if it matches the major/minor version you request and has a higher revision. To force it to use the locally installed SDK, you must set an environment variable `DOTNET_MULTILEVEL_LOOKUP=0` in your shell. You can use `dotnet --info` to verify what version of the Shared Framework it is using. -To setup the SDK download the zip and extract it somewhere and add the root folder to your path or always fully -qualify the path to dotnet in the root of this folder for all the instructions in this document. +3. Reminder: if you are using a local copy of the dotnet CLI, take care that when you type `dotnet` you do not inadvertently pick up a different copy that you may have in your path. On Windows, for example, if you use a Developer Command Prompt, a global copy may be in the path, so use the fully qualified path to your local `dotnet`. If you receive an error "The current .NET SDK does not support targeting .NET Core 2.1." then you may be executing an older `dotnet`. -Note: Installer will put dotnet globally in your path which you might not want for dogfooding daily toolsets. +After setting up dotnet you can verify you are using the newer version by executing `dotnet --info` -- the version should be greater than 2.2.0-* (dotnet CLI is currently numbered 2.2.0-* not 2.1.0-* ). Here is an example output at the time of writing: +``` +>dotnet.exe --info +.NET Command Line Tools (2.2.0-preview1-007460) -After setting up dotnet you can verify you are using the newer version by: +Product Information: + Version: 2.2.0-preview1-007460 + Commit SHA-1 hash: 173cc035e4 -`dotnet --info` -- the version should be greater than 2.0.0-* +Runtime Environment: + OS Name: Windows + OS Version: 10.0.16299 + OS Platform: Windows + RID: win10-x64 + Base Path: F:\dotnet\sdk\2.2.0-preview1-007460\ + +Microsoft .NET Core Shared Framework Host + + Version : 2.1.0-preview1-25825-07 + Build : 4c165c13bd390adf66f9af30a088d634d3f37a9d +``` + +4. Our nightly builds are uploaded to MyGet, not NuGet - so ensure the .NET Core MyGet feed is in your nuget configuration in case you need other packages from .NET Core that aren't included in the download. For example, on Windows you could edit `%userprofile%\appdata\roaming\nuget\nuget.config` or on Linux edit `~/.nuget/NuGet/NuGet.Config` to add this line: +```xml + + + ... + +``` +(Documentation for configuring feeds is [here](https://docs.microsoft.com/en-us/nuget/consume-packages/configuring-nuget-behavior).) ## Setup the project 1. Create a new project - - Create a new folder for your app + - Create a new folder for your app and change to that folder - Create project file by running `dotnet new console` 2. Restore packages so that you're ready to play: @@ -37,25 +59,6 @@ $ dotnet restore ## Consume the new build -Edit your `Program.cs` to consume the new APIs, for example: - -```CSharp -using System; -using System.Net; - -class Program -{ - static void Main(string[] args) - { - WebUtility.HtmlDecode("&", Console.Out); - Console.WriteLine(); - Console.WriteLine("Hello World!"); - } -} -``` - -Run the bits: - ``` $ dotnet run ``` @@ -65,7 +68,7 @@ Rinse and repeat! ## Advanced Scenario - Using a nightly build of Microsoft.NETCore.App When using the above instructions, your application will run against the same -.NET Core 2.0 runtime that comes with the SDK. That works fine to get up and +.NET Core runtime that comes with the SDK. That works fine to get up and running quickly. However, there are times when you need to use a nightly build of Microsoft.NETCore.App which hasn't made its way into the SDK yet. To enable this, there are two options you can take. @@ -84,8 +87,8 @@ runtime. ```XML Exe - netcoreapp2.0 - 2.0.0-beta-xyz-00 + netcoreapp2.1 + 2.1.0-preview1-25825-07 ``` @@ -105,19 +108,21 @@ make it self-contained ```XML Exe - netcoreapp2.0 - 2.0.0-beta-xyz-00 - win7-x64 + netcoreapp2.1 + 2.1.0-preview1-25825-07 + win-x64 ``` ``` $ dotnet restore $ dotnet publish -$ bin\Debug\netcoreapp2.0\win7-x64\publish\App.exe +$ bin\Debug\netcoreapp2.1\win-x64\publish\App.exe ``` -## Using your local CoreFx build +## More Advanced Scenario - Using your local CoreFx build + +If you built corefx locally with `build -allconfigurations` after building binaries it will build NuGet packages containing them. You can use those in your projects. To use your local built corefx packages you will need to be a self-contained application and so you will need to follow the "Self-contained" steps from above. Once you can successfully restore, build, publish, @@ -127,11 +132,11 @@ and run a self-contained application you need the following steps to consume you Look for a package named `Microsoft.Private.CoreFx.NETCoreApp..nupkg` under `corefx\bin\packages\Debug` (or Release if you built a release version of corefx). -Once you find the version number (for this example assume it is `4.4.0-beta-25102-0`) you need to add the following line to your project file: +Once you find the version number (for this example assume it is `4.5.0-preview1-25830-0`) you need to add the following line to your project file: ``` - + ``` @@ -146,25 +151,26 @@ you need to tell the tooling to use the assets from your local package. To do th Replacing the RID in `runtime.win-x64.Microsoft.Private.CoreFx.NETCoreApp` with the RID of your current build. +Note these instructions above were only about updates to the binaries that are part of Microsoft.NETCore.App, if you want to test a package for library that ships in its own nuget package you can follow the same steps above but instead add a package reference to that package instead of "Microsoft.Private.CoreFx.NETCoreApp". + #### 2 - Add your bin directory to the Nuget feed list By default the dogfooding dotnet SDK will create a Nuget.Config file next to your project, if it doesn't you can create one. Your config file will need a source for your local corefx package directory as well -as a reference to our nightly dotnet-core feed on myget: +as a reference to our nightly dotnet-core feed on myget. The Nuget.Config file content should be: ```xml - + - ``` -Obviously **you need to update path in the XML to be the path to output directory for your build**. +Be sure to correct the path to your build output above. -On Windows you also have the alternative of modifying the Nuget.Config -at `%HOMEPATH%\AppData\Roaming\Nuget\Nuget.Config` (`~/.nuget/NuGet/NuGet.Config` on Linux) with the new location. +You also have the alternative of modifying the Nuget.Config +at `%HOMEPATH%\AppData\Roaming\Nuget\Nuget.Config` (Windows) or `~/.nuget/NuGet/NuGet.Config` (Linux) with the new location. This will allow your new runtime to be used on any 'dotnet restore' run by the current user. Alternatively you can skip creating this file and pass the path to your package directory using the -s SOURCE qualifer on the dotnet restore command below. The important part is that somehow @@ -178,9 +184,15 @@ dotnet publish ``` Now your publication directory should contain your local built CoreFx binaries. -#### 3 - Consuming updated packages +#### 3 - Consuming subsequent code changes by overwriting the binary (Alternative 1) -One possible problem with the technique above is that Nuget assumes that distinct builds have distinct version numbers. +To apply changes you subsequently make in your source tree, it's usually easiest to just overwrite the binary in the publish folder. Build the assembly containing your change as normal, then overwrite the assembly in your publish folder and running the app will pick up that binary. This relies on the fact that all the other binaries still match what is in your bin folder so everything works together. + +#### 3 - Consuming subsequent code changes by rebuilding the package (Alternative 2) + +This is more cumbersome than just overwriting the binaries, but is more correct. + +First note that Nuget assumes that distinct builds have distinct version numbers. Thus if you modify the source and create a new NuGet package you must give it a new version number and use that in your application's project. Otherwise the dotnet.exe tool will assume that the existing version is fine and you won't get the updated bits. This is what the Minor Build number is all about. By default it is 0, but you can @@ -188,66 +200,13 @@ give it a value by setting the BuildNumberMinor environment variable. ```bat set BuildNumberMinor=3 ``` -before packaging. You should see this number show up in the version number (e.g. 4.4.0-beta-25102-03). +before packaging. You should see this number show up in the version number (e.g. 4.5.0-preview1-25830-03). -As an alternative you can delete the existing copy of the package from the Nuget cache. For example on +Alternatively just delete the existing copy of the package from the Nuget cache. For example on windows (on Linux substitute ~/ for %HOMEPATH%) you could delete ```bat - %HOMEPATH%\.nuget\packages\Microsoft.Private.CoreFx.NETCoreApp\4.4.0-beta-25102-0 + %HOMEPATH%\.nuget\packages\Microsoft.Private.CoreFx.NETCoreApp\4.5.0-preview1-25830-0 + %HOMEPATH%\.nuget\packages\runtime.win-x64.microsoft.private.corefx.netcoreapp\4.5.0-preview1-25830-0 ``` -which should make things work (but is fragile, confirm file timestamps that you are getting the version you expect) +which should make `dotnet restore` now pick up the new copy. -### Consuming individual library packages - -The instructions above were only about updates to the binaries that are part of Microsoft.NETCore.App, if you want to test a package -for library that ships in its own nuget package you can follow the same steps above but instead add a package reference to the -individual library package from your `bin\packages\Debug` folder. - -## Consuming non-NetStandard assets in a .NET Core 2.0 application - -Currently if you reference a NuGet package that does not have a NETStandard asset in your .NET Core 2.0 application, you will hit package -incompatibility errors when trying to restore packages. You can resolve this issue by adding `PackageTargetFallback` property -(MSBuild equivalent of `imports`) to your .csproj: - -```XML - $(PackageTargetFallback);net45 -``` - -Note that this can fix the problem if the package is actually compatible with netcoreapp2.0 (meaning it does not use types/APIs -that are not available in netcoreapp2.0) - -For final release, we are considering modifying NuGet behavior to automatically consume the non-netstandard asset if there is no netstandard available. - - -## Creating a .NET Core 2.0 console application from Visual Studio 2017 - -File > New > Project > Console App (.NET Core) - -By default, Visual Studio creates a netcoreapp1.1 application. After installing the prerequisites mentioned above, you will -need to modify your .csproj to target netcoreapp2.0 and reference the nightly build of Microsoft.NETCore.APP - -```XML - - Exe - netcoreapp2.0 - 2.0.0-beta-xyz-00 - -``` - -In a future update to Visual Studio, it will no longer be necessary to make this edit. - -## Finding specific builds - -The URL scheme for the runtime is as follows: - -``` -https://dotnetcli.azureedge.net/dotnet/master/Installers/$version$/dotnet-$os$-$arch$.$version$.exe -https://dotnetcli.azureedge.net/dotnet/master/Installers/2.0.0-preview1-001915-00/dotnet-win-x64.2.0.0-preview1-001915-00.exe -``` - -The URL scheme for the SDK & CLI is as follows: - -``` -https://dotnetcli.azureedge.net/dotnet/Sdk/$version$/dotnet-dev-$os$-$arch.$version$.exe -https://dotnetcli.azureedge.net/dotnet/Sdk/2.0.0-preview1-005791/dotnet-dev-win-x86.2.0.0-preview1-005791.exe -``` diff --git a/external/corefx/Documentation/project-docs/issue-guide.md b/external/corefx/Documentation/project-docs/issue-guide.md index 48a9d490a0..bf9b2d2b92 100644 --- a/external/corefx/Documentation/project-docs/issue-guide.md +++ b/external/corefx/Documentation/project-docs/issue-guide.md @@ -44,7 +44,7 @@ Areas are tracked by labels area-* (e.g. area-System.Collections). Each area |-----------------------------------------------------------------------------------------------|------------------|-------------| | [area-Infrastructure](https://github.com/dotnet/corefx/labels/area-Infrastructure) | [@weshaggard](https://github.com/weshaggard), [@ericstj](https://github.com/ericstj) |Covers:
  • Packaging
  • Build and test infra for CoreFX repo
  • VS integration

| | [area-Meta](https://github.com/dotnet/corefx/labels/area-Meta) | [@tarekgh](https://github.com/tarekgh) | Issues without clear association to any specific API/contract, e.g.
  • new contract proposals
  • cross-cutting code/test pattern changes (e.g. FxCop failures)
  • project-wide docs

| -| [area-Serialization](https://github.com/dotnet/corefx/labels/area-Serialization) | [@shmao](https://github.com/shmao), [@zhenlan](https://github.com/zhenlan) | Packages:
  • System.Runtime.Serialization.Xml
  • System.Runtime.Serialization.Json
  • System.Private.DataContractSerialization
  • System.Xml.XmlSerializer
Excluded:
  • System.Runtime.Serialization.Formatters
| +| [area-Serialization](https://github.com/dotnet/corefx/labels/area-Serialization) | [@huanwu](https://github.com/huanwu), [@zhenlan](https://github.com/zhenlan) | Packages:
  • System.Runtime.Serialization.Xml
  • System.Runtime.Serialization.Json
  • System.Private.DataContractSerialization
  • System.Xml.XmlSerializer
Excluded:
  • System.Runtime.Serialization.Formatters
| | **System contract assemblies** | | | | [System.AppContext](https://github.com/dotnet/corefx/labels/area-System.AppContext) | [@AlexGhiondea](https://github.com/AlexGhiondea) | | | | [System.Buffers](https://github.com/dotnet/corefx/labels/area-System.Buffers) | [@safern](https://github.com/safern) | | @@ -52,14 +52,14 @@ Areas are tracked by labels area-* (e.g. area-System.Collections). Each area | [System.Collections](https://github.com/dotnet/corefx/labels/area-System.Collections) |**[@safern](https://github.com/safern)**, [@ianhays](https://github.com/ianhays) | Excluded:
  • System.Array -> System.Runtime
| | [System.ComponentModel](https://github.com/dotnet/corefx/labels/area-System.ComponentModel) | **[@maryamariyan](https://github.com/maryamariyan)**, [@safern](https://github.com/safern) | | | [System.ComponentModel.DataAnnotations](https://github.com/dotnet/corefx/labels/area-System.ComponentModel.DataAnnotations) | [@lajones](https://github.com/lajones), [@divega](https://github.com/divega), [@ajcvickers](https://github.com/ajcvickers) | | -| [System.Composition](https://github.com/dotnet/corefx/labels/area-System.Composition) | [@ViktorHofer](https://github.com/ViktorHofer) | | +| [System.Composition](https://github.com/dotnet/corefx/labels/area-System.Composition) | **[@maryamariyan](https://github.com/maryamariyan)**, [@ViktorHofer](https://github.com/ViktorHofer) | | | [System.Configuration](https://github.com/dotnet/corefx/labels/area-System.Configuration) | [@maryamariyan](https://github.com/maryamariyan) | | | [System.Console](https://github.com/dotnet/corefx/labels/area-System.Console) | **[@joperezr](https://github.com/joperezr)**, [@ianhays](https://github.com/ianhays) | | | [System.Data](https://github.com/dotnet/corefx/labels/area-System.Data) | [@saurabh500](https://github.com/saurabh500), [@corivera](https://github.com/corivera) | | | [System.Data.SqlClient](https://github.com/dotnet/corefx/labels/area-System.Data.SqlClient) | [@saurabh500](https://github.com/saurabh500), [@corivera](https://github.com/corivera) | | -| [System.Diagnostics](https://github.com/dotnet/corefx/labels/area-System.Diagnostics) | **[@joperezr](https://github.com/joperezr)**, [@wtgodbe](https://github.com/wtgodbe) | | +| [System.Diagnostics](https://github.com/dotnet/corefx/labels/area-System.Diagnostics) | **[@joperezr](https://github.com/joperezr)**, [@wtgodbe](https://github.com/wtgodbe) |
  • System.Diagnostics.EventLog [@Anipik](https://github.com/Anipik)
| | [System.Diagnostics.Process](https://github.com/dotnet/corefx/labels/area-System.Diagnostics.Process) | **[@joperezr](https://github.com/joperezr)**, [@wtgodbe](https://github.com/wtgodbe) | | -| [System.Diagnostics.Tracing](https://github.com/dotnet/corefx/labels/area-System.Diagnostics.Tracing) | [@brianrob](https://github.com/brianrob), [@vancem](https://github.com/vancem), [@valenis](https://github.com/valenis)| Packages:
  • System.Diagnostics.DiagnosticSource
  • System.Diagnostics.PerformanceCounter
  • System.Diagnostics.Tracing
  • System.Diagnostics.TraceSource

| +| [System.Diagnostics.Tracing](https://github.com/dotnet/corefx/labels/area-System.Diagnostics.Tracing) | [@brianrob](https://github.com/brianrob), [@vancem](https://github.com/vancem), [@valenis](https://github.com/valenis)| Packages:
  • System.Diagnostics.DiagnosticSource
  • System.Diagnostics.PerformanceCounter - [@adiaaida](https://github.com/adiaaida)
  • System.Diagnostics.Tracing
  • System.Diagnostics.TraceSource

| | [System.DirectoryServices](https://github.com/dotnet/corefx/labels/area-System.DirectoryServices) | [@tquerec](https://github.com/tquerec) | | | [System.Drawing](https://github.com/dotnet/corefx/labels/area-System.Drawing) | [@safern](https://github.com/safern) | | | [System.Dynamic.Runtime](https://github.com/dotnet/corefx/labels/area-System.Dynamic.Runtime) | [@VSadov](https://github.com/VSadov), [@OmarTawfik](https://github.com/OmarTawfik) | | @@ -68,34 +68,38 @@ Areas are tracked by labels area-* (e.g. area-System.Collections). Each area | [System.IO.Compression](https://github.com/dotnet/corefx/labels/area-System.IO.Compression) | **[@ViktorHofer](https://github.com/ViktorHofer)**, [@ianhays](https://github.com/ianhays) | | | [System.Linq](https://github.com/dotnet/corefx/labels/area-System.Linq) | [@VSadov](https://github.com/VSadov), [@OmarTawfik](https://github.com/OmarTawfik) | | | [System.Linq.Expressions](https://github.com/dotnet/corefx/labels/area-System.Linq.Expressions) | [@VSadov](https://github.com/VSadov), [@OmarTawfik](https://github.com/OmarTawfik) | | -| [System.Linq.Parallel](https://github.com/dotnet/corefx/labels/area-System.Linq.Parallel) | [@kouvel](https://github.com/kouvel) | | +| [System.Linq.Parallel](https://github.com/dotnet/corefx/labels/area-System.Linq.Parallel) | **[@tarekgh](https://github.com/tarekgh)**, [@kouvel](https://github.com/kouvel) | | +| [System.Management](https://github.com/dotnet/corefx/labels/area-System.Management) | **[@Anipik](https://github.com/Anipik)**, [@pjanotti](https://github.com/pjanotti) | WMI | | [System.Memory](https://github.com/dotnet/corefx/labels/area-System.Memory) | [@KrzysztofCwalina](https://github.com/KrzysztofCwalina), [@ahsonkhan](https://github.com/ahsonkhan) | | | [System.Net](https://github.com/dotnet/corefx/labels/area-System.Net) | [@davidsh](https://github.com/davidsh), [@Priya91](https://github.com/Priya91), [@wfurt](https://github.com/wfurt) | Included:
  • System.Uri
| | [System.Net.Http](https://github.com/dotnet/corefx/labels/area-System.Net.Http) | [@davidsh](https://github.com/davidsh), [@Priya91](https://github.com/Priya91), [@wfurt](https://github.com/wfurt) | | +| [System.Net.Http.ManagedHandler](https://github.com/dotnet/corefx/labels/area-System.Net.Http.ManagedHandler) | [@geoffkizer](https://github.com/geoffkizer), [@Priya91](https://github.com/Priya91), [@wfurt](https://github.com/wfurt), [@davidsh](https://github.com/davidsh) | | | [System.Net.Security](https://github.com/dotnet/corefx/labels/area-System.Net.Security) | [@davidsh](https://github.com/davidsh), [@Priya91](https://github.com/Priya91), [@wfurt](https://github.com/wfurt) | | | [System.Net.Sockets](https://github.com/dotnet/corefx/labels/area-System.Net.Sockets) | [@davidsh](https://github.com/davidsh), [@Priya91](https://github.com/Priya91), [@wfurt](https://github.com/wfurt) | | | [System.Numerics](https://github.com/dotnet/corefx/labels/area-System.Numerics) | [@eerhardt](https://github.com/eerhardt), [@ViktorHofer](https://github.com/ViktorHofer) | | -| [System.Reflection](https://github.com/dotnet/corefx/labels/area-System.Reflection) | [@dnlharvey](https://github.com/dnlharvey), [@AtsushiKan](https://github.com/AtsushiKan) | | -| [System.Reflection.Emit](https://github.com/dotnet/corefx/labels/area-System.Reflection.Emit) | [@dnlharvey](https://github.com/dnlharvey), [@AtsushiKan](https://github.com/AtsushiKan) | | +| [System.Reflection](https://github.com/dotnet/corefx/labels/area-System.Reflection) | [@AtsushiKan](https://github.com/AtsushiKan) | | +| [System.Reflection.Emit](https://github.com/dotnet/corefx/labels/area-System.Reflection.Emit) | [@AtsushiKan](https://github.com/AtsushiKan) | | | [System.Reflection.Metadata](https://github.com/dotnet/corefx/labels/area-System.Reflection.Metadata) | [@tmat](https://github.com/tmat), [@nguerrera](https://github.com/nguerrera) | | | [System.Resources](https://github.com/dotnet/corefx/labels/area-System.Resources) | **[@krwq](https://github.com/krwq)**, [@tarekgh](https://github.com/tarekgh) | | | [System.Runtime](https://github.com/dotnet/corefx/labels/area-System.Runtime) | **[@joperezr](https://github.com/joperezr)**, [@AlexGhiondea](https://github.com/AlexGhiondea) | Included:
  • System.Runtime.Serialization.Formatters
  • System.Runtime.InteropServices.RuntimeInfo
  • System.Array
Excluded:
  • Path -> System.IO
  • StopWatch -> System.Diagnostics
  • Uri -> System.Net
  • WebUtility -> System.Net
| +| [System.Runtime.Caching](https://github.com/dotnet/corefx/labels/area-System.Runtime.Caching) | [@KKhurin](https://github.com/KKhurin), [@zhenlan](https://github.com/zhenlan) | | | [System.Runtime.CompilerServices](https://github.com/dotnet/corefx/labels/area-System.Runtime.CompilerServices) | **[@joperezr](https://github.com/joperezr)**, [@AlexGhiondea](https://github.com/AlexGhiondea) | | | [System.Runtime.Extensions](https://github.com/dotnet/corefx/labels/area-System.Runtime.Extensions) | **[@joperezr](https://github.com/joperezr)**, [@AlexGhiondea](https://github.com/AlexGhiondea) | | -| [System.Runtime.InteropServices](https://github.com/dotnet/corefx/labels/area-System.Runtime.InteropServices) | [@tijoytom](https://github.com/tijoytom), [@luqunl](https://github.com/luqunl) | Excluded:
  • System.Runtime.InteropServices.RuntimeInfo
| +| [System.Runtime.InteropServices](https://github.com/dotnet/corefx/labels/area-System.Runtime.InteropServices) | [@luqunl](https://github.com/luqunl), [@shrah](https://github.com/shrah) | Excluded:
  • System.Runtime.InteropServices.RuntimeInfo
| +| [System.Runtime.Intrinsics](https://github.com/dotnet/corefx/labels/area-System.Runtime.Intrinsics) | [@eerhardt](https://github.com/eerhardt), [@CarolEidt](https://github.com/CarolEidt), [@RussKeldorph](https://github.com/RussKeldorph) | | | [System.Security](https://github.com/dotnet/corefx/labels/area-System.Security) | [@bartonjs](https://github.com/bartonjs), [@ianhays](https://github.com/ianhays) | | | System.ServiceModel | N/A | [dotnet/wcf](https://github.com/dotnet/wcf) (except System.ServiceModel.Syndication) | -| [System.ServiceModel.Syndication](https://github.com/dotnet/corefx/labels/area-System.ServiceModel.Syndication) | [@shmao](https://github.com/shmao), [@zhenlan](https://github.com/zhenlan) | | -| [System.ServiceProcess](https://github.com/dotnet/corefx/labels/area-System.ServiceProcess) | [@maryamariyan](https://github.com/maryamariyan) | | +| [System.ServiceModel.Syndication](https://github.com/dotnet/corefx/labels/area-System.ServiceModel.Syndication) | [@huanwu](https://github.com/huanwu), [@zhenlan](https://github.com/zhenlan) | | +| [System.ServiceProcess](https://github.com/dotnet/corefx/labels/area-System.ServiceProcess) | **[@maryamariyan](https://github.com/maryamariyan)**, [@Anipik](https://github.com/Anipik) | | | [System.Text.Encoding](https://github.com/dotnet/corefx/labels/area-System.Text.Encoding) | **[@krwq](https://github.com/krwq)**, [@tarekgh](https://github.com/tarekgh) | Included:
  • System.Text.Encoding**s**.Web
| | [System.Text.RegularExpressions](https://github.com/dotnet/corefx/labels/area-System.Text.RegularExpressions) | **[@ViktorHofer](https://github.com/ViktorHofer)**, [@Priya91](https://github.com/Priya91) | | -| [System.Threading](https://github.com/dotnet/corefx/labels/area-System.Threading) | [@kouvel](https://github.com/kouvel) | | +| [System.Threading](https://github.com/dotnet/corefx/labels/area-System.Threading) | **[@kouvel](https://github.com/kouvel)**| | | [System.Transactions](https://github.com/dotnet/corefx/labels/area-System.Transactions) | [@jimcarley](https://github.com/jimcarley), [@qizhanMS](https://github.com/qizhanMS), [@dmetzgar](https://github.com/dmetzgar) | | -| [System.Xml](https://github.com/dotnet/corefx/labels/area-System.Xml) | **[@krwq](https://github.com/krwq)**, [@pjanotti](https://github.com/pjanotti) | | +| [System.Xml](https://github.com/dotnet/corefx/labels/area-System.Xml) | **[@krwq](https://github.com/krwq)**, [@pjanotti](https://github.com/pjanotti) | | | **Microsoft contract assemblies** | | | | [Microsoft.CSharp](https://github.com/dotnet/corefx/labels/area-Microsoft.CSharp) | [@VSadov](https://github.com/VSadov), [@OmarTawfik](https://github.com/OmarTawfik) | | | [Microsoft.VisualBasic](https://github.com/dotnet/corefx/labels/area-Microsoft.VisualBasic) | [@VSadov](https://github.com/VSadov), [@OmarTawfik](https://github.com/OmarTawfik) | | -| [Microsoft.Win32](https://github.com/dotnet/corefx/labels/area-Microsoft.Win32) | [@maryamariyan](https://github.com/maryamariyan) | | +| [Microsoft.Win32](https://github.com/dotnet/corefx/labels/area-Microsoft.Win32) | **[@maryamariyan](https://github.com/maryamariyan)**, , [@Anipik](https://github.com/Anipik) | | Note: Area triage will apply the new scheme (issue types and assignee) throughout 2016. diff --git a/external/corefx/README.md b/external/corefx/README.md index 8c61a927dd..96035b65d7 100644 --- a/external/corefx/README.md +++ b/external/corefx/README.md @@ -1,105 +1,3 @@ -# .NET Core Libraries (CoreFX) +# Mono fork of .NET Core Libraries (CoreFX) -This repo contains the library implementation (called "CoreFX") for .NET Core. It includes System.Collections, System.IO, System.Xml, and many other components. -The corresponding [.NET Core Runtime repo](https://github.com/dotnet/coreclr) (called "CoreCLR") contains the runtime implementation for .NET Core. It includes RyuJIT, the .NET GC, and many other components. -Runtime-specific library code ([mscorlib](https://github.com/dotnet/coreclr/tree/master/src/mscorlib)) lives in the CoreCLR repo. It needs to be built and versioned in tandem with the runtime. The rest of CoreFX is agnostic of runtime-implementation and can be run on any compatible .NET runtime (e.g. [CoreRT](https://github.com/dotnet/corert)). - - - -## .NET Core - -Official Starting Page: http://dotnet.github.io - -* [How to use .NET Core](https://github.com/dotnet/core/#get-started) (with VS, VS Code, command-line CLI) - * [Install official releases](https://www.microsoft.com/net/core) - * [Documentation](https://docs.microsoft.com/en-us/dotnet) (Get Started, Tutorials, Porting from .NET Framework, API reference, ...) - * [Deploying apps](https://docs.microsoft.com/en-us/dotnet/articles/core/preview3/deploying) - * [Supported OS versions](https://github.com/dotnet/core/blob/master/roadmap.md#technology-roadmaps) -* [Roadmap](https://github.com/dotnet/core/blob/master/roadmap.md) -* [Releases](https://github.com/dotnet/core/tree/master/release-notes) -* [Bringing more APIs to .NET Core](https://github.com/dotnet/corefx/blob/master/Documentation/project-docs/porting.md) (and why some APIs will be left out) - - - -## How to Engage, Contribute and Provide Feedback - -Some of the best ways to contribute are to try things out, file bugs, join in design conversations, and fix issues. - -* [Dogfooding daily builds](https://github.com/dotnet/corefx/blob/master/Documentation/project-docs/dogfooding.md) -* If you have a question or idea, [file a new issue](https://github.com/dotnet/corefx/issues/new). - -If you are having issues with the "full" .NET Framework (also called "Desktop"), the best way to file a bug is at [Connect](http://connect.microsoft.com/VisualStudio) or through [Product Support](https://support.microsoft.com/en-us/contactus?ws=support) if you have a contract. - -### Issue Guide - -This section is **in progress** here: [New contributor Docs - Issues](https://github.com/dotnet/corefx/wiki/New-contributor-Docs#issue-guide) (feel free to make it better - it's easy-to-edit wiki with RW permissions to everyone!) - -Each issue area has one or more Microsoft owners, who are [listed here](https://github.com/dotnet/corefx/blob/master/Documentation/project-docs/issue-guide.md). - -### Contributing Guide - -This section is **in progress** here: [New contributor Docs - Contributing](https://github.com/dotnet/corefx/wiki/New-contributor-Docs#contributing-guide) (feel free to make it better - it's easy-to-edit wiki with RW permissions to everyone!) - -### Useful Links - -* CoreFX source index: https://source.dot.net -* API Reference docs: https://docs.microsoft.com/en-us/dotnet/core/api -* .NET API Catalog: http://apisof.net (incl. APIs from daily builds and API usage info) -* "Full" .NET Framework source index: https://referencesource.microsoft.com - -### Community - -* General .NET OSS discussions: [.NET Foundation forums](http://forums.dotnetfoundation.org) -* Chat with other community members [![Join the chat at https://gitter.im/dotnet/corefx](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dotnet/corefx?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![.NET Slack Status](https://aspnetcoreslack.herokuapp.com/badge.svg?2)](http://tattoocoder.com/aspnet-slack-sign-up) - -This project has adopted the code of conduct defined by the [Contributor Covenant](http://contributor-covenant.org/) -to clarify expected behavior in our community. For more information, see the [.NET Foundation Code of Conduct](http://www.dotnetfoundation.org/code-of-conduct). - -### Reporting security issues and security bugs - -Security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC) . You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Further information, including the MSRC PGP key, can be found in the [Security TechCenter](https://technet.microsoft.com/en-us/security/ff852094.aspx). - -Also see info about related [Microsoft .NET Core and ASP.NET Core Bug Bounty Program](https://technet.microsoft.com/en-us/mt764065.aspx). - -## License - -.NET Core (including the corefx repo) is licensed under the [MIT license](LICENSE.TXT). - - - -## .NET Foundation - -.NET Core is a [.NET Foundation](http://www.dotnetfoundation.org/projects) project. - -There are many .NET related projects on GitHub. - -- [.NET home repo](https://github.com/Microsoft/dotnet) - links to 100s of .NET projects, from Microsoft and the community. -- [ASP.NET Core home](https://github.com/aspnet/home) - the best place to start learning about ASP.NET Core. - - - -## CoreFX Project - -### Daily Builds - -Daily builds of .NET Core components are published to [dotnet-core MyGet gallery](https://dotnet.myget.org/gallery/dotnet-core). -The latest version number of each library can be seen in that gallery. - -### Build & Test Status - -Note: See officially supported [OS versions](https://github.com/dotnet/core/blob/master/roadmap.md#technology-roadmaps). - -| | Inner x64 Debug | Inner x64 Release | Outer x64 Debug | Outer x64 Release | -|:---|----------------:|------------------:|----------------:|------------------:| -|**CentOS 7.1**|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/centos7.1_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/centos7.1_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/centos7.1_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/centos7.1_release/lastCompletedBuild/testReport)|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_centos7.1_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_centos7.1_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_centos7.1_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_centos7.1_release/lastCompletedBuild/testReport)| -|**Debian 8**|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/debian8.4_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/debian8.4_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/debian8.4_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/debian8.4_release/lastCompletedBuild/testReport)|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_debian8.4_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_debian8.4_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_debian8.4_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_debian8.4_release/lastCompletedBuild/testReport)| -|**Fedora 24**|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/fedora24_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/fedora24_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/fedora24_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/fedora24_release/lastCompletedBuild/testReport)|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_fedora24_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_fedora24_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_fedora24_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_fedora24_release/lastCompletedBuild/testReport)| -|**OS X 10.12**|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/osx10.12_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/osx10.12_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/osx10.12_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/osx10.12_release/lastCompletedBuild/testReport)|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_osx_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_osx_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_osx_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_osx_release/lastCompletedBuild/testReport)| -|**Red Hat 7.2**|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/rhel7.2_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/rhel7.2_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/rhel7.2_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/rhel7.2_release/lastCompletedBuild/testReport)|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_rhel7.2_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_rhel7.2_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_rhel7.2_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_rhel7.2_release/lastCompletedBuild/testReport)| -|**Ubuntu 14.04**|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu14.04_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu14.04_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu14.04_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu14.04_release/lastCompletedBuild/testReport)|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_ubuntu14.04_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_ubuntu14.04_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_ubuntu14.04_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_ubuntu14.04_release/lastCompletedBuild/testReport)| -|**Ubuntu 16.04**|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu16.04_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu16.04_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu16.04_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu16.04_release/lastCompletedBuild/testReport)|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_ubuntu16.04_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_ubuntu16.04_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_ubuntu16.04_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_ubuntu16.04_release/lastCompletedBuild/testReport)| -|**Ubuntu 16.10**|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu16.10_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu16.10_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu16.10_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu16.10_release/lastCompletedBuild/testReport)|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_ubuntu16.10_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_ubuntu16.10_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_ubuntu16.10_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_ubuntu16.10_release/lastCompletedBuild/testReport)| -|**PortableLinux**|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/portablelinux_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/portablelinux_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/portablelinux_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/portablelinux_release/lastCompletedBuild/testReport)|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_portablelinux_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_portablelinux_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_portablelinux_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_portablelinux_release/lastCompletedBuild/testReport)| -|**Windows 7**| | |[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_win7_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_win7_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_win7_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_win7_release/lastCompletedBuild/testReport)| -|**Windows 8.1**|(x86) [![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/windows_nt_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/windows_nt_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/windows_nt_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/windows_nt_release/lastCompletedBuild/testReport)|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_windows_nt_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_windows_nt_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_windows_nt_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_netcoreapp_windows_nt_release/lastCompletedBuild/testReport)| -|**Code Coverage (Windows)**| | |[![code coverage](https://ci.dot.net/job/dotnet_corefx/job/master/job/code_coverage_windows/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/code_coverage_windows/Code_Coverage_Report)| +Tracking https://github.com/dotnet/corefx/tree/release/2.1 branch diff --git a/external/corefx/THIRD-PARTY-NOTICES.TXT b/external/corefx/THIRD-PARTY-NOTICES.TXT index 06055ff03e..db542ca24f 100644 --- a/external/corefx/THIRD-PARTY-NOTICES.TXT +++ b/external/corefx/THIRD-PARTY-NOTICES.TXT @@ -224,3 +224,86 @@ descriptions are © 1997-2005 Sean Eron Anderson. The code and descriptions are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY and without even the implied warranty of merchantability or fitness for a particular purpose. + +License notice for Brotli +-------------------------------------- + +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +compress_fragment.c: +Copyright (c) 2011, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +""AS IS"" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +decode_fuzzer.c: +Copyright (c) 2015 The Chromium Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +""AS IS"" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + diff --git a/external/corefx/build-managed.sh b/external/corefx/build-managed.sh index 34fa4539b6..b0d649bb2d 100755 --- a/external/corefx/build-managed.sh +++ b/external/corefx/build-managed.sh @@ -1,4 +1,4 @@ #!/usr/bin/env bash working_tree_root="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -$working_tree_root/run.sh build-managed $* +$working_tree_root/run.sh build-managed "$@" exit $? diff --git a/external/corefx/build-native.sh b/external/corefx/build-native.sh index a9bfabf0e0..24d037a38d 100755 --- a/external/corefx/build-native.sh +++ b/external/corefx/build-native.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash working_tree_root="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #The Run Command Tool is calling src/Native/build-native.sh -$working_tree_root/run.sh build-native $* +$working_tree_root/run.sh build-native "$@" exit $? diff --git a/external/corefx/build-packages.sh b/external/corefx/build-packages.sh index dc51ea2b1b..9e805291e1 100755 --- a/external/corefx/build-packages.sh +++ b/external/corefx/build-packages.sh @@ -1,4 +1,4 @@ #!/usr/bin/env bash working_tree_root="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -$working_tree_root/run.sh build-managed -packages $* +$working_tree_root/run.sh build-managed -packages "$@" exit $? diff --git a/external/corefx/build.sh b/external/corefx/build.sh index e739d7f231..408744b4fc 100755 --- a/external/corefx/build.sh +++ b/external/corefx/build.sh @@ -38,10 +38,10 @@ if [ "$1" != "" ] && [[ "$1" != -* ]]; then fi fi -"$__scriptpath/build-native.sh" $* +"$__scriptpath/build-native.sh" "$@" if [ $? -ne 0 ];then exit 1 fi -"$__scriptpath/build-managed.sh" -BuildPackages=true $* +"$__scriptpath/build-managed.sh" -BuildPackages=true "$@" exit $? diff --git a/external/corefx/buildpipeline/DotNet-CoreFx-Trusted-Linux-Crossbuild.json b/external/corefx/buildpipeline/DotNet-CoreFx-Trusted-Linux-Crossbuild.json index 4ab48e9fb7..8822a96405 100644 --- a/external/corefx/buildpipeline/DotNet-CoreFx-Trusted-Linux-Crossbuild.json +++ b/external/corefx/buildpipeline/DotNet-CoreFx-Trusted-Linux-Crossbuild.json @@ -153,7 +153,7 @@ }, "inputs": { "filename": "docker", - "arguments": "run $(PB_DockerCommonRunArgs) $(PB_DockerVolumeName)/sync.sh -p -- /p:ArchGroup=$(PB_Architecture)", + "arguments": "run $(PB_DockerCommonRunArgs) $(PB_DockerVolumeName)/sync.sh $(PB_SyncArguments)", "workingFolder": "", "failOnStandardError": "false" } @@ -205,6 +205,7 @@ "alwaysRun": true, "displayName": "Copy Files to: $(Build.StagingDirectory)\\BuildLogs", "timeoutInMinutes": 0, + "condition": "succeededOrFailed()", "refName": "CopyFiles1", "task": { "id": "5bfb729a-a7c8-4a78-a7c3-8d717bb7c13c", @@ -227,6 +228,7 @@ "alwaysRun": true, "displayName": "Publish Artifact: BuildLogs", "timeoutInMinutes": 0, + "condition": "succeededOrFailed()", "refName": "PublishBuildArtifacts2", "task": { "id": "2ff763a7-ce83-4e1f-bc89-0ae63477cebe", @@ -246,9 +248,10 @@ "environment": {}, "enabled": true, "continueOnError": true, - "alwaysRun": true, + "alwaysRun": false, "displayName": "Cleanup Docker", "timeoutInMinutes": 0, + "condition": "always()", "refName": "Task12", "task": { "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", @@ -266,9 +269,10 @@ "environment": {}, "enabled": true, "continueOnError": true, - "alwaysRun": true, + "alwaysRun": false, "displayName": "Cleanup VSTS Agent", "timeoutInMinutes": 0, + "condition": "always()", "refName": "Task13", "task": { "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", @@ -342,7 +346,7 @@ "value": "Release" }, "PB_DockerCommonRunArgs": { - "value": "--rm --name $(PB_DockerContainerName) -v \"$(PB_GitDirectory):$(PB_DockerVolumeName)\" -w=\"$(PB_DockerVolumeName)\" $(PB_DockerImageName)" + "value": "--rm --name $(PB_DockerContainerName) -v \"$(PB_GitDirectory):$(PB_DockerVolumeName)\" -w=\"$(PB_DockerVolumeName)\" -e \"PACKAGEVERSIONPROPSURL=$(PB_PackageVersionPropsUrl)\" $(PB_DockerImageName)" }, "PB_DockerContainerName": { "value": "corefx-cross-$(Build.BuildId)" @@ -396,6 +400,16 @@ "VsoPassword": { "value": null, "isSecret": true + }, + "PB_SyncArguments": { + "value": "-p -- /p:ArchGroup=$(PB_Architecture)", + "allowOverride": true + }, + "PB_PackageVersionPropsUrl": { + "value": "" + }, + "PB_AssetRootUrl": { + "value": "" } }, "demands": [ @@ -442,6 +456,7 @@ }, "processParameters": {}, "quality": "definition", + "drafts": [], "queue": { "id": 36, "name": "DotNet-Build", @@ -461,7 +476,7 @@ "description": "Visual Studio and DevDiv team project for git source code repositories. Work items will be added for Adams, Dev14 work items are tracked in vstfdevdiv. ", "url": "https://devdiv.visualstudio.com/DefaultCollection/_apis/projects/0bdbc590-a062-4c3f-b0f6-9383f67865ee", "state": "wellFormed", - "revision": 418098167, + "revision": 418098432, "visibility": "organization" } -} +} \ No newline at end of file diff --git a/external/corefx/buildpipeline/DotNet-CoreFx-Trusted-Linux.json b/external/corefx/buildpipeline/DotNet-CoreFx-Trusted-Linux.json index 5eaeb4717e..43e45cfc8a 100644 --- a/external/corefx/buildpipeline/DotNet-CoreFx-Trusted-Linux.json +++ b/external/corefx/buildpipeline/DotNet-CoreFx-Trusted-Linux.json @@ -85,6 +85,7 @@ "alwaysRun": false, "displayName": "Create host machine tools sandbox", "timeoutInMinutes": 0, + "refName": "Task5", "task": { "id": "5bfb729a-a7c8-4a78-a7c3-8d717bb7c13c", "versionSpec": "2.*", @@ -92,7 +93,7 @@ }, "inputs": { "SourceFolder": "$(PB_GitDirectory)", - "Contents": "init-tools.sh\nBuildToolsVersion.txt\nDotnetCLIVersion.txt\ninit-tools.msbuild", + "Contents": "init-tools.sh\nBuildToolsVersion.txt\nDotnetCLIVersion.txt\ninit-tools.msbuild\ndependencies.props", "TargetFolder": "$(DockerHost_Sandbox)", "CleanTargetFolder": "false", "OverWrite": "false", @@ -100,6 +101,7 @@ } }, { + "environment": {}, "enabled": true, "continueOnError": false, "alwaysRun": false, @@ -205,6 +207,7 @@ "alwaysRun": false, "displayName": "Build tests", "timeoutInMinutes": 0, + "condition": "and(succeeded(), ne(variables.PB_SkipTests, 'true'), ne(variables.PB_SkipTestBuild, 'true'))", "refName": "Task10", "task": { "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", @@ -225,6 +228,7 @@ "alwaysRun": false, "displayName": "Create Helix Test Jobs", "timeoutInMinutes": 0, + "condition": "and(succeeded(), ne(variables.PB_SkipTests, 'true'), ne(variables.PB_EnableCloudTest, 'false'))", "refName": "Task11", "task": { "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", @@ -265,6 +269,7 @@ "alwaysRun": true, "displayName": "Copy Files to: $(Build.StagingDirectory)\\BuildLogs", "timeoutInMinutes": 0, + "condition": "succeededOrFailed()", "refName": "CopyFiles1", "task": { "id": "5bfb729a-a7c8-4a78-a7c3-8d717bb7c13c", @@ -287,6 +292,7 @@ "alwaysRun": true, "displayName": "Publish Artifact: BuildLogs", "timeoutInMinutes": 0, + "condition": "succeededOrFailed()", "refName": "PublishBuildArtifacts2", "task": { "id": "2ff763a7-ce83-4e1f-bc89-0ae63477cebe", @@ -306,9 +312,10 @@ "environment": {}, "enabled": true, "continueOnError": true, - "alwaysRun": true, + "alwaysRun": false, "displayName": "Cleanup Docker", "timeoutInMinutes": 0, + "condition": "always()", "refName": "Task14", "task": { "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", @@ -326,9 +333,10 @@ "environment": {}, "enabled": true, "continueOnError": true, - "alwaysRun": true, + "alwaysRun": false, "displayName": "Cleanup VSTS Agent", "timeoutInMinutes": 0, + "condition": "always()", "refName": "Task15", "task": { "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", @@ -416,7 +424,7 @@ "allowOverride": true }, "PB_DockerCommonRunArgs": { - "value": "--rm --name $(PB_DockerContainerName) -v \"$(PB_GitDirectory):$(PB_DockerVolumeName)\" -w=\"$(PB_DockerVolumeName)\" $(PB_DockerImageName)" + "value": "--rm --name $(PB_DockerContainerName) -v \"$(PB_GitDirectory):$(PB_DockerVolumeName)\" -w=\"$(PB_DockerVolumeName)\" -e \"PACKAGEVERSIONPROPSURL=$(PB_PackageVersionPropsUrl)\" $(PB_DockerImageName)" }, "PB_DockerContainerName": { "value": "corefx-$(Build.BuildId)" @@ -471,6 +479,16 @@ "VsoPassword": { "value": null, "isSecret": true + }, + "PB_SkipTests": { + "value": "false", + "allowOverride": true + }, + "PB_PackageVersionPropsUrl": { + "value": "" + }, + "PB_AssetRootUrl": { + "value": "" } }, "demands": [ @@ -516,6 +534,7 @@ }, "processParameters": {}, "quality": "definition", + "drafts": [], "queue": { "id": 36, "name": "DotNet-Build", @@ -535,7 +554,7 @@ "description": "Visual Studio and DevDiv team project for git source code repositories. Work items will be added for Adams, Dev14 work items are tracked in vstfdevdiv. ", "url": "https://devdiv.visualstudio.com/DefaultCollection/_apis/projects/0bdbc590-a062-4c3f-b0f6-9383f67865ee", "state": "wellFormed", - "revision": 418098167, + "revision": 418098432, "visibility": "organization" } -} +} \ No newline at end of file diff --git a/external/corefx/buildpipeline/DotNet-CoreFx-Trusted-OSX.json b/external/corefx/buildpipeline/DotNet-CoreFx-Trusted-OSX.json index c7bba81ce2..67a343ede9 100644 --- a/external/corefx/buildpipeline/DotNet-CoreFx-Trusted-OSX.json +++ b/external/corefx/buildpipeline/DotNet-CoreFx-Trusted-OSX.json @@ -145,6 +145,7 @@ "alwaysRun": false, "displayName": "Run $(Agent.BuildDirectory)/s/corefx/build-tests.sh", "timeoutInMinutes": 0, + "condition": "and(succeeded(), ne(variables.PB_SkipTests, 'true'))", "refName": "Task8", "task": { "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", @@ -165,6 +166,7 @@ "alwaysRun": false, "displayName": "Create Helix Test Jobs", "timeoutInMinutes": 0, + "condition": "and(succeeded(), ne(variables.PB_SkipTests, 'true'), ne(variables.PB_EnableCloudTest, 'false'))", "refName": "Task9", "task": { "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", @@ -205,6 +207,7 @@ "alwaysRun": true, "displayName": "Copy Files to: $(Build.StagingDirectory)\\BuildLogs", "timeoutInMinutes": 0, + "condition": "succeededOrFailed()", "refName": "CopyFiles1", "task": { "id": "5bfb729a-a7c8-4a78-a7c3-8d717bb7c13c", @@ -227,6 +230,7 @@ "alwaysRun": true, "displayName": "Publish Artifact: BuildLogs", "timeoutInMinutes": 0, + "condition": "succeededOrFailed()", "refName": "PublishBuildArtifacts2", "task": { "id": "2ff763a7-ce83-4e1f-bc89-0ae63477cebe", @@ -328,6 +332,19 @@ "PB_CreateHelixArguments": { "value": "/t:CloudBuild /p:ArchGroup=x64 /p:ConfigurationGroup=Release /p:\"EnableCloudTest=true /p:\"TestProduct=corefx /p:\"TimeoutInSeconds=1200\" /p:\"TargetOS=OSX\" /p:FilterToOSGroup=OSX", "allowOverride": true + }, + "PB_SkipTests": { + "value": "false", + "allowOverride": true + }, + "PB_PackageVersionPropsUrl": { + "value": "" + }, + "PACKAGEVERSIONPROPSURL": { + "value": "$(PB_PackageVersionPropsUrl)" + }, + "PB_AssetRootUrl": { + "value": "" } }, "demands": [ @@ -373,6 +390,7 @@ }, "processParameters": {}, "quality": "definition", + "drafts": [], "queue": { "id": 330, "name": "DotNetCore-Build", @@ -392,7 +410,7 @@ "description": "Visual Studio and DevDiv team project for git source code repositories. Work items will be added for Adams, Dev14 work items are tracked in vstfdevdiv. ", "url": "https://devdiv.visualstudio.com/DefaultCollection/_apis/projects/0bdbc590-a062-4c3f-b0f6-9383f67865ee", "state": "wellFormed", - "revision": 418098167, + "revision": 418098432, "visibility": "organization" } } \ No newline at end of file diff --git a/external/corefx/buildpipeline/DotNet-CoreFx-Trusted-Windows-NoTest.json b/external/corefx/buildpipeline/DotNet-CoreFx-Trusted-Windows-NoTest.json index f980cf4753..df278c3263 100644 --- a/external/corefx/buildpipeline/DotNet-CoreFx-Trusted-Windows-NoTest.json +++ b/external/corefx/buildpipeline/DotNet-CoreFx-Trusted-Windows-NoTest.json @@ -18,7 +18,7 @@ "scriptName": "", "arguments": "-path $(build.SourcesDirectory)\\corefx", "workingFolder": "", - "inlineScript": "param($path)\n\nif (Test-Path $path){\n Stop-Process -processname msbuild -ErrorAction Ignore -Verbose\n Stop-Process -processname dotnet -ErrorAction Ignore -Verbose\n Stop-Process -processname vbcscompiler -ErrorAction Ignore -Verbose\n # this will print out an error each time a file can't be deleted.\n Remove-Item -Recurse -Force $path\n }\n", + "inlineScript": "param($path)\nif ($path -and (Test-Path $path)){\nStop-Process -processname msbuild -ErrorAction Ignore -Verbose\nStop-Process -processname dotnet -ErrorAction Ignore -Verbose\nStop-Process -processname vbcscompiler -ErrorAction Ignore -Verbose\n$emptyFolder = (New-Item -ItemType Directory (Join-Path -Path $env:TEMP -ChildPath ([System.IO.Path]::GetRandomFileName()))).FullName\nrobocopy $emptyFolder $path /purge\nRemove-Item -Recurse -Force $path,$emptyFolder \nexit 0\n}", "failOnStandardError": "true" } }, @@ -69,7 +69,7 @@ "alwaysRun": false, "displayName": "Install Signing Plugin", "timeoutInMinutes": 0, - "condition": "ne(variables['PB_SignType'], 'oss')", + "condition": "and(succeeded(), in(variables.PB_SignType, 'real', 'test'))", "refName": "Task4", "task": { "id": "30666190-6959-11e5-9f96-f56098202fef", @@ -183,50 +183,6 @@ "failOnStandardError": "false" } }, - { - "environment": {}, - "enabled": true, - "continueOnError": true, - "alwaysRun": true, - "displayName": "Copy Files to: $(Build.StagingDirectory)\\BuildLogs", - "timeoutInMinutes": 0, - "refName": "CopyFiles1", - "task": { - "id": "5bfb729a-a7c8-4a78-a7c3-8d717bb7c13c", - "versionSpec": "2.*", - "definitionType": "task" - }, - "inputs": { - "SourceFolder": "$(Build.SourcesDirectory)\\corefx", - "Contents": "*.log", - "TargetFolder": "$(Build.StagingDirectory)\\BuildLogs", - "CleanTargetFolder": "false", - "OverWrite": "false", - "flattenFolders": "false" - } - }, - { - "environment": {}, - "enabled": true, - "continueOnError": true, - "alwaysRun": true, - "displayName": "Publish Artifact: BuildLogs", - "timeoutInMinutes": 0, - "refName": "PublishBuildArtifacts2", - "task": { - "id": "2ff763a7-ce83-4e1f-bc89-0ae63477cebe", - "versionSpec": "1.*", - "definitionType": "task" - }, - "inputs": { - "PathtoPublish": "$(Build.StagingDirectory)\\BuildLogs", - "ArtifactName": "BuildLogs", - "ArtifactType": "Container", - "TargetPath": "\\\\my\\share\\$(Build.DefinitionName)\\$(Build.BuildNumber)", - "Parallel": "false", - "ParallelCount": "8" - } - }, { "environment": {}, "enabled": true, @@ -252,6 +208,52 @@ "SymbolsArtifactName": "Symbols_$(PB_ConfigurationGroup)" } }, + { + "environment": {}, + "enabled": true, + "continueOnError": true, + "alwaysRun": true, + "displayName": "Copy Files to: $(Build.StagingDirectory)\\BuildLogs", + "timeoutInMinutes": 0, + "condition": "succeededOrFailed()", + "refName": "CopyFiles1", + "task": { + "id": "5bfb729a-a7c8-4a78-a7c3-8d717bb7c13c", + "versionSpec": "2.*", + "definitionType": "task" + }, + "inputs": { + "SourceFolder": "$(Build.SourcesDirectory)\\corefx", + "Contents": "*.log", + "TargetFolder": "$(Build.StagingDirectory)\\BuildLogs", + "CleanTargetFolder": "false", + "OverWrite": "false", + "flattenFolders": "false" + } + }, + { + "environment": {}, + "enabled": true, + "continueOnError": true, + "alwaysRun": true, + "displayName": "Publish Artifact: BuildLogs", + "timeoutInMinutes": 0, + "condition": "succeededOrFailed()", + "refName": "PublishBuildArtifacts2", + "task": { + "id": "2ff763a7-ce83-4e1f-bc89-0ae63477cebe", + "versionSpec": "1.*", + "definitionType": "task" + }, + "inputs": { + "PathtoPublish": "$(Build.StagingDirectory)\\BuildLogs", + "ArtifactName": "BuildLogs", + "ArtifactType": "Container", + "TargetPath": "\\\\my\\share\\$(Build.DefinitionName)\\$(Build.BuildNumber)", + "Parallel": "false", + "ParallelCount": "8" + } + }, { "environment": {}, "enabled": true, @@ -259,6 +261,7 @@ "alwaysRun": false, "displayName": "Execute cleanup tasks", "timeoutInMinutes": 0, + "condition": "always()", "refName": "Task12", "task": { "id": "521a94ea-9e68-468a-8167-6dcf361ea776", @@ -274,6 +277,7 @@ "alwaysRun": false, "displayName": "Final clean to remove any lingering process", "timeoutInMinutes": 0, + "condition": "always()", "refName": "Task13", "task": { "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", @@ -294,6 +298,7 @@ "alwaysRun": false, "displayName": "Build solution corefx\\Tools\\scripts\\vstsagent\\cleanupagent.proj", "timeoutInMinutes": 0, + "condition": "always()", "refName": "Task14", "task": { "id": "c6c4c611-aa2e-4a33-b606-5eaba2196824", @@ -422,6 +427,15 @@ "PB_SignType": { "value": "real", "allowOverride": true + }, + "PB_PackageVersionPropsUrl": { + "value": "" + }, + "PACKAGEVERSIONPROPSURL": { + "value": "$(PB_PackageVersionPropsUrl)" + }, + "PB_AssetRootUrl": { + "value": "" } }, "demands": [ @@ -468,6 +482,7 @@ }, "processParameters": {}, "quality": "definition", + "drafts": [], "queue": { "id": 36, "name": "DotNet-Build", @@ -487,7 +502,7 @@ "description": "Visual Studio and DevDiv team project for git source code repositories. Work items will be added for Adams, Dev14 work items are tracked in vstfdevdiv. ", "url": "https://devdiv.visualstudio.com/DefaultCollection/_apis/projects/0bdbc590-a062-4c3f-b0f6-9383f67865ee", "state": "wellFormed", - "revision": 418098167, + "revision": 418098432, "visibility": "organization" } -} +} \ No newline at end of file diff --git a/external/corefx/buildpipeline/DotNet-CoreFx-Trusted-Windows.json b/external/corefx/buildpipeline/DotNet-CoreFx-Trusted-Windows.json index 207cd5d247..fd989b23d3 100644 --- a/external/corefx/buildpipeline/DotNet-CoreFx-Trusted-Windows.json +++ b/external/corefx/buildpipeline/DotNet-CoreFx-Trusted-Windows.json @@ -18,7 +18,7 @@ "scriptName": "", "arguments": "-path $(build.SourcesDirectory)\\corefx", "workingFolder": "", - "inlineScript": "param($path)\n\nif (Test-Path $path){\n Stop-Process -processname msbuild -ErrorAction Ignore -Verbose\n Stop-Process -processname dotnet -ErrorAction Ignore -Verbose\n Stop-Process -processname vbcscompiler -ErrorAction Ignore -Verbose\n # this will print out an error each time a file can't be deleted.\n Remove-Item -Recurse -Force $path\n }", + "inlineScript": "param($path)\nif ($path -and (Test-Path $path)){\nStop-Process -processname msbuild -ErrorAction Ignore -Verbose\nStop-Process -processname dotnet -ErrorAction Ignore -Verbose\nStop-Process -processname vbcscompiler -ErrorAction Ignore -Verbose\n$emptyFolder = (New-Item -ItemType Directory (Join-Path -Path $env:TEMP -ChildPath ([System.IO.Path]::GetRandomFileName()))).FullName\nrobocopy $emptyFolder $path /purge\nRemove-Item -Recurse -Force $path,$emptyFolder \nexit 0\n}", "failOnStandardError": "true" } }, @@ -69,7 +69,7 @@ "alwaysRun": false, "displayName": "Install Signing Plugin", "timeoutInMinutes": 0, - "condition": "ne(variables['PB_SignType'], 'oss')", + "condition": "and(succeeded(), in(variables.PB_SignType, 'real', 'test'))", "refName": "Task4", "task": { "id": "30666190-6959-11e5-9f96-f56098202fef", @@ -170,6 +170,7 @@ "alwaysRun": false, "displayName": "Run $(Build.SourcesDirectory)\\corefx\\build-tests.cmd", "timeoutInMinutes": 0, + "condition": "and(succeeded(), ne(variables.PB_SkipTests, 'true'))", "refName": "Task9", "task": { "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", @@ -190,6 +191,7 @@ "alwaysRun": false, "displayName": "Create Helix Test Jobs", "timeoutInMinutes": 0, + "condition": "and(succeeded(), ne(variables.PB_SkipTests, 'true'), ne(variables.PB_EnableCloudTest, 'false'))", "refName": "Task10", "task": { "id": "c6c4c611-aa2e-4a33-b606-5eaba2196824", @@ -232,50 +234,6 @@ "failOnStandardError": "false" } }, - { - "environment": {}, - "enabled": true, - "continueOnError": true, - "alwaysRun": true, - "displayName": "Copy Files to: $(Build.StagingDirectory)\\BuildLogs", - "timeoutInMinutes": 0, - "refName": "CopyFiles1", - "task": { - "id": "5bfb729a-a7c8-4a78-a7c3-8d717bb7c13c", - "versionSpec": "2.*", - "definitionType": "task" - }, - "inputs": { - "SourceFolder": "$(Build.SourcesDirectory)\\corefx", - "Contents": "*.log", - "TargetFolder": "$(Build.StagingDirectory)\\BuildLogs", - "CleanTargetFolder": "false", - "OverWrite": "false", - "flattenFolders": "false" - } - }, - { - "environment": {}, - "enabled": true, - "continueOnError": true, - "alwaysRun": true, - "displayName": "Publish Artifact: BuildLogs", - "timeoutInMinutes": 0, - "refName": "PublishBuildArtifacts2", - "task": { - "id": "2ff763a7-ce83-4e1f-bc89-0ae63477cebe", - "versionSpec": "1.*", - "definitionType": "task" - }, - "inputs": { - "PathtoPublish": "$(Build.StagingDirectory)\\BuildLogs", - "ArtifactName": "BuildLogs", - "ArtifactType": "Container", - "TargetPath": "\\\\my\\share\\$(Build.DefinitionName)\\$(Build.BuildNumber)", - "Parallel": "false", - "ParallelCount": "8" - } - }, { "environment": {}, "enabled": true, @@ -301,6 +259,52 @@ "SymbolsArtifactName": "Symbols_$(PB_ConfigurationGroup)" } }, + { + "environment": {}, + "enabled": true, + "continueOnError": true, + "alwaysRun": true, + "displayName": "Copy Files to: $(Build.StagingDirectory)\\BuildLogs", + "timeoutInMinutes": 0, + "condition": "succeededOrFailed()", + "refName": "CopyFiles1", + "task": { + "id": "5bfb729a-a7c8-4a78-a7c3-8d717bb7c13c", + "versionSpec": "2.*", + "definitionType": "task" + }, + "inputs": { + "SourceFolder": "$(Build.SourcesDirectory)\\corefx", + "Contents": "*.log", + "TargetFolder": "$(Build.StagingDirectory)\\BuildLogs", + "CleanTargetFolder": "false", + "OverWrite": "false", + "flattenFolders": "false" + } + }, + { + "environment": {}, + "enabled": true, + "continueOnError": true, + "alwaysRun": true, + "displayName": "Publish Artifact: BuildLogs", + "timeoutInMinutes": 0, + "condition": "succeededOrFailed()", + "refName": "PublishBuildArtifacts2", + "task": { + "id": "2ff763a7-ce83-4e1f-bc89-0ae63477cebe", + "versionSpec": "1.*", + "definitionType": "task" + }, + "inputs": { + "PathtoPublish": "$(Build.StagingDirectory)\\BuildLogs", + "ArtifactName": "BuildLogs", + "ArtifactType": "Container", + "TargetPath": "\\\\my\\share\\$(Build.DefinitionName)\\$(Build.BuildNumber)", + "Parallel": "false", + "ParallelCount": "8" + } + }, { "environment": {}, "enabled": true, @@ -308,6 +312,7 @@ "alwaysRun": false, "displayName": "Execute cleanup tasks", "timeoutInMinutes": 0, + "condition": "always()", "refName": "Task14", "task": { "id": "521a94ea-9e68-468a-8167-6dcf361ea776", @@ -323,6 +328,7 @@ "alwaysRun": false, "displayName": "Final clean to remove any lingering process", "timeoutInMinutes": 0, + "condition": "always()", "refName": "Task15", "task": { "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", @@ -343,6 +349,7 @@ "alwaysRun": false, "displayName": "Build solution corefx\\Tools\\scripts\\vstsagent\\cleanupagent.proj", "timeoutInMinutes": 0, + "condition": "always()", "refName": "Task16", "task": { "id": "c6c4c611-aa2e-4a33-b606-5eaba2196824", @@ -503,6 +510,19 @@ "PB_SignType": { "value": "real", "allowOverride": true + }, + "PB_SkipTests": { + "value": "false", + "allowOverride": true + }, + "PB_PackageVersionPropsUrl": { + "value": "" + }, + "PACKAGEVERSIONPROPSURL": { + "value": "$(PB_PackageVersionPropsUrl)" + }, + "PB_AssetRootUrl": { + "value": "" } }, "demands": [ @@ -548,6 +568,7 @@ }, "processParameters": {}, "quality": "definition", + "drafts": [], "queue": { "id": 36, "name": "DotNet-Build", @@ -567,7 +588,7 @@ "description": "Visual Studio and DevDiv team project for git source code repositories. Work items will be added for Adams, Dev14 work items are tracked in vstfdevdiv. ", "url": "https://devdiv.visualstudio.com/DefaultCollection/_apis/projects/0bdbc590-a062-4c3f-b0f6-9383f67865ee", "state": "wellFormed", - "revision": 418098167, + "revision": 418098432, "visibility": "organization" } -} +} \ No newline at end of file diff --git a/external/corefx/buildpipeline/DotNet-Trusted-Publish.json b/external/corefx/buildpipeline/DotNet-Trusted-Publish.json index 9d04c76464..e6e765f3c3 100644 --- a/external/corefx/buildpipeline/DotNet-Trusted-Publish.json +++ b/external/corefx/buildpipeline/DotNet-Trusted-Publish.json @@ -1,12 +1,14 @@ { "build": [ { + "environment": {}, "enabled": true, "continueOnError": false, "alwaysRun": false, "displayName": "Install Signing Plugin", "timeoutInMinutes": 0, - "condition": "and(succeeded(), ne(variables['PB_SignType'], 'oss'))", + "condition": "and(succeeded(), in(variables.PB_SignType, 'real', 'test'))", + "refName": "Task1", "task": { "id": "30666190-6959-11e5-9f96-f56098202fef", "versionSpec": "1.*", @@ -20,11 +22,13 @@ } }, { + "environment": {}, "enabled": true, "continueOnError": false, "alwaysRun": false, "displayName": "Run script $(VS140COMNTOOLS)\\VsDevCmd.bat", "timeoutInMinutes": 0, + "refName": "Task2", "task": { "id": "bfc8bf76-e7ac-4a8c-9a55-a944a9f632fd", "versionSpec": "1.*", @@ -39,11 +43,13 @@ } }, { + "environment": {}, "enabled": true, "continueOnError": false, "alwaysRun": false, "displayName": "Fetch custom tooling (NuGet, EmbedIndex)", "timeoutInMinutes": 0, + "refName": "Task3", "task": { "id": "e213ff0f-5d5c-4791-802d-52ea3e7be1f1", "versionSpec": "1.*", @@ -53,17 +59,19 @@ "scriptType": "filePath", "scriptName": "scripts/DotNet-Trusted-Publish/Fetch-Tools.ps1", "arguments": "$(Build.StagingDirectory)\\ToolingDownload", - "inlineScript": "# You can write your powershell scripts inline here. \n# You can also pass predefined and custom variables to this scripts using arguments\n\n Write-Host \"Hello World\"", "workingFolder": "", + "inlineScript": "# You can write your powershell scripts inline here. \n# You can also pass predefined and custom variables to this scripts using arguments\n\n Write-Host \"Hello World\"", "failOnStandardError": "true" } }, { + "environment": {}, "enabled": true, "continueOnError": false, "alwaysRun": false, "displayName": "Set up pipeline-specific git repository", "timeoutInMinutes": 0, + "refName": "Task4", "task": { "id": "e213ff0f-5d5c-4791-802d-52ea3e7be1f1", "versionSpec": "1.*", @@ -73,17 +81,19 @@ "scriptType": "inlineScript", "scriptName": "", "arguments": "-gitUrl $(PB_VstsRepoGitUrl) -root $(Pipeline.SourcesDirectory)", - "inlineScript": "param($gitUrl, $root)\n\nif (Test-Path $root)\n{\n Remove-Item -Recurse -Force $root\n}\ngit clone --no-checkout $gitUrl $root 2>&1 | Write-Host\ncd $root\ngit checkout $env:SourceVersion 2>&1 | Write-Host\n\nWrite-Host (\"##vso[task.setvariable variable=Pipeline.SourcesDirectory;]$root\")", "workingFolder": "", + "inlineScript": "param($gitUrl, $root)\n\nif (Test-Path $root)\n{\n Remove-Item -Recurse -Force $root\n}\ngit clone --no-checkout $gitUrl $root 2>&1 | Write-Host\ncd $root\ngit checkout $env:SourceVersion 2>&1 | Write-Host\n\nWrite-Host (\"##vso[task.setvariable variable=Pipeline.SourcesDirectory;]$root\")", "failOnStandardError": "true" } }, { + "environment": {}, "enabled": true, "continueOnError": false, "alwaysRun": false, "displayName": "sync -ab", "timeoutInMinutes": 0, + "refName": "Task5", "task": { "id": "e213ff0f-5d5c-4791-802d-52ea3e7be1f1", "versionSpec": "1.*", @@ -93,18 +103,20 @@ "scriptType": "inlineScript", "scriptName": "", "arguments": "$(PB_CloudDropAccountName) $(CloudDropAccessToken) $(PB_Label)", - "inlineScript": "param($account, $token, $container)\n.\\sync.cmd -ab -- /p:CloudDropAccountName=$account /p:CloudDropAccessToken=$token /p:ContainerName=$container", "workingFolder": "$(Pipeline.SourcesDirectory)", + "inlineScript": "param($account, $token, $container)\n.\\sync.cmd -ab -- /p:CloudDropAccountName=$account /p:CloudDropAccessToken=$token /p:ContainerName=$container", "failOnStandardError": "false" } }, { + "environment": {}, "enabled": true, "continueOnError": false, "alwaysRun": false, "displayName": "Inject signed symbol catalogs", "timeoutInMinutes": 0, "condition": "succeeded()", + "refName": "Task6", "task": { "id": "e213ff0f-5d5c-4791-802d-52ea3e7be1f1", "versionSpec": "1.*", @@ -120,11 +132,13 @@ } }, { + "environment": {}, "enabled": true, "continueOnError": false, "alwaysRun": false, "displayName": "Index symbol packages", "timeoutInMinutes": 0, + "refName": "Task7", "task": { "id": "e213ff0f-5d5c-4791-802d-52ea3e7be1f1", "versionSpec": "1.*", @@ -134,17 +148,19 @@ "scriptType": "inlineScript", "scriptName": "", "arguments": "-ConfigGroup $(PB_ConfigurationGroup) -SymPkgGlob $(PB_AzureContainerSymbolPackageGlob) -PipelineSrcDir $(Pipeline.SourcesDirectory)", - "inlineScript": "param($ConfigGroup, $SymPkgGlob, $PipelineSrcDir)\nif ($ConfigGroup -ne \"Release\") { exit }\n\n& $env:Build_SourcesDirectory\\scripts\\DotNet-Trusted-Publish\\Embed-Index.ps1 `\n $PipelineSrcDir\\packages\\AzureTransfer\\$ConfigGroup\\$SymPkgGlob `\n $env:Build_StagingDirectory\\IndexedSymbolPackages", "workingFolder": "", + "inlineScript": "param($ConfigGroup, $SymPkgGlob, $PipelineSrcDir)\nif ($ConfigGroup -ne \"Release\") { exit }\n\n& $env:Build_SourcesDirectory\\scripts\\DotNet-Trusted-Publish\\Embed-Index.ps1 `\n $PipelineSrcDir\\packages\\AzureTransfer\\$ConfigGroup\\$SymPkgGlob `\n $env:Build_StagingDirectory\\IndexedSymbolPackages", "failOnStandardError": "true" } }, { + "environment": {}, "enabled": true, "continueOnError": false, "alwaysRun": false, "displayName": "Generate Version Assets", "timeoutInMinutes": 0, + "refName": "Task8", "task": { "id": "e213ff0f-5d5c-4791-802d-52ea3e7be1f1", "versionSpec": "1.*", @@ -154,17 +170,20 @@ "scriptType": "inlineScript", "scriptName": "", "arguments": "-OfficialBuildId $(OfficialBuildId)", - "inlineScript": "param($OfficialBuildId)\n msbuild build.proj /t:CreateOrUpdateCurrentVersionFile /p:OfficialBuildId=$OfficialBuildId /p:BuildVersionFile=bin\\obj\\BuildVersion-$OfficialBuildId.props", "workingFolder": "$(Pipeline.SourcesDirectory)", + "inlineScript": "param($OfficialBuildId)\n msbuild build.proj /t:CreateOrUpdateCurrentVersionFile /p:OfficialBuildId=$OfficialBuildId /p:BuildVersionFile=bin\\obj\\BuildVersion-$OfficialBuildId.props", "failOnStandardError": "true" } }, { + "environment": {}, "enabled": true, "continueOnError": false, "alwaysRun": false, "displayName": "packages -> dotnet.myget.org", "timeoutInMinutes": 0, + "condition": "and(succeeded(), contains(variables.PB_PublishType, 'myget'), eq(variables.PB_ConfigurationGroup, 'Release'))", + "refName": "Task9", "task": { "id": "e213ff0f-5d5c-4791-802d-52ea3e7be1f1", "versionSpec": "1.*", @@ -173,18 +192,42 @@ "inputs": { "scriptType": "inlineScript", "scriptName": "", - "arguments": "-ApiKey $(MyGetApiKey) -ConfigurationGroup $(PB_ConfigurationGroup) -PackagesGlob $(Pipeline.SourcesDirectory)\\packages\\AzureTransfer\\$(PB_ConfigurationGroup)\\$(PB_AzureContainerPackageGlob) -MyGetFeedUrl $(PB_MyGetFeedUrl)", + "arguments": "-ApiKey $(MyGetApiKey) -PackagesGlob $(Pipeline.SourcesDirectory)\\packages\\AzureTransfer\\$(PB_ConfigurationGroup)\\$(PB_AzureContainerPackageGlob) -MyGetFeedUrl $(PB_MyGetFeedUrl)", "workingFolder": "$(Pipeline.SourcesDirectory)", - "inlineScript": "param($ApiKey, $ConfigurationGroup, $PackagesGlob, $MyGetFeedUrl, $SignType=\"unset\")\n\nif ($ConfigurationGroup.ToLower() -ne \"release\" ) { Write-host \"Chose not to publish\"; exit }\n\n.\\build-managed.cmd -- /t:NuGetPush /v:Normal `\n/p:NuGetExePath=$env:CustomNuGetPath `\n/p:NuGetApiKey=$ApiKey `\n/p:NuGetSource=$MyGetFeedUrl `\n/p:PackagesGlob=$PackagesGlob", + "inlineScript": "param($ApiKey, $PackagesGlob, $MyGetFeedUrl)\n.\\build-managed.cmd -- /t:NuGetPush /v:Normal `\n/p:NuGetExePath=$env:CustomNuGetPath `\n/p:NuGetApiKey=$ApiKey `\n/p:NuGetSource=$MyGetFeedUrl `\n/p:PackagesGlob=$PackagesGlob", "failOnStandardError": "true" } }, { + "environment": {}, + "enabled": true, + "continueOnError": false, + "alwaysRun": false, + "displayName": "packages -> Blob Feed", + "timeoutInMinutes": 0, + "condition": "and(succeeded(), contains(variables.PB_PublishType, 'blob'), eq(variables.PB_ConfigurationGroup, 'Release'))", + "refName": "CmdLine1", + "task": { + "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", + "versionSpec": "1.*", + "definitionType": "task" + }, + "inputs": { + "filename": "msbuild", + "arguments": "src\\publish.proj /t:PublishToAzureBlobFeed $(FeedPublishArguments)", + "workingFolder": "$(Pipeline.SourcesDirectory)", + "failOnStandardError": "false" + } + }, + { + "environment": {}, "enabled": true, "continueOnError": false, "alwaysRun": false, "displayName": "symbol packages -> dotnet.myget.org", "timeoutInMinutes": 0, + "condition": "and(succeeded(), contains(variables.PB_PublishType, 'myget'), eq(variables.PB_ConfigurationGroup, 'Release'))", + "refName": "Task11", "task": { "id": "e213ff0f-5d5c-4791-802d-52ea3e7be1f1", "versionSpec": "1.*", @@ -195,16 +238,40 @@ "scriptName": "", "arguments": "-ApiKey $(MyGetApiKey) -ConfigurationGroup $(PB_ConfigurationGroup) -PackagesGlob $(Build.StagingDirectory)\\IndexedSymbolPackages\\*.nupkg -MyGetFeedUrl $(PB_MyGetFeedUrl)", "workingFolder": "$(Pipeline.SourcesDirectory)", - "inlineScript": "param($ApiKey, $ConfigurationGroup, $PackagesGlob, $MyGetFeedUrl, $SignType=\"unset\")\n\nif ($ConfigurationGroup.ToLower() -ne \"release\" ) { Write-host \"Chose not to publish\"; exit }\nif ($env:SourceBranch.StartsWith(\"release/\")) { exit }\n\n.\\build-managed.cmd -- /t:NuGetPush /v:Normal `\n/p:NuGetExePath=$env:CustomNuGetPath `\n/p:NuGetApiKey=$ApiKey `\n/p:NuGetSource=$MyGetFeedUrl `\n/p:PackagesGlob=$PackagesGlob", + "inlineScript": "param($ApiKey, $ConfigurationGroup, $PackagesGlob, $MyGetFeedUrl)\nif ($env:SourceBranch.StartsWith(\"release/\")) { exit }\n\n.\\build-managed.cmd -- /t:NuGetPush /v:Normal `\n/p:NuGetExePath=$env:CustomNuGetPath `\n/p:NuGetApiKey=$ApiKey `\n/p:NuGetSource=$MyGetFeedUrl `\n/p:PackagesGlob=$PackagesGlob", "failOnStandardError": "true" } }, { + "environment": {}, + "enabled": true, + "continueOnError": false, + "alwaysRun": false, + "displayName": "symbol packages -> Blob Feed", + "timeoutInMinutes": 0, + "condition": "and(succeeded(), contains(variables.PB_PublishType, 'blob'), eq(variables.PB_ConfigurationGroup, 'Release'))", + "refName": "CmdLine2", + "task": { + "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", + "versionSpec": "1.*", + "definitionType": "task" + }, + "inputs": { + "filename": "msbuild", + "arguments": "src\\publish.proj /t:PublishSymbolsToAzureBlobFeed /p:PublishSymbols=\"true\" $(FeedPublishArguments)", + "workingFolder": "$(Pipeline.SourcesDirectory)", + "failOnStandardError": "false" + } + }, + { + "environment": {}, "enabled": true, "continueOnError": false, "alwaysRun": false, "displayName": "Update versions repository", "timeoutInMinutes": 0, + "condition": "and(succeeded(), contains(variables.PB_PublishType, 'versions'), eq(variables.PB_ConfigurationGroup, 'Release'))", + "refName": "Task13", "task": { "id": "e213ff0f-5d5c-4791-802d-52ea3e7be1f1", "versionSpec": "1.*", @@ -214,17 +281,19 @@ "scriptType": "inlineScript", "scriptName": "", "arguments": "-ghAuthToken $(PB_DotNetBuildBotAccessToken) -root $(Pipeline.SourcesDirectory) -cg $(PB_ConfigurationGroup) -fullPkgGlob $(Pipeline.SourcesDirectory)\\packages\\AzureTransfer\\$(PB_ConfigurationGroup)\\$(PB_AzureContainerPackageGlob) ", - "inlineScript": "param($ghAuthToken, $root, $cg, $fullPkgGlob, $SignType=\"unset\")\nif ($cg -ne \"Release\" ) { exit }\ncd $root\n. $root\\build-managed.cmd -- /t:UpdatePublishedVersions `\n/p:GitHubUser=dotnet-helix-bot `\n/p:GitHubEmail=dotnet-helix-bot@microsoft.com `\n/p:GitHubAuthToken=$ghAuthToken `\n/p:VersionsRepoOwner=$env:PB_VersionsRepoOwner `\n/p:VersionsRepo=versions `\n/p:VersionsRepoPath=build-info/dotnet/$env:PB_GitHubRepositoryName/$env:SourceBranch `\n/p:ShippedNuGetPackageGlobPath=$fullPkgGlob", "workingFolder": "", + "inlineScript": "param($ghAuthToken, $root, $cg, $fullPkgGlob, $SignType=\"unset\")\nif ($cg -ne \"Release\" ) { exit }\ncd $root\n. $root\\build-managed.cmd -- /t:UpdatePublishedVersions `\n/p:GitHubUser=dotnet-helix-bot `\n/p:GitHubEmail=dotnet-helix-bot@microsoft.com `\n/p:GitHubAuthToken=$ghAuthToken `\n/p:VersionsRepoOwner=$env:PB_VersionsRepoOwner `\n/p:VersionsRepo=versions `\n/p:VersionsRepoPath=build-info/dotnet/$env:PB_GitHubRepositoryName/$env:SourceBranch `\n/p:ShippedNuGetPackageGlobPath=$fullPkgGlob", "failOnStandardError": "true" } }, { + "environment": {}, "enabled": true, "continueOnError": false, "alwaysRun": false, "displayName": "Get Build Number", "timeoutInMinutes": 0, + "refName": "Task14", "task": { "id": "e213ff0f-5d5c-4791-802d-52ea3e7be1f1", "versionSpec": "1.*", @@ -234,17 +303,19 @@ "scriptType": "inlineScript", "scriptName": "", "arguments": "$(OfficialBuildId) $(Pipeline.SourcesDirectory)", - "inlineScript": "param(\n [string]$OfficialBuildId,\n [string]$SourcesDir\n)\n$VersionPropsFile=$SourcesDir + \"\\bin\\obj\\BuildVersion-\" + $OfficialBuildId + \".props\"\n[xml]$versionXml=Get-Content $VersionPropsFile\n$env:BuildNumber=$versionXml.Project.PropertyGroup.BuildNumberMajor.InnerText + \".\" + $versionXml.Project.PropertyGroup.BuildNumberMinor.InnerText\nWrite-Host (\"##vso[task.setvariable variable=BuildNumber;]$env:BuildNumber\")", "workingFolder": "", + "inlineScript": "param(\n [string]$OfficialBuildId,\n [string]$SourcesDir\n)\n$VersionPropsFile=$SourcesDir + \"\\bin\\obj\\BuildVersion-\" + $OfficialBuildId + \".props\"\n[xml]$versionXml=Get-Content $VersionPropsFile\n$env:BuildNumber=$versionXml.Project.PropertyGroup.BuildNumberMajor.InnerText + \".\" + $versionXml.Project.PropertyGroup.BuildNumberMinor.InnerText\nWrite-Host (\"##vso[task.setvariable variable=BuildNumber;]$env:BuildNumber\")", "failOnStandardError": "true" } }, { + "environment": {}, "enabled": true, "continueOnError": true, "alwaysRun": false, "displayName": "Publish to Artifact Services Drop", "timeoutInMinutes": 0, + "refName": "Task15", "task": { "id": "f9d96d25-0c81-4e77-8282-1ad1f785cbb4", "versionSpec": "0.*", @@ -263,12 +334,14 @@ } }, { + "environment": {}, "enabled": true, "continueOnError": false, "alwaysRun": false, "displayName": "Send Telemetry", "timeoutInMinutes": 0, "condition": "always()", + "refName": "Task16", "task": { "id": "521a94ea-9e68-468a-8167-6dcf361ea776", "versionSpec": "1.*", @@ -278,18 +351,6 @@ } ], "options": [ - { - "enabled": false, - "definition": { - "id": "7c555368-ca64-4199-add6-9ebaf0b0137d" - }, - "inputs": { - "multipliers": "[]", - "parallel": "false", - "continueOnError": "true", - "additionalFields": "{}" - } - }, { "enabled": false, "definition": { @@ -309,6 +370,16 @@ "inputs": { "additionalFields": "{}" } + }, + { + "enabled": false, + "definition": { + "id": "5d58cc01-7c75-450c-be18-a388ddb129ec" + }, + "inputs": { + "branchFilters": "[\"+refs/heads/*\"]", + "additionalFields": "{}" + } } ], "variables": { @@ -423,6 +494,9 @@ "value": "master", "allowOverride": true }, + "FeedPublishArguments": { + "value": "$(PB_BuildOutputManifestArguments) /p:AccountKey=$(PB_PublishBlobFeedKey) /p:ExpectedFeedUrl=$(PB_PublishBlobFeedUrl) /p:ConfigurationGroup=$(PB_ConfigurationGroup)" + }, "PB_AzureContainerPackageGlob": { "value": "*.nupkg", "allowOverride": true @@ -441,6 +515,20 @@ }, "PB_ToolPackageSource": { "value": "https://www.myget.org/F/dagood-test-buildtools/api/v3/index.json" + }, + "PB_PublishBlobFeedUrl": { + "value": "", + "allowOverride": true + }, + "PB_PublishBlobFeedKey": { + "value": null, + "isSecret": true + }, + "PB_PublishType": { + "value": "" + }, + "PB_BuildOutputManifestArguments": { + "value": "/p:ManifestBuildId=$(OfficialBuildId) /p:ManifestBranch=$(SourceBranch) /p:ManifestCommit=$(SourceVersion)" } }, "retentionRules": [ @@ -462,6 +550,7 @@ "buildNumberFormat": "$(date:yyyyMMdd)$(rev:-rr)", "jobAuthorizationScope": "projectCollection", "jobTimeoutInMinutes": 180, + "jobCancelTimeoutInMinutes": 5, "repository": { "properties": { "labelSources": "0", @@ -469,7 +558,9 @@ "fetchDepth": "0", "gitLfsSupport": "false", "skipSyncSource": "false", - "cleanOptions": "0" + "cleanOptions": "0", + "checkoutNestedSubmodules": "false", + "labelSourcesFormat": "$(build.buildNumber)" }, "id": "0a2b2664-c1be-429c-9b40-8a24dee27a4a", "type": "TfsGit", @@ -479,25 +570,29 @@ "clean": "true", "checkoutSubmodules": false }, + "processParameters": {}, "quality": "definition", + "drafts": [], "queue": { + "id": 36, + "name": "DotNet-Build", "pool": { "id": 39, "name": "DotNet-Build" - }, - "id": 36, - "name": "DotNet-Build" + } }, - "path": "\\", - "type": "build", "id": 2943, "name": "DotNet-Trusted-Publish", + "path": "\\", + "type": "build", + "queueStatus": "enabled", "project": { "id": "0bdbc590-a062-4c3f-b0f6-9383f67865ee", "name": "DevDiv", "description": "Visual Studio and DevDiv team project for git source code repositories. Work items will be added for Adams, Dev14 work items are tracked in vstfdevdiv. ", "url": "https://devdiv.visualstudio.com/DefaultCollection/_apis/projects/0bdbc590-a062-4c3f-b0f6-9383f67865ee", "state": "wellFormed", - "revision": 418097459 + "revision": 418098311, + "visibility": "organization" } } diff --git a/external/corefx/buildpipeline/alpine.3.6.groovy b/external/corefx/buildpipeline/alpine.3.6.groovy new file mode 100644 index 0000000000..08ed7cb4af --- /dev/null +++ b/external/corefx/buildpipeline/alpine.3.6.groovy @@ -0,0 +1,39 @@ +@Library('dotnet-ci') _ + +// Incoming parameters. Access with "params.". +// Note that the parameters will be set as env variables so we cannot use names that conflict +// with the engineering system parameter names. +// CGroup - Build configuration. +// TestOuter - If true, runs outerloop, if false runs just innerloop + +simpleDockerNode('microsoft/dotnet-buildtools-prereqs:alpine-3.6-3148f11-20171119021156') { + stage ('Checkout source') { + checkoutRepo() + } + + stage ('Initialize tools') { + // Init tools + sh './init-tools.sh' + } + stage ('Generate version assets') { + // Generate the version assets. Do we need to even do this for non-official builds? + sh "./build-managed.sh -runtimeos=alpine.3.6 -- /t:GenerateVersionSourceFile /p:GenerateVersionSourceFile=true /p:PortableBuild=false" + } + stage ('Sync') { + sh "./sync.sh -p -runtimeos=alpine.3.6 -- /p:ArchGroup=x64 /p:PortableBuild=false" + } + stage ('Build Product') { + sh "./build.sh -buildArch=x64 -runtimeos=alpine.3.6 -${params.CGroup} -- /p:PortableBuild=false" + } + stage ('Build Tests') { + def additionalArgs = '' + if (params.TestOuter) { + additionalArgs = '-Outerloop' + } + sh "./build-tests.sh -buildArch=x64 -${params.CGroup} -SkipTests ${additionalArgs} -- /p:ArchiveTests=true /p:EnableDumpling=true /p:PortableBuild=false" + } + + // TODO: Add submission for Helix testing once we have queue for Alpine Linux working +} + +// TODO: Add "Execute tests" stage once we have queue for Alpine Linux working diff --git a/external/corefx/buildpipeline/linux.groovy b/external/corefx/buildpipeline/linux.groovy index f664cc8f26..c510d48fa0 100644 --- a/external/corefx/buildpipeline/linux.groovy +++ b/external/corefx/buildpipeline/linux.groovy @@ -52,13 +52,12 @@ simpleDockerNode('microsoft/dotnet-buildtools-prereqs:rhel7_prereqs_2') { 'Debian.87.Amd64.Open', 'Ubuntu.1404.Amd64.Open', 'Ubuntu.1604.Amd64.Open', - 'suse.422.amd64.Open', + 'opensuse.422.amd64.open', 'fedora.25.amd64.Open',] if (params.TestOuter) { targetHelixQueues += ['Debian.90.Amd64.Open', 'Fedora.26.Amd64.Open', - 'SLES.12.Amd64.Open', - 'Ubuntu.1704.Amd64.Open',] + 'SLES.12.Amd64.Open',] } sh "./Tools/msbuild.sh src/upload-tests.proj /p:ArchGroup=x64 /p:ConfigurationGroup=${params.CGroup} /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Linux /p:HelixJobType=test/functional/cli/ /p:HelixSource=${helixSource} /p:BuildMoniker=${helixBuild} /p:HelixCreator=${helixCreator} /p:CloudDropAccountName=dotnetbuilddrops /p:CloudResultsAccountName=dotnetjobresults /p:CloudDropAccessToken=\$CloudDropAccessToken /p:CloudResultsAccessToken=\$OutputCloudResultsAccessToken /p:HelixApiEndpoint=https://helix.dot.net/api/2017-04-14/jobs /p:TargetQueues=${targetHelixQueues.join('+')} /p:HelixLogFolder=${WORKSPACE}/${logFolder}/ /p:HelixCorrelationInfoFileName=SubmittedHelixRuns.txt" diff --git a/external/corefx/buildpipeline/pipeline.json b/external/corefx/buildpipeline/pipeline.json index 2210ed41ee..ad222739f5 100644 --- a/external/corefx/buildpipeline/pipeline.json +++ b/external/corefx/buildpipeline/pipeline.json @@ -7,45 +7,58 @@ }, "Pipelines": [ { - "Name": "Trusted-All-Release-Linux", + "Name": "Trusted-All-Linux", "Parameters": { "TreatWarningsAsErrors": "false" }, - "BuildParameters": { - "PB_ConfigurationGroup": "Release" - }, "Definitions": [{ "Name": "DotNet-CoreFx-Trusted-Linux", "Parameters": { "PB_DockerTag": "centos-7-d485f41-20173404063424", - "PB_BuildArguments": "-buildArch=x64 -Release -stripSymbols", - "PB_BuildTestsArguments": "-buildArch=x64 -Release -SkipTests -Outerloop -- /p:ArchiveTests=true /p:EnableDumpling=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=x64", + "PB_BuildArguments": "-buildArch=x64 -$(PB_ConfigurationGroup) -stripSymbols", + "PB_BuildTestsArguments": "-buildArch=x64 -$(PB_ConfigurationGroup) -SkipTests -Outerloop -- /p:ArchiveTests=true /p:EnableDumpling=true", + "PB_SyncArguments": "-p -- /p:ArchGroup=x64 /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)", "PB_TargetQueue": "Centos.73.Amd64+RedHat.72.Amd64+RedHat.73.Amd64+Debian.87.Amd64+Debian.90.Amd64+Ubuntu.1404.Amd64+Ubuntu.1604.Amd64+Ubuntu.1704.Amd64+Ubuntu.1710.Amd64+suse.422.amd64+SLES.12.Amd64+fedora.25.amd64+Fedora.26.Amd64", - "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=Release /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Linux" + "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=$(PB_ConfigurationGroup) /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Linux" }, "ReportingParameters": { "OperatingSystem": "Linux", "Platform": "x64", - "Type": "build/product/", - "ConfigurationGroup": "Release" + "Type": "build/product/" } }, { "Name": "DotNet-CoreFx-Trusted-Linux", "Parameters": { "PB_DockerTag": "centos-6-376e1a3-20174311014331", - "PB_BuildArguments": "-buildArch=x64 -Release -stripSymbols -RuntimeOS=rhel.6 -- /p:PortableBuild=false", - "PB_BuildTestsArguments": "-buildArch=x64 -Release -SkipTests -Outerloop -RuntimeOS=rhel.6 -- /p:ArchiveTests=true /p:EnableDumpling=true /p:PortableBuild=false", - "PB_SyncArguments": "-p -RuntimeOS=rhel.6 -- /p:ArchGroup=x64 /p:PortableBuild=false", + "PB_BuildArguments": "-buildArch=x64 -$(PB_ConfigurationGroup) -stripSymbols -RuntimeOS=rhel.6 -- /p:PortableBuild=false", + "PB_BuildTestsArguments": "-buildArch=x64 -$(PB_ConfigurationGroup) -SkipTests -Outerloop -RuntimeOS=rhel.6 -- /p:ArchiveTests=true /p:EnableDumpling=true /p:PortableBuild=false", + "PB_SyncArguments": "-p -RuntimeOS=rhel.6 -- /p:ArchGroup=x64 /p:PortableBuild=false /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)", "PB_TargetQueue": "RedHat.69.Amd64", - "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=Release /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Linux" + "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=$(PB_ConfigurationGroup) /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Linux" }, "ReportingParameters": { "OperatingSystem": "RedHat6", "Platform": "x64", - "Type": "build/product/", - "ConfigurationGroup": "Release" + "Type": "build/product/" + } + }, + { + "Name": "DotNet-CoreFx-Trusted-Linux", + "Parameters": { + "PB_DockerTag": "alpine-3.6-3148f11-20171119021156", + "PB_BuildArguments": "-buildArch=x64 -$(PB_ConfigurationGroup) -BuildTests=false -stripSymbols -RuntimeOS=alpine.3.6 -- /p:PortableBuild=false", + "PB_SkipTestBuild" : "true", + "PB_BuildTestsArguments": "-buildArch=x64 -$(PB_ConfigurationGroup) -SkipTests -Outerloop -RuntimeOS=alpine.3.6 -- /p:ArchiveTests=false /p:EnableDumpling=true /p:PortableBuild=false", + "PB_SyncArguments": "-p -BuildTests=false -RuntimeOS=alpine.3.6 -- /p:ArchGroup=x64 /p:PortableBuild=false /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)", + "PB_TargetQueue": "Alpine.36.Amd64", + "PB_EnableCloudTest" : "false", + "PB_CreateHelixArguments": "/p:EnableCloudTest=$(PB_EnableCloudTest) /p:ArchGroup=x64 /p:ConfigurationGroup=$(PB_ConfigurationGroup) /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Linux" + }, + "ReportingParameters": { + "OperatingSystem": "Alpine3.6", + "Platform": "x64", + "Type": "build/product/" } }, { @@ -53,53 +66,50 @@ "Parameters": { "PB_DockerTag": "ubuntu-14.04-cross-0cd4667-20170319080304", "PB_Architecture": "arm", - "PB_BuildArguments": "-buildArch=arm -Release -stripSymbols", - "PB_SyncArguments": "-p -- /p:ArchGroup=arm" + "PB_BuildArguments": "-buildArch=arm -$(PB_ConfigurationGroup) -stripSymbols", + "PB_SyncArguments": "-p -- /p:ArchGroup=arm /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)" }, "ReportingParameters": { "OperatingSystem": "Linux", "Platform": "arm", - "Type": "build/product/", - "ConfigurationGroup": "Release" + "Type": "build/product/" } } ] }, { - "Name": "Trusted-All-Release-OSX", + "Name": "Trusted-All-OSX", "Parameters": { "TreatWarningsAsErrors": "false" }, "BuildParameters": { - "PB_BuildArguments": "-buildArch=x64 -Release -stripSymbols", - "PB_BuildTestsArguments": "-buildArch=x64 -Release -SkipTests -Outerloop -- /p:ArchiveTests=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=x64", - "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=Release /p:EnableCloudTest=false /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=OSX" + "PB_BuildArguments": "-buildArch=x64 -$(PB_ConfigurationGroup) -stripSymbols", + "PB_BuildTestsArguments": "-buildArch=x64 -$(PB_ConfigurationGroup) -SkipTests -Outerloop -- /p:ArchiveTests=true", + "PB_SyncArguments": "-p -- /p:ArchGroup=x64 /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)", + "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=$(PB_ConfigurationGroup) /p:EnableCloudTest=false /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=OSX" }, "Definitions": [{ "Name": "DotNet-CoreFx-Trusted-OSX", "Parameters": { - "PB_BuildArguments": "-buildArch=x64 -Release", - "PB_SyncArguments": "-p -- /p:ArchGroup=x64", + "PB_BuildArguments": "-buildArch=x64 -$(PB_ConfigurationGroup)", + "PB_SyncArguments": "-p -- /p:ArchGroup=x64 /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)", "PB_TargetQueue": "OSX.1012.Amd64+OSX.1013.Amd64", - "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=Release /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=OSX" + "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=$(PB_ConfigurationGroup) /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=OSX" }, "ReportingParameters": { "OperatingSystem": "OSX", "Platform": "x64", - "Type": "build/product/", - "ConfigurationGroup": "Release" + "Type": "build/product/" } } ] }, { - "Name": "Trusted-All-Release-Windows", + "Name": "Trusted-All-Windows", "Parameters": { "TreatWarningsAsErrors": "false" }, "BuildParameters": { - "PB_ConfigurationGroup": "Release", "PB_PipelineBuildMSBuildArguments": "/p:EnableProfileGuidedOptimization=true" }, "Definitions": [ @@ -107,16 +117,15 @@ "Name": "DotNet-CoreFx-Trusted-Windows", "Parameters": { "PB_Platform": "arm", - "PB_BuildArguments": "-buildArch=arm -Release -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", - "PB_BuildTestsArguments": "-buildArch=arm -Release -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=arm /p:RuntimeOS=win10", - "PB_CreateHelixArguments": "/p:ArchGroup=arm /p:ConfigurationGroup=Release /p:EnableCloudTest=false /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT" + "PB_BuildArguments": "-buildArch=arm -$(PB_ConfigurationGroup) -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", + "PB_BuildTestsArguments": "-buildArch=arm -$(PB_ConfigurationGroup) -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", + "PB_SyncArguments": "-p -- /p:ArchGroup=arm /p:RuntimeOS=win10 /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)", + "PB_CreateHelixArguments": "/p:ArchGroup=arm /p:ConfigurationGroup=$(PB_ConfigurationGroup) /p:EnableCloudTest=false /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT" }, "ReportingParameters": { "OperatingSystem": "Windows", "Type": "build/product/", "Platform": "arm", - "ConfigurationGroup": "Release", "SubType": "netcoreapp" } }, @@ -124,16 +133,15 @@ "Name": "DotNet-CoreFx-Trusted-Windows", "Parameters": { "PB_Platform": "arm64", - "PB_BuildArguments": "-buildArch=arm64 -Release -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", - "PB_BuildTestsArguments": "-buildArch=arm64 -Release -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=arm64 /p:RuntimeOS=win10", - "PB_CreateHelixArguments": "/p:ArchGroup=arm64 /p:ConfigurationGroup=Release /p:EnableCloudTest=false /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT" + "PB_BuildArguments": "-buildArch=arm64 -$(PB_ConfigurationGroup) -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", + "PB_BuildTestsArguments": "-buildArch=arm64 -$(PB_ConfigurationGroup) -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", + "PB_SyncArguments": "-p -- /p:ArchGroup=arm64 /p:RuntimeOS=win10 /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)", + "PB_CreateHelixArguments": "/p:ArchGroup=arm64 /p:ConfigurationGroup=$(PB_ConfigurationGroup) /p:EnableCloudTest=false /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT" }, "ReportingParameters": { "OperatingSystem": "Windows", "Type": "build/product/", "Platform": "arm64", - "ConfigurationGroup": "Release", "SubType": "netcoreapp" } }, @@ -141,17 +149,16 @@ "Name": "DotNet-CoreFx-Trusted-Windows", "Parameters": { "PB_Platform": "x64", - "PB_BuildArguments": "-buildArch=x64 -Release -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", - "PB_BuildTestsArguments": "-buildArch=x64 -Release -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=x64 /p:RuntimeOS=win10", + "PB_BuildArguments": "-buildArch=x64 -$(PB_ConfigurationGroup) -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", + "PB_BuildTestsArguments": "-buildArch=x64 -$(PB_ConfigurationGroup) -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", + "PB_SyncArguments": "-p -- /p:ArchGroup=x64 /p:RuntimeOS=win10 /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)", "PB_TargetQueue": "Windows.10.Amd64+Windows.10.Nano.Amd64+Windows.10.Amd64.Core+Windows.7.Amd64+Windows.81.Amd64", - "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=Release /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT" + "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=$(PB_ConfigurationGroup) /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT" }, "ReportingParameters": { "OperatingSystem": "Windows", "Type": "build/product/", "Platform": "x64", - "ConfigurationGroup": "Release", "SubType": "netcoreapp" } }, @@ -159,17 +166,16 @@ "Name": "DotNet-CoreFx-Trusted-Windows", "Parameters": { "PB_Platform": "x86", - "PB_BuildArguments": "-buildArch=x86 -Release -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", - "PB_BuildTestsArguments": "-buildArch=x86 -Release -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=x86 /p:RuntimeOS=win10", + "PB_BuildArguments": "-buildArch=x86 -$(PB_ConfigurationGroup) -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", + "PB_BuildTestsArguments": "-buildArch=x86 -$(PB_ConfigurationGroup) -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", + "PB_SyncArguments": "-p -- /p:ArchGroup=x86 /p:RuntimeOS=win10 /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)", "PB_TargetQueue": "Windows.10.Amd64+Windows.10.Amd64.Core+Windows.7.Amd64+Windows.81.Amd64", - "PB_CreateHelixArguments": "/p:ArchGroup=x86 /p:ConfigurationGroup=Release /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT" + "PB_CreateHelixArguments": "/p:ArchGroup=x86 /p:ConfigurationGroup=$(PB_ConfigurationGroup) /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT" }, "ReportingParameters": { "OperatingSystem": "Windows", "Type": "build/product/", "Platform": "x86", - "ConfigurationGroup": "Release", "SubType": "netcoreapp" } }, @@ -177,17 +183,16 @@ "Name": "DotNet-CoreFx-Trusted-Windows", "Parameters": { "PB_Platform": "arm", - "PB_BuildArguments": "-framework=uap -buildArch=arm -Release -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", - "PB_BuildTestsArguments": "-framework=uap -buildArch=arm -Release -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=arm /p:RuntimeOS=win10 /p:TargetGroup=uap", + "PB_BuildArguments": "-framework=uap -buildArch=arm -$(PB_ConfigurationGroup) -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", + "PB_BuildTestsArguments": "-framework=uap -buildArch=arm -$(PB_ConfigurationGroup) -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", + "PB_SyncArguments": "-p -- /p:ArchGroup=arm /p:RuntimeOS=win10 /p:TargetGroup=uap /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)", "PB_TargetQueue": "Windows.10.Arm64", - "PB_CreateHelixArguments": "/p:ArchGroup=arm /p:TargetGroup=uap /p:ConfigurationGroup=Release /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/uwp/\"" + "PB_CreateHelixArguments": "/p:ArchGroup=arm /p:TargetGroup=uap /p:ConfigurationGroup=$(PB_ConfigurationGroup) /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/uwp/\"" }, "ReportingParameters": { "OperatingSystem": "Windows", "Type": "build/product/", "Platform": "arm", - "ConfigurationGroup": "Release", "SubType": "uap" } }, @@ -195,17 +200,16 @@ "Name": "DotNet-CoreFx-Trusted-Windows", "Parameters": { "PB_Platform": "x64", - "PB_BuildArguments": "-framework=uap -buildArch=x64 -Release -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", - "PB_BuildTestsArguments": "-framework=uap -buildArch=x64 -Release -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=x64 /p:RuntimeOS=win10 /p:TargetGroup=uap", + "PB_BuildArguments": "-framework=uap -buildArch=x64 -$(PB_ConfigurationGroup) -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", + "PB_BuildTestsArguments": "-framework=uap -buildArch=x64 -$(PB_ConfigurationGroup) -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", + "PB_SyncArguments": "-p -- /p:ArchGroup=x64 /p:RuntimeOS=win10 /p:TargetGroup=uap /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)", "PB_TargetQueue": "Windows.10.Amd64.ClientRS3", - "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:TargetGroup=uap /p:ConfigurationGroup=Release /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/uwp/\"" + "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:TargetGroup=uap /p:ConfigurationGroup=$(PB_ConfigurationGroup) /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/uwp/\"" }, "ReportingParameters": { "OperatingSystem": "Windows", "Type": "build/product/", "Platform": "x64", - "ConfigurationGroup": "Release", "SubType": "uap" } }, @@ -213,17 +217,16 @@ "Name": "DotNet-CoreFx-Trusted-Windows", "Parameters": { "PB_Platform": "x86", - "PB_BuildArguments": "-framework=uap -buildArch=x86 -Release -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", - "PB_BuildTestsArguments": "-framework=uap -buildArch=x86 -Release -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=x86 /p:RuntimeOS=win10 /p:TargetGroup=uap", + "PB_BuildArguments": "-framework=uap -buildArch=x86 -$(PB_ConfigurationGroup) -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", + "PB_BuildTestsArguments": "-framework=uap -buildArch=x86 -$(PB_ConfigurationGroup) -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", + "PB_SyncArguments": "-p -- /p:ArchGroup=x86 /p:RuntimeOS=win10 /p:TargetGroup=uap /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)", "PB_TargetQueue": "Windows.10.Amd64.ClientRS3", - "PB_CreateHelixArguments": "/p:ArchGroup=x86 /p:TargetGroup=uap /p:ConfigurationGroup=Release /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/uwp/\"" + "PB_CreateHelixArguments": "/p:ArchGroup=x86 /p:TargetGroup=uap /p:ConfigurationGroup=$(PB_ConfigurationGroup) /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/uwp/\"" }, "ReportingParameters": { "OperatingSystem": "Windows", "Type": "build/product/", "Platform": "x86", - "ConfigurationGroup": "Release", "SubType": "uap" } }, @@ -231,17 +234,30 @@ "Name": "DotNet-CoreFx-Trusted-Windows", "Parameters": { "PB_Platform": "arm", - "PB_BuildArguments": "-framework=uapaot -buildArch=arm -Release -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", - "PB_BuildTestsArguments": "-framework=uapaot -buildArch=arm -Release -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=arm /p:RuntimeOS=win10 /p:TargetGroup=uapaot", + "PB_BuildArguments": "-framework=uapaot -buildArch=arm -$(PB_ConfigurationGroup) -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10 /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)", + "PB_BuildTestsArguments": "-framework=uapaot -buildArch=arm -$(PB_ConfigurationGroup) -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", + "PB_SyncArguments": "-p -- /p:ArchGroup=arm /p:RuntimeOS=win10 /p:TargetGroup=uapaot /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)", "PB_TargetQueue": "Windows.10.Amd64", - "PB_CreateHelixArguments": "/p:ArchGroup=arm /p:TargetGroup=uapaot /p:ConfigurationGroup=Release /p:SecondaryQueue=Windows.10.Arm64 /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/ilc/\"" + "PB_CreateHelixArguments": "/p:ArchGroup=arm /p:TargetGroup=uapaot /p:ConfigurationGroup=$(PB_ConfigurationGroup) /p:SecondaryQueue=Windows.10.Arm64 /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/ilc/\"" }, "ReportingParameters": { "OperatingSystem": "Windows", "Type": "build/product/", "Platform": "arm", - "ConfigurationGroup": "Release", + "SubType": "uapaot" + } + }, + { + "Name": "DotNet-CoreFx-Trusted-Windows-NoTest", + "Parameters": { + "PB_Platform": "arm64", + "PB_BuildArguments": "-framework=uapaot -buildArch=arm64 -$(PB_ConfigurationGroup) -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10 /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)", + "PB_SyncArguments": "-p -- /p:ArchGroup=arm64 /p:RuntimeOS=win10 /p:TargetGroup=uapaot /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)", + }, + "ReportingParameters": { + "OperatingSystem": "Windows", + "Type": "build/product/", + "Platform": "arm64", "SubType": "uapaot" } }, @@ -249,17 +265,16 @@ "Name": "DotNet-CoreFx-Trusted-Windows", "Parameters": { "PB_Platform": "x64", - "PB_BuildArguments": "-framework=uapaot -buildArch=x64 -Release -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", - "PB_BuildTestsArguments": "-framework=uapaot -buildArch=x64 -Release -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=x64 /p:RuntimeOS=win10 /p:TargetGroup=uapaot", + "PB_BuildArguments": "-framework=uapaot -buildArch=x64 -$(PB_ConfigurationGroup) -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10 /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)", + "PB_BuildTestsArguments": "-framework=uapaot -buildArch=x64 -$(PB_ConfigurationGroup) -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", + "PB_SyncArguments": "-p -- /p:ArchGroup=x64 /p:RuntimeOS=win10 /p:TargetGroup=uapaot /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)", "PB_TargetQueue": "Windows.10.Amd64.ClientRS3", - "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:TargetGroup=uapaot /p:ConfigurationGroup=Release /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/ilc/\"" + "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:TargetGroup=uapaot /p:ConfigurationGroup=$(PB_ConfigurationGroup) /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/ilc/\"" }, "ReportingParameters": { "OperatingSystem": "Windows", "Type": "build/product/", "Platform": "x64", - "ConfigurationGroup": "Release", "SubType": "uapaot" } }, @@ -267,17 +282,16 @@ "Name": "DotNet-CoreFx-Trusted-Windows", "Parameters": { "PB_Platform": "x86", - "PB_BuildArguments": "-framework=uapaot -buildArch=x86 -Release -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", - "PB_BuildTestsArguments": "-framework=uapaot -buildArch=x86 -Release -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=x86 /p:RuntimeOS=win10", + "PB_BuildArguments": "-framework=uapaot -buildArch=x86 -$(PB_ConfigurationGroup) -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10 /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)", + "PB_BuildTestsArguments": "-framework=uapaot -buildArch=x86 -$(PB_ConfigurationGroup) -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", + "PB_SyncArguments": "-p -- /p:ArchGroup=x86 /p:RuntimeOS=win10 /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)", "PB_TargetQueue": "Windows.10.Amd64.ClientRS3", - "PB_CreateHelixArguments": "/p:ArchGroup=x86 /p:TargetGroup=uapaot /p:ConfigurationGroup=Release /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/ilc/\"" + "PB_CreateHelixArguments": "/p:ArchGroup=x86 /p:TargetGroup=uapaot /p:ConfigurationGroup=$(PB_ConfigurationGroup) /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/ilc/\"" }, "ReportingParameters": { "OperatingSystem": "Windows", "Type": "build/product/", "Platform": "x86", - "ConfigurationGroup": "Release", "SubType": "uapaot" } }, @@ -285,14 +299,13 @@ "Name": "DotNet-CoreFx-Trusted-Windows-NoTest", "Parameters": { "PB_Platform": "x64", - "PB_BuildArguments": "-allConfigurations -buildArch=x64 -Release -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", - "PB_SyncArguments": "-p -- /p:ArchGroup=x64 /p:RuntimeOS=win10" + "PB_BuildArguments": "-allConfigurations -buildArch=x64 -$(PB_ConfigurationGroup) -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", + "PB_SyncArguments": "-p -- /p:ArchGroup=x64 /p:RuntimeOS=win10 /p:BuildAllConfigurations=true /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)" }, "ReportingParameters": { "OperatingSystem": "Windows", "Type": "build/product/", "Platform": "x64", - "ConfigurationGroup": "Release", "SubType": "AllConfigurations" } }, @@ -300,17 +313,16 @@ "Name": "DotNet-CoreFx-Trusted-Windows", "Parameters": { "PB_Platform": "x64", - "PB_BuildArguments": "-framework=netfx -buildArch=x64 -Release -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", - "PB_BuildTestsArguments": "-framework=netfx -buildArch=x64 -Release -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=x64 /p:RuntimeOS=win10 /p:TargetGroup=netfx", + "PB_BuildArguments": "-framework=netfx -buildArch=x64 -$(PB_ConfigurationGroup) -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", + "PB_BuildTestsArguments": "-framework=netfx -buildArch=x64 -$(PB_ConfigurationGroup) -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", + "PB_SyncArguments": "-p -- /p:ArchGroup=x64 /p:RuntimeOS=win10 /p:TargetGroup=netfx /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)", "PB_TargetQueue": "Windows.10.Amd64", - "PB_CreateHelixArguments": "/p:EnableCloudTest=false /p:ArchGroup=x64 /p:TargetGroup=netfx /p:ConfigurationGroup=Release /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/desktop/cli/\"" + "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:TargetGroup=netfx /p:ConfigurationGroup=$(PB_ConfigurationGroup) /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/desktop/cli/\"" }, "ReportingParameters": { "OperatingSystem": "Windows", "Type": "build/product/", "Platform": "x64", - "ConfigurationGroup": "Release", "SubType": "netfx" } }, @@ -318,283 +330,26 @@ "Name": "DotNet-CoreFx-Trusted-Windows", "Parameters": { "PB_Platform": "x86", - "PB_BuildArguments": "-framework=netfx -buildArch=x86 -Release -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", - "PB_BuildTestsArguments": "-framework=netfx -buildArch=x86 -Release -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=x86 /p:RuntimeOS=win10 /p:TargetGroup=netfx", + "PB_BuildArguments": "-framework=netfx -buildArch=x86 -$(PB_ConfigurationGroup) -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", + "PB_BuildTestsArguments": "-framework=netfx -buildArch=x86 -$(PB_ConfigurationGroup) -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", + "PB_SyncArguments": "-p -- /p:ArchGroup=x86 /p:RuntimeOS=win10 /p:TargetGroup=netfx /p:DotNetRestoreSources=$(PB_RestoreSource) /p:DotNetAssetRootUrl=$(PB_AssetRootUrl)", "PB_TargetQueue": "Windows.10.Amd64", - "PB_CreateHelixArguments": "/p:EnableCloudTest=false /p:ArchGroup=x86 /p:TargetGroup=netfx /p:ConfigurationGroup=Release /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/desktop/cli/\"" + "PB_CreateHelixArguments": "/p:ArchGroup=x86 /p:TargetGroup=netfx /p:ConfigurationGroup=$(PB_ConfigurationGroup) /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/desktop/cli/\"" }, "ReportingParameters": { "OperatingSystem": "Windows", "Type": "build/product/", "Platform": "x86", - "ConfigurationGroup": "Release", "SubType": "netfx" } } ] }, { - "Name": "Trusted-All-Debug-Linux", + "Name": "Publish Packages", "Parameters": { "TreatWarningsAsErrors": "false" }, - "BuildParameters": { - "PB_ConfigurationGroup": "Debug" - }, - "Definitions": [{ - "Name": "DotNet-CoreFx-Trusted-Linux", - "Parameters": { - "PB_DockerTag": "centos-7-34f1db9-20171620021620", - "PB_BuildArguments": "-buildArch=x64 -Debug", - "PB_BuildTestsArguments": "-buildArch=x64 -Debug -SkipTests -Outerloop -- /p:ArchiveTests=true /p:EnableDumpling=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=x64", - "PB_TargetQueue": "Centos.73.Amd64+RedHat.72.Amd64+RedHat.73.Amd64+Debian.87.Amd64+Debian.90.Amd64+Ubuntu.1404.Amd64+Ubuntu.1604.Amd64+Ubuntu.1704.Amd64+Ubuntu.1710.Amd64+suse.422.amd64+SLES.12.Amd64+fedora.25.amd64+Fedora.26.Amd64", - "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=Debug /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Linux" - }, - "ReportingParameters": { - "OperatingSystem": "Linux", - "Platform": "x64", - "Type": "build/product/", - "ConfigurationGroup": "Debug" - } - }, - { - "Name": "DotNet-CoreFx-Trusted-Linux", - "Parameters": { - "PB_DockerTag": "centos-6-c8c9b08-20174310104313", - "PB_BuildArguments": "-buildArch=x64 -Debug -RuntimeOS=rhel.6 -- /p:PortableBuild=false", - "PB_BuildTestsArguments": "-buildArch=x64 -Debug -SkipTests -Outerloop -RuntimeOS=rhel.6 -- /p:ArchiveTests=true /p:EnableDumpling=true /p:PortableBuild=false", - "PB_SyncArguments": "-p -RuntimeOS=rhel.6 -- /p:ArchGroup=x64 /p:PortableBuild=false", - "PB_TargetQueue": "RedHat.69.Amd64", - "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=Debug /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Linux" - }, - "ReportingParameters": { - "OperatingSystem": "RedHat6", - "Platform": "x64", - "Type": "build/product/", - "ConfigurationGroup": "Debug" - } - }, - { - "Name": "DotNet-CoreFx-Trusted-Linux-Crossbuild", - "Parameters": { - "PB_DockerTag": "ubuntu-14.04-cross-0cd4667-20170319080304", - "PB_Architecture": "arm", - "PB_BuildArguments": "-buildArch=arm -Debug", - "PB_SyncArguments": "-p -- /p:ArchGroup=arm" - }, - "ReportingParameters": { - "OperatingSystem": "Linux", - "Platform": "arm", - "Type": "build/product/", - "ConfigurationGroup": "Debug" - } - } - ] - }, - { - "Name": "Trusted-All-Debug-OSX", - "Parameters": { - "TreatWarningsAsErrors": "false" - }, - "BuildParameters": { - "PB_BuildArguments": "-buildArch=x64 -Debug", - "PB_BuildTestsArguments": "-buildArch=x64 -Debug -SkipTests -Outerloop -- /p:ArchiveTests=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=x64", - "PB_CreateHelixArguments": "/p:EnableCloudTest=false /p:ArchGroup=x64 /p:ConfigurationGroup=Debug /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:\"TargetOS=OSX\"" - }, - "Definitions": [{ - "Name": "DotNet-CoreFx-Trusted-OSX", - "Parameters": { - "PB_TargetQueue": "OSX.1012.Amd64+OSX.1013.Amd64" - }, - "ReportingParameters": { - "OperatingSystem": "OSX", - "Platform": "x64", - "Type": "build/product/", - "ConfigurationGroup": "Debug" - } - }] - }, - { - "Name": "Trusted-All-Debug-Windows", - "Parameters": { - "TreatWarningsAsErrors": "false" - }, - "BuildParameters": { - "PB_ConfigurationGroup": "Debug" - }, - "Definitions": [ - { - "Name": "DotNet-CoreFx-Trusted-Windows", - "Parameters": { - "PB_Platform": "arm", - "PB_BuildArguments": "-framework=uap -buildArch=arm -Debug -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", - "PB_BuildTestsArguments": "-framework=uap -buildArch=arm -Debug -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=arm /p:RuntimeOS=win10 /p:TargetGroup=uap", - "PB_TargetQueue": "Windows.10.Arm64", - "PB_CreateHelixArguments": "/p:ArchGroup=arm /p:TargetGroup=uap /p:ConfigurationGroup=Debug /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/uwp/\"" - }, - "ReportingParameters": { - "OperatingSystem": "Windows", - "Type": "build/product/", - "Platform": "arm", - "ConfigurationGroup": "Debug", - "SubType": "uap" - } - }, - { - "Name": "DotNet-CoreFx-Trusted-Windows", - "Parameters": { - "PB_Platform": "x64", - "PB_BuildArguments": "-framework=uap -buildArch=x64 -Debug -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", - "PB_BuildTestsArguments": "-framework=uap -buildArch=x64 -Debug -SkipTests -- /p:RuntimeOS=win10 /p:ArchiveTests=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=x64 /p:RuntimeOS=win10 /p:TargetGroup=uap", - "PB_TargetQueue": "Windows.10.Amd64.ClientRS3", - "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:TargetGroup=uap /p:ConfigurationGroup=Debug /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/uwp/\"" - }, - "ReportingParameters": { - "OperatingSystem": "Windows", - "Type": "build/product/", - "Platform": "x64", - "ConfigurationGroup": "Debug", - "SubType": "uap" - } - }, - { - "Name": "DotNet-CoreFx-Trusted-Windows", - "Parameters": { - "PB_Platform": "x86", - "PB_BuildArguments": "-framework=uap -buildArch=x86 -Debug -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", - "PB_BuildTestsArguments": "-framework=uap -buildArch=x86 -Debug -SkipTests -- /p:RuntimeOS=win10 /p:ArchiveTests=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=x86 /p:RuntimeOS=win10 /p:TargetGroup=uap", - "PB_TargetQueue": "Windows.10.Amd64.ClientRS3", - "PB_CreateHelixArguments": "/p:ArchGroup=x86 /p:TargetGroup=uap /p:ConfigurationGroup=Debug /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/uwp/\"" - }, - "ReportingParameters": { - "OperatingSystem": "Windows", - "Type": "build/product/", - "Platform": "x86", - "ConfigurationGroup": "Debug", - "SubType": "uap" - } - }, - { - "Name": "DotNet-CoreFx-Trusted-Windows", - "Parameters": { - "PB_Platform": "arm", - "PB_BuildArguments": "-framework=uapaot -buildArch=arm -Debug -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", - "PB_BuildTestsArguments": "-framework=uapaot -buildArch=arm -Debug -SkipTests -- /p:RuntimeOS=win10 /p:ArchiveTests=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=arm /p:RuntimeOS=win10 /p:TargetGroup=uapaot", - "PB_TargetQueue": "Windows.10.Amd64", - "PB_CreateHelixArguments": "/p:ArchGroup=arm /p:TargetGroup=uapaot /p:ConfigurationGroup=Debug /p:SecondaryQueue=Windows.10.Arm64 /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/ilc/\"" - }, - "ReportingParameters": { - "OperatingSystem": "Windows", - "Type": "build/product/", - "Platform": "arm", - "ConfigurationGroup": "Debug", - "SubType": "uapaot" - } - }, - { - "Name": "DotNet-CoreFx-Trusted-Windows", - "Parameters": { - "PB_Platform": "x64", - "PB_BuildArguments": "-framework:uapaot -buildArch=x64 -Debug -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", - "PB_BuildTestsArguments": "-framework=uapaot -buildArch=x64 -Debug -SkipTests -- /p:RuntimeOS=win10 /p:ArchiveTests=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=x64 /p:RuntimeOS=win10 /p:TargetGroup=uapaot", - "PB_TargetQueue": "Windows.10.Amd64.ClientRS3", - "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:TargetGroup=uapaot /p:ConfigurationGroup=Debug /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/ilc/\"" - }, - "ReportingParameters": { - "OperatingSystem": "Windows", - "Type": "build/product/", - "Platform": "x64", - "ConfigurationGroup": "Debug", - "SubType": "uapaot" - } - }, - { - "Name": "DotNet-CoreFx-Trusted-Windows", - "Parameters": { - "PB_Platform": "x86", - "PB_BuildArguments": "-framework:uapaot -buildArch=x86 -Debug -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", - "PB_BuildTestsArguments": "-framework=uapaot -buildArch=x86 -Debug -SkipTests -- /p:RuntimeOS=win10 /p:ArchiveTests=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=x86 /p:RuntimeOS=win10", - "PB_TargetQueue": "Windows.10.Amd64.ClientRS3", - "PB_CreateHelixArguments": "/p:ArchGroup=x86 /p:TargetGroup=uapaot /p:ConfigurationGroup=Debug /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/ilc/\"" - }, - "ReportingParameters": { - "OperatingSystem": "Windows", - "Type": "build/product/", - "Platform": "x86", - "ConfigurationGroup": "Debug", - "SubType": "uapaot" - } - }, - { - "Name": "DotNet-CoreFx-Trusted-Windows-NoTest", - "Parameters": { - "PB_Platform": "x64", - "PB_BuildArguments": "-allConfigurations -buildArch=x64 -Debug -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", - "PB_SyncArguments": "-p -- /p:ArchGroup=x64 /p:RuntimeOS=win10" - }, - "ReportingParameters": { - "OperatingSystem": "Windows", - "Type": "build/product/", - "Platform": "x64", - "ConfigurationGroup": "Debug", - "SubType": "AllConfigurations" - } - }, - { - "Name": "DotNet-CoreFx-Trusted-Windows", - "Parameters": { - "PB_Platform": "x64", - "PB_BuildArguments": "-framework=netfx -buildArch=x64 -Debug -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", - "PB_BuildTestsArguments": "-framework=netfx -buildArch=x64 -Debug -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=x64 /p:RuntimeOS=win10 /p:TargetGroup=netfx", - "PB_TargetQueue": "Windows.10.Amd64", - "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:TargetGroup=netfx /p:ConfigurationGroup=Debug /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/desktop/cli/\"" - }, - "ReportingParameters": { - "OperatingSystem": "Windows", - "Type": "build/product/", - "Platform": "x64", - "ConfigurationGroup": "Debug", - "SubType": "netfx" - } - }, - { - "Name": "DotNet-CoreFx-Trusted-Windows", - "Parameters": { - "PB_Platform": "x86", - "PB_BuildArguments": "-framework=netfx -buildArch=x86 -Debug -- /p:SignType=$(PB_SignType) /p:RuntimeOS=win10", - "PB_BuildTestsArguments": "-framework=netfx -buildArch=x86 -Debug -SkipTests -Outerloop -- /p:RuntimeOS=win10 /p:ArchiveTests=true", - "PB_SyncArguments": "-p -- /p:ArchGroup=x86 /p:RuntimeOS=win10 /p:TargetGroup=netfx", - "PB_TargetQueue": "Windows.10.Amd64", - "PB_CreateHelixArguments": "/p:ArchGroup=x86 /p:TargetGroup=netfx /p:ConfigurationGroup=Debug /p:TestProduct=corefx /p:TimeoutInSeconds=1200 /p:TargetOS=Windows_NT /p:\"HelixJobType=test/functional/desktop/cli/\"" - }, - "ReportingParameters": { - "OperatingSystem": "Windows", - "Type": "build/product/", - "Platform": "x86", - "ConfigurationGroup": "Debug", - "SubType": "netfx" - } - } - ] - }, - { - "Name": "Publish Packages to Feeds - Release", - "Parameters": { - "TreatWarningsAsErrors": "false" - }, - "BuildParameters": { - "PB_ConfigurationGroup": "Release" - }, "Definitions": [{ "Name": "DotNet-Trusted-Publish", "Parameters": { @@ -604,49 +359,20 @@ "ReportingParameters": { "TaskName": "Package Publish", "Type": "build/publish/", - "ConfigurationGroup": "Release - Push to MyGet Feed" + "ConfigurationGroup": "Publish Packages" } }], "DependsOn": [ - "Trusted-All-Release-Windows", - "Trusted-All-Release-OSX", - "Trusted-All-Release-Linux" + "Trusted-All-Windows", + "Trusted-All-OSX", + "Trusted-All-Linux" ] }, { - "Name": "Publish Packages to Drop - Debug", + "Name": "Publish Symbols", "Parameters": { "TreatWarningsAsErrors": "false" }, - "BuildParameters": { - "PB_ConfigurationGroup": "Debug" - }, - "Definitions": [{ - "Name": "DotNet-Trusted-Publish", - "Parameters": { - "PB_VsoRepositoryName": "DotNet-CoreFX-Trusted", - "PB_Repo": "corefx" - }, - "ReportingParameters": { - "TaskName": "Package Publish", - "Type": "build/publish/", - "ConfigurationGroup": "Debug - Push to Azure Storage" - } - }], - "DependsOn": [ - "Trusted-All-Debug-Windows", - "Trusted-All-Debug-OSX", - "Trusted-All-Debug-Linux" - ] - }, - { - "Name": "Publish Symbols - Release", - "Parameters": { - "TreatWarningsAsErrors": "false" - }, - "BuildParameters": { - "PB_ConfigurationGroup": "Release" - }, "Definitions": [{ "Name": "DotNet-Trusted-Publish-Symbols", "Parameters": { @@ -655,13 +381,13 @@ "ReportingParameters": { "TaskName": "Symbol Publish", "Type": "build/publish/", - "ConfigurationGroup": "Release - Publish Symbols" + "ConfigurationGroup": "Publish Symbols" } }], "DependsOn": [ - "Trusted-All-Release-Windows", - "Trusted-All-Release-OSX", - "Trusted-All-Release-Linux" + "Trusted-All-Windows", + "Trusted-All-OSX", + "Trusted-All-Linux" ] } ] diff --git a/external/corefx/buildpipeline/pipelinejobs.groovy b/external/corefx/buildpipeline/pipelinejobs.groovy index f91ded76ca..5f2243a4bb 100644 --- a/external/corefx/buildpipeline/pipelinejobs.groovy +++ b/external/corefx/buildpipeline/pipelinejobs.groovy @@ -16,12 +16,14 @@ def branch = GithubBranchName def linPipeline = Pipeline.createPipelineForGithub(this, project, branch, 'buildpipeline/linux.groovy') def centos6Pipeline = Pipeline.createPipelineForGithub(this, project, branch, 'buildpipeline/centos.6.groovy') +def alpine36Pipeline = Pipeline.createPipelineForGithub(this, project, branch, 'buildpipeline/alpine.3.6.groovy') def osxPipeline = Pipeline.createPipelineForGithub(this, project, branch, 'buildpipeline/osx.groovy') def winPipeline = Pipeline.createPipelineForGithub(this, project, branch, 'buildpipeline/windows.groovy') def configurations = [ ['TGroup':"netcoreapp", 'Pipeline':linPipeline, 'Name':'Linux' ,'ForPR':"Release-x64", 'Arch':['x64']], ['TGroup':"netcoreapp", 'Pipeline':centos6Pipeline, 'Name':'CentOS.6' ,'ForPR':"", 'Arch':['x64']], + ['TGroup':"netcoreapp", 'Pipeline':alpine36Pipeline, 'Name':'Alpine.3.6' ,'ForPR':"Debug-x64", 'Arch':['x64']], ['TGroup':"netcoreapp", 'Pipeline':osxPipeline, 'Name':'OSX', 'ForPR':"Debug-x64", 'Arch':['x64']], ['TGroup':"netcoreapp", 'Pipeline':winPipeline, 'Name':'Windows' , 'ForPR':"Debug-x64|Release-x86"], ['TGroup':"netfx", 'Pipeline':winPipeline, 'Name':'NETFX', 'ForPR':"Release-x86"], @@ -62,4 +64,4 @@ JobReport.Report.generateJobReport(out) // Make the call to generate the help job Utilities.createHelperJob(this, project, branch, "Welcome to the ${project} Repository", // This is prepended to the help message - "Have a nice day!") // This is appended to the help message. You might put known issues here. \ No newline at end of file + "Have a nice day!") // This is appended to the help message. You might put known issues here. diff --git a/external/corefx/buildvertical.targets b/external/corefx/buildvertical.targets index c1f28f0ad3..564d74a4fa 100644 --- a/external/corefx/buildvertical.targets +++ b/external/corefx/buildvertical.targets @@ -28,6 +28,9 @@ Configuration=%(Identity);%(_projectBuildConfigurations.AdditionalProperties) + + <_projectBuildConfigurations Remove="@(_projectBuildConfigurations)" Condition="$([System.String]::Copy('%(Identity)').StartsWith('_'))" /> + <_projectWithConfiguration Include="@(_projectBuildConfigurations->'%(OriginalItemSpec)')" /> @@ -54,6 +57,9 @@ Configuration=%(Identity);%(_NonPkgProjProjectReferenceBuildConfigurations.AdditionalProperties) + + <_NonPkgProjProjectReferenceBuildConfigurations Remove="@(_NonPkgProjProjectReferenceBuildConfigurations)" Condition="$([System.String]::Copy('%(Identity)').StartsWith('_'))" /> + <_NonPkgProjProjectReferenceWitnConfiguration Include="@(_NonPkgProjProjectReferenceBuildConfigurations->'%(OriginalItemSpec)')" /> @@ -176,6 +182,8 @@ <_buildConfigurations Include="$(BuildConfigurations)" /> + + <_buildConfigurations Remove="@(_buildConfigurations)" Condition="$([System.String]::Copy('%(Identity)').StartsWith('_'))" /> <_buildConfigurations Condition="'@(_buildConfigurations)' == ''" Include="$(_traversalBuildConfigurations)" /> diff --git a/external/corefx/clean.cmd b/external/corefx/clean.cmd index 90c62f53ea..17f9e402c6 100644 --- a/external/corefx/clean.cmd +++ b/external/corefx/clean.cmd @@ -1,19 +1,30 @@ @if not defined _echo @echo off setlocal EnableDelayedExpansion -echo Stop VBCSCompiler.exe execution. -for /f "tokens=2 delims=," %%F in ('tasklist /nh /fi "imagename eq VBCSCompiler.exe" /fo csv') do taskkill /f /PID %%~F +set NO_DASHES_ARG=%1 +if not defined NO_DASHES_ARG goto no_help +if /I [%NO_DASHES_ARG:-=%] == [?] goto Usage +if /I [%NO_DASHES_ARG:-=%] == [h] goto Usage + +:no_help +:: Check if VBCSCompiler.exe is running +tasklist /fi "imagename eq VBCSCompiler.exe" |find ":" > nul +:: Compiler is running if errorlevel == 1 +if errorlevel 1 ( + echo Stop VBCSCompiler.exe execution. + for /f "tokens=2 delims=," %%F in ('tasklist /nh /fi "imagename eq VBCSCompiler.exe" /fo csv') do taskkill /f /PID %%~F +) :: Strip all dashes off the argument and use invariant :: compare to match as many versions of "all" that we can :: All other argument validation happens inside Run.exe -set NO_DASHES_ARG=%1 if not defined NO_DASHES_ARG goto no_args if /I [%NO_DASHES_ARG:-=%] == [all] ( echo Cleaning entire working directory ... call git clean -xdf exit /b !ERRORLEVEL! ) + :no_args if [%1]==[] set __args=-b call %~dp0run.cmd clean %__args% %* @@ -21,14 +32,13 @@ exit /b %ERRORLEVEL% :Usage echo. +echo Usage: clean [-b] [-p] [-c] [-all] echo Repository cleaning script. -echo. echo Options: -echo -b - Deletes the binary output directory. -echo -p - Deletes the repo-local nuget package directory. -echo -c - Deletes the user-local nuget package cache. -echo -all - Combines all of the above. +echo -b - Delete the binary output directory. +echo -p - Delete the repo-local NuGet package directory. +echo -c - Deletes the user-local NuGet package cache. +echo -all - Cleans repository and restores it to pristine state. echo. -echo If no option is specified then clean.cmd -b is implied. - -exit /b 1 +echo ^If no option is specified then "clean -b" is implied. +exit /b \ No newline at end of file diff --git a/external/corefx/clean.sh b/external/corefx/clean.sh index 227e691d31..79b13493f9 100755 --- a/external/corefx/clean.sh +++ b/external/corefx/clean.sh @@ -18,6 +18,8 @@ if [ "$1" == "-?" ] || [ "$1" == "-h" ]; then usage fi +# Implement VBCSCompiler.exe kill logic once VBCSCompiler.exe is ported to unixes + __working_tree_root="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" if [ "$*" == "-all" ] @@ -31,5 +33,5 @@ if [ $# == 0 ]; then __args=-b fi -$__working_tree_root/run.sh clean $__args $* +$__working_tree_root/run.sh clean $__args "$@" exit $? diff --git a/external/corefx/dependencies.props b/external/corefx/dependencies.props index d49037fd17..ad118c13e1 100644 --- a/external/corefx/dependencies.props +++ b/external/corefx/dependencies.props @@ -9,39 +9,52 @@ These ref versions are pulled from https://github.com/dotnet/versions. --> - 3a5b9d25514fe788eeeb6ecdca6c5b187703d5f3 - 41cec0941b3e81e30e69648c44110acc2a069bba - 41cec0941b3e81e30e69648c44110acc2a069bba + 3c5f8e69e456790cc711a78c66b89f5b8035f02b + f9f2dcffe189a681d43949ee3b02dd1fc9d9e636 + 3c5f8e69e456790cc711a78c66b89f5b8035f02b 96dc7805f5df4a70a55783964ce69dcd91bfca80 - 20f0758de84e9de4a31912d79333a34b16017a21 - 20f0758de84e9de4a31912d79333a34b16017a21 + f9f2dcffe189a681d43949ee3b02dd1fc9d9e636 + f9f2dcffe189a681d43949ee3b02dd1fc9d9e636 8bd1ec5fac9f0eec34ff6b34b1d878b4359e02dd - 3814b0d53f4139b68c2fac85c28030fe8fa50a9d - 075b515e3dfb615b45182c033af48abed349eea5 + 6298244e25cf84d91e3cda9627315f2425274624 + e697c7d95c14fc4e39a0de1a09d0dd9f1faf3f92 - + - 2.1.0-preview1-25819-01 - preview1-25819-01 - 2.1.0-preview1-25818-04 - beta-25627-00 - beta-25813-00 - beta-25813-00 - 1.0.0-beta-25813-00 - 2.1.0-preview1-25818-01 - NETStandard.Library - 2.1.0-preview1-25818-02 - - 4.4.0 + 2.0.1 + NETStandard.Library + + + 4.4.0 - + + preview1-26122-01 + 2.1.0-preview1-26122-01 + 2.1.0-preview1-26122-04 + beta-26122-00 + beta-26122-00 + 1.0.0-beta-26122-00 + 2.1.0-preview1-26121-02 + 2.1.0-preview1-26121-02 + 2.1.0-preview1-26121-02 + 2.1.0-preview1-26121-02 + + + 4.4.0 + 1.0.3-prerelease-00921-01 - 1.0.0-beta-build0007 + 1.0.0-beta-build0015 1.0.3-alpha-experimental - 1.0.1-prerelease-02118-01 + 2.1.0-prerelease-02419-02 + + + + + Microsoft.DotNet.Build.Tasks.Feed + 2.1.0-prerelease-02419-02 @@ -73,7 +86,7 @@ $(SniCurrentRef) - $(BaseDotNetBuildInfo)standard/$(DependencyBranch) + $(BaseDotNetBuildInfo)standard/release/2.0.0 $(StandardCurrentRef) @@ -100,23 +113,18 @@ $(MSBuildThisFileFullPath) - PlatformPackageVersion + MicrosoftNETCorePlatformsPackageVersion Microsoft.NETCore.Platforms $(MSBuildThisFileFullPath) - CoreClrPackageVersion + MicrosoftNETCoreRuntimeCoreCLRPackageVersion Microsoft.NETCore.Runtime.CoreCLR - - $(MSBuildThisFileFullPath) - ExternalExpectedPrerelease - External - $(MSBuildThisFileFullPath) - NETStandardPackageVersion - $(NETStandardPackageId) + NETStandardLibraryPackageVersion + $(NETStandardLibraryPackageId) $(MSBuildThisFileFullPath) @@ -135,28 +143,54 @@ $(MSBuildThisFileFullPath) - SniPackageVersion + RuntimeWinX64RuntimeNativeSystemDataSqlClientSniPackageVersion runtime.win-x64.runtime.native.System.Data.SqlClient.sni + + $(MSBuildThisFileFullPath) + MicrosoftNETCoreDotNetHostPackageVersion + Microsoft.NETCore.DotNetHost + + + $(MSBuildThisFileFullPath) + MicrosoftNETCoreDotNetHostPolicyPackageVersion + Microsoft.NETCore.DotNetHostPolicy + $(MSBuildThisFileFullPath) MicrosoftNETCoreAppPackageVersion Microsoft.NETCore.App + + $(MSBuildThisFileFullPath) + MicrosoftDotNetPlatformAbstractionsPackageVersion + Microsoft.DotNet.PlatformAbstractions + File $(MSBuildThisFileDirectory)BuildToolsVersion.txt Microsoft.DotNet.BuildTools + + File + $(MSBuildThisFileDirectory)tools-local/ILAsmVersion.txt + Microsoft.NETCore.ILAsm + $(MSBuildThisFileFullPath) XunitNetcoreExtensionsVersion Microsoft.xunit.netcore.extensions + + $(MSBuildThisFileFullPath) + FeedTasksPackageVersion + $(FeedTasksPackage) + 2.3.0-beta1-build3642 + 2.0.0 @@ -207,4 +241,17 @@ + + + + + + + + $(MicrosoftNETCorePlatformsPackageVersion) + diff --git a/external/corefx/dir.props b/external/corefx/dir.props index ece69a00a9..e70c05766a 100644 --- a/external/corefx/dir.props +++ b/external/corefx/dir.props @@ -11,7 +11,7 @@ - 10.0.15138 + 10.0.16300 UAP,Version=v$(UAPvNextVersion) uap$(UAPvNextVersion) @@ -23,6 +23,11 @@ true
+ + + false + + $(MSBuildThisFileDirectory) @@ -41,6 +46,7 @@ $(BinDir)packages/ + $(DotNetRestorePackagesPath) $(ProjectDir)packages/ $(ProjectDir)Tools/ $(ToolsDir)ilasm/ilasm @@ -66,17 +72,19 @@ - - - - - - - - - + + + + $(OverridePackageSource); + https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json; + https://dotnet.myget.org/F/dotnet-buildtools/api/v3/index.json; + https://dotnet.myget.org/F/dotnet-core/api/v3/index.json; + https://api.nuget.org/v3/index.json; + $(RestoreSources) + + @@ -145,7 +153,7 @@ $(_packageRID) $(RuntimeOS)-$(ArchGroup) - + true @@ -163,6 +171,12 @@ true + + + Portable + true + + @@ -209,7 +223,7 @@ false false - + latest @@ -217,6 +231,11 @@ true + + + $(NoWarn);0169;0649 + + $(SourceDir)Common\src @@ -240,9 +259,9 @@ $(TestWorkingDir)$(MSBuildProjectName)/$(BuildConfiguration)/ $(BinDir)ref/ $(RefRootPath)$(_bc_TargetGroup)/ - $(RefRootPath)$(_bc_TargetGroup.TrimEnd('aot'))/ + $(RefRootPath)$(_bc_TargetGroup.TrimEnd('t').TrimEnd('o').TrimEnd('a'))/ $(RefRootPath)$(TargetGroup)/ - $(RefRootPath)$(TargetGroup.TrimEnd('aot'))/ + $(RefRootPath)$(TargetGroup.TrimEnd('t').TrimEnd('o').TrimEnd('a'))/ $(RefRootPath)netstandard/ $(RefRootPath)netfx/ diff --git a/external/corefx/external/ILLink/ILLink.depproj b/external/corefx/external/ILLink/ILLink.depproj index 2a5ea0be96..dbc59f043b 100644 --- a/external/corefx/external/ILLink/ILLink.depproj +++ b/external/corefx/external/ILLink/ILLink.depproj @@ -9,7 +9,7 @@ $(ToolRuntimeRID) true illink.tasks - 0.1.4-preview-981901 + 0.1.4-preview-1222833 diff --git a/external/corefx/external/dir.proj b/external/corefx/external/dir.proj index 55f28134c5..68a0a8b122 100644 --- a/external/corefx/external/dir.proj +++ b/external/corefx/external/dir.proj @@ -7,15 +7,14 @@ - + - + - - - + + diff --git a/external/corefx/external/ilasm/ilasm.depproj b/external/corefx/external/ilasm/ilasm.depproj deleted file mode 100644 index 9a7184b98a..0000000000 --- a/external/corefx/external/ilasm/ilasm.depproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - - $(ToolRuntimeRID) - $(ToolsDir)ilasm - false - true - - - - $(PlatformPackageVersion) - - - $(CoreClrPackageVersion) - - - $(CoreClrPackageVersion) - - - - - - - - diff --git a/external/corefx/external/netstandard/netstandard.depproj b/external/corefx/external/netstandard/netstandard.depproj index f818449ea9..44dab19787 100644 --- a/external/corefx/external/netstandard/netstandard.depproj +++ b/external/corefx/external/netstandard/netstandard.depproj @@ -11,7 +11,7 @@ - $(NETStandardPackageVersion) + $(NETStandardLibraryPackageVersion) @@ -53,13 +53,13 @@ - <_NETStandardRefFolder>$(PackagesDir)$(NETStandardPackageId.ToLower())\$(NETStandardPackageVersion)\build\$(_NETStandardTFMFolder)\ref + <_NETStandardRefFolder>$(PackagesDir)$(NETStandardLibraryPackageId.ToLower())\$(NETStandardLibraryPackageVersion)\build\$(_NETStandardTFMFolder)\ref False - $(NETStandardPackageId) - $(NETStandardPackageVersion) + $(NETStandardLibraryPackageId) + $(NETStandardLibraryPackageVersion) diff --git a/external/corefx/external/runtime/runtime.depproj b/external/corefx/external/runtime/runtime.depproj index 7a3c76cf90..2d147b9fac 100644 --- a/external/corefx/external/runtime/runtime.depproj +++ b/external/corefx/external/runtime/runtime.depproj @@ -26,22 +26,22 @@ - $(PlatformPackageVersion) + $(MicrosoftNETCorePlatformsPackageVersion) - $(CoreClrPackageVersion) + $(MicrosoftNETCoreRuntimeCoreCLRPackageVersion) - $(CoreClrPackageVersion) + $(MicrosoftNETCoreRuntimeCoreCLRPackageVersion) - $(PackageVersion)-$(CoreFxExpectedPrerelease) + $(RuntimeNativeSystemDataSqlClientSniPackageVersion) - 2.0.1-servicing-25615-03 + $(MicrosoftNETCoreDotNetHostPackageVersion) - 2.0.1-servicing-25615-03 + $(MicrosoftNETCoreDotNetHostPolicyPackageVersion) @@ -82,7 +82,9 @@ <_CoreCLRSymbolPackagesToDownload Include="@(ReferenceCopyLocalPaths->'%(NuGetPackageId)')" Condition="$([System.String]::Copy('%(Identity)').EndsWith('System.Private.CoreLib.dll'))"> - https://dotnet.myget.org/F/dotnet-core/symbols/%(NuGetPackageId)/%(NuGetPackageVersion)/ + + $(DotNetAssetRootUrl)symbols/%(NuGetPackageId).%(NuGetPackageVersion).symbols.nupkg + https://dotnet.myget.org/F/dotnet-core/symbols/%(NuGetPackageId)/%(NuGetPackageVersion)/ %(NuGetPackageId).%(NuGetPackageVersion).symbols.nupkg.zip $(SymbolPackagesDir)/%(NuGetPackageId).%(NuGetPackageVersion) diff --git a/external/corefx/external/test-runtime/XUnit.Runtime.depproj b/external/corefx/external/test-runtime/XUnit.Runtime.depproj index 959b3b345c..ef5c2b6b82 100644 --- a/external/corefx/external/test-runtime/XUnit.Runtime.depproj +++ b/external/corefx/external/test-runtime/XUnit.Runtime.depproj @@ -50,6 +50,9 @@ 0.10.0-alpha-experimental + + $(MicrosoftDotNetPlatformAbstractionsPackageVersion) + 2.1.1-beta @@ -66,7 +69,7 @@ 3.0.1 - 1.0.5-prerelease + 1.0.6-prerelease 1.0.0-prerelease @@ -95,6 +98,7 @@ + diff --git a/external/corefx/external/test-runtime/optional.json b/external/corefx/external/test-runtime/optional.json index 11ee6b5e4d..698dd33a76 100644 --- a/external/corefx/external/test-runtime/optional.json +++ b/external/corefx/external/test-runtime/optional.json @@ -3,9 +3,9 @@ "net45": { "dependencies": { "Microsoft.DotNet.IBCMerge": "4.6.0-alpha-00001", - "TestILC.amd64ret": "1.0.0-beta-25813-00", - "TestILC.armret": "1.0.0-beta-25813-00", - "TestILC.x86ret": "1.0.0-beta-25813-00" + "TestILC.amd64ret": "1.0.0-beta-26122-00", + "TestILC.armret": "1.0.0-beta-26122-00", + "TestILC.x86ret": "1.0.0-beta-26122-00" } } } diff --git a/external/corefx/illink.targets b/external/corefx/illink.targets index 7501e2925a..8e3525d77f 100644 --- a/external/corefx/illink.targets +++ b/external/corefx/illink.targets @@ -71,6 +71,8 @@ $(ILLinkArgs) -v true $(ILLinkArgs) -h LdtokenTypeMethods,InstanceConstructors + + $(ILLinkArgs) -s ILLink.CustomSteps.ClearInitLocalsStep,ILLink.CustomSteps:OutputStep diff --git a/external/corefx/init-tools.cmd b/external/corefx/init-tools.cmd index 695360ba6d..fa0e44b991 100644 --- a/external/corefx/init-tools.cmd +++ b/external/corefx/init-tools.cmd @@ -10,7 +10,8 @@ if [%BUILDTOOLS_SOURCE%]==[] set BUILDTOOLS_SOURCE=https://dotnet.myget.org/F/do set /P BUILDTOOLS_VERSION=< "%~dp0BuildToolsVersion.txt" set BUILD_TOOLS_PATH=%PACKAGES_DIR%Microsoft.DotNet.BuildTools\%BUILDTOOLS_VERSION%\lib\ set INIT_TOOLS_RESTORE_PROJECT=%~dp0init-tools.msbuild -set BUILD_TOOLS_SEMAPHORE=%TOOLRUNTIME_DIR%\%BUILDTOOLS_VERSION%\init-tools.completed +set BUILD_TOOLS_SEMAPHORE_DIR=%TOOLRUNTIME_DIR%\%BUILDTOOLS_VERSION% +set BUILD_TOOLS_SEMAPHORE=%BUILD_TOOLS_SEMAPHORE_DIR%\init-tools.completed :: if force option is specified then clean the tool runtime and build tools package directory to force it to get recreated if [%1]==[force] ( @@ -26,6 +27,22 @@ if exist "%BUILD_TOOLS_SEMAPHORE%" ( if exist "%TOOLRUNTIME_DIR%" rmdir /S /Q "%TOOLRUNTIME_DIR%" +if NOT exist "%BUILD_TOOLS_SEMAPHORE_DIR%" mkdir "%BUILD_TOOLS_SEMAPHORE_DIR%" + +if exist "%DotNetBuildToolsDir%" ( + echo Using tools from '%DotNetBuildToolsDir%'. + mklink /j "%TOOLRUNTIME_DIR%" "%DotNetBuildToolsDir%" + + if not exist "%DOTNET_CMD%" ( + echo ERROR: Ensure that '%DotNetBuildToolsDir%' contains the .NET Core SDK at '%DOTNET_PATH%' + exit /b 1 + ) + + echo Done initializing tools. + echo Using tools from '%DotNetBuildToolsDir%'. > "%BUILD_TOOLS_SEMAPHORE%" + exit /b 0 +) + echo Running %0 > "%INIT_TOOLS_LOG%" set /p DOTNET_VERSION=< "%~dp0DotnetCLIVersion.txt" @@ -56,6 +73,9 @@ if NOT exist "%BUILD_TOOLS_PATH%init-tools.cmd" ( :afterbuildtoolsrestore +:: Ask init-tools to also restore ILAsm +set /p ILASMCOMPILER_VERSION=< "%~dp0tools-local\ILAsmVersion.txt" + echo Initializing BuildTools... echo Running: "%BUILD_TOOLS_PATH%init-tools.cmd" "%~dp0" "%DOTNET_CMD%" "%TOOLRUNTIME_DIR%" >> "%INIT_TOOLS_LOG%" call "%BUILD_TOOLS_PATH%init-tools.cmd" "%~dp0" "%DOTNET_CMD%" "%TOOLRUNTIME_DIR%" >> "%INIT_TOOLS_LOG%" diff --git a/external/corefx/init-tools.msbuild b/external/corefx/init-tools.msbuild index 1ec33f6782..68db6cb157 100644 --- a/external/corefx/init-tools.msbuild +++ b/external/corefx/init-tools.msbuild @@ -5,7 +5,9 @@ true $(MSBuildThisFileDirectory)Tools/$(BuildToolsPackageVersion) + + \ No newline at end of file diff --git a/external/corefx/init-tools.sh b/external/corefx/init-tools.sh index 07b1e4a791..0e699ce5de 100755 --- a/external/corefx/init-tools.sh +++ b/external/corefx/init-tools.sh @@ -8,147 +8,175 @@ __DOTNET_PATH=$__TOOLRUNTIME_DIR/dotnetcli __DOTNET_CMD=$__DOTNET_PATH/dotnet if [ -z "$__BUILDTOOLS_SOURCE" ]; then __BUILDTOOLS_SOURCE=https://dotnet.myget.org/F/dotnet-buildtools/api/v3/index.json; fi export __BUILDTOOLS_USE_CSPROJ=true -__BUILD_TOOLS_PACKAGE_VERSION=$(cat $__scriptpath/BuildToolsVersion.txt) -__DOTNET_TOOLS_VERSION=$(cat $__scriptpath/DotnetCLIVersion.txt) +__BUILD_TOOLS_PACKAGE_VERSION=$(cat $__scriptpath/BuildToolsVersion.txt | sed 's/\r$//') # remove CR if mounted repo on Windows drive +__DOTNET_TOOLS_VERSION=$(cat $__scriptpath/DotnetCLIVersion.txt | sed 's/\r$//') # remove CR if mounted repo on Windows drive +__ILASM_VERSION=$(cat $__scriptpath/tools-local/ILAsmVersion.txt | sed 's/\r$//') # remove CR if mounted repo on Windows drive __BUILD_TOOLS_PATH=$__PACKAGES_DIR/microsoft.dotnet.buildtools/$__BUILD_TOOLS_PACKAGE_VERSION/lib __INIT_TOOLS_RESTORE_PROJECT=$__scriptpath/init-tools.msbuild -__INIT_TOOLS_DONE_MARKER_DIR=$__TOOLRUNTIME_DIR/$__BUILD_TOOLS_PACKAGE_VERSION -__INIT_TOOLS_DONE_MARKER=$__INIT_TOOLS_DONE_MARKER_DIR/done +__BUILD_TOOLS_SEMAPHORE=$__TOOLRUNTIME_DIR/$__BUILD_TOOLS_PACKAGE_VERSION/init-tools.complete -if [ -z "$__DOTNET_PKG" ]; then - if [ "$(uname -m | grep "i[3456]86")" = "i686" ]; then - echo "Warning: build not supported on 32 bit Unix" +if [ -e $__BUILD_TOOLS_SEMAPHORE ]; then + echo "Tools are already initialized" + return #return instead of exit because this script is inlined in other scripts which we don't want to exit +fi + +if [ -e $__TOOLRUNTIME_DIR ]; then rm -rf -- $__TOOLRUNTIME_DIR; fi + +if [ -d "$DotNetBuildToolsDir" ]; then + echo "Using tools from '$DotNetBuildToolsDir'." + ln -s "$DotNetBuildToolsDir" "$__TOOLRUNTIME_DIR" + + if [ ! -e "$__DOTNET_CMD" ]; then + echo "ERROR: Ensure that $DotNetBuildToolsDir contains the .NET Core SDK at $__DOTNET_PATH" + exit 1 fi - __PKG_ARCH=x64 - - OSName=$(uname -s) - case $OSName in - Darwin) - OS=OSX - __PKG_RID=osx - ulimit -n 2048 - # Format x.y.z as single integer with three digits for each part - VERSION=`sw_vers -productVersion| sed -e 's/\./ /g' | xargs printf "%03d%03d%03d"` - if [ "$VERSION" -lt 010012000 ]; then - echo error: macOS version `sw_vers -productVersion` is too old. 10.12 is needed as minimum. - exit 1 - fi - ;; - - Linux) - __PKG_RID=linux - OS=Linux - - if [ -e /etc/os-release ]; then - source /etc/os-release - if [[ $ID == "alpine" ]]; then - # remove the last version digit - VERSION_ID=${VERSION_ID%.*} - __PKG_RID=alpine.$VERSION_ID - fi - elif [ -e /etc/redhat-release ]; then - redhatRelease=$( $__init_tools_log + display_error_message() { echo "Please check the detailed log that follows." 1>&2 cat "$__init_tools_log" 1>&2 } -if [ ! -e $__INIT_TOOLS_DONE_MARKER ]; then - __PATCH_CLI_NUGET_FRAMEWORKS=0 - - if [ -e $__TOOLRUNTIME_DIR ]; then rm -rf -- $__TOOLRUNTIME_DIR; fi - echo "Running: $__scriptpath/init-tools.sh" > $__init_tools_log - - if [ ! -e $__DOTNET_PATH ]; then - - mkdir -p "$__DOTNET_PATH" - - if [ -n "$DOTNET_TOOLSET_DIR" ] && [ -d "$DOTNET_TOOLSET_DIR/$__DOTNET_TOOLS_VERSION" ]; then - echo "Copying $DOTNET_TOOLSET_DIR/$__DOTNET_TOOLS_VERSION to $__DOTNET_PATH" >> $__init_tools_log - cp -r $DOTNET_TOOLSET_DIR/$__DOTNET_TOOLS_VERSION/* $__DOTNET_PATH - elif [ -n "$DOTNET_TOOL_DIR" ] && [ -d "$DOTNET_TOOL_DIR" ]; then - echo "Copying $DOTNET_TOOL_DIR to $__DOTNET_PATH" >> $__init_tools_log - cp -r $DOTNET_TOOL_DIR/* $__DOTNET_PATH +# Executes a command and retries if it fails. +execute_with_retry() { + local count=0 + local retries=${retries:-5} + local waitFactor=${waitFactor:-6} + until "$@"; do + local exit=$? + count=$(( $count + 1 )) + if [ $count -lt $retries ]; then + local wait=$(( waitFactor ** (( count - 1 )) )) + echo "Retry $count/$retries exited $exit, retrying in $wait seconds..." + sleep $wait else - echo "Installing dotnet cli..." - __DOTNET_LOCATION="https://dotnetcli.azureedge.net/dotnet/Sdk/${__DOTNET_TOOLS_VERSION}/${__DOTNET_PKG}.tar.gz" - # curl has HTTPS CA trust-issues less often than wget, so lets try that first. - echo "Installing '${__DOTNET_LOCATION}' to '$__DOTNET_PATH/dotnet.tar'" >> $__init_tools_log - if command -v curl > /dev/null; then - curl --retry 10 -sSL --create-dirs -o $__DOTNET_PATH/dotnet.tar ${__DOTNET_LOCATION} - else - wget -q -O $__DOTNET_PATH/dotnet.tar ${__DOTNET_LOCATION} - fi - cd $__DOTNET_PATH - tar -xf $__DOTNET_PATH/dotnet.tar - - cd $__scriptpath - - __PATCH_CLI_NUGET_FRAMEWORKS=1 + say_err "Retry $count/$retries exited $exit, no more retries left." + return $exit fi + done + + return 0 +} + +if [ ! -e $__DOTNET_PATH ]; then + if [ -z "$__DOTNET_PKG" ]; then + if [ "$(uname -m | grep "i[3456]86")" = "i686" ]; then + echo "Warning: build not supported on 32 bit Unix" + fi + + __PKG_ARCH=x64 + + OSName=$(uname -s) + case $OSName in + Darwin) + OS=OSX + __PKG_RID=osx + ulimit -n 2048 + # Format x.y.z as single integer with three digits for each part + VERSION=`sw_vers -productVersion| sed -e 's/\./ /g' | xargs printf "%03d%03d%03d"` + if [ "$VERSION" -lt 010012000 ]; then + echo error: macOS version `sw_vers -productVersion` is too old. 10.12 is needed as minimum. + exit 1 + fi + ;; + + Linux) + __PKG_RID=linux + OS=Linux + + if [ -e /etc/os-release ]; then + source /etc/os-release + if [[ $ID == "alpine" ]]; then + # remove the last version digit + VERSION_ID=${VERSION_ID%.*} + __PKG_RID=alpine.$VERSION_ID + fi + elif [ -e /etc/redhat-release ]; then + redhatRelease=$(> $__init_tools_log - cp -r $BUILD_TOOLS_TOOLSET_DIR/$__BUILD_TOOLS_PACKAGE_VERSION/* $__TOOLRUNTIME_DIR - elif [ -n "$BUILD_TOOLS_TOOL_DIR" ] && [ -d "$BUILD_TOOLS_TOOL_DIR" ]; then - echo "Copying $BUILD_TOOLS_TOOL_DIR to $__TOOLRUNTIME_DIR" >> $__init_tools_log - cp -r $BUILD_TOOLS_TOOL_DIR/* $__TOOLRUNTIME_DIR - else - if [ ! -e $__BUILD_TOOLS_PATH ]; then - echo "Restoring BuildTools version $__BUILD_TOOLS_PACKAGE_VERSION..." - echo "Running: $__DOTNET_CMD restore \"$__INIT_TOOLS_RESTORE_PROJECT\" --no-cache --packages $__PACKAGES_DIR --source $__BUILDTOOLS_SOURCE /p:BuildToolsPackageVersion=$__BUILD_TOOLS_PACKAGE_VERSION" >> $__init_tools_log - $__DOTNET_CMD restore "$__INIT_TOOLS_RESTORE_PROJECT" --no-cache --packages $__PACKAGES_DIR --source $__BUILDTOOLS_SOURCE /p:BuildToolsPackageVersion=$__BUILD_TOOLS_PACKAGE_VERSION >> $__init_tools_log - if [ ! -e "$__BUILD_TOOLS_PATH/init-tools.sh" ]; then - echo "ERROR: Could not restore build tools correctly." 1>&2 - display_error_message - fi + install_dotnet_cli() { + echo "Installing '${__DOTNET_LOCATION}' to '$__DOTNET_PATH/dotnet.tar'" >> "$__init_tools_log" + rm -rf -- "$__DOTNET_PATH/*" + # curl has HTTPS CA trust-issues less often than wget, so lets try that first. + if command -v curl > /dev/null; then + curl --retry 10 -sSL --create-dirs -o $__DOTNET_PATH/dotnet.tar ${__DOTNET_LOCATION} + else + wget -q -O $__DOTNET_PATH/dotnet.tar ${__DOTNET_LOCATION} fi + cd $__DOTNET_PATH + tar -xf $__DOTNET_PATH/dotnet.tar + } + execute_with_retry install_dotnet_cli >> "$__init_tools_log" 2>&1 - echo "Initializing BuildTools..." - echo "Running: $__BUILD_TOOLS_PATH/init-tools.sh $__scriptpath $__DOTNET_CMD $__TOOLRUNTIME_DIR" >> $__init_tools_log - - # Executables restored with .NET Core 2.0 do not have executable permission flags. https://github.com/NuGet/Home/issues/4424 - chmod +x $__BUILD_TOOLS_PATH/init-tools.sh - $__BUILD_TOOLS_PATH/init-tools.sh $__scriptpath $__DOTNET_CMD $__TOOLRUNTIME_DIR >> $__init_tools_log - if [ "$?" != "0" ]; then - echo "ERROR: An error occurred when trying to initialize the tools." 1>&2 - display_error_message - exit 1 - fi - fi - - echo "Making all .sh files executable under Tools." - # Executables restored with .NET Core 2.0 do not have executable permission flags. https://github.com/NuGet/Home/issues/4424 - ls $__scriptpath/Tools/*.sh | xargs chmod +x - ls $__scriptpath/Tools/scripts/docker/*.sh | xargs chmod +x - - Tools/crossgen.sh $__scriptpath/Tools - - mkdir -p $__INIT_TOOLS_DONE_MARKER_DIR - touch $__INIT_TOOLS_DONE_MARKER - - echo "Done initializing tools." -else - echo "Tools are already initialized" + cd $__scriptpath fi + +if [ ! -e $__BUILD_TOOLS_PATH ]; then + echo "Restoring BuildTools version $__BUILD_TOOLS_PACKAGE_VERSION..." + echo "Running: $__DOTNET_CMD restore \"$__INIT_TOOLS_RESTORE_PROJECT\" --no-cache --packages $__PACKAGES_DIR --source $__BUILDTOOLS_SOURCE /p:BuildToolsPackageVersion=$__BUILD_TOOLS_PACKAGE_VERSION" >> $__init_tools_log + $__DOTNET_CMD restore "$__INIT_TOOLS_RESTORE_PROJECT" --no-cache --packages $__PACKAGES_DIR --source $__BUILDTOOLS_SOURCE /p:BuildToolsPackageVersion=$__BUILD_TOOLS_PACKAGE_VERSION >> $__init_tools_log + if [ ! -e "$__BUILD_TOOLS_PATH/init-tools.sh" ]; then + echo "ERROR: Could not restore build tools correctly." 1>&2 + display_error_message + fi +fi + +if [ -z "$__ILASM_RID" ]; then + __ILASM_RID=$__PKG_RID-$__PKG_ARCH +fi + +echo "Using RID $__ILASM_RID for BuildTools native tools" + +export ILASMCOMPILER_VERSION=$__ILASM_VERSION +export NATIVE_TOOLS_RID=$__ILASM_RID + +echo "Initializing BuildTools..." +echo "Running: $__BUILD_TOOLS_PATH/init-tools.sh $__scriptpath $__DOTNET_CMD $__TOOLRUNTIME_DIR" >> $__init_tools_log + +# Executables restored with .NET Core 2.0 do not have executable permission flags. https://github.com/NuGet/Home/issues/4424 +chmod +x $__BUILD_TOOLS_PATH/init-tools.sh +$__BUILD_TOOLS_PATH/init-tools.sh $__scriptpath $__DOTNET_CMD $__TOOLRUNTIME_DIR >> $__init_tools_log +if [ "$?" != "0" ]; then + echo "ERROR: An error occurred when trying to initialize the tools." 1>&2 + display_error_message + exit 1 +fi + +echo "Making all .sh files executable under Tools." +# Executables restored with .NET Core 2.0 do not have executable permission flags. https://github.com/NuGet/Home/issues/4424 +ls $__scriptpath/Tools/*.sh | xargs chmod +x +ls $__scriptpath/Tools/scripts/docker/*.sh | xargs chmod +x + +Tools/crossgen.sh $__scriptpath/Tools + +mkdir -p "$(dirname "$__BUILD_TOOLS_SEMAPHORE")" && touch $__BUILD_TOOLS_SEMAPHORE + +echo "Done initializing tools." + diff --git a/external/corefx/perf.groovy b/external/corefx/perf.groovy index 75a916c170..837850117e 100644 --- a/external/corefx/perf.groovy +++ b/external/corefx/perf.groovy @@ -49,7 +49,7 @@ def osShortName = ['Windows 10': 'win10', // ************************** [true, false].each { isPR -> ['Release'].each { configurationGroup -> - ['Windows_NT', 'Ubuntu14.04'].each { os -> + ['Windows_NT', 'Ubuntu16.04'].each { os -> def osGroup = osGroupMap[os] def newJobName = "perf_${os.toLowerCase()}_${configurationGroup.toLowerCase()}" @@ -58,7 +58,7 @@ def osShortName = ['Windows 10': 'win10', label('windows_server_2016_clr_perf') } else { - label('linux_clr_perf') + label('ubuntu_1604_clr_perf') } wrappers { @@ -103,7 +103,7 @@ def osShortName = ['Windows 10': 'win10', steps { //We need to specify the max cpu count to be one as we do not want to be executing performance tests in parallel shell("./build.sh -release") - shell("sudo find . -type f -name dotnet | xargs chmod +x") + shell("find . -type f -name dotnet | xargs chmod u+x") shell("curl \"http://benchviewtestfeed.azurewebsites.net/nuget/FindPackagesById()?id='Microsoft.BenchView.JSONFormat'\" | grep \"content type\" | sed \"\$ s/.*src=\\\"\\([^\\\"]*\\)\\\".*/\\1/;tx;d;:x\" | xargs curl -o benchview.zip") shell("unzip -q -o benchview.zip -d \"\${WORKSPACE}/Tools/Microsoft.BenchView.JSONFormat\"") @@ -113,7 +113,7 @@ def osShortName = ['Windows 10': 'win10', "python3.5 \"\${WORKSPACE}/Tools/Microsoft.BenchView.JSONFormat/tools/submission-metadata.py\" --name " + "\"" + benchViewName + "\"" + " --user-email " + "\"dotnet-bot@microsoft.com\"\n" + "python3.5 \"\${WORKSPACE}/Tools/Microsoft.BenchView.JSONFormat/tools/build.py\" git --branch \$GIT_BRANCH_WITHOUT_ORIGIN --type " + runType) shell("python3.5 \"\${WORKSPACE}/Tools/Microsoft.BenchView.JSONFormat/tools/machinedata.py\"") - shell("sudo -E bash ./build-managed.sh -release -tests -- /p:Performance=true /p:TargetOS=${osGroup} /m:1 /p:LogToBenchview=true /p:BenchviewRunType=${runType}") + shell("bash ./build-managed.sh -release -tests -- /p:Performance=true /p:TargetOS=${osGroup} /m:1 /p:LogToBenchview=true /p:BenchviewRunType=${runType}") } } } diff --git a/external/corefx/pkg/Microsoft.NETCore.Platforms/runtime.compatibility.json b/external/corefx/pkg/Microsoft.NETCore.Platforms/runtime.compatibility.json index 8ff708f2ec..552ccf502a 100644 --- a/external/corefx/pkg/Microsoft.NETCore.Platforms/runtime.compatibility.json +++ b/external/corefx/pkg/Microsoft.NETCore.Platforms/runtime.compatibility.json @@ -1,12 +1,15 @@ { "alpine": [ "alpine", + "unix", "any", "base" ], "alpine-corert": [ "alpine-corert", "alpine", + "unix-corert", + "unix", "corert", "any", "base" @@ -14,6 +17,8 @@ "alpine-x64": [ "alpine-x64", "alpine", + "unix-x64", + "unix", "any", "base" ], @@ -21,7 +26,11 @@ "alpine-x64-corert", "alpine-corert", "alpine-x64", + "unix-x64-corert", "alpine", + "unix-corert", + "unix-x64", + "unix", "corert", "any", "base" @@ -29,6 +38,7 @@ "alpine.3.6": [ "alpine.3.6", "alpine", + "unix", "any", "base" ], @@ -37,6 +47,8 @@ "alpine.3.6", "alpine-corert", "alpine", + "unix-corert", + "unix", "corert", "any", "base" @@ -46,6 +58,8 @@ "alpine.3.6", "alpine-x64", "alpine", + "unix-x64", + "unix", "any", "base" ], @@ -58,6 +72,10 @@ "alpine-corert", "alpine-x64", "alpine", + "unix-x64-corert", + "unix-corert", + "unix-x64", + "unix", "corert", "any", "base" diff --git a/external/corefx/pkg/Microsoft.NETCore.Platforms/runtime.json b/external/corefx/pkg/Microsoft.NETCore.Platforms/runtime.json index 8e16fcdd96..7346469800 100644 --- a/external/corefx/pkg/Microsoft.NETCore.Platforms/runtime.json +++ b/external/corefx/pkg/Microsoft.NETCore.Platforms/runtime.json @@ -2,24 +2,26 @@ "runtimes": { "alpine": { "#import": [ - "any" + "unix" ] }, "alpine-corert": { "#import": [ "alpine", - "corert" + "unix-corert" ] }, "alpine-x64": { "#import": [ - "alpine" + "alpine", + "unix-x64" ] }, "alpine-x64-corert": { "#import": [ "alpine-corert", - "alpine-x64" + "alpine-x64", + "unix-x64-corert" ] }, "alpine.3.6": { diff --git a/external/corefx/pkg/Microsoft.NETCore.Platforms/runtimeGroups.props b/external/corefx/pkg/Microsoft.NETCore.Platforms/runtimeGroups.props index 0f2b4a4197..d3aacf4ca2 100644 --- a/external/corefx/pkg/Microsoft.NETCore.Platforms/runtimeGroups.props +++ b/external/corefx/pkg/Microsoft.NETCore.Platforms/runtimeGroups.props @@ -11,7 +11,7 @@ - any + unix x64 3.6 diff --git a/external/corefx/pkg/Microsoft.NETFramework.Compatibility/Microsoft.NETFramework.Compatibility.pkgproj b/external/corefx/pkg/Microsoft.NETFramework.Compatibility/Microsoft.NETFramework.Compatibility.pkgproj deleted file mode 100644 index c2e8ce256a..0000000000 --- a/external/corefx/pkg/Microsoft.NETFramework.Compatibility/Microsoft.NETFramework.Compatibility.pkgproj +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - 2.1.0 - - - - - 4.4.0 - - - 4.5.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/external/corefx/pkg/Microsoft.Private.CoreFx.UAP/uap.rids.props b/external/corefx/pkg/Microsoft.Private.CoreFx.UAP/uap.rids.props index 4dda2a8ff5..1bccba4a0b 100644 --- a/external/corefx/pkg/Microsoft.Private.CoreFx.UAP/uap.rids.props +++ b/external/corefx/pkg/Microsoft.Private.CoreFx.UAP/uap.rids.props @@ -8,6 +8,9 @@ arm + + arm64 + x86 @@ -15,5 +18,8 @@ arm + + arm64 + \ No newline at end of file diff --git a/external/corefx/pkg/Microsoft.Private.PackageBaseline/Microsoft.Private.PackageBaseline.pkgproj b/external/corefx/pkg/Microsoft.Private.PackageBaseline/Microsoft.Private.PackageBaseline.pkgproj index 5c9dd1f1be..1c70eb4e07 100644 --- a/external/corefx/pkg/Microsoft.Private.PackageBaseline/Microsoft.Private.PackageBaseline.pkgproj +++ b/external/corefx/pkg/Microsoft.Private.PackageBaseline/Microsoft.Private.PackageBaseline.pkgproj @@ -36,10 +36,10 @@ $(UAPvNextTFM) - + netstandard2.0 - + net461 diff --git a/external/corefx/pkg/Microsoft.Private.PackageBaseline/packageIndex.json.REMOVED.git-id b/external/corefx/pkg/Microsoft.Private.PackageBaseline/packageIndex.json.REMOVED.git-id index e20776917e..bb64b0f58b 100644 --- a/external/corefx/pkg/Microsoft.Private.PackageBaseline/packageIndex.json.REMOVED.git-id +++ b/external/corefx/pkg/Microsoft.Private.PackageBaseline/packageIndex.json.REMOVED.git-id @@ -1 +1 @@ -f3f128e745dd276fe999ccec1dd9d48f056ecb9f \ No newline at end of file +28e475f5bd0c3d7de2ba5852f5a4ed767748c8e4 \ No newline at end of file diff --git a/external/corefx/pkg/Microsoft.NETFramework.Compatibility/Microsoft.NETFramework.Compatibility.builds b/external/corefx/pkg/Microsoft.Windows.Compatibility.Shims/Microsoft.Windows.Compatibility.Shims.builds similarity index 99% rename from external/corefx/pkg/Microsoft.NETFramework.Compatibility/Microsoft.NETFramework.Compatibility.builds rename to external/corefx/pkg/Microsoft.Windows.Compatibility.Shims/Microsoft.Windows.Compatibility.Shims.builds index 6d6d97a476..b74a5259bb 100644 --- a/external/corefx/pkg/Microsoft.NETFramework.Compatibility/Microsoft.NETFramework.Compatibility.builds +++ b/external/corefx/pkg/Microsoft.Windows.Compatibility.Shims/Microsoft.Windows.Compatibility.Shims.builds @@ -8,5 +8,4 @@ - \ No newline at end of file diff --git a/external/corefx/pkg/Microsoft.Windows.Compatibility.Shims/Microsoft.Windows.Compatibility.Shims.pkgproj b/external/corefx/pkg/Microsoft.Windows.Compatibility.Shims/Microsoft.Windows.Compatibility.Shims.pkgproj new file mode 100644 index 0000000000..1005126635 --- /dev/null +++ b/external/corefx/pkg/Microsoft.Windows.Compatibility.Shims/Microsoft.Windows.Compatibility.Shims.pkgproj @@ -0,0 +1,45 @@ + + + + + + $(CompatibilityShimsPackageVersion) + <_ShimsLocationPath>$(BaseIntermediateOutputPath)shims/netcoreapp/facades/ + + + + + + + + + + + + + + + + + + + + + + + + /lib/netcoreapp2.0 + true + + + + /lib/netstandard2.0 + + + + + + + + diff --git a/external/corefx/pkg/Microsoft.Windows.Compatibility/Microsoft.Windows.Compatibility.builds b/external/corefx/pkg/Microsoft.Windows.Compatibility/Microsoft.Windows.Compatibility.builds new file mode 100644 index 0000000000..b74a5259bb --- /dev/null +++ b/external/corefx/pkg/Microsoft.Windows.Compatibility/Microsoft.Windows.Compatibility.builds @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/external/corefx/pkg/Microsoft.Windows.Compatibility/Microsoft.Windows.Compatibility.pkgproj b/external/corefx/pkg/Microsoft.Windows.Compatibility/Microsoft.Windows.Compatibility.pkgproj new file mode 100644 index 0000000000..1f4239875e --- /dev/null +++ b/external/corefx/pkg/Microsoft.Windows.Compatibility/Microsoft.Windows.Compatibility.pkgproj @@ -0,0 +1,78 @@ + + + + + + 2.0.0 + + + + + 4.4.0 + + + 4.5.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 4.4.1-servicing-25917-01 + + + + + + + + + + + + + + + + + + $(CompatibilityShimsPackageVersion) + + + + + diff --git a/external/corefx/pkg/Microsoft.NETFramework.Compatibility/externalIndex.json b/external/corefx/pkg/Microsoft.Windows.Compatibility/externalIndex.json similarity index 100% rename from external/corefx/pkg/Microsoft.NETFramework.Compatibility/externalIndex.json rename to external/corefx/pkg/Microsoft.Windows.Compatibility/externalIndex.json diff --git a/external/corefx/pkg/Microsoft.Windows.Compatibility/tests/Microsoft.Windows.Compatibility.Validation.csproj b/external/corefx/pkg/Microsoft.Windows.Compatibility/tests/Microsoft.Windows.Compatibility.Validation.csproj new file mode 100644 index 0000000000..45ec708355 --- /dev/null +++ b/external/corefx/pkg/Microsoft.Windows.Compatibility/tests/Microsoft.Windows.Compatibility.Validation.csproj @@ -0,0 +1,11 @@ + + + true + true + + + + + + + \ No newline at end of file diff --git a/external/corefx/pkg/Microsoft.Windows.Compatibility/tests/publishcompatibilityassets.ps1 b/external/corefx/pkg/Microsoft.Windows.Compatibility/tests/publishcompatibilityassets.ps1 new file mode 100644 index 0000000000..0db2d675c0 --- /dev/null +++ b/external/corefx/pkg/Microsoft.Windows.Compatibility/tests/publishcompatibilityassets.ps1 @@ -0,0 +1,65 @@ +$repoRoot = ((get-item $PSScriptRoot).parent.parent.parent.FullName); +$winRID = "win7-x64"; +$dotnetPath = -join($repoRoot, "\Tools\dotnetcli\dotnet.exe") +$csprojPath = -join($PSScriptRoot, "\", (Get-ChildItem $PSScriptRoot"\*.csproj" | Select-Object -ExpandProperty Name)) +$packagesCachePath = -join($repoRoot, "\packages") +$localPackageSourcePath = -join($repoRoot, "\bin\packages\Debug\") +$packageName = "Microsoft.Windows.Compatibility" + +if (!(Test-Path $localPackageSourcePath)) +{ + $localPackageSourcePath = -join($repoRoot, "\bin\packages\Release\") + if (!(Test-Path $localPackageSourcePath)) + { + Write-Error -Message "Local package source must exist."; + Exit; + } +} + +function _getPackageVersion() +{ + $searchPattern = -join($localPackageSourcePath, $packageName, ".[0-9].[0-9].[0-9]*.nupkg") + if (!(Test-Path $searchPattern)) + { + Write-Error -Message (-join("Didn't find package: Microsoft.Windows.Compatibility in source: ", $localPackageSourcePath, " please run build -allConfigurations")) + Exit; + } + + if (!((get-item $searchPattern).FullName -match '([0-9].[0-9].[0-9][-a-z0-9]*)')) + { + Write-Error -Message "Package name is invalid" + Exit; + } + + return $matches[0] +} + +function _restoreAndPublish($targetFramework, $rid, $runtimeFramework, $refDirName) +{ + $packageVersion = _getPackageVersion + & $dotnetPath restore --packages $packagesCachePath /p:RestoreSources="https://api.nuget.org/v3/index.json;$localPackageSourcePath" /p:TargetFramework=$targetFramework /p:CompatibilityPackageVersion=$packageVersion $csprojPath + & $dotnetPath publish -r $rid /p:RestoreSources="https://api.nuget.org/v3/index.json;$localPackageSourcePath" /p:TargetFramework=$targetFramework /p:CompatibilityPackageVersion=$packageVersion /p:RuntimeFrameworkVersion=$runtimeFramework $csprojPath + + $outputPath = -join($PSScriptRoot, "\bin\Debug\", $targetFramework, "\", $rid, "\publish\refs\") + + if (!(Test-Path $outputPath)) + { + Write-Error -Message (-join("There was an error while publishing for framework: ", $targetFramework)) + Exit; + } + + Write-Output (-join("Published succedded for: ", $targetFramework)) + + $refPath = -join($repoRoot, "\bin\ref\", $refDirName) + + if (Test-Path $refPath) + { + Remove-Item $refPath -r -force + } + + New-Item $refPath -ItemType directory + Copy-Item (-join($outputPath, "*")) $refPath +} + +_restoreAndPublish "netcoreapp2.0" $winRID "2.0.0" "netcoreapp20_compat" +_restoreAndPublish "netstandard2.0" $winRID "2.0.0" "netstandard20_compat" \ No newline at end of file diff --git a/external/corefx/pkg/descriptions.json b/external/corefx/pkg/descriptions.json index 9a7e1c5b18..83319dc0b7 100644 --- a/external/corefx/pkg/descriptions.json +++ b/external/corefx/pkg/descriptions.json @@ -64,8 +64,13 @@ "CommonTypes": [] }, { - "Name": "Microsoft.NETFramework.Compatibility", - "Description": "References a number of .NET Core libraries that are intended to provide support for more APIs that exist on .NET Framework.", + "Name": "Microsoft.Windows.Compatibility", + "Description": "This Windows Compatibility Pack provides access to APIs that were previously available only for .NET Framework. It can be used from both .NET Core as well as .NET Standard.", + "CommonTypes": [] + }, + { + "Name": "Microsoft.Windows.Compatibility.Shims", + "Description": "This package provides infrastructure services and shouldn't be referenced directly from your code. It provides facade assemblies in order to make the Microsoft.Windows.Compatibility package work across all .NET implementations.", "CommonTypes": [] }, { @@ -163,7 +168,7 @@ }, { "Name": "Microsoft.XmlSerializer.Generator", - "Description": "Provides SGen tool to generate Xml serialization code for types in a specified assembly in order to improve the startup performance of a XmlSerializer when it serializes or deserializes objects of the specified types", + "Description": "Like the Xml Serializer Generator (sgen.exe) on desktop, Microsoft.XmlSerializer.Generator NuGet package is the solution for .NET Core and .NET Standard Libraries. It creates an Xml serialization assembly for types contained in an assembly in order to improve the startup performance of Xml serialization when serializing or de-serializing objects of those types using XmlSerializer.", "CommonTypes": [] }, { @@ -334,6 +339,27 @@ "System.ComponentModel.DataAnnotations.KeyAttribute" ] }, + { + "Name": "System.ComponentModel.Composition", + "Description": "This namespace provides classes that constitute the core of the Managed Extensibility Framework, or MEF.", + "CommonTypes": [ + "System.ComponentModel.Composition.ChangeRejectedException", + "System.ComponentModel.Composition.CompositionContractMismatchException", + "System.ComponentModel.Composition.CompositionError", + "System.ComponentModel.Composition.CompositionException", + "System.ComponentModel.Composition.ExportAttribute", + "System.ComponentModel.Composition.ImportAttribute", + "System.ComponentModel.Composition.ImportCardinalityMismatchException", + "System.ComponentModel.Composition.Hosting.AggregateCatalog", + "System.ComponentModel.Composition.Hosting.ApplicationCatalog", + "System.ComponentModel.Composition.Hosting.AssemblyCatalog", + "System.ComponentModel.Composition.Hosting.CompositionContainer", + "System.ComponentModel.Composition.Primitives.ComposablePartException", + "System.ComponentModel.Composition.Primitives.ExportDefinition", + "System.ComponentModel.Composition.Primitives.ImportDefinition", + "System.ComponentModel.Composition.ReflectionModel.ReflectionModelServices" + ] + }, { "Name": "System.ComponentModel.EventBasedAsync", "Description": "Provides support classes and delegates for the event-based asynchronous pattern. Developers should prefer the classes in the System.Threading.Tasks package.", @@ -558,6 +584,20 @@ "System.Diagnostics.FileVersionInfo" ] }, + { + "Name": "System.Diagnostics.EventLog", + "Description": "Provides the System.Diagnostics.EventLog class, which allows the applications to use the windows event log service.", + "CommonTypes": [ + "System.Diagnostics.EventLog" + ] + }, + { + "Name": "System.Diagnostics.PerformanceCounter", + "Description": "Provides the System.Diagnostics.PerformanceCounter class, which allows access to Windows performance counters.", + "CommonTypes": [ + "System.Diagnostics.PerformanceCounter" + ] + }, { "Name": "System.Diagnostics.Process", "Description": "Provides the System.Diagnostics.Process class, which allows interaction with local and remote processes.", @@ -940,6 +980,15 @@ "System.Linq.EnumerableQuery" ] }, + { + "Name": "System.Management", + "Description": "Provides access to a rich set of management information and management events about the system, devices, and applications instrumented to the Windows Management Instrumentation (WMI) infrastructure.", + "CommonTypes": [ + "System.Management.ManagementClass", + "System.Management.ManagementObject", + "System.Management.SelectQuery" + ] + }, { "Name": "System.Memory", "Description": "Provides types for efficient low-allocation access to memory.", @@ -990,6 +1039,13 @@ "System.Net.WebResponse" ] }, + { + "Name": "System.Net.WebSockets.WebSocketProtocol", + "Description": "Provides the WebSocketProtocol class, which allows creating a websocket from a connected stream using WebSocketsProtocol.CreateFromConnectedStream.", + "CommonTypes": [ + "System.Net.WebSockets.WebSocketProtocol" + ] + }, { "Name": "System.Net.Http.WinHttpHandler", "Description": "Provides a message handler for HttpClient based on the WinHTTP interface of Windows. While similar to HttpClientHandler, it provides developers more granular control over the application's HTTP communication than the HttpClientHandler.", @@ -1349,6 +1405,22 @@ "System.Uri" ] }, + { + "Name": "System.Runtime.Caching", + "Description": "Provides classes to use caching facilities.", + "CommonTypes": [ + "System.Runtime.Caching.CacheEntryChangeMonitor", + "System.Runtime.Caching.CacheEntryRemovedArguments", + "System.Runtime.Caching.CacheEntryUpdateArguments", + "System.Runtime.Caching.CacheItem", + "System.Runtime.Caching.CacheItemPolicy", + "System.Runtime.Caching.ChangeMonitor", + "System.Runtime.Caching.FileChangeMonitor", + "System.Runtime.Caching.HostFileChangeMonitor", + "System.Runtime.Caching.MemoryCache", + "System.Runtime.Caching.ObjectCache", + ] + }, { "Name": "System.Runtime.CompilerServices.Unsafe", "Description": "Provides the System.Runtime.CompilerServices.Unsafe class, which provides generic, low-level functionality for manipulating pointers.", @@ -1872,6 +1944,14 @@ "System.Security.AccessControl.SemaphoreSecurity" ] }, + { + "Name": "System.Threading.Channels", + "Description": "Provides types for passing data between producers and consumers.", + "CommonTypes": [ + "System.Threading.Channel", + "System.Threading.Channel" + ] + }, { "Name": "System.Threading.ExecutionContext", "Description": "Provides types for managing the information relevant to a logic thread or task of execution.", diff --git a/external/corefx/pkg/frameworkPackage.targets b/external/corefx/pkg/frameworkPackage.targets index c2df42f615..f1cf08f8b4 100644 --- a/external/corefx/pkg/frameworkPackage.targets +++ b/external/corefx/pkg/frameworkPackage.targets @@ -6,7 +6,7 @@ true 2.0 - $(PackagesDir)$(NETStandardPackageId.ToLower())\$(NETStandardPackageVersion)\build\netstandard$(NETStandardVersion)\ref + $(PackagesDir)$(NETStandardLibraryPackageId.ToLower())\$(NETStandardLibraryPackageVersion)\build\netstandard$(NETStandardVersion)\ref true true @@ -30,7 +30,7 @@ - $(PlatformPackageVersion) + $(MicrosoftNETCorePlatformsPackageVersion) $(TargetFramework) @@ -165,6 +165,9 @@ <_NETStandardFile Include="$(NETStandardPackageRefPath)\*.dll" /> + + + <_NETStandardMissingFile Include="@(_NETStandardFile->'%(FileName)')" Exclude="@(ClosureFile->'%(FileName)')" /> <_NETStandardMissingFileError Include="@(_NETStandardMissingFile)" Exclude="@(SuppressNETStandardMissingFile)" /> <_NETStandardSuppressedMissingFile Include="@(_NETStandardMissingFile)" Exclude="@(_NETStandardMissingFileError)" /> @@ -173,8 +176,8 @@ - - + + diff --git a/external/corefx/publish-packages.sh b/external/corefx/publish-packages.sh index 7027bd9483..783b930dbb 100755 --- a/external/corefx/publish-packages.sh +++ b/external/corefx/publish-packages.sh @@ -1,4 +1,4 @@ #!/usr/bin/env bash working_tree_root="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -$working_tree_root/run.sh publish-packages $* +$working_tree_root/run.sh publish-packages "$@" exit $? diff --git a/external/corefx/run-test.sh b/external/corefx/run-test.sh index 1fdc2d7991..9e9e0eac89 100755 --- a/external/corefx/run-test.sh +++ b/external/corefx/run-test.sh @@ -119,7 +119,9 @@ case $CPUName in aarch64) __Arch=arm64 ;; - + amd64) + __Arch=x64 + ;; *) echo "Unknown CPU $CPUName detected, configuring as if for x64" __Arch=x64 @@ -385,7 +387,7 @@ if [ $RunTestSequential -eq 1 ] then maxProcesses=1; else - if [ `uname` = "NetBSD" ]; then + if [ `uname` = "NetBSD" ] || [ `uname` = "FreeBSD" ]; then maxProcesses=$(($(getconf NPROCESSORS_ONLN)+1)) else maxProcesses=$(($(getconf _NPROCESSORS_ONLN)+1)) diff --git a/external/corefx/run.cmd b/external/corefx/run.cmd index eca223aaa9..7a2fb13559 100644 --- a/external/corefx/run.cmd +++ b/external/corefx/run.cmd @@ -21,7 +21,7 @@ if defined VisualStudioVersion goto :Run set _VSWHERE="%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" if exist %_VSWHERE% ( - for /f "usebackq tokens=*" %%i in (`%_VSWHERE% -latest -property installationPath`) do set _VSCOMNTOOLS=%%i\Common7\Tools + for /f "usebackq tokens=*" %%i in (`%_VSWHERE% -latest -prerelease -property installationPath`) do set _VSCOMNTOOLS=%%i\Common7\Tools ) if not exist "%_VSCOMNTOOLS%" set _VSCOMNTOOLS=%VS140COMNTOOLS% if not exist "%_VSCOMNTOOLS%" ( @@ -37,6 +37,11 @@ call "%_VSCOMNTOOLS%\VsDevCmd.bat" :: misleading value (such as 'MCD' in HP PCs) may lead to build breakage (issue: #69). set Platform= +:: Disable telemetry, first time experience, and global sdk look for the CLI +set DOTNET_CLI_TELEMETRY_OPTOUT=1 +set DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 +set DOTNET_MULTILEVEL_LOOKUP=0 + :: Restore the Tools directory call %~dp0init-tools.cmd if NOT [%ERRORLEVEL%]==[0] exit /b 1 diff --git a/external/corefx/run.sh b/external/corefx/run.sh index e090ef467c..36a209c533 100755 --- a/external/corefx/run.sh +++ b/external/corefx/run.sh @@ -2,6 +2,11 @@ __scriptpath=$(cd "$(dirname "$0")"; pwd -P) +# Disable telemetry, first time experience, and global sdk look for the CLI +export DOTNET_CLI_TELEMETRY_OPTOUT=1 +export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 +export DOTNET_MULTILEVEL_LOOKUP=0 + # Source the init-tools.sh script rather than execute in order to preserve ulimit values in child-processes. https://github.com/dotnet/corefx/issues/19152 . $__scriptpath/init-tools.sh @@ -9,5 +14,5 @@ __toolRuntime=$__scriptpath/Tools __dotnet=$__toolRuntime/dotnetcli/dotnet cd $__scriptpath -$__dotnet $__toolRuntime/run.exe $__scriptpath/config.json $* +$__dotnet $__toolRuntime/run.exe $__scriptpath/config.json "$@" exit $? diff --git a/external/corefx/src/Common/perf/PerfRunner/AssemblyAttributes.cs b/external/corefx/src/Common/perf/PerfRunner/AssemblyAttributes.cs index c1a414fe42..42582dccfb 100644 --- a/external/corefx/src/Common/perf/PerfRunner/AssemblyAttributes.cs +++ b/external/corefx/src/Common/perf/PerfRunner/AssemblyAttributes.cs @@ -2,4 +2,4 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -[assembly: System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAssembly] +[assembly: System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] diff --git a/external/corefx/src/Common/perf/PerfRunner/PerfRunner.cs b/external/corefx/src/Common/perf/PerfRunner/PerfRunner.cs index 383fa9b293..33f0a6168a 100644 --- a/external/corefx/src/Common/perf/PerfRunner/PerfRunner.cs +++ b/external/corefx/src/Common/perf/PerfRunner/PerfRunner.cs @@ -1,3 +1,8 @@ +// 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.Reflection; using System.Collections.Generic; @@ -5,14 +10,25 @@ using Microsoft.Xunit.Performance.Api; public class PerfHarness { - public static void Main(string[] args) + public static int Main(string[] args) { - using (XunitPerformanceHarness harness = new XunitPerformanceHarness(args)) + try { - foreach(var testName in GetTestAssemblies()) + using (XunitPerformanceHarness harness = new XunitPerformanceHarness(args)) { - harness.RunBenchmarks(GetTestAssembly(testName)); + foreach(var testName in GetTestAssemblies()) + { + harness.RunBenchmarks(GetTestAssembly(testName)); + } } + + return 0; + } + catch (Exception ex) + { + Console.WriteLine("[ERROR] Benchmark execution failed."); + Console.WriteLine($" {ex.ToString()}"); + return 1; } } @@ -27,4 +43,4 @@ public class PerfHarness { return Directory.EnumerateFiles(".", "*.Performance.Tests.dll"); } -} \ No newline at end of file +} diff --git a/external/corefx/src/Common/perf/PerfRunner/PerfRunner.csproj b/external/corefx/src/Common/perf/PerfRunner/PerfRunner.csproj index d0babba17c..f6707c29b4 100644 --- a/external/corefx/src/Common/perf/PerfRunner/PerfRunner.csproj +++ b/external/corefx/src/Common/perf/PerfRunner/PerfRunner.csproj @@ -1,4 +1,4 @@ - + @@ -9,21 +9,19 @@ true false 0436 - true + true - - - - Common\System\Diagnostics\CodeAnalysis\ExcludeFromCodeCoverageAssemblyAttribute.cs + + Common\System\Diagnostics\CodeAnalysis\ExcludeFromCodeCoverageAttribute.cs @@ -32,4 +30,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/Common/src/CoreLib/Internal/Runtime/CompilerServices/Unsafe.cs b/external/corefx/src/Common/src/CoreLib/Internal/Runtime/CompilerServices/Unsafe.cs new file mode 100644 index 0000000000..cd3dd052a2 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Internal/Runtime/CompilerServices/Unsafe.cs @@ -0,0 +1,331 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.Versioning; + +#if BIT64 +using nuint = System.UInt64; +using nint = System.Int64; +#else +using nuint = System.UInt32; +using nint = System.Int32; +#endif + +// +// The implementations of most the methods in this file are provided as intrinsics. +// In CoreCLR, the body of the functions are replaced by the EE with unsafe code. See see getILIntrinsicImplementationForUnsafe for details. +// In CoreRT, see Internal.IL.Stubs.UnsafeIntrinsics for details. +// + +namespace Internal.Runtime.CompilerServices +{ + // + // Subsetted clone of System.Runtime.CompilerServices.Unsafe for internal runtime use. + // Keep in sync with https://github.com/dotnet/corefx/tree/master/src/System.Runtime.CompilerServices.Unsafe. + // + + /// + /// For internal use only. Contains generic, low-level functionality for manipulating pointers. + /// + [CLSCompliant(false)] + public static unsafe class Unsafe + { + /// + /// Returns a pointer to the given by-ref parameter. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void* AsPointer(ref T value) + { + throw new PlatformNotSupportedException(); + + // ldarg.0 + // conv.u + // ret + } + + /// + /// Returns the size of an object of the given type parameter. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int SizeOf() + { +#if CORECLR + typeof(T).ToString(); // Type token used by the actual method body +#endif + throw new PlatformNotSupportedException(); + + // sizeof !!0 + // ret + } + + /// + /// Casts the given object to the specified type, performs no dynamic type checking. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T As(object value) where T : class + { + throw new PlatformNotSupportedException(); + + // ldarg.0 + // ret + } + + /// + /// Reinterprets the given reference as a reference to a value of type . + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref TTo As(ref TFrom source) + { + throw new PlatformNotSupportedException(); + + // ldarg.0 + // ret + } + + /// + /// Adds an element offset to the given reference. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T Add(ref T source, int elementOffset) + { +#if CORECLR + typeof(T).ToString(); // Type token used by the actual method body + throw new PlatformNotSupportedException(); +#else + return ref AddByteOffset(ref source, (IntPtr)(elementOffset * (nint)SizeOf())); +#endif + } + + /// + /// Adds an element offset to the given reference. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T Add(ref T source, IntPtr elementOffset) + { +#if CORECLR + typeof(T).ToString(); // Type token used by the actual method body + throw new PlatformNotSupportedException(); +#else + return ref AddByteOffset(ref source, (IntPtr)((nint)elementOffset * (nint)SizeOf())); +#endif + } + + /// + /// Adds an element offset to the given pointer. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void* Add(void* source, int elementOffset) + { +#if CORECLR + typeof(T).ToString(); // Type token used by the actual method body + throw new PlatformNotSupportedException(); +#else + return (byte*)source + (elementOffset * (nint)SizeOf()); +#endif + } + + /// + /// Adds an element offset to the given reference. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ref T AddByteOffset(ref T source, nuint byteOffset) + { + return ref AddByteOffset(ref source, (IntPtr)(void*)byteOffset); + } + + /// + /// Determines whether the specified references point to the same location. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool AreSame(ref T left, ref T right) + { + throw new PlatformNotSupportedException(); + + // ldarg.0 + // ldarg.1 + // ceq + // ret + } + + /// + /// Initializes a block of memory at the given location with a given initial value + /// without assuming architecture dependent alignment of the address. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void InitBlockUnaligned(ref byte startAddress, byte value, uint byteCount) + { + for (uint i = 0; i < byteCount; i++) + AddByteOffset(ref startAddress, i) = value; + } + + /// + /// Reads a value of type from the given location. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T ReadUnaligned(void* source) + { +#if CORECLR + typeof(T).ToString(); // Type token used by the actual method body + throw new PlatformNotSupportedException(); +#else + return Unsafe.As(ref *(byte*)source); +#endif + } + + /// + /// Reads a value of type from the given location. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T ReadUnaligned(ref byte source) + { +#if CORECLR + typeof(T).ToString(); // Type token used by the actual method body + throw new PlatformNotSupportedException(); +#else + return Unsafe.As(ref source); +#endif + } + + /// + /// Writes a value of type to the given location. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteUnaligned(void* destination, T value) + { +#if CORECLR + typeof(T).ToString(); // Type token used by the actual method body + throw new PlatformNotSupportedException(); +#else + Unsafe.As(ref *(byte*)destination) = value; +#endif + } + + /// + /// Writes a value of type to the given location. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteUnaligned(ref byte destination, T value) + { +#if CORECLR + typeof(T).ToString(); // Type token used by the actual method body + throw new PlatformNotSupportedException(); +#else + Unsafe.As(ref destination) = value; +#endif + } + + /// + /// Adds an element offset to the given reference. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T AddByteOffset(ref T source, IntPtr byteOffset) + { + // This method is implemented by the toolchain + throw new PlatformNotSupportedException(); + + // ldarg.0 + // ldarg.1 + // add + // ret + } + + /// + /// Reads a value of type from the given location. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Read(void* source) + { + return Unsafe.As(ref *(byte*)source); + } + + /// + /// Reads a value of type from the given location. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Read(ref byte source) + { + return Unsafe.As(ref source); + } + + /// + /// Writes a value of type to the given location. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Write(void* destination, T value) + { + Unsafe.As(ref *(byte*)destination) = value; + } + + /// + /// Writes a value of type to the given location. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Write(ref byte destination, T value) + { + Unsafe.As(ref destination) = value; + } + + /// + /// Reinterprets the given location as a reference to a value of type . + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T AsRef(void* source) + { + return ref Unsafe.As(ref *(byte*)source); + } + + /// + /// Determines the byte offset from origin to target from the given references. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IntPtr ByteOffset(ref T origin, ref T target) + { + throw new PlatformNotSupportedException(); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/Interop.Errors.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/Interop.Errors.cs new file mode 100644 index 0000000000..4248434db3 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/Interop.Errors.cs @@ -0,0 +1,207 @@ +// 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.Runtime.InteropServices; + +internal static partial class Interop +{ + /// Common Unix errno error codes. + internal enum Error + { + // These values were defined in src/Native/System.Native/fxerrno.h + // + // They compare against values obtained via Interop.Sys.GetLastError() not Marshal.GetLastWin32Error() + // which obtains the raw errno that varies between unixes. The strong typing as an enum is meant to + // prevent confusing the two. Casting to or from int is suspect. Use GetLastErrorInfo() if you need to + // correlate these to the underlying platform values or obtain the corresponding error message. + // + + SUCCESS = 0, + + E2BIG = 0x10001, // Argument list too long. + EACCES = 0x10002, // Permission denied. + EADDRINUSE = 0x10003, // Address in use. + EADDRNOTAVAIL = 0x10004, // Address not available. + EAFNOSUPPORT = 0x10005, // Address family not supported. + EAGAIN = 0x10006, // Resource unavailable, try again (same value as EWOULDBLOCK), + EALREADY = 0x10007, // Connection already in progress. + EBADF = 0x10008, // Bad file descriptor. + EBADMSG = 0x10009, // Bad message. + EBUSY = 0x1000A, // Device or resource busy. + ECANCELED = 0x1000B, // Operation canceled. + ECHILD = 0x1000C, // No child processes. + ECONNABORTED = 0x1000D, // Connection aborted. + ECONNREFUSED = 0x1000E, // Connection refused. + ECONNRESET = 0x1000F, // Connection reset. + EDEADLK = 0x10010, // Resource deadlock would occur. + EDESTADDRREQ = 0x10011, // Destination address required. + EDOM = 0x10012, // Mathematics argument out of domain of function. + EDQUOT = 0x10013, // Reserved. + EEXIST = 0x10014, // File exists. + EFAULT = 0x10015, // Bad address. + EFBIG = 0x10016, // File too large. + EHOSTUNREACH = 0x10017, // Host is unreachable. + EIDRM = 0x10018, // Identifier removed. + EILSEQ = 0x10019, // Illegal byte sequence. + EINPROGRESS = 0x1001A, // Operation in progress. + EINTR = 0x1001B, // Interrupted function. + EINVAL = 0x1001C, // Invalid argument. + EIO = 0x1001D, // I/O error. + EISCONN = 0x1001E, // Socket is connected. + EISDIR = 0x1001F, // Is a directory. + ELOOP = 0x10020, // Too many levels of symbolic links. + EMFILE = 0x10021, // File descriptor value too large. + EMLINK = 0x10022, // Too many links. + EMSGSIZE = 0x10023, // Message too large. + EMULTIHOP = 0x10024, // Reserved. + ENAMETOOLONG = 0x10025, // Filename too long. + ENETDOWN = 0x10026, // Network is down. + ENETRESET = 0x10027, // Connection aborted by network. + ENETUNREACH = 0x10028, // Network unreachable. + ENFILE = 0x10029, // Too many files open in system. + ENOBUFS = 0x1002A, // No buffer space available. + ENODEV = 0x1002C, // No such device. + ENOENT = 0x1002D, // No such file or directory. + ENOEXEC = 0x1002E, // Executable file format error. + ENOLCK = 0x1002F, // No locks available. + ENOLINK = 0x10030, // Reserved. + ENOMEM = 0x10031, // Not enough space. + ENOMSG = 0x10032, // No message of the desired type. + ENOPROTOOPT = 0x10033, // Protocol not available. + ENOSPC = 0x10034, // No space left on device. + ENOSYS = 0x10037, // Function not supported. + ENOTCONN = 0x10038, // The socket is not connected. + ENOTDIR = 0x10039, // Not a directory or a symbolic link to a directory. + ENOTEMPTY = 0x1003A, // Directory not empty. + ENOTSOCK = 0x1003C, // Not a socket. + ENOTSUP = 0x1003D, // Not supported (same value as EOPNOTSUP). + ENOTTY = 0x1003E, // Inappropriate I/O control operation. + ENXIO = 0x1003F, // No such device or address. + EOVERFLOW = 0x10040, // Value too large to be stored in data type. + EPERM = 0x10042, // Operation not permitted. + EPIPE = 0x10043, // Broken pipe. + EPROTO = 0x10044, // Protocol error. + EPROTONOSUPPORT = 0x10045, // Protocol not supported. + EPROTOTYPE = 0x10046, // Protocol wrong type for socket. + ERANGE = 0x10047, // Result too large. + EROFS = 0x10048, // Read-only file system. + ESPIPE = 0x10049, // Invalid seek. + ESRCH = 0x1004A, // No such process. + ESTALE = 0x1004B, // Reserved. + ETIMEDOUT = 0x1004D, // Connection timed out. + ETXTBSY = 0x1004E, // Text file busy. + EXDEV = 0x1004F, // Cross-device link. + ESOCKTNOSUPPORT = 0x1005E, // Socket type not supported. + EPFNOSUPPORT = 0x10060, // Protocol family not supported. + ESHUTDOWN = 0x1006C, // Socket shutdown. + EHOSTDOWN = 0x10070, // Host is down. + ENODATA = 0x10071, // No data available. + + // POSIX permits these to have the same value and we make them always equal so + // that CoreFX cannot introduce a dependency on distinguishing between them that + // would not work on all platforms. + EOPNOTSUPP = ENOTSUP, // Operation not supported on socket. + EWOULDBLOCK = EAGAIN, // Operation would block. + } + + + // Represents a platform-agnostic Error and underlying platform-specific errno + internal struct ErrorInfo + { + private Error _error; + private int _rawErrno; + + internal ErrorInfo(int errno) + { + _error = Interop.Sys.ConvertErrorPlatformToPal(errno); + _rawErrno = errno; + } + + internal ErrorInfo(Error error) + { + _error = error; + _rawErrno = -1; + } + + internal Error Error + { + get { return _error; } + } + + internal int RawErrno + { + get { return _rawErrno == -1 ? (_rawErrno = Interop.Sys.ConvertErrorPalToPlatform(_error)) : _rawErrno; } + } + + internal string GetErrorMessage() + { + return Interop.Sys.StrError(RawErrno); + } + + public override string ToString() + { + return string.Format( + "RawErrno: {0} Error: {1} GetErrorMessage: {2}", // No localization required; text is member names used for debugging purposes + RawErrno, Error, GetErrorMessage()); + } + } + + internal partial class Sys + { + internal static Error GetLastError() + { + return ConvertErrorPlatformToPal(Marshal.GetLastWin32Error()); + } + + internal static ErrorInfo GetLastErrorInfo() + { + return new ErrorInfo(Marshal.GetLastWin32Error()); + } + + internal static unsafe string StrError(int platformErrno) + { + int maxBufferLength = 1024; // should be long enough for most any UNIX error + byte* buffer = stackalloc byte[maxBufferLength]; + byte* message = StrErrorR(platformErrno, buffer, maxBufferLength); + + if (message == null) + { + // This means the buffer was not large enough, but still contains + // as much of the error message as possible and is guaranteed to + // be null-terminated. We're not currently resizing/retrying because + // maxBufferLength is large enough in practice, but we could do + // so here in the future if necessary. + message = buffer; + } + + return Marshal.PtrToStringAnsi((IntPtr)message); + } + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_ConvertErrorPlatformToPal")] + internal static extern Error ConvertErrorPlatformToPal(int platformErrno); + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_ConvertErrorPalToPlatform")] + internal static extern int ConvertErrorPalToPlatform(Error error); + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_StrErrorR")] + private static unsafe extern byte* StrErrorR(int platformErrno, byte* buffer, int bufferSize); + } +} + +// NOTE: extension method can't be nested inside Interop class. +internal static class InteropErrorExtensions +{ + // Intended usage is e.g. Interop.Error.EFAIL.Info() for brevity + // vs. new Interop.ErrorInfo(Interop.Error.EFAIL) for synthesizing + // errors. Errors originated from the system should be obtained + // via GetLastErrorInfo(), not GetLastError().Info() as that will + // convert twice, which is not only inefficient but also lossy if + // we ever encounter a raw errno that no equivalent in the Error + // enum. + public static Interop.ErrorInfo Info(this Interop.Error error) + { + return new Interop.ErrorInfo(error); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/Interop.IOErrors.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/Interop.IOErrors.cs new file mode 100644 index 0000000000..5babcd1d05 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/Interop.IOErrors.cs @@ -0,0 +1,172 @@ +// 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.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + private static void ThrowExceptionForIoErrno(ErrorInfo errorInfo, string path, bool isDirectory, Func errorRewriter) + { + Debug.Assert(errorInfo.Error != Error.SUCCESS); + Debug.Assert(errorInfo.Error != Error.EINTR, "EINTR errors should be handled by the native shim and never bubble up to managed code"); + + if (errorRewriter != null) + { + errorInfo = errorRewriter(errorInfo); + } + + throw Interop.GetExceptionForIoErrno(errorInfo, path, isDirectory); + } + + internal static void CheckIo(Error error, string path = null, bool isDirectory = false, Func errorRewriter = null) + { + if (error != Interop.Error.SUCCESS) + { + ThrowExceptionForIoErrno(error.Info(), path, isDirectory, errorRewriter); + } + } + + /// + /// Validates the result of system call that returns greater than or equal to 0 on success + /// and less than 0 on failure, with errno set to the error code. + /// If the system call failed for any reason, an exception is thrown. Otherwise, the system call succeeded. + /// + /// The result of the system call. + /// The path with which this error is associated. This may be null. + /// true if the is known to be a directory; otherwise, false. + /// Optional function to change an error code prior to processing it. + /// + /// On success, returns the non-negative result long that was validated. + /// + internal static long CheckIo(long result, string path = null, bool isDirectory = false, Func errorRewriter = null) + { + if (result < 0) + { + ThrowExceptionForIoErrno(Sys.GetLastErrorInfo(), path, isDirectory, errorRewriter); + } + + return result; + } + + /// + /// Validates the result of system call that returns greater than or equal to 0 on success + /// and less than 0 on failure, with errno set to the error code. + /// If the system call failed for any reason, an exception is thrown. Otherwise, the system call succeeded. + /// + /// + /// On success, returns the non-negative result int that was validated. + /// + internal static int CheckIo(int result, string path = null, bool isDirectory = false, Func errorRewriter = null) + { + CheckIo((long)result, path, isDirectory, errorRewriter); + + return result; + } + + /// + /// Validates the result of system call that returns greater than or equal to 0 on success + /// and less than 0 on failure, with errno set to the error code. + /// If the system call failed for any reason, an exception is thrown. Otherwise, the system call succeeded. + /// + /// + /// On success, returns the non-negative result IntPtr that was validated. + /// + internal static IntPtr CheckIo(IntPtr result, string path = null, bool isDirectory = false, Func errorRewriter = null) + { + CheckIo((long)result, path, isDirectory, errorRewriter); + + return result; + } + + /// + /// Validates the result of system call that returns greater than or equal to 0 on success + /// and less than 0 on failure, with errno set to the error code. + /// If the system call failed for any reason, an exception is thrown. Otherwise, the system call succeeded. + /// + /// + /// On success, returns the valid SafeFileHandle that was validated. + /// + internal static TSafeHandle CheckIo(TSafeHandle handle, string path = null, bool isDirectory = false, Func errorRewriter = null) + where TSafeHandle : SafeHandle + { + if (handle.IsInvalid) + { + ThrowExceptionForIoErrno(Sys.GetLastErrorInfo(), path, isDirectory, errorRewriter); + } + + return handle; + } + + /// + /// Gets an Exception to represent the supplied error info. + /// + /// The error info + /// The path with which this error is associated. This may be null. + /// true if the is known to be a directory; otherwise, false. + /// + internal static Exception GetExceptionForIoErrno(ErrorInfo errorInfo, string path = null, bool isDirectory = false) + { + // Translate the errno into a known set of exception types. For cases where multiple errnos map + // to the same exception type, include an inner exception with the details. + switch (errorInfo.Error) + { + case Error.ENOENT: + if (isDirectory) + { + return !string.IsNullOrEmpty(path) ? + new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, path)) : + new DirectoryNotFoundException(SR.IO_PathNotFound_NoPathName); + } + else + { + return !string.IsNullOrEmpty(path) ? + new FileNotFoundException(SR.Format(SR.IO_FileNotFound_FileName, path), path) : + new FileNotFoundException(SR.IO_FileNotFound); + } + + case Error.EACCES: + case Error.EBADF: + case Error.EPERM: + Exception inner = GetIOException(errorInfo); + return !string.IsNullOrEmpty(path) ? + new UnauthorizedAccessException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, path), inner) : + new UnauthorizedAccessException(SR.UnauthorizedAccess_IODenied_NoPathName, inner); + + case Error.ENAMETOOLONG: + return !string.IsNullOrEmpty(path) ? + new PathTooLongException(SR.Format(SR.IO_PathTooLong_Path, path)) : + new PathTooLongException(SR.IO_PathTooLong); + + case Error.EWOULDBLOCK: + return !string.IsNullOrEmpty(path) ? + new IOException(SR.Format(SR.IO_SharingViolation_File, path), errorInfo.RawErrno) : + new IOException(SR.IO_SharingViolation_NoFileName, errorInfo.RawErrno); + + case Error.ECANCELED: + return new OperationCanceledException(); + + case Error.EFBIG: + return new ArgumentOutOfRangeException("value", SR.ArgumentOutOfRange_FileLengthTooBig); + + case Error.EEXIST: + if (!string.IsNullOrEmpty(path)) + { + return new IOException(SR.Format(SR.IO_FileExists_Name, path), errorInfo.RawErrno); + } + goto default; + + default: + return GetIOException(errorInfo); + } + } + + internal static Exception GetIOException(Interop.ErrorInfo errorInfo) + { + return new IOException(errorInfo.GetErrorMessage(), errorInfo.RawErrno); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/Interop.Libraries.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/Interop.Libraries.cs new file mode 100644 index 0000000000..7b3dea453d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/Interop.Libraries.cs @@ -0,0 +1,12 @@ +// 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. + +internal static partial class Interop +{ + internal static partial class Libraries + { + internal const string GlobalizationInterop = "System.Globalization.Native"; + internal const string SystemNative = "System.Native"; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.Calendar.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.Calendar.cs new file mode 100644 index 0000000000..7b3caeabdd --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.Calendar.cs @@ -0,0 +1,33 @@ +// 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.Globalization; +using System.Runtime.InteropServices; +using System.Text; + +internal static partial class Interop +{ + internal static partial class GlobalizationInterop + { + internal delegate void EnumCalendarInfoCallback( + [MarshalAs(UnmanagedType.LPWStr)] string calendarString, + IntPtr context); + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetCalendars")] + internal static extern int GetCalendars(string localeName, CalendarId[] calendars, int calendarsCapacity); + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetCalendarInfo")] + internal static extern ResultCode GetCalendarInfo(string localeName, CalendarId calendarId, CalendarDataType calendarDataType, [Out] StringBuilder result, int resultCapacity); + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_EnumCalendarInfo")] + internal static extern bool EnumCalendarInfo(EnumCalendarInfoCallback callback, string localeName, CalendarId calendarId, CalendarDataType calendarDataType, IntPtr context); + + [DllImport(Libraries.GlobalizationInterop, EntryPoint = "GlobalizationNative_GetLatestJapaneseEra")] + internal static extern int GetLatestJapaneseEra(); + + [DllImport(Libraries.GlobalizationInterop, EntryPoint = "GlobalizationNative_GetJapaneseEraStartDate")] + internal static extern bool GetJapaneseEraStartDate(int era, out int startYear, out int startMonth, out int startDay); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.Casing.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.Casing.cs new file mode 100644 index 0000000000..769506b8f6 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.Casing.cs @@ -0,0 +1,23 @@ +// 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.Runtime.InteropServices; +using System.Security; +using System.Text; + +internal static partial class Interop +{ + internal static partial class GlobalizationInterop + { + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_ChangeCase")] + internal unsafe static extern void ChangeCase(char* src, int srcLen, char* dstBuffer, int dstBufferCapacity, bool bToUpper); + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_ChangeCaseInvariant")] + internal unsafe static extern void ChangeCaseInvariant(char* src, int srcLen, char* dstBuffer, int dstBufferCapacity, bool bToUpper); + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_ChangeCaseTurkish")] + internal unsafe static extern void ChangeCaseTurkish(char* src, int srcLen, char* dstBuffer, int dstBufferCapacity, bool bToUpper); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.Collation.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.Collation.cs new file mode 100644 index 0000000000..683845dbc1 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.Collation.cs @@ -0,0 +1,69 @@ +// 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.Globalization; +using System.Runtime.InteropServices; +using System.Security; + +internal static partial class Interop +{ + internal static partial class GlobalizationInterop + { + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetSortHandle")] + internal unsafe static extern ResultCode GetSortHandle(byte[] localeName, out SafeSortHandle sortHandle); + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_CloseSortHandle")] + internal unsafe static extern void CloseSortHandle(IntPtr handle); + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_CompareString")] + internal unsafe static extern int CompareString(SafeSortHandle sortHandle, char* lpStr1, int cwStr1Len, char* lpStr2, int cwStr2Len, CompareOptions options); + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_IndexOf")] + internal unsafe static extern int IndexOf(SafeSortHandle sortHandle, string target, int cwTargetLength, char* pSource, int cwSourceLength, CompareOptions options, int* matchLengthPtr); + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_LastIndexOf")] + internal unsafe static extern int LastIndexOf(SafeSortHandle sortHandle, string target, int cwTargetLength, char* pSource, int cwSourceLength, CompareOptions options); + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_IndexOfOrdinalIgnoreCase")] + internal unsafe static extern int IndexOfOrdinalIgnoreCase(string target, int cwTargetLength, char* pSource, int cwSourceLength, bool findLast); + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_StartsWith")] + [return: MarshalAs(UnmanagedType.Bool)] + internal unsafe static extern bool StartsWith(SafeSortHandle sortHandle, string target, int cwTargetLength, string source, int cwSourceLength, CompareOptions options); + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_EndsWith")] + [return: MarshalAs(UnmanagedType.Bool)] + internal unsafe static extern bool EndsWith(SafeSortHandle sortHandle, string target, int cwTargetLength, string source, int cwSourceLength, CompareOptions options); + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetSortKey")] + internal unsafe static extern int GetSortKey(SafeSortHandle sortHandle, string str, int strLength, byte* sortKey, int sortKeyLength, CompareOptions options); + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_CompareStringOrdinalIgnoreCase")] + internal unsafe static extern int CompareStringOrdinalIgnoreCase(char* lpStr1, int cwStr1Len, char* lpStr2, int cwStr2Len); + + [DllImport(Libraries.GlobalizationInterop, EntryPoint = "GlobalizationNative_GetSortVersion")] + internal static extern int GetSortVersion(SafeSortHandle sortHandle); + + internal class SafeSortHandle : SafeHandle + { + private SafeSortHandle() : + base(IntPtr.Zero, true) + { + } + + public override bool IsInvalid + { + get { return handle == IntPtr.Zero; } + } + + protected override bool ReleaseHandle() + { + CloseSortHandle(handle); + SetHandle(IntPtr.Zero); + return true; + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.ICU.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.ICU.cs new file mode 100644 index 0000000000..c690884145 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.ICU.cs @@ -0,0 +1,16 @@ +// 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.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +internal static partial class Interop +{ + internal static partial class GlobalizationInterop + { + [DllImport(Libraries.GlobalizationInterop, EntryPoint = "GlobalizationNative_LoadICU")] + internal static extern int LoadICU(); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.Idna.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.Idna.cs new file mode 100644 index 0000000000..43c72281ae --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.Idna.cs @@ -0,0 +1,21 @@ +// 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.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class GlobalizationInterop + { + internal const int AllowUnassigned = 0x1; + internal const int UseStd3AsciiRules = 0x2; + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_ToAscii")] + internal static unsafe extern int ToAscii(uint flags, char* src, int srcLen, char* dstBuffer, int dstBufferCapacity); + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_ToUnicode")] + internal static unsafe extern int ToUnicode(uint flags, char* src, int srcLen, char* dstBuffer, int dstBufferCapacity); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.Locale.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.Locale.cs new file mode 100644 index 0000000000..fcea708ee8 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.Locale.cs @@ -0,0 +1,40 @@ +// 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.Runtime.InteropServices; +using System.Text; + +internal static partial class Interop +{ + internal static partial class GlobalizationInterop + { + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocaleName")] + [return: MarshalAs(UnmanagedType.Bool)] + internal unsafe static extern bool GetLocaleName(string localeName, [Out] StringBuilder value, int valueLength); + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocaleInfoString")] + [return: MarshalAs(UnmanagedType.Bool)] + internal unsafe static extern bool GetLocaleInfoString(string localeName, uint localeStringData, [Out] StringBuilder value, int valueLength); + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetDefaultLocaleName")] + [return: MarshalAs(UnmanagedType.Bool)] + internal unsafe static extern bool GetDefaultLocaleName([Out] StringBuilder value, int valueLength); + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocaleTimeFormat")] + [return: MarshalAs(UnmanagedType.Bool)] + internal unsafe static extern bool GetLocaleTimeFormat(string localeName, bool shortFormat, [Out] StringBuilder value, int valueLength); + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocaleInfoInt")] + [return: MarshalAs(UnmanagedType.Bool)] + internal unsafe static extern bool GetLocaleInfoInt(string localeName, uint localeNumberData, ref int value); + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocaleInfoGroupingSizes")] + [return: MarshalAs(UnmanagedType.Bool)] + internal unsafe static extern bool GetLocaleInfoGroupingSizes(string localeName, uint localeGroupingData, ref int primaryGroupSize, ref int secondaryGroupSize); + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocales")] + internal unsafe static extern int GetLocales([Out] Char[] value, int valueLength); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.Normalization.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.Normalization.cs new file mode 100644 index 0000000000..c4cb9fb851 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.Normalization.cs @@ -0,0 +1,19 @@ +// 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.Runtime.InteropServices; +using System.Text; + +internal static partial class Interop +{ + internal static partial class GlobalizationInterop + { + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_IsNormalized")] + internal static extern int IsNormalized(NormalizationForm normalizationForm, string src, int srcLen); + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_NormalizeString")] + internal static extern int NormalizeString(NormalizationForm normalizationForm, string src, int srcLen, [Out] char[] dstBuffer, int dstBufferCapacity); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.ResultCode.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.ResultCode.cs new file mode 100644 index 0000000000..cca6ae4dcb --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.ResultCode.cs @@ -0,0 +1,18 @@ +// 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. + +internal static partial class Interop +{ + internal static partial class GlobalizationInterop + { + // needs to be kept in sync with ResultCode in System.Globalization.Native + internal enum ResultCode + { + Success = 0, + UnknownError = 1, + InsufficentBuffer = 2, + OutOfMemory = 3 + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.TimeZoneInfo.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.TimeZoneInfo.cs new file mode 100644 index 0000000000..271ec3f9dc --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.TimeZoneInfo.cs @@ -0,0 +1,28 @@ +// 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.Runtime.InteropServices; +using System.Text; + +internal static partial class Interop +{ + internal static partial class GlobalizationInterop + { + // needs to be kept in sync with TimeZoneDisplayNameType in System.Globalization.Native + internal enum TimeZoneDisplayNameType + { + Generic = 0, + Standard = 1, + DaylightSavings = 2, + } + + [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetTimeZoneDisplayName")] + internal static extern ResultCode GetTimeZoneDisplayName( + string localeName, + string timeZoneId, + TimeZoneDisplayNameType type, + [Out] StringBuilder result, + int resultLength); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.Utils.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.Utils.cs new file mode 100644 index 0000000000..33b10c0d74 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Globalization.Native/Interop.Utils.cs @@ -0,0 +1,52 @@ +// 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.Text; + +internal static partial class Interop +{ + /// + /// Helper for making interop calls that return a string, but we don't know + /// the correct size of buffer to make. So invoke the interop call with an + /// increasing buffer until the size is big enough. + /// + internal static bool CallStringMethod( + Func interopCall, + TArg1 arg1, + TArg2 arg2, + TArg3 arg3, + out string result) + { + const int initialStringSize = 80; + const int maxDoubleAttempts = 5; + + StringBuilder stringBuilder = StringBuilderCache.Acquire(initialStringSize); + + for (int i = 0; i < maxDoubleAttempts; i++) + { + GlobalizationInterop.ResultCode resultCode = interopCall(arg1, arg2, arg3, stringBuilder); + + if (resultCode == GlobalizationInterop.ResultCode.Success) + { + result = StringBuilderCache.GetStringAndRelease(stringBuilder); + return true; + } + else if (resultCode == GlobalizationInterop.ResultCode.InsufficentBuffer) + { + // increase the string size and loop + stringBuilder.EnsureCapacity(stringBuilder.Capacity * 2); + } + else + { + // if there is an unknown error, don't proceed + break; + } + } + + StringBuilderCache.Release(stringBuilder); + result = null; + return false; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Close.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Close.cs new file mode 100644 index 0000000000..8d192398a0 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Close.cs @@ -0,0 +1,15 @@ +// 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.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Sys + { + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Close", SetLastError = true)] + internal static extern int Close(IntPtr fd); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.FLock.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.FLock.cs new file mode 100644 index 0000000000..22934a3e77 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.FLock.cs @@ -0,0 +1,31 @@ +// 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.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class Sys + { + internal enum LockOperations : int + { + LOCK_SH = 1, /* shared lock */ + LOCK_EX = 2, /* exclusive lock */ + LOCK_NB = 4, /* don't block when locking*/ + LOCK_UN = 8, /* unlock */ + } + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_FLock", SetLastError = true)] + internal static extern int FLock(SafeFileHandle fd, LockOperations operation); + + /// + /// Exposing this for SafeFileHandle.ReleaseHandle() to call. + /// Normal callers should use FLock(SafeFileHandle fd). + /// + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_FLock", SetLastError = true)] + internal static extern int FLock(IntPtr fd, LockOperations operation); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.FSync.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.FSync.cs new file mode 100644 index 0000000000..e3ab970931 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.FSync.cs @@ -0,0 +1,15 @@ +// 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.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class Sys + { + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_FSync", SetLastError = true)] + internal static extern int FSync(SafeFileHandle fd); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.FTruncate.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.FTruncate.cs new file mode 100644 index 0000000000..5dad650362 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.FTruncate.cs @@ -0,0 +1,15 @@ +// 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.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class Sys + { + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_FTruncate", SetLastError = true)] + internal static extern int FTruncate(SafeFileHandle fd, long length); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.GetCwd.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.GetCwd.cs new file mode 100644 index 0000000000..a8bef6bde8 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.GetCwd.cs @@ -0,0 +1,74 @@ +// 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.Buffers; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Sys + { + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetCwd", SetLastError = true)] + private static extern unsafe byte* GetCwd(byte* buffer, int bufferLength); + + internal static unsafe string GetCwd() + { + const int StackLimit = 256; + + // First try to get the path into a buffer on the stack + byte* stackBuf = stackalloc byte[StackLimit]; + string result = GetCwdHelper(stackBuf, StackLimit); + if (result != null) + { + return result; + } + + // If that was too small, try increasing large buffer sizes + int bufferSize = StackLimit; + do + { + checked { bufferSize *= 2; } + byte[] buf = ArrayPool.Shared.Rent(bufferSize); + try + { + fixed (byte* ptr = &buf[0]) + { + result = GetCwdHelper(ptr, buf.Length); + if (result != null) + { + return result; + } + } + } + finally + { + ArrayPool.Shared.Return(buf); + } + } + while (true); + } + + private static unsafe string GetCwdHelper(byte* ptr, int bufferSize) + { + // Call the real getcwd + byte* result = GetCwd(ptr, bufferSize); + + // If it returned non-null, the null-terminated path is in the buffer + if (result != null) + { + return Marshal.PtrToStringAnsi((IntPtr)ptr); + } + + // Otherwise, if it failed due to the buffer being too small, return null; + // for anything else, throw. + ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); + if (errorInfo.Error == Interop.Error.ERANGE) + { + return null; + } + throw Interop.GetExceptionForIoErrno(errorInfo); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.GetRandomBytes.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.GetRandomBytes.cs new file mode 100644 index 0000000000..62156e8d8e --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.GetRandomBytes.cs @@ -0,0 +1,21 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal unsafe partial class Sys + { + [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_GetNonCryptographicallySecureRandomBytes")] + internal static unsafe extern void GetNonCryptographicallySecureRandomBytes(byte* buffer, int length); + } + + internal static unsafe void GetRandomBytes(byte* buffer, int length) + { + Sys.GetNonCryptographicallySecureRandomBytes(buffer, length); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.LSeek.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.LSeek.cs new file mode 100644 index 0000000000..7f8df7c6bf --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.LSeek.cs @@ -0,0 +1,22 @@ +// 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.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class Sys + { + internal enum SeekWhence + { + SEEK_SET = 0, + SEEK_CUR = 1, + SEEK_END = 2 + } + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_LSeek", SetLastError = true)] + internal static extern long LSeek(SafeFileHandle fd, long offset, SeekWhence whence); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.LockFileRegion.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.LockFileRegion.cs new file mode 100644 index 0000000000..23b48a4f5d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.LockFileRegion.cs @@ -0,0 +1,21 @@ +// 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.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Sys + { + internal enum LockType : short + { + F_WRLCK = 1, // exclusive or write lock + F_UNLCK = 2 // unlock + } + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_LockFileRegion", SetLastError=true)] + internal static extern int LockFileRegion(SafeHandle fd, long offset, long length, LockType lockType); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.MksTemps.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.MksTemps.cs new file mode 100644 index 0000000000..b8694d9007 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.MksTemps.cs @@ -0,0 +1,17 @@ +// 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.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Sys + { + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_MksTemps", SetLastError = true)] + internal static extern IntPtr MksTemps( + byte[] template, + int suffixlen); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Open.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Open.cs new file mode 100644 index 0000000000..a9a994c78c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Open.cs @@ -0,0 +1,15 @@ +// 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.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class Sys + { + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Open", SetLastError = true)] + internal static extern SafeFileHandle Open(string filename, OpenFlags flags, int mode); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.OpenFlags.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.OpenFlags.cs new file mode 100644 index 0000000000..f9e54c3cbc --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.OpenFlags.cs @@ -0,0 +1,27 @@ +// 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; + +internal static partial class Interop +{ + internal static partial class Sys + { + [Flags] + internal enum OpenFlags + { + // Access modes (mutually exclusive) + O_RDONLY = 0x0000, + O_WRONLY = 0x0001, + O_RDWR = 0x0002, + + // Flags (combinable) + O_CLOEXEC = 0x0010, + O_CREAT = 0x0020, + O_EXCL = 0x0040, + O_TRUNC = 0x0080, + O_SYNC = 0x0100, + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.PathConf.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.PathConf.cs new file mode 100644 index 0000000000..eb9e32db0c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.PathConf.cs @@ -0,0 +1,30 @@ +// 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.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Sys + { + internal static int DEFAULT_PC_NAME_MAX = 255; + + internal enum PathConfName : int + { + PC_LINK_MAX = 1, + PC_MAX_CANON = 2, + PC_MAX_INPUT = 3, + PC_NAME_MAX = 4, + PC_PATH_MAX = 5, + PC_PIPE_BUF = 6, + PC_CHOWN_RESTRICTED = 7, + PC_NO_TRUNC = 8, + PC_VDISABLE = 9, + } + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_PathConf", SetLastError = true)] + private static extern int PathConf(string path, PathConfName name); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Permissions.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Permissions.cs new file mode 100644 index 0000000000..f1d13787d2 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Permissions.cs @@ -0,0 +1,32 @@ +// 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; + +internal static partial class Interop +{ + internal static partial class Sys + { + [Flags] + internal enum Permissions + { + Mask = S_IRWXU | S_IRWXG | S_IRWXO, + + S_IRWXU = S_IRUSR | S_IWUSR | S_IXUSR, + S_IRUSR = 0x100, + S_IWUSR = 0x80, + S_IXUSR = 0x40, + + S_IRWXG = S_IRGRP | S_IWGRP | S_IXGRP, + S_IRGRP = 0x20, + S_IWGRP = 0x10, + S_IXGRP = 0x8, + + S_IRWXO = S_IROTH | S_IWOTH | S_IXOTH, + S_IROTH = 0x4, + S_IWOTH = 0x2, + S_IXOTH = 0x1, + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.PosixFAdvise.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.PosixFAdvise.cs new file mode 100644 index 0000000000..ad8b73aed2 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.PosixFAdvise.cs @@ -0,0 +1,36 @@ +// 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.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class Sys + { + internal enum FileAdvice : int + { + POSIX_FADV_NORMAL = 0, /* no special advice, the default value */ + POSIX_FADV_RANDOM = 1, /* random I/O access */ + POSIX_FADV_SEQUENTIAL = 2, /* sequential I/O access */ + POSIX_FADV_WILLNEED = 3, /* will need specified pages */ + POSIX_FADV_DONTNEED = 4, /* don't need the specified pages */ + POSIX_FADV_NOREUSE = 5, /* data will only be accessed once */ + } + + /// + /// Notifies the OS kernel that the specified file will be accessed in a particular way soon; this allows the kernel to + /// potentially optimize the access pattern of the file. + /// + /// The file descriptor of the file + /// The start of the region to advise about + /// The number of bytes of the region (until the end of the file if 0) + /// The type of advice to give the kernel about the specified region + /// + /// Returns 0 on success; otherwise, the error code is returned + /// + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_PosixFAdvise", SetLastError = false /* this is explicitly called out in the man page */)] + internal static extern int PosixFAdvise(SafeFileHandle fd, long offset, long length, FileAdvice advice); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Read.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Read.cs new file mode 100644 index 0000000000..812ae348dc --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Read.cs @@ -0,0 +1,25 @@ +// 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.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class Sys + { + /// + /// Reads a number of bytes from an open file descriptor into a specified buffer. + /// + /// The open file descriptor to try to read from + /// The buffer to read info into + /// The size of the buffer + /// + /// Returns the number of bytes read on success; otherwise, -1 is returned + /// Note - on fail. the position of the stream may change depending on the platform; consult man 2 read for more info + /// + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Read", SetLastError = true)] + internal static unsafe extern int Read(SafeFileHandle fd, byte* buffer, int count); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.ReadLink.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.ReadLink.cs new file mode 100644 index 0000000000..50f1ae545e --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.ReadLink.cs @@ -0,0 +1,63 @@ +// 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.Runtime.InteropServices; +using System.Buffers; +using System.Text; + +internal static partial class Interop +{ + internal static partial class Sys + { + /// + /// Takes a path to a symbolic link and attempts to place the link target path into the buffer. If the buffer is too + /// small, the path will be truncated. No matter what, the buffer will not be null terminated. + /// + /// The path to the symlink + /// The buffer to hold the output path + /// The size of the buffer + /// + /// Returns the number of bytes placed into the buffer on success; bufferSize if the buffer is too small; and -1 on error. + /// + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_ReadLink", SetLastError = true)] + private static extern unsafe int ReadLink(string path, byte[] buffer, int bufferSize); + + /// + /// Takes a path to a symbolic link and returns the link target path. + /// + /// The path to the symlink + /// + /// Returns the link to the target path on success; and null otherwise. + /// + public static string ReadLink(string path) + { + int bufferSize = 256; + do + { + byte[] buffer = ArrayPool.Shared.Rent(bufferSize); + try + { + int resultLength = Interop.Sys.ReadLink(path, buffer, buffer.Length); + if (resultLength < 0) + { + // error + return null; + } + else if (resultLength < buffer.Length) + { + // success + return Encoding.UTF8.GetString(buffer, 0, resultLength); + } + } + finally + { + ArrayPool.Shared.Return(buffer); + } + + // buffer was too small, loop around again and try with a larger buffer. + bufferSize *= 2; + } while (true); + } + } +} \ No newline at end of file diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Stat.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Stat.cs new file mode 100644 index 0000000000..0ca199b70d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Stat.cs @@ -0,0 +1,65 @@ +// 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.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class Sys + { + // Even though csc will by default use a sequential layout, a CS0649 warning as error + // is produced for un-assigned fields when no StructLayout is specified. + // + // Explicitly saying Sequential disables that warning/error for consumers which only + // use Stat in debug builds. + [StructLayout(LayoutKind.Sequential)] + internal struct FileStatus + { + internal FileStatusFlags Flags; + internal int Mode; + internal uint Uid; + internal uint Gid; + internal long Size; + internal long ATime; + internal long ATimeNsec; + internal long MTime; + internal long MTimeNsec; + internal long CTime; + internal long CTimeNsec; + internal long BirthTime; + internal long BirthTimeNsec; + internal long Dev; + internal long Ino; + } + + internal static class FileTypes + { + internal const int S_IFMT = 0xF000; + internal const int S_IFIFO = 0x1000; + internal const int S_IFCHR = 0x2000; + internal const int S_IFDIR = 0x4000; + internal const int S_IFREG = 0x8000; + internal const int S_IFLNK = 0xA000; + internal const int S_IFSOCK = 0xC000; + } + + [Flags] + internal enum FileStatusFlags + { + None = 0, + HasBirthTime = 1, + } + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_FStat2", SetLastError = true)] + internal static extern int FStat(SafeFileHandle fd, out FileStatus output); + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Stat2", SetLastError = true)] + internal static extern int Stat(string path, out FileStatus output); + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_LStat2", SetLastError = true)] + internal static extern int LStat(string path, out FileStatus output); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.SysLog.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.SysLog.cs new file mode 100644 index 0000000000..6b6a74b743 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.SysLog.cs @@ -0,0 +1,58 @@ +// 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.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Sys + { + internal enum SysLogPriority : int + { + // Priorities + LOG_EMERG = 0, /* system is unusable */ + LOG_ALERT = 1, /* action must be taken immediately */ + LOG_CRIT = 2, /* critical conditions */ + LOG_ERR = 3, /* error conditions */ + LOG_WARNING = 4, /* warning conditions */ + LOG_NOTICE = 5, /* normal but significant condition */ + LOG_INFO = 6, /* informational */ + LOG_DEBUG = 7, /* debug-level messages */ + // Facilities + LOG_KERN = (0<<3), /* kernel messages */ + LOG_USER = (1<<3), /* random user-level messages */ + LOG_MAIL = (2<<3), /* mail system */ + LOG_DAEMON = (3<<3), /* system daemons */ + LOG_AUTH = (4<<3), /* authorization messages */ + LOG_SYSLOG = (5<<3), /* messages generated internally by syslogd */ + LOG_LPR = (6<<3), /* line printer subsystem */ + LOG_NEWS = (7<<3), /* network news subsystem */ + LOG_UUCP = (8<<3), /* UUCP subsystem */ + LOG_CRON = (9<<3), /* clock daemon */ + LOG_AUTHPRIV = (10<<3), /* authorization messages (private) */ + LOG_FTP = (11<<3), /* ftp daemon */ + // Between FTP and Local is reserved for system use + LOG_LOCAL0 = (16<<3), /* reserved for local use */ + LOG_LOCAL1 = (17<<3), /* reserved for local use */ + LOG_LOCAL2 = (18<<3), /* reserved for local use */ + LOG_LOCAL3 = (19<<3), /* reserved for local use */ + LOG_LOCAL4 = (20<<3), /* reserved for local use */ + LOG_LOCAL5 = (21<<3), /* reserved for local use */ + LOG_LOCAL6 = (22<<3), /* reserved for local use */ + LOG_LOCAL7 = (23<<3), /* reserved for local use */ + } + + /// + /// Write a message to the system logger, which in turn writes the message to the system console, log files, etc. + /// See man 3 syslog for more info + /// + /// + /// The OR of a priority and facility in the SysLogPriority enum to declare the priority and facility of the log entry + /// + /// The message to put in the log entry + /// Like printf, the argument is passed to the variadic part of the C++ function to wildcards in the message + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_SysLog")] + internal static extern void SysLog(SysLogPriority priority, string message, string arg1); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Unlink.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Unlink.cs new file mode 100644 index 0000000000..829210fa7e --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Unlink.cs @@ -0,0 +1,15 @@ +// 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.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Sys + { + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Unlink", SetLastError = true)] + internal static extern int Unlink(string pathname); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Write.cs b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Write.cs new file mode 100644 index 0000000000..c14fc26263 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Write.cs @@ -0,0 +1,27 @@ +// 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.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class Sys + { + /// + /// Writes the specified buffer to the provided open file descriptor + /// + /// The file descriptor to try and write to + /// The data to attempt to write + /// The amount of data to write, in bytes + /// + /// Returns the number of bytes written on success; otherwise, returns -1 and sets errno + /// + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Write", SetLastError = true)] + internal static unsafe extern int Write(SafeFileHandle fd, byte* buffer, int bufferSize); + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Write", SetLastError = true)] + internal static unsafe extern int Write(int fd, byte* buffer, int bufferSize); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/BCrypt/Interop.BCryptGenRandom.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/BCrypt/Interop.BCryptGenRandom.cs new file mode 100644 index 0000000000..bc357125b5 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/BCrypt/Interop.BCryptGenRandom.cs @@ -0,0 +1,44 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class BCrypt + { + internal static unsafe int BCryptGenRandom(byte* pbBuffer, int count) + { + Debug.Assert(pbBuffer != null); + Debug.Assert(count >= 0); + + return BCryptGenRandom(IntPtr.Zero, pbBuffer, count, BCRYPT_USE_SYSTEM_PREFERRED_RNG); + } + + private const int BCRYPT_USE_SYSTEM_PREFERRED_RNG = 0x00000002; + internal const int STATUS_SUCCESS = 0x0; + internal const int STATUS_NO_MEMORY = unchecked((int)0xC0000017); + + [DllImport(Libraries.BCrypt, CharSet = CharSet.Unicode)] + private static unsafe extern int BCryptGenRandom(IntPtr hAlgorithm, byte* pbBuffer, int cbBuffer, int dwFlags); + } + + internal static unsafe void GetRandomBytes(byte* buffer, int length) + { + int status = BCrypt.BCryptGenRandom(buffer, length); + if (status != BCrypt.STATUS_SUCCESS) + { + if (status == BCrypt.STATUS_NO_MEMORY) + { + throw new OutOfMemoryException(); + } + else + { + throw new InvalidOperationException(); + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Crypt32/Interop.CryptProtectMemory.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Crypt32/Interop.CryptProtectMemory.cs new file mode 100644 index 0000000000..b10cb6a041 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Crypt32/Interop.CryptProtectMemory.cs @@ -0,0 +1,22 @@ +// 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.Runtime.InteropServices; +using System.Security; + +internal partial class Interop +{ + internal partial class Crypt32 + { + internal const uint CRYPTPROTECTMEMORY_BLOCK_SIZE = 16; + internal const uint CRYPTPROTECTMEMORY_SAME_PROCESS = 0; + + [DllImport(Libraries.Crypt32, CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern bool CryptProtectMemory(SafeBSTRHandle pData, uint cbData, uint dwFlags); + + [DllImport(Libraries.Crypt32, CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern bool CryptUnprotectMemory(SafeBSTRHandle pData, uint cbData, uint dwFlags); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Interop.BOOL.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Interop.BOOL.cs new file mode 100644 index 0000000000..9f4dab8935 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Interop.BOOL.cs @@ -0,0 +1,21 @@ +// 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. + +internal partial class Interop +{ + /// + /// Blittable version of Windows BOOL type. It is convenient in situations where + /// manual marshalling is required, or to avoid overhead of regular bool marshalling. + /// + /// + /// Some Windows APIs return arbitrary integer values although the return type is defined + /// as BOOL. It is best to never compare BOOL to TRUE. Always use bResult != BOOL.FALSE + /// or bResult == BOOL.FALSE . + /// + internal enum BOOL : int + { + FALSE = 0, + TRUE = 1, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Interop.Errors.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Interop.Errors.cs new file mode 100644 index 0000000000..ec52a81bf6 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Interop.Errors.cs @@ -0,0 +1,45 @@ +// 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. + +internal partial class Interop +{ + // As defined in winerror.h and https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382.aspx + internal partial class Errors + { + internal const int ERROR_SUCCESS = 0x0; + internal const int ERROR_FILE_NOT_FOUND = 0x2; + internal const int ERROR_PATH_NOT_FOUND = 0x3; + internal const int ERROR_ACCESS_DENIED = 0x5; + internal const int ERROR_INVALID_HANDLE = 0x6; + internal const int ERROR_NOT_ENOUGH_MEMORY = 0x8; + internal const int ERROR_INVALID_DRIVE = 0xF; + internal const int ERROR_NO_MORE_FILES = 0x12; + internal const int ERROR_NOT_READY = 0x15; + internal const int ERROR_SHARING_VIOLATION = 0x20; + internal const int ERROR_HANDLE_EOF = 0x26; + internal const int ERROR_FILE_EXISTS = 0x50; + internal const int ERROR_INVALID_PARAMETER = 0x57; + internal const int ERROR_BROKEN_PIPE = 0x6D; + internal const int ERROR_INSUFFICIENT_BUFFER = 0x7A; + internal const int ERROR_INVALID_NAME = 0x7B; + internal const int ERROR_BAD_PATHNAME = 0xA1; + internal const int ERROR_ALREADY_EXISTS = 0xB7; + internal const int ERROR_ENVVAR_NOT_FOUND = 0xCB; + internal const int ERROR_FILENAME_EXCED_RANGE = 0xCE; + internal const int ERROR_NO_DATA = 0xE8; + internal const int ERROR_MORE_DATA = 0xEA; + internal const int ERROR_NO_MORE_ITEMS = 0x103; + internal const int ERROR_NOT_OWNER = 0x120; + internal const int ERROR_TOO_MANY_POSTS = 0x12A; + internal const int ERROR_ARITHMETIC_OVERFLOW = 0x216; + internal const int ERROR_MUTANT_LIMIT_EXCEEDED = 0x24B; + internal const int ERROR_OPERATION_ABORTED = 0x3E3; + internal const int ERROR_IO_PENDING = 0x3E5; + internal const int ERROR_NO_UNICODE_TRANSLATION = 0x459; + internal const int ERROR_NOT_FOUND = 0x490; + internal const int ERROR_BAD_IMPERSONATION_LEVEL = 0x542; + internal const int E_FILENOTFOUND = unchecked((int)0x80070002); + internal const int ERROR_TIMEOUT = 0x000005B4; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Interop.Libraries.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Interop.Libraries.cs new file mode 100644 index 0000000000..45d910bfcc --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Interop.Libraries.cs @@ -0,0 +1,16 @@ +// 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. + +internal static partial class Interop +{ + internal static partial class Libraries + { + internal const string BCrypt = "BCrypt.dll"; + internal const string Crypt32 = "crypt32.dll"; + internal const string Kernel32 = "kernel32.dll"; + internal const string Ole32 = "ole32.dll"; + internal const string OleAut32 = "oleaut32.dll"; + internal const string User32 = "user32.dll"; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs new file mode 100644 index 0000000000..16365ee651 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs @@ -0,0 +1,24 @@ +// 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 Microsoft.Win32.SafeHandles; +using System; +using System.IO; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + internal unsafe struct CREATEFILE2_EXTENDED_PARAMETERS + { + internal uint dwSize; + internal uint dwFileAttributes; + internal uint dwFileFlags; + internal uint dwSecurityQosFlags; + internal SECURITY_ATTRIBUTES* lpSecurityAttributes; + internal IntPtr hTemplateFile; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.CancelIoEx.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.CancelIoEx.cs new file mode 100644 index 0000000000..fc99e3052f --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.CancelIoEx.cs @@ -0,0 +1,16 @@ +// 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 Microsoft.Win32.SafeHandles; +using System.Runtime.InteropServices; +using System.Threading; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal static extern unsafe bool CancelIoEx(SafeHandle handle, NativeOverlapped* lpOverlapped); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.CloseHandle.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.CloseHandle.cs new file mode 100644 index 0000000000..96ed922a84 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.CloseHandle.cs @@ -0,0 +1,16 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool CloseHandle(IntPtr handle); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.CreateFile.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.CreateFile.cs new file mode 100644 index 0000000000..9ee1e16fa6 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.CreateFile.cs @@ -0,0 +1,40 @@ +// 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 Microsoft.Win32.SafeHandles; +using System; +using System.IO; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + /// + /// WARNING: This method does not implicitly handle long paths. Use CreateFile. + /// + [DllImport(Libraries.Kernel32, EntryPoint = "CreateFileW", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)] + private static extern SafeFileHandle CreateFilePrivate( + string lpFileName, + int dwDesiredAccess, + System.IO.FileShare dwShareMode, + ref SECURITY_ATTRIBUTES securityAttrs, + System.IO.FileMode dwCreationDisposition, + int dwFlagsAndAttributes, + IntPtr hTemplateFile); + + internal static SafeFileHandle CreateFile( + string lpFileName, + int dwDesiredAccess, + System.IO.FileShare dwShareMode, + ref SECURITY_ATTRIBUTES securityAttrs, + System.IO.FileMode dwCreationDisposition, + int dwFlagsAndAttributes, + IntPtr hTemplateFile) + { + lpFileName = PathInternal.EnsureExtendedPrefixOverMaxPath(lpFileName); + return CreateFilePrivate(lpFileName, dwDesiredAccess, dwShareMode, ref securityAttrs, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.CreateFile2.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.CreateFile2.cs new file mode 100644 index 0000000000..ddc18f6c42 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.CreateFile2.cs @@ -0,0 +1,32 @@ +// 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 Microsoft.Win32.SafeHandles; +using System.IO; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, EntryPoint = "CreateFile2", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern SafeFileHandle CreateFile2Private( + string lpFileName, + int dwDesiredAccess, + FileShare dwShareMode, + FileMode dwCreationDisposition, + ref Kernel32.CREATEFILE2_EXTENDED_PARAMETERS pCreateExParams); + + internal static SafeFileHandle CreateFile2( + string lpFileName, + int dwDesiredAccess, + FileShare dwShareMode, + FileMode dwCreationDisposition, + ref Kernel32.CREATEFILE2_EXTENDED_PARAMETERS pCreateExParams) + { + lpFileName = PathInternal.EnsureExtendedPrefixOverMaxPath(lpFileName); + return CreateFile2Private(lpFileName, dwDesiredAccess, dwShareMode, dwCreationDisposition, ref pCreateExParams); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FILE_INFO_BY_HANDLE_CLASS.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FILE_INFO_BY_HANDLE_CLASS.cs new file mode 100644 index 0000000000..e31a453ba9 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FILE_INFO_BY_HANDLE_CLASS.cs @@ -0,0 +1,39 @@ +// 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 Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + internal enum FILE_INFO_BY_HANDLE_CLASS : uint + { + FileBasicInfo = 0x0u, + FileStandardInfo = 0x1u, + FileNameInfo = 0x2u, + FileRenameInfo = 0x3u, + FileDispositionInfo = 0x4u, + FileAllocationInfo = 0x5u, + FileEndOfFileInfo = 0x6u, + FileStreamInfo = 0x7u, + FileCompressionInfo = 0x8u, + FileAttributeTagInfo = 0x9u, + FileIdBothDirectoryInfo = 0xAu, + FileIdBothDirectoryRestartInfo = 0xBu, + FileIoPriorityHintInfo = 0xCu, + FileRemoteProtocolInfo = 0xDu, + FileFullDirectoryInfo = 0xEu, + FileFullDirectoryRestartInfo = 0xFu, + FileStorageInfo = 0x10u, + FileAlignmentInfo = 0x11u, + FileIdInfo = 0x12u, + FileIdExtdDirectoryInfo = 0x13u, + FileIdExtdDirectoryRestartInfo = 0x14u, + MaximumFileInfoByHandleClass = 0x15u, + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FileAttributes.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FileAttributes.cs new file mode 100644 index 0000000000..725a25a719 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FileAttributes.cs @@ -0,0 +1,17 @@ +// 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. + +internal partial class Interop +{ + internal partial class Kernel32 + { + internal partial class FileAttributes + { + internal const int FILE_ATTRIBUTE_NORMAL = 0x00000080; + internal const int FILE_ATTRIBUTE_READONLY = 0x00000001; + internal const int FILE_ATTRIBUTE_DIRECTORY = 0x00000010; + internal const int FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FileTypes.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FileTypes.cs new file mode 100644 index 0000000000..1d306665b1 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FileTypes.cs @@ -0,0 +1,16 @@ +// 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. + +internal partial class Interop +{ + internal partial class Kernel32 + { + internal partial class FileTypes + { + internal const int FILE_TYPE_DISK = 0x0001; + internal const int FILE_TYPE_CHAR = 0x0002; + internal const int FILE_TYPE_PIPE = 0x0003; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FindClose.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FindClose.cs new file mode 100644 index 0000000000..03d8c8b323 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FindClose.cs @@ -0,0 +1,16 @@ +// 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 Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal extern static bool FindClose(IntPtr hFindFile); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FindFirstFileEx.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FindFirstFileEx.cs new file mode 100644 index 0000000000..80b1ddd28d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FindFirstFileEx.cs @@ -0,0 +1,43 @@ +// 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 Microsoft.Win32.SafeHandles; +using System; +using System.IO; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + /// + /// WARNING: This method does not implicitly handle long paths. Use FindFirstFile. + /// + [DllImport(Libraries.Kernel32, EntryPoint = "FindFirstFileExW", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern SafeFindHandle FindFirstFileExPrivate(string lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, ref WIN32_FIND_DATA lpFindFileData, FINDEX_SEARCH_OPS fSearchOp, IntPtr lpSearchFilter, int dwAdditionalFlags); + + internal static SafeFindHandle FindFirstFile(string fileName, ref WIN32_FIND_DATA data) + { + fileName = PathInternal.EnsureExtendedPrefixOverMaxPath(fileName); + + // use FindExInfoBasic since we don't care about short name and it has better perf + return FindFirstFileExPrivate(fileName, FINDEX_INFO_LEVELS.FindExInfoBasic, ref data, FINDEX_SEARCH_OPS.FindExSearchNameMatch, IntPtr.Zero, 0); + } + + internal enum FINDEX_INFO_LEVELS : uint + { + FindExInfoStandard = 0x0u, + FindExInfoBasic = 0x1u, + FindExInfoMaxInfoLevel = 0x2u, + } + + internal enum FINDEX_SEARCH_OPS : uint + { + FindExSearchNameMatch = 0x0u, + FindExSearchLimitToDirectories = 0x1u, + FindExSearchLimitToDevices = 0x2u, + FindExSearchMaxSearchOp = 0x3u, + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FlushFileBuffers.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FlushFileBuffers.cs new file mode 100644 index 0000000000..e10a2279cf --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FlushFileBuffers.cs @@ -0,0 +1,17 @@ +// 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 Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool FlushFileBuffers(SafeHandle hHandle); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FormatMessage.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FormatMessage.cs new file mode 100644 index 0000000000..94722b6c8b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FormatMessage.cs @@ -0,0 +1,112 @@ +// 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.Text; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + private const int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200; + private const int FORMAT_MESSAGE_FROM_HMODULE = 0x00000800; + private const int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; + private const int FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000; + + + private const int ERROR_INSUFFICIENT_BUFFER = 0x7A; + + [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, EntryPoint = "FormatMessageW", SetLastError = true, BestFitMapping = true)] + private static extern int FormatMessage( + int dwFlags, + IntPtr lpSource, + uint dwMessageId, + int dwLanguageId, + [Out] StringBuilder lpBuffer, + int nSize, + IntPtr[] arguments); + + /// + /// Returns a string message for the specified Win32 error code. + /// + internal static string GetMessage(int errorCode) + { + return GetMessage(IntPtr.Zero, errorCode); + } + + internal static string GetMessage(IntPtr moduleHandle, int errorCode) + { + var sb = new StringBuilder(InitialBufferSize); + do + { + string errorMsg; + if (TryGetErrorMessage(moduleHandle, errorCode, sb, out errorMsg)) + { + return errorMsg; + } + else + { + // increase the capacity of the StringBuilder. + sb.Capacity *= BufferSizeIncreaseFactor; + } + } + while (sb.Capacity < MaxAllowedBufferSize); + + // If you come here then a size as large as 65K is also not sufficient and so we give the generic errorMsg. + return string.Format("Unknown error (0x{0:x})", errorCode); + } + + private static bool TryGetErrorMessage(IntPtr moduleHandle, int errorCode, StringBuilder sb, out string errorMsg) + { + errorMsg = ""; + + int flags = FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY; + if (moduleHandle != IntPtr.Zero) + { + flags |= FORMAT_MESSAGE_FROM_HMODULE; + } + + int result = FormatMessage(flags, moduleHandle, (uint)errorCode, 0, sb, sb.Capacity, null); + if (result != 0) + { + int i = sb.Length; + while (i > 0) + { + char ch = sb[i - 1]; + if (ch > 32 && ch != '.') break; + i--; + } + errorMsg = sb.ToString(0, i); + } + else if (Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER) + { + return false; + } + else + { + errorMsg = string.Format("Unknown error (0x{0:x})", errorCode); + } + + return true; + } + + // Windows API FormatMessage lets you format a message string given an errorcode. + // Unlike other APIs this API does not support a way to query it for the total message size. + // + // So the API can only be used in one of these two ways. + // a. You pass a buffer of appropriate size and get the resource. + // b. Windows creates a buffer and passes the address back and the onus of releasing the buffer lies on the caller. + // + // Since the error code is coming from the user, it is not possible to know the size in advance. + // Unfortunately we can't use option b. since the buffer can only be freed using LocalFree and it is a private API on onecore. + // Also, using option b is ugly for the managed code and could cause memory leak in situations where freeing is unsuccessful. + // + // As a result we use the following approach. + // We initially call the API with a buffer size of 256 and then gradually increase the size in case of failure until we reach the maximum allowed limit of 65K. + private const int InitialBufferSize = 256; + private const int BufferSizeIncreaseFactor = 4; + private const int MaxAllowedBufferSize = 65 * 1024; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FreeEnvironmentStrings.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FreeEnvironmentStrings.cs new file mode 100644 index 0000000000..c372a8553e --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FreeEnvironmentStrings.cs @@ -0,0 +1,14 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, EntryPoint = "FreeEnvironmentStringsW", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)] + internal static extern unsafe bool FreeEnvironmentStrings(char* lpszEnvironmentBlock); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FreeLibrary.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FreeLibrary.cs new file mode 100644 index 0000000000..c70865350a --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.FreeLibrary.cs @@ -0,0 +1,15 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)] + internal static extern bool FreeLibrary(IntPtr hModule); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetCPInfo.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetCPInfo.cs new file mode 100644 index 0000000000..1665119420 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetCPInfo.cs @@ -0,0 +1,24 @@ +// 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.IO; +using System.Text; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + internal unsafe struct CPINFO + { + internal int MaxCharSize; + + internal fixed byte DefaultChar[2 /* MAX_DEFAULTCHAR */]; + internal fixed byte LeadByte[12 /* MAX_LEADBYTES */]; + } + + [DllImport(Libraries.Kernel32)] + internal static extern unsafe int GetCPInfo(uint codePage, CPINFO* lpCpInfo); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetEnvironmentStrings.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetEnvironmentStrings.cs new file mode 100644 index 0000000000..29a686026a --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetEnvironmentStrings.cs @@ -0,0 +1,14 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, EntryPoint = "GetEnvironmentStringsW", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)] + internal static extern unsafe char* GetEnvironmentStrings(); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetFileAttributesEx.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetFileAttributesEx.cs new file mode 100644 index 0000000000..4cce56bd05 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetFileAttributesEx.cs @@ -0,0 +1,94 @@ +// 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 Microsoft.Win32.SafeHandles; +using System; +using System.IO; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + /// + /// WARNING: This method does not implicitly handle long paths. Use GetFileAttributesEx. + /// + [DllImport(Libraries.Kernel32, EntryPoint = "GetFileAttributesExW", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern bool GetFileAttributesExPrivate(string name, GET_FILEEX_INFO_LEVELS fileInfoLevel, ref WIN32_FILE_ATTRIBUTE_DATA lpFileInformation); + + internal static bool GetFileAttributesEx(string name, GET_FILEEX_INFO_LEVELS fileInfoLevel, ref WIN32_FILE_ATTRIBUTE_DATA lpFileInformation) + { + name = PathInternal.EnsureExtendedPrefixOverMaxPath(name); + return GetFileAttributesExPrivate(name, fileInfoLevel, ref lpFileInformation); + } + + internal enum GET_FILEEX_INFO_LEVELS : uint + { + GetFileExInfoStandard = 0x0u, + GetFileExMaxInfoLevel = 0x1u, + } + + internal struct WIN32_FILE_ATTRIBUTE_DATA + { + internal int fileAttributes; + internal uint ftCreationTimeLow; + internal uint ftCreationTimeHigh; + internal uint ftLastAccessTimeLow; + internal uint ftLastAccessTimeHigh; + internal uint ftLastWriteTimeLow; + internal uint ftLastWriteTimeHigh; + internal uint fileSizeHigh; + internal uint fileSizeLow; + + internal void PopulateFrom(ref WIN32_FIND_DATA findData) + { + // Copy the information to data + fileAttributes = (int)findData.dwFileAttributes; + ftCreationTimeLow = findData.ftCreationTime.dwLowDateTime; + ftCreationTimeHigh = findData.ftCreationTime.dwHighDateTime; + ftLastAccessTimeLow = findData.ftLastAccessTime.dwLowDateTime; + ftLastAccessTimeHigh = findData.ftLastAccessTime.dwHighDateTime; + ftLastWriteTimeLow = findData.ftLastWriteTime.dwLowDateTime; + ftLastWriteTimeHigh = findData.ftLastWriteTime.dwHighDateTime; + fileSizeHigh = findData.nFileSizeHigh; + fileSizeLow = findData.nFileSizeLow; + } + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + [BestFitMapping(false)] + internal unsafe struct WIN32_FIND_DATA + { + internal uint dwFileAttributes; + internal FILE_TIME ftCreationTime; + internal FILE_TIME ftLastAccessTime; + internal FILE_TIME ftLastWriteTime; + internal uint nFileSizeHigh; + internal uint nFileSizeLow; + internal uint dwReserved0; + internal uint dwReserved1; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + internal string cFileName; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] + internal string cAlternateFileName; + } + + internal struct FILE_TIME + { + internal uint dwLowDateTime; + internal uint dwHighDateTime; + + internal FILE_TIME(long fileTime) + { + dwLowDateTime = (uint)fileTime; + dwHighDateTime = (uint)(fileTime >> 32); + } + + internal long ToTicks() + { + return ((long)dwHighDateTime << 32) + dwLowDateTime; + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetFileInformationByHandleEx.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetFileInformationByHandleEx.cs new file mode 100644 index 0000000000..1106cff1c5 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetFileInformationByHandleEx.cs @@ -0,0 +1,25 @@ +// 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 Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal static extern bool GetFileInformationByHandleEx(SafeFileHandle hFile, FILE_INFO_BY_HANDLE_CLASS FileInformationClass, out FILE_STANDARD_INFO lpFileInformation, uint dwBufferSize); + + internal struct FILE_STANDARD_INFO + { + internal long AllocationSize; + internal long EndOfFile; + internal uint NumberOfLinks; + internal BOOL DeletePending; + internal BOOL Directory; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetFileType_SafeHandle.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetFileType_SafeHandle.cs new file mode 100644 index 0000000000..c07a1683a5 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetFileType_SafeHandle.cs @@ -0,0 +1,15 @@ +// 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 Microsoft.Win32.SafeHandles; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal extern static int GetFileType(SafeHandle hFile); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetFullPathNameW.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetFullPathNameW.cs new file mode 100644 index 0000000000..15dd581113 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetFullPathNameW.cs @@ -0,0 +1,18 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + /// + /// WARNING: This method does not implicitly handle long paths. Use GetFullPathName or PathHelper. + /// + [DllImport(Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false, ExactSpelling = true)] + unsafe internal static extern uint GetFullPathNameW(char* path, uint numBufferChars, char[] buffer, IntPtr mustBeZero); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetLongPathNameW.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetLongPathNameW.cs new file mode 100644 index 0000000000..ce04078af5 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetLongPathNameW.cs @@ -0,0 +1,18 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + /// + /// WARNING: This method does not implicitly handle long paths. Use GetFullPath/PathHelper. + /// + [DllImport(Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false, ExactSpelling = true)] + internal static extern uint GetLongPathNameW(char[] lpszShortPath, char[] lpszLongPath, uint cchBuffer); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetTempFileNameW.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetTempFileNameW.cs new file mode 100644 index 0000000000..36673895b4 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetTempFileNameW.cs @@ -0,0 +1,16 @@ +// 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.Text; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false)] + internal static extern uint GetTempFileNameW(string tmpPath, string prefix, uint uniqueIdOrZero, [Out]StringBuilder tmpFileName); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetTempPathW.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetTempPathW.cs new file mode 100644 index 0000000000..ff2783be26 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.GetTempPathW.cs @@ -0,0 +1,16 @@ +// 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.IO; +using System.Text; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, BestFitMapping = false)] + internal static extern uint GetTempPathW(int bufferLen, [Out]StringBuilder buffer); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.Globalization.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.Globalization.cs new file mode 100644 index 0000000000..00bec5d3e2 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.Globalization.cs @@ -0,0 +1,133 @@ +// 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.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static unsafe partial class Kernel32 + { + internal const int LOCALE_NAME_MAX_LENGTH = 85; + internal const uint LOCALE_ALLOW_NEUTRAL_NAMES = 0x08000000; // Flag to allow returning neutral names/lcids for name conversion + internal const uint LOCALE_SUPPLEMENTAL = 0x00000002; + internal const uint LOCALE_REPLACEMENT = 0x00000008; + internal const uint LOCALE_NEUTRALDATA = 0x00000010; + internal const uint LOCALE_SPECIFICDATA = 0x00000020; + internal const int COMPARE_STRING = 0x0001; + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + internal extern static unsafe int LCIDToLocaleName(int locale, char *pLocaleName, int cchName, uint dwFlags); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + internal extern static int LocaleNameToLCID(string lpName, uint dwFlags); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + internal extern static unsafe int LCMapStringEx( + string lpLocaleName, + uint dwMapFlags, + char* lpSrcStr, + int cchSrc, + void* lpDestStr, + int cchDest, + void* lpVersionInformation, + void* lpReserved, + IntPtr sortHandle); + + [DllImport("kernel32.dll", EntryPoint = "FindNLSStringEx")] + internal extern static unsafe int FindNLSStringEx( + char* lpLocaleName, + uint dwFindNLSStringFlags, + char* lpStringSource, + int cchSource, + char* lpStringValue, + int cchValue, + int* pcchFound, + void* lpVersionInformation, + void* lpReserved, + IntPtr sortHandle); + + [DllImport("kernel32.dll", EntryPoint = "CompareStringEx")] + internal extern static unsafe int CompareStringEx( + char* lpLocaleName, + uint dwCmpFlags, + char* lpString1, + int cchCount1, + char* lpString2, + int cchCount2, + void* lpVersionInformation, + void* lpReserved, + IntPtr lParam); + + [DllImport("kernel32.dll", EntryPoint = "CompareStringOrdinal")] + internal extern static unsafe int CompareStringOrdinal( + char* lpString1, + int cchCount1, + char* lpString2, + int cchCount2, + bool bIgnoreCase); + + [DllImport("kernel32.dll", EntryPoint = "FindStringOrdinal")] + internal extern static unsafe int FindStringOrdinal( + uint dwFindStringOrdinalFlags, + char* lpStringSource, + int cchSource, + char* lpStringValue, + int cchValue, + int bIgnoreCase); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + internal extern static unsafe bool IsNLSDefinedString( + int Function, + uint dwFlags, + IntPtr lpVersionInformation, + char* lpString, + int cchStr); + +#if !PROJECTN + [DllImport("Kernel32.dll", CharSet = CharSet.Auto)] + internal static extern bool GetUserPreferredUILanguages(uint dwFlags, out uint pulNumLanguages, char [] pwszLanguagesBuffer, ref uint pcchLanguagesBuffer); +#endif //!PROJECTN + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + internal static extern int GetLocaleInfoEx(string lpLocaleName, uint LCType, void* lpLCData, int cchData); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + internal extern static bool EnumSystemLocalesEx(EnumLocalesProcEx lpLocaleEnumProcEx, uint dwFlags, void* lParam, IntPtr reserved); + + internal delegate BOOL EnumLocalesProcEx(char* lpLocaleString, uint dwFlags, void* lParam); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + internal extern static int ResolveLocaleName(string lpNameToResolve, char* lpLocaleName, int cchLocaleName); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + internal extern static bool EnumTimeFormatsEx(EnumTimeFormatsProcEx lpTimeFmtEnumProcEx, string lpLocaleName, uint dwFlags, void* lParam); + + internal delegate BOOL EnumTimeFormatsProcEx(char* lpTimeFormatString, void* lParam); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + internal extern static int GetCalendarInfoEx(string lpLocaleName, uint Calendar, IntPtr lpReserved, uint CalType, IntPtr lpCalData, int cchData, out int lpValue); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + internal extern static int GetCalendarInfoEx(string lpLocaleName, uint Calendar, IntPtr lpReserved, uint CalType, IntPtr lpCalData, int cchData, IntPtr lpValue); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + internal extern static bool EnumCalendarInfoExEx(EnumCalendarInfoProcExEx pCalInfoEnumProcExEx, string lpLocaleName, uint Calendar, string lpReserved, uint CalType, void* lParam); + + internal delegate BOOL EnumCalendarInfoProcExEx(char* lpCalendarInfoString, uint Calendar, IntPtr lpReserved, void* lParam); + + [StructLayout(LayoutKind.Sequential)] + internal struct NlsVersionInfoEx + { + internal int dwNLSVersionInfoSize; + internal int dwNLSVersion; + internal int dwDefinedVersion; + internal int dwEffectiveId; + internal Guid guidCustomVersion; + } + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + internal extern static unsafe bool GetNLSVersionEx(int function, string localeName, NlsVersionInfoEx* lpVersionInformation); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.LoadLibraryEx.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.LoadLibraryEx.cs new file mode 100644 index 0000000000..4eef5852fe --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.LoadLibraryEx.cs @@ -0,0 +1,18 @@ +// 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 Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + internal const int LOAD_LIBRARY_AS_DATAFILE = 0x00000002; + + [DllImport(Libraries.Kernel32, EntryPoint = "LoadLibraryExW", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern SafeLibraryHandle LoadLibraryEx(string libFilename, IntPtr reserved, int flags); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.LockFile.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.LockFile.cs new file mode 100644 index 0000000000..a21d00f4f6 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.LockFile.cs @@ -0,0 +1,20 @@ +// 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 Microsoft.Win32.SafeHandles; +using System; +using System.IO; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal static extern bool LockFile(SafeFileHandle handle, int offsetLow, int offsetHigh, int countLow, int countHigh); + + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal static extern bool UnlockFile(SafeFileHandle handle, int offsetLow, int offsetHigh, int countLow, int countHigh); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.OutputDebugString.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.OutputDebugString.cs new file mode 100644 index 0000000000..8da50ff8ab --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.OutputDebugString.cs @@ -0,0 +1,14 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Interop.Libraries.Kernel32, CharSet = CharSet.Unicode, EntryPoint = "OutputDebugStringW", ExactSpelling = true)] + internal static extern void OutputDebugString(string message); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.ReadFile_SafeHandle_IntPtr.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.ReadFile_SafeHandle_IntPtr.cs new file mode 100644 index 0000000000..076f7f136f --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.ReadFile_SafeHandle_IntPtr.cs @@ -0,0 +1,21 @@ +// 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 Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal static extern unsafe int ReadFile( + SafeHandle handle, + byte* bytes, + int numBytesToRead, + out int numBytesRead, + IntPtr mustBeZero); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.ReadFile_SafeHandle_NativeOverlapped.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.ReadFile_SafeHandle_NativeOverlapped.cs new file mode 100644 index 0000000000..3ae65a8806 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.ReadFile_SafeHandle_NativeOverlapped.cs @@ -0,0 +1,22 @@ +// 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 Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; +using System.Threading; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal static extern unsafe int ReadFile( + SafeHandle handle, + byte* bytes, + int numBytesToRead, + IntPtr numBytesRead_mustBeZero, + NativeOverlapped* overlapped); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.SECURITY_ATTRIBUTES.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.SECURITY_ATTRIBUTES.cs new file mode 100644 index 0000000000..8d31f8622f --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.SECURITY_ATTRIBUTES.cs @@ -0,0 +1,21 @@ +// 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 Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [StructLayout(LayoutKind.Sequential)] + internal struct SECURITY_ATTRIBUTES + { + internal uint nLength; + internal IntPtr lpSecurityDescriptor; + internal BOOL bInheritHandle; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.SecurityOptions.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.SecurityOptions.cs new file mode 100644 index 0000000000..4a4402484f --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.SecurityOptions.cs @@ -0,0 +1,18 @@ +// 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. + +internal partial class Interop +{ + internal partial class Kernel32 + { + internal partial class SecurityOptions + { + internal const int SECURITY_SQOS_PRESENT = 0x00100000; + internal const int SECURITY_ANONYMOUS = 0 << 16; + internal const int SECURITY_IDENTIFICATION = 1 << 16; + internal const int SECURITY_IMPERSONATION = 2 << 16; + internal const int SECURITY_DELEGATION = 3 << 16; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.SetEndOfFile.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.SetEndOfFile.cs new file mode 100644 index 0000000000..e5d60041a8 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.SetEndOfFile.cs @@ -0,0 +1,15 @@ +// 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 Microsoft.Win32.SafeHandles; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal static extern bool SetEndOfFile(SafeFileHandle hFile); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.SetEnvironmentVariable.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.SetEnvironmentVariable.cs new file mode 100644 index 0000000000..cff406fca5 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.SetEnvironmentVariable.cs @@ -0,0 +1,14 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, EntryPoint = "SetEnvironmentVariableW", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)] + internal static extern bool SetEnvironmentVariable(string lpName, string lpValue); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.SetFilePointerEx.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.SetFilePointerEx.cs new file mode 100644 index 0000000000..c0e5247a52 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.SetFilePointerEx.cs @@ -0,0 +1,15 @@ +// 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 Microsoft.Win32.SafeHandles; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal static extern bool SetFilePointerEx(SafeFileHandle hFile, long liDistanceToMove, out long lpNewFilePointer, uint dwMoveMethod); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.SetThreadErrorMode.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.SetThreadErrorMode.cs new file mode 100644 index 0000000000..123eb75d7b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.SetThreadErrorMode.cs @@ -0,0 +1,16 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, SetLastError = true, ExactSpelling = true)] + internal static extern bool SetThreadErrorMode(uint dwNewMode, out uint lpOldMode); + + internal const uint SEM_FAILCRITICALERRORS = 1; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.WideCharToMultiByte.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.WideCharToMultiByte.cs new file mode 100644 index 0000000000..a06c9153f4 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.WideCharToMultiByte.cs @@ -0,0 +1,22 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32)] + internal static extern unsafe int WideCharToMultiByte( + uint CodePage, uint dwFlags, + char* lpWideCharStr, int cchWideChar, + byte* lpMultiByteStr, int cbMultiByte, + IntPtr lpDefaultChar, IntPtr lpUsedDefaultChar); + + internal const uint CP_ACP = 0; + internal const uint WC_NO_BEST_FIT_CHARS = 0x00000400; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.WriteFile_SafeHandle_IntPtr.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.WriteFile_SafeHandle_IntPtr.cs new file mode 100644 index 0000000000..69651ca1ca --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.WriteFile_SafeHandle_IntPtr.cs @@ -0,0 +1,23 @@ +// 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 Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + // Note there are two different WriteFile prototypes - this is to use + // the type system to force you to not trip across a "feature" in + // Win32's async IO support. You can't do the following three things + // simultaneously: overlapped IO, free the memory for the overlapped + // struct in a callback (or an EndWrite method called by that callback), + // and pass in an address for the numBytesRead parameter. + + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal static extern unsafe int WriteFile(SafeHandle handle, byte* bytes, int numBytesToWrite, out int numBytesWritten, IntPtr mustBeZero); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.WriteFile_SafeHandle_NativeOverlapped.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.WriteFile_SafeHandle_NativeOverlapped.cs new file mode 100644 index 0000000000..dc1e97555b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Kernel32/Interop.WriteFile_SafeHandle_NativeOverlapped.cs @@ -0,0 +1,22 @@ +// 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 Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; +using System.Threading; +internal partial class Interop +{ + internal partial class Kernel32 + { + // Note there are two different WriteFile prototypes - this is to use + // the type system to force you to not trip across a "feature" in + // Win32's async IO support. You can't do the following three things + // simultaneously: overlapped IO, free the memory for the overlapped + // struct in a callback (or an EndWrite method called by that callback), + // and pass in an address for the numBytesRead parameter. + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal static extern unsafe int WriteFile(SafeHandle handle, byte* bytes, int numBytesToWrite, IntPtr numBytesWritten_mustBeZero, NativeOverlapped* lpOverlapped); + } +} diff --git a/external/corert/src/Common/src/Interop/Windows/mincore/Interop.Idna.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Normaliz/Interop.Idna.cs similarity index 50% rename from external/corert/src/Common/src/Interop/Windows/mincore/Interop.Idna.cs rename to external/corefx/src/Common/src/CoreLib/Interop/Windows/Normaliz/Interop.Idna.cs index e14f16b048..f49ce8dd61 100644 --- a/external/corert/src/Common/src/Interop/Windows/mincore/Interop.Idna.cs +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Normaliz/Interop.Idna.cs @@ -7,28 +7,26 @@ using System.Runtime.InteropServices; internal partial class Interop { - internal partial class mincore + internal partial class Normaliz { // // Idn APIs // - [DllImport("api-ms-win-core-localization-l1-2-0.dll", CharSet = CharSet.Unicode, SetLastError = true)] - internal static extern int IdnToAscii( + [DllImport("Normaliz.dll", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern unsafe int IdnToAscii( uint dwFlags, - IntPtr lpUnicodeCharStr, + char* lpUnicodeCharStr, int cchUnicodeChar, - [System.Runtime.InteropServices.OutAttribute()] - IntPtr lpASCIICharStr, + char* lpASCIICharStr, int cchASCIIChar); - [DllImport("api-ms-win-core-localization-l1-2-0.dll", CharSet = CharSet.Unicode, SetLastError = true)] - internal static extern int IdnToUnicode( + [DllImport("Normaliz.dll", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern unsafe int IdnToUnicode( uint dwFlags, - IntPtr lpASCIICharStr, + char* lpASCIICharStr, int cchASCIIChar, - [System.Runtime.InteropServices.OutAttribute()] - IntPtr lpUnicodeCharStr, + char* lpUnicodeCharStr, int cchUnicodeChar); internal const int IDN_ALLOW_UNASSIGNED = 0x1; diff --git a/external/corert/src/Common/src/Interop/Windows/mincore/Interop.Normalization.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Normaliz/Interop.Normalization.cs similarity index 54% rename from external/corert/src/Common/src/Interop/Windows/mincore/Interop.Normalization.cs rename to external/corefx/src/Common/src/CoreLib/Interop/Windows/Normaliz/Interop.Normalization.cs index 17047e9b28..3e49f1f64c 100644 --- a/external/corert/src/Common/src/Interop/Windows/mincore/Interop.Normalization.cs +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/Normaliz/Interop.Normalization.cs @@ -7,24 +7,12 @@ using System.Runtime.InteropServices; internal partial class Interop { - // These are error codes we get back from the Normalization DLL - internal const int ERROR_SUCCESS = 0; - internal const int ERROR_NOT_ENOUGH_MEMORY = 8; - internal const int ERROR_INVALID_PARAMETER = 87; - internal const int ERROR_INSUFFICIENT_BUFFER = 122; - internal const int ERROR_INVALID_NAME = 123; - internal const int ERROR_NO_UNICODE_TRANSLATION = 1113; - - internal partial class mincore + internal partial class Normaliz { - // - // Normalization APIs - // - - [DllImport("api-ms-win-core-normalization-l1-1-0.dll", CharSet = CharSet.Unicode, SetLastError = true)] + [DllImport("Normaliz.dll", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool IsNormalizedString(int normForm, string source, int length); - [DllImport("api-ms-win-core-normalization-l1-1-0.dll", CharSet = CharSet.Unicode, SetLastError = true)] + [DllImport("Normaliz.dll", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern int NormalizeString( int normForm, string source, diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/OleAut32/Interop.SysAllocStringLen.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/OleAut32/Interop.SysAllocStringLen.cs new file mode 100644 index 0000000000..1af2b3fce9 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/OleAut32/Interop.SysAllocStringLen.cs @@ -0,0 +1,19 @@ +// 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.Runtime.InteropServices; +using System.Security; + +internal partial class Interop +{ + internal partial class OleAut32 + { + [DllImport(Libraries.OleAut32, CharSet = CharSet.Unicode)] + internal static extern SafeBSTRHandle SysAllocStringLen(IntPtr src, uint len); + + [DllImport(Libraries.OleAut32, CharSet = CharSet.Unicode)] + internal static extern IntPtr SysAllocStringLen(String src, int len); + } +} diff --git a/external/corefx/src/System.IO.FileSystem/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.netstandard1.3.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/OleAut32/Interop.SysFreeString.cs similarity index 59% rename from external/corefx/src/System.IO.FileSystem/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.netstandard1.3.cs rename to external/corefx/src/Common/src/CoreLib/Interop/Windows/OleAut32/Interop.SysFreeString.cs index b99a9993fc..8673cc6724 100644 --- a/external/corefx/src/System.IO.FileSystem/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.netstandard1.3.cs +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/OleAut32/Interop.SysFreeString.cs @@ -3,14 +3,13 @@ // See the LICENSE file in the project root for more information. using System; -using System.Security; using System.Runtime.InteropServices; -using Microsoft.Win32; -namespace Microsoft.Win32.SafeHandles +internal partial class Interop { - public sealed partial class SafeFileHandle : SafeHandle + internal partial class OleAut32 { - internal bool? IsAsync { get; set; } + [DllImport(Libraries.OleAut32)] + internal static extern void SysFreeString(IntPtr bstr); } } diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/OleAut32/Interop.SysStringLen.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/OleAut32/Interop.SysStringLen.cs new file mode 100644 index 0000000000..cf65d6b086 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/OleAut32/Interop.SysStringLen.cs @@ -0,0 +1,19 @@ +// 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.Runtime.InteropServices; +using System.Security; + +internal partial class Interop +{ + internal partial class OleAut32 + { + [DllImport(Libraries.OleAut32)] + internal static extern uint SysStringLen(SafeBSTRHandle bstr); + + [DllImport(Libraries.OleAut32)] + internal static extern uint SysStringLen(IntPtr bstr); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/User32/Interop.Constants.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/User32/Interop.Constants.cs new file mode 100644 index 0000000000..f1f09740e9 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/User32/Interop.Constants.cs @@ -0,0 +1,12 @@ +// 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. + +internal partial class Interop +{ + internal partial class User32 + { + internal const int HWND_BROADCAST = 0xffff; + internal const int WM_SETTINGCHANGE = 0x001A; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/User32/Interop.LoadString.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/User32/Interop.LoadString.cs new file mode 100644 index 0000000000..d3d575e221 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/User32/Interop.LoadString.cs @@ -0,0 +1,16 @@ +// 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.Runtime.InteropServices; +using System.Text; +using Microsoft.Win32.SafeHandles; + +internal partial class Interop +{ + internal partial class User32 + { + [DllImport(Libraries.User32, SetLastError = true, EntryPoint = "LoadStringW", CharSet = CharSet.Unicode)] + internal static extern int LoadString(SafeLibraryHandle handle, int id, [Out] StringBuilder buffer, int bufferLength); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Interop/Windows/User32/Interop.SendMessageTimeout.cs b/external/corefx/src/Common/src/CoreLib/Interop/Windows/User32/Interop.SendMessageTimeout.cs new file mode 100644 index 0000000000..c8a97e6b9d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Interop/Windows/User32/Interop.SendMessageTimeout.cs @@ -0,0 +1,15 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class User32 + { + [DllImport(Libraries.User32, EntryPoint = "SendMessageTimeoutW", CharSet = CharSet.Unicode)] + public static extern IntPtr SendMessageTimeout(IntPtr hWnd, int msg, IntPtr wParam, string lParam, int flags, int timeout, IntPtr pdwResult); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Microsoft/Win32/SafeHandles/CriticalHandleMinusOneIsInvalid.cs b/external/corefx/src/Common/src/CoreLib/Microsoft/Win32/SafeHandles/CriticalHandleMinusOneIsInvalid.cs new file mode 100644 index 0000000000..a76c51d966 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Microsoft/Win32/SafeHandles/CriticalHandleMinusOneIsInvalid.cs @@ -0,0 +1,20 @@ +// 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.Runtime.InteropServices; + +namespace Microsoft.Win32.SafeHandles +{ + // Class of critical handle which uses only -1 as an invalid handle. + public abstract class CriticalHandleMinusOneIsInvalid : CriticalHandle + { + protected CriticalHandleMinusOneIsInvalid() + : base(new IntPtr(-1)) + { + } + + public override bool IsInvalid => handle == new IntPtr(-1); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Microsoft/Win32/SafeHandles/CriticalHandleZeroOrMinusOneIsInvalid.cs b/external/corefx/src/Common/src/CoreLib/Microsoft/Win32/SafeHandles/CriticalHandleZeroOrMinusOneIsInvalid.cs new file mode 100644 index 0000000000..28d0219489 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Microsoft/Win32/SafeHandles/CriticalHandleZeroOrMinusOneIsInvalid.cs @@ -0,0 +1,20 @@ +// 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.Runtime.InteropServices; + +namespace Microsoft.Win32.SafeHandles +{ + // Class of critical handle which uses 0 or -1 as an invalid handle. + public abstract class CriticalHandleZeroOrMinusOneIsInvalid : CriticalHandle + { + protected CriticalHandleZeroOrMinusOneIsInvalid() + : base(IntPtr.Zero) + { + } + + public override bool IsInvalid => handle == IntPtr.Zero || handle == new IntPtr(-1); + } +} diff --git a/external/corefx/src/System.IO.FileSystem/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs b/external/corefx/src/Common/src/CoreLib/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs similarity index 64% rename from external/corefx/src/System.IO.FileSystem/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs rename to external/corefx/src/Common/src/CoreLib/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs index 5ddb31ad36..f28f44fdad 100644 --- a/external/corefx/src/System.IO.FileSystem/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs +++ b/external/corefx/src/Common/src/CoreLib/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs @@ -4,11 +4,11 @@ using System; using System.Diagnostics; +using System.IO; using System.Runtime.InteropServices; namespace Microsoft.Win32.SafeHandles { - [System.Security.SecurityCritical] public sealed class SafeFileHandle : SafeHandleZeroOrMinusOneIsInvalid { /// A handle value of -1. @@ -39,18 +39,30 @@ namespace Microsoft.Win32.SafeHandles internal static SafeFileHandle Open(string path, Interop.Sys.OpenFlags flags, int mode) { Debug.Assert(path != null); + SafeFileHandle handle = Interop.Sys.Open(path, flags, mode); - // If we fail to open the file due to a path not existing, we need to know whether to blame - // the file itself or its directory. If we're creating the file, then we blame the directory, - // otherwise we blame the file. - bool enoentDueToDirectory = (flags & Interop.Sys.OpenFlags.O_CREAT) != 0; + if (handle.IsInvalid) + { + handle.Dispose(); + Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo(); - // Open the file. - SafeFileHandle handle = Interop.CheckIo( - Interop.Sys.Open(path, flags, mode), - path, - isDirectory: enoentDueToDirectory, - errorRewriter: e => (e.Error == Interop.Error.EISDIR) ? Interop.Error.EACCES.Info() : e); + // If we fail to open the file due to a path not existing, we need to know whether to blame + // the file itself or its directory. If we're creating the file, then we blame the directory, + // otherwise we blame the file. + // + // When opening, we need to align with Windows, which considers a missing path to be + // FileNotFound only if the containing directory exists. + + bool isDirectory = (error.Error == Interop.Error.ENOENT) && + ((flags & Interop.Sys.OpenFlags.O_CREAT) != 0 + || !DirectoryExists(Path.GetDirectoryName(PathInternal.TrimEndingDirectorySeparator(path)))); + + Interop.CheckIo( + error.Error, + path, + isDirectory, + errorRewriter: e => (e.Error == Interop.Error.EISDIR) ? Interop.Error.EACCES.Info() : e); + } // Make sure it's not a directory; we do this after opening it once we have a file descriptor // to avoid race conditions. @@ -69,6 +81,31 @@ namespace Microsoft.Win32.SafeHandles return handle; } + private static bool DirectoryExists(string fullPath) + { + int fileType = Interop.Sys.FileTypes.S_IFDIR; + + Interop.Sys.FileStatus fileinfo; + Interop.ErrorInfo errorInfo = default(Interop.ErrorInfo); + + // First use stat, as we want to follow symlinks. If that fails, it could be because the symlink + // is broken, we don't have permissions, etc., in which case fall back to using LStat to evaluate + // based on the symlink itself. + if (Interop.Sys.Stat(fullPath, out fileinfo) < 0 && + Interop.Sys.LStat(fullPath, out fileinfo) < 0) + { + errorInfo = Interop.Sys.GetLastErrorInfo(); + return false; + } + + // Something exists at this path. If the caller is asking for a directory, return true if it's + // a directory and false for everything else. If the caller is asking for a file, return false for + // a directory and true for everything else. + return + (fileType == Interop.Sys.FileTypes.S_IFDIR) == + ((fileinfo.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR); + } + /// Opens a SafeFileHandle for a file descriptor created by a provided delegate. /// /// The function that creates the file descriptor. Returns the file descriptor on success, or an invalid @@ -83,7 +120,6 @@ namespace Microsoft.Win32.SafeHandles return handle; } - [System.Security.SecurityCritical] protected override bool ReleaseHandle() { // When the SafeFileHandle was opened, we likely issued an flock on the created descriptor in order to add @@ -111,7 +147,6 @@ namespace Microsoft.Win32.SafeHandles public override bool IsInvalid { - [System.Security.SecurityCritical] get { long h = (long)handle; diff --git a/external/corefx/src/Common/src/CoreLib/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs b/external/corefx/src/Common/src/CoreLib/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs new file mode 100644 index 0000000000..4eabe8f08c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs @@ -0,0 +1,50 @@ +// 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.Security; +using System.Runtime.InteropServices; +using System.Threading; +using Microsoft.Win32; + +namespace Microsoft.Win32.SafeHandles +{ + public sealed class SafeFileHandle : SafeHandleZeroOrMinusOneIsInvalid + { + private bool? _isAsync; + + private SafeFileHandle() : base(true) + { + _isAsync = null; + } + + public SafeFileHandle(IntPtr preexistingHandle, bool ownsHandle) : base(ownsHandle) + { + SetHandle(preexistingHandle); + + _isAsync = null; + } + + internal bool? IsAsync + { + get + { + return _isAsync; + } + + set + { + _isAsync = value; + } + } + + internal ThreadPoolBoundHandle ThreadPoolBinding { get; set; } + + override protected bool ReleaseHandle() + { + return Interop.Kernel32.CloseHandle(handle); + } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/Microsoft/Win32/SafeHandles/SafeHandleMinusOneIsInvalid.cs b/external/corefx/src/Common/src/CoreLib/Microsoft/Win32/SafeHandles/SafeHandleMinusOneIsInvalid.cs new file mode 100644 index 0000000000..5415f2c35d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Microsoft/Win32/SafeHandles/SafeHandleMinusOneIsInvalid.cs @@ -0,0 +1,19 @@ +// 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.Runtime.InteropServices; + +namespace Microsoft.Win32.SafeHandles +{ + // Class of safe handle which uses only -1 as an invalid handle. + public abstract class SafeHandleMinusOneIsInvalid : SafeHandle + { + protected SafeHandleMinusOneIsInvalid(bool ownsHandle) : base(new IntPtr(-1), ownsHandle) + { + } + + public override bool IsInvalid => handle == new IntPtr(-1); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/Microsoft/Win32/SafeHandles/SafeHandleZeroOrMinusOneIsInvalid.cs b/external/corefx/src/Common/src/CoreLib/Microsoft/Win32/SafeHandles/SafeHandleZeroOrMinusOneIsInvalid.cs new file mode 100644 index 0000000000..8d0220bf90 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/Microsoft/Win32/SafeHandles/SafeHandleZeroOrMinusOneIsInvalid.cs @@ -0,0 +1,19 @@ +// 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.Runtime.InteropServices; + +namespace Microsoft.Win32.SafeHandles +{ + // Class of safe handle which uses 0 or -1 as an invalid handle. + public abstract class SafeHandleZeroOrMinusOneIsInvalid : SafeHandle + { + protected SafeHandleZeroOrMinusOneIsInvalid(bool ownsHandle) : base(IntPtr.Zero, ownsHandle) + { + } + + public override bool IsInvalid => handle == IntPtr.Zero || handle == new IntPtr(-1); + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeLibraryHandle.cs b/external/corefx/src/Common/src/CoreLib/Microsoft/Win32/SafeHandles/SafeLibraryHandle.cs similarity index 76% rename from external/corert/src/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeLibraryHandle.cs rename to external/corefx/src/Common/src/CoreLib/Microsoft/Win32/SafeHandles/SafeLibraryHandle.cs index 5543ba7a8d..3be2e354ab 100644 --- a/external/corert/src/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeLibraryHandle.cs +++ b/external/corefx/src/Common/src/CoreLib/Microsoft/Win32/SafeHandles/SafeLibraryHandle.cs @@ -2,10 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.Win32.SafeHandles; -using System; - -namespace Microsoft.Win32 +namespace Microsoft.Win32.SafeHandles { sealed internal class SafeLibraryHandle : SafeHandleZeroOrMinusOneIsInvalid { @@ -13,7 +10,7 @@ namespace Microsoft.Win32 override protected bool ReleaseHandle() { - return Interop.mincore.FreeLibrary(handle); + return Interop.Kernel32.FreeLibrary(handle); } } } diff --git a/external/corefx/src/Common/src/CoreLib/README.md b/external/corefx/src/Common/src/CoreLib/README.md new file mode 100644 index 0000000000..beda969ff7 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/README.md @@ -0,0 +1,19 @@ +# System.Private.CoreLib Shared Sources + +This directory contains the shared sources for System.Private.CoreLib. These are shared between [dotnet/corert](https://github.com/dotnet/corert/tree/master/src/System.Private.CoreLib/shared), [dotnet/coreclr](https://github.com/dotnet/coreclr/tree/master/src/mscorlib/shared) and [dotnet/corefx](https://github.com/dotnet/corefx/tree/master/src/Common/src/CoreLib). + +The sources are synchronized with a mirroring tool that watches for new commits on either side and creates new pull requests (as @dotnet-bot) in the other repository. + +## Conventions + +Code in the shared directory should have no code specific to CoreCLR, CoreRT or CoreFX. Parts of classes that need to have different implementations on different runtimes should use partial classes and *.CoreRT.cs/*.CoreCLR.cs/*.CoreFX.cs files in the non shared portion. Code that is different based on platform (Windows/Unix) is fine to leave in the shared portion. Remember to follow the [style guidelines](https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/coding-style.md). + +## Getting clean CI and merging the mirror PRs + +Once the mirror PR is created there is a chance that the new code will require changes to get a clean CI. Any changes can be added to the PR by checking out the PR branch and adding new commits. Please follow the following guidelines for modifying these PRs. + + - **DO NOT** modify the commits made by @dotnet-bot in any way. + - **TRY** to only make changes outside of shared. + - Changes made in the shared folder in additional commits will get mirrored properly if the mirror PR is merged with a **REBASE** + - **ALWAYS** Merge the mirror PR with the **REBASE** option. + - Using one of the other options will cause the mirror to miss commits diff --git a/external/corefx/src/Common/src/CoreLib/System.Private.CoreLib.Shared.projitems b/external/corefx/src/Common/src/CoreLib/System.Private.CoreLib.Shared.projitems new file mode 100644 index 0000000000..e49edfd367 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System.Private.CoreLib.Shared.projitems @@ -0,0 +1,754 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + c5ed3c1d-b572-46f1-8f96-522a85ce1179 + + + + + + false + false + false + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/corert/src/System.Private.CoreLib/src/System/AccessViolationException.cs b/external/corefx/src/Common/src/CoreLib/System/AccessViolationException.cs similarity index 62% rename from external/corert/src/System.Private.CoreLib/src/System/AccessViolationException.cs rename to external/corefx/src/Common/src/CoreLib/System/AccessViolationException.cs index 0ad6c36a5b..280d9b8a97 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/AccessViolationException.cs +++ b/external/corefx/src/Common/src/CoreLib/System/AccessViolationException.cs @@ -17,26 +17,35 @@ using System.Runtime.Serialization; namespace System { [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class AccessViolationException : SystemException { public AccessViolationException() : base(SR.Arg_AccessViolationException) { - HResult = __HResults.E_POINTER; + HResult = HResults.E_POINTER; } public AccessViolationException(String message) : base(message) { - HResult = __HResults.E_POINTER; + HResult = HResults.E_POINTER; } public AccessViolationException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.E_POINTER; + HResult = HResults.E_POINTER; } - protected AccessViolationException(SerializationInfo info, StreamingContext context) : base(info, context) { } + protected AccessViolationException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + +#pragma warning disable 169 // Field is not used from managed. + private IntPtr _ip; // Address of faulting instruction. + private IntPtr _target; // Address that could not be accessed. + private int _accessType; // 0:read, 1:write +#pragma warning restore 169 } } diff --git a/external/corefx/src/Common/src/CoreLib/System/Action.cs b/external/corefx/src/Common/src/CoreLib/System/Action.cs new file mode 100644 index 0000000000..6e3ccff48c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Action.cs @@ -0,0 +1,38 @@ +// 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. + +namespace System +{ + public delegate void Action(); + public delegate void Action(T obj); + public delegate void Action(T1 arg1, T2 arg2); + public delegate void Action(T1 arg1, T2 arg2, T3 arg3); + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4); + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6); + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7); + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8); + + public delegate TResult Func(); + public delegate TResult Func(T arg); + public delegate TResult Func(T1 arg1, T2 arg2); + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3); + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4); + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6); + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7); + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8); + + public delegate int Comparison(T x, T y); + + public delegate TOutput Converter(TInput input); + + public delegate bool Predicate(T obj); +} + +namespace System.Buffers +{ + public delegate void SpanAction(Span span, TArg arg); + public delegate void ReadOnlySpanAction(ReadOnlySpan span, TArg arg); +} diff --git a/external/corefx/src/Common/src/CoreLib/System/AggregateException.cs b/external/corefx/src/Common/src/CoreLib/System/AggregateException.cs new file mode 100644 index 0000000000..99ba703a52 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/AggregateException.cs @@ -0,0 +1,484 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.ExceptionServices; +using System.Runtime.Serialization; +using System.Security; +using System.Text; +using System.Threading; + +namespace System +{ + /// Represents one or more errors that occur during application execution. + /// + /// is used to consolidate multiple failures into a single, throwable + /// exception object. + /// + [Serializable] + [DebuggerDisplay("Count = {InnerExceptionCount}")] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class AggregateException : Exception + { + private ReadOnlyCollection m_innerExceptions; // Complete set of exceptions. Do not rename (binary serialization) + + /// + /// Initializes a new instance of the class. + /// + public AggregateException() + : base(SR.AggregateException_ctor_DefaultMessage) + { + m_innerExceptions = new ReadOnlyCollection(Array.Empty()); + } + + /// + /// Initializes a new instance of the class with + /// a specified error message. + /// + /// The error message that explains the reason for the exception. + public AggregateException(string message) + : base(message) + { + m_innerExceptions = new ReadOnlyCollection(Array.Empty()); + } + + /// + /// Initializes a new instance of the class with a specified error + /// message and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception. + /// The argument + /// is null. + public AggregateException(string message, Exception innerException) + : base(message, innerException) + { + if (innerException == null) + { + throw new ArgumentNullException(nameof(innerException)); + } + + m_innerExceptions = new ReadOnlyCollection(new Exception[] { innerException }); + } + + /// + /// Initializes a new instance of the class with + /// references to the inner exceptions that are the cause of this exception. + /// + /// The exceptions that are the cause of the current exception. + /// The argument + /// is null. + /// An element of is + /// null. + public AggregateException(IEnumerable innerExceptions) : + this(SR.AggregateException_ctor_DefaultMessage, innerExceptions) + { + } + + /// + /// Initializes a new instance of the class with + /// references to the inner exceptions that are the cause of this exception. + /// + /// The exceptions that are the cause of the current exception. + /// The argument + /// is null. + /// An element of is + /// null. + public AggregateException(params Exception[] innerExceptions) : + this(SR.AggregateException_ctor_DefaultMessage, innerExceptions) + { + } + + /// + /// Initializes a new instance of the class with a specified error + /// message and references to the inner exceptions that are the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exceptions that are the cause of the current exception. + /// The argument + /// is null. + /// An element of is + /// null. + public AggregateException(string message, IEnumerable innerExceptions) + // If it's already an IList, pass that along (a defensive copy will be made in the delegated ctor). If it's null, just pass along + // null typed correctly. Otherwise, create an IList from the enumerable and pass that along. + : this(message, innerExceptions as IList ?? (innerExceptions == null ? (List)null : new List(innerExceptions))) + { + } + + /// + /// Initializes a new instance of the class with a specified error + /// message and references to the inner exceptions that are the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exceptions that are the cause of the current exception. + /// The argument + /// is null. + /// An element of is + /// null. + public AggregateException(string message, params Exception[] innerExceptions) : + this(message, (IList)innerExceptions) + { + } + + /// + /// Allocates a new aggregate exception with the specified message and list of inner exceptions. + /// + /// The error message that explains the reason for the exception. + /// The exceptions that are the cause of the current exception. + /// The argument + /// is null. + /// An element of is + /// null. + private AggregateException(string message, IList innerExceptions) + : base(message, innerExceptions != null && innerExceptions.Count > 0 ? innerExceptions[0] : null) + { + if (innerExceptions == null) + { + throw new ArgumentNullException(nameof(innerExceptions)); + } + + // Copy exceptions to our internal array and validate them. We must copy them, + // because we're going to put them into a ReadOnlyCollection which simply reuses + // the list passed in to it. We don't want callers subsequently mutating. + Exception[] exceptionsCopy = new Exception[innerExceptions.Count]; + + for (int i = 0; i < exceptionsCopy.Length; i++) + { + exceptionsCopy[i] = innerExceptions[i]; + + if (exceptionsCopy[i] == null) + { + throw new ArgumentException(SR.AggregateException_ctor_InnerExceptionNull); + } + } + + m_innerExceptions = new ReadOnlyCollection(exceptionsCopy); + } + + /// + /// Initializes a new instance of the class with + /// references to the inner exception dispatch info objects that represent the cause of this exception. + /// + /// + /// Information about the exceptions that are the cause of the current exception. + /// + /// The argument + /// is null. + /// An element of is + /// null. + internal AggregateException(IEnumerable innerExceptionInfos) : + this(SR.AggregateException_ctor_DefaultMessage, innerExceptionInfos) + { + } + + /// + /// Initializes a new instance of the class with a specified error + /// message and references to the inner exception dispatch info objects that represent the cause of + /// this exception. + /// + /// The error message that explains the reason for the exception. + /// + /// Information about the exceptions that are the cause of the current exception. + /// + /// The argument + /// is null. + /// An element of is + /// null. + internal AggregateException(string message, IEnumerable innerExceptionInfos) + // If it's already an IList, pass that along (a defensive copy will be made in the delegated ctor). If it's null, just pass along + // null typed correctly. Otherwise, create an IList from the enumerable and pass that along. + : this(message, innerExceptionInfos as IList ?? + (innerExceptionInfos == null ? + (List)null : + new List(innerExceptionInfos))) + { + } + + /// + /// Allocates a new aggregate exception with the specified message and list of inner + /// exception dispatch info objects. + /// + /// The error message that explains the reason for the exception. + /// + /// Information about the exceptions that are the cause of the current exception. + /// + /// The argument + /// is null. + /// An element of is + /// null. + private AggregateException(string message, IList innerExceptionInfos) + : base(message, innerExceptionInfos != null && innerExceptionInfos.Count > 0 && innerExceptionInfos[0] != null ? + innerExceptionInfos[0].SourceException : null) + { + if (innerExceptionInfos == null) + { + throw new ArgumentNullException(nameof(innerExceptionInfos)); + } + + // Copy exceptions to our internal array and validate them. We must copy them, + // because we're going to put them into a ReadOnlyCollection which simply reuses + // the list passed in to it. We don't want callers subsequently mutating. + Exception[] exceptionsCopy = new Exception[innerExceptionInfos.Count]; + + for (int i = 0; i < exceptionsCopy.Length; i++) + { + var edi = innerExceptionInfos[i]; + if (edi != null) exceptionsCopy[i] = edi.SourceException; + + if (exceptionsCopy[i] == null) + { + throw new ArgumentException(SR.AggregateException_ctor_InnerExceptionNull); + } + } + + m_innerExceptions = new ReadOnlyCollection(exceptionsCopy); + } + + /// + /// Initializes a new instance of the class with serialized data. + /// + /// The that holds + /// the serialized object data about the exception being thrown. + /// The that + /// contains contextual information about the source or destination. + /// The argument is null. + /// The exception could not be deserialized correctly. + protected AggregateException(SerializationInfo info, StreamingContext context) : + base(info, context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + Exception[] innerExceptions = info.GetValue("InnerExceptions", typeof(Exception[])) as Exception[]; + if (innerExceptions == null) + { + throw new SerializationException(SR.AggregateException_DeserializationFailure); + } + + m_innerExceptions = new ReadOnlyCollection(innerExceptions); + } + + /// + /// Sets the with information about + /// the exception. + /// + /// The that holds + /// the serialized object data about the exception being thrown. + /// The that + /// contains contextual information about the source or destination. + /// The argument is null. + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + + Exception[] innerExceptions = new Exception[m_innerExceptions.Count]; + m_innerExceptions.CopyTo(innerExceptions, 0); + info.AddValue("InnerExceptions", innerExceptions, typeof(Exception[])); + } + + /// + /// Returns the that is the root cause of this exception. + /// + public override Exception GetBaseException() + { + // Returns the first inner AggregateException that contains more or less than one inner exception + + // Recursively traverse the inner exceptions as long as the inner exception of type AggregateException and has only one inner exception + Exception back = this; + AggregateException backAsAggregate = this; + while (backAsAggregate != null && backAsAggregate.InnerExceptions.Count == 1) + { + back = back.InnerException; + backAsAggregate = back as AggregateException; + } + return back; + } + + /// + /// Gets a read-only collection of the instances that caused the + /// current exception. + /// + public ReadOnlyCollection InnerExceptions + { + get { return m_innerExceptions; } + } + + + /// + /// Invokes a handler on each contained by this . + /// + /// The predicate to execute for each exception. The predicate accepts as an + /// argument the to be processed and returns a Boolean to indicate + /// whether the exception was handled. + /// + /// Each invocation of the returns true or false to indicate whether the + /// was handled. After all invocations, if any exceptions went + /// unhandled, all unhandled exceptions will be put into a new + /// which will be thrown. Otherwise, the method simply returns. If any + /// invocations of the throws an exception, it will halt the processing + /// of any more exceptions and immediately propagate the thrown exception as-is. + /// + /// An exception contained by this was not handled. + /// The argument is + /// null. + public void Handle(Func predicate) + { + if (predicate == null) + { + throw new ArgumentNullException(nameof(predicate)); + } + + List unhandledExceptions = null; + for (int i = 0; i < m_innerExceptions.Count; i++) + { + // If the exception was not handled, lazily allocate a list of unhandled + // exceptions (to be rethrown later) and add it. + if (!predicate(m_innerExceptions[i])) + { + if (unhandledExceptions == null) + { + unhandledExceptions = new List(); + } + + unhandledExceptions.Add(m_innerExceptions[i]); + } + } + + // If there are unhandled exceptions remaining, throw them. + if (unhandledExceptions != null) + { + throw new AggregateException(Message, unhandledExceptions); + } + } + + + /// + /// Flattens the inner instances of by expanding its contained instances + /// into a new + /// + /// A new, flattened . + /// + /// If any inner exceptions are themselves instances of + /// , this method will recursively flatten all of them. The + /// inner exceptions returned in the new + /// will be the union of all of the inner exceptions from exception tree rooted at the provided + /// instance. + /// + public AggregateException Flatten() + { + // Initialize a collection to contain the flattened exceptions. + List flattenedExceptions = new List(); + + // Create a list to remember all aggregates to be flattened, this will be accessed like a FIFO queue + List exceptionsToFlatten = new List(); + exceptionsToFlatten.Add(this); + int nDequeueIndex = 0; + + // Continue removing and recursively flattening exceptions, until there are no more. + while (exceptionsToFlatten.Count > nDequeueIndex) + { + // dequeue one from exceptionsToFlatten + IList currentInnerExceptions = exceptionsToFlatten[nDequeueIndex++].InnerExceptions; + + for (int i = 0; i < currentInnerExceptions.Count; i++) + { + Exception currentInnerException = currentInnerExceptions[i]; + + if (currentInnerException == null) + { + continue; + } + + AggregateException currentInnerAsAggregate = currentInnerException as AggregateException; + + // If this exception is an aggregate, keep it around for later. Otherwise, + // simply add it to the list of flattened exceptions to be returned. + if (currentInnerAsAggregate != null) + { + exceptionsToFlatten.Add(currentInnerAsAggregate); + } + else + { + flattenedExceptions.Add(currentInnerException); + } + } + } + + + return new AggregateException(Message, flattenedExceptions); + } + + /// Gets a message that describes the exception. + public override string Message + { + get + { + if (m_innerExceptions.Count == 0) + { + return base.Message; + } + + StringBuilder sb = StringBuilderCache.Acquire(); + sb.Append(base.Message); + sb.Append(' '); + for (int i = 0; i < m_innerExceptions.Count; i++) + { + sb.Append('('); + sb.Append(m_innerExceptions[i].Message); + sb.Append(") "); + } + sb.Length -= 1; + return StringBuilderCache.GetStringAndRelease(sb); + } + } + + /// + /// Creates and returns a string representation of the current . + /// + /// A string representation of the current exception. + public override string ToString() + { + StringBuilder text = new StringBuilder(); + text.Append(base.ToString()); + + for (int i = 0; i < m_innerExceptions.Count; i++) + { + text.AppendLine(); + text.Append("---> "); + text.AppendFormat(CultureInfo.InvariantCulture, SR.AggregateException_InnerException, i); + text.Append(m_innerExceptions[i].ToString()); + text.Append("<---"); + text.AppendLine(); + } + + return text.ToString(); + } + + /// + /// This helper property is used by the DebuggerDisplay. + /// + /// Note that we don't want to remove this property and change the debugger display to {InnerExceptions.Count} + /// because DebuggerDisplay should be a single property access or parameterless method call, so that the debugger + /// can use a fast path without using the expression evaluator. + /// + /// See http://msdn.microsoft.com/en-us/library/x810d419.aspx + /// + private int InnerExceptionCount + { + get + { + return InnerExceptions.Count; + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/ApplicationException.cs b/external/corefx/src/Common/src/CoreLib/System/ApplicationException.cs new file mode 100644 index 0000000000..f36e2c1274 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ApplicationException.cs @@ -0,0 +1,58 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: The base class for all "less serious" exceptions that must be +** declared or caught. +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + // The ApplicationException is the base class for nonfatal, + // application errors that occur. These exceptions are generated + // (i.e., thrown) by an application, not the Runtime. Applications that need + // to create their own exceptions do so by extending this class. + // ApplicationException extends but adds no new functionality to + // RecoverableException. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class ApplicationException : Exception + { + // Creates a new ApplicationException with its message string set to + // the empty string, its HRESULT set to COR_E_APPLICATION, + // and its ExceptionInfo reference set to null. + public ApplicationException() + : base(SR.Arg_ApplicationException) + { + HResult = HResults.COR_E_APPLICATION; + } + + // Creates a new ApplicationException with its message string set to + // message, its HRESULT set to COR_E_APPLICATION, + // and its ExceptionInfo reference set to null. + // + public ApplicationException(String message) + : base(message) + { + HResult = HResults.COR_E_APPLICATION; + } + + public ApplicationException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_APPLICATION; + } + + protected ApplicationException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/ArgumentException.cs b/external/corefx/src/Common/src/CoreLib/System/ArgumentException.cs new file mode 100644 index 0000000000..8a8fe3e5e4 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ArgumentException.cs @@ -0,0 +1,97 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: Exception class for invalid arguments to a method. +** +** +=============================================================================*/ + +using System.Globalization; +using System.Runtime.Serialization; + +namespace System +{ + // The ArgumentException is thrown when an argument does not meet + // the contract of the method. Ideally it should give a meaningful error + // message describing what was wrong and which parameter is incorrect. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class ArgumentException : SystemException + { + private String _paramName; + + // Creates a new ArgumentException with its message + // string set to the empty string. + public ArgumentException() + : base(SR.Arg_ArgumentException) + { + HResult = HResults.COR_E_ARGUMENT; + } + + // Creates a new ArgumentException with its message + // string set to message. + // + public ArgumentException(String message) + : base(message) + { + HResult = HResults.COR_E_ARGUMENT; + } + + public ArgumentException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_ARGUMENT; + } + + public ArgumentException(String message, String paramName, Exception innerException) + : base(message, innerException) + { + _paramName = paramName; + HResult = HResults.COR_E_ARGUMENT; + } + + public ArgumentException(String message, String paramName) + : base(message) + { + _paramName = paramName; + HResult = HResults.COR_E_ARGUMENT; + } + + protected ArgumentException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + _paramName = info.GetString("ParamName"); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("ParamName", _paramName, typeof(string)); + } + + public override String Message + { + get + { + String s = base.Message; + if (!String.IsNullOrEmpty(_paramName)) + { + String resourceString = SR.Format(SR.Arg_ParamName_Name, _paramName); + return s + Environment.NewLine + resourceString; + } + else + return s; + } + } + + public virtual String ParamName + { + get { return _paramName; } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/ArgumentNullException.cs b/external/corefx/src/Common/src/CoreLib/System/ArgumentNullException.cs new file mode 100644 index 0000000000..80e43cc265 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ArgumentNullException.cs @@ -0,0 +1,55 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: Exception class for null arguments to a method. +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + // The ArgumentException is thrown when an argument + // is null when it shouldn't be. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class ArgumentNullException : ArgumentException + { + // Creates a new ArgumentNullException with its message + // string set to a default message explaining an argument was null. + public ArgumentNullException() + : base(SR.ArgumentNull_Generic) + { + // Use E_POINTER - COM used that for null pointers. Description is "invalid pointer" + HResult = HResults.E_POINTER; + } + + public ArgumentNullException(String paramName) + : base(SR.ArgumentNull_Generic, paramName) + { + HResult = HResults.E_POINTER; + } + + public ArgumentNullException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.E_POINTER; + } + + public ArgumentNullException(String paramName, String message) + : base(message, paramName) + { + HResult = HResults.E_POINTER; + } + + protected ArgumentNullException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/ArgumentOutOfRangeException.cs b/external/corefx/src/Common/src/CoreLib/System/ArgumentOutOfRangeException.cs new file mode 100644 index 0000000000..604caa8ee8 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ArgumentOutOfRangeException.cs @@ -0,0 +1,100 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: Exception class for method arguments outside of the legal range. +** +** +=============================================================================*/ + +using System.Globalization; +using System.Runtime.Serialization; + +namespace System +{ + // The ArgumentOutOfRangeException is thrown when an argument + // is outside the legal range for that argument. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class ArgumentOutOfRangeException : ArgumentException + { + private Object _actualValue; + + // Creates a new ArgumentOutOfRangeException with its message + // string set to a default message explaining an argument was out of range. + public ArgumentOutOfRangeException() + : base(SR.Arg_ArgumentOutOfRangeException) + { + HResult = HResults.COR_E_ARGUMENTOUTOFRANGE; + } + + public ArgumentOutOfRangeException(String paramName) + : base(SR.Arg_ArgumentOutOfRangeException, paramName) + { + HResult = HResults.COR_E_ARGUMENTOUTOFRANGE; + } + + public ArgumentOutOfRangeException(String paramName, String message) + : base(message, paramName) + { + HResult = HResults.COR_E_ARGUMENTOUTOFRANGE; + } + + public ArgumentOutOfRangeException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_ARGUMENTOUTOFRANGE; + } + + // We will not use this in the classlibs, but we'll provide it for + // anyone that's really interested so they don't have to stick a bunch + // of printf's in their code. + public ArgumentOutOfRangeException(String paramName, Object actualValue, String message) + : base(message, paramName) + { + _actualValue = actualValue; + HResult = HResults.COR_E_ARGUMENTOUTOFRANGE; + } + + protected ArgumentOutOfRangeException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + _actualValue = info.GetValue("ActualValue", typeof(object)); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("ActualValue", _actualValue, typeof(object)); + } + + public override String Message + { + get + { + String s = base.Message; + if (_actualValue != null) + { + String valueMessage = SR.Format(SR.ArgumentOutOfRange_ActualValue, _actualValue.ToString()); + if (s == null) + return valueMessage; + return s + Environment.NewLine + valueMessage; + } + return s; + } + } + + // Gets the value of the argument that caused the exception. + // Note - we don't set this anywhere in the class libraries in + // version 1, but it might come in handy for other developers who + // want to avoid sticking printf's in their code. + public virtual Object ActualValue + { + get { return _actualValue; } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/ArithmeticException.cs b/external/corefx/src/Common/src/CoreLib/System/ArithmeticException.cs new file mode 100644 index 0000000000..606f1debfd --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ArithmeticException.cs @@ -0,0 +1,53 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: Exception class for bad arithmetic conditions! +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + // The ArithmeticException is thrown when overflow or underflow + // occurs. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class ArithmeticException : SystemException + { + // Creates a new ArithmeticException with its message string set to + // the empty string, its HRESULT set to COR_E_ARITHMETIC, + // and its ExceptionInfo reference set to null. + public ArithmeticException() + : base(SR.Arg_ArithmeticException) + { + HResult = HResults.COR_E_ARITHMETIC; + } + + // Creates a new ArithmeticException with its message string set to + // message, its HRESULT set to COR_E_ARITHMETIC, + // and its ExceptionInfo reference set to null. + // + public ArithmeticException(String message) + : base(message) + { + HResult = HResults.COR_E_ARITHMETIC; + } + + public ArithmeticException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_ARITHMETIC; + } + + protected ArithmeticException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/ArraySegment.cs b/external/corefx/src/Common/src/CoreLib/System/ArraySegment.cs similarity index 79% rename from external/corert/src/System.Private.CoreLib/src/System/ArraySegment.cs rename to external/corefx/src/Common/src/CoreLib/System/ArraySegment.cs index edb832589e..d45fb0dc2b 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/ArraySegment.cs +++ b/external/corefx/src/Common/src/CoreLib/System/ArraySegment.cs @@ -16,7 +16,6 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.Contracts; namespace System { @@ -25,22 +24,22 @@ namespace System // because assignments to value types are not atomic, and also because one thread reading // three fields from an ArraySegment may not see the same ArraySegment from one call to another // (ie, users could assign a new value to the old location). - - public struct ArraySegment : IList, IReadOnlyList + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public readonly struct ArraySegment : IList, IReadOnlyList { // Do not replace the array allocation with Array.Empty. We don't want to have the overhead of // instantiating another generic type in addition to ArraySegment for new type parameters. public static ArraySegment Empty { get; } = new ArraySegment(new T[0]); - private readonly T[] _array; - private readonly int _offset; - private readonly int _count; + private readonly T[] _array; // Do not rename (binary serialization) + private readonly int _offset; // Do not rename (binary serialization) + private readonly int _count; // Do not rename (binary serialization) public ArraySegment(T[] array) { if (array == null) - throw new ArgumentNullException(nameof(array)); - Contract.EndContractBlock(); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); _array = array; _offset = 0; @@ -49,15 +48,11 @@ namespace System public ArraySegment(T[] array, int offset, int count) { - if (array == null) - throw new ArgumentNullException(nameof(array)); - if (offset < 0) - throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); - if (count < 0) - throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - if (array.Length - offset < count) - throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.EndContractBlock(); + // Validate arguments, check is minimal instructions with reduced branching for inlinable fast-path + // Negative values discovered though conversion to high values when converted to unsigned + // Failure should be rare and location determination and message is delegated to failure functions + if (array == null || (uint)offset > (uint)array.Length || (uint)count > (uint)(array.Length - offset)) + ThrowHelper.ThrowArraySegmentCtorValidationFailedExceptions(array, offset, count); _array = array; _offset = offset; @@ -76,7 +71,7 @@ namespace System { if ((uint)index >= (uint)_count) { - throw new ArgumentOutOfRangeException(nameof(index)); + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); } return _array[_offset + index]; @@ -85,7 +80,7 @@ namespace System { if ((uint)index >= (uint)_count) { - throw new ArgumentOutOfRangeException(nameof(index)); + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); } _array[_offset + index] = value; @@ -155,7 +150,7 @@ namespace System if ((uint)index > (uint)_count) { - throw new ArgumentOutOfRangeException(nameof(index)); + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); } return new ArraySegment(_array, _offset + index, _count - index); @@ -167,7 +162,7 @@ namespace System if ((uint)index > (uint)_count || (uint)count > (uint)(_count - index)) { - throw new ArgumentOutOfRangeException(nameof(index)); + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); } return new ArraySegment(_array, _offset + index, count); @@ -206,17 +201,16 @@ namespace System { ThrowInvalidOperationIfDefault(); if (index < 0 || index >= _count) - throw new ArgumentOutOfRangeException(nameof(index)); - Contract.EndContractBlock(); + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); return _array[_offset + index]; } + set { ThrowInvalidOperationIfDefault(); if (index < 0 || index >= _count) - throw new ArgumentOutOfRangeException(nameof(index)); - Contract.EndContractBlock(); + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); _array[_offset + index] = value; } @@ -236,12 +230,12 @@ namespace System void IList.Insert(int index, T item) { - throw new NotSupportedException(); + ThrowHelper.ThrowNotSupportedException(); } void IList.RemoveAt(int index) { - throw new NotSupportedException(); + ThrowHelper.ThrowNotSupportedException(); } #endregion @@ -252,8 +246,7 @@ namespace System { ThrowInvalidOperationIfDefault(); if (index < 0 || index >= _count) - throw new ArgumentOutOfRangeException(nameof(index)); - Contract.EndContractBlock(); + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); return _array[_offset + index]; } @@ -273,12 +266,12 @@ namespace System void ICollection.Add(T item) { - throw new NotSupportedException(); + ThrowHelper.ThrowNotSupportedException(); } void ICollection.Clear() { - throw new NotSupportedException(); + ThrowHelper.ThrowNotSupportedException(); } bool ICollection.Contains(T item) @@ -293,23 +286,20 @@ namespace System return index >= 0; } - void ICollection.CopyTo(T[] array, int arrayIndex) - { - ThrowInvalidOperationIfDefault(); - System.Array.Copy(_array, _offset, array, arrayIndex, _count); - } - bool ICollection.Remove(T item) { - throw new NotSupportedException(); + ThrowHelper.ThrowNotSupportedException(); + return default(bool); } #endregion #region IEnumerable + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); #endregion #region IEnumerable + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); #endregion @@ -317,7 +307,7 @@ namespace System { if (_array == null) { - throw new InvalidOperationException(SR.InvalidOperation_NullArray); + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NullArray); } } @@ -335,10 +325,10 @@ namespace System Debug.Assert(arraySegment.Count >= 0); Debug.Assert(arraySegment.Offset + arraySegment.Count <= arraySegment.Array.Length); - _array = arraySegment._array; - _start = arraySegment._offset; - _end = _start + arraySegment._count; - _current = _start - 1; + _array = arraySegment.Array; + _start = arraySegment.Offset; + _end = arraySegment.Offset + arraySegment.Count; + _current = arraySegment.Offset - 1; } public bool MoveNext() @@ -356,9 +346,9 @@ namespace System get { if (_current < _start) - throw new InvalidOperationException(SR.InvalidOperation_EnumNotStarted); + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumNotStarted(); if (_current >= _end) - throw new InvalidOperationException(SR.InvalidOperation_EnumEnded); + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumEnded(); return _array[_current]; } } diff --git a/external/corefx/src/Common/src/CoreLib/System/ArrayTypeMismatchException.cs b/external/corefx/src/Common/src/CoreLib/System/ArrayTypeMismatchException.cs new file mode 100644 index 0000000000..49820f58f5 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ArrayTypeMismatchException.cs @@ -0,0 +1,53 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: The arrays are of different primitive types. +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + // The ArrayMismatchException is thrown when an attempt to store + // an object of the wrong type within an array occurs. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class ArrayTypeMismatchException : SystemException + { + // Creates a new ArrayMismatchException with its message string set to + // the empty string, its HRESULT set to COR_E_ARRAYTYPEMISMATCH, + // and its ExceptionInfo reference set to null. + public ArrayTypeMismatchException() + : base(SR.Arg_ArrayTypeMismatchException) + { + HResult = HResults.COR_E_ARRAYTYPEMISMATCH; + } + + // Creates a new ArrayMismatchException with its message string set to + // message, its HRESULT set to COR_E_ARRAYTYPEMISMATCH, + // and its ExceptionInfo reference set to null. + // + public ArrayTypeMismatchException(String message) + : base(message) + { + HResult = HResults.COR_E_ARRAYTYPEMISMATCH; + } + + public ArrayTypeMismatchException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_ARRAYTYPEMISMATCH; + } + + protected ArrayTypeMismatchException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/AssemblyLoadEventArgs.cs b/external/corefx/src/Common/src/CoreLib/System/AssemblyLoadEventArgs.cs new file mode 100644 index 0000000000..d7e5249693 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/AssemblyLoadEventArgs.cs @@ -0,0 +1,18 @@ +// 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.Reflection; + +namespace System +{ + public class AssemblyLoadEventArgs : EventArgs + { + public AssemblyLoadEventArgs(Assembly loadedAssembly) + { + LoadedAssembly = loadedAssembly; + } + + public Assembly LoadedAssembly { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/AssemblyLoadEventHandler.cs b/external/corefx/src/Common/src/CoreLib/System/AssemblyLoadEventHandler.cs new file mode 100644 index 0000000000..752379fdc7 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/AssemblyLoadEventHandler.cs @@ -0,0 +1,8 @@ +// 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. + +namespace System +{ + public delegate void AssemblyLoadEventHandler(object sender, AssemblyLoadEventArgs args); +} diff --git a/external/corefx/src/Common/src/CoreLib/System/AsyncCallback.cs b/external/corefx/src/Common/src/CoreLib/System/AsyncCallback.cs new file mode 100644 index 0000000000..036d44a4b9 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/AsyncCallback.cs @@ -0,0 +1,16 @@ +// 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. + +/*============================================================ +** +** Interface: AsyncCallbackDelegate +** +** Purpose: Type of callback for async operations +** +===========================================================*/ + +namespace System +{ + public delegate void AsyncCallback(IAsyncResult ar); +} diff --git a/external/corefx/src/Common/src/CoreLib/System/AttributeTargets.cs b/external/corefx/src/Common/src/CoreLib/System/AttributeTargets.cs new file mode 100644 index 0000000000..c33d19e85e --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/AttributeTargets.cs @@ -0,0 +1,35 @@ +// 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. + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +namespace System +{ + // Enum used to indicate all the elements of the + // VOS it is valid to attach this element to. + [Flags] + public enum AttributeTargets + { + Assembly = 0x0001, + Module = 0x0002, + Class = 0x0004, + Struct = 0x0008, + Enum = 0x0010, + Constructor = 0x0020, + Method = 0x0040, + Property = 0x0080, + Field = 0x0100, + Event = 0x0200, + Interface = 0x0400, + Parameter = 0x0800, + Delegate = 0x1000, + ReturnValue = 0x2000, + GenericParameter = 0x4000, + + All = Assembly | Module | Class | Struct | Enum | Constructor | + Method | Property | Field | Event | Interface | Parameter | + Delegate | ReturnValue | GenericParameter + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/AttributeUsageAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/AttributeUsageAttribute.cs new file mode 100644 index 0000000000..219dc43e15 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/AttributeUsageAttribute.cs @@ -0,0 +1,57 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: The class denotes how to specify the usage of an attribute +** +** +===========================================================*/ + +using System.Reflection; + +namespace System +{ + /* By default, attributes are inherited and multiple attributes are not allowed */ + [AttributeUsage(AttributeTargets.Class, Inherited = true)] + public sealed class AttributeUsageAttribute : Attribute + { + private AttributeTargets _attributeTarget = AttributeTargets.All; // Defaults to all + private bool _allowMultiple = false; // Defaults to false + private bool _inherited = true; // Defaults to true + + internal static AttributeUsageAttribute Default = new AttributeUsageAttribute(AttributeTargets.All); + + //Constructors + public AttributeUsageAttribute(AttributeTargets validOn) + { + _attributeTarget = validOn; + } + internal AttributeUsageAttribute(AttributeTargets validOn, bool allowMultiple, bool inherited) + { + _attributeTarget = validOn; + _allowMultiple = allowMultiple; + _inherited = inherited; + } + + public AttributeTargets ValidOn + { + get { return _attributeTarget; } + } + + public bool AllowMultiple + { + get { return _allowMultiple; } + set { _allowMultiple = value; } + } + + public bool Inherited + { + get { return _inherited; } + set { _inherited = value; } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/BadImageFormatException.cs b/external/corefx/src/Common/src/CoreLib/System/BadImageFormatException.cs new file mode 100644 index 0000000000..1743075a6f --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/BadImageFormatException.cs @@ -0,0 +1,129 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: Exception to an invalid dll or executable format. +** +** +===========================================================*/ + +using System.Globalization; +using System.IO; +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public partial class BadImageFormatException : SystemException + { + private String _fileName; // The name of the corrupt PE file. + private String _fusionLog = null; // fusion log (when applicable) + + public BadImageFormatException() + : base(SR.Arg_BadImageFormatException) + { + HResult = HResults.COR_E_BADIMAGEFORMAT; + } + + public BadImageFormatException(String message) + : base(message) + { + HResult = HResults.COR_E_BADIMAGEFORMAT; + } + + public BadImageFormatException(String message, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_BADIMAGEFORMAT; + } + + public BadImageFormatException(String message, String fileName) : base(message) + { + HResult = HResults.COR_E_BADIMAGEFORMAT; + _fileName = fileName; + } + + public BadImageFormatException(String message, String fileName, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_BADIMAGEFORMAT; + _fileName = fileName; + } + + protected BadImageFormatException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + _fileName = info.GetString("BadImageFormat_FileName"); + _fusionLog = info.GetString("BadImageFormat_FusionLog"); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("BadImageFormat_FileName", _fileName, typeof(string)); + info.AddValue("BadImageFormat_FusionLog", _fusionLog, typeof(string)); + } + + public override String Message + { + get + { + SetMessageField(); + return _message; + } + } + + private void SetMessageField() + { + if (_message == null) + { + if ((_fileName == null) && + (HResult == HResults.COR_E_EXCEPTION)) + _message = SR.Arg_BadImageFormatException; + + else + _message = FileLoadException.FormatFileLoadExceptionMessage(_fileName, HResult); + } + } + + public String FileName + { + get { return _fileName; } + } + + public override String ToString() + { + String s = GetType().ToString() + ": " + Message; + + if (_fileName != null && _fileName.Length != 0) + s += Environment.NewLine + SR.Format(SR.IO_FileName_Name, _fileName); + + if (InnerException != null) + s = s + " ---> " + InnerException.ToString(); + + if (StackTrace != null) + s += Environment.NewLine + StackTrace; + + if (_fusionLog != null) + { + if (s == null) + s = " "; + s += Environment.NewLine; + s += Environment.NewLine; + s += _fusionLog; + } + + return s; + } + + public String FusionLog + { + get { return _fusionLog; } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/BitConverter.cs b/external/corefx/src/Common/src/CoreLib/System/BitConverter.cs new file mode 100644 index 0000000000..e3cf20eb6a --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/BitConverter.cs @@ -0,0 +1,474 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using Internal.Runtime.CompilerServices; + +namespace System +{ + // The BitConverter class contains methods for + // converting an array of bytes to one of the base data + // types, as well as for converting a base data type to an + // array of bytes. + public static class BitConverter + { + // This field indicates the "endianess" of the architecture. + // The value is set to true if the architecture is + // little endian; false if it is big endian. +#if BIGENDIAN + public static readonly bool IsLittleEndian /* = false */; +#else + public static readonly bool IsLittleEndian = true; +#endif + + // Converts a Boolean into an array of bytes with length one. + public static byte[] GetBytes(bool value) + { + byte[] r = new byte[1]; + r[0] = (value ? (byte)1 : (byte)0); + return r; + } + + // Converts a Boolean into a Span of bytes with length one. + public static bool TryWriteBytes(Span destination, bool value) + { + if (destination.Length < sizeof(byte)) + return false; + + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value ? (byte)1 : (byte)0); + return true; + } + + // Converts a char into an array of bytes with length two. + public static byte[] GetBytes(char value) + { + byte[] bytes = new byte[sizeof(char)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; + } + + // Converts a char into a Span + public static bool TryWriteBytes(Span destination, char value) + { + if (destination.Length < sizeof(char)) + return false; + + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value); + return true; + } + + // Converts a short into an array of bytes with length + // two. + public static byte[] GetBytes(short value) + { + byte[] bytes = new byte[sizeof(short)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; + } + + // Converts a short into a Span + public static bool TryWriteBytes(Span destination, short value) + { + if (destination.Length < sizeof(short)) + return false; + + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value); + return true; + } + + // Converts an int into an array of bytes with length + // four. + public static byte[] GetBytes(int value) + { + byte[] bytes = new byte[sizeof(int)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; + } + + // Converts an int into a Span + public static bool TryWriteBytes(Span destination, int value) + { + if (destination.Length < sizeof(int)) + return false; + + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value); + return true; + } + + // Converts a long into an array of bytes with length + // eight. + public static byte[] GetBytes(long value) + { + byte[] bytes = new byte[sizeof(long)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; + } + + // Converts a long into a Span + public static bool TryWriteBytes(Span destination, long value) + { + if (destination.Length < sizeof(long)) + return false; + + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value); + return true; + } + + // Converts an ushort into an array of bytes with + // length two. + [CLSCompliant(false)] + public static byte[] GetBytes(ushort value) + { + byte[] bytes = new byte[sizeof(ushort)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; + } + + // Converts a ushort into a Span + [CLSCompliant(false)] + public static bool TryWriteBytes(Span destination, ushort value) + { + if (destination.Length < sizeof(ushort)) + return false; + + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value); + return true; + } + + // Converts an uint into an array of bytes with + // length four. + [CLSCompliant(false)] + public static byte[] GetBytes(uint value) + { + byte[] bytes = new byte[sizeof(uint)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; + } + + // Converts a uint into a Span + [CLSCompliant(false)] + public static bool TryWriteBytes(Span destination, uint value) + { + if (destination.Length < sizeof(uint)) + return false; + + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value); + return true; + } + + // Converts an unsigned long into an array of bytes with + // length eight. + [CLSCompliant(false)] + public static byte[] GetBytes(ulong value) + { + byte[] bytes = new byte[sizeof(ulong)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; + } + + // Converts a ulong into a Span + [CLSCompliant(false)] + public static bool TryWriteBytes(Span destination, ulong value) + { + if (destination.Length < sizeof(ulong)) + return false; + + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value); + return true; + } + + // Converts a float into an array of bytes with length + // four. + public static byte[] GetBytes(float value) + { + byte[] bytes = new byte[sizeof(float)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; + } + + // Converts a float into a Span + public static bool TryWriteBytes(Span destination, float value) + { + if (destination.Length < sizeof(float)) + return false; + + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value); + return true; + } + + // Converts a double into an array of bytes with length + // eight. + public static byte[] GetBytes(double value) + { + byte[] bytes = new byte[sizeof(double)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; + } + + // Converts a double into a Span + public static bool TryWriteBytes(Span destination, double value) + { + if (destination.Length < sizeof(double)) + return false; + + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value); + return true; + } + + // Converts an array of bytes into a char. + public static char ToChar(byte[] value, int startIndex) => unchecked((char)ToInt16(value, startIndex)); + + // Converts a Span into a char + public static char ToChar(ReadOnlySpan value) + { + if (value.Length < sizeof(char)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(value)); + } + + // Converts an array of bytes into a short. + public static short ToInt16(byte[] value, int startIndex) + { + if (value == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); + if (unchecked((uint)startIndex) >= unchecked((uint)value.Length)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index); + if (startIndex > value.Length - sizeof(short)) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall, ExceptionArgument.value); + + return Unsafe.ReadUnaligned(ref value[startIndex]); + } + + // Converts a Span into a short + public static short ToInt16(ReadOnlySpan value) + { + if (value.Length < sizeof(short)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(value)); + } + + // Converts an array of bytes into an int. + public static int ToInt32(byte[] value, int startIndex) + { + if (value == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); + if (unchecked((uint)startIndex) >= unchecked((uint)value.Length)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index); + if (startIndex > value.Length - sizeof(int)) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall, ExceptionArgument.value); + + return Unsafe.ReadUnaligned(ref value[startIndex]); + } + + // Converts a Span into an int + public static int ToInt32(ReadOnlySpan value) + { + if (value.Length < sizeof(int)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(value)); + } + + // Converts an array of bytes into a long. + public static long ToInt64(byte[] value, int startIndex) + { + if (value == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); + if (unchecked((uint)startIndex) >= unchecked((uint)value.Length)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index); + if (startIndex > value.Length - sizeof(long)) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall, ExceptionArgument.value); + + return Unsafe.ReadUnaligned(ref value[startIndex]); + } + + // Converts a Span into a long + public static long ToInt64(ReadOnlySpan value) + { + if (value.Length < sizeof(long)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(value)); + } + + // Converts an array of bytes into an ushort. + // + [CLSCompliant(false)] + public static ushort ToUInt16(byte[] value, int startIndex) => unchecked((ushort)ToInt16(value, startIndex)); + + // Converts a Span into a ushort + [CLSCompliant(false)] + public static ushort ToUInt16(ReadOnlySpan value) + { + if (value.Length < sizeof(ushort)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(value)); + } + + // Converts an array of bytes into an uint. + // + [CLSCompliant(false)] + public static uint ToUInt32(byte[] value, int startIndex) => unchecked((uint)ToInt32(value, startIndex)); + + // Convert a Span into a uint + [CLSCompliant(false)] + public static uint ToUInt32(ReadOnlySpan value) + { + if (value.Length < sizeof(uint)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(value)); + } + + // Converts an array of bytes into an unsigned long. + // + [CLSCompliant(false)] + public static ulong ToUInt64(byte[] value, int startIndex) => unchecked((ulong)ToInt64(value, startIndex)); + + // Converts a Span into an unsigned long + [CLSCompliant(false)] + public static ulong ToUInt64(ReadOnlySpan value) + { + if (value.Length < sizeof(ulong)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(value)); + } + + // Converts an array of bytes into a float. + public static float ToSingle(byte[] value, int startIndex) => Int32BitsToSingle(ToInt32(value, startIndex)); + + // Converts a Span into a float + public static float ToSingle(ReadOnlySpan value) + { + if (value.Length < sizeof(float)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(value)); + } + + // Converts an array of bytes into a double. + public static double ToDouble(byte[] value, int startIndex) => Int64BitsToDouble(ToInt64(value, startIndex)); + + // Converts a Span into a double + public static double ToDouble(ReadOnlySpan value) + { + if (value.Length < sizeof(double)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(value)); + } + + // Converts an array of bytes into a String. + public static string ToString(byte[] value, int startIndex, int length) + { + if (value == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); + if (startIndex < 0 || startIndex >= value.Length && startIndex > 0) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index); + if (length < 0) + throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_GenericPositive); + if (startIndex > value.Length - length) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall, ExceptionArgument.value); + + if (length == 0) + { + return string.Empty; + } + + if (length > (int.MaxValue / 3)) + { + // (Int32.MaxValue / 3) == 715,827,882 Bytes == 699 MB + throw new ArgumentOutOfRangeException(nameof(length), SR.Format(SR.ArgumentOutOfRange_LengthTooLarge, (int.MaxValue / 3))); + } + + return string.Create(length * 3 - 1, (value, startIndex, length), (dst, state) => + { + const string HexValues = "0123456789ABCDEF"; + + var src = new ReadOnlySpan(state.value, state.startIndex, state.length); + + int i = 0; + int j = 0; + + byte b = src[i++]; + dst[j++] = HexValues[b >> 4]; + dst[j++] = HexValues[b & 0xF]; + + while (i < src.Length) + { + b = src[i++]; + dst[j++] = '-'; + dst[j++] = HexValues[b >> 4]; + dst[j++] = HexValues[b & 0xF]; + } + }); + } + + // Converts an array of bytes into a String. + public static string ToString(byte[] value) + { + if (value == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); + return ToString(value, 0, value.Length); + } + + // Converts an array of bytes into a String. + public static string ToString(byte[] value, int startIndex) + { + if (value == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); + return ToString(value, startIndex, value.Length - startIndex); + } + + /*==================================ToBoolean=================================== + **Action: Convert an array of bytes to a boolean value. We treat this array + ** as if the first 4 bytes were an Int4 an operate on this value. + **Returns: True if the Int4 value of the first 4 bytes is non-zero. + **Arguments: value -- The byte array + ** startIndex -- The position within the array. + **Exceptions: See ToInt4. + ==============================================================================*/ + // Converts an array of bytes into a boolean. + public static bool ToBoolean(byte[] value, int startIndex) + { + if (value == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); + if (startIndex < 0) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index); + if (startIndex > value.Length - 1) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index); // differs from other overloads, which throw base ArgumentException + + return value[startIndex] != 0; + } + + public static bool ToBoolean(ReadOnlySpan value) + { + if (value.Length < sizeof(byte)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(value)) != 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe long DoubleToInt64Bits(double value) + { + return *((long*)&value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe double Int64BitsToDouble(long value) + { + return *((double*)&value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe int SingleToInt32Bits(float value) + { + return *((int*)&value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe float Int32BitsToSingle(int value) + { + return *((float*)&value); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Boolean.cs b/external/corefx/src/Common/src/CoreLib/System/Boolean.cs similarity index 69% rename from external/corert/src/System.Private.CoreLib/src/System/Boolean.cs rename to external/corefx/src/Common/src/CoreLib/System/Boolean.cs index 64d08f69d5..896e5f18e5 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Boolean.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Boolean.cs @@ -12,27 +12,27 @@ ** ===========================================================*/ -using System; -using System.Globalization; -using System.Diagnostics.Contracts; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; namespace System { - // The Boolean class provides the - // object representation of the boolean primitive type. - public struct Boolean : IComparable, IComparable, IEquatable, IConvertible + [Serializable] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Boolean : IComparable, IConvertible, IComparable, IEquatable { // // Member Variables // - private bool _value; + private bool m_value; // Do not rename (binary serialization) - // The true value. - // + // The true value. + // internal const int True = 1; // The false value. - // + // internal const int False = 0; @@ -73,7 +73,7 @@ namespace System // Provides a hash code for this instance. public override int GetHashCode() { - return (_value) ? True : False; + return (m_value) ? True : False; } /*===================================ToString=================================== @@ -84,7 +84,7 @@ namespace System // Converts the boolean value of this instance to a String. public override String ToString() { - if (false == _value) + if (false == m_value) { return FalseLiteral; } @@ -96,6 +96,24 @@ namespace System return ToString(); } + public bool TryFormat(Span destination, out int charsWritten) + { + string s = m_value ? TrueLiteral : FalseLiteral; + + if (s.Length <= destination.Length) + { + bool copied = s.AsReadOnlySpan().TryCopyTo(destination); + Debug.Assert(copied); + charsWritten = s.Length; + return true; + } + else + { + charsWritten = 0; + return false; + } + } + // Determines whether two Boolean objects are equal. public override bool Equals(Object obj) { @@ -105,12 +123,13 @@ namespace System return false; } - return (_value == ((Boolean)obj)._value); + return (m_value == ((Boolean)obj).m_value); } + [NonVersionable] public bool Equals(Boolean obj) { - return _value == obj; + return m_value == obj; } // Compares this object to another object, returning an integer that @@ -131,11 +150,11 @@ namespace System throw new ArgumentException(SR.Arg_MustBeBoolean); } - if (_value == ((Boolean)obj)._value) + if (m_value == ((Boolean)obj).m_value) { return 0; } - else if (_value == false) + else if (m_value == false) { return -1; } @@ -144,11 +163,11 @@ namespace System public int CompareTo(Boolean value) { - if (_value == value) + if (m_value == value) { return 0; } - else if (_value == false) + else if (m_value == false) { return -1; } @@ -164,35 +183,36 @@ namespace System public static Boolean Parse(String value) { if (value == null) throw new ArgumentNullException(nameof(value)); - Contract.EndContractBlock(); - Boolean result = false; - if (!TryParse(value, out result)) - { - throw new FormatException(SR.Format_BadBoolean); - } - else - { - return result; - } + return Parse(value.AsReadOnlySpan()); } + public static bool Parse(ReadOnlySpan value) => + TryParse(value, out bool result) ? result : throw new FormatException(SR.Format_BadBoolean); + // Determines whether a String represents true or false. // public static Boolean TryParse(String value, out Boolean result) { - result = false; if (value == null) { + result = false; return false; } - // For perf reasons, let's first see if they're equal, then do the - // trim to get rid of white space, and check again. - if (TrueLiteral.Equals(value, StringComparison.OrdinalIgnoreCase)) + + return TryParse(value.AsReadOnlySpan(), out result); + } + + public static bool TryParse(ReadOnlySpan value, out bool result) + { + ReadOnlySpan trueSpan = TrueLiteral.AsReadOnlySpan(); + if (StringSpanHelpers.Equals(trueSpan, value, StringComparison.OrdinalIgnoreCase)) { result = true; return true; } - if (FalseLiteral.Equals(value, StringComparison.OrdinalIgnoreCase)) + + ReadOnlySpan falseSpan = FalseLiteral.AsReadOnlySpan(); + if (StringSpanHelpers.Equals(falseSpan, value, StringComparison.OrdinalIgnoreCase)) { result = false; return true; @@ -201,27 +221,27 @@ namespace System // Special case: Trim whitespace as well as null characters. value = TrimWhiteSpaceAndNull(value); - if (TrueLiteral.Equals(value, StringComparison.OrdinalIgnoreCase)) + if (StringSpanHelpers.Equals(trueSpan, value, StringComparison.OrdinalIgnoreCase)) { result = true; return true; } - if (FalseLiteral.Equals(value, StringComparison.OrdinalIgnoreCase)) + if (StringSpanHelpers.Equals(falseSpan, value, StringComparison.OrdinalIgnoreCase)) { result = false; return true; } + result = false; return false; } - private static String TrimWhiteSpaceAndNull(String value) + private static ReadOnlySpan TrimWhiteSpaceAndNull(ReadOnlySpan value) { - int start = 0; - int end = value.Length - 1; - char nullChar = (char)0x0000; + const char nullChar = (char)0x0000; + int start = 0; while (start < value.Length) { if (!Char.IsWhiteSpace(value[start]) && value[start] != nullChar) @@ -231,6 +251,7 @@ namespace System start++; } + int end = value.Length - 1; while (end >= start) { if (!Char.IsWhiteSpace(value[end]) && value[end] != nullChar) @@ -240,7 +261,7 @@ namespace System end--; } - return value.Substring(start, end - start + 1); + return value.Slice(start, end - start + 1); } // @@ -255,72 +276,72 @@ namespace System bool IConvertible.ToBoolean(IFormatProvider provider) { - return _value; + return m_value; } char IConvertible.ToChar(IFormatProvider provider) { - throw new InvalidCastException(String.Format(SR.InvalidCast_FromTo, "Boolean", "Char")); + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Boolean", "Char")); } sbyte IConvertible.ToSByte(IFormatProvider provider) { - return Convert.ToSByte(_value); + return Convert.ToSByte(m_value); } byte IConvertible.ToByte(IFormatProvider provider) { - return Convert.ToByte(_value); + return Convert.ToByte(m_value); } short IConvertible.ToInt16(IFormatProvider provider) { - return Convert.ToInt16(_value); + return Convert.ToInt16(m_value); } ushort IConvertible.ToUInt16(IFormatProvider provider) { - return Convert.ToUInt16(_value); + return Convert.ToUInt16(m_value); } int IConvertible.ToInt32(IFormatProvider provider) { - return Convert.ToInt32(_value); + return Convert.ToInt32(m_value); } uint IConvertible.ToUInt32(IFormatProvider provider) { - return Convert.ToUInt32(_value); + return Convert.ToUInt32(m_value); } long IConvertible.ToInt64(IFormatProvider provider) { - return Convert.ToInt64(_value); + return Convert.ToInt64(m_value); } ulong IConvertible.ToUInt64(IFormatProvider provider) { - return Convert.ToUInt64(_value); + return Convert.ToUInt64(m_value); } float IConvertible.ToSingle(IFormatProvider provider) { - return Convert.ToSingle(_value); + return Convert.ToSingle(m_value); } double IConvertible.ToDouble(IFormatProvider provider) { - return Convert.ToDouble(_value); + return Convert.ToDouble(m_value); } Decimal IConvertible.ToDecimal(IFormatProvider provider) { - return Convert.ToDecimal(_value); + return Convert.ToDecimal(m_value); } DateTime IConvertible.ToDateTime(IFormatProvider provider) { - throw new InvalidCastException(String.Format(SR.InvalidCast_FromTo, "Boolean", "DateTime")); + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Boolean", "DateTime")); } Object IConvertible.ToType(Type type, IFormatProvider provider) diff --git a/external/corefx/src/Common/src/CoreLib/System/Buffers/ArrayPool.cs b/external/corefx/src/Common/src/CoreLib/System/Buffers/ArrayPool.cs new file mode 100644 index 0000000000..77a07f7fa5 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Buffers/ArrayPool.cs @@ -0,0 +1,100 @@ +// 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. + +namespace System.Buffers +{ + /// + /// Provides a resource pool that enables reusing instances of type . + /// + /// + /// + /// Renting and returning buffers with an can increase performance + /// in situations where arrays are created and destroyed frequently, resulting in significant + /// memory pressure on the garbage collector. + /// + /// + /// This class is thread-safe. All members may be used by multiple threads concurrently. + /// + /// + public abstract class ArrayPool + { + /// + /// Retrieves a shared instance. + /// + /// + /// The shared pool provides a default implementation of + /// that's intended for general applicability. It maintains arrays of multiple sizes, and + /// may hand back a larger array than was actually requested, but will never hand back a smaller + /// array than was requested. Renting a buffer from it with will result in an + /// existing buffer being taken from the pool if an appropriate buffer is available or in a new + /// buffer being allocated if one is not available. + /// byte[] and char[] are the most commonly pooled array types. For these we use a special pool type + /// optimized for very fast access speeds, at the expense of more memory consumption. + /// The shared pool instance is created lazily on first access. + /// + public static ArrayPool Shared { get; } = + typeof(T) == typeof(byte) || typeof(T) == typeof(char) ? new TlsOverPerCoreLockedStacksArrayPool() : + Create(); + + /// + /// Creates a new instance using default configuration options. + /// + /// A new instance. + public static ArrayPool Create() => new ConfigurableArrayPool(); + + /// + /// Creates a new instance using custom configuration options. + /// + /// The maximum length of array instances that may be stored in the pool. + /// + /// The maximum number of array instances that may be stored in each bucket in the pool. The pool + /// groups arrays of similar lengths into buckets for faster access. + /// + /// A new instance with the specified configuration options. + /// + /// The created pool will group arrays into buckets, with no more than + /// in each bucket and with those arrays not exceeding in length. + /// + public static ArrayPool Create(int maxArrayLength, int maxArraysPerBucket) => + new ConfigurableArrayPool(maxArrayLength, maxArraysPerBucket); + + /// + /// Retrieves a buffer that is at least the requested length. + /// + /// The minimum length of the array needed. + /// + /// An that is at least in length. + /// + /// + /// This buffer is loaned to the caller and should be returned to the same pool via + /// so that it may be reused in subsequent usage of . + /// It is not a fatal error to not return a rented buffer, but failure to do so may lead to + /// decreased application performance, as the pool may need to create a new buffer to replace + /// the one lost. + /// + public abstract T[] Rent(int minimumLength); + + /// + /// Returns to the pool an array that was previously obtained via on the same + /// instance. + /// + /// + /// The buffer previously obtained from to return to the pool. + /// + /// + /// If true and if the pool will store the buffer to enable subsequent reuse, + /// will clear of its contents so that a subsequent consumer via + /// will not see the previous consumer's content. If false or if the pool will release the buffer, + /// the array's contents are left unchanged. + /// + /// + /// Once a buffer has been returned to the pool, the caller gives up all ownership of the buffer + /// and must not use it. The reference returned from a given call to must only be + /// returned via once. The default + /// may hold onto the returned buffer in order to rent it again, or it may release the returned buffer + /// if it's determined that the pool already has enough buffers stored. + /// + public abstract void Return(T[] array, bool clearArray = false); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Buffers/ArrayPoolEventSource.cs b/external/corefx/src/Common/src/CoreLib/System/Buffers/ArrayPoolEventSource.cs new file mode 100644 index 0000000000..9482744144 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Buffers/ArrayPoolEventSource.cs @@ -0,0 +1,78 @@ +// 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.Diagnostics.Tracing; + +namespace System.Buffers +{ + [EventSource(Name = "System.Buffers.ArrayPoolEventSource")] + internal sealed class ArrayPoolEventSource : EventSource + { + internal readonly static ArrayPoolEventSource Log = new ArrayPoolEventSource(); + + /// The reason for a BufferAllocated event. + internal enum BufferAllocatedReason : int + { + /// The pool is allocating a buffer to be pooled in a bucket. + Pooled, + /// The requested buffer size was too large to be pooled. + OverMaximumSize, + /// The pool has already allocated for pooling as many buffers of a particular size as it's allowed. + PoolExhausted + } + + /// + /// Event for when a buffer is rented. This is invoked once for every successful call to Rent, + /// regardless of whether a buffer is allocated or a buffer is taken from the pool. In a + /// perfect situation where all rented buffers are returned, we expect to see the number + /// of BufferRented events exactly match the number of BuferReturned events, with the number + /// of BufferAllocated events being less than or equal to those numbers (ideally significantly + /// less than). + /// + [Event(1, Level = EventLevel.Verbose)] + internal unsafe void BufferRented(int bufferId, int bufferSize, int poolId, int bucketId) + { + EventData* payload = stackalloc EventData[4]; + payload[0].Size = sizeof(int); + payload[0].DataPointer = ((IntPtr)(&bufferId)); + payload[1].Size = sizeof(int); + payload[1].DataPointer = ((IntPtr)(&bufferSize)); + payload[2].Size = sizeof(int); + payload[2].DataPointer = ((IntPtr)(&poolId)); + payload[3].Size = sizeof(int); + payload[3].DataPointer = ((IntPtr)(&bucketId)); + WriteEventCore(1, 4, payload); + } + + /// + /// Event for when a buffer is allocated by the pool. In an ideal situation, the number + /// of BufferAllocated events is significantly smaller than the number of BufferRented and + /// BufferReturned events. + /// + [Event(2, Level = EventLevel.Informational)] + internal unsafe void BufferAllocated(int bufferId, int bufferSize, int poolId, int bucketId, BufferAllocatedReason reason) + { + EventData* payload = stackalloc EventData[5]; + payload[0].Size = sizeof(int); + payload[0].DataPointer = ((IntPtr)(&bufferId)); + payload[1].Size = sizeof(int); + payload[1].DataPointer = ((IntPtr)(&bufferSize)); + payload[2].Size = sizeof(int); + payload[2].DataPointer = ((IntPtr)(&poolId)); + payload[3].Size = sizeof(int); + payload[3].DataPointer = ((IntPtr)(&bucketId)); + payload[4].Size = sizeof(BufferAllocatedReason); + payload[4].DataPointer = ((IntPtr)(&reason)); + WriteEventCore(2, 5, payload); + } + + /// + /// Event raised when a buffer is returned to the pool. This event is raised regardless of whether + /// the returned buffer is stored or dropped. In an ideal situation, the number of BufferReturned + /// events exactly matches the number of BufferRented events. + /// + [Event(3, Level = EventLevel.Verbose)] + internal void BufferReturned(int bufferId, int bufferSize, int poolId) => WriteEvent(3, bufferId, bufferSize, poolId); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Buffers/ConfigurableArrayPool.cs b/external/corefx/src/Common/src/CoreLib/System/Buffers/ConfigurableArrayPool.cs new file mode 100644 index 0000000000..f7b6034d20 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Buffers/ConfigurableArrayPool.cs @@ -0,0 +1,265 @@ +// 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.Diagnostics; +using System.Threading; + +namespace System.Buffers +{ + internal sealed partial class ConfigurableArrayPool : ArrayPool + { + /// The default maximum length of each array in the pool (2^20). + private const int DefaultMaxArrayLength = 1024 * 1024; + /// The default maximum number of arrays per bucket that are available for rent. + private const int DefaultMaxNumberOfArraysPerBucket = 50; + + private readonly Bucket[] _buckets; + + internal ConfigurableArrayPool() : this(DefaultMaxArrayLength, DefaultMaxNumberOfArraysPerBucket) + { + } + + internal ConfigurableArrayPool(int maxArrayLength, int maxArraysPerBucket) + { + if (maxArrayLength <= 0) + { + throw new ArgumentOutOfRangeException(nameof(maxArrayLength)); + } + if (maxArraysPerBucket <= 0) + { + throw new ArgumentOutOfRangeException(nameof(maxArraysPerBucket)); + } + + // Our bucketing algorithm has a min length of 2^4 and a max length of 2^30. + // Constrain the actual max used to those values. + const int MinimumArrayLength = 0x10, MaximumArrayLength = 0x40000000; + if (maxArrayLength > MaximumArrayLength) + { + maxArrayLength = MaximumArrayLength; + } + else if (maxArrayLength < MinimumArrayLength) + { + maxArrayLength = MinimumArrayLength; + } + + // Create the buckets. + int poolId = Id; + int maxBuckets = Utilities.SelectBucketIndex(maxArrayLength); + var buckets = new Bucket[maxBuckets + 1]; + for (int i = 0; i < buckets.Length; i++) + { + buckets[i] = new Bucket(Utilities.GetMaxSizeForBucket(i), maxArraysPerBucket, poolId); + } + _buckets = buckets; + } + + /// Gets an ID for the pool to use with events. + private int Id => GetHashCode(); + + public override T[] Rent(int minimumLength) + { + // Arrays can't be smaller than zero. We allow requesting zero-length arrays (even though + // pooling such an array isn't valuable) as it's a valid length array, and we want the pool + // to be usable in general instead of using `new`, even for computed lengths. + if (minimumLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(minimumLength)); + } + else if (minimumLength == 0) + { + // No need for events with the empty array. Our pool is effectively infinite + // and we'll never allocate for rents and never store for returns. + return Array.Empty(); + } + + var log = ArrayPoolEventSource.Log; + T[] buffer = null; + + int index = Utilities.SelectBucketIndex(minimumLength); + if (index < _buckets.Length) + { + // Search for an array starting at the 'index' bucket. If the bucket is empty, bump up to the + // next higher bucket and try that one, but only try at most a few buckets. + const int MaxBucketsToTry = 2; + int i = index; + do + { + // Attempt to rent from the bucket. If we get a buffer from it, return it. + buffer = _buckets[i].Rent(); + if (buffer != null) + { + if (log.IsEnabled()) + { + log.BufferRented(buffer.GetHashCode(), buffer.Length, Id, _buckets[i].Id); + } + return buffer; + } + } + while (++i < _buckets.Length && i != index + MaxBucketsToTry); + + // The pool was exhausted for this buffer size. Allocate a new buffer with a size corresponding + // to the appropriate bucket. + buffer = new T[_buckets[index]._bufferLength]; + } + else + { + // The request was for a size too large for the pool. Allocate an array of exactly the requested length. + // When it's returned to the pool, we'll simply throw it away. + buffer = new T[minimumLength]; + } + + if (log.IsEnabled()) + { + int bufferId = buffer.GetHashCode(), bucketId = -1; // no bucket for an on-demand allocated buffer + log.BufferRented(bufferId, buffer.Length, Id, bucketId); + log.BufferAllocated(bufferId, buffer.Length, Id, bucketId, index >= _buckets.Length ? + ArrayPoolEventSource.BufferAllocatedReason.OverMaximumSize : ArrayPoolEventSource.BufferAllocatedReason.PoolExhausted); + } + + return buffer; + } + + public override void Return(T[] array, bool clearArray = false) + { + if (array == null) + { + throw new ArgumentNullException(nameof(array)); + } + else if (array.Length == 0) + { + // Ignore empty arrays. When a zero-length array is rented, we return a singleton + // rather than actually taking a buffer out of the lowest bucket. + return; + } + + // Determine with what bucket this array length is associated + int bucket = Utilities.SelectBucketIndex(array.Length); + + // If we can tell that the buffer was allocated, drop it. Otherwise, check if we have space in the pool + if (bucket < _buckets.Length) + { + // Clear the array if the user requests + if (clearArray) + { + Array.Clear(array, 0, array.Length); + } + + // Return the buffer to its bucket. In the future, we might consider having Return return false + // instead of dropping a bucket, in which case we could try to return to a lower-sized bucket, + // just as how in Rent we allow renting from a higher-sized bucket. + _buckets[bucket].Return(array); + } + + // Log that the buffer was returned + var log = ArrayPoolEventSource.Log; + if (log.IsEnabled()) + { + log.BufferReturned(array.GetHashCode(), array.Length, Id); + } + } + + /// Provides a thread-safe bucket containing buffers that can be Rent'd and Return'd. + private sealed class Bucket + { + internal readonly int _bufferLength; + private readonly T[][] _buffers; + private readonly int _poolId; + + private SpinLock _lock; // do not make this readonly; it's a mutable struct + private int _index; + + /// + /// Creates the pool with numberOfBuffers arrays where each buffer is of bufferLength length. + /// + internal Bucket(int bufferLength, int numberOfBuffers, int poolId) + { + _lock = new SpinLock(Debugger.IsAttached); // only enable thread tracking if debugger is attached; it adds non-trivial overheads to Enter/Exit + _buffers = new T[numberOfBuffers][]; + _bufferLength = bufferLength; + _poolId = poolId; + } + + /// Gets an ID for the bucket to use with events. + internal int Id => GetHashCode(); + + /// Takes an array from the bucket. If the bucket is empty, returns null. + internal T[] Rent() + { + T[][] buffers = _buffers; + T[] buffer = null; + + // While holding the lock, grab whatever is at the next available index and + // update the index. We do as little work as possible while holding the spin + // lock to minimize contention with other threads. The try/finally is + // necessary to properly handle thread aborts on platforms which have them. + bool lockTaken = false, allocateBuffer = false; + try + { + _lock.Enter(ref lockTaken); + + if (_index < buffers.Length) + { + buffer = buffers[_index]; + buffers[_index++] = null; + allocateBuffer = buffer == null; + } + } + finally + { + if (lockTaken) _lock.Exit(false); + } + + // While we were holding the lock, we grabbed whatever was at the next available index, if + // there was one. If we tried and if we got back null, that means we hadn't yet allocated + // for that slot, in which case we should do so now. + if (allocateBuffer) + { + buffer = new T[_bufferLength]; + + var log = ArrayPoolEventSource.Log; + if (log.IsEnabled()) + { + log.BufferAllocated(buffer.GetHashCode(), _bufferLength, _poolId, Id, + ArrayPoolEventSource.BufferAllocatedReason.Pooled); + } + } + + return buffer; + } + + /// + /// Attempts to return the buffer to the bucket. If successful, the buffer will be stored + /// in the bucket and true will be returned; otherwise, the buffer won't be stored, and false + /// will be returned. + /// + internal void Return(T[] array) + { + // Check to see if the buffer is the correct size for this bucket + if (array.Length != _bufferLength) + { + throw new ArgumentException(SR.ArgumentException_BufferNotFromPool, nameof(array)); + } + + // While holding the spin lock, if there's room available in the bucket, + // put the buffer into the next available slot. Otherwise, we just drop it. + // The try/finally is necessary to properly handle thread aborts on platforms + // which have them. + bool lockTaken = false; + try + { + _lock.Enter(ref lockTaken); + + if (_index != 0) + { + _buffers[--_index] = array; + } + } + finally + { + if (lockTaken) _lock.Exit(false); + } + } + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/IRetainable.cs b/external/corefx/src/Common/src/CoreLib/System/Buffers/IRetainable.cs similarity index 99% rename from external/corefx/src/System.Memory/src/System/Buffers/IRetainable.cs rename to external/corefx/src/Common/src/CoreLib/System/Buffers/IRetainable.cs index 3a92b3d5f5..6ac508859c 100644 --- a/external/corefx/src/System.Memory/src/System/Buffers/IRetainable.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Buffers/IRetainable.cs @@ -17,11 +17,10 @@ namespace System.Buffers /// Do not dispose until Release is called. /// void Retain(); - /// /// Call this method to indicate that the IRetainable object is no longer in use. /// The object can now be disposed. /// bool Release(); } -} +} \ No newline at end of file diff --git a/external/corefx/src/System.Memory/src/System/Buffers/MemoryHandle.cs b/external/corefx/src/Common/src/CoreLib/System/Buffers/MemoryHandle.cs similarity index 54% rename from external/corefx/src/System.Memory/src/System/Buffers/MemoryHandle.cs rename to external/corefx/src/Common/src/CoreLib/System/Buffers/MemoryHandle.cs index 68c4b4892f..7544038629 100644 --- a/external/corefx/src/System.Memory/src/System/Buffers/MemoryHandle.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Buffers/MemoryHandle.cs @@ -20,56 +20,45 @@ namespace System.Buffers /// Creates a new memory handle for the memory. /// /// reference to manually managed object - /// pointer to the buffer, or null if the buffer is not pinned + /// pointer to memory, or null if a pointer was not provided when the handle was created /// handle used to pin array buffers - public MemoryHandle(IRetainable retainable, void* pinnedPointer = null, GCHandle handle = default(GCHandle)) + [CLSCompliant(false)] + public MemoryHandle(IRetainable retainable, void* pointer = null, GCHandle handle = default(GCHandle)) { _retainable = retainable; - _pointer = pinnedPointer; + _pointer = pointer; _handle = handle; } /// - /// Returns the address of the pinned object, or null if the object is not pinned. + /// Returns the pointer to memory, or null if a pointer was not provided when the handle was created. /// - public void* PinnedPointer => _pointer; + [CLSCompliant(false)] + public void* Pointer => _pointer; /// - /// Adds an offset to the pinned pointer. + /// Returns false if the pointer to memory is null. /// - /// - /// Throw when pinned pointer is null. - /// - internal void AddOffset(int offset) - { - if (_pointer == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.pointer); - } - else - { - _pointer = (void*)((byte*)_pointer + offset); - } - } + public bool HasPointer => _pointer != null; /// /// Frees the pinned handle and releases IRetainable. /// - public void Dispose() - { - if (_handle.IsAllocated) + public void Dispose() + { + if (_handle.IsAllocated) { _handle.Free(); } - if (_retainable != null) + if (_retainable != null) { _retainable.Release(); _retainable = null; } - _pointer = null; + _pointer = null; } - + } -} +} \ No newline at end of file diff --git a/external/corefx/src/System.Memory/src/System/Buffers/OwnedMemory.cs b/external/corefx/src/Common/src/CoreLib/System/Buffers/OwnedMemory.cs similarity index 92% rename from external/corefx/src/System.Memory/src/System/Buffers/OwnedMemory.cs rename to external/corefx/src/Common/src/CoreLib/System/Buffers/OwnedMemory.cs index 3daae843da..6946addc80 100644 --- a/external/corefx/src/System.Memory/src/System/Buffers/OwnedMemory.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Buffers/OwnedMemory.cs @@ -7,13 +7,11 @@ using System.Runtime.CompilerServices; namespace System.Buffers { - /// /// Owner of Memory that provides appropriate lifetime management mechanisms for it. /// public abstract class OwnedMemory : IDisposable, IRetainable { - /// /// The number of items in the Memory. /// @@ -32,20 +30,20 @@ namespace System.Buffers /// public Memory Memory { - get + get { - if (IsDisposed) + if (IsDisposed) { ThrowHelper.ThrowObjectDisposedException_MemoryDisposed(nameof(OwnedMemory)); } - return new Memory(this, 0, Length); + return new Memory(owner: this, 0, Length); } } /// /// Returns a handle for the array that has been pinned and hence its address can be taken /// - public abstract MemoryHandle Pin(); + public abstract MemoryHandle Pin(int offset = 0); /// /// Returns an array segment. @@ -53,14 +51,14 @@ namespace System.Buffers protected internal abstract bool TryGetArray(out ArraySegment arraySegment); /// - /// Implements IDisposable. + /// Implements IDisposable. /// /// /// Throw when there are still retained references to the memory /// public void Dispose() { - if (IsRetained) + if (IsRetained) { ThrowHelper.ThrowInvalidOperationException_OutstandingReferences(); } @@ -84,7 +82,7 @@ namespace System.Buffers public abstract bool IsDisposed { get; } /// - /// Implements IRetainable. Prevent accidental disposal of the memory. + /// Implements IRetainable. Prevent accidental disposal of the memory. /// public abstract void Retain(); diff --git a/external/corefx/src/Common/src/CoreLib/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs b/external/corefx/src/Common/src/CoreLib/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs new file mode 100644 index 0000000000..64c5cebe85 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs @@ -0,0 +1,292 @@ +// 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 Microsoft.Win32; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace System.Buffers +{ + /// + /// Provides an ArrayPool implementation meant to be used as the singleton returned from ArrayPool.Shared. + /// + /// + /// The implementation uses a tiered caching scheme, with a small per-thread cache for each array size, followed + /// by a cache per array size shared by all threads, split into per-core stacks meant to be used by threads + /// running on that core. Locks are used to protect each per-core stack, because a thread can migrate after + /// checking its processor number, because multiple threads could interleave on the same core, and because + /// a thread is allowed to check other core's buckets if its core's bucket is empty/full. + /// + internal sealed partial class TlsOverPerCoreLockedStacksArrayPool : ArrayPool + { + // TODO: #7747: "Investigate optimizing ArrayPool heuristics" + // - Explore caching in TLS more than one array per size per thread, and moving stale buffers to the global queue. + // - Explore dumping stale buffers from the global queue, similar to PinnableBufferCache (maybe merging them). + // - Explore changing the size of each per-core bucket, potentially dynamically or based on other factors like array size. + // - Explore changing number of buckets and what sizes of arrays are cached. + // - Investigate whether false sharing is causing any issues, in particular on LockedStack's count and the contents of its array. + // ... + + /// The number of buckets (array sizes) in the pool, one for each array length, starting from length 16. + private const int NumBuckets = 17; // Utilities.SelectBucketIndex(2*1024*1024) + /// Maximum number of per-core stacks to use per array size. + private const int MaxPerCorePerArraySizeStacks = 64; // selected to avoid needing to worry about processor groups + /// The maximum number of buffers to store in a bucket's global queue. + private const int MaxBuffersPerArraySizePerCore = 8; + + /// The length of arrays stored in the corresponding indices in and . + private readonly int[] _bucketArraySizes; + /// + /// An array of per-core array stacks. The slots are lazily initialized to avoid creating + /// lots of overhead for unused array sizes. + /// + private readonly PerCoreLockedStacks[] _buckets = new PerCoreLockedStacks[NumBuckets]; + /// A per-thread array of arrays, to cache one array per array size per thread. + [ThreadStatic] + private static T[][] t_tlsBuckets; + + /// Initialize the pool. + public TlsOverPerCoreLockedStacksArrayPool() + { + var sizes = new int[NumBuckets]; + for (int i = 0; i < sizes.Length; i++) + { + sizes[i] = Utilities.GetMaxSizeForBucket(i); + } + _bucketArraySizes = sizes; + } + + /// Allocate a new PerCoreLockedStacks and try to store it into the array. + private PerCoreLockedStacks CreatePerCoreLockedStacks(int bucketIndex) + { + var inst = new PerCoreLockedStacks(); + return Interlocked.CompareExchange(ref _buckets[bucketIndex], inst, null) ?? inst; + } + + /// Gets an ID for the pool to use with events. + private int Id => GetHashCode(); + + public override T[] Rent(int minimumLength) + { + // Arrays can't be smaller than zero. We allow requesting zero-length arrays (even though + // pooling such an array isn't valuable) as it's a valid length array, and we want the pool + // to be usable in general instead of using `new`, even for computed lengths. + if (minimumLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(minimumLength)); + } + else if (minimumLength == 0) + { + // No need to log the empty array. Our pool is effectively infinite + // and we'll never allocate for rents and never store for returns. + return Array.Empty(); + } + + ArrayPoolEventSource log = ArrayPoolEventSource.Log; + T[] buffer; + + // Get the bucket number for the array length + int bucketIndex = Utilities.SelectBucketIndex(minimumLength); + + // If the array could come from a bucket... + if (bucketIndex < _buckets.Length) + { + // First try to get it from TLS if possible. + T[][] tlsBuckets = t_tlsBuckets; + if (tlsBuckets != null) + { + buffer = tlsBuckets[bucketIndex]; + if (buffer != null) + { + tlsBuckets[bucketIndex] = null; + if (log.IsEnabled()) + { + log.BufferRented(buffer.GetHashCode(), buffer.Length, Id, bucketIndex); + } + return buffer; + } + } + + // We couldn't get a buffer from TLS, so try the global stack. + PerCoreLockedStacks b = _buckets[bucketIndex]; + if (b != null) + { + buffer = b.TryPop(); + if (buffer != null) + { + if (log.IsEnabled()) + { + log.BufferRented(buffer.GetHashCode(), buffer.Length, Id, bucketIndex); + } + return buffer; + } + } + + // No buffer available. Allocate a new buffer with a size corresponding to the appropriate bucket. + buffer = new T[_bucketArraySizes[bucketIndex]]; + } + else + { + // The request was for a size too large for the pool. Allocate an array of exactly the requested length. + // When it's returned to the pool, we'll simply throw it away. + buffer = new T[minimumLength]; + } + + if (log.IsEnabled()) + { + int bufferId = buffer.GetHashCode(), bucketId = -1; // no bucket for an on-demand allocated buffer + log.BufferRented(bufferId, buffer.Length, Id, bucketId); + log.BufferAllocated(bufferId, buffer.Length, Id, bucketId, bucketIndex >= _buckets.Length ? + ArrayPoolEventSource.BufferAllocatedReason.OverMaximumSize : + ArrayPoolEventSource.BufferAllocatedReason.PoolExhausted); + } + + return buffer; + } + + public override void Return(T[] array, bool clearArray = false) + { + if (array == null) + { + throw new ArgumentNullException(nameof(array)); + } + + // Determine with what bucket this array length is associated + int bucketIndex = Utilities.SelectBucketIndex(array.Length); + + // If we can tell that the buffer was allocated (or empty), drop it. Otherwise, check if we have space in the pool. + if (bucketIndex < _buckets.Length) + { + // Clear the array if the user requests. + if (clearArray) + { + Array.Clear(array, 0, array.Length); + } + + // Check to see if the buffer is the correct size for this bucket + if (array.Length != _bucketArraySizes[bucketIndex]) + { + throw new ArgumentException(SR.ArgumentException_BufferNotFromPool, nameof(array)); + } + + // Write through the TLS bucket. If there weren't any buckets, create them + // and store this array into it. If there were, store this into it, and + // if there was a previous one there, push that to the global stack. This + // helps to keep LIFO access such that the most recently pushed stack will + // be in TLS and the first to be popped next. + T[][] tlsBuckets = t_tlsBuckets; + if (tlsBuckets == null) + { + t_tlsBuckets = tlsBuckets = new T[NumBuckets][]; + tlsBuckets[bucketIndex] = array; + } + else + { + T[] prev = tlsBuckets[bucketIndex]; + tlsBuckets[bucketIndex] = array; + if (prev != null) + { + PerCoreLockedStacks bucket = _buckets[bucketIndex] ?? CreatePerCoreLockedStacks(bucketIndex); + bucket.TryPush(prev); + } + } + } + + // Log that the buffer was returned + ArrayPoolEventSource log = ArrayPoolEventSource.Log; + if (log.IsEnabled()) + { + log.BufferReturned(array.GetHashCode(), array.Length, Id); + } + } + + /// + /// Stores a set of stacks of arrays, with one stack per core. + /// + private sealed class PerCoreLockedStacks + { + /// The stacks. + private readonly LockedStack[] _perCoreStacks; + + /// Initializes the stacks. + public PerCoreLockedStacks() + { + // Create the stacks. We create as many as there are processors, limited by our max. + var stacks = new LockedStack[Math.Min(Environment.ProcessorCount, MaxPerCorePerArraySizeStacks)]; + for (int i = 0; i < stacks.Length; i++) + { + stacks[i] = new LockedStack(); + } + _perCoreStacks = stacks; + } + + /// Try to push the array into the stacks. If each is full when it's tested, the array will be dropped. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void TryPush(T[] array) + { + // Try to push on to the associated stack first. If that fails, + // round-robin through the other stacks. + LockedStack[] stacks = _perCoreStacks; + int index = Environment.CurrentExecutionId % stacks.Length; + for (int i = 0; i < stacks.Length; i++) + { + if (stacks[index].TryPush(array)) return; + if (++index == stacks.Length) index = 0; + } + } + + /// Try to get an array from the stacks. If each is empty when it's tested, null will be returned. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T[] TryPop() + { + // Try to pop from the associated stack first. If that fails, + // round-robin through the other stacks. + T[] arr; + LockedStack[] stacks = _perCoreStacks; + int index = Environment.CurrentExecutionId % stacks.Length; + for (int i = 0; i < stacks.Length; i++) + { + if ((arr = stacks[index].TryPop()) != null) return arr; + if (++index == stacks.Length) index = 0; + } + return null; + } + } + + /// Provides a simple stack of arrays, protected by a lock. + private sealed class LockedStack + { + private readonly T[][] _arrays = new T[MaxBuffersPerArraySizePerCore][]; + private int _count; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryPush(T[] array) + { + bool enqueued = false; + Monitor.Enter(this); + if (_count < MaxBuffersPerArraySizePerCore) + { + _arrays[_count++] = array; + enqueued = true; + } + Monitor.Exit(this); + return enqueued; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T[] TryPop() + { + T[] arr = null; + Monitor.Enter(this); + if (_count > 0) + { + arr = _arrays[--_count]; + _arrays[_count] = null; + } + Monitor.Exit(this); + return arr; + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Buffers/Utilities.cs b/external/corefx/src/Common/src/CoreLib/System/Buffers/Utilities.cs new file mode 100644 index 0000000000..4f115fe9dd --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Buffers/Utilities.cs @@ -0,0 +1,35 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; + +namespace System.Buffers +{ + internal static class Utilities + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static int SelectBucketIndex(int bufferSize) + { + uint bitsRemaining = ((uint)bufferSize - 1) >> 4; + + int poolIndex = 0; + if (bitsRemaining > 0xFFFF) { bitsRemaining >>= 16; poolIndex = 16; } + if (bitsRemaining > 0xFF) { bitsRemaining >>= 8; poolIndex += 8; } + if (bitsRemaining > 0xF) { bitsRemaining >>= 4; poolIndex += 4; } + if (bitsRemaining > 0x3) { bitsRemaining >>= 2; poolIndex += 2; } + if (bitsRemaining > 0x1) { bitsRemaining >>= 1; poolIndex += 1; } + + return poolIndex + (int)bitsRemaining; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static int GetMaxSizeForBucket(int binIndex) + { + int maxSize = 16 << binIndex; + Debug.Assert(maxSize >= 0); + return maxSize; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Byte.cs b/external/corefx/src/Common/src/CoreLib/System/Byte.cs similarity index 50% rename from external/corert/src/System.Private.CoreLib/src/System/Byte.cs rename to external/corefx/src/Common/src/CoreLib/System/Byte.cs index 51cd83eade..13ceb7573d 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Byte.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Byte.cs @@ -2,30 +2,19 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -/*============================================================ -** -** -** -** Purpose: This class will encapsulate a byte and provide an -** Object representation of it. -** -** -===========================================================*/ - using System.Globalization; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; -using System.Diagnostics.Contracts; namespace System { - // The Byte class extends the Value class and - // provides object representation of the byte primitive type. - // - [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)] - public struct Byte : IComparable, IFormattable, IComparable, IEquatable, IConvertible + [Serializable] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Byte : IComparable, IConvertible, IFormattable, IComparable, IEquatable, ISpanFormattable { - private byte _value; + private byte m_value; // Do not rename (binary serialization) // The maximum value that a Byte may represent: 255. public const byte MaxValue = (byte)0xFF; @@ -51,12 +40,12 @@ namespace System throw new ArgumentException(SR.Arg_MustBeByte); } - return _value - (((Byte)value)._value); + return m_value - (((Byte)value).m_value); } public int CompareTo(Byte value) { - return _value - value; + return m_value - value; } // Determines whether two Byte objects are equal. @@ -66,54 +55,62 @@ namespace System { return false; } - return _value == ((Byte)obj)._value; + return m_value == ((Byte)obj).m_value; } [NonVersionable] public bool Equals(Byte obj) { - return _value == obj; + return m_value == obj; } // Gets a hash code for this instance. public override int GetHashCode() { - return _value; + return m_value; } - - [Pure] public static byte Parse(String s) { - return Parse(s, NumberStyles.Integer, null); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } - [Pure] public static byte Parse(String s, NumberStyles style) { - UInt32.ValidateParseStyleInteger(style); - return Parse(s, style, null); + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, style, NumberFormatInfo.CurrentInfo); } - [Pure] public static byte Parse(String s, IFormatProvider provider) { - return Parse(s, NumberStyles.Integer, provider); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } - // Parses an unsigned byte from a String in the given style. If // a NumberFormatInfo isn't specified, the current culture's // NumberFormatInfo is assumed. - [Pure] public static byte Parse(String s, NumberStyles style, IFormatProvider provider) { - UInt32.ValidateParseStyleInteger(style); + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider)); + } + public static byte Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Parse(s, style, NumberFormatInfo.GetInstance(provider)); + } + + private static byte Parse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info) + { int i = 0; try { - i = FormatProvider.ParseInt32(s, style, provider); + i = Number.ParseInt32(s, style, info); } catch (OverflowException e) { @@ -126,16 +123,44 @@ namespace System public static bool TryParse(String s, out Byte result) { - return TryParse(s, NumberStyles.Integer, null /* NumberFormatInfo.CurrentInfo */, out result); + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(ReadOnlySpan s, out byte result) + { + return TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); } public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out Byte result) { - UInt32.ValidateParseStyleInteger(style); + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out byte result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + private static bool TryParse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out Byte result) + { result = 0; int i; - if (!FormatProvider.TryParseInt32(s, style, provider, out i)) + if (!Number.TryParseInt32(s, style, info, out i)) { return false; } @@ -147,45 +172,38 @@ namespace System return true; } - [Pure] public override String ToString() { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatInt32(_value, null, null); + return Number.FormatInt32(m_value, null, null); } - [Pure] public String ToString(String format) { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatInt32(_value, format, null); + return Number.FormatInt32(m_value, format, null); } - [Pure] public String ToString(IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); - // if (provider == null) - // throw new ArgumentNullException("provider"); - - return FormatProvider.FormatInt32(_value, null, provider); + return Number.FormatInt32(m_value, null, provider); } - [Pure] public String ToString(String format, IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); + return Number.FormatInt32(m_value, format, provider); + } - // if (provider == null) - // throw new ArgumentNullException("provider"); + // TODO https://github.com/dotnet/corefx/issues/25337: Remove this overload once corefx is updated to target the new signatures + public bool TryFormat(Span destination, out int charsWritten, string format, IFormatProvider provider) => + TryFormat(destination, out charsWritten, (ReadOnlySpan)format, provider); - return FormatProvider.FormatInt32(_value, format, provider); + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) + { + return Number.TryFormatInt32(m_value, format, provider, destination, out charsWritten); } // // IConvertible implementation // - [Pure] public TypeCode GetTypeCode() { return TypeCode.Byte; @@ -194,72 +212,72 @@ namespace System bool IConvertible.ToBoolean(IFormatProvider provider) { - return Convert.ToBoolean(_value); + return Convert.ToBoolean(m_value); } char IConvertible.ToChar(IFormatProvider provider) { - return Convert.ToChar(_value); + return Convert.ToChar(m_value); } sbyte IConvertible.ToSByte(IFormatProvider provider) { - return Convert.ToSByte(_value); + return Convert.ToSByte(m_value); } byte IConvertible.ToByte(IFormatProvider provider) { - return _value; + return m_value; } short IConvertible.ToInt16(IFormatProvider provider) { - return Convert.ToInt16(_value); + return Convert.ToInt16(m_value); } ushort IConvertible.ToUInt16(IFormatProvider provider) { - return Convert.ToUInt16(_value); + return Convert.ToUInt16(m_value); } int IConvertible.ToInt32(IFormatProvider provider) { - return Convert.ToInt32(_value); + return Convert.ToInt32(m_value); } uint IConvertible.ToUInt32(IFormatProvider provider) { - return Convert.ToUInt32(_value); + return Convert.ToUInt32(m_value); } long IConvertible.ToInt64(IFormatProvider provider) { - return Convert.ToInt64(_value); + return Convert.ToInt64(m_value); } ulong IConvertible.ToUInt64(IFormatProvider provider) { - return Convert.ToUInt64(_value); + return Convert.ToUInt64(m_value); } float IConvertible.ToSingle(IFormatProvider provider) { - return Convert.ToSingle(_value); + return Convert.ToSingle(m_value); } double IConvertible.ToDouble(IFormatProvider provider) { - return Convert.ToDouble(_value); + return Convert.ToDouble(m_value); } Decimal IConvertible.ToDecimal(IFormatProvider provider) { - return Convert.ToDecimal(_value); + return Convert.ToDecimal(m_value); } DateTime IConvertible.ToDateTime(IFormatProvider provider) { - throw new InvalidCastException(String.Format(SR.InvalidCast_FromTo, "Byte", "DateTime")); + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Byte", "DateTime")); } Object IConvertible.ToType(Type type, IFormatProvider provider) diff --git a/external/corefx/src/Common/src/CoreLib/System/CLSCompliantAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/CLSCompliantAttribute.cs new file mode 100644 index 0000000000..d895b5ac71 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/CLSCompliantAttribute.cs @@ -0,0 +1,33 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: Container for assemblies. +** +** +=============================================================================*/ + +namespace System +{ + [AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = false)] + public sealed class CLSCompliantAttribute : Attribute + { + private bool _compliant; + + public CLSCompliantAttribute(bool isCompliant) + { + _compliant = isCompliant; + } + public bool IsCompliant + { + get + { + return _compliant; + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Char.cs b/external/corefx/src/Common/src/CoreLib/System/Char.cs new file mode 100644 index 0000000000..179ac40282 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Char.cs @@ -0,0 +1,1076 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: This is the value class representing a Unicode character +** Char methods until we create this functionality. +** +** +===========================================================*/ + +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; + +namespace System +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Char : IComparable, IComparable, IEquatable, IConvertible + { + // + // Member Variables + // + private char m_value; // Do not rename (binary serialization) + + // + // Public Constants + // + // The maximum character value. + public const char MaxValue = (char)0xFFFF; + // The minimum character value. + public const char MinValue = (char)0x00; + + // Unicode category values from Unicode U+0000 ~ U+00FF. Store them in byte[] array to save space. + private static readonly byte[] s_categoryForLatin1 = { + (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0000 - 0007 + (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0008 - 000F + (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0010 - 0017 + (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0018 - 001F + (byte)UnicodeCategory.SpaceSeparator, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.CurrencySymbol, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation, // 0020 - 0027 + (byte)UnicodeCategory.OpenPunctuation, (byte)UnicodeCategory.ClosePunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.DashPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation, // 0028 - 002F + (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, // 0030 - 0037 + (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.OtherPunctuation, // 0038 - 003F + (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, // 0040 - 0047 + (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, // 0048 - 004F + (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, // 0050 - 0057 + (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.OpenPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.ClosePunctuation, (byte)UnicodeCategory.ModifierSymbol, (byte)UnicodeCategory.ConnectorPunctuation, // 0058 - 005F + (byte)UnicodeCategory.ModifierSymbol, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, // 0060 - 0067 + (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, // 0068 - 006F + (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, // 0070 - 0077 + (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.OpenPunctuation, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.ClosePunctuation, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.Control, // 0078 - 007F + (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0080 - 0087 + (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0088 - 008F + (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0090 - 0097 + (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0098 - 009F + (byte)UnicodeCategory.SpaceSeparator, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.CurrencySymbol, (byte)UnicodeCategory.CurrencySymbol, (byte)UnicodeCategory.CurrencySymbol, (byte)UnicodeCategory.CurrencySymbol, (byte)UnicodeCategory.OtherSymbol, (byte)UnicodeCategory.OtherSymbol, // 00A0 - 00A7 + (byte)UnicodeCategory.ModifierSymbol, (byte)UnicodeCategory.OtherSymbol, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.InitialQuotePunctuation, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.DashPunctuation, (byte)UnicodeCategory.OtherSymbol, (byte)UnicodeCategory.ModifierSymbol, // 00A8 - 00AF + (byte)UnicodeCategory.OtherSymbol, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.ModifierSymbol, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.OtherSymbol, (byte)UnicodeCategory.OtherPunctuation, // 00B0 - 00B7 + (byte)UnicodeCategory.ModifierSymbol, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.FinalQuotePunctuation, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.OtherPunctuation, // 00B8 - 00BF + (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, // 00C0 - 00C7 + (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, // 00C8 - 00CF + (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.MathSymbol, // 00D0 - 00D7 + (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.LowercaseLetter, // 00D8 - 00DF + (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, // 00E0 - 00E7 + (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, // 00E8 - 00EF + (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.MathSymbol, // 00F0 - 00F7 + (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, // 00F8 - 00FF + }; + + // Return true for all characters below or equal U+00ff, which is ASCII + Latin-1 Supplement. + private static bool IsLatin1(char ch) + { + return (ch <= '\x00ff'); + } + + // Return true for all characters below or equal U+007f, which is ASCII. + private static bool IsAscii(char ch) + { + return (ch <= '\x007f'); + } + + // Return the Unicode category for Unicode character <= 0x00ff. + private static UnicodeCategory GetLatin1UnicodeCategory(char ch) + { + Debug.Assert(IsLatin1(ch), "Char.GetLatin1UnicodeCategory(): ch should be <= 007f"); + return (UnicodeCategory)(s_categoryForLatin1[(int)ch]); + } + + // + // Private Constants + // + + // + // Overriden Instance Methods + // + + // Calculate a hashcode for a 2 byte Unicode character. + public override int GetHashCode() + { + return (int)m_value | ((int)m_value << 16); + } + + // Used for comparing two boxed Char objects. + // + public override bool Equals(Object obj) + { + if (!(obj is Char)) + { + return false; + } + return (m_value == ((Char)obj).m_value); + } + + [System.Runtime.Versioning.NonVersionable] + public bool Equals(Char obj) + { + return m_value == obj; + } + + // Compares this object to another object, returning an integer that + // indicates the relationship. + // Returns a value less than zero if this object + // null is considered to be less than any instance. + // If object is not of type Char, this method throws an ArgumentException. + // + public int CompareTo(Object value) + { + if (value == null) + { + return 1; + } + if (!(value is Char)) + { + throw new ArgumentException(SR.Arg_MustBeChar); + } + + return (m_value - ((Char)value).m_value); + } + + public int CompareTo(Char value) + { + return (m_value - value); + } + + // Overrides System.Object.ToString. + public override String ToString() + { + return Char.ToString(m_value); + } + + public String ToString(IFormatProvider provider) + { + return Char.ToString(m_value); + } + + // + // Formatting Methods + // + + /*===================================ToString=================================== + **This static methods takes a character and returns the String representation of it. + ==============================================================================*/ + // Provides a string representation of a character. + public static string ToString(char c) => string.CreateFromChar(c); + + public static char Parse(String s) + { + if (s == null) + { + throw new ArgumentNullException(nameof(s)); + } + + if (s.Length != 1) + { + throw new FormatException(SR.Format_NeedSingleChar); + } + return s[0]; + } + + public static bool TryParse(String s, out Char result) + { + result = '\0'; + if (s == null) + { + return false; + } + if (s.Length != 1) + { + return false; + } + result = s[0]; + return true; + } + + // + // Static Methods + // + /*=================================ISDIGIT====================================== + **A wrapper for Char. Returns a boolean indicating whether ** + **character c is considered to be a digit. ** + ==============================================================================*/ + // Determines whether a character is a digit. + public static bool IsDigit(char c) + { + if (IsLatin1(c)) + { + return (c >= '0' && c <= '9'); + } + return (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.DecimalDigitNumber); + } + + + /*=================================CheckLetter===================================== + ** Check if the specified UnicodeCategory belongs to the letter categories. + ==============================================================================*/ + internal static bool CheckLetter(UnicodeCategory uc) + { + switch (uc) + { + case (UnicodeCategory.UppercaseLetter): + case (UnicodeCategory.LowercaseLetter): + case (UnicodeCategory.TitlecaseLetter): + case (UnicodeCategory.ModifierLetter): + case (UnicodeCategory.OtherLetter): + return (true); + } + return (false); + } + + /*=================================ISLETTER===================================== + **A wrapper for Char. Returns a boolean indicating whether ** + **character c is considered to be a letter. ** + ==============================================================================*/ + // Determines whether a character is a letter. + public static bool IsLetter(char c) + { + if (IsLatin1(c)) + { + if (IsAscii(c)) + { + c |= (char)0x20; + return ((c >= 'a' && c <= 'z')); + } + return (CheckLetter(GetLatin1UnicodeCategory(c))); + } + return (CheckLetter(CharUnicodeInfo.GetUnicodeCategory(c))); + } + + private static bool IsWhiteSpaceLatin1(char c) + { + // There are characters which belong to UnicodeCategory.Control but are considered as white spaces. + // We use code point comparisons for these characters here as a temporary fix. + + // U+0009 = HORIZONTAL TAB + // U+000a = LINE FEED + // U+000b = VERTICAL TAB + // U+000c = FORM FEED + // U+000d = CARRIAGE RETURN + // U+0085 = NEXT LINE + // U+00a0 = NO-BREAK SPACE + return + c == ' ' || + (uint)(c - '\x0009') <= ('\x000d' - '\x0009') || // (c >= '\x0009' && c <= '\x000d') + c == '\x00a0' || + c == '\x0085'; + } + + /*===============================ISWHITESPACE=================================== + **A wrapper for Char. Returns a boolean indicating whether ** + **character c is considered to be a whitespace character. ** + ==============================================================================*/ + // Determines whether a character is whitespace. + public static bool IsWhiteSpace(char c) + { + if (IsLatin1(c)) + { + return (IsWhiteSpaceLatin1(c)); + } + return CharUnicodeInfo.IsWhiteSpace(c); + } + + + /*===================================IsUpper==================================== + **Arguments: c -- the characater to be checked. + **Returns: True if c is an uppercase character. + ==============================================================================*/ + // Determines whether a character is upper-case. + public static bool IsUpper(char c) + { + if (IsLatin1(c)) + { + if (IsAscii(c)) + { + return (c >= 'A' && c <= 'Z'); + } + return (GetLatin1UnicodeCategory(c) == UnicodeCategory.UppercaseLetter); + } + return (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.UppercaseLetter); + } + + /*===================================IsLower==================================== + **Arguments: c -- the characater to be checked. + **Returns: True if c is an lowercase character. + ==============================================================================*/ + // Determines whether a character is lower-case. + public static bool IsLower(char c) + { + if (IsLatin1(c)) + { + if (IsAscii(c)) + { + return (c >= 'a' && c <= 'z'); + } + return (GetLatin1UnicodeCategory(c) == UnicodeCategory.LowercaseLetter); + } + return (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.LowercaseLetter); + } + + internal static bool CheckPunctuation(UnicodeCategory uc) + { + switch (uc) + { + case UnicodeCategory.ConnectorPunctuation: + case UnicodeCategory.DashPunctuation: + case UnicodeCategory.OpenPunctuation: + case UnicodeCategory.ClosePunctuation: + case UnicodeCategory.InitialQuotePunctuation: + case UnicodeCategory.FinalQuotePunctuation: + case UnicodeCategory.OtherPunctuation: + return (true); + } + return (false); + } + + + /*================================IsPunctuation================================= + **Arguments: c -- the characater to be checked. + **Returns: True if c is an punctuation mark + ==============================================================================*/ + // Determines whether a character is a punctuation mark. + public static bool IsPunctuation(char c) + { + if (IsLatin1(c)) + { + return (CheckPunctuation(GetLatin1UnicodeCategory(c))); + } + return (CheckPunctuation(CharUnicodeInfo.GetUnicodeCategory(c))); + } + + /*=================================CheckLetterOrDigit===================================== + ** Check if the specified UnicodeCategory belongs to the letter or digit categories. + ==============================================================================*/ + internal static bool CheckLetterOrDigit(UnicodeCategory uc) + { + switch (uc) + { + case UnicodeCategory.UppercaseLetter: + case UnicodeCategory.LowercaseLetter: + case UnicodeCategory.TitlecaseLetter: + case UnicodeCategory.ModifierLetter: + case UnicodeCategory.OtherLetter: + case UnicodeCategory.DecimalDigitNumber: + return (true); + } + return (false); + } + + // Determines whether a character is a letter or a digit. + public static bool IsLetterOrDigit(char c) + { + if (IsLatin1(c)) + { + return (CheckLetterOrDigit(GetLatin1UnicodeCategory(c))); + } + return (CheckLetterOrDigit(CharUnicodeInfo.GetUnicodeCategory(c))); + } + + /*===================================ToUpper==================================== + ** + ==============================================================================*/ + // Converts a character to upper-case for the specified culture. + // <;<;Not fully implemented>;>; + public static char ToUpper(char c, CultureInfo culture) + { + if (culture == null) + throw new ArgumentNullException(nameof(culture)); + return culture.TextInfo.ToUpper(c); + } + + /*=================================TOUPPER====================================== + **A wrapper for Char.toUpperCase. Converts character c to its ** + **uppercase equivalent. If c is already an uppercase character or is not an ** + **alphabetic, nothing happens. ** + ==============================================================================*/ + // Converts a character to upper-case for the default culture. + // + public static char ToUpper(char c) + { + return ToUpper(c, CultureInfo.CurrentCulture); + } + + + // Converts a character to upper-case for invariant culture. + public static char ToUpperInvariant(char c) + { + return ToUpper(c, CultureInfo.InvariantCulture); + } + + + /*===================================ToLower==================================== + ** + ==============================================================================*/ + // Converts a character to lower-case for the specified culture. + // <;<;Not fully implemented>;>; + public static char ToLower(char c, CultureInfo culture) + { + if (culture == null) + throw new ArgumentNullException(nameof(culture)); + return culture.TextInfo.ToLower(c); + } + + /*=================================TOLOWER====================================== + **A wrapper for Char.toLowerCase. Converts character c to its ** + **lowercase equivalent. If c is already a lowercase character or is not an ** + **alphabetic, nothing happens. ** + ==============================================================================*/ + // Converts a character to lower-case for the default culture. + public static char ToLower(char c) + { + return ToLower(c, CultureInfo.CurrentCulture); + } + + + // Converts a character to lower-case for invariant culture. + public static char ToLowerInvariant(char c) + { + return ToLower(c, CultureInfo.InvariantCulture); + } + + + // + // IConvertible implementation + // + public TypeCode GetTypeCode() + { + return TypeCode.Char; + } + + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "Boolean")); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return m_value; + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(m_value); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "Single")); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "Double")); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "Decimal")); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + public static bool IsControl(char c) + { + if (IsLatin1(c)) + { + return (GetLatin1UnicodeCategory(c) == UnicodeCategory.Control); + } + return (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.Control); + } + + public static bool IsControl(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + char c = s[index]; + if (IsLatin1(c)) + { + return (GetLatin1UnicodeCategory(c) == UnicodeCategory.Control); + } + return (CharUnicodeInfo.GetUnicodeCategory(s, index) == UnicodeCategory.Control); + } + + + public static bool IsDigit(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + char c = s[index]; + if (IsLatin1(c)) + { + return (c >= '0' && c <= '9'); + } + return (CharUnicodeInfo.GetUnicodeCategory(s, index) == UnicodeCategory.DecimalDigitNumber); + } + + public static bool IsLetter(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + char c = s[index]; + if (IsLatin1(c)) + { + if (IsAscii(c)) + { + c |= (char)0x20; + return ((c >= 'a' && c <= 'z')); + } + return (CheckLetter(GetLatin1UnicodeCategory(c))); + } + return (CheckLetter(CharUnicodeInfo.GetUnicodeCategory(s, index))); + } + + public static bool IsLetterOrDigit(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + char c = s[index]; + if (IsLatin1(c)) + { + return CheckLetterOrDigit(GetLatin1UnicodeCategory(c)); + } + return CheckLetterOrDigit(CharUnicodeInfo.GetUnicodeCategory(s, index)); + } + + public static bool IsLower(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + char c = s[index]; + if (IsLatin1(c)) + { + if (IsAscii(c)) + { + return (c >= 'a' && c <= 'z'); + } + return (GetLatin1UnicodeCategory(c) == UnicodeCategory.LowercaseLetter); + } + + return (CharUnicodeInfo.GetUnicodeCategory(s, index) == UnicodeCategory.LowercaseLetter); + } + + /*=================================CheckNumber===================================== + ** Check if the specified UnicodeCategory belongs to the number categories. + ==============================================================================*/ + + internal static bool CheckNumber(UnicodeCategory uc) + { + switch (uc) + { + case (UnicodeCategory.DecimalDigitNumber): + case (UnicodeCategory.LetterNumber): + case (UnicodeCategory.OtherNumber): + return (true); + } + return (false); + } + + public static bool IsNumber(char c) + { + if (IsLatin1(c)) + { + if (IsAscii(c)) + { + return (c >= '0' && c <= '9'); + } + return (CheckNumber(GetLatin1UnicodeCategory(c))); + } + return (CheckNumber(CharUnicodeInfo.GetUnicodeCategory(c))); + } + + public static bool IsNumber(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + char c = s[index]; + if (IsLatin1(c)) + { + if (IsAscii(c)) + { + return (c >= '0' && c <= '9'); + } + return (CheckNumber(GetLatin1UnicodeCategory(c))); + } + return (CheckNumber(CharUnicodeInfo.GetUnicodeCategory(s, index))); + } + + //////////////////////////////////////////////////////////////////////// + // + // IsPunctuation + // + // Determines if the given character is a punctuation character. + // + //////////////////////////////////////////////////////////////////////// + + public static bool IsPunctuation(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + char c = s[index]; + if (IsLatin1(c)) + { + return (CheckPunctuation(GetLatin1UnicodeCategory(c))); + } + return (CheckPunctuation(CharUnicodeInfo.GetUnicodeCategory(s, index))); + } + + + /*================================= CheckSeparator ============================ + ** Check if the specified UnicodeCategory belongs to the seprator categories. + ==============================================================================*/ + + internal static bool CheckSeparator(UnicodeCategory uc) + { + switch (uc) + { + case UnicodeCategory.SpaceSeparator: + case UnicodeCategory.LineSeparator: + case UnicodeCategory.ParagraphSeparator: + return (true); + } + return (false); + } + + private static bool IsSeparatorLatin1(char c) + { + // U+00a0 = NO-BREAK SPACE + // There is no LineSeparator or ParagraphSeparator in Latin 1 range. + return (c == '\x0020' || c == '\x00a0'); + } + + public static bool IsSeparator(char c) + { + if (IsLatin1(c)) + { + return (IsSeparatorLatin1(c)); + } + return (CheckSeparator(CharUnicodeInfo.GetUnicodeCategory(c))); + } + + public static bool IsSeparator(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + char c = s[index]; + if (IsLatin1(c)) + { + return (IsSeparatorLatin1(c)); + } + return (CheckSeparator(CharUnicodeInfo.GetUnicodeCategory(s, index))); + } + + public static bool IsSurrogate(char c) + { + return (c >= HIGH_SURROGATE_START && c <= LOW_SURROGATE_END); + } + + public static bool IsSurrogate(String s, int index) + { + if (s == null) + { + throw new ArgumentNullException(nameof(s)); + } + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + return (IsSurrogate(s[index])); + } + + /*================================= CheckSymbol ============================ + ** Check if the specified UnicodeCategory belongs to the symbol categories. + ==============================================================================*/ + + internal static bool CheckSymbol(UnicodeCategory uc) + { + switch (uc) + { + case (UnicodeCategory.MathSymbol): + case (UnicodeCategory.CurrencySymbol): + case (UnicodeCategory.ModifierSymbol): + case (UnicodeCategory.OtherSymbol): + return (true); + } + return (false); + } + + public static bool IsSymbol(char c) + { + if (IsLatin1(c)) + { + return (CheckSymbol(GetLatin1UnicodeCategory(c))); + } + return (CheckSymbol(CharUnicodeInfo.GetUnicodeCategory(c))); + } + + public static bool IsSymbol(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + char c = s[index]; + if (IsLatin1(c)) + { + return (CheckSymbol(GetLatin1UnicodeCategory(c))); + } + return (CheckSymbol(CharUnicodeInfo.GetUnicodeCategory(s, index))); + } + + + public static bool IsUpper(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + char c = s[index]; + if (IsLatin1(c)) + { + if (IsAscii(c)) + { + return (c >= 'A' && c <= 'Z'); + } + return (GetLatin1UnicodeCategory(c) == UnicodeCategory.UppercaseLetter); + } + + return (CharUnicodeInfo.GetUnicodeCategory(s, index) == UnicodeCategory.UppercaseLetter); + } + + public static bool IsWhiteSpace(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + if (IsLatin1(s[index])) + { + return IsWhiteSpaceLatin1(s[index]); + } + + return CharUnicodeInfo.IsWhiteSpace(s, index); + } + + public static UnicodeCategory GetUnicodeCategory(char c) + { + if (IsLatin1(c)) + { + return (GetLatin1UnicodeCategory(c)); + } + return CharUnicodeInfo.GetUnicodeCategory((int)c); + } + + public static UnicodeCategory GetUnicodeCategory(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + if (IsLatin1(s[index])) + { + return (GetLatin1UnicodeCategory(s[index])); + } + return CharUnicodeInfo.InternalGetUnicodeCategory(s, index); + } + + public static double GetNumericValue(char c) + { + return CharUnicodeInfo.GetNumericValue(c); + } + + public static double GetNumericValue(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + return CharUnicodeInfo.GetNumericValue(s, index); + } + + + /*================================= IsHighSurrogate ============================ + ** Check if a char is a high surrogate. + ==============================================================================*/ + public static bool IsHighSurrogate(char c) + { + return ((c >= CharUnicodeInfo.HIGH_SURROGATE_START) && (c <= CharUnicodeInfo.HIGH_SURROGATE_END)); + } + + public static bool IsHighSurrogate(String s, int index) + { + if (s == null) + { + throw new ArgumentNullException(nameof(s)); + } + if (index < 0 || index >= s.Length) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + return (IsHighSurrogate(s[index])); + } + + /*================================= IsLowSurrogate ============================ + ** Check if a char is a low surrogate. + ==============================================================================*/ + public static bool IsLowSurrogate(char c) + { + return ((c >= CharUnicodeInfo.LOW_SURROGATE_START) && (c <= CharUnicodeInfo.LOW_SURROGATE_END)); + } + + public static bool IsLowSurrogate(String s, int index) + { + if (s == null) + { + throw new ArgumentNullException(nameof(s)); + } + if (index < 0 || index >= s.Length) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + return (IsLowSurrogate(s[index])); + } + + /*================================= IsSurrogatePair ============================ + ** Check if the string specified by the index starts with a surrogate pair. + ==============================================================================*/ + public static bool IsSurrogatePair(String s, int index) + { + if (s == null) + { + throw new ArgumentNullException(nameof(s)); + } + if (index < 0 || index >= s.Length) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + if (index + 1 < s.Length) + { + return (IsSurrogatePair(s[index], s[index + 1])); + } + return (false); + } + + public static bool IsSurrogatePair(char highSurrogate, char lowSurrogate) + { + return ((highSurrogate >= CharUnicodeInfo.HIGH_SURROGATE_START && highSurrogate <= CharUnicodeInfo.HIGH_SURROGATE_END) && + (lowSurrogate >= CharUnicodeInfo.LOW_SURROGATE_START && lowSurrogate <= CharUnicodeInfo.LOW_SURROGATE_END)); + } + + internal const int UNICODE_PLANE00_END = 0x00ffff; + // The starting codepoint for Unicode plane 1. Plane 1 contains 0x010000 ~ 0x01ffff. + internal const int UNICODE_PLANE01_START = 0x10000; + // The end codepoint for Unicode plane 16. This is the maximum code point value allowed for Unicode. + // Plane 16 contains 0x100000 ~ 0x10ffff. + internal const int UNICODE_PLANE16_END = 0x10ffff; + + internal const int HIGH_SURROGATE_START = 0x00d800; + internal const int LOW_SURROGATE_END = 0x00dfff; + + + + /*================================= ConvertFromUtf32 ============================ + ** Convert an UTF32 value into a surrogate pair. + ==============================================================================*/ + + public static String ConvertFromUtf32(int utf32) + { + // For UTF32 values from U+00D800 ~ U+00DFFF, we should throw. They + // are considered as irregular code unit sequence, but they are not illegal. + if ((utf32 < 0 || utf32 > UNICODE_PLANE16_END) || (utf32 >= HIGH_SURROGATE_START && utf32 <= LOW_SURROGATE_END)) + { + throw new ArgumentOutOfRangeException(nameof(utf32), SR.ArgumentOutOfRange_InvalidUTF32); + } + + if (utf32 < UNICODE_PLANE01_START) + { + // This is a BMP character. + return (Char.ToString((char)utf32)); + } + + unsafe + { + // This is a supplementary character. Convert it to a surrogate pair in UTF-16. + utf32 -= UNICODE_PLANE01_START; + uint surrogate = 0; // allocate 2 chars worth of stack space + char* address = (char*)&surrogate; + address[0] = (char)((utf32 / 0x400) + (int)CharUnicodeInfo.HIGH_SURROGATE_START); + address[1] = (char)((utf32 % 0x400) + (int)CharUnicodeInfo.LOW_SURROGATE_START); + return new string(address, 0, 2); + } + } + + + /*=============================ConvertToUtf32=================================== + ** Convert a surrogate pair to UTF32 value + ==============================================================================*/ + + public static int ConvertToUtf32(char highSurrogate, char lowSurrogate) + { + if (!IsHighSurrogate(highSurrogate)) + { + throw new ArgumentOutOfRangeException(nameof(highSurrogate), SR.ArgumentOutOfRange_InvalidHighSurrogate); + } + if (!IsLowSurrogate(lowSurrogate)) + { + throw new ArgumentOutOfRangeException(nameof(lowSurrogate), SR.ArgumentOutOfRange_InvalidLowSurrogate); + } + return (((highSurrogate - CharUnicodeInfo.HIGH_SURROGATE_START) * 0x400) + (lowSurrogate - CharUnicodeInfo.LOW_SURROGATE_START) + UNICODE_PLANE01_START); + } + + /*=============================ConvertToUtf32=================================== + ** Convert a character or a surrogate pair starting at index of the specified string + ** to UTF32 value. + ** The char pointed by index should be a surrogate pair or a BMP character. + ** This method throws if a high-surrogate is not followed by a low surrogate. + ** This method throws if a low surrogate is seen without preceding a high-surrogate. + ==============================================================================*/ + + public static int ConvertToUtf32(String s, int index) + { + if (s == null) + { + throw new ArgumentNullException(nameof(s)); + } + + if (index < 0 || index >= s.Length) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + } + // Check if the character at index is a high surrogate. + int temp1 = (int)s[index] - CharUnicodeInfo.HIGH_SURROGATE_START; + if (temp1 >= 0 && temp1 <= 0x7ff) + { + // Found a surrogate char. + if (temp1 <= 0x3ff) + { + // Found a high surrogate. + if (index < s.Length - 1) + { + int temp2 = (int)s[index + 1] - CharUnicodeInfo.LOW_SURROGATE_START; + if (temp2 >= 0 && temp2 <= 0x3ff) + { + // Found a low surrogate. + return ((temp1 * 0x400) + temp2 + UNICODE_PLANE01_START); + } + else + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidHighSurrogate, index), nameof(s)); + } + } + else + { + // Found a high surrogate at the end of the string. + throw new ArgumentException(SR.Format(SR.Argument_InvalidHighSurrogate, index), nameof(s)); + } + } + else + { + // Find a low surrogate at the character pointed by index. + throw new ArgumentException(SR.Format(SR.Argument_InvalidLowSurrogate, index), nameof(s)); + } + } + // Not a high-surrogate or low-surrogate. Genereate the UTF32 value for the BMP characters. + return ((int)s[index]); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/CharEnumerator.cs b/external/corefx/src/Common/src/CoreLib/System/CharEnumerator.cs new file mode 100644 index 0000000000..ea9915a7c4 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/CharEnumerator.cs @@ -0,0 +1,80 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: Enumerates the characters on a string. skips range +** checks. +** +** +============================================================*/ + +using System.Collections; +using System.Collections.Generic; + +namespace System +{ + public sealed class CharEnumerator : IEnumerator, IEnumerator, IDisposable, ICloneable + { + private String _str; + private int _index; + private char _currentElement; + + internal CharEnumerator(String str) + { + _str = str; + _index = -1; + } + + public object Clone() + { + return MemberwiseClone(); + } + + public bool MoveNext() + { + if (_index < (_str.Length - 1)) + { + _index++; + _currentElement = _str[_index]; + return true; + } + else + _index = _str.Length; + return false; + } + + public void Dispose() + { + if (_str != null) + _index = _str.Length; + _str = null; + } + + Object IEnumerator.Current + { + get { return Current; } + } + + public char Current + { + get + { + if (_index == -1) + throw new InvalidOperationException(SR.InvalidOperation_EnumNotStarted); + if (_index >= _str.Length) + throw new InvalidOperationException(SR.InvalidOperation_EnumEnded); + return _currentElement; + } + } + + public void Reset() + { + _currentElement = (char)0; + _index = -1; + } + } +} diff --git a/external/corefx/src/System.Runtime.Extensions/src/System/Collections/Comparer.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/Comparer.cs similarity index 72% rename from external/corefx/src/System.Runtime.Extensions/src/System/Collections/Comparer.cs rename to external/corefx/src/Common/src/CoreLib/System/Collections/Comparer.cs index 12a04d4391..0d42352bc9 100644 --- a/external/corefx/src/System.Runtime.Extensions/src/System/Collections/Comparer.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/Comparer.cs @@ -4,15 +4,11 @@ /*============================================================ ** -** Class: Comparer -** -** Purpose: Compares two objects for equivalence, -** where string comparisons are case-sensitive. +** Purpose: Default IComparer implementation. ** ===========================================================*/ using System.Globalization; -using System.Diagnostics.Contracts; using System.Runtime.Serialization; namespace System.Collections @@ -24,47 +20,32 @@ namespace System.Collections public sealed class Comparer : IComparer, ISerializable { private CompareInfo _compareInfo; + public static readonly Comparer Default = new Comparer(CultureInfo.CurrentCulture); public static readonly Comparer DefaultInvariant = new Comparer(CultureInfo.InvariantCulture); - private const string CompareInfoName = "CompareInfo"; // Do not rename (binary serialization) - public Comparer(CultureInfo culture) { if (culture == null) - { throw new ArgumentNullException(nameof(culture)); - } - Contract.EndContractBlock(); + _compareInfo = culture.CompareInfo; } private Comparer(SerializationInfo info, StreamingContext context) { - _compareInfo = null; - SerializationInfoEnumerator enumerator = info.GetEnumerator(); - while (enumerator.MoveNext()) - { - switch (enumerator.Name) - { - case CompareInfoName: - _compareInfo = (CompareInfo)info.GetValue(CompareInfoName, typeof(CompareInfo)); - break; - } - } + if (info == null) + throw new ArgumentNullException(nameof(info)); + + _compareInfo = (CompareInfo)info.GetValue("CompareInfo", typeof(CompareInfo)); } public void GetObjectData(SerializationInfo info, StreamingContext context) { if (info == null) - { throw new ArgumentNullException(nameof(info)); - } - if (_compareInfo != null) - { - info.AddValue(CompareInfoName, _compareInfo); - } + info.AddValue("CompareInfo", _compareInfo); } // Compares two Objects by calling CompareTo. diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/DictionaryEntry.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/DictionaryEntry.cs new file mode 100644 index 0000000000..d0cbcb6663 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/DictionaryEntry.cs @@ -0,0 +1,65 @@ +// 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. + +#if !MONO +using System.ComponentModel; +#endif + +namespace System.Collections +{ + // A DictionaryEntry holds a key and a value from a dictionary. + // It is returned by IDictionaryEnumerator::GetEntry(). + [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif + public struct DictionaryEntry + { + private Object _key; // Do not rename (binary serialization) + private Object _value; // Do not rename (binary serialization) + + // Constructs a new DictionaryEnumerator by setting the Key + // and Value fields appropriately. + public DictionaryEntry(Object key, Object value) + { + _key = key; + _value = value; + } + + public Object Key + { + get + { + return _key; + } + + set + { + _key = value; + } + } + + public Object Value + { + get + { + return _value; + } + + set + { + _value = value; + } + } + +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public void Deconstruct(out object key, out object value) + { + key = Key; + value = Value; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/Dictionary.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/Dictionary.cs new file mode 100644 index 0000000000..7801f7d1d6 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/Dictionary.cs @@ -0,0 +1,1519 @@ +// 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.Collections; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; + +namespace System.Collections.Generic +{ + /// + /// Used internally to control behavior of insertion into a . + /// + internal enum InsertionBehavior : byte + { + /// + /// The default insertion behavior. + /// + None = 0, + + /// + /// Specifies that an existing entry with the same key should be overwritten if encountered. + /// + OverwriteExisting = 1, + + /// + /// Specifies that if an existing entry with the same key is encountered, an exception should be thrown. + /// + ThrowOnExisting = 2 + } + + [DebuggerTypeProxy(typeof(IDictionaryDebugView<,>))] + [DebuggerDisplay("Count = {Count}")] + [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif + public class Dictionary : IDictionary, IDictionary, IReadOnlyDictionary, ISerializable, IDeserializationCallback + { + private struct Entry + { + public int hashCode; // Lower 31 bits of hash code, -1 if unused + public int next; // Index of next entry, -1 if last + public TKey key; // Key of entry + public TValue value; // Value of entry + } + + private int[] _buckets; + private Entry[] _entries; + private int _count; + private int _freeList; + private int _freeCount; + private int _version; + private IEqualityComparer _comparer; + private KeyCollection _keys; + private ValueCollection _values; + private object _syncRoot; + + // constants for serialization + private const string VersionName = "Version"; // Do not rename (binary serialization) + private const string HashSizeName = "HashSize"; // Do not rename (binary serialization). Must save buckets.Length + private const string KeyValuePairsName = "KeyValuePairs"; // Do not rename (binary serialization) + private const string ComparerName = "Comparer"; // Do not rename (binary serialization) + + public Dictionary() : this(0, null) { } + + public Dictionary(int capacity) : this(capacity, null) { } + + public Dictionary(IEqualityComparer comparer) : this(0, comparer) { } + + public Dictionary(int capacity, IEqualityComparer comparer) + { + if (capacity < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity); + if (capacity > 0) Initialize(capacity); + _comparer = comparer ?? EqualityComparer.Default; +#if !MONO + if (_comparer == EqualityComparer.Default) + { + _comparer = (IEqualityComparer)NonRandomizedStringEqualityComparer.Default; + } +#endif + } + + public Dictionary(IDictionary dictionary) : this(dictionary, null) { } + + public Dictionary(IDictionary dictionary, IEqualityComparer comparer) : + this(dictionary != null ? dictionary.Count : 0, comparer) + { + if (dictionary == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary); + } + + // It is likely that the passed-in dictionary is Dictionary. When this is the case, + // avoid the enumerator allocation and overhead by looping through the entries array directly. + // We only do this when dictionary is Dictionary and not a subclass, to maintain + // back-compat with subclasses that may have overridden the enumerator behavior. + if (dictionary.GetType() == typeof(Dictionary)) + { + Dictionary d = (Dictionary)dictionary; + int count = d._count; + Entry[] entries = d._entries; + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) + { + Add(entries[i].key, entries[i].value); + } + } + return; + } + + foreach (KeyValuePair pair in dictionary) + { + Add(pair.Key, pair.Value); + } + } + + public Dictionary(IEnumerable> collection) : this(collection, null) { } + + public Dictionary(IEnumerable> collection, IEqualityComparer comparer) : + this((collection as ICollection>)?.Count ?? 0, comparer) + { + if (collection == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection); + } + + foreach (KeyValuePair pair in collection) + { + Add(pair.Key, pair.Value); + } + } + + protected Dictionary(SerializationInfo info, StreamingContext context) + { + // We can't do anything with the keys and values until the entire graph has been deserialized + // and we have a resonable estimate that GetHashCode is not going to fail. For the time being, + // we'll just cache this. The graph is not valid until OnDeserialization has been called. + HashHelpers.SerializationInfoTable.Add(this, info); + } + + public IEqualityComparer Comparer + { + get + { + return _comparer; + } + } + + public int Count + { + get { return _count - _freeCount; } + } + + public KeyCollection Keys + { + get + { + if (_keys == null) _keys = new KeyCollection(this); + return _keys; + } + } + + ICollection IDictionary.Keys + { + get + { + if (_keys == null) _keys = new KeyCollection(this); + return _keys; + } + } + + IEnumerable IReadOnlyDictionary.Keys + { + get + { + if (_keys == null) _keys = new KeyCollection(this); + return _keys; + } + } + + public ValueCollection Values + { + get + { + if (_values == null) _values = new ValueCollection(this); + return _values; + } + } + + ICollection IDictionary.Values + { + get + { + if (_values == null) _values = new ValueCollection(this); + return _values; + } + } + + IEnumerable IReadOnlyDictionary.Values + { + get + { + if (_values == null) _values = new ValueCollection(this); + return _values; + } + } + + public TValue this[TKey key] + { + get + { + int i = FindEntry(key); + if (i >= 0) return _entries[i].value; + ThrowHelper.ThrowKeyNotFoundException(key); + return default(TValue); + } + set + { + bool modified = TryInsert(key, value, InsertionBehavior.OverwriteExisting); +#if !MONO + Debug.Assert(modified); +#endif + } + } + + public void Add(TKey key, TValue value) + { + bool modified = TryInsert(key, value, InsertionBehavior.ThrowOnExisting); +#if !MONO + Debug.Assert(modified); // If there was an existing key and the Add failed, an exception will already have been thrown. +#endif + } + + void ICollection>.Add(KeyValuePair keyValuePair) + { + Add(keyValuePair.Key, keyValuePair.Value); + } + + bool ICollection>.Contains(KeyValuePair keyValuePair) + { + int i = FindEntry(keyValuePair.Key); + if (i >= 0 && EqualityComparer.Default.Equals(_entries[i].value, keyValuePair.Value)) + { + return true; + } + return false; + } + + bool ICollection>.Remove(KeyValuePair keyValuePair) + { + int i = FindEntry(keyValuePair.Key); + if (i >= 0 && EqualityComparer.Default.Equals(_entries[i].value, keyValuePair.Value)) + { + Remove(keyValuePair.Key); + return true; + } + return false; + } + + public void Clear() + { + int count = _count; + if (count > 0) + { + int[] buckets = _buckets; + for (int i = 0; i < buckets.Length; i++) + { + buckets[i] = -1; + } + + _count = 0; + _freeList = -1; + _freeCount = 0; + _version++; + Array.Clear(_entries, 0, count); + } + } + + public bool ContainsKey(TKey key) + { + return FindEntry(key) >= 0; + } + + public bool ContainsValue(TValue value) + { + if (value == null) + { + for (int i = 0; i < _count; i++) + { + if (_entries[i].hashCode >= 0 && _entries[i].value == null) return true; + } + } + else + { + EqualityComparer c = EqualityComparer.Default; + for (int i = 0; i < _count; i++) + { + if (_entries[i].hashCode >= 0 && c.Equals(_entries[i].value, value)) return true; + } + } + return false; + } + + private void CopyTo(KeyValuePair[] array, int index) + { + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + } + + if (index < 0 || index > array.Length) + { + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); + } + + if (array.Length - index < Count) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); + } + + int count = _count; + Entry[] entries = _entries; + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) + { + array[index++] = new KeyValuePair(entries[i].key, entries[i].value); + } + } + } + + public Enumerator GetEnumerator() + { + return new Enumerator(this, Enumerator.KeyValuePair); + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + return new Enumerator(this, Enumerator.KeyValuePair); + } + + public virtual void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info); + } + + info.AddValue(VersionName, _version); + info.AddValue(ComparerName, _comparer, typeof(IEqualityComparer)); + info.AddValue(HashSizeName, _buckets == null ? 0 : _buckets.Length); // This is the length of the bucket array + + if (_buckets != null) + { + var array = new KeyValuePair[Count]; + CopyTo(array, 0); + info.AddValue(KeyValuePairsName, array, typeof(KeyValuePair[])); + } + } + + private int FindEntry(TKey key) + { + if (key == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); + } + + if (_buckets != null) + { + int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF; + for (int i = _buckets[hashCode % _buckets.Length]; i >= 0; i = _entries[i].next) + { + if (_entries[i].hashCode == hashCode && _comparer.Equals(_entries[i].key, key)) return i; + } + } + return -1; + } + + private int Initialize(int capacity) + { + int size = HashHelpers.GetPrime(capacity); + int[] buckets = new int[size]; + for (int i = 0; i < buckets.Length; i++) + { + buckets[i] = -1; + } + + _freeList = -1; + _buckets = buckets; + _entries = new Entry[size]; + + return size; + } + + private bool TryInsert(TKey key, TValue value, InsertionBehavior behavior) + { + if (key == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); + } + + if (_buckets == null) Initialize(0); + int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF; + int targetBucket = hashCode % _buckets.Length; + int collisionCount = 0; + + for (int i = _buckets[targetBucket]; i >= 0; i = _entries[i].next) + { + if (_entries[i].hashCode == hashCode && _comparer.Equals(_entries[i].key, key)) + { + if (behavior == InsertionBehavior.OverwriteExisting) + { + _entries[i].value = value; + _version++; + return true; + } + + if (behavior == InsertionBehavior.ThrowOnExisting) + { + ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(key); + } + + return false; + } + collisionCount++; + } + + int index; + if (_freeCount > 0) + { + index = _freeList; + _freeList = _entries[index].next; + _freeCount--; + } + else + { + if (_count == _entries.Length) + { + Resize(); + targetBucket = hashCode % _buckets.Length; + } + index = _count; + _count++; + } + + _entries[index].hashCode = hashCode; + _entries[index].next = _buckets[targetBucket]; + _entries[index].key = key; + _entries[index].value = value; + _buckets[targetBucket] = index; + _version++; +#if !MONO + // If we hit the collision threshold we'll need to switch to the comparer which is using randomized string hashing + // i.e. EqualityComparer.Default. + + if (collisionCount > HashHelpers.HashCollisionThreshold && _comparer is NonRandomizedStringEqualityComparer) + { + _comparer = (IEqualityComparer)EqualityComparer.Default; + Resize(_entries.Length, true); + } +#endif + return true; + } + + public virtual void OnDeserialization(object sender) + { + SerializationInfo siInfo; + HashHelpers.SerializationInfoTable.TryGetValue(this, out siInfo); + + if (siInfo == null) + { + // We can return immediately if this function is called twice. + // Note we remove the serialization info from the table at the end of this method. + return; + } + + int realVersion = siInfo.GetInt32(VersionName); + int hashsize = siInfo.GetInt32(HashSizeName); + _comparer = (IEqualityComparer)siInfo.GetValue(ComparerName, typeof(IEqualityComparer)); + + if (hashsize != 0) + { + Initialize(hashsize); + + KeyValuePair[] array = (KeyValuePair[]) + siInfo.GetValue(KeyValuePairsName, typeof(KeyValuePair[])); + + if (array == null) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MissingKeys); + } + + for (int i = 0; i < array.Length; i++) + { + if (array[i].Key == null) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_NullKey); + } + Add(array[i].Key, array[i].Value); + } + } + else + { + _buckets = null; + } + + _version = realVersion; + HashHelpers.SerializationInfoTable.Remove(this); + } + + private void Resize() + { + Resize(HashHelpers.ExpandPrime(_count), false); + } + + private void Resize(int newSize, bool forceNewHashCodes) + { +#if !MONO + Debug.Assert(newSize >= _entries.Length); +#endif + + int[] buckets = new int[newSize]; + for (int i = 0; i < buckets.Length; i++) + { + buckets[i] = -1; + } + Entry[] entries = new Entry[newSize]; + + int count = _count; + Array.Copy(_entries, 0, entries, 0, count); + + if (forceNewHashCodes) + { + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode != -1) + { + entries[i].hashCode = (_comparer.GetHashCode(entries[i].key) & 0x7FFFFFFF); + } + } + } + + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) + { + int bucket = entries[i].hashCode % newSize; + entries[i].next = buckets[bucket]; + buckets[bucket] = i; + } + } + + _buckets = buckets; + _entries = entries; + } + + // The overload Remove(TKey key, out TValue value) is a copy of this method with one additional + // statement to copy the value for entry being removed into the output parameter. + // Code has been intentionally duplicated for performance reasons. + public bool Remove(TKey key) + { + if (key == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); + } + + if (_buckets != null) + { + int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF; + int bucket = hashCode % _buckets.Length; + int last = -1; + int i = _buckets[bucket]; + while (i >= 0) + { + ref Entry entry = ref _entries[i]; + + if (entry.hashCode == hashCode && _comparer.Equals(entry.key, key)) + { + if (last < 0) + { + _buckets[bucket] = entry.next; + } + else + { + _entries[last].next = entry.next; + } + entry.hashCode = -1; + entry.next = _freeList; + + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + entry.key = default(TKey); + } + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + entry.value = default(TValue); + } + _freeList = i; + _freeCount++; + _version++; + return true; + } + + last = i; + i = entry.next; + } + } + return false; + } + + // This overload is a copy of the overload Remove(TKey key) with one additional + // statement to copy the value for entry being removed into the output parameter. + // Code has been intentionally duplicated for performance reasons. + public bool Remove(TKey key, out TValue value) + { + if (key == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); + } + + if (_buckets != null) + { + int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF; + int bucket = hashCode % _buckets.Length; + int last = -1; + int i = _buckets[bucket]; + while (i >= 0) + { + ref Entry entry = ref _entries[i]; + + if (entry.hashCode == hashCode && _comparer.Equals(entry.key, key)) + { + if (last < 0) + { + _buckets[bucket] = entry.next; + } + else + { + _entries[last].next = entry.next; + } + + value = entry.value; + + entry.hashCode = -1; + entry.next = _freeList; + + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + entry.key = default(TKey); + } + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + entry.value = default(TValue); + } + _freeList = i; + _freeCount++; + _version++; + return true; + } + + last = i; + i = entry.next; + } + } + value = default(TValue); + return false; + } + + public bool TryGetValue(TKey key, out TValue value) + { + int i = FindEntry(key); + if (i >= 0) + { + value = _entries[i].value; + return true; + } + value = default(TValue); + return false; + } + + public bool TryAdd(TKey key, TValue value) => TryInsert(key, value, InsertionBehavior.None); + + bool ICollection>.IsReadOnly + { + get { return false; } + } + + void ICollection>.CopyTo(KeyValuePair[] array, int index) + { + CopyTo(array, index); + } + + void ICollection.CopyTo(Array array, int index) + { + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + } + + if (array.Rank != 1) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); + } + + if (array.GetLowerBound(0) != 0) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound); + } + + if (index < 0 || index > array.Length) + { + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); + } + + if (array.Length - index < Count) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); + } + + KeyValuePair[] pairs = array as KeyValuePair[]; + if (pairs != null) + { + CopyTo(pairs, index); + } + else if (array is DictionaryEntry[]) + { + DictionaryEntry[] dictEntryArray = array as DictionaryEntry[]; + Entry[] entries = _entries; + for (int i = 0; i < _count; i++) + { + if (entries[i].hashCode >= 0) + { + dictEntryArray[index++] = new DictionaryEntry(entries[i].key, entries[i].value); + } + } + } + else + { + object[] objects = array as object[]; + if (objects == null) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + + try + { + int count = _count; + Entry[] entries = _entries; + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) + { + objects[index++] = new KeyValuePair(entries[i].key, entries[i].value); + } + } + } + catch (ArrayTypeMismatchException) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(this, Enumerator.KeyValuePair); + } + + /// + /// Ensures that the dictionary can hold up to 'capacity' entries without any further expansion of its backing storage + /// + public int EnsureCapacity(int capacity) + { + if (capacity < 0) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity); + if (_entries != null && _entries.Length >= capacity) + return _entries.Length; + if (_buckets == null) + return Initialize(capacity); + int newSize = HashHelpers.GetPrime(capacity); + Resize(newSize, forceNewHashCodes: false); + return newSize; + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get + { + if (_syncRoot == null) + { + System.Threading.Interlocked.CompareExchange(ref _syncRoot, new Object(), null); + } + return _syncRoot; + } + } + + bool IDictionary.IsFixedSize + { + get { return false; } + } + + bool IDictionary.IsReadOnly + { + get { return false; } + } + + ICollection IDictionary.Keys + { + get { return (ICollection)Keys; } + } + + ICollection IDictionary.Values + { + get { return (ICollection)Values; } + } + + object IDictionary.this[object key] + { + get + { + if (IsCompatibleKey(key)) + { + int i = FindEntry((TKey)key); + if (i >= 0) + { + return _entries[i].value; + } + } + return null; + } + set + { + if (key == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); + } + ThrowHelper.IfNullAndNullsAreIllegalThenThrow(value, ExceptionArgument.value); + + try + { + TKey tempKey = (TKey)key; + try + { + this[tempKey] = (TValue)value; + } + catch (InvalidCastException) + { + ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(TValue)); + } + } + catch (InvalidCastException) + { + ThrowHelper.ThrowWrongKeyTypeArgumentException(key, typeof(TKey)); + } + } + } + + private static bool IsCompatibleKey(object key) + { + if (key == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); + } + return (key is TKey); + } + + void IDictionary.Add(object key, object value) + { + if (key == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); + } + ThrowHelper.IfNullAndNullsAreIllegalThenThrow(value, ExceptionArgument.value); + + try + { + TKey tempKey = (TKey)key; + + try + { + Add(tempKey, (TValue)value); + } + catch (InvalidCastException) + { + ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(TValue)); + } + } + catch (InvalidCastException) + { + ThrowHelper.ThrowWrongKeyTypeArgumentException(key, typeof(TKey)); + } + } + + bool IDictionary.Contains(object key) + { + if (IsCompatibleKey(key)) + { + return ContainsKey((TKey)key); + } + + return false; + } + + IDictionaryEnumerator IDictionary.GetEnumerator() + { + return new Enumerator(this, Enumerator.DictEntry); + } + + void IDictionary.Remove(object key) + { + if (IsCompatibleKey(key)) + { + Remove((TKey)key); + } + } + +#if MONO + [Serializable] +#endif + public struct Enumerator : IEnumerator>, + IDictionaryEnumerator + { + private Dictionary _dictionary; + private int _version; + private int _index; + private KeyValuePair _current; + private int _getEnumeratorRetType; // What should Enumerator.Current return? + + internal const int DictEntry = 1; + internal const int KeyValuePair = 2; + + internal Enumerator(Dictionary dictionary, int getEnumeratorRetType) + { + _dictionary = dictionary; + _version = dictionary._version; + _index = 0; + _getEnumeratorRetType = getEnumeratorRetType; + _current = new KeyValuePair(); + } + + public bool MoveNext() + { + if (_version != _dictionary._version) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); + } + + // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends. + // dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue + while ((uint)_index < (uint)_dictionary._count) + { + ref Entry entry = ref _dictionary._entries[_index++]; + + if (entry.hashCode >= 0) + { + _current = new KeyValuePair(entry.key, entry.value); + return true; + } + } + + _index = _dictionary._count + 1; + _current = new KeyValuePair(); + return false; + } + + public KeyValuePair Current + { + get { return _current; } + } + + public void Dispose() + { + } + + object IEnumerator.Current + { + get + { + if (_index == 0 || (_index == _dictionary._count + 1)) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); + } + + if (_getEnumeratorRetType == DictEntry) + { + return new System.Collections.DictionaryEntry(_current.Key, _current.Value); + } + else + { + return new KeyValuePair(_current.Key, _current.Value); + } + } + } + + void IEnumerator.Reset() + { + if (_version != _dictionary._version) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); + } + + _index = 0; + _current = new KeyValuePair(); + } + + DictionaryEntry IDictionaryEnumerator.Entry + { + get + { + if (_index == 0 || (_index == _dictionary._count + 1)) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); + } + + return new DictionaryEntry(_current.Key, _current.Value); + } + } + + object IDictionaryEnumerator.Key + { + get + { + if (_index == 0 || (_index == _dictionary._count + 1)) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); + } + + return _current.Key; + } + } + + object IDictionaryEnumerator.Value + { + get + { + if (_index == 0 || (_index == _dictionary._count + 1)) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); + } + + return _current.Value; + } + } + } + + [DebuggerTypeProxy(typeof(DictionaryKeyCollectionDebugView<,>))] + [DebuggerDisplay("Count = {Count}")] +#if MONO + [Serializable] +#endif + public sealed class KeyCollection : ICollection, ICollection, IReadOnlyCollection + { + private Dictionary _dictionary; + + public KeyCollection(Dictionary dictionary) + { + if (dictionary == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary); + } + _dictionary = dictionary; + } + + public Enumerator GetEnumerator() + { + return new Enumerator(_dictionary); + } + + public void CopyTo(TKey[] array, int index) + { + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + } + + if (index < 0 || index > array.Length) + { + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); + } + + if (array.Length - index < _dictionary.Count) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); + } + + int count = _dictionary._count; + Entry[] entries = _dictionary._entries; + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) array[index++] = entries[i].key; + } + } + + public int Count + { + get { return _dictionary.Count; } + } + + bool ICollection.IsReadOnly + { + get { return true; } + } + + void ICollection.Add(TKey item) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet); + } + + void ICollection.Clear() + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet); + } + + bool ICollection.Contains(TKey item) + { + return _dictionary.ContainsKey(item); + } + + bool ICollection.Remove(TKey item) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet); + return false; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(_dictionary); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(_dictionary); + } + + void ICollection.CopyTo(Array array, int index) + { + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + } + + if (array.Rank != 1) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); + } + + if (array.GetLowerBound(0) != 0) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound); + } + + if (index < 0 || index > array.Length) + { + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); + } + + if (array.Length - index < _dictionary.Count) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); + } + + TKey[] keys = array as TKey[]; + if (keys != null) + { + CopyTo(keys, index); + } + else + { + object[] objects = array as object[]; + if (objects == null) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + + int count = _dictionary._count; + Entry[] entries = _dictionary._entries; + try + { + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) objects[index++] = entries[i].key; + } + } + catch (ArrayTypeMismatchException) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + } + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get { return ((ICollection)_dictionary).SyncRoot; } + } + +#if MONO + [Serializable] +#endif + public struct Enumerator : IEnumerator, System.Collections.IEnumerator + { + private Dictionary _dictionary; + private int _index; + private int _version; + private TKey _currentKey; + + internal Enumerator(Dictionary dictionary) + { + _dictionary = dictionary; + _version = dictionary._version; + _index = 0; + _currentKey = default(TKey); + } + + public void Dispose() + { + } + + public bool MoveNext() + { + if (_version != _dictionary._version) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); + } + + while ((uint)_index < (uint)_dictionary._count) + { + ref Entry entry = ref _dictionary._entries[_index++]; + + if (entry.hashCode >= 0) + { + _currentKey = entry.key; + return true; + } + } + + _index = _dictionary._count + 1; + _currentKey = default(TKey); + return false; + } + + public TKey Current + { + get + { + return _currentKey; + } + } + + object System.Collections.IEnumerator.Current + { + get + { + if (_index == 0 || (_index == _dictionary._count + 1)) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); + } + + return _currentKey; + } + } + + void System.Collections.IEnumerator.Reset() + { + if (_version != _dictionary._version) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); + } + + _index = 0; + _currentKey = default(TKey); + } + } + } + + [DebuggerTypeProxy(typeof(DictionaryValueCollectionDebugView<,>))] + [DebuggerDisplay("Count = {Count}")] +#if MONO + [Serializable] +#endif + public sealed class ValueCollection : ICollection, ICollection, IReadOnlyCollection + { + private Dictionary _dictionary; + + public ValueCollection(Dictionary dictionary) + { + if (dictionary == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary); + } + _dictionary = dictionary; + } + + public Enumerator GetEnumerator() + { + return new Enumerator(_dictionary); + } + + public void CopyTo(TValue[] array, int index) + { + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + } + + if (index < 0 || index > array.Length) + { + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); + } + + if (array.Length - index < _dictionary.Count) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); + } + + int count = _dictionary._count; + Entry[] entries = _dictionary._entries; + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) array[index++] = entries[i].value; + } + } + + public int Count + { + get { return _dictionary.Count; } + } + + bool ICollection.IsReadOnly + { + get { return true; } + } + + void ICollection.Add(TValue item) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet); + } + + bool ICollection.Remove(TValue item) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet); + return false; + } + + void ICollection.Clear() + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet); + } + + bool ICollection.Contains(TValue item) + { + return _dictionary.ContainsValue(item); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(_dictionary); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(_dictionary); + } + + void ICollection.CopyTo(Array array, int index) + { + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + } + + if (array.Rank != 1) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); + } + + if (array.GetLowerBound(0) != 0) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound); + } + + if (index < 0 || index > array.Length) + { + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); + } + + if (array.Length - index < _dictionary.Count) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); + + TValue[] values = array as TValue[]; + if (values != null) + { + CopyTo(values, index); + } + else + { + object[] objects = array as object[]; + if (objects == null) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + + int count = _dictionary._count; + Entry[] entries = _dictionary._entries; + try + { + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) objects[index++] = entries[i].value; + } + } + catch (ArrayTypeMismatchException) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + } + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get { return ((ICollection)_dictionary).SyncRoot; } + } + +#if MONO + [Serializable] +#endif + public struct Enumerator : IEnumerator, System.Collections.IEnumerator + { + private Dictionary _dictionary; + private int _index; + private int _version; + private TValue _currentValue; + + internal Enumerator(Dictionary dictionary) + { + _dictionary = dictionary; + _version = dictionary._version; + _index = 0; + _currentValue = default(TValue); + } + + public void Dispose() + { + } + + public bool MoveNext() + { + if (_version != _dictionary._version) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); + } + + while ((uint)_index < (uint)_dictionary._count) + { + ref Entry entry = ref _dictionary._entries[_index++]; + + if (entry.hashCode >= 0) + { + _currentValue = entry.value; + return true; + } + } + _index = _dictionary._count + 1; + _currentValue = default(TValue); + return false; + } + + public TValue Current + { + get + { + return _currentValue; + } + } + + object System.Collections.IEnumerator.Current + { + get + { + if (_index == 0 || (_index == _dictionary._count + 1)) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); + } + + return _currentValue; + } + } + + void System.Collections.IEnumerator.Reset() + { + if (_version != _dictionary._version) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); + } + _index = 0; + _currentValue = default(TValue); + } + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/ICollection.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/ICollection.cs new file mode 100644 index 0000000000..78ee5cb1f5 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/ICollection.cs @@ -0,0 +1,34 @@ +// 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.Runtime.CompilerServices; + +namespace System.Collections.Generic +{ + // Base interface for all collections, defining enumerators, size, and + // synchronization methods. + public interface ICollection : IEnumerable + { + // Number of items in the collections. + int Count { get; } + + bool IsReadOnly { get; } + + void Add(T item); + + void Clear(); + + bool Contains(T item); + + // CopyTo copies a collection into an Array, starting at a particular + // index into the array. + // + void CopyTo(T[] array, int arrayIndex); + + //void CopyTo(int sourceIndex, T[] destinationArray, int destinationIndex, int count); + + bool Remove(T item); + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/ICollectionDebugView.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/ICollectionDebugView.cs similarity index 100% rename from external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/ICollectionDebugView.cs rename to external/corefx/src/Common/src/CoreLib/System/Collections/Generic/ICollectionDebugView.cs diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IComparer.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IComparer.cs new file mode 100644 index 0000000000..713d499cc8 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IComparer.cs @@ -0,0 +1,20 @@ +// 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; + +namespace System.Collections.Generic +{ + // The generic IComparer interface implements a method that compares + // two objects. It is used in conjunction with the Sort and + // BinarySearch methods on the Array, List, and SortedList classes. + public interface IComparer + { + // Compares two objects. An implementation of this method must return a + // value less than zero if x is less than y, zero if x is equal to y, or a + // value greater than zero if x is greater than y. + // + int Compare(T x, T y); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IDictionary.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IDictionary.cs new file mode 100644 index 0000000000..05677da3b2 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IDictionary.cs @@ -0,0 +1,50 @@ +// 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; + +namespace System.Collections.Generic +{ + // An IDictionary is a possibly unordered set of key-value pairs. + // Keys can be any non-null object. Values can be any object. + // You can look up a value in an IDictionary via the default indexed + // property, Items. + public interface IDictionary : ICollection> + { + // Interfaces are not serializable + // The Item property provides methods to read and edit entries + // in the Dictionary. + TValue this[TKey key] + { + get; + set; + } + + // Returns a collections of the keys in this dictionary. + ICollection Keys + { + get; + } + + // Returns a collections of the values in this dictionary. + ICollection Values + { + get; + } + + // Returns whether this dictionary contains a particular key. + // + bool ContainsKey(TKey key); + + // Adds a key-value pair to the dictionary. + // + void Add(TKey key, TValue value); + + // Removes a particular key from the dictionary. + // + bool Remove(TKey key); + + bool TryGetValue(TKey key, out TValue value); + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/IDictionaryDebugView.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IDictionaryDebugView.cs similarity index 100% rename from external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/IDictionaryDebugView.cs rename to external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IDictionaryDebugView.cs diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IEnumerable.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IEnumerable.cs new file mode 100644 index 0000000000..ddb798e8a6 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IEnumerable.cs @@ -0,0 +1,20 @@ +// 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.Collections; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.Collections.Generic +{ + // Implement this interface if you need to support foreach semantics. + public interface IEnumerable : IEnumerable + { + // Returns an IEnumerator for this enumerable Object. The enumerator provides + // a simple way to access all the contents of a collection. + /// + new IEnumerator GetEnumerator(); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IEnumerator.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IEnumerator.cs new file mode 100644 index 0000000000..6360576974 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IEnumerator.cs @@ -0,0 +1,26 @@ +// 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.Runtime.InteropServices; + +namespace System.Collections.Generic +{ + // Base interface for all generic enumerators, providing a simple approach + // to iterating over a collection. + public interface IEnumerator : IDisposable, IEnumerator + { + // Returns the current element of the enumeration. The returned value is + // undefined before the first call to MoveNext and following a + // call to MoveNext that returned false. Multiple calls to + // GetCurrent with no intervening calls to MoveNext + // will return the same object. + // + /// + new T Current + { + get; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IEqualityComparer.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IEqualityComparer.cs new file mode 100644 index 0000000000..543bdb5fce --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IEqualityComparer.cs @@ -0,0 +1,18 @@ +// 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; + +namespace System.Collections.Generic +{ + // The generic IEqualityComparer interface implements methods to if check two objects are equal + // and generate Hashcode for an object. + // It is use in Dictionary class. + public interface IEqualityComparer + { + bool Equals(T x, T y); + int GetHashCode(T obj); + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IList.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IList.cs new file mode 100644 index 0000000000..2abc7b9142 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IList.cs @@ -0,0 +1,36 @@ +// 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.Collections; +using System.Runtime.CompilerServices; + +namespace System.Collections.Generic +{ + // An IList is an ordered collection of objects. The exact ordering + // is up to the implementation of the list, ranging from a sorted + // order to insertion order. + public interface IList : ICollection + { + // The Item property provides methods to read and edit entries in the List. + T this[int index] + { + get; + set; + } + + // Returns the index of a particular item, if it is in the list. + // Returns -1 if the item isn't in the list. + int IndexOf(T item); + + // Inserts value into the list at position index. + // index must be non-negative and less than or equal to the + // number of elements in the list. If index equals the number + // of items in the list, then value is appended to the end. + void Insert(int index, T item); + + // Removes the item at position index. + void RemoveAt(int index); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IReadOnlyCollection.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IReadOnlyCollection.cs new file mode 100644 index 0000000000..9eea39de22 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IReadOnlyCollection.cs @@ -0,0 +1,15 @@ +// 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.Runtime.CompilerServices; + +namespace System.Collections.Generic +{ + // Provides a read-only, covariant view of a generic list. + public interface IReadOnlyCollection : IEnumerable + { + int Count { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IReadOnlyDictionary.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IReadOnlyDictionary.cs new file mode 100644 index 0000000000..300b996611 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IReadOnlyDictionary.cs @@ -0,0 +1,19 @@ +// 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; + +namespace System.Collections.Generic +{ + // Provides a read-only view of a generic dictionary. + public interface IReadOnlyDictionary : IReadOnlyCollection> + { + bool ContainsKey(TKey key); + bool TryGetValue(TKey key, out TValue value); + + TValue this[TKey key] { get; } + IEnumerable Keys { get; } + IEnumerable Values { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IReadOnlyList.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IReadOnlyList.cs new file mode 100644 index 0000000000..7193805b06 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IReadOnlyList.cs @@ -0,0 +1,15 @@ +// 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.Runtime.CompilerServices; + +namespace System.Collections.Generic +{ + // Provides a read-only, covariant view of a generic list. + public interface IReadOnlyList : IReadOnlyCollection + { + T this[int index] { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/KeyNotFoundException.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/KeyNotFoundException.cs new file mode 100644 index 0000000000..48eddb8745 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/KeyNotFoundException.cs @@ -0,0 +1,36 @@ +// 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.Runtime.Serialization; + +namespace System.Collections.Generic +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class KeyNotFoundException : SystemException + { + public KeyNotFoundException() + : base(SR.Arg_KeyNotFound) + { + HResult = HResults.COR_E_KEYNOTFOUND; + } + + public KeyNotFoundException(String message) + : base(message) + { + HResult = HResults.COR_E_KEYNOTFOUND; + } + + public KeyNotFoundException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_KEYNOTFOUND; + } + + protected KeyNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/KeyValuePair.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/KeyValuePair.cs new file mode 100644 index 0000000000..612d243a2f --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/KeyValuePair.cs @@ -0,0 +1,89 @@ +// 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. + +#if !MONO +using System.ComponentModel; +#endif +using System.Text; + +namespace System.Collections.Generic +{ + // Provides the Create factory method for KeyValuePair. + public static class KeyValuePair + { + // Creates a new KeyValuePair from the given values. + public static KeyValuePair Create(TKey key, TValue value) + { + return new KeyValuePair(key, value); + } + + /// + /// Used by KeyValuePair.ToString to reduce generic code + /// + internal static string PairToString(object key, object value) + { + StringBuilder s = StringBuilderCache.Acquire(); + s.Append('['); + + if (key != null) + { + s.Append(key); + } + + s.Append(", "); + + if (value != null) + { + s.Append(value); + } + + s.Append(']'); + + return StringBuilderCache.GetStringAndRelease(s); + } + } + + // A KeyValuePair holds a key and a value from a dictionary. + // It is used by the IEnumerable implementation for both IDictionary + // and IReadOnlyDictionary. + [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif + public readonly struct KeyValuePair + { + private readonly TKey key; // Do not rename (binary serialization) + private readonly TValue value; // Do not rename (binary serialization) + + public KeyValuePair(TKey key, TValue value) + { + this.key = key; + this.value = value; + } + + public TKey Key + { + get { return key; } + } + + public TValue Value + { + get { return value; } + } + + public override string ToString() + { + return KeyValuePair.PairToString(Key, Value); + } + +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public void Deconstruct(out TKey key, out TValue value) + { + key = Key; + value = Value; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/List.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/List.cs similarity index 72% rename from external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/List.cs rename to external/corefx/src/Common/src/CoreLib/System/Collections/Generic/List.cs index 2c33f91d1f..56bbea895e 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/List.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/List.cs @@ -2,12 +2,9 @@ // 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.Collections.ObjectModel; using System.Diagnostics; -using System.Diagnostics.Contracts; -using System.Runtime; -using System.Runtime.Versioning; +using System.Runtime.CompilerServices; namespace System.Collections.Generic { @@ -20,14 +17,14 @@ namespace System.Collections.Generic [DebuggerTypeProxy(typeof(ICollectionDebugView<>))] [DebuggerDisplay("Count = {Count}")] [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class List : IList, System.Collections.IList, IReadOnlyList { - private const int _defaultCapacity = 4; + private const int DefaultCapacity = 4; - private T[] _items; - [ContractPublicPropertyName("Count")] - private int _size; - private int _version; + private T[] _items; // Do not rename (binary serialization) + private int _size; // Do not rename (binary serialization) + private int _version; // Do not rename (binary serialization) [NonSerialized] private object _syncRoot; @@ -35,7 +32,7 @@ namespace System.Collections.Generic // Constructs a List. The list is initially empty and has a capacity // of zero. Upon adding the first element to the list the capacity is - // increased to _defaultCapacity, and then increased in multiples of two + // increased to DefaultCapacity, and then increased in multiples of two // as required. public List() { @@ -48,8 +45,8 @@ namespace System.Collections.Generic // public List(int capacity) { - if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity), capacity, SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); + if (capacity < 0) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); if (capacity == 0) _items = s_emptyArray; @@ -64,8 +61,7 @@ namespace System.Collections.Generic public List(IEnumerable collection) { if (collection == null) - throw new ArgumentNullException(nameof(collection)); - Contract.EndContractBlock(); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection); ICollection c = collection as ICollection; if (c != null) @@ -86,16 +82,7 @@ namespace System.Collections.Generic { _size = 0; _items = s_emptyArray; - // This enumerable could be empty. Let Add allocate a new array, if needed. - // Note it will also go to _defaultCapacity first, not 1, then 2, etc. - - using (IEnumerator en = collection.GetEnumerator()) - { - while (en.MoveNext()) - { - Add(en.Current); - } - } + AddEnumerable(collection); } } @@ -107,24 +94,25 @@ namespace System.Collections.Generic { get { - Contract.Ensures(Contract.Result() >= 0); return _items.Length; } set { if (value < _size) { - throw new ArgumentOutOfRangeException(nameof(value), value, SR.ArgumentOutOfRange_SmallCapacity); + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_SmallCapacity); } - Contract.EndContractBlock(); if (value != _items.Length) { if (value > 0) { - var items = new T[value]; - Array.Copy(_items, 0, items, 0, _size); - _items = items; + T[] newItems = new T[value]; + if (_size > 0) + { + Array.Copy(_items, 0, newItems, 0, _size); + } + _items = newItems; } else { @@ -139,7 +127,6 @@ namespace System.Collections.Generic { get { - Contract.Ensures(Contract.Result() >= 0); return _size; } } @@ -187,9 +174,8 @@ namespace System.Collections.Generic // Following trick can reduce the range check by one if ((uint)index >= (uint)_size) { - throw new ArgumentOutOfRangeException(); + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); } - Contract.EndContractBlock(); return _items[index]; } @@ -197,9 +183,8 @@ namespace System.Collections.Generic { if ((uint)index >= (uint)_size) { - throw new ArgumentOutOfRangeException(); + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); } - Contract.EndContractBlock(); _items[index] = value; _version++; } @@ -220,8 +205,7 @@ namespace System.Collections.Generic } set { - if (value == null && !(default(T) == null)) - throw new ArgumentNullException(nameof(value)); + ThrowHelper.IfNullAndNullsAreIllegalThenThrow(value, ExceptionArgument.value); try { @@ -229,7 +213,7 @@ namespace System.Collections.Generic } catch (InvalidCastException) { - throw new ArgumentException(SR.Format(SR.Arg_WrongType, value, typeof(T)), nameof(value)); + ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T)); } } } @@ -237,17 +221,37 @@ namespace System.Collections.Generic // Adds the given object to the end of this list. The size of the list is // increased by one. If required, the capacity of the list is doubled // before adding the new element. + // + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Add(T item) { - if (_size == _items.Length) EnsureCapacity(_size + 1); - _items[_size++] = item; + var array = _items; + var size = _size; _version++; + if ((uint)size < (uint)array.Length) + { + _size = size + 1; + array[size] = item; + } + else + { + AddWithResize(item); + } } - int System.Collections.IList.Add(object item) + // Non-inline from List.Add to improve its code quality as uncommon path + [MethodImpl(MethodImplOptions.NoInlining)] + private void AddWithResize(T item) { - if (item == null && !(default(T) == null)) - throw new ArgumentNullException(nameof(item)); + var size = _size; + EnsureCapacity(size + 1); + _size = size + 1; + _items[size] = item; + } + + int System.Collections.IList.Add(Object item) + { + ThrowHelper.IfNullAndNullsAreIllegalThenThrow(item, ExceptionArgument.item); try { @@ -255,7 +259,7 @@ namespace System.Collections.Generic } catch (InvalidCastException) { - throw new ArgumentException(SR.Format(SR.Arg_WrongType, item, typeof(T)), nameof(item)); + ThrowHelper.ThrowWrongValueTypeArgumentException(item, typeof(T)); } return Count - 1; @@ -264,10 +268,9 @@ namespace System.Collections.Generic // Adds the elements of the given collection to the end of this list. If // required, the capacity of the list is increased to twice the previous // capacity or the new size, whichever is larger. + // public void AddRange(IEnumerable collection) { - Contract.Ensures(Count >= Contract.OldValue(Count)); - InsertRange(_size, collection); } @@ -295,47 +298,55 @@ namespace System.Collections.Generic // // The method uses the Array.BinarySearch method to perform the // search. + // public int BinarySearch(int index, int count, T item, IComparer comparer) { if (index < 0) - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_NeedNonNegNum); + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); if (count < 0) - throw new ArgumentOutOfRangeException(nameof(count), count, SR.ArgumentOutOfRange_NeedNonNegNum); + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); if (_size - index < count) - throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.Ensures(Contract.Result() <= index + count); - Contract.EndContractBlock(); + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); return Array.BinarySearch(_items, index, count, item, comparer); } public int BinarySearch(T item) { - Contract.Ensures(Contract.Result() <= Count); return BinarySearch(0, Count, item, null); } public int BinarySearch(T item, IComparer comparer) { - Contract.Ensures(Contract.Result() <= Count); return BinarySearch(0, Count, item, comparer); } // Clears the contents of List. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear() { - if (_size > 0) + if (RuntimeHelpers.IsReferenceOrContainsReferences()) { - Array.Clear(_items, 0, _size); // Don't need to doc this but we clear the elements so that the gc can reclaim the references. + int size = _size; _size = 0; + _version++; + if (size > 0) + { + Array.Clear(_items, 0, size); // Clear the elements so that the gc can reclaim the references. + } + } + else + { + _size = 0; + _version++; } - _version++; } // Contains returns true if the specified element is in the List. // It does a linear, O(n) search. Equality is determined by calling // EqualityComparer.Default.Equals(). + // public bool Contains(T item) { // PERF: IndexOf calls Array.IndexOf, which internally @@ -362,9 +373,8 @@ namespace System.Collections.Generic { if (converter == null) { - throw new ArgumentNullException(nameof(converter)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.converter); } - Contract.EndContractBlock(); List list = new List(_size); for (int i = 0; i < _size; i++) @@ -388,9 +398,8 @@ namespace System.Collections.Generic { if ((array != null) && (array.Rank != 1)) { - throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); } - Contract.EndContractBlock(); try { @@ -399,20 +408,20 @@ namespace System.Collections.Generic } catch (ArrayTypeMismatchException) { - throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); } } // Copies a section of this list to the given array at the given index. // // The method uses the Array.Copy method to copy the elements. + // public void CopyTo(int index, T[] array, int arrayIndex, int count) { if (_size - index < count) { - throw new ArgumentException(SR.Argument_InvalidOffLen); + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); } - Contract.EndContractBlock(); // Delegate rest of error checking to Array.Copy. Array.Copy(_items, index, array, arrayIndex, count); @@ -428,14 +437,15 @@ namespace System.Collections.Generic // value. If the current capacity of the list is less than min, the // capacity is increased to twice the current capacity or to min, // whichever is larger. + // private void EnsureCapacity(int min) { if (_items.Length < min) { - int newCapacity = _items.Length == 0 ? _defaultCapacity : _items.Length * 2; + int newCapacity = _items.Length == 0 ? DefaultCapacity : _items.Length * 2; // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow. // Note that this check works even when _items.Length overflowed thanks to the (uint) cast - //if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength; + if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength; if (newCapacity < min) newCapacity = min; Capacity = newCapacity; } @@ -450,9 +460,8 @@ namespace System.Collections.Generic { if (match == null) { - throw new ArgumentNullException(nameof(match)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); } - Contract.EndContractBlock(); for (int i = 0; i < _size; i++) { @@ -468,9 +477,8 @@ namespace System.Collections.Generic { if (match == null) { - throw new ArgumentNullException(nameof(match)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); } - Contract.EndContractBlock(); List list = new List(); for (int i = 0; i < _size; i++) @@ -485,15 +493,11 @@ namespace System.Collections.Generic public int FindIndex(Predicate match) { - Contract.Ensures(Contract.Result() >= -1); - Contract.Ensures(Contract.Result() < Count); return FindIndex(0, _size, match); } public int FindIndex(int startIndex, Predicate match) { - Contract.Ensures(Contract.Result() >= -1); - Contract.Ensures(Contract.Result() < startIndex + Count); return FindIndex(startIndex, _size - startIndex, match); } @@ -501,21 +505,18 @@ namespace System.Collections.Generic { if ((uint)startIndex > (uint)_size) { - throw new ArgumentOutOfRangeException(nameof(startIndex), startIndex, SR.ArgumentOutOfRange_Index); + ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index(); } if (count < 0 || startIndex > _size - count) { - throw new ArgumentOutOfRangeException(nameof(count), count, SR.ArgumentOutOfRange_Count); + ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count(); } if (match == null) { - throw new ArgumentNullException(nameof(match)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); } - Contract.Ensures(Contract.Result() >= -1); - Contract.Ensures(Contract.Result() < startIndex + count); - Contract.EndContractBlock(); int endIndex = startIndex + count; for (int i = startIndex; i < endIndex; i++) @@ -529,9 +530,8 @@ namespace System.Collections.Generic { if (match == null) { - throw new ArgumentNullException(nameof(match)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); } - Contract.EndContractBlock(); for (int i = _size - 1; i >= 0; i--) { @@ -545,15 +545,11 @@ namespace System.Collections.Generic public int FindLastIndex(Predicate match) { - Contract.Ensures(Contract.Result() >= -1); - Contract.Ensures(Contract.Result() < Count); return FindLastIndex(_size - 1, _size, match); } public int FindLastIndex(int startIndex, Predicate match) { - Contract.Ensures(Contract.Result() >= -1); - Contract.Ensures(Contract.Result() <= startIndex); return FindLastIndex(startIndex, startIndex + 1, match); } @@ -561,29 +557,30 @@ namespace System.Collections.Generic { if (match == null) { - throw new ArgumentNullException(nameof(match)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); } - Contract.Ensures(Contract.Result() >= -1); - Contract.Ensures(Contract.Result() <= startIndex); - Contract.EndContractBlock(); if (_size == 0) { // Special case for 0 length List if (startIndex != -1) { - throw new ArgumentOutOfRangeException(nameof(startIndex), startIndex, SR.ArgumentOutOfRange_Index); + ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index(); } } - else if ((uint)startIndex >= (uint)_size) // Make sure we're not out of range + else { - throw new ArgumentOutOfRangeException(nameof(startIndex), startIndex, SR.ArgumentOutOfRange_Index); + // Make sure we're not out of range + if ((uint)startIndex >= (uint)_size) + { + ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index(); + } } // 2nd have of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0. if (count < 0 || startIndex - count + 1 < 0) { - throw new ArgumentOutOfRangeException(nameof(count), count, SR.ArgumentOutOfRange_Count); + ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count(); } int endIndex = startIndex - count; @@ -601,7 +598,7 @@ namespace System.Collections.Generic { if (action == null) { - throw new ArgumentNullException(nameof(action)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.action); } int version = _version; @@ -616,13 +613,14 @@ namespace System.Collections.Generic } if (version != _version) - throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); } // Returns an enumerator for this list with the given // permission for removal of elements. If modifications made to the list // while an enumeration is in progress, the MoveNext and // GetObject methods of the enumerator will throw an exception. + // public Enumerator GetEnumerator() { return new Enumerator(this); @@ -642,20 +640,18 @@ namespace System.Collections.Generic { if (index < 0) { - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_NeedNonNegNum); + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); } if (count < 0) { - throw new ArgumentOutOfRangeException(nameof(count), count, SR.ArgumentOutOfRange_NeedNonNegNum); + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); } if (_size - index < count) { - throw new ArgumentException(SR.Argument_InvalidOffLen); + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); } - Contract.Ensures(Contract.Result>() != null); - Contract.EndContractBlock(); List list = new List(count); Array.Copy(_items, index, list._items, 0, count); @@ -671,10 +667,9 @@ namespace System.Collections.Generic // // This method uses the Array.IndexOf method to perform the // search. + // public int IndexOf(T item) { - Contract.Ensures(Contract.Result() >= -1); - Contract.Ensures(Contract.Result() < Count); return Array.IndexOf(_items, item, 0, _size); } @@ -695,13 +690,11 @@ namespace System.Collections.Generic // // This method uses the Array.IndexOf method to perform the // search. + // public int IndexOf(T item, int index) { if (index > _size) - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); - Contract.Ensures(Contract.Result() >= -1); - Contract.Ensures(Contract.Result() < Count); - Contract.EndContractBlock(); + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); return Array.IndexOf(_items, item, index, _size - index); } @@ -713,16 +706,14 @@ namespace System.Collections.Generic // // This method uses the Array.IndexOf method to perform the // search. + // public int IndexOf(T item, int index, int count) { if (index > _size) - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); if (count < 0 || index > _size - count) - throw new ArgumentOutOfRangeException(nameof(count), count, SR.ArgumentOutOfRange_Count); - Contract.Ensures(Contract.Result() >= -1); - Contract.Ensures(Contract.Result() < Count); - Contract.EndContractBlock(); + ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count(); return Array.IndexOf(_items, item, index, count); } @@ -730,14 +721,14 @@ namespace System.Collections.Generic // Inserts an element into this list at a given index. The size of the list // is increased by one. If required, the capacity of the list is doubled // before inserting the new element. + // public void Insert(int index, T item) { // Note that insertions at the end are legal. if ((uint)index > (uint)_size) { - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_BiggerThanCollection); + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_ListInsert); } - Contract.EndContractBlock(); if (_size == _items.Length) EnsureCapacity(_size + 1); if (index < _size) { @@ -748,10 +739,9 @@ namespace System.Collections.Generic _version++; } - void System.Collections.IList.Insert(int index, object item) + void System.Collections.IList.Insert(int index, Object item) { - if (item == null && !(default(T) == null)) - throw new ArgumentNullException(nameof(item)); + ThrowHelper.IfNullAndNullsAreIllegalThenThrow(item, ExceptionArgument.item); try { @@ -759,7 +749,7 @@ namespace System.Collections.Generic } catch (InvalidCastException) { - throw new ArgumentException(SR.Format(SR.Arg_WrongType, item, typeof(T)), nameof(item)); + ThrowHelper.ThrowWrongValueTypeArgumentException(item, typeof(T)); } } @@ -767,18 +757,18 @@ namespace System.Collections.Generic // required, the capacity of the list is increased to twice the previous // capacity or the new size, whichever is larger. Ranges may be added // to the end of the list by setting index to the List's size. + // public void InsertRange(int index, IEnumerable collection) { if (collection == null) { - throw new ArgumentNullException(nameof(collection)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection); } if ((uint)index > (uint)_size) { - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); } - Contract.EndContractBlock(); ICollection c = collection as ICollection; if (c != null) @@ -807,8 +797,9 @@ namespace System.Collections.Generic _size += count; } } - else + else if (index < _size) { + // We're inserting a lazy enumerable. Call Insert on each of the constituent items. using (IEnumerator en = collection.GetEnumerator()) { while (en.MoveNext()) @@ -817,6 +808,11 @@ namespace System.Collections.Generic } } } + else + { + // We're adding a lazy enumerable because the index is at the end of this list. + AddEnumerable(collection); + } _version++; } @@ -827,10 +823,9 @@ namespace System.Collections.Generic // // This method uses the Array.LastIndexOf method to perform the // search. + // public int LastIndexOf(T item) { - Contract.Ensures(Contract.Result() >= -1); - Contract.Ensures(Contract.Result() < Count); if (_size == 0) { // Special case for empty list return -1; @@ -849,13 +844,11 @@ namespace System.Collections.Generic // // This method uses the Array.LastIndexOf method to perform the // search. + // public int LastIndexOf(T item, int index) { if (index >= _size) - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); - Contract.Ensures(Contract.Result() >= -1); - Contract.Ensures(((Count == 0) && (Contract.Result() == -1)) || ((Count > 0) && (Contract.Result() <= index))); - Contract.EndContractBlock(); + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); return LastIndexOf(item, index, index + 1); } @@ -867,20 +860,18 @@ namespace System.Collections.Generic // // This method uses the Array.LastIndexOf method to perform the // search. + // public int LastIndexOf(T item, int index, int count) { if ((Count != 0) && (index < 0)) { - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_NeedNonNegNum); + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); } if ((Count != 0) && (count < 0)) { - throw new ArgumentOutOfRangeException(nameof(count), count, SR.ArgumentOutOfRange_NeedNonNegNum); + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); } - Contract.Ensures(Contract.Result() >= -1); - Contract.Ensures(((Count == 0) && (Contract.Result() == -1)) || ((Count > 0) && (Contract.Result() <= index))); - Contract.EndContractBlock(); if (_size == 0) { // Special case for empty list @@ -889,12 +880,12 @@ namespace System.Collections.Generic if (index >= _size) { - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_BiggerThanCollection); } if (count > index + 1) { - throw new ArgumentOutOfRangeException(nameof(count), count, SR.ArgumentOutOfRange_Count); + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_BiggerThanCollection); } return Array.LastIndexOf(_items, item, index, count); @@ -923,16 +914,13 @@ namespace System.Collections.Generic } // This method removes all items which matches the predicate. - // The complexity is O(n). + // The complexity is O(n). public int RemoveAll(Predicate match) { if (match == null) { - throw new ArgumentNullException(nameof(match)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); } - Contract.Ensures(Contract.Result() >= 0); - Contract.Ensures(Contract.Result() <= Contract.OldValue(Count)); - Contract.EndContractBlock(); int freeIndex = 0; // the first free slot in items array @@ -953,7 +941,11 @@ namespace System.Collections.Generic } } - Array.Clear(_items, freeIndex, _size - freeIndex); + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + Array.Clear(_items, freeIndex, _size - freeIndex); // Clear the elements so that the gc can reclaim the references. + } + int result = _size - freeIndex; _size = freeIndex; _version++; @@ -966,15 +958,17 @@ namespace System.Collections.Generic { if ((uint)index >= (uint)_size) { - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); } - Contract.EndContractBlock(); _size--; if (index < _size) { Array.Copy(_items, index + 1, _items, index, _size - index); } - _items[_size] = default(T); + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + _items[_size] = default(T); + } _version++; } @@ -983,17 +977,16 @@ namespace System.Collections.Generic { if (index < 0) { - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_NeedNonNegNum); + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); } if (count < 0) { - throw new ArgumentOutOfRangeException(nameof(count), count, SR.ArgumentOutOfRange_NeedNonNegNum); + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); } if (_size - index < count) - throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.EndContractBlock(); + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); if (count > 0) { @@ -1003,8 +996,12 @@ namespace System.Collections.Generic { Array.Copy(_items, index + count, _items, index, _size - index); } - Array.Clear(_items, _size, count); + _version++; + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + Array.Clear(_items, _size, count); + } } } @@ -1018,36 +1015,25 @@ namespace System.Collections.Generic // method, an element in the range given by index and count // which was previously located at index i will now be located at // index index + (index + count - i - 1). + // public void Reverse(int index, int count) { if (index < 0) { - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_NeedNonNegNum); + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); } if (count < 0) { - throw new ArgumentOutOfRangeException(nameof(count), count, SR.ArgumentOutOfRange_NeedNonNegNum); + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); } if (_size - index < count) - throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.EndContractBlock(); + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); - // The non-generic Array.Reverse is not used because it does not perform - // well for non-primitive value types. - // If/when a generic Array.Reverse becomes available, the below code - // can be deleted and replaced with a call to Array.Reverse. - int i = index; - int j = index + count - 1; - T[] array = _items; - while (i < j) + if (count > 1) { - T temp = array[i]; - array[i] = array[j]; - array[j] = temp; - i++; - j--; + Array.Reverse(_items, index, count); } _version++; } @@ -1073,21 +1059,21 @@ namespace System.Collections.Generic // elements of the list. // // This method uses the Array.Sort method to sort the elements. + // public void Sort(int index, int count, IComparer comparer) { if (index < 0) { - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_NeedNonNegNum); + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); } if (count < 0) { - throw new ArgumentOutOfRangeException(nameof(count), count, SR.ArgumentOutOfRange_NeedNonNegNum); + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); } if (_size - index < count) - throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.EndContractBlock(); + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); if (count > 1) { @@ -1100,9 +1086,8 @@ namespace System.Collections.Generic { if (comparison == null) { - throw new ArgumentNullException(nameof(comparison)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparison); } - Contract.EndContractBlock(); if (_size > 1) { @@ -1111,13 +1096,10 @@ namespace System.Collections.Generic _version++; } - // ToArray returns a new Object array containing the contents of the List. + // ToArray returns an array containing the contents of the List. // This requires copying the List, which is an O(n) operation. public T[] ToArray() { - Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == Count); - if (_size == 0) { return s_emptyArray; @@ -1136,6 +1118,7 @@ namespace System.Collections.Generic // // list.Clear(); // list.TrimExcess(); + // public void TrimExcess() { int threshold = (int)(((double)_items.Length) * 0.9); @@ -1149,9 +1132,8 @@ namespace System.Collections.Generic { if (match == null) { - throw new ArgumentNullException(nameof(match)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); } - Contract.EndContractBlock(); for (int i = 0; i < _size; i++) { @@ -1163,7 +1145,31 @@ namespace System.Collections.Generic return true; } - [Serializable] + private void AddEnumerable(IEnumerable enumerable) + { + Debug.Assert(enumerable != null); + Debug.Assert(!(enumerable is ICollection), "We should have optimized for this beforehand."); + + using (IEnumerator en = enumerable.GetEnumerator()) + { + _version++; // Even if the enumerable has no items, we can update _version. + + while (en.MoveNext()) + { + // Capture Current before doing anything else. If this throws + // an exception, we want to make a clean break. + T current = en.Current; + + if (_size == _items.Length) + { + EnsureCapacity(_size + 1); + } + + _items[_size++] = current; + } + } + } + public struct Enumerator : IEnumerator, System.Collections.IEnumerator { private List _list; @@ -1200,7 +1206,7 @@ namespace System.Collections.Generic { if (_version != _list._version) { - throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); } _index = _list._size + 1; @@ -1222,7 +1228,7 @@ namespace System.Collections.Generic { if (_index == 0 || _index == _list._size + 1) { - throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); } return Current; } @@ -1232,7 +1238,7 @@ namespace System.Collections.Generic { if (_version != _list._version) { - throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); } _index = 0; diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs new file mode 100644 index 0000000000..72f62c2b9a --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs @@ -0,0 +1,38 @@ +// 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.Runtime.Serialization; + +namespace System.Collections.Generic +{ + // NonRandomizedStringEqualityComparer is the comparer used by default with the Dictionary + // We use NonRandomizedStringEqualityComparer as default comparer as it doesnt use the randomized string hashing which + // keeps the performance not affected till we hit collision threshold and then we switch to the comparer which is using + // randomized string hashing. + [Serializable] // Required for compatibility with .NET Core 2.0 as we exposed the NonRandomizedStringEqualityComparer inside the serialization blob +#if CORERT + public +#else + internal +#endif + sealed class NonRandomizedStringEqualityComparer : EqualityComparer, ISerializable + { + internal static new IEqualityComparer Default { get; } = new NonRandomizedStringEqualityComparer(); + + private NonRandomizedStringEqualityComparer() { } + + // This is used by the serialization engine. + private NonRandomizedStringEqualityComparer(SerializationInfo information, StreamingContext context) { } + + public sealed override bool Equals(string x, string y) => string.Equals(x, y); + + public sealed override int GetHashCode(string obj) => obj?.GetLegacyNonRandomizedHashCode() ?? 0; + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + // We are doing this to stay compatible with .NET Framework. + info.SetType(typeof(GenericEqualityComparer)); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/HashHelpers.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/HashHelpers.cs new file mode 100644 index 0000000000..49cff85b58 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/HashHelpers.cs @@ -0,0 +1,108 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; +using System.Threading; + +namespace System.Collections +{ + internal static class HashHelpers + { + public const int HashCollisionThreshold = 100; + + public const int HashPrime = 101; + + // Table of prime numbers to use as hash table sizes. + // A typical resize algorithm would pick the smallest prime number in this array + // that is larger than twice the previous capacity. + // Suppose our Hashtable currently has capacity x and enough elements are added + // such that a resize needs to occur. Resizing first computes 2x then finds the + // first prime in the table greater than 2x, i.e. if primes are ordered + // p_1, p_2, ..., p_i, ..., it finds p_n such that p_n-1 < 2x < p_n. + // Doubling is important for preserving the asymptotic complexity of the + // hashtable operations such as add. Having a prime guarantees that double + // hashing does not lead to infinite loops. IE, your hash function will be + // h1(key) + i*h2(key), 0 <= i < size. h2 and the size must be relatively prime. + public static readonly int[] primes = { + 3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919, + 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, + 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437, + 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263, + 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369}; + + public static bool IsPrime(int candidate) + { + if ((candidate & 1) != 0) + { + int limit = (int)Math.Sqrt(candidate); + for (int divisor = 3; divisor <= limit; divisor += 2) + { + if ((candidate % divisor) == 0) + return false; + } + return true; + } + return (candidate == 2); + } + + public static int GetPrime(int min) + { + if (min < 0) + throw new ArgumentException(SR.Arg_HTCapacityOverflow); + + for (int i = 0; i < primes.Length; i++) + { + int prime = primes[i]; + if (prime >= min) return prime; + } + + //outside of our predefined table. + //compute the hard way. + for (int i = (min | 1); i < Int32.MaxValue; i += 2) + { + if (IsPrime(i) && ((i - 1) % HashPrime != 0)) + return i; + } + return min; + } + + // Returns size of hashtable to grow to. + public static int ExpandPrime(int oldSize) + { + int newSize = 2 * oldSize; + + // Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow. + // Note that this check works even when _items.Length overflowed thanks to the (uint) cast + if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize) + { + Debug.Assert(MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength"); + return MaxPrimeArrayLength; + } + + return GetPrime(newSize); + } + + + // This is the maximum prime smaller than Array.MaxArrayLength + public const int MaxPrimeArrayLength = 0x7FEFFFFD; + + + // Used by Hashtable and Dictionary's SeralizationInfo .ctor's to store the SeralizationInfo + // object until OnDeserialization is called. + private static ConditionalWeakTable s_serializationInfoTable; + + internal static ConditionalWeakTable SerializationInfoTable + { + get + { + if (s_serializationInfoTable == null) + Interlocked.CompareExchange(ref s_serializationInfoTable, new ConditionalWeakTable(), null); + + return s_serializationInfoTable; + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/ICollection.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/ICollection.cs new file mode 100644 index 0000000000..f0149020d2 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/ICollection.cs @@ -0,0 +1,69 @@ +// 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; + +namespace System.Collections +{ + // Base interface for all collections, defining enumerators, size, and + // synchronization methods. + public interface ICollection : IEnumerable + { + // Interfaces are not serialable + // CopyTo copies a collection into an Array, starting at a particular + // index into the array. + // + void CopyTo(Array array, int index); + + // Number of items in the collections. + int Count + { get; } + + + // SyncRoot will return an Object to use for synchronization + // (thread safety). You can use this object in your code to take a + // lock on the collection, even if this collection is a wrapper around + // another collection. The intent is to tunnel through to a real + // implementation of a collection, and use one of the internal objects + // found in that code. + // + // In the absence of a static Synchronized method on a collection, + // the expected usage for SyncRoot would look like this: + // + // ICollection col = ... + // lock (col.SyncRoot) { + // // Some operation on the collection, which is now thread safe. + // // This may include multiple operations. + // } + // + // + // The system-provided collections have a static method called + // Synchronized which will create a thread-safe wrapper around the + // collection. All access to the collection that you want to be + // thread-safe should go through that wrapper collection. However, if + // you need to do multiple calls on that collection (such as retrieving + // two items, or checking the count then doing something), you should + // NOT use our thread-safe wrapper since it only takes a lock for the + // duration of a single method call. Instead, use Monitor.Enter/Exit + // or your language's equivalent to the C# lock keyword as mentioned + // above. + // + // For collections with no publically available underlying store, the + // expected implementation is to simply return the this pointer. Note + // that the this pointer may not be sufficient for collections that + // wrap other collections; those should return the underlying + // collection's SyncRoot property. + Object SyncRoot + { get; } + + // Is this collection synchronized (i.e., thread-safe)? If you want a + // thread-safe collection, you can use SyncRoot as an object to + // synchronize your collection with. If you're using one of the + // collections in System.Collections, you could call the static + // Synchronized method to get a thread-safe wrapper around the + // underlying collection. + bool IsSynchronized + { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/IComparer.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/IComparer.cs new file mode 100644 index 0000000000..cef91c3ffa --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/IComparer.cs @@ -0,0 +1,22 @@ +// 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; + +namespace System.Collections +{ + // The IComparer interface implements a method that compares two objects. It is + // used in conjunction with the Sort and BinarySearch methods on + // the Array and List classes. + // + // Interfaces are not serializable + public interface IComparer + { + // Compares two objects. An implementation of this method must return a + // value less than zero if x is less than y, zero if x is equal to y, or a + // value greater than zero if x is greater than y. + // + int Compare(Object x, Object y); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/IDictionary.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/IDictionary.cs new file mode 100644 index 0000000000..b934c2399c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/IDictionary.cs @@ -0,0 +1,60 @@ +// 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; + +namespace System.Collections +{ + // An IDictionary is a possibly unordered set of key-value pairs. + // Keys can be any non-null object. Values can be any object. + // You can look up a value in an IDictionary via the default indexed + // property, Items. + public interface IDictionary : ICollection + { + // Interfaces are not serializable + // The Item property provides methods to read and edit entries + // in the Dictionary. + Object this[Object key] + { + get; + set; + } + + // Returns a collections of the keys in this dictionary. + ICollection Keys + { + get; + } + + // Returns a collections of the values in this dictionary. + ICollection Values + { + get; + } + + // Returns whether this dictionary contains a particular key. + // + bool Contains(Object key); + + // Adds a key-value pair to the dictionary. + // + void Add(Object key, Object value); + + // Removes all pairs from the dictionary. + void Clear(); + + bool IsReadOnly + { get; } + + bool IsFixedSize + { get; } + + // Returns an IDictionaryEnumerator for this dictionary. + new IDictionaryEnumerator GetEnumerator(); + + // Removes a particular key from the dictionary. + // + void Remove(Object key); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/IDictionaryEnumerator.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/IDictionaryEnumerator.cs new file mode 100644 index 0000000000..451cf68976 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/IDictionaryEnumerator.cs @@ -0,0 +1,68 @@ +// 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. + +namespace System.Collections +{ + // This interface represents an enumerator that allows sequential access to the + // elements of a dictionary. Upon creation, an enumerator is conceptually + // positioned before the first element of the enumeration. The first call to the + // MoveNext method brings the first element of the enumeration into view, + // and each successive call to MoveNext brings the next element into + // view until MoveNext returns false, indicating that there are no more + // elements to enumerate. Following each call to MoveNext, the + // Key and Value methods are used to obtain the key and + // value of the element currently in view. The values returned by calls to + // Key and Value are undefined before the first call to + // MoveNext and following a call to MoveNext that returned false. + // Enumerators are typically used in while loops of the form + // + // IDictionaryEnumerator e = ...; + // while (e.MoveNext()) { + // Object key = e.Key; + // Object value = e.Value; + // ... + // } + // + // The IDictionaryEnumerator interface extends the IEnumerator + // inerface and can thus be used as a regular enumerator. The Current + // method of an IDictionaryEnumerator returns a DictionaryEntry containing + // the current key and value pair. However, the GetEntry method will + // return the same DictionaryEntry and avoids boxing the DictionaryEntry (boxing + // is somewhat expensive). + // + public interface IDictionaryEnumerator : IEnumerator + { + // Returns the key of the current element of the enumeration. The returned + // value is undefined before the first call to GetNext and following + // a call to GetNext that returned false. Multiple calls to + // GetKey with no intervening calls to GetNext will return + // the same object. + // + Object Key + { + get; + } + + // Returns the value of the current element of the enumeration. The + // returned value is undefined before the first call to GetNext and + // following a call to GetNext that returned false. Multiple calls + // to GetValue with no intervening calls to GetNext will + // return the same object. + // + Object Value + { + get; + } + + // GetBlock will copy dictionary values into the given Array. It will either + // fill up the array, or if there aren't enough elements, it will + // copy as much as possible into the Array. The number of elements + // copied is returned. + // + DictionaryEntry Entry + { + get; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/IEnumerable.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/IEnumerable.cs new file mode 100644 index 0000000000..e5edeff266 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/IEnumerable.cs @@ -0,0 +1,16 @@ +// 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.Runtime.InteropServices; + +namespace System.Collections +{ + public interface IEnumerable + { + // Returns an IEnumerator for this enumerable Object. The enumerator provides + // a simple way to access all the contents of a collection. + IEnumerator GetEnumerator(); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/IEnumerator.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/IEnumerator.cs new file mode 100644 index 0000000000..2c2c2e4a97 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/IEnumerator.cs @@ -0,0 +1,41 @@ +// 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.Runtime.InteropServices; + +namespace System.Collections +{ + // Base interface for all enumerators, providing a simple approach + // to iterating over a collection. + public interface IEnumerator + { + // Advances the enumerator to the next element of the enumeration and + // returns a boolean indicating whether an element is available. Upon + // creation, an enumerator is conceptually positioned before the first + // element of the enumeration, and the first call to MoveNext + // brings the first element of the enumeration into view. + // + bool MoveNext(); + + // Returns the current element of the enumeration. The returned value is + // undefined before the first call to MoveNext and following a + // call to MoveNext that returned false. Multiple calls to + // GetCurrent with no intervening calls to MoveNext + // will return the same object. + // + Object Current + { + get; + } + + // Resets the enumerator to the beginning of the enumeration, starting over. + // The preferred behavior for Reset is to return the exact same enumeration. + // This means if you modify the underlying collection then call Reset, your + // IEnumerator will be invalid, just as it would have been if you had called + // MoveNext or Current. + // + void Reset(); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/IEqualityComparer.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/IEqualityComparer.cs new file mode 100644 index 0000000000..35513f577d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/IEqualityComparer.cs @@ -0,0 +1,16 @@ +// 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; + +namespace System.Collections +{ + // An IEqualityComparer is a mechanism to consume custom performant comparison infrastructure + // that can be consumed by some of the common collections. + public interface IEqualityComparer + { + bool Equals(Object x, Object y); + int GetHashCode(Object obj); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/IList.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/IList.cs new file mode 100644 index 0000000000..0110eca1f5 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/IList.cs @@ -0,0 +1,59 @@ +// 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; + +namespace System.Collections +{ + // An IList is an ordered collection of objects. The exact ordering + // is up to the implementation of the list, ranging from a sorted + // order to insertion order. + public interface IList : ICollection + { + // The Item property provides methods to read and edit entries in the List. + Object this[int index] + { + get; + set; + } + + // Adds an item to the list. The exact position in the list is + // implementation-dependent, so while ArrayList may always insert + // in the last available location, a SortedList most likely would not. + // The return value is the position the new element was inserted in. + int Add(Object value); + + // Returns whether the list contains a particular item. + bool Contains(Object value); + + // Removes all items from the list. + void Clear(); + + bool IsReadOnly + { get; } + + + bool IsFixedSize + { + get; + } + + + // Returns the index of a particular item, if it is in the list. + // Returns -1 if the item isn't in the list. + int IndexOf(Object value); + + // Inserts value into the list at position index. + // index must be non-negative and less than or equal to the + // number of elements in the list. If index equals the number + // of items in the list, then value is appended to the end. + void Insert(int index, Object value); + + // Removes an item from the list. + void Remove(Object value); + + // Removes the item at position index. + void RemoveAt(int index); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/IStructuralComparable.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/IStructuralComparable.cs new file mode 100644 index 0000000000..a5e4834b9b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/IStructuralComparable.cs @@ -0,0 +1,13 @@ +// 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; + +namespace System.Collections +{ + public interface IStructuralComparable + { + Int32 CompareTo(Object other, IComparer comparer); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Collections/IStructuralEquatable.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/IStructuralEquatable.cs new file mode 100644 index 0000000000..4e61d5e75f --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/IStructuralEquatable.cs @@ -0,0 +1,12 @@ +// 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. + +namespace System.Collections +{ + public interface IStructuralEquatable + { + Boolean Equals(Object other, IEqualityComparer comparer); + int GetHashCode(IEqualityComparer comparer); + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Collections/LowLevelListDictionary.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/ListDictionaryInternal.cs similarity index 68% rename from external/corert/src/System.Private.CoreLib/src/System/Collections/LowLevelListDictionary.cs rename to external/corefx/src/Common/src/CoreLib/System/Collections/ListDictionaryInternal.cs index 970ad9c111..a8b7a187d9 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Collections/LowLevelListDictionary.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/ListDictionaryInternal.cs @@ -1,28 +1,39 @@ // 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. - /*============================================================ ** +** +** +** +** ** Purpose: List for exceptions. +** ** ===========================================================*/ -using System.Diagnostics.Contracts; namespace System.Collections { /// This is a simple implementation of IDictionary using a singly linked list. This /// will be smaller and faster than a Hashtable if the number of elements is 10 or less. /// This should not be used if performance is important for large numbers of elements. - internal class LowLevelListDictionary : IDictionary + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#if CORERT + public +#else + internal +#endif + class ListDictionaryInternal : IDictionary { - private DictionaryNode _head; - private int _version; - private int _count; + private DictionaryNode head; // Do not rename (binary serialization) + private int version; // Do not rename (binary serialization) + private int count; // Do not rename (binary serialization) + [NonSerialized] private Object _syncRoot; - public LowLevelListDictionary() + public ListDictionaryInternal() { } @@ -34,8 +45,7 @@ namespace System.Collections { throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); } - Contract.EndContractBlock(); - DictionaryNode node = _head; + DictionaryNode node = head; while (node != null) { @@ -53,20 +63,12 @@ namespace System.Collections { throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); } - Contract.EndContractBlock(); -#if FEATURE_SERIALIZATION - if (!key.GetType().IsSerializable) - throw new ArgumentException(Environment.GetResourceString("Argument_NotSerializable"), nameof(key)); - if ((value != null) && (!value.GetType().IsSerializable)) - throw new ArgumentException(Environment.GetResourceString("Argument_NotSerializable"), nameof(value)); -#endif - - _version++; + version++; DictionaryNode last = null; DictionaryNode node; - for (node = _head; node != null; node = node.next) + for (node = head; node != null; node = node.next) { if (node.key.Equals(key)) { @@ -90,9 +92,9 @@ namespace System.Collections } else { - _head = newNode; + head = newNode; } - _count++; + count++; } } @@ -100,7 +102,7 @@ namespace System.Collections { get { - return _count; + return count; } } @@ -162,20 +164,12 @@ namespace System.Collections { throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); } - Contract.EndContractBlock(); -#if FEATURE_SERIALIZATION - if (!key.GetType().IsSerializable) - throw new ArgumentException(Environment.GetResourceString("Argument_NotSerializable"), nameof(key)); - if ((value != null) && (!value.GetType().IsSerializable)) - throw new ArgumentException(Environment.GetResourceString("Argument_NotSerializable"), nameof(value)); -#endif - - _version++; + version++; DictionaryNode last = null; DictionaryNode node; - for (node = _head; node != null; node = node.next) + for (node = head; node != null; node = node.next) { if (node.key.Equals(key)) { @@ -199,16 +193,16 @@ namespace System.Collections } else { - _head = newNode; + head = newNode; } - _count++; + count++; } public void Clear() { - _count = 0; - _head = null; - _version++; + count = 0; + head = null; + version++; } public bool Contains(Object key) @@ -217,8 +211,7 @@ namespace System.Collections { throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); } - Contract.EndContractBlock(); - for (DictionaryNode node = _head; node != null; node = node.next) + for (DictionaryNode node = head; node != null; node = node.next) { if (node.key.Equals(key)) { @@ -241,9 +234,8 @@ namespace System.Collections if (array.Length - index < this.Count) throw new ArgumentException(SR.ArgumentOutOfRange_Index, nameof(index)); - Contract.EndContractBlock(); - for (DictionaryNode node = _head; node != null; node = node.next) + for (DictionaryNode node = head; node != null; node = node.next) { array.SetValue(new DictionaryEntry(node.key, node.value), index); index++; @@ -266,11 +258,10 @@ namespace System.Collections { throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); } - Contract.EndContractBlock(); - _version++; + version++; DictionaryNode last = null; DictionaryNode node; - for (node = _head; node != null; node = node.next) + for (node = head; node != null; node = node.next) { if (node.key.Equals(key)) { @@ -282,31 +273,31 @@ namespace System.Collections { return; } - if (node == _head) + if (node == head) { - _head = node.next; + head = node.next; } else { last.next = node.next; } - _count--; + count--; } private class NodeEnumerator : IDictionaryEnumerator { - private LowLevelListDictionary _list; - private DictionaryNode _current; - private int _version; - private bool _start; + private ListDictionaryInternal list; + private DictionaryNode current; + private int version; + private bool start; - public NodeEnumerator(LowLevelListDictionary list) + public NodeEnumerator(ListDictionaryInternal list) { - _list = list; - _version = list._version; - _start = true; - _current = null; + this.list = list; + version = list.version; + start = true; + current = null; } public Object Current @@ -321,11 +312,11 @@ namespace System.Collections { get { - if (_current == null) + if (current == null) { throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); } - return new DictionaryEntry(_current.key, _current.value); + return new DictionaryEntry(current.key, current.value); } } @@ -333,11 +324,11 @@ namespace System.Collections { get { - if (_current == null) + if (current == null) { throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); } - return _current.key; + return current.key; } } @@ -345,56 +336,56 @@ namespace System.Collections { get { - if (_current == null) + if (current == null) { throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); } - return _current.value; + return current.value; } } public bool MoveNext() { - if (_version != _list._version) + if (version != list.version) { throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); } - if (_start) + if (start) { - _current = _list._head; - _start = false; + current = list.head; + start = false; } else { - if (_current != null) + if (current != null) { - _current = _current.next; + current = current.next; } } - return (_current != null); + return (current != null); } public void Reset() { - if (_version != _list._version) + if (version != list.version) { throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); } - _start = true; - _current = null; + start = true; + current = null; } } private class NodeKeyValueCollection : ICollection { - private LowLevelListDictionary _list; - private bool _isKeys; + private ListDictionaryInternal list; + private bool isKeys; - public NodeKeyValueCollection(LowLevelListDictionary list, bool isKeys) + public NodeKeyValueCollection(ListDictionaryInternal list, bool isKeys) { - _list = list; - _isKeys = isKeys; + this.list = list; + this.isKeys = isKeys; } void ICollection.CopyTo(Array array, int index) @@ -405,12 +396,11 @@ namespace System.Collections throw new ArgumentException(SR.Arg_RankMultiDimNotSupported); if (index < 0) throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); - if (array.Length - index < _list.Count) + if (array.Length - index < list.Count) throw new ArgumentException(SR.ArgumentOutOfRange_Index, nameof(index)); - for (DictionaryNode node = _list._head; node != null; node = node.next) + for (DictionaryNode node = list.head; node != null; node = node.next) { - array.SetValue(_isKeys ? node.key : node.value, index); + array.SetValue(isKeys ? node.key : node.value, index); index++; } } @@ -420,7 +410,7 @@ namespace System.Collections get { int count = 0; - for (DictionaryNode node = _list._head; node != null; node = node.next) + for (DictionaryNode node = list.head; node != null; node = node.next) { count++; } @@ -440,78 +430,79 @@ namespace System.Collections { get { - return _list.SyncRoot; + return list.SyncRoot; } } IEnumerator IEnumerable.GetEnumerator() { - return new NodeKeyValueEnumerator(_list, _isKeys); + return new NodeKeyValueEnumerator(list, isKeys); } private class NodeKeyValueEnumerator : IEnumerator { - private LowLevelListDictionary _list; - private DictionaryNode _current; - private int _version; - private bool _isKeys; - private bool _start; + private ListDictionaryInternal list; + private DictionaryNode current; + private int version; + private bool isKeys; + private bool start; - public NodeKeyValueEnumerator(LowLevelListDictionary list, bool isKeys) + public NodeKeyValueEnumerator(ListDictionaryInternal list, bool isKeys) { - _list = list; - _isKeys = isKeys; - _version = list._version; - _start = true; - _current = null; + this.list = list; + this.isKeys = isKeys; + version = list.version; + start = true; + current = null; } public Object Current { get { - if (_current == null) + if (current == null) { throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); } - return _isKeys ? _current.key : _current.value; + return isKeys ? current.key : current.value; } } public bool MoveNext() { - if (_version != _list._version) + if (version != list.version) { throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); } - if (_start) + if (start) { - _current = _list._head; - _start = false; + current = list.head; + start = false; } else { - if (_current != null) + if (current != null) { - _current = _current.next; + current = current.next; } } - return (_current != null); + return (current != null); } public void Reset() { - if (_version != _list._version) + if (version != list.version) { throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); } - _start = true; - _current = null; + start = true; + current = null; } } } + [Serializable] private class DictionaryNode { public Object key; diff --git a/external/corert/src/System.Private.CoreLib/src/System/Collections/ObjectModel/Collection.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/ObjectModel/Collection.cs similarity index 57% rename from external/corert/src/System.Private.CoreLib/src/System/Collections/ObjectModel/Collection.cs rename to external/corefx/src/Common/src/CoreLib/System/Collections/ObjectModel/Collection.cs index 0e19a5933b..1e1b2c7959 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Collections/ObjectModel/Collection.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/ObjectModel/Collection.cs @@ -2,62 +2,58 @@ // 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.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Runtime; - -using Internal.Runtime.Augments; namespace System.Collections.ObjectModel { - [DebuggerTypeProxy(typeof(Mscorlib_CollectionDebugView<>))] + [Serializable] + [DebuggerTypeProxy(typeof(ICollectionDebugView<>))] [DebuggerDisplay("Count = {Count}")] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class Collection : IList, IList, IReadOnlyList { - private IList _items; + private IList items; // Do not rename (binary serialization) + [NonSerialized] private Object _syncRoot; public Collection() { - // We must implement our backing list using List() as we have store apps that call Collection.Items and cast - // the result to List. - _items = new List(); + items = new List(); } public Collection(IList list) { if (list == null) { - throw new ArgumentNullException(nameof(list)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.list); } - _items = list; + items = list; } public int Count { - get { return _items.Count; } + get { return items.Count; } } protected IList Items { - get { return _items; } + get { return items; } } public T this[int index] { - get { return _items[index]; } + get { return items[index]; } set { - if (_items.IsReadOnly) + if (items.IsReadOnly) { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); } - if (index < 0 || index >= _items.Count) + if (index < 0 || index >= items.Count) { - throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_ListItem); + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); } SetItem(index, value); @@ -66,20 +62,20 @@ namespace System.Collections.ObjectModel public void Add(T item) { - if (_items.IsReadOnly) + if (items.IsReadOnly) { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); } - int index = _items.Count; + int index = items.Count; InsertItem(index, item); } public void Clear() { - if (_items.IsReadOnly) + if (items.IsReadOnly) { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); } ClearItems(); @@ -87,34 +83,34 @@ namespace System.Collections.ObjectModel public void CopyTo(T[] array, int index) { - _items.CopyTo(array, index); + items.CopyTo(array, index); } public bool Contains(T item) { - return _items.Contains(item); + return items.Contains(item); } public IEnumerator GetEnumerator() { - return _items.GetEnumerator(); + return items.GetEnumerator(); } public int IndexOf(T item) { - return _items.IndexOf(item); + return items.IndexOf(item); } public void Insert(int index, T item) { - if (_items.IsReadOnly) + if (items.IsReadOnly) { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); } - if (index < 0 || index > _items.Count) + if (index < 0 || index > items.Count) { - throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_ListInsert); + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_ListInsert); } InsertItem(index, item); @@ -122,12 +118,12 @@ namespace System.Collections.ObjectModel public bool Remove(T item) { - if (_items.IsReadOnly) + if (items.IsReadOnly) { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); } - int index = _items.IndexOf(item); + int index = items.IndexOf(item); if (index < 0) return false; RemoveItem(index); return true; @@ -135,14 +131,14 @@ namespace System.Collections.ObjectModel public void RemoveAt(int index) { - if (_items.IsReadOnly) + if (items.IsReadOnly) { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); } - if (index < 0 || index >= _items.Count) + if (index < 0 || index >= items.Count) { - throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_ListRemoveAt); + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); } RemoveItem(index); @@ -150,35 +146,35 @@ namespace System.Collections.ObjectModel protected virtual void ClearItems() { - _items.Clear(); + items.Clear(); } protected virtual void InsertItem(int index, T item) { - _items.Insert(index, item); + items.Insert(index, item); } protected virtual void RemoveItem(int index) { - _items.RemoveAt(index); + items.RemoveAt(index); } protected virtual void SetItem(int index, T item) { - _items[index] = item; + items[index] = item; } bool ICollection.IsReadOnly { get { - return _items.IsReadOnly; + return items.IsReadOnly; } } IEnumerator IEnumerable.GetEnumerator() { - return ((IEnumerable)_items).GetEnumerator(); + return ((IEnumerable)items).GetEnumerator(); } bool ICollection.IsSynchronized @@ -192,7 +188,7 @@ namespace System.Collections.ObjectModel { if (_syncRoot == null) { - ICollection c = _items as ICollection; + ICollection c = items as ICollection; if (c != null) { _syncRoot = c.SyncRoot; @@ -210,50 +206,48 @@ namespace System.Collections.ObjectModel { if (array == null) { - throw new ArgumentNullException(nameof(array)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); } if (array.Rank != 1) { - throw new ArgumentException(SR.Arg_RankMultiDimNotSupported); + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); } if (array.GetLowerBound(0) != 0) { - throw new ArgumentException(SR.Arg_NonZeroLowerBound); + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound); } if (index < 0) { - throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum); + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); } if (array.Length - index < Count) { - throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); } T[] tArray = array as T[]; if (tArray != null) { - _items.CopyTo(tArray, index); + items.CopyTo(tArray, index); } else { - /* ProjectN port note: IsAssignable no longer available on Type surface area. This is a non-reliable check so we should be able to do without. // // Catch the obvious case assignment will fail. - // We can found all possible problems by doing the check though. + // We can't find all possible problems by doing the check though. // For example, if the element type of the Array is derived from T, // we can't figure out if we can successfully copy the element beforehand. // - IResolvedRuntimeType targetType = array.GetType().GetElementType().ResolvedType; - IResolvedRuntimeType sourceType = typeof(T).ResolvedType; - if(!(targetType.IsAssignableFrom(sourceType) || sourceType.IsAssignableFrom(targetType))) { - throw new ArgumentException(SR.Argument_InvalidArrayType); + Type targetType = array.GetType().GetElementType(); + Type sourceType = typeof(T); + if (!(targetType.IsAssignableFrom(sourceType) || sourceType.IsAssignableFrom(targetType))) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); } - */ - // // We can't cast array of value type to object[], so we don't support @@ -262,33 +256,30 @@ namespace System.Collections.ObjectModel object[] objects = array as object[]; if (objects == null) { - throw new ArgumentException(SR.Argument_InvalidArrayType); + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); } - int count = _items.Count; + int count = items.Count; try { for (int i = 0; i < count; i++) { - objects[index++] = _items[i]; + objects[index++] = items[i]; } } catch (ArrayTypeMismatchException) { - throw new ArgumentException(SR.Argument_InvalidArrayType); + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); } } } object IList.this[int index] { - get { return _items[index]; } + get { return items[index]; } set { - if (value == null && !(default(T) == null)) - { - throw new ArgumentNullException(nameof(value)); - } + ThrowHelper.IfNullAndNullsAreIllegalThenThrow(value, ExceptionArgument.value); try { @@ -296,7 +287,7 @@ namespace System.Collections.ObjectModel } catch (InvalidCastException) { - throw new ArgumentException(SR.Format(SR.Arg_WrongType, value, typeof(T)), nameof(value)); + ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T)); } } } @@ -305,7 +296,7 @@ namespace System.Collections.ObjectModel { get { - return _items.IsReadOnly; + return items.IsReadOnly; } } @@ -317,26 +308,22 @@ namespace System.Collections.ObjectModel // readonly collections are fixed size, if our internal item // collection does not implement IList. Note that Array implements // IList, and therefore T[] and U[] will be fixed-size. - IList list = _items as IList; + IList list = items as IList; if (list != null) { return list.IsFixedSize; } - return _items.IsReadOnly; + return items.IsReadOnly; } } int IList.Add(object value) { - if (_items.IsReadOnly) + if (items.IsReadOnly) { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); - } - - if (value == null && !(default(T) == null)) - { - throw new ArgumentNullException(nameof(value)); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); } + ThrowHelper.IfNullAndNullsAreIllegalThenThrow(value, ExceptionArgument.value); try { @@ -344,7 +331,7 @@ namespace System.Collections.ObjectModel } catch (InvalidCastException) { - throw new ArgumentException(SR.Format(SR.Arg_WrongType, value, typeof(T)), nameof(value)); + ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T)); } return this.Count - 1; @@ -370,14 +357,11 @@ namespace System.Collections.ObjectModel void IList.Insert(int index, object value) { - if (_items.IsReadOnly) + if (items.IsReadOnly) { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); - } - if (value == null && !(default(T) == null)) - { - throw new ArgumentNullException(nameof(value)); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); } + ThrowHelper.IfNullAndNullsAreIllegalThenThrow(value, ExceptionArgument.value); try { @@ -385,15 +369,15 @@ namespace System.Collections.ObjectModel } catch (InvalidCastException) { - throw new ArgumentException(SR.Format(SR.Arg_WrongType, value, typeof(T)), nameof(value)); + ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T)); } } void IList.Remove(object value) { - if (_items.IsReadOnly) + if (items.IsReadOnly) { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); } if (IsCompatibleObject(value)) diff --git a/external/corert/src/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs b/external/corefx/src/Common/src/CoreLib/System/Collections/ObjectModel/ReadOnlyCollection.cs similarity index 66% rename from external/corert/src/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs rename to external/corefx/src/Common/src/CoreLib/System/Collections/ObjectModel/ReadOnlyCollection.cs index 93cd7890ff..dbf88d8b8d 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Collections/ObjectModel/ReadOnlyCollection.cs @@ -2,26 +2,26 @@ // 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.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Runtime; namespace System.Collections.ObjectModel { - [DebuggerTypeProxy(typeof(Mscorlib_CollectionDebugView<>))] + [Serializable] + [DebuggerTypeProxy(typeof(ICollectionDebugView<>))] [DebuggerDisplay("Count = {Count}")] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class ReadOnlyCollection : IList, IList, IReadOnlyList { - private IList list; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private Object _syncRoot; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. + private IList list; // Do not rename (binary serialization) + [NonSerialized] + private Object _syncRoot; public ReadOnlyCollection(IList list) { if (list == null) { - throw new ArgumentNullException(nameof(list)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.list); } this.list = list; } @@ -74,33 +74,34 @@ namespace System.Collections.ObjectModel get { return list[index]; } set { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); } } void ICollection.Add(T value) { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); } void ICollection.Clear() { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); } void IList.Insert(int index, T value) { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); } bool ICollection.Remove(T value) { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + return false; } void IList.RemoveAt(int index) { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); } IEnumerator IEnumerable.GetEnumerator() @@ -137,27 +138,27 @@ namespace System.Collections.ObjectModel { if (array == null) { - throw new ArgumentNullException(nameof(array)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); } if (array.Rank != 1) { - throw new ArgumentException(SR.Arg_RankMultiDimNotSupported); + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); } if (array.GetLowerBound(0) != 0) { - throw new ArgumentException(SR.Arg_NonZeroLowerBound); + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound); } if (index < 0) { - throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum); + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); } if (array.Length - index < Count) { - throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); } T[] items = array as T[]; @@ -167,19 +168,18 @@ namespace System.Collections.ObjectModel } else { - /* ProjectN port note: IsAssignable no longer available on Type surface area. This is a non-reliable check so we should be able to do without. // // Catch the obvious case assignment will fail. - // We can found all possible problems by doing the check though. + // We can't find all possible problems by doing the check though. // For example, if the element type of the Array is derived from T, // we can't figure out if we can successfully copy the element beforehand. // - IResolvedRuntimeType targetType = array.GetType().GetElementType().ResolvedType; - IResolvedRuntimeType sourceType = typeof(T).ResolvedType; - if(!(targetType.IsAssignableFrom(sourceType) || sourceType.IsAssignableFrom(targetType))) { - throw new ArgumentException(SR.Argument_InvalidArrayType); + Type targetType = array.GetType().GetElementType(); + Type sourceType = typeof(T); + if (!(targetType.IsAssignableFrom(sourceType) || sourceType.IsAssignableFrom(targetType))) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); } - */ // // We can't cast array of value type to object[], so we don't support @@ -188,7 +188,7 @@ namespace System.Collections.ObjectModel object[] objects = array as object[]; if (objects == null) { - throw new ArgumentException(SR.Argument_InvalidArrayType); + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); } int count = list.Count; @@ -201,7 +201,7 @@ namespace System.Collections.ObjectModel } catch (ArrayTypeMismatchException) { - throw new ArgumentException(SR.Argument_InvalidArrayType); + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); } } } @@ -221,18 +221,19 @@ namespace System.Collections.ObjectModel get { return list[index]; } set { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); } } int IList.Add(object value) { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + return -1; } void IList.Clear() { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); } private static bool IsCompatibleObject(object value) @@ -262,17 +263,17 @@ namespace System.Collections.ObjectModel void IList.Insert(int index, object value) { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); } void IList.Remove(object value) { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); } void IList.RemoveAt(int index) { - throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); } } } diff --git a/external/corefx/src/Common/src/CoreLib/System/ComponentModel/DefaultValueAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/ComponentModel/DefaultValueAttribute.cs new file mode 100644 index 0000000000..3cdc907297 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ComponentModel/DefaultValueAttribute.cs @@ -0,0 +1,228 @@ +// 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.ComponentModel; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; + +namespace System.ComponentModel +{ + /// + /// Specifies the default value for a property. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes")] + [AttributeUsage(AttributeTargets.All)] + public class DefaultValueAttribute : Attribute + { + /// + /// This is the default value. + /// + private object _value; + + /// + /// Initializes a new instance of the class, converting the + /// specified value to the + /// specified type, and using the U.S. English culture as the + /// translation + /// context. + /// + public DefaultValueAttribute(Type type, string value) + { + // The try/catch here is because attributes should never throw exceptions. We would fail to + // load an otherwise normal class. + try + { + if (type.IsSubclassOf(typeof(Enum))) + { + _value = Enum.Parse(type, value, true); + } + else if (type == typeof(TimeSpan)) + { + _value = TimeSpan.Parse(value); + } + else + { + _value = Convert.ChangeType(value, type, CultureInfo.InvariantCulture); + } + } + catch + { + } + } + + /// + /// Initializes a new instance of the class using a Unicode + /// character. + /// + public DefaultValueAttribute(char value) + { + _value = value; + } + /// + /// Initializes a new instance of the class using an 8-bit unsigned + /// integer. + /// + public DefaultValueAttribute(byte value) + { + _value = value; + } + /// + /// Initializes a new instance of the class using a 16-bit signed + /// integer. + /// + public DefaultValueAttribute(short value) + { + _value = value; + } + /// + /// Initializes a new instance of the class using a 32-bit signed + /// integer. + /// + public DefaultValueAttribute(int value) + { + _value = value; + } + /// + /// Initializes a new instance of the class using a 64-bit signed + /// integer. + /// + public DefaultValueAttribute(long value) + { + _value = value; + } + /// + /// Initializes a new instance of the class using a + /// single-precision floating point + /// number. + /// + public DefaultValueAttribute(float value) + { + _value = value; + } + /// + /// Initializes a new instance of the class using a + /// double-precision floating point + /// number. + /// + public DefaultValueAttribute(double value) + { + _value = value; + } + /// + /// Initializes a new instance of the class using a + /// value. + /// + public DefaultValueAttribute(bool value) + { + _value = value; + } + /// + /// Initializes a new instance of the class using a . + /// + public DefaultValueAttribute(string value) + { + _value = value; + } + + /// + /// Initializes a new instance of the + /// class. + /// + public DefaultValueAttribute(object value) + { + _value = value; + } + + /// + /// Initializes a new instance of the class using a + /// value. + /// + [CLSCompliant(false)] + public DefaultValueAttribute(sbyte value) + { + _value = value; + } + + /// + /// Initializes a new instance of the class using a + /// value. + /// + [CLSCompliant(false)] + public DefaultValueAttribute(ushort value) + { + _value = value; + } + + /// + /// Initializes a new instance of the class using a + /// value. + /// + [CLSCompliant(false)] + public DefaultValueAttribute(uint value) + { + _value = value; + } + + /// + /// Initializes a new instance of the class using a + /// value. + /// + [CLSCompliant(false)] + public DefaultValueAttribute(ulong value) + { + _value = value; + } + + /// + /// + /// Gets the default value of the property this + /// attribute is + /// bound to. + /// + /// + public virtual object Value + { + get + { + return _value; + } + } + + public override bool Equals(object obj) + { + if (obj == this) + { + return true; + } + + DefaultValueAttribute other = obj as DefaultValueAttribute; + + if (other != null) + { + if (Value != null) + { + return Value.Equals(other.Value); + } + else + { + return (other.Value == null); + } + } + return false; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + protected void SetValue(object value) + { + _value = value; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/ComponentModel/EditorBrowsableAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/ComponentModel/EditorBrowsableAttribute.cs new file mode 100644 index 0000000000..9b4d6e626e --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ComponentModel/EditorBrowsableAttribute.cs @@ -0,0 +1,48 @@ +// 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. + +namespace System.ComponentModel +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Delegate | AttributeTargets.Interface)] + public sealed class EditorBrowsableAttribute : Attribute + { + private EditorBrowsableState browsableState; + + public EditorBrowsableAttribute(EditorBrowsableState state) + { + browsableState = state; + } + + public EditorBrowsableAttribute() : this(EditorBrowsableState.Always) { } + + public EditorBrowsableState State + { + get { return browsableState; } + } + + public override bool Equals(object obj) + { + if (obj == this) + { + return true; + } + + EditorBrowsableAttribute other = obj as EditorBrowsableAttribute; + + return (other != null) && other.browsableState == browsableState; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + } + + public enum EditorBrowsableState + { + Always, + Never, + Advanced + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Configuration/Assemblies/AssemblyHashAlgorithm.cs b/external/corefx/src/Common/src/CoreLib/System/Configuration/Assemblies/AssemblyHashAlgorithm.cs new file mode 100644 index 0000000000..aca8da5932 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Configuration/Assemblies/AssemblyHashAlgorithm.cs @@ -0,0 +1,16 @@ +// 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. + +namespace System.Configuration.Assemblies +{ + public enum AssemblyHashAlgorithm + { + None = 0, + MD5 = 0x8003, + SHA1 = 0x8004, + SHA256 = 0x800c, + SHA384 = 0x800d, + SHA512 = 0x800e, + } +} diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystem.Current.MuxWin32WinRT.cs b/external/corefx/src/Common/src/CoreLib/System/Configuration/Assemblies/AssemblyVersionCompatibility.cs similarity index 57% rename from external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystem.Current.MuxWin32WinRT.cs rename to external/corefx/src/Common/src/CoreLib/System/Configuration/Assemblies/AssemblyVersionCompatibility.cs index 1431d1f03d..ef7b3eb45f 100644 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystem.Current.MuxWin32WinRT.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Configuration/Assemblies/AssemblyVersionCompatibility.cs @@ -2,10 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace System.IO +namespace System.Configuration.Assemblies { - internal abstract partial class FileSystem + public enum AssemblyVersionCompatibility { - private static readonly FileSystem s_current = new MultiplexingWin32WinRTFileSystem(); + SameMachine = 1, + SameProcess = 2, + SameDomain = 3, } } diff --git a/external/corefx/src/Common/src/CoreLib/System/Convert.cs.REMOVED.git-id b/external/corefx/src/Common/src/CoreLib/System/Convert.cs.REMOVED.git-id new file mode 100644 index 0000000000..72b7d96440 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Convert.cs.REMOVED.git-id @@ -0,0 +1 @@ +488ea773387e0c95bf1e04d9917242c88b9dcbbe \ No newline at end of file diff --git a/external/corefx/src/Common/src/CoreLib/System/CurrentSystemTimeZone.cs b/external/corefx/src/Common/src/CoreLib/System/CurrentSystemTimeZone.cs new file mode 100644 index 0000000000..bab6a92bf5 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/CurrentSystemTimeZone.cs @@ -0,0 +1,197 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: +** This class represents the current system timezone. It is +** the only meaningful implementation of the TimeZone class +** available in this version. +** +** The only TimeZone that we support in version 1 is the +** CurrentTimeZone as determined by the system timezone. +** +** +============================================================*/ + +using System; +using System.Text; +using System.Collections; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; + +namespace System +{ + [Obsolete("System.CurrentSystemTimeZone has been deprecated. Please investigate the use of System.TimeZoneInfo.Local instead.")] + internal partial class CurrentSystemTimeZone : TimeZone + { + // Standard offset in ticks to the Universal time if + // no daylight saving is in used. + // E.g. the offset for PST (Pacific Standard time) should be -8 * 60 * 60 * 1000 * 10000. + // (1 millisecond = 10000 ticks) + private long m_ticksOffset; + private String m_standardName; + private String m_daylightName; + + internal CurrentSystemTimeZone() + { + TimeZoneInfo local = TimeZoneInfo.Local; + + m_ticksOffset = local.BaseUtcOffset.Ticks; + m_standardName = local.StandardName; + m_daylightName = local.DaylightName; + } + + public override String StandardName + { + get + { + return m_standardName; + } + } + + public override String DaylightName + { + get + { + return m_daylightName; + } + } + + internal long GetUtcOffsetFromUniversalTime(DateTime time, ref Boolean isAmbiguousLocalDst) + { + // Get the daylight changes for the year of the specified time. + TimeSpan offset = new TimeSpan(m_ticksOffset); + DaylightTime daylightTime = GetDaylightChanges(time.Year); + isAmbiguousLocalDst = false; + + if (daylightTime == null || daylightTime.Delta.Ticks == 0) + { + return offset.Ticks; + } + + // The start and end times represent the range of universal times that are in DST for that year. + // Within that there is an ambiguous hour, usually right at the end, but at the beginning in + // the unusual case of a negative daylight savings delta. + DateTime startTime = daylightTime.Start - offset; + DateTime endTime = daylightTime.End - offset - daylightTime.Delta; + DateTime ambiguousStart; + DateTime ambiguousEnd; + + if (daylightTime.Delta.Ticks > 0) + { + ambiguousStart = endTime - daylightTime.Delta; + ambiguousEnd = endTime; + } + else + { + ambiguousStart = startTime; + ambiguousEnd = startTime - daylightTime.Delta; + } + + Boolean isDst = false; + if (startTime > endTime) + { + // In southern hemisphere, the daylight saving time starts later in the year, and ends in the beginning of next year. + // Note, the summer in the southern hemisphere begins late in the year. + isDst = (time < endTime || time >= startTime); + } + else + { + // In northern hemisphere, the daylight saving time starts in the middle of the year. + isDst = (time >= startTime && time < endTime); + } + + if (isDst) + { + offset += daylightTime.Delta; + + // See if the resulting local time becomes ambiguous. This must be captured here or the + // DateTime will not be able to round-trip back to UTC accurately. + if (time >= ambiguousStart && time < ambiguousEnd) + { + isAmbiguousLocalDst = true; + } + } + return offset.Ticks; + } + + public override DateTime ToLocalTime(DateTime time) + { + if (time.Kind == DateTimeKind.Local) + { + return time; + } + Boolean isAmbiguousLocalDst = false; + Int64 offset = GetUtcOffsetFromUniversalTime(time, ref isAmbiguousLocalDst); + long tick = time.Ticks + offset; + if (tick > DateTime.MaxTicks) + { + return new DateTime(DateTime.MaxTicks, DateTimeKind.Local); + } + if (tick < DateTime.MinTicks) + { + return new DateTime(DateTime.MinTicks, DateTimeKind.Local); + } + return new DateTime(tick, DateTimeKind.Local, isAmbiguousLocalDst); + } + + public override DaylightTime GetDaylightChanges(int year) + { + if (year < 1 || year > 9999) + { + throw new ArgumentOutOfRangeException(nameof(year), SR.Format(SR.ArgumentOutOfRange_Range, 1, 9999)); + } + + return GetCachedDaylightChanges(year); + } + + private static DaylightTime CreateDaylightChanges(int year) + { + DaylightTime currentDaylightChanges = null; + + if (TimeZoneInfo.Local.SupportsDaylightSavingTime) + { + DateTime start; + DateTime end; + TimeSpan delta; + + foreach (var rule in TimeZoneInfo.Local.GetAdjustmentRules()) + { + if (rule.DateStart.Year <= year && rule.DateEnd.Year >= year && rule.DaylightDelta != TimeSpan.Zero) + { + start = TimeZoneInfo.TransitionTimeToDateTime(year, rule.DaylightTransitionStart); + end = TimeZoneInfo.TransitionTimeToDateTime(year, rule.DaylightTransitionEnd); + delta = rule.DaylightDelta; + + currentDaylightChanges = new DaylightTime(start, end, delta); + break; + } + } + } + + if (currentDaylightChanges == null) + { + currentDaylightChanges = new DaylightTime(DateTime.MinValue, DateTime.MinValue, TimeSpan.Zero); + } + + return currentDaylightChanges; + } + + public override TimeSpan GetUtcOffset(DateTime time) + { + if (time.Kind == DateTimeKind.Utc) + { + return TimeSpan.Zero; + } + else + { + return new TimeSpan(TimeZone.CalculateUtcOffset(time, GetDaylightChanges(time.Year)).Ticks + m_ticksOffset); + } + } + } // class CurrentSystemTimeZone +} diff --git a/external/corefx/src/Common/src/CoreLib/System/DBNull.cs b/external/corefx/src/Common/src/CoreLib/System/DBNull.cs new file mode 100644 index 0000000000..3cee2b15c8 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/DBNull.cs @@ -0,0 +1,119 @@ +// 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.Runtime.Serialization; + +namespace System +{ + [Serializable] + public sealed class DBNull : ISerializable, IConvertible + { + private DBNull() + { + } + + private DBNull(SerializationInfo info, StreamingContext context) + { + throw new NotSupportedException(SR.NotSupported_DBNullSerial); + } + + public static readonly DBNull Value = new DBNull(); + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + UnitySerializationHolder.GetUnitySerializationInfo(info, UnitySerializationHolder.NullUnity); + } + + public override string ToString() + { + return string.Empty; + } + + public string ToString(IFormatProvider provider) + { + return string.Empty; + } + + public TypeCode GetTypeCode() + { + return TypeCode.DBNull; + } + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + decimal IConvertible.ToDecimal(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/DataMisalignedException.cs b/external/corefx/src/Common/src/CoreLib/System/DataMisalignedException.cs new file mode 100644 index 0000000000..2a245b6ef7 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/DataMisalignedException.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. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** Purpose: The exception class for a misaligned access exception +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class DataMisalignedException : SystemException + { + public DataMisalignedException() + : base(SR.Arg_DataMisalignedException) + { + HResult = HResults.COR_E_DATAMISALIGNED; + } + + public DataMisalignedException(String message) + : base(message) + { + HResult = HResults.COR_E_DATAMISALIGNED; + } + + public DataMisalignedException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_DATAMISALIGNED; + } + + internal DataMisalignedException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/DateTime.cs b/external/corefx/src/Common/src/CoreLib/System/DateTime.cs new file mode 100644 index 0000000000..b5deefa94a --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/DateTime.cs @@ -0,0 +1,1609 @@ +// 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.Diagnostics; +using System.Threading; +using System.Globalization; +using System.Runtime; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; +using System.Runtime.Versioning; +using System.Security; +using CultureInfo = System.Globalization.CultureInfo; +using Calendar = System.Globalization.Calendar; + +namespace System +{ + // This value type represents a date and time. Every DateTime + // object has a private field (Ticks) of type Int64 that stores the + // date and time as the number of 100 nanosecond intervals since + // 12:00 AM January 1, year 1 A.D. in the proleptic Gregorian Calendar. + // + // Starting from V2.0, DateTime also stored some context about its time + // zone in the form of a 3-state value representing Unspecified, Utc or + // Local. This is stored in the two top bits of the 64-bit numeric value + // with the remainder of the bits storing the tick count. This information + // is only used during time zone conversions and is not part of the + // identity of the DateTime. Thus, operations like Compare and Equals + // ignore this state. This is to stay compatible with earlier behavior + // and performance characteristics and to avoid forcing people into dealing + // with the effects of daylight savings. Note, that this has little effect + // on how the DateTime works except in a context where its specific time + // zone is needed, such as during conversions and some parsing and formatting + // cases. + // + // There is also 4th state stored that is a special type of Local value that + // is used to avoid data loss when round-tripping between local and UTC time. + // See below for more information on this 4th state, although it is + // effectively hidden from most users, who just see the 3-state DateTimeKind + // enumeration. + // + // For compatibility, DateTime does not serialize the Kind data when used in + // binary serialization. + // + // For a description of various calendar issues, look at + // + // Calendar Studies web site, at + // http://serendipity.nofadz.com/hermetic/cal_stud.htm. + // + // + [StructLayout(LayoutKind.Auto)] + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public readonly partial struct DateTime : IComparable, IFormattable, IConvertible, IComparable, IEquatable, ISerializable, ISpanFormattable + { + // Number of 100ns ticks per time unit + private const long TicksPerMillisecond = 10000; + private const long TicksPerSecond = TicksPerMillisecond * 1000; + private const long TicksPerMinute = TicksPerSecond * 60; + private const long TicksPerHour = TicksPerMinute * 60; + private const long TicksPerDay = TicksPerHour * 24; + + // Number of milliseconds per time unit + private const int MillisPerSecond = 1000; + private const int MillisPerMinute = MillisPerSecond * 60; + private const int MillisPerHour = MillisPerMinute * 60; + private const int MillisPerDay = MillisPerHour * 24; + + // Number of days in a non-leap year + private const int DaysPerYear = 365; + // Number of days in 4 years + private const int DaysPer4Years = DaysPerYear * 4 + 1; // 1461 + // Number of days in 100 years + private const int DaysPer100Years = DaysPer4Years * 25 - 1; // 36524 + // Number of days in 400 years + private const int DaysPer400Years = DaysPer100Years * 4 + 1; // 146097 + + // Number of days from 1/1/0001 to 12/31/1600 + private const int DaysTo1601 = DaysPer400Years * 4; // 584388 + // Number of days from 1/1/0001 to 12/30/1899 + private const int DaysTo1899 = DaysPer400Years * 4 + DaysPer100Years * 3 - 367; + // Number of days from 1/1/0001 to 12/31/1969 + internal const int DaysTo1970 = DaysPer400Years * 4 + DaysPer100Years * 3 + DaysPer4Years * 17 + DaysPerYear; // 719,162 + // Number of days from 1/1/0001 to 12/31/9999 + private const int DaysTo10000 = DaysPer400Years * 25 - 366; // 3652059 + + internal const long MinTicks = 0; + internal const long MaxTicks = DaysTo10000 * TicksPerDay - 1; + private const long MaxMillis = (long)DaysTo10000 * MillisPerDay; + + internal const long UnixEpochTicks = DaysTo1970 * TicksPerDay; + private const long FileTimeOffset = DaysTo1601 * TicksPerDay; + private const long DoubleDateOffset = DaysTo1899 * TicksPerDay; + // The minimum OA date is 0100/01/01 (Note it's year 100). + // The maximum OA date is 9999/12/31 + private const long OADateMinAsTicks = (DaysPer100Years - DaysPerYear) * TicksPerDay; + // All OA dates must be greater than (not >=) OADateMinAsDouble + private const double OADateMinAsDouble = -657435.0; + // All OA dates must be less than (not <=) OADateMaxAsDouble + private const double OADateMaxAsDouble = 2958466.0; + + private const int DatePartYear = 0; + private const int DatePartDayOfYear = 1; + private const int DatePartMonth = 2; + private const int DatePartDay = 3; + + private static readonly int[] s_daysToMonth365 = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}; + private static readonly int[] s_daysToMonth366 = { + 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}; + + public static readonly DateTime MinValue = new DateTime(MinTicks, DateTimeKind.Unspecified); + public static readonly DateTime MaxValue = new DateTime(MaxTicks, DateTimeKind.Unspecified); + public static readonly DateTime UnixEpoch = new DateTime(UnixEpochTicks, DateTimeKind.Utc); + + private const UInt64 TicksMask = 0x3FFFFFFFFFFFFFFF; + private const UInt64 FlagsMask = 0xC000000000000000; + private const UInt64 LocalMask = 0x8000000000000000; + private const Int64 TicksCeiling = 0x4000000000000000; + private const UInt64 KindUnspecified = 0x0000000000000000; + private const UInt64 KindUtc = 0x4000000000000000; + private const UInt64 KindLocal = 0x8000000000000000; + private const UInt64 KindLocalAmbiguousDst = 0xC000000000000000; + private const Int32 KindShift = 62; + + private const String TicksField = "ticks"; // Do not rename (binary serialization) + private const String DateDataField = "dateData"; // Do not rename (binary serialization) + + // The data is stored as an unsigned 64-bit integer + // Bits 01-62: The value of 100-nanosecond ticks where 0 represents 1/1/0001 12:00am, up until the value + // 12/31/9999 23:59:59.9999999 + // Bits 63-64: A four-state value that describes the DateTimeKind value of the date time, with a 2nd + // value for the rare case where the date time is local, but is in an overlapped daylight + // savings time hour and it is in daylight savings time. This allows distinction of these + // otherwise ambiguous local times and prevents data loss when round tripping from Local to + // UTC time. + private readonly UInt64 _dateData; + + // Constructs a DateTime from a tick count. The ticks + // argument specifies the date as the number of 100-nanosecond intervals + // that have elapsed since 1/1/0001 12:00am. + // + public DateTime(long ticks) + { + if (ticks < MinTicks || ticks > MaxTicks) + throw new ArgumentOutOfRangeException(nameof(ticks), SR.ArgumentOutOfRange_DateTimeBadTicks); + _dateData = (UInt64)ticks; + } + + private DateTime(UInt64 dateData) + { + this._dateData = dateData; + } + + public DateTime(long ticks, DateTimeKind kind) + { + if (ticks < MinTicks || ticks > MaxTicks) + { + throw new ArgumentOutOfRangeException(nameof(ticks), SR.ArgumentOutOfRange_DateTimeBadTicks); + } + if (kind < DateTimeKind.Unspecified || kind > DateTimeKind.Local) + { + throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind)); + } + _dateData = ((UInt64)ticks | ((UInt64)kind << KindShift)); + } + + internal DateTime(long ticks, DateTimeKind kind, Boolean isAmbiguousDst) + { + if (ticks < MinTicks || ticks > MaxTicks) + { + throw new ArgumentOutOfRangeException(nameof(ticks), SR.ArgumentOutOfRange_DateTimeBadTicks); + } + Debug.Assert(kind == DateTimeKind.Local, "Internal Constructor is for local times only"); + _dateData = ((UInt64)ticks | (isAmbiguousDst ? KindLocalAmbiguousDst : KindLocal)); + } + + // Constructs a DateTime from a given year, month, and day. The + // time-of-day of the resulting DateTime is always midnight. + // + public DateTime(int year, int month, int day) + { + _dateData = (UInt64)DateToTicks(year, month, day); + } + + // Constructs a DateTime from a given year, month, and day for + // the specified calendar. The + // time-of-day of the resulting DateTime is always midnight. + // + public DateTime(int year, int month, int day, Calendar calendar) + : this(year, month, day, 0, 0, 0, calendar) + { + } + + // Constructs a DateTime from a given year, month, day, hour, + // minute, and second. + // + public DateTime(int year, int month, int day, int hour, int minute, int second) + { + _dateData = (UInt64)(DateToTicks(year, month, day) + TimeToTicks(hour, minute, second)); + } + + public DateTime(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind) + { + if (kind < DateTimeKind.Unspecified || kind > DateTimeKind.Local) + { + throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind)); + } + Int64 ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second); + _dateData = ((UInt64)ticks | ((UInt64)kind << KindShift)); + } + + // Constructs a DateTime from a given year, month, day, hour, + // minute, and second for the specified calendar. + // + public DateTime(int year, int month, int day, int hour, int minute, int second, Calendar calendar) + { + if (calendar == null) + throw new ArgumentNullException(nameof(calendar)); + _dateData = (UInt64)calendar.ToDateTime(year, month, day, hour, minute, second, 0).Ticks; + } + + // Constructs a DateTime from a given year, month, day, hour, + // minute, and second. + // + public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond) + { + if (millisecond < 0 || millisecond >= MillisPerSecond) + { + throw new ArgumentOutOfRangeException(nameof(millisecond), SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1)); + } + Int64 ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second); + ticks += millisecond * TicksPerMillisecond; + if (ticks < MinTicks || ticks > MaxTicks) + throw new ArgumentException(SR.Arg_DateTimeRange); + _dateData = (UInt64)ticks; + } + + public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind) + { + if (millisecond < 0 || millisecond >= MillisPerSecond) + { + throw new ArgumentOutOfRangeException(nameof(millisecond), SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1)); + } + if (kind < DateTimeKind.Unspecified || kind > DateTimeKind.Local) + { + throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind)); + } + Int64 ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second); + ticks += millisecond * TicksPerMillisecond; + if (ticks < MinTicks || ticks > MaxTicks) + throw new ArgumentException(SR.Arg_DateTimeRange); + _dateData = ((UInt64)ticks | ((UInt64)kind << KindShift)); + } + + // Constructs a DateTime from a given year, month, day, hour, + // minute, and second for the specified calendar. + // + public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar) + { + if (calendar == null) + throw new ArgumentNullException(nameof(calendar)); + if (millisecond < 0 || millisecond >= MillisPerSecond) + { + throw new ArgumentOutOfRangeException(nameof(millisecond), SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1)); + } + Int64 ticks = calendar.ToDateTime(year, month, day, hour, minute, second, 0).Ticks; + ticks += millisecond * TicksPerMillisecond; + if (ticks < MinTicks || ticks > MaxTicks) + throw new ArgumentException(SR.Arg_DateTimeRange); + _dateData = (UInt64)ticks; + } + + public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, DateTimeKind kind) + { + if (calendar == null) + throw new ArgumentNullException(nameof(calendar)); + if (millisecond < 0 || millisecond >= MillisPerSecond) + { + throw new ArgumentOutOfRangeException(nameof(millisecond), SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1)); + } + if (kind < DateTimeKind.Unspecified || kind > DateTimeKind.Local) + { + throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind)); + } + Int64 ticks = calendar.ToDateTime(year, month, day, hour, minute, second, 0).Ticks; + ticks += millisecond * TicksPerMillisecond; + if (ticks < MinTicks || ticks > MaxTicks) + throw new ArgumentException(SR.Arg_DateTimeRange); + _dateData = ((UInt64)ticks | ((UInt64)kind << KindShift)); + } + + private DateTime(SerializationInfo info, StreamingContext context) + { + if (info == null) + throw new ArgumentNullException(nameof(info)); + + Boolean foundTicks = false; + Boolean foundDateData = false; + Int64 serializedTicks = 0; + UInt64 serializedDateData = 0; + + + // Get the data + SerializationInfoEnumerator enumerator = info.GetEnumerator(); + while (enumerator.MoveNext()) + { + switch (enumerator.Name) + { + case TicksField: + serializedTicks = Convert.ToInt64(enumerator.Value, CultureInfo.InvariantCulture); + foundTicks = true; + break; + case DateDataField: + serializedDateData = Convert.ToUInt64(enumerator.Value, CultureInfo.InvariantCulture); + foundDateData = true; + break; + default: + // Ignore other fields for forward compatibility. + break; + } + } + if (foundDateData) + { + _dateData = serializedDateData; + } + else if (foundTicks) + { + _dateData = (UInt64)serializedTicks; + } + else + { + throw new SerializationException(SR.Serialization_MissingDateTimeData); + } + Int64 ticks = InternalTicks; + if (ticks < MinTicks || ticks > MaxTicks) + { + throw new SerializationException(SR.Serialization_DateTimeTicksOutOfRange); + } + } + + + + internal Int64 InternalTicks + { + get + { + return (Int64)(_dateData & TicksMask); + } + } + + private UInt64 InternalKind + { + get + { + return (_dateData & FlagsMask); + } + } + + // Returns the DateTime resulting from adding the given + // TimeSpan to this DateTime. + // + public DateTime Add(TimeSpan value) + { + return AddTicks(value._ticks); + } + + // Returns the DateTime resulting from adding a fractional number of + // time units to this DateTime. + private DateTime Add(double value, int scale) + { + long millis = (long)(value * scale + (value >= 0 ? 0.5 : -0.5)); + if (millis <= -MaxMillis || millis >= MaxMillis) + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_AddValue); + return AddTicks(millis * TicksPerMillisecond); + } + + // Returns the DateTime resulting from adding a fractional number of + // days to this DateTime. The result is computed by rounding the + // fractional number of days given by value to the nearest + // millisecond, and adding that interval to this DateTime. The + // value argument is permitted to be negative. + // + public DateTime AddDays(double value) + { + return Add(value, MillisPerDay); + } + + // Returns the DateTime resulting from adding a fractional number of + // hours to this DateTime. The result is computed by rounding the + // fractional number of hours given by value to the nearest + // millisecond, and adding that interval to this DateTime. The + // value argument is permitted to be negative. + // + public DateTime AddHours(double value) + { + return Add(value, MillisPerHour); + } + + // Returns the DateTime resulting from the given number of + // milliseconds to this DateTime. The result is computed by rounding + // the number of milliseconds given by value to the nearest integer, + // and adding that interval to this DateTime. The value + // argument is permitted to be negative. + // + public DateTime AddMilliseconds(double value) + { + return Add(value, 1); + } + + // Returns the DateTime resulting from adding a fractional number of + // minutes to this DateTime. The result is computed by rounding the + // fractional number of minutes given by value to the nearest + // millisecond, and adding that interval to this DateTime. The + // value argument is permitted to be negative. + // + public DateTime AddMinutes(double value) + { + return Add(value, MillisPerMinute); + } + + // Returns the DateTime resulting from adding the given number of + // months to this DateTime. The result is computed by incrementing + // (or decrementing) the year and month parts of this DateTime by + // months months, and, if required, adjusting the day part of the + // resulting date downwards to the last day of the resulting month in the + // resulting year. The time-of-day part of the result is the same as the + // time-of-day part of this DateTime. + // + // In more precise terms, considering this DateTime to be of the + // form y / m / d + t, where y is the + // year, m is the month, d is the day, and t is the + // time-of-day, the result is y1 / m1 / d1 + t, + // where y1 and m1 are computed by adding months months + // to y and m, and d1 is the largest value less than + // or equal to d that denotes a valid day in month m1 of year + // y1. + // + public DateTime AddMonths(int months) + { + if (months < -120000 || months > 120000) throw new ArgumentOutOfRangeException(nameof(months), SR.ArgumentOutOfRange_DateTimeBadMonths); + GetDatePart(out int y, out int m, out int d); + int i = m - 1 + months; + if (i >= 0) + { + m = i % 12 + 1; + y = y + i / 12; + } + else + { + m = 12 + (i + 1) % 12; + y = y + (i - 11) / 12; + } + if (y < 1 || y > 9999) + { + throw new ArgumentOutOfRangeException(nameof(months), SR.ArgumentOutOfRange_DateArithmetic); + } + int days = DaysInMonth(y, m); + if (d > days) d = days; + return new DateTime((UInt64)(DateToTicks(y, m, d) + InternalTicks % TicksPerDay) | InternalKind); + } + + // Returns the DateTime resulting from adding a fractional number of + // seconds to this DateTime. The result is computed by rounding the + // fractional number of seconds given by value to the nearest + // millisecond, and adding that interval to this DateTime. The + // value argument is permitted to be negative. + // + public DateTime AddSeconds(double value) + { + return Add(value, MillisPerSecond); + } + + // Returns the DateTime resulting from adding the given number of + // 100-nanosecond ticks to this DateTime. The value argument + // is permitted to be negative. + // + public DateTime AddTicks(long value) + { + long ticks = InternalTicks; + if (value > MaxTicks - ticks || value < MinTicks - ticks) + { + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_DateArithmetic); + } + return new DateTime((UInt64)(ticks + value) | InternalKind); + } + + // Returns the DateTime resulting from adding the given number of + // years to this DateTime. The result is computed by incrementing + // (or decrementing) the year part of this DateTime by value + // years. If the month and day of this DateTime is 2/29, and if the + // resulting year is not a leap year, the month and day of the resulting + // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day + // parts of the result are the same as those of this DateTime. + // + public DateTime AddYears(int value) + { + if (value < -10000 || value > 10000) + { + // DateTimeOffset.AddYears(int years) is implemented on top of DateTime.AddYears(int value). Use the more appropriate + // parameter name out of the two for the exception. + throw new ArgumentOutOfRangeException("years", SR.ArgumentOutOfRange_DateTimeBadYears); + } + return AddMonths(value * 12); + } + + // Compares two DateTime values, returning an integer that indicates + // their relationship. + // + public static int Compare(DateTime t1, DateTime t2) + { + Int64 ticks1 = t1.InternalTicks; + Int64 ticks2 = t2.InternalTicks; + if (ticks1 > ticks2) return 1; + if (ticks1 < ticks2) return -1; + return 0; + } + + // Compares this DateTime to a given object. This method provides an + // implementation of the IComparable interface. The object + // argument must be another DateTime, or otherwise an exception + // occurs. Null is considered less than any instance. + // + // Returns a value less than zero if this object + public int CompareTo(Object value) + { + if (value == null) return 1; + if (!(value is DateTime)) + { + throw new ArgumentException(SR.Arg_MustBeDateTime); + } + + return Compare(this, (DateTime)value); + } + + public int CompareTo(DateTime value) + { + return Compare(this, value); + } + + // Returns the tick count corresponding to the given year, month, and day. + // Will check the if the parameters are valid. + private static long DateToTicks(int year, int month, int day) + { + if (year >= 1 && year <= 9999 && month >= 1 && month <= 12) + { + int[] days = IsLeapYear(year) ? s_daysToMonth366 : s_daysToMonth365; + if (day >= 1 && day <= days[month] - days[month - 1]) + { + int y = year - 1; + int n = y * 365 + y / 4 - y / 100 + y / 400 + days[month - 1] + day - 1; + return n * TicksPerDay; + } + } + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay); + } + + // Return the tick count corresponding to the given hour, minute, second. + // Will check the if the parameters are valid. + private static long TimeToTicks(int hour, int minute, int second) + { + //TimeSpan.TimeToTicks is a family access function which does no error checking, so + //we need to put some error checking out here. + if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >= 0 && second < 60) + { + return (TimeSpan.TimeToTicks(hour, minute, second)); + } + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadHourMinuteSecond); + } + + // Returns the number of days in the month given by the year and + // month arguments. + // + public static int DaysInMonth(int year, int month) + { + if (month < 1 || month > 12) throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month); + // IsLeapYear checks the year argument + int[] days = IsLeapYear(year) ? s_daysToMonth366 : s_daysToMonth365; + return days[month] - days[month - 1]; + } + + // Converts an OLE Date to a tick count. + // This function is duplicated in COMDateTime.cpp + internal static long DoubleDateToTicks(double value) + { + // The check done this way will take care of NaN + if (!(value < OADateMaxAsDouble) || !(value > OADateMinAsDouble)) + throw new ArgumentException(SR.Arg_OleAutDateInvalid); + + // Conversion to long will not cause an overflow here, as at this point the "value" is in between OADateMinAsDouble and OADateMaxAsDouble + long millis = (long)(value * MillisPerDay + (value >= 0 ? 0.5 : -0.5)); + // The interesting thing here is when you have a value like 12.5 it all positive 12 days and 12 hours from 01/01/1899 + // However if you a value of -12.25 it is minus 12 days but still positive 6 hours, almost as though you meant -11.75 all negative + // This line below fixes up the millis in the negative case + if (millis < 0) + { + millis -= (millis % MillisPerDay) * 2; + } + + millis += DoubleDateOffset / TicksPerMillisecond; + + if (millis < 0 || millis >= MaxMillis) throw new ArgumentException(SR.Arg_OleAutDateScale); + return millis * TicksPerMillisecond; + } + + // Checks if this DateTime is equal to a given object. Returns + // true if the given object is a boxed DateTime and its value + // is equal to the value of this DateTime. Returns false + // otherwise. + // + public override bool Equals(Object value) + { + if (value is DateTime) + { + return InternalTicks == ((DateTime)value).InternalTicks; + } + return false; + } + + public bool Equals(DateTime value) + { + return InternalTicks == value.InternalTicks; + } + + // Compares two DateTime values for equality. Returns true if + // the two DateTime values are equal, or false if they are + // not equal. + // + public static bool Equals(DateTime t1, DateTime t2) + { + return t1.InternalTicks == t2.InternalTicks; + } + + public static DateTime FromBinary(Int64 dateData) + { + if ((dateData & (unchecked((Int64)LocalMask))) != 0) + { + // Local times need to be adjusted as you move from one time zone to another, + // just as they are when serializing in text. As such the format for local times + // changes to store the ticks of the UTC time, but with flags that look like a + // local date. + Int64 ticks = dateData & (unchecked((Int64)TicksMask)); + // Negative ticks are stored in the top part of the range and should be converted back into a negative number + if (ticks > TicksCeiling - TicksPerDay) + { + ticks = ticks - TicksCeiling; + } + // Convert the ticks back to local. If the UTC ticks are out of range, we need to default to + // the UTC offset from MinValue and MaxValue to be consistent with Parse. + Boolean isAmbiguousLocalDst = false; + Int64 offsetTicks; + if (ticks < MinTicks) + { + offsetTicks = TimeZoneInfo.GetLocalUtcOffset(DateTime.MinValue, TimeZoneInfoOptions.NoThrowOnInvalidTime).Ticks; + } + else if (ticks > MaxTicks) + { + offsetTicks = TimeZoneInfo.GetLocalUtcOffset(DateTime.MaxValue, TimeZoneInfoOptions.NoThrowOnInvalidTime).Ticks; + } + else + { + // Because the ticks conversion between UTC and local is lossy, we need to capture whether the + // time is in a repeated hour so that it can be passed to the DateTime constructor. + DateTime utcDt = new DateTime(ticks, DateTimeKind.Utc); + Boolean isDaylightSavings = false; + offsetTicks = TimeZoneInfo.GetUtcOffsetFromUtc(utcDt, TimeZoneInfo.Local, out isDaylightSavings, out isAmbiguousLocalDst).Ticks; + } + ticks += offsetTicks; + // Another behaviour of parsing is to cause small times to wrap around, so that they can be used + // to compare times of day + if (ticks < 0) + { + ticks += TicksPerDay; + } + if (ticks < MinTicks || ticks > MaxTicks) + { + throw new ArgumentException(SR.Argument_DateTimeBadBinaryData, nameof(dateData)); + } + return new DateTime(ticks, DateTimeKind.Local, isAmbiguousLocalDst); + } + else + { + return DateTime.FromBinaryRaw(dateData); + } + } + + // A version of ToBinary that uses the real representation and does not adjust local times. This is needed for + // scenarios where the serialized data must maintain compatibility + internal static DateTime FromBinaryRaw(Int64 dateData) + { + Int64 ticks = dateData & (Int64)TicksMask; + if (ticks < MinTicks || ticks > MaxTicks) + throw new ArgumentException(SR.Argument_DateTimeBadBinaryData, nameof(dateData)); + return new DateTime((UInt64)dateData); + } + + // Creates a DateTime from a Windows filetime. A Windows filetime is + // a long representing the date and time as the number of + // 100-nanosecond intervals that have elapsed since 1/1/1601 12:00am. + // + public static DateTime FromFileTime(long fileTime) + { + return FromFileTimeUtc(fileTime).ToLocalTime(); + } + + public static DateTime FromFileTimeUtc(long fileTime) + { + if (fileTime < 0 || fileTime > MaxTicks - FileTimeOffset) + { + throw new ArgumentOutOfRangeException(nameof(fileTime), SR.ArgumentOutOfRange_FileTimeInvalid); + } + + // This is the ticks in Universal time for this fileTime. + long universalTicks = fileTime + FileTimeOffset; + return new DateTime(universalTicks, DateTimeKind.Utc); + } + + // Creates a DateTime from an OLE Automation Date. + // + public static DateTime FromOADate(double d) + { + return new DateTime(DoubleDateToTicks(d), DateTimeKind.Unspecified); + } + + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + // Serialize both the old and the new format + info.AddValue(TicksField, InternalTicks); + info.AddValue(DateDataField, _dateData); + } + + public Boolean IsDaylightSavingTime() + { + if (Kind == DateTimeKind.Utc) + { + return false; + } + return TimeZoneInfo.Local.IsDaylightSavingTime(this, TimeZoneInfoOptions.NoThrowOnInvalidTime); + } + + public static DateTime SpecifyKind(DateTime value, DateTimeKind kind) + { + return new DateTime(value.InternalTicks, kind); + } + + public Int64 ToBinary() + { + if (Kind == DateTimeKind.Local) + { + // Local times need to be adjusted as you move from one time zone to another, + // just as they are when serializing in text. As such the format for local times + // changes to store the ticks of the UTC time, but with flags that look like a + // local date. + + // To match serialization in text we need to be able to handle cases where + // the UTC value would be out of range. Unused parts of the ticks range are + // used for this, so that values just past max value are stored just past the + // end of the maximum range, and values just below minimum value are stored + // at the end of the ticks area, just below 2^62. + TimeSpan offset = TimeZoneInfo.GetLocalUtcOffset(this, TimeZoneInfoOptions.NoThrowOnInvalidTime); + Int64 ticks = Ticks; + Int64 storedTicks = ticks - offset.Ticks; + if (storedTicks < 0) + { + storedTicks = TicksCeiling + storedTicks; + } + return storedTicks | (unchecked((Int64)LocalMask)); + } + else + { + return (Int64)_dateData; + } + } + + // Returns the date part of this DateTime. The resulting value + // corresponds to this DateTime with the time-of-day part set to + // zero (midnight). + // + public DateTime Date + { + get + { + Int64 ticks = InternalTicks; + return new DateTime((UInt64)(ticks - ticks % TicksPerDay) | InternalKind); + } + } + + // Returns a given date part of this DateTime. This method is used + // to compute the year, day-of-year, month, or day part. + private int GetDatePart(int part) + { + Int64 ticks = InternalTicks; + // n = number of days since 1/1/0001 + int n = (int)(ticks / TicksPerDay); + // y400 = number of whole 400-year periods since 1/1/0001 + int y400 = n / DaysPer400Years; + // n = day number within 400-year period + n -= y400 * DaysPer400Years; + // y100 = number of whole 100-year periods within 400-year period + int y100 = n / DaysPer100Years; + // Last 100-year period has an extra day, so decrement result if 4 + if (y100 == 4) y100 = 3; + // n = day number within 100-year period + n -= y100 * DaysPer100Years; + // y4 = number of whole 4-year periods within 100-year period + int y4 = n / DaysPer4Years; + // n = day number within 4-year period + n -= y4 * DaysPer4Years; + // y1 = number of whole years within 4-year period + int y1 = n / DaysPerYear; + // Last year has an extra day, so decrement result if 4 + if (y1 == 4) y1 = 3; + // If year was requested, compute and return it + if (part == DatePartYear) + { + return y400 * 400 + y100 * 100 + y4 * 4 + y1 + 1; + } + // n = day number within year + n -= y1 * DaysPerYear; + // If day-of-year was requested, return it + if (part == DatePartDayOfYear) return n + 1; + // Leap year calculation looks different from IsLeapYear since y1, y4, + // and y100 are relative to year 1, not year 0 + bool leapYear = y1 == 3 && (y4 != 24 || y100 == 3); + int[] days = leapYear ? s_daysToMonth366 : s_daysToMonth365; + // All months have less than 32 days, so n >> 5 is a good conservative + // estimate for the month + int m = (n >> 5) + 1; + // m = 1-based month number + while (n >= days[m]) m++; + // If month was requested, return it + if (part == DatePartMonth) return m; + // Return 1-based day-of-month + return n - days[m - 1] + 1; + } + + // Exactly the same as GetDatePart(int part), except computing all of + // year/month/day rather than just one of them. Used when all three + // are needed rather than redoing the computations for each. + internal void GetDatePart(out int year, out int month, out int day) + { + Int64 ticks = InternalTicks; + // n = number of days since 1/1/0001 + int n = (int)(ticks / TicksPerDay); + // y400 = number of whole 400-year periods since 1/1/0001 + int y400 = n / DaysPer400Years; + // n = day number within 400-year period + n -= y400 * DaysPer400Years; + // y100 = number of whole 100-year periods within 400-year period + int y100 = n / DaysPer100Years; + // Last 100-year period has an extra day, so decrement result if 4 + if (y100 == 4) y100 = 3; + // n = day number within 100-year period + n -= y100 * DaysPer100Years; + // y4 = number of whole 4-year periods within 100-year period + int y4 = n / DaysPer4Years; + // n = day number within 4-year period + n -= y4 * DaysPer4Years; + // y1 = number of whole years within 4-year period + int y1 = n / DaysPerYear; + // Last year has an extra day, so decrement result if 4 + if (y1 == 4) y1 = 3; + // compute year + year = y400 * 400 + y100 * 100 + y4 * 4 + y1 + 1; + // n = day number within year + n -= y1 * DaysPerYear; + // dayOfYear = n + 1; + // Leap year calculation looks different from IsLeapYear since y1, y4, + // and y100 are relative to year 1, not year 0 + bool leapYear = y1 == 3 && (y4 != 24 || y100 == 3); + int[] days = leapYear ? s_daysToMonth366 : s_daysToMonth365; + // All months have less than 32 days, so n >> 5 is a good conservative + // estimate for the month + int m = (n >> 5) + 1; + // m = 1-based month number + while (n >= days[m]) m++; + // compute month and day + month = m; + day = n - days[m - 1] + 1; + } + + // Returns the day-of-month part of this DateTime. The returned + // value is an integer between 1 and 31. + // + public int Day + { + get + { + return GetDatePart(DatePartDay); + } + } + + // Returns the day-of-week part of this DateTime. The returned value + // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates + // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates + // Thursday, 5 indicates Friday, and 6 indicates Saturday. + // + public DayOfWeek DayOfWeek + { + get + { + return (DayOfWeek)((InternalTicks / TicksPerDay + 1) % 7); + } + } + + // Returns the day-of-year part of this DateTime. The returned value + // is an integer between 1 and 366. + // + public int DayOfYear + { + get + { + return GetDatePart(DatePartDayOfYear); + } + } + + // Returns the hash code for this DateTime. + // + public override int GetHashCode() + { + Int64 ticks = InternalTicks; + return unchecked((int)ticks) ^ (int)(ticks >> 32); + } + + // Returns the hour part of this DateTime. The returned value is an + // integer between 0 and 23. + // + public int Hour + { + get + { + return (int)((InternalTicks / TicksPerHour) % 24); + } + } + + internal Boolean IsAmbiguousDaylightSavingTime() + { + return (InternalKind == KindLocalAmbiguousDst); + } + + public DateTimeKind Kind + { + get + { + switch (InternalKind) + { + case KindUnspecified: + return DateTimeKind.Unspecified; + case KindUtc: + return DateTimeKind.Utc; + default: + return DateTimeKind.Local; + } + } + } + + // Returns the millisecond part of this DateTime. The returned value + // is an integer between 0 and 999. + // + public int Millisecond + { + get + { + return (int)((InternalTicks / TicksPerMillisecond) % 1000); + } + } + + // Returns the minute part of this DateTime. The returned value is + // an integer between 0 and 59. + // + public int Minute + { + get + { + return (int)((InternalTicks / TicksPerMinute) % 60); + } + } + + // Returns the month part of this DateTime. The returned value is an + // integer between 1 and 12. + // + public int Month + { + get + { + return GetDatePart(DatePartMonth); + } + } + + // Returns a DateTime representing the current date and time. The + // resolution of the returned value depends on the system timer. + public static DateTime Now + { + get + { + DateTime utc = UtcNow; + Boolean isAmbiguousLocalDst = false; + Int64 offset = TimeZoneInfo.GetDateTimeNowUtcOffsetFromUtc(utc, out isAmbiguousLocalDst).Ticks; + long tick = utc.Ticks + offset; + if (tick > DateTime.MaxTicks) + { + return new DateTime(DateTime.MaxTicks, DateTimeKind.Local); + } + if (tick < DateTime.MinTicks) + { + return new DateTime(DateTime.MinTicks, DateTimeKind.Local); + } + return new DateTime(tick, DateTimeKind.Local, isAmbiguousLocalDst); + } + } + + // Returns the second part of this DateTime. The returned value is + // an integer between 0 and 59. + // + public int Second + { + get + { + return (int)((InternalTicks / TicksPerSecond) % 60); + } + } + + // Returns the tick count for this DateTime. The returned value is + // the number of 100-nanosecond intervals that have elapsed since 1/1/0001 + // 12:00am. + // + public long Ticks + { + get + { + return InternalTicks; + } + } + + // Returns the time-of-day part of this DateTime. The returned value + // is a TimeSpan that indicates the time elapsed since midnight. + // + public TimeSpan TimeOfDay + { + get + { + return new TimeSpan(InternalTicks % TicksPerDay); + } + } + + // Returns a DateTime representing the current date. The date part + // of the returned value is the current date, and the time-of-day part of + // the returned value is zero (midnight). + // + public static DateTime Today + { + get + { + return DateTime.Now.Date; + } + } + + // Returns the year part of this DateTime. The returned value is an + // integer between 1 and 9999. + // + public int Year + { + get + { + return GetDatePart(DatePartYear); + } + } + + // Checks whether a given year is a leap year. This method returns true if + // year is a leap year, or false if not. + // + public static bool IsLeapYear(int year) + { + if (year < 1 || year > 9999) + { + throw new ArgumentOutOfRangeException(nameof(year), SR.ArgumentOutOfRange_Year); + } + return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); + } + + // Constructs a DateTime from a string. The string must specify a + // date and optionally a time in a culture-specific or universal format. + // Leading and trailing whitespace characters are allowed. + // + public static DateTime Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return (DateTimeParse.Parse(s, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None)); + } + + // Constructs a DateTime from a string. The string must specify a + // date and optionally a time in a culture-specific or universal format. + // Leading and trailing whitespace characters are allowed. + // + public static DateTime Parse(String s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return (DateTimeParse.Parse(s, DateTimeFormatInfo.GetInstance(provider), DateTimeStyles.None)); + } + + public static DateTime Parse(String s, IFormatProvider provider, DateTimeStyles styles) + { + DateTimeFormatInfo.ValidateStyles(styles, nameof(styles)); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return (DateTimeParse.Parse(s, DateTimeFormatInfo.GetInstance(provider), styles)); + } + + public static DateTime Parse(ReadOnlySpan s, IFormatProvider provider = null, DateTimeStyles styles = DateTimeStyles.None) + { + DateTimeFormatInfo.ValidateStyles(styles, nameof(styles)); + return DateTimeParse.Parse(s, DateTimeFormatInfo.GetInstance(provider), styles); + } + + // Constructs a DateTime from a string. The string must specify a + // date and optionally a time in a culture-specific or universal format. + // Leading and trailing whitespace characters are allowed. + // + public static DateTime ParseExact(String s, String format, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format); + return (DateTimeParse.ParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), DateTimeStyles.None)); + } + + // Constructs a DateTime from a string. The string must specify a + // date and optionally a time in a culture-specific or universal format. + // Leading and trailing whitespace characters are allowed. + // + public static DateTime ParseExact(String s, String format, IFormatProvider provider, DateTimeStyles style) + { + DateTimeFormatInfo.ValidateStyles(style, nameof(style)); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format); + return (DateTimeParse.ParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), style)); + } + + // TODO https://github.com/dotnet/corefx/issues/25337: Remove this overload once corefx is updated to target the new signatures + public static DateTime ParseExact(ReadOnlySpan s, string format, IFormatProvider provider, DateTimeStyles style) + { + if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format); + return ParseExact(s, (ReadOnlySpan)format, provider, style); + } + + public static DateTime ParseExact(ReadOnlySpan s, ReadOnlySpan format, IFormatProvider provider, DateTimeStyles style = DateTimeStyles.None) + { + DateTimeFormatInfo.ValidateStyles(style, nameof(style)); + return DateTimeParse.ParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), style); + } + + public static DateTime ParseExact(String s, String[] formats, IFormatProvider provider, DateTimeStyles style) + { + DateTimeFormatInfo.ValidateStyles(style, nameof(style)); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return DateTimeParse.ParseExactMultiple(s, formats, DateTimeFormatInfo.GetInstance(provider), style); + } + + public static DateTime ParseExact(ReadOnlySpan s, string[] formats, IFormatProvider provider, DateTimeStyles style = DateTimeStyles.None) + { + DateTimeFormatInfo.ValidateStyles(style, nameof(style)); + return DateTimeParse.ParseExactMultiple(s, formats, DateTimeFormatInfo.GetInstance(provider), style); + } + + public TimeSpan Subtract(DateTime value) + { + return new TimeSpan(InternalTicks - value.InternalTicks); + } + + public DateTime Subtract(TimeSpan value) + { + long ticks = InternalTicks; + long valueTicks = value._ticks; + if (ticks - MinTicks < valueTicks || ticks - MaxTicks > valueTicks) + { + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_DateArithmetic); + } + return new DateTime((UInt64)(ticks - valueTicks) | InternalKind); + } + + // This function is duplicated in COMDateTime.cpp + private static double TicksToOADate(long value) + { + if (value == 0) + return 0.0; // Returns OleAut's zero'ed date value. + if (value < TicksPerDay) // This is a fix for VB. They want the default day to be 1/1/0001 rathar then 12/30/1899. + value += DoubleDateOffset; // We could have moved this fix down but we would like to keep the bounds check. + if (value < OADateMinAsTicks) + throw new OverflowException(SR.Arg_OleAutDateInvalid); + // Currently, our max date == OA's max date (12/31/9999), so we don't + // need an overflow check in that direction. + long millis = (value - DoubleDateOffset) / TicksPerMillisecond; + if (millis < 0) + { + long frac = millis % MillisPerDay; + if (frac != 0) millis -= (MillisPerDay + frac) * 2; + } + return (double)millis / MillisPerDay; + } + + // Converts the DateTime instance into an OLE Automation compatible + // double date. + public double ToOADate() + { + return TicksToOADate(InternalTicks); + } + + public long ToFileTime() + { + // Treats the input as local if it is not specified + return ToUniversalTime().ToFileTimeUtc(); + } + + public long ToFileTimeUtc() + { + // Treats the input as universal if it is not specified + long ticks = ((InternalKind & LocalMask) != 0) ? ToUniversalTime().InternalTicks : this.InternalTicks; + ticks -= FileTimeOffset; + if (ticks < 0) + { + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_FileTimeInvalid); + } + return ticks; + } + + public DateTime ToLocalTime() + { + return ToLocalTime(false); + } + + internal DateTime ToLocalTime(bool throwOnOverflow) + { + if (Kind == DateTimeKind.Local) + { + return this; + } + Boolean isDaylightSavings = false; + Boolean isAmbiguousLocalDst = false; + Int64 offset = TimeZoneInfo.GetUtcOffsetFromUtc(this, TimeZoneInfo.Local, out isDaylightSavings, out isAmbiguousLocalDst).Ticks; + long tick = Ticks + offset; + if (tick > DateTime.MaxTicks) + { + if (throwOnOverflow) + throw new ArgumentException(SR.Arg_ArgumentOutOfRangeException); + else + return new DateTime(DateTime.MaxTicks, DateTimeKind.Local); + } + if (tick < DateTime.MinTicks) + { + if (throwOnOverflow) + throw new ArgumentException(SR.Arg_ArgumentOutOfRangeException); + else + return new DateTime(DateTime.MinTicks, DateTimeKind.Local); + } + return new DateTime(tick, DateTimeKind.Local, isAmbiguousLocalDst); + } + + public String ToLongDateString() + { + return DateTimeFormat.Format(this, "D", DateTimeFormatInfo.CurrentInfo); + } + + public String ToLongTimeString() + { + return DateTimeFormat.Format(this, "T", DateTimeFormatInfo.CurrentInfo); + } + + public String ToShortDateString() + { + return DateTimeFormat.Format(this, "d", DateTimeFormatInfo.CurrentInfo); + } + + public String ToShortTimeString() + { + return DateTimeFormat.Format(this, "t", DateTimeFormatInfo.CurrentInfo); + } + + public override String ToString() + { + return DateTimeFormat.Format(this, null, DateTimeFormatInfo.CurrentInfo); + } + + public String ToString(String format) + { + return DateTimeFormat.Format(this, format, DateTimeFormatInfo.CurrentInfo); + } + + public String ToString(IFormatProvider provider) + { + return DateTimeFormat.Format(this, null, DateTimeFormatInfo.GetInstance(provider)); + } + + public String ToString(String format, IFormatProvider provider) + { + return DateTimeFormat.Format(this, format, DateTimeFormatInfo.GetInstance(provider)); + } + + // TODO https://github.com/dotnet/corefx/issues/25337: Remove this overload once corefx is updated to target the new signatures + public bool TryFormat(Span destination, out int charsWritten, string format, IFormatProvider provider) => + TryFormat(destination, out charsWritten, (ReadOnlySpan)format, provider); + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) => + DateTimeFormat.TryFormat(this, destination, out charsWritten, format, DateTimeFormatInfo.GetInstance(provider)); + + public DateTime ToUniversalTime() + { + return TimeZoneInfo.ConvertTimeToUtc(this, TimeZoneInfoOptions.NoThrowOnInvalidTime); + } + + public static Boolean TryParse(String s, out DateTime result) + { + if (s == null) + { + result = default; + return false; + } + return DateTimeParse.TryParse(s, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None, out result); + } + + public static bool TryParse(ReadOnlySpan s, out DateTime result) + { + return DateTimeParse.TryParse(s, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None, out result); + } + + public static Boolean TryParse(String s, IFormatProvider provider, DateTimeStyles styles, out DateTime result) + { + DateTimeFormatInfo.ValidateStyles(styles, nameof(styles)); + + if (s == null) + { + result = default; + return false; + } + + return DateTimeParse.TryParse(s, DateTimeFormatInfo.GetInstance(provider), styles, out result); + } + + public static bool TryParse(ReadOnlySpan s, IFormatProvider provider, DateTimeStyles styles, out DateTime result) + { + DateTimeFormatInfo.ValidateStyles(styles, nameof(styles)); + return DateTimeParse.TryParse(s, DateTimeFormatInfo.GetInstance(provider), styles, out result); + } + + public static Boolean TryParseExact(String s, String format, IFormatProvider provider, DateTimeStyles style, out DateTime result) + { + DateTimeFormatInfo.ValidateStyles(style, nameof(style)); + + if (s == null || format == null) + { + result = default; + return false; + } + + return DateTimeParse.TryParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), style, out result); + } + + // TODO https://github.com/dotnet/corefx/issues/25337: Remove this overload once corefx is updated to target the new signatures + public static bool TryParseExact(ReadOnlySpan s, string format, IFormatProvider provider, DateTimeStyles style, out DateTime result) + { + if (format == null) + { + result = default; + return false; + } + + return TryParseExact(s, (ReadOnlySpan)format, provider, style, out result); + } + + public static bool TryParseExact(ReadOnlySpan s, ReadOnlySpan format, IFormatProvider provider, DateTimeStyles style, out DateTime result) + { + DateTimeFormatInfo.ValidateStyles(style, nameof(style)); + return DateTimeParse.TryParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), style, out result); + } + + public static Boolean TryParseExact(String s, String[] formats, IFormatProvider provider, DateTimeStyles style, out DateTime result) + { + DateTimeFormatInfo.ValidateStyles(style, nameof(style)); + + if (s == null) + { + result = default; + return false; + } + + return DateTimeParse.TryParseExactMultiple(s, formats, DateTimeFormatInfo.GetInstance(provider), style, out result); + } + + public static bool TryParseExact(ReadOnlySpan s, string[] formats, IFormatProvider provider, DateTimeStyles style, out DateTime result) + { + DateTimeFormatInfo.ValidateStyles(style, nameof(style)); + return DateTimeParse.TryParseExactMultiple(s, formats, DateTimeFormatInfo.GetInstance(provider), style, out result); + } + + public static DateTime operator +(DateTime d, TimeSpan t) + { + long ticks = d.InternalTicks; + long valueTicks = t._ticks; + if (valueTicks > MaxTicks - ticks || valueTicks < MinTicks - ticks) + { + throw new ArgumentOutOfRangeException(nameof(t), SR.ArgumentOutOfRange_DateArithmetic); + } + return new DateTime((UInt64)(ticks + valueTicks) | d.InternalKind); + } + + public static DateTime operator -(DateTime d, TimeSpan t) + { + long ticks = d.InternalTicks; + long valueTicks = t._ticks; + if (ticks - MinTicks < valueTicks || ticks - MaxTicks > valueTicks) + { + throw new ArgumentOutOfRangeException(nameof(t), SR.ArgumentOutOfRange_DateArithmetic); + } + return new DateTime((UInt64)(ticks - valueTicks) | d.InternalKind); + } + + public static TimeSpan operator -(DateTime d1, DateTime d2) + { + return new TimeSpan(d1.InternalTicks - d2.InternalTicks); + } + + public static bool operator ==(DateTime d1, DateTime d2) + { + return d1.InternalTicks == d2.InternalTicks; + } + + public static bool operator !=(DateTime d1, DateTime d2) + { + return d1.InternalTicks != d2.InternalTicks; + } + + public static bool operator <(DateTime t1, DateTime t2) + { + return t1.InternalTicks < t2.InternalTicks; + } + + public static bool operator <=(DateTime t1, DateTime t2) + { + return t1.InternalTicks <= t2.InternalTicks; + } + + public static bool operator >(DateTime t1, DateTime t2) + { + return t1.InternalTicks > t2.InternalTicks; + } + + public static bool operator >=(DateTime t1, DateTime t2) + { + return t1.InternalTicks >= t2.InternalTicks; + } + + + // Returns a string array containing all of the known date and time options for the + // current culture. The strings returned are properly formatted date and + // time strings for the current instance of DateTime. + public String[] GetDateTimeFormats() + { + return (GetDateTimeFormats(CultureInfo.CurrentCulture)); + } + + // Returns a string array containing all of the known date and time options for the + // using the information provided by IFormatProvider. The strings returned are properly formatted date and + // time strings for the current instance of DateTime. + public String[] GetDateTimeFormats(IFormatProvider provider) + { + return (DateTimeFormat.GetAllDateTimes(this, DateTimeFormatInfo.GetInstance(provider))); + } + + + // Returns a string array containing all of the date and time options for the + // given format format and current culture. The strings returned are properly formatted date and + // time strings for the current instance of DateTime. + public String[] GetDateTimeFormats(char format) + { + return (GetDateTimeFormats(format, CultureInfo.CurrentCulture)); + } + + // Returns a string array containing all of the date and time options for the + // given format format and given culture. The strings returned are properly formatted date and + // time strings for the current instance of DateTime. + public String[] GetDateTimeFormats(char format, IFormatProvider provider) + { + return (DateTimeFormat.GetAllDateTimes(this, format, DateTimeFormatInfo.GetInstance(provider))); + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.DateTime; + } + + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Boolean")); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Char")); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "SByte")); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Byte")); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Int16")); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "UInt16")); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Int32")); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "UInt32")); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Int64")); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "UInt64")); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Single")); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Double")); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Decimal")); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + return this; + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + + // Tries to construct a DateTime from a given year, month, day, hour, + // minute, second and millisecond. + // + internal static Boolean TryCreate(int year, int month, int day, int hour, int minute, int second, int millisecond, out DateTime result) + { + result = DateTime.MinValue; + if (year < 1 || year > 9999 || month < 1 || month > 12) + { + return false; + } + int[] days = IsLeapYear(year) ? s_daysToMonth366 : s_daysToMonth365; + if (day < 1 || day > days[month] - days[month - 1]) + { + return false; + } + if (hour < 0 || hour >= 24 || minute < 0 || minute >= 60 || second < 0 || second >= 60) + { + return false; + } + if (millisecond < 0 || millisecond >= MillisPerSecond) + { + return false; + } + long ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second); + + ticks += millisecond * TicksPerMillisecond; + if (ticks < MinTicks || ticks > MaxTicks) + { + return false; + } + result = new DateTime(ticks, DateTimeKind.Unspecified); + return true; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/DateTimeKind.cs b/external/corefx/src/Common/src/CoreLib/System/DateTimeKind.cs new file mode 100644 index 0000000000..33c9bd925f --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/DateTimeKind.cs @@ -0,0 +1,16 @@ +// 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. + +namespace System +{ + // This enum is used to indentify DateTime instances in cases when they are known to be in local time, + // UTC time or if this information has not been specified or is not applicable. + + public enum DateTimeKind + { + Unspecified = 0, + Utc = 1, + Local = 2, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/DateTimeOffset.cs b/external/corefx/src/Common/src/CoreLib/System/DateTimeOffset.cs new file mode 100644 index 0000000000..bb2196348c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/DateTimeOffset.cs @@ -0,0 +1,1028 @@ +// 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.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace System +{ + // DateTimeOffset is a value type that consists of a DateTime and a time zone offset, + // ie. how far away the time is from GMT. The DateTime is stored whole, and the offset + // is stored as an Int16 internally to save space, but presented as a TimeSpan. + // + // The range is constrained so that both the represented clock time and the represented + // UTC time fit within the boundaries of MaxValue. This gives it the same range as DateTime + // for actual UTC times, and a slightly constrained range on one end when an offset is + // present. + // + // This class should be substitutable for date time in most cases; so most operations + // effectively work on the clock time. However, the underlying UTC time is what counts + // for the purposes of identity, sorting and subtracting two instances. + // + // + // There are theoretically two date times stored, the UTC and the relative local representation + // or the 'clock' time. It actually does not matter which is stored in m_dateTime, so it is desirable + // for most methods to go through the helpers UtcDateTime and ClockDateTime both to abstract this + // out and for internal readability. + + [StructLayout(LayoutKind.Auto)] + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct DateTimeOffset : IComparable, IFormattable, IComparable, IEquatable, ISerializable, IDeserializationCallback, ISpanFormattable + { + // Constants + internal const Int64 MaxOffset = TimeSpan.TicksPerHour * 14; + internal const Int64 MinOffset = -MaxOffset; + + private const long UnixEpochSeconds = DateTime.UnixEpochTicks / TimeSpan.TicksPerSecond; // 62,135,596,800 + private const long UnixEpochMilliseconds = DateTime.UnixEpochTicks / TimeSpan.TicksPerMillisecond; // 62,135,596,800,000 + + internal const long UnixMinSeconds = DateTime.MinTicks / TimeSpan.TicksPerSecond - UnixEpochSeconds; + internal const long UnixMaxSeconds = DateTime.MaxTicks / TimeSpan.TicksPerSecond - UnixEpochSeconds; + + // Static Fields + public static readonly DateTimeOffset MinValue = new DateTimeOffset(DateTime.MinTicks, TimeSpan.Zero); + public static readonly DateTimeOffset MaxValue = new DateTimeOffset(DateTime.MaxTicks, TimeSpan.Zero); + public static readonly DateTimeOffset UnixEpoch = new DateTimeOffset(DateTime.UnixEpochTicks, TimeSpan.Zero); + + // Instance Fields + private DateTime _dateTime; + private Int16 _offsetMinutes; + + // Constructors + + // Constructs a DateTimeOffset from a tick count and offset + public DateTimeOffset(long ticks, TimeSpan offset) + { + _offsetMinutes = ValidateOffset(offset); + // Let the DateTime constructor do the range checks + DateTime dateTime = new DateTime(ticks); + _dateTime = ValidateDate(dateTime, offset); + } + + // Constructs a DateTimeOffset from a DateTime. For Local and Unspecified kinds, + // extracts the local offset. For UTC, creates a UTC instance with a zero offset. + public DateTimeOffset(DateTime dateTime) + { + TimeSpan offset; + if (dateTime.Kind != DateTimeKind.Utc) + { + // Local and Unspecified are both treated as Local + offset = TimeZoneInfo.GetLocalUtcOffset(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime); + } + else + { + offset = new TimeSpan(0); + } + _offsetMinutes = ValidateOffset(offset); + _dateTime = ValidateDate(dateTime, offset); + } + + // Constructs a DateTimeOffset from a DateTime. And an offset. Always makes the clock time + // consistent with the DateTime. For Utc ensures the offset is zero. For local, ensures that + // the offset corresponds to the local. + public DateTimeOffset(DateTime dateTime, TimeSpan offset) + { + if (dateTime.Kind == DateTimeKind.Local) + { + if (offset != TimeZoneInfo.GetLocalUtcOffset(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime)) + { + throw new ArgumentException(SR.Argument_OffsetLocalMismatch, nameof(offset)); + } + } + else if (dateTime.Kind == DateTimeKind.Utc) + { + if (offset != TimeSpan.Zero) + { + throw new ArgumentException(SR.Argument_OffsetUtcMismatch, nameof(offset)); + } + } + _offsetMinutes = ValidateOffset(offset); + _dateTime = ValidateDate(dateTime, offset); + } + + // Constructs a DateTimeOffset from a given year, month, day, hour, + // minute, second and offset. + public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, TimeSpan offset) + { + _offsetMinutes = ValidateOffset(offset); + _dateTime = ValidateDate(new DateTime(year, month, day, hour, minute, second), offset); + } + + // Constructs a DateTimeOffset from a given year, month, day, hour, + // minute, second, millsecond and offset + public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, TimeSpan offset) + { + _offsetMinutes = ValidateOffset(offset); + _dateTime = ValidateDate(new DateTime(year, month, day, hour, minute, second, millisecond), offset); + } + + // Constructs a DateTimeOffset from a given year, month, day, hour, + // minute, second, millsecond, Calendar and offset. + public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, TimeSpan offset) + { + _offsetMinutes = ValidateOffset(offset); + _dateTime = ValidateDate(new DateTime(year, month, day, hour, minute, second, millisecond, calendar), offset); + } + + // Returns a DateTimeOffset representing the current date and time. The + // resolution of the returned value depends on the system timer. + public static DateTimeOffset Now + { + get + { + return new DateTimeOffset(DateTime.Now); + } + } + + public static DateTimeOffset UtcNow + { + get + { + return new DateTimeOffset(DateTime.UtcNow); + } + } + + public DateTime DateTime + { + get + { + return ClockDateTime; + } + } + + public DateTime UtcDateTime + { + get + { + return DateTime.SpecifyKind(_dateTime, DateTimeKind.Utc); + } + } + + public DateTime LocalDateTime + { + get + { + return UtcDateTime.ToLocalTime(); + } + } + + // Adjust to a given offset with the same UTC time. Can throw ArgumentException + // + public DateTimeOffset ToOffset(TimeSpan offset) + { + return new DateTimeOffset((_dateTime + offset).Ticks, offset); + } + + + // Instance Properties + + // The clock or visible time represented. This is just a wrapper around the internal date because this is + // the chosen storage mechanism. Going through this helper is good for readability and maintainability. + // This should be used for display but not identity. + private DateTime ClockDateTime + { + get + { + return new DateTime((_dateTime + Offset).Ticks, DateTimeKind.Unspecified); + } + } + + // Returns the date part of this DateTimeOffset. The resulting value + // corresponds to this DateTimeOffset with the time-of-day part set to + // zero (midnight). + // + public DateTime Date + { + get + { + return ClockDateTime.Date; + } + } + + // Returns the day-of-month part of this DateTimeOffset. The returned + // value is an integer between 1 and 31. + // + public int Day + { + get + { + return ClockDateTime.Day; + } + } + + // Returns the day-of-week part of this DateTimeOffset. The returned value + // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates + // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates + // Thursday, 5 indicates Friday, and 6 indicates Saturday. + // + public DayOfWeek DayOfWeek + { + get + { + return ClockDateTime.DayOfWeek; + } + } + + // Returns the day-of-year part of this DateTimeOffset. The returned value + // is an integer between 1 and 366. + // + public int DayOfYear + { + get + { + return ClockDateTime.DayOfYear; + } + } + + // Returns the hour part of this DateTimeOffset. The returned value is an + // integer between 0 and 23. + // + public int Hour + { + get + { + return ClockDateTime.Hour; + } + } + + + // Returns the millisecond part of this DateTimeOffset. The returned value + // is an integer between 0 and 999. + // + public int Millisecond + { + get + { + return ClockDateTime.Millisecond; + } + } + + // Returns the minute part of this DateTimeOffset. The returned value is + // an integer between 0 and 59. + // + public int Minute + { + get + { + return ClockDateTime.Minute; + } + } + + // Returns the month part of this DateTimeOffset. The returned value is an + // integer between 1 and 12. + // + public int Month + { + get + { + return ClockDateTime.Month; + } + } + + public TimeSpan Offset + { + get + { + return new TimeSpan(0, _offsetMinutes, 0); + } + } + + // Returns the second part of this DateTimeOffset. The returned value is + // an integer between 0 and 59. + // + public int Second + { + get + { + return ClockDateTime.Second; + } + } + + // Returns the tick count for this DateTimeOffset. The returned value is + // the number of 100-nanosecond intervals that have elapsed since 1/1/0001 + // 12:00am. + // + public long Ticks + { + get + { + return ClockDateTime.Ticks; + } + } + + public long UtcTicks + { + get + { + return UtcDateTime.Ticks; + } + } + + // Returns the time-of-day part of this DateTimeOffset. The returned value + // is a TimeSpan that indicates the time elapsed since midnight. + // + public TimeSpan TimeOfDay + { + get + { + return ClockDateTime.TimeOfDay; + } + } + + // Returns the year part of this DateTimeOffset. The returned value is an + // integer between 1 and 9999. + // + public int Year + { + get + { + return ClockDateTime.Year; + } + } + + // Returns the DateTimeOffset resulting from adding the given + // TimeSpan to this DateTimeOffset. + // + public DateTimeOffset Add(TimeSpan timeSpan) + { + return new DateTimeOffset(ClockDateTime.Add(timeSpan), Offset); + } + + // Returns the DateTimeOffset resulting from adding a fractional number of + // days to this DateTimeOffset. The result is computed by rounding the + // fractional number of days given by value to the nearest + // millisecond, and adding that interval to this DateTimeOffset. The + // value argument is permitted to be negative. + // + public DateTimeOffset AddDays(double days) + { + return new DateTimeOffset(ClockDateTime.AddDays(days), Offset); + } + + // Returns the DateTimeOffset resulting from adding a fractional number of + // hours to this DateTimeOffset. The result is computed by rounding the + // fractional number of hours given by value to the nearest + // millisecond, and adding that interval to this DateTimeOffset. The + // value argument is permitted to be negative. + // + public DateTimeOffset AddHours(double hours) + { + return new DateTimeOffset(ClockDateTime.AddHours(hours), Offset); + } + + // Returns the DateTimeOffset resulting from the given number of + // milliseconds to this DateTimeOffset. The result is computed by rounding + // the number of milliseconds given by value to the nearest integer, + // and adding that interval to this DateTimeOffset. The value + // argument is permitted to be negative. + // + public DateTimeOffset AddMilliseconds(double milliseconds) + { + return new DateTimeOffset(ClockDateTime.AddMilliseconds(milliseconds), Offset); + } + + // Returns the DateTimeOffset resulting from adding a fractional number of + // minutes to this DateTimeOffset. The result is computed by rounding the + // fractional number of minutes given by value to the nearest + // millisecond, and adding that interval to this DateTimeOffset. The + // value argument is permitted to be negative. + // + public DateTimeOffset AddMinutes(double minutes) + { + return new DateTimeOffset(ClockDateTime.AddMinutes(minutes), Offset); + } + + public DateTimeOffset AddMonths(int months) + { + return new DateTimeOffset(ClockDateTime.AddMonths(months), Offset); + } + + // Returns the DateTimeOffset resulting from adding a fractional number of + // seconds to this DateTimeOffset. The result is computed by rounding the + // fractional number of seconds given by value to the nearest + // millisecond, and adding that interval to this DateTimeOffset. The + // value argument is permitted to be negative. + // + public DateTimeOffset AddSeconds(double seconds) + { + return new DateTimeOffset(ClockDateTime.AddSeconds(seconds), Offset); + } + + // Returns the DateTimeOffset resulting from adding the given number of + // 100-nanosecond ticks to this DateTimeOffset. The value argument + // is permitted to be negative. + // + public DateTimeOffset AddTicks(long ticks) + { + return new DateTimeOffset(ClockDateTime.AddTicks(ticks), Offset); + } + + // Returns the DateTimeOffset resulting from adding the given number of + // years to this DateTimeOffset. The result is computed by incrementing + // (or decrementing) the year part of this DateTimeOffset by value + // years. If the month and day of this DateTimeOffset is 2/29, and if the + // resulting year is not a leap year, the month and day of the resulting + // DateTimeOffset becomes 2/28. Otherwise, the month, day, and time-of-day + // parts of the result are the same as those of this DateTimeOffset. + // + public DateTimeOffset AddYears(int years) + { + return new DateTimeOffset(ClockDateTime.AddYears(years), Offset); + } + + // Compares two DateTimeOffset values, returning an integer that indicates + // their relationship. + // + public static int Compare(DateTimeOffset first, DateTimeOffset second) + { + return DateTime.Compare(first.UtcDateTime, second.UtcDateTime); + } + + // Compares this DateTimeOffset to a given object. This method provides an + // implementation of the IComparable interface. The object + // argument must be another DateTimeOffset, or otherwise an exception + // occurs. Null is considered less than any instance. + // + int IComparable.CompareTo(Object obj) + { + if (obj == null) return 1; + if (!(obj is DateTimeOffset)) + { + throw new ArgumentException(SR.Arg_MustBeDateTimeOffset); + } + + DateTime objUtc = ((DateTimeOffset)obj).UtcDateTime; + DateTime utc = UtcDateTime; + if (utc > objUtc) return 1; + if (utc < objUtc) return -1; + return 0; + } + + public int CompareTo(DateTimeOffset other) + { + DateTime otherUtc = other.UtcDateTime; + DateTime utc = UtcDateTime; + if (utc > otherUtc) return 1; + if (utc < otherUtc) return -1; + return 0; + } + + + // Checks if this DateTimeOffset is equal to a given object. Returns + // true if the given object is a boxed DateTimeOffset and its value + // is equal to the value of this DateTimeOffset. Returns false + // otherwise. + // + public override bool Equals(Object obj) + { + if (obj is DateTimeOffset) + { + return UtcDateTime.Equals(((DateTimeOffset)obj).UtcDateTime); + } + return false; + } + + public bool Equals(DateTimeOffset other) + { + return UtcDateTime.Equals(other.UtcDateTime); + } + + public bool EqualsExact(DateTimeOffset other) + { + // + // returns true when the ClockDateTime, Kind, and Offset match + // + // currently the Kind should always be Unspecified, but there is always the possibility that a future version + // of DateTimeOffset overloads the Kind field + // + return (ClockDateTime == other.ClockDateTime && Offset == other.Offset && ClockDateTime.Kind == other.ClockDateTime.Kind); + } + + // Compares two DateTimeOffset values for equality. Returns true if + // the two DateTimeOffset values are equal, or false if they are + // not equal. + // + public static bool Equals(DateTimeOffset first, DateTimeOffset second) + { + return DateTime.Equals(first.UtcDateTime, second.UtcDateTime); + } + + // Creates a DateTimeOffset from a Windows filetime. A Windows filetime is + // a long representing the date and time as the number of + // 100-nanosecond intervals that have elapsed since 1/1/1601 12:00am. + // + public static DateTimeOffset FromFileTime(long fileTime) + { + return new DateTimeOffset(DateTime.FromFileTime(fileTime)); + } + + public static DateTimeOffset FromUnixTimeSeconds(long seconds) + { + if (seconds < UnixMinSeconds || seconds > UnixMaxSeconds) + { + throw new ArgumentOutOfRangeException(nameof(seconds), + SR.Format(SR.ArgumentOutOfRange_Range, UnixMinSeconds, UnixMaxSeconds)); + } + + long ticks = seconds * TimeSpan.TicksPerSecond + DateTime.UnixEpochTicks; + return new DateTimeOffset(ticks, TimeSpan.Zero); + } + + public static DateTimeOffset FromUnixTimeMilliseconds(long milliseconds) + { + const long MinMilliseconds = DateTime.MinTicks / TimeSpan.TicksPerMillisecond - UnixEpochMilliseconds; + const long MaxMilliseconds = DateTime.MaxTicks / TimeSpan.TicksPerMillisecond - UnixEpochMilliseconds; + + if (milliseconds < MinMilliseconds || milliseconds > MaxMilliseconds) + { + throw new ArgumentOutOfRangeException(nameof(milliseconds), + SR.Format(SR.ArgumentOutOfRange_Range, MinMilliseconds, MaxMilliseconds)); + } + + long ticks = milliseconds * TimeSpan.TicksPerMillisecond + DateTime.UnixEpochTicks; + return new DateTimeOffset(ticks, TimeSpan.Zero); + } + + // ----- SECTION: private serialization instance methods ----------------* + + void IDeserializationCallback.OnDeserialization(Object sender) + { + try + { + _offsetMinutes = ValidateOffset(Offset); + _dateTime = ValidateDate(ClockDateTime, Offset); + } + catch (ArgumentException e) + { + throw new SerializationException(SR.Serialization_InvalidData, e); + } + } + + + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + info.AddValue("DateTime", _dateTime); // Do not rename (binary serialization) + info.AddValue("OffsetMinutes", _offsetMinutes); // Do not rename (binary serialization) + } + + + private DateTimeOffset(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + _dateTime = (DateTime)info.GetValue("DateTime", typeof(DateTime)); // Do not rename (binary serialization) + _offsetMinutes = (Int16)info.GetValue("OffsetMinutes", typeof(Int16)); // Do not rename (binary serialization) + } + + // Returns the hash code for this DateTimeOffset. + // + public override int GetHashCode() + { + return UtcDateTime.GetHashCode(); + } + + // Constructs a DateTimeOffset from a string. The string must specify a + // date and optionally a time in a culture-specific or universal format. + // Leading and trailing whitespace characters are allowed. + // + public static DateTimeOffset Parse(String input) + { + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + + TimeSpan offset; + DateTime dateResult = DateTimeParse.Parse(input, + DateTimeFormatInfo.CurrentInfo, + DateTimeStyles.None, + out offset); + return new DateTimeOffset(dateResult.Ticks, offset); + } + + // Constructs a DateTimeOffset from a string. The string must specify a + // date and optionally a time in a culture-specific or universal format. + // Leading and trailing whitespace characters are allowed. + // + public static DateTimeOffset Parse(String input, IFormatProvider formatProvider) + { + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + return Parse(input, formatProvider, DateTimeStyles.None); + } + + public static DateTimeOffset Parse(String input, IFormatProvider formatProvider, DateTimeStyles styles) + { + styles = ValidateStyles(styles, nameof(styles)); + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + + TimeSpan offset; + DateTime dateResult = DateTimeParse.Parse(input, + DateTimeFormatInfo.GetInstance(formatProvider), + styles, + out offset); + return new DateTimeOffset(dateResult.Ticks, offset); + } + + public static DateTimeOffset Parse(ReadOnlySpan input, IFormatProvider formatProvider = null, DateTimeStyles styles = DateTimeStyles.None) + { + styles = ValidateStyles(styles, nameof(styles)); + DateTime dateResult = DateTimeParse.Parse(input, DateTimeFormatInfo.GetInstance(formatProvider), styles, out TimeSpan offset); + return new DateTimeOffset(dateResult.Ticks, offset); + } + + // Constructs a DateTimeOffset from a string. The string must specify a + // date and optionally a time in a culture-specific or universal format. + // Leading and trailing whitespace characters are allowed. + // + public static DateTimeOffset ParseExact(String input, String format, IFormatProvider formatProvider) + { + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format); + return ParseExact(input, format, formatProvider, DateTimeStyles.None); + } + + // Constructs a DateTimeOffset from a string. The string must specify a + // date and optionally a time in a culture-specific or universal format. + // Leading and trailing whitespace characters are allowed. + // + public static DateTimeOffset ParseExact(String input, String format, IFormatProvider formatProvider, DateTimeStyles styles) + { + styles = ValidateStyles(styles, nameof(styles)); + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format); + + TimeSpan offset; + DateTime dateResult = DateTimeParse.ParseExact(input, + format, + DateTimeFormatInfo.GetInstance(formatProvider), + styles, + out offset); + return new DateTimeOffset(dateResult.Ticks, offset); + } + + // TODO https://github.com/dotnet/corefx/issues/25337: Remove this overload once corefx is updated to target the new signatures + public static DateTimeOffset ParseExact(ReadOnlySpan input, string format, IFormatProvider formatProvider, DateTimeStyles styles) + { + if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format); + return ParseExact(input, (ReadOnlySpan)format, formatProvider, styles); + } + + public static DateTimeOffset ParseExact(ReadOnlySpan input, ReadOnlySpan format, IFormatProvider formatProvider, DateTimeStyles styles = DateTimeStyles.None) + { + styles = ValidateStyles(styles, nameof(styles)); + DateTime dateResult = DateTimeParse.ParseExact(input, format, DateTimeFormatInfo.GetInstance(formatProvider), styles, out TimeSpan offset); + return new DateTimeOffset(dateResult.Ticks, offset); + } + + public static DateTimeOffset ParseExact(String input, String[] formats, IFormatProvider formatProvider, DateTimeStyles styles) + { + styles = ValidateStyles(styles, nameof(styles)); + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + + TimeSpan offset; + DateTime dateResult = DateTimeParse.ParseExactMultiple(input, + formats, + DateTimeFormatInfo.GetInstance(formatProvider), + styles, + out offset); + return new DateTimeOffset(dateResult.Ticks, offset); + } + + public static DateTimeOffset ParseExact(ReadOnlySpan input, string[] formats, IFormatProvider formatProvider, DateTimeStyles styles = DateTimeStyles.None) + { + styles = ValidateStyles(styles, nameof(styles)); + DateTime dateResult = DateTimeParse.ParseExactMultiple(input, formats, DateTimeFormatInfo.GetInstance(formatProvider), styles, out TimeSpan offset); + return new DateTimeOffset(dateResult.Ticks, offset); + } + + public TimeSpan Subtract(DateTimeOffset value) + { + return UtcDateTime.Subtract(value.UtcDateTime); + } + + public DateTimeOffset Subtract(TimeSpan value) + { + return new DateTimeOffset(ClockDateTime.Subtract(value), Offset); + } + + + public long ToFileTime() + { + return UtcDateTime.ToFileTime(); + } + + public long ToUnixTimeSeconds() + { + // Truncate sub-second precision before offsetting by the Unix Epoch to avoid + // the last digit being off by one for dates that result in negative Unix times. + // + // For example, consider the DateTimeOffset 12/31/1969 12:59:59.001 +0 + // ticks = 621355967990010000 + // ticksFromEpoch = ticks - DateTime.UnixEpochTicks = -9990000 + // secondsFromEpoch = ticksFromEpoch / TimeSpan.TicksPerSecond = 0 + // + // Notice that secondsFromEpoch is rounded *up* by the truncation induced by integer division, + // whereas we actually always want to round *down* when converting to Unix time. This happens + // automatically for positive Unix time values. Now the example becomes: + // seconds = ticks / TimeSpan.TicksPerSecond = 62135596799 + // secondsFromEpoch = seconds - UnixEpochSeconds = -1 + // + // In other words, we want to consistently round toward the time 1/1/0001 00:00:00, + // rather than toward the Unix Epoch (1/1/1970 00:00:00). + long seconds = UtcDateTime.Ticks / TimeSpan.TicksPerSecond; + return seconds - UnixEpochSeconds; + } + + public long ToUnixTimeMilliseconds() + { + // Truncate sub-millisecond precision before offsetting by the Unix Epoch to avoid + // the last digit being off by one for dates that result in negative Unix times + long milliseconds = UtcDateTime.Ticks / TimeSpan.TicksPerMillisecond; + return milliseconds - UnixEpochMilliseconds; + } + + public DateTimeOffset ToLocalTime() + { + return ToLocalTime(false); + } + + internal DateTimeOffset ToLocalTime(bool throwOnOverflow) + { + return new DateTimeOffset(UtcDateTime.ToLocalTime(throwOnOverflow)); + } + + public override String ToString() + { + return DateTimeFormat.Format(ClockDateTime, null, DateTimeFormatInfo.CurrentInfo, Offset); + } + + public String ToString(String format) + { + return DateTimeFormat.Format(ClockDateTime, format, DateTimeFormatInfo.CurrentInfo, Offset); + } + + public String ToString(IFormatProvider formatProvider) + { + return DateTimeFormat.Format(ClockDateTime, null, DateTimeFormatInfo.GetInstance(formatProvider), Offset); + } + + public String ToString(String format, IFormatProvider formatProvider) + { + return DateTimeFormat.Format(ClockDateTime, format, DateTimeFormatInfo.GetInstance(formatProvider), Offset); + } + + // TODO https://github.com/dotnet/corefx/issues/25337: Remove this overload once corefx is updated to target the new signatures + public bool TryFormat(Span destination, out int charsWritten, string format, IFormatProvider provider) => + TryFormat(destination, out charsWritten, (ReadOnlySpan)format, provider); + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider formatProvider = null) => + DateTimeFormat.TryFormat(ClockDateTime, destination, out charsWritten, format, DateTimeFormatInfo.GetInstance(formatProvider), Offset); + + public DateTimeOffset ToUniversalTime() + { + return new DateTimeOffset(UtcDateTime); + } + + public static Boolean TryParse(String input, out DateTimeOffset result) + { + TimeSpan offset; + DateTime dateResult; + Boolean parsed = DateTimeParse.TryParse(input, + DateTimeFormatInfo.CurrentInfo, + DateTimeStyles.None, + out dateResult, + out offset); + result = new DateTimeOffset(dateResult.Ticks, offset); + return parsed; + } + + public static bool TryParse(ReadOnlySpan input, out DateTimeOffset result) + { + bool parsed = DateTimeParse.TryParse(input, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None, out DateTime dateResult, out TimeSpan offset); + result = new DateTimeOffset(dateResult.Ticks, offset); + return parsed; + } + + public static Boolean TryParse(String input, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result) + { + styles = ValidateStyles(styles, nameof(styles)); + if (input == null) + { + result = default(DateTimeOffset); + return false; + } + + TimeSpan offset; + DateTime dateResult; + Boolean parsed = DateTimeParse.TryParse(input, + DateTimeFormatInfo.GetInstance(formatProvider), + styles, + out dateResult, + out offset); + result = new DateTimeOffset(dateResult.Ticks, offset); + return parsed; + } + + public static bool TryParse(ReadOnlySpan input, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result) + { + styles = ValidateStyles(styles, nameof(styles)); + bool parsed = DateTimeParse.TryParse(input, DateTimeFormatInfo.GetInstance(formatProvider), styles, out DateTime dateResult, out TimeSpan offset); + result = new DateTimeOffset(dateResult.Ticks, offset); + return parsed; + } + + public static Boolean TryParseExact(String input, String format, IFormatProvider formatProvider, DateTimeStyles styles, + out DateTimeOffset result) + { + styles = ValidateStyles(styles, nameof(styles)); + if (input == null || format == null) + { + result = default(DateTimeOffset); + return false; + } + + TimeSpan offset; + DateTime dateResult; + Boolean parsed = DateTimeParse.TryParseExact(input, + format, + DateTimeFormatInfo.GetInstance(formatProvider), + styles, + out dateResult, + out offset); + result = new DateTimeOffset(dateResult.Ticks, offset); + return parsed; + } + + // TODO https://github.com/dotnet/corefx/issues/25337: Remove this overload once corefx is updated to target the new signatures + public static bool TryParseExact(ReadOnlySpan input, string format, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result) + { + if (format == null) + { + result = default; + return false; + } + + return TryParseExact(input, (ReadOnlySpan)format, formatProvider, styles, out result); + } + + public static bool TryParseExact( + ReadOnlySpan input, ReadOnlySpan format, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result) + { + styles = ValidateStyles(styles, nameof(styles)); + bool parsed = DateTimeParse.TryParseExact(input, format, DateTimeFormatInfo.GetInstance(formatProvider), styles, out DateTime dateResult, out TimeSpan offset); + result = new DateTimeOffset(dateResult.Ticks, offset); + return parsed; + } + + public static Boolean TryParseExact(String input, String[] formats, IFormatProvider formatProvider, DateTimeStyles styles, + out DateTimeOffset result) + { + styles = ValidateStyles(styles, nameof(styles)); + if (input == null) + { + result = default(DateTimeOffset); + return false; + } + + TimeSpan offset; + DateTime dateResult; + Boolean parsed = DateTimeParse.TryParseExactMultiple(input, + formats, + DateTimeFormatInfo.GetInstance(formatProvider), + styles, + out dateResult, + out offset); + result = new DateTimeOffset(dateResult.Ticks, offset); + return parsed; + } + + public static bool TryParseExact( + ReadOnlySpan input, string[] formats, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result) + { + styles = ValidateStyles(styles, nameof(styles)); + bool parsed = DateTimeParse.TryParseExactMultiple(input, formats, DateTimeFormatInfo.GetInstance(formatProvider), styles, out DateTime dateResult, out TimeSpan offset); + result = new DateTimeOffset(dateResult.Ticks, offset); + return parsed; + } + + // Ensures the TimeSpan is valid to go in a DateTimeOffset. + private static Int16 ValidateOffset(TimeSpan offset) + { + Int64 ticks = offset.Ticks; + if (ticks % TimeSpan.TicksPerMinute != 0) + { + throw new ArgumentException(SR.Argument_OffsetPrecision, nameof(offset)); + } + if (ticks < MinOffset || ticks > MaxOffset) + { + throw new ArgumentOutOfRangeException(nameof(offset), SR.Argument_OffsetOutOfRange); + } + return (Int16)(offset.Ticks / TimeSpan.TicksPerMinute); + } + + // Ensures that the time and offset are in range. + private static DateTime ValidateDate(DateTime dateTime, TimeSpan offset) + { + // The key validation is that both the UTC and clock times fit. The clock time is validated + // by the DateTime constructor. + Debug.Assert(offset.Ticks >= MinOffset && offset.Ticks <= MaxOffset, "Offset not validated."); + + // This operation cannot overflow because offset should have already been validated to be within + // 14 hours and the DateTime instance is more than that distance from the boundaries of Int64. + Int64 utcTicks = dateTime.Ticks - offset.Ticks; + if (utcTicks < DateTime.MinTicks || utcTicks > DateTime.MaxTicks) + { + throw new ArgumentOutOfRangeException(nameof(offset), SR.Argument_UTCOutOfRange); + } + // make sure the Kind is set to Unspecified + // + return new DateTime(utcTicks, DateTimeKind.Unspecified); + } + + private static DateTimeStyles ValidateStyles(DateTimeStyles style, String parameterName) + { + if ((style & DateTimeFormatInfo.InvalidDateTimeStyles) != 0) + { + throw new ArgumentException(SR.Argument_InvalidDateTimeStyles, parameterName); + } + if (((style & (DateTimeStyles.AssumeLocal)) != 0) && ((style & (DateTimeStyles.AssumeUniversal)) != 0)) + { + throw new ArgumentException(SR.Argument_ConflictingDateTimeStyles, parameterName); + } + if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) + { + throw new ArgumentException(SR.Argument_DateTimeOffsetInvalidDateTimeStyles, parameterName); + } + + // RoundtripKind does not make sense for DateTimeOffset; ignore this flag for backward compatibility with DateTime + style &= ~DateTimeStyles.RoundtripKind; + + // AssumeLocal is also ignored as that is what we do by default with DateTimeOffset.Parse + style &= ~DateTimeStyles.AssumeLocal; + + return style; + } + + // Operators + + public static implicit operator DateTimeOffset(DateTime dateTime) + { + return new DateTimeOffset(dateTime); + } + + public static DateTimeOffset operator +(DateTimeOffset dateTimeOffset, TimeSpan timeSpan) + { + return new DateTimeOffset(dateTimeOffset.ClockDateTime + timeSpan, dateTimeOffset.Offset); + } + + + public static DateTimeOffset operator -(DateTimeOffset dateTimeOffset, TimeSpan timeSpan) + { + return new DateTimeOffset(dateTimeOffset.ClockDateTime - timeSpan, dateTimeOffset.Offset); + } + + public static TimeSpan operator -(DateTimeOffset left, DateTimeOffset right) + { + return left.UtcDateTime - right.UtcDateTime; + } + + public static bool operator ==(DateTimeOffset left, DateTimeOffset right) + { + return left.UtcDateTime == right.UtcDateTime; + } + + public static bool operator !=(DateTimeOffset left, DateTimeOffset right) + { + return left.UtcDateTime != right.UtcDateTime; + } + + public static bool operator <(DateTimeOffset left, DateTimeOffset right) + { + return left.UtcDateTime < right.UtcDateTime; + } + + public static bool operator <=(DateTimeOffset left, DateTimeOffset right) + { + return left.UtcDateTime <= right.UtcDateTime; + } + + public static bool operator >(DateTimeOffset left, DateTimeOffset right) + { + return left.UtcDateTime > right.UtcDateTime; + } + + public static bool operator >=(DateTimeOffset left, DateTimeOffset right) + { + return left.UtcDateTime >= right.UtcDateTime; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/DayOfWeek.cs b/external/corefx/src/Common/src/CoreLib/System/DayOfWeek.cs new file mode 100644 index 0000000000..f67d10e181 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/DayOfWeek.cs @@ -0,0 +1,26 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: Enum for the day of the week. +** +** +============================================================*/ + +namespace System +{ + public enum DayOfWeek + { + Sunday = 0, + Monday = 1, + Tuesday = 2, + Wednesday = 3, + Thursday = 4, + Friday = 5, + Saturday = 6, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/DefaultBinder.cs b/external/corefx/src/Common/src/CoreLib/System/DefaultBinder.cs new file mode 100644 index 0000000000..f8d8c0c168 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/DefaultBinder.cs @@ -0,0 +1,1221 @@ +// 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.Reflection; +using System.Diagnostics; +using CultureInfo = System.Globalization.CultureInfo; + +namespace System +{ +#if CORERT + public sealed +#else + internal +#endif + partial class DefaultBinder : Binder + { + // This method is passed a set of methods and must choose the best + // fit. The methods all have the same number of arguments and the object + // array args. On exit, this method will choice the best fit method + // and coerce the args to match that method. By match, we mean all primitive + // arguments are exact matchs and all object arguments are exact or subclasses + // of the target. If the target OR is an interface, the object must implement + // that interface. There are a couple of exceptions + // thrown when a method cannot be returned. If no method matchs the args and + // ArgumentException is thrown. If multiple methods match the args then + // an AmbiguousMatchException is thrown. + // + // The most specific match will be selected. + // + public sealed override MethodBase BindToMethod( + BindingFlags bindingAttr, MethodBase[] match, ref object[] args, + ParameterModifier[] modifiers, CultureInfo cultureInfo, string[] names, out object state) + { + if (match == null || match.Length == 0) + throw new ArgumentException(SR.Arg_EmptyArray, nameof(match)); + + MethodBase[] candidates = (MethodBase[])match.Clone(); + + int i; + int j; + + state = null; + +#region Map named parameters to candidate parameter positions + // We are creating an paramOrder array to act as a mapping + // between the order of the args and the actual order of the + // parameters in the method. This order may differ because + // named parameters (names) may change the order. If names + // is not provided, then we assume the default mapping (0,1,...) + int[][] paramOrder = new int[candidates.Length][]; + + for (i = 0; i < candidates.Length; i++) + { + ParameterInfo[] par = candidates[i].GetParametersNoCopy(); + + // args.Length + 1 takes into account the possibility of a last paramArray that can be omitted + paramOrder[i] = new int[(par.Length > args.Length) ? par.Length : args.Length]; + + if (names == null) + { + // Default mapping + for (j = 0; j < args.Length; j++) + paramOrder[i][j] = j; + } + else + { + // Named parameters, reorder the mapping. If CreateParamOrder fails, it means that the method + // doesn't have a name that matchs one of the named parameters so we don't consider it any further. + if (!CreateParamOrder(paramOrder[i], par, names)) + candidates[i] = null; + } + } +#endregion + + Type[] paramArrayTypes = new Type[candidates.Length]; + + Type[] argTypes = new Type[args.Length]; + +#region Cache the type of the provided arguments + // object that contain a null are treated as if they were typeless (but match either object + // references or value classes). We mark this condition by placing a null in the argTypes array. + for (i = 0; i < args.Length; i++) + { + if (args[i] != null) + { + argTypes[i] = args[i].GetType(); + } + } +#endregion + + + // Find the method that matches... + int CurIdx = 0; + bool defaultValueBinding = ((bindingAttr & BindingFlags.OptionalParamBinding) != 0); + + Type paramArrayType = null; + +#region Filter methods by parameter count and type + for (i = 0; i < candidates.Length; i++) + { + paramArrayType = null; + + // If we have named parameters then we may have a hole in the candidates array. + if (candidates[i] == null) + continue; + + // Validate the parameters. + ParameterInfo[] par = candidates[i].GetParametersNoCopy(); + +#region Match method by parameter count + if (par.Length == 0) + { +#region No formal parameters + if (args.Length != 0) + { + if ((candidates[i].CallingConvention & CallingConventions.VarArgs) == 0) + continue; + } + + // This is a valid routine so we move it up the candidates list. + paramOrder[CurIdx] = paramOrder[i]; + candidates[CurIdx++] = candidates[i]; + + continue; +#endregion + } + else if (par.Length > args.Length) + { +#region Shortage of provided parameters + // If the number of parameters is greater than the number of args then + // we are in the situation were we may be using default values. + for (j = args.Length; j < par.Length - 1; j++) + { + if (par[j].DefaultValue == System.DBNull.Value) + break; + } + + if (j != par.Length - 1) + continue; + + if (par[j].DefaultValue == System.DBNull.Value) + { + if (!par[j].ParameterType.IsArray) + continue; + + if (!par[j].IsDefined(typeof(ParamArrayAttribute), true)) + continue; + + paramArrayType = par[j].ParameterType.GetElementType(); + } +#endregion + } + else if (par.Length < args.Length) + { +#region Excess provided parameters + // test for the ParamArray case + int lastArgPos = par.Length - 1; + + if (!par[lastArgPos].ParameterType.IsArray) + continue; + + if (!par[lastArgPos].IsDefined(typeof(ParamArrayAttribute), true)) + continue; + + if (paramOrder[i][lastArgPos] != lastArgPos) + continue; + + paramArrayType = par[lastArgPos].ParameterType.GetElementType(); +#endregion + } + else + { +#region Test for paramArray, save paramArray type + int lastArgPos = par.Length - 1; + + if (par[lastArgPos].ParameterType.IsArray + && par[lastArgPos].IsDefined(typeof(ParamArrayAttribute), true) + && paramOrder[i][lastArgPos] == lastArgPos) + { + if (!par[lastArgPos].ParameterType.IsAssignableFrom(argTypes[lastArgPos])) + paramArrayType = par[lastArgPos].ParameterType.GetElementType(); + } +#endregion + } +#endregion + + Type pCls = null; + int argsToCheck = (paramArrayType != null) ? par.Length - 1 : args.Length; + +#region Match method by parameter type + for (j = 0; j < argsToCheck; j++) + { +#region Classic argument coersion checks + // get the formal type + pCls = par[j].ParameterType; + + if (pCls.IsByRef) + pCls = pCls.GetElementType(); + + // the type is the same + if (pCls == argTypes[paramOrder[i][j]]) + continue; + + // a default value is available + if (defaultValueBinding && args[paramOrder[i][j]] == Type.Missing) + continue; + + // the argument was null, so it matches with everything + if (args[paramOrder[i][j]] == null) + continue; + + // the type is Object, so it will match everything + if (pCls == typeof(object)) + continue; + + // now do a "classic" type check + if (pCls.IsPrimitive) + { + if (argTypes[paramOrder[i][j]] == null || !CanChangePrimitiveObjectToType(args[paramOrder[i][j]], pCls)) + { + break; + } + } + else + { + if (argTypes[paramOrder[i][j]] == null) + continue; + + if (!pCls.IsAssignableFrom(argTypes[paramOrder[i][j]])) + { + if (argTypes[paramOrder[i][j]].IsCOMObject) + { + if (pCls.IsInstanceOfType(args[paramOrder[i][j]])) + continue; + } + break; + } + } +#endregion + } + + if (paramArrayType != null && j == par.Length - 1) + { +#region Check that excess arguments can be placed in the param array + for (; j < args.Length; j++) + { + if (paramArrayType.IsPrimitive) + { + if (argTypes[j] == null || !CanChangePrimitiveObjectToType(args[j], paramArrayType)) + break; + } + else + { + if (argTypes[j] == null) + continue; + + if (!paramArrayType.IsAssignableFrom(argTypes[j])) + { + if (argTypes[j].IsCOMObject) + { + if (paramArrayType.IsInstanceOfType(args[j])) + continue; + } + + break; + } + } + } +#endregion + } +#endregion + + if (j == args.Length) + { +#region This is a valid routine so we move it up the candidates list + paramOrder[CurIdx] = paramOrder[i]; + paramArrayTypes[CurIdx] = paramArrayType; + candidates[CurIdx++] = candidates[i]; +#endregion + } + } +#endregion + + // If we didn't find a method + if (CurIdx == 0) + throw new MissingMethodException(SR.MissingMember); + + if (CurIdx == 1) + { +#region Found only one method + if (names != null) + { + state = new BinderState((int[])paramOrder[0].Clone(), args.Length, paramArrayTypes[0] != null); + ReorderParams(paramOrder[0], args); + } + + // If the parameters and the args are not the same length or there is a paramArray + // then we need to create a argument array. + ParameterInfo[] parms = candidates[0].GetParametersNoCopy(); + + if (parms.Length == args.Length) + { + if (paramArrayTypes[0] != null) + { + object[] objs = new object[parms.Length]; + int lastPos = parms.Length - 1; + Array.Copy(args, 0, objs, 0, lastPos); + objs[lastPos] = Array.CreateInstance(paramArrayTypes[0], 1); + ((Array)objs[lastPos]).SetValue(args[lastPos], 0); + args = objs; + } + } + else if (parms.Length > args.Length) + { + object[] objs = new object[parms.Length]; + + for (i = 0; i < args.Length; i++) + objs[i] = args[i]; + + for (; i < parms.Length - 1; i++) + objs[i] = parms[i].DefaultValue; + + if (paramArrayTypes[0] != null) + objs[i] = Array.CreateInstance(paramArrayTypes[0], 0); // create an empty array for the + + else + objs[i] = parms[i].DefaultValue; + + args = objs; + } + else + { + if ((candidates[0].CallingConvention & CallingConventions.VarArgs) == 0) + { + object[] objs = new object[parms.Length]; + int paramArrayPos = parms.Length - 1; + Array.Copy(args, 0, objs, 0, paramArrayPos); + objs[paramArrayPos] = Array.CreateInstance(paramArrayTypes[0], args.Length - paramArrayPos); + Array.Copy(args, paramArrayPos, (System.Array)objs[paramArrayPos], 0, args.Length - paramArrayPos); + args = objs; + } + } +#endregion + + return candidates[0]; + } + + int currentMin = 0; + bool ambig = false; + for (i = 1; i < CurIdx; i++) + { +#region Walk all of the methods looking the most specific method to invoke + int newMin = FindMostSpecificMethod(candidates[currentMin], paramOrder[currentMin], paramArrayTypes[currentMin], + candidates[i], paramOrder[i], paramArrayTypes[i], argTypes, args); + + if (newMin == 0) + { + ambig = true; + } + else if (newMin == 2) + { + currentMin = i; + ambig = false; + } +#endregion + } + + if (ambig) + throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException); + + // Reorder (if needed) + if (names != null) + { + state = new BinderState((int[])paramOrder[currentMin].Clone(), args.Length, paramArrayTypes[currentMin] != null); + ReorderParams(paramOrder[currentMin], args); + } + + // If the parameters and the args are not the same length or there is a paramArray + // then we need to create a argument array. + ParameterInfo[] parameters = candidates[currentMin].GetParametersNoCopy(); + if (parameters.Length == args.Length) + { + if (paramArrayTypes[currentMin] != null) + { + object[] objs = new object[parameters.Length]; + int lastPos = parameters.Length - 1; + Array.Copy(args, 0, objs, 0, lastPos); + objs[lastPos] = Array.CreateInstance(paramArrayTypes[currentMin], 1); + ((Array)objs[lastPos]).SetValue(args[lastPos], 0); + args = objs; + } + } + else if (parameters.Length > args.Length) + { + object[] objs = new object[parameters.Length]; + + for (i = 0; i < args.Length; i++) + objs[i] = args[i]; + + for (; i < parameters.Length - 1; i++) + objs[i] = parameters[i].DefaultValue; + + if (paramArrayTypes[currentMin] != null) + { + objs[i] = Array.CreateInstance(paramArrayTypes[currentMin], 0); + } + else + { + objs[i] = parameters[i].DefaultValue; + } + + args = objs; + } + else + { + if ((candidates[currentMin].CallingConvention & CallingConventions.VarArgs) == 0) + { + object[] objs = new object[parameters.Length]; + int paramArrayPos = parameters.Length - 1; + Array.Copy(args, 0, objs, 0, paramArrayPos); + objs[paramArrayPos] = Array.CreateInstance(paramArrayTypes[currentMin], args.Length - paramArrayPos); + Array.Copy(args, paramArrayPos, (System.Array)objs[paramArrayPos], 0, args.Length - paramArrayPos); + args = objs; + } + } + + return candidates[currentMin]; + } + + + // Given a set of fields that match the base criteria, select a field. + // if value is null then we have no way to select a field + public sealed override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo cultureInfo) + { + if (match == null) + { + throw new ArgumentNullException(nameof(match)); + } + + int i; + // Find the method that match... + int CurIdx = 0; + + Type valueType = null; + + FieldInfo[] candidates = (FieldInfo[])match.Clone(); + + // If we are a FieldSet, then use the value's type to disambiguate + if ((bindingAttr & BindingFlags.SetField) != 0) + { + valueType = value.GetType(); + + for (i = 0; i < candidates.Length; i++) + { + Type pCls = candidates[i].FieldType; + if (pCls == valueType) + { + candidates[CurIdx++] = candidates[i]; + continue; + } + if (value == Empty.Value) + { + // the object passed in was null which would match any non primitive non value type + if (pCls.IsClass) + { + candidates[CurIdx++] = candidates[i]; + continue; + } + } + if (pCls == typeof(object)) + { + candidates[CurIdx++] = candidates[i]; + continue; + } + if (pCls.IsPrimitive) + { + if (CanChangePrimitiveObjectToType(value, pCls)) + { + candidates[CurIdx++] = candidates[i]; + continue; + } + } + else + { + if (pCls.IsAssignableFrom(valueType)) + { + candidates[CurIdx++] = candidates[i]; + continue; + } + } + } + if (CurIdx == 0) + throw new MissingFieldException(SR.MissingField); + if (CurIdx == 1) + return candidates[0]; + } + + // Walk all of the methods looking the most specific method to invoke + int currentMin = 0; + bool ambig = false; + for (i = 1; i < CurIdx; i++) + { + int newMin = FindMostSpecificField(candidates[currentMin], candidates[i]); + if (newMin == 0) + ambig = true; + else + { + if (newMin == 2) + { + currentMin = i; + ambig = false; + } + } + } + if (ambig) + throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException); + return candidates[currentMin]; + } + + // Given a set of methods that match the base criteria, select a method based + // upon an array of types. This method should return null if no method matchs + // the criteria. + public sealed override MethodBase SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers) + { + int i; + int j; + + Type[] realTypes = new Type[types.Length]; + for (i = 0; i < types.Length; i++) + { + realTypes[i] = types[i].UnderlyingSystemType; + if (!(realTypes[i].IsRuntimeImplemented() || realTypes[i] is SignatureType)) + throw new ArgumentException(SR.Arg_MustBeType, nameof(types)); + } + types = realTypes; + + // We don't automatically jump out on exact match. + if (match == null || match.Length == 0) + throw new ArgumentException(SR.Arg_EmptyArray, nameof(match)); + + MethodBase[] candidates = (MethodBase[])match.Clone(); + + // Find all the methods that can be described by the types parameter. + // Remove all of them that cannot. + int CurIdx = 0; + for (i = 0; i < candidates.Length; i++) + { + ParameterInfo[] par = candidates[i].GetParametersNoCopy(); + if (par.Length != types.Length) + continue; + for (j = 0; j < types.Length; j++) + { + Type pCls = par[j].ParameterType; + if (types[j].MatchesParameterTypeExactly(par[j])) + continue; + if (pCls == typeof(object)) + continue; + + Type type = types[j]; + if (type is SignatureType signatureType) + { + if (!(candidates[i] is MethodInfo methodInfo)) + break; + type = signatureType.TryResolveAgainstGenericMethod(methodInfo); + if (type == null) + break; + } + + if (pCls.IsPrimitive) + { + if (!(type.UnderlyingSystemType.IsRuntimeImplemented()) || + !CanChangePrimitive(type.UnderlyingSystemType, pCls.UnderlyingSystemType)) + break; + } + else + { + if (!pCls.IsAssignableFrom(type)) + break; + } + } + if (j == types.Length) + candidates[CurIdx++] = candidates[i]; + } + if (CurIdx == 0) + return null; + if (CurIdx == 1) + return candidates[0]; + + // Walk all of the methods looking the most specific method to invoke + int currentMin = 0; + bool ambig = false; + int[] paramOrder = new int[types.Length]; + for (i = 0; i < types.Length; i++) + paramOrder[i] = i; + for (i = 1; i < CurIdx; i++) + { + int newMin = FindMostSpecificMethod(candidates[currentMin], paramOrder, null, candidates[i], paramOrder, null, types, null); + if (newMin == 0) + ambig = true; + else + { + if (newMin == 2) + { + currentMin = i; + ambig = false; + currentMin = i; + } + } + } + if (ambig) + throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException); + return candidates[currentMin]; + } + + // Given a set of properties that match the base criteria, select one. + public sealed override PropertyInfo SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, Type returnType, + Type[] indexes, ParameterModifier[] modifiers) + { + // Allow a null indexes array. But if it is not null, every element must be non-null as well. + if (indexes != null) + { + foreach (Type index in indexes) + { + if (index == null) + throw new ArgumentNullException(nameof(indexes)); + } + } + + if (match == null || match.Length == 0) + throw new ArgumentException(SR.Arg_EmptyArray, nameof(match)); + + PropertyInfo[] candidates = (PropertyInfo[])match.Clone(); + + int i, j = 0; + + // Find all the properties that can be described by type indexes parameter + int CurIdx = 0; + int indexesLength = (indexes != null) ? indexes.Length : 0; + for (i = 0; i < candidates.Length; i++) + { + if (indexes != null) + { + ParameterInfo[] par = candidates[i].GetIndexParameters(); + if (par.Length != indexesLength) + continue; + + for (j = 0; j < indexesLength; j++) + { + Type pCls = par[j].ParameterType; + + // If the classes exactly match continue + if (pCls == indexes[j]) + continue; + if (pCls == typeof(object)) + continue; + + if (pCls.IsPrimitive) + { + if (!(indexes[j].UnderlyingSystemType.IsRuntimeImplemented()) || + !CanChangePrimitive(indexes[j].UnderlyingSystemType, pCls.UnderlyingSystemType)) + break; + } + else + { + if (!pCls.IsAssignableFrom(indexes[j])) + break; + } + } + } + + if (j == indexesLength) + { + if (returnType != null) + { + if (candidates[i].PropertyType.IsPrimitive) + { + if (!(returnType.UnderlyingSystemType.IsRuntimeImplemented()) || + !CanChangePrimitive(returnType.UnderlyingSystemType, candidates[i].PropertyType.UnderlyingSystemType)) + continue; + } + else + { + if (!candidates[i].PropertyType.IsAssignableFrom(returnType)) + continue; + } + } + candidates[CurIdx++] = candidates[i]; + } + } + if (CurIdx == 0) + return null; + if (CurIdx == 1) + return candidates[0]; + + // Walk all of the properties looking the most specific method to invoke + int currentMin = 0; + bool ambig = false; + int[] paramOrder = new int[indexesLength]; + for (i = 0; i < indexesLength; i++) + paramOrder[i] = i; + for (i = 1; i < CurIdx; i++) + { + int newMin = FindMostSpecificType(candidates[currentMin].PropertyType, candidates[i].PropertyType, returnType); + if (newMin == 0 && indexes != null) + newMin = FindMostSpecific(candidates[currentMin].GetIndexParameters(), + paramOrder, + null, + candidates[i].GetIndexParameters(), + paramOrder, + null, + indexes, + null); + if (newMin == 0) + { + newMin = FindMostSpecificProperty(candidates[currentMin], candidates[i]); + if (newMin == 0) + ambig = true; + } + if (newMin == 2) + { + ambig = false; + currentMin = i; + } + } + + if (ambig) + throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException); + return candidates[currentMin]; + } + + // ChangeType + // The default binder doesn't support any change type functionality. + // This is because the default is built into the low level invoke code. + public override object ChangeType(object value, Type type, CultureInfo cultureInfo) + { + throw new NotSupportedException(SR.NotSupported_ChangeType); + } + + public sealed override void ReorderArgumentArray(ref object[] args, object state) + { + BinderState binderState = (BinderState)state; + ReorderParams(binderState._argsMap, args); + if (binderState._isParamArray) + { + int paramArrayPos = args.Length - 1; + if (args.Length == binderState._originalSize) + args[paramArrayPos] = ((object[])args[paramArrayPos])[0]; + else + { + // must be args.Length < state.originalSize + object[] newArgs = new object[args.Length]; + Array.Copy(args, 0, newArgs, 0, paramArrayPos); + for (int i = paramArrayPos, j = 0; i < newArgs.Length; i++, j++) + { + newArgs[i] = ((object[])args[paramArrayPos])[j]; + } + args = newArgs; + } + } + else + { + if (args.Length > binderState._originalSize) + { + object[] newArgs = new object[binderState._originalSize]; + Array.Copy(args, 0, newArgs, 0, binderState._originalSize); + args = newArgs; + } + } + } + + // Return any exact bindings that may exist. (This method is not defined on the + // Binder and is used by RuntimeType.) + public static MethodBase ExactBinding(MethodBase[] match, Type[] types, ParameterModifier[] modifiers) + { + if (match == null) + throw new ArgumentNullException(nameof(match)); + + MethodBase[] aExactMatches = new MethodBase[match.Length]; + int cExactMatches = 0; + + for (int i = 0; i < match.Length; i++) + { + ParameterInfo[] par = match[i].GetParametersNoCopy(); + if (par.Length == 0) + { + continue; + } + int j; + for (j = 0; j < types.Length; j++) + { + Type pCls = par[j].ParameterType; + + // If the classes exactly match continue + if (!pCls.Equals(types[j])) + break; + } + if (j < types.Length) + continue; + + // Add the exact match to the array of exact matches. + aExactMatches[cExactMatches] = match[i]; + cExactMatches++; + } + + if (cExactMatches == 0) + return null; + + if (cExactMatches == 1) + return aExactMatches[0]; + + return FindMostDerivedNewSlotMeth(aExactMatches, cExactMatches); + } + + // Return any exact bindings that may exist. (This method is not defined on the + // Binder and is used by RuntimeType.) + public static PropertyInfo ExactPropertyBinding(PropertyInfo[] match, Type returnType, Type[] types, ParameterModifier[] modifiers) + { + if (match == null) + throw new ArgumentNullException(nameof(match)); + + PropertyInfo bestMatch = null; + int typesLength = (types != null) ? types.Length : 0; + for (int i = 0; i < match.Length; i++) + { + ParameterInfo[] par = match[i].GetIndexParameters(); + int j; + for (j = 0; j < typesLength; j++) + { + Type pCls = par[j].ParameterType; + + // If the classes exactly match continue + if (pCls != types[j]) + break; + } + if (j < typesLength) + continue; + if (returnType != null && returnType != match[i].PropertyType) + continue; + + if (bestMatch != null) + throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException); + + bestMatch = match[i]; + } + return bestMatch; + } + + private static int FindMostSpecific(ParameterInfo[] p1, int[] paramOrder1, Type paramArrayType1, + ParameterInfo[] p2, int[] paramOrder2, Type paramArrayType2, + Type[] types, object[] args) + { + // A method using params is always less specific than one not using params + if (paramArrayType1 != null && paramArrayType2 == null) return 2; + if (paramArrayType2 != null && paramArrayType1 == null) return 1; + + // now either p1 and p2 both use params or neither does. + + bool p1Less = false; + bool p2Less = false; + + for (int i = 0; i < types.Length; i++) + { + if (args != null && args[i] == Type.Missing) + continue; + + Type c1, c2; + + // If a param array is present, then either + // the user re-ordered the parameters in which case + // the argument to the param array is either an array + // in which case the params is conceptually ignored and so paramArrayType1 == null + // or the argument to the param array is a single element + // in which case paramOrder[i] == p1.Length - 1 for that element + // or the user did not re-order the parameters in which case + // the paramOrder array could contain indexes larger than p.Length - 1 (see VSW 577286) + // so any index >= p.Length - 1 is being put in the param array + + if (paramArrayType1 != null && paramOrder1[i] >= p1.Length - 1) + c1 = paramArrayType1; + else + c1 = p1[paramOrder1[i]].ParameterType; + + if (paramArrayType2 != null && paramOrder2[i] >= p2.Length - 1) + c2 = paramArrayType2; + else + c2 = p2[paramOrder2[i]].ParameterType; + + if (c1 == c2) continue; + + switch (FindMostSpecificType(c1, c2, types[i])) + { + case 0: return 0; + case 1: p1Less = true; break; + case 2: p2Less = true; break; + } + } + + // Two way p1Less and p2Less can be equal. All the arguments are the + // same they both equal false, otherwise there were things that both + // were the most specific type on.... + if (p1Less == p2Less) + { + // if we cannot tell which is a better match based on parameter types (p1Less == p2Less), + // let's see which one has the most matches without using the params array (the longer one wins). + if (!p1Less && args != null) + { + if (p1.Length > p2.Length) + { + return 1; + } + else if (p2.Length > p1.Length) + { + return 2; + } + } + + return 0; + } + else + { + return (p1Less == true) ? 1 : 2; + } + } + + private static int FindMostSpecificType(Type c1, Type c2, Type t) + { + // If the two types are exact move on... + if (c1 == c2) + return 0; + + if (t is SignatureType signatureType) + { + if (signatureType.MatchesExactly(c1)) + return 1; + + if (signatureType.MatchesExactly(c2)) + return 2; + } + else + { + if (c1 == t) + return 1; + + if (c2 == t) + return 2; + } + + bool c1FromC2; + bool c2FromC1; + + if (c1.IsByRef || c2.IsByRef) + { + if (c1.IsByRef && c2.IsByRef) + { + c1 = c1.GetElementType(); + c2 = c2.GetElementType(); + } + else if (c1.IsByRef) + { + if (c1.GetElementType() == c2) + return 2; + + c1 = c1.GetElementType(); + } + else + { + if (c2.GetElementType() == c1) + return 1; + + c2 = c2.GetElementType(); + } + } + + + if (c1.IsPrimitive && c2.IsPrimitive) + { + c1FromC2 = CanChangePrimitive(c2, c1); + c2FromC1 = CanChangePrimitive(c1, c2); + } + else + { + c1FromC2 = c1.IsAssignableFrom(c2); + c2FromC1 = c2.IsAssignableFrom(c1); + } + + if (c1FromC2 == c2FromC1) + return 0; + + if (c1FromC2) + { + return 2; + } + else + { + return 1; + } + } + + private static int FindMostSpecificMethod(MethodBase m1, int[] paramOrder1, Type paramArrayType1, + MethodBase m2, int[] paramOrder2, Type paramArrayType2, + Type[] types, object[] args) + { + // Find the most specific method based on the parameters. + int res = FindMostSpecific(m1.GetParametersNoCopy(), paramOrder1, paramArrayType1, + m2.GetParametersNoCopy(), paramOrder2, paramArrayType2, types, args); + + // If the match was not ambigous then return the result. + if (res != 0) + return res; + + // Check to see if the methods have the exact same name and signature. + if (CompareMethodSig(m1, m2)) + { + // Determine the depth of the declaring types for both methods. + int hierarchyDepth1 = GetHierarchyDepth(m1.DeclaringType); + int hierarchyDepth2 = GetHierarchyDepth(m2.DeclaringType); + + // The most derived method is the most specific one. + if (hierarchyDepth1 == hierarchyDepth2) + { + return 0; + } + else if (hierarchyDepth1 < hierarchyDepth2) + { + return 2; + } + else + { + return 1; + } + } + + // The match is ambigous. + return 0; + } + + private static int FindMostSpecificField(FieldInfo cur1, FieldInfo cur2) + { + // Check to see if the fields have the same name. + if (cur1.Name == cur2.Name) + { + int hierarchyDepth1 = GetHierarchyDepth(cur1.DeclaringType); + int hierarchyDepth2 = GetHierarchyDepth(cur2.DeclaringType); + + if (hierarchyDepth1 == hierarchyDepth2) + { + Debug.Assert(cur1.IsStatic != cur2.IsStatic, "hierarchyDepth1 == hierarchyDepth2"); + return 0; + } + else if (hierarchyDepth1 < hierarchyDepth2) + return 2; + else + return 1; + } + + // The match is ambigous. + return 0; + } + + private static int FindMostSpecificProperty(PropertyInfo cur1, PropertyInfo cur2) + { + // Check to see if the fields have the same name. + if (cur1.Name == cur2.Name) + { + int hierarchyDepth1 = GetHierarchyDepth(cur1.DeclaringType); + int hierarchyDepth2 = GetHierarchyDepth(cur2.DeclaringType); + + if (hierarchyDepth1 == hierarchyDepth2) + { + return 0; + } + else if (hierarchyDepth1 < hierarchyDepth2) + return 2; + else + return 1; + } + + // The match is ambigous. + return 0; + } + + public static bool CompareMethodSig(MethodBase m1, MethodBase m2) + { + ParameterInfo[] params1 = m1.GetParametersNoCopy(); + ParameterInfo[] params2 = m2.GetParametersNoCopy(); + + if (params1.Length != params2.Length) + return false; + + int numParams = params1.Length; + for (int i = 0; i < numParams; i++) + { + if (params1[i].ParameterType != params2[i].ParameterType) + return false; + } + + return true; + } + + private static int GetHierarchyDepth(Type t) + { + int depth = 0; + + Type currentType = t; + do + { + depth++; + currentType = currentType.BaseType; + } while (currentType != null); + + return depth; + } + + internal static MethodBase FindMostDerivedNewSlotMeth(MethodBase[] match, int cMatches) + { + int deepestHierarchy = 0; + MethodBase methWithDeepestHierarchy = null; + + for (int i = 0; i < cMatches; i++) + { + // Calculate the depth of the hierarchy of the declaring type of the + // current method. + int currentHierarchyDepth = GetHierarchyDepth(match[i].DeclaringType); + + // The two methods have the same name, signature, and hierarchy depth. + // This can only happen if at least one is vararg or generic. + if (currentHierarchyDepth == deepestHierarchy) + { + throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException); + } + + // Check to see if this method is on the most derived class. + if (currentHierarchyDepth > deepestHierarchy) + { + deepestHierarchy = currentHierarchyDepth; + methWithDeepestHierarchy = match[i]; + } + } + + return methWithDeepestHierarchy; + } + + // This method will sort the vars array into the mapping order stored + // in the paramOrder array. + private static void ReorderParams(int[] paramOrder, object[] vars) + { + object[] varsCopy = new object[vars.Length]; + for (int i = 0; i < vars.Length; i++) + varsCopy[i] = vars[i]; + + for (int i = 0; i < vars.Length; i++) + vars[i] = varsCopy[paramOrder[i]]; + } + + // This method will create the mapping between the Parameters and the underlying + // data based upon the names array. The names array is stored in the same order + // as the values and maps to the parameters of the method. We store the mapping + // from the parameters to the names in the paramOrder array. All parameters that + // don't have matching names are then stored in the array in order. + private static bool CreateParamOrder(int[] paramOrder, ParameterInfo[] pars, string[] names) + { + bool[] used = new bool[pars.Length]; + + // Mark which parameters have not been found in the names list + for (int i = 0; i < pars.Length; i++) + paramOrder[i] = -1; + // Find the parameters with names. + for (int i = 0; i < names.Length; i++) + { + int j; + for (j = 0; j < pars.Length; j++) + { + if (names[i].Equals(pars[j].Name)) + { + paramOrder[j] = i; + used[i] = true; + break; + } + } + // This is an error condition. The name was not found. This + // method must not match what we sent. + if (j == pars.Length) + return false; + } + + // Now we fill in the holes with the parameters that are unused. + int pos = 0; + for (int i = 0; i < pars.Length; i++) + { + if (paramOrder[i] == -1) + { + for (; pos < pars.Length; pos++) + { + if (!used[pos]) + { + paramOrder[i] = pos; + pos++; + break; + } + } + } + } + return true; + } + + internal class BinderState + { + internal readonly int[] _argsMap; + internal readonly int _originalSize; + internal readonly bool _isParamArray; + + internal BinderState(int[] argsMap, int originalSize, bool isParamArray) + { + _argsMap = argsMap; + _originalSize = originalSize; + _isParamArray = isParamArray; + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Diagnostics/CodeAnalysis/SuppressMessageAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/CodeAnalysis/SuppressMessageAttribute.cs new file mode 100644 index 0000000000..893d7b8bc9 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/CodeAnalysis/SuppressMessageAttribute.cs @@ -0,0 +1,39 @@ +// 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. + +/*============================================================ +** +** +** +** An attribute to suppress violation messages/warnings +** by static code analysis tools. +** +** +===========================================================*/ + +namespace System.Diagnostics.CodeAnalysis +{ + [AttributeUsage( + AttributeTargets.All, + Inherited = false, + AllowMultiple = true + ) + ] + [Conditional("CODE_ANALYSIS")] + public sealed class SuppressMessageAttribute : Attribute + { + public SuppressMessageAttribute(string category, string checkId) + { + Category = category; + CheckId = checkId; + } + + public string Category { get; } + public string CheckId { get; } + public string Scope { get; set; } + public string Target { get; set; } + public string MessageId { get; set; } + public string Justification { get; set; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Diagnostics/ConditionalAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/ConditionalAttribute.cs new file mode 100644 index 0000000000..416625b779 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/ConditionalAttribute.cs @@ -0,0 +1,17 @@ +// 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. + +namespace System.Diagnostics +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)] + public sealed class ConditionalAttribute : Attribute + { + public ConditionalAttribute(string conditionString) + { + ConditionString = conditionString; + } + + public string ConditionString { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Debug.Unix.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Debug.Unix.cs new file mode 100644 index 0000000000..495f2f713c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Debug.Unix.cs @@ -0,0 +1,95 @@ +// 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 Microsoft.Win32.SafeHandles; + +namespace System.Diagnostics +{ + public static partial class Debug + { + private static readonly bool s_shouldWriteToStdErr = Environment.GetEnvironmentVariable("COMPlus_DebugWriteToStdErr") == "1"; + + private static void ShowAssertDialog(string stackTrace, string message, string detailMessage) + { + if (Debugger.IsAttached) + { + Debugger.Break(); + } + else + { + // In Core, we do not show a dialog. + // Fail in order to avoid anyone catching an exception and masking + // an assert failure. + var ex = new DebugAssertException(message, detailMessage, stackTrace); + Environment.FailFast(ex.Message, ex); + } + } + + private static void WriteCore(string message) + { + WriteToDebugger(message); + + if (s_shouldWriteToStdErr) + { + WriteToStderr(message); + } + } + + private static void WriteToDebugger(string message) + { + if (Debugger.IsLogging()) + { + Debugger.Log(0, null, message); + } + else + { + Interop.Sys.SysLog(Interop.Sys.SysLogPriority.LOG_USER | Interop.Sys.SysLogPriority.LOG_DEBUG, "%s", message); + } + } + + private static void WriteToStderr(string message) + { + // We don't want to write UTF-16 to a file like standard error. Ideally we would transcode this + // to UTF8, but the downside of that is it pulls in a bunch of stuff into what is ideally + // a path with minimal dependencies (as to prevent re-entrency), so we'll take the strategy + // of just throwing away any non ASCII characters from the message and writing the rest + + const int BufferLength = 256; + + unsafe + { + byte* buf = stackalloc byte[BufferLength]; + int bufCount; + int i = 0; + + while (i < message.Length) + { + for (bufCount = 0; bufCount < BufferLength && i < message.Length; i++) + { + if (message[i] <= 0x7F) + { + buf[bufCount] = (byte)message[i]; + bufCount++; + } + } + + int totalBytesWritten = 0; + while (bufCount > 0) + { + int bytesWritten = Interop.Sys.Write(2 /* stderr */, buf + totalBytesWritten, bufCount); + if (bytesWritten < 0) + { + // On error, simply stop writing the debug output. This could commonly happen if stderr + // was piped to a program that ended before this program did, resulting in EPIPE errors. + return; + } + + bufCount -= bytesWritten; + totalBytesWritten += bytesWritten; + } + } + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Debug.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Debug.cs new file mode 100644 index 0000000000..5178f7f5c5 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Debug.cs @@ -0,0 +1,321 @@ +// 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. + +// Do not remove this, it is needed to retain calls to these conditional methods in release builds +#define DEBUG + +namespace System.Diagnostics +{ + /// + /// Provides a set of properties and methods for debugging code. + /// + public static partial class Debug + { + private static readonly object s_lock = new object(); + + public static bool AutoFlush { get { return true; } set { } } + + [ThreadStatic] + private static int s_indentLevel; + public static int IndentLevel + { + get + { + return s_indentLevel; + } + set + { + s_indentLevel = value < 0 ? 0 : value; + } + } + + private static int s_indentSize = 4; + public static int IndentSize + { + get + { + return s_indentSize; + } + set + { + s_indentSize = value < 0 ? 0 : value; + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Close() { } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Flush() { } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Indent() + { + IndentLevel++; + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Unindent() + { + IndentLevel--; + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Print(string message) + { + Write(message); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Print(string format, params object[] args) + { + Write(string.Format(null, format, args)); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Assert(bool condition) + { + Assert(condition, string.Empty, string.Empty); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Assert(bool condition, string message) + { + Assert(condition, message, string.Empty); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Assert(bool condition, string message, string detailMessage) + { + if (!condition) + { + string stackTrace; + + try + { + stackTrace = Internal.Runtime.Augments.EnvironmentAugments.StackTrace; + } + catch + { + stackTrace = ""; + } + + WriteLine(FormatAssert(stackTrace, message, detailMessage)); + s_ShowAssertDialog(stackTrace, message, detailMessage); + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Fail(string message) + { + Assert(false, message, string.Empty); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Fail(string message, string detailMessage) + { + Assert(false, message, detailMessage); + } + + private static string FormatAssert(string stackTrace, string message, string detailMessage) + { + string newLine = GetIndentString() + Environment.NewLine; + return SR.DebugAssertBanner + newLine + + SR.DebugAssertShortMessage + newLine + + message + newLine + + SR.DebugAssertLongMessage + newLine + + detailMessage + newLine + + stackTrace; + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Assert(bool condition, string message, string detailMessageFormat, params object[] args) + { + Assert(condition, message, string.Format(detailMessageFormat, args)); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteLine(string message) + { + Write(message + Environment.NewLine); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Write(string message) + { + lock (s_lock) + { + if (message == null) + { + s_WriteCore(string.Empty); + return; + } + if (s_needIndent) + { + message = GetIndentString() + message; + s_needIndent = false; + } + s_WriteCore(message); + if (message.EndsWith(Environment.NewLine)) + { + s_needIndent = true; + } + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteLine(object value) + { + WriteLine(value?.ToString()); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteLine(object value, string category) + { + WriteLine(value?.ToString(), category); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteLine(string format, params object[] args) + { + WriteLine(string.Format(null, format, args)); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteLine(string message, string category) + { + if (category == null) + { + WriteLine(message); + } + else + { + WriteLine(category + ":" + message); + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Write(object value) + { + Write(value?.ToString()); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Write(string message, string category) + { + if (category == null) + { + Write(message); + } + else + { + Write(category + ":" + message); + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Write(object value, string category) + { + Write(value?.ToString(), category); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteIf(bool condition, string message) + { + if (condition) + { + Write(message); + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteIf(bool condition, object value) + { + if (condition) + { + Write(value); + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteIf(bool condition, string message, string category) + { + if (condition) + { + Write(message, category); + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteIf(bool condition, object value, string category) + { + if (condition) + { + Write(value, category); + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteLineIf(bool condition, object value) + { + if (condition) + { + WriteLine(value); + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteLineIf(bool condition, object value, string category) + { + if (condition) + { + WriteLine(value, category); + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteLineIf(bool condition, string message) + { + if (condition) + { + WriteLine(message); + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteLineIf(bool condition, string message, string category) + { + if (condition) + { + WriteLine(message, category); + } + } + + private static bool s_needIndent; + + private static string s_indentString; + + private static string GetIndentString() + { + int indentCount = IndentSize * IndentLevel; + if (s_indentString?.Length == indentCount) + { + return s_indentString; + } + return s_indentString = new string(' ', indentCount); + } + + private sealed class DebugAssertException : Exception + { + internal DebugAssertException(string message, string detailMessage, string stackTrace) : + base(message + Environment.NewLine + detailMessage + Environment.NewLine + stackTrace) + { + } + } + + // internal and not readonly so that the tests can swap this out. + internal static Action s_ShowAssertDialog = ShowAssertDialog; + internal static Action s_WriteCore = WriteCore; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggableAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggableAttribute.cs new file mode 100644 index 0000000000..d05f8471b3 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggableAttribute.cs @@ -0,0 +1,54 @@ +// 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. + +namespace System.Diagnostics +{ + // Attribute class used by the compiler to mark modules. + // If present, then debugging information for everything in the + // assembly was generated by the compiler, and will be preserved + // by the Runtime so that the debugger can provide full functionality + // in the case of JIT attach. If not present, then the compiler may + // or may not have included debugging information, and the Runtime + // won't preserve the debugging info, which will make debugging after + // a JIT attach difficult. + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module, AllowMultiple = false)] + public sealed class DebuggableAttribute : Attribute + { + [Flags] + public enum DebuggingModes + { + None = 0x0, + Default = 0x1, + DisableOptimizations = 0x100, + IgnoreSymbolStoreSequencePoints = 0x2, + EnableEditAndContinue = 0x4 + } + + public DebuggableAttribute(bool isJITTrackingEnabled, bool isJITOptimizerDisabled) + { + DebuggingFlags = 0; + + if (isJITTrackingEnabled) + { + DebuggingFlags |= DebuggingModes.Default; + } + + if (isJITOptimizerDisabled) + { + DebuggingFlags |= DebuggingModes.DisableOptimizations; + } + } + + public DebuggableAttribute(DebuggingModes modes) + { + DebuggingFlags = modes; + } + + public bool IsJITTrackingEnabled => (DebuggingFlags & DebuggingModes.Default) != 0; + + public bool IsJITOptimizerDisabled => (DebuggingFlags & DebuggingModes.DisableOptimizations) != 0; + + public DebuggingModes DebuggingFlags { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerBrowsableAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerBrowsableAttribute.cs new file mode 100644 index 0000000000..b9d62225a9 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerBrowsableAttribute.cs @@ -0,0 +1,41 @@ +// 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. + +namespace System.Diagnostics +{ + // DebuggerBrowsableState states are defined as follows: + // Never never show this element + // Expanded expansion of the class is done, so that all visible internal members are shown + // Collapsed expansion of the class is not performed. Internal visible members are hidden + // RootHidden The target element itself should not be shown, but should instead be + // automatically expanded to have its members displayed. + // Default value is collapsed + + // Please also change the code which validates DebuggerBrowsableState variable (in this file) + // if you change this enum. + public enum DebuggerBrowsableState + { + Never = 0, + //Expanded is not supported in this release + //Expanded = 1, + Collapsed = 2, + RootHidden = 3 + } + + + // the one currently supported with the csee.dat + // (mcee.dat, autoexp.dat) file. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)] + public sealed class DebuggerBrowsableAttribute : Attribute + { + public DebuggerBrowsableAttribute(DebuggerBrowsableState state) + { + if (state < DebuggerBrowsableState.Never || state > DebuggerBrowsableState.RootHidden) + throw new ArgumentOutOfRangeException(nameof(state)); + + State = state; + } + public DebuggerBrowsableState State { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerDisplayAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerDisplayAttribute.cs new file mode 100644 index 0000000000..7aae4b9397 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerDisplayAttribute.cs @@ -0,0 +1,51 @@ +// 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. + +namespace System.Diagnostics +{ + // This attribute is used to control what is displayed for the given class or field + // in the data windows in the debugger. The single argument to this attribute is + // the string that will be displayed in the value column for instances of the type. + // This string can include text between { and } which can be either a field, + // property or method (as will be documented in mscorlib). In the C# case, + // a general expression will be allowed which only has implicit access to the this pointer + // for the current instance of the target type. The expression will be limited, + // however: there is no access to aliases, locals, or pointers. + // In addition, attributes on properties referenced in the expression are not processed. + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class DebuggerDisplayAttribute : Attribute + { + private Type _target; + + public DebuggerDisplayAttribute(string value) + { + Value = value ?? ""; + Name = ""; + Type = ""; + } + + public string Value { get; } + + public string Name { get; set; } + + public string Type { get; set; } + + public Type Target + { + get => _target; + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + TargetTypeName = value.AssemblyQualifiedName; + _target = value; + } + } + + public string TargetTypeName { get; set; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerHiddenAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerHiddenAttribute.cs new file mode 100644 index 0000000000..ace452e911 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerHiddenAttribute.cs @@ -0,0 +1,12 @@ +// 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. + +namespace System.Diagnostics +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Constructor, Inherited = false)] + public sealed class DebuggerHiddenAttribute : Attribute + { + public DebuggerHiddenAttribute() { } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerNonUserCodeAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerNonUserCodeAttribute.cs new file mode 100644 index 0000000000..1b61cb7262 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerNonUserCodeAttribute.cs @@ -0,0 +1,12 @@ +// 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. + +namespace System.Diagnostics +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Constructor | AttributeTargets.Struct, Inherited = false)] + public sealed class DebuggerNonUserCodeAttribute : Attribute + { + public DebuggerNonUserCodeAttribute() { } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerStepThroughAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerStepThroughAttribute.cs new file mode 100644 index 0000000000..82a164771e --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerStepThroughAttribute.cs @@ -0,0 +1,16 @@ +// 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. + +namespace System.Diagnostics +{ +#if PROJECTN + // Used by the IL2IL toolchain to mark generated code to control debugger stepping policy + [System.Runtime.CompilerServices.DependencyReductionRoot] +#endif + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false)] + public sealed class DebuggerStepThroughAttribute : Attribute + { + public DebuggerStepThroughAttribute() { } + } +} diff --git a/external/corefx/src/System.Diagnostics.Debug/src/System/Diagnostics/DebuggerStepperBoundaryAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerStepperBoundaryAttribute.cs similarity index 90% rename from external/corefx/src/System.Diagnostics.Debug/src/System/Diagnostics/DebuggerStepperBoundaryAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerStepperBoundaryAttribute.cs index 4d8a0ca0fe..647f2fdb00 100644 --- a/external/corefx/src/System.Diagnostics.Debug/src/System/Diagnostics/DebuggerStepperBoundaryAttribute.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerStepperBoundaryAttribute.cs @@ -6,8 +6,8 @@ namespace System.Diagnostics { /// Indicates the code following the attribute is to be executed in run, not step, mode. [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method, Inherited = false)] - [Serializable] public sealed class DebuggerStepperBoundaryAttribute : Attribute { + public DebuggerStepperBoundaryAttribute() { } } } diff --git a/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerTypeProxyAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerTypeProxyAttribute.cs new file mode 100644 index 0000000000..445834e056 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerTypeProxyAttribute.cs @@ -0,0 +1,46 @@ +// 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. + +namespace System.Diagnostics +{ + [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class DebuggerTypeProxyAttribute : Attribute + { + private Type _target; + + public DebuggerTypeProxyAttribute(Type type) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + ProxyTypeName = type.AssemblyQualifiedName; + } + + public DebuggerTypeProxyAttribute(string typeName) + { + ProxyTypeName = typeName; + } + + public string ProxyTypeName { get; } + + public Type Target + { + get => _target; + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + TargetTypeName = value.AssemblyQualifiedName; + _target = value; + } + } + + public string TargetTypeName { get; set; } + } +} diff --git a/external/corefx/src/System.Diagnostics.Debug/src/System/Diagnostics/DebuggerVisualizerAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerVisualizerAttribute.cs similarity index 100% rename from external/corefx/src/System.Diagnostics.Debug/src/System/Diagnostics/DebuggerVisualizerAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/DebuggerVisualizerAttribute.cs diff --git a/external/corefx/src/Common/src/CoreLib/System/Diagnostics/StackTraceHiddenAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/StackTraceHiddenAttribute.cs new file mode 100644 index 0000000000..474274ac08 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/StackTraceHiddenAttribute.cs @@ -0,0 +1,12 @@ +// 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. + +namespace System.Diagnostics +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Struct, Inherited = false)] + internal sealed class StackTraceHiddenAttribute : Attribute + { + public StackTraceHiddenAttribute() { } + } +} diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/ActivityTracker.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/ActivityTracker.cs similarity index 99% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/ActivityTracker.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/ActivityTracker.cs index bb52a9bb94..9ac32c3bd6 100644 --- a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/ActivityTracker.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/ActivityTracker.cs @@ -525,7 +525,7 @@ namespace System.Diagnostics.Tracing /// private static unsafe void WriteNibble(ref byte* ptr, byte* endPtr, uint value) { - Debug.Assert(0 <= value && value < 16); + Debug.Assert(value < 16); Debug.Assert(ptr < endPtr); if (*ptr != 0) diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/EventActivityOptions.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/EventActivityOptions.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/EventActivityOptions.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/EventActivityOptions.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/EventDescriptor.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/EventDescriptor.cs similarity index 94% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/EventDescriptor.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/EventDescriptor.cs index 8fb471a99f..b036b28b4b 100644 --- a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/EventDescriptor.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/EventDescriptor.cs @@ -89,12 +89,12 @@ namespace System.Diagnostics.Tracing { if (id < 0) { - throw new ArgumentOutOfRangeException(nameof(id), Resources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(id), SR.ArgumentOutOfRange_NeedNonNegNum); } if (id > ushort.MaxValue) { - throw new ArgumentOutOfRangeException(nameof(id), Resources.GetResourceString("ArgumentOutOfRange_NeedValidId", 1, ushort.MaxValue)); + throw new ArgumentOutOfRangeException(nameof(id), SR.Format(SR.ArgumentOutOfRange_NeedValidId, 1, ushort.MaxValue)); } m_traceloggingId = 0; @@ -107,12 +107,12 @@ namespace System.Diagnostics.Tracing if (task < 0) { - throw new ArgumentOutOfRangeException(nameof(task), Resources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(task), SR.ArgumentOutOfRange_NeedNonNegNum); } if (task > ushort.MaxValue) { - throw new ArgumentOutOfRangeException(nameof(task), Resources.GetResourceString("ArgumentOutOfRange_NeedValidId", 1, ushort.MaxValue)); + throw new ArgumentOutOfRangeException(nameof(task), SR.Format(SR.ArgumentOutOfRange_NeedValidId, 1, ushort.MaxValue)); } m_task = (ushort)task; diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/EventProvider.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/EventProvider.cs similarity index 99% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/EventProvider.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/EventProvider.cs index a74125a35a..64c2491769 100644 --- a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/EventProvider.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/EventProvider.cs @@ -149,7 +149,7 @@ namespace System.Diagnostics.Tracing status = EventRegister(eventSource, m_etwCallback); if (status != 0) { -#if PLATFORM_WINDOWS +#if PLATFORM_WINDOWS && !ES_BUILD_STANDALONE throw new ArgumentException(Interop.Kernel32.GetMessage(unchecked((int)status))); #else throw new ArgumentException(Convert.ToString(unchecked((int)status))); diff --git a/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/EventSource.cs.REMOVED.git-id b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/EventSource.cs.REMOVED.git-id new file mode 100644 index 0000000000..4f26205c3c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/EventSource.cs.REMOVED.git-id @@ -0,0 +1 @@ +f6d48998436a4b293130436942ccb5891b740605 \ No newline at end of file diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/EventSourceException.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/EventSourceException.cs similarity index 85% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/EventSourceException.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/EventSourceException.cs index 51222dad3c..be1bf3940a 100644 --- a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/EventSourceException.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/EventSourceException.cs @@ -19,16 +19,16 @@ namespace System.Diagnostics.Tracing /// /// Exception that is thrown when an error occurs during EventSource operation. /// -#if !CORECLR && !ES_BUILD_PN && !ES_BUILD_PCL && !CORERT +#if !ES_BUILD_PCL [Serializable] -#endif // !CORECLR && !ES_BUILD_PN && !ES_BUILD_PCL && !CORERT +#endif public class EventSourceException : Exception { /// /// Initializes a new instance of the EventSourceException class. /// public EventSourceException() : - base(Resources.GetResourceString("EventSource_ListenerWriteFailure")) { } + base(SR.EventSource_ListenerWriteFailure) { } /// /// Initializes a new instance of the EventSourceException class with a specified error message. @@ -49,6 +49,6 @@ namespace System.Diagnostics.Tracing #endif internal EventSourceException(Exception innerException) : - base(Resources.GetResourceString("EventSource_ListenerWriteFailure"), innerException) { } + base(SR.EventSource_ListenerWriteFailure, innerException) { } } } diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/IEventProvider.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/IEventProvider.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/IEventProvider.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/IEventProvider.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/StubEnvironment.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/StubEnvironment.cs similarity index 95% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/StubEnvironment.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/StubEnvironment.cs index 9d363dadd7..1b3ca8004c 100644 --- a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/StubEnvironment.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/StubEnvironment.cs @@ -52,29 +52,6 @@ namespace System.Diagnostics.Tracing.Internal #if ES_BUILD_AGAINST_DOTNET_V35 -namespace Microsoft.Diagnostics.Contracts.Internal -{ - internal class Contract - { - public static void Assert(bool invariant) - { - Assert(invariant, string.Empty); - } - public static void Assert(bool invariant, string message) - { - if (!invariant) - { - if (System.Diagnostics.Debugger.IsAttached) - System.Diagnostics.Debugger.Break(); - throw new Exception("Assertion failed: " + message); - } - } - public static void EndContractBlock() - { } - } -} - - namespace Microsoft.Internal { using System.Text; diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/ArrayTypeInfo.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/ArrayTypeInfo.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/ArrayTypeInfo.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/ArrayTypeInfo.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/ConcurrentSet.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/ConcurrentSet.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/ConcurrentSet.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/ConcurrentSet.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/ConcurrentSetItem.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/ConcurrentSetItem.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/ConcurrentSetItem.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/ConcurrentSetItem.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/DataCollector.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/DataCollector.cs similarity index 95% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/DataCollector.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/DataCollector.cs index 840a2f8753..1444c267cb 100644 --- a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/DataCollector.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/DataCollector.cs @@ -86,7 +86,7 @@ namespace System.Diagnostics.Tracing var scratchNew = scratchOld + size; if (this.scratchEnd < scratchNew) { - throw new IndexOutOfRangeException(Resources.GetResourceString("EventSource_AddScalarOutOfRange")); + throw new IndexOutOfRangeException(SR.EventSource_AddScalarOutOfRange); } this.ScalarsBegin(); @@ -273,13 +273,13 @@ namespace System.Diagnostics.Tracing var pinsTemp = this.pins; if (this.pinsEnd <= pinsTemp) { - throw new IndexOutOfRangeException(Resources.GetResourceString("EventSource_PinArrayOutOfRange")); + throw new IndexOutOfRangeException(SR.EventSource_PinArrayOutOfRange); } var datasTemp = this.datas; if (this.datasEnd <= datasTemp) { - throw new IndexOutOfRangeException(Resources.GetResourceString("EventSource_DataDescriptorsOutOfRange")); + throw new IndexOutOfRangeException(SR.EventSource_DataDescriptorsOutOfRange); } this.pins = pinsTemp + 1; @@ -297,7 +297,7 @@ namespace System.Diagnostics.Tracing var datasTemp = this.datas; if (this.datasEnd <= datasTemp) { - throw new IndexOutOfRangeException(Resources.GetResourceString("EventSource_DataDescriptorsOutOfRange")); + throw new IndexOutOfRangeException(SR.EventSource_DataDescriptorsOutOfRange); } datasTemp->DataPointer = (IntPtr) this.scratch; diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/EmptyStruct.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/EmptyStruct.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/EmptyStruct.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/EmptyStruct.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/EnumHelper.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/EnumHelper.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/EnumHelper.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/EnumHelper.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/EnumerableTypeInfo.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/EnumerableTypeInfo.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/EnumerableTypeInfo.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/EnumerableTypeInfo.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/EventDataAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/EventDataAttribute.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/EventDataAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/EventDataAttribute.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/EventFieldAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/EventFieldAttribute.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/EventFieldAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/EventFieldAttribute.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/EventFieldFormat.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/EventFieldFormat.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/EventFieldFormat.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/EventFieldFormat.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/EventIgnoreAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/EventIgnoreAttribute.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/EventIgnoreAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/EventIgnoreAttribute.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/EventPayload.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs similarity index 98% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/EventPayload.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs index 5967ad6ab5..30a941195f 100644 --- a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/EventPayload.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs @@ -2,6 +2,7 @@ // 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.Collections.Generic; using System.Collections; using System.Diagnostics; @@ -53,7 +54,7 @@ namespace System.Diagnostics.Tracing position++; } - throw new System.Collections.Generic.KeyNotFoundException(); + throw new System.Collections.Generic.KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key.ToString())); } set { diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/EventSourceActivity.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs similarity index 99% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/EventSourceActivity.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs index acc3eeb233..865082f767 100644 --- a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/EventSourceActivity.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs @@ -36,7 +36,6 @@ namespace System.Diagnostics.Tracing { if (eventSource == null) throw new ArgumentNullException(nameof(eventSource)); - Contract.EndContractBlock(); this.eventSource = eventSource; } diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/EventSourceOptions.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/EventSourceOptions.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/EventSourceOptions.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/EventSourceOptions.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/FieldMetadata.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/FieldMetadata.cs similarity index 93% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/FieldMetadata.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/FieldMetadata.cs index f84a1fd703..9c7c6369ec 100644 --- a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/FieldMetadata.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/FieldMetadata.cs @@ -129,17 +129,17 @@ namespace System.Diagnostics.Tracing { if (coreType == (int)TraceLoggingDataType.Nil) { - throw new NotSupportedException(Resources.GetResourceString("EventSource_NotSupportedArrayOfNil")); + throw new NotSupportedException(SR.EventSource_NotSupportedArrayOfNil); } if (coreType == (int)TraceLoggingDataType.Binary) { - throw new NotSupportedException(Resources.GetResourceString("EventSource_NotSupportedArrayOfBinary")); + throw new NotSupportedException(SR.EventSource_NotSupportedArrayOfBinary); } #if !BROKEN_UNTIL_M3 if (coreType == (int)TraceLoggingDataType.Utf16String || coreType == (int)TraceLoggingDataType.MbcsString) { - throw new NotSupportedException(Resources.GetResourceString("EventSource_NotSupportedArrayOfNullTerminatedString")); + throw new NotSupportedException(SR.EventSource_NotSupportedArrayOfNullTerminatedString); } #endif } @@ -161,7 +161,7 @@ namespace System.Diagnostics.Tracing this.outType++; if ((this.outType & Statics.OutTypeMask) == 0) { - throw new NotSupportedException(Resources.GetResourceString("EventSource_TooManyFields")); + throw new NotSupportedException(SR.EventSource_TooManyFields); } } diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/InvokeTypeInfo.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/InvokeTypeInfo.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/InvokeTypeInfo.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/InvokeTypeInfo.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/NameInfo.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/NameInfo.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/NameInfo.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/NameInfo.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/PropertyAnalysis.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/PropertyAnalysis.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/PropertyAnalysis.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/PropertyAnalysis.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/PropertyValue.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs similarity index 98% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/PropertyValue.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs index 854bc06bb6..ae60888493 100644 --- a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/PropertyValue.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs @@ -1,4 +1,8 @@ -using System; +// 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.Reflection; using System.Runtime.InteropServices; using System.Diagnostics; @@ -27,7 +31,7 @@ namespace System.Diagnostics.Tracing #else internal #endif - unsafe struct PropertyValue + unsafe readonly struct PropertyValue { /// /// Union of well-known value types, to avoid boxing those types. diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/SimpleEventTypes.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/SimpleEventTypes.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/SimpleEventTypes.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/SimpleEventTypes.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/SimpleTypeInfos.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/SimpleTypeInfos.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/SimpleTypeInfos.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/SimpleTypeInfos.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/Statics.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/Statics.cs similarity index 99% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/Statics.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/Statics.cs index d95e316768..05539ab4fd 100644 --- a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/Statics.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/Statics.cs @@ -532,7 +532,7 @@ namespace System.Diagnostics.Tracing if (recursionCheck.Contains(dataType)) { - throw new NotSupportedException(Resources.GetResourceString("EventSource_RecursiveTypeDefinition")); + throw new NotSupportedException(SR.EventSource_RecursiveTypeDefinition); } recursionCheck.Add(dataType); @@ -715,7 +715,7 @@ namespace System.Diagnostics.Tracing } else { - throw new ArgumentException(Resources.GetResourceString("EventSource_NonCompliantTypeError", dataType.Name)); + throw new ArgumentException(SR.Format(SR.EventSource_NonCompliantTypeError, dataType.Name)); } } } diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/TraceLoggingDataCollector.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataCollector.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/TraceLoggingDataCollector.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataCollector.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/TraceLoggingDataType.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/TraceLoggingDataType.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/TraceLoggingEventSource.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs similarity index 98% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/TraceLoggingEventSource.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs index a8017bb76f..bf29d71844 100644 --- a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/TraceLoggingEventSource.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs @@ -103,7 +103,6 @@ namespace System.Diagnostics.Tracing { throw new ArgumentNullException(nameof(eventSourceName)); } - Contract.EndContractBlock(); } /// @@ -118,8 +117,6 @@ namespace System.Diagnostics.Tracing throw new ArgumentNullException(nameof(eventName)); } - Contract.EndContractBlock(); - if (!this.IsEnabled()) { return; @@ -145,8 +142,6 @@ namespace System.Diagnostics.Tracing throw new ArgumentNullException(nameof(eventName)); } - Contract.EndContractBlock(); - if (!this.IsEnabled()) { return; @@ -780,7 +775,7 @@ namespace System.Diagnostics.Tracing } else { - throw new ArgumentException(Resources.GetResourceString("UnknownEtwTrait", etwTrait), "traits"); + throw new ArgumentException(SR.Format(SR.EventSource_UnknownEtwTrait, etwTrait), "traits"); } } string value = m_traits[i + 1]; @@ -823,7 +818,7 @@ namespace System.Diagnostics.Tracing { if (!(i + 1 < value.Length)) { - throw new ArgumentException(Resources.GetResourceString("EvenHexDigits"), "traits"); + throw new ArgumentException(SR.EventSource_EvenHexDigits, "traits"); } metaData.Add((byte)(HexDigit(value[i]) * 16 + HexDigit(value[i + 1]))); i++; @@ -836,7 +831,7 @@ namespace System.Diagnostics.Tracing } else { - throw new ArgumentException(Resources.GetResourceString("IllegalValue", value), "traits"); + throw new ArgumentException(SR.Format(SR.EventSource_IllegalValue, value), "traits"); } return metaData.Count - startPos; @@ -860,7 +855,7 @@ namespace System.Diagnostics.Tracing return (c - 'A' + 10); } - throw new ArgumentException(Resources.GetResourceString("BadHexDigit", c), "traits"); + throw new ArgumentException(SR.Format(SR.EventSource_BadHexDigit, c), "traits"); } private NameInfo UpdateDescriptor( diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/TraceLoggingEventTraits.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTraits.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/TraceLoggingEventTraits.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTraits.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/TraceLoggingEventTypes.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTypes.cs similarity index 97% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/TraceLoggingEventTypes.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTypes.cs index c2239671bb..3c775a3cef 100644 --- a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/TraceLoggingEventTypes.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTypes.cs @@ -97,8 +97,6 @@ namespace System.Diagnostics.Tracing throw new ArgumentNullException(nameof(name)); } - Contract.EndContractBlock(); - this.typeInfos = MakeArray(paramInfos); this.name = name; this.tags = tags; @@ -135,8 +133,6 @@ namespace System.Diagnostics.Tracing throw new ArgumentNullException(nameof(defaultName)); } - Contract.EndContractBlock(); - this.typeInfos = typeInfos; this.name = defaultName; this.tags = tags; @@ -215,8 +211,6 @@ namespace System.Diagnostics.Tracing throw new ArgumentNullException(nameof(paramInfos)); } - Contract.EndContractBlock(); - var recursionCheck = new List(paramInfos.Length); var result = new TraceLoggingTypeInfo[paramInfos.Length]; for (int i = 0; i < paramInfos.Length; ++i) @@ -234,8 +228,6 @@ namespace System.Diagnostics.Tracing throw new ArgumentNullException(nameof(types)); } - Contract.EndContractBlock(); - var recursionCheck = new List(types.Length); var result = new TraceLoggingTypeInfo[types.Length]; for (int i = 0; i < types.Length; i++) @@ -254,8 +246,6 @@ namespace System.Diagnostics.Tracing throw new ArgumentNullException(nameof(typeInfos)); } - Contract.EndContractBlock(); - return (TraceLoggingTypeInfo[])typeInfos.Clone(); ; } } diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/TraceLoggingMetadataCollector.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/TraceLoggingMetadataCollector.cs similarity index 96% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/TraceLoggingMetadataCollector.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/TraceLoggingMetadataCollector.cs index 41225c8626..1db1a28c9d 100644 --- a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/TraceLoggingMetadataCollector.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/TraceLoggingMetadataCollector.cs @@ -232,7 +232,7 @@ namespace System.Diagnostics.Tracing if (this.BeginningBufferedArray) { - throw new NotSupportedException(Resources.GetResourceString("EventSource_NotSupportedNestedArraysEnums")); + throw new NotSupportedException(SR.EventSource_NotSupportedNestedArraysEnums); } this.impl.AddScalar(2); @@ -244,7 +244,7 @@ namespace System.Diagnostics.Tracing { if (this.bufferedArrayFieldCount >= 0) { - throw new NotSupportedException(Resources.GetResourceString("EventSource_NotSupportedNestedArraysEnums")); + throw new NotSupportedException(SR.EventSource_NotSupportedNestedArraysEnums); } this.bufferedArrayFieldCount = 0; @@ -255,7 +255,7 @@ namespace System.Diagnostics.Tracing { if (this.bufferedArrayFieldCount != 1) { - throw new InvalidOperationException(Resources.GetResourceString("EventSource_IncorrentlyAuthoredTypeInfo")); + throw new InvalidOperationException(SR.EventSource_IncorrentlyAuthoredTypeInfo); } this.bufferedArrayFieldCount = int.MinValue; @@ -274,7 +274,7 @@ namespace System.Diagnostics.Tracing { if (this.BeginningBufferedArray) { - throw new NotSupportedException(Resources.GetResourceString("EventSource_NotSupportedCustomSerializedData")); + throw new NotSupportedException(SR.EventSource_NotSupportedCustomSerializedData); } this.impl.AddScalar(2); diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/TraceLoggingTypeInfo.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/TraceLoggingTypeInfo.cs similarity index 98% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/TraceLoggingTypeInfo.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/TraceLoggingTypeInfo.cs index d68e106b0b..511a4fe480 100644 --- a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/TraceLoggingTypeInfo.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/TraceLoggingTypeInfo.cs @@ -39,8 +39,6 @@ namespace System.Diagnostics.Tracing throw new ArgumentNullException(nameof(dataType)); } - Contract.EndContractBlock(); - this.name = dataType.Name; this.dataType = dataType; this.propertyValueFactory = PropertyValue.GetFactory(dataType); @@ -64,8 +62,6 @@ namespace System.Diagnostics.Tracing throw new ArgumentNullException(nameof(name)); } - Contract.EndContractBlock(); - Statics.CheckName(name); this.name = name; diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/TypeAnalysis.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/TypeAnalysis.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/TraceLogging/TypeAnalysis.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/TraceLogging/TypeAnalysis.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/Winmeta.cs b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/Winmeta.cs similarity index 94% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/Winmeta.cs rename to external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/Winmeta.cs index ac756b6059..c60ca5b365 100644 --- a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/Winmeta.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Diagnostics/Tracing/Winmeta.cs @@ -55,9 +55,6 @@ namespace System.Diagnostics.Tracing /// /// WindowsEventTask. Custom values must be in the range from 1 through 65534 /// -#if (!ES_BUILD_STANDALONE && !ES_BUILD_PN) - [System.Runtime.CompilerServices.FriendAccessAllowed] -#endif public enum EventTask { /// @@ -68,9 +65,6 @@ namespace System.Diagnostics.Tracing /// /// EventOpcode. Custom values must be in the range from 11 through 239 /// -#if (!ES_BUILD_STANDALONE && !ES_BUILD_PN) - [System.Runtime.CompilerServices.FriendAccessAllowed] -#endif public enum EventOpcode { /// @@ -124,9 +118,6 @@ namespace System.Diagnostics.Tracing /// EventChannel. Custom values must be in the range from 16 through 255. Currently only predefined values allowed. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1028:EnumStorageShouldBeInt32", Justification = "Backwards compatibility")] -#if (!ES_BUILD_STANDALONE && !ES_BUILD_PN) - [System.Runtime.CompilerServices.FriendAccessAllowed] -#endif public enum EventChannel : byte { /// diff --git a/external/corefx/src/Common/src/CoreLib/System/DivideByZeroException.cs b/external/corefx/src/Common/src/CoreLib/System/DivideByZeroException.cs new file mode 100644 index 0000000000..b309695ff3 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/DivideByZeroException.cs @@ -0,0 +1,44 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: Exception class for bad arithmetic conditions! +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class DivideByZeroException : ArithmeticException + { + public DivideByZeroException() + : base(SR.Arg_DivideByZero) + { + HResult = HResults.COR_E_DIVIDEBYZERO; + } + + public DivideByZeroException(String message) + : base(message) + { + HResult = HResults.COR_E_DIVIDEBYZERO; + } + + public DivideByZeroException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_DIVIDEBYZERO; + } + + protected DivideByZeroException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/DllNotFoundException.cs b/external/corefx/src/Common/src/CoreLib/System/DllNotFoundException.cs new file mode 100644 index 0000000000..14fb50d9c5 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/DllNotFoundException.cs @@ -0,0 +1,45 @@ +// 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. + +/*============================================================================= +** +** Class: DllNotFoundException +** +** +** Purpose: The exception class for some failed P/Invoke calls. +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class DllNotFoundException : TypeLoadException + { + public DllNotFoundException() + : base(SR.Arg_DllNotFoundException) + { + HResult = HResults.COR_E_DLLNOTFOUND; + } + + public DllNotFoundException(String message) + : base(message) + { + HResult = HResults.COR_E_DLLNOTFOUND; + } + + public DllNotFoundException(String message, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_DLLNOTFOUND; + } + + protected DllNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Double.cs b/external/corefx/src/Common/src/CoreLib/System/Double.cs new file mode 100644 index 0000000000..3652963ef6 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Double.cs @@ -0,0 +1,448 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: A representation of an IEEE double precision +** floating point number. +** +** +===========================================================*/ + +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Double : IComparable, IConvertible, IFormattable, IComparable, IEquatable, ISpanFormattable + { + private double m_value; // Do not rename (binary serialization) + + // + // Public Constants + // + public const double MinValue = -1.7976931348623157E+308; + public const double MaxValue = 1.7976931348623157E+308; + + // Note Epsilon should be a double whose hex representation is 0x1 + // on little endian machines. + public const double Epsilon = 4.9406564584124654E-324; + public const double NegativeInfinity = (double)-1.0 / (double)(0.0); + public const double PositiveInfinity = (double)1.0 / (double)(0.0); + public const double NaN = (double)0.0 / (double)0.0; + + // We use this explicit definition to avoid the confusion between 0.0 and -0.0. + internal const double NegativeZero = -0.0; + + /// Determines whether the specified value is finite (zero, subnormal, or normal). + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsFinite(double d) + { + var bits = BitConverter.DoubleToInt64Bits(d); + return (bits & 0x7FFFFFFFFFFFFFFF) < 0x7FF0000000000000; + } + + /// Determines whether the specified value is infinite. + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsInfinity(double d) + { + var bits = BitConverter.DoubleToInt64Bits(d); + return (bits & 0x7FFFFFFFFFFFFFFF) == 0x7FF0000000000000; + } + + /// Determines whether the specified value is NaN. + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsNaN(double d) + { + var bits = BitConverter.DoubleToInt64Bits(d); + return (bits & 0x7FFFFFFFFFFFFFFF) > 0x7FF0000000000000; + } + + /// Determines whether the specified value is negative. + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsNegative(double d) + { + var bits = unchecked((ulong)BitConverter.DoubleToInt64Bits(d)); + return (bits & 0x8000000000000000) == 0x8000000000000000; + } + + /// Determines whether the specified value is negative infinity. + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNegativeInfinity(double d) + { + return (d == double.NegativeInfinity); + } + + /// Determines whether the specified value is normal. + [NonVersionable] + // This is probably not worth inlining, it has branches and should be rarely called + public unsafe static bool IsNormal(double d) + { + var bits = BitConverter.DoubleToInt64Bits(d); + bits &= 0x7FFFFFFFFFFFFFFF; + return (bits < 0x7FF0000000000000) && (bits != 0) && ((bits & 0x7FF0000000000000) != 0); + } + + /// Determines whether the specified value is positive infinity. + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsPositiveInfinity(double d) + { + return (d == double.PositiveInfinity); + } + + /// Determines whether the specified value is subnormal. + [NonVersionable] + // This is probably not worth inlining, it has branches and should be rarely called + public unsafe static bool IsSubnormal(double d) + { + var bits = BitConverter.DoubleToInt64Bits(d); + bits &= 0x7FFFFFFFFFFFFFFF; + return (bits < 0x7FF0000000000000) && (bits != 0) && ((bits & 0x7FF0000000000000) == 0); + } + + // Compares this object to another object, returning an instance of System.Relation. + // Null is considered less than any instance. + // + // If object is not of type Double, this method throws an ArgumentException. + // + // Returns a value less than zero if this object + // + public int CompareTo(Object value) + { + if (value == null) + { + return 1; + } + if (value is Double) + { + double d = (double)value; + if (m_value < d) return -1; + if (m_value > d) return 1; + if (m_value == d) return 0; + + // At least one of the values is NaN. + if (IsNaN(m_value)) + return (IsNaN(d) ? 0 : -1); + else + return 1; + } + throw new ArgumentException(SR.Arg_MustBeDouble); + } + + public int CompareTo(Double value) + { + if (m_value < value) return -1; + if (m_value > value) return 1; + if (m_value == value) return 0; + + // At least one of the values is NaN. + if (IsNaN(m_value)) + return (IsNaN(value) ? 0 : -1); + else + return 1; + } + + // True if obj is another Double with the same value as the current instance. This is + // a method of object equality, that only returns true if obj is also a double. + public override bool Equals(Object obj) + { + if (!(obj is Double)) + { + return false; + } + double temp = ((Double)obj).m_value; + // This code below is written this way for performance reasons i.e the != and == check is intentional. + if (temp == m_value) + { + return true; + } + return IsNaN(temp) && IsNaN(m_value); + } + + [NonVersionable] + public static bool operator ==(Double left, Double right) + { + return left == right; + } + + [NonVersionable] + public static bool operator !=(Double left, Double right) + { + return left != right; + } + + [NonVersionable] + public static bool operator <(Double left, Double right) + { + return left < right; + } + + [NonVersionable] + public static bool operator >(Double left, Double right) + { + return left > right; + } + + [NonVersionable] + public static bool operator <=(Double left, Double right) + { + return left <= right; + } + + [NonVersionable] + public static bool operator >=(Double left, Double right) + { + return left >= right; + } + + public bool Equals(Double obj) + { + if (obj == m_value) + { + return true; + } + return IsNaN(obj) && IsNaN(m_value); + } + + //The hashcode for a double is the absolute value of the integer representation + //of that double. + // + public unsafe override int GetHashCode() + { + double d = m_value; + if (d == 0) + { + // Ensure that 0 and -0 have the same hash code + return 0; + } + long value = *(long*)(&d); + return unchecked((int)value) ^ ((int)(value >> 32)); + } + + public override String ToString() + { + return Number.FormatDouble(m_value, null, NumberFormatInfo.CurrentInfo); + } + + public String ToString(String format) + { + return Number.FormatDouble(m_value, format, NumberFormatInfo.CurrentInfo); + } + + public String ToString(IFormatProvider provider) + { + return Number.FormatDouble(m_value, null, NumberFormatInfo.GetInstance(provider)); + } + + public String ToString(String format, IFormatProvider provider) + { + return Number.FormatDouble(m_value, format, NumberFormatInfo.GetInstance(provider)); + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) + { + return Number.TryFormatDouble(m_value, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); + } + + public static double Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseDouble(s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo); + } + + public static double Parse(String s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseDouble(s, style, NumberFormatInfo.CurrentInfo); + } + + public static double Parse(String s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseDouble(s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.GetInstance(provider)); + } + + public static double Parse(String s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseDouble(s, style, NumberFormatInfo.GetInstance(provider)); + } + + // Parses a double from a String in the given style. If + // a NumberFormatInfo isn't specified, the current culture's + // NumberFormatInfo is assumed. + // + // This method will not throw an OverflowException, but will return + // PositiveInfinity or NegativeInfinity for a number that is too + // large or too small. + + public static double Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return Number.ParseDouble(s, style, NumberFormatInfo.GetInstance(provider)); + } + + + + public static bool TryParse(String s, out double result) + { + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(ReadOnlySpan s, out double result) + { + return TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out double result) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out double result) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + private static bool TryParse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out double result) + { + bool success = Number.TryParseDouble(s, style, info, out result); + if (!success) + { + ReadOnlySpan sTrim = StringSpanHelpers.Trim(s); + if (StringSpanHelpers.Equals(sTrim, info.PositiveInfinitySymbol)) + { + result = PositiveInfinity; + } + else if (StringSpanHelpers.Equals(sTrim, info.NegativeInfinitySymbol)) + { + result = NegativeInfinity; + } + else if (StringSpanHelpers.Equals(sTrim, info.NaNSymbol)) + { + result = NaN; + } + else + { + return false; // We really failed + } + } + return true; + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.Double; + } + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Double", "Char")); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(m_value); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(m_value); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return m_value; + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Double", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/DuplicateWaitObjectException.cs b/external/corefx/src/Common/src/CoreLib/System/DuplicateWaitObjectException.cs new file mode 100644 index 0000000000..77303846a3 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/DuplicateWaitObjectException.cs @@ -0,0 +1,66 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: Exception class for duplicate objects in WaitAll/WaitAny. +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + // The DuplicateWaitObjectException is thrown when an object + // appears more than once in the list of objects to WaitAll or WaitAny. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class DuplicateWaitObjectException : ArgumentException + { + private static volatile String s_duplicateWaitObjectMessage = null; + + private static String DuplicateWaitObjectMessage + { + get + { + if (s_duplicateWaitObjectMessage == null) + s_duplicateWaitObjectMessage = SR.Arg_DuplicateWaitObjectException; + return s_duplicateWaitObjectMessage; + } + } + + // Creates a new DuplicateWaitObjectException with its message + // string set to a default message. + public DuplicateWaitObjectException() + : base(DuplicateWaitObjectMessage) + { + HResult = HResults.COR_E_DUPLICATEWAITOBJECT; + } + + public DuplicateWaitObjectException(String parameterName) + : base(DuplicateWaitObjectMessage, parameterName) + { + HResult = HResults.COR_E_DUPLICATEWAITOBJECT; + } + + public DuplicateWaitObjectException(String parameterName, String message) + : base(message, parameterName) + { + HResult = HResults.COR_E_DUPLICATEWAITOBJECT; + } + + public DuplicateWaitObjectException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_DUPLICATEWAITOBJECT; + } + + protected DuplicateWaitObjectException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Empty.cs b/external/corefx/src/Common/src/CoreLib/System/Empty.cs new file mode 100644 index 0000000000..186b92078e --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Empty.cs @@ -0,0 +1,25 @@ +// 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. + +namespace System +{ +#if CORERT + public // Needs to be public so that Reflection.Core can see it. +#else + internal +#endif + sealed class Empty + { + private Empty() + { + } + + public static readonly Empty Value = new Empty(); + + public override string ToString() + { + return string.Empty; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/EntryPointNotFoundException.cs b/external/corefx/src/Common/src/CoreLib/System/EntryPointNotFoundException.cs new file mode 100644 index 0000000000..dac1cdb971 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/EntryPointNotFoundException.cs @@ -0,0 +1,45 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: The exception class for some failed P/Invoke calls. +** +** +=============================================================================*/ + +using System; +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class EntryPointNotFoundException : TypeLoadException + { + public EntryPointNotFoundException() + : base(SR.Arg_EntryPointNotFoundException) + { + HResult = HResults.COR_E_ENTRYPOINTNOTFOUND; + } + + public EntryPointNotFoundException(String message) + : base(message) + { + HResult = HResults.COR_E_ENTRYPOINTNOTFOUND; + } + + public EntryPointNotFoundException(String message, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_ENTRYPOINTNOTFOUND; + } + + protected EntryPointNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/EventArgs.cs b/external/corefx/src/Common/src/CoreLib/System/EventArgs.cs new file mode 100644 index 0000000000..f3561a8d0b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/EventArgs.cs @@ -0,0 +1,20 @@ +// 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; + +namespace System +{ + // The base class for all event classes. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class EventArgs + { + public static readonly EventArgs Empty = new EventArgs(); + + public EventArgs() + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/EventHandler.cs b/external/corefx/src/Common/src/CoreLib/System/EventHandler.cs new file mode 100644 index 0000000000..3d1cbfef26 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/EventHandler.cs @@ -0,0 +1,12 @@ +// 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; + +namespace System +{ + public delegate void EventHandler(Object sender, EventArgs e); + + public delegate void EventHandler(Object sender, TEventArgs e); // Removed TEventArgs constraint post-.NET 4 +} diff --git a/external/corefx/src/Common/src/CoreLib/System/ExecutionEngineException.cs b/external/corefx/src/Common/src/CoreLib/System/ExecutionEngineException.cs new file mode 100644 index 0000000000..5edd5cf19f --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ExecutionEngineException.cs @@ -0,0 +1,50 @@ +// 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. + +// +// +/*============================================================================= +** +** +** +** Purpose: The exception class for misc execution engine exceptions. +** Currently, its only used as a placeholder type when the EE +** does a FailFast. +** +** +=============================================================================*/ + +using System; +using System.Runtime.Serialization; + +namespace System +{ + [Obsolete("This type previously indicated an unspecified fatal error in the runtime. The runtime no longer raises this exception so this type is obsolete.")] + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class ExecutionEngineException : SystemException + { + public ExecutionEngineException() + : base(SR.Arg_ExecutionEngineException) + { + HResult = HResults.COR_E_EXECUTIONENGINE; + } + + public ExecutionEngineException(String message) + : base(message) + { + HResult = HResults.COR_E_EXECUTIONENGINE; + } + + public ExecutionEngineException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_EXECUTIONENGINE; + } + + internal ExecutionEngineException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/FieldAccessException.cs b/external/corefx/src/Common/src/CoreLib/System/FieldAccessException.cs new file mode 100644 index 0000000000..cb28264d61 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/FieldAccessException.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. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** Purpose: The exception class for class loading failures. +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class FieldAccessException : MemberAccessException + { + public FieldAccessException() + : base(SR.Arg_FieldAccessException) + { + HResult = HResults.COR_E_FIELDACCESS; + } + + public FieldAccessException(String message) + : base(message) + { + HResult = HResults.COR_E_FIELDACCESS; + } + + public FieldAccessException(String message, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_FIELDACCESS; + } + + protected FieldAccessException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/FlagsAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/FlagsAttribute.cs new file mode 100644 index 0000000000..4f3ab36bfd --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/FlagsAttribute.cs @@ -0,0 +1,21 @@ +// 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. + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +namespace System +{ + // Custom attribute to indicate that the enum + // should be treated as a bitfield (or set of flags). + // An IDE may use this information to provide a richer + // development experience. + [AttributeUsage(AttributeTargets.Enum, Inherited = false)] + public class FlagsAttribute : Attribute + { + public FlagsAttribute() + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/FormatException.cs b/external/corefx/src/Common/src/CoreLib/System/FormatException.cs new file mode 100644 index 0000000000..b0e273369c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/FormatException.cs @@ -0,0 +1,44 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: Exception to designate an illegal argument to FormatMessage. +** +** +===========================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class FormatException : SystemException + { + public FormatException() + : base(SR.Arg_FormatException) + { + HResult = HResults.COR_E_FORMAT; + } + + public FormatException(String message) + : base(message) + { + HResult = HResults.COR_E_FORMAT; + } + + public FormatException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_FORMAT; + } + + protected FormatException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/FormattableString.cs b/external/corefx/src/Common/src/CoreLib/System/FormattableString.cs new file mode 100644 index 0000000000..6369363b5d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/FormattableString.cs @@ -0,0 +1,81 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: implementation of the FormattableString +** class. +** +===========================================================*/ + +namespace System +{ + /// + /// A composite format string along with the arguments to be formatted. An instance of this + /// type may result from the use of the C# or VB language primitive "interpolated string". + /// + public abstract class FormattableString : IFormattable + { + /// + /// The composite format string. + /// + public abstract string Format { get; } + + /// + /// Returns an object array that contains zero or more objects to format. Clients should not + /// mutate the contents of the array. + /// + public abstract object[] GetArguments(); + + /// + /// The number of arguments to be formatted. + /// + public abstract int ArgumentCount { get; } + + /// + /// Returns one argument to be formatted from argument position . + /// + public abstract object GetArgument(int index); + + /// + /// Format to a string using the given culture. + /// + public abstract string ToString(IFormatProvider formatProvider); + + string IFormattable.ToString(string ignored, IFormatProvider formatProvider) + { + return ToString(formatProvider); + } + + /// + /// Format the given object in the invariant culture. This static method may be + /// imported in C# by + /// + /// using static System.FormattableString; + /// . + /// Within the scope + /// of that import directive an interpolated string may be formatted in the + /// invariant culture by writing, for example, + /// + /// Invariant($"{{ lat = {latitude}; lon = {longitude} }}") + /// + /// + public static string Invariant(FormattableString formattable) + { + if (formattable == null) + { + throw new ArgumentNullException(nameof(formattable)); + } + + return formattable.ToString(Globalization.CultureInfo.InvariantCulture); + } + + public override string ToString() + { + return ToString(Globalization.CultureInfo.CurrentCulture); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/BidiCategory.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/BidiCategory.cs new file mode 100644 index 0000000000..abe6950893 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/BidiCategory.cs @@ -0,0 +1,33 @@ +// 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. + +namespace System.Globalization +{ + internal enum BidiCategory + { + LeftToRight = 0, + LeftToRightEmbedding = 1, + LeftToRightOverride = 2, + RightToLeft = 3, + RightToLeftArabic = 4, + RightToLeftEmbedding = 5, + RightToLeftOverride = 6, + PopDirectionalFormat = 7, + EuropeanNumber = 8, + EuropeanNumberSeparator = 9, + EuropeanNumberTerminator = 10, + ArabicNumber = 11, + CommonNumberSeparator = 12, + NonSpacingMark = 13, + BoundaryNeutral = 14, + ParagraphSeparator = 15, + SegmentSeparator = 16, + Whitespace = 17, + OtherNeutrals = 18, + LeftToRightIsolate = 19, + RightToLeftIsolate = 20, + FirstStrongIsolate = 21, + PopDirectionIsolate = 22, + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/Calendar.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/Calendar.cs similarity index 99% rename from external/corert/src/System.Private.CoreLib/src/System/Globalization/Calendar.cs rename to external/corefx/src/Common/src/CoreLib/System/Globalization/Calendar.cs index 0baa1fef55..49ad597ceb 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/Calendar.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/Calendar.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.Serialization; namespace System.Globalization @@ -28,7 +27,6 @@ namespace System.Globalization // The calculation of hour/minute/second is moved to Calendar from GregorianCalendar, // since most of the calendars (or all?) have the same way of calcuating hour/minute/second. - [Serializable] public abstract class Calendar : ICloneable { // Number of 100ns (10E-7 second) ticks per time unit @@ -58,10 +56,8 @@ namespace System.Globalization internal const long MaxMillis = (long)DaysTo10000 * MillisPerDay; - private int _currentEraValue = -1; - private bool _isReadOnly = false; // The minimum supported DateTime range for the calendar. @@ -154,7 +150,6 @@ namespace System.Globalization public static Calendar ReadOnly(Calendar calendar) { if (calendar == null) { throw new ArgumentNullException(nameof(calendar)); } - Contract.EndContractBlock(); if (calendar.IsReadOnly) { return (calendar); } Calendar clonedCalendar = (Calendar)(calendar.MemberwiseClone()); @@ -214,7 +209,6 @@ namespace System.Globalization String.Format(CultureInfo.InvariantCulture, SR.Format(SR.Argument_ResultCalendarRange, minValue, maxValue))); } - Contract.EndContractBlock(); } internal DateTime Add(DateTime time, double value, int scale) @@ -636,7 +630,6 @@ namespace System.Globalization nameof(firstDayOfWeek), SR.Format(SR.ArgumentOutOfRange_Range, DayOfWeek.Sunday, DayOfWeek.Saturday)); } - Contract.EndContractBlock(); switch (rule) { case CalendarWeekRule.FirstDay: @@ -806,7 +799,6 @@ namespace System.Globalization throw new ArgumentOutOfRangeException(nameof(year), SR.ArgumentOutOfRange_NeedNonNegNum); } - Contract.EndContractBlock(); if (year < 100) { return ((TwoDigitYearMax / 100 - (year > TwoDigitYearMax % 100 ? 1 : 0)) * 100 + year); @@ -837,7 +829,6 @@ namespace System.Globalization internal static int GetSystemTwoDigitYearSetting(CalendarId CalID, int defaultYearValue) { - // Call nativeGetTwoDigitYearMax int twoDigitYearMax = CalendarData.GetTwoDigitYearMax(CalID); if (twoDigitYearMax < 0) { diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/CalendarAlgorithmType.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/CalendarAlgorithmType.cs new file mode 100644 index 0000000000..4ddc307abf --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/CalendarAlgorithmType.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.Globalization +{ + public enum CalendarAlgorithmType + { + Unknown = 0, // This is the default value to return in the Calendar base class. + SolarCalendar = 1, // Solar-base calendar, such as GregorianCalendar, jaoaneseCalendar, JulianCalendar, etc. + // Solar calendars are based on the solar year and seasons. + LunarCalendar = 2, // Lunar-based calendar, such as Hijri and UmAlQuraCalendar. + // Lunar calendars are based on the path of the moon. The seasons are not accurately represented. + LunisolarCalendar = 3 // Lunisolar-based calendar which use leap month rule, such as HebrewCalendar and Asian Lunisolar calendars. + // Lunisolar calendars are based on the cycle of the moon, but consider the seasons as a secondary consideration, + // so they align with the seasons as well as lunar events. + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/CalendarData.Unix.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/CalendarData.Unix.cs new file mode 100644 index 0000000000..4d1a63c23c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/CalendarData.Unix.cs @@ -0,0 +1,335 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Security; +using System.Text; +using Internal.Runtime.CompilerServices; + +namespace System.Globalization +{ + // needs to be kept in sync with CalendarDataType in System.Globalization.Native + internal enum CalendarDataType + { + Uninitialized = 0, + NativeName = 1, + MonthDay = 2, + ShortDates = 3, + LongDates = 4, + YearMonths = 5, + DayNames = 6, + AbbrevDayNames = 7, + MonthNames = 8, + AbbrevMonthNames = 9, + SuperShortDayNames = 10, + MonthGenitiveNames = 11, + AbbrevMonthGenitiveNames = 12, + EraNames = 13, + AbbrevEraNames = 14, + } + + internal partial class CalendarData + { + private bool LoadCalendarDataFromSystem(String localeName, CalendarId calendarId) + { + bool result = true; + result &= GetCalendarInfo(localeName, calendarId, CalendarDataType.NativeName, out this.sNativeName); + result &= GetCalendarInfo(localeName, calendarId, CalendarDataType.MonthDay, out this.sMonthDay); + this.sMonthDay = NormalizeDatePattern(this.sMonthDay); + + result &= EnumDatePatterns(localeName, calendarId, CalendarDataType.ShortDates, out this.saShortDates); + result &= EnumDatePatterns(localeName, calendarId, CalendarDataType.LongDates, out this.saLongDates); + result &= EnumDatePatterns(localeName, calendarId, CalendarDataType.YearMonths, out this.saYearMonths); + result &= EnumCalendarInfo(localeName, calendarId, CalendarDataType.DayNames, out this.saDayNames); + result &= EnumCalendarInfo(localeName, calendarId, CalendarDataType.AbbrevDayNames, out this.saAbbrevDayNames); + result &= EnumCalendarInfo(localeName, calendarId, CalendarDataType.SuperShortDayNames, out this.saSuperShortDayNames); + result &= EnumMonthNames(localeName, calendarId, CalendarDataType.MonthNames, out this.saMonthNames); + result &= EnumMonthNames(localeName, calendarId, CalendarDataType.AbbrevMonthNames, out this.saAbbrevMonthNames); + result &= EnumMonthNames(localeName, calendarId, CalendarDataType.MonthGenitiveNames, out this.saMonthGenitiveNames); + result &= EnumMonthNames(localeName, calendarId, CalendarDataType.AbbrevMonthGenitiveNames, out this.saAbbrevMonthGenitiveNames); + result &= EnumEraNames(localeName, calendarId, CalendarDataType.EraNames, out this.saEraNames); + result &= EnumEraNames(localeName, calendarId, CalendarDataType.AbbrevEraNames, out this.saAbbrevEraNames); + + return result; + } + + internal static int GetTwoDigitYearMax(CalendarId calendarId) + { + // There is no user override for this value on Linux or in ICU. + // So just return -1 to use the hard-coded defaults. + return -1; + } + + // Call native side to figure out which calendars are allowed + internal static int GetCalendars(string localeName, bool useUserOverride, CalendarId[] calendars) + { + Debug.Assert(!GlobalizationMode.Invariant); + + // NOTE: there are no 'user overrides' on Linux + int count = Interop.GlobalizationInterop.GetCalendars(localeName, calendars, calendars.Length); + + // ensure there is at least 1 calendar returned + if (count == 0 && calendars.Length > 0) + { + calendars[0] = CalendarId.GREGORIAN; + count = 1; + } + + return count; + } + + private static bool SystemSupportsTaiwaneseCalendar() + { + return true; + } + + // PAL Layer ends here + + private static bool GetCalendarInfo(string localeName, CalendarId calendarId, CalendarDataType dataType, out string calendarString) + { + Debug.Assert(!GlobalizationMode.Invariant); + + return Interop.CallStringMethod( + (locale, calId, type, stringBuilder) => + Interop.GlobalizationInterop.GetCalendarInfo( + locale, + calId, + type, + stringBuilder, + stringBuilder.Capacity), + localeName, + calendarId, + dataType, + out calendarString); + } + + private static bool EnumDatePatterns(string localeName, CalendarId calendarId, CalendarDataType dataType, out string[] datePatterns) + { + datePatterns = null; + + EnumCalendarsData callbackContext = new EnumCalendarsData(); + callbackContext.Results = new List(); + callbackContext.DisallowDuplicates = true; + bool result = EnumCalendarInfo(localeName, calendarId, dataType, ref callbackContext); + if (result) + { + List datePatternsList = callbackContext.Results; + + datePatterns = new string[datePatternsList.Count]; + for (int i = 0; i < datePatternsList.Count; i++) + { + datePatterns[i] = NormalizeDatePattern(datePatternsList[i]); + } + } + + return result; + } + + /// + /// The ICU date format characters are not exactly the same as the .NET date format characters. + /// NormalizeDatePattern will take in an ICU date pattern and return the equivalent .NET date pattern. + /// + /// + /// see Date Field Symbol Table in http://userguide.icu-project.org/formatparse/datetime + /// and https://msdn.microsoft.com/en-us/library/8kb3ddd4(v=vs.110).aspx + /// + private static string NormalizeDatePattern(string input) + { + StringBuilder destination = StringBuilderCache.Acquire(input.Length); + + int index = 0; + while (index < input.Length) + { + switch (input[index]) + { + case '\'': + // single quotes escape characters, like 'de' in es-SP + // so read verbatim until the next single quote + destination.Append(input[index++]); + while (index < input.Length) + { + char current = input[index++]; + destination.Append(current); + if (current == '\'') + { + break; + } + } + break; + case 'E': + case 'e': + case 'c': + // 'E' in ICU is the day of the week, which maps to 3 or 4 'd's in .NET + // 'e' in ICU is the local day of the week, which has no representation in .NET, but + // maps closest to 3 or 4 'd's in .NET + // 'c' in ICU is the stand-alone day of the week, which has no representation in .NET, but + // maps closest to 3 or 4 'd's in .NET + NormalizeDayOfWeek(input, destination, ref index); + break; + case 'L': + case 'M': + // 'L' in ICU is the stand-alone name of the month, + // which maps closest to 'M' in .NET since it doesn't support stand-alone month names in patterns + // 'M' in both ICU and .NET is the month, + // but ICU supports 5 'M's, which is the super short month name + int occurrences = CountOccurrences(input, input[index], ref index); + if (occurrences > 4) + { + // 5 'L's or 'M's in ICU is the super short name, which maps closest to MMM in .NET + occurrences = 3; + } + destination.Append('M', occurrences); + break; + case 'G': + // 'G' in ICU is the era, which maps to 'g' in .NET + occurrences = CountOccurrences(input, 'G', ref index); + + // it doesn't matter how many 'G's, since .NET only supports 'g' or 'gg', and they + // have the same meaning + destination.Append('g'); + break; + case 'y': + // a single 'y' in ICU is the year with no padding or trimming. + // a single 'y' in .NET is the year with 1 or 2 digits + // so convert any single 'y' to 'yyyy' + occurrences = CountOccurrences(input, 'y', ref index); + if (occurrences == 1) + { + occurrences = 4; + } + destination.Append('y', occurrences); + break; + default: + const string unsupportedDateFieldSymbols = "YuUrQqwWDFg"; + Debug.Assert(unsupportedDateFieldSymbols.IndexOf(input[index]) == -1, + string.Format(CultureInfo.InvariantCulture, + "Encountered an unexpected date field symbol '{0}' from ICU which has no known corresponding .NET equivalent.", + input[index])); + + destination.Append(input[index++]); + break; + } + } + + return StringBuilderCache.GetStringAndRelease(destination); + } + + private static void NormalizeDayOfWeek(string input, StringBuilder destination, ref int index) + { + char dayChar = input[index]; + int occurrences = CountOccurrences(input, dayChar, ref index); + occurrences = Math.Max(occurrences, 3); + if (occurrences > 4) + { + // 5 and 6 E/e/c characters in ICU is the super short names, which maps closest to ddd in .NET + occurrences = 3; + } + + destination.Append('d', occurrences); + } + + private static int CountOccurrences(string input, char value, ref int index) + { + int startIndex = index; + while (index < input.Length && input[index] == value) + { + index++; + } + + return index - startIndex; + } + + private static bool EnumMonthNames(string localeName, CalendarId calendarId, CalendarDataType dataType, out string[] monthNames) + { + monthNames = null; + + EnumCalendarsData callbackContext = new EnumCalendarsData(); + callbackContext.Results = new List(); + bool result = EnumCalendarInfo(localeName, calendarId, dataType, ref callbackContext); + if (result) + { + // the month-name arrays are expected to have 13 elements. If ICU only returns 12, add an + // extra empty string to fill the array. + if (callbackContext.Results.Count == 12) + { + callbackContext.Results.Add(string.Empty); + } + + monthNames = callbackContext.Results.ToArray(); + } + + return result; + } + + private static bool EnumEraNames(string localeName, CalendarId calendarId, CalendarDataType dataType, out string[] eraNames) + { + bool result = EnumCalendarInfo(localeName, calendarId, dataType, out eraNames); + + // .NET expects that only the Japanese calendars have more than 1 era. + // So for other calendars, only return the latest era. + if (calendarId != CalendarId.JAPAN && calendarId != CalendarId.JAPANESELUNISOLAR && eraNames.Length > 0) + { + string[] latestEraName = new string[] { eraNames[eraNames.Length - 1] }; + eraNames = latestEraName; + } + + return result; + } + + internal static bool EnumCalendarInfo(string localeName, CalendarId calendarId, CalendarDataType dataType, out string[] calendarData) + { + calendarData = null; + + EnumCalendarsData callbackContext = new EnumCalendarsData(); + callbackContext.Results = new List(); + bool result = EnumCalendarInfo(localeName, calendarId, dataType, ref callbackContext); + if (result) + { + calendarData = callbackContext.Results.ToArray(); + } + + return result; + } + + private static unsafe bool EnumCalendarInfo(string localeName, CalendarId calendarId, CalendarDataType dataType, ref EnumCalendarsData callbackContext) + { + return Interop.GlobalizationInterop.EnumCalendarInfo(EnumCalendarInfoCallback, localeName, calendarId, dataType, (IntPtr)Unsafe.AsPointer(ref callbackContext)); + } + + private static unsafe void EnumCalendarInfoCallback(string calendarString, IntPtr context) + { + try + { + ref EnumCalendarsData callbackContext = ref Unsafe.As(ref *(byte*)context); + + if (callbackContext.DisallowDuplicates) + { + foreach (string existingResult in callbackContext.Results) + { + if (string.Equals(calendarString, existingResult, StringComparison.Ordinal)) + { + // the value is already in the results, so don't add it again + return; + } + } + } + + callbackContext.Results.Add(calendarString); + } + catch (Exception e) + { + Debug.Fail(e.ToString()); + // we ignore the managed exceptions here because EnumCalendarInfoCallback will get called from the native code. + // If we don't ignore the exception here that can cause the runtime to fail fast. + } + } + + private struct EnumCalendarsData + { + public List Results; + public bool DisallowDuplicates; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/CalendarData.Windows.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/CalendarData.Windows.cs new file mode 100644 index 0000000000..03f9088d62 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/CalendarData.Windows.cs @@ -0,0 +1,469 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using Internal.Runtime.CompilerServices; + +namespace System.Globalization +{ + internal partial class CalendarData + { + private bool LoadCalendarDataFromSystem(String localeName, CalendarId calendarId) + { + Debug.Assert(!GlobalizationMode.Invariant); + + bool ret = true; + + uint useOverrides = this.bUseUserOverrides ? 0 : CAL_NOUSEROVERRIDE; + + // + // Windows doesn't support some calendars right now, so remap those. + // + switch (calendarId) + { + case CalendarId.JAPANESELUNISOLAR: // Data looks like Japanese + calendarId = CalendarId.JAPAN; + break; + case CalendarId.JULIAN: // Data looks like gregorian US + case CalendarId.CHINESELUNISOLAR: // Algorithmic, so actual data is irrelevent + case CalendarId.SAKA: // reserved to match Office but not implemented in our code, so data is irrelevent + case CalendarId.LUNAR_ETO_CHN: // reserved to match Office but not implemented in our code, so data is irrelevent + case CalendarId.LUNAR_ETO_KOR: // reserved to match Office but not implemented in our code, so data is irrelevent + case CalendarId.LUNAR_ETO_ROKUYOU: // reserved to match Office but not implemented in our code, so data is irrelevent + case CalendarId.KOREANLUNISOLAR: // Algorithmic, so actual data is irrelevent + case CalendarId.TAIWANLUNISOLAR: // Algorithmic, so actual data is irrelevent + calendarId = CalendarId.GREGORIAN_US; + break; + } + + // + // Special handling for some special calendar due to OS limitation. + // This includes calendar like Taiwan calendar, UmAlQura calendar, etc. + // + CheckSpecialCalendar(ref calendarId, ref localeName); + + // Numbers + ret &= CallGetCalendarInfoEx(localeName, calendarId, CAL_ITWODIGITYEARMAX | useOverrides, out this.iTwoDigitYearMax); + + // Strings + ret &= CallGetCalendarInfoEx(localeName, calendarId, CAL_SCALNAME, out this.sNativeName); + ret &= CallGetCalendarInfoEx(localeName, calendarId, CAL_SMONTHDAY | useOverrides, out this.sMonthDay); + + // String Arrays + // Formats + ret &= CallEnumCalendarInfo(localeName, calendarId, CAL_SSHORTDATE, LOCALE_SSHORTDATE | useOverrides, out this.saShortDates); + ret &= CallEnumCalendarInfo(localeName, calendarId, CAL_SLONGDATE, LOCALE_SLONGDATE | useOverrides, out this.saLongDates); + + // Get the YearMonth pattern. + ret &= CallEnumCalendarInfo(localeName, calendarId, CAL_SYEARMONTH, LOCALE_SYEARMONTH, out this.saYearMonths); + + // Day & Month Names + // These are all single calType entries, 1 per day, so we have to make 7 or 13 calls to collect all the names + + // Day + // Note that we're off-by-one since managed starts on sunday and windows starts on monday + ret &= GetCalendarDayInfo(localeName, calendarId, CAL_SDAYNAME7, out this.saDayNames); + ret &= GetCalendarDayInfo(localeName, calendarId, CAL_SABBREVDAYNAME7, out this.saAbbrevDayNames); + + // Month names + ret &= GetCalendarMonthInfo(localeName, calendarId, CAL_SMONTHNAME1, out this.saMonthNames); + ret &= GetCalendarMonthInfo(localeName, calendarId, CAL_SABBREVMONTHNAME1, out this.saAbbrevMonthNames); + + // + // The following LCTYPE are not supported in some platforms. If the call fails, + // don't return a failure. + // + GetCalendarDayInfo(localeName, calendarId, CAL_SSHORTESTDAYNAME7, out this.saSuperShortDayNames); + + // Gregorian may have genitive month names + if (calendarId == CalendarId.GREGORIAN) + { + GetCalendarMonthInfo(localeName, calendarId, CAL_SMONTHNAME1 | CAL_RETURN_GENITIVE_NAMES, out this.saMonthGenitiveNames); + GetCalendarMonthInfo(localeName, calendarId, CAL_SABBREVMONTHNAME1 | CAL_RETURN_GENITIVE_NAMES, out this.saAbbrevMonthGenitiveNames); + } + + // Calendar Parts Names + // This doesn't get always get localized names for gregorian (not available in windows < 7) + // so: eg: coreclr on win < 7 won't get these + CallEnumCalendarInfo(localeName, calendarId, CAL_SERASTRING, 0, out this.saEraNames); + CallEnumCalendarInfo(localeName, calendarId, CAL_SABBREVERASTRING, 0, out this.saAbbrevEraNames); + + // + // Calendar Era Info + // Note that calendar era data (offsets, etc) is hard coded for each calendar since this + // data is implementation specific and not dynamic (except perhaps Japanese) + // + + // Clean up the escaping of the formats + this.saShortDates = CultureData.ReescapeWin32Strings(this.saShortDates); + this.saLongDates = CultureData.ReescapeWin32Strings(this.saLongDates); + this.saYearMonths = CultureData.ReescapeWin32Strings(this.saYearMonths); + this.sMonthDay = CultureData.ReescapeWin32String(this.sMonthDay); + + return ret; + } + + // Get native two digit year max + internal static int GetTwoDigitYearMax(CalendarId calendarId) + { + if (GlobalizationMode.Invariant) + { + return Invariant.iTwoDigitYearMax; + } + + int twoDigitYearMax = -1; + + if (!CallGetCalendarInfoEx(null, calendarId, (uint)CAL_ITWODIGITYEARMAX, out twoDigitYearMax)) + { + twoDigitYearMax = -1; + } + + return twoDigitYearMax; + } + + // Call native side to figure out which calendars are allowed + internal static int GetCalendars(String localeName, bool useUserOverride, CalendarId[] calendars) + { + Debug.Assert(!GlobalizationMode.Invariant); + + EnumCalendarsData data = new EnumCalendarsData(); + data.userOverride = 0; + data.calendars = new List(); + + // First call GetLocaleInfo if necessary + if (useUserOverride) + { + // They want user overrides, see if the user calendar matches the input calendar + int userCalendar = CultureData.GetLocaleInfoExInt(localeName, LOCALE_ICALENDARTYPE); + + // If we got a default, then use it as the first calendar + if (userCalendar != 0) + { + data.userOverride = userCalendar; + data.calendars.Add(userCalendar); + } + } + + unsafe + { + Interop.Kernel32.EnumCalendarInfoExEx(EnumCalendarsCallback, localeName, ENUM_ALL_CALENDARS, null, CAL_ICALINTVALUE, Unsafe.AsPointer(ref data)); + } + + // Copy to the output array + for (int i = 0; i < Math.Min(calendars.Length, data.calendars.Count); i++) + calendars[i] = (CalendarId)data.calendars[i]; + + // Now we have a list of data, return the count + return data.calendars.Count; + } + + private static bool SystemSupportsTaiwaneseCalendar() + { + Debug.Assert(!GlobalizationMode.Invariant); + + string data; + // Taiwanese calendar get listed as one of the optional zh-TW calendars only when having zh-TW UI + return CallGetCalendarInfoEx("zh-TW", CalendarId.TAIWAN, CAL_SCALNAME, out data); + } + + // PAL Layer ends here + + private const uint CAL_RETURN_NUMBER = 0x20000000; + private const uint CAL_RETURN_GENITIVE_NAMES = 0x10000000; + private const uint CAL_NOUSEROVERRIDE = 0x80000000; + private const uint CAL_SCALNAME = 0x00000002; + private const uint CAL_SMONTHDAY = 0x00000038; + private const uint CAL_SSHORTDATE = 0x00000005; + private const uint CAL_SLONGDATE = 0x00000006; + private const uint CAL_SYEARMONTH = 0x0000002f; + private const uint CAL_SDAYNAME7 = 0x0000000d; + private const uint CAL_SABBREVDAYNAME7 = 0x00000014; + private const uint CAL_SMONTHNAME1 = 0x00000015; + private const uint CAL_SABBREVMONTHNAME1 = 0x00000022; + private const uint CAL_SSHORTESTDAYNAME7 = 0x00000037; + private const uint CAL_SERASTRING = 0x00000004; + private const uint CAL_SABBREVERASTRING = 0x00000039; + private const uint CAL_ICALINTVALUE = 0x00000001; + private const uint CAL_ITWODIGITYEARMAX = 0x00000030; + + private const uint ENUM_ALL_CALENDARS = 0xffffffff; + + private const uint LOCALE_SSHORTDATE = 0x0000001F; + private const uint LOCALE_SLONGDATE = 0x00000020; + private const uint LOCALE_SYEARMONTH = 0x00001006; + private const uint LOCALE_ICALENDARTYPE = 0x00001009; + + //////////////////////////////////////////////////////////////////////// + // + // For calendars like Gregorain US/Taiwan/UmAlQura, they are not available + // in all OS or all localized versions of OS. + // If OS does not support these calendars, we will fallback by using the + // appropriate fallback calendar and locale combination to retrieve data from OS. + // + // Parameters: + // __deref_inout pCalendarInt: + // Pointer to the calendar ID. This will be updated to new fallback calendar ID if needed. + // __in_out pLocaleNameStackBuffer + // Pointer to the StackSString object which holds the locale name to be checked. + // This will be updated to new fallback locale name if needed. + // + //////////////////////////////////////////////////////////////////////// + private static void CheckSpecialCalendar(ref CalendarId calendar, ref string localeName) + { + string data; + + // Gregorian-US isn't always available in the OS, however it is the same for all locales + switch (calendar) + { + case CalendarId.GREGORIAN_US: + // See if this works + if (!CallGetCalendarInfoEx(localeName, calendar, CAL_SCALNAME, out data)) + { + // Failed, set it to a locale (fa-IR) that's alway has Gregorian US available in the OS + localeName = "fa-IR"; + } + // See if that works + if (!CallGetCalendarInfoEx(localeName, calendar, CAL_SCALNAME, out data)) + { + // Failed again, just use en-US with the gregorian calendar + localeName = "en-US"; + calendar = CalendarId.GREGORIAN; + } + break; + case CalendarId.TAIWAN: + // Taiwan calendar data is not always in all language version of OS due to Geopolical reasons. + // It is only available in zh-TW localized versions of Windows. + // Let's check if OS supports it. If not, fallback to Greogrian localized for Taiwan calendar. + if (!SystemSupportsTaiwaneseCalendar()) + { + calendar = CalendarId.GREGORIAN; + } + break; + } + } + + private static bool CallGetCalendarInfoEx(string localeName, CalendarId calendar, uint calType, out int data) + { + return (Interop.Kernel32.GetCalendarInfoEx(localeName, (uint)calendar, IntPtr.Zero, calType | CAL_RETURN_NUMBER, IntPtr.Zero, 0, out data) != 0); + } + + private static unsafe bool CallGetCalendarInfoEx(string localeName, CalendarId calendar, uint calType, out string data) + { + const int BUFFER_LENGTH = 80; + + // The maximum size for values returned from GetCalendarInfoEx is 80 characters. + char* buffer = stackalloc char[BUFFER_LENGTH]; + + int ret = Interop.Kernel32.GetCalendarInfoEx(localeName, (uint)calendar, IntPtr.Zero, calType, (IntPtr)buffer, BUFFER_LENGTH, IntPtr.Zero); + if (ret > 0) + { + if (buffer[ret - 1] == '\0') + { + ret--; // don't include the null termination in the string + } + data = new string(buffer, 0, ret); + return true; + } + data = ""; + return false; + } + + // Context for EnumCalendarInfoExEx callback. + private struct EnumData + { + public string userOverride; + public List strings; + } + + // EnumCalendarInfoExEx callback itself. + // [NativeCallable(CallingConvention = CallingConvention.StdCall)] + private static unsafe Interop.BOOL EnumCalendarInfoCallback(char* lpCalendarInfoString, uint calendar, IntPtr pReserved, void* lParam) + { + ref EnumData context = ref Unsafe.As(ref *(byte*)lParam); + try + { + string calendarInfo = new string(lpCalendarInfoString); + + // If we had a user override, check to make sure this differs + if (context.userOverride != calendarInfo) + context.strings.Add(calendarInfo); + + return Interop.BOOL.TRUE; + } + catch (Exception) + { + return Interop.BOOL.FALSE; + } + } + + private static unsafe bool CallEnumCalendarInfo(string localeName, CalendarId calendar, uint calType, uint lcType, out string[] data) + { + EnumData context = new EnumData(); + context.userOverride = null; + context.strings = new List(); + // First call GetLocaleInfo if necessary + if (((lcType != 0) && ((lcType & CAL_NOUSEROVERRIDE) == 0)) && + // Get user locale, see if it matches localeName. + // Note that they should match exactly, including letter case + GetUserDefaultLocaleName() == localeName) + { + // They want user overrides, see if the user calendar matches the input calendar + CalendarId userCalendar = (CalendarId)CultureData.GetLocaleInfoExInt(localeName, LOCALE_ICALENDARTYPE); + + // If the calendars were the same, see if the locales were the same + if (userCalendar == calendar) + { + // They matched, get the user override since locale & calendar match + string res = CultureData.GetLocaleInfoEx(localeName, lcType); + + // if it succeeded remember the override for the later callers + if (res != null) + { + // Remember this was the override (so we can look for duplicates later in the enum function) + context.userOverride = res; + + // Add to the result strings. + context.strings.Add(res); + } + } + } + + unsafe + { + // Now call the enumeration API. Work is done by our callback function + Interop.Kernel32.EnumCalendarInfoExEx(EnumCalendarInfoCallback, localeName, (uint)calendar, null, calType, Unsafe.AsPointer(ref context)); + } + + // Now we have a list of data, fail if we didn't find anything. + if (context.strings.Count == 0) + { + data = null; + return false; + } + + string[] output = context.strings.ToArray(); + + if (calType == CAL_SABBREVERASTRING || calType == CAL_SERASTRING) + { + // Eras are enumerated backwards. (oldest era name first, but + // Japanese calendar has newest era first in array, and is only + // calendar with multiple eras) + Array.Reverse(output, 0, output.Length); + } + + data = output; + + return true; + } + + //////////////////////////////////////////////////////////////////////// + // + // Get the native day names + // + // NOTE: There's a disparity between .Net & windows day orders, the input day should + // start with Sunday + // + // Parameters: + // OUT pOutputStrings The output string[] value. + // + //////////////////////////////////////////////////////////////////////// + private static bool GetCalendarDayInfo(string localeName, CalendarId calendar, uint calType, out string[] outputStrings) + { + bool result = true; + + // + // We'll need a new array of 7 items + // + string[] results = new string[7]; + + // Get each one of them + for (int i = 0; i < 7; i++, calType++) + { + result &= CallGetCalendarInfoEx(localeName, calendar, calType, out results[i]); + + // On the first iteration we need to go from CAL_SDAYNAME7 to CAL_SDAYNAME1, so subtract 7 before the ++ happens + // This is because the framework starts on sunday and windows starts on monday when counting days + if (i == 0) + calType -= 7; + } + + outputStrings = results; + + return result; + } + + //////////////////////////////////////////////////////////////////////// + // + // Get the native month names + // + // Parameters: + // OUT pOutputStrings The output string[] value. + // + //////////////////////////////////////////////////////////////////////// + private static bool GetCalendarMonthInfo(string localeName, CalendarId calendar, uint calType, out string[] outputStrings) + { + // + // We'll need a new array of 13 items + // + string[] results = new string[13]; + + // Get each one of them + for (int i = 0; i < 13; i++, calType++) + { + if (!CallGetCalendarInfoEx(localeName, calendar, calType, out results[i])) + results[i] = ""; + } + + outputStrings = results; + + return true; + } + + // + // struct to help our calendar data enumaration callback + // + private struct EnumCalendarsData + { + public int userOverride; // user override value (if found) + public List calendars; // list of calendars found so far + } + + // [NativeCallable(CallingConvention = CallingConvention.StdCall)] + private static unsafe Interop.BOOL EnumCalendarsCallback(char* lpCalendarInfoString, uint calendar, IntPtr reserved, void* lParam) + { + ref EnumCalendarsData context = ref Unsafe.As(ref *(byte*)lParam); + try + { + // If we had a user override, check to make sure this differs + if (context.userOverride != calendar) + context.calendars.Add((int)calendar); + + return Interop.BOOL.TRUE; + } + catch (Exception) + { + return Interop.BOOL.FALSE; + } + } + + private static unsafe String GetUserDefaultLocaleName() + { + Debug.Assert(!GlobalizationMode.Invariant); + + const int LOCALE_NAME_MAX_LENGTH = 85; + const uint LOCALE_SNAME = 0x0000005c; + const string LOCALE_NAME_USER_DEFAULT = null; + + int result; + char* localeName = stackalloc char[LOCALE_NAME_MAX_LENGTH]; + result = CultureData.GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SNAME, localeName, LOCALE_NAME_MAX_LENGTH); + + return result <= 0 ? "" : new String(localeName, 0, result - 1); // exclude the null termination + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CalendarData.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/CalendarData.cs similarity index 97% rename from external/corert/src/System.Private.CoreLib/src/System/Globalization/CalendarData.cs rename to external/corefx/src/Common/src/CoreLib/System/Globalization/CalendarData.cs index c57d3c403c..ea70a1ce9a 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CalendarData.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/CalendarData.cs @@ -48,13 +48,13 @@ namespace System.Globalization internal bool bUseUserOverrides; // True if we want user overrides. // Static invariant for the invariant locale - internal static CalendarData Invariant; + internal static readonly CalendarData Invariant = CreateInvariant(); // Private constructor private CalendarData() { } - // Invariant constructor - static CalendarData() + // Invariant factory + private static CalendarData CreateInvariant() { // Set our default/gregorian US calendar data // Calendar IDs are 1-based, arrays are 0 based. @@ -91,8 +91,7 @@ namespace System.Globalization invariant.bUseUserOverrides = false; - // Calendar was built, go ahead and assign it... - Invariant = invariant; + return invariant; } // @@ -102,9 +101,11 @@ namespace System.Globalization { this.bUseUserOverrides = bUseUserOverrides; + Debug.Assert(!GlobalizationMode.Invariant); + if (!LoadCalendarDataFromSystem(localeName, calendarId)) { - Debug.Assert(false, "[CalendarData] LoadCalendarDataFromSystem call isn't expected to fail for calendar " + calendarId + " locale " + localeName); + Debug.Fail("[CalendarData] LoadCalendarDataFromSystem call isn't expected to fail for calendar " + calendarId + " locale " + localeName); // Something failed, try invariant for missing parts // This is really not good, but we don't want the callers to crash. diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/CalendarWeekRule.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/CalendarWeekRule.cs new file mode 100644 index 0000000000..f683ed05e7 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/CalendarWeekRule.cs @@ -0,0 +1,15 @@ +// 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. + +namespace System.Globalization +{ + public enum CalendarWeekRule + { + FirstDay = 0, // Week 1 begins on the first day of the year + + FirstFullWeek = 1, // Week 1 begins on first FirstDayOfWeek not before the first day of the year + + FirstFourDayWeek = 2 // Week 1 begins on first FirstDayOfWeek such that FirstDayOfWeek+3 is not before the first day of the year + }; +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/CalendricalCalculationsHelper.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/CalendricalCalculationsHelper.cs new file mode 100644 index 0000000000..e0a3072b22 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/CalendricalCalculationsHelper.cs @@ -0,0 +1,412 @@ +// 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.Diagnostics; + +namespace System.Globalization +{ + internal class CalendricalCalculationsHelper + { + private const double FullCircleOfArc = 360.0; // 360.0; + private const int HalfCircleOfArc = 180; + private const double TwelveHours = 0.5; // half a day + private const double Noon2000Jan01 = 730120.5; + internal const double MeanTropicalYearInDays = 365.242189; + private const double MeanSpeedOfSun = MeanTropicalYearInDays / FullCircleOfArc; + private const double LongitudeSpring = 0.0; + private const double TwoDegreesAfterSpring = 2.0; + private const int SecondsPerDay = 24 * 60 * 60; // 24 hours * 60 minutes * 60 seconds + + private const int DaysInUniformLengthCentury = 36525; + private const int SecondsPerMinute = 60; + private const int MinutesPerDegree = 60; + + private static readonly long s_startOf1810 = GetNumberOfDays(new DateTime(1810, 1, 1)); + private static readonly long s_startOf1900Century = GetNumberOfDays(new DateTime(1900, 1, 1)); + + private static readonly double[] s_coefficients1900to1987 = new double[] { -0.00002, 0.000297, 0.025184, -0.181133, 0.553040, -0.861938, 0.677066, -0.212591 }; + private static readonly double[] s_coefficients1800to1899 = new double[] { -0.000009, 0.003844, 0.083563, 0.865736, 4.867575, 15.845535, 31.332267, 38.291999, 28.316289, 11.636204, 2.043794 }; + private static readonly double[] s_coefficients1700to1799 = new double[] { 8.118780842, -0.005092142, 0.003336121, -0.0000266484 }; + private static readonly double[] s_coefficients1620to1699 = new double[] { 196.58333, -4.0675, 0.0219167 }; + private static readonly double[] s_lambdaCoefficients = new double[] { 280.46645, 36000.76983, 0.0003032 }; + private static readonly double[] s_anomalyCoefficients = new double[] { 357.52910, 35999.05030, -0.0001559, -0.00000048 }; + private static readonly double[] s_eccentricityCoefficients = new double[] { 0.016708617, -0.000042037, -0.0000001236 }; + private static readonly double[] s_coefficients = new double[] { Angle(23, 26, 21.448), Angle(0, 0, -46.8150), Angle(0, 0, -0.00059), Angle(0, 0, 0.001813) }; + private static readonly double[] s_coefficientsA = new double[] { 124.90, -1934.134, 0.002063 }; + private static readonly double[] s_coefficientsB = new double[] { 201.11, 72001.5377, 0.00057 }; + + private static double RadiansFromDegrees(double degree) + { + return degree * Math.PI / 180; + } + + private static double SinOfDegree(double degree) + { + return Math.Sin(RadiansFromDegrees(degree)); + } + + private static double CosOfDegree(double degree) + { + return Math.Cos(RadiansFromDegrees(degree)); + } + private static double TanOfDegree(double degree) + { + return Math.Tan(RadiansFromDegrees(degree)); + } + + public static double Angle(int degrees, int minutes, double seconds) + { + return ((seconds / SecondsPerMinute + minutes) / MinutesPerDegree) + degrees; + } + + private static double Obliquity(double julianCenturies) + { + return PolynomialSum(s_coefficients, julianCenturies); + } + + internal static long GetNumberOfDays(DateTime date) + { + return date.Ticks / GregorianCalendar.TicksPerDay; + } + + private static int GetGregorianYear(double numberOfDays) + { + return new DateTime(Math.Min((long)(Math.Floor(numberOfDays) * GregorianCalendar.TicksPerDay), DateTime.MaxValue.Ticks)).Year; + } + + private enum CorrectionAlgorithm + { + Default, + Year1988to2019, + Year1900to1987, + Year1800to1899, + Year1700to1799, + Year1620to1699 + } + + private struct EphemerisCorrectionAlgorithmMap + { + public EphemerisCorrectionAlgorithmMap(int year, CorrectionAlgorithm algorithm) + { + _lowestYear = year; + _algorithm = algorithm; + } + + internal int _lowestYear; + internal CorrectionAlgorithm _algorithm; + }; + + private static readonly EphemerisCorrectionAlgorithmMap[] s_ephemerisCorrectionTable = new EphemerisCorrectionAlgorithmMap[] + { + // lowest year that starts algorithm, algorithm to use + new EphemerisCorrectionAlgorithmMap(2020, CorrectionAlgorithm.Default), + new EphemerisCorrectionAlgorithmMap(1988, CorrectionAlgorithm.Year1988to2019), + new EphemerisCorrectionAlgorithmMap(1900, CorrectionAlgorithm.Year1900to1987), + new EphemerisCorrectionAlgorithmMap(1800, CorrectionAlgorithm.Year1800to1899), + new EphemerisCorrectionAlgorithmMap(1700, CorrectionAlgorithm.Year1700to1799), + new EphemerisCorrectionAlgorithmMap(1620, CorrectionAlgorithm.Year1620to1699), + new EphemerisCorrectionAlgorithmMap(int.MinValue, CorrectionAlgorithm.Default) // default must be last + }; + + private static double Reminder(double divisor, double dividend) + { + double whole = Math.Floor(divisor / dividend); + return divisor - (dividend * whole); + } + + private static double NormalizeLongitude(double longitude) + { + longitude = Reminder(longitude, FullCircleOfArc); + if (longitude < 0) + { + longitude += FullCircleOfArc; + } + return longitude; + } + + public static double AsDayFraction(double longitude) + { + return longitude / FullCircleOfArc; + } + + private static double PolynomialSum(double[] coefficients, double indeterminate) + { + double sum = coefficients[0]; + double indeterminateRaised = 1; + for (int i = 1; i < coefficients.Length; i++) + { + indeterminateRaised *= indeterminate; + sum += (coefficients[i] * indeterminateRaised); + } + + return sum; + } + + private static double CenturiesFrom1900(int gregorianYear) + { + long july1stOfYear = GetNumberOfDays(new DateTime(gregorianYear, 7, 1)); + return (double)(july1stOfYear - s_startOf1900Century) / DaysInUniformLengthCentury; + } + + // the following formulas defines a polynomial function which gives us the amount that the earth is slowing down for specific year ranges + private static double DefaultEphemerisCorrection(int gregorianYear) + { + Debug.Assert(gregorianYear < 1620 || 2020 <= gregorianYear); + long january1stOfYear = GetNumberOfDays(new DateTime(gregorianYear, 1, 1)); + double daysSinceStartOf1810 = january1stOfYear - s_startOf1810; + double x = TwelveHours + daysSinceStartOf1810; + return ((Math.Pow(x, 2) / 41048480) - 15) / SecondsPerDay; + } + + private static double EphemerisCorrection1988to2019(int gregorianYear) + { + Debug.Assert(1988 <= gregorianYear && gregorianYear <= 2019); + return (double)(gregorianYear - 1933) / SecondsPerDay; + } + + private static double EphemerisCorrection1900to1987(int gregorianYear) + { + Debug.Assert(1900 <= gregorianYear && gregorianYear <= 1987); + double centuriesFrom1900 = CenturiesFrom1900(gregorianYear); + return PolynomialSum(s_coefficients1900to1987, centuriesFrom1900); + } + + private static double EphemerisCorrection1800to1899(int gregorianYear) + { + Debug.Assert(1800 <= gregorianYear && gregorianYear <= 1899); + double centuriesFrom1900 = CenturiesFrom1900(gregorianYear); + return PolynomialSum(s_coefficients1800to1899, centuriesFrom1900); + } + + private static double EphemerisCorrection1700to1799(int gregorianYear) + { + Debug.Assert(1700 <= gregorianYear && gregorianYear <= 1799); + double yearsSince1700 = gregorianYear - 1700; + return PolynomialSum(s_coefficients1700to1799, yearsSince1700) / SecondsPerDay; + } + + private static double EphemerisCorrection1620to1699(int gregorianYear) + { + Debug.Assert(1620 <= gregorianYear && gregorianYear <= 1699); + double yearsSince1600 = gregorianYear - 1600; + return PolynomialSum(s_coefficients1620to1699, yearsSince1600) / SecondsPerDay; + } + + // ephemeris-correction: correction to account for the slowing down of the rotation of the earth + private static double EphemerisCorrection(double time) + { + int year = GetGregorianYear(time); + foreach (EphemerisCorrectionAlgorithmMap map in s_ephemerisCorrectionTable) + { + if (map._lowestYear <= year) + { + switch (map._algorithm) + { + case CorrectionAlgorithm.Default: return DefaultEphemerisCorrection(year); + case CorrectionAlgorithm.Year1988to2019: return EphemerisCorrection1988to2019(year); + case CorrectionAlgorithm.Year1900to1987: return EphemerisCorrection1900to1987(year); + case CorrectionAlgorithm.Year1800to1899: return EphemerisCorrection1800to1899(year); + case CorrectionAlgorithm.Year1700to1799: return EphemerisCorrection1700to1799(year); + case CorrectionAlgorithm.Year1620to1699: return EphemerisCorrection1620to1699(year); + } + + break; // break the loop and assert eventually + } + } + + Debug.Fail("Not expected to come here"); + return DefaultEphemerisCorrection(year); + } + + public static double JulianCenturies(double moment) + { + double dynamicalMoment = moment + EphemerisCorrection(moment); + return (dynamicalMoment - Noon2000Jan01) / DaysInUniformLengthCentury; + } + + private static bool IsNegative(double value) + { + return Math.Sign(value) == -1; + } + + private static double CopySign(double value, double sign) + { + return (IsNegative(value) == IsNegative(sign)) ? value : -value; + } + + // equation-of-time; approximate the difference between apparent solar time and mean solar time + // formal definition is EOT = GHA - GMHA + // GHA is the Greenwich Hour Angle of the apparent (actual) Sun + // GMHA is the Greenwich Mean Hour Angle of the mean (fictitious) Sun + // http://www.esrl.noaa.gov/gmd/grad/solcalc/ + // http://en.wikipedia.org/wiki/Equation_of_time + private static double EquationOfTime(double time) + { + double julianCenturies = JulianCenturies(time); + double lambda = PolynomialSum(s_lambdaCoefficients, julianCenturies); + double anomaly = PolynomialSum(s_anomalyCoefficients, julianCenturies); + double eccentricity = PolynomialSum(s_eccentricityCoefficients, julianCenturies); + + double epsilon = Obliquity(julianCenturies); + double tanHalfEpsilon = TanOfDegree(epsilon / 2); + double y = tanHalfEpsilon * tanHalfEpsilon; + + double dividend = ((y * SinOfDegree(2 * lambda)) + - (2 * eccentricity * SinOfDegree(anomaly)) + + (4 * eccentricity * y * SinOfDegree(anomaly) * CosOfDegree(2 * lambda)) + - (0.5 * Math.Pow(y, 2) * SinOfDegree(4 * lambda)) + - (1.25 * Math.Pow(eccentricity, 2) * SinOfDegree(2 * anomaly))); + double divisor = 2 * Math.PI; + double equation = dividend / divisor; + + // approximation of equation of time is not valid for dates that are many millennia in the past or future + // thus limited to a half day + return CopySign(Math.Min(Math.Abs(equation), TwelveHours), equation); + } + + private static double AsLocalTime(double apparentMidday, double longitude) + { + // slightly inaccurate since equation of time takes mean time not apparent time as its argument, but the difference is negligible + double universalTime = apparentMidday - AsDayFraction(longitude); + return apparentMidday - EquationOfTime(universalTime); + } + + // midday + public static double Midday(double date, double longitude) + { + return AsLocalTime(date + TwelveHours, longitude) - AsDayFraction(longitude); + } + + private static double InitLongitude(double longitude) + { + return NormalizeLongitude(longitude + HalfCircleOfArc) - HalfCircleOfArc; + } + + // midday-in-tehran + public static double MiddayAtPersianObservationSite(double date) + { + return Midday(date, InitLongitude(52.5)); // 52.5 degrees east - longitude of UTC+3:30 which defines Iranian Standard Time + } + + private static double PeriodicTerm(double julianCenturies, int x, double y, double z) + { + return x * SinOfDegree(y + z * julianCenturies); + } + + private static double SumLongSequenceOfPeriodicTerms(double julianCenturies) + { + double sum = 0.0; + sum += PeriodicTerm(julianCenturies, 403406, 270.54861, 0.9287892); + sum += PeriodicTerm(julianCenturies, 195207, 340.19128, 35999.1376958); + sum += PeriodicTerm(julianCenturies, 119433, 63.91854, 35999.4089666); + sum += PeriodicTerm(julianCenturies, 112392, 331.2622, 35998.7287385); + sum += PeriodicTerm(julianCenturies, 3891, 317.843, 71998.20261); + sum += PeriodicTerm(julianCenturies, 2819, 86.631, 71998.4403); + sum += PeriodicTerm(julianCenturies, 1721, 240.052, 36000.35726); + sum += PeriodicTerm(julianCenturies, 660, 310.26, 71997.4812); + sum += PeriodicTerm(julianCenturies, 350, 247.23, 32964.4678); + sum += PeriodicTerm(julianCenturies, 334, 260.87, -19.441); + sum += PeriodicTerm(julianCenturies, 314, 297.82, 445267.1117); + sum += PeriodicTerm(julianCenturies, 268, 343.14, 45036.884); + sum += PeriodicTerm(julianCenturies, 242, 166.79, 3.1008); + sum += PeriodicTerm(julianCenturies, 234, 81.53, 22518.4434); + sum += PeriodicTerm(julianCenturies, 158, 3.5, -19.9739); + sum += PeriodicTerm(julianCenturies, 132, 132.75, 65928.9345); + sum += PeriodicTerm(julianCenturies, 129, 182.95, 9038.0293); + sum += PeriodicTerm(julianCenturies, 114, 162.03, 3034.7684); + sum += PeriodicTerm(julianCenturies, 99, 29.8, 33718.148); + sum += PeriodicTerm(julianCenturies, 93, 266.4, 3034.448); + sum += PeriodicTerm(julianCenturies, 86, 249.2, -2280.773); + sum += PeriodicTerm(julianCenturies, 78, 157.6, 29929.992); + sum += PeriodicTerm(julianCenturies, 72, 257.8, 31556.493); + sum += PeriodicTerm(julianCenturies, 68, 185.1, 149.588); + sum += PeriodicTerm(julianCenturies, 64, 69.9, 9037.75); + sum += PeriodicTerm(julianCenturies, 46, 8.0, 107997.405); + sum += PeriodicTerm(julianCenturies, 38, 197.1, -4444.176); + sum += PeriodicTerm(julianCenturies, 37, 250.4, 151.771); + sum += PeriodicTerm(julianCenturies, 32, 65.3, 67555.316); + sum += PeriodicTerm(julianCenturies, 29, 162.7, 31556.08); + sum += PeriodicTerm(julianCenturies, 28, 341.5, -4561.54); + sum += PeriodicTerm(julianCenturies, 27, 291.6, 107996.706); + sum += PeriodicTerm(julianCenturies, 27, 98.5, 1221.655); + sum += PeriodicTerm(julianCenturies, 25, 146.7, 62894.167); + sum += PeriodicTerm(julianCenturies, 24, 110.0, 31437.369); + sum += PeriodicTerm(julianCenturies, 21, 5.2, 14578.298); + sum += PeriodicTerm(julianCenturies, 21, 342.6, -31931.757); + sum += PeriodicTerm(julianCenturies, 20, 230.9, 34777.243); + sum += PeriodicTerm(julianCenturies, 18, 256.1, 1221.999); + sum += PeriodicTerm(julianCenturies, 17, 45.3, 62894.511); + sum += PeriodicTerm(julianCenturies, 14, 242.9, -4442.039); + sum += PeriodicTerm(julianCenturies, 13, 115.2, 107997.909); + sum += PeriodicTerm(julianCenturies, 13, 151.8, 119.066); + sum += PeriodicTerm(julianCenturies, 13, 285.3, 16859.071); + sum += PeriodicTerm(julianCenturies, 12, 53.3, -4.578); + sum += PeriodicTerm(julianCenturies, 10, 126.6, 26895.292); + sum += PeriodicTerm(julianCenturies, 10, 205.7, -39.127); + sum += PeriodicTerm(julianCenturies, 10, 85.9, 12297.536); + sum += PeriodicTerm(julianCenturies, 10, 146.1, 90073.778); + return sum; + } + + private static double Aberration(double julianCenturies) + { + return (0.0000974 * CosOfDegree(177.63 + (35999.01848 * julianCenturies))) - 0.005575; + } + + private static double Nutation(double julianCenturies) + { + double a = PolynomialSum(s_coefficientsA, julianCenturies); + double b = PolynomialSum(s_coefficientsB, julianCenturies); + return (-0.004778 * SinOfDegree(a)) - (0.0003667 * SinOfDegree(b)); + } + + public static double Compute(double time) + { + double julianCenturies = JulianCenturies(time); + double lambda = 282.7771834 + + (36000.76953744 * julianCenturies) + + (0.000005729577951308232 * SumLongSequenceOfPeriodicTerms(julianCenturies)); + + double longitude = lambda + Aberration(julianCenturies) + Nutation(julianCenturies); + return InitLongitude(longitude); + } + + public static double AsSeason(double longitude) + { + return (longitude < 0) ? (longitude + FullCircleOfArc) : longitude; + } + + private static double EstimatePrior(double longitude, double time) + { + double timeSunLastAtLongitude = time - (MeanSpeedOfSun * AsSeason(InitLongitude(Compute(time) - longitude))); + double longitudeErrorDelta = InitLongitude(Compute(timeSunLastAtLongitude) - longitude); + return Math.Min(time, timeSunLastAtLongitude - (MeanSpeedOfSun * longitudeErrorDelta)); + } + + // persian-new-year-on-or-before + // number of days is the absolute date. The absolute date is the number of days from January 1st, 1 A.D. + // 1/1/0001 is absolute date 1. + internal static long PersianNewYearOnOrBefore(long numberOfDays) + { + double date = (double)numberOfDays; + + double approx = EstimatePrior(LongitudeSpring, MiddayAtPersianObservationSite(date)); + long lowerBoundNewYearDay = (long)Math.Floor(approx) - 1; + long upperBoundNewYearDay = lowerBoundNewYearDay + 3; // estimate is generally within a day of the actual occurrance (at the limits, the error expands, since the calculations rely on the mean tropical year which changes...) + long day = lowerBoundNewYearDay; + for (; day != upperBoundNewYearDay; ++day) + { + double midday = MiddayAtPersianObservationSite((double)day); + double l = Compute(midday); + if ((LongitudeSpring <= l) && (l <= TwoDegreesAfterSpring)) + { + break; + } + } + Debug.Assert(day != upperBoundNewYearDay); + + return day - 1; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/CharUnicodeInfo.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/CharUnicodeInfo.cs new file mode 100644 index 0000000000..0cd8429bbc --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/CharUnicodeInfo.cs @@ -0,0 +1,397 @@ +// 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. + +//////////////////////////////////////////////////////////////////////////// +// +// +// Purpose: This class implements a set of methods for retrieving +// character type information. Character type information is +// independent of culture and region. +// +// +//////////////////////////////////////////////////////////////////////////// + +using System.Diagnostics; + +namespace System.Globalization +{ + public static partial class CharUnicodeInfo + { + //--------------------------------------------------------------------// + // Internal Information // + //--------------------------------------------------------------------// + + // + // Native methods to access the Unicode category data tables in charinfo.nlp. + // + internal const char HIGH_SURROGATE_START = '\ud800'; + internal const char HIGH_SURROGATE_END = '\udbff'; + internal const char LOW_SURROGATE_START = '\udc00'; + internal const char LOW_SURROGATE_END = '\udfff'; + + internal const int UNICODE_CATEGORY_OFFSET = 0; + internal const int BIDI_CATEGORY_OFFSET = 1; + + // The starting codepoint for Unicode plane 1. Plane 1 contains 0x010000 ~ 0x01ffff. + internal const int UNICODE_PLANE01_START = 0x10000; + + + //////////////////////////////////////////////////////////////////////// + // + // Actions: + // Convert the BMP character or surrogate pointed by index to a UTF32 value. + // This is similar to Char.ConvertToUTF32, but the difference is that + // it does not throw exceptions when invalid surrogate characters are passed in. + // + // WARNING: since it doesn't throw an exception it CAN return a value + // in the surrogate range D800-DFFF, which are not legal unicode values. + // + //////////////////////////////////////////////////////////////////////// + + internal static int InternalConvertToUtf32(String s, int index) + { + Debug.Assert(s != null, "s != null"); + Debug.Assert(index >= 0 && index < s.Length, "index < s.Length"); + if (index < s.Length - 1) + { + int temp1 = (int)s[index] - HIGH_SURROGATE_START; + if (temp1 >= 0 && temp1 <= 0x3ff) + { + int temp2 = (int)s[index + 1] - LOW_SURROGATE_START; + if (temp2 >= 0 && temp2 <= 0x3ff) + { + // Convert the surrogate to UTF32 and get the result. + return ((temp1 * 0x400) + temp2 + UNICODE_PLANE01_START); + } + } + } + return ((int)s[index]); + } + //////////////////////////////////////////////////////////////////////// + // + // Convert a character or a surrogate pair starting at index of string s + // to UTF32 value. + // + // Parameters: + // s The string + // index The starting index. It can point to a BMP character or + // a surrogate pair. + // len The length of the string. + // charLength [out] If the index points to a BMP char, charLength + // will be 1. If the index points to a surrogate pair, + // charLength will be 2. + // + // WARNING: since it doesn't throw an exception it CAN return a value + // in the surrogate range D800-DFFF, which are not legal unicode values. + // + // Returns: + // The UTF32 value + // + //////////////////////////////////////////////////////////////////////// + + internal static int InternalConvertToUtf32(String s, int index, out int charLength) + { + Debug.Assert(s != null, "s != null"); + Debug.Assert(s.Length > 0, "s.Length > 0"); + Debug.Assert(index >= 0 && index < s.Length, "index >= 0 && index < s.Length"); + charLength = 1; + if (index < s.Length - 1) + { + int temp1 = (int)s[index] - HIGH_SURROGATE_START; + if (temp1 >= 0 && temp1 <= 0x3ff) + { + int temp2 = (int)s[index + 1] - LOW_SURROGATE_START; + if (temp2 >= 0 && temp2 <= 0x3ff) + { + // Convert the surrogate to UTF32 and get the result. + charLength++; + return ((temp1 * 0x400) + temp2 + UNICODE_PLANE01_START); + } + } + } + return ((int)s[index]); + } + + //////////////////////////////////////////////////////////////////////// + // + // IsWhiteSpace + // + // Determines if the given character is a white space character. + // + //////////////////////////////////////////////////////////////////////// + + internal static bool IsWhiteSpace(String s, int index) + { + Debug.Assert(s != null, "s!=null"); + Debug.Assert(index >= 0 && index < s.Length, "index >= 0 && index < s.Length"); + + UnicodeCategory uc = GetUnicodeCategory(s, index); + // In Unicode 3.0, U+2028 is the only character which is under the category "LineSeparator". + // And U+2029 is th eonly character which is under the category "ParagraphSeparator". + switch (uc) + { + case (UnicodeCategory.SpaceSeparator): + case (UnicodeCategory.LineSeparator): + case (UnicodeCategory.ParagraphSeparator): + return (true); + } + return (false); + } + + + internal static bool IsWhiteSpace(char c) + { + UnicodeCategory uc = GetUnicodeCategory(c); + // In Unicode 3.0, U+2028 is the only character which is under the category "LineSeparator". + // And U+2029 is th eonly character which is under the category "ParagraphSeparator". + switch (uc) + { + case (UnicodeCategory.SpaceSeparator): + case (UnicodeCategory.LineSeparator): + case (UnicodeCategory.ParagraphSeparator): + return (true); + } + + return (false); + } + + + // + // This is called by the public char and string, index versions + // + // Note that for ch in the range D800-DFFF we just treat it as any other non-numeric character + // + internal static unsafe double InternalGetNumericValue(int ch) + { + Debug.Assert(ch >= 0 && ch <= 0x10ffff, "ch is not in valid Unicode range."); + // Get the level 2 item from the highest 12 bit (8 - 19) of ch. + ushort index = s_pNumericLevel1Index[ch >> 8]; + // Get the level 2 WORD offset from the 4 - 7 bit of ch. This provides the base offset of the level 3 table. + // The offset is referred to an float item in m_pNumericFloatData. + // Note that & has the lower precedence than addition, so don't forget the parathesis. + index = s_pNumericLevel1Index[index + ((ch >> 4) & 0x000f)]; + + fixed (ushort* pUshortPtr = &(s_pNumericLevel1Index[index])) + { + byte* pBytePtr = (byte*)pUshortPtr; + fixed (byte* pByteNum = s_pNumericValues) + { + double* pDouble = (double*)pByteNum; + return pDouble[pBytePtr[(ch & 0x000f)]]; + } + } + } + + internal static unsafe ushort InternalGetDigitValues(int ch) + { + Debug.Assert(ch >= 0 && ch <= 0x10ffff, "ch is not in valid Unicode range."); + // Get the level 2 item from the highest 12 bit (8 - 19) of ch. + ushort index = s_pNumericLevel1Index[ch >> 8]; + // Get the level 2 WORD offset from the 4 - 7 bit of ch. This provides the base offset of the level 3 table. + // Note that & has the lower precedence than addition, so don't forget the parathesis. + index = s_pNumericLevel1Index[index + ((ch >> 4) & 0x000f)]; + + fixed (ushort* pUshortPtr = &(s_pNumericLevel1Index[index])) + { + byte* pBytePtr = (byte*)pUshortPtr; + return s_pDigitValues[pBytePtr[(ch & 0x000f)]]; + } + } + + //////////////////////////////////////////////////////////////////////// + // + //Returns the numeric value associated with the character c. If the character is a fraction, + // the return value will not be an integer. If the character does not have a numeric value, the return value is -1. + // + //Returns: + // the numeric value for the specified Unicode character. If the character does not have a numeric value, the return value is -1. + //Arguments: + // ch a Unicode character + //Exceptions: + // ArgumentNullException + // ArgumentOutOfRangeException + // + //////////////////////////////////////////////////////////////////////// + + + public static double GetNumericValue(char ch) + { + return (InternalGetNumericValue(ch)); + } + + + public static double GetNumericValue(String s, int index) + { + if (s == null) + { + throw new ArgumentNullException(nameof(s)); + } + if (index < 0 || index >= s.Length) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + } + return (InternalGetNumericValue(InternalConvertToUtf32(s, index))); + } + + public static int GetDecimalDigitValue(char ch) + { + return (sbyte)(InternalGetDigitValues(ch) >> 8); + } + + public static int GetDecimalDigitValue(String s, int index) + { + if (s == null) + { + throw new ArgumentNullException(nameof(s)); + } + + if (index < 0 || index >= s.Length) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + } + + return (sbyte)(InternalGetDigitValues(InternalConvertToUtf32(s, index)) >> 8); + } + + public static int GetDigitValue(char ch) + { + return (sbyte)(InternalGetDigitValues(ch) & 0x00FF); + } + + public static int GetDigitValue(String s, int index) + { + if (s == null) + { + throw new ArgumentNullException(nameof(s)); + } + + if (index < 0 || index >= s.Length) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + } + + return (sbyte)(InternalGetDigitValues(InternalConvertToUtf32(s, index)) & 0x00FF); + } + + public static UnicodeCategory GetUnicodeCategory(char ch) + { + return (GetUnicodeCategory((int)ch)); + } + + public static UnicodeCategory GetUnicodeCategory(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + return InternalGetUnicodeCategory(s, index); + } + + public static UnicodeCategory GetUnicodeCategory(int codePoint) + { + return ((UnicodeCategory)InternalGetCategoryValue(codePoint, UNICODE_CATEGORY_OFFSET)); + } + + + //////////////////////////////////////////////////////////////////////// + // + //Action: Returns the Unicode Category property for the character c. + //Returns: + // an value in UnicodeCategory enum + //Arguments: + // ch a Unicode character + //Exceptions: + // None + // + //Note that this API will return values for D800-DF00 surrogate halves. + // + //////////////////////////////////////////////////////////////////////// + + internal static unsafe byte InternalGetCategoryValue(int ch, int offset) + { + Debug.Assert(ch >= 0 && ch <= 0x10ffff, "ch is not in valid Unicode range."); + // Get the level 2 item from the highest 12 bit (8 - 19) of ch. + ushort index = s_pCategoryLevel1Index[ch >> 8]; + // Get the level 2 WORD offset from the 4 - 7 bit of ch. This provides the base offset of the level 3 table. + // Note that & has the lower precedence than addition, so don't forget the parathesis. + index = s_pCategoryLevel1Index[index + ((ch >> 4) & 0x000f)]; + + fixed (ushort* pUshortPtr = &(s_pCategoryLevel1Index[index])) + { + byte* pBytePtr = (byte*)pUshortPtr; + // Get the result from the 0 -3 bit of ch. + byte valueIndex = pBytePtr[(ch & 0x000f)]; + byte uc = s_pCategoriesValue[valueIndex * 2 + offset]; + // + // Make sure that OtherNotAssigned is the last category in UnicodeCategory. + // If that changes, change the following assertion as well. + // + //Debug.Assert(uc >= 0 && uc <= UnicodeCategory.OtherNotAssigned, "Table returns incorrect Unicode category"); + return (uc); + } + } + + //////////////////////////////////////////////////////////////////////// + // + //Action: Returns the Unicode Category property for the character c. + //Returns: + // an value in UnicodeCategory enum + //Arguments: + // value a Unicode String + // index Index for the specified string. + //Exceptions: + // None + // + //////////////////////////////////////////////////////////////////////// + + internal static UnicodeCategory InternalGetUnicodeCategory(String value, int index) + { + Debug.Assert(value != null, "value can not be null"); + Debug.Assert(index < value.Length, "index < value.Length"); + + return (GetUnicodeCategory(InternalConvertToUtf32(value, index))); + } + + internal static BidiCategory GetBidiCategory(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + return ((BidiCategory) InternalGetCategoryValue(InternalConvertToUtf32(s, index), BIDI_CATEGORY_OFFSET)); + } + + //////////////////////////////////////////////////////////////////////// + // + // Get the Unicode category of the character starting at index. If the character is in BMP, charLength will return 1. + // If the character is a valid surrogate pair, charLength will return 2. + // + //////////////////////////////////////////////////////////////////////// + + internal static UnicodeCategory InternalGetUnicodeCategory(String str, int index, out int charLength) + { + Debug.Assert(str != null, "str can not be null"); + Debug.Assert(str.Length > 0, "str.Length > 0"); ; + Debug.Assert(index >= 0 && index < str.Length, "index >= 0 && index < str.Length"); + + return (GetUnicodeCategory(InternalConvertToUtf32(str, index, out charLength))); + } + + internal static bool IsCombiningCategory(UnicodeCategory uc) + { + Debug.Assert(uc >= 0, "uc >= 0"); + return ( + uc == UnicodeCategory.NonSpacingMark || + uc == UnicodeCategory.SpacingCombiningMark || + uc == UnicodeCategory.EnclosingMark + ); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/ChineseLunisolarCalendar.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/ChineseLunisolarCalendar.cs new file mode 100644 index 0000000000..d2b52b97be --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/ChineseLunisolarCalendar.cs @@ -0,0 +1,386 @@ +// 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. + + +namespace System.Globalization +{ + /* + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 1901/02/19 2101/01/28 + ** ChineseLunisolar 1901/01/01 2100/12/29 + */ + + public class ChineseLunisolarCalendar : EastAsianLunisolarCalendar + { + // + // The era value for the current era. + // + + public const int ChineseEra = 1; + + internal const int MIN_LUNISOLAR_YEAR = 1901; + internal const int MAX_LUNISOLAR_YEAR = 2100; + + internal const int MIN_GREGORIAN_YEAR = 1901; + internal const int MIN_GREGORIAN_MONTH = 2; + internal const int MIN_GREGORIAN_DAY = 19; + + internal const int MAX_GREGORIAN_YEAR = 2101; + internal const int MAX_GREGORIAN_MONTH = 1; + internal const int MAX_GREGORIAN_DAY = 28; + + internal static DateTime minDate = new DateTime(MIN_GREGORIAN_YEAR, MIN_GREGORIAN_MONTH, MIN_GREGORIAN_DAY); + internal static DateTime maxDate = new DateTime((new DateTime(MAX_GREGORIAN_YEAR, MAX_GREGORIAN_MONTH, MAX_GREGORIAN_DAY, 23, 59, 59, 999)).Ticks + 9999); + + public override DateTime MinSupportedDateTime + { + get + { + return (minDate); + } + } + + + public override DateTime MaxSupportedDateTime + { + get + { + return (maxDate); + } + } + + protected override int DaysInYearBeforeMinSupportedYear + { + get + { + // 1900: 1-29 2-30 3-29 4-29 5-30 6-29 7-30 8-30 Leap8-29 9-30 10-30 11-29 12-30 from Calendrical Tabulations + return 384; + } + } + + + private static readonly int[,] s_yinfo = + { + /*Y LM Lmon Lday DaysPerMonth D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 D13 #Days + 1901 */ + { 0 , 2 , 19 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1902 */{ 0 , 2 , 8 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +1903 */{ 5 , 1 , 29 , 21096 },/* 29 30 29 30 29 29 30 29 29 30 30 29 30 383 +1904 */{ 0 , 2 , 16 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354 +1905 */{ 0 , 2 , 4 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355 +1906 */{ 4 , 1 , 25 , 27304 },/* 29 30 30 29 30 29 30 29 30 29 30 29 30 384 +1907 */{ 0 , 2 , 13 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1908 */{ 0 , 2 , 2 , 39632 },/* 30 29 29 30 30 29 30 29 30 30 29 30 0 355 +1909 */{ 2 , 1 , 22 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +1910 */{ 0 , 2 , 10 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1911 */{ 6 , 1 , 30 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384 +1912 */{ 0 , 2 , 18 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +1913 */{ 0 , 2 , 6 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +1914 */{ 5 , 1 , 26 , 54568 },/* 30 30 29 30 29 30 29 30 29 29 30 29 30 384 +1915 */{ 0 , 2 , 14 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354 +1916 */{ 0 , 2 , 3 , 54944 },/* 30 30 29 30 29 30 30 29 30 29 30 29 0 355 +1917 */{ 2 , 1 , 23 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384 +1918 */{ 0 , 2 , 11 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1919 */{ 7 , 2 , 1 , 18872 },/* 29 30 29 29 30 29 29 30 30 29 30 30 30 384 +1920 */{ 0 , 2 , 20 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1921 */{ 0 , 2 , 8 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1922 */{ 5 , 1 , 28 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384 +1923 */{ 0 , 2 , 16 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354 +1924 */{ 0 , 2 , 5 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354 +1925 */{ 4 , 1 , 24 , 44456 },/* 30 29 30 29 30 30 29 30 30 29 30 29 30 385 +1926 */{ 0 , 2 , 13 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1927 */{ 0 , 2 , 2 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 0 355 +1928 */{ 2 , 1 , 23 , 18808 },/* 29 30 29 29 30 29 29 30 29 30 30 30 30 384 +1929 */{ 0 , 2 , 10 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1930 */{ 6 , 1 , 30 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 29 383 +1931 */{ 0 , 2 , 17 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +1932 */{ 0 , 2 , 6 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355 +1933 */{ 5 , 1 , 26 , 27976 },/* 29 30 30 29 30 30 29 30 29 30 29 29 30 384 +1934 */{ 0 , 2 , 14 , 23248 },/* 29 30 29 30 30 29 30 29 30 30 29 30 0 355 +1935 */{ 0 , 2 , 4 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1936 */{ 3 , 1 , 24 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 29 384 +1937 */{ 0 , 2 , 11 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +1938 */{ 7 , 1 , 31 , 51560 },/* 30 30 29 29 30 29 29 30 29 30 30 29 30 384 +1939 */{ 0 , 2 , 19 , 51536 },/* 30 30 29 29 30 29 29 30 29 30 29 30 0 354 +1940 */{ 0 , 2 , 8 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +1941 */{ 6 , 1 , 27 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 29 384 +1942 */{ 0 , 2 , 15 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +1943 */{ 0 , 2 , 5 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1944 */{ 4 , 1 , 25 , 43736 },/* 30 29 30 29 30 29 30 29 30 30 29 30 30 385 +1945 */{ 0 , 2 , 13 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 +1946 */{ 0 , 2 , 2 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354 +1947 */{ 2 , 1 , 22 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384 +1948 */{ 0 , 2 , 10 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +1949 */{ 7 , 1 , 29 , 46248 },/* 30 29 30 30 29 30 29 29 30 29 30 29 30 384 +1950 */{ 0 , 2 , 17 , 27808 },/* 29 30 30 29 30 30 29 29 30 29 30 29 0 354 +1951 */{ 0 , 2 , 6 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +1952 */{ 5 , 1 , 27 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384 +1953 */{ 0 , 2 , 14 , 19872 },/* 29 30 29 29 30 30 29 30 30 29 30 29 0 354 +1954 */{ 0 , 2 , 3 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355 +1955 */{ 3 , 1 , 24 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384 +1956 */{ 0 , 2 , 12 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +1957 */{ 8 , 1 , 31 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 29 383 +1958 */{ 0 , 2 , 18 , 59728 },/* 30 30 30 29 30 29 29 30 29 30 29 30 0 355 +1959 */{ 0 , 2 , 8 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +1960 */{ 6 , 1 , 28 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384 +1961 */{ 0 , 2 , 15 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355 +1962 */{ 0 , 2 , 5 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 +1963 */{ 4 , 1 , 25 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384 +1964 */{ 0 , 2 , 13 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +1965 */{ 0 , 2 , 2 , 21088 },/* 29 30 29 30 29 29 30 29 29 30 30 29 0 353 +1966 */{ 3 , 1 , 21 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384 +1967 */{ 0 , 2 , 9 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355 +1968 */{ 7 , 1 , 30 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 +1969 */{ 0 , 2 , 17 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1970 */{ 0 , 2 , 6 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355 +1971 */{ 5 , 1 , 27 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +1972 */{ 0 , 2 , 15 , 19152 },/* 29 30 29 29 30 29 30 29 30 30 29 30 0 354 +1973 */{ 0 , 2 , 3 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +1974 */{ 4 , 1 , 23 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384 +1975 */{ 0 , 2 , 11 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +1976 */{ 8 , 1 , 31 , 54568 },/* 30 30 29 30 29 30 29 30 29 29 30 29 30 384 +1977 */{ 0 , 2 , 18 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354 +1978 */{ 0 , 2 , 7 , 46752 },/* 30 29 30 30 29 30 30 29 30 29 30 29 0 355 +1979 */{ 6 , 1 , 28 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384 +1980 */{ 0 , 2 , 16 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1981 */{ 0 , 2 , 5 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +1982 */{ 4 , 1 , 25 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384 +1983 */{ 0 , 2 , 13 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1984 */{ 10 , 2 , 2 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384 +1985 */{ 0 , 2 , 20 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354 +1986 */{ 0 , 2 , 9 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354 +1987 */{ 6 , 1 , 29 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 29 384 +1988 */{ 0 , 2 , 17 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355 +1989 */{ 0 , 2 , 6 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 0 355 +1990 */{ 5 , 1 , 27 , 18808 },/* 29 30 29 29 30 29 29 30 29 30 30 30 30 384 +1991 */{ 0 , 2 , 15 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1992 */{ 0 , 2 , 4 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354 +1993 */{ 3 , 1 , 23 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383 +1994 */{ 0 , 2 , 10 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355 +1995 */{ 8 , 1 , 31 , 27432 },/* 29 30 30 29 30 29 30 30 29 29 30 29 30 384 +1996 */{ 0 , 2 , 19 , 23232 },/* 29 30 29 30 30 29 30 29 30 30 29 29 0 354 +1997 */{ 0 , 2 , 7 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355 +1998 */{ 5 , 1 , 28 , 37736 },/* 30 29 29 30 29 29 30 30 29 30 30 29 30 384 +1999 */{ 0 , 2 , 16 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +2000 */{ 0 , 2 , 5 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354 +2001 */{ 4 , 1 , 24 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384 +2002 */{ 0 , 2 , 12 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +2003 */{ 0 , 2 , 1 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355 +2004 */{ 2 , 1 , 22 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 +2005 */{ 0 , 2 , 9 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +2006 */{ 7 , 1 , 29 , 43736 },/* 30 29 30 29 30 29 30 29 30 30 29 30 30 385 +2007 */{ 0 , 2 , 18 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 +2008 */{ 0 , 2 , 7 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354 +2009 */{ 5 , 1 , 26 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384 +2010 */{ 0 , 2 , 14 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +2011 */{ 0 , 2 , 3 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354 +2012 */{ 4 , 1 , 23 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384 +2013 */{ 0 , 2 , 10 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +2014 */{ 9 , 1 , 31 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384 +2015 */{ 0 , 2 , 19 , 19360 },/* 29 30 29 29 30 29 30 30 30 29 30 29 0 354 +2016 */{ 0 , 2 , 8 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355 +2017 */{ 6 , 1 , 28 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384 +2018 */{ 0 , 2 , 16 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +2019 */{ 0 , 2 , 5 , 43312 },/* 30 29 30 29 30 29 29 30 29 29 30 30 0 354 +2020 */{ 4 , 1 , 25 , 29864 },/* 29 30 30 30 29 30 29 29 30 29 30 29 30 384 +2021 */{ 0 , 2 , 12 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +2022 */{ 0 , 2 , 1 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +2023 */{ 2 , 1 , 22 , 19880 },/* 29 30 29 29 30 30 29 30 30 29 30 29 30 384 +2024 */{ 0 , 2 , 10 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 +2025 */{ 6 , 1 , 29 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384 +2026 */{ 0 , 2 , 17 , 42208 },/* 30 29 30 29 29 30 29 29 30 30 30 29 0 354 +2027 */{ 0 , 2 , 6 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354 +2028 */{ 5 , 1 , 26 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384 +2029 */{ 0 , 2 , 13 , 54576 },/* 30 30 29 30 29 30 29 30 29 29 30 30 0 355 +2030 */{ 0 , 2 , 3 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 +2031 */{ 3 , 1 , 23 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 29 384 +2032 */{ 0 , 2 , 11 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355 +2033 */{ 11 , 1 , 31 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +2034 */{ 0 , 2 , 19 , 19152 },/* 29 30 29 29 30 29 30 29 30 30 29 30 0 354 +2035 */{ 0 , 2 , 8 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +2036 */{ 6 , 1 , 28 , 53848 },/* 30 30 29 30 29 29 30 29 29 30 29 30 30 384 +2037 */{ 0 , 2 , 15 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +2038 */{ 0 , 2 , 4 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354 +2039 */{ 5 , 1 , 24 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 29 384 +2040 */{ 0 , 2 , 12 , 46496 },/* 30 29 30 30 29 30 29 30 30 29 30 29 0 355 +2041 */{ 0 , 2 , 1 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355 +2042 */{ 2 , 1 , 22 , 19160 },/* 29 30 29 29 30 29 30 29 30 30 29 30 30 384 +2043 */{ 0 , 2 , 10 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +2044 */{ 7 , 1 , 30 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384 +2045 */{ 0 , 2 , 17 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +2046 */{ 0 , 2 , 6 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354 +2047 */{ 5 , 1 , 26 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384 +2048 */{ 0 , 2 , 14 , 27936 },/* 29 30 30 29 30 30 29 30 29 29 30 29 0 354 +2049 */{ 0 , 2 , 2 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 0 355 +2050 */{ 3 , 1 , 23 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 29 384 +2051 */{ 0 , 2 , 11 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 0 355 +2052 */{ 8 , 2 , 1 , 18808 },/* 29 30 29 29 30 29 29 30 29 30 30 30 30 384 +2053 */{ 0 , 2 , 19 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +2054 */{ 0 , 2 , 8 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354 +2055 */{ 6 , 1 , 28 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383 +2056 */{ 0 , 2 , 15 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355 +2057 */{ 0 , 2 , 4 , 27424 },/* 29 30 30 29 30 29 30 30 29 29 30 29 0 354 +2058 */{ 4 , 1 , 24 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 29 384 +2059 */{ 0 , 2 , 12 , 43744 },/* 30 29 30 29 30 29 30 29 30 30 30 29 0 355 +2060 */{ 0 , 2 , 2 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +2061 */{ 3 , 1 , 21 , 51568 },/* 30 30 29 29 30 29 29 30 29 30 30 30 29 384 +2062 */{ 0 , 2 , 9 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354 +2063 */{ 7 , 1 , 29 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384 +2064 */{ 0 , 2 , 17 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +2065 */{ 0 , 2 , 5 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355 +2066 */{ 5 , 1 , 26 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 +2067 */{ 0 , 2 , 14 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +2068 */{ 0 , 2 , 3 , 42704 },/* 30 29 30 29 29 30 30 29 30 30 29 30 0 355 +2069 */{ 4 , 1 , 23 , 21224 },/* 29 30 29 30 29 29 30 29 30 30 30 29 30 384 +2070 */{ 0 , 2 , 11 , 21200 },/* 29 30 29 30 29 29 30 29 30 30 29 30 0 354 +2071 */{ 8 , 1 , 31 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384 +2072 */{ 0 , 2 , 19 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +2073 */{ 0 , 2 , 7 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354 +2074 */{ 6 , 1 , 27 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384 +2075 */{ 0 , 2 , 15 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +2076 */{ 0 , 2 , 5 , 21920 },/* 29 30 29 30 29 30 29 30 30 29 30 29 0 354 +2077 */{ 4 , 1 , 24 , 42448 },/* 30 29 30 29 29 30 29 30 30 30 29 30 29 384 +2078 */{ 0 , 2 , 12 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355 +2079 */{ 0 , 2 , 2 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +2080 */{ 3 , 1 , 22 , 43320 },/* 30 29 30 29 30 29 29 30 29 29 30 30 30 384 +2081 */{ 0 , 2 , 9 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 0 354 +2082 */{ 7 , 1 , 29 , 29336 },/* 29 30 30 30 29 29 30 29 30 29 29 30 30 384 +2083 */{ 0 , 2 , 17 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +2084 */{ 0 , 2 , 6 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +2085 */{ 5 , 1 , 26 , 19880 },/* 29 30 29 29 30 30 29 30 30 29 30 29 30 384 +2086 */{ 0 , 2 , 14 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 +2087 */{ 0 , 2 , 3 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +2088 */{ 4 , 1 , 24 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 29 383 +2089 */{ 0 , 2 , 10 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354 +2090 */{ 8 , 1 , 30 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384 +2091 */{ 0 , 2 , 18 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354 +2092 */{ 0 , 2 , 7 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 0 355 +2093 */{ 6 , 1 , 27 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 29 384 +2094 */{ 0 , 2 , 15 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355 +2095 */{ 0 , 2 , 5 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +2096 */{ 4 , 1 , 25 , 42216 },/* 30 29 30 29 29 30 29 29 30 30 30 29 30 384 +2097 */{ 0 , 2 , 12 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +2098 */{ 0 , 2 , 1 , 53584 },/* 30 30 29 30 29 29 29 30 29 30 29 30 0 354 +2099 */{ 2 , 1 , 21 , 55592 },/* 30 30 29 30 30 29 29 30 29 29 30 29 30 384 +2100 */{ 0 , 2 , 9 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354 + */}; + + + internal override int MinCalendarYear + { + get + { + return (MIN_LUNISOLAR_YEAR); + } + } + + internal override int MaxCalendarYear + { + get + { + return (MAX_LUNISOLAR_YEAR); + } + } + + internal override DateTime MinDate + { + get + { + return (minDate); + } + } + + internal override DateTime MaxDate + { + get + { + return (maxDate); + } + } + + internal override EraInfo[] CalEraInfo + { + get + { + return (null); + } + } + + internal override int GetYearInfo(int lunarYear, int index) + { + if ((lunarYear < MIN_LUNISOLAR_YEAR) || (lunarYear > MAX_LUNISOLAR_YEAR)) + { + throw new ArgumentOutOfRangeException( + "year", + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, MIN_LUNISOLAR_YEAR, MAX_LUNISOLAR_YEAR)); + } + + return s_yinfo[lunarYear - MIN_LUNISOLAR_YEAR, index]; + } + + internal override int GetYear(int year, DateTime time) + { + return year; + } + + internal override int GetGregorianYear(int year, int era) + { + if (era != CurrentEra && era != ChineseEra) + { + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + + if (year < MIN_LUNISOLAR_YEAR || year > MAX_LUNISOLAR_YEAR) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, MIN_LUNISOLAR_YEAR, MAX_LUNISOLAR_YEAR)); + } + + return year; + } + + public ChineseLunisolarCalendar() + { + } + + public override int GetEra(DateTime time) + { + CheckTicksRange(time.Ticks); + return (ChineseEra); + } + + internal override CalendarId ID + { + get + { + return (CalendarId.CHINESELUNISOLAR); + } + } + + internal override CalendarId BaseCalendarID + { + get + { + //Use CAL_GREGORIAN just to get CurrentEraValue as 1 since we do not have data under the ID CAL_ChineseLunisolar yet + return (CalendarId.GREGORIAN); + } + } + + + public override int[] Eras + { + get + { + return (new int[] { ChineseEra }); + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/CompareInfo.Invariant.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/CompareInfo.Invariant.cs new file mode 100644 index 0000000000..13725bcc51 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/CompareInfo.Invariant.cs @@ -0,0 +1,236 @@ +// 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.Diagnostics; + +namespace System.Globalization +{ + public partial class CompareInfo + { + internal static unsafe int InvariantIndexOf(string source, string value, int startIndex, int count, bool ignoreCase) + { + Debug.Assert(source != null); + Debug.Assert(value != null); + Debug.Assert(startIndex >= 0 && startIndex < source.Length); + + fixed (char* pSource = source) fixed (char* pValue = value) + { + char* pSrc = &pSource[startIndex]; + int index = InvariantFindString(pSrc, count, pValue, value.Length, ignoreCase, start : true); + if (index >= 0) + { + return index + startIndex; + } + return -1; + } + } + + internal static unsafe int InvariantLastIndexOf(string source, string value, int startIndex, int count, bool ignoreCase) + { + Debug.Assert(source != null); + Debug.Assert(value != null); + Debug.Assert(startIndex >= 0 && startIndex < source.Length); + + fixed (char* pSource = source) fixed (char* pValue = value) + { + char* pSrc = &pSource[startIndex - count + 1]; + int index = InvariantFindString(pSrc, count, pValue, value.Length, ignoreCase, start : false); + if (index >= 0) + { + return index + startIndex - count + 1; + } + return -1; + } + } + + private static unsafe int InvariantFindString(char* source, int sourceCount, char* value, int valueCount, bool ignoreCase, bool start) + { + int ctrSource = 0; // index value into source + int ctrValue = 0; // index value into value + char sourceChar; // Character for case lookup in source + char valueChar; // Character for case lookup in value + int lastSourceStart; + + Debug.Assert(source != null); + Debug.Assert(value != null); + Debug.Assert(sourceCount >= 0); + Debug.Assert(valueCount >= 0); + + if (valueCount == 0) + { + return start ? 0 : sourceCount - 1; + } + + if (sourceCount < valueCount) + { + return -1; + } + + if (start) + { + lastSourceStart = sourceCount - valueCount; + if (ignoreCase) + { + char firstValueChar = InvariantToUpper(value[0]); + for (ctrSource = 0; ctrSource <= lastSourceStart; ctrSource++) + { + sourceChar = InvariantToUpper(source[ctrSource]); + if (sourceChar != firstValueChar) + { + continue; + } + + for (ctrValue = 1; ctrValue < valueCount; ctrValue++) + { + sourceChar = InvariantToUpper(source[ctrSource + ctrValue]); + valueChar = InvariantToUpper(value[ctrValue]); + + if (sourceChar != valueChar) + { + break; + } + } + + if (ctrValue == valueCount) + { + return ctrSource; + } + } + } + else + { + char firstValueChar = value[0]; + for (ctrSource = 0; ctrSource <= lastSourceStart; ctrSource++) + { + sourceChar = source[ctrSource]; + if (sourceChar != firstValueChar) + { + continue; + } + + for (ctrValue = 1; ctrValue < valueCount; ctrValue++) + { + sourceChar = source[ctrSource + ctrValue]; + valueChar = value[ctrValue]; + + if (sourceChar != valueChar) + { + break; + } + } + + if (ctrValue == valueCount) + { + return ctrSource; + } + } + } + } + else + { + lastSourceStart = sourceCount - valueCount; + if (ignoreCase) + { + char firstValueChar = InvariantToUpper(value[0]); + for (ctrSource = lastSourceStart; ctrSource >= 0; ctrSource--) + { + sourceChar = InvariantToUpper(source[ctrSource]); + if (sourceChar != firstValueChar) + { + continue; + } + for (ctrValue = 1; ctrValue < valueCount; ctrValue++) + { + sourceChar = InvariantToUpper(source[ctrSource + ctrValue]); + valueChar = InvariantToUpper(value[ctrValue]); + + if (sourceChar != valueChar) + { + break; + } + } + + if (ctrValue == valueCount) + { + return ctrSource; + } + } + } + else + { + char firstValueChar = value[0]; + for (ctrSource = lastSourceStart; ctrSource >= 0; ctrSource--) + { + sourceChar = source[ctrSource]; + if (sourceChar != firstValueChar) + { + continue; + } + + for (ctrValue = 1; ctrValue < valueCount; ctrValue++) + { + sourceChar = source[ctrSource + ctrValue]; + valueChar = value[ctrValue]; + + if (sourceChar != valueChar) + { + break; + } + } + + if (ctrValue == valueCount) + { + return ctrSource; + } + } + } + } + + return -1; + } + + private static char InvariantToUpper(char c) + { + return (uint)(c - 'a') <= (uint)('z' - 'a') ? (char)(c - 0x20) : c; + } + + private unsafe SortKey InvariantCreateSortKey(string source, CompareOptions options) + { + if (source == null) { throw new ArgumentNullException(nameof(source)); } + + if ((options & ValidSortkeyCtorMaskOffFlags) != 0) + { + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); + } + + byte [] keyData; + if (source.Length == 0) + { + keyData = Array.Empty(); + } + else + { + // In the invariant mode, all string comparisons are done as ordinal so when generating the sort keys we generate it according to this fact + keyData = new byte[source.Length * sizeof(char)]; + + fixed (char* pChar = source) fixed (byte* pByte = keyData) + { + if ((options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0) + { + short *pShort = (short *) pByte; + for (int i=0; i null + } + + if (_invariantMode) + { + if ((options & CompareOptions.IgnoreCase) != 0) + return CompareOrdinalIgnoreCase(string1, 0, string1.Length, string2, 0, string2.Length); + + return String.CompareOrdinal(string1, string2); + } + + return CompareString(string1.AsReadOnlySpan(), string2.AsReadOnlySpan(), options); + } + + // TODO https://github.com/dotnet/coreclr/issues/13827: + // This method shouldn't be necessary, as we should be able to just use the overload + // that takes two spans. But due to this issue, that's adding significant overhead. + internal unsafe int Compare(ReadOnlySpan string1, string string2, CompareOptions options) + { + if (options == CompareOptions.OrdinalIgnoreCase) + { + return CompareOrdinalIgnoreCase(string1, string2.AsReadOnlySpan()); + } + + // Verify the options before we do any real comparison. + if ((options & CompareOptions.Ordinal) != 0) + { + if (options != CompareOptions.Ordinal) + { + throw new ArgumentException(SR.Argument_CompareOptionOrdinal, nameof(options)); + } + + return string.CompareOrdinal(string1, string2.AsReadOnlySpan()); + } + + if ((options & ValidCompareMaskOffFlags) != 0) + { + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); + } + + // null sorts less than any other string. + if (string2 == null) + { + return 1; + } + + if (_invariantMode) + { + return (options & CompareOptions.IgnoreCase) != 0 ? + CompareOrdinalIgnoreCase(string1, string2.AsReadOnlySpan()) : + string.CompareOrdinal(string1, string2.AsReadOnlySpan()); + } + + return CompareString(string1, string2, options); + } + + // TODO https://github.com/dotnet/corefx/issues/21395: Expose this publicly? + internal unsafe virtual int Compare(ReadOnlySpan string1, ReadOnlySpan string2, CompareOptions options) + { + if (options == CompareOptions.OrdinalIgnoreCase) + { + return CompareOrdinalIgnoreCase(string1, string2); + } + + // Verify the options before we do any real comparison. + if ((options & CompareOptions.Ordinal) != 0) + { + if (options != CompareOptions.Ordinal) + { + throw new ArgumentException(SR.Argument_CompareOptionOrdinal, nameof(options)); + } + + return string.CompareOrdinal(string1, string2); + } + + if ((options & ValidCompareMaskOffFlags) != 0) + { + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); + } + + if (_invariantMode) + { + return (options & CompareOptions.IgnoreCase) != 0 ? + CompareOrdinalIgnoreCase(string1, string2) : + string.CompareOrdinal(string1, string2); + } + + return CompareString(string1, string2, options); + } + + //////////////////////////////////////////////////////////////////////// + // + // Compare + // + // Compares the specified regions of the two strings with the given + // options. + // Returns 0 if the two strings are equal, a number less than 0 if + // string1 is less than string2, and a number greater than 0 if + // string1 is greater than string2. + // + //////////////////////////////////////////////////////////////////////// + + + public unsafe virtual int Compare(string string1, int offset1, int length1, string string2, int offset2, int length2) + { + return Compare(string1, offset1, length1, string2, offset2, length2, 0); + } + + + public virtual int Compare(string string1, int offset1, string string2, int offset2, CompareOptions options) + { + return Compare(string1, offset1, string1 == null ? 0 : string1.Length - offset1, + string2, offset2, string2 == null ? 0 : string2.Length - offset2, options); + } + + + public virtual int Compare(string string1, int offset1, string string2, int offset2) + { + return Compare(string1, offset1, string2, offset2, 0); + } + + + public virtual int Compare(string string1, int offset1, int length1, string string2, int offset2, int length2, CompareOptions options) + { + if (options == CompareOptions.OrdinalIgnoreCase) + { + int result = String.Compare(string1, offset1, string2, offset2, length1 < length2 ? length1 : length2, StringComparison.OrdinalIgnoreCase); + if ((length1 != length2) && result == 0) + return (length1 > length2 ? 1 : -1); + return (result); + } + + // Verify inputs + if (length1 < 0 || length2 < 0) + { + throw new ArgumentOutOfRangeException((length1 < 0) ? nameof(length1) : nameof(length2), SR.ArgumentOutOfRange_NeedPosNum); + } + if (offset1 < 0 || offset2 < 0) + { + throw new ArgumentOutOfRangeException((offset1 < 0) ? nameof(offset1) : nameof(offset2), SR.ArgumentOutOfRange_NeedPosNum); + } + if (offset1 > (string1 == null ? 0 : string1.Length) - length1) + { + throw new ArgumentOutOfRangeException(nameof(string1), SR.ArgumentOutOfRange_OffsetLength); + } + if (offset2 > (string2 == null ? 0 : string2.Length) - length2) + { + throw new ArgumentOutOfRangeException(nameof(string2), SR.ArgumentOutOfRange_OffsetLength); + } + if ((options & CompareOptions.Ordinal) != 0) + { + if (options != CompareOptions.Ordinal) + { + throw new ArgumentException(SR.Argument_CompareOptionOrdinal, + nameof(options)); + } + } + else if ((options & ValidCompareMaskOffFlags) != 0) + { + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); + } + + // + // Check for the null case. + // + if (string1 == null) + { + if (string2 == null) + { + return (0); + } + return (-1); + } + if (string2 == null) + { + return (1); + } + + if (options == CompareOptions.Ordinal) + { + return CompareOrdinal(string1, offset1, length1, + string2, offset2, length2); + } + + if (_invariantMode) + { + if ((options & CompareOptions.IgnoreCase) != 0) + return CompareOrdinalIgnoreCase(string1, offset1, length1, string2, offset2, length2); + + return CompareOrdinal(string1, offset1, length1, string2, offset2, length2); + } + + return CompareString( + string1.AsReadOnlySpan().Slice(offset1, length1), + string2.AsReadOnlySpan().Slice(offset2, length2), + options); + } + + private static int CompareOrdinal(string string1, int offset1, int length1, string string2, int offset2, int length2) + { + int result = String.CompareOrdinal(string1, offset1, string2, offset2, + (length1 < length2 ? length1 : length2)); + if ((length1 != length2) && result == 0) + { + return (length1 > length2 ? 1 : -1); + } + return (result); + } + + // + // CompareOrdinalIgnoreCase compare two string ordinally with ignoring the case. + // it assumes the strings are Ascii string till we hit non Ascii character in strA or strB and then we continue the comparison by + // calling the OS. + // + internal static unsafe int CompareOrdinalIgnoreCase(string strA, int indexA, int lengthA, string strB, int indexB, int lengthB) + { + Debug.Assert(indexA + lengthA <= strA.Length); + Debug.Assert(indexB + lengthB <= strB.Length); + return CompareOrdinalIgnoreCase(strA.AsReadOnlySpan().Slice(indexA, lengthA), strB.AsReadOnlySpan().Slice(indexB, lengthB)); + } + + internal static unsafe int CompareOrdinalIgnoreCase(ReadOnlySpan strA, ReadOnlySpan strB) + { + int length = Math.Min(strA.Length, strB.Length); + int range = length; + + fixed (char* ap = &MemoryMarshal.GetReference(strA)) + fixed (char* bp = &MemoryMarshal.GetReference(strB)) + { + char* a = ap; + char* b = bp; + + // in InvariantMode we support all range and not only the ascii characters. + char maxChar = (char) (GlobalizationMode.Invariant ? 0xFFFF : 0x80); + + while (length != 0 && (*a <= maxChar) && (*b <= maxChar)) + { + int charA = *a; + int charB = *b; + + if (charA == charB) + { + a++; b++; + length--; + continue; + } + + // uppercase both chars - notice that we need just one compare per char + if ((uint)(charA - 'a') <= 'z' - 'a') charA -= 0x20; + if ((uint)(charB - 'a') <= 'z' - 'a') charB -= 0x20; + + // Return the (case-insensitive) difference between them. + if (charA != charB) + return charA - charB; + + // Next char + a++; b++; + length--; + } + + if (length == 0) + return strA.Length - strB.Length; + + Debug.Assert(!GlobalizationMode.Invariant); + + range -= length; + + return CompareStringOrdinalIgnoreCase(a, strA.Length - range, b, strB.Length - range); + } + } + + //////////////////////////////////////////////////////////////////////// + // + // IsPrefix + // + // Determines whether prefix is a prefix of string. If prefix equals + // String.Empty, true is returned. + // + //////////////////////////////////////////////////////////////////////// + public virtual bool IsPrefix(string source, string prefix, CompareOptions options) + { + if (source == null || prefix == null) + { + throw new ArgumentNullException((source == null ? nameof(source) : nameof(prefix)), + SR.ArgumentNull_String); + } + + if (prefix.Length == 0) + { + return (true); + } + + if (source.Length == 0) + { + return false; + } + + if (options == CompareOptions.OrdinalIgnoreCase) + { + return source.StartsWith(prefix, StringComparison.OrdinalIgnoreCase); + } + + if (options == CompareOptions.Ordinal) + { + return source.StartsWith(prefix, StringComparison.Ordinal); + } + + if ((options & ValidIndexMaskOffFlags) != 0) + { + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); + } + + if (_invariantMode) + { + return source.StartsWith(prefix, (options & CompareOptions.IgnoreCase) != 0 ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); + } + + return StartsWith(source, prefix, options); + } + + public virtual bool IsPrefix(string source, string prefix) + { + return (IsPrefix(source, prefix, 0)); + } + + //////////////////////////////////////////////////////////////////////// + // + // IsSuffix + // + // Determines whether suffix is a suffix of string. If suffix equals + // String.Empty, true is returned. + // + //////////////////////////////////////////////////////////////////////// + public virtual bool IsSuffix(string source, string suffix, CompareOptions options) + { + if (source == null || suffix == null) + { + throw new ArgumentNullException((source == null ? nameof(source) : nameof(suffix)), + SR.ArgumentNull_String); + } + + if (suffix.Length == 0) + { + return (true); + } + + if (source.Length == 0) + { + return false; + } + + if (options == CompareOptions.OrdinalIgnoreCase) + { + return source.EndsWith(suffix, StringComparison.OrdinalIgnoreCase); + } + + if (options == CompareOptions.Ordinal) + { + return source.EndsWith(suffix, StringComparison.Ordinal); + } + + if ((options & ValidIndexMaskOffFlags) != 0) + { + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); + } + + if (_invariantMode) + { + return source.EndsWith(suffix, (options & CompareOptions.IgnoreCase) != 0 ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); + } + + return EndsWith(source, suffix, options); + } + + + public virtual bool IsSuffix(string source, string suffix) + { + return (IsSuffix(source, suffix, 0)); + } + + //////////////////////////////////////////////////////////////////////// + // + // IndexOf + // + // Returns the first index where value is found in string. The + // search starts from startIndex and ends at endIndex. Returns -1 if + // the specified value is not found. If value equals String.Empty, + // startIndex is returned. Throws IndexOutOfRange if startIndex or + // endIndex is less than zero or greater than the length of string. + // Throws ArgumentException if value is null. + // + //////////////////////////////////////////////////////////////////////// + + + public virtual int IndexOf(string source, char value) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + + return IndexOf(source, value, 0, source.Length, CompareOptions.None); + } + + + public virtual int IndexOf(string source, string value) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + + return IndexOf(source, value, 0, source.Length, CompareOptions.None); + } + + + public virtual int IndexOf(string source, char value, CompareOptions options) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + + return IndexOf(source, value, 0, source.Length, options); + } + + + public virtual int IndexOf(string source, string value, CompareOptions options) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + + return IndexOf(source, value, 0, source.Length, options); + } + + public virtual int IndexOf(string source, char value, int startIndex) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + + return IndexOf(source, value, startIndex, source.Length - startIndex, CompareOptions.None); + } + + public virtual int IndexOf(string source, string value, int startIndex) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + + return IndexOf(source, value, startIndex, source.Length - startIndex, CompareOptions.None); + } + + public virtual int IndexOf(string source, char value, int startIndex, CompareOptions options) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + + return IndexOf(source, value, startIndex, source.Length - startIndex, options); + } + + + public virtual int IndexOf(string source, string value, int startIndex, CompareOptions options) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + + return IndexOf(source, value, startIndex, source.Length - startIndex, options); + } + + + public virtual int IndexOf(string source, char value, int startIndex, int count) + { + return IndexOf(source, value, startIndex, count, CompareOptions.None); + } + + + public virtual int IndexOf(string source, string value, int startIndex, int count) + { + return IndexOf(source, value, startIndex, count, CompareOptions.None); + } + + public unsafe virtual int IndexOf(string source, char value, int startIndex, int count, CompareOptions options) + { + // Validate inputs + if (source == null) + throw new ArgumentNullException(nameof(source)); + + if (startIndex < 0 || startIndex > source.Length) + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); + + if (count < 0 || startIndex > source.Length - count) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count); + + if (options == CompareOptions.OrdinalIgnoreCase) + { + return source.IndexOf(value.ToString(), startIndex, count, StringComparison.OrdinalIgnoreCase); + } + + // Validate CompareOptions + // Ordinal can't be selected with other flags + if ((options & ValidIndexMaskOffFlags) != 0 && (options != CompareOptions.Ordinal)) + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); + + if (_invariantMode) + return IndexOfOrdinal(source, new string(value, 1), startIndex, count, ignoreCase: (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0); + + return IndexOfCore(source, new string(value, 1), startIndex, count, options, null); + } + + public unsafe virtual int IndexOf(string source, string value, int startIndex, int count, CompareOptions options) + { + // Validate inputs + if (source == null) + throw new ArgumentNullException(nameof(source)); + if (value == null) + throw new ArgumentNullException(nameof(value)); + + if (startIndex > source.Length) + { + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); + } + + // In Everett we used to return -1 for empty string even if startIndex is negative number so we keeping same behavior here. + // We return 0 if both source and value are empty strings for Everett compatibility too. + if (source.Length == 0) + { + if (value.Length == 0) + { + return 0; + } + return -1; + } + + if (startIndex < 0) + { + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); + } + + if (count < 0 || startIndex > source.Length - count) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count); + + if (options == CompareOptions.OrdinalIgnoreCase) + { + return IndexOfOrdinal(source, value, startIndex, count, ignoreCase: true); + } + + // Validate CompareOptions + // Ordinal can't be selected with other flags + if ((options & ValidIndexMaskOffFlags) != 0 && (options != CompareOptions.Ordinal)) + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); + + if (_invariantMode) + return IndexOfOrdinal(source, value, startIndex, count, ignoreCase: (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0); + + return IndexOfCore(source, value, startIndex, count, options, null); + } + + // The following IndexOf overload is mainly used by String.Replace. This overload assumes the parameters are already validated + // and the caller is passing a valid matchLengthPtr pointer. + internal unsafe int IndexOf(string source, string value, int startIndex, int count, CompareOptions options, int* matchLengthPtr) + { + Debug.Assert(source != null); + Debug.Assert(value != null); + Debug.Assert(startIndex >= 0); + Debug.Assert(matchLengthPtr != null); + *matchLengthPtr = 0; + + if (source.Length == 0) + { + if (value.Length == 0) + { + return 0; + } + return -1; + } + + if (startIndex >= source.Length) + { + return -1; + } + + if (options == CompareOptions.OrdinalIgnoreCase) + { + int res = IndexOfOrdinal(source, value, startIndex, count, ignoreCase: true); + if (res >= 0) + { + *matchLengthPtr = value.Length; + } + return res; + } + + if (_invariantMode) + { + int res = IndexOfOrdinal(source, value, startIndex, count, ignoreCase: (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0); + if (res >= 0) + { + *matchLengthPtr = value.Length; + } + return res; + } + + return IndexOfCore(source, value, startIndex, count, options, matchLengthPtr); + } + + internal int IndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase) + { + if (_invariantMode) + { + return InvariantIndexOf(source, value, startIndex, count, ignoreCase); + } + + return IndexOfOrdinalCore(source, value, startIndex, count, ignoreCase); + } + + //////////////////////////////////////////////////////////////////////// + // + // LastIndexOf + // + // Returns the last index where value is found in string. The + // search starts from startIndex and ends at endIndex. Returns -1 if + // the specified value is not found. If value equals String.Empty, + // endIndex is returned. Throws IndexOutOfRange if startIndex or + // endIndex is less than zero or greater than the length of string. + // Throws ArgumentException if value is null. + // + //////////////////////////////////////////////////////////////////////// + + + public virtual int LastIndexOf(String source, char value) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + + // Can't start at negative index, so make sure we check for the length == 0 case. + return LastIndexOf(source, value, source.Length - 1, source.Length, CompareOptions.None); + } + + + public virtual int LastIndexOf(string source, string value) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + + // Can't start at negative index, so make sure we check for the length == 0 case. + return LastIndexOf(source, value, source.Length - 1, + source.Length, CompareOptions.None); + } + + + public virtual int LastIndexOf(string source, char value, CompareOptions options) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + + // Can't start at negative index, so make sure we check for the length == 0 case. + return LastIndexOf(source, value, source.Length - 1, + source.Length, options); + } + + public virtual int LastIndexOf(string source, string value, CompareOptions options) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + + // Can't start at negative index, so make sure we check for the length == 0 case. + return LastIndexOf(source, value, source.Length - 1, source.Length, options); + } + + public virtual int LastIndexOf(string source, char value, int startIndex) + { + return LastIndexOf(source, value, startIndex, startIndex + 1, CompareOptions.None); + } + + + public virtual int LastIndexOf(string source, string value, int startIndex) + { + return LastIndexOf(source, value, startIndex, startIndex + 1, CompareOptions.None); + } + + public virtual int LastIndexOf(string source, char value, int startIndex, CompareOptions options) + { + return LastIndexOf(source, value, startIndex, startIndex + 1, options); + } + + + public virtual int LastIndexOf(string source, string value, int startIndex, CompareOptions options) + { + return LastIndexOf(source, value, startIndex, startIndex + 1, options); + } + + + public virtual int LastIndexOf(string source, char value, int startIndex, int count) + { + return LastIndexOf(source, value, startIndex, count, CompareOptions.None); + } + + + public virtual int LastIndexOf(string source, string value, int startIndex, int count) + { + return LastIndexOf(source, value, startIndex, count, CompareOptions.None); + } + + + public virtual int LastIndexOf(string source, char value, int startIndex, int count, CompareOptions options) + { + // Verify Arguments + if (source == null) + throw new ArgumentNullException(nameof(source)); + + // Validate CompareOptions + // Ordinal can't be selected with other flags + if ((options & ValidIndexMaskOffFlags) != 0 && + (options != CompareOptions.Ordinal) && + (options != CompareOptions.OrdinalIgnoreCase)) + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); + + // Special case for 0 length input strings + if (source.Length == 0 && (startIndex == -1 || startIndex == 0)) + return -1; + + // Make sure we're not out of range + if (startIndex < 0 || startIndex > source.Length) + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); + + // Make sure that we allow startIndex == source.Length + if (startIndex == source.Length) + { + startIndex--; + if (count > 0) + count--; + } + + // 2nd have of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0. + if (count < 0 || startIndex - count + 1 < 0) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count); + + if (options == CompareOptions.OrdinalIgnoreCase) + { + return source.LastIndexOf(value.ToString(), startIndex, count, StringComparison.OrdinalIgnoreCase); + } + + if (_invariantMode) + return InvariantLastIndexOf(source, new string(value, 1), startIndex, count, (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0); + + return LastIndexOfCore(source, value.ToString(), startIndex, count, options); + } + + + public virtual int LastIndexOf(string source, string value, int startIndex, int count, CompareOptions options) + { + // Verify Arguments + if (source == null) + throw new ArgumentNullException(nameof(source)); + if (value == null) + throw new ArgumentNullException(nameof(value)); + + // Validate CompareOptions + // Ordinal can't be selected with other flags + if ((options & ValidIndexMaskOffFlags) != 0 && + (options != CompareOptions.Ordinal) && + (options != CompareOptions.OrdinalIgnoreCase)) + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); + + // Special case for 0 length input strings + if (source.Length == 0 && (startIndex == -1 || startIndex == 0)) + return (value.Length == 0) ? 0 : -1; + + // Make sure we're not out of range + if (startIndex < 0 || startIndex > source.Length) + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); + + // Make sure that we allow startIndex == source.Length + if (startIndex == source.Length) + { + startIndex--; + if (count > 0) + count--; + + // If we are looking for nothing, just return 0 + if (value.Length == 0 && count >= 0 && startIndex - count + 1 >= 0) + return startIndex; + } + + // 2nd half of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0. + if (count < 0 || startIndex - count + 1 < 0) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count); + + if (options == CompareOptions.OrdinalIgnoreCase) + { + return LastIndexOfOrdinal(source, value, startIndex, count, ignoreCase: true); + } + + if (_invariantMode) + return InvariantLastIndexOf(source, value, startIndex, count, (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0); + + return LastIndexOfCore(source, value, startIndex, count, options); + } + + internal int LastIndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase) + { + if (_invariantMode) + { + return InvariantLastIndexOf(source, value, startIndex, count, ignoreCase); + } + + return LastIndexOfOrdinalCore(source, value, startIndex, count, ignoreCase); + } + + //////////////////////////////////////////////////////////////////////// + // + // GetSortKey + // + // Gets the SortKey for the given string with the given options. + // + //////////////////////////////////////////////////////////////////////// + public virtual SortKey GetSortKey(string source, CompareOptions options) + { + if (_invariantMode) + return InvariantCreateSortKey(source, options); + + return CreateSortKey(source, options); + } + + + public virtual SortKey GetSortKey(string source) + { + if (_invariantMode) + return InvariantCreateSortKey(source, CompareOptions.None); + + return CreateSortKey(source, CompareOptions.None); + } + + //////////////////////////////////////////////////////////////////////// + // + // Equals + // + // Implements Object.Equals(). Returns a boolean indicating whether + // or not object refers to the same CompareInfo as the current + // instance. + // + //////////////////////////////////////////////////////////////////////// + + + public override bool Equals(Object value) + { + CompareInfo that = value as CompareInfo; + + if (that != null) + { + return this.Name == that.Name; + } + + return (false); + } + + + //////////////////////////////////////////////////////////////////////// + // + // GetHashCode + // + // Implements Object.GetHashCode(). Returns the hash code for the + // CompareInfo. The hash code is guaranteed to be the same for + // CompareInfo A and B where A.Equals(B) is true. + // + //////////////////////////////////////////////////////////////////////// + + + public override int GetHashCode() + { + return (this.Name.GetHashCode()); + } + + + //////////////////////////////////////////////////////////////////////// + // + // GetHashCodeOfString + // + // This internal method allows a method that allows the equivalent of creating a Sortkey for a + // string from CompareInfo, and generate a hashcode value from it. It is not very convenient + // to use this method as is and it creates an unnecessary Sortkey object that will be GC'ed. + // + // The hash code is guaranteed to be the same for string A and B where A.Equals(B) is true and both + // the CompareInfo and the CompareOptions are the same. If two different CompareInfo objects + // treat the string the same way, this implementation will treat them differently (the same way that + // Sortkey does at the moment). + // + // This method will never be made public itself, but public consumers of it could be created, e.g.: + // + // string.GetHashCode(CultureInfo) + // string.GetHashCode(CompareInfo) + // string.GetHashCode(CultureInfo, CompareOptions) + // string.GetHashCode(CompareInfo, CompareOptions) + // etc. + // + // (the methods above that take a CultureInfo would use CultureInfo.CompareInfo) + // + //////////////////////////////////////////////////////////////////////// + internal int GetHashCodeOfString(string source, CompareOptions options) + { + // + // Parameter validation + // + if (null == source) + { + throw new ArgumentNullException(nameof(source)); + } + + if ((options & ValidHashCodeOfStringMaskOffFlags) != 0) + { + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); + } + + return GetHashCodeOfStringCore(source, options); + } + + public virtual int GetHashCode(string source, CompareOptions options) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (options == CompareOptions.Ordinal) + { + return source.GetHashCode(); + } + + if (options == CompareOptions.OrdinalIgnoreCase) + { + return TextInfo.GetHashCodeOrdinalIgnoreCase(source); + } + + // + // GetHashCodeOfString does more parameters validation. basically will throw when + // having Ordinal, OrdinalIgnoreCase and StringSort + // + + return GetHashCodeOfString(source, options); + } + + //////////////////////////////////////////////////////////////////////// + // + // ToString + // + // Implements Object.ToString(). Returns a string describing the + // CompareInfo. + // + //////////////////////////////////////////////////////////////////////// + public override string ToString() + { + return ("CompareInfo - " + this.Name); + } + + public SortVersion Version + { + get + { + if (m_SortVersion == null) + { + if (_invariantMode) + { + m_SortVersion = new SortVersion(0, CultureInfo.LOCALE_INVARIANT, new Guid(0, 0, 0, 0, 0, 0, 0, + (byte) (CultureInfo.LOCALE_INVARIANT >> 24), + (byte) ((CultureInfo.LOCALE_INVARIANT & 0x00FF0000) >> 16), + (byte) ((CultureInfo.LOCALE_INVARIANT & 0x0000FF00) >> 8), + (byte) (CultureInfo.LOCALE_INVARIANT & 0xFF))); + } + else + { + m_SortVersion = GetSortVersion(); + } + } + + return m_SortVersion; + } + } + + public int LCID + { + get + { + return CultureInfo.GetCultureInfo(Name).LCID; + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/CultureData.Unix.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/CultureData.Unix.cs new file mode 100644 index 0000000000..1b335c28ee --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/CultureData.Unix.cs @@ -0,0 +1,430 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Security; +using System.Text; + +namespace System.Globalization +{ + internal partial class CultureData + { + // ICU constants + const int ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY = 100; // max size of keyword or value + const int ICU_ULOC_FULLNAME_CAPACITY = 157; // max size of locale name + const string ICU_COLLATION_KEYWORD = "@collation="; + + + /// + /// This method uses the sRealName field (which is initialized by the constructor before this is called) to + /// initialize the rest of the state of CultureData based on the underlying OS globalization library. + /// + private unsafe bool InitCultureData() + { + Debug.Assert(_sRealName != null); + + Debug.Assert(!GlobalizationMode.Invariant); + + string alternateSortName = string.Empty; + string realNameBuffer = _sRealName; + + // Basic validation + if (realNameBuffer.Contains('@')) + { + return false; // don't allow ICU variants to come in directly + } + + // Replace _ (alternate sort) with @collation= for ICU + int index = realNameBuffer.IndexOf('_'); + if (index > 0) + { + if (index >= (realNameBuffer.Length - 1) // must have characters after _ + || realNameBuffer.Substring(index + 1).Contains('_')) // only one _ allowed + { + return false; // fail + } + alternateSortName = realNameBuffer.Substring(index + 1); + realNameBuffer = realNameBuffer.Substring(0, index) + ICU_COLLATION_KEYWORD + alternateSortName; + } + + // Get the locale name from ICU + if (!GetLocaleName(realNameBuffer, out _sWindowsName)) + { + return false; // fail + } + + // Replace the ICU collation keyword with an _ + index = _sWindowsName.IndexOf(ICU_COLLATION_KEYWORD, StringComparison.Ordinal); + if (index >= 0) + { + _sName = _sWindowsName.Substring(0, index) + "_" + alternateSortName; + } + else + { + _sName = _sWindowsName; + } + _sRealName = _sName; + + _iLanguage = this.ILANGUAGE; + if (_iLanguage == 0) + { + _iLanguage = CultureInfo.LOCALE_CUSTOM_UNSPECIFIED; + } + + _bNeutral = (this.SISO3166CTRYNAME.Length == 0); + + _sSpecificCulture = _bNeutral ? LocaleData.GetSpecificCultureName(_sRealName) : _sRealName; + + // Remove the sort from sName unless custom culture + if (index>0 && !_bNeutral && !IsCustomCultureId(_iLanguage)) + { + _sName = _sWindowsName.Substring(0, index); + } + return true; + } + + internal static bool GetLocaleName(string localeName, out string windowsName) + { + // Get the locale name from ICU + StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_FULLNAME_CAPACITY); + if (!Interop.GlobalizationInterop.GetLocaleName(localeName, sb, sb.Capacity)) + { + StringBuilderCache.Release(sb); + windowsName = null; + return false; // fail + } + + // Success - use the locale name returned which may be different than realNameBuffer (casing) + windowsName = StringBuilderCache.GetStringAndRelease(sb); // the name passed to subsequent ICU calls + return true; + } + + internal static bool GetDefaultLocaleName(out string windowsName) + { + // Get the default (system) locale name from ICU + StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_FULLNAME_CAPACITY); + if (!Interop.GlobalizationInterop.GetDefaultLocaleName(sb, sb.Capacity)) + { + StringBuilderCache.Release(sb); + windowsName = null; + return false; // fail + } + + // Success - use the locale name returned which may be different than realNameBuffer (casing) + windowsName = StringBuilderCache.GetStringAndRelease(sb); // the name passed to subsequent ICU calls + return true; + } + + private string GetLocaleInfo(LocaleStringData type) + { + Debug.Assert(!GlobalizationMode.Invariant); + + Debug.Assert(_sWindowsName != null, "[CultureData.GetLocaleInfo] Expected _sWindowsName to be populated already"); + return GetLocaleInfo(_sWindowsName, type); + } + + // For LOCALE_SPARENT we need the option of using the "real" name (forcing neutral names) instead of the + // "windows" name, which can be specific for downlevel (< windows 7) os's. + private string GetLocaleInfo(string localeName, LocaleStringData type) + { + Debug.Assert(localeName != null, "[CultureData.GetLocaleInfo] Expected localeName to be not be null"); + + switch (type) + { + case LocaleStringData.NegativeInfinitySymbol: + // not an equivalent in ICU; prefix the PositiveInfinitySymbol with NegativeSign + return GetLocaleInfo(localeName, LocaleStringData.NegativeSign) + + GetLocaleInfo(localeName, LocaleStringData.PositiveInfinitySymbol); + } + + StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY); + + bool result = Interop.GlobalizationInterop.GetLocaleInfoString(localeName, (uint)type, sb, sb.Capacity); + if (!result) + { + // Failed, just use empty string + StringBuilderCache.Release(sb); + Debug.Fail("[CultureData.GetLocaleInfo(LocaleStringData)] Failed"); + return String.Empty; + } + return StringBuilderCache.GetStringAndRelease(sb); + } + + private int GetLocaleInfo(LocaleNumberData type) + { + Debug.Assert(!GlobalizationMode.Invariant); + + Debug.Assert(_sWindowsName != null, "[CultureData.GetLocaleInfo(LocaleNumberData)] Expected _sWindowsName to be populated already"); + + switch (type) + { + case LocaleNumberData.CalendarType: + // returning 0 will cause the first supported calendar to be returned, which is the preferred calendar + return 0; + } + + + int value = 0; + bool result = Interop.GlobalizationInterop.GetLocaleInfoInt(_sWindowsName, (uint)type, ref value); + if (!result) + { + // Failed, just use 0 + Debug.Fail("[CultureData.GetLocaleInfo(LocaleNumberData)] failed"); + } + + return value; + } + + private int[] GetLocaleInfo(LocaleGroupingData type) + { + Debug.Assert(_sWindowsName != null, "[CultureData.GetLocaleInfo(LocaleGroupingData)] Expected _sWindowsName to be populated already"); + + int primaryGroupingSize = 0; + int secondaryGroupingSize = 0; + bool result = Interop.GlobalizationInterop.GetLocaleInfoGroupingSizes(_sWindowsName, (uint)type, ref primaryGroupingSize, ref secondaryGroupingSize); + if (!result) + { + Debug.Fail("[CultureData.GetLocaleInfo(LocaleGroupingData type)] failed"); + } + + if (secondaryGroupingSize == 0) + { + return new int[] { primaryGroupingSize }; + } + + return new int[] { primaryGroupingSize, secondaryGroupingSize }; + } + + private string GetTimeFormatString() + { + return GetTimeFormatString(false); + } + + private string GetTimeFormatString(bool shortFormat) + { + Debug.Assert(_sWindowsName != null, "[CultureData.GetTimeFormatString(bool shortFormat)] Expected _sWindowsName to be populated already"); + + StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY); + + bool result = Interop.GlobalizationInterop.GetLocaleTimeFormat(_sWindowsName, shortFormat, sb, sb.Capacity); + if (!result) + { + // Failed, just use empty string + StringBuilderCache.Release(sb); + Debug.Fail("[CultureData.GetTimeFormatString(bool shortFormat)] Failed"); + return String.Empty; + } + + return ConvertIcuTimeFormatString(StringBuilderCache.GetStringAndRelease(sb)); + } + + private int GetFirstDayOfWeek() + { + return this.GetLocaleInfo(LocaleNumberData.FirstDayOfWeek); + } + + private String[] GetTimeFormats() + { + string format = GetTimeFormatString(false); + return new string[] { format }; + } + + private String[] GetShortTimeFormats() + { + string format = GetTimeFormatString(true); + return new string[] { format }; + } + + private static CultureData GetCultureDataFromRegionName(String regionName) + { + // no support to lookup by region name, other than the hard-coded list in CultureData + return null; + } + + private static string GetLanguageDisplayName(string cultureName) + { + return new CultureInfo(cultureName)._cultureData.GetLocaleInfo(cultureName, LocaleStringData.LocalizedDisplayName); + } + + private static string GetRegionDisplayName(string isoCountryCode) + { + // use the fallback which is to return NativeName + return null; + } + + private static CultureInfo GetUserDefaultCulture() + { + return CultureInfo.GetUserDefaultCulture(); + } + + private static string ConvertIcuTimeFormatString(string icuFormatString) + { + StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_FULLNAME_CAPACITY); + bool amPmAdded = false; + + for (int i = 0; i < icuFormatString.Length; i++) + { + switch(icuFormatString[i]) + { + case ':': + case '.': + case 'H': + case 'h': + case 'm': + case 's': + sb.Append(icuFormatString[i]); + break; + + case ' ': + case '\u00A0': + // Convert nonbreaking spaces into regular spaces + sb.Append(' '); + break; + + case 'a': // AM/PM + if (!amPmAdded) + { + amPmAdded = true; + sb.Append("tt"); + } + break; + + } + } + + return StringBuilderCache.GetStringAndRelease(sb); + } + + private static string LCIDToLocaleName(int culture) + { + Debug.Assert(!GlobalizationMode.Invariant); + + return LocaleData.LCIDToLocaleName(culture); + } + + private static int LocaleNameToLCID(string cultureName) + { + Debug.Assert(!GlobalizationMode.Invariant); + + int lcid = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.Lcid); + return lcid == -1 ? CultureInfo.LOCALE_CUSTOM_UNSPECIFIED : lcid; + } + + private static int GetAnsiCodePage(string cultureName) + { + int ansiCodePage = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.AnsiCodePage); + return ansiCodePage == -1 ? CultureData.Invariant.IDEFAULTANSICODEPAGE : ansiCodePage; + } + + private static int GetOemCodePage(string cultureName) + { + int oemCodePage = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.OemCodePage); + return oemCodePage == -1 ? CultureData.Invariant.IDEFAULTOEMCODEPAGE : oemCodePage; + } + + private static int GetMacCodePage(string cultureName) + { + int macCodePage = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.MacCodePage); + return macCodePage == -1 ? CultureData.Invariant.IDEFAULTMACCODEPAGE : macCodePage; + } + + private static int GetEbcdicCodePage(string cultureName) + { + int ebcdicCodePage = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.EbcdicCodePage); + return ebcdicCodePage == -1 ? CultureData.Invariant.IDEFAULTEBCDICCODEPAGE : ebcdicCodePage; + } + + private static int GetGeoId(string cultureName) + { + int geoId = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.GeoId); + return geoId == -1 ? CultureData.Invariant.IGEOID : geoId; + } + + private static int GetDigitSubstitution(string cultureName) + { + int digitSubstitution = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.DigitSubstitution); + return digitSubstitution == -1 ? (int) DigitShapes.None : digitSubstitution; + } + + private static string GetThreeLetterWindowsLanguageName(string cultureName) + { + string langName = LocaleData.GetThreeLetterWindowsLangageName(cultureName); + return langName == null ? "ZZZ" /* default lang name */ : langName; + } + + private static CultureInfo[] EnumCultures(CultureTypes types) + { + Debug.Assert(!GlobalizationMode.Invariant); + + if ((types & (CultureTypes.NeutralCultures | CultureTypes.SpecificCultures)) == 0) + { + return Array.Empty(); + } + + int bufferLength = Interop.GlobalizationInterop.GetLocales(null, 0); + if (bufferLength <= 0) + { + return Array.Empty(); + } + + Char [] chars = new Char[bufferLength]; + + bufferLength = Interop.GlobalizationInterop.GetLocales(chars, bufferLength); + if (bufferLength <= 0) + { + return Array.Empty(); + } + + bool enumNeutrals = (types & CultureTypes.NeutralCultures) != 0; + bool enumSpecificss = (types & CultureTypes.SpecificCultures) != 0; + + List list = new List(); + if (enumNeutrals) + { + list.Add(CultureInfo.InvariantCulture); + } + + int index = 0; + while (index < bufferLength) + { + int length = (int) chars[index++]; + if (index + length <= bufferLength) + { + CultureInfo ci = CultureInfo.GetCultureInfo(new String(chars, index, length)); + if ((enumNeutrals && ci.IsNeutralCulture) || (enumSpecificss && !ci.IsNeutralCulture)) + { + list.Add(ci); + } + } + + index += length; + } + + return list.ToArray(); + } + + private static string GetConsoleFallbackName(string cultureName) + { + return LocaleData.GetConsoleUICulture(cultureName); + } + + internal bool IsFramework // not applicable on Linux based systems + { + get { return false; } + } + + internal bool IsWin32Installed // not applicable on Linux based systems + { + get { return false; } + } + + internal bool IsReplacementCulture // not applicable on Linux based systems + { + get { return false; } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/CultureData.Windows.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/CultureData.Windows.cs new file mode 100644 index 0000000000..393f983bba --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/CultureData.Windows.cs @@ -0,0 +1,774 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using Internal.Runtime.CompilerServices; + +#if ENABLE_WINRT +using Internal.Runtime.Augments; +#endif + +namespace System.Globalization +{ + internal partial class CultureData + { + private const uint LOCALE_NOUSEROVERRIDE = 0x80000000; + private const uint LOCALE_RETURN_NUMBER = 0x20000000; + private const uint LOCALE_SISO3166CTRYNAME = 0x0000005A; + + private const uint TIME_NOSECONDS = 0x00000002; + + /// + /// Check with the OS to see if this is a valid culture. + /// If so we populate a limited number of fields. If its not valid we return false. + /// + /// The fields we populate: + /// + /// sWindowsName -- The name that windows thinks this culture is, ie: + /// en-US if you pass in en-US + /// de-DE_phoneb if you pass in de-DE_phoneb + /// fj-FJ if you pass in fj (neutral, on a pre-Windows 7 machine) + /// fj if you pass in fj (neutral, post-Windows 7 machine) + /// + /// sRealName -- The name you used to construct the culture, in pretty form + /// en-US if you pass in EN-us + /// en if you pass in en + /// de-DE_phoneb if you pass in de-DE_phoneb + /// + /// sSpecificCulture -- The specific culture for this culture + /// en-US for en-US + /// en-US for en + /// de-DE_phoneb for alt sort + /// fj-FJ for fj (neutral) + /// + /// sName -- The IETF name of this culture (ie: no sort info, could be neutral) + /// en-US if you pass in en-US + /// en if you pass in en + /// de-DE if you pass in de-DE_phoneb + /// + /// bNeutral -- TRUE if it is a neutral locale + /// + /// For a neutral we just populate the neutral name, but we leave the windows name pointing to the + /// windows locale that's going to provide data for us. + /// + private unsafe bool InitCultureData() + { + Debug.Assert(!GlobalizationMode.Invariant); + + const uint LOCALE_ILANGUAGE = 0x00000001; + const uint LOCALE_INEUTRAL = 0x00000071; + const uint LOCALE_SNAME = 0x0000005c; + + int result; + string realNameBuffer = _sRealName; + char* pBuffer = stackalloc char[LOCALE_NAME_MAX_LENGTH]; + + result = GetLocaleInfoEx(realNameBuffer, LOCALE_SNAME, pBuffer, LOCALE_NAME_MAX_LENGTH); + + // Did it fail? + if (result == 0) + { + return false; + } + + // It worked, note that the name is the locale name, so use that (even for neutrals) + // We need to clean up our "real" name, which should look like the windows name right now + // so overwrite the input with the cleaned up name + _sRealName = new string(pBuffer, 0, result - 1); + realNameBuffer = _sRealName; + + // Check for neutrality, don't expect to fail + // (buffer has our name in it, so we don't have to do the gc. stuff) + + result = GetLocaleInfoEx(realNameBuffer, LOCALE_INEUTRAL | LOCALE_RETURN_NUMBER, pBuffer, sizeof(int) / sizeof(char)); + if (result == 0) + { + return false; + } + + // Remember our neutrality + _bNeutral = *((uint*)pBuffer) != 0; + + // Note: Parents will be set dynamically + + // Start by assuming the windows name will be the same as the specific name since windows knows + // about specifics on all versions. Only for downlevel Neutral locales does this have to change. + _sWindowsName = realNameBuffer; + + // Neutrals and non-neutrals are slightly different + if (_bNeutral) + { + // Neutral Locale + + // IETF name looks like neutral name + _sName = realNameBuffer; + + // Specific locale name is whatever ResolveLocaleName (win7+) returns. + // (Buffer has our name in it, and we can recycle that because windows resolves it before writing to the buffer) + result = Interop.Kernel32.ResolveLocaleName(realNameBuffer, pBuffer, LOCALE_NAME_MAX_LENGTH); + + // 0 is failure, 1 is invariant (""), which we expect + if (result < 1) + { + return false; + } + // We found a locale name, so use it. + // In vista this should look like a sort name (de-DE_phoneb) or a specific culture (en-US) and be in the "pretty" form + _sSpecificCulture = new string(pBuffer, 0, result - 1); + } + else + { + // Specific Locale + + // Specific culture's the same as the locale name since we know its not neutral + // On mac we'll use this as well, even for neutrals. There's no obvious specific + // culture to use and this isn't exposed, but behaviorally this is correct on mac. + // Note that specifics include the sort name (de-DE_phoneb) + _sSpecificCulture = realNameBuffer; + + _sName = realNameBuffer; + + // We need the IETF name (sname) + // If we aren't an alt sort locale then this is the same as the windows name. + // If we are an alt sort locale then this is the same as the part before the _ in the windows name + // This is for like de-DE_phoneb and es-ES_tradnl that hsouldn't have the _ part + + result = GetLocaleInfoEx(realNameBuffer, LOCALE_ILANGUAGE | LOCALE_RETURN_NUMBER, pBuffer, sizeof(int) / sizeof(char)); + if (result == 0) + { + return false; + } + + _iLanguage = *((int*)pBuffer); + + if (!IsCustomCultureId(_iLanguage)) + { + // not custom locale + int index = realNameBuffer.IndexOf('_'); + if (index > 0 && index < realNameBuffer.Length) + { + _sName = realNameBuffer.Substring(0, index); + } + } + } + + // It succeeded. + return true; + } + + // Wrappers around the GetLocaleInfoEx APIs which handle marshalling the returned + // data as either and Int or string. + internal static unsafe string GetLocaleInfoEx(string localeName, uint field) + { + // REVIEW: Determine the maximum size for the buffer + const int BUFFER_SIZE = 530; + + char* pBuffer = stackalloc char[BUFFER_SIZE]; + int resultCode = GetLocaleInfoEx(localeName, field, pBuffer, BUFFER_SIZE); + if (resultCode > 0) + { + return new string(pBuffer); + } + + return null; + } + + internal static unsafe int GetLocaleInfoExInt(string localeName, uint field) + { + const uint LOCALE_RETURN_NUMBER = 0x20000000; + field |= LOCALE_RETURN_NUMBER; + int value = 0; + GetLocaleInfoEx(localeName, field, (char*) &value, sizeof(int)); + return value; + } + + internal static unsafe int GetLocaleInfoEx(string lpLocaleName, uint lcType, char* lpLCData, int cchData) + { + Debug.Assert(!GlobalizationMode.Invariant); + + return Interop.Kernel32.GetLocaleInfoEx(lpLocaleName, lcType, lpLCData, cchData); + } + + private string GetLocaleInfo(LocaleStringData type) + { + Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfo] Expected _sWindowsName to be populated by already"); + return GetLocaleInfo(_sWindowsName, type); + } + + // For LOCALE_SPARENT we need the option of using the "real" name (forcing neutral names) instead of the + // "windows" name, which can be specific for downlevel (< windows 7) os's. + private string GetLocaleInfo(string localeName, LocaleStringData type) + { + uint lctype = (uint)type; + + return GetLocaleInfoFromLCType(localeName, lctype, UseUserOverride); + } + + private int GetLocaleInfo(LocaleNumberData type) + { + uint lctype = (uint)type; + + // Fix lctype if we don't want overrides + if (!UseUserOverride) + { + lctype |= LOCALE_NOUSEROVERRIDE; + } + + // Ask OS for data, note that we presume it returns success, so we have to know that + // sWindowsName is valid before calling. + Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected _sWindowsName to be populated by already"); + return GetLocaleInfoExInt(_sWindowsName, lctype); + } + + private int[] GetLocaleInfo(LocaleGroupingData type) + { + return ConvertWin32GroupString(GetLocaleInfoFromLCType(_sWindowsName, (uint)type, UseUserOverride)); + } + + private string GetTimeFormatString() + { + const uint LOCALE_STIMEFORMAT = 0x00001003; + + return ReescapeWin32String(GetLocaleInfoFromLCType(_sWindowsName, LOCALE_STIMEFORMAT, UseUserOverride)); + } + + private int GetFirstDayOfWeek() + { + Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected _sWindowsName to be populated by already"); + + const uint LOCALE_IFIRSTDAYOFWEEK = 0x0000100C; + + int result = GetLocaleInfoExInt(_sWindowsName, LOCALE_IFIRSTDAYOFWEEK | (!UseUserOverride ? LOCALE_NOUSEROVERRIDE : 0)); + + // Win32 and .NET disagree on the numbering for days of the week, so we have to convert. + return ConvertFirstDayOfWeekMonToSun(result); + } + + private string[] GetTimeFormats() + { + // Note that this gets overrides for us all the time + Debug.Assert(_sWindowsName != null, "[CultureData.DoEnumTimeFormats] Expected _sWindowsName to be populated by already"); + string[] result = ReescapeWin32Strings(nativeEnumTimeFormats(_sWindowsName, 0, UseUserOverride)); + + return result; + } + + private string[] GetShortTimeFormats() + { + // Note that this gets overrides for us all the time + Debug.Assert(_sWindowsName != null, "[CultureData.DoEnumShortTimeFormats] Expected _sWindowsName to be populated by already"); + string[] result = ReescapeWin32Strings(nativeEnumTimeFormats(_sWindowsName, TIME_NOSECONDS, UseUserOverride)); + + return result; + } + + // Enumerate all system cultures and then try to find out which culture has + // region name match the requested region name + private static CultureData GetCultureDataFromRegionName(string regionName) + { + Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(regionName != null); + + const uint LOCALE_SUPPLEMENTAL = 0x00000002; + const uint LOCALE_SPECIFICDATA = 0x00000020; + + EnumLocaleData context = new EnumLocaleData(); + context.cultureName = null; + context.regionName = regionName; + + unsafe + { + Interop.Kernel32.EnumSystemLocalesEx(EnumSystemLocalesProc, LOCALE_SPECIFICDATA | LOCALE_SUPPLEMENTAL, Unsafe.AsPointer(ref context), IntPtr.Zero); + } + + if (context.cultureName != null) + { + // we got a matched culture + return GetCultureData(context.cultureName, true); + } + + return null; + } + + private string GetLanguageDisplayName(string cultureName) + { +#if ENABLE_WINRT + return WinRTInterop.Callbacks.GetLanguageDisplayName(cultureName); +#else + // Usually the UI culture shouldn't be different than what we got from WinRT except + // if DefaultThreadCurrentUICulture was set + CultureInfo ci; + + if (CultureInfo.DefaultThreadCurrentUICulture != null && + ((ci = GetUserDefaultCulture()) != null) && + !CultureInfo.DefaultThreadCurrentUICulture.Name.Equals(ci.Name)) + { + return SNATIVEDISPLAYNAME; + } + else + { + return GetLocaleInfo(cultureName, LocaleStringData.LocalizedDisplayName); + } +#endif // ENABLE_WINRT + } + + private string GetRegionDisplayName(string isoCountryCode) + { +#if ENABLE_WINRT + return WinRTInterop.Callbacks.GetRegionDisplayName(isoCountryCode); +#else + // If the current UI culture matching the OS UI language, we'll get the display name from the OS. + // otherwise, we use the native name as we don't carry resources for the region display names anyway. + if (CultureInfo.CurrentUICulture.Name.Equals(CultureInfo.UserDefaultUICulture.Name)) + { + return GetLocaleInfo(LocaleStringData.LocalizedCountryName); + } + + return SNATIVECOUNTRY; +#endif // ENABLE_WINRT + } + + private static CultureInfo GetUserDefaultCulture() + { +#if ENABLE_WINRT + return (CultureInfo)WinRTInterop.Callbacks.GetUserDefaultCulture(); +#else + return CultureInfo.GetUserDefaultCulture(); +#endif // ENABLE_WINRT + } + + // PAL methods end here. + + private static string GetLocaleInfoFromLCType(string localeName, uint lctype, bool useUserOveride) + { + Debug.Assert(localeName != null, "[CultureData.GetLocaleInfoFromLCType] Expected localeName to be not be null"); + + // Fix lctype if we don't want overrides + if (!useUserOveride) + { + lctype |= LOCALE_NOUSEROVERRIDE; + } + + // Ask OS for data + string result = GetLocaleInfoEx(localeName, lctype); + if (result == null) + { + // Failed, just use empty string + result = string.Empty; + } + + return result; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Reescape a Win32 style quote string as a NLS+ style quoted string + // + // This is also the escaping style used by custom culture data files + // + // NLS+ uses \ to escape the next character, whether in a quoted string or + // not, so we always have to change \ to \\. + // + // NLS+ uses \' to escape a quote inside a quoted string so we have to change + // '' to \' (if inside a quoted string) + // + // We don't build the stringbuilder unless we find something to change + //////////////////////////////////////////////////////////////////////////// + internal static string ReescapeWin32String(string str) + { + // If we don't have data, then don't try anything + if (str == null) + return null; + + StringBuilder result = null; + + bool inQuote = false; + for (int i = 0; i < str.Length; i++) + { + // Look for quote + if (str[i] == '\'') + { + // Already in quote? + if (inQuote) + { + // See another single quote. Is this '' of 'fred''s' or '''', or is it an ending quote? + if (i + 1 < str.Length && str[i + 1] == '\'') + { + // Found another ', so we have ''. Need to add \' instead. + // 1st make sure we have our stringbuilder + if (result == null) + result = new StringBuilder(str, 0, i, str.Length * 2); + + // Append a \' and keep going (so we don't turn off quote mode) + result.Append("\\'"); + i++; + continue; + } + + // Turning off quote mode, fall through to add it + inQuote = false; + } + else + { + // Found beginning quote, fall through to add it + inQuote = true; + } + } + // Is there a single \ character? + else if (str[i] == '\\') + { + // Found a \, need to change it to \\ + // 1st make sure we have our stringbuilder + if (result == null) + result = new StringBuilder(str, 0, i, str.Length * 2); + + // Append our \\ to the string & continue + result.Append("\\\\"); + continue; + } + + // If we have a builder we need to add our character + if (result != null) + result.Append(str[i]); + } + + // Unchanged string? , just return input string + if (result == null) + return str; + + // String changed, need to use the builder + return result.ToString(); + } + + internal static string[] ReescapeWin32Strings(string[] array) + { + if (array != null) + { + for (int i = 0; i < array.Length; i++) + { + array[i] = ReescapeWin32String(array[i]); + } + } + + return array; + } + + // If we get a group from windows, then its in 3;0 format with the 0 backwards + // of how NLS+ uses it (ie: if the string has a 0, then the int[] shouldn't and vice versa) + // EXCEPT in the case where the list only contains 0 in which NLS and NLS+ have the same meaning. + private static int[] ConvertWin32GroupString(string win32Str) + { + // None of these cases make any sense + if (win32Str == null || win32Str.Length == 0) + { + return (new int[] { 3 }); + } + + if (win32Str[0] == '0') + { + return (new int[] { 0 }); + } + + // Since its in n;n;n;n;n format, we can always get the length quickly + int[] values; + if (win32Str[win32Str.Length - 1] == '0') + { + // Trailing 0 gets dropped. 1;0 -> 1 + values = new int[(win32Str.Length / 2)]; + } + else + { + // Need extra space for trailing zero 1 -> 1;0 + values = new int[(win32Str.Length / 2) + 2]; + values[values.Length - 1] = 0; + } + + int i; + int j; + for (i = 0, j = 0; i < win32Str.Length && j < values.Length; i += 2, j++) + { + // Note that this # shouldn't ever be zero, 'cause 0 is only at end + // But we'll test because its registry that could be anything + if (win32Str[i] < '1' || win32Str[i] > '9') + return new int[] { 3 }; + + values[j] = (int)(win32Str[i] - '0'); + } + + return (values); + } + + private static int ConvertFirstDayOfWeekMonToSun(int iTemp) + { + // Convert Mon-Sun to Sun-Sat format + iTemp++; + if (iTemp > 6) + { + // Wrap Sunday and convert invalid data to Sunday + iTemp = 0; + } + return iTemp; + } + + + // Context for EnumCalendarInfoExEx callback. + private class EnumLocaleData + { + public string regionName; + public string cultureName; + } + + // EnumSystemLocaleEx callback. + // [NativeCallable(CallingConvention = CallingConvention.StdCall)] + private static unsafe Interop.BOOL EnumSystemLocalesProc(char* lpLocaleString, uint flags, void* contextHandle) + { + ref EnumLocaleData context = ref Unsafe.As(ref *(byte*)contextHandle); + try + { + string cultureName = new string(lpLocaleString); + string regionName = GetLocaleInfoEx(cultureName, LOCALE_SISO3166CTRYNAME); + if (regionName != null && regionName.Equals(context.regionName, StringComparison.OrdinalIgnoreCase)) + { + context.cultureName = cultureName; + return Interop.BOOL.FALSE; // we found a match, then stop the enumeration + } + + return Interop.BOOL.TRUE; + } + catch (Exception) + { + return Interop.BOOL.FALSE; + } + } + + // EnumSystemLocaleEx callback. + // [NativeCallable(CallingConvention = CallingConvention.StdCall)] + private static unsafe Interop.BOOL EnumAllSystemLocalesProc(char* lpLocaleString, uint flags, void* contextHandle) + { + ref EnumData context = ref Unsafe.As(ref *(byte*)contextHandle); + try + { + context.strings.Add(new string(lpLocaleString)); + return Interop.BOOL.TRUE; + } + catch (Exception) + { + return Interop.BOOL.FALSE; + } + } + + // Context for EnumTimeFormatsEx callback. + private struct EnumData + { + public List strings; + } + + // EnumTimeFormatsEx callback itself. + // [NativeCallable(CallingConvention = CallingConvention.StdCall)] + private static unsafe Interop.BOOL EnumTimeCallback(char* lpTimeFormatString, void* lParam) + { + ref EnumData context = ref Unsafe.As(ref *(byte*)lParam); + try + { + context.strings.Add(new string(lpTimeFormatString)); + return Interop.BOOL.TRUE; + } + catch (Exception) + { + return Interop.BOOL.FALSE; + } + } + + private static unsafe string[] nativeEnumTimeFormats(string localeName, uint dwFlags, bool useUserOverride) + { + const uint LOCALE_SSHORTTIME = 0x00000079; + const uint LOCALE_STIMEFORMAT = 0x00001003; + + EnumData data = new EnumData(); + data.strings = new List(); + + // Now call the enumeration API. Work is done by our callback function + Interop.Kernel32.EnumTimeFormatsEx(EnumTimeCallback, localeName, (uint)dwFlags, Unsafe.AsPointer(ref data)); + + if (data.strings.Count > 0) + { + // Now we need to allocate our stringarray and populate it + string[] results = data.strings.ToArray(); + + if (!useUserOverride && data.strings.Count > 1) + { + // Since there is no "NoUserOverride" aware EnumTimeFormatsEx, we always get an override + // The override is the first entry if it is overriden. + // We can check if we have overrides by checking the GetLocaleInfo with no override + // If we do have an override, we don't know if it is a user defined override or if the + // user has just selected one of the predefined formats so we can't just remove it + // but we can move it down. + uint lcType = (dwFlags == TIME_NOSECONDS) ? LOCALE_SSHORTTIME : LOCALE_STIMEFORMAT; + string timeFormatNoUserOverride = GetLocaleInfoFromLCType(localeName, lcType, useUserOverride); + if (timeFormatNoUserOverride != "") + { + string firstTimeFormat = results[0]; + if (timeFormatNoUserOverride != firstTimeFormat) + { + results[0] = results[1]; + results[1] = firstTimeFormat; + } + } + } + + return results; + } + + return null; + } + + private static int LocaleNameToLCID(string cultureName) + { + Debug.Assert(!GlobalizationMode.Invariant); + + return Interop.Kernel32.LocaleNameToLCID(cultureName, Interop.Kernel32.LOCALE_ALLOW_NEUTRAL_NAMES); + } + + private static unsafe string LCIDToLocaleName(int culture) + { + Debug.Assert(!GlobalizationMode.Invariant); + + char *pBuffer = stackalloc char[Interop.Kernel32.LOCALE_NAME_MAX_LENGTH + 1]; // +1 for the null termination + int length = Interop.Kernel32.LCIDToLocaleName(culture, pBuffer, Interop.Kernel32.LOCALE_NAME_MAX_LENGTH + 1, Interop.Kernel32.LOCALE_ALLOW_NEUTRAL_NAMES); + + if (length > 0) + { + return new string(pBuffer); + } + + return null; + } + + private int GetAnsiCodePage(string cultureName) + { + return GetLocaleInfo(LocaleNumberData.AnsiCodePage); + } + + private int GetOemCodePage(string cultureName) + { + return GetLocaleInfo(LocaleNumberData.OemCodePage); + } + + private int GetMacCodePage(string cultureName) + { + return GetLocaleInfo(LocaleNumberData.MacCodePage); + } + + private int GetEbcdicCodePage(string cultureName) + { + return GetLocaleInfo(LocaleNumberData.EbcdicCodePage); + } + + private int GetGeoId(string cultureName) + { + return GetLocaleInfo(LocaleNumberData.GeoId); + } + + private int GetDigitSubstitution(string cultureName) + { + return GetLocaleInfo(LocaleNumberData.DigitSubstitution); + } + + private string GetThreeLetterWindowsLanguageName(string cultureName) + { + return GetLocaleInfo(cultureName, LocaleStringData.AbbreviatedWindowsLanguageName); + } + + private static CultureInfo[] EnumCultures(CultureTypes types) + { + Debug.Assert(!GlobalizationMode.Invariant); + + uint flags = 0; + +#pragma warning disable 618 + if ((types & (CultureTypes.FrameworkCultures | CultureTypes.InstalledWin32Cultures | CultureTypes.ReplacementCultures)) != 0) + { + flags |= Interop.Kernel32.LOCALE_NEUTRALDATA | Interop.Kernel32.LOCALE_SPECIFICDATA; + } +#pragma warning restore 618 + + if ((types & CultureTypes.NeutralCultures) != 0) + { + flags |= Interop.Kernel32.LOCALE_NEUTRALDATA; + } + + if ((types & CultureTypes.SpecificCultures) != 0) + { + flags |= Interop.Kernel32.LOCALE_SPECIFICDATA; + } + + if ((types & CultureTypes.UserCustomCulture) != 0) + { + flags |= Interop.Kernel32.LOCALE_SUPPLEMENTAL; + } + + if ((types & CultureTypes.ReplacementCultures) != 0) + { + flags |= Interop.Kernel32.LOCALE_SUPPLEMENTAL; + } + + EnumData context = new EnumData(); + context.strings = new List(); + + unsafe + { + Interop.Kernel32.EnumSystemLocalesEx(EnumAllSystemLocalesProc, flags, Unsafe.AsPointer(ref context), IntPtr.Zero); + } + + CultureInfo [] cultures = new CultureInfo[context.strings.Count]; + for (int i = 0; i < cultures.Length; i++) + { + cultures[i] = new CultureInfo(context.strings[i]); + } + + return cultures; + } + + private string GetConsoleFallbackName(string cultureName) + { + return GetLocaleInfo(cultureName, LocaleStringData.ConsoleFallbackName); + } + + internal bool IsFramework + { + get { return false; } + } + + internal bool IsWin32Installed + { + get { return true; } + } + + internal bool IsReplacementCulture + { + get + { + EnumData context = new EnumData(); + context.strings = new List(); + + unsafe + { + Interop.Kernel32.EnumSystemLocalesEx(EnumAllSystemLocalesProc, Interop.Kernel32.LOCALE_REPLACEMENT, Unsafe.AsPointer(ref context), IntPtr.Zero); + } + + for (int i=0; i InvalidCultureId + { + get { return _invalidCultureId; } + } + + public virtual string InvalidCultureName + { + get { return _invalidCultureName; } + } + + private static String DefaultMessage + { + get + { + return SR.Argument_CultureNotSupported; + } + } + + private String FormatedInvalidCultureId + { + get + { + return InvalidCultureId != null ? + String.Format(CultureInfo.InvariantCulture, "{0} (0x{0:x4})", (int)InvalidCultureId) : + InvalidCultureName; + } + } + + public override String Message + { + get + { + String s = base.Message; + if (_invalidCultureId != null || _invalidCultureName != null) + { + String valueMessage = SR.Format(SR.Argument_CultureInvalidIdentifier, FormatedInvalidCultureId); + if (s == null) + { + return valueMessage; + } + + return s + Environment.NewLine + valueMessage; + } + return s; + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/CultureTypes.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/CultureTypes.cs new file mode 100644 index 0000000000..f52ac82a83 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/CultureTypes.cs @@ -0,0 +1,27 @@ +// 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. + +// The enumeration constants used in CultureInfo.GetCultures(). +// On Linux platforms, the only enum values used there is NeutralCultures and SpecificCultures +// the rest are obsolete or not valid on Linux + +namespace System.Globalization +{ + [Flags] + public enum CultureTypes + { + NeutralCultures = 0x0001, // Neutral cultures are cultures like "en", "de", "zh", etc, for enumeration this includes ALL neutrals regardless of other flags + SpecificCultures = 0x0002, // Non-netural cultuers. Examples are "en-us", "zh-tw", etc., for enumeration this includes ALL specifics regardless of other flags + InstalledWin32Cultures = 0x0004, // Win32 installed cultures in the system and exists in the framework too., this is effectively all cultures + + AllCultures = NeutralCultures | SpecificCultures | InstalledWin32Cultures, + + UserCustomCulture = 0x0008, // User defined custom culture + ReplacementCultures = 0x0010, // User defined replacement custom culture. + [Obsolete("This value has been deprecated. Please use other values in CultureTypes.")] + WindowsOnlyCultures = 0x0020, // this will always return empty list. + [Obsolete("This value has been deprecated. Please use other values in CultureTypes.")] + FrameworkCultures = 0x0040, // will return only the v2 cultures marked as Framework culture. + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/DateTimeFormat.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/DateTimeFormat.cs new file mode 100644 index 0000000000..d15cc1cc8c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/DateTimeFormat.cs @@ -0,0 +1,1236 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Text; + +namespace System +{ + /* + Customized format patterns: + P.S. Format in the table below is the internal number format used to display the pattern. + + Patterns Format Description Example + ========= ========== ===================================== ======== + "h" "0" hour (12-hour clock)w/o leading zero 3 + "hh" "00" hour (12-hour clock)with leading zero 03 + "hh*" "00" hour (12-hour clock)with leading zero 03 + + "H" "0" hour (24-hour clock)w/o leading zero 8 + "HH" "00" hour (24-hour clock)with leading zero 08 + "HH*" "00" hour (24-hour clock) 08 + + "m" "0" minute w/o leading zero + "mm" "00" minute with leading zero + "mm*" "00" minute with leading zero + + "s" "0" second w/o leading zero + "ss" "00" second with leading zero + "ss*" "00" second with leading zero + + "f" "0" second fraction (1 digit) + "ff" "00" second fraction (2 digit) + "fff" "000" second fraction (3 digit) + "ffff" "0000" second fraction (4 digit) + "fffff" "00000" second fraction (5 digit) + "ffffff" "000000" second fraction (6 digit) + "fffffff" "0000000" second fraction (7 digit) + + "F" "0" second fraction (up to 1 digit) + "FF" "00" second fraction (up to 2 digit) + "FFF" "000" second fraction (up to 3 digit) + "FFFF" "0000" second fraction (up to 4 digit) + "FFFFF" "00000" second fraction (up to 5 digit) + "FFFFFF" "000000" second fraction (up to 6 digit) + "FFFFFFF" "0000000" second fraction (up to 7 digit) + + "t" first character of AM/PM designator A + "tt" AM/PM designator AM + "tt*" AM/PM designator PM + + "d" "0" day w/o leading zero 1 + "dd" "00" day with leading zero 01 + "ddd" short weekday name (abbreviation) Mon + "dddd" full weekday name Monday + "dddd*" full weekday name Monday + + + "M" "0" month w/o leading zero 2 + "MM" "00" month with leading zero 02 + "MMM" short month name (abbreviation) Feb + "MMMM" full month name Febuary + "MMMM*" full month name Febuary + + "y" "0" two digit year (year % 100) w/o leading zero 0 + "yy" "00" two digit year (year % 100) with leading zero 00 + "yyy" "D3" year 2000 + "yyyy" "D4" year 2000 + "yyyyy" "D5" year 2000 + ... + + "z" "+0;-0" timezone offset w/o leading zero -8 + "zz" "+00;-00" timezone offset with leading zero -08 + "zzz" "+00;-00" for hour offset, "00" for minute offset full timezone offset -07:30 + "zzz*" "+00;-00" for hour offset, "00" for minute offset full timezone offset -08:00 + + "K" -Local "zzz", e.g. -08:00 + -Utc "'Z'", representing UTC + -Unspecified "" + -DateTimeOffset "zzzzz" e.g -07:30:15 + + "g*" the current era name A.D. + + ":" time separator : -- DEPRECATED - Insert separator directly into pattern (eg: "H.mm.ss") + "/" date separator /-- DEPRECATED - Insert separator directly into pattern (eg: "M-dd-yyyy") + "'" quoted string 'ABC' will insert ABC into the formatted string. + '"' quoted string "ABC" will insert ABC into the formatted string. + "%" used to quote a single pattern characters E.g.The format character "%y" is to print two digit year. + "\" escaped character E.g. '\d' insert the character 'd' into the format string. + other characters insert the character into the format string. + + Pre-defined format characters: + (U) to indicate Universal time is used. + (G) to indicate Gregorian calendar is used. + + Format Description Real format Example + ========= ================================= ====================== ======================= + "d" short date culture-specific 10/31/1999 + "D" long data culture-specific Sunday, October 31, 1999 + "f" full date (long date + short time) culture-specific Sunday, October 31, 1999 2:00 AM + "F" full date (long date + long time) culture-specific Sunday, October 31, 1999 2:00:00 AM + "g" general date (short date + short time) culture-specific 10/31/1999 2:00 AM + "G" general date (short date + long time) culture-specific 10/31/1999 2:00:00 AM + "m"/"M" Month/Day date culture-specific October 31 +(G) "o"/"O" Round Trip XML "yyyy-MM-ddTHH:mm:ss.fffffffK" 1999-10-31 02:00:00.0000000Z +(G) "r"/"R" RFC 1123 date, "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'" Sun, 31 Oct 1999 10:00:00 GMT +(G) "s" Sortable format, based on ISO 8601. "yyyy-MM-dd'T'HH:mm:ss" 1999-10-31T02:00:00 + ('T' for local time) + "t" short time culture-specific 2:00 AM + "T" long time culture-specific 2:00:00 AM +(G) "u" Universal time with sortable format, "yyyy'-'MM'-'dd HH':'mm':'ss'Z'" 1999-10-31 10:00:00Z + based on ISO 8601. +(U) "U" Universal time with full culture-specific Sunday, October 31, 1999 10:00:00 AM + (long date + long time) format + "y"/"Y" Year/Month day culture-specific October, 1999 + + */ + + //This class contains only static members and does not require the serializable attribute. + internal static + class DateTimeFormat + { + internal const int MaxSecondsFractionDigits = 7; + internal static readonly TimeSpan NullOffset = TimeSpan.MinValue; + + internal static char[] allStandardFormats = + { + 'd', 'D', 'f', 'F', 'g', 'G', + 'm', 'M', 'o', 'O', 'r', 'R', + 's', 't', 'T', 'u', 'U', 'y', 'Y', + }; + + internal const String RoundtripFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK"; + internal const String RoundtripDateTimeUnfixed = "yyyy'-'MM'-'ddTHH':'mm':'ss zzz"; + + private const int DEFAULT_ALL_DATETIMES_SIZE = 132; + + internal static readonly DateTimeFormatInfo InvariantFormatInfo = CultureInfo.InvariantCulture.DateTimeFormat; + internal static readonly string[] InvariantAbbreviatedMonthNames = InvariantFormatInfo.AbbreviatedMonthNames; + internal static readonly string[] InvariantAbbreviatedDayNames = InvariantFormatInfo.AbbreviatedDayNames; + internal const string Gmt = "GMT"; + + internal static String[] fixedNumberFormats = new String[] { + "0", + "00", + "000", + "0000", + "00000", + "000000", + "0000000", + }; + + //////////////////////////////////////////////////////////////////////////// + // + // Format the positive integer value to a string and perfix with assigned + // length of leading zero. + // + // Parameters: + // value: The value to format + // len: The maximum length for leading zero. + // If the digits of the value is greater than len, no leading zero is added. + // + // Notes: + // The function can format to Int32.MaxValue. + // + //////////////////////////////////////////////////////////////////////////// + internal static void FormatDigits(StringBuilder outputBuffer, int value, int len) + { + Debug.Assert(value >= 0, "DateTimeFormat.FormatDigits(): value >= 0"); + FormatDigits(outputBuffer, value, len, false); + } + + internal unsafe static void FormatDigits(StringBuilder outputBuffer, int value, int len, bool overrideLengthLimit) + { + Debug.Assert(value >= 0, "DateTimeFormat.FormatDigits(): value >= 0"); + + // Limit the use of this function to be two-digits, so that we have the same behavior + // as RTM bits. + if (!overrideLengthLimit && len > 2) + { + len = 2; + } + + char* buffer = stackalloc char[16]; + char* p = buffer + 16; + int n = value; + do + { + *--p = (char)(n % 10 + '0'); + n /= 10; + } while ((n != 0) && (p > buffer)); + + int digits = (int)(buffer + 16 - p); + + //If the repeat count is greater than 0, we're trying + //to emulate the "00" format, so we have to prepend + //a zero if the string only has one character. + while ((digits < len) && (p > buffer)) + { + *--p = '0'; + digits++; + } + outputBuffer.Append(p, digits); + } + + private static void HebrewFormatDigits(StringBuilder outputBuffer, int digits) + { + outputBuffer.Append(HebrewNumber.ToString(digits)); + } + + internal static int ParseRepeatPattern(ReadOnlySpan format, int pos, char patternChar) + { + int len = format.Length; + int index = pos + 1; + while ((index < len) && (format[index] == patternChar)) + { + index++; + } + return (index - pos); + } + + private static String FormatDayOfWeek(int dayOfWeek, int repeat, DateTimeFormatInfo dtfi) + { + Debug.Assert(dayOfWeek >= 0 && dayOfWeek <= 6, "dayOfWeek >= 0 && dayOfWeek <= 6"); + if (repeat == 3) + { + return (dtfi.GetAbbreviatedDayName((DayOfWeek)dayOfWeek)); + } + // Call dtfi.GetDayName() here, instead of accessing DayNames property, because we don't + // want a clone of DayNames, which will hurt perf. + return (dtfi.GetDayName((DayOfWeek)dayOfWeek)); + } + + private static String FormatMonth(int month, int repeatCount, DateTimeFormatInfo dtfi) + { + Debug.Assert(month >= 1 && month <= 12, "month >=1 && month <= 12"); + if (repeatCount == 3) + { + return (dtfi.GetAbbreviatedMonthName(month)); + } + // Call GetMonthName() here, instead of accessing MonthNames property, because we don't + // want a clone of MonthNames, which will hurt perf. + return (dtfi.GetMonthName(month)); + } + + // + // FormatHebrewMonthName + // + // Action: Return the Hebrew month name for the specified DateTime. + // Returns: The month name string for the specified DateTime. + // Arguments: + // time the time to format + // month The month is the value of HebrewCalendar.GetMonth(time). + // repeat Return abbreviated month name if repeat=3, or full month name if repeat=4 + // dtfi The DateTimeFormatInfo which uses the Hebrew calendars as its calendar. + // Exceptions: None. + // + + /* Note: + If DTFI is using Hebrew calendar, GetMonthName()/GetAbbreviatedMonthName() will return month names like this: + 1 Hebrew 1st Month + 2 Hebrew 2nd Month + .. ... + 6 Hebrew 6th Month + 7 Hebrew 6th Month II (used only in a leap year) + 8 Hebrew 7th Month + 9 Hebrew 8th Month + 10 Hebrew 9th Month + 11 Hebrew 10th Month + 12 Hebrew 11th Month + 13 Hebrew 12th Month + + Therefore, if we are in a regular year, we have to increment the month name if moth is greater or eqaul to 7. + */ + private static String FormatHebrewMonthName(DateTime time, int month, int repeatCount, DateTimeFormatInfo dtfi) + { + Debug.Assert(repeatCount != 3 || repeatCount != 4, "repeateCount should be 3 or 4"); + if (dtfi.Calendar.IsLeapYear(dtfi.Calendar.GetYear(time))) + { + // This month is in a leap year + return (dtfi.internalGetMonthName(month, MonthNameStyles.LeapYear, (repeatCount == 3))); + } + // This is in a regular year. + if (month >= 7) + { + month++; + } + if (repeatCount == 3) + { + return (dtfi.GetAbbreviatedMonthName(month)); + } + return (dtfi.GetMonthName(month)); + } + + // + // The pos should point to a quote character. This method will + // append to the result StringBuilder the string encloed by the quote character. + // + internal static int ParseQuoteString(ReadOnlySpan format, int pos, StringBuilder result) + { + // + // NOTE : pos will be the index of the quote character in the 'format' string. + // + int formatLen = format.Length; + int beginPos = pos; + char quoteChar = format[pos++]; // Get the character used to quote the following string. + + bool foundQuote = false; + while (pos < formatLen) + { + char ch = format[pos++]; + if (ch == quoteChar) + { + foundQuote = true; + break; + } + else if (ch == '\\') + { + // The following are used to support escaped character. + // Escaped character is also supported in the quoted string. + // Therefore, someone can use a format like "'minute:' mm\"" to display: + // minute: 45" + // because the second double quote is escaped. + if (pos < formatLen) + { + result.Append(format[pos++]); + } + else + { + // + // This means that '\' is at the end of the formatting string. + // + throw new FormatException(SR.Format_InvalidString); + } + } + else + { + result.Append(ch); + } + } + + if (!foundQuote) + { + // Here we can't find the matching quote. + throw new FormatException( + String.Format( + CultureInfo.CurrentCulture, + SR.Format_BadQuote, quoteChar)); + } + + // + // Return the character count including the begin/end quote characters and enclosed string. + // + return (pos - beginPos); + } + + // + // Get the next character at the index of 'pos' in the 'format' string. + // Return value of -1 means 'pos' is already at the end of the 'format' string. + // Otherwise, return value is the int value of the next character. + // + internal static int ParseNextChar(ReadOnlySpan format, int pos) + { + if (pos >= format.Length - 1) + { + return (-1); + } + return ((int)format[pos + 1]); + } + + // + // IsUseGenitiveForm + // + // Actions: Check the format to see if we should use genitive month in the formatting. + // Starting at the position (index) in the (format) string, look back and look ahead to + // see if there is "d" or "dd". In the case like "d MMMM" or "MMMM dd", we can use + // genitive form. Genitive form is not used if there is more than two "d". + // Arguments: + // format The format string to be scanned. + // index Where we should start the scanning. This is generally where "M" starts. + // tokenLen The len of the current pattern character. This indicates how many "M" that we have. + // patternToMatch The pattern that we want to search. This generally uses "d" + // + private static bool IsUseGenitiveForm(ReadOnlySpan format, int index, int tokenLen, char patternToMatch) + { + int i; + int repeat = 0; + // + // Look back to see if we can find "d" or "ddd" + // + + // Find first "d". + for (i = index - 1; i >= 0 && format[i] != patternToMatch; i--) { /*Do nothing here */ }; + + if (i >= 0) + { + // Find a "d", so look back to see how many "d" that we can find. + while (--i >= 0 && format[i] == patternToMatch) + { + repeat++; + } + // + // repeat == 0 means that we have one (patternToMatch) + // repeat == 1 means that we have two (patternToMatch) + // + if (repeat <= 1) + { + return (true); + } + // Note that we can't just stop here. We may find "ddd" while looking back, and we have to look + // ahead to see if there is "d" or "dd". + } + + // + // If we can't find "d" or "dd" by looking back, try look ahead. + // + + // Find first "d" + for (i = index + tokenLen; i < format.Length && format[i] != patternToMatch; i++) { /* Do nothing here */ }; + + if (i < format.Length) + { + repeat = 0; + // Find a "d", so contine the walk to see how may "d" that we can find. + while (++i < format.Length && format[i] == patternToMatch) + { + repeat++; + } + // + // repeat == 0 means that we have one (patternToMatch) + // repeat == 1 means that we have two (patternToMatch) + // + if (repeat <= 1) + { + return (true); + } + } + return (false); + } + + + // + // FormatCustomized + // + // Actions: Format the DateTime instance using the specified format. + // + private static StringBuilder FormatCustomized( + DateTime dateTime, ReadOnlySpan format, DateTimeFormatInfo dtfi, TimeSpan offset, StringBuilder result) + { + Calendar cal = dtfi.Calendar; + + bool resultBuilderIsPooled = false; + if (result == null) + { + resultBuilderIsPooled = true; + result = StringBuilderCache.Acquire(); + } + + // This is a flag to indicate if we are format the dates using Hebrew calendar. + bool isHebrewCalendar = (cal.ID == CalendarId.HEBREW); + // This is a flag to indicate if we are formating hour/minute/second only. + bool bTimeOnly = true; + + int i = 0; + int tokenLen, hour12; + + while (i < format.Length) + { + char ch = format[i]; + int nextChar; + switch (ch) + { + case 'g': + tokenLen = ParseRepeatPattern(format, i, ch); + result.Append(dtfi.GetEraName(cal.GetEra(dateTime))); + break; + case 'h': + tokenLen = ParseRepeatPattern(format, i, ch); + hour12 = dateTime.Hour % 12; + if (hour12 == 0) + { + hour12 = 12; + } + FormatDigits(result, hour12, tokenLen); + break; + case 'H': + tokenLen = ParseRepeatPattern(format, i, ch); + FormatDigits(result, dateTime.Hour, tokenLen); + break; + case 'm': + tokenLen = ParseRepeatPattern(format, i, ch); + FormatDigits(result, dateTime.Minute, tokenLen); + break; + case 's': + tokenLen = ParseRepeatPattern(format, i, ch); + FormatDigits(result, dateTime.Second, tokenLen); + break; + case 'f': + case 'F': + tokenLen = ParseRepeatPattern(format, i, ch); + if (tokenLen <= MaxSecondsFractionDigits) + { + long fraction = (dateTime.Ticks % Calendar.TicksPerSecond); + fraction = fraction / (long)Math.Pow(10, 7 - tokenLen); + if (ch == 'f') + { + result.Append(((int)fraction).ToString(fixedNumberFormats[tokenLen - 1], CultureInfo.InvariantCulture)); + } + else + { + int effectiveDigits = tokenLen; + while (effectiveDigits > 0) + { + if (fraction % 10 == 0) + { + fraction = fraction / 10; + effectiveDigits--; + } + else + { + break; + } + } + if (effectiveDigits > 0) + { + result.Append(((int)fraction).ToString(fixedNumberFormats[effectiveDigits - 1], CultureInfo.InvariantCulture)); + } + else + { + // No fraction to emit, so see if we should remove decimal also. + if (result.Length > 0 && result[result.Length - 1] == '.') + { + result.Remove(result.Length - 1, 1); + } + } + } + } + else + { + if (resultBuilderIsPooled) + { + StringBuilderCache.Release(result); + } + throw new FormatException(SR.Format_InvalidString); + } + break; + case 't': + tokenLen = ParseRepeatPattern(format, i, ch); + if (tokenLen == 1) + { + if (dateTime.Hour < 12) + { + if (dtfi.AMDesignator.Length >= 1) + { + result.Append(dtfi.AMDesignator[0]); + } + } + else + { + if (dtfi.PMDesignator.Length >= 1) + { + result.Append(dtfi.PMDesignator[0]); + } + } + } + else + { + result.Append((dateTime.Hour < 12 ? dtfi.AMDesignator : dtfi.PMDesignator)); + } + break; + case 'd': + // + // tokenLen == 1 : Day of month as digits with no leading zero. + // tokenLen == 2 : Day of month as digits with leading zero for single-digit months. + // tokenLen == 3 : Day of week as a three-leter abbreviation. + // tokenLen >= 4 : Day of week as its full name. + // + tokenLen = ParseRepeatPattern(format, i, ch); + if (tokenLen <= 2) + { + int day = cal.GetDayOfMonth(dateTime); + if (isHebrewCalendar) + { + // For Hebrew calendar, we need to convert numbers to Hebrew text for yyyy, MM, and dd values. + HebrewFormatDigits(result, day); + } + else + { + FormatDigits(result, day, tokenLen); + } + } + else + { + int dayOfWeek = (int)cal.GetDayOfWeek(dateTime); + result.Append(FormatDayOfWeek(dayOfWeek, tokenLen, dtfi)); + } + bTimeOnly = false; + break; + case 'M': + // + // tokenLen == 1 : Month as digits with no leading zero. + // tokenLen == 2 : Month as digits with leading zero for single-digit months. + // tokenLen == 3 : Month as a three-letter abbreviation. + // tokenLen >= 4 : Month as its full name. + // + tokenLen = ParseRepeatPattern(format, i, ch); + int month = cal.GetMonth(dateTime); + if (tokenLen <= 2) + { + if (isHebrewCalendar) + { + // For Hebrew calendar, we need to convert numbers to Hebrew text for yyyy, MM, and dd values. + HebrewFormatDigits(result, month); + } + else + { + FormatDigits(result, month, tokenLen); + } + } + else + { + if (isHebrewCalendar) + { + result.Append(FormatHebrewMonthName(dateTime, month, tokenLen, dtfi)); + } + else + { + if ((dtfi.FormatFlags & DateTimeFormatFlags.UseGenitiveMonth) != 0 && tokenLen >= 4) + { + result.Append( + dtfi.internalGetMonthName( + month, + IsUseGenitiveForm(format, i, tokenLen, 'd') ? MonthNameStyles.Genitive : MonthNameStyles.Regular, + false)); + } + else + { + result.Append(FormatMonth(month, tokenLen, dtfi)); + } + } + } + bTimeOnly = false; + break; + case 'y': + // Notes about OS behavior: + // y: Always print (year % 100). No leading zero. + // yy: Always print (year % 100) with leading zero. + // yyy/yyyy/yyyyy/... : Print year value. No leading zero. + + int year = cal.GetYear(dateTime); + tokenLen = ParseRepeatPattern(format, i, ch); + if (dtfi.HasForceTwoDigitYears) + { + FormatDigits(result, year, tokenLen <= 2 ? tokenLen : 2); + } + else if (cal.ID == CalendarId.HEBREW) + { + HebrewFormatDigits(result, year); + } + else + { + if (tokenLen <= 2) + { + FormatDigits(result, year % 100, tokenLen); + } + else + { + String fmtPattern = "D" + tokenLen.ToString(); + result.Append(year.ToString(fmtPattern, CultureInfo.InvariantCulture)); + } + } + bTimeOnly = false; + break; + case 'z': + tokenLen = ParseRepeatPattern(format, i, ch); + FormatCustomizedTimeZone(dateTime, offset, format, tokenLen, bTimeOnly, result); + break; + case 'K': + tokenLen = 1; + FormatCustomizedRoundripTimeZone(dateTime, offset, result); + break; + case ':': + result.Append(dtfi.TimeSeparator); + tokenLen = 1; + break; + case '/': + result.Append(dtfi.DateSeparator); + tokenLen = 1; + break; + case '\'': + case '\"': + tokenLen = ParseQuoteString(format, i, result); + break; + case '%': + // Optional format character. + // For example, format string "%d" will print day of month + // without leading zero. Most of the cases, "%" can be ignored. + nextChar = ParseNextChar(format, i); + // nextChar will be -1 if we already reach the end of the format string. + // Besides, we will not allow "%%" appear in the pattern. + if (nextChar >= 0 && nextChar != '%') + { + char nextCharChar = (char)nextChar; + StringBuilder origStringBuilder = FormatCustomized(dateTime, ReadOnlySpan.DangerousCreate(null, ref nextCharChar, 1), dtfi, offset, result); + Debug.Assert(ReferenceEquals(origStringBuilder, result)); + tokenLen = 2; + } + else + { + // + // This means that '%' is at the end of the format string or + // "%%" appears in the format string. + // + if (resultBuilderIsPooled) + { + StringBuilderCache.Release(result); + } + throw new FormatException(SR.Format_InvalidString); + } + break; + case '\\': + // Escaped character. Can be used to insert character into the format string. + // For exmple, "\d" will insert the character 'd' into the string. + // + // NOTENOTE : we can remove this format character if we enforce the enforced quote + // character rule. + // That is, we ask everyone to use single quote or double quote to insert characters, + // then we can remove this character. + // + nextChar = ParseNextChar(format, i); + if (nextChar >= 0) + { + result.Append(((char)nextChar)); + tokenLen = 2; + } + else + { + // + // This means that '\' is at the end of the formatting string. + // + if (resultBuilderIsPooled) + { + StringBuilderCache.Release(result); + } + throw new FormatException(SR.Format_InvalidString); + } + break; + default: + // NOTENOTE : we can remove this rule if we enforce the enforced quote + // character rule. + // That is, if we ask everyone to use single quote or double quote to insert characters, + // then we can remove this default block. + result.Append(ch); + tokenLen = 1; + break; + } + i += tokenLen; + } + return result; + } + + + // output the 'z' famliy of formats, which output a the offset from UTC, e.g. "-07:30" + private static void FormatCustomizedTimeZone(DateTime dateTime, TimeSpan offset, ReadOnlySpan format, Int32 tokenLen, Boolean timeOnly, StringBuilder result) + { + // See if the instance already has an offset + Boolean dateTimeFormat = (offset == NullOffset); + if (dateTimeFormat) + { + // No offset. The instance is a DateTime and the output should be the local time zone + + if (timeOnly && dateTime.Ticks < Calendar.TicksPerDay) + { + // For time only format and a time only input, the time offset on 0001/01/01 is less + // accurate than the system's current offset because of daylight saving time. + offset = TimeZoneInfo.GetLocalUtcOffset(DateTime.Now, TimeZoneInfoOptions.NoThrowOnInvalidTime); + } + else if (dateTime.Kind == DateTimeKind.Utc) + { + offset = TimeSpan.Zero; + } + else + { + offset = TimeZoneInfo.GetLocalUtcOffset(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime); + } + } + if (offset >= TimeSpan.Zero) + { + result.Append('+'); + } + else + { + result.Append('-'); + // get a positive offset, so that you don't need a separate code path for the negative numbers. + offset = offset.Negate(); + } + + if (tokenLen <= 1) + { + // 'z' format e.g "-7" + result.AppendFormat(CultureInfo.InvariantCulture, "{0:0}", offset.Hours); + } + else + { + // 'zz' or longer format e.g "-07" + result.AppendFormat(CultureInfo.InvariantCulture, "{0:00}", offset.Hours); + if (tokenLen >= 3) + { + // 'zzz*' or longer format e.g "-07:30" + result.AppendFormat(CultureInfo.InvariantCulture, ":{0:00}", offset.Minutes); + } + } + } + + // output the 'K' format, which is for round-tripping the data + private static void FormatCustomizedRoundripTimeZone(DateTime dateTime, TimeSpan offset, StringBuilder result) + { + // The objective of this format is to round trip the data in the type + // For DateTime it should round-trip the Kind value and preserve the time zone. + // DateTimeOffset instance, it should do so by using the internal time zone. + + if (offset == NullOffset) + { + // source is a date time, so behavior depends on the kind. + switch (dateTime.Kind) + { + case DateTimeKind.Local: + // This should output the local offset, e.g. "-07:30" + offset = TimeZoneInfo.GetLocalUtcOffset(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime); + // fall through to shared time zone output code + break; + case DateTimeKind.Utc: + // The 'Z' constant is a marker for a UTC date + result.Append("Z"); + return; + default: + // If the kind is unspecified, we output nothing here + return; + } + } + if (offset >= TimeSpan.Zero) + { + result.Append('+'); + } + else + { + result.Append('-'); + // get a positive offset, so that you don't need a separate code path for the negative numbers. + offset = offset.Negate(); + } + + AppendNumber(result, offset.Hours, 2); + result.Append(':'); + AppendNumber(result, offset.Minutes, 2); + } + + internal static String GetRealFormat(ReadOnlySpan format, DateTimeFormatInfo dtfi) + { + String realFormat = null; + + switch (format[0]) + { + case 'd': // Short Date + realFormat = dtfi.ShortDatePattern; + break; + case 'D': // Long Date + realFormat = dtfi.LongDatePattern; + break; + case 'f': // Full (long date + short time) + realFormat = dtfi.LongDatePattern + " " + dtfi.ShortTimePattern; + break; + case 'F': // Full (long date + long time) + realFormat = dtfi.FullDateTimePattern; + break; + case 'g': // General (short date + short time) + realFormat = dtfi.GeneralShortTimePattern; + break; + case 'G': // General (short date + long time) + realFormat = dtfi.GeneralLongTimePattern; + break; + case 'm': + case 'M': // Month/Day Date + realFormat = dtfi.MonthDayPattern; + break; + case 'o': + case 'O': + realFormat = RoundtripFormat; + break; + case 'r': + case 'R': // RFC 1123 Standard + realFormat = dtfi.RFC1123Pattern; + break; + case 's': // Sortable without Time Zone Info + realFormat = dtfi.SortableDateTimePattern; + break; + case 't': // Short Time + realFormat = dtfi.ShortTimePattern; + break; + case 'T': // Long Time + realFormat = dtfi.LongTimePattern; + break; + case 'u': // Universal with Sortable format + realFormat = dtfi.UniversalSortableDateTimePattern; + break; + case 'U': // Universal with Full (long date + long time) format + realFormat = dtfi.FullDateTimePattern; + break; + case 'y': + case 'Y': // Year/Month Date + realFormat = dtfi.YearMonthPattern; + break; + default: + throw new FormatException(SR.Format_InvalidString); + } + return (realFormat); + } + + + // Expand a pre-defined format string (like "D" for long date) to the real format that + // we are going to use in the date time parsing. + // This method also convert the dateTime if necessary (e.g. when the format is in Universal time), + // and change dtfi if necessary (e.g. when the format should use invariant culture). + // + private static String ExpandPredefinedFormat(ReadOnlySpan format, ref DateTime dateTime, ref DateTimeFormatInfo dtfi, ref TimeSpan offset) + { + switch (format[0]) + { + case 'o': + case 'O': // Round trip format + dtfi = DateTimeFormatInfo.InvariantInfo; + break; + case 'r': + case 'R': // RFC 1123 Standard + if (offset != NullOffset) + { + // Convert to UTC invariants mean this will be in range + dateTime = dateTime - offset; + } + else if (dateTime.Kind == DateTimeKind.Local) + { + InvalidFormatForLocal(format, dateTime); + } + dtfi = DateTimeFormatInfo.InvariantInfo; + break; + case 's': // Sortable without Time Zone Info + dtfi = DateTimeFormatInfo.InvariantInfo; + break; + case 'u': // Universal time in sortable format. + if (offset != NullOffset) + { + // Convert to UTC invariants mean this will be in range + dateTime = dateTime - offset; + } + else if (dateTime.Kind == DateTimeKind.Local) + { + InvalidFormatForLocal(format, dateTime); + } + dtfi = DateTimeFormatInfo.InvariantInfo; + break; + case 'U': // Universal time in culture dependent format. + if (offset != NullOffset) + { + // This format is not supported by DateTimeOffset + throw new FormatException(SR.Format_InvalidString); + } + // Universal time is always in Greogrian calendar. + // + // Change the Calendar to be Gregorian Calendar. + // + dtfi = (DateTimeFormatInfo)dtfi.Clone(); + if (dtfi.Calendar.GetType() != typeof(GregorianCalendar)) + { + dtfi.Calendar = GregorianCalendar.GetDefaultInstance(); + } + dateTime = dateTime.ToUniversalTime(); + break; + } + return GetRealFormat(format, dtfi); + } + + internal static String Format(DateTime dateTime, String format, DateTimeFormatInfo dtfi) + { + return Format(dateTime, format, dtfi, NullOffset); + } + + internal static string Format(DateTime dateTime, String format, DateTimeFormatInfo dtfi, TimeSpan offset) => + StringBuilderCache.GetStringAndRelease(FormatStringBuilder(dateTime, format, dtfi, offset)); + + internal static bool TryFormat(DateTime dateTime, Span destination, out int charsWritten, ReadOnlySpan format, DateTimeFormatInfo dtfi) => + TryFormat(dateTime, destination, out charsWritten, format, dtfi, NullOffset); + + internal static bool TryFormat(DateTime dateTime, Span destination, out int charsWritten, ReadOnlySpan format, DateTimeFormatInfo dtfi, TimeSpan offset) + { + StringBuilder sb = FormatStringBuilder(dateTime, format, dtfi, offset); + + bool success = sb.Length <= destination.Length; + if (success) + { + sb.CopyTo(0, destination, sb.Length); + charsWritten = sb.Length; + } + else + { + charsWritten = 0; + } + + StringBuilderCache.Release(sb); + return success; + } + + internal static StringBuilder FormatStringBuilder(DateTime dateTime, ReadOnlySpan format, DateTimeFormatInfo dtfi, TimeSpan offset) + { + Debug.Assert(dtfi != null); + if (format.Length == 0) + { + Boolean timeOnlySpecialCase = false; + if (dateTime.Ticks < Calendar.TicksPerDay) + { + // If the time is less than 1 day, consider it as time of day. + // Just print out the short time format. + // + // This is a workaround for VB, since they use ticks less then one day to be + // time of day. In cultures which use calendar other than Gregorian calendar, these + // alternative calendar may not support ticks less than a day. + // For example, Japanese calendar only supports date after 1868/9/8. + // This will pose a problem when people in VB get the time of day, and use it + // to call ToString(), which will use the general format (short date + long time). + // Since Japanese calendar does not support Gregorian year 0001, an exception will be + // thrown when we try to get the Japanese year for Gregorian year 0001. + // Therefore, the workaround allows them to call ToString() for time of day from a DateTime by + // formatting as ISO 8601 format. + switch (dtfi.Calendar.ID) + { + case CalendarId.JAPAN: + case CalendarId.TAIWAN: + case CalendarId.HIJRI: + case CalendarId.HEBREW: + case CalendarId.JULIAN: + case CalendarId.UMALQURA: + case CalendarId.PERSIAN: + timeOnlySpecialCase = true; + dtfi = DateTimeFormatInfo.InvariantInfo; + break; + } + } + if (offset == NullOffset) + { + // Default DateTime.ToString case. + format = timeOnlySpecialCase ? "s" : "G"; + } + else + { + // Default DateTimeOffset.ToString case. + format = timeOnlySpecialCase ? RoundtripDateTimeUnfixed : dtfi.DateTimeOffsetPattern; + } + } + + if (format.Length == 1) + { + switch (format[0]) + { + case 'O': + case 'o': + return FastFormatRoundtrip(dateTime, offset); + case 'R': + case 'r': + return FastFormatRfc1123(dateTime, offset, dtfi); + } + + format = ExpandPredefinedFormat(format, ref dateTime, ref dtfi, ref offset); + } + + return FormatCustomized(dateTime, format, dtfi, offset, result: null); + } + + internal static StringBuilder FastFormatRfc1123(DateTime dateTime, TimeSpan offset, DateTimeFormatInfo dtfi) + { + // ddd, dd MMM yyyy HH:mm:ss GMT + const int Rfc1123FormatLength = 29; + StringBuilder result = StringBuilderCache.Acquire(Rfc1123FormatLength); + + if (offset != NullOffset) + { + // Convert to UTC invariants + dateTime = dateTime - offset; + } + + dateTime.GetDatePart(out int year, out int month, out int day); + result.Append(InvariantAbbreviatedDayNames[(int)dateTime.DayOfWeek]); + result.Append(','); + result.Append(' '); + AppendNumber(result, day, 2); + result.Append(' '); + result.Append(InvariantAbbreviatedMonthNames[month - 1]); + result.Append(' '); + AppendNumber(result, year, 4); + result.Append(' '); + AppendHHmmssTimeOfDay(result, dateTime); + result.Append(' '); + result.Append(Gmt); + + return result; + } + + internal static StringBuilder FastFormatRoundtrip(DateTime dateTime, TimeSpan offset) + { + // yyyy-MM-ddTHH:mm:ss.fffffffK + const int roundTripFormatLength = 28; + StringBuilder result = StringBuilderCache.Acquire(roundTripFormatLength); + + dateTime.GetDatePart(out int year, out int month, out int day); + AppendNumber(result, year, 4); + result.Append('-'); + AppendNumber(result, month, 2); + result.Append('-'); + AppendNumber(result, day, 2); + result.Append('T'); + AppendHHmmssTimeOfDay(result, dateTime); + result.Append('.'); + + long fraction = dateTime.Ticks % TimeSpan.TicksPerSecond; + AppendNumber(result, fraction, 7); + + FormatCustomizedRoundripTimeZone(dateTime, offset, result); + + return result; + } + + private static void AppendHHmmssTimeOfDay(StringBuilder result, DateTime dateTime) + { + // HH:mm:ss + AppendNumber(result, dateTime.Hour, 2); + result.Append(':'); + AppendNumber(result, dateTime.Minute, 2); + result.Append(':'); + AppendNumber(result, dateTime.Second, 2); + } + + internal static void AppendNumber(StringBuilder builder, long val, int digits) + { + for (int i = 0; i < digits; i++) + { + builder.Append('0'); + } + + int index = 1; + while (val > 0 && index <= digits) + { + builder[builder.Length - index] = (char)('0' + (val % 10)); + val = val / 10; + index++; + } + + Debug.Assert(val == 0, "DateTimeFormat.AppendNumber(): digits less than size of val"); + } + + internal static String[] GetAllDateTimes(DateTime dateTime, char format, DateTimeFormatInfo dtfi) + { + Debug.Assert(dtfi != null); + String[] allFormats = null; + String[] results = null; + + switch (format) + { + case 'd': + case 'D': + case 'f': + case 'F': + case 'g': + case 'G': + case 'm': + case 'M': + case 't': + case 'T': + case 'y': + case 'Y': + allFormats = dtfi.GetAllDateTimePatterns(format); + results = new String[allFormats.Length]; + for (int i = 0; i < allFormats.Length; i++) + { + results[i] = Format(dateTime, allFormats[i], dtfi); + } + break; + case 'U': + DateTime universalTime = dateTime.ToUniversalTime(); + allFormats = dtfi.GetAllDateTimePatterns(format); + results = new String[allFormats.Length]; + for (int i = 0; i < allFormats.Length; i++) + { + results[i] = Format(universalTime, allFormats[i], dtfi); + } + break; + // + // The following ones are special cases because these patterns are read-only in + // DateTimeFormatInfo. + // + case 'r': + case 'R': + case 'o': + case 'O': + case 's': + case 'u': + results = new String[] { Format(dateTime, new String(format, 1), dtfi) }; + break; + default: + throw new FormatException(SR.Format_InvalidString); + } + return (results); + } + + internal static String[] GetAllDateTimes(DateTime dateTime, DateTimeFormatInfo dtfi) + { + List results = new List(DEFAULT_ALL_DATETIMES_SIZE); + + for (int i = 0; i < allStandardFormats.Length; i++) + { + String[] strings = GetAllDateTimes(dateTime, allStandardFormats[i], dtfi); + for (int j = 0; j < strings.Length; j++) + { + results.Add(strings[j]); + } + } + String[] value = new String[results.Count]; + results.CopyTo(0, value, 0, results.Count); + return (value); + } + + // This is a placeholder for an MDA to detect when the user is using a + // local DateTime with a format that will be interpreted as UTC. + internal static void InvalidFormatForLocal(ReadOnlySpan format, DateTime dateTime) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/DateTimeFormatInfo.cs.REMOVED.git-id b/external/corefx/src/Common/src/CoreLib/System/Globalization/DateTimeFormatInfo.cs.REMOVED.git-id new file mode 100644 index 0000000000..857885c948 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/DateTimeFormatInfo.cs.REMOVED.git-id @@ -0,0 +1 @@ +c8f5ee477e60808f0cb7a884a5df82a46c0cc360 \ No newline at end of file diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/DateTimeFormatInfoScanner.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/DateTimeFormatInfoScanner.cs new file mode 100644 index 0000000000..d3c3aac84e --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/DateTimeFormatInfoScanner.cs @@ -0,0 +1,729 @@ +// 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. + +//////////////////////////////////////////////////////////////////////////// +// +// DateTimeFormatInfoScanner +// +// Scan a specified DateTimeFormatInfo to search for data used in DateTime.Parse() +// +// The data includes: +// +// DateWords: such as "de" used in es-ES (Spanish) LongDatePattern. +// Postfix: such as "ta" used in fi-FI after the month name. +// +// This class is shared among mscorlib.dll and sysglobl.dll. +// Use conditional CULTURE_AND_REGIONINFO_BUILDER_ONLY to differentiate between +// methods for mscorlib.dll and sysglobl.dll. +// +//////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Text; + +namespace System.Globalization +{ + // + // from LocaleEx.txt header + // + //; IFORMATFLAGS + //; Parsing/formatting flags. + internal enum FORMATFLAGS + { + None = 0x00000000, + UseGenitiveMonth = 0x00000001, + UseLeapYearMonth = 0x00000002, + UseSpacesInMonthNames = 0x00000004, + UseHebrewParsing = 0x00000008, + UseSpacesInDayNames = 0x00000010, // Has spaces or non-breaking space in the day names. + UseDigitPrefixInTokens = 0x00000020, // Has token starting with numbers. + } + + internal enum CalendarId : ushort + { + UNINITIALIZED_VALUE = 0, + GREGORIAN = 1, // Gregorian (localized) calendar + GREGORIAN_US = 2, // Gregorian (U.S.) calendar + JAPAN = 3, // Japanese Emperor Era calendar + /* SSS_WARNINGS_OFF */ + TAIWAN = 4, // Taiwan Era calendar /* SSS_WARNINGS_ON */ + KOREA = 5, // Korean Tangun Era calendar + HIJRI = 6, // Hijri (Arabic Lunar) calendar + THAI = 7, // Thai calendar + HEBREW = 8, // Hebrew (Lunar) calendar + GREGORIAN_ME_FRENCH = 9, // Gregorian Middle East French calendar + GREGORIAN_ARABIC = 10, // Gregorian Arabic calendar + GREGORIAN_XLIT_ENGLISH = 11, // Gregorian Transliterated English calendar + GREGORIAN_XLIT_FRENCH = 12, + // Note that all calendars after this point are MANAGED ONLY for now. + JULIAN = 13, + JAPANESELUNISOLAR = 14, + CHINESELUNISOLAR = 15, + SAKA = 16, // reserved to match Office but not implemented in our code + LUNAR_ETO_CHN = 17, // reserved to match Office but not implemented in our code + LUNAR_ETO_KOR = 18, // reserved to match Office but not implemented in our code + LUNAR_ETO_ROKUYOU = 19, // reserved to match Office but not implemented in our code + KOREANLUNISOLAR = 20, + TAIWANLUNISOLAR = 21, + PERSIAN = 22, + UMALQURA = 23, + LAST_CALENDAR = 23 // Last calendar ID + } + + internal class DateTimeFormatInfoScanner + { + // Special prefix-like flag char in DateWord array. + + // Use char in PUA area since we won't be using them in real data. + // The char used to tell a read date word or a month postfix. A month postfix + // is "ta" in the long date pattern like "d. MMMM'ta 'yyyy" for fi-FI. + // In this case, it will be stored as "\xfffeta" in the date word array. + internal const char MonthPostfixChar = '\xe000'; + + // Add ignorable symbol in a DateWord array. + + // hu-HU has: + // shrot date pattern: yyyy. MM. dd.;yyyy-MM-dd;yy-MM-dd + // long date pattern: yyyy. MMMM d. + // Here, "." is the date separator (derived from short date pattern). However, + // "." also appear at the end of long date pattern. In this case, we just + // "." as ignorable symbol so that the DateTime.Parse() state machine will not + // treat the additional date separator at the end of y,m,d pattern as an error + // condition. + internal const char IgnorableSymbolChar = '\xe001'; + + // Known CJK suffix + internal const String CJKYearSuff = "\u5e74"; + internal const String CJKMonthSuff = "\u6708"; + internal const String CJKDaySuff = "\u65e5"; + + internal const String KoreanYearSuff = "\ub144"; + internal const String KoreanMonthSuff = "\uc6d4"; + internal const String KoreanDaySuff = "\uc77c"; + + internal const String KoreanHourSuff = "\uc2dc"; + internal const String KoreanMinuteSuff = "\ubd84"; + internal const String KoreanSecondSuff = "\ucd08"; + + internal const String CJKHourSuff = "\u6642"; + internal const String ChineseHourSuff = "\u65f6"; + + internal const String CJKMinuteSuff = "\u5206"; + internal const String CJKSecondSuff = "\u79d2"; + + // The collection fo date words & postfix. + internal List m_dateWords = new List(); + // Hashtable for the known words. + private static volatile Dictionary s_knownWords; + + static Dictionary KnownWords + { + get + { + if (s_knownWords == null) + { + Dictionary temp = new Dictionary(); + // Add known words into the hash table. + + // Skip these special symbols. + temp.Add("/", String.Empty); + temp.Add("-", String.Empty); + temp.Add(".", String.Empty); + // Skip known CJK suffixes. + temp.Add(CJKYearSuff, String.Empty); + temp.Add(CJKMonthSuff, String.Empty); + temp.Add(CJKDaySuff, String.Empty); + temp.Add(KoreanYearSuff, String.Empty); + temp.Add(KoreanMonthSuff, String.Empty); + temp.Add(KoreanDaySuff, String.Empty); + temp.Add(KoreanHourSuff, String.Empty); + temp.Add(KoreanMinuteSuff, String.Empty); + temp.Add(KoreanSecondSuff, String.Empty); + temp.Add(CJKHourSuff, String.Empty); + temp.Add(ChineseHourSuff, String.Empty); + temp.Add(CJKMinuteSuff, String.Empty); + temp.Add(CJKSecondSuff, String.Empty); + + s_knownWords = temp; + } + return (s_knownWords); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Parameters: + // pattern: The pattern to be scanned. + // currentIndex: the current index to start the scan. + // + // Returns: + // Return the index with the first character that is a letter, which will + // be the start of a date word. + // Note that the index can be pattern.Length if we reach the end of the string. + // + //////////////////////////////////////////////////////////////////////////// + internal static int SkipWhiteSpacesAndNonLetter(String pattern, int currentIndex) + { + while (currentIndex < pattern.Length) + { + char ch = pattern[currentIndex]; + if (ch == '\\') + { + // Escaped character. Look ahead one character. + currentIndex++; + if (currentIndex < pattern.Length) + { + ch = pattern[currentIndex]; + if (ch == '\'') + { + // Skip the leading single quote. We will + // stop at the first letter. + continue; + } + // Fall thru to check if this is a letter. + } + else + { + // End of string + break; + } + } + if (Char.IsLetter(ch) || ch == '\'' || ch == '.') + { + break; + } + // Skip the current char since it is not a letter. + currentIndex++; + } + return (currentIndex); + } + + //////////////////////////////////////////////////////////////////////////// + // + // A helper to add the found date word or month postfix into ArrayList for date words. + // + // Parameters: + // formatPostfix: What kind of postfix this is. + // Possible values: + // null: This is a regular date word + // "MMMM": month postfix + // word: The date word or postfix to be added. + // + //////////////////////////////////////////////////////////////////////////// + internal void AddDateWordOrPostfix(String formatPostfix, String str) + { + if (str.Length > 0) + { + // Some cultures use . like an abbreviation + if (str.Equals(".")) + { + AddIgnorableSymbols("."); + return; + } + String words; + if (KnownWords.TryGetValue(str, out words) == false) + { + if (m_dateWords == null) + { + m_dateWords = new List(); + } + if (formatPostfix == "MMMM") + { + // Add the word into the ArrayList as "\xfffe" + real month postfix. + String temp = MonthPostfixChar + str; + if (!m_dateWords.Contains(temp)) + { + m_dateWords.Add(temp); + } + } + else + { + if (!m_dateWords.Contains(str)) + { + m_dateWords.Add(str); + } + if (str[str.Length - 1] == '.') + { + // Old version ignore the trialing dot in the date words. Support this as well. + String strWithoutDot = str.Substring(0, str.Length - 1); + if (!m_dateWords.Contains(strWithoutDot)) + { + m_dateWords.Add(strWithoutDot); + } + } + } + } + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Scan the pattern from the specified index and add the date word/postfix + // when appropriate. + // + // Parameters: + // pattern: The pattern to be scanned. + // index: The starting index to be scanned. + // formatPostfix: The kind of postfix to be scanned. + // Possible values: + // null: This is a regular date word + // "MMMM": month postfix + // + // + //////////////////////////////////////////////////////////////////////////// + internal int AddDateWords(String pattern, int index, String formatPostfix) + { + // Skip any whitespaces so we will start from a letter. + int newIndex = SkipWhiteSpacesAndNonLetter(pattern, index); + if (newIndex != index && formatPostfix != null) + { + // There are whitespaces. This will not be a postfix. + formatPostfix = null; + } + index = newIndex; + + // This is the first char added into dateWord. + // Skip all non-letter character. We will add the first letter into DateWord. + StringBuilder dateWord = new StringBuilder(); + // We assume that date words should start with a letter. + // Skip anything until we see a letter. + + while (index < pattern.Length) + { + char ch = pattern[index]; + if (ch == '\'') + { + // We have seen the end of quote. Add the word if we do not see it before, + // and break the while loop. + AddDateWordOrPostfix(formatPostfix, dateWord.ToString()); + index++; + break; + } + else if (ch == '\\') + { + // + // Escaped character. Look ahead one character + // + + // Skip escaped backslash. + index++; + if (index < pattern.Length) + { + dateWord.Append(pattern[index]); + index++; + } + } + else if (Char.IsWhiteSpace(ch)) + { + // Found a whitespace. We have to add the current date word/postfix. + AddDateWordOrPostfix(formatPostfix, dateWord.ToString()); + if (formatPostfix != null) + { + // Done with postfix. The rest will be regular date word. + formatPostfix = null; + } + // Reset the dateWord. + dateWord.Length = 0; + index++; + } + else + { + dateWord.Append(ch); + index++; + } + } + return (index); + } + + //////////////////////////////////////////////////////////////////////////// + // + // A simple helper to find the repeat count for a specified char. + // + //////////////////////////////////////////////////////////////////////////// + internal static int ScanRepeatChar(String pattern, char ch, int index, out int count) + { + count = 1; + while (++index < pattern.Length && pattern[index] == ch) + { + count++; + } + // Return the updated position. + return (index); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Add the text that is a date separator but is treated like ignroable symbol. + // E.g. + // hu-HU has: + // shrot date pattern: yyyy. MM. dd.;yyyy-MM-dd;yy-MM-dd + // long date pattern: yyyy. MMMM d. + // Here, "." is the date separator (derived from short date pattern). However, + // "." also appear at the end of long date pattern. In this case, we just + // "." as ignorable symbol so that the DateTime.Parse() state machine will not + // treat the additional date separator at the end of y,m,d pattern as an error + // condition. + // + //////////////////////////////////////////////////////////////////////////// + + internal void AddIgnorableSymbols(String text) + { + if (m_dateWords == null) + { + // Create the date word array. + m_dateWords = new List(); + } + // Add the ignorable symbol into the ArrayList. + String temp = IgnorableSymbolChar + text; + if (!m_dateWords.Contains(temp)) + { + m_dateWords.Add(temp); + } + } + + + // + // Flag used to trace the date patterns (yy/yyyyy/M/MM/MMM/MMM/d/dd) that we have seen. + // + private enum FoundDatePattern + { + None = 0x0000, + FoundYearPatternFlag = 0x0001, + FoundMonthPatternFlag = 0x0002, + FoundDayPatternFlag = 0x0004, + FoundYMDPatternFlag = 0x0007, // FoundYearPatternFlag | FoundMonthPatternFlag | FoundDayPatternFlag; + } + + // Check if we have found all of the year/month/day pattern. + private FoundDatePattern _ymdFlags = FoundDatePattern.None; + + + //////////////////////////////////////////////////////////////////////////// + // + // Given a date format pattern, scan for date word or postfix. + // + // A date word should be always put in a single quoted string. And it will + // start from a letter, so whitespace and symbols will be ignored before + // the first letter. + // + // Examples of date word: + // 'de' in es-SP: dddd, dd' de 'MMMM' de 'yyyy + // "\x0443." in bg-BG: dd.M.yyyy '\x0433.' + // + // Example of postfix: + // month postfix: + // "ta" in fi-FI: d. MMMM'ta 'yyyy + // Currently, only month postfix is supported. + // + // Usage: + // Always call this with Framework-style pattern, instead of Windows style pattern. + // Windows style pattern uses '' for single quote, while .NET uses \' + // + //////////////////////////////////////////////////////////////////////////// + internal void ScanDateWord(String pattern) + { + // Check if we have found all of the year/month/day pattern. + _ymdFlags = FoundDatePattern.None; + + int i = 0; + while (i < pattern.Length) + { + char ch = pattern[i]; + int chCount; + + switch (ch) + { + case '\'': + // Find a beginning quote. Search until the end quote. + i = AddDateWords(pattern, i + 1, null); + break; + case 'M': + i = ScanRepeatChar(pattern, 'M', i, out chCount); + if (chCount >= 4) + { + if (i < pattern.Length && pattern[i] == '\'') + { + i = AddDateWords(pattern, i + 1, "MMMM"); + } + } + _ymdFlags |= FoundDatePattern.FoundMonthPatternFlag; + break; + case 'y': + i = ScanRepeatChar(pattern, 'y', i, out chCount); + _ymdFlags |= FoundDatePattern.FoundYearPatternFlag; + break; + case 'd': + i = ScanRepeatChar(pattern, 'd', i, out chCount); + if (chCount <= 2) + { + // Only count "d" & "dd". + // ddd, dddd are day names. Do not count them. + _ymdFlags |= FoundDatePattern.FoundDayPatternFlag; + } + break; + case '\\': + // Found a escaped char not in a quoted string. Skip the current backslash + // and its next character. + i += 2; + break; + case '.': + if (_ymdFlags == FoundDatePattern.FoundYMDPatternFlag) + { + // If we find a dot immediately after the we have seen all of the y, m, d pattern. + // treat it as a ignroable symbol. Check for comments in AddIgnorableSymbols for + // more details. + AddIgnorableSymbols("."); + _ymdFlags = FoundDatePattern.None; + } + i++; + break; + default: + if (_ymdFlags == FoundDatePattern.FoundYMDPatternFlag && !Char.IsWhiteSpace(ch)) + { + // We are not seeing "." after YMD. Clear the flag. + _ymdFlags = FoundDatePattern.None; + } + // We are not in quote. Skip the current character. + i++; + break; + } + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Given a DTFI, get all of the date words from date patterns and time patterns. + // + //////////////////////////////////////////////////////////////////////////// + + internal String[] GetDateWordsOfDTFI(DateTimeFormatInfo dtfi) + { + // Enumarate all LongDatePatterns, and get the DateWords and scan for month postfix. + String[] datePatterns = dtfi.GetAllDateTimePatterns('D'); + int i; + + // Scan the long date patterns + for (i = 0; i < datePatterns.Length; i++) + { + ScanDateWord(datePatterns[i]); + } + + // Scan the short date patterns + datePatterns = dtfi.GetAllDateTimePatterns('d'); + for (i = 0; i < datePatterns.Length; i++) + { + ScanDateWord(datePatterns[i]); + } + // Scan the YearMonth patterns. + datePatterns = dtfi.GetAllDateTimePatterns('y'); + for (i = 0; i < datePatterns.Length; i++) + { + ScanDateWord(datePatterns[i]); + } + + // Scan the month/day pattern + ScanDateWord(dtfi.MonthDayPattern); + + // Scan the long time patterns. + datePatterns = dtfi.GetAllDateTimePatterns('T'); + for (i = 0; i < datePatterns.Length; i++) + { + ScanDateWord(datePatterns[i]); + } + + // Scan the short time patterns. + datePatterns = dtfi.GetAllDateTimePatterns('t'); + for (i = 0; i < datePatterns.Length; i++) + { + ScanDateWord(datePatterns[i]); + } + + String[] result = null; + if (m_dateWords != null && m_dateWords.Count > 0) + { + result = new String[m_dateWords.Count]; + for (i = 0; i < m_dateWords.Count; i++) + { + result[i] = m_dateWords[i]; + } + } + return (result); + } + + + //////////////////////////////////////////////////////////////////////////// + // + // Scan the month names to see if genitive month names are used, and return + // the format flag. + // + //////////////////////////////////////////////////////////////////////////// + internal static FORMATFLAGS GetFormatFlagGenitiveMonth(String[] monthNames, String[] genitveMonthNames, String[] abbrevMonthNames, String[] genetiveAbbrevMonthNames) + { + // If we have different names in regular and genitive month names, use genitive month flag. + return ((!EqualStringArrays(monthNames, genitveMonthNames) || !EqualStringArrays(abbrevMonthNames, genetiveAbbrevMonthNames)) + ? FORMATFLAGS.UseGenitiveMonth : 0); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Scan the month names to see if spaces are used or start with a digit, and return the format flag + // + //////////////////////////////////////////////////////////////////////////// + internal static FORMATFLAGS GetFormatFlagUseSpaceInMonthNames(String[] monthNames, String[] genitveMonthNames, String[] abbrevMonthNames, String[] genetiveAbbrevMonthNames) + { + FORMATFLAGS formatFlags = 0; + formatFlags |= (ArrayElementsBeginWithDigit(monthNames) || + ArrayElementsBeginWithDigit(genitveMonthNames) || + ArrayElementsBeginWithDigit(abbrevMonthNames) || + ArrayElementsBeginWithDigit(genetiveAbbrevMonthNames) + ? FORMATFLAGS.UseDigitPrefixInTokens : 0); + + formatFlags |= (ArrayElementsHaveSpace(monthNames) || + ArrayElementsHaveSpace(genitveMonthNames) || + ArrayElementsHaveSpace(abbrevMonthNames) || + ArrayElementsHaveSpace(genetiveAbbrevMonthNames) + ? FORMATFLAGS.UseSpacesInMonthNames : 0); + return (formatFlags); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Scan the day names and set the correct format flag. + // + //////////////////////////////////////////////////////////////////////////// + internal static FORMATFLAGS GetFormatFlagUseSpaceInDayNames(String[] dayNames, String[] abbrevDayNames) + { + return ((ArrayElementsHaveSpace(dayNames) || + ArrayElementsHaveSpace(abbrevDayNames)) + ? FORMATFLAGS.UseSpacesInDayNames : 0); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Check the calendar to see if it is HebrewCalendar and set the Hebrew format flag if necessary. + // + //////////////////////////////////////////////////////////////////////////// + internal static FORMATFLAGS GetFormatFlagUseHebrewCalendar(int calID) + { + return (calID == (int)CalendarId.HEBREW ? + FORMATFLAGS.UseHebrewParsing | FORMATFLAGS.UseLeapYearMonth : 0); + } + + + //----------------------------------------------------------------------------- + // EqualStringArrays + // compares two string arrays and return true if all elements of the first + // array equals to all elmentsof the second array. + // otherwise it returns false. + //----------------------------------------------------------------------------- + + private static bool EqualStringArrays(string[] array1, string[] array2) + { + // Shortcut if they're the same array + if (array1 == array2) + { + return true; + } + + // This is effectively impossible + if (array1.Length != array2.Length) + { + return false; + } + + // Check each string + for (int i = 0; i < array1.Length; i++) + { + if (!array1[i].Equals(array2[i])) + { + return false; + } + } + + return true; + } + + //----------------------------------------------------------------------------- + // ArrayElementsHaveSpace + // It checks all input array elements if any of them has space character + // returns true if found space character in one of the array elements. + // otherwise returns false. + //----------------------------------------------------------------------------- + + private static bool ArrayElementsHaveSpace(string[] array) + { + for (int i = 0; i < array.Length; i++) + { + // it is faster to check for space character manually instead of calling IndexOf + // so we don't have to go to native code side. + for (int j = 0; j < array[i].Length; j++) + { + if (Char.IsWhiteSpace(array[i][j])) + { + return true; + } + } + } + + return false; + } + + + //////////////////////////////////////////////////////////////////////////// + // + // Check if any element of the array start with a digit. + // + //////////////////////////////////////////////////////////////////////////// + private static bool ArrayElementsBeginWithDigit(string[] array) + { + for (int i = 0; i < array.Length; i++) + { + // it is faster to check for space character manually instead of calling IndexOf + // so we don't have to go to native code side. + if (array[i].Length > 0 && + array[i][0] >= '0' && array[i][0] <= '9') + { + int index = 1; + while (index < array[i].Length && array[i][index] >= '0' && array[i][index] <= '9') + { + // Skip other digits. + index++; + } + if (index == array[i].Length) + { + return (false); + } + + if (index == array[i].Length - 1) + { + // Skip known CJK month suffix. + // CJK uses month name like "1\x6708", since \x6708 is a known month suffix, + // we don't need the UseDigitPrefixInTokens since it is slower. + switch (array[i][index]) + { + case '\x6708': // CJKMonthSuff + case '\xc6d4': // KoreanMonthSuff + return (false); + } + } + + if (index == array[i].Length - 4) + { + // Skip known CJK month suffix. + // Starting with Windows 8, the CJK months for some cultures looks like: "1' \x6708'" + // instead of just "1\x6708" + if (array[i][index] == '\'' && array[i][index + 1] == ' ' && + array[i][index + 2] == '\x6708' && array[i][index + 3] == '\'') + { + return (false); + } + } + return (true); + } + } + + return false; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/DateTimeParse.cs.REMOVED.git-id b/external/corefx/src/Common/src/CoreLib/System/Globalization/DateTimeParse.cs.REMOVED.git-id new file mode 100644 index 0000000000..2750fbe551 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/DateTimeParse.cs.REMOVED.git-id @@ -0,0 +1 @@ +40962f890ca764aeaf692b318788a308b597599a \ No newline at end of file diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/DateTimeStyles.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/DateTimeStyles.cs new file mode 100644 index 0000000000..79232ff199 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/DateTimeStyles.cs @@ -0,0 +1,49 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: Contains valid formats for DateTime recognized by +** the DateTime class' parsing code. +** +** +===========================================================*/ + +namespace System.Globalization +{ + [Flags] + public enum DateTimeStyles + { + // Bit flag indicating that leading whitespace is allowed. Character values + // 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, and 0x0020 are considered to be + // whitespace. + + + None = 0x00000000, + + AllowLeadingWhite = 0x00000001, + + AllowTrailingWhite = 0x00000002, //Bitflag indicating trailing whitespace is allowed. + + AllowInnerWhite = 0x00000004, + + AllowWhiteSpaces = AllowLeadingWhite | AllowInnerWhite | AllowTrailingWhite, + // When parsing a date/time string, if all year/month/day are missing, set the default date + // to 0001/1/1, instead of the current year/month/day. + + NoCurrentDateDefault = 0x00000008, + // When parsing a date/time string, if a timezone specifier ("GMT","Z","+xxxx", "-xxxx" exists), we will + // ajdust the parsed time based to GMT. + + AdjustToUniversal = 0x00000010, + + AssumeLocal = 0x00000020, + + AssumeUniversal = 0x00000040, + // Attempt to preserve whether the input is unspecified, local or UTC + RoundtripKind = 0x00000080, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/DaylightTime.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/DaylightTime.cs new file mode 100644 index 0000000000..e6920b3666 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/DaylightTime.cs @@ -0,0 +1,49 @@ +// 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. + +namespace System.Globalization +{ + // This class represents a starting/ending time for a period of daylight saving time. + public class DaylightTime + { + private readonly DateTime _start; + private readonly DateTime _end; + private readonly TimeSpan _delta; + + private DaylightTime() + { + } + + public DaylightTime(DateTime start, DateTime end, TimeSpan delta) + { + _start = start; + _end = end; + _delta = delta; + } + + // The start date of a daylight saving period. + public DateTime Start => _start; + + // The end date of a daylight saving period. + public DateTime End => _end; + + // Delta to stardard offset in ticks. + public TimeSpan Delta => _delta; + } + + // Value type version of DaylightTime + internal readonly struct DaylightTimeStruct + { + public DaylightTimeStruct(DateTime start, DateTime end, TimeSpan delta) + { + Start = start; + End = end; + Delta = delta; + } + + public readonly DateTime Start; + public readonly DateTime End; + public readonly TimeSpan Delta; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/DigitShapes.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/DigitShapes.cs new file mode 100644 index 0000000000..1ce45dbeb6 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/DigitShapes.cs @@ -0,0 +1,13 @@ +// 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. + +namespace System.Globalization +{ + public enum DigitShapes : int + { + Context = 0x0000, // The shape depends on the previous text in the same output. + None = 0x0001, // Gives full Unicode compatibility. + NativeNational = 0x0002 // National shapes + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/EastAsianLunisolarCalendar.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/EastAsianLunisolarCalendar.cs new file mode 100644 index 0000000000..358f4df182 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/EastAsianLunisolarCalendar.cs @@ -0,0 +1,703 @@ +// 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. + + +namespace System.Globalization +{ + public abstract class EastAsianLunisolarCalendar : Calendar + { + internal const int LeapMonth = 0; + internal const int Jan1Month = 1; + internal const int Jan1Date = 2; + internal const int nDaysPerMonth = 3; + + // # of days so far in the solar year + internal static readonly int[] DaysToMonth365 = + { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 + }; + + internal static readonly int[] DaysToMonth366 = + { + 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 + }; + + internal const int DatePartYear = 0; + internal const int DatePartDayOfYear = 1; + internal const int DatePartMonth = 2; + internal const int DatePartDay = 3; + + public override CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.LunisolarCalendar; + } + } + + // Return the year number in the 60-year cycle. + // + + public virtual int GetSexagenaryYear(DateTime time) + { + CheckTicksRange(time.Ticks); + + int year = 0, month = 0, day = 0; + TimeToLunar(time, ref year, ref month, ref day); + + return ((year - 4) % 60) + 1; + } + + // Return the celestial year from the 60-year cycle. + // The returned value is from 1 ~ 10. + // + + public int GetCelestialStem(int sexagenaryYear) + { + if ((sexagenaryYear < 1) || (sexagenaryYear > 60)) + { + throw new ArgumentOutOfRangeException( + nameof(sexagenaryYear), + SR.Format(SR.ArgumentOutOfRange_Range, 1, 60)); + } + + return ((sexagenaryYear - 1) % 10) + 1; + } + + // Return the Terrestial Branch from the 60-year cycle. + // The returned value is from 1 ~ 12. + // + + public int GetTerrestrialBranch(int sexagenaryYear) + { + if ((sexagenaryYear < 1) || (sexagenaryYear > 60)) + { + throw new ArgumentOutOfRangeException( + nameof(sexagenaryYear), + SR.Format(SR.ArgumentOutOfRange_Range, 1, 60)); + } + + return ((sexagenaryYear - 1) % 12) + 1; + } + + internal abstract int GetYearInfo(int LunarYear, int Index); + internal abstract int GetYear(int year, DateTime time); + internal abstract int GetGregorianYear(int year, int era); + + internal abstract int MinCalendarYear { get; } + internal abstract int MaxCalendarYear { get; } + internal abstract EraInfo[] CalEraInfo { get; } + internal abstract DateTime MinDate { get; } + internal abstract DateTime MaxDate { get; } + + internal const int MaxCalendarMonth = 13; + internal const int MaxCalendarDay = 30; + + internal int MinEraCalendarYear(int era) + { + EraInfo[] mEraInfo = CalEraInfo; + //ChineseLunisolarCalendar does not has m_EraInfo it is going to retuen null + if (mEraInfo == null) + { + return MinCalendarYear; + } + + if (era == Calendar.CurrentEra) + { + era = CurrentEraValue; + } + //era has to be in the supported range otherwise we will throw exception in CheckEraRange() + if (era == GetEra(MinDate)) + { + return (GetYear(MinCalendarYear, MinDate)); + } + + for (int i = 0; i < mEraInfo.Length; i++) + { + if (era == mEraInfo[i].era) + { + return (mEraInfo[i].minEraYear); + } + } + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + + internal int MaxEraCalendarYear(int era) + { + EraInfo[] mEraInfo = CalEraInfo; + //ChineseLunisolarCalendar does not has m_EraInfo it is going to retuen null + if (mEraInfo == null) + { + return MaxCalendarYear; + } + + if (era == Calendar.CurrentEra) + { + era = CurrentEraValue; + } + //era has to be in the supported range otherwise we will throw exception in CheckEraRange() + if (era == GetEra(MaxDate)) + { + return (GetYear(MaxCalendarYear, MaxDate)); + } + + for (int i = 0; i < mEraInfo.Length; i++) + { + if (era == mEraInfo[i].era) + { + return (mEraInfo[i].maxEraYear); + } + } + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + + internal EastAsianLunisolarCalendar() + { + } + + internal void CheckTicksRange(long ticks) + { + if (ticks < MinSupportedDateTime.Ticks || ticks > MaxSupportedDateTime.Ticks) + { + throw new ArgumentOutOfRangeException( + "time", + String.Format(CultureInfo.InvariantCulture, SR.ArgumentOutOfRange_CalendarRange, + MinSupportedDateTime, MaxSupportedDateTime)); + } + } + + internal void CheckEraRange(int era) + { + if (era == Calendar.CurrentEra) + { + era = CurrentEraValue; + } + + if ((era < GetEra(MinDate)) || (era > GetEra(MaxDate))) + { + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + } + + internal int CheckYearRange(int year, int era) + { + CheckEraRange(era); + year = GetGregorianYear(year, era); + + if ((year < MinCalendarYear) || (year > MaxCalendarYear)) + { + throw new ArgumentOutOfRangeException( + nameof(year), + SR.Format(SR.ArgumentOutOfRange_Range, MinEraCalendarYear(era), MaxEraCalendarYear(era))); + } + return year; + } + + internal int CheckYearMonthRange(int year, int month, int era) + { + year = CheckYearRange(year, era); + + if (month == 13) + { + //Reject if there is no leap month this year + if (GetYearInfo(year, LeapMonth) == 0) + throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month); + } + + if (month < 1 || month > 13) + { + throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month); + } + return year; + } + + internal int InternalGetDaysInMonth(int year, int month) + { + int nDays; + int mask; // mask for extracting bits + + mask = 0x8000; + // convert the lunar day into a lunar month/date + mask >>= (month - 1); + if ((GetYearInfo(year, nDaysPerMonth) & mask) == 0) + nDays = 29; + else + nDays = 30; + return nDays; + } + + // Returns the number of days in the month given by the year and + // month arguments. + // + + public override int GetDaysInMonth(int year, int month, int era) + { + year = CheckYearMonthRange(year, month, era); + return InternalGetDaysInMonth(year, month); + } + + private static int GregorianIsLeapYear(int y) + { + return ((((y) % 4) != 0) ? 0 : ((((y) % 100) != 0) ? 1 : ((((y) % 400) != 0) ? 0 : 1))); + } + + // Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid. + // + + public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) + { + year = CheckYearMonthRange(year, month, era); + int daysInMonth = InternalGetDaysInMonth(year, month); + if (day < 1 || day > daysInMonth) + { + throw new ArgumentOutOfRangeException( + nameof(day), + SR.Format(SR.ArgumentOutOfRange_Day, daysInMonth, month)); + } + + int gy = 0; int gm = 0; int gd = 0; + + if (LunarToGregorian(year, month, day, ref gy, ref gm, ref gd)) + { + return new DateTime(gy, gm, gd, hour, minute, second, millisecond); + } + else + { + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay); + } + } + + + // + // GregorianToLunar calculates lunar calendar info for the given gregorian year, month, date. + // The input date should be validated before calling this method. + // + internal void GregorianToLunar(int nSYear, int nSMonth, int nSDate, ref int nLYear, ref int nLMonth, ref int nLDate) + { + // unsigned int nLYear, nLMonth, nLDate; // lunar ymd + int nSolarDay; // day # in solar year + int nLunarDay; // day # in lunar year + int fLeap; // is it a solar leap year? + int LDpM; // lunar days/month bitfield + int mask; // mask for extracting bits + int nDays; // # days this lunar month + int nJan1Month, nJan1Date; + + // calc the solar day of year + fLeap = GregorianIsLeapYear(nSYear); + nSolarDay = (fLeap == 1) ? DaysToMonth366[nSMonth - 1] : DaysToMonth365[nSMonth - 1]; + nSolarDay += nSDate; + + // init lunar year info + nLunarDay = nSolarDay; + nLYear = nSYear; + if (nLYear == (MaxCalendarYear + 1)) + { + nLYear--; + nLunarDay += ((GregorianIsLeapYear(nLYear) == 1) ? 366 : 365); + nJan1Month = GetYearInfo(nLYear, Jan1Month); + nJan1Date = GetYearInfo(nLYear, Jan1Date); + } + else + { + nJan1Month = GetYearInfo(nLYear, Jan1Month); + nJan1Date = GetYearInfo(nLYear, Jan1Date); + + // check if this solar date is actually part of the previous + // lunar year + if ((nSMonth < nJan1Month) || + (nSMonth == nJan1Month && nSDate < nJan1Date)) + { + // the corresponding lunar day is actually part of the previous + // lunar year + nLYear--; + + // add a solar year to the lunar day # + nLunarDay += ((GregorianIsLeapYear(nLYear) == 1) ? 366 : 365); + + // update the new start of year + nJan1Month = GetYearInfo(nLYear, Jan1Month); + nJan1Date = GetYearInfo(nLYear, Jan1Date); + } + } + + // convert solar day into lunar day. + // subtract off the beginning part of the solar year which is not + // part of the lunar year. since this part is always in Jan or Feb, + // we don't need to handle Leap Year (LY only affects March + // and later). + nLunarDay -= DaysToMonth365[nJan1Month - 1]; + nLunarDay -= (nJan1Date - 1); + + // convert the lunar day into a lunar month/date + mask = 0x8000; + LDpM = GetYearInfo(nLYear, nDaysPerMonth); + nDays = ((LDpM & mask) != 0) ? 30 : 29; + nLMonth = 1; + while (nLunarDay > nDays) + { + nLunarDay -= nDays; + nLMonth++; + mask >>= 1; + nDays = ((LDpM & mask) != 0) ? 30 : 29; + } + nLDate = nLunarDay; + } + + /* + //Convert from Lunar to Gregorian + //Highly inefficient, but it works based on the forward conversion + */ + internal bool LunarToGregorian(int nLYear, int nLMonth, int nLDate, ref int nSolarYear, ref int nSolarMonth, ref int nSolarDay) + { + int numLunarDays; + + if (nLDate < 1 || nLDate > 30) + return false; + + numLunarDays = nLDate - 1; + + //Add previous months days to form the total num of days from the first of the month. + for (int i = 1; i < nLMonth; i++) + { + numLunarDays += InternalGetDaysInMonth(nLYear, i); + } + + //Get Gregorian First of year + int nJan1Month = GetYearInfo(nLYear, Jan1Month); + int nJan1Date = GetYearInfo(nLYear, Jan1Date); + + // calc the solar day of year of 1 Lunar day + int fLeap = GregorianIsLeapYear(nLYear); + int[] days = (fLeap == 1) ? DaysToMonth366 : DaysToMonth365; + + nSolarDay = nJan1Date; + + if (nJan1Month > 1) + nSolarDay += days[nJan1Month - 1]; + + // Add the actual lunar day to get the solar day we want + nSolarDay = nSolarDay + numLunarDays;// - 1; + + if (nSolarDay > (fLeap + 365)) + { + nSolarYear = nLYear + 1; + nSolarDay -= (fLeap + 365); + } + else + { + nSolarYear = nLYear; + } + + for (nSolarMonth = 1; nSolarMonth < 12; nSolarMonth++) + { + if (days[nSolarMonth] >= nSolarDay) + break; + } + + nSolarDay -= days[nSolarMonth - 1]; + return true; + } + + internal DateTime LunarToTime(DateTime time, int year, int month, int day) + { + int gy = 0; int gm = 0; int gd = 0; + LunarToGregorian(year, month, day, ref gy, ref gm, ref gd); + return (GregorianCalendar.GetDefaultInstance().ToDateTime(gy, gm, gd, time.Hour, time.Minute, time.Second, time.Millisecond)); + } + + internal void TimeToLunar(DateTime time, ref int year, ref int month, ref int day) + { + int gy = 0; int gm = 0; int gd = 0; + + Calendar Greg = GregorianCalendar.GetDefaultInstance(); + gy = Greg.GetYear(time); + gm = Greg.GetMonth(time); + gd = Greg.GetDayOfMonth(time); + + GregorianToLunar(gy, gm, gd, ref year, ref month, ref day); + } + + // Returns the DateTime resulting from adding the given number of + // months to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year and month parts of the specified DateTime by + // value months, and, if required, adjusting the day part of the + // resulting date downwards to the last day of the resulting month in the + // resulting year. The time-of-day part of the result is the same as the + // time-of-day part of the specified DateTime. + // + + public override DateTime AddMonths(DateTime time, int months) + { + if (months < -120000 || months > 120000) + { + throw new ArgumentOutOfRangeException( + nameof(months), + SR.Format(SR.ArgumentOutOfRange_Range, -120000, 120000)); + } + + CheckTicksRange(time.Ticks); + + int y = 0; int m = 0; int d = 0; + TimeToLunar(time, ref y, ref m, ref d); + + int i = m + months; + if (i > 0) + { + int monthsInYear = InternalIsLeapYear(y) ? 13 : 12; + + while (i - monthsInYear > 0) + { + i -= monthsInYear; + y++; + monthsInYear = InternalIsLeapYear(y) ? 13 : 12; + } + m = i; + } + else + { + int monthsInYear; + while (i <= 0) + { + monthsInYear = InternalIsLeapYear(y - 1) ? 13 : 12; + i += monthsInYear; + y--; + } + m = i; + } + + int days = InternalGetDaysInMonth(y, m); + if (d > days) + { + d = days; + } + DateTime dt = LunarToTime(time, y, m, d); + + CheckAddResult(dt.Ticks, MinSupportedDateTime, MaxSupportedDateTime); + return (dt); + } + + + public override DateTime AddYears(DateTime time, int years) + { + CheckTicksRange(time.Ticks); + + int y = 0; int m = 0; int d = 0; + TimeToLunar(time, ref y, ref m, ref d); + + y += years; + + if (m == 13 && !InternalIsLeapYear(y)) + { + m = 12; + d = InternalGetDaysInMonth(y, m); + } + int DaysInMonths = InternalGetDaysInMonth(y, m); + if (d > DaysInMonths) + { + d = DaysInMonths; + } + + DateTime dt = LunarToTime(time, y, m, d); + CheckAddResult(dt.Ticks, MinSupportedDateTime, MaxSupportedDateTime); + return (dt); + } + + // Returns the day-of-year part of the specified DateTime. The returned value + // is an integer between 1 and [354|355 |383|384]. + // + + public override int GetDayOfYear(DateTime time) + { + CheckTicksRange(time.Ticks); + + int y = 0; int m = 0; int d = 0; + TimeToLunar(time, ref y, ref m, ref d); + + for (int i = 1; i < m; i++) + { + d = d + InternalGetDaysInMonth(y, i); + } + return d; + } + + // Returns the day-of-month part of the specified DateTime. The returned + // value is an integer between 1 and 29 or 30. + // + + public override int GetDayOfMonth(DateTime time) + { + CheckTicksRange(time.Ticks); + + int y = 0; int m = 0; int d = 0; + TimeToLunar(time, ref y, ref m, ref d); + + return d; + } + + // Returns the number of days in the year given by the year argument for the current era. + // + + public override int GetDaysInYear(int year, int era) + { + year = CheckYearRange(year, era); + + int Days = 0; + int monthsInYear = InternalIsLeapYear(year) ? 13 : 12; + + while (monthsInYear != 0) + Days += InternalGetDaysInMonth(year, monthsInYear--); + + return Days; + } + + // Returns the month part of the specified DateTime. The returned value is an + // integer between 1 and 13. + // + + public override int GetMonth(DateTime time) + { + CheckTicksRange(time.Ticks); + + int y = 0; int m = 0; int d = 0; + TimeToLunar(time, ref y, ref m, ref d); + + return m; + } + + // Returns the year part of the specified DateTime. The returned value is an + // integer between 1 and MaxCalendarYear. + // + + public override int GetYear(DateTime time) + { + CheckTicksRange(time.Ticks); + + int y = 0; int m = 0; int d = 0; + TimeToLunar(time, ref y, ref m, ref d); + + return GetYear(y, time); + } + + // Returns the day-of-week part of the specified DateTime. The returned value + // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates + // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates + // Thursday, 5 indicates Friday, and 6 indicates Saturday. + // + + public override DayOfWeek GetDayOfWeek(DateTime time) + { + CheckTicksRange(time.Ticks); + return ((DayOfWeek)((int)(time.Ticks / Calendar.TicksPerDay + 1) % 7)); + } + + // Returns the number of months in the specified year and era. + + public override int GetMonthsInYear(int year, int era) + { + year = CheckYearRange(year, era); + return (InternalIsLeapYear(year) ? 13 : 12); + } + + // Checks whether a given day in the specified era is a leap day. This method returns true if + // the date is a leap day, or false if not. + // + + public override bool IsLeapDay(int year, int month, int day, int era) + { + year = CheckYearMonthRange(year, month, era); + int daysInMonth = InternalGetDaysInMonth(year, month); + + if (day < 1 || day > daysInMonth) + { + throw new ArgumentOutOfRangeException( + nameof(day), + SR.Format(SR.ArgumentOutOfRange_Day, daysInMonth, month)); + } + int m = GetYearInfo(year, LeapMonth); + return ((m != 0) && (month == (m + 1))); + } + + // Checks whether a given month in the specified era is a leap month. This method returns true if + // month is a leap month, or false if not. + // + + public override bool IsLeapMonth(int year, int month, int era) + { + year = CheckYearMonthRange(year, month, era); + int m = GetYearInfo(year, LeapMonth); + return ((m != 0) && (month == (m + 1))); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this this year is not a leap year. + // + + public override int GetLeapMonth(int year, int era) + { + year = CheckYearRange(year, era); + int month = GetYearInfo(year, LeapMonth); + if (month > 0) + { + return (month + 1); + } + return 0; + } + + internal bool InternalIsLeapYear(int year) + { + return (GetYearInfo(year, LeapMonth) != 0); + } + // Checks whether a given year in the specified era is a leap year. This method returns true if + // year is a leap year, or false if not. + // + + public override bool IsLeapYear(int year, int era) + { + year = CheckYearRange(year, era); + return InternalIsLeapYear(year); + } + + private const int DEFAULT_GREGORIAN_TWO_DIGIT_YEAR_MAX = 2029; + + + public override int TwoDigitYearMax + { + get + { + if (twoDigitYearMax == -1) + { + twoDigitYearMax = GetSystemTwoDigitYearSetting(BaseCalendarID, GetYear(new DateTime(DEFAULT_GREGORIAN_TWO_DIGIT_YEAR_MAX, 1, 1))); + } + return (twoDigitYearMax); + } + + set + { + VerifyWritable(); + if (value < 99 || value > MaxCalendarYear) + { + throw new ArgumentOutOfRangeException( + nameof(value), + SR.Format(SR.ArgumentOutOfRange_Range, 99, MaxCalendarYear)); + } + twoDigitYearMax = value; + } + } + + + public override int ToFourDigitYear(int year) + { + if (year < 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedNonNegNum); + } + + year = base.ToFourDigitYear(year); + CheckYearRange(year, CurrentEra); + return (year); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/GregorianCalendar.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/GregorianCalendar.cs similarity index 86% rename from external/corert/src/System.Private.CoreLib/src/System/Globalization/GregorianCalendar.cs rename to external/corefx/src/Common/src/CoreLib/System/Globalization/GregorianCalendar.cs index 1accaffb27..16023209ea 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/GregorianCalendar.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/GregorianCalendar.cs @@ -2,22 +2,15 @@ // 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.Threading; using System; using System.Globalization; -using System.Diagnostics.Contracts; +using System.Threading; namespace System.Globalization { // This calendar recognizes two era values: // 0 CurrentEra (AD) // 1 BeforeCurrentEra (BC) - - public class GregorianCalendar : Calendar { /* @@ -26,12 +19,6 @@ namespace System.Globalization public const int ADEra = 1; - - internal const int DatePartYear = 0; - internal const int DatePartDayOfYear = 1; - internal const int DatePartMonth = 2; - internal const int DatePartDay = 3; - // // This is the max Gregorian year can be represented by DateTime class. The limitation // is derived from DateTime class. @@ -111,7 +98,6 @@ namespace System.Globalization SR.Format(SR.ArgumentOutOfRange_Range, GregorianCalendarTypes.Localized, GregorianCalendarTypes.TransliteratedFrench)); } - Contract.EndContractBlock(); this.m_type = type; } @@ -138,7 +124,7 @@ namespace System.Globalization break; default: - throw new ArgumentOutOfRangeException(nameof(m_type), SR.ArgumentOutOfRange_Enum); + throw new ArgumentOutOfRangeException("m_type", SR.ArgumentOutOfRange_Enum); } } } @@ -156,57 +142,6 @@ namespace System.Globalization } - // Returns a given date part of this DateTime. This method is used - // to compute the year, day-of-year, month, or day part. - internal virtual int GetDatePart(long ticks, int part) - { - // n = number of days since 1/1/0001 - int n = (int)(ticks / TicksPerDay); - // y400 = number of whole 400-year periods since 1/1/0001 - int y400 = n / DaysPer400Years; - // n = day number within 400-year period - n -= y400 * DaysPer400Years; - // y100 = number of whole 100-year periods within 400-year period - int y100 = n / DaysPer100Years; - // Last 100-year period has an extra day, so decrement result if 4 - if (y100 == 4) y100 = 3; - // n = day number within 100-year period - n -= y100 * DaysPer100Years; - // y4 = number of whole 4-year periods within 100-year period - int y4 = n / DaysPer4Years; - // n = day number within 4-year period - n -= y4 * DaysPer4Years; - // y1 = number of whole years within 4-year period - int y1 = n / DaysPerYear; - // Last year has an extra day, so decrement result if 4 - if (y1 == 4) y1 = 3; - // If year was requested, compute and return it - if (part == DatePartYear) - { - return (y400 * 400 + y100 * 100 + y4 * 4 + y1 + 1); - } - // n = day number within year - n -= y1 * DaysPerYear; - // If day-of-year was requested, return it - if (part == DatePartDayOfYear) - { - return (n + 1); - } - // Leap year calculation looks different from IsLeapYear since y1, y4, - // and y100 are relative to year 1, not year 0 - bool leapYear = (y1 == 3 && (y4 != 24 || y100 == 3)); - int[] days = leapYear ? DaysToMonth366 : DaysToMonth365; - // All months have less than 32 days, so n >> 5 is a good conservative - // estimate for the month - int m = (n >> 5) + 1; - // m = 1-based month number - while (n >= days[m]) m++; - // If month was requested, return it - if (part == DatePartMonth) return (m); - // Return 1-based day-of-month - return (n - days[m - 1] + 1); - } - /*=================================GetAbsoluteDate========================== **Action: Gets the absolute date for the given Gregorian date. The absolute date means ** the number of days from January 1st, 1 A.D. @@ -277,10 +212,7 @@ namespace System.Globalization -120000, 120000)); } - Contract.EndContractBlock(); - int y = GetDatePart(time.Ticks, DatePartYear); - int m = GetDatePart(time.Ticks, DatePartMonth); - int d = GetDatePart(time.Ticks, DatePartDay); + time.GetDatePart(out int y, out int m, out int d); int i = m - 1 + months; if (i >= 0) { @@ -326,7 +258,7 @@ namespace System.Globalization public override int GetDayOfMonth(DateTime time) { - return (GetDatePart(time.Ticks, DatePartDay)); + return time.Day; } // Returns the day-of-week part of the specified DateTime. The returned value @@ -346,7 +278,7 @@ namespace System.Globalization public override int GetDayOfYear(DateTime time) { - return (GetDatePart(time.Ticks, DatePartDayOfYear)); + return time.DayOfYear; } // Returns the number of days in the month given by the year and @@ -417,7 +349,7 @@ namespace System.Globalization public override int GetMonth(DateTime time) { - return (GetDatePart(time.Ticks, DatePartMonth)); + return time.Month; } // Returns the number of months in the specified year and era. @@ -447,7 +379,23 @@ namespace System.Globalization public override int GetYear(DateTime time) { - return (GetDatePart(time.Ticks, DatePartYear)); + return time.Year; + } + + internal override bool IsValidYear(int year, int era) => year >= 1 && year <= MaxYear; + + internal override bool IsValidDay(int year, int month, int day, int era) + { + if ((era != CurrentEra && era != ADEra) || + year < 1 || year > MaxYear || + month < 1 || month > 12 || + day < 1) + { + return false; + } + + int[] days = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ? DaysToMonth366 : DaysToMonth365; + return day <= (days[month] - days[month - 1]); } // Checks whether a given day in the specified era is a leap day. This method returns true if @@ -461,7 +409,6 @@ namespace System.Globalization throw new ArgumentOutOfRangeException(nameof(month), SR.Format(SR.ArgumentOutOfRange_Range, 1, 12)); } - Contract.EndContractBlock(); if (era != CurrentEra && era != ADEra) { @@ -508,7 +455,6 @@ namespace System.Globalization CultureInfo.CurrentCulture, SR.ArgumentOutOfRange_Range, 1, MaxYear)); } - Contract.EndContractBlock(); return (0); } @@ -537,7 +483,6 @@ namespace System.Globalization throw new ArgumentOutOfRangeException(nameof(month), SR.Format(SR.ArgumentOutOfRange_Range, 1, 12)); } - Contract.EndContractBlock(); return (false); } @@ -638,7 +583,6 @@ namespace System.Globalization throw new ArgumentOutOfRangeException(nameof(year), SR.ArgumentOutOfRange_NeedNonNegNum); } - Contract.EndContractBlock(); if (year > MaxYear) { diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/GregorianCalendarHelper.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/GregorianCalendarHelper.cs similarity index 98% rename from external/corert/src/System.Private.CoreLib/src/System/Globalization/GregorianCalendarHelper.cs rename to external/corefx/src/Common/src/CoreLib/System/Globalization/GregorianCalendarHelper.cs index 35423035ba..2842bd3a16 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/GregorianCalendarHelper.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/GregorianCalendarHelper.cs @@ -3,14 +3,11 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics.Contracts; -using System.Runtime.Serialization; using System.Threading; namespace System.Globalization { // Gregorian Calendars use Era Info - [Serializable] internal class EraInfo { internal int era; // The value of the era. @@ -143,7 +140,6 @@ namespace System.Globalization throw new ArgumentOutOfRangeException(nameof(year), SR.ArgumentOutOfRange_NeedNonNegNum); } - Contract.EndContractBlock(); if (era == Calendar.CurrentEra) { @@ -325,7 +321,6 @@ namespace System.Globalization m_Cal.MinSupportedDateTime, m_Cal.MaxSupportedDateTime)); } - Contract.EndContractBlock(); } // Returns the DateTime resulting from adding the given number of @@ -357,7 +352,6 @@ namespace System.Globalization -120000, 120000)); } - Contract.EndContractBlock(); CheckTicksRange(time.Ticks); int y = GetDatePart(time.Ticks, DatePartYear); @@ -429,7 +423,6 @@ namespace System.Globalization // Returns the number of days in the month given by the year and // month arguments. // - [Pure] public int GetDaysInMonth(int year, int month, int era) { // @@ -557,7 +550,6 @@ namespace System.Globalization 1, GetDaysInMonth(year, month, era))); } - Contract.EndContractBlock(); if (!IsLeapYear(year, era)) { @@ -635,7 +627,6 @@ namespace System.Globalization throw new ArgumentOutOfRangeException(nameof(year), SR.ArgumentOutOfRange_NeedPosNum); } - Contract.EndContractBlock(); if (year < 100) { diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/GregorianCalendarTypes.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/GregorianCalendarTypes.cs new file mode 100644 index 0000000000..46f13b00e0 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/GregorianCalendarTypes.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.Globalization +{ + // Note: The values of the members of this enum must match the coresponding values + // in the CalendarId enum (since we cast between GregorianCalendarTypes and CalendarId). + public enum GregorianCalendarTypes + { + Localized = CalendarId.GREGORIAN, + USEnglish = CalendarId.GREGORIAN_US, + MiddleEastFrench = CalendarId.GREGORIAN_ME_FRENCH, + Arabic = CalendarId.GREGORIAN_ARABIC, + TransliteratedEnglish = CalendarId.GREGORIAN_XLIT_ENGLISH, + TransliteratedFrench = CalendarId.GREGORIAN_XLIT_FRENCH, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/HebrewCalendar.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/HebrewCalendar.cs new file mode 100644 index 0000000000..533d3831bf --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/HebrewCalendar.cs @@ -0,0 +1,1124 @@ +// 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.Diagnostics; + +namespace System.Globalization +{ + //////////////////////////////////////////////////////////////////////////// + // + // Rules for the Hebrew calendar: + // - The Hebrew calendar is both a Lunar (months) and Solar (years) + // calendar, but allows for a week of seven days. + // - Days begin at sunset. + // - Leap Years occur in the 3, 6, 8, 11, 14, 17, & 19th years of a + // 19-year cycle. Year = leap iff ((7y+1) mod 19 < 7). + // - There are 12 months in a common year and 13 months in a leap year. + // - In a common year, the 6th month, Adar, has 29 days. In a leap + // year, the 6th month, Adar I, has 30 days and the leap month, + // Adar II, has 29 days. + // - Common years have 353-355 days. Leap years have 383-385 days. + // - The Hebrew new year (Rosh HaShanah) begins on the 1st of Tishri, + // the 7th month in the list below. + // - The new year may not begin on Sunday, Wednesday, or Friday. + // - If the new year would fall on a Tuesday and the conjunction of + // the following year were at midday or later, the new year is + // delayed until Thursday. + // - If the new year would fall on a Monday after a leap year, the + // new year is delayed until Tuesday. + // - The length of the 8th and 9th months vary from year to year, + // depending on the overall length of the year. + // - The length of a year is determined by the dates of the new + // years (Tishri 1) preceding and following the year in question. + // - The 2th month is long (30 days) if the year has 355 or 385 days. + // - The 3th month is short (29 days) if the year has 353 or 383 days. + // - The Hebrew months are: + // 1. Tishri (30 days) + // 2. Heshvan (29 or 30 days) + // 3. Kislev (29 or 30 days) + // 4. Teveth (29 days) + // 5. Shevat (30 days) + // 6. Adar I (30 days) + // 7. Adar {II} (29 days, this only exists if that year is a leap year) + // 8. Nisan (30 days) + // 9. Iyyar (29 days) + // 10. Sivan (30 days) + // 11. Tammuz (29 days) + // 12. Av (30 days) + // 13. Elul (29 days) + // + //////////////////////////////////////////////////////////////////////////// + /* + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 1583/01/01 2239/09/29 + ** Hebrew 5343/04/07 5999/13/29 + */ + + // Includes CHebrew implemetation;i.e All the code necessary for converting + // Gregorian to Hebrew Lunar from 1583 to 2239. + + + public class HebrewCalendar : Calendar + { + public static readonly int HebrewEra = 1; + + internal const int DatePartYear = 0; + internal const int DatePartDayOfYear = 1; + internal const int DatePartMonth = 2; + internal const int DatePartDay = 3; + internal const int DatePartDayOfWeek = 4; + + // + // Hebrew Translation Table. + // + // This table is used to get the following Hebrew calendar information for a + // given Gregorian year: + // 1. The day of the Hebrew month corresponding to Gregorian January 1st + // for a given Gregorian year. + // 2. The month of the Hebrew month corresponding to Gregorian January 1st + // for a given Gregorian year. + // The information is not directly in the table. Instead, the info is decoded + // by special values (numbers above 29 and below 1). + // 3. The type of the Hebrew year for a given Gregorian year. + // + + /* + More notes: + + This table includes 2 numbers for each year. + The offset into the table determines the year. (offset 0 is Gregorian year 1500) + 1st number determines the day of the Hebrew month coresponeds to January 1st. + 2nd number determines the type of the Hebrew year. (the type determines how + many days are there in the year.) + + normal years : 1 = 353 days 2 = 354 days 3 = 355 days. + Leap years : 4 = 383 5 384 6 = 385 days. + + A 99 means the year is not supported for translation. + for convenience the table was defined for 750 year, + but only 640 years are supported. (from 1583 to 2239) + the years before 1582 (starting of Georgian calander) + and after 2239, are filled with 99. + + Greogrian January 1st falls usually in Tevet (4th month). Tevet has always 29 days. + That's why, there no nead to specify the lunar month in the table. + There are exceptions, these are coded by giving numbers above 29 and below 1. + Actual decoding is takenig place whenever fetching information from the table. + The function for decoding is in GetLunarMonthDay(). + + Example: + The data for 2000 - 2005 A.D. is: + + 23,6,6,1,17,2,27,6,7,3, // 2000 - 2004 + + For year 2000, we know it has a Hebrew year type 6, which means it has 385 days. + And 1/1/2000 A.D. is Hebrew year 5760, 23rd day of 4th month. + */ + + // + // Jewish Era in use today is dated from the supposed year of the + // Creation with its beginning in 3761 B.C. + // + + // The Hebrew year of Gregorian 1st year AD. + // 0001/01/01 AD is Hebrew 3760/01/01 + private const int HebrewYearOf1AD = 3760; + + // The first Gregorian year in HebrewTable. + private const int FirstGregorianTableYear = 1583; // == Hebrew Year 5343 + // The last Gregorian year in HebrewTable. + private const int LastGregorianTableYear = 2239; // == Hebrew Year 5999 + private const int TABLESIZE = (LastGregorianTableYear - FirstGregorianTableYear); + + private const int MinHebrewYear = HebrewYearOf1AD + FirstGregorianTableYear; // == 5343 + private const int MaxHebrewYear = HebrewYearOf1AD + LastGregorianTableYear; // == 5999 + + private static readonly byte[] s_hebrewTable = { + 7,3,17,3, // 1583-1584 (Hebrew year: 5343 - 5344) + 0,4,11,2,21,6,1,3,13,2, // 1585-1589 + 25,4,5,3,16,2,27,6,9,1, // 1590-1594 + 20,2,0,6,11,3,23,4,4,2, // 1595-1599 + 14,3,27,4,8,2,18,3,28,6, // 1600 + 11,1,22,5,2,3,12,3,25,4, // 1605 + 6,2,16,3,26,6,8,2,20,1, // 1610 + 0,6,11,2,24,4,4,3,15,2, // 1615 + 25,6,8,1,19,2,29,6,9,3, // 1620 + 22,4,3,2,13,3,25,4,6,3, // 1625 + 17,2,27,6,7,3,19,2,31,4, // 1630 + 11,3,23,4,5,2,15,3,25,6, // 1635 + 6,2,19,1,29,6,10,2,22,4, // 1640 + 3,3,14,2,24,6,6,1,17,3, // 1645 + 28,5,8,3,20,1,32,5,12,3, // 1650 + 22,6,4,1,16,2,26,6,6,3, // 1655 + 17,2,0,4,10,3,22,4,3,2, // 1660 + 14,3,24,6,5,2,17,1,28,6, // 1665 + 9,2,19,3,31,4,13,2,23,6, // 1670 + 3,3,15,1,27,5,7,3,17,3, // 1675 + 29,4,11,2,21,6,3,1,14,2, // 1680 + 25,6,5,3,16,2,28,4,9,3, // 1685 + 20,2,0,6,12,1,23,6,4,2, // 1690 + 14,3,26,4,8,2,18,3,0,4, // 1695 + 10,3,21,5,1,3,13,1,24,5, // 1700 + 5,3,15,3,27,4,8,2,19,3, // 1705 + 29,6,10,2,22,4,3,3,14,2, // 1710 + 26,4,6,3,18,2,28,6,10,1, // 1715 + 20,6,2,2,12,3,24,4,5,2, // 1720 + 16,3,28,4,8,3,19,2,0,6, // 1725 + 12,1,23,5,3,3,14,3,26,4, // 1730 + 7,2,17,3,28,6,9,2,21,4, // 1735 + 1,3,13,2,25,4,5,3,16,2, // 1740 + 27,6,9,1,19,3,0,5,11,3, // 1745 + 23,4,4,2,14,3,25,6,7,1, // 1750 + 18,2,28,6,9,3,21,4,2,2, // 1755 + 12,3,25,4,6,2,16,3,26,6, // 1760 + 8,2,20,1,0,6,11,2,22,6, // 1765 + 4,1,15,2,25,6,6,3,18,1, // 1770 + 29,5,9,3,22,4,2,3,13,2, // 1775 + 23,6,4,3,15,2,27,4,7,3, // 1780 + 19,2,31,4,11,3,21,6,3,2, // 1785 + 15,1,25,6,6,2,17,3,29,4, // 1790 + 10,2,20,6,3,1,13,3,24,5, // 1795 + 4,3,16,1,27,5,7,3,17,3, // 1800 + 0,4,11,2,21,6,1,3,13,2, // 1805 + 25,4,5,3,16,2,29,4,9,3, // 1810 + 19,6,30,2,13,1,23,6,4,2, // 1815 + 14,3,27,4,8,2,18,3,0,4, // 1820 + 11,3,22,5,2,3,14,1,26,5, // 1825 + 6,3,16,3,28,4,10,2,20,6, // 1830 + 30,3,11,2,24,4,4,3,15,2, // 1835 + 25,6,8,1,19,2,29,6,9,3, // 1840 + 22,4,3,2,13,3,25,4,7,2, // 1845 + 17,3,27,6,9,1,21,5,1,3, // 1850 + 11,3,23,4,5,2,15,3,25,6, // 1855 + 6,2,19,1,29,6,10,2,22,4, // 1860 + 3,3,14,2,24,6,6,1,18,2, // 1865 + 28,6,8,3,20,4,2,2,12,3, // 1870 + 24,4,4,3,16,2,26,6,6,3, // 1875 + 17,2,0,4,10,3,22,4,3,2, // 1880 + 14,3,24,6,5,2,17,1,28,6, // 1885 + 9,2,21,4,1,3,13,2,23,6, // 1890 + 5,1,15,3,27,5,7,3,19,1, // 1895 + 0,5,10,3,22,4,2,3,13,2, // 1900 + 24,6,4,3,15,2,27,4,8,3, // 1905 + 20,4,1,2,11,3,22,6,3,2, // 1910 + 15,1,25,6,7,2,17,3,29,4, // 1915 + 10,2,21,6,1,3,13,1,24,5, // 1920 + 5,3,15,3,27,4,8,2,19,6, // 1925 + 1,1,12,2,22,6,3,3,14,2, // 1930 + 26,4,6,3,18,2,28,6,10,1, // 1935 + 20,6,2,2,12,3,24,4,5,2, // 1940 + 16,3,28,4,9,2,19,6,30,3, // 1945 + 12,1,23,5,3,3,14,3,26,4, // 1950 + 7,2,17,3,28,6,9,2,21,4, // 1955 + 1,3,13,2,25,4,5,3,16,2, // 1960 + 27,6,9,1,19,6,30,2,11,3, // 1965 + 23,4,4,2,14,3,27,4,7,3, // 1970 + 18,2,28,6,11,1,22,5,2,3, // 1975 + 12,3,25,4,6,2,16,3,26,6, // 1980 + 8,2,20,4,30,3,11,2,24,4, // 1985 + 4,3,15,2,25,6,8,1,18,3, // 1990 + 29,5,9,3,22,4,3,2,13,3, // 1995 + 23,6,6,1,17,2,27,6,7,3, // 2000 - 2004 + 20,4,1,2,11,3,23,4,5,2, // 2005 - 2009 + 15,3,25,6,6,2,19,1,29,6, // 2010 + 10,2,20,6,3,1,14,2,24,6, // 2015 + 4,3,17,1,28,5,8,3,20,4, // 2020 + 1,3,12,2,22,6,2,3,14,2, // 2025 + 26,4,6,3,17,2,0,4,10,3, // 2030 + 20,6,1,2,14,1,24,6,5,2, // 2035 + 15,3,28,4,9,2,19,6,1,1, // 2040 + 12,3,23,5,3,3,15,1,27,5, // 2045 + 7,3,17,3,29,4,11,2,21,6, // 2050 + 1,3,12,2,25,4,5,3,16,2, // 2055 + 28,4,9,3,19,6,30,2,12,1, // 2060 + 23,6,4,2,14,3,26,4,8,2, // 2065 + 18,3,0,4,10,3,22,5,2,3, // 2070 + 14,1,25,5,6,3,16,3,28,4, // 2075 + 9,2,20,6,30,3,11,2,23,4, // 2080 + 4,3,15,2,27,4,7,3,19,2, // 2085 + 29,6,11,1,21,6,3,2,13,3, // 2090 + 25,4,6,2,17,3,27,6,9,1, // 2095 + 20,5,30,3,10,3,22,4,3,2, // 2100 + 14,3,24,6,5,2,17,1,28,6, // 2105 + 9,2,21,4,1,3,13,2,23,6, // 2110 + 5,1,16,2,27,6,7,3,19,4, // 2115 + 30,2,11,3,23,4,3,3,14,2, // 2120 + 25,6,5,3,16,2,28,4,9,3, // 2125 + 21,4,2,2,12,3,23,6,4,2, // 2130 + 16,1,26,6,8,2,20,4,30,3, // 2135 + 11,2,22,6,4,1,14,3,25,5, // 2140 + 6,3,18,1,29,5,9,3,22,4, // 2145 + 2,3,13,2,23,6,4,3,15,2, // 2150 + 27,4,7,3,20,4,1,2,11,3, // 2155 + 21,6,3,2,15,1,25,6,6,2, // 2160 + 17,3,29,4,10,2,20,6,3,1, // 2165 + 13,3,24,5,4,3,17,1,28,5, // 2170 + 8,3,18,6,1,1,12,2,22,6, // 2175 + 2,3,14,2,26,4,6,3,17,2, // 2180 + 28,6,10,1,20,6,1,2,12,3, // 2185 + 24,4,5,2,15,3,28,4,9,2, // 2190 + 19,6,33,3,12,1,23,5,3,3, // 2195 + 13,3,25,4,6,2,16,3,26,6, // 2200 + 8,2,20,4,30,3,11,2,24,4, // 2205 + 4,3,15,2,25,6,8,1,18,6, // 2210 + 33,2,9,3,22,4,3,2,13,3, // 2215 + 25,4,6,3,17,2,27,6,9,1, // 2220 + 21,5,1,3,11,3,23,4,5,2, // 2225 + 15,3,25,6,6,2,19,4,33,3, // 2230 + 10,2,22,4,3,3,14,2,24,6, // 2235 + 6,1 // 2240 (Hebrew year: 6000) + }; + + private const int MaxMonthPlusOne = 14; + + // + // The lunar calendar has 6 different variations of month lengths + // within a year. + // + private static readonly byte[] s_lunarMonthLen = { + 0,00,00,00,00,00,00,00,00,00,00,00,00,0, + 0,30,29,29,29,30,29,30,29,30,29,30,29,0, // 3 common year variations + 0,30,29,30,29,30,29,30,29,30,29,30,29,0, + 0,30,30,30,29,30,29,30,29,30,29,30,29,0, + 0,30,29,29,29,30,30,29,30,29,30,29,30,29, // 3 leap year variations + 0,30,29,30,29,30,30,29,30,29,30,29,30,29, + 0,30,30,30,29,30,30,29,30,29,30,29,30,29 + }; + + internal static readonly DateTime calendarMinValue = new DateTime(1583, 1, 1); + // Gregorian 2239/9/29 = Hebrew 5999/13/29 (last day in Hebrew year 5999). + // We can only format/parse Hebrew numbers up to 999, so we limit the max range to Hebrew year 5999. + internal static readonly DateTime calendarMaxValue = new DateTime((new DateTime(2239, 9, 29, 23, 59, 59, 999)).Ticks + 9999); + + + + public override DateTime MinSupportedDateTime + { + get + { + return (calendarMinValue); + } + } + + + + public override DateTime MaxSupportedDateTime + { + get + { + return (calendarMaxValue); + } + } + + public override CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.LunisolarCalendar; + } + } + + public HebrewCalendar() + { + } + + internal override CalendarId ID + { + get + { + return (CalendarId.HEBREW); + } + } + + + /*=================================CheckHebrewYearValue========================== + **Action: Check if the Hebrew year value is supported in this class. + **Returns: None. + **Arguments: y Hebrew year value + ** ear Hebrew era value + **Exceptions: ArgumentOutOfRange_Range if the year value is not supported. + **Note: + ** We use a table for the Hebrew calendar calculation, so the year supported is limited. + ============================================================================*/ + + private static void CheckHebrewYearValue(int y, int era, String varName) + { + CheckEraRange(era); + if (y > MaxHebrewYear || y < MinHebrewYear) + { + throw new ArgumentOutOfRangeException( + varName, + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + MinHebrewYear, + MaxHebrewYear)); + } + } + + /*=================================CheckHebrewMonthValue========================== + **Action: Check if the Hebrew month value is valid. + **Returns: None. + **Arguments: year Hebrew year value + ** month Hebrew month value + **Exceptions: ArgumentOutOfRange_Range if the month value is not valid. + **Note: + ** Call CheckHebrewYearValue() before calling this to verify the year value is supported. + ============================================================================*/ + + private void CheckHebrewMonthValue(int year, int month, int era) + { + int monthsInYear = GetMonthsInYear(year, era); + if (month < 1 || month > monthsInYear) + { + throw new ArgumentOutOfRangeException( + nameof(month), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + monthsInYear)); + } + } + + /*=================================CheckHebrewDayValue========================== + **Action: Check if the Hebrew day value is valid. + **Returns: None. + **Arguments: year Hebrew year value + ** month Hebrew month value + ** day Hebrew day value. + **Exceptions: ArgumentOutOfRange_Range if the day value is not valid. + **Note: + ** Call CheckHebrewYearValue()/CheckHebrewMonthValue() before calling this to verify the year/month values are valid. + ============================================================================*/ + + private void CheckHebrewDayValue(int year, int month, int day, int era) + { + int daysInMonth = GetDaysInMonth(year, month, era); + if (day < 1 || day > daysInMonth) + { + throw new ArgumentOutOfRangeException( + nameof(day), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + daysInMonth)); + } + } + + internal static void CheckEraRange(int era) + { + if (era != CurrentEra && era != HebrewEra) + { + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + } + + private static void CheckTicksRange(long ticks) + { + if (ticks < calendarMinValue.Ticks || ticks > calendarMaxValue.Ticks) + { + throw new ArgumentOutOfRangeException( + "time", + // Print out the date in Gregorian using InvariantCulture since the DateTime is based on GreograinCalendar. + String.Format( + CultureInfo.InvariantCulture, + SR.ArgumentOutOfRange_CalendarRange, + calendarMinValue, + calendarMaxValue)); + } + } + + internal static int GetResult(__DateBuffer result, int part) + { + switch (part) + { + case DatePartYear: + return (result.year); + case DatePartMonth: + return (result.month); + case DatePartDay: + return (result.day); + } + + throw new InvalidOperationException(SR.InvalidOperation_DateTimeParsing); + } + + /*=================================GetLunarMonthDay========================== + **Action: Using the Hebrew table (HebrewTable) to get the Hebrew month/day value for Gregorian January 1st + ** in a given Gregorian year. + ** Greogrian January 1st falls usually in Tevet (4th month). Tevet has always 29 days. + ** That's why, there no nead to specify the lunar month in the table. There are exceptions, and these + ** are coded by giving numbers above 29 and below 1. + ** Actual decoding is takenig place in the switch statement below. + **Returns: + ** The Hebrew year type. The value is from 1 to 6. + ** normal years : 1 = 353 days 2 = 354 days 3 = 355 days. + ** Leap years : 4 = 383 5 384 6 = 385 days. + **Arguments: + ** gregorianYear The year value in Gregorian calendar. The value should be between 1500 and 2239. + ** lunarDate Object to take the result of the Hebrew year/month/day. + **Exceptions: + ============================================================================*/ + + internal static int GetLunarMonthDay(int gregorianYear, __DateBuffer lunarDate) + { + // + // Get the offset into the LunarMonthLen array and the lunar day + // for January 1st. + // + int index = gregorianYear - FirstGregorianTableYear; + if (index < 0 || index > TABLESIZE) + { + throw new ArgumentOutOfRangeException(nameof(gregorianYear)); + } + + index *= 2; + lunarDate.day = s_hebrewTable[index]; + + // Get the type of the year. The value is from 1 to 6 + int LunarYearType = s_hebrewTable[index + 1]; + + // + // Get the Lunar Month. + // + switch (lunarDate.day) + { + case (0): // 1/1 is on Shvat 1 + lunarDate.month = 5; + lunarDate.day = 1; + break; + case (30): // 1/1 is on Kislev 30 + lunarDate.month = 3; + break; + case (31): // 1/1 is on Shvat 2 + lunarDate.month = 5; + lunarDate.day = 2; + break; + case (32): // 1/1 is on Shvat 3 + lunarDate.month = 5; + lunarDate.day = 3; + break; + case (33): // 1/1 is on Kislev 29 + lunarDate.month = 3; + lunarDate.day = 29; + break; + default: // 1/1 is on Tevet (This is the general case) + lunarDate.month = 4; + break; + } + return (LunarYearType); + } + + // Returns a given date part of this DateTime. This method is used + // to compute the year, day-of-year, month, or day part. + + internal virtual int GetDatePart(long ticks, int part) + { + // The Gregorian year, month, day value for ticks. + int gregorianYear, gregorianMonth, gregorianDay; + int hebrewYearType; // lunar year type + long AbsoluteDate; // absolute date - absolute date 1/1/1600 + + // + // Make sure we have a valid Gregorian date that will fit into our + // Hebrew conversion limits. + // + CheckTicksRange(ticks); + + DateTime time = new DateTime(ticks); + + // + // Save the Gregorian date values. + // + time.GetDatePart(out gregorianYear, out gregorianMonth, out gregorianDay); + + __DateBuffer lunarDate = new __DateBuffer(); // lunar month and day for Jan 1 + + // From the table looking-up value of HebrewTable[index] (stored in lunarDate.day), we get the the + // lunar month and lunar day where the Gregorian date 1/1 falls. + lunarDate.year = gregorianYear + HebrewYearOf1AD; + hebrewYearType = GetLunarMonthDay(gregorianYear, lunarDate); + + // This is the buffer used to store the result Hebrew date. + __DateBuffer result = new __DateBuffer(); + + // + // Store the values for the start of the new year - 1/1. + // + result.year = lunarDate.year; + result.month = lunarDate.month; + result.day = lunarDate.day; + + // + // Get the absolute date from 1/1/1600. + // + AbsoluteDate = GregorianCalendar.GetAbsoluteDate(gregorianYear, gregorianMonth, gregorianDay); + + // + // If the requested date was 1/1, then we're done. + // + if ((gregorianMonth == 1) && (gregorianDay == 1)) + { + return (GetResult(result, part)); + } + + // + // Calculate the number of days between 1/1 and the requested date. + // + long NumDays; // number of days since 1/1 + NumDays = AbsoluteDate - GregorianCalendar.GetAbsoluteDate(gregorianYear, 1, 1); + + // + // If the requested date is within the current lunar month, then + // we're done. + // + if ((NumDays + (long)lunarDate.day) <= (long)(s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + lunarDate.month])) + { + result.day += (int)NumDays; + return (GetResult(result, part)); + } + + // + // Adjust for the current partial month. + // + result.month++; + result.day = 1; + + // + // Adjust the Lunar Month and Year (if necessary) based on the number + // of days between 1/1 and the requested date. + // + // Assumes Jan 1 can never translate to the last Lunar month, which + // is true. + // + NumDays -= (long)(s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + lunarDate.month] - lunarDate.day); + Debug.Assert(NumDays >= 1, "NumDays >= 1"); + + // If NumDays is 1, then we are done. Otherwise, find the correct Hebrew month + // and day. + if (NumDays > 1) + { + // + // See if we're on the correct Lunar month. + // + while (NumDays > (long)(s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + result.month])) + { + // + // Adjust the number of days and move to the next month. + // + NumDays -= (long)(s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + result.month++]); + + // + // See if we need to adjust the Year. + // Must handle both 12 and 13 month years. + // + if ((result.month > 13) || (s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + result.month] == 0)) + { + // + // Adjust the Year. + // + result.year++; + hebrewYearType = s_hebrewTable[(gregorianYear + 1 - FirstGregorianTableYear) * 2 + 1]; + + // + // Adjust the Month. + // + result.month = 1; + } + } + // + // Found the right Lunar month. + // + result.day += (int)(NumDays - 1); + } + return (GetResult(result, part)); + } + + // Returns the DateTime resulting from adding the given number of + // months to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year and month parts of the specified DateTime by + // value months, and, if required, adjusting the day part of the + // resulting date downwards to the last day of the resulting month in the + // resulting year. The time-of-day part of the result is the same as the + // time-of-day part of the specified DateTime. + // + // In more precise terms, considering the specified DateTime to be of the + // form y / m / d + t, where y is the + // year, m is the month, d is the day, and t is the + // time-of-day, the result is y1 / m1 / d1 + t, + // where y1 and m1 are computed by adding value months + // to y and m, and d1 is the largest value less than + // or equal to d that denotes a valid day in month m1 of year + // y1. + // + + public override DateTime AddMonths(DateTime time, int months) + { + try + { + int y = GetDatePart(time.Ticks, DatePartYear); + int m = GetDatePart(time.Ticks, DatePartMonth); + int d = GetDatePart(time.Ticks, DatePartDay); + + + int monthsInYear; + int i; + if (months >= 0) + { + i = m + months; + while (i > (monthsInYear = GetMonthsInYear(y, CurrentEra))) + { + y++; + i -= monthsInYear; + } + } + else + { + if ((i = m + months) <= 0) + { + months = -months; + months -= m; + y--; + + while (months > (monthsInYear = GetMonthsInYear(y, CurrentEra))) + { + y--; + months -= monthsInYear; + } + monthsInYear = GetMonthsInYear(y, CurrentEra); + i = monthsInYear - months; + } + } + + int days = GetDaysInMonth(y, i); + if (d > days) + { + d = days; + } + return (new DateTime(ToDateTime(y, i, d, 0, 0, 0, 0).Ticks + (time.Ticks % TicksPerDay))); + } + // We expect ArgumentException and ArgumentOutOfRangeException (which is subclass of ArgumentException) + // If exception is thrown in the calls above, we are out of the supported range of this calendar. + catch (ArgumentException) + { + throw new ArgumentOutOfRangeException( + nameof(months), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_AddValue)); + } + } + + // Returns the DateTime resulting from adding the given number of + // years to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year part of the specified DateTime by value + // years. If the month and day of the specified DateTime is 2/29, and if the + // resulting year is not a leap year, the month and day of the resulting + // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day + // parts of the result are the same as those of the specified DateTime. + // + + public override DateTime AddYears(DateTime time, int years) + { + int y = GetDatePart(time.Ticks, DatePartYear); + int m = GetDatePart(time.Ticks, DatePartMonth); + int d = GetDatePart(time.Ticks, DatePartDay); + + y += years; + CheckHebrewYearValue(y, Calendar.CurrentEra, nameof(years)); + + int months = GetMonthsInYear(y, CurrentEra); + if (m > months) + { + m = months; + } + + int days = GetDaysInMonth(y, m); + if (d > days) + { + d = days; + } + + long ticks = ToDateTime(y, m, d, 0, 0, 0, 0).Ticks + (time.Ticks % TicksPerDay); + Calendar.CheckAddResult(ticks, MinSupportedDateTime, MaxSupportedDateTime); + return (new DateTime(ticks)); + } + + // Returns the day-of-month part of the specified DateTime. The returned + // value is an integer between 1 and 31. + // + + public override int GetDayOfMonth(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartDay)); + } + + // Returns the day-of-week part of the specified DateTime. The returned value + // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates + // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates + // Thursday, 5 indicates Friday, and 6 indicates Saturday. + // + + public override DayOfWeek GetDayOfWeek(DateTime time) + { + // If we calculate back, the Hebrew day of week for Gregorian 0001/1/1 is Monday (1). + // Therfore, the fomula is: + return ((DayOfWeek)((int)(time.Ticks / TicksPerDay + 1) % 7)); + } + + internal static int GetHebrewYearType(int year, int era) + { + CheckHebrewYearValue(year, era, nameof(year)); + // The HebrewTable is indexed by Gregorian year and starts from FirstGregorianYear. + // So we need to convert year (Hebrew year value) to Gregorian Year below. + return (s_hebrewTable[(year - HebrewYearOf1AD - FirstGregorianTableYear) * 2 + 1]); + } + + // Returns the day-of-year part of the specified DateTime. The returned value + // is an integer between 1 and 366. + // + + public override int GetDayOfYear(DateTime time) + { + // Get Hebrew year value of the specified time. + int year = GetYear(time); + DateTime beginOfYearDate; + if (year == 5343) + { + // Gregorian 1583/01/01 corresponds to Hebrew 5343/04/07 (MinSupportedDateTime) + // To figure out the Gregorian date associated with Hebrew 5343/01/01, we need to + // count the days from 5343/01/01 to 5343/04/07 and subtract that from Gregorian + // 1583/01/01. + // 1. Tishri (30 days) + // 2. Heshvan (30 days since 5343 has 355 days) + // 3. Kislev (30 days since 5343 has 355 days) + // 96 days to get from 5343/01/01 to 5343/04/07 + // Gregorian 1583/01/01 - 96 days = 1582/9/27 + + // the beginning of Hebrew year 5343 corresponds to Gregorian September 27, 1582. + beginOfYearDate = new DateTime(1582, 9, 27); + } + else + { + // following line will fail when year is 5343 (first supported year) + beginOfYearDate = ToDateTime(year, 1, 1, 0, 0, 0, 0, CurrentEra); + } + return ((int)((time.Ticks - beginOfYearDate.Ticks) / TicksPerDay) + 1); + } + + // Returns the number of days in the month given by the year and + // month arguments. + // + + public override int GetDaysInMonth(int year, int month, int era) + { + CheckEraRange(era); + int hebrewYearType = GetHebrewYearType(year, era); + CheckHebrewMonthValue(year, month, era); + + Debug.Assert(hebrewYearType >= 1 && hebrewYearType <= 6, + "hebrewYearType should be from 1 to 6, but now hebrewYearType = " + hebrewYearType + " for hebrew year " + year); + int monthDays = s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + month]; + if (monthDays == 0) + { + throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month); + } + return (monthDays); + } + + // Returns the number of days in the year given by the year argument for the current era. + // + + public override int GetDaysInYear(int year, int era) + { + CheckEraRange(era); + // normal years : 1 = 353 days 2 = 354 days 3 = 355 days. + // Leap years : 4 = 383 5 384 6 = 385 days. + + // LunarYearType is from 1 to 6 + int LunarYearType = GetHebrewYearType(year, era); + if (LunarYearType < 4) + { + // common year: LunarYearType = 1, 2, 3 + return (352 + LunarYearType); + } + return (382 + (LunarYearType - 3)); + } + + // Returns the era for the specified DateTime value. + + public override int GetEra(DateTime time) + { + return (HebrewEra); + } + + + public override int[] Eras + { + get + { + return (new int[] { HebrewEra }); + } + } + + // Returns the month part of the specified DateTime. The returned value is an + // integer between 1 and 12. + // + + public override int GetMonth(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartMonth)); + } + + // Returns the number of months in the specified year and era. + + public override int GetMonthsInYear(int year, int era) + { + return (IsLeapYear(year, era) ? 13 : 12); + } + + // Returns the year part of the specified DateTime. The returned value is an + // integer between 1 and 9999. + // + + public override int GetYear(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartYear)); + } + + // Checks whether a given day in the specified era is a leap day. This method returns true if + // the date is a leap day, or false if not. + // + + public override bool IsLeapDay(int year, int month, int day, int era) + { + if (IsLeapMonth(year, month, era)) + { + // Every day in a leap month is a leap day. + CheckHebrewDayValue(year, month, day, era); + return (true); + } + else if (IsLeapYear(year, Calendar.CurrentEra)) + { + // There is an additional day in the 6th month in the leap year (the extra day is the 30th day in the 6th month), + // so we should return true for 6/30 if that's in a leap year. + if (month == 6 && day == 30) + { + return (true); + } + } + CheckHebrewDayValue(year, month, day, era); + return (false); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this calendar does not have leap month, or this year is not a leap year. + // + + + public override int GetLeapMonth(int year, int era) + { + // Year/era values are checked in IsLeapYear(). + if (IsLeapYear(year, era)) + { + // The 7th month in a leap year is a leap month. + return (7); + } + return (0); + } + + // Checks whether a given month in the specified era is a leap month. This method returns true if + // month is a leap month, or false if not. + // + + public override bool IsLeapMonth(int year, int month, int era) + { + // Year/era values are checked in IsLeapYear(). + bool isLeapYear = IsLeapYear(year, era); + CheckHebrewMonthValue(year, month, era); + // The 7th month in a leap year is a leap month. + if (isLeapYear) + { + if (month == 7) + { + return (true); + } + } + return (false); + } + + // Checks whether a given year in the specified era is a leap year. This method returns true if + // year is a leap year, or false if not. + // + + public override bool IsLeapYear(int year, int era) + { + CheckHebrewYearValue(year, era, nameof(year)); + return (((7 * (long)year + 1) % 19) < 7); + } + + // (month1, day1) - (month2, day2) + private static int GetDayDifference(int lunarYearType, int month1, int day1, int month2, int day2) + { + if (month1 == month2) + { + return (day1 - day2); + } + + // Make sure that (month1, day1) < (month2, day2) + bool swap = (month1 > month2); + if (swap) + { + // (month1, day1) < (month2, day2). Swap the values. + // The result will be a negative number. + int tempMonth, tempDay; + tempMonth = month1; tempDay = day1; + month1 = month2; day1 = day2; + month2 = tempMonth; day2 = tempDay; + } + + // Get the number of days from (month1,day1) to (month1, end of month1) + int days = s_lunarMonthLen[lunarYearType * MaxMonthPlusOne + month1] - day1; + + // Move to next month. + month1++; + + // Add up the days. + while (month1 < month2) + { + days += s_lunarMonthLen[lunarYearType * MaxMonthPlusOne + month1++]; + } + days += day2; + + return (swap ? days : -days); + } + + /*=================================HebrewToGregorian========================== + **Action: Convert Hebrew date to Gregorian date. + **Returns: + **Arguments: + **Exceptions: + ** The algorithm is like this: + ** The hebrew year has an offset to the Gregorian year, so we can guess the Gregorian year for + ** the specified Hebrew year. That is, GreogrianYear = HebrewYear - FirstHebrewYearOf1AD. + ** + ** From the Gregorian year and HebrewTable, we can get the Hebrew month/day value + ** of the Gregorian date January 1st. Let's call this month/day value [hebrewDateForJan1] + ** + ** If the requested Hebrew month/day is less than [hebrewDateForJan1], we know the result + ** Gregorian date falls in previous year. So we decrease the Gregorian year value, and + ** retrieve the Hebrew month/day value of the Gregorian date january 1st again. + ** + ** Now, we get the answer of the Gregorian year. + ** + ** The next step is to get the number of days between the requested Hebrew month/day + ** and [hebrewDateForJan1]. When we get that, we can create the DateTime by adding/subtracting + ** the ticks value of the number of days. + ** + ============================================================================*/ + + + private static DateTime HebrewToGregorian(int hebrewYear, int hebrewMonth, int hebrewDay, int hour, int minute, int second, int millisecond) + { + // Get the rough Gregorian year for the specified hebrewYear. + // + int gregorianYear = hebrewYear - HebrewYearOf1AD; + + __DateBuffer hebrewDateOfJan1 = new __DateBuffer(); // year value is unused. + int lunarYearType = GetLunarMonthDay(gregorianYear, hebrewDateOfJan1); + + if ((hebrewMonth == hebrewDateOfJan1.month) && (hebrewDay == hebrewDateOfJan1.day)) + { + return (new DateTime(gregorianYear, 1, 1, hour, minute, second, millisecond)); + } + + int days = GetDayDifference(lunarYearType, hebrewMonth, hebrewDay, hebrewDateOfJan1.month, hebrewDateOfJan1.day); + + DateTime gregorianNewYear = new DateTime(gregorianYear, 1, 1); + return (new DateTime(gregorianNewYear.Ticks + days * TicksPerDay + + TimeToTicks(hour, minute, second, millisecond))); + } + + // Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid. + // + + public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) + { + CheckHebrewYearValue(year, era, nameof(year)); + CheckHebrewMonthValue(year, month, era); + CheckHebrewDayValue(year, month, day, era); + DateTime dt = HebrewToGregorian(year, month, day, hour, minute, second, millisecond); + CheckTicksRange(dt.Ticks); + return (dt); + } + + private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 5790; + + + public override int TwoDigitYearMax + { + get + { + if (twoDigitYearMax == -1) + { + twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX); + } + return (twoDigitYearMax); + } + + set + { + VerifyWritable(); + if (value == 99) + { + // Do nothing here. Year 99 is allowed so that TwoDitYearMax is disabled. + } + else + { + CheckHebrewYearValue(value, HebrewEra, nameof(value)); + } + twoDigitYearMax = value; + } + } + + + public override int ToFourDigitYear(int year) + { + if (year < 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedNonNegNum); + } + + if (year < 100) + { + return (base.ToFourDigitYear(year)); + } + + if (year > MaxHebrewYear || year < MinHebrewYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + MinHebrewYear, + MaxHebrewYear)); + } + return (year); + } + + internal class __DateBuffer + { + internal int year; + internal int month; + internal int day; + } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/HebrewNumber.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/HebrewNumber.cs new file mode 100644 index 0000000000..1e8fff2bcb --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/HebrewNumber.cs @@ -0,0 +1,457 @@ +// 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.Text; +using System.Diagnostics; + +namespace System.Globalization +{ + //////////////////////////////////////////////////////////////////////////// + // + // Used in HebrewNumber.ParseByChar to maintain the context information ( + // the state in the state machine and current Hebrew number values, etc.) + // when parsing Hebrew number character by character. + // + //////////////////////////////////////////////////////////////////////////// + + internal struct HebrewNumberParsingContext + { + // The current state of the state machine for parsing Hebrew numbers. + internal HebrewNumber.HS state; + // The current value of the Hebrew number. + // The final value is determined when state is FoundEndOfHebrewNumber. + internal int result; + + public HebrewNumberParsingContext(int result) + { + // Set the start state of the state machine for parsing Hebrew numbers. + state = HebrewNumber.HS.Start; + this.result = result; + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Please see ParseByChar() for comments about different states defined here. + // + //////////////////////////////////////////////////////////////////////////// + + internal enum HebrewNumberParsingState + { + InvalidHebrewNumber, + NotHebrewDigit, + FoundEndOfHebrewNumber, + ContinueParsing, + } + + //////////////////////////////////////////////////////////////////////////// + // + // class HebrewNumber + // + // Provides static methods for formatting integer values into + // Hebrew text and parsing Hebrew number text. + // + // Limitations: + // Parse can only handles value 1 ~ 999. + // ToString() can only handles 1 ~ 999. If value is greater than 5000, + // 5000 will be subtracted from the value. + // + //////////////////////////////////////////////////////////////////////////// + + internal class HebrewNumber + { + // This class contains only static methods. Add a private ctor so that + // compiler won't generate a default one for us. + private HebrewNumber() + { + } + + //////////////////////////////////////////////////////////////////////////// + // + // ToString + // + // Converts the given number to Hebrew letters according to the numeric + // value of each Hebrew letter. Basically, this converts the lunar year + // and the lunar month to letters. + // + // The character of a year is described by three letters of the Hebrew + // alphabet, the first and third giving, respectively, the days of the + // weeks on which the New Year occurs and Passover begins, while the + // second is the initial of the Hebrew word for defective, normal, or + // complete. + // + // Defective Year : Both Heshvan and Kislev are defective (353 or 383 days) + // Normal Year : Heshvan is defective, Kislev is full (354 or 384 days) + // Complete Year : Both Heshvan and Kislev are full (355 or 385 days) + // + //////////////////////////////////////////////////////////////////////////// + + internal static String ToString(int Number) + { + char cTens = '\x0'; + char cUnits; // tens and units chars + int Hundreds, Tens; // hundreds and tens values + StringBuilder szHebrew = new StringBuilder(); + + + // + // Adjust the number if greater than 5000. + // + if (Number > 5000) + { + Number -= 5000; + } + + Debug.Assert(Number > 0 && Number <= 999, "Number is out of range."); ; + + // + // Get the Hundreds. + // + Hundreds = Number / 100; + + if (Hundreds > 0) + { + Number -= Hundreds * 100; + // \x05e7 = 100 + // \x05e8 = 200 + // \x05e9 = 300 + // \x05ea = 400 + // If the number is greater than 400, use the multiples of 400. + for (int i = 0; i < (Hundreds / 4); i++) + { + szHebrew.Append('\x05ea'); + } + + int remains = Hundreds % 4; + if (remains > 0) + { + szHebrew.Append((char)((int)'\x05e6' + remains)); + } + } + + // + // Get the Tens. + // + Tens = Number / 10; + Number %= 10; + + switch (Tens) + { + case (0): + cTens = '\x0'; + break; + case (1): + cTens = '\x05d9'; // Hebrew Letter Yod + break; + case (2): + cTens = '\x05db'; // Hebrew Letter Kaf + break; + case (3): + cTens = '\x05dc'; // Hebrew Letter Lamed + break; + case (4): + cTens = '\x05de'; // Hebrew Letter Mem + break; + case (5): + cTens = '\x05e0'; // Hebrew Letter Nun + break; + case (6): + cTens = '\x05e1'; // Hebrew Letter Samekh + break; + case (7): + cTens = '\x05e2'; // Hebrew Letter Ayin + break; + case (8): + cTens = '\x05e4'; // Hebrew Letter Pe + break; + case (9): + cTens = '\x05e6'; // Hebrew Letter Tsadi + break; + } + + // + // Get the Units. + // + cUnits = (char)(Number > 0 ? ((int)'\x05d0' + Number - 1) : 0); + + if ((cUnits == '\x05d4') && // Hebrew Letter He (5) + (cTens == '\x05d9')) + { // Hebrew Letter Yod (10) + cUnits = '\x05d5'; // Hebrew Letter Vav (6) + cTens = '\x05d8'; // Hebrew Letter Tet (9) + } + + if ((cUnits == '\x05d5') && // Hebrew Letter Vav (6) + (cTens == '\x05d9')) + { // Hebrew Letter Yod (10) + cUnits = '\x05d6'; // Hebrew Letter Zayin (7) + cTens = '\x05d8'; // Hebrew Letter Tet (9) + } + + // + // Copy the appropriate info to the given buffer. + // + + if (cTens != '\x0') + { + szHebrew.Append(cTens); + } + + if (cUnits != '\x0') + { + szHebrew.Append(cUnits); + } + + if (szHebrew.Length > 1) + { + szHebrew.Insert(szHebrew.Length - 1, '"'); + } + else + { + szHebrew.Append('\''); + } + + // + // Return success. + // + return (szHebrew.ToString()); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Token used to tokenize a Hebrew word into tokens so that we can use in the + // state machine. + // + //////////////////////////////////////////////////////////////////////////// + + private enum HebrewToken : short + { + Invalid = -1, + Digit400 = 0, + Digit200_300 = 1, + Digit100 = 2, + Digit10 = 3, // 10 ~ 90 + Digit1 = 4, // 1, 2, 3, 4, 5, 8, + Digit6_7 = 5, + Digit7 = 6, + Digit9 = 7, + SingleQuote = 8, + DoubleQuote = 9, + }; + + //////////////////////////////////////////////////////////////////////////// + // + // This class is used to map a token into its Hebrew digit value. + // + //////////////////////////////////////////////////////////////////////////// + + private struct HebrewValue + { + internal HebrewToken token; + internal short value; + internal HebrewValue(HebrewToken token, short value) + { + this.token = token; + this.value = value; + } + } + + // + // Map a Hebrew character from U+05D0 ~ U+05EA to its digit value. + // The value is -1 if the Hebrew character does not have a associated value. + // + private static readonly HebrewValue[] s_hebrewValues = { + new HebrewValue(HebrewToken.Digit1, 1) , // '\x05d0 + new HebrewValue(HebrewToken.Digit1, 2) , // '\x05d1 + new HebrewValue(HebrewToken.Digit1, 3) , // '\x05d2 + new HebrewValue(HebrewToken.Digit1, 4) , // '\x05d3 + new HebrewValue(HebrewToken.Digit1, 5) , // '\x05d4 + new HebrewValue(HebrewToken.Digit6_7,6) , // '\x05d5 + new HebrewValue(HebrewToken.Digit6_7,7) , // '\x05d6 + new HebrewValue(HebrewToken.Digit1, 8) , // '\x05d7 + new HebrewValue(HebrewToken.Digit9, 9) , // '\x05d8 + new HebrewValue(HebrewToken.Digit10, 10) , // '\x05d9; // Hebrew Letter Yod + new HebrewValue(HebrewToken.Invalid, -1) , // '\x05da; + new HebrewValue(HebrewToken.Digit10, 20) , // '\x05db; // Hebrew Letter Kaf + new HebrewValue(HebrewToken.Digit10, 30) , // '\x05dc; // Hebrew Letter Lamed + new HebrewValue(HebrewToken.Invalid, -1) , // '\x05dd; + new HebrewValue(HebrewToken.Digit10, 40) , // '\x05de; // Hebrew Letter Mem + new HebrewValue(HebrewToken.Invalid, -1) , // '\x05df; + new HebrewValue(HebrewToken.Digit10, 50) , // '\x05e0; // Hebrew Letter Nun + new HebrewValue(HebrewToken.Digit10, 60) , // '\x05e1; // Hebrew Letter Samekh + new HebrewValue(HebrewToken.Digit10, 70) , // '\x05e2; // Hebrew Letter Ayin + new HebrewValue(HebrewToken.Invalid, -1) , // '\x05e3; + new HebrewValue(HebrewToken.Digit10, 80) , // '\x05e4; // Hebrew Letter Pe + new HebrewValue(HebrewToken.Invalid, -1) , // '\x05e5; + new HebrewValue(HebrewToken.Digit10, 90) , // '\x05e6; // Hebrew Letter Tsadi + new HebrewValue(HebrewToken.Digit100, 100) , // '\x05e7; + new HebrewValue(HebrewToken.Digit200_300, 200) , // '\x05e8; + new HebrewValue(HebrewToken.Digit200_300, 300) , // '\x05e9; + new HebrewValue(HebrewToken.Digit400, 400) , // '\x05ea; + }; + + private const int minHebrewNumberCh = 0x05d0; + private static char s_maxHebrewNumberCh = (char)(minHebrewNumberCh + s_hebrewValues.Length - 1); + + //////////////////////////////////////////////////////////////////////////// + // + // Hebrew number parsing State + // The current state and the next token will lead to the next state in the state machine. + // DQ = Double Quote + // + //////////////////////////////////////////////////////////////////////////// + + internal enum HS : sbyte + { + _err = -1, // an error state + Start = 0, + S400 = 1, // a Hebrew digit 400 + S400_400 = 2, // Two Hebrew digit 400 + S400_X00 = 3, // Two Hebrew digit 400 and followed by 100 + S400_X0 = 4, // Hebrew digit 400 and followed by 10 ~ 90 + X00_DQ = 5, // A hundred number and followed by a double quote. + S400_X00_X0 = 6, + X0_DQ = 7, // A two-digit number and followed by a double quote. + X = 8, // A single digit Hebrew number. + X0 = 9, // A two-digit Hebrew number + X00 = 10, // A three-digit Hebrew number + S400_DQ = 11, // A Hebrew digit 400 and followed by a double quote. + S400_400_DQ = 12, + S400_400_100 = 13, + S9 = 14, // Hebrew digit 9 + X00_S9 = 15, // A hundered number and followed by a digit 9 + S9_DQ = 16, // Hebrew digit 9 and followed by a double quote + END = 100, // A terminial state is reached. + } + + // + // The state machine for Hebrew number pasing. + // + private static readonly HS[] s_numberPasingState = + { + // 400 300/200 100 90~10 8~1 6, 7, 9, ' " + /* 0 */ + HS.S400, HS.X00, HS.X00, HS.X0, HS.X, HS.X, HS.X, HS.S9, HS._err, HS._err, + /* 1: S400 */ + HS.S400_400, HS.S400_X00, HS.S400_X00, HS.S400_X0, HS._err, HS._err, HS._err, HS.X00_S9 ,HS.END, HS.S400_DQ, + /* 2: S400_400 */ + HS._err, HS._err, HS.S400_400_100,HS.S400_X0, HS._err, HS._err, HS._err, HS.X00_S9 ,HS._err, HS.S400_400_DQ, + /* 3: S400_X00 */ + HS._err, HS._err, HS._err, HS.S400_X00_X0, HS._err, HS._err, HS._err, HS.X00_S9 ,HS._err, HS.X00_DQ, + /* 4: S400_X0 */ + HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.X0_DQ, + /* 5: X00_DQ */ + HS._err, HS._err, HS._err, HS.END, HS.END, HS.END, HS.END, HS.END, HS._err, HS._err, + /* 6: S400_X00_X0 */ + HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.X0_DQ, + /* 7: X0_DQ */ + HS._err, HS._err, HS._err, HS._err, HS.END, HS.END, HS.END, HS.END, HS._err, HS._err, + /* 8: X */ + HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.END, HS._err, + /* 9: X0 */ + HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.END, HS.X0_DQ, + /* 10: X00 */ + HS._err, HS._err, HS._err, HS.S400_X0, HS._err, HS._err, HS._err, HS.X00_S9, HS.END, HS.X00_DQ, + /* 11: S400_DQ */ + HS.END, HS.END, HS.END, HS.END, HS.END, HS.END, HS.END, HS.END, HS._err, HS._err, + /* 12: S400_400_DQ*/ + HS._err, HS._err, HS.END, HS.END, HS.END, HS.END, HS.END, HS.END, HS._err, HS._err, + /* 13: S400_400_100*/ + HS._err, HS._err, HS._err, HS.S400_X00_X0, HS._err, HS._err, HS._err, HS.X00_S9, HS._err, HS.X00_DQ, + /* 14: S9 */ + HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.END, HS.S9_DQ, + /* 15: X00_S9 */ + HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.S9_DQ, + /* 16: S9_DQ */ + HS._err, HS._err, HS._err, HS._err, HS._err, HS.END, HS.END, HS._err, HS._err, HS._err + }; + + // Count of valid HebrewToken, column count in the NumberPasingState array + private const int HebrewTokenCount = 10; + + //////////////////////////////////////////////////////////////////////// + // + // Actions: + // Parse the Hebrew number by passing one character at a time. + // The state between characters are maintained at HebrewNumberPasingContext. + // Returns: + // Return a enum of HebrewNumberParsingState. + // NotHebrewDigit: The specified ch is not a valid Hebrew digit. + // InvalidHebrewNumber: After parsing the specified ch, it will lead into + // an invalid Hebrew number text. + // FoundEndOfHebrewNumber: A terminal state is reached. This means that + // we find a valid Hebrew number text after the specified ch is parsed. + // ContinueParsing: The specified ch is a valid Hebrew digit, and + // it will lead into a valid state in the state machine, we should + // continue to parse incoming characters. + // + //////////////////////////////////////////////////////////////////////// + + internal static HebrewNumberParsingState ParseByChar(char ch, ref HebrewNumberParsingContext context) + { + Debug.Assert(s_numberPasingState.Length == HebrewTokenCount * ((int)HS.S9_DQ + 1)); + + HebrewToken token; + if (ch == '\'') + { + token = HebrewToken.SingleQuote; + } + else if (ch == '\"') + { + token = HebrewToken.DoubleQuote; + } + else + { + int index = (int)ch - minHebrewNumberCh; + if (index >= 0 && index < s_hebrewValues.Length) + { + token = s_hebrewValues[index].token; + if (token == HebrewToken.Invalid) + { + return (HebrewNumberParsingState.NotHebrewDigit); + } + context.result += s_hebrewValues[index].value; + } + else + { + // Not in valid Hebrew digit range. + return (HebrewNumberParsingState.NotHebrewDigit); + } + } + context.state = s_numberPasingState[(int)context.state * (int)HebrewTokenCount + (int)token]; + if (context.state == HS._err) + { + // Invalid Hebrew state. This indicates an incorrect Hebrew number. + return (HebrewNumberParsingState.InvalidHebrewNumber); + } + if (context.state == HS.END) + { + // Reach a terminal state. + return (HebrewNumberParsingState.FoundEndOfHebrewNumber); + } + // We should continue to parse. + return (HebrewNumberParsingState.ContinueParsing); + } + + //////////////////////////////////////////////////////////////////////// + // + // Actions: + // Check if the ch is a valid Hebrew number digit. + // This function will return true if the specified char is a legal Hebrew + // digit character, single quote, or double quote. + // Returns: + // true if the specified character is a valid Hebrew number character. + // + //////////////////////////////////////////////////////////////////////// + + internal static bool IsDigit(char ch) + { + if (ch >= minHebrewNumberCh && ch <= s_maxHebrewNumberCh) + { + return (s_hebrewValues[ch - minHebrewNumberCh].value >= 0); + } + return (ch == '\'' || ch == '\"'); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/HijriCalendar.Unix.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/HijriCalendar.Unix.cs new file mode 100644 index 0000000000..a6e8f73d3e --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/HijriCalendar.Unix.cs @@ -0,0 +1,15 @@ +// 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. + +namespace System.Globalization +{ + public partial class HijriCalendar : Calendar + { + private static int GetHijriDateAdjustment() + { + // this setting is not supported on Unix, so always return 0 + return 0; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/HijriCalendar.Win32.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/HijriCalendar.Win32.cs new file mode 100644 index 0000000000..09b1f20c48 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/HijriCalendar.Win32.cs @@ -0,0 +1,94 @@ +// 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 Microsoft.Win32; + +namespace System.Globalization +{ + public partial class HijriCalendar : Calendar + { + private int GetHijriDateAdjustment() + { + if (_hijriAdvance == Int32.MinValue) + { + // Never been set before. Use the system value from registry. + _hijriAdvance = GetAdvanceHijriDate(); + } + return (_hijriAdvance); + } + + private const String InternationalRegKey = "Control Panel\\International"; + private const String HijriAdvanceRegKeyEntry = "AddHijriDate"; + + /*=================================GetAdvanceHijriDate========================== + **Action: Gets the AddHijriDate value from the registry. + **Returns: + **Arguments: None. + **Exceptions: + **Note: + ** The HijriCalendar has a user-overidable calculation. That is, use can set a value from the control + ** panel, so that the calculation of the Hijri Calendar can move ahead or backwards from -2 to +2 days. + ** + ** The valid string values in the registry are: + ** "AddHijriDate-2" => Add -2 days to the current calculated Hijri date. + ** "AddHijriDate" => Add -1 day to the current calculated Hijri date. + ** "" => Add 0 day to the current calculated Hijri date. + ** "AddHijriDate+1" => Add +1 days to the current calculated Hijri date. + ** "AddHijriDate+2" => Add +2 days to the current calculated Hijri date. + ============================================================================*/ + private static int GetAdvanceHijriDate() + { + int hijriAdvance = 0; + Microsoft.Win32.RegistryKey key = null; + + try + { + // Open in read-only mode. + key = RegistryKey.GetBaseKey(RegistryKey.HKEY_CURRENT_USER).OpenSubKey(InternationalRegKey, false); + } + //If this fails for any reason, we'll just return 0. + catch (ObjectDisposedException) { return 0; } + catch (ArgumentException) { return 0; } + + if (key != null) + { + try + { + Object value = key.InternalGetValue(HijriAdvanceRegKeyEntry, null, false, false); + if (value == null) + { + return (0); + } + String str = value.ToString(); + if (String.Compare(str, 0, HijriAdvanceRegKeyEntry, 0, HijriAdvanceRegKeyEntry.Length, StringComparison.OrdinalIgnoreCase) == 0) + { + if (str.Length == HijriAdvanceRegKeyEntry.Length) + hijriAdvance = -1; + else + { + try + { + int advance = Int32.Parse(str.AsReadOnlySpan().Slice(HijriAdvanceRegKeyEntry.Length), provider:CultureInfo.InvariantCulture); + if ((advance >= MinAdvancedHijri) && (advance <= MaxAdvancedHijri)) + { + hijriAdvance = advance; + } + } + // If we got garbage from registry just ignore it. + // hijriAdvance = 0 because of declaraction assignment up above. + catch (ArgumentException) { } + catch (FormatException) { } + catch (OverflowException) { } + } + } + } + finally + { + key.Close(); + } + } + return (hijriAdvance); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/HijriCalendar.WinRT.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/HijriCalendar.WinRT.cs new file mode 100644 index 0000000000..fb91c27ef6 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/HijriCalendar.WinRT.cs @@ -0,0 +1,16 @@ +// 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 Internal.Runtime.Augments; + +namespace System.Globalization +{ + public partial class HijriCalendar : Calendar + { + private static int GetHijriDateAdjustment() + { + return WinRTInterop.Callbacks.GetHijriDateAdjustment(); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/HijriCalendar.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/HijriCalendar.cs new file mode 100644 index 0000000000..6755844620 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/HijriCalendar.cs @@ -0,0 +1,671 @@ +// 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. + + +namespace System.Globalization +{ + //////////////////////////////////////////////////////////////////////////// + // + // Rules for the Hijri calendar: + // - The Hijri calendar is a strictly Lunar calendar. + // - Days begin at sunset. + // - Islamic Year 1 (Muharram 1, 1 A.H.) is equivalent to absolute date + // 227015 (Friday, July 16, 622 C.E. - Julian). + // - Leap Years occur in the 2, 5, 7, 10, 13, 16, 18, 21, 24, 26, & 29th + // years of a 30-year cycle. Year = leap iff ((11y+14) mod 30 < 11). + // - There are 12 months which contain alternately 30 and 29 days. + // - The 12th month, Dhu al-Hijjah, contains 30 days instead of 29 days + // in a leap year. + // - Common years have 354 days. Leap years have 355 days. + // - There are 10,631 days in a 30-year cycle. + // - The Islamic months are: + // 1. Muharram (30 days) 7. Rajab (30 days) + // 2. Safar (29 days) 8. Sha'ban (29 days) + // 3. Rabi I (30 days) 9. Ramadan (30 days) + // 4. Rabi II (29 days) 10. Shawwal (29 days) + // 5. Jumada I (30 days) 11. Dhu al-Qada (30 days) + // 6. Jumada II (29 days) 12. Dhu al-Hijjah (29 days) {30} + // + // NOTENOTE + // The calculation of the HijriCalendar is based on the absolute date. And the + // absolute date means the number of days from January 1st, 1 A.D. + // Therefore, we do not support the days before the January 1st, 1 A.D. + // + //////////////////////////////////////////////////////////////////////////// + /* + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 0622/07/18 9999/12/31 + ** Hijri 0001/01/01 9666/04/03 + */ + + public partial class HijriCalendar : Calendar + { + public static readonly int HijriEra = 1; + + internal const int DatePartYear = 0; + internal const int DatePartDayOfYear = 1; + internal const int DatePartMonth = 2; + internal const int DatePartDay = 3; + + internal const int MinAdvancedHijri = -2; + internal const int MaxAdvancedHijri = 2; + + internal static readonly int[] HijriMonthDays = { 0, 30, 59, 89, 118, 148, 177, 207, 236, 266, 295, 325, 355 }; + + private int _hijriAdvance = Int32.MinValue; + + // DateTime.MaxValue = Hijri calendar (year:9666, month: 4, day: 3). + internal const int MaxCalendarYear = 9666; + internal const int MaxCalendarMonth = 4; + internal const int MaxCalendarDay = 3; + // Hijri calendar (year: 1, month: 1, day:1 ) = Gregorian (year: 622, month: 7, day: 18) + // This is the minimal Gregorian date that we support in the HijriCalendar. + internal static readonly DateTime calendarMinValue = new DateTime(622, 7, 18); + internal static readonly DateTime calendarMaxValue = DateTime.MaxValue; + + + public override DateTime MinSupportedDateTime + { + get + { + return (calendarMinValue); + } + } + + + public override DateTime MaxSupportedDateTime + { + get + { + return (calendarMaxValue); + } + } + + public override CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.LunarCalendar; + } + } + + public HijriCalendar() + { + } + + internal override CalendarId ID + { + get + { + return CalendarId.HIJRI; + } + } + + protected override int DaysInYearBeforeMinSupportedYear + { + get + { + // the year before the 1st year of the cycle would have been the 30th year + // of the previous cycle which is not a leap year. Common years have 354 days. + return 354; + } + } + + + + /*=================================GetAbsoluteDateHijri========================== + **Action: Gets the Absolute date for the given Hijri date. The absolute date means + ** the number of days from January 1st, 1 A.D. + **Returns: + **Arguments: + **Exceptions: + ============================================================================*/ + + private long GetAbsoluteDateHijri(int y, int m, int d) + { + return (long)(DaysUpToHijriYear(y) + HijriMonthDays[m - 1] + d - 1 - HijriAdjustment); + } + + /*=================================DaysUpToHijriYear========================== + **Action: Gets the total number of days (absolute date) up to the given Hijri Year. + ** The absolute date means the number of days from January 1st, 1 A.D. + **Returns: Gets the total number of days (absolute date) up to the given Hijri Year. + **Arguments: HijriYear year value in Hijri calendar. + **Exceptions: None + **Notes: + ============================================================================*/ + + private long DaysUpToHijriYear(int HijriYear) + { + long NumDays; // number of absolute days + int NumYear30; // number of years up to current 30 year cycle + int NumYearsLeft; // number of years into 30 year cycle + + // + // Compute the number of years up to the current 30 year cycle. + // + NumYear30 = ((HijriYear - 1) / 30) * 30; + + // + // Compute the number of years left. This is the number of years + // into the 30 year cycle for the given year. + // + NumYearsLeft = HijriYear - NumYear30 - 1; + + // + // Compute the number of absolute days up to the given year. + // + NumDays = ((NumYear30 * 10631L) / 30L) + 227013L; + while (NumYearsLeft > 0) + { + // Common year is 354 days, and leap year is 355 days. + NumDays += 354 + (IsLeapYear(NumYearsLeft, CurrentEra) ? 1 : 0); + NumYearsLeft--; + } + + // + // Return the number of absolute days. + // + return (NumDays); + } + + public int HijriAdjustment + { + get + { + if (_hijriAdvance == Int32.MinValue) + { + // Never been set before. Use the system value from registry. + _hijriAdvance = GetHijriDateAdjustment(); + } + return (_hijriAdvance); + } + + set + { + // NOTE: Check the value of Min/MaxAdvancedHijri with Arabic speakers to see if the assumption is good. + if (value < MinAdvancedHijri || value > MaxAdvancedHijri) + { + throw new ArgumentOutOfRangeException( + "HijriAdjustment", + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Bounds_Lower_Upper, + MinAdvancedHijri, + MaxAdvancedHijri)); + } + VerifyWritable(); + + _hijriAdvance = value; + } + } + + internal static void CheckTicksRange(long ticks) + { + if (ticks < calendarMinValue.Ticks || ticks > calendarMaxValue.Ticks) + { + throw new ArgumentOutOfRangeException( + "time", + String.Format( + CultureInfo.InvariantCulture, + SR.ArgumentOutOfRange_CalendarRange, + calendarMinValue, + calendarMaxValue)); + } + } + + internal static void CheckEraRange(int era) + { + if (era != CurrentEra && era != HijriEra) + { + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + } + + internal static void CheckYearRange(int year, int era) + { + CheckEraRange(era); + if (year < 1 || year > MaxCalendarYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + MaxCalendarYear)); + } + } + + internal static void CheckYearMonthRange(int year, int month, int era) + { + CheckYearRange(year, era); + if (year == MaxCalendarYear) + { + if (month > MaxCalendarMonth) + { + throw new ArgumentOutOfRangeException( + nameof(month), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + MaxCalendarMonth)); + } + } + + if (month < 1 || month > 12) + { + throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month); + } + } + + /*=================================GetDatePart========================== + **Action: Returns a given date part of this DateTime. This method is used + ** to compute the year, day-of-year, month, or day part. + **Returns: + **Arguments: + **Exceptions: ArgumentException if part is incorrect. + **Notes: + ** First, we get the absolute date (the number of days from January 1st, 1 A.C) for the given ticks. + ** Use the formula (((AbsoluteDate - 227013) * 30) / 10631) + 1, we can a rough value for the Hijri year. + ** In order to get the exact Hijri year, we compare the exact absolute date for HijriYear and (HijriYear + 1). + ** From here, we can get the correct Hijri year. + ============================================================================*/ + + internal virtual int GetDatePart(long ticks, int part) + { + int HijriYear; // Hijri year + int HijriMonth; // Hijri month + int HijriDay; // Hijri day + long NumDays; // The calculation buffer in number of days. + + CheckTicksRange(ticks); + + // + // Get the absolute date. The absolute date is the number of days from January 1st, 1 A.D. + // 1/1/0001 is absolute date 1. + // + NumDays = ticks / GregorianCalendar.TicksPerDay + 1; + + // + // See how much we need to backup or advance + // + NumDays += HijriAdjustment; + + // + // Calculate the appromixate Hijri Year from this magic formula. + // + HijriYear = (int)(((NumDays - 227013) * 30) / 10631) + 1; + + long daysToHijriYear = DaysUpToHijriYear(HijriYear); // The absolute date for HijriYear + long daysOfHijriYear = GetDaysInYear(HijriYear, CurrentEra); // The number of days for (HijriYear+1) year. + + if (NumDays < daysToHijriYear) + { + daysToHijriYear -= daysOfHijriYear; + HijriYear--; + } + else if (NumDays == daysToHijriYear) + { + HijriYear--; + daysToHijriYear -= GetDaysInYear(HijriYear, CurrentEra); + } + else + { + if (NumDays > daysToHijriYear + daysOfHijriYear) + { + daysToHijriYear += daysOfHijriYear; + HijriYear++; + } + } + if (part == DatePartYear) + { + return (HijriYear); + } + + // + // Calculate the Hijri Month. + // + + HijriMonth = 1; + NumDays -= daysToHijriYear; + + if (part == DatePartDayOfYear) + { + return ((int)NumDays); + } + + while ((HijriMonth <= 12) && (NumDays > HijriMonthDays[HijriMonth - 1])) + { + HijriMonth++; + } + HijriMonth--; + + if (part == DatePartMonth) + { + return (HijriMonth); + } + + // + // Calculate the Hijri Day. + // + HijriDay = (int)(NumDays - HijriMonthDays[HijriMonth - 1]); + + if (part == DatePartDay) + { + return (HijriDay); + } + // Incorrect part value. + throw new InvalidOperationException(SR.InvalidOperation_DateTimeParsing); + } + + // Returns the DateTime resulting from adding the given number of + // months to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year and month parts of the specified DateTime by + // value months, and, if required, adjusting the day part of the + // resulting date downwards to the last day of the resulting month in the + // resulting year. The time-of-day part of the result is the same as the + // time-of-day part of the specified DateTime. + // + // In more precise terms, considering the specified DateTime to be of the + // form y / m / d + t, where y is the + // year, m is the month, d is the day, and t is the + // time-of-day, the result is y1 / m1 / d1 + t, + // where y1 and m1 are computed by adding value months + // to y and m, and d1 is the largest value less than + // or equal to d that denotes a valid day in month m1 of year + // y1. + // + + public override DateTime AddMonths(DateTime time, int months) + { + if (months < -120000 || months > 120000) + { + throw new ArgumentOutOfRangeException( + nameof(months), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + -120000, + 120000)); + } + // Get the date in Hijri calendar. + int y = GetDatePart(time.Ticks, DatePartYear); + int m = GetDatePart(time.Ticks, DatePartMonth); + int d = GetDatePart(time.Ticks, DatePartDay); + int i = m - 1 + months; + if (i >= 0) + { + m = i % 12 + 1; + y = y + i / 12; + } + else + { + m = 12 + (i + 1) % 12; + y = y + (i - 11) / 12; + } + int days = GetDaysInMonth(y, m); + if (d > days) + { + d = days; + } + long ticks = GetAbsoluteDateHijri(y, m, d) * TicksPerDay + (time.Ticks % TicksPerDay); + Calendar.CheckAddResult(ticks, MinSupportedDateTime, MaxSupportedDateTime); + return (new DateTime(ticks)); + } + + // Returns the DateTime resulting from adding the given number of + // years to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year part of the specified DateTime by value + // years. If the month and day of the specified DateTime is 2/29, and if the + // resulting year is not a leap year, the month and day of the resulting + // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day + // parts of the result are the same as those of the specified DateTime. + // + + public override DateTime AddYears(DateTime time, int years) + { + return (AddMonths(time, years * 12)); + } + + // Returns the day-of-month part of the specified DateTime. The returned + // value is an integer between 1 and 31. + // + + public override int GetDayOfMonth(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartDay)); + } + + // Returns the day-of-week part of the specified DateTime. The returned value + // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates + // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates + // Thursday, 5 indicates Friday, and 6 indicates Saturday. + // + + public override DayOfWeek GetDayOfWeek(DateTime time) + { + return ((DayOfWeek)((int)(time.Ticks / TicksPerDay + 1) % 7)); + } + + // Returns the day-of-year part of the specified DateTime. The returned value + // is an integer between 1 and 366. + // + + public override int GetDayOfYear(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartDayOfYear)); + } + + // Returns the number of days in the month given by the year and + // month arguments. + // + public override int GetDaysInMonth(int year, int month, int era) + { + CheckYearMonthRange(year, month, era); + if (month == 12) + { + // For the 12th month, leap year has 30 days, and common year has 29 days. + return (IsLeapYear(year, CurrentEra) ? 30 : 29); + } + // Other months contain 30 and 29 days alternatively. The 1st month has 30 days. + return (((month % 2) == 1) ? 30 : 29); + } + + // Returns the number of days in the year given by the year argument for the current era. + // + + public override int GetDaysInYear(int year, int era) + { + CheckYearRange(year, era); + // Common years have 354 days. Leap years have 355 days. + return (IsLeapYear(year, CurrentEra) ? 355 : 354); + } + + // Returns the era for the specified DateTime value. + + public override int GetEra(DateTime time) + { + CheckTicksRange(time.Ticks); + return (HijriEra); + } + + + public override int[] Eras + { + get + { + return (new int[] { HijriEra }); + } + } + + // Returns the month part of the specified DateTime. The returned value is an + // integer between 1 and 12. + // + + public override int GetMonth(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartMonth)); + } + + // Returns the number of months in the specified year and era. + + public override int GetMonthsInYear(int year, int era) + { + CheckYearRange(year, era); + return (12); + } + + // Returns the year part of the specified DateTime. The returned value is an + // integer between 1 and MaxCalendarYear. + // + + public override int GetYear(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartYear)); + } + + // Checks whether a given day in the specified era is a leap day. This method returns true if + // the date is a leap day, or false if not. + // + + public override bool IsLeapDay(int year, int month, int day, int era) + { + // The year/month/era value checking is done in GetDaysInMonth(). + int daysInMonth = GetDaysInMonth(year, month, era); + if (day < 1 || day > daysInMonth) + { + throw new ArgumentOutOfRangeException( + nameof(day), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Day, + daysInMonth, + month)); + } + return (IsLeapYear(year, era) && month == 12 && day == 30); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this calendar does not have leap month, or this year is not a leap year. + // + + public override int GetLeapMonth(int year, int era) + { + CheckYearRange(year, era); + return (0); + } + + // Checks whether a given month in the specified era is a leap month. This method returns true if + // month is a leap month, or false if not. + // + + public override bool IsLeapMonth(int year, int month, int era) + { + CheckYearMonthRange(year, month, era); + return (false); + } + + // Checks whether a given year in the specified era is a leap year. This method returns true if + // year is a leap year, or false if not. + // + + public override bool IsLeapYear(int year, int era) + { + CheckYearRange(year, era); + return ((((year * 11) + 14) % 30) < 11); + } + + // Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid. + // + + public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) + { + // The year/month/era checking is done in GetDaysInMonth(). + int daysInMonth = GetDaysInMonth(year, month, era); + if (day < 1 || day > daysInMonth) + { + throw new ArgumentOutOfRangeException( + nameof(day), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Day, + daysInMonth, + month)); + } + + long lDate = GetAbsoluteDateHijri(year, month, day); + + if (lDate >= 0) + { + return (new DateTime(lDate * GregorianCalendar.TicksPerDay + TimeToTicks(hour, minute, second, millisecond))); + } + else + { + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay); + } + } + + private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 1451; + + + public override int TwoDigitYearMax + { + get + { + if (twoDigitYearMax == -1) + { + twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX); + } + return (twoDigitYearMax); + } + + set + { + VerifyWritable(); + if (value < 99 || value > MaxCalendarYear) + { + throw new ArgumentOutOfRangeException( + nameof(value), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 99, + MaxCalendarYear)); + } + twoDigitYearMax = value; + } + } + + + public override int ToFourDigitYear(int year) + { + if (year < 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedNonNegNum); + } + + if (year < 100) + { + return (base.ToFourDigitYear(year)); + } + + if (year > MaxCalendarYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + MaxCalendarYear)); + } + return (year); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/IdnMapping.Unix.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/IdnMapping.Unix.cs new file mode 100644 index 0000000000..2bbda0d3a7 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/IdnMapping.Unix.cs @@ -0,0 +1,142 @@ +// 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.Diagnostics; + +namespace System.Globalization +{ + sealed partial class IdnMapping + { + private unsafe string GetAsciiCore(char* unicode, int count) + { + Debug.Assert(!GlobalizationMode.Invariant); + + uint flags = Flags; + CheckInvalidIdnCharacters(unicode, count, flags, nameof(unicode)); + + const int StackallocThreshold = 512; + // Each unicode character is represented by up to 3 ASCII chars + // and the whole string is prefixed by "xn--" (length 4) + int estimatedLength = (int)Math.Min(count * 3L + 4, StackallocThreshold); + int actualLength; + if (estimatedLength < StackallocThreshold) + { + char* outputStack = stackalloc char[estimatedLength]; + actualLength = Interop.GlobalizationInterop.ToAscii(flags, unicode, count, outputStack, estimatedLength); + if (actualLength > 0 && actualLength <= estimatedLength) + { + return new string(outputStack, 0, actualLength); + } + } + else + { + actualLength = Interop.GlobalizationInterop.ToAscii(flags, unicode, count, null, 0); + } + if (actualLength == 0) + { + throw new ArgumentException(SR.Argument_IdnIllegalName, nameof(unicode)); + } + + char[] outputHeap = new char[actualLength]; + fixed (char* pOutputHeap = &outputHeap[0]) + { + actualLength = Interop.GlobalizationInterop.ToAscii(flags, unicode, count, pOutputHeap, actualLength); + if (actualLength == 0 || actualLength > outputHeap.Length) + { + throw new ArgumentException(SR.Argument_IdnIllegalName, nameof(unicode)); + } + return new string(pOutputHeap, 0, actualLength); + } + } + + private unsafe string GetUnicodeCore(char* ascii, int count) + { + Debug.Assert(!GlobalizationMode.Invariant); + + uint flags = Flags; + CheckInvalidIdnCharacters(ascii, count, flags, nameof(ascii)); + + const int StackAllocThreshold = 512; + if (count < StackAllocThreshold) + { + char* output = stackalloc char[count]; + return GetUnicodeCore(ascii, count, flags, output, count, reattempt: true); + } + else + { + char[] output = new char[count]; + fixed (char* pOutput = &output[0]) + { + return GetUnicodeCore(ascii, count, flags, pOutput, count, reattempt: true); + } + } + } + + private unsafe string GetUnicodeCore(char* ascii, int count, uint flags, char* output, int outputLength, bool reattempt) + { + Debug.Assert(!GlobalizationMode.Invariant); + + int realLen = Interop.GlobalizationInterop.ToUnicode(flags, ascii, count, output, outputLength); + + if (realLen == 0) + { + throw new ArgumentException(SR.Argument_IdnIllegalName, nameof(ascii)); + } + else if (realLen <= outputLength) + { + return new string(output, 0, realLen); + } + else if (reattempt) + { + char[] newOutput = new char[realLen]; + fixed (char* pNewOutput = newOutput) + { + return GetUnicodeCore(ascii, count, flags, pNewOutput, realLen, reattempt: false); + } + } + + throw new ArgumentException(SR.Argument_IdnIllegalName, nameof(ascii)); + } + + // ----------------------------- + // ---- PAL layer ends here ---- + // ----------------------------- + + private uint Flags + { + get + { + int flags = + (AllowUnassigned ? Interop.GlobalizationInterop.AllowUnassigned : 0) | + (UseStd3AsciiRules ? Interop.GlobalizationInterop.UseStd3AsciiRules : 0); + return (uint)flags; + } + } + + /// + /// ICU doesn't check for invalid characters unless the STD3 rules option + /// is enabled. + /// + /// To match Windows behavior, we walk the string ourselves looking for these + /// bad characters so we can continue to throw ArgumentException in these cases. + /// + private static unsafe void CheckInvalidIdnCharacters(char* s, int count, uint flags, string paramName) + { + if ((flags & Interop.GlobalizationInterop.UseStd3AsciiRules) == 0) + { + for (int i = 0; i < count; i++) + { + char c = s[i]; + + // These characters are prohibited regardless of the UseStd3AsciiRules property. + // See https://msdn.microsoft.com/en-us/library/system.globalization.idnmapping.usestd3asciirules(v=vs.110).aspx + if (c <= 0x1F || c == 0x7F) + { + throw new ArgumentException(SR.Argument_IdnIllegalName, paramName); + } + } + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/IdnMapping.Windows.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/IdnMapping.Windows.cs similarity index 68% rename from external/corert/src/System.Private.CoreLib/src/System/Globalization/IdnMapping.Windows.cs rename to external/corefx/src/Common/src/CoreLib/System/Globalization/IdnMapping.Windows.cs index 33264d9a71..35da7343e7 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/IdnMapping.Windows.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/IdnMapping.Windows.cs @@ -11,13 +11,15 @@ namespace System.Globalization { private unsafe string GetAsciiCore(char* unicode, int count) { + Debug.Assert(!GlobalizationMode.Invariant); + uint flags = Flags; // Determine the required length - int length = Interop.mincore.IdnToAscii(flags, new IntPtr(unicode), count, IntPtr.Zero, 0); + int length = Interop.Normaliz.IdnToAscii(flags, unicode, count, null, 0); if (length == 0) { - ThrowForZeroLength(nameof(unicode), SR.Argument_IdnIllegalName, SR.Argument_InvalidCharSequenceNoIndex); + ThrowForZeroLength(unicode: true); } // Do the conversion @@ -39,10 +41,12 @@ namespace System.Globalization private unsafe string GetAsciiCore(char* unicode, int count, uint flags, char* output, int outputLength) { - int length = Interop.mincore.IdnToAscii(flags, new IntPtr(unicode), count, new IntPtr(output), outputLength); + Debug.Assert(!GlobalizationMode.Invariant); + + int length = Interop.Normaliz.IdnToAscii(flags, unicode, count, output, outputLength); if (length == 0) { - ThrowForZeroLength(nameof(unicode), SR.Argument_IdnIllegalName, SR.Argument_InvalidCharSequenceNoIndex); + ThrowForZeroLength(unicode: true); } Debug.Assert(length == outputLength); return new string(output, 0, length); @@ -50,13 +54,15 @@ namespace System.Globalization private unsafe string GetUnicodeCore(char* ascii, int count) { + Debug.Assert(!GlobalizationMode.Invariant); + uint flags = Flags; // Determine the required length - int length = Interop.mincore.IdnToUnicode(flags, new IntPtr(ascii), count, IntPtr.Zero, 0); + int length = Interop.Normaliz.IdnToUnicode(flags, ascii, count, null, 0); if (length == 0) { - ThrowForZeroLength(nameof(ascii), SR.Argument_IdnIllegalName, SR.Argument_IdnBadPunycode); + ThrowForZeroLength(unicode: false); } // Do the conversion @@ -78,10 +84,12 @@ namespace System.Globalization private unsafe string GetUnicodeCore(char* ascii, int count, uint flags, char* output, int outputLength) { - int length = Interop.mincore.IdnToUnicode(flags, new IntPtr(ascii), count, new IntPtr(output), outputLength); + Debug.Assert(!GlobalizationMode.Invariant); + + int length = Interop.Normaliz.IdnToUnicode(flags, ascii, count, output, outputLength); if (length == 0) { - ThrowForZeroLength(nameof(ascii), SR.Argument_IdnIllegalName, SR.Argument_IdnBadPunycode); + ThrowForZeroLength(unicode: false); } Debug.Assert(length == outputLength); return new string(output, 0, length); @@ -96,19 +104,20 @@ namespace System.Globalization get { int flags = - (AllowUnassigned ? Interop.mincore.IDN_ALLOW_UNASSIGNED : 0) | - (UseStd3AsciiRules ? Interop.mincore.IDN_USE_STD3_ASCII_RULES : 0); + (AllowUnassigned ? Interop.Normaliz.IDN_ALLOW_UNASSIGNED : 0) | + (UseStd3AsciiRules ? Interop.Normaliz.IDN_USE_STD3_ASCII_RULES : 0); return (uint)flags; } } - private static void ThrowForZeroLength(string paramName, string invalidNameString, string otherString) + private static void ThrowForZeroLength(bool unicode) { - int lastError = Interop.mincore.GetLastError(); + int lastError = Marshal.GetLastWin32Error(); throw new ArgumentException( - lastError == Interop.ERROR_INVALID_NAME ? invalidNameString : otherString, - paramName); + lastError == Interop.Errors.ERROR_INVALID_NAME ? SR.Argument_IdnIllegalName : + (unicode ? SR.Argument_InvalidCharSequenceNoIndex : SR.Argument_IdnBadPunycode), + unicode ? "unicode" : "ascii"); } } } diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/IdnMapping.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/IdnMapping.cs new file mode 100644 index 0000000000..176e5feed5 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/IdnMapping.cs @@ -0,0 +1,893 @@ +// 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. + +// This file contains the IDN functions and implementation. +// +// This allows encoding of non-ASCII domain names in a "punycode" form, +// for example: +// +// \u5B89\u5BA4\u5948\u7F8E\u6075-with-SUPER-MONKEYS +// +// is encoded as: +// +// xn---with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n +// +// Additional options are provided to allow unassigned IDN characters and +// to validate according to the Std3ASCII Rules (like DNS names). +// +// There are also rules regarding bidirectionality of text and the length +// of segments. +// +// For additional rules see also: +// RFC 3490 - Internationalizing Domain Names in Applications (IDNA) +// RFC 3491 - Nameprep: A Stringprep Profile for Internationalized Domain Names (IDN) +// RFC 3492 - Punycode: A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA) + +using System.Diagnostics; +using System.Text; + +namespace System.Globalization +{ + // IdnMapping class used to map names to Punycode + public sealed partial class IdnMapping + { + private bool _allowUnassigned; + private bool _useStd3AsciiRules; + + public IdnMapping() + { + } + + public bool AllowUnassigned + { + get { return _allowUnassigned; } + set { _allowUnassigned = value; } + } + + public bool UseStd3AsciiRules + { + get { return _useStd3AsciiRules; } + set { _useStd3AsciiRules = value; } + } + + // Gets ASCII (Punycode) version of the string + public string GetAscii(string unicode) + { + return GetAscii(unicode, 0); + } + + public string GetAscii(string unicode, int index) + { + if (unicode == null) + throw new ArgumentNullException(nameof(unicode)); + return GetAscii(unicode, index, unicode.Length - index); + } + + public string GetAscii(string unicode, int index, int count) + { + if (unicode == null) + throw new ArgumentNullException(nameof(unicode)); + if (index < 0 || count < 0) + throw new ArgumentOutOfRangeException((index < 0) ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + if (index > unicode.Length) + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + if (index > unicode.Length - count) + throw new ArgumentOutOfRangeException(nameof(unicode), SR.ArgumentOutOfRange_IndexCountBuffer); + + if (count == 0) + { + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(unicode)); + } + if (unicode[index + count - 1] == 0) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidCharSequence, index + count - 1), nameof(unicode)); + } + + if (GlobalizationMode.Invariant) + { + return GetAsciiInvariant(unicode, index, count); + } + + unsafe + { + fixed (char* pUnicode = unicode) + { + return GetAsciiCore(pUnicode + index, count); + } + } + } + + // Gets Unicode version of the string. Normalized and limited to IDNA characters. + public string GetUnicode(string ascii) + { + return GetUnicode(ascii, 0); + } + + public string GetUnicode(string ascii, int index) + { + if (ascii == null) + throw new ArgumentNullException(nameof(ascii)); + return GetUnicode(ascii, index, ascii.Length - index); + } + + public string GetUnicode(string ascii, int index, int count) + { + if (ascii == null) + throw new ArgumentNullException(nameof(ascii)); + if (index < 0 || count < 0) + throw new ArgumentOutOfRangeException((index < 0) ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + if (index > ascii.Length) + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + if (index > ascii.Length - count) + throw new ArgumentOutOfRangeException(nameof(ascii), SR.ArgumentOutOfRange_IndexCountBuffer); + + // This is a case (i.e. explicitly null-terminated input) where behavior in .NET and Win32 intentionally differ. + // The .NET APIs should (and did in v4.0 and earlier) throw an ArgumentException on input that includes a terminating null. + // The Win32 APIs fail on an embedded null, but not on a terminating null. + if (count > 0 && ascii[index + count - 1] == (char)0) + throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(ascii)); + + if (GlobalizationMode.Invariant) + { + return GetUnicodeInvariant(ascii, index, count); + } + + unsafe + { + fixed (char* pAscii = ascii) + { + return GetUnicodeCore(pAscii + index, count); + } + } + } + + public override bool Equals(object obj) + { + IdnMapping that = obj as IdnMapping; + return + that != null && + _allowUnassigned == that._allowUnassigned && + _useStd3AsciiRules == that._useStd3AsciiRules; + } + + public override int GetHashCode() + { + return (_allowUnassigned ? 100 : 200) + (_useStd3AsciiRules ? 1000 : 2000); + } + + // + // Invariant implementation + // + + private const char c_delimiter = '-'; + private const string c_strAcePrefix = "xn--"; + private const int c_labelLimit = 63; // Not including dots + private const int c_defaultNameLimit = 255; // Including dots + private const int c_initialN = 0x80; + private const int c_maxint = 0x7ffffff; + private const int c_initialBias = 72; + private const int c_punycodeBase = 36; + private const int c_tmin = 1; + private const int c_tmax = 26; + private const int c_skew = 38; + private const int c_damp = 700; + + + // Legal "dot" separators (i.e: . in www.microsoft.com) + private static char[] c_Dots = { '.', '\u3002', '\uFF0E', '\uFF61' }; + + private string GetAsciiInvariant(string unicode, int index, int count) + { + if (index > 0 || count < unicode.Length) + { + unicode = unicode.Substring(index, count); + } + + // Check for ASCII only string, which will be unchanged + if (ValidateStd3AndAscii(unicode, UseStd3AsciiRules, true)) + { + return unicode; + } + + // Cannot be null terminated (normalization won't help us with this one, and + // may have returned false before checking the whole string above) + Debug.Assert(count >= 1, "[IdnMapping.GetAscii] Expected 0 length strings to fail before now."); + if (unicode[unicode.Length - 1] <= 0x1f) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidCharSequence, unicode.Length - 1), nameof(unicode)); + } + + // Have to correctly IDNA normalize the string and Unassigned flags + bool bHasLastDot = (unicode.Length > 0) && IsDot(unicode[unicode.Length - 1]); + + // Make sure we didn't normalize away something after a last dot + if ((!bHasLastDot) && unicode.Length > 0 && IsDot(unicode[unicode.Length - 1])) + { + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(unicode)); + } + + // May need to check Std3 rules again for non-ascii + if (UseStd3AsciiRules) + { + ValidateStd3AndAscii(unicode, true, false); + } + + // Go ahead and encode it + return PunycodeEncode(unicode); + } + + // See if we're only ASCII + static bool ValidateStd3AndAscii(string unicode, bool bUseStd3, bool bCheckAscii) + { + // If its empty, then its too small + if (unicode.Length == 0) + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(unicode)); + + int iLastDot = -1; + + // Loop the whole string + for (int i = 0; i < unicode.Length; i++) + { + // Aren't allowing control chars (or 7f, but idn tables catch that, they don't catch \0 at end though) + if (unicode[i] <= 0x1f) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidCharSequence, i ), nameof(unicode)); + } + + // If its Unicode or a control character, return false (non-ascii) + if (bCheckAscii && unicode[i] >= 0x7f) + return false; + + // Check for dots + if (IsDot(unicode[i])) + { + // Can't have 2 dots in a row + if (i == iLastDot + 1) + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(unicode)); + + // If its too far between dots then fail + if (i - iLastDot > c_labelLimit + 1) + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(unicode)); + + // If validating Std3, then char before dot can't be - char + if (bUseStd3 && i > 0) + ValidateStd3(unicode[i - 1], true); + + // Remember where the last dot is + iLastDot = i; + continue; + } + + // If necessary, make sure its a valid std3 character + if (bUseStd3) + { + ValidateStd3(unicode[i], (i == iLastDot + 1)); + } + } + + // If we never had a dot, then we need to be shorter than the label limit + if (iLastDot == -1 && unicode.Length > c_labelLimit) + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(unicode)); + + // Need to validate entire string length, 1 shorter if last char wasn't a dot + if (unicode.Length > c_defaultNameLimit - (IsDot(unicode[unicode.Length - 1]) ? 0 : 1)) + throw new ArgumentException(SR.Format(SR.Argument_IdnBadNameSize, + c_defaultNameLimit - (IsDot(unicode[unicode.Length - 1]) ? 0 : 1)), nameof(unicode)); + + // If last char wasn't a dot we need to check for trailing - + if (bUseStd3 && !IsDot(unicode[unicode.Length - 1])) + ValidateStd3(unicode[unicode.Length - 1], true); + + return true; + } + + /* PunycodeEncode() converts Unicode to Punycode. The input */ + /* is represented as an array of Unicode code points (not code */ + /* units; surrogate pairs are not allowed), and the output */ + /* will be represented as an array of ASCII code points. The */ + /* output string is *not* null-terminated; it will contain */ + /* zeros if and only if the input contains zeros. (Of course */ + /* the caller can leave room for a terminator and add one if */ + /* needed.) The input_length is the number of code points in */ + /* the input. The output_length is an in/out argument: the */ + /* caller passes in the maximum number of code points that it */ + + /* can receive, and on successful return it will contain the */ + /* number of code points actually output. The case_flags array */ + /* holds input_length boolean values, where nonzero suggests that */ + /* the corresponding Unicode character be forced to uppercase */ + /* after being decoded (if possible), and zero suggests that */ + /* it be forced to lowercase (if possible). ASCII code points */ + /* are encoded literally, except that ASCII letters are forced */ + /* to uppercase or lowercase according to the corresponding */ + /* uppercase flags. If case_flags is a null pointer then ASCII */ + /* letters are left as they are, and other code points are */ + /* treated as if their uppercase flags were zero. The return */ + /* value can be any of the punycode_status values defined above */ + /* except punycode_bad_input; if not punycode_success, then */ + /* output_size and output might contain garbage. */ + static string PunycodeEncode(string unicode) + { + // 0 length strings aren't allowed + if (unicode.Length == 0) + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(unicode)); + + StringBuilder output = new StringBuilder(unicode.Length); + int iNextDot = 0; + int iAfterLastDot = 0; + int iOutputAfterLastDot = 0; + + // Find the next dot + while (iNextDot < unicode.Length) + { + // Find end of this segment + iNextDot = unicode.IndexOfAny(c_Dots, iAfterLastDot); + Debug.Assert(iNextDot <= unicode.Length, "[IdnMapping.punycode_encode]IndexOfAny is broken"); + if (iNextDot < 0) + iNextDot = unicode.Length; + + // Only allowed to have empty . section at end (www.microsoft.com.) + if (iNextDot == iAfterLastDot) + { + // Only allowed to have empty sections as trailing . + if (iNextDot != unicode.Length) + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(unicode)); + // Last dot, stop + break; + } + + // We'll need an Ace prefix + output.Append(c_strAcePrefix); + + // Everything resets every segment. + bool bRightToLeft = false; + + // Check for RTL. If right-to-left, then 1st & last chars must be RTL + BidiCategory eBidi = CharUnicodeInfo.GetBidiCategory(unicode, iAfterLastDot); + if (eBidi == BidiCategory.RightToLeft || eBidi == BidiCategory.RightToLeftArabic) + { + // It has to be right to left. + bRightToLeft = true; + + // Check last char + int iTest = iNextDot - 1; + if (Char.IsLowSurrogate(unicode, iTest)) + { + iTest--; + } + + eBidi = CharUnicodeInfo.GetBidiCategory(unicode, iTest); + if (eBidi != BidiCategory.RightToLeft && eBidi != BidiCategory.RightToLeftArabic) + { + // Oops, last wasn't RTL, last should be RTL if first is RTL + throw new ArgumentException(SR.Argument_IdnBadBidi, nameof(unicode)); + } + } + + // Handle the basic code points + int basicCount; + int numProcessed = 0; // Num code points that have been processed so far (this segment) + for (basicCount = iAfterLastDot; basicCount < iNextDot; basicCount++) + { + // Can't be lonely surrogate because it would've thrown in normalization + Debug.Assert(Char.IsLowSurrogate(unicode, basicCount) == false, "[IdnMapping.punycode_encode]Unexpected low surrogate"); + + // Double check our bidi rules + BidiCategory testBidi = CharUnicodeInfo.GetBidiCategory(unicode, basicCount); + + // If we're RTL, we can't have LTR chars + if (bRightToLeft && testBidi == BidiCategory.LeftToRight) + { + // Oops, throw error + throw new ArgumentException(SR.Argument_IdnBadBidi, nameof(unicode)); + } + + // If we're not RTL we can't have RTL chars + if (!bRightToLeft && (testBidi == BidiCategory.RightToLeft || testBidi == BidiCategory.RightToLeftArabic)) + { + // Oops, throw error + throw new ArgumentException(SR.Argument_IdnBadBidi, nameof(unicode)); + } + + // If its basic then add it + if (Basic(unicode[basicCount])) + { + output.Append(EncodeBasic(unicode[basicCount])); + numProcessed++; + } + // If its a surrogate, skip the next since our bidi category tester doesn't handle it. + else if (Char.IsSurrogatePair(unicode, basicCount)) + basicCount++; + } + + int numBasicCodePoints = numProcessed; // number of basic code points + + // Stop if we ONLY had basic code points + if (numBasicCodePoints == iNextDot - iAfterLastDot) + { + // Get rid of xn-- and this segments done + output.Remove(iOutputAfterLastDot, c_strAcePrefix.Length); + } + else + { + // If it has some non-basic code points the input cannot start with xn-- + if (unicode.Length - iAfterLastDot >= c_strAcePrefix.Length && + unicode.Substring(iAfterLastDot, c_strAcePrefix.Length).Equals( + c_strAcePrefix, StringComparison.OrdinalIgnoreCase)) + throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(unicode)); + + // Need to do ACE encoding + int numSurrogatePairs = 0; // number of surrogate pairs so far + + // Add a delimiter (-) if we had any basic code points (between basic and encoded pieces) + if (numBasicCodePoints > 0) + { + output.Append(c_delimiter); + } + + // Initialize the state + int n = c_initialN; + int delta = 0; + int bias = c_initialBias; + + // Main loop + while (numProcessed < (iNextDot - iAfterLastDot)) + { + /* All non-basic code points < n have been */ + /* handled already. Find the next larger one: */ + int j; + int m; + int test = 0; + for (m = c_maxint, j = iAfterLastDot; + j < iNextDot; + j += IsSupplementary(test) ? 2 : 1) + { + test = Char.ConvertToUtf32(unicode, j); + if (test >= n && test < m) m = test; + } + + /* Increase delta enough to advance the decoder's */ + /* state to , but guard against overflow: */ + delta += (int)((m - n) * ((numProcessed - numSurrogatePairs) + 1)); + Debug.Assert(delta > 0, "[IdnMapping.cs]1 punycode_encode - delta overflowed int"); + n = m; + + for (j = iAfterLastDot; j < iNextDot; j+= IsSupplementary(test) ? 2 : 1) + { + // Make sure we're aware of surrogates + test = Char.ConvertToUtf32(unicode, j); + + // Adjust for character position (only the chars in our string already, some + // haven't been processed. + + if (test < n) + { + delta++; + Debug.Assert(delta > 0, "[IdnMapping.cs]2 punycode_encode - delta overflowed int"); + } + + if (test == n) + { + // Represent delta as a generalized variable-length integer: + int q, k; + for (q = delta, k = c_punycodeBase; ; k += c_punycodeBase) + { + int t = k <= bias ? c_tmin : k >= bias + c_tmax ? c_tmax : k - bias; + if (q < t) break; + Debug.Assert(c_punycodeBase != t, "[IdnMapping.punycode_encode]Expected c_punycodeBase (36) to be != t"); + output.Append(EncodeDigit(t + (q - t) % (c_punycodeBase - t))); + q = (q - t) / (c_punycodeBase - t); + } + + output.Append(EncodeDigit(q)); + bias = Adapt(delta, (numProcessed - numSurrogatePairs) + 1, numProcessed == numBasicCodePoints); + delta = 0; + numProcessed++; + + if (IsSupplementary(m)) + { + numProcessed++; + numSurrogatePairs++; + } + } + } + ++delta; + ++n; + Debug.Assert(delta > 0, "[IdnMapping.cs]3 punycode_encode - delta overflowed int"); + } + } + + // Make sure its not too big + if (output.Length - iOutputAfterLastDot > c_labelLimit) + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(unicode)); + + // Done with this segment, add dot if necessary + if (iNextDot != unicode.Length) + output.Append('.'); + + iAfterLastDot = iNextDot + 1; + iOutputAfterLastDot = output.Length; + } + + // Throw if we're too long + if (output.Length > c_defaultNameLimit - (IsDot(unicode[unicode.Length-1]) ? 0 : 1)) + throw new ArgumentException(SR.Format(SR.Argument_IdnBadNameSize, + c_defaultNameLimit - (IsDot(unicode[unicode.Length-1]) ? 0 : 1)), nameof(unicode)); + // Return our output string + return output.ToString(); + } + + // Is it a dot? + // are we U+002E (., full stop), U+3002 (ideographic full stop), U+FF0E (fullwidth full stop), or + // U+FF61 (halfwidth ideographic full stop). + // Note: IDNA Normalization gets rid of dots now, but testing for last dot is before normalization + private static bool IsDot(char c) + { + return c == '.' || c == '\u3002' || c == '\uFF0E' || c == '\uFF61'; + } + + private static bool IsSupplementary(int cTest) + { + return cTest >= 0x10000; + } + + private static bool Basic(uint cp) + { + // Is it in ASCII range? + return cp < 0x80; + } + + // Validate Std3 rules for a character + private static void ValidateStd3(char c, bool bNextToDot) + { + // Check for illegal characters + if ((c <= ',' || c == '/' || (c >= ':' && c <= '@') || // Lots of characters not allowed + (c >= '[' && c <= '`') || (c >= '{' && c <= (char)0x7F)) || + (c == '-' && bNextToDot)) + throw new ArgumentException(SR.Format(SR.Argument_IdnBadStd3, c), nameof(c)); + } + + private string GetUnicodeInvariant(string ascii, int index, int count) + { + if (index > 0 || count < ascii.Length) + { + // We're only using part of the string + ascii = ascii.Substring(index, count); + } + // Convert Punycode to Unicode + string strUnicode = PunycodeDecode(ascii); + + // Output name MUST obey IDNA rules & round trip (casing differences are allowed) + if (!ascii.Equals(GetAscii(strUnicode), StringComparison.OrdinalIgnoreCase)) + throw new ArgumentException(SR.Argument_IdnIllegalName, nameof(ascii)); + + return strUnicode; + } + + /* PunycodeDecode() converts Punycode to Unicode. The input is */ + /* represented as an array of ASCII code points, and the output */ + /* will be represented as an array of Unicode code points. The */ + /* input_length is the number of code points in the input. The */ + /* output_length is an in/out argument: the caller passes in */ + /* the maximum number of code points that it can receive, and */ + /* on successful return it will contain the actual number of */ + /* code points output. The case_flags array needs room for at */ + /* least output_length values, or it can be a null pointer if the */ + /* case information is not needed. A nonzero flag suggests that */ + /* the corresponding Unicode character be forced to uppercase */ + /* by the caller (if possible), while zero suggests that it be */ + /* forced to lowercase (if possible). ASCII code points are */ + /* output already in the proper case, but their flags will be set */ + /* appropriately so that applying the flags would be harmless. */ + /* The return value can be any of the punycode_status values */ + /* defined above; if not punycode_success, then output_length, */ + /* output, and case_flags might contain garbage. On success, the */ + /* decoder will never need to write an output_length greater than */ + /* input_length, because of how the encoding is defined. */ + + private static string PunycodeDecode(string ascii) + { + // 0 length strings aren't allowed + if (ascii.Length == 0) + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(ascii)); + + // Throw if we're too long + if (ascii.Length > c_defaultNameLimit - (IsDot(ascii[ascii.Length-1]) ? 0 : 1)) + throw new ArgumentException(SR.Format(SR.Argument_IdnBadNameSize, + c_defaultNameLimit - (IsDot(ascii[ascii.Length-1]) ? 0 : 1)), nameof(ascii)); + + // output stringbuilder + StringBuilder output = new StringBuilder(ascii.Length); + + // Dot searching + int iNextDot = 0; + int iAfterLastDot = 0; + int iOutputAfterLastDot = 0; + + while (iNextDot < ascii.Length) + { + // Find end of this segment + iNextDot = ascii.IndexOf('.', iAfterLastDot); + if (iNextDot < 0 || iNextDot > ascii.Length) + iNextDot = ascii.Length; + + // Only allowed to have empty . section at end (www.microsoft.com.) + if (iNextDot == iAfterLastDot) + { + // Only allowed to have empty sections as trailing . + if (iNextDot != ascii.Length) + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(ascii)); + + // Last dot, stop + break; + } + + // In either case it can't be bigger than segment size + if (iNextDot - iAfterLastDot > c_labelLimit) + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(ascii)); + + // See if this section's ASCII or ACE + if (ascii.Length < c_strAcePrefix.Length + iAfterLastDot || + !ascii.Substring(iAfterLastDot,c_strAcePrefix.Length).Equals(c_strAcePrefix, StringComparison.OrdinalIgnoreCase)) + { + // Its ASCII, copy it + output.Append(ascii.Substring(iAfterLastDot, iNextDot - iAfterLastDot)); + } + else + { + // Not ASCII, bump up iAfterLastDot to be after ACE Prefix + iAfterLastDot += c_strAcePrefix.Length; + + // Get number of basic code points (where delimiter is) + // numBasicCodePoints < 0 if there're no basic code points + int iTemp = ascii.LastIndexOf(c_delimiter, iNextDot - 1); + + // Trailing - not allowed + if (iTemp == iNextDot - 1) + throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(ascii)); + + int numBasicCodePoints; + if (iTemp <= iAfterLastDot) + numBasicCodePoints = 0; + else + { + numBasicCodePoints = iTemp - iAfterLastDot; + + // Copy all the basic code points, making sure they're all in the allowed range, + // and losing the casing for all of them. + for (int copyAscii = iAfterLastDot; copyAscii < iAfterLastDot + numBasicCodePoints; copyAscii++) + { + // Make sure we don't allow unicode in the ascii part + if (ascii[copyAscii] > 0x7f) + throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(ascii)); + + // When appending make sure they get lower cased + output.Append((char)(ascii[copyAscii] >= 'A' && ascii[copyAscii] <='Z' ? ascii[copyAscii] - 'A' + 'a' : ascii[copyAscii])); + } + } + + // Get ready for main loop. Start at beginning if we didn't have any + // basic code points, otherwise start after the -. + // asciiIndex will be next character to read from ascii + int asciiIndex = iAfterLastDot + (numBasicCodePoints > 0 ? numBasicCodePoints + 1 : 0); + + // initialize our state + int n = c_initialN; + int bias = c_initialBias; + int i = 0; + + int w, k; + + // no Supplementary characters yet + int numSurrogatePairs = 0; + + // Main loop, read rest of ascii + while (asciiIndex < iNextDot) + { + /* Decode a generalized variable-length integer into delta, */ + /* which gets added to i. The overflow checking is easier */ + /* if we increase i as we go, then subtract off its starting */ + /* value at the end to obtain delta. */ + int oldi = i; + + for (w = 1, k = c_punycodeBase; ; k += c_punycodeBase) + { + // Check to make sure we aren't overrunning our ascii string + if (asciiIndex >= iNextDot) + throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(ascii)); + + // decode the digit from the next char + int digit = DecodeDigit(ascii[asciiIndex++]); + + Debug.Assert(w > 0, "[IdnMapping.punycode_decode]Expected w > 0"); + if (digit > (c_maxint - i) / w) + throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(ascii)); + + i += (int)(digit * w); + int t = k <= bias ? c_tmin : k >= bias + c_tmax ? c_tmax : k - bias; + if (digit < t) + break; + Debug.Assert(c_punycodeBase != t, "[IdnMapping.punycode_decode]Expected t != c_punycodeBase (36)"); + if (w > c_maxint / (c_punycodeBase - t)) + throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(ascii)); + w *= (c_punycodeBase - t); + } + + bias = Adapt(i - oldi, (output.Length - iOutputAfterLastDot - numSurrogatePairs) + 1, oldi == 0); + + /* i was supposed to wrap around from output.Length to 0, */ + /* incrementing n each time, so we'll fix that now: */ + Debug.Assert((output.Length - iOutputAfterLastDot - numSurrogatePairs) + 1 > 0, + "[IdnMapping.punycode_decode]Expected to have added > 0 characters this segment"); + if (i / ((output.Length - iOutputAfterLastDot - numSurrogatePairs) + 1) > c_maxint - n) + throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(ascii)); + n += (int)(i / (output.Length - iOutputAfterLastDot - numSurrogatePairs + 1)); + i %= (output.Length - iOutputAfterLastDot - numSurrogatePairs + 1); + + // Make sure n is legal + if ((n < 0 || n > 0x10ffff) || (n >= 0xD800 && n <= 0xDFFF)) + throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(ascii)); + + // insert n at position i of the output: Really tricky if we have surrogates + int iUseInsertLocation; + String strTemp = Char.ConvertFromUtf32(n); + + // If we have supplimentary characters + if (numSurrogatePairs > 0) + { + // Hard way, we have supplimentary characters + int iCount; + for (iCount = i, iUseInsertLocation = iOutputAfterLastDot; iCount > 0; iCount--, iUseInsertLocation++) + { + // If its a surrogate, we have to go one more + if (iUseInsertLocation >= output.Length) + throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(ascii)); + if (Char.IsSurrogate(output[iUseInsertLocation])) + iUseInsertLocation++; + } + } + else + { + // No Supplementary chars yet, just add i + iUseInsertLocation = iOutputAfterLastDot + i; + } + + // Insert it + output.Insert(iUseInsertLocation, strTemp); + + // If it was a surrogate increment our counter + if (IsSupplementary(n)) + numSurrogatePairs++; + + // Index gets updated + i++; + } + + // Do BIDI testing + bool bRightToLeft = false; + + // Check for RTL. If right-to-left, then 1st & last chars must be RTL + BidiCategory eBidi = CharUnicodeInfo.GetBidiCategory(output.ToString(), iOutputAfterLastDot); + if (eBidi == BidiCategory.RightToLeft || eBidi == BidiCategory.RightToLeftArabic) + { + // It has to be right to left. + bRightToLeft = true; + } + + // Check the rest of them to make sure RTL/LTR is consistent + for (int iTest = iOutputAfterLastDot; iTest < output.Length; iTest++) + { + // This might happen if we run into a pair + if (Char.IsLowSurrogate(output.ToString(), iTest)) + continue; + + // Check to see if its LTR + eBidi = CharUnicodeInfo.GetBidiCategory(output.ToString(), iTest); + if ((bRightToLeft && eBidi == BidiCategory.LeftToRight) || + (!bRightToLeft && (eBidi == BidiCategory.RightToLeft || eBidi == BidiCategory.RightToLeftArabic))) + throw new ArgumentException(SR.Argument_IdnBadBidi, nameof(ascii)); + } + + // Its also a requirement that the last one be RTL if 1st is RTL + if (bRightToLeft && eBidi != BidiCategory.RightToLeft && eBidi != BidiCategory.RightToLeftArabic) + { + // Oops, last wasn't RTL, last should be RTL if first is RTL + throw new ArgumentException(SR.Argument_IdnBadBidi, nameof(ascii)); + } + } + + // See if this label was too long + if (iNextDot - iAfterLastDot > c_labelLimit) + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(ascii)); + + // Done with this segment, add dot if necessary + if (iNextDot != ascii.Length) + output.Append('.'); + + iAfterLastDot = iNextDot + 1; + iOutputAfterLastDot = output.Length; + } + + // Throw if we're too long + if (output.Length > c_defaultNameLimit - (IsDot(output[output.Length-1]) ? 0 : 1)) + throw new ArgumentException(SR.Format(SR.Argument_IdnBadNameSize, c_defaultNameLimit - (IsDot(output[output.Length-1]) ? 0 : 1)), nameof(ascii)); + + // Return our output string + return output.ToString(); + } + + // DecodeDigit(cp) returns the numeric value of a basic code */ + // point (for use in representing integers) in the range 0 to */ + // c_punycodeBase-1, or <0 if cp is does not represent a value. */ + + private static int DecodeDigit(char cp) + { + if (cp >= '0' && cp <= '9') + return cp - '0' + 26; + + // Two flavors for case differences + if (cp >= 'a' && cp <= 'z') + return cp - 'a'; + + if (cp >= 'A' && cp <= 'Z') + return cp - 'A'; + + // Expected 0-9, A-Z or a-z, everything else is illegal + throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(cp)); + } + + private static int Adapt(int delta, int numpoints, bool firsttime) + { + uint k; + + delta = firsttime ? delta / c_damp : delta / 2; + Debug.Assert(numpoints != 0, "[IdnMapping.adapt]Expected non-zero numpoints."); + delta += delta / numpoints; + + for (k = 0; delta > ((c_punycodeBase - c_tmin) * c_tmax) / 2; k += c_punycodeBase) + { + delta /= c_punycodeBase - c_tmin; + } + + Debug.Assert(delta + c_skew != 0, "[IdnMapping.adapt]Expected non-zero delta+skew."); + return (int)(k + (c_punycodeBase - c_tmin + 1) * delta / (delta + c_skew)); + } + + /* EncodeBasic(bcp,flag) forces a basic code point to lowercase */ + /* if flag is false, uppercase if flag is true, and returns */ + /* the resulting code point. The code point is unchanged if it */ + /* is caseless. The behavior is undefined if bcp is not a basic */ + /* code point. */ + + static char EncodeBasic(char bcp) + { + if (HasUpperCaseFlag(bcp)) + bcp += (char)('a' - 'A'); + + return bcp; + } + + // Return whether a punycode code point is flagged as being upper case. + private static bool HasUpperCaseFlag(char punychar) + { + return (punychar >= 'A' && punychar <= 'Z'); + } + + /* EncodeDigit(d,flag) returns the basic code point whose value */ + /* (when used for representing integers) is d, which needs to be in */ + /* the range 0 to punycodeBase-1. The lowercase form is used unless flag is */ + /* true, in which case the uppercase form is used. */ + + private static char EncodeDigit(int d) + { + Debug.Assert(d >= 0 && d < c_punycodeBase, "[IdnMapping.encode_digit]Expected 0 <= d < punycodeBase"); + // 26-35 map to ASCII 0-9 + if (d > 25) return (char)(d - 26 + '0'); + + // 0-25 map to a-z or A-Z + return (char)(d + 'a'); + } + + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/InternalGlobalizationHelper.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/InternalGlobalizationHelper.cs new file mode 100644 index 0000000000..f5eea1b629 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/InternalGlobalizationHelper.cs @@ -0,0 +1,48 @@ +// 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. + +namespace System.Globalization +{ + internal class InternalGlobalizationHelper + { + // Copied from the TimeSpan to be used inside the globalization code and avoid internal dependancy on TimeSpan class + internal static long TimeToTicks(int hour, int minute, int second) + { + // totalSeconds is bounded by 2^31 * 2^12 + 2^31 * 2^8 + 2^31, + // which is less than 2^44, meaning we won't overflow totalSeconds. + long totalSeconds = (long)hour * 3600 + (long)minute * 60 + (long)second; + if (totalSeconds > MaxSeconds || totalSeconds < MinSeconds) + throw new ArgumentOutOfRangeException(null, SR.Overflow_TimeSpanTooLong); + return totalSeconds * TicksPerSecond; + } + + + // + // Define needed constants so globalization code can be independant from any other types + // + + internal const long TicksPerMillisecond = 10000; + internal const long TicksPerTenthSecond = TicksPerMillisecond * 100; + internal const long TicksPerSecond = TicksPerMillisecond * 1000; // 10,000,000 + internal const long MaxSeconds = Int64.MaxValue / TicksPerSecond; + internal const long MinSeconds = Int64.MinValue / TicksPerSecond; + private const int DaysPerYear = 365; + private const int DaysPer4Years = DaysPerYear * 4 + 1; // 1461 + private const int DaysPer100Years = DaysPer4Years * 25 - 1; // 36524 + private const int DaysPer400Years = DaysPer100Years * 4 + 1; // 146097 + private const int DaysTo10000 = DaysPer400Years * 25 - 366; // 3652059 + private const long TicksPerMinute = TicksPerSecond * 60; + private const long TicksPerHour = TicksPerMinute * 60; + private const long TicksPerDay = TicksPerHour * 24; + internal const long MaxTicks = DaysTo10000 * TicksPerDay - 1; + internal const long MinTicks = 0; + internal const long MaxMilliSeconds = Int64.MaxValue / TicksPerMillisecond; + internal const long MinMilliSeconds = Int64.MinValue / TicksPerMillisecond; + + internal const int StringBuilderDefaultCapacity = 16; + + internal const Int64 MaxOffset = TimeSpan.TicksPerHour * 14; + internal const Int64 MinOffset = -MaxOffset; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/JapaneseCalendar.Unix.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/JapaneseCalendar.Unix.cs new file mode 100644 index 0000000000..51ff8095a3 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/JapaneseCalendar.Unix.cs @@ -0,0 +1,96 @@ +// 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.Collections.Generic; +using System.Diagnostics; + +namespace System.Globalization +{ + public partial class JapaneseCalendar : Calendar + { + private static EraInfo[] GetJapaneseEras() + { + if (GlobalizationMode.Invariant) + { + return null; + } + + string[] eraNames; + if (!CalendarData.EnumCalendarInfo("ja-JP", CalendarId.JAPAN, CalendarDataType.EraNames, out eraNames)) + { + return null; + } + + string[] abbrevEnglishEraNames; + if (!CalendarData.EnumCalendarInfo("en", CalendarId.JAPAN, CalendarDataType.AbbrevEraNames, out abbrevEnglishEraNames)) + { + return null; + } + + List eras = new List(); + int lastMaxYear = GregorianCalendar.MaxYear; + + int latestEra = Interop.GlobalizationInterop.GetLatestJapaneseEra(); + for (int i = latestEra; i >= 0; i--) + { + DateTime dt; + if (!GetJapaneseEraStartDate(i, out dt)) + { + return null; + } + + if (dt < JapaneseCalendar.calendarMinValue) + { + // only populate the Eras that are valid JapaneseCalendar date times + break; + } + + eras.Add(new EraInfo(i, dt.Year, dt.Month, dt.Day, dt.Year - 1, 1, lastMaxYear - dt.Year + 1, + eraNames[i], GetAbbreviatedEraName(eraNames, i), abbrevEnglishEraNames[i])); + + lastMaxYear = dt.Year; + } + + // remap the Era numbers, now that we know how many there will be + for (int i = 0; i < eras.Count; i++) + { + eras[i].era = eras.Count - i; + } + + return eras.ToArray(); + } + + // PAL Layer ends here + + private static string GetAbbreviatedEraName(string[] eraNames, int eraIndex) + { + // This matches the behavior on Win32 - only returning the first character of the era name. + // See Calendar.EraAsString(Int32) - https://msdn.microsoft.com/en-us/library/windows/apps/br206751.aspx + return eraNames[eraIndex].Substring(0, 1); + } + + private static bool GetJapaneseEraStartDate(int era, out DateTime dateTime) + { + Debug.Assert(!GlobalizationMode.Invariant); + + dateTime = default(DateTime); + + int startYear; + int startMonth; + int startDay; + bool result = Interop.GlobalizationInterop.GetJapaneseEraStartDate( + era, + out startYear, + out startMonth, + out startDay); + + if (result) + { + dateTime = new DateTime(startYear, startMonth, startDay); + } + + return result; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/JapaneseCalendar.Win32.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/JapaneseCalendar.Win32.cs new file mode 100644 index 0000000000..9ea6c21c2e --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/JapaneseCalendar.Win32.cs @@ -0,0 +1,210 @@ +// 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.Diagnostics; + +using Microsoft.Win32; + +namespace System.Globalization +{ + public partial class JapaneseCalendar : Calendar + { + private const string c_japaneseErasHive = @"System\CurrentControlSet\Control\Nls\Calendars\Japanese\Eras"; + private const string c_japaneseErasHivePermissionList = @"HKEY_LOCAL_MACHINE\" + c_japaneseErasHive; + + // We know about 4 built-in eras, however users may add additional era(s) from the + // registry, by adding values to HKLM\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese\Eras + // + // Registry values look like: + // yyyy.mm.dd=era_abbrev_english_englishabbrev + // + // Where yyyy.mm.dd is the registry value name, and also the date of the era start. + // yyyy, mm, and dd are the year, month & day the era begins (4, 2 & 2 digits long) + // era is the Japanese Era name + // abbrev is the Abbreviated Japanese Era Name + // english is the English name for the Era (unused) + // englishabbrev is the Abbreviated English name for the era. + // . is a delimiter, but the value of . doesn't matter. + // '_' marks the space between the japanese era name, japanese abbreviated era name + // english name, and abbreviated english names. + private static EraInfo[] GetJapaneseEras() + { + // Look in the registry key and see if we can find any ranges + int iFoundEras = 0; + EraInfo[] registryEraRanges = null; + + try + { + // Need to access registry + RegistryKey key = RegistryKey.GetBaseKey(RegistryKey.HKEY_LOCAL_MACHINE).OpenSubKey(c_japaneseErasHive, false); + + // Abort if we didn't find anything + if (key == null) return null; + + // Look up the values in our reg key + String[] valueNames = key.GetValueNames(); + if (valueNames != null && valueNames.Length > 0) + { + registryEraRanges = new EraInfo[valueNames.Length]; + + // Loop through the registry and read in all the values + for (int i = 0; i < valueNames.Length; i++) + { + // See if the era is a valid date + EraInfo era = GetEraFromValue(valueNames[i], key.GetValue(valueNames[i]).ToString()); + + // continue if not valid + if (era == null) continue; + + // Remember we found one. + registryEraRanges[iFoundEras] = era; + iFoundEras++; + } + } + } + catch (System.Security.SecurityException) + { + // If we weren't allowed to read, then just ignore the error + return null; + } + catch (System.IO.IOException) + { + // If key is being deleted just ignore the error + return null; + } + catch (System.UnauthorizedAccessException) + { + // Registry access rights permissions, just ignore the error + return null; + } + + // + // If we didn't have valid eras, then fail + // should have at least 4 eras + // + if (iFoundEras < 4) return null; + + // + // Now we have eras, clean them up. + // + // Clean up array length + Array.Resize(ref registryEraRanges, iFoundEras); + + // Sort them + Array.Sort(registryEraRanges, CompareEraRanges); + + // Clean up era information + for (int i = 0; i < registryEraRanges.Length; i++) + { + // eras count backwards from length to 1 (and are 1 based indexes into string arrays) + registryEraRanges[i].era = registryEraRanges.Length - i; + + // update max era year + if (i == 0) + { + // First range is 'til the end of the calendar + registryEraRanges[0].maxEraYear = GregorianCalendar.MaxYear - registryEraRanges[0].yearOffset; + } + else + { + // Rest are until the next era (remember most recent era is first in array) + registryEraRanges[i].maxEraYear = registryEraRanges[i - 1].yearOffset + 1 - registryEraRanges[i].yearOffset; + } + } + + // Return our ranges + return registryEraRanges; + } + + // + // Compare two era ranges, eg just the ticks + // Remember the era array is supposed to be in reverse chronological order + // + private static int CompareEraRanges(EraInfo a, EraInfo b) + { + return b.ticks.CompareTo(a.ticks); + } + + // + // GetEraFromValue + // + // Parse the registry value name/data pair into an era + // + // Registry values look like: + // yyyy.mm.dd=era_abbrev_english_englishabbrev + // + // Where yyyy.mm.dd is the registry value name, and also the date of the era start. + // yyyy, mm, and dd are the year, month & day the era begins (4, 2 & 2 digits long) + // era is the Japanese Era name + // abbrev is the Abbreviated Japanese Era Name + // english is the English name for the Era (unused) + // englishabbrev is the Abbreviated English name for the era. + // . is a delimiter, but the value of . doesn't matter. + // '_' marks the space between the japanese era name, japanese abbreviated era name + // english name, and abbreviated english names. + private static EraInfo GetEraFromValue(String value, String data) + { + // Need inputs + if (value == null || data == null) return null; + + // + // Get Date + // + // Need exactly 10 characters in name for date + // yyyy.mm.dd although the . can be any character + if (value.Length != 10) return null; + + int year; + int month; + int day; + + ReadOnlySpan valueSpan = value.AsReadOnlySpan(); + if (!Int32.TryParse(valueSpan.Slice(0, 4), NumberStyles.None, NumberFormatInfo.InvariantInfo, out year) || + !Int32.TryParse(valueSpan.Slice(5, 2), NumberStyles.None, NumberFormatInfo.InvariantInfo, out month) || + !Int32.TryParse(valueSpan.Slice(8, 2), NumberStyles.None, NumberFormatInfo.InvariantInfo, out day)) + { + // Couldn't convert integer, fail + return null; + } + + // + // Get Strings + // + // Needs to be a certain length e_a_E_A at least (7 chars, exactly 4 groups) + String[] names = data.Split('_'); + + // Should have exactly 4 parts + // 0 - Era Name + // 1 - Abbreviated Era Name + // 2 - English Era Name + // 3 - Abbreviated English Era Name + if (names.Length != 4) return null; + + // Each part should have data in it + if (names[0].Length == 0 || + names[1].Length == 0 || + names[2].Length == 0 || + names[3].Length == 0) + return null; + + // + // Now we have an era we can build + // Note that the era # and max era year need cleaned up after sorting + // Don't use the full English Era Name (names[2]) + // + return new EraInfo(0, year, month, day, year - 1, 1, 0, + names[0], names[1], names[3]); + } + + // PAL Layer ends here + + private static string[] s_japaneseErasEnglishNames = new String[] { "M", "T", "S", "H" }; + + private static string GetJapaneseEnglishEraName(int era) + { + Debug.Assert(era > 0); + return era <= s_japaneseErasEnglishNames.Length ? s_japaneseErasEnglishNames[era - 1] : " "; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/JapaneseCalendar.WinRT.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/JapaneseCalendar.WinRT.cs new file mode 100644 index 0000000000..6a9df97200 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/JapaneseCalendar.WinRT.cs @@ -0,0 +1,62 @@ +// 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.Diagnostics; + +using Internal.Runtime.Augments; + +namespace System.Globalization +{ + public partial class JapaneseCalendar : Calendar + { + private static EraInfo[] GetJapaneseEras() + { + int erasCount = WinRTInterop.Callbacks.GetJapaneseEraCount(); + if (erasCount < 4) + { + return null; + } + + EraInfo[] eras = new EraInfo[erasCount]; + int lastMaxYear = GregorianCalendar.MaxYear; + + for (int i = erasCount; i > 0; i--) + { + DateTimeOffset dateOffset; + + string eraName; + string abbreviatedEraName; + + if (!GetJapaneseEraInfo(i, out dateOffset, out eraName, out abbreviatedEraName)) + { + return null; + } + + DateTime dt = new DateTime(dateOffset.Ticks); + + eras[erasCount - i] = new EraInfo(i, dt.Year, dt.Month, dt.Day, dt.Year - 1, 1, lastMaxYear - dt.Year + 1, + eraName, abbreviatedEraName, GetJapaneseEnglishEraName(i)); // era #4 start year/month/day, yearOffset, minEraYear + + lastMaxYear = dt.Year; + } + + return eras; + } + + // PAL Layer ends here + + private static string[] JapaneseErasEnglishNames = new String[] { "M", "T", "S", "H" }; + + private static string GetJapaneseEnglishEraName(int era) + { + Debug.Assert(era > 0); + return era <= JapaneseErasEnglishNames.Length ? JapaneseErasEnglishNames[era - 1] : " "; + } + + private static bool GetJapaneseEraInfo(int era, out DateTimeOffset dateOffset, out string eraName, out string abbreviatedEraName) + { + return WinRTInterop.Callbacks.GetJapaneseEraInfo(era, out dateOffset, out eraName, out abbreviatedEraName); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/JapaneseCalendar.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/JapaneseCalendar.cs new file mode 100644 index 0000000000..50195d7f1c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/JapaneseCalendar.cs @@ -0,0 +1,405 @@ +// 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.Diagnostics.CodeAnalysis; + +namespace System.Globalization +{ + /*=================================JapaneseCalendar========================== + ** + ** JapaneseCalendar is based on Gregorian calendar. The month and day values are the same as + ** Gregorian calendar. However, the year value is an offset to the Gregorian + ** year based on the era. + ** + ** This system is adopted by Emperor Meiji in 1868. The year value is counted based on the reign of an emperor, + ** and the era begins on the day an emperor ascends the throne and continues until his death. + ** The era changes at 12:00AM. + ** + ** For example, the current era is Heisei. It started on 1989/1/8 A.D. Therefore, Gregorian year 1989 is also Heisei 1st. + ** 1989/1/8 A.D. is also Heisei 1st 1/8. + ** + ** Any date in the year during which era is changed can be reckoned in either era. For example, + ** 1989/1/1 can be 1/1 Heisei 1st year or 1/1 Showa 64th year. + ** + ** Note: + ** The DateTime can be represented by the JapaneseCalendar are limited to two factors: + ** 1. The min value and max value of DateTime class. + ** 2. The available era information. + ** + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 1868/09/08 9999/12/31 + ** Japanese Meiji 01/01 Heisei 8011/12/31 + ============================================================================*/ + + + public partial class JapaneseCalendar : Calendar + { + internal static readonly DateTime calendarMinValue = new DateTime(1868, 9, 8); + + + public override DateTime MinSupportedDateTime + { + get + { + return (calendarMinValue); + } + } + + public override DateTime MaxSupportedDateTime + { + get + { + return (DateTime.MaxValue); + } + } + + public override CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.SolarCalendar; + } + } + + // + // Using a field initializer rather than a static constructor so that the whole class can be lazy + // init. + internal static volatile EraInfo[] japaneseEraInfo; + + // + // Read our era info + // + // m_EraInfo must be listed in reverse chronological order. The most recent era + // should be the first element. + // That is, m_EraInfo[0] contains the most recent era. + // + // We know about 4 built-in eras, however users may add additional era(s) from the + // registry, by adding values to HKLM\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese\Eras + // we don't read the registry and instead we call WinRT to get the needed informatio + // + // Registry values look like: + // yyyy.mm.dd=era_abbrev_english_englishabbrev + // + // Where yyyy.mm.dd is the registry value name, and also the date of the era start. + // yyyy, mm, and dd are the year, month & day the era begins (4, 2 & 2 digits long) + // era is the Japanese Era name + // abbrev is the Abbreviated Japanese Era Name + // english is the English name for the Era (unused) + // englishabbrev is the Abbreviated English name for the era. + // . is a delimiter, but the value of . doesn't matter. + // '_' marks the space between the japanese era name, japanese abbreviated era name + // english name, and abbreviated english names. + // + internal static EraInfo[] GetEraInfo() + { + // See if we need to build it + if (japaneseEraInfo == null) + { + japaneseEraInfo = GetJapaneseEras(); + // See if we have to use the built-in eras + if (japaneseEraInfo == null) + { + // We know about some built-in ranges + EraInfo[] defaultEraRanges = new EraInfo[4]; + defaultEraRanges[0] = new EraInfo(4, 1989, 1, 8, 1988, 1, GregorianCalendar.MaxYear - 1988, + "\x5e73\x6210", "\x5e73", "H"); // era #4 start year/month/day, yearOffset, minEraYear + defaultEraRanges[1] = new EraInfo(3, 1926, 12, 25, 1925, 1, 1989 - 1925, + "\x662d\x548c", "\x662d", "S"); // era #3,start year/month/day, yearOffset, minEraYear + defaultEraRanges[2] = new EraInfo(2, 1912, 7, 30, 1911, 1, 1926 - 1911, + "\x5927\x6b63", "\x5927", "T"); // era #2,start year/month/day, yearOffset, minEraYear + defaultEraRanges[3] = new EraInfo(1, 1868, 1, 1, 1867, 1, 1912 - 1867, + "\x660e\x6cbb", "\x660e", "M"); // era #1,start year/month/day, yearOffset, minEraYear + + // Remember the ranges we built + japaneseEraInfo = defaultEraRanges; + } + } + + // return the era we found/made + return japaneseEraInfo; + } + + internal static volatile Calendar s_defaultInstance; + internal GregorianCalendarHelper helper; + + /*=================================GetDefaultInstance========================== + **Action: Internal method to provide a default intance of JapaneseCalendar. Used by NLS+ implementation + ** and other calendars. + **Returns: + **Arguments: + **Exceptions: + ============================================================================*/ + + internal static Calendar GetDefaultInstance() + { + if (s_defaultInstance == null) + { + s_defaultInstance = new JapaneseCalendar(); + } + return (s_defaultInstance); + } + + + public JapaneseCalendar() + { + try + { + new CultureInfo("ja-JP"); + } + catch (ArgumentException e) + { + throw new TypeInitializationException(this.GetType().ToString(), e); + } + helper = new GregorianCalendarHelper(this, GetEraInfo()); + } + + internal override CalendarId ID + { + get + { + return CalendarId.JAPAN; + } + } + + + public override DateTime AddMonths(DateTime time, int months) + { + return (helper.AddMonths(time, months)); + } + + + public override DateTime AddYears(DateTime time, int years) + { + return (helper.AddYears(time, years)); + } + + /*=================================GetDaysInMonth========================== + **Action: Returns the number of days in the month given by the year and month arguments. + **Returns: The number of days in the given month. + **Arguments: + ** year The year in Japanese calendar. + ** month The month + ** era The Japanese era value. + **Exceptions + ** ArgumentException If month is less than 1 or greater * than 12. + ============================================================================*/ + + + public override int GetDaysInMonth(int year, int month, int era) + { + return (helper.GetDaysInMonth(year, month, era)); + } + + + public override int GetDaysInYear(int year, int era) + { + return (helper.GetDaysInYear(year, era)); + } + + + public override int GetDayOfMonth(DateTime time) + { + return (helper.GetDayOfMonth(time)); + } + + + public override DayOfWeek GetDayOfWeek(DateTime time) + { + return (helper.GetDayOfWeek(time)); + } + + + public override int GetDayOfYear(DateTime time) + { + return (helper.GetDayOfYear(time)); + } + + + public override int GetMonthsInYear(int year, int era) + { + return (helper.GetMonthsInYear(year, era)); + } + + + public override int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek) + { + return (helper.GetWeekOfYear(time, rule, firstDayOfWeek)); + } + + /*=================================GetEra========================== + **Action: Get the era value of the specified time. + **Returns: The era value for the specified time. + **Arguments: + ** time the specified date time. + **Exceptions: ArgumentOutOfRangeException if time is out of the valid era ranges. + ============================================================================*/ + + + public override int GetEra(DateTime time) + { + return (helper.GetEra(time)); + } + + + public override int GetMonth(DateTime time) + { + return (helper.GetMonth(time)); + } + + + public override int GetYear(DateTime time) + { + return (helper.GetYear(time)); + } + + + public override bool IsLeapDay(int year, int month, int day, int era) + { + return (helper.IsLeapDay(year, month, day, era)); + } + + + public override bool IsLeapYear(int year, int era) + { + return (helper.IsLeapYear(year, era)); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this calendar does not have leap month, or this year is not a leap year. + // + + public override int GetLeapMonth(int year, int era) + { + return (helper.GetLeapMonth(year, era)); + } + + + public override bool IsLeapMonth(int year, int month, int era) + { + return (helper.IsLeapMonth(year, month, era)); + } + + + public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) + { + return (helper.ToDateTime(year, month, day, hour, minute, second, millisecond, era)); + } + + // For Japanese calendar, four digit year is not used. Few emperors will live for more than one hundred years. + // Therefore, for any two digit number, we just return the original number. + + public override int ToFourDigitYear(int year) + { + if (year <= 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedPosNum); + } + + if (year > helper.MaxYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + helper.MaxYear)); + } + return (year); + } + + + public override int[] Eras + { + get + { + return (helper.Eras); + } + } + + // + // Return the various era strings + // Note: The arrays are backwards of the eras + // + internal static String[] EraNames() + { + EraInfo[] eras = GetEraInfo(); + String[] eraNames = new String[eras.Length]; + + for (int i = 0; i < eras.Length; i++) + { + // Strings are in chronological order, eras are backwards order. + eraNames[i] = eras[eras.Length - i - 1].eraName; + } + + return eraNames; + } + + internal static String[] AbbrevEraNames() + { + EraInfo[] eras = GetEraInfo(); + String[] erasAbbrev = new String[eras.Length]; + + for (int i = 0; i < eras.Length; i++) + { + // Strings are in chronological order, eras are backwards order. + erasAbbrev[i] = eras[eras.Length - i - 1].abbrevEraName; + } + + return erasAbbrev; + } + + internal static String[] EnglishEraNames() + { + EraInfo[] eras = GetEraInfo(); + String[] erasEnglish = new String[eras.Length]; + + for (int i = 0; i < eras.Length; i++) + { + // Strings are in chronological order, eras are backwards order. + erasEnglish[i] = eras[eras.Length - i - 1].englishEraName; + } + + return erasEnglish; + } + + private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 99; + + internal override bool IsValidYear(int year, int era) + { + return helper.IsValidYear(year, era); + } + + public override int TwoDigitYearMax + { + get + { + if (twoDigitYearMax == -1) + { + twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX); + } + return (twoDigitYearMax); + } + + set + { + VerifyWritable(); + if (value < 99 || value > helper.MaxYear) + { + throw new ArgumentOutOfRangeException( + "year", + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 99, + helper.MaxYear)); + } + twoDigitYearMax = value; + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/JapaneseLunisolarCalendar.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/JapaneseLunisolarCalendar.cs new file mode 100644 index 0000000000..e8a2dcd637 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/JapaneseLunisolarCalendar.cs @@ -0,0 +1,302 @@ +// 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. + + +namespace System.Globalization +{ + /* + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 1960/01/28 2050/01/22 + ** JapaneseLunisolar 1960/01/01 2049/12/29 + */ + + public class JapaneseLunisolarCalendar : EastAsianLunisolarCalendar + { + // + // The era value for the current era. + // + + public const int JapaneseEra = 1; + + internal GregorianCalendarHelper helper; + + internal const int MIN_LUNISOLAR_YEAR = 1960; + internal const int MAX_LUNISOLAR_YEAR = 2049; + + internal const int MIN_GREGORIAN_YEAR = 1960; + internal const int MIN_GREGORIAN_MONTH = 1; + internal const int MIN_GREGORIAN_DAY = 28; + + internal const int MAX_GREGORIAN_YEAR = 2050; + internal const int MAX_GREGORIAN_MONTH = 1; + internal const int MAX_GREGORIAN_DAY = 22; + + internal static DateTime minDate = new DateTime(MIN_GREGORIAN_YEAR, MIN_GREGORIAN_MONTH, MIN_GREGORIAN_DAY); + internal static DateTime maxDate = new DateTime((new DateTime(MAX_GREGORIAN_YEAR, MAX_GREGORIAN_MONTH, MAX_GREGORIAN_DAY, 23, 59, 59, 999)).Ticks + 9999); + + public override DateTime MinSupportedDateTime + { + get + { + return (minDate); + } + } + + + public override DateTime MaxSupportedDateTime + { + get + { + return (maxDate); + } + } + protected override int DaysInYearBeforeMinSupportedYear + { + get + { + // 1959 from ChineseLunisolarCalendar + return 354; + } + } + + private static readonly int[,] s_yinfo = + { + /*Y LM Lmon Lday DaysPerMonth D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 D13 #Days + 1960 */ + { 6 , 1 , 28 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384 +1961 */{ 0 , 2 , 15 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355 +1962 */{ 0 , 2 , 5 , 19808 },/* 29 30 29 29 30 30 29 30 29 30 30 29 0 354 +1963 */{ 4 , 1 , 25 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384 +1964 */{ 0 , 2 , 13 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +1965 */{ 0 , 2 , 2 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 0 354 +1966 */{ 3 , 1 , 22 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 29 383 +1967 */{ 0 , 2 , 9 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355 +1968 */{ 7 , 1 , 30 , 27304 },/* 29 30 30 29 30 29 30 29 30 29 30 29 30 384 +1969 */{ 0 , 2 , 17 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1970 */{ 0 , 2 , 6 , 39632 },/* 30 29 29 30 30 29 30 29 30 30 29 30 0 355 +1971 */{ 5 , 1 , 27 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +1972 */{ 0 , 2 , 15 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1973 */{ 0 , 2 , 3 , 42208 },/* 30 29 30 29 29 30 29 29 30 30 30 29 0 354 +1974 */{ 4 , 1 , 23 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384 +1975 */{ 0 , 2 , 11 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +1976 */{ 8 , 1 , 31 , 54600 },/* 30 30 29 30 29 30 29 30 29 30 29 29 30 384 +1977 */{ 0 , 2 , 18 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354 +1978 */{ 0 , 2 , 7 , 54944 },/* 30 30 29 30 29 30 30 29 30 29 30 29 0 355 +1979 */{ 6 , 1 , 28 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384 +1980 */{ 0 , 2 , 16 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1981 */{ 0 , 2 , 5 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +1982 */{ 4 , 1 , 25 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384 +1983 */{ 0 , 2 , 13 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1984 */{ 10 , 2 , 2 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384 +1985 */{ 0 , 2 , 20 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354 +1986 */{ 0 , 2 , 9 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354 +1987 */{ 6 , 1 , 29 , 46504 },/* 30 29 30 30 29 30 29 30 30 29 30 29 30 385 +1988 */{ 0 , 2 , 18 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1989 */{ 0 , 2 , 6 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1990 */{ 5 , 1 , 27 , 18872 },/* 29 30 29 29 30 29 29 30 30 29 30 30 30 384 +1991 */{ 0 , 2 , 15 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1992 */{ 0 , 2 , 4 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354 +1993 */{ 3 , 1 , 23 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383 +1994 */{ 0 , 2 , 10 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355 +1995 */{ 8 , 1 , 31 , 27976 },/* 29 30 30 29 30 30 29 30 29 30 29 29 30 384 +1996 */{ 0 , 2 , 19 , 23248 },/* 29 30 29 30 30 29 30 29 30 30 29 30 0 355 +1997 */{ 0 , 2 , 8 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1998 */{ 5 , 1 , 28 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 29 384 +1999 */{ 0 , 2 , 16 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +2000 */{ 0 , 2 , 5 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354 +2001 */{ 4 , 1 , 24 , 58536 },/* 30 30 30 29 29 30 29 29 30 29 30 29 30 384 +2002 */{ 0 , 2 , 12 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +2003 */{ 0 , 2 , 1 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355 +2004 */{ 2 , 1 , 22 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 +2005 */{ 0 , 2 , 9 , 22208 },/* 29 30 29 30 29 30 30 29 30 30 29 29 0 354 +2006 */{ 7 , 1 , 29 , 43736 },/* 30 29 30 29 30 29 30 29 30 30 29 30 30 385 +2007 */{ 0 , 2 , 18 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 +2008 */{ 0 , 2 , 7 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354 +2009 */{ 5 , 1 , 26 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384 +2010 */{ 0 , 2 , 14 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +2011 */{ 0 , 2 , 3 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354 +2012 */{ 3 , 1 , 23 , 47696 },/* 30 29 30 30 30 29 30 29 29 30 29 30 29 384 +2013 */{ 0 , 2 , 10 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +2014 */{ 9 , 1 , 31 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384 +2015 */{ 0 , 2 , 19 , 19360 },/* 29 30 29 29 30 29 30 30 30 29 30 29 0 354 +2016 */{ 0 , 2 , 8 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355 +2017 */{ 5 , 1 , 28 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384 +2018 */{ 0 , 2 , 16 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +2019 */{ 0 , 2 , 5 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +2020 */{ 4 , 1 , 25 , 46248 },/* 30 29 30 30 29 30 29 29 30 29 30 29 30 384 +2021 */{ 0 , 2 , 12 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +2022 */{ 0 , 2 , 1 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +2023 */{ 2 , 1 , 22 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384 +2024 */{ 0 , 2 , 10 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 +2025 */{ 6 , 1 , 29 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384 +2026 */{ 0 , 2 , 17 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +2027 */{ 0 , 2 , 7 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 0 354 +2028 */{ 5 , 1 , 27 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 29 383 +2029 */{ 0 , 2 , 13 , 55600 },/* 30 30 29 30 30 29 29 30 29 29 30 30 0 355 +2030 */{ 0 , 2 , 3 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 +2031 */{ 3 , 1 , 23 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 29 384 +2032 */{ 0 , 2 , 11 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355 +2033 */{ 11 , 1 , 31 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +2034 */{ 0 , 2 , 19 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +2035 */{ 0 , 2 , 8 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +2036 */{ 6 , 1 , 28 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384 +2037 */{ 0 , 2 , 15 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +2038 */{ 0 , 2 , 4 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354 +2039 */{ 5 , 1 , 24 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 29 384 +2040 */{ 0 , 2 , 12 , 46752 },/* 30 29 30 30 29 30 30 29 30 29 30 29 0 355 +2041 */{ 0 , 2 , 1 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355 +2042 */{ 2 , 1 , 22 , 19160 },/* 29 30 29 29 30 29 30 29 30 30 29 30 30 384 +2043 */{ 0 , 2 , 10 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +2044 */{ 7 , 1 , 30 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384 +2045 */{ 0 , 2 , 17 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +2046 */{ 0 , 2 , 6 , 45648 },/* 30 29 30 30 29 29 30 29 29 30 29 30 0 354 +2047 */{ 5 , 1 , 26 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384 +2048 */{ 0 , 2 , 14 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354 +2049 */{ 0 , 2 , 2 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 0 355 + */ }; + + internal override int MinCalendarYear + { + get + { + return (MIN_LUNISOLAR_YEAR); + } + } + + internal override int MaxCalendarYear + { + get + { + return (MAX_LUNISOLAR_YEAR); + } + } + + internal override DateTime MinDate + { + get + { + return (minDate); + } + } + + internal override DateTime MaxDate + { + get + { + return (maxDate); + } + } + + internal override EraInfo[] CalEraInfo + { + get + { + return (JapaneseCalendar.GetEraInfo()); + } + } + + internal override int GetYearInfo(int lunarYear, int index) + { + if ((lunarYear < MIN_LUNISOLAR_YEAR) || (lunarYear > MAX_LUNISOLAR_YEAR)) + { + throw new ArgumentOutOfRangeException( + "year", + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + MIN_LUNISOLAR_YEAR, + MAX_LUNISOLAR_YEAR)); + } + + return s_yinfo[lunarYear - MIN_LUNISOLAR_YEAR, index]; + } + + internal override int GetYear(int year, DateTime time) + { + return helper.GetYear(year, time); + } + + internal override int GetGregorianYear(int year, int era) + { + return helper.GetGregorianYear(year, era); + } + + // Trim off the eras that are before our date range + private static EraInfo[] TrimEras(EraInfo[] baseEras) + { + EraInfo[] newEras = new EraInfo[baseEras.Length]; + int newIndex = 0; + + // Eras have most recent first, so start with that + for (int i = 0; i < baseEras.Length; i++) + { + // If this one's minimum year is bigger than our maximum year + // then we can't use it. + if (baseEras[i].yearOffset + baseEras[i].minEraYear >= MAX_LUNISOLAR_YEAR) + { + // skip this one. + continue; + } + + // If this one's maximum era is less than our minimum era + // then we've gotten too low in the era #s, so we're done + if (baseEras[i].yearOffset + baseEras[i].maxEraYear < MIN_LUNISOLAR_YEAR) + { + break; + } + + // Wasn't too large or too small, can use this one + newEras[newIndex] = baseEras[i]; + newIndex++; + } + + // If we didn't copy any then something was wrong, just return base + if (newIndex == 0) return baseEras; + + // Resize the output array + Array.Resize(ref newEras, newIndex); + return newEras; + } + + + // Construct an instance of JapaneseLunisolar calendar. + public JapaneseLunisolarCalendar() + { + helper = new GregorianCalendarHelper(this, TrimEras(JapaneseCalendar.GetEraInfo())); + } + + + public override int GetEra(DateTime time) + { + return (helper.GetEra(time)); + } + + internal override CalendarId BaseCalendarID + { + get + { + return (CalendarId.JAPAN); + } + } + + internal override CalendarId ID + { + get + { + return (CalendarId.JAPANESELUNISOLAR); + } + } + + + public override int[] Eras + { + get + { + return (helper.Eras); + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/JulianCalendar.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/JulianCalendar.cs new file mode 100644 index 0000000000..82e4d589d3 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/JulianCalendar.cs @@ -0,0 +1,439 @@ +// 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. + + +namespace System.Globalization +{ + // + // This class implements the Julian calendar. In 48 B.C. Julius Caesar ordered a calendar reform, and this calendar + // is called Julian calendar. It consisted of a solar year of twelve months and of 365 days with an extra day + // every fourth year. + //* + //* Calendar support range: + //* Calendar Minimum Maximum + //* ========== ========== ========== + //* Gregorian 0001/01/01 9999/12/31 + //* Julia 0001/01/03 9999/10/19 + + public class JulianCalendar : Calendar + { + public static readonly int JulianEra = 1; + + private const int DatePartYear = 0; + private const int DatePartDayOfYear = 1; + private const int DatePartMonth = 2; + private const int DatePartDay = 3; + + // Number of days in a non-leap year + private const int JulianDaysPerYear = 365; + // Number of days in 4 years + private const int JulianDaysPer4Years = JulianDaysPerYear * 4 + 1; + + private static readonly int[] s_daysToMonth365 = + { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 + }; + + private static readonly int[] s_daysToMonth366 = + { + 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 + }; + + // Gregorian Calendar 9999/12/31 = Julian Calendar 9999/10/19 + // keep it as variable field for serialization compat. + internal int MaxYear = 9999; + + + public override DateTime MinSupportedDateTime + { + get + { + return (DateTime.MinValue); + } + } + + public override DateTime MaxSupportedDateTime + { + get + { + return (DateTime.MaxValue); + } + } + + public override CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.SolarCalendar; + } + } + + public JulianCalendar() + { + // There is no system setting of TwoDigitYear max, so set the value here. + twoDigitYearMax = 2029; + } + + internal override CalendarId ID + { + get + { + return CalendarId.JULIAN; + } + } + + internal static void CheckEraRange(int era) + { + if (era != CurrentEra && era != JulianEra) + { + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + } + + internal void CheckYearEraRange(int year, int era) + { + CheckEraRange(era); + if (year <= 0 || year > MaxYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + MaxYear)); + } + } + + internal static void CheckMonthRange(int month) + { + if (month < 1 || month > 12) + { + throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month); + } + } + + /*===================================CheckDayRange============================ + **Action: Check for if the day value is valid. + **Returns: + **Arguments: + **Exceptions: + **Notes: + ** Before calling this method, call CheckYearEraRange()/CheckMonthRange() to make + ** sure year/month values are correct. + ============================================================================*/ + + internal static void CheckDayRange(int year, int month, int day) + { + if (year == 1 && month == 1) + { + // The mimimum supported Julia date is Julian 0001/01/03. + if (day < 3) + { + throw new ArgumentOutOfRangeException(null, + SR.ArgumentOutOfRange_BadYearMonthDay); + } + } + bool isLeapYear = (year % 4) == 0; + int[] days = isLeapYear ? s_daysToMonth366 : s_daysToMonth365; + int monthDays = days[month] - days[month - 1]; + if (day < 1 || day > monthDays) + { + throw new ArgumentOutOfRangeException( + nameof(day), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + monthDays)); + } + } + + + // Returns a given date part of this DateTime. This method is used + // to compute the year, day-of-year, month, or day part. + internal static int GetDatePart(long ticks, int part) + { + // Gregorian 1/1/0001 is Julian 1/3/0001. Remember DateTime(0) is refered to Gregorian 1/1/0001. + // The following line convert Gregorian ticks to Julian ticks. + long julianTicks = ticks + TicksPerDay * 2; + // n = number of days since 1/1/0001 + int n = (int)(julianTicks / TicksPerDay); + // y4 = number of whole 4-year periods within 100-year period + int y4 = n / JulianDaysPer4Years; + // n = day number within 4-year period + n -= y4 * JulianDaysPer4Years; + // y1 = number of whole years within 4-year period + int y1 = n / JulianDaysPerYear; + // Last year has an extra day, so decrement result if 4 + if (y1 == 4) y1 = 3; + // If year was requested, compute and return it + if (part == DatePartYear) + { + return (y4 * 4 + y1 + 1); + } + // n = day number within year + n -= y1 * JulianDaysPerYear; + // If day-of-year was requested, return it + if (part == DatePartDayOfYear) + { + return (n + 1); + } + // Leap year calculation looks different from IsLeapYear since y1, y4, + // and y100 are relative to year 1, not year 0 + bool leapYear = (y1 == 3); + int[] days = leapYear ? s_daysToMonth366 : s_daysToMonth365; + // All months have less than 32 days, so n >> 5 is a good conservative + // estimate for the month + int m = (n >> 5) + 1; + // m = 1-based month number + while (n >= days[m]) m++; + // If month was requested, return it + if (part == DatePartMonth) return (m); + // Return 1-based day-of-month + return (n - days[m - 1] + 1); + } + + // Returns the tick count corresponding to the given year, month, and day. + internal static long DateToTicks(int year, int month, int day) + { + int[] days = (year % 4 == 0) ? s_daysToMonth366 : s_daysToMonth365; + int y = year - 1; + int n = y * 365 + y / 4 + days[month - 1] + day - 1; + // Gregorian 1/1/0001 is Julian 1/3/0001. n * TicksPerDay is the ticks in JulianCalendar. + // Therefore, we subtract two days in the following to convert the ticks in JulianCalendar + // to ticks in Gregorian calendar. + return ((n - 2) * TicksPerDay); + } + + + public override DateTime AddMonths(DateTime time, int months) + { + if (months < -120000 || months > 120000) + { + throw new ArgumentOutOfRangeException( + nameof(months), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + -120000, + 120000)); + } + int y = GetDatePart(time.Ticks, DatePartYear); + int m = GetDatePart(time.Ticks, DatePartMonth); + int d = GetDatePart(time.Ticks, DatePartDay); + int i = m - 1 + months; + if (i >= 0) + { + m = i % 12 + 1; + y = y + i / 12; + } + else + { + m = 12 + (i + 1) % 12; + y = y + (i - 11) / 12; + } + int[] daysArray = (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) ? s_daysToMonth366 : s_daysToMonth365; + int days = (daysArray[m] - daysArray[m - 1]); + + if (d > days) + { + d = days; + } + long ticks = DateToTicks(y, m, d) + time.Ticks % TicksPerDay; + Calendar.CheckAddResult(ticks, MinSupportedDateTime, MaxSupportedDateTime); + return (new DateTime(ticks)); + } + + + public override DateTime AddYears(DateTime time, int years) + { + return (AddMonths(time, years * 12)); + } + + + public override int GetDayOfMonth(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartDay)); + } + + + public override DayOfWeek GetDayOfWeek(DateTime time) + { + return ((DayOfWeek)((int)(time.Ticks / TicksPerDay + 1) % 7)); + } + + + public override int GetDayOfYear(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartDayOfYear)); + } + + + public override int GetDaysInMonth(int year, int month, int era) + { + CheckYearEraRange(year, era); + CheckMonthRange(month); + int[] days = (year % 4 == 0) ? s_daysToMonth366 : s_daysToMonth365; + return (days[month] - days[month - 1]); + } + + + public override int GetDaysInYear(int year, int era) + { + // Year/Era range is done in IsLeapYear(). + return (IsLeapYear(year, era) ? 366 : 365); + } + + + public override int GetEra(DateTime time) + { + return (JulianEra); + } + + + public override int GetMonth(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartMonth)); + } + + + public override int[] Eras + { + get + { + return (new int[] { JulianEra }); + } + } + + + public override int GetMonthsInYear(int year, int era) + { + CheckYearEraRange(year, era); + return (12); + } + + + public override int GetYear(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartYear)); + } + + + public override bool IsLeapDay(int year, int month, int day, int era) + { + CheckMonthRange(month); + // Year/Era range check is done in IsLeapYear(). + if (IsLeapYear(year, era)) + { + CheckDayRange(year, month, day); + return (month == 2 && day == 29); + } + CheckDayRange(year, month, day); + return (false); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this calendar does not have leap month, or this year is not a leap year. + // + + public override int GetLeapMonth(int year, int era) + { + CheckYearEraRange(year, era); + return (0); + } + + + public override bool IsLeapMonth(int year, int month, int era) + { + CheckYearEraRange(year, era); + CheckMonthRange(month); + return (false); + } + + // Checks whether a given year in the specified era is a leap year. This method returns true if + // year is a leap year, or false if not. + // + + public override bool IsLeapYear(int year, int era) + { + CheckYearEraRange(year, era); + return (year % 4 == 0); + } + + + public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) + { + CheckYearEraRange(year, era); + CheckMonthRange(month); + CheckDayRange(year, month, day); + if (millisecond < 0 || millisecond >= MillisPerSecond) + { + throw new ArgumentOutOfRangeException( + nameof(millisecond), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 0, + MillisPerSecond - 1)); + } + + if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >= 0 && second < 60) + { + return new DateTime(DateToTicks(year, month, day) + (new TimeSpan(0, hour, minute, second, millisecond)).Ticks); + } + else + { + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadHourMinuteSecond); + } + } + + + public override int TwoDigitYearMax + { + get + { + return (twoDigitYearMax); + } + + set + { + VerifyWritable(); + if (value < 99 || value > MaxYear) + { + throw new ArgumentOutOfRangeException( + "year", + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 99, + MaxYear)); + } + twoDigitYearMax = value; + } + } + + + public override int ToFourDigitYear(int year) + { + if (year < 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedNonNegNum); + } + + if (year > MaxYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Bounds_Lower_Upper, + 1, + MaxYear)); + } + return (base.ToFourDigitYear(year)); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/KoreanCalendar.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/KoreanCalendar.cs new file mode 100644 index 0000000000..9168b664e0 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/KoreanCalendar.cs @@ -0,0 +1,262 @@ +// 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.Diagnostics.CodeAnalysis; + +namespace System.Globalization +{ + /*=================================KoreanCalendar========================== + ** + ** Korean calendar is based on the Gregorian calendar. And the year is an offset to Gregorian calendar. + ** That is, + ** Korean year = Gregorian year + 2333. So 2000/01/01 A.D. is Korean 4333/01/01 + ** + ** 0001/1/1 A.D. is Korean year 2334. + ** + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 0001/01/01 9999/12/31 + ** Korean 2334/01/01 12332/12/31 + ============================================================================*/ + + + public class KoreanCalendar : Calendar + { + // + // The era value for the current era. + // + + public const int KoreanEra = 1; + + // Since + // Gregorian Year = Era Year + yearOffset + // Gregorian Year 1 is Korean year 2334, so that + // 1 = 2334 + yearOffset + // So yearOffset = -2333 + // Gregorian year 2001 is Korean year 4334. + + //m_EraInfo[0] = new EraInfo(1, new DateTime(1, 1, 1).Ticks, -2333, 2334, GregorianCalendar.MaxYear + 2333); + + // Initialize our era info. + internal static EraInfo[] koreanEraInfo = new EraInfo[] { + new EraInfo( 1, 1, 1, 1, -2333, 2334, GregorianCalendar.MaxYear + 2333) // era #, start year/month/day, yearOffset, minEraYear + }; + + internal GregorianCalendarHelper helper; + + + public override DateTime MinSupportedDateTime + { + get + { + return (DateTime.MinValue); + } + } + + public override DateTime MaxSupportedDateTime + { + get + { + return (DateTime.MaxValue); + } + } + + public override CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.SolarCalendar; + } + } + + public KoreanCalendar() + { + try + { + new CultureInfo("ko-KR"); + } + catch (ArgumentException e) + { + throw new TypeInitializationException(this.GetType().ToString(), e); + } + helper = new GregorianCalendarHelper(this, koreanEraInfo); + } + + internal override CalendarId ID + { + get + { + return CalendarId.KOREA; + } + } + + + public override DateTime AddMonths(DateTime time, int months) + { + return (helper.AddMonths(time, months)); + } + + + public override DateTime AddYears(DateTime time, int years) + { + return (helper.AddYears(time, years)); + } + + /*=================================GetDaysInMonth========================== + **Action: Returns the number of days in the month given by the year and month arguments. + **Returns: The number of days in the given month. + **Arguments: + ** year The year in Korean calendar. + ** month The month + ** era The Japanese era value. + **Exceptions + ** ArgumentException If month is less than 1 or greater * than 12. + ============================================================================*/ + + + public override int GetDaysInMonth(int year, int month, int era) + { + return (helper.GetDaysInMonth(year, month, era)); + } + + + public override int GetDaysInYear(int year, int era) + { + return (helper.GetDaysInYear(year, era)); + } + + + public override int GetDayOfMonth(DateTime time) + { + return (helper.GetDayOfMonth(time)); + } + + + public override DayOfWeek GetDayOfWeek(DateTime time) + { + return (helper.GetDayOfWeek(time)); + } + + + public override int GetDayOfYear(DateTime time) + { + return (helper.GetDayOfYear(time)); + } + + + public override int GetMonthsInYear(int year, int era) + { + return (helper.GetMonthsInYear(year, era)); + } + + + public override int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek) + { + return (helper.GetWeekOfYear(time, rule, firstDayOfWeek)); + } + + + public override int GetEra(DateTime time) + { + return (helper.GetEra(time)); + } + + public override int GetMonth(DateTime time) + { + return (helper.GetMonth(time)); + } + + + public override int GetYear(DateTime time) + { + return (helper.GetYear(time)); + } + + + public override bool IsLeapDay(int year, int month, int day, int era) + { + return (helper.IsLeapDay(year, month, day, era)); + } + + + public override bool IsLeapYear(int year, int era) + { + return (helper.IsLeapYear(year, era)); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this calendar does not have leap month, or this year is not a leap year. + // + + public override int GetLeapMonth(int year, int era) + { + return (helper.GetLeapMonth(year, era)); + } + + + public override bool IsLeapMonth(int year, int month, int era) + { + return (helper.IsLeapMonth(year, month, era)); + } + + + public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) + { + return (helper.ToDateTime(year, month, day, hour, minute, second, millisecond, era)); + } + + + public override int[] Eras + { + get + { + return (helper.Eras); + } + } + + private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 4362; + + + public override int TwoDigitYearMax + { + get + { + if (twoDigitYearMax == -1) + { + twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX); + } + return (twoDigitYearMax); + } + + set + { + VerifyWritable(); + if (value < 99 || value > helper.MaxYear) + { + throw new ArgumentOutOfRangeException( + "year", + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 99, + helper.MaxYear)); + } + twoDigitYearMax = value; + } + } + + + public override int ToFourDigitYear(int year) + { + if (year < 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedNonNegNum); + } + + return (helper.ToFourDigitYear(year, this.TwoDigitYearMax)); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/KoreanLunisolarCalendar.cs.REMOVED.git-id b/external/corefx/src/Common/src/CoreLib/System/Globalization/KoreanLunisolarCalendar.cs.REMOVED.git-id new file mode 100644 index 0000000000..184ae46bbb --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/KoreanLunisolarCalendar.cs.REMOVED.git-id @@ -0,0 +1 @@ +8cdade866c50ba78d20e7913ee0c9d88b0d1e0ed \ No newline at end of file diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/LocaleData.Unix.cs.REMOVED.git-id b/external/corefx/src/Common/src/CoreLib/System/Globalization/LocaleData.Unix.cs.REMOVED.git-id new file mode 100644 index 0000000000..e3bad5c749 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/LocaleData.Unix.cs.REMOVED.git-id @@ -0,0 +1 @@ +ccf1078ac6369f6e9dfd610d27cdc94b588fc84d \ No newline at end of file diff --git a/external/corert/src/System.Private.CoreLib/src/System/Text/Normalization.Unix.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/Normalization.Unix.cs similarity index 81% rename from external/corert/src/System.Private.CoreLib/src/System/Text/Normalization.Unix.cs rename to external/corefx/src/Common/src/CoreLib/System/Globalization/Normalization.Unix.cs index c999441027..a25c1b9380 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Text/Normalization.Unix.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/Normalization.Unix.cs @@ -2,15 +2,22 @@ // 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.Security; +using System.Diagnostics; using System.Text; -namespace System.Text +namespace System.Globalization { - static partial class Normalization + internal static partial class Normalization { - public static bool IsNormalized(this string strInput, NormalizationForm normalizationForm) + internal static bool IsNormalized(string strInput, NormalizationForm normalizationForm) { + if (GlobalizationMode.Invariant) + { + // In Invariant mode we assume all characters are normalized. + // This is because we don't support any linguistic operation on the strings + return true; + } + ValidateArguments(strInput, normalizationForm); int ret = Interop.GlobalizationInterop.IsNormalized(normalizationForm, strInput, strInput.Length); @@ -23,8 +30,15 @@ namespace System.Text return ret == 1; } - public static string Normalize(this string strInput, NormalizationForm normalizationForm) + internal static string Normalize(string strInput, NormalizationForm normalizationForm) { + if (GlobalizationMode.Invariant) + { + // In Invariant mode we assume all characters are normalized. + // This is because we don't support any linguistic operation on the strings + return strInput; + } + ValidateArguments(strInput, normalizationForm); char[] buf = new char[strInput.Length]; @@ -55,10 +69,7 @@ namespace System.Text private static void ValidateArguments(string strInput, NormalizationForm normalizationForm) { - if (strInput == null) - { - throw new ArgumentNullException(nameof(strInput)); - } + Debug.Assert(strInput != null); if (normalizationForm != NormalizationForm.FormC && normalizationForm != NormalizationForm.FormD && normalizationForm != NormalizationForm.FormKC && normalizationForm != NormalizationForm.FormKD) diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/Normalization.Windows.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/Normalization.Windows.cs new file mode 100644 index 0000000000..8418c53b6c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/Normalization.Windows.cs @@ -0,0 +1,148 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; + +namespace System.Globalization +{ + internal static partial class Normalization + { + internal static bool IsNormalized(string strInput, NormalizationForm normalizationForm) + { + if (GlobalizationMode.Invariant) + { + // In Invariant mode we assume all characters are normalized. + // This is because we don't support any linguistic operation on the strings + return true; + } + + Debug.Assert(strInput != null); + + // The only way to know if IsNormalizedString failed is through checking the Win32 last error + // IsNormalizedString pinvoke has SetLastError attribute property which will set the last error + // to 0 (ERROR_SUCCESS) before executing the calls. + bool result = Interop.Normaliz.IsNormalizedString((int)normalizationForm, strInput, strInput.Length); + + int lastError = Marshal.GetLastWin32Error(); + switch (lastError) + { + case Interop.Errors.ERROR_SUCCESS: + break; + + case Interop.Errors.ERROR_INVALID_PARAMETER: + case Interop.Errors.ERROR_NO_UNICODE_TRANSLATION: + if (normalizationForm != NormalizationForm.FormC && + normalizationForm != NormalizationForm.FormD && + normalizationForm != NormalizationForm.FormKC && + normalizationForm != NormalizationForm.FormKD) + { + throw new ArgumentException(SR.Argument_InvalidNormalizationForm, nameof(normalizationForm)); + } + + throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex, nameof(strInput)); + + case Interop.Errors.ERROR_NOT_ENOUGH_MEMORY: + throw new OutOfMemoryException(); + + default: + throw new InvalidOperationException(SR.Format(SR.UnknownError_Num, lastError)); + } + + return result; + } + + internal static string Normalize(string strInput, NormalizationForm normalizationForm) + { + if (GlobalizationMode.Invariant) + { + // In Invariant mode we assume all characters are normalized. + // This is because we don't support any linguistic operation on the strings + return strInput; + } + + Debug.Assert(strInput != null); + + // we depend on Win32 last error when calling NormalizeString + // NormalizeString pinvoke has SetLastError attribute property which will set the last error + // to 0 (ERROR_SUCCESS) before executing the calls. + + // Guess our buffer size first + int iLength = Interop.Normaliz.NormalizeString((int)normalizationForm, strInput, strInput.Length, null, 0); + + int lastError = Marshal.GetLastWin32Error(); + // Could have an error (actually it'd be quite hard to have an error here) + if ((lastError != Interop.Errors.ERROR_SUCCESS) || iLength < 0) + { + if (lastError == Interop.Errors.ERROR_INVALID_PARAMETER) + { + if (normalizationForm != NormalizationForm.FormC && + normalizationForm != NormalizationForm.FormD && + normalizationForm != NormalizationForm.FormKC && + normalizationForm != NormalizationForm.FormKD) + { + throw new ArgumentException(SR.Argument_InvalidNormalizationForm, nameof(normalizationForm)); + } + + throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex, nameof(strInput)); + } + + // We shouldn't really be able to get here..., guessing length is + // a trivial math function... + // Can't really be Out of Memory, but just in case: + if (lastError == Interop.Errors.ERROR_NOT_ENOUGH_MEMORY) + throw new OutOfMemoryException(); + + // Who knows what happened? Not us! + throw new InvalidOperationException(SR.Format(SR.UnknownError_Num, lastError)); + } + + // Don't break for empty strings (only possible for D & KD and not really possible at that) + if (iLength == 0) return string.Empty; + + // Someplace to stick our buffer + char[] cBuffer = null; + + for (;;) + { + // (re)allocation buffer and normalize string + cBuffer = new char[iLength]; + + // NormalizeString pinvoke has SetLastError attribute property which will set the last error + // to 0 (ERROR_SUCCESS) before executing the calls. + iLength = Interop.Normaliz.NormalizeString((int)normalizationForm, strInput, strInput.Length, cBuffer, cBuffer.Length); + lastError = Marshal.GetLastWin32Error(); + + if (lastError == Interop.Errors.ERROR_SUCCESS) + break; + + // Could have an error (actually it'd be quite hard to have an error here) + switch (lastError) + { + // Do appropriate stuff for the individual errors: + case Interop.Errors.ERROR_INSUFFICIENT_BUFFER: + iLength = Math.Abs(iLength); + Debug.Assert(iLength > cBuffer.Length, "Buffer overflow should have iLength > cBuffer.Length"); + continue; + + case Interop.Errors.ERROR_INVALID_PARAMETER: + case Interop.Errors.ERROR_NO_UNICODE_TRANSLATION: + // Illegal code point or order found. Ie: FFFE or D800 D800, etc. + throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex, nameof(strInput)); + + case Interop.Errors.ERROR_NOT_ENOUGH_MEMORY: + throw new OutOfMemoryException(); + + default: + // We shouldn't get here... + throw new InvalidOperationException(SR.Format(SR.UnknownError_Num, lastError)); + } + } + + // Copy our buffer into our new string, which will be the appropriate size + return new string(cBuffer, 0, iLength); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/NumberFormatInfo.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/NumberFormatInfo.cs new file mode 100644 index 0000000000..a98023dc96 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/NumberFormatInfo.cs @@ -0,0 +1,842 @@ +// 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.Text; + +namespace System.Globalization +{ + // + // Property Default Description + // PositiveSign '+' Character used to indicate positive values. + // NegativeSign '-' Character used to indicate negative values. + // NumberDecimalSeparator '.' The character used as the decimal separator. + // NumberGroupSeparator ',' The character used to separate groups of + // digits to the left of the decimal point. + // NumberDecimalDigits 2 The default number of decimal places. + // NumberGroupSizes 3 The number of digits in each group to the + // left of the decimal point. + // NaNSymbol "NaN" The string used to represent NaN values. + // PositiveInfinitySymbol"Infinity" The string used to represent positive + // infinities. + // NegativeInfinitySymbol"-Infinity" The string used to represent negative + // infinities. + // + // + // + // Property Default Description + // CurrencyDecimalSeparator '.' The character used as the decimal + // separator. + // CurrencyGroupSeparator ',' The character used to separate groups + // of digits to the left of the decimal + // point. + // CurrencyDecimalDigits 2 The default number of decimal places. + // CurrencyGroupSizes 3 The number of digits in each group to + // the left of the decimal point. + // CurrencyPositivePattern 0 The format of positive values. + // CurrencyNegativePattern 0 The format of negative values. + // CurrencySymbol "$" String used as local monetary symbol. + // + + sealed public class NumberFormatInfo : IFormatProvider, ICloneable + { + // invariantInfo is constant irrespective of your current culture. + private static volatile NumberFormatInfo s_invariantInfo; + + // READTHIS READTHIS READTHIS + // This class has an exact mapping onto a native structure defined in COMNumber.cpp + // DO NOT UPDATE THIS WITHOUT UPDATING THAT STRUCTURE. IF YOU ADD BOOL, ADD THEM AT THE END. + // ALSO MAKE SURE TO UPDATE mscorlib.h in the VM directory to check field offsets. + // READTHIS READTHIS READTHIS + internal int[] numberGroupSizes = new int[] { 3 }; + internal int[] currencyGroupSizes = new int[] { 3 }; + internal int[] percentGroupSizes = new int[] { 3 }; + internal String positiveSign = "+"; + internal String negativeSign = "-"; + internal String numberDecimalSeparator = "."; + internal String numberGroupSeparator = ","; + internal String currencyGroupSeparator = ","; + internal String currencyDecimalSeparator = "."; + internal String currencySymbol = "\x00a4"; // U+00a4 is the symbol for International Monetary Fund. + internal String nanSymbol = "NaN"; + internal String positiveInfinitySymbol = "Infinity"; + internal String negativeInfinitySymbol = "-Infinity"; + internal String percentDecimalSeparator = "."; + internal String percentGroupSeparator = ","; + internal String percentSymbol = "%"; + internal String perMilleSymbol = "\u2030"; + + + internal String[] nativeDigits = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; + + internal int numberDecimalDigits = 2; + internal int currencyDecimalDigits = 2; + internal int currencyPositivePattern = 0; + internal int currencyNegativePattern = 0; + internal int numberNegativePattern = 1; + internal int percentPositivePattern = 0; + internal int percentNegativePattern = 0; + internal int percentDecimalDigits = 2; + + internal int digitSubstitution = (int)DigitShapes.None; + + internal bool isReadOnly = false; + + // Is this NumberFormatInfo for invariant culture? + internal bool m_isInvariant = false; + + public NumberFormatInfo() : this(null) + { + } + + private static void VerifyDecimalSeparator(String decSep, String propertyName) + { + if (decSep == null) + { + throw new ArgumentNullException(propertyName, + SR.ArgumentNull_String); + } + + if (decSep.Length == 0) + { + throw new ArgumentException(SR.Argument_EmptyDecString); + } + } + + private static void VerifyGroupSeparator(String groupSep, String propertyName) + { + if (groupSep == null) + { + throw new ArgumentNullException(propertyName, + SR.ArgumentNull_String); + } + } + + private static void VerifyNativeDigits(string[] nativeDig, string propertyName) + { + if (nativeDig == null) + { + throw new ArgumentNullException(propertyName, SR.ArgumentNull_Array); + } + + if (nativeDig.Length != 10) + { + throw new ArgumentException(SR.Argument_InvalidNativeDigitCount, propertyName); + } + + for (int i = 0; i < nativeDig.Length; i++) + { + if (nativeDig[i] == null) + { + throw new ArgumentNullException(propertyName, SR.ArgumentNull_ArrayValue); + } + + if (nativeDig[i].Length != 1) + { + if (nativeDig[i].Length != 2) + { + // Not 1 or 2 UTF-16 code points + throw new ArgumentException(SR.Argument_InvalidNativeDigitValue, propertyName); + } + else if (!char.IsSurrogatePair(nativeDig[i][0], nativeDig[i][1])) + { + // 2 UTF-6 code points, but not a surrogate pair + throw new ArgumentException(SR.Argument_InvalidNativeDigitValue, propertyName); + } + } + + if (CharUnicodeInfo.GetDecimalDigitValue(nativeDig[i], 0) != i && + CharUnicodeInfo.GetUnicodeCategory(nativeDig[i], 0) != UnicodeCategory.PrivateUse) + { + // Not the appropriate digit according to the Unicode data properties + // (Digit 0 must be a 0, etc.). + throw new ArgumentException(SR.Argument_InvalidNativeDigitValue, propertyName); + } + } + } + + private static void VerifyDigitSubstitution(DigitShapes digitSub, string propertyName) + { + switch (digitSub) + { + case DigitShapes.Context: + case DigitShapes.None: + case DigitShapes.NativeNational: + // Success. + break; + + default: + throw new ArgumentException(SR.Argument_InvalidDigitSubstitution, propertyName); + } + } + + internal NumberFormatInfo(CultureData cultureData) + { + if (cultureData != null) + { + // We directly use fields here since these data is coming from data table or Win32, so we + // don't need to verify their values (except for invalid parsing situations). + cultureData.GetNFIValues(this); + + if (cultureData.IsInvariantCulture) + { + // For invariant culture + this.m_isInvariant = true; + } + } + } + + private void VerifyWritable() + { + if (isReadOnly) + { + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + } + } + + // Returns a default NumberFormatInfo that will be universally + // supported and constant irrespective of the current culture. + // Used by FromString methods. + // + + public static NumberFormatInfo InvariantInfo + { + get + { + if (s_invariantInfo == null) + { + // Lazy create the invariant info. This cannot be done in a .cctor because exceptions can + // be thrown out of a .cctor stack that will need this. + NumberFormatInfo nfi = new NumberFormatInfo(); + nfi.m_isInvariant = true; + s_invariantInfo = ReadOnly(nfi); + } + return s_invariantInfo; + } + } + + public static NumberFormatInfo GetInstance(IFormatProvider formatProvider) + { + return formatProvider == null ? + CurrentInfo : // Fast path for a null provider + GetProviderNonNull(formatProvider); + + NumberFormatInfo GetProviderNonNull(IFormatProvider provider) + { + // Fast path for a regular CultureInfo + if (provider is CultureInfo cultureProvider && !cultureProvider._isInherited) + { + return cultureProvider.numInfo ?? cultureProvider.NumberFormat; + } + + return + provider as NumberFormatInfo ?? // Fast path for an NFI + provider.GetFormat(typeof(NumberFormatInfo)) as NumberFormatInfo ?? + CurrentInfo; + } + } + + public Object Clone() + { + NumberFormatInfo n = (NumberFormatInfo)MemberwiseClone(); + n.isReadOnly = false; + return n; + } + + + public int CurrencyDecimalDigits + { + get { return currencyDecimalDigits; } + set + { + if (value < 0 || value > 99) + { + throw new ArgumentOutOfRangeException( + nameof(CurrencyDecimalDigits), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 0, + 99)); + } + VerifyWritable(); + currencyDecimalDigits = value; + } + } + + + public String CurrencyDecimalSeparator + { + get { return currencyDecimalSeparator; } + set + { + VerifyWritable(); + VerifyDecimalSeparator(value, nameof(CurrencyDecimalSeparator)); + currencyDecimalSeparator = value; + } + } + + + public bool IsReadOnly + { + get + { + return isReadOnly; + } + } + + // + // Check the values of the groupSize array. + // + // Every element in the groupSize array should be between 1 and 9 + // excpet the last element could be zero. + // + internal static void CheckGroupSize(String propName, int[] groupSize) + { + for (int i = 0; i < groupSize.Length; i++) + { + if (groupSize[i] < 1) + { + if (i == groupSize.Length - 1 && groupSize[i] == 0) + return; + throw new ArgumentException(SR.Argument_InvalidGroupSize, propName); + } + else if (groupSize[i] > 9) + { + throw new ArgumentException(SR.Argument_InvalidGroupSize, propName); + } + } + } + + + public int[] CurrencyGroupSizes + { + get + { + return ((int[])currencyGroupSizes.Clone()); + } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(CurrencyGroupSizes), + SR.ArgumentNull_Obj); + } + VerifyWritable(); + + Int32[] inputSizes = (Int32[])value.Clone(); + CheckGroupSize(nameof(CurrencyGroupSizes), inputSizes); + currencyGroupSizes = inputSizes; + } + } + + + + public int[] NumberGroupSizes + { + get + { + return ((int[])numberGroupSizes.Clone()); + } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(NumberGroupSizes), + SR.ArgumentNull_Obj); + } + VerifyWritable(); + + Int32[] inputSizes = (Int32[])value.Clone(); + CheckGroupSize(nameof(NumberGroupSizes), inputSizes); + numberGroupSizes = inputSizes; + } + } + + + public int[] PercentGroupSizes + { + get + { + return ((int[])percentGroupSizes.Clone()); + } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(PercentGroupSizes), + SR.ArgumentNull_Obj); + } + VerifyWritable(); + Int32[] inputSizes = (Int32[])value.Clone(); + CheckGroupSize(nameof(PercentGroupSizes), inputSizes); + percentGroupSizes = inputSizes; + } + } + + + public String CurrencyGroupSeparator + { + get { return currencyGroupSeparator; } + set + { + VerifyWritable(); + VerifyGroupSeparator(value, nameof(CurrencyGroupSeparator)); + currencyGroupSeparator = value; + } + } + + + public String CurrencySymbol + { + get { return currencySymbol; } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(CurrencySymbol), + SR.ArgumentNull_String); + } + VerifyWritable(); + currencySymbol = value; + } + } + + // Returns the current culture's NumberFormatInfo. Used by Parse methods. + // + + public static NumberFormatInfo CurrentInfo + { + get + { + System.Globalization.CultureInfo culture = CultureInfo.CurrentCulture; + if (!culture._isInherited) + { + NumberFormatInfo info = culture.numInfo; + if (info != null) + { + return info; + } + } + return ((NumberFormatInfo)culture.GetFormat(typeof(NumberFormatInfo))); + } + } + + + public String NaNSymbol + { + get + { + return nanSymbol; + } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(NaNSymbol), + SR.ArgumentNull_String); + } + VerifyWritable(); + nanSymbol = value; + } + } + + + + public int CurrencyNegativePattern + { + get { return currencyNegativePattern; } + set + { + if (value < 0 || value > 15) + { + throw new ArgumentOutOfRangeException( + nameof(CurrencyNegativePattern), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 0, + 15)); + } + VerifyWritable(); + currencyNegativePattern = value; + } + } + + + public int NumberNegativePattern + { + get { return numberNegativePattern; } + set + { + // + // NOTENOTE: the range of value should correspond to negNumberFormats[] in vm\COMNumber.cpp. + // + if (value < 0 || value > 4) + { + throw new ArgumentOutOfRangeException( + nameof(NumberNegativePattern), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 0, + 4)); + } + VerifyWritable(); + numberNegativePattern = value; + } + } + + + public int PercentPositivePattern + { + get { return percentPositivePattern; } + set + { + // + // NOTENOTE: the range of value should correspond to posPercentFormats[] in vm\COMNumber.cpp. + // + if (value < 0 || value > 3) + { + throw new ArgumentOutOfRangeException( + nameof(PercentPositivePattern), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 0, + 3)); + } + VerifyWritable(); + percentPositivePattern = value; + } + } + + + public int PercentNegativePattern + { + get { return percentNegativePattern; } + set + { + // + // NOTENOTE: the range of value should correspond to posPercentFormats[] in vm\COMNumber.cpp. + // + if (value < 0 || value > 11) + { + throw new ArgumentOutOfRangeException( + nameof(PercentNegativePattern), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 0, + 11)); + } + VerifyWritable(); + percentNegativePattern = value; + } + } + + + public String NegativeInfinitySymbol + { + get + { + return negativeInfinitySymbol; + } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(NegativeInfinitySymbol), + SR.ArgumentNull_String); + } + VerifyWritable(); + negativeInfinitySymbol = value; + } + } + + + public String NegativeSign + { + get { return negativeSign; } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(NegativeSign), + SR.ArgumentNull_String); + } + VerifyWritable(); + negativeSign = value; + } + } + + + public int NumberDecimalDigits + { + get { return numberDecimalDigits; } + set + { + if (value < 0 || value > 99) + { + throw new ArgumentOutOfRangeException( + nameof(NumberDecimalDigits), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 0, + 99)); + } + VerifyWritable(); + numberDecimalDigits = value; + } + } + + + public String NumberDecimalSeparator + { + get { return numberDecimalSeparator; } + set + { + VerifyWritable(); + VerifyDecimalSeparator(value, nameof(NumberDecimalSeparator)); + numberDecimalSeparator = value; + } + } + + + public String NumberGroupSeparator + { + get { return numberGroupSeparator; } + set + { + VerifyWritable(); + VerifyGroupSeparator(value, nameof(NumberGroupSeparator)); + numberGroupSeparator = value; + } + } + + + public int CurrencyPositivePattern + { + get { return currencyPositivePattern; } + set + { + if (value < 0 || value > 3) + { + throw new ArgumentOutOfRangeException( + nameof(CurrencyPositivePattern), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 0, + 3)); + } + VerifyWritable(); + currencyPositivePattern = value; + } + } + + + public String PositiveInfinitySymbol + { + get + { + return positiveInfinitySymbol; + } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(PositiveInfinitySymbol), + SR.ArgumentNull_String); + } + VerifyWritable(); + positiveInfinitySymbol = value; + } + } + + + public String PositiveSign + { + get { return positiveSign; } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(PositiveSign), + SR.ArgumentNull_String); + } + VerifyWritable(); + positiveSign = value; + } + } + + + public int PercentDecimalDigits + { + get { return percentDecimalDigits; } + set + { + if (value < 0 || value > 99) + { + throw new ArgumentOutOfRangeException( + nameof(PercentDecimalDigits), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 0, + 99)); + } + VerifyWritable(); + percentDecimalDigits = value; + } + } + + + public String PercentDecimalSeparator + { + get { return percentDecimalSeparator; } + set + { + VerifyWritable(); + VerifyDecimalSeparator(value, nameof(PercentDecimalSeparator)); + percentDecimalSeparator = value; + } + } + + + public String PercentGroupSeparator + { + get { return percentGroupSeparator; } + set + { + VerifyWritable(); + VerifyGroupSeparator(value, nameof(PercentGroupSeparator)); + percentGroupSeparator = value; + } + } + + + public String PercentSymbol + { + get + { + return percentSymbol; + } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(PercentSymbol), + SR.ArgumentNull_String); + } + VerifyWritable(); + percentSymbol = value; + } + } + + + public String PerMilleSymbol + { + get { return perMilleSymbol; } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(PerMilleSymbol), + SR.ArgumentNull_String); + } + VerifyWritable(); + perMilleSymbol = value; + } + } + + public string[] NativeDigits + { + get { return (String[])nativeDigits.Clone(); } + set + { + VerifyWritable(); + VerifyNativeDigits(value, nameof(NativeDigits)); + nativeDigits = value; + } + } + + public DigitShapes DigitSubstitution + { + get { return (DigitShapes)digitSubstitution; } + set + { + VerifyWritable(); + VerifyDigitSubstitution(value, nameof(DigitSubstitution)); + digitSubstitution = (int)value; + } + } + + public Object GetFormat(Type formatType) + { + return formatType == typeof(NumberFormatInfo) ? this : null; + } + + public static NumberFormatInfo ReadOnly(NumberFormatInfo nfi) + { + if (nfi == null) + { + throw new ArgumentNullException(nameof(nfi)); + } + if (nfi.IsReadOnly) + { + return (nfi); + } + NumberFormatInfo info = (NumberFormatInfo)(nfi.MemberwiseClone()); + info.isReadOnly = true; + return info; + } + + // private const NumberStyles InvalidNumberStyles = unchecked((NumberStyles) 0xFFFFFC00); + private const NumberStyles InvalidNumberStyles = ~(NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite + | NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingSign + | NumberStyles.AllowParentheses | NumberStyles.AllowDecimalPoint + | NumberStyles.AllowThousands | NumberStyles.AllowExponent + | NumberStyles.AllowCurrencySymbol | NumberStyles.AllowHexSpecifier); + + internal static void ValidateParseStyleInteger(NumberStyles style) + { + // Check for undefined flags + if ((style & InvalidNumberStyles) != 0) + { + throw new ArgumentException(SR.Argument_InvalidNumberStyles, nameof(style)); + } + if ((style & NumberStyles.AllowHexSpecifier) != 0) + { // Check for hex number + if ((style & ~NumberStyles.HexNumber) != 0) + { + throw new ArgumentException(SR.Arg_InvalidHexStyle); + } + } + } + + internal static void ValidateParseStyleFloatingPoint(NumberStyles style) + { + // Check for undefined flags + if ((style & InvalidNumberStyles) != 0) + { + throw new ArgumentException(SR.Argument_InvalidNumberStyles, nameof(style)); + } + if ((style & NumberStyles.AllowHexSpecifier) != 0) + { // Check for hex number + throw new ArgumentException(SR.Arg_HexStyleNotSupported); + } + } + } // NumberFormatInfo +} + + + + + + + + + diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/NumberStyles.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/NumberStyles.cs new file mode 100644 index 0000000000..5909d65a2c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/NumberStyles.cs @@ -0,0 +1,65 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: Contains valid formats for Numbers recognized by +** the Number class' parsing code. +** +** +===========================================================*/ + +namespace System.Globalization +{ + [Flags] + public enum NumberStyles + { + // Bit flag indicating that leading whitespace is allowed. Character values + // 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, and 0x0020 are considered to be + // whitespace. + + None = 0x00000000, + + AllowLeadingWhite = 0x00000001, + + AllowTrailingWhite = 0x00000002, //Bitflag indicating trailing whitespace is allowed. + + AllowLeadingSign = 0x00000004, //Can the number start with a sign char. + //Specified by NumberFormatInfo.PositiveSign and NumberFormatInfo.NegativeSign + + AllowTrailingSign = 0x00000008, //Allow the number to end with a sign char + + AllowParentheses = 0x00000010, //Allow the number to be enclosed in parens + + AllowDecimalPoint = 0x00000020, //Allow a decimal point + + AllowThousands = 0x00000040, //Allow thousands separators (more properly, allow group separators) + + AllowExponent = 0x00000080, //Allow an exponent + + AllowCurrencySymbol = 0x00000100, //Allow a currency symbol. + + AllowHexSpecifier = 0x00000200, //Allow specifiying hexadecimal. + //Common uses. These represent some of the most common combinations of these flags. + + + Integer = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign, + + HexNumber = AllowLeadingWhite | AllowTrailingWhite | AllowHexSpecifier, + + Number = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign | AllowTrailingSign | + AllowDecimalPoint | AllowThousands, + + Float = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign | + AllowDecimalPoint | AllowExponent, + + Currency = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign | AllowTrailingSign | + AllowParentheses | AllowDecimalPoint | AllowThousands | AllowCurrencySymbol, + + Any = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign | AllowTrailingSign | + AllowParentheses | AllowDecimalPoint | AllowThousands | AllowCurrencySymbol | AllowExponent, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/PersianCalendar.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/PersianCalendar.cs new file mode 100644 index 0000000000..10912f85b1 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/PersianCalendar.cs @@ -0,0 +1,601 @@ +// 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.Diagnostics; + +namespace System.Globalization +{ + // Modern Persian calendar is a solar observation based calendar. Each new year begins on the day when the vernal equinox occurs before noon. + // The epoch is the date of the vernal equinox prior to the epoch of the Islamic calendar (March 19, 622 Julian or March 22, 622 Gregorian) + + // There is no Persian year 0. Ordinary years have 365 days. Leap years have 366 days with the last month (Esfand) gaining the extra day. + /* + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 0622/03/22 9999/12/31 + ** Persian 0001/01/01 9378/10/13 + */ + + public class PersianCalendar : Calendar + { + public static readonly int PersianEra = 1; + + internal static long PersianEpoch = new DateTime(622, 3, 22).Ticks / GregorianCalendar.TicksPerDay; + private const int ApproximateHalfYear = 180; + + internal const int DatePartYear = 0; + internal const int DatePartDayOfYear = 1; + internal const int DatePartMonth = 2; + internal const int DatePartDay = 3; + internal const int MonthsPerYear = 12; + + internal static int[] DaysToMonth = { 0, 31, 62, 93, 124, 155, 186, 216, 246, 276, 306, 336, 366 }; + + internal const int MaxCalendarYear = 9378; + internal const int MaxCalendarMonth = 10; + internal const int MaxCalendarDay = 13; + + // Persian calendar (year: 1, month: 1, day:1 ) = Gregorian (year: 622, month: 3, day: 22) + // This is the minimal Gregorian date that we support in the PersianCalendar. + internal static DateTime minDate = new DateTime(622, 3, 22); + internal static DateTime maxDate = DateTime.MaxValue; + + public override DateTime MinSupportedDateTime + { + get + { + return (minDate); + } + } + + public override DateTime MaxSupportedDateTime + { + get + { + return (maxDate); + } + } + + public override CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.SolarCalendar; + } + } + + // Construct an instance of Persian calendar. + + public PersianCalendar() + { + } + + + internal override CalendarId BaseCalendarID + { + get + { + return CalendarId.GREGORIAN; + } + } + + internal override CalendarId ID + { + get + { + return CalendarId.PERSIAN; + } + } + + + /*=================================GetAbsoluteDatePersian========================== + **Action: Gets the Absolute date for the given Persian date. The absolute date means + ** the number of days from January 1st, 1 A.D. + **Returns: + **Arguments: + **Exceptions: + ============================================================================*/ + + private long GetAbsoluteDatePersian(int year, int month, int day) + { + if (year >= 1 && year <= MaxCalendarYear && month >= 1 && month <= 12) + { + int ordinalDay = DaysInPreviousMonths(month) + day - 1; // day is one based, make 0 based since this will be the number of days we add to beginning of year below + int approximateDaysFromEpochForYearStart = (int)(CalendricalCalculationsHelper.MeanTropicalYearInDays * (year - 1)); + long yearStart = CalendricalCalculationsHelper.PersianNewYearOnOrBefore(PersianEpoch + approximateDaysFromEpochForYearStart + ApproximateHalfYear); + yearStart += ordinalDay; + return yearStart; + } + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay); + } + + internal static void CheckTicksRange(long ticks) + { + if (ticks < minDate.Ticks || ticks > maxDate.Ticks) + { + throw new ArgumentOutOfRangeException( + "time", + String.Format( + CultureInfo.InvariantCulture, + SR.ArgumentOutOfRange_CalendarRange, + minDate, + maxDate)); + } + } + + internal static void CheckEraRange(int era) + { + if (era != CurrentEra && era != PersianEra) + { + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + } + + internal static void CheckYearRange(int year, int era) + { + CheckEraRange(era); + if (year < 1 || year > MaxCalendarYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + MaxCalendarYear)); + } + } + + internal static void CheckYearMonthRange(int year, int month, int era) + { + CheckYearRange(year, era); + if (year == MaxCalendarYear) + { + if (month > MaxCalendarMonth) + { + throw new ArgumentOutOfRangeException( + nameof(month), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + MaxCalendarMonth)); + } + } + + if (month < 1 || month > 12) + { + throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month); + } + } + + private static int MonthFromOrdinalDay(int ordinalDay) + { + Debug.Assert(ordinalDay <= 366); + int index = 0; + while (ordinalDay > DaysToMonth[index]) + index++; + + return index; + } + + private static int DaysInPreviousMonths(int month) + { + Debug.Assert(1 <= month && month <= 12); + --month; // months are one based but for calculations use 0 based + return DaysToMonth[month]; + } + + /*=================================GetDatePart========================== + **Action: Returns a given date part of this DateTime. This method is used + ** to compute the year, day-of-year, month, or day part. + **Returns: + **Arguments: + **Exceptions: ArgumentException if part is incorrect. + ============================================================================*/ + + internal int GetDatePart(long ticks, int part) + { + long NumDays; // The calculation buffer in number of days. + + CheckTicksRange(ticks); + + // + // Get the absolute date. The absolute date is the number of days from January 1st, 1 A.D. + // 1/1/0001 is absolute date 1. + // + NumDays = ticks / GregorianCalendar.TicksPerDay + 1; + + // + // Calculate the appromixate Persian Year. + // + + long yearStart = CalendricalCalculationsHelper.PersianNewYearOnOrBefore(NumDays); + int y = (int)(Math.Floor(((yearStart - PersianEpoch) / CalendricalCalculationsHelper.MeanTropicalYearInDays) + 0.5)) + 1; + Debug.Assert(y >= 1); + + if (part == DatePartYear) + { + return y; + } + + // + // Calculate the Persian Month. + // + + int ordinalDay = (int)(NumDays - CalendricalCalculationsHelper.GetNumberOfDays(this.ToDateTime(y, 1, 1, 0, 0, 0, 0, 1))); + + if (part == DatePartDayOfYear) + { + return ordinalDay; + } + + int m = MonthFromOrdinalDay(ordinalDay); + Debug.Assert(ordinalDay >= 1); + Debug.Assert(m >= 1 && m <= 12); + if (part == DatePartMonth) + { + return m; + } + + int d = ordinalDay - DaysInPreviousMonths(m); + Debug.Assert(1 <= d); + Debug.Assert(d <= 31); + + // + // Calculate the Persian Day. + // + + if (part == DatePartDay) + { + return (d); + } + + // Incorrect part value. + throw new InvalidOperationException(SR.InvalidOperation_DateTimeParsing); + } + + // Returns the DateTime resulting from adding the given number of + // months to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year and month parts of the specified DateTime by + // value months, and, if required, adjusting the day part of the + // resulting date downwards to the last day of the resulting month in the + // resulting year. The time-of-day part of the result is the same as the + // time-of-day part of the specified DateTime. + // + // In more precise terms, considering the specified DateTime to be of the + // form y / m / d + t, where y is the + // year, m is the month, d is the day, and t is the + // time-of-day, the result is y1 / m1 / d1 + t, + // where y1 and m1 are computed by adding value months + // to y and m, and d1 is the largest value less than + // or equal to d that denotes a valid day in month m1 of year + // y1. + // + + + public override DateTime AddMonths(DateTime time, int months) + { + if (months < -120000 || months > 120000) + { + throw new ArgumentOutOfRangeException( + nameof(months), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + -120000, + 120000)); + } + // Get the date in Persian calendar. + int y = GetDatePart(time.Ticks, DatePartYear); + int m = GetDatePart(time.Ticks, DatePartMonth); + int d = GetDatePart(time.Ticks, DatePartDay); + int i = m - 1 + months; + if (i >= 0) + { + m = i % 12 + 1; + y = y + i / 12; + } + else + { + m = 12 + (i + 1) % 12; + y = y + (i - 11) / 12; + } + int days = GetDaysInMonth(y, m); + if (d > days) + { + d = days; + } + long ticks = GetAbsoluteDatePersian(y, m, d) * TicksPerDay + time.Ticks % TicksPerDay; + Calendar.CheckAddResult(ticks, MinSupportedDateTime, MaxSupportedDateTime); + return (new DateTime(ticks)); + } + + // Returns the DateTime resulting from adding the given number of + // years to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year part of the specified DateTime by value + // years. If the month and day of the specified DateTime is 2/29, and if the + // resulting year is not a leap year, the month and day of the resulting + // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day + // parts of the result are the same as those of the specified DateTime. + // + + + public override DateTime AddYears(DateTime time, int years) + { + return (AddMonths(time, years * 12)); + } + + // Returns the day-of-month part of the specified DateTime. The returned + // value is an integer between 1 and 31. + // + + + public override int GetDayOfMonth(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartDay)); + } + + // Returns the day-of-week part of the specified DateTime. The returned value + // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates + // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates + // Thursday, 5 indicates Friday, and 6 indicates Saturday. + // + + + public override DayOfWeek GetDayOfWeek(DateTime time) + { + return ((DayOfWeek)((int)(time.Ticks / TicksPerDay + 1) % 7)); + } + + // Returns the day-of-year part of the specified DateTime. The returned value + // is an integer between 1 and 366. + // + + + public override int GetDayOfYear(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartDayOfYear)); + } + + // Returns the number of days in the month given by the year and + // month arguments. + // + + + public override int GetDaysInMonth(int year, int month, int era) + { + CheckYearMonthRange(year, month, era); + + if ((month == MaxCalendarMonth) && (year == MaxCalendarYear)) + { + return MaxCalendarDay; + } + + int daysInMonth = DaysToMonth[month] - DaysToMonth[month - 1]; + if ((month == MonthsPerYear) && !IsLeapYear(year)) + { + Debug.Assert(daysInMonth == 30); + --daysInMonth; + } + return daysInMonth; + } + + // Returns the number of days in the year given by the year argument for the current era. + // + + public override int GetDaysInYear(int year, int era) + { + CheckYearRange(year, era); + if (year == MaxCalendarYear) + { + return DaysToMonth[MaxCalendarMonth - 1] + MaxCalendarDay; + } + // Common years have 365 days. Leap years have 366 days. + return (IsLeapYear(year, CurrentEra) ? 366 : 365); + } + + // Returns the era for the specified DateTime value. + + + public override int GetEra(DateTime time) + { + CheckTicksRange(time.Ticks); + return (PersianEra); + } + + + + public override int[] Eras + { + get + { + return (new int[] { PersianEra }); + } + } + + // Returns the month part of the specified DateTime. The returned value is an + // integer between 1 and 12. + // + + + public override int GetMonth(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartMonth)); + } + + // Returns the number of months in the specified year and era. + + + public override int GetMonthsInYear(int year, int era) + { + CheckYearRange(year, era); + if (year == MaxCalendarYear) + { + return MaxCalendarMonth; + } + return (12); + } + + // Returns the year part of the specified DateTime. The returned value is an + // integer between 1 and MaxCalendarYear. + // + + + public override int GetYear(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartYear)); + } + + // Checks whether a given day in the specified era is a leap day. This method returns true if + // the date is a leap day, or false if not. + // + + + public override bool IsLeapDay(int year, int month, int day, int era) + { + // The year/month/era value checking is done in GetDaysInMonth(). + int daysInMonth = GetDaysInMonth(year, month, era); + if (day < 1 || day > daysInMonth) + { + throw new ArgumentOutOfRangeException( + nameof(day), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Day, + daysInMonth, + month)); + } + return (IsLeapYear(year, era) && month == 12 && day == 30); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this calendar does not have leap month, or this year is not a leap year. + // + + + public override int GetLeapMonth(int year, int era) + { + CheckYearRange(year, era); + return (0); + } + + // Checks whether a given month in the specified era is a leap month. This method returns true if + // month is a leap month, or false if not. + // + + + public override bool IsLeapMonth(int year, int month, int era) + { + CheckYearMonthRange(year, month, era); + return (false); + } + + // Checks whether a given year in the specified era is a leap year. This method returns true if + // year is a leap year, or false if not. + // + + public override bool IsLeapYear(int year, int era) + { + CheckYearRange(year, era); + + if (year == MaxCalendarYear) + { + return false; + } + + return (GetAbsoluteDatePersian(year + 1, 1, 1) - GetAbsoluteDatePersian(year, 1, 1)) == 366; + } + + // Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid. + // + + + public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) + { + // The year/month/era checking is done in GetDaysInMonth(). + int daysInMonth = GetDaysInMonth(year, month, era); + if (day < 1 || day > daysInMonth) + { + throw new ArgumentOutOfRangeException( + nameof(day), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Day, + daysInMonth, + month)); + } + + long lDate = GetAbsoluteDatePersian(year, month, day); + + if (lDate >= 0) + { + return (new DateTime(lDate * GregorianCalendar.TicksPerDay + TimeToTicks(hour, minute, second, millisecond))); + } + else + { + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay); + } + } + + private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 1410; + + public override int TwoDigitYearMax + { + get + { + if (twoDigitYearMax == -1) + { + twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX); + } + return (twoDigitYearMax); + } + + set + { + VerifyWritable(); + if (value < 99 || value > MaxCalendarYear) + { + throw new ArgumentOutOfRangeException( + nameof(value), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 99, + MaxCalendarYear)); + } + twoDigitYearMax = value; + } + } + + + + public override int ToFourDigitYear(int year) + { + if (year < 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedNonNegNum); + } + + if (year < 100) + { + return (base.ToFourDigitYear(year)); + } + + if (year > MaxCalendarYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + MaxCalendarYear)); + } + return (year); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/RegionInfo.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/RegionInfo.cs similarity index 95% rename from external/corert/src/System.Private.CoreLib/src/System/Globalization/RegionInfo.cs rename to external/corefx/src/Common/src/CoreLib/System/Globalization/RegionInfo.cs index 5555dcf34f..2cad7bb31c 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/RegionInfo.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/RegionInfo.cs @@ -15,8 +15,6 @@ //////////////////////////////////////////////////////////////////////////// using System.Diagnostics; -using System.Diagnostics.Contracts; -using System.Runtime.Serialization; namespace System.Globalization { @@ -66,7 +64,6 @@ namespace System.Globalization throw new ArgumentException(SR.Argument_NoRegionInvariantCulture, nameof(name)); } - Contract.EndContractBlock(); // // For CoreCLR we only want the region names that are full culture names @@ -89,7 +86,7 @@ namespace System.Globalization public RegionInfo(int culture) { if (culture == CultureInfo.LOCALE_INVARIANT) //The InvariantCulture has no matching region - { + { throw new ArgumentException(SR.Argument_NoRegionInvariantCulture); } @@ -104,7 +101,7 @@ namespace System.Globalization // Not supposed to be neutral throw new ArgumentException(SR.Format(SR.Argument_CustomCultureCannotBePassedByNumber, culture), nameof(culture)); } - + _cultureData = CultureData.GetCultureData(culture, true); _name = _cultureData.SREGIONNAME; @@ -123,11 +120,8 @@ namespace System.Globalization private void SetName(string name) { - // when creating region by culture name, we keep the region name as the culture name so regions - // created by custom culture names can be differentiated from built in regions. - this._name = name.Equals(_cultureData.SREGIONNAME, StringComparison.OrdinalIgnoreCase) ? - _cultureData.SREGIONNAME : - _cultureData.CultureName; + // Use the name of the region we found + _name = _cultureData.SREGIONNAME; } //////////////////////////////////////////////////////////////////////// @@ -284,7 +278,7 @@ namespace System.Globalization } } - public virtual int GeoId + public virtual int GeoId { get { diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/SortKey.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/SortKey.cs new file mode 100644 index 0000000000..647db75b63 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/SortKey.cs @@ -0,0 +1,175 @@ +// 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. + +//////////////////////////////////////////////////////////////////////////// +// +// +// Purpose: This class implements a set of methods for retrieving +// sort key information. +// +// +//////////////////////////////////////////////////////////////////////////// + +using System; +using System.Runtime.CompilerServices; +using System.Diagnostics; + +namespace System.Globalization +{ + public partial class SortKey + { + //--------------------------------------------------------------------// + // Internal Information // + //--------------------------------------------------------------------// + + internal string _localeName; // locale identifier + + internal CompareOptions _options; // options + internal string _string; // original string + internal byte[] _keyData; // sortkey data + + // + // The following constructor is designed to be called from CompareInfo to get the + // the sort key of specific string for synthetic culture + // + internal SortKey(String localeName, String str, CompareOptions options, byte[] keyData) + { + _keyData = keyData; + _localeName = localeName; + _options = options; + _string = str; + } + + //////////////////////////////////////////////////////////////////////// + // + // GetOriginalString + // + // Returns the original string used to create the current instance + // of SortKey. + // + //////////////////////////////////////////////////////////////////////// + public virtual String OriginalString + { + get + { + return (_string); + } + } + + //////////////////////////////////////////////////////////////////////// + // + // GetKeyData + // + // Returns a byte array representing the current instance of the + // sort key. + // + //////////////////////////////////////////////////////////////////////// + public virtual byte[] KeyData + { + get + { + return (byte[])(_keyData.Clone()); + } + } + + //////////////////////////////////////////////////////////////////////// + // + // Compare + // + // Compares the two sort keys. Returns 0 if the two sort keys are + // equal, a number less than 0 if sortkey1 is less than sortkey2, + // and a number greater than 0 if sortkey1 is greater than sortkey2. + // + //////////////////////////////////////////////////////////////////////// + public static int Compare(SortKey sortkey1, SortKey sortkey2) + { + if (sortkey1 == null || sortkey2 == null) + { + throw new ArgumentNullException((sortkey1 == null ? nameof(sortkey1) : nameof(sortkey2))); + } + + byte[] key1Data = sortkey1._keyData; + byte[] key2Data = sortkey2._keyData; + + Debug.Assert(key1Data != null, "key1Data != null"); + Debug.Assert(key2Data != null, "key2Data != null"); + + if (key1Data.Length == 0) + { + if (key2Data.Length == 0) + { + return (0); + } + return (-1); + } + if (key2Data.Length == 0) + { + return (1); + } + + int compLen = (key1Data.Length < key2Data.Length) ? key1Data.Length : key2Data.Length; + + for (int i = 0; i < compLen; i++) + { + if (key1Data[i] > key2Data[i]) + { + return (1); + } + if (key1Data[i] < key2Data[i]) + { + return (-1); + } + } + + return 0; + } + + //////////////////////////////////////////////////////////////////////// + // + // Equals + // + // Implements Object.Equals(). Returns a boolean indicating whether + // or not object refers to the same SortKey as the current instance. + // + //////////////////////////////////////////////////////////////////////// + public override bool Equals(Object value) + { + SortKey that = value as SortKey; + + if (that != null) + { + return Compare(this, that) == 0; + } + + return (false); + } + + //////////////////////////////////////////////////////////////////////// + // + // GetHashCode + // + // Implements Object.GetHashCode(). Returns the hash code for the + // SortKey. The hash code is guaranteed to be the same for + // SortKey A and B where A.Equals(B) is true. + // + //////////////////////////////////////////////////////////////////////// + public override int GetHashCode() + { + return (CompareInfo.GetCompareInfo(_localeName).GetHashCodeOfString(_string, _options)); + } + + //////////////////////////////////////////////////////////////////////// + // + // ToString + // + // Implements Object.ToString(). Returns a string describing the + // SortKey. + // + //////////////////////////////////////////////////////////////////////// + public override String ToString() + { + return ("SortKey - " + _localeName + ", " + _options + ", " + _string); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/SortVersion.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/SortVersion.cs new file mode 100644 index 0000000000..46e9a833ec --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/SortVersion.cs @@ -0,0 +1,99 @@ +// 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. + +namespace System.Globalization +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class SortVersion : IEquatable + { + private int m_NlsVersion; // Do not rename (binary serialization) + private Guid m_SortId; // Do not rename (binary serialization) + + public int FullVersion + { + get + { + return m_NlsVersion; + } + } + + public Guid SortId + { + get + { + return m_SortId; + } + } + + public SortVersion(int fullVersion, Guid sortId) + { + m_SortId = sortId; + m_NlsVersion = fullVersion; + } + + internal SortVersion(int nlsVersion, int effectiveId, Guid customVersion) + { + m_NlsVersion = nlsVersion; + + if (customVersion == Guid.Empty) + { + byte b1 = (byte)(effectiveId >> 24); + byte b2 = (byte)((effectiveId & 0x00FF0000) >> 16); + byte b3 = (byte)((effectiveId & 0x0000FF00) >> 8); + byte b4 = (byte)(effectiveId & 0xFF); + customVersion = new Guid(0, 0, 0, 0, 0, 0, 0, b1, b2, b3, b4); + } + + m_SortId = customVersion; + } + + public override bool Equals(object obj) + { + SortVersion n = obj as SortVersion; + if (n != null) + { + return this.Equals(n); + } + + return false; + } + + public bool Equals(SortVersion other) + { + if (other == null) + { + return false; + } + + return m_NlsVersion == other.m_NlsVersion && m_SortId == other.m_SortId; + } + + public override int GetHashCode() + { + return m_NlsVersion * 7 | m_SortId.GetHashCode(); + } + + public static bool operator ==(SortVersion left, SortVersion right) + { + if (((object)left) != null) + { + return left.Equals(right); + } + + if (((object)right) != null) + { + return right.Equals(left); + } + + // Both null. + return true; + } + + public static bool operator !=(SortVersion left, SortVersion right) + { + return !(left == right); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/StringInfo.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/StringInfo.cs new file mode 100644 index 0000000000..aa62b2c474 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/StringInfo.cs @@ -0,0 +1,350 @@ +// 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. + +//////////////////////////////////////////////////////////////////////////// +// +// +// Purpose: This class defines behaviors specific to a writing system. +// A writing system is the collection of scripts and +// orthographic rules required to represent a language as text. +// +// +//////////////////////////////////////////////////////////////////////////// + +using System; +using System.Diagnostics; + +namespace System.Globalization +{ + public class StringInfo + { + private string _str; + + private int[] _indexes; + + // Legacy constructor + public StringInfo() : this("") { } + + // Primary, useful constructor + public StringInfo(string value) + { + this.String = value; + } + + public override bool Equals(Object value) + { + StringInfo that = value as StringInfo; + if (that != null) + { + return (_str.Equals(that._str)); + } + return (false); + } + + public override int GetHashCode() + { + return _str.GetHashCode(); + } + + + // Our zero-based array of index values into the string. Initialize if + // our private array is not yet, in fact, initialized. + private int[] Indexes + { + get + { + if ((null == _indexes) && (0 < this.String.Length)) + { + _indexes = StringInfo.ParseCombiningCharacters(this.String); + } + + return (_indexes); + } + } + + public string String + { + get + { + return (_str); + } + set + { + if (null == value) + { + throw new ArgumentNullException(nameof(String), + SR.ArgumentNull_String); + } + + _str = value; + _indexes = null; + } + } + + public int LengthInTextElements + { + get + { + if (null == this.Indexes) + { + // Indexes not initialized, so assume length zero + return (0); + } + + return (this.Indexes.Length); + } + } + + public string SubstringByTextElements(int startingTextElement) + { + // If the string is empty, no sense going further. + if (null == this.Indexes) + { + // Just decide which error to give depending on the param they gave us.... + if (startingTextElement < 0) + { + throw new ArgumentOutOfRangeException(nameof(startingTextElement), SR.ArgumentOutOfRange_NeedPosNum); + } + else + { + throw new ArgumentOutOfRangeException(nameof(startingTextElement), SR.Arg_ArgumentOutOfRangeException); + } + } + return (SubstringByTextElements(startingTextElement, Indexes.Length - startingTextElement)); + } + + public string SubstringByTextElements(int startingTextElement, int lengthInTextElements) + { + if (startingTextElement < 0) + { + throw new ArgumentOutOfRangeException(nameof(startingTextElement), SR.ArgumentOutOfRange_NeedPosNum); + } + + if (this.String.Length == 0 || startingTextElement >= Indexes.Length) + { + throw new ArgumentOutOfRangeException(nameof(startingTextElement), SR.Arg_ArgumentOutOfRangeException); + } + + if (lengthInTextElements < 0) + { + throw new ArgumentOutOfRangeException(nameof(lengthInTextElements), SR.ArgumentOutOfRange_NeedPosNum); + } + + if (startingTextElement > Indexes.Length - lengthInTextElements) + { + throw new ArgumentOutOfRangeException(nameof(lengthInTextElements), SR.Arg_ArgumentOutOfRangeException); + } + + int start = Indexes[startingTextElement]; + + if (startingTextElement + lengthInTextElements == Indexes.Length) + { + // We are at the last text element in the string and because of that + // must handle the call differently. + return (this.String.Substring(start)); + } + else + { + return (this.String.Substring(start, (Indexes[lengthInTextElements + startingTextElement] - start))); + } + } + + public static string GetNextTextElement(string str) + { + return (GetNextTextElement(str, 0)); + } + + + //////////////////////////////////////////////////////////////////////// + // + // Get the code point count of the current text element. + // + // A combining class is defined as: + // A character/surrogate that has the following Unicode category: + // * NonSpacingMark (e.g. U+0300 COMBINING GRAVE ACCENT) + // * SpacingCombiningMark (e.g. U+ 0903 DEVANGARI SIGN VISARGA) + // * EnclosingMark (e.g. U+20DD COMBINING ENCLOSING CIRCLE) + // + // In the context of GetNextTextElement() and ParseCombiningCharacters(), a text element is defined as: + // + // 1. If a character/surrogate is in the following category, it is a text element. + // It can NOT further combine with characters in the combinging class to form a text element. + // * one of the Unicode category in the combinging class + // * UnicodeCategory.Format + // * UnicodeCateogry.Control + // * UnicodeCategory.OtherNotAssigned + // 2. Otherwise, the character/surrogate can be combined with characters in the combinging class to form a text element. + // + // Return: + // The length of the current text element + // + // Parameters: + // String str + // index The starting index + // len The total length of str (to define the upper boundary) + // ucCurrent The Unicode category pointed by Index. It will be updated to the uc of next character if this is not the last text element. + // currentCharCount The char count of an abstract char pointed by Index. It will be updated to the char count of next abstract character if this is not the last text element. + // + //////////////////////////////////////////////////////////////////////// + + internal static int GetCurrentTextElementLen(string str, int index, int len, ref UnicodeCategory ucCurrent, ref int currentCharCount) + { + Debug.Assert(index >= 0 && len >= 0, "StringInfo.GetCurrentTextElementLen() : index = " + index + ", len = " + len); + Debug.Assert(index < len, "StringInfo.GetCurrentTextElementLen() : index = " + index + ", len = " + len); + if (index + currentCharCount == len) + { + // This is the last character/surrogate in the string. + return (currentCharCount); + } + + // Call an internal GetUnicodeCategory, which will tell us both the unicode category, and also tell us if it is a surrogate pair or not. + int nextCharCount; + UnicodeCategory ucNext = CharUnicodeInfo.InternalGetUnicodeCategory(str, index + currentCharCount, out nextCharCount); + if (CharUnicodeInfo.IsCombiningCategory(ucNext)) + { + // The next element is a combining class. + // Check if the current text element to see if it is a valid base category (i.e. it should not be a combining category, + // not a format character, and not a control character). + + if (CharUnicodeInfo.IsCombiningCategory(ucCurrent) + || (ucCurrent == UnicodeCategory.Format) + || (ucCurrent == UnicodeCategory.Control) + || (ucCurrent == UnicodeCategory.OtherNotAssigned) + || (ucCurrent == UnicodeCategory.Surrogate)) // An unpair high surrogate or low surrogate + { + // Will fall thru and return the currentCharCount + } + else + { + int startIndex = index; // Remember the current index. + + // We have a valid base characters, and we have a character (or surrogate) that is combining. + // Check if there are more combining characters to follow. + // Check if the next character is a nonspacing character. + index += currentCharCount + nextCharCount; + + while (index < len) + { + ucNext = CharUnicodeInfo.InternalGetUnicodeCategory(str, index, out nextCharCount); + if (!CharUnicodeInfo.IsCombiningCategory(ucNext)) + { + ucCurrent = ucNext; + currentCharCount = nextCharCount; + break; + } + index += nextCharCount; + } + return (index - startIndex); + } + } + // The return value will be the currentCharCount. + int ret = currentCharCount; + ucCurrent = ucNext; + // Update currentCharCount. + currentCharCount = nextCharCount; + return (ret); + } + + // Returns the str containing the next text element in str starting at + // index index. If index is not supplied, then it will start at the beginning + // of str. It recognizes a base character plus one or more combining + // characters or a properly formed surrogate pair as a text element. See also + // the ParseCombiningCharacters() and the ParseSurrogates() methods. + public static string GetNextTextElement(string str, int index) + { + // + // Validate parameters. + // + if (str == null) + { + throw new ArgumentNullException(nameof(str)); + } + + int len = str.Length; + if (index < 0 || index >= len) + { + if (index == len) + { + return (String.Empty); + } + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + } + + int charLen; + UnicodeCategory uc = CharUnicodeInfo.InternalGetUnicodeCategory(str, index, out charLen); + return (str.Substring(index, GetCurrentTextElementLen(str, index, len, ref uc, ref charLen))); + } + + public static TextElementEnumerator GetTextElementEnumerator(string str) + { + return (GetTextElementEnumerator(str, 0)); + } + + public static TextElementEnumerator GetTextElementEnumerator(string str, int index) + { + // + // Validate parameters. + // + if (str == null) + { + throw new ArgumentNullException(nameof(str)); + } + + int len = str.Length; + if (index < 0 || (index > len)) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + } + + return (new TextElementEnumerator(str, index, len)); + } + + /* + * Returns the indices of each base character or properly formed surrogate pair + * within the str. It recognizes a base character plus one or more combining + * characters or a properly formed surrogate pair as a text element and returns + * the index of the base character or high surrogate. Each index is the + * beginning of a text element within a str. The length of each element is + * easily computed as the difference between successive indices. The length of + * the array will always be less than or equal to the length of the str. For + * example, given the str \u4f00\u302a\ud800\udc00\u4f01, this method would + * return the indices: 0, 2, 4. + */ + + public static int[] ParseCombiningCharacters(string str) + { + if (str == null) + { + throw new ArgumentNullException(nameof(str)); + } + + int len = str.Length; + int[] result = new int[len]; + if (len == 0) + { + return (result); + } + + int resultCount = 0; + + int i = 0; + int currentCharLen; + UnicodeCategory currentCategory = CharUnicodeInfo.InternalGetUnicodeCategory(str, 0, out currentCharLen); + + while (i < len) + { + result[resultCount++] = i; + i += GetCurrentTextElementLen(str, i, len, ref currentCategory, ref currentCharLen); + } + + if (resultCount < len) + { + int[] returnArray = new int[resultCount]; + Array.Copy(result, returnArray, resultCount); + return (returnArray); + } + return (result); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/TaiwanCalendar.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/TaiwanCalendar.cs new file mode 100644 index 0000000000..c5161138de --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/TaiwanCalendar.cs @@ -0,0 +1,281 @@ +// 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.Diagnostics.CodeAnalysis; + +namespace System.Globalization +{ + /*=================================TaiwanCalendar========================== + ** + ** Taiwan calendar is based on the Gregorian calendar. And the year is an offset to Gregorian calendar. + ** That is, + ** Taiwan year = Gregorian year - 1911. So 1912/01/01 A.D. is Taiwan 1/01/01 + ** + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 1912/01/01 9999/12/31 + ** Taiwan 01/01/01 8088/12/31 + ============================================================================*/ + + public class TaiwanCalendar : Calendar + { + // + // The era value for the current era. + // + + // Since + // Gregorian Year = Era Year + yearOffset + // When Gregorian Year 1912 is year 1, so that + // 1912 = 1 + yearOffset + // So yearOffset = 1911 + //m_EraInfo[0] = new EraInfo(1, new DateTime(1912, 1, 1).Ticks, 1911, 1, GregorianCalendar.MaxYear - 1911); + + // Initialize our era info. + internal static EraInfo[] taiwanEraInfo = new EraInfo[] { + new EraInfo( 1, 1912, 1, 1, 1911, 1, GregorianCalendar.MaxYear - 1911) // era #, start year/month/day, yearOffset, minEraYear + }; + + internal static volatile Calendar s_defaultInstance; + + internal GregorianCalendarHelper helper; + + /*=================================GetDefaultInstance========================== + **Action: Internal method to provide a default intance of TaiwanCalendar. Used by NLS+ implementation + ** and other calendars. + **Returns: + **Arguments: + **Exceptions: + ============================================================================*/ + + internal static Calendar GetDefaultInstance() + { + if (s_defaultInstance == null) + { + s_defaultInstance = new TaiwanCalendar(); + } + return (s_defaultInstance); + } + + internal static readonly DateTime calendarMinValue = new DateTime(1912, 1, 1); + + + public override DateTime MinSupportedDateTime + { + get + { + return (calendarMinValue); + } + } + + public override DateTime MaxSupportedDateTime + { + get + { + return (DateTime.MaxValue); + } + } + + public override CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.SolarCalendar; + } + } + + // Return the type of the Taiwan calendar. + // + + public TaiwanCalendar() + { + try + { + new CultureInfo("zh-TW"); + } + catch (ArgumentException e) + { + throw new TypeInitializationException(this.GetType().ToString(), e); + } + helper = new GregorianCalendarHelper(this, taiwanEraInfo); + } + + internal override CalendarId ID + { + get + { + return CalendarId.TAIWAN; + } + } + + + public override DateTime AddMonths(DateTime time, int months) + { + return (helper.AddMonths(time, months)); + } + + + public override DateTime AddYears(DateTime time, int years) + { + return (helper.AddYears(time, years)); + } + + + public override int GetDaysInMonth(int year, int month, int era) + { + return (helper.GetDaysInMonth(year, month, era)); + } + + + public override int GetDaysInYear(int year, int era) + { + return (helper.GetDaysInYear(year, era)); + } + + + public override int GetDayOfMonth(DateTime time) + { + return (helper.GetDayOfMonth(time)); + } + + + public override DayOfWeek GetDayOfWeek(DateTime time) + { + return (helper.GetDayOfWeek(time)); + } + + + public override int GetDayOfYear(DateTime time) + { + return (helper.GetDayOfYear(time)); + } + + + public override int GetMonthsInYear(int year, int era) + { + return (helper.GetMonthsInYear(year, era)); + } + + + public override int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek) + { + return (helper.GetWeekOfYear(time, rule, firstDayOfWeek)); + } + + + public override int GetEra(DateTime time) + { + return (helper.GetEra(time)); + } + + public override int GetMonth(DateTime time) + { + return (helper.GetMonth(time)); + } + + + public override int GetYear(DateTime time) + { + return (helper.GetYear(time)); + } + + + public override bool IsLeapDay(int year, int month, int day, int era) + { + return (helper.IsLeapDay(year, month, day, era)); + } + + + public override bool IsLeapYear(int year, int era) + { + return (helper.IsLeapYear(year, era)); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this calendar does not have leap month, or this year is not a leap year. + // + + public override int GetLeapMonth(int year, int era) + { + return (helper.GetLeapMonth(year, era)); + } + + + public override bool IsLeapMonth(int year, int month, int era) + { + return (helper.IsLeapMonth(year, month, era)); + } + + + public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) + { + return (helper.ToDateTime(year, month, day, hour, minute, second, millisecond, era)); + } + + + public override int[] Eras + { + get + { + return (helper.Eras); + } + } + + private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 99; + + public override int TwoDigitYearMax + { + get + { + if (twoDigitYearMax == -1) + { + twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX); + } + return (twoDigitYearMax); + } + + set + { + VerifyWritable(); + if (value < 99 || value > helper.MaxYear) + { + throw new ArgumentOutOfRangeException( + "year", + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 99, + helper.MaxYear)); + } + twoDigitYearMax = value; + } + } + + // For Taiwan calendar, four digit year is not used. + // Therefore, for any two digit number, we just return the original number. + + public override int ToFourDigitYear(int year) + { + if (year <= 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedPosNum); + } + + if (year > helper.MaxYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + helper.MaxYear)); + } + return (year); + } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/TaiwanLunisolarCalendar.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/TaiwanLunisolarCalendar.cs new file mode 100644 index 0000000000..60e84f7f33 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/TaiwanLunisolarCalendar.cs @@ -0,0 +1,322 @@ +// 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; + +namespace System.Globalization +{ + /* + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 1912/02/18 2051/02/10 + ** TaiwanLunisolar 1912/01/01 2050/13/29 + */ + + public class TaiwanLunisolarCalendar : EastAsianLunisolarCalendar + { + // Since + // Gregorian Year = Era Year + yearOffset + // When Gregorian Year 1912 is year 1, so that + // 1912 = 1 + yearOffset + // So yearOffset = 1911 + //m_EraInfo[0] = new EraInfo(1, new DateTime(1912, 1, 1).Ticks, 1911, 1, GregorianCalendar.MaxYear - 1911); + + // Initialize our era info. + internal static EraInfo[] taiwanLunisolarEraInfo = new EraInfo[] { + new EraInfo( 1, 1912, 1, 1, 1911, 1, GregorianCalendar.MaxYear - 1911) // era #, start year/month/day, yearOffset, minEraYear + }; + + internal GregorianCalendarHelper helper; + + internal const int MIN_LUNISOLAR_YEAR = 1912; + internal const int MAX_LUNISOLAR_YEAR = 2050; + + internal const int MIN_GREGORIAN_YEAR = 1912; + internal const int MIN_GREGORIAN_MONTH = 2; + internal const int MIN_GREGORIAN_DAY = 18; + + internal const int MAX_GREGORIAN_YEAR = 2051; + internal const int MAX_GREGORIAN_MONTH = 2; + internal const int MAX_GREGORIAN_DAY = 10; + + internal static DateTime minDate = new DateTime(MIN_GREGORIAN_YEAR, MIN_GREGORIAN_MONTH, MIN_GREGORIAN_DAY); + internal static DateTime maxDate = new DateTime((new DateTime(MAX_GREGORIAN_YEAR, MAX_GREGORIAN_MONTH, MAX_GREGORIAN_DAY, 23, 59, 59, 999)).Ticks + 9999); + + public override DateTime MinSupportedDateTime + { + get + { + return (minDate); + } + } + + + + public override DateTime MaxSupportedDateTime + { + get + { + return (maxDate); + } + } + + protected override int DaysInYearBeforeMinSupportedYear + { + get + { + // 1911 from ChineseLunisolarCalendar + return 384; + } + } + + private static readonly int[,] s_yinfo = + { + /*Y LM Lmon Lday DaysPerMonth D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 D13 #Days + 1912 */ + { 0 , 2 , 18 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +1913 */{ 0 , 2 , 6 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +1914 */{ 5 , 1 , 26 , 54568 },/* 30 30 29 30 29 30 29 30 29 29 30 29 30 384 +1915 */{ 0 , 2 , 14 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354 +1916 */{ 0 , 2 , 3 , 54944 },/* 30 30 29 30 29 30 30 29 30 29 30 29 0 355 +1917 */{ 2 , 1 , 23 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384 +1918 */{ 0 , 2 , 11 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1919 */{ 7 , 2 , 1 , 18872 },/* 29 30 29 29 30 29 29 30 30 29 30 30 30 384 +1920 */{ 0 , 2 , 20 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1921 */{ 0 , 2 , 8 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1922 */{ 5 , 1 , 28 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384 +1923 */{ 0 , 2 , 16 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354 +1924 */{ 0 , 2 , 5 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354 +1925 */{ 4 , 1 , 24 , 44456 },/* 30 29 30 29 30 30 29 30 30 29 30 29 30 385 +1926 */{ 0 , 2 , 13 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1927 */{ 0 , 2 , 2 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 0 355 +1928 */{ 2 , 1 , 23 , 18808 },/* 29 30 29 29 30 29 29 30 29 30 30 30 30 384 +1929 */{ 0 , 2 , 10 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1930 */{ 6 , 1 , 30 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 29 383 +1931 */{ 0 , 2 , 17 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +1932 */{ 0 , 2 , 6 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355 +1933 */{ 5 , 1 , 26 , 27976 },/* 29 30 30 29 30 30 29 30 29 30 29 29 30 384 +1934 */{ 0 , 2 , 14 , 23248 },/* 29 30 29 30 30 29 30 29 30 30 29 30 0 355 +1935 */{ 0 , 2 , 4 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1936 */{ 3 , 1 , 24 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 29 384 +1937 */{ 0 , 2 , 11 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +1938 */{ 7 , 1 , 31 , 51560 },/* 30 30 29 29 30 29 29 30 29 30 30 29 30 384 +1939 */{ 0 , 2 , 19 , 51536 },/* 30 30 29 29 30 29 29 30 29 30 29 30 0 354 +1940 */{ 0 , 2 , 8 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +1941 */{ 6 , 1 , 27 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 29 384 +1942 */{ 0 , 2 , 15 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +1943 */{ 0 , 2 , 5 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1944 */{ 4 , 1 , 25 , 43736 },/* 30 29 30 29 30 29 30 29 30 30 29 30 30 385 +1945 */{ 0 , 2 , 13 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 +1946 */{ 0 , 2 , 2 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354 +1947 */{ 2 , 1 , 22 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384 +1948 */{ 0 , 2 , 10 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +1949 */{ 7 , 1 , 29 , 46248 },/* 30 29 30 30 29 30 29 29 30 29 30 29 30 384 +1950 */{ 0 , 2 , 17 , 27808 },/* 29 30 30 29 30 30 29 29 30 29 30 29 0 354 +1951 */{ 0 , 2 , 6 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +1952 */{ 5 , 1 , 27 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384 +1953 */{ 0 , 2 , 14 , 19872 },/* 29 30 29 29 30 30 29 30 30 29 30 29 0 354 +1954 */{ 0 , 2 , 3 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355 +1955 */{ 3 , 1 , 24 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384 +1956 */{ 0 , 2 , 12 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +1957 */{ 8 , 1 , 31 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 29 383 +1958 */{ 0 , 2 , 18 , 59728 },/* 30 30 30 29 30 29 29 30 29 30 29 30 0 355 +1959 */{ 0 , 2 , 8 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +1960 */{ 6 , 1 , 28 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384 +1961 */{ 0 , 2 , 15 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355 +1962 */{ 0 , 2 , 5 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 +1963 */{ 4 , 1 , 25 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384 +1964 */{ 0 , 2 , 13 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +1965 */{ 0 , 2 , 2 , 21088 },/* 29 30 29 30 29 29 30 29 29 30 30 29 0 353 +1966 */{ 3 , 1 , 21 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384 +1967 */{ 0 , 2 , 9 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355 +1968 */{ 7 , 1 , 30 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 +1969 */{ 0 , 2 , 17 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1970 */{ 0 , 2 , 6 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355 +1971 */{ 5 , 1 , 27 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +1972 */{ 0 , 2 , 15 , 19152 },/* 29 30 29 29 30 29 30 29 30 30 29 30 0 354 +1973 */{ 0 , 2 , 3 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +1974 */{ 4 , 1 , 23 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384 +1975 */{ 0 , 2 , 11 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +1976 */{ 8 , 1 , 31 , 54568 },/* 30 30 29 30 29 30 29 30 29 29 30 29 30 384 +1977 */{ 0 , 2 , 18 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354 +1978 */{ 0 , 2 , 7 , 46752 },/* 30 29 30 30 29 30 30 29 30 29 30 29 0 355 +1979 */{ 6 , 1 , 28 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384 +1980 */{ 0 , 2 , 16 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1981 */{ 0 , 2 , 5 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +1982 */{ 4 , 1 , 25 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384 +1983 */{ 0 , 2 , 13 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1984 */{ 10 , 2 , 2 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384 +1985 */{ 0 , 2 , 20 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354 +1986 */{ 0 , 2 , 9 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354 +1987 */{ 6 , 1 , 29 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 29 384 +1988 */{ 0 , 2 , 17 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355 +1989 */{ 0 , 2 , 6 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 0 355 +1990 */{ 5 , 1 , 27 , 18808 },/* 29 30 29 29 30 29 29 30 29 30 30 30 30 384 +1991 */{ 0 , 2 , 15 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1992 */{ 0 , 2 , 4 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354 +1993 */{ 3 , 1 , 23 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383 +1994 */{ 0 , 2 , 10 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355 +1995 */{ 8 , 1 , 31 , 27432 },/* 29 30 30 29 30 29 30 30 29 29 30 29 30 384 +1996 */{ 0 , 2 , 19 , 23232 },/* 29 30 29 30 30 29 30 29 30 30 29 29 0 354 +1997 */{ 0 , 2 , 7 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355 +1998 */{ 5 , 1 , 28 , 37736 },/* 30 29 29 30 29 29 30 30 29 30 30 29 30 384 +1999 */{ 0 , 2 , 16 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +2000 */{ 0 , 2 , 5 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354 +2001 */{ 4 , 1 , 24 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384 +2002 */{ 0 , 2 , 12 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +2003 */{ 0 , 2 , 1 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355 +2004 */{ 2 , 1 , 22 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 +2005 */{ 0 , 2 , 9 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +2006 */{ 7 , 1 , 29 , 43736 },/* 30 29 30 29 30 29 30 29 30 30 29 30 30 385 +2007 */{ 0 , 2 , 18 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 +2008 */{ 0 , 2 , 7 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354 +2009 */{ 5 , 1 , 26 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384 +2010 */{ 0 , 2 , 14 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +2011 */{ 0 , 2 , 3 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354 +2012 */{ 4 , 1 , 23 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384 +2013 */{ 0 , 2 , 10 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +2014 */{ 9 , 1 , 31 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384 +2015 */{ 0 , 2 , 19 , 19360 },/* 29 30 29 29 30 29 30 30 30 29 30 29 0 354 +2016 */{ 0 , 2 , 8 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355 +2017 */{ 6 , 1 , 28 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384 +2018 */{ 0 , 2 , 16 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +2019 */{ 0 , 2 , 5 , 43312 },/* 30 29 30 29 30 29 29 30 29 29 30 30 0 354 +2020 */{ 4 , 1 , 25 , 29864 },/* 29 30 30 30 29 30 29 29 30 29 30 29 30 384 +2021 */{ 0 , 2 , 12 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +2022 */{ 0 , 2 , 1 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +2023 */{ 2 , 1 , 22 , 19880 },/* 29 30 29 29 30 30 29 30 30 29 30 29 30 384 +2024 */{ 0 , 2 , 10 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 +2025 */{ 6 , 1 , 29 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384 +2026 */{ 0 , 2 , 17 , 42208 },/* 30 29 30 29 29 30 29 29 30 30 30 29 0 354 +2027 */{ 0 , 2 , 6 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354 +2028 */{ 5 , 1 , 26 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384 +2029 */{ 0 , 2 , 13 , 54576 },/* 30 30 29 30 29 30 29 30 29 29 30 30 0 355 +2030 */{ 0 , 2 , 3 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 +2031 */{ 3 , 1 , 23 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 29 384 +2032 */{ 0 , 2 , 11 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355 +2033 */{ 11 , 1 , 31 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +2034 */{ 0 , 2 , 19 , 19152 },/* 29 30 29 29 30 29 30 29 30 30 29 30 0 354 +2035 */{ 0 , 2 , 8 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +2036 */{ 6 , 1 , 28 , 53848 },/* 30 30 29 30 29 29 30 29 29 30 29 30 30 384 +2037 */{ 0 , 2 , 15 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +2038 */{ 0 , 2 , 4 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354 +2039 */{ 5 , 1 , 24 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 29 384 +2040 */{ 0 , 2 , 12 , 46496 },/* 30 29 30 30 29 30 29 30 30 29 30 29 0 355 +2041 */{ 0 , 2 , 1 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355 +2042 */{ 2 , 1 , 22 , 19160 },/* 29 30 29 29 30 29 30 29 30 30 29 30 30 384 +2043 */{ 0 , 2 , 10 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +2044 */{ 7 , 1 , 30 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384 +2045 */{ 0 , 2 , 17 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +2046 */{ 0 , 2 , 6 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354 +2047 */{ 5 , 1 , 26 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384 +2048 */{ 0 , 2 , 14 , 27936 },/* 29 30 30 29 30 30 29 30 29 29 30 29 0 354 +2049 */{ 0 , 2 , 2 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 0 355 +2050 */{ 3 , 1 , 23 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 29 384 + */}; + + + internal override int MinCalendarYear + { + get + { + return (MIN_LUNISOLAR_YEAR); + } + } + + internal override int MaxCalendarYear + { + get + { + return (MAX_LUNISOLAR_YEAR); + } + } + + internal override DateTime MinDate + { + get + { + return (minDate); + } + } + + internal override DateTime MaxDate + { + get + { + return (maxDate); + } + } + + internal override EraInfo[] CalEraInfo + { + get + { + return (taiwanLunisolarEraInfo); + } + } + + internal override int GetYearInfo(int lunarYear, int index) + { + if ((lunarYear < MIN_LUNISOLAR_YEAR) || (lunarYear > MAX_LUNISOLAR_YEAR)) + { + throw new ArgumentOutOfRangeException( + "year", + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + MIN_LUNISOLAR_YEAR, + MAX_LUNISOLAR_YEAR)); + } + + return s_yinfo[lunarYear - MIN_LUNISOLAR_YEAR, index]; + } + + internal override int GetYear(int year, DateTime time) + { + return helper.GetYear(year, time); + } + + internal override int GetGregorianYear(int year, int era) + { + return helper.GetGregorianYear(year, era); + } + + public TaiwanLunisolarCalendar() + { + helper = new GregorianCalendarHelper(this, taiwanLunisolarEraInfo); + } + + public override int GetEra(DateTime time) + { + return (helper.GetEra(time)); + } + + internal override CalendarId BaseCalendarID + { + get + { + return (CalendarId.TAIWAN); + } + } + + internal override CalendarId ID + { + get + { + return (CalendarId.TAIWANLUNISOLAR); + } + } + + + + public override int[] Eras + { + get + { + return (helper.Eras); + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/TextElementEnumerator.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/TextElementEnumerator.cs similarity index 93% rename from external/corert/src/System.Private.CoreLib/src/System/Globalization/TextElementEnumerator.cs rename to external/corefx/src/Common/src/CoreLib/System/Globalization/TextElementEnumerator.cs index 4013a67dd4..8b0f102a77 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/TextElementEnumerator.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/TextElementEnumerator.cs @@ -12,7 +12,6 @@ using System.Collections; using System.Diagnostics; -using System.Runtime.Serialization; namespace System.Globalization { @@ -20,17 +19,14 @@ namespace System.Globalization // This is public because GetTextElement() is public. // - [Serializable] public class TextElementEnumerator : IEnumerator { private String _str; private int _index; private int _startIndex; - [NonSerialized] private int _strLen; // This is the length of the total string, counting from the beginning of string. - [NonSerialized] private int _currTextElementLen; // The current text element lenght after MoveNext() is called. private UnicodeCategory _uc; @@ -52,7 +48,7 @@ namespace System.Globalization { if (_index >= _strLen) { - // Make the index to be greater than strLen so that we can throw exception if GetTextElement() is called. + // Make the _index to be greater than _strLen so that we can throw exception if GetTextElement() is called. _index = _strLen + 1; return (false); } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/TextInfo.Unix.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/TextInfo.Unix.cs similarity index 83% rename from external/corert/src/System.Private.CoreLib/src/System/Globalization/TextInfo.Unix.cs rename to external/corefx/src/Common/src/CoreLib/System/Globalization/TextInfo.Unix.cs index 73e50e3f66..dd2433d18f 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/TextInfo.Unix.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/TextInfo.Unix.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Security; using System.Text; @@ -11,30 +10,16 @@ namespace System.Globalization { public partial class TextInfo { - [NonSerialized] private Tristate _needsTurkishCasing = Tristate.NotInitialized; - ////////////////////////////////////////////////////////////////////////// - //// - //// TextInfo Constructors - //// - //// Implements CultureInfo.TextInfo. - //// - ////////////////////////////////////////////////////////////////////////// - internal unsafe TextInfo(CultureData cultureData) - { - _cultureData = cultureData; - _cultureName = _cultureData.CultureName; - _textInfoName = _cultureData.STEXTINFO; - FinishInitialization(_textInfoName); - } - - private void FinishInitialization(string textInfoName) + private void FinishInitialization() { } private unsafe string ChangeCase(string s, bool toUpper) { + Debug.Assert(!_invariantMode); + Debug.Assert(s != null); if (s.Length == 0) @@ -81,6 +66,8 @@ namespace System.Globalization private unsafe char ChangeCase(char c, bool toUpper) { + Debug.Assert(!_invariantMode); + char dst = default(char); ChangeCase(&c, 1, &dst, 1, toUpper); @@ -103,6 +90,8 @@ namespace System.Globalization internal unsafe void ChangeCase(char* src, int srcLen, char* dstBuffer, int dstBufferCapacity, bool bToUpper) { + Debug.Assert(!_invariantMode); + if (IsInvariant) { Interop.GlobalizationInterop.ChangeCaseInvariant(src, srcLen, dstBuffer, dstBufferCapacity, bToUpper); diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/TextInfo.Windows.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/TextInfo.Windows.cs new file mode 100644 index 0000000000..8b27e42d1d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/TextInfo.Windows.cs @@ -0,0 +1,113 @@ +// 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.Diagnostics; + +namespace System.Globalization +{ + public partial class TextInfo + { + private unsafe void FinishInitialization() + { + if (_invariantMode) + { + _sortHandle = IntPtr.Zero; + return; + } + + const uint LCMAP_SORTHANDLE = 0x20000000; + + IntPtr handle; + int ret = Interop.Kernel32.LCMapStringEx(_textInfoName, LCMAP_SORTHANDLE, null, 0, &handle, IntPtr.Size, null, null, IntPtr.Zero); + _sortHandle = ret > 0 ? handle : IntPtr.Zero; + } + + private unsafe string ChangeCase(string s, bool toUpper) + { + Debug.Assert(!_invariantMode); + + Debug.Assert(s != null); + + // + // Get the length of the string. + // + int nLengthInput = s.Length; + + // + // Check if we have the empty string. + // + if (nLengthInput == 0) + { + return s; + } + + int ret; + + // Check for Invariant to avoid A/V in LCMapStringEx + uint linguisticCasing = IsInvariantLocale(_textInfoName) ? 0 : LCMAP_LINGUISTIC_CASING; + + // + // Create the result string. + // + string result = string.FastAllocateString(nLengthInput); + + fixed (char* pSource = s) + fixed (char* pResult = result) + { + ret = Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _textInfoName, + linguisticCasing | (toUpper ? LCMAP_UPPERCASE : LCMAP_LOWERCASE), + pSource, + nLengthInput, + pResult, + nLengthInput, + null, + null, + _sortHandle); + } + + if (ret == 0) + { + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + } + + Debug.Assert(ret == nLengthInput, "Expected getting the same length of the original string"); + return result; + } + + private unsafe char ChangeCase(char c, bool toUpper) + { + Debug.Assert(!_invariantMode); + + char retVal = '\0'; + + // Check for Invariant to avoid A/V in LCMapStringEx + uint linguisticCasing = IsInvariantLocale(_textInfoName) ? 0 : LCMAP_LINGUISTIC_CASING; + + Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _textInfoName, + toUpper ? LCMAP_UPPERCASE | linguisticCasing : LCMAP_LOWERCASE | linguisticCasing, + &c, + 1, + &retVal, + 1, + null, + null, + _sortHandle); + + return retVal; + } + + // PAL Ends here + + private IntPtr _sortHandle; + + private const uint LCMAP_LINGUISTIC_CASING = 0x01000000; + private const uint LCMAP_LOWERCASE = 0x00000100; + private const uint LCMAP_UPPERCASE = 0x00000200; + + private static bool IsInvariantLocale(string localeName) + { + return localeName == ""; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/TextInfo.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/TextInfo.cs new file mode 100644 index 0000000000..d9d6f3bd3b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/TextInfo.cs @@ -0,0 +1,736 @@ +// 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. + +//////////////////////////////////////////////////////////////////////////// +// +// +// Purpose: This Class defines behaviors specific to a writing system. +// A writing system is the collection of scripts and +// orthographic rules required to represent a language as text. +// +// +//////////////////////////////////////////////////////////////////////////// + +using System.Diagnostics; +using System.Runtime.Serialization; +using System.Text; + +namespace System.Globalization +{ + public partial class TextInfo : ICloneable, IDeserializationCallback + { + private enum Tristate : byte + { + NotInitialized, + True, + False, + } + + private string _listSeparator; + private bool _isReadOnly = false; + + /* _cultureName is the name of the creating culture. + _cultureData is the data that backs this class. + _textInfoName is the actual name of the textInfo (from cultureData.STEXTINFO) + In the desktop, when we call the sorting dll, it doesn't + know how to resolve custom locle names to sort ids so we have to have already resolved this. + */ + + private readonly string _cultureName; // Name of the culture that created this text info + private readonly CultureData _cultureData; // Data record for the culture that made us, not for this textinfo + private readonly string _textInfoName; // Name of the text info we're using (ie: _cultureData.STEXTINFO) + + private Tristate _isAsciiCasingSameAsInvariant = Tristate.NotInitialized; + + // _invariantMode is defined for the perf reason as accessing the instance field is faster than access the static property GlobalizationMode.Invariant + private readonly bool _invariantMode = GlobalizationMode.Invariant; + + // Invariant text info + internal static TextInfo Invariant + { + get + { + if (s_Invariant == null) + s_Invariant = new TextInfo(CultureData.Invariant); + return s_Invariant; + } + } + internal volatile static TextInfo s_Invariant; + + ////////////////////////////////////////////////////////////////////////// + //// + //// TextInfo Constructors + //// + //// Implements CultureInfo.TextInfo. + //// + ////////////////////////////////////////////////////////////////////////// + internal TextInfo(CultureData cultureData) + { + // This is our primary data source, we don't need most of the rest of this + _cultureData = cultureData; + _cultureName = _cultureData.CultureName; + _textInfoName = _cultureData.STEXTINFO; + + FinishInitialization(); + } + + void IDeserializationCallback.OnDeserialization(Object sender) + { + throw new PlatformNotSupportedException(); + } + + // + // Internal ordinal comparison functions + // + + internal static int GetHashCodeOrdinalIgnoreCase(string s) + { + // This is the same as an case insensitive hash for Invariant + // (not necessarily true for sorting, but OK for casing & then we apply normal hash code rules) + return Invariant.GetCaseInsensitiveHashCode(s); + } + + public virtual int ANSICodePage => _cultureData.IDEFAULTANSICODEPAGE; + + public virtual int OEMCodePage => _cultureData.IDEFAULTOEMCODEPAGE; + + public virtual int MacCodePage => _cultureData.IDEFAULTMACCODEPAGE; + + public virtual int EBCDICCodePage => _cultureData.IDEFAULTEBCDICCODEPAGE; + + // Just use the LCID from our text info name + public int LCID => CultureInfo.GetCultureInfo(_textInfoName).LCID; + + public string CultureName => _textInfoName; + + public bool IsReadOnly => _isReadOnly; + + ////////////////////////////////////////////////////////////////////////// + //// + //// Clone + //// + //// Is the implementation of ICloneable. + //// + ////////////////////////////////////////////////////////////////////////// + public virtual object Clone() + { + object o = MemberwiseClone(); + ((TextInfo)o).SetReadOnlyState(false); + return o; + } + + //////////////////////////////////////////////////////////////////////// + // + // ReadOnly + // + // Create a cloned readonly instance or return the input one if it is + // readonly. + // + //////////////////////////////////////////////////////////////////////// + public static TextInfo ReadOnly(TextInfo textInfo) + { + if (textInfo == null) { throw new ArgumentNullException(nameof(textInfo)); } + if (textInfo.IsReadOnly) { return textInfo; } + + TextInfo clonedTextInfo = (TextInfo)(textInfo.MemberwiseClone()); + clonedTextInfo.SetReadOnlyState(true); + + return clonedTextInfo; + } + + private void VerifyWritable() + { + if (_isReadOnly) + { + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + } + } + + internal void SetReadOnlyState(bool readOnly) + { + _isReadOnly = readOnly; + } + + + //////////////////////////////////////////////////////////////////////// + // + // ListSeparator + // + // Returns the string used to separate items in a list. + // + //////////////////////////////////////////////////////////////////////// + public virtual string ListSeparator + { + get + { + if (_listSeparator == null) + { + _listSeparator = _cultureData.SLIST; + } + return _listSeparator; + } + + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value), SR.ArgumentNull_String); + } + VerifyWritable(); + _listSeparator = value; + } + } + + //////////////////////////////////////////////////////////////////////// + // + // ToLower + // + // Converts the character or string to lower case. Certain locales + // have different casing semantics from the file systems in Win32. + // + //////////////////////////////////////////////////////////////////////// + public unsafe virtual char ToLower(char c) + { + if (_invariantMode || (IsAscii(c) && IsAsciiCasingSameAsInvariant)) + { + return ToLowerAsciiInvariant(c); + } + + return ChangeCase(c, toUpper: false); + } + + public unsafe virtual string ToLower(string str) + { + if (str == null) { throw new ArgumentNullException(nameof(str)); } + + if (_invariantMode) + { + return ToLowerAsciiInvariant(str); + } + + return ChangeCase(str, toUpper: false); + } + + private unsafe string ToLowerAsciiInvariant(string s) + { + if (s.Length == 0) + { + return string.Empty; + } + + fixed (char* pSource = s) + { + int i = 0; + while (i < s.Length) + { + if ((uint)(pSource[i] - 'A') <= (uint)('Z' - 'A')) + { + break; + } + i++; + } + + if (i >= s.Length) + { + return s; + } + + string result = string.FastAllocateString(s.Length); + fixed (char* pResult = result) + { + for (int j = 0; j < i; j++) + { + pResult[j] = pSource[j]; + } + + pResult[i] = (char)(pSource[i] | 0x20); + i++; + + while (i < s.Length) + { + pResult[i] = ToLowerAsciiInvariant(pSource[i]); + i++; + } + } + + return result; + } + } + + private unsafe string ToUpperAsciiInvariant(string s) + { + if (s.Length == 0) + { + return string.Empty; + } + + fixed (char* pSource = s) + { + int i = 0; + while (i < s.Length) + { + if ((uint)(pSource[i] - 'a') <= (uint)('z' - 'a')) + { + break; + } + i++; + } + + if (i >= s.Length) + { + return s; + } + + string result = string.FastAllocateString(s.Length); + fixed (char* pResult = result) + { + for (int j = 0; j < i; j++) + { + pResult[j] = pSource[j]; + } + + pResult[i] = (char)(pSource[i] & ~0x20); + i++; + + while (i < s.Length) + { + pResult[i] = ToUpperAsciiInvariant(pSource[i]); + i++; + } + } + + return result; + } + } + + private static char ToLowerAsciiInvariant(char c) + { + if ((uint)(c - 'A') <= (uint)('Z' - 'A')) + { + c = (char)(c | 0x20); + } + return c; + } + + //////////////////////////////////////////////////////////////////////// + // + // ToUpper + // + // Converts the character or string to upper case. Certain locales + // have different casing semantics from the file systems in Win32. + // + //////////////////////////////////////////////////////////////////////// + public unsafe virtual char ToUpper(char c) + { + if (_invariantMode || (IsAscii(c) && IsAsciiCasingSameAsInvariant)) + { + return ToUpperAsciiInvariant(c); + } + + return ChangeCase(c, toUpper: true); + } + + public unsafe virtual string ToUpper(string str) + { + if (str == null) { throw new ArgumentNullException(nameof(str)); } + + if (_invariantMode) + { + return ToUpperAsciiInvariant(str); + } + + return ChangeCase(str, toUpper: true); + } + + internal static char ToUpperAsciiInvariant(char c) + { + if ((uint)(c - 'a') <= (uint)('z' - 'a')) + { + c = (char)(c & ~0x20); + } + return c; + } + + private static bool IsAscii(char c) + { + return c < 0x80; + } + + private bool IsAsciiCasingSameAsInvariant + { + get + { + if (_isAsciiCasingSameAsInvariant == Tristate.NotInitialized) + { + _isAsciiCasingSameAsInvariant = CultureInfo.GetCultureInfo(_textInfoName).CompareInfo.Compare("abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + CompareOptions.IgnoreCase) == 0 ? Tristate.True : Tristate.False; + } + return _isAsciiCasingSameAsInvariant == Tristate.True; + } + } + + // IsRightToLeft + // + // Returns true if the dominant direction of text and UI such as the relative position of buttons and scroll bars + // + public bool IsRightToLeft => _cultureData.IsRightToLeft; + + //////////////////////////////////////////////////////////////////////// + // + // Equals + // + // Implements Object.Equals(). Returns a boolean indicating whether + // or not object refers to the same CultureInfo as the current instance. + // + //////////////////////////////////////////////////////////////////////// + public override bool Equals(Object obj) + { + TextInfo that = obj as TextInfo; + + if (that != null) + { + return CultureName.Equals(that.CultureName); + } + + return false; + } + + //////////////////////////////////////////////////////////////////////// + // + // GetHashCode + // + // Implements Object.GetHashCode(). Returns the hash code for the + // CultureInfo. The hash code is guaranteed to be the same for CultureInfo A + // and B where A.Equals(B) is true. + // + //////////////////////////////////////////////////////////////////////// + public override int GetHashCode() + { + return CultureName.GetHashCode(); + } + + //////////////////////////////////////////////////////////////////////// + // + // ToString + // + // Implements Object.ToString(). Returns a string describing the + // TextInfo. + // + //////////////////////////////////////////////////////////////////////// + public override string ToString() + { + return "TextInfo - " + _cultureData.CultureName; + } + + // + // Titlecasing: + // ----------- + // Titlecasing refers to a casing practice wherein the first letter of a word is an uppercase letter + // and the rest of the letters are lowercase. The choice of which words to titlecase in headings + // and titles is dependent on language and local conventions. For example, "The Merry Wives of Windor" + // is the appropriate titlecasing of that play's name in English, with the word "of" not titlecased. + // In German, however, the title is "Die lustigen Weiber von Windsor," and both "lustigen" and "von" + // are not titlecased. In French even fewer words are titlecased: "Les joyeuses commeres de Windsor." + // + // Moreover, the determination of what actually constitutes a word is language dependent, and this can + // influence which letter or letters of a "word" are uppercased when titlecasing strings. For example + // "l'arbre" is considered two words in French, whereas "can't" is considered one word in English. + // + public unsafe string ToTitleCase(string str) + { + if (str == null) + { + throw new ArgumentNullException(nameof(str)); + } + if (str.Length == 0) + { + return str; + } + + StringBuilder result = new StringBuilder(); + string lowercaseData = null; + // Store if the current culture is Dutch (special case) + bool isDutchCulture = CultureName.StartsWith("nl-", StringComparison.OrdinalIgnoreCase); + + for (int i = 0; i < str.Length; i++) + { + UnicodeCategory charType; + int charLen; + + charType = CharUnicodeInfo.InternalGetUnicodeCategory(str, i, out charLen); + if (char.CheckLetter(charType)) + { + // Special case to check for Dutch specific titlecasing with "IJ" characters + // at the beginning of a word + if (isDutchCulture && i < str.Length - 1 && (str[i] == 'i' || str[i] == 'I') && (str[i+1] == 'j' || str[i+1] == 'J')) + { + result.Append("IJ"); + i += 2; + } + else + { + // Do the titlecasing for the first character of the word. + i = AddTitlecaseLetter(ref result, ref str, i, charLen) + 1; + } + + // + // Convert the characters until the end of the this word + // to lowercase. + // + int lowercaseStart = i; + + // + // Use hasLowerCase flag to prevent from lowercasing acronyms (like "URT", "USA", etc) + // This is in line with Word 2000 behavior of titlecasing. + // + bool hasLowerCase = (charType == UnicodeCategory.LowercaseLetter); + // Use a loop to find all of the other letters following this letter. + while (i < str.Length) + { + charType = CharUnicodeInfo.InternalGetUnicodeCategory(str, i, out charLen); + if (IsLetterCategory(charType)) + { + if (charType == UnicodeCategory.LowercaseLetter) + { + hasLowerCase = true; + } + i += charLen; + } + else if (str[i] == '\'') + { + i++; + if (hasLowerCase) + { + if (lowercaseData == null) + { + lowercaseData = ToLower(str); + } + result.Append(lowercaseData, lowercaseStart, i - lowercaseStart); + } + else + { + result.Append(str, lowercaseStart, i - lowercaseStart); + } + lowercaseStart = i; + hasLowerCase = true; + } + else if (!IsWordSeparator(charType)) + { + // This category is considered to be part of the word. + // This is any category that is marked as false in wordSeprator array. + i+= charLen; + } + else + { + // A word separator. Break out of the loop. + break; + } + } + + int count = i - lowercaseStart; + + if (count > 0) + { + if (hasLowerCase) + { + if (lowercaseData == null) + { + lowercaseData = ToLower(str); + } + result.Append(lowercaseData, lowercaseStart, count); + } + else + { + result.Append(str, lowercaseStart, count); + } + } + + if (i < str.Length) + { + // not a letter, just append it + i = AddNonLetter(ref result, ref str, i, charLen); + } + } + else + { + // not a letter, just append it + i = AddNonLetter(ref result, ref str, i, charLen); + } + } + return result.ToString(); + } + + private static int AddNonLetter(ref StringBuilder result, ref string input, int inputIndex, int charLen) + { + Debug.Assert(charLen == 1 || charLen == 2, "[TextInfo.AddNonLetter] CharUnicodeInfo.InternalGetUnicodeCategory returned an unexpected charLen!"); + if (charLen == 2) + { + // Surrogate pair + result.Append(input[inputIndex++]); + result.Append(input[inputIndex]); + } + else + { + result.Append(input[inputIndex]); + } + return inputIndex; + } + + private int AddTitlecaseLetter(ref StringBuilder result, ref string input, int inputIndex, int charLen) + { + Debug.Assert(charLen == 1 || charLen == 2, "[TextInfo.AddTitlecaseLetter] CharUnicodeInfo.InternalGetUnicodeCategory returned an unexpected charLen!"); + + // for surrogate pairs do a simple ToUpper operation on the substring + if (charLen == 2) + { + // Surrogate pair + result.Append(ToUpper(input.Substring(inputIndex, charLen))); + inputIndex++; + } + else + { + switch (input[inputIndex]) + { + // + // For AppCompat, the Titlecase Case Mapping data from NDP 2.0 is used below. + case (char) 0x01C4: // DZ with Caron -> Dz with Caron + case (char) 0x01C5: // Dz with Caron -> Dz with Caron + case (char) 0x01C6: // dz with Caron -> Dz with Caron + result.Append((char) 0x01C5); + break; + case (char) 0x01C7: // LJ -> Lj + case (char) 0x01C8: // Lj -> Lj + case (char) 0x01C9: // lj -> Lj + result.Append((char) 0x01C8); + break; + case (char) 0x01CA: // NJ -> Nj + case (char) 0x01CB: // Nj -> Nj + case (char) 0x01CC: // nj -> Nj + result.Append((char) 0x01CB); + break; + case (char) 0x01F1: // DZ -> Dz + case (char) 0x01F2: // Dz -> Dz + case (char) 0x01F3: // dz -> Dz + result.Append((char) 0x01F2); + break; + default: + result.Append(ToUpper(input[inputIndex])); + break; + } + } + return inputIndex; + } + + // + // Used in ToTitleCase(): + // When we find a starting letter, the following array decides if a category should be + // considered as word seprator or not. + // + private const int c_wordSeparatorMask = + /* false */ (0 << 0) | // UppercaseLetter = 0, + /* false */ (0 << 1) | // LowercaseLetter = 1, + /* false */ (0 << 2) | // TitlecaseLetter = 2, + /* false */ (0 << 3) | // ModifierLetter = 3, + /* false */ (0 << 4) | // OtherLetter = 4, + /* false */ (0 << 5) | // NonSpacingMark = 5, + /* false */ (0 << 6) | // SpacingCombiningMark = 6, + /* false */ (0 << 7) | // EnclosingMark = 7, + /* false */ (0 << 8) | // DecimalDigitNumber = 8, + /* false */ (0 << 9) | // LetterNumber = 9, + /* false */ (0 << 10) | // OtherNumber = 10, + /* true */ (1 << 11) | // SpaceSeparator = 11, + /* true */ (1 << 12) | // LineSeparator = 12, + /* true */ (1 << 13) | // ParagraphSeparator = 13, + /* true */ (1 << 14) | // Control = 14, + /* true */ (1 << 15) | // Format = 15, + /* false */ (0 << 16) | // Surrogate = 16, + /* false */ (0 << 17) | // PrivateUse = 17, + /* true */ (1 << 18) | // ConnectorPunctuation = 18, + /* true */ (1 << 19) | // DashPunctuation = 19, + /* true */ (1 << 20) | // OpenPunctuation = 20, + /* true */ (1 << 21) | // ClosePunctuation = 21, + /* true */ (1 << 22) | // InitialQuotePunctuation = 22, + /* true */ (1 << 23) | // FinalQuotePunctuation = 23, + /* true */ (1 << 24) | // OtherPunctuation = 24, + /* true */ (1 << 25) | // MathSymbol = 25, + /* true */ (1 << 26) | // CurrencySymbol = 26, + /* true */ (1 << 27) | // ModifierSymbol = 27, + /* true */ (1 << 28) | // OtherSymbol = 28, + /* false */ (0 << 29); // OtherNotAssigned = 29; + + private static bool IsWordSeparator(UnicodeCategory category) + { + return (c_wordSeparatorMask & (1 << (int) category)) != 0; + } + + private static bool IsLetterCategory(UnicodeCategory uc) + { + return (uc == UnicodeCategory.UppercaseLetter + || uc == UnicodeCategory.LowercaseLetter + || uc == UnicodeCategory.TitlecaseLetter + || uc == UnicodeCategory.ModifierLetter + || uc == UnicodeCategory.OtherLetter); + } + + // + // Get case-insensitive hash code for the specified string. + // + internal unsafe int GetCaseInsensitiveHashCode(string str) + { + // Validate inputs + if (str == null) + { + throw new ArgumentNullException(nameof(str)); + } + + // This code assumes that ASCII casing is safe for whatever context is passed in. + // this is true today, because we only ever call these methods on Invariant. It would be ideal to refactor + // these methods so they were correct by construction and we could only ever use Invariant. + + uint hash = 5381; + uint c; + + // Note: We assume that str contains only ASCII characters until + // we hit a non-ASCII character to optimize the common case. + for (int i = 0; i < str.Length; i++) + { + c = str[i]; + if (c >= 0x80) + { + return GetCaseInsensitiveHashCodeSlow(str); + } + + // If we have a lowercase character, ANDing off 0x20 + // will make it an uppercase character. + if ((c - 'a') <= ('z' - 'a')) + { + c = (uint)((int)c & ~0x20); + } + + hash = ((hash << 5) + hash) ^ c; + } + + return (int)hash; + } + + private unsafe int GetCaseInsensitiveHashCodeSlow(string str) + { + Debug.Assert(str != null); + + string upper = ToUpper(str); + + uint hash = 5381; + uint c; + + for (int i = 0; i < upper.Length; i++) + { + c = upper[i]; + hash = ((hash << 5) + hash) ^ c; + } + + return (int)hash; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/ThaiBuddhistCalendar.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/ThaiBuddhistCalendar.cs new file mode 100644 index 0000000000..4f8fd8f019 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/ThaiBuddhistCalendar.cs @@ -0,0 +1,232 @@ +// 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.Diagnostics.CodeAnalysis; + +namespace System.Globalization +{ + /*=================================ThaiBuddhistCalendar========================== + ** + ** ThaiBuddhistCalendar is based on Gregorian calendar. Its year value has + ** an offset to the Gregorain calendar. + ** + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 0001/01/01 9999/12/31 + ** Thai 0544/01/01 10542/12/31 + ============================================================================*/ + + public class ThaiBuddhistCalendar : Calendar + { + // Initialize our era info. + internal static EraInfo[] thaiBuddhistEraInfo = new EraInfo[] { + new EraInfo( 1, 1, 1, 1, -543, 544, GregorianCalendar.MaxYear + 543) // era #, start year/month/day, yearOffset, minEraYear + }; + + // + // The era value for the current era. + // + + public const int ThaiBuddhistEra = 1; + + internal GregorianCalendarHelper helper; + + + public override DateTime MinSupportedDateTime + { + get + { + return (DateTime.MinValue); + } + } + + public override DateTime MaxSupportedDateTime + { + get + { + return (DateTime.MaxValue); + } + } + + public override CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.SolarCalendar; + } + } + + public ThaiBuddhistCalendar() + { + helper = new GregorianCalendarHelper(this, thaiBuddhistEraInfo); + } + + internal override CalendarId ID + { + get + { + return (CalendarId.THAI); + } + } + + + public override DateTime AddMonths(DateTime time, int months) + { + return (helper.AddMonths(time, months)); + } + + + public override DateTime AddYears(DateTime time, int years) + { + return (helper.AddYears(time, years)); + } + + + public override int GetDaysInMonth(int year, int month, int era) + { + return (helper.GetDaysInMonth(year, month, era)); + } + + + public override int GetDaysInYear(int year, int era) + { + return (helper.GetDaysInYear(year, era)); + } + + + public override int GetDayOfMonth(DateTime time) + { + return (helper.GetDayOfMonth(time)); + } + + + public override DayOfWeek GetDayOfWeek(DateTime time) + { + return (helper.GetDayOfWeek(time)); + } + + + public override int GetDayOfYear(DateTime time) + { + return (helper.GetDayOfYear(time)); + } + + + public override int GetMonthsInYear(int year, int era) + { + return (helper.GetMonthsInYear(year, era)); + } + + + public override int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek) + { + return (helper.GetWeekOfYear(time, rule, firstDayOfWeek)); + } + + + public override int GetEra(DateTime time) + { + return (helper.GetEra(time)); + } + + public override int GetMonth(DateTime time) + { + return (helper.GetMonth(time)); + } + + + public override int GetYear(DateTime time) + { + return (helper.GetYear(time)); + } + + + public override bool IsLeapDay(int year, int month, int day, int era) + { + return (helper.IsLeapDay(year, month, day, era)); + } + + + public override bool IsLeapYear(int year, int era) + { + return (helper.IsLeapYear(year, era)); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this calendar does not have leap month, or this year is not a leap year. + // + + public override int GetLeapMonth(int year, int era) + { + return (helper.GetLeapMonth(year, era)); + } + + + public override bool IsLeapMonth(int year, int month, int era) + { + return (helper.IsLeapMonth(year, month, era)); + } + + + public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) + { + return (helper.ToDateTime(year, month, day, hour, minute, second, millisecond, era)); + } + + + public override int[] Eras + { + get + { + return (helper.Eras); + } + } + + private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 2572; + + + public override int TwoDigitYearMax + { + get + { + if (twoDigitYearMax == -1) + { + twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX); + } + return (twoDigitYearMax); + } + + set + { + VerifyWritable(); + if (value < 99 || value > helper.MaxYear) + { + throw new ArgumentOutOfRangeException( + "year", + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 99, + helper.MaxYear)); + } + twoDigitYearMax = value; + } + } + + + public override int ToFourDigitYear(int year) + { + if (year < 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedNonNegNum); + } + + return (helper.ToFourDigitYear(year, this.TwoDigitYearMax)); + } + } +} + diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/TimeSpanFormat.cs similarity index 62% rename from external/corert/src/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs rename to external/corefx/src/Common/src/CoreLib/System/Globalization/TimeSpanFormat.cs index 3457786a29..bf12b246b0 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/TimeSpanFormat.cs @@ -3,21 +3,36 @@ // See the LICENSE file in the project root for more information. using System.Text; -using System; -using System.Globalization; +using System.Diagnostics; namespace System.Globalization { internal static class TimeSpanFormat { - // auto-generated - private static String IntToString(int n, int digits) + private static unsafe void AppendNonNegativeInt32(StringBuilder sb, int n, int digits) { - return n.ToString(new string('0', digits)); + Debug.Assert(n >= 0); + uint value = (uint)n; + + const int MaxUInt32Digits = 10; + char* buffer = stackalloc char[MaxUInt32Digits]; + + int index = 0; + do + { + uint div = value / 10; + buffer[index++] = (char)(value - (div * 10) + '0'); + value = div; + } + while (value != 0); + Debug.Assert(index <= MaxUInt32Digits); + + for (int i = digits - index; i > 0; --i) sb.Append('0'); + for (int i = index - 1; i >= 0; --i) sb.Append(buffer[i]); } - internal static readonly FormatLiterals PositiveInvariantFormatLiterals = TimeSpanFormat.FormatLiterals.InitInvariant(false /*isNegative*/); - internal static readonly FormatLiterals NegativeInvariantFormatLiterals = TimeSpanFormat.FormatLiterals.InitInvariant(true /*isNegative*/); + internal static readonly FormatLiterals PositiveInvariantFormatLiterals = TimeSpanFormat.FormatLiterals.InitInvariant(isNegative: false); + internal static readonly FormatLiterals NegativeInvariantFormatLiterals = TimeSpanFormat.FormatLiterals.InitInvariant(isNegative: true); internal enum Pattern { @@ -26,51 +41,71 @@ namespace System.Globalization Full = 2, } - // - // Format - // - // Actions: Main method called from TimeSpan.ToString - // - internal static String Format(TimeSpan value, String format, IFormatProvider formatProvider) - { - if (format == null || format.Length == 0) - format = "c"; + /// Main method called from TimeSpan.ToString. + internal static string Format(TimeSpan value, string format, IFormatProvider formatProvider) => + StringBuilderCache.GetStringAndRelease(FormatToBuilder(value, format, formatProvider)); - // standard formats + /// Main method called from TimeSpan.TryFormat. + internal static bool TryFormat(TimeSpan value, Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider formatProvider) + { + StringBuilder sb = FormatToBuilder(value, format, formatProvider); + if (sb.Length <= destination.Length) + { + charsWritten = sb.Length; + sb.CopyTo(0, destination, sb.Length); + StringBuilderCache.Release(sb); + return true; + } + else + { + StringBuilderCache.Release(sb); + charsWritten = 0; + return false; + } + } + + private static StringBuilder FormatToBuilder(TimeSpan value, ReadOnlySpan format, IFormatProvider formatProvider) + { + if (format.Length == 0) + { + format = "c"; + } + + // Standard formats if (format.Length == 1) { char f = format[0]; - - if (f == 'c' || f == 't' || f == 'T') - return FormatStandard(value, true, format, Pattern.Minimum); - if (f == 'g' || f == 'G') + switch (f) { - Pattern pattern; - DateTimeFormatInfo dtfi = DateTimeFormatInfo.GetInstance(formatProvider); + case 'c': + case 't': + case 'T': + return FormatStandard( + value, + isInvariant: true, + format: format, + pattern: Pattern.Minimum); - if (value._ticks < 0) - format = dtfi.FullTimeSpanNegativePattern; - else - format = dtfi.FullTimeSpanPositivePattern; - if (f == 'g') - pattern = Pattern.Minimum; - else - pattern = Pattern.Full; + case 'g': + case 'G': + DateTimeFormatInfo dtfi = DateTimeFormatInfo.GetInstance(formatProvider); + return FormatStandard( + value, + isInvariant: false, + format: value.Ticks < 0 ? dtfi.FullTimeSpanNegativePattern : dtfi.FullTimeSpanPositivePattern, + pattern: f == 'g' ? Pattern.Minimum : Pattern.Full); - return FormatStandard(value, false, format, pattern); + default: + throw new FormatException(SR.Format_InvalidString); } - throw new FormatException(SR.Format_InvalidString); } - return FormatCustomized(value, format, DateTimeFormatInfo.GetInstance(formatProvider)); + // Custom formats + return FormatCustomized(value, format, DateTimeFormatInfo.GetInstance(formatProvider), result: null); } - // - // FormatStandard - // - // Actions: Format the TimeSpan instance using the specified format. - // - private static String FormatStandard(TimeSpan value, bool isInvariant, String format, Pattern pattern) + /// Format the TimeSpan instance using the specified format. + private static StringBuilder FormatStandard(TimeSpan value, bool isInvariant, ReadOnlySpan format, Pattern pattern) { StringBuilder sb = StringBuilderCache.Acquire(InternalGlobalizationHelper.StringBuilderDefaultCapacity); int day = (int)(value.Ticks / TimeSpan.TicksPerDay); @@ -89,19 +124,20 @@ namespace System.Globalization FormatLiterals literal; if (isInvariant) { - if (value.Ticks < 0) - literal = NegativeInvariantFormatLiterals; - else - literal = PositiveInvariantFormatLiterals; + literal = value.Ticks < 0 ? + NegativeInvariantFormatLiterals : + PositiveInvariantFormatLiterals; } else { literal = new FormatLiterals(); literal.Init(format, pattern == Pattern.Full); } + if (fraction != 0) - { // truncate the partial second to the specified length - fraction = (int)((long)fraction / (long)Math.Pow(10, DateTimeFormat.MaxSecondsFractionDigits - literal.ff)); + { + // truncate the partial second to the specified length + fraction = (int)(fraction / TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - literal.ff)); } // Pattern.Full: [-]dd.hh:mm:ss.fffffff @@ -109,15 +145,15 @@ namespace System.Globalization sb.Append(literal.Start); // [-] if (pattern == Pattern.Full || day != 0) - { // + { sb.Append(day); // [dd] sb.Append(literal.DayHourSep); // [.] } // - sb.Append(IntToString(hours, literal.hh)); // hh + AppendNonNegativeInt32(sb, hours, literal.hh); // hh sb.Append(literal.HourMinuteSep); // : - sb.Append(IntToString(minutes, literal.mm)); // mm + AppendNonNegativeInt32(sb, minutes, literal.mm); // mm sb.Append(literal.MinuteSecondSep); // : - sb.Append(IntToString(seconds, literal.ss)); // ss + AppendNonNegativeInt32(sb, seconds, literal.ss); // ss if (!isInvariant && pattern == Pattern.Minimum) { int effectiveDigits = literal.ff; @@ -141,24 +177,26 @@ namespace System.Globalization } else if (pattern == Pattern.Full || fraction != 0) { - sb.Append(literal.SecondFractionSep); // [.] - sb.Append(IntToString(fraction, literal.ff)); // [fffffff] - } // - sb.Append(literal.End); // + sb.Append(literal.SecondFractionSep); // [.] + AppendNonNegativeInt32(sb, fraction, literal.ff); // [fffffff] + } + sb.Append(literal.End); - return StringBuilderCache.GetStringAndRelease(sb); + return sb; } - - - - // - // FormatCustomized - // - // Actions: Format the TimeSpan instance using the specified format. - // - internal static String FormatCustomized(TimeSpan value, String format, DateTimeFormatInfo dtfi) + /// Format the TimeSpan instance using the specified format. + private static StringBuilder FormatCustomized(TimeSpan value, ReadOnlySpan format, DateTimeFormatInfo dtfi, StringBuilder result) { + Debug.Assert(dtfi != null); + + bool resultBuilderIsPooled = false; + if (result == null) + { + result = StringBuilderCache.Acquire(InternalGlobalizationHelper.StringBuilderDefaultCapacity); + resultBuilderIsPooled = true; + } + int day = (int)(value.Ticks / TimeSpan.TicksPerDay); long time = value.Ticks % TimeSpan.TicksPerDay; @@ -175,7 +213,6 @@ namespace System.Globalization long tmp = 0; int i = 0; int tokenLen; - StringBuilder result = StringBuilderCache.Acquire(InternalGlobalizationHelper.StringBuilderDefaultCapacity); while (i < format.Length) { @@ -186,19 +223,25 @@ namespace System.Globalization case 'h': tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); if (tokenLen > 2) - throw new FormatException(SR.Format_InvalidString); + { + goto default; // to release the builder and throw + } DateTimeFormat.FormatDigits(result, hours, tokenLen); break; case 'm': tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); if (tokenLen > 2) - throw new FormatException(SR.Format_InvalidString); + { + goto default; // to release the builder and throw + } DateTimeFormat.FormatDigits(result, minutes, tokenLen); break; case 's': tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); if (tokenLen > 2) - throw new FormatException(SR.Format_InvalidString); + { + goto default; // to release the builder and throw + } DateTimeFormat.FormatDigits(result, seconds, tokenLen); break; case 'f': @@ -207,10 +250,12 @@ namespace System.Globalization // tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits) - throw new FormatException(SR.Format_InvalidString); + { + goto default; // to release the builder and throw + } - tmp = (long)fraction; - tmp /= (long)Math.Pow(10, DateTimeFormat.MaxSecondsFractionDigits - tokenLen); + tmp = fraction; + tmp /= TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen); result.Append((tmp).ToString(DateTimeFormat.fixedNumberFormats[tokenLen - 1], CultureInfo.InvariantCulture)); break; case 'F': @@ -219,10 +264,12 @@ namespace System.Globalization // tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits) - throw new FormatException(SR.Format_InvalidString); + { + goto default; // to release the builder and throw + } - tmp = (long)fraction; - tmp /= (long)Math.Pow(10, DateTimeFormat.MaxSecondsFractionDigits - tokenLen); + tmp = fraction; + tmp /= TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen); int effectiveDigits = tokenLen; while (effectiveDigits > 0) { @@ -248,7 +295,10 @@ namespace System.Globalization // tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); if (tokenLen > 8) - throw new FormatException(SR.Format_InvalidString); + { + goto default; // to release the builder and throw + } + DateTimeFormat.FormatDigits(result, day, tokenLen, true); break; case '\'': @@ -264,7 +314,9 @@ namespace System.Globalization // Besides, we will not allow "%%" appear in the pattern. if (nextChar >= 0 && nextChar != (int)'%') { - result.Append(TimeSpanFormat.FormatCustomized(value, ((char)nextChar).ToString(), dtfi)); + char nextCharChar = (char)nextChar; + StringBuilder origStringBuilder = FormatCustomized(value, ReadOnlySpan.DangerousCreate(null, ref nextCharChar, 1), dtfi, result); + Debug.Assert(ReferenceEquals(origStringBuilder, result)); tokenLen = 2; } else @@ -273,7 +325,7 @@ namespace System.Globalization // This means that '%' is at the end of the format string or // "%%" appears in the format string. // - throw new FormatException(SR.Format_InvalidString); + goto default; // to release the builder and throw } break; case '\\': @@ -291,82 +343,51 @@ namespace System.Globalization // // This means that '\' is at the end of the formatting string. // - throw new FormatException(SR.Format_InvalidString); + goto default; // to release the builder and throw } break; default: + // Invalid format string + if (resultBuilderIsPooled) + { + StringBuilderCache.Release(result); + } throw new FormatException(SR.Format_InvalidString); } i += tokenLen; } - return StringBuilderCache.GetStringAndRelease(result); + return result; } internal struct FormatLiterals { - internal String Start - { - get - { - return _literals[0]; - } - } - internal String DayHourSep - { - get - { - return _literals[1]; - } - } - internal String HourMinuteSep - { - get - { - return _literals[2]; - } - } - internal String MinuteSecondSep - { - get - { - return _literals[3]; - } - } - internal String SecondFractionSep - { - get - { - return _literals[4]; - } - } - internal String End - { - get - { - return _literals[5]; - } - } - internal String AppCompatLiteral; + internal string AppCompatLiteral; internal int dd; internal int hh; internal int mm; internal int ss; internal int ff; - private String[] _literals; + private string[] _literals; + internal string Start => _literals[0]; + internal string DayHourSep => _literals[1]; + internal string HourMinuteSep => _literals[2]; + internal string MinuteSecondSep => _literals[3]; + internal string SecondFractionSep => _literals[4]; + internal string End => _literals[5]; /* factory method for static invariant FormatLiterals */ internal static FormatLiterals InitInvariant(bool isNegative) { FormatLiterals x = new FormatLiterals(); - x._literals = new String[6]; - x._literals[0] = isNegative ? "-" : String.Empty; + x._literals = new string[6]; + x._literals[0] = isNegative ? "-" : string.Empty; x._literals[1] = "."; x._literals[2] = ":"; x._literals[3] = ":"; x._literals[4] = "."; - x._literals[5] = String.Empty; + x._literals[5] = string.Empty; x.AppCompatLiteral = ":."; // MinuteSecondSep+SecondFractionSep; x.dd = 2; x.hh = 2; @@ -380,16 +401,14 @@ namespace System.Globalization // the constants guaranteed to include DHMSF ordered greatest to least significant. // Once the data becomes more complex than this we will need to write a proper tokenizer for // parsing and formatting - internal void Init(String format, bool useInvariantFieldLengths) + internal void Init(ReadOnlySpan format, bool useInvariantFieldLengths) { - _literals = new String[6]; + dd = hh = mm = ss = ff = 0; + _literals = new string[6]; for (int i = 0; i < _literals.Length; i++) - _literals[i] = String.Empty; - dd = 0; - hh = 0; - mm = 0; - ss = 0; - ff = 0; + { + _literals[i] = string.Empty; + } StringBuilder sb = StringBuilderCache.Acquire(InternalGlobalizationHelper.StringBuilderDefaultCapacity); bool inQuote = false; @@ -404,6 +423,7 @@ namespace System.Globalization case '\"': if (inQuote && (quote == format[i])) { + /* we were in a quote and found a matching exit quote, so we are outside a quote now */ if (field >= 0 && field <= 5) { _literals[field] = sb.ToString(); @@ -412,6 +432,7 @@ namespace System.Globalization } else { + Debug.Fail($"Unexpected field value: {field}"); return; // how did we get here? } } @@ -427,6 +448,7 @@ namespace System.Globalization } break; case '%': + Debug.Fail("Unexpected special token '%', Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); goto default; case '\\': if (!inQuote) @@ -438,6 +460,7 @@ namespace System.Globalization case 'd': if (!inQuote) { + Debug.Assert((field == 0 && sb.Length == 0) || field == 1, "field == 0 || field == 1, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); field = 1; // DayHourSep dd++; } @@ -445,6 +468,7 @@ namespace System.Globalization case 'h': if (!inQuote) { + Debug.Assert((field == 1 && sb.Length == 0) || field == 2, "field == 1 || field == 2, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); field = 2; // HourMinuteSep hh++; } @@ -452,6 +476,7 @@ namespace System.Globalization case 'm': if (!inQuote) { + Debug.Assert((field == 2 && sb.Length == 0) || field == 3, "field == 2 || field == 3, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); field = 3; // MinuteSecondSep mm++; } @@ -459,6 +484,7 @@ namespace System.Globalization case 's': if (!inQuote) { + Debug.Assert((field == 3 && sb.Length == 0) || field == 4, "field == 3 || field == 4, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); field = 4; // SecondFractionSep ss++; } @@ -467,6 +493,7 @@ namespace System.Globalization case 'F': if (!inQuote) { + Debug.Assert((field == 4 && sb.Length == 0) || field == 5, "field == 4 || field == 5, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); field = 5; // End ff++; } @@ -476,8 +503,16 @@ namespace System.Globalization break; } } + + Debug.Assert(field == 5); AppCompatLiteral = MinuteSecondSep + SecondFractionSep; + Debug.Assert(0 < dd && dd < 3, "0 < dd && dd < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + Debug.Assert(0 < hh && hh < 3, "0 < hh && hh < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + Debug.Assert(0 < mm && mm < 3, "0 < mm && mm < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + Debug.Assert(0 < ss && ss < 3, "0 < ss && ss < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + Debug.Assert(0 < ff && ff < 8, "0 < ff && ff < 8, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + if (useInvariantFieldLengths) { dd = 2; @@ -496,6 +531,6 @@ namespace System.Globalization } StringBuilderCache.Release(sb); } - } //end of struct FormatLiterals + } } } diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/TimeSpanParse.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/TimeSpanParse.cs new file mode 100644 index 0000000000..ae77957cec --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/TimeSpanParse.cs @@ -0,0 +1,1666 @@ +// 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. + +//////////////////////////////////////////////////////////////////////////// +// +// Purpose: Used by TimeSpan to parse a time interval string. +// +// Standard Format: +// -=-=-=-=-=-=-=- +// "c": Constant format. [-][d'.']hh':'mm':'ss['.'fffffff] +// Not culture sensitive. Default format (and null/empty format string) map to this format. +// +// "g": General format, short: [-][d':']h':'mm':'ss'.'FFFFFFF +// Only print what's needed. Localized (if you want Invariant, pass in Invariant). +// The fractional seconds separator is localized, equal to the culture's DecimalSeparator. +// +// "G": General format, long: [-]d':'hh':'mm':'ss'.'fffffff +// Always print days and 7 fractional digits. Localized (if you want Invariant, pass in Invariant). +// The fractional seconds separator is localized, equal to the culture's DecimalSeparator. +// +// * "TryParseTimeSpan" is the main method for Parse/TryParse +// +// - TimeSpanTokenizer.GetNextToken() is used to split the input string into number and literal tokens. +// - TimeSpanRawInfo.ProcessToken() adds the next token into the parsing intermediary state structure +// - ProcessTerminalState() uses the fully initialized TimeSpanRawInfo to find a legal parse match. +// The terminal states are attempted as follows: +// foreach (+InvariantPattern, -InvariantPattern, +LocalizedPattern, -LocalizedPattern) try +// 1 number => d +// 2 numbers => h:m +// 3 numbers => h:m:s | d.h:m | h:m:.f +// 4 numbers => h:m:s.f | d.h:m:s | d.h:m:.f +// 5 numbers => d.h:m:s.f +// +// Custom Format: +// -=-=-=-=-=-=-= +// +// * "TryParseExactTimeSpan" is the main method for ParseExact/TryParseExact methods +// * "TryParseExactMultipleTimeSpan" is the main method for ParseExact/TryparseExact +// methods that take a string[] of formats +// +// - For single-letter formats "TryParseTimeSpan" is called (see above) +// - For multi-letter formats "TryParseByFormat" is called +// - TryParseByFormat uses helper methods (ParseExactLiteral, ParseExactDigits, etc) +// which drive the underlying TimeSpanTokenizer. However, unlike standard formatting which +// operates on whole-tokens, ParseExact operates at the character-level. As such, +// TimeSpanTokenizer.NextChar and TimeSpanTokenizer.BackOne() are called directly. +// +//////////////////////////////////////////////////////////////////////////// + +using System.Diagnostics; +using System.Text; + +namespace System.Globalization +{ + internal static class TimeSpanParse + { + private const int MaxFractionDigits = 7; + private const int MaxDays = 10675199; + private const int MaxHours = 23; + private const int MaxMinutes = 59; + private const int MaxSeconds = 59; + private const int MaxFraction = 9999999; + + private enum ParseFailureKind : byte + { + None = 0, + ArgumentNull = 1, + Format = 2, + FormatWithParameter = 3, + Overflow = 4, + } + + [Flags] + private enum TimeSpanStandardStyles : byte + { + // Standard Format Styles + None = 0x00000000, + Invariant = 0x00000001, //Allow Invariant Culture + Localized = 0x00000002, //Allow Localized Culture + RequireFull = 0x00000004, //Require the input to be in DHMSF format + Any = Invariant | Localized, + } + + // TimeSpan Token Types + private enum TTT : byte + { + None = 0, // None of the TimeSpanToken fields are set + End = 1, // '\0' + Num = 2, // Number + Sep = 3, // literal + NumOverflow = 4, // Number that overflowed + } + + private ref struct TimeSpanToken + { + internal TTT _ttt; + internal int _num; // Store the number that we are parsing (if any) + internal int _zeroes; // Store the number of leading zeroes (if any) + internal ReadOnlySpan _sep; // Store the literal that we are parsing (if any) + + public TimeSpanToken(TTT type) : this(type, 0, 0, default(ReadOnlySpan)) { } + + public TimeSpanToken(int number) : this(TTT.Num, number, 0, default(ReadOnlySpan)) { } + + public TimeSpanToken(int number, int leadingZeroes) : this(TTT.Num, number, leadingZeroes, default(ReadOnlySpan)) { } + + public TimeSpanToken(TTT type, int number, int leadingZeroes, ReadOnlySpan separator) + { + _ttt = type; + _num = number; + _zeroes = leadingZeroes; + _sep = separator; + } + + public bool IsInvalidFraction() + { + Debug.Assert(_ttt == TTT.Num); + Debug.Assert(_num > -1); + + if (_num > MaxFraction || _zeroes > MaxFractionDigits) + return true; + + if (_num == 0 || _zeroes == 0) + return false; + + // num > 0 && zeroes > 0 && num <= maxValue && zeroes <= maxPrecision + return _num >= MaxFraction / Pow10(_zeroes - 1); + } + } + + private ref struct TimeSpanTokenizer + { + private ReadOnlySpan _value; + private int _pos; + + internal TimeSpanTokenizer(ReadOnlySpan input) : this(input, 0) { } + + internal TimeSpanTokenizer(ReadOnlySpan input, int startPosition) + { + _value = input; + _pos = startPosition; + } + + /// Returns the next token in the input string + /// Used by the parsing routines that operate on standard-formats. + internal TimeSpanToken GetNextToken() + { + // Get the position of the next character to be processed. If there is no + // next character, we're at the end. + int pos = _pos; + Debug.Assert(pos > -1); + if (pos >= _value.Length) + { + return new TimeSpanToken(TTT.End); + } + + // Now retrieve that character. If it's a digit, we're processing a number. + int num = _value[pos] - '0'; + if ((uint)num <= 9) + { + int zeroes = 0; + if (num == 0) + { + // Read all leading zeroes. + zeroes = 1; + while (true) + { + int digit; + if (++_pos >= _value.Length || (uint)(digit = _value[_pos] - '0') > 9) + { + return new TimeSpanToken(TTT.Num, 0, zeroes, default(ReadOnlySpan)); + } + + if (digit == 0) + { + zeroes++; + continue; + } + + num = digit; + break; + } + } + + // Continue to read as long as we're reading digits. + while (++_pos < _value.Length) + { + int digit = _value[_pos] - '0'; + if ((uint)digit > 9) + { + break; + } + + num = num * 10 + digit; + if ((num & 0xF0000000) != 0) + { + return new TimeSpanToken(TTT.NumOverflow); + } + } + + return new TimeSpanToken(TTT.Num, num, zeroes, default(ReadOnlySpan)); + } + + // Otherwise, we're processing a separator, and we've already processed the first + // character of it. Continue processing characters as long as they're not digits. + int length = 1; + while (true) + { + if (++_pos >= _value.Length || (uint)(_value[_pos] - '0') <= 9) + { + break; + } + length++; + } + + // Return the separator. + return new TimeSpanToken(TTT.Sep, 0, 0, _value.Slice(pos, length)); + } + + internal bool EOL => _pos >= (_value.Length - 1); + + internal void BackOne() + { + if (_pos > 0) --_pos; + } + + internal char NextChar + { + get + { + int pos = ++_pos; + return (uint)pos < (uint)_value.Length ? + _value[pos] : + (char)0; + } + } + } + + /// Stores intermediary parsing state for the standard formats. + private ref struct TimeSpanRawInfo + { + internal TimeSpanFormat.FormatLiterals PositiveInvariant => TimeSpanFormat.PositiveInvariantFormatLiterals; + internal TimeSpanFormat.FormatLiterals NegativeInvariant => TimeSpanFormat.NegativeInvariantFormatLiterals; + + internal TimeSpanFormat.FormatLiterals PositiveLocalized + { + get + { + if (!_posLocInit) + { + _posLoc = new TimeSpanFormat.FormatLiterals(); + _posLoc.Init(_fullPosPattern, false); + _posLocInit = true; + } + return _posLoc; + } + } + + internal TimeSpanFormat.FormatLiterals NegativeLocalized + { + get + { + if (!_negLocInit) + { + _negLoc = new TimeSpanFormat.FormatLiterals(); + _negLoc.Init(_fullNegPattern, false); + _negLocInit = true; + } + return _negLoc; + } + } + + internal bool FullAppCompatMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 5 + && _numCount == 4 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.DayHourSep) + && StringSpanHelpers.Equals(_literals2, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals3, pattern.AppCompatLiteral) + && StringSpanHelpers.Equals(_literals4, pattern.End); + + internal bool PartialAppCompatMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 4 + && _numCount == 3 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals2, pattern.AppCompatLiteral) + && StringSpanHelpers.Equals(_literals3, pattern.End); + + /// DHMSF (all values matched) + internal bool FullMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == MaxLiteralTokens + && _numCount == MaxNumericTokens + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.DayHourSep) + && StringSpanHelpers.Equals(_literals2, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals3, pattern.MinuteSecondSep) + && StringSpanHelpers.Equals(_literals4, pattern.SecondFractionSep) + && StringSpanHelpers.Equals(_literals5, pattern.End); + + /// D (no hours, minutes, seconds, or fractions) + internal bool FullDMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 2 + && _numCount == 1 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.End); + + /// HM (no days, seconds, or fractions) + internal bool FullHMMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 3 + && _numCount == 2 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals2, pattern.End); + + /// DHM (no seconds or fraction) + internal bool FullDHMMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 4 + && _numCount == 3 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.DayHourSep) + && StringSpanHelpers.Equals(_literals2, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals3, pattern.End); + + /// HMS (no days or fraction) + internal bool FullHMSMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 4 + && _numCount == 3 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals2, pattern.MinuteSecondSep) + && StringSpanHelpers.Equals(_literals3, pattern.End); + + /// DHMS (no fraction) + internal bool FullDHMSMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 5 + && _numCount == 4 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.DayHourSep) + && StringSpanHelpers.Equals(_literals2, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals3, pattern.MinuteSecondSep) + && StringSpanHelpers.Equals(_literals4, pattern.End); + + /// HMSF (no days) + internal bool FullHMSFMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 5 + && _numCount == 4 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals2, pattern.MinuteSecondSep) + && StringSpanHelpers.Equals(_literals3, pattern.SecondFractionSep) + && StringSpanHelpers.Equals(_literals4, pattern.End); + + internal TTT _lastSeenTTT; + internal int _tokenCount; + internal int _sepCount; + internal int _numCount; + + private TimeSpanFormat.FormatLiterals _posLoc; + private TimeSpanFormat.FormatLiterals _negLoc; + private bool _posLocInit; + private bool _negLocInit; + private string _fullPosPattern; + private string _fullNegPattern; + + private const int MaxTokens = 11; + private const int MaxLiteralTokens = 6; + private const int MaxNumericTokens = 5; + + internal TimeSpanToken _numbers0, _numbers1, _numbers2, _numbers3, _numbers4; // MaxNumbericTokens = 5 + internal ReadOnlySpan _literals0, _literals1, _literals2, _literals3, _literals4, _literals5; // MaxLiteralTokens=6 + + internal void Init(DateTimeFormatInfo dtfi) + { + Debug.Assert(dtfi != null); + + _lastSeenTTT = TTT.None; + _tokenCount = 0; + _sepCount = 0; + _numCount = 0; + + _fullPosPattern = dtfi.FullTimeSpanPositivePattern; + _fullNegPattern = dtfi.FullTimeSpanNegativePattern; + _posLocInit = false; + _negLocInit = false; + } + + internal bool ProcessToken(ref TimeSpanToken tok, ref TimeSpanResult result) + { + switch (tok._ttt) + { + case TTT.Num: + if ((_tokenCount == 0 && !AddSep(default(ReadOnlySpan), ref result)) || !AddNum(tok, ref result)) + { + return false; + } + break; + + case TTT.Sep: + if (!AddSep(tok._sep, ref result)) + { + return false; + } + break; + + case TTT.NumOverflow: + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + + default: + // Some unknown token or a repeat token type in the input + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + _lastSeenTTT = tok._ttt; + Debug.Assert(_tokenCount == (_sepCount + _numCount), "tokenCount == (SepCount + NumCount)"); + return true; + } + + private bool AddSep(ReadOnlySpan sep, ref TimeSpanResult result) + { + if (_sepCount >= MaxLiteralTokens || _tokenCount >= MaxTokens) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + switch (_sepCount++) + { + case 0: _literals0 = sep; break; + case 1: _literals1 = sep; break; + case 2: _literals2 = sep; break; + case 3: _literals3 = sep; break; + case 4: _literals4 = sep; break; + default: _literals5 = sep; break; + } + + _tokenCount++; + return true; + } + private bool AddNum(TimeSpanToken num, ref TimeSpanResult result) + { + if (_numCount >= MaxNumericTokens || _tokenCount >= MaxTokens) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + switch (_numCount++) + { + case 0: _numbers0 = num; break; + case 1: _numbers1 = num; break; + case 2: _numbers2 = num; break; + case 3: _numbers3 = num; break; + default: _numbers4 = num; break; + } + + _tokenCount++; + return true; + } + } + + /// Store the result of the parsing. + private struct TimeSpanResult + { + internal TimeSpan parsedTimeSpan; + private readonly bool _throwOnFailure; + + internal TimeSpanResult(bool throwOnFailure) + { + parsedTimeSpan = default(TimeSpan); + _throwOnFailure = throwOnFailure; + } + + internal bool SetFailure(ParseFailureKind kind, string resourceKey, object messageArgument = null, string argumentName = null) + { + if (!_throwOnFailure) + { + return false; + } + + string message = SR.GetResourceString(resourceKey); + switch (kind) + { + case ParseFailureKind.ArgumentNull: + Debug.Assert(argumentName != null); + throw new ArgumentNullException(argumentName, message); + + case ParseFailureKind.FormatWithParameter: + throw new FormatException(SR.Format(message, messageArgument)); + + case ParseFailureKind.Overflow: + throw new OverflowException(message); + + default: + Debug.Assert(kind == ParseFailureKind.Format, $"Unexpected failure {kind}"); + throw new FormatException(message); + } + } + } + + internal static long Pow10(int pow) + { + switch (pow) + { + case 0: return 1; + case 1: return 10; + case 2: return 100; + case 3: return 1000; + case 4: return 10000; + case 5: return 100000; + case 6: return 1000000; + case 7: return 10000000; + default: return (long)Math.Pow(10, pow); + } + } + + private static bool TryTimeToTicks(bool positive, TimeSpanToken days, TimeSpanToken hours, TimeSpanToken minutes, TimeSpanToken seconds, TimeSpanToken fraction, out long result) + { + if (days._num > MaxDays || + hours._num > MaxHours || + minutes._num > MaxMinutes || + seconds._num > MaxSeconds || + fraction.IsInvalidFraction()) + { + result = 0; + return false; + } + + long ticks = ((long)days._num * 3600 * 24 + (long)hours._num * 3600 + (long)minutes._num * 60 + seconds._num) * 1000; + if (ticks > InternalGlobalizationHelper.MaxMilliSeconds || ticks < InternalGlobalizationHelper.MinMilliSeconds) + { + result = 0; + return false; + } + + // Normalize the fraction component + // + // string representation => (zeroes,num) => resultant fraction ticks + // --------------------- ------------ ------------------------ + // ".9999999" => (0,9999999) => 9,999,999 ticks (same as constant maxFraction) + // ".1" => (0,1) => 1,000,000 ticks + // ".01" => (1,1) => 100,000 ticks + // ".001" => (2,1) => 10,000 ticks + long f = fraction._num; + if (f != 0) + { + long lowerLimit = InternalGlobalizationHelper.TicksPerTenthSecond; + if (fraction._zeroes > 0) + { + long divisor = Pow10(fraction._zeroes); + lowerLimit = lowerLimit / divisor; + } + + while (f < lowerLimit) + { + f *= 10; + } + } + + result = ticks * TimeSpan.TicksPerMillisecond + f; + if (positive && result < 0) + { + result = 0; + return false; + } + + return true; + } + + internal static TimeSpan Parse(ReadOnlySpan input, IFormatProvider formatProvider) + { + var parseResult = new TimeSpanResult(throwOnFailure: true); + bool success = TryParseTimeSpan(input, TimeSpanStandardStyles.Any, formatProvider, ref parseResult); + Debug.Assert(success, "Should have thrown on failure"); + return parseResult.parsedTimeSpan; + } + + internal static bool TryParse(ReadOnlySpan input, IFormatProvider formatProvider, out TimeSpan result) + { + var parseResult = new TimeSpanResult(throwOnFailure: false); + + if (TryParseTimeSpan(input, TimeSpanStandardStyles.Any, formatProvider, ref parseResult)) + { + result = parseResult.parsedTimeSpan; + return true; + } + + result = default(TimeSpan); + return false; + } + + internal static TimeSpan ParseExact(ReadOnlySpan input, ReadOnlySpan format, IFormatProvider formatProvider, TimeSpanStyles styles) + { + var parseResult = new TimeSpanResult(throwOnFailure: true); + bool success = TryParseExactTimeSpan(input, format, formatProvider, styles, ref parseResult); + Debug.Assert(success, "Should have thrown on failure"); + return parseResult.parsedTimeSpan; + } + + internal static bool TryParseExact(ReadOnlySpan input, ReadOnlySpan format, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result) + { + var parseResult = new TimeSpanResult(throwOnFailure: false); + + if (TryParseExactTimeSpan(input, format, formatProvider, styles, ref parseResult)) + { + result = parseResult.parsedTimeSpan; + return true; + } + + result = default(TimeSpan); + return false; + } + + internal static TimeSpan ParseExactMultiple(ReadOnlySpan input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles) + { + var parseResult = new TimeSpanResult(throwOnFailure: true); + bool success = TryParseExactMultipleTimeSpan(input, formats, formatProvider, styles, ref parseResult); + Debug.Assert(success, "Should have thrown on failure"); + return parseResult.parsedTimeSpan; + } + + internal static bool TryParseExactMultiple(ReadOnlySpan input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result) + { + var parseResult = new TimeSpanResult(throwOnFailure: false); + + if (TryParseExactMultipleTimeSpan(input, formats, formatProvider, styles, ref parseResult)) + { + result = parseResult.parsedTimeSpan; + return true; + } + + result = default(TimeSpan); + return false; + } + + /// Common private Parse method called by both Parse and TryParse. + private static bool TryParseTimeSpan(ReadOnlySpan input, TimeSpanStandardStyles style, IFormatProvider formatProvider, ref TimeSpanResult result) + { + input = input.Trim(); + if (input.IsEmpty) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + var tokenizer = new TimeSpanTokenizer(input); + + var raw = new TimeSpanRawInfo(); + raw.Init(DateTimeFormatInfo.GetInstance(formatProvider)); + + TimeSpanToken tok = tokenizer.GetNextToken(); + + // The following loop will break out when we reach the end of the str or + // when we can determine that the input is invalid. + while (tok._ttt != TTT.End) + { + if (!raw.ProcessToken(ref tok, ref result)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + tok = tokenizer.GetNextToken(); + } + Debug.Assert(tokenizer.EOL); + + if (!ProcessTerminalState(ref raw, style, ref result)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + return true; + } + + /// + /// Validate the terminal state of a standard format parse. + /// Sets result.parsedTimeSpan on success. + /// Calculates the resultant TimeSpan from the TimeSpanRawInfo. + /// + /// + /// try => +InvariantPattern, -InvariantPattern, +LocalizedPattern, -LocalizedPattern + /// 1) Verify Start matches + /// 2) Verify End matches + /// 3) 1 number => d + /// 2 numbers => h:m + /// 3 numbers => h:m:s | d.h:m | h:m:.f + /// 4 numbers => h:m:s.f | d.h:m:s | d.h:m:.f + /// 5 numbers => d.h:m:s.f + /// + private static bool ProcessTerminalState(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) + { + if (raw._lastSeenTTT == TTT.Num) + { + TimeSpanToken tok = new TimeSpanToken(); + tok._ttt = TTT.Sep; + if (!raw.ProcessToken(ref tok, ref result)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + } + + switch (raw._numCount) + { + case 1: return ProcessTerminal_D(ref raw, style, ref result); + case 2: return ProcessTerminal_HM(ref raw, style, ref result); + case 3: return ProcessTerminal_HM_S_D(ref raw, style, ref result); + case 4: return ProcessTerminal_HMS_F_D(ref raw, style, ref result); + case 5: return ProcessTerminal_DHMSF(ref raw, style, ref result); + default: return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + } + + /// Validate the 5-number "Days.Hours:Minutes:Seconds.Fraction" terminal case. + private static bool ProcessTerminal_DHMSF(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) + { + if (raw._sepCount != 6) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + Debug.Assert(raw._numCount == 5); + + bool inv = (style & TimeSpanStandardStyles.Invariant) != 0; + bool loc = (style & TimeSpanStandardStyles.Localized) != 0; + bool positive = false; + bool match = false; + + if (inv) + { + if (raw.FullMatch(raw.PositiveInvariant)) + { + match = true; + positive = true; + } + if (!match && raw.FullMatch(raw.NegativeInvariant)) + { + match = true; + positive = false; + } + } + + if (loc) + { + if (!match && raw.FullMatch(raw.PositiveLocalized)) + { + match = true; + positive = true; + } + if (!match && raw.FullMatch(raw.NegativeLocalized)) + { + match = true; + positive = false; + } + } + + if (match) + { + long ticks; + + if (!TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, raw._numbers4, out ticks)) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + + if (!positive) + { + ticks = -ticks; + if (ticks > 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + + result.parsedTimeSpan._ticks = ticks; + return true; + } + + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + + /// + /// Validate the ambiguous 4-number "Hours:Minutes:Seconds.Fraction", "Days.Hours:Minutes:Seconds", + /// or "Days.Hours:Minutes:.Fraction" terminal case. + /// + private static bool ProcessTerminal_HMS_F_D(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) + { + if (raw._sepCount != 5 || (style & TimeSpanStandardStyles.RequireFull) != 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + Debug.Assert(raw._numCount == 4); + + bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0); + bool loc = ((style & TimeSpanStandardStyles.Localized) != 0); + + long ticks = 0; + bool positive = false, match = false, overflow = false; + var zero = new TimeSpanToken(0); + + if (inv) + { + if (raw.FullHMSFMatch(raw.PositiveInvariant)) + { + positive = true; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMSMatch(raw.PositiveInvariant)) + { + positive = true; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullAppCompatMatch(raw.PositiveInvariant)) + { + positive = true; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, raw._numbers3, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullHMSFMatch(raw.NegativeInvariant)) + { + positive = false; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMSMatch(raw.NegativeInvariant)) + { + positive = false; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullAppCompatMatch(raw.NegativeInvariant)) + { + positive = false; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, raw._numbers3, out ticks); + overflow = overflow || !match; + } + } + + if (loc) + { + if (!match && raw.FullHMSFMatch(raw.PositiveLocalized)) + { + positive = true; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMSMatch(raw.PositiveLocalized)) + { + positive = true; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullAppCompatMatch(raw.PositiveLocalized)) + { + positive = true; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, raw._numbers3, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullHMSFMatch(raw.NegativeLocalized)) + { + positive = false; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMSMatch(raw.NegativeLocalized)) + { + positive = false; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullAppCompatMatch(raw.NegativeLocalized)) + { + positive = false; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, raw._numbers3, out ticks); + overflow = overflow || !match; + } + } + + if (match) + { + if (!positive) + { + ticks = -ticks; + if (ticks > 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + + result.parsedTimeSpan._ticks = ticks; + return true; + } + + return overflow ? + result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)) : // we found at least one literal pattern match but the numbers just didn't fit + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); // we couldn't find a thing + } + + /// Validate the ambiguous 3-number "Hours:Minutes:Seconds", "Days.Hours:Minutes", or "Hours:Minutes:.Fraction" terminal case. + private static bool ProcessTerminal_HM_S_D(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) + { + if (raw._sepCount != 4 || (style & TimeSpanStandardStyles.RequireFull) != 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + Debug.Assert(raw._numCount == 3); + + bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0); + bool loc = ((style & TimeSpanStandardStyles.Localized) != 0); + + bool positive = false, match = false, overflow = false; + var zero = new TimeSpanToken(0); + long ticks = 0; + + if (inv) + { + if (raw.FullHMSMatch(raw.PositiveInvariant)) + { + positive = true; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMMatch(raw.PositiveInvariant)) + { + positive = true; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.PartialAppCompatMatch(raw.PositiveInvariant)) + { + positive = true; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, zero, raw._numbers2, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullHMSMatch(raw.NegativeInvariant)) + { + positive = false; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMMatch(raw.NegativeInvariant)) + { + positive = false; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.PartialAppCompatMatch(raw.NegativeInvariant)) + { + positive = false; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, zero, raw._numbers2, out ticks); + overflow = overflow || !match; + } + } + + if (loc) + { + if (!match && raw.FullHMSMatch(raw.PositiveLocalized)) + { + positive = true; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMMatch(raw.PositiveLocalized)) + { + positive = true; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.PartialAppCompatMatch(raw.PositiveLocalized)) + { + positive = true; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, zero, raw._numbers2, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullHMSMatch(raw.NegativeLocalized)) + { + positive = false; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMMatch(raw.NegativeLocalized)) + { + positive = false; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.PartialAppCompatMatch(raw.NegativeLocalized)) + { + positive = false; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, zero, raw._numbers2, out ticks); + overflow = overflow || !match; + } + } + + if (match) + { + if (!positive) + { + ticks = -ticks; + if (ticks > 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + + result.parsedTimeSpan._ticks = ticks; + return true; + } + + return overflow ? + result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)) : // we found at least one literal pattern match but the numbers just didn't fit + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); // we couldn't find a thing + } + + /// Validate the 2-number "Hours:Minutes" terminal case. + private static bool ProcessTerminal_HM(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) + { + if (raw._sepCount != 3 || (style & TimeSpanStandardStyles.RequireFull) != 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + Debug.Assert(raw._numCount == 2); + + bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0); + bool loc = ((style & TimeSpanStandardStyles.Localized) != 0); + + bool positive = false, match = false; + + if (inv) + { + if (raw.FullHMMatch(raw.PositiveInvariant)) + { + match = true; + positive = true; + } + + if (!match && raw.FullHMMatch(raw.NegativeInvariant)) + { + match = true; + positive = false; + } + } + + if (loc) + { + if (!match && raw.FullHMMatch(raw.PositiveLocalized)) + { + match = true; + positive = true; + } + + if (!match && raw.FullHMMatch(raw.NegativeLocalized)) + { + match = true; + positive = false; + } + } + + if (match) + { + long ticks = 0; + var zero = new TimeSpanToken(0); + + if (!TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, zero, zero, out ticks)) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + + if (!positive) + { + ticks = -ticks; + if (ticks > 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + + result.parsedTimeSpan._ticks = ticks; + return true; + } + + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + /// Validate the 1-number "Days" terminal case. + private static bool ProcessTerminal_D(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) + { + if (raw._sepCount != 2 || (style & TimeSpanStandardStyles.RequireFull) != 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + Debug.Assert(raw._numCount == 1); + + bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0); + bool loc = ((style & TimeSpanStandardStyles.Localized) != 0); + + bool positive = false, match = false; + + if (inv) + { + if (raw.FullDMatch(raw.PositiveInvariant)) + { + match = true; + positive = true; + } + + if (!match && raw.FullDMatch(raw.NegativeInvariant)) + { + match = true; + positive = false; + } + } + + if (loc) + { + if (!match && raw.FullDMatch(raw.PositiveLocalized)) + { + match = true; + positive = true; + } + + if (!match && raw.FullDMatch(raw.NegativeLocalized)) + { + match = true; + positive = false; + } + } + + if (match) + { + long ticks = 0; + var zero = new TimeSpanToken(0); + + if (!TryTimeToTicks(positive, raw._numbers0, zero, zero, zero, zero, out ticks)) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + + if (!positive) + { + ticks = -ticks; + if (ticks > 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + + result.parsedTimeSpan._ticks = ticks; + return true; + } + + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + /// Common private ParseExact method called by both ParseExact and TryParseExact. + private static bool TryParseExactTimeSpan(ReadOnlySpan input, ReadOnlySpan format, IFormatProvider formatProvider, TimeSpanStyles styles, ref TimeSpanResult result) + { + if (format.Length == 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier)); + } + + if (format.Length == 1) + { + switch (format[0]) + { + case 'c': + case 't': + case 'T': + return TryParseTimeSpanConstant(input, ref result); // fast path for legacy style TimeSpan formats. + + case 'g': + return TryParseTimeSpan(input, TimeSpanStandardStyles.Localized, formatProvider, ref result); + + case 'G': + return TryParseTimeSpan(input, TimeSpanStandardStyles.Localized | TimeSpanStandardStyles.RequireFull, formatProvider, ref result); + + default: + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier)); + } + } + + return TryParseByFormat(input, format, styles, ref result); + } + + /// Parse the TimeSpan instance using the specified format. Used by TryParseExactTimeSpan. + private static bool TryParseByFormat(ReadOnlySpan input, ReadOnlySpan format, TimeSpanStyles styles, ref TimeSpanResult result) + { + bool seenDD = false; // already processed days? + bool seenHH = false; // already processed hours? + bool seenMM = false; // already processed minutes? + bool seenSS = false; // already processed seconds? + bool seenFF = false; // already processed fraction? + + int dd = 0; // parsed days + int hh = 0; // parsed hours + int mm = 0; // parsed minutes + int ss = 0; // parsed seconds + int leadingZeroes = 0; // number of leading zeroes in the parsed fraction + int ff = 0; // parsed fraction + int i = 0; // format string position + int tokenLen = 0; // length of current format token, used to update index 'i' + + var tokenizer = new TimeSpanTokenizer(input, -1); + + while (i < format.Length) + { + char ch = format[i]; + int nextFormatChar; + switch (ch) + { + case 'h': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > 2 || seenHH || !ParseExactDigits(ref tokenizer, tokenLen, out hh)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + seenHH = true; + break; + + case 'm': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > 2 || seenMM || !ParseExactDigits(ref tokenizer, tokenLen, out mm)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + seenMM = true; + break; + + case 's': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > 2 || seenSS || !ParseExactDigits(ref tokenizer, tokenLen, out ss)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + seenSS = true; + break; + + case 'f': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits || seenFF || !ParseExactDigits(ref tokenizer, tokenLen, tokenLen, out leadingZeroes, out ff)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + seenFF = true; + break; + + case 'F': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits || seenFF) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + ParseExactDigits(ref tokenizer, tokenLen, tokenLen, out leadingZeroes, out ff); + seenFF = true; + break; + + case 'd': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + int tmp = 0; + if (tokenLen > 8 || seenDD || !ParseExactDigits(ref tokenizer, (tokenLen < 2) ? 1 : tokenLen, (tokenLen < 2) ? 8 : tokenLen, out tmp, out dd)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + seenDD = true; + break; + + case '\'': + case '\"': + StringBuilder enquotedString = StringBuilderCache.Acquire(); + if (!DateTimeParse.TryParseQuoteString(format, i, enquotedString, out tokenLen)) + { + StringBuilderCache.Release(enquotedString); + return result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadQuote), ch); + } + if (!ParseExactLiteral(ref tokenizer, enquotedString)) + { + StringBuilderCache.Release(enquotedString); + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + StringBuilderCache.Release(enquotedString); + break; + + case '%': + // Optional format character. + // For example, format string "%d" will print day + // Most of the cases, "%" can be ignored. + nextFormatChar = DateTimeFormat.ParseNextChar(format, i); + + // nextFormatChar will be -1 if we already reach the end of the format string. + // Besides, we will not allow "%%" appear in the pattern. + if (nextFormatChar >= 0 && nextFormatChar != '%') + { + tokenLen = 1; // skip the '%' and process the format character + break; + } + else + { + // This means that '%' is at the end of the format string or + // "%%" appears in the format string. + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + + case '\\': + // Escaped character. Can be used to insert character into the format string. + // For example, "\d" will insert the character 'd' into the string. + // + nextFormatChar = DateTimeFormat.ParseNextChar(format, i); + if (nextFormatChar >= 0 && tokenizer.NextChar == (char)nextFormatChar) + { + tokenLen = 2; + } + else + { + // This means that '\' is at the end of the format string or the literal match failed. + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + break; + + default: + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + + i += tokenLen; + } + + + if (!tokenizer.EOL) + { + // the custom format didn't consume the entire input + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + bool positive = (styles & TimeSpanStyles.AssumeNegative) == 0; + if (TryTimeToTicks(positive, new TimeSpanToken(dd), + new TimeSpanToken(hh), + new TimeSpanToken(mm), + new TimeSpanToken(ss), + new TimeSpanToken(ff, leadingZeroes), + out long ticks)) + { + if (!positive) + { + ticks = -ticks; + } + + result.parsedTimeSpan._ticks = ticks; + return true; + } + else + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + + private static bool ParseExactDigits(ref TimeSpanTokenizer tokenizer, int minDigitLength, out int result) + { + result = 0; + int zeroes = 0; + int maxDigitLength = (minDigitLength == 1) ? 2 : minDigitLength; + return ParseExactDigits(ref tokenizer, minDigitLength, maxDigitLength, out zeroes, out result); + } + + private static bool ParseExactDigits(ref TimeSpanTokenizer tokenizer, int minDigitLength, int maxDigitLength, out int zeroes, out int result) + { + int tmpResult = 0, tmpZeroes = 0; + + int tokenLength = 0; + while (tokenLength < maxDigitLength) + { + char ch = tokenizer.NextChar; + if (ch < '0' || ch > '9') + { + tokenizer.BackOne(); + break; + } + + tmpResult = tmpResult * 10 + (ch - '0'); + if (tmpResult == 0) tmpZeroes++; + tokenLength++; + } + + zeroes = tmpZeroes; + result = tmpResult; + return tokenLength >= minDigitLength; + } + + private static bool ParseExactLiteral(ref TimeSpanTokenizer tokenizer, StringBuilder enquotedString) + { + for (int i = 0; i < enquotedString.Length; i++) + { + if (enquotedString[i] != tokenizer.NextChar) + { + return false; + } + } + + return true; + } + + /// + /// Parses the "c" (constant) format. This code is 100% identical to the non-globalized v1.0-v3.5 TimeSpan.Parse() routine + /// and exists for performance/appcompat with legacy callers who cannot move onto the globalized Parse overloads. + /// + private static bool TryParseTimeSpanConstant(ReadOnlySpan input, ref TimeSpanResult result) => + new StringParser().TryParse(input, ref result); + + private ref struct StringParser + { + private ReadOnlySpan _str; + private char _ch; + private int _pos; + private int _len; + + internal void NextChar() + { + if (_pos < _len) + { + _pos++; + } + + _ch = _pos < _len ? + _str[_pos] : + (char)0; + } + + internal char NextNonDigit() + { + int i = _pos; + while (i < _len) + { + char ch = _str[i]; + if (ch < '0' || ch > '9') return ch; + i++; + } + + return (char)0; + } + + internal bool TryParse(ReadOnlySpan input, ref TimeSpanResult result) + { + result.parsedTimeSpan._ticks = 0; + + _str = input; + _len = input.Length; + _pos = -1; + NextChar(); + SkipBlanks(); + + bool negative = false; + if (_ch == '-') + { + negative = true; + NextChar(); + } + + long time; + if (NextNonDigit() == ':') + { + if (!ParseTime(out time, ref result)) + { + return false; + }; + } + else + { + int days; + if (!ParseInt((int)(0x7FFFFFFFFFFFFFFFL / TimeSpan.TicksPerDay), out days, ref result)) + { + return false; + } + + time = days * TimeSpan.TicksPerDay; + + if (_ch == '.') + { + NextChar(); + long remainingTime; + if (!ParseTime(out remainingTime, ref result)) + { + return false; + }; + time += remainingTime; + } + } + + if (negative) + { + time = -time; + // Allow -0 as well + if (time > 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + else + { + if (time < 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + + SkipBlanks(); + + if (_pos < _len) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + result.parsedTimeSpan._ticks = time; + return true; + } + + internal bool ParseInt(int max, out int i, ref TimeSpanResult result) + { + i = 0; + int p = _pos; + while (_ch >= '0' && _ch <= '9') + { + if ((i & 0xF0000000) != 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + + i = i * 10 + _ch - '0'; + if (i < 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + + NextChar(); + } + + if (p == _pos) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + if (i > max) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + + return true; + } + + internal bool ParseTime(out long time, ref TimeSpanResult result) + { + time = 0; + int unit; + + if (!ParseInt(23, out unit, ref result)) + { + return false; + } + + time = unit * TimeSpan.TicksPerHour; + if (_ch != ':') + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + NextChar(); + + if (!ParseInt(59, out unit, ref result)) + { + return false; + } + + time += unit * TimeSpan.TicksPerMinute; + + if (_ch == ':') + { + NextChar(); + + // allow seconds with the leading zero + if (_ch != '.') + { + if (!ParseInt(59, out unit, ref result)) + { + return false; + } + time += unit * TimeSpan.TicksPerSecond; + } + + if (_ch == '.') + { + NextChar(); + int f = (int)TimeSpan.TicksPerSecond; + while (f > 1 && _ch >= '0' && _ch <= '9') + { + f /= 10; + time += (_ch - '0') * f; + NextChar(); + } + } + } + + return true; + } + + internal void SkipBlanks() + { + while (_ch == ' ' || _ch == '\t') NextChar(); + } + } + + /// Common private ParseExactMultiple method called by both ParseExactMultiple and TryParseExactMultiple. + private static bool TryParseExactMultipleTimeSpan(ReadOnlySpan input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, ref TimeSpanResult result) + { + if (formats == null) + { + return result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), argumentName: nameof(formats)); + } + + if (input.Length == 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + if (formats.Length == 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier)); + } + + // Do a loop through the provided formats and see if we can parse succesfully in + // one of the formats. + for (int i = 0; i < formats.Length; i++) + { + if (formats[i] == null || formats[i].Length == 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier)); + } + + // Create a new non-throwing result each time to ensure the runs are independent. + TimeSpanResult innerResult = new TimeSpanResult(throwOnFailure: false); + + if (TryParseExactTimeSpan(input, formats[i], formatProvider, styles, ref innerResult)) + { + result.parsedTimeSpan = innerResult.parsedTimeSpan; + return true; + } + } + + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + } +} diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystem.Current.Unix.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/TimeSpanStyles.cs similarity index 60% rename from external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystem.Current.Unix.cs rename to external/corefx/src/Common/src/CoreLib/System/Globalization/TimeSpanStyles.cs index eed4e936ac..68a47bcbe6 100644 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystem.Current.Unix.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/TimeSpanStyles.cs @@ -2,10 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace System.IO +namespace System.Globalization { - internal abstract partial class FileSystem + [Flags] + public enum TimeSpanStyles { - private static readonly FileSystem s_current = new UnixFileSystem(); + None = 0x00000000, + AssumeNegative = 0x00000001, } } diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/UmAlQuraCalendar.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/UmAlQuraCalendar.cs new file mode 100644 index 0000000000..21a938f8f1 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/UmAlQuraCalendar.cs @@ -0,0 +1,858 @@ +// 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.Diagnostics; + +namespace System.Globalization +{ + /* + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 1900/04/30 2077/05/13 + ** UmAlQura 1318/01/01 1500/12/30 + */ + + public partial class UmAlQuraCalendar : Calendar + { + internal const int MinCalendarYear = 1318; + internal const int MaxCalendarYear = 1500; + + internal struct DateMapping + { + internal DateMapping(int MonthsLengthFlags, int GYear, int GMonth, int GDay) + { + HijriMonthsLengthFlags = MonthsLengthFlags; + GregorianDate = new DateTime(GYear, GMonth, GDay); + } + internal int HijriMonthsLengthFlags; + internal DateTime GregorianDate; + } + + private static readonly DateMapping[] s_hijriYearInfo = InitDateMapping(); + + private static DateMapping[] InitDateMapping() + { + short[] rawData = new short[] + { + //These data is taken from Tables/Excel/UmAlQura.xls please make sure that the two places are in sync + /* DaysPerM GY GM GD D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 + 1318*/0x02EA, 1900, 4, 30,/* 0 1 0 1 0 1 1 1 0 1 0 0 4/30/1900 + 1319*/0x06E9, 1901, 4, 19,/* 1 0 0 1 0 1 1 1 0 1 1 0 4/19/1901 + 1320*/0x0ED2, 1902, 4, 9,/* 0 1 0 0 1 0 1 1 0 1 1 1 4/9/1902 + 1321*/0x0EA4, 1903, 3, 30,/* 0 0 1 0 0 1 0 1 0 1 1 1 3/30/1903 + 1322*/0x0D4A, 1904, 3, 18,/* 0 1 0 1 0 0 1 0 1 0 1 1 3/18/1904 + 1323*/0x0A96, 1905, 3, 7,/* 0 1 1 0 1 0 0 1 0 1 0 1 3/7/1905 + 1324*/0x0536, 1906, 2, 24,/* 0 1 1 0 1 1 0 0 1 0 1 0 2/24/1906 + 1325*/0x0AB5, 1907, 2, 13,/* 1 0 1 0 1 1 0 1 0 1 0 1 2/13/1907 + 1326*/0x0DAA, 1908, 2, 3,/* 0 1 0 1 0 1 0 1 1 0 1 1 2/3/1908 + 1327*/0x0BA4, 1909, 1, 23,/* 0 0 1 0 0 1 0 1 1 1 0 1 1/23/1909 + 1328*/0x0B49, 1910, 1, 12,/* 1 0 0 1 0 0 1 0 1 1 0 1 1/12/1910 + 1329*/0x0A93, 1911, 1, 1,/* 1 1 0 0 1 0 0 1 0 1 0 1 1/1/1911 + 1330*/0x052B, 1911, 12, 21,/* 1 1 0 1 0 1 0 0 1 0 1 0 12/21/1911 + 1331*/0x0A57, 1912, 12, 9,/* 1 1 1 0 1 0 1 0 0 1 0 1 12/9/1912 + 1332*/0x04B6, 1913, 11, 29,/* 0 1 1 0 1 1 0 1 0 0 1 0 11/29/1913 + 1333*/0x0AB5, 1914, 11, 18,/* 1 0 1 0 1 1 0 1 0 1 0 1 11/18/1914 + 1334*/0x05AA, 1915, 11, 8,/* 0 1 0 1 0 1 0 1 1 0 1 0 11/8/1915 + 1335*/0x0D55, 1916, 10, 27,/* 1 0 1 0 1 0 1 0 1 0 1 1 10/27/1916 + 1336*/0x0D2A, 1917, 10, 17,/* 0 1 0 1 0 1 0 0 1 0 1 1 10/17/1917 + 1337*/0x0A56, 1918, 10, 6,/* 0 1 1 0 1 0 1 0 0 1 0 1 10/6/1918 + 1338*/0x04AE, 1919, 9, 25,/* 0 1 1 1 0 1 0 1 0 0 1 0 9/25/1919 + 1339*/0x095D, 1920, 9, 13,/* 1 0 1 1 1 0 1 0 1 0 0 1 9/13/1920 + 1340*/0x02EC, 1921, 9, 3,/* 0 0 1 1 0 1 1 1 0 1 0 0 9/3/1921 + 1341*/0x06D5, 1922, 8, 23,/* 1 0 1 0 1 0 1 1 0 1 1 0 8/23/1922 + 1342*/0x06AA, 1923, 8, 13,/* 0 1 0 1 0 1 0 1 0 1 1 0 8/13/1923 + 1343*/0x0555, 1924, 8, 1,/* 1 0 1 0 1 0 1 0 1 0 1 0 8/1/1924 + 1344*/0x04AB, 1925, 7, 21,/* 1 1 0 1 0 1 0 1 0 0 1 0 7/21/1925 + 1345*/0x095B, 1926, 7, 10,/* 1 1 0 1 1 0 1 0 1 0 0 1 7/10/1926 + 1346*/0x02BA, 1927, 6, 30,/* 0 1 0 1 1 1 0 1 0 1 0 0 6/30/1927 + 1347*/0x0575, 1928, 6, 18,/* 1 0 1 0 1 1 1 0 1 0 1 0 6/18/1928 + 1348*/0x0BB2, 1929, 6, 8,/* 0 1 0 0 1 1 0 1 1 1 0 1 6/8/1929 + 1349*/0x0764, 1930, 5, 29,/* 0 0 1 0 0 1 1 0 1 1 1 0 5/29/1930 + 1350*/0x0749, 1931, 5, 18,/* 1 0 0 1 0 0 1 0 1 1 1 0 5/18/1931 + 1351*/0x0655, 1932, 5, 6,/* 1 0 1 0 1 0 1 0 0 1 1 0 5/6/1932 + 1352*/0x02AB, 1933, 4, 25,/* 1 1 0 1 0 1 0 1 0 1 0 0 4/25/1933 + 1353*/0x055B, 1934, 4, 14,/* 1 1 0 1 1 0 1 0 1 0 1 0 4/14/1934 + 1354*/0x0ADA, 1935, 4, 4,/* 0 1 0 1 1 0 1 1 0 1 0 1 4/4/1935 + 1355*/0x06D4, 1936, 3, 24,/* 0 0 1 0 1 0 1 1 0 1 1 0 3/24/1936 + 1356*/0x0EC9, 1937, 3, 13,/* 1 0 0 1 0 0 1 1 0 1 1 1 3/13/1937 + 1357*/0x0D92, 1938, 3, 3,/* 0 1 0 0 1 0 0 1 1 0 1 1 3/3/1938 + 1358*/0x0D25, 1939, 2, 20,/* 1 0 1 0 0 1 0 0 1 0 1 1 2/20/1939 + 1359*/0x0A4D, 1940, 2, 9,/* 1 0 1 1 0 0 1 0 0 1 0 1 2/9/1940 + 1360*/0x02AD, 1941, 1, 28,/* 1 0 1 1 0 1 0 1 0 1 0 0 1/28/1941 + 1361*/0x056D, 1942, 1, 17,/* 1 0 1 1 0 1 1 0 1 0 1 0 1/17/1942 + 1362*/0x0B6A, 1943, 1, 7,/* 0 1 0 1 0 1 1 0 1 1 0 1 1/7/1943 + 1363*/0x0B52, 1943, 12, 28,/* 0 1 0 0 1 0 1 0 1 1 0 1 12/28/1943 + 1364*/0x0AA5, 1944, 12, 16,/* 1 0 1 0 0 1 0 1 0 1 0 1 12/16/1944 + 1365*/0x0A4B, 1945, 12, 5,/* 1 1 0 1 0 0 1 0 0 1 0 1 12/5/1945 + 1366*/0x0497, 1946, 11, 24,/* 1 1 1 0 1 0 0 1 0 0 1 0 11/24/1946 + 1367*/0x0937, 1947, 11, 13,/* 1 1 1 0 1 1 0 0 1 0 0 1 11/13/1947 + 1368*/0x02B6, 1948, 11, 2,/* 0 1 1 0 1 1 0 1 0 1 0 0 11/2/1948 + 1369*/0x0575, 1949, 10, 22,/* 1 0 1 0 1 1 1 0 1 0 1 0 10/22/1949 + 1370*/0x0D6A, 1950, 10, 12,/* 0 1 0 1 0 1 1 0 1 0 1 1 10/12/1950 + 1371*/0x0D52, 1951, 10, 2,/* 0 1 0 0 1 0 1 0 1 0 1 1 10/2/1951 + 1372*/0x0A96, 1952, 9, 20,/* 0 1 1 0 1 0 0 1 0 1 0 1 9/20/1952 + 1373*/0x092D, 1953, 9, 9,/* 1 0 1 1 0 1 0 0 1 0 0 1 9/9/1953 + 1374*/0x025D, 1954, 8, 29,/* 1 0 1 1 1 0 1 0 0 1 0 0 8/29/1954 + 1375*/0x04DD, 1955, 8, 18,/* 1 0 1 1 1 0 1 1 0 0 1 0 8/18/1955 + 1376*/0x0ADA, 1956, 8, 7,/* 0 1 0 1 1 0 1 1 0 1 0 1 8/7/1956 + 1377*/0x05D4, 1957, 7, 28,/* 0 0 1 0 1 0 1 1 1 0 1 0 7/28/1957 + 1378*/0x0DA9, 1958, 7, 17,/* 1 0 0 1 0 1 0 1 1 0 1 1 7/17/1958 + 1379*/0x0D52, 1959, 7, 7,/* 0 1 0 0 1 0 1 0 1 0 1 1 7/7/1959 + 1380*/0x0AAA, 1960, 6, 25,/* 0 1 0 1 0 1 0 1 0 1 0 1 6/25/1960 + 1381*/0x04D6, 1961, 6, 14,/* 0 1 1 0 1 0 1 1 0 0 1 0 6/14/1961 + 1382*/0x09B6, 1962, 6, 3,/* 0 1 1 0 1 1 0 1 1 0 0 1 6/3/1962 + 1383*/0x0374, 1963, 5, 24,/* 0 0 1 0 1 1 1 0 1 1 0 0 5/24/1963 + 1384*/0x0769, 1964, 5, 12,/* 1 0 0 1 0 1 1 0 1 1 1 0 5/12/1964 + 1385*/0x0752, 1965, 5, 2,/* 0 1 0 0 1 0 1 0 1 1 1 0 5/2/1965 + 1386*/0x06A5, 1966, 4, 21,/* 1 0 1 0 0 1 0 1 0 1 1 0 4/21/1966 + 1387*/0x054B, 1967, 4, 10,/* 1 1 0 1 0 0 1 0 1 0 1 0 4/10/1967 + 1388*/0x0AAB, 1968, 3, 29,/* 1 1 0 1 0 1 0 1 0 1 0 1 3/29/1968 + 1389*/0x055A, 1969, 3, 19,/* 0 1 0 1 1 0 1 0 1 0 1 0 3/19/1969 + 1390*/0x0AD5, 1970, 3, 8,/* 1 0 1 0 1 0 1 1 0 1 0 1 3/8/1970 + 1391*/0x0DD2, 1971, 2, 26,/* 0 1 0 0 1 0 1 1 1 0 1 1 2/26/1971 + 1392*/0x0DA4, 1972, 2, 16,/* 0 0 1 0 0 1 0 1 1 0 1 1 2/16/1972 + 1393*/0x0D49, 1973, 2, 4,/* 1 0 0 1 0 0 1 0 1 0 1 1 2/4/1973 + 1394*/0x0A95, 1974, 1, 24,/* 1 0 1 0 1 0 0 1 0 1 0 1 1/24/1974 + 1395*/0x052D, 1975, 1, 13,/* 1 0 1 1 0 1 0 0 1 0 1 0 1/13/1975 + 1396*/0x0A5D, 1976, 1, 2,/* 1 0 1 1 1 0 1 0 0 1 0 1 1/2/1976 + 1397*/0x055A, 1976, 12, 22,/* 0 1 0 1 1 0 1 0 1 0 1 0 12/22/1976 + 1398*/0x0AD5, 1977, 12, 11,/* 1 0 1 0 1 0 1 1 0 1 0 1 12/11/1977 + 1399*/0x06AA, 1978, 12, 1,/* 0 1 0 1 0 1 0 1 0 1 1 0 12/1/1978 + 1400*/0x0695, 1979, 11, 20,/* 1 0 1 0 1 0 0 1 0 1 1 0 11/20/1979 + 1401*/0x052B, 1980, 11, 8,/* 1 1 0 1 0 1 0 0 1 0 1 0 11/8/1980 + 1402*/0x0A57, 1981, 10, 28,/* 1 1 1 0 1 0 1 0 0 1 0 1 10/28/1981 + 1403*/0x04AE, 1982, 10, 18,/* 0 1 1 1 0 1 0 1 0 0 1 0 10/18/1982 + 1404*/0x0976, 1983, 10, 7,/* 0 1 1 0 1 1 1 0 1 0 0 1 10/7/1983 + 1405*/0x056C, 1984, 9, 26,/* 0 0 1 1 0 1 1 0 1 0 1 0 9/26/1984 + 1406*/0x0B55, 1985, 9, 15,/* 1 0 1 0 1 0 1 0 1 1 0 1 9/15/1985 + 1407*/0x0AAA, 1986, 9, 5,/* 0 1 0 1 0 1 0 1 0 1 0 1 9/5/1986 + 1408*/0x0A55, 1987, 8, 25,/* 1 0 1 0 1 0 1 0 0 1 0 1 8/25/1987 + 1409*/0x04AD, 1988, 8, 13,/* 1 0 1 1 0 1 0 1 0 0 1 0 8/13/1988 + 1410*/0x095D, 1989, 8, 2,/* 1 0 1 1 1 0 1 0 1 0 0 1 8/2/1989 + 1411*/0x02DA, 1990, 7, 23,/* 0 1 0 1 1 0 1 1 0 1 0 0 7/23/1990 + 1412*/0x05D9, 1991, 7, 12,/* 1 0 0 1 1 0 1 1 1 0 1 0 7/12/1991 + 1413*/0x0DB2, 1992, 7, 1,/* 0 1 0 0 1 1 0 1 1 0 1 1 7/1/1992 + 1414*/0x0BA4, 1993, 6, 21,/* 0 0 1 0 0 1 0 1 1 1 0 1 6/21/1993 + 1415*/0x0B4A, 1994, 6, 10,/* 0 1 0 1 0 0 1 0 1 1 0 1 6/10/1994 + 1416*/0x0A55, 1995, 5, 30,/* 1 0 1 0 1 0 1 0 0 1 0 1 5/30/1995 + 1417*/0x02B5, 1996, 5, 18,/* 1 0 1 0 1 1 0 1 0 1 0 0 5/18/1996 + 1418*/0x0575, 1997, 5, 7,/* 1 0 1 0 1 1 1 0 1 0 1 0 5/7/1997 + 1419*/0x0B6A, 1998, 4, 27,/* 0 1 0 1 0 1 1 0 1 1 0 1 4/27/1998 + 1420*/0x0BD2, 1999, 4, 17,/* 0 1 0 0 1 0 1 1 1 1 0 1 4/17/1999 + 1421*/0x0BC4, 2000, 4, 6,/* 0 0 1 0 0 0 1 1 1 1 0 1 4/6/2000 + 1422*/0x0B89, 2001, 3, 26,/* 1 0 0 1 0 0 0 1 1 1 0 1 3/26/2001 + 1423*/0x0A95, 2002, 3, 15,/* 1 0 1 0 1 0 0 1 0 1 0 1 3/15/2002 + 1424*/0x052D, 2003, 3, 4,/* 1 0 1 1 0 1 0 0 1 0 1 0 3/4/2003 + 1425*/0x05AD, 2004, 2, 21,/* 1 0 1 1 0 1 0 1 1 0 1 0 2/21/2004 + 1426*/0x0B6A, 2005, 2, 10,/* 0 1 0 1 0 1 1 0 1 1 0 1 2/10/2005 + 1427*/0x06D4, 2006, 1, 31,/* 0 0 1 0 1 0 1 1 0 1 1 0 1/31/2006 + 1428*/0x0DC9, 2007, 1, 20,/* 1 0 0 1 0 0 1 1 1 0 1 1 1/20/2007 + 1429*/0x0D92, 2008, 1, 10,/* 0 1 0 0 1 0 0 1 1 0 1 1 1/10/2008 + 1430*/0x0AA6, 2008, 12, 29,/* 0 1 1 0 0 1 0 1 0 1 0 1 12/29/2008 + 1431*/0x0956, 2009, 12, 18,/* 0 1 1 0 1 0 1 0 1 0 0 1 12/18/2009 + 1432*/0x02AE, 2010, 12, 7,/* 0 1 1 1 0 1 0 1 0 1 0 0 12/7/2010 + 1433*/0x056D, 2011, 11, 26,/* 1 0 1 1 0 1 1 0 1 0 1 0 11/26/2011 + 1434*/0x036A, 2012, 11, 15,/* 0 1 0 1 0 1 1 0 1 1 0 0 11/15/2012 + 1435*/0x0B55, 2013, 11, 4,/* 1 0 1 0 1 0 1 0 1 1 0 1 11/4/2013 + 1436*/0x0AAA, 2014, 10, 25,/* 0 1 0 1 0 1 0 1 0 1 0 1 10/25/2014 + 1437*/0x094D, 2015, 10, 14,/* 1 0 1 1 0 0 1 0 1 0 0 1 10/14/2015 + 1438*/0x049D, 2016, 10, 2,/* 1 0 1 1 1 0 0 1 0 0 1 0 10/2/2016 + 1439*/0x095D, 2017, 9, 21,/* 1 0 1 1 1 0 1 0 1 0 0 1 9/21/2017 + 1440*/0x02BA, 2018, 9, 11,/* 0 1 0 1 1 1 0 1 0 1 0 0 9/11/2018 + 1441*/0x05B5, 2019, 8, 31,/* 1 0 1 0 1 1 0 1 1 0 1 0 8/31/2019 + 1442*/0x05AA, 2020, 8, 20,/* 0 1 0 1 0 1 0 1 1 0 1 0 8/20/2020 + 1443*/0x0D55, 2021, 8, 9,/* 1 0 1 0 1 0 1 0 1 0 1 1 8/9/2021 + 1444*/0x0A9A, 2022, 7, 30,/* 0 1 0 1 1 0 0 1 0 1 0 1 7/30/2022 + 1445*/0x092E, 2023, 7, 19,/* 0 1 1 1 0 1 0 0 1 0 0 1 7/19/2023 + 1446*/0x026E, 2024, 7, 7,/* 0 1 1 1 0 1 1 0 0 1 0 0 7/7/2024 + 1447*/0x055D, 2025, 6, 26,/* 1 0 1 1 1 0 1 0 1 0 1 0 6/26/2025 + 1448*/0x0ADA, 2026, 6, 16,/* 0 1 0 1 1 0 1 1 0 1 0 1 6/16/2026 + 1449*/0x06D4, 2027, 6, 6,/* 0 0 1 0 1 0 1 1 0 1 1 0 6/6/2027 + 1450*/0x06A5, 2028, 5, 25,/* 1 0 1 0 0 1 0 1 0 1 1 0 5/25/2028 + 1451*/0x054B, 2029, 5, 14,/* 1 1 0 1 0 0 1 0 1 0 1 0 5/14/2029 + 1452*/0x0A97, 2030, 5, 3,/* 1 1 1 0 1 0 0 1 0 1 0 1 5/3/2030 + 1453*/0x054E, 2031, 4, 23,/* 0 1 1 1 0 0 1 0 1 0 1 0 4/23/2031 + 1454*/0x0AAE, 2032, 4, 11,/* 0 1 1 1 0 1 0 1 0 1 0 1 4/11/2032 + 1455*/0x05AC, 2033, 4, 1,/* 0 0 1 1 0 1 0 1 1 0 1 0 4/1/2033 + 1456*/0x0BA9, 2034, 3, 21,/* 1 0 0 1 0 1 0 1 1 1 0 1 3/21/2034 + 1457*/0x0D92, 2035, 3, 11,/* 0 1 0 0 1 0 0 1 1 0 1 1 3/11/2035 + 1458*/0x0B25, 2036, 2, 28,/* 1 0 1 0 0 1 0 0 1 1 0 1 2/28/2036 + 1459*/0x064B, 2037, 2, 16,/* 1 1 0 1 0 0 1 0 0 1 1 0 2/16/2037 + 1460*/0x0CAB, 2038, 2, 5,/* 1 1 0 1 0 1 0 1 0 0 1 1 2/5/2038 + 1461*/0x055A, 2039, 1, 26,/* 0 1 0 1 1 0 1 0 1 0 1 0 1/26/2039 + 1462*/0x0B55, 2040, 1, 15,/* 1 0 1 0 1 0 1 0 1 1 0 1 1/15/2040 + 1463*/0x06D2, 2041, 1, 4,/* 0 1 0 0 1 0 1 1 0 1 1 0 1/4/2041 + 1464*/0x0EA5, 2041, 12, 24,/* 1 0 1 0 0 1 0 1 0 1 1 1 12/24/2041 + 1465*/0x0E4A, 2042, 12, 14,/* 0 1 0 1 0 0 1 0 0 1 1 1 12/14/2042 + 1466*/0x0A95, 2043, 12, 3,/* 1 0 1 0 1 0 0 1 0 1 0 1 12/3/2043 + 1467*/0x052D, 2044, 11, 21,/* 1 0 1 1 0 1 0 0 1 0 1 0 11/21/2044 + 1468*/0x0AAD, 2045, 11, 10,/* 1 0 1 1 0 1 0 1 0 1 0 1 11/10/2045 + 1469*/0x036C, 2046, 10, 31,/* 0 0 1 1 0 1 1 0 1 1 0 0 10/31/2046 + 1470*/0x0759, 2047, 10, 20,/* 1 0 0 1 1 0 1 0 1 1 1 0 10/20/2047 + 1471*/0x06D2, 2048, 10, 9,/* 0 1 0 0 1 0 1 1 0 1 1 0 10/9/2048 + 1472*/0x0695, 2049, 9, 28,/* 1 0 1 0 1 0 0 1 0 1 1 0 9/28/2049 + 1473*/0x052D, 2050, 9, 17,/* 1 0 1 1 0 1 0 0 1 0 1 0 9/17/2050 + 1474*/0x0A5B, 2051, 9, 6,/* 1 1 0 1 1 0 1 0 0 1 0 1 9/6/2051 + 1475*/0x04BA, 2052, 8, 26,/* 0 1 0 1 1 1 0 1 0 0 1 0 8/26/2052 + 1476*/0x09BA, 2053, 8, 15,/* 0 1 0 1 1 1 0 1 1 0 0 1 8/15/2053 + 1477*/0x03B4, 2054, 8, 5,/* 0 0 1 0 1 1 0 1 1 1 0 0 8/5/2054 + 1478*/0x0B69, 2055, 7, 25,/* 1 0 0 1 0 1 1 0 1 1 0 1 7/25/2055 + 1479*/0x0B52, 2056, 7, 14,/* 0 1 0 0 1 0 1 0 1 1 0 1 7/14/2056 + 1480*/0x0AA6, 2057, 7, 3,/* 0 1 1 0 0 1 0 1 0 1 0 1 7/3/2057 + 1481*/0x04B6, 2058, 6, 22,/* 0 1 1 0 1 1 0 1 0 0 1 0 6/22/2058 + 1482*/0x096D, 2059, 6, 11,/* 1 0 1 1 0 1 1 0 1 0 0 1 6/11/2059 + 1483*/0x02EC, 2060, 5, 31,/* 0 0 1 1 0 1 1 1 0 1 0 0 5/31/2060 + 1484*/0x06D9, 2061, 5, 20,/* 1 0 0 1 1 0 1 1 0 1 1 0 5/20/2061 + 1485*/0x0EB2, 2062, 5, 10,/* 0 1 0 0 1 1 0 1 0 1 1 1 5/10/2062 + 1486*/0x0D54, 2063, 4, 30,/* 0 0 1 0 1 0 1 0 1 0 1 1 4/30/2063 + 1487*/0x0D2A, 2064, 4, 18,/* 0 1 0 1 0 1 0 0 1 0 1 1 4/18/2064 + 1488*/0x0A56, 2065, 4, 7,/* 0 1 1 0 1 0 1 0 0 1 0 1 4/7/2065 + 1489*/0x04AE, 2066, 3, 27,/* 0 1 1 1 0 1 0 1 0 0 1 0 3/27/2066 + 1490*/0x096D, 2067, 3, 16,/* 1 0 1 1 0 1 1 0 1 0 0 1 3/16/2067 + 1491*/0x0D6A, 2068, 3, 5,/* 0 1 0 1 0 1 1 0 1 0 1 1 3/5/2068 + 1492*/0x0B54, 2069, 2, 23,/* 0 0 1 0 1 0 1 0 1 1 0 1 2/23/2069 + 1493*/0x0B29, 2070, 2, 12,/* 1 0 0 1 0 1 0 0 1 1 0 1 2/12/2070 + 1494*/0x0A93, 2071, 2, 1,/* 1 1 0 0 1 0 0 1 0 1 0 1 2/1/2071 + 1495*/0x052B, 2072, 1, 21,/* 1 1 0 1 0 1 0 0 1 0 1 0 1/21/2072 + 1496*/0x0A57, 2073, 1, 9,/* 1 1 1 0 1 0 1 0 0 1 0 1 1/9/2073 + 1497*/0x0536, 2073, 12, 30,/* 0 1 1 0 1 1 0 0 1 0 1 0 12/30/2073 + 1498*/0x0AB5, 2074, 12, 19,/* 1 0 1 0 1 1 0 1 0 1 0 1 12/19/2074 + 1499*/0x06AA, 2075, 12, 9,/* 0 1 0 1 0 1 0 1 0 1 1 0 12/9/2075 + 1500*/0x0E93, 2076, 11, 27,/* 1 1 0 0 1 0 0 1 0 1 1 1 11/27/2076 + 1501*/ 0, 2077, 11, 17,/* 0 0 0 0 0 0 0 0 0 0 0 0 11/17/2077 + */ }; + // Direct inline initialization of DateMapping array would produce a lot of code bloat. + + // We take advantage of C# compiler compiles inline initialization of primitive type array into very compact code. + // So we start with raw data stored in primitive type array, and initialize the DateMapping out of it + + DateMapping[] mapping = new DateMapping[rawData.Length / 4]; + for (int i = 0; i < mapping.Length; i++) + mapping[i] = new DateMapping(rawData[i * 4], rawData[i * 4 + 1], rawData[i * 4 + 2], rawData[i * 4 + 3]); + return mapping; + } + + public const int UmAlQuraEra = 1; + + internal const int DateCycle = 30; + internal const int DatePartYear = 0; + internal const int DatePartDayOfYear = 1; + internal const int DatePartMonth = 2; + internal const int DatePartDay = 3; + + + // This is the minimal Gregorian date that we support in the UmAlQuraCalendar. + internal static DateTime minDate = new DateTime(1900, 4, 30); + internal static DateTime maxDate = new DateTime((new DateTime(2077, 11, 16, 23, 59, 59, 999)).Ticks + 9999); + + public override DateTime MinSupportedDateTime + { + get + { + return (minDate); + } + } + + public override DateTime MaxSupportedDateTime + { + get + { + return (maxDate); + } + } + + public override CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.LunarCalendar; + } + } + + public UmAlQuraCalendar() + { + } + + internal override CalendarId BaseCalendarID + { + get + { + return (CalendarId.HIJRI); + } + } + + internal override CalendarId ID + { + get + { + return (CalendarId.UMALQURA); + } + } + + protected override int DaysInYearBeforeMinSupportedYear + { + get + { + // HijriCalendar has same number of days as UmAlQuraCalendar for any given year + // HijriCalendar says year 1317 has 355 days. + return 355; + } + } + + /*==========================ConvertHijriToGregorian========================== + ** Purpose: convert Hdate(year,month,day) to Gdate(year,month,day) + ** Arguments: + ** Input/Ouput: Hijrah date: year:yh, month:mh, day:dh + ** Output: Gregorian date: year:yg, month:mg, day:dg , day of week:dayweek + ** and returns flag found:1 not found:0 + =========================ConvertHijriToGregorian============================*/ + private static void ConvertHijriToGregorian(int HijriYear, int HijriMonth, int HijriDay, ref int yg, ref int mg, ref int dg) + { + Debug.Assert((HijriYear >= MinCalendarYear) && (HijriYear <= MaxCalendarYear), "Hijri year is out of range."); + Debug.Assert(HijriMonth >= 1, "Hijri month is out of range."); + Debug.Assert(HijriDay >= 1, "Hijri day is out of range."); + int index, b, nDays = HijriDay - 1; + DateTime dt; + + + index = HijriYear - MinCalendarYear; + dt = s_hijriYearInfo[index].GregorianDate; + + + b = s_hijriYearInfo[index].HijriMonthsLengthFlags; + + for (int m = 1; m < HijriMonth; m++) + { + nDays = nDays + 29 + (b & 1); /* Add the months lengths before mh */ + b = b >> 1; + } + + dt = dt.AddDays(nDays); + dt.GetDatePart(out yg, out mg, out dg); + } + + /*=================================GetAbsoluteDateUmAlQura========================== + **Action: Gets the Absolute date for the given UmAlQura date. The absolute date means + ** the number of days from January 1st, 1 A.D. + **Returns: + **Arguments: + **Exceptions: + ============================================================================*/ + private static long GetAbsoluteDateUmAlQura(int year, int month, int day) + { + //Caller should check the validaty of year, month and day. + + int yg = 0, mg = 0, dg = 0; + ConvertHijriToGregorian(year, month, day, ref yg, ref mg, ref dg); + return GregorianCalendar.GetAbsoluteDate(yg, mg, dg); + } + + internal static void CheckTicksRange(long ticks) + { + if (ticks < minDate.Ticks || ticks > maxDate.Ticks) + { + throw new ArgumentOutOfRangeException( + "time", + String.Format( + CultureInfo.InvariantCulture, + SR.ArgumentOutOfRange_CalendarRange, + minDate, + maxDate)); + } + } + + internal static void CheckEraRange(int era) + { + if (era != CurrentEra && era != UmAlQuraEra) + { + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + } + + internal static void CheckYearRange(int year, int era) + { + CheckEraRange(era); + if (year < MinCalendarYear || year > MaxCalendarYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + MinCalendarYear, + MaxCalendarYear)); + } + } + + internal static void CheckYearMonthRange(int year, int month, int era) + { + CheckYearRange(year, era); + if (month < 1 || month > 12) + { + throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month); + } + } + + /*========================ConvertGregorianToHijri============================ + ** Purpose: convert DateTime to Hdate(year,month,day) + ** Arguments: + ** Input: DateTime + ** Output: Hijrah date: year:yh, month:mh, day:dh + ============================================================================*/ + private static void ConvertGregorianToHijri(DateTime time, ref int HijriYear, ref int HijriMonth, ref int HijriDay) + { + int index, b, DaysPerThisMonth; + double nDays; + TimeSpan ts; + int yh1 = 0, mh1 = 0, dh1 = 0; + + Debug.Assert((time.Ticks >= minDate.Ticks) && (time.Ticks <= maxDate.Ticks), "Gregorian date is out of range."); + + // Find the index where we should start our search by quessing the Hijri year that we will be in HijriYearInfo. + // A Hijri year is 354 or 355 days. Use 355 days so that we will search from a lower index. + + index = (int)((time.Ticks - minDate.Ticks) / Calendar.TicksPerDay) / 355; + do + { + } while (time.CompareTo(s_hijriYearInfo[++index].GregorianDate) > 0); //while greater + + if (time.CompareTo(s_hijriYearInfo[index].GregorianDate) != 0) + { + index--; + } + + ts = time.Subtract(s_hijriYearInfo[index].GregorianDate); + yh1 = index + MinCalendarYear; + + mh1 = 1; + dh1 = 1; + nDays = ts.TotalDays; + b = s_hijriYearInfo[index].HijriMonthsLengthFlags; + DaysPerThisMonth = 29 + (b & 1); + + while (nDays >= DaysPerThisMonth) + { + nDays -= DaysPerThisMonth; + b = b >> 1; + DaysPerThisMonth = 29 + (b & 1); + mh1++; + } + dh1 += (int)nDays; + + HijriDay = dh1; + HijriMonth = mh1; + HijriYear = yh1; + } + + /*=================================GetDatePart========================== + **Action: Returns a given date part of this DateTime. This method is used + ** to compute the year, day-of-year, month, or day part. + **Returns: + **Arguments: + **Exceptions: ArgumentException if part is incorrect. + **Notes: + ** First, we get the absolute date (the number of days from January 1st, 1 A.C) for the given ticks. + ** Use the formula (((AbsoluteDate - 226894) * 33) / (33 * 365 + 8)) + 1, we can a rough value for the UmAlQura year. + ** In order to get the exact UmAlQura year, we compare the exact absolute date for UmAlQuraYear and (UmAlQuraYear + 1). + ** From here, we can get the correct UmAlQura year. + ============================================================================*/ + + internal virtual int GetDatePart(DateTime time, int part) + { + int UmAlQuraYear = 0; // UmAlQura year + int UmAlQuraMonth = 0; // UmAlQura month + int UmAlQuraDay = 0; // UmAlQura day + long ticks = time.Ticks; + CheckTicksRange(ticks); + + ConvertGregorianToHijri(time, ref UmAlQuraYear, ref UmAlQuraMonth, ref UmAlQuraDay); + + if (part == DatePartYear) + return (UmAlQuraYear); + + if (part == DatePartMonth) + return (UmAlQuraMonth); + + if (part == DatePartDay) + return (UmAlQuraDay); + + if (part == DatePartDayOfYear) + return (int)(GetAbsoluteDateUmAlQura(UmAlQuraYear, UmAlQuraMonth, UmAlQuraDay) - GetAbsoluteDateUmAlQura(UmAlQuraYear, 1, 1) + 1); + + // Incorrect part value. + throw new InvalidOperationException(SR.InvalidOperation_DateTimeParsing); + } + + // Returns the DateTime resulting from adding the given number of + // months to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year and month parts of the specified DateTime by + // value months, and, if required, adjusting the day part of the + // resulting date downwards to the last day of the resulting month in the + // resulting year. The time-of-day part of the result is the same as the + // time-of-day part of the specified DateTime. + // + // In more precise terms, considering the specified DateTime to be of the + // form y / m / d + t, where y is the + // year, m is the month, d is the day, and t is the + // time-of-day, the result is y1 / m1 / d1 + t, + // where y1 and m1 are computed by adding value months + // to y and m, and d1 is the largest value less than + // or equal to d that denotes a valid day in month m1 of year + // y1. + // + + + public override DateTime AddMonths(DateTime time, int months) + { + if (months < -120000 || months > 120000) + { + throw new ArgumentOutOfRangeException( + nameof(months), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + -120000, + 120000)); + } + // Get the date in UmAlQura calendar. + int y = GetDatePart(time, DatePartYear); + int m = GetDatePart(time, DatePartMonth); + int d = GetDatePart(time, DatePartDay); + int i = m - 1 + months; + + if (i >= 0) + { + m = i % 12 + 1; + y = y + i / 12; + } + else + { + m = 12 + (i + 1) % 12; + y = y + (i - 11) / 12; + } + + if (d > 29) + { + int days = GetDaysInMonth(y, m); + if (d > days) + { + d = days; + } + } + CheckYearRange(y, UmAlQuraEra); + DateTime dt = new DateTime(GetAbsoluteDateUmAlQura(y, m, d) * TicksPerDay + time.Ticks % TicksPerDay); + Calendar.CheckAddResult(dt.Ticks, MinSupportedDateTime, MaxSupportedDateTime); + return (dt); + } + + // Returns the DateTime resulting from adding the given number of + // years to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year part of the specified DateTime by value + // years. If the month and day of the specified DateTime is 2/29, and if the + // resulting year is not a leap year, the month and day of the resulting + // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day + // parts of the result are the same as those of the specified DateTime. + // + + + public override DateTime AddYears(DateTime time, int years) + { + return (AddMonths(time, years * 12)); + } + + // Returns the day-of-month part of the specified DateTime. The returned + // value is an integer between 1 and 31. + // + + + public override int GetDayOfMonth(DateTime time) + { + return (GetDatePart(time, DatePartDay)); + } + + // Returns the day-of-week part of the specified DateTime. The returned value + // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates + // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates + // Thursday, 5 indicates Friday, and 6 indicates Saturday. + // + + + public override DayOfWeek GetDayOfWeek(DateTime time) + { + return ((DayOfWeek)((int)(time.Ticks / TicksPerDay + 1) % 7)); + } + + // Returns the day-of-year part of the specified DateTime. The returned value + // is an integer between 1 and 354 or 355. + // + + + public override int GetDayOfYear(DateTime time) + { + return (GetDatePart(time, DatePartDayOfYear)); + } + + /* + internal bool CouldBeLeapYear(int year) + { + return ((((year * 11) + 14) % 30) < 11); + } + */ + + // Returns the number of days in the month given by the year and + // month arguments. + // + + + public override int GetDaysInMonth(int year, int month, int era) + { + CheckYearMonthRange(year, month, era); + + if ((s_hijriYearInfo[year - MinCalendarYear].HijriMonthsLengthFlags & (1 << month - 1)) == 0) + return 29; + else + return 30; + } + + internal static int RealGetDaysInYear(int year) + { + int days = 0, b; + + Debug.Assert((year >= MinCalendarYear) && (year <= MaxCalendarYear), "Hijri year is out of range."); + + b = s_hijriYearInfo[year - MinCalendarYear].HijriMonthsLengthFlags; + + for (int m = 1; m <= 12; m++) + { + days = days + 29 + (b & 1); /* Add the months lengths before mh */ + b = b >> 1; + } + Debug.Assert((days == 354) || (days == 355), "Hijri year has to be 354 or 355 days."); + return days; + } + + // Returns the number of days in the year given by the year argument for the current era. + // + + + public override int GetDaysInYear(int year, int era) + { + CheckYearRange(year, era); + return (RealGetDaysInYear(year)); + } + + // Returns the era for the specified DateTime value. + + + public override int GetEra(DateTime time) + { + CheckTicksRange(time.Ticks); + return (UmAlQuraEra); + } + + + + public override int[] Eras + { + get + { + return (new int[] { UmAlQuraEra }); + } + } + + // Returns the month part of the specified DateTime. The returned value is an + // integer between 1 and 12. + // + + + public override int GetMonth(DateTime time) + { + return (GetDatePart(time, DatePartMonth)); + } + + // Returns the number of months in the specified year and era. + + + public override int GetMonthsInYear(int year, int era) + { + CheckYearRange(year, era); + return (12); + } + + // Returns the year part of the specified DateTime. The returned value is an + // integer between MinCalendarYear and MaxCalendarYear. + // + + + public override int GetYear(DateTime time) + { + return (GetDatePart(time, DatePartYear)); + } + + // Checks whether a given day in the specified era is a leap day. This method returns true if + // the date is a leap day, or false if not. + // + + + public override bool IsLeapDay(int year, int month, int day, int era) + { + if (day >= 1 && day <= 29) + { + CheckYearMonthRange(year, month, era); + return (false); + } + + // The year/month/era value checking is done in GetDaysInMonth(). + int daysInMonth = GetDaysInMonth(year, month, era); + if (day < 1 || day > daysInMonth) + { + throw new ArgumentOutOfRangeException( + nameof(day), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Day, + daysInMonth, + month)); + } + return (false); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this calendar does not have leap month, or this year is not a leap year. + // + + + public override int GetLeapMonth(int year, int era) + { + CheckYearRange(year, era); + return (0); + } + + // Checks whether a given month in the specified era is a leap month. This method returns true if + // month is a leap month, or false if not. + // + + + public override bool IsLeapMonth(int year, int month, int era) + { + CheckYearMonthRange(year, month, era); + return (false); + } + + // Checks whether a given year in the specified era is a leap year. This method returns true if + // year is a leap year, or false if not. + // + + + public override bool IsLeapYear(int year, int era) + { + CheckYearRange(year, era); + if (RealGetDaysInYear(year) == 355) + return true; + else + return false; + } + + // Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid. + // + + + public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) + { + if (day >= 1 && day <= 29) + { + CheckYearMonthRange(year, month, era); + goto DayInRang; + } + + // The year/month/era value checking is done in GetDaysInMonth(). + int daysInMonth = GetDaysInMonth(year, month, era); + + if (day < 1 || day > daysInMonth) + { + throw new ArgumentOutOfRangeException( + nameof(day), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Day, + daysInMonth, + month)); + } + DayInRang: + long lDate = GetAbsoluteDateUmAlQura(year, month, day); + + if (lDate >= 0) + { + return (new DateTime(lDate * GregorianCalendar.TicksPerDay + TimeToTicks(hour, minute, second, millisecond))); + } + else + { + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay); + } + } + + private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 1451; + + + + public override int TwoDigitYearMax + { + get + { + if (twoDigitYearMax == -1) + { + twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX); + } + return (twoDigitYearMax); + } + + set + { + if (value != 99 && (value < MinCalendarYear || value > MaxCalendarYear)) + { + throw new ArgumentOutOfRangeException( + nameof(value), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + MinCalendarYear, + MaxCalendarYear)); + } + VerifyWritable(); + // We allow year 99 to be set so that one can make ToFourDigitYearMax a no-op by setting TwoDigitYearMax to 99. + twoDigitYearMax = value; + } + } + + + + public override int ToFourDigitYear(int year) + { + if (year < 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedNonNegNum); + } + + if (year < 100) + { + return (base.ToFourDigitYear(year)); + } + + if ((year < MinCalendarYear) || (year > MaxCalendarYear)) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + MinCalendarYear, + MaxCalendarYear)); + } + return (year); + } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Globalization/UnicodeCategory.cs b/external/corefx/src/Common/src/CoreLib/System/Globalization/UnicodeCategory.cs new file mode 100644 index 0000000000..f0ae1fdfd9 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Globalization/UnicodeCategory.cs @@ -0,0 +1,40 @@ +// 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. + +namespace System.Globalization +{ + public enum UnicodeCategory + { + UppercaseLetter = 0, + LowercaseLetter = 1, + TitlecaseLetter = 2, + ModifierLetter = 3, + OtherLetter = 4, + NonSpacingMark = 5, + SpacingCombiningMark = 6, + EnclosingMark = 7, + DecimalDigitNumber = 8, + LetterNumber = 9, + OtherNumber = 10, + SpaceSeparator = 11, + LineSeparator = 12, + ParagraphSeparator = 13, + Control = 14, + Format = 15, + Surrogate = 16, + PrivateUse = 17, + ConnectorPunctuation = 18, + DashPunctuation = 19, + OpenPunctuation = 20, + ClosePunctuation = 21, + InitialQuotePunctuation = 22, + FinalQuotePunctuation = 23, + OtherPunctuation = 24, + MathSymbol = 25, + CurrencySymbol = 26, + ModifierSymbol = 27, + OtherSymbol = 28, + OtherNotAssigned = 29, + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Guid.cs b/external/corefx/src/Common/src/CoreLib/System/Guid.cs similarity index 53% rename from external/corert/src/System.Private.CoreLib/src/System/Guid.cs rename to external/corefx/src/Common/src/CoreLib/System/Guid.cs index 5e0410ce5e..423d5bc78c 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Guid.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Guid.cs @@ -3,50 +3,56 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Internal.Runtime.CompilerServices; + namespace System { // Represents a Globally Unique Identifier. [StructLayout(LayoutKind.Sequential)] - public partial struct Guid : IFormattable, IComparable, IComparable, IEquatable + [Serializable] + [Runtime.Versioning.NonVersionable] // This only applies to field layout + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public partial struct Guid : IFormattable, IComparable, IComparable, IEquatable, ISpanFormattable { public static readonly Guid Empty = new Guid(); + //////////////////////////////////////////////////////////////////////////////// // Member variables //////////////////////////////////////////////////////////////////////////////// - private int _a; - private short _b; - private short _c; - private byte _d; - private byte _e; - private byte _f; - private byte _g; - private byte _h; - private byte _i; - private byte _j; - private byte _k; - - + private int _a; // Do not rename (binary serialization) + private short _b; // Do not rename (binary serialization) + private short _c; // Do not rename (binary serialization) + private byte _d; // Do not rename (binary serialization) + private byte _e; // Do not rename (binary serialization) + private byte _f; // Do not rename (binary serialization) + private byte _g; // Do not rename (binary serialization) + private byte _h; // Do not rename (binary serialization) + private byte _i; // Do not rename (binary serialization) + private byte _j; // Do not rename (binary serialization) + private byte _k; // Do not rename (binary serialization) //////////////////////////////////////////////////////////////////////////////// // Constructors //////////////////////////////////////////////////////////////////////////////// // Creates a new guid from an array of bytes. - // - public Guid(byte[] b) + public Guid(byte[] b) : + this(new ReadOnlySpan(b ?? throw new ArgumentNullException(nameof(b)))) + { + } + + // Creates a new guid from a read-only span. + public Guid(ReadOnlySpan b) { - if (b == null) - throw new ArgumentNullException(nameof(b)); if (b.Length != 16) throw new ArgumentException(SR.Format(SR.Arg_GuidArrayCtor, "16"), nameof(b)); - Contract.EndContractBlock(); - _a = ((int)b[3] << 24) | ((int)b[2] << 16) | ((int)b[1] << 8) | b[0]; - _b = (short)(((int)b[5] << 8) | b[4]); - _c = (short)(((int)b[7] << 8) | b[6]); + _a = b[3] << 24 | b[2] << 16 | b[1] << 8 | b[0]; + _b = (short)(b[5] << 8 | b[4]); + _c = (short)(b[7] << 8 | b[6]); _d = b[8]; _e = b[9]; _f = b[10]; @@ -73,7 +79,6 @@ namespace System _k = k; } - // Creates a new GUID initialized to the value represented by the arguments. // public Guid(int a, short b, short c, byte[] d) @@ -83,7 +88,6 @@ namespace System // Check that array is not too big if (d.Length != 8) throw new ArgumentException(SR.Format(SR.Arg_GuidArrayCtor, "8"), nameof(d)); - Contract.EndContractBlock(); _a = a; _b = b; @@ -156,43 +160,46 @@ namespace System // This will store the result of the parsing. And it will eventually be used to construct a Guid instance. private struct GuidResult { - internal Guid parsedGuid; - internal GuidParseThrowStyle throwStyle; + internal Guid _parsedGuid; + internal GuidParseThrowStyle _throwStyle; - internal ParseFailureKind m_failure; - internal string m_resourceMessageFormat; - internal object m_failureMessageFormatArgument; - internal string m_failureArgumentName; - internal Exception m_innerException; + private ParseFailureKind _failure; + private string _failureMessageID; + private object _failureMessageFormatArgument; + private string _failureArgumentName; + private Exception _innerException; internal void Init(GuidParseThrowStyle canThrow) { - parsedGuid = Guid.Empty; - throwStyle = canThrow; + _throwStyle = canThrow; } + internal void SetFailure(Exception nativeException) { - m_failure = ParseFailureKind.NativeException; - m_innerException = nativeException; + _failure = ParseFailureKind.NativeException; + _innerException = nativeException; } + internal void SetFailure(ParseFailureKind failure, string failureMessageID) { SetFailure(failure, failureMessageID, null, null, null); } + internal void SetFailure(ParseFailureKind failure, string failureMessageID, object failureMessageFormatArgument) { SetFailure(failure, failureMessageID, failureMessageFormatArgument, null, null); } - internal void SetFailure(ParseFailureKind failure, string failureMessageFormat, object failureMessageFormatArgument, + + internal void SetFailure(ParseFailureKind failure, string failureMessageID, object failureMessageFormatArgument, string failureArgumentName, Exception innerException) { Debug.Assert(failure != ParseFailureKind.NativeException, "ParseFailureKind.NativeException should not be used with this overload"); - m_failure = failure; - m_resourceMessageFormat = failureMessageFormat; - m_failureMessageFormatArgument = failureMessageFormatArgument; - m_failureArgumentName = failureArgumentName; - m_innerException = innerException; - if (throwStyle != GuidParseThrowStyle.None) + _failure = failure; + _failureMessageID = failureMessageID; + _failureMessageFormatArgument = failureMessageFormatArgument; + _failureArgumentName = failureArgumentName; + _innerException = innerException; + if (_throwStyle != GuidParseThrowStyle.None) { throw GetGuidParseException(); } @@ -200,25 +207,25 @@ namespace System internal Exception GetGuidParseException() { - switch (m_failure) + switch (_failure) { case ParseFailureKind.ArgumentNull: - return new ArgumentNullException(m_failureArgumentName, m_resourceMessageFormat); + return new ArgumentNullException(_failureArgumentName, SR.GetResourceString(_failureMessageID)); case ParseFailureKind.FormatWithInnerException: - return new FormatException(m_resourceMessageFormat, m_innerException); + return new FormatException(SR.GetResourceString(_failureMessageID), _innerException); case ParseFailureKind.FormatWithParameter: - return new FormatException(SR.Format(m_resourceMessageFormat, m_failureMessageFormatArgument)); + return new FormatException(SR.Format(SR.GetResourceString(_failureMessageID), _failureMessageFormatArgument)); case ParseFailureKind.Format: - return new FormatException(m_resourceMessageFormat); + return new FormatException(SR.GetResourceString(_failureMessageID)); case ParseFailureKind.NativeException: - return m_innerException; + return _innerException; default: - Debug.Assert(false, "Unknown GuidParseFailure: " + m_failure); + Debug.Fail("Unknown GuidParseFailure: " + _failure); return new FormatException(SR.Format_GuidUnrecognized); } } @@ -232,20 +239,18 @@ namespace System // d is a hex digit. (That is 8 hex digits, followed by 4, then 4, then 4, // then 12) such as: "CA761232-ED42-11CE-BACD-00AA0057B223" // - public Guid(String g) + public Guid(string g) { if (g == null) { throw new ArgumentNullException(nameof(g)); } - Contract.EndContractBlock(); - this = Guid.Empty; GuidResult result = new GuidResult(); result.Init(GuidParseThrowStyle.All); if (TryParseGuid(g, GuidStyles.Any, ref result)) { - this = result.parsedGuid; + this = result._parsedGuid; } else { @@ -253,20 +258,16 @@ namespace System } } + public static Guid Parse(string input) => + Parse(input != null ? (ReadOnlySpan)input : throw new ArgumentNullException(nameof(input))); - public static Guid Parse(String input) + public static Guid Parse(ReadOnlySpan input) { - if (input == null) - { - throw new ArgumentNullException(nameof(input)); - } - Contract.EndContractBlock(); - GuidResult result = new GuidResult(); result.Init(GuidParseThrowStyle.AllButOverflow); if (TryParseGuid(input, GuidStyles.Any, ref result)) { - return result.parsedGuid; + return result._parsedGuid; } else { @@ -274,30 +275,40 @@ namespace System } } - public static bool TryParse(String input, out Guid result) + public static bool TryParse(string input, out Guid result) + { + if (input == null) + { + result = default(Guid); + return false; + } + + return TryParse((ReadOnlySpan)input, out result); + } + + public static bool TryParse(ReadOnlySpan input, out Guid result) { GuidResult parseResult = new GuidResult(); parseResult.Init(GuidParseThrowStyle.None); if (TryParseGuid(input, GuidStyles.Any, ref parseResult)) { - result = parseResult.parsedGuid; + result = parseResult._parsedGuid; return true; } else { - result = Guid.Empty; + result = default(Guid); return false; } } - public static Guid ParseExact(String input, String format) + public static Guid ParseExact(string input, string format) => + ParseExact( + input != null ? (ReadOnlySpan)input : throw new ArgumentNullException(nameof(input)), + format != null ? (ReadOnlySpan)format : throw new ArgumentNullException(nameof(format))); + + public static Guid ParseExact(ReadOnlySpan input, ReadOnlySpan format) { - if (input == null) - throw new ArgumentNullException(nameof(input)); - - if (format == null) - throw new ArgumentNullException(nameof(format)); - if (format.Length != 1) { // all acceptable format strings are of length 1 @@ -305,37 +316,37 @@ namespace System } GuidStyles style; - char formatCh = format[0]; - if (formatCh == 'D' || formatCh == 'd') + switch (format[0]) { - style = GuidStyles.DigitFormat; - } - else if (formatCh == 'N' || formatCh == 'n') - { - style = GuidStyles.NumberFormat; - } - else if (formatCh == 'B' || formatCh == 'b') - { - style = GuidStyles.BraceFormat; - } - else if (formatCh == 'P' || formatCh == 'p') - { - style = GuidStyles.ParenthesisFormat; - } - else if (formatCh == 'X' || formatCh == 'x') - { - style = GuidStyles.HexFormat; - } - else - { - throw new FormatException(SR.Format_InvalidGuidFormatSpecification); + case 'D': + case 'd': + style = GuidStyles.DigitFormat; + break; + case 'N': + case 'n': + style = GuidStyles.NumberFormat; + break; + case 'B': + case 'b': + style = GuidStyles.BraceFormat; + break; + case 'P': + case 'p': + style = GuidStyles.ParenthesisFormat; + break; + case 'X': + case 'x': + style = GuidStyles.HexFormat; + break; + default: + throw new FormatException(SR.Format_InvalidGuidFormatSpecification); } GuidResult result = new GuidResult(); result.Init(GuidParseThrowStyle.AllButOverflow); if (TryParseGuid(input, style, ref result)) { - return result.parsedGuid; + return result._parsedGuid; } else { @@ -343,83 +354,87 @@ namespace System } } - public static bool TryParseExact(String input, String format, out Guid result) + public static bool TryParseExact(string input, string format, out Guid result) { - if (format == null || format.Length != 1) + if (input == null) { - result = Guid.Empty; + result = default(Guid); + return false; + } + + return TryParseExact((ReadOnlySpan)input, format, out result); + } + + public static bool TryParseExact(ReadOnlySpan input, ReadOnlySpan format, out Guid result) + { + if (format.Length != 1) + { + result = default(Guid); return false; } GuidStyles style; - char formatCh = format[0]; - - if (formatCh == 'D' || formatCh == 'd') + switch (format[0]) { - style = GuidStyles.DigitFormat; - } - else if (formatCh == 'N' || formatCh == 'n') - { - style = GuidStyles.NumberFormat; - } - else if (formatCh == 'B' || formatCh == 'b') - { - style = GuidStyles.BraceFormat; - } - else if (formatCh == 'P' || formatCh == 'p') - { - style = GuidStyles.ParenthesisFormat; - } - else if (formatCh == 'X' || formatCh == 'x') - { - style = GuidStyles.HexFormat; - } - else - { - // invalid guid format specification - result = Guid.Empty; - return false; + case 'D': + case 'd': + style = GuidStyles.DigitFormat; + break; + case 'N': + case 'n': + style = GuidStyles.NumberFormat; + break; + case 'B': + case 'b': + style = GuidStyles.BraceFormat; + break; + case 'P': + case 'p': + style = GuidStyles.ParenthesisFormat; + break; + case 'X': + case 'x': + style = GuidStyles.HexFormat; + break; + default: + // invalid guid format specification + result = default(Guid); + return false; } GuidResult parseResult = new GuidResult(); parseResult.Init(GuidParseThrowStyle.None); if (TryParseGuid(input, style, ref parseResult)) { - result = parseResult.parsedGuid; + result = parseResult._parsedGuid; return true; } else { - result = Guid.Empty; + result = default(Guid); return false; } } - - private static bool TryParseGuid(String g, GuidStyles flags, ref GuidResult result) + private static bool TryParseGuid(ReadOnlySpan guidString, GuidStyles flags, ref GuidResult result) { - if (g == null) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidUnrecognized); - return false; - } - String guidString = g.Trim(); //Remove Whitespace + guidString = guidString.Trim(); // Remove whitespace from beginning and end if (guidString.Length == 0) { - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidUnrecognized); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); return false; } // Check for dashes - bool dashesExistInString = (guidString.IndexOf('-', 0) >= 0); + bool dashesExistInString = guidString.IndexOf('-') >= 0; if (dashesExistInString) { if ((flags & (GuidStyles.AllowDashes | GuidStyles.RequireDashes)) == 0) { // dashes are not allowed - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidUnrecognized); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); return false; } } @@ -428,7 +443,7 @@ namespace System if ((flags & GuidStyles.RequireDashes) != 0) { // dashes are required - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidUnrecognized); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); return false; } } @@ -441,7 +456,7 @@ namespace System if ((flags & (GuidStyles.AllowBraces | GuidStyles.RequireBraces)) == 0) { // braces are not allowed - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidUnrecognized); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); return false; } } @@ -450,7 +465,7 @@ namespace System if ((flags & GuidStyles.RequireBraces) != 0) { // braces are required - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidUnrecognized); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); return false; } } @@ -463,7 +478,7 @@ namespace System if ((flags & (GuidStyles.AllowParenthesis | GuidStyles.RequireParenthesis)) == 0) { // parenthesis are not allowed - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidUnrecognized); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); return false; } } @@ -472,7 +487,7 @@ namespace System if ((flags & GuidStyles.RequireParenthesis) != 0) { // parenthesis are required - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidUnrecognized); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); return false; } } @@ -498,19 +513,18 @@ namespace System } catch (IndexOutOfRangeException ex) { - result.SetFailure(ParseFailureKind.FormatWithInnerException, SR.Format_GuidUnrecognized, null, null, ex); + result.SetFailure(ParseFailureKind.FormatWithInnerException, nameof(SR.Format_GuidUnrecognized), null, null, ex); return false; } catch (ArgumentException ex) { - result.SetFailure(ParseFailureKind.FormatWithInnerException, SR.Format_GuidUnrecognized, null, null, ex); + result.SetFailure(ParseFailureKind.FormatWithInnerException, nameof(SR.Format_GuidUnrecognized), null, null, ex); return false; } } - // Check if it's of the form {0xdddddddd,0xdddd,0xdddd,{0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd}} - private static bool TryParseGuidWithHexPrefix(String guidString, ref GuidResult result) + private static bool TryParseGuidWithHexPrefix(ReadOnlySpan guidString, ref GuidResult result) { int numStart = 0; int numLen = 0; @@ -519,16 +533,16 @@ namespace System guidString = EatAllWhitespace(guidString); // Check for leading '{' - if (String.IsNullOrEmpty(guidString) || guidString[0] != '{') + if (guidString.Length == 0 || guidString[0] != '{') { - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidBrace); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidBrace)); return false; } // Check for '0x' if (!IsHexPrefix(guidString, 1)) { - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidHexPrefix, "{0xdddddddd, etc}"); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidHexPrefix), "{0xdddddddd, etc}"); return false; } @@ -537,18 +551,17 @@ namespace System numLen = guidString.IndexOf(',', numStart) - numStart; if (numLen <= 0) { - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidComma); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma)); return false; } - - if (!StringToInt(guidString.Substring(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result.parsedGuid._a, ref result)) + if (!StringToInt(guidString.Slice(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._a, ref result)) return false; // Check for '0x' if (!IsHexPrefix(guidString, numStart + numLen + 1)) { - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidHexPrefix, "{0xdddddddd, 0xdddd, etc}"); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidHexPrefix), "{0xdddddddd, 0xdddd, etc}"); return false; } // +3 to get by ',0x' @@ -556,17 +569,17 @@ namespace System numLen = guidString.IndexOf(',', numStart) - numStart; if (numLen <= 0) { - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidComma); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma)); return false; } // Read in the number - if (!StringToShort(guidString.Substring(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result.parsedGuid._b, ref result)) + if (!StringToShort(guidString.Slice(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._b, ref result)) return false; // Check for '0x' if (!IsHexPrefix(guidString, numStart + numLen + 1)) { - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidHexPrefix, "{0xdddddddd, 0xdddd, 0xdddd, etc}"); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidHexPrefix), "{0xdddddddd, 0xdddd, 0xdddd, etc}"); return false; } // +3 to get by ',0x' @@ -574,31 +587,31 @@ namespace System numLen = guidString.IndexOf(',', numStart) - numStart; if (numLen <= 0) { - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidComma); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma)); return false; } // Read in the number - if (!StringToShort(guidString.Substring(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result.parsedGuid._c, ref result)) + if (!StringToShort(guidString.Slice(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._c, ref result)) return false; // Check for '{' if (guidString.Length <= numStart + numLen + 1 || guidString[numStart + numLen + 1] != '{') { - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidBrace); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidBrace)); return false; } // Prepare for loop numLen++; - byte[] bytes = new byte[8]; + Span bytes = stackalloc byte[8]; - for (int i = 0; i < 8; i++) + for (int i = 0; i < bytes.Length; i++) { // Check for '0x' if (!IsHexPrefix(guidString, numStart + numLen + 1)) { - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidHexPrefix, "{... { ... 0xdd, ...}}"); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidHexPrefix), "{... { ... 0xdd, ...}}"); return false; } @@ -611,7 +624,7 @@ namespace System numLen = guidString.IndexOf(',', numStart) - numStart; if (numLen <= 0) { - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidComma); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma)); return false; } } @@ -620,42 +633,48 @@ namespace System numLen = guidString.IndexOf('}', numStart) - numStart; if (numLen <= 0) { - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidBraceAfterLastNumber); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidBraceAfterLastNumber)); return false; } } // Read in the number - uint number = (uint)ParseNumbers.StringToInt(guidString.Substring(numStart, numLen), 16, ParseNumbers.IsTight); + int signedNumber; + if (!StringToInt(guidString.Slice(numStart, numLen), -1, ParseNumbers.IsTight, out signedNumber, ref result)) + { + return false; + } + uint number = (uint)signedNumber; + // check for overflow if (number > 255) { - result.SetFailure(ParseFailureKind.Format, SR.Overflow_Byte); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Overflow_Byte)); return false; } bytes[i] = (byte)number; } - result.parsedGuid._d = bytes[0]; - result.parsedGuid._e = bytes[1]; - result.parsedGuid._f = bytes[2]; - result.parsedGuid._g = bytes[3]; - result.parsedGuid._h = bytes[4]; - result.parsedGuid._i = bytes[5]; - result.parsedGuid._j = bytes[6]; - result.parsedGuid._k = bytes[7]; + result._parsedGuid._d = bytes[0]; + result._parsedGuid._e = bytes[1]; + result._parsedGuid._f = bytes[2]; + result._parsedGuid._g = bytes[3]; + result._parsedGuid._h = bytes[4]; + result._parsedGuid._i = bytes[5]; + result._parsedGuid._j = bytes[6]; + result._parsedGuid._k = bytes[7]; // Check for last '}' if (numStart + numLen + 1 >= guidString.Length || guidString[numStart + numLen + 1] != '}') { - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidEndBrace); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidEndBrace)); return false; } // Check if we have extra characters at the end if (numStart + numLen + 1 != guidString.Length - 1) { - result.SetFailure(ParseFailureKind.Format, SR.Format_ExtraJunkAtEnd); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_ExtraJunkAtEnd)); return false; } @@ -663,7 +682,7 @@ namespace System } // Check if it's of the form dddddddddddddddddddddddddddddddd - private static bool TryParseGuidWithNoStyle(String guidString, ref GuidResult result) + private static bool TryParseGuidWithNoStyle(ReadOnlySpan guidString, ref GuidResult result) { int startPos = 0; int temp; @@ -672,7 +691,7 @@ namespace System if (guidString.Length != 32) { - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidInvLen); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); return false; } @@ -685,30 +704,30 @@ namespace System } else { - char upperCaseCh = Char.ToUpperInvariant(ch); + char upperCaseCh = char.ToUpperInvariant(ch); if (upperCaseCh >= 'A' && upperCaseCh <= 'F') { continue; } } - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidInvalidChar); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvalidChar)); return false; } - if (!StringToInt(guidString.Substring(startPos, 8) /*first DWORD*/, -1, ParseNumbers.IsTight, out result.parsedGuid._a, ref result)) + if (!StringToInt(guidString.Slice(startPos, 8) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._a, ref result)) return false; startPos += 8; - if (!StringToShort(guidString.Substring(startPos, 4), -1, ParseNumbers.IsTight, out result.parsedGuid._b, ref result)) + if (!StringToShort(guidString.Slice(startPos, 4), -1, ParseNumbers.IsTight, out result._parsedGuid._b, ref result)) return false; startPos += 4; - if (!StringToShort(guidString.Substring(startPos, 4), -1, ParseNumbers.IsTight, out result.parsedGuid._c, ref result)) + if (!StringToShort(guidString.Slice(startPos, 4), -1, ParseNumbers.IsTight, out result._parsedGuid._c, ref result)) return false; startPos += 4; - if (!StringToInt(guidString.Substring(startPos, 4), -1, ParseNumbers.IsTight, out temp, ref result)) + if (!StringToInt(guidString.Slice(startPos, 4), -1, ParseNumbers.IsTight, out temp, ref result)) return false; startPos += 4; @@ -719,27 +738,26 @@ namespace System if (currentPos - startPos != 12) { - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidInvLen); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); return false; } - result.parsedGuid._d = (byte)(temp >> 8); - result.parsedGuid._e = (byte)(temp); + result._parsedGuid._d = (byte)(temp >> 8); + result._parsedGuid._e = (byte)(temp); temp = (int)(templ >> 32); - result.parsedGuid._f = (byte)(temp >> 8); - result.parsedGuid._g = (byte)(temp); + result._parsedGuid._f = (byte)(temp >> 8); + result._parsedGuid._g = (byte)(temp); temp = (int)(templ); - result.parsedGuid._h = (byte)(temp >> 24); - result.parsedGuid._i = (byte)(temp >> 16); - result.parsedGuid._j = (byte)(temp >> 8); - result.parsedGuid._k = (byte)(temp); + result._parsedGuid._h = (byte)(temp >> 24); + result._parsedGuid._i = (byte)(temp >> 16); + result._parsedGuid._j = (byte)(temp >> 8); + result._parsedGuid._k = (byte)(temp); return true; } - // Check if it's of the form [{|(]dddddddd-dddd-dddd-dddd-dddddddddddd[}|)] - private static bool TryParseGuidWithDashes(String guidString, ref GuidResult result) + private static bool TryParseGuidWithDashes(ReadOnlySpan guidString, ref GuidResult result) { int startPos = 0; int temp; @@ -751,7 +769,7 @@ namespace System { if (guidString.Length != 38 || guidString[37] != '}') { - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidInvLen); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); return false; } startPos = 1; @@ -760,14 +778,14 @@ namespace System { if (guidString.Length != 38 || guidString[37] != ')') { - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidInvLen); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); return false; } startPos = 1; } else if (guidString.Length != 36) { - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidInvLen); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); return false; } @@ -776,24 +794,24 @@ namespace System guidString[18 + startPos] != '-' || guidString[23 + startPos] != '-') { - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidDashes); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidDashes)); return false; } currentPos = startPos; if (!StringToInt(guidString, ref currentPos, 8, ParseNumbers.NoSpace, out temp, ref result)) return false; - result.parsedGuid._a = temp; + result._parsedGuid._a = temp; ++currentPos; //Increment past the '-'; if (!StringToInt(guidString, ref currentPos, 4, ParseNumbers.NoSpace, out temp, ref result)) return false; - result.parsedGuid._b = (short)temp; + result._parsedGuid._b = (short)temp; ++currentPos; //Increment past the '-'; if (!StringToInt(guidString, ref currentPos, 4, ParseNumbers.NoSpace, out temp, ref result)) return false; - result.parsedGuid._c = (short)temp; + result._parsedGuid._c = (short)temp; ++currentPos; //Increment past the '-'; if (!StringToInt(guidString, ref currentPos, 4, ParseNumbers.NoSpace, out temp, ref result)) @@ -806,35 +824,30 @@ namespace System if (currentPos - startPos != 12) { - result.SetFailure(ParseFailureKind.Format, SR.Format_GuidInvLen); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); return false; } - result.parsedGuid._d = (byte)(temp >> 8); - result.parsedGuid._e = (byte)(temp); + result._parsedGuid._d = (byte)(temp >> 8); + result._parsedGuid._e = (byte)(temp); temp = (int)(templ >> 32); - result.parsedGuid._f = (byte)(temp >> 8); - result.parsedGuid._g = (byte)(temp); + result._parsedGuid._f = (byte)(temp >> 8); + result._parsedGuid._g = (byte)(temp); temp = (int)(templ); - result.parsedGuid._h = (byte)(temp >> 24); - result.parsedGuid._i = (byte)(temp >> 16); - result.parsedGuid._j = (byte)(temp >> 8); - result.parsedGuid._k = (byte)(temp); + result._parsedGuid._h = (byte)(temp >> 24); + result._parsedGuid._i = (byte)(temp >> 16); + result._parsedGuid._j = (byte)(temp >> 8); + result._parsedGuid._k = (byte)(temp); return true; } - - // - // StringToShort, StringToInt, and StringToLong are wrappers around COMUtilNative integer parsing routines; - // - - private static bool StringToShort(String str, int requiredLength, int flags, out short result, ref GuidResult parseResult) + private static bool StringToShort(ReadOnlySpan str, int requiredLength, int flags, out short result, ref GuidResult parseResult) { int parsePos = 0; return StringToShort(str, ref parsePos, requiredLength, flags, out result, ref parseResult); } - private static bool StringToShort(String str, ref int parsePos, int requiredLength, int flags, out short result, ref GuidResult parseResult) + private static bool StringToShort(ReadOnlySpan str, ref int parsePos, int requiredLength, int flags, out short result, ref GuidResult parseResult) { result = 0; int x; @@ -843,14 +856,13 @@ namespace System return retValue; } - - private static bool StringToInt(String str, int requiredLength, int flags, out int result, ref GuidResult parseResult) + private static bool StringToInt(ReadOnlySpan str, int requiredLength, int flags, out int result, ref GuidResult parseResult) { int parsePos = 0; return StringToInt(str, ref parsePos, requiredLength, flags, out result, ref parseResult); } - private static bool StringToInt(String str, ref int parsePos, int requiredLength, int flags, out int result, ref GuidResult parseResult) + private static bool StringToInt(ReadOnlySpan str, ref int parsePos, int requiredLength, int flags, out int result, ref GuidResult parseResult) { result = 0; @@ -861,11 +873,11 @@ namespace System } catch (OverflowException ex) { - if (parseResult.throwStyle == GuidParseThrowStyle.All) + if (parseResult._throwStyle == GuidParseThrowStyle.All) { throw; } - else if (parseResult.throwStyle == GuidParseThrowStyle.AllButOverflow) + else if (parseResult._throwStyle == GuidParseThrowStyle.AllButOverflow) { throw new FormatException(SR.Format_GuidUnrecognized, ex); } @@ -877,7 +889,7 @@ namespace System } catch (Exception ex) { - if (parseResult.throwStyle == GuidParseThrowStyle.None) + if (parseResult._throwStyle == GuidParseThrowStyle.None) { parseResult.SetFailure(ex); return false; @@ -891,13 +903,13 @@ namespace System //If we didn't parse enough characters, there's clearly an error. if (requiredLength != -1 && parsePos - currStart != requiredLength) { - parseResult.SetFailure(ParseFailureKind.Format, SR.Format_GuidInvalidChar); + parseResult.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvalidChar)); return false; } return true; } - private static bool StringToLong(String str, ref int parsePos, int flags, out long result, ref GuidResult parseResult) + private static unsafe bool StringToLong(ReadOnlySpan str, ref int parsePos, int flags, out long result, ref GuidResult parseResult) { result = 0; @@ -907,11 +919,11 @@ namespace System } catch (OverflowException ex) { - if (parseResult.throwStyle == GuidParseThrowStyle.All) + if (parseResult._throwStyle == GuidParseThrowStyle.All) { throw; } - else if (parseResult.throwStyle == GuidParseThrowStyle.AllButOverflow) + else if (parseResult._throwStyle == GuidParseThrowStyle.AllButOverflow) { throw new FormatException(SR.Format_GuidUnrecognized, ex); } @@ -923,7 +935,7 @@ namespace System } catch (Exception ex) { - if (parseResult.throwStyle == GuidParseThrowStyle.None) + if (parseResult._throwStyle == GuidParseThrowStyle.None) { parseResult.SetFailure(ex); return false; @@ -936,78 +948,98 @@ namespace System return true; } - - private static String EatAllWhitespace(String str) + private static ReadOnlySpan EatAllWhitespace(ReadOnlySpan str) { - int newLength = 0; - char[] chArr = new char[str.Length]; - char curChar; - - // Now get each char from str and if it is not whitespace add it to chArr - for (int i = 0; i < str.Length; i++) + // Find the first whitespace character. If there is none, just return the input. + int i; + for (i = 0; i < str.Length && !char.IsWhiteSpace(str[i]); i++) ; + if (i == str.Length) { - curChar = str[i]; - if (!Char.IsWhiteSpace(curChar)) + return str; + } + + // There was at least one whitespace. Copy over everything prior to it to a new array. + var chArr = new char[str.Length]; + int newLength = 0; + if (i > 0) + { + newLength = i; + str.Slice(0, i).CopyTo(chArr); + } + + // Loop through the remaining chars, copying over non-whitespace. + for (; i < str.Length; i++) + { + char c = str[i]; + if (!char.IsWhiteSpace(c)) { - chArr[newLength++] = curChar; + chArr[newLength++] = c; } } - // Return a new string based on chArr - return new String(chArr, 0, newLength); + // Return the string with the whitespace removed. + return new ReadOnlySpan(chArr, 0, newLength); } - private static bool IsHexPrefix(String str, int i) + private static bool IsHexPrefix(ReadOnlySpan str, int i) => + i + 1 < str.Length && + str[i] == '0' && + (str[i + 1] == 'x' || char.ToLowerInvariant(str[i + 1]) == 'x'); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteByteHelper(Span destination) { - if (str.Length > i + 1 && str[i] == '0' && (Char.ToLowerInvariant(str[i + 1]) == 'x')) - return true; - else - return false; + destination[0] = (byte)(_a); + destination[1] = (byte)(_a >> 8); + destination[2] = (byte)(_a >> 16); + destination[3] = (byte)(_a >> 24); + destination[4] = (byte)(_b); + destination[5] = (byte)(_b >> 8); + destination[6] = (byte)(_c); + destination[7] = (byte)(_c >> 8); + destination[8] = _d; + destination[9] = _e; + destination[10] = _f; + destination[11] = _g; + destination[12] = _h; + destination[13] = _i; + destination[14] = _j; + destination[15] = _k; } - // Returns an unsigned byte array containing the GUID. public byte[] ToByteArray() { - byte[] g = new byte[16]; - - g[0] = (byte)(_a); - g[1] = (byte)(_a >> 8); - g[2] = (byte)(_a >> 16); - g[3] = (byte)(_a >> 24); - g[4] = (byte)(_b); - g[5] = (byte)(_b >> 8); - g[6] = (byte)(_c); - g[7] = (byte)(_c >> 8); - g[8] = _d; - g[9] = _e; - g[10] = _f; - g[11] = _g; - g[12] = _h; - g[13] = _i; - g[14] = _j; - g[15] = _k; - + var g = new byte[16]; + WriteByteHelper(g); return g; } + // Returns whether bytes are sucessfully written to given span. + public bool TryWriteBytes(Span destination) + { + if (destination.Length < 16) + return false; + + WriteByteHelper(destination); + return true; + } // Returns the guid in "registry" format. - public override String ToString() + public override string ToString() { return ToString("D", null); } - public unsafe override int GetHashCode() + public override int GetHashCode() { // Simply XOR all the bits of the GUID 32 bits at a time. - fixed (int* ptr = &_a) - return ptr[0] ^ ptr[1] ^ ptr[2] ^ ptr[3]; + return _a ^ Unsafe.Add(ref _a, 1) ^ Unsafe.Add(ref _a, 2) ^ Unsafe.Add(ref _a, 3); } // Returns true if and only if the guid represented // by o is the same as this instance. - public override bool Equals(Object o) + public override bool Equals(object o) { Guid g; // Check that o is a Guid first @@ -1016,88 +1048,19 @@ namespace System else g = (Guid)o; // Now compare each of the elements - if (g._a != _a) - return false; - if (g._b != _b) - return false; - if (g._c != _c) - return false; - if (g._d != _d) - return false; - if (g._e != _e) - return false; - if (g._f != _f) - return false; - if (g._g != _g) - return false; - if (g._h != _h) - return false; - if (g._i != _i) - return false; - if (g._j != _j) - return false; - if (g._k != _k) - return false; - - return true; + return g._a == _a && + Unsafe.Add(ref g._a, 1) == Unsafe.Add(ref _a, 1) && + Unsafe.Add(ref g._a, 2) == Unsafe.Add(ref _a, 2) && + Unsafe.Add(ref g._a, 3) == Unsafe.Add(ref _a, 3); } public bool Equals(Guid g) { // Now compare each of the elements - if (g._a != _a) - return false; - if (g._b != _b) - return false; - if (g._c != _c) - return false; - if (g._d != _d) - return false; - if (g._e != _e) - return false; - if (g._f != _f) - return false; - if (g._g != _g) - return false; - if (g._h != _h) - return false; - if (g._i != _i) - return false; - if (g._j != _j) - return false; - if (g._k != _k) - return false; - - return true; - } - - internal bool Equals(ref Guid g) - { - // Now compare each of the elements - if (g._a != _a) - return false; - if (g._b != _b) - return false; - if (g._c != _c) - return false; - if (g._d != _d) - return false; - if (g._e != _e) - return false; - if (g._f != _f) - return false; - if (g._g != _g) - return false; - if (g._h != _h) - return false; - if (g._i != _i) - return false; - if (g._j != _j) - return false; - if (g._k != _k) - return false; - - return true; + return g._a == _a && + Unsafe.Add(ref g._a, 1) == Unsafe.Add(ref _a, 1) && + Unsafe.Add(ref g._a, 2) == Unsafe.Add(ref _a, 2) && + Unsafe.Add(ref g._a, 3) == Unsafe.Add(ref _a, 3); } private int GetResult(uint me, uint them) @@ -1109,7 +1072,7 @@ namespace System return 1; } - public int CompareTo(Object value) + public int CompareTo(object value) { if (value == null) { @@ -1138,42 +1101,42 @@ namespace System if (g._d != _d) { - return GetResult((uint)_d, (uint)g._d); + return GetResult(_d, g._d); } if (g._e != _e) { - return GetResult((uint)_e, (uint)g._e); + return GetResult(_e, g._e); } if (g._f != _f) { - return GetResult((uint)_f, (uint)g._f); + return GetResult(_f, g._f); } if (g._g != _g) { - return GetResult((uint)_g, (uint)g._g); + return GetResult(_g, g._g); } if (g._h != _h) { - return GetResult((uint)_h, (uint)g._h); + return GetResult(_h, g._h); } if (g._i != _i) { - return GetResult((uint)_i, (uint)g._i); + return GetResult(_i, g._i); } if (g._j != _j) { - return GetResult((uint)_j, (uint)g._j); + return GetResult(_j, g._j); } if (g._k != _k) { - return GetResult((uint)_k, (uint)g._k); + return GetResult(_k, g._k); } return 0; @@ -1198,42 +1161,42 @@ namespace System if (value._d != _d) { - return GetResult((uint)_d, (uint)value._d); + return GetResult(_d, value._d); } if (value._e != _e) { - return GetResult((uint)_e, (uint)value._e); + return GetResult(_e, value._e); } if (value._f != _f) { - return GetResult((uint)_f, (uint)value._f); + return GetResult(_f, value._f); } if (value._g != _g) { - return GetResult((uint)_g, (uint)value._g); + return GetResult(_g, value._g); } if (value._h != _h) { - return GetResult((uint)_h, (uint)value._h); + return GetResult(_h, value._h); } if (value._i != _i) { - return GetResult((uint)_i, (uint)value._i); + return GetResult(_i, value._i); } if (value._j != _j) { - return GetResult((uint)_j, (uint)value._j); + return GetResult(_j, value._j); } if (value._k != _k) { - return GetResult((uint)_k, (uint)value._k); + return GetResult(_k, value._k); } return 0; @@ -1242,193 +1205,232 @@ namespace System public static bool operator ==(Guid a, Guid b) { // Now compare each of the elements - if (a._a != b._a) - return false; - if (a._b != b._b) - return false; - if (a._c != b._c) - return false; - if (a._d != b._d) - return false; - if (a._e != b._e) - return false; - if (a._f != b._f) - return false; - if (a._g != b._g) - return false; - if (a._h != b._h) - return false; - if (a._i != b._i) - return false; - if (a._j != b._j) - return false; - if (a._k != b._k) - return false; - - return true; + return a._a == b._a && + Unsafe.Add(ref a._a, 1) == Unsafe.Add(ref b._a, 1) && + Unsafe.Add(ref a._a, 2) == Unsafe.Add(ref b._a, 2) && + Unsafe.Add(ref a._a, 3) == Unsafe.Add(ref b._a, 3); } public static bool operator !=(Guid a, Guid b) { - return !(a == b); + // Now compare each of the elements + return a._a != b._a || + Unsafe.Add(ref a._a, 1) != Unsafe.Add(ref b._a, 1) || + Unsafe.Add(ref a._a, 2) != Unsafe.Add(ref b._a, 2) || + Unsafe.Add(ref a._a, 3) != Unsafe.Add(ref b._a, 3); } - public String ToString(String format) + public string ToString(string format) { return ToString(format, null); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static char HexToChar(int a) { a = a & 0xf; return (char)((a > 9) ? a - 10 + 0x61 : a + 0x30); } - unsafe private static int HexsToChars(char* guidChars, int offset, int a, int b) + unsafe private static int HexsToChars(char* guidChars, int a, int b) { - return HexsToChars(guidChars, offset, a, b, false); + guidChars[0] = HexToChar(a >> 4); + guidChars[1] = HexToChar(a); + + guidChars[2] = HexToChar(b >> 4); + guidChars[3] = HexToChar(b); + + return 4; } - unsafe private static int HexsToChars(char* guidChars, int offset, int a, int b, bool hex) + unsafe private static int HexsToCharsHexOutput(char* guidChars, int a, int b) { - if (hex) - { - guidChars[offset++] = '0'; - guidChars[offset++] = 'x'; - } - guidChars[offset++] = HexToChar(a >> 4); - guidChars[offset++] = HexToChar(a); - if (hex) - { - guidChars[offset++] = ','; - guidChars[offset++] = '0'; - guidChars[offset++] = 'x'; - } - guidChars[offset++] = HexToChar(b >> 4); - guidChars[offset++] = HexToChar(b); - return offset; + guidChars[0] = '0'; + guidChars[1] = 'x'; + + guidChars[2] = HexToChar(a >> 4); + guidChars[3] = HexToChar(a); + + guidChars[4] = ','; + guidChars[5] = '0'; + guidChars[6] = 'x'; + + guidChars[7] = HexToChar(b >> 4); + guidChars[8] = HexToChar(b); + + return 9; } // IFormattable interface // We currently ignore provider - public String ToString(String format, IFormatProvider provider) + public string ToString(string format, IFormatProvider provider) { if (format == null || format.Length == 0) format = "D"; - string guidString; - int offset = 0; + // all acceptable format strings are of length 1 + if (format.Length != 1) + throw new FormatException(SR.Format_InvalidGuidFormatSpecification); + + int guidSize; + switch (format[0]) + { + case 'D': + case 'd': + guidSize = 36; + break; + case 'N': + case 'n': + guidSize = 32; + break; + case 'B': + case 'b': + case 'P': + case 'p': + guidSize = 38; + break; + case 'X': + case 'x': + guidSize = 68; + break; + default: + throw new FormatException(SR.Format_InvalidGuidFormatSpecification); + } + + string guidString = string.FastAllocateString(guidSize); + + int bytesWritten; + bool result = TryFormat(new Span(ref guidString.GetRawStringData(), guidString.Length), out bytesWritten, format); + Debug.Assert(result && bytesWritten == guidString.Length, "Formatting guid should have succeeded."); + + return guidString; + } + + // Returns whether the guid is successfully formatted as a span. + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default) + { + if (format.Length == 0) + format = "D"; + + // all acceptable format strings are of length 1 + if (format.Length != 1) + throw new FormatException(SR.Format_InvalidGuidFormatSpecification); + bool dash = true; bool hex = false; + int braces = 0; - if (format.Length != 1) + int guidSize; + + switch (format[0]) { - // all acceptable format strings are of length 1 - throw new FormatException(SR.Format_InvalidGuidFormatSpecification); + case 'D': + case 'd': + guidSize = 36; + break; + case 'N': + case 'n': + dash = false; + guidSize = 32; + break; + case 'B': + case 'b': + braces = '{' + ('}' << 16); + guidSize = 38; + break; + case 'P': + case 'p': + braces = '(' + (')' << 16); + guidSize = 38; + break; + case 'X': + case 'x': + braces = '{' + ('}' << 16); + dash = false; + hex = true; + guidSize = 68; + break; + default: + throw new FormatException(SR.Format_InvalidGuidFormatSpecification); } - char formatCh = format[0]; - if (formatCh == 'D' || formatCh == 'd') + if (destination.Length < guidSize) { - guidString = string.FastAllocateString(36); - } - else if (formatCh == 'N' || formatCh == 'n') - { - guidString = string.FastAllocateString(32); - dash = false; - } - else if (formatCh == 'B' || formatCh == 'b') - { - guidString = string.FastAllocateString(38); - unsafe - { - fixed (char* guidChars = guidString) - { - guidChars[offset++] = '{'; - guidChars[37] = '}'; - } - } - } - else if (formatCh == 'P' || formatCh == 'p') - { - guidString = string.FastAllocateString(38); - unsafe - { - fixed (char* guidChars = guidString) - { - guidChars[offset++] = '('; - guidChars[37] = ')'; - } - } - } - else if (formatCh == 'X' || formatCh == 'x') - { - guidString = string.FastAllocateString(68); - unsafe - { - fixed (char* guidChars = guidString) - { - guidChars[offset++] = '{'; - guidChars[67] = '}'; - } - } - dash = false; - hex = true; - } - else - { - throw new FormatException(SR.Format_InvalidGuidFormatSpecification); + charsWritten = 0; + return false; } unsafe { - fixed (char* guidChars = guidString) + fixed (char* guidChars = &MemoryMarshal.GetReference(destination)) { + char * p = guidChars; + + if (braces != 0) + *p++ = (char)braces; + if (hex) { // {0xdddddddd,0xdddd,0xdddd,{0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd}} - guidChars[offset++] = '0'; - guidChars[offset++] = 'x'; - offset = HexsToChars(guidChars, offset, _a >> 24, _a >> 16); - offset = HexsToChars(guidChars, offset, _a >> 8, _a); - guidChars[offset++] = ','; - guidChars[offset++] = '0'; - guidChars[offset++] = 'x'; - offset = HexsToChars(guidChars, offset, _b >> 8, _b); - guidChars[offset++] = ','; - guidChars[offset++] = '0'; - guidChars[offset++] = 'x'; - offset = HexsToChars(guidChars, offset, _c >> 8, _c); - guidChars[offset++] = ','; - guidChars[offset++] = '{'; - offset = HexsToChars(guidChars, offset, _d, _e, true); - guidChars[offset++] = ','; - offset = HexsToChars(guidChars, offset, _f, _g, true); - guidChars[offset++] = ','; - offset = HexsToChars(guidChars, offset, _h, _i, true); - guidChars[offset++] = ','; - offset = HexsToChars(guidChars, offset, _j, _k, true); - guidChars[offset++] = '}'; + *p++ = '0'; + *p++ = 'x'; + p += HexsToChars(p, _a >> 24, _a >> 16); + p += HexsToChars(p, _a >> 8, _a); + *p++ = ','; + *p++ = '0'; + *p++ = 'x'; + p += HexsToChars(p, _b >> 8, _b); + *p++ = ','; + *p++ = '0'; + *p++ = 'x'; + p += HexsToChars(p, _c >> 8, _c); + *p++ = ','; + *p++ = '{'; + p += HexsToCharsHexOutput(p, _d, _e); + *p++ = ','; + p += HexsToCharsHexOutput(p, _f, _g); + *p++ = ','; + p += HexsToCharsHexOutput(p, _h, _i); + *p++ = ','; + p += HexsToCharsHexOutput(p, _j, _k); + *p++ = '}'; } else { // [{|(]dddddddd[-]dddd[-]dddd[-]dddd[-]dddddddddddd[}|)] - offset = HexsToChars(guidChars, offset, _a >> 24, _a >> 16); - offset = HexsToChars(guidChars, offset, _a >> 8, _a); - if (dash) guidChars[offset++] = '-'; - offset = HexsToChars(guidChars, offset, _b >> 8, _b); - if (dash) guidChars[offset++] = '-'; - offset = HexsToChars(guidChars, offset, _c >> 8, _c); - if (dash) guidChars[offset++] = '-'; - offset = HexsToChars(guidChars, offset, _d, _e); - if (dash) guidChars[offset++] = '-'; - offset = HexsToChars(guidChars, offset, _f, _g); - offset = HexsToChars(guidChars, offset, _h, _i); - offset = HexsToChars(guidChars, offset, _j, _k); + p += HexsToChars(p, _a >> 24, _a >> 16); + p += HexsToChars(p, _a >> 8, _a); + if (dash) + *p++ = '-'; + p += HexsToChars(p, _b >> 8, _b); + if (dash) + *p++ = '-'; + p += HexsToChars(p, _c >> 8, _c); + if (dash) + *p++ = '-'; + p += HexsToChars(p, _d, _e); + if (dash) + *p++ = '-'; + p += HexsToChars(p, _f, _g); + p += HexsToChars(p, _h, _i); + p += HexsToChars(p, _j, _k); } + + if (braces != 0) + *p++ = (char)(braces >> 16); + + Debug.Assert(p - guidChars == guidSize); } } - return guidString; + + charsWritten = guidSize; + return true; + } + + bool ISpanFormattable.TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider provider) + { + // Like with the IFormattable implementation, provider is ignored. + return TryFormat(destination, out charsWritten, format); } } } diff --git a/external/corefx/src/Common/src/CoreLib/System/HResults.cs b/external/corefx/src/Common/src/CoreLib/System/HResults.cs new file mode 100644 index 0000000000..ffc47d85bf --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/HResults.cs @@ -0,0 +1,126 @@ +// 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. + +//============================================================================= +// +// +// Purpose: Define HResult constants. Every exception has one of these. +// +// +//===========================================================================*/ +// Note: FACILITY_URT is defined as 0x13 (0x8013xxxx). Within that +// range, 0x1yyy is for Runtime errors (used for Security, Metadata, etc). +// In that subrange, 0x15zz and 0x16zz have been allocated for classlib-type +// HResults. Also note that some of our HResults have to map to certain +// COM HR's, etc. + +// Reflection will use 0x1600 -> 0x161f. IO will use 0x1620 -> 0x163f. +// Security will use 0x1640 -> 0x165f + + +using System; + +namespace System +{ + internal static partial class HResults + { + internal const int COR_E_ABANDONEDMUTEX = unchecked((int)0x8013152D); + internal const int COR_E_AMBIGUOUSMATCH = unchecked((int)0x8000211D); + internal const int COR_E_APPDOMAINUNLOADED = unchecked((int)0x80131014); + internal const int COR_E_APPLICATION = unchecked((int)0x80131600); + internal const int COR_E_ARGUMENT = unchecked((int)0x80070057); + internal const int COR_E_ARGUMENTOUTOFRANGE = unchecked((int)0x80131502); + internal const int COR_E_ARITHMETIC = unchecked((int)0x80070216); + internal const int COR_E_ARRAYTYPEMISMATCH = unchecked((int)0x80131503); + internal const int COR_E_BADEXEFORMAT = unchecked((int)0x800700C1); + internal const int COR_E_BADIMAGEFORMAT = unchecked((int)0x8007000B); + internal const int COR_E_CANNOTUNLOADAPPDOMAIN = unchecked((int)0x80131015); + internal const int COR_E_COMEMULATE = unchecked((int)0x80131535); + internal const int COR_E_CONTEXTMARSHAL = unchecked((int)0x80131504); + internal const int COR_E_CUSTOMATTRIBUTEFORMAT = unchecked((int)0x80131605); + internal const int COR_E_DATAMISALIGNED = unchecked((int)0x80131541); + internal const int COR_E_DIRECTORYNOTFOUND = unchecked((int)0x80070003); + internal const int COR_E_DIVIDEBYZERO = unchecked((int)0x80020012); // DISP_E_DIVBYZERO + internal const int COR_E_DLLNOTFOUND = unchecked((int)0x80131524); + internal const int COR_E_DUPLICATEWAITOBJECT = unchecked((int)0x80131529); + internal const int COR_E_ENDOFSTREAM = unchecked((int)0x80070026); // OS defined + internal const int COR_E_ENTRYPOINTNOTFOUND = unchecked((int)0x80131523); + internal const int COR_E_EXCEPTION = unchecked((int)0x80131500); + internal const int COR_E_EXECUTIONENGINE = unchecked((int)0x80131506); + internal const int COR_E_FIELDACCESS = unchecked((int)0x80131507); + internal const int COR_E_FILELOAD = unchecked((int)0x80131621); + internal const int COR_E_FILENOTFOUND = unchecked((int)0x80070002); + internal const int COR_E_FORMAT = unchecked((int)0x80131537); + internal const int COR_E_HOSTPROTECTION = unchecked((int)0x80131640); + internal const int COR_E_INDEXOUTOFRANGE = unchecked((int)0x80131508); + internal const int COR_E_INSUFFICIENTEXECUTIONSTACK = unchecked((int)0x80131578); + internal const int COR_E_INSUFFICIENTMEMORY = unchecked((int)0x8013153D); + internal const int COR_E_INVALIDCAST = unchecked((int)0x80004002); + internal const int COR_E_INVALIDCOMOBJECT = unchecked((int)0x80131527); + internal const int COR_E_INVALIDFILTERCRITERIA = unchecked((int)0x80131601); + internal const int COR_E_INVALIDOLEVARIANTTYPE = unchecked((int)0x80131531); + internal const int COR_E_INVALIDOPERATION = unchecked((int)0x80131509); + internal const int COR_E_INVALIDPROGRAM = unchecked((int)0x8013153A); + internal const int COR_E_IO = unchecked((int)0x80131620); + internal const int COR_E_KEYNOTFOUND = unchecked((int)0x80131577); + internal const int COR_E_MARSHALDIRECTIVE = unchecked((int)0x80131535); + internal const int COR_E_MEMBERACCESS = unchecked((int)0x8013151A); + internal const int COR_E_METHODACCESS = unchecked((int)0x80131510); + internal const int COR_E_MISSINGFIELD = unchecked((int)0x80131511); + internal const int COR_E_MISSINGMANIFESTRESOURCE = unchecked((int)0x80131532); + internal const int COR_E_MISSINGMEMBER = unchecked((int)0x80131512); + internal const int COR_E_MISSINGMETHOD = unchecked((int)0x80131513); + internal const int COR_E_MISSINGSATELLITEASSEMBLY = unchecked((int)0x80131536); + internal const int COR_E_MULTICASTNOTSUPPORTED = unchecked((int)0x80131514); + internal const int COR_E_NOTFINITENUMBER = unchecked((int)0x80131528); + internal const int COR_E_NOTSUPPORTED = unchecked((int)0x80131515); + internal const int COR_E_NULLREFERENCE = unchecked((int)0x80004003); + internal const int COR_E_OBJECTDISPOSED = unchecked((int)0x80131622); + internal const int COR_E_OPERATIONCANCELED = unchecked((int)0x8013153B); + internal const int COR_E_OUTOFMEMORY = unchecked((int)0x8007000E); + internal const int COR_E_OVERFLOW = unchecked((int)0x80131516); + internal const int COR_E_PATHTOOLONG = unchecked((int)0x800700CE); + internal const int COR_E_PLATFORMNOTSUPPORTED = unchecked((int)0x80131539); + internal const int COR_E_RANK = unchecked((int)0x80131517); + internal const int COR_E_REFLECTIONTYPELOAD = unchecked((int)0x80131602); + internal const int COR_E_RUNTIMEWRAPPED = unchecked((int)0x8013153E); + internal const int COR_E_SAFEARRAYRANKMISMATCH = unchecked((int)0x80131538); + internal const int COR_E_SAFEARRAYTYPEMISMATCH = unchecked((int)0x80131533); + internal const int COR_E_SAFEHANDLEMISSINGATTRIBUTE = unchecked((int)0x80131623); + internal const int COR_E_SECURITY = unchecked((int)0x8013150A); + internal const int COR_E_SEMAPHOREFULL = unchecked((int)0x8013152B); + internal const int COR_E_SERIALIZATION = unchecked((int)0x8013150C); + internal const int COR_E_STACKOVERFLOW = unchecked((int)0x800703E9); + internal const int COR_E_SYNCHRONIZATIONLOCK = unchecked((int)0x80131518); + internal const int COR_E_SYSTEM = unchecked((int)0x80131501); + internal const int COR_E_TARGET = unchecked((int)0x80131603); + internal const int COR_E_TARGETINVOCATION = unchecked((int)0x80131604); + internal const int COR_E_TARGETPARAMCOUNT = unchecked((int)0x8002000E); + internal const int COR_E_THREADABORTED = unchecked((int)0x80131530); + internal const int COR_E_THREADINTERRUPTED = unchecked((int)0x80131519); + internal const int COR_E_THREADSTART = unchecked((int)0x80131525); + internal const int COR_E_THREADSTATE = unchecked((int)0x80131520); + internal const int COR_E_THREADSTOP = unchecked((int)0x80131521); + internal const int COR_E_TIMEOUT = unchecked((int)0x80131505); + internal const int COR_E_TYPEACCESS = unchecked((int)0x80131543); + internal const int COR_E_TYPEINITIALIZATION = unchecked((int)0x80131534); + internal const int COR_E_TYPELOAD = unchecked((int)0x80131522); + internal const int COR_E_TYPEUNLOADED = unchecked((int)0x80131013); + internal const int COR_E_UNAUTHORIZEDACCESS = unchecked((int)0x80070005); + internal const int COR_E_UNSUPPORTEDFORMAT = unchecked((int)0x80131523); + internal const int COR_E_VERIFICATION = unchecked((int)0x8013150D); + internal const int COR_E_WAITHANDLECANNOTBEOPENED = unchecked((int)0x8013152C); + internal const int DISP_E_OVERFLOW = unchecked((int)0x8002000A); + internal const int E_BOUNDS = unchecked((int)0x8000000B); + internal const int E_CHANGED_STATE = unchecked((int)0x8000000C); + internal const int E_FAIL = unchecked((int)0x80004005); + internal const int E_HANDLE = unchecked((int)0x80070006); + internal const int E_INVALIDARG = unchecked((int)0x80070057); + internal const int E_NOTIMPL = unchecked((int)0x80004001); + internal const int E_POINTER = unchecked((int)0x80004003); + internal const int ERROR_MRM_MAP_NOT_FOUND = unchecked((int)0x80073B1F); + internal const int RO_E_CLOSED = unchecked((int)0x80000013); + internal const int TYPE_E_TYPEMISMATCH = unchecked((int)0x80028CA0); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/HashCode.cs b/external/corefx/src/Common/src/CoreLib/System/HashCode.cs new file mode 100644 index 0000000000..4be57be02a --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/HashCode.cs @@ -0,0 +1,430 @@ +// 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. + +/* + +The xxHash32 implementation is based on the code published by Yann Collet: +https://raw.githubusercontent.com/Cyan4973/xxHash/5c174cfa4e45a42f94082dc0d4539b39696afea1/xxhash.c + + xxHash - Fast Hash algorithm + Copyright (C) 2012-2016, Yann Collet + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - xxHash homepage: http://www.xxhash.com + - xxHash source repository : https://github.com/Cyan4973/xxHash + +*/ + +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace System +{ + // xxHash32 is used for the hash code. + // https://github.com/Cyan4973/xxHash + + public struct HashCode + { + private static readonly uint s_seed = GenerateGlobalSeed(); + + private const uint Prime1 = 2654435761U; + private const uint Prime2 = 2246822519U; + private const uint Prime3 = 3266489917U; + private const uint Prime4 = 668265263U; + private const uint Prime5 = 374761393U; + + private uint _v1, _v2, _v3, _v4; + private uint _queue1, _queue2, _queue3; + private uint _length; + + private static unsafe uint GenerateGlobalSeed() + { + uint result; + Interop.GetRandomBytes((byte*)&result, sizeof(uint)); + return result; + } + + public static int Combine(T1 value1) + { + // Provide a way of diffusing bits from something with a limited + // input hash space. For example, many enums only have a few + // possible hashes, only using the bottom few bits of the code. Some + // collections are built on the assumption that hashes are spread + // over a larger space, so diffusing the bits may help the + // collection work more efficiently. + + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + + uint hash = MixEmptyState(); + hash += 4; + + hash = QueueRound(hash, hc1); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + + uint hash = MixEmptyState(); + hash += 8; + + hash = QueueRound(hash, hc1); + hash = QueueRound(hash, hc2); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + + uint hash = MixEmptyState(); + hash += 12; + + hash = QueueRound(hash, hc1); + hash = QueueRound(hash, hc2); + hash = QueueRound(hash, hc3); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + var hc4 = (uint)(value4?.GetHashCode() ?? 0); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + uint hash = MixState(v1, v2, v3, v4); + hash += 16; + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + var hc4 = (uint)(value4?.GetHashCode() ?? 0); + var hc5 = (uint)(value5?.GetHashCode() ?? 0); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + uint hash = MixState(v1, v2, v3, v4); + hash += 20; + + hash = QueueRound(hash, hc5); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + var hc4 = (uint)(value4?.GetHashCode() ?? 0); + var hc5 = (uint)(value5?.GetHashCode() ?? 0); + var hc6 = (uint)(value6?.GetHashCode() ?? 0); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + uint hash = MixState(v1, v2, v3, v4); + hash += 24; + + hash = QueueRound(hash, hc5); + hash = QueueRound(hash, hc6); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + var hc4 = (uint)(value4?.GetHashCode() ?? 0); + var hc5 = (uint)(value5?.GetHashCode() ?? 0); + var hc6 = (uint)(value6?.GetHashCode() ?? 0); + var hc7 = (uint)(value7?.GetHashCode() ?? 0); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + uint hash = MixState(v1, v2, v3, v4); + hash += 28; + + hash = QueueRound(hash, hc5); + hash = QueueRound(hash, hc6); + hash = QueueRound(hash, hc7); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + var hc4 = (uint)(value4?.GetHashCode() ?? 0); + var hc5 = (uint)(value5?.GetHashCode() ?? 0); + var hc6 = (uint)(value6?.GetHashCode() ?? 0); + var hc7 = (uint)(value7?.GetHashCode() ?? 0); + var hc8 = (uint)(value8?.GetHashCode() ?? 0); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + v1 = Round(v1, hc5); + v2 = Round(v2, hc6); + v3 = Round(v3, hc7); + v4 = Round(v4, hc8); + + uint hash = MixState(v1, v2, v3, v4); + hash += 32; + + hash = MixFinal(hash); + return (int)hash; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Rol(uint value, int count) + => (value << count) | (value >> (32 - count)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Initialize(out uint v1, out uint v2, out uint v3, out uint v4) + { + v1 = s_seed + Prime1 + Prime2; + v2 = s_seed + Prime2; + v3 = s_seed; + v4 = s_seed - Prime1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Round(uint hash, uint input) + { + hash += input * Prime2; + hash = Rol(hash, 13); + hash *= Prime1; + return hash; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint QueueRound(uint hash, uint queuedValue) + { + hash += queuedValue * Prime3; + return Rol(hash, 17) * Prime4; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint MixState(uint v1, uint v2, uint v3, uint v4) + { + return Rol(v1, 1) + Rol(v2, 7) + Rol(v3, 12) + Rol(v4, 18); + } + + private static uint MixEmptyState() + { + return s_seed + Prime5; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint MixFinal(uint hash) + { + hash ^= hash >> 15; + hash *= Prime2; + hash ^= hash >> 13; + hash *= Prime3; + hash ^= hash >> 16; + return hash; + } + + public void Add(T value) + { + Add(value?.GetHashCode() ?? 0); + } + + public void Add(T value, IEqualityComparer comparer) + { + Add(comparer != null ? comparer.GetHashCode(value) : (value?.GetHashCode() ?? 0)); + } + + private void Add(int value) + { + // The original xxHash works as follows: + // 0. Initialize immediately. We can't do this in a struct (no + // default ctor). + // 1. Accumulate blocks of length 16 (4 uints) into 4 accumulators. + // 2. Accumulate remaining blocks of length 4 (1 uint) into the + // hash. + // 3. Accumulate remaining blocks of length 1 into the hash. + + // There is no need for #3 as this type only accepts ints. _queue1, + // _queue2 and _queue3 are basically a buffer so that when + // ToHashCode is called we can execute #2 correctly. + + // We need to initialize the xxHash32 state (_v1 to _v4) lazily (see + // #0) nd the last place that can be done if you look at the + // original code is just before the first block of 16 bytes is mixed + // in. The xxHash32 state is never used for streams containing fewer + // than 16 bytes. + + // To see what's really going on here, have a look at the Combine + // methods. + + var val = (uint)value; + + // Storing the value of _length locally shaves of quite a few bytes + // in the resulting machine code. + uint previousLength = _length++; + uint position = previousLength % 4; + + // Switch can't be inlined. + + if (position == 0) + _queue1 = val; + else if (position == 1) + _queue2 = val; + else if (position == 2) + _queue3 = val; + else // position == 3 + { + if (previousLength == 3) + Initialize(out _v1, out _v2, out _v3, out _v4); + + _v1 = Round(_v1, _queue1); + _v2 = Round(_v2, _queue2); + _v3 = Round(_v3, _queue3); + _v4 = Round(_v4, val); + } + } + + public int ToHashCode() + { + // Storing the value of _length locally shaves of quite a few bytes + // in the resulting machine code. + uint length = _length; + + // position refers to the *next* queue position in this method, so + // position == 1 means that _queue1 is populated; _queue2 would have + // been populated on the next call to Add. + uint position = length % 4; + + // If the length is less than 4, _v1 to _v4 don't contain anything + // yet. xxHash32 treats this differently. + + uint hash = length < 4 ? MixEmptyState() : MixState(_v1, _v2, _v3, _v4); + + // _length is incremented once per Add(Int32) and is therefore 4 + // times too small (xxHash length is in bytes, not ints). + + hash += length * 4; + + // Mix what remains in the queue + + // Switch can't be inlined right now, so use as few branches as + // possible by manually excluding impossible scenarios (position > 1 + // is always false if position is not > 0). + if (position > 0) + { + hash = QueueRound(hash, _queue1); + if (position > 1) + { + hash = QueueRound(hash, _queue2); + if (position > 2) + hash = QueueRound(hash, _queue3); + } + } + + hash = MixFinal(hash); + return (int)hash; + } + +#pragma warning disable 0809 + // Obsolete member 'memberA' overrides non-obsolete member 'memberB'. + // Disallowing GetHashCode and Equals is by design + + // * We decided to not override GetHashCode() to produce the hash code + // as this would be weird, both naming-wise as well as from a + // behavioral standpoint (GetHashCode() should return the object's + // hash code, not the one being computed). + + // * Even though ToHashCode() can be called safely multiple times on + // this implementation, it is not part of the contract. If the + // implementation has to change in the future we don't want to worry + // about people who might have incorrectly used this type. + + [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code.", error: true)] + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => throw new NotSupportedException(SR.HashCode_HashCodeNotSupported); + + [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes.", error: true)] + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => throw new NotSupportedException(SR.HashCode_EqualityNotSupported); +#pragma warning restore 0809 + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IAsyncResult.cs b/external/corefx/src/Common/src/CoreLib/System/IAsyncResult.cs new file mode 100644 index 0000000000..0abeaca525 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IAsyncResult.cs @@ -0,0 +1,30 @@ +// 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. + +/*============================================================ +** +** Interface: IAsyncResult +** +** Purpose: Interface to encapsulate the results of an async +** operation +** +===========================================================*/ + +using System; +using System.Threading; + +namespace System +{ + public interface IAsyncResult + { + bool IsCompleted { get; } + + WaitHandle AsyncWaitHandle { get; } + + + Object AsyncState { get; } + + bool CompletedSynchronously { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/ICloneable.cs b/external/corefx/src/Common/src/CoreLib/System/ICloneable.cs new file mode 100644 index 0000000000..9f123e45c8 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ICloneable.cs @@ -0,0 +1,11 @@ +// 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. + +namespace System +{ + public interface ICloneable + { + object Clone(); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IComparable.cs b/external/corefx/src/Common/src/CoreLib/System/IComparable.cs new file mode 100644 index 0000000000..72aeeb027c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IComparable.cs @@ -0,0 +1,37 @@ +// 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. + +namespace System +{ + // The IComparable interface is implemented by classes that support an + // ordering of instances of the class. The ordering represented by + // IComparable can be used to sort arrays and collections of objects + // that implement the interface. + // + public interface IComparable + { + // Interface does not need to be marked with the serializable attribute + // Compares this object to another object, returning an integer that + // indicates the relationship. An implementation of this method must return + // a value less than zero if this is less than object, zero + // if this is equal to object, or a value greater than zero + // if this is greater than object. + // + int CompareTo(Object obj); + } + + // Generic version of IComparable. + + public interface IComparable + { + // Interface does not need to be marked with the serializable attribute + // Compares this object to another object, returning an integer that + // indicates the relationship. An implementation of this method must return + // a value less than zero if this is less than object, zero + // if this is equal to object, or a value greater than zero + // if this is greater than object. + // + int CompareTo(T other); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IConvertible.cs b/external/corefx/src/Common/src/CoreLib/System/IConvertible.cs new file mode 100644 index 0000000000..87351127f2 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IConvertible.cs @@ -0,0 +1,63 @@ +// 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. + +namespace System +{ + // The IConvertible interface represents an object that contains a value. This + // interface is implemented by the following types in the System namespace: + // Boolean, Char, SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, + // Single, Double, Decimal, DateTime, and String. The interface may + // be implemented by other types that are to be considered values. For example, + // a library of nullable database types could implement IConvertible. + // + // Note: The interface was originally proposed as IValue. + // + // The implementations of IConvertible provided by the System.XXX value classes + // simply forward to the appropriate Value.ToXXX(YYY) methods (a description of + // the Value class follows below). In cases where a Value.ToXXX(YYY) method + // does not exist (because the particular conversion is not supported), the + // IConvertible implementation should simply throw an InvalidCastException. + + [CLSCompliant(false)] + public interface IConvertible + { + // Returns the type code of this object. An implementation of this method + // must not return TypeCode.Empty (which represents a null reference) or + // TypeCode.Object (which represents an object that doesn't implement the + // IConvertible interface). An implementation of this method should return + // TypeCode.DBNull if the value of this object is a database null. For + // example, a nullable integer type should return TypeCode.DBNull if the + // value of the object is the database null. Otherwise, an implementation + // of this method should return the TypeCode that best describes the + // internal representation of the object. + + TypeCode GetTypeCode(); + + // The ToXXX methods convert the value of the underlying object to the + // given type. If a particular conversion is not supported, the + // implementation must throw an InvalidCastException. If the value of the + // underlying object is not within the range of the target type, the + // implementation must throw an OverflowException. The + // IFormatProvider will be used to get a NumberFormatInfo or similar + // appropriate service object, and may safely be null. + + bool ToBoolean(IFormatProvider provider); + char ToChar(IFormatProvider provider); + sbyte ToSByte(IFormatProvider provider); + byte ToByte(IFormatProvider provider); + short ToInt16(IFormatProvider provider); + ushort ToUInt16(IFormatProvider provider); + int ToInt32(IFormatProvider provider); + uint ToUInt32(IFormatProvider provider); + long ToInt64(IFormatProvider provider); + ulong ToUInt64(IFormatProvider provider); + float ToSingle(IFormatProvider provider); + double ToDouble(IFormatProvider provider); + Decimal ToDecimal(IFormatProvider provider); + DateTime ToDateTime(IFormatProvider provider); + String ToString(IFormatProvider provider); + Object ToType(Type conversionType, IFormatProvider provider); + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/ICustomFormatter.cs b/external/corefx/src/Common/src/CoreLib/System/ICustomFormatter.cs new file mode 100644 index 0000000000..47340f3093 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ICustomFormatter.cs @@ -0,0 +1,24 @@ +// 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. + +/*============================================================ +** +** Interface: ICustomFormatter +** +** +** Purpose: Marks a class as providing special formatting +** +** +===========================================================*/ + +using System; + +namespace System +{ + public interface ICustomFormatter + { + // Interface does not need to be marked with the serializable attribute + String Format(String format, Object arg, IFormatProvider formatProvider); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IDisposable.cs b/external/corefx/src/Common/src/CoreLib/System/IDisposable.cs new file mode 100644 index 0000000000..24f0740edc --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IDisposable.cs @@ -0,0 +1,60 @@ +// 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. + +/*============================================================ +** +** Interface: IDisposable +** +** +** Purpose: Interface for assisting with deterministic finalization. +** +** +===========================================================*/ + +namespace System +{ + // IDisposable is an attempt at helping to solve problems with deterministic + // finalization. The GC of course doesn't leave any way to deterministically + // know when a finalizer will run. This forces classes that hold onto OS + // resources or some sort of important state (such as a FileStream or a + // network connection) to provide a Close or Dispose method so users can + // run clean up code deterministically. We have formalized this into an + // interface with one method. Classes may privately implement IDisposable and + // provide a Close method instead, if that name is by far the expected name + // for objects in that domain (ie, you don't Dispose of a FileStream, you Close + // it). + // + // This interface could be theoretically used as a marker by a compiler to + // ensure a disposable object has been cleaned up along all code paths if it's + // been allocated in that method, though in practice any compiler that + // draconian may tick off any number of people. Perhaps an external tool (like + // like Purify or BoundsChecker) could do this. Instead, C# has added a using + // clause, which will generate a try/finally statement where the resource + // passed into the using clause will always have it's Dispose method called. + // Syntax is using(FileStream fs = ...) { .. }; + // + // Dispose should meet the following conditions: + // 1) Be safely callable multiple times + // 2) Release any resources associated with the instance + // 3) Call the base class's Dispose method, if necessary + // 4) Suppress finalization of this class to help the GC by reducing the + // number of objects on the finalization queue. + // 5) Dispose shouldn't generally throw exceptions, except for very serious + // errors that are particularly unexpected. (ie, OutOfMemoryException) + // Ideally, nothing should go wrong with your object by calling Dispose. + // + // If possible, a class should define a finalizer that calls Dispose. + // However, in many situations, this is impractical. For instance, take the + // classic example of a Stream and a StreamWriter (which has an internal + // buffer of data to write to the Stream). If both objects are collected + // before Close or Dispose has been called on either, then the GC may run the + // finalizer for the Stream first, before the StreamWriter. At that point, any + // data buffered by the StreamWriter cannot be written to the Stream. In this + // case, it doesn't make much sense to provide a finalizer on the StreamWriter + // since you cannot solve this problem correctly. + public interface IDisposable + { + void Dispose(); + } +} diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ResetBindException.cs b/external/corefx/src/Common/src/CoreLib/System/IEquatable.cs similarity index 70% rename from external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ResetBindException.cs rename to external/corefx/src/Common/src/CoreLib/System/IEquatable.cs index 0563d85611..1264fdd57a 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ResetBindException.cs +++ b/external/corefx/src/Common/src/CoreLib/System/IEquatable.cs @@ -4,9 +4,11 @@ using System; -namespace Microsoft.CSharp.RuntimeBinder +namespace System { - internal sealed class ResetBindException : Exception + public interface IEquatable { + bool Equals(T other); } } + diff --git a/external/corefx/src/Common/src/CoreLib/System/IFormatProvider.cs b/external/corefx/src/Common/src/CoreLib/System/IFormatProvider.cs new file mode 100644 index 0000000000..0c17354af3 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IFormatProvider.cs @@ -0,0 +1,23 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: Notes a class which knows how to return formatting information +** +** +============================================================*/ + +using System; + +namespace System +{ + public interface IFormatProvider + { + // Interface does not need to be marked with the serializable attribute + Object GetFormat(Type formatType); + } +} diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystem.Current.Win32.cs b/external/corefx/src/Common/src/CoreLib/System/IFormattable.cs similarity index 60% rename from external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystem.Current.Win32.cs rename to external/corefx/src/Common/src/CoreLib/System/IFormattable.cs index 69bd3e6a5f..1f2f7022cc 100644 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystem.Current.Win32.cs +++ b/external/corefx/src/Common/src/CoreLib/System/IFormattable.cs @@ -2,10 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace System.IO +using System; + +namespace System { - internal abstract partial class FileSystem + public interface IFormattable { - private static readonly FileSystem s_current = new Win32FileSystem(); + String ToString(String format, IFormatProvider formatProvider); } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/IO/BinaryWriter.cs b/external/corefx/src/Common/src/CoreLib/System/IO/BinaryWriter.cs similarity index 82% rename from external/corert/src/System.Private.CoreLib/src/System/IO/BinaryWriter.cs rename to external/corefx/src/Common/src/CoreLib/System/IO/BinaryWriter.cs index a80e55cac1..ad1d31f577 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/IO/BinaryWriter.cs +++ b/external/corefx/src/Common/src/CoreLib/System/IO/BinaryWriter.cs @@ -4,13 +4,14 @@ using System.Text; using System.Diagnostics; +using System.Buffers; namespace System.IO { // This abstract base class represents a writer that can write // primitives to an arbitrary stream. A subclass can override methods to // give unique encodings. - [Serializable] + // public class BinaryWriter : IDisposable { public static readonly BinaryWriter Null = new BinaryWriter(); @@ -49,17 +50,11 @@ namespace System.IO public BinaryWriter(Stream output, Encoding encoding, bool leaveOpen) { if (output == null) - { throw new ArgumentNullException(nameof(output)); - } if (encoding == null) - { throw new ArgumentNullException(nameof(encoding)); - } if (!output.CanWrite) - { throw new ArgumentException(SR.Argument_StreamNotWritable); - } OutStream = output; _buffer = new byte[16]; @@ -68,18 +63,22 @@ namespace System.IO _leaveOpen = leaveOpen; } + // Closes this writer and releases any system resources associated with the + // writer. Following a call to Close, any operations on the writer + // may raise exceptions. + public virtual void Close() + { + Dispose(true); + } + protected virtual void Dispose(bool disposing) { if (disposing) { if (_leaveOpen) - { OutStream.Flush(); - } else - { - OutStream.Dispose(); - } + OutStream.Close(); } } @@ -88,19 +87,9 @@ namespace System.IO Dispose(true); } - /// - /// Override Dispose(bool) instead of Close(). This API exists for compatibility purposes. - /// - public virtual void Close() - { - Dispose(true); - } - - /// - /// Returns the stream associated with the writer. It flushes all pending - /// writes before returning. All subclasses should override Flush to - /// ensure that all buffered data is sent to the stream. - /// + // Returns the stream associated with the writer. It flushes all pending + // writes before returning. All subclasses should override Flush to + // ensure that all buffered data is sent to the stream. public virtual Stream BaseStream { get @@ -156,10 +145,7 @@ namespace System.IO public virtual void Write(byte[] buffer) { if (buffer == null) - { throw new ArgumentNullException(nameof(buffer)); - } - OutStream.Write(buffer, 0, buffer.Length); } @@ -178,17 +164,17 @@ namespace System.IO // advanced by two. // Note this method cannot handle surrogates properly in UTF-8. // - public virtual void Write(char ch) + public unsafe virtual void Write(char ch) { - if (char.IsSurrogate(ch)) - { + if (Char.IsSurrogate(ch)) throw new ArgumentException(SR.Arg_SurrogatesNotAllowedAsSingleChar); - } Debug.Assert(_encoding.GetMaxByteCount(1) <= 16, "_encoding.GetMaxByteCount(1) <= 16)"); int numBytes = 0; - char[] chBuf = new char[] { ch }; - numBytes = _encoder.GetBytes(chBuf, 0, 1, _buffer, 0, true); + fixed (byte* pBytes = &_buffer[0]) + { + numBytes = _encoder.GetBytes(&ch, 1, pBytes, _buffer.Length, flush: true); + } OutStream.Write(_buffer, 0, numBytes); } @@ -200,9 +186,7 @@ namespace System.IO public virtual void Write(char[] chars) { if (chars == null) - { throw new ArgumentNullException(nameof(chars)); - } byte[] bytes = _encoding.GetBytes(chars, 0, chars.Length); OutStream.Write(bytes, 0, bytes.Length); @@ -239,33 +223,7 @@ namespace System.IO public virtual void Write(decimal value) { - int[] bits = decimal.GetBits(value); - Debug.Assert(bits.Length == 4); - - int lo = bits[0]; - _buffer[0] = (byte)lo; - _buffer[1] = (byte)(lo >> 8); - _buffer[2] = (byte)(lo >> 16); - _buffer[3] = (byte)(lo >> 24); - - int mid = bits[1]; - _buffer[4] = (byte)mid; - _buffer[5] = (byte)(mid >> 8); - _buffer[6] = (byte)(mid >> 16); - _buffer[7] = (byte)(mid >> 24); - - int hi = bits[2]; - _buffer[8] = (byte)hi; - _buffer[9] = (byte)(hi >> 8); - _buffer[10] = (byte)(hi >> 16); - _buffer[11] = (byte)(hi >> 24); - - int flags = bits[3]; - _buffer[12] = (byte)flags; - _buffer[13] = (byte)(flags >> 8); - _buffer[14] = (byte)(flags >> 16); - _buffer[15] = (byte)(flags >> 24); - + Decimal.GetBytes(value, _buffer); OutStream.Write(_buffer, 0, 16); } @@ -367,12 +325,10 @@ namespace System.IO // a four-byte unsigned integer, and then writes that many characters // to the stream. // - public virtual void Write(string value) + public unsafe virtual void Write(String value) { if (value == null) - { throw new ArgumentNullException(nameof(value)); - } int len = _encoding.GetByteCount(value); Write7BitEncodedInt(len); @@ -380,11 +336,12 @@ namespace System.IO if (_largeByteBuffer == null) { _largeByteBuffer = new byte[LargeByteBufferSize]; - _maxChars = LargeByteBufferSize / _encoding.GetMaxByteCount(1); + _maxChars = _largeByteBuffer.Length / _encoding.GetMaxByteCount(1); } - if (len <= LargeByteBufferSize) + if (len <= _largeByteBuffer.Length) { + //Debug.Assert(len == _encoding.GetBytes(chars, 0, chars.Length, _largeByteBuffer, 0), "encoding's GetByteCount & GetBytes gave different answers! encoding type: "+_encoding.GetType().Name); _encoding.GetBytes(value, 0, value.Length, _largeByteBuffer, 0); OutStream.Write(_largeByteBuffer, 0, len); } @@ -404,10 +361,24 @@ namespace System.IO // Figure out how many chars to process this round. int charCount = (numLeft > _maxChars) ? _maxChars : numLeft; int byteLen; - byteLen = _encoder.GetBytes(value.ToCharArray(), charStart, charCount, _largeByteBuffer, 0, charCount == numLeft); + + checked + { + if (charStart < 0 || charCount < 0 || charStart > value.Length - charCount) + { + throw new ArgumentOutOfRangeException(nameof(charCount)); + } + fixed (char* pChars = value) + { + fixed (byte* pBytes = &_largeByteBuffer[0]) + { + byteLen = _encoder.GetBytes(pChars + charStart, charCount, pBytes, _largeByteBuffer.Length, charCount == numLeft); + } + } + } #if DEBUG totalBytes += byteLen; - Debug.Assert(totalBytes <= len && byteLen <= LargeByteBufferSize, "BinaryWriter::Write(String) - More bytes encoded than expected!"); + Debug.Assert(totalBytes <= len && byteLen <= _largeByteBuffer.Length, "BinaryWriter::Write(String) - More bytes encoded than expected!"); #endif OutStream.Write(_largeByteBuffer, 0, byteLen); charStart += charCount; @@ -419,6 +390,41 @@ namespace System.IO } } + public virtual void Write(ReadOnlySpan buffer) + { + if (GetType() == typeof(BinaryWriter)) + { + OutStream.Write(buffer); + } + else + { + byte[] array = ArrayPool.Shared.Rent(buffer.Length); + try + { + buffer.CopyTo(array); + Write(array, 0, buffer.Length); + } + finally + { + ArrayPool.Shared.Return(array); + } + } + } + + public virtual void Write(ReadOnlySpan chars) + { + byte[] bytes = ArrayPool.Shared.Rent(_encoding.GetMaxByteCount(chars.Length)); + try + { + int bytesWritten = _encoding.GetBytes(chars, bytes); + Write(bytes, 0, bytesWritten); + } + finally + { + ArrayPool.Shared.Return(bytes); + } + } + protected void Write7BitEncodedInt(int value) { // Write out an int 7 bits at a time. The high bit of the byte, diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/DirectoryNotFoundException.cs b/external/corefx/src/Common/src/CoreLib/System/IO/DirectoryNotFoundException.cs new file mode 100644 index 0000000000..d7c6eacb9a --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/DirectoryNotFoundException.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. +// See the LICENSE file in the project root for more information. + +using System.Runtime.Serialization; + +namespace System.IO +{ + /* + * Thrown when trying to access a directory that doesn't exist on disk. + * From COM Interop, this exception is thrown for 2 HRESULTS: + * the Win32 errorcode-as-HRESULT ERROR_PATH_NOT_FOUND (0x80070003) + * and STG_E_PATHNOTFOUND (0x80030003). + */ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class DirectoryNotFoundException : IOException + { + public DirectoryNotFoundException() + : base(SR.Arg_DirectoryNotFoundException) + { + HResult = HResults.COR_E_DIRECTORYNOTFOUND; + } + + public DirectoryNotFoundException(string message) + : base(message) + { + HResult = HResults.COR_E_DIRECTORYNOTFOUND; + } + + public DirectoryNotFoundException(string message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_DIRECTORYNOTFOUND; + } + + protected DirectoryNotFoundException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/DisableMediaInsertionPrompt.cs b/external/corefx/src/Common/src/CoreLib/System/IO/DisableMediaInsertionPrompt.cs new file mode 100644 index 0000000000..aa10e8d883 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/DisableMediaInsertionPrompt.cs @@ -0,0 +1,36 @@ +// 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. + +namespace System.IO +{ + /// + /// Simple wrapper to safely disable the normal media insertion prompt for + /// removable media (floppies, cds, memory cards, etc.) + /// + /// + /// Note that removable media file systems lazily load. After starting the OS + /// they won't be loaded until you have media in the drive- and as such the + /// prompt won't happen. You have to have had media in at least once to get + /// the file system to load and then have removed it. + /// + internal struct DisableMediaInsertionPrompt : IDisposable + { + private bool _disableSuccess; + private uint _oldMode; + + public static DisableMediaInsertionPrompt Create() + { + DisableMediaInsertionPrompt prompt = new DisableMediaInsertionPrompt(); + prompt._disableSuccess = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS, out prompt._oldMode); + return prompt; + } + + public void Dispose() + { + uint ignore; + if (_disableSuccess) + Interop.Kernel32.SetThreadErrorMode(_oldMode, out ignore); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/DriveNotFoundException.cs b/external/corefx/src/Common/src/CoreLib/System/IO/DriveNotFoundException.cs new file mode 100644 index 0000000000..3f2c88c74b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/DriveNotFoundException.cs @@ -0,0 +1,36 @@ +// 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.Runtime.Serialization; + +namespace System.IO +{ + //Thrown when trying to access a drive that is not available. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + internal class DriveNotFoundException : IOException + { + public DriveNotFoundException() + : base(SR.Arg_DriveNotFoundException) + { + HResult = HResults.COR_E_DIRECTORYNOTFOUND; + } + + public DriveNotFoundException(string message) + : base(message) + { + HResult = HResults.COR_E_DIRECTORYNOTFOUND; + } + + public DriveNotFoundException(string message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_DIRECTORYNOTFOUND; + } + + protected DriveNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/System.Runtime.Extensions/src/System/IO/EncodingCache.cs b/external/corefx/src/Common/src/CoreLib/System/IO/EncodingCache.cs similarity index 100% rename from external/corefx/src/System.Runtime.Extensions/src/System/IO/EncodingCache.cs rename to external/corefx/src/Common/src/CoreLib/System/IO/EncodingCache.cs diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/EndOfStreamException.cs b/external/corefx/src/Common/src/CoreLib/System/IO/EndOfStreamException.cs new file mode 100644 index 0000000000..606073ad9a --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/EndOfStreamException.cs @@ -0,0 +1,36 @@ +// 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.Runtime.Serialization; + +namespace System.IO +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class EndOfStreamException : IOException + { + public EndOfStreamException() + : base(SR.Arg_EndOfStreamException) + { + HResult = HResults.COR_E_ENDOFSTREAM; + } + + public EndOfStreamException(string message) + : base(message) + { + HResult = HResults.COR_E_ENDOFSTREAM; + } + + public EndOfStreamException(string message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_ENDOFSTREAM; + } + + protected EndOfStreamException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/Error.cs b/external/corefx/src/Common/src/CoreLib/System/IO/Error.cs new file mode 100644 index 0000000000..1e319a0680 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/Error.cs @@ -0,0 +1,47 @@ +// 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.Runtime.InteropServices; +using System.Text; +using System.Globalization; + +namespace System.IO +{ + /// + /// Provides centralized methods for creating exceptions for System.IO.FileSystem. + /// + internal static class Error + { + internal static Exception GetStreamIsClosed() + { + return new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); + } + + internal static Exception GetEndOfFile() + { + return new EndOfStreamException(SR.IO_EOF_ReadBeyondEOF); + } + + internal static Exception GetFileNotOpen() + { + return new ObjectDisposedException(null, SR.ObjectDisposed_FileClosed); + } + + internal static Exception GetReadNotSupported() + { + return new NotSupportedException(SR.NotSupported_UnreadableStream); + } + + internal static Exception GetSeekNotSupported() + { + return new NotSupportedException(SR.NotSupported_UnseekableStream); + } + + internal static Exception GetWriteNotSupported() + { + return new NotSupportedException(SR.NotSupported_UnwritableStream); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/FileAccess.cs b/external/corefx/src/Common/src/CoreLib/System/IO/FileAccess.cs new file mode 100644 index 0000000000..1b70bae172 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/FileAccess.cs @@ -0,0 +1,28 @@ +// 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; + +namespace System.IO +{ + // Contains constants for specifying the access you want for a file. + // You can have Read, Write or ReadWrite access. + // + [Flags] + public enum FileAccess + { + // Specifies read access to the file. Data can be read from the file and + // the file pointer can be moved. Combine with WRITE for read-write access. + Read = 1, + + // Specifies write access to the file. Data can be written to the file and + // the file pointer can be moved. Combine with READ for read-write access. + Write = 2, + + // Specifies read and write access to the file. Data can be written to the + // file and the file pointer can be moved. Data can also be read from the + // file. + ReadWrite = 3, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/FileLoadException.cs b/external/corefx/src/Common/src/CoreLib/System/IO/FileLoadException.cs new file mode 100644 index 0000000000..8c31244e1d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/FileLoadException.cs @@ -0,0 +1,98 @@ +// 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.Runtime.Serialization; + +namespace System.IO +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public partial class FileLoadException : IOException + { + public FileLoadException() + : base(SR.IO_FileLoad) + { + HResult = HResults.COR_E_FILELOAD; + } + + public FileLoadException(string message) + : base(message) + { + HResult = HResults.COR_E_FILELOAD; + } + + public FileLoadException(string message, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_FILELOAD; + } + + public FileLoadException(string message, string fileName) : base(message) + { + HResult = HResults.COR_E_FILELOAD; + FileName = fileName; + } + + public FileLoadException(string message, string fileName, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_FILELOAD; + FileName = fileName; + } + + public override string Message + { + get + { + if (_message == null) + { + _message = FormatFileLoadExceptionMessage(FileName, HResult); + } + return _message; + } + } + + public string FileName { get; } + public string FusionLog { get; } + + public override string ToString() + { + string s = GetType().ToString() + ": " + Message; + + if (FileName != null && FileName.Length != 0) + s += Environment.NewLine + SR.Format(SR.IO_FileName_Name, FileName); + + if (InnerException != null) + s = s + " ---> " + InnerException.ToString(); + + if (StackTrace != null) + s += Environment.NewLine + StackTrace; + + if (FusionLog != null) + { + if (s == null) + s = " "; + s += Environment.NewLine; + s += Environment.NewLine; + s += FusionLog; + } + + return s; + } + + protected FileLoadException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + FileName = info.GetString("FileLoad_FileName"); + FusionLog = info.GetString("FileLoad_FusionLog"); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("FileLoad_FileName", FileName, typeof(string)); + info.AddValue("FileLoad_FusionLog", FusionLog, typeof(string)); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/FileMode.cs b/external/corefx/src/Common/src/CoreLib/System/IO/FileMode.cs new file mode 100644 index 0000000000..77f2fe6f20 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/FileMode.cs @@ -0,0 +1,38 @@ +// 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. + +namespace System.IO +{ + // Contains constants for specifying how the OS should open a file. + // These will control whether you overwrite a file, open an existing + // file, or some combination thereof. + // + // To append to a file, use Append (which maps to OpenOrCreate then we seek + // to the end of the file). To truncate a file or create it if it doesn't + // exist, use Create. + // + public enum FileMode + { + // Creates a new file. An exception is raised if the file already exists. + CreateNew = 1, + + // Creates a new file. If the file already exists, it is overwritten. + Create = 2, + + // Opens an existing file. An exception is raised if the file does not exist. + Open = 3, + + // Opens the file if it exists. Otherwise, creates a new file. + OpenOrCreate = 4, + + // Opens an existing file. Once opened, the file is truncated so that its + // size is zero bytes. The calling process must open the file with at least + // WRITE access. An exception is raised if the file does not exist. + Truncate = 5, + + // Opens the file if it exists and seeks to the end. Otherwise, + // creates a new file. + Append = 6, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/FileNotFoundException.cs b/external/corefx/src/Common/src/CoreLib/System/IO/FileNotFoundException.cs new file mode 100644 index 0000000000..72cff4cfc0 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/FileNotFoundException.cs @@ -0,0 +1,110 @@ +// 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.Runtime.Serialization; + +namespace System.IO +{ + // Thrown when trying to access a file that doesn't exist on disk. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public partial class FileNotFoundException : IOException + { + public FileNotFoundException() + : base(SR.IO_FileNotFound) + { + HResult = HResults.COR_E_FILENOTFOUND; + } + + public FileNotFoundException(string message) + : base(message) + { + HResult = HResults.COR_E_FILENOTFOUND; + } + + public FileNotFoundException(string message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_FILENOTFOUND; + } + + public FileNotFoundException(string message, string fileName) + : base(message) + { + HResult = HResults.COR_E_FILENOTFOUND; + FileName = fileName; + } + + public FileNotFoundException(string message, string fileName, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_FILENOTFOUND; + FileName = fileName; + } + + public override string Message + { + get + { + SetMessageField(); + return _message; + } + } + + private void SetMessageField() + { + if (_message == null) + { + if ((FileName == null) && + (HResult == System.HResults.COR_E_EXCEPTION)) + _message = SR.IO_FileNotFound; + + else if (FileName != null) + _message = FileLoadException.FormatFileLoadExceptionMessage(FileName, HResult); + } + } + + public string FileName { get; } + public string FusionLog { get; } + + public override string ToString() + { + string s = GetType().ToString() + ": " + Message; + + if (FileName != null && FileName.Length != 0) + s += Environment.NewLine + SR.Format(SR.IO_FileName_Name, FileName); + + if (InnerException != null) + s = s + " ---> " + InnerException.ToString(); + + if (StackTrace != null) + s += Environment.NewLine + StackTrace; + + if (FusionLog != null) + { + if (s == null) + s = " "; + s += Environment.NewLine; + s += Environment.NewLine; + s += FusionLog; + } + return s; + } + + protected FileNotFoundException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + FileName = info.GetString("FileNotFound_FileName"); + FusionLog = info.GetString("FileNotFound_FusionLog"); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("FileNotFound_FileName", FileName, typeof(string)); + info.AddValue("FileNotFound_FusionLog", FusionLog, typeof(string)); + } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/FileOptions.cs b/external/corefx/src/Common/src/CoreLib/System/IO/FileOptions.cs new file mode 100644 index 0000000000..ae8396a588 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/FileOptions.cs @@ -0,0 +1,33 @@ +// 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.Runtime.InteropServices; + +namespace System.IO +{ + // Maps to FILE_FLAG_DELETE_ON_CLOSE and similar values from winbase.h. + // We didn't expose a number of these values because we didn't believe + // a number of them made sense in managed code, at least not yet. + [Flags] + public enum FileOptions + { + // NOTE: any change to FileOptions enum needs to be + // matched in the FileStream ctor for error validation + None = 0, + WriteThrough = unchecked((int)0x80000000), + Asynchronous = unchecked((int)0x40000000), // FILE_FLAG_OVERLAPPED + // NoBuffering = 0x20000000, + RandomAccess = 0x10000000, + DeleteOnClose = 0x04000000, + SequentialScan = 0x08000000, + // AllowPosix = 0x01000000, // FILE_FLAG_POSIX_SEMANTICS + // BackupOrRestore, + // DisallowReparsePoint = 0x00200000, // FILE_FLAG_OPEN_REPARSE_POINT + // NoRemoteRecall = 0x00100000, // FILE_FLAG_OPEN_NO_RECALL + // FirstPipeInstance = 0x00080000, // FILE_FLAG_FIRST_PIPE_INSTANCE + Encrypted = 0x00004000, // FILE_ATTRIBUTE_ENCRYPTED + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/FileShare.cs b/external/corefx/src/Common/src/CoreLib/System/IO/FileShare.cs new file mode 100644 index 0000000000..e9b9b5e32f --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/FileShare.cs @@ -0,0 +1,45 @@ +// 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; + +namespace System.IO +{ + // Contains constants for controlling file sharing options while + // opening files. You can specify what access other processes trying + // to open the same file concurrently can have. + // + // Note these values currently match the values for FILE_SHARE_READ, + // FILE_SHARE_WRITE, and FILE_SHARE_DELETE in winnt.h + // + [Flags] + public enum FileShare + { + // No sharing. Any request to open the file (by this process or another + // process) will fail until the file is closed. + None = 0, + + // Allows subsequent opening of the file for reading. If this flag is not + // specified, any request to open the file for reading (by this process or + // another process) will fail until the file is closed. + Read = 1, + + // Allows subsequent opening of the file for writing. If this flag is not + // specified, any request to open the file for writing (by this process or + // another process) will fail until the file is closed. + Write = 2, + + // Allows subsequent opening of the file for writing or reading. If this flag + // is not specified, any request to open the file for writing or reading (by + // this process or another process) will fail until the file is closed. + ReadWrite = 3, + + // Open the file, but allow someone else to delete the file. + Delete = 4, + + // Whether the file handle should be inheritable by child processes. + // Note this is not directly supported like this by Win32. + Inheritable = 0x10, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/FileStream.Linux.cs b/external/corefx/src/Common/src/CoreLib/System/IO/FileStream.Linux.cs new file mode 100644 index 0000000000..873c4eb559 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/FileStream.Linux.cs @@ -0,0 +1,30 @@ +// 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 Microsoft.Win32.SafeHandles; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; + +namespace System.IO +{ + public partial class FileStream : Stream + { + /// Prevents other processes from reading from or writing to the FileStream. + /// The beginning of the range to lock. + /// The range to be locked. + private void LockInternal(long position, long length) + { + CheckFileCall(Interop.Sys.LockFileRegion(_fileHandle, position, length, Interop.Sys.LockType.F_WRLCK)); + } + + /// Allows access by other processes to all or part of a file that was previously locked. + /// The beginning of the range to unlock. + /// The range to be unlocked. + private void UnlockInternal(long position, long length) + { + CheckFileCall(Interop.Sys.LockFileRegion(_fileHandle, position, length, Interop.Sys.LockType.F_UNLCK)); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/FileStream.OSX.cs b/external/corefx/src/Common/src/CoreLib/System/IO/FileStream.OSX.cs new file mode 100644 index 0000000000..f29e922337 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/FileStream.OSX.cs @@ -0,0 +1,19 @@ +// 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. + +namespace System.IO +{ + public partial class FileStream : Stream + { + private void LockInternal(long position, long length) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_OSXFileLocking); + } + + private void UnlockInternal(long position, long length) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_OSXFileLocking); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/FileStream.Unix.cs b/external/corefx/src/Common/src/CoreLib/System/IO/FileStream.Unix.cs new file mode 100644 index 0000000000..34164abc33 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/FileStream.Unix.cs @@ -0,0 +1,813 @@ +// 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 Microsoft.Win32.SafeHandles; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace System.IO +{ + /// Provides an implementation of a file stream for Unix files. + public partial class FileStream : Stream + { + /// File mode. + private FileMode _mode; + + /// Advanced options requested when opening the file. + private FileOptions _options; + + /// If the file was opened with FileMode.Append, the length of the file when opened; otherwise, -1. + private long _appendStart = -1; + + /// + /// Extra state used by the file stream when _useAsyncIO is true. This includes + /// the semaphore used to serialize all operation, the buffer/offset/count provided by the + /// caller for ReadAsync/WriteAsync operations, and the last successful task returned + /// synchronously from ReadAsync which can be reused if the count matches the next request. + /// Only initialized when is true. + /// + private AsyncState _asyncState; + + /// Lazily-initialized value for whether the file supports seeking. + private bool? _canSeek; + + private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) + { + // FileStream performs most of the general argument validation. We can assume here that the arguments + // are all checked and consistent (e.g. non-null-or-empty path; valid enums in mode, access, share, and options; etc.) + // Store the arguments + _mode = mode; + _options = options; + + if (_useAsyncIO) + _asyncState = new AsyncState(); + + // Translate the arguments into arguments for an open call. + Interop.Sys.OpenFlags openFlags = PreOpenConfigurationFromOptions(mode, _access, share, options); + + // If the file gets created a new, we'll select the permissions for it. Most Unix utilities by default use 666 (read and + // write for all), so we do the same (even though this doesn't match Windows, where by default it's possible to write out + // a file and then execute it). No matter what we choose, it'll be subject to the umask applied by the system, such that the + // actual permissions will typically be less than what we select here. + const Interop.Sys.Permissions OpenPermissions = + Interop.Sys.Permissions.S_IRUSR | Interop.Sys.Permissions.S_IWUSR | + Interop.Sys.Permissions.S_IRGRP | Interop.Sys.Permissions.S_IWGRP | + Interop.Sys.Permissions.S_IROTH | Interop.Sys.Permissions.S_IWOTH; + + // Open the file and store the safe handle. + return SafeFileHandle.Open(_path, openFlags, (int)OpenPermissions); + } + + /// Initializes a stream for reading or writing a Unix file. + /// How the file should be opened. + /// What other access to the file should be allowed. This is currently ignored. + private void Init(FileMode mode, FileShare share) + { + _fileHandle.IsAsync = _useAsyncIO; + + // Lock the file if requested via FileShare. This is only advisory locking. FileShare.None implies an exclusive + // lock on the file and all other modes use a shared lock. While this is not as granular as Windows, not mandatory, + // and not atomic with file opening, it's better than nothing. + Interop.Sys.LockOperations lockOperation = (share == FileShare.None) ? Interop.Sys.LockOperations.LOCK_EX : Interop.Sys.LockOperations.LOCK_SH; + if (Interop.Sys.FLock(_fileHandle, lockOperation | Interop.Sys.LockOperations.LOCK_NB) < 0) + { + // The only error we care about is EWOULDBLOCK, which indicates that the file is currently locked by someone + // else and we would block trying to access it. Other errors, such as ENOTSUP (locking isn't supported) or + // EACCES (the file system doesn't allow us to lock), will only hamper FileStream's usage without providing value, + // given again that this is only advisory / best-effort. + Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); + if (errorInfo.Error == Interop.Error.EWOULDBLOCK) + { + throw Interop.GetExceptionForIoErrno(errorInfo, _path, isDirectory: false); + } + } + + // These provide hints around how the file will be accessed. Specifying both RandomAccess + // and Sequential together doesn't make sense as they are two competing options on the same spectrum, + // so if both are specified, we prefer RandomAccess (behavior on Windows is unspecified if both are provided). + Interop.Sys.FileAdvice fadv = + (_options & FileOptions.RandomAccess) != 0 ? Interop.Sys.FileAdvice.POSIX_FADV_RANDOM : + (_options & FileOptions.SequentialScan) != 0 ? Interop.Sys.FileAdvice.POSIX_FADV_SEQUENTIAL : + 0; + if (fadv != 0) + { + CheckFileCall(Interop.Sys.PosixFAdvise(_fileHandle, 0, 0, fadv), + ignoreNotSupported: true); // just a hint. + } + + if (_mode == FileMode.Append) + { + // Jump to the end of the file if opened as Append. + _appendStart = SeekCore(_fileHandle, 0, SeekOrigin.End); + } + else if (mode == FileMode.Create || mode == FileMode.Truncate) + { + // Truncate the file now if the file mode requires it. This ensures that the file only will be truncated + // if opened successfully. + CheckFileCall(Interop.Sys.FTruncate(_fileHandle, 0)); + } + } + + /// Initializes a stream from an already open file handle (file descriptor). + private void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO) + { + if (useAsyncIO) + _asyncState = new AsyncState(); + + if (CanSeekCore(handle)) // use non-virtual CanSeekCore rather than CanSeek to avoid making virtual call during ctor + SeekCore(handle, 0, SeekOrigin.Current); + } + + /// Translates the FileMode, FileAccess, and FileOptions values into flags to be passed when opening the file. + /// The FileMode provided to the stream's constructor. + /// The FileAccess provided to the stream's constructor + /// The FileShare provided to the stream's constructor + /// The FileOptions provided to the stream's constructor + /// The flags value to be passed to the open system call. + private static Interop.Sys.OpenFlags PreOpenConfigurationFromOptions(FileMode mode, FileAccess access, FileShare share, FileOptions options) + { + // Translate FileMode. Most of the values map cleanly to one or more options for open. + Interop.Sys.OpenFlags flags = default(Interop.Sys.OpenFlags); + switch (mode) + { + default: + case FileMode.Open: // Open maps to the default behavior for open(...). No flags needed. + case FileMode.Truncate: // We truncate the file after getting the lock + break; + + case FileMode.Append: // Append is the same as OpenOrCreate, except that we'll also separately jump to the end later + case FileMode.OpenOrCreate: + case FileMode.Create: // We truncate the file after getting the lock + flags |= Interop.Sys.OpenFlags.O_CREAT; + break; + + case FileMode.CreateNew: + flags |= (Interop.Sys.OpenFlags.O_CREAT | Interop.Sys.OpenFlags.O_EXCL); + break; + } + + // Translate FileAccess. All possible values map cleanly to corresponding values for open. + switch (access) + { + case FileAccess.Read: + flags |= Interop.Sys.OpenFlags.O_RDONLY; + break; + + case FileAccess.ReadWrite: + flags |= Interop.Sys.OpenFlags.O_RDWR; + break; + + case FileAccess.Write: + flags |= Interop.Sys.OpenFlags.O_WRONLY; + break; + } + + // Handle Inheritable, other FileShare flags are handled by Init + if ((share & FileShare.Inheritable) == 0) + { + flags |= Interop.Sys.OpenFlags.O_CLOEXEC; + } + + // Translate some FileOptions; some just aren't supported, and others will be handled after calling open. + // - Asynchronous: Handled in ctor, setting _useAsync and SafeFileHandle.IsAsync to true + // - DeleteOnClose: Doesn't have a Unix equivalent, but we approximate it in Dispose + // - Encrypted: No equivalent on Unix and is ignored + // - RandomAccess: Implemented after open if posix_fadvise is available + // - SequentialScan: Implemented after open if posix_fadvise is available + // - WriteThrough: Handled here + if ((options & FileOptions.WriteThrough) != 0) + { + flags |= Interop.Sys.OpenFlags.O_SYNC; + } + + return flags; + } + + /// Gets a value indicating whether the current stream supports seeking. + public override bool CanSeek => CanSeekCore(_fileHandle); + + /// Gets a value indicating whether the current stream supports seeking. + /// + /// Separated out of CanSeek to enable making non-virtual call to this logic. + /// We also pass in the file handle to allow the constructor to use this before it stashes the handle. + /// + private bool CanSeekCore(SafeFileHandle fileHandle) + { + if (fileHandle.IsClosed) + { + return false; + } + + if (!_canSeek.HasValue) + { + // Lazily-initialize whether we're able to seek, tested by seeking to our current location. + _canSeek = Interop.Sys.LSeek(fileHandle, 0, Interop.Sys.SeekWhence.SEEK_CUR) >= 0; + } + + return _canSeek.Value; + } + + private long GetLengthInternal() + { + // Get the length of the file as reported by the OS + Interop.Sys.FileStatus status; + CheckFileCall(Interop.Sys.FStat(_fileHandle, out status)); + long length = status.Size; + + // But we may have buffered some data to be written that puts our length + // beyond what the OS is aware of. Update accordingly. + if (_writePos > 0 && _filePosition + _writePos > length) + { + length = _writePos + _filePosition; + } + + return length; + } + + /// Releases the unmanaged resources used by the stream. + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected override void Dispose(bool disposing) + { + try + { + if (_fileHandle != null && !_fileHandle.IsClosed) + { + // Flush any remaining data in the file + try + { + FlushWriteBuffer(); + } + catch (IOException) when (!disposing) + { + // On finalization, ignore failures from trying to flush the write buffer, + // e.g. if this stream is wrapping a pipe and the pipe is now broken. + } + + // If DeleteOnClose was requested when constructed, delete the file now. + // (Unix doesn't directly support DeleteOnClose, so we mimic it here.) + if (_path != null && (_options & FileOptions.DeleteOnClose) != 0) + { + // Since we still have the file open, this will end up deleting + // it (assuming we're the only link to it) once it's closed, but the + // name will be removed immediately. + Interop.Sys.Unlink(_path); // ignore errors; it's valid that the path may no longer exist + } + } + } + finally + { + if (_fileHandle != null && !_fileHandle.IsClosed) + { + _fileHandle.Dispose(); + } + base.Dispose(disposing); + } + } + + /// Flushes the OS buffer. This does not flush the internal read/write buffer. + private void FlushOSBuffer() + { + if (Interop.Sys.FSync(_fileHandle) < 0) + { + Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); + switch (errorInfo.Error) + { + case Interop.Error.EROFS: + case Interop.Error.EINVAL: + case Interop.Error.ENOTSUP: + // Ignore failures due to the FileStream being bound to a special file that + // doesn't support synchronization. In such cases there's nothing to flush. + break; + default: + throw Interop.GetExceptionForIoErrno(errorInfo, _path, isDirectory: false); + } + } + } + + private void FlushWriteBufferForWriteByte() + { + _asyncState?.Wait(); + try { FlushWriteBuffer(); } + finally { _asyncState?.Release(); } + } + + /// Writes any data in the write buffer to the underlying stream and resets the buffer. + private void FlushWriteBuffer() + { + AssertBufferInvariants(); + if (_writePos > 0) + { + WriteNative(new ReadOnlySpan(GetBuffer(), 0, _writePos)); + _writePos = 0; + } + } + + /// Asynchronously clears all buffers for this stream, causing any buffered data to be written to the underlying device. + /// The token to monitor for cancellation requests. + /// A task that represents the asynchronous flush operation. + private Task FlushAsyncInternal(CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + if (_fileHandle.IsClosed) + { + throw Error.GetFileNotOpen(); + } + + // As with Win32FileStream, flush the buffers synchronously to avoid race conditions. + try + { + FlushInternalBuffer(); + } + catch (Exception e) + { + return Task.FromException(e); + } + + // We then separately flush to disk asynchronously. This is only + // necessary if we support writing; otherwise, we're done. + if (CanWrite) + { + return Task.Factory.StartNew( + state => ((FileStream)state).FlushOSBuffer(), + this, + cancellationToken, + TaskCreationOptions.DenyChildAttach, + TaskScheduler.Default); + } + else + { + return Task.CompletedTask; + } + } + + /// Sets the length of this stream to the given value. + /// The new length of the stream. + private void SetLengthInternal(long value) + { + FlushInternalBuffer(); + + if (_appendStart != -1 && value < _appendStart) + { + throw new IOException(SR.IO_SetLengthAppendTruncate); + } + + long origPos = _filePosition; + + VerifyOSHandlePosition(); + + if (_filePosition != value) + { + SeekCore(_fileHandle, value, SeekOrigin.Begin); + } + + CheckFileCall(Interop.Sys.FTruncate(_fileHandle, value)); + + // Return file pointer to where it was before setting length + if (origPos != value) + { + if (origPos < value) + { + SeekCore(_fileHandle, origPos, SeekOrigin.Begin); + } + else + { + SeekCore(_fileHandle, 0, SeekOrigin.End); + } + } + } + + /// Reads a block of bytes from the stream and writes the data in a given buffer. + private int ReadSpan(Span destination) + { + PrepareForReading(); + + // Are there any bytes available in the read buffer? If yes, + // we can just return from the buffer. If the buffer is empty + // or has no more available data in it, we can either refill it + // (and then read from the buffer into the user's buffer) or + // we can just go directly into the user's buffer, if they asked + // for more data than we'd otherwise buffer. + int numBytesAvailable = _readLength - _readPos; + bool readFromOS = false; + if (numBytesAvailable == 0) + { + // If we're not able to seek, then we're not able to rewind the stream (i.e. flushing + // a read buffer), in which case we don't want to use a read buffer. Similarly, if + // the user has asked for more data than we can buffer, we also want to skip the buffer. + if (!CanSeek || (destination.Length >= _bufferLength)) + { + // Read directly into the user's buffer + _readPos = _readLength = 0; + return ReadNative(destination); + } + else + { + // Read into our buffer. + _readLength = numBytesAvailable = ReadNative(GetBuffer()); + _readPos = 0; + if (numBytesAvailable == 0) + { + return 0; + } + + // Note that we did an OS read as part of this Read, so that later + // we don't try to do one again if what's in the buffer doesn't + // meet the user's request. + readFromOS = true; + } + } + + // Now that we know there's data in the buffer, read from it into the user's buffer. + Debug.Assert(numBytesAvailable > 0, "Data must be in the buffer to be here"); + int bytesRead = Math.Min(numBytesAvailable, destination.Length); + new Span(GetBuffer(), _readPos, bytesRead).CopyTo(destination); + _readPos += bytesRead; + + // We may not have had enough data in the buffer to completely satisfy the user's request. + // While Read doesn't require that we return as much data as the user requested (any amount + // up to the requested count is fine), FileStream on Windows tries to do so by doing a + // subsequent read from the file if we tried to satisfy the request with what was in the + // buffer but the buffer contained less than the requested count. To be consistent with that + // behavior, we do the same thing here on Unix. Note that we may still get less the requested + // amount, as the OS may give us back fewer than we request, either due to reaching the end of + // file, or due to its own whims. + if (!readFromOS && bytesRead < destination.Length) + { + Debug.Assert(_readPos == _readLength, "bytesToRead should only be < destination.Length if numBytesAvailable < destination.Length"); + _readPos = _readLength = 0; // no data left in the read buffer + bytesRead += ReadNative(destination.Slice(bytesRead)); + } + + return bytesRead; + } + + /// Unbuffered, reads a block of bytes from the file handle into the given buffer. + /// The buffer into which data from the file is read. + /// + /// The total number of bytes read into the buffer. This might be less than the number of bytes requested + /// if that number of bytes are not currently available, or zero if the end of the stream is reached. + /// + private unsafe int ReadNative(Span buffer) + { + FlushWriteBuffer(); // we're about to read; dump the write buffer + + VerifyOSHandlePosition(); + + int bytesRead; + fixed (byte* bufPtr = &MemoryMarshal.GetReference(buffer)) + { + bytesRead = CheckFileCall(Interop.Sys.Read(_fileHandle, bufPtr, buffer.Length)); + Debug.Assert(bytesRead <= buffer.Length); + } + _filePosition += bytesRead; + return bytesRead; + } + + /// + /// Asynchronously reads a sequence of bytes from the current stream and advances + /// the position within the stream by the number of bytes read. + /// + /// The buffer to write the data into. + /// The token to monitor for cancellation requests. + /// If the operation completes synchronously, the number of bytes read. + /// A task that represents the asynchronous read operation. + private Task ReadAsyncInternal(Memory destination, CancellationToken cancellationToken, out int synchronousResult) + { + Debug.Assert(_useAsyncIO); + + if (!CanRead) // match Windows behavior; this gets thrown synchronously + { + throw Error.GetReadNotSupported(); + } + + // Serialize operations using the semaphore. + Task waitTask = _asyncState.WaitAsync(); + + // If we got ownership immediately, and if there's enough data in our buffer + // to satisfy the full request of the caller, hand back the buffered data. + // While it would be a legal implementation of the Read contract, we don't + // hand back here less than the amount requested so as to match the behavior + // in ReadCore that will make a native call to try to fulfill the remainder + // of the request. + if (waitTask.Status == TaskStatus.RanToCompletion) + { + int numBytesAvailable = _readLength - _readPos; + if (numBytesAvailable >= destination.Length) + { + try + { + PrepareForReading(); + + new Span(GetBuffer(), _readPos, destination.Length).CopyTo(destination.Span); + _readPos += destination.Length; + + synchronousResult = destination.Length; + return null; + } + catch (Exception exc) + { + synchronousResult = 0; + return Task.FromException(exc); + } + finally + { + _asyncState.Release(); + } + } + } + + // Otherwise, issue the whole request asynchronously. + synchronousResult = 0; + _asyncState.Memory = destination; + return waitTask.ContinueWith((t, s) => + { + // The options available on Unix for writing asynchronously to an arbitrary file + // handle typically amount to just using another thread to do the synchronous write, + // which is exactly what this implementation does. This does mean there are subtle + // differences in certain FileStream behaviors between Windows and Unix when multiple + // asynchronous operations are issued against the stream to execute concurrently; on + // Unix the operations will be serialized due to the usage of a semaphore, but the + // position /length information won't be updated until after the write has completed, + // whereas on Windows it may happen before the write has completed. + + Debug.Assert(t.Status == TaskStatus.RanToCompletion); + var thisRef = (FileStream)s; + try + { + Memory memory = thisRef._asyncState.Memory; + thisRef._asyncState.Memory = default(Memory); + return thisRef.ReadSpan(memory.Span); + } + finally { thisRef._asyncState.Release(); } + }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default); + } + + /// Reads from the file handle into the buffer, overwriting anything in it. + private int FillReadBufferForReadByte() + { + _asyncState?.Wait(); + try { return ReadNative(_buffer); } + finally { _asyncState?.Release(); } + } + + /// Writes a block of bytes to the file stream. + /// The buffer containing data to write to the stream. + private void WriteSpan(ReadOnlySpan source) + { + PrepareForWriting(); + + // If no data is being written, nothing more to do. + if (source.Length == 0) + { + return; + } + + // If there's already data in our write buffer, then we need to go through + // our buffer to ensure data isn't corrupted. + if (_writePos > 0) + { + // If there's space remaining in the buffer, then copy as much as + // we can from the user's buffer into ours. + int spaceRemaining = _bufferLength - _writePos; + if (spaceRemaining >= source.Length) + { + source.CopyTo(new Span(GetBuffer()).Slice(_writePos)); + _writePos += source.Length; + return; + } + else if (spaceRemaining > 0) + { + source.Slice(0, spaceRemaining).CopyTo(new Span(GetBuffer()).Slice(_writePos)); + _writePos += spaceRemaining; + source = source.Slice(spaceRemaining); + } + + // At this point, the buffer is full, so flush it out. + FlushWriteBuffer(); + } + + // Our buffer is now empty. If using the buffer would slow things down (because + // the user's looking to write more data than we can store in the buffer), + // skip the buffer. Otherwise, put the remaining data into the buffer. + Debug.Assert(_writePos == 0); + if (source.Length >= _bufferLength) + { + WriteNative(source); + } + else + { + source.CopyTo(new Span(GetBuffer())); + _writePos = source.Length; + } + } + + /// Unbuffered, writes a block of bytes to the file stream. + /// The buffer containing data to write to the stream. + private unsafe void WriteNative(ReadOnlySpan source) + { + VerifyOSHandlePosition(); + + fixed (byte* bufPtr = &MemoryMarshal.GetReference(source)) + { + int offset = 0; + int count = source.Length; + while (count > 0) + { + int bytesWritten = CheckFileCall(Interop.Sys.Write(_fileHandle, bufPtr + offset, count)); + _filePosition += bytesWritten; + offset += bytesWritten; + count -= bytesWritten; + } + } + } + + /// + /// Asynchronously writes a sequence of bytes to the current stream, advances + /// the current position within this stream by the number of bytes written, and + /// monitors cancellation requests. + /// + /// The buffer to write data from. + /// The token to monitor for cancellation requests. + /// A task that represents the asynchronous write operation. + private Task WriteAsyncInternal(ReadOnlyMemory source, CancellationToken cancellationToken) + { + Debug.Assert(_useAsyncIO); + + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + + if (_fileHandle.IsClosed) + throw Error.GetFileNotOpen(); + + if (!CanWrite) // match Windows behavior; this gets thrown synchronously + { + throw Error.GetWriteNotSupported(); + } + + // Serialize operations using the semaphore. + Task waitTask = _asyncState.WaitAsync(); + + // If we got ownership immediately, and if there's enough space in our buffer + // to buffer the entire write request, then do so and we're done. + if (waitTask.Status == TaskStatus.RanToCompletion) + { + int spaceRemaining = _bufferLength - _writePos; + if (spaceRemaining >= source.Length) + { + try + { + PrepareForWriting(); + + source.Span.CopyTo(new Span(GetBuffer(), _writePos, source.Length)); + _writePos += source.Length; + + return Task.CompletedTask; + } + catch (Exception exc) + { + return Task.FromException(exc); + } + finally + { + _asyncState.Release(); + } + } + } + + // Otherwise, issue the whole request asynchronously. + _asyncState.ReadOnlyMemory = source; + return waitTask.ContinueWith((t, s) => + { + // The options available on Unix for writing asynchronously to an arbitrary file + // handle typically amount to just using another thread to do the synchronous write, + // which is exactly what this implementation does. This does mean there are subtle + // differences in certain FileStream behaviors between Windows and Unix when multiple + // asynchronous operations are issued against the stream to execute concurrently; on + // Unix the operations will be serialized due to the usage of a semaphore, but the + // position/length information won't be updated until after the write has completed, + // whereas on Windows it may happen before the write has completed. + + Debug.Assert(t.Status == TaskStatus.RanToCompletion); + var thisRef = (FileStream)s; + try + { + ReadOnlyMemory readOnlyMemory = thisRef._asyncState.ReadOnlyMemory; + thisRef._asyncState.ReadOnlyMemory = default(ReadOnlyMemory); + thisRef.WriteSpan(readOnlyMemory.Span); + } + finally { thisRef._asyncState.Release(); } + }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default); + } + + /// Sets the current position of this stream to the given value. + /// The point relative to origin from which to begin seeking. + /// + /// Specifies the beginning, the end, or the current position as a reference + /// point for offset, using a value of type SeekOrigin. + /// + /// The new position in the stream. + public override long Seek(long offset, SeekOrigin origin) + { + if (origin < SeekOrigin.Begin || origin > SeekOrigin.End) + { + throw new ArgumentException(SR.Argument_InvalidSeekOrigin, nameof(origin)); + } + if (_fileHandle.IsClosed) + { + throw Error.GetFileNotOpen(); + } + if (!CanSeek) + { + throw Error.GetSeekNotSupported(); + } + + VerifyOSHandlePosition(); + + // Flush our write/read buffer. FlushWrite will output any write buffer we have and reset _bufferWritePos. + // We don't call FlushRead, as that will do an unnecessary seek to rewind the read buffer, and since we're + // about to seek and update our position, we can simply update the offset as necessary and reset our read + // position and length to 0. (In the future, for some simple cases we could potentially add an optimization + // here to just move data around in the buffer for short jumps, to avoid re-reading the data from disk.) + FlushWriteBuffer(); + if (origin == SeekOrigin.Current) + { + offset -= (_readLength - _readPos); + } + _readPos = _readLength = 0; + + // Keep track of where we were, in case we're in append mode and need to verify + long oldPos = 0; + if (_appendStart >= 0) + { + oldPos = SeekCore(_fileHandle, 0, SeekOrigin.Current); + } + + // Jump to the new location + long pos = SeekCore(_fileHandle, offset, origin); + + // Prevent users from overwriting data in a file that was opened in append mode. + if (_appendStart != -1 && pos < _appendStart) + { + SeekCore(_fileHandle, oldPos, SeekOrigin.Begin); + throw new IOException(SR.IO_SeekAppendOverwrite); + } + + // Return the new position + return pos; + } + + /// Sets the current position of this stream to the given value. + /// The point relative to origin from which to begin seeking. + /// + /// Specifies the beginning, the end, or the current position as a reference + /// point for offset, using a value of type SeekOrigin. + /// + /// The new position in the stream. + private long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin) + { + Debug.Assert(!fileHandle.IsClosed && (GetType() != typeof(FileStream) || CanSeekCore(fileHandle))); // verify that we can seek, but only if CanSeek won't be a virtual call (which could happen in the ctor) + Debug.Assert(origin >= SeekOrigin.Begin && origin <= SeekOrigin.End); + + long pos = CheckFileCall(Interop.Sys.LSeek(fileHandle, offset, (Interop.Sys.SeekWhence)(int)origin)); // SeekOrigin values are the same as Interop.libc.SeekWhence values + _filePosition = pos; + return pos; + } + + private long CheckFileCall(long result, bool ignoreNotSupported = false) + { + if (result < 0) + { + Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); + if (!(ignoreNotSupported && errorInfo.Error == Interop.Error.ENOTSUP)) + { + throw Interop.GetExceptionForIoErrno(errorInfo, _path, isDirectory: false); + } + } + + return result; + } + + private int CheckFileCall(int result, bool ignoreNotSupported = false) + { + CheckFileCall((long)result, ignoreNotSupported); + + return result; + } + + /// State used when the stream is in async mode. + private sealed class AsyncState : SemaphoreSlim + { + internal ReadOnlyMemory ReadOnlyMemory; + internal Memory Memory; + + /// Initialize the AsyncState. + internal AsyncState() : base(initialCount: 1, maxCount: 1) { } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/FileStream.Win32.cs b/external/corefx/src/Common/src/CoreLib/System/IO/FileStream.Win32.cs new file mode 100644 index 0000000000..4b75ad6dad --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/FileStream.Win32.cs @@ -0,0 +1,47 @@ +// 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 Microsoft.Win32.SafeHandles; + +namespace System.IO +{ + public partial class FileStream : Stream + { + private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) + { + return CreateFileOpenHandle(mode, share, options); + } + + private unsafe SafeFileHandle CreateFileOpenHandle(FileMode mode, FileShare share, FileOptions options) + { + Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share); + + int fAccess = + ((_access & FileAccess.Read) == FileAccess.Read ? GENERIC_READ : 0) | + ((_access & FileAccess.Write) == FileAccess.Write ? GENERIC_WRITE : 0); + + // Our Inheritable bit was stolen from Windows, but should be set in + // the security attributes class. Don't leave this bit set. + share &= ~FileShare.Inheritable; + + // Must use a valid Win32 constant here... + if (mode == FileMode.Append) + mode = FileMode.OpenOrCreate; + + int flagsAndAttributes = (int)options; + + // For mitigating local elevation of privilege attack through named pipes + // make sure we always call CreateFile with SECURITY_ANONYMOUS so that the + // named pipe server can't impersonate a high privileged client security context + // (note that this is the effective default on CreateFile2) + flagsAndAttributes |= (Interop.Kernel32.SecurityOptions.SECURITY_SQOS_PRESENT | Interop.Kernel32.SecurityOptions.SECURITY_ANONYMOUS); + + using (DisableMediaInsertionPrompt.Create()) + { + return ValidateFileHandle( + Interop.Kernel32.CreateFile(_path, fAccess, share, ref secAttrs, mode, flagsAndAttributes, IntPtr.Zero)); + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/FileStream.WinRT.cs b/external/corefx/src/Common/src/CoreLib/System/IO/FileStream.WinRT.cs new file mode 100644 index 0000000000..304088eea7 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/FileStream.WinRT.cs @@ -0,0 +1,44 @@ +// 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 Microsoft.Win32.SafeHandles; +using System.Runtime.InteropServices; + +namespace System.IO +{ + public partial class FileStream : Stream + { + private unsafe SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) + { + Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share); + + int access = + ((_access & FileAccess.Read) == FileAccess.Read ? GENERIC_READ : 0) | + ((_access & FileAccess.Write) == FileAccess.Write ? GENERIC_WRITE : 0); + + // Our Inheritable bit was stolen from Windows, but should be set in + // the security attributes class. Don't leave this bit set. + share &= ~FileShare.Inheritable; + + // Must use a valid Win32 constant here... + if (mode == FileMode.Append) + mode = FileMode.OpenOrCreate; + + Interop.Kernel32.CREATEFILE2_EXTENDED_PARAMETERS parameters = new Interop.Kernel32.CREATEFILE2_EXTENDED_PARAMETERS(); + parameters.dwSize = (uint)sizeof(Interop.Kernel32.CREATEFILE2_EXTENDED_PARAMETERS); + parameters.dwFileFlags = (uint)options; + parameters.lpSecurityAttributes = &secAttrs; + + using (DisableMediaInsertionPrompt.Create()) + { + return ValidateFileHandle(Interop.Kernel32.CreateFile2( + lpFileName: _path, + dwDesiredAccess: access, + dwShareMode: share, + dwCreationDisposition: mode, + pCreateExParams: ref parameters)); + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/FileStream.Windows.cs b/external/corefx/src/Common/src/CoreLib/System/IO/FileStream.Windows.cs new file mode 100644 index 0000000000..477b9430fc --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/FileStream.Windows.cs @@ -0,0 +1,1668 @@ +// 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.Buffers; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; +using System.Runtime.CompilerServices; + +/* + * Win32FileStream supports different modes of accessing the disk - async mode + * and sync mode. They are two completely different codepaths in the + * sync & async methods (i.e. Read/Write vs. ReadAsync/WriteAsync). File + * handles in NT can be opened in only sync or overlapped (async) mode, + * and we have to deal with this pain. Stream has implementations of + * the sync methods in terms of the async ones, so we'll + * call through to our base class to get those methods when necessary. + * + * Also buffering is added into Win32FileStream as well. Folded in the + * code from BufferedStream, so all the comments about it being mostly + * aggressive (and the possible perf improvement) apply to Win32FileStream as + * well. Also added some buffering to the async code paths. + * + * Class Invariants: + * The class has one buffer, shared for reading & writing. It can only be + * used for one or the other at any point in time - not both. The following + * should be true: + * 0 <= _readPos <= _readLen < _bufferSize + * 0 <= _writePos < _bufferSize + * _readPos == _readLen && _readPos > 0 implies the read buffer is valid, + * but we're at the end of the buffer. + * _readPos == _readLen == 0 means the read buffer contains garbage. + * Either _writePos can be greater than 0, or _readLen & _readPos can be + * greater than zero, but neither can be greater than zero at the same time. + * + */ + +namespace System.IO +{ + public partial class FileStream : Stream + { + private bool _canSeek; + private bool _isPipe; // Whether to disable async buffering code. + private long _appendStart; // When appending, prevent overwriting file. + + private static unsafe IOCompletionCallback s_ioCallback = FileStreamCompletionSource.IOCallback; + + private Task _activeBufferOperation = null; // tracks in-progress async ops using the buffer + private PreAllocatedOverlapped _preallocatedOverlapped; // optimization for async ops to avoid per-op allocations + private FileStreamCompletionSource _currentOverlappedOwner; // async op currently using the preallocated overlapped + + private void Init(FileMode mode, FileShare share) + { + // Disallow access to all non-file devices from the Win32FileStream + // constructors that take a String. Everyone else can call + // CreateFile themselves then use the constructor that takes an + // IntPtr. Disallows "con:", "com1:", "lpt1:", etc. + int fileType = Interop.Kernel32.GetFileType(_fileHandle); + if (fileType != Interop.Kernel32.FileTypes.FILE_TYPE_DISK) + { + _fileHandle.Dispose(); + throw new NotSupportedException(SR.NotSupported_FileStreamOnNonFiles); + } + + // This is necessary for async IO using IO Completion ports via our + // managed Threadpool API's. This (theoretically) calls the OS's + // BindIoCompletionCallback method, and passes in a stub for the + // LPOVERLAPPED_COMPLETION_ROUTINE. This stub looks at the Overlapped + // struct for this request and gets a delegate to a managed callback + // from there, which it then calls on a threadpool thread. (We allocate + // our native OVERLAPPED structs 2 pointers too large and store EE state + // & GC handles there, one to an IAsyncResult, the other to a delegate.) + if (_useAsyncIO) + { + try + { + _fileHandle.ThreadPoolBinding = ThreadPoolBoundHandle.BindHandle(_fileHandle); + } + catch (ArgumentException ex) + { + throw new IOException(SR.IO_BindHandleFailed, ex); + } + finally + { + if (_fileHandle.ThreadPoolBinding == null) + { + // We should close the handle so that the handle is not open until SafeFileHandle GC + Debug.Assert(!_exposedHandle, "Are we closing handle that we exposed/not own, how?"); + _fileHandle.Dispose(); + } + } + } + + _canSeek = true; + + // For Append mode... + if (mode == FileMode.Append) + { + _appendStart = SeekCore(_fileHandle, 0, SeekOrigin.End); + } + else + { + _appendStart = -1; + } + } + + private void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO) + { +#if DEBUG + bool hadBinding = handle.ThreadPoolBinding != null; + + try + { +#endif + InitFromHandleImpl(handle, access, useAsyncIO); +#if DEBUG + } + catch + { + Debug.Assert(hadBinding || handle.ThreadPoolBinding == null, "We should never error out with a ThreadPoolBinding we've added"); + throw; + } +#endif + } + + private void InitFromHandleImpl(SafeFileHandle handle, FileAccess access, bool useAsyncIO) + { + int handleType = Interop.Kernel32.GetFileType(handle); + Debug.Assert(handleType == Interop.Kernel32.FileTypes.FILE_TYPE_DISK || handleType == Interop.Kernel32.FileTypes.FILE_TYPE_PIPE || handleType == Interop.Kernel32.FileTypes.FILE_TYPE_CHAR, "FileStream was passed an unknown file type!"); + + _canSeek = handleType == Interop.Kernel32.FileTypes.FILE_TYPE_DISK; + _isPipe = handleType == Interop.Kernel32.FileTypes.FILE_TYPE_PIPE; + + // This is necessary for async IO using IO Completion ports via our + // managed Threadpool API's. This calls the OS's + // BindIoCompletionCallback method, and passes in a stub for the + // LPOVERLAPPED_COMPLETION_ROUTINE. This stub looks at the Overlapped + // struct for this request and gets a delegate to a managed callback + // from there, which it then calls on a threadpool thread. (We allocate + // our native OVERLAPPED structs 2 pointers too large and store EE + // state & a handle to a delegate there.) + // + // If, however, we've already bound this file handle to our completion port, + // don't try to bind it again because it will fail. A handle can only be + // bound to a single completion port at a time. + if (useAsyncIO && !(handle.IsAsync ?? false)) + { + try + { + handle.ThreadPoolBinding = ThreadPoolBoundHandle.BindHandle(handle); + } + catch (Exception ex) + { + // If you passed in a synchronous handle and told us to use + // it asynchronously, throw here. + throw new ArgumentException(SR.Arg_HandleNotAsync, nameof(handle), ex); + } + } + else if (!useAsyncIO) + { + if (handleType != Interop.Kernel32.FileTypes.FILE_TYPE_PIPE) + VerifyHandleIsSync(handle, access); + } + + if (_canSeek) + SeekCore(handle, 0, SeekOrigin.Current); + else + _filePosition = 0; + } + + private unsafe static Interop.Kernel32.SECURITY_ATTRIBUTES GetSecAttrs(FileShare share) + { + Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = default(Interop.Kernel32.SECURITY_ATTRIBUTES); + if ((share & FileShare.Inheritable) != 0) + { + secAttrs = new Interop.Kernel32.SECURITY_ATTRIBUTES(); + secAttrs.nLength = (uint)sizeof(Interop.Kernel32.SECURITY_ATTRIBUTES); + + secAttrs.bInheritHandle = Interop.BOOL.TRUE; + } + return secAttrs; + } + + // Verifies that this handle supports synchronous IO operations (unless you + // didn't open it for either reading or writing). + private unsafe static void VerifyHandleIsSync(SafeFileHandle handle, FileAccess access) + { + // Do NOT use this method on pipes. Reading or writing to a pipe may + // cause an app to block incorrectly, introducing a deadlock (depending + // on whether a write will wake up an already-blocked thread or this + // Win32FileStream's thread). + Debug.Assert(Interop.Kernel32.GetFileType(handle) != Interop.Kernel32.FileTypes.FILE_TYPE_PIPE); + + byte* bytes = stackalloc byte[1]; + int numBytesReadWritten; + int r = -1; + + // If the handle is a pipe, ReadFile will block until there + // has been a write on the other end. We'll just have to deal with it, + // For the read end of a pipe, you can mess up and + // accidentally read synchronously from an async pipe. + if ((access & FileAccess.Read) != 0) // don't use the virtual CanRead or CanWrite, as this may be used in the ctor + { + r = Interop.Kernel32.ReadFile(handle, bytes, 0, out numBytesReadWritten, IntPtr.Zero); + } + else if ((access & FileAccess.Write) != 0) // don't use the virtual CanRead or CanWrite, as this may be used in the ctor + { + r = Interop.Kernel32.WriteFile(handle, bytes, 0, out numBytesReadWritten, IntPtr.Zero); + } + + if (r == 0) + { + int errorCode = Marshal.GetLastWin32Error(); + switch (errorCode) + { + case Interop.Errors.ERROR_INVALID_PARAMETER: + throw new ArgumentException(SR.Arg_HandleNotSync, "handle"); + case Interop.Errors.ERROR_INVALID_HANDLE: + throw Win32Marshal.GetExceptionForWin32Error(errorCode); + } + } + } + + private bool HasActiveBufferOperation + { + get { return _activeBufferOperation != null && !_activeBufferOperation.IsCompleted; } + } + + public override bool CanSeek + { + get { return _canSeek; } + } + + private unsafe long GetLengthInternal() + { + Interop.Kernel32.FILE_STANDARD_INFO info = new Interop.Kernel32.FILE_STANDARD_INFO(); + + if (!Interop.Kernel32.GetFileInformationByHandleEx(_fileHandle, Interop.Kernel32.FILE_INFO_BY_HANDLE_CLASS.FileStandardInfo, out info, (uint)sizeof(Interop.Kernel32.FILE_STANDARD_INFO))) + throw Win32Marshal.GetExceptionForLastWin32Error(); + long len = info.EndOfFile; + // If we're writing near the end of the file, we must include our + // internal buffer in our Length calculation. Don't flush because + // we use the length of the file in our async write method. + if (_writePos > 0 && _filePosition + _writePos > len) + len = _writePos + _filePosition; + return len; + } + + protected override void Dispose(bool disposing) + { + // Nothing will be done differently based on whether we are + // disposing vs. finalizing. This is taking advantage of the + // weak ordering between normal finalizable objects & critical + // finalizable objects, which I included in the SafeHandle + // design for Win32FileStream, which would often "just work" when + // finalized. + try + { + if (_fileHandle != null && !_fileHandle.IsClosed) + { + // Flush data to disk iff we were writing. After + // thinking about this, we also don't need to flush + // our read position, regardless of whether the handle + // was exposed to the user. They probably would NOT + // want us to do this. + if (_writePos > 0) + { + FlushWriteBuffer(!disposing); + } + } + } + finally + { + if (_fileHandle != null && !_fileHandle.IsClosed) + { + if (_fileHandle.ThreadPoolBinding != null) + _fileHandle.ThreadPoolBinding.Dispose(); + + _fileHandle.Dispose(); + } + + if (_preallocatedOverlapped != null) + _preallocatedOverlapped.Dispose(); + + _canSeek = false; + + // Don't set the buffer to null, to avoid a NullReferenceException + // when users have a race condition in their code (i.e. they call + // Close when calling another method on Stream like Read). + //_buffer = null; + base.Dispose(disposing); + } + } + + private void FlushOSBuffer() + { + if (!Interop.Kernel32.FlushFileBuffers(_fileHandle)) + { + throw Win32Marshal.GetExceptionForLastWin32Error(); + } + } + + // Returns a task that flushes the internal write buffer + private Task FlushWriteAsync(CancellationToken cancellationToken) + { + Debug.Assert(_useAsyncIO); + Debug.Assert(_readPos == 0 && _readLength == 0, "FileStream: Read buffer must be empty in FlushWriteAsync!"); + + // If the buffer is already flushed, don't spin up the OS write + if (_writePos == 0) return Task.CompletedTask; + + Task flushTask = WriteAsyncInternalCore(new ReadOnlyMemory(GetBuffer(), 0, _writePos), cancellationToken); + _writePos = 0; + + // Update the active buffer operation + _activeBufferOperation = HasActiveBufferOperation ? + Task.WhenAll(_activeBufferOperation, flushTask) : + flushTask; + + return flushTask; + } + + private void FlushWriteBufferForWriteByte() => FlushWriteBuffer(); + + // Writes are buffered. Anytime the buffer fills up + // (_writePos + delta > _bufferSize) or the buffer switches to reading + // and there is left over data (_writePos > 0), this function must be called. + private void FlushWriteBuffer(bool calledFromFinalizer = false) + { + if (_writePos == 0) return; + Debug.Assert(_readPos == 0 && _readLength == 0, "FileStream: Read buffer must be empty in FlushWrite!"); + + if (_useAsyncIO) + { + Task writeTask = FlushWriteAsync(CancellationToken.None); + // With our Whidbey async IO & overlapped support for AD unloads, + // we don't strictly need to block here to release resources + // since that support takes care of the pinning & freeing the + // overlapped struct. We need to do this when called from + // Close so that the handle is closed when Close returns, but + // we don't need to call EndWrite from the finalizer. + // Additionally, if we do call EndWrite, we block forever + // because AD unloads prevent us from running the managed + // callback from the IO completion port. Blocking here when + // called from the finalizer during AD unload is clearly wrong, + // but we can't use any sort of test for whether the AD is + // unloading because if we weren't unloading, an AD unload + // could happen on a separate thread before we call EndWrite. + if (!calledFromFinalizer) + { + writeTask.GetAwaiter().GetResult(); + } + } + else + { + WriteCore(new ReadOnlySpan(GetBuffer(), 0, _writePos)); + } + + _writePos = 0; + } + + private void SetLengthInternal(long value) + { + // Handle buffering updates. + if (_writePos > 0) + { + FlushWriteBuffer(); + } + else if (_readPos < _readLength) + { + FlushReadBuffer(); + } + _readPos = 0; + _readLength = 0; + + if (_appendStart != -1 && value < _appendStart) + throw new IOException(SR.IO_SetLengthAppendTruncate); + SetLengthCore(value); + } + + // We absolutely need this method broken out so that WriteInternalCoreAsync can call + // a method without having to go through buffering code that might call FlushWrite. + private void SetLengthCore(long value) + { + Debug.Assert(value >= 0, "value >= 0"); + long origPos = _filePosition; + + VerifyOSHandlePosition(); + if (_filePosition != value) + SeekCore(_fileHandle, value, SeekOrigin.Begin); + if (!Interop.Kernel32.SetEndOfFile(_fileHandle)) + { + int errorCode = Marshal.GetLastWin32Error(); + if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_FileLengthTooBig); + throw Win32Marshal.GetExceptionForWin32Error(errorCode); + } + // Return file pointer to where it was before setting length + if (origPos != value) + { + if (origPos < value) + SeekCore(_fileHandle, origPos, SeekOrigin.Begin); + else + SeekCore(_fileHandle, 0, SeekOrigin.End); + } + } + + // Instance method to help code external to this MarshalByRefObject avoid + // accessing its fields by ref. This avoids a compiler warning. + private FileStreamCompletionSource CompareExchangeCurrentOverlappedOwner(FileStreamCompletionSource newSource, FileStreamCompletionSource existingSource) => Interlocked.CompareExchange(ref _currentOverlappedOwner, newSource, existingSource); + + private int ReadSpan(Span destination) + { + Debug.Assert(!_useAsyncIO, "Must only be used when in synchronous mode"); + Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), + "We're either reading or writing, but not both."); + + bool isBlocked = false; + int n = _readLength - _readPos; + // if the read buffer is empty, read into either user's array or our + // buffer, depending on number of bytes user asked for and buffer size. + if (n == 0) + { + if (!CanRead) throw Error.GetReadNotSupported(); + if (_writePos > 0) FlushWriteBuffer(); + if (!CanSeek || (destination.Length >= _bufferLength)) + { + n = ReadNative(destination); + // Throw away read buffer. + _readPos = 0; + _readLength = 0; + return n; + } + n = ReadNative(GetBuffer()); + if (n == 0) return 0; + isBlocked = n < _bufferLength; + _readPos = 0; + _readLength = n; + } + // Now copy min of count or numBytesAvailable (i.e. near EOF) to array. + if (n > destination.Length) n = destination.Length; + new ReadOnlySpan(GetBuffer(), _readPos, n).CopyTo(destination); + _readPos += n; + + // We may have read less than the number of bytes the user asked + // for, but that is part of the Stream contract. Reading again for + // more data may cause us to block if we're using a device with + // no clear end of file, such as a serial port or pipe. If we + // blocked here & this code was used with redirected pipes for a + // process's standard output, this can lead to deadlocks involving + // two processes. But leave this here for files to avoid what would + // probably be a breaking change. -- + + // If we are reading from a device with no clear EOF like a + // serial port or a pipe, this will cause us to block incorrectly. + if (!_isPipe) + { + // If we hit the end of the buffer and didn't have enough bytes, we must + // read some more from the underlying stream. However, if we got + // fewer bytes from the underlying stream than we asked for (i.e. we're + // probably blocked), don't ask for more bytes. + if (n < destination.Length && !isBlocked) + { + Debug.Assert(_readPos == _readLength, "Read buffer should be empty!"); + int moreBytesRead = ReadNative(destination.Slice(n)); + n += moreBytesRead; + // We've just made our buffer inconsistent with our position + // pointer. We must throw away the read buffer. + _readPos = 0; + _readLength = 0; + } + } + + return n; + } + + [Conditional("DEBUG")] + private void AssertCanRead() + { + Debug.Assert(!_fileHandle.IsClosed, "!_fileHandle.IsClosed"); + Debug.Assert(CanRead, "CanRead"); + } + + /// Reads from the file handle into the buffer, overwriting anything in it. + private int FillReadBufferForReadByte() => + _useAsyncIO ? + ReadNativeAsync(new Memory(_buffer), 0, CancellationToken.None).GetAwaiter().GetResult() : + ReadNative(_buffer); + + private unsafe int ReadNative(Span buffer) + { + Debug.Assert(!_useAsyncIO, $"{nameof(ReadNative)} doesn't work on asynchronous file streams."); + AssertCanRead(); + + // Make sure we are reading from the right spot + VerifyOSHandlePosition(); + + int errorCode = 0; + int r = ReadFileNative(_fileHandle, buffer, null, out errorCode); + + if (r == -1) + { + // For pipes, ERROR_BROKEN_PIPE is the normal end of the pipe. + if (errorCode == ERROR_BROKEN_PIPE) + { + r = 0; + } + else + { + if (errorCode == ERROR_INVALID_PARAMETER) + throw new ArgumentException(SR.Arg_HandleNotSync, "_fileHandle"); + + throw Win32Marshal.GetExceptionForWin32Error(errorCode); + } + } + Debug.Assert(r >= 0, "FileStream's ReadNative is likely broken."); + _filePosition += r; + + return r; + } + + public override long Seek(long offset, SeekOrigin origin) + { + if (origin < SeekOrigin.Begin || origin > SeekOrigin.End) + throw new ArgumentException(SR.Argument_InvalidSeekOrigin, nameof(origin)); + if (_fileHandle.IsClosed) throw Error.GetFileNotOpen(); + if (!CanSeek) throw Error.GetSeekNotSupported(); + + Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both."); + + // If we've got bytes in our buffer to write, write them out. + // If we've read in and consumed some bytes, we'll have to adjust + // our seek positions ONLY IF we're seeking relative to the current + // position in the stream. This simulates doing a seek to the new + // position, then a read for the number of bytes we have in our buffer. + if (_writePos > 0) + { + FlushWriteBuffer(); + } + else if (origin == SeekOrigin.Current) + { + // Don't call FlushRead here, which would have caused an infinite + // loop. Simply adjust the seek origin. This isn't necessary + // if we're seeking relative to the beginning or end of the stream. + offset -= (_readLength - _readPos); + } + _readPos = _readLength = 0; + + // Verify that internal position is in sync with the handle + VerifyOSHandlePosition(); + + long oldPos = _filePosition + (_readPos - _readLength); + long pos = SeekCore(_fileHandle, offset, origin); + + // Prevent users from overwriting data in a file that was opened in + // append mode. + if (_appendStart != -1 && pos < _appendStart) + { + SeekCore(_fileHandle, oldPos, SeekOrigin.Begin); + throw new IOException(SR.IO_SeekAppendOverwrite); + } + + // We now must update the read buffer. We can in some cases simply + // update _readPos within the buffer, copy around the buffer so our + // Position property is still correct, and avoid having to do more + // reads from the disk. Otherwise, discard the buffer's contents. + if (_readLength > 0) + { + // We can optimize the following condition: + // oldPos - _readPos <= pos < oldPos + _readLen - _readPos + if (oldPos == pos) + { + if (_readPos > 0) + { + Buffer.BlockCopy(GetBuffer(), _readPos, GetBuffer(), 0, _readLength - _readPos); + _readLength -= _readPos; + _readPos = 0; + } + // If we still have buffered data, we must update the stream's + // position so our Position property is correct. + if (_readLength > 0) + SeekCore(_fileHandle, _readLength, SeekOrigin.Current); + } + else if (oldPos - _readPos < pos && pos < oldPos + _readLength - _readPos) + { + int diff = (int)(pos - oldPos); + Buffer.BlockCopy(GetBuffer(), _readPos + diff, GetBuffer(), 0, _readLength - (_readPos + diff)); + _readLength -= (_readPos + diff); + _readPos = 0; + if (_readLength > 0) + SeekCore(_fileHandle, _readLength, SeekOrigin.Current); + } + else + { + // Lose the read buffer. + _readPos = 0; + _readLength = 0; + } + Debug.Assert(_readLength >= 0 && _readPos <= _readLength, "_readLen should be nonnegative, and _readPos should be less than or equal _readLen"); + Debug.Assert(pos == Position, "Seek optimization: pos != Position! Buffer math was mangled."); + } + return pos; + } + + // This doesn't do argument checking. Necessary for SetLength, which must + // set the file pointer beyond the end of the file. This will update the + // internal position + private long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, bool closeInvalidHandle = false) + { + Debug.Assert(!fileHandle.IsClosed && _canSeek, "!fileHandle.IsClosed && _canSeek"); + Debug.Assert(origin >= SeekOrigin.Begin && origin <= SeekOrigin.End, "origin >= SeekOrigin.Begin && origin <= SeekOrigin.End"); + + long ret = 0; + + if (!Interop.Kernel32.SetFilePointerEx(fileHandle, offset, out ret, (uint)origin)) + { + if (closeInvalidHandle) + { + throw Win32Marshal.GetExceptionForWin32Error(GetLastWin32ErrorAndDisposeHandleIfInvalid(throwIfInvalidHandle: false)); + } + else + { + throw Win32Marshal.GetExceptionForWin32Error(Marshal.GetLastWin32Error()); + } + } + + _filePosition = ret; + return ret; + } + + partial void OnBufferAllocated() + { + Debug.Assert(_buffer != null); + Debug.Assert(_preallocatedOverlapped == null); + + if (_useAsyncIO) + _preallocatedOverlapped = new PreAllocatedOverlapped(s_ioCallback, this, _buffer); + } + + private void WriteSpan(ReadOnlySpan source) + { + Debug.Assert(!_useAsyncIO, "Must only be used when in synchronous mode"); + + if (_writePos == 0) + { + // Ensure we can write to the stream, and ready buffer for writing. + if (!CanWrite) throw Error.GetWriteNotSupported(); + if (_readPos < _readLength) FlushReadBuffer(); + _readPos = 0; + _readLength = 0; + } + + // If our buffer has data in it, copy data from the user's array into + // the buffer, and if we can fit it all there, return. Otherwise, write + // the buffer to disk and copy any remaining data into our buffer. + // The assumption here is memcpy is cheaper than disk (or net) IO. + // (10 milliseconds to disk vs. ~20-30 microseconds for a 4K memcpy) + // So the extra copying will reduce the total number of writes, in + // non-pathological cases (i.e. write 1 byte, then write for the buffer + // size repeatedly) + if (_writePos > 0) + { + int numBytes = _bufferLength - _writePos; // space left in buffer + if (numBytes > 0) + { + if (numBytes >= source.Length) + { + source.CopyTo(new Span(GetBuffer()).Slice(_writePos)); + _writePos += source.Length; + return; + } + else + { + source.Slice(0, numBytes).CopyTo(new Span(GetBuffer()).Slice(_writePos)); + _writePos += numBytes; + source = source.Slice(numBytes); + } + } + // Reset our buffer. We essentially want to call FlushWrite + // without calling Flush on the underlying Stream. + + WriteCore(new ReadOnlySpan(GetBuffer(), 0, _writePos)); + _writePos = 0; + } + + // If the buffer would slow writes down, avoid buffer completely. + if (source.Length >= _bufferLength) + { + Debug.Assert(_writePos == 0, "FileStream cannot have buffered data to write here! Your stream will be corrupted."); + WriteCore(source); + return; + } + else if (source.Length == 0) + { + return; // Don't allocate a buffer then call memcpy for 0 bytes. + } + + // Copy remaining bytes into buffer, to write at a later date. + source.CopyTo(new Span(GetBuffer()).Slice(_writePos)); + _writePos = source.Length; + return; + } + + private unsafe void WriteCore(ReadOnlySpan source) + { + Debug.Assert(!_useAsyncIO); + Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); + Debug.Assert(CanWrite, "_parent.CanWrite"); + Debug.Assert(_readPos == _readLength, "_readPos == _readLen"); + + // Make sure we are writing to the position that we think we are + VerifyOSHandlePosition(); + + int errorCode = 0; + int r = WriteFileNative(_fileHandle, source, null, out errorCode); + + if (r == -1) + { + // For pipes, ERROR_NO_DATA is not an error, but the pipe is closing. + if (errorCode == ERROR_NO_DATA) + { + r = 0; + } + else + { + // ERROR_INVALID_PARAMETER may be returned for writes + // where the position is too large or for synchronous writes + // to a handle opened asynchronously. + if (errorCode == ERROR_INVALID_PARAMETER) + throw new IOException(SR.IO_FileTooLongOrHandleNotSync); + throw Win32Marshal.GetExceptionForWin32Error(errorCode); + } + } + Debug.Assert(r >= 0, "FileStream's WriteCore is likely broken."); + _filePosition += r; + return; + } + + private Task ReadAsyncInternal(Memory destination, CancellationToken cancellationToken, out int synchronousResult) + { + Debug.Assert(_useAsyncIO); + if (!CanRead) throw Error.GetReadNotSupported(); + + Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both."); + + if (_isPipe) + { + // Pipes are tricky, at least when you have 2 different pipes + // that you want to use simultaneously. When redirecting stdout + // & stderr with the Process class, it's easy to deadlock your + // parent & child processes when doing writes 4K at a time. The + // OS appears to use a 4K buffer internally. If you write to a + // pipe that is full, you will block until someone read from + // that pipe. If you try reading from an empty pipe and + // Win32FileStream's ReadAsync blocks waiting for data to fill it's + // internal buffer, you will be blocked. In a case where a child + // process writes to stdout & stderr while a parent process tries + // reading from both, you can easily get into a deadlock here. + // To avoid this deadlock, don't buffer when doing async IO on + // pipes. But don't completely ignore buffered data either. + if (_readPos < _readLength) + { + int n = Math.Min(_readLength - _readPos, destination.Length); + new Span(GetBuffer(), _readPos, n).CopyTo(destination.Span); + _readPos += n; + synchronousResult = n; + return null; + } + else + { + Debug.Assert(_writePos == 0, "Win32FileStream must not have buffered write data here! Pipes should be unidirectional."); + synchronousResult = 0; + return ReadNativeAsync(destination, 0, cancellationToken); + } + } + + Debug.Assert(!_isPipe, "Should not be a pipe."); + + // Handle buffering. + if (_writePos > 0) FlushWriteBuffer(); + if (_readPos == _readLength) + { + // I can't see how to handle buffering of async requests when + // filling the buffer asynchronously, without a lot of complexity. + // The problems I see are issuing an async read, we do an async + // read to fill the buffer, then someone issues another read + // (either synchronously or asynchronously) before the first one + // returns. This would involve some sort of complex buffer locking + // that we probably don't want to get into, at least not in V1. + // If we did a sync read to fill the buffer, we could avoid the + // problem, and any async read less than 64K gets turned into a + // synchronous read by NT anyways... -- + + if (destination.Length < _bufferLength) + { + Task readTask = ReadNativeAsync(new Memory(GetBuffer()), 0, cancellationToken); + _readLength = readTask.GetAwaiter().GetResult(); + int n = Math.Min(_readLength, destination.Length); + new Span(GetBuffer(), 0, n).CopyTo(destination.Span); + _readPos = n; + + synchronousResult = n; + return null; + } + else + { + // Here we're making our position pointer inconsistent + // with our read buffer. Throw away the read buffer's contents. + _readPos = 0; + _readLength = 0; + synchronousResult = 0; + return ReadNativeAsync(destination, 0, cancellationToken); + } + } + else + { + int n = Math.Min(_readLength - _readPos, destination.Length); + new Span(GetBuffer(), _readPos, n).CopyTo(destination.Span); + _readPos += n; + + if (n == destination.Length) + { + // Return a completed task + synchronousResult = n; + return null; + } + else + { + // For streams with no clear EOF like serial ports or pipes + // we cannot read more data without causing an app to block + // incorrectly. Pipes don't go down this path + // though. This code needs to be fixed. + // Throw away read buffer. + _readPos = 0; + _readLength = 0; + synchronousResult = 0; + return ReadNativeAsync(destination.Slice(n), n, cancellationToken); + } + } + } + + unsafe private Task ReadNativeAsync(Memory destination, int numBufferedBytesRead, CancellationToken cancellationToken) + { + AssertCanRead(); + Debug.Assert(_useAsyncIO, "ReadNativeAsync doesn't work on synchronous file streams!"); + + // Create and store async stream class library specific data in the async result + FileStreamCompletionSource completionSource = destination.TryGetArray(out ArraySegment memoryArray) ? + new FileStreamCompletionSource(this, numBufferedBytesRead, memoryArray.Array) : + new MemoryFileStreamCompletionSource(this, numBufferedBytesRead, destination); + NativeOverlapped* intOverlapped = completionSource.Overlapped; + + // Calculate position in the file we should be at after the read is done + if (CanSeek) + { + long len = Length; + + // Make sure we are reading from the position that we think we are + VerifyOSHandlePosition(); + + if (_filePosition + destination.Length > len) + { + if (_filePosition <= len) + { + destination = destination.Slice(0, (int)(len - _filePosition)); + } + else + { + destination = default(Memory); + } + } + + // Now set the position to read from in the NativeOverlapped struct + // For pipes, we should leave the offset fields set to 0. + intOverlapped->OffsetLow = unchecked((int)_filePosition); + intOverlapped->OffsetHigh = (int)(_filePosition >> 32); + + // When using overlapped IO, the OS is not supposed to + // touch the file pointer location at all. We will adjust it + // ourselves. This isn't threadsafe. + + // WriteFile should not update the file pointer when writing + // in overlapped mode, according to MSDN. But it does update + // the file pointer when writing to a UNC path! + // So changed the code below to seek to an absolute + // location, not a relative one. ReadFile seems consistent though. + SeekCore(_fileHandle, destination.Length, SeekOrigin.Current); + } + + // queue an async ReadFile operation and pass in a packed overlapped + int errorCode = 0; + int r = ReadFileNative(_fileHandle, destination.Span, intOverlapped, out errorCode); + + // ReadFile, the OS version, will return 0 on failure. But + // my ReadFileNative wrapper returns -1. My wrapper will return + // the following: + // On error, r==-1. + // On async requests that are still pending, r==-1 w/ errorCode==ERROR_IO_PENDING + // on async requests that completed sequentially, r==0 + // You will NEVER RELIABLY be able to get the number of bytes + // read back from this call when using overlapped structures! You must + // not pass in a non-null lpNumBytesRead to ReadFile when using + // overlapped structures! This is by design NT behavior. + if (r == -1) + { + // For pipes, when they hit EOF, they will come here. + if (errorCode == ERROR_BROKEN_PIPE) + { + // Not an error, but EOF. AsyncFSCallback will NOT be + // called. Call the user callback here. + + // We clear the overlapped status bit for this special case. + // Failure to do so looks like we are freeing a pending overlapped later. + intOverlapped->InternalLow = IntPtr.Zero; + completionSource.SetCompletedSynchronously(0); + } + else if (errorCode != ERROR_IO_PENDING) + { + if (!_fileHandle.IsClosed && CanSeek) // Update Position - It could be anywhere. + { + SeekCore(_fileHandle, 0, SeekOrigin.Current); + } + + completionSource.ReleaseNativeResource(); + + if (errorCode == ERROR_HANDLE_EOF) + { + throw Error.GetEndOfFile(); + } + else + { + throw Win32Marshal.GetExceptionForWin32Error(errorCode); + } + } + else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING + { + // Only once the IO is pending do we register for cancellation + completionSource.RegisterForCancellation(cancellationToken); + } + } + else + { + // Due to a workaround for a race condition in NT's ReadFile & + // WriteFile routines, we will always be returning 0 from ReadFileNative + // when we do async IO instead of the number of bytes read, + // irregardless of whether the operation completed + // synchronously or asynchronously. We absolutely must not + // set asyncResult._numBytes here, since will never have correct + // results. + } + + return completionSource.Task; + } + + private Task WriteAsyncInternal(ReadOnlyMemory source, CancellationToken cancellationToken) + { + Debug.Assert(_useAsyncIO); + Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both."); + Debug.Assert(!_isPipe || (_readPos == 0 && _readLength == 0), "Win32FileStream must not have buffered data here! Pipes should be unidirectional."); + + if (!CanWrite) throw Error.GetWriteNotSupported(); + + bool writeDataStoredInBuffer = false; + if (!_isPipe) // avoid async buffering with pipes, as doing so can lead to deadlocks (see comments in ReadInternalAsyncCore) + { + // Ensure the buffer is clear for writing + if (_writePos == 0) + { + if (_readPos < _readLength) + { + FlushReadBuffer(); + } + _readPos = 0; + _readLength = 0; + } + + // Determine how much space remains in the buffer + int remainingBuffer = _bufferLength - _writePos; + Debug.Assert(remainingBuffer >= 0); + + // Simple/common case: + // - The write is smaller than our buffer, such that it's worth considering buffering it. + // - There's no active flush operation, such that we don't have to worry about the existing buffer being in use. + // - And the data we're trying to write fits in the buffer, meaning it wasn't already filled by previous writes. + // In that case, just store it in the buffer. + if (source.Length < _bufferLength && !HasActiveBufferOperation && source.Length <= remainingBuffer) + { + source.Span.CopyTo(new Span(GetBuffer(), _writePos, source.Length)); + _writePos += source.Length; + writeDataStoredInBuffer = true; + + // There is one special-but-common case, common because devs often use + // byte[] sizes that are powers of 2 and thus fit nicely into our buffer, which is + // also a power of 2. If after our write the buffer still has remaining space, + // then we're done and can return a completed task now. But if we filled the buffer + // completely, we want to do the asynchronous flush/write as part of this operation + // rather than waiting until the next write that fills the buffer. + if (source.Length != remainingBuffer) + return Task.CompletedTask; + + Debug.Assert(_writePos == _bufferLength); + } + } + + // At this point, at least one of the following is true: + // 1. There was an active flush operation (it could have completed by now, though). + // 2. The data doesn't fit in the remaining buffer (or it's a pipe and we chose not to try). + // 3. We wrote all of the data to the buffer, filling it. + // + // If there's an active operation, we can't touch the current buffer because it's in use. + // That gives us a choice: we can either allocate a new buffer, or we can skip the buffer + // entirely (even if the data would otherwise fit in it). For now, for simplicity, we do + // the latter; it could also have performance wins due to OS-level optimizations, and we could + // potentially add support for PreAllocatedOverlapped due to having a single buffer. (We can + // switch to allocating a new buffer, potentially experimenting with buffer pooling, should + // performance data suggest it's appropriate.) + // + // If the data doesn't fit in the remaining buffer, it could be because it's so large + // it's greater than the entire buffer size, in which case we'd always skip the buffer, + // or it could be because there's more data than just the space remaining. For the latter + // case, we need to issue an asynchronous write to flush that data, which then turns this into + // the first case above with an active operation. + // + // If we already stored the data, then we have nothing additional to write beyond what + // we need to flush. + // + // In any of these cases, we have the same outcome: + // - If there's data in the buffer, flush it by writing it out asynchronously. + // - Then, if there's any data to be written, issue a write for it concurrently. + // We return a Task that represents one or both. + + // Flush the buffer asynchronously if there's anything to flush + Task flushTask = null; + if (_writePos > 0) + { + flushTask = FlushWriteAsync(cancellationToken); + + // If we already copied all of the data into the buffer, + // simply return the flush task here. Same goes for if the task has + // already completed and was unsuccessful. + if (writeDataStoredInBuffer || + flushTask.IsFaulted || + flushTask.IsCanceled) + { + return flushTask; + } + } + + Debug.Assert(!writeDataStoredInBuffer); + Debug.Assert(_writePos == 0); + + // Finally, issue the write asynchronously, and return a Task that logically + // represents the write operation, including any flushing done. + Task writeTask = WriteAsyncInternalCore(source, cancellationToken); + return + (flushTask == null || flushTask.Status == TaskStatus.RanToCompletion) ? writeTask : + (writeTask.Status == TaskStatus.RanToCompletion) ? flushTask : + Task.WhenAll(flushTask, writeTask); + } + + private unsafe Task WriteAsyncInternalCore(ReadOnlyMemory source, CancellationToken cancellationToken) + { + Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); + Debug.Assert(CanWrite, "_parent.CanWrite"); + Debug.Assert(_readPos == _readLength, "_readPos == _readLen"); + Debug.Assert(_useAsyncIO, "WriteInternalCoreAsync doesn't work on synchronous file streams!"); + + // Create and store async stream class library specific data in the async result + FileStreamCompletionSource completionSource = MemoryMarshal.TryGetArray(source, out ArraySegment array) ? + new FileStreamCompletionSource(this, 0, array.Array) : + new MemoryFileStreamCompletionSource(this, 0, source); + NativeOverlapped* intOverlapped = completionSource.Overlapped; + + if (CanSeek) + { + // Make sure we set the length of the file appropriately. + long len = Length; + + // Make sure we are writing to the position that we think we are + VerifyOSHandlePosition(); + + if (_filePosition + source.Length > len) + { + SetLengthCore(_filePosition + source.Length); + } + + // Now set the position to read from in the NativeOverlapped struct + // For pipes, we should leave the offset fields set to 0. + intOverlapped->OffsetLow = (int)_filePosition; + intOverlapped->OffsetHigh = (int)(_filePosition >> 32); + + // When using overlapped IO, the OS is not supposed to + // touch the file pointer location at all. We will adjust it + // ourselves. This isn't threadsafe. + SeekCore(_fileHandle, source.Length, SeekOrigin.Current); + } + + int errorCode = 0; + // queue an async WriteFile operation and pass in a packed overlapped + int r = WriteFileNative(_fileHandle, source.Span, intOverlapped, out errorCode); + + // WriteFile, the OS version, will return 0 on failure. But + // my WriteFileNative wrapper returns -1. My wrapper will return + // the following: + // On error, r==-1. + // On async requests that are still pending, r==-1 w/ errorCode==ERROR_IO_PENDING + // On async requests that completed sequentially, r==0 + // You will NEVER RELIABLY be able to get the number of bytes + // written back from this call when using overlapped IO! You must + // not pass in a non-null lpNumBytesWritten to WriteFile when using + // overlapped structures! This is ByDesign NT behavior. + if (r == -1) + { + // For pipes, when they are closed on the other side, they will come here. + if (errorCode == ERROR_NO_DATA) + { + // Not an error, but EOF. AsyncFSCallback will NOT be called. + // Completing TCS and return cached task allowing the GC to collect TCS. + completionSource.SetCompletedSynchronously(0); + return Task.CompletedTask; + } + else if (errorCode != ERROR_IO_PENDING) + { + if (!_fileHandle.IsClosed && CanSeek) // Update Position - It could be anywhere. + { + SeekCore(_fileHandle, 0, SeekOrigin.Current); + } + + completionSource.ReleaseNativeResource(); + + if (errorCode == ERROR_HANDLE_EOF) + { + throw Error.GetEndOfFile(); + } + else + { + throw Win32Marshal.GetExceptionForWin32Error(errorCode); + } + } + else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING + { + // Only once the IO is pending do we register for cancellation + completionSource.RegisterForCancellation(cancellationToken); + } + } + else + { + // Due to a workaround for a race condition in NT's ReadFile & + // WriteFile routines, we will always be returning 0 from WriteFileNative + // when we do async IO instead of the number of bytes written, + // irregardless of whether the operation completed + // synchronously or asynchronously. We absolutely must not + // set asyncResult._numBytes here, since will never have correct + // results. + } + + return completionSource.Task; + } + + // Windows API definitions, from winbase.h and others + + private const int FILE_ATTRIBUTE_NORMAL = 0x00000080; + private const int FILE_ATTRIBUTE_ENCRYPTED = 0x00004000; + private const int FILE_FLAG_OVERLAPPED = 0x40000000; + internal const int GENERIC_READ = unchecked((int)0x80000000); + private const int GENERIC_WRITE = 0x40000000; + + private const int FILE_BEGIN = 0; + private const int FILE_CURRENT = 1; + private const int FILE_END = 2; + + // Error codes (not HRESULTS), from winerror.h + internal const int ERROR_BROKEN_PIPE = 109; + internal const int ERROR_NO_DATA = 232; + private const int ERROR_HANDLE_EOF = 38; + private const int ERROR_INVALID_PARAMETER = 87; + private const int ERROR_IO_PENDING = 997; + + // __ConsoleStream also uses this code. + private unsafe int ReadFileNative(SafeFileHandle handle, Span bytes, NativeOverlapped* overlapped, out int errorCode) + { + Debug.Assert(handle != null, "handle != null"); + Debug.Assert((_useAsyncIO && overlapped != null) || (!_useAsyncIO && overlapped == null), "Async IO and overlapped parameters inconsistent in call to ReadFileNative."); + + int r; + int numBytesRead = 0; + + fixed (byte* p = &MemoryMarshal.GetReference(bytes)) + { + r = _useAsyncIO ? + Interop.Kernel32.ReadFile(handle, p, bytes.Length, IntPtr.Zero, overlapped) : + Interop.Kernel32.ReadFile(handle, p, bytes.Length, out numBytesRead, IntPtr.Zero); + } + + if (r == 0) + { + errorCode = GetLastWin32ErrorAndDisposeHandleIfInvalid(); + return -1; + } + else + { + errorCode = 0; + return numBytesRead; + } + } + + private unsafe int WriteFileNative(SafeFileHandle handle, ReadOnlySpan buffer, NativeOverlapped* overlapped, out int errorCode) + { + Debug.Assert(handle != null, "handle != null"); + Debug.Assert((_useAsyncIO && overlapped != null) || (!_useAsyncIO && overlapped == null), "Async IO and overlapped parameters inconsistent in call to WriteFileNative."); + + int numBytesWritten = 0; + int r; + + fixed (byte* p = &MemoryMarshal.GetReference(buffer)) + { + r = _useAsyncIO ? + Interop.Kernel32.WriteFile(handle, p, buffer.Length, IntPtr.Zero, overlapped) : + Interop.Kernel32.WriteFile(handle, p, buffer.Length, out numBytesWritten, IntPtr.Zero); + } + + if (r == 0) + { + errorCode = GetLastWin32ErrorAndDisposeHandleIfInvalid(); + return -1; + } + else + { + errorCode = 0; + return numBytesWritten; + } + } + + private int GetLastWin32ErrorAndDisposeHandleIfInvalid(bool throwIfInvalidHandle = false) + { + int errorCode = Marshal.GetLastWin32Error(); + + // If ERROR_INVALID_HANDLE is returned, it doesn't suffice to set + // the handle as invalid; the handle must also be closed. + // + // Marking the handle as invalid but not closing the handle + // resulted in exceptions during finalization and locked column + // values (due to invalid but unclosed handle) in SQL Win32FileStream + // scenarios. + // + // A more mainstream scenario involves accessing a file on a + // network share. ERROR_INVALID_HANDLE may occur because the network + // connection was dropped and the server closed the handle. However, + // the client side handle is still open and even valid for certain + // operations. + // + // Note that _parent.Dispose doesn't throw so we don't need to special case. + // SetHandleAsInvalid only sets _closed field to true (without + // actually closing handle) so we don't need to call that as well. + if (errorCode == Interop.Errors.ERROR_INVALID_HANDLE) + { + _fileHandle.Dispose(); + + if (throwIfInvalidHandle) + throw Win32Marshal.GetExceptionForWin32Error(errorCode); + } + + return errorCode; + } + + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + // If we're in sync mode, just use the shared CopyToAsync implementation that does + // typical read/write looping. We also need to take this path if this is a derived + // instance from FileStream, as a derived type could have overridden ReadAsync, in which + // case our custom CopyToAsync implementation isn't necessarily correct. + if (!_useAsyncIO || GetType() != typeof(FileStream)) + { + return base.CopyToAsync(destination, bufferSize, cancellationToken); + } + + StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize); + + // Bail early for cancellation if cancellation has been requested + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + // Fail if the file was closed + if (_fileHandle.IsClosed) + { + throw Error.GetFileNotOpen(); + } + + // Do the async copy, with differing implementations based on whether the FileStream was opened as async or sync + Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both."); + return AsyncModeCopyToAsync(destination, bufferSize, cancellationToken); + } + + private async Task AsyncModeCopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + Debug.Assert(_useAsyncIO, "This implementation is for async mode only"); + Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); + Debug.Assert(CanRead, "_parent.CanRead"); + + // Make sure any pending writes have been flushed before we do a read. + if (_writePos > 0) + { + await FlushWriteAsync(cancellationToken).ConfigureAwait(false); + } + + // Typically CopyToAsync would be invoked as the only "read" on the stream, but it's possible some reading is + // done and then the CopyToAsync is issued. For that case, see if we have any data available in the buffer. + if (GetBuffer() != null) + { + int bufferedBytes = _readLength - _readPos; + if (bufferedBytes > 0) + { + await destination.WriteAsync(GetBuffer(), _readPos, bufferedBytes, cancellationToken).ConfigureAwait(false); + _readPos = _readLength = 0; + } + } + + // For efficiency, we avoid creating a new task and associated state for each asynchronous read. + // Instead, we create a single reusable awaitable object that will be triggered when an await completes + // and reset before going again. + var readAwaitable = new AsyncCopyToAwaitable(this); + + // Make sure we are reading from the position that we think we are. + // Only set the position in the awaitable if we can seek (e.g. not for pipes). + bool canSeek = CanSeek; + if (canSeek) + { + VerifyOSHandlePosition(); + readAwaitable._position = _filePosition; + } + + // Get the buffer to use for the copy operation, as the base CopyToAsync does. We don't try to use + // _buffer here, even if it's not null, as concurrent operations are allowed, and another operation may + // actually be using the buffer already. Plus, it'll be rare for _buffer to be non-null, as typically + // CopyToAsync is used as the only operation performed on the stream, and the buffer is lazily initialized. + // Further, typically the CopyToAsync buffer size will be larger than that used by the FileStream, such that + // we'd likely be unable to use it anyway. Instead, we rent the buffer from a pool. + byte[] copyBuffer = ArrayPool.Shared.Rent(bufferSize); + bufferSize = 0; // repurpose bufferSize to be the high water mark for the buffer, to avoid an extra field in the state machine + + // Allocate an Overlapped we can use repeatedly for all operations + var awaitableOverlapped = new PreAllocatedOverlapped(AsyncCopyToAwaitable.s_callback, readAwaitable, copyBuffer); + var cancellationReg = default(CancellationTokenRegistration); + try + { + // Register for cancellation. We do this once for the whole copy operation, and just try to cancel + // whatever read operation may currently be in progress, if there is one. It's possible the cancellation + // request could come in between operations, in which case we flag that with explicit calls to ThrowIfCancellationRequested + // in the read/write copy loop. + if (cancellationToken.CanBeCanceled) + { + cancellationReg = cancellationToken.Register(s => + { + var innerAwaitable = (AsyncCopyToAwaitable)s; + unsafe + { + lock (innerAwaitable.CancellationLock) // synchronize with cleanup of the overlapped + { + if (innerAwaitable._nativeOverlapped != null) + { + // Try to cancel the I/O. We ignore the return value, as cancellation is opportunistic and we + // don't want to fail the operation because we couldn't cancel it. + Interop.Kernel32.CancelIoEx(innerAwaitable._fileStream._fileHandle, innerAwaitable._nativeOverlapped); + } + } + } + }, readAwaitable); + } + + // Repeatedly read from this FileStream and write the results to the destination stream. + while (true) + { + cancellationToken.ThrowIfCancellationRequested(); + readAwaitable.ResetForNextOperation(); + + try + { + bool synchronousSuccess; + int errorCode; + unsafe + { + // Allocate a native overlapped for our reusable overlapped, and set position to read based on the next + // desired address stored in the awaitable. (This position may be 0, if either we're at the beginning or + // if the stream isn't seekable.) + readAwaitable._nativeOverlapped = _fileHandle.ThreadPoolBinding.AllocateNativeOverlapped(awaitableOverlapped); + if (canSeek) + { + readAwaitable._nativeOverlapped->OffsetLow = unchecked((int)readAwaitable._position); + readAwaitable._nativeOverlapped->OffsetHigh = (int)(readAwaitable._position >> 32); + } + + // Kick off the read. + synchronousSuccess = ReadFileNative(_fileHandle, copyBuffer, readAwaitable._nativeOverlapped, out errorCode) >= 0; + } + + // If the operation did not synchronously succeed, it either failed or initiated the asynchronous operation. + if (!synchronousSuccess) + { + switch (errorCode) + { + case ERROR_IO_PENDING: + // Async operation in progress. + break; + case ERROR_BROKEN_PIPE: + case ERROR_HANDLE_EOF: + // We're at or past the end of the file, and the overlapped callback + // won't be raised in these cases. Mark it as completed so that the await + // below will see it as such. + readAwaitable.MarkCompleted(); + break; + default: + // Everything else is an error (and there won't be a callback). + throw Win32Marshal.GetExceptionForWin32Error(errorCode); + } + } + + // Wait for the async operation (which may or may not have already completed), then throw if it failed. + await readAwaitable; + switch (readAwaitable._errorCode) + { + case 0: // success + Debug.Assert(readAwaitable._numBytes >= 0, $"Expected non-negative numBytes, got {readAwaitable._numBytes}"); + break; + case ERROR_BROKEN_PIPE: // logically success with 0 bytes read (write end of pipe closed) + case ERROR_HANDLE_EOF: // logically success with 0 bytes read (read at end of file) + Debug.Assert(readAwaitable._numBytes == 0, $"Expected 0 bytes read, got {readAwaitable._numBytes}"); + break; + case Interop.Errors.ERROR_OPERATION_ABORTED: // canceled + throw new OperationCanceledException(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)); + default: // error + throw Win32Marshal.GetExceptionForWin32Error((int)readAwaitable._errorCode); + } + + // Successful operation. If we got zero bytes, we're done: exit the read/write loop. + int numBytesRead = (int)readAwaitable._numBytes; + if (numBytesRead == 0) + { + break; + } + + // Otherwise, update the read position for next time accordingly. + if (canSeek) + { + readAwaitable._position += numBytesRead; + } + + // (and keep track of the maximum number of bytes in the buffer we used, to avoid excessive and unnecessary + // clearing of the buffer before we return it to the pool) + if (numBytesRead > bufferSize) + { + bufferSize = numBytesRead; + } + } + finally + { + // Free the resources for this read operation + unsafe + { + NativeOverlapped* overlapped; + lock (readAwaitable.CancellationLock) // just an Exchange, but we need this to be synchronized with cancellation, so using the same lock + { + overlapped = readAwaitable._nativeOverlapped; + readAwaitable._nativeOverlapped = null; + } + if (overlapped != null) + { + _fileHandle.ThreadPoolBinding.FreeNativeOverlapped(overlapped); + } + } + } + + // Write out the read data. + await destination.WriteAsync(copyBuffer, 0, (int)readAwaitable._numBytes, cancellationToken).ConfigureAwait(false); + } + } + finally + { + // Cleanup from the whole copy operation + cancellationReg.Dispose(); + awaitableOverlapped.Dispose(); + + Array.Clear(copyBuffer, 0, bufferSize); + ArrayPool.Shared.Return(copyBuffer, clearArray: false); + + // Make sure the stream's current position reflects where we ended up + if (!_fileHandle.IsClosed && CanSeek) + { + SeekCore(_fileHandle, 0, SeekOrigin.End); + } + } + } + + /// Used by CopyToAsync to enable awaiting the result of an overlapped I/O operation with minimal overhead. + private sealed unsafe class AsyncCopyToAwaitable : ICriticalNotifyCompletion + { + /// Sentinel object used to indicate that the I/O operation has completed before being awaited. + private readonly static Action s_sentinel = () => { }; + /// Cached delegate to IOCallback. + internal static readonly IOCompletionCallback s_callback = IOCallback; + + /// The FileStream that owns this instance. + internal readonly FileStream _fileStream; + + /// Tracked position representing the next location from which to read. + internal long _position; + /// The current native overlapped pointer. This changes for each operation. + internal NativeOverlapped* _nativeOverlapped; + /// + /// null if the operation is still in progress, + /// s_sentinel if the I/O operation completed before the await, + /// s_callback if it completed after the await yielded. + /// + internal Action _continuation; + /// Last error code from completed operation. + internal uint _errorCode; + /// Last number of read bytes from completed operation. + internal uint _numBytes; + + /// Lock object used to protect cancellation-related access to _nativeOverlapped. + internal object CancellationLock => this; + + /// Initialize the awaitable. + internal unsafe AsyncCopyToAwaitable(FileStream fileStream) + { + _fileStream = fileStream; + } + + /// Reset state to prepare for the next read operation. + internal void ResetForNextOperation() + { + Debug.Assert(_position >= 0, $"Expected non-negative position, got {_position}"); + _continuation = null; + _errorCode = 0; + _numBytes = 0; + } + + /// Overlapped callback: store the results, then invoke the continuation delegate. + internal unsafe static void IOCallback(uint errorCode, uint numBytes, NativeOverlapped* pOVERLAP) + { + var awaitable = (AsyncCopyToAwaitable)ThreadPoolBoundHandle.GetNativeOverlappedState(pOVERLAP); + + Debug.Assert(!ReferenceEquals(awaitable._continuation, s_sentinel), "Sentinel must not have already been set as the continuation"); + awaitable._errorCode = errorCode; + awaitable._numBytes = numBytes; + + (awaitable._continuation ?? Interlocked.CompareExchange(ref awaitable._continuation, s_sentinel, null))?.Invoke(); + } + + /// + /// Called when it's known that the I/O callback for an operation will not be invoked but we'll + /// still be awaiting the awaitable. + /// + internal void MarkCompleted() + { + Debug.Assert(_continuation == null, "Expected null continuation"); + _continuation = s_sentinel; + } + + public AsyncCopyToAwaitable GetAwaiter() => this; + public bool IsCompleted => ReferenceEquals(_continuation, s_sentinel); + public void GetResult() { } + public void OnCompleted(Action continuation) => UnsafeOnCompleted(continuation); + public void UnsafeOnCompleted(Action continuation) + { + if (ReferenceEquals(_continuation, s_sentinel) || + Interlocked.CompareExchange(ref _continuation, continuation, null) != null) + { + Debug.Assert(ReferenceEquals(_continuation, s_sentinel), $"Expected continuation set to s_sentinel, got ${_continuation}"); + Task.Run(continuation); + } + } + } + + // Unlike Flush(), FlushAsync() always flushes to disk. This is intentional. + // Legend is that we chose not to flush the OS file buffers in Flush() in fear of + // perf problems with frequent, long running FlushFileBuffers() calls. But we don't + // have that problem with FlushAsync() because we will call FlushFileBuffers() in the background. + private Task FlushAsyncInternal(CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + + if (_fileHandle.IsClosed) + throw Error.GetFileNotOpen(); + + // The always synchronous data transfer between the OS and the internal buffer is intentional + // because this is needed to allow concurrent async IO requests. Concurrent data transfer + // between the OS and the internal buffer will result in race conditions. Since FlushWrite and + // FlushRead modify internal state of the stream and transfer data between the OS and the + // internal buffer, they cannot be truly async. We will, however, flush the OS file buffers + // asynchronously because it doesn't modify any internal state of the stream and is potentially + // a long running process. + try + { + FlushInternalBuffer(); + } + catch (Exception e) + { + return Task.FromException(e); + } + + if (CanWrite) + { + return Task.Factory.StartNew( + state => ((FileStream)state).FlushOSBuffer(), + this, + cancellationToken, + TaskCreationOptions.DenyChildAttach, + TaskScheduler.Default); + } + else + { + return Task.CompletedTask; + } + } + + private void LockInternal(long position, long length) + { + int positionLow = unchecked((int)(position)); + int positionHigh = unchecked((int)(position >> 32)); + int lengthLow = unchecked((int)(length)); + int lengthHigh = unchecked((int)(length >> 32)); + + if (!Interop.Kernel32.LockFile(_fileHandle, positionLow, positionHigh, lengthLow, lengthHigh)) + { + throw Win32Marshal.GetExceptionForLastWin32Error(); + } + } + + private void UnlockInternal(long position, long length) + { + int positionLow = unchecked((int)(position)); + int positionHigh = unchecked((int)(position >> 32)); + int lengthLow = unchecked((int)(length)); + int lengthHigh = unchecked((int)(length >> 32)); + + if (!Interop.Kernel32.UnlockFile(_fileHandle, positionLow, positionHigh, lengthLow, lengthHigh)) + { + throw Win32Marshal.GetExceptionForLastWin32Error(); + } + } + private SafeFileHandle ValidateFileHandle(SafeFileHandle fileHandle) + { + if (fileHandle.IsInvalid) + { + // Return a meaningful exception with the full path. + + // NT5 oddity - when trying to open "C:\" as a Win32FileStream, + // we usually get ERROR_PATH_NOT_FOUND from the OS. We should + // probably be consistent w/ every other directory. + int errorCode = Marshal.GetLastWin32Error(); + + if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && _path.Length == PathInternal.GetRootLength(_path)) + errorCode = Interop.Errors.ERROR_ACCESS_DENIED; + + throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); + } + + fileHandle.IsAsync = _useAsyncIO; + return fileHandle; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/FileStream.cs b/external/corefx/src/Common/src/CoreLib/System/IO/FileStream.cs new file mode 100644 index 0000000000..65c63bcc53 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/FileStream.cs @@ -0,0 +1,864 @@ +// 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.Threading; +using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; +using System.Diagnostics; + +namespace System.IO +{ + public partial class FileStream : Stream + { + private const FileShare DefaultShare = FileShare.Read; + private const bool DefaultIsAsync = false; + internal const int DefaultBufferSize = 4096; + + private byte[] _buffer; + private int _bufferLength; + private readonly SafeFileHandle _fileHandle; + + /// Whether the file is opened for reading, writing, or both. + private readonly FileAccess _access; + + /// The path to the opened file. + private readonly string _path; + + /// The next available byte to be read from the _buffer. + private int _readPos; + + /// The number of valid bytes in _buffer. + private int _readLength; + + /// The next location in which a write should occur to the buffer. + private int _writePos; + + /// + /// Whether asynchronous read/write/flush operations should be performed using async I/O. + /// On Windows FileOptions.Asynchronous controls how the file handle is configured, + /// and then as a result how operations are issued against that file handle. On Unix, + /// there isn't any distinction around how file descriptors are created for async vs + /// sync, but we still differentiate how the operations are issued in order to provide + /// similar behavioral semantics and performance characteristics as on Windows. On + /// Windows, if non-async, async read/write requests just delegate to the base stream, + /// and no attempt is made to synchronize between sync and async operations on the stream; + /// if async, then async read/write requests are implemented specially, and sync read/write + /// requests are coordinated with async ones by implementing the sync ones over the async + /// ones. On Unix, we do something similar. If non-async, async read/write requests just + /// delegate to the base stream, and no attempt is made to synchronize. If async, we use + /// a semaphore to coordinate both sync and async operations. + /// + private readonly bool _useAsyncIO; + + /// cached task for read ops that complete synchronously + private Task _lastSynchronouslyCompletedTask = null; + + /// + /// Currently cached position in the stream. This should always mirror the underlying file's actual position, + /// and should only ever be out of sync if another stream with access to this same file manipulates it, at which + /// point we attempt to error out. + /// + private long _filePosition; + + /// Whether the file stream's handle has been exposed. + private bool _exposedHandle; + + [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead. http://go.microsoft.com/fwlink/?linkid=14202")] + public FileStream(IntPtr handle, FileAccess access) + : this(handle, access, true, DefaultBufferSize, false) + { + } + + [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. http://go.microsoft.com/fwlink/?linkid=14202")] + public FileStream(IntPtr handle, FileAccess access, bool ownsHandle) + : this(handle, access, ownsHandle, DefaultBufferSize, false) + { + } + + [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. http://go.microsoft.com/fwlink/?linkid=14202")] + public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize) + : this(handle, access, ownsHandle, bufferSize, false) + { + } + + [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. http://go.microsoft.com/fwlink/?linkid=14202")] + public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync) + { + SafeFileHandle safeHandle = new SafeFileHandle(handle, ownsHandle: ownsHandle); + try + { + ValidateAndInitFromHandle(safeHandle, access, bufferSize, isAsync); + } + catch + { + // We don't want to take ownership of closing passed in handles + // *unless* the constructor completes successfully. + GC.SuppressFinalize(safeHandle); + + // This would also prevent Close from being called, but is unnecessary + // as we've removed the object from the finalizer queue. + // + // safeHandle.SetHandleAsInvalid(); + throw; + } + + // Note: Cleaner to set the following fields in ValidateAndInitFromHandle, + // but we can't as they're readonly. + _access = access; + _useAsyncIO = isAsync; + + // As the handle was passed in, we must set the handle field at the very end to + // avoid the finalizer closing the handle when we throw errors. + _fileHandle = safeHandle; + } + + public FileStream(SafeFileHandle handle, FileAccess access) + : this(handle, access, DefaultBufferSize) + { + } + + public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) + : this(handle, access, bufferSize, GetDefaultIsAsync(handle)) + { + } + + private void ValidateAndInitFromHandle(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) + { + if (handle.IsInvalid) + throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle)); + + if (access < FileAccess.Read || access > FileAccess.ReadWrite) + throw new ArgumentOutOfRangeException(nameof(access), SR.ArgumentOutOfRange_Enum); + if (bufferSize <= 0) + throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum); + + if (handle.IsClosed) + throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); + if (handle.IsAsync.HasValue && isAsync != handle.IsAsync.Value) + throw new ArgumentException(SR.Arg_HandleNotAsync, nameof(handle)); + + _exposedHandle = true; + _bufferLength = bufferSize; + + InitFromHandle(handle, access, isAsync); + } + + public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) + { + ValidateAndInitFromHandle(handle, access, bufferSize, isAsync); + + // Note: Cleaner to set the following fields in ValidateAndInitFromHandle, + // but we can't as they're readonly. + _access = access; + _useAsyncIO = isAsync; + + // As the handle was passed in, we must set the handle field at the very end to + // avoid the finalizer closing the handle when we throw errors. + _fileHandle = handle; + } + + public FileStream(string path, FileMode mode) : + this(path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), DefaultShare, DefaultBufferSize, DefaultIsAsync) + { } + + public FileStream(string path, FileMode mode, FileAccess access) : + this(path, mode, access, DefaultShare, DefaultBufferSize, DefaultIsAsync) + { } + + public FileStream(string path, FileMode mode, FileAccess access, FileShare share) : + this(path, mode, access, share, DefaultBufferSize, DefaultIsAsync) + { } + + public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize) : + this(path, mode, access, share, bufferSize, DefaultIsAsync) + { } + + public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync) : + this(path, mode, access, share, bufferSize, useAsync ? FileOptions.Asynchronous : FileOptions.None) + { } + + public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) + { + if (path == null) + throw new ArgumentNullException(nameof(path), SR.ArgumentNull_Path); + if (path.Length == 0) + throw new ArgumentException(SR.Argument_EmptyPath, nameof(path)); + + // don't include inheritable in our bounds check for share + FileShare tempshare = share & ~FileShare.Inheritable; + string badArg = null; + + if (mode < FileMode.CreateNew || mode > FileMode.Append) + badArg = nameof(mode); + else if (access < FileAccess.Read || access > FileAccess.ReadWrite) + badArg = nameof(access); + else if (tempshare < FileShare.None || tempshare > (FileShare.ReadWrite | FileShare.Delete)) + badArg = nameof(share); + + if (badArg != null) + throw new ArgumentOutOfRangeException(badArg, SR.ArgumentOutOfRange_Enum); + + // NOTE: any change to FileOptions enum needs to be matched here in the error validation + if (options != FileOptions.None && (options & ~(FileOptions.WriteThrough | FileOptions.Asynchronous | FileOptions.RandomAccess | FileOptions.DeleteOnClose | FileOptions.SequentialScan | FileOptions.Encrypted | (FileOptions)0x20000000 /* NoBuffering */)) != 0) + throw new ArgumentOutOfRangeException(nameof(options), SR.ArgumentOutOfRange_Enum); + + if (bufferSize <= 0) + throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum); + + // Write access validation + if ((access & FileAccess.Write) == 0) + { + if (mode == FileMode.Truncate || mode == FileMode.CreateNew || mode == FileMode.Create || mode == FileMode.Append) + { + // No write access, mode and access disagree but flag access since mode comes first + throw new ArgumentException(SR.Format(SR.Argument_InvalidFileModeAndAccessCombo, mode, access), nameof(access)); + } + } + + if ((access & FileAccess.Read) != 0 && mode == FileMode.Append) + throw new ArgumentException(SR.Argument_InvalidAppendMode, nameof(access)); + + string fullPath = Path.GetFullPath(path); + + _path = fullPath; + _access = access; + _bufferLength = bufferSize; + + if ((options & FileOptions.Asynchronous) != 0) + _useAsyncIO = true; + + _fileHandle = OpenHandle(mode, share, options); + + try + { + Init(mode, share); + } + catch + { + // If anything goes wrong while setting up the stream, make sure we deterministically dispose + // of the opened handle. + _fileHandle.Dispose(); + _fileHandle = null; + throw; + } + } + + private static bool GetDefaultIsAsync(SafeFileHandle handle) + { + // This will eventually get more complicated as we can actually check the underlying handle type on Windows + return handle.IsAsync.HasValue ? handle.IsAsync.Value : false; + } + + [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. http://go.microsoft.com/fwlink/?linkid=14202")] + public virtual IntPtr Handle { get { return SafeFileHandle.DangerousGetHandle(); } } + + public virtual void Lock(long position, long length) + { + if (position < 0 || length < 0) + { + throw new ArgumentOutOfRangeException(position < 0 ? nameof(position) : nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); + } + + if (_fileHandle.IsClosed) + { + throw Error.GetFileNotOpen(); + } + + LockInternal(position, length); + } + + public virtual void Unlock(long position, long length) + { + if (position < 0 || length < 0) + { + throw new ArgumentOutOfRangeException(position < 0 ? nameof(position) : nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); + } + + if (_fileHandle.IsClosed) + { + throw Error.GetFileNotOpen(); + } + + UnlockInternal(position, length); + } + + public override Task FlushAsync(CancellationToken cancellationToken) + { + // If we have been inherited into a subclass, the following implementation could be incorrect + // since it does not call through to Flush() which a subclass might have overridden. To be safe + // we will only use this implementation in cases where we know it is safe to do so, + // and delegate to our base class (which will call into Flush) when we are not sure. + if (GetType() != typeof(FileStream)) + return base.FlushAsync(cancellationToken); + + return FlushAsyncInternal(cancellationToken); + } + + public override int Read(byte[] array, int offset, int count) + { + ValidateReadWriteArgs(array, offset, count); + return _useAsyncIO ? + ReadAsyncTask(array, offset, count, CancellationToken.None).GetAwaiter().GetResult() : + ReadSpan(new Span(array, offset, count)); + } + + public override int Read(Span destination) + { + if (GetType() == typeof(FileStream) && !_useAsyncIO) + { + if (_fileHandle.IsClosed) + { + throw Error.GetFileNotOpen(); + } + return ReadSpan(destination); + } + else + { + // This type is derived from FileStream and/or the stream is in async mode. If this is a + // derived type, it may have overridden Read(byte[], int, int) prior to this Read(Span) + // overload being introduced. In that case, this Read(Span) overload should use the behavior + // of Read(byte[],int,int) overload. Or if the stream is in async mode, we can't call the + // synchronous ReadSpan, so we similarly call the base Read, which will turn delegate to + // Read(byte[],int,int), which will do the right thing if we're in async mode. + return base.Read(destination); + } + } + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + if (buffer == null) + throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + if (buffer.Length - offset < count) + throw new ArgumentException(SR.Argument_InvalidOffLen /*, no good single parameter name to pass*/); + + // If we have been inherited into a subclass, the following implementation could be incorrect + // since it does not call through to Read() which a subclass might have overridden. + // To be safe we will only use this implementation in cases where we know it is safe to do so, + // and delegate to our base class (which will call into Read/ReadAsync) when we are not sure. + // Similarly, if we weren't opened for asynchronous I/O, call to the base implementation so that + // Read is invoked asynchronously. + if (GetType() != typeof(FileStream) || !_useAsyncIO) + return base.ReadAsync(buffer, offset, count, cancellationToken); + + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + + if (IsClosed) + throw Error.GetFileNotOpen(); + + return ReadAsyncTask(buffer, offset, count, cancellationToken); + } + + public override ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default(CancellationToken)) + { + if (!_useAsyncIO || GetType() != typeof(FileStream)) + { + // If we're not using async I/O, delegate to the base, which will queue a call to Read. + // Or if this isn't a concrete FileStream, a derived type may have overridden ReadAsync(byte[],...), + // which was introduced first, so delegate to the base which will delegate to that. + return base.ReadAsync(destination, cancellationToken); + } + + if (cancellationToken.IsCancellationRequested) + { + return new ValueTask(Task.FromCanceled(cancellationToken)); + } + + if (IsClosed) + { + throw Error.GetFileNotOpen(); + } + + Task t = ReadAsyncInternal(destination, cancellationToken, out int synchronousResult); + return t != null ? + new ValueTask(t) : + new ValueTask(synchronousResult); + } + + private Task ReadAsyncTask(byte[] array, int offset, int count, CancellationToken cancellationToken) + { + Task t = ReadAsyncInternal(new Memory(array, offset, count), cancellationToken, out int synchronousResult); + + if (t == null) + { + t = _lastSynchronouslyCompletedTask; + Debug.Assert(t == null || t.IsCompletedSuccessfully, "Cached task should have completed successfully"); + + if (t == null || t.Result != synchronousResult) + { + _lastSynchronouslyCompletedTask = t = Task.FromResult(synchronousResult); + } + } + + return t; + } + + public override void Write(byte[] array, int offset, int count) + { + ValidateReadWriteArgs(array, offset, count); + if (_useAsyncIO) + { + WriteAsyncInternal(new ReadOnlyMemory(array, offset, count), CancellationToken.None).GetAwaiter().GetResult(); + } + else + { + WriteSpan(new ReadOnlySpan(array, offset, count)); + } + } + + public override void Write(ReadOnlySpan destination) + { + if (GetType() == typeof(FileStream) && !_useAsyncIO) + { + if (_fileHandle.IsClosed) + { + throw Error.GetFileNotOpen(); + } + WriteSpan(destination); + } + else + { + // This type is derived from FileStream and/or the stream is in async mode. If this is a + // derived type, it may have overridden Write(byte[], int, int) prior to this Write(ReadOnlySpan) + // overload being introduced. In that case, this Write(ReadOnlySpan) overload should use the behavior + // of Write(byte[],int,int) overload. Or if the stream is in async mode, we can't call the + // synchronous WriteSpan, so we similarly call the base Write, which will turn delegate to + // Write(byte[],int,int), which will do the right thing if we're in async mode. + base.Write(destination); + } + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + if (buffer == null) + throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + if (buffer.Length - offset < count) + throw new ArgumentException(SR.Argument_InvalidOffLen /*, no good single parameter name to pass*/); + + // If we have been inherited into a subclass, the following implementation could be incorrect + // since it does not call through to Write() or WriteAsync() which a subclass might have overridden. + // To be safe we will only use this implementation in cases where we know it is safe to do so, + // and delegate to our base class (which will call into Write/WriteAsync) when we are not sure. + if (!_useAsyncIO || GetType() != typeof(FileStream)) + return base.WriteAsync(buffer, offset, count, cancellationToken); + + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + + if (IsClosed) + throw Error.GetFileNotOpen(); + + return WriteAsyncInternal(new ReadOnlyMemory(buffer, offset, count), cancellationToken); + } + + public override Task WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken = default(CancellationToken)) + { + if (!_useAsyncIO || GetType() != typeof(FileStream)) + { + // If we're not using async I/O, delegate to the base, which will queue a call to Write. + // Or if this isn't a concrete FileStream, a derived type may have overridden WriteAsync(byte[],...), + // which was introduced first, so delegate to the base which will delegate to that. + return base.WriteAsync(source, cancellationToken); + } + + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + if (IsClosed) + { + throw Error.GetFileNotOpen(); + } + + return WriteAsyncInternal(source, cancellationToken); + } + + /// + /// Clears buffers for this stream and causes any buffered data to be written to the file. + /// + public override void Flush() + { + // Make sure that we call through the public virtual API + Flush(flushToDisk: false); + } + + /// + /// Clears buffers for this stream, and if is true, + /// causes any buffered data to be written to the file. + /// + public virtual void Flush(bool flushToDisk) + { + if (IsClosed) throw Error.GetFileNotOpen(); + + FlushInternalBuffer(); + + if (flushToDisk && CanWrite) + { + FlushOSBuffer(); + } + } + + /// Gets a value indicating whether the current stream supports reading. + public override bool CanRead + { + get { return !_fileHandle.IsClosed && (_access & FileAccess.Read) != 0; } + } + + /// Gets a value indicating whether the current stream supports writing. + public override bool CanWrite + { + get { return !_fileHandle.IsClosed && (_access & FileAccess.Write) != 0; } + } + + /// Validates arguments to Read and Write and throws resulting exceptions. + /// The buffer to read from or write to. + /// The zero-based offset into the array. + /// The maximum number of bytes to read or write. + private void ValidateReadWriteArgs(byte[] array, int offset, int count) + { + if (array == null) + throw new ArgumentNullException(nameof(array), SR.ArgumentNull_Buffer); + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + if (array.Length - offset < count) + throw new ArgumentException(SR.Argument_InvalidOffLen /*, no good single parameter name to pass*/); + if (_fileHandle.IsClosed) + throw Error.GetFileNotOpen(); + } + + /// Sets the length of this stream to the given value. + /// The new length of the stream. + public override void SetLength(long value) + { + if (value < 0) + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); + if (_fileHandle.IsClosed) + throw Error.GetFileNotOpen(); + if (!CanSeek) + throw Error.GetSeekNotSupported(); + if (!CanWrite) + throw Error.GetWriteNotSupported(); + + SetLengthInternal(value); + } + + public virtual SafeFileHandle SafeFileHandle + { + get + { + Flush(); + _exposedHandle = true; + return _fileHandle; + } + } + + /// Gets the path that was passed to the constructor. + public virtual string Name { get { return _path ?? SR.IO_UnknownFileName; } } + + /// Gets a value indicating whether the stream was opened for I/O to be performed synchronously or asynchronously. + public virtual bool IsAsync + { + get { return _useAsyncIO; } + } + + /// Gets the length of the stream in bytes. + public override long Length + { + get + { + if (_fileHandle.IsClosed) throw Error.GetFileNotOpen(); + if (!CanSeek) throw Error.GetSeekNotSupported(); + return GetLengthInternal(); + } + } + + /// + /// Verify that the actual position of the OS's handle equals what we expect it to. + /// This will fail if someone else moved the UnixFileStream's handle or if + /// our position updating code is incorrect. + /// + private void VerifyOSHandlePosition() + { + bool verifyPosition = _exposedHandle; // in release, only verify if we've given out the handle such that someone else could be manipulating it +#if DEBUG + verifyPosition = true; // in debug, always make sure our position matches what the OS says it should be +#endif + if (verifyPosition && CanSeek) + { + long oldPos = _filePosition; // SeekCore will override the current _position, so save it now + long curPos = SeekCore(_fileHandle, 0, SeekOrigin.Current); + if (oldPos != curPos) + { + // For reads, this is non-fatal but we still could have returned corrupted + // data in some cases, so discard the internal buffer. For writes, + // this is a problem; discard the buffer and error out. + _readPos = _readLength = 0; + if (_writePos > 0) + { + _writePos = 0; + throw new IOException(SR.IO_FileStreamHandlePosition); + } + } + } + } + + /// Verifies that state relating to the read/write buffer is consistent. + [Conditional("DEBUG")] + private void AssertBufferInvariants() + { + // Read buffer values must be in range: 0 <= _bufferReadPos <= _bufferReadLength <= _bufferLength + Debug.Assert(0 <= _readPos && _readPos <= _readLength && _readLength <= _bufferLength); + + // Write buffer values must be in range: 0 <= _bufferWritePos <= _bufferLength + Debug.Assert(0 <= _writePos && _writePos <= _bufferLength); + + // Read buffering and write buffering can't both be active + Debug.Assert((_readPos == 0 && _readLength == 0) || _writePos == 0); + } + + /// Validates that we're ready to read from the stream. + private void PrepareForReading() + { + if (_fileHandle.IsClosed) + throw Error.GetFileNotOpen(); + if (_readLength == 0 && !CanRead) + throw Error.GetReadNotSupported(); + + AssertBufferInvariants(); + } + + /// Gets or sets the position within the current stream + public override long Position + { + get + { + if (_fileHandle.IsClosed) + throw Error.GetFileNotOpen(); + + if (!CanSeek) + throw Error.GetSeekNotSupported(); + + AssertBufferInvariants(); + VerifyOSHandlePosition(); + + // We may have read data into our buffer from the handle, such that the handle position + // is artificially further along than the consumer's view of the stream's position. + // Thus, when reading, our position is really starting from the handle position negatively + // offset by the number of bytes in the buffer and positively offset by the number of + // bytes into that buffer we've read. When writing, both the read length and position + // must be zero, and our position is just the handle position offset positive by how many + // bytes we've written into the buffer. + return (_filePosition - _readLength) + _readPos + _writePos; + } + set + { + if (value < 0) + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); + + Seek(value, SeekOrigin.Begin); + } + } + + internal virtual bool IsClosed => _fileHandle.IsClosed; + + /// + /// Gets the array used for buffering reading and writing. + /// If the array hasn't been allocated, this will lazily allocate it. + /// + /// The buffer. + private byte[] GetBuffer() + { + Debug.Assert(_buffer == null || _buffer.Length == _bufferLength); + if (_buffer == null) + { + _buffer = new byte[_bufferLength]; + OnBufferAllocated(); + } + + return _buffer; + } + + partial void OnBufferAllocated(); + + /// + /// Flushes the internal read/write buffer for this stream. If write data has been buffered, + /// that data is written out to the underlying file. Or if data has been buffered for + /// reading from the stream, the data is dumped and our position in the underlying file + /// is rewound as necessary. This does not flush the OS buffer. + /// + private void FlushInternalBuffer() + { + AssertBufferInvariants(); + if (_writePos > 0) + { + FlushWriteBuffer(); + } + else if (_readPos < _readLength && CanSeek) + { + FlushReadBuffer(); + } + } + + /// Dumps any read data in the buffer and rewinds our position in the stream, accordingly, as necessary. + private void FlushReadBuffer() + { + // Reading is done by blocks from the file, but someone could read + // 1 byte from the buffer then write. At that point, the OS's file + // pointer is out of sync with the stream's position. All write + // functions should call this function to preserve the position in the file. + + AssertBufferInvariants(); + Debug.Assert(_writePos == 0, "FileStream: Write buffer must be empty in FlushReadBuffer!"); + + int rewind = _readPos - _readLength; + if (rewind != 0) + { + Debug.Assert(CanSeek, "FileStream will lose buffered read data now."); + SeekCore(_fileHandle, rewind, SeekOrigin.Current); + } + _readPos = _readLength = 0; + } + + /// + /// Reads a byte from the file stream. Returns the byte cast to an int + /// or -1 if reading from the end of the stream. + /// + public override int ReadByte() + { + PrepareForReading(); + + byte[] buffer = GetBuffer(); + if (_readPos == _readLength) + { + FlushWriteBuffer(); + _readLength = FillReadBufferForReadByte(); + _readPos = 0; + if (_readLength == 0) + { + return -1; + } + } + + return buffer[_readPos++]; + } + + /// + /// Writes a byte to the current position in the stream and advances the position + /// within the stream by one byte. + /// + /// The byte to write to the stream. + public override void WriteByte(byte value) + { + PrepareForWriting(); + + // Flush the write buffer if it's full + if (_writePos == _bufferLength) + FlushWriteBufferForWriteByte(); + + // We now have space in the buffer. Store the byte. + GetBuffer()[_writePos++] = value; + } + + /// + /// Validates that we're ready to write to the stream, + /// including flushing a read buffer if necessary. + /// + private void PrepareForWriting() + { + if (_fileHandle.IsClosed) + throw Error.GetFileNotOpen(); + + // Make sure we're good to write. We only need to do this if there's nothing already + // in our write buffer, since if there is something in the buffer, we've already done + // this checking and flushing. + if (_writePos == 0) + { + if (!CanWrite) throw Error.GetWriteNotSupported(); + FlushReadBuffer(); + Debug.Assert(_bufferLength > 0, "_bufferSize > 0"); + } + } + + ~FileStream() + { + // Preserved for compatibility since FileStream has defined a + // finalizer in past releases and derived classes may depend + // on Dispose(false) call. + Dispose(false); + } + + public override IAsyncResult BeginRead(byte[] array, int offset, int numBytes, AsyncCallback callback, object state) + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + if (numBytes < 0) + throw new ArgumentOutOfRangeException(nameof(numBytes), SR.ArgumentOutOfRange_NeedNonNegNum); + if (array.Length - offset < numBytes) + throw new ArgumentException(SR.Argument_InvalidOffLen); + + if (IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); + if (!CanRead) throw new NotSupportedException(SR.NotSupported_UnreadableStream); + + if (!IsAsync) + return base.BeginRead(array, offset, numBytes, callback, state); + else + return TaskToApm.Begin(ReadAsyncTask(array, offset, numBytes, CancellationToken.None), callback, state); + } + + public override IAsyncResult BeginWrite(byte[] array, int offset, int numBytes, AsyncCallback callback, object state) + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + if (numBytes < 0) + throw new ArgumentOutOfRangeException(nameof(numBytes), SR.ArgumentOutOfRange_NeedNonNegNum); + if (array.Length - offset < numBytes) + throw new ArgumentException(SR.Argument_InvalidOffLen); + + if (IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); + if (!CanWrite) throw new NotSupportedException(SR.NotSupported_UnwritableStream); + + if (!IsAsync) + return base.BeginWrite(array, offset, numBytes, callback, state); + else + return TaskToApm.Begin(WriteAsyncInternal(new ReadOnlyMemory(array, offset, numBytes), CancellationToken.None), callback, state); + } + + public override int EndRead(IAsyncResult asyncResult) + { + if (asyncResult == null) + throw new ArgumentNullException(nameof(asyncResult)); + + if (!IsAsync) + return base.EndRead(asyncResult); + else + return TaskToApm.End(asyncResult); + } + + public override void EndWrite(IAsyncResult asyncResult) + { + if (asyncResult == null) + throw new ArgumentNullException(nameof(asyncResult)); + + if (!IsAsync) + base.EndWrite(asyncResult); + else + TaskToApm.End(asyncResult); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/FileStreamCompletionSource.Win32.cs b/external/corefx/src/Common/src/CoreLib/System/IO/FileStreamCompletionSource.Win32.cs new file mode 100644 index 0000000000..4e19f465bd --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/FileStreamCompletionSource.Win32.cs @@ -0,0 +1,245 @@ +// 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.Buffers; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace System.IO +{ + public partial class FileStream : Stream + { + // This is an internal object extending TaskCompletionSource with fields + // for all of the relevant data necessary to complete the IO operation. + // This is used by IOCallback and all of the async methods. + private unsafe class FileStreamCompletionSource : TaskCompletionSource + { + private const long NoResult = 0; + private const long ResultSuccess = (long)1 << 32; + private const long ResultError = (long)2 << 32; + private const long RegisteringCancellation = (long)4 << 32; + private const long CompletedCallback = (long)8 << 32; + private const ulong ResultMask = ((ulong)uint.MaxValue) << 32; + + private static Action s_cancelCallback; + + private readonly FileStream _stream; + private readonly int _numBufferedBytes; + private CancellationTokenRegistration _cancellationRegistration; +#if DEBUG + private bool _cancellationHasBeenRegistered; +#endif + private NativeOverlapped* _overlapped; // Overlapped class responsible for operations in progress when an appdomain unload occurs + private long _result; // Using long since this needs to be used in Interlocked APIs + + // Using RunContinuationsAsynchronously for compat reasons (old API used Task.Factory.StartNew for continuations) + internal FileStreamCompletionSource(FileStream stream, int numBufferedBytes, byte[] bytes) + : base(TaskCreationOptions.RunContinuationsAsynchronously) + { + _numBufferedBytes = numBufferedBytes; + _stream = stream; + _result = NoResult; + + // Create the native overlapped. We try to use the preallocated overlapped if possible: it's possible if the byte + // buffer is null (there's nothing to pin) or the same one that's associated with the preallocated overlapped (and + // thus is already pinned) and if no one else is currently using the preallocated overlapped. This is the fast-path + // for cases where the user-provided buffer is smaller than the FileStream's buffer (such that the FileStream's + // buffer is used) and where operations on the FileStream are not being performed concurrently. + _overlapped = (bytes == null || ReferenceEquals(bytes, _stream._buffer)) && _stream.CompareExchangeCurrentOverlappedOwner(this, null) == null ? + _stream._fileHandle.ThreadPoolBinding.AllocateNativeOverlapped(_stream._preallocatedOverlapped) : + _stream._fileHandle.ThreadPoolBinding.AllocateNativeOverlapped(s_ioCallback, this, bytes); + Debug.Assert(_overlapped != null, "AllocateNativeOverlapped returned null"); + } + + internal NativeOverlapped* Overlapped + { + get { return _overlapped; } + } + + public void SetCompletedSynchronously(int numBytes) + { + ReleaseNativeResource(); + TrySetResult(numBytes + _numBufferedBytes); + } + + public void RegisterForCancellation(CancellationToken cancellationToken) + { +#if DEBUG + Debug.Assert(cancellationToken.CanBeCanceled); + Debug.Assert(!_cancellationHasBeenRegistered, "Cannot register for cancellation twice"); + _cancellationHasBeenRegistered = true; +#endif + + // Quick check to make sure the IO hasn't completed + if (_overlapped != null) + { + var cancelCallback = s_cancelCallback; + if (cancelCallback == null) s_cancelCallback = cancelCallback = Cancel; + + // Register the cancellation only if the IO hasn't completed + long packedResult = Interlocked.CompareExchange(ref _result, RegisteringCancellation, NoResult); + if (packedResult == NoResult) + { + _cancellationRegistration = cancellationToken.Register(cancelCallback, this); + + // Switch the result, just in case IO completed while we were setting the registration + packedResult = Interlocked.Exchange(ref _result, NoResult); + } + else if (packedResult != CompletedCallback) + { + // Failed to set the result, IO is in the process of completing + // Attempt to take the packed result + packedResult = Interlocked.Exchange(ref _result, NoResult); + } + + // If we have a callback that needs to be completed + if ((packedResult != NoResult) && (packedResult != CompletedCallback) && (packedResult != RegisteringCancellation)) + { + CompleteCallback((ulong)packedResult); + } + } + } + + internal virtual void ReleaseNativeResource() + { + // Ensure that cancellation has been completed and cleaned up. + _cancellationRegistration.Dispose(); + + // Free the overlapped. + // NOTE: The cancellation must *NOT* be running at this point, or it may observe freed memory + // (this is why we disposed the registration above). + if (_overlapped != null) + { + _stream._fileHandle.ThreadPoolBinding.FreeNativeOverlapped(_overlapped); + _overlapped = null; + } + + // Ensure we're no longer set as the current completion source (we may not have been to begin with). + // Only one operation at a time is eligible to use the preallocated overlapped, + _stream.CompareExchangeCurrentOverlappedOwner(null, this); + } + + // When doing IO asynchronously (i.e. _isAsync==true), this callback is + // called by a free thread in the threadpool when the IO operation + // completes. + internal static unsafe void IOCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped) + { + // Extract the completion source from the overlapped. The state in the overlapped + // will either be a Win32FileStream (in the case where the preallocated overlapped was used), + // in which case the operation being completed is its _currentOverlappedOwner, or it'll + // be directly the FileStreamCompletion that's completing (in the case where the preallocated + // overlapped was already in use by another operation). + object state = ThreadPoolBoundHandle.GetNativeOverlappedState(pOverlapped); + FileStream fs = state as FileStream; + FileStreamCompletionSource completionSource = fs != null ? + fs._currentOverlappedOwner : + (FileStreamCompletionSource)state; + Debug.Assert(completionSource._overlapped == pOverlapped, "Overlaps don't match"); + + // Handle reading from & writing to closed pipes. While I'm not sure + // this is entirely necessary anymore, maybe it's possible for + // an async read on a pipe to be issued and then the pipe is closed, + // returning this error. This may very well be necessary. + ulong packedResult; + if (errorCode != 0 && errorCode != ERROR_BROKEN_PIPE && errorCode != ERROR_NO_DATA) + { + packedResult = ((ulong)ResultError | errorCode); + } + else + { + packedResult = ((ulong)ResultSuccess | numBytes); + } + + // Stow the result so that other threads can observe it + // And, if no other thread is registering cancellation, continue + if (NoResult == Interlocked.Exchange(ref completionSource._result, (long)packedResult)) + { + // Successfully set the state, attempt to take back the callback + if (Interlocked.Exchange(ref completionSource._result, CompletedCallback) != NoResult) + { + // Successfully got the callback, finish the callback + completionSource.CompleteCallback(packedResult); + } + // else: Some other thread stole the result, so now it is responsible to finish the callback + } + // else: Some other thread is registering a cancellation, so it *must* finish the callback + } + + private void CompleteCallback(ulong packedResult) + { + // Free up the native resource and cancellation registration + CancellationToken cancellationToken = _cancellationRegistration.Token; // access before disposing registration + ReleaseNativeResource(); + + // Unpack the result and send it to the user + long result = (long)(packedResult & ResultMask); + if (result == ResultError) + { + int errorCode = unchecked((int)(packedResult & uint.MaxValue)); + if (errorCode == Interop.Errors.ERROR_OPERATION_ABORTED) + { + TrySetCanceled(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)); + } + else + { + TrySetException(Win32Marshal.GetExceptionForWin32Error(errorCode)); + } + } + else + { + Debug.Assert(result == ResultSuccess, "Unknown result"); + TrySetResult((int)(packedResult & uint.MaxValue) + _numBufferedBytes); + } + } + + private static void Cancel(object state) + { + // WARNING: This may potentially be called under a lock (during cancellation registration) + + FileStreamCompletionSource completionSource = state as FileStreamCompletionSource; + Debug.Assert(completionSource != null, "Unknown state passed to cancellation"); + Debug.Assert(completionSource._overlapped != null && !completionSource.Task.IsCompleted, "IO should not have completed yet"); + + // If the handle is still valid, attempt to cancel the IO + if (!completionSource._stream._fileHandle.IsInvalid && + !Interop.Kernel32.CancelIoEx(completionSource._stream._fileHandle, completionSource._overlapped)) + { + int errorCode = Marshal.GetLastWin32Error(); + + // ERROR_NOT_FOUND is returned if CancelIoEx cannot find the request to cancel. + // This probably means that the IO operation has completed. + if (errorCode != Interop.Errors.ERROR_NOT_FOUND) + { + throw Win32Marshal.GetExceptionForWin32Error(errorCode); + } + } + } + } + + /// + /// Extends with to support disposing of a + /// when the operation has completed. This should only be used + /// when memory doesn't wrap a byte[]. + /// + private sealed class MemoryFileStreamCompletionSource : FileStreamCompletionSource + { + private MemoryHandle _handle; // mutable struct; do not make this readonly + + internal MemoryFileStreamCompletionSource(FileStream stream, int numBufferedBytes, ReadOnlyMemory memory) : + base(stream, numBufferedBytes, bytes: null) // this type handles the pinning, so null is passed for bytes + { + Debug.Assert(!MemoryMarshal.TryGetArray(memory, out ArraySegment array), "The base should be used directly if we can get the array."); + _handle = memory.Retain(pin: true); + } + + internal override void ReleaseNativeResource() + { + _handle.Dispose(); + base.ReleaseNativeResource(); + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/IO/IOException.cs b/external/corefx/src/Common/src/CoreLib/System/IO/IOException.cs similarity index 73% rename from external/corert/src/System.Private.CoreLib/src/System/IO/IOException.cs rename to external/corefx/src/Common/src/CoreLib/System/IO/IOException.cs index c75d5bef0c..89b25d5142 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/IO/IOException.cs +++ b/external/corefx/src/Common/src/CoreLib/System/IO/IOException.cs @@ -8,18 +8,19 @@ using System.Runtime.Serialization; namespace System.IO { [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class IOException : SystemException { public IOException() : base(SR.Arg_IOException) { - HResult = __HResults.COR_E_IO; + HResult = HResults.COR_E_IO; } public IOException(String message) : base(message) { - HResult = __HResults.COR_E_IO; + HResult = HResults.COR_E_IO; } public IOException(String message, int hresult) @@ -31,9 +32,11 @@ namespace System.IO public IOException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_IO; + HResult = HResults.COR_E_IO; } - protected IOException(SerializationInfo info, StreamingContext context) : base(info, context) { } + protected IOException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/IO/MemoryStream.cs b/external/corefx/src/Common/src/CoreLib/System/IO/MemoryStream.cs similarity index 67% rename from external/corert/src/System.Private.CoreLib/src/System/IO/MemoryStream.cs rename to external/corefx/src/Common/src/CoreLib/System/IO/MemoryStream.cs index 2369b90dde..c5e5ea918b 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/IO/MemoryStream.cs +++ b/external/corefx/src/Common/src/CoreLib/System/IO/MemoryStream.cs @@ -2,15 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Internal.Runtime.CompilerServices; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using System.Diagnostics; -using System.Diagnostics.Contracts; namespace System.IO { - // A MemoryStream represents a Stream in memory (i.e, it has no backing store). + // A MemoryStream represents a Stream in memory (ie, it has no backing store). // This stream may reduce the need for temporary buffers and files in // an application. // @@ -18,13 +18,11 @@ namespace System.IO // from an unsigned byte array, or you can create an empty one. Empty // memory streams are resizable, while ones created with a byte array provide // a stream "view" of the data. - [Serializable] public class MemoryStream : Stream { private byte[] _buffer; // Either allocated internally or externally. private int _origin; // For user-provided arrays, start at this origin private int _position; // read/write head. - [ContractPublicPropertyName("Length")] private int _length; // Number of bytes within the memory stream private int _capacity; // length of usable portion of buffer for stream // Note that _capacity == _buffer.Length for non-user-provided byte[]'s @@ -34,8 +32,8 @@ namespace System.IO private bool _exposable; // Whether the array can be returned to the user. private bool _isOpen; // Is this stream open or closed? - // In V2, if we get support for arrays of more than 2 GB worth of elements, - // consider removing this constraint, or setting it to Int64.MaxValue. + private Task _lastReadTask; // The last successful task returned from ReadAsync + private const int MemStreamMaxLength = int.MaxValue; public MemoryStream() @@ -46,9 +44,7 @@ namespace System.IO public MemoryStream(int capacity) { if (capacity < 0) - { throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_NegativeCapacity); - } _buffer = capacity != 0 ? new byte[capacity] : Array.Empty(); _capacity = capacity; @@ -67,9 +63,7 @@ namespace System.IO public MemoryStream(byte[] buffer, bool writable) { if (buffer == null) - { throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); - } _buffer = buffer; _length = _capacity = buffer.Length; @@ -92,58 +86,39 @@ namespace System.IO public MemoryStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible) { if (buffer == null) - { throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); - } if (index < 0) - { throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum); - } if (count < 0) - { throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - } if (buffer.Length - index < count) - { throw new ArgumentException(SR.Argument_InvalidOffLen); - } _buffer = buffer; _origin = _position = index; _length = _capacity = index + count; _writable = writable; - _exposable = publiclyVisible; // Can TryGetBuffer return the array? + _exposable = publiclyVisible; // Can TryGetBuffer/GetBuffer return the array? _expandable = false; _isOpen = true; } - public override bool CanRead - { - [Pure] - get - { return _isOpen; } - } + public override bool CanRead => _isOpen; - public override bool CanSeek - { - [Pure] - get - { return _isOpen; } - } + public override bool CanSeek => _isOpen; - public override bool CanWrite + public override bool CanWrite => _writable; + + private void EnsureNotClosed() { - [Pure] - get - { return _writable; } + if (!_isOpen) + throw Error.GetStreamIsClosed(); } private void EnsureWriteable() { if (!CanWrite) - { - throw new NotSupportedException(SR.NotSupported_UnwritableStream); - } + throw Error.GetWriteNotSupported(); } protected override void Dispose(bool disposing) @@ -155,7 +130,8 @@ namespace System.IO _isOpen = false; _writable = false; _expandable = false; - // Don't set buffer to null - allow TryGetBuffer & ToArray to work. + // Don't set buffer to null - allow TryGetBuffer, GetBuffer & ToArray to work. + _lastReadTask = null; } } finally @@ -170,9 +146,8 @@ namespace System.IO { // Check for overflow if (value < 0) - { - throw new IOException(SR.IO_IO_StreamTooLong); - } + throw new IOException(SR.IO_StreamTooLong); + if (value > _capacity) { int newCapacity = value; @@ -180,11 +155,21 @@ namespace System.IO { newCapacity = 256; } + + // We are ok with this overflowing since the next statement will deal + // with the cases where _capacity*2 overflows. if (newCapacity < _capacity * 2) { newCapacity = _capacity * 2; } + // We want to expand the array up to Array.MaxByteArrayLength + // And we want to give the user the value that they asked for + if ((uint)(_capacity * 2) > Array.MaxByteArrayLength) + { + newCapacity = value > Array.MaxByteArrayLength ? value : Array.MaxByteArrayLength; + } + Capacity = newCapacity; return true; } @@ -195,14 +180,29 @@ namespace System.IO { } -#pragma warning disable 1998 //async method with no await operators - public override async Task FlushAsync(CancellationToken cancellationToken) + public override Task FlushAsync(CancellationToken cancellationToken) { - cancellationToken.ThrowIfCancellationRequested(); + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); - Flush(); + try + { + Flush(); + return Task.CompletedTask; + } + catch (Exception ex) + { + return Task.FromException(ex); + } + } + + + public virtual byte[] GetBuffer() + { + if (!_exposable) + throw new UnauthorizedAccessException(SR.UnauthorizedAccess_MemStreamBuffer); + return _buffer; } -#pragma warning restore 1998 public virtual bool TryGetBuffer(out ArraySegment buffer) { @@ -216,12 +216,7 @@ namespace System.IO return true; } - public virtual byte[] GetBuffer() - { - if (!_exposable) - throw new UnauthorizedAccessException(SR.UnauthorizedAccess_MemStreamBuffer); - return _buffer; - } + // -------------- PERF: Internal functions for fast direct access of MemoryStream buffer (cf. BinaryReader for usage) --------------- // PERF: Internal sibling of GetBuffer, always returns a buffer (cf. GetBuffer()) internal byte[] InternalGetBuffer() @@ -232,26 +227,19 @@ namespace System.IO // PERF: True cursor position, we don't need _origin for direct access internal int InternalGetPosition() { - if (!_isOpen) - { - throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); - } return _position; } // PERF: Takes out Int32 as fast as possible internal int InternalReadInt32() { - if (!_isOpen) - { - throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); - } + EnsureNotClosed(); - int pos = (_position += 4); // use temp to avoid race + int pos = (_position += 4); // use temp to avoid a race condition if (pos > _length) { _position = _length; - throw new EndOfStreamException(SR.IO_EOF_ReadBeyondEOF); + throw Error.GetEndOfFile(); } return (int)(_buffer[pos - 4] | _buffer[pos - 3] << 8 | _buffer[pos - 2] << 16 | _buffer[pos - 1] << 24); } @@ -259,20 +247,13 @@ namespace System.IO // PERF: Get actual length of bytes available for read; do sanity checks; shift position - i.e. everything except actual copying bytes internal int InternalEmulateRead(int count) { - if (!_isOpen) - { - throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); - } + EnsureNotClosed(); int n = _length - _position; if (n > count) - { n = count; - } if (n < 0) - { n = 0; - } Debug.Assert(_position + n >= 0, "_position + n >= 0"); // len is less than 2^31 -1. _position += n; @@ -287,11 +268,7 @@ namespace System.IO { get { - if (!_isOpen) - { - throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); - } - + EnsureNotClosed(); return _capacity - _origin; } set @@ -299,17 +276,12 @@ namespace System.IO // Only update the capacity if the MS is expandable and the value is different than the current capacity. // Special behavior if the MS isn't expandable: we don't throw if value is the same as the current capacity if (value < Length) - { throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_SmallCapacity); - } - if (!_isOpen) - { - throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); - } + + EnsureNotClosed(); + if (!_expandable && (value != Capacity)) - { throw new NotSupportedException(SR.NotSupported_MemStreamNotExpandable); - } // MemoryStream has this invariant: _origin > 0 => !expandable (see ctors) if (_expandable && value != _capacity) @@ -321,7 +293,6 @@ namespace System.IO { Buffer.BlockCopy(_buffer, 0, newBuffer, 0, _length); } - _buffer = newBuffer; } else @@ -337,11 +308,7 @@ namespace System.IO { get { - if (!_isOpen) - { - throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); - } - + EnsureNotClosed(); return _length - _origin; } } @@ -350,28 +317,18 @@ namespace System.IO { get { - if (!_isOpen) - { - throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); - } - + EnsureNotClosed(); return _position - _origin; } set { if (value < 0) - { throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); - } - if (!_isOpen) - { - throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); - } - if (value > MemStreamMaxLength) - { - throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_StreamLength); - } + EnsureNotClosed(); + + if (value > MemStreamMaxLength) + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_StreamLength); _position = _origin + (int)value; } } @@ -379,35 +336,21 @@ namespace System.IO public override int Read(byte[] buffer, int offset, int count) { if (buffer == null) - { throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); - } if (offset < 0) - { throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); - } if (count < 0) - { throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - } if (buffer.Length - offset < count) - { throw new ArgumentException(SR.Argument_InvalidOffLen); - } - if (!_isOpen) - { - throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); - } + + EnsureNotClosed(); int n = _length - _position; if (n > count) - { n = count; - } if (n <= 0) - { return 0; - } Debug.Assert(_position + n >= 0, "_position + n >= 0"); // len is less than 2^31 -1. @@ -424,54 +367,107 @@ namespace System.IO return n; } + public override int Read(Span destination) + { + if (GetType() != typeof(MemoryStream)) + { + // MemoryStream is not sealed, and a derived type may have overridden Read(byte[], int, int) prior + // to this Read(Span) overload being introduced. In that case, this Read(Span) overload + // should use the behavior of Read(byte[],int,int) overload. + return base.Read(destination); + } + + EnsureNotClosed(); + + int n = Math.Min(_length - _position, destination.Length); + if (n <= 0) + return 0; + + // TODO https://github.com/dotnet/coreclr/issues/15076: + // Read(byte[], int, int) has an n <= 8 optimization, presumably based + // on benchmarking. Determine if/where such a cut-off is here and add + // an equivalent optimization if necessary. + new Span(_buffer, _position, n).CopyTo(destination); + + _position += n; + return n; + } + public override Task ReadAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken) { if (buffer == null) - { throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); - } if (offset < 0) - { throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); - } if (count < 0) - { throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - } if (buffer.Length - offset < count) - { throw new ArgumentException(SR.Argument_InvalidOffLen); + + // If cancellation was requested, bail early + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + + try + { + int n = Read(buffer, offset, count); + var t = _lastReadTask; + Debug.Assert(t == null || t.Status == TaskStatus.RanToCompletion, + "Expected that a stored last task completed successfully"); + return (t != null && t.Result == n) ? t : (_lastReadTask = Task.FromResult(n)); + } + catch (OperationCanceledException oce) + { + return Task.FromCancellation(oce); + } + catch (Exception exception) + { + return Task.FromException(exception); + } + } + + public override ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default(CancellationToken)) + { + if (cancellationToken.IsCancellationRequested) + { + return new ValueTask(Task.FromCanceled(cancellationToken)); } - return ReadAsyncImpl(buffer, offset, count, cancellationToken); + try + { + // ReadAsync(Memory,...) needs to delegate to an existing virtual to do the work, in case an existing derived type + // has changed or augmented the logic associated with reads. If the Memory wraps an array, we could delegate to + // ReadAsync(byte[], ...), but that would defeat part of the purpose, as ReadAsync(byte[], ...) often needs to allocate + // a Task for the return value, so we want to delegate to one of the synchronous methods. We could always + // delegate to the Read(Span) method, and that's the most efficient solution when dealing with a concrete + // MemoryStream, but if we're dealing with a type derived from MemoryStream, Read(Span) will end up delegating + // to Read(byte[], ...), which requires it to get a byte[] from ArrayPool and copy the data. So, we special-case the + // very common case of the Memory wrapping an array: if it does, we delegate to Read(byte[], ...) with it, + // as that will be efficient in both cases, and we fall back to Read(Span) if the Memory wrapped something + // else; if this is a concrete MemoryStream, that'll be efficient, and only in the case where the Memory wrapped + // something other than an array and this is a MemoryStream-derived type that doesn't override Read(Span) will + // it then fall back to doing the ArrayPool/copy behavior. + return new ValueTask( + destination.TryGetArray(out ArraySegment destinationArray) ? + Read(destinationArray.Array, destinationArray.Offset, destinationArray.Count) : + Read(destination.Span)); + } + catch (OperationCanceledException oce) + { + return new ValueTask(Task.FromCancellation(oce)); + } + catch (Exception exception) + { + return new ValueTask(Task.FromException(exception)); + } } -#pragma warning disable 1998 //async method with no await operators - private async Task ReadAsyncImpl(Byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - return Read(buffer, offset, count); - } -#pragma warning restore 1998 - - public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) => - TaskToApm.Begin(ReadAsync(buffer, offset, count, CancellationToken.None), callback, state); - - public override int EndRead(IAsyncResult asyncResult) => - TaskToApm.End(asyncResult); - public override int ReadByte() { - if (!_isOpen) - { - throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); - } + EnsureNotClosed(); if (_position >= _length) - { return -1; - } return _buffer[_position++]; } @@ -517,16 +513,11 @@ namespace System.IO // To be safe we will only use this implementation in cases where we know it is safe to do so, // and delegate to our base class (which will call into ReadAsync) when we are not sure. if (GetType() != typeof(MemoryStream)) - { return base.CopyToAsync(destination, bufferSize, cancellationToken); - } - return CopyToAsyncImpl(destination, bufferSize, cancellationToken); - } - - private async Task CopyToAsyncImpl(Stream destination, int bufferSize, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); + // If cancelled - return fast: + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); // Avoid copying data from this buffer into a temp buffer: // (require that InternalEmulateRead does not throw, @@ -535,29 +526,34 @@ namespace System.IO int pos = _position; int n = InternalEmulateRead(_length - _position); + // If we were already at or past the end, there's no copying to do so just quit. + if (n == 0) + return Task.CompletedTask; + // If destination is not a memory stream, write there asynchronously: MemoryStream memStrDest = destination as MemoryStream; if (memStrDest == null) + return destination.WriteAsync(_buffer, pos, n, cancellationToken); + + try { - await destination.WriteAsync(_buffer, pos, n, cancellationToken).ConfigureAwait(false); - } - else - { + // If destination is a MemoryStream, CopyTo synchronously: memStrDest.Write(_buffer, pos, n); + return Task.CompletedTask; + } + catch (Exception ex) + { + return Task.FromException(ex); } } public override long Seek(long offset, SeekOrigin loc) { - if (!_isOpen) - { - throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); - } + EnsureNotClosed(); + if (offset > MemStreamMaxLength) - { throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_StreamLength); - } switch (loc) { @@ -565,10 +561,7 @@ namespace System.IO { int tempPosition = unchecked(_origin + (int)offset); if (offset < 0 || tempPosition < _origin) - { - throw new IOException(SR.IO_IO_SeekBeforeBegin); - } - + throw new IOException(SR.IO_SeekBeforeBegin); _position = tempPosition; break; } @@ -576,10 +569,7 @@ namespace System.IO { int tempPosition = unchecked(_position + (int)offset); if (unchecked(_position + offset) < _origin || tempPosition < _origin) - { - throw new IOException(SR.IO_IO_SeekBeforeBegin); - } - + throw new IOException(SR.IO_SeekBeforeBegin); _position = tempPosition; break; } @@ -587,10 +577,7 @@ namespace System.IO { int tempPosition = unchecked(_length + (int)offset); if (unchecked(_length + offset) < _origin || tempPosition < _origin) - { - throw new IOException(SR.IO_IO_SeekBeforeBegin); - } - + throw new IOException(SR.IO_SeekBeforeBegin); _position = tempPosition; break; } @@ -604,88 +591,63 @@ namespace System.IO // Sets the length of the stream to a given value. The new // value must be nonnegative and less than the space remaining in - // the array, Int32.MaxValue - origin + // the array, int.MaxValue - origin // Origin is 0 in all cases other than a MemoryStream created on // top of an existing array and a specific starting offset was passed // into the MemoryStream constructor. The upper bounds prevents any // situations where a stream may be created on top of an array then // the stream is made longer than the maximum possible length of the - // array (Int32.MaxValue). + // array (int.MaxValue). // public override void SetLength(long value) { if (value < 0 || value > int.MaxValue) - { throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_StreamLength); - } + EnsureWriteable(); // Origin wasn't publicly exposed above. Debug.Assert(MemStreamMaxLength == int.MaxValue); // Check parameter validation logic in this method if this fails. if (value > (int.MaxValue - _origin)) - { throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_StreamLength); - } int newLength = _origin + (int)value; bool allocatedNewArray = EnsureCapacity(newLength); if (!allocatedNewArray && newLength > _length) - { Array.Clear(_buffer, _length, newLength - _length); - } - _length = newLength; if (_position > newLength) - { _position = newLength; - } } public virtual byte[] ToArray() { - //BCLDebug.Perf(_exposable, "MemoryStream::GetBuffer will let you avoid a copy."); int count = _length - _origin; if (count == 0) - { return Array.Empty(); - } - byte[] copy = new byte[count]; - Buffer.BlockCopy(_buffer, _origin, copy, 0, _length - _origin); + Buffer.BlockCopy(_buffer, _origin, copy, 0, count); return copy; } public override void Write(byte[] buffer, int offset, int count) { if (buffer == null) - { throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); - } if (offset < 0) - { throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); - } if (count < 0) - { throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - } if (buffer.Length - offset < count) - { throw new ArgumentException(SR.Argument_InvalidOffLen); - } - if (!_isOpen) - { - throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); - } + EnsureNotClosed(); EnsureWriteable(); int i = _position + count; // Check for overflow if (i < 0) - { - throw new IOException(SR.IO_IO_StreamTooLong); - } + throw new IOException(SR.IO_StreamTooLong); if (i > _length) { @@ -719,49 +681,111 @@ namespace System.IO _position = i; } + public override void Write(ReadOnlySpan source) + { + if (GetType() != typeof(MemoryStream)) + { + // MemoryStream is not sealed, and a derived type may have overridden Write(byte[], int, int) prior + // to this Write(Span) overload being introduced. In that case, this Write(Span) overload + // should use the behavior of Write(byte[],int,int) overload. + base.Write(source); + return; + } + + EnsureNotClosed(); + EnsureWriteable(); + + // Check for overflow + int i = _position + source.Length; + if (i < 0) + throw new IOException(SR.IO_StreamTooLong); + + if (i > _length) + { + bool mustZero = _position > _length; + if (i > _capacity) + { + bool allocatedNewArray = EnsureCapacity(i); + if (allocatedNewArray) + { + mustZero = false; + } + } + if (mustZero) + { + Array.Clear(_buffer, _length, i - _length); + } + _length = i; + } + + source.CopyTo(new Span(_buffer, _position, source.Length)); + _position = i; + } + public override Task WriteAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken) { if (buffer == null) - { throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); - } if (offset < 0) - { throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); - } if (count < 0) - { throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - } if (buffer.Length - offset < count) - { throw new ArgumentException(SR.Argument_InvalidOffLen); + + // If cancellation is already requested, bail early + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + + try + { + Write(buffer, offset, count); + return Task.CompletedTask; + } + catch (OperationCanceledException oce) + { + return Task.FromCancellation(oce); + } + catch (Exception exception) + { + return Task.FromException(exception); + } + } + + public override Task WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken = default(CancellationToken)) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); } - return WriteAsyncImpl(buffer, offset, count, cancellationToken); + try + { + // See corresponding comment in ReadAsync for why we don't just always use Write(ReadOnlySpan). + // Unlike ReadAsync, we could delegate to WriteAsync(byte[], ...) here, but we don't for consistency. + if (MemoryMarshal.TryGetArray(source, out ArraySegment sourceArray)) + { + Write(sourceArray.Array, sourceArray.Offset, sourceArray.Count); + } + else + { + Write(source.Span); + } + return Task.CompletedTask; + } + catch (OperationCanceledException oce) + { + return Task.FromCancellation(oce); + } + catch (Exception exception) + { + return Task.FromException(exception); + } } -#pragma warning disable 1998 //async method with no await operators - private async Task WriteAsyncImpl(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - Write(buffer, offset, count); - } -#pragma warning restore 1998 - - public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) => - TaskToApm.Begin(WriteAsync(buffer, offset, count, CancellationToken.None), callback, state); - - public override void EndWrite(IAsyncResult asyncResult) => - TaskToApm.End(asyncResult); - public override void WriteByte(byte value) { - if (!_isOpen) - { - throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); - } + EnsureNotClosed(); EnsureWriteable(); if (_position >= _length) @@ -789,13 +813,9 @@ namespace System.IO public virtual void WriteTo(Stream stream) { if (stream == null) - { throw new ArgumentNullException(nameof(stream), SR.ArgumentNull_Stream); - } - if (!_isOpen) - { - throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); - } + + EnsureNotClosed(); stream.Write(_buffer, _origin, _length - _origin); } diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/Path.Unix.cs b/external/corefx/src/Common/src/CoreLib/System/IO/Path.Unix.cs new file mode 100644 index 0000000000..1143c05208 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/Path.Unix.cs @@ -0,0 +1,205 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; + +namespace System.IO +{ + public static partial class Path + { + public static char[] GetInvalidFileNameChars() => new char[] { '\0', '/' }; + + public static char[] GetInvalidPathChars() => new char[] { '\0' }; + + // Expands the given path to a fully qualified path. + public static string GetFullPath(string path) + { + if (path == null) + throw new ArgumentNullException(nameof(path)); + + if (path.Length == 0) + throw new ArgumentException(SR.Arg_PathEmpty, nameof(path)); + + if (path.IndexOf('\0') != -1) + throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path)); + + // Expand with current directory if necessary + if (!IsPathRooted(path)) + { + path = Combine(Interop.Sys.GetCwd(), path); + } + + // We would ideally use realpath to do this, but it resolves symlinks, requires that the file actually exist, + // and turns it into a full path, which we only want if fullCheck is true. + string collapsedString = RemoveRelativeSegments(path); + + Debug.Assert(collapsedString.Length < path.Length || collapsedString.ToString() == path, + "Either we've removed characters, or the string should be unmodified from the input path."); + + string result = collapsedString.Length == 0 ? PathInternal.DirectorySeparatorCharAsString : collapsedString; + + return result; + } + + /// + /// Try to remove relative segments from the given path (without combining with a root). + /// + /// Skip the specified number of characters before evaluating. + private static string RemoveRelativeSegments(string path, int skip = 0) + { + bool flippedSeparator = false; + + // Remove "//", "/./", and "/../" from the path by copying each character to the output, + // except the ones we're removing, such that the builder contains the normalized path + // at the end. + var sb = StringBuilderCache.Acquire(path.Length); + if (skip > 0) + { + sb.Append(path, 0, skip); + } + + for (int i = skip; i < path.Length; i++) + { + char c = path[i]; + + if (PathInternal.IsDirectorySeparator(c) && i + 1 < path.Length) + { + // Skip this character if it's a directory separator and if the next character is, too, + // e.g. "parent//child" => "parent/child" + if (PathInternal.IsDirectorySeparator(path[i + 1])) + { + continue; + } + + // Skip this character and the next if it's referring to the current directory, + // e.g. "parent/./child" =? "parent/child" + if ((i + 2 == path.Length || PathInternal.IsDirectorySeparator(path[i + 2])) && + path[i + 1] == '.') + { + i++; + continue; + } + + // Skip this character and the next two if it's referring to the parent directory, + // e.g. "parent/child/../grandchild" => "parent/grandchild" + if (i + 2 < path.Length && + (i + 3 == path.Length || PathInternal.IsDirectorySeparator(path[i + 3])) && + path[i + 1] == '.' && path[i + 2] == '.') + { + // Unwind back to the last slash (and if there isn't one, clear out everything). + int s; + for (s = sb.Length - 1; s >= 0; s--) + { + if (PathInternal.IsDirectorySeparator(sb[s])) + { + sb.Length = s; + break; + } + } + if (s < 0) + { + sb.Length = 0; + } + + i += 2; + continue; + } + } + + // Normalize the directory separator if needed + if (c != PathInternal.DirectorySeparatorChar && c == PathInternal.AltDirectorySeparatorChar) + { + c = PathInternal.DirectorySeparatorChar; + flippedSeparator = true; + } + + sb.Append(c); + } + + if (flippedSeparator || sb.Length != path.Length) + { + return StringBuilderCache.GetStringAndRelease(sb); + } + else + { + // We haven't changed the source path, return the original + StringBuilderCache.Release(sb); + return path; + } + } + + private static string RemoveLongPathPrefix(string path) + { + return path; // nop. There's nothing special about "long" paths on Unix. + } + + public static string GetTempPath() + { + const string TempEnvVar = "TMPDIR"; + const string DefaultTempPath = "/tmp/"; + + // Get the temp path from the TMPDIR environment variable. + // If it's not set, just return the default path. + // If it is, return it, ensuring it ends with a slash. + string path = Environment.GetEnvironmentVariable(TempEnvVar); + return + string.IsNullOrEmpty(path) ? DefaultTempPath : + PathInternal.IsDirectorySeparator(path[path.Length - 1]) ? path : + path + PathInternal.DirectorySeparatorChar; + } + + public static string GetTempFileName() + { + const string Suffix = ".tmp"; + const int SuffixByteLength = 4; + + // mkstemps takes a char* and overwrites the XXXXXX with six characters + // that'll result in a unique file name. + string template = GetTempPath() + "tmpXXXXXX" + Suffix + "\0"; + byte[] name = Encoding.UTF8.GetBytes(template); + + // Create, open, and close the temp file. + IntPtr fd = Interop.CheckIo(Interop.Sys.MksTemps(name, SuffixByteLength)); + Interop.Sys.Close(fd); // ignore any errors from close; nothing to do if cleanup isn't possible + + // 'name' is now the name of the file + Debug.Assert(name[name.Length - 1] == '\0'); + return Encoding.UTF8.GetString(name, 0, name.Length - 1); // trim off the trailing '\0' + } + + public static bool IsPathRooted(string path) + { + if (path == null) + return false; + + return path.Length > 0 && path[0] == PathInternal.DirectorySeparatorChar; + } + + // The resulting string is null if path is null. If the path is empty or + // only contains whitespace characters an ArgumentException gets thrown. + public static string GetPathRoot(string path) + { + if (path == null) return null; + if (PathInternal.IsEffectivelyEmpty(path)) + throw new ArgumentException(SR.Arg_PathEmpty, nameof(path)); + + return IsPathRooted(path) ? PathInternal.DirectorySeparatorCharAsString : String.Empty; + } + + /// Gets whether the system is case-sensitive. + internal static bool IsCaseSensitive + { + get + { + #if PLATFORM_OSX + return false; + #else + return true; + #endif + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/Path.Windows.cs b/external/corefx/src/Common/src/CoreLib/System/IO/Path.Windows.cs new file mode 100644 index 0000000000..5d92d3b490 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/Path.Windows.cs @@ -0,0 +1,155 @@ +// 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.Diagnostics; +using System.Text; + +namespace System.IO +{ + public static partial class Path + { + public static char[] GetInvalidFileNameChars() => new char[] + { + '\"', '<', '>', '|', '\0', + (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10, + (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20, + (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30, + (char)31, ':', '*', '?', '\\', '/' + }; + + public static char[] GetInvalidPathChars() => new char[] + { + '|', '\0', + (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10, + (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20, + (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30, + (char)31 + }; + + // The max total path is 260, and the max individual component length is 255. + // For example, D:\<256 char file name> isn't legal, even though it's under 260 chars. + internal const int MaxPath = 260; + + // Expands the given path to a fully qualified path. + public static string GetFullPath(string path) + { + if (path == null) + throw new ArgumentNullException(nameof(path)); + + // Embedded null characters are the only invalid character case we want to check up front. + // This is because the nulls will signal the end of the string to Win32 and therefore have + // unpredictable results. Other invalid characters we give a chance to be normalized out. + if (path.IndexOf('\0') != -1) + throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path)); + + if (PathInternal.IsExtended(path)) + { + // We can't really know what is valid for all cases of extended paths. + // + // - object names can include other characters as well (':', '/', etc.) + // - even file objects have different rules (pipe names can contain most characters) + // + // As such we will do no further analysis of extended paths to avoid blocking known and unknown + // scenarios as well as minimizing compat breaks should we block now and need to unblock later. + return path; + } + + bool isDevice = PathInternal.IsDevice(path); + if (!isDevice) + { + // Toss out paths with colons that aren't a valid drive specifier. + // Cannot start with a colon and can only be of the form "C:". + // (Note that we used to explicitly check "http:" and "file:"- these are caught by this check now.) + int startIndex = PathInternal.PathStartSkip(path); + + // Move past the colon + startIndex += 2; + + if ((path.Length > 0 && path[0] == PathInternal.VolumeSeparatorChar) + || (path.Length >= startIndex && path[startIndex - 1] == PathInternal.VolumeSeparatorChar && !PathInternal.IsValidDriveChar(path[startIndex - 2])) + || (path.Length > startIndex && path.IndexOf(PathInternal.VolumeSeparatorChar, startIndex) != -1)) + { + throw new NotSupportedException(SR.Format(SR.Argument_PathFormatNotSupported_Path, path)); + } + } + + // Technically this doesn't matter but we used to throw for this case + if (PathInternal.IsEffectivelyEmpty(path)) + throw new ArgumentException(SR.Arg_PathEmpty, nameof(path)); + + // We don't want to check invalid characters for device format- see comments for extended above + string fullPath = PathHelper.Normalize(path, checkInvalidCharacters: !isDevice, expandShortPaths: true); + + if (!isDevice) + { + // Emulate FileIOPermissions checks, retained for compatibility (normal invalid characters have already been checked) + if (PathInternal.HasWildCardCharacters(fullPath)) + throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path)); + } + + return fullPath; + } + + public static string GetTempPath() + { + StringBuilder sb = StringBuilderCache.Acquire(MaxPath); + uint r = Interop.Kernel32.GetTempPathW(MaxPath, sb); + if (r == 0) + throw Win32Marshal.GetExceptionForLastWin32Error(); + return GetFullPath(StringBuilderCache.GetStringAndRelease(sb)); + } + + // Returns a unique temporary file name, and creates a 0-byte file by that + // name on disk. + public static string GetTempFileName() + { + string path = GetTempPath(); + + StringBuilder sb = StringBuilderCache.Acquire(MaxPath); + uint r = Interop.Kernel32.GetTempFileNameW(path, "tmp", 0, sb); + if (r == 0) + throw Win32Marshal.GetExceptionForLastWin32Error(); + return StringBuilderCache.GetStringAndRelease(sb); + } + + // Tests if the given path contains a root. A path is considered rooted + // if it starts with a backslash ("\") or a valid drive letter and a colon (":"). + public static bool IsPathRooted(string path) + { + if (path != null) + { + int length = path.Length; + if ((length >= 1 && PathInternal.IsDirectorySeparator(path[0])) || + (length >= 2 && PathInternal.IsValidDriveChar(path[0]) && path[1] == PathInternal.VolumeSeparatorChar)) + return true; + } + return false; + } + + // Returns the root portion of the given path. The resulting string + // consists of those rightmost characters of the path that constitute the + // root of the path. Possible patterns for the resulting string are: An + // empty string (a relative path on the current drive), "\" (an absolute + // path on the current drive), "X:" (a relative path on a given drive, + // where X is the drive letter), "X:\" (an absolute path on a given drive), + // and "\\server\share" (a UNC path for a given server and share name). + // The resulting string is null if path is null. If the path is empty or + // only contains whitespace characters an ArgumentException gets thrown. + public static string GetPathRoot(string path) + { + if (path == null) return null; + if (PathInternal.IsEffectivelyEmpty(path)) + throw new ArgumentException(SR.Arg_PathEmpty, nameof(path)); + + // Need to return the normalized directory separator + path = PathInternal.NormalizeDirectorySeparators(path); + + int pathRoot = PathInternal.GetRootLength(path); + return pathRoot <= 0 ? string.Empty : path.Substring(0, pathRoot); + } + + /// Gets whether the system is case-sensitive. + internal static bool IsCaseSensitive { get { return false; } } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/Path.cs b/external/corefx/src/Common/src/CoreLib/System/IO/Path.cs new file mode 100644 index 0000000000..9f3f486000 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/Path.cs @@ -0,0 +1,573 @@ +// 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.Diagnostics; +using System.Text; + +namespace System.IO +{ + // Provides methods for processing file system strings in a cross-platform manner. + // Most of the methods don't do a complete parsing (such as examining a UNC hostname), + // but they will handle most string operations. + public static partial class Path + { + // Public static readonly variant of the separators. The Path implementation itself is using + // internal const variant of the separators for better performance. + public static readonly char DirectorySeparatorChar = PathInternal.DirectorySeparatorChar; + public static readonly char AltDirectorySeparatorChar = PathInternal.AltDirectorySeparatorChar; + public static readonly char VolumeSeparatorChar = PathInternal.VolumeSeparatorChar; + public static readonly char PathSeparator = PathInternal.PathSeparator; + + // For generating random file names + // 8 random bytes provides 12 chars in our encoding for the 8.3 name. + private const int KeyLength = 8; + + [Obsolete("Please use GetInvalidPathChars or GetInvalidFileNameChars instead.")] + public static readonly char[] InvalidPathChars = GetInvalidPathChars(); + + // Changes the extension of a file path. The path parameter + // specifies a file path, and the extension parameter + // specifies a file extension (with a leading period, such as + // ".exe" or ".cs"). + // + // The function returns a file path with the same root, directory, and base + // name parts as path, but with the file extension changed to + // the specified extension. If path is null, the function + // returns null. If path does not contain a file extension, + // the new file extension is appended to the path. If extension + // is null, any existing extension is removed from path. + public static string ChangeExtension(string path, string extension) + { + if (path != null) + { + string s = path; + for (int i = path.Length - 1; i >= 0; i--) + { + char ch = path[i]; + if (ch == '.') + { + s = path.Substring(0, i); + break; + } + if (PathInternal.IsDirectoryOrVolumeSeparator(ch)) break; + } + + if (extension != null && path.Length != 0) + { + s = (extension.Length == 0 || extension[0] != '.') ? + s + "." + extension : + s + extension; + } + + return s; + } + return null; + } + + // Returns the directory path of a file path. This method effectively + // removes the last element of the given file path, i.e. it returns a + // string consisting of all characters up to but not including the last + // backslash ("\") in the file path. The returned value is null if the file + // path is null or if the file path denotes a root (such as "\", "C:", or + // "\\server\share"). + public static string GetDirectoryName(string path) + { + if (path == null) + return null; + + if (PathInternal.IsEffectivelyEmpty(path)) + throw new ArgumentException(SR.Arg_PathEmpty, nameof(path)); + + path = PathInternal.NormalizeDirectorySeparators(path); + int root = PathInternal.GetRootLength(path); + + int i = path.Length; + if (i > root) + { + while (i > root && !PathInternal.IsDirectorySeparator(path[--i])) ; + return path.Substring(0, i); + } + + return null; + } + + // Returns the extension of the given path. The returned value includes the + // period (".") character of the extension except when you have a terminal period when you get string.Empty, such as ".exe" or + // ".cpp". The returned value is null if the given path is + // null or if the given path does not include an extension. + public static string GetExtension(string path) + { + if (path == null) + return null; + + int length = path.Length; + for (int i = length - 1; i >= 0; i--) + { + char ch = path[i]; + if (ch == '.') + { + if (i != length - 1) + return path.Substring(i, length - i); + else + return string.Empty; + } + if (PathInternal.IsDirectoryOrVolumeSeparator(ch)) + break; + } + return string.Empty; + } + + // Returns the name and extension parts of the given path. The resulting + // string contains the characters of path that follow the last + // separator in path. The resulting string is null if path is null. + public static string GetFileName(string path) + { + if (path == null) + return null; + + int offset = PathInternal.FindFileNameIndex(path); + int count = path.Length - offset; + return path.Substring(offset, count); + } + + public static string GetFileNameWithoutExtension(string path) + { + if (path == null) + return null; + + int length = path.Length; + int offset = PathInternal.FindFileNameIndex(path); + + int end = path.LastIndexOf('.', length - 1, length - offset); + return end == -1 ? + path.Substring(offset) : // No extension was found + path.Substring(offset, end - offset); + } + + // Returns a cryptographically strong random 8.3 string that can be + // used as either a folder name or a file name. + public static unsafe string GetRandomFileName() + { + byte* pKey = stackalloc byte[KeyLength]; + Interop.GetRandomBytes(pKey, KeyLength); + + const int RandomFileNameLength = 12; + char* pRandomFileName = stackalloc char[RandomFileNameLength]; + Populate83FileNameFromRandomBytes(pKey, KeyLength, pRandomFileName, RandomFileNameLength); + return new string(pRandomFileName, 0, RandomFileNameLength); + } + + /// + /// Returns true if the path is fixed to a specific drive or UNC path. This method does no + /// validation of the path (URIs will be returned as relative as a result). + /// Returns false if the path specified is relative to the current drive or working directory. + /// + /// + /// Handles paths that use the alternate directory separator. It is a frequent mistake to + /// assume that rooted paths are not relative. This isn't the case. + /// "C:a" is drive relative- meaning that it will be resolved against the current directory + /// for C: (rooted, but relative). "C:\a" is rooted and not relative (the current directory + /// will not be used to modify the path). + /// + /// + /// Thrown if is null. + /// + public static bool IsPathFullyQualified(string path) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + return !PathInternal.IsPartiallyQualified(path); + } + + // Tests if a path includes a file extension. The result is + // true if the characters that follow the last directory + // separator ('\\' or '/') or volume separator (':') in the path include + // a period (".") other than a terminal period. The result is false otherwise. + public static bool HasExtension(string path) + { + if (path != null) + { + for (int i = path.Length - 1; i >= 0; i--) + { + char ch = path[i]; + if (ch == '.') + { + return i != path.Length - 1; + } + if (PathInternal.IsDirectoryOrVolumeSeparator(ch)) break; + } + } + return false; + } + + public static string Combine(string path1, string path2) + { + if (path1 == null || path2 == null) + throw new ArgumentNullException((path1 == null) ? nameof(path1) : nameof(path2)); + + return CombineNoChecks(path1, path2); + } + + public static string Combine(string path1, string path2, string path3) + { + if (path1 == null || path2 == null || path3 == null) + throw new ArgumentNullException((path1 == null) ? nameof(path1) : (path2 == null) ? nameof(path2) : nameof(path3)); + + return CombineNoChecks(path1, path2, path3); + } + + public static string Combine(string path1, string path2, string path3, string path4) + { + if (path1 == null || path2 == null || path3 == null || path4 == null) + throw new ArgumentNullException((path1 == null) ? nameof(path1) : (path2 == null) ? nameof(path2) : (path3 == null) ? nameof(path3) : nameof(path4)); + + return CombineNoChecks(path1, path2, path3, path4); + } + + public static string Combine(params string[] paths) + { + if (paths == null) + { + throw new ArgumentNullException(nameof(paths)); + } + + int finalSize = 0; + int firstComponent = 0; + + // We have two passes, the first calculates how large a buffer to allocate and does some precondition + // checks on the paths passed in. The second actually does the combination. + + for (int i = 0; i < paths.Length; i++) + { + if (paths[i] == null) + { + throw new ArgumentNullException(nameof(paths)); + } + + if (paths[i].Length == 0) + { + continue; + } + + if (IsPathRooted(paths[i])) + { + firstComponent = i; + finalSize = paths[i].Length; + } + else + { + finalSize += paths[i].Length; + } + + char ch = paths[i][paths[i].Length - 1]; + if (!PathInternal.IsDirectoryOrVolumeSeparator(ch)) + finalSize++; + } + + StringBuilder finalPath = StringBuilderCache.Acquire(finalSize); + + for (int i = firstComponent; i < paths.Length; i++) + { + if (paths[i].Length == 0) + { + continue; + } + + if (finalPath.Length == 0) + { + finalPath.Append(paths[i]); + } + else + { + char ch = finalPath[finalPath.Length - 1]; + if (!PathInternal.IsDirectoryOrVolumeSeparator(ch)) + { + finalPath.Append(PathInternal.DirectorySeparatorChar); + } + + finalPath.Append(paths[i]); + } + } + + return StringBuilderCache.GetStringAndRelease(finalPath); + } + + private static string CombineNoChecks(string path1, string path2) + { + if (path2.Length == 0) + return path1; + + if (path1.Length == 0) + return path2; + + if (IsPathRooted(path2)) + return path2; + + char ch = path1[path1.Length - 1]; + return PathInternal.IsDirectoryOrVolumeSeparator(ch) ? + path1 + path2 : + path1 + PathInternal.DirectorySeparatorCharAsString + path2; + } + + private static string CombineNoChecks(string path1, string path2, string path3) + { + if (path1.Length == 0) + return CombineNoChecks(path2, path3); + if (path2.Length == 0) + return CombineNoChecks(path1, path3); + if (path3.Length == 0) + return CombineNoChecks(path1, path2); + + if (IsPathRooted(path3)) + return path3; + if (IsPathRooted(path2)) + return CombineNoChecks(path2, path3); + + bool hasSep1 = PathInternal.IsDirectoryOrVolumeSeparator(path1[path1.Length - 1]); + bool hasSep2 = PathInternal.IsDirectoryOrVolumeSeparator(path2[path2.Length - 1]); + + if (hasSep1 && hasSep2) + { + return path1 + path2 + path3; + } + else if (hasSep1) + { + return path1 + path2 + PathInternal.DirectorySeparatorCharAsString + path3; + } + else if (hasSep2) + { + return path1 + PathInternal.DirectorySeparatorCharAsString + path2 + path3; + } + else + { + // string.Concat only has string-based overloads up to four arguments; after that requires allocating + // a params string[]. Instead, try to use a cached StringBuilder. + StringBuilder sb = StringBuilderCache.Acquire(path1.Length + path2.Length + path3.Length + 2); + sb.Append(path1) + .Append(PathInternal.DirectorySeparatorChar) + .Append(path2) + .Append(PathInternal.DirectorySeparatorChar) + .Append(path3); + return StringBuilderCache.GetStringAndRelease(sb); + } + } + + private static string CombineNoChecks(string path1, string path2, string path3, string path4) + { + if (path1.Length == 0) + return CombineNoChecks(path2, path3, path4); + if (path2.Length == 0) + return CombineNoChecks(path1, path3, path4); + if (path3.Length == 0) + return CombineNoChecks(path1, path2, path4); + if (path4.Length == 0) + return CombineNoChecks(path1, path2, path3); + + if (IsPathRooted(path4)) + return path4; + if (IsPathRooted(path3)) + return CombineNoChecks(path3, path4); + if (IsPathRooted(path2)) + return CombineNoChecks(path2, path3, path4); + + bool hasSep1 = PathInternal.IsDirectoryOrVolumeSeparator(path1[path1.Length - 1]); + bool hasSep2 = PathInternal.IsDirectoryOrVolumeSeparator(path2[path2.Length - 1]); + bool hasSep3 = PathInternal.IsDirectoryOrVolumeSeparator(path3[path3.Length - 1]); + + if (hasSep1 && hasSep2 && hasSep3) + { + // Use string.Concat overload that takes four strings + return path1 + path2 + path3 + path4; + } + else + { + // string.Concat only has string-based overloads up to four arguments; after that requires allocating + // a params string[]. Instead, try to use a cached StringBuilder. + StringBuilder sb = StringBuilderCache.Acquire(path1.Length + path2.Length + path3.Length + path4.Length + 3); + + sb.Append(path1); + if (!hasSep1) + { + sb.Append(PathInternal.DirectorySeparatorChar); + } + + sb.Append(path2); + if (!hasSep2) + { + sb.Append(PathInternal.DirectorySeparatorChar); + } + + sb.Append(path3); + if (!hasSep3) + { + sb.Append(PathInternal.DirectorySeparatorChar); + } + + sb.Append(path4); + + return StringBuilderCache.GetStringAndRelease(sb); + } + } + + private static readonly char[] s_base32Char = { + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', '0', '1', '2', '3', '4', '5'}; + + private static unsafe void Populate83FileNameFromRandomBytes(byte* bytes, int byteCount, char* chars, int charCount) + { + Debug.Assert(bytes != null); + Debug.Assert(chars != null); + + // This method requires bytes of length 8 and chars of length 12. + Debug.Assert(byteCount == 8, $"Unexpected {nameof(byteCount)}"); + Debug.Assert(charCount == 12, $"Unexpected {nameof(charCount)}"); + + byte b0 = bytes[0]; + byte b1 = bytes[1]; + byte b2 = bytes[2]; + byte b3 = bytes[3]; + byte b4 = bytes[4]; + + // Consume the 5 Least significant bits of the first 5 bytes + chars[0] = s_base32Char[b0 & 0x1F]; + chars[1] = s_base32Char[b1 & 0x1F]; + chars[2] = s_base32Char[b2 & 0x1F]; + chars[3] = s_base32Char[b3 & 0x1F]; + chars[4] = s_base32Char[b4 & 0x1F]; + + // Consume 3 MSB of b0, b1, MSB bits 6, 7 of b3, b4 + chars[5] = s_base32Char[( + ((b0 & 0xE0) >> 5) | + ((b3 & 0x60) >> 2))]; + + chars[6] = s_base32Char[( + ((b1 & 0xE0) >> 5) | + ((b4 & 0x60) >> 2))]; + + // Consume 3 MSB bits of b2, 1 MSB bit of b3, b4 + b2 >>= 5; + + Debug.Assert(((b2 & 0xF8) == 0), "Unexpected set bits"); + + if ((b3 & 0x80) != 0) + b2 |= 0x08; + if ((b4 & 0x80) != 0) + b2 |= 0x10; + + chars[7] = s_base32Char[b2]; + + // Set the file extension separator + chars[8] = '.'; + + // Consume the 5 Least significant bits of the remaining 3 bytes + chars[9] = s_base32Char[(bytes[5] & 0x1F)]; + chars[10] = s_base32Char[(bytes[6] & 0x1F)]; + chars[11] = s_base32Char[(bytes[7] & 0x1F)]; + } + + /// + /// Create a relative path from one path to another. Paths will be resolved before calculating the difference. + /// Default path comparison for the active platform will be used (OrdinalIgnoreCase for Windows or Mac, Ordinal for Unix). + /// + /// The source path the output should be relative to. This path is always considered to be a directory. + /// The destination path. + /// The relative path or if the paths don't share the same root. + /// Thrown if or is null or an empty string. + public static string GetRelativePath(string relativeTo, string path) + { + return GetRelativePath(relativeTo, path, StringComparison); + } + + private static string GetRelativePath(string relativeTo, string path, StringComparison comparisonType) + { + if (string.IsNullOrEmpty(relativeTo)) throw new ArgumentNullException(nameof(relativeTo)); + if (PathInternal.IsEffectivelyEmpty(path)) throw new ArgumentNullException(nameof(path)); + Debug.Assert(comparisonType == StringComparison.Ordinal || comparisonType == StringComparison.OrdinalIgnoreCase); + + relativeTo = GetFullPath(relativeTo); + path = GetFullPath(path); + + // Need to check if the roots are different- if they are we need to return the "to" path. + if (!PathInternal.AreRootsEqual(relativeTo, path, comparisonType)) + return path; + + int commonLength = PathInternal.GetCommonPathLength(relativeTo, path, ignoreCase: comparisonType == StringComparison.OrdinalIgnoreCase); + + // If there is nothing in common they can't share the same root, return the "to" path as is. + if (commonLength == 0) + return path; + + // Trailing separators aren't significant for comparison + int relativeToLength = relativeTo.Length; + if (PathInternal.EndsInDirectorySeparator(relativeTo)) + relativeToLength--; + + bool pathEndsInSeparator = PathInternal.EndsInDirectorySeparator(path); + int pathLength = path.Length; + if (pathEndsInSeparator) + pathLength--; + + // If we have effectively the same path, return "." + if (relativeToLength == pathLength && commonLength >= relativeToLength) return "."; + + // We have the same root, we need to calculate the difference now using the + // common Length and Segment count past the length. + // + // Some examples: + // + // C:\Foo C:\Bar L3, S1 -> ..\Bar + // C:\Foo C:\Foo\Bar L6, S0 -> Bar + // C:\Foo\Bar C:\Bar\Bar L3, S2 -> ..\..\Bar\Bar + // C:\Foo\Foo C:\Foo\Bar L7, S1 -> ..\Bar + + StringBuilder sb = StringBuilderCache.Acquire(Math.Max(relativeTo.Length, path.Length)); + + // Add parent segments for segments past the common on the "from" path + if (commonLength < relativeToLength) + { + sb.Append(PathInternal.ParentDirectoryPrefix); + + for (int i = commonLength; i < relativeToLength; i++) + { + if (PathInternal.IsDirectorySeparator(relativeTo[i])) + { + sb.Append(PathInternal.ParentDirectoryPrefix); + } + } + } + else if (PathInternal.IsDirectorySeparator(path[commonLength])) + { + // No parent segments and we need to eat the initial separator + // (C:\Foo C:\Foo\Bar case) + commonLength++; + } + + // Now add the rest of the "to" path, adding back the trailing separator + int count = pathLength - commonLength; + if (pathEndsInSeparator) + count++; + + sb.Append(path, commonLength, count); + return StringBuilderCache.GetStringAndRelease(sb); + } + + // StringComparison and IsCaseSensitive are also available in PathInternal.CaseSensitivity but we are + // too low in System.Runtime.Extensions to use it (no FileStream, etc.) + + /// Returns a comparison that can be used to compare file and directory names for equality. + internal static StringComparison StringComparison + { + get + { + return IsCaseSensitive ? + StringComparison.Ordinal : + StringComparison.OrdinalIgnoreCase; + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/PathHelper.Windows.cs b/external/corefx/src/Common/src/CoreLib/System/IO/PathHelper.Windows.cs new file mode 100644 index 0000000000..a0dba661f8 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/PathHelper.Windows.cs @@ -0,0 +1,384 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.IO +{ + /// + /// Wrapper to help with path normalization. + /// + internal class PathHelper + { + // Can't be over 8.3 and be a short name + private const int MaxShortName = 12; + + private const char LastAnsi = (char)255; + private const char Delete = (char)127; + + /// + /// Normalize the given path. + /// + /// + /// Normalizes via Win32 GetFullPathName(). Will also trim initial + /// spaces if the path is determined to be rooted. + /// + /// Note that invalid characters will be checked after the path is normalized, which could remove bad characters. (C:\|\..\a.txt -- C:\a.txt) + /// + /// Path to normalize + /// True to check for invalid characters + /// Attempt to expand short paths if true + /// Thrown if the path is an illegal UNC (does not contain a full server/share) or contains illegal characters. + /// Thrown if the path or a path segment exceeds the filesystem limits. + /// Thrown if Windows returns ERROR_FILE_NOT_FOUND. (See Win32Marshal.GetExceptionForWin32Error) + /// Thrown if Windows returns ERROR_PATH_NOT_FOUND. (See Win32Marshal.GetExceptionForWin32Error) + /// Thrown if Windows returns ERROR_ACCESS_DENIED. (See Win32Marshal.GetExceptionForWin32Error) + /// Thrown if Windows returns an error that doesn't map to the above. (See Win32Marshal.GetExceptionForWin32Error) + /// Normalized path + internal static string Normalize(string path, bool checkInvalidCharacters, bool expandShortPaths) + { + // Get the full path + StringBuffer fullPath = new StringBuffer(PathInternal.MaxShortPath); + + try + { + GetFullPathName(path, ref fullPath); + + // Checking path validity used to happen before getting the full path name. To avoid additional input allocation + // (to trim trailing whitespace) we now do it after the Win32 call. This will allow legitimate paths through that + // used to get kicked back (notably segments with invalid characters might get removed via ".."). + // + // There is no way that GetLongPath can invalidate the path so we'll do this (cheaper) check before we attempt to + // expand short file names. + + // Scan the path for: + // + // - Illegal path characters. + // - Invalid UNC paths like \\, \\server, \\server\. + + // As the path could be > 30K, we'll combine the validity scan. None of these checks are performed by the Win32 + // GetFullPathName() API. + + bool possibleShortPath = false; + bool foundTilde = false; + + // We can get UNCs as device paths through this code (e.g. \\.\UNC\), we won't validate them as there isn't + // an easy way to normalize without extensive cost (we'd have to hunt down the canonical name for any device + // path that contains UNC or to see if the path was doing something like \\.\GLOBALROOT\Device\Mup\, + // \\.\GLOBAL\UNC\, \\.\GLOBALROOT\GLOBAL??\UNC\, etc. + bool specialPath = fullPath.Length > 1 && fullPath[0] == '\\' && fullPath[1] == '\\'; + bool isDevice = PathInternal.IsDevice(ref fullPath); + bool possibleBadUnc = specialPath && !isDevice; + int index = specialPath ? 2 : 0; + int lastSeparator = specialPath ? 1 : 0; + int segmentLength; + char current; + + while (index < fullPath.Length) + { + current = fullPath[index]; + + // Try to skip deeper analysis. '?' and higher are valid/ignorable except for '\', '|', and '~' + if (current < '?' || current == '\\' || current == '|' || current == '~') + { + switch (current) + { + case '|': + case '>': + case '<': + case '\"': + if (checkInvalidCharacters) throw new ArgumentException(SR.Argument_InvalidPathChars); + foundTilde = false; + break; + case '~': + foundTilde = true; + break; + case '\\': + segmentLength = index - lastSeparator - 1; + lastSeparator = index; + + if (foundTilde) + { + if (segmentLength <= MaxShortName) + { + // Possibly a short path. + possibleShortPath = true; + } + + foundTilde = false; + } + + if (possibleBadUnc) + { + // If we're at the end of the path and this is the first separator, we're missing the share. + // Otherwise we're good, so ignore UNC tracking from here. + if (index == fullPath.Length - 1) + throw new ArgumentException(SR.Format(SR.Arg_PathIllegalUNC_Path, fullPath.ToString())); + else + possibleBadUnc = false; + } + + break; + + default: + if (checkInvalidCharacters && current < ' ') throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path)); + break; + } + } + + index++; + } + + if (possibleBadUnc) + throw new ArgumentException(SR.Format(SR.Arg_PathIllegalUNC_Path, fullPath.ToString())); + + segmentLength = fullPath.Length - lastSeparator - 1; + + if (foundTilde && segmentLength <= MaxShortName) + possibleShortPath = true; + + // Check for a short filename path and try and expand it. Technically you don't need to have a tilde for a short name, but + // this is how we've always done this. This expansion is costly so we'll continue to let other short paths slide. + if (expandShortPaths && possibleShortPath) + { + return TryExpandShortFileName(ref fullPath, originalPath: path); + } + else + { + if (fullPath.Length == path.Length && fullPath.StartsWith(path)) + { + // If we have the exact same string we were passed in, don't bother to allocate another string from the StringBuilder. + return path; + } + else + { + return fullPath.ToString(); + } + } + } + finally + { + // Clear the buffer + fullPath.Free(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsDosUnc(ref StringBuffer buffer) + { + return !PathInternal.IsDevice(ref buffer) && buffer.Length > 1 && buffer[0] == '\\' && buffer[1] == '\\'; + } + + private static unsafe void GetFullPathName(string path, ref StringBuffer fullPath) + { + // If the string starts with an extended prefix we would need to remove it from the path before we call GetFullPathName as + // it doesn't root extended paths correctly. We don't currently resolve extended paths, so we'll just assert here. + Debug.Assert(PathInternal.IsPartiallyQualified(path) || !PathInternal.IsExtended(path)); + + // Historically we would skip leading spaces *only* if the path started with a drive " C:" or a UNC " \\" + int startIndex = PathInternal.PathStartSkip(path); + + fixed (char* pathStart = path) + { + uint result = 0; + while ((result = Interop.Kernel32.GetFullPathNameW(pathStart + startIndex, (uint)fullPath.Capacity, fullPath.UnderlyingArray, IntPtr.Zero)) > fullPath.Capacity) + { + // Reported size is greater than the buffer size. Increase the capacity. + fullPath.EnsureCapacity(checked((int)result)); + } + + if (result == 0) + { + // Failure, get the error and throw + int errorCode = Marshal.GetLastWin32Error(); + if (errorCode == 0) + errorCode = Interop.Errors.ERROR_BAD_PATHNAME; + throw Win32Marshal.GetExceptionForWin32Error(errorCode, path); + } + + fullPath.Length = checked((int)result); + } + } + + private static int GetInputBuffer(ref StringBuffer content, bool isDosUnc, ref StringBuffer buffer) + { + int length = content.Length; + + length += isDosUnc + ? PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength + : PathInternal.DevicePrefixLength; + + buffer.EnsureCapacity(length + 1); + + if (isDosUnc) + { + // Put the extended UNC prefix (\\?\UNC\) in front of the path + buffer.CopyFrom(bufferIndex: 0, source: PathInternal.UncExtendedPathPrefix); + + // Copy the source buffer over after the existing UNC prefix + content.CopyTo( + bufferIndex: PathInternal.UncPrefixLength, + destination: ref buffer, + destinationIndex: PathInternal.UncExtendedPrefixLength, + count: content.Length - PathInternal.UncPrefixLength); + + // Return the prefix difference + return PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength; + } + else + { + int prefixSize = PathInternal.ExtendedPathPrefix.Length; + buffer.CopyFrom(bufferIndex: 0, source: PathInternal.ExtendedPathPrefix); + content.CopyTo(bufferIndex: 0, destination: ref buffer, destinationIndex: prefixSize, count: content.Length); + return prefixSize; + } + } + + private static string TryExpandShortFileName(ref StringBuffer outputBuffer, string originalPath) + { + // We guarantee we'll expand short names for paths that only partially exist. As such, we need to find the part of the path that actually does exist. To + // avoid allocating like crazy we'll create only one input array and modify the contents with embedded nulls. + + Debug.Assert(!PathInternal.IsPartiallyQualified(ref outputBuffer), "should have resolved by now"); + + // We'll have one of a few cases by now (the normalized path will have already: + // + // 1. Dos path (C:\) + // 2. Dos UNC (\\Server\Share) + // 3. Dos device path (\\.\C:\, \\?\C:\) + // + // We want to put the extended syntax on the front if it doesn't already have it, which may mean switching from \\.\. + // + // Note that we will never get \??\ here as GetFullPathName() does not recognize \??\ and will return it as C:\??\ (or whatever the current drive is). + + int rootLength = PathInternal.GetRootLength(ref outputBuffer); + bool isDevice = PathInternal.IsDevice(ref outputBuffer); + + StringBuffer inputBuffer = new StringBuffer(0); + try + { + bool isDosUnc = false; + int rootDifference = 0; + bool wasDotDevice = false; + + // Add the extended prefix before expanding to allow growth over MAX_PATH + if (isDevice) + { + // We have one of the following (\\?\ or \\.\) + inputBuffer.Append(ref outputBuffer); + + if (outputBuffer[2] == '.') + { + wasDotDevice = true; + inputBuffer[2] = '?'; + } + } + else + { + isDosUnc = IsDosUnc(ref outputBuffer); + rootDifference = GetInputBuffer(ref outputBuffer, isDosUnc, ref inputBuffer); + } + + rootLength += rootDifference; + int inputLength = inputBuffer.Length; + + bool success = false; + int foundIndex = inputBuffer.Length - 1; + + while (!success) + { + uint result = Interop.Kernel32.GetLongPathNameW(inputBuffer.UnderlyingArray, outputBuffer.UnderlyingArray, (uint)outputBuffer.Capacity); + + // Replace any temporary null we added + if (inputBuffer[foundIndex] == '\0') inputBuffer[foundIndex] = '\\'; + + if (result == 0) + { + // Look to see if we couldn't find the file + int error = Marshal.GetLastWin32Error(); + if (error != Interop.Errors.ERROR_FILE_NOT_FOUND && error != Interop.Errors.ERROR_PATH_NOT_FOUND) + { + // Some other failure, give up + break; + } + + // We couldn't find the path at the given index, start looking further back in the string. + foundIndex--; + + for (; foundIndex > rootLength && inputBuffer[foundIndex] != '\\'; foundIndex--) ; + if (foundIndex == rootLength) + { + // Can't trim the path back any further + break; + } + else + { + // Temporarily set a null in the string to get Windows to look further up the path + inputBuffer[foundIndex] = '\0'; + } + } + else if (result > outputBuffer.Capacity) + { + // Not enough space. The result count for this API does not include the null terminator. + outputBuffer.EnsureCapacity(checked((int)result)); + result = Interop.Kernel32.GetLongPathNameW(inputBuffer.UnderlyingArray, outputBuffer.UnderlyingArray, (uint)outputBuffer.Capacity); + } + else + { + // Found the path + success = true; + outputBuffer.Length = checked((int)result); + if (foundIndex < inputLength - 1) + { + // It was a partial find, put the non-existent part of the path back + outputBuffer.Append(ref inputBuffer, foundIndex, inputBuffer.Length - foundIndex); + } + } + } + + // Strip out the prefix and return the string + ref StringBuffer bufferToUse = ref Choose(success, ref outputBuffer, ref inputBuffer); + + // Switch back from \\?\ to \\.\ if necessary + if (wasDotDevice) + bufferToUse[2] = '.'; + + string returnValue = null; + + int newLength = (int)(bufferToUse.Length - rootDifference); + if (isDosUnc) + { + // Need to go from \\?\UNC\ to \\?\UN\\ + bufferToUse[PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength] = '\\'; + } + + // We now need to strip out any added characters at the front of the string + if (bufferToUse.SubstringEquals(originalPath, rootDifference, newLength)) + { + // Use the original path to avoid allocating + returnValue = originalPath; + } + else + { + returnValue = bufferToUse.Substring(rootDifference, newLength); + } + + return returnValue; + } + finally + { + inputBuffer.Free(); + } + } + + // Helper method to workaround lack of operator ? support for ref values + private static ref StringBuffer Choose(bool condition, ref StringBuffer s1, ref StringBuffer s2) + { + if (condition) return ref s1; + else return ref s2; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/PathInternal.Unix.cs b/external/corefx/src/Common/src/CoreLib/System/IO/PathInternal.Unix.cs new file mode 100644 index 0000000000..2f65a4252b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/PathInternal.Unix.cs @@ -0,0 +1,112 @@ +// 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.Diagnostics; +using System.Text; + +namespace System.IO +{ + /// Contains internal path helpers that are shared between many projects. + internal static partial class PathInternal + { + internal const char DirectorySeparatorChar = '/'; + internal const char AltDirectorySeparatorChar = '/'; + internal const char VolumeSeparatorChar = '/'; + internal const char PathSeparator = ':'; + + internal const string DirectorySeparatorCharAsString = "/"; + + // There is only one invalid path character in Unix + private const char InvalidPathChar = '\0'; + + internal const string ParentDirectoryPrefix = @"../"; + + internal static int GetRootLength(string path) + { + return path.Length > 0 && IsDirectorySeparator(path[0]) ? 1 : 0; + } + + internal static bool IsDirectorySeparator(char c) + { + // The alternate directory separator char is the same as the directory separator, + // so we only need to check one. + Debug.Assert(DirectorySeparatorChar == AltDirectorySeparatorChar); + return c == DirectorySeparatorChar; + } + + /// + /// Normalize separators in the given path. Compresses forward slash runs. + /// + internal static string NormalizeDirectorySeparators(string path) + { + if (string.IsNullOrEmpty(path)) return path; + + // Make a pass to see if we need to normalize so we can potentially skip allocating + bool normalized = true; + + for (int i = 0; i < path.Length; i++) + { + if (IsDirectorySeparator(path[i]) + && (i + 1 < path.Length && IsDirectorySeparator(path[i + 1]))) + { + normalized = false; + break; + } + } + + if (normalized) return path; + + StringBuilder builder = new StringBuilder(path.Length); + + for (int i = 0; i < path.Length; i++) + { + char current = path[i]; + + // Skip if we have another separator following + if (IsDirectorySeparator(current) + && (i + 1 < path.Length && IsDirectorySeparator(path[i + 1]))) + continue; + + builder.Append(current); + } + + return builder.ToString(); + } + + /// + /// Returns true if the character is a directory or volume separator. + /// + /// The character to test. + internal static bool IsDirectoryOrVolumeSeparator(char ch) + { + // The directory separator, volume separator, and the alternate directory + // separator should be the same on Unix, so we only need to check one. + Debug.Assert(DirectorySeparatorChar == AltDirectorySeparatorChar); + Debug.Assert(DirectorySeparatorChar == VolumeSeparatorChar); + return ch == DirectorySeparatorChar; + } + + internal static bool IsPartiallyQualified(string path) + { + // This is much simpler than Windows where paths can be rooted, but not fully qualified (such as Drive Relative) + // As long as the path is rooted in Unix it doesn't use the current directory and therefore is fully qualified. + return !Path.IsPathRooted(path); + } + + internal static string TrimEndingDirectorySeparator(string path) => + path.Length > 1 && IsDirectorySeparator(path[path.Length - 1]) ? // exclude root "/" + path.Substring(0, path.Length - 1) : + path; + + /// + /// Returns true if the path is effectively empty for the current OS. + /// For unix, this is empty or null. For Windows, this is empty, null, or + /// just spaces ((char)32). + /// + internal static bool IsEffectivelyEmpty(string path) + { + return string.IsNullOrEmpty(path); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/PathInternal.Windows.StringBuffer.cs b/external/corefx/src/Common/src/CoreLib/System/IO/PathInternal.Windows.StringBuffer.cs new file mode 100644 index 0000000000..84953df37b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/PathInternal.Windows.StringBuffer.cs @@ -0,0 +1,93 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.IO +{ + /// Contains internal path helpers that are shared between many projects. + internal static partial class PathInternal + { + /// + /// Returns true if the path uses the extended syntax (\\?\) + /// + internal static bool IsExtended(ref StringBuffer path) + { + // While paths like "//?/C:/" will work, they're treated the same as "\\.\" paths. + // Skipping of normalization will *only* occur if back slashes ('\') are used. + return path.Length >= DevicePrefixLength + && path[0] == '\\' + && (path[1] == '\\' || path[1] == '?') + && path[2] == '?' + && path[3] == '\\'; + } + + /// + /// Gets the length of the root of the path (drive, share, etc.). + /// + internal unsafe static int GetRootLength(ref StringBuffer path) + { + if (path.Length == 0) return 0; + + fixed (char* value = path.UnderlyingArray) + { + return GetRootLength(value, path.Length); + } + } + + /// + /// Returns true if the path uses any of the DOS device path syntaxes. ("\\.\", "\\?\", or "\??\") + /// + internal static bool IsDevice(ref StringBuffer path) + { + // If the path begins with any two separators is will be recognized and normalized and prepped with + // "\??\" for internal usage correctly. "\??\" is recognized and handled, "/??/" is not. + return IsExtended(ref path) + || + ( + path.Length >= DevicePrefixLength + && IsDirectorySeparator(path[0]) + && IsDirectorySeparator(path[1]) + && (path[2] == '.' || path[2] == '?') + && IsDirectorySeparator(path[3]) + ); + } + + /// + /// Returns true if the path specified is relative to the current drive or working directory. + /// Returns false if the path is fixed to a specific drive or UNC path. This method does no + /// validation of the path (URIs will be returned as relative as a result). + /// + /// + /// Handles paths that use the alternate directory separator. It is a frequent mistake to + /// assume that rooted paths (Path.IsPathRooted) are not relative. This isn't the case. + /// "C:a" is drive relative- meaning that it will be resolved against the current directory + /// for C: (rooted, but relative). "C:\a" is rooted and not relative (the current directory + /// will not be used to modify the path). + /// + internal static bool IsPartiallyQualified(ref StringBuffer path) + { + if (path.Length < 2) + { + // It isn't fixed, it must be relative. There is no way to specify a fixed + // path with one character (or less). + return true; + } + + if (IsDirectorySeparator(path[0])) + { + // There is no valid way to specify a relative path with two initial slashes or + // \? as ? isn't valid for drive relative paths and \??\ is equivalent to \\?\ + return !(path[1] == '?' || IsDirectorySeparator(path[1])); + } + + // The only way to specify a fixed path that doesn't begin with two slashes + // is the drive, colon, slash format- i.e. C:\ + return !((path.Length >= 3) + && (path[1] == VolumeSeparatorChar) + && IsDirectorySeparator(path[2])); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/PathInternal.Windows.cs b/external/corefx/src/Common/src/CoreLib/System/IO/PathInternal.Windows.cs new file mode 100644 index 0000000000..f315f43fd5 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/PathInternal.Windows.cs @@ -0,0 +1,428 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text; + +namespace System.IO +{ + /// Contains internal path helpers that are shared between many projects. + internal static partial class PathInternal + { + // All paths in Win32 ultimately end up becoming a path to a File object in the Windows object manager. Passed in paths get mapped through + // DosDevice symbolic links in the object tree to actual File objects under \Devices. To illustrate, this is what happens with a typical + // path "Foo" passed as a filename to any Win32 API: + // + // 1. "Foo" is recognized as a relative path and is appended to the current directory (say, "C:\" in our example) + // 2. "C:\Foo" is prepended with the DosDevice namespace "\??\" + // 3. CreateFile tries to create an object handle to the requested file "\??\C:\Foo" + // 4. The Object Manager recognizes the DosDevices prefix and looks + // a. First in the current session DosDevices ("\Sessions\1\DosDevices\" for example, mapped network drives go here) + // b. If not found in the session, it looks in the Global DosDevices ("\GLOBAL??\") + // 5. "C:" is found in DosDevices (in our case "\GLOBAL??\C:", which is a symbolic link to "\Device\HarddiskVolume6") + // 6. The full path is now "\Device\HarddiskVolume6\Foo", "\Device\HarddiskVolume6" is a File object and parsing is handed off + // to the registered parsing method for Files + // 7. The registered open method for File objects is invoked to create the file handle which is then returned + // + // There are multiple ways to directly specify a DosDevices path. The final format of "\??\" is one way. It can also be specified + // as "\\.\" (the most commonly documented way) and "\\?\". If the question mark syntax is used the path will skip normalization + // (essentially GetFullPathName()) and path length checks. + + // Windows Kernel-Mode Object Manager + // https://msdn.microsoft.com/en-us/library/windows/hardware/ff565763.aspx + // https://channel9.msdn.com/Shows/Going+Deep/Windows-NT-Object-Manager + // + // Introduction to MS-DOS Device Names + // https://msdn.microsoft.com/en-us/library/windows/hardware/ff548088.aspx + // + // Local and Global MS-DOS Device Names + // https://msdn.microsoft.com/en-us/library/windows/hardware/ff554302.aspx + + internal const char DirectorySeparatorChar = '\\'; + internal const char AltDirectorySeparatorChar = '/'; + internal const char VolumeSeparatorChar = ':'; + internal const char PathSeparator = ';'; + + internal const string DirectorySeparatorCharAsString = "\\"; + + internal const string ExtendedPathPrefix = @"\\?\"; + internal const string UncPathPrefix = @"\\"; + internal const string UncExtendedPrefixToInsert = @"?\UNC\"; + internal const string UncExtendedPathPrefix = @"\\?\UNC\"; + internal const string DevicePathPrefix = @"\\.\"; + internal const string ParentDirectoryPrefix = @"..\"; + + internal const int MaxShortPath = 260; + internal const int MaxShortDirectoryPath = 248; + // \\?\, \\.\, \??\ + internal const int DevicePrefixLength = 4; + // \\ + internal const int UncPrefixLength = 2; + // \\?\UNC\, \\.\UNC\ + internal const int UncExtendedPrefixLength = 8; + + /// + /// Returns true if the given character is a valid drive letter + /// + internal static bool IsValidDriveChar(char value) + { + return ((value >= 'A' && value <= 'Z') || (value >= 'a' && value <= 'z')); + } + + /// + /// Adds the extended path prefix (\\?\) if not already a device path, IF the path is not relative, + /// AND the path is more than 259 characters. (> MAX_PATH + null) + /// + internal static string EnsureExtendedPrefixOverMaxPath(string path) + { + if (path != null && path.Length >= MaxShortPath) + { + return EnsureExtendedPrefix(path); + } + else + { + return path; + } + } + + /// + /// Adds the extended path prefix (\\?\) if not relative or already a device path. + /// + internal static string EnsureExtendedPrefix(string path) + { + // Putting the extended prefix on the path changes the processing of the path. It won't get normalized, which + // means adding to relative paths will prevent them from getting the appropriate current directory inserted. + + // If it already has some variant of a device path (\??\, \\?\, \\.\, //./, etc.) we don't need to change it + // as it is either correct or we will be changing the behavior. When/if Windows supports long paths implicitly + // in the future we wouldn't want normalization to come back and break existing code. + + // In any case, all internal usages should be hitting normalize path (Path.GetFullPath) before they hit this + // shimming method. (Or making a change that doesn't impact normalization, such as adding a filename to a + // normalized base path.) + if (IsPartiallyQualified(path) || IsDevice(path)) + return path; + + // Given \\server\share in longpath becomes \\?\UNC\server\share + if (path.StartsWith(UncPathPrefix, StringComparison.OrdinalIgnoreCase)) + return path.Insert(2, UncExtendedPrefixToInsert); + + return ExtendedPathPrefix + path; + } + + /// + /// Returns true if the path uses any of the DOS device path syntaxes. ("\\.\", "\\?\", or "\??\") + /// + internal static bool IsDevice(string path) + { + // If the path begins with any two separators is will be recognized and normalized and prepped with + // "\??\" for internal usage correctly. "\??\" is recognized and handled, "/??/" is not. + return IsExtended(path) + || + ( + path.Length >= DevicePrefixLength + && IsDirectorySeparator(path[0]) + && IsDirectorySeparator(path[1]) + && (path[2] == '.' || path[2] == '?') + && IsDirectorySeparator(path[3]) + ); + } + + /// + /// Returns true if the path uses the canonical form of extended syntax ("\\?\" or "\??\"). If the + /// path matches exactly (cannot use alternate directory separators) Windows will skip normalization + /// and path length checks. + /// + internal static bool IsExtended(string path) + { + // While paths like "//?/C:/" will work, they're treated the same as "\\.\" paths. + // Skipping of normalization will *only* occur if back slashes ('\') are used. + return path.Length >= DevicePrefixLength + && path[0] == '\\' + && (path[1] == '\\' || path[1] == '?') + && path[2] == '?' + && path[3] == '\\'; + } + + /// + /// Check for known wildcard characters. '*' and '?' are the most common ones. + /// + internal static bool HasWildCardCharacters(string path) + { + // Question mark is part of dos device syntax so we have to skip if we are + int startIndex = IsDevice(path) ? ExtendedPathPrefix.Length : 0; + + // [MS - FSA] 2.1.4.4 Algorithm for Determining if a FileName Is in an Expression + // https://msdn.microsoft.com/en-us/library/ff469270.aspx + for (int i = startIndex; i < path.Length; i++) + { + char c = path[i]; + if (c <= '?') // fast path for common case - '?' is highest wildcard character + { + if (c == '\"' || c == '<' || c == '>' || c == '*' || c == '?') + return true; + } + } + + return false; + } + + /// + /// Gets the length of the root of the path (drive, share, etc.). + /// + internal unsafe static int GetRootLength(string path) + { + fixed (char* value = path) + { + return GetRootLength(value, path.Length); + } + } + + private unsafe static int GetRootLength(char* path, int pathLength) + { + int i = 0; + int volumeSeparatorLength = 2; // Length to the colon "C:" + int uncRootLength = 2; // Length to the start of the server name "\\" + + bool extendedSyntax = StartsWithOrdinal(path, pathLength, ExtendedPathPrefix); + bool extendedUncSyntax = StartsWithOrdinal(path, pathLength, UncExtendedPathPrefix); + if (extendedSyntax) + { + // Shift the position we look for the root from to account for the extended prefix + if (extendedUncSyntax) + { + // "\\" -> "\\?\UNC\" + uncRootLength = UncExtendedPathPrefix.Length; + } + else + { + // "C:" -> "\\?\C:" + volumeSeparatorLength += ExtendedPathPrefix.Length; + } + } + + if ((!extendedSyntax || extendedUncSyntax) && pathLength > 0 && IsDirectorySeparator(path[0])) + { + // UNC or simple rooted path (e.g. "\foo", NOT "\\?\C:\foo") + + i = 1; // Drive rooted (\foo) is one character + if (extendedUncSyntax || (pathLength > 1 && IsDirectorySeparator(path[1]))) + { + // UNC (\\?\UNC\ or \\), scan past the next two directory separators at most + // (e.g. to \\?\UNC\Server\Share or \\Server\Share\) + i = uncRootLength; + int n = 2; // Maximum separators to skip + while (i < pathLength && (!IsDirectorySeparator(path[i]) || --n > 0)) i++; + } + } + else if (pathLength >= volumeSeparatorLength && path[volumeSeparatorLength - 1] == VolumeSeparatorChar) + { + // Path is at least longer than where we expect a colon, and has a colon (\\?\A:, A:) + // If the colon is followed by a directory separator, move past it + i = volumeSeparatorLength; + if (pathLength >= volumeSeparatorLength + 1 && IsDirectorySeparator(path[volumeSeparatorLength])) i++; + } + return i; + } + + private unsafe static bool StartsWithOrdinal(char* source, int sourceLength, string value) + { + if (sourceLength < value.Length) return false; + for (int i = 0; i < value.Length; i++) + { + if (value[i] != source[i]) return false; + } + return true; + } + + /// + /// Returns true if the path specified is relative to the current drive or working directory. + /// Returns false if the path is fixed to a specific drive or UNC path. This method does no + /// validation of the path (URIs will be returned as relative as a result). + /// + /// + /// Handles paths that use the alternate directory separator. It is a frequent mistake to + /// assume that rooted paths (Path.IsPathRooted) are not relative. This isn't the case. + /// "C:a" is drive relative- meaning that it will be resolved against the current directory + /// for C: (rooted, but relative). "C:\a" is rooted and not relative (the current directory + /// will not be used to modify the path). + /// + internal static bool IsPartiallyQualified(string path) + { + if (path.Length < 2) + { + // It isn't fixed, it must be relative. There is no way to specify a fixed + // path with one character (or less). + return true; + } + + if (IsDirectorySeparator(path[0])) + { + // There is no valid way to specify a relative path with two initial slashes or + // \? as ? isn't valid for drive relative paths and \??\ is equivalent to \\?\ + return !(path[1] == '?' || IsDirectorySeparator(path[1])); + } + + // The only way to specify a fixed path that doesn't begin with two slashes + // is the drive, colon, slash format- i.e. C:\ + return !((path.Length >= 3) + && (path[1] == VolumeSeparatorChar) + && IsDirectorySeparator(path[2]) + // To match old behavior we'll check the drive character for validity as the path is technically + // not qualified if you don't have a valid drive. "=:\" is the "=" file's default data stream. + && IsValidDriveChar(path[0])); + } + + /// + /// Returns the characters to skip at the start of the path if it starts with space(s) and a drive or directory separator. + /// (examples are " C:", " \") + /// This is a legacy behavior of Path.GetFullPath(). + /// + /// + /// Note that this conflicts with IsPathRooted() which doesn't (and never did) such a skip. + /// + internal static int PathStartSkip(string path) + { + int startIndex = 0; + while (startIndex < path.Length && path[startIndex] == ' ') startIndex++; + + if (startIndex > 0 && (startIndex < path.Length && IsDirectorySeparator(path[startIndex])) + || (startIndex + 1 < path.Length && path[startIndex + 1] == ':' && IsValidDriveChar(path[startIndex]))) + { + // Go ahead and skip spaces as we're either " C:" or " \" + return startIndex; + } + + return 0; + } + + /// + /// True if the given character is a directory separator. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsDirectorySeparator(char c) + { + return c == DirectorySeparatorChar || c == AltDirectorySeparatorChar; + } + + /// + /// Normalize separators in the given path. Converts forward slashes into back slashes and compresses slash runs, keeping initial 2 if present. + /// Also trims initial whitespace in front of "rooted" paths (see PathStartSkip). + /// + /// This effectively replicates the behavior of the legacy NormalizePath when it was called with fullCheck=false and expandShortpaths=false. + /// The current NormalizePath gets directory separator normalization from Win32's GetFullPathName(), which will resolve relative paths and as + /// such can't be used here (and is overkill for our uses). + /// + /// Like the current NormalizePath this will not try and analyze periods/spaces within directory segments. + /// + /// + /// The only callers that used to use Path.Normalize(fullCheck=false) were Path.GetDirectoryName() and Path.GetPathRoot(). Both usages do + /// not need trimming of trailing whitespace here. + /// + /// GetPathRoot() could technically skip normalizing separators after the second segment- consider as a future optimization. + /// + /// For legacy desktop behavior with ExpandShortPaths: + /// - It has no impact on GetPathRoot() so doesn't need consideration. + /// - It could impact GetDirectoryName(), but only if the path isn't relative (C:\ or \\Server\Share). + /// + /// In the case of GetDirectoryName() the ExpandShortPaths behavior was undocumented and provided inconsistent results if the path was + /// fixed/relative. For example: "C:\PROGRA~1\A.TXT" would return "C:\Program Files" while ".\PROGRA~1\A.TXT" would return ".\PROGRA~1". If you + /// ultimately call GetFullPath() this doesn't matter, but if you don't or have any intermediate string handling could easily be tripped up by + /// this undocumented behavior. + /// + /// We won't match this old behavior because: + /// + /// 1. It was undocumented + /// 2. It was costly (extremely so if it actually contained '~') + /// 3. Doesn't play nice with string logic + /// 4. Isn't a cross-plat friendly concept/behavior + /// + internal static string NormalizeDirectorySeparators(string path) + { + if (string.IsNullOrEmpty(path)) return path; + + char current; + int start = PathStartSkip(path); + + if (start == 0) + { + // Make a pass to see if we need to normalize so we can potentially skip allocating + bool normalized = true; + + for (int i = 0; i < path.Length; i++) + { + current = path[i]; + if (IsDirectorySeparator(current) + && (current != DirectorySeparatorChar + // Check for sequential separators past the first position (we need to keep initial two for UNC/extended) + || (i > 0 && i + 1 < path.Length && IsDirectorySeparator(path[i + 1])))) + { + normalized = false; + break; + } + } + + if (normalized) return path; + } + + StringBuilder builder = new StringBuilder(path.Length); + + if (IsDirectorySeparator(path[start])) + { + start++; + builder.Append(DirectorySeparatorChar); + } + + for (int i = start; i < path.Length; i++) + { + current = path[i]; + + // If we have a separator + if (IsDirectorySeparator(current)) + { + // If the next is a separator, skip adding this + if (i + 1 < path.Length && IsDirectorySeparator(path[i + 1])) + { + continue; + } + + // Ensure it is the primary separator + current = DirectorySeparatorChar; + } + + builder.Append(current); + } + + return builder.ToString(); + } + + /// + /// Returns true if the character is a directory or volume separator. + /// + /// The character to test. + internal static bool IsDirectoryOrVolumeSeparator(char ch) + { + return IsDirectorySeparator(ch) || VolumeSeparatorChar == ch; + } + + /// + /// Returns true if the path is effectively empty for the current OS. + /// For unix, this is empty or null. For Windows, this is empty, null, or + /// just spaces ((char)32). + /// + internal static bool IsEffectivelyEmpty(string path) + { + if (string.IsNullOrEmpty(path)) + return true; + + foreach (char c in path) + { + if (c != ' ') + return false; + } + return true; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/PathInternal.cs b/external/corefx/src/Common/src/CoreLib/System/IO/PathInternal.cs new file mode 100644 index 0000000000..bfd69e9251 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/PathInternal.cs @@ -0,0 +1,139 @@ +// 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.Diagnostics; +using System.Text; + +namespace System.IO +{ + /// Contains internal path helpers that are shared between many projects. + internal static partial class PathInternal + { + /// + /// Returns the start index of the filename + /// in the given path, or 0 if no directory + /// or volume separator is found. + /// + /// The path in which to find the index of the filename. + /// + /// This method returns path.Length for + /// inputs like "/usr/foo/" on Unix. As such, + /// it is not safe for being used to index + /// the string without additional verification. + /// + internal static int FindFileNameIndex(string path) + { + Debug.Assert(path != null); + + for (int i = path.Length - 1; i >= 0; i--) + { + char ch = path[i]; + if (IsDirectoryOrVolumeSeparator(ch)) + return i + 1; + } + + return 0; // the whole path is the filename + } + + /// + /// Returns true if the path ends in a directory separator. + /// + internal static bool EndsInDirectorySeparator(string path) => + !string.IsNullOrEmpty(path) && IsDirectorySeparator(path[path.Length - 1]); + + /// + /// Get the common path length from the start of the string. + /// + internal static int GetCommonPathLength(string first, string second, bool ignoreCase) + { + int commonChars = EqualStartingCharacterCount(first, second, ignoreCase: ignoreCase); + + // If nothing matches + if (commonChars == 0) + return commonChars; + + // Or we're a full string and equal length or match to a separator + if (commonChars == first.Length + && (commonChars == second.Length || IsDirectorySeparator(second[commonChars]))) + return commonChars; + + if (commonChars == second.Length && IsDirectorySeparator(first[commonChars])) + return commonChars; + + // It's possible we matched somewhere in the middle of a segment e.g. C:\Foodie and C:\Foobar. + while (commonChars > 0 && !IsDirectorySeparator(first[commonChars - 1])) + commonChars--; + + return commonChars; + } + + /// + /// Gets the count of common characters from the left optionally ignoring case + /// + unsafe internal static int EqualStartingCharacterCount(string first, string second, bool ignoreCase) + { + if (string.IsNullOrEmpty(first) || string.IsNullOrEmpty(second)) return 0; + + int commonChars = 0; + + fixed (char* f = first) + fixed (char* s = second) + { + char* l = f; + char* r = s; + char* leftEnd = l + first.Length; + char* rightEnd = r + second.Length; + + while (l != leftEnd && r != rightEnd + && (*l == *r || (ignoreCase && char.ToUpperInvariant((*l)) == char.ToUpperInvariant((*r))))) + { + commonChars++; + l++; + r++; + } + } + + return commonChars; + } + + /// + /// Returns true if the two paths have the same root + /// + internal static bool AreRootsEqual(string first, string second, StringComparison comparisonType) + { + int firstRootLength = GetRootLength(first); + int secondRootLength = GetRootLength(second); + + return firstRootLength == secondRootLength + && string.Compare( + strA: first, + indexA: 0, + strB: second, + indexB: 0, + length: firstRootLength, + comparisonType: comparisonType) == 0; + } + + /// + /// Returns false for ".." unless it is specified as a part of a valid File/Directory name. + /// (Used to avoid moving up directories.) + /// + /// Valid: a..b abc..d + /// Invalid: ..ab ab.. .. abc..d\abc.. + /// + internal static void CheckSearchPattern(string searchPattern) + { + int index; + while ((index = searchPattern.IndexOf("..", StringComparison.Ordinal)) != -1) + { + // Terminal ".." . Files names cannot end in ".." + if (index + 2 == searchPattern.Length + || IsDirectorySeparator(searchPattern[index + 2])) + throw new ArgumentException(SR.Format(SR.Arg_InvalidSearchPattern, searchPattern)); + + searchPattern = searchPattern.Substring(index + 2); + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/PathTooLongException.cs b/external/corefx/src/Common/src/CoreLib/System/IO/PathTooLongException.cs new file mode 100644 index 0000000000..7af2452736 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/PathTooLongException.cs @@ -0,0 +1,38 @@ +// 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.Runtime.Serialization; + +namespace System.IO +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class PathTooLongException : IOException + { + public PathTooLongException() + : base(SR.IO_PathTooLong) + { + HResult = HResults.COR_E_PATHTOOLONG; + } + + public PathTooLongException(string message) + : base(message) + { + HResult = HResults.COR_E_PATHTOOLONG; + } + + public PathTooLongException(string message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_PATHTOOLONG; + } + + protected PathTooLongException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/PinnedBufferMemoryStream.cs b/external/corefx/src/Common/src/CoreLib/System/IO/PinnedBufferMemoryStream.cs new file mode 100644 index 0000000000..dfcc05d066 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/PinnedBufferMemoryStream.cs @@ -0,0 +1,60 @@ +// 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. + +/*============================================================ +** +** +** +** +** +** Purpose: Pins a byte[], exposing it as an unmanaged memory +** stream. Used in ResourceReader for corner cases. +** +** +===========================================================*/ + +using System; +using System.Runtime.InteropServices; +using System.Diagnostics; + +namespace System.IO +{ + internal sealed unsafe class PinnedBufferMemoryStream : UnmanagedMemoryStream + { + private byte[] _array; + private GCHandle _pinningHandle; + + internal PinnedBufferMemoryStream(byte[] array) + { + Debug.Assert(array != null, "Array can't be null"); + + _array = array; + _pinningHandle = GCHandle.Alloc(array, GCHandleType.Pinned); + // Now the byte[] is pinned for the lifetime of this instance. + // But I also need to get a pointer to that block of memory... + int len = array.Length; + fixed (byte* ptr = &MemoryMarshal.GetReference((Span)array)) + Initialize(ptr, len, len, FileAccess.Read); + } + + public override int Read(Span destination) => ReadCore(destination); + + public override void Write(ReadOnlySpan source) => WriteCore(source); + + ~PinnedBufferMemoryStream() + { + Dispose(false); + } + + protected override void Dispose(bool disposing) + { + if (_pinningHandle.IsAllocated) + { + _pinningHandle.Free(); + } + + base.Dispose(disposing); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/SeekOrigin.cs b/external/corefx/src/Common/src/CoreLib/System/IO/SeekOrigin.cs new file mode 100644 index 0000000000..3798a0ce70 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/SeekOrigin.cs @@ -0,0 +1,16 @@ +// 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. + +namespace System.IO +{ + // Provides seek reference points. To seek to the end of a stream, + // call stream.Seek(0, SeekOrigin.End). + public enum SeekOrigin + { + // These constants match Win32's FILE_BEGIN, FILE_CURRENT, and FILE_END + Begin = 0, + Current = 1, + End = 2, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/StreamHelpers.CopyValidation.cs b/external/corefx/src/Common/src/CoreLib/System/IO/StreamHelpers.CopyValidation.cs new file mode 100644 index 0000000000..45bbd816df --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/StreamHelpers.CopyValidation.cs @@ -0,0 +1,46 @@ +// 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. + +namespace System.IO +{ + /// Provides methods to help in the implementation of Stream-derived types. + internal static partial class StreamHelpers + { + /// Validate the arguments to CopyTo, as would Stream.CopyTo. + public static void ValidateCopyToArgs(Stream source, Stream destination, int bufferSize) + { + if (destination == null) + { + throw new ArgumentNullException(nameof(destination)); + } + + if (bufferSize <= 0) + { + throw new ArgumentOutOfRangeException(nameof(bufferSize), bufferSize, SR.ArgumentOutOfRange_NeedPosNum); + } + + bool sourceCanRead = source.CanRead; + if (!sourceCanRead && !source.CanWrite) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); + } + + bool destinationCanWrite = destination.CanWrite; + if (!destinationCanWrite && !destination.CanRead) + { + throw new ObjectDisposedException(nameof(destination), SR.ObjectDisposed_StreamClosed); + } + + if (!sourceCanRead) + { + throw new NotSupportedException(SR.NotSupported_UnreadableStream); + } + + if (!destinationCanWrite) + { + throw new NotSupportedException(SR.NotSupported_UnwritableStream); + } + } + } +} diff --git a/external/corefx/src/System.Runtime.Extensions/src/System/IO/StreamReader.cs b/external/corefx/src/Common/src/CoreLib/System/IO/StreamReader.cs similarity index 91% rename from external/corefx/src/System.Runtime.Extensions/src/System/IO/StreamReader.cs rename to external/corefx/src/Common/src/CoreLib/System/IO/StreamReader.cs index f8b1baaafd..392582b960 100644 --- a/external/corefx/src/System.Runtime.Extensions/src/System/IO/StreamReader.cs +++ b/external/corefx/src/Common/src/CoreLib/System/IO/StreamReader.cs @@ -2,11 +2,12 @@ // 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.Text; -using System.Diagnostics.CodeAnalysis; -using System.Threading.Tasks; using System.Diagnostics; -using System.Diagnostics.Contracts; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; namespace System.IO { @@ -305,7 +306,6 @@ namespace System.IO } } - [Pure] public override int Peek() { if (_stream == null) @@ -361,6 +361,15 @@ namespace System.IO throw new ArgumentException(SR.Argument_InvalidOffLen); } + return ReadSpan(new Span(buffer, index, count)); + } + + public override int Read(Span buffer) => + GetType() == typeof(StreamReader) ? ReadSpan(buffer) : + base.Read(buffer); // Defer to Read(char[], ...) if a derived type may have previously overridden it + + private int ReadSpan(Span buffer) + { if (_stream == null) { throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed); @@ -372,12 +381,13 @@ namespace System.IO // As a perf optimization, if we had exactly one buffer's worth of // data read in, let's try writing directly to the user's buffer. bool readToUserBuffer = false; + int count = buffer.Length; while (count > 0) { int n = _charLen - _charPos; if (n == 0) { - n = ReadBuffer(buffer, index + charsRead, count, out readToUserBuffer); + n = ReadBuffer(buffer.Slice(charsRead), out readToUserBuffer); } if (n == 0) { @@ -389,7 +399,7 @@ namespace System.IO } if (!readToUserBuffer) { - Buffer.BlockCopy(_charBuffer, _charPos * 2, buffer, (index + charsRead) * 2, n * 2); + new Span(_charBuffer, _charPos, n).CopyTo(buffer.Slice(charsRead)); _charPos += n; } @@ -451,6 +461,23 @@ namespace System.IO return base.ReadBlock(buffer, index, count); } + public override int ReadBlock(Span buffer) + { + if (GetType() != typeof(StreamReader)) + { + // Defer to Read(char[], ...) if a derived type may have previously overridden it. + return base.ReadBlock(buffer); + } + + int i, n = 0; + do + { + i = ReadSpan(buffer.Slice(n)); + n += i; + } while (i > 0 && n < buffer.Length); + return n; + } + // Trims n bytes from the front of the buffer. private void CompressBuffer(int n) { @@ -651,7 +678,7 @@ namespace System.IO // buffer's worth of bytes could produce. // This optimization, if run, will break SwitchEncoding, so we must not do // this on the first call to ReadBuffer. - private int ReadBuffer(char[] userBuffer, int userOffset, int desiredChars, out bool readToUserBuffer) + private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) { _charLen = 0; _charPos = 0; @@ -674,7 +701,7 @@ namespace System.IO // buffer optimization. This affects reads where the end of the // Stream comes in the middle somewhere, and when you ask for // fewer chars than your buffer could produce. - readToUserBuffer = desiredChars >= _maxCharsPerBuffer; + readToUserBuffer = userBuffer.Length >= _maxCharsPerBuffer; do { @@ -694,7 +721,7 @@ namespace System.IO { if (readToUserBuffer) { - charsRead = _decoder.GetChars(_byteBuffer, 0, _byteLen, userBuffer, userOffset + charsRead); + charsRead = _decoder.GetChars(new ReadOnlySpan(_byteBuffer, 0, _byteLen), userBuffer.Slice(charsRead), flush: false); _charLen = 0; // StreamReader's buffer is empty. } else @@ -743,13 +770,13 @@ namespace System.IO { DetectEncoding(); // DetectEncoding changes some buffer state. Recompute this. - readToUserBuffer = desiredChars >= _maxCharsPerBuffer; + readToUserBuffer = userBuffer.Length >= _maxCharsPerBuffer; } _charPos = 0; if (readToUserBuffer) { - charsRead += _decoder.GetChars(_byteBuffer, 0, _byteLen, userBuffer, userOffset + charsRead); + charsRead += _decoder.GetChars(new ReadOnlySpan(_byteBuffer, 0, _byteLen), userBuffer.Slice(charsRead), flush:false); _charLen = 0; // StreamReader's buffer is empty. } else @@ -759,7 +786,7 @@ namespace System.IO } } while (charsRead == 0); - _isBlocked &= charsRead < desiredChars; + _isBlocked &= charsRead < userBuffer.Length; //Console.WriteLine("ReadBuffer: charsRead: "+charsRead+" readToUserBuffer: "+readToUserBuffer); return charsRead; @@ -991,13 +1018,36 @@ namespace System.IO CheckAsyncTaskInProgress(); - Task task = ReadAsyncInternal(buffer, index, count); + Task task = ReadAsyncInternal(new Memory(buffer, index, count), default).AsTask(); _asyncReadTask = task; return task; } - internal override async Task ReadAsyncInternal(char[] buffer, int index, int count) + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + if (GetType() != typeof(StreamReader)) + { + // Ensure we use existing overrides if a class already overrode existing overloads. + return base.ReadAsync(buffer, cancellationToken); + } + + if (_stream == null) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed); + } + + CheckAsyncTaskInProgress(); + + if (cancellationToken.IsCancellationRequested) + { + return new ValueTask(Task.FromCanceled(cancellationToken)); + } + + return ReadAsyncInternal(buffer, cancellationToken); + } + + internal override async ValueTask ReadAsyncInternal(Memory buffer, CancellationToken cancellationToken) { if (_charPos == _charLen && (await ReadBufferAsync().ConfigureAwait(false)) == 0) { @@ -1013,6 +1063,7 @@ namespace System.IO Byte[] tmpByteBuffer = _byteBuffer; Stream tmpStream = _stream; + int count = buffer.Length; while (count > 0) { // n is the characters available in _charBuffer @@ -1041,7 +1092,7 @@ namespace System.IO { Debug.Assert(_bytePos <= _encoding.Preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?"); int tmpBytePos = _bytePos; - int len = await tmpStream.ReadAsync(tmpByteBuffer, tmpBytePos, tmpByteBuffer.Length - tmpBytePos).ConfigureAwait(false); + int len = await tmpStream.ReadAsync(tmpByteBuffer, tmpBytePos, tmpByteBuffer.Length - tmpBytePos, cancellationToken).ConfigureAwait(false); Debug.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class."); if (len == 0) @@ -1052,7 +1103,7 @@ namespace System.IO { if (readToUserBuffer) { - n = _decoder.GetChars(tmpByteBuffer, 0, _byteLen, buffer, index + charsRead); + n = _decoder.GetChars(new ReadOnlySpan(tmpByteBuffer, 0, _byteLen), buffer.Span.Slice(charsRead), flush: false); _charLen = 0; // StreamReader's buffer is empty. } else @@ -1077,7 +1128,7 @@ namespace System.IO { Debug.Assert(_bytePos == 0, "_bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?"); - _byteLen = await tmpStream.ReadAsync(tmpByteBuffer, 0, tmpByteBuffer.Length).ConfigureAwait(false); + _byteLen = await tmpStream.ReadAsync(tmpByteBuffer, 0, tmpByteBuffer.Length, cancellationToken).ConfigureAwait(false); Debug.Assert(_byteLen >= 0, "Stream.Read returned a negative number! This is a bug in your stream class."); @@ -1116,7 +1167,7 @@ namespace System.IO _charPos = 0; if (readToUserBuffer) { - n += _decoder.GetChars(tmpByteBuffer, 0, _byteLen, buffer, index + charsRead); + n += _decoder.GetChars(new ReadOnlySpan(tmpByteBuffer, 0, _byteLen), buffer.Span.Slice(charsRead), flush: false); // Why did the bytes yield no chars? Debug.Assert(n > 0); @@ -1148,7 +1199,7 @@ namespace System.IO if (!readToUserBuffer) { - Buffer.BlockCopy(_charBuffer, _charPos * 2, buffer, (index + charsRead) * 2, n * 2); + new Span(_charBuffer, _charPos, n).CopyTo(buffer.Span.Slice(charsRead)); _charPos += n; } @@ -1204,6 +1255,38 @@ namespace System.IO return task; } + public override ValueTask ReadBlockAsync(Memory buffer, CancellationToken cancellationToken = default) + { + if (GetType() != typeof(StreamReader)) + { + // If a derived type may have overridden ReadBlockAsync(char[], ...) before this overload + // was introduced, defer to it. + return base.ReadBlockAsync(buffer, cancellationToken); + } + + if (_stream == null) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed); + } + + CheckAsyncTaskInProgress(); + + if (cancellationToken.IsCancellationRequested) + { + return new ValueTask(Task.FromCanceled(cancellationToken)); + } + + ValueTask vt = ReadBlockAsyncInternal(buffer, cancellationToken); + if (vt.IsCompletedSuccessfully) + { + return vt; + } + + Task t = vt.AsTask(); + _asyncReadTask = t; + return new ValueTask(t); + } + private async Task ReadBufferAsync() { _charLen = 0; diff --git a/external/corefx/src/System.Runtime.Extensions/src/System/IO/StreamWriter.cs b/external/corefx/src/Common/src/CoreLib/System/IO/StreamWriter.cs similarity index 82% rename from external/corefx/src/System.Runtime.Extensions/src/System/IO/StreamWriter.cs rename to external/corefx/src/Common/src/CoreLib/System/IO/StreamWriter.cs index 2f902b2110..d0d014db33 100644 --- a/external/corefx/src/System.Runtime.Extensions/src/System/IO/StreamWriter.cs +++ b/external/corefx/src/Common/src/CoreLib/System/IO/StreamWriter.cs @@ -2,9 +2,11 @@ // 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.Text; -using System.Threading.Tasks; using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; namespace System.IO { @@ -324,59 +326,9 @@ namespace System.IO public override void Write(char[] buffer) { - // This may be faster than the one with the index & count since it - // has to do less argument checking. - if (buffer == null) + if (buffer != null) { - return; - } - - CheckAsyncTaskInProgress(); - - // Threshold of 4 was chosen after running perf tests - if (buffer.Length <= 4) - { - for (int i = 0; i < buffer.Length; i++) - { - if (_charPos == _charLen) - { - Flush(false, false); - } - - Debug.Assert(_charLen - _charPos > 0, "StreamWriter::Write(char[]) isn't making progress! This is most likely a race in user code."); - _charBuffer[_charPos] = buffer[i]; - _charPos++; - } - } - else - { - int count = buffer.Length; - - int index = 0; - while (count > 0) - { - if (_charPos == _charLen) - { - Flush(false, false); - } - - int n = _charLen - _charPos; - if (n > count) - { - n = count; - } - - Debug.Assert(n > 0, "StreamWriter::Write(char[]) isn't making progress! This is most likely a race in user code."); - Buffer.BlockCopy(buffer, index * sizeof(char), _charBuffer, _charPos * sizeof(char), n * sizeof(char)); - _charPos += n; - index += n; - count -= n; - } - } - - if (_autoFlush) - { - Flush(true, false); + WriteCore(buffer, _autoFlush); } } @@ -399,49 +351,79 @@ namespace System.IO throw new ArgumentException(SR.Argument_InvalidOffLen); } + WriteCore(new ReadOnlySpan(buffer, index, count), _autoFlush); + } + + public override void Write(ReadOnlySpan buffer) + { + if (GetType() == typeof(StreamWriter)) + { + WriteCore(buffer, _autoFlush); + } + else + { + // If a derived class may have overridden existing Write behavior, + // we need to make sure we use it. + base.Write(buffer); + } + } + + private unsafe void WriteCore(ReadOnlySpan buffer, bool autoFlush) + { CheckAsyncTaskInProgress(); - // Threshold of 4 was chosen after running perf tests - if (count <= 4) + if (buffer.Length <= 4 && // Threshold of 4 chosen based on perf experimentation + buffer.Length <= _charLen - _charPos) { - while (count > 0) + // For very short buffers and when we don't need to worry about running out of space + // in the char buffer, just copy the chars individually. + for (int i = 0; i < buffer.Length; i++) { - if (_charPos == _charLen) - { - Flush(false, false); - } - - Debug.Assert(_charLen - _charPos > 0, "StreamWriter::Write(char[]) isn't making progress! This is most likely a race in user code."); - _charBuffer[_charPos] = buffer[index]; - _charPos++; - index++; - count--; + _charBuffer[_charPos++] = buffer[i]; } } else { - while (count > 0) + // For larger buffers or when we may run out of room in the internal char buffer, copy in chunks. + // Use unsafe code until https://github.com/dotnet/coreclr/issues/13827 is addressed, as spans are + // resulting in significant overhead (even when the if branch above is taken rather than this + // else) due to temporaries that need to be cleared. Given the use of unsafe code, we also + // make local copies of instance state to protect against potential concurrent misuse. + + char[] charBuffer = _charBuffer; + if (charBuffer == null) { - if (_charPos == _charLen) - { - Flush(false, false); - } + throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed); + } - int n = _charLen - _charPos; - if (n > count) + fixed (char* bufferPtr = &MemoryMarshal.GetReference(buffer)) + fixed (char* dstPtr = &charBuffer[0]) + { + char* srcPtr = bufferPtr; + int count = buffer.Length; + int dstPos = _charPos; // use a local copy of _charPos for safety + while (count > 0) { - n = count; - } + if (dstPos == charBuffer.Length) + { + Flush(false, false); + dstPos = 0; + } - Debug.Assert(n > 0, "StreamWriter::Write(char[], int, int) isn't making progress! This is most likely a race condition in user code."); - Buffer.BlockCopy(buffer, index * sizeof(char), _charBuffer, _charPos * sizeof(char), n * sizeof(char)); - _charPos += n; - index += n; - count -= n; + int n = Math.Min(charBuffer.Length - dstPos, count); + int bytesToCopy = n * sizeof(char); + + Buffer.MemoryCopy(srcPtr, dstPtr + dstPos, bytesToCopy, bytesToCopy); + + _charPos += n; + dstPos += n; + srcPtr += n; + count -= n; + } } } - if (_autoFlush) + if (autoFlush) { Flush(true, false); } @@ -449,57 +431,9 @@ namespace System.IO public override void Write(string value) { - if (value == null) + if (value != null) { - return; - } - - CheckAsyncTaskInProgress(); - - int count = value.Length; - - // Threshold of 4 was chosen after running perf tests - if (count <= 4) - { - for (int i = 0; i < count; i++) - { - if (_charPos == _charLen) - { - Flush(false, false); - } - - Debug.Assert(_charLen - _charPos > 0, "StreamWriter::Write(String) isn't making progress! This is most likely a race condition in user code."); - _charBuffer[_charPos] = value[i]; - _charPos++; - } - } - else - { - int index = 0; - while (count > 0) - { - if (_charPos == _charLen) - { - Flush(false, false); - } - - int n = _charLen - _charPos; - if (n > count) - { - n = count; - } - - Debug.Assert(n > 0, "StreamWriter::Write(String) isn't making progress! This is most likely a race condition in user code."); - value.CopyTo(index, _charBuffer, _charPos, n); - _charPos += n; - index += n; - count -= n; - } - } - - if (_autoFlush) - { - Flush(true, false); + WriteCore(value.AsReadOnlySpan(), _autoFlush); } } @@ -509,50 +443,27 @@ namespace System.IO // public override void WriteLine(string value) { - if (value == null) - { - value = String.Empty; - } - CheckAsyncTaskInProgress(); - - int count = value.Length; - int index = 0; - while (count > 0) + if (value != null) { - if (_charPos == _charLen) - { - Flush(false, false); - } - - int n = _charLen - _charPos; - if (n > count) - { - n = count; - } - - Debug.Assert(n > 0, "StreamWriter::WriteLine(String) isn't making progress! This is most likely a race condition in user code."); - value.CopyTo(index, _charBuffer, _charPos, n); - _charPos += n; - index += n; - count -= n; + WriteCore(value.AsReadOnlySpan(), autoFlush: false); } + WriteCore(new ReadOnlySpan(CoreNewLine), autoFlush: true); + } - char[] coreNewLine = CoreNewLine; - for (int i = 0; i < coreNewLine.Length; i++) // Expect 2 iterations, no point calling BlockCopy + public override void WriteLine(ReadOnlySpan value) + { + if (GetType() == typeof(StreamWriter)) { - if (_charPos == _charLen) - { - Flush(false, false); - } - - _charBuffer[_charPos] = coreNewLine[i]; - _charPos++; + CheckAsyncTaskInProgress(); + WriteCore(value, autoFlush: false); + WriteCore(new ReadOnlySpan(CoreNewLine), autoFlush: true); } - - if (_autoFlush) + else { - Flush(true, false); + // If a derived class may have overridden existing WriteLine behavior, + // we need to make sure we use it. + base.WriteLine(value); } } @@ -752,46 +663,60 @@ namespace System.IO CheckAsyncTaskInProgress(); - Task task = WriteAsyncInternal(this, buffer, index, count, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: false); + Task task = WriteAsyncInternal(this, new ReadOnlyMemory(buffer, index, count), _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: false, cancellationToken: default); _asyncWriteTask = task; return task; } + public override Task WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + if (GetType() != typeof(StreamWriter)) + { + // If a derived type may have overridden existing WriteASync(char[], ...) behavior, make sure we use it. + return base.WriteAsync(buffer, cancellationToken); + } + + if (_stream == null) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed); + } + + CheckAsyncTaskInProgress(); + + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + Task task = WriteAsyncInternal(this, buffer, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: false, cancellationToken: cancellationToken); + _asyncWriteTask = task; + return task; + } + // We pass in private instance fields of this MarshalByRefObject-derived type as local params // to ensure performant access inside the state machine that corresponds this async method. // Fields that are written to must be assigned at the end of the method *and* before instance invocations. - private static async Task WriteAsyncInternal(StreamWriter _this, char[] buffer, int index, int count, + private static async Task WriteAsyncInternal(StreamWriter _this, ReadOnlyMemory source, char[] charBuffer, int charPos, int charLen, char[] coreNewLine, - bool autoFlush, bool appendNewLine) + bool autoFlush, bool appendNewLine, CancellationToken cancellationToken) { - Debug.Assert(count == 0 || (count > 0 && buffer != null)); - Debug.Assert(index >= 0); - Debug.Assert(count >= 0); - Debug.Assert(buffer == null || (buffer != null && buffer.Length - index >= count)); - - while (count > 0) + int copied = 0; + while (copied < source.Length) { if (charPos == charLen) { - await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false); + await _this.FlushAsyncInternal(false, false, charBuffer, charPos, cancellationToken).ConfigureAwait(false); Debug.Assert(_this._charPos == 0); charPos = 0; } - int n = charLen - charPos; - if (n > count) - { - n = count; - } - + int n = Math.Min(charLen - charPos, source.Length - copied); Debug.Assert(n > 0, "StreamWriter::Write(char[], int, int) isn't making progress! This is most likely a race condition in user code."); - Buffer.BlockCopy(buffer, index * sizeof(char), charBuffer, charPos * sizeof(char), n * sizeof(char)); - + source.Span.Slice(copied, n).CopyTo(new Span(charBuffer, charPos, n)); charPos += n; - index += n; - count -= n; + copied += n; } if (appendNewLine) @@ -800,7 +725,7 @@ namespace System.IO { if (charPos == charLen) { - await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false); + await _this.FlushAsyncInternal(false, false, charBuffer, charPos, cancellationToken).ConfigureAwait(false); Debug.Assert(_this._charPos == 0); charPos = 0; } @@ -812,7 +737,7 @@ namespace System.IO if (autoFlush) { - await _this.FlushAsyncInternal(true, false, charBuffer, charPos).ConfigureAwait(false); + await _this.FlushAsyncInternal(true, false, charBuffer, charPos, cancellationToken).ConfigureAwait(false); Debug.Assert(_this._charPos == 0); charPos = 0; } @@ -838,7 +763,7 @@ namespace System.IO CheckAsyncTaskInProgress(); - Task task = WriteAsyncInternal(this, null, 0, 0, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true); + Task task = WriteAsyncInternal(this, ReadOnlyMemory.Empty, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true, cancellationToken: default); _asyncWriteTask = task; return task; @@ -935,7 +860,32 @@ namespace System.IO CheckAsyncTaskInProgress(); - Task task = WriteAsyncInternal(this, buffer, index, count, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true); + Task task = WriteAsyncInternal(this, new ReadOnlyMemory(buffer, index, count), _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true, cancellationToken: default); + _asyncWriteTask = task; + + return task; + } + + public override Task WriteLineAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + if (GetType() != typeof(StreamWriter)) + { + return base.WriteLineAsync(buffer, cancellationToken); + } + + if (_stream == null) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed); + } + + CheckAsyncTaskInProgress(); + + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + Task task = WriteAsyncInternal(this, buffer, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true, cancellationToken: cancellationToken); _asyncWriteTask = task; return task; @@ -981,8 +931,13 @@ namespace System.IO } private Task FlushAsyncInternal(bool flushStream, bool flushEncoder, - char[] sCharBuffer, int sCharPos) + char[] sCharBuffer, int sCharPos, CancellationToken cancellationToken = default) { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + // Perf boost for Flush on non-dirty writers. if (sCharPos == 0 && !flushStream && !flushEncoder) { @@ -990,7 +945,7 @@ namespace System.IO } Task flushTask = FlushAsyncInternal(this, flushStream, flushEncoder, sCharBuffer, sCharPos, _haveWrittenPreamble, - _encoding, _encoder, _byteBuffer, _stream); + _encoding, _encoder, _byteBuffer, _stream, cancellationToken); _charPos = 0; return flushTask; @@ -1001,7 +956,7 @@ namespace System.IO // to ensure performant access inside the state machine that corresponds this async method. private static async Task FlushAsyncInternal(StreamWriter _this, bool flushStream, bool flushEncoder, char[] charBuffer, int charPos, bool haveWrittenPreamble, - Encoding encoding, Encoder encoder, Byte[] byteBuffer, Stream stream) + Encoding encoding, Encoder encoder, Byte[] byteBuffer, Stream stream, CancellationToken cancellationToken) { if (!haveWrittenPreamble) { @@ -1009,14 +964,14 @@ namespace System.IO byte[] preamble = encoding.GetPreamble(); if (preamble.Length > 0) { - await stream.WriteAsync(preamble, 0, preamble.Length).ConfigureAwait(false); + await stream.WriteAsync(preamble, 0, preamble.Length, cancellationToken).ConfigureAwait(false); } } int count = encoder.GetBytes(charBuffer, 0, charPos, byteBuffer, 0, flushEncoder); if (count > 0) { - await stream.WriteAsync(byteBuffer, 0, count).ConfigureAwait(false); + await stream.WriteAsync(byteBuffer, 0, count, cancellationToken).ConfigureAwait(false); } // By definition, calling Flush should flush the stream, but this is @@ -1024,7 +979,7 @@ namespace System.IO // Services guys have some perf tests where flushing needlessly hurts. if (flushStream) { - await stream.FlushAsync().ConfigureAwait(false); + await stream.FlushAsync(cancellationToken).ConfigureAwait(false); } } #endregion diff --git a/external/corefx/src/System.Runtime.Extensions/src/System/IO/TextReader.cs b/external/corefx/src/Common/src/CoreLib/System/IO/TextReader.cs similarity index 79% rename from external/corefx/src/System.Runtime.Extensions/src/System/IO/TextReader.cs rename to external/corefx/src/Common/src/CoreLib/System/IO/TextReader.cs index f3345fce09..321c974739 100644 --- a/external/corefx/src/System.Runtime.Extensions/src/System/IO/TextReader.cs +++ b/external/corefx/src/Common/src/CoreLib/System/IO/TextReader.cs @@ -6,7 +6,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Buffers; @@ -49,7 +48,6 @@ namespace System.IO // // This default method simply returns -1. // - [Pure] public virtual int Peek() { return -1; @@ -89,16 +87,14 @@ namespace System.IO throw new ArgumentException(SR.Argument_InvalidOffLen); } - int n = 0; - do + int n; + for (n = 0; n < count; n++) { int ch = Read(); - if (ch == -1) - { - break; - } - buffer[index + n++] = (char)ch; - } while (n < count); + if (ch == -1) break; + buffer[index + n] = (char)ch; + } + return n; } @@ -106,23 +102,23 @@ namespace System.IO // count characters from this TextReader into the // span of characters Returns the actual number of characters read. // - public virtual int Read(Span destination) + public virtual int Read(Span buffer) { - char[] buffer = ArrayPool.Shared.Rent(destination.Length); + char[] array = ArrayPool.Shared.Rent(buffer.Length); try { - int numRead = Read(buffer, 0, destination.Length); - if ((uint)numRead > destination.Length) + int numRead = Read(array, 0, buffer.Length); + if ((uint)numRead > buffer.Length) { throw new IOException(SR.IO_InvalidReadLength); } - new Span(buffer, 0, numRead).CopyTo(destination); + new Span(array, 0, numRead).CopyTo(buffer); return numRead; } finally { - ArrayPool.Shared.Return(buffer); + ArrayPool.Shared.Return(array); } } @@ -156,23 +152,23 @@ namespace System.IO // Blocking version of read for span of characters. Returns only when count // characters have been read or the end of the file was reached. // - public virtual int ReadBlock(Span destination) + public virtual int ReadBlock(Span buffer) { - char[] buffer = ArrayPool.Shared.Rent(destination.Length); + char[] array = ArrayPool.Shared.Rent(buffer.Length); try { - int numRead = ReadBlock(buffer, 0, destination.Length); - if ((uint)numRead > destination.Length) + int numRead = ReadBlock(array, 0, buffer.Length); + if ((uint)numRead > buffer.Length) { throw new IOException(SR.IO_InvalidReadLength); } - new Span(buffer, 0, numRead).CopyTo(destination); + new Span(array, 0, numRead).CopyTo(buffer); return numRead; } finally { - ArrayPool.Shared.Return(buffer); + ArrayPool.Shared.Return(array); } } @@ -220,12 +216,19 @@ namespace System.IO public async virtual Task ReadToEndAsync() { - char[] chars = new char[4096]; - int len; - StringBuilder sb = new StringBuilder(4096); - while ((len = await ReadAsyncInternal(chars, 0, chars.Length).ConfigureAwait(false)) != 0) + var sb = new StringBuilder(4096); + char[] chars = ArrayPool.Shared.Rent(4096); + try { - sb.Append(chars, 0, len); + int len; + while ((len = await ReadAsyncInternal(chars, default).ConfigureAwait(false)) != 0) + { + sb.Append(chars, 0, len); + } + } + finally + { + ArrayPool.Shared.Return(chars); } return sb.ToString(); } @@ -245,32 +248,27 @@ namespace System.IO throw new ArgumentException(SR.Argument_InvalidOffLen); } - return ReadAsyncInternal(buffer, index, count); + return ReadAsyncInternal(new Memory(buffer, index, count), default).AsTask(); } - public virtual ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default(CancellationToken)) => - new ValueTask(destination.TryGetArray(out ArraySegment array) ? + public virtual ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default(CancellationToken)) => + new ValueTask(buffer.TryGetArray(out ArraySegment array) ? ReadAsync(array.Array, array.Offset, array.Count) : Task.Factory.StartNew(state => { var t = (Tuple>)state; return t.Item1.Read(t.Item2.Span); - }, Tuple.Create(this, destination), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)); + }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)); - internal virtual Task ReadAsyncInternal(char[] buffer, int index, int count) + internal virtual ValueTask ReadAsyncInternal(Memory buffer, CancellationToken cancellationToken) { - Debug.Assert(buffer != null); - Debug.Assert(index >= 0); - Debug.Assert(count >= 0); - Debug.Assert(buffer.Length - index >= count); - - var tuple = new Tuple(this, buffer, index, count); - return Task.Factory.StartNew(state => + var tuple = new Tuple>(this, buffer); + return new ValueTask(Task.Factory.StartNew(state => { - var t = (Tuple)state; - return t.Item1.Read(t.Item2, t.Item3, t.Item4); + var t = (Tuple>)state; + return t.Item1.Read(t.Item2.Span); }, - tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); + tuple, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)); } public virtual Task ReadBlockAsync(char[] buffer, int index, int count) @@ -288,31 +286,26 @@ namespace System.IO throw new ArgumentException(SR.Argument_InvalidOffLen); } - return ReadBlockAsyncInternal(buffer, index, count); + return ReadBlockAsyncInternal(new Memory(buffer, index, count), default).AsTask(); } - public virtual ValueTask ReadBlockAsync(Memory destination, CancellationToken cancellationToken = default(CancellationToken)) => - new ValueTask(destination.TryGetArray(out ArraySegment array) ? + public virtual ValueTask ReadBlockAsync(Memory buffer, CancellationToken cancellationToken = default(CancellationToken)) => + new ValueTask(buffer.TryGetArray(out ArraySegment array) ? ReadBlockAsync(array.Array, array.Offset, array.Count) : Task.Factory.StartNew(state => { var t = (Tuple>)state; return t.Item1.ReadBlock(t.Item2.Span); - }, Tuple.Create(this, destination), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)); + }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)); - private async Task ReadBlockAsyncInternal(char[] buffer, int index, int count) + internal async ValueTask ReadBlockAsyncInternal(Memory buffer, CancellationToken cancellationToken) { - Debug.Assert(buffer != null); - Debug.Assert(index >= 0); - Debug.Assert(count >= 0); - Debug.Assert(buffer.Length - index >= count); - - int i, n = 0; + int n = 0, i; do { - i = await ReadAsyncInternal(buffer, index + n, count - n).ConfigureAwait(false); + i = await ReadAsyncInternal(buffer.Slice(n), cancellationToken).ConfigureAwait(false); n += i; - } while (i > 0 && n < count); + } while (i > 0 && n < buffer.Length); return n; } diff --git a/external/corefx/src/System.Runtime.Extensions/src/System/IO/TextWriter.cs b/external/corefx/src/Common/src/CoreLib/System/IO/TextWriter.cs similarity index 94% rename from external/corefx/src/System.Runtime.Extensions/src/System/IO/TextWriter.cs rename to external/corefx/src/Common/src/CoreLib/System/IO/TextWriter.cs index 6960810565..1445f53bef 100644 --- a/external/corefx/src/System.Runtime.Extensions/src/System/IO/TextWriter.cs +++ b/external/corefx/src/Common/src/CoreLib/System/IO/TextWriter.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Globalization; using System.Threading.Tasks; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Buffers; namespace System.IO @@ -92,15 +93,18 @@ namespace System.IO get; } - // Returns the line terminator string used by this TextWriter. The default line - // terminator string is a carriage return followed by a line feed ("\r\n"). - // - // Sets the line terminator string for this TextWriter. The line terminator - // string is written to the text stream whenever one of the - // WriteLine methods are called. In order for text written by - // the TextWriter to be readable by a TextReader, only one of the following line - // terminator strings should be used: "\r", "\n", or "\r\n". - // + /// + /// Returns the line terminator string used by this TextWriter. The default line + /// terminator string is Environment.NewLine, which is platform specific. + /// On Windows this is a carriage return followed by a line feed ("\r\n"). + /// On OSX and Linux this is a line feed ("\n"). + /// + /// + /// The line terminator string is written to the text stream whenever one of the + /// WriteLine methods are called. In order for text written by + /// the TextWriter to be readable by a TextReader, only one of the following line + /// terminator strings should be used: "\r", "\n", or "\r\n". + /// public virtual string NewLine { get { return CoreNewLineStr; } @@ -165,18 +169,18 @@ namespace System.IO // Writes a span of characters to the text stream. // - public virtual void Write(ReadOnlySpan source) + public virtual void Write(ReadOnlySpan buffer) { - char[] buffer = ArrayPool.Shared.Rent(source.Length); + char[] array = ArrayPool.Shared.Rent(buffer.Length); try { - source.CopyTo(new Span(buffer)); - Write(buffer, 0, source.Length); + buffer.CopyTo(new Span(array)); + Write(array, 0, buffer.Length); } finally { - ArrayPool.Shared.Return(buffer); + ArrayPool.Shared.Return(array); } } @@ -347,18 +351,18 @@ namespace System.IO WriteLine(); } - public virtual void WriteLine(ReadOnlySpan source) + public virtual void WriteLine(ReadOnlySpan buffer) { - char[] buffer = ArrayPool.Shared.Rent(source.Length); + char[] array = ArrayPool.Shared.Rent(buffer.Length); try { - source.CopyTo(new Span(buffer)); - WriteLine(buffer, 0, source.Length); + buffer.CopyTo(new Span(array)); + WriteLine(array, 0, buffer.Length); } finally { - ArrayPool.Shared.Return(buffer); + ArrayPool.Shared.Return(array); } } @@ -545,14 +549,14 @@ namespace System.IO tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); } - public virtual Task WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken = default(CancellationToken)) => - source.DangerousTryGetArray(out ArraySegment array) ? + public virtual Task WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default(CancellationToken)) => + MemoryMarshal.TryGetArray(buffer, out ArraySegment array) ? WriteAsync(array.Array, array.Offset, array.Count) : Task.Factory.StartNew(state => { var t = (Tuple>)state; t.Item1.Write(t.Item2.Span); - }, Tuple.Create(this, source), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); + }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); public virtual Task WriteLineAsync(char value) { @@ -597,14 +601,14 @@ namespace System.IO tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); } - public virtual Task WriteLineAsync(ReadOnlyMemory source, CancellationToken cancellationToken = default(CancellationToken)) => - source.DangerousTryGetArray(out ArraySegment array) ? + public virtual Task WriteLineAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default(CancellationToken)) => + MemoryMarshal.TryGetArray(buffer, out ArraySegment array) ? WriteLineAsync(array.Array, array.Offset, array.Count) : Task.Factory.StartNew(state => { var t = (Tuple>)state; t.Item1.WriteLine(t.Item2.Span); - }, Tuple.Create(this, source), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); + }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); public virtual Task WriteLineAsync() { diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/UnmanagedMemoryAccessor.cs b/external/corefx/src/Common/src/CoreLib/System/IO/UnmanagedMemoryAccessor.cs new file mode 100644 index 0000000000..1247ad6d5d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/UnmanagedMemoryAccessor.cs @@ -0,0 +1,670 @@ +// 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. + +/*============================================================ +** +** +** +** +** Purpose: Provides a fast, AV free, cross-language way of +** accessing unmanaged memory in a random fashion. +** +** +===========================================================*/ + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Internal.Runtime.CompilerServices; + +namespace System.IO +{ + /// Perf notes: ReadXXX, WriteXXX (for basic types) acquire and release the + /// SafeBuffer pointer rather than relying on generic Read(T) from SafeBuffer because + /// this gives better throughput; benchmarks showed about 12-15% better. + public class UnmanagedMemoryAccessor : IDisposable + { + private SafeBuffer _buffer; + private long _offset; + private long _capacity; + private FileAccess _access; + private bool _isOpen; + private bool _canRead; + private bool _canWrite; + + protected UnmanagedMemoryAccessor() + { + _isOpen = false; + } + + public UnmanagedMemoryAccessor(SafeBuffer buffer, long offset, long capacity) + { + Initialize(buffer, offset, capacity, FileAccess.Read); + } + + public UnmanagedMemoryAccessor(SafeBuffer buffer, long offset, long capacity, FileAccess access) + { + Initialize(buffer, offset, capacity, access); + } + + protected void Initialize(SafeBuffer buffer, long offset, long capacity, FileAccess access) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (capacity < 0) + { + throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (buffer.ByteLength < (ulong)(offset + capacity)) + { + throw new ArgumentException(SR.Argument_OffsetAndCapacityOutOfBounds); + } + if (access < FileAccess.Read || access > FileAccess.ReadWrite) + { + throw new ArgumentOutOfRangeException(nameof(access)); + } + + if (_isOpen) + { + throw new InvalidOperationException(SR.InvalidOperation_CalledTwice); + } + + unsafe + { + byte* pointer = null; + + try + { + buffer.AcquirePointer(ref pointer); + if (((byte*)((long)pointer + offset + capacity)) < pointer) + { + throw new ArgumentException(SR.Argument_UnmanagedMemAccessorWrapAround); + } + } + finally + { + if (pointer != null) + { + buffer.ReleasePointer(); + } + } + } + + _offset = offset; + _buffer = buffer; + _capacity = capacity; + _access = access; + _isOpen = true; + _canRead = (_access & FileAccess.Read) != 0; + _canWrite = (_access & FileAccess.Write) != 0; + } + + public long Capacity => _capacity; + + public bool CanRead => _isOpen && _canRead; + + public bool CanWrite => _isOpen && _canWrite; + + protected virtual void Dispose(bool disposing) + { + _isOpen = false; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected bool IsOpen => _isOpen; + + // ************** Read Methods ****************/ + + public bool ReadBoolean(long position) => ReadByte(position) != 0; + + public byte ReadByte(long position) + { + EnsureSafeToRead(position, sizeof(byte)); + + byte result; + unsafe + { + byte* pointer = null; + + try + { + _buffer.AcquirePointer(ref pointer); + result = *((byte*)(pointer + _offset + position)); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + return result; + } + + public char ReadChar(long position) => unchecked((char)ReadInt16(position)); + + public short ReadInt16(long position) + { + EnsureSafeToRead(position, sizeof(short)); + + short result; + unsafe + { + byte* pointer = null; + + try + { + _buffer.AcquirePointer(ref pointer); + result = Unsafe.ReadUnaligned(pointer + _offset + position); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + return result; + } + + public int ReadInt32(long position) + { + EnsureSafeToRead(position, sizeof(int)); + + int result; + unsafe + { + byte* pointer = null; + + try + { + _buffer.AcquirePointer(ref pointer); + result = Unsafe.ReadUnaligned(pointer + _offset + position); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + return result; + } + + public long ReadInt64(long position) + { + EnsureSafeToRead(position, sizeof(long)); + + long result; + unsafe + { + byte* pointer = null; + + try + { + _buffer.AcquirePointer(ref pointer); + result = Unsafe.ReadUnaligned(pointer + _offset + position); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + return result; + } + + public decimal ReadDecimal(long position) + { + const int ScaleMask = 0x00FF0000; + const int SignMask = unchecked((int)0x80000000); + + EnsureSafeToRead(position, sizeof(decimal)); + + int lo, mid, hi, flags; + + unsafe + { + byte* pointer = null; + try + { + _buffer.AcquirePointer(ref pointer); + pointer += (_offset + position); + + lo = Unsafe.ReadUnaligned(pointer); + mid = Unsafe.ReadUnaligned(pointer + 4); + hi = Unsafe.ReadUnaligned(pointer + 8); + flags = Unsafe.ReadUnaligned(pointer + 12); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + + // Check for invalid Decimal values + if (!((flags & ~(SignMask | ScaleMask)) == 0 && (flags & ScaleMask) <= (28 << 16))) + { + throw new ArgumentException(SR.Arg_BadDecimal); // Throw same Exception type as Decimal(int[]) ctor for compat + } + + bool isNegative = (flags & SignMask) != 0; + byte scale = (byte)(flags >> 16); + + return new decimal(lo, mid, hi, isNegative, scale); + } + + public float ReadSingle(long position) => BitConverter.Int32BitsToSingle(ReadInt32(position)); + + public double ReadDouble(long position) => BitConverter.Int64BitsToDouble(ReadInt64(position)); + + [CLSCompliant(false)] + public sbyte ReadSByte(long position) => unchecked((sbyte)ReadByte(position)); + + [CLSCompliant(false)] + public ushort ReadUInt16(long position) => unchecked((ushort)ReadInt16(position)); + + [CLSCompliant(false)] + public uint ReadUInt32(long position) => unchecked((uint)ReadInt32(position)); + + [CLSCompliant(false)] + public ulong ReadUInt64(long position) => unchecked((ulong)ReadInt64(position)); + + // Reads a struct of type T from unmanaged memory, into the reference pointed to by ref value. + // Note: this method is not safe, since it overwrites the contents of a structure, it can be + // used to modify the private members of a struct. + // This method is most performant when used with medium to large sized structs + // (larger than 8 bytes -- though this is number is JIT and architecture dependent). As + // such, it is best to use the ReadXXX methods for small standard types such as ints, longs, + // bools, etc. + public void Read(long position, out T structure) where T : struct + { + if (position < 0) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum); + } + + if (!_isOpen) + { + throw new ObjectDisposedException(nameof(UnmanagedMemoryAccessor), SR.ObjectDisposed_ViewAccessorClosed); + } + if (!_canRead) + { + throw new NotSupportedException(SR.NotSupported_Reading); + } + + uint sizeOfT = SafeBuffer.SizeOf(); + if (position > _capacity - sizeOfT) + { + if (position >= _capacity) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired); + } + else + { + throw new ArgumentException(SR.Format(SR.Argument_NotEnoughBytesToRead, typeof(T)), nameof(position)); + } + } + + structure = _buffer.Read((ulong)(_offset + position)); + } + + // Reads 'count' structs of type T from unmanaged memory, into 'array' starting at 'offset'. + // Note: this method is not safe, since it overwrites the contents of structures, it can + // be used to modify the private members of a struct. + public int ReadArray(long position, T[] array, int offset, int count) where T : struct + { + if (array == null) + { + throw new ArgumentNullException(nameof(array), SR.ArgumentNull_Buffer); + } + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (array.Length - offset < count) + { + throw new ArgumentException(SR.Argument_InvalidOffLen); + } + if (!_isOpen) + { + throw new ObjectDisposedException(nameof(UnmanagedMemoryAccessor), SR.ObjectDisposed_ViewAccessorClosed); + } + if (!_canRead) + { + throw new NotSupportedException(SR.NotSupported_Reading); + } + if (position < 0) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum); + } + + uint sizeOfT = SafeBuffer.AlignedSizeOf(); + + // only check position and ask for fewer Ts if count is too big + if (position >= _capacity) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired); + } + + int n = count; + long spaceLeft = _capacity - position; + if (spaceLeft < 0) + { + n = 0; + } + else + { + ulong spaceNeeded = (ulong)(sizeOfT * count); + if ((ulong)spaceLeft < spaceNeeded) + { + n = (int)(spaceLeft / sizeOfT); + } + } + + _buffer.ReadArray((ulong)(_offset + position), array, offset, n); + + return n; + } + + // ************** Write Methods ****************/ + + public void Write(long position, bool value) => Write(position, (byte)(value ? 1 : 0)); + + public void Write(long position, byte value) + { + EnsureSafeToWrite(position, sizeof(byte)); + + unsafe + { + byte* pointer = null; + + try + { + _buffer.AcquirePointer(ref pointer); + *((byte*)(pointer + _offset + position)) = value; + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + } + + public void Write(long position, char value) => Write(position, unchecked((short)value)); + + public void Write(long position, short value) + { + EnsureSafeToWrite(position, sizeof(short)); + + unsafe + { + byte* pointer = null; + + try + { + _buffer.AcquirePointer(ref pointer); + Unsafe.WriteUnaligned(pointer + _offset + position, value); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + } + + public void Write(long position, int value) + { + EnsureSafeToWrite(position, sizeof(int)); + + unsafe + { + byte* pointer = null; + + try + { + _buffer.AcquirePointer(ref pointer); + Unsafe.WriteUnaligned(pointer + _offset + position, value); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + } + + public void Write(long position, long value) + { + EnsureSafeToWrite(position, sizeof(long)); + + unsafe + { + byte* pointer = null; + + try + { + _buffer.AcquirePointer(ref pointer); + Unsafe.WriteUnaligned(pointer + _offset + position, value); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + } + + public void Write(long position, decimal value) + { + EnsureSafeToWrite(position, sizeof(decimal)); + + unsafe + { + int* valuePtr = (int*)(&value); + int flags = *valuePtr; + int hi = *(valuePtr + 1); + int lo = *(valuePtr + 2); + int mid = *(valuePtr + 3); + + byte* pointer = null; + try + { + _buffer.AcquirePointer(ref pointer); + pointer += (_offset + position); + + Unsafe.WriteUnaligned(pointer, lo); + Unsafe.WriteUnaligned(pointer + 4, mid); + Unsafe.WriteUnaligned(pointer + 8, hi); + Unsafe.WriteUnaligned(pointer + 12, flags); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + } + + public void Write(long position, float value) => Write(position, BitConverter.SingleToInt32Bits(value)); + + public void Write(long position, double value) => Write(position, BitConverter.DoubleToInt64Bits(value)); + + [CLSCompliant(false)] + public void Write(long position, sbyte value) => Write(position, unchecked((byte)value)); + + [CLSCompliant(false)] + public void Write(long position, ushort value) => Write(position, unchecked((short)value)); + + [CLSCompliant(false)] + public void Write(long position, uint value) => Write(position, unchecked((int)value)); + + [CLSCompliant(false)] + public void Write(long position, ulong value) => Write(position, unchecked((long)value)); + + // Writes the struct pointed to by ref value into unmanaged memory. Note that this method + // is most performant when used with medium to large sized structs (larger than 8 bytes + // though this is number is JIT and architecture dependent). As such, it is best to use + // the WriteX methods for small standard types such as ints, longs, bools, etc. + public void Write(long position, ref T structure) where T : struct + { + if (position < 0) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (!_isOpen) + { + throw new ObjectDisposedException(nameof(UnmanagedMemoryAccessor), SR.ObjectDisposed_ViewAccessorClosed); + } + if (!_canWrite) + { + throw new NotSupportedException(SR.NotSupported_Writing); + } + + uint sizeOfT = SafeBuffer.SizeOf(); + if (position > _capacity - sizeOfT) + { + if (position >= _capacity) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired); + } + else + { + throw new ArgumentException(SR.Format(SR.Argument_NotEnoughBytesToWrite, typeof(T)), nameof(position)); + } + } + + _buffer.Write((ulong)(_offset + position), structure); + } + + // Writes 'count' structs of type T from 'array' (starting at 'offset') into unmanaged memory. + public void WriteArray(long position, T[] array, int offset, int count) where T : struct + { + if (array == null) + { + throw new ArgumentNullException(nameof(array), SR.ArgumentNull_Buffer); + } + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (array.Length - offset < count) + { + throw new ArgumentException(SR.Argument_InvalidOffLen); + } + if (position < 0) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (position >= Capacity) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired); + } + + if (!_isOpen) + { + throw new ObjectDisposedException(nameof(UnmanagedMemoryAccessor), SR.ObjectDisposed_ViewAccessorClosed); + } + if (!_canWrite) + { + throw new NotSupportedException(SR.NotSupported_Writing); + } + + _buffer.WriteArray((ulong)(_offset + position), array, offset, count); + } + + private void EnsureSafeToRead(long position, int sizeOfType) + { + if (!_isOpen) + { + throw new ObjectDisposedException(nameof(UnmanagedMemoryAccessor), SR.ObjectDisposed_ViewAccessorClosed); + } + if (!_canRead) + { + throw new NotSupportedException(SR.NotSupported_Reading); + } + if (position < 0) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (position > _capacity - sizeOfType) + { + if (position >= _capacity) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired); + } + else + { + throw new ArgumentException(SR.Argument_NotEnoughBytesToRead, nameof(position)); + } + } + } + + private void EnsureSafeToWrite(long position, int sizeOfType) + { + if (!_isOpen) + { + throw new ObjectDisposedException(nameof(UnmanagedMemoryAccessor), SR.ObjectDisposed_ViewAccessorClosed); + } + if (!_canWrite) + { + throw new NotSupportedException(SR.NotSupported_Writing); + } + if (position < 0) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (position > _capacity - sizeOfType) + { + if (position >= _capacity) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired); + } + else + { + throw new ArgumentException(SR.Argument_NotEnoughBytesToWrite, nameof(position)); + } + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/UnmanagedMemoryStream.cs b/external/corefx/src/Common/src/CoreLib/System/IO/UnmanagedMemoryStream.cs new file mode 100644 index 0000000000..171113542f --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/UnmanagedMemoryStream.cs @@ -0,0 +1,882 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace System.IO +{ + /* + * This class is used to access a contiguous block of memory, likely outside + * the GC heap (or pinned in place in the GC heap, but a MemoryStream may + * make more sense in those cases). It's great if you have a pointer and + * a length for a section of memory mapped in by someone else and you don't + * want to copy this into the GC heap. UnmanagedMemoryStream assumes these + * two things: + * + * 1) All the memory in the specified block is readable or writable, + * depending on the values you pass to the constructor. + * 2) The lifetime of the block of memory is at least as long as the lifetime + * of the UnmanagedMemoryStream. + * 3) You clean up the memory when appropriate. The UnmanagedMemoryStream + * currently will do NOTHING to free this memory. + * 4) All calls to Write and WriteByte may not be threadsafe currently. + * + * It may become necessary to add in some sort of + * DeallocationMode enum, specifying whether we unmap a section of memory, + * call free, run a user-provided delegate to free the memory, etc. + * We'll suggest user write a subclass of UnmanagedMemoryStream that uses + * a SafeHandle subclass to hold onto the memory. + * + */ + + /// + /// Stream over a memory pointer or over a SafeBuffer + /// + public class UnmanagedMemoryStream : Stream + { + private SafeBuffer _buffer; + private unsafe byte* _mem; + private long _length; + private long _capacity; + private long _position; + private long _offset; + private FileAccess _access; + private bool _isOpen; + private Task _lastReadTask; // The last successful task returned from ReadAsync + + /// + /// Creates a closed stream. + /// + // Needed for subclasses that need to map a file, etc. + protected UnmanagedMemoryStream() + { + unsafe + { + _mem = null; + } + _isOpen = false; + } + + /// + /// Creates a stream over a SafeBuffer. + /// + /// + /// + /// + public UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length) + { + Initialize(buffer, offset, length, FileAccess.Read); + } + + /// + /// Creates a stream over a SafeBuffer. + /// + public UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length, FileAccess access) + { + Initialize(buffer, offset, length, access); + } + + /// + /// Subclasses must call this method (or the other overload) to properly initialize all instance fields. + /// + /// + /// + /// + /// + protected void Initialize(SafeBuffer buffer, long offset, long length, FileAccess access) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (length < 0) + { + throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (buffer.ByteLength < (ulong)(offset + length)) + { + throw new ArgumentException(SR.Argument_InvalidSafeBufferOffLen); + } + if (access < FileAccess.Read || access > FileAccess.ReadWrite) + { + throw new ArgumentOutOfRangeException(nameof(access)); + } + + if (_isOpen) + { + throw new InvalidOperationException(SR.InvalidOperation_CalledTwice); + } + + // check for wraparound + unsafe + { + byte* pointer = null; + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + buffer.AcquirePointer(ref pointer); + if ((pointer + offset + length) < pointer) + { + throw new ArgumentException(SR.ArgumentOutOfRange_UnmanagedMemStreamWrapAround); + } + } + finally + { + if (pointer != null) + { + buffer.ReleasePointer(); + } + } + } + + _offset = offset; + _buffer = buffer; + _length = length; + _capacity = length; + _access = access; + _isOpen = true; + } + + /// + /// Creates a stream over a byte*. + /// + [CLSCompliant(false)] + public unsafe UnmanagedMemoryStream(byte* pointer, long length) + { + Initialize(pointer, length, length, FileAccess.Read); + } + + /// + /// Creates a stream over a byte*. + /// + [CLSCompliant(false)] + public unsafe UnmanagedMemoryStream(byte* pointer, long length, long capacity, FileAccess access) + { + Initialize(pointer, length, capacity, access); + } + + /// + /// Subclasses must call this method (or the other overload) to properly initialize all instance fields. + /// + [CLSCompliant(false)] + protected unsafe void Initialize(byte* pointer, long length, long capacity, FileAccess access) + { + if (pointer == null) + throw new ArgumentNullException(nameof(pointer)); + if (length < 0 || capacity < 0) + throw new ArgumentOutOfRangeException((length < 0) ? nameof(length) : nameof(capacity), SR.ArgumentOutOfRange_NeedNonNegNum); + if (length > capacity) + throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_LengthGreaterThanCapacity); + // Check for wraparound. + if (((byte*)((long)pointer + capacity)) < pointer) + throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_UnmanagedMemStreamWrapAround); + if (access < FileAccess.Read || access > FileAccess.ReadWrite) + throw new ArgumentOutOfRangeException(nameof(access), SR.ArgumentOutOfRange_Enum); + if (_isOpen) + throw new InvalidOperationException(SR.InvalidOperation_CalledTwice); + + _mem = pointer; + _offset = 0; + _length = length; + _capacity = capacity; + _access = access; + _isOpen = true; + } + + /// + /// Returns true if the stream can be read; otherwise returns false. + /// + public override bool CanRead + { + get { return _isOpen && (_access & FileAccess.Read) != 0; } + } + + /// + /// Returns true if the stream can seek; otherwise returns false. + /// + public override bool CanSeek + { + get { return _isOpen; } + } + + /// + /// Returns true if the stream can be written to; otherwise returns false. + /// + public override bool CanWrite + { + get { return _isOpen && (_access & FileAccess.Write) != 0; } + } + + /// + /// Closes the stream. The stream's memory needs to be dealt with separately. + /// + /// + protected override void Dispose(bool disposing) + { + _isOpen = false; + unsafe { _mem = null; } + + // Stream allocates WaitHandles for async calls. So for correctness + // call base.Dispose(disposing) for better perf, avoiding waiting + // for the finalizers to run on those types. + base.Dispose(disposing); + } + + private void EnsureNotClosed() + { + if (!_isOpen) + throw Error.GetStreamIsClosed(); + } + + private void EnsureReadable() + { + if (!CanRead) + throw Error.GetReadNotSupported(); + } + + private void EnsureWriteable() + { + if (!CanWrite) + throw Error.GetWriteNotSupported(); + } + + /// + /// Since it's a memory stream, this method does nothing. + /// + public override void Flush() + { + EnsureNotClosed(); + } + + /// + /// Since it's a memory stream, this method does nothing specific. + /// + /// + /// + public override Task FlushAsync(CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + + try + { + Flush(); + return Task.CompletedTask; + } + catch (Exception ex) + { + return Task.FromException(ex); + } + } + + /// + /// Number of bytes in the stream. + /// + public override long Length + { + get + { + EnsureNotClosed(); + return Interlocked.Read(ref _length); + } + } + + /// + /// Number of bytes that can be written to the stream. + /// + public long Capacity + { + get + { + EnsureNotClosed(); + return _capacity; + } + } + + /// + /// ReadByte will read byte at the Position in the stream + /// + public override long Position + { + get + { + if (!CanSeek) throw Error.GetStreamIsClosed(); + return Interlocked.Read(ref _position); + } + set + { + if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); + if (!CanSeek) throw Error.GetStreamIsClosed(); + + Interlocked.Exchange(ref _position, value); + } + } + + /// + /// Pointer to memory at the current Position in the stream. + /// + [CLSCompliant(false)] + public unsafe byte* PositionPointer + { + get + { + if (_buffer != null) + throw new NotSupportedException(SR.NotSupported_UmsSafeBuffer); + + EnsureNotClosed(); + + // Use a temp to avoid a race + long pos = Interlocked.Read(ref _position); + if (pos > _capacity) + throw new IndexOutOfRangeException(SR.IndexOutOfRange_UMSPosition); + byte* ptr = _mem + pos; + return ptr; + } + set + { + if (_buffer != null) + throw new NotSupportedException(SR.NotSupported_UmsSafeBuffer); + + EnsureNotClosed(); + + if (value < _mem) + throw new IOException(SR.IO_SeekBeforeBegin); + long newPosition = (long)value - (long)_mem; + if (newPosition < 0) + throw new ArgumentOutOfRangeException("offset", SR.ArgumentOutOfRange_UnmanagedMemStreamLength); + + Interlocked.Exchange(ref _position, newPosition); + } + } + + /// + /// Reads bytes from stream and puts them into the buffer + /// + /// Buffer to read the bytes to. + /// Starting index in the buffer. + /// Maximum number of bytes to read. + /// Number of bytes actually read. + public override int Read(byte[] buffer, int offset, int count) + { + if (buffer == null) + throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + if (buffer.Length - offset < count) + throw new ArgumentException(SR.Argument_InvalidOffLen); + + return ReadCore(new Span(buffer, offset, count)); + } + + public override int Read(Span destination) + { + if (GetType() == typeof(UnmanagedMemoryStream)) + { + return ReadCore(destination); + } + else + { + // UnmanagedMemoryStream is not sealed, and a derived type may have overridden Read(byte[], int, int) prior + // to this Read(Span) overload being introduced. In that case, this Read(Span) overload + // should use the behavior of Read(byte[],int,int) overload. + return base.Read(destination); + } + } + + internal int ReadCore(Span destination) + { + EnsureNotClosed(); + EnsureReadable(); + + // Use a local variable to avoid a race where another thread + // changes our position after we decide we can read some bytes. + long pos = Interlocked.Read(ref _position); + long len = Interlocked.Read(ref _length); + long n = Math.Min(len - pos, destination.Length); + if (n <= 0) + { + return 0; + } + + int nInt = (int)n; // Safe because n <= count, which is an Int32 + if (nInt < 0) + { + return 0; // _position could be beyond EOF + } + Debug.Assert(pos + nInt >= 0, "_position + n >= 0"); // len is less than 2^63 -1. + + unsafe + { + fixed (byte* pBuffer = &MemoryMarshal.GetReference(destination)) + { + if (_buffer != null) + { + byte* pointer = null; + + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + _buffer.AcquirePointer(ref pointer); + Buffer.Memcpy(pBuffer, pointer + pos + _offset, nInt); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + else + { + Buffer.Memcpy(pBuffer, _mem + pos, nInt); + } + } + } + Interlocked.Exchange(ref _position, pos + n); + return nInt; + } + + /// + /// Reads bytes from stream and puts them into the buffer + /// + /// Buffer to read the bytes to. + /// Starting index in the buffer. + /// Maximum number of bytes to read. + /// Token that can be used to cancel this operation. + /// Task that can be used to access the number of bytes actually read. + public override Task ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken) + { + if (buffer == null) + throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + if (buffer.Length - offset < count) + throw new ArgumentException(SR.Argument_InvalidOffLen); + + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + + try + { + Int32 n = Read(buffer, offset, count); + Task t = _lastReadTask; + return (t != null && t.Result == n) ? t : (_lastReadTask = Task.FromResult(n)); + } + catch (Exception ex) + { + Debug.Assert(!(ex is OperationCanceledException)); + return Task.FromException(ex); + } + } + + /// + /// Reads bytes from stream and puts them into the buffer + /// + /// Buffer to read the bytes to. + /// Token that can be used to cancel this operation. + public override ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default(CancellationToken)) + { + if (cancellationToken.IsCancellationRequested) + { + return new ValueTask(Task.FromCanceled(cancellationToken)); + } + + try + { + // ReadAsync(Memory,...) needs to delegate to an existing virtual to do the work, in case an existing derived type + // has changed or augmented the logic associated with reads. If the Memory wraps an array, we could delegate to + // ReadAsync(byte[], ...), but that would defeat part of the purpose, as ReadAsync(byte[], ...) often needs to allocate + // a Task for the return value, so we want to delegate to one of the synchronous methods. We could always + // delegate to the Read(Span) method, and that's the most efficient solution when dealing with a concrete + // UnmanagedMemoryStream, but if we're dealing with a type derived from UnmanagedMemoryStream, Read(Span) will end up delegating + // to Read(byte[], ...), which requires it to get a byte[] from ArrayPool and copy the data. So, we special-case the + // very common case of the Memory wrapping an array: if it does, we delegate to Read(byte[], ...) with it, + // as that will be efficient in both cases, and we fall back to Read(Span) if the Memory wrapped something + // else; if this is a concrete UnmanagedMemoryStream, that'll be efficient, and only in the case where the Memory wrapped + // something other than an array and this is an UnmanagedMemoryStream-derived type that doesn't override Read(Span) will + // it then fall back to doing the ArrayPool/copy behavior. + return new ValueTask( + destination.TryGetArray(out ArraySegment destinationArray) ? + Read(destinationArray.Array, destinationArray.Offset, destinationArray.Count) : + Read(destination.Span)); + } + catch (Exception ex) + { + return new ValueTask(Task.FromException(ex)); + } + } + + /// + /// Returns the byte at the stream current Position and advances the Position. + /// + /// + public override int ReadByte() + { + EnsureNotClosed(); + EnsureReadable(); + + long pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition + long len = Interlocked.Read(ref _length); + if (pos >= len) + return -1; + Interlocked.Exchange(ref _position, pos + 1); + int result; + if (_buffer != null) + { + unsafe + { + byte* pointer = null; + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + _buffer.AcquirePointer(ref pointer); + result = *(pointer + pos + _offset); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + } + else + { + unsafe + { + result = _mem[pos]; + } + } + return result; + } + + /// + /// Advanced the Position to specific location in the stream. + /// + /// Offset from the loc parameter. + /// Origin for the offset parameter. + /// + public override long Seek(long offset, SeekOrigin loc) + { + EnsureNotClosed(); + + switch (loc) + { + case SeekOrigin.Begin: + if (offset < 0) + throw new IOException(SR.IO_SeekBeforeBegin); + Interlocked.Exchange(ref _position, offset); + break; + + case SeekOrigin.Current: + long pos = Interlocked.Read(ref _position); + if (offset + pos < 0) + throw new IOException(SR.IO_SeekBeforeBegin); + Interlocked.Exchange(ref _position, offset + pos); + break; + + case SeekOrigin.End: + long len = Interlocked.Read(ref _length); + if (len + offset < 0) + throw new IOException(SR.IO_SeekBeforeBegin); + Interlocked.Exchange(ref _position, len + offset); + break; + + default: + throw new ArgumentException(SR.Argument_InvalidSeekOrigin); + } + + long finalPos = Interlocked.Read(ref _position); + Debug.Assert(finalPos >= 0, "_position >= 0"); + return finalPos; + } + + /// + /// Sets the Length of the stream. + /// + /// + public override void SetLength(long value) + { + if (value < 0) + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); + if (_buffer != null) + throw new NotSupportedException(SR.NotSupported_UmsSafeBuffer); + + EnsureNotClosed(); + EnsureWriteable(); + + if (value > _capacity) + throw new IOException(SR.IO_FixedCapacity); + + long pos = Interlocked.Read(ref _position); + long len = Interlocked.Read(ref _length); + if (value > len) + { + unsafe + { + Buffer.ZeroMemory(_mem + len, value - len); + } + } + Interlocked.Exchange(ref _length, value); + if (pos > value) + { + Interlocked.Exchange(ref _position, value); + } + } + + /// + /// Writes buffer into the stream + /// + /// Buffer that will be written. + /// Starting index in the buffer. + /// Number of bytes to write. + public override void Write(byte[] buffer, int offset, int count) + { + if (buffer == null) + throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + if (buffer.Length - offset < count) + throw new ArgumentException(SR.Argument_InvalidOffLen); + + WriteCore(new Span(buffer, offset, count)); + } + + public override void Write(ReadOnlySpan source) + { + if (GetType() == typeof(UnmanagedMemoryStream)) + { + WriteCore(source); + } + else + { + // UnmanagedMemoryStream is not sealed, and a derived type may have overridden Write(byte[], int, int) prior + // to this Write(Span) overload being introduced. In that case, this Write(Span) overload + // should use the behavior of Write(byte[],int,int) overload. + base.Write(source); + } + } + + internal unsafe void WriteCore(ReadOnlySpan source) + { + EnsureNotClosed(); + EnsureWriteable(); + + long pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition + long len = Interlocked.Read(ref _length); + long n = pos + source.Length; + // Check for overflow + if (n < 0) + { + throw new IOException(SR.IO_StreamTooLong); + } + + if (n > _capacity) + { + throw new NotSupportedException(SR.IO_FixedCapacity); + } + + if (_buffer == null) + { + // Check to see whether we are now expanding the stream and must + // zero any memory in the middle. + if (pos > len) + { + Buffer.ZeroMemory(_mem + len, pos - len); + } + + // set length after zeroing memory to avoid race condition of accessing unzeroed memory + if (n > len) + { + Interlocked.Exchange(ref _length, n); + } + } + + fixed (byte* pBuffer = &MemoryMarshal.GetReference(source)) + { + if (_buffer != null) + { + long bytesLeft = _capacity - pos; + if (bytesLeft < source.Length) + { + throw new ArgumentException(SR.Arg_BufferTooSmall); + } + + byte* pointer = null; + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + _buffer.AcquirePointer(ref pointer); + Buffer.Memcpy(pointer + pos + _offset, pBuffer, source.Length); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + else + { + Buffer.Memcpy(_mem + pos, pBuffer, source.Length); + } + } + + Interlocked.Exchange(ref _position, n); + return; + } + + /// + /// Writes buffer into the stream. The operation completes synchronously. + /// + /// Buffer that will be written. + /// Starting index in the buffer. + /// Number of bytes to write. + /// Token that can be used to cancel the operation. + /// Task that can be awaited + public override Task WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken) + { + if (buffer == null) + throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + if (buffer.Length - offset < count) + throw new ArgumentException(SR.Argument_InvalidOffLen); + + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + + try + { + Write(buffer, offset, count); + return Task.CompletedTask; + } + catch (Exception ex) + { + Debug.Assert(!(ex is OperationCanceledException)); + return Task.FromException(ex); + } + } + + /// + /// Writes buffer into the stream. The operation completes synchronously. + /// + /// Buffer that will be written. + /// Token that can be used to cancel the operation. + public override Task WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken = default(CancellationToken)) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + try + { + // See corresponding comment in ReadAsync for why we don't just always use Write(ReadOnlySpan). + // Unlike ReadAsync, we could delegate to WriteAsync(byte[], ...) here, but we don't for consistency. + if (MemoryMarshal.TryGetArray(source, out ArraySegment sourceArray)) + { + Write(sourceArray.Array, sourceArray.Offset, sourceArray.Count); + } + else + { + Write(source.Span); + } + return Task.CompletedTask; + } + catch (Exception ex) + { + return Task.FromException(ex); + } + } + + /// + /// Writes a byte to the stream and advances the current Position. + /// + /// + public override void WriteByte(byte value) + { + EnsureNotClosed(); + EnsureWriteable(); + + long pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition + long len = Interlocked.Read(ref _length); + long n = pos + 1; + if (pos >= len) + { + // Check for overflow + if (n < 0) + throw new IOException(SR.IO_StreamTooLong); + + if (n > _capacity) + throw new NotSupportedException(SR.IO_FixedCapacity); + + // Check to see whether we are now expanding the stream and must + // zero any memory in the middle. + // don't do if created from SafeBuffer + if (_buffer == null) + { + if (pos > len) + { + unsafe + { + Buffer.ZeroMemory(_mem + len, pos - len); + } + } + + // set length after zeroing memory to avoid race condition of accessing unzeroed memory + Interlocked.Exchange(ref _length, n); + } + } + + if (_buffer != null) + { + unsafe + { + byte* pointer = null; + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + _buffer.AcquirePointer(ref pointer); + *(pointer + pos + _offset) = value; + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + } + else + { + unsafe + { + _mem[pos] = value; + } + } + Interlocked.Exchange(ref _position, n); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/UnmanagedMemoryStreamWrapper.cs b/external/corefx/src/Common/src/CoreLib/System/IO/UnmanagedMemoryStreamWrapper.cs new file mode 100644 index 0000000000..90bb21ac5b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/UnmanagedMemoryStreamWrapper.cs @@ -0,0 +1,226 @@ +// 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. + +/*============================================================ +** +** +** +** +** Purpose: Create a Memorystream over an UnmanagedMemoryStream +** +===========================================================*/ + +using System; +using System.Runtime.InteropServices; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; + +namespace System.IO +{ + // Needed for backwards compatibility with V1.x usages of the + // ResourceManager, where a MemoryStream is now returned as an + // UnmanagedMemoryStream from ResourceReader. + internal sealed class UnmanagedMemoryStreamWrapper : MemoryStream + { + private UnmanagedMemoryStream _unmanagedStream; + + internal UnmanagedMemoryStreamWrapper(UnmanagedMemoryStream stream) + { + _unmanagedStream = stream; + } + + public override bool CanRead + { + get { return _unmanagedStream.CanRead; } + } + + public override bool CanSeek + { + get { return _unmanagedStream.CanSeek; } + } + + public override bool CanWrite + { + get { return _unmanagedStream.CanWrite; } + } + + protected override void Dispose(bool disposing) + { + try + { + if (disposing) + _unmanagedStream.Dispose(); + } + finally + { + base.Dispose(disposing); + } + } + + public override void Flush() + { + _unmanagedStream.Flush(); + } + + public override byte[] GetBuffer() + { + throw new UnauthorizedAccessException(SR.UnauthorizedAccess_MemStreamBuffer); + } + + public override bool TryGetBuffer(out ArraySegment buffer) + { + buffer = default(ArraySegment); + return false; + } + + public override int Capacity + { + get + { + return (int)_unmanagedStream.Capacity; + } + set + { + throw new IOException(SR.IO_FixedCapacity); + } + } + + public override long Length + { + get + { + return _unmanagedStream.Length; + } + } + + public override long Position + { + get + { + return _unmanagedStream.Position; + } + set + { + _unmanagedStream.Position = value; + } + } + + public override int Read(byte[] buffer, int offset, int count) + { + return _unmanagedStream.Read(buffer, offset, count); + } + + public override int Read(Span destination) + { + return _unmanagedStream.Read(destination); + } + + public override int ReadByte() + { + return _unmanagedStream.ReadByte(); + } + + public override long Seek(long offset, SeekOrigin loc) + { + return _unmanagedStream.Seek(offset, loc); + } + + public unsafe override byte[] ToArray() + { + byte[] buffer = new byte[_unmanagedStream.Length]; + _unmanagedStream.Read(buffer, 0, (int)_unmanagedStream.Length); + return buffer; + } + + public override void Write(byte[] buffer, int offset, int count) + { + _unmanagedStream.Write(buffer, offset, count); + } + + public override void Write(ReadOnlySpan source) + { + _unmanagedStream.Write(source); + } + + public override void WriteByte(byte value) + { + _unmanagedStream.WriteByte(value); + } + + // Writes this MemoryStream to another stream. + public unsafe override void WriteTo(Stream stream) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream), SR.ArgumentNull_Stream); + + byte[] buffer = ToArray(); + + stream.Write(buffer, 0, buffer.Length); + } + + public override void SetLength(Int64 value) + { + // This was probably meant to call _unmanagedStream.SetLength(value), but it was forgotten in V.4.0. + // Now this results in a call to the base which touches the underlying array which is never actually used. + // We cannot fix it due to compat now, but we should fix this at the next SxS release oportunity. + base.SetLength(value); + } + + + public override Task CopyToAsync(Stream destination, Int32 bufferSize, CancellationToken cancellationToken) + { + // The parameter checks must be in sync with the base version: + if (destination == null) + throw new ArgumentNullException(nameof(destination)); + + if (bufferSize <= 0) + throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum); + + if (!CanRead && !CanWrite) + throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); + + if (!destination.CanRead && !destination.CanWrite) + throw new ObjectDisposedException(nameof(destination), SR.ObjectDisposed_StreamClosed); + + if (!CanRead) + throw new NotSupportedException(SR.NotSupported_UnreadableStream); + + if (!destination.CanWrite) + throw new NotSupportedException(SR.NotSupported_UnwritableStream); + + + return _unmanagedStream.CopyToAsync(destination, bufferSize, cancellationToken); + } + + + public override Task FlushAsync(CancellationToken cancellationToken) + { + return _unmanagedStream.FlushAsync(cancellationToken); + } + + + public override Task ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken) + { + return _unmanagedStream.ReadAsync(buffer, offset, count, cancellationToken); + } + + public override ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default(CancellationToken)) + { + return _unmanagedStream.ReadAsync(destination, cancellationToken); + } + + + public override Task WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken) + { + return _unmanagedStream.WriteAsync(buffer, offset, count, cancellationToken); + } + + public override Task WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken = default(CancellationToken)) + { + return _unmanagedStream.WriteAsync(source, cancellationToken); + } + } // class UnmanagedMemoryStreamWrapper +} // namespace + diff --git a/external/corefx/src/Common/src/CoreLib/System/IO/Win32Marshal.cs b/external/corefx/src/Common/src/CoreLib/System/IO/Win32Marshal.cs new file mode 100644 index 0000000000..14a064a700 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IO/Win32Marshal.cs @@ -0,0 +1,107 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.IO +{ + /// + /// Provides static methods for converting from Win32 errors codes to exceptions, HRESULTS and error messages. + /// + internal static class Win32Marshal + { + /// + /// Converts, resetting it, the last Win32 error into a corresponding object. + /// + internal static Exception GetExceptionForLastWin32Error() + { + int errorCode = Marshal.GetLastWin32Error(); + return GetExceptionForWin32Error(errorCode, string.Empty); + } + + /// + /// Converts the specified Win32 error into a corresponding object. + /// + internal static Exception GetExceptionForWin32Error(int errorCode) + { + return GetExceptionForWin32Error(errorCode, string.Empty); + } + + /// + /// Converts the specified Win32 error into a corresponding object, optionally + /// including the specified path in the error message. + /// + internal static Exception GetExceptionForWin32Error(int errorCode, string path) + { + switch (errorCode) + { + case Interop.Errors.ERROR_FILE_NOT_FOUND: + if (path.Length == 0) + return new FileNotFoundException(SR.IO_FileNotFound); + else + return new FileNotFoundException(SR.Format(SR.IO_FileNotFound_FileName, path), path); + + case Interop.Errors.ERROR_PATH_NOT_FOUND: + if (path.Length == 0) + return new DirectoryNotFoundException(SR.IO_PathNotFound_NoPathName); + else + return new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, path)); + + case Interop.Errors.ERROR_ACCESS_DENIED: + if (path.Length == 0) + return new UnauthorizedAccessException(SR.UnauthorizedAccess_IODenied_NoPathName); + else + return new UnauthorizedAccessException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, path)); + + case Interop.Errors.ERROR_ALREADY_EXISTS: + if (path.Length == 0) + goto default; + + return new IOException(SR.Format(SR.IO_AlreadyExists_Name, path), MakeHRFromErrorCode(errorCode)); + + case Interop.Errors.ERROR_FILENAME_EXCED_RANGE: + if (path.Length == 0) + return new PathTooLongException(SR.IO_PathTooLong); + else + return new PathTooLongException(SR.Format(SR.IO_PathTooLong_Path, path)); + + case Interop.Errors.ERROR_INVALID_DRIVE: + throw new DriveNotFoundException(SR.Format(SR.IO_DriveNotFound_Drive, path)); + + case Interop.Errors.ERROR_INVALID_PARAMETER: + return new IOException(Interop.Kernel32.GetMessage(errorCode), MakeHRFromErrorCode(errorCode)); + + case Interop.Errors.ERROR_SHARING_VIOLATION: + if (path.Length == 0) + return new IOException(SR.IO_SharingViolation_NoFileName, MakeHRFromErrorCode(errorCode)); + else + return new IOException(SR.Format(SR.IO_SharingViolation_File, path), MakeHRFromErrorCode(errorCode)); + + case Interop.Errors.ERROR_FILE_EXISTS: + if (path.Length == 0) + goto default; + + return new IOException(SR.Format(SR.IO_FileExists_Name, path), MakeHRFromErrorCode(errorCode)); + + case Interop.Errors.ERROR_OPERATION_ABORTED: + return new OperationCanceledException(); + + default: + return new IOException(Interop.Kernel32.GetMessage(errorCode), MakeHRFromErrorCode(errorCode)); + } + } + + /// + /// Returns a HRESULT for the specified Win32 error code. + /// + internal static int MakeHRFromErrorCode(int errorCode) + { + Debug.Assert((0xFFFF0000 & errorCode) == 0, "This is an HRESULT, not an error code!"); + + return unchecked(((int)0x80070000) | errorCode); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IObservable.cs b/external/corefx/src/Common/src/CoreLib/System/IObservable.cs new file mode 100644 index 0000000000..aabb0b8fb4 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IObservable.cs @@ -0,0 +1,11 @@ +// 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. + +namespace System +{ + public interface IObservable + { + IDisposable Subscribe(IObserver observer); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IObserver.cs b/external/corefx/src/Common/src/CoreLib/System/IObserver.cs new file mode 100644 index 0000000000..39e123de8d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IObserver.cs @@ -0,0 +1,13 @@ +// 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. + +namespace System +{ + public interface IObserver + { + void OnNext(T value); + void OnError(Exception error); + void OnCompleted(); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IProgress.cs b/external/corefx/src/Common/src/CoreLib/System/IProgress.cs new file mode 100644 index 0000000000..724c7bdce9 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IProgress.cs @@ -0,0 +1,15 @@ +// 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. + +namespace System +{ + /// Defines a provider for progress updates. + /// The type of progress update value. + public interface IProgress + { + /// Reports a progress update. + /// The value of the updated progress. + void Report(T value); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/ISpanFormattable.cs b/external/corefx/src/Common/src/CoreLib/System/ISpanFormattable.cs new file mode 100644 index 0000000000..df46b5bcd1 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ISpanFormattable.cs @@ -0,0 +1,11 @@ +// 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. + +namespace System +{ + internal interface ISpanFormattable + { + bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider provider); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/IndexOutOfRangeException.cs b/external/corefx/src/Common/src/CoreLib/System/IndexOutOfRangeException.cs new file mode 100644 index 0000000000..b6d93ef568 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/IndexOutOfRangeException.cs @@ -0,0 +1,44 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: Exception class for invalid array indices. +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class IndexOutOfRangeException : SystemException + { + public IndexOutOfRangeException() + : base(SR.Arg_IndexOutOfRangeException) + { + HResult = HResults.COR_E_INDEXOUTOFRANGE; + } + + public IndexOutOfRangeException(String message) + : base(message) + { + HResult = HResults.COR_E_INDEXOUTOFRANGE; + } + + public IndexOutOfRangeException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_INDEXOUTOFRANGE; + } + + internal IndexOutOfRangeException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/InsufficientExecutionStackException.cs b/external/corefx/src/Common/src/CoreLib/System/InsufficientExecutionStackException.cs new file mode 100644 index 0000000000..4822028f85 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/InsufficientExecutionStackException.cs @@ -0,0 +1,35 @@ +// 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.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class InsufficientExecutionStackException : SystemException + { + public InsufficientExecutionStackException() + : base(SR.Arg_InsufficientExecutionStackException) + { + HResult = HResults.COR_E_INSUFFICIENTEXECUTIONSTACK; + } + + public InsufficientExecutionStackException(String message) + : base(message) + { + HResult = HResults.COR_E_INSUFFICIENTEXECUTIONSTACK; + } + + public InsufficientExecutionStackException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_INSUFFICIENTEXECUTIONSTACK; + } + + internal InsufficientExecutionStackException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Int16.cs b/external/corefx/src/Common/src/CoreLib/System/Int16.cs similarity index 52% rename from external/corert/src/System.Private.CoreLib/src/System/Int16.cs rename to external/corefx/src/Common/src/CoreLib/System/Int16.cs index d0d2281f19..fecc87e9fe 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Int16.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Int16.cs @@ -2,27 +2,19 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -/*============================================================ -** -** -** -** Purpose: This class will encapsulate a short and provide an -** Object representation of it. -** -** -===========================================================*/ - using System.Globalization; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; -using System.Diagnostics.Contracts; namespace System { - [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)] - public struct Int16 : IComparable, IFormattable, IComparable, IEquatable, IConvertible + [Serializable] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Int16 : IComparable, IConvertible, IFormattable, IComparable, IEquatable, ISpanFormattable { - private short _value; + private short m_value; // Do not rename (binary serialization) public const short MaxValue = (short)0x7FFF; public const short MinValue = unchecked((short)0x8000); @@ -42,7 +34,7 @@ namespace System if (value is Int16) { - return _value - ((Int16)value)._value; + return m_value - ((Int16)value).m_value; } throw new ArgumentException(SR.Arg_MustBeInt16); @@ -50,7 +42,7 @@ namespace System public int CompareTo(Int16 value) { - return _value - value; + return m_value - value; } public override bool Equals(Object obj) @@ -59,76 +51,96 @@ namespace System { return false; } - return _value == ((Int16)obj)._value; + return m_value == ((Int16)obj).m_value; } [NonVersionable] public bool Equals(Int16 obj) { - return _value == obj; + return m_value == obj; } // Returns a HashCode for the Int16 public override int GetHashCode() { - return ((int)((ushort)_value) | (((int)_value) << 16)); + return ((int)((ushort)m_value) | (((int)m_value) << 16)); } public override String ToString() { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatInt32(_value, null, null); + return Number.FormatInt32(m_value, null, null); } public String ToString(IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatInt32(_value, null, provider); + return Number.FormatInt32(m_value, null, provider); } public String ToString(String format) { - Contract.Ensures(Contract.Result() != null); return ToString(format, null); } public String ToString(String format, IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); - - - if (_value < 0 && format != null && format.Length > 0 && (format[0] == 'X' || format[0] == 'x')) + if (m_value < 0 && format != null && format.Length > 0 && (format[0] == 'X' || format[0] == 'x')) { - uint temp = (uint)(_value & 0x0000FFFF); - return FormatProvider.FormatUInt32(temp, format, provider); + uint temp = (uint)(m_value & 0x0000FFFF); + return Number.FormatUInt32(temp, format, provider); } - return FormatProvider.FormatInt32(_value, format, provider); + + return Number.FormatInt32(m_value, format, provider); + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) + { + if (m_value < 0 && format.Length > 0 && (format[0] == 'X' || format[0] == 'x')) + { + uint temp = (uint)(m_value & 0x0000FFFF); + return Number.TryFormatUInt32(temp, format, provider, destination, out charsWritten); + } + return Number.TryFormatInt32(m_value, format, provider, destination, out charsWritten); } public static short Parse(String s) { - return Parse(s, NumberStyles.Integer, null); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } public static short Parse(String s, NumberStyles style) { - UInt32.ValidateParseStyleInteger(style); - return Parse(s, style, null); + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, style, NumberFormatInfo.CurrentInfo); } public static short Parse(String s, IFormatProvider provider) { - return Parse(s, NumberStyles.Integer, provider); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } public static short Parse(String s, NumberStyles style, IFormatProvider provider) { - UInt32.ValidateParseStyleInteger(style); + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider)); + } + + public static short Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Parse(s, style, NumberFormatInfo.GetInstance(provider)); + } + + private static short Parse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info) + { int i = 0; try { - i = FormatProvider.ParseInt32(s, style, provider); + i = Number.ParseInt32(s, style, info); } catch (OverflowException e) { @@ -152,15 +164,44 @@ namespace System public static bool TryParse(String s, out Int16 result) { - return TryParse(s, NumberStyles.Integer, null, out result); + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(ReadOnlySpan s, out short result) + { + return TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); } public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out Int16 result) { - UInt32.ValidateParseStyleInteger(style); + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out short result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + private static bool TryParse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out Int16 result) + { result = 0; int i; - if (!FormatProvider.TryParseInt32(s, style, provider, out i)) + if (!Number.TryParseInt32(s, style, info, out i)) { return false; } @@ -197,72 +238,72 @@ namespace System bool IConvertible.ToBoolean(IFormatProvider provider) { - return Convert.ToBoolean(_value); + return Convert.ToBoolean(m_value); } char IConvertible.ToChar(IFormatProvider provider) { - return Convert.ToChar(_value); + return Convert.ToChar(m_value); } sbyte IConvertible.ToSByte(IFormatProvider provider) { - return Convert.ToSByte(_value); + return Convert.ToSByte(m_value); } byte IConvertible.ToByte(IFormatProvider provider) { - return Convert.ToByte(_value); + return Convert.ToByte(m_value); } short IConvertible.ToInt16(IFormatProvider provider) { - return _value; + return m_value; } ushort IConvertible.ToUInt16(IFormatProvider provider) { - return Convert.ToUInt16(_value); + return Convert.ToUInt16(m_value); } int IConvertible.ToInt32(IFormatProvider provider) { - return Convert.ToInt32(_value); + return Convert.ToInt32(m_value); } uint IConvertible.ToUInt32(IFormatProvider provider) { - return Convert.ToUInt32(_value); + return Convert.ToUInt32(m_value); } long IConvertible.ToInt64(IFormatProvider provider) { - return Convert.ToInt64(_value); + return Convert.ToInt64(m_value); } ulong IConvertible.ToUInt64(IFormatProvider provider) { - return Convert.ToUInt64(_value); + return Convert.ToUInt64(m_value); } float IConvertible.ToSingle(IFormatProvider provider) { - return Convert.ToSingle(_value); + return Convert.ToSingle(m_value); } double IConvertible.ToDouble(IFormatProvider provider) { - return Convert.ToDouble(_value); + return Convert.ToDouble(m_value); } Decimal IConvertible.ToDecimal(IFormatProvider provider) { - return Convert.ToDecimal(_value); + return Convert.ToDecimal(m_value); } DateTime IConvertible.ToDateTime(IFormatProvider provider) { - throw new InvalidCastException(String.Format(SR.InvalidCast_FromTo, "Int16", "DateTime")); + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Int16", "DateTime")); } Object IConvertible.ToType(Type type, IFormatProvider provider) diff --git a/external/corert/src/System.Private.CoreLib/src/System/Int32.cs b/external/corefx/src/Common/src/CoreLib/System/Int32.cs similarity index 55% rename from external/corert/src/System.Private.CoreLib/src/System/Int32.cs rename to external/corefx/src/Common/src/CoreLib/System/Int32.cs index f80fdff18a..b573e950e4 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Int32.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Int32.cs @@ -2,33 +2,19 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -/*============================================================ -** -** Purpose: A representation of a 32 bit 2's complement -** integer. -** -===========================================================*/ - using System.Globalization; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; -using System.Diagnostics.Contracts; namespace System { - // CONTRACT with Runtime - // The Int32 type is one of the primitives understood by the compilers and runtime - // Data Contract: Single field of type int - // This type is LayoutKind Sequential - - [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)] - public struct Int32 : IComparable, IFormattable, IComparable, IEquatable, IConvertible + [Serializable] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Int32 : IComparable, IConvertible, IFormattable, IComparable, IEquatable, ISpanFormattable { - // _value is never assigned to by any of the methods. - // Disabling the warning as this type is a built-in primitive that the compilers know about -#pragma warning disable 0649 - private int _value; -#pragma warning restore 0649 + private int m_value; // Do not rename (binary serialization) public const int MaxValue = 0x7fffffff; public const int MinValue = unchecked((int)0x80000000); @@ -53,8 +39,8 @@ namespace System // NOTE: Cannot use return (_value - value) as this causes a wrap // around in cases where _value - value > MaxValue. int i = (int)value; - if (_value < i) return -1; - if (_value > i) return 1; + if (m_value < i) return -1; + if (m_value > i) return 1; return 0; } throw new ArgumentException(SR.Arg_MustBeInt32); @@ -64,8 +50,8 @@ namespace System { // NOTE: Cannot use return (_value - value) as this causes a wrap // around in cases where _value - value > MaxValue. - if (_value < value) return -1; - if (_value > value) return 1; + if (m_value < value) return -1; + if (m_value > value) return 1; return 0; } @@ -75,110 +61,131 @@ namespace System { return false; } - return _value == ((Int32)obj)._value; + return m_value == ((Int32)obj).m_value; } [NonVersionable] public bool Equals(Int32 obj) { - return _value == obj; + return m_value == obj; } // The absolute value of the int contained. public override int GetHashCode() { - return _value; + return m_value; } - [Pure] public override String ToString() { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatInt32(_value, null, null); + return Number.FormatInt32(m_value, null, null); } - [Pure] public String ToString(String format) { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatInt32(_value, format, null); + return Number.FormatInt32(m_value, format, null); } - [Pure] public String ToString(IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatInt32(_value, null, provider); + return Number.FormatInt32(m_value, null, provider); } - [Pure] public String ToString(String format, IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatInt32(_value, format, provider); + return Number.FormatInt32(m_value, format, provider); + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) + { + return Number.TryFormatInt32(m_value, format, provider, destination, out charsWritten); } - // Parses an integer from a String in the given style. If - // a NumberFormatInfo isn't specified, the current culture's - // NumberFormatInfo is assumed. - [Pure] public static int Parse(String s) { - return FormatProvider.ParseInt32(s, NumberStyles.Integer, null); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } - [Pure] public static int Parse(String s, NumberStyles style) { - UInt32.ValidateParseStyleInteger(style); - return FormatProvider.ParseInt32(s, style, null); + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt32(s, style, NumberFormatInfo.CurrentInfo); } // Parses an integer from a String in the given style. If // a NumberFormatInfo isn't specified, the current culture's // NumberFormatInfo is assumed. // - [Pure] public static int Parse(String s, IFormatProvider provider) { - return FormatProvider.ParseInt32(s, NumberStyles.Integer, provider); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt32(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } // Parses an integer from a String in the given style. If // a NumberFormatInfo isn't specified, the current culture's // NumberFormatInfo is assumed. // - [Pure] public static int Parse(String s, NumberStyles style, IFormatProvider provider) { - UInt32.ValidateParseStyleInteger(style); - return FormatProvider.ParseInt32(s, style, provider); + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt32(s, style, NumberFormatInfo.GetInstance(provider)); + } + + public static int Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.ParseInt32(s, style, NumberFormatInfo.GetInstance(provider)); } // Parses an integer from a String. Returns false rather // than throwing exceptin if input is invalid // - [Pure] public static bool TryParse(String s, out Int32 result) { - return FormatProvider.TryParseInt32(s, NumberStyles.Integer, null, out result); + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(ReadOnlySpan s, out int result) + { + return Number.TryParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); } // Parses an integer from a String in the given style. Returns false rather // than throwing exceptin if input is invalid // - [Pure] public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out Int32 result) { - UInt32.ValidateParseStyleInteger(style); - return FormatProvider.TryParseInt32(s, style, provider, out result); + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseInt32(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out int result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseInt32(s, style, NumberFormatInfo.GetInstance(provider), out result); } // // IConvertible implementation // - [Pure] public TypeCode GetTypeCode() { return TypeCode.Int32; @@ -186,72 +193,72 @@ namespace System bool IConvertible.ToBoolean(IFormatProvider provider) { - return Convert.ToBoolean(_value); + return Convert.ToBoolean(m_value); } char IConvertible.ToChar(IFormatProvider provider) { - return Convert.ToChar(_value); + return Convert.ToChar(m_value); } sbyte IConvertible.ToSByte(IFormatProvider provider) { - return Convert.ToSByte(_value); + return Convert.ToSByte(m_value); } byte IConvertible.ToByte(IFormatProvider provider) { - return Convert.ToByte(_value); + return Convert.ToByte(m_value); } short IConvertible.ToInt16(IFormatProvider provider) { - return Convert.ToInt16(_value); + return Convert.ToInt16(m_value); } ushort IConvertible.ToUInt16(IFormatProvider provider) { - return Convert.ToUInt16(_value); + return Convert.ToUInt16(m_value); } int IConvertible.ToInt32(IFormatProvider provider) { - return _value; + return m_value; } uint IConvertible.ToUInt32(IFormatProvider provider) { - return Convert.ToUInt32(_value); + return Convert.ToUInt32(m_value); } long IConvertible.ToInt64(IFormatProvider provider) { - return Convert.ToInt64(_value); + return Convert.ToInt64(m_value); } ulong IConvertible.ToUInt64(IFormatProvider provider) { - return Convert.ToUInt64(_value); + return Convert.ToUInt64(m_value); } float IConvertible.ToSingle(IFormatProvider provider) { - return Convert.ToSingle(_value); + return Convert.ToSingle(m_value); } double IConvertible.ToDouble(IFormatProvider provider) { - return Convert.ToDouble(_value); + return Convert.ToDouble(m_value); } Decimal IConvertible.ToDecimal(IFormatProvider provider) { - return Convert.ToDecimal(_value); + return Convert.ToDecimal(m_value); } DateTime IConvertible.ToDateTime(IFormatProvider provider) { - throw new InvalidCastException(String.Format(SR.InvalidCast_FromTo, "Int32", "DateTime")); + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Int32", "DateTime")); } Object IConvertible.ToType(Type type, IFormatProvider provider) diff --git a/external/corert/src/System.Private.CoreLib/src/System/Int64.cs b/external/corefx/src/Common/src/CoreLib/System/Int64.cs similarity index 51% rename from external/corert/src/System.Private.CoreLib/src/System/Int64.cs rename to external/corefx/src/Common/src/CoreLib/System/Int64.cs index 196ce52aa2..0bcca87309 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Int64.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Int64.cs @@ -2,27 +2,19 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -/*============================================================ -** -** -** -** Purpose: This class will encapsulate a long and provide an -** Object representation of it. -** -** -===========================================================*/ - using System.Globalization; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; -using System.Diagnostics.Contracts; namespace System { - [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)] - public struct Int64 : IComparable, IFormattable, IComparable, IEquatable, IConvertible + [Serializable] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Int64 : IComparable, IConvertible, IFormattable, IComparable, IEquatable, ISpanFormattable { - private long _value; + private long m_value; // Do not rename (binary serialization) public const long MaxValue = 0x7fffffffffffffffL; public const long MinValue = unchecked((long)0x8000000000000000L); @@ -44,8 +36,8 @@ namespace System // Need to use compare because subtraction will wrap // to positive for very large neg numbers, etc. long i = (long)value; - if (_value < i) return -1; - if (_value > i) return 1; + if (m_value < i) return -1; + if (m_value > i) return 1; return 0; } throw new ArgumentException(SR.Arg_MustBeInt64); @@ -55,8 +47,8 @@ namespace System { // Need to use compare because subtraction will wrap // to positive for very large neg numbers, etc. - if (_value < value) return -1; - if (_value > value) return 1; + if (m_value < value) return -1; + if (m_value > value) return 1; return 0; } @@ -66,59 +58,63 @@ namespace System { return false; } - return _value == ((Int64)obj)._value; + return m_value == ((Int64)obj).m_value; } [NonVersionable] public bool Equals(Int64 obj) { - return _value == obj; + return m_value == obj; } // The value of the lower 32 bits XORed with the uppper 32 bits. public override int GetHashCode() { - return (unchecked((int)((long)_value)) ^ (int)(_value >> 32)); + return (unchecked((int)((long)m_value)) ^ (int)(m_value >> 32)); } public override String ToString() { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatInt64(_value, null, null); + return Number.FormatInt64(m_value, null, null); } public String ToString(IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatInt64(_value, null, provider); + return Number.FormatInt64(m_value, null, provider); } public String ToString(String format) { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatInt64(_value, format, null); + return Number.FormatInt64(m_value, format, null); } public String ToString(String format, IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatInt64(_value, format, provider); + return Number.FormatInt64(m_value, format, provider); + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) + { + return Number.TryFormatInt64(m_value, format, provider, destination, out charsWritten); } public static long Parse(String s) { - return FormatProvider.ParseInt64(s, NumberStyles.Integer, null); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } public static long Parse(String s, NumberStyles style) { - UInt32.ValidateParseStyleInteger(style); - return FormatProvider.ParseInt64(s, style, null); + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt64(s, style, NumberFormatInfo.CurrentInfo); } public static long Parse(String s, IFormatProvider provider) { - return FormatProvider.ParseInt64(s, NumberStyles.Integer, provider); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt64(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } @@ -128,19 +124,50 @@ namespace System // public static long Parse(String s, NumberStyles style, IFormatProvider provider) { - UInt32.ValidateParseStyleInteger(style); - return FormatProvider.ParseInt64(s, style, provider); + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt64(s, style, NumberFormatInfo.GetInstance(provider)); + } + + public static long Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.ParseInt64(s, style, NumberFormatInfo.GetInstance(provider)); } public static Boolean TryParse(String s, out Int64 result) { - return FormatProvider.TryParseInt64(s, NumberStyles.Integer, null, out result); + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(ReadOnlySpan s, out long result) + { + return Number.TryParseInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); } public static Boolean TryParse(String s, NumberStyles style, IFormatProvider provider, out Int64 result) { - UInt32.ValidateParseStyleInteger(style); - return FormatProvider.TryParseInt64(s, style, provider, out result); + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseInt64(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out long result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseInt64(s, style, NumberFormatInfo.GetInstance(provider), out result); } // @@ -154,72 +181,72 @@ namespace System bool IConvertible.ToBoolean(IFormatProvider provider) { - return Convert.ToBoolean(_value); + return Convert.ToBoolean(m_value); } char IConvertible.ToChar(IFormatProvider provider) { - return Convert.ToChar(_value); + return Convert.ToChar(m_value); } sbyte IConvertible.ToSByte(IFormatProvider provider) { - return Convert.ToSByte(_value); + return Convert.ToSByte(m_value); } byte IConvertible.ToByte(IFormatProvider provider) { - return Convert.ToByte(_value); + return Convert.ToByte(m_value); } short IConvertible.ToInt16(IFormatProvider provider) { - return Convert.ToInt16(_value); + return Convert.ToInt16(m_value); } ushort IConvertible.ToUInt16(IFormatProvider provider) { - return Convert.ToUInt16(_value); + return Convert.ToUInt16(m_value); } int IConvertible.ToInt32(IFormatProvider provider) { - return Convert.ToInt32(_value); + return Convert.ToInt32(m_value); } uint IConvertible.ToUInt32(IFormatProvider provider) { - return Convert.ToUInt32(_value); + return Convert.ToUInt32(m_value); } long IConvertible.ToInt64(IFormatProvider provider) { - return _value; + return m_value; } ulong IConvertible.ToUInt64(IFormatProvider provider) { - return Convert.ToUInt64(_value); + return Convert.ToUInt64(m_value); } float IConvertible.ToSingle(IFormatProvider provider) { - return Convert.ToSingle(_value); + return Convert.ToSingle(m_value); } double IConvertible.ToDouble(IFormatProvider provider) { - return Convert.ToDouble(_value); + return Convert.ToDouble(m_value); } Decimal IConvertible.ToDecimal(IFormatProvider provider) { - return Convert.ToDecimal(_value); + return Convert.ToDecimal(m_value); } DateTime IConvertible.ToDateTime(IFormatProvider provider) { - throw new InvalidCastException(String.Format(SR.InvalidCast_FromTo, "Int64", "DateTime")); + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Int64", "DateTime")); } Object IConvertible.ToType(Type type, IFormatProvider provider) diff --git a/external/corert/src/System.Private.CoreLib/src/System/IntPtr.cs b/external/corefx/src/Common/src/CoreLib/System/IntPtr.cs similarity index 76% rename from external/corert/src/System.Private.CoreLib/src/System/IntPtr.cs rename to external/corefx/src/Common/src/CoreLib/System/IntPtr.cs index 5e8f0786d4..45c2ded160 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/IntPtr.cs +++ b/external/corefx/src/Common/src/CoreLib/System/IntPtr.cs @@ -7,20 +7,23 @@ using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Runtime.Versioning; +#if BIT64 +using nint = System.Int64; +#else +using nint = System.Int32; +#endif + namespace System { - // CONTRACT with Runtime - // The IntPtr type is one of the primitives understood by the compilers and runtime - // Data Contract: Single field of type void * - [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public struct IntPtr : IEquatable, ISerializable { // WARNING: We allow diagnostic tools to directly inspect this member (_value). // See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details. // Please do not change the type, the name, or the semantic usage of this member without understanding the implication for tools. // Get in touch with the diagnostics team if you have questions. - unsafe private void* _value; // The compiler treats void* closest to uint hence explicit casts are required to preserve int behavior + unsafe private void* _value; // Do not rename (binary serialization) [Intrinsic] public static readonly IntPtr Zero; @@ -29,11 +32,7 @@ namespace System [NonVersionable] public unsafe IntPtr(int value) { -#if BIT64 - _value = (void*)(long)value; -#else _value = (void*)value; -#endif } [Intrinsic] @@ -70,19 +69,31 @@ namespace System if (info == null) throw new ArgumentNullException(nameof(info)); -#if BIT64 - info.AddValue("value", (long)(_value)); -#else // !BIT64 (32) - info.AddValue("value", (long)((int)_value)); -#endif + info.AddValue("value", ToInt64()); } - [CLSCompliant(false)] - [Intrinsic] - [NonVersionable] - public unsafe void* ToPointer() + public unsafe override bool Equals(Object obj) { - return _value; + if (obj is IntPtr) + { + return (_value == ((IntPtr)obj)._value); + } + return false; + } + + unsafe bool IEquatable.Equals(IntPtr value) + { + return _value == value._value; + } + + public unsafe override int GetHashCode() + { +#if BIT64 + long l = (long)_value; + return (unchecked((int)l) ^ (int)(l >> 32)); +#else + return unchecked((int)_value); +#endif } [Intrinsic] @@ -101,11 +112,7 @@ namespace System [NonVersionable] public unsafe long ToInt64() { -#if BIT64 - return (long)_value; -#else - return (long)(int)_value; -#endif + return (nint)_value; } [Intrinsic] @@ -154,16 +161,7 @@ namespace System [NonVersionable] public static unsafe explicit operator long(IntPtr value) { -#if BIT64 - return (long)value._value; -#else - return (long)(int)value._value; -#endif - } - - unsafe bool IEquatable.Equals(IntPtr value) - { - return _value == value._value; + return (nint)value._value; } [Intrinsic] @@ -180,11 +178,6 @@ namespace System return value1._value != value2._value; } - internal unsafe bool IsNull() - { - return (_value == null); - } - [NonVersionable] public static IntPtr Add(IntPtr pointer, int offset) { @@ -195,11 +188,7 @@ namespace System [NonVersionable] public static unsafe IntPtr operator +(IntPtr pointer, int offset) { -#if BIT64 - return new IntPtr((long)pointer._value + offset); -#else - return new IntPtr((int)pointer._value + offset); -#endif + return new IntPtr((nint)pointer._value + offset); } [NonVersionable] @@ -212,62 +201,35 @@ namespace System [NonVersionable] public static unsafe IntPtr operator -(IntPtr pointer, int offset) { -#if BIT64 - return new IntPtr((long)pointer._value - offset); -#else - return new IntPtr((int)pointer._value - offset); -#endif + return new IntPtr((nint)pointer._value - offset); } - public static unsafe int Size + public static int Size { [Intrinsic] [NonVersionable] get { -#if BIT64 - return 8; -#else - return 4; -#endif + return sizeof(nint); } } - public unsafe override String ToString() + [CLSCompliant(false)] + [Intrinsic] + [NonVersionable] + public unsafe void* ToPointer() { -#if BIT64 - return ((long)_value).ToString(FormatProvider.InvariantCulture); -#else - return ((int)_value).ToString(FormatProvider.InvariantCulture); -#endif + return _value; } - public unsafe String ToString(String format) + public unsafe override string ToString() { -#if BIT64 - return ((long)_value).ToString(format, FormatProvider.InvariantCulture); -#else - return ((int)_value).ToString(format, FormatProvider.InvariantCulture); -#endif + return ((nint)_value).ToString(CultureInfo.InvariantCulture); } - public unsafe override bool Equals(Object obj) + public unsafe string ToString(string format) { - if (obj is IntPtr) - { - return (_value == ((IntPtr)obj)._value); - } - return false; - } - - public unsafe override int GetHashCode() - { -#if BIT64 - long l = (long)_value; - return (unchecked((int)l) ^ (int)(l >> 32)); -#else - return unchecked((int)_value); -#endif + return ((nint)_value).ToString(format, CultureInfo.InvariantCulture); } } } diff --git a/external/corefx/src/Common/src/CoreLib/System/InvalidCastException.cs b/external/corefx/src/Common/src/CoreLib/System/InvalidCastException.cs new file mode 100644 index 0000000000..055643278a --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/InvalidCastException.cs @@ -0,0 +1,47 @@ +// 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. + +/*============================================================================= +** +** Purpose: Exception class for invalid cast conditions! +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class InvalidCastException : SystemException + { + public InvalidCastException() + : base(SR.Arg_InvalidCastException) + { + HResult = HResults.COR_E_INVALIDCAST; + } + + public InvalidCastException(String message) + : base(message) + { + HResult = HResults.COR_E_INVALIDCAST; + } + + public InvalidCastException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_INVALIDCAST; + } + + public InvalidCastException(String message, int errorCode) + : base(message) + { + HResult = errorCode; + } + + protected InvalidCastException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/InvalidOperationException.cs b/external/corefx/src/Common/src/CoreLib/System/InvalidOperationException.cs new file mode 100644 index 0000000000..62c222af40 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/InvalidOperationException.cs @@ -0,0 +1,45 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: Exception class for denoting an object was in a state that +** made calling a method illegal. +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class InvalidOperationException : SystemException + { + public InvalidOperationException() + : base(SR.Arg_InvalidOperationException) + { + HResult = HResults.COR_E_INVALIDOPERATION; + } + + public InvalidOperationException(String message) + : base(message) + { + HResult = HResults.COR_E_INVALIDOPERATION; + } + + public InvalidOperationException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_INVALIDOPERATION; + } + + protected InvalidOperationException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/InvalidProgramException.cs b/external/corefx/src/Common/src/CoreLib/System/InvalidProgramException.cs new file mode 100644 index 0000000000..c8047c548b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/InvalidProgramException.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. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: The exception class for programs with invalid IL or bad metadata. +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class InvalidProgramException : SystemException + { + public InvalidProgramException() + : base(SR.InvalidProgram_Default) + { + HResult = HResults.COR_E_INVALIDPROGRAM; + } + + public InvalidProgramException(String message) + : base(message) + { + HResult = HResults.COR_E_INVALIDPROGRAM; + } + + public InvalidProgramException(String message, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_INVALIDPROGRAM; + } + + internal InvalidProgramException(SerializationInfo info, StreamingContext context) : base(info, context) { } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/InvalidTimeZoneException.cs b/external/corefx/src/Common/src/CoreLib/System/InvalidTimeZoneException.cs new file mode 100644 index 0000000000..25b155e8d1 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/InvalidTimeZoneException.cs @@ -0,0 +1,31 @@ +// 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.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class InvalidTimeZoneException : Exception + { + public InvalidTimeZoneException() + { + } + + public InvalidTimeZoneException(String message) + : base(message) + { + } + + public InvalidTimeZoneException(String message, Exception innerException) + : base(message, innerException) + { + } + + protected InvalidTimeZoneException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Lazy.cs b/external/corefx/src/Common/src/CoreLib/System/Lazy.cs new file mode 100644 index 0000000000..6410c2e285 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Lazy.cs @@ -0,0 +1,548 @@ +// 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. + +// -------------------------------------------------------------------------------------- +// +// A class that provides a simple, lightweight implementation of lazy initialization, +// obviating the need for a developer to implement a custom, thread-safe lazy initialization +// solution. +// +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +#pragma warning disable 0420 + +using System.Diagnostics; +using System.Runtime.ExceptionServices; +using System.Runtime.InteropServices; +using System.Threading; + +namespace System +{ + internal enum LazyState + { + NoneViaConstructor = 0, + NoneViaFactory = 1, + NoneException = 2, + + PublicationOnlyViaConstructor = 3, + PublicationOnlyViaFactory = 4, + PublicationOnlyWait = 5, + PublicationOnlyException = 6, + + ExecutionAndPublicationViaConstructor = 7, + ExecutionAndPublicationViaFactory = 8, + ExecutionAndPublicationException = 9, + } + + /// + /// LazyHelper serves multiples purposes + /// - minimizing code size of Lazy<T> by implementing as much of the code that is not generic + /// this reduces generic code bloat, making faster class initialization + /// - contains singleton objects that are used to handle threading primitives for PublicationOnly mode + /// - allows for instantiation for ExecutionAndPublication so as to create an object for locking on + /// - holds exception information. + /// + internal class LazyHelper + { + internal readonly static LazyHelper NoneViaConstructor = new LazyHelper(LazyState.NoneViaConstructor); + internal readonly static LazyHelper NoneViaFactory = new LazyHelper(LazyState.NoneViaFactory); + internal readonly static LazyHelper PublicationOnlyViaConstructor = new LazyHelper(LazyState.PublicationOnlyViaConstructor); + internal readonly static LazyHelper PublicationOnlyViaFactory = new LazyHelper(LazyState.PublicationOnlyViaFactory); + internal readonly static LazyHelper PublicationOnlyWaitForOtherThreadToPublish = new LazyHelper(LazyState.PublicationOnlyWait); + + internal LazyState State { get; } + + private readonly ExceptionDispatchInfo _exceptionDispatch; + + /// + /// Constructor that defines the state + /// + internal LazyHelper(LazyState state) + { + State = state; + } + + /// + /// Constructor used for exceptions + /// + internal LazyHelper(LazyThreadSafetyMode mode, Exception exception) + { + switch(mode) + { + case LazyThreadSafetyMode.ExecutionAndPublication: + State = LazyState.ExecutionAndPublicationException; + break; + + case LazyThreadSafetyMode.None: + State = LazyState.NoneException; + break; + + case LazyThreadSafetyMode.PublicationOnly: + State = LazyState.PublicationOnlyException; + break; + + default: + Debug.Fail("internal constructor, this should never occur"); + break; + } + + _exceptionDispatch = ExceptionDispatchInfo.Capture(exception); + } + + internal void ThrowException() + { + Debug.Assert(_exceptionDispatch != null, "execution path is invalid"); + + _exceptionDispatch.Throw(); + } + + private LazyThreadSafetyMode GetMode() + { + switch (State) + { + case LazyState.NoneViaConstructor: + case LazyState.NoneViaFactory: + case LazyState.NoneException: + return LazyThreadSafetyMode.None; + + case LazyState.PublicationOnlyViaConstructor: + case LazyState.PublicationOnlyViaFactory: + case LazyState.PublicationOnlyWait: + case LazyState.PublicationOnlyException: + return LazyThreadSafetyMode.PublicationOnly; + + case LazyState.ExecutionAndPublicationViaConstructor: + case LazyState.ExecutionAndPublicationViaFactory: + case LazyState.ExecutionAndPublicationException: + return LazyThreadSafetyMode.ExecutionAndPublication; + + default: + Debug.Fail("Invalid logic; State should always have a valid value"); + return default(LazyThreadSafetyMode); + } + } + + internal static LazyThreadSafetyMode? GetMode(LazyHelper state) + { + if (state == null) + return null; // we don't know the mode anymore + return state.GetMode(); + } + + internal static bool GetIsValueFaulted(LazyHelper state) => state?._exceptionDispatch != null; + + internal static LazyHelper Create(LazyThreadSafetyMode mode, bool useDefaultConstructor) + { + switch (mode) + { + case LazyThreadSafetyMode.None: + return useDefaultConstructor ? NoneViaConstructor : NoneViaFactory; + + case LazyThreadSafetyMode.PublicationOnly: + return useDefaultConstructor ? PublicationOnlyViaConstructor : PublicationOnlyViaFactory; + + case LazyThreadSafetyMode.ExecutionAndPublication: + // we need to create an object for ExecutionAndPublication because we use Monitor-based locking + var state = useDefaultConstructor ? LazyState.ExecutionAndPublicationViaConstructor : LazyState.ExecutionAndPublicationViaFactory; + return new LazyHelper(state); + + default: + throw new ArgumentOutOfRangeException(nameof(mode), SR.Lazy_ctor_ModeInvalid); + } + } + + internal static object CreateViaDefaultConstructor(Type type) + { + try + { + return Activator.CreateInstance(type); + } + catch (MissingMethodException) + { + throw new MissingMemberException(SR.Lazy_CreateValue_NoParameterlessCtorForT); + } + } + + internal static LazyThreadSafetyMode GetModeFromIsThreadSafe(bool isThreadSafe) + { + return isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None; + } + } + + /// + /// Provides support for lazy initialization. + /// + /// Specifies the type of element being lazily initialized. + /// + /// + /// By default, all public and protected members of are thread-safe and may be used + /// concurrently from multiple threads. These thread-safety guarantees may be removed optionally and per instance + /// using parameters to the type's constructors. + /// + /// + [DebuggerTypeProxy(typeof(LazyDebugView<>))] + [DebuggerDisplay("ThreadSafetyMode={Mode}, IsValueCreated={IsValueCreated}, IsValueFaulted={IsValueFaulted}, Value={ValueForDebugDisplay}")] + public class Lazy + { + private static T CreateViaDefaultConstructor() + { + return (T)LazyHelper.CreateViaDefaultConstructor(typeof(T)); + } + + // _state, a volatile reference, is set to null after _value has been set + private volatile LazyHelper _state; + + // we ensure that _factory when finished is set to null to allow garbage collector to clean up + // any referenced items + private Func _factory; + + // _value eventually stores the lazily created value. It is valid when _state = null. + private T _value; + + /// + /// Initializes a new instance of the class that + /// uses 's default constructor for lazy initialization. + /// + /// + /// An instance created with this constructor may be used concurrently from multiple threads. + /// + public Lazy() + : this(null, LazyThreadSafetyMode.ExecutionAndPublication, useDefaultConstructor:true) + { + } + + /// + /// Initializes a new instance of the class that + /// uses a pre-initialized specified value. + /// + /// + /// An instance created with this constructor should be usable by multiple threads + // concurrently. + /// + public Lazy(T value) + { + _value = value; + } + + /// + /// Initializes a new instance of the class that uses a + /// specified initialization function. + /// + /// + /// The invoked to produce the lazily-initialized value when it is + /// needed. + /// + /// is a null + /// reference (Nothing in Visual Basic). + /// + /// An instance created with this constructor may be used concurrently from multiple threads. + /// + public Lazy(Func valueFactory) + : this(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication, useDefaultConstructor:false) + { + } + + /// + /// Initializes a new instance of the + /// class that uses 's default constructor and a specified thread-safety mode. + /// + /// true if this instance should be usable by multiple threads concurrently; false if the instance will only be used by one thread at a time. + /// + public Lazy(bool isThreadSafe) : + this(null, LazyHelper.GetModeFromIsThreadSafe(isThreadSafe), useDefaultConstructor:true) + { + } + + /// + /// Initializes a new instance of the + /// class that uses 's default constructor and a specified thread-safety mode. + /// + /// The lazy thread-safety mode + /// mode contains an invalid valuee + public Lazy(LazyThreadSafetyMode mode) : + this(null, mode, useDefaultConstructor:true) + { + } + + /// + /// Initializes a new instance of the class + /// that uses a specified initialization function and a specified thread-safety mode. + /// + /// + /// The invoked to produce the lazily-initialized value when it is needed. + /// + /// true if this instance should be usable by multiple threads concurrently; false if the instance will only be used by one thread at a time. + /// + /// is + /// a null reference (Nothing in Visual Basic). + public Lazy(Func valueFactory, bool isThreadSafe) : + this(valueFactory, LazyHelper.GetModeFromIsThreadSafe(isThreadSafe), useDefaultConstructor:false) + { + } + + /// + /// Initializes a new instance of the class + /// that uses a specified initialization function and a specified thread-safety mode. + /// + /// + /// The invoked to produce the lazily-initialized value when it is needed. + /// + /// The lazy thread-safety mode. + /// is + /// a null reference (Nothing in Visual Basic). + /// mode contains an invalid value. + public Lazy(Func valueFactory, LazyThreadSafetyMode mode) + : this(valueFactory, mode, useDefaultConstructor:false) + { + } + + private Lazy(Func valueFactory, LazyThreadSafetyMode mode, bool useDefaultConstructor) + { + if (valueFactory == null && !useDefaultConstructor) + throw new ArgumentNullException(nameof(valueFactory)); + + _factory = valueFactory; + _state = LazyHelper.Create(mode, useDefaultConstructor); + } + + private void ViaConstructor() + { + _value = CreateViaDefaultConstructor(); + _state = null; // volatile write, must occur after setting _value + } + + private void ViaFactory(LazyThreadSafetyMode mode) + { + try + { + Func factory = _factory; + if (factory == null) + throw new InvalidOperationException(SR.Lazy_Value_RecursiveCallsToValue); + _factory = null; + + _value = factory(); + _state = null; // volatile write, must occur after setting _value + } + catch (Exception exception) + { + _state = new LazyHelper(mode, exception); + throw; + } + } + + private void ExecutionAndPublication(LazyHelper executionAndPublication, bool useDefaultConstructor) + { + lock (executionAndPublication) + { + // it's possible for multiple calls to have piled up behind the lock, so we need to check + // to see if the ExecutionAndPublication object is still the current implementation. + if (ReferenceEquals(_state, executionAndPublication)) + { + if (useDefaultConstructor) + { + ViaConstructor(); + } + else + { + ViaFactory(LazyThreadSafetyMode.ExecutionAndPublication); + } + } + } + } + + private void PublicationOnly(LazyHelper publicationOnly, T possibleValue) + { + LazyHelper previous = Interlocked.CompareExchange(ref _state, LazyHelper.PublicationOnlyWaitForOtherThreadToPublish, publicationOnly); + if (previous == publicationOnly) + { + _factory = null; + _value = possibleValue; + _state = null; // volatile write, must occur after setting _value + } + } + + private void PublicationOnlyViaConstructor(LazyHelper initializer) + { + PublicationOnly(initializer, CreateViaDefaultConstructor()); + } + + private void PublicationOnlyViaFactory(LazyHelper initializer) + { + Func factory = _factory; + if (factory == null) + { + PublicationOnlyWaitForOtherThreadToPublish(); + } + else + { + PublicationOnly(initializer, factory()); + } + } + + private void PublicationOnlyWaitForOtherThreadToPublish() + { + var spinWait = new SpinWait(); + while (!ReferenceEquals(_state, null)) + { + // We get here when PublicationOnly temporarily sets _state to LazyHelper.PublicationOnlyWaitForOtherThreadToPublish. + // This temporary state should be quickly followed by _state being set to null. + spinWait.SpinOnce(); + } + } + + private T CreateValue() + { + // we have to create a copy of state here, and use the copy exclusively from here on in + // so as to ensure thread safety. + var state = _state; + if (state != null) + { + switch (state.State) + { + case LazyState.NoneViaConstructor: + ViaConstructor(); + break; + + case LazyState.NoneViaFactory: + ViaFactory(LazyThreadSafetyMode.None); + break; + + case LazyState.PublicationOnlyViaConstructor: + PublicationOnlyViaConstructor(state); + break; + + case LazyState.PublicationOnlyViaFactory: + PublicationOnlyViaFactory(state); + break; + + case LazyState.PublicationOnlyWait: + PublicationOnlyWaitForOtherThreadToPublish(); + break; + + case LazyState.ExecutionAndPublicationViaConstructor: + ExecutionAndPublication(state, useDefaultConstructor:true); + break; + + case LazyState.ExecutionAndPublicationViaFactory: + ExecutionAndPublication(state, useDefaultConstructor:false); + break; + + default: + state.ThrowException(); + break; + } + } + return Value; + } + + /// Creates and returns a string representation of this instance. + /// The result of calling on the . + /// + /// The is null. + /// + public override string ToString() + { + return IsValueCreated ? Value.ToString() : SR.Lazy_ToString_ValueNotCreated; + } + + /// Gets the value of the Lazy<T> for debugging display purposes. + internal T ValueForDebugDisplay + { + get + { + if (!IsValueCreated) + { + return default(T); + } + return _value; + } + } + + /// + /// Gets a value indicating whether this instance may be used concurrently from multiple threads. + /// + internal LazyThreadSafetyMode? Mode => LazyHelper.GetMode(_state); + + /// + /// Gets whether the value creation is faulted or not + /// + internal bool IsValueFaulted => LazyHelper.GetIsValueFaulted(_state); + + /// Gets a value indicating whether the has been initialized. + /// + /// true if the instance has been initialized; + /// otherwise, false. + /// + /// The initialization of a instance may result in either + /// a value being produced or an exception being thrown. If an exception goes unhandled during initialization, + /// will return false. + /// + public bool IsValueCreated => _state == null; + + /// Gets the lazily initialized value of the current . + /// The lazily initialized value of the current . + /// + /// The was initialized to use the default constructor + /// of the type being lazily initialized, and that type does not have a public, parameterless constructor. + /// + /// + /// The was initialized to use the default constructor + /// of the type being lazily initialized, and permissions to access the constructor were missing. + /// + /// + /// The was constructed with the or + /// and the initialization function attempted to access on this instance. + /// + /// + /// If is false, accessing will force initialization. + /// Please for more information on how will behave if an exception is thrown + /// from initialization delegate. + /// + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + public T Value => _state == null ? _value : CreateValue(); + } + + /// A debugger view of the Lazy<T> to surface additional debugging properties and + /// to ensure that the Lazy<T> does not become initialized if it was not already. + internal sealed class LazyDebugView + { + //The Lazy object being viewed. + private readonly Lazy _lazy; + + /// Constructs a new debugger view object for the provided Lazy object. + /// A Lazy object to browse in the debugger. + public LazyDebugView(Lazy lazy) + { + _lazy = lazy; + } + + /// Returns whether the Lazy object is initialized or not. + public bool IsValueCreated + { + get { return _lazy.IsValueCreated; } + } + + /// Returns the value of the Lazy object. + public T Value + { + get + { return _lazy.ValueForDebugDisplay; } + } + + /// Returns the execution mode of the Lazy object + public LazyThreadSafetyMode? Mode + { + get { return _lazy.Mode; } + } + + /// Returns the execution mode of the Lazy object + public bool IsValueFaulted + { + get { return _lazy.IsValueFaulted; } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/MarshalByRefObject.cs b/external/corefx/src/Common/src/CoreLib/System/MarshalByRefObject.cs new file mode 100644 index 0000000000..390b728329 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/MarshalByRefObject.cs @@ -0,0 +1,29 @@ +// 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. + +namespace System +{ + public abstract class MarshalByRefObject + { + protected MarshalByRefObject() + { + } + + public object GetLifetimeService() + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_Remoting); + } + + public virtual object InitializeLifetimeService() + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_Remoting); + } + + protected MarshalByRefObject MemberwiseClone(bool cloneIdentity) + { + MarshalByRefObject mbr = (MarshalByRefObject)base.MemberwiseClone(); + return mbr; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Math.cs b/external/corefx/src/Common/src/CoreLib/System/Math.cs new file mode 100644 index 0000000000..a175103f81 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Math.cs @@ -0,0 +1,825 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: Some floating-point math operations +** +** +===========================================================*/ + +//This class contains only static members and doesn't require serialization. + +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; + +namespace System +{ + public static partial class Math + { + public const double E = 2.7182818284590452354; + + public const double PI = 3.14159265358979323846; + + private const int maxRoundingDigits = 15; + + private static double doubleRoundLimit = 1e16d; + + // This table is required for the Round function which can specify the number of digits to round to + private static double[] roundPower10Double = new double[] { + 1E0, 1E1, 1E2, 1E3, 1E4, 1E5, 1E6, 1E7, 1E8, + 1E9, 1E10, 1E11, 1E12, 1E13, 1E14, 1E15 + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static short Abs(short value) + { + if (value < 0) + { + value = (short)-value; + if (value < 0) + { + ThrowAbsOverflow(); + } + } + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Abs(int value) + { + if (value < 0) + { + value = -value; + if (value < 0) + { + ThrowAbsOverflow(); + } + } + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long Abs(long value) + { + if (value < 0) + { + value = -value; + if (value < 0) + { + ThrowAbsOverflow(); + } + } + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static sbyte Abs(sbyte value) + { + if (value < 0) + { + value = (sbyte)-value; + if (value < 0) + { + ThrowAbsOverflow(); + } + } + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Abs(decimal value) + { + return decimal.Abs(ref value); + } + + [StackTraceHidden] + private static void ThrowAbsOverflow() + { + throw new OverflowException(SR.Overflow_NegateTwosCompNum); + } + + public static long BigMul(int a, int b) + { + return ((long)a) * b; + } + + public static int DivRem(int a, int b, out int result) + { + // TODO https://github.com/dotnet/coreclr/issues/3439: + // Restore to using % and / when the JIT is able to eliminate one of the idivs. + // In the meantime, a * and - is measurably faster than an extra /. + + int div = a / b; + result = a - (div * b); + return div; + } + + public static long DivRem(long a, long b, out long result) + { + // TODO https://github.com/dotnet/coreclr/issues/3439: + // Restore to using % and / when the JIT is able to eliminate one of the idivs. + // In the meantime, a * and - is measurably faster than an extra /. + + long div = a / b; + result = a - (div * b); + return div; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Ceiling(decimal d) + { + return decimal.Ceiling(d); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte Clamp(byte value, byte min, byte max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Clamp(decimal value, decimal min, decimal max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Clamp(double value, double min, double max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static short Clamp(short value, short min, short max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Clamp(int value, int min, int max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long Clamp(long value, long min, long max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static sbyte Clamp(sbyte value, sbyte min, sbyte max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Clamp(float value, float min, float max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort Clamp(ushort value, ushort min, ushort max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static uint Clamp(uint value, uint min, uint max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static ulong Clamp(ulong value, ulong min, ulong max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Floor(decimal d) + { + return decimal.Floor(d); + } + + public static double IEEERemainder(double x, double y) + { + if (double.IsNaN(x)) + { + return x; // IEEE 754-2008: NaN payload must be preserved + } + + if (double.IsNaN(y)) + { + return y; // IEEE 754-2008: NaN payload must be preserved + } + + var regularMod = x % y; + + if (double.IsNaN(regularMod)) + { + return double.NaN; + } + + if ((regularMod == 0) && double.IsNegative(x)) + { + return double.NegativeZero; + } + + var alternativeResult = (regularMod - (Abs(y) * Sign(x))); + + if (Abs(alternativeResult) == Abs(regularMod)) + { + var divisionResult = x / y; + var roundedResult = Round(divisionResult); + + if (Abs(roundedResult) > Abs(divisionResult)) + { + return alternativeResult; + } + else + { + return regularMod; + } + } + + if (Abs(alternativeResult) < Abs(regularMod)) + { + return alternativeResult; + } + else + { + return regularMod; + } + } + + public static double Log(double a, double newBase) + { + if (double.IsNaN(a)) + { + return a; // IEEE 754-2008: NaN payload must be preserved + } + + if (double.IsNaN(newBase)) + { + return newBase; // IEEE 754-2008: NaN payload must be preserved + } + + if (newBase == 1) + { + return double.NaN; + } + + if ((a != 1) && ((newBase == 0) || double.IsPositiveInfinity(newBase))) + { + return double.NaN; + } + + return (Log(a) / Log(newBase)); + } + + [NonVersionable] + public static byte Max(byte val1, byte val2) + { + return (val1 >= val2) ? val1 : val2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Max(decimal val1, decimal val2) + { + return decimal.Max(ref val1, ref val2); + } + + public static double Max(double val1, double val2) + { + if (val1 > val2) + { + return val1; + } + + if (double.IsNaN(val1)) + { + return val1; + } + + return val2; + } + + [NonVersionable] + public static short Max(short val1, short val2) + { + return (val1 >= val2) ? val1 : val2; + } + + [NonVersionable] + public static int Max(int val1, int val2) + { + return (val1 >= val2) ? val1 : val2; + } + + [NonVersionable] + public static long Max(long val1, long val2) + { + return (val1 >= val2) ? val1 : val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static sbyte Max(sbyte val1, sbyte val2) + { + return (val1 >= val2) ? val1 : val2; + } + + public static float Max(float val1, float val2) + { + if (val1 > val2) + { + return val1; + } + + if (float.IsNaN(val1)) + { + return val1; + } + + return val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static ushort Max(ushort val1, ushort val2) + { + return (val1 >= val2) ? val1 : val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static uint Max(uint val1, uint val2) + { + return (val1 >= val2) ? val1 : val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static ulong Max(ulong val1, ulong val2) + { + return (val1 >= val2) ? val1 : val2; + } + + [NonVersionable] + public static byte Min(byte val1, byte val2) + { + return (val1 <= val2) ? val1 : val2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Min(decimal val1, decimal val2) + { + return decimal.Min(ref val1, ref val2); + } + + public static double Min(double val1, double val2) + { + if (val1 < val2) + { + return val1; + } + + if (double.IsNaN(val1)) + { + return val1; + } + + return val2; + } + + [NonVersionable] + public static short Min(short val1, short val2) + { + return (val1 <= val2) ? val1 : val2; + } + + [NonVersionable] + public static int Min(int val1, int val2) + { + return (val1 <= val2) ? val1 : val2; + } + + [NonVersionable] + public static long Min(long val1, long val2) + { + return (val1 <= val2) ? val1 : val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static sbyte Min(sbyte val1, sbyte val2) + { + return (val1 <= val2) ? val1 : val2; + } + + public static float Min(float val1, float val2) + { + if (val1 < val2) + { + return val1; + } + + if (float.IsNaN(val1)) + { + return val1; + } + + return val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static ushort Min(ushort val1, ushort val2) + { + return (val1 <= val2) ? val1 : val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static uint Min(uint val1, uint val2) + { + return (val1 <= val2) ? val1 : val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static ulong Min(ulong val1, ulong val2) + { + return (val1 <= val2) ? val1 : val2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Round(decimal d) + { + return decimal.Round(d, 0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Round(decimal d, int decimals) + { + return decimal.Round(d, decimals); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Round(decimal d, MidpointRounding mode) + { + return decimal.Round(d, 0, mode); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Round(decimal d, int decimals, MidpointRounding mode) + { + return decimal.Round(d, decimals, mode); + } + + [Intrinsic] + public static double Round(double a) + { + // ************************************************************************************ + // IMPORTANT: Do not change this implementation without also updating Math.Round(double), + // FloatingPointUtils::round(double), and FloatingPointUtils::round(float) + // ************************************************************************************ + + // If the number has no fractional part do nothing + // This shortcut is necessary to workaround precision loss in borderline cases on some platforms + + if (a == (double)((long)a)) + { + return a; + } + + // We had a number that was equally close to 2 integers. + // We need to return the even one. + + double flrTempVal = Floor(a + 0.5); + + if ((a == (Floor(a) + 0.5)) && (FMod(flrTempVal, 2.0) != 0)) + { + flrTempVal -= 1.0; + } + + return copysign(flrTempVal, a); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Round(double value, int digits) + { + return Round(value, digits, MidpointRounding.ToEven); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Round(double value, MidpointRounding mode) + { + return Round(value, 0, mode); + } + + public static unsafe double Round(double value, int digits, MidpointRounding mode) + { + if ((digits < 0) || (digits > maxRoundingDigits)) + { + throw new ArgumentOutOfRangeException(nameof(digits), SR.ArgumentOutOfRange_RoundingDigits); + } + + if (mode < MidpointRounding.ToEven || mode > MidpointRounding.AwayFromZero) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidEnumValue, mode, nameof(MidpointRounding)), nameof(mode)); + } + + if (Abs(value) < doubleRoundLimit) + { + var power10 = roundPower10Double[digits]; + + value *= power10; + + if (mode == MidpointRounding.AwayFromZero) + { + var fraction = ModF(value, &value); + + if (Abs(fraction) >= 0.5) + { + value += Sign(fraction); + } + } + else + { + value = Round(value); + } + + value /= power10; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Sign(decimal value) + { + return decimal.Sign(ref value); + } + + public static int Sign(double value) + { + if (value < 0) + { + return -1; + } + else if (value > 0) + { + return 1; + } + else if (value == 0) + { + return 0; + } + + throw new ArithmeticException(SR.Arithmetic_NaN); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Sign(short value) + { + return Sign((int)value); + } + + public static int Sign(int value) + { + return unchecked(value >> 31 | (int)((uint)-value >> 31)); + } + + public static int Sign(long value) + { + return unchecked((int)(value >> 63 | (long)((ulong)-value >> 63))); + } + + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Sign(sbyte value) + { + return Sign((int)value); + } + + public static int Sign(float value) + { + if (value < 0) + { + return -1; + } + else if (value > 0) + { + return 1; + } + else if (value == 0) + { + return 0; + } + + throw new ArithmeticException(SR.Arithmetic_NaN); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Truncate(decimal d) + { + return decimal.Truncate(d); + } + + public static unsafe double Truncate(double d) + { + ModF(d, &d); + return d; + } + + private static unsafe double copysign(double x, double y) + { + var xbits = BitConverter.DoubleToInt64Bits(x); + var ybits = BitConverter.DoubleToInt64Bits(y); + + // If the sign bits of x and y are not the same, + // flip the sign bit of x and return the new value; + // otherwise, just return x + + if (((xbits ^ ybits) >> 63) != 0) + { + return BitConverter.Int64BitsToDouble(xbits ^ long.MinValue); + } + + return x; + } + + private static void ThrowMinMaxException(T min, T max) + { + throw new ArgumentException(SR.Format(SR.Argument_MinMaxValue, min, max)); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/MathF.cs b/external/corefx/src/Common/src/CoreLib/System/MathF.cs new file mode 100644 index 0000000000..5b7e48b062 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/MathF.cs @@ -0,0 +1,235 @@ +// 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. + +/*============================================================ +** +** Purpose: Some single-precision floating-point math operations +** +===========================================================*/ + +//This class contains only static members and doesn't require serialization. + +using System.Runtime; +using System.Runtime.CompilerServices; + +namespace System +{ + public static partial class MathF + { + public const float E = 2.71828183f; + + public const float PI = 3.14159265f; + + private const int maxRoundingDigits = 6; + + // This table is required for the Round function which can specify the number of digits to round to + private static float[] roundPower10Single = new float[] { + 1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f, 1e6f + }; + + private static float singleRoundLimit = 1e8f; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Abs(float x) + { + return Math.Abs(x); + } + + public static float IEEERemainder(float x, float y) + { + if (float.IsNaN(x)) + { + return x; // IEEE 754-2008: NaN payload must be preserved + } + + if (float.IsNaN(y)) + { + return y; // IEEE 754-2008: NaN payload must be preserved + } + + var regularMod = x % y; + + if (float.IsNaN(regularMod)) + { + return float.NaN; + } + + if ((regularMod == 0) && float.IsNegative(x)) + { + return float.NegativeZero; + } + + var alternativeResult = (regularMod - (Abs(y) * Sign(x))); + + if (Abs(alternativeResult) == Abs(regularMod)) + { + var divisionResult = x / y; + var roundedResult = Round(divisionResult); + + if (Abs(roundedResult) > Abs(divisionResult)) + { + return alternativeResult; + } + else + { + return regularMod; + } + } + + if (Abs(alternativeResult) < Abs(regularMod)) + { + return alternativeResult; + } + else + { + return regularMod; + } + } + + public static float Log(float x, float y) + { + if (float.IsNaN(x)) + { + return x; // IEEE 754-2008: NaN payload must be preserved + } + + if (float.IsNaN(y)) + { + return y; // IEEE 754-2008: NaN payload must be preserved + } + + if (y == 1) + { + return float.NaN; + } + + if ((x != 1) && ((y == 0) || float.IsPositiveInfinity(y))) + { + return float.NaN; + } + + return Log(x) / Log(y); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Max(float x, float y) + { + return Math.Max(x, y); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Min(float x, float y) + { + return Math.Min(x, y); + } + + [Intrinsic] + public static float Round(float x) + { + // ************************************************************************************ + // IMPORTANT: Do not change this implementation without also updating Math.Round(double), + // FloatingPointUtils::round(double), and FloatingPointUtils::round(float) + // ************************************************************************************ + + // If the number has no fractional part do nothing + // This shortcut is necessary to workaround precision loss in borderline cases on some platforms + + if (x == (float)((int)x)) + { + return x; + } + + // We had a number that was equally close to 2 integers. + // We need to return the even one. + + float flrTempVal = Floor(x + 0.5f); + + if ((x == (Floor(x) + 0.5f)) && (FMod(flrTempVal, 2.0f) != 0)) + { + flrTempVal -= 1.0f; + } + + return CopySign(flrTempVal, x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Round(float x, int digits) + { + return Round(x, digits, MidpointRounding.ToEven); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Round(float x, MidpointRounding mode) + { + return Round(x, 0, mode); + } + + public static unsafe float Round(float x, int digits, MidpointRounding mode) + { + if ((digits < 0) || (digits > maxRoundingDigits)) + { + throw new ArgumentOutOfRangeException(nameof(digits), SR.ArgumentOutOfRange_RoundingDigits); + } + + if (mode < MidpointRounding.ToEven || mode > MidpointRounding.AwayFromZero) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidEnum, mode, nameof(MidpointRounding)), nameof(mode)); + } + + if (Abs(x) < singleRoundLimit) + { + var power10 = roundPower10Single[digits]; + + x *= power10; + + if (mode == MidpointRounding.AwayFromZero) + { + var fraction = ModF(x, &x); + + if (Abs(fraction) >= 0.5f) + { + x += Sign(fraction); + } + } + else + { + x = Round(x); + } + + x /= power10; + } + + return x; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Sign(float x) + { + return Math.Sign(x); + } + + public static unsafe float Truncate(float x) + { + ModF(x, &x); + return x; + } + + private static unsafe float CopySign(float x, float y) + { + var xbits = BitConverter.SingleToInt32Bits(x); + var ybits = BitConverter.SingleToInt32Bits(y); + + // If the sign bits of x and y are not the same, + // flip the sign bit of x and return the new value; + // otherwise, just return x + + if (((xbits ^ ybits) >> 31) != 0) + { + return BitConverter.Int32BitsToSingle(xbits ^ int.MinValue); + } + + return x; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/MemberAccessException.cs b/external/corefx/src/Common/src/CoreLib/System/MemberAccessException.cs new file mode 100644 index 0000000000..dfea52dbed --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/MemberAccessException.cs @@ -0,0 +1,50 @@ +// 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. + +//////////////////////////////////////////////////////////////////////////////// +// MemberAccessException +// Thrown when we try accessing a member that we cannot +// access, due to it being removed, private or something similar. +//////////////////////////////////////////////////////////////////////////////// + +using System.Runtime.Serialization; + +namespace System +{ + // The MemberAccessException is thrown when trying to access a class + // member fails. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class MemberAccessException : SystemException + { + // Creates a new MemberAccessException with its message string set to + // the empty string, its HRESULT set to COR_E_MEMBERACCESS, + // and its ExceptionInfo reference set to null. + public MemberAccessException() + : base(SR.Arg_AccessException) + { + HResult = HResults.COR_E_MEMBERACCESS; + } + + // Creates a new MemberAccessException with its message string set to + // message, its HRESULT set to COR_E_ACCESS, + // and its ExceptionInfo reference set to null. + // + public MemberAccessException(String message) + : base(message) + { + HResult = HResults.COR_E_MEMBERACCESS; + } + + public MemberAccessException(String message, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_MEMBERACCESS; + } + + protected MemberAccessException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Memory.cs b/external/corefx/src/Common/src/CoreLib/System/Memory.cs new file mode 100644 index 0000000000..40c4cd9e53 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Memory.cs @@ -0,0 +1,376 @@ +// 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.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +#if !MONO +using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute; +using EditorBrowsableState = System.ComponentModel.EditorBrowsableState; +#endif +#if !FEATURE_PORTABLE_SPAN +using Internal.Runtime.CompilerServices; +#endif // FEATURE_PORTABLE_SPAN + +namespace System +{ + /// + /// Memory represents a contiguous region of arbitrary memory similar to . + /// Unlike , it is not a byref-like type. + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + [DebuggerTypeProxy(typeof(MemoryDebugView<>))] + public readonly struct Memory + { + // NOTE: With the current implementation, Memory and ReadOnlyMemory must have the same layout, + // as code uses Unsafe.As to cast between them. + + // The highest order bit of _index is used to discern whether _object is an array/string or an owned memory + // if (_index >> 31) == 1, object _object is an OwnedMemory + // else, object _object is a T[] or a string. It can only be a string if the Memory was created by + // using unsafe / marshaling code to reinterpret a ReadOnlyMemory wrapped around a string as + // a Memory. + private readonly object _object; + private readonly int _index; + private readonly int _length; + + private const int RemoveOwnedFlagBitMask = 0x7FFFFFFF; + + /// + /// Creates a new memory over the entirety of the target array. + /// + /// The target array. + /// Thrown when is a null + /// reference (Nothing in Visual Basic). + /// Thrown when is covariant and array's type is not exactly T[]. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Memory(T[] array) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + if (default(T) == null && array.GetType() != typeof(T[])) + ThrowHelper.ThrowArrayTypeMismatchException(); + + _object = array; + _index = 0; + _length = array.Length; + } + + /// + /// Creates a new memory over the portion of the target array beginning + /// at 'start' index and ending at 'end' index (exclusive). + /// + /// The target array. + /// The index at which to begin the memory. + /// The number of items in the memory. + /// Thrown when is a null + /// reference (Nothing in Visual Basic). + /// Thrown when is covariant and array's type is not exactly T[]. + /// + /// Thrown when the specified or end index is not in the range (<0 or >=Length). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Memory(T[] array, int start, int length) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + if (default(T) == null && array.GetType() != typeof(T[])) + ThrowHelper.ThrowArrayTypeMismatchException(); + if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + _object = array; + _index = start; + _length = length; + } + + // Constructor for internal use only. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Memory(OwnedMemory owner, int index, int length) + { + // No validation performed; caller must provide any necessary validation. + _object = owner; + _index = index | (1 << 31); // Before using _index, check if _index < 0, then 'and' it with RemoveOwnedFlagBitMask + _length = length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Memory(object obj, int index, int length) + { + // No validation performed; caller must provide any necessary validation. + _object = obj; + _index = index; + _length = length; + } + + /// + /// Defines an implicit conversion of an array to a + /// + public static implicit operator Memory(T[] array) => (array != null) ? new Memory(array) : default; + + /// + /// Defines an implicit conversion of a to a + /// + public static implicit operator Memory(ArraySegment arraySegment) => new Memory(arraySegment.Array, arraySegment.Offset, arraySegment.Count); + + /// + /// Defines an implicit conversion of a to a + /// + public static implicit operator ReadOnlyMemory(Memory memory) => + Unsafe.As, ReadOnlyMemory>(ref memory); + + //Debugger Display = {T[length]} + private string DebuggerDisplay => string.Format("{{{0}[{1}]}}", typeof(T).Name, _length); + + /// + /// Returns an empty + /// + public static Memory Empty => default; + + /// + /// The number of items in the memory. + /// + public int Length => _length; + + /// + /// Returns true if Length is 0. + /// + public bool IsEmpty => _length == 0; + + /// + /// Forms a slice out of the given memory, beginning at 'start'. + /// + /// The index at which to begin this slice. + /// + /// Thrown when the specified index is not in range (<0 or >=Length). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Memory Slice(int start) + { + if ((uint)start > (uint)_length) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); + } + + return new Memory(_object, _index + start, _length - start); + } + + /// + /// Forms a slice out of the given memory, beginning at 'start', of given length + /// + /// The index at which to begin this slice. + /// The desired length for the slice (exclusive). + /// + /// Thrown when the specified or end index is not in range (<0 or >=Length). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Memory Slice(int start, int length) + { + if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start)) + { + ThrowHelper.ThrowArgumentOutOfRangeException(); + } + + return new Memory(_object, _index + start, length); + } + + /// + /// Returns a span from the memory. + /// + public Span Span + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + if (_index < 0) + { + return ((OwnedMemory)_object).Span.Slice(_index & RemoveOwnedFlagBitMask, _length); + } + else if (typeof(T) == typeof(char) && _object is string s) + { + // This is dangerous, returning a writable span for a string that should be immutable. + // However, we need to handle the case where a ReadOnlyMemory was created from a string + // and then cast to a Memory. Such a cast can only be done with unsafe or marshaling code, + // in which case that's the dangerous operation performed by the dev, and we're just following + // suit here to make it work as best as possible. +#if FEATURE_PORTABLE_SPAN + return new Span(Unsafe.As>(s), MemoryExtensions.StringAdjustment, s.Length).Slice(_index, _length); +#else + return new Span(ref Unsafe.As(ref s.GetRawStringData()), s.Length).Slice(_index, _length); +#endif // FEATURE_PORTABLE_SPAN + } + else if (_object != null) + { + return new Span((T[])_object, _index, _length); + } + else + { + return default; + } + } + } + + /// + /// Copies the contents of the memory into the destination. If the source + /// and destination overlap, this method behaves as if the original values are in + /// a temporary location before the destination is overwritten. + /// + /// The Memory to copy items into. + /// + /// Thrown when the destination is shorter than the source. + /// + /// + public void CopyTo(Memory destination) => Span.CopyTo(destination.Span); + + /// + /// Copies the contents of the memory into the destination. If the source + /// and destination overlap, this method behaves as if the original values are in + /// a temporary location before the destination is overwritten. + /// + /// If the destination is shorter than the source, this method + /// return false and no data is written to the destination. + /// + /// The span to copy items into. + public bool TryCopyTo(Memory destination) => Span.TryCopyTo(destination.Span); + + /// + /// Returns a handle for the array. + /// If pin is true, the GC will not move the array and hence its address can be taken + /// + public unsafe MemoryHandle Retain(bool pin = false) + { + MemoryHandle memoryHandle = default; + if (pin) + { + if (_index < 0) + { + memoryHandle = ((OwnedMemory)_object).Pin((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf()); + } + else if (typeof(T) == typeof(char) && _object is string s) + { + // This case can only happen if a ReadOnlyMemory was created around a string + // and then that was cast to a Memory using unsafe / marshaling code. This needs + // to work, however, so that code that uses a single Memory field to store either + // a readable ReadOnlyMemory or a writable Memory can still be pinned and + // used for interop purposes. + GCHandle handle = GCHandle.Alloc(s, GCHandleType.Pinned); +#if FEATURE_PORTABLE_SPAN + void* pointer = Unsafe.Add((void*)handle.AddrOfPinnedObject(), _index); +#else + void* pointer = Unsafe.Add(Unsafe.AsPointer(ref s.GetRawStringData()), _index); +#endif // FEATURE_PORTABLE_SPAN + memoryHandle = new MemoryHandle(null, pointer, handle); + } + else if (_object is T[] array) + { + var handle = GCHandle.Alloc(array, GCHandleType.Pinned); +#if FEATURE_PORTABLE_SPAN + void* pointer = Unsafe.Add((void*)handle.AddrOfPinnedObject(), _index); +#else + void* pointer = Unsafe.Add(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index); +#endif // FEATURE_PORTABLE_SPAN + memoryHandle = new MemoryHandle(null, pointer, handle); + } + } + else + { + if (_index < 0) + { + ((OwnedMemory)_object).Retain(); + memoryHandle = new MemoryHandle((OwnedMemory)_object); + } + } + return memoryHandle; + } + + /// + /// Get an array segment from the underlying memory. + /// If unable to get the array segment, return false with a default array segment. + /// + public bool TryGetArray(out ArraySegment arraySegment) + { + if (_index < 0) + { + if (((OwnedMemory)_object).TryGetArray(out var segment)) + { + arraySegment = new ArraySegment(segment.Array, segment.Offset + (_index & RemoveOwnedFlagBitMask), _length); + return true; + } + } + else if (_object is T[] arr) + { + arraySegment = new ArraySegment(arr, _index, _length); + return true; + } + + arraySegment = default(ArraySegment); + return false; + } + + /// + /// Copies the contents from the memory into a new array. This heap + /// allocates, so should generally be avoided, however it is sometimes + /// necessary to bridge the gap with APIs written in terms of arrays. + /// + public T[] ToArray() => Span.ToArray(); + + /// + /// Determines whether the specified object is equal to the current object. + /// Returns true if the object is Memory or ReadOnlyMemory and if both objects point to the same array and have the same length. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public override bool Equals(object obj) + { + if (obj is ReadOnlyMemory) + { + return ((ReadOnlyMemory)obj).Equals(this); + } + else if (obj is Memory memory) + { + return Equals(memory); + } + else + { + return false; + } + } + + /// + /// Returns true if the memory points to the same array and has the same length. Note that + /// this does *not* check to see if the *contents* are equal. + /// + public bool Equals(Memory other) + { + return + _object == other._object && + _index == other._index && + _length == other._length; + } + + /// + /// Serves as the default hash function. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public override int GetHashCode() + { + return _object != null ? CombineHashCodes(_object.GetHashCode(), _index.GetHashCode(), _length.GetHashCode()) : 0; + } + + private static int CombineHashCodes(int left, int right) + { + return ((left << 5) + left) ^ right; + } + + private static int CombineHashCodes(int h1, int h2, int h3) + { + return CombineHashCodes(CombineHashCodes(h1, h2), h3); + } + + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/MemoryDebugView.cs b/external/corefx/src/Common/src/CoreLib/System/MemoryDebugView.cs new file mode 100644 index 0000000000..fa508b286f --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/MemoryDebugView.cs @@ -0,0 +1,52 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; + +namespace System +{ + internal sealed class MemoryDebugView + { + private readonly ReadOnlyMemory _memory; + + public MemoryDebugView(Memory memory) + { + _memory = memory; + } + + public MemoryDebugView(ReadOnlyMemory memory) + { + _memory = memory; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public T[] Items + { + // This is a work around since we cannot use _memory.ToArray() due to + // https://devdiv.visualstudio.com/DevDiv/_workitems?id=286592 + get + { + if (MemoryMarshal.TryGetArray(_memory, out ArraySegment segment)) + { + T[] array = new T[_memory.Length]; + Array.Copy(segment.Array, segment.Offset, array, 0, array.Length); + return array; + } + + if (typeof(T) == typeof(char) && + ((ReadOnlyMemory)(object)_memory).TryGetString(out string text, out int start, out int length)) + { + return (T[])(object)text.Substring(start, length).ToCharArray(); + } + +#if FEATURE_PORTABLE_SPAN + return SpanHelpers.PerTypeValues.EmptyArray; +#else + return Array.Empty(); +#endif // FEATURE_PORTABLE_SPAN + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/MethodAccessException.cs b/external/corefx/src/Common/src/CoreLib/System/MethodAccessException.cs new file mode 100644 index 0000000000..1ca0297b94 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/MethodAccessException.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. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** Purpose: The exception class for class loading failures. +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class MethodAccessException : MemberAccessException + { + public MethodAccessException() + : base(SR.Arg_MethodAccessException) + { + HResult = HResults.COR_E_METHODACCESS; + } + + public MethodAccessException(String message) + : base(message) + { + HResult = HResults.COR_E_METHODACCESS; + } + + public MethodAccessException(String message, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_METHODACCESS; + } + + protected MethodAccessException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/MidpointRounding.cs b/external/corefx/src/Common/src/CoreLib/System/MidpointRounding.cs new file mode 100644 index 0000000000..a75de43e65 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/MidpointRounding.cs @@ -0,0 +1,12 @@ +// 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. + +namespace System +{ + public enum MidpointRounding + { + ToEven = 0, + AwayFromZero = 1, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/MissingMethodException.cs b/external/corefx/src/Common/src/CoreLib/System/MissingMethodException.cs new file mode 100644 index 0000000000..abb6c0e97b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/MissingMethodException.cs @@ -0,0 +1,61 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: The exception class for class loading failures. +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class MissingMethodException : MissingMemberException + { + public MissingMethodException() + : base(SR.Arg_MissingMethodException) + { + HResult = HResults.COR_E_MISSINGMETHOD; + } + + public MissingMethodException(string message) + : base(message) + { + HResult = HResults.COR_E_MISSINGMETHOD; + } + + public MissingMethodException(string message, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_MISSINGMETHOD; + } + + public MissingMethodException(string className, string methodName) + { + ClassName = className; + MemberName = methodName; + } + + protected MissingMethodException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + public override string Message + { + get + { + return ClassName == null ? base.Message : + SR.Format(SR.MissingMethod_Name, ClassName + "." + MemberName + + (Signature != null ? " " + FormatSignature(Signature) : string.Empty)); + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/MulticastNotSupportedException.cs b/external/corefx/src/Common/src/CoreLib/System/MulticastNotSupportedException.cs new file mode 100644 index 0000000000..cc6c77023e --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/MulticastNotSupportedException.cs @@ -0,0 +1,40 @@ +// 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. + +//////////////////////////////////////////////////////////////////////////////// +// MulticastNotSupportedException +// This is thrown when you add multiple callbacks to a non-multicast delegate. +//////////////////////////////////////////////////////////////////////////////// + +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class MulticastNotSupportedException : SystemException + { + public MulticastNotSupportedException() + : base(SR.Arg_MulticastNotSupportedException) + { + HResult = HResults.COR_E_MULTICASTNOTSUPPORTED; + } + + public MulticastNotSupportedException(String message) + : base(message) + { + HResult = HResults.COR_E_MULTICASTNOTSUPPORTED; + } + + public MulticastNotSupportedException(String message, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_MULTICASTNOTSUPPORTED; + } + + internal MulticastNotSupportedException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/NonSerializedAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/NonSerializedAttribute.cs similarity index 100% rename from external/corert/src/System.Private.CoreLib/src/System/NonSerializedAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/NonSerializedAttribute.cs diff --git a/external/corefx/src/Common/src/CoreLib/System/NotFiniteNumberException.cs b/external/corefx/src/Common/src/CoreLib/System/NotFiniteNumberException.cs new file mode 100644 index 0000000000..b9c9af06d3 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/NotFiniteNumberException.cs @@ -0,0 +1,72 @@ +// 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.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class NotFiniteNumberException : ArithmeticException + { + private double _offendingNumber; + + public NotFiniteNumberException() + : base(SR.Arg_NotFiniteNumberException) + { + _offendingNumber = 0; + HResult = HResults.COR_E_NOTFINITENUMBER; + } + + public NotFiniteNumberException(double offendingNumber) + : base() + { + _offendingNumber = offendingNumber; + HResult = HResults.COR_E_NOTFINITENUMBER; + } + + public NotFiniteNumberException(string message) + : base(message) + { + _offendingNumber = 0; + HResult = HResults.COR_E_NOTFINITENUMBER; + } + + public NotFiniteNumberException(string message, double offendingNumber) + : base(message) + { + _offendingNumber = offendingNumber; + HResult = HResults.COR_E_NOTFINITENUMBER; + } + + public NotFiniteNumberException(string message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_NOTFINITENUMBER; + } + + public NotFiniteNumberException(string message, double offendingNumber, Exception innerException) + : base(message, innerException) + { + _offendingNumber = offendingNumber; + HResult = HResults.COR_E_NOTFINITENUMBER; + } + + protected NotFiniteNumberException(SerializationInfo info, StreamingContext context) : base(info, context) + { + _offendingNumber = info.GetInt32("OffendingNumber"); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("OffendingNumber", _offendingNumber, typeof(int)); + } + + public double OffendingNumber + { + get { return _offendingNumber; } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/NotImplementedException.cs b/external/corefx/src/Common/src/CoreLib/System/NotImplementedException.cs new file mode 100644 index 0000000000..1a3b6afcd4 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/NotImplementedException.cs @@ -0,0 +1,43 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: Exception thrown when a requested method or operation is not +** implemented. +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class NotImplementedException : SystemException + { + public NotImplementedException() + : base(SR.Arg_NotImplementedException) + { + HResult = HResults.E_NOTIMPL; + } + public NotImplementedException(String message) + : base(message) + { + HResult = HResults.E_NOTIMPL; + } + public NotImplementedException(String message, Exception inner) + : base(message, inner) + { + HResult = HResults.E_NOTIMPL; + } + + protected NotImplementedException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/NotSupportedException.cs b/external/corefx/src/Common/src/CoreLib/System/NotSupportedException.cs new file mode 100644 index 0000000000..3180bc2837 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/NotSupportedException.cs @@ -0,0 +1,44 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: For methods that should be implemented on subclasses. +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class NotSupportedException : SystemException + { + public NotSupportedException() + : base(SR.Arg_NotSupportedException) + { + HResult = HResults.COR_E_NOTSUPPORTED; + } + + public NotSupportedException(String message) + : base(message) + { + HResult = HResults.COR_E_NOTSUPPORTED; + } + + public NotSupportedException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_NOTSUPPORTED; + } + + protected NotSupportedException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/NullReferenceException.cs b/external/corefx/src/Common/src/CoreLib/System/NullReferenceException.cs new file mode 100644 index 0000000000..c2e722470c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/NullReferenceException.cs @@ -0,0 +1,44 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: Exception class for dereferencing a null reference. +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class NullReferenceException : SystemException + { + public NullReferenceException() + : base(SR.Arg_NullReferenceException) + { + HResult = HResults.COR_E_NULLREFERENCE; + } + + public NullReferenceException(String message) + : base(message) + { + HResult = HResults.COR_E_NULLREFERENCE; + } + + public NullReferenceException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_NULLREFERENCE; + } + + protected NullReferenceException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Nullable.cs b/external/corefx/src/Common/src/CoreLib/System/Nullable.cs similarity index 53% rename from external/corert/src/System.Private.CoreLib/src/System/Nullable.cs rename to external/corefx/src/Common/src/CoreLib/System/Nullable.cs index 4e30531ebc..73ad6056c2 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Nullable.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Nullable.cs @@ -2,22 +2,24 @@ // 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.Diagnostics.Contracts; -using System.Globalization; using System.Collections.Generic; -using System.Runtime; -using System.Runtime.CompilerServices; -using Internal.Reflection.Core.NonPortable; +using System.Runtime.Versioning; namespace System { + // Because we have special type system support that says a a boxed Nullable + // can be used where a boxed is use, Nullable can not implement any intefaces + // at all (since T may not). Do NOT add any interfaces to Nullable! + // + [Serializable] + [NonVersionable] // This only applies to field layout + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public struct Nullable where T : struct { - // Changing the name of this field will break MDbg and Debugger tests - private bool hasValue; - internal T value; + private readonly bool hasValue; // Do not rename (binary serialization) + internal T value; // Do not rename (binary serialization) or make readonly (can be mutated in ToString, etc.) + [NonVersionable] public Nullable(T value) { this.value = value; @@ -26,51 +28,61 @@ namespace System public bool HasValue { - get { return hasValue; } + [NonVersionable] + get + { + return hasValue; + } } public T Value { get { - if (!HasValue) - throw new InvalidOperationException(SR.InvalidOperation_NoValue); + if (!hasValue) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_NoValue(); + } return value; } } + [NonVersionable] public T GetValueOrDefault() { return value; } + [NonVersionable] public T GetValueOrDefault(T defaultValue) { - return HasValue ? value : defaultValue; + return hasValue ? value : defaultValue; } public override bool Equals(object other) { - if (!HasValue) return other == null; + if (!hasValue) return other == null; if (other == null) return false; return value.Equals(other); } public override int GetHashCode() { - return HasValue ? value.GetHashCode() : 0; + return hasValue ? value.GetHashCode() : 0; } public override string ToString() { - return HasValue ? value.ToString() : ""; + return hasValue ? value.ToString() : ""; } + [NonVersionable] public static implicit operator Nullable(T value) { return new Nullable(value); } + [NonVersionable] public static explicit operator T(Nullable value) { return value.Value; @@ -101,36 +113,40 @@ namespace System return true; } + // If the type provided is not a Nullable Type, return null. + // Otherwise, returns the underlying type of the Nullable type public static Type GetUnderlyingType(Type nullableType) { if ((object)nullableType == null) { throw new ArgumentNullException(nameof(nullableType)); } - Contract.EndContractBlock(); - Type result = null; - - EETypePtr nullableEEType; - if (nullableType.TryGetEEType(out nullableEEType)) +#if CORERT + // This is necessary to handle types without reflection metadata + if (nullableType.TryGetEEType(out EETypePtr nullableEEType)) { if (nullableEEType.IsGeneric) { if (nullableEEType.IsNullable) { - EETypePtr underlyingEEType = nullableEEType.NullableType; - result = ReflectionCoreNonPortable.GetRuntimeTypeForEEType(underlyingEEType); + return Internal.Reflection.Core.NonPortable.RuntimeTypeUnifier.GetRuntimeTypeForEEType(nullableEEType.NullableType); } } + return null; } - else +#endif + + if (nullableType.IsGenericType && !nullableType.IsGenericTypeDefinition) { - // If we got here, the type was not statically bound in the image. However, it may still be a browsable metadata-based type. - // Fall back to using Reflection for these. - if (nullableType.IsConstructedGenericType && nullableType.GetGenericTypeDefinition() == typeof(Nullable<>)) - result = nullableType.GenericTypeArguments[0]; + // instantiated generic type only + Type genericType = nullableType.GetGenericTypeDefinition(); + if (Object.ReferenceEquals(genericType, typeof(Nullable<>))) + { + return nullableType.GetGenericArguments()[0]; + } } - return result; + return null; } } } diff --git a/external/corefx/src/Common/src/CoreLib/System/Number.Formatting.cs b/external/corefx/src/Common/src/CoreLib/System/Number.Formatting.cs new file mode 100644 index 0000000000..70b35a08aa --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Number.Formatting.cs @@ -0,0 +1,2237 @@ +// 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.Diagnostics; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace System +{ + // The Format methods provided by the numeric classes convert + // the numeric value to a string using the format string given by the + // format parameter. If the format parameter is null or + // an empty string, the number is formatted as if the string "G" (general + // format) was specified. The info parameter specifies the + // NumberFormatInfo instance to use when formatting the number. If the + // info parameter is null or omitted, the numeric formatting information + // is obtained from the current culture. The NumberFormatInfo supplies + // such information as the characters to use for decimal and thousand + // separators, and the spelling and placement of currency symbols in monetary + // values. + // + // Format strings fall into two categories: Standard format strings and + // user-defined format strings. A format string consisting of a single + // alphabetic character (A-Z or a-z), optionally followed by a sequence of + // digits (0-9), is a standard format string. All other format strings are + // used-defined format strings. + // + // A standard format string takes the form Axx, where A is an + // alphabetic character called the format specifier and xx is a + // sequence of digits called the precision specifier. The format + // specifier controls the type of formatting applied to the number and the + // precision specifier controls the number of significant digits or decimal + // places of the formatting operation. The following table describes the + // supported standard formats. + // + // C c - Currency format. The number is + // converted to a string that represents a currency amount. The conversion is + // controlled by the currency format information of the NumberFormatInfo + // used to format the number. The precision specifier indicates the desired + // number of decimal places. If the precision specifier is omitted, the default + // currency precision given by the NumberFormatInfo is used. + // + // D d - Decimal format. This format is + // supported for integral types only. The number is converted to a string of + // decimal digits, prefixed by a minus sign if the number is negative. The + // precision specifier indicates the minimum number of digits desired in the + // resulting string. If required, the number will be left-padded with zeros to + // produce the number of digits given by the precision specifier. + // + // E e Engineering (scientific) format. + // The number is converted to a string of the form + // "-d.ddd...E+ddd" or "-d.ddd...e+ddd", where each + // 'd' indicates a digit (0-9). The string starts with a minus sign if the + // number is negative, and one digit always precedes the decimal point. The + // precision specifier indicates the desired number of digits after the decimal + // point. If the precision specifier is omitted, a default of 6 digits after + // the decimal point is used. The format specifier indicates whether to prefix + // the exponent with an 'E' or an 'e'. The exponent is always consists of a + // plus or minus sign and three digits. + // + // F f Fixed point format. The number is + // converted to a string of the form "-ddd.ddd....", where each + // 'd' indicates a digit (0-9). The string starts with a minus sign if the + // number is negative. The precision specifier indicates the desired number of + // decimal places. If the precision specifier is omitted, the default numeric + // precision given by the NumberFormatInfo is used. + // + // G g - General format. The number is + // converted to the shortest possible decimal representation using fixed point + // or scientific format. The precision specifier determines the number of + // significant digits in the resulting string. If the precision specifier is + // omitted, the number of significant digits is determined by the type of the + // number being converted (10 for int, 19 for long, 7 for + // float, 15 for double, 19 for Currency, and 29 for + // Decimal). Trailing zeros after the decimal point are removed, and the + // resulting string contains a decimal point only if required. The resulting + // string uses fixed point format if the exponent of the number is less than + // the number of significant digits and greater than or equal to -4. Otherwise, + // the resulting string uses scientific format, and the case of the format + // specifier controls whether the exponent is prefixed with an 'E' or an 'e'. + // + // N n Number format. The number is + // converted to a string of the form "-d,ddd,ddd.ddd....", where + // each 'd' indicates a digit (0-9). The string starts with a minus sign if the + // number is negative. Thousand separators are inserted between each group of + // three digits to the left of the decimal point. The precision specifier + // indicates the desired number of decimal places. If the precision specifier + // is omitted, the default numeric precision given by the + // NumberFormatInfo is used. + // + // X x - Hexadecimal format. This format is + // supported for integral types only. The number is converted to a string of + // hexadecimal digits. The format specifier indicates whether to use upper or + // lower case characters for the hexadecimal digits above 9 ('X' for 'ABCDEF', + // and 'x' for 'abcdef'). The precision specifier indicates the minimum number + // of digits desired in the resulting string. If required, the number will be + // left-padded with zeros to produce the number of digits given by the + // precision specifier. + // + // Some examples of standard format strings and their results are shown in the + // table below. (The examples all assume a default NumberFormatInfo.) + // + // Value Format Result + // 12345.6789 C $12,345.68 + // -12345.6789 C ($12,345.68) + // 12345 D 12345 + // 12345 D8 00012345 + // 12345.6789 E 1.234568E+004 + // 12345.6789 E10 1.2345678900E+004 + // 12345.6789 e4 1.2346e+004 + // 12345.6789 F 12345.68 + // 12345.6789 F0 12346 + // 12345.6789 F6 12345.678900 + // 12345.6789 G 12345.6789 + // 12345.6789 G7 12345.68 + // 123456789 G7 1.234568E8 + // 12345.6789 N 12,345.68 + // 123456789 N4 123,456,789.0000 + // 0x2c45e x 2c45e + // 0x2c45e X 2C45E + // 0x2c45e X8 0002C45E + // + // Format strings that do not start with an alphabetic character, or that start + // with an alphabetic character followed by a non-digit, are called + // user-defined format strings. The following table describes the formatting + // characters that are supported in user defined format strings. + // + // + // 0 - Digit placeholder. If the value being + // formatted has a digit in the position where the '0' appears in the format + // string, then that digit is copied to the output string. Otherwise, a '0' is + // stored in that position in the output string. The position of the leftmost + // '0' before the decimal point and the rightmost '0' after the decimal point + // determines the range of digits that are always present in the output + // string. + // + // # - Digit placeholder. If the value being + // formatted has a digit in the position where the '#' appears in the format + // string, then that digit is copied to the output string. Otherwise, nothing + // is stored in that position in the output string. + // + // . - Decimal point. The first '.' character + // in the format string determines the location of the decimal separator in the + // formatted value; any additional '.' characters are ignored. The actual + // character used as a the decimal separator in the output string is given by + // the NumberFormatInfo used to format the number. + // + // , - Thousand separator and number scaling. + // The ',' character serves two purposes. First, if the format string contains + // a ',' character between two digit placeholders (0 or #) and to the left of + // the decimal point if one is present, then the output will have thousand + // separators inserted between each group of three digits to the left of the + // decimal separator. The actual character used as a the decimal separator in + // the output string is given by the NumberFormatInfo used to format the + // number. Second, if the format string contains one or more ',' characters + // immediately to the left of the decimal point, or after the last digit + // placeholder if there is no decimal point, then the number will be divided by + // 1000 times the number of ',' characters before it is formatted. For example, + // the format string '0,,' will represent 100 million as just 100. Use of the + // ',' character to indicate scaling does not also cause the formatted number + // to have thousand separators. Thus, to scale a number by 1 million and insert + // thousand separators you would use the format string '#,##0,,'. + // + // % - Percentage placeholder. The presence of + // a '%' character in the format string causes the number to be multiplied by + // 100 before it is formatted. The '%' character itself is inserted in the + // output string where it appears in the format string. + // + // E+ E- e+ e- - Scientific notation. + // If any of the strings 'E+', 'E-', 'e+', or 'e-' are present in the format + // string and are immediately followed by at least one '0' character, then the + // number is formatted using scientific notation with an 'E' or 'e' inserted + // between the number and the exponent. The number of '0' characters following + // the scientific notation indicator determines the minimum number of digits to + // output for the exponent. The 'E+' and 'e+' formats indicate that a sign + // character (plus or minus) should always precede the exponent. The 'E-' and + // 'e-' formats indicate that a sign character should only precede negative + // exponents. + // + // \ - Literal character. A backslash character + // causes the next character in the format string to be copied to the output + // string as-is. The backslash itself isn't copied, so to place a backslash + // character in the output string, use two backslashes (\\) in the format + // string. + // + // 'ABC' "ABC" - Literal string. Characters + // enclosed in single or double quotation marks are copied to the output string + // as-is and do not affect formatting. + // + // ; - Section separator. The ';' character is + // used to separate sections for positive, negative, and zero numbers in the + // format string. + // + // Other - All other characters are copied to + // the output string in the position they appear. + // + // For fixed point formats (formats not containing an 'E+', 'E-', 'e+', or + // 'e-'), the number is rounded to as many decimal places as there are digit + // placeholders to the right of the decimal point. If the format string does + // not contain a decimal point, the number is rounded to the nearest + // integer. If the number has more digits than there are digit placeholders to + // the left of the decimal point, the extra digits are copied to the output + // string immediately before the first digit placeholder. + // + // For scientific formats, the number is rounded to as many significant digits + // as there are digit placeholders in the format string. + // + // To allow for different formatting of positive, negative, and zero values, a + // user-defined format string may contain up to three sections separated by + // semicolons. The results of having one, two, or three sections in the format + // string are described in the table below. + // + // Sections: + // + // One - The format string applies to all values. + // + // Two - The first section applies to positive values + // and zeros, and the second section applies to negative values. If the number + // to be formatted is negative, but becomes zero after rounding according to + // the format in the second section, then the resulting zero is formatted + // according to the first section. + // + // Three - The first section applies to positive + // values, the second section applies to negative values, and the third section + // applies to zeros. The second section may be left empty (by having no + // characters between the semicolons), in which case the first section applies + // to all non-zero values. If the number to be formatted is non-zero, but + // becomes zero after rounding according to the format in the first or second + // section, then the resulting zero is formatted according to the third + // section. + // + // For both standard and user-defined formatting operations on values of type + // float and double, if the value being formatted is a NaN (Not + // a Number) or a positive or negative infinity, then regardless of the format + // string, the resulting string is given by the NaNSymbol, + // PositiveInfinitySymbol, or NegativeInfinitySymbol property of + // the NumberFormatInfo used to format the number. + + internal static partial class Number + { + internal const int DecimalPrecision = 29; // Decimal.DecCalc also uses this value + private const int FloatPrecision = 7; + private const int DoublePrecision = 15; + private const int ScaleNAN = unchecked((int)0x80000000); + private const int ScaleINF = 0x7FFFFFFF; + private const int MaxUInt32HexDigits = 8; + private const int MaxUInt32DecDigits = 10; + private const int MaxUInt64DecDigits = 20; + private const int CharStackBufferSize = 32; + private const string PosNumberFormat = "#"; + + private static readonly string[] s_posCurrencyFormats = + { + "$#", "#$", "$ #", "# $" + }; + + private static readonly string[] s_negCurrencyFormats = + { + "($#)", "-$#", "$-#", "$#-", + "(#$)", "-#$", "#-$", "#$-", + "-# $", "-$ #", "# $-", "$ #-", + "$ -#", "#- $", "($ #)", "(# $)" + }; + + private static readonly string[] s_posPercentFormats = + { + "# %", "#%", "%#", "% #" + }; + + private static readonly string[] s_negPercentFormats = + { + "-# %", "-#%", "-%#", + "%-#", "%#-", + "#-%", "#%-", + "-% #", "# %-", "% #-", + "% -#", "#- %" + }; + + private static readonly string[] s_negNumberFormats = + { + "(#)", "-#", "- #", "#-", "# -", + }; + + public static string FormatDecimal(decimal value, ReadOnlySpan format, NumberFormatInfo info) + { + char fmt = ParseFormatSpecifier(format, out int digits); + + NumberBuffer number = default; + DecimalToNumber(value, ref number); + + ValueStringBuilder sb; + unsafe + { + char* stackPtr = stackalloc char[CharStackBufferSize]; + sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + } + + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, isDecimal:true); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + + return sb.ToString(); + } + + public static bool TryFormatDecimal(decimal value, ReadOnlySpan format, NumberFormatInfo info, Span destination, out int charsWritten) + { + char fmt = ParseFormatSpecifier(format, out int digits); + + NumberBuffer number = default; + DecimalToNumber(value, ref number); + + ValueStringBuilder sb; + unsafe + { + char* stackPtr = stackalloc char[CharStackBufferSize]; + sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + } + + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, isDecimal: true); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + + return sb.TryCopyTo(destination, out charsWritten); + } + + private static unsafe void DecimalToNumber(decimal value, ref NumberBuffer number) + { + decimal d = value; + + char* buffer = number.digits; + number.precision = DecimalPrecision; + number.sign = d.IsNegative; + + char* p = buffer + DecimalPrecision; + while ((d.Mid | d.High) != 0) + { + p = UInt32ToDecChars(p, decimal.DecDivMod1E9(ref d), 9); + } + p = UInt32ToDecChars(p, d.Low, 0); + + int i = (int)(buffer + DecimalPrecision - p); + number.scale = i - d.Scale; + + char* dst = number.digits; + while (--i >= 0) + { + *dst++ = *p++; + } + *dst = '\0'; + } + + public static string FormatDouble(double value, string format, NumberFormatInfo info) + { + Span stackBuffer = stackalloc char[CharStackBufferSize]; + var sb = new ValueStringBuilder(stackBuffer); + return FormatDouble(ref sb, value, format, info) ?? sb.ToString(); + } + + public static bool TryFormatDouble(double value, ReadOnlySpan format, NumberFormatInfo info, Span destination, out int charsWritten) + { + Span stackBuffer = stackalloc char[CharStackBufferSize]; + var sb = new ValueStringBuilder(stackBuffer); + string s = FormatDouble(ref sb, value, format, info); + return s != null ? + TryCopyTo(s, destination, out charsWritten) : + sb.TryCopyTo(destination, out charsWritten); + } + + /// Formats the specified value according to the specified format and info. + /// + /// Non-null if an existing string can be returned, in which case the builder will be unmodified. + /// Null if no existing string was returned, in which case the formatted output is in the builder. + /// + private static string FormatDouble(ref ValueStringBuilder sb, double value, ReadOnlySpan format, NumberFormatInfo info) + { + char fmt = ParseFormatSpecifier(format, out int digits); + int precision = DoublePrecision; + NumberBuffer number = default; + + switch (fmt) + { + case 'R': + case 'r': + { + // In order to give numbers that are both friendly to display and round-trippable, we parse the + // number using 15 digits and then determine if it round trips to the same value. If it does, we + // convert that NUMBER to a string, otherwise we reparse using 17 digits and display that. + DoubleToNumber(value, DoublePrecision, ref number); + if (number.scale == ScaleNAN) + { + return info.NaNSymbol; + } + else if (number.scale == ScaleINF) + { + return number.sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol; + } + + if (NumberToDouble(ref number) == value) + { + NumberToString(ref sb, ref number, 'G', DoublePrecision, info, isDecimal: false); + } + else + { + DoubleToNumber(value, 17, ref number); + NumberToString(ref sb, ref number, 'G', 17, info, isDecimal: false); + } + + return null; + } + + case 'E': + case 'e': + // Round values less than E14 to 15 digits + if (digits > 14) + { + precision = 17; + } + break; + + case 'G': + case 'g': + // Round values less than G15 to 15 digits. G16 and G17 will not be touched. + if (digits > 15) + { + precision = 17; + } + break; + } + + DoubleToNumber(value, precision, ref number); + if (number.scale == ScaleNAN) + { + return info.NaNSymbol; + } + else if (number.scale == ScaleINF) + { + return number.sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol; + } + + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, isDecimal: false); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + + return null; + } + + public static string FormatSingle(float value, string format, NumberFormatInfo info) + { + Span stackBuffer = stackalloc char[CharStackBufferSize]; + var sb = new ValueStringBuilder(stackBuffer); + return FormatSingle(ref sb, value, format, info) ?? sb.ToString(); + } + + public static bool TryFormatSingle(float value, ReadOnlySpan format, NumberFormatInfo info, Span destination, out int charsWritten) + { + Span stackBuffer = stackalloc char[CharStackBufferSize]; + var sb = new ValueStringBuilder(stackBuffer); + string s = FormatSingle(ref sb, value, format, info); + return s != null ? + TryCopyTo(s, destination, out charsWritten) : + sb.TryCopyTo(destination, out charsWritten); + } + + /// Formats the specified value according to the specified format and info. + /// + /// Non-null if an existing string can be returned, in which case the builder will be unmodified. + /// Null if no existing string was returned, in which case the formatted output is in the builder. + /// + private static string FormatSingle(ref ValueStringBuilder sb, float value, ReadOnlySpan format, NumberFormatInfo info) + { + char fmt = ParseFormatSpecifier(format, out int digits); + int precision = FloatPrecision; + NumberBuffer number = default; + + switch (fmt) + { + case 'R': + case 'r': + { + // In order to give numbers that are both friendly to display and round-trippable, we parse the + // number using 7 digits and then determine if it round trips to the same value. If it does, we + // convert that NUMBER to a string, otherwise we reparse using 9 digits and display that. + DoubleToNumber(value, FloatPrecision, ref number); + if (number.scale == ScaleNAN) + { + return info.NaNSymbol; + } + else if (number.scale == ScaleINF) + { + return number.sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol; + } + + if ((float)NumberToDouble(ref number) == value) + { + NumberToString(ref sb, ref number, 'G', FloatPrecision, info, isDecimal: false); + } + else + { + DoubleToNumber(value, 9, ref number); + NumberToString(ref sb, ref number, 'G', 9, info, isDecimal: false); + } + return null; + } + + case 'E': + case 'e': + // Round values less than E14 to 15 digits. + if (digits > 6) + { + precision = 9; + } + break; + + case 'G': + case 'g': + // Round values less than G15 to 15 digits. G16 and G17 will not be touched. + if (digits > 7) + { + precision = 9; + } + break; + } + + DoubleToNumber(value, precision, ref number); + if (number.scale == ScaleNAN) + { + return info.NaNSymbol; + } + else if (number.scale == ScaleINF) + { + return number.sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol; + } + + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, false); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + return null; + } + + private static bool TryCopyTo(string source, Span destination, out int charsWritten) + { + Debug.Assert(source != null); + + if (source.AsReadOnlySpan().TryCopyTo(destination)) + { + charsWritten = source.Length; + return true; + } + + charsWritten = 0; + return false; + } + + public static string FormatInt32(int value, ReadOnlySpan format, IFormatProvider provider) + { + // Fast path for default format with a non-negative value + if (value >= 0 && format.Length == 0) + { + return UInt32ToDecStr((uint)value, digits: -1); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') + { + return value >= 0 ? + UInt32ToDecStr((uint)value, digits) : + NegativeInt32ToDecStr(value, digits, info.NegativeSign); + } + else if (fmtUpper == 'X') + { + // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase + // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code produces lowercase. + return Int32ToHexStr(value, (char)(fmt - ('X' - 'A' + 10)), digits); + } + else + { + NumberBuffer number = default; + Int32ToNumber(value, ref number); + ValueStringBuilder sb; + unsafe + { + char* stackPtr = stackalloc char[CharStackBufferSize]; + sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + } + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, false); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + return sb.ToString(); + } + } + + public static bool TryFormatInt32(int value, ReadOnlySpan format, IFormatProvider provider, Span destination, out int charsWritten) + { + // Fast path for default format with a non-negative value + if (value >= 0 && format.Length == 0) + { + return TryUInt32ToDecStr((uint)value, digits: -1, destination, out charsWritten); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') + { + return value >= 0 ? + TryUInt32ToDecStr((uint)value, digits, destination, out charsWritten) : + TryNegativeInt32ToDecStr(value, digits, info.NegativeSign, destination, out charsWritten); + } + else if (fmtUpper == 'X') + { + // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase + // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code produces lowercase. + return TryInt32ToHexStr(value, (char)(fmt - ('X' - 'A' + 10)), digits, destination, out charsWritten); + } + else + { + NumberBuffer number = default; + Int32ToNumber(value, ref number); + ValueStringBuilder sb; + unsafe + { + char* stackPtr = stackalloc char[CharStackBufferSize]; + sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + } + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, false); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + return sb.TryCopyTo(destination, out charsWritten); + } + } + + public static string FormatUInt32(uint value, ReadOnlySpan format, IFormatProvider provider) + { + // Fast path for default format + if (format.Length == 0) + { + return UInt32ToDecStr(value, digits: -1); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') + { + return UInt32ToDecStr(value, digits); + } + else if (fmtUpper == 'X') + { + // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase + // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code produces lowercase. + return Int32ToHexStr((int)value, (char)(fmt - ('X' - 'A' + 10)), digits); + } + else + { + NumberBuffer number = default; + UInt32ToNumber(value, ref number); + ValueStringBuilder sb; + unsafe + { + char* stackPtr = stackalloc char[CharStackBufferSize]; + sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + } + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, false); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + return sb.ToString(); + } + } + + public static bool TryFormatUInt32(uint value, ReadOnlySpan format, IFormatProvider provider, Span destination, out int charsWritten) + { + // Fast path for default format + if (format.Length == 0) + { + return TryUInt32ToDecStr(value, digits: -1, destination, out charsWritten); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') + { + return TryUInt32ToDecStr(value, digits, destination, out charsWritten); + } + else if (fmtUpper == 'X') + { + // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase + // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code produces lowercase. + return TryInt32ToHexStr((int)value, (char)(fmt - ('X' - 'A' + 10)), digits, destination, out charsWritten); + } + else + { + NumberBuffer number = default; + UInt32ToNumber(value, ref number); + ValueStringBuilder sb; + unsafe + { + char* stackPtr = stackalloc char[CharStackBufferSize]; + sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + } + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, false); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + return sb.TryCopyTo(destination, out charsWritten); + } + } + + public static string FormatInt64(long value, ReadOnlySpan format, IFormatProvider provider) + { + // Fast path for default format with a non-negative value + if (value >= 0 && format.Length == 0) + { + return UInt64ToDecStr((ulong)value, digits: -1); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') + { + return value >= 0 ? + UInt64ToDecStr((ulong)value, digits) : + NegativeInt64ToDecStr(value, digits, info.NegativeSign); + } + else if (fmtUpper == 'X') + { + // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase + // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code + // produces lowercase. + return Int64ToHexStr(value, (char)(fmt - ('X' - 'A' + 10)), digits); + } + else + { + NumberBuffer number = default; + Int64ToNumber(value, ref number); + ValueStringBuilder sb; + unsafe + { + char* stackPtr = stackalloc char[CharStackBufferSize]; + sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + } + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, false); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + return sb.ToString(); + } + } + + public static bool TryFormatInt64(long value, ReadOnlySpan format, IFormatProvider provider, Span destination, out int charsWritten) + { + // Fast path for default format with a non-negative value + if (value >= 0 && format.Length == 0) + { + return TryUInt64ToDecStr((ulong)value, digits: -1, destination, out charsWritten); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') + { + return value >= 0 ? + TryUInt64ToDecStr((ulong)value, digits, destination, out charsWritten) : + TryNegativeInt64ToDecStr(value, digits, info.NegativeSign, destination, out charsWritten); + } + else if (fmtUpper == 'X') + { + // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase + // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code + // produces lowercase. + return TryInt64ToHexStr(value, (char)(fmt - ('X' - 'A' + 10)), digits, destination, out charsWritten); + } + else + { + NumberBuffer number = default; + Int64ToNumber(value, ref number); + ValueStringBuilder sb; + unsafe + { + char* stackPtr = stackalloc char[CharStackBufferSize]; + sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + } + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, false); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + return sb.TryCopyTo(destination, out charsWritten); + } + } + + public static string FormatUInt64(ulong value, ReadOnlySpan format, IFormatProvider provider) + { + // Fast path for default format + if (format.Length == 0) + { + return UInt64ToDecStr(value, digits: -1); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') + { + return UInt64ToDecStr(value, digits); + } + else if (fmtUpper == 'X') + { + // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase + // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code + // produces lowercase. + return Int64ToHexStr((long)value, (char)(fmt - ('X' - 'A' + 10)), digits); + } + else + { + NumberBuffer number = default; + UInt64ToNumber(value, ref number); + ValueStringBuilder sb; + unsafe + { + char* stackPtr = stackalloc char[CharStackBufferSize]; + sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + } + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, false); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + return sb.ToString(); + } + } + + public static bool TryFormatUInt64(ulong value, ReadOnlySpan format, IFormatProvider provider, Span destination, out int charsWritten) + { + // Fast path for default format + if (format.Length == 0) + { + return TryUInt64ToDecStr(value, digits: -1, destination, out charsWritten); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') + { + return TryUInt64ToDecStr(value, digits, destination, out charsWritten); + } + else if (fmtUpper == 'X') + { + // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase + // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code + // produces lowercase. + return TryInt64ToHexStr((long)value, (char)(fmt - ('X' - 'A' + 10)), digits, destination, out charsWritten); + } + else + { + NumberBuffer number = default; + UInt64ToNumber(value, ref number); + ValueStringBuilder sb; + unsafe + { + char* stackPtr = stackalloc char[CharStackBufferSize]; + sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + } + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, false); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + return sb.TryCopyTo(destination, out charsWritten); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] // called from only one location + private static unsafe void Int32ToNumber(int value, ref NumberBuffer number) + { + number.precision = Int32Precision; + + if (value >= 0) + { + number.sign = false; + } + else + { + number.sign = true; + value = -value; + } + + char* buffer = number.digits; + char* p = UInt32ToDecChars(buffer + Int32Precision, (uint)value, 0); + int i = (int)(buffer + Int32Precision - p); + + number.scale = i; + + char* dst = number.digits; + while (--i >= 0) + *dst++ = *p++; + *dst = '\0'; + } + + private static unsafe string NegativeInt32ToDecStr(int value, int digits, string sNegative) + { + Debug.Assert(value < 0); + + if (digits < 1) + digits = 1; + + int bufferLength = Math.Max(digits, MaxUInt32DecDigits) + sNegative.Length; + int index = bufferLength; + + char* buffer = stackalloc char[bufferLength]; + char* p = UInt32ToDecChars(buffer + bufferLength, (uint)(-value), digits); + for (int i = sNegative.Length - 1; i >= 0; i--) + { + *(--p) = sNegative[i]; + } + + Debug.Assert(buffer + bufferLength - p >= 0 && buffer <= p); + return new string(p, 0, (int)(buffer + bufferLength - p)); + } + + private static unsafe bool TryNegativeInt32ToDecStr(int value, int digits, string sNegative, Span destination, out int charsWritten) + { + Debug.Assert(value < 0); + + if (digits < 1) + digits = 1; + + int bufferLength = Math.Max(digits, MaxUInt32DecDigits) + sNegative.Length; + int index = bufferLength; + + char* buffer = stackalloc char[bufferLength]; + char* p = UInt32ToDecChars(buffer + bufferLength, (uint)(-value), digits); + for (int i = sNegative.Length - 1; i >= 0; i--) + { + *(--p) = sNegative[i]; + } + + Debug.Assert(buffer + bufferLength - p >= 0 && buffer <= p); + return TryCopyTo(p, (int)(buffer + bufferLength - p), destination, out charsWritten); + } + + private static unsafe string Int32ToHexStr(int value, char hexBase, int digits) + { + if (digits < 1) + digits = 1; + + int bufferLength = Math.Max(digits, MaxUInt32HexDigits); + char* buffer = stackalloc char[bufferLength]; + + char* p = Int32ToHexChars(buffer + bufferLength, (uint)value, hexBase, digits); + return new string(p, 0, (int)(buffer + bufferLength - p)); + } + + private static unsafe bool TryInt32ToHexStr(int value, char hexBase, int digits, Span destination, out int charsWritten) + { + if (digits < 1) + digits = 1; + + int bufferLength = Math.Max(digits, MaxUInt32HexDigits); + char* buffer = stackalloc char[bufferLength]; + + char* p = Int32ToHexChars(buffer + bufferLength, (uint)value, hexBase, digits); + return TryCopyTo(p, (int)(buffer + bufferLength - p), destination, out charsWritten); + } + + private static unsafe char* Int32ToHexChars(char* buffer, uint value, int hexBase, int digits) + { + while (--digits >= 0 || value != 0) + { + byte digit = (byte)(value & 0xF); + *(--buffer) = (char)(digit + (digit < 10 ? (byte)'0' : hexBase)); + value >>= 4; + } + return buffer; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] // called from only one location + private static unsafe void UInt32ToNumber(uint value, ref NumberBuffer number) + { + number.precision = UInt32Precision; + number.sign = false; + + char* buffer = number.digits; + char* p = UInt32ToDecChars(buffer + UInt32Precision, value, 0); + int i = (int)(buffer + UInt32Precision - p); + number.scale = i; + + char* dst = number.digits; + while (--i >= 0) + *dst++ = *p++; + *dst = '\0'; + } + + internal static unsafe char* UInt32ToDecChars(char* bufferEnd, uint value, int digits) + { + while (--digits >= 0 || value != 0) + { + // TODO https://github.com/dotnet/coreclr/issues/3439 + uint newValue = value / 10; + *(--bufferEnd) = (char)(value - (newValue * 10) + '0'); + value = newValue; + } + return bufferEnd; + } + + private static unsafe string UInt32ToDecStr(uint value, int digits) + { + if (digits <= 1) + { + char* buffer = stackalloc char[MaxUInt32DecDigits]; + + char* start = buffer + MaxUInt32DecDigits; + char* p = start; + do + { + // TODO https://github.com/dotnet/coreclr/issues/3439 + uint div = value / 10; + *(--p) = (char)('0' + value - (div * 10)); + value = div; + } + while (value != 0); + + return new string(p, 0, (int)(start - p)); + } + else + { + int bufferSize = Math.Max(digits, MaxUInt32DecDigits); + char* buffer = stackalloc char[bufferSize]; + char* p = UInt32ToDecChars(buffer + bufferSize, value, digits); + return new string(p, 0, (int)(buffer + bufferSize - p)); + } + } + + private static unsafe bool TryUInt32ToDecStr(uint value, int digits, Span destination, out int charsWritten) + { + if (digits <= 1) + { + char* buffer = stackalloc char[MaxUInt32DecDigits]; + char* start = buffer + MaxUInt32DecDigits; + char* p = start; + do + { + // TODO https://github.com/dotnet/coreclr/issues/3439 + uint div = value / 10; + *(--p) = (char)('0' + value - (div * 10)); + value = div; + } + while (value != 0); + return TryCopyTo(p, (int)(start - p), destination, out charsWritten); + } + else + { + int bufferSize = Math.Max(digits, MaxUInt32DecDigits); + char* buffer = stackalloc char[bufferSize]; + char* p = UInt32ToDecChars(buffer + bufferSize, value, digits); + return TryCopyTo(p, (int)(buffer + bufferSize - p), destination, out charsWritten); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe bool TryCopyTo(char* src, int length, Span destination, out int charsWritten) + { + if (length <= destination.Length) + { + bool copied = new ReadOnlySpan(src, length).TryCopyTo(destination); + Debug.Assert(copied); + charsWritten = length; + return true; + } + else + { + charsWritten = 0; + return false; + } + } + + private static unsafe void Int64ToNumber(long input, ref NumberBuffer number) + { + ulong value = (ulong)input; + number.sign = input < 0; + number.precision = Int64Precision; + if (number.sign) + { + value = (ulong)(-input); + } + + char* buffer = number.digits; + char* p = buffer + Int64Precision; + while (High32(value) != 0) + p = UInt32ToDecChars(p, Int64DivMod1E9(ref value), 9); + p = UInt32ToDecChars(p, Low32(value), 0); + int i = (int)(buffer + Int64Precision - p); + + number.scale = i; + + char* dst = number.digits; + while (--i >= 0) + *dst++ = *p++; + *dst = '\0'; + } + + private static unsafe string NegativeInt64ToDecStr(long input, int digits, string sNegative) + { + Debug.Assert(input < 0); + + if (digits < 1) + { + digits = 1; + } + + ulong value = (ulong)(-input); + + int bufferLength = Math.Max(digits, MaxUInt64DecDigits) + sNegative.Length; + int index = bufferLength; + + char* buffer = stackalloc char[bufferLength]; + char* p = buffer + bufferLength; + while (High32(value) != 0) + { + p = UInt32ToDecChars(p, Int64DivMod1E9(ref value), 9); + digits -= 9; + } + p = UInt32ToDecChars(p, Low32(value), digits); + + for (int i = sNegative.Length - 1; i >= 0; i--) + { + *(--p) = sNegative[i]; + } + + return new string(p, 0, (int)(buffer + bufferLength - p)); + } + + private static unsafe bool TryNegativeInt64ToDecStr(long input, int digits, string sNegative, Span destination, out int charsWritten) + { + Debug.Assert(input < 0); + + if (digits < 1) + { + digits = 1; + } + + ulong value = (ulong)(-input); + + int bufferLength = Math.Max(digits, MaxUInt64DecDigits) + sNegative.Length; + int index = bufferLength; + + char* buffer = stackalloc char[bufferLength]; + char* p = buffer + bufferLength; + while (High32(value) != 0) + { + p = UInt32ToDecChars(p, Int64DivMod1E9(ref value), 9); + digits -= 9; + } + p = UInt32ToDecChars(p, Low32(value), digits); + + for (int i = sNegative.Length - 1; i >= 0; i--) + { + *(--p) = sNegative[i]; + } + + return TryCopyTo(p, (int)(buffer + bufferLength - p), destination, out charsWritten); + } + + private static unsafe string Int64ToHexStr(long value, char hexBase, int digits) + { + int bufferLength = Math.Max(digits, MaxUInt32HexDigits * 2); + char* buffer = stackalloc char[bufferLength]; + int index = bufferLength; + + char* p; + if (High32((ulong)value) != 0) + { + p = Int32ToHexChars(buffer + index, Low32((ulong)value), hexBase, 8); + p = Int32ToHexChars(p, High32((ulong)value), hexBase, digits - 8); + } + else + { + p = Int32ToHexChars(buffer + index, Low32((ulong)value), hexBase, Math.Max(digits, 1)); + } + + return new string(p, 0, (int)(buffer + bufferLength - p)); + } + + private static unsafe bool TryInt64ToHexStr(long value, char hexBase, int digits, Span destination, out int charsWritten) + { + int bufferLength = Math.Max(digits, MaxUInt32HexDigits * 2); + char* buffer = stackalloc char[bufferLength]; + int index = bufferLength; + + char* p; + if (High32((ulong)value) != 0) + { + p = Int32ToHexChars(buffer + index, Low32((ulong)value), hexBase, 8); + p = Int32ToHexChars(p, High32((ulong)value), hexBase, digits - 8); + } + else + { + p = Int32ToHexChars(buffer + index, Low32((ulong)value), hexBase, Math.Max(digits, 1)); + } + + return TryCopyTo(p, (int)(buffer + bufferLength - p), destination, out charsWritten); + } + + private static unsafe void UInt64ToNumber(ulong value, ref NumberBuffer number) + { + number.precision = UInt64Precision; + number.sign = false; + + char* buffer = number.digits; + char* p = buffer + UInt64Precision; + + while (High32(value) != 0) + p = UInt32ToDecChars(p, Int64DivMod1E9(ref value), 9); + p = UInt32ToDecChars(p, Low32(value), 0); + int i = (int)(buffer + UInt64Precision - p); + + number.scale = i; + + char* dst = number.digits; + while (--i >= 0) + *dst++ = *p++; + *dst = '\0'; + } + + private static unsafe string UInt64ToDecStr(ulong value, int digits) + { + if (digits < 1) + digits = 1; + + int bufferSize = Math.Max(digits, MaxUInt64DecDigits); + char* buffer = stackalloc char[bufferSize]; + char* p = buffer + bufferSize; + while (High32(value) != 0) + { + p = UInt32ToDecChars(p, Int64DivMod1E9(ref value), 9); + digits -= 9; + } + p = UInt32ToDecChars(p, Low32(value), digits); + + return new string(p, 0, (int)(buffer + bufferSize - p)); + } + + private static unsafe bool TryUInt64ToDecStr(ulong value, int digits, Span destination, out int charsWritten) + { + if (digits < 1) + digits = 1; + + int bufferSize = Math.Max(digits, MaxUInt64DecDigits); + char* buffer = stackalloc char[bufferSize]; + char* p = buffer + bufferSize; + while (High32(value) != 0) + { + p = UInt32ToDecChars(p, Int64DivMod1E9(ref value), 9); + digits -= 9; + } + p = UInt32ToDecChars(p, Low32(value), digits); + + return TryCopyTo(p, (int)(buffer + bufferSize - p), destination, out charsWritten); + } + + internal static unsafe char ParseFormatSpecifier(ReadOnlySpan format, out int digits) + { + char c = default; + if (format.Length > 0) + { + // If the format begins with a symbol, see if it's a standard format + // with or without a specified number of digits. + c = format[0]; + if ((uint)(c - 'A') <= 'Z' - 'A' || + (uint)(c - 'a') <= 'z' - 'a') + { + // Fast path for sole symbol, e.g. "D" + if (format.Length == 1) + { + digits = -1; + return c; + } + + if (format.Length == 2) + { + // Fast path for symbol and single digit, e.g. "X4" + int d = format[1] - '0'; + if ((uint)d < 10) + { + digits = d; + return c; + } + } + else if (format.Length == 3) + { + // Fast path for symbol and double digit, e.g. "F12" + int d1 = format[1] - '0', d2 = format[2] - '0'; + if ((uint)d1 < 10 && (uint)d2 < 10) + { + digits = d1 * 10 + d2; + return c; + } + } + + // Fallback for symbol and any length digits. The digits value must be >= 0 && <= 99, + // but it can begin with any number of 0s, and thus we may need to check more than two + // digits. Further, for compat, we need to stop when we hit a null char. + int n = 0; + int i = 1; + while (i < format.Length && (((uint)format[i] - '0') < 10) && n < 10) + { + n = (n * 10) + format[i++] - '0'; + } + + // If we're at the end of the digits rather than having stopped because we hit something + // other than a digit or overflowed, return the standard format info. + if (i == format.Length || format[i] == '\0') + { + digits = n; + return c; + } + } + } + + // Default empty format to be "G"; custom format is signified with '\0'. + digits = -1; + return format.Length == 0 || c == '\0' ? // For compat, treat '\0' as the end of the specifier, even if the specifier extends beyond it. + 'G' : + '\0'; + } + + internal static unsafe void NumberToString(ref ValueStringBuilder sb, ref NumberBuffer number, char format, int nMaxDigits, NumberFormatInfo info, bool isDecimal) + { + int nMinDigits = -1; + + switch (format) + { + case 'C': + case 'c': + { + nMinDigits = nMaxDigits >= 0 ? nMaxDigits : info.CurrencyDecimalDigits; + if (nMaxDigits < 0) + nMaxDigits = info.CurrencyDecimalDigits; + + RoundNumber(ref number, number.scale + nMaxDigits); // Don't change this line to use digPos since digCount could have its sign changed. + + FormatCurrency(ref sb, ref number, nMinDigits, nMaxDigits, info); + + break; + } + + case 'F': + case 'f': + { + if (nMaxDigits < 0) + nMaxDigits = nMinDigits = info.NumberDecimalDigits; + else + nMinDigits = nMaxDigits; + + RoundNumber(ref number, number.scale + nMaxDigits); + + if (number.sign) + sb.Append(info.NegativeSign); + + FormatFixed(ref sb, ref number, nMinDigits, nMaxDigits, info, null, info.NumberDecimalSeparator, null); + + break; + } + + case 'N': + case 'n': + { + if (nMaxDigits < 0) + nMaxDigits = nMinDigits = info.NumberDecimalDigits; // Since we are using digits in our calculation + else + nMinDigits = nMaxDigits; + + RoundNumber(ref number, number.scale + nMaxDigits); + + FormatNumber(ref sb, ref number, nMinDigits, nMaxDigits, info); + + break; + } + + case 'E': + case 'e': + { + if (nMaxDigits < 0) + nMaxDigits = nMinDigits = 6; + else + nMinDigits = nMaxDigits; + nMaxDigits++; + + RoundNumber(ref number, nMaxDigits); + + if (number.sign) + sb.Append(info.NegativeSign); + + FormatScientific(ref sb, ref number, nMinDigits, nMaxDigits, info, format); + + break; + } + + case 'G': + case 'g': + { + bool enableRounding = true; + if (nMaxDigits < 1) + { + if (isDecimal && (nMaxDigits == -1)) + { + // Default to 29 digits precision only for G formatting without a precision specifier + // This ensures that the PAL code pads out to the correct place even when we use the default precision + nMaxDigits = nMinDigits = DecimalPrecision; + enableRounding = false; // Turn off rounding for ECMA compliance to output trailing 0's after decimal as significant + } + else + { + // This ensures that the PAL code pads out to the correct place even when we use the default precision + nMaxDigits = nMinDigits = number.precision; + } + } + else + nMinDigits = nMaxDigits; + + if (enableRounding) // Don't round for G formatting without precision + RoundNumber(ref number, nMaxDigits); // This also fixes up the minus zero case + else + { + if (isDecimal && (number.digits[0] == 0)) + { + // Minus zero should be formatted as 0 + number.sign = false; + } + } + + if (number.sign) + sb.Append(info.NegativeSign); + + FormatGeneral(ref sb, ref number, nMinDigits, nMaxDigits, info, (char)(format - ('G' - 'E')), !enableRounding); + + break; + } + + case 'P': + case 'p': + { + if (nMaxDigits < 0) + nMaxDigits = nMinDigits = info.PercentDecimalDigits; + else + nMinDigits = nMaxDigits; + number.scale += 2; + + RoundNumber(ref number, number.scale + nMaxDigits); + + FormatPercent(ref sb, ref number, nMinDigits, nMaxDigits, info); + + break; + } + + default: + throw new FormatException(SR.Argument_BadFormatSpecifier); + } + } + + internal static unsafe void NumberToStringFormat(ref ValueStringBuilder sb, ref NumberBuffer number, ReadOnlySpan format, NumberFormatInfo info) + { + int digitCount; + int decimalPos; + int firstDigit; + int lastDigit; + int digPos; + bool scientific; + int thousandPos; + int thousandCount = 0; + bool thousandSeps; + int scaleAdjust; + int adjust; + + int section; + int src; + char* dig = number.digits; + char ch; + + section = FindSection(format, dig[0] == 0 ? 2 : number.sign ? 1 : 0); + + while (true) + { + digitCount = 0; + decimalPos = -1; + firstDigit = 0x7FFFFFFF; + lastDigit = 0; + scientific = false; + thousandPos = -1; + thousandSeps = false; + scaleAdjust = 0; + src = section; + + fixed (char* pFormat = &MemoryMarshal.GetReference(format)) + { + while (src < format.Length && (ch = pFormat[src++]) != 0 && ch != ';') + { + switch (ch) + { + case '#': + digitCount++; + break; + case '0': + if (firstDigit == 0x7FFFFFFF) + firstDigit = digitCount; + digitCount++; + lastDigit = digitCount; + break; + case '.': + if (decimalPos < 0) + decimalPos = digitCount; + break; + case ',': + if (digitCount > 0 && decimalPos < 0) + { + if (thousandPos >= 0) + { + if (thousandPos == digitCount) + { + thousandCount++; + break; + } + thousandSeps = true; + } + thousandPos = digitCount; + thousandCount = 1; + } + break; + case '%': + scaleAdjust += 2; + break; + case '\x2030': + scaleAdjust += 3; + break; + case '\'': + case '"': + while (src < format.Length && pFormat[src] != 0 && pFormat[src++] != ch) + ; + break; + case '\\': + if (src < format.Length && pFormat[src] != 0) + src++; + break; + case 'E': + case 'e': + if ((src < format.Length && pFormat[src] == '0') || + (src + 1 < format.Length && (pFormat[src] == '+' || pFormat[src] == '-') && pFormat[src + 1] == '0')) + { + while (++src < format.Length && pFormat[src] == '0'); + scientific = true; + } + break; + } + } + } + + if (decimalPos < 0) + decimalPos = digitCount; + + if (thousandPos >= 0) + { + if (thousandPos == decimalPos) + scaleAdjust -= thousandCount * 3; + else + thousandSeps = true; + } + + if (dig[0] != 0) + { + number.scale += scaleAdjust; + int pos = scientific ? digitCount : number.scale + digitCount - decimalPos; + RoundNumber(ref number, pos); + if (dig[0] == 0) + { + src = FindSection(format, 2); + if (src != section) + { + section = src; + continue; + } + } + } + else + { + number.sign = false; // We need to format -0 without the sign set. + number.scale = 0; // Decimals with scale ('0.00') should be rounded. + } + + break; + } + + firstDigit = firstDigit < decimalPos ? decimalPos - firstDigit : 0; + lastDigit = lastDigit > decimalPos ? decimalPos - lastDigit : 0; + if (scientific) + { + digPos = decimalPos; + adjust = 0; + } + else + { + digPos = number.scale > decimalPos ? number.scale : decimalPos; + adjust = number.scale - decimalPos; + } + src = section; + + // Adjust can be negative, so we make this an int instead of an unsigned int. + // Adjust represents the number of characters over the formatting e.g. format string is "0000" and you are trying to + // format 100000 (6 digits). Means adjust will be 2. On the other hand if you are trying to format 10 adjust will be + // -2 and we'll need to fixup these digits with 0 padding if we have 0 formatting as in this example. + Span thousandsSepPos = stackalloc int[4]; + int thousandsSepCtr = -1; + + if (thousandSeps) + { + // We need to precompute this outside the number formatting loop + if (info.NumberGroupSeparator.Length > 0) + { + // We need this array to figure out where to insert the thousands separator. We would have to traverse the string + // backwards. PIC formatting always traverses forwards. These indices are precomputed to tell us where to insert + // the thousands separator so we can get away with traversing forwards. Note we only have to compute up to digPos. + // The max is not bound since you can have formatting strings of the form "000,000..", and this + // should handle that case too. + + int[] groupDigits = info.numberGroupSizes; + + int groupSizeIndex = 0; // Index into the groupDigits array. + int groupTotalSizeCount = 0; + int groupSizeLen = groupDigits.Length; // The length of groupDigits array. + if (groupSizeLen != 0) + groupTotalSizeCount = groupDigits[groupSizeIndex]; // The current running total of group size. + int groupSize = groupTotalSizeCount; + + int totalDigits = digPos + ((adjust < 0) ? adjust : 0); // Actual number of digits in o/p + int numDigits = (firstDigit > totalDigits) ? firstDigit : totalDigits; + while (numDigits > groupTotalSizeCount) + { + if (groupSize == 0) + break; + ++thousandsSepCtr; + if (thousandsSepCtr >= thousandsSepPos.Length) + { + var newThousandsSepPos = new int[thousandsSepPos.Length * 2]; + bool copied = thousandsSepPos.TryCopyTo(newThousandsSepPos); + Debug.Assert(copied, "Expect copy to succeed, as the new array is larger than the original"); + thousandsSepPos = newThousandsSepPos; + } + + thousandsSepPos[thousandsSepCtr] = groupTotalSizeCount; + if (groupSizeIndex < groupSizeLen - 1) + { + groupSizeIndex++; + groupSize = groupDigits[groupSizeIndex]; + } + groupTotalSizeCount += groupSize; + } + } + } + + if (number.sign && section == 0) + sb.Append(info.NegativeSign); + + bool decimalWritten = false; + + fixed (char* pFormat = &MemoryMarshal.GetReference(format)) + { + char* cur = dig; + + while (src < format.Length && (ch = pFormat[src++]) != 0 && ch != ';') + { + if (adjust > 0) + { + switch (ch) + { + case '#': + case '0': + case '.': + while (adjust > 0) + { + // digPos will be one greater than thousandsSepPos[thousandsSepCtr] since we are at + // the character after which the groupSeparator needs to be appended. + sb.Append(*cur != 0 ? *cur++ : '0'); + if (thousandSeps && digPos > 1 && thousandsSepCtr >= 0) + { + if (digPos == thousandsSepPos[thousandsSepCtr] + 1) + { + sb.Append(info.NumberGroupSeparator); + thousandsSepCtr--; + } + } + digPos--; + adjust--; + } + break; + } + } + + switch (ch) + { + case '#': + case '0': + { + if (adjust < 0) + { + adjust++; + ch = digPos <= firstDigit ? '0' : '\0'; + } + else + { + ch = *cur != 0 ? *cur++ : digPos > lastDigit ? '0' : '\0'; + } + if (ch != 0) + { + sb.Append(ch); + if (thousandSeps && digPos > 1 && thousandsSepCtr >= 0) + { + if (digPos == thousandsSepPos[thousandsSepCtr] + 1) + { + sb.Append(info.NumberGroupSeparator); + thousandsSepCtr--; + } + } + } + + digPos--; + break; + } + case '.': + { + if (digPos != 0 || decimalWritten) + { + // For compatibility, don't echo repeated decimals + break; + } + // If the format has trailing zeros or the format has a decimal and digits remain + if (lastDigit < 0 || (decimalPos < digitCount && *cur != 0)) + { + sb.Append(info.NumberDecimalSeparator); + decimalWritten = true; + } + break; + } + case '\x2030': + sb.Append(info.PerMilleSymbol); + break; + case '%': + sb.Append(info.PercentSymbol); + break; + case ',': + break; + case '\'': + case '"': + while (src < format.Length && pFormat[src] != 0 && pFormat[src] != ch) + sb.Append(pFormat[src++]); + if (src < format.Length && pFormat[src] != 0) + src++; + break; + case '\\': + if (src < format.Length && pFormat[src] != 0) + sb.Append(pFormat[src++]); + break; + case 'E': + case 'e': + { + bool positiveSign = false; + int i = 0; + if (scientific) + { + if (src < format.Length && pFormat[src] == '0') + { + // Handles E0, which should format the same as E-0 + i++; + } + else if (src+1 < format.Length && pFormat[src] == '+' && pFormat[src + 1] == '0') + { + // Handles E+0 + positiveSign = true; + } + else if (src+1 < format.Length && pFormat[src] == '-' && pFormat[src + 1] == '0') + { + // Handles E-0 + // Do nothing, this is just a place holder s.t. we don't break out of the loop. + } + else + { + sb.Append(ch); + break; + } + + while (++src < format.Length && pFormat[src] == '0') + i++; + if (i > 10) + i = 10; + + int exp = dig[0] == 0 ? 0 : number.scale - decimalPos; + FormatExponent(ref sb, info, exp, ch, i, positiveSign); + scientific = false; + } + else + { + sb.Append(ch); // Copy E or e to output + if (src < format.Length) + { + if (pFormat[src] == '+' || pFormat[src] == '-') + sb.Append(pFormat[src++]); + while (src < format.Length && pFormat[src] == '0') + sb.Append(pFormat[src++]); + } + } + break; + } + default: + sb.Append(ch); + break; + } + } + } + } + + private static void FormatCurrency(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info) + { + string fmt = number.sign ? + s_negCurrencyFormats[info.CurrencyNegativePattern] : + s_posCurrencyFormats[info.CurrencyPositivePattern]; + + foreach (char ch in fmt) + { + switch (ch) + { + case '#': + FormatFixed(ref sb, ref number, nMinDigits, nMaxDigits, info, info.currencyGroupSizes, info.CurrencyDecimalSeparator, info.CurrencyGroupSeparator); + break; + case '-': + sb.Append(info.NegativeSign); + break; + case '$': + sb.Append(info.CurrencySymbol); + break; + default: + sb.Append(ch); + break; + } + } + } + + private static unsafe void FormatFixed(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, int[] groupDigits, string sDecimal, string sGroup) + { + int digPos = number.scale; + char* dig = number.digits; + + if (digPos > 0) + { + if (groupDigits != null) + { + int groupSizeIndex = 0; // Index into the groupDigits array. + int groupSizeCount = groupDigits[groupSizeIndex]; // The current total of group size. + int bufferSize = digPos; // The length of the result buffer string. + int groupSize = 0; // The current group size. + + // Find out the size of the string buffer for the result. + if (groupDigits.Length != 0) // You can pass in 0 length arrays + { + while (digPos > groupSizeCount) + { + groupSize = groupDigits[groupSizeIndex]; + if (groupSize == 0) + break; + + bufferSize += sGroup.Length; + if (groupSizeIndex < groupDigits.Length - 1) + groupSizeIndex++; + + groupSizeCount += groupDigits[groupSizeIndex]; + if (groupSizeCount < 0 || bufferSize < 0) + throw new ArgumentOutOfRangeException(); // If we overflow + } + + groupSize = groupSizeCount == 0 ? 0 : groupDigits[0]; // If you passed in an array with one entry as 0, groupSizeCount == 0 + } + + groupSizeIndex = 0; + int digitCount = 0; + int digLength = string.wcslen(dig); + int digStart = (digPos < digLength) ? digPos : digLength; + fixed (char* spanPtr = &MemoryMarshal.GetReference(sb.AppendSpan(bufferSize))) + { + char* p = spanPtr + bufferSize - 1; + for (int i = digPos - 1; i >= 0; i--) + { + *(p--) = (i < digStart) ? dig[i] : '0'; + + if (groupSize > 0) + { + digitCount++; + if ((digitCount == groupSize) && (i != 0)) + { + for (int j = sGroup.Length - 1; j >= 0; j--) + *(p--) = sGroup[j]; + + if (groupSizeIndex < groupDigits.Length - 1) + { + groupSizeIndex++; + groupSize = groupDigits[groupSizeIndex]; + } + digitCount = 0; + } + } + } + + Debug.Assert(p >= spanPtr - 1, "Underflow"); + dig += digStart; + } + } + else + { + do + { + sb.Append(*dig != 0 ? *dig++ : '0'); + } + while (--digPos > 0); + } + } + else + { + sb.Append('0'); + } + + if (nMaxDigits > 0) + { + sb.Append(sDecimal); + if ((digPos < 0) && (nMaxDigits > 0)) + { + int zeroes = Math.Min(-digPos, nMaxDigits); + sb.Append('0', zeroes); + digPos += zeroes; + nMaxDigits -= zeroes; + } + + while (nMaxDigits > 0) + { + sb.Append((*dig != 0) ? *dig++ : '0'); + nMaxDigits--; + } + } + } + + private static void FormatNumber(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info) + { + string fmt = number.sign ? + s_negNumberFormats[info.NumberNegativePattern] : + PosNumberFormat; + + foreach (char ch in fmt) + { + switch (ch) + { + case '#': + FormatFixed(ref sb, ref number, nMinDigits, nMaxDigits, info, info.numberGroupSizes, info.NumberDecimalSeparator, info.NumberGroupSeparator); + break; + case '-': + sb.Append(info.NegativeSign); + break; + default: + sb.Append(ch); + break; + } + } + } + + private static unsafe void FormatScientific(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, char expChar) + { + char* dig = number.digits; + + sb.Append((*dig != 0) ? *dig++ : '0'); + + if (nMaxDigits != 1) // For E0 we would like to suppress the decimal point + sb.Append(info.NumberDecimalSeparator); + + while (--nMaxDigits > 0) + sb.Append((*dig != 0) ? *dig++ : '0'); + + int e = number.digits[0] == 0 ? 0 : number.scale - 1; + FormatExponent(ref sb, info, e, expChar, 3, true); + } + + private static unsafe void FormatExponent(ref ValueStringBuilder sb, NumberFormatInfo info, int value, char expChar, int minDigits, bool positiveSign) + { + sb.Append(expChar); + + if (value < 0) + { + sb.Append(info.NegativeSign); + value = -value; + } + else + { + if (positiveSign) + sb.Append(info.PositiveSign); + } + + char* digits = stackalloc char[MaxUInt32DecDigits]; + char* p = UInt32ToDecChars(digits + MaxUInt32DecDigits, (uint)value, minDigits); + int i = (int)(digits + MaxUInt32DecDigits - p); + sb.Append(p, (int)(digits + MaxUInt32DecDigits - p)); + } + + private static unsafe void FormatGeneral(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, char expChar, bool bSuppressScientific) + { + int digPos = number.scale; + bool scientific = false; + + if (!bSuppressScientific) + { + // Don't switch to scientific notation + if (digPos > nMaxDigits || digPos < -3) + { + digPos = 1; + scientific = true; + } + } + + char* dig = number.digits; + + if (digPos > 0) + { + do + { + sb.Append((*dig != 0) ? *dig++ : '0'); + } while (--digPos > 0); + } + else + { + sb.Append('0'); + } + + if (*dig != 0 || digPos < 0) + { + sb.Append(info.NumberDecimalSeparator); + + while (digPos < 0) + { + sb.Append('0'); + digPos++; + } + + while (*dig != 0) + sb.Append(*dig++); + } + + if (scientific) + FormatExponent(ref sb, info, number.scale - 1, expChar, 2, true); + } + + private static void FormatPercent(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info) + { + string fmt = number.sign ? + s_negPercentFormats[info.PercentNegativePattern] : + s_posPercentFormats[info.PercentPositivePattern]; + + foreach (char ch in fmt) + { + switch (ch) + { + case '#': + FormatFixed(ref sb, ref number, nMinDigits, nMaxDigits, info, info.percentGroupSizes, info.PercentDecimalSeparator, info.PercentGroupSeparator); + break; + case '-': + sb.Append(info.NegativeSign); + break; + case '%': + sb.Append(info.PercentSymbol); + break; + default: + sb.Append(ch); + break; + } + } + } + + private static unsafe void RoundNumber(ref NumberBuffer number, int pos) + { + char* dig = number.digits; + + int i = 0; + while (i < pos && dig[i] != 0) + i++; + + if (i == pos && dig[i] >= '5') + { + while (i > 0 && dig[i - 1] == '9') + i--; + + if (i > 0) + { + dig[i - 1]++; + } + else + { + number.scale++; + dig[0] = '1'; + i = 1; + } + } + else + { + while (i > 0 && dig[i - 1] == '0') + i--; + } + if (i == 0) + { + number.scale = 0; + number.sign = false; + } + dig[i] = '\0'; + } + + private static unsafe int FindSection(ReadOnlySpan format, int section) + { + int src; + char ch; + + if (section == 0) + return 0; + + fixed (char* pFormat = &MemoryMarshal.GetReference(format)) + { + src = 0; + for (;;) + { + if (src >= format.Length) + { + return 0; + } + + switch (ch = pFormat[src++]) + { + case '\'': + case '"': + while (src < format.Length && pFormat[src] != 0 && pFormat[src++] != ch) + ; + break; + case '\\': + if (src < format.Length && pFormat[src] != 0) + src++; + break; + case ';': + if (--section != 0) + break; + if (src < format.Length && pFormat[src] != 0 && pFormat[src] != ';') + return src; + goto case '\0'; + case '\0': + return 0; + } + } + } + } + + private static uint Low32(ulong value) => (uint)value; + + private static uint High32(ulong value) => (uint)((value & 0xFFFFFFFF00000000) >> 32); + + private static uint Int64DivMod1E9(ref ulong value) + { + uint rem = (uint)(value % 1000000000); + value /= 1000000000; + return rem; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Number.NumberBuffer.cs b/external/corefx/src/Common/src/CoreLib/System/Number.NumberBuffer.cs new file mode 100644 index 0000000000..2316f99bd3 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Number.NumberBuffer.cs @@ -0,0 +1,31 @@ +// 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.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using Internal.Runtime.CompilerServices; + +namespace System +{ + internal static partial class Number + { + private const int NumberMaxDigits = 50; // needs to == NUMBER_MAXDIGITS in coreclr's src/classlibnative/bcltype/number.h. + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal unsafe ref struct NumberBuffer // needs to match layout of NUMBER in coreclr's src/classlibnative/bcltype/number.h + { + public int precision; + public int scale; + private int _sign; + private DigitsAndNullTerminator _digits; + private char* _allDigits; + + public bool sign { get => _sign != 0; set => _sign = value ? 1 : 0; } + public char* digits => (char*)Unsafe.AsPointer(ref _digits); + + [StructLayout(LayoutKind.Sequential, Size = (NumberMaxDigits + 1) * sizeof(char))] + private struct DigitsAndNullTerminator { } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Number.Parsing.cs b/external/corefx/src/Common/src/CoreLib/System/Number.Parsing.cs new file mode 100644 index 0000000000..46951094eb --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Number.Parsing.cs @@ -0,0 +1,964 @@ +// 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.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; + +namespace System +{ + // The Parse methods provided by the numeric classes convert a + // string to a numeric value. The optional style parameter specifies the + // permitted style of the numeric string. It must be a combination of bit flags + // from the NumberStyles enumeration. The optional info parameter + // specifies the NumberFormatInfo instance to use when parsing the + // string. If the info parameter is null or omitted, the numeric + // formatting information is obtained from the current culture. + // + // Numeric strings produced by the Format methods using the Currency, + // Decimal, Engineering, Fixed point, General, or Number standard formats + // (the C, D, E, F, G, and N format specifiers) are guaranteed to be parseable + // by the Parse methods if the NumberStyles.Any style is + // specified. Note, however, that the Parse methods do not accept + // NaNs or Infinities. + + internal partial class Number + { + private const int Int32Precision = 10; + private const int UInt32Precision = Int32Precision; + private const int Int64Precision = 19; + private const int UInt64Precision = 20; + + private static bool HexNumberToInt32(ref NumberBuffer number, ref int value) + { + uint passedValue = 0; + bool returnValue = HexNumberToUInt32(ref number, ref passedValue); + value = (int)passedValue; + return returnValue; + } + + private static bool HexNumberToInt64(ref NumberBuffer number, ref long value) + { + ulong passedValue = 0; + bool returnValue = HexNumberToUInt64(ref number, ref passedValue); + value = (long)passedValue; + return returnValue; + } + + private static unsafe bool HexNumberToUInt32(ref NumberBuffer number, ref uint value) + { + int i = number.scale; + if (i > UInt32Precision || i < number.precision) + { + return false; + } + char* p = number.digits; + Debug.Assert(p != null); + + uint n = 0; + while (--i >= 0) + { + if (n > ((uint)0xFFFFFFFF / 16)) + { + return false; + } + n *= 16; + if (*p != '\0') + { + uint newN = n; + if (*p != '\0') + { + if (*p >= '0' && *p <= '9') + { + newN += (uint)(*p - '0'); + } + else + { + if (*p >= 'A' && *p <= 'F') + { + newN += (uint)((*p - 'A') + 10); + } + else + { + Debug.Assert(*p >= 'a' && *p <= 'f'); + newN += (uint)((*p - 'a') + 10); + } + } + p++; + } + + // Detect an overflow here... + if (newN < n) + { + return false; + } + n = newN; + } + } + value = n; + return true; + } + + private static unsafe bool HexNumberToUInt64(ref NumberBuffer number, ref ulong value) + { + int i = number.scale; + if (i > UInt64Precision || i < number.precision) + { + return false; + } + char* p = number.digits; + Debug.Assert(p != null); + + ulong n = 0; + while (--i >= 0) + { + if (n > (0xFFFFFFFFFFFFFFFF / 16)) + { + return false; + } + n *= 16; + if (*p != '\0') + { + ulong newN = n; + if (*p != '\0') + { + if (*p >= '0' && *p <= '9') + { + newN += (ulong)(*p - '0'); + } + else + { + if (*p >= 'A' && *p <= 'F') + { + newN += (ulong)((*p - 'A') + 10); + } + else + { + Debug.Assert(*p >= 'a' && *p <= 'f'); + newN += (ulong)((*p - 'a') + 10); + } + } + p++; + } + + // Detect an overflow here... + if (newN < n) + { + return false; + } + n = newN; + } + } + value = n; + return true; + } + + private static unsafe bool NumberToInt32(ref NumberBuffer number, ref int value) + { + int i = number.scale; + if (i > Int32Precision || i < number.precision) + { + return false; + } + char* p = number.digits; + Debug.Assert(p != null); + int n = 0; + while (--i >= 0) + { + if ((uint)n > (0x7FFFFFFF / 10)) + { + return false; + } + n *= 10; + if (*p != '\0') + { + n += (int)(*p++ - '0'); + } + } + if (number.sign) + { + n = -n; + if (n > 0) + { + return false; + } + } + else + { + if (n < 0) + { + return false; + } + } + value = n; + return true; + } + + private static unsafe bool NumberToInt64(ref NumberBuffer number, ref long value) + { + int i = number.scale; + if (i > Int64Precision || i < number.precision) + { + return false; + } + char* p = number.digits; + Debug.Assert(p != null); + long n = 0; + while (--i >= 0) + { + if ((ulong)n > (0x7FFFFFFFFFFFFFFF / 10)) + { + return false; + } + n *= 10; + if (*p != '\0') + { + n += (int)(*p++ - '0'); + } + } + if (number.sign) + { + n = -n; + if (n > 0) + { + return false; + } + } + else + { + if (n < 0) + { + return false; + } + } + value = n; + return true; + } + + private static unsafe bool NumberToUInt32(ref NumberBuffer number, ref uint value) + { + int i = number.scale; + if (i > UInt32Precision || i < number.precision || number.sign) + { + return false; + } + char* p = number.digits; + Debug.Assert(p != null); + uint n = 0; + while (--i >= 0) + { + if (n > (0xFFFFFFFF / 10)) + { + return false; + } + n *= 10; + if (*p != '\0') + { + uint newN = n + (uint)(*p++ - '0'); + // Detect an overflow here... + if (newN < n) + { + return false; + } + n = newN; + } + } + value = n; + return true; + } + + private static unsafe bool NumberToUInt64(ref NumberBuffer number, ref ulong value) + { + int i = number.scale; + if (i > UInt64Precision || i < number.precision || number.sign) + { + return false; + } + char* p = number.digits; + Debug.Assert(p != null); + ulong n = 0; + while (--i >= 0) + { + if (n > (0xFFFFFFFFFFFFFFFF / 10)) + { + return false; + } + n *= 10; + if (*p != '\0') + { + ulong newN = n + (ulong)(*p++ - '0'); + // Detect an overflow here... + if (newN < n) + { + return false; + } + n = newN; + } + } + value = n; + return true; + } + + internal unsafe static int ParseInt32(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info) + { + NumberBuffer number = default; + int i = 0; + + StringToNumber(s, style, ref number, info, false); + + if ((style & NumberStyles.AllowHexSpecifier) != 0) + { + if (!HexNumberToInt32(ref number, ref i)) + { + throw new OverflowException(SR.Overflow_Int32); + } + } + else + { + if (!NumberToInt32(ref number, ref i)) + { + throw new OverflowException(SR.Overflow_Int32); + } + } + return i; + } + + internal unsafe static long ParseInt64(ReadOnlySpan value, NumberStyles options, NumberFormatInfo numfmt) + { + NumberBuffer number = default; + long i = 0; + + StringToNumber(value, options, ref number, numfmt, false); + + if ((options & NumberStyles.AllowHexSpecifier) != 0) + { + if (!HexNumberToInt64(ref number, ref i)) + { + throw new OverflowException(SR.Overflow_Int64); + } + } + else + { + if (!NumberToInt64(ref number, ref i)) + { + throw new OverflowException(SR.Overflow_Int64); + } + } + return i; + } + + internal unsafe static uint ParseUInt32(ReadOnlySpan value, NumberStyles options, NumberFormatInfo numfmt) + { + NumberBuffer number = default; + uint i = 0; + + StringToNumber(value, options, ref number, numfmt, false); + + if ((options & NumberStyles.AllowHexSpecifier) != 0) + { + if (!HexNumberToUInt32(ref number, ref i)) + { + throw new OverflowException(SR.Overflow_UInt32); + } + } + else + { + if (!NumberToUInt32(ref number, ref i)) + { + throw new OverflowException(SR.Overflow_UInt32); + } + } + + return i; + } + + internal unsafe static ulong ParseUInt64(ReadOnlySpan value, NumberStyles options, NumberFormatInfo numfmt) + { + NumberBuffer number = default; + ulong i = 0; + + StringToNumber(value, options, ref number, numfmt, false); + if ((options & NumberStyles.AllowHexSpecifier) != 0) + { + if (!HexNumberToUInt64(ref number, ref i)) + { + throw new OverflowException(SR.Overflow_UInt64); + } + } + else + { + if (!NumberToUInt64(ref number, ref i)) + { + throw new OverflowException(SR.Overflow_UInt64); + } + } + return i; + } + + private unsafe static bool ParseNumber(ref char* str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal) + { + const int StateSign = 0x0001; + const int StateParens = 0x0002; + const int StateDigits = 0x0004; + const int StateNonZero = 0x0008; + const int StateDecimal = 0x0010; + const int StateCurrency = 0x0020; + + number.scale = 0; + number.sign = false; + string decSep; // decimal separator from NumberFormatInfo. + string groupSep; // group separator from NumberFormatInfo. + string currSymbol = null; // currency symbol from NumberFormatInfo. + + bool parsingCurrency = false; + if ((options & NumberStyles.AllowCurrencySymbol) != 0) + { + currSymbol = numfmt.CurrencySymbol; + + // The idea here is to match the currency separators and on failure match the number separators to keep the perf of VB's IsNumeric fast. + // The values of decSep are setup to use the correct relevant separator (currency in the if part and decimal in the else part). + decSep = numfmt.CurrencyDecimalSeparator; + groupSep = numfmt.CurrencyGroupSeparator; + parsingCurrency = true; + } + else + { + decSep = numfmt.NumberDecimalSeparator; + groupSep = numfmt.NumberGroupSeparator; + } + + int state = 0; + char* p = str; + char ch = *p; + char* next; + + while (true) + { + // Eat whitespace unless we've found a sign which isn't followed by a currency symbol. + // "-Kr 1231.47" is legal but "- 1231.47" is not. + if (!IsWhite(ch) || (options & NumberStyles.AllowLeadingWhite) == 0 || ((state & StateSign) != 0 && ((state & StateCurrency) == 0 && numfmt.NumberNegativePattern != 2))) + { + if ((((options & NumberStyles.AllowLeadingSign) != 0) && (state & StateSign) == 0) && ((next = MatchChars(p, numfmt.PositiveSign)) != null || ((next = MatchChars(p, numfmt.NegativeSign)) != null && (number.sign = true)))) + { + state |= StateSign; + p = next - 1; + } + else if (ch == '(' && ((options & NumberStyles.AllowParentheses) != 0) && ((state & StateSign) == 0)) + { + state |= StateSign | StateParens; + number.sign = true; + } + else if (currSymbol != null && (next = MatchChars(p, currSymbol)) != null) + { + state |= StateCurrency; + currSymbol = null; + // We already found the currency symbol. There should not be more currency symbols. Set + // currSymbol to NULL so that we won't search it again in the later code path. + p = next - 1; + } + else + { + break; + } + } + ch = *++p; + } + int digCount = 0; + int digEnd = 0; + while (true) + { + if ((ch >= '0' && ch <= '9') || (((options & NumberStyles.AllowHexSpecifier) != 0) && ((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')))) + { + state |= StateDigits; + + if (ch != '0' || (state & StateNonZero) != 0) + { + if (digCount < NumberMaxDigits) + { + number.digits[digCount++] = ch; + if (ch != '0' || parseDecimal) + { + digEnd = digCount; + } + } + if ((state & StateDecimal) == 0) + { + number.scale++; + } + state |= StateNonZero; + } + else if ((state & StateDecimal) != 0) + { + number.scale--; + } + } + else if (((options & NumberStyles.AllowDecimalPoint) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, decSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, numfmt.NumberDecimalSeparator)) != null)) + { + state |= StateDecimal; + p = next - 1; + } + else if (((options & NumberStyles.AllowThousands) != 0) && ((state & StateDigits) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, groupSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, numfmt.NumberGroupSeparator)) != null)) + { + p = next - 1; + } + else + { + break; + } + ch = *++p; + } + + bool negExp = false; + number.precision = digEnd; + number.digits[digEnd] = '\0'; + if ((state & StateDigits) != 0) + { + if ((ch == 'E' || ch == 'e') && ((options & NumberStyles.AllowExponent) != 0)) + { + char* temp = p; + ch = *++p; + if ((next = MatchChars(p, numfmt.positiveSign)) != null) + { + ch = *(p = next); + } + else if ((next = MatchChars(p, numfmt.negativeSign)) != null) + { + ch = *(p = next); + negExp = true; + } + if (ch >= '0' && ch <= '9') + { + int exp = 0; + do + { + exp = exp * 10 + (ch - '0'); + ch = *++p; + if (exp > 1000) + { + exp = 9999; + while (ch >= '0' && ch <= '9') + { + ch = *++p; + } + } + } while (ch >= '0' && ch <= '9'); + if (negExp) + { + exp = -exp; + } + number.scale += exp; + } + else + { + p = temp; + ch = *p; + } + } + while (true) + { + if (!IsWhite(ch) || (options & NumberStyles.AllowTrailingWhite) == 0) + { + if (((options & NumberStyles.AllowTrailingSign) != 0 && ((state & StateSign) == 0)) && ((next = MatchChars(p, numfmt.PositiveSign)) != null || (((next = MatchChars(p, numfmt.NegativeSign)) != null) && (number.sign = true)))) + { + state |= StateSign; + p = next - 1; + } + else if (ch == ')' && ((state & StateParens) != 0)) + { + state &= ~StateParens; + } + else if (currSymbol != null && (next = MatchChars(p, currSymbol)) != null) + { + currSymbol = null; + p = next - 1; + } + else + { + break; + } + } + ch = *++p; + } + if ((state & StateParens) == 0) + { + if ((state & StateNonZero) == 0) + { + if (!parseDecimal) + { + number.scale = 0; + } + if ((state & StateDecimal) == 0) + { + number.sign = false; + } + } + str = p; + return true; + } + } + str = p; + return false; + } + + internal unsafe static bool TryParseInt32(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out int result) + { + NumberBuffer number = default; + result = 0; + + if (!TryStringToNumber(s, style, ref number, info, false)) + { + return false; + } + + if ((style & NumberStyles.AllowHexSpecifier) != 0) + { + if (!HexNumberToInt32(ref number, ref result)) + { + return false; + } + } + else + { + if (!NumberToInt32(ref number, ref result)) + { + return false; + } + } + return true; + } + + internal unsafe static bool TryParseInt64(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out long result) + { + NumberBuffer number = default; + result = 0; + + if (!TryStringToNumber(s, style, ref number, info, false)) + { + return false; + } + + if ((style & NumberStyles.AllowHexSpecifier) != 0) + { + if (!HexNumberToInt64(ref number, ref result)) + { + return false; + } + } + else + { + if (!NumberToInt64(ref number, ref result)) + { + return false; + } + } + return true; + } + + internal unsafe static bool TryParseUInt32(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out uint result) + { + NumberBuffer number = default; + result = 0; + + if (!TryStringToNumber(s, style, ref number, info, false)) + { + return false; + } + + if ((style & NumberStyles.AllowHexSpecifier) != 0) + { + if (!HexNumberToUInt32(ref number, ref result)) + { + return false; + } + } + else + { + if (!NumberToUInt32(ref number, ref result)) + { + return false; + } + } + return true; + } + + internal unsafe static bool TryParseUInt64(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out ulong result) + { + NumberBuffer number = default; + result = 0; + + if (!TryStringToNumber(s, style, ref number, info, false)) + { + return false; + } + + if ((style & NumberStyles.AllowHexSpecifier) != 0) + { + if (!HexNumberToUInt64(ref number, ref result)) + { + return false; + } + } + else + { + if (!NumberToUInt64(ref number, ref result)) + { + return false; + } + } + return true; + } + + internal unsafe static decimal ParseDecimal(ReadOnlySpan value, NumberStyles options, NumberFormatInfo numfmt) + { + NumberBuffer number = default; + decimal result = 0; + + StringToNumber(value, options, ref number, numfmt, true); + + if (!NumberBufferToDecimal(ref number, ref result)) + { + throw new OverflowException(SR.Overflow_Decimal); + } + return result; + } + + internal unsafe static double ParseDouble(ReadOnlySpan value, NumberStyles options, NumberFormatInfo numfmt) + { + NumberBuffer number = default; + double d = 0; + + if (!TryStringToNumber(value, options, ref number, numfmt, false)) + { + //If we failed TryStringToNumber, it may be from one of our special strings. + //Check the three with which we're concerned and rethrow if it's not one of + //those strings. + ReadOnlySpan sTrim = value.Trim(); + if (StringSpanHelpers.Equals(sTrim, numfmt.PositiveInfinitySymbol)) + { + return double.PositiveInfinity; + } + if (StringSpanHelpers.Equals(sTrim, numfmt.NegativeInfinitySymbol)) + { + return double.NegativeInfinity; + } + if (StringSpanHelpers.Equals(sTrim, numfmt.NaNSymbol)) + { + return double.NaN; + } + throw new FormatException(SR.Format_InvalidString); + } + + if (!NumberBufferToDouble(ref number, ref d)) + { + throw new OverflowException(SR.Overflow_Double); + } + + return d; + } + + internal unsafe static float ParseSingle(ReadOnlySpan value, NumberStyles options, NumberFormatInfo numfmt) + { + NumberBuffer number = default; + double d = 0; + + if (!TryStringToNumber(value, options, ref number, numfmt, false)) + { + //If we failed TryStringToNumber, it may be from one of our special strings. + //Check the three with which we're concerned and rethrow if it's not one of + //those strings. + ReadOnlySpan sTrim = value.Trim(); + if (StringSpanHelpers.Equals(sTrim, numfmt.PositiveInfinitySymbol)) + { + return float.PositiveInfinity; + } + if (StringSpanHelpers.Equals(sTrim, numfmt.NegativeInfinitySymbol)) + { + return float.NegativeInfinity; + } + if (StringSpanHelpers.Equals(sTrim, numfmt.NaNSymbol)) + { + return float.NaN; + } + throw new FormatException(SR.Format_InvalidString); + } + + if (!NumberBufferToDouble(ref number, ref d)) + { + throw new OverflowException(SR.Overflow_Single); + } + float castSingle = (float)d; + if (float.IsInfinity(castSingle)) + { + throw new OverflowException(SR.Overflow_Single); + } + return castSingle; + } + + internal unsafe static bool TryParseDecimal(ReadOnlySpan value, NumberStyles options, NumberFormatInfo numfmt, out decimal result) + { + NumberBuffer number = default; + result = 0; + + if (!TryStringToNumber(value, options, ref number, numfmt, true)) + { + return false; + } + + if (!NumberBufferToDecimal(ref number, ref result)) + { + return false; + } + return true; + } + + internal unsafe static bool TryParseDouble(ReadOnlySpan value, NumberStyles options, NumberFormatInfo numfmt, out double result) + { + NumberBuffer number = default; + result = 0; + + + if (!TryStringToNumber(value, options, ref number, numfmt, false)) + { + return false; + } + if (!NumberBufferToDouble(ref number, ref result)) + { + return false; + } + return true; + } + + internal unsafe static bool TryParseSingle(ReadOnlySpan value, NumberStyles options, NumberFormatInfo numfmt, out float result) + { + NumberBuffer number = default; + result = 0; + double d = 0; + + if (!TryStringToNumber(value, options, ref number, numfmt, false)) + { + return false; + } + if (!NumberBufferToDouble(ref number, ref d)) + { + return false; + } + float castSingle = (float)d; + if (float.IsInfinity(castSingle)) + { + return false; + } + + result = castSingle; + return true; + } + + private static unsafe void StringToNumber(ReadOnlySpan str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal) + { + Debug.Assert(info != null); + fixed (char* stringPointer = &MemoryMarshal.GetReference(str)) + { + char* p = stringPointer; + if (!ParseNumber(ref p, options, ref number, info, parseDecimal) + || (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer)))) + { + throw new FormatException(SR.Format_InvalidString); + } + } + } + + internal static unsafe bool TryStringToNumber(ReadOnlySpan str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal) + { + Debug.Assert(numfmt != null); + fixed (char* stringPointer = &MemoryMarshal.GetReference(str)) + { + char* p = stringPointer; + if (!ParseNumber(ref p, options, ref number, numfmt, parseDecimal) + || (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer)))) + { + return false; + } + } + + return true; + } + + private static bool TrailingZeros(ReadOnlySpan s, int index) + { + // For compatibility, we need to allow trailing zeros at the end of a number string + for (int i = index; i < s.Length; i++) + { + if (s[i] != '\0') + { + return false; + } + } + + return true; + } + + private unsafe static char* MatchChars(char* p, string str) + { + fixed (char* stringPointer = str) + { + return MatchChars(p, stringPointer); + } + } + + private unsafe static char* MatchChars(char* p, char* str) + { + Debug.Assert(p != null && str != null); + + if (*str == '\0') + { + return null; + } + + // We only hurt the failure case + // This fix is for French or Kazakh cultures. Since a user cannot type 0xA0 as a + // space character we use 0x20 space character instead to mean the same. + while (*p == *str || (*str == '\u00a0' && *p == '\u0020')) + { + p++; + str++; + if (*str == '\0') return p; + } + + return null; + } + + private static bool IsWhite(char ch) => ch == 0x20 || (ch >= 0x09 && ch <= 0x0D); + + private static bool NumberBufferToDouble(ref NumberBuffer number, ref double value) + { + double d = NumberToDouble(ref number); + uint e = DoubleHelper.Exponent(d); + ulong m = DoubleHelper.Mantissa(d); + + if (e == 0x7FF) + { + return false; + } + + if (e == 0 && m == 0) + { + d = 0; + } + + value = d; + return true; + } + + private static class DoubleHelper + { + public static unsafe uint Exponent(double d) => + (*((uint*)&d + 1) >> 20) & 0x000007ff; + + public static unsafe ulong Mantissa(double d) => + *((ulong*)&d) & 0x000fffffffffffff; + + public static unsafe bool Sign(double d) => + (*((uint*)&d + 1) >> 31) != 0; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/ObjectDisposedException.cs b/external/corefx/src/Common/src/CoreLib/System/ObjectDisposedException.cs new file mode 100644 index 0000000000..3daed13bb6 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ObjectDisposedException.cs @@ -0,0 +1,83 @@ +// 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.Globalization; +using System.Runtime.Serialization; + +namespace System +{ + /// + /// The exception that is thrown when accessing an object that was + /// disposed. + /// + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class ObjectDisposedException : InvalidOperationException + { + private String _objectName; + + // This constructor should only be called by the EE (COMPlusThrow) + private ObjectDisposedException() : + this(null, SR.ObjectDisposed_Generic) + { + } + + public ObjectDisposedException(String objectName) : + this(objectName, SR.ObjectDisposed_Generic) + { + } + + public ObjectDisposedException(String objectName, String message) : base(message) + { + HResult = HResults.COR_E_OBJECTDISPOSED; + _objectName = objectName; + } + + public ObjectDisposedException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_OBJECTDISPOSED; + } + + protected ObjectDisposedException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + _objectName = info.GetString("ObjectName"); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("ObjectName", ObjectName, typeof(string)); + } + + /// + /// Gets the text for the message for this exception. + /// + public override String Message + { + get + { + String name = ObjectName; + if (name == null || name.Length == 0) + return base.Message; + + String objectDisposed = SR.Format(SR.ObjectDisposed_ObjectName_Name, name); + return base.Message + Environment.NewLine + objectDisposed; + } + } + + public String ObjectName + { + get + { + if (_objectName == null) + { + return String.Empty; + } + return _objectName; + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/ObsoleteAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/ObsoleteAttribute.cs new file mode 100644 index 0000000000..a63db137f8 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ObsoleteAttribute.cs @@ -0,0 +1,60 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: Attribute for functions, etc that will be removed. +** +** +===========================================================*/ + +using System; + +namespace System +{ + // This attribute is attached to members that are not to be used any longer. + // Message is some human readable explanation of what to use + // Error indicates if the compiler should treat usage of such a method as an + // error. (this would be used if the actual implementation of the obsolete + // method's implementation had changed). + // + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | + AttributeTargets.Interface | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Delegate + , Inherited = false)] + public sealed class ObsoleteAttribute : Attribute + { + private String _message; + private bool _error; + + public ObsoleteAttribute() + { + _message = null; + _error = false; + } + + public ObsoleteAttribute(String message) + { + _message = message; + _error = false; + } + + public ObsoleteAttribute(String message, bool error) + { + _message = message; + _error = error; + } + + public String Message + { + get { return _message; } + } + + public bool IsError + { + get { return _error; } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/OperationCanceledException.cs b/external/corefx/src/Common/src/CoreLib/System/OperationCanceledException.cs new file mode 100644 index 0000000000..8a472c9ff0 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/OperationCanceledException.cs @@ -0,0 +1,74 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: Exception for cancelled IO requests. +** +** +===========================================================*/ + +using System; +using System.Runtime.Serialization; +using System.Threading; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class OperationCanceledException : SystemException + { + [NonSerialized] + private CancellationToken _cancellationToken; + + public CancellationToken CancellationToken + { + get { return _cancellationToken; } + private set { _cancellationToken = value; } + } + + public OperationCanceledException() + : base(SR.OperationCanceled) + { + HResult = HResults.COR_E_OPERATIONCANCELED; + } + + public OperationCanceledException(String message) + : base(message) + { + HResult = HResults.COR_E_OPERATIONCANCELED; + } + + public OperationCanceledException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_OPERATIONCANCELED; + } + + + public OperationCanceledException(CancellationToken token) + : this() + { + CancellationToken = token; + } + + public OperationCanceledException(String message, CancellationToken token) + : this(message) + { + CancellationToken = token; + } + + public OperationCanceledException(String message, Exception innerException, CancellationToken token) + : this(message, innerException) + { + CancellationToken = token; + } + + protected OperationCanceledException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/OverflowException.cs b/external/corefx/src/Common/src/CoreLib/System/OverflowException.cs new file mode 100644 index 0000000000..963825b350 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/OverflowException.cs @@ -0,0 +1,44 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: Exception class for Arthimatic Overflows. +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class OverflowException : ArithmeticException + { + public OverflowException() + : base(SR.Arg_OverflowException) + { + HResult = HResults.COR_E_OVERFLOW; + } + + public OverflowException(String message) + : base(message) + { + HResult = HResults.COR_E_OVERFLOW; + } + + public OverflowException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_OVERFLOW; + } + + protected OverflowException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/ParamArrayAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/ParamArrayAttribute.cs new file mode 100644 index 0000000000..d3c3d46d56 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ParamArrayAttribute.cs @@ -0,0 +1,21 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: Attribute for multiple parameters. +** +** +=============================================================================*/ + +namespace System +{ + [AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)] + public sealed class ParamArrayAttribute : Attribute + { + public ParamArrayAttribute() { } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/ParamsArray.cs b/external/corefx/src/Common/src/CoreLib/System/ParamsArray.cs new file mode 100644 index 0000000000..043ee679ee --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ParamsArray.cs @@ -0,0 +1,82 @@ +// 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. + +namespace System +{ + internal readonly struct ParamsArray + { + // Sentinel fixed-length arrays eliminate the need for a "count" field keeping this + // struct down to just 4 fields. These are only used for their "Length" property, + // that is, their elements are never set or referenced. + private static readonly object[] s_oneArgArray = new object[1]; + private static readonly object[] s_twoArgArray = new object[2]; + private static readonly object[] s_threeArgArray = new object[3]; + + private readonly object _arg0; + private readonly object _arg1; + private readonly object _arg2; + + // After construction, the first three elements of this array will never be accessed + // because the indexer will retrieve those values from arg0, arg1, and arg2. + private readonly object[] _args; + + public ParamsArray(object arg0) + { + _arg0 = arg0; + _arg1 = null; + _arg2 = null; + + // Always assign this.args to make use of its "Length" property + _args = s_oneArgArray; + } + + public ParamsArray(object arg0, object arg1) + { + _arg0 = arg0; + _arg1 = arg1; + _arg2 = null; + + // Always assign this.args to make use of its "Length" property + _args = s_twoArgArray; + } + + public ParamsArray(object arg0, object arg1, object arg2) + { + _arg0 = arg0; + _arg1 = arg1; + _arg2 = arg2; + + // Always assign this.args to make use of its "Length" property + _args = s_threeArgArray; + } + + public ParamsArray(object[] args) + { + int len = args.Length; + _arg0 = len > 0 ? args[0] : null; + _arg1 = len > 1 ? args[1] : null; + _arg2 = len > 2 ? args[2] : null; + _args = args; + } + + public int Length + { + get { return _args.Length; } + } + + public object this[int index] + { + get { return index == 0 ? _arg0 : GetAtSlow(index); } + } + + private object GetAtSlow(int index) + { + if (index == 1) + return _arg1; + if (index == 2) + return _arg2; + return _args[index]; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/ParseNumbers.cs b/external/corefx/src/Common/src/CoreLib/System/ParseNumbers.cs new file mode 100644 index 0000000000..dab4cb2190 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ParseNumbers.cs @@ -0,0 +1,664 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; + +namespace System +{ + /// Methods for parsing numbers and strings. + internal static class ParseNumbers + { + internal const int LeftAlign = 0x0001; + internal const int RightAlign = 0x0004; + internal const int PrefixSpace = 0x0008; + internal const int PrintSign = 0x0010; + internal const int PrintBase = 0x0020; + internal const int PrintAsI1 = 0x0040; + internal const int PrintAsI2 = 0x0080; + internal const int PrintAsI4 = 0x0100; + internal const int TreatAsUnsigned = 0x0200; + internal const int TreatAsI1 = 0x0400; + internal const int TreatAsI2 = 0x0800; + internal const int IsTight = 0x1000; + internal const int NoSpace = 0x2000; + internal const int PrintRadixBase = 0x4000; + + private const int MinRadix = 2; + private const int MaxRadix = 36; + + public static unsafe long StringToLong(ReadOnlySpan s, int radix, int flags) + { + int pos = 0; + return StringToLong(s, radix, flags, ref pos); + } + + public static long StringToLong(ReadOnlySpan s, int radix, int flags, ref int currPos) + { + int i = currPos; + + // Do some radix checking. + // A radix of -1 says to use whatever base is spec'd on the number. + // Parse in Base10 until we figure out what the base actually is. + int r = (-1 == radix) ? 10 : radix; + + if (r != 2 && r != 10 && r != 8 && r != 16) + throw new ArgumentException(SR.Arg_InvalidBase, nameof(radix)); + + int length = s.Length; + + if (i < 0 || i >= length) + throw new ArgumentOutOfRangeException(SR.ArgumentOutOfRange_Index); + + // Get rid of the whitespace and then check that we've still got some digits to parse. + if (((flags & IsTight) == 0) && ((flags & NoSpace) == 0)) + { + EatWhiteSpace(s, ref i); + if (i == length) + throw new FormatException(SR.Format_EmptyInputString); + } + + // Check for a sign + int sign = 1; + if (s[i] == '-') + { + if (r != 10) + throw new ArgumentException(SR.Arg_CannotHaveNegativeValue); + + if ((flags & TreatAsUnsigned) != 0) + throw new OverflowException(SR.Overflow_NegativeUnsigned); + + sign = -1; + i++; + } + else if (s[i] == '+') + { + i++; + } + + if ((radix == -1 || radix == 16) && (i + 1 < length) && s[i] == '0') + { + if (s[i + 1] == 'x' || s[i + 1] == 'X') + { + r = 16; + i += 2; + } + } + + int grabNumbersStart = i; + long result = GrabLongs(r, s, ref i, (flags & TreatAsUnsigned) != 0); + + // Check if they passed us a string with no parsable digits. + if (i == grabNumbersStart) + throw new FormatException(SR.Format_NoParsibleDigits); + + if ((flags & IsTight) != 0) + { + //If we've got effluvia left at the end of the string, complain. + if (i < length) + throw new FormatException(SR.Format_ExtraJunkAtEnd); + } + + // Put the current index back into the correct place. + currPos = i; + + // Return the value properly signed. + if ((ulong)result == 0x8000000000000000 && sign == 1 && r == 10 && ((flags & TreatAsUnsigned) == 0)) + throw new OverflowException(SR.Overflow_Int64); + + if (r == 10) + { + result *= sign; + } + + return result; + } + + public static int StringToInt(ReadOnlySpan s, int radix, int flags) + { + int pos = 0; + return StringToInt(s, radix, flags, ref pos); + } + + public static int StringToInt(ReadOnlySpan s, int radix, int flags, ref int currPos) + { + // They're requied to tell me where to start parsing. + int i = currPos; + + // Do some radix checking. + // A radix of -1 says to use whatever base is spec'd on the number. + // Parse in Base10 until we figure out what the base actually is. + int r = (-1 == radix) ? 10 : radix; + + if (r != 2 && r != 10 && r != 8 && r != 16) + throw new ArgumentException(SR.Arg_InvalidBase, nameof(radix)); + + int length = s.Length; + + if (i < 0 || i >= length) + throw new ArgumentOutOfRangeException(SR.ArgumentOutOfRange_Index); + + // Get rid of the whitespace and then check that we've still got some digits to parse. + if (((flags & IsTight) == 0) && ((flags & NoSpace) == 0)) + { + EatWhiteSpace(s, ref i); + if (i == length) + throw new FormatException(SR.Format_EmptyInputString); + } + + // Check for a sign + int sign = 1; + if (s[i] == '-') + { + if (r != 10) + throw new ArgumentException(SR.Arg_CannotHaveNegativeValue); + + if ((flags & TreatAsUnsigned) != 0) + throw new OverflowException(SR.Overflow_NegativeUnsigned); + + sign = -1; + i++; + } + else if (s[i] == '+') + { + i++; + } + + // Consume the 0x if we're in an unknown base or in base-16. + if ((radix == -1 || radix == 16) && (i + 1 < length) && s[i] == '0') + { + if (s[i + 1] == 'x' || s[i + 1] == 'X') + { + r = 16; + i += 2; + } + } + + int grabNumbersStart = i; + int result = GrabInts(r, s, ref i, ((flags & TreatAsUnsigned) != 0)); + + // Check if they passed us a string with no parsable digits. + if (i == grabNumbersStart) + throw new FormatException(SR.Format_NoParsibleDigits); + + if ((flags & IsTight) != 0) + { + // If we've got effluvia left at the end of the string, complain. + if (i < length) + throw new FormatException(SR.Format_ExtraJunkAtEnd); + } + + // Put the current index back into the correct place. + currPos = i; + + // Return the value properly signed. + if ((flags & TreatAsI1) != 0) + { + if ((uint)result > 0xFF) + throw new OverflowException(SR.Overflow_SByte); + } + else if ((flags & TreatAsI2) != 0) + { + if ((uint)result > 0xFFFF) + throw new OverflowException(SR.Overflow_Int16); + } + else if ((uint)result == 0x80000000 && sign == 1 && r == 10 && ((flags & TreatAsUnsigned) == 0)) + { + throw new OverflowException(SR.Overflow_Int32); + } + + if (r == 10) + { + result *= sign; + } + + return result; + } + + public static string IntToString(int n, int radix, int width, char paddingChar, int flags) + { + Span buffer = stackalloc char[66]; // Longest possible string length for an integer in binary notation with prefix + + if (radix < MinRadix || radix > MaxRadix) + throw new ArgumentException(SR.Arg_InvalidBase, nameof(radix)); + + // If the number is negative, make it positive and remember the sign. + // If the number is MIN_VALUE, this will still be negative, so we'll have to + // special case this later. + bool isNegative = false; + uint l; + if (n < 0) + { + isNegative = true; + + // For base 10, write out -num, but other bases write out the + // 2's complement bit pattern + l = (10 == radix) ? (uint)-n : (uint)n; + } + else + { + l = (uint)n; + } + + // The conversion to a uint will sign extend the number. In order to ensure + // that we only get as many bits as we expect, we chop the number. + if ((flags & PrintAsI1) != 0) + { + l &= 0xFF; + } + else if ((flags & PrintAsI2) != 0) + { + l &= 0xFFFF; + } + + // Special case the 0. + int index; + if (0 == l) + { + buffer[0] = '0'; + index = 1; + } + else + { + index = 0; + for (int i = 0; i < buffer.Length; i++) // for(...;i buffer = stackalloc char[67]; // Longest possible string length for an integer in binary notation with prefix + + if (radix < MinRadix || radix > MaxRadix) + throw new ArgumentException(SR.Arg_InvalidBase, nameof(radix)); + + //If the number is negative, make it positive and remember the sign. + ulong ul; + bool isNegative = false; + if (n < 0) + { + isNegative = true; + + // For base 10, write out -num, but other bases write out the + // 2's complement bit pattern + ul = (10 == radix) ? (ulong)(-n) : (ulong)n; + } + else + { + ul = (ulong)n; + } + + if ((flags & PrintAsI1) != 0) + { + ul = ul & 0xFF; + } + else if ((flags & PrintAsI2) != 0) + { + ul = ul & 0xFFFF; + } + else if ((flags & PrintAsI4) != 0) + { + ul = ul & 0xFFFFFFFF; + } + + //Special case the 0. + int index; + if (0 == ul) + { + buffer[0] = '0'; + index = 1; + } + else + { + index = 0; + for (int i = 0; i < buffer.Length; i++) // for loop instead of do{...}while(l!=0) to help JIT eliminate span bounds checks + { + ulong div = ul / (ulong)radix; // TODO https://github.com/dotnet/coreclr/issues/3439 + int charVal = (int)(ul - (div * (ulong)radix)); + ul = div; + + buffer[i] = (charVal < 10) ? + (char)(charVal + '0') : + (char)(charVal + 'a' - 10); + + if (ul == 0) + { + index = i + 1; + break; + } + } + Debug.Assert(ul == 0, $"Expected {ul} == 0"); + } + + //If they want the base, append that to the string (in reverse order) + if (radix != 10 && ((flags & PrintBase) != 0)) + { + if (16 == radix) + { + buffer[index++] = 'x'; + buffer[index++] = '0'; + } + else if (8 == radix) + { + buffer[index++] = '0'; + } + else if ((flags & PrintRadixBase) != 0) + { + buffer[index++] = '#'; + buffer[index++] = (char)((radix % 10) + '0'); + buffer[index++] = (char)((radix / 10) + '0'); + } + } + + if (10 == radix) + { + //If it was negative, append the sign. + if (isNegative) + { + buffer[index++] = '-'; + } + + //else if they requested, add the '+'; + else if ((flags & PrintSign) != 0) + { + buffer[index++] = '+'; + } + + //If they requested a leading space, put it on. + else if ((flags & PrefixSpace) != 0) + { + buffer[index++] = ' '; + } + } + + // Figure out the size of and allocate the resulting string + string result = string.FastAllocateString(Math.Max(width, index)); + unsafe + { + // Put the characters into the string in reverse order. + // Fill the remaining space, if there is any, with the correct padding character. + fixed (char* resultPtr = result) + { + char* p = resultPtr; + int padding = result.Length - index; + + if ((flags & LeftAlign) != 0) + { + for (int i = 0; i < padding; i++) + { + *p++ = paddingChar; + } + + for (int i = 0; i < index; i++) + { + *p++ = buffer[index - i - 1]; + } + } + else + { + for (int i = 0; i < index; i++) + { + *p++ = buffer[index - i - 1]; + } + + for (int i = 0; i < padding; i++) + { + *p++ = paddingChar; + } + } + + Debug.Assert((p - resultPtr) == result.Length, $"Expected {p - resultPtr} == {result.Length}"); + } + } + return result; + } + + private static void EatWhiteSpace(ReadOnlySpan s, ref int i) + { + int localIndex = i; + for (; localIndex < s.Length && char.IsWhiteSpace(s[localIndex]); localIndex++); + i = localIndex; + } + + private static long GrabLongs(int radix, ReadOnlySpan s, ref int i, bool isUnsigned) + { + ulong result = 0; + ulong maxVal; + + // Allow all non-decimal numbers to set the sign bit. + if (radix == 10 && !isUnsigned) + { + maxVal = 0x7FFFFFFFFFFFFFFF / 10; + + // Read all of the digits and convert to a number + while (i < s.Length && IsDigit(s[i], radix, out int value)) + { + // Check for overflows - this is sufficient & correct. + if (result > maxVal || ((long)result) < 0) + { + ThrowOverflowInt64Exception(); + } + + result = result * (ulong)radix + (ulong)value; + i++; + } + + if ((long)result < 0 && result != 0x8000000000000000) + { + ThrowOverflowInt64Exception(); + } + } + else + { + Debug.Assert(radix == 2 || radix == 8 || radix == 10 || radix == 16); + maxVal = + radix == 10 ? 0xffffffffffffffff / 10 : + radix == 16 ? 0xffffffffffffffff / 16 : + radix == 8 ? 0xffffffffffffffff / 8 : + 0xffffffffffffffff / 2; + + // Read all of the digits and convert to a number + while (i < s.Length && IsDigit(s[i], radix, out int value)) + { + // Check for overflows - this is sufficient & correct. + if (result > maxVal) + { + ThrowOverflowUInt64Exception(); + } + + ulong temp = result * (ulong)radix + (ulong)value; + + if (temp < result) // this means overflow as well + { + ThrowOverflowUInt64Exception(); + } + + result = temp; + i++; + } + } + + return (long)result; + } + + private static int GrabInts(int radix, ReadOnlySpan s, ref int i, bool isUnsigned) + { + uint result = 0; + uint maxVal; + + // Allow all non-decimal numbers to set the sign bit. + if (radix == 10 && !isUnsigned) + { + maxVal = (0x7FFFFFFF / 10); + + // Read all of the digits and convert to a number + while (i < s.Length && IsDigit(s[i], radix, out int value)) + { + // Check for overflows - this is sufficient & correct. + if (result > maxVal || (int)result < 0) + { + ThrowOverflowInt32Exception(); + } + result = result * (uint)radix + (uint)value; + i++; + } + if ((int)result < 0 && result != 0x80000000) + { + ThrowOverflowInt32Exception(); + } + } + else + { + Debug.Assert(radix == 2 || radix == 8 || radix == 10 || radix == 16); + maxVal = + radix == 10 ? 0xffffffff / 10 : + radix == 16 ? 0xffffffff / 16 : + radix == 8 ? 0xffffffff / 8 : + 0xffffffff / 2; + + // Read all of the digits and convert to a number + while (i < s.Length && IsDigit(s[i], radix, out int value)) + { + // Check for overflows - this is sufficient & correct. + if (result > maxVal) + { + throw new OverflowException(SR.Overflow_UInt32); + } + + uint temp = result * (uint)radix + (uint)value; + + if (temp < result) // this means overflow as well + { + ThrowOverflowUInt32Exception(); + } + + result = temp; + i++; + } + } + + return (int)result; + } + + private static void ThrowOverflowInt32Exception() => throw new OverflowException(SR.Overflow_Int32); + private static void ThrowOverflowInt64Exception() => throw new OverflowException(SR.Overflow_Int64); + private static void ThrowOverflowUInt32Exception() => throw new OverflowException(SR.Overflow_UInt32); + private static void ThrowOverflowUInt64Exception() => throw new OverflowException(SR.Overflow_UInt64); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsDigit(char c, int radix, out int result) + { + int tmp; + if ((uint)(c - '0') <= 9) + { + result = tmp = c - '0'; + } + else if ((uint)(c - 'A') <= 'Z' - 'A') + { + result = tmp = c - 'A' + 10; + } + else if ((uint)(c - 'a') <= 'z' - 'a') + { + result = tmp = c - 'a' + 10; + } + else + { + result = -1; + return false; + } + + return tmp < radix; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/PlatformNotSupportedException.cs b/external/corefx/src/Common/src/CoreLib/System/PlatformNotSupportedException.cs new file mode 100644 index 0000000000..5039f3f441 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/PlatformNotSupportedException.cs @@ -0,0 +1,44 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: To handle features that don't run on particular platforms +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class PlatformNotSupportedException : NotSupportedException + { + public PlatformNotSupportedException() + : base(SR.Arg_PlatformNotSupported) + { + HResult = HResults.COR_E_PLATFORMNOTSUPPORTED; + } + + public PlatformNotSupportedException(String message) + : base(message) + { + HResult = HResults.COR_E_PLATFORMNOTSUPPORTED; + } + + public PlatformNotSupportedException(String message, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_PLATFORMNOTSUPPORTED; + } + + protected PlatformNotSupportedException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Progress.cs b/external/corefx/src/Common/src/CoreLib/System/Progress.cs new file mode 100644 index 0000000000..755e7719fe --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Progress.cs @@ -0,0 +1,105 @@ +// 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.Threading; +using System.Diagnostics; + +namespace System +{ + /// + /// Provides an IProgress{T} that invokes callbacks for each reported progress value. + /// + /// Specifies the type of the progress report value. + /// + /// Any handler provided to the constructor or event handlers registered with + /// the event are invoked through a + /// instance captured + /// when the instance is constructed. If there is no current SynchronizationContext + /// at the time of construction, the callbacks will be invoked on the ThreadPool. + /// + public class Progress : IProgress + { + /// The synchronization context captured upon construction. This will never be null. + private readonly SynchronizationContext _synchronizationContext; + /// The handler specified to the constructor. This may be null. + private readonly Action _handler; + /// A cached delegate used to post invocation to the synchronization context. + private readonly SendOrPostCallback _invokeHandlers; + + /// Initializes the . + public Progress() + { + // Capture the current synchronization context. + // If there is no current context, we use a default instance targeting the ThreadPool. + _synchronizationContext = SynchronizationContext.Current ?? ProgressStatics.DefaultContext; + Debug.Assert(_synchronizationContext != null); + _invokeHandlers = new SendOrPostCallback(InvokeHandlers); + } + + /// Initializes the with the specified callback. + /// + /// A handler to invoke for each reported progress value. This handler will be invoked + /// in addition to any delegates registered with the event. + /// Depending on the instance captured by + /// the at construction, it's possible that this handler instance + /// could be invoked concurrently with itself. + /// + /// The is null (Nothing in Visual Basic). + public Progress(Action handler) : this() + { + if (handler == null) throw new ArgumentNullException(nameof(handler)); + _handler = handler; + } + + /// Raised for each reported progress value. + /// + /// Handlers registered with this event will be invoked on the + /// captured when the instance was constructed. + /// + public event EventHandler ProgressChanged; + + /// Reports a progress change. + /// The value of the updated progress. + protected virtual void OnReport(T value) + { + // If there's no handler, don't bother going through the sync context. + // Inside the callback, we'll need to check again, in case + // an event handler is removed between now and then. + Action handler = _handler; + EventHandler changedEvent = ProgressChanged; + if (handler != null || changedEvent != null) + { + // Post the processing to the sync context. + // (If T is a value type, it will get boxed here.) + _synchronizationContext.Post(_invokeHandlers, value); + } + } + + /// Reports a progress change. + /// The value of the updated progress. + void IProgress.Report(T value) { OnReport(value); } + + /// Invokes the action and event callbacks. + /// The progress value. + private void InvokeHandlers(object state) + { + T value = (T)state; + + Action handler = _handler; + EventHandler changedEvent = ProgressChanged; + + if (handler != null) handler(value); + if (changedEvent != null) changedEvent(this, value); + } + } + + /// Holds static values for . + /// This avoids one static instance per type T. + internal static class ProgressStatics + { + /// A default synchronization context that targets the ThreadPool. + internal static readonly SynchronizationContext DefaultContext = new SynchronizationContext(); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Random.cs b/external/corefx/src/Common/src/CoreLib/System/Random.cs new file mode 100644 index 0000000000..20f035e2e8 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Random.cs @@ -0,0 +1,263 @@ +// 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. + +namespace System +{ + public class Random + { + // + // Private Constants + // + private const int MBIG = int.MaxValue; + private const int MSEED = 161803398; + private const int MZ = 0; + + // + // Member Variables + // + private int _inext; + private int _inextp; + private int[] _seedArray = new int[56]; + + // + // Public Constants + // + + // + // Native Declarations + // + + // + // Constructors + // + + /*========================================================================================= + **Action: Initializes a new instance of the Random class, using a default seed value + ===========================================================================================*/ + public Random() + : this(GenerateSeed()) + { + } + + /*========================================================================================= + **Action: Initializes a new instance of the Random class, using a specified seed value + ===========================================================================================*/ + public Random(int Seed) + { + int ii = 0; + int mj, mk; + + //Initialize our Seed array. + int subtraction = (Seed == int.MinValue) ? int.MaxValue : Math.Abs(Seed); + mj = MSEED - subtraction; + _seedArray[55] = mj; + mk = 1; + for (int i = 1; i < 55; i++) + { //Apparently the range [1..55] is special (Knuth) and so we're wasting the 0'th position. + if ((ii += 21) >= 55) ii -= 55; + _seedArray[ii] = mk; + mk = mj - mk; + if (mk < 0) mk += MBIG; + mj = _seedArray[ii]; + } + for (int k = 1; k < 5; k++) + { + for (int i = 1; i < 56; i++) + { + int n = i + 30; + if (n >= 55) n -= 55; + _seedArray[i] -= _seedArray[1 + n]; + if (_seedArray[i] < 0) _seedArray[i] += MBIG; + } + } + _inext = 0; + _inextp = 21; + Seed = 1; + } + + // + // Package Private Methods + // + + /*====================================Sample==================================== + **Action: Return a new random number [0..1) and reSeed the Seed array. + **Returns: A double [0..1) + **Arguments: None + **Exceptions: None + ==============================================================================*/ + protected virtual double Sample() + { + //Including this division at the end gives us significantly improved + //random number distribution. + return (InternalSample() * (1.0 / MBIG)); + } + + private int InternalSample() + { + int retVal; + int locINext = _inext; + int locINextp = _inextp; + + if (++locINext >= 56) locINext = 1; + if (++locINextp >= 56) locINextp = 1; + + retVal = _seedArray[locINext] - _seedArray[locINextp]; + + if (retVal == MBIG) retVal--; + if (retVal < 0) retVal += MBIG; + + _seedArray[locINext] = retVal; + + _inext = locINext; + _inextp = locINextp; + + return retVal; + } + + [ThreadStatic] + private static Random t_threadRandom; + private static readonly Random s_globalRandom = new Random(GenerateGlobalSeed()); + + /*=====================================GenerateSeed===================================== + **Returns: An integer that can be used as seed values for consecutively + creating lots of instances on the same thread within a short period of time. + ========================================================================================*/ + private static int GenerateSeed() + { + Random rnd = t_threadRandom; + if (rnd == null) + { + int seed; + lock (s_globalRandom) + { + seed = s_globalRandom.Next(); + } + rnd = new Random(seed); + t_threadRandom = rnd; + } + return rnd.Next(); + } + + /*==================================GenerateGlobalSeed==================================== + **Action: Creates a number to use as global seed. + **Returns: An integer that is safe to use as seed values for thread-local seed generators. + ==========================================================================================*/ + private static unsafe int GenerateGlobalSeed() + { + int result; + Interop.GetRandomBytes((byte*)&result, sizeof(int)); + return result; + } + + // + // Public Instance Methods + // + + + /*=====================================Next===================================== + **Returns: An int [0..Int32.MaxValue) + **Arguments: None + **Exceptions: None. + ==============================================================================*/ + public virtual int Next() + { + return InternalSample(); + } + + private double GetSampleForLargeRange() + { + // The distribution of double value returned by Sample + // is not distributed well enough for a large range. + // If we use Sample for a range [Int32.MinValue..Int32.MaxValue) + // We will end up getting even numbers only. + + int result = InternalSample(); + // Note we can't use addition here. The distribution will be bad if we do that. + bool negative = (InternalSample() % 2 == 0) ? true : false; // decide the sign based on second sample + if (negative) + { + result = -result; + } + double d = result; + d += (int.MaxValue - 1); // get a number in range [0 .. 2 * Int32MaxValue - 1) + d /= 2 * (uint)int.MaxValue - 1; + return d; + } + + + /*=====================================Next===================================== + **Returns: An int [minvalue..maxvalue) + **Arguments: minValue -- the least legal value for the Random number. + ** maxValue -- One greater than the greatest legal return value. + **Exceptions: None. + ==============================================================================*/ + public virtual int Next(int minValue, int maxValue) + { + if (minValue > maxValue) + { + throw new ArgumentOutOfRangeException(nameof(minValue), SR.Format(SR.Argument_MinMaxValue, nameof(minValue), nameof(maxValue))); + } + + long range = (long)maxValue - minValue; + if (range <= int.MaxValue) + { + return ((int)(Sample() * range) + minValue); + } + else + { + return (int)((long)(GetSampleForLargeRange() * range) + minValue); + } + } + + + /*=====================================Next===================================== + **Returns: An int [0..maxValue) + **Arguments: maxValue -- One more than the greatest legal return value. + **Exceptions: None. + ==============================================================================*/ + public virtual int Next(int maxValue) + { + if (maxValue < 0) + { + throw new ArgumentOutOfRangeException(nameof(maxValue), SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(maxValue))); + } + return (int)(Sample() * maxValue); + } + + + /*=====================================Next===================================== + **Returns: A double [0..1) + **Arguments: None + **Exceptions: None + ==============================================================================*/ + public virtual double NextDouble() + { + return Sample(); + } + + + /*==================================NextBytes=================================== + **Action: Fills the byte array with random bytes [0..0x7f]. The entire array is filled. + **Returns:Void + **Arguments: buffer -- the array to be filled. + **Exceptions: None + ==============================================================================*/ + public virtual void NextBytes(byte[] buffer) + { + if (buffer == null) throw new ArgumentNullException(nameof(buffer)); + for (int i = 0; i < buffer.Length; i++) + { + buffer[i] = (byte)InternalSample(); + } + } + + public virtual void NextBytes(Span buffer) + { + for (int i = 0; i < buffer.Length; i++) + { + buffer[i] = (byte)Next(); + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/RankException.cs b/external/corefx/src/Common/src/CoreLib/System/RankException.cs new file mode 100644 index 0000000000..bdd2cd51f1 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/RankException.cs @@ -0,0 +1,45 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: For methods that are passed arrays with the wrong number of +** dimensions. +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class RankException : SystemException + { + public RankException() + : base(SR.Arg_RankException) + { + HResult = HResults.COR_E_RANK; + } + + public RankException(String message) + : base(message) + { + HResult = HResults.COR_E_RANK; + } + + public RankException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_RANK; + } + + protected RankException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/ReadOnlyMemory.cs b/external/corefx/src/Common/src/CoreLib/System/ReadOnlyMemory.cs new file mode 100644 index 0000000000..de423bbd06 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ReadOnlyMemory.cs @@ -0,0 +1,331 @@ +// 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.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +#if !MONO +using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute; +using EditorBrowsableState = System.ComponentModel.EditorBrowsableState; +#endif +#if !FEATURE_PORTABLE_SPAN +using Internal.Runtime.CompilerServices; +#endif // FEATURE_PORTABLE_SPAN + +namespace System +{ + /// + /// Represents a contiguous region of memory, similar to . + /// Unlike , it is not a byref-like type. + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + [DebuggerTypeProxy(typeof(MemoryDebugView<>))] + public readonly struct ReadOnlyMemory + { + // NOTE: With the current implementation, Memory and ReadOnlyMemory must have the same layout, + // as code uses Unsafe.As to cast between them. + + // The highest order bit of _index is used to discern whether _object is an array/string or an owned memory + // if (_index >> 31) == 1, _object is an OwnedMemory + // else, _object is a T[] or string + private readonly object _object; + private readonly int _index; + private readonly int _length; + + internal const int RemoveOwnedFlagBitMask = 0x7FFFFFFF; + + /// + /// Creates a new memory over the entirety of the target array. + /// + /// The target array. + /// Thrown when is a null + /// reference (Nothing in Visual Basic). + /// Thrown when is covariant and array's type is not exactly T[]. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlyMemory(T[] array) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + + _object = array; + _index = 0; + _length = array.Length; + } + + /// + /// Creates a new memory over the portion of the target array beginning + /// at 'start' index and ending at 'end' index (exclusive). + /// + /// The target array. + /// The index at which to begin the memory. + /// The number of items in the memory. + /// Thrown when is a null + /// reference (Nothing in Visual Basic). + /// Thrown when is covariant and array's type is not exactly T[]. + /// + /// Thrown when the specified or end index is not in the range (<0 or >=Length). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlyMemory(T[] array, int start, int length) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + _object = array; + _index = start; + _length = length; + } + + /// Creates a new memory over the existing object, start, and length. No validation is performed. + /// The target object. + /// The index at which to begin the memory. + /// The number of items in the memory. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ReadOnlyMemory(object obj, int start, int length) + { + // No validation performed; caller must provide any necessary validation. + _object = obj; + _index = start; + _length = length; + } + + //Debugger Display = {T[length]} + private string DebuggerDisplay => string.Format("{{{0}[{1}]}}", typeof(T).Name, _length); + + /// + /// Defines an implicit conversion of an array to a + /// + public static implicit operator ReadOnlyMemory(T[] array) => (array != null) ? new ReadOnlyMemory(array) : default; + + /// + /// Defines an implicit conversion of a to a + /// + public static implicit operator ReadOnlyMemory(ArraySegment arraySegment) => new ReadOnlyMemory(arraySegment.Array, arraySegment.Offset, arraySegment.Count); + + /// + /// Returns an empty + /// + public static ReadOnlyMemory Empty => default; + + /// + /// The number of items in the memory. + /// + public int Length => _length; + + /// + /// Returns true if Length is 0. + /// + public bool IsEmpty => _length == 0; + + /// + /// Forms a slice out of the given memory, beginning at 'start'. + /// + /// The index at which to begin this slice. + /// + /// Thrown when the specified index is not in range (<0 or >=Length). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlyMemory Slice(int start) + { + if ((uint)start > (uint)_length) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); + } + + return new ReadOnlyMemory(_object, _index + start, _length - start); + } + + /// + /// Forms a slice out of the given memory, beginning at 'start', of given length + /// + /// The index at which to begin this slice. + /// The desired length for the slice (exclusive). + /// + /// Thrown when the specified or end index is not in range (<0 or >=Length). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlyMemory Slice(int start, int length) + { + if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start)) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); + } + + return new ReadOnlyMemory(_object, _index + start, length); + } + + /// + /// Returns a span from the memory. + /// + public ReadOnlySpan Span + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + if (_index < 0) + { + return ((OwnedMemory)_object).Span.Slice(_index & RemoveOwnedFlagBitMask, _length); + } + else if (typeof(T) == typeof(char) && _object is string s) + { +#if FEATURE_PORTABLE_SPAN + return new ReadOnlySpan(Unsafe.As>(s), MemoryExtensions.StringAdjustment, s.Length).Slice(_index, _length); +#else + return new ReadOnlySpan(ref Unsafe.As(ref s.GetRawStringData()), s.Length).Slice(_index, _length); +#endif // FEATURE_PORTABLE_SPAN + } + else if (_object != null) + { + return new ReadOnlySpan((T[])_object, _index, _length); + } + else + { + return default; + } + } + } + + /// + /// Copies the contents of the read-only memory into the destination. If the source + /// and destination overlap, this method behaves as if the original values are in + /// a temporary location before the destination is overwritten. + /// + /// The Memory to copy items into. + /// + /// Thrown when the destination is shorter than the source. + /// + /// + public void CopyTo(Memory destination) => Span.CopyTo(destination.Span); + + /// + /// Copies the contents of the readonly-only memory into the destination. If the source + /// and destination overlap, this method behaves as if the original values are in + /// a temporary location before the destination is overwritten. + /// + /// If the destination is shorter than the source, this method + /// return false and no data is written to the destination. + /// + /// The span to copy items into. + public bool TryCopyTo(Memory destination) => Span.TryCopyTo(destination.Span); + + /// Creates a handle for the memory. + /// + /// If pin is true, the GC will not move the array until the returned + /// is disposed, enabling the memory's address can be taken and used. + /// + public unsafe MemoryHandle Retain(bool pin = false) + { + MemoryHandle memoryHandle = default; + if (pin) + { + if (_index < 0) + { + memoryHandle = ((OwnedMemory)_object).Pin((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf()); + } + else if (typeof(T) == typeof(char) && _object is string s) + { + GCHandle handle = GCHandle.Alloc(s, GCHandleType.Pinned); +#if FEATURE_PORTABLE_SPAN + void* pointer = Unsafe.Add((void*)handle.AddrOfPinnedObject(), _index); +#else + void* pointer = Unsafe.Add(Unsafe.AsPointer(ref s.GetRawStringData()), _index); +#endif // FEATURE_PORTABLE_SPAN + memoryHandle = new MemoryHandle(null, pointer, handle); + } + else if (_object is T[] array) + { + var handle = GCHandle.Alloc(array, GCHandleType.Pinned); +#if FEATURE_PORTABLE_SPAN + void* pointer = Unsafe.Add((void*)handle.AddrOfPinnedObject(), _index); +#else + void* pointer = Unsafe.Add(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index); +#endif // FEATURE_PORTABLE_SPAN + memoryHandle = new MemoryHandle(null, pointer, handle); + } + } + else + { + if (_index < 0) + { + ((OwnedMemory)_object).Retain(); + memoryHandle = new MemoryHandle((OwnedMemory)_object); + } + } + return memoryHandle; + } + + /// + /// Copies the contents from the memory into a new array. This heap + /// allocates, so should generally be avoided, however it is sometimes + /// necessary to bridge the gap with APIs written in terms of arrays. + /// + public T[] ToArray() => Span.ToArray(); + + /// Determines whether the specified object is equal to the current object. +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public override bool Equals(object obj) + { + if (obj is ReadOnlyMemory readOnlyMemory) + { + return Equals(readOnlyMemory); + } + else if (obj is Memory memory) + { + return Equals(memory); + } + else + { + return false; + } + } + + /// + /// Returns true if the memory points to the same array and has the same length. Note that + /// this does *not* check to see if the *contents* are equal. + /// + public bool Equals(ReadOnlyMemory other) + { + return + _object == other._object && + _index == other._index && + _length == other._length; + } + + /// Returns the hash code for this +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public override int GetHashCode() + { + return _object != null ? CombineHashCodes(_object.GetHashCode(), _index.GetHashCode(), _length.GetHashCode()) : 0; + } + + private static int CombineHashCodes(int left, int right) + { + return ((left << 5) + left) ^ right; + } + + private static int CombineHashCodes(int h1, int h2, int h3) + { + return CombineHashCodes(CombineHashCodes(h1, h2), h3); + } + + /// Gets the state of the memory as individual fields. + /// The offset. + /// The count. + /// The object. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal object GetObjectStartLength(out int start, out int length) + { + start = _index; + length = _length; + return _object; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/ReadOnlySpan.cs b/external/corefx/src/Common/src/CoreLib/System/ReadOnlySpan.cs new file mode 100644 index 0000000000..1854df40fa --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ReadOnlySpan.cs @@ -0,0 +1,369 @@ +// 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.ComponentModel; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; +using Internal.Runtime.CompilerServices; + +#pragma warning disable 0809 //warning CS0809: Obsolete member 'Span.Equals(object)' overrides non-obsolete member 'object.Equals(object)' + +namespace System +{ + /// + /// ReadOnlySpan represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed + /// or native memory, or to memory allocated on the stack. It is type- and memory-safe. + /// + [DebuggerTypeProxy(typeof(SpanDebugView<>))] + [DebuggerDisplay("{DebuggerDisplay,nq}")] + [NonVersionable] + public readonly ref struct ReadOnlySpan + { + /// A byref or a native ptr. + internal readonly ByReference _pointer; + /// The number of elements this ReadOnlySpan contains. +#if PROJECTN + [Bound] +#endif + private readonly int _length; + + /// + /// Creates a new read-only span over the entirety of the target array. + /// + /// The target array. + /// Thrown when is a null + /// reference (Nothing in Visual Basic). + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan(T[] array) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + + _pointer = new ByReference(ref Unsafe.As(ref array.GetRawSzArrayData())); + _length = array.Length; + } + + /// + /// Creates a new read-only span over the portion of the target array beginning + /// at 'start' index and ending at 'end' index (exclusive). + /// + /// The target array. + /// The index at which to begin the read-only span. + /// The number of items in the read-only span. + /// Thrown when is a null + /// reference (Nothing in Visual Basic). + /// + /// Thrown when the specified or end index is not in the range (<0 or >=Length). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan(T[] array, int start, int length) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + _pointer = new ByReference(ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), start)); + _length = length; + } + + /// + /// Creates a new read-only span over the target unmanaged buffer. Clearly this + /// is quite dangerous, because we are creating arbitrarily typed T's + /// out of a void*-typed block of memory. And the length is not checked. + /// But if this creation is correct, then all subsequent uses are correct. + /// + /// An unmanaged pointer to memory. + /// The number of elements the memory contains. + /// + /// Thrown when is reference type or contains pointers and hence cannot be stored in unmanaged memory. + /// + /// + /// Thrown when the specified is negative. + /// + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe ReadOnlySpan(void* pointer, int length) + { + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); + if (length < 0) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + _pointer = new ByReference(ref Unsafe.As(ref *(byte*)pointer)); + _length = length; + } + + /// + /// Create a new read-only span over a portion of a regular managed object. This can be useful + /// if part of a managed object represents a "fixed array." This is dangerous because neither the + /// is checked, nor being null, nor the fact that + /// "rawPointer" actually lies within . + /// + /// The managed object that contains the data to span over. + /// A reference to data within that object. + /// The number of elements the memory contains. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [EditorBrowsable(EditorBrowsableState.Never)] + public static ReadOnlySpan DangerousCreate(object obj, ref T objectData, int length) => new ReadOnlySpan(ref objectData, length); + + // Constructor for internal use only. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ReadOnlySpan(ref T ptr, int length) + { + Debug.Assert(length >= 0); + + _pointer = new ByReference(ref ptr); + _length = length; + } + + //Debugger Display = {T[length]} + private string DebuggerDisplay => string.Format("{{{0}[{1}]}}", typeof(T).Name, _length); + + /// + /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element + /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [EditorBrowsable(EditorBrowsableState.Never)] + internal ref readonly T DangerousGetPinnableReference() + { + return ref _pointer.Value; + } + + /// + /// The number of items in the read-only span. + /// + public int Length + { + [NonVersionable] + get + { + return _length; + } + } + + /// + /// Returns true if Length is 0. + /// + public bool IsEmpty + { + [NonVersionable] + get + { + return _length == 0; + } + } + + /// + /// Returns the specified element of the read-only span. + /// + /// + /// + /// + /// Thrown when index less than 0 or index greater than or equal to Length + /// + + public ref readonly T this[int index] + { +#if PROJECTN + [BoundsChecking] + get + { + return ref Unsafe.Add(ref _pointer.Value, index); + } +#else + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [NonVersionable] + get + { + if ((uint)index >= (uint)_length) + ThrowHelper.ThrowIndexOutOfRangeException(); + return ref Unsafe.Add(ref _pointer.Value, index); + } +#endif + } + + /// + /// Copies the contents of this read-only span into destination span. If the source + /// and destinations overlap, this method behaves as if the original values in + /// a temporary location before the destination is overwritten. + /// + /// The span to copy items into. + /// + /// Thrown when the destination Span is shorter than the source Span. + /// + /// + public void CopyTo(Span destination) + { + if (!TryCopyTo(destination)) + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + } + + /// Copies the contents of this read-only span into destination span. If the source + /// and destinations overlap, this method behaves as if the original values in + /// a temporary location before the destination is overwritten. + /// + /// If the destination span is shorter than the source span, this method + /// return false and no data is written to the destination. + /// The span to copy items into. + public bool TryCopyTo(Span destination) + { + if ((uint)_length > (uint)destination.Length) + return false; + + Span.CopyTo(ref destination.DangerousGetPinnableReference(), ref _pointer.Value, _length); + return true; + } + + /// + /// Returns true if left and right point at the same memory and have the same length. Note that + /// this does *not* check to see if the *contents* are equal. + /// + public static bool operator ==(ReadOnlySpan left, ReadOnlySpan right) + { + return left._length == right._length && Unsafe.AreSame(ref left._pointer.Value, ref right._pointer.Value); + } + + /// + /// Returns false if left and right point at the same memory and have the same length. Note that + /// this does *not* check to see if the *contents* are equal. + /// + public static bool operator !=(ReadOnlySpan left, ReadOnlySpan right) => !(left == right); + + /// + /// This method is not supported as spans cannot be boxed. To compare two spans, use operator==. + /// + /// Always thrown by this method. + /// + /// + [Obsolete("Equals() on ReadOnlySpan will always throw an exception. Use == instead.")] + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) + { + throw new NotSupportedException(SR.NotSupported_CannotCallEqualsOnSpan); + } + + /// + /// This method is not supported as spans cannot be boxed. + /// + /// Always thrown by this method. + /// + /// + [Obsolete("GetHashCode() on ReadOnlySpan will always throw an exception.")] + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() + { + throw new NotSupportedException(SR.NotSupported_CannotCallGetHashCodeOnSpan); + } + + /// + /// Defines an implicit conversion of an array to a + /// + public static implicit operator ReadOnlySpan(T[] array) => array != null ? new ReadOnlySpan(array) : default; + + /// + /// Defines an implicit conversion of a to a + /// + public static implicit operator ReadOnlySpan(ArraySegment arraySegment) + => arraySegment.Array != null ? new ReadOnlySpan(arraySegment.Array, arraySegment.Offset, arraySegment.Count) : default; + + /// + /// Forms a slice out of the given read-only span, beginning at 'start'. + /// + /// The index at which to begin this slice. + /// + /// Thrown when the specified index is not in range (<0 or >=Length). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan Slice(int start) + { + if ((uint)start > (uint)_length) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + return new ReadOnlySpan(ref Unsafe.Add(ref _pointer.Value, start), _length - start); + } + + /// + /// Forms a slice out of the given read-only span, beginning at 'start', of given length + /// + /// The index at which to begin this slice. + /// The desired length for the slice (exclusive). + /// + /// Thrown when the specified or end index is not in range (<0 or >=Length). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan Slice(int start, int length) + { + if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + return new ReadOnlySpan(ref Unsafe.Add(ref _pointer.Value, start), length); + } + + /// + /// Copies the contents of this read-only span into a new array. This heap + /// allocates, so should generally be avoided, however it is sometimes + /// necessary to bridge the gap with APIs written in terms of arrays. + /// + public T[] ToArray() + { + if (_length == 0) + return Array.Empty(); + + var destination = new T[_length]; + Span.CopyTo(ref Unsafe.As(ref destination.GetRawSzArrayData()), ref _pointer.Value, _length); + return destination; + } + + /// + /// Returns a 0-length read-only span whose base is the null pointer. + /// + public static ReadOnlySpan Empty => default(ReadOnlySpan); + + /// Gets an enumerator for this span. + public Enumerator GetEnumerator() => new Enumerator(this); + + /// Enumerates the elements of a . + public ref struct Enumerator + { + /// The span being enumerated. + private readonly ReadOnlySpan _span; + /// The next index to yield. + private int _index; + + /// Initialize the enumerator. + /// The span to enumerate. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Enumerator(ReadOnlySpan span) + { + _span = span; + _index = -1; + } + + /// Advances the enumerator to the next element of the span. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + int index = _index + 1; + if (index < _span.Length) + { + _index = index; + return true; + } + + return false; + } + + /// Gets the element at the current position of the enumerator. + public ref readonly T Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref _span[_index]; + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AmbiguousMatchException.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AmbiguousMatchException.cs new file mode 100644 index 0000000000..643a127c49 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/AmbiguousMatchException.cs @@ -0,0 +1,35 @@ +// 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.Runtime.Serialization; + +namespace System.Reflection +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class AmbiguousMatchException : SystemException + { + public AmbiguousMatchException() + : base(SR.RFLCT_Ambiguous) + { + HResult = HResults.COR_E_AMBIGUOUSMATCH; + } + + public AmbiguousMatchException(string message) + : base(message) + { + HResult = HResults.COR_E_AMBIGUOUSMATCH; + } + + public AmbiguousMatchException(string message, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_AMBIGUOUSMATCH; + } + + internal AmbiguousMatchException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/Assembly.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/Assembly.cs new file mode 100644 index 0000000000..7280869c02 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/Assembly.cs @@ -0,0 +1,201 @@ +// 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.IO; +using System.Globalization; +using System.Collections.Generic; +using System.Configuration.Assemblies; +using System.Runtime.Serialization; +using System.Security; + +namespace System.Reflection +{ + public abstract partial class Assembly : ICustomAttributeProvider, ISerializable + { + protected Assembly() { } + + public virtual IEnumerable DefinedTypes + { + get + { + Type[] types = GetTypes(); + TypeInfo[] typeinfos = new TypeInfo[types.Length]; + for (int i = 0; i < types.Length; i++) + { + TypeInfo typeinfo = types[i].GetTypeInfo(); + if (typeinfo == null) + throw new NotSupportedException(SR.Format(SR.NotSupported_NoTypeInfo, types[i].FullName)); + + typeinfos[i] = typeinfo; + } + return typeinfos; + } + } + + public virtual Type[] GetTypes() + { + Module[] m = GetModules(false); + + int finalLength = 0; + Type[][] moduleTypes = new Type[m.Length][]; + + for (int i = 0; i < moduleTypes.Length; i++) + { + moduleTypes[i] = m[i].GetTypes(); + finalLength += moduleTypes[i].Length; + } + + int current = 0; + Type[] ret = new Type[finalLength]; + for (int i = 0; i < moduleTypes.Length; i++) + { + int length = moduleTypes[i].Length; + Array.Copy(moduleTypes[i], 0, ret, current, length); + current += length; + } + + return ret; + } + + public virtual IEnumerable ExportedTypes => GetExportedTypes(); + public virtual Type[] GetExportedTypes() { throw NotImplemented.ByDesign; } + public virtual Type[] GetForwardedTypes() { throw NotImplemented.ByDesign; } + + public virtual string CodeBase { get { throw NotImplemented.ByDesign; } } + public virtual MethodInfo EntryPoint { get { throw NotImplemented.ByDesign; } } + public virtual string FullName { get { throw NotImplemented.ByDesign; } } + public virtual string ImageRuntimeVersion { get { throw NotImplemented.ByDesign; } } + public virtual bool IsDynamic => false; + public virtual string Location { get { throw NotImplemented.ByDesign; } } + public virtual bool ReflectionOnly { get { throw NotImplemented.ByDesign; } } + + public virtual ManifestResourceInfo GetManifestResourceInfo(string resourceName) { throw NotImplemented.ByDesign; } + public virtual string[] GetManifestResourceNames() { throw NotImplemented.ByDesign; } + public virtual Stream GetManifestResourceStream(string name) { throw NotImplemented.ByDesign; } + public virtual Stream GetManifestResourceStream(Type type, string name) { throw NotImplemented.ByDesign; } + + public bool IsFullyTrusted => true; + + public virtual AssemblyName GetName() => GetName(copiedName: false); + public virtual AssemblyName GetName(bool copiedName) { throw NotImplemented.ByDesign; } + + public virtual Type GetType(string name) => GetType(name, throwOnError: false, ignoreCase: false); + public virtual Type GetType(string name, bool throwOnError) => GetType(name, throwOnError: throwOnError, ignoreCase: false); + public virtual Type GetType(string name, bool throwOnError, bool ignoreCase) { throw NotImplemented.ByDesign; } + + public virtual bool IsDefined(Type attributeType, bool inherit) { throw NotImplemented.ByDesign; } + + public virtual IEnumerable CustomAttributes => GetCustomAttributesData(); + public virtual IList GetCustomAttributesData() { throw NotImplemented.ByDesign; } + + public virtual object[] GetCustomAttributes(bool inherit) { throw NotImplemented.ByDesign; } + public virtual object[] GetCustomAttributes(Type attributeType, bool inherit) { throw NotImplemented.ByDesign; } + + public virtual string EscapedCodeBase => AssemblyName.EscapeCodeBase(CodeBase); + + public object CreateInstance(string typeName) => CreateInstance(typeName, false, BindingFlags.Public | BindingFlags.Instance, binder: null, args: null, culture: null, activationAttributes: null); + public object CreateInstance(string typeName, bool ignoreCase) => CreateInstance(typeName, ignoreCase, BindingFlags.Public | BindingFlags.Instance, binder: null, args: null, culture: null, activationAttributes: null); + public virtual object CreateInstance(string typeName, bool ignoreCase, BindingFlags bindingAttr, Binder binder, object[] args, CultureInfo culture, object[] activationAttributes) + { + Type t = GetType(typeName, throwOnError: false, ignoreCase: ignoreCase); + if (t == null) + return null; + + return Activator.CreateInstance(t, bindingAttr, binder, args, culture, activationAttributes); + } + + public virtual event ModuleResolveEventHandler ModuleResolve { add { throw NotImplemented.ByDesign; } remove { throw NotImplemented.ByDesign; } } + + public virtual Module ManifestModule { get { throw NotImplemented.ByDesign; } } + public virtual Module GetModule(string name) { throw NotImplemented.ByDesign; } + + public Module[] GetModules() => GetModules(getResourceModules: false); + public virtual Module[] GetModules(bool getResourceModules) { throw NotImplemented.ByDesign; } + + public virtual IEnumerable Modules => GetLoadedModules(getResourceModules: true); + public Module[] GetLoadedModules() => GetLoadedModules(getResourceModules: false); + public virtual Module[] GetLoadedModules(bool getResourceModules) { throw NotImplemented.ByDesign; } + + public virtual AssemblyName[] GetReferencedAssemblies() { throw NotImplemented.ByDesign; } + + public virtual Assembly GetSatelliteAssembly(CultureInfo culture) { throw NotImplemented.ByDesign; } + public virtual Assembly GetSatelliteAssembly(CultureInfo culture, Version version) { throw NotImplemented.ByDesign; } + + public virtual FileStream GetFile(string name) { throw NotImplemented.ByDesign; } + public virtual FileStream[] GetFiles() => GetFiles(getResourceModules: false); + public virtual FileStream[] GetFiles(bool getResourceModules) { throw NotImplemented.ByDesign; } + + public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { throw NotImplemented.ByDesign; } + + public override string ToString() + { + string displayName = FullName; + if (displayName == null) + return base.ToString(); + else + return displayName; + } + + /* + Returns true if the assembly was loaded from the global assembly cache. + */ + public virtual bool GlobalAssemblyCache { get { throw NotImplemented.ByDesign; } } + public virtual Int64 HostContext { get { throw NotImplemented.ByDesign; } } + + public override bool Equals(object o) => base.Equals(o); + public override int GetHashCode() => base.GetHashCode(); + + public static bool operator ==(Assembly left, Assembly right) + { + if (object.ReferenceEquals(left, right)) + return true; + + if ((object)left == null || (object)right == null) + return false; + + return left.Equals(right); + } + + public static bool operator !=(Assembly left, Assembly right) + { + return !(left == right); + } + + public static string CreateQualifiedName(string assemblyName, string typeName) => typeName + ", " + assemblyName; + + public static Assembly GetAssembly(Type type) + { + if (type == null) + throw new ArgumentNullException(nameof(type)); + + Module m = type.Module; + if (m == null) + return null; + else + return m.Assembly; + } + + public static Assembly Load(byte[] rawAssembly) => Load(rawAssembly, rawSymbolStore: null); + + [Obsolete("This method has been deprecated. Please use Assembly.Load() instead. http://go.microsoft.com/fwlink/?linkid=14202")] + public static Assembly LoadWithPartialName(string partialName) + { + if (partialName == null) + throw new ArgumentNullException(nameof(partialName)); + + return Load(partialName); + } + + public static Assembly UnsafeLoadFrom(string assemblyFile) => LoadFrom(assemblyFile); + + public Module LoadModule(string moduleName, byte[] rawModule) => LoadModule(moduleName, rawModule, null); + public virtual Module LoadModule(string moduleName, byte[] rawModule, byte[] rawSymbolStore) { throw NotImplemented.ByDesign; } + + public static Assembly ReflectionOnlyLoad(byte[] rawAssembly) { throw new PlatformNotSupportedException(SR.PlatformNotSupported_ReflectionOnly); } + public static Assembly ReflectionOnlyLoad(string assemblyString) { throw new PlatformNotSupportedException(SR.PlatformNotSupported_ReflectionOnly); } + public static Assembly ReflectionOnlyLoadFrom(string assemblyFile) { throw new PlatformNotSupportedException(SR.PlatformNotSupported_ReflectionOnly); } + + public virtual SecurityRuleSet SecurityRuleSet => SecurityRuleSet.None; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyAlgorithmIdAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyAlgorithmIdAttribute.cs new file mode 100644 index 0000000000..fe24f353be --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyAlgorithmIdAttribute.cs @@ -0,0 +1,27 @@ +// 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.Configuration.Assemblies; + +namespace System.Reflection +{ + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + public sealed class AssemblyAlgorithmIdAttribute : Attribute + { + public AssemblyAlgorithmIdAttribute(AssemblyHashAlgorithm algorithmId) + { + AlgorithmId = (uint)algorithmId; + } + + [CLSCompliant(false)] + public AssemblyAlgorithmIdAttribute(uint algorithmId) + { + AlgorithmId = algorithmId; + } + + [CLSCompliant(false)] + public uint AlgorithmId { get; } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyCompanyAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyCompanyAttribute.cs new file mode 100644 index 0000000000..d986db60a3 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyCompanyAttribute.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.Reflection +{ + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + public sealed class AssemblyCompanyAttribute : Attribute + { + public AssemblyCompanyAttribute(string company) + { + Company = company; + } + + public string Company { get; } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyConfigurationAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyConfigurationAttribute.cs new file mode 100644 index 0000000000..195c4d0ca6 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyConfigurationAttribute.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.Reflection +{ + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + public sealed class AssemblyConfigurationAttribute : Attribute + { + public AssemblyConfigurationAttribute(string configuration) + { + Configuration = configuration; + } + + public string Configuration { get; } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyContentType.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyContentType.cs new file mode 100644 index 0000000000..2ee1a00818 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyContentType.cs @@ -0,0 +1,13 @@ +// 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. + +namespace System.Reflection +{ + public enum AssemblyContentType + { + Default = 0, + WindowsRuntime = 1, + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyCopyrightAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyCopyrightAttribute.cs new file mode 100644 index 0000000000..e50e19932b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyCopyrightAttribute.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.Reflection +{ + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + public sealed class AssemblyCopyrightAttribute : Attribute + { + public AssemblyCopyrightAttribute(string copyright) + { + Copyright = copyright; + } + + public string Copyright { get; } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyCultureAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyCultureAttribute.cs new file mode 100644 index 0000000000..e31c6f9c1c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyCultureAttribute.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.Reflection +{ + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + public sealed class AssemblyCultureAttribute : Attribute + { + public AssemblyCultureAttribute(string culture) + { + Culture = culture; + } + + public string Culture { get; } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyDefaultAliasAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyDefaultAliasAttribute.cs new file mode 100644 index 0000000000..ced35ed3fd --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyDefaultAliasAttribute.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.Reflection +{ + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + public sealed class AssemblyDefaultAliasAttribute : Attribute + { + public AssemblyDefaultAliasAttribute(string defaultAlias) + { + DefaultAlias = defaultAlias; + } + + public string DefaultAlias { get; } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyDelaySignAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyDelaySignAttribute.cs new file mode 100644 index 0000000000..eae2cf613c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyDelaySignAttribute.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.Reflection +{ + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + public sealed class AssemblyDelaySignAttribute : Attribute + { + public AssemblyDelaySignAttribute(bool delaySign) + { + DelaySign = delaySign; + } + + public bool DelaySign { get; } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyDescriptionAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyDescriptionAttribute.cs new file mode 100644 index 0000000000..50f57c96a6 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyDescriptionAttribute.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.Reflection +{ + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + public sealed class AssemblyDescriptionAttribute : Attribute + { + public AssemblyDescriptionAttribute(string description) + { + Description = description; + } + + public string Description { get; } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyFileVersionAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyFileVersionAttribute.cs new file mode 100644 index 0000000000..b5face65bc --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyFileVersionAttribute.cs @@ -0,0 +1,20 @@ +// 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. + +namespace System.Reflection +{ + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + public sealed class AssemblyFileVersionAttribute : Attribute + { + public AssemblyFileVersionAttribute(string version) + { + if (version == null) + throw new ArgumentNullException(nameof(version)); + Version = version; + } + + public string Version { get; } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyFlagsAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyFlagsAttribute.cs new file mode 100644 index 0000000000..103413340c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyFlagsAttribute.cs @@ -0,0 +1,43 @@ +// 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. + +namespace System.Reflection +{ + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + public sealed class AssemblyFlagsAttribute : Attribute + { + private AssemblyNameFlags _flags; + + [Obsolete("This constructor has been deprecated. Please use AssemblyFlagsAttribute(AssemblyNameFlags) instead. http://go.microsoft.com/fwlink/?linkid=14202")] + [CLSCompliant(false)] + public AssemblyFlagsAttribute(uint flags) + { + _flags = (AssemblyNameFlags)flags; + } + + [Obsolete("This property has been deprecated. Please use AssemblyFlags instead. http://go.microsoft.com/fwlink/?linkid=14202")] + [CLSCompliant(false)] + public uint Flags + { + get { return (uint)_flags; } + } + + public int AssemblyFlags + { + get { return (int)_flags; } + } + + [Obsolete("This constructor has been deprecated. Please use AssemblyFlagsAttribute(AssemblyNameFlags) instead. http://go.microsoft.com/fwlink/?linkid=14202")] + public AssemblyFlagsAttribute(int assemblyFlags) + { + _flags = (AssemblyNameFlags)assemblyFlags; + } + + public AssemblyFlagsAttribute(AssemblyNameFlags assemblyFlags) + { + _flags = assemblyFlags; + } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyInformationalVersionAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyInformationalVersionAttribute.cs new file mode 100644 index 0000000000..915b973ab9 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyInformationalVersionAttribute.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.Reflection +{ + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + public sealed class AssemblyInformationalVersionAttribute : Attribute + { + public AssemblyInformationalVersionAttribute(string informationalVersion) + { + InformationalVersion = informationalVersion; + } + + public string InformationalVersion { get; } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyKeyFileAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyKeyFileAttribute.cs new file mode 100644 index 0000000000..9f7387d8af --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyKeyFileAttribute.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.Reflection +{ + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + public sealed class AssemblyKeyFileAttribute : Attribute + { + public AssemblyKeyFileAttribute(string keyFile) + { + KeyFile = keyFile; + } + + public string KeyFile { get; } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyKeyNameAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyKeyNameAttribute.cs new file mode 100644 index 0000000000..4cf51754ea --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyKeyNameAttribute.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.Reflection +{ + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + public sealed class AssemblyKeyNameAttribute : Attribute + { + public AssemblyKeyNameAttribute(string keyName) + { + KeyName = keyName; + } + + public string KeyName { get; } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyMetadataAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyMetadataAttribute.cs new file mode 100644 index 0000000000..de9f6351ec --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyMetadataAttribute.cs @@ -0,0 +1,21 @@ +// 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. + +namespace System.Reflection +{ + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)] + public sealed class AssemblyMetadataAttribute : Attribute + { + public AssemblyMetadataAttribute(string key, string value) + { + Key = key; + Value = value; + } + + public string Key { get; } + + public string Value { get; } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyNameFlags.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyNameFlags.cs new file mode 100644 index 0000000000..d321032031 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyNameFlags.cs @@ -0,0 +1,21 @@ +// 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. + +namespace System.Reflection +{ + [Flags] + public enum AssemblyNameFlags + { + None = 0x0000, + // Flag used to indicate that an assembly ref contains the full public key, not the compressed token. + // Must match afPublicKey in CorHdr.h. + PublicKey = 0x0001, + //ProcArchMask = 0x00F0, // Bits describing the processor architecture + // Accessible via AssemblyName.ProcessorArchitecture + EnableJITcompileOptimizer = 0x4000, + EnableJITcompileTracking = 0x8000, + Retargetable = 0x0100, + //ContentType = 0x0E00, // Bits describing the ContentType are accessible via AssemblyName.ContentType + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyNameFormatter.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyNameFormatter.cs new file mode 100644 index 0000000000..716afb045c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/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. +// See the LICENSE file in the project root for more information. + +using System.IO; +using System.Text; +using System.Globalization; +using System.Collections.Generic; + +namespace System.Reflection +{ + internal static class AssemblyNameFormatter + { + public static string ComputeDisplayName(string name, Version version, string cultureName, byte[] pkt, AssemblyNameFlags flags, AssemblyContentType contentType) + { + const int PUBLIC_KEY_TOKEN_LEN = 8; + + if (name == string.Empty) + throw new FileLoadException(); + + StringBuilder sb = new StringBuilder(); + if (name != null) + { + sb.AppendQuoted(name); + } + + if (version != null) + { + Version canonicalizedVersion = 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); + } + } + } + } + } + + if (cultureName != null) + { + if (cultureName == string.Empty) + cultureName = "neutral"; + sb.Append(", Culture="); + sb.AppendQuoted(cultureName); + } + + 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 != (flags & AssemblyNameFlags.Retargetable)) + sb.Append(", Retargetable=Yes"); + + 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 kv in 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); + } + + 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[] EscapeSequences = + { + new KeyValuePair('\\', "\\"), + new KeyValuePair(',', ","), + new KeyValuePair('=', "="), + new KeyValuePair('\'', "'"), + new KeyValuePair('\"', "\""), + new KeyValuePair('n', Environment.NewLine), + new KeyValuePair('t', "\t"), + }; + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyProductAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyProductAttribute.cs new file mode 100644 index 0000000000..43cb62df99 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyProductAttribute.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.Reflection +{ + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + public sealed class AssemblyProductAttribute : Attribute + { + public AssemblyProductAttribute(string product) + { + Product = product; + } + + public string Product { get; } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblySignatureKeyAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblySignatureKeyAttribute.cs new file mode 100644 index 0000000000..e6ec8af1b3 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblySignatureKeyAttribute.cs @@ -0,0 +1,21 @@ +// 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. + +namespace System.Reflection +{ + [AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)] + public sealed class AssemblySignatureKeyAttribute : Attribute + { + public AssemblySignatureKeyAttribute(string publicKey, string countersignature) + { + PublicKey = publicKey; + Countersignature = countersignature; + } + + public string PublicKey { get; } + + public string Countersignature { get; } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyTitleAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyTitleAttribute.cs new file mode 100644 index 0000000000..26d7a2e66c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyTitleAttribute.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.Reflection +{ + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + public sealed class AssemblyTitleAttribute : Attribute + { + public AssemblyTitleAttribute(string title) + { + Title = title; + } + + public string Title { get; } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyTrademarkAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyTrademarkAttribute.cs new file mode 100644 index 0000000000..1d3edf51d5 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyTrademarkAttribute.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.Reflection +{ + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + public sealed class AssemblyTrademarkAttribute : Attribute + { + public AssemblyTrademarkAttribute(string trademark) + { + Trademark = trademark; + } + + public string Trademark { get; } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyVersionAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyVersionAttribute.cs new file mode 100644 index 0000000000..b3557bac97 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/AssemblyVersionAttribute.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.Reflection +{ + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + public sealed class AssemblyVersionAttribute : Attribute + { + public AssemblyVersionAttribute(string version) + { + Version = version; + } + + public string Version { get; } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/Binder.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/Binder.cs new file mode 100644 index 0000000000..3dc5665d52 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/Binder.cs @@ -0,0 +1,19 @@ +// 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.Globalization; + +namespace System.Reflection +{ + public abstract class Binder + { + protected Binder() { } + public abstract FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo culture); + public abstract MethodBase BindToMethod(BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] names, out object state); + public abstract object ChangeType(object value, Type type, CultureInfo culture); + public abstract void ReorderArgumentArray(ref object[] args, object state); + public abstract MethodBase SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers); + public abstract PropertyInfo SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, Type returnType, Type[] indexes, ParameterModifier[] modifiers); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/BindingFlags.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/BindingFlags.cs new file mode 100644 index 0000000000..7ba83e20da --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/BindingFlags.cs @@ -0,0 +1,51 @@ +// 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. + +namespace System.Reflection +{ + [Flags] + public enum BindingFlags + { + // NOTES: We have lookup masks defined in RuntimeType and Activator. If we + // change the lookup values then these masks may need to change also. + + // a place holder for no flag specifed + Default = 0x00, + + // These flags indicate what to search for when binding + IgnoreCase = 0x01, // Ignore the case of Names while searching + DeclaredOnly = 0x02, // Only look at the members declared on the Type + Instance = 0x04, // Include Instance members in search + Static = 0x08, // Include Static members in search + Public = 0x10, // Include Public members in search + NonPublic = 0x20, // Include Non-Public members in search + FlattenHierarchy = 0x40, // Rollup the statics into the class. + + // These flags are used by InvokeMember to determine + // what type of member we are trying to Invoke. + // BindingAccess = 0xFF00; + InvokeMethod = 0x0100, + CreateInstance = 0x0200, + GetField = 0x0400, + SetField = 0x0800, + GetProperty = 0x1000, + SetProperty = 0x2000, + + // These flags are also used by InvokeMember but they should only + // be used when calling InvokeMember on a COM object. + PutDispProperty = 0x4000, + PutRefDispProperty = 0x8000, + + ExactBinding = 0x010000, // Bind with Exact Type matching, No Change type + SuppressChangeType = 0x020000, + + // DefaultValueBinding will return the set of methods having ArgCount or + // more parameters. This is used for default values, etc. + OptionalParamBinding = 0x040000, + + // These are a couple of misc attributes used + IgnoreReturn = 0x01000000, // This is used in COM Interop + DoNotWrapExceptions = 0x02000000, // Disables wrapping exceptions in TargetInvocationException + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/CallingConventions.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/CallingConventions.cs new file mode 100644 index 0000000000..bb6d6cd809 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/CallingConventions.cs @@ -0,0 +1,20 @@ +// 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. + +// CallingConventions is a set of Bits representing the calling conventions in the system. + +namespace System.Reflection +{ + [Flags] + public enum CallingConventions + { + //NOTE: If you change this please update COMMember.cpp. These + // are defined there. + Standard = 0x0001, + VarArgs = 0x0002, + Any = Standard | VarArgs, + HasThis = 0x0020, + ExplicitThis = 0x0040, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/ConstructorInfo.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/ConstructorInfo.cs new file mode 100644 index 0000000000..3ee1dbf855 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/ConstructorInfo.cs @@ -0,0 +1,40 @@ +// 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.Diagnostics; +using System.Globalization; + +namespace System.Reflection +{ + public abstract partial class ConstructorInfo : MethodBase + { + protected ConstructorInfo() { } + + public override MemberTypes MemberType => MemberTypes.Constructor; + + [DebuggerHidden] + [DebuggerStepThrough] + public object Invoke(object[] parameters) => Invoke(BindingFlags.Default, binder: null, parameters: parameters, culture: null); + public abstract object Invoke(BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture); + + public override bool Equals(object obj) => base.Equals(obj); + public override int GetHashCode() => base.GetHashCode(); + + public static bool operator ==(ConstructorInfo left, ConstructorInfo right) + { + if (object.ReferenceEquals(left, right)) + return true; + + if ((object)left == null || (object)right == null) + return false; + + return left.Equals(right); + } + + public static bool operator !=(ConstructorInfo left, ConstructorInfo right) => !(left == right); + + public static readonly string ConstructorName = ".ctor"; + public static readonly string TypeConstructorName = ".cctor"; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/CustomAttributeFormatException.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/CustomAttributeFormatException.cs new file mode 100644 index 0000000000..1d7d4a7671 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/CustomAttributeFormatException.cs @@ -0,0 +1,34 @@ +// 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.Runtime.Serialization; + +namespace System.Reflection +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class CustomAttributeFormatException : FormatException + { + public CustomAttributeFormatException() + : this(SR.Arg_CustomAttributeFormatException) + { + } + + public CustomAttributeFormatException(string message) + : this(message, null) + { + } + + public CustomAttributeFormatException(string message, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_CUSTOMATTRIBUTEFORMAT; + } + + protected CustomAttributeFormatException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/DefaultMemberAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/DefaultMemberAttribute.cs new file mode 100644 index 0000000000..585fdb07cd --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/DefaultMemberAttribute.cs @@ -0,0 +1,21 @@ +// 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. + +namespace System.Reflection +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)] + public sealed class DefaultMemberAttribute : Attribute + { + // You must provide the name of the member, this is required + public DefaultMemberAttribute(string memberName) + { + MemberName = memberName; + } + + // A get accessor to return the name from the attribute. + // NOTE: There is no setter because the name must be provided + // to the constructor. The name is not optional. + public string MemberName { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/EventAttributes.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/EventAttributes.cs new file mode 100644 index 0000000000..fbc2972f69 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/EventAttributes.cs @@ -0,0 +1,22 @@ +// 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. + +// EventAttributes are an enum defining the attributes associated with and Event. +// These are defined in CorHdr.h and are a combination of bits and enums. + +namespace System.Reflection +{ + [Flags] + public enum EventAttributes + { + None = 0x0000, + + // This Enum matchs the CorEventAttr defined in CorHdr.h + SpecialName = 0x0200, // event is special. Name describes how. + + RTSpecialName = 0x0400, // Runtime(metadata internal APIs) should check name encoding. + + ReservedMask = 0x0400, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/EventInfo.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/EventInfo.cs new file mode 100644 index 0000000000..ccd9acf648 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/EventInfo.cs @@ -0,0 +1,115 @@ +// 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.Diagnostics; + +#if FEATURE_COMINTEROP +using EventRegistrationToken = System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken; +#endif //#if FEATURE_COMINTEROP + +namespace System.Reflection +{ + public abstract class EventInfo : MemberInfo + { + protected EventInfo() { } + + public override MemberTypes MemberType => MemberTypes.Event; + + public abstract EventAttributes Attributes { get; } + public bool IsSpecialName => (Attributes & EventAttributes.SpecialName) != 0; + + public MethodInfo[] GetOtherMethods() => GetOtherMethods(nonPublic: false); + public virtual MethodInfo[] GetOtherMethods(bool nonPublic) { throw NotImplemented.ByDesign; } + + public virtual MethodInfo AddMethod => GetAddMethod(nonPublic: true); + public virtual MethodInfo RemoveMethod => GetRemoveMethod(nonPublic: true); + public virtual MethodInfo RaiseMethod => GetRaiseMethod(nonPublic: true); + + public MethodInfo GetAddMethod() => GetAddMethod(nonPublic: false); + public MethodInfo GetRemoveMethod() => GetRemoveMethod(nonPublic: false); + public MethodInfo GetRaiseMethod() => GetRaiseMethod(nonPublic: false); + + public abstract MethodInfo GetAddMethod(bool nonPublic); + public abstract MethodInfo GetRemoveMethod(bool nonPublic); + public abstract MethodInfo GetRaiseMethod(bool nonPublic); + + public virtual bool IsMulticast + { + get + { + Type cl = EventHandlerType; + Type mc = typeof(MulticastDelegate); + return mc.IsAssignableFrom(cl); + } + } + + public virtual Type EventHandlerType + { + get + { + MethodInfo m = GetAddMethod(true); + ParameterInfo[] p = m.GetParametersNoCopy(); + Type del = typeof(Delegate); + for (int i = 0; i < p.Length; i++) + { + Type c = p[i].ParameterType; + if (c.IsSubclassOf(del)) + return c; + } + return null; + } + } + + [DebuggerHidden] + [DebuggerStepThrough] + public virtual void AddEventHandler(object target, Delegate handler) + { + MethodInfo addMethod = GetAddMethod(nonPublic: false); + + if (addMethod == null) + throw new InvalidOperationException(SR.InvalidOperation_NoPublicAddMethod); + +#if FEATURE_COMINTEROP + if (addMethod.ReturnType == typeof(EventRegistrationToken)) + throw new InvalidOperationException(SR.InvalidOperation_NotSupportedOnWinRTEvent); +#endif //#if FEATURE_COMINTEROP + + addMethod.Invoke(target, new object[] { handler }); + } + + [DebuggerHidden] + [DebuggerStepThrough] + public virtual void RemoveEventHandler(object target, Delegate handler) + { + MethodInfo removeMethod = GetRemoveMethod(nonPublic: false); + + if (removeMethod == null) + throw new InvalidOperationException(SR.InvalidOperation_NoPublicRemoveMethod); + +#if FEATURE_COMINTEROP + ParameterInfo[] parameters = removeMethod.GetParametersNoCopy(); + if (parameters[0].ParameterType == typeof(EventRegistrationToken)) + throw new InvalidOperationException(SR.InvalidOperation_NotSupportedOnWinRTEvent); +#endif //#if FEATURE_COMINTEROP + + removeMethod.Invoke(target, new object[] { handler }); + } + + public override bool Equals(object obj) => base.Equals(obj); + public override int GetHashCode() => base.GetHashCode(); + + public static bool operator ==(EventInfo left, EventInfo right) + { + if (object.ReferenceEquals(left, right)) + return true; + + if ((object)left == null || (object)right == null) + return false; + + return left.Equals(right); + } + + public static bool operator !=(EventInfo left, EventInfo right) => !(left == right); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/ExceptionHandlingClauseOptions.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/ExceptionHandlingClauseOptions.cs new file mode 100644 index 0000000000..46285f7c82 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/ExceptionHandlingClauseOptions.cs @@ -0,0 +1,16 @@ +// 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. + +namespace System.Reflection +{ + [Flags] + public enum ExceptionHandlingClauseOptions : int + { + Clause = 0x0, + Filter = 0x1, + Finally = 0x2, + Fault = 0x4, + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/FieldAttributes.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/FieldAttributes.cs new file mode 100644 index 0000000000..048d0e7031 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/FieldAttributes.cs @@ -0,0 +1,40 @@ +// 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. + +namespace System.Reflection +{ + // This Enum matchs the CorFieldAttr defined in CorHdr.h + [Flags] + public enum FieldAttributes + { + // member access mask - Use this mask to retrieve accessibility information. + FieldAccessMask = 0x0007, + PrivateScope = 0x0000, // Member not referenceable. + Private = 0x0001, // Accessible only by the parent type. + FamANDAssem = 0x0002, // Accessible by sub-types only in this Assembly. + Assembly = 0x0003, // Accessibly by anyone in the Assembly. + Family = 0x0004, // Accessible only by type and sub-types. + FamORAssem = 0x0005, // Accessibly by sub-types anywhere, plus anyone in assembly. + Public = 0x0006, // Accessibly by anyone who has visibility to this scope. + // end member access mask + + // field contract attributes. + Static = 0x0010, // Defined on type, else per instance. + InitOnly = 0x0020, // Field may only be initialized, not written to after init. + Literal = 0x0040, // Value is compile time constant. + NotSerialized = 0x0080, // Field does not have to be serialized when type is remoted. + + SpecialName = 0x0200, // field is special. Name describes how. + + // interop attributes + PinvokeImpl = 0x2000, // Implementation is forwarded through pinvoke. + + RTSpecialName = 0x0400, // Runtime(metadata internal APIs) should check name encoding. + HasFieldMarshal = 0x1000, // Field has marshalling information. + HasDefault = 0x8000, // Field has default. + HasFieldRVA = 0x0100, // Field has RVA. + + ReservedMask = 0x9500, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/FieldInfo.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/FieldInfo.cs new file mode 100644 index 0000000000..0863c2b019 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/FieldInfo.cs @@ -0,0 +1,72 @@ +// 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.Diagnostics; +using System.Globalization; + +namespace System.Reflection +{ + public abstract partial class FieldInfo : MemberInfo + { + protected FieldInfo() { } + + public override MemberTypes MemberType => MemberTypes.Field; + + public abstract FieldAttributes Attributes { get; } + public abstract Type FieldType { get; } + + public bool IsInitOnly => (Attributes & FieldAttributes.InitOnly) != 0; + public bool IsLiteral => (Attributes & FieldAttributes.Literal) != 0; + public bool IsNotSerialized => (Attributes & FieldAttributes.NotSerialized) != 0; + public bool IsPinvokeImpl => (Attributes & FieldAttributes.PinvokeImpl) != 0; + public bool IsSpecialName => (Attributes & FieldAttributes.SpecialName) != 0; + public bool IsStatic => (Attributes & FieldAttributes.Static) != 0; + + public bool IsAssembly => (Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Assembly; + public bool IsFamily => (Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Family; + public bool IsFamilyAndAssembly => (Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.FamANDAssem; + public bool IsFamilyOrAssembly => (Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.FamORAssem; + public bool IsPrivate => (Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Private; + public bool IsPublic => (Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Public; + + public virtual bool IsSecurityCritical => true; + public virtual bool IsSecuritySafeCritical => false; + public virtual bool IsSecurityTransparent => false; + + public abstract RuntimeFieldHandle FieldHandle { get; } + + public override bool Equals(object obj) => base.Equals(obj); + public override int GetHashCode() => base.GetHashCode(); + + public static bool operator ==(FieldInfo left, FieldInfo right) + { + if (object.ReferenceEquals(left, right)) + return true; + + if ((object)left == null || (object)right == null) + return false; + + return left.Equals(right); + } + + public static bool operator !=(FieldInfo left, FieldInfo right) => !(left == right); + + public abstract object GetValue(object obj); + + [DebuggerHidden] + [DebuggerStepThrough] + public void SetValue(object obj, object value) => SetValue(obj, value, BindingFlags.Default, Type.DefaultBinder, null); + public abstract void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture); + + [CLSCompliant(false)] + public virtual void SetValueDirect(TypedReference obj, object value) { throw new NotSupportedException(SR.NotSupported_AbstractNonCLS); } + [CLSCompliant(false)] + public virtual object GetValueDirect(TypedReference obj) { throw new NotSupportedException(SR.NotSupported_AbstractNonCLS); } + + public virtual object GetRawConstantValue() { throw new NotSupportedException(SR.NotSupported_AbstractNonCLS); } + + public virtual Type[] GetOptionalCustomModifiers() { throw NotImplemented.ByDesign; } + public virtual Type[] GetRequiredCustomModifiers() { throw NotImplemented.ByDesign; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/GenericParameterAttributes.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/GenericParameterAttributes.cs new file mode 100644 index 0000000000..4b579d273e --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/GenericParameterAttributes.cs @@ -0,0 +1,20 @@ +// 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. + +namespace System.Reflection +{ + [Flags] + public enum GenericParameterAttributes + { + None = 0x0000, + VarianceMask = 0x0003, + Covariant = 0x0001, + Contravariant = 0x0002, + SpecialConstraintMask = 0x001C, + ReferenceTypeConstraint = 0x0004, + NotNullableValueTypeConstraint = 0x0008, + DefaultConstructorConstraint = 0x0010, + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/ICustomAttributeProvider.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/ICustomAttributeProvider.cs new file mode 100644 index 0000000000..3cae295bc4 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/ICustomAttributeProvider.cs @@ -0,0 +1,13 @@ +// 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. + +namespace System.Reflection +{ + public interface ICustomAttributeProvider + { + object[] GetCustomAttributes(bool inherit); + object[] GetCustomAttributes(Type attributeType, bool inherit); + bool IsDefined(Type attributeType, bool inherit); + } +} \ No newline at end of file diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/IReflect.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/IReflect.cs new file mode 100644 index 0000000000..a7e84b6168 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/IReflect.cs @@ -0,0 +1,76 @@ +// 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.Globalization; + +namespace System.Reflection +{ + public interface IReflect + { + // Return the requested method if it is implemented by the Reflection object. The + // match is based upon the name and DescriptorInfo which describes the signature + // of the method. + MethodInfo GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers); + + // Return the requested method if it is implemented by the Reflection object. The + // match is based upon the name of the method. If the object implementes multiple methods + // with the same name an AmbiguousMatchException is thrown. + MethodInfo GetMethod(string name, BindingFlags bindingAttr); + + MethodInfo[] GetMethods(BindingFlags bindingAttr); + + // Return the requestion field if it is implemented by the Reflection object. The + // match is based upon a name. There cannot be more than a single field with + // a name. + FieldInfo GetField(string name, BindingFlags bindingAttr); + + FieldInfo[] GetFields(BindingFlags bindingAttr); + + // Return the property based upon name. If more than one property has the given + // name an AmbiguousMatchException will be thrown. Returns null if no property + // is found. + PropertyInfo GetProperty(string name, BindingFlags bindingAttr); + + // Return the property based upon the name and Descriptor info describing the property + // indexing. Return null if no property is found. + PropertyInfo GetProperty(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers); + + // Returns an array of PropertyInfos for all the properties defined on + // the Reflection object. + PropertyInfo[] GetProperties(BindingFlags bindingAttr); + + // Return an array of members which match the passed in name. + MemberInfo[] GetMember(string name, BindingFlags bindingAttr); + + // Return an array of all of the members defined for this object. + MemberInfo[] GetMembers(BindingFlags bindingAttr); + + // Description of the Binding Process. + // We must invoke a method that is accessible and for which the provided + // parameters have the most specific match. A method may be called if + // 1. The number of parameters in the method declaration equals the number of + // arguments provided to the invocation + // 2. The type of each argument can be converted by the binder to the + // type of the type of the parameter. + // + // The binder will find all of the matching methods. These method are found based + // upon the type of binding requested (MethodInvoke, Get/Set Properties). The set + // of methods is filtered by the name, number of arguments and a set of search modifiers + // defined in the Binder. + // + // After the method is selected, it will be invoked. Accessibility is checked + // at that point. The search may be control which set of methods are searched based + // upon the accessibility attribute associated with the method. + // + // The BindToMethod method is responsible for selecting the method to be invoked. + // For the default binder, the most specific method will be selected. + // + // This will invoke a specific member... + object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters); + + // Return the underlying Type that represents the IReflect Object. For expando object, + // this is the (Object) IReflectInstance.GetType(). For Type object it is this. + Type UnderlyingSystemType { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/IReflectableType.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/IReflectableType.cs new file mode 100644 index 0000000000..5e2c0edab4 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/IReflectableType.cs @@ -0,0 +1,12 @@ +// 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. + +namespace System.Reflection +{ + public interface IReflectableType + { + TypeInfo GetTypeInfo(); + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/ImageFileMachine.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/ImageFileMachine.cs new file mode 100644 index 0000000000..230bc952e5 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/ImageFileMachine.cs @@ -0,0 +1,15 @@ +// 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. + +namespace System.Reflection +{ + public enum ImageFileMachine + { + I386 = 0x014c, + IA64 = 0x0200, + AMD64 = 0x8664, + ARM = 0x01c4, + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/InterfaceMapping.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/InterfaceMapping.cs new file mode 100644 index 0000000000..2e0c0d8a28 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/InterfaceMapping.cs @@ -0,0 +1,14 @@ +// 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. + +namespace System.Reflection +{ + public struct InterfaceMapping + { + public Type TargetType; // The type implementing the interface + public Type InterfaceType; // The type representing the interface + public MethodInfo[] TargetMethods; // The methods implementing the interface + public MethodInfo[] InterfaceMethods; // The methods defined on the interface + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/IntrospectionExtensions.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/IntrospectionExtensions.cs new file mode 100644 index 0000000000..acf5987d44 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/IntrospectionExtensions.cs @@ -0,0 +1,23 @@ +// 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.Diagnostics; + +namespace System.Reflection +{ + public static class IntrospectionExtensions + { + public static TypeInfo GetTypeInfo(this Type type) + { + if (type == null) + throw new ArgumentNullException(nameof(type)); + + if (type is IReflectableType reflectableType) + return reflectableType.GetTypeInfo(); + + return new TypeDelegator(type); + } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/InvalidFilterCriteriaException.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/InvalidFilterCriteriaException.cs new file mode 100644 index 0000000000..dedcc54f4c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/InvalidFilterCriteriaException.cs @@ -0,0 +1,34 @@ +// 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.Runtime.Serialization; + +namespace System.Reflection +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class InvalidFilterCriteriaException : ApplicationException + { + public InvalidFilterCriteriaException() + : this(SR.Arg_InvalidFilterCriteriaException) + { + } + + public InvalidFilterCriteriaException(string message) + : this(message, null) + { + } + + public InvalidFilterCriteriaException(string message, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_INVALIDFILTERCRITERIA; + } + + protected InvalidFilterCriteriaException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/ManifestResourceInfo.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/ManifestResourceInfo.cs new file mode 100644 index 0000000000..b9c56ab857 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/ManifestResourceInfo.cs @@ -0,0 +1,23 @@ +// 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. + +namespace System.Reflection +{ + public class ManifestResourceInfo + { + public ManifestResourceInfo(Assembly containingAssembly, + string containingFileName, + ResourceLocation resourceLocation) + { + ReferencedAssembly = containingAssembly; + FileName = containingFileName; + ResourceLocation = resourceLocation; + } + + public virtual Assembly ReferencedAssembly { get; } + public virtual string FileName { get; } + public virtual ResourceLocation ResourceLocation { get; } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/MemberFilter.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/MemberFilter.cs new file mode 100644 index 0000000000..bb1b15796a --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/MemberFilter.cs @@ -0,0 +1,8 @@ +// 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. + +namespace System.Reflection +{ + public delegate bool MemberFilter(MemberInfo m, object filterCriteria); +} \ No newline at end of file diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/MemberInfo.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/MemberInfo.cs new file mode 100644 index 0000000000..d8a7458632 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/MemberInfo.cs @@ -0,0 +1,77 @@ +// 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.Collections.Generic; + +namespace System.Reflection +{ + public abstract partial class MemberInfo : ICustomAttributeProvider + { + protected MemberInfo() { } + + public abstract MemberTypes MemberType { get; } + public abstract string Name { get; } + public abstract Type DeclaringType { get; } + public abstract Type ReflectedType { get; } + + public virtual Module Module + { + get + { + // This check is necessary because for some reason, Type adds a new "Module" property that hides the inherited one instead + // of overriding. + + Type type = this as Type; + if (type != null) + return type.Module; + + throw NotImplemented.ByDesign; + } + } + + public virtual bool HasSameMetadataDefinitionAs(MemberInfo other) { throw NotImplemented.ByDesign; } + + public abstract bool IsDefined(Type attributeType, bool inherit); + public abstract object[] GetCustomAttributes(bool inherit); + public abstract object[] GetCustomAttributes(Type attributeType, bool inherit); + + public virtual IEnumerable CustomAttributes => GetCustomAttributesData(); + public virtual IList GetCustomAttributesData() { throw NotImplemented.ByDesign; } + + public virtual int MetadataToken { get { throw new InvalidOperationException(); } } + + public override bool Equals(object obj) => base.Equals(obj); + public override int GetHashCode() => base.GetHashCode(); + + public static bool operator ==(MemberInfo left, MemberInfo right) + { + if (object.ReferenceEquals(left, right)) + return true; + + if ((object)left == null || (object)right == null) + return false; + + Type type1, type2; + MethodBase method1, method2; + FieldInfo field1, field2; + EventInfo event1, event2; + PropertyInfo property1, property2; + + if ((type1 = left as Type) != null && (type2 = right as Type) != null) + return type1 == type2; + else if ((method1 = left as MethodBase) != null && (method2 = right as MethodBase) != null) + return method1 == method2; + else if ((field1 = left as FieldInfo) != null && (field2 = right as FieldInfo) != null) + return field1 == field2; + else if ((event1 = left as EventInfo) != null && (event2 = right as EventInfo) != null) + return event1 == event2; + else if ((property1 = left as PropertyInfo) != null && (property2 = right as PropertyInfo) != null) + return property1 == property2; + + return false; + } + + public static bool operator !=(MemberInfo left, MemberInfo right) => !(left == right); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/MemberTypes.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/MemberTypes.cs new file mode 100644 index 0000000000..57072dcfbe --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/MemberTypes.cs @@ -0,0 +1,20 @@ +// 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. + +namespace System.Reflection +{ + [Flags] + public enum MemberTypes + { + Constructor = 0x01, + Event = 0x02, + Field = 0x04, + Method = 0x08, + Property = 0x10, + TypeInfo = 0x20, + Custom = 0x40, + NestedType = 0x80, + All = Constructor | Event | Field | Method | Property | TypeInfo | NestedType, + } +} \ No newline at end of file diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/MethodAttributes.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/MethodAttributes.cs new file mode 100644 index 0000000000..1a7c7bf154 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/MethodAttributes.cs @@ -0,0 +1,50 @@ +// 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. + +namespace System.Reflection +{ + [Flags] + public enum MethodAttributes + { + // NOTE: This Enum matchs the CorMethodAttr defined in CorHdr.h + + // member access mask - Use this mask to retrieve accessibility information. + MemberAccessMask = 0x0007, + PrivateScope = 0x0000, // Member not referenceable. + Private = 0x0001, // Accessible only by the parent type. + FamANDAssem = 0x0002, // Accessible by sub-types only in this Assembly. + Assembly = 0x0003, // Accessibly by anyone in the Assembly. + Family = 0x0004, // Accessible only by type and sub-types. + FamORAssem = 0x0005, // Accessibly by sub-types anywhere, plus anyone in assembly. + Public = 0x0006, // Accessibly by anyone who has visibility to this scope. + // end member access mask + + // method contract attributes. + Static = 0x0010, // Defined on type, else per instance. + Final = 0x0020, // Method may not be overridden. + Virtual = 0x0040, // Method virtual. + HideBySig = 0x0080, // Method hides by name+sig, else just by name. + CheckAccessOnOverride = 0x0200, + + // vtable layout mask - Use this mask to retrieve vtable attributes. + VtableLayoutMask = 0x0100, + ReuseSlot = 0x0000, // The default. + NewSlot = 0x0100, // Method always gets a new slot in the vtable. + // end vtable layout mask + + // method implementation attributes. + Abstract = 0x0400, // Method does not provide an implementation. + SpecialName = 0x0800, // Method is special. Name describes how. + + // interop attributes + PinvokeImpl = 0x2000, // Implementation is forwarded through pinvoke. + UnmanagedExport = 0x0008, // Managed method exported via thunk to unmanaged code. + RTSpecialName = 0x1000, // Runtime should check name encoding. + + HasSecurity = 0x4000, // Method has security associate with it. + RequireSecObject = 0x8000, // Method calls another method containing security code. + + ReservedMask = 0xd000, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/MethodBase.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/MethodBase.cs new file mode 100644 index 0000000000..0037c74f4c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/MethodBase.cs @@ -0,0 +1,86 @@ +// 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.Globalization; +using System.Diagnostics; + +namespace System.Reflection +{ + public abstract partial class MethodBase : MemberInfo + { + protected MethodBase() { } + + public abstract ParameterInfo[] GetParameters(); + public abstract MethodAttributes Attributes { get; } + public virtual MethodImplAttributes MethodImplementationFlags => GetMethodImplementationFlags(); + public abstract MethodImplAttributes GetMethodImplementationFlags(); + public virtual MethodBody GetMethodBody() { throw new InvalidOperationException(); } + public virtual CallingConventions CallingConvention => CallingConventions.Standard; + + public bool IsAbstract => (Attributes & MethodAttributes.Abstract) != 0; + public bool IsConstructor + { + get + { + // To be backward compatible we only return true for instance RTSpecialName ctors. + return (this is ConstructorInfo && + !IsStatic && + ((Attributes & MethodAttributes.RTSpecialName) == MethodAttributes.RTSpecialName)); + } + } + public bool IsFinal => (Attributes & MethodAttributes.Final) != 0; + public bool IsHideBySig => (Attributes & MethodAttributes.HideBySig) != 0; + public bool IsSpecialName => (Attributes & MethodAttributes.SpecialName) != 0; + public bool IsStatic => (Attributes & MethodAttributes.Static) != 0; + public bool IsVirtual => (Attributes & MethodAttributes.Virtual) != 0; + + public bool IsAssembly => (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Assembly; + public bool IsFamily => (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Family; + public bool IsFamilyAndAssembly => (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamANDAssem; + public bool IsFamilyOrAssembly => (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamORAssem; + public bool IsPrivate => (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Private; + public bool IsPublic => (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Public; + + public virtual bool IsConstructedGenericMethod => IsGenericMethod && !IsGenericMethodDefinition; + public virtual bool IsGenericMethod => false; + public virtual bool IsGenericMethodDefinition => false; + public virtual Type[] GetGenericArguments() { throw new NotSupportedException(SR.NotSupported_SubclassOverride); } + public virtual bool ContainsGenericParameters => false; + + [DebuggerHidden] + [DebuggerStepThrough] + public object Invoke(object obj, object[] parameters) => Invoke(obj, BindingFlags.Default, binder: null, parameters: parameters, culture: null); + public abstract object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture); + + public abstract RuntimeMethodHandle MethodHandle { get; } + + public virtual bool IsSecurityCritical { get { throw NotImplemented.ByDesign; } } + public virtual bool IsSecuritySafeCritical { get { throw NotImplemented.ByDesign; } } + public virtual bool IsSecurityTransparent { get { throw NotImplemented.ByDesign; } } + + public override bool Equals(object obj) => base.Equals(obj); + public override int GetHashCode() => base.GetHashCode(); + + public static bool operator ==(MethodBase left, MethodBase right) + { + if (object.ReferenceEquals(left, right)) + return true; + + if ((object)left == null || (object)right == null) + return false; + + MethodInfo method1, method2; + ConstructorInfo constructor1, constructor2; + + if ((method1 = left as MethodInfo) != null && (method2 = right as MethodInfo) != null) + return method1 == method2; + else if ((constructor1 = left as ConstructorInfo) != null && (constructor2 = right as ConstructorInfo) != null) + return constructor1 == constructor2; + + return false; + } + + public static bool operator !=(MethodBase left, MethodBase right) => !(left == right); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/MethodImplAttributes.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/MethodImplAttributes.cs new file mode 100644 index 0000000000..a1ed326002 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/MethodImplAttributes.cs @@ -0,0 +1,37 @@ +// 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. + +namespace System.Reflection +{ + // This Enum matchs the CorMethodImpl defined in CorHdr.h + public enum MethodImplAttributes + { + // code impl mask + CodeTypeMask = 0x0003, // Flags about code type. + IL = 0x0000, // Method impl is IL. + Native = 0x0001, // Method impl is native. + OPTIL = 0x0002, // Method impl is OPTIL + Runtime = 0x0003, // Method impl is provided by the runtime. + // end code impl mask + + // managed mask + ManagedMask = 0x0004, // Flags specifying whether the code is managed or unmanaged. + Unmanaged = 0x0004, // Method impl is unmanaged, otherwise managed. + Managed = 0x0000, // Method impl is managed. + // end managed mask + + // implementation info and interop + ForwardRef = 0x0010, // Indicates method is not defined; used primarily in merge scenarios. + PreserveSig = 0x0080, // Indicates method sig is exported exactly as declared. + + InternalCall = 0x1000, // Internal Call... + + Synchronized = 0x0020, // Method is single threaded through the body. + NoInlining = 0x0008, // Method may not be inlined. + AggressiveInlining = 0x0100, // Method should be inlined if possible. + NoOptimization = 0x0040, // Method may not be optimized. + + MaxMethodImplVal = 0xffff, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/MethodInfo.Internal.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/MethodInfo.Internal.cs new file mode 100644 index 0000000000..2806be639e --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/MethodInfo.Internal.cs @@ -0,0 +1,16 @@ +// 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. + +namespace System.Reflection +{ + public abstract partial class MethodInfo : MethodBase + { +#if CORERT + public // Needs to be public so that Reflection.Core can see it. +#else + internal +#endif + virtual int GenericParameterCount => GetGenericArguments().Length; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/MethodInfo.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/MethodInfo.cs new file mode 100644 index 0000000000..95c62ba7f0 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/MethodInfo.cs @@ -0,0 +1,43 @@ +// 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. + +namespace System.Reflection +{ + public abstract partial class MethodInfo : MethodBase + { + protected MethodInfo() { } + + public override MemberTypes MemberType => MemberTypes.Method; + + public virtual ParameterInfo ReturnParameter { get { throw NotImplemented.ByDesign; } } + public virtual Type ReturnType { get { throw NotImplemented.ByDesign; } } + + public override Type[] GetGenericArguments() { throw new NotSupportedException(SR.NotSupported_SubclassOverride); } + public virtual MethodInfo GetGenericMethodDefinition() { throw new NotSupportedException(SR.NotSupported_SubclassOverride); } + public virtual MethodInfo MakeGenericMethod(params Type[] typeArguments) { throw new NotSupportedException(SR.NotSupported_SubclassOverride); } + + public abstract MethodInfo GetBaseDefinition(); + + public abstract ICustomAttributeProvider ReturnTypeCustomAttributes { get; } + + public virtual Delegate CreateDelegate(Type delegateType) { throw new NotSupportedException(SR.NotSupported_SubclassOverride); } + public virtual Delegate CreateDelegate(Type delegateType, object target) { throw new NotSupportedException(SR.NotSupported_SubclassOverride); } + + public override bool Equals(object obj) => base.Equals(obj); + public override int GetHashCode() => base.GetHashCode(); + + public static bool operator ==(MethodInfo left, MethodInfo right) + { + if (object.ReferenceEquals(left, right)) + return true; + + if ((object)left == null || (object)right == null) + return false; + + return left.Equals(right); + } + + public static bool operator !=(MethodInfo left, MethodInfo right) => !(left == right); + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Empty.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/Missing.cs similarity index 56% rename from external/corert/src/System.Private.CoreLib/src/System/Empty.cs rename to external/corefx/src/Common/src/CoreLib/System/Reflection/Missing.cs index f775eb9986..46ab32fccf 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Empty.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/Missing.cs @@ -4,16 +4,15 @@ using System.Runtime.Serialization; -namespace System +namespace System.Reflection { - [Serializable] - public sealed class Empty : ISerializable + public sealed class Missing : ISerializable { - private Empty() { } + public static readonly Missing Value = new Missing(); - public static readonly Empty Value = new Empty(); + private Missing() { } - public void GetObjectData(SerializationInfo info, StreamingContext context) + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { throw new PlatformNotSupportedException(); } diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/Module.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/Module.cs new file mode 100644 index 0000000000..56f83c40d9 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/Module.cs @@ -0,0 +1,182 @@ +// 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.Collections.Generic; +using System.Runtime.Serialization; + +namespace System.Reflection +{ + public abstract class Module : ICustomAttributeProvider, ISerializable + { + protected Module() { } + + public virtual Assembly Assembly { get { throw NotImplemented.ByDesign; } } + public virtual string FullyQualifiedName { get { throw NotImplemented.ByDesign; } } + public virtual string Name { get { throw NotImplemented.ByDesign; } } + + public virtual int MDStreamVersion { get { throw NotImplemented.ByDesign; } } + public virtual Guid ModuleVersionId { get { throw NotImplemented.ByDesign; } } + public virtual string ScopeName { get { throw NotImplemented.ByDesign; } } + public ModuleHandle ModuleHandle => GetModuleHandleImpl(); + protected virtual ModuleHandle GetModuleHandleImpl() => ModuleHandle.EmptyHandle; // Not an api but declared protected because of Reflection.Core/Corelib divide (when built by CoreRt) + public virtual void GetPEKind(out PortableExecutableKinds peKind, out ImageFileMachine machine) { throw NotImplemented.ByDesign; } + public virtual bool IsResource() { throw NotImplemented.ByDesign; } + + public virtual bool IsDefined(Type attributeType, bool inherit) { throw NotImplemented.ByDesign; } + public virtual IEnumerable CustomAttributes => GetCustomAttributesData(); + public virtual IList GetCustomAttributesData() { throw NotImplemented.ByDesign; } + public virtual object[] GetCustomAttributes(bool inherit) { throw NotImplemented.ByDesign; } + public virtual object[] GetCustomAttributes(Type attributeType, bool inherit) { throw NotImplemented.ByDesign; } + + public MethodInfo GetMethod(string name) + { + if (name == null) + throw new ArgumentNullException(nameof(name)); + + return GetMethodImpl(name, Module.DefaultLookup, null, CallingConventions.Any, null, null); + } + + public MethodInfo GetMethod(string name, Type[] types) => GetMethod(name, Module.DefaultLookup, null, CallingConventions.Any, types, null); + public MethodInfo GetMethod(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + if (name == null) + throw new ArgumentNullException(nameof(name)); + if (types == null) + throw new ArgumentNullException(nameof(types)); + for (int i = 0; i < types.Length; i++) + { + if (types[i] == null) + throw new ArgumentNullException(nameof(types)); + } + return GetMethodImpl(name, bindingAttr, binder, callConvention, types, modifiers); + } + + protected virtual MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) { throw NotImplemented.ByDesign; } + + public MethodInfo[] GetMethods() => GetMethods(Module.DefaultLookup); + public virtual MethodInfo[] GetMethods(BindingFlags bindingFlags) { throw NotImplemented.ByDesign; } + + public FieldInfo GetField(string name) => GetField(name, Module.DefaultLookup); + public virtual FieldInfo GetField(string name, BindingFlags bindingAttr) { throw NotImplemented.ByDesign; } + + public FieldInfo[] GetFields() => GetFields(Module.DefaultLookup); + public virtual FieldInfo[] GetFields(BindingFlags bindingFlags) { throw NotImplemented.ByDesign; } + + public virtual Type[] GetTypes() { throw NotImplemented.ByDesign; } + + public virtual Type GetType(string className) => GetType(className, throwOnError: false, ignoreCase: false); + public virtual Type GetType(string className, bool ignoreCase) => GetType(className, throwOnError: false, ignoreCase: ignoreCase); + public virtual Type GetType(string className, bool throwOnError, bool ignoreCase) { throw NotImplemented.ByDesign; } + + public virtual Type[] FindTypes(TypeFilter filter, object filterCriteria) + { + Type[] c = GetTypes(); + int cnt = 0; + for (int i = 0; i < c.Length; i++) + { + if (filter != null && !filter(c[i], filterCriteria)) + c[i] = null; + else + cnt++; + } + if (cnt == c.Length) + return c; + + Type[] ret = new Type[cnt]; + cnt = 0; + for (int i = 0; i < c.Length; i++) + { + if (c[i] != null) + ret[cnt++] = c[i]; + } + return ret; + } + + public virtual int MetadataToken { get { throw NotImplemented.ByDesign; } } + + public FieldInfo ResolveField(int metadataToken) => ResolveField(metadataToken, null, null); + public virtual FieldInfo ResolveField(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) { throw NotImplemented.ByDesign; } + + public MemberInfo ResolveMember(int metadataToken) => ResolveMember(metadataToken, null, null); + public virtual MemberInfo ResolveMember(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) { throw NotImplemented.ByDesign; } + + public MethodBase ResolveMethod(int metadataToken) => ResolveMethod(metadataToken, null, null); + public virtual MethodBase ResolveMethod(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) { throw NotImplemented.ByDesign; } + + public virtual byte[] ResolveSignature(int metadataToken) { throw NotImplemented.ByDesign; } + public virtual string ResolveString(int metadataToken) { throw NotImplemented.ByDesign; } + + public Type ResolveType(int metadataToken) => ResolveType(metadataToken, null, null); + public virtual Type ResolveType(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) { throw NotImplemented.ByDesign; } + + public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { throw NotImplemented.ByDesign; } + + public override bool Equals(object o) => base.Equals(o); + public override int GetHashCode() => base.GetHashCode(); + + public static bool operator ==(Module left, Module right) + { + if (object.ReferenceEquals(left, right)) + return true; + + if ((object)left == null || (object)right == null) + return false; + + return left.Equals(right); + } + + public static bool operator !=(Module left, Module right) => !(left == right); + + public override string ToString() => ScopeName; + + public static readonly TypeFilter FilterTypeName = FilterTypeNameImpl; + public static readonly TypeFilter FilterTypeNameIgnoreCase = FilterTypeNameIgnoreCaseImpl; + + private const BindingFlags DefaultLookup = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; + + // FilterTypeName + // This method will filter the class based upon the name. It supports + // a trailing wild card. + private static bool FilterTypeNameImpl(Type cls, object filterCriteria) + { + // Check that the criteria object is a String object + if (filterCriteria == null || !(filterCriteria is string)) + throw new InvalidFilterCriteriaException(SR.InvalidFilterCriteriaException_CritString); + + string str = (string)filterCriteria; + + // Check to see if this is a prefix or exact match requirement + if (str.Length > 0 && str[str.Length - 1] == '*') + { + str = str.Substring(0, str.Length - 1); + return cls.Name.StartsWith(str, StringComparison.Ordinal); + } + + return cls.Name.Equals(str); + } + + // FilterFieldNameIgnoreCase + // This method filter the Type based upon name, it ignores case. + private static bool FilterTypeNameIgnoreCaseImpl(Type cls, object filterCriteria) + { + // Check that the criteria object is a String object + if (filterCriteria == null || !(filterCriteria is string)) + throw new InvalidFilterCriteriaException(SR.InvalidFilterCriteriaException_CritString); + + string str = (string)filterCriteria; + + // Check to see if this is a prefix or exact match requirement + if (str.Length > 0 && str[str.Length - 1] == '*') + { + str = str.Substring(0, str.Length - 1); + string name = cls.Name; + if (name.Length >= str.Length) + return (string.Compare(name, 0, str, 0, str.Length, StringComparison.OrdinalIgnoreCase) == 0); + else + return false; + } + return (string.Compare(str, cls.Name, StringComparison.OrdinalIgnoreCase) == 0); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/ModuleResolveEventHandler.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/ModuleResolveEventHandler.cs new file mode 100644 index 0000000000..eb8926b5db --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/ModuleResolveEventHandler.cs @@ -0,0 +1,9 @@ +// 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. + +namespace System.Reflection +{ + public delegate Module ModuleResolveEventHandler(object sender, ResolveEventArgs e); +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/ObfuscateAssemblyAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/ObfuscateAssemblyAttribute.cs new file mode 100644 index 0000000000..f8f765ced2 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/ObfuscateAssemblyAttribute.cs @@ -0,0 +1,19 @@ +// 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. + +namespace System.Reflection +{ + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)] + public sealed class ObfuscateAssemblyAttribute : Attribute + { + public ObfuscateAssemblyAttribute(bool assemblyIsPrivate) + { + AssemblyIsPrivate = assemblyIsPrivate; + } + + public bool AssemblyIsPrivate { get; } + public bool StripAfterObfuscation { get; set; } = true; + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/ObfuscationAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/ObfuscationAttribute.cs new file mode 100644 index 0000000000..11d93b6313 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/ObfuscationAttribute.cs @@ -0,0 +1,21 @@ +// 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. + +namespace System.Reflection +{ + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Enum | AttributeTargets.Delegate, + AllowMultiple = true, Inherited = false)] + public sealed class ObfuscationAttribute : Attribute + { + public ObfuscationAttribute() + { + } + + public bool StripAfterObfuscation { get; set; } = true; + public bool Exclude { get; set; } = true; + public bool ApplyToMembers { get; set; } = true; + public string Feature { get; set; } = "all"; + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/ParameterAttributes.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/ParameterAttributes.cs new file mode 100644 index 0000000000..ce195897c2 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/ParameterAttributes.cs @@ -0,0 +1,29 @@ +// 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. + +// ParameterAttributes is an enum defining the attributes that may be +// associated with a Parameter. These are defined in CorHdr.h. + +namespace System.Reflection +{ + // This Enum matchs the CorParamAttr defined in CorHdr.h + [Flags] + public enum ParameterAttributes + { + None = 0x0000, // no flag is specified + In = 0x0001, // Param is [In] + Out = 0x0002, // Param is [Out] + Lcid = 0x0004, // Param is [lcid] + + Retval = 0x0008, // Param is [Retval] + Optional = 0x0010, // Param is optional + + HasDefault = 0x1000, // Param has default value. + HasFieldMarshal = 0x2000, // Param has FieldMarshal. + + Reserved3 = 0x4000, + Reserved4 = 0x8000, + ReservedMask = 0xf000, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/ParameterInfo.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/ParameterInfo.cs new file mode 100644 index 0000000000..94bfffaa53 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/ParameterInfo.cs @@ -0,0 +1,110 @@ +// 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.Collections.Generic; +using System.Runtime.Serialization; + +namespace System.Reflection +{ + public class ParameterInfo : ICustomAttributeProvider, IObjectReference + { + protected ParameterInfo() { } + + public virtual ParameterAttributes Attributes => AttrsImpl; + public virtual MemberInfo Member => MemberImpl; + public virtual string Name => NameImpl; + public virtual Type ParameterType => ClassImpl; + public virtual int Position => PositionImpl; + + public bool IsIn => (Attributes & ParameterAttributes.In) != 0; + public bool IsLcid => (Attributes & ParameterAttributes.Lcid) != 0; + public bool IsOptional => (Attributes & ParameterAttributes.Optional) != 0; + public bool IsOut => (Attributes & ParameterAttributes.Out) != 0; + public bool IsRetval => (Attributes & ParameterAttributes.Retval) != 0; + + public virtual object DefaultValue { get { throw NotImplemented.ByDesign; } } + public virtual object RawDefaultValue { get { throw NotImplemented.ByDesign; } } + public virtual bool HasDefaultValue { get { throw NotImplemented.ByDesign; } } + + public virtual bool IsDefined(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + + return false; + } + + public virtual IEnumerable CustomAttributes => GetCustomAttributesData(); + public virtual IList GetCustomAttributesData() { throw NotImplemented.ByDesign; } + + public virtual object[] GetCustomAttributes(bool inherit) => Array.Empty(); + public virtual object[] GetCustomAttributes(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + + return Array.Empty(); + } + + public virtual Type[] GetOptionalCustomModifiers() => Array.Empty(); + public virtual Type[] GetRequiredCustomModifiers() => Array.Empty(); + + public virtual int MetadataToken => MetadataToken_ParamDef; + + public object GetRealObject(StreamingContext context) + { + // Once all the serializable fields have come in we can set up the real + // instance based on just two of them (MemberImpl and PositionImpl). + + if (MemberImpl == null) + throw new SerializationException(SR.Serialization_InsufficientState); + + ParameterInfo[] args = null; + + switch (MemberImpl.MemberType) + { + case MemberTypes.Constructor: + case MemberTypes.Method: + if (PositionImpl == -1) + { + if (MemberImpl.MemberType == MemberTypes.Method) + return ((MethodInfo)MemberImpl).ReturnParameter; + else + throw new SerializationException(SR.Serialization_BadParameterInfo); + } + else + { + args = ((MethodBase)MemberImpl).GetParametersNoCopy(); + + if (args != null && PositionImpl < args.Length) + return args[PositionImpl]; + else + throw new SerializationException(SR.Serialization_BadParameterInfo); + } + + case MemberTypes.Property: + args = ((PropertyInfo)MemberImpl).GetIndexParameters(); + + if (args != null && PositionImpl > -1 && PositionImpl < args.Length) + return args[PositionImpl]; + else + throw new SerializationException(SR.Serialization_BadParameterInfo); + + default: + throw new SerializationException(SR.Serialization_NoParameterInfo); + } + } + + public override string ToString() => ParameterType.FormatTypeName() + " " + Name; + + protected ParameterAttributes AttrsImpl; + protected Type ClassImpl; + protected object DefaultValueImpl; + protected MemberInfo MemberImpl; + protected string NameImpl; + protected int PositionImpl; + + private const int MetadataToken_ParamDef = 0x08000000; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/ParameterModifier.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/ParameterModifier.cs new file mode 100644 index 0000000000..0fb75ffbd8 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/ParameterModifier.cs @@ -0,0 +1,35 @@ +// 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. + +namespace System.Reflection +{ + public readonly struct ParameterModifier + { + private readonly bool[] _byRef; + + public ParameterModifier(int parameterCount) + { + if (parameterCount <= 0) + throw new ArgumentException(SR.Arg_ParmArraySize); + + _byRef = new bool[parameterCount]; + } + + public bool this[int index] + { + get + { + return _byRef[index]; + } + set + { + _byRef[index] = value; + } + } + +#if CORECLR + internal bool[] IsByRefArray => _byRef; +#endif + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/Pointer.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/Pointer.cs new file mode 100644 index 0000000000..e1a9990cf0 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/Pointer.cs @@ -0,0 +1,51 @@ +// 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.Diagnostics; +using System.Runtime.Serialization; + +namespace System.Reflection +{ + [CLSCompliant(false)] + public sealed unsafe class Pointer : ISerializable + { + // CoreCLR: Do not add or remove fields without updating the ReflectionPointer class in runtimehandles.h + private readonly void* _ptr; + private readonly Type _ptrType; + + private Pointer(void* ptr, Type ptrType) + { + Debug.Assert(ptrType.IsRuntimeImplemented()); // CoreCLR: For CoreRT's sake, _ptrType has to be declared as "Type", but in fact, it is always a RuntimeType. Code on CoreCLR expects this. + _ptr = ptr; + _ptrType = ptrType; + } + + public static object Box(void* ptr, Type type) + { + if (type == null) + throw new ArgumentNullException(nameof(type)); + if (!type.IsPointer) + throw new ArgumentException(SR.Arg_MustBePointer, nameof(ptr)); + if (!type.IsRuntimeImplemented()) + throw new ArgumentException(SR.Arg_MustBeType, nameof(ptr)); + + return new Pointer(ptr, type); + } + + public static void* Unbox(object ptr) + { + if (!(ptr is Pointer)) + throw new ArgumentException(SR.Arg_MustBePointer, nameof(ptr)); + return ((Pointer)ptr)._ptr; + } + + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + throw new PlatformNotSupportedException(); + } + + internal Type GetPointerType() => _ptrType; + internal IntPtr GetPointerValue() => (IntPtr)_ptr; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/PortableExecutableKinds.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/PortableExecutableKinds.cs new file mode 100644 index 0000000000..79be338685 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/PortableExecutableKinds.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.Reflection +{ + [Flags] + public enum PortableExecutableKinds + { + NotAPortableExecutableImage = 0x0, + ILOnly = 0x1, + Required32Bit = 0x2, + PE32Plus = 0x4, + Unmanaged32Bit = 0x8, + Preferred32Bit = 0x10, + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/ProcessorArchitecture.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/ProcessorArchitecture.cs new file mode 100644 index 0000000000..becb346c4f --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/ProcessorArchitecture.cs @@ -0,0 +1,16 @@ +// 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. + +namespace System.Reflection +{ + public enum ProcessorArchitecture + { + None = 0x0000, + MSIL = 0x0001, + X86 = 0x0002, + IA64 = 0x0003, + Amd64 = 0x0004, + Arm = 0x0005 + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/PropertyAttributes.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/PropertyAttributes.cs new file mode 100644 index 0000000000..31e7a653bb --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/PropertyAttributes.cs @@ -0,0 +1,25 @@ +// 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. + +// PropertyAttributes is an enum which defines the attributes that may be associated +// with a property. The values here are defined in Corhdr.h. + +namespace System.Reflection +{ + // This Enum matchs the CorPropertyAttr defined in CorHdr.h + [Flags] + public enum PropertyAttributes + { + None = 0x0000, + SpecialName = 0x0200, // property is special. Name describes how. + + RTSpecialName = 0x0400, // Runtime(metadata internal APIs) should check name encoding. + HasDefault = 0x1000, // Property has default + + Reserved2 = 0x2000, + Reserved3 = 0x4000, + Reserved4 = 0x8000, + ReservedMask = 0xf400, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/PropertyInfo.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/PropertyInfo.cs new file mode 100644 index 0000000000..ff8a02e96d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/PropertyInfo.cs @@ -0,0 +1,74 @@ +// 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.Diagnostics; +using System.Globalization; + +namespace System.Reflection +{ + public abstract class PropertyInfo : MemberInfo + { + protected PropertyInfo() { } + + public override MemberTypes MemberType => MemberTypes.Property; + + public abstract Type PropertyType { get; } + public abstract ParameterInfo[] GetIndexParameters(); + + public abstract PropertyAttributes Attributes { get; } + public bool IsSpecialName => (Attributes & PropertyAttributes.SpecialName) != 0; + + public abstract bool CanRead { get; } + public abstract bool CanWrite { get; } + + public MethodInfo[] GetAccessors() => GetAccessors(nonPublic: false); + public abstract MethodInfo[] GetAccessors(bool nonPublic); + + public virtual MethodInfo GetMethod => GetGetMethod(nonPublic: true); + public MethodInfo GetGetMethod() => GetGetMethod(nonPublic: false); + public abstract MethodInfo GetGetMethod(bool nonPublic); + + public virtual MethodInfo SetMethod => GetSetMethod(nonPublic: true); + public MethodInfo GetSetMethod() => GetSetMethod(nonPublic: false); + public abstract MethodInfo GetSetMethod(bool nonPublic); + + public virtual Type[] GetOptionalCustomModifiers() => Array.Empty(); + public virtual Type[] GetRequiredCustomModifiers() => Array.Empty(); + + [DebuggerHidden] + [DebuggerStepThrough] + public object GetValue(object obj) => GetValue(obj, index: null); + [DebuggerHidden] + [DebuggerStepThrough] + public virtual object GetValue(object obj, object[] index) => GetValue(obj, BindingFlags.Default, binder: null, index: index, culture: null); + public abstract object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture); + + public virtual object GetConstantValue() { throw NotImplemented.ByDesign; } + public virtual object GetRawConstantValue() { throw NotImplemented.ByDesign; } + + [DebuggerHidden] + [DebuggerStepThrough] + public void SetValue(object obj, object value) => SetValue(obj, value, index: null); + [DebuggerHidden] + [DebuggerStepThrough] + public virtual void SetValue(object obj, object value, object[] index) => SetValue(obj, value, BindingFlags.Default, binder: null, index: index, culture: null); + public abstract void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture); + + public override bool Equals(object obj) => base.Equals(obj); + public override int GetHashCode() => base.GetHashCode(); + + public static bool operator ==(PropertyInfo left, PropertyInfo right) + { + if (object.ReferenceEquals(left, right)) + return true; + + if ((object)left == null || (object)right == null) + return false; + + return left.Equals(right); + } + + public static bool operator !=(PropertyInfo left, PropertyInfo right) => !(left == right); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/ReflectionContext.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/ReflectionContext.cs new file mode 100644 index 0000000000..e9e93dab81 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/ReflectionContext.cs @@ -0,0 +1,24 @@ +// 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. + +namespace System.Reflection +{ + public abstract class ReflectionContext + { + protected ReflectionContext() { } + + public abstract Assembly MapAssembly(Assembly assembly); + + public abstract TypeInfo MapType(TypeInfo type); + + public virtual TypeInfo GetTypeForObject(object value) + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + return MapType(value.GetType().GetTypeInfo()); + } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/ReflectionTypeLoadException.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/ReflectionTypeLoadException.cs new file mode 100644 index 0000000000..5011c50053 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/ReflectionTypeLoadException.cs @@ -0,0 +1,73 @@ +// 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.Runtime.Serialization; +using System.Text; + +namespace System.Reflection +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class ReflectionTypeLoadException : SystemException, ISerializable + { + public ReflectionTypeLoadException(Type[] classes, Exception[] exceptions) + : base(null) + { + Types = classes; + LoaderExceptions = exceptions; + HResult = HResults.COR_E_REFLECTIONTYPELOAD; + } + + public ReflectionTypeLoadException(Type[] classes, Exception[] exceptions, string message) + : base(message) + { + Types = classes; + LoaderExceptions = exceptions; + HResult = HResults.COR_E_REFLECTIONTYPELOAD; + } + + private ReflectionTypeLoadException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + LoaderExceptions = (Exception[])(info.GetValue("Exceptions", typeof(Exception[]))); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("Types", null, typeof(Type[])); + info.AddValue("Exceptions", LoaderExceptions, typeof(Exception[])); + } + + public Type[] Types { get; } + + public Exception[] LoaderExceptions { get; } + + public override string Message => CreateString(isMessage: true); + + public override string ToString() => CreateString(isMessage: false); + + private string CreateString(bool isMessage) + { + string baseValue = isMessage ? base.Message : base.ToString(); + + Exception[] exceptions = LoaderExceptions; + if (exceptions == null || exceptions.Length == 0) + { + return baseValue; + } + + var text = new StringBuilder(baseValue); + foreach (Exception e in exceptions) + { + if (e != null) + { + text.AppendLine(); + text.Append(isMessage ? e.Message : e.ToString()); + } + } + return text.ToString(); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/ResourceAttributes.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/ResourceAttributes.cs new file mode 100644 index 0000000000..2d03f42ba0 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/ResourceAttributes.cs @@ -0,0 +1,14 @@ +// 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. + +namespace System.Reflection +{ + [Flags] + public enum ResourceAttributes + { + Public = 0x0001, + Private = 0x0002, + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/ResourceLocation.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/ResourceLocation.cs new file mode 100644 index 0000000000..4902333ac0 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/ResourceLocation.cs @@ -0,0 +1,15 @@ +// 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. + +namespace System.Reflection +{ + [Flags] + public enum ResourceLocation + { + ContainedInAnotherAssembly = 2, + ContainedInManifestFile = 4, + Embedded = 1, + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureArrayType.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureArrayType.cs new file mode 100644 index 0000000000..52011b8a9d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureArrayType.cs @@ -0,0 +1,46 @@ +// 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.Diagnostics; + +namespace System.Reflection +{ + internal sealed class SignatureArrayType : SignatureHasElementType + { + internal SignatureArrayType(SignatureType elementType, int rank, bool isMultiDim) + : base(elementType) + { + Debug.Assert(rank > 0); + Debug.Assert(rank == 1 || isMultiDim); + + _rank = rank; + _isMultiDim = isMultiDim; + } + + protected sealed override bool IsArrayImpl() => true; + protected sealed override bool IsByRefImpl() => false; + protected sealed override bool IsPointerImpl() => false; + + public sealed override bool IsSZArray => !_isMultiDim; + public sealed override bool IsVariableBoundArray => _isMultiDim; + + public sealed override int GetArrayRank() => _rank; + + protected sealed override string Suffix + { + get + { + if (!_isMultiDim) + return "[]"; + else if (_rank == 1) + return "[*]"; + else + return "[" + new string(',', _rank - 1) + "]"; + } + } + + private readonly int _rank; + private readonly bool _isMultiDim; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureByRefType.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureByRefType.cs new file mode 100644 index 0000000000..eb5f6de42e --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureByRefType.cs @@ -0,0 +1,27 @@ +// 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; + +namespace System.Reflection +{ + internal sealed class SignatureByRefType : SignatureHasElementType + { + internal SignatureByRefType(SignatureType elementType) + : base(elementType) + { + } + + protected sealed override bool IsArrayImpl() => false; + protected sealed override bool IsByRefImpl() => true; + protected sealed override bool IsPointerImpl() => false; + + public sealed override bool IsSZArray => false; + public sealed override bool IsVariableBoundArray => false; + + public sealed override int GetArrayRank() => throw new ArgumentException(SR.Argument_HasToBeArrayClass); + + protected sealed override string Suffix => "&"; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureConstructedGenericType.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureConstructedGenericType.cs new file mode 100644 index 0000000000..cd97ffa21b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureConstructedGenericType.cs @@ -0,0 +1,74 @@ +// 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.Text; +using System.Diagnostics; + +namespace System.Reflection +{ + internal sealed class SignatureConstructedGenericType : SignatureType + { + internal SignatureConstructedGenericType(Type genericTypeDefinition, Type[] genericTypeArguments) + { + Debug.Assert(genericTypeDefinition != null && genericTypeArguments != null); + _genericTypeDefinition = genericTypeDefinition; + _genericTypeArguments = (Type[])(genericTypeArguments.Clone()); + } + + public sealed override bool IsTypeDefinition => false; + public sealed override bool IsGenericTypeDefinition => false; + protected sealed override bool HasElementTypeImpl() => false; + protected sealed override bool IsArrayImpl() => false; + protected sealed override bool IsByRefImpl() => false; + public sealed override bool IsByRefLike => _genericTypeDefinition.IsByRefLike; + protected sealed override bool IsPointerImpl() => false; + public sealed override bool IsSZArray => false; + public sealed override bool IsVariableBoundArray => false; + public sealed override bool IsConstructedGenericType => true; + public sealed override bool IsGenericParameter => false; + public sealed override bool IsGenericTypeParameter => false; + public sealed override bool IsGenericMethodParameter => false; + public sealed override bool ContainsGenericParameters + { + get + { + for (int i = 0; i < _genericTypeArguments.Length; i++) + { + if (_genericTypeArguments[i].ContainsGenericParameters) + return true; + } + return false; + } + } + + internal sealed override SignatureType ElementType => null; + public sealed override int GetArrayRank() => throw new ArgumentException(SR.Argument_HasToBeArrayClass); + public sealed override Type GetGenericTypeDefinition() => _genericTypeDefinition; + public sealed override Type[] GetGenericArguments() => GenericTypeArguments; + public sealed override Type[] GenericTypeArguments => (Type[])(_genericTypeArguments.Clone()); + public sealed override int GenericParameterPosition => throw new InvalidOperationException(SR.Arg_NotGenericParameter); + + public sealed override string Name => _genericTypeDefinition.Name; + public sealed override string Namespace => _genericTypeDefinition.Namespace; + + public sealed override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append(_genericTypeDefinition.ToString()); + sb.Append('['); + for (int i = 0; i < _genericTypeArguments.Length; i++) + { + if (i != 0) + sb.Append(','); + sb.Append(_genericTypeArguments[i].ToString()); + } + sb.Append(']'); + return sb.ToString(); + } + + private readonly Type _genericTypeDefinition; + private readonly Type[] _genericTypeArguments; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureGenericMethodParameterType.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureGenericMethodParameterType.cs new file mode 100644 index 0000000000..d0790283fb --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureGenericMethodParameterType.cs @@ -0,0 +1,19 @@ +// 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. + +namespace System.Reflection +{ + internal sealed class SignatureGenericMethodParameterType : SignatureGenericParameterType + { + internal SignatureGenericMethodParameterType(int position) + : base(position) + { + } + + public sealed override bool IsGenericTypeParameter => false; + public sealed override bool IsGenericMethodParameter => true; + + public sealed override string Name => "!!" + GenericParameterPosition; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureGenericParameterType.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureGenericParameterType.cs new file mode 100644 index 0000000000..fee7bce353 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureGenericParameterType.cs @@ -0,0 +1,46 @@ +// 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.Diagnostics; + +namespace System.Reflection +{ + internal abstract class SignatureGenericParameterType : SignatureType + { + protected SignatureGenericParameterType(int position) + { + Debug.Assert(position >= 0); + _position = position; + } + + public sealed override bool IsTypeDefinition => false; + public sealed override bool IsGenericTypeDefinition => false; + protected sealed override bool HasElementTypeImpl() => false; + protected sealed override bool IsArrayImpl() => false; + protected sealed override bool IsByRefImpl() => false; + public sealed override bool IsByRefLike => false; + protected sealed override bool IsPointerImpl() => false; + public sealed override bool IsSZArray => false; + public sealed override bool IsVariableBoundArray => false; + public sealed override bool IsConstructedGenericType => false; + public sealed override bool IsGenericParameter => true; + public abstract override bool IsGenericMethodParameter { get; } + public sealed override bool ContainsGenericParameters => true; + + internal sealed override SignatureType ElementType => null; + public sealed override int GetArrayRank() => throw new ArgumentException(SR.Argument_HasToBeArrayClass); + public sealed override Type GetGenericTypeDefinition() => throw new InvalidOperationException(SR.InvalidOperation_NotGenericType); + public sealed override Type[] GetGenericArguments() => Array.Empty(); + public sealed override Type[] GenericTypeArguments => Array.Empty(); + public sealed override int GenericParameterPosition => _position; + + public abstract override string Name { get; } + public sealed override string Namespace => null; + + public sealed override string ToString() => Name; + + private readonly int _position; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureHasElementType.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureHasElementType.cs new file mode 100644 index 0000000000..e74e5f5aa2 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureHasElementType.cs @@ -0,0 +1,49 @@ +// 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.Diagnostics; + +namespace System.Reflection +{ + internal abstract class SignatureHasElementType : SignatureType + { + protected SignatureHasElementType(SignatureType elementType) + { + Debug.Assert(elementType != null); + _elementType = elementType; + } + + public sealed override bool IsTypeDefinition => false; + public sealed override bool IsGenericTypeDefinition => false; + protected sealed override bool HasElementTypeImpl() => true; + protected abstract override bool IsArrayImpl(); + protected abstract override bool IsByRefImpl(); + public sealed override bool IsByRefLike => false; + protected abstract override bool IsPointerImpl(); + public abstract override bool IsSZArray { get; } + public abstract override bool IsVariableBoundArray { get; } + public sealed override bool IsConstructedGenericType => false; + public sealed override bool IsGenericParameter => false; + public sealed override bool IsGenericTypeParameter => false; + public sealed override bool IsGenericMethodParameter => false; + public sealed override bool ContainsGenericParameters => _elementType.ContainsGenericParameters; + + internal sealed override SignatureType ElementType => _elementType; + public abstract override int GetArrayRank(); + public sealed override Type GetGenericTypeDefinition() => throw new InvalidOperationException(SR.InvalidOperation_NotGenericType); + public sealed override Type[] GetGenericArguments() => Array.Empty(); + public sealed override Type[] GenericTypeArguments => Array.Empty(); + public sealed override int GenericParameterPosition => throw new InvalidOperationException(SR.Arg_NotGenericParameter); + + public sealed override string Name => _elementType.Name + Suffix; + public sealed override string Namespace => _elementType.Namespace; + + public sealed override string ToString() => _elementType.ToString() + Suffix; + + protected abstract string Suffix { get; } + + private readonly SignatureType _elementType; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/SignaturePointerType.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/SignaturePointerType.cs new file mode 100644 index 0000000000..a75a208165 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/SignaturePointerType.cs @@ -0,0 +1,27 @@ +// 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; + +namespace System.Reflection +{ + internal sealed class SignaturePointerType : SignatureHasElementType + { + internal SignaturePointerType(SignatureType elementType) + : base(elementType) + { + } + + protected sealed override bool IsArrayImpl() => false; + protected sealed override bool IsByRefImpl() => false; + protected sealed override bool IsPointerImpl() => true; + + public sealed override bool IsSZArray => false; + public sealed override bool IsVariableBoundArray => false; + + public sealed override int GetArrayRank() => throw new ArgumentException(SR.Argument_HasToBeArrayClass); + + protected sealed override string Suffix => "*"; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureType.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureType.cs new file mode 100644 index 0000000000..40a0590448 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureType.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.Globalization; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace System.Reflection +{ + // + // Signature Types are highly restricted Type objects that can be passed in the Type[] parameter to Type.GetMethod(). Their primary + // use is to pass types representing generic parameters defined by the method, or types composed from them. Passing in the normal + // generic parameter Type obtained from MethodInfo.GetGenericArguments() is usually impractical (if you had the method, you wouldn't + // be looking for it!) + // + internal abstract class SignatureType : Type + { + public sealed override bool IsSignatureType => true; + + // Type flavor predicates + public abstract override bool IsTypeDefinition { get; } + protected abstract override bool HasElementTypeImpl(); + protected abstract override bool IsArrayImpl(); + public abstract override bool IsSZArray { get; } + public abstract override bool IsVariableBoundArray { get; } + protected abstract override bool IsByRefImpl(); + public abstract override bool IsByRefLike { get; } + protected abstract override bool IsPointerImpl(); + public sealed override bool IsGenericType => IsGenericTypeDefinition || IsConstructedGenericType; + public abstract override bool IsGenericTypeDefinition { get; } + public abstract override bool IsConstructedGenericType { get; } + public abstract override bool IsGenericParameter { get; } + public abstract override bool IsGenericTypeParameter { get; } + public abstract override bool IsGenericMethodParameter { get; } + public abstract override bool ContainsGenericParameters { get; } + public sealed override MemberTypes MemberType => MemberTypes.TypeInfo; + + // Compositors + public sealed override Type MakeArrayType() => new SignatureArrayType(this, rank: 1, isMultiDim: false); + public sealed override Type MakeArrayType(int rank) + { + if (rank <= 0) + throw new IndexOutOfRangeException(); + return new SignatureArrayType(this, rank: rank, isMultiDim: true); + } + public sealed override Type MakeByRefType() => new SignatureByRefType(this); + public sealed override Type MakePointerType() => new SignaturePointerType(this); + public sealed override Type MakeGenericType(params Type[] typeArguments) => throw new NotSupportedException(SR.NotSupported_SignatureType); // There is no SignatureType for type definition types so it would never be legal to call this. + + // Dissectors + public sealed override Type GetElementType() => ElementType; + public abstract override int GetArrayRank(); + public abstract override Type GetGenericTypeDefinition(); + public abstract override Type[] GenericTypeArguments { get; } + public abstract override Type[] GetGenericArguments(); + public abstract override int GenericParameterPosition { get; } + internal abstract SignatureType ElementType { get; } + + // Identity +#if DEBUG + public sealed override bool Equals(object o) => base.Equals(o); + public sealed override bool Equals(Type o) => base.Equals(o); + public sealed override int GetHashCode() => base.GetHashCode(); +#endif + public sealed override Type UnderlyingSystemType => this; // Equals(Type) depends on this. + + // Naming and diagnostics + public abstract override string Name { get; } + public abstract override string Namespace { get; } + public sealed override string FullName => null; + public sealed override string AssemblyQualifiedName => null; + public abstract override string ToString(); + + // Not supported on Signature Types + public sealed override Assembly Assembly => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Module Module => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type ReflectedType => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type BaseType => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type[] GetInterfaces() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsAssignableFrom(Type c) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override int MetadataToken => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type DeclaringType => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override MethodBase DeclaringMethod => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type[] GetGenericParameterConstraints() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override GenericParameterAttributes GenericParameterAttributes => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsEnumDefined(object value) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override string GetEnumName(object value) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override string[] GetEnumNames() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type GetEnumUnderlyingType() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Array GetEnumValues() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Guid GUID => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override TypeCode GetTypeCodeImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override TypeAttributes GetAttributeFlagsImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override EventInfo GetEvent(string name, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override EventInfo[] GetEvents(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override FieldInfo GetField(string name, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override FieldInfo[] GetFields(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override MemberInfo[] GetMembers(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override MethodInfo[] GetMethods(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type GetNestedType(string name, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type[] GetNestedTypes(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override PropertyInfo[] GetProperties(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override MethodInfo GetMethodImpl(string name, int genericParameterCount, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override MemberInfo[] FindMembers(MemberTypes memberType, BindingFlags bindingAttr, MemberFilter filter, object filterCriteria) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override MemberInfo[] GetMember(string name, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override MemberInfo[] GetMember(string name, MemberTypes type, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override MemberInfo[] GetDefaultMembers() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override EventInfo[] GetEvents() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override object[] GetCustomAttributes(bool inherit) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override object[] GetCustomAttributes(Type attributeType, bool inherit) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsDefined(Type attributeType, bool inherit) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override IList GetCustomAttributesData() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type GetInterface(string name, bool ignoreCase) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override bool IsCOMObjectImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override bool IsPrimitiveImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override IEnumerable CustomAttributes => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type[] FindInterfaces(TypeFilter filter, object filterCriteria) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override InterfaceMapping GetInterfaceMap(Type interfaceType) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override bool IsContextfulImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsEnum => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsEquivalentTo(Type other) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsInstanceOfType(object o) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override bool IsMarshalByRefImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsSecurityCritical => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsSecuritySafeCritical => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsSecurityTransparent => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsSerializable => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsSubclassOf(Type c) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override bool IsValueTypeImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override StructLayoutAttribute StructLayoutAttribute => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override RuntimeTypeHandle TypeHandle => throw new NotSupportedException(SR.NotSupported_SignatureType); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureTypeExtensions.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureTypeExtensions.cs new file mode 100644 index 0000000000..5847944f14 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/SignatureTypeExtensions.cs @@ -0,0 +1,227 @@ +// 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.Reflection; +using System.Diagnostics; + +namespace System.Reflection +{ +#if CORERT + public // Needs to be public so that Reflection.Core can see it. +#else + internal +#endif + static class SignatureTypeExtensions + { + /// + /// This is semantically identical to + /// + /// parameter.ParameterType == pattern.TryResolveAgainstGenericMethod(parameter.Member) + /// + /// but without the allocation overhead of TryResolve. + /// + public static bool MatchesParameterTypeExactly(this Type pattern, ParameterInfo parameter) + { + if (pattern is SignatureType signatureType) + return signatureType.MatchesExactly(parameter.ParameterType); + else + return pattern == (object)(parameter.ParameterType); + } + + /// + /// This is semantically identical to + /// + /// actual == pattern.TryResolveAgainstGenericMethod(parameterMember) + /// + /// but without the allocation overhead of TryResolve. + /// + internal static bool MatchesExactly(this SignatureType pattern, Type actual) + { + if (pattern.IsSZArray) + { + return actual.IsSZArray && pattern.ElementType.MatchesExactly(actual.GetElementType()); + } + else if (pattern.IsVariableBoundArray) + { + return actual.IsVariableBoundArray && pattern.GetArrayRank() == actual.GetArrayRank() && pattern.ElementType.MatchesExactly(actual.GetElementType()); + } + else if (pattern.IsByRef) + { + return actual.IsByRef && pattern.ElementType.MatchesExactly(actual.GetElementType()); + } + else if (pattern.IsPointer) + { + return actual.IsPointer && pattern.ElementType.MatchesExactly(actual.GetElementType()); + } + else if (pattern.IsConstructedGenericType) + { + if (!actual.IsConstructedGenericType) + return false; + if (!(pattern.GetGenericTypeDefinition() == actual.GetGenericTypeDefinition())) + return false; + Type[] patternGenericTypeArguments = pattern.GenericTypeArguments; + Type[] actualGenericTypeArguments = actual.GenericTypeArguments; + int count = patternGenericTypeArguments.Length; + if (count != actualGenericTypeArguments.Length) + return false; + for (int i = 0; i < count; i++) + { + Type patternGenericTypeArgument = patternGenericTypeArguments[i]; + if (patternGenericTypeArgument is SignatureType signatureType) + { + if (!signatureType.MatchesExactly(actualGenericTypeArguments[i])) + return false; + } + else + { + if (patternGenericTypeArgument != actualGenericTypeArguments[i]) + return false; + } + } + return true; + } + else if (pattern.IsGenericMethodParameter) + { + if (!actual.IsGenericMethodParameter) + return false; + if (pattern.GenericParameterPosition != actual.GenericParameterPosition) + return false; + return true; + } + else + { + return false; + } + } + + /// + /// Translates a SignatureType into its equivalent resolved Type by recursively substituting all generic parameter references + /// with its corresponding generic parameter definition. This is slow so MatchesExactly or MatchesParameterTypeExactly should be + /// substituted instead whenever possible. This is only used by the DefaultBinder when its fast-path checks have been exhausted and + /// it needs to call non-trivial methods like IsAssignableFrom which SignatureTypes will never support. + /// + /// Because this method is used to eliminate method candidates in a GetMethod() lookup, it is entirely possible that the Type + /// might not be creatable due to conflicting generic constraints. Since this merely implies that this candidate is not + /// the method we're looking for, we return null rather than let the TypeLoadException bubble up. The DefaultBinder will catch + /// the null and continue its search for a better candidate. + /// + internal static Type TryResolveAgainstGenericMethod(this SignatureType signatureType, MethodInfo genericMethod) + { + return signatureType.TryResolve(genericMethod.GetGenericArguments()); + } + + private static Type TryResolve(this SignatureType signatureType, Type[] genericMethodParameters) + { + if (signatureType.IsSZArray) + { + return signatureType.ElementType.TryResolve(genericMethodParameters)?.TryMakeArrayType(); + } + else if (signatureType.IsVariableBoundArray) + { + return signatureType.ElementType.TryResolve(genericMethodParameters)?.TryMakeArrayType(signatureType.GetArrayRank()); + } + else if (signatureType.IsByRef) + { + return signatureType.ElementType.TryResolve(genericMethodParameters)?.TryMakeByRefType(); + } + else if (signatureType.IsPointer) + { + return signatureType.ElementType.TryResolve(genericMethodParameters)?.TryMakePointerType(); + } + else if (signatureType.IsConstructedGenericType) + { + Type[] genericTypeArguments = signatureType.GenericTypeArguments; + int count = genericTypeArguments.Length; + Type[] newGenericTypeArguments = new Type[count]; + for (int i = 0; i < count; i++) + { + Type genericTypeArgument = genericTypeArguments[i]; + if (genericTypeArgument is SignatureType signatureGenericTypeArgument) + { + newGenericTypeArguments[i] = signatureGenericTypeArgument.TryResolve(genericMethodParameters); + if (newGenericTypeArguments[i] == null) + return null; + } + else + { + newGenericTypeArguments[i] = genericTypeArgument; + } + } + return signatureType.GetGenericTypeDefinition().TryMakeGenericType(newGenericTypeArguments); + } + else if (signatureType.IsGenericMethodParameter) + { + int position = signatureType.GenericParameterPosition; + if (position >= genericMethodParameters.Length) + return null; + return genericMethodParameters[position]; + } + else + { + return null; + } + } + + private static Type TryMakeArrayType(this Type type) + { + try + { + return type.MakeArrayType(); + } + catch + { + return null; + } + } + + private static Type TryMakeArrayType(this Type type, int rank) + { + try + { + return type.MakeArrayType(rank); + } + catch + { + return null; + } + } + + private static Type TryMakeByRefType(this Type type) + { + try + { + return type.MakeByRefType(); + } + catch + { + return null; + } + } + + private static Type TryMakePointerType(this Type type) + { + try + { + return type.MakePointerType(); + } + catch + { + return null; + } + } + + private static Type TryMakeGenericType(this Type type, Type[] instantiation) + { + try + { + return type.MakeGenericType(instantiation); + } + catch + { + return null; + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/StrongNameKeyPair.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/StrongNameKeyPair.cs new file mode 100644 index 0000000000..a0ba97f835 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/StrongNameKeyPair.cs @@ -0,0 +1,58 @@ +// 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.IO; +using System.Runtime.Serialization; + +namespace System.Reflection +{ + public class StrongNameKeyPair : IDeserializationCallback, ISerializable + { + // Build key pair from file. + public StrongNameKeyPair(FileStream keyPairFile) + { + if (keyPairFile == null) + throw new ArgumentNullException(nameof(keyPairFile)); + + int length = (int)keyPairFile.Length; + byte[] keyPairArray = new byte[length]; + keyPairFile.Read(keyPairArray, 0, length); + } + + // Build key pair from byte array in memory. + public StrongNameKeyPair(byte[] keyPairArray) + { + if (keyPairArray == null) + throw new ArgumentNullException(nameof(keyPairArray)); + } + + protected StrongNameKeyPair(SerializationInfo info, StreamingContext context) + { + throw new PlatformNotSupportedException(); + } + + public StrongNameKeyPair(string keyPairContainer) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_StrongNameSigning); + } + + public byte[] PublicKey + { + get + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_StrongNameSigning); + } + } + + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + throw new PlatformNotSupportedException(); + } + + void IDeserializationCallback.OnDeserialization(object sender) + { + throw new PlatformNotSupportedException(); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/TargetException.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/TargetException.cs new file mode 100644 index 0000000000..c200000738 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/TargetException.cs @@ -0,0 +1,34 @@ +// 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.Runtime.Serialization; + +namespace System.Reflection +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class TargetException : ApplicationException + { + public TargetException() + : this(null) + { + } + + public TargetException(string message) + : this(message, null) + { + } + + public TargetException(string message, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_TARGET; + } + + protected TargetException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/TargetInvocationException.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/TargetInvocationException.cs new file mode 100644 index 0000000000..822ddfdfb2 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/TargetInvocationException.cs @@ -0,0 +1,30 @@ +// 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.Runtime.Serialization; + +namespace System.Reflection +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class TargetInvocationException : ApplicationException + { + public TargetInvocationException(Exception inner) + : base(SR.Arg_TargetInvocationException, inner) + { + HResult = HResults.COR_E_TARGETINVOCATION; + } + + public TargetInvocationException(string message, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_TARGETINVOCATION; + } + + internal TargetInvocationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/TargetParameterCountException.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/TargetParameterCountException.cs new file mode 100644 index 0000000000..c68c467290 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/TargetParameterCountException.cs @@ -0,0 +1,36 @@ +// 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.Runtime.Serialization; + +namespace System.Reflection +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class TargetParameterCountException : ApplicationException + { + public TargetParameterCountException() + : base(SR.Arg_TargetParameterCountException) + { + HResult = HResults.COR_E_TARGETPARAMCOUNT; + } + + public TargetParameterCountException(string message) + : base(message) + { + HResult = HResults.COR_E_TARGETPARAMCOUNT; + } + + public TargetParameterCountException(string message, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_TARGETPARAMCOUNT; + } + + internal TargetParameterCountException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/TypeAttributes.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/TypeAttributes.cs new file mode 100644 index 0000000000..aa30331856 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/TypeAttributes.cs @@ -0,0 +1,63 @@ +// 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.Runtime.InteropServices; + +namespace System.Reflection +{ + // This Enum matchs the CorTypeAttr defined in CorHdr.h + [Flags] + public enum TypeAttributes + { + VisibilityMask = 0x00000007, + NotPublic = 0x00000000, // Class is not public scope. + Public = 0x00000001, // Class is public scope. + NestedPublic = 0x00000002, // Class is nested with public visibility. + NestedPrivate = 0x00000003, // Class is nested with private visibility. + NestedFamily = 0x00000004, // Class is nested with family visibility. + NestedAssembly = 0x00000005, // Class is nested with assembly visibility. + NestedFamANDAssem = 0x00000006, // Class is nested with family and assembly visibility. + NestedFamORAssem = 0x00000007, // Class is nested with family or assembly visibility. + + // Use this mask to retrieve class layout informaiton + // 0 is AutoLayout, 0x2 is SequentialLayout, 4 is ExplicitLayout + LayoutMask = 0x00000018, + AutoLayout = 0x00000000, // Class fields are auto-laid out + SequentialLayout = 0x00000008, // Class fields are laid out sequentially + ExplicitLayout = 0x00000010, // Layout is supplied explicitly + // end layout mask + + // Use this mask to distinguish whether a type declaration is an interface. (Class vs. ValueType done based on whether it subclasses S.ValueType) + ClassSemanticsMask = 0x00000020, + Class = 0x00000000, // Type is a class (or a value type). + Interface = 0x00000020, // Type is an interface. + + // Special semantics in addition to class semantics. + Abstract = 0x00000080, // Class is abstract + Sealed = 0x00000100, // Class is concrete and may not be extended + SpecialName = 0x00000400, // Class name is special. Name describes how. + + // Implementation attributes. + Import = 0x00001000, // Class / interface is imported + Serializable = 0x00002000, // The class is Serializable. + WindowsRuntime = 0x00004000, // Type is a Windows Runtime type. + + // Use tdStringFormatMask to retrieve string information for native interop + StringFormatMask = 0x00030000, + AnsiClass = 0x00000000, // LPTSTR is interpreted as ANSI in this class + UnicodeClass = 0x00010000, // LPTSTR is interpreted as UNICODE + AutoClass = 0x00020000, // LPTSTR is interpreted automatically + CustomFormatClass = 0x00030000, // A non-standard encoding specified by CustomFormatMask + CustomFormatMask = 0x00C00000, // Use this mask to retrieve non-standard encoding information for native interop. The meaning of the values of these 2 bits is unspecified. + + // end string format mask + + BeforeFieldInit = 0x00100000, // Initialize the class any time before first static field access. + + RTSpecialName = 0x00000800, // Runtime should check name encoding. + HasSecurity = 0x00040000, // Class has security associate with it. + + ReservedMask = 0x00040800, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/TypeDelegator.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/TypeDelegator.cs new file mode 100644 index 0000000000..e0be6e87b6 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/TypeDelegator.cs @@ -0,0 +1,130 @@ +// 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. + +// TypeDelegator +// +// This class wraps a Type object and delegates all methods to that Type. + +using CultureInfo = System.Globalization.CultureInfo; + +namespace System.Reflection +{ + public class TypeDelegator : TypeInfo + { + public override bool IsAssignableFrom(TypeInfo typeInfo) + { + if (typeInfo == null) + return false; + return IsAssignableFrom(typeInfo.AsType()); + } + + protected Type typeImpl; + + protected TypeDelegator() { } + + public TypeDelegator(Type delegatingType) + { + if (delegatingType == null) + throw new ArgumentNullException(nameof(delegatingType)); + + typeImpl = delegatingType; + } + + public override Guid GUID => typeImpl.GUID; + public override int MetadataToken => typeImpl.MetadataToken; + + public override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, + object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) + { + return typeImpl.InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters); + } + + public override Module Module => typeImpl.Module; + public override Assembly Assembly => typeImpl.Assembly; + public override RuntimeTypeHandle TypeHandle => typeImpl.TypeHandle; + public override string Name => typeImpl.Name; + public override string FullName => typeImpl.FullName; + public override string Namespace => typeImpl.Namespace; + public override string AssemblyQualifiedName => typeImpl.AssemblyQualifiedName; + public override Type BaseType => typeImpl.BaseType; + + protected override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, + CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + return typeImpl.GetConstructor(bindingAttr, binder, callConvention, types, modifiers); + } + + public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) => typeImpl.GetConstructors(bindingAttr); + + protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, + CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + // This is interesting there are two paths into the impl. One that validates + // type as non-null and one where type may be null. + if (types == null) + return typeImpl.GetMethod(name, bindingAttr); + else + return typeImpl.GetMethod(name, bindingAttr, binder, callConvention, types, modifiers); + } + + public override MethodInfo[] GetMethods(BindingFlags bindingAttr) => typeImpl.GetMethods(bindingAttr); + + public override FieldInfo GetField(string name, BindingFlags bindingAttr) => typeImpl.GetField(name, bindingAttr); + public override FieldInfo[] GetFields(BindingFlags bindingAttr) => typeImpl.GetFields(bindingAttr); + + public override Type GetInterface(string name, bool ignoreCase) => typeImpl.GetInterface(name, ignoreCase); + + public override Type[] GetInterfaces() => typeImpl.GetInterfaces(); + + public override EventInfo GetEvent(string name, BindingFlags bindingAttr) => typeImpl.GetEvent(name, bindingAttr); + + public override EventInfo[] GetEvents() => typeImpl.GetEvents(); + + protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, + Type returnType, Type[] types, ParameterModifier[] modifiers) + { + if (returnType == null && types == null) + return typeImpl.GetProperty(name, bindingAttr); + else + return typeImpl.GetProperty(name, bindingAttr, binder, returnType, types, modifiers); + } + + public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) => typeImpl.GetProperties(bindingAttr); + public override EventInfo[] GetEvents(BindingFlags bindingAttr) => typeImpl.GetEvents(bindingAttr); + public override Type[] GetNestedTypes(BindingFlags bindingAttr) => typeImpl.GetNestedTypes(bindingAttr); + public override Type GetNestedType(string name, BindingFlags bindingAttr) => typeImpl.GetNestedType(name, bindingAttr); + public override MemberInfo[] GetMember(string name, MemberTypes type, BindingFlags bindingAttr) => typeImpl.GetMember(name, type, bindingAttr); + public override MemberInfo[] GetMembers(BindingFlags bindingAttr) => typeImpl.GetMembers(bindingAttr); + + protected override TypeAttributes GetAttributeFlagsImpl() => typeImpl.Attributes; + + public override bool IsTypeDefinition => typeImpl.IsTypeDefinition; + public override bool IsSZArray => typeImpl.IsSZArray; + + protected override bool IsArrayImpl() => typeImpl.IsArray; + protected override bool IsPrimitiveImpl() => typeImpl.IsPrimitive; + protected override bool IsByRefImpl() => typeImpl.IsByRef; + public override bool IsGenericTypeParameter => typeImpl.IsGenericTypeParameter; + public override bool IsGenericMethodParameter => typeImpl.IsGenericMethodParameter; + protected override bool IsPointerImpl() => typeImpl.IsPointer; + protected override bool IsValueTypeImpl() => typeImpl.IsValueType; + protected override bool IsCOMObjectImpl() => typeImpl.IsCOMObject; + public override bool IsByRefLike => typeImpl.IsByRefLike; + public override bool IsConstructedGenericType => typeImpl.IsConstructedGenericType; + + public override bool IsCollectible => typeImpl.IsCollectible; + + public override Type GetElementType() => typeImpl.GetElementType(); + protected override bool HasElementTypeImpl() => typeImpl.HasElementType; + + public override Type UnderlyingSystemType => typeImpl.UnderlyingSystemType; + + // ICustomAttributeProvider + public override object[] GetCustomAttributes(bool inherit) => typeImpl.GetCustomAttributes(inherit); + public override object[] GetCustomAttributes(Type attributeType, bool inherit) => typeImpl.GetCustomAttributes(attributeType, inherit); + + public override bool IsDefined(Type attributeType, bool inherit) => typeImpl.IsDefined(attributeType, inherit); + public override InterfaceMapping GetInterfaceMap(Type interfaceType) => typeImpl.GetInterfaceMap(interfaceType); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/TypeFilter.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/TypeFilter.cs new file mode 100644 index 0000000000..eb049f81f9 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/TypeFilter.cs @@ -0,0 +1,8 @@ +// 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. + +namespace System.Reflection +{ + public delegate bool TypeFilter(Type m, object filterCriteria); +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Reflection/TypeInfo.cs b/external/corefx/src/Common/src/CoreLib/System/Reflection/TypeInfo.cs new file mode 100644 index 0000000000..f4add736f4 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Reflection/TypeInfo.cs @@ -0,0 +1,84 @@ +// 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.Collections.Generic; + +namespace System.Reflection +{ + public abstract partial class TypeInfo : Type, IReflectableType + { + protected TypeInfo() { } + + TypeInfo IReflectableType.GetTypeInfo() => this; + public virtual Type AsType() => this; + + public virtual Type[] GenericTypeParameters => IsGenericTypeDefinition ? GetGenericArguments() : Type.EmptyTypes; + + public virtual EventInfo GetDeclaredEvent(string name) => GetEvent(name, TypeInfo.DeclaredOnlyLookup); + public virtual FieldInfo GetDeclaredField(string name) => GetField(name, TypeInfo.DeclaredOnlyLookup); + public virtual MethodInfo GetDeclaredMethod(string name) => GetMethod(name, TypeInfo.DeclaredOnlyLookup); + public virtual TypeInfo GetDeclaredNestedType(string name) => GetNestedType(name, TypeInfo.DeclaredOnlyLookup)?.GetTypeInfo(); + public virtual PropertyInfo GetDeclaredProperty(string name) => GetProperty(name, TypeInfo.DeclaredOnlyLookup); + + public virtual IEnumerable GetDeclaredMethods(string name) + { + foreach (MethodInfo method in GetMethods(TypeInfo.DeclaredOnlyLookup)) + { + if (method.Name == name) + yield return method; + } + } + + public virtual IEnumerable DeclaredConstructors => GetConstructors(TypeInfo.DeclaredOnlyLookup); + public virtual IEnumerable DeclaredEvents => GetEvents(TypeInfo.DeclaredOnlyLookup); + public virtual IEnumerable DeclaredFields => GetFields(TypeInfo.DeclaredOnlyLookup); + public virtual IEnumerable DeclaredMembers => GetMembers(TypeInfo.DeclaredOnlyLookup); + public virtual IEnumerable DeclaredMethods => GetMethods(TypeInfo.DeclaredOnlyLookup); + public virtual IEnumerable DeclaredNestedTypes + { + get + { + foreach (Type t in GetNestedTypes(TypeInfo.DeclaredOnlyLookup)) + { + yield return t.GetTypeInfo(); + } + } + } + public virtual IEnumerable DeclaredProperties => GetProperties(TypeInfo.DeclaredOnlyLookup); + + public virtual IEnumerable ImplementedInterfaces => GetInterfaces(); + + //a re-implementation of ISAF from Type, skipping the use of UnderlyingType + public virtual bool IsAssignableFrom(TypeInfo typeInfo) + { + if (typeInfo == null) + return false; + + if (this == typeInfo) + return true; + + // If c is a subclass of this class, then c can be cast to this type. + if (typeInfo.IsSubclassOf(this)) + return true; + + if (this.IsInterface) + { + return typeInfo.ImplementInterface(this); + } + else if (IsGenericParameter) + { + Type[] constraints = GetGenericParameterConstraints(); + for (int i = 0; i < constraints.Length; i++) + if (!constraints[i].IsAssignableFrom(typeInfo)) + return false; + + return true; + } + + return false; + } + + private const BindingFlags DeclaredOnlyLookup = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/ResolveEventArgs.cs b/external/corefx/src/Common/src/CoreLib/System/ResolveEventArgs.cs new file mode 100644 index 0000000000..6196947bb5 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ResolveEventArgs.cs @@ -0,0 +1,25 @@ +// 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.Reflection; + +namespace System +{ + public class ResolveEventArgs : EventArgs + { + public ResolveEventArgs(string name) + { + Name = name; + } + + public ResolveEventArgs(string name, Assembly requestingAssembly) + { + Name = name; + RequestingAssembly = requestingAssembly; + } + + public string Name { get; } + public Assembly RequestingAssembly { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/ResolveEventHandler.cs b/external/corefx/src/Common/src/CoreLib/System/ResolveEventHandler.cs new file mode 100644 index 0000000000..cb9af5de66 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ResolveEventHandler.cs @@ -0,0 +1,10 @@ +// 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.Reflection; + +namespace System +{ + public delegate Assembly ResolveEventHandler(object sender, ResolveEventArgs args); +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Resources/IResourceReader.cs b/external/corefx/src/Common/src/CoreLib/System/Resources/IResourceReader.cs new file mode 100644 index 0000000000..543a5a67de --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Resources/IResourceReader.cs @@ -0,0 +1,30 @@ +// 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. + +/*============================================================ +** +** +** +** +** +** Purpose: Abstraction to read streams of resources. +** +** +===========================================================*/ + +using System; +using System.Collections; + +namespace System.Resources +{ + public interface IResourceReader : IEnumerable, IDisposable + { + // Interface does not need to be marked with the serializable attribute + // Closes the ResourceReader, releasing any resources associated with it. + // This could close a network connection, a file, or do nothing. + void Close(); + + new IDictionaryEnumerator GetEnumerator(); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Resources/MissingManifestResourceException.cs b/external/corefx/src/Common/src/CoreLib/System/Resources/MissingManifestResourceException.cs new file mode 100644 index 0000000000..20914ac7e1 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Resources/MissingManifestResourceException.cs @@ -0,0 +1,37 @@ +// 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.Runtime.Serialization; + +namespace System.Resources +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class MissingManifestResourceException : SystemException + { + public MissingManifestResourceException() + : base(SR.Arg_MissingManifestResourceException) + { + HResult = System.HResults.COR_E_MISSINGMANIFESTRESOURCE; + } + + public MissingManifestResourceException(string message) + : base(message) + { + HResult = System.HResults.COR_E_MISSINGMANIFESTRESOURCE; + } + + public MissingManifestResourceException(string message, Exception inner) + : base(message, inner) + { + HResult = System.HResults.COR_E_MISSINGMANIFESTRESOURCE; + } + + protected MissingManifestResourceException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Resources/MissingSatelliteAssemblyException.cs b/external/corefx/src/Common/src/CoreLib/System/Resources/MissingSatelliteAssemblyException.cs new file mode 100644 index 0000000000..af547b21f1 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Resources/MissingSatelliteAssemblyException.cs @@ -0,0 +1,64 @@ +// 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. + +/*============================================================ +** +** +** +** +** +** Purpose: Exception for a missing satellite assembly needed +** for ultimate resource fallback. This usually +** indicates a setup and/or deployment problem. +** +** +===========================================================*/ + +using System; +using System.Runtime.Serialization; + +namespace System.Resources +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class MissingSatelliteAssemblyException : SystemException + { + private String _cultureName; + + public MissingSatelliteAssemblyException() + : base(SR.MissingSatelliteAssembly_Default) + { + HResult = System.HResults.COR_E_MISSINGSATELLITEASSEMBLY; + } + + public MissingSatelliteAssemblyException(string message) + : base(message) + { + HResult = System.HResults.COR_E_MISSINGSATELLITEASSEMBLY; + } + + public MissingSatelliteAssemblyException(string message, String cultureName) + : base(message) + { + HResult = System.HResults.COR_E_MISSINGSATELLITEASSEMBLY; + _cultureName = cultureName; + } + + public MissingSatelliteAssemblyException(string message, Exception inner) + : base(message, inner) + { + HResult = System.HResults.COR_E_MISSINGSATELLITEASSEMBLY; + } + + protected MissingSatelliteAssemblyException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + public String CultureName + { + get { return _cultureName; } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Resources/NeutralResourcesLanguageAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Resources/NeutralResourcesLanguageAttribute.cs new file mode 100644 index 0000000000..495c5205b2 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Resources/NeutralResourcesLanguageAttribute.cs @@ -0,0 +1,33 @@ +// 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. + +namespace System.Resources +{ + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] + public sealed class NeutralResourcesLanguageAttribute : Attribute + { + public NeutralResourcesLanguageAttribute(string cultureName) + { + if (cultureName == null) + throw new ArgumentNullException(nameof(cultureName)); + + CultureName = cultureName; + Location = UltimateResourceFallbackLocation.MainAssembly; + } + + public NeutralResourcesLanguageAttribute(string cultureName, UltimateResourceFallbackLocation location) + { + if (cultureName == null) + throw new ArgumentNullException(nameof(cultureName)); + if (!Enum.IsDefined(typeof(UltimateResourceFallbackLocation), location)) + throw new ArgumentException(SR.Format(SR.Arg_InvalidNeutralResourcesLanguage_FallbackLoc, location)); + + CultureName = cultureName; + Location = location; + } + + public string CultureName { get; } + public UltimateResourceFallbackLocation Location { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Resources/ResourceFallbackManager.cs b/external/corefx/src/Common/src/CoreLib/System/Resources/ResourceFallbackManager.cs new file mode 100644 index 0000000000..8268f3208d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Resources/ResourceFallbackManager.cs @@ -0,0 +1,87 @@ +// 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. + +/*============================================================ +** +** +** +** +** +** Purpose: Encapsulates CultureInfo fallback for resource +** lookup +** +** +===========================================================*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System.Resources +{ + internal class ResourceFallbackManager : IEnumerable + { + private CultureInfo m_startingCulture; + private CultureInfo m_neutralResourcesCulture; + private bool m_useParents; + + internal ResourceFallbackManager(CultureInfo startingCulture, CultureInfo neutralResourcesCulture, bool useParents) + { + if (startingCulture != null) + { + m_startingCulture = startingCulture; + } + else + { + m_startingCulture = CultureInfo.CurrentUICulture; + } + + m_neutralResourcesCulture = neutralResourcesCulture; + m_useParents = useParents; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + // WARING: This function must be kept in sync with ResourceManager.GetFirstResourceSet() + public IEnumerator GetEnumerator() + { + bool reachedNeutralResourcesCulture = false; + + // 1. starting culture chain, up to neutral + CultureInfo currentCulture = m_startingCulture; + do + { + if (m_neutralResourcesCulture != null && currentCulture.Name == m_neutralResourcesCulture.Name) + { + // Return the invariant culture all the time, even if the UltimateResourceFallbackLocation + // is a satellite assembly. This is fixed up later in ManifestBasedResourceGroveler::UltimateFallbackFixup. + yield return CultureInfo.InvariantCulture; + reachedNeutralResourcesCulture = true; + break; + } + yield return currentCulture; + currentCulture = currentCulture.Parent; + } while (m_useParents && !currentCulture.HasInvariantCultureName); + + if (!m_useParents || m_startingCulture.HasInvariantCultureName) + { + yield break; + } + + // 2. invariant + // Don't return invariant twice though. + if (reachedNeutralResourcesCulture) + yield break; + + yield return CultureInfo.InvariantCulture; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Resources/ResourceTypeCode.cs b/external/corefx/src/Common/src/CoreLib/System/Resources/ResourceTypeCode.cs new file mode 100644 index 0000000000..b0ceb61df4 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Resources/ResourceTypeCode.cs @@ -0,0 +1,58 @@ +// 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. + +/*============================================================ +** +** +** +** +** +** Purpose: Marker for types in .resources files +** +** +===========================================================*/ + +namespace System.Resources +{ + /* An internal implementation detail for .resources files, describing + what type an object is. + Ranges: + 0 - 0x1F Primitives and reserved values + 0x20 - 0x3F Specially recognized types, like byte[] and Streams + + Note this data must be included in any documentation describing the + internals of .resources files. + */ + internal enum ResourceTypeCode + { + // Primitives + Null = 0, + String = 1, + Boolean = 2, + Char = 3, + Byte = 4, + SByte = 5, + Int16 = 6, + UInt16 = 7, + Int32 = 8, + UInt32 = 9, + Int64 = 0xa, + UInt64 = 0xb, + Single = 0xc, + Double = 0xd, + Decimal = 0xe, + DateTime = 0xf, + TimeSpan = 0x10, + + // A meta-value - change this if you add new primitives + LastPrimitive = TimeSpan, + + // Types with a special representation, like byte[] and Stream + ByteArray = 0x20, + Stream = 0x21, + + // User types - serialized using the binary formatter. + StartOfUserTypes = 0x40 + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Resources/RuntimeResourceSet.cs b/external/corefx/src/Common/src/CoreLib/System/Resources/RuntimeResourceSet.cs new file mode 100644 index 0000000000..a63e68c19d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Resources/RuntimeResourceSet.cs @@ -0,0 +1,434 @@ +// 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. + +/*============================================================ +** +** +** +** +** +** Purpose: CultureInfo-specific collection of resources. +** +** +===========================================================*/ + +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using System.Runtime.Versioning; +using System.Diagnostics; + +namespace System.Resources +{ + // A RuntimeResourceSet stores all the resources defined in one + // particular CultureInfo, with some loading optimizations. + // + // It is expected that nearly all the runtime's users will be satisfied with the + // default resource file format, and it will be more efficient than most simple + // implementations. Users who would consider creating their own ResourceSets and/or + // ResourceReaders and ResourceWriters are people who have to interop with a + // legacy resource file format, are creating their own resource file format + // (using XML, for instance), or require doing resource lookups at runtime over + // the network. This group will hopefully be small, but all the infrastructure + // should be in place to let these users write & plug in their own tools. + // + // The Default Resource File Format + // + // The fundamental problems addressed by the resource file format are: + // + // * Versioning - A ResourceReader could in theory support many different + // file format revisions. + // * Storing intrinsic datatypes (ie, ints, Strings, DateTimes, etc) in a compact + // format + // * Support for user-defined classes - Accomplished using Serialization + // * Resource lookups should not require loading an entire resource file - If you + // look up a resource, we only load the value for that resource, minimizing working set. + // + // + // There are four sections to the default file format. The first + // is the Resource Manager header, which consists of a magic number + // that identifies this as a Resource file, and a ResourceSet class name. + // The class name is written here to allow users to provide their own + // implementation of a ResourceSet (and a matching ResourceReader) to + // control policy. If objects greater than a certain size or matching a + // certain naming scheme shouldn't be stored in memory, users can tweak that + // with their own subclass of ResourceSet. + // + // The second section in the system default file format is the + // RuntimeResourceSet specific header. This contains a version number for + // the .resources file, the number of resources in this file, the number of + // different types contained in the file, followed by a list of fully + // qualified type names. After this, we include an array of hash values for + // each resource name, then an array of virtual offsets into the name section + // of the file. The hashes allow us to do a binary search on an array of + // integers to find a resource name very quickly without doing many string + // compares (except for once we find the real type, of course). If a hash + // matches, the index into the array of hash values is used as the index + // into the name position array to find the name of the resource. The type + // table allows us to read multiple different classes from the same file, + // including user-defined types, in a more efficient way than using + // Serialization, at least when your .resources file contains a reasonable + // proportion of base data types such as Strings or ints. We use + // Serialization for all the non-instrinsic types. + // + // The third section of the file is the name section. It contains a + // series of resource names, written out as byte-length prefixed little + // endian Unicode strings (UTF-16). After each name is a four byte virtual + // offset into the data section of the file, pointing to the relevant + // string or serialized blob for this resource name. + // + // The fourth section in the file is the data section, which consists + // of a type and a blob of bytes for each item in the file. The type is + // an integer index into the type table. The data is specific to that type, + // but may be a number written in binary format, a String, or a serialized + // Object. + // + // The system default file format (V1) is as follows: + // + // What Type of Data + // ==================================================== =========== + // + // Resource Manager header + // Magic Number (0xBEEFCACE) Int32 + // Resource Manager header version Int32 + // Num bytes to skip from here to get past this header Int32 + // Class name of IResourceReader to parse this file String + // Class name of ResourceSet to parse this file String + // + // RuntimeResourceReader header + // ResourceReader version number Int32 + // [Only in debug V2 builds - "***DEBUG***"] String + // Number of resources in the file Int32 + // Number of types in the type table Int32 + // Name of each type Set of Strings + // Padding bytes for 8-byte alignment (use PAD) Bytes (0-7) + // Hash values for each resource name Int32 array, sorted + // Virtual offset of each resource name Int32 array, coupled with hash values + // Absolute location of Data section Int32 + // + // RuntimeResourceReader Name Section + // Name & virtual offset of each resource Set of (UTF-16 String, Int32) pairs + // + // RuntimeResourceReader Data Section + // Type and Value of each resource Set of (Int32, blob of bytes) pairs + // + // This implementation, when used with the default ResourceReader class, + // loads only the strings that you look up for. It can do string comparisons + // without having to create a new String instance due to some memory mapped + // file optimizations in the ResourceReader and FastResourceComparer + // classes. This keeps the memory we touch to a minimum when loading + // resources. + // + // If you use a different IResourceReader class to read a file, or if you + // do case-insensitive lookups (and the case-sensitive lookup fails) then + // we will load all the names of each resource and each resource value. + // This could probably use some optimization. + // + // In addition, this supports object serialization in a similar fashion. + // We build an array of class types contained in this file, and write it + // to RuntimeResourceReader header section of the file. Every resource + // will contain its type (as an index into the array of classes) with the data + // for that resource. We will use the Runtime's serialization support for this. + // + // All strings in the file format are written with BinaryReader and + // BinaryWriter, which writes out the length of the String in bytes as an + // Int32 then the contents as Unicode chars encoded in UTF-8. In the name + // table though, each resource name is written in UTF-16 so we can do a + // string compare byte by byte against the contents of the file, without + // allocating objects. Ideally we'd have a way of comparing UTF-8 bytes + // directly against a String object, but that may be a lot of work. + // + // The offsets of each resource string are relative to the beginning + // of the Data section of the file. This way, if a tool decided to add + // one resource to a file, it would only need to increment the number of + // resources, add the hash & location of last byte in the name section + // to the array of resource hashes and resource name positions (carefully + // keeping these arrays sorted), add the name to the end of the name & + // offset list, possibly add the type list of types types (and increase + // the number of items in the type table), and add the resource value at + // the end of the file. The other offsets wouldn't need to be updated to + // reflect the longer header section. + // + // Resource files are currently limited to 2 gigabytes due to these + // design parameters. A future version may raise the limit to 4 gigabytes + // by using unsigned integers, or may use negative numbers to load items + // out of an assembly manifest. Also, we may try sectioning the resource names + // into smaller chunks, each of size sqrt(n), would be substantially better for + // resource files containing thousands of resources. + // +#if CORERT + public // On CoreRT, this must be public because of need to whitelist past the ReflectionBlock. +#else + internal +#endif + sealed class RuntimeResourceSet : ResourceSet, IEnumerable + { + internal const int Version = 2; // File format version number + + // Cache for resources. Key is the resource name, which can be cached + // for arbitrarily long times, since the object is usually a string + // literal that will live for the lifetime of the appdomain. The + // value is a ResourceLocator instance, which might cache the object. + private Dictionary _resCache; + + + // For our special load-on-demand reader, cache the cast. The + // RuntimeResourceSet's implementation knows how to treat this reader specially. + private ResourceReader _defaultReader; + + // This is a lookup table for case-insensitive lookups, and may be null. + // Consider always using a case-insensitive resource cache, as we don't + // want to fill this out if we can avoid it. The problem is resource + // fallback will somewhat regularly cause us to look up resources that + // don't exist. + private Dictionary _caseInsensitiveTable; + + // If we're not using our custom reader, then enumerate through all + // the resources once, adding them into the table. + private bool _haveReadFromReader; + + internal RuntimeResourceSet(String fileName) : base(false) + { + _resCache = new Dictionary(FastResourceComparer.Default); + Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); + _defaultReader = new ResourceReader(stream, _resCache); + Reader = _defaultReader; + } + + internal RuntimeResourceSet(Stream stream) : base(false) + { + _resCache = new Dictionary(FastResourceComparer.Default); + _defaultReader = new ResourceReader(stream, _resCache); + Reader = _defaultReader; + } + + protected override void Dispose(bool disposing) + { + if (Reader == null) + return; + + if (disposing) + { + lock (Reader) + { + _resCache = null; + if (_defaultReader != null) + { + _defaultReader.Close(); + _defaultReader = null; + } + _caseInsensitiveTable = null; + // Set Reader to null to avoid a race in GetObject. + base.Dispose(disposing); + } + } + else + { + // Just to make sure we always clear these fields in the future... + _resCache = null; + _caseInsensitiveTable = null; + _defaultReader = null; + base.Dispose(disposing); + } + } + + public override IDictionaryEnumerator GetEnumerator() + { + return GetEnumeratorHelper(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumeratorHelper(); + } + + private IDictionaryEnumerator GetEnumeratorHelper() + { + IResourceReader copyOfReader = Reader; + if (copyOfReader == null || _resCache == null) + throw new ObjectDisposedException(null, SR.ObjectDisposed_ResourceSet); + + return copyOfReader.GetEnumerator(); + } + + + public override String GetString(String key) + { + Object o = GetObject(key, false, true); + return (String)o; + } + + public override String GetString(String key, bool ignoreCase) + { + Object o = GetObject(key, ignoreCase, true); + return (String)o; + } + + public override Object GetObject(String key) + { + return GetObject(key, false, false); + } + + public override Object GetObject(String key, bool ignoreCase) + { + return GetObject(key, ignoreCase, false); + } + + private Object GetObject(String key, bool ignoreCase, bool isString) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + if (Reader == null || _resCache == null) + throw new ObjectDisposedException(null, SR.ObjectDisposed_ResourceSet); + + Object value = null; + ResourceLocator resLocation; + + lock (Reader) + { + if (Reader == null) + throw new ObjectDisposedException(null, SR.ObjectDisposed_ResourceSet); + + if (_defaultReader != null) + { + // Find the offset within the data section + int dataPos = -1; + if (_resCache.TryGetValue(key, out resLocation)) + { + value = resLocation.Value; + dataPos = resLocation.DataPosition; + } + + if (dataPos == -1 && value == null) + { + dataPos = _defaultReader.FindPosForResource(key); + } + + if (dataPos != -1 && value == null) + { + Debug.Assert(dataPos >= 0, "data section offset cannot be negative!"); + // Normally calling LoadString or LoadObject requires + // taking a lock. Note that in this case, we took a + // lock on the entire RuntimeResourceSet, which is + // sufficient since we never pass this ResourceReader + // to anyone else. + ResourceTypeCode typeCode; + if (isString) + { + value = _defaultReader.LoadString(dataPos); + typeCode = ResourceTypeCode.String; + } + else + { + value = _defaultReader.LoadObject(dataPos, out typeCode); + } + + resLocation = new ResourceLocator(dataPos, (ResourceLocator.CanCache(typeCode)) ? value : null); + lock (_resCache) + { + _resCache[key] = resLocation; + } + } + + if (value != null || !ignoreCase) + { + return value; // may be null + } + } // if (_defaultReader != null) + + // At this point, we either don't have our default resource reader + // or we haven't found the particular resource we're looking for + // and may have to search for it in a case-insensitive way. + if (!_haveReadFromReader) + { + // If necessary, init our case insensitive hash table. + if (ignoreCase && _caseInsensitiveTable == null) + { + _caseInsensitiveTable = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + if (_defaultReader == null) + { + IDictionaryEnumerator en = Reader.GetEnumerator(); + while (en.MoveNext()) + { + DictionaryEntry entry = en.Entry; + String readKey = (String)entry.Key; + ResourceLocator resLoc = new ResourceLocator(-1, entry.Value); + _resCache.Add(readKey, resLoc); + if (ignoreCase) + _caseInsensitiveTable.Add(readKey, resLoc); + } + // Only close the reader if it is NOT our default one, + // since we need it around to resolve ResourceLocators. + if (!ignoreCase) + Reader.Close(); + } + else + { + Debug.Assert(ignoreCase, "This should only happen for case-insensitive lookups"); + ResourceReader.ResourceEnumerator en = _defaultReader.GetEnumeratorInternal(); + while (en.MoveNext()) + { + // Note: Always ask for the resource key before the data position. + String currentKey = (String)en.Key; + int dataPos = en.DataPosition; + ResourceLocator resLoc = new ResourceLocator(dataPos, null); + _caseInsensitiveTable.Add(currentKey, resLoc); + } + } + _haveReadFromReader = true; + } + Object obj = null; + bool found = false; + bool keyInWrongCase = false; + if (_defaultReader != null) + { + if (_resCache.TryGetValue(key, out resLocation)) + { + found = true; + obj = ResolveResourceLocator(resLocation, key, _resCache, keyInWrongCase); + } + } + if (!found && ignoreCase) + { + if (_caseInsensitiveTable.TryGetValue(key, out resLocation)) + { + found = true; + keyInWrongCase = true; + obj = ResolveResourceLocator(resLocation, key, _resCache, keyInWrongCase); + } + } + return obj; + } // lock(Reader) + } + + // The last parameter indicates whether the lookup required a + // case-insensitive lookup to succeed, indicating we shouldn't add + // the ResourceLocation to our case-sensitive cache. + private Object ResolveResourceLocator(ResourceLocator resLocation, String key, Dictionary copyOfCache, bool keyInWrongCase) + { + // We need to explicitly resolve loosely linked manifest + // resources, and we need to resolve ResourceLocators with null objects. + Object value = resLocation.Value; + if (value == null) + { + ResourceTypeCode typeCode; + lock (Reader) + { + value = _defaultReader.LoadObject(resLocation.DataPosition, out typeCode); + } + if (!keyInWrongCase && ResourceLocator.CanCache(typeCode)) + { + resLocation.Value = value; + copyOfCache[key] = resLocation; + } + } + return value; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Resources/SatelliteContractVersionAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Resources/SatelliteContractVersionAttribute.cs new file mode 100644 index 0000000000..0707447677 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Resources/SatelliteContractVersionAttribute.cs @@ -0,0 +1,31 @@ +// 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. + +/*============================================================ +** +** +** +** +** +** Purpose: Specifies which version of a satellite assembly +** the ResourceManager should ask for. +** +** +===========================================================*/ + +namespace System.Resources +{ + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] + public sealed class SatelliteContractVersionAttribute : Attribute + { + public SatelliteContractVersionAttribute(String version) + { + if (version == null) + throw new ArgumentNullException(nameof(version)); + Version = version; + } + + public String Version { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Resources/UltimateResourceFallbackLocation.cs b/external/corefx/src/Common/src/CoreLib/System/Resources/UltimateResourceFallbackLocation.cs new file mode 100644 index 0000000000..83640ec9fe --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Resources/UltimateResourceFallbackLocation.cs @@ -0,0 +1,25 @@ +// 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. + +/*============================================================ +** +** +** +** +** Purpose: Tells the ResourceManager where to find the +** ultimate fallback resources for your assembly. +** +** +===========================================================*/ + +using System; + +namespace System.Resources +{ + public enum UltimateResourceFallbackLocation + { + MainAssembly, + Satellite + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/AccessedThroughPropertyAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/AccessedThroughPropertyAttribute.cs new file mode 100644 index 0000000000..25efcafa3f --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/AccessedThroughPropertyAttribute.cs @@ -0,0 +1,17 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Field)] + public sealed class AccessedThroughPropertyAttribute : Attribute + { + public AccessedThroughPropertyAttribute(string propertyName) + { + PropertyName = propertyName; + } + + public string PropertyName { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/AsyncMethodBuilderAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/AsyncMethodBuilderAttribute.cs new file mode 100644 index 0000000000..688a3a01ba --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/AsyncMethodBuilderAttribute.cs @@ -0,0 +1,21 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + /// + /// Indicates the type of the async method builder that should be used by a language compiler to + /// build the attributed type when used as the return type of an async method. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Delegate | AttributeTargets.Enum, Inherited = false, AllowMultiple = false)] + public sealed class AsyncMethodBuilderAttribute : Attribute + { + /// Initializes the . + /// The of the associated builder. + public AsyncMethodBuilderAttribute(Type builderType) => BuilderType = builderType; + + /// Gets the of the associated builder. + public Type BuilderType { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/AsyncStateMachineAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/AsyncStateMachineAttribute.cs new file mode 100644 index 0000000000..66c9175ee7 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/AsyncStateMachineAttribute.cs @@ -0,0 +1,15 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + public sealed class AsyncStateMachineAttribute : StateMachineAttribute + { + public AsyncStateMachineAttribute(Type stateMachineType) + : base(stateMachineType) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs new file mode 100644 index 0000000000..b5ecd7924c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs @@ -0,0 +1,111 @@ +// 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.Runtime.InteropServices; +using System.Security; +using System.Threading.Tasks; + +namespace System.Runtime.CompilerServices +{ + /// Represents a builder for asynchronous methods that returns a . + /// The type of the result. + [StructLayout(LayoutKind.Auto)] + public struct AsyncValueTaskMethodBuilder + { + /// The to which most operations are delegated. + private AsyncTaskMethodBuilder _methodBuilder; // mutable struct; do not make it readonly + /// The result for this builder, if it's completed before any awaits occur. + private TResult _result; + /// true if contains the synchronous result for the async method; otherwise, false. + private bool _haveResult; + /// true if the builder should be used for setting/getting the result; otherwise, false. + private bool _useBuilder; + + /// Creates an instance of the struct. + /// The initialized instance. + public static AsyncValueTaskMethodBuilder Create() => +#if CORERT + // corert's AsyncTaskMethodBuilder.Create() currently does additional debugger-related + // work, so we need to delegate to it. + new AsyncValueTaskMethodBuilder() { _methodBuilder = AsyncTaskMethodBuilder.Create() }; +#else + // _methodBuilder should be initialized to AsyncTaskMethodBuilder.Create(), but on coreclr + // that Create() is a nop, so we can just return the default here. + default(AsyncValueTaskMethodBuilder); +#endif + + /// Begins running the builder with the associated state machine. + /// The type of the state machine. + /// The state machine instance, passed by reference. + public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine => + _methodBuilder.Start(ref stateMachine); // will provide the right ExecutionContext semantics + + /// Associates the builder with the specified state machine. + /// The state machine instance to associate with the builder. + public void SetStateMachine(IAsyncStateMachine stateMachine) => _methodBuilder.SetStateMachine(stateMachine); + + /// Marks the task as successfully completed. + /// The result to use to complete the task. + public void SetResult(TResult result) + { + if (_useBuilder) + { + _methodBuilder.SetResult(result); + } + else + { + _result = result; + _haveResult = true; + } + } + + /// Marks the task as failed and binds the specified exception to the task. + /// The exception to bind to the task. + public void SetException(Exception exception) => _methodBuilder.SetException(exception); + + /// Gets the task for this builder. + public ValueTask Task + { + get + { + if (_haveResult) + { + return new ValueTask(_result); + } + else + { + _useBuilder = true; + return new ValueTask(_methodBuilder.Task); + } + } + } + + /// Schedules the state machine to proceed to the next action when the specified awaiter completes. + /// The type of the awaiter. + /// The type of the state machine. + /// the awaiter + /// The state machine. + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine + { + _useBuilder = true; + _methodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine); + } + + /// Schedules the state machine to proceed to the next action when the specified awaiter completes. + /// The type of the awaiter. + /// The type of the state machine. + /// the awaiter + /// The state machine. + [SecuritySafeCritical] + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine + { + _useBuilder = true; + _methodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CallerFilePathAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CallerFilePathAttribute.cs new file mode 100644 index 0000000000..5858634b42 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CallerFilePathAttribute.cs @@ -0,0 +1,14 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + public sealed class CallerFilePathAttribute : Attribute + { + public CallerFilePathAttribute() + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CallerLineNumberAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CallerLineNumberAttribute.cs new file mode 100644 index 0000000000..5bd2fcb91b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CallerLineNumberAttribute.cs @@ -0,0 +1,14 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + public sealed class CallerLineNumberAttribute : Attribute + { + public CallerLineNumberAttribute() + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CallerMemberNameAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CallerMemberNameAttribute.cs new file mode 100644 index 0000000000..8b046335b5 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CallerMemberNameAttribute.cs @@ -0,0 +1,14 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + public sealed class CallerMemberNameAttribute : Attribute + { + public CallerMemberNameAttribute() + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CompilationRelaxations.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CompilationRelaxations.cs new file mode 100644 index 0000000000..88e2657a6a --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CompilationRelaxations.cs @@ -0,0 +1,15 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + /// IMPORTANT: Keep this in sync with corhdr.h + [Flags] + public enum CompilationRelaxations : int + { + NoStringInterning = 0x0008 // Start in 0x0008, we had other non public flags in this enum before, + // so we'll start here just in case somebody used them. This flag is only + // valid when set for Assemblies. + } +} \ No newline at end of file diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CompilationRelaxationsAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CompilationRelaxationsAttribute.cs new file mode 100644 index 0000000000..d6da23fdf2 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CompilationRelaxationsAttribute.cs @@ -0,0 +1,22 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Method)] + public class CompilationRelaxationsAttribute : Attribute + { + public CompilationRelaxationsAttribute(int relaxations) + { + CompilationRelaxations = relaxations; + } + + public CompilationRelaxationsAttribute(CompilationRelaxations relaxations) + { + CompilationRelaxations = (int)relaxations; + } + + public int CompilationRelaxations { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CompilerGeneratedAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CompilerGeneratedAttribute.cs new file mode 100644 index 0000000000..1c05abd1fe --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CompilerGeneratedAttribute.cs @@ -0,0 +1,12 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.All, Inherited = true)] + public sealed class CompilerGeneratedAttribute : Attribute + { + public CompilerGeneratedAttribute() { } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CompilerGlobalScopeAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CompilerGlobalScopeAttribute.cs new file mode 100644 index 0000000000..752295e876 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CompilerGlobalScopeAttribute.cs @@ -0,0 +1,15 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + // Attribute used to communicate to the VS7 debugger that a class should be treated as if it has global scope. + + [AttributeUsage(AttributeTargets.Class)] + public class CompilerGlobalScopeAttribute : Attribute + { + public CompilerGlobalScopeAttribute() { } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs new file mode 100644 index 0000000000..4e8ce691be --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs @@ -0,0 +1,88 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace System.Runtime.CompilerServices +{ + /// Provides an awaitable type that enables configured awaits on a . + /// The type of the result produced. + [StructLayout(LayoutKind.Auto)] + public readonly struct ConfiguredValueTaskAwaitable + { + /// The wrapped . + private readonly ValueTask _value; + /// true to attempt to marshal the continuation back to the original context captured; otherwise, false. + private readonly bool _continueOnCapturedContext; + + /// Initializes the awaitable. + /// The wrapped . + /// + /// true to attempt to marshal the continuation back to the original synchronization context captured; otherwise, false. + /// + internal ConfiguredValueTaskAwaitable(ValueTask value, bool continueOnCapturedContext) + { + _value = value; + _continueOnCapturedContext = continueOnCapturedContext; + } + + /// Returns an awaiter for this instance. + public ConfiguredValueTaskAwaiter GetAwaiter() => + new ConfiguredValueTaskAwaiter(_value, _continueOnCapturedContext); + + /// Provides an awaiter for a . + [StructLayout(LayoutKind.Auto)] + public struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion, IConfiguredValueTaskAwaiter + { + /// The value being awaited. + private ValueTask _value; // Methods are called on this; avoid making it readonly so as to avoid unnecessary copies + /// The value to pass to ConfigureAwait. + internal readonly bool _continueOnCapturedContext; + + /// Initializes the awaiter. + /// The value to be awaited. + /// The value to pass to ConfigureAwait. + internal ConfiguredValueTaskAwaiter(ValueTask value, bool continueOnCapturedContext) + { + _value = value; + _continueOnCapturedContext = continueOnCapturedContext; + } + + /// Gets whether the has completed. + public bool IsCompleted => _value.IsCompleted; + + /// Gets the result of the ValueTask. + [StackTraceHidden] + public TResult GetResult() => + _value._task == null ? + _value._result : + _value._task.GetAwaiter().GetResult(); + + /// Schedules the continuation action for the . + public void OnCompleted(Action continuation) => + _value.AsTask().ConfigureAwait(_continueOnCapturedContext).GetAwaiter().OnCompleted(continuation); + + /// Schedules the continuation action for the . + public void UnsafeOnCompleted(Action continuation) => + _value.AsTask().ConfigureAwait(_continueOnCapturedContext).GetAwaiter().UnsafeOnCompleted(continuation); + + /// Gets the task underlying . + internal Task AsTask() => _value.AsTask(); + + /// Gets the task underlying the incomplete . + /// This method is used when awaiting and IsCompleted returned false; thus we expect the value task to be wrapping a non-null task. + (Task task, bool continueOnCapturedContext) IConfiguredValueTaskAwaiter.GetTask() => (_value.AsTaskExpectNonNull(), _continueOnCapturedContext); + } + } + + /// + /// Internal interface used to enable extract the Task from arbitrary configured ValueTask awaiters. + /// + internal interface IConfiguredValueTaskAwaiter + { + (Task task, bool continueOnCapturedContext) GetTask(); + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/CustomConstantAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CustomConstantAttribute.cs similarity index 88% rename from external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/CustomConstantAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CustomConstantAttribute.cs index dab4bbd538..f75693eb40 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/CustomConstantAttribute.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/CustomConstantAttribute.cs @@ -2,9 +2,6 @@ // 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.Reflection; -using System.Collections.Generic; - namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)] @@ -13,4 +10,3 @@ namespace System.Runtime.CompilerServices public abstract Object Value { get; } } } - diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs similarity index 64% rename from external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs index 60d81cd541..813e6803bf 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs @@ -2,28 +2,18 @@ // 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.Reflection; -using System.Diagnostics.Contracts; - namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)] public sealed class DateTimeConstantAttribute : CustomConstantAttribute { + private DateTime _date; + public DateTimeConstantAttribute(long ticks) { - _date = new System.DateTime(ticks); + _date = new DateTime(ticks); } - public override Object Value - { - get - { - return _date; - } - } - - private System.DateTime _date; + public override Object Value => _date; } } - diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/DecimalConstantAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/DecimalConstantAttribute.cs similarity index 71% rename from external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/DecimalConstantAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/DecimalConstantAttribute.cs index b918101fc2..19db84eb43 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/DecimalConstantAttribute.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/DecimalConstantAttribute.cs @@ -4,13 +4,13 @@ // Note: If you add a new ctor overloads you need to update ParameterInfo.RawDefaultValue -using System.Collections.Generic; - namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)] public sealed class DecimalConstantAttribute : Attribute { + private Decimal _dec; + [CLSCompliant(false)] public DecimalConstantAttribute( byte scale, @@ -20,7 +20,7 @@ namespace System.Runtime.CompilerServices uint low ) { - _dec = new System.Decimal((int)low, (int)mid, (int)hi, (sign != 0), scale); + _dec = new Decimal((int)low, (int)mid, (int)hi, (sign != 0), scale); } public DecimalConstantAttribute( @@ -31,18 +31,9 @@ namespace System.Runtime.CompilerServices int low ) { - _dec = new System.Decimal(low, mid, hi, (sign != 0), scale); + _dec = new Decimal(low, mid, hi, (sign != 0), scale); } - public System.Decimal Value - { - get - { - return _dec; - } - } - - private System.Decimal _dec; + public Decimal Value => _dec; } } - diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/DefaultDependencyAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/DefaultDependencyAttribute.cs new file mode 100644 index 0000000000..4c1f489215 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/DefaultDependencyAttribute.cs @@ -0,0 +1,17 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Assembly)] + public sealed class DefaultDependencyAttribute : Attribute + { + public DefaultDependencyAttribute(LoadHint loadHintArgument) + { + LoadHint = loadHintArgument; + } + + public LoadHint LoadHint { get; } + } +} \ No newline at end of file diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/DependencyAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/DependencyAttribute.cs new file mode 100644 index 0000000000..0fe07edc9e --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/DependencyAttribute.cs @@ -0,0 +1,19 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class DependencyAttribute : Attribute + { + public DependencyAttribute(String dependentAssemblyArgument, LoadHint loadHintArgument) + { + DependentAssembly = dependentAssemblyArgument; + LoadHint = loadHintArgument; + } + + public String DependentAssembly { get; } + public LoadHint LoadHint { get; } + } +} \ No newline at end of file diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/DisablePrivateReflectionAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/DisablePrivateReflectionAttribute.cs new file mode 100644 index 0000000000..4fc00e10ed --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/DisablePrivateReflectionAttribute.cs @@ -0,0 +1,13 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)] + public sealed class DisablePrivateReflectionAttribute : Attribute + { + public DisablePrivateReflectionAttribute() { } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/DiscardableAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/DiscardableAttribute.cs new file mode 100644 index 0000000000..c88b3a7599 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/DiscardableAttribute.cs @@ -0,0 +1,13 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + // Custom attribute to indicating a TypeDef is a discardable attribute. + + public class DiscardableAttribute : Attribute + { + public DiscardableAttribute() { } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/ExtensionAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/ExtensionAttribute.cs new file mode 100644 index 0000000000..92170880f1 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/ExtensionAttribute.cs @@ -0,0 +1,14 @@ +// 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; + +namespace System.Runtime.CompilerServices +{ + /// + /// Indicates that a method is an extension method, or that a class or assembly contains extension methods. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)] + public sealed class ExtensionAttribute : Attribute { } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/FixedAddressValueTypeAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/FixedAddressValueTypeAttribute.cs new file mode 100644 index 0000000000..8dc6c43126 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/FixedAddressValueTypeAttribute.cs @@ -0,0 +1,12 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Field)] + public sealed class FixedAddressValueTypeAttribute : Attribute + { + public FixedAddressValueTypeAttribute() { } + } +} \ No newline at end of file diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/FixedBufferAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/FixedBufferAttribute.cs new file mode 100644 index 0000000000..bb8f00f686 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/FixedBufferAttribute.cs @@ -0,0 +1,32 @@ +// 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. + +/*============================================================ +** +** +** Purpose: Used by a compiler for generating value types +** in-place within other value types containing a certain +** number of elements of the given (primitive) type. Somewhat +** similar to P/Invoke's ByValTStr attribute. +** Used by C# with this syntax: "fixed int buffer[10];" +** +===========================================================*/ + +using System; + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Field, Inherited = false)] + public sealed class FixedBufferAttribute : Attribute + { + public FixedBufferAttribute(Type elementType, int length) + { + ElementType = elementType; + Length = length; + } + + public Type ElementType { get; } + public int Length { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/FormattableStringFactory.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/FormattableStringFactory.cs new file mode 100644 index 0000000000..23d03860ed --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/FormattableStringFactory.cs @@ -0,0 +1,58 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: implementation of the FormattableStringFactory +** class. +** +===========================================================*/ + +namespace System.Runtime.CompilerServices +{ + /// + /// A factory type used by compilers to create instances of the type . + /// + public static class FormattableStringFactory + { + /// + /// Create a from a composite format string and object + /// array containing zero or more objects to format. + /// + public static FormattableString Create(string format, params object[] arguments) + { + if (format == null) + { + throw new ArgumentNullException(nameof(format)); + } + + if (arguments == null) + { + throw new ArgumentNullException(nameof(arguments)); + } + + return new ConcreteFormattableString(format, arguments); + } + + private sealed class ConcreteFormattableString : FormattableString + { + private readonly string _format; + private readonly object[] _arguments; + + internal ConcreteFormattableString(string format, object[] arguments) + { + _format = format; + _arguments = arguments; + } + + public override string Format { get { return _format; } } + public override object[] GetArguments() { return _arguments; } + public override int ArgumentCount { get { return _arguments.Length; } } + public override object GetArgument(int index) { return _arguments[index]; } + public override string ToString(IFormatProvider formatProvider) { return string.Format(formatProvider, _format, _arguments); } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IAsyncStateMachine.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IAsyncStateMachine.cs new file mode 100644 index 0000000000..7fb7ea5395 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IAsyncStateMachine.cs @@ -0,0 +1,27 @@ +// 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. + +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// +// +// Represents state machines generated for asynchronous methods. +// +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +namespace System.Runtime.CompilerServices +{ + /// + /// Represents state machines generated for asynchronous methods. + /// This type is intended for compiler use only. + /// + public interface IAsyncStateMachine + { + /// Moves the state machine to its next state. + void MoveNext(); + /// Configures the state machine with a heap-allocated replica. + /// The heap-allocated replica. + void SetStateMachine(IAsyncStateMachine stateMachine); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/INotifyCompletion.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/INotifyCompletion.cs new file mode 100644 index 0000000000..aba0a0691f --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/INotifyCompletion.cs @@ -0,0 +1,39 @@ +// 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. + +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= +// +// +// +// Interfaces used to represent instances that notify listeners of their completion via continuations. +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +using System; +using System.Security; + +namespace System.Runtime.CompilerServices +{ + /// + /// Represents an operation that will schedule continuations when the operation completes. + /// + public interface INotifyCompletion + { + /// Schedules the continuation action to be invoked when the instance completes. + /// The action to invoke when the operation completes. + /// The argument is null (Nothing in Visual Basic). + void OnCompleted(Action continuation); + } + + /// + /// Represents an awaiter used to schedule continuations when an await operation completes. + /// + public interface ICriticalNotifyCompletion : INotifyCompletion + { + /// Schedules the continuation action to be invoked when the instance completes. + /// The action to invoke when the operation completes. + /// The argument is null (Nothing in Visual Basic). + /// Unlike OnCompleted, UnsafeOnCompleted need not propagate ExecutionContext information. + void UnsafeOnCompleted(Action continuation); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/ITuple.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/ITuple.cs new file mode 100644 index 0000000000..cafee11f8a --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/ITuple.cs @@ -0,0 +1,22 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + /// + /// This interface is required for types that want to be indexed into by dynamic patterns. + /// + public interface ITuple + { + /// + /// The number of positions in this data structure. + /// + int Length { get; } + + /// + /// Get the element at position . + /// + object this[int index] { get; } + } +} diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/HoistedLocal.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IndexerNameAttribute.cs similarity index 51% rename from external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/HoistedLocal.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IndexerNameAttribute.cs index d74b8b3f92..ea843b3daa 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/HoistedLocal.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IndexerNameAttribute.cs @@ -2,12 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Microsoft.CSharp.RuntimeBinder.Semantics +namespace System.Runtime.CompilerServices { - internal sealed class ExprHoistedLocalExpr : ExprWithType + [AttributeUsage(AttributeTargets.Property, Inherited = true)] + public sealed class IndexerNameAttribute : Attribute { - public ExprHoistedLocalExpr(CType type) - : base(ExpressionKind.HoistedLocalExpression, type) + public IndexerNameAttribute(String indexerName) { } } diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/InternalsVisibleToAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/InternalsVisibleToAttribute.cs new file mode 100644 index 0000000000..f754694815 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/InternalsVisibleToAttribute.cs @@ -0,0 +1,21 @@ +// 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; + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)] + public sealed class InternalsVisibleToAttribute : Attribute + { + public InternalsVisibleToAttribute(string assemblyName) + { + AssemblyName = assemblyName; + } + + public string AssemblyName { get; } + public bool AllInternalsVisible { get; set; } = true; + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IntrinsicAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IntrinsicAttribute.cs new file mode 100644 index 0000000000..381b4c63f7 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IntrinsicAttribute.cs @@ -0,0 +1,14 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + // Calls to methods or references to fields marked with this attribute may be replaced at + // some call sites with jit intrinsic expansions. + // Types marked with this attribute may be specially treated by the rumtime/compiler. + [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Field, Inherited = false)] + internal sealed class IntrinsicAttribute : Attribute + { + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IsByRefLikeAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IsByRefLikeAttribute.cs new file mode 100644 index 0000000000..61fa2261ea --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IsByRefLikeAttribute.cs @@ -0,0 +1,25 @@ +// 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. + +#if !MONO +using System.ComponentModel; +#endif + +namespace System.Runtime.CompilerServices +{ + /// + /// Reserved to be used by the compiler for tracking metadata. + /// This attribute should not be used by developers in source code. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + [AttributeUsage(AttributeTargets.Struct)] + public sealed class IsByRefLikeAttribute : Attribute + { + public IsByRefLikeAttribute() + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IsConst.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IsConst.cs new file mode 100644 index 0000000000..7f948b608a --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IsConst.cs @@ -0,0 +1,10 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + public static partial class IsConst + { + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IsReadOnlyAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IsReadOnlyAttribute.cs new file mode 100644 index 0000000000..5bff2a2c07 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IsReadOnlyAttribute.cs @@ -0,0 +1,25 @@ +// 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. + +#if !MONO +using System.ComponentModel; +#endif + +namespace System.Runtime.CompilerServices +{ + /// + /// Reserved to be used by the compiler for tracking metadata. + /// This attribute should not be used by developers in source code. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + [AttributeUsage(AttributeTargets.All, Inherited = false)] + public sealed class IsReadOnlyAttribute : Attribute + { + public IsReadOnlyAttribute() + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IsVolatile.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IsVolatile.cs new file mode 100644 index 0000000000..fd1c6a1b12 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IsVolatile.cs @@ -0,0 +1,12 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + public static class IsVolatile + { + // no instantiation, please! + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IteratorStateMachineAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IteratorStateMachineAttribute.cs new file mode 100644 index 0000000000..53afc95664 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IteratorStateMachineAttribute.cs @@ -0,0 +1,15 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + public sealed class IteratorStateMachineAttribute : StateMachineAttribute + { + public IteratorStateMachineAttribute(Type stateMachineType) + : base(stateMachineType) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/LoadHint.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/LoadHint.cs new file mode 100644 index 0000000000..3820f8544b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/LoadHint.cs @@ -0,0 +1,13 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + public enum LoadHint + { + Default = 0x0000, // No preference specified + Always = 0x0001, // Dependency is always loaded + Sometimes = 0x0002, // Dependency is sometimes loaded + } +} \ No newline at end of file diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/MethodCodeType.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/MethodCodeType.cs new file mode 100644 index 0000000000..841b666198 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/MethodCodeType.cs @@ -0,0 +1,16 @@ +// 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.Reflection; + +namespace System.Runtime.CompilerServices +{ + public enum MethodCodeType + { + IL = MethodImplAttributes.IL, + Native = MethodImplAttributes.Native, + OPTIL = MethodImplAttributes.OPTIL, + Runtime = MethodImplAttributes.Runtime + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/MethodImplAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/MethodImplAttribute.cs similarity index 98% rename from external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/MethodImplAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/MethodImplAttribute.cs index f7a12f144d..8e8f93c268 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/MethodImplAttribute.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/MethodImplAttribute.cs @@ -2,8 +2,6 @@ // 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; - namespace System.Runtime.CompilerServices { // Custom attribute to specify additional method properties. diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/MethodImplOptions.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/MethodImplOptions.cs new file mode 100644 index 0000000000..2b5affc699 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/MethodImplOptions.cs @@ -0,0 +1,21 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + // This Enum matchs the miImpl flags defined in corhdr.h. It is used to specify + // certain method properties. + [Flags] + public enum MethodImplOptions + { + Unmanaged = 0x0004, + NoInlining = 0x0008, + ForwardRef = 0x0010, + Synchronized = 0x0020, + NoOptimization = 0x0040, + PreserveSig = 0x0080, + AggressiveInlining = 0x0100, + InternalCall = 0x1000 + } +} \ No newline at end of file diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/ReferenceAssemblyAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/ReferenceAssemblyAttribute.cs new file mode 100644 index 0000000000..f3842ec562 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/ReferenceAssemblyAttribute.cs @@ -0,0 +1,32 @@ +// 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. + +/*============================================================ +** +** Attribute: ReferenceAssemblyAttribute +** +** Purpose: Identifies an assembly as being a "reference +** assembly", meaning it contains public surface area but +** no usable implementation. Reference assemblies +** should be loadable for introspection, but not execution. +** +============================================================*/ + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] + public sealed class ReferenceAssemblyAttribute : Attribute + { + public ReferenceAssemblyAttribute() + { + } + + public ReferenceAssemblyAttribute(String description) + { + Description = description; + } + + public String Description { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/RuntimeCompatibilityAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/RuntimeCompatibilityAttribute.cs new file mode 100644 index 0000000000..609c560330 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/RuntimeCompatibilityAttribute.cs @@ -0,0 +1,30 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: Mark up the program to indicate various legacy or new opt-in behaviors. +** +** +=============================================================================*/ + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)] + public sealed class RuntimeCompatibilityAttribute : Attribute + { + public RuntimeCompatibilityAttribute() + { + // legacy behavior is the default, and WrapNonExceptionThrows is implicitly + // false thanks to the CLR's guarantee of zeroed memory. + } + + // If a non-CLSCompliant exception (i.e. one that doesn't derive from System.Exception) is + // thrown, should it be wrapped up in a System.Runtime.CompilerServices.RuntimeWrappedException + // instance when presented to catch handlers? + public bool WrapNonExceptionThrows { get; set; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/RuntimeFeature.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/RuntimeFeature.cs new file mode 100644 index 0000000000..c6a97eca20 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/RuntimeFeature.cs @@ -0,0 +1,38 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + public static class RuntimeFeature + { + /// + /// Name of the Portable PDB feature. + /// + public const string PortablePdb = nameof(PortablePdb); + +#if FEATURE_DEFAULT_INTERFACES + /// + /// Indicates that this version of runtime supports default interface method implementations. + /// + public const string DefaultImplementationsOfInterfaces = nameof(DefaultImplementationsOfInterfaces); +#endif + + /// + /// Checks whether a certain feature is supported by the Runtime. + /// + public static bool IsSupported(string feature) + { + switch (feature) + { + case PortablePdb: +#if FEATURE_DEFAULT_INTERFACES + case DefaultImplementationsOfInterfaces: +#endif + return true; + } + + return false; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeWrappedException.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/RuntimeWrappedException.cs similarity index 60% rename from external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeWrappedException.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/RuntimeWrappedException.cs index ab58b17261..72996c6dfc 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeWrappedException.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/RuntimeWrappedException.cs @@ -2,49 +2,40 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -/*============================================================================= -** -** -** -** Purpose: The exception class uses to wrap all non-CLS compliant exceptions. -** -** -=============================================================================*/ - -using System; using System.Runtime.Serialization; namespace System.Runtime.CompilerServices { + /// + /// Exception used to wrap all non-CLS compliant exceptions. + /// [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class RuntimeWrappedException : Exception { - private Object _wrappedException; + private object _wrappedException; // EE expects this name // Not an api but has to be public as System.Linq.Expression invokes this through Reflection when an expression // throws an object that doesn't derive from Exception. - public RuntimeWrappedException(Object thrownObject) + public RuntimeWrappedException(object thrownObject) : base(SR.RuntimeWrappedException) { - HResult = __HResults.COR_E_RUNTIMEWRAPPED; + HResult = HResults.COR_E_RUNTIMEWRAPPED; _wrappedException = thrownObject; } + private RuntimeWrappedException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + _wrappedException = info.GetValue("WrappedException", typeof(object)); + } + public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); - info.AddValue("WrappedException", _wrappedException, typeof(Object)); + info.AddValue("WrappedException", _wrappedException, typeof(object)); } - internal RuntimeWrappedException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - _wrappedException = info.GetValue("WrappedException", typeof(Object)); - } - - public Object WrappedException - { - get { return _wrappedException; } - } + public object WrappedException => _wrappedException; } } diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/SpecialNameAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/SpecialNameAttribute.cs new file mode 100644 index 0000000000..b18e62895f --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/SpecialNameAttribute.cs @@ -0,0 +1,12 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Struct)] + public sealed class SpecialNameAttribute : Attribute + { + public SpecialNameAttribute() { } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/StateMachineAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/StateMachineAttribute.cs new file mode 100644 index 0000000000..e081d63e71 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/StateMachineAttribute.cs @@ -0,0 +1,19 @@ +// 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; + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + public class StateMachineAttribute : Attribute + { + public StateMachineAttribute(Type stateMachineType) + { + StateMachineType = stateMachineType; + } + + public Type StateMachineType { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/StringFreezingAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/StringFreezingAttribute.cs new file mode 100644 index 0000000000..25a8bfbc26 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/StringFreezingAttribute.cs @@ -0,0 +1,14 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + // Custom attribute to indicate that strings should be frozen. + + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + public sealed class StringFreezingAttribute : Attribute + { + public StringFreezingAttribute() { } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/StrongBox.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/StrongBox.cs new file mode 100644 index 0000000000..0a1a565f54 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/StrongBox.cs @@ -0,0 +1,59 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + /// + /// Holds a reference to a value. + /// + /// The type of the value that the references. + public class StrongBox : IStrongBox + { + /// + /// Gets the strongly typed value associated with the + /// This is explicitly exposed as a field instead of a property to enable loading the address of the field. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")] + public T Value; + + /// + /// Initializes a new StrongBox which can receive a value when used in a reference call. + /// + public StrongBox() + { + } + + /// + /// Initializes a new with the specified value. + /// + /// A value that the will reference. + public StrongBox(T value) + { + Value = value; + } + + object IStrongBox.Value + { + get + { + return Value; + } + set + { + Value = (T)value; + } + } + } + + /// + /// Defines a property for accessing the value that an object references. + /// + public interface IStrongBox + { + /// + /// Gets or sets the value the object references. + /// + object Value { get; set; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/SuppressIldasmAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/SuppressIldasmAttribute.cs new file mode 100644 index 0000000000..b4224b1c89 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/SuppressIldasmAttribute.cs @@ -0,0 +1,13 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module)] + public sealed class SuppressIldasmAttribute : Attribute + { + public SuppressIldasmAttribute() { } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/TupleElementNamesAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/TupleElementNamesAttribute.cs new file mode 100644 index 0000000000..ad923dfae5 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/TupleElementNamesAttribute.cs @@ -0,0 +1,57 @@ +// 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.Collections.Generic; + +namespace System.Runtime.CompilerServices +{ + /// + /// Indicates that the use of on a member is meant to be treated as a tuple with element names. + /// + [CLSCompliant(false)] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Event)] + public sealed class TupleElementNamesAttribute : Attribute + { + private readonly string[] _transformNames; + + /// + /// Initializes a new instance of the class. + /// + /// + /// Specifies, in a pre-order depth-first traversal of a type's + /// construction, which occurrences are + /// meant to carry element names. + /// + /// + /// This constructor is meant to be used on types that contain an + /// instantiation of that contains + /// element names. For instance, if C is a generic type with + /// two type parameters, then a use of the constructed type C{, might be intended to + /// treat the first type argument as a tuple with element names and the + /// second as a tuple without element names. In which case, the + /// appropriate attribute specification should use a + /// transformNames value of { "name1", "name2", null, null, + /// null }. + /// + public TupleElementNamesAttribute(string[] transformNames) + { + if (transformNames == null) + { + throw new ArgumentNullException(nameof(transformNames)); + } + + _transformNames = transformNames; + } + + /// + /// Specifies, in a pre-order depth-first traversal of a type's + /// construction, which elements are + /// meant to carry element names. + /// + public IList TransformNames => _transformNames; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/TypeForwardedFromAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/TypeForwardedFromAttribute.cs new file mode 100644 index 0000000000..c4a8558243 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/TypeForwardedFromAttribute.cs @@ -0,0 +1,17 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false, AllowMultiple = false)] + public sealed class TypeForwardedFromAttribute : Attribute + { + public TypeForwardedFromAttribute(string assemblyFullName) + { + AssemblyFullName = assemblyFullName; + } + + public string AssemblyFullName { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/TypeForwardedToAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/TypeForwardedToAttribute.cs new file mode 100644 index 0000000000..85d5c030c1 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/TypeForwardedToAttribute.cs @@ -0,0 +1,17 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)] + public sealed class TypeForwardedToAttribute : Attribute + { + public TypeForwardedToAttribute(Type destination) + { + Destination = destination; + } + + public Type Destination { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/UnsafeValueTypeAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/UnsafeValueTypeAttribute.cs new file mode 100644 index 0000000000..f049c89b3f --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/UnsafeValueTypeAttribute.cs @@ -0,0 +1,11 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Struct)] + sealed public class UnsafeValueTypeAttribute : Attribute + { + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/ValueTaskAwaiter.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/ValueTaskAwaiter.cs new file mode 100644 index 0000000000..7bc8b5cc7d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/ValueTaskAwaiter.cs @@ -0,0 +1,53 @@ +// 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.Diagnostics; +using System.Threading.Tasks; + +namespace System.Runtime.CompilerServices +{ + /// Provides an awaiter for a . + public struct ValueTaskAwaiter : ICriticalNotifyCompletion, IValueTaskAwaiter + { + /// The value being awaited. + private ValueTask _value; // Methods are called on this; avoid making it readonly so as to avoid unnecessary copies + + /// Initializes the awaiter. + /// The value to be awaited. + internal ValueTaskAwaiter(ValueTask value) => _value = value; + + /// Gets whether the has completed. + public bool IsCompleted => _value.IsCompleted; + + /// Gets the result of the ValueTask. + [StackTraceHidden] + public TResult GetResult() => + _value._task == null ? + _value._result : + _value._task.GetAwaiter().GetResult(); + + /// Schedules the continuation action for this ValueTask. + public void OnCompleted(Action continuation) => + _value.AsTask().ConfigureAwait(continueOnCapturedContext: true).GetAwaiter().OnCompleted(continuation); + + /// Schedules the continuation action for this ValueTask. + public void UnsafeOnCompleted(Action continuation) => + _value.AsTask().ConfigureAwait(continueOnCapturedContext: true).GetAwaiter().UnsafeOnCompleted(continuation); + + /// Gets the task underlying . + internal Task AsTask() => _value.AsTask(); + + /// Gets the task underlying the incomplete . + /// This method is used when awaiting and IsCompleted returned false; thus we expect the value task to be wrapping a non-null task. + Task IValueTaskAwaiter.GetTask() => _value.AsTaskExpectNonNull(); + } + + /// + /// Internal interface used to enable extract the Task from arbitrary ValueTask awaiters. + /// > + internal interface IValueTaskAwaiter + { + Task GetTask(); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/YieldAwaitable.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/YieldAwaitable.cs new file mode 100644 index 0000000000..c45ef2484c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/YieldAwaitable.cs @@ -0,0 +1,165 @@ +// 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. + +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// +// +// Compiler-targeted type for switching back into the current execution context, e.g. +// +// await Task.Yield(); +// ===================== +// var $awaiter = Task.Yield().GetAwaiter(); +// if (!$awaiter.IsCompleted) +// { +// $builder.AwaitUnsafeOnCompleted(ref $awaiter, ref this); +// return; +// Label: +// } +// $awaiter.GetResult(); +// +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +using System; +using System.Security; +using System.Diagnostics; +using System.Diagnostics.Tracing; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Runtime.CompilerServices +{ + // NOTE: YieldAwaitable currently has no state; while developers are encouraged to use Task.Yield() to produce one, + // no validation is performed to ensure that the developer isn't doing "await new YieldAwaitable()". Such validation + // would require additional, useless state to be stored, and as this is a type in the CompilerServices namespace, and + // as the above example isn't harmful, we take the cheaper approach of not validating anything. + + /// Provides an awaitable context for switching into a target environment. + /// This type is intended for compiler use only. + public readonly struct YieldAwaitable + { + /// Gets an awaiter for this . + /// An awaiter for this awaitable. + /// This method is intended for compiler user rather than use directly in code. + public YieldAwaiter GetAwaiter() { return new YieldAwaiter(); } + + /// Provides an awaiter that switches into a target environment. + /// This type is intended for compiler use only. + public readonly struct YieldAwaiter : ICriticalNotifyCompletion + { + /// Gets whether a yield is not required. + /// This property is intended for compiler user rather than use directly in code. + public bool IsCompleted { get { return false; } } // yielding is always required for YieldAwaiter, hence false + + /// Posts the back to the current context. + /// The action to invoke asynchronously. + /// The argument is null (Nothing in Visual Basic). + public void OnCompleted(Action continuation) + { + QueueContinuation(continuation, flowContext: true); + } + + /// Posts the back to the current context. + /// The action to invoke asynchronously. + /// The argument is null (Nothing in Visual Basic). + public void UnsafeOnCompleted(Action continuation) + { + QueueContinuation(continuation, flowContext: false); + } + + /// Posts the back to the current context. + /// The action to invoke asynchronously. + /// true to flow ExecutionContext; false if flowing is not required. + /// The argument is null (Nothing in Visual Basic). + private static void QueueContinuation(Action continuation, bool flowContext) + { + // Validate arguments + if (continuation == null) throw new ArgumentNullException(nameof(continuation)); + + if (TplEtwProvider.Log.IsEnabled()) + { + continuation = OutputCorrelationEtwEvent(continuation); + } + // Get the current SynchronizationContext, and if there is one, + // post the continuation to it. However, treat the base type + // as if there wasn't a SynchronizationContext, since that's what it + // logically represents. + var syncCtx = SynchronizationContext.Current; + if (syncCtx != null && syncCtx.GetType() != typeof(SynchronizationContext)) + { + syncCtx.Post(s_sendOrPostCallbackRunAction, continuation); + } + else + { + // If we're targeting the default scheduler, queue to the thread pool, so that we go into the global + // queue. As we're going into the global queue, we might as well use QUWI, which for the global queue is + // just a tad faster than task, due to a smaller object getting allocated and less work on the execution path. + TaskScheduler scheduler = TaskScheduler.Current; + if (scheduler == TaskScheduler.Default) + { + if (flowContext) + { + ThreadPool.QueueUserWorkItem(s_waitCallbackRunAction, continuation); + } + else + { + ThreadPool.UnsafeQueueUserWorkItem(s_waitCallbackRunAction, continuation); + } + } + // We're targeting a custom scheduler, so queue a task. + else + { + Task.Factory.StartNew(continuation, default(CancellationToken), TaskCreationOptions.PreferFairness, scheduler); + } + } + } + + private static Action OutputCorrelationEtwEvent(Action continuation) + { +#if CORERT + // TODO + return continuation; +#else + int continuationId = Task.NewId(); + Task currentTask = Task.InternalCurrent; + // fire the correlation ETW event + TplEtwProvider.Log.AwaitTaskContinuationScheduled(TaskScheduler.Current.Id, (currentTask != null) ? currentTask.Id : 0, continuationId); + + return AsyncMethodBuilderCore.CreateContinuationWrapper(continuation, (innerContinuation,continuationIdTask) => + { + var etwLog = TplEtwProvider.Log; + etwLog.TaskWaitContinuationStarted(((Task)continuationIdTask).Result); + + // ETW event for Task Wait End. + Guid prevActivityId = new Guid(); + // Ensure the continuation runs under the correlated activity ID generated above + if (etwLog.TasksSetActivityIds) + EventSource.SetCurrentThreadActivityId(TplEtwProvider.CreateGuidForTaskID(((Task)continuationIdTask).Result), out prevActivityId); + + // Invoke the original continuation provided to OnCompleted. + innerContinuation(); + // Restore activity ID + + if (etwLog.TasksSetActivityIds) + EventSource.SetCurrentThreadActivityId(prevActivityId); + + etwLog.TaskWaitContinuationComplete(((Task)continuationIdTask).Result); + }, Task.FromResult(continuationId)); // pass the ID in a task to avoid a closure\ +#endif + } + + /// WaitCallback that invokes the Action supplied as object state. + private static readonly WaitCallback s_waitCallbackRunAction = RunAction; + /// SendOrPostCallback that invokes the Action supplied as object state. + private static readonly SendOrPostCallback s_sendOrPostCallbackRunAction = RunAction; + + /// Runs an Action delegate provided as state. + /// The Action delegate to invoke. + private static void RunAction(object state) { ((Action)state)(); } + + /// Ends the await operation. + public void GetResult() { } // Nop. It exists purely because the compiler pattern demands it. + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/ConstrainedExecution/Cer.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/ConstrainedExecution/Cer.cs new file mode 100644 index 0000000000..77ab3ea770 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/ConstrainedExecution/Cer.cs @@ -0,0 +1,13 @@ +// 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. + +namespace System.Runtime.ConstrainedExecution +{ + public enum Cer : int + { + None = 0, + MayFail = 1, // Might fail, but the method will say it failed + Success = 2, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/ConstrainedExecution/Consistency.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/ConstrainedExecution/Consistency.cs new file mode 100644 index 0000000000..e2cc79ec35 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/ConstrainedExecution/Consistency.cs @@ -0,0 +1,14 @@ +// 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. + +namespace System.Runtime.ConstrainedExecution +{ + public enum Consistency : int + { + MayCorruptProcess = 0, + MayCorruptAppDomain = 1, + MayCorruptInstance = 2, + WillNotCorruptState = 3, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/ConstrainedExecution/ReliabilityContractAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/ConstrainedExecution/ReliabilityContractAttribute.cs new file mode 100644 index 0000000000..b3cb0143fa --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/ConstrainedExecution/ReliabilityContractAttribute.cs @@ -0,0 +1,33 @@ +// 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. + +// +/*============================================================ +** +** +** +** Purpose: Defines a publically documentable contract for +** reliability between a method and its callers, expressing +** what state will remain consistent in the presence of +** failures (ie async exceptions like thread abort) and whether +** the method needs to be called from within a CER. +** +** +===========================================================*/ + +namespace System.Runtime.ConstrainedExecution +{ + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Interface /* | AttributeTargets.Delegate*/, Inherited = false)] + public sealed class ReliabilityContractAttribute : Attribute + { + public ReliabilityContractAttribute(Consistency consistencyGuarantee, Cer cer) + { + ConsistencyGuarantee = consistencyGuarantee; + Cer = cer; + } + + public Consistency ConsistencyGuarantee { get; } + public Cer Cer { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/ExceptionServices/ExceptionNotification.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/ExceptionServices/ExceptionNotification.cs new file mode 100644 index 0000000000..605588d657 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/ExceptionServices/ExceptionNotification.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.Runtime.ExceptionServices +{ + // Definition of the argument-type passed to the FirstChanceException event handler + public class FirstChanceExceptionEventArgs : EventArgs + { + public FirstChanceExceptionEventArgs(Exception exception) + { + Exception = exception; + } + + // Returns the exception object pertaining to the first chance exception + public Exception Exception { get; } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionAttributes.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/ExceptionServices/HandleProcessCorruptedStateExceptionsAttribute.cs similarity index 97% rename from external/corert/src/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionAttributes.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/ExceptionServices/HandleProcessCorruptedStateExceptionsAttribute.cs index 7b4f8b6c80..cc1bc81e5a 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionAttributes.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/ExceptionServices/HandleProcessCorruptedStateExceptionsAttribute.cs @@ -2,8 +2,6 @@ // 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; - namespace System.Runtime.ExceptionServices { // This attribute can be applied to methods to indicate that ProcessCorruptedState diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/BestFitMappingAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/BestFitMappingAttribute.cs similarity index 79% rename from external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/BestFitMappingAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/BestFitMappingAttribute.cs index a880c62e6f..4ebee1538c 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/BestFitMappingAttribute.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/BestFitMappingAttribute.cs @@ -7,14 +7,13 @@ namespace System.Runtime.InteropServices [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)] public sealed class BestFitMappingAttribute : Attribute { - internal bool _bestFitMapping; - public BestFitMappingAttribute(bool BestFitMapping) { - _bestFitMapping = BestFitMapping; + this.BestFitMapping = BestFitMapping; } - public bool BestFitMapping { get { return _bestFitMapping; } } + public bool BestFitMapping { get; } + public bool ThrowOnUnmappableChar; } -} \ No newline at end of file +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/CallingConvention.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/CallingConvention.cs new file mode 100644 index 0000000000..3b18fdee3a --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/CallingConvention.cs @@ -0,0 +1,16 @@ +// 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. + +namespace System.Runtime.InteropServices +{ + // Used for the CallingConvention named argument to the DllImport and NativeCallable attribute + public enum CallingConvention + { + Winapi = 1, + Cdecl = 2, + StdCall = 3, + ThisCall = 4, + FastCall = 5, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/CharSet.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/CharSet.cs new file mode 100644 index 0000000000..d587ec006b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/CharSet.cs @@ -0,0 +1,21 @@ +// 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. + +namespace System.Runtime.InteropServices +{ + // Use this in P/Invoke function prototypes to specify + // which character set to use when marshalling Strings. + // Using Ansi will marshal the strings as 1 byte char*'s. + // Using Unicode will marshal the strings as 2 byte wchar*'s. + // Generally you probably want to use Auto, which does the + // right thing 99% of the time. + + public enum CharSet + { + None = 1, // User didn't specify how to marshal strings. + Ansi = 2, // Strings should be marshalled as ANSI 1 byte chars. + Unicode = 3, // Strings should be marshalled as Unicode 2 byte chars. + Auto = 4, // Marshal Strings in the right way for the target system. + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/ComVisibleAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/ComVisibleAttribute.cs new file mode 100644 index 0000000000..84b9505a5a --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/ComVisibleAttribute.cs @@ -0,0 +1,17 @@ +// 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. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property, Inherited = false)] + public sealed class ComVisibleAttribute : Attribute + { + public ComVisibleAttribute(bool visibility) + { + Value = visibility; + } + + public bool Value { get; } + } +} diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/DefaultCharSetAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/DefaultCharSetAttribute.cs similarity index 75% rename from external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/DefaultCharSetAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/DefaultCharSetAttribute.cs index 98b39ef18f..7a486f7017 100644 --- a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/DefaultCharSetAttribute.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/DefaultCharSetAttribute.cs @@ -2,24 +2,16 @@ // 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; - namespace System.Runtime.InteropServices { [AttributeUsage(AttributeTargets.Module, Inherited = false)] public sealed class DefaultCharSetAttribute : Attribute { - internal CharSet _CharSet; - public DefaultCharSetAttribute(CharSet charSet) { - _CharSet = charSet; + CharSet = charSet; } - public CharSet CharSet { get { return _CharSet; } } + public CharSet CharSet { get; } } } diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/DefaultDllImportSearchPathAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/DefaultDllImportSearchPathsAttribute.cs similarity index 76% rename from external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/DefaultDllImportSearchPathAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/DefaultDllImportSearchPathsAttribute.cs index c77baf859d..1ff27fbbd5 100644 --- a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/DefaultDllImportSearchPathAttribute.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/DefaultDllImportSearchPathsAttribute.cs @@ -2,23 +2,16 @@ // 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; - namespace System.Runtime.InteropServices { [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Method, AllowMultiple = false)] public sealed class DefaultDllImportSearchPathsAttribute : Attribute { - internal DllImportSearchPath _paths; public DefaultDllImportSearchPathsAttribute(DllImportSearchPath paths) { - _paths = paths; + Paths = paths; } - public DllImportSearchPath Paths { get { return _paths; } } + public DllImportSearchPath Paths { get; } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/DllImportAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/DllImportAttribute.cs similarity index 74% rename from external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/DllImportAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/DllImportAttribute.cs index 0a0eabbebe..97f870d49c 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/DllImportAttribute.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/DllImportAttribute.cs @@ -4,10 +4,16 @@ namespace System.Runtime.InteropServices { - [AttributeUsage(AttributeTargets.Method)] + [AttributeUsage(AttributeTargets.Method, Inherited = false)] public sealed class DllImportAttribute : Attribute { - private string _dllName; + public DllImportAttribute(string dllName) + { + Value = dllName; + } + + public string Value { get; } + public string EntryPoint; public CharSet CharSet; public bool SetLastError; @@ -16,18 +22,5 @@ namespace System.Runtime.InteropServices public bool BestFitMapping; public bool PreserveSig; public bool ThrowOnUnmappableChar; - - public DllImportAttribute(string dllName) - { - _dllName = dllName; - } - - public string Value - { - get - { - return _dllName; - } - } } } diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/DllImportSearchPath.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/DllImportSearchPath.cs similarity index 95% rename from external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/DllImportSearchPath.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/DllImportSearchPath.cs index a2f98a064c..8dbdb40be9 100644 --- a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/DllImportSearchPath.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/DllImportSearchPath.cs @@ -2,12 +2,6 @@ // 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; - namespace System.Runtime.InteropServices { [Flags] diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/ExternalException.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/ExternalException.cs new file mode 100644 index 0000000000..160fe301e8 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/ExternalException.cs @@ -0,0 +1,89 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: Exception base class for all errors from Interop or Structured +** Exception Handling code. +** +** +=============================================================================*/ + +using System; +using System.Globalization; +using System.Runtime.Serialization; + +namespace System.Runtime.InteropServices +{ + // Base exception for COM Interop errors &; Structured Exception Handler + // exceptions. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class ExternalException : SystemException + { + public ExternalException() + : base(SR.Arg_ExternalException) + { + HResult = HResults.E_FAIL; + } + + public ExternalException(string message) + : base(message) + { + HResult = HResults.E_FAIL; + } + + public ExternalException(string message, Exception inner) + : base(message, inner) + { + HResult = HResults.E_FAIL; + } + + public ExternalException(string message, int errorCode) + : base(message) + { + HResult = errorCode; + } + + protected ExternalException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + public virtual int ErrorCode + { + get + { + return HResult; + } + } + + public override string ToString() + { + string message = Message; + string className = GetType().ToString(); + + string s = className + " (0x" + HResult.ToString("X8", CultureInfo.InvariantCulture) + ")"; + + if (!(String.IsNullOrEmpty(message))) + { + s = s + ": " + message; + } + + Exception innerException = InnerException; + + if (innerException != null) + { + s = s + " ---> " + innerException.ToString(); + } + + if (StackTrace != null) + s += Environment.NewLine + StackTrace; + + return s; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/FieldOffsetAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/FieldOffsetAttribute.cs similarity index 81% rename from external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/FieldOffsetAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/FieldOffsetAttribute.cs index 711d14489e..27e1097749 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/FieldOffsetAttribute.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/FieldOffsetAttribute.cs @@ -7,11 +7,11 @@ namespace System.Runtime.InteropServices [AttributeUsage(AttributeTargets.Field, Inherited = false)] public sealed class FieldOffsetAttribute : Attribute { - private int _val; public FieldOffsetAttribute(int offset) { - _val = offset; + Value = offset; } - public int Value { get { return _val; } } + + public int Value { get; } } } diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/GuidAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/GuidAttribute.cs similarity index 76% rename from external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/GuidAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/GuidAttribute.cs index b6be0decc4..cf60b9bf70 100644 --- a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/GuidAttribute.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/GuidAttribute.cs @@ -2,18 +2,16 @@ // 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; - namespace System.Runtime.InteropServices { [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Struct | AttributeTargets.Delegate, Inherited = false)] public sealed class GuidAttribute : Attribute { - internal String _val; - public GuidAttribute(String guid) + public GuidAttribute(string guid) { - _val = guid; + Value = guid; } - public String Value { get { return _val; } } + + public string Value { get; } } } diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/HandleRef.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/HandleRef.cs similarity index 69% rename from external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/HandleRef.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/HandleRef.cs index 41eb1c24ba..64d0553130 100644 --- a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/HandleRef.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/HandleRef.cs @@ -2,31 +2,27 @@ // 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; - namespace System.Runtime.InteropServices { public struct HandleRef { // ! Do not add or rearrange fields as the EE depends on this layout. //------------------------------------------------------------------ - internal Object m_wrapper; - internal IntPtr m_handle; + private object _wrapper; + private IntPtr _handle; //------------------------------------------------------------------ - - public HandleRef(Object wrapper, IntPtr handle) + public HandleRef(object wrapper, IntPtr handle) { - m_wrapper = wrapper; - m_handle = handle; + _wrapper = wrapper; + _handle = handle; } - public Object Wrapper + public object Wrapper { get { - return m_wrapper; + return _wrapper; } } @@ -34,19 +30,18 @@ namespace System.Runtime.InteropServices { get { - return m_handle; + return _handle; } } - public static explicit operator IntPtr(HandleRef value) { - return value.m_handle; + return value._handle; } public static IntPtr ToIntPtr(HandleRef value) { - return value.m_handle; + return value._handle; } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/InAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/InAttribute.cs similarity index 100% rename from external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/InAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/InAttribute.cs diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/LayoutKind.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/LayoutKind.cs new file mode 100644 index 0000000000..dbd7ec62d5 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/LayoutKind.cs @@ -0,0 +1,14 @@ +// 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. + +namespace System.Runtime.InteropServices +{ + // Used in the StructLayoutAttribute class + public enum LayoutKind + { + Sequential = 0, + Explicit = 2, + Auto = 3, + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/MarshalAsAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/MarshalAsAttribute.cs similarity index 76% rename from external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/MarshalAsAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/MarshalAsAttribute.cs index abc683771a..4a64050ed1 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/MarshalAsAttribute.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/MarshalAsAttribute.cs @@ -2,23 +2,21 @@ // 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; - namespace System.Runtime.InteropServices { [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.ReturnValue, Inherited = false)] - public unsafe sealed class MarshalAsAttribute : Attribute + public sealed class MarshalAsAttribute : Attribute { - internal UnmanagedType _val; public MarshalAsAttribute(UnmanagedType unmanagedType) { - _val = unmanagedType; + Value = unmanagedType; } public MarshalAsAttribute(short unmanagedType) { - _val = (UnmanagedType)unmanagedType; + Value = (UnmanagedType)unmanagedType; } - public UnmanagedType Value { get { return _val; } } + + public UnmanagedType Value { get; } // Fields used with SubType = SafeArray. public VarEnum SafeArraySubType; @@ -34,8 +32,8 @@ namespace System.Runtime.InteropServices public int SizeConst; // constant C // Fields used with SubType = CustomMarshaler - public String MarshalType; // Name of marshaler class + public string MarshalType; // Name of marshaler class public Type MarshalTypeRef; // Type of marshaler class - public String MarshalCookie; // cookie to pass to marshaler + public string MarshalCookie; // cookie to pass to marshaler } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/MarshalDirectiveException.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/MarshalDirectiveException.cs similarity index 80% rename from external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/MarshalDirectiveException.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/MarshalDirectiveException.cs index f1b3b71fa3..1d0d59fab6 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/MarshalDirectiveException.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/MarshalDirectiveException.cs @@ -4,7 +4,6 @@ /*============================================================================= ** -** Class: MarshalDirectiveException ** ** Purpose: This exception is thrown when the marshaller encounters a signature ** that has an invalid MarshalAs CA for a given argument or is not @@ -12,30 +11,32 @@ ** =============================================================================*/ + using System; using System.Runtime.Serialization; namespace System.Runtime.InteropServices { [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class MarshalDirectiveException : SystemException { public MarshalDirectiveException() : base(SR.Arg_MarshalDirectiveException) { - HResult = __HResults.COR_E_MARSHALDIRECTIVE; + HResult = HResults.COR_E_MARSHALDIRECTIVE; } public MarshalDirectiveException(String message) : base(message) { - HResult = __HResults.COR_E_MARSHALDIRECTIVE; + HResult = HResults.COR_E_MARSHALDIRECTIVE; } public MarshalDirectiveException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_MARSHALDIRECTIVE; + HResult = HResults.COR_E_MARSHALDIRECTIVE; } protected MarshalDirectiveException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/MemoryMarshal.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/MemoryMarshal.cs new file mode 100644 index 0000000000..63e301beca --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/MemoryMarshal.cs @@ -0,0 +1,94 @@ +// 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.Buffers; +using System.Runtime.CompilerServices; +#if !FEATURE_PORTABLE_SPAN +using Internal.Runtime.CompilerServices; +#endif // FEATURE_PORTABLE_SPAN + +namespace System.Runtime.InteropServices +{ + /// + /// Provides a collection of methods for interoperating with , , + /// , and . + /// + public static class MemoryMarshal + { + /// Creates a from a . + /// The . + /// A representing the same memory as the , but writable. + /// + /// must be used with extreme caution. is used + /// to represent immutable data and other memory that is not meant to be written to; instances created + /// by should not be written to. The method exists to enable variables typed + /// as but only used for reading to store a . + /// + public static Memory AsMemory(ReadOnlyMemory readOnlyMemory) => + Unsafe.As, Memory>(ref readOnlyMemory); + +#if FEATURE_PORTABLE_SPAN + /// + /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element + /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. + /// + public static ref T GetReference(Span span) + { + if (span.Pinnable == null) + unsafe { return ref Unsafe.AsRef(span.ByteOffset.ToPointer()); } + else + return ref Unsafe.AddByteOffset(ref span.Pinnable.Data, span.ByteOffset); + } + + /// + /// Returns a reference to the 0th element of the ReadOnlySpan. If the Span is empty, returns a reference to the location where the 0th element + /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. + /// + public static ref T GetReference(ReadOnlySpan span) + { + if (span.Pinnable == null) + unsafe { return ref Unsafe.AsRef(span.ByteOffset.ToPointer()); } + else + return ref Unsafe.AddByteOffset(ref span.Pinnable.Data, span.ByteOffset); + } +#else + /// + /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element + /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. + /// + public static ref T GetReference(Span span) => ref span._pointer.Value; + + /// + /// Returns a reference to the 0th element of the ReadOnlySpan. If the Span is empty, returns a reference to the location where the 0th element + /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. + /// + public static ref T GetReference(ReadOnlySpan span) => ref span._pointer.Value; +#endif // FEATURE_PORTABLE_SPAN + + /// + /// Get an array segment from the underlying memory. + /// If unable to get the array segment, return false with a default array segment. + /// + public static bool TryGetArray(ReadOnlyMemory readOnlyMemory, out ArraySegment arraySegment) + { + object obj = readOnlyMemory.GetObjectStartLength(out int index, out int length); + if (index < 0) + { + if (((OwnedMemory)obj).TryGetArray(out var segment)) + { + arraySegment = new ArraySegment(segment.Array, segment.Offset + (index & ReadOnlyMemory.RemoveOwnedFlagBitMask), length); + return true; + } + } + else if (obj is T[] arr) + { + arraySegment = new ArraySegment(arr, index, length); + return true; + } + + arraySegment = default; + return false; + } + } +} diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/OptionalAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/OptionalAttribute.cs similarity index 87% rename from external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/OptionalAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/OptionalAttribute.cs index b404ceb8f3..5ac75d7b3e 100644 --- a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/OptionalAttribute.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/OptionalAttribute.cs @@ -2,12 +2,13 @@ // 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; - namespace System.Runtime.InteropServices { [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] public sealed class OptionalAttribute : Attribute { + public OptionalAttribute() + { + } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/OutAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/OutAttribute.cs similarity index 97% rename from external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/OutAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/OutAttribute.cs index 6aefd0cab4..338ceac91e 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/OutAttribute.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/OutAttribute.cs @@ -2,10 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -// - -// - namespace System.Runtime.InteropServices { [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/PreserveSigAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/PreserveSigAttribute.cs similarity index 86% rename from external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/PreserveSigAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/PreserveSigAttribute.cs index 902fa3780d..464e1abcbe 100644 --- a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/PreserveSigAttribute.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/PreserveSigAttribute.cs @@ -2,12 +2,13 @@ // 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; - namespace System.Runtime.InteropServices { [AttributeUsage(AttributeTargets.Method, Inherited = false)] public sealed class PreserveSigAttribute : Attribute { + public PreserveSigAttribute() + { + } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeBuffer.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/SafeBuffer.cs similarity index 85% rename from external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeBuffer.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/SafeBuffer.cs index db0c5164cb..455e413928 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeBuffer.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/SafeBuffer.cs @@ -59,7 +59,7 @@ // Implementation notes: // *) The Initialize method must be called before you use any instance of -// a SafeBuffer. To avoid races when storing SafeBuffers in statics, +// a SafeBuffer. To avoid race conditions when storing SafeBuffers in statics, // you either need to take a lock when publishing the SafeBuffer, or you // need to create a local, initialize the SafeBuffer, then assign to the // static variable (perhaps using Interlocked.CompareExchange). Of course, @@ -67,9 +67,8 @@ using System; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; -using System.Runtime.Versioning; +using Internal.Runtime.CompilerServices; using Microsoft.Win32.SafeHandles; namespace System.Runtime.InteropServices @@ -87,19 +86,6 @@ namespace System.Runtime.InteropServices _numBytes = Uninitialized; } - // On the desktop CLR, SafeBuffer has access to the internal handle field since they're both in - // mscorlib. For this refactoring, we'll keep the name the same to minimize deltas, but shim - // through to DangerousGetHandle - private new IntPtr handle - { - get { return DangerousGetHandle(); } - } - - public override bool IsInvalid - { - get { return DangerousGetHandle() == IntPtr.Zero || DangerousGetHandle() == new IntPtr(-1); } - } - /// /// Specifies the size of the region of memory, in bytes. Must be /// called before using the SafeBuffer. @@ -108,42 +94,33 @@ namespace System.Runtime.InteropServices [CLSCompliant(false)] public void Initialize(ulong numBytes) { - if (numBytes < 0) - throw new ArgumentOutOfRangeException(nameof(numBytes), SR.ArgumentOutOfRange_NeedNonNegNum); if (IntPtr.Size == 4 && numBytes > UInt32.MaxValue) throw new ArgumentOutOfRangeException(nameof(numBytes), SR.ArgumentOutOfRange_AddressSpace); - Contract.EndContractBlock(); if (numBytes >= (ulong)Uninitialized) - throw new ArgumentOutOfRangeException(nameof(numBytes), SR.ArgumentOutOfRange_UIntPtrMaxMinusOne); + throw new ArgumentOutOfRangeException(nameof(numBytes), SR.ArgumentOutOfRange_UIntPtrMax); _numBytes = (UIntPtr)numBytes; } /// - /// Specifies the the size of the region in memory, as the number of + /// Specifies the size of the region in memory, as the number of /// elements in an array. Must be called before using the SafeBuffer. /// [CLSCompliant(false)] public void Initialize(uint numElements, uint sizeOfEachElement) { - if (numElements < 0) - throw new ArgumentOutOfRangeException(nameof(numElements), SR.ArgumentOutOfRange_NeedNonNegNum); - if (sizeOfEachElement < 0) - throw new ArgumentOutOfRangeException(nameof(sizeOfEachElement), SR.ArgumentOutOfRange_NeedNonNegNum); - if (IntPtr.Size == 4 && numElements * sizeOfEachElement > UInt32.MaxValue) throw new ArgumentOutOfRangeException("numBytes", SR.ArgumentOutOfRange_AddressSpace); - Contract.EndContractBlock(); if (numElements * sizeOfEachElement >= (ulong)Uninitialized) - throw new ArgumentOutOfRangeException(nameof(numElements), SR.ArgumentOutOfRange_UIntPtrMaxMinusOne); + throw new ArgumentOutOfRangeException(nameof(numElements), SR.ArgumentOutOfRange_UIntPtrMax); _numBytes = checked((UIntPtr)(numElements * sizeOfEachElement)); } /// - /// Specifies the the size of the region in memory, as the number of + /// Specifies the size of the region in memory, as the number of /// elements in an array. Must be called before using the SafeBuffer. /// [CLSCompliant(false)] @@ -154,12 +131,11 @@ namespace System.Runtime.InteropServices // Callers should ensure that they check whether the pointer ref param // is null when AcquirePointer returns. If it is not null, they must - // call ReleasePointer in a CER. This method calls DangerousAddRef + // call ReleasePointer. This method calls DangerousAddRef // & exposes the pointer. Unlike Read, it does not alter the "current // position" of the pointer. Here's how to use it: // // byte* pointer = null; - // RuntimeHelpers.PrepareConstrainedRegions(); // try { // safeBuffer.AcquirePointer(ref pointer); // // Use pointer here, with your own bounds checking @@ -175,9 +151,9 @@ namespace System.Runtime.InteropServices /// /// Obtain the pointer from a SafeBuffer for a block of code, /// with the express responsibility for bounds checking and calling - /// ReleasePointer later within a CER to ensure the pointer can be - /// freed later. This method either completes successfully or - /// throws an exception and returns with pointer set to null. + /// ReleasePointer later to ensure the pointer can be freed later. + /// This method either completes successfully or throws an exception + /// and returns with pointer set to null. /// /// A byte*, passed by reference, to receive /// the pointer from within the SafeBuffer. You must set @@ -189,15 +165,10 @@ namespace System.Runtime.InteropServices throw NotInitialized(); pointer = null; - try - { - } - finally - { - bool junk = false; - DangerousAddRef(ref junk); - pointer = (byte*)handle; - } + + bool junk = false; + DangerousAddRef(ref junk); + pointer = (byte*)handle; } public void ReleasePointer() @@ -256,7 +227,6 @@ namespace System.Runtime.InteropServices throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); if (array.Length - index < count) throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.EndContractBlock(); if (_numBytes == Uninitialized) throw NotInitialized(); @@ -336,7 +306,6 @@ namespace System.Runtime.InteropServices throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); if (array.Length - index < count) throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.EndContractBlock(); if (_numBytes == Uninitialized) throw NotInitialized(); @@ -403,13 +372,13 @@ namespace System.Runtime.InteropServices private static InvalidOperationException NotInitialized() { - Debug.Assert(false, "Uninitialized SafeBuffer! Someone needs to call Initialize before using this instance!"); return new InvalidOperationException(SR.InvalidOperation_MustCallInitialize); } - #region "SizeOf Helpers" /// - /// Returns the aligned size of an instance of a value type. + /// Returns the size that SafeBuffer (and hence, UnmanagedMemoryAccessor) reserves in the unmanaged buffer for each element of an array of T. This is not the same + /// value that sizeof(T) returns! Since the primary use case is to parse memory mapped files, we cannot change this algorithm as this defines a de-facto serialization format. + /// Throws if T contains GC references. /// internal static uint AlignedSizeOf() where T : struct { @@ -418,22 +387,19 @@ namespace System.Runtime.InteropServices { return size; } - if (IntPtr.Size == 8 && size == 4) - { - return size; - } return (uint)(((size + 3) & (~3))); } - private static uint SizeOf() where T : struct + /// + /// Returns same value as sizeof(T) but throws if T contains GC references. + /// + internal static uint SizeOf() where T : struct { - RuntimeTypeHandle structureTypeHandle = typeof(T).TypeHandle; - if (!structureTypeHandle.IsBlittable()) + if (RuntimeHelpers.IsReferenceOrContainsReferences()) throw new ArgumentException(SR.Argument_NeedStructWithNoRefs); return (uint)Unsafe.SizeOf(); } - #endregion } } diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/StringBuffer.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/StringBuffer.cs new file mode 100644 index 0000000000..fdd0b95590 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/StringBuffer.cs @@ -0,0 +1,301 @@ +// 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.Buffers; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + /// + /// Buffer that deals in char size increments. Dispose to free memory. Always makes ordinal + /// comparisons. Not thread safe. + /// + /// A more performant replacement for StringBuilder when performing native interop. + /// + /// "No copy" valuetype. Has to be passed as "ref". + /// + /// + /// + /// Suggested use through P/Invoke: define DllImport arguments that take a character buffer as SafeHandle and pass StringBuffer.GetHandle(). + /// + internal struct StringBuffer + { + private char[] _buffer; + private int _length; + + /// + /// Instantiate the buffer with capacity for at least the specified number of characters. Capacity + /// includes the trailing null character. + /// + public StringBuffer(int initialCapacity) + { + _buffer = ArrayPool.Shared.Rent(initialCapacity); + _length = 0; + } + + /// + /// Get/set the character at the given index. + /// + /// Thrown if attempting to index outside of the buffer length. + public char this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + if (index >= _length) throw new ArgumentOutOfRangeException(nameof(index)); + return _buffer[index]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + if (index >= _length) throw new ArgumentOutOfRangeException(nameof(index)); + _buffer[index] = value; + } + } + + /// + /// Underlying storage of the buffer. Used for interop. + /// + public char[] UnderlyingArray => _buffer; + + /// + /// Character capacity of the buffer. Includes the count for the trailing null character. + /// + public int Capacity => _buffer.Length; + + /// + /// Ensure capacity in characters is at least the given minimum. + /// + /// Thrown if unable to allocate memory when setting. + public void EnsureCapacity(int minCapacity) + { + if (minCapacity > Capacity) + { + char[] oldBuffer = _buffer; + _buffer = ArrayPool.Shared.Rent(minCapacity); + Array.Copy(oldBuffer, 0, _buffer, 0, oldBuffer.Length); + ArrayPool.Shared.Return(oldBuffer); + } + } + + /// + /// The logical length of the buffer in characters. (Does not include the final null.) Will automatically attempt to increase capacity. + /// This is where the usable data ends. + /// + /// Thrown if unable to allocate memory when setting. + /// Thrown if the set size in bytes is int.MaxValue (as space is implicitly reserved for the trailing null). + public int Length + { + get { return _length; } + set + { + // Null terminate + EnsureCapacity(checked(value + 1)); + _buffer[value] = '\0'; + + _length = value; + } + } + + /// + /// True if the buffer contains the given character. + /// + public unsafe bool Contains(char value) + { + fixed (char* start = _buffer) + { + int length = _length; + for (int i = 0; i < length; i++) + { + if (start[i] == value) return true; + } + } + + return false; + } + + /// + /// Returns true if the buffer starts with the given string. + /// + public bool StartsWith(string value) + { + if (value == null) throw new ArgumentNullException(nameof(value)); + if (_length < value.Length) return false; + return SubstringEquals(value, startIndex: 0, count: value.Length); + } + + /// + /// Returns true if the specified StringBuffer substring equals the given value. + /// + /// The value to compare against the specified substring. + /// Start index of the sub string. + /// Length of the substring, or -1 to check all remaining. + /// + /// Thrown if or are outside the range + /// of the buffer's length. + /// + public unsafe bool SubstringEquals(string value, int startIndex = 0, int count = -1) + { + if (value == null) return false; + if (count < -1) throw new ArgumentOutOfRangeException(nameof(count)); + if (startIndex > _length) throw new ArgumentOutOfRangeException(nameof(startIndex)); + + int realCount = count == -1 ? _length - startIndex : (int)count; + if (checked(startIndex + realCount) > _length) throw new ArgumentOutOfRangeException(nameof(count)); + + int length = value.Length; + + // Check the substring length against the input length + if (realCount != length) return false; + + fixed (char* valueStart = value) + fixed (char* bufferStart = _buffer) + { + char* subStringStart = bufferStart + startIndex; + + for (int i = 0; i < length; i++) + { + if (subStringStart[i] != valueStart[i]) return false; + } + } + + return true; + } + + /// + /// Append the given buffer. + /// + /// The buffer to append. + /// The index in the input buffer to start appending from. + /// The count of characters to copy from the buffer string. + /// Thrown if is null. + /// + /// Thrown if or are outside the range + /// of characters. + /// + public void Append(ref StringBuffer value, int startIndex = 0) + { + if (value.Length == 0) return; + + value.CopyTo( + bufferIndex: startIndex, + destination: ref this, + destinationIndex: _length, + count: value.Length); + } + + /// + /// Append the given buffer. + /// + /// The buffer to append. + /// The index in the input buffer to start appending from. + /// The count of characters to copy from the buffer string. + /// Thrown if is null. + /// + /// Thrown if or are outside the range + /// of characters. + /// + public void Append(ref StringBuffer value, int startIndex, int count) + { + if (count == 0) return; + + value.CopyTo( + bufferIndex: startIndex, + destination: ref this, + destinationIndex: _length, + count: count); + } + + /// + /// Copy contents to the specified buffer. Destination index must be within current destination length. + /// Will grow the destination buffer if needed. + /// + /// + /// Thrown if or or are outside the range + /// of characters. + /// + /// Thrown if is null. + public void CopyTo(int bufferIndex, ref StringBuffer destination, int destinationIndex, int count) + { + if (destinationIndex > destination._length) throw new ArgumentOutOfRangeException(nameof(destinationIndex)); + if (bufferIndex >= _length) throw new ArgumentOutOfRangeException(nameof(bufferIndex)); + if (_length < checked(bufferIndex + count)) throw new ArgumentOutOfRangeException(nameof(count)); + + if (count == 0) return; + int lastIndex = checked(destinationIndex + count); + if (destination.Length < lastIndex) destination.Length = lastIndex; + + Array.Copy(UnderlyingArray, bufferIndex, destination.UnderlyingArray, destinationIndex, count); + } + + /// + /// Copy contents from the specified string into the buffer at the given index. Start index must be within the current length of + /// the buffer, will grow as necessary. + /// + public void CopyFrom(int bufferIndex, string source, int sourceIndex = 0, int count = -1) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (bufferIndex > _length) throw new ArgumentOutOfRangeException(nameof(bufferIndex)); + if (sourceIndex < 0 || sourceIndex > source.Length) throw new ArgumentOutOfRangeException(nameof(sourceIndex)); + if (count == -1) count = source.Length - sourceIndex; + if (count < 0 || source.Length - count < sourceIndex) throw new ArgumentOutOfRangeException(nameof(count)); + + if (count == 0) return; + int lastIndex = bufferIndex + (int)count; + if (_length < lastIndex) Length = lastIndex; + + source.CopyTo(sourceIndex, UnderlyingArray, bufferIndex, count); + } + + /// + /// Trim the specified values from the end of the buffer. If nothing is specified, nothing is trimmed. + /// + public void TrimEnd(char[] values) + { + if (values == null || values.Length == 0 || _length == 0) return; + + while (_length > 0 && Array.IndexOf(values, _buffer[_length - 1]) >= 0) + { + Length = _length - 1; + } + } + + /// + /// String representation of the entire buffer. If the buffer is larger than the maximum size string (int.MaxValue) this will throw. + /// + /// Thrown if the buffer is too big to fit into a string. + public override string ToString() + { + return new string(_buffer, startIndex: 0, length: _length); + } + + /// + /// Get the given substring in the buffer. + /// + /// Count of characters to take, or remaining characters from if -1. + /// + /// Thrown if or are outside the range of the buffer's length + /// or count is greater than the maximum string size (int.MaxValue). + /// + public string Substring(int startIndex, int count = -1) + { + if (startIndex > (_length == 0 ? 0 : _length - 1)) throw new ArgumentOutOfRangeException(nameof(startIndex)); + if (count < -1) throw new ArgumentOutOfRangeException(nameof(count)); + + int realCount = count == -1 ? _length - startIndex : (int)count; + if (realCount > int.MaxValue || checked(startIndex + realCount) > _length) throw new ArgumentOutOfRangeException(nameof(count)); + + // The buffer could be bigger than will fit into a string, but the substring might fit. As the starting + // index might be bigger than int we need to index ourselves. + return new string(_buffer, startIndex: startIndex, length: realCount); + } + + public void Free() + { + ArrayPool.Shared.Return(_buffer); + _buffer = null; + _length = 0; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/StructLayoutAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/StructLayoutAttribute.cs similarity index 78% rename from external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/StructLayoutAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/StructLayoutAttribute.cs index 25e074a9c4..c4cce9956e 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/StructLayoutAttribute.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/StructLayoutAttribute.cs @@ -2,28 +2,23 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -// - -// - namespace System.Runtime.InteropServices { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)] public sealed class StructLayoutAttribute : Attribute { - internal LayoutKind _val; - public StructLayoutAttribute(LayoutKind layoutKind) { - _val = layoutKind; + Value = layoutKind; } public StructLayoutAttribute(short layoutKind) { - _val = (LayoutKind)layoutKind; + Value = (LayoutKind)layoutKind; } - public LayoutKind Value { get { return _val; } } + public LayoutKind Value { get; } + public int Pack; public int Size; public CharSet CharSet; diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/UnmanagedFunctionPointerAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/UnmanagedFunctionPointerAttribute.cs new file mode 100644 index 0000000000..c4f96903ee --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/UnmanagedFunctionPointerAttribute.cs @@ -0,0 +1,27 @@ +// 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. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] + public sealed class UnmanagedFunctionPointerAttribute : Attribute + { + public UnmanagedFunctionPointerAttribute() + { + CallingConvention = CallingConvention.Winapi; + } + + public UnmanagedFunctionPointerAttribute(CallingConvention callingConvention) + { + CallingConvention = callingConvention; + } + + public CallingConvention CallingConvention { get; } + + public bool BestFitMapping; + public bool SetLastError; + public bool ThrowOnUnmappableChar; + public CharSet CharSet; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/UnmanagedType.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/UnmanagedType.cs new file mode 100644 index 0000000000..4deca7fe0c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/UnmanagedType.cs @@ -0,0 +1,48 @@ +// 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. + +namespace System.Runtime.InteropServices +{ + public enum UnmanagedType + { + Bool = 0x2, // 4 byte boolean value (true != 0, false == 0) + I1 = 0x3, // 1 byte signed value + U1 = 0x4, // 1 byte unsigned value + I2 = 0x5, // 2 byte signed value + U2 = 0x6, // 2 byte unsigned value + I4 = 0x7, // 4 byte signed value + U4 = 0x8, // 4 byte unsigned value + I8 = 0x9, // 8 byte signed value + U8 = 0xa, // 8 byte unsigned value + R4 = 0xb, // 4 byte floating point + R8 = 0xc, // 8 byte floating point + Currency = 0xf, // A currency + BStr = 0x13, // OLE Unicode BSTR + LPStr = 0x14, // Ptr to SBCS string + LPWStr = 0x15, // Ptr to Unicode string + LPTStr = 0x16, // Ptr to OS preferred (SBCS/Unicode) string + ByValTStr = 0x17, // OS preferred (SBCS/Unicode) inline string (only valid in structs) + IUnknown = 0x19, // COM IUnknown pointer. + IDispatch = 0x1a, // COM IDispatch pointer + Struct = 0x1b, // Structure + Interface = 0x1c, // COM interface + SafeArray = 0x1d, // OLE SafeArray + ByValArray = 0x1e, // Array of fixed size (only valid in structs) + SysInt = 0x1f, // Hardware natural sized signed integer + SysUInt = 0x20, + VBByRefStr = 0x22, + AnsiBStr = 0x23, // OLE BSTR containing SBCS characters + TBStr = 0x24, // Ptr to OS preferred (SBCS/Unicode) BSTR + VariantBool = 0x25, // OLE defined BOOLEAN (2 bytes, true == -1, false == 0) + FunctionPtr = 0x26, // Function pointer + AsAny = 0x28, // Paired with Object type and does runtime marshalling determination + LPArray = 0x2a, // C style array + LPStruct = 0x2b, // Pointer to a structure + CustomMarshaler = 0x2c, + Error = 0x2d, + IInspectable = 0x2e, + HString = 0x2f, // Windows Runtime HSTRING + LPUTF8Str = 0x30, // UTF8 string + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/VarEnum.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/VarEnum.cs new file mode 100644 index 0000000000..495aeca6d1 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/VarEnum.cs @@ -0,0 +1,54 @@ +// 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. + +namespace System.Runtime.InteropServices +{ + public enum VarEnum + { + VT_EMPTY = 0, + VT_NULL = 1, + VT_I2 = 2, + VT_I4 = 3, + VT_R4 = 4, + VT_R8 = 5, + VT_CY = 6, + VT_DATE = 7, + VT_BSTR = 8, + VT_DISPATCH = 9, + VT_ERROR = 10, + VT_BOOL = 11, + VT_VARIANT = 12, + VT_UNKNOWN = 13, + VT_DECIMAL = 14, + VT_I1 = 16, + VT_UI1 = 17, + VT_UI2 = 18, + VT_UI4 = 19, + VT_I8 = 20, + VT_UI8 = 21, + VT_INT = 22, + VT_UINT = 23, + VT_VOID = 24, + VT_HRESULT = 25, + VT_PTR = 26, + VT_SAFEARRAY = 27, + VT_CARRAY = 28, + VT_USERDEFINED = 29, + VT_LPSTR = 30, + VT_LPWSTR = 31, + VT_RECORD = 36, + VT_FILETIME = 64, + VT_BLOB = 65, + VT_STREAM = 66, + VT_STORAGE = 67, + VT_STREAMED_OBJECT = 68, + VT_STORED_OBJECT = 69, + VT_BLOB_OBJECT = 70, + VT_CF = 71, + VT_CLSID = 72, + VT_VECTOR = 0x1000, + VT_ARRAY = 0x2000, + VT_BYREF = 0x4000 + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/IDeserializationCallback.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/IDeserializationCallback.cs new file mode 100644 index 0000000000..a1c1671a8b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/IDeserializationCallback.cs @@ -0,0 +1,11 @@ +// 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. + +namespace System.Runtime.Serialization +{ + public interface IDeserializationCallback + { + void OnDeserialization(object sender); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/IFormatterConverter.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/IFormatterConverter.cs new file mode 100644 index 0000000000..c173144854 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/IFormatterConverter.cs @@ -0,0 +1,28 @@ +// 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. + +namespace System.Runtime.Serialization +{ + [CLSCompliant(false)] + public interface IFormatterConverter + { + object Convert(object value, Type type); + object Convert(object value, TypeCode typeCode); + bool ToBoolean(object value); + char ToChar(object value); + sbyte ToSByte(object value); + byte ToByte(object value); + short ToInt16(object value); + ushort ToUInt16(object value); + int ToInt32(object value); + uint ToUInt32(object value); + long ToInt64(object value); + ulong ToUInt64(object value); + float ToSingle(object value); + double ToDouble(object value); + decimal ToDecimal(object value); + DateTime ToDateTime(object value); + string ToString(object value); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/IObjectReference.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/IObjectReference.cs new file mode 100644 index 0000000000..d41bc50dde --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/IObjectReference.cs @@ -0,0 +1,11 @@ +// 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. + +namespace System.Runtime.Serialization +{ + public interface IObjectReference + { + object GetRealObject(StreamingContext context); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/ISafeSerializationData.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/ISafeSerializationData.cs new file mode 100644 index 0000000000..5089d134c3 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/ISafeSerializationData.cs @@ -0,0 +1,207 @@ +// 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. + +namespace System.Runtime.Serialization +{ + // + // #SafeSerialization + // + // Types which are serializable via the ISerializable interface have a problem when it comes to allowing + // transparent subtypes which can allow themselves to serialize since the GetObjectData method is + // SecurityCritical. + // + // For instance, System.Exception implements ISerializable, however it is also desirable to have + // transparent exceptions with their own fields that need to be serialized. (For instance, in transparent + // assemblies such as the DLR and F#, or even in partial trust application code). Since overriding + // GetObjectData requires that the overriding method be security critical, this won't work directly. + // + // SafeSerializationManager solves this problem by allowing any partial trust code to contribute + // individual chunks of serializable data to be included in the serialized version of the derived class. + // These chunks are then deserialized back out of the serialized type and notified that they should + // populate the fields of the deserialized object when serialization is complete. This allows partial + // trust or transparent code to participate in serialization of an ISerializable type without having to + // override GetObjectData or implement the ISerializable constructor. + // + // On the serialization side, SafeSerializationManager has an event SerializeObjectState which it will + // fire in response to serialization in order to gather the units of serializable data that should be + // stored with the rest of the object during serialization. Methods which respond to these events + // create serializable objects which implement the ISafeSerializationData interface and add them to the + // collection of other serialized data by calling AddSerializedState on the SafeSerializationEventArgs + // passed into the event. + // + // By using an event rather than a virtual method on the base ISerializable object, we allow multiple + // potentially untrusted subclasses to participate in serialization, without each one having to ensure + // that it calls up to the base type in order for the whole system to work. (For instance Exception : + // TrustedException : UntrustedException, in this scenario UntrustedException would be able to override + // the virtual method an prevent TrustedException from ever seeing the method call, either accidentally + // or maliciously). + // + // Further, by only allowing additions of new chunks of serialization state rather than exposing the + // whole underlying list, we avoid exposing potentially sensitive serialized state to any of the + // potentially untrusted subclasses. + // + // At deserialization time, SafeSerializationManager performs the reverse operation. It deserializes the + // chunks of serialized state, and then notifies them that the object they belong to is deserialized by + // calling their CompleteSerialization method. In repsonse to this call, the state objects populate the + // fields of the object being deserialized with the state that they held. + // + // From a security perspective, the chunks of serialized state can only contain data that the specific + // subclass itself had access to read (otherwise it wouldn't be able to populate the type with that + // data), as opposed to having access to far more data in the SerializationInfo that GetObjectData uses. + // Similarly, at deserialization time, the serialized state can only modify fields that the type itself + // has access to (again, as opposed to the full SerializationInfo which could be modified). + // + // Individual types which wish to participate in safe serialization do so by containing an instance of a + // SafeSerializationManager and exposing its serialization event. During GetObjectData, the + // SafeSerializationManager is serialized just like any other field of the containing type. However, at + // the end of serialization it is called back one last time to CompleteSerialization. + // + // In CompleteSerialization, if the SafeSerializationManager detects that it has extra chunks of + // data to handle, it substitutes the root type being serialized (formerly the real type hosting the + // SafeSerializationManager) with itself. This allows it to gain more control over the deserialization + // process. It also saves away an extra bit of state in the serialization info indicating the real type + // of object that should be recreated during deserialization. + // + // At this point the serialized state looks like this: + // Data: + // realSerializedData1 + // ... + // realSerializedDataN + // safeSerializationData -> this is the serialization data member of the parent type + // _serializedState -> list of saved serialized states from subclasses responding to the safe + // serialization event + // RealTypeSerializationName -> type which is using safe serialization + // Type: + // SafeSerializationManager + // + // That is, the serialized data claims to be of type SafeSerializationManager, however contains only the + // data from the real object being serialized along with one bit of safe serialization metadata. + // + // At deserialization time, since the serialized data claims to be of type SafeSerializationManager, the + // root object being created is an instance of the SafeSerializationManager class. However, it detects + // that this isn't a real SafeSerializationManager (by looking for the real type field in the metadata), + // and simply saves away the SerializationInfo and the real type being deserialized. + // + // Since SafeSerializationManager implements IObjectReference, the next step of deserialization is the + // GetRealObject callback. This callback is the one responsible for getting the + // SafeSerializationManager out of the way and instead creating an instance of the actual type which was + // serialized. + // + // It does this by first creating an instance of the real type being deserialzed (saved away in the + // deserialzation constructor), but not running any of its constructors. Instead, it walks the + // inheritance hierarchy (moving toward the most derived type) looking for the last full trust type to + // implement the standard ISerializable constructor before any type does not implement the constructor. + // It is this last type's deserialization constructor which is then invoked, passing in the saved + // SerializationInfo. Once the constructors are run, we return this object as the real deserialized + // object. + // + // The reason that we do this walk is so that ISerializable types can protect themselves from malicious + // input during deserialization by making their deserialization constructors unavailable to partial + // trust code. By not requiring every type have a copy of this constructor, partial trust code can + // participate in safe serialization and not be required to have access to the parent's constructor. + // + // It should be noted however, that this heuristic means that if a full trust type does derive from + // a transparent or partial trust type using this safe serialization mechanism, that full trust type + // will not have its constructor called. Further, the protection of not invoking partial trust + // deserialization constructors only comes into play if SafeSerializationManager is in control of + // deserialization, which means there must be at least one (even empty) safe serialization event + // handler registered. + // + // Another interesting note is that at this point there are now two SafeSerializationManagers alive for + // this deserialization. The first object is the one which is controlling the deserialization and was + // created as the root object of the deserialization. The second one is the object which contains the + // serialized data chunks and is a data member of the real object being deserialized. For this reason, + // the data objects cannot be notified that the deserialization is complete during GetRealObject since + // the ISafeSerializationData objects are not members of the active SafeSerializationManager instance. + // + // The next step is the OnDeserialized callback, which comes to SafeSerializableObject since it was + // pretending to be the root object of the deserialization. It responds to this callback by calling + // any existing OnDeserialized callback on the real type that was deserialized. + // + // The real type needs to call its data member SafeSerializationData object's CompleteDeserialization + // method in response to the OnDeserialized call. This CompleteDeserialization call will then iterate + // through the ISafeSerializationData objects calling each of their CompleteDeserialization methods so + // that they can plug the nearly-complete object with their saved data. + // + // The reason for having a new ISafeSerializationData interface which is basically identical to + // IDeserializationCallback is that IDeserializationCallback will be called on the stored data chunks + // by the serialization code when they are deserialized, and that's not a desirable behavior. + // Essentially, we need to change the meaning of the object parameter to mean "parent object which + // participated in safe serialization", rather than "this object". + // + // Implementing safe serialization on an ISerialiable type is relatively straight forward. (For an + // example, see System.Exception): + // + // 1. Include a data member of type SafeSerializationManager: + // + // private SafeSerializationManager _safeSerializationManager; + // + // 2. Add a protected SerializeObjectState event, which passes through to the SafeSerializationManager: + // + // protected event EventHandler SerializeObjectState + // { + // add { _safeSerializationManager.SerializeObjectState += value; } + // remove { _safeSerializationManager.SerializeObjectState -= value; } + // } + // + // 3. Serialize the safe serialization object in GetObjectData, and call its CompleteSerialization method: + // + // { + // info.AddValue("_safeSerializationManager", _safeSerializationManager, typeof(SafeSerializationManager)); + // _safeSerializationManager.CompleteSerialization(this, info, context); + // } + // + // 4. Add an OnDeserialized handler if one doesn't already exist, and call CompleteDeserialization in it: + // + // [OnDeserialized] + // private void OnDeserialized(StreamingContext context) + // { + // _safeSerializationManager.CompleteDeserialization(this); + // } + // + // On the client side, using safe serialization is also pretty easy. For example: + // + // [Serializable] + // public class TransparentException : Exception + // { + // [Serializable] + // private struct TransparentExceptionState : ISafeSerializationData + // { + // public string _extraData; + // + // void ISafeSerializationData.CompleteDeserialization(object obj) + // { + // TransparentException exception = obj as TransparentException; + // exception._state = this; + // } + // } + // + // [NonSerialized] + // private TransparentExceptionState _state = new TransparentExceptionState(); + // + // public TransparentException() + // { + // SerializeObjectState += delegate(object exception, SafeSerializationEventArgs eventArgs) + // { + // eventArgs.AddSerializedState(_state); + // }; + // } + // + // public string ExtraData + // { + // get { return _state._extraData; } + // set { _state._extraData = value; } + // } + // } + // + + // Interface to be supported by objects which are stored in safe serialization stores + public interface ISafeSerializationData + { + // CompleteDeserialization is called when the object to which the extra serialized data was attached + // has completed its deserialization, and now needs to be populated with the extra data stored in + // this object. + void CompleteDeserialization(object deserialized); + } +} diff --git a/external/corefx/src/System.Data.Odbc/src/Common/System/Data/Common/ExternDll.Linux.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/ISerializable.cs similarity index 59% rename from external/corefx/src/System.Data.Odbc/src/Common/System/Data/Common/ExternDll.Linux.cs rename to external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/ISerializable.cs index 2351f0f1a9..383b3f07af 100644 --- a/external/corefx/src/System.Data.Odbc/src/Common/System/Data/Common/ExternDll.Linux.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/ISerializable.cs @@ -2,11 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace System.Data.Common +namespace System.Runtime.Serialization { - internal static class ExternDll + public interface ISerializable { - public const string Odbc32 = "libodbc.so.2"; + void GetObjectData(SerializationInfo info, StreamingContext context); } } - diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/OnDeserializedAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/OnDeserializedAttribute.cs new file mode 100644 index 0000000000..408a55ccf9 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/OnDeserializedAttribute.cs @@ -0,0 +1,11 @@ +// 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. + +namespace System.Runtime.Serialization +{ + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + public sealed class OnDeserializedAttribute : Attribute + { + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/OnDeserializingAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/OnDeserializingAttribute.cs new file mode 100644 index 0000000000..162857e8d3 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/OnDeserializingAttribute.cs @@ -0,0 +1,11 @@ +// 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. + +namespace System.Runtime.Serialization +{ + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + public sealed class OnDeserializingAttribute : Attribute + { + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/OnSerializedAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/OnSerializedAttribute.cs new file mode 100644 index 0000000000..020dd0257c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/OnSerializedAttribute.cs @@ -0,0 +1,11 @@ +// 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. + +namespace System.Runtime.Serialization +{ + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + public sealed class OnSerializedAttribute : Attribute + { + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/OnSerializingAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/OnSerializingAttribute.cs new file mode 100644 index 0000000000..8dc8af3f23 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/OnSerializingAttribute.cs @@ -0,0 +1,11 @@ +// 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. + +namespace System.Runtime.Serialization +{ + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + public sealed class OnSerializingAttribute : Attribute + { + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/OptionalFieldAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/OptionalFieldAttribute.cs new file mode 100644 index 0000000000..84daa539be --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/OptionalFieldAttribute.cs @@ -0,0 +1,25 @@ +// 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. + +namespace System.Runtime.Serialization +{ + [AttributeUsage(AttributeTargets.Field, Inherited = false)] + public sealed class OptionalFieldAttribute : Attribute + { + private int _versionAdded = 1; + + public int VersionAdded + { + get { return _versionAdded; } + set + { + if (value < 1) + { + throw new ArgumentException(SR.Serialization_OptionalFieldVersionValue); + } + _versionAdded = value; + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/SafeSerializationEventArgs.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/SafeSerializationEventArgs.cs new file mode 100644 index 0000000000..896b91fca0 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/SafeSerializationEventArgs.cs @@ -0,0 +1,31 @@ +// 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.Collections.Generic; + +namespace System.Runtime.Serialization +{ + // SafeSerializationEventArgs are provided to the delegates which do safe serialization. Each delegate + // serializes its own state into an IDeserializationCallback instance which must, itself, be serializable. + // These indivdiual states are then added to the SafeSerializationEventArgs in order to be saved away when + // the original ISerializable type is serialized. + public sealed class SafeSerializationEventArgs : EventArgs + { + private readonly List _serializedStates = new List(); + + internal SafeSerializationEventArgs() { } + + public void AddSerializedState(ISafeSerializationData serializedState) + { + if (serializedState == null) + throw new ArgumentNullException(nameof(serializedState)); + if (!serializedState.GetType().IsSerializable) + throw new ArgumentException(SR.Format(SR.Serialization_NonSerType, serializedState.GetType(), serializedState.GetType().Assembly.FullName)); + + _serializedStates.Add(serializedState); + } + + public StreamingContext StreamingContext { get; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/SerializationException.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/SerializationException.cs new file mode 100644 index 0000000000..1c9c21eabb --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/SerializationException.cs @@ -0,0 +1,40 @@ +// 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.Runtime.Serialization; + +namespace System.Runtime.Serialization +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class SerializationException : SystemException + { + private static String s_nullMessage = SR.SerializationException; + + // Creates a new SerializationException with its message + // string set to a default message. + public SerializationException() + : base(s_nullMessage) + { + HResult = HResults.COR_E_SERIALIZATION; + } + + public SerializationException(String message) + : base(message) + { + HResult = HResults.COR_E_SERIALIZATION; + } + + public SerializationException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_SERIALIZATION; + } + + protected SerializationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/SerializationInfoEnumerator.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/SerializationInfoEnumerator.cs new file mode 100644 index 0000000000..6399510736 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/SerializationInfoEnumerator.cs @@ -0,0 +1,127 @@ +// 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.Collections; +using System.Diagnostics; + +namespace System.Runtime.Serialization +{ + public struct SerializationEntry + { + private string _name; + private object _value; + private Type _type; + + internal SerializationEntry(string entryName, object entryValue, Type entryType) + { + _name = entryName; + _value = entryValue; + _type = entryType; + } + + public object Value => _value; + public string Name => _name; + public Type ObjectType => _type; + } + + public sealed class SerializationInfoEnumerator : IEnumerator + { + private readonly string[] _members; + private readonly object[] _data; + private readonly Type[] _types; + private readonly int _numItems; + private int _currItem; + private bool _current; + + internal SerializationInfoEnumerator(string[] members, object[] info, Type[] types, int numItems) + { + Debug.Assert(members != null, "[SerializationInfoEnumerator.ctor]members!=null"); + Debug.Assert(info != null, "[SerializationInfoEnumerator.ctor]info!=null"); + Debug.Assert(types != null, "[SerializationInfoEnumerator.ctor]types!=null"); + Debug.Assert(numItems >= 0, "[SerializationInfoEnumerator.ctor]numItems>=0"); + Debug.Assert(members.Length >= numItems, "[SerializationInfoEnumerator.ctor]members.Length>=numItems"); + Debug.Assert(info.Length >= numItems, "[SerializationInfoEnumerator.ctor]info.Length>=numItems"); + Debug.Assert(types.Length >= numItems, "[SerializationInfoEnumerator.ctor]types.Length>=numItems"); + + _members = members; + _data = info; + _types = types; + + //The MoveNext semantic is much easier if we enforce that [0..m_numItems] are valid entries + //in the enumerator, hence we subtract 1. + _numItems = numItems - 1; + _currItem = -1; + _current = false; + } + + public bool MoveNext() + { + if (_currItem < _numItems) + { + _currItem++; + _current = true; + } + else + { + _current = false; + } + + return _current; + } + + object IEnumerator.Current => Current; + + public SerializationEntry Current + { + get + { + if (_current == false) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + return new SerializationEntry(_members[_currItem], _data[_currItem], _types[_currItem]); + } + } + + public void Reset() + { + _currItem = -1; + _current = false; + } + + public string Name + { + get + { + if (_current == false) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + return _members[_currItem]; + } + } + public object Value + { + get + { + if (_current == false) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + return _data[_currItem]; + } + } + public Type ObjectType + { + get + { + if (_current == false) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + return _types[_currItem]; + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/StreamingContext.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/StreamingContext.cs new file mode 100644 index 0000000000..cdcb1c335b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/Serialization/StreamingContext.cs @@ -0,0 +1,52 @@ +// 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. + +namespace System.Runtime.Serialization +{ + public readonly struct StreamingContext + { + private readonly object _additionalContext; + private readonly StreamingContextStates _state; + + public StreamingContext(StreamingContextStates state) : this(state, null) + { + } + + public StreamingContext(StreamingContextStates state, object additional) + { + _state = state; + _additionalContext = additional; + } + + public override bool Equals(object obj) + { + if (!(obj is StreamingContext)) + { + return false; + } + StreamingContext ctx = (StreamingContext)obj; + return ctx._additionalContext == _additionalContext && ctx._state == _state; + } + + public override int GetHashCode() => (int)_state; + + public StreamingContextStates State => _state; + + public object Context => _additionalContext; + } + + [Flags] + public enum StreamingContextStates + { + CrossProcess = 0x01, + CrossMachine = 0x02, + File = 0x04, + Persistence = 0x08, + Remoting = 0x10, + Other = 0x20, + Clone = 0x40, + CrossAppDomain = 0x80, + All = 0xFF, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/Versioning/NonVersionableAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/Versioning/NonVersionableAttribute.cs new file mode 100644 index 0000000000..e4809953bc --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/Versioning/NonVersionableAttribute.cs @@ -0,0 +1,36 @@ +// 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. + +/*============================================================ +** +** +** +** The [NonVersionable] attribute is applied to indicate that the implementation +** of a particular member or layout of a struct cannot be changed for given platform in incompatible way. +** This allows cross-module inlining of methods and data structures whose implementation +** is never changed in ReadyToRun native images. Any changes to such members or types would be +** breaking changes for ReadyToRun. +** +** Applying this type also has the side effect that the inlining tables in R2R images will not +** report that inlining of NonVersionable attributed methods occured. These inlining tables are used +** by profilers to figure out the set of methods that need to be rejited when one method is instrumented, +** so in effect NonVersionable methods are also non-instrumentable. Generally this is OK for +** extremely trivial low level methods where NonVersionable gets used, but if there is any plan to +** significantly extend its usage or allow 3rd parties to use it please discuss with the diagnostics team. +===========================================================*/ + +using System; +using System.Diagnostics; + +namespace System.Runtime.Versioning +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor, + AllowMultiple = false, Inherited = false)] + internal sealed class NonVersionableAttribute : Attribute + { + public NonVersionableAttribute() + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Runtime/Versioning/TargetFrameworkAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Runtime/Versioning/TargetFrameworkAttribute.cs new file mode 100644 index 0000000000..a819066382 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Runtime/Versioning/TargetFrameworkAttribute.cs @@ -0,0 +1,46 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: Identifies which SKU and version of the .NET +** Framework that a particular library was compiled against. +** Emitted by VS, and can help catch deployment problems. +** +===========================================================*/ + +using System; + +namespace System.Runtime.Versioning +{ + [AttributeUsageAttribute(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)] + public sealed class TargetFrameworkAttribute : Attribute + { + private String _frameworkName; // A target framework moniker + private String _frameworkDisplayName; + + // The frameworkName parameter is intended to be the string form of a FrameworkName instance. + public TargetFrameworkAttribute(String frameworkName) + { + if (frameworkName == null) + throw new ArgumentNullException(nameof(frameworkName)); + _frameworkName = frameworkName; + } + + // The target framework moniker that this assembly was compiled against. + // Use the FrameworkName class to interpret target framework monikers. + public String FrameworkName + { + get { return _frameworkName; } + } + + public String FrameworkDisplayName + { + get { return _frameworkDisplayName; } + set { _frameworkDisplayName = value; } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/SByte.cs b/external/corefx/src/Common/src/CoreLib/System/SByte.cs similarity index 52% rename from external/corert/src/System.Private.CoreLib/src/System/SByte.cs rename to external/corefx/src/Common/src/CoreLib/System/SByte.cs index 4f8dbc81ae..c7cee2adc2 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/SByte.cs +++ b/external/corefx/src/Common/src/CoreLib/System/SByte.cs @@ -3,17 +3,18 @@ // See the LICENSE file in the project root for more information. using System.Globalization; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; -using System.Diagnostics.Contracts; namespace System { - // A place holder class for signed bytes. - [CLSCompliant(false), System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)] - public struct SByte : IComparable, IFormattable, IComparable, IEquatable, IConvertible + [Serializable] + [CLSCompliant(false)] [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct SByte : IComparable, IConvertible, IFormattable, IComparable, IEquatable, ISpanFormattable { - private sbyte _value; + private sbyte m_value; // Do not rename (binary serialization) // The maximum value that a Byte may represent: 127. public const sbyte MaxValue = (sbyte)0x7F; @@ -38,12 +39,12 @@ namespace System { throw new ArgumentException(SR.Arg_MustBeSByte); } - return _value - ((SByte)obj)._value; + return m_value - ((SByte)obj).m_value; } public int CompareTo(SByte value) { - return _value - value; + return m_value - value; } // Determines whether two Byte objects are equal. @@ -53,71 +54,78 @@ namespace System { return false; } - return _value == ((SByte)obj)._value; + return m_value == ((SByte)obj).m_value; } [NonVersionable] public bool Equals(SByte obj) { - return _value == obj; + return m_value == obj; } // Gets a hash code for this instance. public override int GetHashCode() { - return ((int)_value ^ (int)_value << 8); + return ((int)m_value ^ (int)m_value << 8); } // Provides a string representation of a byte. public override String ToString() { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatInt32(_value, null, null); + return Number.FormatInt32(m_value, null, null); } public String ToString(IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatInt32(_value, null, provider); + return Number.FormatInt32(m_value, null, provider); } public String ToString(String format) { - Contract.Ensures(Contract.Result() != null); return ToString(format, null); } public String ToString(String format, IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); - - - if (_value < 0 && format != null && format.Length > 0 && (format[0] == 'X' || format[0] == 'x')) + if (m_value < 0 && format != null && format.Length > 0 && (format[0] == 'X' || format[0] == 'x')) { - uint temp = (uint)(_value & 0x000000FF); - return FormatProvider.FormatUInt32(temp, format, provider); + uint temp = (uint)(m_value & 0x000000FF); + return Number.FormatUInt32(temp, format, provider); } - return FormatProvider.FormatInt32(_value, format, provider); + return Number.FormatInt32(m_value, format, provider); + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) + { + if (m_value < 0 && format.Length > 0 && (format[0] == 'X' || format[0] == 'x')) + { + uint temp = (uint)(m_value & 0x000000FF); + return Number.TryFormatUInt32(temp, format, provider, destination, out charsWritten); + } + return Number.TryFormatInt32(m_value, format, provider, destination, out charsWritten); } [CLSCompliant(false)] public static sbyte Parse(String s) { - return Parse(s, NumberStyles.Integer, null); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } [CLSCompliant(false)] public static sbyte Parse(String s, NumberStyles style) { - UInt32.ValidateParseStyleInteger(style); - return Parse(s, style, null); + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, style, NumberFormatInfo.CurrentInfo); } [CLSCompliant(false)] public static sbyte Parse(String s, IFormatProvider provider) { - return Parse(s, NumberStyles.Integer, provider); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } // Parses a signed byte from a String in the given style. If @@ -127,11 +135,30 @@ namespace System [CLSCompliant(false)] public static sbyte Parse(String s, NumberStyles style, IFormatProvider provider) { - UInt32.ValidateParseStyleInteger(style); + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static sbyte Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Parse(s, style, NumberFormatInfo.GetInstance(provider)); + } + + private static sbyte Parse(String s, NumberStyles style, NumberFormatInfo info) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, style, info); + } + + private static sbyte Parse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info) + { int i = 0; try { - i = FormatProvider.ParseInt32(s, style, provider); + i = Number.ParseInt32(s, style, info); } catch (OverflowException e) { @@ -154,16 +181,47 @@ namespace System [CLSCompliant(false)] public static bool TryParse(String s, out SByte result) { - return TryParse(s, NumberStyles.Integer, null, out result); + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan s, out sbyte result) + { + return TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); } [CLSCompliant(false)] public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out SByte result) { - UInt32.ValidateParseStyleInteger(style); + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out sbyte result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + private static bool TryParse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out SByte result) + { result = 0; int i; - if (!FormatProvider.TryParseInt32(s, style, provider, out i)) + if (!Number.TryParseInt32(s, style, info, out i)) { return false; } @@ -198,72 +256,72 @@ namespace System bool IConvertible.ToBoolean(IFormatProvider provider) { - return Convert.ToBoolean(_value); + return Convert.ToBoolean(m_value); } char IConvertible.ToChar(IFormatProvider provider) { - return Convert.ToChar(_value); + return Convert.ToChar(m_value); } sbyte IConvertible.ToSByte(IFormatProvider provider) { - return _value; + return m_value; } byte IConvertible.ToByte(IFormatProvider provider) { - return Convert.ToByte(_value); + return Convert.ToByte(m_value); } short IConvertible.ToInt16(IFormatProvider provider) { - return Convert.ToInt16(_value); + return Convert.ToInt16(m_value); } ushort IConvertible.ToUInt16(IFormatProvider provider) { - return Convert.ToUInt16(_value); + return Convert.ToUInt16(m_value); } int IConvertible.ToInt32(IFormatProvider provider) { - return _value; + return m_value; } uint IConvertible.ToUInt32(IFormatProvider provider) { - return Convert.ToUInt32(_value); + return Convert.ToUInt32(m_value); } long IConvertible.ToInt64(IFormatProvider provider) { - return Convert.ToInt64(_value); + return Convert.ToInt64(m_value); } ulong IConvertible.ToUInt64(IFormatProvider provider) { - return Convert.ToUInt64(_value); + return Convert.ToUInt64(m_value); } float IConvertible.ToSingle(IFormatProvider provider) { - return Convert.ToSingle(_value); + return Convert.ToSingle(m_value); } double IConvertible.ToDouble(IFormatProvider provider) { - return Convert.ToDouble(_value); + return Convert.ToDouble(m_value); } Decimal IConvertible.ToDecimal(IFormatProvider provider) { - return Convert.ToDecimal(_value); + return Convert.ToDecimal(m_value); } DateTime IConvertible.ToDateTime(IFormatProvider provider) { - throw new InvalidCastException(String.Format(SR.InvalidCast_FromTo, "SByte", "DateTime")); + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "SByte", "DateTime")); } Object IConvertible.ToType(Type type, IFormatProvider provider) diff --git a/external/corefx/src/Common/src/CoreLib/System/Security/AllowPartiallyTrustedCallersAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Security/AllowPartiallyTrustedCallersAttribute.cs new file mode 100644 index 0000000000..84ad65c4c0 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Security/AllowPartiallyTrustedCallersAttribute.cs @@ -0,0 +1,19 @@ +// 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. + +namespace System.Security +{ + // AllowPartiallyTrustedCallersAttribute: + // Indicates that the Assembly is secure and can be used by untrusted + // and semitrusted clients + // For v.1, this is valid only on Assemblies, but could be expanded to + // include Module, Method, class + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)] + public sealed class AllowPartiallyTrustedCallersAttribute : Attribute + { + public AllowPartiallyTrustedCallersAttribute() { } + public PartialTrustVisibilityLevel PartialTrustVisibilityLevel { get; set; } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Security/CryptographicException.cs b/external/corefx/src/Common/src/CoreLib/System/Security/CryptographicException.cs new file mode 100644 index 0000000000..78ee290693 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Security/CryptographicException.cs @@ -0,0 +1,45 @@ +// 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.Globalization; +using System.Runtime.Serialization; + +namespace System.Security.Cryptography +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class CryptographicException : SystemException + { + public CryptographicException() + : base(SR.Arg_CryptographyException) + { + } + + public CryptographicException(int hr) + : base(SR.Arg_CryptographyException) + { + HResult = hr; + } + + public CryptographicException(string message) + : base(message) + { + } + + public CryptographicException(string message, Exception inner) + : base(message, inner) + { + } + + public CryptographicException(string format, string insert) + : base(string.Format(CultureInfo.CurrentCulture, format, insert)) + { + } + + protected CryptographicException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Security/PartialTrustVisibilityLevel.cs b/external/corefx/src/Common/src/CoreLib/System/Security/PartialTrustVisibilityLevel.cs new file mode 100644 index 0000000000..a0cb5789ac --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Security/PartialTrustVisibilityLevel.cs @@ -0,0 +1,13 @@ +// 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. + +namespace System.Security +{ + public enum PartialTrustVisibilityLevel + { + VisibleToAllHosts = 0, + NotVisibleByDefault = 1 + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Security/SafeBSTRHandle.cs b/external/corefx/src/Common/src/CoreLib/System/Security/SafeBSTRHandle.cs new file mode 100644 index 0000000000..227fed3fc3 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Security/SafeBSTRHandle.cs @@ -0,0 +1,82 @@ +// 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.Runtime; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Security +{ + internal sealed class SafeBSTRHandle : SafeBuffer + { + internal SafeBSTRHandle() : base(true) { } + + internal static SafeBSTRHandle Allocate(uint lenInChars) + { + ulong lenInBytes = (ulong)lenInChars * sizeof(char); + SafeBSTRHandle bstr = Interop.OleAut32.SysAllocStringLen(IntPtr.Zero, lenInChars); + if (bstr.IsInvalid) // SysAllocStringLen returns a NULL ptr when there's insufficient memory + { + throw new OutOfMemoryException(); + } + bstr.Initialize(lenInBytes); + return bstr; + } + + override protected bool ReleaseHandle() + { + RuntimeImports.RhZeroMemory(handle, (UIntPtr)(Interop.OleAut32.SysStringLen(handle) * sizeof(char))); + Interop.OleAut32.SysFreeString(handle); + return true; + } + + internal unsafe void ClearBuffer() + { + byte* bufferPtr = null; + try + { + AcquirePointer(ref bufferPtr); + RuntimeImports.RhZeroMemory((IntPtr)bufferPtr, (UIntPtr)(Interop.OleAut32.SysStringLen((IntPtr)bufferPtr) * sizeof(char))); + } + finally + { + if (bufferPtr != null) + { + ReleasePointer(); + } + } + } + + internal unsafe uint Length => Interop.OleAut32.SysStringLen(this); + + internal unsafe static void Copy(SafeBSTRHandle source, SafeBSTRHandle target, uint bytesToCopy) + { + if (bytesToCopy == 0) + { + return; + } + + byte* sourcePtr = null, targetPtr = null; + try + { + source.AcquirePointer(ref sourcePtr); + target.AcquirePointer(ref targetPtr); + + Debug.Assert(source.ByteLength >= bytesToCopy, "Source buffer is too small."); + Buffer.MemoryCopy(sourcePtr, targetPtr, target.ByteLength, bytesToCopy); + } + finally + { + if (targetPtr != null) + { + target.ReleasePointer(); + } + if (sourcePtr != null) + { + source.ReleasePointer(); + } + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Security/SecureString.Unix.cs b/external/corefx/src/Common/src/CoreLib/System/Security/SecureString.Unix.cs new file mode 100644 index 0000000000..cfeebc1daf --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Security/SecureString.Unix.cs @@ -0,0 +1,325 @@ +// 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.Diagnostics; +using System.Runtime; +using System.Runtime.InteropServices; +using System.Text; + +namespace System.Security +{ + // SecureString attempts to provide a defense-in-depth solution. + // + // On Windows, this is done with several mechanisms: + // 1. keeping the data in unmanaged memory so that copies of it aren't implicitly made by the GC moving it around + // 2. zero'ing out that unmanaged memory so that the string is reliably removed from memory when done with it + // 3. encrypting the data while it's not being used (it's unencrypted to manipulate and use it) + // + // On Unix, we do 1 and 2, but we don't do 3 as there's no CryptProtectData equivalent. + + public sealed partial class SecureString + { + private UnmanagedBuffer _buffer; + + internal SecureString(SecureString str) + { + // Allocate enough space to store the provided string + EnsureCapacity(str._decryptedLength); + _decryptedLength = str._decryptedLength; + + // Copy the string into the newly allocated space + if (_decryptedLength > 0) + { + UnmanagedBuffer.Copy(str._buffer, _buffer, (ulong)(str._decryptedLength * sizeof(char))); + } + } + + private unsafe void InitializeSecureString(char* value, int length) + { + // Allocate enough space to store the provided string + EnsureCapacity(length); + _decryptedLength = length; + if (length == 0) + { + return; + } + + // Copy the string into the newly allocated space + byte* ptr = null; + try + { + _buffer.AcquirePointer(ref ptr); + Buffer.MemoryCopy(value, ptr, _buffer.ByteLength, (ulong)(length * sizeof(char))); + } + finally + { + if (ptr != null) + { + _buffer.ReleasePointer(); + } + } + } + + private void DisposeCore() + { + if (_buffer != null && !_buffer.IsInvalid) + { + _buffer.Dispose(); + _buffer = null; + } + } + + private void EnsureNotDisposed() + { + if (_buffer == null) + { + throw new ObjectDisposedException(GetType().Name); + } + } + + private void ClearCore() + { + _decryptedLength = 0; + _buffer.Clear(); + } + + private unsafe void AppendCharCore(char c) + { + // Make sure we have enough space for the new character, then write it at the end. + EnsureCapacity(_decryptedLength + 1); + _buffer.Write((ulong)(_decryptedLength * sizeof(char)), c); + _decryptedLength++; + } + + private unsafe void InsertAtCore(int index, char c) + { + // Make sure we have enough space for the new character, then shift all of the characters above it and insert it. + EnsureCapacity(_decryptedLength + 1); + byte* ptr = null; + try + { + _buffer.AcquirePointer(ref ptr); + ptr += index * sizeof(char); + long bytesToShift = (_decryptedLength - index) * sizeof(char); + Buffer.MemoryCopy(ptr, ptr + sizeof(char), bytesToShift, bytesToShift); + *((char*)ptr) = c; + ++_decryptedLength; + } + finally + { + if (ptr != null) + { + _buffer.ReleasePointer(); + } + } + } + + private unsafe void RemoveAtCore(int index) + { + // Shift down all values above the specified index, then null out the empty space at the end. + byte* ptr = null; + try + { + _buffer.AcquirePointer(ref ptr); + ptr += index * sizeof(char); + long bytesToShift = (_decryptedLength - index - 1) * sizeof(char); + Buffer.MemoryCopy(ptr + sizeof(char), ptr, bytesToShift, bytesToShift); + *((char*)(ptr + bytesToShift)) = (char)0; + --_decryptedLength; + } + finally + { + if (ptr != null) + { + _buffer.ReleasePointer(); + } + } + } + + private void SetAtCore(int index, char c) + { + // Overwrite the character at the specified index + _buffer.Write((ulong)(index * sizeof(char)), c); + } + + internal unsafe IntPtr MarshalToBSTR() + { + int length = _decryptedLength; + IntPtr ptr = IntPtr.Zero; + IntPtr result = IntPtr.Zero; + byte* bufferPtr = null; + + try + { + _buffer.AcquirePointer(ref bufferPtr); + int resultByteLength = (length + 1) * sizeof(char); + + ptr = PInvokeMarshal.AllocBSTR(length); + + Buffer.MemoryCopy(bufferPtr, (byte*)ptr, resultByteLength, length * sizeof(char)); + + result = ptr; + } + finally + { + // If we failed for any reason, free the new buffer + if (result == IntPtr.Zero && ptr != IntPtr.Zero) + { + RuntimeImports.RhZeroMemory(ptr, (UIntPtr)(length * sizeof(char))); + PInvokeMarshal.FreeBSTR(ptr); + } + + if (bufferPtr != null) + { + _buffer.ReleasePointer(); + } + } + return result; + } + + internal unsafe IntPtr MarshalToStringCore(bool globalAlloc, bool unicode) + { + int length = _decryptedLength; + + byte* bufferPtr = null; + IntPtr stringPtr = IntPtr.Zero, result = IntPtr.Zero; + try + { + _buffer.AcquirePointer(ref bufferPtr); + if (unicode) + { + int resultLength = (length + 1) * sizeof(char); + stringPtr = globalAlloc ? Marshal.AllocHGlobal(resultLength) : Marshal.AllocCoTaskMem(resultLength); + Buffer.MemoryCopy( + source: bufferPtr, + destination: (byte*)stringPtr.ToPointer(), + destinationSizeInBytes: resultLength, + sourceBytesToCopy: length * sizeof(char)); + *(length + (char*)stringPtr) = '\0'; + } + else + { + int resultLength = Encoding.UTF8.GetByteCount((char*)bufferPtr, length) + 1; + stringPtr = globalAlloc ? Marshal.AllocHGlobal(resultLength) : Marshal.AllocCoTaskMem(resultLength); + int encodedLength = Encoding.UTF8.GetBytes((char*)bufferPtr, length, (byte*)stringPtr, resultLength); + Debug.Assert(encodedLength + 1 == resultLength, $"Expected encoded length to match result, got {encodedLength} != {resultLength}"); + *(resultLength - 1 + (byte*)stringPtr) = 0; + } + + result = stringPtr; + } + finally + { + // If there was a failure, such that result isn't initialized, + // release the string if we had one. + if (stringPtr != IntPtr.Zero && result == IntPtr.Zero) + { + RuntimeImports.RhZeroMemory(stringPtr, (UIntPtr)(length * sizeof(char))); + MarshalFree(stringPtr, globalAlloc); + } + + if (bufferPtr != null) + { + _buffer.ReleasePointer(); + } + } + + return result; + } + + // ----------------------------- + // ---- PAL layer ends here ---- + // ----------------------------- + + private void EnsureCapacity(int capacity) + { + // Make sure the requested capacity doesn't exceed SecureString's defined limit + if (capacity > MaxLength) + { + throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_Capacity); + } + + // If we already have enough space allocated, we're done + if (_buffer != null && (capacity * sizeof(char)) <= (int)_buffer.ByteLength) + { + return; + } + + // We need more space, so allocate a new buffer, copy all our data into it, + // and then swap the new for the old. + UnmanagedBuffer newBuffer = UnmanagedBuffer.Allocate(capacity * sizeof(char)); + if (_buffer != null) + { + UnmanagedBuffer.Copy(_buffer, newBuffer, _buffer.ByteLength); + _buffer.Dispose(); + } + _buffer = newBuffer; + } + + /// SafeBuffer for managing memory meant to be kept confidential. + private sealed class UnmanagedBuffer : SafeBuffer + { + internal UnmanagedBuffer() : base(true) { } + + internal static UnmanagedBuffer Allocate(int bytes) + { + Debug.Assert(bytes >= 0); + UnmanagedBuffer buffer = new UnmanagedBuffer(); + buffer.SetHandle(Marshal.AllocHGlobal(bytes)); + buffer.Initialize((ulong)bytes); + return buffer; + } + + internal unsafe void Clear() + { + byte* ptr = null; + try + { + AcquirePointer(ref ptr); + RuntimeImports.RhZeroMemory((IntPtr)ptr, (UIntPtr)ByteLength); + } + finally + { + if (ptr != null) + { + ReleasePointer(); + } + } + } + + internal static unsafe void Copy(UnmanagedBuffer source, UnmanagedBuffer destination, ulong bytesLength) + { + if (bytesLength == 0) + { + return; + } + + byte* srcPtr = null, dstPtr = null; + try + { + source.AcquirePointer(ref srcPtr); + destination.AcquirePointer(ref dstPtr); + Buffer.MemoryCopy(srcPtr, dstPtr, destination.ByteLength, bytesLength); + } + finally + { + if (dstPtr != null) + { + destination.ReleasePointer(); + } + if (srcPtr != null) + { + source.ReleasePointer(); + } + } + } + + protected override unsafe bool ReleaseHandle() + { + Marshal.FreeHGlobal(handle); + return true; + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Security/SecureString.Windows.cs b/external/corefx/src/Common/src/CoreLib/System/Security/SecureString.Windows.cs new file mode 100644 index 0000000000..2a80081912 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Security/SecureString.Windows.cs @@ -0,0 +1,308 @@ +// 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.Diagnostics; +using System.Runtime; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using Microsoft.Win32; + +namespace System.Security +{ + public sealed partial class SecureString + { + internal SecureString(SecureString str) + { + Debug.Assert(str != null, "Expected non-null SecureString"); + Debug.Assert(str._buffer != null, "Expected other SecureString's buffer to be non-null"); + Debug.Assert(str._encrypted, "Expected to be used only on encrypted SecureStrings"); + + AllocateBuffer(str._buffer.Length); + SafeBSTRHandle.Copy(str._buffer, _buffer, str._buffer.Length * sizeof(char)); + + _decryptedLength = str._decryptedLength; + _encrypted = str._encrypted; + } + + private unsafe void InitializeSecureString(char* value, int length) + { + Debug.Assert(length >= 0, $"Expected non-negative length, got {length}"); + + AllocateBuffer((uint)length); + _decryptedLength = length; + + byte* bufferPtr = null; + try + { + _buffer.AcquirePointer(ref bufferPtr); + Buffer.MemoryCopy((byte*)value, bufferPtr, (long)_buffer.ByteLength, length * sizeof(char)); + } + finally + { + if (bufferPtr != null) + { + _buffer.ReleasePointer(); + } + } + + ProtectMemory(); + } + + private void AppendCharCore(char c) + { + UnprotectMemory(); + try + { + EnsureCapacity(_decryptedLength + 1); + _buffer.Write((uint)_decryptedLength * sizeof(char), c); + _decryptedLength++; + } + finally + { + ProtectMemory(); + } + } + + private void ClearCore() + { + _decryptedLength = 0; + _buffer.ClearBuffer(); + } + + private void DisposeCore() + { + if (_buffer != null) + { + _buffer.Dispose(); + _buffer = null; + } + } + + private unsafe void InsertAtCore(int index, char c) + { + byte* bufferPtr = null; + UnprotectMemory(); + try + { + EnsureCapacity(_decryptedLength + 1); + + _buffer.AcquirePointer(ref bufferPtr); + char* pBuffer = (char*)bufferPtr; + + for (int i = _decryptedLength; i > index; i--) + { + pBuffer[i] = pBuffer[i - 1]; + } + pBuffer[index] = c; + ++_decryptedLength; + } + finally + { + ProtectMemory(); + if (bufferPtr != null) + { + _buffer.ReleasePointer(); + } + } + } + + private unsafe void RemoveAtCore(int index) + { + byte* bufferPtr = null; + UnprotectMemory(); + try + { + _buffer.AcquirePointer(ref bufferPtr); + char* pBuffer = (char*)bufferPtr; + + for (int i = index; i < _decryptedLength - 1; i++) + { + pBuffer[i] = pBuffer[i + 1]; + } + pBuffer[--_decryptedLength] = (char)0; + } + finally + { + ProtectMemory(); + if (bufferPtr != null) + { + _buffer.ReleasePointer(); + } + } + } + + private void SetAtCore(int index, char c) + { + UnprotectMemory(); + try + { + _buffer.Write((uint)index * sizeof(char), c); + } + finally + { + ProtectMemory(); + } + } + + internal unsafe IntPtr MarshalToBSTR() + { + int length = _decryptedLength; + IntPtr ptr = IntPtr.Zero; + IntPtr result = IntPtr.Zero; + byte* bufferPtr = null; + + UnprotectMemory(); + try + { + _buffer.AcquirePointer(ref bufferPtr); + int resultByteLength = (length + 1) * sizeof(char); + + ptr = PInvokeMarshal.AllocBSTR(length); + + Buffer.MemoryCopy(bufferPtr, (byte*)ptr, resultByteLength, length * sizeof(char)); + + result = ptr; + } + finally + { + ProtectMemory(); + + // If we failed for any reason, free the new buffer + if (result == IntPtr.Zero && ptr != IntPtr.Zero) + { + RuntimeImports.RhZeroMemory(ptr, (UIntPtr)(length * sizeof(char))); + PInvokeMarshal.FreeBSTR(ptr); + } + + if (bufferPtr != null) + { + _buffer.ReleasePointer(); + } + } + return result; + } + + internal unsafe IntPtr MarshalToStringCore(bool globalAlloc, bool unicode) + { + int length = _decryptedLength; + IntPtr ptr = IntPtr.Zero; + IntPtr result = IntPtr.Zero; + byte* bufferPtr = null; + + UnprotectMemory(); + try + { + _buffer.AcquirePointer(ref bufferPtr); + if (unicode) + { + int resultByteLength = (length + 1) * sizeof(char); + ptr = globalAlloc ? Marshal.AllocHGlobal(resultByteLength) : Marshal.AllocCoTaskMem(resultByteLength); + Buffer.MemoryCopy(bufferPtr, (byte*)ptr, resultByteLength, length * sizeof(char)); + *(length + (char*)ptr) = '\0'; + } + else + { + uint defaultChar = '?'; + int resultByteLength = 1 + Interop.Kernel32.WideCharToMultiByte( + Interop.Kernel32.CP_ACP, Interop.Kernel32.WC_NO_BEST_FIT_CHARS, (char*)bufferPtr, length, null, 0, (IntPtr)(&defaultChar), IntPtr.Zero); + ptr = globalAlloc ? Marshal.AllocHGlobal(resultByteLength) : Marshal.AllocCoTaskMem(resultByteLength); + Interop.Kernel32.WideCharToMultiByte( + Interop.Kernel32.CP_ACP, Interop.Kernel32.WC_NO_BEST_FIT_CHARS, (char*)bufferPtr, length, (byte*)ptr, resultByteLength - 1, (IntPtr)(&defaultChar), IntPtr.Zero); + *(resultByteLength - 1 + (byte*)ptr) = 0; + } + result = ptr; + } + finally + { + ProtectMemory(); + + // If we failed for any reason, free the new buffer + if (result == IntPtr.Zero && ptr != IntPtr.Zero) + { + RuntimeImports.RhZeroMemory(ptr, (UIntPtr)(length * sizeof(char))); + MarshalFree(ptr, globalAlloc); + } + + if (bufferPtr != null) + { + _buffer.ReleasePointer(); + } + } + return result; + } + + private void EnsureNotDisposed() + { + if (_buffer == null) + { + throw new ObjectDisposedException(GetType().Name); + } + } + + // ----------------------------- + // ---- PAL layer ends here ---- + // ----------------------------- + + private const int BlockSize = (int)Interop.Crypt32.CRYPTPROTECTMEMORY_BLOCK_SIZE / sizeof(char); + private SafeBSTRHandle _buffer; + private bool _encrypted; + + private void AllocateBuffer(uint size) + { + _buffer = SafeBSTRHandle.Allocate(GetAlignedSize(size)); + } + + private static uint GetAlignedSize(uint size) => + size == 0 || size % BlockSize != 0 ? + BlockSize + ((size / BlockSize) * BlockSize) : + size; + + private void EnsureCapacity(int capacity) + { + if (capacity > MaxLength) + { + throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_Capacity); + } + + if (((uint)capacity * sizeof(char)) <= _buffer.ByteLength) + { + return; + } + + var oldBuffer = _buffer; + SafeBSTRHandle newBuffer = SafeBSTRHandle.Allocate(GetAlignedSize((uint)capacity)); + SafeBSTRHandle.Copy(oldBuffer, newBuffer, (uint)_decryptedLength * sizeof(char)); + _buffer = newBuffer; + oldBuffer.Dispose(); + } + + private void ProtectMemory() + { + Debug.Assert(!_buffer.IsInvalid, "Invalid buffer!"); + + if (_decryptedLength != 0 && + !_encrypted && + !Interop.Crypt32.CryptProtectMemory(_buffer, _buffer.Length * sizeof(char), Interop.Crypt32.CRYPTPROTECTMEMORY_SAME_PROCESS)) + { + throw new CryptographicException(Marshal.GetLastWin32Error()); + } + + _encrypted = true; + } + + private void UnprotectMemory() + { + Debug.Assert(!_buffer.IsInvalid, "Invalid buffer!"); + + if (_decryptedLength != 0 && + _encrypted && + !Interop.Crypt32.CryptUnprotectMemory(_buffer, _buffer.Length * sizeof(char), Interop.Crypt32.CRYPTPROTECTMEMORY_SAME_PROCESS)) + { + throw new CryptographicException(Marshal.GetLastWin32Error()); + } + + _encrypted = false; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Security/SecureString.cs b/external/corefx/src/Common/src/CoreLib/System/Security/SecureString.cs new file mode 100644 index 0000000000..9059f90e60 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Security/SecureString.cs @@ -0,0 +1,189 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Security +{ + public sealed partial class SecureString : IDisposable + { + private const int MaxLength = 65536; + private readonly object _methodLock = new object(); + private bool _readOnly; + private int _decryptedLength; + + public unsafe SecureString() + { + InitializeSecureString(null, 0); + } + + [CLSCompliant(false)] + public unsafe SecureString(char* value, int length) + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + if (length < 0) + { + throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (length > MaxLength) + { + throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_Length); + } + + InitializeSecureString(value, length); + } + + public int Length + { + get + { + lock (_methodLock) + { + EnsureNotDisposed(); + return _decryptedLength; + } + } + } + + public void AppendChar(char c) + { + lock (_methodLock) + { + EnsureNotDisposed(); + EnsureNotReadOnly(); + AppendCharCore(c); + } + } + + // clears the current contents. Only available if writable + public void Clear() + { + lock (_methodLock) + { + EnsureNotDisposed(); + EnsureNotReadOnly(); + ClearCore(); + } + } + + // Do a deep-copy of the SecureString + public SecureString Copy() + { + lock (_methodLock) + { + EnsureNotDisposed(); + return new SecureString(this); + } + } + + public void Dispose() + { + lock (_methodLock) + { + DisposeCore(); + } + } + + public void InsertAt(int index, char c) + { + lock (_methodLock) + { + if (index < 0 || index > _decryptedLength) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_IndexString); + } + + EnsureNotDisposed(); + EnsureNotReadOnly(); + + InsertAtCore(index, c); + } + } + + public bool IsReadOnly() + { + lock (_methodLock) + { + EnsureNotDisposed(); + return _readOnly; + } + } + + public void MakeReadOnly() + { + lock (_methodLock) + { + EnsureNotDisposed(); + _readOnly = true; + } + } + + public void RemoveAt(int index) + { + lock (_methodLock) + { + EnsureNotDisposed(); + EnsureNotReadOnly(); + + if (index < 0 || index >= _decryptedLength) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_IndexString); + } + + RemoveAtCore(index); + } + } + + public void SetAt(int index, char c) + { + lock (_methodLock) + { + if (index < 0 || index >= _decryptedLength) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_IndexString); + } + Debug.Assert(index <= Int32.MaxValue / sizeof(char)); + + EnsureNotDisposed(); + EnsureNotReadOnly(); + + SetAtCore(index, c); + } + } + + private void EnsureNotReadOnly() + { + if (_readOnly) + { + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + } + } + + internal unsafe IntPtr MarshalToString(bool globalAlloc, bool unicode) + { + lock (_methodLock) + { + EnsureNotDisposed(); + return MarshalToStringCore(globalAlloc, unicode); + } + } + + private static void MarshalFree(IntPtr ptr, bool globalAlloc) + { + if (globalAlloc) + { + Marshal.FreeHGlobal(ptr); + } + else + { + Marshal.FreeCoTaskMem(ptr); + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Security/SecurityCriticalAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Security/SecurityCriticalAttribute.cs new file mode 100644 index 0000000000..2bf1700afb --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Security/SecurityCriticalAttribute.cs @@ -0,0 +1,36 @@ +// 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. + +namespace System.Security +{ + // SecurityCriticalAttribute + // Indicates that the decorated code or assembly performs security critical operations (e.g. Assert, "unsafe", LinkDemand, etc.) + // The attribute can be placed on most targets, except on arguments/return values. + [AttributeUsage(AttributeTargets.Assembly | + AttributeTargets.Class | + AttributeTargets.Struct | + AttributeTargets.Enum | + AttributeTargets.Constructor | + AttributeTargets.Method | + AttributeTargets.Field | + AttributeTargets.Interface | + AttributeTargets.Delegate, + AllowMultiple = false, + Inherited = false)] + public sealed class SecurityCriticalAttribute : Attribute + { +#pragma warning disable 618 // We still use SecurityCriticalScope for v2 compat + public SecurityCriticalAttribute() { } + + public SecurityCriticalAttribute(SecurityCriticalScope scope) + { + Scope = scope; + } + + [Obsolete("SecurityCriticalScope is only used for .NET 2.0 transparency compatibility.")] + public SecurityCriticalScope Scope { get; } +#pragma warning restore 618 + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Security/SecurityCriticalScope.cs b/external/corefx/src/Common/src/CoreLib/System/Security/SecurityCriticalScope.cs new file mode 100644 index 0000000000..e0f5a8e2cd --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Security/SecurityCriticalScope.cs @@ -0,0 +1,14 @@ +// 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. + +namespace System.Security +{ + [Obsolete("SecurityCriticalScope is only used for .NET 2.0 transparency compatibility.")] + public enum SecurityCriticalScope + { + Explicit = 0, + Everything = 0x1 + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Security/SecurityException.cs b/external/corefx/src/Common/src/CoreLib/System/Security/SecurityException.cs new file mode 100644 index 0000000000..61504c3ba1 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Security/SecurityException.cs @@ -0,0 +1,89 @@ +// 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.Reflection; +using System.Runtime.Serialization; + +namespace System.Security +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class SecurityException : SystemException + { + private const string DemandedName = "Demanded"; + private const string GrantedSetName = "GrantedSet"; + private const string RefusedSetName = "RefusedSet"; + private const string DeniedName = "Denied"; + private const string PermitOnlyName = "PermitOnly"; + private const string UrlName = "Url"; + + public SecurityException() + : base(SR.Arg_SecurityException) + { + HResult = HResults.COR_E_SECURITY; + } + + public SecurityException(string message) + : base(message) + { + HResult = HResults.COR_E_SECURITY; + } + + public SecurityException(string message, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_SECURITY; + } + + public SecurityException(string message, Type type) + : base(message) + { + HResult = HResults.COR_E_SECURITY; + PermissionType = type; + } + + public SecurityException(string message, Type type, string state) + : base(message) + { + HResult = HResults.COR_E_SECURITY; + PermissionType = type; + PermissionState = state; + } + + protected SecurityException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + Demanded = (string)info.GetValueNoThrow(DemandedName, typeof(string)); + GrantedSet = (string)info.GetValueNoThrow(GrantedSetName, typeof(string)); + RefusedSet = (string)info.GetValueNoThrow(RefusedSetName, typeof(string)); + DenySetInstance = (string)info.GetValueNoThrow(DeniedName, typeof(string)); + PermitOnlySetInstance = (string)info.GetValueNoThrow(PermitOnlyName, typeof(string)); + Url = (string)info.GetValueNoThrow(UrlName, typeof(string)); + } + + public override string ToString() => base.ToString(); + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue(DemandedName, Demanded, typeof(string)); + info.AddValue(GrantedSetName, GrantedSet, typeof(string)); + info.AddValue(RefusedSetName, RefusedSet, typeof(string)); + info.AddValue(DeniedName, DenySetInstance, typeof(string)); + info.AddValue(PermitOnlyName, PermitOnlySetInstance, typeof(string)); + info.AddValue(UrlName, Url, typeof(string)); + } + + public object Demanded { get; set; } + public object DenySetInstance { get; set; } + public AssemblyName FailedAssemblyInfo { get; set; } + public string GrantedSet { get; set; } + public MethodInfo Method { get; set; } + public string PermissionState { get; set; } + public Type PermissionType { get; set; } + public object PermitOnlySetInstance { get; set; } + public string RefusedSet { get; set; } + public string Url { get; set; } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Security/SecurityRuleSet.cs b/external/corefx/src/Common/src/CoreLib/System/Security/SecurityRuleSet.cs new file mode 100644 index 0000000000..1b62fd4e7d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Security/SecurityRuleSet.cs @@ -0,0 +1,14 @@ +// 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. + +namespace System.Security +{ + public enum SecurityRuleSet : byte + { + None = 0, + Level1 = 1, // v2.0 transparency model + Level2 = 2, // v4.0 transparency model + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Security/SecurityRulesAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Security/SecurityRulesAttribute.cs new file mode 100644 index 0000000000..ad17087f8b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Security/SecurityRulesAttribute.cs @@ -0,0 +1,28 @@ +// 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. + +namespace System.Security +{ + // SecurityRulesAttribute + // + // Indicates which set of security rules an assembly was authored against, and therefore which set of + // rules the runtime should enforce on the assembly. For instance, an assembly marked with + // [SecurityRules(SecurityRuleSet.Level1)] will follow the v2.0 transparency rules, where transparent code + // can call a LinkDemand by converting it to a full demand, public critical methods are implicitly + // treat as safe, and the remainder of the v2.0 rules apply. + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] + public sealed class SecurityRulesAttribute : Attribute + { + public SecurityRulesAttribute(SecurityRuleSet ruleSet) + { + RuleSet = ruleSet; + } + + // Should fully trusted transparent code skip IL verification + public bool SkipVerificationInFullTrust { get; set; } + + public SecurityRuleSet RuleSet { get; } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Security/SecuritySafeCriticalAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Security/SecuritySafeCriticalAttribute.cs new file mode 100644 index 0000000000..ee2e4b0499 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Security/SecuritySafeCriticalAttribute.cs @@ -0,0 +1,30 @@ +// 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. + +namespace System.Security +{ + // SecuritySafeCriticalAttribute: + // Indicates that the code may contain violations to the security critical rules (e.g. transitions from + // critical to non-public transparent, transparent to non-public critical, etc.), has been audited for + // security concerns and is considered security clean. Also indicates that the code is considered SecurityCritical. + // The effect of this attribute is as if the code was marked [SecurityCritical][SecurityTreatAsSafe]. + // At assembly-scope, all rule checks will be suppressed within the assembly and for calls made against the assembly. + // At type-scope, all rule checks will be suppressed for members within the type and for calls made against the type. + // At member level (e.g. field and method) the code will be treated as public - i.e. no rule checks for the members. + + [AttributeUsage(AttributeTargets.Class | + AttributeTargets.Struct | + AttributeTargets.Enum | + AttributeTargets.Constructor | + AttributeTargets.Method | + AttributeTargets.Field | + AttributeTargets.Interface | + AttributeTargets.Delegate, + AllowMultiple = false, + Inherited = false)] + public sealed class SecuritySafeCriticalAttribute : Attribute + { + public SecuritySafeCriticalAttribute() { } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Security/SecurityTransparentAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Security/SecurityTransparentAttribute.cs new file mode 100644 index 0000000000..03f41387ae --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Security/SecurityTransparentAttribute.cs @@ -0,0 +1,19 @@ +// 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. + +namespace System.Security +{ + // SecurityTransparentAttribute: + // Indicates the assembly contains only transparent code. + // Security critical actions will be restricted or converted into less critical actions. For example, + // Assert will be restricted, SuppressUnmanagedCode, LinkDemand, unsafe, and unverifiable code will be converted + // into Full-Demands. + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)] + public sealed class SecurityTransparentAttribute : Attribute + { + public SecurityTransparentAttribute() { } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Security/SecurityTreatAsSafeAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Security/SecurityTreatAsSafeAttribute.cs new file mode 100644 index 0000000000..7a95122bf0 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Security/SecurityTreatAsSafeAttribute.cs @@ -0,0 +1,32 @@ +// 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. + +namespace System.Security +{ + // SecurityTreatAsSafeAttribute: + // Indicates that the code may contain violations to the security critical rules (e.g. transitions from + // critical to non-public transparent, transparent to non-public critical, etc.), has been audited for + // security concerns and is considered security clean. + // At assembly-scope, all rule checks will be suppressed within the assembly and for calls made against the assembly. + // At type-scope, all rule checks will be suppressed for members within the type and for calls made against the type. + // At member level (e.g. field and method) the code will be treated as public - i.e. no rule checks for the members. + + [AttributeUsage(AttributeTargets.Assembly | + AttributeTargets.Class | + AttributeTargets.Struct | + AttributeTargets.Enum | + AttributeTargets.Constructor | + AttributeTargets.Method | + AttributeTargets.Field | + AttributeTargets.Interface | + AttributeTargets.Delegate, + AllowMultiple = false, + Inherited = false)] + [Obsolete("SecurityTreatAsSafe is only used for .NET 2.0 transparency compatibility. Please use the SecuritySafeCriticalAttribute instead.")] + public sealed class SecurityTreatAsSafeAttribute : Attribute + { + public SecurityTreatAsSafeAttribute() { } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Security/SuppressUnmanagedCodeSecurityAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Security/SuppressUnmanagedCodeSecurityAttribute.cs new file mode 100644 index 0000000000..a60b8d3668 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Security/SuppressUnmanagedCodeSecurityAttribute.cs @@ -0,0 +1,16 @@ +// 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. + +namespace System.Security +{ + // SuppressUnmanagedCodeSecurityAttribute: + // Indicates that the target P/Invoke method(s) should skip the per-call + // security checked for unmanaged code permission. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = true, Inherited = false)] + public sealed class SuppressUnmanagedCodeSecurityAttribute : Attribute + { + public SuppressUnmanagedCodeSecurityAttribute() { } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Security/UnverifiableCodeAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/Security/UnverifiableCodeAttribute.cs new file mode 100644 index 0000000000..1560b6617b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Security/UnverifiableCodeAttribute.cs @@ -0,0 +1,15 @@ +// 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. + +namespace System.Security +{ + // UnverifiableCodeAttribute: + // Indicates that the target module contains unverifiable code. + [AttributeUsage(AttributeTargets.Module, AllowMultiple = true, Inherited = false)] + public sealed class UnverifiableCodeAttribute : Attribute + { + public UnverifiableCodeAttribute() { } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Security/VerificationException.cs b/external/corefx/src/Common/src/CoreLib/System/Security/VerificationException.cs new file mode 100644 index 0000000000..e2afd4cabe --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Security/VerificationException.cs @@ -0,0 +1,36 @@ +// 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.Runtime.Serialization; + +namespace System.Security +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class VerificationException : SystemException + { + public VerificationException() + : base(SR.Verification_Exception) + { + HResult = HResults.COR_E_VERIFICATION; + } + + public VerificationException(string message) + : base(message) + { + HResult = HResults.COR_E_VERIFICATION; + } + + public VerificationException(string message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_VERIFICATION; + } + + protected VerificationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/SerializableAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/SerializableAttribute.cs similarity index 100% rename from external/corert/src/System.Private.CoreLib/src/System/SerializableAttribute.cs rename to external/corefx/src/Common/src/CoreLib/System/SerializableAttribute.cs diff --git a/external/corefx/src/Common/src/CoreLib/System/Single.cs b/external/corefx/src/Common/src/CoreLib/System/Single.cs new file mode 100644 index 0000000000..df97427d38 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Single.cs @@ -0,0 +1,439 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: A wrapper class for the primitive type float. +** +** +===========================================================*/ + +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Single : IComparable, IConvertible, IFormattable, IComparable, IEquatable, ISpanFormattable + { + private float m_value; // Do not rename (binary serialization) + + // + // Public constants + // + public const float MinValue = (float)-3.40282346638528859e+38; + public const float Epsilon = (float)1.4e-45; + public const float MaxValue = (float)3.40282346638528859e+38; + public const float PositiveInfinity = (float)1.0 / (float)0.0; + public const float NegativeInfinity = (float)-1.0 / (float)0.0; + public const float NaN = (float)0.0 / (float)0.0; + + // We use this explicit definition to avoid the confusion between 0.0 and -0.0. + internal const float NegativeZero = (float)-0.0; + + /// Determines whether the specified value is finite (zero, subnormal, or normal). + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFinite(float f) + { + var bits = BitConverter.SingleToInt32Bits(f); + return (bits & 0x7FFFFFFF) < 0x7F800000; + } + + /// Determines whether the specified value is infinite. + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsInfinity(float f) + { + var bits = BitConverter.SingleToInt32Bits(f); + return (bits & 0x7FFFFFFF) == 0x7F800000; + } + + /// Determines whether the specified value is NaN. + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsNaN(float f) + { + var bits = BitConverter.SingleToInt32Bits(f); + return (bits & 0x7FFFFFFF) > 0x7F800000; + } + + /// Determines whether the specified value is negative. + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsNegative(float f) + { + var bits = unchecked((uint)BitConverter.SingleToInt32Bits(f)); + return (bits & 0x80000000) == 0x80000000; + } + + /// Determines whether the specified value is negative infinity. + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsNegativeInfinity(float f) + { + return (f == float.NegativeInfinity); + } + + /// Determines whether the specified value is normal. + [NonVersionable] + // This is probably not worth inlining, it has branches and should be rarely called + public unsafe static bool IsNormal(float f) + { + var bits = BitConverter.SingleToInt32Bits(f); + bits &= 0x7FFFFFFF; + return (bits < 0x7F800000) && (bits != 0) && ((bits & 0x7F800000) != 0); + } + + /// Determines whether the specified value is positive infinity. + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsPositiveInfinity(float f) + { + return (f == float.PositiveInfinity); + } + + /// Determines whether the specified value is subnormal. + [NonVersionable] + // This is probably not worth inlining, it has branches and should be rarely called + public unsafe static bool IsSubnormal(float f) + { + var bits = BitConverter.SingleToInt32Bits(f); + bits &= 0x7FFFFFFF; + return (bits < 0x7F800000) && (bits != 0) && ((bits & 0x7F800000) == 0); + } + + // Compares this object to another object, returning an integer that + // indicates the relationship. + // Returns a value less than zero if this object + // null is considered to be less than any instance. + // If object is not of type Single, this method throws an ArgumentException. + // + public int CompareTo(Object value) + { + if (value == null) + { + return 1; + } + if (value is Single) + { + float f = (float)value; + if (m_value < f) return -1; + if (m_value > f) return 1; + if (m_value == f) return 0; + + // At least one of the values is NaN. + if (IsNaN(m_value)) + return (IsNaN(f) ? 0 : -1); + else // f is NaN. + return 1; + } + throw new ArgumentException(SR.Arg_MustBeSingle); + } + + + public int CompareTo(Single value) + { + if (m_value < value) return -1; + if (m_value > value) return 1; + if (m_value == value) return 0; + + // At least one of the values is NaN. + if (IsNaN(m_value)) + return (IsNaN(value) ? 0 : -1); + else // f is NaN. + return 1; + } + + [NonVersionable] + public static bool operator ==(Single left, Single right) + { + return left == right; + } + + [NonVersionable] + public static bool operator !=(Single left, Single right) + { + return left != right; + } + + [NonVersionable] + public static bool operator <(Single left, Single right) + { + return left < right; + } + + [NonVersionable] + public static bool operator >(Single left, Single right) + { + return left > right; + } + + [NonVersionable] + public static bool operator <=(Single left, Single right) + { + return left <= right; + } + + [NonVersionable] + public static bool operator >=(Single left, Single right) + { + return left >= right; + } + + public override bool Equals(Object obj) + { + if (!(obj is Single)) + { + return false; + } + float temp = ((Single)obj).m_value; + if (temp == m_value) + { + return true; + } + + return IsNaN(temp) && IsNaN(m_value); + } + + public bool Equals(Single obj) + { + if (obj == m_value) + { + return true; + } + + return IsNaN(obj) && IsNaN(m_value); + } + + public unsafe override int GetHashCode() + { + float f = m_value; + if (f == 0) + { + // Ensure that 0 and -0 have the same hash code + return 0; + } + int v = *(int*)(&f); + return v; + } + + public override String ToString() + { + return Number.FormatSingle(m_value, null, NumberFormatInfo.CurrentInfo); + } + + public String ToString(IFormatProvider provider) + { + return Number.FormatSingle(m_value, null, NumberFormatInfo.GetInstance(provider)); + } + + public String ToString(String format) + { + return Number.FormatSingle(m_value, format, NumberFormatInfo.CurrentInfo); + } + + public String ToString(String format, IFormatProvider provider) + { + return Number.FormatSingle(m_value, format, NumberFormatInfo.GetInstance(provider)); + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) + { + return Number.TryFormatSingle(m_value, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); + } + + // Parses a float from a String in the given style. If + // a NumberFormatInfo isn't specified, the current culture's + // NumberFormatInfo is assumed. + // + // This method will not throw an OverflowException, but will return + // PositiveInfinity or NegativeInfinity for a number that is too + // large or too small. + // + public static float Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseSingle(s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo); + } + + public static float Parse(String s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseSingle(s, style, NumberFormatInfo.CurrentInfo); + } + + public static float Parse(String s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseSingle(s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.GetInstance(provider)); + } + + public static float Parse(String s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseSingle(s, style, NumberFormatInfo.GetInstance(provider)); + } + + public static float Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return Number.ParseSingle(s, style, NumberFormatInfo.GetInstance(provider)); + } + + public static Boolean TryParse(String s, out Single result) + { + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(ReadOnlySpan s, out float result) + { + return TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo, out result); + } + + public static Boolean TryParse(String s, NumberStyles style, IFormatProvider provider, out Single result) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out float result) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + private static Boolean TryParse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out Single result) + { + bool success = Number.TryParseSingle(s, style, info, out result); + if (!success) + { + ReadOnlySpan sTrim = s.Trim(); + if (StringSpanHelpers.Equals(sTrim, info.PositiveInfinitySymbol)) + { + result = PositiveInfinity; + } + else if (StringSpanHelpers.Equals(sTrim, info.NegativeInfinitySymbol)) + { + result = NegativeInfinity; + } + else if (StringSpanHelpers.Equals(sTrim, info.NaNSymbol)) + { + result = NaN; + } + else + { + return false; // We really failed + } + } + return true; + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.Single; + } + + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Single", "Char")); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(m_value); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return m_value; + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(m_value); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Single", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Span.NonGeneric.cs b/external/corefx/src/Common/src/CoreLib/System/Span.NonGeneric.cs new file mode 100644 index 0000000000..f6cd939a73 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Span.NonGeneric.cs @@ -0,0 +1,638 @@ +// 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.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using Internal.Runtime.CompilerServices; + +#if BIT64 +using nuint = System.UInt64; +#else +using nuint = System.UInt32; +#endif + +namespace System +{ + /// + /// Extension methods and non-generic helpers for Span, ReadOnlySpan, Memory, and ReadOnlyMemory. + /// + public static class Span + { + /// Creates a new over the portion of the target string. + /// The target string. + /// Thrown when is a null reference (Nothing in Visual Basic). + public static ReadOnlyMemory AsReadOnlyMemory(this string text) + { + if (text == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); + + return new ReadOnlyMemory(text, 0, text.Length); + } + + /// Creates a new over the portion of the target string. + /// The target string. + /// Thrown when is a null reference (Nothing in Visual Basic). + /// + /// Thrown when the specified index is not in range (<0 or >text.Length). + /// + public static ReadOnlyMemory AsReadOnlyMemory(this string text, int start) + { + if (text == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); + + if ((uint)start > (uint)text.Length) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); + + return new ReadOnlyMemory(text, start, text.Length - start); + } + + /// Creates a new over the portion of the target string. + /// The target string. + /// Thrown when is a null reference (Nothing in Visual Basic). + /// + /// Thrown when the specified index or is not in range. + /// + public static ReadOnlyMemory AsReadOnlyMemory(this string text, int start, int length) + { + if (text == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); + + if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); + + return new ReadOnlyMemory(text, start, length); + } + + /// Attempts to get the underlying from a . + /// The memory that may be wrapping a object. + /// The string. + /// The starting location in . + /// The number of items in . + /// + public static bool TryGetString(this ReadOnlyMemory readOnlyMemory, out string text, out int start, out int length) + { + if (readOnlyMemory.GetObjectStartLength(out int offset, out int count) is string s) + { + text = s; + start = offset; + length = count; + return true; + } + else + { + text = null; + start = 0; + length = 0; + return false; + } + } + + /// + /// Casts a Span of one primitive type to Span of bytes. + /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety. + /// + /// The source slice, of type . + /// + /// Thrown when contains pointers. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span AsBytes(this Span source) + where T : struct + { + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); + + return new Span( + ref Unsafe.As(ref MemoryMarshal.GetReference(source)), + checked(source.Length * Unsafe.SizeOf())); + } + + /// + /// Casts a ReadOnlySpan of one primitive type to ReadOnlySpan of bytes. + /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety. + /// + /// The source slice, of type . + /// + /// Thrown when contains pointers. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan AsBytes(this ReadOnlySpan source) + where T : struct + { + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); + + return new ReadOnlySpan( + ref Unsafe.As(ref MemoryMarshal.GetReference(source)), + checked(source.Length * Unsafe.SizeOf())); + } + + /// + /// Casts a Span of one primitive type to another primitive type . + /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety. + /// + /// + /// Supported only for platforms that support misaligned memory access. + /// + /// The source slice, of type . + /// + /// Thrown when or contains pointers. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span NonPortableCast(this Span source) + where TFrom : struct + where TTo : struct + { + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TFrom)); + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo)); + + return new Span( + ref Unsafe.As(ref source.DangerousGetPinnableReference()), + checked((int)((long)source.Length * Unsafe.SizeOf() / Unsafe.SizeOf()))); + } + + /// + /// Casts a ReadOnlySpan of one primitive type to another primitive type . + /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety. + /// + /// + /// Supported only for platforms that support misaligned memory access. + /// + /// The source slice, of type . + /// + /// Thrown when or contains pointers. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan NonPortableCast(this ReadOnlySpan source) + where TFrom : struct + where TTo : struct + { + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TFrom)); + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo)); + + return new ReadOnlySpan( + ref Unsafe.As(ref MemoryMarshal.GetReference(source)), + checked((int)((long)source.Length * Unsafe.SizeOf() / Unsafe.SizeOf()))); + } + + /// + /// Creates a new readonly span over the portion of the target string. + /// + /// The target string. + /// Thrown when is a null + /// reference (Nothing in Visual Basic). + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan AsReadOnlySpan(this string text) + { + if (text == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); + + return new ReadOnlySpan(ref text.GetRawStringData(), text.Length); + } + + /// + /// Creates a new readonly span over the portion of the target string. + /// + /// The target string. + /// Thrown when is a null + /// reference (Nothing in Visual Basic). + /// + /// + /// Thrown when the specified index is not in range (<0 or >text.Length). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan AsReadOnlySpan(this string text, int start) + { + if (text == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); + + if ((uint)start > (uint)text.Length) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); + + return new ReadOnlySpan(ref Unsafe.Add(ref text.GetRawStringData(), start), text.Length - start); + } + + /// + /// Creates a new readonly span over the portion of the target string. + /// + /// The target string. + /// Thrown when is a null + /// reference (Nothing in Visual Basic). + /// + /// + /// Thrown when the specified index or is not in range. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan AsReadOnlySpan(this string text, int start, int length) + { + if (text == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); + + if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); + + return new ReadOnlySpan(ref Unsafe.Add(ref text.GetRawStringData(), start), length); + } + + internal static unsafe void CopyTo(ref T destination, ref T source, int elementsCount) + { + if (Unsafe.AreSame(ref destination, ref source)) + return; + + if (elementsCount <= 1) + { + if (elementsCount == 1) + { + destination = source; + } + return; + } + + nuint byteCount = (nuint)elementsCount * (nuint)Unsafe.SizeOf(); + if (!RuntimeHelpers.IsReferenceOrContainsReferences()) + { + fixed (byte* pDestination = &Unsafe.As(ref destination)) + { + fixed (byte* pSource = &Unsafe.As(ref source)) + { + Buffer.Memmove(pDestination, pSource, byteCount); + } + } + } + else + { + RuntimeImports.RhBulkMoveWithWriteBarrier( + ref Unsafe.As(ref destination), + ref Unsafe.As(ref source), + byteCount); + } + } + + internal static unsafe void ClearWithoutReferences(ref byte b, nuint byteLength) + { + if (byteLength == 0) + return; + +#if CORECLR && (AMD64 || ARM64) + if (byteLength > 4096) goto PInvoke; + Unsafe.InitBlockUnaligned(ref b, 0, (uint)byteLength); + return; +#else + // TODO: Optimize other platforms to be on par with AMD64 CoreCLR + // Note: It's important that this switch handles lengths at least up to 22. + // See notes below near the main loop for why. + + // The switch will be very fast since it can be implemented using a jump + // table in assembly. See http://stackoverflow.com/a/449297/4077294 for more info. + + switch (byteLength) + { + case 1: + b = 0; + return; + case 2: + Unsafe.As(ref b) = 0; + return; + case 3: + Unsafe.As(ref b) = 0; + Unsafe.Add(ref b, 2) = 0; + return; + case 4: + Unsafe.As(ref b) = 0; + return; + case 5: + Unsafe.As(ref b) = 0; + Unsafe.Add(ref b, 4) = 0; + return; + case 6: + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 4)) = 0; + return; + case 7: + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 4)) = 0; + Unsafe.Add(ref b, 6) = 0; + return; + case 8: +#if BIT64 + Unsafe.As(ref b) = 0; +#else + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 4)) = 0; +#endif + return; + case 9: +#if BIT64 + Unsafe.As(ref b) = 0; +#else + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 4)) = 0; +#endif + Unsafe.Add(ref b, 8) = 0; + return; + case 10: +#if BIT64 + Unsafe.As(ref b) = 0; +#else + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 4)) = 0; +#endif + Unsafe.As(ref Unsafe.Add(ref b, 8)) = 0; + return; + case 11: +#if BIT64 + Unsafe.As(ref b) = 0; +#else + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 4)) = 0; +#endif + Unsafe.As(ref Unsafe.Add(ref b, 8)) = 0; + Unsafe.Add(ref b, 10) = 0; + return; + case 12: +#if BIT64 + Unsafe.As(ref b) = 0; +#else + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 4)) = 0; +#endif + Unsafe.As(ref Unsafe.Add(ref b, 8)) = 0; + return; + case 13: +#if BIT64 + Unsafe.As(ref b) = 0; +#else + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 4)) = 0; +#endif + Unsafe.As(ref Unsafe.Add(ref b, 8)) = 0; + Unsafe.Add(ref b, 12) = 0; + return; + case 14: +#if BIT64 + Unsafe.As(ref b) = 0; +#else + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 4)) = 0; +#endif + Unsafe.As(ref Unsafe.Add(ref b, 8)) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 12)) = 0; + return; + case 15: +#if BIT64 + Unsafe.As(ref b) = 0; +#else + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 4)) = 0; +#endif + Unsafe.As(ref Unsafe.Add(ref b, 8)) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 12)) = 0; + Unsafe.Add(ref b, 14) = 0; + return; + case 16: +#if BIT64 + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 8)) = 0; +#else + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 4)) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 8)) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 12)) = 0; +#endif + return; + case 17: +#if BIT64 + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 8)) = 0; +#else + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 4)) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 8)) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 12)) = 0; +#endif + Unsafe.Add(ref b, 16) = 0; + return; + case 18: +#if BIT64 + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 8)) = 0; +#else + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 4)) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 8)) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 12)) = 0; +#endif + Unsafe.As(ref Unsafe.Add(ref b, 16)) = 0; + return; + case 19: +#if BIT64 + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 8)) = 0; +#else + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 4)) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 8)) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 12)) = 0; +#endif + Unsafe.As(ref Unsafe.Add(ref b, 16)) = 0; + Unsafe.Add(ref b, 18) = 0; + return; + case 20: +#if BIT64 + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 8)) = 0; +#else + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 4)) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 8)) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 12)) = 0; +#endif + Unsafe.As(ref Unsafe.Add(ref b, 16)) = 0; + return; + case 21: +#if BIT64 + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 8)) = 0; +#else + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 4)) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 8)) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 12)) = 0; +#endif + Unsafe.As(ref Unsafe.Add(ref b, 16)) = 0; + Unsafe.Add(ref b, 20) = 0; + return; + case 22: +#if BIT64 + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 8)) = 0; +#else + Unsafe.As(ref b) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 4)) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 8)) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 12)) = 0; +#endif + Unsafe.As(ref Unsafe.Add(ref b, 16)) = 0; + Unsafe.As(ref Unsafe.Add(ref b, 20)) = 0; + return; + } + + // P/Invoke into the native version for large lengths + if (byteLength >= 512) goto PInvoke; + + nuint i = 0; // byte offset at which we're copying + + if ((Unsafe.As(ref b) & 3) != 0) + { + if ((Unsafe.As(ref b) & 1) != 0) + { + Unsafe.AddByteOffset(ref b, i) = 0; + i += 1; + if ((Unsafe.As(ref b) & 2) != 0) + goto IntAligned; + } + Unsafe.As(ref Unsafe.AddByteOffset(ref b, i)) = 0; + i += 2; + } + + IntAligned: + + // On 64-bit IntPtr.Size == 8, so we want to advance to the next 8-aligned address. If + // (int)b % 8 is 0, 5, 6, or 7, we will already have advanced by 0, 3, 2, or 1 + // bytes to the next aligned address (respectively), so do nothing. On the other hand, + // if it is 1, 2, 3, or 4 we will want to copy-and-advance another 4 bytes until + // we're aligned. + // The thing 1, 2, 3, and 4 have in common that the others don't is that if you + // subtract one from them, their 3rd lsb will not be set. Hence, the below check. + + if (((Unsafe.As(ref b) - 1) & 4) == 0) + { + Unsafe.As(ref Unsafe.AddByteOffset(ref b, i)) = 0; + i += 4; + } + + nuint end = byteLength - 16; + byteLength -= i; // lower 4 bits of byteLength represent how many bytes are left *after* the unrolled loop + + // We know due to the above switch-case that this loop will always run 1 iteration; max + // bytes we clear before checking is 23 (7 to align the pointers, 16 for 1 iteration) so + // the switch handles lengths 0-22. + Debug.Assert(end >= 7 && i <= end); + + // This is separated out into a different variable, so the i + 16 addition can be + // performed at the start of the pipeline and the loop condition does not have + // a dependency on the writes. + nuint counter; + + do + { + counter = i + 16; + + // This loop looks very costly since there appear to be a bunch of temporary values + // being created with the adds, but the jit (for x86 anyways) will convert each of + // these to use memory addressing operands. + + // So the only cost is a bit of code size, which is made up for by the fact that + // we save on writes to b. + +#if BIT64 + Unsafe.As(ref Unsafe.AddByteOffset(ref b, i)) = 0; + Unsafe.As(ref Unsafe.AddByteOffset(ref b, i + 8)) = 0; +#else + Unsafe.As(ref Unsafe.AddByteOffset(ref b, i)) = 0; + Unsafe.As(ref Unsafe.AddByteOffset(ref b, i + 4)) = 0; + Unsafe.As(ref Unsafe.AddByteOffset(ref b, i + 8)) = 0; + Unsafe.As(ref Unsafe.AddByteOffset(ref b, i + 12)) = 0; +#endif + + i = counter; + + // See notes above for why this wasn't used instead + // i += 16; + } + while (counter <= end); + + if ((byteLength & 8) != 0) + { +#if BIT64 + Unsafe.As(ref Unsafe.AddByteOffset(ref b, i)) = 0; +#else + Unsafe.As(ref Unsafe.AddByteOffset(ref b, i)) = 0; + Unsafe.As(ref Unsafe.AddByteOffset(ref b, i + 4)) = 0; +#endif + i += 8; + } + if ((byteLength & 4) != 0) + { + Unsafe.As(ref Unsafe.AddByteOffset(ref b, i)) = 0; + i += 4; + } + if ((byteLength & 2) != 0) + { + Unsafe.As(ref Unsafe.AddByteOffset(ref b, i)) = 0; + i += 2; + } + if ((byteLength & 1) != 0) + { + Unsafe.AddByteOffset(ref b, i) = 0; + // We're not using i after this, so not needed + // i += 1; + } + + return; +#endif + + PInvoke: + RuntimeImports.RhZeroMemory(ref b, byteLength); + } + + internal static unsafe void ClearWithReferences(ref IntPtr ip, nuint pointerSizeLength) + { + if (pointerSizeLength == 0) + return; + + // TODO: Perhaps do switch casing to improve small size perf + + nuint i = 0; + nuint n = 0; + while ((n = i + 8) <= (pointerSizeLength)) + { + Unsafe.AddByteOffset(ref ip, (i + 0) * (nuint)sizeof(IntPtr)) = default(IntPtr); + Unsafe.AddByteOffset(ref ip, (i + 1) * (nuint)sizeof(IntPtr)) = default(IntPtr); + Unsafe.AddByteOffset(ref ip, (i + 2) * (nuint)sizeof(IntPtr)) = default(IntPtr); + Unsafe.AddByteOffset(ref ip, (i + 3) * (nuint)sizeof(IntPtr)) = default(IntPtr); + Unsafe.AddByteOffset(ref ip, (i + 4) * (nuint)sizeof(IntPtr)) = default(IntPtr); + Unsafe.AddByteOffset(ref ip, (i + 5) * (nuint)sizeof(IntPtr)) = default(IntPtr); + Unsafe.AddByteOffset(ref ip, (i + 6) * (nuint)sizeof(IntPtr)) = default(IntPtr); + Unsafe.AddByteOffset(ref ip, (i + 7) * (nuint)sizeof(IntPtr)) = default(IntPtr); + i = n; + } + if ((n = i + 4) <= (pointerSizeLength)) + { + Unsafe.AddByteOffset(ref ip, (i + 0) * (nuint)sizeof(IntPtr)) = default(IntPtr); + Unsafe.AddByteOffset(ref ip, (i + 1) * (nuint)sizeof(IntPtr)) = default(IntPtr); + Unsafe.AddByteOffset(ref ip, (i + 2) * (nuint)sizeof(IntPtr)) = default(IntPtr); + Unsafe.AddByteOffset(ref ip, (i + 3) * (nuint)sizeof(IntPtr)) = default(IntPtr); + i = n; + } + if ((n = i + 2) <= (pointerSizeLength)) + { + Unsafe.AddByteOffset(ref ip, (i + 0) * (nuint)sizeof(IntPtr)) = default(IntPtr); + Unsafe.AddByteOffset(ref ip, (i + 1) * (nuint)sizeof(IntPtr)) = default(IntPtr); + i = n; + } + if ((i + 1) <= (pointerSizeLength)) + { + Unsafe.AddByteOffset(ref ip, (i + 0) * (nuint)sizeof(IntPtr)) = default(IntPtr); + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Span.cs b/external/corefx/src/Common/src/CoreLib/System/Span.cs new file mode 100644 index 0000000000..5a813174d9 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Span.cs @@ -0,0 +1,455 @@ +// 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.ComponentModel; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; +using Internal.Runtime.CompilerServices; + +#pragma warning disable 0809 //warning CS0809: Obsolete member 'Span.Equals(object)' overrides non-obsolete member 'object.Equals(object)' + +#if BIT64 +using nuint = System.UInt64; +#else +using nuint = System.UInt32; +#endif + +namespace System +{ + /// + /// Span represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed + /// or native memory, or to memory allocated on the stack. It is type- and memory-safe. + /// + [DebuggerTypeProxy(typeof(SpanDebugView<>))] + [DebuggerDisplay("{DebuggerDisplay,nq}")] + [NonVersionable] + public readonly ref struct Span + { + /// A byref or a native ptr. + internal readonly ByReference _pointer; + /// The number of elements this Span contains. +#if PROJECTN + [Bound] +#endif + private readonly int _length; + + /// + /// Creates a new span over the entirety of the target array. + /// + /// The target array. + /// Thrown when is a null + /// reference (Nothing in Visual Basic). + /// Thrown when is covariant and array's type is not exactly T[]. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span(T[] array) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + if (default(T) == null && array.GetType() != typeof(T[])) + ThrowHelper.ThrowArrayTypeMismatchException(); + + _pointer = new ByReference(ref Unsafe.As(ref array.GetRawSzArrayData())); + _length = array.Length; + } + + /// + /// Creates a new span over the portion of the target array beginning + /// at 'start' index and ending at 'end' index (exclusive). + /// + /// The target array. + /// The index at which to begin the span. + /// The number of items in the span. + /// Thrown when is a null + /// reference (Nothing in Visual Basic). + /// Thrown when is covariant and array's type is not exactly T[]. + /// + /// Thrown when the specified or end index is not in the range (<0 or >=Length). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span(T[] array, int start, int length) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + if (default(T) == null && array.GetType() != typeof(T[])) + ThrowHelper.ThrowArrayTypeMismatchException(); + if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + _pointer = new ByReference(ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), start)); + _length = length; + } + + /// + /// Creates a new span over the target unmanaged buffer. Clearly this + /// is quite dangerous, because we are creating arbitrarily typed T's + /// out of a void*-typed block of memory. And the length is not checked. + /// But if this creation is correct, then all subsequent uses are correct. + /// + /// An unmanaged pointer to memory. + /// The number of elements the memory contains. + /// + /// Thrown when is reference type or contains pointers and hence cannot be stored in unmanaged memory. + /// + /// + /// Thrown when the specified is negative. + /// + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe Span(void* pointer, int length) + { + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); + if (length < 0) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + _pointer = new ByReference(ref Unsafe.As(ref *(byte*)pointer)); + _length = length; + } + + /// + /// Create a new span over a portion of a regular managed object. This can be useful + /// if part of a managed object represents a "fixed array." This is dangerous because neither the + /// is checked, nor being null, nor the fact that + /// "rawPointer" actually lies within . + /// + /// The managed object that contains the data to span over. + /// A reference to data within that object. + /// The number of elements the memory contains. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [EditorBrowsable(EditorBrowsableState.Never)] + public static Span DangerousCreate(object obj, ref T objectData, int length) => new Span(ref objectData, length); + + // Constructor for internal use only. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Span(ref T ptr, int length) + { + Debug.Assert(length >= 0); + + _pointer = new ByReference(ref ptr); + _length = length; + } + + //Debugger Display = {T[length]} + private string DebuggerDisplay => string.Format("{{{0}[{1}]}}", typeof(T).Name, _length); + + /// + /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element + /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [EditorBrowsable(EditorBrowsableState.Never)] + internal ref T DangerousGetPinnableReference() + { + return ref _pointer.Value; + } + + /// + /// The number of items in the span. + /// + public int Length + { + [NonVersionable] + get + { + return _length; + } + } + + /// + /// Returns true if Length is 0. + /// + public bool IsEmpty + { + [NonVersionable] + get + { + return _length == 0; + } + } + + /// + /// Returns a reference to specified element of the Span. + /// + /// + /// + /// + /// Thrown when index less than 0 or index greater than or equal to Length + /// + public ref T this[int index] + { +#if PROJECTN + [BoundsChecking] + get + { + return ref Unsafe.Add(ref _pointer.Value, index); + } +#else + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [NonVersionable] + get + { + if ((uint)index >= (uint)_length) + ThrowHelper.ThrowIndexOutOfRangeException(); + return ref Unsafe.Add(ref _pointer.Value, index); + } +#endif + } + + /// + /// Clears the contents of this span. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + Span.ClearWithReferences(ref Unsafe.As(ref _pointer.Value), (nuint)_length * (nuint)(Unsafe.SizeOf() / sizeof(nuint))); + } + else + { + Span.ClearWithoutReferences(ref Unsafe.As(ref _pointer.Value), (nuint)_length * (nuint)Unsafe.SizeOf()); + } + } + + /// + /// Fills the contents of this span with the given value. + /// + public void Fill(T value) + { + if (Unsafe.SizeOf() == 1) + { + uint length = (uint)_length; + if (length == 0) + return; + + T tmp = value; // Avoid taking address of the "value" argument. It would regress performance of the loop below. + Unsafe.InitBlockUnaligned(ref Unsafe.As(ref _pointer.Value), Unsafe.As(ref tmp), length); + } + else + { + // Do all math as nuint to avoid unnecessary 64->32->64 bit integer truncations + nuint length = (uint)_length; + if (length == 0) + return; + + ref T r = ref DangerousGetPinnableReference(); + + // TODO: Create block fill for value types of power of two sizes e.g. 2,4,8,16 + + nuint elementSize = (uint)Unsafe.SizeOf(); + nuint i = 0; + for (; i < (length & ~(nuint)7); i += 8) + { + Unsafe.AddByteOffset(ref r, (i + 0) * elementSize) = value; + Unsafe.AddByteOffset(ref r, (i + 1) * elementSize) = value; + Unsafe.AddByteOffset(ref r, (i + 2) * elementSize) = value; + Unsafe.AddByteOffset(ref r, (i + 3) * elementSize) = value; + Unsafe.AddByteOffset(ref r, (i + 4) * elementSize) = value; + Unsafe.AddByteOffset(ref r, (i + 5) * elementSize) = value; + Unsafe.AddByteOffset(ref r, (i + 6) * elementSize) = value; + Unsafe.AddByteOffset(ref r, (i + 7) * elementSize) = value; + } + if (i < (length & ~(nuint)3)) + { + Unsafe.AddByteOffset(ref r, (i + 0) * elementSize) = value; + Unsafe.AddByteOffset(ref r, (i + 1) * elementSize) = value; + Unsafe.AddByteOffset(ref r, (i + 2) * elementSize) = value; + Unsafe.AddByteOffset(ref r, (i + 3) * elementSize) = value; + i += 4; + } + for (; i < length; i++) + { + Unsafe.AddByteOffset(ref r, i * elementSize) = value; + } + } + } + + /// + /// Copies the contents of this span into destination span. If the source + /// and destinations overlap, this method behaves as if the original values in + /// a temporary location before the destination is overwritten. + /// + /// The span to copy items into. + /// + /// Thrown when the destination Span is shorter than the source Span. + /// + public void CopyTo(Span destination) + { + if (!TryCopyTo(destination)) + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + } + + /// + /// Copies the contents of this span into destination span. If the source + /// and destinations overlap, this method behaves as if the original values in + /// a temporary location before the destination is overwritten. + /// + /// The span to copy items into. + /// If the destination span is shorter than the source span, this method + /// return false and no data is written to the destination. + public bool TryCopyTo(Span destination) + { + if ((uint)_length > (uint)destination.Length) + return false; + + Span.CopyTo(ref destination._pointer.Value, ref _pointer.Value, _length); + return true; + } + + /// + /// Returns true if left and right point at the same memory and have the same length. Note that + /// this does *not* check to see if the *contents* are equal. + /// + public static bool operator ==(Span left, Span right) + { + return left._length == right._length && Unsafe.AreSame(ref left._pointer.Value, ref right._pointer.Value); + } + + /// + /// Returns false if left and right point at the same memory and have the same length. Note that + /// this does *not* check to see if the *contents* are equal. + /// + public static bool operator !=(Span left, Span right) => !(left == right); + + /// + /// This method is not supported as spans cannot be boxed. To compare two spans, use operator==. + /// + /// Always thrown by this method. + /// + /// + [Obsolete("Equals() on Span will always throw an exception. Use == instead.")] + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) + { + throw new NotSupportedException(SR.NotSupported_CannotCallEqualsOnSpan); + } + + /// + /// This method is not supported as spans cannot be boxed. + /// + /// Always thrown by this method. + /// + /// + [Obsolete("GetHashCode() on Span will always throw an exception.")] + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() + { + throw new NotSupportedException(SR.NotSupported_CannotCallGetHashCodeOnSpan); + } + + /// + /// Defines an implicit conversion of an array to a + /// + public static implicit operator Span(T[] array) => array != null ? new Span(array) : default; + + /// + /// Defines an implicit conversion of a to a + /// + public static implicit operator Span(ArraySegment arraySegment) + => arraySegment.Array != null ? new Span(arraySegment.Array, arraySegment.Offset, arraySegment.Count) : default; + + /// + /// Defines an implicit conversion of a to a + /// + public static implicit operator ReadOnlySpan(Span span) => new ReadOnlySpan(ref span._pointer.Value, span._length); + + /// + /// Forms a slice out of the given span, beginning at 'start'. + /// + /// The index at which to begin this slice. + /// + /// Thrown when the specified index is not in range (<0 or >=Length). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span Slice(int start) + { + if ((uint)start > (uint)_length) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + return new Span(ref Unsafe.Add(ref _pointer.Value, start), _length - start); + } + + /// + /// Forms a slice out of the given span, beginning at 'start', of given length + /// + /// The index at which to begin this slice. + /// The desired length for the slice (exclusive). + /// + /// Thrown when the specified or end index is not in range (<0 or >=Length). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span Slice(int start, int length) + { + if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + return new Span(ref Unsafe.Add(ref _pointer.Value, start), length); + } + + /// + /// Copies the contents of this span into a new array. This heap + /// allocates, so should generally be avoided, however it is sometimes + /// necessary to bridge the gap with APIs written in terms of arrays. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T[] ToArray() + { + if (_length == 0) + return Array.Empty(); + + var destination = new T[_length]; + Span.CopyTo(ref Unsafe.As(ref destination.GetRawSzArrayData()), ref _pointer.Value, _length); + return destination; + } + + // + /// Returns an empty + /// + public static Span Empty => default(Span); + + /// Gets an enumerator for this span. + public Enumerator GetEnumerator() => new Enumerator(this); + + /// Enumerates the elements of a . + public ref struct Enumerator + { + /// The span being enumerated. + private readonly Span _span; + /// The next index to yield. + private int _index; + + /// Initialize the enumerator. + /// The span to enumerate. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Enumerator(Span span) + { + _span = span; + _index = -1; + } + + /// Advances the enumerator to the next element of the span. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + int index = _index + 1; + if (index < _span.Length) + { + _index = index; + return true; + } + + return false; + } + + /// Gets the element at the current position of the enumerator. + public ref T Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref _span[_index]; + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/SpanDebugView.cs b/external/corefx/src/Common/src/CoreLib/System/SpanDebugView.cs new file mode 100644 index 0000000000..caa12ef9ed --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/SpanDebugView.cs @@ -0,0 +1,28 @@ +// 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.Diagnostics; +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace System +{ + internal sealed class SpanDebugView + { + private readonly T[] _array; + + public SpanDebugView(Span span) + { + _array = span.ToArray(); + } + + public SpanDebugView(ReadOnlySpan span) + { + _array = span.ToArray(); + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public T[] Items => _array; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/StackOverflowException.cs b/external/corefx/src/Common/src/CoreLib/System/StackOverflowException.cs new file mode 100644 index 0000000000..6f954cc75a --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/StackOverflowException.cs @@ -0,0 +1,44 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: The exception class for stack overflow. +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class StackOverflowException : SystemException + { + public StackOverflowException() + : base(SR.Arg_StackOverflowException) + { + HResult = HResults.COR_E_STACKOVERFLOW; + } + + public StackOverflowException(String message) + : base(message) + { + HResult = HResults.COR_E_STACKOVERFLOW; + } + + public StackOverflowException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_STACKOVERFLOW; + } + + internal StackOverflowException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/String.Searching.cs b/external/corefx/src/Common/src/CoreLib/System/String.Searching.cs similarity index 59% rename from external/corert/src/System.Private.CoreLib/src/System/String.Searching.cs rename to external/corefx/src/Common/src/CoreLib/System/String.Searching.cs index b93dfc0aef..b0ba92b6e5 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/String.Searching.cs +++ b/external/corefx/src/Common/src/CoreLib/System/String.Searching.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Globalization; +using System.Runtime.InteropServices; namespace System { @@ -13,10 +14,24 @@ namespace System return (IndexOf(value, StringComparison.Ordinal) >= 0); } - // Returns the index of the first occurrence of value in the current instance. + public bool Contains(string value, StringComparison comparisonType) + { + return (IndexOf(value, comparisonType) >= 0); + } + + public bool Contains(char value) + { + return IndexOf(value) != -1; + } + + public bool Contains(char value, StringComparison comparisonType) + { + return IndexOf(value, comparisonType) != -1; + } + + // Returns the index of the first occurrence of a specified character in the current instance. // The search starts at startIndex and runs thorough the next count characters. // - public int IndexOf(char value) { return IndexOf(value, 0, this.Length); @@ -27,6 +42,33 @@ namespace System return IndexOf(value, startIndex, this.Length - startIndex); } + public int IndexOf(char value, StringComparison comparisonType) + { + switch (comparisonType) + { + case StringComparison.CurrentCulture: + return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this, value, CompareOptions.None); + + case StringComparison.CurrentCultureIgnoreCase: + return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this, value, CompareOptions.IgnoreCase); + + case StringComparison.InvariantCulture: + return CompareInfo.Invariant.IndexOf(this, value, CompareOptions.None); + + case StringComparison.InvariantCultureIgnoreCase: + return CompareInfo.Invariant.IndexOf(this, value, CompareOptions.IgnoreCase); + + case StringComparison.Ordinal: + return CompareInfo.Invariant.IndexOf(this, value, CompareOptions.Ordinal); + + case StringComparison.OrdinalIgnoreCase: + return CompareInfo.Invariant.IndexOf(this, value, CompareOptions.OrdinalIgnoreCase); + + default: + throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType)); + } + } + public unsafe int IndexOf(char value, int startIndex, int count) { if (startIndex < 0 || startIndex > Length) @@ -72,7 +114,6 @@ namespace System // Returns the index of the first occurrence of any specified character in the current instance. // The search starts at startIndex and runs to startIndex + count - 1. // - public int IndexOfAny(char[] anyOf) { return IndexOfAny(anyOf, 0, this.Length); @@ -83,34 +124,118 @@ namespace System return IndexOfAny(anyOf, startIndex, this.Length - startIndex); } - public unsafe int IndexOfAny(char[] anyOf, int startIndex, int count) + public int IndexOfAny(char[] anyOf, int startIndex, int count) { if (anyOf == null) throw new ArgumentNullException(nameof(anyOf)); - if (startIndex < 0 || startIndex > Length) + if ((uint)startIndex > (uint)Length) throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); - if (count < 0 || count > Length - startIndex) + if ((uint)count > (uint)(Length - startIndex)) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count); + if (anyOf.Length == 2) + { + // Very common optimization for directory separators (/, \), quotes (", '), brackets, etc + return IndexOfAny(anyOf[0], anyOf[1], startIndex, count); + } + else if (anyOf.Length == 3) + { + return IndexOfAny(anyOf[0], anyOf[1], anyOf[2], startIndex, count); + } + else if (anyOf.Length > 3) + { + return IndexOfCharArray(anyOf, startIndex, count); + } + else if (anyOf.Length == 1) + { + return IndexOf(anyOf[0], startIndex, count); + } + else // anyOf.Length == 0 + { + return -1; + } + } + + private unsafe int IndexOfAny(char value1, char value2, int startIndex, int count) + { + fixed (char* pChars = &_firstChar) + { + char* pCh = pChars + startIndex; + + while (count > 0) + { + char c = *pCh; + + if (c == value1 || c == value2) + return (int)(pCh - pChars); + + // Possibly reads outside of count and can include null terminator + // Handled in the return logic + c = *(pCh + 1); + + if (c == value1 || c == value2) + return (count == 1 ? -1 : (int)(pCh - pChars) + 1); + + pCh += 2; + count -= 2; + } + + return -1; + } + } + + private unsafe int IndexOfAny(char value1, char value2, char value3, int startIndex, int count) + { + fixed (char* pChars = &_firstChar) + { + char* pCh = pChars + startIndex; + + while (count > 0) + { + char c = *pCh; + + if (c == value1 || c == value2 || c == value3) + return (int)(pCh - pChars); + + pCh++; + count--; + } + + return -1; + } + } + + private unsafe int IndexOfCharArray(char[] anyOf, int startIndex, int count) + { // use probabilistic map, see InitializeProbabilisticMap - uint* charMap = stackalloc uint[PROBABILISTICMAP_SIZE]; + ProbabilisticMap map = default(ProbabilisticMap); + uint* charMap = (uint*)↦ + InitializeProbabilisticMap(charMap, anyOf); fixed (char* pChars = &_firstChar) { char* pCh = pChars + startIndex; - for (int i = 0; i < count; i++) - { - char thisChar = *pCh++; - if (ProbablyContains(charMap, thisChar)) - if (ArrayContains(thisChar, anyOf) >= 0) - return i + startIndex; - } - } - return -1; + while (count > 0) + { + int thisChar = *pCh; + + if (IsCharBitSet(charMap, (byte)thisChar) && + IsCharBitSet(charMap, (byte)(thisChar >> 8)) && + ArrayContains((char)thisChar, anyOf)) + { + return (int)(pCh - pChars); + } + + count--; + pCh++; + } + + return -1; + } } private const int PROBABILISTICMAP_BLOCK_INDEX_MASK = 0x7; @@ -127,52 +252,57 @@ namespace System // in each byte in the character is used to index into this map to get the // right block, the value of the remaining 5 msb are used as the bit position // inside this block. - private static unsafe void InitializeProbabilisticMap(uint* charMap, char[] anyOf) + private static unsafe void InitializeProbabilisticMap(uint* charMap, ReadOnlySpan anyOf) { + bool hasAscii = false; + uint* charMapLocal = charMap; // https://github.com/dotnet/coreclr/issues/14264 + for (int i = 0; i < anyOf.Length; ++i) { - uint hi, lo; - char c = anyOf[i]; - hi = ((uint)c >> 8) & 0xFF; - lo = (uint)c & 0xFF; + int c = anyOf[i]; - uint* value = &charMap[lo & PROBABILISTICMAP_BLOCK_INDEX_MASK]; - *value |= (1u << (int)(lo >> PROBABILISTICMAP_BLOCK_INDEX_SHIFT)); + // Map low bit + SetCharBit(charMapLocal, (byte)c); - value = &charMap[hi & PROBABILISTICMAP_BLOCK_INDEX_MASK]; - *value |= (1u << (int)(hi >> PROBABILISTICMAP_BLOCK_INDEX_SHIFT)); + // Map high bit + c >>= 8; + + if (c == 0) + { + hasAscii = true; + } + else + { + SetCharBit(charMapLocal, (byte)c); + } + } + + if (hasAscii) + { + // Common to search for ASCII symbols. Just set the high value once. + charMapLocal[0] |= 1u; } } - // Use the probabilistic map to decide if the character value exists in the - // map. When this method return false, we are certain the character doesn't - // exist, however a true return means it *may* exist. - private static unsafe bool ProbablyContains(uint* charMap, char searchValue) + private static bool ArrayContains(char searchChar, char[] anyOf) { - uint lo, hi; - - lo = (uint)searchValue & 0xFF; - uint value = charMap[lo & PROBABILISTICMAP_BLOCK_INDEX_MASK]; - - if ((value & (1u << (int)(lo >> PROBABILISTICMAP_BLOCK_INDEX_SHIFT))) != 0) + for (int i = 0; i < anyOf.Length; i++) { - hi = ((uint)searchValue >> 8) & 0xFF; - value = charMap[hi & PROBABILISTICMAP_BLOCK_INDEX_MASK]; - - return (value & (1u << (int)(hi >> PROBABILISTICMAP_BLOCK_INDEX_SHIFT))) != 0; + if (anyOf[i] == searchChar) + return true; } return false; } - private static int ArrayContains(char searchChar, char[] anyOf) + private unsafe static bool IsCharBitSet(uint* charMap, byte value) { - for (int i = 0; i < anyOf.Length; i++) - { - if (anyOf[i] == searchChar) - return i; - } - return -1; + return (charMap[value & PROBABILISTICMAP_BLOCK_INDEX_MASK] & (1u << (value >> PROBABILISTICMAP_BLOCK_INDEX_SHIFT))) != 0; + } + + private unsafe static void SetCharBit(uint* charMap, byte value) + { + charMap[value & PROBABILISTICMAP_BLOCK_INDEX_MASK] |= 1u << (value >> PROBABILISTICMAP_BLOCK_INDEX_SHIFT); } public int IndexOf(String value) @@ -180,6 +310,11 @@ namespace System return IndexOf(value, StringComparison.CurrentCulture); } + public int IndexOf(String value, int startIndex) + { + return IndexOf(value, startIndex, StringComparison.CurrentCulture); + } + public int IndexOf(String value, int startIndex, int count) { if (startIndex < 0 || startIndex > this.Length) @@ -195,11 +330,6 @@ namespace System return IndexOf(value, startIndex, count, StringComparison.CurrentCulture); } - public int IndexOf(String value, int startIndex) - { - return IndexOf(value, startIndex, StringComparison.CurrentCulture); - } - public int IndexOf(String value, StringComparison comparisonType) { return IndexOf(value, 0, this.Length, comparisonType); @@ -225,16 +355,22 @@ namespace System switch (comparisonType) { case StringComparison.CurrentCulture: - return FormatProvider.IndexOf(this, value, startIndex, count); + return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.None); case StringComparison.CurrentCultureIgnoreCase: - return FormatProvider.IndexOfIgnoreCase(this, value, startIndex, count); + return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.IgnoreCase); + + case StringComparison.InvariantCulture: + return CompareInfo.Invariant.IndexOf(this, value, startIndex, count, CompareOptions.None); + + case StringComparison.InvariantCultureIgnoreCase: + return CompareInfo.Invariant.IndexOf(this, value, startIndex, count, CompareOptions.IgnoreCase); case StringComparison.Ordinal: - return FormatProvider.OrdinalIndexOf(this, value, startIndex, count); + return CompareInfo.Invariant.IndexOfOrdinal(this, value, startIndex, count, ignoreCase: false); case StringComparison.OrdinalIgnoreCase: - return FormatProvider.OrdinalIndexOfIgnoreCase(this, value, startIndex, count); + return CompareInfo.Invariant.IndexOfOrdinal(this, value, startIndex, count, ignoreCase: true); default: throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType)); @@ -246,7 +382,6 @@ namespace System // The character at position startIndex is included in the search. startIndex is the larger // index within the string. // - public int LastIndexOf(char value) { return LastIndexOf(value, this.Length - 1, this.Length); @@ -308,14 +443,11 @@ namespace System // The character at position startIndex is included in the search. startIndex is the larger // index within the string. // - - //ForceInline ... Jit can't recognize String.get_Length to determine that this is "fluff" public int LastIndexOfAny(char[] anyOf) { return LastIndexOfAny(anyOf, this.Length - 1, this.Length); } - //ForceInline ... Jit can't recognize String.get_Length to determine that this is "fluff" public int LastIndexOfAny(char[] anyOf, int startIndex) { return LastIndexOfAny(anyOf, startIndex, startIndex + 1); @@ -329,32 +461,59 @@ namespace System if (Length == 0) return -1; - if ((startIndex < 0) || (startIndex >= Length)) + if ((uint)startIndex >= (uint)Length) + { throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); + } if ((count < 0) || ((count - 1) > startIndex)) { throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count); } + if (anyOf.Length > 1) + { + return LastIndexOfCharArray(anyOf, startIndex, count); + } + else if (anyOf.Length == 1) + { + return LastIndexOf(anyOf[0], startIndex, count); + } + else // anyOf.Length == 0 + { + return -1; + } + } + + private unsafe int LastIndexOfCharArray(char[] anyOf, int startIndex, int count) + { // use probabilistic map, see InitializeProbabilisticMap - uint* charMap = stackalloc uint[PROBABILISTICMAP_SIZE]; + ProbabilisticMap map = default(ProbabilisticMap); + uint* charMap = (uint*)↦ + InitializeProbabilisticMap(charMap, anyOf); fixed (char* pChars = &_firstChar) { char* pCh = pChars + startIndex; - for (int i = 0; i < count; i++) + while (count > 0) { - char thisChar = *pCh--; - if (ProbablyContains(charMap, thisChar)) - if (ArrayContains(thisChar, anyOf) >= 0) - return startIndex - i; - } - } + int thisChar = *pCh; - return -1; + if (IsCharBitSet(charMap, (byte)thisChar) && + IsCharBitSet(charMap, (byte)(thisChar >> 8)) && + ArrayContains((char)thisChar, anyOf)) + { + return (int)(pCh - pChars); + } + + count--; + pCh--; + } + + return -1; + } } // Returns the index of the last occurrence of any character in value in the current instance. @@ -413,30 +572,40 @@ namespace System count--; } - // If we are looking for nothing, just return 0 - if (value.Length == 0 && count >= 0 && startIndex - count + 1 >= 0) - return startIndex; - // 2nd half of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0. if (count < 0 || startIndex - count + 1 < 0) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count); + // If we are looking for nothing, just return startIndex + if (value.Length == 0) + return startIndex; + switch (comparisonType) { case StringComparison.CurrentCulture: - return FormatProvider.LastIndexOf(this, value, startIndex, count); + return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(this, value, startIndex, count, CompareOptions.None); case StringComparison.CurrentCultureIgnoreCase: - return FormatProvider.LastIndexOfIgnoreCase(this, value, startIndex, count); + return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(this, value, startIndex, count, CompareOptions.IgnoreCase); + + case StringComparison.InvariantCulture: + return CompareInfo.Invariant.LastIndexOf(this, value, startIndex, count, CompareOptions.None); + + case StringComparison.InvariantCultureIgnoreCase: + return CompareInfo.Invariant.LastIndexOf(this, value, startIndex, count, CompareOptions.IgnoreCase); case StringComparison.Ordinal: - return FormatProvider.OrdinalLastIndexOf(this, value, startIndex, count); + return CompareInfo.Invariant.LastIndexOfOrdinal(this, value, startIndex, count, ignoreCase: false); case StringComparison.OrdinalIgnoreCase: - return FormatProvider.OrdinalLastIndexOfIgnoreCase(this, value, startIndex, count); + return CompareInfo.Invariant.LastIndexOfOrdinal(this, value, startIndex, count, ignoreCase: true); + default: throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType)); } } + + [StructLayout(LayoutKind.Explicit, Size = PROBABILISTICMAP_SIZE * sizeof(uint))] + private struct ProbabilisticMap { } } } diff --git a/external/corefx/src/Common/src/CoreLib/System/StringComparer.cs b/external/corefx/src/Common/src/CoreLib/System/StringComparer.cs new file mode 100644 index 0000000000..73c013599d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/StringComparer.cs @@ -0,0 +1,352 @@ +// 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.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public abstract class StringComparer : IComparer, IEqualityComparer, IComparer, IEqualityComparer + { + private static readonly CultureAwareComparer s_invariantCulture = new CultureAwareComparer(CultureInfo.InvariantCulture, false); + private static readonly CultureAwareComparer s_invariantCultureIgnoreCase = new CultureAwareComparer(CultureInfo.InvariantCulture, true); + private static readonly OrdinalCaseSensitiveComparer s_ordinal = new OrdinalCaseSensitiveComparer(); + private static readonly OrdinalIgnoreCaseComparer s_ordinalIgnoreCase = new OrdinalIgnoreCaseComparer(); + + public static StringComparer InvariantCulture + { + get + { + return s_invariantCulture; + } + } + + public static StringComparer InvariantCultureIgnoreCase + { + get + { + return s_invariantCultureIgnoreCase; + } + } + + public static StringComparer CurrentCulture + { + get + { + return new CultureAwareComparer(CultureInfo.CurrentCulture, false); + } + } + + public static StringComparer CurrentCultureIgnoreCase + { + get + { + return new CultureAwareComparer(CultureInfo.CurrentCulture, true); + } + } + + public static StringComparer Ordinal + { + get + { + return s_ordinal; + } + } + + public static StringComparer OrdinalIgnoreCase + { + get + { + return s_ordinalIgnoreCase; + } + } + + // Convert a StringComparison to a StringComparer + public static StringComparer FromComparison(StringComparison comparisonType) + { + switch (comparisonType) + { + case StringComparison.CurrentCulture: + return CurrentCulture; + case StringComparison.CurrentCultureIgnoreCase: + return CurrentCultureIgnoreCase; + case StringComparison.InvariantCulture: + return InvariantCulture; + case StringComparison.InvariantCultureIgnoreCase: + return InvariantCultureIgnoreCase; + case StringComparison.Ordinal: + return Ordinal; + case StringComparison.OrdinalIgnoreCase: + return OrdinalIgnoreCase; + default: + throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType)); + } + } + + public static StringComparer Create(CultureInfo culture, bool ignoreCase) + { + if (culture == null) + { + throw new ArgumentNullException(nameof(culture)); + } + + return new CultureAwareComparer(culture, ignoreCase); + } + + public int Compare(object x, object y) + { + if (x == y) return 0; + if (x == null) return -1; + if (y == null) return 1; + + String sa = x as String; + if (sa != null) + { + String sb = y as String; + if (sb != null) + { + return Compare(sa, sb); + } + } + + IComparable ia = x as IComparable; + if (ia != null) + { + return ia.CompareTo(y); + } + + throw new ArgumentException(SR.Argument_ImplementIComparable); + } + + + public new bool Equals(Object x, Object y) + { + if (x == y) return true; + if (x == null || y == null) return false; + + String sa = x as String; + if (sa != null) + { + String sb = y as String; + if (sb != null) + { + return Equals(sa, sb); + } + } + return x.Equals(y); + } + + public int GetHashCode(object obj) + { + if (obj == null) + { + throw new ArgumentNullException(nameof(obj)); + } + + string s = obj as string; + if (s != null) + { + return GetHashCode(s); + } + return obj.GetHashCode(); + } + + public abstract int Compare(String x, String y); + public abstract bool Equals(String x, String y); + public abstract int GetHashCode(string obj); + } + + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class CultureAwareComparer : StringComparer + { + private readonly CompareInfo _compareInfo; // Do not rename (binary serialization) + private readonly bool _ignoreCase; // Do not rename (binary serialization) + + internal CultureAwareComparer(CultureInfo culture, bool ignoreCase) + { + _compareInfo = culture.CompareInfo; + _ignoreCase = ignoreCase; + } + + private CompareOptions Options => _ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None; + + public override int Compare(string x, string y) + { + if (object.ReferenceEquals(x, y)) return 0; + if (x == null) return -1; + if (y == null) return 1; + return _compareInfo.Compare(x, y, Options); + } + + public override bool Equals(string x, string y) + { + if (object.ReferenceEquals(x, y)) return true; + if (x == null || y == null) return false; + return _compareInfo.Compare(x, y, Options) == 0; + } + + public override int GetHashCode(string obj) + { + if (obj == null) + { + throw new ArgumentNullException(nameof(obj)); + } + return _compareInfo.GetHashCodeOfString(obj, Options); + } + + // Equals method for the comparer itself. + public override bool Equals(object obj) + { + CultureAwareComparer comparer = obj as CultureAwareComparer; + return + comparer != null && + _ignoreCase == comparer._ignoreCase && + _compareInfo.Equals(comparer._compareInfo); + } + + public override int GetHashCode() + { + int hashCode = _compareInfo.GetHashCode(); + return _ignoreCase ? ~hashCode : hashCode; + } + } + + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class OrdinalComparer : StringComparer + { + private readonly bool _ignoreCase; // Do not rename (binary serialization) + + internal OrdinalComparer(bool ignoreCase) + { + _ignoreCase = ignoreCase; + } + + public override int Compare(string x, string y) + { + if (ReferenceEquals(x, y)) + return 0; + if (x == null) + return -1; + if (y == null) + return 1; + + if (_ignoreCase) + { + return string.Compare(x, y, StringComparison.OrdinalIgnoreCase); + } + + return string.CompareOrdinal(x, y); + } + + public override bool Equals(string x, string y) + { + if (ReferenceEquals(x, y)) + return true; + if (x == null || y == null) + return false; + + if (_ignoreCase) + { + if (x.Length != y.Length) + { + return false; + } + return (string.Compare(x, y, StringComparison.OrdinalIgnoreCase) == 0); + } + return x.Equals(y); + } + + public override int GetHashCode(string obj) + { + if (obj == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.obj); + } + + if (_ignoreCase) + { + return TextInfo.GetHashCodeOrdinalIgnoreCase(obj); + } + + return obj.GetHashCode(); + } + + // Equals method for the comparer itself. + public override bool Equals(object obj) + { + OrdinalComparer comparer = obj as OrdinalComparer; + if (comparer == null) + { + return false; + } + return (this._ignoreCase == comparer._ignoreCase); + } + + public override int GetHashCode() + { + int hashCode = nameof(OrdinalComparer).GetHashCode(); + return _ignoreCase ? (~hashCode) : hashCode; + } + } + + [Serializable] + internal sealed class OrdinalCaseSensitiveComparer : OrdinalComparer, ISerializable + { + public OrdinalCaseSensitiveComparer() : base(false) + { + } + + public override int Compare(string x, string y) => string.CompareOrdinal(x, y); + + public override bool Equals(string x, string y) => string.Equals(x, y); + + public override int GetHashCode(string obj) + { + if (obj == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.obj); + } + return obj.GetHashCode(); + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.SetType(typeof(OrdinalComparer)); + info.AddValue("_ignoreCase", false); + } + } + + [Serializable] + internal sealed class OrdinalIgnoreCaseComparer : OrdinalComparer, ISerializable + { + public OrdinalIgnoreCaseComparer() : base(true) + { + } + + public override int Compare(string x, string y) => string.Compare(x, y, StringComparison.OrdinalIgnoreCase); + + public override bool Equals(string x, string y) => string.Equals(x, y, StringComparison.OrdinalIgnoreCase); + + public override int GetHashCode(string obj) + { + if (obj == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.obj); + } + return TextInfo.GetHashCodeOrdinalIgnoreCase(obj); + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.SetType(typeof(OrdinalComparer)); + info.AddValue("_ignoreCase", true); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/StringComparison.cs b/external/corefx/src/Common/src/CoreLib/System/StringComparison.cs new file mode 100644 index 0000000000..d5c18c8021 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/StringComparison.cs @@ -0,0 +1,16 @@ +// 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. + +namespace System +{ + public enum StringComparison + { + CurrentCulture = 0, + CurrentCultureIgnoreCase = 1, + InvariantCulture = 2, + InvariantCultureIgnoreCase = 3, + Ordinal = 4, + OrdinalIgnoreCase = 5, + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/StringSpanHelpers.cs b/external/corefx/src/Common/src/CoreLib/System/StringSpanHelpers.cs new file mode 100644 index 0000000000..1c127b19d0 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/StringSpanHelpers.cs @@ -0,0 +1,129 @@ +// 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.Globalization; + +namespace System +{ + /// Helpers for string-like operations on spans of chars. + internal static class StringSpanHelpers + { + // TODO https://github.com/dotnet/corefx/issues/21395: Provide public, efficient implementations + + public static bool Equals(this ReadOnlySpan left, ReadOnlySpan right, StringComparison comparisonType) => + comparisonType == StringComparison.Ordinal ? Equals(left, right) : + comparisonType == StringComparison.OrdinalIgnoreCase ? EqualsOrdinalIgnoreCase(left, right) : + throw new ArgumentOutOfRangeException(nameof(comparisonType)); + + public static bool Equals(this ReadOnlySpan left, string right) => + Equals(left, right.AsReadOnlySpan()); + + public static bool Equals(this ReadOnlySpan left, ReadOnlySpan right) + { + if (left.Length != right.Length) + { + return false; + } + + for (int i = 0; i < left.Length; i++) + { + if (left[i] != right[i]) + { + return false; + } + } + + return true; + } + + private static bool EqualsOrdinalIgnoreCase(this ReadOnlySpan left, ReadOnlySpan right) + { + if (left.Length != right.Length) + { + return false; + } + + for (int i = 0; i < left.Length; i++) + { + char x = left[i], y = right[i]; + if (x != y && + TextInfo.ToUpperAsciiInvariant(x) != TextInfo.ToUpperAsciiInvariant(y)) + { + return false; + } + } + + return true; + } + + public static ReadOnlySpan Trim(this ReadOnlySpan source) + { + int startIndex = 0, endIndex = source.Length - 1; + + while (startIndex <= endIndex && char.IsWhiteSpace(source[startIndex])) + { + startIndex++; + } + + while (endIndex >= startIndex && char.IsWhiteSpace(source[endIndex])) + { + endIndex--; + } + + return source.Slice(startIndex, endIndex - startIndex + 1); + } + + public static int IndexOf(this ReadOnlySpan source, char value) => + IndexOf(source, value, 0); + + public static int IndexOf(this ReadOnlySpan source, char value, int startIndex) + { + for (int i = startIndex; i < source.Length; i++) + { + if (source[i] == value) + { + return i; + } + } + + return -1; + } + + public static bool Contains(this ReadOnlySpan source, char value) + { + for (int i = 0; i < source.Length; i++) + { + if (source[i] == value) + { + return true; + } + } + + return false; + } + + public static ReadOnlySpan Remove(this ReadOnlySpan source, int startIndex, int count) + { + if (startIndex < 0) throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex); + if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NegativeCount); + if (count > source.Length - startIndex) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_IndexCount); + + if (count == 0) + { + return source; + } + + int newLength = source.Length - count; + if (newLength == 0) + { + return ReadOnlySpan.Empty; + } + + Span result = new char[newLength]; + source.Slice(0, startIndex).CopyTo(result); + source.Slice(startIndex + count).CopyTo(result.Slice(startIndex)); + return result; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/StringSplitOptions.cs b/external/corefx/src/Common/src/CoreLib/System/StringSplitOptions.cs new file mode 100644 index 0000000000..d7020559a1 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/StringSplitOptions.cs @@ -0,0 +1,13 @@ +// 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. + +namespace System +{ + [Flags] + public enum StringSplitOptions + { + None = 0, + RemoveEmptyEntries = 1 + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/SystemException.cs b/external/corefx/src/Common/src/CoreLib/System/SystemException.cs new file mode 100644 index 0000000000..b7e8e42175 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/SystemException.cs @@ -0,0 +1,35 @@ +// 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.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class SystemException : Exception + { + public SystemException() + : base(SR.Arg_SystemException) + { + HResult = HResults.COR_E_SYSTEM; + } + + public SystemException(String message) + : base(message) + { + HResult = HResults.COR_E_SYSTEM; + } + + public SystemException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_SYSTEM; + } + + protected SystemException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Text/ASCIIEncoding.cs b/external/corefx/src/Common/src/CoreLib/System/Text/ASCIIEncoding.cs new file mode 100644 index 0000000000..e89943a192 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Text/ASCIIEncoding.cs @@ -0,0 +1,945 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Text +{ + // ASCIIEncoding + // + // Note that ASCIIEncoding is optimized with no best fit and ? for fallback. + // It doesn't come in other flavors. + // + // Note: ASCIIEncoding is the only encoding that doesn't do best fit (windows has best fit). + // + // Note: IsAlwaysNormalized remains false because 1/2 the code points are unassigned, so they'd + // use fallbacks, and we cannot guarantee that fallbacks are normalized. + + public class ASCIIEncoding : Encoding + { + // Allow for devirtualization (see https://github.com/dotnet/coreclr/pull/9230) + internal sealed class ASCIIEncodingSealed : ASCIIEncoding { } + + // Used by Encoding.ASCII for lazy initialization + // The initialization code will not be run until a static member of the class is referenced + internal static readonly ASCIIEncodingSealed s_default = new ASCIIEncodingSealed(); + + public ASCIIEncoding() : base(Encoding.CodePageASCII) + { + } + + internal override void SetDefaultFallbacks() + { + // For ASCIIEncoding we just use default replacement fallback + this.encoderFallback = EncoderFallback.ReplacementFallback; + this.decoderFallback = DecoderFallback.ReplacementFallback; + } + + // WARNING: GetByteCount(string chars), GetBytes(string chars,...), and GetString(byte[] byteIndex...) + // WARNING: have different variable names than EncodingNLS.cs, so this can't just be cut & pasted, + // WARNING: or it'll break VB's way of calling these. + // + // The following methods are copied from EncodingNLS.cs. + // Unfortunately EncodingNLS.cs is internal and we're public, so we have to re-implement them here. + // These should be kept in sync for the following classes: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + // Returns the number of bytes required to encode a range of characters in + // a character array. + // + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe int GetByteCount(char[] chars, int index, int count) + { + // Validate input parameters + if (chars == null) + throw new ArgumentNullException("chars", SR.ArgumentNull_Array); + + if (index < 0 || count < 0) + throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (chars.Length - index < count) + throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); + + // If no input, return 0, avoid fixed empty array problem + if (count == 0) + return 0; + + // Just call the pointer version + fixed (char* pChars = chars) + return GetByteCount(pChars + index, count, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe int GetByteCount(String chars) + { + // Validate input + if (chars==null) + throw new ArgumentNullException("chars"); + + fixed (char* pChars = chars) + return GetByteCount(pChars, chars.Length, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + [CLSCompliant(false)] + public override unsafe int GetByteCount(char* chars, int count) + { + // Validate Parameters + if (chars == null) + throw new ArgumentNullException("chars", SR.ArgumentNull_Array); + + if (count < 0) + throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); + + // Call it with empty encoder + return GetByteCount(chars, count, null); + } + + // Parent method is safe. + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + public override unsafe int GetBytes(String chars, int charIndex, int charCount, + byte[] bytes, int byteIndex) + { + if (chars == null || bytes == null) + throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array); + + if (charIndex < 0 || charCount < 0) + throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (chars.Length - charIndex < charCount) + throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCount); + + if (byteIndex < 0 || byteIndex > bytes.Length) + throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); + + int byteCount = bytes.Length - byteIndex; + + fixed (char* pChars = chars) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) + return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null); + } + + // Encodes a range of characters in a character array into a range of bytes + // in a byte array. An exception occurs if the byte array is not large + // enough to hold the complete encoding of the characters. The + // GetByteCount method can be used to determine the exact number of + // bytes that will be produced for a given range of characters. + // Alternatively, the GetMaxByteCount method can be used to + // determine the maximum number of bytes that will be produced for a given + // number of characters, regardless of the actual character values. + // + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe int GetBytes(char[] chars, int charIndex, int charCount, + byte[] bytes, int byteIndex) + { + // Validate parameters + if (chars == null || bytes == null) + throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array); + + if (charIndex < 0 || charCount < 0) + throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (chars.Length - charIndex < charCount) + throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); + + if (byteIndex < 0 || byteIndex > bytes.Length) + throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); + + // If nothing to encode return 0 + if (charCount == 0) + return 0; + + // Just call pointer version + int byteCount = bytes.Length - byteIndex; + + fixed (char* pChars = chars) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) + // Remember that byteCount is # to decode, not size of array. + return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + [CLSCompliant(false)] + public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount) + { + // Validate Parameters + if (bytes == null || chars == null) + throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + + if (charCount < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + return GetBytes(chars, charCount, bytes, byteCount, null); + } + + // Returns the number of characters produced by decoding a range of bytes + // in a byte array. + // + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe int GetCharCount(byte[] bytes, int index, int count) + { + // Validate Parameters + if (bytes == null) + throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + + if (index < 0 || count < 0) + throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (bytes.Length - index < count) + throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + + // If no input just return 0, fixed doesn't like 0 length arrays + if (count == 0) + return 0; + + // Just call pointer version + fixed (byte* pBytes = bytes) + return GetCharCount(pBytes + index, count, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + [CLSCompliant(false)] + public override unsafe int GetCharCount(byte* bytes, int count) + { + // Validate Parameters + if (bytes == null) + throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + + if (count < 0) + throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); + + return GetCharCount(bytes, count, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount, + char[] chars, int charIndex) + { + // Validate Parameters + if (bytes == null || chars == null) + throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + + if (byteIndex < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if ( bytes.Length - byteIndex < byteCount) + throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + + if (charIndex < 0 || charIndex > chars.Length) + throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index); + + // If no input, return 0 & avoid fixed problem + if (byteCount == 0) + return 0; + + // Just call pointer version + int charCount = chars.Length - charIndex; + + fixed (byte* pBytes = bytes) fixed (char* pChars = &MemoryMarshal.GetReference((Span)chars)) + // Remember that charCount is # to decode, not size of array + return GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + [CLSCompliant(false)] + public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount) + { + // Validate Parameters + if (bytes == null || chars == null) + throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + + if (charCount < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + return GetChars(bytes, byteCount, chars, charCount, null); + } + + // Returns a string containing the decoded representation of a range of + // bytes in a byte array. + // + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe String GetString(byte[] bytes, int byteIndex, int byteCount) + { + // Validate Parameters + if (bytes == null) + throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + + if (byteIndex < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + + if (bytes.Length - byteIndex < byteCount) + throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + + // Avoid problems with empty input buffer + if (byteCount == 0) return String.Empty; + + fixed (byte* pBytes = bytes) + return String.CreateStringFromEncoding( + pBytes + byteIndex, byteCount, this); + } + + // + // End of standard methods copied from EncodingNLS.cs + // + + // GetByteCount + // Note: We start by assuming that the output will be the same as count. Having + // an encoder or fallback may change that assumption + internal override unsafe int GetByteCount(char* chars, int charCount, EncoderNLS encoder) + { + // Just need to ASSERT, this is called by something else internal that checked parameters already + Debug.Assert(charCount >= 0, "[ASCIIEncoding.GetByteCount]count is negative"); + Debug.Assert(chars != null, "[ASCIIEncoding.GetByteCount]chars is null"); + + // Assert because we shouldn't be able to have a null encoder. + Debug.Assert(encoderFallback != null, "[ASCIIEncoding.GetByteCount]Attempting to use null fallback encoder"); + + char charLeftOver = (char)0; + EncoderReplacementFallback fallback = null; + + // Start by assuming default count, then +/- for fallback characters + char* charEnd = chars + charCount; + + // For fallback we may need a fallback buffer, we know we aren't default fallback. + EncoderFallbackBuffer fallbackBuffer = null; + char* charsForFallback; + + if (encoder != null) + { + charLeftOver = encoder._charLeftOver; + Debug.Assert(charLeftOver == 0 || Char.IsHighSurrogate(charLeftOver), + "[ASCIIEncoding.GetByteCount]leftover character should be high surrogate"); + + fallback = encoder.Fallback as EncoderReplacementFallback; + + // We mustn't have left over fallback data when counting + if (encoder.InternalHasFallbackBuffer) + { + // We always need the fallback buffer in get bytes so we can flush any remaining ones if necessary + fallbackBuffer = encoder.FallbackBuffer; + if (fallbackBuffer.Remaining > 0 && encoder._throwOnOverflow) + throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, encoder.Fallback.GetType())); + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(chars, charEnd, encoder, false); + } + + // Verify that we have no fallbackbuffer, for ASCII its always empty, so just assert + Debug.Assert(!encoder._throwOnOverflow || !encoder.InternalHasFallbackBuffer || + encoder.FallbackBuffer.Remaining == 0, + "[ASCIICodePageEncoding.GetByteCount]Expected empty fallback buffer"); + } + else + { + fallback = this.EncoderFallback as EncoderReplacementFallback; + } + + // If we have an encoder AND we aren't using default fallback, + // then we may have a complicated count. + if (fallback != null && fallback.MaxCharCount == 1) + { + // Replacement fallback encodes surrogate pairs as two ?? (or two whatever), so return size is always + // same as input size. + // Note that no existing SBCS code pages map code points to supplimentary characters, so this is easy. + + // We could however have 1 extra byte if the last call had an encoder and a funky fallback and + // if we don't use the funky fallback this time. + + // Do we have an extra char left over from last time? + if (charLeftOver > 0) + charCount++; + + return (charCount); + } + + // Count is more complicated if you have a funky fallback + // For fallback we may need a fallback buffer, we know we're not default fallback + int byteCount = 0; + + // We may have a left over character from last time, try and process it. + if (charLeftOver > 0) + { + Debug.Assert(Char.IsHighSurrogate(charLeftOver), "[ASCIIEncoding.GetByteCount]leftover character should be high surrogate"); + Debug.Assert(encoder != null, "[ASCIIEncoding.GetByteCount]Expected encoder"); + + // Since left over char was a surrogate, it'll have to be fallen back. + // Get Fallback + fallbackBuffer = encoder.FallbackBuffer; + fallbackBuffer.InternalInitialize(chars, charEnd, encoder, false); + + // This will fallback a pair if *chars is a low surrogate + charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered + fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback); + chars = charsForFallback; + } + + // Now we may have fallback char[] already from the encoder + + // Go ahead and do it, including the fallback. + char ch; + while ((ch = (fallbackBuffer == null) ? '\0' : fallbackBuffer.InternalGetNextChar()) != 0 || + chars < charEnd) + { + // First unwind any fallback + if (ch == 0) + { + // No fallback, just get next char + ch = *chars; + chars++; + } + + // Check for fallback, this'll catch surrogate pairs too. + // no chars >= 0x80 are allowed. + if (ch > 0x7f) + { + if (fallbackBuffer == null) + { + // Initialize the buffer + if (encoder == null) + fallbackBuffer = this.encoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = encoder.FallbackBuffer; + fallbackBuffer.InternalInitialize(charEnd - charCount, charEnd, encoder, false); + } + + // Get Fallback + charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered + fallbackBuffer.InternalFallback(ch, ref charsForFallback); + chars = charsForFallback; + continue; + } + + // We'll use this one + byteCount++; + } + + Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0, + "[ASCIIEncoding.GetByteCount]Expected Empty fallback buffer"); + + return byteCount; + } + + internal override unsafe int GetBytes(char* chars, int charCount, + byte* bytes, int byteCount, EncoderNLS encoder) + { + // Just need to ASSERT, this is called by something else internal that checked parameters already + Debug.Assert(bytes != null, "[ASCIIEncoding.GetBytes]bytes is null"); + Debug.Assert(byteCount >= 0, "[ASCIIEncoding.GetBytes]byteCount is negative"); + Debug.Assert(chars != null, "[ASCIIEncoding.GetBytes]chars is null"); + Debug.Assert(charCount >= 0, "[ASCIIEncoding.GetBytes]charCount is negative"); + + // Assert because we shouldn't be able to have a null encoder. + Debug.Assert(encoderFallback != null, "[ASCIIEncoding.GetBytes]Attempting to use null encoder fallback"); + + // Get any left over characters + char charLeftOver = (char)0; + EncoderReplacementFallback fallback = null; + + // For fallback we may need a fallback buffer, we know we aren't default fallback. + EncoderFallbackBuffer fallbackBuffer = null; + char* charsForFallback; + + // prepare our end + char* charEnd = chars + charCount; + byte* byteStart = bytes; + char* charStart = chars; + + if (encoder != null) + { + charLeftOver = encoder._charLeftOver; + fallback = encoder.Fallback as EncoderReplacementFallback; + + // We mustn't have left over fallback data when counting + if (encoder.InternalHasFallbackBuffer) + { + // We always need the fallback buffer in get bytes so we can flush any remaining ones if necessary + fallbackBuffer = encoder.FallbackBuffer; + if (fallbackBuffer.Remaining > 0 && encoder._throwOnOverflow) + throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, encoder.Fallback.GetType())); + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true); + } + + Debug.Assert(charLeftOver == 0 || Char.IsHighSurrogate(charLeftOver), + "[ASCIIEncoding.GetBytes]leftover character should be high surrogate"); + + // Verify that we have no fallbackbuffer, for ASCII its always empty, so just assert + Debug.Assert(!encoder._throwOnOverflow || !encoder.InternalHasFallbackBuffer || + encoder.FallbackBuffer.Remaining == 0, + "[ASCIICodePageEncoding.GetBytes]Expected empty fallback buffer"); + } + else + { + fallback = this.EncoderFallback as EncoderReplacementFallback; + } + + + // See if we do the fast default or slightly slower fallback + if (fallback != null && fallback.MaxCharCount == 1) + { + // Fast version + char cReplacement = fallback.DefaultString[0]; + + // Check for replacements in range, otherwise fall back to slow version. + if (cReplacement <= (char)0x7f) + { + // We should have exactly as many output bytes as input bytes, unless there's a left + // over character, in which case we may need one more. + // If we had a left over character will have to add a ? (This happens if they had a funky + // fallback last time, but not this time.) (We can't spit any out though + // because with fallback encoder each surrogate is treated as a seperate code point) + if (charLeftOver > 0) + { + // Have to have room + // Throw even if doing no throw version because this is just 1 char, + // so buffer will never be big enough + if (byteCount == 0) + ThrowBytesOverflow(encoder, true); + + // This'll make sure we still have more room and also make sure our return value is correct. + *(bytes++) = (byte)cReplacement; + byteCount--; // We used one of the ones we were counting. + } + + // This keeps us from overrunning our output buffer + if (byteCount < charCount) + { + // Throw or make buffer smaller? + ThrowBytesOverflow(encoder, byteCount < 1); + + // Just use what we can + charEnd = chars + byteCount; + } + + // We just do a quick copy + while (chars < charEnd) + { + char ch2 = *(chars++); + if (ch2 >= 0x0080) *(bytes++) = (byte)cReplacement; + else *(bytes++) = unchecked((byte)(ch2)); + } + + // Clear encoder + if (encoder != null) + { + encoder._charLeftOver = (char)0; + encoder._charsUsed = (int)(chars - charStart); + } + + return (int)(bytes - byteStart); + } + } + + // Slower version, have to do real fallback. + + // prepare our end + byte* byteEnd = bytes + byteCount; + + // We may have a left over character from last time, try and process it. + if (charLeftOver > 0) + { + // Initialize the buffer + Debug.Assert(encoder != null, + "[ASCIIEncoding.GetBytes]Expected non null encoder if we have surrogate left over"); + fallbackBuffer = encoder.FallbackBuffer; + fallbackBuffer.InternalInitialize(chars, charEnd, encoder, true); + + // Since left over char was a surrogate, it'll have to be fallen back. + // Get Fallback + // This will fallback a pair if *chars is a low surrogate + charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered + fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback); + chars = charsForFallback; + } + + // Now we may have fallback char[] already from the encoder + + // Go ahead and do it, including the fallback. + char ch; + while ((ch = (fallbackBuffer == null) ? '\0' : fallbackBuffer.InternalGetNextChar()) != 0 || + chars < charEnd) + { + // First unwind any fallback + if (ch == 0) + { + // No fallback, just get next char + ch = *chars; + chars++; + } + + // Check for fallback, this'll catch surrogate pairs too. + // All characters >= 0x80 must fall back. + if (ch > 0x7f) + { + // Initialize the buffer + if (fallbackBuffer == null) + { + if (encoder == null) + fallbackBuffer = this.encoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = encoder.FallbackBuffer; + fallbackBuffer.InternalInitialize(charEnd - charCount, charEnd, encoder, true); + } + + // Get Fallback + charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered + fallbackBuffer.InternalFallback(ch, ref charsForFallback); + chars = charsForFallback; + + // Go ahead & continue (& do the fallback) + continue; + } + + // We'll use this one + // Bounds check + if (bytes >= byteEnd) + { + // didn't use this char, we'll throw or use buffer + if (fallbackBuffer == null || fallbackBuffer.bFallingBack == false) + { + Debug.Assert(chars > charStart || bytes == byteStart, + "[ASCIIEncoding.GetBytes]Expected chars to have advanced already."); + chars--; // don't use last char + } + else + fallbackBuffer.MovePrevious(); + + // Are we throwing or using buffer? + ThrowBytesOverflow(encoder, bytes == byteStart); // throw? + break; // don't throw, stop + } + + // Go ahead and add it + *bytes = unchecked((byte)ch); + bytes++; + } + + // Need to do encoder stuff + if (encoder != null) + { + // Fallback stuck it in encoder if necessary, but we have to clear MustFlush cases + if (fallbackBuffer != null && !fallbackBuffer.bUsedEncoder) + // Clear it in case of MustFlush + encoder._charLeftOver = (char)0; + + // Set our chars used count + encoder._charsUsed = (int)(chars - charStart); + } + + Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0 || + (encoder != null && !encoder._throwOnOverflow), + "[ASCIIEncoding.GetBytes]Expected Empty fallback buffer at end"); + + return (int)(bytes - byteStart); + } + + // This is internal and called by something else, + internal override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS decoder) + { + // Just assert, we're called internally so these should be safe, checked already + Debug.Assert(bytes != null, "[ASCIIEncoding.GetCharCount]bytes is null"); + Debug.Assert(count >= 0, "[ASCIIEncoding.GetCharCount]byteCount is negative"); + + // ASCII doesn't do best fit, so don't have to check for it, find out which decoder fallback we're using + DecoderReplacementFallback fallback = null; + + if (decoder == null) + fallback = this.DecoderFallback as DecoderReplacementFallback; + else + { + fallback = decoder.Fallback as DecoderReplacementFallback; + Debug.Assert(!decoder._throwOnOverflow || !decoder.InternalHasFallbackBuffer || + decoder.FallbackBuffer.Remaining == 0, + "[ASCIICodePageEncoding.GetCharCount]Expected empty fallback buffer"); + } + + if (fallback != null && fallback.MaxCharCount == 1) + { + // Just return length, SBCS stay the same length because they don't map to surrogate + // pairs and we don't have a decoder fallback. + + return count; + } + + // Only need decoder fallback buffer if not using default replacement fallback, no best fit for ASCII + DecoderFallbackBuffer fallbackBuffer = null; + + // Have to do it the hard way. + // Assume charCount will be == count + int charCount = count; + byte[] byteBuffer = new byte[1]; + + // Do it our fast way + byte* byteEnd = bytes + count; + + // Quick loop + while (bytes < byteEnd) + { + // Faster if don't use *bytes++; + byte b = *bytes; + bytes++; + + // If unknown we have to do fallback count + if (b >= 0x80) + { + if (fallbackBuffer == null) + { + if (decoder == null) + fallbackBuffer = this.DecoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = decoder.FallbackBuffer; + fallbackBuffer.InternalInitialize(byteEnd - count, null); + } + + // Use fallback buffer + byteBuffer[0] = b; + charCount--; // Have to unreserve the one we already allocated for b + charCount += fallbackBuffer.InternalFallback(byteBuffer, bytes); + } + } + + // Fallback buffer must be empty + Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0, + "[ASCIIEncoding.GetCharCount]Expected Empty fallback buffer"); + + // Converted sequence is same length as input + return charCount; + } + + internal override unsafe int GetChars(byte* bytes, int byteCount, + char* chars, int charCount, DecoderNLS decoder) + { + // Just need to ASSERT, this is called by something else internal that checked parameters already + Debug.Assert(bytes != null, "[ASCIIEncoding.GetChars]bytes is null"); + Debug.Assert(byteCount >= 0, "[ASCIIEncoding.GetChars]byteCount is negative"); + Debug.Assert(chars != null, "[ASCIIEncoding.GetChars]chars is null"); + Debug.Assert(charCount >= 0, "[ASCIIEncoding.GetChars]charCount is negative"); + + // Do it fast way if using ? replacement fallback + byte* byteEnd = bytes + byteCount; + byte* byteStart = bytes; + char* charStart = chars; + + // Note: ASCII doesn't do best fit, but we have to fallback if they use something > 0x7f + // Only need decoder fallback buffer if not using ? fallback. + // ASCII doesn't do best fit, so don't have to check for it, find out which decoder fallback we're using + DecoderReplacementFallback fallback = null; + char* charsForFallback; + + if (decoder == null) + fallback = this.DecoderFallback as DecoderReplacementFallback; + else + { + fallback = decoder.Fallback as DecoderReplacementFallback; + Debug.Assert(!decoder._throwOnOverflow || !decoder.InternalHasFallbackBuffer || + decoder.FallbackBuffer.Remaining == 0, + "[ASCIICodePageEncoding.GetChars]Expected empty fallback buffer"); + } + + if (fallback != null && fallback.MaxCharCount == 1) + { + // Try it the fast way + char replacementChar = fallback.DefaultString[0]; + + // Need byteCount chars, otherwise too small buffer + if (charCount < byteCount) + { + // Need at least 1 output byte, throw if must throw + ThrowCharsOverflow(decoder, charCount < 1); + + // Not throwing, use what we can + byteEnd = bytes + charCount; + } + + // Quick loop, just do '?' replacement because we don't have fallbacks for decodings. + while (bytes < byteEnd) + { + byte b = *(bytes++); + if (b >= 0x80) + // This is an invalid byte in the ASCII encoding. + *(chars++) = replacementChar; + else + *(chars++) = unchecked((char)b); + } + + // bytes & chars used are the same + if (decoder != null) + decoder._bytesUsed = (int)(bytes - byteStart); + return (int)(chars - charStart); + } + + // Slower way's going to need a fallback buffer + DecoderFallbackBuffer fallbackBuffer = null; + byte[] byteBuffer = new byte[1]; + char* charEnd = chars + charCount; + + // Not quite so fast loop + while (bytes < byteEnd) + { + // Faster if don't use *bytes++; + byte b = *(bytes); + bytes++; + + if (b >= 0x80) + { + // This is an invalid byte in the ASCII encoding. + if (fallbackBuffer == null) + { + if (decoder == null) + fallbackBuffer = this.DecoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = decoder.FallbackBuffer; + fallbackBuffer.InternalInitialize(byteEnd - byteCount, charEnd); + } + + // Use fallback buffer + byteBuffer[0] = b; + + // Note that chars won't get updated unless this succeeds + charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered + bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallback); + chars = charsForFallback; + + if (!fallbackResult) + { + // May or may not throw, but we didn't get this byte + Debug.Assert(bytes > byteStart || chars == charStart, + "[ASCIIEncoding.GetChars]Expected bytes to have advanced already (fallback case)"); + bytes--; // unused byte + fallbackBuffer.InternalReset(); // Didn't fall this back + ThrowCharsOverflow(decoder, chars == charStart); // throw? + break; // don't throw, but stop loop + } + } + else + { + // Make sure we have buffer space + if (chars >= charEnd) + { + Debug.Assert(bytes > byteStart || chars == charStart, + "[ASCIIEncoding.GetChars]Expected bytes to have advanced already (normal case)"); + bytes--; // unused byte + ThrowCharsOverflow(decoder, chars == charStart); // throw? + break; // don't throw, but stop loop + } + + *(chars) = unchecked((char)b); + chars++; + } + } + + // Might have had decoder fallback stuff. + if (decoder != null) + decoder._bytesUsed = (int)(bytes - byteStart); + + // Expect Empty fallback buffer for GetChars + Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0, + "[ASCIIEncoding.GetChars]Expected Empty fallback buffer"); + + return (int)(chars - charStart); + } + + + public override int GetMaxByteCount(int charCount) + { + if (charCount < 0) + throw new ArgumentOutOfRangeException(nameof(charCount), + SR.ArgumentOutOfRange_NeedNonNegNum); + + // Characters would be # of characters + 1 in case high surrogate is ? * max fallback + long byteCount = (long)charCount + 1; + + if (EncoderFallback.MaxCharCount > 1) + byteCount *= EncoderFallback.MaxCharCount; + + // 1 to 1 for most characters. Only surrogates with fallbacks have less. + + if (byteCount > 0x7fffffff) + throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_GetByteCountOverflow); + return (int)byteCount; + } + + + public override int GetMaxCharCount(int byteCount) + { + if (byteCount < 0) + throw new ArgumentOutOfRangeException(nameof(byteCount), + SR.ArgumentOutOfRange_NeedNonNegNum); + + // Just return length, SBCS stay the same length because they don't map to surrogate + long charCount = (long)byteCount; + + // 1 to 1 for most characters. Only surrogates with fallbacks have less, unknown fallbacks could be longer. + if (DecoderFallback.MaxCharCount > 1) + charCount *= DecoderFallback.MaxCharCount; + + if (charCount > 0x7fffffff) + throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_GetCharCountOverflow); + + return (int)charCount; + } + + // True if and only if the encoding only uses single byte code points. (Ie, ASCII, 1252, etc) + + public override bool IsSingleByte + { + get + { + return true; + } + } + + public override Decoder GetDecoder() + { + return new DecoderNLS(this); + } + + + public override Encoder GetEncoder() + { + return new EncoderNLS(this); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Text/Decoder.cs b/external/corefx/src/Common/src/CoreLib/System/Text/Decoder.cs new file mode 100644 index 0000000000..b827648fc1 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Text/Decoder.cs @@ -0,0 +1,351 @@ +// 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.Text; +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Text +{ + // A Decoder is used to decode a sequence of blocks of bytes into a + // sequence of blocks of characters. Following instantiation of a decoder, + // sequential blocks of bytes are converted into blocks of characters through + // calls to the GetChars method. The decoder maintains state between the + // conversions, allowing it to correctly decode byte sequences that span + // adjacent blocks. + // + // Instances of specific implementations of the Decoder abstract base + // class are typically obtained through calls to the GetDecoder method + // of Encoding objects. + // + public abstract class Decoder + { + internal DecoderFallback _fallback = null; + + internal DecoderFallbackBuffer _fallbackBuffer = null; + + protected Decoder() + { + // We don't call default reset because default reset probably isn't good if we aren't initialized. + } + + public DecoderFallback Fallback + { + get + { + return _fallback; + } + + set + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + // Can't change fallback if buffer is wrong + if (_fallbackBuffer != null && _fallbackBuffer.Remaining > 0) + throw new ArgumentException( + SR.Argument_FallbackBufferNotEmpty, nameof(value)); + + _fallback = value; + _fallbackBuffer = null; + } + } + + // Note: we don't test for threading here because async access to Encoders and Decoders + // doesn't work anyway. + public DecoderFallbackBuffer FallbackBuffer + { + get + { + if (_fallbackBuffer == null) + { + if (_fallback != null) + _fallbackBuffer = _fallback.CreateFallbackBuffer(); + else + _fallbackBuffer = DecoderFallback.ReplacementFallback.CreateFallbackBuffer(); + } + + return _fallbackBuffer; + } + } + + internal bool InternalHasFallbackBuffer + { + get + { + return _fallbackBuffer != null; + } + } + + // Reset the Decoder + // + // Normally if we call GetChars() and an error is thrown we don't change the state of the Decoder. This + // would allow the caller to correct the error condition and try again (such as if they need a bigger buffer.) + // + // If the caller doesn't want to try again after GetChars() throws an error, then they need to call Reset(). + // + // Virtual implementation has to call GetChars with flush and a big enough buffer to clear a 0 byte string + // We avoid GetMaxCharCount() because a) we can't call the base encoder and b) it might be really big. + public virtual void Reset() + { + byte[] byteTemp = Array.Empty(); + char[] charTemp = new char[GetCharCount(byteTemp, 0, 0, true)]; + GetChars(byteTemp, 0, 0, charTemp, 0, true); + _fallbackBuffer?.Reset(); + } + + // Returns the number of characters the next call to GetChars will + // produce if presented with the given range of bytes. The returned value + // takes into account the state in which the decoder was left following the + // last call to GetChars. The state of the decoder is not affected + // by a call to this method. + // + public abstract int GetCharCount(byte[] bytes, int index, int count); + + public virtual int GetCharCount(byte[] bytes, int index, int count, bool flush) + { + return GetCharCount(bytes, index, count); + } + + // We expect this to be the workhorse for NLS Encodings, but for existing + // ones we need a working (if slow) default implementation) + [CLSCompliant(false)] + public virtual unsafe int GetCharCount(byte* bytes, int count, bool flush) + { + // Validate input parameters + if (bytes == null) + throw new ArgumentNullException(nameof(bytes), + SR.ArgumentNull_Array); + + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), + SR.ArgumentOutOfRange_NeedNonNegNum); + + byte[] arrbyte = new byte[count]; + int index; + + for (index = 0; index < count; index++) + arrbyte[index] = bytes[index]; + + return GetCharCount(arrbyte, 0, count); + } + + public virtual unsafe int GetCharCount(ReadOnlySpan bytes, bool flush) + { + fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + { + return GetCharCount(bytesPtr, bytes.Length, flush); + } + } + + // Decodes a range of bytes in a byte array into a range of characters + // in a character array. The method decodes byteCount bytes from + // bytes starting at index byteIndex, storing the resulting + // characters in chars starting at index charIndex. The + // decoding takes into account the state in which the decoder was left + // following the last call to this method. + // + // An exception occurs if the character array is not large enough to + // hold the complete decoding of the bytes. The GetCharCount method + // can be used to determine the exact number of characters that will be + // produced for a given range of bytes. Alternatively, the + // GetMaxCharCount method of the Encoding that produced this + // decoder can be used to determine the maximum number of characters that + // will be produced for a given number of bytes, regardless of the actual + // byte values. + // + public abstract int GetChars(byte[] bytes, int byteIndex, int byteCount, + char[] chars, int charIndex); + + public virtual int GetChars(byte[] bytes, int byteIndex, int byteCount, + char[] chars, int charIndex, bool flush) + { + return GetChars(bytes, byteIndex, byteCount, chars, charIndex); + } + + // We expect this to be the workhorse for NLS Encodings, but for existing + // ones we need a working (if slow) default implementation) + // + // WARNING WARNING WARNING + // + // WARNING: If this breaks it could be a security threat. Obviously we + // call this internally, so you need to make sure that your pointers, counts + // and indexes are correct when you call this method. + // + // In addition, we have internal code, which will be marked as "safe" calling + // this code. However this code is dependent upon the implementation of an + // external GetChars() method, which could be overridden by a third party and + // the results of which cannot be guaranteed. We use that result to copy + // the char[] to our char* output buffer. If the result count was wrong, we + // could easily overflow our output buffer. Therefore we do an extra test + // when we copy the buffer so that we don't overflow charCount either. + [CLSCompliant(false)] + public virtual unsafe int GetChars(byte* bytes, int byteCount, + char* chars, int charCount, bool flush) + { + // Validate input parameters + if (chars == null || bytes == null) + throw new ArgumentNullException(chars == null ? nameof(chars) : nameof(bytes), + SR.ArgumentNull_Array); + + if (byteCount < 0 || charCount < 0) + throw new ArgumentOutOfRangeException((byteCount < 0 ? nameof(byteCount) : nameof(charCount)), + SR.ArgumentOutOfRange_NeedNonNegNum); + + // Get the byte array to convert + byte[] arrByte = new byte[byteCount]; + + int index; + for (index = 0; index < byteCount; index++) + arrByte[index] = bytes[index]; + + // Get the char array to fill + char[] arrChar = new char[charCount]; + + // Do the work + int result = GetChars(arrByte, 0, byteCount, arrChar, 0, flush); + + Debug.Assert(result <= charCount, "Returned more chars than we have space for"); + + // Copy the char array + // WARNING: We MUST make sure that we don't copy too many chars. We can't + // rely on result because it could be a 3rd party implementation. We need + // to make sure we never copy more than charCount chars no matter the value + // of result + if (result < charCount) + charCount = result; + + // We check both result and charCount so that we don't accidentally overrun + // our pointer buffer just because of an issue in GetChars + for (index = 0; index < charCount; index++) + chars[index] = arrChar[index]; + + return charCount; + } + + public virtual unsafe int GetChars(ReadOnlySpan bytes, Span chars, bool flush) + { + fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) + { + return GetChars(bytesPtr, bytes.Length, charsPtr, chars.Length, flush); + } + } + + // This method is used when the output buffer might not be large enough. + // It will decode until it runs out of bytes, and then it will return + // true if it the entire input was converted. In either case it + // will also return the number of converted bytes and output characters used. + // It will only throw a buffer overflow exception if the entire lenght of chars[] is + // too small to store the next char. (like 0 or maybe 1 or 4 for some encodings) + // We're done processing this buffer only if completed returns true. + // + // Might consider checking Max...Count to avoid the extra counting step. + // + // Note that if all of the input bytes are not consumed, then we'll do a /2, which means + // that its likely that we didn't consume as many bytes as we could have. For some + // applications this could be slow. (Like trying to exactly fill an output buffer from a bigger stream) + public virtual void Convert(byte[] bytes, int byteIndex, int byteCount, + char[] chars, int charIndex, int charCount, bool flush, + out int bytesUsed, out int charsUsed, out bool completed) + { + // Validate parameters + if (bytes == null || chars == null) + throw new ArgumentNullException((bytes == null ? nameof(bytes) : nameof(chars)), + SR.ArgumentNull_Array); + + if (byteIndex < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((byteIndex < 0 ? nameof(byteIndex) : nameof(byteCount)), + SR.ArgumentOutOfRange_NeedNonNegNum); + + if (charIndex < 0 || charCount < 0) + throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)), + SR.ArgumentOutOfRange_NeedNonNegNum); + + if (bytes.Length - byteIndex < byteCount) + throw new ArgumentOutOfRangeException(nameof(bytes), + SR.ArgumentOutOfRange_IndexCountBuffer); + + if (chars.Length - charIndex < charCount) + throw new ArgumentOutOfRangeException(nameof(chars), + SR.ArgumentOutOfRange_IndexCountBuffer); + + bytesUsed = byteCount; + + // Its easy to do if it won't overrun our buffer. + while (bytesUsed > 0) + { + if (GetCharCount(bytes, byteIndex, bytesUsed, flush) <= charCount) + { + charsUsed = GetChars(bytes, byteIndex, bytesUsed, chars, charIndex, flush); + completed = (bytesUsed == byteCount && + (_fallbackBuffer == null || _fallbackBuffer.Remaining == 0)); + return; + } + + // Try again with 1/2 the count, won't flush then 'cause won't read it all + flush = false; + bytesUsed /= 2; + } + + // Oops, we didn't have anything, we'll have to throw an overflow + throw new ArgumentException(SR.Argument_ConversionOverflow); + } + + // This is the version that uses *. + // We're done processing this buffer only if completed returns true. + // + // Might consider checking Max...Count to avoid the extra counting step. + // + // Note that if all of the input bytes are not consumed, then we'll do a /2, which means + // that its likely that we didn't consume as many bytes as we could have. For some + // applications this could be slow. (Like trying to exactly fill an output buffer from a bigger stream) + [CLSCompliant(false)] + public virtual unsafe void Convert(byte* bytes, int byteCount, + char* chars, int charCount, bool flush, + out int bytesUsed, out int charsUsed, out bool completed) + { + // Validate input parameters + if (chars == null || bytes == null) + throw new ArgumentNullException(chars == null ? nameof(chars) : nameof(bytes), + SR.ArgumentNull_Array); + + if (byteCount < 0 || charCount < 0) + throw new ArgumentOutOfRangeException((byteCount < 0 ? nameof(byteCount) : nameof(charCount)), + SR.ArgumentOutOfRange_NeedNonNegNum); + + // Get ready to do it + bytesUsed = byteCount; + + // Its easy to do if it won't overrun our buffer. + while (bytesUsed > 0) + { + if (GetCharCount(bytes, bytesUsed, flush) <= charCount) + { + charsUsed = GetChars(bytes, bytesUsed, chars, charCount, flush); + completed = (bytesUsed == byteCount && + (_fallbackBuffer == null || _fallbackBuffer.Remaining == 0)); + return; + } + + // Try again with 1/2 the count, won't flush then 'cause won't read it all + flush = false; + bytesUsed /= 2; + } + + // Oops, we didn't have anything, we'll have to throw an overflow + throw new ArgumentException(SR.Argument_ConversionOverflow); + } + + public virtual unsafe void Convert(ReadOnlySpan bytes, Span chars, bool flush, out int bytesUsed, out int charsUsed, out bool completed) + { + fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) + { + Convert(bytesPtr, bytes.Length, charsPtr, chars.Length, flush, out bytesUsed, out charsUsed, out completed); + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Text/DecoderBestFitFallback.cs b/external/corefx/src/Common/src/CoreLib/System/Text/DecoderBestFitFallback.cs similarity index 78% rename from external/corert/src/System.Private.CoreLib/src/System/Text/DecoderBestFitFallback.cs rename to external/corefx/src/Common/src/CoreLib/System/Text/DecoderBestFitFallback.cs index 820a8729ea..30c817c91a 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Text/DecoderBestFitFallback.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Text/DecoderBestFitFallback.cs @@ -14,15 +14,14 @@ namespace System.Text internal sealed class InternalDecoderBestFitFallback : DecoderFallback { // Our variables - internal Encoding encoding = null; - internal char[] arrayBestFit = null; - internal char cReplacement = '?'; + internal Encoding _encoding = null; + internal char[] _arrayBestFit = null; + internal char _cReplacement = '?'; internal InternalDecoderBestFitFallback(Encoding encoding) { // Need to load our replacement characters table. - this.encoding = encoding; - this.bIsMicrosoftBestFitFallback = true; + _encoding = encoding; } public override DecoderFallbackBuffer CreateFallbackBuffer() @@ -44,23 +43,23 @@ namespace System.Text InternalDecoderBestFitFallback that = value as InternalDecoderBestFitFallback; if (that != null) { - return (this.encoding.CodePage == that.encoding.CodePage); + return (_encoding.CodePage == that._encoding.CodePage); } return (false); } public override int GetHashCode() { - return this.encoding.CodePage; + return _encoding.CodePage; } } internal sealed class InternalDecoderBestFitFallbackBuffer : DecoderFallbackBuffer { // Our variables - internal char cBestFit = '\0'; - internal int iCount = -1; - internal int iSize; + private char _cBestFit = '\0'; + private int _iCount = -1; + private int _iSize; private InternalDecoderBestFitFallback _oFallback; // Private object for locking instead of locking on a public type for SQL reliability work. @@ -83,14 +82,14 @@ namespace System.Text { _oFallback = fallback; - if (_oFallback.arrayBestFit == null) + if (_oFallback._arrayBestFit == null) { // Lock so we don't confuse ourselves. lock (InternalSyncObject) { // Double check before we do it again. - if (_oFallback.arrayBestFit == null) - _oFallback.arrayBestFit = fallback.encoding.GetBestFitBytesToUnicodeData(); + if (_oFallback._arrayBestFit == null) + _oFallback._arrayBestFit = fallback._encoding.GetBestFitBytesToUnicodeData(); } } } @@ -99,13 +98,13 @@ namespace System.Text public override bool Fallback(byte[] bytesUnknown, int index) { // We expect no previous fallback in our buffer - Debug.Assert(iCount < 1, "[DecoderReplacementFallbackBuffer.Fallback] Calling fallback without a previously empty buffer"); + Debug.Assert(_iCount < 1, "[DecoderReplacementFallbackBuffer.Fallback] Calling fallback without a previously empty buffer"); - cBestFit = TryBestFit(bytesUnknown); - if (cBestFit == '\0') - cBestFit = _oFallback.cReplacement; + _cBestFit = TryBestFit(bytesUnknown); + if (_cBestFit == '\0') + _cBestFit = _oFallback._cReplacement; - iCount = iSize = 1; + _iCount = _iSize = 1; return true; } @@ -115,32 +114,32 @@ namespace System.Text { // We want it to get < 0 because == 0 means that the current/last character is a fallback // and we need to detect recursion. We could have a flag but we already have this counter. - iCount--; + _iCount--; // Do we have anything left? 0 is now last fallback char, negative is nothing left - if (iCount < 0) + if (_iCount < 0) return '\0'; // Need to get it out of the buffer. // Make sure it didn't wrap from the fast count-- path - if (iCount == int.MaxValue) + if (_iCount == int.MaxValue) { - iCount = -1; + _iCount = -1; return '\0'; } // Return the best fit character - return cBestFit; + return _cBestFit; } public override bool MovePrevious() { // Exception fallback doesn't have anywhere to back up to. - if (iCount >= 0) - iCount++; + if (_iCount >= 0) + _iCount++; // Return true if we could do it. - return (iCount >= 0 && iCount <= iSize); + return (_iCount >= 0 && _iCount <= _iSize); } // How many characters left to output? @@ -148,14 +147,14 @@ namespace System.Text { get { - return (iCount > 0) ? iCount : 0; + return (_iCount > 0) ? _iCount : 0; } } // Clear the buffer public override unsafe void Reset() { - iCount = -1; + _iCount = -1; byteStart = null; } @@ -174,7 +173,7 @@ namespace System.Text { // Need to figure out our best fit character, low is beginning of array, high is 1 AFTER end of array int lowBound = 0; - int highBound = _oFallback.arrayBestFit.Length; + int highBound = _oFallback._arrayBestFit.Length; int index; char cCheck; @@ -192,7 +191,7 @@ namespace System.Text cCheck = unchecked((char)((bytesCheck[0] << 8) + bytesCheck[1])); // Check trivial out of range case - if (cCheck < _oFallback.arrayBestFit[0] || cCheck > _oFallback.arrayBestFit[highBound - 2]) + if (cCheck < _oFallback._arrayBestFit[0] || cCheck > _oFallback._arrayBestFit[highBound - 2]) return '\0'; // Binary search the array @@ -204,13 +203,13 @@ namespace System.Text // Also note that index can never == highBound (because diff is rounded down) index = ((iDiff / 2) + lowBound) & 0xFFFE; - char cTest = _oFallback.arrayBestFit[index]; + char cTest = _oFallback._arrayBestFit[index]; if (cTest == cCheck) { // We found it - Debug.Assert(index + 1 < _oFallback.arrayBestFit.Length, + Debug.Assert(index + 1 < _oFallback._arrayBestFit.Length, "[InternalDecoderBestFitFallbackBuffer.TryBestFit]Expected replacement character at end of array"); - return _oFallback.arrayBestFit[index + 1]; + return _oFallback._arrayBestFit[index + 1]; } else if (cTest < cCheck) { @@ -226,12 +225,12 @@ namespace System.Text for (index = lowBound; index < highBound; index += 2) { - if (_oFallback.arrayBestFit[index] == cCheck) + if (_oFallback._arrayBestFit[index] == cCheck) { // We found it - Debug.Assert(index + 1 < _oFallback.arrayBestFit.Length, + Debug.Assert(index + 1 < _oFallback._arrayBestFit.Length, "[InternalDecoderBestFitFallbackBuffer.TryBestFit]Expected replacement character at end of array"); - return _oFallback.arrayBestFit[index + 1]; + return _oFallback._arrayBestFit[index + 1]; } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Text/DecoderExceptionFallback.cs b/external/corefx/src/Common/src/CoreLib/System/Text/DecoderExceptionFallback.cs similarity index 87% rename from external/corert/src/System.Private.CoreLib/src/System/Text/DecoderExceptionFallback.cs rename to external/corefx/src/Common/src/CoreLib/System/Text/DecoderExceptionFallback.cs index d96af2540e..8bfc1f32d3 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Text/DecoderExceptionFallback.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Text/DecoderExceptionFallback.cs @@ -83,7 +83,7 @@ namespace System.Text for (i = 0; i < bytesUnknown.Length && i < 20; i++) { strBytes.Append('['); - strBytes.Append(bytesUnknown[i].ToString("X2", FormatProvider.InvariantCulture)); + strBytes.Append(bytesUnknown[i].ToString("X2", CultureInfo.InvariantCulture)); strBytes.Append(']'); } @@ -100,6 +100,7 @@ namespace System.Text // Exception for decoding unknown byte sequences. [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class DecoderFallbackException : ArgumentException { private byte[] _bytesUnknown = null; @@ -108,16 +109,19 @@ namespace System.Text public DecoderFallbackException() : base(SR.Arg_ArgumentException) { + HResult = HResults.COR_E_ARGUMENT; } public DecoderFallbackException(String message) : base(message) { + HResult = HResults.COR_E_ARGUMENT; } public DecoderFallbackException(String message, Exception innerException) : base(message, innerException) { + HResult = HResults.COR_E_ARGUMENT; } public DecoderFallbackException(String message, byte[] bytesUnknown, int index) @@ -127,8 +131,8 @@ namespace System.Text _index = index; } - internal DecoderFallbackException(SerializationInfo info, StreamingContext context) - : base(info, context) + private DecoderFallbackException(SerializationInfo serializationInfo, StreamingContext streamingContext) + : base(serializationInfo, streamingContext) { } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Text/DecoderFallback.cs b/external/corefx/src/Common/src/CoreLib/System/Text/DecoderFallback.cs similarity index 86% rename from external/corert/src/System.Private.CoreLib/src/System/Text/DecoderFallback.cs rename to external/corefx/src/Common/src/CoreLib/System/Text/DecoderFallback.cs index 4f98fec461..11b9539b5c 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Text/DecoderFallback.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Text/DecoderFallback.cs @@ -10,40 +10,20 @@ namespace System.Text { public abstract class DecoderFallback { - internal bool bIsMicrosoftBestFitFallback = false; - private static DecoderFallback s_replacementFallback; // Default fallback, uses no best fit & "?" private static DecoderFallback s_exceptionFallback; - // Get each of our generic fallbacks. - - public static DecoderFallback ReplacementFallback - { - get - { - if (s_replacementFallback == null) - Interlocked.CompareExchange(ref s_replacementFallback, new DecoderReplacementFallback(), null); - - return s_replacementFallback; - } - } + public static DecoderFallback ReplacementFallback => + s_replacementFallback ?? Interlocked.CompareExchange(ref s_replacementFallback, new DecoderReplacementFallback(), null) ?? s_replacementFallback; - public static DecoderFallback ExceptionFallback - { - get - { - if (s_exceptionFallback == null) - Interlocked.CompareExchange(ref s_exceptionFallback, new DecoderExceptionFallback(), null); - - return s_exceptionFallback; - } - } + public static DecoderFallback ExceptionFallback => + s_exceptionFallback ?? Interlocked.CompareExchange(ref s_exceptionFallback, new DecoderExceptionFallback(), null) ?? s_exceptionFallback; // Fallback // // Return the appropriate unicode string alternative to the character that need to fall back. - // Most implimentations will be: + // Most implementations will be: // return new MyCustomDecoderFallbackBuffer(this); public abstract DecoderFallbackBuffer CreateFallbackBuffer(); @@ -51,22 +31,14 @@ namespace System.Text // Maximum number of characters that this instance of this fallback could return public abstract int MaxCharCount { get; } - - internal bool IsMicrosoftBestFitFallback - { - get - { - return bIsMicrosoftBestFitFallback; - } - } } public abstract class DecoderFallbackBuffer { - // Most implimentations will probably need an implimenation-specific constructor + // Most implementations will probably need an implementation-specific constructor - // internal methods that cannot be overriden that let us do our fallback thing + // internal methods that cannot be overridden that let us do our fallback thing // These wrap the internal methods so that we can check for people doing stuff that's incorrect public abstract bool Fallback(byte[] bytesUnknown, int index); @@ -103,7 +75,7 @@ namespace System.Text } // Set the above values - // This can't be part of the constructor because DecoderFallbacks would have to know how to impliment these. + // This can't be part of the constructor because DecoderFallbacks would have to know how to implement these. internal unsafe void InternalInitialize(byte* byteStart, char* charEnd) { this.byteStart = byteStart; @@ -229,7 +201,7 @@ namespace System.Text { if (strBytes.Length > 0) strBytes.Append(' '); - strBytes.AppendFormat(FormatProvider.InvariantCulture, "\\x{0:X2}", bytesUnknown[i]); + strBytes.AppendFormat(CultureInfo.InvariantCulture, "\\x{0:X2}", bytesUnknown[i]); } // In case the string's really long if (i == 20) diff --git a/external/corert/src/System.Private.CoreLib/src/System/Text/DecoderNLS.cs b/external/corefx/src/Common/src/CoreLib/System/Text/DecoderNLS.cs similarity index 79% rename from external/corert/src/System.Private.CoreLib/src/System/Text/DecoderNLS.cs rename to external/corefx/src/Common/src/CoreLib/System/Text/DecoderNLS.cs index ea67161861..ee88c22f9d 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Text/DecoderNLS.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Text/DecoderNLS.cs @@ -2,9 +2,10 @@ // 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.Runtime.Serialization; using System.Text; using System; -using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; namespace System.Text { @@ -18,34 +19,32 @@ namespace System.Text // Instances of specific implementations of the Decoder abstract base // class are typically obtained through calls to the GetDecoder method // of Encoding objects. - // internal class DecoderNLS : Decoder { // Remember our encoding - protected Encoding m_encoding; - protected bool m_mustFlush; - internal bool m_throwOnOverflow; - internal int m_bytesUsed; + private Encoding _encoding; + private bool _mustFlush; + internal bool _throwOnOverflow; + internal int _bytesUsed; internal DecoderNLS(Encoding encoding) { - this.m_encoding = encoding; - this.m_fallback = this.m_encoding.DecoderFallback; + _encoding = encoding; + _fallback = this._encoding.DecoderFallback; this.Reset(); } // This is used by our child deserializers internal DecoderNLS() { - this.m_encoding = null; + _encoding = null; this.Reset(); } public override void Reset() { - if (m_fallbackBuffer != null) - m_fallbackBuffer.Reset(); + _fallbackBuffer?.Reset(); } public override unsafe int GetCharCount(byte[] bytes, int index, int count) @@ -68,14 +67,8 @@ namespace System.Text throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); - - // Avoid null fixed problem - if (bytes.Length == 0) - bytes = new byte[1]; - // Just call pointer version - fixed (byte* pBytes = &bytes[0]) + fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) return GetCharCount(pBytes + index, count, flush); } @@ -89,14 +82,13 @@ namespace System.Text if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Remember the flush - this.m_mustFlush = flush; - this.m_throwOnOverflow = true; + _mustFlush = flush; + _throwOnOverflow = true; // By default just call the encoding version, no flush by default - return m_encoding.GetCharCount(bytes, count, this); + return _encoding.GetCharCount(bytes, count, this); } public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount, @@ -125,19 +117,11 @@ namespace System.Text throw new ArgumentOutOfRangeException(nameof(charIndex), SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); - - // Avoid empty input fixed problem - if (bytes.Length == 0) - bytes = new byte[1]; - int charCount = chars.Length - charIndex; - if (chars.Length == 0) - chars = new char[1]; // Just call pointer version - fixed (byte* pBytes = &bytes[0]) - fixed (char* pChars = &chars[0]) + fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) + fixed (char* pChars = &MemoryMarshal.GetReference((Span)chars)) // Remember that charCount is # to decode, not size of array return GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, flush); @@ -154,14 +138,13 @@ namespace System.Text if (byteCount < 0 || charCount < 0) throw new ArgumentOutOfRangeException((byteCount < 0 ? nameof(byteCount) : nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Remember our flush - m_mustFlush = flush; - m_throwOnOverflow = true; + _mustFlush = flush; + _throwOnOverflow = true; - // By default just call the encoding's version - return m_encoding.GetChars(bytes, byteCount, chars, charCount, this); + // By default just call the encodings version + return _encoding.GetChars(bytes, byteCount, chars, charCount, this); } // This method is used when the output buffer might not be big enough. @@ -191,18 +174,10 @@ namespace System.Text throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); - - // Avoid empty input problem - if (bytes.Length == 0) - bytes = new byte[1]; - if (chars.Length == 0) - chars = new char[1]; - // Just call the pointer version (public overrides can't do this) - fixed (byte* pBytes = &bytes[0]) + fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) { - fixed (char* pChars = &chars[0]) + fixed (char* pChars = &MemoryMarshal.GetReference((Span)chars)) { Convert(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, flush, out bytesUsed, out charsUsed, out completed); @@ -224,28 +199,28 @@ namespace System.Text if (byteCount < 0 || charCount < 0) throw new ArgumentOutOfRangeException((byteCount < 0 ? nameof(byteCount) : nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // We don't want to throw - this.m_mustFlush = flush; - this.m_throwOnOverflow = false; - this.m_bytesUsed = 0; + _mustFlush = flush; + _throwOnOverflow = false; + _bytesUsed = 0; // Do conversion - charsUsed = this.m_encoding.GetChars(bytes, byteCount, chars, charCount, this); - bytesUsed = this.m_bytesUsed; + charsUsed = _encoding.GetChars(bytes, byteCount, chars, charCount, this); + bytesUsed = _bytesUsed; // Its completed if they've used what they wanted AND if they didn't want flush or if we are flushed completed = (bytesUsed == byteCount) && (!flush || !this.HasState) && - (m_fallbackBuffer == null || m_fallbackBuffer.Remaining == 0); - // Our data thingys are now full, we can return + (_fallbackBuffer == null || _fallbackBuffer.Remaining == 0); + + // Our data thingy are now full, we can return } public bool MustFlush { get { - return m_mustFlush; + return _mustFlush; } } @@ -261,7 +236,7 @@ namespace System.Text // Allow encoding to clear our must flush instead of throwing (in ThrowCharsOverflow) internal void ClearMustFlush() { - m_mustFlush = false; + _mustFlush = false; } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Text/DecoderReplacementFallback.cs b/external/corefx/src/Common/src/CoreLib/System/Text/DecoderReplacementFallback.cs similarity index 97% rename from external/corert/src/System.Private.CoreLib/src/System/Text/DecoderReplacementFallback.cs rename to external/corefx/src/Common/src/CoreLib/System/Text/DecoderReplacementFallback.cs index af0ea35e66..422b80bb2f 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Text/DecoderReplacementFallback.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Text/DecoderReplacementFallback.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; namespace System.Text { @@ -21,7 +20,6 @@ namespace System.Text { if (replacement == null) throw new ArgumentNullException(nameof(replacement)); - Contract.EndContractBlock(); // Make sure it doesn't have bad surrogate pairs bool bFoundHigh = false; @@ -57,7 +55,7 @@ namespace System.Text break; } if (bFoundHigh) - throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex, nameof(replacement)); + throw new ArgumentException(SR.Format(SR.Argument_InvalidCharSequenceNoIndex, nameof(replacement))); _strDefault = replacement; } diff --git a/external/corefx/src/Common/src/CoreLib/System/Text/Encoder.cs b/external/corefx/src/Common/src/CoreLib/System/Text/Encoder.cs new file mode 100644 index 0000000000..fb1bdb8038 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Text/Encoder.cs @@ -0,0 +1,346 @@ +// 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.Text; +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Text +{ + // An Encoder is used to encode a sequence of blocks of characters into + // a sequence of blocks of bytes. Following instantiation of an encoder, + // sequential blocks of characters are converted into blocks of bytes through + // calls to the GetBytes method. The encoder maintains state between the + // conversions, allowing it to correctly encode character sequences that span + // adjacent blocks. + // + // Instances of specific implementations of the Encoder abstract base + // class are typically obtained through calls to the GetEncoder method + // of Encoding objects. + // + public abstract class Encoder + { + internal EncoderFallback _fallback = null; + + internal EncoderFallbackBuffer _fallbackBuffer = null; + + protected Encoder() + { + // We don't call default reset because default reset probably isn't good if we aren't initialized. + } + + public EncoderFallback Fallback + { + get + { + return _fallback; + } + + set + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + // Can't change fallback if buffer is wrong + if (_fallbackBuffer != null && _fallbackBuffer.Remaining > 0) + throw new ArgumentException( + SR.Argument_FallbackBufferNotEmpty, nameof(value)); + + _fallback = value; + _fallbackBuffer = null; + } + } + + // Note: we don't test for threading here because async access to Encoders and Decoders + // doesn't work anyway. + public EncoderFallbackBuffer FallbackBuffer + { + get + { + if (_fallbackBuffer == null) + { + if (_fallback != null) + _fallbackBuffer = _fallback.CreateFallbackBuffer(); + else + _fallbackBuffer = EncoderFallback.ReplacementFallback.CreateFallbackBuffer(); + } + + return _fallbackBuffer; + } + } + + internal bool InternalHasFallbackBuffer + { + get + { + return _fallbackBuffer != null; + } + } + + // Reset the Encoder + // + // Normally if we call GetBytes() and an error is thrown we don't change the state of the encoder. This + // would allow the caller to correct the error condition and try again (such as if they need a bigger buffer.) + // + // If the caller doesn't want to try again after GetBytes() throws an error, then they need to call Reset(). + // + // Virtual implementation has to call GetBytes with flush and a big enough buffer to clear a 0 char string + // We avoid GetMaxByteCount() because a) we can't call the base encoder and b) it might be really big. + public virtual void Reset() + { + char[] charTemp = { }; + byte[] byteTemp = new byte[GetByteCount(charTemp, 0, 0, true)]; + GetBytes(charTemp, 0, 0, byteTemp, 0, true); + if (_fallbackBuffer != null) + _fallbackBuffer.Reset(); + } + + // Returns the number of bytes the next call to GetBytes will + // produce if presented with the given range of characters and the given + // value of the flush parameter. The returned value takes into + // account the state in which the encoder was left following the last call + // to GetBytes. The state of the encoder is not affected by a call + // to this method. + // + public abstract int GetByteCount(char[] chars, int index, int count, bool flush); + + // We expect this to be the workhorse for NLS encodings + // unfortunately for existing overrides, it has to call the [] version, + // which is really slow, so avoid this method if you might be calling external encodings. + [CLSCompliant(false)] + public virtual unsafe int GetByteCount(char* chars, int count, bool flush) + { + // Validate input parameters + if (chars == null) + throw new ArgumentNullException(nameof(chars), + SR.ArgumentNull_Array); + + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), + SR.ArgumentOutOfRange_NeedNonNegNum); + + char[] arrChar = new char[count]; + int index; + + for (index = 0; index < count; index++) + arrChar[index] = chars[index]; + + return GetByteCount(arrChar, 0, count, flush); + } + + public virtual unsafe int GetByteCount(ReadOnlySpan chars, bool flush) + { + fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) + { + return GetByteCount(charsPtr, chars.Length, flush); + } + } + + // Encodes a range of characters in a character array into a range of bytes + // in a byte array. The method encodes charCount characters from + // chars starting at index charIndex, storing the resulting + // bytes in bytes starting at index byteIndex. The encoding + // takes into account the state in which the encoder was left following the + // last call to this method. The flush parameter indicates whether + // the encoder should flush any shift-states and partial characters at the + // end of the conversion. To ensure correct termination of a sequence of + // blocks of encoded bytes, the last call to GetBytes should specify + // a value of true for the flush parameter. + // + // An exception occurs if the byte array is not large enough to hold the + // complete encoding of the characters. The GetByteCount method can + // be used to determine the exact number of bytes that will be produced for + // a given range of characters. Alternatively, the GetMaxByteCount + // method of the Encoding that produced this encoder can be used to + // determine the maximum number of bytes that will be produced for a given + // number of characters, regardless of the actual character values. + // + public abstract int GetBytes(char[] chars, int charIndex, int charCount, + byte[] bytes, int byteIndex, bool flush); + + // We expect this to be the workhorse for NLS Encodings, but for existing + // ones we need a working (if slow) default implementation) + // + // WARNING WARNING WARNING + // + // WARNING: If this breaks it could be a security threat. Obviously we + // call this internally, so you need to make sure that your pointers, counts + // and indexes are correct when you call this method. + // + // In addition, we have internal code, which will be marked as "safe" calling + // this code. However this code is dependent upon the implementation of an + // external GetBytes() method, which could be overridden by a third party and + // the results of which cannot be guaranteed. We use that result to copy + // the byte[] to our byte* output buffer. If the result count was wrong, we + // could easily overflow our output buffer. Therefore we do an extra test + // when we copy the buffer so that we don't overflow byteCount either. + [CLSCompliant(false)] + public virtual unsafe int GetBytes(char* chars, int charCount, + byte* bytes, int byteCount, bool flush) + { + // Validate input parameters + if (bytes == null || chars == null) + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), + SR.ArgumentNull_Array); + + if (charCount < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)), + SR.ArgumentOutOfRange_NeedNonNegNum); + + // Get the char array to convert + char[] arrChar = new char[charCount]; + + int index; + for (index = 0; index < charCount; index++) + arrChar[index] = chars[index]; + + // Get the byte array to fill + byte[] arrByte = new byte[byteCount]; + + // Do the work + int result = GetBytes(arrChar, 0, charCount, arrByte, 0, flush); + + Debug.Assert(result <= byteCount, "Returned more bytes than we have space for"); + + // Copy the byte array + // WARNING: We MUST make sure that we don't copy too many bytes. We can't + // rely on result because it could be a 3rd party implementation. We need + // to make sure we never copy more than byteCount bytes no matter the value + // of result + if (result < byteCount) + byteCount = result; + + // Don't copy too many bytes! + for (index = 0; index < byteCount; index++) + bytes[index] = arrByte[index]; + + return byteCount; + } + + public virtual unsafe int GetBytes(ReadOnlySpan chars, Span bytes, bool flush) + { + fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) + fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + { + return GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length, flush); + } + } + + // This method is used to avoid running out of output buffer space. + // It will encode until it runs out of chars, and then it will return + // true if it the entire input was converted. In either case it + // will also return the number of converted chars and output bytes used. + // It will only throw a buffer overflow exception if the entire lenght of bytes[] is + // too small to store the next byte. (like 0 or maybe 1 or 4 for some encodings) + // We're done processing this buffer only if completed returns true. + // + // Might consider checking Max...Count to avoid the extra counting step. + // + // Note that if all of the input chars are not consumed, then we'll do a /2, which means + // that its likely that we didn't consume as many chars as we could have. For some + // applications this could be slow. (Like trying to exactly fill an output buffer from a bigger stream) + public virtual void Convert(char[] chars, int charIndex, int charCount, + byte[] bytes, int byteIndex, int byteCount, bool flush, + out int charsUsed, out int bytesUsed, out bool completed) + { + // Validate parameters + if (chars == null || bytes == null) + throw new ArgumentNullException((chars == null ? nameof(chars) : nameof(bytes)), + SR.ArgumentNull_Array); + + if (charIndex < 0 || charCount < 0) + throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)), + SR.ArgumentOutOfRange_NeedNonNegNum); + + if (byteIndex < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((byteIndex < 0 ? nameof(byteIndex) : nameof(byteCount)), + SR.ArgumentOutOfRange_NeedNonNegNum); + + if (chars.Length - charIndex < charCount) + throw new ArgumentOutOfRangeException(nameof(chars), + SR.ArgumentOutOfRange_IndexCountBuffer); + + if (bytes.Length - byteIndex < byteCount) + throw new ArgumentOutOfRangeException(nameof(bytes), + SR.ArgumentOutOfRange_IndexCountBuffer); + + charsUsed = charCount; + + // Its easy to do if it won't overrun our buffer. + // Note: We don't want to call unsafe version because that might be an untrusted version + // which could be really unsafe and we don't want to mix it up. + while (charsUsed > 0) + { + if (GetByteCount(chars, charIndex, charsUsed, flush) <= byteCount) + { + bytesUsed = GetBytes(chars, charIndex, charsUsed, bytes, byteIndex, flush); + completed = (charsUsed == charCount && + (_fallbackBuffer == null || _fallbackBuffer.Remaining == 0)); + return; + } + + // Try again with 1/2 the count, won't flush then 'cause won't read it all + flush = false; + charsUsed /= 2; + } + + // Oops, we didn't have anything, we'll have to throw an overflow + throw new ArgumentException(SR.Argument_ConversionOverflow); + } + + // Same thing, but using pointers + // + // Might consider checking Max...Count to avoid the extra counting step. + // + // Note that if all of the input chars are not consumed, then we'll do a /2, which means + // that its likely that we didn't consume as many chars as we could have. For some + // applications this could be slow. (Like trying to exactly fill an output buffer from a bigger stream) + [CLSCompliant(false)] + public virtual unsafe void Convert(char* chars, int charCount, + byte* bytes, int byteCount, bool flush, + out int charsUsed, out int bytesUsed, out bool completed) + { + // Validate input parameters + if (bytes == null || chars == null) + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), + SR.ArgumentNull_Array); + if (charCount < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)), + SR.ArgumentOutOfRange_NeedNonNegNum); + + // Get ready to do it + charsUsed = charCount; + + // Its easy to do if it won't overrun our buffer. + while (charsUsed > 0) + { + if (GetByteCount(chars, charsUsed, flush) <= byteCount) + { + bytesUsed = GetBytes(chars, charsUsed, bytes, byteCount, flush); + completed = (charsUsed == charCount && + (_fallbackBuffer == null || _fallbackBuffer.Remaining == 0)); + return; + } + + // Try again with 1/2 the count, won't flush then 'cause won't read it all + flush = false; + charsUsed /= 2; + } + + // Oops, we didn't have anything, we'll have to throw an overflow + throw new ArgumentException(SR.Argument_ConversionOverflow); + } + + public virtual unsafe void Convert(ReadOnlySpan chars, Span bytes, bool flush, out int charsUsed, out int bytesUsed, out bool completed) + { + fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) + fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + { + Convert(charsPtr, chars.Length, bytesPtr, bytes.Length, flush, out charsUsed, out bytesUsed, out completed); + } + } + } +} + diff --git a/external/corert/src/System.Private.CoreLib/src/System/Text/EncoderBestFitFallback.cs b/external/corefx/src/Common/src/CoreLib/System/Text/EncoderBestFitFallback.cs similarity index 86% rename from external/corert/src/System.Private.CoreLib/src/System/Text/EncoderBestFitFallback.cs rename to external/corefx/src/Common/src/CoreLib/System/Text/EncoderBestFitFallback.cs index 9d32152325..7f3be2a7a6 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Text/EncoderBestFitFallback.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Text/EncoderBestFitFallback.cs @@ -7,7 +7,6 @@ // using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Globalization; using System.Threading; @@ -16,14 +15,13 @@ namespace System.Text internal class InternalEncoderBestFitFallback : EncoderFallback { // Our variables - internal Encoding encoding = null; - internal char[] arrayBestFit = null; + internal Encoding _encoding = null; + internal char[] _arrayBestFit = null; internal InternalEncoderBestFitFallback(Encoding encoding) { // Need to load our replacement characters table. - this.encoding = encoding; - this.bIsMicrosoftBestFitFallback = true; + _encoding = encoding; } public override EncoderFallbackBuffer CreateFallbackBuffer() @@ -45,14 +43,14 @@ namespace System.Text InternalEncoderBestFitFallback that = value as InternalEncoderBestFitFallback; if (that != null) { - return (this.encoding.CodePage == that.encoding.CodePage); + return (_encoding.CodePage == that._encoding.CodePage); } return (false); } public override int GetHashCode() { - return this.encoding.CodePage; + return _encoding.CodePage; } } @@ -84,14 +82,14 @@ namespace System.Text { _oFallback = fallback; - if (_oFallback.arrayBestFit == null) + if (_oFallback._arrayBestFit == null) { // Lock so we don't confuse ourselves. lock (InternalSyncObject) { // Double check before we do it again. - if (_oFallback.arrayBestFit == null) - _oFallback.arrayBestFit = fallback.encoding.GetBestFitUnicodeToBytesData(); + if (_oFallback._arrayBestFit == null) + _oFallback._arrayBestFit = fallback._encoding.GetBestFitUnicodeToBytesData(); } } } @@ -102,7 +100,7 @@ namespace System.Text // If we had a buffer already we're being recursive, throw, it's probably at the suspect // character in our array. // Shouldn't be able to get here for all of our code pages, table would have to be messed up. - Debug.Assert(_iCount < 1, "[InternalEncoderBestFitFallbackBuffer.Fallback(non surrogate)] Fallback char " + ((int)_cBestFit).ToString("X4", FormatProvider.InvariantCulture) + " caused recursive fallback"); + Debug.Assert(_iCount < 1, "[InternalEncoderBestFitFallbackBuffer.Fallback(non surrogate)] Fallback char " + ((int)_cBestFit).ToString("X4", CultureInfo.InvariantCulture) + " caused recursive fallback"); _iCount = _iSize = 1; _cBestFit = TryBestFit(charUnknown); @@ -124,12 +122,11 @@ namespace System.Text throw new ArgumentOutOfRangeException(nameof(charUnknownLow), SR.Format(SR.ArgumentOutOfRange_Range, 0xDC00, 0xDFFF)); - Contract.EndContractBlock(); // If we had a buffer already we're being recursive, throw, it's probably at the suspect // character in our array. 0 is processing last character, < 0 is not falling back // Shouldn't be able to get here, table would have to be messed up. - Debug.Assert(_iCount < 1, "[InternalEncoderBestFitFallbackBuffer.Fallback(surrogate)] Fallback char " + ((int)_cBestFit).ToString("X4", FormatProvider.InvariantCulture) + " caused recursive fallback"); + Debug.Assert(_iCount < 1, "[InternalEncoderBestFitFallbackBuffer.Fallback(surrogate)] Fallback char " + ((int)_cBestFit).ToString("X4", CultureInfo.InvariantCulture) + " caused recursive fallback"); // Go ahead and get our fallback, surrogates don't have best fit _cBestFit = '?'; @@ -194,7 +191,7 @@ namespace System.Text { // Need to figure out our best fit character, low is beginning of array, high is 1 AFTER end of array int lowBound = 0; - int highBound = _oFallback.arrayBestFit.Length; + int highBound = _oFallback._arrayBestFit.Length; int index; // Binary search the array @@ -206,13 +203,13 @@ namespace System.Text // Also note that index can never == highBound (because diff is rounded down) index = ((iDiff / 2) + lowBound) & 0xFFFE; - char cTest = _oFallback.arrayBestFit[index]; + char cTest = _oFallback._arrayBestFit[index]; if (cTest == cUnknown) { // We found it - Debug.Assert(index + 1 < _oFallback.arrayBestFit.Length, + Debug.Assert(index + 1 < _oFallback._arrayBestFit.Length, "[InternalEncoderBestFitFallbackBuffer.TryBestFit]Expected replacement character at end of array"); - return _oFallback.arrayBestFit[index + 1]; + return _oFallback._arrayBestFit[index + 1]; } else if (cTest < cUnknown) { @@ -228,12 +225,12 @@ namespace System.Text for (index = lowBound; index < highBound; index += 2) { - if (_oFallback.arrayBestFit[index] == cUnknown) + if (_oFallback._arrayBestFit[index] == cUnknown) { // We found it - Debug.Assert(index + 1 < _oFallback.arrayBestFit.Length, + Debug.Assert(index + 1 < _oFallback._arrayBestFit.Length, "[InternalEncoderBestFitFallbackBuffer.TryBestFit]Expected replacement character at end of array"); - return _oFallback.arrayBestFit[index + 1]; + return _oFallback._arrayBestFit[index + 1]; } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Text/EncoderExceptionFallback.cs b/external/corefx/src/Common/src/CoreLib/System/Text/EncoderExceptionFallback.cs similarity index 78% rename from external/corert/src/System.Private.CoreLib/src/System/Text/EncoderExceptionFallback.cs rename to external/corefx/src/Common/src/CoreLib/System/Text/EncoderExceptionFallback.cs index 9b50b1b236..66de1940f6 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Text/EncoderExceptionFallback.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Text/EncoderExceptionFallback.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics.Contracts; using System.Runtime.Serialization; namespace System.Text @@ -53,8 +52,7 @@ namespace System.Text { // Fall back our char throw new EncoderFallbackException( - SR.Format(SR.Argument_InvalidCodePageConversionIndex, - (int)charUnknown, index), charUnknown, index); + SR.Format(SR.Argument_InvalidCodePageConversionIndex, (int)charUnknown, index), charUnknown, index); } public override bool Fallback(char charUnknownHigh, char charUnknownLow, int index) @@ -62,23 +60,19 @@ namespace System.Text if (!Char.IsHighSurrogate(charUnknownHigh)) { throw new ArgumentOutOfRangeException(nameof(charUnknownHigh), - SR.Format(SR.ArgumentOutOfRange_Range, - 0xD800, 0xDBFF)); + SR.Format(SR.ArgumentOutOfRange_Range, 0xD800, 0xDBFF)); } if (!Char.IsLowSurrogate(charUnknownLow)) { throw new ArgumentOutOfRangeException(nameof(charUnknownLow), - SR.Format(SR.ArgumentOutOfRange_Range, - 0xDC00, 0xDFFF)); + SR.Format(SR.ArgumentOutOfRange_Range, 0xDC00, 0xDFFF)); } - Contract.EndContractBlock(); int iTemp = Char.ConvertToUtf32(charUnknownHigh, charUnknownLow); // Fall back our char throw new EncoderFallbackException( - SR.Format(SR.Argument_InvalidCodePageConversionIndex, - iTemp, index), charUnknownHigh, charUnknownLow, index); + SR.Format(SR.Argument_InvalidCodePageConversionIndex, iTemp, index), charUnknownHigh, charUnknownLow, index); } public override char GetNextChar() @@ -103,6 +97,7 @@ namespace System.Text } [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class EncoderFallbackException : ArgumentException { private char _charUnknown; @@ -113,49 +108,49 @@ namespace System.Text public EncoderFallbackException() : base(SR.Arg_ArgumentException) { + HResult = HResults.COR_E_ARGUMENT; } public EncoderFallbackException(String message) : base(message) { + HResult = HResults.COR_E_ARGUMENT; } public EncoderFallbackException(String message, Exception innerException) : base(message, innerException) { + HResult = HResults.COR_E_ARGUMENT; } - internal EncoderFallbackException(String message, char charUnknown, int index) - : base(message) + internal EncoderFallbackException( + String message, char charUnknown, int index) : base(message) { _charUnknown = charUnknown; _index = index; } - internal EncoderFallbackException(String message, char charUnknownHigh, char charUnknownLow, int index) - : base(message) + internal EncoderFallbackException( + String message, char charUnknownHigh, char charUnknownLow, int index) : base(message) { if (!Char.IsHighSurrogate(charUnknownHigh)) { throw new ArgumentOutOfRangeException(nameof(charUnknownHigh), - SR.Format(SR.ArgumentOutOfRange_Range, - 0xD800, 0xDBFF)); + SR.Format(SR.ArgumentOutOfRange_Range, 0xD800, 0xDBFF)); } if (!Char.IsLowSurrogate(charUnknownLow)) { - throw new ArgumentOutOfRangeException(nameof(charUnknownLow), - SR.Format(SR.ArgumentOutOfRange_Range, - 0xDC00, 0xDFFF)); + throw new ArgumentOutOfRangeException(nameof(CharUnknownLow), + SR.Format(SR.ArgumentOutOfRange_Range, 0xDC00, 0xDFFF)); } - Contract.EndContractBlock(); _charUnknownHigh = charUnknownHigh; _charUnknownLow = charUnknownLow; _index = index; } - internal EncoderFallbackException(SerializationInfo info, StreamingContext context) - : base(info, context) + private EncoderFallbackException(SerializationInfo serializationInfo, StreamingContext streamingContext) + : base(serializationInfo, streamingContext) { } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Text/EncoderFallback.cs b/external/corefx/src/Common/src/CoreLib/System/Text/EncoderFallback.cs similarity index 93% rename from external/corert/src/System.Private.CoreLib/src/System/Text/EncoderFallback.cs rename to external/corefx/src/Common/src/CoreLib/System/Text/EncoderFallback.cs index 37fede125c..d860a02a76 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Text/EncoderFallback.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Text/EncoderFallback.cs @@ -9,11 +9,6 @@ namespace System.Text { public abstract class EncoderFallback { - // disable csharp compiler warning #0414: field assigned unused value -#pragma warning disable 0414 - internal bool bIsMicrosoftBestFitFallback = false; -#pragma warning restore 0414 - private static EncoderFallback s_replacementFallback; // Default fallback, uses no best fit & "?" private static EncoderFallback s_exceptionFallback; @@ -45,7 +40,7 @@ namespace System.Text // Fallback // // Return the appropriate unicode string alternative to the character that need to fall back. - // Most implimentations will be: + // Most implementations will be: // return new MyCustomEncoderFallbackBuffer(this); public abstract EncoderFallbackBuffer CreateFallbackBuffer(); @@ -58,9 +53,9 @@ namespace System.Text public abstract class EncoderFallbackBuffer { - // Most implementations will probably need an implemenation-specific constructor + // Most implementations will probably need an implementation-specific constructor - // Public methods that cannot be overriden that let us do our fallback thing + // Public methods that cannot be overridden that let us do our fallback thing // These wrap the internal methods so that we can check for people doing stuff that is incorrect public abstract bool Fallback(char charUnknown, int index); @@ -109,7 +104,7 @@ namespace System.Text } // Set the above values - // This can't be part of the constructor because EncoderFallbacks would have to know how to impliment these. + // This can't be part of the constructor because EncoderFallbacks would have to know how to implement these. internal unsafe void InternalInitialize(char* charStart, char* charEnd, EncoderNLS encoder, bool setEncoder) { this.charStart = charStart; @@ -160,7 +155,7 @@ namespace System.Text if (this.setEncoder) { bUsedEncoder = true; - this.encoder.charLeftOver = ch; + this.encoder._charLeftOver = ch; } bFallingBack = false; return false; diff --git a/external/corert/src/System.Private.CoreLib/src/System/Text/EncoderNLS.cs b/external/corefx/src/Common/src/CoreLib/System/Text/EncoderNLS.cs similarity index 77% rename from external/corert/src/System.Private.CoreLib/src/System/Text/EncoderNLS.cs rename to external/corefx/src/Common/src/CoreLib/System/Text/EncoderNLS.cs index 2ff4e2833f..e83666f7a3 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Text/EncoderNLS.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Text/EncoderNLS.cs @@ -4,7 +4,7 @@ using System.Text; using System; -using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; namespace System.Text { @@ -23,33 +23,30 @@ namespace System.Text internal class EncoderNLS : Encoder { // Need a place for the last left over character, most of our encodings use this - internal char charLeftOver; - - protected Encoding m_encoding; - - protected bool m_mustFlush; - internal bool m_throwOnOverflow; - internal int m_charsUsed; + internal char _charLeftOver; + private Encoding _encoding; + private bool _mustFlush; + internal bool _throwOnOverflow; + internal int _charsUsed; internal EncoderNLS(Encoding encoding) { - this.m_encoding = encoding; - this.m_fallback = this.m_encoding.EncoderFallback; + _encoding = encoding; + _fallback = _encoding.EncoderFallback; this.Reset(); } - // This one is used when deserializing (like UTF7Encoding.Encoder) internal EncoderNLS() { - this.m_encoding = null; + _encoding = null; this.Reset(); } public override void Reset() { - this.charLeftOver = (char)0; - if (m_fallbackBuffer != null) - m_fallbackBuffer.Reset(); + _charLeftOver = (char)0; + if (_fallbackBuffer != null) + _fallbackBuffer.Reset(); } public override unsafe int GetByteCount(char[] chars, int index, int count, bool flush) @@ -66,15 +63,10 @@ namespace System.Text if (chars.Length - index < count) throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); - - // Avoid empty input problem - if (chars.Length == 0) - chars = new char[1]; // Just call the pointer version int result = -1; - fixed (char* pChars = &chars[0]) + fixed (char* pChars = &MemoryMarshal.GetReference((Span)chars)) { result = GetByteCount(pChars + index, count, flush); } @@ -91,11 +83,10 @@ namespace System.Text if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); - this.m_mustFlush = flush; - this.m_throwOnOverflow = true; - return m_encoding.GetByteCount(chars, count, this); + _mustFlush = flush; + _throwOnOverflow = true; + return _encoding.GetByteCount(chars, count, this); } public override unsafe int GetBytes(char[] chars, int charIndex, int charCount, @@ -117,18 +108,12 @@ namespace System.Text if (byteIndex < 0 || byteIndex > bytes.Length) throw new ArgumentOutOfRangeException(nameof(byteIndex), SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); - - if (chars.Length == 0) - chars = new char[1]; int byteCount = bytes.Length - byteIndex; - if (bytes.Length == 0) - bytes = new byte[1]; // Just call pointer version - fixed (char* pChars = &chars[0]) - fixed (byte* pBytes = &bytes[0]) + fixed (char* pChars = &MemoryMarshal.GetReference((Span)chars)) + fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) // Remember that charCount is # to decode, not size of array. return GetBytes(pChars + charIndex, charCount, @@ -145,11 +130,10 @@ namespace System.Text if (byteCount < 0 || charCount < 0) throw new ArgumentOutOfRangeException((byteCount < 0 ? nameof(byteCount) : nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); - this.m_mustFlush = flush; - this.m_throwOnOverflow = true; - return m_encoding.GetBytes(chars, charCount, bytes, byteCount, this); + _mustFlush = flush; + _throwOnOverflow = true; + return _encoding.GetBytes(chars, charCount, bytes, byteCount, this); } // This method is used when your output buffer might not be large enough for the entire result. @@ -179,18 +163,10 @@ namespace System.Text throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); - - // Avoid empty input problem - if (chars.Length == 0) - chars = new char[1]; - if (bytes.Length == 0) - bytes = new byte[1]; - // Just call the pointer version (can't do this for non-msft encoders) - fixed (char* pChars = &chars[0]) + fixed (char* pChars = &MemoryMarshal.GetReference((Span)chars)) { - fixed (byte* pBytes = &bytes[0]) + fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) { Convert(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, flush, out charsUsed, out bytesUsed, out completed); @@ -211,20 +187,20 @@ namespace System.Text if (charCount < 0 || byteCount < 0) throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // We don't want to throw - this.m_mustFlush = flush; - this.m_throwOnOverflow = false; - this.m_charsUsed = 0; + _mustFlush = flush; + _throwOnOverflow = false; + _charsUsed = 0; // Do conversion - bytesUsed = this.m_encoding.GetBytes(chars, charCount, bytes, byteCount, this); - charsUsed = this.m_charsUsed; + bytesUsed = _encoding.GetBytes(chars, charCount, bytes, byteCount, this); + charsUsed = _charsUsed; // Its completed if they've used what they wanted AND if they didn't want flush or if we are flushed completed = (charsUsed == charCount) && (!flush || !this.HasState) && - (m_fallbackBuffer == null || m_fallbackBuffer.Remaining == 0); + (_fallbackBuffer == null || _fallbackBuffer.Remaining == 0); + // Our data thingys are now full, we can return } @@ -232,7 +208,7 @@ namespace System.Text { get { - return m_encoding; + return _encoding; } } @@ -240,7 +216,7 @@ namespace System.Text { get { - return m_mustFlush; + return _mustFlush; } } @@ -250,14 +226,14 @@ namespace System.Text { get { - return (this.charLeftOver != (char)0); + return (_charLeftOver != (char)0); } } // Allow encoding to clear our must flush instead of throwing (in ThrowBytesOverflow) internal void ClearMustFlush() { - m_mustFlush = false; + _mustFlush = false; } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Text/EncoderReplacementFallback.cs b/external/corefx/src/Common/src/CoreLib/System/Text/EncoderReplacementFallback.cs similarity index 94% rename from external/corert/src/System.Private.CoreLib/src/System/Text/EncoderReplacementFallback.cs rename to external/corefx/src/Common/src/CoreLib/System/Text/EncoderReplacementFallback.cs index 7cd4ec15c5..a1d0bbcd95 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Text/EncoderReplacementFallback.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Text/EncoderReplacementFallback.cs @@ -2,8 +2,9 @@ // 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.Runtime; using System.Diagnostics; -using System.Diagnostics.Contracts; namespace System.Text { @@ -22,7 +23,6 @@ namespace System.Text // Must not be null if (replacement == null) throw new ArgumentNullException(nameof(replacement)); - Contract.EndContractBlock(); // Make sure it doesn't have bad surrogate pairs bool bFoundHigh = false; @@ -58,7 +58,7 @@ namespace System.Text break; } if (bFoundHigh) - throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex, nameof(replacement)); + throw new ArgumentException(SR.Format(SR.Argument_InvalidCharSequenceNoIndex, nameof(replacement))); _strDefault = replacement; } @@ -146,14 +146,11 @@ namespace System.Text // Double check input surrogate pair if (!Char.IsHighSurrogate(charUnknownHigh)) throw new ArgumentOutOfRangeException(nameof(charUnknownHigh), - SR.Format(SR.ArgumentOutOfRange_Range, - 0xD800, 0xDBFF)); + SR.Format(SR.ArgumentOutOfRange_Range, 0xD800, 0xDBFF)); if (!Char.IsLowSurrogate(charUnknownLow)) throw new ArgumentOutOfRangeException(nameof(charUnknownLow), - SR.Format(SR.ArgumentOutOfRange_Range, - 0xDC00, 0xDFFF)); - Contract.EndContractBlock(); + SR.Format(SR.ArgumentOutOfRange_Range, 0xDC00, 0xDFFF)); // If we had a buffer already we're being recursive, throw, it's probably at the suspect // character in our array. diff --git a/external/corert/src/System.Private.CoreLib/src/System/Text/Encoding.cs b/external/corefx/src/Common/src/CoreLib/System/Text/Encoding.cs similarity index 90% rename from external/corert/src/System.Private.CoreLib/src/System/Text/Encoding.cs rename to external/corefx/src/Common/src/CoreLib/System/Text/Encoding.cs index 99b3dda9ae..e469180ce6 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Text/Encoding.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Text/Encoding.cs @@ -3,8 +3,11 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; +using System.Globalization; using System.Threading; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Diagnostics.CodeAnalysis; namespace System.Text { @@ -73,8 +76,16 @@ namespace System.Text public abstract class Encoding : ICloneable { - private static Encoding defaultEncoding; - + // For netcore we use UTF8 as default encoding since ANSI isn't available + private static readonly UTF8Encoding.UTF8EncodingSealed s_defaultEncoding = new UTF8Encoding.UTF8EncodingSealed(encoderShouldEmitUTF8Identifier: false); + + // Returns an encoding for the system's current ANSI code page. + public static Encoding Default => s_defaultEncoding; + + // + // The following values are from mlang.idl. These values + // should be in sync with those in mlang.idl. + // internal const int MIMECONTF_MAILNEWS = 0x00000001; internal const int MIMECONTF_BROWSER = 0x00000002; internal const int MIMECONTF_SAVABLE_MAILNEWS = 0x00000100; @@ -143,14 +154,12 @@ namespace System.Text private const int CodePageUTF32 = 12000; private const int CodePageUTF32BE = 12001; - internal int m_codePage = 0; - - internal CodePageDataItem dataItem = null; + internal int _codePage = 0; - private string _webName = null; - private string _encodingName = null; + internal CodePageDataItem _dataItem = null; // Because of encoders we may be read only + [OptionalField(VersionAdded = 2)] private bool _isReadOnly = true; // Encoding (encoder) fallback @@ -169,17 +178,16 @@ namespace System.Text { throw new ArgumentOutOfRangeException(nameof(codePage)); } - Contract.EndContractBlock(); // Remember code page - m_codePage = codePage; + _codePage = codePage; // Use default encoder/decoder fallbacks this.SetDefaultFallbacks(); } // This constructor is needed to allow any sub-classing implementation to provide encoder/decoder fallback objects - // because the encoding object is always created as read-only object and don’t allow setting encoder/decoder fallback + // because the encoding object is always created as read-only object and don't allow setting encoder/decoder fallback // after the creation is done. protected Encoding(int codePage, EncoderFallback encoderFallback, DecoderFallback decoderFallback) { @@ -188,10 +196,9 @@ namespace System.Text { throw new ArgumentOutOfRangeException(nameof(codePage)); } - Contract.EndContractBlock(); // Remember code page - m_codePage = codePage; + _codePage = codePage; this.encoderFallback = encoderFallback ?? new InternalEncoderBestFitFallback(this); this.decoderFallback = decoderFallback ?? new InternalDecoderBestFitFallback(this); @@ -202,8 +209,8 @@ namespace System.Text { // For UTF-X encodings, we use a replacement fallback with an "\xFFFD" string, // For ASCII we use "?" replacement fallback, etc. - this.encoderFallback = new InternalEncoderBestFitFallback(this); - this.decoderFallback = new InternalDecoderBestFitFallback(this); + encoderFallback = new InternalEncoderBestFitFallback(this); + decoderFallback = new InternalDecoderBestFitFallback(this); } // Converts a byte array from one encoding to another. The bytes in the @@ -211,13 +218,11 @@ namespace System.Text // dstEncoding, and the returned value is a new byte array // containing the result of the conversion. // - [Pure] public static byte[] Convert(Encoding srcEncoding, Encoding dstEncoding, byte[] bytes) { if (bytes == null) throw new ArgumentNullException(nameof(bytes)); - Contract.Ensures(Contract.Result() != null); return Convert(srcEncoding, dstEncoding, bytes, 0, bytes.Length); } @@ -227,7 +232,6 @@ namespace System.Text // index index from srcEncoding to dstEncoding, and // returns a new byte array containing the result of the conversion. // - [Pure] public static byte[] Convert(Encoding srcEncoding, Encoding dstEncoding, byte[] bytes, int index, int count) { @@ -241,7 +245,6 @@ namespace System.Text throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); } - Contract.Ensures(Contract.Result() != null); return dstEncoding.GetBytes(srcEncoding.GetChars(bytes, index, count)); } @@ -252,7 +255,6 @@ namespace System.Text EncodingProvider.AddProvider(provider); } - [Pure] public static Encoding GetEncoding(int codepage) { Encoding result = EncodingProvider.GetEncodingFromProvider(codepage); @@ -263,20 +265,18 @@ namespace System.Text // NOTE: If you add a new encoding that can be retrieved by codepage, be sure to // add the corresponding item in EncodingTable. // Otherwise, the code below will throw exception when trying to call - // EncodingTable.GetWebNameFromCodePage(). + // EncodingTable.GetDataItem(). // if (codepage < 0 || codepage > 65535) { throw new ArgumentOutOfRangeException( - nameof(codepage), SR.Format(SR.ArgumentOutOfRange_Range, - 0, 65535)); + nameof(codepage), SR.Format(SR.ArgumentOutOfRange_Range, 0, 65535)); } - Contract.EndContractBlock(); switch (codepage) { - case CodePageDefault: return UTF8; // 0 + case CodePageDefault: return Default; // 0 case CodePageUnicode: return Unicode; // 1200 case CodePageBigEndian: return BigEndianUnicode; // 1201 case CodePageUTF32: return UTF32; // 12000 @@ -291,8 +291,7 @@ namespace System.Text case CodePageNoMac: // 2 CP_MACCP case CodePageNoThread: // 3 CP_THREAD_ACP case CodePageNoSymbol: // 42 CP_SYMBOL - throw new ArgumentException(SR.Format( - SR.Argument_CodepageNotSupported, codepage), nameof(codepage)); + throw new ArgumentException(SR.Format(SR.Argument_CodepageNotSupported, codepage), nameof(codepage)); } // Is it a valid code page? @@ -305,7 +304,6 @@ namespace System.Text return UTF8; } - [Pure] public static Encoding GetEncoding(int codepage, EncoderFallback encoderFallback, DecoderFallback decoderFallback) { @@ -327,7 +325,6 @@ namespace System.Text // Returns an Encoding object for a given name or a given code page value. // - [Pure] public static Encoding GetEncoding(String name) { Encoding baseEncoding = EncodingProvider.GetEncodingFromProvider(name); @@ -345,7 +342,6 @@ namespace System.Text // Returns an Encoding object for a given name or a given code page value. // - [Pure] public static Encoding GetEncoding(String name, EncoderFallback encoderFallback, DecoderFallback decoderFallback) { @@ -363,27 +359,26 @@ namespace System.Text } // Return a list of all EncodingInfo objects describing all of our encodings - [Pure] public static EncodingInfo[] GetEncodings() { return EncodingTable.GetEncodings(); } - [Pure] public virtual byte[] GetPreamble() { return Array.Empty(); } + public virtual ReadOnlySpan Preamble => GetPreamble(); + private void GetDataItem() { - if (dataItem == null) + if (_dataItem == null) { - dataItem = EncodingTable.GetCodePageDataItem(m_codePage); - if (dataItem == null) + _dataItem = EncodingTable.GetCodePageDataItem(_codePage); + if (_dataItem == null) { - throw new NotSupportedException( - SR.Format(SR.NotSupported_NoCodepageData, m_codePage)); + throw new NotSupportedException(SR.Format(SR.NotSupported_NoCodepageData, _codePage)); } } } @@ -395,45 +390,40 @@ namespace System.Text { get { - if (dataItem == null) + if (_dataItem == null) { GetDataItem(); } - return (dataItem.BodyName); + return (_dataItem.BodyName); } } // Returns the human-readable description of the encoding ( e.g. Hebrew (DOS)). - +#if PROJECTN public virtual String EncodingName { get { - if (_encodingName == null) + string encodingName = GetLocalizedEncodingNameResource(this.CodePage); + if (encodingName == null) { - _encodingName = GetLocalizedEncodingNameResource(this.CodePage); - if (_encodingName == null) - { - throw new NotSupportedException( - SR.Format(SR.MissingEncodingNameResource, this.CodePage)); - } + throw new NotSupportedException(SR.Format(SR.MissingEncodingNameResource, this.CodePage)); + } - if (_encodingName.StartsWith("Globalization_cp_", StringComparison.Ordinal)) + if (encodingName.StartsWith("Globalization_cp_", StringComparison.Ordinal)) + { + // On ProjectN, resource strings are stripped from retail builds and replaced by + // their identifier names. Since this property is meant to be a localized string, + // but we don't localize ProjectN, we specifically need to do something reasonable + // in this case. This currently returns the English name of the encoding from a + // static data table. + encodingName = EncodingTable.GetCodePageDataItem(this.CodePage).EnglishName; + if (encodingName == null) { - // On ProjectN, resource strings are stripped from retail builds and replaced by - // their identifier names. Since this property is meant to be a localized string, - // but we don't localize ProjectN, we specifically need to do something reasonable - // in this case. This currently returns the English name of the encoding from a - // static data table. - _encodingName = EncodingTable.GetCodePageDataItem(this.CodePage).EnglishName; - if (_encodingName == null) - { - throw new NotSupportedException( - SR.Format(SR.MissingEncodingNameResource, this.WebName, this.CodePage)); - } + throw new NotSupportedException(SR.Format(SR.MissingEncodingNameResource, this.WebName, this.CodePage)); } } - return _encodingName; + return encodingName; } } @@ -452,7 +442,15 @@ namespace System.Text default: return null; } } - +#else + public virtual String EncodingName + { + get + { + return SR.GetResourceString("Globalization_cp_" + _codePage.ToString()); + } + } +#endif // Returns the name for this encoding that can be used with mail agent header // tags. If the encoding may not be used, the string is empty. @@ -460,11 +458,11 @@ namespace System.Text { get { - if (dataItem == null) + if (_dataItem == null) { GetDataItem(); } - return (dataItem.HeaderName); + return (_dataItem.HeaderName); } } @@ -473,11 +471,11 @@ namespace System.Text { get { - if (dataItem == null) + if (_dataItem == null) { GetDataItem(); } - return (dataItem.WebName); + return (_dataItem.WebName); } } @@ -487,11 +485,11 @@ namespace System.Text { get { - if (dataItem == null) + if (_dataItem == null) { GetDataItem(); } - return (dataItem.UIFamilyCodePage); + return (_dataItem.UIFamilyCodePage); } } @@ -502,11 +500,11 @@ namespace System.Text { get { - if (dataItem == null) + if (_dataItem == null) { GetDataItem(); } - return ((dataItem.Flags & MIMECONTF_BROWSER) != 0); + return ((_dataItem.Flags & MIMECONTF_BROWSER) != 0); } } @@ -516,11 +514,11 @@ namespace System.Text { get { - if (dataItem == null) + if (_dataItem == null) { GetDataItem(); } - return ((dataItem.Flags & MIMECONTF_SAVABLE_BROWSER) != 0); + return ((_dataItem.Flags & MIMECONTF_SAVABLE_BROWSER) != 0); } } @@ -530,11 +528,11 @@ namespace System.Text { get { - if (dataItem == null) + if (_dataItem == null) { GetDataItem(); } - return ((dataItem.Flags & MIMECONTF_MAILNEWS) != 0); + return ((_dataItem.Flags & MIMECONTF_MAILNEWS) != 0); } } @@ -546,11 +544,11 @@ namespace System.Text { get { - if (dataItem == null) + if (_dataItem == null) { GetDataItem(); } - return ((dataItem.Flags & MIMECONTF_SAVABLE_MAILNEWS) != 0); + return ((_dataItem.Flags & MIMECONTF_SAVABLE_MAILNEWS) != 0); } } @@ -579,7 +577,6 @@ namespace System.Text if (value == null) throw new ArgumentNullException(nameof(value)); - Contract.EndContractBlock(); encoderFallback = value; } @@ -600,7 +597,6 @@ namespace System.Text if (value == null) throw new ArgumentNullException(nameof(value)); - Contract.EndContractBlock(); decoderFallback = value; } @@ -639,7 +635,6 @@ namespace System.Text // Returns the number of bytes required to encode the given character // array. // - [Pure] public virtual int GetByteCount(char[] chars) { if (chars == null) @@ -647,17 +642,14 @@ namespace System.Text throw new ArgumentNullException(nameof(chars), SR.ArgumentNull_Array); } - Contract.EndContractBlock(); return GetByteCount(chars, 0, chars.Length); } - [Pure] public virtual int GetByteCount(String s) { if (s == null) throw new ArgumentNullException(nameof(s)); - Contract.EndContractBlock(); char[] chars = s.ToCharArray(); return GetByteCount(chars, 0, chars.Length); @@ -666,12 +658,10 @@ namespace System.Text // Returns the number of bytes required to encode a range of characters in // a character array. // - [Pure] public abstract int GetByteCount(char[] chars, int index, int count); // Returns the number of bytes required to encode a string range. // - [Pure] public int GetByteCount(string s, int index, int count) { if (s == null) @@ -686,7 +676,6 @@ namespace System.Text if (index > s.Length - count) throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_IndexCount); - Contract.EndContractBlock(); unsafe { @@ -701,7 +690,6 @@ namespace System.Text // unfortunately for existing overrides, it has to call the [] version, // which is really slow, so this method should be avoided if you're calling // a 3rd party encoding. - [Pure] [CLSCompliant(false)] public virtual unsafe int GetByteCount(char* chars, int count) { @@ -713,7 +701,6 @@ namespace System.Text if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); char[] arrChar = new char[count]; int index; @@ -724,6 +711,14 @@ namespace System.Text return GetByteCount(arrChar, 0, count); } + public virtual unsafe int GetByteCount(ReadOnlySpan chars) + { + fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) + { + return GetByteCount(charsPtr, chars.Length); + } + } + // For NLS Encodings, workhorse takes an encoder (may be null) // Always validate parameters before calling internal version, which will only assert. internal virtual unsafe int GetByteCount(char* chars, int count, EncoderNLS encoder) @@ -737,7 +732,6 @@ namespace System.Text // Returns a byte array containing the encoded representation of the given // character array. // - [Pure] public virtual byte[] GetBytes(char[] chars) { if (chars == null) @@ -745,14 +739,12 @@ namespace System.Text throw new ArgumentNullException(nameof(chars), SR.ArgumentNull_Array); } - Contract.EndContractBlock(); return GetBytes(chars, 0, chars.Length); } // Returns a byte array containing the encoded representation of a range // of characters in a character array. // - [Pure] public virtual byte[] GetBytes(char[] chars, int index, int count) { byte[] result = new byte[GetByteCount(chars, index, count)]; @@ -775,13 +767,11 @@ namespace System.Text // Returns a byte array containing the encoded representation of the given // string. // - [Pure] public virtual byte[] GetBytes(String s) { if (s == null) throw new ArgumentNullException(nameof(s), SR.ArgumentNull_String); - Contract.EndContractBlock(); int byteCount = GetByteCount(s); byte[] bytes = new byte[byteCount]; @@ -793,7 +783,6 @@ namespace System.Text // Returns a byte array containing the encoded representation of the given // string range. // - [Pure] public byte[] GetBytes(string s, int index, int count) { if (s == null) @@ -808,7 +797,6 @@ namespace System.Text if (index > s.Length - count) throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_IndexCount); - Contract.EndContractBlock(); unsafe { @@ -834,7 +822,6 @@ namespace System.Text { if (s == null) throw new ArgumentNullException(nameof(s)); - Contract.EndContractBlock(); return GetBytes(s.ToCharArray(), charIndex, charCount, bytes, byteIndex); } @@ -875,7 +862,6 @@ namespace System.Text if (charCount < 0 || byteCount < 0) throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Get the char array to convert char[] arrChar = new char[charCount]; @@ -907,10 +893,18 @@ namespace System.Text return byteCount; } + public virtual unsafe int GetBytes(ReadOnlySpan chars, Span bytes) + { + fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) + fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + { + return GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length); + } + } + // Returns the number of characters produced by decoding the given byte // array. // - [Pure] public virtual int GetCharCount(byte[] bytes) { if (bytes == null) @@ -918,19 +912,16 @@ namespace System.Text throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); } - Contract.EndContractBlock(); return GetCharCount(bytes, 0, bytes.Length); } // Returns the number of characters produced by decoding a range of bytes // in a byte array. // - [Pure] public abstract int GetCharCount(byte[] bytes, int index, int count); // We expect this to be the workhorse for NLS Encodings, but for existing // ones we need a working (if slow) default implementation) - [Pure] [CLSCompliant(false)] public virtual unsafe int GetCharCount(byte* bytes, int count) { @@ -942,7 +933,6 @@ namespace System.Text if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); byte[] arrbyte = new byte[count]; int index; @@ -953,6 +943,14 @@ namespace System.Text return GetCharCount(arrbyte, 0, count); } + public virtual unsafe int GetCharCount(ReadOnlySpan bytes) + { + fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + { + return GetCharCount(bytesPtr, bytes.Length); + } + } + // This is our internal workhorse // Always validate parameters before calling internal version, which will only assert. internal virtual unsafe int GetCharCount(byte* bytes, int count, DecoderNLS decoder) @@ -963,7 +961,6 @@ namespace System.Text // Returns a character array containing the decoded representation of a // given byte array. // - [Pure] public virtual char[] GetChars(byte[] bytes) { if (bytes == null) @@ -971,14 +968,12 @@ namespace System.Text throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); } - Contract.EndContractBlock(); return GetChars(bytes, 0, bytes.Length); } // Returns a character array containing the decoded representation of a // range of bytes in a byte array. // - [Pure] public virtual char[] GetChars(byte[] bytes, int index, int count) { char[] result = new char[GetCharCount(bytes, index, count)]; @@ -992,7 +987,7 @@ namespace System.Text // GetCharCount method can be used to determine the exact number of // characters that will be produced for a given range of bytes. // Alternatively, the GetMaxCharCount method can be used to - // determine the maximum number of characterss that will be produced for a + // determine the maximum number of characters that will be produced for a // given number of bytes, regardless of the actual byte values. // @@ -1029,7 +1024,6 @@ namespace System.Text if (byteCount < 0 || charCount < 0) throw new ArgumentOutOfRangeException((byteCount < 0 ? nameof(byteCount) : nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Get the byte array to convert byte[] arrByte = new byte[byteCount]; @@ -1061,6 +1055,14 @@ namespace System.Text return charCount; } + public virtual unsafe int GetChars(ReadOnlySpan bytes, Span chars) + { + fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) + { + return GetChars(bytesPtr, bytes.Length, charsPtr, chars.Length); + } + } // This is our internal workhorse // Always validate parameters before calling internal version, which will only assert. @@ -1079,11 +1081,19 @@ namespace System.Text if (byteCount < 0) throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); return String.CreateStringFromEncoding(bytes, byteCount, this); } + public unsafe string GetString(ReadOnlySpan bytes) + { + fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + { + return GetString(bytesPtr, bytes.Length); + } + } + + // Returns the code page identifier of this encoding. The returned value is // an integer between 0 and 65535 if the encoding has a code page // identifier, or -1 if the encoding does not represent a code page. @@ -1093,19 +1103,17 @@ namespace System.Text { get { - return m_codePage; + return _codePage; } } // IsAlwaysNormalized // Returns true if the encoding is always normalized for the specified encoding form - [Pure] public bool IsAlwaysNormalized() { return this.IsAlwaysNormalized(NormalizationForm.FormC); } - [Pure] public virtual bool IsAlwaysNormalized(NormalizationForm form) { // Assume false unless the encoding knows otherwise @@ -1131,26 +1139,6 @@ namespace System.Text return new DefaultDecoder(this); } - private static Encoding CreateDefaultEncoding() - { - // defaultEncoding should be null if we get here, but we can't - // assert that in case another thread beat us to the initialization - - Encoding enc; - - - // For silverlight we use UTF8 since ANSI isn't available - enc = UTF8; - - - // This method should only ever return one Encoding instance - return Interlocked.CompareExchange(ref defaultEncoding, enc, null) ?? enc; - } - - // Returns an encoding for the system's current ANSI code page. - - public static Encoding Default => defaultEncoding ?? CreateDefaultEncoding(); - // Returns an Encoder object for this encoding. The returned object // can be used to encode a sequence of characters into a sequence of bytes. // Contrary to the GetBytes family of methods, an Encoder can @@ -1181,7 +1169,6 @@ namespace System.Text // WARNING: If you're using something besides the default replacement encoder fallback, // then you could have more bytes than this returned from an actual call to GetBytes(). // - [Pure] public abstract int GetMaxByteCount(int charCount); // Returns the maximum number of characters produced by decoding a given @@ -1192,19 +1179,16 @@ namespace System.Text // exceptions will occur if buffers are sized according to the results of // this method. // - [Pure] public abstract int GetMaxCharCount(int byteCount); // Returns a string containing the decoded representation of a given byte // array. // - [Pure] public virtual String GetString(byte[] bytes) { if (bytes == null) throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); - Contract.EndContractBlock(); return GetString(bytes, 0, bytes.Length); } @@ -1214,7 +1198,6 @@ namespace System.Text // // Internally we override this for performance // - [Pure] public virtual String GetString(byte[] bytes, int index, int count) { return new String(GetChars(bytes, index, count)); @@ -1262,7 +1245,7 @@ namespace System.Text { Encoding that = value as Encoding; if (that != null) - return (m_codePage == that.m_codePage) && + return (_codePage == that._codePage) && (EncoderFallback.Equals(that.EncoderFallback)) && (DecoderFallback.Equals(that.DecoderFallback)); return (false); @@ -1271,7 +1254,7 @@ namespace System.Text public override int GetHashCode() { - return m_codePage + this.EncoderFallback.GetHashCode() + this.DecoderFallback.GetHashCode(); + return _codePage + this.EncoderFallback.GetHashCode() + this.DecoderFallback.GetHashCode(); } internal virtual char[] GetBestFitUnicodeToBytesData() @@ -1289,20 +1272,19 @@ namespace System.Text internal void ThrowBytesOverflow() { // Special message to include fallback type in case fallback's GetMaxCharCount is broken - // This happens if user has implimented an encoder fallback with a broken GetMaxCharCount + // This happens if user has implemented an encoder fallback with a broken GetMaxCharCount throw new ArgumentException( - SR.Format(SR.Argument_EncodingConversionOverflowBytes, - EncodingName, EncoderFallback.GetType()), "bytes"); + SR.Format(SR.Argument_EncodingConversionOverflowBytes, EncodingName, EncoderFallback.GetType()), "bytes"); } internal void ThrowBytesOverflow(EncoderNLS encoder, bool nothingEncoded) { - if (encoder == null || encoder.m_throwOnOverflow || nothingEncoded) + if (encoder == null || encoder._throwOnOverflow || nothingEncoded) { if (encoder != null && encoder.InternalHasFallbackBuffer) encoder.FallbackBuffer.InternalReset(); // Special message to include fallback type in case fallback's GetMaxCharCount is broken - // This happens if user has implimented an encoder fallback with a broken GetMaxCharCount + // This happens if user has implemented an encoder fallback with a broken GetMaxCharCount ThrowBytesOverflow(); } @@ -1313,21 +1295,20 @@ namespace System.Text internal void ThrowCharsOverflow() { // Special message to include fallback type in case fallback's GetMaxCharCount is broken - // This happens if user has implimented a decoder fallback with a broken GetMaxCharCount + // This happens if user has implemented a decoder fallback with a broken GetMaxCharCount throw new ArgumentException( - SR.Format(SR.Argument_EncodingConversionOverflowChars, - EncodingName, DecoderFallback.GetType()), "chars"); + SR.Format(SR.Argument_EncodingConversionOverflowChars, EncodingName, DecoderFallback.GetType()), "chars"); } internal void ThrowCharsOverflow(DecoderNLS decoder, bool nothingDecoded) { - if (decoder == null || decoder.m_throwOnOverflow || nothingDecoded) + if (decoder == null || decoder._throwOnOverflow || nothingDecoded) { if (decoder != null && decoder.InternalHasFallbackBuffer) decoder.FallbackBuffer.InternalReset(); // Special message to include fallback type in case fallback's GetMaxCharCount is broken - // This happens if user has implimented a decoder fallback with a broken GetMaxCharCount + // This happens if user has implemented a decoder fallback with a broken GetMaxCharCount ThrowCharsOverflow(); } @@ -1335,7 +1316,7 @@ namespace System.Text decoder.ClearMustFlush(); } - internal class DefaultEncoder : Encoder // , IObjectReference + internal sealed class DefaultEncoder : Encoder, IObjectReference { private Encoding _encoding; @@ -1343,6 +1324,11 @@ namespace System.Text { _encoding = encoding; } + + public Object GetRealObject(StreamingContext context) + { + throw new PlatformNotSupportedException(); + } // Returns the number of bytes the next call to GetBytes will // produce if presented with the given range of characters and the given @@ -1395,7 +1381,7 @@ namespace System.Text } } - internal class DefaultDecoder : Decoder // , IObjectReference + internal sealed class DefaultDecoder : Decoder, IObjectReference { private Encoding _encoding; @@ -1404,6 +1390,11 @@ namespace System.Text _encoding = encoding; } + public Object GetRealObject(StreamingContext context) + { + throw new PlatformNotSupportedException(); + } + // Returns the number of characters the next call to GetChars will // produce if presented with the given range of bytes. The returned value // takes into account the state in which the decoder was left following the @@ -1670,7 +1661,7 @@ namespace System.Text { this.fallbackBuffer = _encoder.FallbackBuffer; // If we're not converting we must not have data in our fallback buffer - if (_encoder.m_throwOnOverflow && _encoder.InternalHasFallbackBuffer && + if (_encoder._throwOnOverflow && _encoder.InternalHasFallbackBuffer && this.fallbackBuffer.Remaining > 0) throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, _encoder.Encoding.EncodingName, _encoder.Fallback.GetType())); diff --git a/external/corefx/src/Common/src/CoreLib/System/Text/EncodingInfo.cs b/external/corefx/src/Common/src/CoreLib/System/Text/EncodingInfo.cs new file mode 100644 index 0000000000..99995f759b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Text/EncodingInfo.cs @@ -0,0 +1,71 @@ +// 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.Text; + +namespace System.Text +{ + public sealed class EncodingInfo + { + private int iCodePage; // Code Page # + private string strEncodingName; // Short name (web name) + private string strDisplayName; // Full localized name + + internal EncodingInfo(int codePage, string name, string displayName) + { + iCodePage = codePage; + strEncodingName = name; + strDisplayName = displayName; + } + + + public int CodePage + { + get + { + return iCodePage; + } + } + + + public string Name + { + get + { + return strEncodingName; + } + } + + + public string DisplayName + { + get + { + return strDisplayName; + } + } + + + public Encoding GetEncoding() + { + return Encoding.GetEncoding(iCodePage); + } + + public override bool Equals(Object value) + { + EncodingInfo that = value as EncodingInfo; + if (that != null) + { + return (this.CodePage == that.CodePage); + } + return (false); + } + + public override int GetHashCode() + { + return this.CodePage; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Text/EncodingNLS.cs b/external/corefx/src/Common/src/CoreLib/System/Text/EncodingNLS.cs new file mode 100644 index 0000000000..d5de9e553a --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Text/EncodingNLS.cs @@ -0,0 +1,298 @@ +// 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.Collections; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Threading; + +namespace System.Text +{ + // This class overrides Encoding with the things we need for our NLS Encodings + // + // All of the GetBytes/Chars GetByte/CharCount methods are just wrappers for the pointer + // plus decoder/encoder method that is our real workhorse. Note that this is an internal + // class, so our public classes cannot derive from this class. Because of this, all of the + // GetBytes/Chars GetByte/CharCount wrapper methods are duplicated in all of our public + // encodings, which currently include: + // + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, & UnicodeEncoding + // + // So if you change the wrappers in this class, you must change the wrappers in the other classes + // as well because they should have the same behavior. + + internal abstract class EncodingNLS : Encoding + { + protected EncodingNLS(int codePage) : base(codePage) + { + } + + // Returns the number of bytes required to encode a range of characters in + // a character array. + // + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + public override unsafe int GetByteCount(char[] chars, int index, int count) + { + // Validate input parameters + if (chars == null) + throw new ArgumentNullException("chars", SR.ArgumentNull_Array); + + if (index < 0 || count < 0) + throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (chars.Length - index < count) + throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); + + // If no input, return 0, avoid fixed empty array problem + if (count == 0) + return 0; + + // Just call the pointer version + fixed (char* pChars = chars) + return GetByteCount(pChars + index, count, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + public override unsafe int GetByteCount(String s) + { + // Validate input + if (s==null) + throw new ArgumentNullException("s"); + + fixed (char* pChars = s) + return GetByteCount(pChars, s.Length, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + public override unsafe int GetByteCount(char* chars, int count) + { + // Validate Parameters + if (chars == null) + throw new ArgumentNullException("chars", SR.ArgumentNull_Array); + + if (count < 0) + throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); + + // Call it with empty encoder + return GetByteCount(chars, count, null); + } + + // Parent method is safe. + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + public override unsafe int GetBytes(String s, int charIndex, int charCount, + byte[] bytes, int byteIndex) + { + if (s == null || bytes == null) + throw new ArgumentNullException((s == null ? "s" : "bytes"), SR.ArgumentNull_Array); + + if (charIndex < 0 || charCount < 0) + throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (s.Length - charIndex < charCount) + throw new ArgumentOutOfRangeException("s", SR.ArgumentOutOfRange_IndexCount); + + if (byteIndex < 0 || byteIndex > bytes.Length) + throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); + + int byteCount = bytes.Length - byteIndex; + + fixed (char* pChars = s) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) + return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null); + } + + // Encodes a range of characters in a character array into a range of bytes + // in a byte array. An exception occurs if the byte array is not large + // enough to hold the complete encoding of the characters. The + // GetByteCount method can be used to determine the exact number of + // bytes that will be produced for a given range of characters. + // Alternatively, the GetMaxByteCount method can be used to + // determine the maximum number of bytes that will be produced for a given + // number of characters, regardless of the actual character values. + // + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + public override unsafe int GetBytes(char[] chars, int charIndex, int charCount, + byte[] bytes, int byteIndex) + { + // Validate parameters + if (chars == null || bytes == null) + throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array); + + if (charIndex < 0 || charCount < 0) + throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (chars.Length - charIndex < charCount) + throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); + + if (byteIndex < 0 || byteIndex > bytes.Length) + throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); + + // If nothing to encode return 0, avoid fixed problem + if (charCount == 0) + return 0; + + // Just call pointer version + int byteCount = bytes.Length - byteIndex; + + fixed (char* pChars = chars) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) + // Remember that byteCount is # to decode, not size of array. + return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount) + { + // Validate Parameters + if (bytes == null || chars == null) + throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + + if (charCount < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + return GetBytes(chars, charCount, bytes, byteCount, null); + } + + // Returns the number of characters produced by decoding a range of bytes + // in a byte array. + // + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + public override unsafe int GetCharCount(byte[] bytes, int index, int count) + { + // Validate Parameters + if (bytes == null) + throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + + if (index < 0 || count < 0) + throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (bytes.Length - index < count) + throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + + // If no input just return 0, fixed doesn't like 0 length arrays + if (count == 0) + return 0; + + // Just call pointer version + fixed (byte* pBytes = bytes) + return GetCharCount(pBytes + index, count, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + public override unsafe int GetCharCount(byte* bytes, int count) + { + // Validate Parameters + if (bytes == null) + throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + + if (count < 0) + throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); + + return GetCharCount(bytes, count, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount, + char[] chars, int charIndex) + { + // Validate Parameters + if (bytes == null || chars == null) + throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + + if (byteIndex < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if ( bytes.Length - byteIndex < byteCount) + throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + + if (charIndex < 0 || charIndex > chars.Length) + throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index); + + // If no input, return 0 & avoid fixed problem + if (byteCount == 0) + return 0; + + // Just call pointer version + int charCount = chars.Length - charIndex; + + fixed (byte* pBytes = bytes) fixed (char* pChars = &MemoryMarshal.GetReference((Span)chars)) + // Remember that charCount is # to decode, not size of array + return GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount) + { + // Validate Parameters + if (bytes == null || chars == null) + throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + + if (charCount < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + return GetChars(bytes, byteCount, chars, charCount, null); + } + + // Returns a string containing the decoded representation of a range of + // bytes in a byte array. + // + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + public override unsafe String GetString(byte[] bytes, int index, int count) + { + // Validate Parameters + if (bytes == null) + throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + + if (index < 0 || count < 0) + throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (bytes.Length - index < count) + throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + + // Avoid problems with empty input buffer + if (count == 0) return String.Empty; + + fixed (byte* pBytes = bytes) + return String.CreateStringFromEncoding( + pBytes + index, count, this); + } + + public override Decoder GetDecoder() + { + return new DecoderNLS(this); + } + + public override Encoder GetEncoder() + { + return new EncoderNLS(this); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Text/EncodingProvider.cs b/external/corefx/src/Common/src/CoreLib/System/Text/EncodingProvider.cs new file mode 100644 index 0000000000..ce8c3e0208 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Text/EncodingProvider.cs @@ -0,0 +1,136 @@ +// 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.Collections; +using System.Collections.Generic; + +namespace System.Text +{ + public abstract class EncodingProvider + { + public EncodingProvider() { } + public abstract Encoding GetEncoding(string name); + public abstract Encoding GetEncoding(int codepage); + + // GetEncoding should return either valid encoding or null. shouldn't throw any exception except on null name + public virtual Encoding GetEncoding(string name, EncoderFallback encoderFallback, DecoderFallback decoderFallback) + { + Encoding enc = GetEncoding(name); + if (enc != null) + { + enc = (Encoding)GetEncoding(name).Clone(); + enc.EncoderFallback = encoderFallback; + enc.DecoderFallback = decoderFallback; + } + + return enc; + } + + public virtual Encoding GetEncoding(int codepage, EncoderFallback encoderFallback, DecoderFallback decoderFallback) + { + Encoding enc = GetEncoding(codepage); + if (enc != null) + { + enc = (Encoding)GetEncoding(codepage).Clone(); + enc.EncoderFallback = encoderFallback; + enc.DecoderFallback = decoderFallback; + } + + return enc; + } + + internal static void AddProvider(EncodingProvider provider) + { + if (provider == null) + throw new ArgumentNullException(nameof(provider)); + + lock (s_InternalSyncObject) + { + if (s_providers == null) + { + s_providers = new EncodingProvider[1] { provider }; + return; + } + + if (Array.IndexOf(s_providers, provider) >= 0) + { + return; + } + + EncodingProvider[] providers = new EncodingProvider[s_providers.Length + 1]; + Array.Copy(s_providers, providers, s_providers.Length); + providers[providers.Length - 1] = provider; + s_providers = providers; + } + } + + internal static Encoding GetEncodingFromProvider(int codepage) + { + if (s_providers == null) + return null; + + EncodingProvider[] providers = s_providers; + foreach (EncodingProvider provider in providers) + { + Encoding enc = provider.GetEncoding(codepage); + if (enc != null) + return enc; + } + + return null; + } + + internal static Encoding GetEncodingFromProvider(string encodingName) + { + if (s_providers == null) + return null; + + EncodingProvider[] providers = s_providers; + foreach (EncodingProvider provider in providers) + { + Encoding enc = provider.GetEncoding(encodingName); + if (enc != null) + return enc; + } + + return null; + } + + internal static Encoding GetEncodingFromProvider(int codepage, EncoderFallback enc, DecoderFallback dec) + { + if (s_providers == null) + return null; + + EncodingProvider[] providers = s_providers; + foreach (EncodingProvider provider in providers) + { + Encoding encing = provider.GetEncoding(codepage, enc, dec); + if (encing != null) + return encing; + } + + return null; + } + + internal static Encoding GetEncodingFromProvider(string encodingName, EncoderFallback enc, DecoderFallback dec) + { + if (s_providers == null) + return null; + + EncodingProvider[] providers = s_providers; + foreach (EncodingProvider provider in providers) + { + Encoding encoding = provider.GetEncoding(encodingName, enc, dec); + if (encoding != null) + return encoding; + } + + return null; + } + + private static Object s_InternalSyncObject = new Object(); + private static volatile EncodingProvider[] s_providers; + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Text/Latin1Encoding.cs b/external/corefx/src/Common/src/CoreLib/System/Text/Latin1Encoding.cs similarity index 82% rename from external/corert/src/System.Private.CoreLib/src/System/Text/Latin1Encoding.cs rename to external/corefx/src/Common/src/CoreLib/System/Text/Latin1Encoding.cs index 612f7dd9f9..335eb76e3c 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Text/Latin1Encoding.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Text/Latin1Encoding.cs @@ -2,8 +2,8 @@ // 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.Diagnostics; -using System.Diagnostics.Contracts; namespace System.Text { @@ -41,14 +41,14 @@ namespace System.Text EncoderReplacementFallback fallback; if (encoder != null) { - charLeftOver = encoder.charLeftOver; + charLeftOver = encoder._charLeftOver; Debug.Assert(charLeftOver == 0 || Char.IsHighSurrogate(charLeftOver), "[Latin1Encoding.GetByteCount]leftover character should be high surrogate"); fallback = encoder.Fallback as EncoderReplacementFallback; // Verify that we have no fallbackbuffer, for Latin1 its always empty, so just assert - Debug.Assert(!encoder.m_throwOnOverflow || !encoder.InternalHasFallbackBuffer || + Debug.Assert(!encoder._throwOnOverflow || !encoder.InternalHasFallbackBuffer || encoder.FallbackBuffer.Remaining == 0, "[Latin1CodePageEncoding.GetByteCount]Expected empty fallback buffer"); } @@ -80,6 +80,7 @@ namespace System.Text // For fallback we may need a fallback buffer, we know we aren't default fallback. EncoderFallbackBuffer fallbackBuffer = null; + char* charsForFallback; // We may have a left over character from last time, try and process it. if (charLeftOver > 0) @@ -93,7 +94,9 @@ namespace System.Text // Since left over char was a surrogate, it'll have to be fallen back. // Get Fallback // This will fallback a pair if *chars is a low surrogate - fallbackBuffer.InternalFallback(charLeftOver, ref chars); + charsForFallback = chars; + fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback); + chars = charsForFallback; } // Now we may have fallback char[] already from the encoder @@ -126,7 +129,9 @@ namespace System.Text } // Get Fallback - fallbackBuffer.InternalFallback(ch, ref chars); + charsForFallback = chars; + fallbackBuffer.InternalFallback(ch, ref charsForFallback); + chars = charsForFallback; continue; } @@ -157,13 +162,13 @@ namespace System.Text EncoderReplacementFallback fallback = null; if (encoder != null) { - charLeftOver = encoder.charLeftOver; + charLeftOver = encoder._charLeftOver; fallback = encoder.Fallback as EncoderReplacementFallback; Debug.Assert(charLeftOver == 0 || Char.IsHighSurrogate(charLeftOver), "[Latin1Encoding.GetBytes]leftover character should be high surrogate"); // Verify that we have no fallbackbuffer, for ASCII its always empty, so just assert - Debug.Assert(!encoder.m_throwOnOverflow || !encoder.InternalHasFallbackBuffer || + Debug.Assert(!encoder._throwOnOverflow || !encoder.InternalHasFallbackBuffer || encoder.FallbackBuffer.Remaining == 0, "[Latin1CodePageEncoding.GetBytes]Expected empty fallback buffer"); } @@ -226,8 +231,8 @@ namespace System.Text // Clear encoder if (encoder != null) { - encoder.charLeftOver = (char)0; - encoder.m_charsUsed = (int)(chars - charStart); + encoder._charLeftOver = (char)0; + encoder._charsUsed = (int)(chars - charStart); } return (int)(bytes - byteStart); } @@ -240,6 +245,7 @@ namespace System.Text // For fallback we may need a fallback buffer, we know we aren't default fallback, create & init it EncoderFallbackBuffer fallbackBuffer = null; + char* charsForFallback; // We may have a left over character from last time, try and process it. if (charLeftOver > 0) @@ -254,7 +260,10 @@ namespace System.Text // Since left over char was a surrogate, it'll have to be fallen back. // Get Fallback // This will fallback a pair if *chars is a low surrogate - fallbackBuffer.InternalFallback(charLeftOver, ref chars); + charsForFallback = chars; + fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback); + chars = charsForFallback; + if (fallbackBuffer.Remaining > byteEnd - bytes) { // Throw it, if we don't have enough for this we never will @@ -292,7 +301,9 @@ namespace System.Text } // Get Fallback - fallbackBuffer.InternalFallback(ch, ref chars); + charsForFallback = chars; + fallbackBuffer.InternalFallback(ch, ref charsForFallback); + chars = charsForFallback; // Make sure we have enough room. Each fallback char will be 1 output char // (or else cause a recursion exception) @@ -341,10 +352,10 @@ namespace System.Text // Fallback stuck it in encoder if necessary, but we have to clear MustFlush cases if (fallbackBuffer != null && !fallbackBuffer.bUsedEncoder) // Clear it in case of MustFlush - encoder.charLeftOver = (char)0; + encoder._charLeftOver = (char)0; // Set our chars used count - encoder.m_charsUsed = (int)(chars - charStart); + encoder._charsUsed = (int)(chars - charStart); } Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0, @@ -397,7 +408,7 @@ namespace System.Text // Might need to know input bytes used if (decoder != null) - decoder.m_bytesUsed = byteCount; + decoder._bytesUsed = byteCount; // Converted sequence is same length as input, so output charsUsed is same as byteCount; return byteCount; @@ -408,7 +419,6 @@ namespace System.Text if (charCount < 0) throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Characters would be # of characters + 1 in case high surrogate is ? * max fallback long byteCount = (long)charCount + 1; @@ -428,7 +438,6 @@ namespace System.Text if (byteCount < 0) throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Just return length, SBCS stay the same length because they don't map to surrogate long charCount = (long)byteCount; @@ -452,113 +461,122 @@ namespace System.Text } } + public override bool IsAlwaysNormalized(NormalizationForm form) + { + // Latin-1 contains precomposed characters, so normal for Form C. + // Since some are composed, not normal for D & KD. + // Also some letters like 0x00A8 (spacing diarisis) have compatibility decompositions, so false for KD & KC. + + // Only true for form C. + return (form == NormalizationForm.FormC); + } // Since our best fit table is small we'll hard code it internal override char[] GetBestFitUnicodeToBytesData() { // Get our best fit data - return Latin1Encoding.s_arrayCharBestFit; + return Latin1Encoding.arrayCharBestFit; } // Best fit for ASCII, and since it works for ASCII, we use it for latin1 as well. - private static readonly char[] s_arrayCharBestFit = + private static readonly char[] arrayCharBestFit = { - // The first many are in case you wanted to use this for ASCIIEncoding, which we don't need to do any more. - // (char)0x00a0, (char)0x0020, // No-Break Space -> Space - // (char)0x00a1, (char)0x0021, // Inverted Exclamation Mark -> ! - // (char)0x00a2, (char)0x0063, // Cent Sign -> c - // (char)0x00a3, (char)0x003f, // Pound Sign - // (char)0x00a4, (char)0x0024, // Currency Sign -> $ - // (char)0x00a5, (char)0x0059, // Yen Sign -> Y - // (char)0x00a6, (char)0x007c, // Broken Bar -> | - // (char)0x00a7, (char)0x003f, // Section Sign - // (char)0x00a8, (char)0x003f, // Diaeresis - // (char)0x00a9, (char)0x0043, // Copyright Sign -> C - // (char)0x00aa, (char)0x0061, // Feminine Ordinal Indicator -> a - // (char)0x00ab, (char)0x003c, // Left-Pointing Double Angle Quotation Mark -> < - // (char)0x00ac, (char)0x003f, // Not Sign - // (char)0x00ad, (char)0x002d, // Soft Hyphen -> - - // (char)0x00ae, (char)0x0052, // Registered Sign -> R - // (char)0x00af, (char)0x003f, // Macron - // (char)0x00b0, (char)0x003f, // Degree Sign - // (char)0x00b1, (char)0x003f, // Plus-Minus Sign - // (char)0x00b2, (char)0x0032, // Superscript Two -> 2 - // (char)0x00b3, (char)0x0033, // Superscript Three -> 3 - // (char)0x00b4, (char)0x003f, // Acute Accent - // (char)0x00b5, (char)0x003f, // Micro Sign - // (char)0x00b6, (char)0x003f, // Pilcrow Sign - // (char)0x00b7, (char)0x002e, // Middle Dot -> . - // (char)0x00b8, (char)0x002c, // Cedilla -> , - // (char)0x00b9, (char)0x0031, // Superscript One -> 1 - // (char)0x00ba, (char)0x006f, // Masculine Ordinal Indicator -> o - // (char)0x00bb, (char)0x003e, // Right-Pointing Double Angle Quotation Mark -> > - // (char)0x00bc, (char)0x003f, // Vulgar Fraction One Quarter - // (char)0x00bd, (char)0x003f, // Vulgar Fraction One Half - // (char)0x00be, (char)0x003f, // Vulgar Fraction Three Quarters - // (char)0x00bf, (char)0x003f, // Inverted Question Mark - // (char)0x00c0, (char)0x0041, // Latin Capital Letter A With Grave -> A - // (char)0x00c1, (char)0x0041, // Latin Capital Letter A With Acute -> A - // (char)0x00c2, (char)0x0041, // Latin Capital Letter A With Circumflex -> A - // (char)0x00c3, (char)0x0041, // Latin Capital Letter A With Tilde -> A - // (char)0x00c4, (char)0x0041, // Latin Capital Letter A With Diaeresis -> A - // (char)0x00c5, (char)0x0041, // Latin Capital Letter A With Ring Above -> A - // (char)0x00c6, (char)0x0041, // Latin Capital Ligature Ae -> A - // (char)0x00c7, (char)0x0043, // Latin Capital Letter C With Cedilla -> C - // (char)0x00c8, (char)0x0045, // Latin Capital Letter E With Grave -> E - // (char)0x00c9, (char)0x0045, // Latin Capital Letter E With Acute -> E - // (char)0x00ca, (char)0x0045, // Latin Capital Letter E With Circumflex -> E - // (char)0x00cb, (char)0x0045, // Latin Capital Letter E With Diaeresis -> E - // (char)0x00cc, (char)0x0049, // Latin Capital Letter I With Grave -> I - // (char)0x00cd, (char)0x0049, // Latin Capital Letter I With Acute -> I - // (char)0x00ce, (char)0x0049, // Latin Capital Letter I With Circumflex -> I - // (char)0x00cf, (char)0x0049, // Latin Capital Letter I With Diaeresis -> I - // (char)0x00d0, (char)0x0044, // Latin Capital Letter Eth -> D - // (char)0x00d1, (char)0x004e, // Latin Capital Letter N With Tilde -> N - // (char)0x00d2, (char)0x004f, // Latin Capital Letter O With Grave -> O - // (char)0x00d3, (char)0x004f, // Latin Capital Letter O With Acute -> O - // (char)0x00d4, (char)0x004f, // Latin Capital Letter O With Circumflex -> O - // (char)0x00d5, (char)0x004f, // Latin Capital Letter O With Tilde -> O - // (char)0x00d6, (char)0x004f, // Latin Capital Letter O With Diaeresis -> O - // (char)0x00d7, (char)0x003f, // Multiplication Sign - // (char)0x00d8, (char)0x004f, // Latin Capital Letter O With Stroke -> O - // (char)0x00d9, (char)0x0055, // Latin Capital Letter U With Grave -> U - // (char)0x00da, (char)0x0055, // Latin Capital Letter U With Acute -> U - // (char)0x00db, (char)0x0055, // Latin Capital Letter U With Circumflex -> U - // (char)0x00dc, (char)0x0055, // Latin Capital Letter U With Diaeresis -> U - // (char)0x00dd, (char)0x0059, // Latin Capital Letter Y With Acute -> Y - // (char)0x00de, (char)0x003f, // Latin Capital Letter Thorn - // (char)0x00df, (char)0x003f, // Latin Small Letter Sharp S - // (char)0x00e0, (char)0x0061, // Latin Small Letter A With Grave -> a - // (char)0x00e1, (char)0x0061, // Latin Small Letter A With Acute -> a - // (char)0x00e2, (char)0x0061, // Latin Small Letter A With Circumflex -> a - // (char)0x00e3, (char)0x0061, // Latin Small Letter A With Tilde -> a - // (char)0x00e4, (char)0x0061, // Latin Small Letter A With Diaeresis -> a - // (char)0x00e5, (char)0x0061, // Latin Small Letter A With Ring Above -> a - // (char)0x00e6, (char)0x0061, // Latin Small Ligature Ae -> a - // (char)0x00e7, (char)0x0063, // Latin Small Letter C With Cedilla -> c - // (char)0x00e8, (char)0x0065, // Latin Small Letter E With Grave -> e - // (char)0x00e9, (char)0x0065, // Latin Small Letter E With Acute -> e - // (char)0x00ea, (char)0x0065, // Latin Small Letter E With Circumflex -> e - // (char)0x00eb, (char)0x0065, // Latin Small Letter E With Diaeresis -> e - // (char)0x00ec, (char)0x0069, // Latin Small Letter I With Grave -> i - // (char)0x00ed, (char)0x0069, // Latin Small Letter I With Acute -> i - // (char)0x00ee, (char)0x0069, // Latin Small Letter I With Circumflex -> i - // (char)0x00ef, (char)0x0069, // Latin Small Letter I With Diaeresis -> i - // (char)0x00f0, (char)0x003f, // Latin Small Letter Eth - // (char)0x00f1, (char)0x006e, // Latin Small Letter N With Tilde -> n - // (char)0x00f2, (char)0x006f, // Latin Small Letter O With Grave -> o - // (char)0x00f3, (char)0x006f, // Latin Small Letter O With Acute -> o - // (char)0x00f4, (char)0x006f, // Latin Small Letter O With Circumflex -> o - // (char)0x00f5, (char)0x006f, // Latin Small Letter O With Tilde -> o - // (char)0x00f6, (char)0x006f, // Latin Small Letter O With Diaeresis -> o - // (char)0x00f7, (char)0x003f, // Division Sign - // (char)0x00f8, (char)0x006f, // Latin Small Letter O With Stroke -> o - // (char)0x00f9, (char)0x0075, // Latin Small Letter U With Grave -> u - // (char)0x00fa, (char)0x0075, // Latin Small Letter U With Acute -> u - // (char)0x00fb, (char)0x0075, // Latin Small Letter U With Circumflex -> u - // (char)0x00fc, (char)0x0075, // Latin Small Letter U With Diaeresis -> u - // (char)0x00fd, (char)0x0079, // Latin Small Letter Y With Acute -> y - // (char)0x00fe, (char)0x003f, // Latin Small Letter Thorn - // (char)0x00ff, (char)0x0079, // Latin Small Letter Y With Diaeresis -> y +// The first many are in case you wanted to use this for ASCIIEncoding, which we don't need to do any more. +// (char)0x00a0, (char)0x0020, // No-Break Space -> Space +// (char)0x00a1, (char)0x0021, // Inverted Exclamation Mark -> ! +// (char)0x00a2, (char)0x0063, // Cent Sign -> c +// (char)0x00a3, (char)0x003f, // Pound Sign +// (char)0x00a4, (char)0x0024, // Currency Sign -> $ +// (char)0x00a5, (char)0x0059, // Yen Sign -> Y +// (char)0x00a6, (char)0x007c, // Broken Bar -> | +// (char)0x00a7, (char)0x003f, // Section Sign +// (char)0x00a8, (char)0x003f, // Diaeresis +// (char)0x00a9, (char)0x0043, // Copyright Sign -> C +// (char)0x00aa, (char)0x0061, // Feminine Ordinal Indicator -> a +// (char)0x00ab, (char)0x003c, // Left-Pointing Double Angle Quotation Mark -> < +// (char)0x00ac, (char)0x003f, // Not Sign +// (char)0x00ad, (char)0x002d, // Soft Hyphen -> - +// (char)0x00ae, (char)0x0052, // Registered Sign -> R +// (char)0x00af, (char)0x003f, // Macron +// (char)0x00b0, (char)0x003f, // Degree Sign +// (char)0x00b1, (char)0x003f, // Plus-Minus Sign +// (char)0x00b2, (char)0x0032, // Superscript Two -> 2 +// (char)0x00b3, (char)0x0033, // Superscript Three -> 3 +// (char)0x00b4, (char)0x003f, // Acute Accent +// (char)0x00b5, (char)0x003f, // Micro Sign +// (char)0x00b6, (char)0x003f, // Pilcrow Sign +// (char)0x00b7, (char)0x002e, // Middle Dot -> . +// (char)0x00b8, (char)0x002c, // Cedilla -> , +// (char)0x00b9, (char)0x0031, // Superscript One -> 1 +// (char)0x00ba, (char)0x006f, // Masculine Ordinal Indicator -> o +// (char)0x00bb, (char)0x003e, // Right-Pointing Double Angle Quotation Mark -> > +// (char)0x00bc, (char)0x003f, // Vulgar Fraction One Quarter +// (char)0x00bd, (char)0x003f, // Vulgar Fraction One Half +// (char)0x00be, (char)0x003f, // Vulgar Fraction Three Quarters +// (char)0x00bf, (char)0x003f, // Inverted Question Mark +// (char)0x00c0, (char)0x0041, // Latin Capital Letter A With Grave -> A +// (char)0x00c1, (char)0x0041, // Latin Capital Letter A With Acute -> A +// (char)0x00c2, (char)0x0041, // Latin Capital Letter A With Circumflex -> A +// (char)0x00c3, (char)0x0041, // Latin Capital Letter A With Tilde -> A +// (char)0x00c4, (char)0x0041, // Latin Capital Letter A With Diaeresis -> A +// (char)0x00c5, (char)0x0041, // Latin Capital Letter A With Ring Above -> A +// (char)0x00c6, (char)0x0041, // Latin Capital Ligature Ae -> A +// (char)0x00c7, (char)0x0043, // Latin Capital Letter C With Cedilla -> C +// (char)0x00c8, (char)0x0045, // Latin Capital Letter E With Grave -> E +// (char)0x00c9, (char)0x0045, // Latin Capital Letter E With Acute -> E +// (char)0x00ca, (char)0x0045, // Latin Capital Letter E With Circumflex -> E +// (char)0x00cb, (char)0x0045, // Latin Capital Letter E With Diaeresis -> E +// (char)0x00cc, (char)0x0049, // Latin Capital Letter I With Grave -> I +// (char)0x00cd, (char)0x0049, // Latin Capital Letter I With Acute -> I +// (char)0x00ce, (char)0x0049, // Latin Capital Letter I With Circumflex -> I +// (char)0x00cf, (char)0x0049, // Latin Capital Letter I With Diaeresis -> I +// (char)0x00d0, (char)0x0044, // Latin Capital Letter Eth -> D +// (char)0x00d1, (char)0x004e, // Latin Capital Letter N With Tilde -> N +// (char)0x00d2, (char)0x004f, // Latin Capital Letter O With Grave -> O +// (char)0x00d3, (char)0x004f, // Latin Capital Letter O With Acute -> O +// (char)0x00d4, (char)0x004f, // Latin Capital Letter O With Circumflex -> O +// (char)0x00d5, (char)0x004f, // Latin Capital Letter O With Tilde -> O +// (char)0x00d6, (char)0x004f, // Latin Capital Letter O With Diaeresis -> O +// (char)0x00d7, (char)0x003f, // Multiplication Sign +// (char)0x00d8, (char)0x004f, // Latin Capital Letter O With Stroke -> O +// (char)0x00d9, (char)0x0055, // Latin Capital Letter U With Grave -> U +// (char)0x00da, (char)0x0055, // Latin Capital Letter U With Acute -> U +// (char)0x00db, (char)0x0055, // Latin Capital Letter U With Circumflex -> U +// (char)0x00dc, (char)0x0055, // Latin Capital Letter U With Diaeresis -> U +// (char)0x00dd, (char)0x0059, // Latin Capital Letter Y With Acute -> Y +// (char)0x00de, (char)0x003f, // Latin Capital Letter Thorn +// (char)0x00df, (char)0x003f, // Latin Small Letter Sharp S +// (char)0x00e0, (char)0x0061, // Latin Small Letter A With Grave -> a +// (char)0x00e1, (char)0x0061, // Latin Small Letter A With Acute -> a +// (char)0x00e2, (char)0x0061, // Latin Small Letter A With Circumflex -> a +// (char)0x00e3, (char)0x0061, // Latin Small Letter A With Tilde -> a +// (char)0x00e4, (char)0x0061, // Latin Small Letter A With Diaeresis -> a +// (char)0x00e5, (char)0x0061, // Latin Small Letter A With Ring Above -> a +// (char)0x00e6, (char)0x0061, // Latin Small Ligature Ae -> a +// (char)0x00e7, (char)0x0063, // Latin Small Letter C With Cedilla -> c +// (char)0x00e8, (char)0x0065, // Latin Small Letter E With Grave -> e +// (char)0x00e9, (char)0x0065, // Latin Small Letter E With Acute -> e +// (char)0x00ea, (char)0x0065, // Latin Small Letter E With Circumflex -> e +// (char)0x00eb, (char)0x0065, // Latin Small Letter E With Diaeresis -> e +// (char)0x00ec, (char)0x0069, // Latin Small Letter I With Grave -> i +// (char)0x00ed, (char)0x0069, // Latin Small Letter I With Acute -> i +// (char)0x00ee, (char)0x0069, // Latin Small Letter I With Circumflex -> i +// (char)0x00ef, (char)0x0069, // Latin Small Letter I With Diaeresis -> i +// (char)0x00f0, (char)0x003f, // Latin Small Letter Eth +// (char)0x00f1, (char)0x006e, // Latin Small Letter N With Tilde -> n +// (char)0x00f2, (char)0x006f, // Latin Small Letter O With Grave -> o +// (char)0x00f3, (char)0x006f, // Latin Small Letter O With Acute -> o +// (char)0x00f4, (char)0x006f, // Latin Small Letter O With Circumflex -> o +// (char)0x00f5, (char)0x006f, // Latin Small Letter O With Tilde -> o +// (char)0x00f6, (char)0x006f, // Latin Small Letter O With Diaeresis -> o +// (char)0x00f7, (char)0x003f, // Division Sign +// (char)0x00f8, (char)0x006f, // Latin Small Letter O With Stroke -> o +// (char)0x00f9, (char)0x0075, // Latin Small Letter U With Grave -> u +// (char)0x00fa, (char)0x0075, // Latin Small Letter U With Acute -> u +// (char)0x00fb, (char)0x0075, // Latin Small Letter U With Circumflex -> u +// (char)0x00fc, (char)0x0075, // Latin Small Letter U With Diaeresis -> u +// (char)0x00fd, (char)0x0079, // Latin Small Letter Y With Acute -> y +// (char)0x00fe, (char)0x003f, // Latin Small Letter Thorn +// (char)0x00ff, (char)0x0079, // Latin Small Letter Y With Diaeresis -> y (char)0x0100, (char)0x0041, // Latin Capital Letter A With Macron -> A (char)0x0101, (char)0x0061, // Latin Small Letter A With Macron -> a (char)0x0102, (char)0x0041, // Latin Capital Letter A With Breve -> A diff --git a/external/corefx/src/Common/src/CoreLib/System/Text/NormalizationForm.cs b/external/corefx/src/Common/src/CoreLib/System/Text/NormalizationForm.cs new file mode 100644 index 0000000000..976756251a --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Text/NormalizationForm.cs @@ -0,0 +1,14 @@ +// 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. + +namespace System.Text +{ + public enum NormalizationForm + { + FormC = 1, + FormD = 2, + FormKC = 5, + FormKD = 6 + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Text/StringBuilder.cs b/external/corefx/src/Common/src/CoreLib/System/Text/StringBuilder.cs new file mode 100644 index 0000000000..19c15498af --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Text/StringBuilder.cs @@ -0,0 +1,2438 @@ +// 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.Text; +using System.Runtime; +using System.Runtime.Serialization; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Security; +using System.Threading; +using System.Globalization; +using System.Diagnostics; +using System.Collections.Generic; + +namespace System.Text +{ + // This class represents a mutable string. It is convenient for situations in + // which it is desirable to modify a string, perhaps by removing, replacing, or + // inserting characters, without creating a new String subsequent to + // each modification. + // + // The methods contained within this class do not return a new StringBuilder + // object unless specified otherwise. This class may be used in conjunction with the String + // class to carry out modifications upon strings. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed partial class StringBuilder : ISerializable + { + // A StringBuilder is internally represented as a linked list of blocks each of which holds + // a chunk of the string. It turns out string as a whole can also be represented as just a chunk, + // so that is what we do. + + /// + /// The character buffer for this chunk. + /// + internal char[] m_ChunkChars; + + /// + /// The chunk that logically precedes this chunk. + /// + internal StringBuilder m_ChunkPrevious; + + /// + /// The number of characters in this chunk. + /// This is the number of elements in that are in use, from the start of the buffer. + /// + internal int m_ChunkLength; + + /// + /// The logical offset of this chunk's characters in the string it is a part of. + /// This is the sum of the number of characters in preceding blocks. + /// + internal int m_ChunkOffset; + + /// + /// The maximum capacity this builder is allowed to have. + /// + internal int m_MaxCapacity; + + /// + /// The default capacity of a . + /// + internal const int DefaultCapacity = 16; + + private const string CapacityField = "Capacity"; // Do not rename (binary serialization) + private const string MaxCapacityField = "m_MaxCapacity"; // Do not rename (binary serialization) + private const string StringValueField = "m_StringValue"; // Do not rename (binary serialization) + private const string ThreadIDField = "m_currentThread"; // Do not rename (binary serialization) + + // We want to keep chunk arrays out of large object heap (< 85K bytes ~ 40K chars) to be sure. + // Making the maximum chunk size big means less allocation code called, but also more waste + // in unused characters and slower inserts / replaces (since you do need to slide characters over + // within a buffer). + internal const int MaxChunkSize = 8000; + + /// + /// Initializes a new instance of the class. + /// + public StringBuilder() + { + m_MaxCapacity = int.MaxValue; + m_ChunkChars = new char[DefaultCapacity]; + } + + /// + /// Initializes a new instance of the class. + /// + /// The initial capacity of this builder. + public StringBuilder(int capacity) + : this(capacity, int.MaxValue) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The initial contents of this builder. + public StringBuilder(string value) + : this(value, DefaultCapacity) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The initial contents of this builder. + /// The initial capacity of this builder. + public StringBuilder(string value, int capacity) + : this(value, 0, value?.Length ?? 0, capacity) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The initial contents of this builder. + /// The index to start in . + /// The number of characters to read in . + /// The initial capacity of this builder. + public StringBuilder(string value, int startIndex, int length, int capacity) + { + if (capacity < 0) + { + throw new ArgumentOutOfRangeException(nameof(capacity), SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(capacity))); + } + if (length < 0) + { + throw new ArgumentOutOfRangeException(nameof(length), SR.Format(SR.ArgumentOutOfRange_MustBeNonNegNum, nameof(length))); + } + if (startIndex < 0) + { + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex); + } + + if (value == null) + { + value = string.Empty; + } + if (startIndex > value.Length - length) + { + throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_IndexLength); + } + + m_MaxCapacity = int.MaxValue; + if (capacity == 0) + { + capacity = DefaultCapacity; + } + capacity = Math.Max(capacity, length); + + m_ChunkChars = new char[capacity]; + m_ChunkLength = length; + + unsafe + { + fixed (char* sourcePtr = value) + { + ThreadSafeCopy(sourcePtr + startIndex, m_ChunkChars, 0, length); + } + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The initial capacity of this builder. + /// The maximum capacity of this builder. + public StringBuilder(int capacity, int maxCapacity) + { + if (capacity > maxCapacity) + { + throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_Capacity); + } + if (maxCapacity < 1) + { + throw new ArgumentOutOfRangeException(nameof(maxCapacity), SR.ArgumentOutOfRange_SmallMaxCapacity); + } + if (capacity < 0) + { + throw new ArgumentOutOfRangeException(nameof(capacity), SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(capacity))); + } + + if (capacity == 0) + { + capacity = Math.Min(DefaultCapacity, maxCapacity); + } + + m_MaxCapacity = maxCapacity; + m_ChunkChars = new char[capacity]; + } + + private StringBuilder(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + int persistedCapacity = 0; + string persistedString = null; + int persistedMaxCapacity = Int32.MaxValue; + bool capacityPresent = false; + + // Get the data + SerializationInfoEnumerator enumerator = info.GetEnumerator(); + while (enumerator.MoveNext()) + { + switch (enumerator.Name) + { + case MaxCapacityField: + persistedMaxCapacity = info.GetInt32(MaxCapacityField); + break; + case StringValueField: + persistedString = info.GetString(StringValueField); + break; + case CapacityField: + persistedCapacity = info.GetInt32(CapacityField); + capacityPresent = true; + break; + default: + // Ignore other fields for forwards-compatibility. + break; + } + } + + // Check values and set defaults + if (persistedString == null) + { + persistedString = string.Empty; + } + if (persistedMaxCapacity < 1 || persistedString.Length > persistedMaxCapacity) + { + throw new SerializationException(SR.Serialization_StringBuilderMaxCapacity); + } + + if (!capacityPresent) + { + // StringBuilder in V1.X did not persist the Capacity, so this is a valid legacy code path. + persistedCapacity = Math.Min(Math.Max(DefaultCapacity, persistedString.Length), persistedMaxCapacity); + } + + if (persistedCapacity < 0 || persistedCapacity < persistedString.Length || persistedCapacity > persistedMaxCapacity) + { + throw new SerializationException(SR.Serialization_StringBuilderCapacity); + } + + // Assign + m_MaxCapacity = persistedMaxCapacity; + m_ChunkChars = new char[persistedCapacity]; + persistedString.CopyTo(0, m_ChunkChars, 0, persistedString.Length); + m_ChunkLength = persistedString.Length; + m_ChunkPrevious = null; + AssertInvariants(); + } + + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + AssertInvariants(); + info.AddValue(MaxCapacityField, m_MaxCapacity); + info.AddValue(CapacityField, Capacity); + info.AddValue(StringValueField, ToString()); + // Note: persist "m_currentThread" to be compatible with old versions + info.AddValue(ThreadIDField, 0); + } + + [System.Diagnostics.Conditional("DEBUG")] + private void AssertInvariants() + { + Debug.Assert(m_ChunkOffset + m_ChunkChars.Length >= m_ChunkOffset, "The length of the string is greater than int.MaxValue."); + + StringBuilder currentBlock = this; + int maxCapacity = this.m_MaxCapacity; + for (;;) + { + // All blocks have the same max capacity. + Debug.Assert(currentBlock.m_MaxCapacity == maxCapacity); + Debug.Assert(currentBlock.m_ChunkChars != null); + + Debug.Assert(currentBlock.m_ChunkLength <= currentBlock.m_ChunkChars.Length); + Debug.Assert(currentBlock.m_ChunkLength >= 0); + Debug.Assert(currentBlock.m_ChunkOffset >= 0); + + StringBuilder prevBlock = currentBlock.m_ChunkPrevious; + if (prevBlock == null) + { + Debug.Assert(currentBlock.m_ChunkOffset == 0); + break; + } + // There are no gaps in the blocks. + Debug.Assert(currentBlock.m_ChunkOffset == prevBlock.m_ChunkOffset + prevBlock.m_ChunkLength); + currentBlock = prevBlock; + } + } + + public int Capacity + { + get { return m_ChunkChars.Length + m_ChunkOffset; } + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NegativeCapacity); + } + if (value > MaxCapacity) + { + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_Capacity); + } + if (value < Length) + { + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_SmallCapacity); + } + + if (Capacity != value) + { + int newLen = value - m_ChunkOffset; + char[] newArray = new char[newLen]; + Array.Copy(m_ChunkChars, 0, newArray, 0, m_ChunkLength); + m_ChunkChars = newArray; + } + } + } + + /// + /// Gets the maximum capacity this builder is allowed to have. + /// + public int MaxCapacity => m_MaxCapacity; + + /// + /// Ensures that the capacity of this builder is at least the specified value. + /// + /// The new capacity for this builder. + /// + /// If is less than or equal to the current capacity of + /// this builder, the capacity remains unchanged. + /// + public int EnsureCapacity(int capacity) + { + if (capacity < 0) + { + throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_NegativeCapacity); + } + + if (Capacity < capacity) + Capacity = capacity; + return Capacity; + } + + public override String ToString() + { + AssertInvariants(); + + if (Length == 0) + { + return string.Empty; + } + + string result = string.FastAllocateString(Length); + StringBuilder chunk = this; + unsafe + { + fixed (char* destinationPtr = result) + { + do + { + if (chunk.m_ChunkLength > 0) + { + // Copy these into local variables so that they are stable even in the presence of race conditions + char[] sourceArray = chunk.m_ChunkChars; + int chunkOffset = chunk.m_ChunkOffset; + int chunkLength = chunk.m_ChunkLength; + + // Check that we will not overrun our boundaries. + if ((uint)(chunkLength + chunkOffset) <= (uint)result.Length && (uint)chunkLength <= (uint)sourceArray.Length) + { + fixed (char* sourcePtr = &sourceArray[0]) + string.wstrcpy(destinationPtr + chunkOffset, sourcePtr, chunkLength); + } + else + { + throw new ArgumentOutOfRangeException(nameof(chunkLength), SR.ArgumentOutOfRange_Index); + } + } + chunk = chunk.m_ChunkPrevious; + } + while (chunk != null); + + return result; + } + } + } + + /// + /// Creates a string from a substring of this builder. + /// + /// The index to start in this builder. + /// The number of characters to read in this builder. + public string ToString(int startIndex, int length) + { + int currentLength = this.Length; + if (startIndex < 0) + { + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex); + } + if (startIndex > currentLength) + { + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndexLargerThanLength); + } + if (length < 0) + { + throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NegativeLength); + } + if (startIndex > currentLength - length) + { + throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_IndexLength); + } + + AssertInvariants(); + string result = string.FastAllocateString(length); + unsafe + { + fixed (char* destinationPtr = result) + { + this.CopyTo(startIndex, new Span(destinationPtr, length), length); + return result; + } + } + } + + public StringBuilder Clear() + { + this.Length = 0; + return this; + } + + /// + /// Gets or sets the length of this builder. + /// + public int Length + { + get + { + return m_ChunkOffset + m_ChunkLength; + } + set + { + //If the new length is less than 0 or greater than our Maximum capacity, bail. + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NegativeLength); + } + + if (value > MaxCapacity) + { + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_SmallCapacity); + } + + int originalCapacity = Capacity; + + if (value == 0 && m_ChunkPrevious == null) + { + m_ChunkLength = 0; + m_ChunkOffset = 0; + Debug.Assert(Capacity >= originalCapacity); + return; + } + + int delta = value - Length; + if (delta > 0) + { + // Pad ourselves with null characters. + Append('\0', delta); + } + else + { + StringBuilder chunk = FindChunkForIndex(value); + if (chunk != this) + { + // We crossed a chunk boundary when reducing the Length. We must replace this middle-chunk with a new larger chunk, + // to ensure the original capacity is preserved. + int newLen = originalCapacity - chunk.m_ChunkOffset; + char[] newArray = new char[newLen]; + + Debug.Assert(newLen > chunk.m_ChunkChars.Length, "The new chunk should be larger than the one it is replacing."); + Array.Copy(chunk.m_ChunkChars, 0, newArray, 0, chunk.m_ChunkLength); + + m_ChunkChars = newArray; + m_ChunkPrevious = chunk.m_ChunkPrevious; + m_ChunkOffset = chunk.m_ChunkOffset; + } + m_ChunkLength = value - chunk.m_ChunkOffset; + AssertInvariants(); + } + Debug.Assert(Capacity >= originalCapacity); + } + } + + [IndexerName("Chars")] + public char this[int index] + { + get + { + StringBuilder chunk = this; + for (;;) + { + int indexInBlock = index - chunk.m_ChunkOffset; + if (indexInBlock >= 0) + { + if (indexInBlock >= chunk.m_ChunkLength) + { + throw new IndexOutOfRangeException(); + } + return chunk.m_ChunkChars[indexInBlock]; + } + chunk = chunk.m_ChunkPrevious; + if (chunk == null) + { + throw new IndexOutOfRangeException(); + } + } + } + set + { + StringBuilder chunk = this; + for (;;) + { + int indexInBlock = index - chunk.m_ChunkOffset; + if (indexInBlock >= 0) + { + if (indexInBlock >= chunk.m_ChunkLength) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + } + chunk.m_ChunkChars[indexInBlock] = value; + return; + } + chunk = chunk.m_ChunkPrevious; + if (chunk == null) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + } + } + } + } + + /// + /// Appends a character 0 or more times to the end of this builder. + /// + /// The character to append. + /// The number of times to append . + public StringBuilder Append(char value, int repeatCount) + { + if (repeatCount < 0) + { + throw new ArgumentOutOfRangeException(nameof(repeatCount), SR.ArgumentOutOfRange_NegativeCount); + } + + if (repeatCount == 0) + { + return this; + } + + // this is where we can check if the repeatCount will put us over m_MaxCapacity + // We are doing the check here to prevent the corruption of the StringBuilder. + int newLength = Length + repeatCount; + if (newLength > m_MaxCapacity || newLength < repeatCount) + { + throw new ArgumentOutOfRangeException(nameof(repeatCount), SR.ArgumentOutOfRange_LengthGreaterThanCapacity); + } + + int index = m_ChunkLength; + while (repeatCount > 0) + { + if (index < m_ChunkChars.Length) + { + m_ChunkChars[index++] = value; + --repeatCount; + } + else + { + m_ChunkLength = index; + ExpandByABlock(repeatCount); + Debug.Assert(m_ChunkLength == 0); + index = 0; + } + } + + m_ChunkLength = index; + AssertInvariants(); + return this; + } + + /// + /// Appends a range of characters to the end of this builder. + /// + /// The characters to append. + /// The index to start in . + /// The number of characters to read in . + public StringBuilder Append(char[] value, int startIndex, int charCount) + { + if (startIndex < 0) + { + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_GenericPositive); + } + if (charCount < 0) + { + throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_GenericPositive); + } + + if (value == null) + { + if (startIndex == 0 && charCount == 0) + { + return this; + } + + throw new ArgumentNullException(nameof(value)); + } + if (charCount > value.Length - startIndex) + { + throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_Index); + } + + if (charCount == 0) + { + return this; + } + + unsafe + { + fixed (char* valueChars = &value[startIndex]) + { + Append(valueChars, charCount); + return this; + } + } + } + + + /// + /// Appends a string to the end of this builder. + /// + /// The string to append. + public StringBuilder Append(String value) + { + if (value != null) + { + // We could have just called AppendHelper here; this is a hand-specialization of that code. + char[] chunkChars = m_ChunkChars; + int chunkLength = m_ChunkLength; + int valueLen = value.Length; + int newCurrentIndex = chunkLength + valueLen; + + if (newCurrentIndex < chunkChars.Length) // Use strictly < to avoid issues if count == 0, newIndex == length + { + if (valueLen <= 2) + { + if (valueLen > 0) + chunkChars[chunkLength] = value[0]; + if (valueLen > 1) + chunkChars[chunkLength + 1] = value[1]; + } + else + { + unsafe + { + fixed (char* valuePtr = value) + fixed (char* destPtr = &chunkChars[chunkLength]) + { + string.wstrcpy(destPtr, valuePtr, valueLen); + } + } + } + + m_ChunkLength = newCurrentIndex; + } + else + { + AppendHelper(value); + } + } + + return this; + } + + // We put this fixed in its own helper to avoid the cost of zero-initing `valueChars` in the + // case we don't actually use it. + private void AppendHelper(string value) + { + unsafe + { + fixed (char* valueChars = value) + { + Append(valueChars, value.Length); + } + } + } + + /// + /// Appends part of a string to the end of this builder. + /// + /// The string to append. + /// The index to start in . + /// The number of characters to read in . + public StringBuilder Append(string value, int startIndex, int count) + { + if (startIndex < 0) + { + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); + } + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_GenericPositive); + } + + if (value == null) + { + if (startIndex == 0 && count == 0) + { + return this; + } + throw new ArgumentNullException(nameof(value)); + } + + if (count == 0) + { + return this; + } + + if (startIndex > value.Length - count) + { + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); + } + + unsafe + { + fixed (char* valueChars = value) + { + Append(valueChars + startIndex, count); + return this; + } + } + } + + public StringBuilder Append(StringBuilder value) + { + if (value != null && value.Length != 0) + { + return AppendCore(value, 0, value.Length); + } + return this; + } + + public StringBuilder Append(StringBuilder value, int startIndex, int count) + { + if (startIndex < 0) + { + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_GenericPositive); + } + + if (value == null) + { + if (startIndex == 0 && count == 0) + { + return this; + } + throw new ArgumentNullException(nameof(value)); + } + + if (count == 0) + { + return this; + } + + if (count > value.Length - startIndex) + { + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); + } + + return AppendCore(value, startIndex, count); + } + + private StringBuilder AppendCore(StringBuilder value, int startIndex, int count) + { + if (value == this) + return Append(value.ToString(startIndex, count)); + + int newLength = Length + count; + + if ((uint)newLength > (uint)m_MaxCapacity) + { + throw new ArgumentOutOfRangeException(nameof(Capacity), SR.ArgumentOutOfRange_Capacity); + } + + while (count > 0) + { + int length = Math.Min(m_ChunkChars.Length - m_ChunkLength, count); + if (length == 0) + { + ExpandByABlock(count); + length = Math.Min(m_ChunkChars.Length - m_ChunkLength, count); + } + value.CopyTo(startIndex, new Span(m_ChunkChars, m_ChunkLength, length), length); + + m_ChunkLength += length; + startIndex += length; + count -= length; + } + + return this; + } + + public StringBuilder AppendLine() => Append(Environment.NewLine); + + public StringBuilder AppendLine(string value) + { + Append(value); + return Append(Environment.NewLine); + } + + public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count) + { + if (destination == null) + { + throw new ArgumentNullException(nameof(destination)); + } + + if (destinationIndex < 0) + { + throw new ArgumentOutOfRangeException(nameof(destinationIndex), SR.Format(SR.ArgumentOutOfRange_MustBeNonNegNum, nameof(destinationIndex))); + } + + if (destinationIndex > destination.Length - count) + { + throw new ArgumentException(SR.ArgumentOutOfRange_OffsetOut); + } + + CopyTo(sourceIndex, new Span(destination).Slice(destinationIndex), count); + } + + public void CopyTo(int sourceIndex, Span destination, int count) + { + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.Arg_NegativeArgCount); + } + + if ((uint)sourceIndex > (uint)Length) + { + throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_Index); + } + + if (sourceIndex > Length - count) + { + throw new ArgumentException(SR.Arg_LongerThanSrcString); + } + + AssertInvariants(); + + StringBuilder chunk = this; + int sourceEndIndex = sourceIndex + count; + int curDestIndex = count; + while (count > 0) + { + int chunkEndIndex = sourceEndIndex - chunk.m_ChunkOffset; + if (chunkEndIndex >= 0) + { + chunkEndIndex = Math.Min(chunkEndIndex, chunk.m_ChunkLength); + + int chunkCount = count; + int chunkStartIndex = chunkEndIndex - count; + if (chunkStartIndex < 0) + { + chunkCount += chunkStartIndex; + chunkStartIndex = 0; + } + curDestIndex -= chunkCount; + count -= chunkCount; + + // We ensure that chunkStartIndex + chunkCount are within range of m_chunkChars as well as + // ensuring that curDestIndex + chunkCount are within range of destination + ThreadSafeCopy(chunk.m_ChunkChars, chunkStartIndex, destination, curDestIndex, chunkCount); + } + chunk = chunk.m_ChunkPrevious; + } + } + + /// + /// Inserts a string 0 or more times into this builder at the specified position. + /// + /// The index to insert in this builder. + /// The string to insert. + /// The number of times to insert the string. + public StringBuilder Insert(int index, String value, int count) + { + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + } + + int currentLength = Length; + if ((uint)index > (uint)currentLength) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + } + + if (string.IsNullOrEmpty(value) || count == 0) + { + return this; + } + + // Ensure we don't insert more chars than we can hold, and we don't + // have any integer overflow in our new length. + long insertingChars = (long)value.Length * count; + if (insertingChars > MaxCapacity - this.Length) + { + throw new OutOfMemoryException(); + } + Debug.Assert(insertingChars + this.Length < int.MaxValue); + + StringBuilder chunk; + int indexInChunk; + MakeRoom(index, (int)insertingChars, out chunk, out indexInChunk, false); + unsafe + { + fixed (char* valuePtr = value) + { + while (count > 0) + { + ReplaceInPlaceAtChunk(ref chunk, ref indexInChunk, valuePtr, value.Length); + --count; + } + + return this; + } + } + } + + /// + /// Removes a range of characters from this builder. + /// + /// + /// This method does not reduce the capacity of this builder. + /// + public StringBuilder Remove(int startIndex, int length) + { + if (length < 0) + { + throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NegativeLength); + } + + if (startIndex < 0) + { + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex); + } + + if (length > Length - startIndex) + { + throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_Index); + } + + if (Length == length && startIndex == 0) + { + Length = 0; + return this; + } + + if (length > 0) + { + StringBuilder chunk; + int indexInChunk; + Remove(startIndex, length, out chunk, out indexInChunk); + } + + return this; + } + + public StringBuilder Append(bool value) => Append(value.ToString()); + + public StringBuilder Append(char value) + { + if (m_ChunkLength < m_ChunkChars.Length) + { + m_ChunkChars[m_ChunkLength++] = value; + } + else + { + Append(value, 1); + } + + return this; + } + + [CLSCompliant(false)] + public StringBuilder Append(sbyte value) => AppendSpanFormattable(value); + + public StringBuilder Append(byte value) => AppendSpanFormattable(value); + + public StringBuilder Append(short value) => AppendSpanFormattable(value); + + public StringBuilder Append(int value) => AppendSpanFormattable(value); + + public StringBuilder Append(long value) => AppendSpanFormattable(value); + + public StringBuilder Append(float value) => AppendSpanFormattable(value); + + public StringBuilder Append(double value) => AppendSpanFormattable(value); + + public StringBuilder Append(decimal value) => AppendSpanFormattable(value); + + [CLSCompliant(false)] + public StringBuilder Append(ushort value) => AppendSpanFormattable(value); + + [CLSCompliant(false)] + public StringBuilder Append(uint value) => AppendSpanFormattable(value); + + [CLSCompliant(false)] + public StringBuilder Append(ulong value) => AppendSpanFormattable(value); + + private StringBuilder AppendSpanFormattable(T value) where T : ISpanFormattable + { + if (value.TryFormat(RemainingCurrentChunk, out int charsWritten, format: default, provider: null)) + { + m_ChunkLength += charsWritten; + return this; + } + + return Append(value.ToString()); + } + + public StringBuilder Append(object value) => (value == null) ? this : Append(value.ToString()); + + public StringBuilder Append(char[] value) + { + if (value?.Length > 0) + { + unsafe + { + fixed (char* valueChars = &value[0]) + { + Append(valueChars, value.Length); + } + } + } + return this; + } + + public StringBuilder Append(ReadOnlySpan value) + { + if (value.Length > 0) + { + unsafe + { + fixed (char* valueChars = &MemoryMarshal.GetReference(value)) + { + Append(valueChars, value.Length); + } + } + } + return this; + } + + #region AppendJoin + + public unsafe StringBuilder AppendJoin(string separator, params object[] values) + { + separator = separator ?? string.Empty; + fixed (char* pSeparator = separator) + { + return AppendJoinCore(pSeparator, separator.Length, values); + } + } + + public unsafe StringBuilder AppendJoin(string separator, IEnumerable values) + { + separator = separator ?? string.Empty; + fixed (char* pSeparator = separator) + { + return AppendJoinCore(pSeparator, separator.Length, values); + } + } + + public unsafe StringBuilder AppendJoin(string separator, params string[] values) + { + separator = separator ?? string.Empty; + fixed (char* pSeparator = separator) + { + return AppendJoinCore(pSeparator, separator.Length, values); + } + } + + public unsafe StringBuilder AppendJoin(char separator, params object[] values) + { + return AppendJoinCore(&separator, 1, values); + } + + public unsafe StringBuilder AppendJoin(char separator, IEnumerable values) + { + return AppendJoinCore(&separator, 1, values); + } + + public unsafe StringBuilder AppendJoin(char separator, params string[] values) + { + return AppendJoinCore(&separator, 1, values); + } + + private unsafe StringBuilder AppendJoinCore(char* separator, int separatorLength, IEnumerable values) + { + Debug.Assert(separator != null); + Debug.Assert(separatorLength >= 0); + + if (values == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); + } + + using (IEnumerator en = values.GetEnumerator()) + { + if (!en.MoveNext()) + { + return this; + } + + var value = en.Current; + if (value != null) + { + Append(value.ToString()); + } + + while (en.MoveNext()) + { + Append(separator, separatorLength); + value = en.Current; + if (value != null) + { + Append(value.ToString()); + } + } + } + return this; + } + + private unsafe StringBuilder AppendJoinCore(char* separator, int separatorLength, T[] values) + { + if (values == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); + } + + if (values.Length == 0) + { + return this; + } + + if (values[0] != null) + { + Append(values[0].ToString()); + } + + for (int i = 1; i < values.Length; i++) + { + Append(separator, separatorLength); + if (values[i] != null) + { + Append(values[i].ToString()); + } + } + return this; + } + + #endregion + + public StringBuilder Insert(int index, String value) + { + if ((uint)index > (uint)Length) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + } + + if (value != null) + { + unsafe + { + fixed (char* sourcePtr = value) + Insert(index, sourcePtr, value.Length); + } + } + return this; + } + + public StringBuilder Insert(int index, bool value) => Insert(index, value.ToString(), 1); + + [CLSCompliant(false)] + public StringBuilder Insert(int index, sbyte value) => Insert(index, value.ToString(), 1); + + public StringBuilder Insert(int index, byte value) => Insert(index, value.ToString(), 1); + + public StringBuilder Insert(int index, short value) => Insert(index, value.ToString(), 1); + + public StringBuilder Insert(int index, char value) + { + unsafe + { + Insert(index, &value, 1); + } + return this; + } + + public StringBuilder Insert(int index, char[] value) + { + if ((uint)index > (uint)Length) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + } + + if (value != null) + Insert(index, value, 0, value.Length); + return this; + } + + public StringBuilder Insert(int index, char[] value, int startIndex, int charCount) + { + int currentLength = Length; + if ((uint)index > (uint)currentLength) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + } + + if (value == null) + { + if (startIndex == 0 && charCount == 0) + { + return this; + } + throw new ArgumentNullException(nameof(value), SR.ArgumentNull_String); + } + + if (startIndex < 0) + { + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex); + } + + if (charCount < 0) + { + throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_GenericPositive); + } + + if (startIndex > value.Length - charCount) + { + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); + } + + if (charCount > 0) + { + unsafe + { + fixed (char* sourcePtr = &value[startIndex]) + Insert(index, sourcePtr, charCount); + } + } + return this; + } + + public StringBuilder Insert(int index, int value) => Insert(index, value.ToString(), 1); + + public StringBuilder Insert(int index, long value) => Insert(index, value.ToString(), 1); + + public StringBuilder Insert(int index, float value) => Insert(index, value.ToString(), 1); + + public StringBuilder Insert(int index, double value) => Insert(index, value.ToString(), 1); + + public StringBuilder Insert(int index, decimal value) => Insert(index, value.ToString(), 1); + + [CLSCompliant(false)] + public StringBuilder Insert(int index, ushort value) => Insert(index, value.ToString(), 1); + + [CLSCompliant(false)] + public StringBuilder Insert(int index, uint value) => Insert(index, value.ToString(), 1); + + [CLSCompliant(false)] + public StringBuilder Insert(int index, ulong value) => Insert(index, value.ToString(), 1); + + public StringBuilder Insert(int index, Object value) => (value == null) ? this : Insert(index, value.ToString(), 1); + + public StringBuilder Insert(int index, ReadOnlySpan value) + { + if ((uint)index > (uint)Length) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + } + + if (value.Length > 0) + { + unsafe + { + fixed (char* sourcePtr = &MemoryMarshal.GetReference(value)) + Insert(index, sourcePtr, value.Length); + } + } + return this; + } + + public StringBuilder AppendFormat(String format, Object arg0) => AppendFormatHelper(null, format, new ParamsArray(arg0)); + + public StringBuilder AppendFormat(String format, Object arg0, Object arg1) => AppendFormatHelper(null, format, new ParamsArray(arg0, arg1)); + + public StringBuilder AppendFormat(String format, Object arg0, Object arg1, Object arg2) => AppendFormatHelper(null, format, new ParamsArray(arg0, arg1, arg2)); + + public StringBuilder AppendFormat(String format, params Object[] args) + { + if (args == null) + { + // To preserve the original exception behavior, throw an exception about format if both + // args and format are null. The actual null check for format is in AppendFormatHelper. + string paramName = (format == null) ? nameof(format) : nameof(args); + throw new ArgumentNullException(paramName); + } + + return AppendFormatHelper(null, format, new ParamsArray(args)); + } + + public StringBuilder AppendFormat(IFormatProvider provider, String format, Object arg0) => AppendFormatHelper(provider, format, new ParamsArray(arg0)); + + public StringBuilder AppendFormat(IFormatProvider provider, String format, Object arg0, Object arg1) => AppendFormatHelper(provider, format, new ParamsArray(arg0, arg1)); + + public StringBuilder AppendFormat(IFormatProvider provider, String format, Object arg0, Object arg1, Object arg2) => AppendFormatHelper(provider, format, new ParamsArray(arg0, arg1, arg2)); + + public StringBuilder AppendFormat(IFormatProvider provider, String format, params Object[] args) + { + if (args == null) + { + // To preserve the original exception behavior, throw an exception about format if both + // args and format are null. The actual null check for format is in AppendFormatHelper. + string paramName = (format == null) ? nameof(format) : nameof(args); + throw new ArgumentNullException(paramName); + } + + return AppendFormatHelper(provider, format, new ParamsArray(args)); + } + + private static void FormatError() + { + throw new FormatException(SR.Format_InvalidString); + } + + // Undocumented exclusive limits on the range for Argument Hole Index and Argument Hole Alignment. + private const int IndexLimit = 1000000; // Note: 0 <= ArgIndex < IndexLimit + private const int WidthLimit = 1000000; // Note: -WidthLimit < ArgAlign < WidthLimit + + internal StringBuilder AppendFormatHelper(IFormatProvider provider, String format, ParamsArray args) + { + if (format == null) + { + throw new ArgumentNullException(nameof(format)); + } + + int pos = 0; + int len = format.Length; + char ch = '\x0'; + StringBuilder unescapedItemFormat = null; + + ICustomFormatter cf = null; + if (provider != null) + { + cf = (ICustomFormatter)provider.GetFormat(typeof(ICustomFormatter)); + } + + while (true) + { + while (pos < len) + { + ch = format[pos]; + + pos++; + // Is it a closing brace? + if (ch == '}') + { + // Check next character (if there is one) to see if it is escaped. eg }} + if (pos < len && format[pos] == '}') + pos++; + else + // Otherwise treat it as an error (Mismatched closing brace) + FormatError(); + } + // Is it a opening brace? + if (ch == '{') + { + // Check next character (if there is one) to see if it is escaped. eg {{ + if (pos < len && format[pos] == '{') + pos++; + else + { + // Otherwise treat it as the opening brace of an Argument Hole. + pos--; + break; + } + } + // If it neither then treat the character as just text. + Append(ch); + } + + // + // Start of parsing of Argument Hole. + // Argument Hole ::= { Index (, WS* Alignment WS*)? (: Formatting)? } + // + if (pos == len) break; + + // + // Start of parsing required Index parameter. + // Index ::= ('0'-'9')+ WS* + // + pos++; + // If reached end of text then error (Unexpected end of text) + // or character is not a digit then error (Unexpected Character) + if (pos == len || (ch = format[pos]) < '0' || ch > '9') FormatError(); + int index = 0; + do + { + index = index * 10 + ch - '0'; + pos++; + // If reached end of text then error (Unexpected end of text) + if (pos == len) FormatError(); + ch = format[pos]; + // so long as character is digit and value of the index is less than 1000000 ( index limit ) + } + while (ch >= '0' && ch <= '9' && index < IndexLimit); + + // If value of index is not within the range of the arguments passed in then error (Index out of range) + if (index >= args.Length) throw new FormatException(SR.Format_IndexOutOfRange); + + // Consume optional whitespace. + while (pos < len && (ch = format[pos]) == ' ') pos++; + // End of parsing index parameter. + + // + // Start of parsing of optional Alignment + // Alignment ::= comma WS* minus? ('0'-'9')+ WS* + // + bool leftJustify = false; + int width = 0; + // Is the character a comma, which indicates the start of alignment parameter. + if (ch == ',') + { + pos++; + + // Consume Optional whitespace + while (pos < len && format[pos] == ' ') pos++; + + // If reached the end of the text then error (Unexpected end of text) + if (pos == len) FormatError(); + + // Is there a minus sign? + ch = format[pos]; + if (ch == '-') + { + // Yes, then alignment is left justified. + leftJustify = true; + pos++; + // If reached end of text then error (Unexpected end of text) + if (pos == len) FormatError(); + ch = format[pos]; + } + + // If current character is not a digit then error (Unexpected character) + if (ch < '0' || ch > '9') FormatError(); + // Parse alignment digits. + do + { + width = width * 10 + ch - '0'; + pos++; + // If reached end of text then error. (Unexpected end of text) + if (pos == len) FormatError(); + ch = format[pos]; + // So long a current character is a digit and the value of width is less than 100000 ( width limit ) + } + while (ch >= '0' && ch <= '9' && width < WidthLimit); + // end of parsing Argument Alignment + } + + // Consume optional whitespace + while (pos < len && (ch = format[pos]) == ' ') pos++; + + // + // Start of parsing of optional formatting parameter. + // + Object arg = args[index]; + String itemFormat = null; + ReadOnlySpan itemFormatSpan = default; // used if itemFormat is null + // Is current character a colon? which indicates start of formatting parameter. + if (ch == ':') + { + pos++; + int startPos = pos; + + while (true) + { + // If reached end of text then error. (Unexpected end of text) + if (pos == len) FormatError(); + ch = format[pos]; + pos++; + + // Is character a opening or closing brace? + if (ch == '}' || ch == '{') + { + if (ch == '{') + { + // Yes, is next character also a opening brace, then treat as escaped. eg {{ + if (pos < len && format[pos] == '{') + pos++; + else + // Error Argument Holes can not be nested. + FormatError(); + } + else + { + // Yes, is next character also a closing brace, then treat as escaped. eg }} + if (pos < len && format[pos] == '}') + pos++; + else + { + // No, then treat it as the closing brace of an Arg Hole. + pos--; + break; + } + } + + // Reaching here means the brace has been escaped + // so we need to build up the format string in segments + if (unescapedItemFormat == null) + { + unescapedItemFormat = new StringBuilder(); + } + unescapedItemFormat.Append(format, startPos, pos - startPos - 1); + startPos = pos; + } + } + + if (unescapedItemFormat == null || unescapedItemFormat.Length == 0) + { + if (startPos != pos) + { + // There was no brace escaping, extract the item format as a single string + itemFormatSpan = format.AsReadOnlySpan().Slice(startPos, pos - startPos); + } + } + else + { + unescapedItemFormat.Append(format, startPos, pos - startPos); + itemFormatSpan = itemFormat = unescapedItemFormat.ToString(); + unescapedItemFormat.Clear(); + } + } + // If current character is not a closing brace then error. (Unexpected Character) + if (ch != '}') FormatError(); + // Construct the output for this arg hole. + pos++; + String s = null; + if (cf != null) + { + if (itemFormatSpan.Length != 0 && itemFormat == null) + { + itemFormat = new string(itemFormatSpan); + } + s = cf.Format(itemFormat, arg, provider); + } + + if (s == null) + { + // If arg is ISpanFormattable and the beginning doesn't need padding, + // try formatting it into the remaining current chunk. + if (arg is ISpanFormattable spanFormattableArg && + (leftJustify || width == 0) && + spanFormattableArg.TryFormat(RemainingCurrentChunk, out int charsWritten, itemFormatSpan, provider)) + { + m_ChunkLength += charsWritten; + + // Pad the end, if needed. + int padding = width - charsWritten; + if (leftJustify && padding > 0) Append(' ', padding); + + // Continue to parse other characters. + continue; + } + + // Otherwise, fallback to trying IFormattable or calling ToString. + if (arg is IFormattable formattableArg) + { + if (itemFormatSpan.Length != 0 && itemFormat == null) + { + itemFormat = new string(itemFormatSpan); + } + s = formattableArg.ToString(itemFormat, provider); + } + else if (arg != null) + { + s = arg.ToString(); + } + } + // Append it to the final output of the Format String. + if (s == null) s = String.Empty; + int pad = width - s.Length; + if (!leftJustify && pad > 0) Append(' ', pad); + Append(s); + if (leftJustify && pad > 0) Append(' ', pad); + // Continue to parse other characters. + } + return this; + } + + /// + /// Replaces all instances of one string with another in this builder. + /// + /// The string to replace. + /// The string to replace with. + /// + /// If is null, instances of + /// are removed from this builder. + /// + public StringBuilder Replace(String oldValue, String newValue) => Replace(oldValue, newValue, 0, Length); + + /// + /// Determines if the contents of this builder are equal to the contents of another builder. + /// + /// The other builder. + public bool Equals(StringBuilder sb) + { + if (sb == null) + return false; + if (Capacity != sb.Capacity || MaxCapacity != sb.MaxCapacity || Length != sb.Length) + return false; + if (sb == this) + return true; + + StringBuilder thisChunk = this; + int thisChunkIndex = thisChunk.m_ChunkLength; + StringBuilder sbChunk = sb; + int sbChunkIndex = sbChunk.m_ChunkLength; + for (;;) + { + --thisChunkIndex; + --sbChunkIndex; + + while (thisChunkIndex < 0) + { + thisChunk = thisChunk.m_ChunkPrevious; + if (thisChunk == null) + break; + thisChunkIndex = thisChunk.m_ChunkLength + thisChunkIndex; + } + + while (sbChunkIndex < 0) + { + sbChunk = sbChunk.m_ChunkPrevious; + if (sbChunk == null) + break; + sbChunkIndex = sbChunk.m_ChunkLength + sbChunkIndex; + } + + if (thisChunkIndex < 0) + return sbChunkIndex < 0; + if (sbChunkIndex < 0) + return false; + if (thisChunk.m_ChunkChars[thisChunkIndex] != sbChunk.m_ChunkChars[sbChunkIndex]) + return false; + } + } + + /// + /// Determines if the contents of this builder are equal to the contents of ReadOnlySpan. + /// + /// The ReadOnlySpan{char}. + public bool Equals(ReadOnlySpan value) + { + if (value.Length != Length) + return false; + + StringBuilder sbChunk = this; + int offset = 0; + + do + { + int chunk_length = sbChunk.m_ChunkLength; + offset += chunk_length; + + ReadOnlySpan chunk = new ReadOnlySpan(sbChunk.m_ChunkChars, 0, chunk_length); + + if (!chunk.Equals(value.Slice(value.Length - offset, chunk_length))) + return false; + + sbChunk = sbChunk.m_ChunkPrevious; + } while (sbChunk != null); + + Debug.Assert(offset == Length); + return true; + } + + /// + /// Replaces all instances of one string with another in part of this builder. + /// + /// The string to replace. + /// The string to replace with. + /// The index to start in this builder. + /// The number of characters to read in this builder. + /// + /// If is null, instances of + /// are removed from this builder. + /// + public StringBuilder Replace(String oldValue, String newValue, int startIndex, int count) + { + int currentLength = Length; + if ((uint)startIndex > (uint)currentLength) + { + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); + } + if (count < 0 || startIndex > currentLength - count) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Index); + } + if (oldValue == null) + { + throw new ArgumentNullException(nameof(oldValue)); + } + if (oldValue.Length == 0) + { + throw new ArgumentException(SR.Argument_EmptyName, nameof(oldValue)); + } + + newValue = newValue ?? string.Empty; + + int deltaLength = newValue.Length - oldValue.Length; + + int[] replacements = null; // A list of replacement positions in a chunk to apply + int replacementsCount = 0; + + // Find the chunk, indexInChunk for the starting point + StringBuilder chunk = FindChunkForIndex(startIndex); + int indexInChunk = startIndex - chunk.m_ChunkOffset; + while (count > 0) + { + // Look for a match in the chunk,indexInChunk pointer + if (StartsWith(chunk, indexInChunk, count, oldValue)) + { + // Push it on the replacements array (with growth), we will do all replacements in a + // given chunk in one operation below (see ReplaceAllInChunk) so we don't have to slide + // many times. + if (replacements == null) + { + replacements = new int[5]; + } + else if (replacementsCount >= replacements.Length) + { + Array.Resize(ref replacements, replacements.Length * 3 / 2 + 4); // Grow by ~1.5x, but more in the begining + } + replacements[replacementsCount++] = indexInChunk; + indexInChunk += oldValue.Length; + count -= oldValue.Length; + } + else + { + indexInChunk++; + --count; + } + + if (indexInChunk >= chunk.m_ChunkLength || count == 0) // Have we moved out of the current chunk? + { + // Replacing mutates the blocks, so we need to convert to a logical index and back afterwards. + int index = indexInChunk + chunk.m_ChunkOffset; + int indexBeforeAdjustment = index; + + // See if we accumulated any replacements, if so apply them. + ReplaceAllInChunk(replacements, replacementsCount, chunk, oldValue.Length, newValue); + // The replacement has affected the logical index. Adjust it. + index += ((newValue.Length - oldValue.Length) * replacementsCount); + replacementsCount = 0; + + chunk = FindChunkForIndex(index); + indexInChunk = index - chunk.m_ChunkOffset; + Debug.Assert(chunk != null || count == 0, "Chunks ended prematurely!"); + } + } + + AssertInvariants(); + return this; + } + + /// + /// Replaces all instances of one character with another in this builder. + /// + /// The character to replace. + /// The character to replace with. + public StringBuilder Replace(char oldChar, char newChar) + { + return Replace(oldChar, newChar, 0, Length); + } + + /// + /// Replaces all instances of one character with another in this builder. + /// + /// The character to replace. + /// The character to replace with. + /// The index to start in this builder. + /// The number of characters to read in this builder. + public StringBuilder Replace(char oldChar, char newChar, int startIndex, int count) + { + int currentLength = Length; + if ((uint)startIndex > (uint)currentLength) + { + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); + } + + if (count < 0 || startIndex > currentLength - count) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Index); + } + + int endIndex = startIndex + count; + StringBuilder chunk = this; + + for (;;) + { + int endIndexInChunk = endIndex - chunk.m_ChunkOffset; + int startIndexInChunk = startIndex - chunk.m_ChunkOffset; + if (endIndexInChunk >= 0) + { + int curInChunk = Math.Max(startIndexInChunk, 0); + int endInChunk = Math.Min(chunk.m_ChunkLength, endIndexInChunk); + while (curInChunk < endInChunk) + { + if (chunk.m_ChunkChars[curInChunk] == oldChar) + chunk.m_ChunkChars[curInChunk] = newChar; + curInChunk++; + } + } + if (startIndexInChunk >= 0) + break; + chunk = chunk.m_ChunkPrevious; + } + + AssertInvariants(); + return this; + } + + /// + /// Appends a character buffer to this builder. + /// + /// The pointer to the start of the buffer. + /// The number of characters in the buffer. + [CLSCompliant(false)] + public unsafe StringBuilder Append(char* value, int valueCount) + { + // We don't check null value as this case will throw null reference exception anyway + if (valueCount < 0) + { + throw new ArgumentOutOfRangeException(nameof(valueCount), SR.ArgumentOutOfRange_NegativeCount); + } + + // this is where we can check if the valueCount will put us over m_MaxCapacity + // We are doing the check here to prevent the corruption of the StringBuilder. + int newLength = Length + valueCount; + if (newLength > m_MaxCapacity || newLength < valueCount) + { + throw new ArgumentOutOfRangeException(nameof(valueCount), SR.ArgumentOutOfRange_LengthGreaterThanCapacity); + } + + // This case is so common we want to optimize for it heavily. + int newIndex = valueCount + m_ChunkLength; + if (newIndex <= m_ChunkChars.Length) + { + ThreadSafeCopy(value, m_ChunkChars, m_ChunkLength, valueCount); + m_ChunkLength = newIndex; + } + else + { + // Copy the first chunk + int firstLength = m_ChunkChars.Length - m_ChunkLength; + if (firstLength > 0) + { + ThreadSafeCopy(value, m_ChunkChars, m_ChunkLength, firstLength); + m_ChunkLength = m_ChunkChars.Length; + } + + // Expand the builder to add another chunk. + int restLength = valueCount - firstLength; + ExpandByABlock(restLength); + Debug.Assert(m_ChunkLength == 0, "A new block was not created."); + + // Copy the second chunk + ThreadSafeCopy(value + firstLength, m_ChunkChars, 0, restLength); + m_ChunkLength = restLength; + } + AssertInvariants(); + return this; + } + + /// + /// Inserts a character buffer into this builder at the specified position. + /// + /// The index to insert in this builder. + /// The pointer to the start of the buffer. + /// The number of characters in the buffer. + private unsafe void Insert(int index, char* value, int valueCount) + { + if ((uint)index > (uint)Length) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + } + + if (valueCount > 0) + { + StringBuilder chunk; + int indexInChunk; + MakeRoom(index, valueCount, out chunk, out indexInChunk, false); + ReplaceInPlaceAtChunk(ref chunk, ref indexInChunk, value, valueCount); + } + } + + /// + /// Replaces strings at specified indices with a new string in a chunk. + /// + /// The list of indices, relative to the beginning of the chunk, to remove at. + /// The number of replacements to make. + /// The source chunk. + /// The number of characters to remove at each replacement. + /// The string to insert at each replacement. + /// + /// This routine is very efficient because it does replacements in bulk. + /// + private void ReplaceAllInChunk(int[] replacements, int replacementsCount, StringBuilder sourceChunk, int removeCount, string value) + { + if (replacementsCount <= 0) + { + return; + } + + unsafe + { + fixed (char* valuePtr = value) + { + // calculate the total amount of extra space or space needed for all the replacements. + int delta = (value.Length - removeCount) * replacementsCount; + + StringBuilder targetChunk = sourceChunk; // the target as we copy chars down + int targetIndexInChunk = replacements[0]; + + // Make the room needed for all the new characters if needed. + if (delta > 0) + MakeRoom(targetChunk.m_ChunkOffset + targetIndexInChunk, delta, out targetChunk, out targetIndexInChunk, true); + // We made certain that characters after the insertion point are not moved, + int i = 0; + for (;;) + { + // Copy in the new string for the ith replacement + ReplaceInPlaceAtChunk(ref targetChunk, ref targetIndexInChunk, valuePtr, value.Length); + int gapStart = replacements[i] + removeCount; + i++; + if (i >= replacementsCount) + { + break; + } + + int gapEnd = replacements[i]; + Debug.Assert(gapStart < sourceChunk.m_ChunkChars.Length, "gap starts at end of buffer. Should not happen"); + Debug.Assert(gapStart <= gapEnd, "negative gap size"); + Debug.Assert(gapEnd <= sourceChunk.m_ChunkLength, "gap too big"); + if (delta != 0) // can skip the sliding of gaps if source an target string are the same size. + { + // Copy the gap data between the current replacement and the next replacement + fixed (char* sourcePtr = &sourceChunk.m_ChunkChars[gapStart]) + ReplaceInPlaceAtChunk(ref targetChunk, ref targetIndexInChunk, sourcePtr, gapEnd - gapStart); + } + else + { + targetIndexInChunk += gapEnd - gapStart; + Debug.Assert(targetIndexInChunk <= targetChunk.m_ChunkLength, "gap not in chunk"); + } + } + + // Remove extra space if necessary. + if (delta < 0) + Remove(targetChunk.m_ChunkOffset + targetIndexInChunk, -delta, out targetChunk, out targetIndexInChunk); + } + } + } + + /// + /// Returns a value indicating whether a substring of a builder starts with a specified prefix. + /// + /// The chunk in which the substring starts. + /// The index in at which the substring starts. + /// The logical count of the substring. + /// The prefix. + private bool StartsWith(StringBuilder chunk, int indexInChunk, int count, string value) + { + for (int i = 0; i < value.Length; i++) + { + if (count == 0) + { + return false; + } + + if (indexInChunk >= chunk.m_ChunkLength) + { + chunk = Next(chunk); + if (chunk == null) + return false; + indexInChunk = 0; + } + + if (value[i] != chunk.m_ChunkChars[indexInChunk]) + { + return false; + } + + indexInChunk++; + --count; + } + + return true; + } + + /// + /// Replaces characters at a specified location with the contents of a character buffer. + /// This function is the logical equivalent of memcpy. + /// + /// + /// The chunk in which to start replacing characters. + /// Receives the chunk in which character replacement ends. + /// + /// + /// The index in to start replacing characters at. + /// Receives the index at which character replacement ends. + /// + /// The pointer to the start of the character buffer. + /// The number of characters in the buffer. + unsafe private void ReplaceInPlaceAtChunk(ref StringBuilder chunk, ref int indexInChunk, char* value, int count) + { + if (count != 0) + { + for (;;) + { + int lengthInChunk = chunk.m_ChunkLength - indexInChunk; + Debug.Assert(lengthInChunk >= 0, "Index isn't in the chunk."); + + int lengthToCopy = Math.Min(lengthInChunk, count); + ThreadSafeCopy(value, chunk.m_ChunkChars, indexInChunk, lengthToCopy); + + // Advance the index. + indexInChunk += lengthToCopy; + if (indexInChunk >= chunk.m_ChunkLength) + { + chunk = Next(chunk); + indexInChunk = 0; + } + count -= lengthToCopy; + if (count == 0) + { + break; + } + value += lengthToCopy; + } + } + } + + /// + /// This method prevents out-of-bounds writes in the case a different thread updates a field in the builder just before a copy begins. + /// All interesting variables are copied out of the heap into the parameters of this method, and then bounds checks are run. + /// + private static unsafe void ThreadSafeCopy(char* sourcePtr, char[] destination, int destinationIndex, int count) + { + if (count > 0) + { + if ((uint)destinationIndex <= (uint)destination.Length && (destinationIndex + count) <= destination.Length) + { + fixed (char* destinationPtr = &destination[destinationIndex]) + string.wstrcpy(destinationPtr, sourcePtr, count); + } + else + { + throw new ArgumentOutOfRangeException(nameof(destinationIndex), SR.ArgumentOutOfRange_Index); + } + } + } + + private static unsafe void ThreadSafeCopy(char[] source, int sourceIndex, Span destination, int destinationIndex, int count) + { + if (count > 0) + { + if ((uint)sourceIndex > (uint)source.Length || count > source.Length - sourceIndex) + { + throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_Index); + } + + if ((uint)destinationIndex > (uint)destination.Length || count > destination.Length - destinationIndex) + { + throw new ArgumentOutOfRangeException(nameof(destinationIndex), SR.ArgumentOutOfRange_Index); + } + + fixed (char* sourcePtr = &source[sourceIndex]) + fixed (char* destinationPtr = &MemoryMarshal.GetReference(destination)) + string.wstrcpy(destinationPtr + destinationIndex, sourcePtr, count); + } + } + + /// + /// Gets the chunk corresponding to the logical index in this builder. + /// + /// The logical index in this builder. + /// + /// After calling this method, you can obtain the actual index within the chunk by + /// subtracting from . + /// + private StringBuilder FindChunkForIndex(int index) + { + Debug.Assert(0 <= index && index <= Length); + + StringBuilder result = this; + while (result.m_ChunkOffset > index) + { + result = result.m_ChunkPrevious; + } + + Debug.Assert(result != null); + return result; + } + + /// + /// Gets the chunk corresponding to the logical byte index in this builder. + /// + /// The logical byte index in this builder. + private StringBuilder FindChunkForByte(int byteIndex) + { + Debug.Assert(0 <= byteIndex && byteIndex <= Length * sizeof(char)); + + StringBuilder result = this; + while (result.m_ChunkOffset * sizeof(char) > byteIndex) + { + result = result.m_ChunkPrevious; + } + + Debug.Assert(result != null); + return result; + } + + /// Gets a span representing the remaining space available in the current chunk. + private Span RemainingCurrentChunk + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Span(m_ChunkChars, m_ChunkLength, m_ChunkChars.Length - m_ChunkLength); + } + + /// + /// Finds the chunk that logically succeeds the specified chunk. + /// + /// The chunk whose successor should be found. + /// + /// Each chunk only stores the pointer to its logical predecessor, so this routine has to start + /// from the 'this' pointer (which is assumed to represent the whole StringBuilder) and work its + /// way down until it finds the specified chunk (which is O(n)). Thus, it is more expensive than + /// a field fetch. + /// + private StringBuilder Next(StringBuilder chunk) => chunk == this ? null : FindChunkForIndex(chunk.m_ChunkOffset + chunk.m_ChunkLength); + + /// + /// Transfers the character buffer from this chunk to a new chunk, and allocates a new buffer with a minimum size for this chunk. + /// + /// The minimum size of the new buffer to be allocated for this chunk. + /// + /// This method requires that the current chunk is full. Otherwise, there's no point in shifting the characters over. + /// It also assumes that 'this' is the last chunk in the linked list. + /// + private void ExpandByABlock(int minBlockCharCount) + { + Debug.Assert(Capacity == Length, nameof(ExpandByABlock) + " should only be called when there is no space left."); + Debug.Assert(minBlockCharCount > 0); + + AssertInvariants(); + + if ((minBlockCharCount + Length) > m_MaxCapacity || minBlockCharCount + Length < minBlockCharCount) + { + throw new ArgumentOutOfRangeException("requiredLength", SR.ArgumentOutOfRange_SmallCapacity); + } + + // - We always need to make the new chunk at least as big as was requested (`minBlockCharCount`). + // - We'd also prefer to make it at least at big as the current length (thus doubling capacity). + // - But this is only up to a maximum, so we stay in the small object heap, and never allocate + // really big chunks even if the string gets really big. + int newBlockLength = Math.Max(minBlockCharCount, Math.Min(Length, MaxChunkSize)); + + // Move all of the data from this chunk to a new one, via a few O(1) pointer adjustments. + // Then, have this chunk point to the new one as its predecessor. + m_ChunkPrevious = new StringBuilder(this); + m_ChunkOffset += m_ChunkLength; + m_ChunkLength = 0; + + // Check for integer overflow (logical buffer size > int.MaxValue) + if (m_ChunkOffset + newBlockLength < newBlockLength) + { + m_ChunkChars = null; + throw new OutOfMemoryException(); + } + m_ChunkChars = new char[newBlockLength]; + + AssertInvariants(); + } + + /// + /// Creates a new chunk with fields copied from an existing chunk. + /// + /// The chunk from which to copy fields. + /// + /// + /// This method runs in O(1) time. It does not copy data within the character buffer + /// holds, but copies the reference to the character buffer itself + /// (plus a few other fields). + /// + /// + /// Callers are expected to update subsequently to point to this + /// chunk as its predecessor. + /// + /// + private StringBuilder(StringBuilder from) + { + m_ChunkLength = from.m_ChunkLength; + m_ChunkOffset = from.m_ChunkOffset; + m_ChunkChars = from.m_ChunkChars; + m_ChunkPrevious = from.m_ChunkPrevious; + m_MaxCapacity = from.m_MaxCapacity; + + AssertInvariants(); + } + + /// + /// Creates a gap at a logical index with the specified count. + /// + /// The logical index in this builder. + /// The number of characters in the gap. + /// Receives the chunk containing the gap. + /// The index in that points to the gap. + /// + /// - If true, then room must be made by inserting a chunk before the current chunk. + /// - If false, then room can be made by shifting characters ahead of + /// in this block forward by provided the characters will still fit in + /// the current chunk after being shifted. + /// - Providing false does not make a difference most of the time, but it can matter when someone + /// inserts lots of small strings at a position in the buffer. + /// + /// + /// + /// Since chunks do not contain references to their successors, it is not always possible for us to make room + /// by inserting space after in case this chunk runs out of space. Thus, we make room + /// by inserting space before the specified index, and having logical indices refer to new locations by the end + /// of this method. + /// + /// + /// can be used in conjunction with this method to fill in the newly created gap. + /// + /// + private void MakeRoom(int index, int count, out StringBuilder chunk, out int indexInChunk, bool doNotMoveFollowingChars) + { + AssertInvariants(); + Debug.Assert(count > 0); + Debug.Assert(index >= 0); + + if (count + Length > m_MaxCapacity || count + Length < count) + { + throw new ArgumentOutOfRangeException("requiredLength", SR.ArgumentOutOfRange_SmallCapacity); + } + + chunk = this; + while (chunk.m_ChunkOffset > index) + { + chunk.m_ChunkOffset += count; + chunk = chunk.m_ChunkPrevious; + } + indexInChunk = index - chunk.m_ChunkOffset; + + // Cool, we have some space in this block, and we don't have to copy much to get at it, so go ahead and use it. + // This typically happens when someone repeatedly inserts small strings at a spot (usually the absolute front) of the buffer. + if (!doNotMoveFollowingChars && chunk.m_ChunkLength <= DefaultCapacity * 2 && chunk.m_ChunkChars.Length - chunk.m_ChunkLength >= count) + { + for (int i = chunk.m_ChunkLength; i > indexInChunk; ) + { + --i; + chunk.m_ChunkChars[i + count] = chunk.m_ChunkChars[i]; + } + chunk.m_ChunkLength += count; + return; + } + + // Allocate space for the new chunk, which will go before the current one. + StringBuilder newChunk = new StringBuilder(Math.Max(count, DefaultCapacity), chunk.m_MaxCapacity, chunk.m_ChunkPrevious); + newChunk.m_ChunkLength = count; + + // Copy the head of the current buffer to the new buffer. + int copyCount1 = Math.Min(count, indexInChunk); + if (copyCount1 > 0) + { + unsafe + { + fixed (char* chunkCharsPtr = &chunk.m_ChunkChars[0]) + { + ThreadSafeCopy(chunkCharsPtr, newChunk.m_ChunkChars, 0, copyCount1); + + // Slide characters over in the current buffer to make room. + int copyCount2 = indexInChunk - copyCount1; + if (copyCount2 >= 0) + { + ThreadSafeCopy(chunkCharsPtr + copyCount1, chunk.m_ChunkChars, 0, copyCount2); + indexInChunk = copyCount2; + } + } + } + } + + // Wire in the new chunk. + chunk.m_ChunkPrevious = newChunk; + chunk.m_ChunkOffset += count; + if (copyCount1 < count) + { + chunk = newChunk; + indexInChunk = copyCount1; + } + + AssertInvariants(); + } + + /// + /// Used by to allocate another chunk. + /// + /// The size of the character buffer for this chunk. + /// The maximum capacity, to be stored in this chunk. + /// The predecessor of this chunk. + private StringBuilder(int size, int maxCapacity, StringBuilder previousBlock) + { + Debug.Assert(size > 0); + Debug.Assert(maxCapacity > 0); + + m_ChunkChars = new char[size]; + m_MaxCapacity = maxCapacity; + m_ChunkPrevious = previousBlock; + if (previousBlock != null) + { + m_ChunkOffset = previousBlock.m_ChunkOffset + previousBlock.m_ChunkLength; + } + + AssertInvariants(); + } + + /// + /// Removes a specified number of characters beginning at a logical index in this builder. + /// + /// The logical index in this builder to start removing characters. + /// The number of characters to remove. + /// Receives the new chunk containing the logical index. + /// + /// Receives the new index in that is associated with the logical index. + /// + private void Remove(int startIndex, int count, out StringBuilder chunk, out int indexInChunk) + { + AssertInvariants(); + Debug.Assert(startIndex >= 0 && startIndex < Length); + + int endIndex = startIndex + count; + + // Find the chunks for the start and end of the block to delete. + chunk = this; + StringBuilder endChunk = null; + int endIndexInChunk = 0; + for (;;) + { + if (endIndex - chunk.m_ChunkOffset >= 0) + { + if (endChunk == null) + { + endChunk = chunk; + endIndexInChunk = endIndex - endChunk.m_ChunkOffset; + } + if (startIndex - chunk.m_ChunkOffset >= 0) + { + indexInChunk = startIndex - chunk.m_ChunkOffset; + break; + } + } + else + { + chunk.m_ChunkOffset -= count; + } + chunk = chunk.m_ChunkPrevious; + } + Debug.Assert(chunk != null, "We fell off the beginning of the string!"); + + int copyTargetIndexInChunk = indexInChunk; + int copyCount = endChunk.m_ChunkLength - endIndexInChunk; + if (endChunk != chunk) + { + copyTargetIndexInChunk = 0; + // Remove the characters after `startIndex` to the end of the chunk. + chunk.m_ChunkLength = indexInChunk; + + // Remove the characters in chunks between the start and the end chunk. + endChunk.m_ChunkPrevious = chunk; + endChunk.m_ChunkOffset = chunk.m_ChunkOffset + chunk.m_ChunkLength; + + // If the start is 0, then we can throw away the whole start chunk. + if (indexInChunk == 0) + { + endChunk.m_ChunkPrevious = chunk.m_ChunkPrevious; + chunk = endChunk; + } + } + endChunk.m_ChunkLength -= (endIndexInChunk - copyTargetIndexInChunk); + + // SafeCritical: We ensure that `endIndexInChunk + copyCount` is within range of `m_ChunkChars`, and + // also ensure that `copyTargetIndexInChunk + copyCount` is within the chunk. + + // Remove any characters in the end chunk, by sliding the characters down. + if (copyTargetIndexInChunk != endIndexInChunk) // Sometimes no move is necessary + { + ThreadSafeCopy(endChunk.m_ChunkChars, endIndexInChunk, endChunk.m_ChunkChars, copyTargetIndexInChunk, copyCount); + } + + Debug.Assert(chunk != null, "We fell off the beginning of the string!"); + AssertInvariants(); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Text/UTF32Encoding.cs b/external/corefx/src/Common/src/CoreLib/System/Text/UTF32Encoding.cs new file mode 100644 index 0000000000..7828775ea0 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Text/UTF32Encoding.cs @@ -0,0 +1,1213 @@ +// 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. + +// +// Don't override IsAlwaysNormalized because it is just a Unicode Transformation and could be confused. +// + +using System; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; + +namespace System.Text +{ + // Encodes text into and out of UTF-32. UTF-32 is a way of writing + // Unicode characters with a single storage unit (32 bits) per character, + // + // The UTF-32 byte order mark is simply the Unicode byte order mark + // (0x00FEFF) written in UTF-32 (0x0000FEFF or 0xFFFE0000). The byte order + // mark is used mostly to distinguish UTF-32 text from other encodings, and doesn't + // switch the byte orderings. + + public sealed class UTF32Encoding : Encoding + { + /* + words bits UTF-32 representation + ----- ---- ----------------------------------- + 1 16 00000000 00000000 xxxxxxxx xxxxxxxx + 2 21 00000000 000xxxxx hhhhhhll llllllll + ----- ---- ----------------------------------- + + Surrogate: + Real Unicode value = (HighSurrogate - 0xD800) * 0x400 + (LowSurrogate - 0xDC00) + 0x10000 + */ + + // Used by Encoding.UTF32/BigEndianUTF32 for lazy initialization + // The initialization code will not be run until a static member of the class is referenced + internal static readonly UTF32Encoding s_default = new UTF32Encoding(bigEndian: false, byteOrderMark: true); + internal static readonly UTF32Encoding s_bigEndianDefault = new UTF32Encoding(bigEndian: true, byteOrderMark: true); + + private static readonly byte[] s_bigEndianPreamble = new byte[4] { 0x00, 0x00, 0xFE, 0xFF }; + private static readonly byte[] s_littleEndianPreamble = new byte[4] { 0xFF, 0xFE, 0x00, 0x00 }; + + private bool _emitUTF32ByteOrderMark = false; + private bool _isThrowException = false; + private bool _bigEndian = false; + + + public UTF32Encoding() : this(false, true, false) + { + } + + + public UTF32Encoding(bool bigEndian, bool byteOrderMark) : + this(bigEndian, byteOrderMark, false) + { + } + + + public UTF32Encoding(bool bigEndian, bool byteOrderMark, bool throwOnInvalidCharacters) : + base(bigEndian ? 12001 : 12000) + { + _bigEndian = bigEndian; + _emitUTF32ByteOrderMark = byteOrderMark; + _isThrowException = throwOnInvalidCharacters; + + // Encoding constructor already did this, but it'll be wrong if we're throwing exceptions + if (_isThrowException) + SetDefaultFallbacks(); + } + + internal override void SetDefaultFallbacks() + { + // For UTF-X encodings, we use a replacement fallback with an empty string + if (_isThrowException) + { + this.encoderFallback = EncoderFallback.ExceptionFallback; + this.decoderFallback = DecoderFallback.ExceptionFallback; + } + else + { + this.encoderFallback = new EncoderReplacementFallback("\xFFFD"); + this.decoderFallback = new DecoderReplacementFallback("\xFFFD"); + } + } + + + // The following methods are copied from EncodingNLS.cs. + // Unfortunately EncodingNLS.cs is internal and we're public, so we have to re-implement them here. + // These should be kept in sync for the following classes: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + // Returns the number of bytes required to encode a range of characters in + // a character array. + // + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe int GetByteCount(char[] chars, int index, int count) + { + // Validate input parameters + if (chars == null) + throw new ArgumentNullException("chars", SR.ArgumentNull_Array); + + if (index < 0 || count < 0) + throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (chars.Length - index < count) + throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); + + // If no input, return 0, avoid fixed empty array problem + if (count == 0) + return 0; + + // Just call the pointer version + fixed (char* pChars = chars) + return GetByteCount(pChars + index, count, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe int GetByteCount(String s) + { + // Validate input + if (s==null) + throw new ArgumentNullException("s"); + + fixed (char* pChars = s) + return GetByteCount(pChars, s.Length, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + [CLSCompliant(false)] + public override unsafe int GetByteCount(char* chars, int count) + { + // Validate Parameters + if (chars == null) + throw new ArgumentNullException("chars", SR.ArgumentNull_Array); + + if (count < 0) + throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); + + // Call it with empty encoder + return GetByteCount(chars, count, null); + } + + // Parent method is safe. + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + public override unsafe int GetBytes(String s, int charIndex, int charCount, + byte[] bytes, int byteIndex) + { + if (s == null || bytes == null) + throw new ArgumentNullException((s == null ? "s" : "bytes"), SR.ArgumentNull_Array); + + if (charIndex < 0 || charCount < 0) + throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (s.Length - charIndex < charCount) + throw new ArgumentOutOfRangeException("s", SR.ArgumentOutOfRange_IndexCount); + + if (byteIndex < 0 || byteIndex > bytes.Length) + throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); + + int byteCount = bytes.Length - byteIndex; + + fixed (char* pChars = s) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) + return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null); + } + + // Encodes a range of characters in a character array into a range of bytes + // in a byte array. An exception occurs if the byte array is not large + // enough to hold the complete encoding of the characters. The + // GetByteCount method can be used to determine the exact number of + // bytes that will be produced for a given range of characters. + // Alternatively, the GetMaxByteCount method can be used to + // determine the maximum number of bytes that will be produced for a given + // number of characters, regardless of the actual character values. + // + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe int GetBytes(char[] chars, int charIndex, int charCount, + byte[] bytes, int byteIndex) + { + // Validate parameters + if (chars == null || bytes == null) + throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array); + + if (charIndex < 0 || charCount < 0) + throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (chars.Length - charIndex < charCount) + throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); + + if (byteIndex < 0 || byteIndex > bytes.Length) + throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); + + // If nothing to encode return 0, avoid fixed problem + if (charCount == 0) + return 0; + + // Just call pointer version + int byteCount = bytes.Length - byteIndex; + + fixed (char* pChars = chars) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) + // Remember that byteCount is # to decode, not size of array. + return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + [CLSCompliant(false)] + public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount) + { + // Validate Parameters + if (bytes == null || chars == null) + throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + + if (charCount < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + return GetBytes(chars, charCount, bytes, byteCount, null); + } + + // Returns the number of characters produced by decoding a range of bytes + // in a byte array. + // + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe int GetCharCount(byte[] bytes, int index, int count) + { + // Validate Parameters + if (bytes == null) + throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + + if (index < 0 || count < 0) + throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (bytes.Length - index < count) + throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + + // If no input just return 0, fixed doesn't like 0 length arrays. + if (count == 0) + return 0; + + // Just call pointer version + fixed (byte* pBytes = bytes) + return GetCharCount(pBytes + index, count, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + [CLSCompliant(false)] + public override unsafe int GetCharCount(byte* bytes, int count) + { + // Validate Parameters + if (bytes == null) + throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + + if (count < 0) + throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); + + return GetCharCount(bytes, count, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount, + char[] chars, int charIndex) + { + // Validate Parameters + if (bytes == null || chars == null) + throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + + if (byteIndex < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if ( bytes.Length - byteIndex < byteCount) + throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + + if (charIndex < 0 || charIndex > chars.Length) + throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index); + + // If no input, return 0 & avoid fixed problem + if (byteCount == 0) + return 0; + + // Just call pointer version + int charCount = chars.Length - charIndex; + + fixed (byte* pBytes = bytes) fixed (char* pChars = &MemoryMarshal.GetReference((Span)chars)) + // Remember that charCount is # to decode, not size of array + return GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + [CLSCompliant(false)] + public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount) + { + // Validate Parameters + if (bytes == null || chars == null) + throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + + if (charCount < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + return GetChars(bytes, byteCount, chars, charCount, null); + } + + // Returns a string containing the decoded representation of a range of + // bytes in a byte array. + // + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe String GetString(byte[] bytes, int index, int count) + { + // Validate Parameters + if (bytes == null) + throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + + if (index < 0 || count < 0) + throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (bytes.Length - index < count) + throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + + // Avoid problems with empty input buffer + if (count == 0) return String.Empty; + + fixed (byte* pBytes = bytes) + return String.CreateStringFromEncoding( + pBytes + index, count, this); + } + + // + // End of standard methods copied from EncodingNLS.cs + // + + internal override unsafe int GetByteCount(char* chars, int count, EncoderNLS encoder) + { + Debug.Assert(chars != null, "[UTF32Encoding.GetByteCount]chars!=null"); + Debug.Assert(count >= 0, "[UTF32Encoding.GetByteCount]count >=0"); + + char* end = chars + count; + char* charStart = chars; + int byteCount = 0; + + char highSurrogate = '\0'; + + // For fallback we may need a fallback buffer + EncoderFallbackBuffer fallbackBuffer = null; + char* charsForFallback; + + if (encoder != null) + { + highSurrogate = encoder._charLeftOver; + fallbackBuffer = encoder.FallbackBuffer; + + // We mustn't have left over fallback data when counting + if (fallbackBuffer.Remaining > 0) + throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, encoder.Fallback.GetType())); + } + else + { + fallbackBuffer = this.encoderFallback.CreateFallbackBuffer(); + } + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(charStart, end, encoder, false); + + char ch; + TryAgain: + + while (((ch = fallbackBuffer.InternalGetNextChar()) != 0) || chars < end) + { + // First unwind any fallback + if (ch == 0) + { + // No fallback, just get next char + ch = *chars; + chars++; + } + + // Do we need a low surrogate? + if (highSurrogate != '\0') + { + // + // In previous char, we encounter a high surrogate, so we are expecting a low surrogate here. + // + if (Char.IsLowSurrogate(ch)) + { + // They're all legal + highSurrogate = '\0'; + + // + // One surrogate pair will be translated into 4 bytes UTF32. + // + + byteCount += 4; + continue; + } + + // We are missing our low surrogate, decrement chars and fallback the high surrogate + // The high surrogate may have come from the encoder, but nothing else did. + Debug.Assert(chars > charStart, + "[UTF32Encoding.GetByteCount]Expected chars to have advanced if no low surrogate"); + chars--; + + // Do the fallback + charsForFallback = chars; + fallbackBuffer.InternalFallback(highSurrogate, ref charsForFallback); + chars = charsForFallback; + + // We're going to fallback the old high surrogate. + highSurrogate = '\0'; + continue; + } + + // Do we have another high surrogate? + if (Char.IsHighSurrogate(ch)) + { + // + // We'll have a high surrogate to check next time. + // + highSurrogate = ch; + continue; + } + + // Check for illegal characters + if (Char.IsLowSurrogate(ch)) + { + // We have a leading low surrogate, do the fallback + charsForFallback = chars; + fallbackBuffer.InternalFallback(ch, ref charsForFallback); + chars = charsForFallback; + + // Try again with fallback buffer + continue; + } + + // We get to add the character (4 bytes UTF32) + byteCount += 4; + } + + // May have to do our last surrogate + if ((encoder == null || encoder.MustFlush) && highSurrogate > 0) + { + // We have to do the fallback for the lonely high surrogate + charsForFallback = chars; + fallbackBuffer.InternalFallback(highSurrogate, ref charsForFallback); + chars = charsForFallback; + + highSurrogate = (char)0; + goto TryAgain; + } + + // Check for overflows. + if (byteCount < 0) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_GetByteCountOverflow); + + // Shouldn't have anything in fallback buffer for GetByteCount + // (don't have to check _throwOnOverflow for count) + Debug.Assert(fallbackBuffer.Remaining == 0, + "[UTF32Encoding.GetByteCount]Expected empty fallback buffer at end"); + + // Return our count + return byteCount; + } + + internal override unsafe int GetBytes(char* chars, int charCount, + byte* bytes, int byteCount, EncoderNLS encoder) + { + Debug.Assert(chars != null, "[UTF32Encoding.GetBytes]chars!=null"); + Debug.Assert(bytes != null, "[UTF32Encoding.GetBytes]bytes!=null"); + Debug.Assert(byteCount >= 0, "[UTF32Encoding.GetBytes]byteCount >=0"); + Debug.Assert(charCount >= 0, "[UTF32Encoding.GetBytes]charCount >=0"); + + char* charStart = chars; + char* charEnd = chars + charCount; + byte* byteStart = bytes; + byte* byteEnd = bytes + byteCount; + + char highSurrogate = '\0'; + + // For fallback we may need a fallback buffer + EncoderFallbackBuffer fallbackBuffer = null; + char* charsForFallback; + + if (encoder != null) + { + highSurrogate = encoder._charLeftOver; + fallbackBuffer = encoder.FallbackBuffer; + + // We mustn't have left over fallback data when not converting + if (encoder._throwOnOverflow && fallbackBuffer.Remaining > 0) + throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, encoder.Fallback.GetType())); + } + else + { + fallbackBuffer = this.encoderFallback.CreateFallbackBuffer(); + } + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true); + + char ch; + TryAgain: + + while (((ch = fallbackBuffer.InternalGetNextChar()) != 0) || chars < charEnd) + { + // First unwind any fallback + if (ch == 0) + { + // No fallback, just get next char + ch = *chars; + chars++; + } + + // Do we need a low surrogate? + if (highSurrogate != '\0') + { + // + // In previous char, we encountered a high surrogate, so we are expecting a low surrogate here. + // + if (Char.IsLowSurrogate(ch)) + { + // Is it a legal one? + uint iTemp = GetSurrogate(highSurrogate, ch); + highSurrogate = '\0'; + + // + // One surrogate pair will be translated into 4 bytes UTF32. + // + if (bytes + 3 >= byteEnd) + { + // Don't have 4 bytes + if (fallbackBuffer.bFallingBack) + { + fallbackBuffer.MovePrevious(); // Aren't using these 2 fallback chars + fallbackBuffer.MovePrevious(); + } + else + { + // If we don't have enough room, then either we should've advanced a while + // or we should have bytes==byteStart and throw below + Debug.Assert(chars > charStart + 1 || bytes == byteStart, + "[UnicodeEncoding.GetBytes]Expected chars to have when no room to add surrogate pair"); + chars -= 2; // Aren't using those 2 chars + } + ThrowBytesOverflow(encoder, bytes == byteStart); // Throw maybe (if no bytes written) + highSurrogate = (char)0; // Nothing left over (we backed up to start of pair if supplimentary) + break; + } + + if (_bigEndian) + { + *(bytes++) = (byte)(0x00); + *(bytes++) = (byte)(iTemp >> 16); // Implies & 0xFF, which isn't needed cause high are all 0 + *(bytes++) = (byte)(iTemp >> 8); // Implies & 0xFF + *(bytes++) = (byte)(iTemp); // Implies & 0xFF + } + else + { + *(bytes++) = (byte)(iTemp); // Implies & 0xFF + *(bytes++) = (byte)(iTemp >> 8); // Implies & 0xFF + *(bytes++) = (byte)(iTemp >> 16); // Implies & 0xFF, which isn't needed cause high are all 0 + *(bytes++) = (byte)(0x00); + } + continue; + } + + // We are missing our low surrogate, decrement chars and fallback the high surrogate + // The high surrogate may have come from the encoder, but nothing else did. + Debug.Assert(chars > charStart, + "[UTF32Encoding.GetBytes]Expected chars to have advanced if no low surrogate"); + chars--; + + // Do the fallback + charsForFallback = chars; + fallbackBuffer.InternalFallback(highSurrogate, ref charsForFallback); + chars = charsForFallback; + + // We're going to fallback the old high surrogate. + highSurrogate = '\0'; + continue; + } + + // Do we have another high surrogate?, if so remember it + if (Char.IsHighSurrogate(ch)) + { + // + // We'll have a high surrogate to check next time. + // + highSurrogate = ch; + continue; + } + + // Check for illegal characters (low surrogate) + if (Char.IsLowSurrogate(ch)) + { + // We have a leading low surrogate, do the fallback + charsForFallback = chars; + fallbackBuffer.InternalFallback(ch, ref charsForFallback); + chars = charsForFallback; + + // Try again with fallback buffer + continue; + } + + // We get to add the character, yippee. + if (bytes + 3 >= byteEnd) + { + // Don't have 4 bytes + if (fallbackBuffer.bFallingBack) + fallbackBuffer.MovePrevious(); // Aren't using this fallback char + else + { + // Must've advanced already + Debug.Assert(chars > charStart, + "[UTF32Encoding.GetBytes]Expected chars to have advanced if normal character"); + chars--; // Aren't using this char + } + ThrowBytesOverflow(encoder, bytes == byteStart); // Throw maybe (if no bytes written) + break; // Didn't throw, stop + } + + if (_bigEndian) + { + *(bytes++) = (byte)(0x00); + *(bytes++) = (byte)(0x00); + *(bytes++) = (byte)((uint)ch >> 8); // Implies & 0xFF + *(bytes++) = (byte)(ch); // Implies & 0xFF + } + else + { + *(bytes++) = (byte)(ch); // Implies & 0xFF + *(bytes++) = (byte)((uint)ch >> 8); // Implies & 0xFF + *(bytes++) = (byte)(0x00); + *(bytes++) = (byte)(0x00); + } + } + + // May have to do our last surrogate + if ((encoder == null || encoder.MustFlush) && highSurrogate > 0) + { + // We have to do the fallback for the lonely high surrogate + charsForFallback = chars; + fallbackBuffer.InternalFallback(highSurrogate, ref charsForFallback); + chars = charsForFallback; + + highSurrogate = (char)0; + goto TryAgain; + } + + // Fix our encoder if we have one + Debug.Assert(highSurrogate == 0 || (encoder != null && !encoder.MustFlush), + "[UTF32Encoding.GetBytes]Expected encoder to be flushed."); + + if (encoder != null) + { + // Remember our left over surrogate (or 0 if flushing) + encoder._charLeftOver = highSurrogate; + + // Need # chars used + encoder._charsUsed = (int)(chars - charStart); + } + + // return the new length + return (int)(bytes - byteStart); + } + + internal override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder) + { + Debug.Assert(bytes != null, "[UTF32Encoding.GetCharCount]bytes!=null"); + Debug.Assert(count >= 0, "[UTF32Encoding.GetCharCount]count >=0"); + + UTF32Decoder decoder = (UTF32Decoder)baseDecoder; + + // None so far! + int charCount = 0; + byte* end = bytes + count; + byte* byteStart = bytes; + + // Set up decoder + int readCount = 0; + uint iChar = 0; + + // For fallback we may need a fallback buffer + DecoderFallbackBuffer fallbackBuffer = null; + + // See if there's anything in our decoder + if (decoder != null) + { + readCount = decoder.readByteCount; + iChar = (uint)decoder.iChar; + fallbackBuffer = decoder.FallbackBuffer; + + // Shouldn't have anything in fallback buffer for GetCharCount + // (don't have to check _throwOnOverflow for chars or count) + Debug.Assert(fallbackBuffer.Remaining == 0, + "[UTF32Encoding.GetCharCount]Expected empty fallback buffer at start"); + } + else + { + fallbackBuffer = this.decoderFallback.CreateFallbackBuffer(); + } + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(byteStart, null); + + // Loop through our input, 4 characters at a time! + while (bytes < end && charCount >= 0) + { + // Get our next character + if (_bigEndian) + { + // Scoot left and add it to the bottom + iChar <<= 8; + iChar += *(bytes++); + } + else + { + // Scoot right and add it to the top + iChar >>= 8; + iChar += (uint)(*(bytes++)) << 24; + } + + readCount++; + + // See if we have all the bytes yet + if (readCount < 4) + continue; + + // Have the bytes + readCount = 0; + + // See if its valid to encode + if (iChar > 0x10FFFF || (iChar >= 0xD800 && iChar <= 0xDFFF)) + { + // Need to fall back these 4 bytes + byte[] fallbackBytes; + if (_bigEndian) + { + fallbackBytes = new byte[] { + unchecked((byte)(iChar>>24)), unchecked((byte)(iChar>>16)), + unchecked((byte)(iChar>>8)), unchecked((byte)(iChar)) }; + } + else + { + fallbackBytes = new byte[] { + unchecked((byte)(iChar)), unchecked((byte)(iChar>>8)), + unchecked((byte)(iChar>>16)), unchecked((byte)(iChar>>24)) }; + } + + charCount += fallbackBuffer.InternalFallback(fallbackBytes, bytes); + + // Ignore the illegal character + iChar = 0; + continue; + } + + // Ok, we have something we can add to our output + if (iChar >= 0x10000) + { + // Surrogates take 2 + charCount++; + } + + // Add the rest of the surrogate or our normal character + charCount++; + + // iChar is back to 0 + iChar = 0; + } + + // See if we have something left over that has to be decoded + if (readCount > 0 && (decoder == null || decoder.MustFlush)) + { + // Oops, there's something left over with no place to go. + byte[] fallbackBytes = new byte[readCount]; + if (_bigEndian) + { + while (readCount > 0) + { + fallbackBytes[--readCount] = unchecked((byte)iChar); + iChar >>= 8; + } + } + else + { + while (readCount > 0) + { + fallbackBytes[--readCount] = unchecked((byte)(iChar >> 24)); + iChar <<= 8; + } + } + + charCount += fallbackBuffer.InternalFallback(fallbackBytes, bytes); + } + + // Check for overflows. + if (charCount < 0) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_GetByteCountOverflow); + + // Shouldn't have anything in fallback buffer for GetCharCount + // (don't have to check _throwOnOverflow for chars or count) + Debug.Assert(fallbackBuffer.Remaining == 0, + "[UTF32Encoding.GetCharCount]Expected empty fallback buffer at end"); + + // Return our count + return charCount; + } + + internal override unsafe int GetChars(byte* bytes, int byteCount, + char* chars, int charCount, DecoderNLS baseDecoder) + { + Debug.Assert(chars != null, "[UTF32Encoding.GetChars]chars!=null"); + Debug.Assert(bytes != null, "[UTF32Encoding.GetChars]bytes!=null"); + Debug.Assert(byteCount >= 0, "[UTF32Encoding.GetChars]byteCount >=0"); + Debug.Assert(charCount >= 0, "[UTF32Encoding.GetChars]charCount >=0"); + + UTF32Decoder decoder = (UTF32Decoder)baseDecoder; + + // None so far! + char* charStart = chars; + char* charEnd = chars + charCount; + + byte* byteStart = bytes; + byte* byteEnd = bytes + byteCount; + + // See if there's anything in our decoder (but don't clear it yet) + int readCount = 0; + uint iChar = 0; + + // For fallback we may need a fallback buffer + DecoderFallbackBuffer fallbackBuffer = null; + char* charsForFallback; + + // See if there's anything in our decoder + if (decoder != null) + { + readCount = decoder.readByteCount; + iChar = (uint)decoder.iChar; + fallbackBuffer = baseDecoder.FallbackBuffer; + + // Shouldn't have anything in fallback buffer for GetChars + // (don't have to check _throwOnOverflow for chars) + Debug.Assert(fallbackBuffer.Remaining == 0, + "[UTF32Encoding.GetChars]Expected empty fallback buffer at start"); + } + else + { + fallbackBuffer = this.decoderFallback.CreateFallbackBuffer(); + } + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(bytes, chars + charCount); + + // Loop through our input, 4 characters at a time! + while (bytes < byteEnd) + { + // Get our next character + if (_bigEndian) + { + // Scoot left and add it to the bottom + iChar <<= 8; + iChar += *(bytes++); + } + else + { + // Scoot right and add it to the top + iChar >>= 8; + iChar += (uint)(*(bytes++)) << 24; + } + + readCount++; + + // See if we have all the bytes yet + if (readCount < 4) + continue; + + // Have the bytes + readCount = 0; + + // See if its valid to encode + if (iChar > 0x10FFFF || (iChar >= 0xD800 && iChar <= 0xDFFF)) + { + // Need to fall back these 4 bytes + byte[] fallbackBytes; + if (_bigEndian) + { + fallbackBytes = new byte[] { + unchecked((byte)(iChar>>24)), unchecked((byte)(iChar>>16)), + unchecked((byte)(iChar>>8)), unchecked((byte)(iChar)) }; + } + else + { + fallbackBytes = new byte[] { + unchecked((byte)(iChar)), unchecked((byte)(iChar>>8)), + unchecked((byte)(iChar>>16)), unchecked((byte)(iChar>>24)) }; + } + + // Chars won't be updated unless this works. + charsForFallback = chars; + bool fallbackResult = fallbackBuffer.InternalFallback(fallbackBytes, bytes, ref charsForFallback); + chars = charsForFallback; + + if (!fallbackResult) + { + // Couldn't fallback, throw or wait til next time + // We either read enough bytes for bytes-=4 to work, or we're + // going to throw in ThrowCharsOverflow because chars == charStart + Debug.Assert(bytes >= byteStart + 4 || chars == charStart, + "[UTF32Encoding.GetChars]Expected to have consumed bytes or throw (bad surrogate)"); + bytes -= 4; // get back to where we were + iChar = 0; // Remembering nothing + fallbackBuffer.InternalReset(); + ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output + break; // Stop here, didn't throw + } + + // Ignore the illegal character + iChar = 0; + continue; + } + + + // Ok, we have something we can add to our output + if (iChar >= 0x10000) + { + // Surrogates take 2 + if (chars >= charEnd - 1) + { + // Throwing or stopping + // We either read enough bytes for bytes-=4 to work, or we're + // going to throw in ThrowCharsOverflow because chars == charStart + Debug.Assert(bytes >= byteStart + 4 || chars == charStart, + "[UTF32Encoding.GetChars]Expected to have consumed bytes or throw (surrogate)"); + bytes -= 4; // get back to where we were + iChar = 0; // Remembering nothing + ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output + break; // Stop here, didn't throw + } + + *(chars++) = GetHighSurrogate(iChar); + iChar = GetLowSurrogate(iChar); + } + // Bounds check for normal character + else if (chars >= charEnd) + { + // Throwing or stopping + // We either read enough bytes for bytes-=4 to work, or we're + // going to throw in ThrowCharsOverflow because chars == charStart + Debug.Assert(bytes >= byteStart + 4 || chars == charStart, + "[UTF32Encoding.GetChars]Expected to have consumed bytes or throw (normal char)"); + bytes -= 4; // get back to where we were + iChar = 0; // Remembering nothing + ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output + break; // Stop here, didn't throw + } + + // Add the rest of the surrogate or our normal character + *(chars++) = (char)iChar; + + // iChar is back to 0 + iChar = 0; + } + + // See if we have something left over that has to be decoded + if (readCount > 0 && (decoder == null || decoder.MustFlush)) + { + // Oops, there's something left over with no place to go. + byte[] fallbackBytes = new byte[readCount]; + int tempCount = readCount; + if (_bigEndian) + { + while (tempCount > 0) + { + fallbackBytes[--tempCount] = unchecked((byte)iChar); + iChar >>= 8; + } + } + else + { + while (tempCount > 0) + { + fallbackBytes[--tempCount] = unchecked((byte)(iChar >> 24)); + iChar <<= 8; + } + } + + charsForFallback = chars; + bool fallbackResult = fallbackBuffer.InternalFallback(fallbackBytes, bytes, ref charsForFallback); + chars = charsForFallback; + + if (!fallbackResult) + { + // Couldn't fallback. + fallbackBuffer.InternalReset(); + ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output + // Stop here, didn't throw, backed up, so still nothing in buffer + } + else + { + // Don't clear our decoder unless we could fall it back. + // If we caught the if above, then we're a convert() and will catch this next time. + readCount = 0; + iChar = 0; + } + } + + // Remember any left over stuff, clearing buffer as well for MustFlush + if (decoder != null) + { + decoder.iChar = (int)iChar; + decoder.readByteCount = readCount; + decoder._bytesUsed = (int)(bytes - byteStart); + } + + // Shouldn't have anything in fallback buffer for GetChars + // (don't have to check _throwOnOverflow for chars) + Debug.Assert(fallbackBuffer.Remaining == 0, + "[UTF32Encoding.GetChars]Expected empty fallback buffer at end"); + + // Return our count + return (int)(chars - charStart); + } + + + private uint GetSurrogate(char cHigh, char cLow) + { + return (((uint)cHigh - 0xD800) * 0x400) + ((uint)cLow - 0xDC00) + 0x10000; + } + + private char GetHighSurrogate(uint iChar) + { + return (char)((iChar - 0x10000) / 0x400 + 0xD800); + } + + private char GetLowSurrogate(uint iChar) + { + return (char)((iChar - 0x10000) % 0x400 + 0xDC00); + } + + + public override Decoder GetDecoder() + { + return new UTF32Decoder(this); + } + + + public override Encoder GetEncoder() + { + return new EncoderNLS(this); + } + + + public override int GetMaxByteCount(int charCount) + { + if (charCount < 0) + throw new ArgumentOutOfRangeException(nameof(charCount), + SR.ArgumentOutOfRange_NeedNonNegNum); + + // Characters would be # of characters + 1 in case left over high surrogate is ? * max fallback + long byteCount = (long)charCount + 1; + + if (EncoderFallback.MaxCharCount > 1) + byteCount *= EncoderFallback.MaxCharCount; + + // 4 bytes per char + byteCount *= 4; + + if (byteCount > 0x7fffffff) + throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_GetByteCountOverflow); + + return (int)byteCount; + } + + + public override int GetMaxCharCount(int byteCount) + { + if (byteCount < 0) + throw new ArgumentOutOfRangeException(nameof(byteCount), + SR.ArgumentOutOfRange_NeedNonNegNum); + + // A supplementary character becomes 2 surrogate characters, so 4 input bytes becomes 2 chars, + // plus we may have 1 surrogate char left over if the decoder has 3 bytes in it already for a non-bmp char. + // Have to add another one because 1/2 == 0, but 3 bytes left over could be 2 char surrogate pair + int charCount = (byteCount / 2) + 2; + + // Also consider fallback because our input bytes could be out of range of unicode. + // Since fallback would fallback 4 bytes at a time, we'll only fall back 1/2 of MaxCharCount. + if (DecoderFallback.MaxCharCount > 2) + { + // Multiply time fallback size + charCount *= DecoderFallback.MaxCharCount; + + // We were already figuring 2 chars per 4 bytes, but fallback will be different # + charCount /= 2; + } + + if (charCount > 0x7fffffff) + throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_GetCharCountOverflow); + + return (int)charCount; + } + + + public override byte[] GetPreamble() + { + if (_emitUTF32ByteOrderMark) + { + // Allocate new array to prevent users from modifying it. + if (_bigEndian) + { + return new byte[4] { 0x00, 0x00, 0xFE, 0xFF }; + } + else + { + return new byte[4] { 0xFF, 0xFE, 0x00, 0x00 }; // 00 00 FE FF + } + } + else + return Array.Empty(); + } + + public override ReadOnlySpan Preamble => + GetType() != typeof(UTF32Encoding) ? GetPreamble() : // in case a derived UTF32Encoding overrode GetPreamble + _emitUTF32ByteOrderMark ? (_bigEndian ? s_bigEndianPreamble : s_littleEndianPreamble) : + Array.Empty(); + + public override bool Equals(Object value) + { + UTF32Encoding that = value as UTF32Encoding; + if (that != null) + { + return (_emitUTF32ByteOrderMark == that._emitUTF32ByteOrderMark) && + (_bigEndian == that._bigEndian) && + (EncoderFallback.Equals(that.EncoderFallback)) && + (DecoderFallback.Equals(that.DecoderFallback)); + } + return (false); + } + + + public override int GetHashCode() + { + //Not great distribution, but this is relatively unlikely to be used as the key in a hashtable. + return this.EncoderFallback.GetHashCode() + this.DecoderFallback.GetHashCode() + + CodePage + (_emitUTF32ByteOrderMark ? 4 : 0) + (_bigEndian ? 8 : 0); + } + + private sealed class UTF32Decoder : DecoderNLS + { + // Need a place to store any extra bytes we may have picked up + internal int iChar = 0; + internal int readByteCount = 0; + + public UTF32Decoder(UTF32Encoding encoding) : base(encoding) + { + // base calls reset + } + + public override void Reset() + { + this.iChar = 0; + this.readByteCount = 0; + if (_fallbackBuffer != null) + _fallbackBuffer.Reset(); + } + + // Anything left in our decoder? + internal override bool HasState + { + get + { + // ReadByteCount is our flag. (iChar==0 doesn't mean much). + return (this.readByteCount != 0); + } + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Text/UTF7Encoding.cs b/external/corefx/src/Common/src/CoreLib/System/Text/UTF7Encoding.cs similarity index 90% rename from external/corert/src/System.Private.CoreLib/src/System/Text/UTF7Encoding.cs rename to external/corefx/src/Common/src/CoreLib/System/Text/UTF7Encoding.cs index 6516dd5164..0246c28915 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Text/UTF7Encoding.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Text/UTF7Encoding.cs @@ -7,13 +7,11 @@ // using System; -using System.Runtime.Serialization; using System.Diagnostics; -using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; namespace System.Text { - [Serializable] public class UTF7Encoding : Encoding { private const String base64Chars = @@ -44,7 +42,6 @@ namespace System.Text // This array has a size of 128. private bool[] _directEncode; - [OptionalField(VersionAdded = 2)] private bool _allowOptionals; private const int UTF7_CODEPAGE = 65000; @@ -120,7 +117,7 @@ namespace System.Text } // The following methods are copied from EncodingNLS.cs. - // Unfortunately EncodingNLS.cs is internal and we're public, so we have to reimpliment them here. + // Unfortunately EncodingNLS.cs is internal and we're public, so we have to re-implement them here. // These should be kept in sync for the following classes: // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding @@ -143,7 +140,6 @@ namespace System.Text if (chars.Length - index < count) throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); // If no input, return 0, avoid fixed empty array problem if (count == 0) @@ -164,7 +160,6 @@ namespace System.Text // Validate input if (s==null) throw new ArgumentNullException("s"); - Contract.EndContractBlock(); fixed (char* pChars = s) return GetByteCount(pChars, s.Length, null); @@ -183,7 +178,6 @@ namespace System.Text if (count < 0) throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Call it with empty encoder return GetByteCount(chars, count, null); @@ -208,15 +202,10 @@ namespace System.Text if (byteIndex < 0 || byteIndex > bytes.Length) throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); int byteCount = bytes.Length - byteIndex; - // Fixed doesn't like empty arrays - if (bytes.Length == 0) - bytes = new byte[1]; - - fixed (char* pChars = s) fixed (byte* pBytes = &bytes[0]) + fixed (char* pChars = s) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null); } @@ -249,7 +238,6 @@ namespace System.Text if (byteIndex < 0 || byteIndex > bytes.Length) throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); // If nothing to encode return 0, avoid fixed problem if (charCount == 0) @@ -258,11 +246,7 @@ namespace System.Text // Just call pointer version int byteCount = bytes.Length - byteIndex; - // Fixed doesn't like empty arrays - if (bytes.Length == 0) - bytes = new byte[1]; - - fixed (char* pChars = chars) fixed (byte* pBytes = &bytes[0]) + fixed (char* pChars = chars) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) // Remember that byteCount is # to decode, not size of array. return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null); } @@ -280,7 +264,6 @@ namespace System.Text if (charCount < 0 || byteCount < 0) throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); return GetBytes(chars, charCount, bytes, byteCount, null); } @@ -304,7 +287,6 @@ namespace System.Text if (bytes.Length - index < count) throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); // If no input just return 0, fixed doesn't like 0 length arrays. if (count == 0) @@ -328,7 +310,6 @@ namespace System.Text if (count < 0) throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); return GetCharCount(bytes, count, null); } @@ -353,7 +334,6 @@ namespace System.Text if (charIndex < 0 || charIndex > chars.Length) throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); // If no input, return 0 & avoid fixed problem if (byteCount == 0) @@ -362,11 +342,7 @@ namespace System.Text // Just call pointer version int charCount = chars.Length - charIndex; - // Fixed doesn't like empty arrays - if (chars.Length == 0) - chars = new char[1]; - - fixed (byte* pBytes = bytes) fixed (char* pChars = &chars[0]) + fixed (byte* pBytes = bytes) fixed (char* pChars = &MemoryMarshal.GetReference((Span)chars)) // Remember that charCount is # to decode, not size of array return GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, null); } @@ -384,7 +360,6 @@ namespace System.Text if (charCount < 0 || byteCount < 0) throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); return GetChars(bytes, byteCount, chars, charCount, null); } @@ -408,7 +383,6 @@ namespace System.Text if (bytes.Length - index < count) throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); // Avoid problems with empty input buffer if (count == 0) return String.Empty; @@ -565,7 +539,7 @@ namespace System.Text // We already cleared bits & bitcount for mustflush case encoder.bits = bits; encoder.bitCount = bitCount; - encoder.m_charsUsed = buffer.CharsUsed; + encoder._charsUsed = buffer.CharsUsed; } return buffer.Count; @@ -652,7 +626,7 @@ namespace System.Text if (currentByte != '-') { // >= 0x80 (because of 1st if statemtn) - // We need this check since the base64Values[b] check below need b <= 0x7f. + // We need this check since the _base64Values[b] check below need b <= 0x7f. // This is not a valid base 64 byte. Terminate the shifted-sequence and // emit this byte. @@ -739,7 +713,7 @@ namespace System.Text decoder.bitCount = bitCount; decoder.firstByte = firstByte; } - decoder.m_bytesUsed = buffer.BytesUsed; + decoder._bytesUsed = buffer.BytesUsed; } // else ignore any hanging bits. @@ -765,7 +739,6 @@ namespace System.Text if (charCount < 0) throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Suppose that every char can not be direct-encoded, we know that // a byte can encode 6 bits of the Unicode character. And we will @@ -798,7 +771,6 @@ namespace System.Text if (byteCount < 0) throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Worst case is 1 char per byte. Minimum 1 for left over bits in case decoder is being flushed // Also note that we ignore extra bits (per spec), so UTF7 doesn't have unknown in this direction. @@ -808,10 +780,9 @@ namespace System.Text return charCount; } - [Serializable] // Of all the amazing things... This MUST be Decoder so that our com name // for System.Text.Decoder doesn't change - private sealed class Decoder : DecoderNLS, ISerializable + private sealed class Decoder : DecoderNLS { /*private*/ internal int bits; @@ -825,41 +796,13 @@ namespace System.Text // base calls reset } - // Constructor called by serialization, have to handle deserializing from Everett - internal Decoder(SerializationInfo info, StreamingContext context) - { - // Any info? - if (info == null) throw new ArgumentNullException(nameof(info)); - Contract.EndContractBlock(); - - // Get common info - this.bits = (int)info.GetValue("bits", typeof(int)); - this.bitCount = (int)info.GetValue("bitCount", typeof(int)); - this.firstByte = (bool)info.GetValue("firstByte", typeof(bool)); - this.m_encoding = (Encoding)info.GetValue("encoding", typeof(Encoding)); - } - - // ISerializable implementation, get data for this object - void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) - { - // Any info? - if (info == null) throw new ArgumentNullException(nameof(info)); - Contract.EndContractBlock(); - - // Save Whidbey data - info.AddValue("encoding", this.m_encoding); - info.AddValue("bits", this.bits); - info.AddValue("bitCount", this.bitCount); - info.AddValue("firstByte", this.firstByte); - } - public override void Reset() { this.bits = 0; this.bitCount = -1; this.firstByte = false; - if (m_fallbackBuffer != null) - m_fallbackBuffer.Reset(); + if (_fallbackBuffer != null) + _fallbackBuffer.Reset(); } // Anything left in our encoder? @@ -874,10 +817,9 @@ namespace System.Text } } - [Serializable] // Of all the amazing things... This MUST be Encoder so that our com name // for System.Text.Encoder doesn't change - private sealed class Encoder : EncoderNLS, ISerializable + private sealed class Encoder : EncoderNLS { /*private*/ internal int bits; @@ -889,38 +831,12 @@ namespace System.Text // base calls reset } - // Constructor called by serialization, have to handle deserializing from Everett - internal Encoder(SerializationInfo info, StreamingContext context) - { - // Any info? - if (info == null) throw new ArgumentNullException(nameof(info)); - Contract.EndContractBlock(); - - // Get common info - this.bits = (int)info.GetValue("bits", typeof(int)); - this.bitCount = (int)info.GetValue("bitCount", typeof(int)); - this.m_encoding = (Encoding)info.GetValue("encoding", typeof(Encoding)); - } - - // ISerializable implementation, get data for this object - void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) - { - // Any info? - if (info == null) throw new ArgumentNullException(nameof(info)); - Contract.EndContractBlock(); - - // Save Whidbey data - info.AddValue("encoding", this.m_encoding); - info.AddValue("bits", this.bits); - info.AddValue("bitCount", this.bitCount); - } - public override void Reset() { this.bitCount = -1; this.bits = 0; - if (m_fallbackBuffer != null) - m_fallbackBuffer.Reset(); + if (_fallbackBuffer != null) + _fallbackBuffer.Reset(); } // Anything left in our encoder? @@ -935,7 +851,6 @@ namespace System.Text // Preexisting UTF7 behavior for bad bytes was just to spit out the byte as the next char // and turn off base64 mode if it was in that mode. We still exit the mode, but now we fallback. - [Serializable] private sealed class DecoderUTF7Fallback : DecoderFallback { // Construction. Default replacement fallback uses no best fit and ? replacement string diff --git a/external/corefx/src/Common/src/CoreLib/System/Text/UTF8Encoding.cs.REMOVED.git-id b/external/corefx/src/Common/src/CoreLib/System/Text/UTF8Encoding.cs.REMOVED.git-id new file mode 100644 index 0000000000..6176a63d8f --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Text/UTF8Encoding.cs.REMOVED.git-id @@ -0,0 +1 @@ +a89af8edb0b2b357812747b41f60144b3881dee9 \ No newline at end of file diff --git a/external/corefx/src/Common/src/CoreLib/System/Text/UnicodeEncoding.cs b/external/corefx/src/Common/src/CoreLib/System/Text/UnicodeEncoding.cs new file mode 100644 index 0000000000..342bf53d62 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Text/UnicodeEncoding.cs @@ -0,0 +1,1985 @@ +// 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. + +// +// Don't override IsAlwaysNormalized because it is just a Unicode Transformation and could be confused. +// + +using System; +using System.Globalization; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Text +{ + public class UnicodeEncoding : Encoding + { + // Used by Encoding.BigEndianUnicode/Unicode for lazy initialization + // The initialization code will not be run until a static member of the class is referenced + internal static readonly UnicodeEncoding s_bigEndianDefault = new UnicodeEncoding(bigEndian: true, byteOrderMark: true); + internal static readonly UnicodeEncoding s_littleEndianDefault = new UnicodeEncoding(bigEndian: false, byteOrderMark: true); + + private static readonly byte[] s_bigEndianPreamble = new byte[2] { 0xfe, 0xff }; + private static readonly byte[] s_littleEndianPreamble = new byte[2] { 0xff, 0xfe }; + + internal bool isThrowException = false; + + internal bool bigEndian = false; + internal bool byteOrderMark = true; + + // Unicode version 2.0 character size in bytes + public const int CharSize = 2; + + + public UnicodeEncoding() + : this(false, true) + { + } + + + public UnicodeEncoding(bool bigEndian, bool byteOrderMark) + : this(bigEndian, byteOrderMark, false) + { + } + + + public UnicodeEncoding(bool bigEndian, bool byteOrderMark, bool throwOnInvalidBytes) + : base(bigEndian ? 1201 : 1200) //Set the data item. + { + this.isThrowException = throwOnInvalidBytes; + this.bigEndian = bigEndian; + this.byteOrderMark = byteOrderMark; + + // Encoding constructor already did this, but it'll be wrong if we're throwing exceptions + if (this.isThrowException) + SetDefaultFallbacks(); + } + + internal override void SetDefaultFallbacks() + { + // For UTF-X encodings, we use a replacement fallback with an empty string + if (this.isThrowException) + { + this.encoderFallback = EncoderFallback.ExceptionFallback; + this.decoderFallback = DecoderFallback.ExceptionFallback; + } + else + { + this.encoderFallback = new EncoderReplacementFallback("\xFFFD"); + this.decoderFallback = new DecoderReplacementFallback("\xFFFD"); + } + } + + // The following methods are copied from EncodingNLS.cs. + // Unfortunately EncodingNLS.cs is internal and we're public, so we have to re-implement them here. + // These should be kept in sync for the following classes: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // + + // Returns the number of bytes required to encode a range of characters in + // a character array. + // + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe int GetByteCount(char[] chars, int index, int count) + { + // Validate input parameters + if (chars == null) + throw new ArgumentNullException("chars", SR.ArgumentNull_Array); + + if (index < 0 || count < 0) + throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (chars.Length - index < count) + throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); + + // If no input, return 0, avoid fixed empty array problem + if (count == 0) + return 0; + + // Just call the pointer version + fixed (char* pChars = chars) + return GetByteCount(pChars + index, count, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe int GetByteCount(String s) + { + // Validate input + if (s==null) + throw new ArgumentNullException("s"); + + fixed (char* pChars = s) + return GetByteCount(pChars, s.Length, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + [CLSCompliant(false)] + public override unsafe int GetByteCount(char* chars, int count) + { + // Validate Parameters + if (chars == null) + throw new ArgumentNullException("chars", SR.ArgumentNull_Array); + + if (count < 0) + throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); + + // Call it with empty encoder + return GetByteCount(chars, count, null); + } + + // Parent method is safe. + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + public override unsafe int GetBytes(String s, int charIndex, int charCount, + byte[] bytes, int byteIndex) + { + if (s == null || bytes == null) + throw new ArgumentNullException((s == null ? "s" : "bytes"), SR.ArgumentNull_Array); + + if (charIndex < 0 || charCount < 0) + throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (s.Length - charIndex < charCount) + throw new ArgumentOutOfRangeException("s", SR.ArgumentOutOfRange_IndexCount); + + if (byteIndex < 0 || byteIndex > bytes.Length) + throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); + + int byteCount = bytes.Length - byteIndex; + + fixed (char* pChars = s) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) + return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null); + } + + // Encodes a range of characters in a character array into a range of bytes + // in a byte array. An exception occurs if the byte array is not large + // enough to hold the complete encoding of the characters. The + // GetByteCount method can be used to determine the exact number of + // bytes that will be produced for a given range of characters. + // Alternatively, the GetMaxByteCount method can be used to + // determine the maximum number of bytes that will be produced for a given + // number of characters, regardless of the actual character values. + // + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe int GetBytes(char[] chars, int charIndex, int charCount, + byte[] bytes, int byteIndex) + { + // Validate parameters + if (chars == null || bytes == null) + throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array); + + if (charIndex < 0 || charCount < 0) + throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (chars.Length - charIndex < charCount) + throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); + + if (byteIndex < 0 || byteIndex > bytes.Length) + throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); + + // If nothing to encode return 0, avoid fixed problem + if (charCount == 0) + return 0; + + // Just call pointer version + int byteCount = bytes.Length - byteIndex; + + fixed (char* pChars = chars) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) + // Remember that byteCount is # to decode, not size of array. + return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + [CLSCompliant(false)] + public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount) + { + // Validate Parameters + if (bytes == null || chars == null) + throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + + if (charCount < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + return GetBytes(chars, charCount, bytes, byteCount, null); + } + + // Returns the number of characters produced by decoding a range of bytes + // in a byte array. + // + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe int GetCharCount(byte[] bytes, int index, int count) + { + // Validate Parameters + if (bytes == null) + throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + + if (index < 0 || count < 0) + throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (bytes.Length - index < count) + throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + + // If no input just return 0, fixed doesn't like 0 length arrays + if (count == 0) + return 0; + + // Just call pointer version + fixed (byte* pBytes = bytes) + return GetCharCount(pBytes + index, count, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + [CLSCompliant(false)] + public override unsafe int GetCharCount(byte* bytes, int count) + { + // Validate Parameters + if (bytes == null) + throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + + if (count < 0) + throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); + + return GetCharCount(bytes, count, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount, + char[] chars, int charIndex) + { + // Validate Parameters + if (bytes == null || chars == null) + throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + + if (byteIndex < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if ( bytes.Length - byteIndex < byteCount) + throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + + if (charIndex < 0 || charIndex > chars.Length) + throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index); + + // If no input, return 0 & avoid fixed problem + if (byteCount == 0) + return 0; + + // Just call pointer version + int charCount = chars.Length - charIndex; + + fixed (byte* pBytes = bytes) fixed (char* pChars = &MemoryMarshal.GetReference((Span)chars)) + // Remember that charCount is # to decode, not size of array + return GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + [CLSCompliant(false)] + public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount) + { + // Validate Parameters + if (bytes == null || chars == null) + throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + + if (charCount < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + return GetChars(bytes, byteCount, chars, charCount, null); + } + + // Returns a string containing the decoded representation of a range of + // bytes in a byte array. + // + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe string GetString(byte[] bytes, int index, int count) + { + // Validate Parameters + if (bytes == null) + throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + + if (index < 0 || count < 0) + throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (bytes.Length - index < count) + throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + + // Avoid problems with empty input buffer + if (count == 0) return String.Empty; + + fixed (byte* pBytes = bytes) + return String.CreateStringFromEncoding( + pBytes + index, count, this); + } + + // + // End of standard methods copied from EncodingNLS.cs + // + + internal override unsafe int GetByteCount(char* chars, int count, EncoderNLS encoder) + { + Debug.Assert(chars != null, "[UnicodeEncoding.GetByteCount]chars!=null"); + Debug.Assert(count >= 0, "[UnicodeEncoding.GetByteCount]count >=0"); + + // Start by assuming each char gets 2 bytes + int byteCount = count << 1; + + // Check for overflow in byteCount + // (If they were all invalid chars, this would actually be wrong, + // but that's a ridiculously large # so we're not concerned about that case) + if (byteCount < 0) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_GetByteCountOverflow); + + char* charStart = chars; + char* charEnd = chars + count; + char charLeftOver = (char)0; + + bool wasHereBefore = false; + + // Need -1 to check 2 at a time. If we have an even #, longChars will go + // from longEnd - 1/2 long to longEnd + 1/2 long. If we're odd, longChars + // will go from longEnd - 1 long to longEnd. (Might not get to use this) + ulong* longEnd = (ulong*)(charEnd - 3); + + // For fallback we may need a fallback buffer + EncoderFallbackBuffer fallbackBuffer = null; + char* charsForFallback; + + if (encoder != null) + { + charLeftOver = encoder._charLeftOver; + + // Assume extra bytes to encode charLeftOver if it existed + if (charLeftOver > 0) + byteCount += 2; + + // We mustn't have left over fallback data when counting + if (encoder.InternalHasFallbackBuffer) + { + fallbackBuffer = encoder.FallbackBuffer; + if (fallbackBuffer.Remaining > 0) + throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, encoder.Fallback.GetType())); + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false); + } + } + + char ch; + TryAgain: + + while (((ch = (fallbackBuffer == null) ? (char)0 : fallbackBuffer.InternalGetNextChar()) != 0) || chars < charEnd) + { + // First unwind any fallback + if (ch == 0) + { + // No fallback, maybe we can do it fast +#if !NO_FAST_UNICODE_LOOP +#if BIGENDIAN // If endianess is backwards then each pair of bytes would be backwards. + if ( bigEndian && +#else + if (!bigEndian && +#endif // BIGENDIAN + +#if BIT64 // 64 bit CPU needs to be long aligned for this to work. + charLeftOver == 0 && (unchecked((long)chars) & 7) == 0) +#else + charLeftOver == 0 && (unchecked((int)chars) & 3) == 0) +#endif + { + // Need new char* so we can check 4 at a time + ulong* longChars = (ulong*)chars; + + while (longChars < longEnd) + { + // See if we potentially have surrogates (0x8000 bit set) + // (We're either big endian on a big endian machine or little endian on + // a little endian machine so that'll work) + if ((0x8000800080008000 & *longChars) != 0) + { + // See if any of these are high or low surrogates (0xd800 - 0xdfff). If the high + // 5 bits looks like 11011, then its a high or low surrogate. + // We do the & f800 to filter the 5 bits, then ^ d800 to ensure the 0 isn't set. + // Note that we expect BMP characters to be more common than surrogates + // & each char with 11111... then ^ with 11011. Zeroes then indicate surrogates + ulong uTemp = (0xf800f800f800f800 & *longChars) ^ 0xd800d800d800d800; + + // Check each of the 4 chars. 0 for those 16 bits means it was a surrogate + // but no clue if they're high or low. + // If each of the 4 characters are non-zero, then none are surrogates. + if ((uTemp & 0xFFFF000000000000) == 0 || + (uTemp & 0x0000FFFF00000000) == 0 || + (uTemp & 0x00000000FFFF0000) == 0 || + (uTemp & 0x000000000000FFFF) == 0) + { + // It has at least 1 surrogate, but we don't know if they're high or low surrogates, + // or if there's 1 or 4 surrogates + + // If they happen to be high/low/high/low, we may as well continue. Check the next + // bit to see if its set (low) or not (high) in the right pattern +#if BIGENDIAN + if (((0xfc00fc00fc00fc00 & *longChars) ^ 0xd800dc00d800dc00) != 0) +#else + if (((0xfc00fc00fc00fc00 & *longChars) ^ 0xdc00d800dc00d800) != 0) +#endif + { + // Either there weren't 4 surrogates, or the 0x0400 bit was set when a high + // was hoped for or the 0x0400 bit wasn't set where a low was hoped for. + + // Drop out to the slow loop to resolve the surrogates + break; + } + // else they are all surrogates in High/Low/High/Low order, so we can use them. + } + // else none are surrogates, so we can use them. + } + // else all < 0x8000 so we can use them + + // We already counted these four chars, go to next long. + longChars++; + } + + chars = (char*)longChars; + + if (chars >= charEnd) + break; + } +#endif // !NO_FAST_UNICODE_LOOP + + // No fallback, just get next char + ch = *chars; + chars++; + } + else + { + // We weren't preallocating fallback space. + byteCount += 2; + } + + // Check for high or low surrogates + if (ch >= 0xd800 && ch <= 0xdfff) + { + // Was it a high surrogate? + if (ch <= 0xdbff) + { + // Its a high surrogate, if we already had a high surrogate do its fallback + if (charLeftOver > 0) + { + // Unwind the current character, this should be safe because we + // don't have leftover data in the fallback, so chars must have + // advanced already. + Debug.Assert(chars > charStart, + "[UnicodeEncoding.GetByteCount]Expected chars to have advanced in unexpected high surrogate"); + chars--; + + // If previous high surrogate deallocate 2 bytes + byteCount -= 2; + + // Fallback the previous surrogate + // Need to initialize fallback buffer? + if (fallbackBuffer == null) + { + if (encoder == null) + fallbackBuffer = this.encoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = encoder.FallbackBuffer; + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false); + } + + charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered + fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback); + chars = charsForFallback; + + // Now no high surrogate left over + charLeftOver = (char)0; + continue; + } + + // Remember this high surrogate + charLeftOver = ch; + continue; + } + + + // Its a low surrogate + if (charLeftOver == 0) + { + // Expected a previous high surrogate. + // Don't count this one (we'll count its fallback if necessary) + byteCount -= 2; + + // fallback this one + // Need to initialize fallback buffer? + if (fallbackBuffer == null) + { + if (encoder == null) + fallbackBuffer = this.encoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = encoder.FallbackBuffer; + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false); + } + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered + fallbackBuffer.InternalFallback(ch, ref charsForFallback); + chars = charsForFallback; + continue; + } + + // Valid surrogate pair, add our charLeftOver + charLeftOver = (char)0; + continue; + } + else if (charLeftOver > 0) + { + // Expected a low surrogate, but this char is normal + + // Rewind the current character, fallback previous character. + // this should be safe because we don't have leftover data in the + // fallback, so chars must have advanced already. + Debug.Assert(chars > charStart, + "[UnicodeEncoding.GetByteCount]Expected chars to have advanced when expected low surrogate"); + chars--; + + // fallback previous chars + // Need to initialize fallback buffer? + if (fallbackBuffer == null) + { + if (encoder == null) + fallbackBuffer = this.encoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = encoder.FallbackBuffer; + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false); + } + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered + fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback); + chars = charsForFallback; + + // Ignore charLeftOver or throw + byteCount -= 2; + charLeftOver = (char)0; + + continue; + } + + // Ok we had something to add (already counted) + } + + // Don't allocate space for left over char + if (charLeftOver > 0) + { + byteCount -= 2; + + // If we have to flush, stick it in fallback and try again + if (encoder == null || encoder.MustFlush) + { + if (wasHereBefore) + { + // Throw it, using our complete character + throw new ArgumentException( + SR.Format(SR.Argument_RecursiveFallback, charLeftOver), nameof(chars)); + } + else + { + // Need to initialize fallback buffer? + if (fallbackBuffer == null) + { + if (encoder == null) + fallbackBuffer = this.encoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = encoder.FallbackBuffer; + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false); + } + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered + fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback); + chars = charsForFallback; + charLeftOver = (char)0; + wasHereBefore = true; + goto TryAgain; + } + } + } + + // Shouldn't have anything in fallback buffer for GetByteCount + // (don't have to check _throwOnOverflow for count) + Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0, + "[UnicodeEncoding.GetByteCount]Expected empty fallback buffer at end"); + + // Don't remember fallbackBuffer.encoder for counting + return byteCount; + } + + internal override unsafe int GetBytes(char* chars, int charCount, + byte* bytes, int byteCount, EncoderNLS encoder) + { + Debug.Assert(chars != null, "[UnicodeEncoding.GetBytes]chars!=null"); + Debug.Assert(byteCount >= 0, "[UnicodeEncoding.GetBytes]byteCount >=0"); + Debug.Assert(charCount >= 0, "[UnicodeEncoding.GetBytes]charCount >=0"); + Debug.Assert(bytes != null, "[UnicodeEncoding.GetBytes]bytes!=null"); + + char charLeftOver = (char)0; + char ch; + bool wasHereBefore = false; + + + byte* byteEnd = bytes + byteCount; + char* charEnd = chars + charCount; + byte* byteStart = bytes; + char* charStart = chars; + + // For fallback we may need a fallback buffer + EncoderFallbackBuffer fallbackBuffer = null; + char* charsForFallback; + + // Get our encoder, but don't clear it yet. + if (encoder != null) + { + charLeftOver = encoder._charLeftOver; + + // We mustn't have left over fallback data when counting + if (encoder.InternalHasFallbackBuffer) + { + // We always need the fallback buffer in get bytes so we can flush any remaining ones if necessary + fallbackBuffer = encoder.FallbackBuffer; + if (fallbackBuffer.Remaining > 0 && encoder._throwOnOverflow) + throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, encoder.Fallback.GetType())); + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false); + } + } + + TryAgain: + while (((ch = (fallbackBuffer == null) ? + (char)0 : fallbackBuffer.InternalGetNextChar()) != 0) || + chars < charEnd) + { + // First unwind any fallback + if (ch == 0) + { + // No fallback, maybe we can do it fast +#if !NO_FAST_UNICODE_LOOP +#if BIGENDIAN // If endianess is backwards then each pair of bytes would be backwards. + if ( bigEndian && +#else + if (!bigEndian && +#endif // BIGENDIAN +#if BIT64 // 64 bit CPU needs to be long aligned for this to work, 32 bit CPU needs to be 32 bit aligned + (unchecked((long)chars) & 7) == 0 && (unchecked((long)bytes) & 7) == 0 && +#else + (unchecked((int)chars) & 3) == 0 && (unchecked((int)bytes) & 3) == 0 && +#endif // BIT64 + charLeftOver == 0) + { + // Need -1 to check 2 at a time. If we have an even #, longChars will go + // from longEnd - 1/2 long to longEnd + 1/2 long. If we're odd, longChars + // will go from longEnd - 1 long to longEnd. (Might not get to use this) + // We can only go iCount units (limited by shorter of char or byte buffers. + ulong* longEnd = (ulong*)(chars - 3 + + (((byteEnd - bytes) >> 1 < charEnd - chars) ? + (byteEnd - bytes) >> 1 : charEnd - chars)); + + // Need new char* so we can check 4 at a time + ulong* longChars = (ulong*)chars; + ulong* longBytes = (ulong*)bytes; + + while (longChars < longEnd) + { + // See if we potentially have surrogates (0x8000 bit set) + // (We're either big endian on a big endian machine or little endian on + // a little endian machine so that'll work) + if ((0x8000800080008000 & *longChars) != 0) + { + // See if any of these are high or low surrogates (0xd800 - 0xdfff). If the high + // 5 bits looks like 11011, then its a high or low surrogate. + // We do the & f800 to filter the 5 bits, then ^ d800 to ensure the 0 isn't set. + // Note that we expect BMP characters to be more common than surrogates + // & each char with 11111... then ^ with 11011. Zeroes then indicate surrogates + ulong uTemp = (0xf800f800f800f800 & *longChars) ^ 0xd800d800d800d800; + + // Check each of the 4 chars. 0 for those 16 bits means it was a surrogate + // but no clue if they're high or low. + // If each of the 4 characters are non-zero, then none are surrogates. + if ((uTemp & 0xFFFF000000000000) == 0 || + (uTemp & 0x0000FFFF00000000) == 0 || + (uTemp & 0x00000000FFFF0000) == 0 || + (uTemp & 0x000000000000FFFF) == 0) + { + // It has at least 1 surrogate, but we don't know if they're high or low surrogates, + // or if there's 1 or 4 surrogates + + // If they happen to be high/low/high/low, we may as well continue. Check the next + // bit to see if its set (low) or not (high) in the right pattern +#if BIGENDIAN + if (((0xfc00fc00fc00fc00 & *longChars) ^ 0xd800dc00d800dc00) != 0) +#else + if (((0xfc00fc00fc00fc00 & *longChars) ^ 0xdc00d800dc00d800) != 0) +#endif + { + // Either there weren't 4 surrogates, or the 0x0400 bit was set when a high + // was hoped for or the 0x0400 bit wasn't set where a low was hoped for. + + // Drop out to the slow loop to resolve the surrogates + break; + } + // else they are all surrogates in High/Low/High/Low order, so we can use them. + } + // else none are surrogates, so we can use them. + } + // else all < 0x8000 so we can use them + + // We can use these 4 chars. + *longBytes = *longChars; + longChars++; + longBytes++; + } + + chars = (char*)longChars; + bytes = (byte*)longBytes; + + if (chars >= charEnd) + break; + } + // Not aligned, but maybe we can still be somewhat faster + // Also somehow this optimizes the above loop? It seems to cause something above + // to get enregistered, but I haven't figured out how to make that happen without this loop. + else if ((charLeftOver == 0) && +#if BIGENDIAN + bigEndian && +#else + !bigEndian && +#endif // BIGENDIAN + +#if BIT64 + (unchecked((long)chars) & 7) != (unchecked((long)bytes) & 7) && // Only do this if chars & bytes are out of line, otherwise faster loop will be faster next time +#else + (unchecked((int)chars) & 3) != (unchecked((int)bytes) & 3) && // Only do this if chars & bytes are out of line, otherwise faster loop will be faster next time +#endif // BIT64 + (unchecked((int)(bytes)) & 1) == 0) + { + // # to use + long iCount = ((byteEnd - bytes) >> 1 < charEnd - chars) ? + (byteEnd - bytes) >> 1 : charEnd - chars; + + // Need new char* + char* charOut = ((char*)bytes); // a char* for our output + char* tempEnd = chars + iCount - 1; // Our end pointer + + while (chars < tempEnd) + { + if (*chars >= (char)0xd800 && *chars <= (char)0xdfff) + { + // break for fallback for low surrogate + if (*chars >= 0xdc00) + break; + + // break if next one's not a low surrogate (will do fallback) + if (*(chars + 1) < 0xdc00 || *(chars + 1) > 0xdfff) + break; + + // They both exist, use them + } + // If 2nd char is surrogate & this one isn't then only add one + else if (*(chars + 1) >= (char)0xd800 && *(chars + 1) <= 0xdfff) + { + *charOut = *chars; + charOut++; + chars++; + continue; + } + + *charOut = *chars; + *(charOut + 1) = *(chars + 1); + charOut += 2; + chars += 2; + } + + bytes = (byte*)charOut; + + if (chars >= charEnd) + break; + } +#endif // !NO_FAST_UNICODE_LOOP + + // No fallback, just get next char + ch = *chars; + chars++; + } + + // Check for high or low surrogates + if (ch >= 0xd800 && ch <= 0xdfff) + { + // Was it a high surrogate? + if (ch <= 0xdbff) + { + // Its a high surrogate, see if we already had a high surrogate + if (charLeftOver > 0) + { + // Unwind the current character, this should be safe because we + // don't have leftover data in the fallback, so chars must have + // advanced already. + Debug.Assert(chars > charStart, + "[UnicodeEncoding.GetBytes]Expected chars to have advanced in unexpected high surrogate"); + chars--; + + // Fallback the previous surrogate + // Might need to create our fallback buffer + if (fallbackBuffer == null) + { + if (encoder == null) + fallbackBuffer = this.encoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = encoder.FallbackBuffer; + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true); + } + + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered + fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback); + chars = charsForFallback; + + charLeftOver = (char)0; + continue; + } + + // Remember this high surrogate + charLeftOver = ch; + continue; + } + + // Its a low surrogate + if (charLeftOver == 0) + { + // We'll fall back this one + // Might need to create our fallback buffer + if (fallbackBuffer == null) + { + if (encoder == null) + fallbackBuffer = this.encoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = encoder.FallbackBuffer; + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true); + } + + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered + fallbackBuffer.InternalFallback(ch, ref charsForFallback); + chars = charsForFallback; + continue; + } + + // Valid surrogate pair, add our charLeftOver + if (bytes + 3 >= byteEnd) + { + // Not enough room to add this surrogate pair + if (fallbackBuffer != null && fallbackBuffer.bFallingBack) + { + // These must have both been from the fallbacks. + // Both of these MUST have been from a fallback because if the 1st wasn't + // from a fallback, then a high surrogate followed by an illegal char + // would've caused the high surrogate to fall back. If a high surrogate + // fell back, then it was consumed and both chars came from the fallback. + fallbackBuffer.MovePrevious(); // Didn't use either fallback surrogate + fallbackBuffer.MovePrevious(); + } + else + { + // If we don't have enough room, then either we should've advanced a while + // or we should have bytes==byteStart and throw below + Debug.Assert(chars > charStart + 1 || bytes == byteStart, + "[UnicodeEncoding.GetBytes]Expected chars to have when no room to add surrogate pair"); + chars -= 2; // Didn't use either surrogate + } + ThrowBytesOverflow(encoder, bytes == byteStart); // Throw maybe (if no bytes written) + charLeftOver = (char)0; // we'll retry it later + break; // Didn't throw, but stop 'til next time. + } + + if (bigEndian) + { + *(bytes++) = (byte)(charLeftOver >> 8); + *(bytes++) = (byte)charLeftOver; + } + else + { + *(bytes++) = (byte)charLeftOver; + *(bytes++) = (byte)(charLeftOver >> 8); + } + + charLeftOver = (char)0; + } + else if (charLeftOver > 0) + { + // Expected a low surrogate, but this char is normal + + // Rewind the current character, fallback previous character. + // this should be safe because we don't have leftover data in the + // fallback, so chars must have advanced already. + Debug.Assert(chars > charStart, + "[UnicodeEncoding.GetBytes]Expected chars to have advanced after expecting low surrogate"); + chars--; + + // fallback previous chars + // Might need to create our fallback buffer + if (fallbackBuffer == null) + { + if (encoder == null) + fallbackBuffer = this.encoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = encoder.FallbackBuffer; + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true); + } + + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered + fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback); + chars = charsForFallback; + + // Ignore charLeftOver or throw + charLeftOver = (char)0; + continue; + } + + // Ok, we have a char to add + if (bytes + 1 >= byteEnd) + { + // Couldn't add this char + if (fallbackBuffer != null && fallbackBuffer.bFallingBack) + fallbackBuffer.MovePrevious(); // Not using this fallback char + else + { + // Lonely charLeftOver (from previous call) would've been caught up above, + // so this must be a case where we've already read an input char. + Debug.Assert(chars > charStart, + "[UnicodeEncoding.GetBytes]Expected chars to have advanced for failed fallback"); + chars--; // Not using this char + } + ThrowBytesOverflow(encoder, bytes == byteStart); // Throw maybe (if no bytes written) + break; // didn't throw, just stop + } + + if (bigEndian) + { + *(bytes++) = (byte)(ch >> 8); + *(bytes++) = (byte)ch; + } + else + { + *(bytes++) = (byte)ch; + *(bytes++) = (byte)(ch >> 8); + } + } + + // Don't allocate space for left over char + if (charLeftOver > 0) + { + // If we aren't flushing we need to fall this back + if (encoder == null || encoder.MustFlush) + { + if (wasHereBefore) + { + // Throw it, using our complete character + throw new ArgumentException( + SR.Format(SR.Argument_RecursiveFallback, charLeftOver), nameof(chars)); + } + else + { + // If we have to flush, stick it in fallback and try again + // Might need to create our fallback buffer + if (fallbackBuffer == null) + { + if (encoder == null) + fallbackBuffer = this.encoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = encoder.FallbackBuffer; + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true); + } + + // If we're not flushing, that'll remember the left over character. + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered + fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback); + chars = charsForFallback; + + charLeftOver = (char)0; + wasHereBefore = true; + goto TryAgain; + } + } + } + + // Not flushing, remember it in the encoder + if (encoder != null) + { + encoder._charLeftOver = charLeftOver; + encoder._charsUsed = (int)(chars - charStart); + } + + // Remember charLeftOver if we must, or clear it if we're flushing + // (charLeftOver should be 0 if we're flushing) + Debug.Assert((encoder != null && !encoder.MustFlush) || charLeftOver == (char)0, + "[UnicodeEncoding.GetBytes] Expected no left over characters if flushing"); + + Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0 || + encoder == null || !encoder._throwOnOverflow, + "[UnicodeEncoding.GetBytes]Expected empty fallback buffer if not converting"); + + // We used to copy it fast, but this doesn't check for surrogates + // System.IO.__UnmanagedMemoryStream.memcpyimpl(bytes, (byte*)chars, usedByteCount); + + return (int)(bytes - byteStart); + } + + internal override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder) + { + Debug.Assert(bytes != null, "[UnicodeEncoding.GetCharCount]bytes!=null"); + Debug.Assert(count >= 0, "[UnicodeEncoding.GetCharCount]count >=0"); + + UnicodeEncoding.Decoder decoder = (UnicodeEncoding.Decoder)baseDecoder; + + byte* byteEnd = bytes + count; + byte* byteStart = bytes; + + // Need last vars + int lastByte = -1; + char lastChar = (char)0; + + // Start by assuming same # of chars as bytes + int charCount = count >> 1; + + // Need -1 to check 2 at a time. If we have an even #, longBytes will go + // from longEnd - 1/2 long to longEnd + 1/2 long. If we're odd, longBytes + // will go from longEnd - 1 long to longEnd. (Might not get to use this) + ulong* longEnd = (ulong*)(byteEnd - 7); + + // For fallback we may need a fallback buffer + DecoderFallbackBuffer fallbackBuffer = null; + + if (decoder != null) + { + lastByte = decoder.lastByte; + lastChar = decoder.lastChar; + + // Assume extra char if last char was around + if (lastChar > 0) + charCount++; + + // Assume extra char if extra last byte makes up odd # of input bytes + if (lastByte >= 0 && (count & 1) == 1) + { + charCount++; + } + + // Shouldn't have anything in fallback buffer for GetCharCount + // (don't have to check _throwOnOverflow for count) + Debug.Assert(!decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0, + "[UnicodeEncoding.GetCharCount]Expected empty fallback buffer at start"); + } + + while (bytes < byteEnd) + { + // If we're aligned then maybe we can do it fast + // That'll hurt if we're unaligned because we'll always test but never be aligned +#if !NO_FAST_UNICODE_LOOP +#if BIGENDIAN + if (bigEndian && +#else // BIGENDIAN + if (!bigEndian && +#endif // BIGENDIAN +#if BIT64 // win64 has to be long aligned + (unchecked((long)bytes) & 7) == 0 && +#else + (unchecked((int)bytes) & 3) == 0 && +#endif // BIT64 + lastByte == -1 && lastChar == 0) + { + // Need new char* so we can check 4 at a time + ulong* longBytes = (ulong*)bytes; + + while (longBytes < longEnd) + { + // See if we potentially have surrogates (0x8000 bit set) + // (We're either big endian on a big endian machine or little endian on + // a little endian machine so that'll work) + if ((0x8000800080008000 & *longBytes) != 0) + { + // See if any of these are high or low surrogates (0xd800 - 0xdfff). If the high + // 5 bits looks like 11011, then its a high or low surrogate. + // We do the & f800 to filter the 5 bits, then ^ d800 to ensure the 0 isn't set. + // Note that we expect BMP characters to be more common than surrogates + // & each char with 11111... then ^ with 11011. Zeroes then indicate surrogates + ulong uTemp = (0xf800f800f800f800 & *longBytes) ^ 0xd800d800d800d800; + + // Check each of the 4 chars. 0 for those 16 bits means it was a surrogate + // but no clue if they're high or low. + // If each of the 4 characters are non-zero, then none are surrogates. + if ((uTemp & 0xFFFF000000000000) == 0 || + (uTemp & 0x0000FFFF00000000) == 0 || + (uTemp & 0x00000000FFFF0000) == 0 || + (uTemp & 0x000000000000FFFF) == 0) + { + // It has at least 1 surrogate, but we don't know if they're high or low surrogates, + // or if there's 1 or 4 surrogates + + // If they happen to be high/low/high/low, we may as well continue. Check the next + // bit to see if its set (low) or not (high) in the right pattern +#if BIGENDIAN + if (((0xfc00fc00fc00fc00 & *longBytes) ^ 0xd800dc00d800dc00) != 0) +#else + if (((0xfc00fc00fc00fc00 & *longBytes) ^ 0xdc00d800dc00d800) != 0) +#endif + { + // Either there weren't 4 surrogates, or the 0x0400 bit was set when a high + // was hoped for or the 0x0400 bit wasn't set where a low was hoped for. + + // Drop out to the slow loop to resolve the surrogates + break; + } + // else they are all surrogates in High/Low/High/Low order, so we can use them. + } + // else none are surrogates, so we can use them. + } + // else all < 0x8000 so we can use them + + // We can use these 4 chars. + longBytes++; + } + + bytes = (byte*)longBytes; + + if (bytes >= byteEnd) + break; + } +#endif // !NO_FAST_UNICODE_LOOP + + // Get 1st byte + if (lastByte < 0) + { + lastByte = *bytes++; + if (bytes >= byteEnd) break; + } + + // Get full char + char ch; + if (bigEndian) + { + ch = (char)(lastByte << 8 | *(bytes++)); + } + else + { + ch = (char)(*(bytes++) << 8 | lastByte); + } + lastByte = -1; + + // See if the char's valid + if (ch >= 0xd800 && ch <= 0xdfff) + { + // Was it a high surrogate? + if (ch <= 0xdbff) + { + // Its a high surrogate, if we had one then do fallback for previous one + if (lastChar > 0) + { + // Ignore previous bad high surrogate + charCount--; + + // Get fallback for previous high surrogate + // Note we have to reconstruct bytes because some may have been in decoder + byte[] byteBuffer = null; + if (bigEndian) + { + byteBuffer = new byte[] + { unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar) }; + } + else + { + byteBuffer = new byte[] + { unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8)) }; + } + + if (fallbackBuffer == null) + { + if (decoder == null) + fallbackBuffer = this.decoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = decoder.FallbackBuffer; + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(byteStart, null); + } + + // Get fallback. + charCount += fallbackBuffer.InternalFallback(byteBuffer, bytes); + } + + // Ignore the last one which fell back already, + // and remember the new high surrogate + lastChar = ch; + continue; + } + + // Its a low surrogate + if (lastChar == 0) + { + // Expected a previous high surrogate + charCount--; + + // Get fallback for this low surrogate + // Note we have to reconstruct bytes because some may have been in decoder + byte[] byteBuffer = null; + if (bigEndian) + { + byteBuffer = new byte[] + { unchecked((byte)(ch >> 8)), unchecked((byte)ch) }; + } + else + { + byteBuffer = new byte[] + { unchecked((byte)ch), unchecked((byte)(ch >> 8)) }; + } + + if (fallbackBuffer == null) + { + if (decoder == null) + fallbackBuffer = this.decoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = decoder.FallbackBuffer; + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(byteStart, null); + } + + charCount += fallbackBuffer.InternalFallback(byteBuffer, bytes); + + // Ignore this one (we already did its fallback) + continue; + } + + // Valid surrogate pair, already counted. + lastChar = (char)0; + } + else if (lastChar > 0) + { + // Had a high surrogate, expected a low surrogate + // Un-count the last high surrogate + charCount--; + + // fall back the high surrogate. + byte[] byteBuffer = null; + if (bigEndian) + { + byteBuffer = new byte[] + { unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar) }; + } + else + { + byteBuffer = new byte[] + { unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8)) }; + } + + if (fallbackBuffer == null) + { + if (decoder == null) + fallbackBuffer = this.decoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = decoder.FallbackBuffer; + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(byteStart, null); + } + + // Already subtracted high surrogate + charCount += fallbackBuffer.InternalFallback(byteBuffer, bytes); + + // Not left over now, clear previous high surrogate and continue to add current char + lastChar = (char)0; + } + + // Valid char, already counted + } + + // Extra space if we can't use decoder + if (decoder == null || decoder.MustFlush) + { + if (lastChar > 0) + { + // No hanging high surrogates allowed, do fallback and remove count for it + charCount--; + byte[] byteBuffer = null; + if (bigEndian) + { + byteBuffer = new byte[] + { unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar) }; + } + else + { + byteBuffer = new byte[] + { unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8)) }; + } + + if (fallbackBuffer == null) + { + if (decoder == null) + fallbackBuffer = this.decoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = decoder.FallbackBuffer; + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(byteStart, null); + } + + charCount += fallbackBuffer.InternalFallback(byteBuffer, bytes); + + lastChar = (char)0; + } + + if (lastByte >= 0) + { + if (fallbackBuffer == null) + { + if (decoder == null) + fallbackBuffer = this.decoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = decoder.FallbackBuffer; + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(byteStart, null); + } + + // No hanging odd bytes allowed if must flush + charCount += fallbackBuffer.InternalFallback(new byte[] { unchecked((byte)lastByte) }, bytes); + lastByte = -1; + } + } + + // If we had a high surrogate left over, we can't count it + if (lastChar > 0) + charCount--; + + // Shouldn't have anything in fallback buffer for GetCharCount + // (don't have to check _throwOnOverflow for count) + Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0, + "[UnicodeEncoding.GetCharCount]Expected empty fallback buffer at end"); + + return charCount; + } + + internal override unsafe int GetChars(byte* bytes, int byteCount, + char* chars, int charCount, DecoderNLS baseDecoder) + { + Debug.Assert(chars != null, "[UnicodeEncoding.GetChars]chars!=null"); + Debug.Assert(byteCount >= 0, "[UnicodeEncoding.GetChars]byteCount >=0"); + Debug.Assert(charCount >= 0, "[UnicodeEncoding.GetChars]charCount >=0"); + Debug.Assert(bytes != null, "[UnicodeEncoding.GetChars]bytes!=null"); + + UnicodeEncoding.Decoder decoder = (UnicodeEncoding.Decoder)baseDecoder; + + // Need last vars + int lastByte = -1; + char lastChar = (char)0; + + // Get our decoder (but don't clear it yet) + if (decoder != null) + { + lastByte = decoder.lastByte; + lastChar = decoder.lastChar; + + // Shouldn't have anything in fallback buffer for GetChars + // (don't have to check _throwOnOverflow for chars) + Debug.Assert(!decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0, + "[UnicodeEncoding.GetChars]Expected empty fallback buffer at start"); + } + + // For fallback we may need a fallback buffer + DecoderFallbackBuffer fallbackBuffer = null; + char* charsForFallback; + + byte* byteEnd = bytes + byteCount; + char* charEnd = chars + charCount; + byte* byteStart = bytes; + char* charStart = chars; + + while (bytes < byteEnd) + { + // If we're aligned then maybe we can do it fast + // That'll hurt if we're unaligned because we'll always test but never be aligned +#if !NO_FAST_UNICODE_LOOP +#if BIGENDIAN + if (bigEndian && +#else // BIGENDIAN + if (!bigEndian && +#endif // BIGENDIAN +#if BIT64 // win64 has to be long aligned + (unchecked((long)chars) & 7) == 0 && (unchecked((long)bytes) & 7) == 0 && +#else + (unchecked((int)chars) & 3) == 0 && (unchecked((int)bytes) & 3) == 0 && +#endif // BIT64 + lastByte == -1 && lastChar == 0) + { + // Need -1 to check 2 at a time. If we have an even #, longChars will go + // from longEnd - 1/2 long to longEnd + 1/2 long. If we're odd, longChars + // will go from longEnd - 1 long to longEnd. (Might not get to use this) + // We can only go iCount units (limited by shorter of char or byte buffers. + ulong* longEnd = (ulong*)(bytes - 7 + + (((byteEnd - bytes) >> 1 < charEnd - chars) ? + (byteEnd - bytes) : (charEnd - chars) << 1)); + + // Need new char* so we can check 4 at a time + ulong* longBytes = (ulong*)bytes; + ulong* longChars = (ulong*)chars; + + while (longBytes < longEnd) + { + // See if we potentially have surrogates (0x8000 bit set) + // (We're either big endian on a big endian machine or little endian on + // a little endian machine so that'll work) + if ((0x8000800080008000 & *longBytes) != 0) + { + // See if any of these are high or low surrogates (0xd800 - 0xdfff). If the high + // 5 bits looks like 11011, then its a high or low surrogate. + // We do the & f800 to filter the 5 bits, then ^ d800 to ensure the 0 isn't set. + // Note that we expect BMP characters to be more common than surrogates + // & each char with 11111... then ^ with 11011. Zeroes then indicate surrogates + ulong uTemp = (0xf800f800f800f800 & *longBytes) ^ 0xd800d800d800d800; + + // Check each of the 4 chars. 0 for those 16 bits means it was a surrogate + // but no clue if they're high or low. + // If each of the 4 characters are non-zero, then none are surrogates. + if ((uTemp & 0xFFFF000000000000) == 0 || + (uTemp & 0x0000FFFF00000000) == 0 || + (uTemp & 0x00000000FFFF0000) == 0 || + (uTemp & 0x000000000000FFFF) == 0) + { + // It has at least 1 surrogate, but we don't know if they're high or low surrogates, + // or if there's 1 or 4 surrogates + + // If they happen to be high/low/high/low, we may as well continue. Check the next + // bit to see if its set (low) or not (high) in the right pattern +#if BIGENDIAN + if (((0xfc00fc00fc00fc00 & *longBytes) ^ 0xd800dc00d800dc00) != 0) +#else + if (((0xfc00fc00fc00fc00 & *longBytes) ^ 0xdc00d800dc00d800) != 0) +#endif + { + // Either there weren't 4 surrogates, or the 0x0400 bit was set when a high + // was hoped for or the 0x0400 bit wasn't set where a low was hoped for. + + // Drop out to the slow loop to resolve the surrogates + break; + } + // else they are all surrogates in High/Low/High/Low order, so we can use them. + } + // else none are surrogates, so we can use them. + } + // else all < 0x8000 so we can use them + + // We can use these 4 chars. + *longChars = *longBytes; + longBytes++; + longChars++; + } + + chars = (char*)longChars; + bytes = (byte*)longBytes; + + if (bytes >= byteEnd) + break; + } +#endif // !NO_FAST_UNICODE_LOOP + + // Get 1st byte + if (lastByte < 0) + { + lastByte = *bytes++; + continue; + } + + // Get full char + char ch; + if (bigEndian) + { + ch = (char)(lastByte << 8 | *(bytes++)); + } + else + { + ch = (char)(*(bytes++) << 8 | lastByte); + } + lastByte = -1; + + // See if the char's valid + if (ch >= 0xd800 && ch <= 0xdfff) + { + // Was it a high surrogate? + if (ch <= 0xdbff) + { + // Its a high surrogate, if we had one then do fallback for previous one + if (lastChar > 0) + { + // Get fallback for previous high surrogate + // Note we have to reconstruct bytes because some may have been in decoder + byte[] byteBuffer = null; + if (bigEndian) + { + byteBuffer = new byte[] + { unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar) }; + } + else + { + byteBuffer = new byte[] + { unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8)) }; + } + + if (fallbackBuffer == null) + { + if (decoder == null) + fallbackBuffer = this.decoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = decoder.FallbackBuffer; + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(byteStart, charEnd); + } + + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered + bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallback); + chars = charsForFallback; + + if (!fallbackResult) + { + // couldn't fall back lonely surrogate + // We either advanced bytes or chars should == charStart and throw below + Debug.Assert(bytes >= byteStart + 2 || chars == charStart, + "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (bad surrogate)"); + bytes -= 2; // didn't use these 2 bytes + fallbackBuffer.InternalReset(); + ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output + break; // couldn't fallback but didn't throw + } + } + + // Ignore the previous high surrogate which fell back already, + // yet remember the current high surrogate for next time. + lastChar = ch; + continue; + } + + // Its a low surrogate + if (lastChar == 0) + { + // Expected a previous high surrogate + // Get fallback for this low surrogate + // Note we have to reconstruct bytes because some may have been in decoder + byte[] byteBuffer = null; + if (bigEndian) + { + byteBuffer = new byte[] + { unchecked((byte)(ch >> 8)), unchecked((byte)ch) }; + } + else + { + byteBuffer = new byte[] + { unchecked((byte)ch), unchecked((byte)(ch >> 8)) }; + } + + if (fallbackBuffer == null) + { + if (decoder == null) + fallbackBuffer = this.decoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = decoder.FallbackBuffer; + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(byteStart, charEnd); + } + + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered + bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallback); + chars = charsForFallback; + + if (!fallbackResult) + { + // couldn't fall back lonely surrogate + // We either advanced bytes or chars should == charStart and throw below + Debug.Assert(bytes >= byteStart + 2 || chars == charStart, + "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (lonely surrogate)"); + bytes -= 2; // didn't use these 2 bytes + fallbackBuffer.InternalReset(); + ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output + break; // couldn't fallback but didn't throw + } + + // Didn't throw, ignore this one (we already did its fallback) + continue; + } + + // Valid surrogate pair, add our lastChar (will need 2 chars) + if (chars >= charEnd - 1) + { + // couldn't find room for this surrogate pair + // We either advanced bytes or chars should == charStart and throw below + Debug.Assert(bytes >= byteStart + 2 || chars == charStart, + "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (surrogate pair)"); + bytes -= 2; // didn't use these 2 bytes + ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output + // Leave lastChar for next call to Convert() + break; // couldn't fallback but didn't throw + } + + *chars++ = lastChar; + lastChar = (char)0; + } + else if (lastChar > 0) + { + // Had a high surrogate, expected a low surrogate, fall back the high surrogate. + byte[] byteBuffer = null; + if (bigEndian) + { + byteBuffer = new byte[] + { unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar) }; + } + else + { + byteBuffer = new byte[] + { unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8)) }; + } + + if (fallbackBuffer == null) + { + if (decoder == null) + fallbackBuffer = this.decoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = decoder.FallbackBuffer; + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(byteStart, charEnd); + } + + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered + bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallback); + chars = charsForFallback; + + if (!fallbackResult) + { + // couldn't fall back high surrogate, or char that would be next + // We either advanced bytes or chars should == charStart and throw below + Debug.Assert(bytes >= byteStart + 2 || chars == charStart, + "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (no low surrogate)"); + bytes -= 2; // didn't use these 2 bytes + fallbackBuffer.InternalReset(); + ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output + break; // couldn't fallback but didn't throw + } + + // Not left over now, clear previous high surrogate and continue to add current char + lastChar = (char)0; + } + + // Valid char, room for it? + if (chars >= charEnd) + { + // 2 bytes couldn't fall back + // We either advanced bytes or chars should == charStart and throw below + Debug.Assert(bytes >= byteStart + 2 || chars == charStart, + "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (normal)"); + bytes -= 2; // didn't use these bytes + ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output + break; // couldn't fallback but didn't throw + } + + // add it + *chars++ = ch; + } + + // Remember our decoder if we must + if (decoder == null || decoder.MustFlush) + { + if (lastChar > 0) + { + // No hanging high surrogates allowed, do fallback and remove count for it + byte[] byteBuffer = null; + if (bigEndian) + { + byteBuffer = new byte[] + { unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar) }; + } + else + { + byteBuffer = new byte[] + { unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8)) }; + } + + if (fallbackBuffer == null) + { + if (decoder == null) + fallbackBuffer = this.decoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = decoder.FallbackBuffer; + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(byteStart, charEnd); + } + + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered + bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallback); + chars = charsForFallback; + + if (!fallbackResult) + { + // 2 bytes couldn't fall back + // We either advanced bytes or chars should == charStart and throw below + Debug.Assert(bytes >= byteStart + 2 || chars == charStart, + "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (decoder)"); + bytes -= 2; // didn't use these bytes + if (lastByte >= 0) + bytes--; // had an extra last byte hanging around + fallbackBuffer.InternalReset(); + ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output + // We'll remember these in our decoder though + bytes += 2; + if (lastByte >= 0) + bytes++; + goto End; + } + + // done with this one + lastChar = (char)0; + } + + if (lastByte >= 0) + { + if (fallbackBuffer == null) + { + if (decoder == null) + fallbackBuffer = this.decoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = decoder.FallbackBuffer; + + // Set our internal fallback interesting things. + fallbackBuffer.InternalInitialize(byteStart, charEnd); + } + + // No hanging odd bytes allowed if must flush + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered + bool fallbackResult = fallbackBuffer.InternalFallback(new byte[] { unchecked((byte)lastByte) }, bytes, ref charsForFallback); + chars = charsForFallback; + + if (!fallbackResult) + { + // odd byte couldn't fall back + bytes--; // didn't use this byte + fallbackBuffer.InternalReset(); + ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output + // didn't throw, but we'll remember it in the decoder + bytes++; + goto End; + } + + // Didn't fail, clear buffer + lastByte = -1; + } + } + + End: + + // Remember our decoder if we must + if (decoder != null) + { + Debug.Assert((decoder.MustFlush == false) || ((lastChar == (char)0) && (lastByte == -1)), + "[UnicodeEncoding.GetChars] Expected no left over chars or bytes if flushing" + // + " " + ((int)lastChar).ToString("X4") + " " + lastByte.ToString("X2") + ); + + decoder._bytesUsed = (int)(bytes - byteStart); + decoder.lastChar = lastChar; + decoder.lastByte = lastByte; + } + + // Used to do this the old way + // System.IO.__UnmanagedMemoryStream.memcpyimpl((byte*)chars, bytes, byteCount); + + // Shouldn't have anything in fallback buffer for GetChars + // (don't have to check _throwOnOverflow for count or chars) + Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0, + "[UnicodeEncoding.GetChars]Expected empty fallback buffer at end"); + + return (int)(chars - charStart); + } + + + public override System.Text.Encoder GetEncoder() + { + return new EncoderNLS(this); + } + + + public override System.Text.Decoder GetDecoder() + { + return new UnicodeEncoding.Decoder(this); + } + + + public override byte[] GetPreamble() + { + if (byteOrderMark) + { + // Note - we must allocate new byte[]'s here to prevent someone + // from modifying a cached byte[]. + if (bigEndian) + return new byte[2] { 0xfe, 0xff }; + else + return new byte[2] { 0xff, 0xfe }; + } + return Array.Empty(); + } + + public override ReadOnlySpan Preamble => + GetType() != typeof(UnicodeEncoding) ? GetPreamble() : // in case a derived UnicodeEncoding overrode GetPreamble + byteOrderMark ? (bigEndian ? s_bigEndianPreamble : s_littleEndianPreamble) : + Array.Empty(); + + public override int GetMaxByteCount(int charCount) + { + if (charCount < 0) + throw new ArgumentOutOfRangeException(nameof(charCount), + SR.ArgumentOutOfRange_NeedNonNegNum); + + // Characters would be # of characters + 1 in case left over high surrogate is ? * max fallback + long byteCount = (long)charCount + 1; + + if (EncoderFallback.MaxCharCount > 1) + byteCount *= EncoderFallback.MaxCharCount; + + // 2 bytes per char + byteCount <<= 1; + + if (byteCount > 0x7fffffff) + throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_GetByteCountOverflow); + + return (int)byteCount; + } + + + public override int GetMaxCharCount(int byteCount) + { + if (byteCount < 0) + throw new ArgumentOutOfRangeException(nameof(byteCount), + SR.ArgumentOutOfRange_NeedNonNegNum); + + // long because byteCount could be biggest int. + // 1 char per 2 bytes. Round up in case 1 left over in decoder. + // Round up using &1 in case byteCount is max size + // Might also need an extra 1 if there's a left over high surrogate in the decoder. + long charCount = (long)(byteCount >> 1) + (byteCount & 1) + 1; + + // Don't forget fallback (in case they have a bunch of lonely surrogates or something bizarre like that) + if (DecoderFallback.MaxCharCount > 1) + charCount *= DecoderFallback.MaxCharCount; + + if (charCount > 0x7fffffff) + throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_GetCharCountOverflow); + + return (int)charCount; + } + + + public override bool Equals(Object value) + { + UnicodeEncoding that = value as UnicodeEncoding; + if (that != null) + { + // + // Big Endian Unicode has different code page (1201) than small Endian one (1200), + // so we still have to check _codePage here. + // + return (CodePage == that.CodePage) && + byteOrderMark == that.byteOrderMark && + // isThrowException == that.isThrowException && // Same as Encoder/Decoder being exception fallbacks + bigEndian == that.bigEndian && + (EncoderFallback.Equals(that.EncoderFallback)) && + (DecoderFallback.Equals(that.DecoderFallback)); + } + return (false); + } + + public override int GetHashCode() + { + return CodePage + this.EncoderFallback.GetHashCode() + this.DecoderFallback.GetHashCode() + + (byteOrderMark ? 4 : 0) + (bigEndian ? 8 : 0); + } + + private sealed class Decoder : System.Text.DecoderNLS + { + internal int lastByte = -1; + internal char lastChar = '\0'; + + public Decoder(UnicodeEncoding encoding) : base(encoding) + { + // base calls reset + } + + public override void Reset() + { + lastByte = -1; + lastChar = '\0'; + if (_fallbackBuffer != null) + _fallbackBuffer.Reset(); + } + + // Anything left in our decoder? + internal override bool HasState + { + get + { + return (this.lastByte != -1 || this.lastChar != '\0'); + } + } + } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Text/ValueStringBuilder.cs b/external/corefx/src/Common/src/CoreLib/System/Text/ValueStringBuilder.cs new file mode 100644 index 0000000000..0a91eb0f63 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Text/ValueStringBuilder.cs @@ -0,0 +1,185 @@ +// 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.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace System.Text +{ + internal ref struct ValueStringBuilder + { + private char[] _arrayToReturnToPool; + private Span _chars; + private int _pos; + + public ValueStringBuilder(Span initialBuffer) + { + _arrayToReturnToPool = null; + _chars = initialBuffer; + _pos = 0; + } + + public int Length => _pos; + + public override string ToString() + { + var s = new string(_chars.Slice(0, _pos)); + Clear(); + return s; + } + + public bool TryCopyTo(Span destination, out int charsWritten) + { + if (_chars.Slice(0, _pos).TryCopyTo(destination)) + { + charsWritten = _pos; + Clear(); + return true; + } + else + { + charsWritten = 0; + Clear(); + return false; + } + } + + public void Insert(int index, char value, int count) + { + if (_pos > _chars.Length - count) + { + Grow(count); + } + + int remaining = _pos - index; + _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count)); + _chars.Slice(index, count).Fill(value); + _pos += count; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(char c) + { + int pos = _pos; + if (pos < _chars.Length) + { + _chars[pos] = c; + _pos = pos + 1; + } + else + { + GrowAndAppend(c); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(string s) + { + int pos = _pos; + if (s.Length == 1 && pos < _chars.Length) // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc. + { + _chars[pos] = s[0]; + _pos = pos + 1; + } + else + { + AppendSlow(s); + } + } + + private void AppendSlow(string s) + { + int pos = _pos; + if (pos > _chars.Length - s.Length) + { + Grow(s.Length); + } + + bool copied = s.AsReadOnlySpan().TryCopyTo(_chars.Slice(pos)); + Debug.Assert(copied, "Grow should have made enough room to successfully copy"); + _pos += s.Length; + } + + public void Append(char c, int count) + { + if (_pos > _chars.Length - count) + { + Grow(count); + } + + Span dst = _chars.Slice(_pos, count); + for (int i = 0; i < dst.Length; i++) + { + dst[i] = c; + } + _pos += count; + } + + public unsafe void Append(char* value, int length) + { + int pos = _pos; + if (pos > _chars.Length - length) + { + Grow(length); + } + + Span dst = _chars.Slice(_pos, length); + for (int i = 0; i < dst.Length; i++) + { + dst[i] = *value++; + } + _pos += length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span AppendSpan(int length) + { + int origPos = _pos; + if (origPos > _chars.Length - length) + { + Grow(length); + } + + _pos = origPos + length; + return _chars.Slice(origPos, length); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void GrowAndAppend(char c) + { + Grow(1); + Append(c); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void Grow(int requiredAdditionalCapacity) + { + Debug.Assert(requiredAdditionalCapacity > _chars.Length - _pos); + + char[] poolArray = ArrayPool.Shared.Rent(Math.Max(_pos + requiredAdditionalCapacity, _chars.Length * 2)); + + bool success = _chars.TryCopyTo(poolArray); + Debug.Assert(success); + + char[] toReturn = _arrayToReturnToPool; + _chars = _arrayToReturnToPool = poolArray; + if (toReturn != null) + { + ArrayPool.Shared.Return(toReturn); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Clear() + { + char[] toReturn = _arrayToReturnToPool; + this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again + if (toReturn != null) + { + ArrayPool.Shared.Return(toReturn); + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/ThreadAttributes.cs b/external/corefx/src/Common/src/CoreLib/System/ThreadAttributes.cs new file mode 100644 index 0000000000..6248736107 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ThreadAttributes.cs @@ -0,0 +1,28 @@ +// 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. + +/*============================================================================= +** +** Purpose: For Threads-related custom attributes. +** +=============================================================================*/ + +namespace System +{ + [AttributeUsage(AttributeTargets.Method)] + public sealed class STAThreadAttribute : Attribute + { + public STAThreadAttribute() + { + } + } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class MTAThreadAttribute : Attribute + { + public MTAThreadAttribute() + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/ThreadStaticAttribute.cs b/external/corefx/src/Common/src/CoreLib/System/ThreadStaticAttribute.cs new file mode 100644 index 0000000000..c12ac1c18d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ThreadStaticAttribute.cs @@ -0,0 +1,27 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: Custom attribute to indicate that the field should be treated +** as a static relative to a thread. +** +** +** +===========================================================*/ + +using System; + +namespace System +{ + [AttributeUsage(AttributeTargets.Field, Inherited = false)] + public class ThreadStaticAttribute : Attribute + { + public ThreadStaticAttribute() + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/AbandonedMutexException.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/AbandonedMutexException.cs new file mode 100644 index 0000000000..c7e604f33d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/AbandonedMutexException.cs @@ -0,0 +1,78 @@ +// 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. +// +// AbandonedMutexException +// Thrown when a wait completes because one or more mutexes was abandoned. +// AbandonedMutexs indicate serious error in user code or machine state. +//////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Threading; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace System.Threading +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class AbandonedMutexException : SystemException + { + private int _mutexIndex = -1; + private Mutex _mutex = null; + + public AbandonedMutexException() + : base(SR.Threading_AbandonedMutexException) + { + HResult = HResults.COR_E_ABANDONEDMUTEX; + } + + public AbandonedMutexException(String message) + : base(message) + { + HResult = HResults.COR_E_ABANDONEDMUTEX; + } + + public AbandonedMutexException(String message, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_ABANDONEDMUTEX; + } + + public AbandonedMutexException(int location, WaitHandle handle) + : base(SR.Threading_AbandonedMutexException) + { + HResult = HResults.COR_E_ABANDONEDMUTEX; + SetupException(location, handle); + } + + public AbandonedMutexException(String message, int location, WaitHandle handle) + : base(message) + { + HResult = HResults.COR_E_ABANDONEDMUTEX; + SetupException(location, handle); + } + + public AbandonedMutexException(String message, Exception inner, int location, WaitHandle handle) + : base(message, inner) + { + HResult = HResults.COR_E_ABANDONEDMUTEX; + SetupException(location, handle); + } + + protected AbandonedMutexException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + private void SetupException(int location, WaitHandle handle) + { + _mutexIndex = location; + if (handle != null) + _mutex = handle as Mutex; + } + + public Mutex Mutex => _mutex; + public int MutexIndex => _mutexIndex; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/ApartmentState.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/ApartmentState.cs new file mode 100644 index 0000000000..47c1677cb5 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/ApartmentState.cs @@ -0,0 +1,16 @@ +// 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. + +namespace System.Threading +{ + public enum ApartmentState + { + /*========================================================================= + ** Constants for thread apartment states. + =========================================================================*/ + STA = 0, + MTA = 1, + Unknown = 2 + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/AsyncLocal.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/AsyncLocal.cs new file mode 100644 index 0000000000..59c8fb3c88 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/AsyncLocal.cs @@ -0,0 +1,484 @@ +// 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.Collections.Generic; +using System.Diagnostics; + +namespace System.Threading +{ + // + // AsyncLocal represents "ambient" data that is local to a given asynchronous control flow, such as an + // async method. For example, say you want to associate a culture with a given async flow: + // + // static AsyncLocal s_currentCulture = new AsyncLocal(); + // + // static async Task SomeOperationAsync(Culture culture) + // { + // s_currentCulture.Value = culture; + // + // await FooAsync(); + // } + // + // static async Task FooAsync() + // { + // PrintStringWithCulture(s_currentCulture.Value); + // } + // + // AsyncLocal also provides optional notifications when the value associated with the current thread + // changes, either because it was explicitly changed by setting the Value property, or implicitly changed + // when the thread encountered an "await" or other context transition. For example, we might want our + // current culture to be communicated to the OS as well: + // + // static AsyncLocal s_currentCulture = new AsyncLocal( + // args => + // { + // NativeMethods.SetThreadCulture(args.CurrentValue.LCID); + // }); + // + public sealed class AsyncLocal : IAsyncLocal + { + private readonly Action> m_valueChangedHandler; + + // + // Constructs an AsyncLocal that does not receive change notifications. + // + public AsyncLocal() + { + } + + // + // Constructs an AsyncLocal with a delegate that is called whenever the current value changes + // on any thread. + // + public AsyncLocal(Action> valueChangedHandler) + { + m_valueChangedHandler = valueChangedHandler; + } + + public T Value + { + get + { + object obj = ExecutionContext.GetLocalValue(this); + return (obj == null) ? default(T) : (T)obj; + } + set + { + ExecutionContext.SetLocalValue(this, value, m_valueChangedHandler != null); + } + } + + void IAsyncLocal.OnValueChanged(object previousValueObj, object currentValueObj, bool contextChanged) + { + Debug.Assert(m_valueChangedHandler != null); + T previousValue = previousValueObj == null ? default(T) : (T)previousValueObj; + T currentValue = currentValueObj == null ? default(T) : (T)currentValueObj; + m_valueChangedHandler(new AsyncLocalValueChangedArgs(previousValue, currentValue, contextChanged)); + } + } + + // + // Interface to allow non-generic code in ExecutionContext to call into the generic AsyncLocal type. + // + internal interface IAsyncLocal + { + void OnValueChanged(object previousValue, object currentValue, bool contextChanged); + } + + public struct AsyncLocalValueChangedArgs + { + public T PreviousValue { get; private set; } + public T CurrentValue { get; private set; } + + // + // If the value changed because we changed to a different ExecutionContext, this is true. If it changed + // because someone set the Value property, this is false. + // + public bool ThreadContextChanged { get; private set; } + + internal AsyncLocalValueChangedArgs(T previousValue, T currentValue, bool contextChanged) + : this() + { + PreviousValue = previousValue; + CurrentValue = currentValue; + ThreadContextChanged = contextChanged; + } + } + + // + // Interface used to store an IAsyncLocal => object mapping in ExecutionContext. + // Implementations are specialized based on the number of elements in the immutable + // map in order to minimize memory consumption and look-up times. + // + internal interface IAsyncLocalValueMap + { + bool TryGetValue(IAsyncLocal key, out object value); + IAsyncLocalValueMap Set(IAsyncLocal key, object value); + } + + // + // Utility functions for getting/creating instances of IAsyncLocalValueMap + // + internal static class AsyncLocalValueMap + { + public static IAsyncLocalValueMap Empty { get; } = new EmptyAsyncLocalValueMap(); + + // Instance without any key/value pairs. Used as a singleton/ + private sealed class EmptyAsyncLocalValueMap : IAsyncLocalValueMap + { + public IAsyncLocalValueMap Set(IAsyncLocal key, object value) + { + // If the value isn't null, then create a new one-element map to store + // the key/value pair. If it is null, then we're still empty. + return value != null ? + new OneElementAsyncLocalValueMap(key, value) : + (IAsyncLocalValueMap)this; + } + + public bool TryGetValue(IAsyncLocal key, out object value) + { + value = null; + return false; + } + } + + // Instance with one key/value pair. + private sealed class OneElementAsyncLocalValueMap : IAsyncLocalValueMap + { + private readonly IAsyncLocal _key1; + private readonly object _value1; + + public OneElementAsyncLocalValueMap(IAsyncLocal key, object value) + { + _key1 = key; _value1 = value; + } + + public IAsyncLocalValueMap Set(IAsyncLocal key, object value) + { + if (value != null) + { + // The value is non-null. If the key matches one already contained in this map, + // then create a new one-element map with the updated value, otherwise create + // a two-element map with the additional key/value. + return ReferenceEquals(key, _key1) ? + new OneElementAsyncLocalValueMap(key, value) : + (IAsyncLocalValueMap)new TwoElementAsyncLocalValueMap(_key1, _value1, key, value); + } + else + { + // The value is null. If the key exists in this map, remove it by downgrading to an empty map. + // Otherwise, there's nothing to add or remove, so just return this map. + return ReferenceEquals(key, _key1) ? + Empty : + (IAsyncLocalValueMap)this; + } + } + + public bool TryGetValue(IAsyncLocal key, out object value) + { + if (ReferenceEquals(key, _key1)) + { + value = _value1; + return true; + } + else + { + value = null; + return false; + } + } + } + + // Instance with two key/value pairs. + private sealed class TwoElementAsyncLocalValueMap : IAsyncLocalValueMap + { + private readonly IAsyncLocal _key1, _key2; + private readonly object _value1, _value2; + + public TwoElementAsyncLocalValueMap(IAsyncLocal key1, object value1, IAsyncLocal key2, object value2) + { + _key1 = key1; _value1 = value1; + _key2 = key2; _value2 = value2; + } + + public IAsyncLocalValueMap Set(IAsyncLocal key, object value) + { + if (value != null) + { + // The value is non-null. If the key matches one already contained in this map, + // then create a new two-element map with the updated value, otherwise create + // a three-element map with the additional key/value. + return + ReferenceEquals(key, _key1) ? new TwoElementAsyncLocalValueMap(key, value, _key2, _value2) : + ReferenceEquals(key, _key2) ? new TwoElementAsyncLocalValueMap(_key1, _value1, key, value) : + (IAsyncLocalValueMap)new ThreeElementAsyncLocalValueMap(_key1, _value1, _key2, _value2, key, value); + } + else + { + // The value is null. If the key exists in this map, remove it by downgrading to a one-element map + // without the key. Otherwise, there's nothing to add or remove, so just return this map. + return + ReferenceEquals(key, _key1) ? new OneElementAsyncLocalValueMap(_key2, _value2) : + ReferenceEquals(key, _key2) ? new OneElementAsyncLocalValueMap(_key1, _value1) : + (IAsyncLocalValueMap)this; + } + } + + public bool TryGetValue(IAsyncLocal key, out object value) + { + if (ReferenceEquals(key, _key1)) + { + value = _value1; + return true; + } + else if (ReferenceEquals(key, _key2)) + { + value = _value2; + return true; + } + else + { + value = null; + return false; + } + } + } + + // Instance with three key/value pairs. + private sealed class ThreeElementAsyncLocalValueMap : IAsyncLocalValueMap + { + private readonly IAsyncLocal _key1, _key2, _key3; + private readonly object _value1, _value2, _value3; + + public ThreeElementAsyncLocalValueMap(IAsyncLocal key1, object value1, IAsyncLocal key2, object value2, IAsyncLocal key3, object value3) + { + _key1 = key1; _value1 = value1; + _key2 = key2; _value2 = value2; + _key3 = key3; _value3 = value3; + } + + public IAsyncLocalValueMap Set(IAsyncLocal key, object value) + { + if (value != null) + { + // The value is non-null. If the key matches one already contained in this map, + // then create a new three-element map with the updated value. + if (ReferenceEquals(key, _key1)) return new ThreeElementAsyncLocalValueMap(key, value, _key2, _value2, _key3, _value3); + if (ReferenceEquals(key, _key2)) return new ThreeElementAsyncLocalValueMap(_key1, _value1, key, value, _key3, _value3); + if (ReferenceEquals(key, _key3)) return new ThreeElementAsyncLocalValueMap(_key1, _value1, _key2, _value2, key, value); + + // The key doesn't exist in this map, so upgrade to a multi map that contains + // the additional key/value pair. + var multi = new MultiElementAsyncLocalValueMap(4); + multi.UnsafeStore(0, _key1, _value1); + multi.UnsafeStore(1, _key2, _value2); + multi.UnsafeStore(2, _key3, _value3); + multi.UnsafeStore(3, key, value); + return multi; + } + else + { + // The value is null. If the key exists in this map, remove it by downgrading to a two-element map + // without the key. Otherwise, there's nothing to add or remove, so just return this map. + return + ReferenceEquals(key, _key1) ? new TwoElementAsyncLocalValueMap(_key2, _value2, _key3, _value3) : + ReferenceEquals(key, _key2) ? new TwoElementAsyncLocalValueMap(_key1, _value1, _key3, _value3) : + ReferenceEquals(key, _key3) ? new TwoElementAsyncLocalValueMap(_key1, _value1, _key2, _value2) : + (IAsyncLocalValueMap)this; + } + } + + public bool TryGetValue(IAsyncLocal key, out object value) + { + if (ReferenceEquals(key, _key1)) + { + value = _value1; + return true; + } + else if (ReferenceEquals(key, _key2)) + { + value = _value2; + return true; + } + else if (ReferenceEquals(key, _key3)) + { + value = _value3; + return true; + } + else + { + value = null; + return false; + } + } + } + + // Instance with up to 16 key/value pairs. + private sealed class MultiElementAsyncLocalValueMap : IAsyncLocalValueMap + { + internal const int MaxMultiElements = 16; + private readonly KeyValuePair[] _keyValues; + + internal MultiElementAsyncLocalValueMap(int count) + { + Debug.Assert(count <= MaxMultiElements); + _keyValues = new KeyValuePair[count]; + } + + internal void UnsafeStore(int index, IAsyncLocal key, object value) + { + Debug.Assert(index < _keyValues.Length); + _keyValues[index] = new KeyValuePair(key, value); + } + + public IAsyncLocalValueMap Set(IAsyncLocal key, object value) + { + // Find the key in this map. + for (int i = 0; i < _keyValues.Length; i++) + { + if (ReferenceEquals(key, _keyValues[i].Key)) + { + // The key is in the map. If the value isn't null, then create a new map of the same + // size that has all of the same pairs, with this new key/value pair overwriting the old. + if (value != null) + { + var multi = new MultiElementAsyncLocalValueMap(_keyValues.Length); + Array.Copy(_keyValues, 0, multi._keyValues, 0, _keyValues.Length); + multi._keyValues[i] = new KeyValuePair(key, value); + return multi; + } + else if (_keyValues.Length == 4) + { + // The value is null, and we only have four elements, one of which we're removing, + // so downgrade to a three-element map, without the matching element. + return + i == 0 ? new ThreeElementAsyncLocalValueMap(_keyValues[1].Key, _keyValues[1].Value, _keyValues[2].Key, _keyValues[2].Value, _keyValues[3].Key, _keyValues[3].Value) : + i == 1 ? new ThreeElementAsyncLocalValueMap(_keyValues[0].Key, _keyValues[0].Value, _keyValues[2].Key, _keyValues[2].Value, _keyValues[3].Key, _keyValues[3].Value) : + i == 2 ? new ThreeElementAsyncLocalValueMap(_keyValues[0].Key, _keyValues[0].Value, _keyValues[1].Key, _keyValues[1].Value, _keyValues[3].Key, _keyValues[3].Value) : + (IAsyncLocalValueMap)new ThreeElementAsyncLocalValueMap(_keyValues[0].Key, _keyValues[0].Value, _keyValues[1].Key, _keyValues[1].Value, _keyValues[2].Key, _keyValues[2].Value); + } + else + { + // The value is null, and we have enough elements remaining to warrant a multi map. + // Create a new one and copy all of the elements from this one, except the one to be removed. + var multi = new MultiElementAsyncLocalValueMap(_keyValues.Length - 1); + if (i != 0) Array.Copy(_keyValues, 0, multi._keyValues, 0, i); + if (i != _keyValues.Length - 1) Array.Copy(_keyValues, i + 1, multi._keyValues, i, _keyValues.Length - i - 1); + return multi; + } + } + } + + // The key does not already exist in this map. + + // If the value is null, then we can simply return this same map, as there's nothing to add or remove. + if (value == null) + { + return this; + } + + // We need to create a new map that has the additional key/value pair. + // If with the addition we can still fit in a multi map, create one. + if (_keyValues.Length < MaxMultiElements) + { + var multi = new MultiElementAsyncLocalValueMap(_keyValues.Length + 1); + Array.Copy(_keyValues, 0, multi._keyValues, 0, _keyValues.Length); + multi._keyValues[_keyValues.Length] = new KeyValuePair(key, value); + return multi; + } + + // Otherwise, upgrade to a many map. + var many = new ManyElementAsyncLocalValueMap(MaxMultiElements + 1); + foreach (KeyValuePair pair in _keyValues) + { + many[pair.Key] = pair.Value; + } + many[key] = value; + return many; + } + + public bool TryGetValue(IAsyncLocal key, out object value) + { + foreach (KeyValuePair pair in _keyValues) + { + if (ReferenceEquals(key, pair.Key)) + { + value = pair.Value; + return true; + } + } + value = null; + return false; + } + } + + // Instance with any number of key/value pairs. + private sealed class ManyElementAsyncLocalValueMap : Dictionary, IAsyncLocalValueMap + { + public ManyElementAsyncLocalValueMap(int capacity) : base(capacity) { } + + public IAsyncLocalValueMap Set(IAsyncLocal key, object value) + { + int count = Count; + bool containsKey = ContainsKey(key); + + // If the value being set exists, create a new many map, copy all of the elements from this one, + // and then store the new key/value pair into it. This is the most common case. + if (value != null) + { + var map = new ManyElementAsyncLocalValueMap(count + (containsKey ? 0 : 1)); + foreach (KeyValuePair pair in this) + { + map[pair.Key] = pair.Value; + } + map[key] = value; + return map; + } + + // Otherwise, the value is null, which means null is being stored into an AsyncLocal.Value. + // Since there's no observable difference at the API level between storing null and the key + // not existing at all, we can downgrade to a smaller map rather than storing null. + + // If the key is contained in this map, we're going to create a new map that's one pair smaller. + if (containsKey) + { + // If the new count would be within range of a multi map instead of a many map, + // downgrade to the many map, which uses less memory and is faster to access. + // Otherwise, just create a new many map that's missing this key. + if (count == MultiElementAsyncLocalValueMap.MaxMultiElements + 1) + { + var multi = new MultiElementAsyncLocalValueMap(MultiElementAsyncLocalValueMap.MaxMultiElements); + int index = 0; + foreach (KeyValuePair pair in this) + { + if (!ReferenceEquals(key, pair.Key)) + { + multi.UnsafeStore(index++, pair.Key, pair.Value); + } + } + Debug.Assert(index == MultiElementAsyncLocalValueMap.MaxMultiElements); + return multi; + } + else + { + var map = new ManyElementAsyncLocalValueMap(count - 1); + foreach (KeyValuePair pair in this) + { + if (!ReferenceEquals(key, pair.Key)) + { + map[pair.Key] = pair.Value; + } + } + Debug.Assert(map.Count == count - 1); + return map; + } + } + + // We were storing null, but the key wasn't in the map, so there's nothing to change. + // Just return this instance. + return this; + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/AutoResetEvent.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/AutoResetEvent.cs new file mode 100644 index 0000000000..8320d7ad5a --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/AutoResetEvent.cs @@ -0,0 +1,12 @@ +// 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. + +namespace System.Threading +{ + public sealed class AutoResetEvent : EventWaitHandle + { + public AutoResetEvent(bool initialState) : base(initialState, EventResetMode.AutoReset) { } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/DeferredDisposableLifetime.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/DeferredDisposableLifetime.cs new file mode 100644 index 0000000000..e2b1eb983b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/DeferredDisposableLifetime.cs @@ -0,0 +1,116 @@ +// 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.Diagnostics; + +namespace System.Threading +{ + /// + /// Provides callbacks to objects whose lifetime is managed by . + /// + internal interface IDeferredDisposable + { + /// + /// Called when the object's refcount reaches zero. + /// + /// + /// Indicates whether the object has been disposed. + /// + /// + /// If the refcount reaches zero before the object is disposed, this method will be called with + /// set to false. If the object is then disposed, this method will be + /// called again, with set to true. If the refcount reaches zero + /// after the object has already been disposed, this will be called a single time, with + /// set to true. + /// + void OnFinalRelease(bool disposed); + } + + /// + /// Manages the lifetime of an object which implements IDisposable, but which must defer the actual + /// cleanup of state until all existing uses of the object are complete. + /// + /// The type of object whose lifetime will be managed. + /// + /// This type maintains a reference count, and tracks whether the object has been disposed. When + /// Callbacks are made to when the refcount + /// reaches zero. Objects that need to defer cleanup until they have been disposed *and* they have + /// no more references can do so in when + /// 'disposed' is true. + /// + internal struct DeferredDisposableLifetime where T : class, IDeferredDisposable + { + // + // _count is positive until Dispose is called, after which it's (-1 - refcount). + // + private int _count; + + public bool AddRef(T obj) + { + while (true) + { + int oldCount = Volatile.Read(ref _count); + + // Have we been disposed? + if (oldCount < 0) + throw new ObjectDisposedException(typeof(T).ToString()); + + int newCount = checked(oldCount + 1); + + if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount) + return true; + } + } + + public void Release(T obj) + { + while (true) + { + int oldCount = Volatile.Read(ref _count); + if (oldCount > 0) + { + // We haven't been disposed. Decrement _count. + int newCount = oldCount - 1; + if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount) + { + if (newCount == 0) + obj.OnFinalRelease(disposed: false); + return; + } + } + else + { + Debug.Assert(oldCount != 0 && oldCount != -1); + + // We've been disposed. Increment _count. + int newCount = oldCount + 1; + if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount) + { + if (newCount == -1) + obj.OnFinalRelease(disposed: true); + return; + } + } + } + } + + public void Dispose(T obj) + { + while (true) + { + int oldCount = Volatile.Read(ref _count); + if (oldCount < 0) + return; // already disposed + + int newCount = -1 - oldCount; + if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount) + { + if (newCount == -1) + obj.OnFinalRelease(disposed: true); + return; + } + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/EventResetMode.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/EventResetMode.cs new file mode 100644 index 0000000000..7aac0f51eb --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/EventResetMode.cs @@ -0,0 +1,22 @@ +// 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. + +/*============================================================================= +** +** Enum: EventResetMode +** +** +** Purpose: Enum to determine the Event type to create +** +** +=============================================================================*/ + +namespace System.Threading +{ + public enum EventResetMode + { + AutoReset = 0, + ManualReset = 1 + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/ExecutionContext.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/ExecutionContext.cs new file mode 100644 index 0000000000..2d5f5be190 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/ExecutionContext.cs @@ -0,0 +1,357 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: Capture execution context for a thread +** +** +===========================================================*/ + +using System.Diagnostics; +using System.Runtime.ExceptionServices; +using System.Runtime.Serialization; + +using Thread = Internal.Runtime.Augments.RuntimeThread; + +namespace System.Threading +{ + public delegate void ContextCallback(Object state); + + internal struct ExecutionContextSwitcher + { + internal ExecutionContext m_ec; + internal SynchronizationContext m_sc; + + internal void Undo(Thread currentThread) + { + Debug.Assert(currentThread == Thread.CurrentThread); + + // The common case is that these have not changed, so avoid the cost of a write if not needed. + if (currentThread.SynchronizationContext != m_sc) + { + currentThread.SynchronizationContext = m_sc; + } + + if (currentThread.ExecutionContext != m_ec) + { + ExecutionContext.Restore(currentThread, m_ec); + } + } + } + + public sealed class ExecutionContext : IDisposable, ISerializable + { + internal static readonly ExecutionContext Default = new ExecutionContext(); + + private readonly IAsyncLocalValueMap m_localValues; + private readonly IAsyncLocal[] m_localChangeNotifications; + private readonly bool m_isFlowSuppressed; + + private ExecutionContext() + { + m_localValues = AsyncLocalValueMap.Empty; + m_localChangeNotifications = Array.Empty(); + } + + private ExecutionContext( + IAsyncLocalValueMap localValues, + IAsyncLocal[] localChangeNotifications, + bool isFlowSuppressed) + { + m_localValues = localValues; + m_localChangeNotifications = localChangeNotifications; + m_isFlowSuppressed = isFlowSuppressed; + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + throw new PlatformNotSupportedException(); + } + + public static ExecutionContext Capture() + { + ExecutionContext executionContext = Thread.CurrentThread.ExecutionContext; + return + executionContext == null ? Default : + executionContext.m_isFlowSuppressed ? null : + executionContext; + } + + private ExecutionContext ShallowClone(bool isFlowSuppressed) + { + Debug.Assert(isFlowSuppressed != m_isFlowSuppressed); + + if (!isFlowSuppressed && + m_localValues == Default.m_localValues && + m_localChangeNotifications == Default.m_localChangeNotifications) + { + return null; // implies the default context + } + return new ExecutionContext(m_localValues, m_localChangeNotifications, isFlowSuppressed); + } + + public static AsyncFlowControl SuppressFlow() + { + Thread currentThread = Thread.CurrentThread; + ExecutionContext executionContext = currentThread.ExecutionContext ?? Default; + if (executionContext.m_isFlowSuppressed) + { + throw new InvalidOperationException(SR.InvalidOperation_CannotSupressFlowMultipleTimes); + } + + executionContext = executionContext.ShallowClone(isFlowSuppressed: true); + var asyncFlowControl = new AsyncFlowControl(); + currentThread.ExecutionContext = executionContext; + asyncFlowControl.Initialize(currentThread); + return asyncFlowControl; + } + + public static void RestoreFlow() + { + Thread currentThread = Thread.CurrentThread; + ExecutionContext executionContext = currentThread.ExecutionContext; + if (executionContext == null || !executionContext.m_isFlowSuppressed) + { + throw new InvalidOperationException(SR.InvalidOperation_CannotRestoreUnsupressedFlow); + } + + currentThread.ExecutionContext = executionContext.ShallowClone(isFlowSuppressed: false); + } + + public static bool IsFlowSuppressed() + { + ExecutionContext executionContext = Thread.CurrentThread.ExecutionContext; + return executionContext != null && executionContext.m_isFlowSuppressed; + } + + public static void Run(ExecutionContext executionContext, ContextCallback callback, Object state) + { + if (executionContext == null) + throw new InvalidOperationException(SR.InvalidOperation_NullContext); + + Thread currentThread = Thread.CurrentThread; + ExecutionContextSwitcher ecsw = default(ExecutionContextSwitcher); + try + { + EstablishCopyOnWriteScope(currentThread, ref ecsw); + ExecutionContext.Restore(currentThread, executionContext); + callback(state); + } + catch + { + // Note: we have a "catch" rather than a "finally" because we want + // to stop the first pass of EH here. That way we can restore the previous + // context before any of our callers' EH filters run. That means we need to + // end the scope separately in the non-exceptional case below. + ecsw.Undo(currentThread); + throw; + } + ecsw.Undo(currentThread); + } + + internal static void Restore(Thread currentThread, ExecutionContext executionContext) + { + Debug.Assert(currentThread == Thread.CurrentThread); + + ExecutionContext previous = currentThread.ExecutionContext ?? Default; + currentThread.ExecutionContext = executionContext; + + // New EC could be null if that's what ECS.Undo saved off. + // For the purposes of dealing with context change, treat this as the default EC + executionContext = executionContext ?? Default; + + if (previous != executionContext) + { + OnContextChanged(previous, executionContext); + } + } + + internal static void EstablishCopyOnWriteScope(Thread currentThread, ref ExecutionContextSwitcher ecsw) + { + Debug.Assert(currentThread == Thread.CurrentThread); + + ecsw.m_ec = currentThread.ExecutionContext; + ecsw.m_sc = currentThread.SynchronizationContext; + } + + private static void OnContextChanged(ExecutionContext previous, ExecutionContext current) + { + Debug.Assert(previous != null); + Debug.Assert(current != null); + Debug.Assert(previous != current); + + foreach (IAsyncLocal local in previous.m_localChangeNotifications) + { + object previousValue; + object currentValue; + previous.m_localValues.TryGetValue(local, out previousValue); + current.m_localValues.TryGetValue(local, out currentValue); + + if (previousValue != currentValue) + local.OnValueChanged(previousValue, currentValue, true); + } + + if (current.m_localChangeNotifications != previous.m_localChangeNotifications) + { + try + { + foreach (IAsyncLocal local in current.m_localChangeNotifications) + { + // If the local has a value in the previous context, we already fired the event for that local + // in the code above. + object previousValue; + if (!previous.m_localValues.TryGetValue(local, out previousValue)) + { + object currentValue; + current.m_localValues.TryGetValue(local, out currentValue); + + if (previousValue != currentValue) + local.OnValueChanged(previousValue, currentValue, true); + } + } + } + catch (Exception ex) + { + Environment.FailFast( + SR.ExecutionContext_ExceptionInAsyncLocalNotification, + ex); + } + } + } + + internal static object GetLocalValue(IAsyncLocal local) + { + ExecutionContext current = Thread.CurrentThread.ExecutionContext; + if (current == null) + return null; + + object value; + current.m_localValues.TryGetValue(local, out value); + return value; + } + + internal static void SetLocalValue(IAsyncLocal local, object newValue, bool needChangeNotifications) + { + ExecutionContext current = Thread.CurrentThread.ExecutionContext ?? ExecutionContext.Default; + + object previousValue; + bool hadPreviousValue = current.m_localValues.TryGetValue(local, out previousValue); + + if (previousValue == newValue) + return; + + IAsyncLocalValueMap newValues = current.m_localValues.Set(local, newValue); + + // + // Either copy the change notification array, or create a new one, depending on whether we need to add a new item. + // + IAsyncLocal[] newChangeNotifications = current.m_localChangeNotifications; + if (needChangeNotifications) + { + if (hadPreviousValue) + { + Debug.Assert(Array.IndexOf(newChangeNotifications, local) >= 0); + } + else + { + int newNotificationIndex = newChangeNotifications.Length; + Array.Resize(ref newChangeNotifications, newNotificationIndex + 1); + newChangeNotifications[newNotificationIndex] = local; + } + } + + Thread.CurrentThread.ExecutionContext = + new ExecutionContext(newValues, newChangeNotifications, current.m_isFlowSuppressed); + + if (needChangeNotifications) + { + local.OnValueChanged(previousValue, newValue, false); + } + } + + public ExecutionContext CreateCopy() + { + return this; // since CoreCLR's ExecutionContext is immutable, we don't need to create copies. + } + + public void Dispose() + { + // For CLR compat only + } + } + + public struct AsyncFlowControl : IDisposable + { + private Thread _thread; + + internal void Initialize(Thread currentThread) + { + Debug.Assert(currentThread == Thread.CurrentThread); + _thread = currentThread; + } + + public void Undo() + { + if (_thread == null) + { + throw new InvalidOperationException(SR.InvalidOperation_CannotUseAFCMultiple); + } + if (Thread.CurrentThread != _thread) + { + throw new InvalidOperationException(SR.InvalidOperation_CannotUseAFCOtherThread); + } + + // An async flow control cannot be undone when a different execution context is applied. The desktop framework + // mutates the execution context when its state changes, and only changes the instance when an execution context + // is applied (for instance, through ExecutionContext.Run). The framework prevents a suppressed-flow execution + // context from being applied by returning null from ExecutionContext.Capture, so the only type of execution + // context that can be applied is one whose flow is not suppressed. After suppressing flow and changing an async + // local's value, the desktop framework verifies that a different execution context has not been applied by + // checking the execution context instance against the one saved from when flow was suppressed. In .NET Core, + // since the execution context instance will change after changing the async local's value, it verifies that a + // different execution context has not been applied, by instead ensuring that the current execution context's + // flow is suppressed. + if (!ExecutionContext.IsFlowSuppressed()) + { + throw new InvalidOperationException(SR.InvalidOperation_AsyncFlowCtrlCtxMismatch); + } + + _thread = null; + ExecutionContext.RestoreFlow(); + } + + public void Dispose() + { + Undo(); + } + + public override bool Equals(object obj) + { + return obj is AsyncFlowControl && Equals((AsyncFlowControl)obj); + } + + public bool Equals(AsyncFlowControl obj) + { + return _thread == obj._thread; + } + + public override int GetHashCode() + { + return _thread?.GetHashCode() ?? 0; + } + + public static bool operator ==(AsyncFlowControl a, AsyncFlowControl b) + { + return a.Equals(b); + } + + public static bool operator !=(AsyncFlowControl a, AsyncFlowControl b) + { + return !(a == b); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/LazyInitializer.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/LazyInitializer.cs similarity index 79% rename from external/corert/src/System.Private.CoreLib/src/System/Threading/LazyInitializer.cs rename to external/corefx/src/Common/src/CoreLib/System/Threading/LazyInitializer.cs index 5853353661..f422ab9172 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/LazyInitializer.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/LazyInitializer.cs @@ -25,7 +25,7 @@ namespace System.Threading /// Initializes a target reference type with the type's default constructor if the target has not /// already been initialized. /// - /// The refence type of the reference to be initialized. + /// The reference type of the reference to be initialized. /// A reference of type to initialize if it has not /// already been initialized. /// The initialized reference of type . @@ -48,7 +48,28 @@ namespace System.Threading /// /// public static T EnsureInitialized(ref T target) where T : class => - Volatile.Read(ref target) ?? EnsureInitializedCore(ref target, LazyHelpers.ActivatorFactorySelectorFunc); + Volatile.Read(ref target) ?? EnsureInitializedCore(ref target); + + /// + /// Initializes a target reference type with the type's default constructor (slow path) + /// + /// The reference type of the reference to be initialized. + /// The variable that need to be initialized + /// The initialized variable + private static T EnsureInitializedCore(ref T target) where T : class + { + try + { + Interlocked.CompareExchange(ref target, Activator.CreateInstance(), null); + } + catch (MissingMethodException) + { + throw new MissingMemberException(SR.Lazy_CreateValue_NoParameterlessCtorForT); + } + + Debug.Assert(target != null); + return target; + } /// /// Initializes a target reference type using the specified function if it has not already been @@ -79,7 +100,7 @@ namespace System.Threading /// /// public static T EnsureInitialized(ref T target, Func valueFactory) where T : class => - Volatile.Read(ref target) ?? EnsureInitializedCore(ref target, valueFactory); + Volatile.Read(ref target) ?? EnsureInitializedCore(ref target, valueFactory); /// /// Initialize the target using the given delegate (slow path). @@ -101,7 +122,6 @@ namespace System.Threading return target; } - /// /// Initializes a target reference or value type with its default constructor if it has not already /// been initialized. @@ -122,7 +142,41 @@ namespace System.Threading return target; } - return EnsureInitializedCore(ref target, ref initialized, ref syncLock, LazyHelpers.ActivatorFactorySelectorFunc); + return EnsureInitializedCore(ref target, ref initialized, ref syncLock); + } + + /// + /// Ensure the target is initialized and return the value (slow path). This overload permits nulls + /// and also works for value type targets. Uses the type's default constructor to create the value. + /// + /// The type of target. + /// A reference to the target to be initialized. + /// A reference to a location tracking whether the target has been initialized. + /// A reference to a location containing a mutual exclusive lock. If is null, + /// a new object will be instantiated. + /// + /// The initialized object. + private static T EnsureInitializedCore(ref T target, ref bool initialized, ref object syncLock) + { + // Lazily initialize the lock if necessary and then double check if initialization is still required. + lock (EnsureLockInitialized(ref syncLock)) + { + if (!Volatile.Read(ref initialized)) + { + try + { + target = Activator.CreateInstance(); + } + catch (MissingMethodException) + { + throw new MissingMemberException(SR.Lazy_CreateValue_NoParameterlessCtorForT); + } + + Volatile.Write(ref initialized, true); + } + } + + return target; } /// @@ -147,33 +201,9 @@ namespace System.Threading return target; } - - return EnsureInitializedCore(ref target, ref initialized, ref syncLock, valueFactory); + return EnsureInitializedCore(ref target, ref initialized, ref syncLock, valueFactory); } - /// - /// Ensure the lock object is intialized. - /// - /// A reference to a location containing a mutual exclusive lock. If is null, - /// a new object will be instantiated. - /// Initialized lock object. - private static object EnsureLockInitialized(ref object syncLock) => - syncLock ?? - Interlocked.CompareExchange(ref syncLock, new object(), null) ?? - syncLock; - - /// - /// Initializes a target reference type with a specified function if it has not already been initialized. - /// - /// The type of the reference to be initialized. Has to be reference type. - /// A reference of type to initialize if it has not already been initialized. - /// A reference to an object used as the mutually exclusive lock for initializing - /// . If is null, a new object will be instantiated. - /// The invoked to initialize the reference. - /// The initialized value of type . - public static T EnsureInitialized(ref T target, ref object syncLock, Func valueFactory) where T : class => - Volatile.Read(ref target) ?? EnsureInitializedCore(ref target, ref syncLock, valueFactory); - /// /// Ensure the target is initialized and return the value (slow path). This overload permits nulls /// and also works for value type targets. Uses the supplied function to create the value. @@ -189,7 +219,7 @@ namespace System.Threading /// The initialized object. private static T EnsureInitializedCore(ref T target, ref bool initialized, ref object syncLock, Func valueFactory) { - // Lazily initialize the lock if necessary and, then double check if initialization is still required. + // Lazily initialize the lock if necessary and then double check if initialization is still required. lock (EnsureLockInitialized(ref syncLock)) { if (!Volatile.Read(ref initialized)) @@ -202,6 +232,18 @@ namespace System.Threading return target; } + /// + /// Initializes a target reference type with a specified function if it has not already been initialized. + /// + /// The type of the reference to be initialized. Has to be reference type. + /// A reference of type to initialize if it has not already been initialized. + /// A reference to an object used as the mutually exclusive lock for initializing + /// . If is null, a new object will be instantiated. + /// The invoked to initialize the reference. + /// The initialized value of type . + public static T EnsureInitialized(ref T target, ref object syncLock, Func valueFactory) where T : class => + Volatile.Read(ref target) ?? EnsureInitializedCore(ref target, ref syncLock, valueFactory); + /// /// Ensure the target is initialized and return the value (slow path). This overload works only for reference type targets. /// Uses the supplied function to create the value. @@ -216,7 +258,7 @@ namespace System.Threading /// The initialized object. private static T EnsureInitializedCore(ref T target, ref object syncLock, Func valueFactory) where T : class { - // Lazily initialize the lock if necessary and, then double check if initialization is still required. + // Lazily initialize the lock if necessary and then double check if initialization is still required. lock (EnsureLockInitialized(ref syncLock)) { if (Volatile.Read(ref target) == null) @@ -231,25 +273,16 @@ namespace System.Threading return target; } - } - // Caches the activation selector function to avoid delegate allocations. - internal static class LazyHelpers - { - // internal static Func s_activatorFactorySelector = new Func(ActivatorFactorySelector); - - internal static Func ActivatorFactorySelectorFunc { get { return ActivatorFactorySelector; } } - - private static T ActivatorFactorySelector() - { - try - { - return Activator.CreateInstance(); - } - catch (MissingMemberException) - { - throw new MissingMemberException(SR.Lazy_CreateValue_NoParameterlessCtorForT); - } - } + /// + /// Ensure the lock object is initialized. + /// + /// A reference to a location containing a mutual exclusive lock. If is null, + /// a new object will be instantiated. + /// Initialized lock object. + private static object EnsureLockInitialized(ref object syncLock) => + syncLock ?? + Interlocked.CompareExchange(ref syncLock, new object(), null) ?? + syncLock; } } diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/LazyThreadSafetyMode.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/LazyThreadSafetyMode.cs new file mode 100644 index 0000000000..2d13f23762 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/LazyThreadSafetyMode.cs @@ -0,0 +1,44 @@ +// 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. + +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// a set of lightweight static helpers for lazy initialization. +// +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +namespace System.Threading +{ + /// + /// Specifies how a instance should synchronize access among multiple threads. + /// + public enum LazyThreadSafetyMode + { + /// + /// This mode makes no guarantees around the thread-safety of the instance. If used from multiple threads, the behavior of the is undefined. + /// This mode should be used when a is guaranteed to never be initialized from more than one thread simultaneously and high performance is crucial. + /// If valueFactory throws an exception when the is initialized, the exception will be cached and returned on subsequent accesses to Value. Also, if valueFactory recursively + /// accesses Value on this instance, a will be thrown. + /// + None, + + /// + /// When multiple threads attempt to simultaneously initialize a instance, this mode allows each thread to execute the + /// valueFactory but only the first thread to complete initialization will be allowed to set the final value of the . + /// Once initialized successfully, any future calls to Value will return the cached result. If valueFactory throws an exception on any thread, that exception will be + /// propagated out of Value. If any thread executes valueFactory without throwing an exception and, therefore, successfully sets the value, that value will be returned on + /// subsequent accesses to Value from any thread. If no thread succeeds in setting the value, IsValueCreated will remain false and subsequent accesses to Value will result in + /// the valueFactory delegate re-executing. Also, if valueFactory recursively accesses Value on this instance, an exception will NOT be thrown. + /// + PublicationOnly, + + /// + /// This mode uses locks to ensure that only a single thread can initialize a instance in a thread-safe manner. In general, + /// taken if this mode is used in conjunction with a valueFactory delegate that uses locks internally, a deadlock can occur if not + /// handled carefully. If valueFactory throws an exception when the is initialized, the exception will be cached and returned on + /// subsequent accesses to Value. Also, if valueFactory recursively accesses Value on this instance, a will be thrown. + /// + ExecutionAndPublication + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/LockRecursionException.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/LockRecursionException.cs new file mode 100644 index 0000000000..c76c7a5040 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/LockRecursionException.cs @@ -0,0 +1,32 @@ +// 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.Runtime.Serialization; + +namespace System.Threading +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class LockRecursionException : System.Exception + { + public LockRecursionException() + { + } + + public LockRecursionException(string message) + : base(message) + { + } + + public LockRecursionException(string message, Exception innerException) + : base(message, innerException) + { + } + + protected LockRecursionException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/IExprWithArgs.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/ManualResetEvent.cs similarity index 53% rename from external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/IExprWithArgs.cs rename to external/corefx/src/Common/src/CoreLib/System/Threading/ManualResetEvent.cs index de77cca5a4..4b8d61f960 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/IExprWithArgs.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/ManualResetEvent.cs @@ -2,12 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Microsoft.CSharp.RuntimeBinder.Semantics +namespace System.Threading { - internal interface IExprWithArgs : IExprWithObject + public sealed class ManualResetEvent : EventWaitHandle { - Expr OptionalArguments { get; set; } - - SymWithType GetSymWithType(); + public ManualResetEvent(bool initialState) : base(initialState, EventResetMode.ManualReset) { } } } + diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/ParameterizedThreadStart.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/ParameterizedThreadStart.cs new file mode 100644 index 0000000000..c0f29e8e80 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/ParameterizedThreadStart.cs @@ -0,0 +1,18 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: This class is a Delegate which defines the start method +** for starting a thread. That method must match this delegate. +** +** +=============================================================================*/ + +namespace System.Threading +{ + public delegate void ParameterizedThreadStart(object obj); +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/ReaderWriterLockSlim.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/ReaderWriterLockSlim.cs similarity index 58% rename from external/corert/src/System.Private.CoreLib/src/System/Threading/ReaderWriterLockSlim.cs rename to external/corefx/src/Common/src/CoreLib/System/Threading/ReaderWriterLockSlim.cs index 74822bbcb0..3c4aad603a 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/ReaderWriterLockSlim.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/ReaderWriterLockSlim.cs @@ -2,11 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Internal.Runtime.Augments; using System.Diagnostics; // for TraceInformation using System.Runtime.CompilerServices; -using Internal.Runtime.Augments; - -// TODO: Delete this and use the implementation from CoreFX namespace System.Threading { @@ -55,19 +53,15 @@ namespace System.Threading /// public class ReaderWriterLockSlim : IDisposable { - //Specifying if locked can be reacquired recursively. - private bool _fIsReentrant; + private static readonly int ProcessorCount = Environment.ProcessorCount; - // Lock specification for myLock: This lock protects exactly the local fields associated with this + //Specifying if the lock can be reacquired recursively. + private readonly bool _fIsReentrant; + + // Lock specification for _spinLock: This lock protects exactly the local fields associated with this // instance of ReaderWriterLockSlim. It does NOT protect the memory associated with // the events that hang off this lock (eg writeEvent, readEvent upgradeEvent). - private int _myLock; - - //The variables controlling spinning behavior of Mylock(which is a spin-lock) - - private const int LockSpinCycles = 20; - private const int LockSpinCount = 10; - private const int LockSleep0Count = 5; + SpinLock _spinLock; // These variables allow use to avoid Setting events (which is expensive) if we don't have to. private uint _numWriteWaiters; // maximum number of threads that can be doing a WaitOne on the writeEvent @@ -75,15 +69,14 @@ namespace System.Threading private uint _numWriteUpgradeWaiters; // maximum number of threads that can be doing a WaitOne on the upgradeEvent (at most 1). private uint _numUpgradeWaiters; - //Variable used for quick check when there are no waiters. - private bool _fNoWaiters; + private WaiterStates _waiterStates; private int _upgradeLockOwnerId; private int _writeLockOwnerId; // conditions we wait on. - private EventWaitHandle _writeEvent; // threads waiting to aquire a write lock go here. - private EventWaitHandle _readEvent; // threads waiting to aquire a read lock go here (will be released in bulk) + private EventWaitHandle _writeEvent; // threads waiting to acquire a write lock go here. + private EventWaitHandle _readEvent; // threads waiting to acquire a read lock go here (will be released in bulk) private EventWaitHandle _upgradeEvent; // thread waiting to acquire the upgrade lock private EventWaitHandle _waitUpgradeEvent; // thread waiting to upgrade from the upgrade lock to a write lock go here (at most one) @@ -147,10 +140,37 @@ namespace System.Threading _fIsReentrant = true; } InitializeThreadCounts(); - _fNoWaiters = true; + _waiterStates = WaiterStates.NoWaiters; _lockID = Interlocked.Increment(ref s_nextLockID); } + private bool HasNoWaiters + { + get + { +#if DEBUG + Debug.Assert(_spinLock.IsHeld); +#endif + + return (_waiterStates & WaiterStates.NoWaiters) != WaiterStates.None; + } + set + { +#if DEBUG + Debug.Assert(_spinLock.IsHeld); +#endif + + if (value) + { + _waiterStates |= WaiterStates.NoWaiters; + } + else + { + _waiterStates &= ~WaiterStates.NoWaiters; + } + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsRWEntryEmpty(ReaderWriterCount rwc) { @@ -297,7 +317,7 @@ namespace System.Threading throw new LockRecursionException(SR.LockRecursionException_ReadAfterWriteNotAllowed); } - EnterMyLock(); + _spinLock.Enter(EnterSpinLockReason.EnterAnyRead); lrwc = GetThreadRWCount(false); @@ -307,7 +327,7 @@ namespace System.Threading //a count in the structure). if (lrwc.readercount > 0) { - ExitMyLock(); + _spinLock.Exit(); throw new LockRecursionException(SR.LockRecursionException_RecursiveReadNotAllowed); } else if (id == _upgradeLockOwnerId) @@ -317,18 +337,18 @@ namespace System.Threading lrwc.readercount++; _owners++; - ExitMyLock(); + _spinLock.Exit(); return true; } } else { - EnterMyLock(); + _spinLock.Enter(EnterSpinLockReason.EnterAnyRead); lrwc = GetThreadRWCount(false); if (lrwc.readercount > 0) { lrwc.readercount++; - ExitMyLock(); + _spinLock.Exit(); return true; } else if (id == _upgradeLockOwnerId) @@ -337,7 +357,7 @@ namespace System.Threading //Update the global read counts and exit. lrwc.readercount++; _owners++; - ExitMyLock(); + _spinLock.Exit(); _fUpgradeThreadHoldingRead = true; return true; } @@ -347,16 +367,15 @@ namespace System.Threading //Update global read counts here, lrwc.readercount++; _owners++; - ExitMyLock(); + _spinLock.Exit(); return true; } } bool retVal = true; + int spinCount = 0; - int spincount = 0; - - for (;;) + for (; ;) { // We can enter a read lock if there are only read-locks have been given out // and a writer is not trying to get in. @@ -369,14 +388,18 @@ namespace System.Threading break; } - if (spincount < MaxSpinCount) + if (timeout.IsExpired) { - ExitMyLock(); - if (timeout.IsExpired) - return false; - spincount++; - SpinWait(spincount); - EnterMyLock(); + _spinLock.Exit(); + return false; + } + + if (spinCount < MaxSpinCount && ShouldSpinForEnterAnyRead()) + { + _spinLock.Exit(); + spinCount++; + SpinWait(spinCount); + _spinLock.Enter(EnterSpinLockReason.EnterAnyRead); //The per-thread structure may have been recycled as the lock is acquired (due to message pumping), load again. if (IsRwHashEntryChanged(lrwc)) lrwc = GetThreadRWCount(false); @@ -386,13 +409,13 @@ namespace System.Threading // Drat, we need to wait. Mark that we have waiters and wait. if (_readEvent == null) // Create the needed event { - LazyCreateEvent(ref _readEvent, false); + LazyCreateEvent(ref _readEvent, EnterLockType.Read); if (IsRwHashEntryChanged(lrwc)) lrwc = GetThreadRWCount(false); continue; // since we left the lock, start over. } - retVal = WaitOnEvent(_readEvent, ref _numReadWaiters, timeout); + retVal = WaitOnEvent(_readEvent, ref _numReadWaiters, timeout, EnterLockType.Read); if (!retVal) { return false; @@ -401,7 +424,7 @@ namespace System.Threading lrwc = GetThreadRWCount(false); } - ExitMyLock(); + _spinLock.Exit(); return retVal; } @@ -436,6 +459,7 @@ namespace System.Threading if (!_fIsReentrant) { + EnterSpinLockReason enterMyLockReason; if (id == _writeLockOwnerId) { //Check for AW->AW @@ -445,27 +469,46 @@ namespace System.Threading { //AU->AW case is allowed once. upgradingToWrite = true; + enterMyLockReason = EnterSpinLockReason.UpgradeToWrite; } + else + { + enterMyLockReason = EnterSpinLockReason.EnterWrite; + } + _spinLock.Enter(enterMyLockReason); - EnterMyLock(); lrwc = GetThreadRWCount(true); //Can't acquire write lock with reader lock held. if (lrwc != null && lrwc.readercount > 0) { - ExitMyLock(); + _spinLock.Exit(); throw new LockRecursionException(SR.LockRecursionException_WriteAfterReadNotAllowed); } } else { - EnterMyLock(); + EnterSpinLockReason enterMyLockReason; + if (id == _writeLockOwnerId) + { + enterMyLockReason = EnterSpinLockReason.EnterRecursiveWrite; + } + else if (id == _upgradeLockOwnerId) + { + enterMyLockReason = EnterSpinLockReason.UpgradeToWrite; + } + else + { + enterMyLockReason = EnterSpinLockReason.EnterWrite; + } + _spinLock.Enter(enterMyLockReason); + lrwc = GetThreadRWCount(false); if (id == _writeLockOwnerId) { lrwc.writercount++; - ExitMyLock(); + _spinLock.Exit(); return true; } else if (id == _upgradeLockOwnerId) @@ -476,15 +519,15 @@ namespace System.Threading { //Write locks may not be acquired if only read locks have been //acquired. - ExitMyLock(); + _spinLock.Exit(); throw new LockRecursionException(SR.LockRecursionException_WriteAfterReadNotAllowed); } } - int spincount = 0; bool retVal = true; + int spinCount = 0; - for (;;) + for (; ;) { if (IsWriterAcquired()) { @@ -529,14 +572,18 @@ namespace System.Threading } } - if (spincount < MaxSpinCount) + if (timeout.IsExpired) { - ExitMyLock(); - if (timeout.IsExpired) - return false; - spincount++; - SpinWait(spincount); - EnterMyLock(); + _spinLock.Exit(); + return false; + } + + if (spinCount < MaxSpinCount && ShouldSpinForEnterAnyWrite(upgradingToWrite)) + { + _spinLock.Exit(); + spinCount++; + SpinWait(spinCount); + _spinLock.Enter(upgradingToWrite ? EnterSpinLockReason.UpgradeToWrite : EnterSpinLockReason.EnterWrite); continue; } @@ -544,13 +591,13 @@ namespace System.Threading { if (_waitUpgradeEvent == null) // Create the needed event { - LazyCreateEvent(ref _waitUpgradeEvent, true); + LazyCreateEvent(ref _waitUpgradeEvent, EnterLockType.UpgradeToWrite); continue; // since we left the lock, start over. } Debug.Assert(_numWriteUpgradeWaiters == 0, "There can be at most one thread with the upgrade lock held."); - retVal = WaitOnEvent(_waitUpgradeEvent, ref _numWriteUpgradeWaiters, timeout); + retVal = WaitOnEvent(_waitUpgradeEvent, ref _numWriteUpgradeWaiters, timeout, EnterLockType.UpgradeToWrite); //The lock is not held in case of failure. if (!retVal) @@ -561,11 +608,11 @@ namespace System.Threading // Drat, we need to wait. Mark that we have waiters and wait. if (_writeEvent == null) // create the needed event. { - LazyCreateEvent(ref _writeEvent, true); + LazyCreateEvent(ref _writeEvent, EnterLockType.Write); continue; // since we left the lock, start over. } - retVal = WaitOnEvent(_writeEvent, ref _numWriteWaiters, timeout); + retVal = WaitOnEvent(_writeEvent, ref _numWriteWaiters, timeout, EnterLockType.Write); //The lock is not held in case of failure. if (!retVal) return false; @@ -581,7 +628,7 @@ namespace System.Threading lrwc.writercount++; } - ExitMyLock(); + _spinLock.Exit(); _writeLockOwnerId = id; @@ -629,24 +676,24 @@ namespace System.Threading throw new LockRecursionException(SR.LockRecursionException_UpgradeAfterWriteNotAllowed); } - EnterMyLock(); + _spinLock.Enter(EnterSpinLockReason.EnterAnyRead); lrwc = GetThreadRWCount(true); //Can't acquire upgrade lock with reader lock held. if (lrwc != null && lrwc.readercount > 0) { - ExitMyLock(); + _spinLock.Exit(); throw new LockRecursionException(SR.LockRecursionException_UpgradeAfterReadNotAllowed); } } else { - EnterMyLock(); + _spinLock.Enter(EnterSpinLockReason.EnterAnyRead); lrwc = GetThreadRWCount(false); if (id == _upgradeLockOwnerId) { lrwc.upgradecount++; - ExitMyLock(); + _spinLock.Exit(); return true; } else if (id == _writeLockOwnerId) @@ -659,23 +706,22 @@ namespace System.Threading lrwc.upgradecount++; if (lrwc.readercount > 0) _fUpgradeThreadHoldingRead = true; - ExitMyLock(); + _spinLock.Exit(); return true; } else if (lrwc.readercount > 0) { //Upgrade locks may not be acquired if only read locks have been //acquired. - ExitMyLock(); + _spinLock.Exit(); throw new LockRecursionException(SR.LockRecursionException_UpgradeAfterReadNotAllowed); } } bool retVal = true; + int spinCount = 0; - int spincount = 0; - - for (;;) + for (; ;) { //Once an upgrade lock is taken, it's like having a reader lock held //until upgrade or downgrade operations are performed. @@ -687,26 +733,30 @@ namespace System.Threading break; } - if (spincount < MaxSpinCount) + if (timeout.IsExpired) { - ExitMyLock(); - if (timeout.IsExpired) - return false; - spincount++; - SpinWait(spincount); - EnterMyLock(); + _spinLock.Exit(); + return false; + } + + if (spinCount < MaxSpinCount && ShouldSpinForEnterAnyRead()) + { + _spinLock.Exit(); + spinCount++; + SpinWait(spinCount); + _spinLock.Enter(EnterSpinLockReason.EnterAnyRead); continue; } // Drat, we need to wait. Mark that we have waiters and wait. if (_upgradeEvent == null) // Create the needed event { - LazyCreateEvent(ref _upgradeEvent, true); + LazyCreateEvent(ref _upgradeEvent, EnterLockType.UpgradeableRead); continue; // since we left the lock, start over. } //Only one thread with the upgrade lock held can proceed. - retVal = WaitOnEvent(_upgradeEvent, ref _numUpgradeWaiters, timeout); + retVal = WaitOnEvent(_upgradeEvent, ref _numUpgradeWaiters, timeout, EnterLockType.UpgradeableRead); if (!retVal) return false; } @@ -720,7 +770,7 @@ namespace System.Threading lrwc.upgradecount++; } - ExitMyLock(); + _spinLock.Exit(); return true; } @@ -729,14 +779,14 @@ namespace System.Threading { ReaderWriterCount lrwc = null; - EnterMyLock(); + _spinLock.Enter(EnterSpinLockReason.ExitAnyRead); lrwc = GetThreadRWCount(true); if (lrwc == null || lrwc.readercount < 1) { //You have to be holding the read lock to make this call. - ExitMyLock(); + _spinLock.Exit(); throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedRead); } @@ -745,7 +795,7 @@ namespace System.Threading if (lrwc.readercount > 1) { lrwc.readercount--; - ExitMyLock(); + _spinLock.Exit(); return; } @@ -775,22 +825,22 @@ namespace System.Threading //You have to be holding the write lock to make this call. throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedWrite); } - EnterMyLock(); + _spinLock.Enter(EnterSpinLockReason.ExitAnyWrite); } else { - EnterMyLock(); + _spinLock.Enter(EnterSpinLockReason.ExitAnyWrite); lrwc = GetThreadRWCount(false); if (lrwc == null) { - ExitMyLock(); + _spinLock.Exit(); throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedWrite); } if (lrwc.writercount < 1) { - ExitMyLock(); + _spinLock.Exit(); throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedWrite); } @@ -798,7 +848,7 @@ namespace System.Threading if (lrwc.writercount > 0) { - ExitMyLock(); + _spinLock.Exit(); return; } } @@ -822,22 +872,22 @@ namespace System.Threading //You have to be holding the upgrade lock to make this call. throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedUpgrade); } - EnterMyLock(); + _spinLock.Enter(EnterSpinLockReason.ExitAnyRead); } else { - EnterMyLock(); + _spinLock.Enter(EnterSpinLockReason.ExitAnyRead); lrwc = GetThreadRWCount(true); if (lrwc == null) { - ExitMyLock(); + _spinLock.Exit(); throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedUpgrade); } if (lrwc.upgradecount < 1) { - ExitMyLock(); + _spinLock.Exit(); throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedUpgrade); } @@ -845,7 +895,7 @@ namespace System.Threading if (lrwc.upgradecount > 0) { - ExitMyLock(); + _spinLock.Exit(); return; } @@ -864,19 +914,39 @@ namespace System.Threading /// while holding a spin lock). If all goes well, reenter the lock and /// set 'waitEvent' /// - private void LazyCreateEvent(ref EventWaitHandle waitEvent, bool makeAutoResetEvent) + private void LazyCreateEvent(ref EventWaitHandle waitEvent, EnterLockType enterLockType) { #if DEBUG - Debug.Assert(MyLockHeld); + Debug.Assert(_spinLock.IsHeld); Debug.Assert(waitEvent == null); #endif - ExitMyLock(); - EventWaitHandle newEvent; - if (makeAutoResetEvent) - newEvent = new AutoResetEvent(false); - else - newEvent = new ManualResetEvent(false); - EnterMyLock(); + + _spinLock.Exit(); + + var newEvent = + new EventWaitHandle( + false, + enterLockType == EnterLockType.Read ? EventResetMode.ManualReset : EventResetMode.AutoReset); + + EnterSpinLockReason enterMyLockReason; + switch (enterLockType) + { + case EnterLockType.Read: + case EnterLockType.UpgradeableRead: + enterMyLockReason = EnterSpinLockReason.EnterAnyRead | EnterSpinLockReason.Wait; + break; + + case EnterLockType.Write: + enterMyLockReason = EnterSpinLockReason.EnterWrite | EnterSpinLockReason.Wait; + break; + + default: + Debug.Assert(enterLockType == EnterLockType.UpgradeToWrite); + enterMyLockReason = EnterSpinLockReason.UpgradeToWrite | EnterSpinLockReason.Wait; + break; + } + _spinLock.Enter(enterMyLockReason); + if (waitEvent == null) // maybe someone snuck in. waitEvent = newEvent; else @@ -887,14 +957,63 @@ namespace System.Threading /// Waits on 'waitEvent' with a timeout /// Before the wait 'numWaiters' is incremented and is restored before leaving this routine. /// - private bool WaitOnEvent(EventWaitHandle waitEvent, ref uint numWaiters, TimeoutTracker timeout) + private bool WaitOnEvent( + EventWaitHandle waitEvent, + ref uint numWaiters, + TimeoutTracker timeout, + EnterLockType enterLockType) { #if DEBUG - Debug.Assert(MyLockHeld); + Debug.Assert(_spinLock.IsHeld); #endif + + WaiterStates waiterSignaledState = WaiterStates.None; + EnterSpinLockReason enterMyLockReason; + switch (enterLockType) + { + case EnterLockType.UpgradeableRead: + waiterSignaledState = WaiterStates.UpgradeableReadWaiterSignaled; + goto case EnterLockType.Read; + + case EnterLockType.Read: + enterMyLockReason = EnterSpinLockReason.EnterAnyRead; + break; + + case EnterLockType.Write: + waiterSignaledState = WaiterStates.WriteWaiterSignaled; + enterMyLockReason = EnterSpinLockReason.EnterWrite; + break; + + default: + Debug.Assert(enterLockType == EnterLockType.UpgradeToWrite); + enterMyLockReason = EnterSpinLockReason.UpgradeToWrite; + break; + } + + // It was not possible to acquire the RW lock because some other thread was holding some type of lock. The other + // thread, when it releases its lock, will wake appropriate waiters. Along with resetting the wait event, clear the + // waiter signaled bit for this type of waiter if applicable, to indicate that a waiter of this type is no longer + // signaled. + // + // If the waiter signaled bit is not updated upon event reset, the following scenario would lead to deadlock: + // - Thread T0 signals the write waiter event or the upgradeable read waiter event to wake a waiter + // - There are no threads waiting on the event, but T1 is in WaitOnEvent() after exiting the spin lock and before + // actually waiting on the event (that is, it's recorded that there is one waiter for the event). It remains in + // this region for a while, in the repro case it typically gets context-switched out. + // - T2 acquires the RW lock in some fashion that blocks T0 or T3 from acquiring the RW lock + // - T0 or T3 fails to acquire the RW lock enough times for it to enter WaitOnEvent for the same event as T1 + // - T0 or T3 resets the event + // - T2 releases the RW lock and does not wake a waiter because the reset at the previous step lost a signal but + // _waiterStates was not updated to reflect that + // - T1 and other threads begin waiting on the event, but there's no longer any thread that would wake them + if (waiterSignaledState != WaiterStates.None && (_waiterStates & waiterSignaledState) != WaiterStates.None) + { + _waiterStates &= ~waiterSignaledState; + } waitEvent.Reset(); + numWaiters++; - _fNoWaiters = false; + HasNoWaiters = false; //Setting these bits will prevent new readers from getting in. if (_numWriteWaiters == 1) @@ -903,7 +1022,7 @@ namespace System.Threading SetUpgraderWaiting(); bool waitSuccessful = false; - ExitMyLock(); // Do the wait outside of any lock + _spinLock.Exit(); // Do the wait outside of any lock try { @@ -911,19 +1030,43 @@ namespace System.Threading } finally { - EnterMyLock(); + _spinLock.Enter(enterMyLockReason); + --numWaiters; + if (waitSuccessful && + waiterSignaledState != WaiterStates.None && + (_waiterStates & waiterSignaledState) != WaiterStates.None) + { + // Indicate that a signaled waiter of this type has woken. Since non-read waiters are signaled to wake one + // at a time, we avoid waking up more than one waiter of that type upon successive enter/exit loops until + // the signaled thread actually wakes up. For example, if there are multiple write waiters and one thread is + // repeatedly entering and exiting a write lock, every exit would otherwise signal a different write waiter + // to wake up unnecessarily when only one woken waiter may actually succeed in entering the write lock. + _waiterStates &= ~waiterSignaledState; + } + if (_numWriteWaiters == 0 && _numWriteUpgradeWaiters == 0 && _numUpgradeWaiters == 0 && _numReadWaiters == 0) - _fNoWaiters = true; + HasNoWaiters = true; if (_numWriteWaiters == 0) ClearWritersWaiting(); if (_numWriteUpgradeWaiters == 0) ClearUpgraderWaiting(); - if (!waitSuccessful) // We may also be about to throw for some reason. Exit myLock. - ExitMyLock(); + if (!waitSuccessful) // We may also be about to throw for some reason. Exit myLock. + { + if (enterLockType >= EnterLockType.Write) + { + // Write waiters block read waiters from acquiring the lock. Since this was the last write waiter, try + // to wake up the appropriate read waiters. + ExitAndWakeUpAppropriateReadWaiters(); + } + else + { + _spinLock.Exit(); + } + } } return waitSuccessful; } @@ -934,11 +1077,11 @@ namespace System.Threading private void ExitAndWakeUpAppropriateWaiters() { #if DEBUG - Debug.Assert(MyLockHeld); + Debug.Assert(_spinLock.IsHeld); #endif - if (_fNoWaiters) + if (HasNoWaiters) { - ExitMyLock(); + _spinLock.Exit(); return; } @@ -947,8 +1090,6 @@ namespace System.Threading private void ExitAndWakeUpAppropriateWaitersPreferringWriters() { - bool setUpgradeEvent = false; - bool setReadEvent = false; uint readercount = GetNumReaders(); //We need this case for EU->ER->EW case, as the read count will be 2 in @@ -957,7 +1098,7 @@ namespace System.Threading { if (_numWriteUpgradeWaiters > 0 && _fUpgradeThreadHoldingRead && readercount == 2) { - ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) + _spinLock.Exit(); // Exit before signaling to improve efficiency (wakee will need the lock) _waitUpgradeEvent.Set(); // release all upgraders (however there can be at most one). return; } @@ -965,43 +1106,73 @@ namespace System.Threading if (readercount == 1 && _numWriteUpgradeWaiters > 0) { - //We have to be careful now, as we are droppping the lock. + //We have to be careful now, as we are dropping the lock. //No new writes should be allowed to sneak in if an upgrade //was pending. - ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) + _spinLock.Exit(); // Exit before signaling to improve efficiency (wakee will need the lock) _waitUpgradeEvent.Set(); // release all upgraders (however there can be at most one). } else if (readercount == 0 && _numWriteWaiters > 0) { - ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) - _writeEvent.Set(); // release one writer. - } - else if (readercount >= 0) - { - if (_numReadWaiters != 0 || _numUpgradeWaiters != 0) + // Check if a waiter of the same type has already been signaled but hasn't woken yet. If so, avoid signaling + // and waking another waiter unnecessarily. + WaiterStates signaled = _waiterStates & WaiterStates.WriteWaiterSignaled; + if (signaled == WaiterStates.None) { - if (_numReadWaiters != 0) - setReadEvent = true; - - if (_numUpgradeWaiters != 0 && _upgradeLockOwnerId == -1) - { - setUpgradeEvent = true; - } - - ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) - - if (setReadEvent) - _readEvent.Set(); // release all readers. - - if (setUpgradeEvent) - _upgradeEvent.Set(); //release one upgrader. + _waiterStates |= WaiterStates.WriteWaiterSignaled; + } + + _spinLock.Exit(); // Exit before signaling to improve efficiency (wakee will need the lock) + + if (signaled == WaiterStates.None) + { + _writeEvent.Set(); // release one writer. } - else - ExitMyLock(); } else - ExitMyLock(); + { + ExitAndWakeUpAppropriateReadWaiters(); + } + } + + private void ExitAndWakeUpAppropriateReadWaiters() + { +#if DEBUG + Debug.Assert(_spinLock.IsHeld); +#endif + + if (_numWriteWaiters != 0 || _numWriteUpgradeWaiters != 0 || HasNoWaiters) + { + _spinLock.Exit(); + return; + } + + Debug.Assert(_numReadWaiters != 0 || _numUpgradeWaiters != 0); + + bool setReadEvent = _numReadWaiters != 0; + bool setUpgradeEvent = _numUpgradeWaiters != 0 && _upgradeLockOwnerId == -1; + if (setUpgradeEvent) + { + // Check if a waiter of the same type has already been signaled but hasn't woken yet. If so, avoid signaling + // and waking another waiter unnecessarily. + if ((_waiterStates & WaiterStates.UpgradeableReadWaiterSignaled) == WaiterStates.None) + { + _waiterStates |= WaiterStates.UpgradeableReadWaiterSignaled; + } + else + { + setUpgradeEvent = false; + } + } + + _spinLock.Exit(); // Exit before signaling to improve efficiency (wakee will need the lock) + + if (setReadEvent) + _readEvent.Set(); // release all readers. + + if (setUpgradeEvent) + _upgradeEvent.Set(); //release one upgrader. } private bool IsWriterAcquired() @@ -1044,61 +1215,44 @@ namespace System.Threading return _owners & READER_MASK; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void EnterMyLock() + private bool ShouldSpinForEnterAnyRead() { - if (Interlocked.CompareExchange(ref _myLock, 1, 0) != 0) - EnterMyLockSpin(); + // If there is a write waiter or write upgrade waiter, the waiter would block a reader from acquiring the RW lock + // because the waiter takes precedence. In that case, the reader is not likely to make progress by spinning. + // Although another thread holding a write lock would prevent this thread from acquiring a read lock, it is by + // itself not a good enough reason to skip spinning. + return HasNoWaiters || (_numWriteWaiters == 0 && _numWriteUpgradeWaiters == 0); } - private void EnterMyLockSpin() + private bool ShouldSpinForEnterAnyWrite(bool isUpgradeToWrite) { - int pc = Environment.ProcessorCount; - for (int i = 0; ; i++) - { - if (i < LockSpinCount && pc > 1) - { - RuntimeThread.SpinWait(LockSpinCycles * (i + 1)); // Wait a few dozen instructions to let another processor release lock. - } - else if (i < (LockSpinCount + LockSleep0Count)) - { - RuntimeThread.Sleep(0); // Give up my quantum. - } - else - { - RuntimeThread.Sleep(1); // Give up my quantum. - } - - if (_myLock == 0 && Interlocked.CompareExchange(ref _myLock, 1, 0) == 0) - return; - } + // If there is a write upgrade waiter, the waiter would block a writer from acquiring the RW lock because the waiter + // holds a read lock. In that case, the writer is not likely to make progress by spinning. Regarding upgrading to a + // write lock, there is no type of waiter that would block the upgrade from happening. Although another thread + // holding a read or write lock would prevent this thread from acquiring the write lock, it is by itself not a good + // enough reason to skip spinning. + return isUpgradeToWrite || _numWriteUpgradeWaiters == 0; } - private void ExitMyLock() + private static void SpinWait(int spinCount) { - Debug.Assert(_myLock != 0, "Exiting spin lock that is not held"); - Volatile.Write(ref _myLock, 0); - } + const int LockSpinCycles = 20; -#if DEBUG - private bool MyLockHeld { get { return _myLock != 0; } } -#endif - - private static void SpinWait(int SpinCount) - { - //Exponential backoff - if ((SpinCount < 5) && (Environment.ProcessorCount > 1)) + //Exponential back-off + if ((spinCount < 5) && (ProcessorCount > 1)) { - RuntimeThread.SpinWait(LockSpinCycles * SpinCount); - } - else if (SpinCount < MaxSpinCount - 3) - { - RuntimeThread.Sleep(0); + RuntimeThread.SpinWait(LockSpinCycles * spinCount); } else { - RuntimeThread.Sleep(1); + RuntimeThread.Sleep(0); } + + // Don't want to Sleep(1) in this spin wait: + // - Don't want to spin for that long, since a proper wait will follow when the spin wait fails. The artifical + // delay introduced by Sleep(1) will in some cases be much longer than desired. + // - Sleep(1) would put the thread into a wait state, and a proper wait will follow when the spin wait fails + // anyway, so it's preferable to put the thread into the proper wait state } public void Dispose() @@ -1290,5 +1444,239 @@ namespace System.Threading return (int)_numWriteWaiters; } } + + private struct SpinLock + { + private int _isLocked; + + /// + /// Used to deprioritize threads attempting to enter the lock when they would not make progress after doing so. + /// avoids acquiring the lock as long as the operation for which it + /// was called is deprioritized. + /// + /// Layout: + /// - Low 16 bits: Number of threads that have deprioritized an enter-any-write operation + /// - High 16 bits: Number of threads that have deprioritized an enter-any-read operation + /// + private int _enterDeprioritizationState; + + // Layout-specific constants for _enterDeprioritizationState + private const int DeprioritizeEnterAnyReadIncrement = 1 << 16; + private const int DeprioritizeEnterAnyWriteIncrement = 1; + + // The variables controlling spinning behavior of this spin lock + private const int LockSpinCycles = 20; + private const int LockSpinCount = 10; + private const int LockSleep0Count = 5; + private const int DeprioritizedLockSleep1Count = 5; + + private static int GetEnterDeprioritizationStateChange(EnterSpinLockReason reason) + { + EnterSpinLockReason operation = reason & EnterSpinLockReason.OperationMask; + switch (operation) + { + case EnterSpinLockReason.EnterAnyRead: + return 0; + + case EnterSpinLockReason.ExitAnyRead: + // A read lock is held until this thread is able to exit it, so deprioritize enter-write threads as they + // will not be able to make progress + return DeprioritizeEnterAnyWriteIncrement; + + case EnterSpinLockReason.EnterWrite: + // Writers are typically much less frequent and much less in number than readers. Waiting writers take + // precedence over new read attempts in order to let current readers release their lock and allow a + // writer to obtain the lock. Before a writer can register as a waiter though, the presence of just + // relatively few enter-read spins can easily starve the enter-write from even entering this lock, + // delaying its spin loop for an unreasonable duration. + // + // Deprioritize enter-read to preference enter-write. This makes it easier for enter-write threads to + // starve enter-read threads. However, writers can already by design starve readers. A waiting writer + // blocks enter-read threads and a new enter-write that needs to wait will be given precedence over + // previously waiting enter-read threads. So this is not a new problem, and the RW lock is designed for + // scenarios where writers are rare compared to readers. + return DeprioritizeEnterAnyReadIncrement; + + default: + Debug.Assert( + operation == EnterSpinLockReason.UpgradeToWrite || + operation == EnterSpinLockReason.EnterRecursiveWrite || + operation == EnterSpinLockReason.ExitAnyWrite); + + // UpgradeToWrite: + // - A read lock is held and an exit-read is not nearby, so deprioritize enter-write threads as they + // will not be able to make progress. This thread also intends to enter a write lock, so deprioritize + // enter -read threads as well, see case EnterSpinLockReason.EnterWrite for the rationale. + // EnterRecursiveWrite, ExitAnyWrite: + // - In both cases, a write lock is held until this thread is able to exit it, so deprioritize + // enter -read and enter-write threads as they will not be able to make progress + return DeprioritizeEnterAnyReadIncrement + DeprioritizeEnterAnyWriteIncrement; + } + } + + private ushort EnterForEnterAnyReadDeprioritizedCount + { + get + { + Debug.Assert(DeprioritizeEnterAnyReadIncrement == (1 << 16)); + return (ushort)((uint)_enterDeprioritizationState >> 16); + } + } + + private ushort EnterForEnterAnyWriteDeprioritizedCount + { + get + { + Debug.Assert(DeprioritizeEnterAnyWriteIncrement == 1); + return (ushort)_enterDeprioritizationState; + } + } + + private bool IsEnterDeprioritized(EnterSpinLockReason reason) + { + Debug.Assert((reason & EnterSpinLockReason.Wait) != 0 || reason == (reason & EnterSpinLockReason.OperationMask)); + Debug.Assert( + (reason & EnterSpinLockReason.Wait) == 0 || + (reason & EnterSpinLockReason.OperationMask) == EnterSpinLockReason.EnterAnyRead || + (reason & EnterSpinLockReason.OperationMask) == EnterSpinLockReason.EnterWrite || + (reason & EnterSpinLockReason.OperationMask) == EnterSpinLockReason.UpgradeToWrite); + + switch (reason) + { + default: + Debug.Assert( + (reason & EnterSpinLockReason.Wait) != 0 || + reason == EnterSpinLockReason.ExitAnyRead || + reason == EnterSpinLockReason.EnterRecursiveWrite || + reason == EnterSpinLockReason.ExitAnyWrite); + return false; + + case EnterSpinLockReason.EnterAnyRead: + return EnterForEnterAnyReadDeprioritizedCount != 0; + + case EnterSpinLockReason.EnterWrite: + Debug.Assert((GetEnterDeprioritizationStateChange(reason) & DeprioritizeEnterAnyWriteIncrement) == 0); + return EnterForEnterAnyWriteDeprioritizedCount != 0; + + case EnterSpinLockReason.UpgradeToWrite: + Debug.Assert((GetEnterDeprioritizationStateChange(reason) & DeprioritizeEnterAnyWriteIncrement) != 0); + return EnterForEnterAnyWriteDeprioritizedCount > 1; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool TryEnter() + { + return Interlocked.CompareExchange(ref _isLocked, 1, 0) == 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Enter(EnterSpinLockReason reason) + { + if (!TryEnter()) + { + EnterSpin(reason); + } + } + + private void EnterSpin(EnterSpinLockReason reason) + { + int deprioritizationStateChange = GetEnterDeprioritizationStateChange(reason); + if (deprioritizationStateChange != 0) + { + Interlocked.Add(ref _enterDeprioritizationState, deprioritizationStateChange); + } + + int processorCount = ProcessorCount; + for (int spinIndex = 0; ; spinIndex++) + { + if (spinIndex < LockSpinCount && processorCount > 1) + { + RuntimeThread.SpinWait(LockSpinCycles * (spinIndex + 1)); // Wait a few dozen instructions to let another processor release lock. + } + else if (spinIndex < (LockSpinCount + LockSleep0Count)) + { + RuntimeThread.Sleep(0); // Give up my quantum. + } + else + { + RuntimeThread.Sleep(1); // Give up my quantum. + } + + if (!IsEnterDeprioritized(reason)) + { + if (_isLocked == 0 && TryEnter()) + { + if (deprioritizationStateChange != 0) + { + Interlocked.Add(ref _enterDeprioritizationState, -deprioritizationStateChange); + } + return; + } + continue; + } + + // It's possible for an Enter thread to be deprioritized for an extended duration. It's undesirable for a + // deprioritized thread to keep waking up to spin despite a Sleep(1) when a large number of such threads are + // involved. After a threshold of Sleep(1)s, ignore the deprioritization and enter this lock to allow this + // thread to stop spinning and hopefully enter a proper wait state. + Debug.Assert( + reason == EnterSpinLockReason.EnterAnyRead || + reason == EnterSpinLockReason.EnterWrite || + reason == EnterSpinLockReason.UpgradeToWrite); + if (spinIndex >= (LockSpinCount + LockSleep0Count + DeprioritizedLockSleep1Count)) + { + reason |= EnterSpinLockReason.Wait; + spinIndex = -1; + } + } + } + + public void Exit() + { + Debug.Assert(_isLocked != 0, "Exiting spin lock that is not held"); + Volatile.Write(ref _isLocked, 0); + } + +#if DEBUG + public bool IsHeld => _isLocked != 0; +#endif + } + + [Flags] + private enum WaiterStates : byte + { + None = 0x0, + + // Used for quick check when there are no waiters + NoWaiters = 0x1, + + // Used to avoid signaling more than one waiter to wake up when only one can make progress, see WaitOnEvent + WriteWaiterSignaled = 0x2, + UpgradeableReadWaiterSignaled = 0x4 + // Write upgrade waiters are excluded because there can only be one at any given time + } + + private enum EnterSpinLockReason + { + EnterAnyRead = 0, + ExitAnyRead = 1, + EnterWrite = 2, + UpgradeToWrite = 3, + EnterRecursiveWrite = 4, + ExitAnyWrite = 5, + + OperationMask = 0x7, + + Wait = 0x8 + } + + private enum EnterLockType + { + Read, + UpgradeableRead, + Write, + UpgradeToWrite + } } } diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/SemaphoreFullException.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/SemaphoreFullException.cs new file mode 100644 index 0000000000..18558b19e0 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/SemaphoreFullException.cs @@ -0,0 +1,30 @@ +// 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.Runtime.Serialization; + +namespace System.Threading +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class SemaphoreFullException : SystemException + { + public SemaphoreFullException() : base(SR.Threading_SemaphoreFullException) + { + } + + public SemaphoreFullException(String message) : base(message) + { + } + + public SemaphoreFullException(String message, Exception innerException) : base(message, innerException) + { + } + + protected SemaphoreFullException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/SendOrPostCallback.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/SendOrPostCallback.cs new file mode 100644 index 0000000000..6692d35ab2 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/SendOrPostCallback.cs @@ -0,0 +1,8 @@ +// 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. + +namespace System.Threading +{ + public delegate void SendOrPostCallback(Object state); +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/SpinWait.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/SpinWait.cs similarity index 69% rename from external/corert/src/System.Private.CoreLib/src/System/Threading/SpinWait.cs rename to external/corefx/src/Common/src/CoreLib/System/Threading/SpinWait.cs index 1d1de2d11a..414ad1852f 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/SpinWait.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/SpinWait.cs @@ -45,7 +45,7 @@ namespace System.Threading /// /// /// encapsulates common spinning logic. On single-processor machines, yields are - /// always used instead of busy waits, and on computers with Intel� processors employing Hyper-Threading� + /// always used instead of busy waits, and on computers with Intel(R) processors employing Hyper-Threading /// technology, it helps to prevent hardware thread starvation. SpinWait encapsulates a good mixture of /// spinning and true yielding. /// @@ -63,16 +63,32 @@ namespace System.Threading /// threads must spin, each should use its own instance of SpinWait. /// /// - public struct SpinWait { // These constants determine the frequency of yields versus spinning. The // numbers may seem fairly arbitrary, but were derived with at least some // thought in the design document. I fully expect they will need to change // over time as we gain more experience with performance. - internal const int YIELD_THRESHOLD = 10; // When to switch over to a true yield. - internal const int SLEEP_0_EVERY_HOW_MANY_TIMES = 5; // After how many yields should we Sleep(0)? - internal const int SLEEP_1_EVERY_HOW_MANY_TIMES = 20; // After how many yields should we Sleep(1)? + internal const int YieldThreshold = 10; // When to switch over to a true yield. + private const int Sleep0EveryHowManyYields = 5; // After how many yields should we Sleep(0)? + internal const int DefaultSleep1Threshold = 20; // After how many yields should we Sleep(1) frequently? + + /// + /// A suggested number of spin iterations before doing a proper wait, such as waiting on an event that becomes signaled + /// when the resource becomes available. + /// + /// + /// These numbers were arrived at by experimenting with different numbers in various cases that currently use it. It's + /// only a suggested value and typically works well when the proper wait is something like an event. + /// + /// Spinning less can lead to early waiting and more context switching, spinning more can decrease latency but may use + /// up some CPU time unnecessarily. Depends on the situation too, for instance SemaphoreSlim uses more iterations + /// because the waiting there is currently a lot more expensive (involves more spinning, taking a lock, etc.). It also + /// depends on the likelihood of the spin being successful and how long the wait would be but those are not accounted + /// for here. + /// + internal static readonly int SpinCountforSpinBeforeWait = PlatformHelper.IsSingleProcessor ? 1 : 35; + internal const int Sleep1ThresholdForSpinBeforeWait = 40; // should be greater than SpinCountforSpinBeforeWait // The number of times we've spun already. private int _count; @@ -82,7 +98,12 @@ namespace System.Threading /// public int Count { - get { return _count; } + get => _count; + internal set + { + Debug.Assert(value >= 0); + _count = value; + } } /// @@ -95,10 +116,7 @@ namespace System.Threading /// On a single-CPU machine, always yields the processor. On machines with /// multiple CPUs, may yield after an unspecified number of calls. /// - public bool NextSpinWillYield - { - get { return _count > YIELD_THRESHOLD || PlatformHelper.IsSingleProcessor; } - } + public bool NextSpinWillYield => _count >= YieldThreshold || PlatformHelper.IsSingleProcessor; /// /// Performs a single spin. @@ -109,7 +127,27 @@ namespace System.Threading /// public void SpinOnce() { - if (NextSpinWillYield) + SpinOnce(DefaultSleep1Threshold); + } + + internal void SpinOnce(int sleep1Threshold) + { + Debug.Assert(sleep1Threshold >= YieldThreshold || PlatformHelper.IsSingleProcessor); // so that NextSpinWillYield behaves as requested + + // (_count - YieldThreshold) % 2 == 0: The purpose of this check is to interleave Thread.Yield/Sleep(0) with + // Thread.SpinWait. Otherwise, the following issues occur: + // - When there are no threads to switch to, Yield and Sleep(0) become no-op and it turns the spin loop into a + // busy-spin that may quickly reach the max spin count and cause the thread to enter a wait state, or may + // just busy-spin for longer than desired before a Sleep(1). Completing the spin loop too early can cause + // excessive context switcing if a wait follows, and entering the Sleep(1) stage too early can cause + // excessive delays. + // - If there are multiple threads doing Yield and Sleep(0) (typically from the same spin loop due to + // contention), they may switch between one another, delaying work that can make progress. + if (( + _count >= YieldThreshold && + (_count >= sleep1Threshold || (_count - YieldThreshold) % 2 == 0) + ) || + PlatformHelper.IsSingleProcessor) { // // We must yield. @@ -117,7 +155,7 @@ namespace System.Threading // We prefer to call Thread.Yield first, triggering a SwitchToThread. This // unfortunately doesn't consider all runnable threads on all OS SKUs. In // some cases, it may only consult the runnable threads whose ideal processor - // is the one currently executing code. Thus we ocassionally issue a call to + // is the one currently executing code. Thus we occasionally issue a call to // Sleep(0), which considers all runnable threads at equal priority. Even this // is insufficient since we may be spin waiting for lower priority threads to // execute; we therefore must call Sleep(1) once in a while too, which considers @@ -126,19 +164,21 @@ namespace System.Threading // configured to use the (default) coarse-grained system timer. // - int yieldsSoFar = (_count >= YIELD_THRESHOLD ? _count - YIELD_THRESHOLD : _count); - - if ((yieldsSoFar % SLEEP_1_EVERY_HOW_MANY_TIMES) == (SLEEP_1_EVERY_HOW_MANY_TIMES - 1)) + if (_count >= sleep1Threshold) { RuntimeThread.Sleep(1); } - else if ((yieldsSoFar % SLEEP_0_EVERY_HOW_MANY_TIMES) == (SLEEP_0_EVERY_HOW_MANY_TIMES - 1)) - { - RuntimeThread.Sleep(0); - } else { - RuntimeThread.Yield(); + int yieldsSoFar = _count >= YieldThreshold ? (_count - YieldThreshold) / 2 : _count; + if ((yieldsSoFar % Sleep0EveryHowManyYields) == (Sleep0EveryHowManyYields - 1)) + { + RuntimeThread.Sleep(0); + } + else + { + RuntimeThread.Yield(); + } } } else @@ -154,11 +194,24 @@ namespace System.Threading // number of spins we are willing to tolerate to reduce delay to the caller, // since we expect most callers will eventually block anyway. // - RuntimeThread.SpinWait(4 << _count); + // Also, cap the maximum spin count to a value such that many thousands of CPU cycles would not be wasted doing + // the equivalent of YieldProcessor(), as that that point SwitchToThread/Sleep(0) are more likely to be able to + // allow other useful work to run. Long YieldProcessor() loops can help to reduce contention, but Sleep(1) is + // usually better for that. + // + // RuntimeThread.OptimalMaxSpinWaitsPerSpinIteration: + // - See Thread::InitializeYieldProcessorNormalized(), which describes and calculates this value. + // + int n = RuntimeThread.OptimalMaxSpinWaitsPerSpinIteration; + if (_count <= 30 && (1 << _count) < n) + { + n = 1 << _count; + } + RuntimeThread.SpinWait(n); } // Finally, increment our spin counter. - _count = (_count == int.MaxValue ? YIELD_THRESHOLD : _count + 1); + _count = (_count == int.MaxValue ? YieldThreshold : _count + 1); } /// @@ -206,15 +259,15 @@ namespace System.Threading public static bool SpinUntil(Func condition, TimeSpan timeout) { // Validate the timeout - Int64 totalMilliseconds = (Int64)timeout.TotalMilliseconds; - if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue) + long totalMilliseconds = (long)timeout.TotalMilliseconds; + if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) { throw new System.ArgumentOutOfRangeException( nameof(timeout), timeout, SR.SpinWait_SpinUntil_TimeoutWrong); } // Call wait with the timeout milliseconds - return SpinUntil(condition, (int)timeout.TotalMilliseconds); + return SpinUntil(condition, (int)totalMilliseconds); } /// @@ -267,7 +320,7 @@ namespace System.Threading } /// - /// A helper class to get the number of preocessors, it updates the numbers of processors every sampling interval + /// A helper class to get the number of processors, it updates the numbers of processors every sampling interval. /// internal static class PlatformHelper { @@ -290,8 +343,8 @@ namespace System.Threading s_lastProcessorCountRefreshTicks = now; } - Debug.Assert(procCount > 0 && procCount <= 64, - "Processor count not within the expected range (1 - 64)."); + Debug.Assert(procCount > 0, + "Processor count should be greater than 0."); return procCount; } @@ -300,9 +353,7 @@ namespace System.Threading /// /// Gets whether the current machine has only a single processor. /// - internal static bool IsSingleProcessor - { - get { return ProcessorCount == 1; } - } + /// This typically does not change on a machine, so it's checked only once. + internal static readonly bool IsSingleProcessor = ProcessorCount == 1; } } diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/SynchronizationLockException.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/SynchronizationLockException.cs new file mode 100644 index 0000000000..e050e1539d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/SynchronizationLockException.cs @@ -0,0 +1,45 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: Wait(), Notify() or NotifyAll() was called from an unsynchronized +** block of code. +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System.Threading +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class SynchronizationLockException : SystemException + { + public SynchronizationLockException() + : base(SR.Arg_SynchronizationLockException) + { + HResult = HResults.COR_E_SYNCHRONIZATIONLOCK; + } + + public SynchronizationLockException(String message) + : base(message) + { + HResult = HResults.COR_E_SYNCHRONIZATIONLOCK; + } + + public SynchronizationLockException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_SYNCHRONIZATIONLOCK; + } + + protected SynchronizationLockException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/Tasks/TaskCanceledException.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/Tasks/TaskCanceledException.cs new file mode 100644 index 0000000000..a6ec030452 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/Tasks/TaskCanceledException.cs @@ -0,0 +1,87 @@ +// 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. + +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// +// +// An exception for task cancellations. +// +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +using System; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace System.Threading.Tasks +{ + /// + /// Represents an exception used to communicate task cancellation. + /// + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class TaskCanceledException : OperationCanceledException + { + [NonSerialized] + private readonly Task _canceledTask; // The task which has been canceled. + + /// + /// Initializes a new instance of the class. + /// + public TaskCanceledException() : base(SR.TaskCanceledException_ctor_DefaultMessage) + { + } + + /// + /// Initializes a new instance of the + /// class with a specified error message. + /// + /// The error message that explains the reason for the exception. + public TaskCanceledException(string message) : base(message) + { + } + + /// + /// Initializes a new instance of the + /// class with a specified error message and a reference to the inner exception that is the cause of + /// this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception. + public TaskCanceledException(string message, Exception innerException) : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the class + /// with a reference to the that has been canceled. + /// + /// A task that has been canceled. + public TaskCanceledException(Task task) : + base(SR.TaskCanceledException_ctor_DefaultMessage, task != null ? task.CancellationToken : new CancellationToken()) + { + _canceledTask = task; + } + + /// + /// Initializes a new instance of the + /// class with serialized data. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected TaskCanceledException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + + /// + /// Gets the task associated with this exception. + /// + /// + /// It is permissible for no Task to be associated with a + /// , in which case + /// this property will return null. + /// + public Task Task => _canceledTask; + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/TaskCompletionSource.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/Tasks/TaskCompletionSource.cs similarity index 89% rename from external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/TaskCompletionSource.cs rename to external/corefx/src/Common/src/CoreLib/System/Threading/Tasks/TaskCompletionSource.cs index 7744274f8c..992e9db767 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/TaskCompletionSource.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/Tasks/TaskCompletionSource.cs @@ -11,9 +11,12 @@ // // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -using System.Collections.Generic; +using System; using System.Diagnostics; +using System.Collections.Generic; +using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; +using System.Threading; // Disable the "reference to volatile field not treated as volatile" error. #pragma warning disable 0420 @@ -41,18 +44,18 @@ namespace System.Threading.Tasks /// and may be used from multiple threads concurrently. /// /// - /// The type of the result value assocatied with this The type of the result value associated with this . public class TaskCompletionSource { - private readonly Task m_task; + private readonly Task _task; /// /// Creates a . /// public TaskCompletionSource() { - m_task = new Task(); + _task = new Task(); } /// @@ -100,7 +103,7 @@ namespace System.Threading.Tasks /// public TaskCompletionSource(object state, TaskCreationOptions creationOptions) { - m_task = new Task(state, creationOptions); + _task = new Task(state, creationOptions); } @@ -116,10 +119,7 @@ namespace System.Threading.Tasks /// methods (and their "Try" variants) on this instance all result in the relevant state /// transitions on this underlying Task. /// - public Task Task - { - get { return m_task; } - } + public Task Task => _task; /// Spins until the underlying task is completed. /// This should only be called if the task is in the process of being completed by another thread. @@ -127,7 +127,7 @@ namespace System.Threading.Tasks { // Spin wait until the completion is finalized by another thread. var sw = new SpinWait(); - while (!m_task.IsCompleted) + while (!_task.IsCompleted) sw.SpinOnce(); } @@ -151,10 +151,10 @@ namespace System.Threading.Tasks /// The was disposed. public bool TrySetException(Exception exception) { - if (exception == null) throw new ArgumentNullException(nameof(exception)); + if (exception == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exception); - bool rval = m_task.TrySetException(exception); - if (!rval && !m_task.IsCompleted) SpinUntilCompleted(); + bool rval = _task.TrySetException(exception); + if (!rval && !_task.IsCompleted) SpinUntilCompleted(); return rval; } @@ -180,21 +180,21 @@ namespace System.Threading.Tasks /// The was disposed. public bool TrySetException(IEnumerable exceptions) { - if (exceptions == null) throw new ArgumentNullException(nameof(exceptions)); + if (exceptions == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exceptions); - LowLevelListWithIList defensiveCopy = new LowLevelListWithIList(); + List defensiveCopy = new List(); foreach (Exception e in exceptions) { if (e == null) - throw new ArgumentException(SR.TaskCompletionSourceT_TrySetException_NullException, nameof(exceptions)); + ThrowHelper.ThrowArgumentException(ExceptionResource.TaskCompletionSourceT_TrySetException_NullException, ExceptionArgument.exceptions); defensiveCopy.Add(e); } if (defensiveCopy.Count == 0) - throw new ArgumentException(SR.TaskCompletionSourceT_TrySetException_NoExceptions, nameof(exceptions)); + ThrowHelper.ThrowArgumentException(ExceptionResource.TaskCompletionSourceT_TrySetException_NoExceptions, ExceptionArgument.exceptions); - bool rval = m_task.TrySetException(defensiveCopy); - if (!rval && !m_task.IsCompleted) SpinUntilCompleted(); + bool rval = _task.TrySetException(defensiveCopy); + if (!rval && !_task.IsCompleted) SpinUntilCompleted(); return rval; } @@ -217,11 +217,11 @@ namespace System.Threading.Tasks /// The was disposed. public void SetException(Exception exception) { - if (exception == null) throw new ArgumentNullException(nameof(exception)); + if (exception == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exception); if (!TrySetException(exception)) { - throw new InvalidOperationException(SR.TaskT_TransitionToFinal_AlreadyCompleted); + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted); } } @@ -247,7 +247,7 @@ namespace System.Threading.Tasks { if (!TrySetException(exceptions)) { - throw new InvalidOperationException(SR.TaskT_TransitionToFinal_AlreadyCompleted); + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted); } } @@ -271,7 +271,7 @@ namespace System.Threading.Tasks /// The was disposed. public bool TrySetResult(TResult result) { - bool rval = m_task.TrySetResult(result); + bool rval = _task.TrySetResult(result); if (!rval) SpinUntilCompleted(); return rval; } @@ -295,7 +295,7 @@ namespace System.Threading.Tasks public void SetResult(TResult result) { if (!TrySetResult(result)) - throw new InvalidOperationException(SR.TaskT_TransitionToFinal_AlreadyCompleted); + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted); } /// @@ -321,8 +321,8 @@ namespace System.Threading.Tasks // Enables a token to be stored into the canceled task public bool TrySetCanceled(CancellationToken cancellationToken) { - bool rval = m_task.TrySetCanceled(cancellationToken); - if (!rval && !m_task.IsCompleted) SpinUntilCompleted(); + bool rval = _task.TrySetCanceled(cancellationToken); + if (!rval && !_task.IsCompleted) SpinUntilCompleted(); return rval; } @@ -343,7 +343,7 @@ namespace System.Threading.Tasks public void SetCanceled() { if (!TrySetCanceled()) - throw new InvalidOperationException(SR.TaskT_TransitionToFinal_AlreadyCompleted); + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted); } } } diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/Tasks/TaskExtensions.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/Tasks/TaskExtensions.cs new file mode 100644 index 0000000000..97f2013d79 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/Tasks/TaskExtensions.cs @@ -0,0 +1,48 @@ +// 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. + +namespace System.Threading.Tasks +{ + /// Provides a set of static methods for working with specific kinds of instances. + public static class TaskExtensions + { + /// Creates a proxy that represents the asynchronous operation of a . + /// The to unwrap. + /// A that represents the asynchronous operation of the provided . + public static Task Unwrap(this Task task) + { + if (task == null) + { + throw new ArgumentNullException(nameof(task)); + } + + // If the task hasn't completed or was faulted/canceled, wrap it in an unwrap promise. Otherwise, + // it completed successfully. Return its inner task to avoid unnecessary wrapping, or if the inner + // task is null, return a canceled task to match the same semantics as CreateUnwrapPromise. + return + !task.IsCompletedSuccessfully ? Task.CreateUnwrapPromise(task, lookForOce: false) : + task.Result ?? + Task.FromCanceled(new CancellationToken(true)); + } + + /// Creates a proxy that represents the asynchronous operation of a . + /// The to unwrap. + /// A that represents the asynchronous operation of the provided . + public static Task Unwrap(this Task> task) + { + if (task == null) + { + throw new ArgumentNullException(nameof(task)); + } + + // If the task hasn't completed or was faulted/canceled, wrap it in an unwrap promise. Otherwise, + // it completed successfully. Return its inner task to avoid unnecessary wrapping, or if the inner + // task is null, return a canceled task to match the same semantics as CreateUnwrapPromise. + return + !task.IsCompletedSuccessfully ? Task.CreateUnwrapPromise(task, lookForOce: false) : + task.Result ?? + Task.FromCanceled(new CancellationToken(true)); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/Tasks/TaskSchedulerException.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/Tasks/TaskSchedulerException.cs new file mode 100644 index 0000000000..85ec497219 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/Tasks/TaskSchedulerException.cs @@ -0,0 +1,78 @@ +// 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. + +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// +// +// An exception for task schedulers. +// +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +using System; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace System.Threading.Tasks +{ + /// + /// Represents an exception used to communicate an invalid operation by a + /// . + /// + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class TaskSchedulerException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public TaskSchedulerException() : base(SR.TaskSchedulerException_ctor_DefaultMessage) // + { + } + + /// + /// Initializes a new instance of the + /// class with a specified error message. + /// + /// The error message that explains the reason for the exception. + public TaskSchedulerException(string message) : base(message) + { + } + + /// + /// Initializes a new instance of the + /// class using the default error message and a reference to the inner exception that is the cause of + /// this exception. + /// + /// The exception that is the cause of the current exception. + public TaskSchedulerException(Exception innerException) + : base(SR.TaskSchedulerException_ctor_DefaultMessage, innerException) + { + } + + /// + /// Initializes a new instance of the + /// class with a specified error message and a reference to the inner exception that is the cause of + /// this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception. + public TaskSchedulerException(string message, Exception innerException) : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the + /// class with serialized data. + /// + /// The that holds + /// the serialized object data about the exception being thrown. + /// The that + /// contains contextual information about the source or destination. + protected TaskSchedulerException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/external/corert/src/Common/src/System/Threading/Tasks/TaskToApm.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/Tasks/TaskToApm.cs similarity index 94% rename from external/corert/src/Common/src/System/Threading/Tasks/TaskToApm.cs rename to external/corefx/src/Common/src/CoreLib/System/Threading/Tasks/TaskToApm.cs index ca42e22a44..add41f588e 100644 --- a/external/corert/src/Common/src/System/Threading/Tasks/TaskToApm.cs +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/Tasks/TaskToApm.cs @@ -2,11 +2,21 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +// Helper methods for using Tasks to implement the APM pattern. // -// Code is duplicated from CoreFX repo. Please keep it in sync +// Example usage, wrapping a Task-returning FooAsync method with Begin/EndFoo methods: +// +// public IAsyncResult BeginFoo(..., AsyncCallback callback, object state) +// { +// Task t = FooAsync(...); +// return TaskToApm.Begin(t, callback, state); +// } +// public int EndFoo(IAsyncResult asyncResult) +// { +// return TaskToApm.End(asyncResult); +// } using System.Diagnostics; -using System.IO; namespace System.Threading.Tasks { @@ -34,10 +44,7 @@ namespace System.Threading.Tasks { // Synchronous completion. asyncResult = new TaskWrapperAsyncResult(task, state, completedSynchronously: true); - if (callback != null) - { - callback(asyncResult); - } + callback?.Invoke(asyncResult); } else { diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/Tasks/ValueTask.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/Tasks/ValueTask.cs new file mode 100644 index 0000000000..de9b016328 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/Tasks/ValueTask.cs @@ -0,0 +1,178 @@ +// 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.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.Threading.Tasks +{ + /// + /// Provides a value type that wraps a and a , + /// only one of which is used. + /// + /// The type of the result. + /// + /// + /// Methods may return an instance of this value type when it's likely that the result of their + /// operations will be available synchronously and when the method is expected to be invoked so + /// frequently that the cost of allocating a new for each call will + /// be prohibitive. + /// + /// + /// There are tradeoffs to using a instead of a . + /// For example, while a can help avoid an allocation in the case where the + /// successful result is available synchronously, it also contains two fields whereas a + /// as a reference type is a single field. This means that a method call ends up returning two fields worth of + /// data instead of one, which is more data to copy. It also means that if a method that returns one of these + /// is awaited within an async method, the state machine for that async method will be larger due to needing + /// to store the struct that's two fields instead of a single reference. + /// + /// + /// Further, for uses other than consuming the result of an asynchronous operation via await, + /// can lead to a more convoluted programming model, which can in turn actually + /// lead to more allocations. For example, consider a method that could return either a + /// with a cached task as a common result or a . If the consumer of the result + /// wants to use it as a , such as to use with in methods like Task.WhenAll and Task.WhenAny, + /// the would first need to be converted into a using + /// , which leads to an allocation that would have been avoided if a cached + /// had been used in the first place. + /// + /// + /// As such, the default choice for any asynchronous method should be to return a or + /// . Only if performance analysis proves it worthwhile should a + /// be used instead of . There is no non-generic version of + /// as the Task.CompletedTask property may be used to hand back a successfully completed singleton in the case where + /// a -returning method completes synchronously and successfully. + /// + /// + [AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder<>))] + [StructLayout(LayoutKind.Auto)] + public readonly struct ValueTask : IEquatable> + { + /// The task to be used if the operation completed asynchronously or if it completed synchronously but non-successfully. + internal readonly Task _task; + /// The result to be used if the operation completed successfully synchronously. + internal readonly TResult _result; + + /// Initialize the with the result of the successful operation. + /// The result. + public ValueTask(TResult result) + { + _task = null; + _result = result; + } + + /// + /// Initialize the with a that represents the operation. + /// + /// The task. + public ValueTask(Task task) + { + if (task == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.task); + } + + _task = task; + _result = default(TResult); + } + + /// Returns the hash code for this instance. + public override int GetHashCode() => + _task != null ? _task.GetHashCode() : + _result != null ? _result.GetHashCode() : + 0; + + /// Returns a value indicating whether this value is equal to a specified . + public override bool Equals(object obj) => + obj is ValueTask && + Equals((ValueTask)obj); + + /// Returns a value indicating whether this value is equal to a specified value. + public bool Equals(ValueTask other) => + _task != null || other._task != null ? + _task == other._task : + EqualityComparer.Default.Equals(_result, other._result); + + /// Returns a value indicating whether two values are equal. + public static bool operator==(ValueTask left, ValueTask right) => + left.Equals(right); + + /// Returns a value indicating whether two values are not equal. + public static bool operator!=(ValueTask left, ValueTask right) => + !left.Equals(right); + + /// + /// Gets a object to represent this ValueTask. It will + /// either return the wrapped task object if one exists, or it'll manufacture a new + /// task object to represent the result. + /// + public Task AsTask() => + // Return the task if we were constructed from one, otherwise manufacture one. We don't + // cache the generated task into _task as it would end up changing both equality comparison + // and the hash code we generate in GetHashCode. + _task ?? AsyncTaskMethodBuilder.GetTaskForResult(_result); + + internal Task AsTaskExpectNonNull() => + // Return the task if we were constructed from one, otherwise manufacture one. + // Unlike AsTask(), this method is called only when we expect _task to be non-null, + // and thus we don't want GetTaskForResult inlined. + _task ?? GetTaskForResultNoInlining(); + + [MethodImpl(MethodImplOptions.NoInlining)] + private Task GetTaskForResultNoInlining() => AsyncTaskMethodBuilder.GetTaskForResult(_result); + + /// Gets whether the represents a completed operation. + public bool IsCompleted => _task == null || _task.IsCompleted; + + /// Gets whether the represents a successfully completed operation. + public bool IsCompletedSuccessfully => _task == null || _task.IsCompletedSuccessfully; + + /// Gets whether the represents a failed operation. + public bool IsFaulted => _task != null && _task.IsFaulted; + + /// Gets whether the represents a canceled operation. + public bool IsCanceled => _task != null && _task.IsCanceled; + + /// Gets the result. + public TResult Result => _task == null ? _result : _task.GetAwaiter().GetResult(); + + /// Gets an awaiter for this value. + public ValueTaskAwaiter GetAwaiter() => new ValueTaskAwaiter(this); + + /// Configures an awaiter for this value. + /// + /// true to attempt to marshal the continuation back to the captured context; otherwise, false. + /// + public ConfiguredValueTaskAwaitable ConfigureAwait(bool continueOnCapturedContext) => + new ConfiguredValueTaskAwaitable(this, continueOnCapturedContext); + + /// Gets a string-representation of this . + public override string ToString() + { + if (_task != null) + { + return _task.IsCompletedSuccessfully && _task.Result != null ? + _task.Result.ToString() : + string.Empty; + } + else + { + return _result != null ? + _result.ToString() : + string.Empty; + } + } + + // TODO https://github.com/dotnet/corefx/issues/22171: + // Remove CreateAsyncMethodBuilder once the C# compiler relies on the AsyncBuilder attribute. + + /// Creates a method builder for use with an async method. + /// The created builder. + [EditorBrowsable(EditorBrowsableState.Never)] // intended only for compiler consumption + public static AsyncValueTaskMethodBuilder CreateAsyncMethodBuilder() => AsyncValueTaskMethodBuilder.Create(); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/ThreadAbortException.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/ThreadAbortException.cs new file mode 100644 index 0000000000..360e84d256 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/ThreadAbortException.cs @@ -0,0 +1,37 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: An exception class which is thrown into a thread to cause it to +** abort. This is a special non-catchable exception and results in +** the thread's death. This is thrown by the VM only and can NOT be +** thrown by any user thread, and subclassing this is useless. +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System.Threading +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class ThreadAbortException : SystemException + { + internal ThreadAbortException() + { + HResult = HResults.COR_E_THREADABORTED; + } + + public object ExceptionState => null; + + internal ThreadAbortException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/ThreadPriority.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/ThreadPriority.cs new file mode 100644 index 0000000000..3b34bd5eac --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/ThreadPriority.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.Threading +{ + public enum ThreadPriority + { + /*========================================================================= + ** Constants for thread priorities. + =========================================================================*/ + Lowest = 0, + BelowNormal = 1, + Normal = 2, + AboveNormal = 3, + Highest = 4 + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/ThreadStart.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/ThreadStart.cs new file mode 100644 index 0000000000..5532539fc7 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/ThreadStart.cs @@ -0,0 +1,18 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: This class is a Delegate which defines the start method +** for starting a thread. That method must match this delegate. +** +** +=============================================================================*/ + +namespace System.Threading +{ + public delegate void ThreadStart(); +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/ThreadStartException.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/ThreadStartException.cs new file mode 100644 index 0000000000..5172555418 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/ThreadStartException.cs @@ -0,0 +1,30 @@ +// 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.Runtime.Serialization; + +namespace System.Threading +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class ThreadStartException : SystemException + { + internal ThreadStartException() + : base(SR.Arg_ThreadStartException) + { + HResult = HResults.COR_E_THREADSTART; + } + + internal ThreadStartException(Exception reason) + : base(SR.Arg_ThreadStartException, reason) + { + HResult = HResults.COR_E_THREADSTART; + } + + private ThreadStartException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/ThreadState.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/ThreadState.cs new file mode 100644 index 0000000000..4bf3b5184d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/ThreadState.cs @@ -0,0 +1,24 @@ +// 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. + +namespace System.Threading +{ + [Flags] + public enum ThreadState + { + /*========================================================================= + ** Constants for thread states. + =========================================================================*/ + Running = 0, + StopRequested = 1, + SuspendRequested = 2, + Background = 4, + Unstarted = 8, + Stopped = 16, + WaitSleepJoin = 32, + Suspended = 64, + AbortRequested = 128, + Aborted = 256 + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/ThreadStateException.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/ThreadStateException.cs new file mode 100644 index 0000000000..6fc06a8516 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/ThreadStateException.cs @@ -0,0 +1,46 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: An exception class to indicate that the Thread class is in an +** invalid state for the method. +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System.Threading +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class ThreadStateException : SystemException + { + public ThreadStateException() + : base(SR.Arg_ThreadStateException) + { + HResult = HResults.COR_E_THREADSTATE; + } + + public ThreadStateException(String message) + : base(message) + { + HResult = HResults.COR_E_THREADSTATE; + } + + public ThreadStateException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_THREADSTATE; + } + + protected ThreadStateException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/Timeout.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/Timeout.cs new file mode 100644 index 0000000000..df1ea5f2bc --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/Timeout.cs @@ -0,0 +1,20 @@ +// 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.Threading; +using System; + +namespace System.Threading +{ + // A constant used by methods that take a timeout (Object.Wait, Thread.Sleep + // etc) to indicate that no timeout should occur. + // + public static class Timeout + { + public static readonly TimeSpan InfiniteTimeSpan = new TimeSpan(0, 0, 0, 0, Timeout.Infinite); + + public const int Infinite = -1; + internal const uint UnsignedInfinite = unchecked((uint)-1); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/TimeoutHelper.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/TimeoutHelper.cs new file mode 100644 index 0000000000..c96a4d00d6 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/TimeoutHelper.cs @@ -0,0 +1,54 @@ +// 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.Diagnostics; + +namespace System.Threading +{ + /// + /// A helper class to capture a start time using Environment.TickCout as a time in milliseconds, also updates a given timeout bu subtracting the current time from + /// the start time + /// + internal static class TimeoutHelper + { + /// + /// Returns the Environment.TickCount as a start time in milliseconds as a uint, TickCount tools over from positive to negative every ~ 25 days + /// then ~25 days to back to positive again, uint is sued to ignore the sign and double the range to 50 days + /// + /// + public static uint GetTime() + { + return (uint)Environment.TickCount; + } + + /// + /// Helper function to measure and update the elapsed time + /// + /// The first time (in milliseconds) observed when the wait started + /// The original wait timeout in milliseconds + /// The new wait time in milliseconds, -1 if the time expired + public static int UpdateTimeOut(uint startTime, int originalWaitMillisecondsTimeout) + { + // The function must be called in case the time out is not infinite + Debug.Assert(originalWaitMillisecondsTimeout != Timeout.Infinite); + + uint elapsedMilliseconds = (GetTime() - startTime); + + // Check the elapsed milliseconds is greater than max int because this property is uint + if (elapsedMilliseconds > int.MaxValue) + { + return 0; + } + + // Subtract the elapsed time from the current wait time + int currentWaitTimeout = originalWaitMillisecondsTimeout - (int)elapsedMilliseconds; ; + if (currentWaitTimeout <= 0) + { + return 0; + } + + return currentWaitTimeout; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Threading/WaitHandleCannotBeOpenedException.cs b/external/corefx/src/Common/src/CoreLib/System/Threading/WaitHandleCannotBeOpenedException.cs new file mode 100644 index 0000000000..e60e46d2be --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Threading/WaitHandleCannotBeOpenedException.cs @@ -0,0 +1,32 @@ +// 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.Runtime.Serialization; + +namespace System.Threading +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class WaitHandleCannotBeOpenedException : ApplicationException + { + public WaitHandleCannotBeOpenedException() : base(SR.Threading_WaitHandleCannotBeOpenedException) + { + HResult = HResults.COR_E_WAITHANDLECANNOTBEOPENED; + } + + public WaitHandleCannotBeOpenedException(String message) : base(message) + { + HResult = HResults.COR_E_WAITHANDLECANNOTBEOPENED; + } + + public WaitHandleCannotBeOpenedException(String message, Exception innerException) : base(message, innerException) + { + HResult = HResults.COR_E_WAITHANDLECANNOTBEOPENED; + } + + protected WaitHandleCannotBeOpenedException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/TimeSpan.cs b/external/corefx/src/Common/src/CoreLib/System/TimeSpan.cs similarity index 79% rename from external/corert/src/System.Private.CoreLib/src/System/TimeSpan.cs rename to external/corefx/src/Common/src/CoreLib/System/TimeSpan.cs index e51f3a96b5..4293ed505b 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/TimeSpan.cs +++ b/external/corefx/src/Common/src/CoreLib/System/TimeSpan.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. @@ -6,7 +6,6 @@ using System.Text; using System; using System.Runtime; using System.Runtime.CompilerServices; -using System.Diagnostics.Contracts; using System.Globalization; namespace System @@ -28,7 +27,8 @@ namespace System // details of this type should change, or new fields added, we need to remember to add // an appropriate custom ILMarshaler to keep WInRT interop scenarios enabled. // - public struct TimeSpan : IComparable, IComparable, IEquatable, IFormattable + [Serializable] + public struct TimeSpan : IComparable, IComparable, IEquatable, IFormattable, ISpanFormattable { public const long TicksPerMillisecond = 10000; private const double MillisecondsPerTick = 1.0 / TicksPerMillisecond; @@ -65,7 +65,7 @@ namespace System // internal so that DateTime doesn't have to call an extra get // method for some arithmetic operations. - internal long _ticks; + internal long _ticks; // Do not rename (binary serialization) public TimeSpan(long ticks) { @@ -206,7 +206,6 @@ namespace System { if (Ticks == TimeSpan.MinValue.Ticks) throw new OverflowException(SR.Overflow_Duration); - Contract.EndContractBlock(); return new TimeSpan(_ticks >= 0 ? _ticks : -_ticks); } @@ -243,7 +242,6 @@ namespace System { if (Double.IsNaN(value)) throw new ArgumentException(SR.Arg_CannotBeNaN); - Contract.EndContractBlock(); double tmp = value * scale; double millis = tmp + (value >= 0 ? 0.5 : -0.5); if ((millis > Int64.MaxValue / TicksPerMillisecond) || (millis < Int64.MinValue / TicksPerMillisecond)) @@ -265,7 +263,6 @@ namespace System { if (Ticks == TimeSpan.MinValue.Ticks) throw new OverflowException(SR.Overflow_NegateTwosCompNum); - Contract.EndContractBlock(); return new TimeSpan(-_ticks); } @@ -315,53 +312,138 @@ namespace System } public static TimeSpan Parse(String s) { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); /* Constructs a TimeSpan from a string. Leading and trailing white space characters are allowed. */ return TimeSpanParse.Parse(s, null); } public static TimeSpan Parse(String input, IFormatProvider formatProvider) + { + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + return TimeSpanParse.Parse(input, formatProvider); + } + public static TimeSpan Parse(ReadOnlySpan input, IFormatProvider formatProvider = null) { return TimeSpanParse.Parse(input, formatProvider); } public static TimeSpan ParseExact(String input, String format, IFormatProvider formatProvider) { + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format); return TimeSpanParse.ParseExact(input, format, formatProvider, TimeSpanStyles.None); } public static TimeSpan ParseExact(String input, String[] formats, IFormatProvider formatProvider) { + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None); } public static TimeSpan ParseExact(String input, String format, IFormatProvider formatProvider, TimeSpanStyles styles) + { + ValidateStyles(styles, nameof(styles)); + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format); + return TimeSpanParse.ParseExact(input, format, formatProvider, styles); + } + + public static TimeSpan ParseExact(ReadOnlySpan input, ReadOnlySpan format, IFormatProvider formatProvider, TimeSpanStyles styles = TimeSpanStyles.None) { ValidateStyles(styles, nameof(styles)); return TimeSpanParse.ParseExact(input, format, formatProvider, styles); } public static TimeSpan ParseExact(String input, String[] formats, IFormatProvider formatProvider, TimeSpanStyles styles) + { + ValidateStyles(styles, nameof(styles)); + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, styles); + } + public static TimeSpan ParseExact(ReadOnlySpan input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles = TimeSpanStyles.None) { ValidateStyles(styles, nameof(styles)); return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, styles); } public static Boolean TryParse(String s, out TimeSpan result) { + if (s == null) + { + result = default(TimeSpan); + return false; + } return TimeSpanParse.TryParse(s, null, out result); } + public static bool TryParse(ReadOnlySpan s, out TimeSpan result) + { + return TimeSpanParse.TryParse(s, null, out result); + } + public static Boolean TryParse(String input, IFormatProvider formatProvider, out TimeSpan result) + { + if (input == null) + { + result = default(TimeSpan); + return false; + } + return TimeSpanParse.TryParse(input, formatProvider, out result); + } + public static bool TryParse(ReadOnlySpan input, IFormatProvider formatProvider, out TimeSpan result) { return TimeSpanParse.TryParse(input, formatProvider, out result); } public static Boolean TryParseExact(String input, String format, IFormatProvider formatProvider, out TimeSpan result) + { + if (input == null || format == null) + { + result = default; + return false; + } + return TimeSpanParse.TryParseExact(input, format, formatProvider, TimeSpanStyles.None, out result); + } + + public static bool TryParseExact(ReadOnlySpan input, ReadOnlySpan format, IFormatProvider formatProvider, out TimeSpan result) { return TimeSpanParse.TryParseExact(input, format, formatProvider, TimeSpanStyles.None, out result); } public static Boolean TryParseExact(String input, String[] formats, IFormatProvider formatProvider, out TimeSpan result) { + if (input == null) + { + result = default(TimeSpan); + return false; + } return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None, out result); } + public static bool TryParseExact(ReadOnlySpan input, string[] formats, IFormatProvider formatProvider, out TimeSpan result) + { + return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None, out result); + } + public static Boolean TryParseExact(String input, String format, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result) + { + ValidateStyles(styles, nameof(styles)); + if (input == null || format == null) + { + result = default; + return false; + } + + return TimeSpanParse.TryParseExact(input, format, formatProvider, styles, out result); + } + + public static bool TryParseExact(ReadOnlySpan input, ReadOnlySpan format, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result) { ValidateStyles(styles, nameof(styles)); return TimeSpanParse.TryParseExact(input, format, formatProvider, styles, out result); } public static Boolean TryParseExact(String input, String[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result) + { + ValidateStyles(styles, nameof(styles)); + if (input == null) + { + result = default(TimeSpan); + return false; + } + return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, styles, out result); + } + + public static bool TryParseExact(ReadOnlySpan input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result) { ValidateStyles(styles, nameof(styles)); return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, styles, out result); @@ -378,6 +460,11 @@ namespace System { return TimeSpanFormat.Format(this, format, formatProvider); } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider formatProvider = null) + { + return TimeSpanFormat.TryFormat(this, destination, out charsWritten, format, formatProvider); + } #endregion public static TimeSpan operator -(TimeSpan t) @@ -440,7 +527,7 @@ namespace System // Using floating-point arithmetic directly means that infinities can be returned, which is reasonable // if we consider TimeSpan.FromHours(1) / TimeSpan.Zero asks how many zero-second intervals there are in - // an hour for which ∞ is the mathematic correct answer. Having TimeSpan.Zero / TimeSpan.Zero return NaN + // an hour for which infinity is the mathematic correct answer. Having TimeSpan.Zero / TimeSpan.Zero return NaN // is perhaps less useful, but no less useful than an exception. public static double operator /(TimeSpan t1, TimeSpan t2) => t1.Ticks / (double)t2.Ticks; diff --git a/external/corefx/src/Common/src/CoreLib/System/TimeZone.cs b/external/corefx/src/Common/src/CoreLib/System/TimeZone.cs new file mode 100644 index 0000000000..d4059babfc --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/TimeZone.cs @@ -0,0 +1,280 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: +** This class is used to represent a TimeZone. It +** has methods for converting a DateTime to UTC from local time +** and to local time from UTC and methods for getting the +** standard name and daylight name of the time zone. +** +** The only TimeZone that we support in version 1 is the +** CurrentTimeZone as determined by the system timezone. +** +** +============================================================*/ + +using System; +using System.Text; +using System.Threading; +using System.Collections; +using System.Globalization; + +namespace System +{ + [Obsolete("System.TimeZone has been deprecated. Please investigate the use of System.TimeZoneInfo instead.")] + public abstract class TimeZone + { + private static volatile TimeZone currentTimeZone = null; + + // Private object for locking instead of locking on a public type for SQL reliability work. + private static Object s_InternalSyncObject; + private static Object InternalSyncObject + { + get + { + if (s_InternalSyncObject == null) + { + Object o = new Object(); + Interlocked.CompareExchange(ref s_InternalSyncObject, o, null); + } + return s_InternalSyncObject; + } + } + + + protected TimeZone() + { + } + + public static TimeZone CurrentTimeZone + { + get + { + //Grabbing the cached value is required at the top of this function so that + //we don't incur a race condition with the ResetTimeZone method below. + TimeZone tz = currentTimeZone; + if (tz == null) + { + lock (InternalSyncObject) + { + if (currentTimeZone == null) + { + currentTimeZone = new CurrentSystemTimeZone(); + } + tz = currentTimeZone; + } + } + return (tz); + } + } + + //This method is called by CultureInfo.ClearCachedData in response to control panel + //change events. It must be synchronized because otherwise there is a race condition + //with the CurrentTimeZone property above. + internal static void ResetTimeZone() + { + if (currentTimeZone != null) + { + lock (InternalSyncObject) + { + currentTimeZone = null; + } + } + } + + public abstract String StandardName + { + get; + } + + public abstract String DaylightName + { + get; + } + + public abstract TimeSpan GetUtcOffset(DateTime time); + + // + // Converts the specified datatime to the Universal time base on the current timezone + // + public virtual DateTime ToUniversalTime(DateTime time) + { + if (time.Kind == DateTimeKind.Utc) + { + return time; + } + long tickCount = time.Ticks - GetUtcOffset(time).Ticks; + if (tickCount > DateTime.MaxTicks) + { + return new DateTime(DateTime.MaxTicks, DateTimeKind.Utc); + } + if (tickCount < DateTime.MinTicks) + { + return new DateTime(DateTime.MinTicks, DateTimeKind.Utc); + } + return new DateTime(tickCount, DateTimeKind.Utc); + } + + // + // Convert the specified datetime value from UTC to the local time based on the time zone. + // + public virtual DateTime ToLocalTime(DateTime time) + { + if (time.Kind == DateTimeKind.Local) + { + return time; + } + Boolean isAmbiguousLocalDst = false; + Int64 offset = ((CurrentSystemTimeZone)(TimeZone.CurrentTimeZone)).GetUtcOffsetFromUniversalTime(time, ref isAmbiguousLocalDst); + return new DateTime(time.Ticks + offset, DateTimeKind.Local, isAmbiguousLocalDst); + } + + // Return an array of DaylightTime which reflects the daylight saving periods in a particular year. + // We currently only support having one DaylightSavingTime per year. + // If daylight saving time is not used in this timezone, null will be returned. + public abstract DaylightTime GetDaylightChanges(int year); + + public virtual bool IsDaylightSavingTime(DateTime time) + { + return (IsDaylightSavingTime(time, GetDaylightChanges(time.Year))); + } + + // Check if the specified time is in a daylight saving time. Allows the user to + // specify the array of Daylight Saving Times. + public static bool IsDaylightSavingTime(DateTime time, DaylightTime daylightTimes) + { + return CalculateUtcOffset(time, daylightTimes) != TimeSpan.Zero; + } + + // + // NOTENOTE: Implementation detail + // In the transition from standard time to daylight saving time, + // if we convert local time to Universal time, we can have the + // following (take PST as an example): + // Local Universal UTC Offset + // ----- --------- ---------- + // 01:00AM 09:00 -8:00 + // 02:00 (=> 03:00) 10:00 -8:00 [This time doesn't actually exist, but it can be created from DateTime] + // 03:00 10:00 -7:00 + // 04:00 11:00 -7:00 + // 05:00 12:00 -7:00 + // + // So from 02:00 - 02:59:59, we should return the standard offset, instead of the daylight saving offset. + // + // In the transition from daylight saving time to standard time, + // if we convert local time to Universal time, we can have the + // following (take PST as an example): + // Local Universal UTC Offset + // ----- --------- ---------- + // 01:00AM 08:00 -7:00 + // 02:00 (=> 01:00) 09:00 -8:00 + // 02:00 10:00 -8:00 + // 03:00 11:00 -8:00 + // 04:00 12:00 -8:00 + // + // So in this case, the 02:00 does exist after the first 2:00 rolls back to 01:00. We don't need to special case this. + // But note that there are two 01:00 in the local time. + + // + // And imagine if the daylight saving offset is negative (although this does not exist in real life) + // In the transition from standard time to daylight saving time, + // if we convert local time to Universal time, we can have the + // following (take PST as an example, but the daylight saving offset is -01:00): + // Local Universal UTC Offset + // ----- --------- ---------- + // 01:00AM 09:00 -8:00 + // 02:00 (=> 01:00) 10:00 -9:00 + // 02:00 11:00 -9:00 + // 03:00 12:00 -9:00 + // 04:00 13:00 -9:00 + // 05:00 14:00 -9:00 + // + // So in this case, the 02:00 does exist after the first 2:00 rolls back to 01:00. We don't need to special case this. + // + // In the transition from daylight saving time to standard time, + // if we convert local time to Universal time, we can have the + // following (take PST as an example, daylight saving offset is -01:00): + // + // Local Universal UTC Offset + // ----- --------- ---------- + // 01:00AM 10:00 -9:00 + // 02:00 (=> 03:00) 11:00 -9:00 + // 03:00 11:00 -8:00 + // 04:00 12:00 -8:00 + // 05:00 13:00 -8:00 + // 06:00 14:00 -8:00 + // + // So from 02:00 - 02:59:59, we should return the daylight saving offset, instead of the standard offset. + // + internal static TimeSpan CalculateUtcOffset(DateTime time, DaylightTime daylightTimes) + { + if (daylightTimes == null) + { + return TimeSpan.Zero; + } + DateTimeKind kind = time.Kind; + if (kind == DateTimeKind.Utc) + { + return TimeSpan.Zero; + } + + DateTime startTime; + DateTime endTime; + + // startTime and endTime represent the period from either the start of DST to the end and includes the + // potentially overlapped times + startTime = daylightTimes.Start + daylightTimes.Delta; + endTime = daylightTimes.End; + + // For normal time zones, the ambiguous hour is the last hour of daylight saving when you wind the + // clock back. It is theoretically possible to have a positive delta, (which would really be daylight + // reduction time), where you would have to wind the clock back in the begnning. + DateTime ambiguousStart; + DateTime ambiguousEnd; + if (daylightTimes.Delta.Ticks > 0) + { + ambiguousStart = endTime - daylightTimes.Delta; + ambiguousEnd = endTime; + } + else + { + ambiguousStart = startTime; + ambiguousEnd = startTime - daylightTimes.Delta; + } + + Boolean isDst = false; + if (startTime > endTime) + { + // In southern hemisphere, the daylight saving time starts later in the year, and ends in the beginning of next year. + // Note, the summer in the southern hemisphere begins late in the year. + if (time >= startTime || time < endTime) + { + isDst = true; + } + } + else if (time >= startTime && time < endTime) + { + // In northern hemisphere, the daylight saving time starts in the middle of the year. + isDst = true; + } + + // If this date was previously converted from a UTC date and we were able to detect that the local + // DateTime would be ambiguous, this data is stored in the DateTime to resolve this ambiguity. + if (isDst && time >= ambiguousStart && time < ambiguousEnd) + { + isDst = time.IsAmbiguousDaylightSavingTime(); + } + + if (isDst) + { + return daylightTimes.Delta; + } + return TimeSpan.Zero; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/TimeZoneInfo.AdjustmentRule.cs b/external/corefx/src/Common/src/CoreLib/System/TimeZoneInfo.AdjustmentRule.cs new file mode 100644 index 0000000000..0e949a30ec --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/TimeZoneInfo.AdjustmentRule.cs @@ -0,0 +1,248 @@ +// 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.Runtime.Serialization; + +namespace System +{ + public sealed partial class TimeZoneInfo + { + [Serializable] + public sealed class AdjustmentRule : IEquatable, ISerializable, IDeserializationCallback + { + private readonly DateTime _dateStart; + private readonly DateTime _dateEnd; + private readonly TimeSpan _daylightDelta; + private readonly TransitionTime _daylightTransitionStart; + private readonly TransitionTime _daylightTransitionEnd; + private readonly TimeSpan _baseUtcOffsetDelta; // delta from the default Utc offset (utcOffset = defaultUtcOffset + _baseUtcOffsetDelta) + private readonly bool _noDaylightTransitions; + + public DateTime DateStart => _dateStart; + + public DateTime DateEnd => _dateEnd; + + public TimeSpan DaylightDelta => _daylightDelta; + + public TransitionTime DaylightTransitionStart => _daylightTransitionStart; + + public TransitionTime DaylightTransitionEnd => _daylightTransitionEnd; + + internal TimeSpan BaseUtcOffsetDelta => _baseUtcOffsetDelta; + + /// + /// Gets a value indicating that this AdjustmentRule fixes the time zone offset + /// from DateStart to DateEnd without any daylight transitions in between. + /// + internal bool NoDaylightTransitions => _noDaylightTransitions; + + internal bool HasDaylightSaving => + DaylightDelta != TimeSpan.Zero || + (DaylightTransitionStart != default(TransitionTime) && DaylightTransitionStart.TimeOfDay != DateTime.MinValue) || + (DaylightTransitionEnd != default(TransitionTime) && DaylightTransitionEnd.TimeOfDay != DateTime.MinValue.AddMilliseconds(1)); + + public bool Equals(AdjustmentRule other) => + other != null && + _dateStart == other._dateStart && + _dateEnd == other._dateEnd && + _daylightDelta == other._daylightDelta && + _baseUtcOffsetDelta == other._baseUtcOffsetDelta && + _daylightTransitionEnd.Equals(other._daylightTransitionEnd) && + _daylightTransitionStart.Equals(other._daylightTransitionStart); + + public override int GetHashCode() => _dateStart.GetHashCode(); + + private AdjustmentRule( + DateTime dateStart, + DateTime dateEnd, + TimeSpan daylightDelta, + TransitionTime daylightTransitionStart, + TransitionTime daylightTransitionEnd, + TimeSpan baseUtcOffsetDelta, + bool noDaylightTransitions) + { + ValidateAdjustmentRule(dateStart, dateEnd, daylightDelta, + daylightTransitionStart, daylightTransitionEnd, noDaylightTransitions); + + _dateStart = dateStart; + _dateEnd = dateEnd; + _daylightDelta = daylightDelta; + _daylightTransitionStart = daylightTransitionStart; + _daylightTransitionEnd = daylightTransitionEnd; + _baseUtcOffsetDelta = baseUtcOffsetDelta; + _noDaylightTransitions = noDaylightTransitions; + } + + public static AdjustmentRule CreateAdjustmentRule( + DateTime dateStart, + DateTime dateEnd, + TimeSpan daylightDelta, + TransitionTime daylightTransitionStart, + TransitionTime daylightTransitionEnd) + { + return new AdjustmentRule( + dateStart, + dateEnd, + daylightDelta, + daylightTransitionStart, + daylightTransitionEnd, + baseUtcOffsetDelta: TimeSpan.Zero, + noDaylightTransitions: false); + } + + internal static AdjustmentRule CreateAdjustmentRule( + DateTime dateStart, + DateTime dateEnd, + TimeSpan daylightDelta, + TransitionTime daylightTransitionStart, + TransitionTime daylightTransitionEnd, + TimeSpan baseUtcOffsetDelta, + bool noDaylightTransitions) + { + return new AdjustmentRule( + dateStart, + dateEnd, + daylightDelta, + daylightTransitionStart, + daylightTransitionEnd, + baseUtcOffsetDelta, + noDaylightTransitions); + } + + // + // When Windows sets the daylight transition start Jan 1st at 12:00 AM, it means the year starts with the daylight saving on. + // We have to special case this value and not adjust it when checking if any date is in the daylight saving period. + // + internal bool IsStartDateMarkerForBeginningOfYear() => + !NoDaylightTransitions && + DaylightTransitionStart.Month == 1 && DaylightTransitionStart.Day == 1 && DaylightTransitionStart.TimeOfDay.Hour == 0 && + DaylightTransitionStart.TimeOfDay.Minute == 0 && DaylightTransitionStart.TimeOfDay.Second == 0 && + _dateStart.Year == _dateEnd.Year; + + // + // When Windows sets the daylight transition end Jan 1st at 12:00 AM, it means the year ends with the daylight saving on. + // We have to special case this value and not adjust it when checking if any date is in the daylight saving period. + // + internal bool IsEndDateMarkerForEndOfYear() => + !NoDaylightTransitions && + DaylightTransitionEnd.Month == 1 && DaylightTransitionEnd.Day == 1 && DaylightTransitionEnd.TimeOfDay.Hour == 0 && + DaylightTransitionEnd.TimeOfDay.Minute == 0 && DaylightTransitionEnd.TimeOfDay.Second == 0 && + _dateStart.Year == _dateEnd.Year; + + /// + /// Helper function that performs all of the validation checks for the factory methods and deserialization callback. + /// + private static void ValidateAdjustmentRule( + DateTime dateStart, + DateTime dateEnd, + TimeSpan daylightDelta, + TransitionTime daylightTransitionStart, + TransitionTime daylightTransitionEnd, + bool noDaylightTransitions) + { + if (dateStart.Kind != DateTimeKind.Unspecified && dateStart.Kind != DateTimeKind.Utc) + { + throw new ArgumentException(SR.Argument_DateTimeKindMustBeUnspecifiedOrUtc, nameof(dateStart)); + } + + if (dateEnd.Kind != DateTimeKind.Unspecified && dateEnd.Kind != DateTimeKind.Utc) + { + throw new ArgumentException(SR.Argument_DateTimeKindMustBeUnspecifiedOrUtc, nameof(dateEnd)); + } + + if (daylightTransitionStart.Equals(daylightTransitionEnd) && !noDaylightTransitions) + { + throw new ArgumentException(SR.Argument_TransitionTimesAreIdentical, nameof(daylightTransitionEnd)); + } + + if (dateStart > dateEnd) + { + throw new ArgumentException(SR.Argument_OutOfOrderDateTimes, nameof(dateStart)); + } + + // This cannot use UtcOffsetOutOfRange to account for the scenario where Samoa moved across the International Date Line, + // which caused their current BaseUtcOffset to be +13. But on the other side of the line it was UTC-11 (+1 for daylight). + // So when trying to describe DaylightDeltas for those times, the DaylightDelta needs + // to be -23 (what it takes to go from UTC+13 to UTC-10) + if (daylightDelta.TotalHours < -23.0 || daylightDelta.TotalHours > 14.0) + { + throw new ArgumentOutOfRangeException(nameof(daylightDelta), daylightDelta, SR.ArgumentOutOfRange_UtcOffset); + } + + if (daylightDelta.Ticks % TimeSpan.TicksPerMinute != 0) + { + throw new ArgumentException(SR.Argument_TimeSpanHasSeconds, nameof(daylightDelta)); + } + + if (dateStart != DateTime.MinValue && dateStart.Kind == DateTimeKind.Unspecified && dateStart.TimeOfDay != TimeSpan.Zero) + { + throw new ArgumentException(SR.Argument_DateTimeHasTimeOfDay, nameof(dateStart)); + } + + if (dateEnd != DateTime.MaxValue && dateEnd.Kind == DateTimeKind.Unspecified && dateEnd.TimeOfDay != TimeSpan.Zero) + { + throw new ArgumentException(SR.Argument_DateTimeHasTimeOfDay, nameof(dateEnd)); + } + } + + void IDeserializationCallback.OnDeserialization(object sender) + { + // OnDeserialization is called after each instance of this class is deserialized. + // This callback method performs AdjustmentRule validation after being deserialized. + + try + { + ValidateAdjustmentRule(_dateStart, _dateEnd, _daylightDelta, + _daylightTransitionStart, _daylightTransitionEnd, _noDaylightTransitions); + } + catch (ArgumentException e) + { + throw new SerializationException(SR.Serialization_InvalidData, e); + } + } + + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + info.AddValue("DateStart", _dateStart); // Do not rename (binary serialization) + info.AddValue("DateEnd", _dateEnd); // Do not rename (binary serialization) + info.AddValue("DaylightDelta", _daylightDelta); // Do not rename (binary serialization) + info.AddValue("DaylightTransitionStart", _daylightTransitionStart); // Do not rename (binary serialization) + info.AddValue("DaylightTransitionEnd", _daylightTransitionEnd); // Do not rename (binary serialization) + info.AddValue("BaseUtcOffsetDelta", _baseUtcOffsetDelta); // Do not rename (binary serialization) + info.AddValue("NoDaylightTransitions", _noDaylightTransitions); // Do not rename (binary serialization) + } + + private AdjustmentRule(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + _dateStart = (DateTime)info.GetValue("DateStart", typeof(DateTime)); // Do not rename (binary serialization) + _dateEnd = (DateTime)info.GetValue("DateEnd", typeof(DateTime)); // Do not rename (binary serialization) + _daylightDelta = (TimeSpan)info.GetValue("DaylightDelta", typeof(TimeSpan)); // Do not rename (binary serialization) + _daylightTransitionStart = (TransitionTime)info.GetValue("DaylightTransitionStart", typeof(TransitionTime)); // Do not rename (binary serialization) + _daylightTransitionEnd = (TransitionTime)info.GetValue("DaylightTransitionEnd", typeof(TransitionTime)); // Do not rename (binary serialization) + + object o = info.GetValueNoThrow("BaseUtcOffsetDelta", typeof(TimeSpan)); // Do not rename (binary serialization) + if (o != null) + { + _baseUtcOffsetDelta = (TimeSpan)o; + } + + o = info.GetValueNoThrow("NoDaylightTransitions", typeof(bool)); // Do not rename (binary serialization) + if (o != null) + { + _noDaylightTransitions = (bool)o; + } + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/TimeZoneInfo.StringSerializer.cs b/external/corefx/src/Common/src/CoreLib/System/TimeZoneInfo.StringSerializer.cs new file mode 100644 index 0000000000..e3e9ddbf58 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/TimeZoneInfo.StringSerializer.cs @@ -0,0 +1,625 @@ +// 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.Collections.Generic; +using System.Globalization; +using System.Runtime.Serialization; +using System.Text; + +namespace System +{ + public sealed partial class TimeZoneInfo + { + /// + /// Used to serialize and deserialize TimeZoneInfo objects based on the custom string serialization format. + /// + private struct StringSerializer + { + private enum State + { + Escaped = 0, + NotEscaped = 1, + StartOfToken = 2, + EndOfLine = 3 + } + + private readonly string _serializedText; + private int _currentTokenStartIndex; + private State _state; + + // the majority of the strings contained in the OS time zones fit in 64 chars + private const int InitialCapacityForString = 64; + private const char Esc = '\\'; + private const char Sep = ';'; + private const char Lhs = '['; + private const char Rhs = ']'; + private const string DateTimeFormat = "MM:dd:yyyy"; + private const string TimeOfDayFormat = "HH:mm:ss.FFF"; + + /// + /// Creates the custom serialized string representation of a TimeZoneInfo instance. + /// + public static string GetSerializedString(TimeZoneInfo zone) + { + StringBuilder serializedText = StringBuilderCache.Acquire(); + + // + // <_id>;<_baseUtcOffset>;<_displayName>;<_standardDisplayName>;<_daylightDispayName> + // + SerializeSubstitute(zone.Id, serializedText); + serializedText.Append(Sep); + serializedText.Append(zone.BaseUtcOffset.TotalMinutes.ToString(CultureInfo.InvariantCulture)); + serializedText.Append(Sep); + SerializeSubstitute(zone.DisplayName, serializedText); + serializedText.Append(Sep); + SerializeSubstitute(zone.StandardName, serializedText); + serializedText.Append(Sep); + SerializeSubstitute(zone.DaylightName, serializedText); + serializedText.Append(Sep); + + AdjustmentRule[] rules = zone.GetAdjustmentRules(); + foreach (AdjustmentRule rule in rules) + { + serializedText.Append(Lhs); + serializedText.Append(rule.DateStart.ToString(DateTimeFormat, DateTimeFormatInfo.InvariantInfo)); + serializedText.Append(Sep); + serializedText.Append(rule.DateEnd.ToString(DateTimeFormat, DateTimeFormatInfo.InvariantInfo)); + serializedText.Append(Sep); + serializedText.Append(rule.DaylightDelta.TotalMinutes.ToString(CultureInfo.InvariantCulture)); + serializedText.Append(Sep); + // serialize the TransitionTime's + SerializeTransitionTime(rule.DaylightTransitionStart, serializedText); + serializedText.Append(Sep); + SerializeTransitionTime(rule.DaylightTransitionEnd, serializedText); + serializedText.Append(Sep); + if (rule.BaseUtcOffsetDelta != TimeSpan.Zero) + { + // Serialize it only when BaseUtcOffsetDelta has a value to reduce the impact of adding rule.BaseUtcOffsetDelta + serializedText.Append(rule.BaseUtcOffsetDelta.TotalMinutes.ToString(CultureInfo.InvariantCulture)); + serializedText.Append(Sep); + } + if (rule.NoDaylightTransitions) + { + // Serialize it only when NoDaylightTransitions is true to reduce the impact of adding rule.NoDaylightTransitions + serializedText.Append('1'); + serializedText.Append(Sep); + } + serializedText.Append(Rhs); + } + serializedText.Append(Sep); + + return StringBuilderCache.GetStringAndRelease(serializedText); + } + + /// + /// Instantiates a TimeZoneInfo from a custom serialized string. + /// + public static TimeZoneInfo GetDeserializedTimeZoneInfo(string source) + { + StringSerializer s = new StringSerializer(source); + + string id = s.GetNextStringValue(); + TimeSpan baseUtcOffset = s.GetNextTimeSpanValue(); + string displayName = s.GetNextStringValue(); + string standardName = s.GetNextStringValue(); + string daylightName = s.GetNextStringValue(); + AdjustmentRule[] rules = s.GetNextAdjustmentRuleArrayValue(); + + try + { + return new TimeZoneInfo(id, baseUtcOffset, displayName, standardName, daylightName, rules, disableDaylightSavingTime: false); + } + catch (ArgumentException ex) + { + throw new SerializationException(SR.Serialization_InvalidData, ex); + } + catch (InvalidTimeZoneException ex) + { + throw new SerializationException(SR.Serialization_InvalidData, ex); + } + } + + private StringSerializer(string str) + { + _serializedText = str; + _currentTokenStartIndex = 0; + _state = State.StartOfToken; + } + + /// + /// Appends the String to the StringBuilder with all of the reserved chars escaped. + /// + /// ";" -> "\;" + /// "[" -> "\[" + /// "]" -> "\]" + /// "\" -> "\\" + /// + private static void SerializeSubstitute(string text, StringBuilder serializedText) + { + foreach (char c in text) + { + if (c == Esc || c == Lhs || c == Rhs || c == Sep) + { + serializedText.Append('\\'); + } + serializedText.Append(c); + } + } + + /// + /// Helper method to serialize a TimeZoneInfo.TransitionTime object. + /// + private static void SerializeTransitionTime(TransitionTime time, StringBuilder serializedText) + { + serializedText.Append(Lhs); + serializedText.Append(time.IsFixedDateRule ? '1' : '0'); + serializedText.Append(Sep); + serializedText.Append(time.TimeOfDay.ToString(TimeOfDayFormat, DateTimeFormatInfo.InvariantInfo)); + serializedText.Append(Sep); + serializedText.Append(time.Month.ToString(CultureInfo.InvariantCulture)); + serializedText.Append(Sep); + if (time.IsFixedDateRule) + { + serializedText.Append(time.Day.ToString(CultureInfo.InvariantCulture)); + serializedText.Append(Sep); + } + else + { + serializedText.Append(time.Week.ToString(CultureInfo.InvariantCulture)); + serializedText.Append(Sep); + serializedText.Append(((int)time.DayOfWeek).ToString(CultureInfo.InvariantCulture)); + serializedText.Append(Sep); + } + serializedText.Append(Rhs); + } + + /// + /// Helper function to determine if the passed in string token is allowed to be preceded by an escape sequence token. + /// + private static void VerifyIsEscapableCharacter(char c) + { + if (c != Esc && c != Sep && c != Lhs && c != Rhs) + { + throw new SerializationException(SR.Format(SR.Serialization_InvalidEscapeSequence, c)); + } + } + + /// + /// Helper function that reads past "v.Next" data fields. Receives a "depth" parameter indicating the + /// current relative nested bracket depth that _currentTokenStartIndex is at. The function ends + /// successfully when "depth" returns to zero (0). + /// + private void SkipVersionNextDataFields(int depth /* starting depth in the nested brackets ('[', ']')*/) + { + if (_currentTokenStartIndex < 0 || _currentTokenStartIndex >= _serializedText.Length) + { + throw new SerializationException(SR.Serialization_InvalidData); + } + State tokenState = State.NotEscaped; + + // walk the serialized text, building up the token as we go... + for (int i = _currentTokenStartIndex; i < _serializedText.Length; i++) + { + if (tokenState == State.Escaped) + { + VerifyIsEscapableCharacter(_serializedText[i]); + tokenState = State.NotEscaped; + } + else if (tokenState == State.NotEscaped) + { + switch (_serializedText[i]) + { + case Esc: + tokenState = State.Escaped; + break; + + case Lhs: + depth++; + break; + case Rhs: + depth--; + if (depth == 0) + { + _currentTokenStartIndex = i + 1; + if (_currentTokenStartIndex >= _serializedText.Length) + { + _state = State.EndOfLine; + } + else + { + _state = State.StartOfToken; + } + return; + } + break; + + case '\0': + // invalid character + throw new SerializationException(SR.Serialization_InvalidData); + + default: + break; + } + } + } + + throw new SerializationException(SR.Serialization_InvalidData); + } + + /// + /// Helper function that reads a string token from the serialized text. The function + /// updates to point to the next token on exit. + /// Also is set to either or + /// on exit. + /// + private string GetNextStringValue() + { + // first verify the internal state of the object + if (_state == State.EndOfLine) + { + throw new SerializationException(SR.Serialization_InvalidData); + } + if (_currentTokenStartIndex < 0 || _currentTokenStartIndex >= _serializedText.Length) + { + throw new SerializationException(SR.Serialization_InvalidData); + } + State tokenState = State.NotEscaped; + StringBuilder token = StringBuilderCache.Acquire(InitialCapacityForString); + + // walk the serialized text, building up the token as we go... + for (int i = _currentTokenStartIndex; i < _serializedText.Length; i++) + { + if (tokenState == State.Escaped) + { + VerifyIsEscapableCharacter(_serializedText[i]); + token.Append(_serializedText[i]); + tokenState = State.NotEscaped; + } + else if (tokenState == State.NotEscaped) + { + switch (_serializedText[i]) + { + case Esc: + tokenState = State.Escaped; + break; + + case Lhs: + // '[' is an unexpected character + throw new SerializationException(SR.Serialization_InvalidData); + + case Rhs: + // ']' is an unexpected character + throw new SerializationException(SR.Serialization_InvalidData); + + case Sep: + _currentTokenStartIndex = i + 1; + if (_currentTokenStartIndex >= _serializedText.Length) + { + _state = State.EndOfLine; + } + else + { + _state = State.StartOfToken; + } + return StringBuilderCache.GetStringAndRelease(token); + + case '\0': + // invalid character + throw new SerializationException(SR.Serialization_InvalidData); + + default: + token.Append(_serializedText[i]); + break; + } + } + } + // + // we are at the end of the line + // + if (tokenState == State.Escaped) + { + // we are at the end of the serialized text but we are in an escaped state + throw new SerializationException(SR.Format(SR.Serialization_InvalidEscapeSequence, string.Empty)); + } + + throw new SerializationException(SR.Serialization_InvalidData); + } + + /// + /// Helper function to read a DateTime token. + /// + private DateTime GetNextDateTimeValue(string format) + { + string token = GetNextStringValue(); + DateTime time; + if (!DateTime.TryParseExact(token, format, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None, out time)) + { + throw new SerializationException(SR.Serialization_InvalidData); + } + return time; + } + + /// + /// Helper function to read a TimeSpan token. + /// + private TimeSpan GetNextTimeSpanValue() + { + int token = GetNextInt32Value(); + try + { + return new TimeSpan(hours: 0, minutes: token, seconds: 0); + } + catch (ArgumentOutOfRangeException e) + { + throw new SerializationException(SR.Serialization_InvalidData, e); + } + } + + /// + /// Helper function to read an Int32 token. + /// + private int GetNextInt32Value() + { + string token = GetNextStringValue(); + int value; + if (!int.TryParse(token, NumberStyles.AllowLeadingSign /* "[sign]digits" */, CultureInfo.InvariantCulture, out value)) + { + throw new SerializationException(SR.Serialization_InvalidData); + } + return value; + } + + /// + /// Helper function to read an AdjustmentRule[] token. + /// + private AdjustmentRule[] GetNextAdjustmentRuleArrayValue() + { + List rules = new List(1); + int count = 0; + + // individual AdjustmentRule array elements do not require semicolons + AdjustmentRule rule = GetNextAdjustmentRuleValue(); + while (rule != null) + { + rules.Add(rule); + count++; + + rule = GetNextAdjustmentRuleValue(); + } + + // the AdjustmentRule array must end with a separator + if (_state == State.EndOfLine) + { + throw new SerializationException(SR.Serialization_InvalidData); + } + if (_currentTokenStartIndex < 0 || _currentTokenStartIndex >= _serializedText.Length) + { + throw new SerializationException(SR.Serialization_InvalidData); + } + + return count != 0 ? rules.ToArray() : null; + } + + /// + /// Helper function to read an AdjustmentRule token. + /// + private AdjustmentRule GetNextAdjustmentRuleValue() + { + // first verify the internal state of the object + if (_state == State.EndOfLine) + { + return null; + } + + if (_currentTokenStartIndex < 0 || _currentTokenStartIndex >= _serializedText.Length) + { + throw new SerializationException(SR.Serialization_InvalidData); + } + + // check to see if the very first token we see is the separator + if (_serializedText[_currentTokenStartIndex] == Sep) + { + return null; + } + + // verify the current token is a left-hand-side marker ("[") + if (_serializedText[_currentTokenStartIndex] != Lhs) + { + throw new SerializationException(SR.Serialization_InvalidData); + } + _currentTokenStartIndex++; + + DateTime dateStart = GetNextDateTimeValue(DateTimeFormat); + DateTime dateEnd = GetNextDateTimeValue(DateTimeFormat); + TimeSpan daylightDelta = GetNextTimeSpanValue(); + TransitionTime daylightStart = GetNextTransitionTimeValue(); + TransitionTime daylightEnd = GetNextTransitionTimeValue(); + TimeSpan baseUtcOffsetDelta = TimeSpan.Zero; + int noDaylightTransitions = 0; + + // verify that the string is now at the right-hand-side marker ("]") ... + + if (_state == State.EndOfLine || _currentTokenStartIndex >= _serializedText.Length) + { + throw new SerializationException(SR.Serialization_InvalidData); + } + + // Check if we have baseUtcOffsetDelta in the serialized string and then deserialize it + if ((_serializedText[_currentTokenStartIndex] >= '0' && _serializedText[_currentTokenStartIndex] <= '9') || + _serializedText[_currentTokenStartIndex] == '-' || _serializedText[_currentTokenStartIndex] == '+') + { + baseUtcOffsetDelta = GetNextTimeSpanValue(); + } + + // Check if we have NoDaylightTransitions in the serialized string and then deserialize it + if ((_serializedText[_currentTokenStartIndex] >= '0' && _serializedText[_currentTokenStartIndex] <= '1')) + { + noDaylightTransitions = GetNextInt32Value(); + } + + if (_state == State.EndOfLine || _currentTokenStartIndex >= _serializedText.Length) + { + throw new SerializationException(SR.Serialization_InvalidData); + } + + if (_serializedText[_currentTokenStartIndex] != Rhs) + { + // skip ahead of any "v.Next" data at the end of the AdjustmentRule + // + // FUTURE: if the serialization format is extended in the future then this + // code section will need to be changed to read the new fields rather + // than just skipping the data at the end of the [AdjustmentRule]. + SkipVersionNextDataFields(1); + } + else + { + _currentTokenStartIndex++; + } + + // create the AdjustmentRule from the deserialized fields ... + + AdjustmentRule rule; + try + { + rule = AdjustmentRule.CreateAdjustmentRule(dateStart, dateEnd, daylightDelta, daylightStart, daylightEnd, baseUtcOffsetDelta, noDaylightTransitions > 0); + } + catch (ArgumentException e) + { + throw new SerializationException(SR.Serialization_InvalidData, e); + } + + // finally set the state to either EndOfLine or StartOfToken for the next caller + if (_currentTokenStartIndex >= _serializedText.Length) + { + _state = State.EndOfLine; + } + else + { + _state = State.StartOfToken; + } + return rule; + } + + /// + /// Helper function to read a TransitionTime token. + /// + private TransitionTime GetNextTransitionTimeValue() + { + // first verify the internal state of the object + + if (_state == State.EndOfLine || + (_currentTokenStartIndex < _serializedText.Length && _serializedText[_currentTokenStartIndex] == Rhs)) + { + // + // we are at the end of the line or we are starting at a "]" character + // + throw new SerializationException(SR.Serialization_InvalidData); + } + + if (_currentTokenStartIndex < 0 || _currentTokenStartIndex >= _serializedText.Length) + { + throw new SerializationException(SR.Serialization_InvalidData); + } + + // verify the current token is a left-hand-side marker ("[") + + if (_serializedText[_currentTokenStartIndex] != Lhs) + { + throw new SerializationException(SR.Serialization_InvalidData); + } + _currentTokenStartIndex++; + + int isFixedDate = GetNextInt32Value(); + + if (isFixedDate != 0 && isFixedDate != 1) + { + throw new SerializationException(SR.Serialization_InvalidData); + } + + TransitionTime transition; + + DateTime timeOfDay = GetNextDateTimeValue(TimeOfDayFormat); + timeOfDay = new DateTime(1, 1, 1, timeOfDay.Hour, timeOfDay.Minute, timeOfDay.Second, timeOfDay.Millisecond); + + int month = GetNextInt32Value(); + + if (isFixedDate == 1) + { + int day = GetNextInt32Value(); + + try + { + transition = TransitionTime.CreateFixedDateRule(timeOfDay, month, day); + } + catch (ArgumentException e) + { + throw new SerializationException(SR.Serialization_InvalidData, e); + } + } + else + { + int week = GetNextInt32Value(); + int dayOfWeek = GetNextInt32Value(); + + try + { + transition = TransitionTime.CreateFloatingDateRule(timeOfDay, month, week, (DayOfWeek)dayOfWeek); + } + catch (ArgumentException e) + { + throw new SerializationException(SR.Serialization_InvalidData, e); + } + } + + // verify that the string is now at the right-hand-side marker ("]") ... + + if (_state == State.EndOfLine || _currentTokenStartIndex >= _serializedText.Length) + { + throw new SerializationException(SR.Serialization_InvalidData); + } + + if (_serializedText[_currentTokenStartIndex] != Rhs) + { + // skip ahead of any "v.Next" data at the end of the AdjustmentRule + // + // FUTURE: if the serialization format is extended in the future then this + // code section will need to be changed to read the new fields rather + // than just skipping the data at the end of the [TransitionTime]. + SkipVersionNextDataFields(1); + } + else + { + _currentTokenStartIndex++; + } + + // check to see if the string is now at the separator (";") ... + bool sepFound = false; + if (_currentTokenStartIndex < _serializedText.Length && + _serializedText[_currentTokenStartIndex] == Sep) + { + // handle the case where we ended on a ";" + _currentTokenStartIndex++; + sepFound = true; + } + + if (!sepFound) + { + // we MUST end on a separator + throw new SerializationException(SR.Serialization_InvalidData); + } + + // finally set the state to either EndOfLine or StartOfToken for the next caller + if (_currentTokenStartIndex >= _serializedText.Length) + { + _state = State.EndOfLine; + } + else + { + _state = State.StartOfToken; + } + return transition; + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/TimeZoneInfo.TransitionTime.cs b/external/corefx/src/Common/src/CoreLib/System/TimeZoneInfo.TransitionTime.cs new file mode 100644 index 0000000000..b93794262c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/TimeZoneInfo.TransitionTime.cs @@ -0,0 +1,155 @@ +// 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.Runtime.Serialization; + +namespace System +{ + public sealed partial class TimeZoneInfo + { + [Serializable] + public readonly struct TransitionTime : IEquatable, ISerializable, IDeserializationCallback + { + private readonly DateTime _timeOfDay; + private readonly byte _month; + private readonly byte _week; + private readonly byte _day; + private readonly DayOfWeek _dayOfWeek; + private readonly bool _isFixedDateRule; + + public DateTime TimeOfDay => _timeOfDay; + + public int Month => _month; + + public int Week => _week; + + public int Day => _day; + + public DayOfWeek DayOfWeek => _dayOfWeek; + + public bool IsFixedDateRule => _isFixedDateRule; + + public override bool Equals(object obj) => + obj is TransitionTime && Equals((TransitionTime)obj); + + public static bool operator ==(TransitionTime t1, TransitionTime t2) => t1.Equals(t2); + + public static bool operator !=(TransitionTime t1, TransitionTime t2) => !t1.Equals(t2); + + public bool Equals(TransitionTime other) => + _isFixedDateRule == other._isFixedDateRule && + _timeOfDay == other._timeOfDay && + _month == other._month && + (other._isFixedDateRule ? + _day == other._day : + _week == other._week && _dayOfWeek == other._dayOfWeek); + + public override int GetHashCode() => (int)_month ^ (int)_week << 8; + + private TransitionTime(DateTime timeOfDay, int month, int week, int day, DayOfWeek dayOfWeek, bool isFixedDateRule) + { + ValidateTransitionTime(timeOfDay, month, week, day, dayOfWeek); + + _timeOfDay = timeOfDay; + _month = (byte)month; + _week = (byte)week; + _day = (byte)day; + _dayOfWeek = dayOfWeek; + _isFixedDateRule = isFixedDateRule; + } + + public static TransitionTime CreateFixedDateRule(DateTime timeOfDay, int month, int day) => + new TransitionTime(timeOfDay, month, 1, day, DayOfWeek.Sunday, isFixedDateRule: true); + + public static TransitionTime CreateFloatingDateRule(DateTime timeOfDay, int month, int week, DayOfWeek dayOfWeek) => + new TransitionTime(timeOfDay, month, week, 1, dayOfWeek, isFixedDateRule: false); + + /// + /// Helper function that validates a TransitionTime instance. + /// + private static void ValidateTransitionTime(DateTime timeOfDay, int month, int week, int day, DayOfWeek dayOfWeek) + { + if (timeOfDay.Kind != DateTimeKind.Unspecified) + { + throw new ArgumentException(SR.Argument_DateTimeKindMustBeUnspecified, nameof(timeOfDay)); + } + + // Month range 1-12 + if (month < 1 || month > 12) + { + throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_MonthParam); + } + + // Day range 1-31 + if (day < 1 || day > 31) + { + throw new ArgumentOutOfRangeException(nameof(day), SR.ArgumentOutOfRange_DayParam); + } + + // Week range 1-5 + if (week < 1 || week > 5) + { + throw new ArgumentOutOfRangeException(nameof(week), SR.ArgumentOutOfRange_Week); + } + + // DayOfWeek range 0-6 + if ((int)dayOfWeek < 0 || (int)dayOfWeek > 6) + { + throw new ArgumentOutOfRangeException(nameof(dayOfWeek), SR.ArgumentOutOfRange_DayOfWeek); + } + + timeOfDay.GetDatePart(out int timeOfDayYear, out int timeOfDayMonth, out int timeOfDayDay); + if (timeOfDayYear != 1 || timeOfDayMonth != 1 || timeOfDayDay != 1 || (timeOfDay.Ticks % TimeSpan.TicksPerMillisecond != 0)) + { + throw new ArgumentException(SR.Argument_DateTimeHasTicks, nameof(timeOfDay)); + } + } + + void IDeserializationCallback.OnDeserialization(object sender) + { + // OnDeserialization is called after each instance of this class is deserialized. + // This callback method performs TransitionTime validation after being deserialized. + + try + { + ValidateTransitionTime(_timeOfDay, _month, _week, _day, _dayOfWeek); + } + catch (ArgumentException e) + { + throw new SerializationException(SR.Serialization_InvalidData, e); + } + } + + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + info.AddValue("TimeOfDay", _timeOfDay); // Do not rename (binary serialization) + info.AddValue("Month", _month); // Do not rename (binary serialization) + info.AddValue("Week", _week); // Do not rename (binary serialization) + info.AddValue("Day", _day); // Do not rename (binary serialization) + info.AddValue("DayOfWeek", _dayOfWeek); // Do not rename (binary serialization) + info.AddValue("IsFixedDateRule", _isFixedDateRule); // Do not rename (binary serialization) + } + + private TransitionTime(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + _timeOfDay = (DateTime)info.GetValue("TimeOfDay", typeof(DateTime)); // Do not rename (binary serialization) + _month = (byte)info.GetValue("Month", typeof(byte)); // Do not rename (binary serialization) + _week = (byte)info.GetValue("Week", typeof(byte)); // Do not rename (binary serialization) + _day = (byte)info.GetValue("Day", typeof(byte)); // Do not rename (binary serialization) + _dayOfWeek = (DayOfWeek)info.GetValue("DayOfWeek", typeof(DayOfWeek)); // Do not rename (binary serialization) + _isFixedDateRule = (bool)info.GetValue("IsFixedDateRule", typeof(bool)); // Do not rename (binary serialization) + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/TimeZoneInfo.cs b/external/corefx/src/Common/src/CoreLib/System/TimeZoneInfo.cs new file mode 100644 index 0000000000..f9b5ce872a --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/TimeZoneInfo.cs @@ -0,0 +1,1993 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.Serialization; +using System.Threading; + +namespace System +{ + // + // DateTime uses TimeZoneInfo under the hood for IsDaylightSavingTime, IsAmbiguousTime, and GetUtcOffset. + // These TimeZoneInfo APIs can throw ArgumentException when an Invalid-Time is passed in. To avoid this + // unwanted behavior in DateTime public APIs, DateTime internally passes the + // TimeZoneInfoOptions.NoThrowOnInvalidTime flag to internal TimeZoneInfo APIs. + // + // In the future we can consider exposing similar options on the public TimeZoneInfo APIs if there is enough + // demand for this alternate behavior. + // + [Flags] + internal enum TimeZoneInfoOptions + { + None = 1, + NoThrowOnInvalidTime = 2 + }; + + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed partial class TimeZoneInfo : IEquatable, ISerializable, IDeserializationCallback + { + private enum TimeZoneInfoResult + { + Success = 0, + TimeZoneNotFoundException = 1, + InvalidTimeZoneException = 2, + SecurityException = 3 + }; + + private readonly string _id; + private readonly string _displayName; + private readonly string _standardDisplayName; + private readonly string _daylightDisplayName; + private readonly TimeSpan _baseUtcOffset; + private readonly bool _supportsDaylightSavingTime; + private readonly AdjustmentRule[] _adjustmentRules; + + // constants for TimeZoneInfo.Local and TimeZoneInfo.Utc + private const string UtcId = "UTC"; + private const string LocalId = "Local"; + + private static readonly TimeZoneInfo s_utcTimeZone = CreateCustomTimeZone(UtcId, TimeSpan.Zero, UtcId, UtcId); + + private static CachedData s_cachedData = new CachedData(); + + // + // All cached data are encapsulated in a helper class to allow consistent view even when the data are refreshed using ClearCachedData() + // + // For example, TimeZoneInfo.Local can be cleared by another thread calling TimeZoneInfo.ClearCachedData. Without the consistent snapshot, + // there is a chance that the internal ConvertTime calls will throw since 'source' won't be reference equal to the new TimeZoneInfo.Local. + // +#pragma warning disable 0420 + private sealed partial class CachedData + { + private volatile TimeZoneInfo _localTimeZone; + + private TimeZoneInfo CreateLocal() + { + lock (this) + { + TimeZoneInfo timeZone = _localTimeZone; + if (timeZone == null) + { + timeZone = GetLocalTimeZone(this); + + // this step is to break the reference equality + // between TimeZoneInfo.Local and a second time zone + // such as "Pacific Standard Time" + timeZone = new TimeZoneInfo( + timeZone._id, + timeZone._baseUtcOffset, + timeZone._displayName, + timeZone._standardDisplayName, + timeZone._daylightDisplayName, + timeZone._adjustmentRules, + disableDaylightSavingTime: false); + + _localTimeZone = timeZone; + } + return timeZone; + } + } + + public TimeZoneInfo Local + { + get + { + TimeZoneInfo timeZone = _localTimeZone; + if (timeZone == null) + { + timeZone = CreateLocal(); + } + return timeZone; + } + } + + /// + /// Helper function that returns the corresponding DateTimeKind for this TimeZoneInfo. + /// + public DateTimeKind GetCorrespondingKind(TimeZoneInfo timeZone) + { + // We check reference equality to see if 'this' is the same as + // TimeZoneInfo.Local or TimeZoneInfo.Utc. This check is needed to + // support setting the DateTime Kind property to 'Local' or + // 'Utc' on the ConverTime(...) return value. + // + // Using reference equality instead of value equality was a + // performance based design compromise. The reference equality + // has much greater performance, but it reduces the number of + // returned DateTime's that can be properly set as 'Local' or 'Utc'. + // + // For example, the user could be converting to the TimeZoneInfo returned + // by FindSystemTimeZoneById("Pacific Standard Time") and their local + // machine may be in Pacific time. If we used value equality to determine + // the corresponding Kind then this conversion would be tagged as 'Local'; + // where as we are currently tagging the returned DateTime as 'Unspecified' + // in this example. Only when the user passes in TimeZoneInfo.Local or + // TimeZoneInfo.Utc to the ConvertTime(...) methods will this check succeed. + // + return + ReferenceEquals(timeZone, s_utcTimeZone) ? DateTimeKind.Utc : + ReferenceEquals(timeZone, _localTimeZone) ? DateTimeKind.Local : + DateTimeKind.Unspecified; + } + + public Dictionary _systemTimeZones; + public ReadOnlyCollection _readOnlySystemTimeZones; + public bool _allSystemTimeZonesRead; + }; +#pragma warning restore 0420 + + // used by GetUtcOffsetFromUtc (DateTime.Now, DateTime.ToLocalTime) for max/min whole-day range checks + private static readonly DateTime s_maxDateOnly = new DateTime(9999, 12, 31); + private static readonly DateTime s_minDateOnly = new DateTime(1, 1, 2); + + public string Id => _id; + + public string DisplayName => _displayName ?? string.Empty; + + public string StandardName => _standardDisplayName ?? string.Empty; + + public string DaylightName => _daylightDisplayName ?? string.Empty; + + public TimeSpan BaseUtcOffset => _baseUtcOffset; + + public bool SupportsDaylightSavingTime => _supportsDaylightSavingTime; + + /// + /// Returns an array of TimeSpan objects representing all of + /// possible UTC offset values for this ambiguous time. + /// + public TimeSpan[] GetAmbiguousTimeOffsets(DateTimeOffset dateTimeOffset) + { + if (!SupportsDaylightSavingTime) + { + throw new ArgumentException(SR.Argument_DateTimeOffsetIsNotAmbiguous, nameof(dateTimeOffset)); + } + + DateTime adjustedTime = ConvertTime(dateTimeOffset, this).DateTime; + + bool isAmbiguous = false; + int? ruleIndex; + AdjustmentRule rule = GetAdjustmentRuleForAmbiguousOffsets(adjustedTime, out ruleIndex); + if (rule != null && rule.HasDaylightSaving) + { + DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule, ruleIndex); + isAmbiguous = GetIsAmbiguousTime(adjustedTime, rule, daylightTime); + } + + if (!isAmbiguous) + { + throw new ArgumentException(SR.Argument_DateTimeOffsetIsNotAmbiguous, nameof(dateTimeOffset)); + } + + // the passed in dateTime is ambiguous in this TimeZoneInfo instance + TimeSpan[] timeSpans = new TimeSpan[2]; + + TimeSpan actualUtcOffset = _baseUtcOffset + rule.BaseUtcOffsetDelta; + + // the TimeSpan array must be sorted from least to greatest + if (rule.DaylightDelta > TimeSpan.Zero) + { + timeSpans[0] = actualUtcOffset; // FUTURE: + rule.StandardDelta; + timeSpans[1] = actualUtcOffset + rule.DaylightDelta; + } + else + { + timeSpans[0] = actualUtcOffset + rule.DaylightDelta; + timeSpans[1] = actualUtcOffset; // FUTURE: + rule.StandardDelta; + } + return timeSpans; + } + + /// + /// Returns an array of TimeSpan objects representing all of + /// possible UTC offset values for this ambiguous time. + /// + public TimeSpan[] GetAmbiguousTimeOffsets(DateTime dateTime) + { + if (!SupportsDaylightSavingTime) + { + throw new ArgumentException(SR.Argument_DateTimeIsNotAmbiguous, nameof(dateTime)); + } + + DateTime adjustedTime; + if (dateTime.Kind == DateTimeKind.Local) + { + CachedData cachedData = s_cachedData; + adjustedTime = ConvertTime(dateTime, cachedData.Local, this, TimeZoneInfoOptions.None, cachedData); + } + else if (dateTime.Kind == DateTimeKind.Utc) + { + CachedData cachedData = s_cachedData; + adjustedTime = ConvertTime(dateTime, s_utcTimeZone, this, TimeZoneInfoOptions.None, cachedData); + } + else + { + adjustedTime = dateTime; + } + + bool isAmbiguous = false; + int? ruleIndex; + AdjustmentRule rule = GetAdjustmentRuleForAmbiguousOffsets(adjustedTime, out ruleIndex); + if (rule != null && rule.HasDaylightSaving) + { + DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule, ruleIndex); + isAmbiguous = GetIsAmbiguousTime(adjustedTime, rule, daylightTime); + } + + if (!isAmbiguous) + { + throw new ArgumentException(SR.Argument_DateTimeIsNotAmbiguous, nameof(dateTime)); + } + + // the passed in dateTime is ambiguous in this TimeZoneInfo instance + TimeSpan[] timeSpans = new TimeSpan[2]; + TimeSpan actualUtcOffset = _baseUtcOffset + rule.BaseUtcOffsetDelta; + + // the TimeSpan array must be sorted from least to greatest + if (rule.DaylightDelta > TimeSpan.Zero) + { + timeSpans[0] = actualUtcOffset; // FUTURE: + rule.StandardDelta; + timeSpans[1] = actualUtcOffset + rule.DaylightDelta; + } + else + { + timeSpans[0] = actualUtcOffset + rule.DaylightDelta; + timeSpans[1] = actualUtcOffset; // FUTURE: + rule.StandardDelta; + } + return timeSpans; + } + + // note the time is already adjusted + private AdjustmentRule GetAdjustmentRuleForAmbiguousOffsets(DateTime adjustedTime, out int? ruleIndex) + { + AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime, out ruleIndex); + if (rule != null && rule.NoDaylightTransitions && !rule.HasDaylightSaving) + { + // When using NoDaylightTransitions rules, each rule is only for one offset. + // When looking for the Daylight savings rules, and we found the non-DST rule, + // then we get the rule right before this rule. + return GetPreviousAdjustmentRule(rule, ruleIndex); + } + + return rule; + } + + /// + /// Gets the AdjustmentRule that is immediately preceding the specified rule. + /// If the specified rule is the first AdjustmentRule, or it isn't in _adjustmentRules, + /// then the specified rule is returned. + /// + private AdjustmentRule GetPreviousAdjustmentRule(AdjustmentRule rule, int? ruleIndex) + { + Debug.Assert(rule.NoDaylightTransitions, "GetPreviousAdjustmentRule should only be used with NoDaylightTransitions rules."); + + if (ruleIndex.HasValue && 0 < ruleIndex.Value && ruleIndex.Value < _adjustmentRules.Length) + { + return _adjustmentRules[ruleIndex.Value - 1]; + } + + AdjustmentRule result = rule; + for (int i = 1; i < _adjustmentRules.Length; i++) + { + // use ReferenceEquals here instead of AdjustmentRule.Equals because + // ReferenceEquals is much faster. This is safe because all the callers + // of GetPreviousAdjustmentRule pass in a rule that was retrieved from + // _adjustmentRules. A different approach will be needed if this ever changes. + if (ReferenceEquals(rule, _adjustmentRules[i])) + { + result = _adjustmentRules[i - 1]; + break; + } + } + return result; + } + + /// + /// Returns the Universal Coordinated Time (UTC) Offset for the current TimeZoneInfo instance. + /// + public TimeSpan GetUtcOffset(DateTimeOffset dateTimeOffset) => + GetUtcOffsetFromUtc(dateTimeOffset.UtcDateTime, this); + + /// + /// Returns the Universal Coordinated Time (UTC) Offset for the current TimeZoneInfo instance. + /// + public TimeSpan GetUtcOffset(DateTime dateTime) => + GetUtcOffset(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime, s_cachedData); + + // Shortcut for TimeZoneInfo.Local.GetUtcOffset + internal static TimeSpan GetLocalUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags) + { + CachedData cachedData = s_cachedData; + return cachedData.Local.GetUtcOffset(dateTime, flags, cachedData); + } + + /// + /// Returns the Universal Coordinated Time (UTC) Offset for the current TimeZoneInfo instance. + /// + internal TimeSpan GetUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags) => + GetUtcOffset(dateTime, flags, s_cachedData); + + private TimeSpan GetUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags, CachedData cachedData) + { + if (dateTime.Kind == DateTimeKind.Local) + { + if (cachedData.GetCorrespondingKind(this) != DateTimeKind.Local) + { + // + // normal case of converting from Local to Utc and then getting the offset from the UTC DateTime + // + DateTime adjustedTime = ConvertTime(dateTime, cachedData.Local, s_utcTimeZone, flags); + return GetUtcOffsetFromUtc(adjustedTime, this); + } + + // + // Fall through for TimeZoneInfo.Local.GetUtcOffset(date) + // to handle an edge case with Invalid-Times for DateTime formatting: + // + // Consider the invalid PST time "2007-03-11T02:00:00.0000000-08:00" + // + // By directly calling GetUtcOffset instead of converting to UTC and then calling GetUtcOffsetFromUtc + // the correct invalid offset of "-08:00" is returned. In the normal case of converting to UTC as an + // interim-step, the invalid time is adjusted into a *valid* UTC time which causes a change in output: + // + // 1) invalid PST time "2007-03-11T02:00:00.0000000-08:00" + // 2) converted to UTC "2007-03-11T10:00:00.0000000Z" + // 3) offset returned "2007-03-11T03:00:00.0000000-07:00" + // + } + else if (dateTime.Kind == DateTimeKind.Utc) + { + if (cachedData.GetCorrespondingKind(this) == DateTimeKind.Utc) + { + return _baseUtcOffset; + } + else + { + // + // passing in a UTC dateTime to a non-UTC TimeZoneInfo instance is a + // special Loss-Less case. + // + return GetUtcOffsetFromUtc(dateTime, this); + } + } + + return GetUtcOffset(dateTime, this, flags); + } + + /// + /// Returns true if the time is during the ambiguous time period + /// for the current TimeZoneInfo instance. + /// + public bool IsAmbiguousTime(DateTimeOffset dateTimeOffset) + { + if (!_supportsDaylightSavingTime) + { + return false; + } + + DateTimeOffset adjustedTime = ConvertTime(dateTimeOffset, this); + return IsAmbiguousTime(adjustedTime.DateTime); + } + + /// + /// Returns true if the time is during the ambiguous time period + /// for the current TimeZoneInfo instance. + /// + public bool IsAmbiguousTime(DateTime dateTime) => + IsAmbiguousTime(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime); + + /// + /// Returns true if the time is during the ambiguous time period + /// for the current TimeZoneInfo instance. + /// + internal bool IsAmbiguousTime(DateTime dateTime, TimeZoneInfoOptions flags) + { + if (!_supportsDaylightSavingTime) + { + return false; + } + + CachedData cachedData = s_cachedData; + DateTime adjustedTime = + dateTime.Kind == DateTimeKind.Local ? ConvertTime(dateTime, cachedData.Local, this, flags, cachedData) : + dateTime.Kind == DateTimeKind.Utc ? ConvertTime(dateTime, s_utcTimeZone, this, flags, cachedData) : + dateTime; + + int? ruleIndex; + AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime, out ruleIndex); + if (rule != null && rule.HasDaylightSaving) + { + DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule, ruleIndex); + return GetIsAmbiguousTime(adjustedTime, rule, daylightTime); + } + return false; + } + + /// + /// Returns true if the time is during Daylight Saving time for the current TimeZoneInfo instance. + /// + public bool IsDaylightSavingTime(DateTimeOffset dateTimeOffset) + { + bool isDaylightSavingTime; + GetUtcOffsetFromUtc(dateTimeOffset.UtcDateTime, this, out isDaylightSavingTime); + return isDaylightSavingTime; + } + + /// + /// Returns true if the time is during Daylight Saving time for the current TimeZoneInfo instance. + /// + public bool IsDaylightSavingTime(DateTime dateTime) => + IsDaylightSavingTime(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime, s_cachedData); + + /// + /// Returns true if the time is during Daylight Saving time for the current TimeZoneInfo instance. + /// + internal bool IsDaylightSavingTime(DateTime dateTime, TimeZoneInfoOptions flags) => + IsDaylightSavingTime(dateTime, flags, s_cachedData); + + private bool IsDaylightSavingTime(DateTime dateTime, TimeZoneInfoOptions flags, CachedData cachedData) + { + // + // dateTime.Kind is UTC, then time will be converted from UTC + // into current instance's timezone + // dateTime.Kind is Local, then time will be converted from Local + // into current instance's timezone + // dateTime.Kind is UnSpecified, then time is already in + // current instance's timezone + // + // Our DateTime handles ambiguous times, (one is in the daylight and + // one is in standard.) If a new DateTime is constructed during ambiguous + // time, it is defaulted to "Standard" (i.e. this will return false). + // For Invalid times, we will return false + + if (!_supportsDaylightSavingTime || _adjustmentRules == null) + { + return false; + } + + DateTime adjustedTime; + // + // handle any Local/Utc special cases... + // + if (dateTime.Kind == DateTimeKind.Local) + { + adjustedTime = ConvertTime(dateTime, cachedData.Local, this, flags, cachedData); + } + else if (dateTime.Kind == DateTimeKind.Utc) + { + if (cachedData.GetCorrespondingKind(this) == DateTimeKind.Utc) + { + // simple always false case: TimeZoneInfo.Utc.IsDaylightSavingTime(dateTime, flags); + return false; + } + else + { + // + // passing in a UTC dateTime to a non-UTC TimeZoneInfo instance is a + // special Loss-Less case. + // + bool isDaylightSavings; + GetUtcOffsetFromUtc(dateTime, this, out isDaylightSavings); + return isDaylightSavings; + } + } + else + { + adjustedTime = dateTime; + } + + // + // handle the normal cases... + // + int? ruleIndex; + AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime, out ruleIndex); + if (rule != null && rule.HasDaylightSaving) + { + DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule, ruleIndex); + return GetIsDaylightSavings(adjustedTime, rule, daylightTime, flags); + } + else + { + return false; + } + } + + /// + /// Returns true when dateTime falls into a "hole in time". + /// + public bool IsInvalidTime(DateTime dateTime) + { + bool isInvalid = false; + + if ((dateTime.Kind == DateTimeKind.Unspecified) || + (dateTime.Kind == DateTimeKind.Local && s_cachedData.GetCorrespondingKind(this) == DateTimeKind.Local)) + { + // only check Unspecified and (Local when this TimeZoneInfo instance is Local) + int? ruleIndex; + AdjustmentRule rule = GetAdjustmentRuleForTime(dateTime, out ruleIndex); + + if (rule != null && rule.HasDaylightSaving) + { + DaylightTimeStruct daylightTime = GetDaylightTime(dateTime.Year, rule, ruleIndex); + isInvalid = GetIsInvalidTime(dateTime, rule, daylightTime); + } + else + { + isInvalid = false; + } + } + + return isInvalid; + } + + /// + /// Clears data from static members. + /// + public static void ClearCachedData() + { + // Clear a fresh instance of cached data + s_cachedData = new CachedData(); + } + + /// + /// Converts the value of a DateTime object from sourceTimeZone to destinationTimeZone. + /// + public static DateTimeOffset ConvertTimeBySystemTimeZoneId(DateTimeOffset dateTimeOffset, string destinationTimeZoneId) => + ConvertTime(dateTimeOffset, FindSystemTimeZoneById(destinationTimeZoneId)); + + /// + /// Converts the value of a DateTime object from sourceTimeZone to destinationTimeZone. + /// + public static DateTime ConvertTimeBySystemTimeZoneId(DateTime dateTime, string destinationTimeZoneId) => + ConvertTime(dateTime, FindSystemTimeZoneById(destinationTimeZoneId)); + + /// + /// Converts the value of a DateTime object from sourceTimeZone to destinationTimeZone. + /// + public static DateTime ConvertTimeBySystemTimeZoneId(DateTime dateTime, string sourceTimeZoneId, string destinationTimeZoneId) + { + if (dateTime.Kind == DateTimeKind.Local && string.Equals(sourceTimeZoneId, Local.Id, StringComparison.OrdinalIgnoreCase)) + { + // TimeZoneInfo.Local can be cleared by another thread calling TimeZoneInfo.ClearCachedData. + // Take snapshot of cached data to guarantee this method will not be impacted by the ClearCachedData call. + // Without the snapshot, there is a chance that ConvertTime will throw since 'source' won't + // be reference equal to the new TimeZoneInfo.Local + // + CachedData cachedData = s_cachedData; + return ConvertTime(dateTime, cachedData.Local, FindSystemTimeZoneById(destinationTimeZoneId), TimeZoneInfoOptions.None, cachedData); + } + else if (dateTime.Kind == DateTimeKind.Utc && string.Equals(sourceTimeZoneId, Utc.Id, StringComparison.OrdinalIgnoreCase)) + { + return ConvertTime(dateTime, s_utcTimeZone, FindSystemTimeZoneById(destinationTimeZoneId), TimeZoneInfoOptions.None, s_cachedData); + } + else + { + return ConvertTime(dateTime, FindSystemTimeZoneById(sourceTimeZoneId), FindSystemTimeZoneById(destinationTimeZoneId)); + } + } + + /// + /// Converts the value of the dateTime object from sourceTimeZone to destinationTimeZone + /// + public static DateTimeOffset ConvertTime(DateTimeOffset dateTimeOffset, TimeZoneInfo destinationTimeZone) + { + if (destinationTimeZone == null) + { + throw new ArgumentNullException(nameof(destinationTimeZone)); + } + + // calculate the destination time zone offset + DateTime utcDateTime = dateTimeOffset.UtcDateTime; + TimeSpan destinationOffset = GetUtcOffsetFromUtc(utcDateTime, destinationTimeZone); + + // check for overflow + long ticks = utcDateTime.Ticks + destinationOffset.Ticks; + + return + ticks > DateTimeOffset.MaxValue.Ticks ? DateTimeOffset.MaxValue : + ticks < DateTimeOffset.MinValue.Ticks ? DateTimeOffset.MinValue : + new DateTimeOffset(ticks, destinationOffset); + } + + /// + /// Converts the value of the dateTime object from sourceTimeZone to destinationTimeZone + /// + public static DateTime ConvertTime(DateTime dateTime, TimeZoneInfo destinationTimeZone) + { + if (destinationTimeZone == null) + { + throw new ArgumentNullException(nameof(destinationTimeZone)); + } + + // Special case to give a way clearing the cache without exposing ClearCachedData() + if (dateTime.Ticks == 0) + { + ClearCachedData(); + } + CachedData cachedData = s_cachedData; + TimeZoneInfo sourceTimeZone = dateTime.Kind == DateTimeKind.Utc ? s_utcTimeZone : cachedData.Local; + return ConvertTime(dateTime, sourceTimeZone, destinationTimeZone, TimeZoneInfoOptions.None, cachedData); + } + + /// + /// Converts the value of the dateTime object from sourceTimeZone to destinationTimeZone + /// + public static DateTime ConvertTime(DateTime dateTime, TimeZoneInfo sourceTimeZone, TimeZoneInfo destinationTimeZone) => + ConvertTime(dateTime, sourceTimeZone, destinationTimeZone, TimeZoneInfoOptions.None, s_cachedData); + + /// + /// Converts the value of the dateTime object from sourceTimeZone to destinationTimeZone + /// + internal static DateTime ConvertTime(DateTime dateTime, TimeZoneInfo sourceTimeZone, TimeZoneInfo destinationTimeZone, TimeZoneInfoOptions flags) => + ConvertTime(dateTime, sourceTimeZone, destinationTimeZone, flags, s_cachedData); + + private static DateTime ConvertTime(DateTime dateTime, TimeZoneInfo sourceTimeZone, TimeZoneInfo destinationTimeZone, TimeZoneInfoOptions flags, CachedData cachedData) + { + if (sourceTimeZone == null) + { + throw new ArgumentNullException(nameof(sourceTimeZone)); + } + + if (destinationTimeZone == null) + { + throw new ArgumentNullException(nameof(destinationTimeZone)); + } + + DateTimeKind sourceKind = cachedData.GetCorrespondingKind(sourceTimeZone); + if (((flags & TimeZoneInfoOptions.NoThrowOnInvalidTime) == 0) && (dateTime.Kind != DateTimeKind.Unspecified) && (dateTime.Kind != sourceKind)) + { + throw new ArgumentException(SR.Argument_ConvertMismatch, nameof(sourceTimeZone)); + } + + // + // check to see if the DateTime is in an invalid time range. This check + // requires the current AdjustmentRule and DaylightTime - which are also + // needed to calculate 'sourceOffset' in the normal conversion case. + // By calculating the 'sourceOffset' here we improve the + // performance for the normal case at the expense of the 'ArgumentException' + // case and Loss-less Local special cases. + // + int? sourceRuleIndex; + AdjustmentRule sourceRule = sourceTimeZone.GetAdjustmentRuleForTime(dateTime, out sourceRuleIndex); + TimeSpan sourceOffset = sourceTimeZone.BaseUtcOffset; + + if (sourceRule != null) + { + sourceOffset = sourceOffset + sourceRule.BaseUtcOffsetDelta; + if (sourceRule.HasDaylightSaving) + { + bool sourceIsDaylightSavings = false; + DaylightTimeStruct sourceDaylightTime = sourceTimeZone.GetDaylightTime(dateTime.Year, sourceRule, sourceRuleIndex); + + // 'dateTime' might be in an invalid time range since it is in an AdjustmentRule + // period that supports DST + if (((flags & TimeZoneInfoOptions.NoThrowOnInvalidTime) == 0) && GetIsInvalidTime(dateTime, sourceRule, sourceDaylightTime)) + { + throw new ArgumentException(SR.Argument_DateTimeIsInvalid, nameof(dateTime)); + } + sourceIsDaylightSavings = GetIsDaylightSavings(dateTime, sourceRule, sourceDaylightTime, flags); + + // adjust the sourceOffset according to the Adjustment Rule / Daylight Saving Rule + sourceOffset += (sourceIsDaylightSavings ? sourceRule.DaylightDelta : TimeSpan.Zero /*FUTURE: sourceRule.StandardDelta*/); + } + } + + DateTimeKind targetKind = cachedData.GetCorrespondingKind(destinationTimeZone); + + // handle the special case of Loss-less Local->Local and UTC->UTC) + if (dateTime.Kind != DateTimeKind.Unspecified && sourceKind != DateTimeKind.Unspecified && sourceKind == targetKind) + { + return dateTime; + } + + long utcTicks = dateTime.Ticks - sourceOffset.Ticks; + + // handle the normal case by converting from 'source' to UTC and then to 'target' + bool isAmbiguousLocalDst; + DateTime targetConverted = ConvertUtcToTimeZone(utcTicks, destinationTimeZone, out isAmbiguousLocalDst); + + if (targetKind == DateTimeKind.Local) + { + // Because the ticks conversion between UTC and local is lossy, we need to capture whether the + // time is in a repeated hour so that it can be passed to the DateTime constructor. + return new DateTime(targetConverted.Ticks, DateTimeKind.Local, isAmbiguousLocalDst); + } + else + { + return new DateTime(targetConverted.Ticks, targetKind); + } + } + + /// + /// Converts the value of a DateTime object from Coordinated Universal Time (UTC) to the destinationTimeZone. + /// + public static DateTime ConvertTimeFromUtc(DateTime dateTime, TimeZoneInfo destinationTimeZone) => + ConvertTime(dateTime, s_utcTimeZone, destinationTimeZone, TimeZoneInfoOptions.None, s_cachedData); + + /// + /// Converts the value of a DateTime object to Coordinated Universal Time (UTC). + /// + public static DateTime ConvertTimeToUtc(DateTime dateTime) + { + if (dateTime.Kind == DateTimeKind.Utc) + { + return dateTime; + } + CachedData cachedData = s_cachedData; + return ConvertTime(dateTime, cachedData.Local, s_utcTimeZone, TimeZoneInfoOptions.None, cachedData); + } + + /// + /// Converts the value of a DateTime object to Coordinated Universal Time (UTC). + /// + internal static DateTime ConvertTimeToUtc(DateTime dateTime, TimeZoneInfoOptions flags) + { + if (dateTime.Kind == DateTimeKind.Utc) + { + return dateTime; + } + CachedData cachedData = s_cachedData; + return ConvertTime(dateTime, cachedData.Local, s_utcTimeZone, flags, cachedData); + } + + /// + /// Converts the value of a DateTime object to Coordinated Universal Time (UTC). + /// + public static DateTime ConvertTimeToUtc(DateTime dateTime, TimeZoneInfo sourceTimeZone) => + ConvertTime(dateTime, sourceTimeZone, s_utcTimeZone, TimeZoneInfoOptions.None, s_cachedData); + + /// + /// Returns value equality. Equals does not compare any localizable + /// String objects (DisplayName, StandardName, DaylightName). + /// + public bool Equals(TimeZoneInfo other) => + other != null && + string.Equals(_id, other._id, StringComparison.OrdinalIgnoreCase) && + HasSameRules(other); + + public override bool Equals(object obj) => Equals(obj as TimeZoneInfo); + + public static TimeZoneInfo FromSerializedString(string source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + if (source.Length == 0) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidSerializedString, source), nameof(source)); + } + + return StringSerializer.GetDeserializedTimeZoneInfo(source); + } + + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(_id); + + /// + /// Returns a ReadOnlyCollection containing all valid TimeZone's + /// from the local machine. The entries in the collection are sorted by + /// . + /// This method does *not* throw TimeZoneNotFoundException or InvalidTimeZoneException. + /// + public static ReadOnlyCollection GetSystemTimeZones() + { + CachedData cachedData = s_cachedData; + + lock (cachedData) + { + if (cachedData._readOnlySystemTimeZones == null) + { + PopulateAllSystemTimeZones(cachedData); + cachedData._allSystemTimeZonesRead = true; + + List list; + if (cachedData._systemTimeZones != null) + { + // return a collection of the cached system time zones + list = new List(cachedData._systemTimeZones.Values); + } + else + { + // return an empty collection + list = new List(); + } + + // sort and copy the TimeZoneInfo's into a ReadOnlyCollection for the user + list.Sort((x, y) => + { + // sort by BaseUtcOffset first and by DisplayName second - this is similar to the Windows Date/Time control panel + int comparison = x.BaseUtcOffset.CompareTo(y.BaseUtcOffset); + return comparison == 0 ? string.CompareOrdinal(x.DisplayName, y.DisplayName) : comparison; + }); + + cachedData._readOnlySystemTimeZones = new ReadOnlyCollection(list); + } + } + return cachedData._readOnlySystemTimeZones; + } + + /// + /// Value equality on the "adjustmentRules" array + /// + public bool HasSameRules(TimeZoneInfo other) + { + if (other == null) + { + throw new ArgumentNullException(nameof(other)); + } + + // check the utcOffset and supportsDaylightSavingTime members + if (_baseUtcOffset != other._baseUtcOffset || + _supportsDaylightSavingTime != other._supportsDaylightSavingTime) + { + return false; + } + + bool sameRules; + AdjustmentRule[] currentRules = _adjustmentRules; + AdjustmentRule[] otherRules = other._adjustmentRules; + + sameRules = + (currentRules == null && otherRules == null) || + (currentRules != null && otherRules != null); + + if (!sameRules) + { + // AdjustmentRule array mismatch + return false; + } + + if (currentRules != null) + { + if (currentRules.Length != otherRules.Length) + { + // AdjustmentRule array length mismatch + return false; + } + + for (int i = 0; i < currentRules.Length; i++) + { + if (!(currentRules[i]).Equals(otherRules[i])) + { + // AdjustmentRule value-equality mismatch + return false; + } + } + } + return sameRules; + } + + /// + /// Returns a TimeZoneInfo instance that represents the local time on the machine. + /// Accessing this property may throw InvalidTimeZoneException or COMException + /// if the machine is in an unstable or corrupt state. + /// + public static TimeZoneInfo Local => s_cachedData.Local; + + // + // ToSerializedString - + // + // "TimeZoneInfo" := TimeZoneInfo Data;[AdjustmentRule Data 1];...;[AdjustmentRule Data N] + // + // "TimeZoneInfo Data" := <_id>;<_baseUtcOffset>;<_displayName>; + // <_standardDisplayName>;<_daylightDispayName>; + // + // "AdjustmentRule Data" := ;;; + // [TransitionTime Data DST Start] + // [TransitionTime Data DST End] + // + // "TransitionTime Data" += ;;;; + // + public string ToSerializedString() => StringSerializer.GetSerializedString(this); + + /// + /// Returns the : "(GMT-08:00) Pacific Time (US & Canada); Tijuana" + /// + public override string ToString() => DisplayName; + + /// + /// Returns a TimeZoneInfo instance that represents Universal Coordinated Time (UTC) + /// + public static TimeZoneInfo Utc => s_utcTimeZone; + + private TimeZoneInfo( + string id, + TimeSpan baseUtcOffset, + string displayName, + string standardDisplayName, + string daylightDisplayName, + AdjustmentRule[] adjustmentRules, + bool disableDaylightSavingTime) + { + bool adjustmentRulesSupportDst; + ValidateTimeZoneInfo(id, baseUtcOffset, adjustmentRules, out adjustmentRulesSupportDst); + + _id = id; + _baseUtcOffset = baseUtcOffset; + _displayName = displayName; + _standardDisplayName = standardDisplayName; + _daylightDisplayName = disableDaylightSavingTime ? null : daylightDisplayName; + _supportsDaylightSavingTime = adjustmentRulesSupportDst && !disableDaylightSavingTime; + _adjustmentRules = adjustmentRules; + } + + /// + /// Returns a simple TimeZoneInfo instance that does not support Daylight Saving Time. + /// + public static TimeZoneInfo CreateCustomTimeZone( + string id, + TimeSpan baseUtcOffset, + string displayName, + string standardDisplayName) + { + return new TimeZoneInfo( + id, + baseUtcOffset, + displayName, + standardDisplayName, + standardDisplayName, + adjustmentRules: null, + disableDaylightSavingTime: false); + } + + /// + /// Returns a TimeZoneInfo instance that may support Daylight Saving Time. + /// + public static TimeZoneInfo CreateCustomTimeZone( + string id, + TimeSpan baseUtcOffset, + string displayName, + string standardDisplayName, + string daylightDisplayName, + AdjustmentRule[] adjustmentRules) + { + return CreateCustomTimeZone( + id, + baseUtcOffset, + displayName, + standardDisplayName, + daylightDisplayName, + adjustmentRules, + disableDaylightSavingTime: false); + } + + /// + /// Returns a TimeZoneInfo instance that may support Daylight Saving Time. + /// + public static TimeZoneInfo CreateCustomTimeZone( + string id, + TimeSpan baseUtcOffset, + string displayName, + string standardDisplayName, + string daylightDisplayName, + AdjustmentRule[] adjustmentRules, + bool disableDaylightSavingTime) + { + if (!disableDaylightSavingTime && adjustmentRules?.Length > 0) + { + adjustmentRules = (AdjustmentRule[])adjustmentRules.Clone(); + } + + return new TimeZoneInfo( + id, + baseUtcOffset, + displayName, + standardDisplayName, + daylightDisplayName, + adjustmentRules, + disableDaylightSavingTime); + } + + void IDeserializationCallback.OnDeserialization(object sender) + { + try + { + bool adjustmentRulesSupportDst; + ValidateTimeZoneInfo(_id, _baseUtcOffset, _adjustmentRules, out adjustmentRulesSupportDst); + + if (adjustmentRulesSupportDst != _supportsDaylightSavingTime) + { + throw new SerializationException(SR.Format(SR.Serialization_CorruptField, "SupportsDaylightSavingTime")); + } + } + catch (ArgumentException e) + { + throw new SerializationException(SR.Serialization_InvalidData, e); + } + catch (InvalidTimeZoneException e) + { + throw new SerializationException(SR.Serialization_InvalidData, e); + } + } + + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + info.AddValue("Id", _id); // Do not rename (binary serialization) + info.AddValue("DisplayName", _displayName); // Do not rename (binary serialization) + info.AddValue("StandardName", _standardDisplayName); // Do not rename (binary serialization) + info.AddValue("DaylightName", _daylightDisplayName); // Do not rename (binary serialization) + info.AddValue("BaseUtcOffset", _baseUtcOffset); // Do not rename (binary serialization) + info.AddValue("AdjustmentRules", _adjustmentRules); // Do not rename (binary serialization) + info.AddValue("SupportsDaylightSavingTime", _supportsDaylightSavingTime); // Do not rename (binary serialization) + } + + private TimeZoneInfo(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + _id = (string)info.GetValue("Id", typeof(string)); // Do not rename (binary serialization) + _displayName = (string)info.GetValue("DisplayName", typeof(string)); // Do not rename (binary serialization) + _standardDisplayName = (string)info.GetValue("StandardName", typeof(string)); // Do not rename (binary serialization) + _daylightDisplayName = (string)info.GetValue("DaylightName", typeof(string)); // Do not rename (binary serialization) + _baseUtcOffset = (TimeSpan)info.GetValue("BaseUtcOffset", typeof(TimeSpan)); // Do not rename (binary serialization) + _adjustmentRules = (AdjustmentRule[])info.GetValue("AdjustmentRules", typeof(AdjustmentRule[])); // Do not rename (binary serialization) + _supportsDaylightSavingTime = (bool)info.GetValue("SupportsDaylightSavingTime", typeof(bool)); // Do not rename (binary serialization) + } + + private AdjustmentRule GetAdjustmentRuleForTime(DateTime dateTime, out int? ruleIndex) + { + AdjustmentRule result = GetAdjustmentRuleForTime(dateTime, dateTimeisUtc: false, ruleIndex: out ruleIndex); + Debug.Assert(result == null || ruleIndex.HasValue, "If an AdjustmentRule was found, ruleIndex should also be set."); + + return result; + } + + private AdjustmentRule GetAdjustmentRuleForTime(DateTime dateTime, bool dateTimeisUtc, out int? ruleIndex) + { + if (_adjustmentRules == null || _adjustmentRules.Length == 0) + { + ruleIndex = null; + return null; + } + + // Only check the whole-date portion of the dateTime for DateTimeKind.Unspecified rules - + // This is because the AdjustmentRule DateStart & DateEnd are stored as + // Date-only values {4/2/2006 - 10/28/2006} but actually represent the + // time span {4/2/2006@00:00:00.00000 - 10/28/2006@23:59:59.99999} + DateTime date = dateTimeisUtc ? + (dateTime + BaseUtcOffset).Date : + dateTime.Date; + + int low = 0; + int high = _adjustmentRules.Length - 1; + + while (low <= high) + { + int median = low + ((high - low) >> 1); + + AdjustmentRule rule = _adjustmentRules[median]; + AdjustmentRule previousRule = median > 0 ? _adjustmentRules[median - 1] : rule; + + int compareResult = CompareAdjustmentRuleToDateTime(rule, previousRule, dateTime, date, dateTimeisUtc); + if (compareResult == 0) + { + ruleIndex = median; + return rule; + } + else if (compareResult < 0) + { + low = median + 1; + } + else + { + high = median - 1; + } + } + + ruleIndex = null; + return null; + } + + /// + /// Determines if 'rule' is the correct AdjustmentRule for the given dateTime. + /// + /// + /// A value less than zero if rule is for times before dateTime. + /// Zero if rule is correct for dateTime. + /// A value greater than zero if rule is for times after dateTime. + /// + private int CompareAdjustmentRuleToDateTime(AdjustmentRule rule, AdjustmentRule previousRule, + DateTime dateTime, DateTime dateOnly, bool dateTimeisUtc) + { + bool isAfterStart; + if (rule.DateStart.Kind == DateTimeKind.Utc) + { + DateTime dateTimeToCompare = dateTimeisUtc ? + dateTime : + // use the previous rule to compute the dateTimeToCompare, since the time daylight savings "switches" + // is based on the previous rule's offset + ConvertToUtc(dateTime, previousRule.DaylightDelta, previousRule.BaseUtcOffsetDelta); + + isAfterStart = dateTimeToCompare >= rule.DateStart; + } + else + { + // if the rule's DateStart is Unspecified, then use the whole-date portion + isAfterStart = dateOnly >= rule.DateStart; + } + + if (!isAfterStart) + { + return 1; + } + + bool isBeforeEnd; + if (rule.DateEnd.Kind == DateTimeKind.Utc) + { + DateTime dateTimeToCompare = dateTimeisUtc ? + dateTime : + ConvertToUtc(dateTime, rule.DaylightDelta, rule.BaseUtcOffsetDelta); + + isBeforeEnd = dateTimeToCompare <= rule.DateEnd; + } + else + { + // if the rule's DateEnd is Unspecified, then use the whole-date portion + isBeforeEnd = dateOnly <= rule.DateEnd; + } + + return isBeforeEnd ? 0 : -1; + } + + /// + /// Converts the dateTime to UTC using the specified deltas. + /// + private DateTime ConvertToUtc(DateTime dateTime, TimeSpan daylightDelta, TimeSpan baseUtcOffsetDelta) => + ConvertToFromUtc(dateTime, daylightDelta, baseUtcOffsetDelta, convertToUtc: true); + + /// + /// Converts the dateTime from UTC using the specified deltas. + /// + private DateTime ConvertFromUtc(DateTime dateTime, TimeSpan daylightDelta, TimeSpan baseUtcOffsetDelta) => + ConvertToFromUtc(dateTime, daylightDelta, baseUtcOffsetDelta, convertToUtc: false); + + /// + /// Converts the dateTime to or from UTC using the specified deltas. + /// + private DateTime ConvertToFromUtc(DateTime dateTime, TimeSpan daylightDelta, TimeSpan baseUtcOffsetDelta, bool convertToUtc) + { + TimeSpan offset = BaseUtcOffset + daylightDelta + baseUtcOffsetDelta; + if (convertToUtc) + { + offset = offset.Negate(); + } + + long ticks = dateTime.Ticks + offset.Ticks; + + return + ticks > DateTime.MaxValue.Ticks ? DateTime.MaxValue : + ticks < DateTime.MinValue.Ticks ? DateTime.MinValue : + new DateTime(ticks); + } + + /// + /// Helper function that converts a dateTime from UTC into the destinationTimeZone + /// - Returns DateTime.MaxValue when the converted value is too large. + /// - Returns DateTime.MinValue when the converted value is too small. + /// + private static DateTime ConvertUtcToTimeZone(long ticks, TimeZoneInfo destinationTimeZone, out bool isAmbiguousLocalDst) + { + // used to calculate the UTC offset in the destinationTimeZone + DateTime utcConverted = + ticks > DateTime.MaxValue.Ticks ? DateTime.MaxValue : + ticks < DateTime.MinValue.Ticks ? DateTime.MinValue : + new DateTime(ticks); + + // verify the time is between MinValue and MaxValue in the new time zone + TimeSpan offset = GetUtcOffsetFromUtc(utcConverted, destinationTimeZone, out isAmbiguousLocalDst); + ticks += offset.Ticks; + + return + ticks > DateTime.MaxValue.Ticks ? DateTime.MaxValue : + ticks < DateTime.MinValue.Ticks ? DateTime.MinValue : + new DateTime(ticks); + } + + /// + /// Helper function that returns a DaylightTime from a year and AdjustmentRule. + /// + private DaylightTimeStruct GetDaylightTime(int year, AdjustmentRule rule, int? ruleIndex) + { + TimeSpan delta = rule.DaylightDelta; + DateTime startTime; + DateTime endTime; + if (rule.NoDaylightTransitions) + { + // NoDaylightTransitions rules don't use DaylightTransition Start and End, instead + // the DateStart and DateEnd are UTC times that represent when daylight savings time changes. + // Convert the UTC times into adjusted time zone times. + + // use the previous rule to calculate the startTime, since the DST change happens w.r.t. the previous rule + AdjustmentRule previousRule = GetPreviousAdjustmentRule(rule, ruleIndex); + startTime = ConvertFromUtc(rule.DateStart, previousRule.DaylightDelta, previousRule.BaseUtcOffsetDelta); + + endTime = ConvertFromUtc(rule.DateEnd, rule.DaylightDelta, rule.BaseUtcOffsetDelta); + } + else + { + startTime = TransitionTimeToDateTime(year, rule.DaylightTransitionStart); + endTime = TransitionTimeToDateTime(year, rule.DaylightTransitionEnd); + } + return new DaylightTimeStruct(startTime, endTime, delta); + } + + /// + /// Helper function that checks if a given dateTime is in Daylight Saving Time (DST). + /// This function assumes the dateTime and AdjustmentRule are both in the same time zone. + /// + private static bool GetIsDaylightSavings(DateTime time, AdjustmentRule rule, DaylightTimeStruct daylightTime, TimeZoneInfoOptions flags) + { + if (rule == null) + { + return false; + } + + DateTime startTime; + DateTime endTime; + + if (time.Kind == DateTimeKind.Local) + { + // startTime and endTime represent the period from either the start of + // DST to the end and ***includes*** the potentially overlapped times + startTime = rule.IsStartDateMarkerForBeginningOfYear() ? + new DateTime(daylightTime.Start.Year, 1, 1, 0, 0, 0) : + daylightTime.Start + daylightTime.Delta; + + endTime = rule.IsEndDateMarkerForEndOfYear() ? + new DateTime(daylightTime.End.Year + 1, 1, 1, 0, 0, 0).AddTicks(-1) : + daylightTime.End; + } + else + { + // startTime and endTime represent the period from either the start of DST to the end and + // ***does not include*** the potentially overlapped times + // + // -=-=-=-=-=- Pacific Standard Time -=-=-=-=-=-=- + // April 2, 2006 October 29, 2006 + // 2AM 3AM 1AM 2AM + // | +1 hr | | -1 hr | + // | | | | + // [========== DST ========>) + // + // -=-=-=-=-=- Some Weird Time Zone -=-=-=-=-=-=- + // April 2, 2006 October 29, 2006 + // 1AM 2AM 2AM 3AM + // | -1 hr | | +1 hr | + // | | | | + // [======== DST ========>) + // + bool invalidAtStart = rule.DaylightDelta > TimeSpan.Zero; + + startTime = rule.IsStartDateMarkerForBeginningOfYear() ? + new DateTime(daylightTime.Start.Year, 1, 1, 0, 0, 0) : + daylightTime.Start + (invalidAtStart ? rule.DaylightDelta : TimeSpan.Zero); /* FUTURE: - rule.StandardDelta; */ + + endTime = rule.IsEndDateMarkerForEndOfYear() ? + new DateTime(daylightTime.End.Year + 1, 1, 1, 0, 0, 0).AddTicks(-1) : + daylightTime.End + (invalidAtStart ? -rule.DaylightDelta : TimeSpan.Zero); + } + + bool isDst = CheckIsDst(startTime, time, endTime, false, rule); + + // If this date was previously converted from a UTC date and we were able to detect that the local + // DateTime would be ambiguous, this data is stored in the DateTime to resolve this ambiguity. + if (isDst && time.Kind == DateTimeKind.Local) + { + // For normal time zones, the ambiguous hour is the last hour of daylight saving when you wind the + // clock back. It is theoretically possible to have a positive delta, (which would really be daylight + // reduction time), where you would have to wind the clock back in the begnning. + if (GetIsAmbiguousTime(time, rule, daylightTime)) + { + isDst = time.IsAmbiguousDaylightSavingTime(); + } + } + + return isDst; + } + + /// + /// Gets the offset that should be used to calculate DST start times from a UTC time. + /// + private TimeSpan GetDaylightSavingsStartOffsetFromUtc(TimeSpan baseUtcOffset, AdjustmentRule rule, int? ruleIndex) + { + if (rule.NoDaylightTransitions) + { + // use the previous rule to calculate the startTime, since the DST change happens w.r.t. the previous rule + AdjustmentRule previousRule = GetPreviousAdjustmentRule(rule, ruleIndex); + return baseUtcOffset + previousRule.BaseUtcOffsetDelta + previousRule.DaylightDelta; + } + else + { + return baseUtcOffset + rule.BaseUtcOffsetDelta; /* FUTURE: + rule.StandardDelta; */ + } + } + + /// + /// Gets the offset that should be used to calculate DST end times from a UTC time. + /// + private TimeSpan GetDaylightSavingsEndOffsetFromUtc(TimeSpan baseUtcOffset, AdjustmentRule rule) + { + // NOTE: even NoDaylightTransitions rules use this logic since DST ends w.r.t. the current rule + return baseUtcOffset + rule.BaseUtcOffsetDelta + rule.DaylightDelta; /* FUTURE: + rule.StandardDelta; */ + } + + /// + /// Helper function that checks if a given dateTime is in Daylight Saving Time (DST). + /// This function assumes the dateTime is in UTC and AdjustmentRule is in a different time zone. + /// + private static bool GetIsDaylightSavingsFromUtc(DateTime time, int year, TimeSpan utc, AdjustmentRule rule, int? ruleIndex, out bool isAmbiguousLocalDst, TimeZoneInfo zone) + { + isAmbiguousLocalDst = false; + + if (rule == null) + { + return false; + } + + // Get the daylight changes for the year of the specified time. + DaylightTimeStruct daylightTime = zone.GetDaylightTime(year, rule, ruleIndex); + + // The start and end times represent the range of universal times that are in DST for that year. + // Within that there is an ambiguous hour, usually right at the end, but at the beginning in + // the unusual case of a negative daylight savings delta. + // We need to handle the case if the current rule has daylight saving end by the end of year. If so, we need to check if next year starts with daylight saving on + // and get the actual daylight saving end time. Here is example for such case: + // Converting the UTC datetime "12/31/2011 8:00:00 PM" to "(UTC+03:00) Moscow, St. Petersburg, Volgograd (RTZ 2)" zone. + // In 2011 the daylight saving will go through the end of the year. If we use the end of 2011 as the daylight saving end, + // that will fail the conversion because the UTC time +4 hours (3 hours for the zone UTC offset and 1 hour for daylight saving) will move us to the next year "1/1/2012 12:00 AM", + // checking against the end of 2011 will tell we are not in daylight saving which is wrong and the conversion will be off by one hour. + // Note we handle the similar case when rule year start with daylight saving and previous year end with daylight saving. + + bool ignoreYearAdjustment = false; + TimeSpan dstStartOffset = zone.GetDaylightSavingsStartOffsetFromUtc(utc, rule, ruleIndex); + DateTime startTime; + if (rule.IsStartDateMarkerForBeginningOfYear() && daylightTime.Start.Year > DateTime.MinValue.Year) + { + int? previousYearRuleIndex; + AdjustmentRule previousYearRule = zone.GetAdjustmentRuleForTime( + new DateTime(daylightTime.Start.Year - 1, 12, 31), + out previousYearRuleIndex); + if (previousYearRule != null && previousYearRule.IsEndDateMarkerForEndOfYear()) + { + DaylightTimeStruct previousDaylightTime = zone.GetDaylightTime( + daylightTime.Start.Year - 1, + previousYearRule, + previousYearRuleIndex); + startTime = previousDaylightTime.Start - utc - previousYearRule.BaseUtcOffsetDelta; + ignoreYearAdjustment = true; + } + else + { + startTime = new DateTime(daylightTime.Start.Year, 1, 1, 0, 0, 0) - dstStartOffset; + } + } + else + { + startTime = daylightTime.Start - dstStartOffset; + } + + TimeSpan dstEndOffset = zone.GetDaylightSavingsEndOffsetFromUtc(utc, rule); + DateTime endTime; + if (rule.IsEndDateMarkerForEndOfYear() && daylightTime.End.Year < DateTime.MaxValue.Year) + { + int? nextYearRuleIndex; + AdjustmentRule nextYearRule = zone.GetAdjustmentRuleForTime( + new DateTime(daylightTime.End.Year + 1, 1, 1), + out nextYearRuleIndex); + if (nextYearRule != null && nextYearRule.IsStartDateMarkerForBeginningOfYear()) + { + if (nextYearRule.IsEndDateMarkerForEndOfYear()) + { + // next year end with daylight saving on too + endTime = new DateTime(daylightTime.End.Year + 1, 12, 31) - utc - nextYearRule.BaseUtcOffsetDelta - nextYearRule.DaylightDelta; + } + else + { + DaylightTimeStruct nextdaylightTime = zone.GetDaylightTime( + daylightTime.End.Year + 1, + nextYearRule, + nextYearRuleIndex); + endTime = nextdaylightTime.End - utc - nextYearRule.BaseUtcOffsetDelta - nextYearRule.DaylightDelta; + } + ignoreYearAdjustment = true; + } + else + { + endTime = new DateTime(daylightTime.End.Year + 1, 1, 1, 0, 0, 0).AddTicks(-1) - dstEndOffset; + } + } + else + { + endTime = daylightTime.End - dstEndOffset; + } + + DateTime ambiguousStart; + DateTime ambiguousEnd; + if (daylightTime.Delta.Ticks > 0) + { + ambiguousStart = endTime - daylightTime.Delta; + ambiguousEnd = endTime; + } + else + { + ambiguousStart = startTime; + ambiguousEnd = startTime - daylightTime.Delta; + } + + bool isDst = CheckIsDst(startTime, time, endTime, ignoreYearAdjustment, rule); + + // See if the resulting local time becomes ambiguous. This must be captured here or the + // DateTime will not be able to round-trip back to UTC accurately. + if (isDst) + { + isAmbiguousLocalDst = (time >= ambiguousStart && time < ambiguousEnd); + + if (!isAmbiguousLocalDst && ambiguousStart.Year != ambiguousEnd.Year) + { + // there exists an extreme corner case where the start or end period is on a year boundary and + // because of this the comparison above might have been performed for a year-early or a year-later + // than it should have been. + DateTime ambiguousStartModified; + DateTime ambiguousEndModified; + try + { + ambiguousStartModified = ambiguousStart.AddYears(1); + ambiguousEndModified = ambiguousEnd.AddYears(1); + isAmbiguousLocalDst = (time >= ambiguousStart && time < ambiguousEnd); + } + catch (ArgumentOutOfRangeException) { } + + if (!isAmbiguousLocalDst) + { + try + { + ambiguousStartModified = ambiguousStart.AddYears(-1); + ambiguousEndModified = ambiguousEnd.AddYears(-1); + isAmbiguousLocalDst = (time >= ambiguousStart && time < ambiguousEnd); + } + catch (ArgumentOutOfRangeException) { } + } + } + } + + return isDst; + } + + private static bool CheckIsDst(DateTime startTime, DateTime time, DateTime endTime, bool ignoreYearAdjustment, AdjustmentRule rule) + { + // NoDaylightTransitions AdjustmentRules should never get their year adjusted since they adjust the offset for the + // entire time period - which may be for multiple years + if (!ignoreYearAdjustment && !rule.NoDaylightTransitions) + { + int startTimeYear = startTime.Year; + int endTimeYear = endTime.Year; + + if (startTimeYear != endTimeYear) + { + endTime = endTime.AddYears(startTimeYear - endTimeYear); + } + + int timeYear = time.Year; + + if (startTimeYear != timeYear) + { + time = time.AddYears(startTimeYear - timeYear); + } + } + + if (startTime > endTime) + { + // In southern hemisphere, the daylight saving time starts later in the year, and ends in the beginning of next year. + // Note, the summer in the southern hemisphere begins late in the year. + return (time < endTime || time >= startTime); + } + else if (rule.NoDaylightTransitions) + { + // In NoDaylightTransitions AdjustmentRules, the startTime is always before the endTime, + // and both the start and end times are inclusive + return time >= startTime && time <= endTime; + } + else + { + // In northern hemisphere, the daylight saving time starts in the middle of the year. + return time >= startTime && time < endTime; + } + } + + /// + /// Returns true when the dateTime falls into an ambiguous time range. + /// + /// For example, in Pacific Standard Time on Sunday, October 29, 2006 time jumps from + /// 2AM to 1AM. This means the timeline on Sunday proceeds as follows: + /// 12AM ... [1AM ... 1:59:59AM -> 1AM ... 1:59:59AM] 2AM ... 3AM ... + /// + /// In this example, any DateTime values that fall into the [1AM - 1:59:59AM] range + /// are ambiguous; as it is unclear if these times are in Daylight Saving Time. + /// + private static bool GetIsAmbiguousTime(DateTime time, AdjustmentRule rule, DaylightTimeStruct daylightTime) + { + bool isAmbiguous = false; + if (rule == null || rule.DaylightDelta == TimeSpan.Zero) + { + return isAmbiguous; + } + + DateTime startAmbiguousTime; + DateTime endAmbiguousTime; + + // if at DST start we transition forward in time then there is an ambiguous time range at the DST end + if (rule.DaylightDelta > TimeSpan.Zero) + { + if (rule.IsEndDateMarkerForEndOfYear()) + { // year end with daylight on so there is no ambiguous time + return false; + } + startAmbiguousTime = daylightTime.End; + endAmbiguousTime = daylightTime.End - rule.DaylightDelta; /* FUTURE: + rule.StandardDelta; */ + } + else + { + if (rule.IsStartDateMarkerForBeginningOfYear()) + { // year start with daylight on so there is no ambiguous time + return false; + } + startAmbiguousTime = daylightTime.Start; + endAmbiguousTime = daylightTime.Start + rule.DaylightDelta; /* FUTURE: - rule.StandardDelta; */ + } + + isAmbiguous = (time >= endAmbiguousTime && time < startAmbiguousTime); + + if (!isAmbiguous && startAmbiguousTime.Year != endAmbiguousTime.Year) + { + // there exists an extreme corner case where the start or end period is on a year boundary and + // because of this the comparison above might have been performed for a year-early or a year-later + // than it should have been. + DateTime startModifiedAmbiguousTime; + DateTime endModifiedAmbiguousTime; + try + { + startModifiedAmbiguousTime = startAmbiguousTime.AddYears(1); + endModifiedAmbiguousTime = endAmbiguousTime.AddYears(1); + isAmbiguous = (time >= endModifiedAmbiguousTime && time < startModifiedAmbiguousTime); + } + catch (ArgumentOutOfRangeException) { } + + if (!isAmbiguous) + { + try + { + startModifiedAmbiguousTime = startAmbiguousTime.AddYears(-1); + endModifiedAmbiguousTime = endAmbiguousTime.AddYears(-1); + isAmbiguous = (time >= endModifiedAmbiguousTime && time < startModifiedAmbiguousTime); + } + catch (ArgumentOutOfRangeException) { } + } + } + return isAmbiguous; + } + + /// + /// Helper function that checks if a given DateTime is in an invalid time ("time hole") + /// A "time hole" occurs at a DST transition point when time jumps forward; + /// For example, in Pacific Standard Time on Sunday, April 2, 2006 time jumps from + /// 1:59:59.9999999 to 3AM. The time range 2AM to 2:59:59.9999999AM is the "time hole". + /// A "time hole" is not limited to only occurring at the start of DST, and may occur at + /// the end of DST as well. + /// + private static bool GetIsInvalidTime(DateTime time, AdjustmentRule rule, DaylightTimeStruct daylightTime) + { + bool isInvalid = false; + if (rule == null || rule.DaylightDelta == TimeSpan.Zero) + { + return isInvalid; + } + + DateTime startInvalidTime; + DateTime endInvalidTime; + + // if at DST start we transition forward in time then there is an ambiguous time range at the DST end + if (rule.DaylightDelta < TimeSpan.Zero) + { + // if the year ends with daylight saving on then there cannot be any time-hole's in that year. + if (rule.IsEndDateMarkerForEndOfYear()) + return false; + + startInvalidTime = daylightTime.End; + endInvalidTime = daylightTime.End - rule.DaylightDelta; /* FUTURE: + rule.StandardDelta; */ + } + else + { + // if the year starts with daylight saving on then there cannot be any time-hole's in that year. + if (rule.IsStartDateMarkerForBeginningOfYear()) + return false; + + startInvalidTime = daylightTime.Start; + endInvalidTime = daylightTime.Start + rule.DaylightDelta; /* FUTURE: - rule.StandardDelta; */ + } + + isInvalid = (time >= startInvalidTime && time < endInvalidTime); + + if (!isInvalid && startInvalidTime.Year != endInvalidTime.Year) + { + // there exists an extreme corner case where the start or end period is on a year boundary and + // because of this the comparison above might have been performed for a year-early or a year-later + // than it should have been. + DateTime startModifiedInvalidTime; + DateTime endModifiedInvalidTime; + try + { + startModifiedInvalidTime = startInvalidTime.AddYears(1); + endModifiedInvalidTime = endInvalidTime.AddYears(1); + isInvalid = (time >= startModifiedInvalidTime && time < endModifiedInvalidTime); + } + catch (ArgumentOutOfRangeException) { } + + if (!isInvalid) + { + try + { + startModifiedInvalidTime = startInvalidTime.AddYears(-1); + endModifiedInvalidTime = endInvalidTime.AddYears(-1); + isInvalid = (time >= startModifiedInvalidTime && time < endModifiedInvalidTime); + } + catch (ArgumentOutOfRangeException) { } + } + } + return isInvalid; + } + + /// + /// Helper function that calculates the UTC offset for a dateTime in a timeZone. + /// This function assumes that the dateTime is already converted into the timeZone. + /// + private static TimeSpan GetUtcOffset(DateTime time, TimeZoneInfo zone, TimeZoneInfoOptions flags) + { + TimeSpan baseOffset = zone.BaseUtcOffset; + int? ruleIndex; + AdjustmentRule rule = zone.GetAdjustmentRuleForTime(time, out ruleIndex); + + if (rule != null) + { + baseOffset = baseOffset + rule.BaseUtcOffsetDelta; + if (rule.HasDaylightSaving) + { + DaylightTimeStruct daylightTime = zone.GetDaylightTime(time.Year, rule, ruleIndex); + bool isDaylightSavings = GetIsDaylightSavings(time, rule, daylightTime, flags); + baseOffset += (isDaylightSavings ? rule.DaylightDelta : TimeSpan.Zero /* FUTURE: rule.StandardDelta */); + } + } + + return baseOffset; + } + + /// + /// Helper function that calculates the UTC offset for a UTC-dateTime in a timeZone. + /// This function assumes that the dateTime is represented in UTC and has *not* already been converted into the timeZone. + /// + private static TimeSpan GetUtcOffsetFromUtc(DateTime time, TimeZoneInfo zone) + { + bool isDaylightSavings; + return GetUtcOffsetFromUtc(time, zone, out isDaylightSavings); + } + + /// + /// Helper function that calculates the UTC offset for a UTC-dateTime in a timeZone. + /// This function assumes that the dateTime is represented in UTC and has *not* already been converted into the timeZone. + /// + private static TimeSpan GetUtcOffsetFromUtc(DateTime time, TimeZoneInfo zone, out bool isDaylightSavings) + { + bool isAmbiguousLocalDst; + return GetUtcOffsetFromUtc(time, zone, out isDaylightSavings, out isAmbiguousLocalDst); + } + + /// + /// Helper function that calculates the UTC offset for a UTC-dateTime in a timeZone. + /// This function assumes that the dateTime is represented in UTC and has *not* already been converted into the timeZone. + /// + internal static TimeSpan GetUtcOffsetFromUtc(DateTime time, TimeZoneInfo zone, out bool isDaylightSavings, out bool isAmbiguousLocalDst) + { + isDaylightSavings = false; + isAmbiguousLocalDst = false; + TimeSpan baseOffset = zone.BaseUtcOffset; + int year; + int? ruleIndex; + AdjustmentRule rule; + + if (time > s_maxDateOnly) + { + rule = zone.GetAdjustmentRuleForTime(DateTime.MaxValue, out ruleIndex); + year = 9999; + } + else if (time < s_minDateOnly) + { + rule = zone.GetAdjustmentRuleForTime(DateTime.MinValue, out ruleIndex); + year = 1; + } + else + { + rule = zone.GetAdjustmentRuleForTime(time, dateTimeisUtc: true, ruleIndex: out ruleIndex); + Debug.Assert(rule == null || ruleIndex.HasValue, + "If GetAdjustmentRuleForTime returned an AdjustmentRule, ruleIndex should also be set."); + + // As we get the associated rule using the adjusted targetTime, we should use the adjusted year (targetTime.Year) too as after adding the baseOffset, + // sometimes the year value can change if the input datetime was very close to the beginning or the end of the year. Examples of such cases: + // Libya Standard Time when used with the date 2011-12-31T23:59:59.9999999Z + // "W. Australia Standard Time" used with date 2005-12-31T23:59:00.0000000Z + DateTime targetTime = time + baseOffset; + year = targetTime.Year; + } + + if (rule != null) + { + baseOffset = baseOffset + rule.BaseUtcOffsetDelta; + if (rule.HasDaylightSaving) + { + isDaylightSavings = GetIsDaylightSavingsFromUtc(time, year, zone._baseUtcOffset, rule, ruleIndex, out isAmbiguousLocalDst, zone); + baseOffset += (isDaylightSavings ? rule.DaylightDelta : TimeSpan.Zero /* FUTURE: rule.StandardDelta */); + } + } + + return baseOffset; + } + + /// + /// Helper function that converts a year and TransitionTime into a DateTime. + /// + internal static DateTime TransitionTimeToDateTime(int year, TransitionTime transitionTime) + { + DateTime value; + DateTime timeOfDay = transitionTime.TimeOfDay; + + if (transitionTime.IsFixedDateRule) + { + // create a DateTime from the passed in year and the properties on the transitionTime + + // if the day is out of range for the month then use the last day of the month + int day = DateTime.DaysInMonth(year, transitionTime.Month); + + value = new DateTime(year, transitionTime.Month, (day < transitionTime.Day) ? day : transitionTime.Day, + timeOfDay.Hour, timeOfDay.Minute, timeOfDay.Second, timeOfDay.Millisecond); + } + else + { + if (transitionTime.Week <= 4) + { + // + // Get the (transitionTime.Week)th Sunday. + // + value = new DateTime(year, transitionTime.Month, 1, + timeOfDay.Hour, timeOfDay.Minute, timeOfDay.Second, timeOfDay.Millisecond); + + int dayOfWeek = (int)value.DayOfWeek; + int delta = (int)transitionTime.DayOfWeek - dayOfWeek; + if (delta < 0) + { + delta += 7; + } + delta += 7 * (transitionTime.Week - 1); + + if (delta > 0) + { + value = value.AddDays(delta); + } + } + else + { + // + // If TransitionWeek is greater than 4, we will get the last week. + // + int daysInMonth = DateTime.DaysInMonth(year, transitionTime.Month); + value = new DateTime(year, transitionTime.Month, daysInMonth, + timeOfDay.Hour, timeOfDay.Minute, timeOfDay.Second, timeOfDay.Millisecond); + + // This is the day of week for the last day of the month. + int dayOfWeek = (int)value.DayOfWeek; + int delta = dayOfWeek - (int)transitionTime.DayOfWeek; + if (delta < 0) + { + delta += 7; + } + + if (delta > 0) + { + value = value.AddDays(-delta); + } + } + } + return value; + } + + /// + /// Helper function for retrieving a TimeZoneInfo object by . + /// + /// This function may return null. + /// + /// assumes cachedData lock is taken + /// + private static TimeZoneInfoResult TryGetTimeZone(string id, bool dstDisabled, out TimeZoneInfo value, out Exception e, CachedData cachedData, bool alwaysFallbackToLocalMachine = false) + { + Debug.Assert(Monitor.IsEntered(cachedData)); + + TimeZoneInfoResult result = TimeZoneInfoResult.Success; + e = null; + TimeZoneInfo match = null; + + // check the cache + if (cachedData._systemTimeZones != null) + { + if (cachedData._systemTimeZones.TryGetValue(id, out match)) + { + if (dstDisabled && match._supportsDaylightSavingTime) + { + // we found a cache hit but we want a time zone without DST and this one has DST data + value = CreateCustomTimeZone(match._id, match._baseUtcOffset, match._displayName, match._standardDisplayName); + } + else + { + value = new TimeZoneInfo(match._id, match._baseUtcOffset, match._displayName, match._standardDisplayName, + match._daylightDisplayName, match._adjustmentRules, disableDaylightSavingTime: false); + } + return result; + } + } + + // Fall back to reading from the local machine when the cache is not fully populated. + // On UNIX, there may be some tzfiles that aren't in the zones.tab file, and thus aren't returned from GetSystemTimeZones(). + // If a caller asks for one of these zones before calling GetSystemTimeZones(), the time zone is returned successfully. But if + // GetSystemTimeZones() is called first, FindSystemTimeZoneById will throw TimeZoneNotFoundException, which is inconsistent. + // To fix this, when 'alwaysFallbackToLocalMachine' is true, even if _allSystemTimeZonesRead is true, try reading the tzfile + // from disk, but don't add the time zone to the list returned from GetSystemTimeZones(). These time zones will only be + // available if asked for directly. + if (!cachedData._allSystemTimeZonesRead || alwaysFallbackToLocalMachine) + { + result = TryGetTimeZoneFromLocalMachine(id, dstDisabled, out value, out e, cachedData); + } + else + { + result = TimeZoneInfoResult.TimeZoneNotFoundException; + value = null; + } + + return result; + } + + private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachine(string id, bool dstDisabled, out TimeZoneInfo value, out Exception e, CachedData cachedData) + { + TimeZoneInfoResult result; + TimeZoneInfo match; + + result = TryGetTimeZoneFromLocalMachine(id, out match, out e); + + if (result == TimeZoneInfoResult.Success) + { + if (cachedData._systemTimeZones == null) + cachedData._systemTimeZones = new Dictionary(); + + cachedData._systemTimeZones.Add(id, match); + + if (dstDisabled && match._supportsDaylightSavingTime) + { + // we found a cache hit but we want a time zone without DST and this one has DST data + value = CreateCustomTimeZone(match._id, match._baseUtcOffset, match._displayName, match._standardDisplayName); + } + else + { + value = new TimeZoneInfo(match._id, match._baseUtcOffset, match._displayName, match._standardDisplayName, + match._daylightDisplayName, match._adjustmentRules, disableDaylightSavingTime: false); + } + } + else + { + value = null; + } + + return result; + } + + /// + /// Helper function that validates the TimeSpan is within +/- 14.0 hours + /// + internal static bool UtcOffsetOutOfRange(TimeSpan offset) => + offset.TotalHours < -14.0 || offset.TotalHours > 14.0; + + /// + /// Helper function that performs all of the validation checks for the + /// factory methods and deserialization callback. + /// + private static void ValidateTimeZoneInfo(string id, TimeSpan baseUtcOffset, AdjustmentRule[] adjustmentRules, out bool adjustmentRulesSupportDst) + { + if (id == null) + { + throw new ArgumentNullException(nameof(id)); + } + + if (id.Length == 0) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidId, id), nameof(id)); + } + + if (UtcOffsetOutOfRange(baseUtcOffset)) + { + throw new ArgumentOutOfRangeException(nameof(baseUtcOffset), SR.ArgumentOutOfRange_UtcOffset); + } + + if (baseUtcOffset.Ticks % TimeSpan.TicksPerMinute != 0) + { + throw new ArgumentException(SR.Argument_TimeSpanHasSeconds, nameof(baseUtcOffset)); + } + + adjustmentRulesSupportDst = false; + + // + // "adjustmentRules" can either be null or a valid array of AdjustmentRule objects. + // A valid array is one that does not contain any null elements and all elements + // are sorted in chronological order + // + + if (adjustmentRules != null && adjustmentRules.Length != 0) + { + adjustmentRulesSupportDst = true; + AdjustmentRule prev = null; + AdjustmentRule current = null; + for (int i = 0; i < adjustmentRules.Length; i++) + { + prev = current; + current = adjustmentRules[i]; + + if (current == null) + { + throw new InvalidTimeZoneException(SR.Argument_AdjustmentRulesNoNulls); + } + + // FUTURE: check to see if this rule supports Daylight Saving Time + // adjustmentRulesSupportDst = adjustmentRulesSupportDst || current.SupportsDaylightSavingTime; + // FUTURE: test baseUtcOffset + current.StandardDelta + + if (UtcOffsetOutOfRange(baseUtcOffset + current.DaylightDelta)) + { + throw new InvalidTimeZoneException(SR.ArgumentOutOfRange_UtcOffsetAndDaylightDelta); + } + + if (prev != null && current.DateStart <= prev.DateEnd) + { + // verify the rules are in chronological order and the DateStart/DateEnd do not overlap + throw new InvalidTimeZoneException(SR.Argument_AdjustmentRulesOutOfOrder); + } + } + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/TimeZoneNotFoundException.cs b/external/corefx/src/Common/src/CoreLib/System/TimeZoneNotFoundException.cs new file mode 100644 index 0000000000..f83c6443c3 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/TimeZoneNotFoundException.cs @@ -0,0 +1,31 @@ +// 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.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class TimeZoneNotFoundException : Exception + { + public TimeZoneNotFoundException() + { + } + + public TimeZoneNotFoundException(String message) + : base(message) + { + } + + public TimeZoneNotFoundException(String message, Exception innerException) + : base(message, innerException) + { + } + + protected TimeZoneNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/TimeoutException.cs b/external/corefx/src/Common/src/CoreLib/System/TimeoutException.cs new file mode 100644 index 0000000000..ddaa47747d --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/TimeoutException.cs @@ -0,0 +1,44 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: Exception class for Timeout +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class TimeoutException : SystemException + { + public TimeoutException() + : base(SR.Arg_TimeoutException) + { + HResult = HResults.COR_E_TIMEOUT; + } + + public TimeoutException(String message) + : base(message) + { + HResult = HResults.COR_E_TIMEOUT; + } + + public TimeoutException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_TIMEOUT; + } + + protected TimeoutException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Tuple.cs b/external/corefx/src/Common/src/CoreLib/System/Tuple.cs new file mode 100644 index 0000000000..2e9dcd6e58 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Tuple.cs @@ -0,0 +1,1289 @@ +// 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.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text; + +// +// Note: F# compiler depends on the exact tuple hashing algorithm. Do not ever change it. +// + +namespace System +{ + /// + /// Helper so we can call some tuple methods recursively without knowing the underlying types. + /// + internal interface ITupleInternal : ITuple + { + string ToString(StringBuilder sb); + int GetHashCode(IEqualityComparer comparer); + } + + public static class Tuple + { + public static Tuple Create(T1 item1) + { + return new Tuple(item1); + } + + public static Tuple Create(T1 item1, T2 item2) + { + return new Tuple(item1, item2); + } + + public static Tuple Create(T1 item1, T2 item2, T3 item3) + { + return new Tuple(item1, item2, item3); + } + + public static Tuple Create(T1 item1, T2 item2, T3 item3, T4 item4) + { + return new Tuple(item1, item2, item3, item4); + } + + public static Tuple Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) + { + return new Tuple(item1, item2, item3, item4, item5); + } + + public static Tuple Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) + { + return new Tuple(item1, item2, item3, item4, item5, item6); + } + + public static Tuple Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) + { + return new Tuple(item1, item2, item3, item4, item5, item6, item7); + } + + public static Tuple> Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8) + { + return new Tuple>(item1, item2, item3, item4, item5, item6, item7, new Tuple(item8)); + } + + // From System.Web.Util.HashCodeCombiner + internal static int CombineHashCodes(int h1, int h2) + { + return (((h1 << 5) + h1) ^ h2); + } + + internal static int CombineHashCodes(int h1, int h2, int h3) + { + return CombineHashCodes(CombineHashCodes(h1, h2), h3); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4) + { + return CombineHashCodes(CombineHashCodes(h1, h2), CombineHashCodes(h3, h4)); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5) + { + return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4), h5); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6) + { + return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4), CombineHashCodes(h5, h6)); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7) + { + return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4), CombineHashCodes(h5, h6, h7)); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7, int h8) + { + return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4), CombineHashCodes(h5, h6, h7, h8)); + } + } + + [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif + public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple + { + private readonly T1 m_Item1; // Do not rename (binary serialization) + + public T1 Item1 { get { return m_Item1; } } + + public Tuple(T1 item1) + { + m_Item1 = item1; + } + + public override Boolean Equals(Object obj) + { + return ((IStructuralEquatable)this).Equals(obj, EqualityComparer.Default); + } + + Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer) + { + if (other == null) return false; + + Tuple objTuple = other as Tuple; + + if (objTuple == null) + { + return false; + } + + return comparer.Equals(m_Item1, objTuple.m_Item1); + } + + Int32 IComparable.CompareTo(Object obj) + { + return ((IStructuralComparable)this).CompareTo(obj, Comparer.Default); + } + + Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer) + { + if (other == null) return 1; + + Tuple objTuple = other as Tuple; + + if (objTuple == null) + { + throw new ArgumentException(SR.Format(SR.ArgumentException_TupleIncorrectType, this.GetType().ToString()), nameof(other)); + } + + return comparer.Compare(m_Item1, objTuple.m_Item1); + } + + public override int GetHashCode() + { + return ((IStructuralEquatable)this).GetHashCode(EqualityComparer.Default); + } + + Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return comparer.GetHashCode(m_Item1); + } + + Int32 ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return ((IStructuralEquatable)this).GetHashCode(comparer); + } + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append('('); + return ((ITupleInternal)this).ToString(sb); + } + + string ITupleInternal.ToString(StringBuilder sb) + { + sb.Append(m_Item1); + sb.Append(')'); + return sb.ToString(); + } + + /// + /// The number of positions in this data structure. + /// + int ITuple.Length => 1; + + /// + /// Get the element at position . + /// + object ITuple.this[int index] + { + get + { + if (index != 0) + { + throw new IndexOutOfRangeException(); + } + return Item1; + } + } + } + + [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif + public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple + { + private readonly T1 m_Item1; // Do not rename (binary serialization) + private readonly T2 m_Item2; // Do not rename (binary serialization) + + public T1 Item1 { get { return m_Item1; } } + public T2 Item2 { get { return m_Item2; } } + + public Tuple(T1 item1, T2 item2) + { + m_Item1 = item1; + m_Item2 = item2; + } + + public override Boolean Equals(Object obj) + { + return ((IStructuralEquatable)this).Equals(obj, EqualityComparer.Default); ; + } + + Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer) + { + if (other == null) return false; + + Tuple objTuple = other as Tuple; + + if (objTuple == null) + { + return false; + } + + return comparer.Equals(m_Item1, objTuple.m_Item1) && comparer.Equals(m_Item2, objTuple.m_Item2); + } + + Int32 IComparable.CompareTo(Object obj) + { + return ((IStructuralComparable)this).CompareTo(obj, Comparer.Default); + } + + Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer) + { + if (other == null) return 1; + + Tuple objTuple = other as Tuple; + + if (objTuple == null) + { + throw new ArgumentException(SR.Format(SR.ArgumentException_TupleIncorrectType, this.GetType().ToString()), nameof(other)); + } + + int c = 0; + + c = comparer.Compare(m_Item1, objTuple.m_Item1); + + if (c != 0) return c; + + return comparer.Compare(m_Item2, objTuple.m_Item2); + } + + public override int GetHashCode() + { + return ((IStructuralEquatable)this).GetHashCode(EqualityComparer.Default); + } + + Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return Tuple.CombineHashCodes(comparer.GetHashCode(m_Item1), comparer.GetHashCode(m_Item2)); + } + + Int32 ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return ((IStructuralEquatable)this).GetHashCode(comparer); + } + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append('('); + return ((ITupleInternal)this).ToString(sb); + } + + string ITupleInternal.ToString(StringBuilder sb) + { + sb.Append(m_Item1); + sb.Append(", "); + sb.Append(m_Item2); + sb.Append(')'); + return sb.ToString(); + } + + /// + /// The number of positions in this data structure. + /// + int ITuple.Length => 2; + + /// + /// Get the element at position . + /// + object ITuple.this[int index] + { + get + { + switch (index) + { + case 0: + return Item1; + case 1: + return Item2; + default: + throw new IndexOutOfRangeException(); + } + } + } + } + + [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif + public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple + { + private readonly T1 m_Item1; // Do not rename (binary serialization) + private readonly T2 m_Item2; // Do not rename (binary serialization) + private readonly T3 m_Item3; // Do not rename (binary serialization) + + public T1 Item1 { get { return m_Item1; } } + public T2 Item2 { get { return m_Item2; } } + public T3 Item3 { get { return m_Item3; } } + + public Tuple(T1 item1, T2 item2, T3 item3) + { + m_Item1 = item1; + m_Item2 = item2; + m_Item3 = item3; + } + + public override Boolean Equals(Object obj) + { + return ((IStructuralEquatable)this).Equals(obj, EqualityComparer.Default); ; + } + + Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer) + { + if (other == null) return false; + + Tuple objTuple = other as Tuple; + + if (objTuple == null) + { + return false; + } + + return comparer.Equals(m_Item1, objTuple.m_Item1) && comparer.Equals(m_Item2, objTuple.m_Item2) && comparer.Equals(m_Item3, objTuple.m_Item3); + } + + Int32 IComparable.CompareTo(Object obj) + { + return ((IStructuralComparable)this).CompareTo(obj, Comparer.Default); + } + + Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer) + { + if (other == null) return 1; + + Tuple objTuple = other as Tuple; + + if (objTuple == null) + { + throw new ArgumentException(SR.Format(SR.ArgumentException_TupleIncorrectType, this.GetType().ToString()), nameof(other)); + } + + int c = 0; + + c = comparer.Compare(m_Item1, objTuple.m_Item1); + + if (c != 0) return c; + + c = comparer.Compare(m_Item2, objTuple.m_Item2); + + if (c != 0) return c; + + return comparer.Compare(m_Item3, objTuple.m_Item3); + } + + public override int GetHashCode() + { + return ((IStructuralEquatable)this).GetHashCode(EqualityComparer.Default); + } + + Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return Tuple.CombineHashCodes(comparer.GetHashCode(m_Item1), comparer.GetHashCode(m_Item2), comparer.GetHashCode(m_Item3)); + } + + Int32 ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return ((IStructuralEquatable)this).GetHashCode(comparer); + } + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append('('); + return ((ITupleInternal)this).ToString(sb); + } + + string ITupleInternal.ToString(StringBuilder sb) + { + sb.Append(m_Item1); + sb.Append(", "); + sb.Append(m_Item2); + sb.Append(", "); + sb.Append(m_Item3); + sb.Append(')'); + return sb.ToString(); + } + + /// + /// The number of positions in this data structure. + /// + int ITuple.Length => 3; + + /// + /// Get the element at position . + /// + object ITuple.this[int index] + { + get + { + switch (index) + { + case 0: + return Item1; + case 1: + return Item2; + case 2: + return Item3; + default: + throw new IndexOutOfRangeException(); + } + } + } + } + + [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif + public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple + { + private readonly T1 m_Item1; // Do not rename (binary serialization) + private readonly T2 m_Item2; // Do not rename (binary serialization) + private readonly T3 m_Item3; // Do not rename (binary serialization) + private readonly T4 m_Item4; // Do not rename (binary serialization) + + public T1 Item1 { get { return m_Item1; } } + public T2 Item2 { get { return m_Item2; } } + public T3 Item3 { get { return m_Item3; } } + public T4 Item4 { get { return m_Item4; } } + + public Tuple(T1 item1, T2 item2, T3 item3, T4 item4) + { + m_Item1 = item1; + m_Item2 = item2; + m_Item3 = item3; + m_Item4 = item4; + } + + public override Boolean Equals(Object obj) + { + return ((IStructuralEquatable)this).Equals(obj, EqualityComparer.Default); ; + } + + Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer) + { + if (other == null) return false; + + Tuple objTuple = other as Tuple; + + if (objTuple == null) + { + return false; + } + + return comparer.Equals(m_Item1, objTuple.m_Item1) && comparer.Equals(m_Item2, objTuple.m_Item2) && comparer.Equals(m_Item3, objTuple.m_Item3) && comparer.Equals(m_Item4, objTuple.m_Item4); + } + + Int32 IComparable.CompareTo(Object obj) + { + return ((IStructuralComparable)this).CompareTo(obj, Comparer.Default); + } + + Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer) + { + if (other == null) return 1; + + Tuple objTuple = other as Tuple; + + if (objTuple == null) + { + throw new ArgumentException(SR.Format(SR.ArgumentException_TupleIncorrectType, this.GetType().ToString()), nameof(other)); + } + + int c = 0; + + c = comparer.Compare(m_Item1, objTuple.m_Item1); + + if (c != 0) return c; + + c = comparer.Compare(m_Item2, objTuple.m_Item2); + + if (c != 0) return c; + + c = comparer.Compare(m_Item3, objTuple.m_Item3); + + if (c != 0) return c; + + return comparer.Compare(m_Item4, objTuple.m_Item4); + } + + public override int GetHashCode() + { + return ((IStructuralEquatable)this).GetHashCode(EqualityComparer.Default); + } + + Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return Tuple.CombineHashCodes(comparer.GetHashCode(m_Item1), comparer.GetHashCode(m_Item2), comparer.GetHashCode(m_Item3), comparer.GetHashCode(m_Item4)); + } + + Int32 ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return ((IStructuralEquatable)this).GetHashCode(comparer); + } + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append('('); + return ((ITupleInternal)this).ToString(sb); + } + + string ITupleInternal.ToString(StringBuilder sb) + { + sb.Append(m_Item1); + sb.Append(", "); + sb.Append(m_Item2); + sb.Append(", "); + sb.Append(m_Item3); + sb.Append(", "); + sb.Append(m_Item4); + sb.Append(')'); + return sb.ToString(); + } + + /// + /// The number of positions in this data structure. + /// + int ITuple.Length => 4; + + /// + /// Get the element at position . + /// + object ITuple.this[int index] + { + get + { + switch (index) + { + case 0: + return Item1; + case 1: + return Item2; + case 2: + return Item3; + case 3: + return Item4; + default: + throw new IndexOutOfRangeException(); + } + } + } + } + + [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif + public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple + { + private readonly T1 m_Item1; // Do not rename (binary serialization) + private readonly T2 m_Item2; // Do not rename (binary serialization) + private readonly T3 m_Item3; // Do not rename (binary serialization) + private readonly T4 m_Item4; // Do not rename (binary serialization) + private readonly T5 m_Item5; // Do not rename (binary serialization) + + public T1 Item1 { get { return m_Item1; } } + public T2 Item2 { get { return m_Item2; } } + public T3 Item3 { get { return m_Item3; } } + public T4 Item4 { get { return m_Item4; } } + public T5 Item5 { get { return m_Item5; } } + + public Tuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) + { + m_Item1 = item1; + m_Item2 = item2; + m_Item3 = item3; + m_Item4 = item4; + m_Item5 = item5; + } + + public override Boolean Equals(Object obj) + { + return ((IStructuralEquatable)this).Equals(obj, EqualityComparer.Default); ; + } + + Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer) + { + if (other == null) return false; + + Tuple objTuple = other as Tuple; + + if (objTuple == null) + { + return false; + } + + return comparer.Equals(m_Item1, objTuple.m_Item1) && comparer.Equals(m_Item2, objTuple.m_Item2) && comparer.Equals(m_Item3, objTuple.m_Item3) && comparer.Equals(m_Item4, objTuple.m_Item4) && comparer.Equals(m_Item5, objTuple.m_Item5); + } + + Int32 IComparable.CompareTo(Object obj) + { + return ((IStructuralComparable)this).CompareTo(obj, Comparer.Default); + } + + Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer) + { + if (other == null) return 1; + + Tuple objTuple = other as Tuple; + + if (objTuple == null) + { + throw new ArgumentException(SR.Format(SR.ArgumentException_TupleIncorrectType, this.GetType().ToString()), nameof(other)); + } + + int c = 0; + + c = comparer.Compare(m_Item1, objTuple.m_Item1); + + if (c != 0) return c; + + c = comparer.Compare(m_Item2, objTuple.m_Item2); + + if (c != 0) return c; + + c = comparer.Compare(m_Item3, objTuple.m_Item3); + + if (c != 0) return c; + + c = comparer.Compare(m_Item4, objTuple.m_Item4); + + if (c != 0) return c; + + return comparer.Compare(m_Item5, objTuple.m_Item5); + } + + public override int GetHashCode() + { + return ((IStructuralEquatable)this).GetHashCode(EqualityComparer.Default); + } + + Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return Tuple.CombineHashCodes(comparer.GetHashCode(m_Item1), comparer.GetHashCode(m_Item2), comparer.GetHashCode(m_Item3), comparer.GetHashCode(m_Item4), comparer.GetHashCode(m_Item5)); + } + + Int32 ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return ((IStructuralEquatable)this).GetHashCode(comparer); + } + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append('('); + return ((ITupleInternal)this).ToString(sb); + } + + string ITupleInternal.ToString(StringBuilder sb) + { + sb.Append(m_Item1); + sb.Append(", "); + sb.Append(m_Item2); + sb.Append(", "); + sb.Append(m_Item3); + sb.Append(", "); + sb.Append(m_Item4); + sb.Append(", "); + sb.Append(m_Item5); + sb.Append(')'); + return sb.ToString(); + } + + /// + /// The number of positions in this data structure. + /// + int ITuple.Length => 5; + + /// + /// Get the element at position . + /// + object ITuple.this[int index] + { + get + { + switch (index) + { + case 0: + return Item1; + case 1: + return Item2; + case 2: + return Item3; + case 3: + return Item4; + case 4: + return Item5; + default: + throw new IndexOutOfRangeException(); + } + } + } + } + + [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif + public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple + { + private readonly T1 m_Item1; // Do not rename (binary serialization) + private readonly T2 m_Item2; // Do not rename (binary serialization) + private readonly T3 m_Item3; // Do not rename (binary serialization) + private readonly T4 m_Item4; // Do not rename (binary serialization) + private readonly T5 m_Item5; // Do not rename (binary serialization) + private readonly T6 m_Item6; // Do not rename (binary serialization) + + public T1 Item1 { get { return m_Item1; } } + public T2 Item2 { get { return m_Item2; } } + public T3 Item3 { get { return m_Item3; } } + public T4 Item4 { get { return m_Item4; } } + public T5 Item5 { get { return m_Item5; } } + public T6 Item6 { get { return m_Item6; } } + + public Tuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) + { + m_Item1 = item1; + m_Item2 = item2; + m_Item3 = item3; + m_Item4 = item4; + m_Item5 = item5; + m_Item6 = item6; + } + + public override Boolean Equals(Object obj) + { + return ((IStructuralEquatable)this).Equals(obj, EqualityComparer.Default); ; + } + + Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer) + { + if (other == null) return false; + + Tuple objTuple = other as Tuple; + + if (objTuple == null) + { + return false; + } + + return comparer.Equals(m_Item1, objTuple.m_Item1) && comparer.Equals(m_Item2, objTuple.m_Item2) && comparer.Equals(m_Item3, objTuple.m_Item3) && comparer.Equals(m_Item4, objTuple.m_Item4) && comparer.Equals(m_Item5, objTuple.m_Item5) && comparer.Equals(m_Item6, objTuple.m_Item6); + } + + Int32 IComparable.CompareTo(Object obj) + { + return ((IStructuralComparable)this).CompareTo(obj, Comparer.Default); + } + + Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer) + { + if (other == null) return 1; + + Tuple objTuple = other as Tuple; + + if (objTuple == null) + { + throw new ArgumentException(SR.Format(SR.ArgumentException_TupleIncorrectType, this.GetType().ToString()), nameof(other)); + } + + int c = 0; + + c = comparer.Compare(m_Item1, objTuple.m_Item1); + + if (c != 0) return c; + + c = comparer.Compare(m_Item2, objTuple.m_Item2); + + if (c != 0) return c; + + c = comparer.Compare(m_Item3, objTuple.m_Item3); + + if (c != 0) return c; + + c = comparer.Compare(m_Item4, objTuple.m_Item4); + + if (c != 0) return c; + + c = comparer.Compare(m_Item5, objTuple.m_Item5); + + if (c != 0) return c; + + return comparer.Compare(m_Item6, objTuple.m_Item6); + } + + public override int GetHashCode() + { + return ((IStructuralEquatable)this).GetHashCode(EqualityComparer.Default); + } + + Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return Tuple.CombineHashCodes(comparer.GetHashCode(m_Item1), comparer.GetHashCode(m_Item2), comparer.GetHashCode(m_Item3), comparer.GetHashCode(m_Item4), comparer.GetHashCode(m_Item5), comparer.GetHashCode(m_Item6)); + } + + Int32 ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return ((IStructuralEquatable)this).GetHashCode(comparer); + } + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append('('); + return ((ITupleInternal)this).ToString(sb); + } + + string ITupleInternal.ToString(StringBuilder sb) + { + sb.Append(m_Item1); + sb.Append(", "); + sb.Append(m_Item2); + sb.Append(", "); + sb.Append(m_Item3); + sb.Append(", "); + sb.Append(m_Item4); + sb.Append(", "); + sb.Append(m_Item5); + sb.Append(", "); + sb.Append(m_Item6); + sb.Append(')'); + return sb.ToString(); + } + + /// + /// The number of positions in this data structure. + /// + int ITuple.Length => 6; + + /// + /// Get the element at position . + /// + object ITuple.this[int index] + { + get + { + switch (index) + { + case 0: + return Item1; + case 1: + return Item2; + case 2: + return Item3; + case 3: + return Item4; + case 4: + return Item5; + case 5: + return Item6; + default: + throw new IndexOutOfRangeException(); + } + } + } + } + + [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif + public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple + { + private readonly T1 m_Item1; // Do not rename (binary serialization) + private readonly T2 m_Item2; // Do not rename (binary serialization) + private readonly T3 m_Item3; // Do not rename (binary serialization) + private readonly T4 m_Item4; // Do not rename (binary serialization) + private readonly T5 m_Item5; // Do not rename (binary serialization) + private readonly T6 m_Item6; // Do not rename (binary serialization) + private readonly T7 m_Item7; // Do not rename (binary serialization) + + public T1 Item1 { get { return m_Item1; } } + public T2 Item2 { get { return m_Item2; } } + public T3 Item3 { get { return m_Item3; } } + public T4 Item4 { get { return m_Item4; } } + public T5 Item5 { get { return m_Item5; } } + public T6 Item6 { get { return m_Item6; } } + public T7 Item7 { get { return m_Item7; } } + + public Tuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) + { + m_Item1 = item1; + m_Item2 = item2; + m_Item3 = item3; + m_Item4 = item4; + m_Item5 = item5; + m_Item6 = item6; + m_Item7 = item7; + } + + public override Boolean Equals(Object obj) + { + return ((IStructuralEquatable)this).Equals(obj, EqualityComparer.Default); ; + } + + Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer) + { + if (other == null) return false; + + Tuple objTuple = other as Tuple; + + if (objTuple == null) + { + return false; + } + + return comparer.Equals(m_Item1, objTuple.m_Item1) && comparer.Equals(m_Item2, objTuple.m_Item2) && comparer.Equals(m_Item3, objTuple.m_Item3) && comparer.Equals(m_Item4, objTuple.m_Item4) && comparer.Equals(m_Item5, objTuple.m_Item5) && comparer.Equals(m_Item6, objTuple.m_Item6) && comparer.Equals(m_Item7, objTuple.m_Item7); + } + + Int32 IComparable.CompareTo(Object obj) + { + return ((IStructuralComparable)this).CompareTo(obj, Comparer.Default); + } + + Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer) + { + if (other == null) return 1; + + Tuple objTuple = other as Tuple; + + if (objTuple == null) + { + throw new ArgumentException(SR.Format(SR.ArgumentException_TupleIncorrectType, this.GetType().ToString()), nameof(other)); + } + + int c = 0; + + c = comparer.Compare(m_Item1, objTuple.m_Item1); + + if (c != 0) return c; + + c = comparer.Compare(m_Item2, objTuple.m_Item2); + + if (c != 0) return c; + + c = comparer.Compare(m_Item3, objTuple.m_Item3); + + if (c != 0) return c; + + c = comparer.Compare(m_Item4, objTuple.m_Item4); + + if (c != 0) return c; + + c = comparer.Compare(m_Item5, objTuple.m_Item5); + + if (c != 0) return c; + + c = comparer.Compare(m_Item6, objTuple.m_Item6); + + if (c != 0) return c; + + return comparer.Compare(m_Item7, objTuple.m_Item7); + } + + public override int GetHashCode() + { + return ((IStructuralEquatable)this).GetHashCode(EqualityComparer.Default); + } + + Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return Tuple.CombineHashCodes(comparer.GetHashCode(m_Item1), comparer.GetHashCode(m_Item2), comparer.GetHashCode(m_Item3), comparer.GetHashCode(m_Item4), comparer.GetHashCode(m_Item5), comparer.GetHashCode(m_Item6), comparer.GetHashCode(m_Item7)); + } + + Int32 ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return ((IStructuralEquatable)this).GetHashCode(comparer); + } + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append('('); + return ((ITupleInternal)this).ToString(sb); + } + + string ITupleInternal.ToString(StringBuilder sb) + { + sb.Append(m_Item1); + sb.Append(", "); + sb.Append(m_Item2); + sb.Append(", "); + sb.Append(m_Item3); + sb.Append(", "); + sb.Append(m_Item4); + sb.Append(", "); + sb.Append(m_Item5); + sb.Append(", "); + sb.Append(m_Item6); + sb.Append(", "); + sb.Append(m_Item7); + sb.Append(')'); + return sb.ToString(); + } + + /// + /// The number of positions in this data structure. + /// + int ITuple.Length => 7; + + /// + /// Get the element at position . + /// + object ITuple.this[int index] + { + get + { + switch (index) + { + case 0: + return Item1; + case 1: + return Item2; + case 2: + return Item3; + case 3: + return Item4; + case 4: + return Item5; + case 5: + return Item6; + case 6: + return Item7; + default: + throw new IndexOutOfRangeException(); + } + } + } + } + + [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif + public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple + { + private readonly T1 m_Item1; // Do not rename (binary serialization) + private readonly T2 m_Item2; // Do not rename (binary serialization) + private readonly T3 m_Item3; // Do not rename (binary serialization) + private readonly T4 m_Item4; // Do not rename (binary serialization) + private readonly T5 m_Item5; // Do not rename (binary serialization) + private readonly T6 m_Item6; // Do not rename (binary serialization) + private readonly T7 m_Item7; // Do not rename (binary serialization) + private readonly TRest m_Rest; // Do not rename (binary serialization) + + public T1 Item1 { get { return m_Item1; } } + public T2 Item2 { get { return m_Item2; } } + public T3 Item3 { get { return m_Item3; } } + public T4 Item4 { get { return m_Item4; } } + public T5 Item5 { get { return m_Item5; } } + public T6 Item6 { get { return m_Item6; } } + public T7 Item7 { get { return m_Item7; } } + public TRest Rest { get { return m_Rest; } } + + public Tuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, TRest rest) + { + if (!(rest is ITupleInternal)) + { + throw new ArgumentException(SR.ArgumentException_TupleLastArgumentNotATuple); + } + + m_Item1 = item1; + m_Item2 = item2; + m_Item3 = item3; + m_Item4 = item4; + m_Item5 = item5; + m_Item6 = item6; + m_Item7 = item7; + m_Rest = rest; + } + + public override Boolean Equals(Object obj) + { + return ((IStructuralEquatable)this).Equals(obj, EqualityComparer.Default); ; + } + + Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer) + { + if (other == null) return false; + + Tuple objTuple = other as Tuple; + + if (objTuple == null) + { + return false; + } + + return comparer.Equals(m_Item1, objTuple.m_Item1) && comparer.Equals(m_Item2, objTuple.m_Item2) && comparer.Equals(m_Item3, objTuple.m_Item3) && comparer.Equals(m_Item4, objTuple.m_Item4) && comparer.Equals(m_Item5, objTuple.m_Item5) && comparer.Equals(m_Item6, objTuple.m_Item6) && comparer.Equals(m_Item7, objTuple.m_Item7) && comparer.Equals(m_Rest, objTuple.m_Rest); + } + + Int32 IComparable.CompareTo(Object obj) + { + return ((IStructuralComparable)this).CompareTo(obj, Comparer.Default); + } + + Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer) + { + if (other == null) return 1; + + Tuple objTuple = other as Tuple; + + if (objTuple == null) + { + throw new ArgumentException(SR.Format(SR.ArgumentException_TupleIncorrectType, this.GetType().ToString()), nameof(other)); + } + + int c = 0; + + c = comparer.Compare(m_Item1, objTuple.m_Item1); + + if (c != 0) return c; + + c = comparer.Compare(m_Item2, objTuple.m_Item2); + + if (c != 0) return c; + + c = comparer.Compare(m_Item3, objTuple.m_Item3); + + if (c != 0) return c; + + c = comparer.Compare(m_Item4, objTuple.m_Item4); + + if (c != 0) return c; + + c = comparer.Compare(m_Item5, objTuple.m_Item5); + + if (c != 0) return c; + + c = comparer.Compare(m_Item6, objTuple.m_Item6); + + if (c != 0) return c; + + c = comparer.Compare(m_Item7, objTuple.m_Item7); + + if (c != 0) return c; + + return comparer.Compare(m_Rest, objTuple.m_Rest); + } + + public override int GetHashCode() + { + return ((IStructuralEquatable)this).GetHashCode(EqualityComparer.Default); + } + + Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + // We want to have a limited hash in this case. We'll use the last 8 elements of the tuple + ITupleInternal t = (ITupleInternal)m_Rest; + if (t.Length >= 8) { return t.GetHashCode(comparer); } + + // In this case, the rest memeber has less than 8 elements so we need to combine some our elements with the elements in rest + int k = 8 - t.Length; + switch (k) + { + case 1: + return Tuple.CombineHashCodes(comparer.GetHashCode(m_Item7), t.GetHashCode(comparer)); + case 2: + return Tuple.CombineHashCodes(comparer.GetHashCode(m_Item6), comparer.GetHashCode(m_Item7), t.GetHashCode(comparer)); + case 3: + return Tuple.CombineHashCodes(comparer.GetHashCode(m_Item5), comparer.GetHashCode(m_Item6), comparer.GetHashCode(m_Item7), t.GetHashCode(comparer)); + case 4: + return Tuple.CombineHashCodes(comparer.GetHashCode(m_Item4), comparer.GetHashCode(m_Item5), comparer.GetHashCode(m_Item6), comparer.GetHashCode(m_Item7), t.GetHashCode(comparer)); + case 5: + return Tuple.CombineHashCodes(comparer.GetHashCode(m_Item3), comparer.GetHashCode(m_Item4), comparer.GetHashCode(m_Item5), comparer.GetHashCode(m_Item6), comparer.GetHashCode(m_Item7), t.GetHashCode(comparer)); + case 6: + return Tuple.CombineHashCodes(comparer.GetHashCode(m_Item2), comparer.GetHashCode(m_Item3), comparer.GetHashCode(m_Item4), comparer.GetHashCode(m_Item5), comparer.GetHashCode(m_Item6), comparer.GetHashCode(m_Item7), t.GetHashCode(comparer)); + case 7: + return Tuple.CombineHashCodes(comparer.GetHashCode(m_Item1), comparer.GetHashCode(m_Item2), comparer.GetHashCode(m_Item3), comparer.GetHashCode(m_Item4), comparer.GetHashCode(m_Item5), comparer.GetHashCode(m_Item6), comparer.GetHashCode(m_Item7), t.GetHashCode(comparer)); + } +#if !MONO + Debug.Fail("Missed all cases for computing Tuple hash code"); +#endif + return -1; + } + + Int32 ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return ((IStructuralEquatable)this).GetHashCode(comparer); + } + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append('('); + return ((ITupleInternal)this).ToString(sb); + } + + string ITupleInternal.ToString(StringBuilder sb) + { + sb.Append(m_Item1); + sb.Append(", "); + sb.Append(m_Item2); + sb.Append(", "); + sb.Append(m_Item3); + sb.Append(", "); + sb.Append(m_Item4); + sb.Append(", "); + sb.Append(m_Item5); + sb.Append(", "); + sb.Append(m_Item6); + sb.Append(", "); + sb.Append(m_Item7); + sb.Append(", "); + return ((ITupleInternal)m_Rest).ToString(sb); + } + + /// + /// The number of positions in this data structure. + /// + int ITuple.Length + { + get + { + return 7 + ((ITupleInternal)Rest).Length; + } + } + + /// + /// Get the element at position . + /// + object ITuple.this[int index] + { + get + { + switch (index) + { + case 0: + return Item1; + case 1: + return Item2; + case 2: + return Item3; + case 3: + return Item4; + case 4: + return Item5; + case 5: + return Item6; + case 6: + return Item7; + } + + return ((ITupleInternal)Rest)[index - 7]; + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/TupleExtensions.cs b/external/corefx/src/Common/src/CoreLib/System/TupleExtensions.cs new file mode 100644 index 0000000000..f217da926b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/TupleExtensions.cs @@ -0,0 +1,974 @@ +// 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. + +#if !MONO +using System.ComponentModel; +#endif +using System.Runtime.CompilerServices; + +namespace System +{ + /// + /// Provides extension methods for instances to interop with C# tuples features (deconstruction syntax, converting from and to ). + /// + public static class TupleExtensions + { + #region Deconstruct + /// + /// Deconstruct a properly nested with 1 elements. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public static void Deconstruct( + this Tuple value, + out T1 item1) + { + item1 = value.Item1; + } + + /// + /// Deconstruct a properly nested with 2 elements. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public static void Deconstruct( + this Tuple value, + out T1 item1, out T2 item2) + { + item1 = value.Item1; + item2 = value.Item2; + } + + /// + /// Deconstruct a properly nested with 3 elements. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public static void Deconstruct( + this Tuple value, + out T1 item1, out T2 item2, out T3 item3) + { + item1 = value.Item1; + item2 = value.Item2; + item3 = value.Item3; + } + + /// + /// Deconstruct a properly nested with 4 elements. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public static void Deconstruct( + this Tuple value, + out T1 item1, out T2 item2, out T3 item3, out T4 item4) + { + item1 = value.Item1; + item2 = value.Item2; + item3 = value.Item3; + item4 = value.Item4; + } + + /// + /// Deconstruct a properly nested with 5 elements. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public static void Deconstruct( + this Tuple value, + out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5) + { + item1 = value.Item1; + item2 = value.Item2; + item3 = value.Item3; + item4 = value.Item4; + item5 = value.Item5; + } + + /// + /// Deconstruct a properly nested with 6 elements. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public static void Deconstruct( + this Tuple value, + out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6) + { + item1 = value.Item1; + item2 = value.Item2; + item3 = value.Item3; + item4 = value.Item4; + item5 = value.Item5; + item6 = value.Item6; + } + + /// + /// Deconstruct a properly nested with 7 elements. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public static void Deconstruct( + this Tuple value, + out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7) + { + item1 = value.Item1; + item2 = value.Item2; + item3 = value.Item3; + item4 = value.Item4; + item5 = value.Item5; + item6 = value.Item6; + item7 = value.Item7; + } + + /// + /// Deconstruct a properly nested with 8 elements. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public static void Deconstruct( + this Tuple> value, + out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8) + { + item1 = value.Item1; + item2 = value.Item2; + item3 = value.Item3; + item4 = value.Item4; + item5 = value.Item5; + item6 = value.Item6; + item7 = value.Item7; + item8 = value.Rest.Item1; + } + + /// + /// Deconstruct a properly nested with 9 elements. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public static void Deconstruct( + this Tuple> value, + out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9) + { + item1 = value.Item1; + item2 = value.Item2; + item3 = value.Item3; + item4 = value.Item4; + item5 = value.Item5; + item6 = value.Item6; + item7 = value.Item7; + item8 = value.Rest.Item1; + item9 = value.Rest.Item2; + } + + /// + /// Deconstruct a properly nested with 10 elements. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public static void Deconstruct( + this Tuple> value, + out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10) + { + item1 = value.Item1; + item2 = value.Item2; + item3 = value.Item3; + item4 = value.Item4; + item5 = value.Item5; + item6 = value.Item6; + item7 = value.Item7; + item8 = value.Rest.Item1; + item9 = value.Rest.Item2; + item10 = value.Rest.Item3; + } + + /// + /// Deconstruct a properly nested with 11 elements. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public static void Deconstruct( + this Tuple> value, + out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10, out T11 item11) + { + item1 = value.Item1; + item2 = value.Item2; + item3 = value.Item3; + item4 = value.Item4; + item5 = value.Item5; + item6 = value.Item6; + item7 = value.Item7; + item8 = value.Rest.Item1; + item9 = value.Rest.Item2; + item10 = value.Rest.Item3; + item11 = value.Rest.Item4; + } + + /// + /// Deconstruct a properly nested with 12 elements. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public static void Deconstruct( + this Tuple> value, + out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10, out T11 item11, out T12 item12) + { + item1 = value.Item1; + item2 = value.Item2; + item3 = value.Item3; + item4 = value.Item4; + item5 = value.Item5; + item6 = value.Item6; + item7 = value.Item7; + item8 = value.Rest.Item1; + item9 = value.Rest.Item2; + item10 = value.Rest.Item3; + item11 = value.Rest.Item4; + item12 = value.Rest.Item5; + } + + /// + /// Deconstruct a properly nested with 13 elements. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public static void Deconstruct( + this Tuple> value, + out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10, out T11 item11, out T12 item12, out T13 item13) + { + item1 = value.Item1; + item2 = value.Item2; + item3 = value.Item3; + item4 = value.Item4; + item5 = value.Item5; + item6 = value.Item6; + item7 = value.Item7; + item8 = value.Rest.Item1; + item9 = value.Rest.Item2; + item10 = value.Rest.Item3; + item11 = value.Rest.Item4; + item12 = value.Rest.Item5; + item13 = value.Rest.Item6; + } + + /// + /// Deconstruct a properly nested with 14 elements. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public static void Deconstruct( + this Tuple> value, + out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10, out T11 item11, out T12 item12, out T13 item13, out T14 item14) + { + item1 = value.Item1; + item2 = value.Item2; + item3 = value.Item3; + item4 = value.Item4; + item5 = value.Item5; + item6 = value.Item6; + item7 = value.Item7; + item8 = value.Rest.Item1; + item9 = value.Rest.Item2; + item10 = value.Rest.Item3; + item11 = value.Rest.Item4; + item12 = value.Rest.Item5; + item13 = value.Rest.Item6; + item14 = value.Rest.Item7; + } + + /// + /// Deconstruct a properly nested with 15 elements. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public static void Deconstruct( + this Tuple>> value, + out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10, out T11 item11, out T12 item12, out T13 item13, out T14 item14, out T15 item15) + { + item1 = value.Item1; + item2 = value.Item2; + item3 = value.Item3; + item4 = value.Item4; + item5 = value.Item5; + item6 = value.Item6; + item7 = value.Item7; + item8 = value.Rest.Item1; + item9 = value.Rest.Item2; + item10 = value.Rest.Item3; + item11 = value.Rest.Item4; + item12 = value.Rest.Item5; + item13 = value.Rest.Item6; + item14 = value.Rest.Item7; + item15 = value.Rest.Rest.Item1; + } + + /// + /// Deconstruct a properly nested with 16 elements. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public static void Deconstruct( + this Tuple>> value, + out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10, out T11 item11, out T12 item12, out T13 item13, out T14 item14, out T15 item15, out T16 item16) + { + item1 = value.Item1; + item2 = value.Item2; + item3 = value.Item3; + item4 = value.Item4; + item5 = value.Item5; + item6 = value.Item6; + item7 = value.Item7; + item8 = value.Rest.Item1; + item9 = value.Rest.Item2; + item10 = value.Rest.Item3; + item11 = value.Rest.Item4; + item12 = value.Rest.Item5; + item13 = value.Rest.Item6; + item14 = value.Rest.Item7; + item15 = value.Rest.Rest.Item1; + item16 = value.Rest.Rest.Item2; + } + + /// + /// Deconstruct a properly nested with 17 elements. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public static void Deconstruct( + this Tuple>> value, + out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10, out T11 item11, out T12 item12, out T13 item13, out T14 item14, out T15 item15, out T16 item16, out T17 item17) + { + item1 = value.Item1; + item2 = value.Item2; + item3 = value.Item3; + item4 = value.Item4; + item5 = value.Item5; + item6 = value.Item6; + item7 = value.Item7; + item8 = value.Rest.Item1; + item9 = value.Rest.Item2; + item10 = value.Rest.Item3; + item11 = value.Rest.Item4; + item12 = value.Rest.Item5; + item13 = value.Rest.Item6; + item14 = value.Rest.Item7; + item15 = value.Rest.Rest.Item1; + item16 = value.Rest.Rest.Item2; + item17 = value.Rest.Rest.Item3; + } + + /// + /// Deconstruct a properly nested with 18 elements. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public static void Deconstruct( + this Tuple>> value, + out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10, out T11 item11, out T12 item12, out T13 item13, out T14 item14, out T15 item15, out T16 item16, out T17 item17, out T18 item18) + { + item1 = value.Item1; + item2 = value.Item2; + item3 = value.Item3; + item4 = value.Item4; + item5 = value.Item5; + item6 = value.Item6; + item7 = value.Item7; + item8 = value.Rest.Item1; + item9 = value.Rest.Item2; + item10 = value.Rest.Item3; + item11 = value.Rest.Item4; + item12 = value.Rest.Item5; + item13 = value.Rest.Item6; + item14 = value.Rest.Item7; + item15 = value.Rest.Rest.Item1; + item16 = value.Rest.Rest.Item2; + item17 = value.Rest.Rest.Item3; + item18 = value.Rest.Rest.Item4; + } + + /// + /// Deconstruct a properly nested with 19 elements. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public static void Deconstruct( + this Tuple>> value, + out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10, out T11 item11, out T12 item12, out T13 item13, out T14 item14, out T15 item15, out T16 item16, out T17 item17, out T18 item18, out T19 item19) + { + item1 = value.Item1; + item2 = value.Item2; + item3 = value.Item3; + item4 = value.Item4; + item5 = value.Item5; + item6 = value.Item6; + item7 = value.Item7; + item8 = value.Rest.Item1; + item9 = value.Rest.Item2; + item10 = value.Rest.Item3; + item11 = value.Rest.Item4; + item12 = value.Rest.Item5; + item13 = value.Rest.Item6; + item14 = value.Rest.Item7; + item15 = value.Rest.Rest.Item1; + item16 = value.Rest.Rest.Item2; + item17 = value.Rest.Rest.Item3; + item18 = value.Rest.Rest.Item4; + item19 = value.Rest.Rest.Item5; + } + + /// + /// Deconstruct a properly nested with 20 elements. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public static void Deconstruct( + this Tuple>> value, + out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10, out T11 item11, out T12 item12, out T13 item13, out T14 item14, out T15 item15, out T16 item16, out T17 item17, out T18 item18, out T19 item19, out T20 item20) + { + item1 = value.Item1; + item2 = value.Item2; + item3 = value.Item3; + item4 = value.Item4; + item5 = value.Item5; + item6 = value.Item6; + item7 = value.Item7; + item8 = value.Rest.Item1; + item9 = value.Rest.Item2; + item10 = value.Rest.Item3; + item11 = value.Rest.Item4; + item12 = value.Rest.Item5; + item13 = value.Rest.Item6; + item14 = value.Rest.Item7; + item15 = value.Rest.Rest.Item1; + item16 = value.Rest.Rest.Item2; + item17 = value.Rest.Rest.Item3; + item18 = value.Rest.Rest.Item4; + item19 = value.Rest.Rest.Item5; + item20 = value.Rest.Rest.Item6; + } + + /// + /// Deconstruct a properly nested with 21 elements. + /// +#if !MONO + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public static void Deconstruct( + this Tuple>> value, + out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10, out T11 item11, out T12 item12, out T13 item13, out T14 item14, out T15 item15, out T16 item16, out T17 item17, out T18 item18, out T19 item19, out T20 item20, out T21 item21) + { + item1 = value.Item1; + item2 = value.Item2; + item3 = value.Item3; + item4 = value.Item4; + item5 = value.Item5; + item6 = value.Item6; + item7 = value.Item7; + item8 = value.Rest.Item1; + item9 = value.Rest.Item2; + item10 = value.Rest.Item3; + item11 = value.Rest.Item4; + item12 = value.Rest.Item5; + item13 = value.Rest.Item6; + item14 = value.Rest.Item7; + item15 = value.Rest.Rest.Item1; + item16 = value.Rest.Rest.Item2; + item17 = value.Rest.Rest.Item3; + item18 = value.Rest.Rest.Item4; + item19 = value.Rest.Rest.Item5; + item20 = value.Rest.Rest.Item6; + item21 = value.Rest.Rest.Item7; + } + #endregion + + #region ToValueTuple + /// + /// Make a properly nested from a properly nested with 1 element. + /// + public static ValueTuple + ToValueTuple( + this Tuple value) + { + return ValueTuple.Create(value.Item1); + } + + /// + /// Make a properly nested from a properly nested with 2 elements. + /// + public static ValueTuple + ToValueTuple( + this Tuple value) + { + return ValueTuple.Create(value.Item1, value.Item2); + } + + /// + /// Make a properly nested from a properly nested with 3 elements. + /// + public static ValueTuple + ToValueTuple( + this Tuple value) + { + return ValueTuple.Create(value.Item1, value.Item2, value.Item3); + } + + /// + /// Make a properly nested from a properly nested with 4 elements. + /// + public static ValueTuple + ToValueTuple( + this Tuple value) + { + return ValueTuple.Create(value.Item1, value.Item2, value.Item3, value.Item4); + } + + /// + /// Make a properly nested from a properly nested with 5 elements. + /// + public static ValueTuple + ToValueTuple( + this Tuple value) + { + return ValueTuple.Create(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5); + } + + /// + /// Make a properly nested from a properly nested with 6 elements. + /// + public static ValueTuple + ToValueTuple( + this Tuple value) + { + return ValueTuple.Create(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6); + } + + /// + /// Make a properly nested from a properly nested with 7 elements. + /// + public static ValueTuple + ToValueTuple( + this Tuple value) + { + return ValueTuple.Create(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7); + } + + /// + /// Make a properly nested from a properly nested with 8 elements. + /// + public static ValueTuple> + ToValueTuple( + this Tuple> value) + { + return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + ValueTuple.Create(value.Rest.Item1)); + } + + /// + /// Make a properly nested from a properly nested with 9 elements. + /// + public static ValueTuple> + ToValueTuple( + this Tuple> value) + { + return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + ValueTuple.Create(value.Rest.Item1, value.Rest.Item2)); + } + + /// + /// Make a properly nested from a properly nested with 10 elements. + /// + public static ValueTuple> + ToValueTuple( + this Tuple> value) + { + return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + ValueTuple.Create(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3)); + } + + /// + /// Make a properly nested from a properly nested with 11 elements. + /// + public static ValueTuple> + ToValueTuple( + this Tuple> value) + { + return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + ValueTuple.Create(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4)); + } + + /// + /// Make a properly nested from a properly nested with 12 elements. + /// + public static ValueTuple> + ToValueTuple( + this Tuple> value) + { + return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + ValueTuple.Create(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5)); + } + + /// + /// Make a properly nested from a properly nested with 13 elements. + /// + public static ValueTuple> + ToValueTuple( + this Tuple> value) + { + return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + ValueTuple.Create(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6)); + } + + /// + /// Make a properly nested from a properly nested with 14 elements. + /// + public static ValueTuple> + ToValueTuple( + this Tuple> value) + { + return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + ValueTuple.Create(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7)); + } + + /// + /// Make a properly nested from a properly nested with 15 elements. + /// + public static ValueTuple>> + ToValueTuple( + this Tuple>> value) + { + return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + CreateLong(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7, + ValueTuple.Create(value.Rest.Rest.Item1))); + } + + /// + /// Make a properly nested from a properly nested with 16 elements. + /// + public static ValueTuple>> + ToValueTuple( + this Tuple>> value) + { + return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + CreateLong(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7, + ValueTuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2))); + } + + /// + /// Make a properly nested from a properly nested with 17 elements. + /// + public static ValueTuple>> + ToValueTuple( + this Tuple>> value) + { + return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + CreateLong(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7, + ValueTuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2, value.Rest.Rest.Item3))); + } + + /// + /// Make a properly nested from a properly nested with 18 elements. + /// + public static ValueTuple>> + ToValueTuple( + this Tuple>> value) + { + return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + CreateLong(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7, + ValueTuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2, value.Rest.Rest.Item3, value.Rest.Rest.Item4))); + } + + /// + /// Make a properly nested from a properly nested with 19 elements. + /// + public static ValueTuple>> + ToValueTuple( + this Tuple>> value) + { + return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + CreateLong(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7, + ValueTuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2, value.Rest.Rest.Item3, value.Rest.Rest.Item4, value.Rest.Rest.Item5))); + } + + /// + /// Make a properly nested from a properly nested with 20 elements. + /// + public static ValueTuple>> + ToValueTuple( + this Tuple>> value) + { + return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + CreateLong(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7, + ValueTuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2, value.Rest.Rest.Item3, value.Rest.Rest.Item4, value.Rest.Rest.Item5, value.Rest.Rest.Item6))); + } + + /// + /// Make a properly nested from a properly nested with 21 elements. + /// + public static ValueTuple>> + ToValueTuple( + this Tuple>> value) + { + return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + CreateLong(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7, + ValueTuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2, value.Rest.Rest.Item3, value.Rest.Rest.Item4, value.Rest.Rest.Item5, value.Rest.Rest.Item6, value.Rest.Rest.Item7))); + } + #endregion + + #region ToTuple + /// + /// Make a properly nested from a properly nested with 1 element. + /// + public static Tuple + ToTuple( + this ValueTuple value) + { + return Tuple.Create(value.Item1); + } + + /// + /// Make a properly nested from a properly nested with 2 elements. + /// + public static Tuple + ToTuple( + this ValueTuple value) + { + return Tuple.Create(value.Item1, value.Item2); + } + + /// + /// Make a properly nested from a properly nested with 3 elements. + /// + public static Tuple + ToTuple( + this ValueTuple value) + { + return Tuple.Create(value.Item1, value.Item2, value.Item3); + } + + /// + /// Make a properly nested from a properly nested with 4 elements. + /// + public static Tuple + ToTuple( + this ValueTuple value) + { + return Tuple.Create(value.Item1, value.Item2, value.Item3, value.Item4); + } + + /// + /// Make a properly nested from a properly nested with 5 elements. + /// + public static Tuple + ToTuple( + this ValueTuple value) + { + return Tuple.Create(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5); + } + + /// + /// Make a properly nested from a properly nested with 6 elements. + /// + public static Tuple + ToTuple( + this ValueTuple value) + { + return Tuple.Create(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6); + } + + /// + /// Make a properly nested from a properly nested with 7 elements. + /// + public static Tuple + ToTuple( + this ValueTuple value) + { + return Tuple.Create(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7); + } + + /// + /// Make a properly nested from a properly nested with 8 elements. + /// + public static Tuple> + ToTuple( + this ValueTuple> value) + { + return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + Tuple.Create(value.Rest.Item1)); + } + + /// + /// Make a properly nested from a properly nested with 9 elements. + /// + public static Tuple> + ToTuple( + this ValueTuple> value) + { + return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + Tuple.Create(value.Rest.Item1, value.Rest.Item2)); + } + + /// + /// Make a properly nested from a properly nested with 10 elements. + /// + public static Tuple> + ToTuple( + this ValueTuple> value) + { + return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + Tuple.Create(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3)); + } + + /// + /// Make a properly nested from a properly nested with 11 elements. + /// + public static Tuple> + ToTuple( + this ValueTuple> value) + { + return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + Tuple.Create(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4)); + } + + /// + /// Make a properly nested from a properly nested with 12 elements. + /// + public static Tuple> + ToTuple( + this ValueTuple> value) + { + return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + Tuple.Create(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5)); + } + + /// + /// Make a properly nested from a properly nested with 13 elements. + /// + public static Tuple> + ToTuple( + this ValueTuple> value) + { + return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + Tuple.Create(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6)); + } + + /// + /// Make a properly nested from a properly nested with 14 elements. + /// + public static Tuple> + ToTuple( + this ValueTuple> value) + { + return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + Tuple.Create(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7)); + } + + /// + /// Make a properly nested from a properly nested with 15 elements. + /// + public static Tuple>> + ToTuple( + this ValueTuple>> value) + { + return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + CreateLongRef(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7, + Tuple.Create(value.Rest.Rest.Item1))); + } + + /// + /// Make a properly nested from a properly nested with 16 elements. + /// + public static Tuple>> + ToTuple( + this ValueTuple>> value) + { + return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + CreateLongRef(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7, + Tuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2))); + } + + /// + /// Make a properly nested from a properly nested with 17 elements. + /// + public static Tuple>> + ToTuple( + this ValueTuple>> value) + { + return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + CreateLongRef(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7, + Tuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2, value.Rest.Rest.Item3))); + } + + /// + /// Make a properly nested from a properly nested with 18 elements. + /// + public static Tuple>> + ToTuple( + this ValueTuple>> value) + { + return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + CreateLongRef(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7, + Tuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2, value.Rest.Rest.Item3, value.Rest.Rest.Item4))); + } + + /// + /// Make a properly nested from a properly nested with 19 elements. + /// + public static Tuple>> + ToTuple( + this ValueTuple>> value) + { + return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + CreateLongRef(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7, + Tuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2, value.Rest.Rest.Item3, value.Rest.Rest.Item4, value.Rest.Rest.Item5))); + } + + /// + /// Make a properly nested from a properly nested with 20 elements. + /// + public static Tuple>> + ToTuple( + this ValueTuple>> value) + { + return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + CreateLongRef(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7, + Tuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2, value.Rest.Rest.Item3, value.Rest.Rest.Item4, value.Rest.Rest.Item5, value.Rest.Rest.Item6))); + } + + /// + /// Make a properly nested from a properly nested with 21 elements. + /// + public static Tuple>> + ToTuple( + this ValueTuple>> value) + { + return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7, + CreateLongRef(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7, + Tuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2, value.Rest.Rest.Item3, value.Rest.Rest.Item4, value.Rest.Rest.Item5, value.Rest.Rest.Item6, value.Rest.Rest.Item7))); + } + #endregion + + private static ValueTuple CreateLong(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, TRest rest) where TRest : struct, ITuple => + new ValueTuple(item1, item2, item3, item4, item5, item6, item7, rest); + + private static Tuple CreateLongRef(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, TRest rest) where TRest : ITuple => + new Tuple(item1, item2, item3, item4, item5, item6, item7, rest); + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Type.Enum.cs b/external/corefx/src/Common/src/CoreLib/System/Type.Enum.cs new file mode 100644 index 0000000000..4d82410383 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Type.Enum.cs @@ -0,0 +1,186 @@ +// 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.Reflection; +using System.Collections; +using System.Collections.Generic; + +namespace System +{ + // + // This file collects a set of Enum-related apis that run when the Type is subclassed by an application. + // None of it runs on normal Type objects supplied by the runtime (as those types override these methods.) + // + // Since app-subclassed Types are "untrusted classes" that may or may not implement the complete surface area correctly, + // this code should be considered brittle and not changed lightly. + // + public abstract partial class Type + { + public virtual bool IsEnumDefined(object value) + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + if (!IsEnum) + throw new ArgumentException(SR.Arg_MustBeEnum, "enumType"); + + // Check if both of them are of the same type + Type valueType = value.GetType(); + + // If the value is an Enum then we need to extract the underlying value from it + if (valueType.IsEnum) + { + if (!valueType.IsEquivalentTo(this)) + throw new ArgumentException(SR.Format(SR.Arg_EnumAndObjectMustBeSameType, valueType.ToString(), this.ToString())); + + valueType = valueType.GetEnumUnderlyingType(); + } + + // If a string is passed in + if (valueType == typeof(string)) + { + string[] names = GetEnumNames(); + if (Array.IndexOf(names, value) >= 0) + return true; + else + return false; + } + + // If an enum or integer value is passed in + if (Type.IsIntegerType(valueType)) + { + Type underlyingType = GetEnumUnderlyingType(); + // We cannot compare the types directly because valueType is always a runtime type but underlyingType might not be. + if (underlyingType.GetTypeCodeImpl() != valueType.GetTypeCodeImpl()) + throw new ArgumentException(SR.Format(SR.Arg_EnumUnderlyingTypeAndObjectMustBeSameType, valueType.ToString(), underlyingType.ToString())); + + Array values = GetEnumRawConstantValues(); + return (BinarySearch(values, value) >= 0); + } + else + { + throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType); + } + } + + public virtual string GetEnumName(object value) + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + if (!IsEnum) + throw new ArgumentException(SR.Arg_MustBeEnum, "enumType"); + + Type valueType = value.GetType(); + + if (!(valueType.IsEnum || Type.IsIntegerType(valueType))) + throw new ArgumentException(SR.Arg_MustBeEnumBaseTypeOrEnum, nameof(value)); + + Array values = GetEnumRawConstantValues(); + int index = BinarySearch(values, value); + + if (index >= 0) + { + string[] names = GetEnumNames(); + return names[index]; + } + + return null; + } + + public virtual string[] GetEnumNames() + { + if (!IsEnum) + throw new ArgumentException(SR.Arg_MustBeEnum, "enumType"); + + string[] names; + Array values; + GetEnumData(out names, out values); + return names; + } + + + // Returns the enum values as an object array. + private Array GetEnumRawConstantValues() + { + string[] names; + Array values; + GetEnumData(out names, out values); + return values; + } + + // This will return enumValues and enumNames sorted by the values. + private void GetEnumData(out string[] enumNames, out Array enumValues) + { + FieldInfo[] flds = GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + + object[] values = new object[flds.Length]; + string[] names = new string[flds.Length]; + + for (int i = 0; i < flds.Length; i++) + { + names[i] = flds[i].Name; + values[i] = flds[i].GetRawConstantValue(); + } + + // Insertion Sort these values in ascending order. + // We use this O(n^2) algorithm, but it turns out that most of the time the elements are already in sorted order and + // the common case performance will be faster than quick sorting this. + IComparer comparer = Comparer.Default; + for (int i = 1; i < values.Length; i++) + { + int j = i; + string tempStr = names[i]; + object val = values[i]; + bool exchanged = false; + + // Since the elements are sorted we only need to do one comparision, we keep the check for j inside the loop. + while (comparer.Compare(values[j - 1], val) > 0) + { + names[j] = names[j - 1]; + values[j] = values[j - 1]; + j--; + exchanged = true; + if (j == 0) + break; + } + + if (exchanged) + { + names[j] = tempStr; + values[j] = val; + } + } + + enumNames = names; + enumValues = values; + } + + // Convert everything to ulong then perform a binary search. + private static int BinarySearch(Array array, object value) + { + ulong[] ulArray = new ulong[array.Length]; + for (int i = 0; i < array.Length; ++i) + ulArray[i] = Enum.ToUInt64(array.GetValue(i)); + + ulong ulValue = Enum.ToUInt64(value); + + return Array.BinarySearch(ulArray, ulValue); + } + + internal static bool IsIntegerType(Type t) + { + return (t == typeof(int) || + t == typeof(short) || + t == typeof(ushort) || + t == typeof(byte) || + t == typeof(sbyte) || + t == typeof(uint) || + t == typeof(long) || + t == typeof(ulong) || + t == typeof(char) || + t == typeof(bool)); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Type.Helpers.cs b/external/corefx/src/Common/src/CoreLib/System/Type.Helpers.cs new file mode 100644 index 0000000000..db8df231e4 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Type.Helpers.cs @@ -0,0 +1,527 @@ +// 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.Reflection; + +namespace System +{ + // This file collects the longer methods of Type to make the main Type class more readable. + public abstract partial class Type : MemberInfo, IReflect + { + public virtual bool IsSerializable + { + get + { + if ((GetAttributeFlagsImpl() & TypeAttributes.Serializable) != 0) + return true; + + Type underlyingType = UnderlyingSystemType; + if (underlyingType.IsRuntimeImplemented()) + { + do + { + // In all sane cases we only need to compare the direct level base type with + // System.Enum and System.MulticastDelegate. However, a generic parameter can + // have a base type constraint that is Delegate or even a real delegate type. + // Let's maintain compatibility and return true for them. + if (underlyingType == typeof(Delegate) || underlyingType == typeof(Enum)) + return true; + + underlyingType = underlyingType.BaseType; + } + while (underlyingType != null); + } + + return false; + } + } + + public virtual bool ContainsGenericParameters + { + get + { + if (HasElementType) + return GetRootElementType().ContainsGenericParameters; + + if (IsGenericParameter) + return true; + + if (!IsGenericType) + return false; + + Type[] genericArguments = GetGenericArguments(); + for (int i = 0; i < genericArguments.Length; i++) + { + if (genericArguments[i].ContainsGenericParameters) + return true; + } + + return false; + } + } + + internal Type GetRootElementType() + { + Type rootElementType = this; + + while (rootElementType.HasElementType) + rootElementType = rootElementType.GetElementType(); + + return rootElementType; + } + + public bool IsVisible + { + get + { +#if CORECLR + RuntimeType rt = this as RuntimeType; + if (rt != null) + return RuntimeTypeHandle.IsVisible(rt); +#endif //CORECLR + + if (IsGenericParameter) + return true; + + if (HasElementType) + return GetElementType().IsVisible; + + Type type = this; + while (type.IsNested) + { + if (!type.IsNestedPublic) + return false; + + // this should be null for non-nested types. + type = type.DeclaringType; + } + + // Now "type" should be a top level type + if (!type.IsPublic) + return false; + + if (IsGenericType && !IsGenericTypeDefinition) + { + foreach (Type t in GetGenericArguments()) + { + if (!t.IsVisible) + return false; + } + } + + return true; + } + } + + public virtual Type[] FindInterfaces(TypeFilter filter, object filterCriteria) + { + if (filter == null) + throw new ArgumentNullException(nameof(filter)); + + Type[] c = GetInterfaces(); + int cnt = 0; + for (int i = 0; i < c.Length; i++) + { + if (!filter(c[i], filterCriteria)) + c[i] = null; + else + cnt++; + } + if (cnt == c.Length) + return c; + + Type[] ret = new Type[cnt]; + cnt = 0; + for (int i = 0; i < c.Length; i++) + { + if (c[i] != null) + ret[cnt++] = c[i]; + } + return ret; + } + + public virtual MemberInfo[] FindMembers(MemberTypes memberType, BindingFlags bindingAttr, MemberFilter filter, object filterCriteria) + { + // Define the work arrays + MethodInfo[] m = null; + ConstructorInfo[] c = null; + FieldInfo[] f = null; + PropertyInfo[] p = null; + EventInfo[] e = null; + Type[] t = null; + + int i = 0; + int cnt = 0; // Total Matchs + + // Check the methods + if ((memberType & MemberTypes.Method) != 0) + { + m = GetMethods(bindingAttr); + if (filter != null) + { + for (i = 0; i < m.Length; i++) + if (!filter(m[i], filterCriteria)) + m[i] = null; + else + cnt++; + } + else + { + cnt += m.Length; + } + } + + // Check the constructors + if ((memberType & MemberTypes.Constructor) != 0) + { + c = GetConstructors(bindingAttr); + if (filter != null) + { + for (i = 0; i < c.Length; i++) + if (!filter(c[i], filterCriteria)) + c[i] = null; + else + cnt++; + } + else + { + cnt += c.Length; + } + } + + // Check the fields + if ((memberType & MemberTypes.Field) != 0) + { + f = GetFields(bindingAttr); + if (filter != null) + { + for (i = 0; i < f.Length; i++) + if (!filter(f[i], filterCriteria)) + f[i] = null; + else + cnt++; + } + else + { + cnt += f.Length; + } + } + + // Check the Properties + if ((memberType & MemberTypes.Property) != 0) + { + p = GetProperties(bindingAttr); + if (filter != null) + { + for (i = 0; i < p.Length; i++) + if (!filter(p[i], filterCriteria)) + p[i] = null; + else + cnt++; + } + else + { + cnt += p.Length; + } + } + + // Check the Events + if ((memberType & MemberTypes.Event) != 0) + { + e = GetEvents(bindingAttr); + if (filter != null) + { + for (i = 0; i < e.Length; i++) + if (!filter(e[i], filterCriteria)) + e[i] = null; + else + cnt++; + } + else + { + cnt += e.Length; + } + } + + // Check the Types + if ((memberType & MemberTypes.NestedType) != 0) + { + t = GetNestedTypes(bindingAttr); + if (filter != null) + { + for (i = 0; i < t.Length; i++) + if (!filter(t[i], filterCriteria)) + t[i] = null; + else + cnt++; + } + else + { + cnt += t.Length; + } + } + + // Allocate the Member Info + MemberInfo[] ret = new MemberInfo[cnt]; + + // Copy the Methods + cnt = 0; + if (m != null) + { + for (i = 0; i < m.Length; i++) + if (m[i] != null) + ret[cnt++] = m[i]; + } + + // Copy the Constructors + if (c != null) + { + for (i = 0; i < c.Length; i++) + if (c[i] != null) + ret[cnt++] = c[i]; + } + + // Copy the Fields + if (f != null) + { + for (i = 0; i < f.Length; i++) + if (f[i] != null) + ret[cnt++] = f[i]; + } + + // Copy the Properties + if (p != null) + { + for (i = 0; i < p.Length; i++) + if (p[i] != null) + ret[cnt++] = p[i]; + } + + // Copy the Events + if (e != null) + { + for (i = 0; i < e.Length; i++) + if (e[i] != null) + ret[cnt++] = e[i]; + } + + // Copy the Types + if (t != null) + { + for (i = 0; i < t.Length; i++) + if (t[i] != null) + ret[cnt++] = t[i]; + } + + return ret; + } + + public virtual bool IsSubclassOf(Type c) + { + Type p = this; + if (p == c) + return false; + while (p != null) + { + if (p == c) + return true; + p = p.BaseType; + } + return false; + } + + public virtual bool IsAssignableFrom(Type c) + { + if (c == null) + return false; + + if (this == c) + return true; + + // For backward-compatibility, we need to special case for the types + // whose UnderlyingSystemType are runtime implemented. + Type toType = this.UnderlyingSystemType; + if (toType.IsRuntimeImplemented()) + return toType.IsAssignableFrom(c); + + // If c is a subclass of this class, then c can be cast to this type. + if (c.IsSubclassOf(this)) + return true; + + if (this.IsInterface) + { + return c.ImplementInterface(this); + } + else if (IsGenericParameter) + { + Type[] constraints = GetGenericParameterConstraints(); + for (int i = 0; i < constraints.Length; i++) + if (!constraints[i].IsAssignableFrom(c)) + return false; + + return true; + } + + return false; + } + + internal bool ImplementInterface(Type ifaceType) + { + Type t = this; + while (t != null) + { + Type[] interfaces = t.GetInterfaces(); + if (interfaces != null) + { + for (int i = 0; i < interfaces.Length; i++) + { + // Interfaces don't derive from other interfaces, they implement them. + // So instead of IsSubclassOf, we should use ImplementInterface instead. + if (interfaces[i] == ifaceType || + (interfaces[i] != null && interfaces[i].ImplementInterface(ifaceType))) + return true; + } + } + + t = t.BaseType; + } + + return false; + } + + // FilterAttribute + // This method will search for a member based upon the attribute passed in. + // filterCriteria -- an Int32 representing the attribute + private static bool FilterAttributeImpl(MemberInfo m, object filterCriteria) + { + // Check that the criteria object is an Integer object + if (filterCriteria == null) + throw new InvalidFilterCriteriaException(SR.InvalidFilterCriteriaException_CritInt); + + switch (m.MemberType) + { + case MemberTypes.Constructor: + case MemberTypes.Method: + { + MethodAttributes criteria = 0; + try + { + int i = (int)filterCriteria; + criteria = (MethodAttributes)i; + } + catch + { + throw new InvalidFilterCriteriaException(SR.InvalidFilterCriteriaException_CritInt); + } + + + MethodAttributes attr; + if (m.MemberType == MemberTypes.Method) + attr = ((MethodInfo)m).Attributes; + else + attr = ((ConstructorInfo)m).Attributes; + + if (((criteria & MethodAttributes.MemberAccessMask) != 0) && (attr & MethodAttributes.MemberAccessMask) != (criteria & MethodAttributes.MemberAccessMask)) + return false; + if (((criteria & MethodAttributes.Static) != 0) && (attr & MethodAttributes.Static) == 0) + return false; + if (((criteria & MethodAttributes.Final) != 0) && (attr & MethodAttributes.Final) == 0) + return false; + if (((criteria & MethodAttributes.Virtual) != 0) && (attr & MethodAttributes.Virtual) == 0) + return false; + if (((criteria & MethodAttributes.Abstract) != 0) && (attr & MethodAttributes.Abstract) == 0) + return false; + if (((criteria & MethodAttributes.SpecialName) != 0) && (attr & MethodAttributes.SpecialName) == 0) + return false; + return true; + } + case MemberTypes.Field: + { + FieldAttributes criteria = 0; + try + { + int i = (int)filterCriteria; + criteria = (FieldAttributes)i; + } + catch + { + throw new InvalidFilterCriteriaException(SR.InvalidFilterCriteriaException_CritInt); + } + + FieldAttributes attr = ((FieldInfo)m).Attributes; + if (((criteria & FieldAttributes.FieldAccessMask) != 0) && (attr & FieldAttributes.FieldAccessMask) != (criteria & FieldAttributes.FieldAccessMask)) + return false; + if (((criteria & FieldAttributes.Static) != 0) && (attr & FieldAttributes.Static) == 0) + return false; + if (((criteria & FieldAttributes.InitOnly) != 0) && (attr & FieldAttributes.InitOnly) == 0) + return false; + if (((criteria & FieldAttributes.Literal) != 0) && (attr & FieldAttributes.Literal) == 0) + return false; + if (((criteria & FieldAttributes.NotSerialized) != 0) && (attr & FieldAttributes.NotSerialized) == 0) + return false; + if (((criteria & FieldAttributes.PinvokeImpl) != 0) && (attr & FieldAttributes.PinvokeImpl) == 0) + return false; + return true; + } + } + + return false; + } + + // FilterName + // This method will filter based upon the name. A partial wildcard + // at the end of the string is supported. + // filterCriteria -- This is the string name + private static bool FilterNameImpl(MemberInfo m, object filterCriteria) + { + // Check that the criteria object is a String object + if (filterCriteria == null || !(filterCriteria is string)) + throw new InvalidFilterCriteriaException(SR.InvalidFilterCriteriaException_CritString); + + // At the moment this fails if its done on a single line.... + string str = ((string)filterCriteria); + str = str.Trim(); + + string name = m.Name; + // Get the nested class name only, as opposed to the mangled one + if (m.MemberType == MemberTypes.NestedType) + name = name.Substring(name.LastIndexOf('+') + 1); + // Check to see if this is a prefix or exact match requirement + if (str.Length > 0 && str[str.Length - 1] == '*') + { + str = str.Substring(0, str.Length - 1); + return (name.StartsWith(str, StringComparison.Ordinal)); + } + + return (name.Equals(str)); + } + + // FilterIgnoreCase + // This delegate will do a name search but does it with the + // ignore case specified. + private static bool FilterNameIgnoreCaseImpl(MemberInfo m, object filterCriteria) + { + // Check that the criteria object is a String object + if (filterCriteria == null || !(filterCriteria is string)) + throw new InvalidFilterCriteriaException(SR.InvalidFilterCriteriaException_CritString); + + string str = (string)filterCriteria; + str = str.Trim(); + + string name = m.Name; + // Get the nested class name only, as opposed to the mangled one + if (m.MemberType == MemberTypes.NestedType) + name = name.Substring(name.LastIndexOf('+') + 1); + // Check to see if this is a prefix or exact match requirement + if (str.Length > 0 && str[str.Length - 1] == '*') + { + str = str.Substring(0, str.Length - 1); + return (string.Compare(name, 0, str, 0, str.Length, StringComparison.OrdinalIgnoreCase) == 0); + } + + return (string.Compare(str, name, StringComparison.OrdinalIgnoreCase) == 0); + } + } +} + diff --git a/external/corefx/src/Common/src/CoreLib/System/Type.cs b/external/corefx/src/Common/src/CoreLib/System/Type.cs new file mode 100644 index 0000000000..a0d219ddd4 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Type.cs @@ -0,0 +1,395 @@ +// 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.Threading; +using System.Reflection; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; + +namespace System +{ + public abstract partial class Type : MemberInfo, IReflect + { + protected Type() { } + + public override MemberTypes MemberType => MemberTypes.TypeInfo; + + public new Type GetType() => base.GetType(); + + public abstract string Namespace { get; } + public abstract string AssemblyQualifiedName { get; } + public abstract string FullName { get; } + + public abstract Assembly Assembly { get; } + public abstract new Module Module { get; } + + public bool IsNested => DeclaringType != null; + public override Type DeclaringType => null; + public virtual MethodBase DeclaringMethod => null; + + public override Type ReflectedType => null; + public abstract Type UnderlyingSystemType { get; } + + public virtual bool IsTypeDefinition { get { throw NotImplemented.ByDesign; } } + public bool IsArray => IsArrayImpl(); + protected abstract bool IsArrayImpl(); + public bool IsByRef => IsByRefImpl(); + protected abstract bool IsByRefImpl(); + public bool IsPointer => IsPointerImpl(); + protected abstract bool IsPointerImpl(); + public virtual bool IsConstructedGenericType { get { throw NotImplemented.ByDesign; } } + public virtual bool IsGenericParameter => false; + public virtual bool IsGenericTypeParameter => IsGenericParameter && DeclaringMethod == null; + public virtual bool IsGenericMethodParameter => IsGenericParameter && DeclaringMethod != null; + public virtual bool IsGenericType => false; + public virtual bool IsGenericTypeDefinition => false; + + public virtual bool IsSZArray { get { throw NotImplemented.ByDesign; } } + public virtual bool IsVariableBoundArray => IsArray && !IsSZArray; + + public virtual bool IsByRefLike => throw new NotSupportedException(SR.NotSupported_SubclassOverride); + + public bool HasElementType => HasElementTypeImpl(); + protected abstract bool HasElementTypeImpl(); + public abstract Type GetElementType(); + + public virtual int GetArrayRank() { throw new NotSupportedException(SR.NotSupported_SubclassOverride); } + + public virtual Type GetGenericTypeDefinition() { throw new NotSupportedException(SR.NotSupported_SubclassOverride); } + public virtual Type[] GenericTypeArguments => (IsGenericType && !IsGenericTypeDefinition) ? GetGenericArguments() : Array.Empty(); + public virtual Type[] GetGenericArguments() { throw new NotSupportedException(SR.NotSupported_SubclassOverride); } + + public virtual int GenericParameterPosition { get { throw new InvalidOperationException(SR.Arg_NotGenericParameter); } } + public virtual GenericParameterAttributes GenericParameterAttributes { get { throw new NotSupportedException(); } } + public virtual Type[] GetGenericParameterConstraints() + { + if (!IsGenericParameter) + throw new InvalidOperationException(SR.Arg_NotGenericParameter); + throw new InvalidOperationException(); + } + + public TypeAttributes Attributes => GetAttributeFlagsImpl(); + protected abstract TypeAttributes GetAttributeFlagsImpl(); + + public bool IsAbstract => (GetAttributeFlagsImpl() & TypeAttributes.Abstract) != 0; + public bool IsImport => (GetAttributeFlagsImpl() & TypeAttributes.Import) != 0; + public bool IsSealed => (GetAttributeFlagsImpl() & TypeAttributes.Sealed) != 0; + public bool IsSpecialName => (GetAttributeFlagsImpl() & TypeAttributes.SpecialName) != 0; + + public bool IsClass => (GetAttributeFlagsImpl() & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Class && !IsValueType; + + public bool IsNestedAssembly => (GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NestedAssembly; + public bool IsNestedFamANDAssem => (GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamANDAssem; + public bool IsNestedFamily => (GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamily; + public bool IsNestedFamORAssem => (GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamORAssem; + public bool IsNestedPrivate => (GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPrivate; + public bool IsNestedPublic => (GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic; + public bool IsNotPublic => (GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NotPublic; + public bool IsPublic => (GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.Public; + + public bool IsAutoLayout => (GetAttributeFlagsImpl() & TypeAttributes.LayoutMask) == TypeAttributes.AutoLayout; + public bool IsExplicitLayout => (GetAttributeFlagsImpl() & TypeAttributes.LayoutMask) == TypeAttributes.ExplicitLayout; + public bool IsLayoutSequential => (GetAttributeFlagsImpl() & TypeAttributes.LayoutMask) == TypeAttributes.SequentialLayout; + + public bool IsAnsiClass => (GetAttributeFlagsImpl() & TypeAttributes.StringFormatMask) == TypeAttributes.AnsiClass; + public bool IsAutoClass => (GetAttributeFlagsImpl() & TypeAttributes.StringFormatMask) == TypeAttributes.AutoClass; + public bool IsUnicodeClass => (GetAttributeFlagsImpl() & TypeAttributes.StringFormatMask) == TypeAttributes.UnicodeClass; + + public bool IsCOMObject => IsCOMObjectImpl(); + protected abstract bool IsCOMObjectImpl(); + public bool IsContextful => IsContextfulImpl(); + protected virtual bool IsContextfulImpl() => false; + + public virtual bool IsCollectible => true; + + public virtual bool IsEnum => IsSubclassOf(typeof(Enum)); + public bool IsMarshalByRef => IsMarshalByRefImpl(); + protected virtual bool IsMarshalByRefImpl() => false; + public bool IsPrimitive => IsPrimitiveImpl(); + protected abstract bool IsPrimitiveImpl(); + public bool IsValueType => IsValueTypeImpl(); + protected virtual bool IsValueTypeImpl() => IsSubclassOf(typeof(ValueType)); + + public virtual bool IsSignatureType => false; + + public virtual bool IsSecurityCritical { get { throw NotImplemented.ByDesign; } } + public virtual bool IsSecuritySafeCritical { get { throw NotImplemented.ByDesign; } } + public virtual bool IsSecurityTransparent { get { throw NotImplemented.ByDesign; } } + + public virtual StructLayoutAttribute StructLayoutAttribute { get { throw new NotSupportedException(); } } + public ConstructorInfo TypeInitializer => GetConstructorImpl(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, CallingConventions.Any, Type.EmptyTypes, null); + + public ConstructorInfo GetConstructor(Type[] types) => GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, types, null); + public ConstructorInfo GetConstructor(BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers) => GetConstructor(bindingAttr, binder, CallingConventions.Any, types, modifiers); + public ConstructorInfo GetConstructor(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + if (types == null) + throw new ArgumentNullException(nameof(types)); + for (int i = 0; i < types.Length; i++) + { + if (types[i] == null) + throw new ArgumentNullException(nameof(types)); + } + return GetConstructorImpl(bindingAttr, binder, callConvention, types, modifiers); + } + protected abstract ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers); + + public ConstructorInfo[] GetConstructors() => GetConstructors(BindingFlags.Public | BindingFlags.Instance); + public abstract ConstructorInfo[] GetConstructors(BindingFlags bindingAttr); + + public EventInfo GetEvent(string name) => GetEvent(name, Type.DefaultLookup); + public abstract EventInfo GetEvent(string name, BindingFlags bindingAttr); + + public virtual EventInfo[] GetEvents() => GetEvents(Type.DefaultLookup); + public abstract EventInfo[] GetEvents(BindingFlags bindingAttr); + + public FieldInfo GetField(string name) => GetField(name, Type.DefaultLookup); + public abstract FieldInfo GetField(string name, BindingFlags bindingAttr); + + public FieldInfo[] GetFields() => GetFields(Type.DefaultLookup); + public abstract FieldInfo[] GetFields(BindingFlags bindingAttr); + + public MemberInfo[] GetMember(string name) => GetMember(name, Type.DefaultLookup); + public virtual MemberInfo[] GetMember(string name, BindingFlags bindingAttr) => GetMember(name, MemberTypes.All, bindingAttr); + public virtual MemberInfo[] GetMember(string name, MemberTypes type, BindingFlags bindingAttr) { throw new NotSupportedException(SR.NotSupported_SubclassOverride); } + + public MemberInfo[] GetMembers() => GetMembers(Type.DefaultLookup); + public abstract MemberInfo[] GetMembers(BindingFlags bindingAttr); + + public MethodInfo GetMethod(string name) => GetMethod(name, Type.DefaultLookup); + public MethodInfo GetMethod(string name, BindingFlags bindingAttr) + { + if (name == null) + throw new ArgumentNullException(nameof(name)); + return GetMethodImpl(name, bindingAttr, null, CallingConventions.Any, null, null); + } + + public MethodInfo GetMethod(string name, Type[] types) => GetMethod(name, types, null); + public MethodInfo GetMethod(string name, Type[] types, ParameterModifier[] modifiers) => GetMethod(name, Type.DefaultLookup, null, types, modifiers); + public MethodInfo GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers) => GetMethod(name, bindingAttr, binder, CallingConventions.Any, types, modifiers); + public MethodInfo GetMethod(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + if (name == null) + throw new ArgumentNullException(nameof(name)); + if (types == null) + throw new ArgumentNullException(nameof(types)); + for (int i = 0; i < types.Length; i++) + { + if (types[i] == null) + throw new ArgumentNullException(nameof(types)); + } + return GetMethodImpl(name, bindingAttr, binder, callConvention, types, modifiers); + } + + protected abstract MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers); + + public MethodInfo GetMethod(string name, int genericParameterCount, Type[] types) => GetMethod(name, genericParameterCount, types, null); + public MethodInfo GetMethod(string name, int genericParameterCount, Type[] types, ParameterModifier[] modifiers) => GetMethod(name, genericParameterCount, Type.DefaultLookup, null, types, modifiers); + public MethodInfo GetMethod(string name, int genericParameterCount, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers) => GetMethod(name, genericParameterCount, bindingAttr, binder, CallingConventions.Any, types, modifiers); + public MethodInfo GetMethod(string name, int genericParameterCount, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + if (name == null) + throw new ArgumentNullException(nameof(name)); + if (genericParameterCount < 0) + throw new ArgumentException(SR.ArgumentOutOfRange_NeedNonNegNum, nameof(genericParameterCount)); + if (types == null) + throw new ArgumentNullException(nameof(types)); + for (int i = 0; i < types.Length; i++) + { + if (types[i] == null) + throw new ArgumentNullException(nameof(types)); + } + return GetMethodImpl(name, genericParameterCount, bindingAttr, binder, callConvention, types, modifiers); + } + + protected virtual MethodInfo GetMethodImpl(string name, int genericParameterCount, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) => throw new NotSupportedException(); + + public MethodInfo[] GetMethods() => GetMethods(Type.DefaultLookup); + public abstract MethodInfo[] GetMethods(BindingFlags bindingAttr); + + public Type GetNestedType(string name) => GetNestedType(name, Type.DefaultLookup); + public abstract Type GetNestedType(string name, BindingFlags bindingAttr); + + public Type[] GetNestedTypes() => GetNestedTypes(Type.DefaultLookup); + public abstract Type[] GetNestedTypes(BindingFlags bindingAttr); + + public PropertyInfo GetProperty(string name) => GetProperty(name, Type.DefaultLookup); + public PropertyInfo GetProperty(string name, BindingFlags bindingAttr) + { + if (name == null) + throw new ArgumentNullException(nameof(name)); + return GetPropertyImpl(name, bindingAttr, null, null, null, null); + } + + public PropertyInfo GetProperty(string name, Type returnType) + { + if (name == null) + throw new ArgumentNullException(nameof(name)); + if (returnType == null) + throw new ArgumentNullException(nameof(returnType)); + return GetPropertyImpl(name, Type.DefaultLookup, null, returnType, null, null); + } + + public PropertyInfo GetProperty(string name, Type[] types) => GetProperty(name, null, types); + public PropertyInfo GetProperty(string name, Type returnType, Type[] types) => GetProperty(name, returnType, types, null); + public PropertyInfo GetProperty(string name, Type returnType, Type[] types, ParameterModifier[] modifiers) => GetProperty(name, Type.DefaultLookup, null, returnType, types, modifiers); + public PropertyInfo GetProperty(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) + { + if (name == null) + throw new ArgumentNullException(nameof(name)); + if (types == null) + throw new ArgumentNullException(nameof(types)); + return GetPropertyImpl(name, bindingAttr, binder, returnType, types, modifiers); + } + + protected abstract PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers); + + public PropertyInfo[] GetProperties() => GetProperties(Type.DefaultLookup); + public abstract PropertyInfo[] GetProperties(BindingFlags bindingAttr); + + public virtual MemberInfo[] GetDefaultMembers() { throw NotImplemented.ByDesign; } + + public virtual RuntimeTypeHandle TypeHandle { get { throw new NotSupportedException(); } } + public static RuntimeTypeHandle GetTypeHandle(object o) + { + if (o == null) + throw new ArgumentNullException(null, SR.Arg_InvalidHandle); + Type type = o.GetType(); + return type.TypeHandle; + } + + public static Type[] GetTypeArray(object[] args) + { + if (args == null) + throw new ArgumentNullException(nameof(args)); + + Type[] cls = new Type[args.Length]; + for (int i = 0; i < cls.Length; i++) + { + if (args[i] == null) + throw new ArgumentNullException(); + cls[i] = args[i].GetType(); + } + return cls; + } + + public static TypeCode GetTypeCode(Type type) + { + if (type == null) + return TypeCode.Empty; + return type.GetTypeCodeImpl(); + } + protected virtual TypeCode GetTypeCodeImpl() + { + if (this != UnderlyingSystemType && UnderlyingSystemType != null) + return Type.GetTypeCode(UnderlyingSystemType); + + return TypeCode.Object; + } + + public abstract Guid GUID { get; } + + public static Type GetTypeFromCLSID(Guid clsid) => GetTypeFromCLSID(clsid, null, throwOnError: false); + public static Type GetTypeFromCLSID(Guid clsid, bool throwOnError) => GetTypeFromCLSID(clsid, null, throwOnError: throwOnError); + public static Type GetTypeFromCLSID(Guid clsid, string server) => GetTypeFromCLSID(clsid, server, throwOnError: false); + + public static Type GetTypeFromProgID(string progID) => GetTypeFromProgID(progID, null, throwOnError: false); + public static Type GetTypeFromProgID(string progID, bool throwOnError) => GetTypeFromProgID(progID, null, throwOnError: throwOnError); + public static Type GetTypeFromProgID(string progID, string server) => GetTypeFromProgID(progID, server, throwOnError: false); + + public abstract Type BaseType { get; } + + [DebuggerHidden] + [DebuggerStepThrough] + public object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args) => InvokeMember(name, invokeAttr, binder, target, args, null, null, null); + + [DebuggerHidden] + [DebuggerStepThrough] + public object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, CultureInfo culture) => InvokeMember(name, invokeAttr, binder, target, args, null, culture, null); + public abstract object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters); + + public Type GetInterface(string name) => GetInterface(name, ignoreCase: false); + public abstract Type GetInterface(string name, bool ignoreCase); + public abstract Type[] GetInterfaces(); + + public virtual InterfaceMapping GetInterfaceMap(Type interfaceType) { throw new NotSupportedException(SR.NotSupported_SubclassOverride); } + + public virtual bool IsInstanceOfType(object o) => o == null ? false : IsAssignableFrom(o.GetType()); + public virtual bool IsEquivalentTo(Type other) => this == other; + + public virtual Type GetEnumUnderlyingType() + { + if (!IsEnum) + throw new ArgumentException(SR.Arg_MustBeEnum, "enumType"); + + FieldInfo[] fields = GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + if (fields == null || fields.Length != 1) + throw new ArgumentException(SR.Argument_InvalidEnum, "enumType"); + + return fields[0].FieldType; + } + public virtual Array GetEnumValues() + { + if (!IsEnum) + throw new ArgumentException(SR.Arg_MustBeEnum, "enumType"); + + // We don't support GetEnumValues in the default implementation because we cannot create an array of + // a non-runtime type. If there is strong need we can consider returning an object or int64 array. + throw NotImplemented.ByDesign; + } + + public virtual Type MakeArrayType() { throw new NotSupportedException(); } + public virtual Type MakeArrayType(int rank) { throw new NotSupportedException(); } + public virtual Type MakeByRefType() { throw new NotSupportedException(); } + public virtual Type MakeGenericType(params Type[] typeArguments) { throw new NotSupportedException(SR.NotSupported_SubclassOverride); } + public virtual Type MakePointerType() { throw new NotSupportedException(); } + + public static Type MakeGenericMethodParameter(int position) + { + if (position < 0) + throw new ArgumentException(SR.ArgumentOutOfRange_MustBeNonNegNum, nameof(position)); + return new SignatureGenericMethodParameterType(position); + } + + public override string ToString() => "Type: " + Name; // Why do we add the "Type: " prefix? + + public override bool Equals(object o) => o == null ? false : Equals(o as Type); + public override int GetHashCode() + { + Type systemType = UnderlyingSystemType; + if (!object.ReferenceEquals(systemType, this)) + return systemType.GetHashCode(); + return base.GetHashCode(); + } + public virtual bool Equals(Type o) => o == null ? false : object.ReferenceEquals(this.UnderlyingSystemType, o.UnderlyingSystemType); + + public static Type ReflectionOnlyGetType(string typeName, bool throwIfNotFound, bool ignoreCase) { throw new PlatformNotSupportedException(SR.PlatformNotSupported_ReflectionOnly); } + + public static Binder DefaultBinder + { + get + { + if (s_defaultBinder == null) + { + DefaultBinder binder = new DefaultBinder(); + Interlocked.CompareExchange(ref s_defaultBinder, binder, null); + } + return s_defaultBinder; + } + } + + private static volatile Binder s_defaultBinder; + + public static readonly char Delimiter = '.'; + public static readonly Type[] EmptyTypes = Array.Empty(); + public static readonly object Missing = System.Reflection.Missing.Value; + + public static readonly MemberFilter FilterAttribute = FilterAttributeImpl; + public static readonly MemberFilter FilterName = FilterNameImpl; + public static readonly MemberFilter FilterNameIgnoreCase = FilterNameIgnoreCaseImpl; + + private const BindingFlags DefaultLookup = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/TypeAccessException.cs b/external/corefx/src/Common/src/CoreLib/System/TypeAccessException.cs new file mode 100644 index 0000000000..e12a8b08dd --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/TypeAccessException.cs @@ -0,0 +1,37 @@ +// 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.Runtime.Serialization; + +namespace System +{ + // TypeAccessException derives from TypeLoadException rather than MemberAccessException because in + // pre-v4 releases of the runtime TypeLoadException was used in lieu of a TypeAccessException. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class TypeAccessException : TypeLoadException + { + public TypeAccessException() + : base(SR.Arg_TypeAccessException) + { + HResult = HResults.COR_E_TYPEACCESS; + } + + public TypeAccessException(string message) + : base(message) + { + HResult = HResults.COR_E_TYPEACCESS; + } + + public TypeAccessException(string message, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_TYPEACCESS; + } + + protected TypeAccessException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/TypeCode.cs b/external/corefx/src/Common/src/CoreLib/System/TypeCode.cs new file mode 100644 index 0000000000..296198656b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/TypeCode.cs @@ -0,0 +1,47 @@ +// 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. + +// The TypeCode enum represents the type code of an object. To obtain the +// TypeCode for a given object, use the Value.GetTypeCode() method. The +// TypeCode.Empty value represents a null object reference. The TypeCode.Object +// value represents an object that doesn't implement the IConvertible interface. The +// TypeCode.DBNull value represents the database null, which is distinct and +// different from a null reference. The other type codes represent values that +// use the given simple type encoding. +// +// Note that when an object has a given TypeCode, there is no guarantee that +// the object is an instance of the corresponding System.XXX value class. For +// example, an object with the type code TypeCode.Int32 might actually be an +// instance of a nullable 32-bit integer type (with a value that isn't the +// database null). +// +// There are no type codes for "Missing", "Error", "IDispatch", and "IUnknown". +// These types of values are instead represented as classes. When the type code +// of an object is TypeCode.Object, a further instance-of check can be used to +// determine if the object is one of these values. + +namespace System +{ + public enum TypeCode + { + Empty = 0, // Null reference + Object = 1, // Instance that isn't a value + DBNull = 2, // Database null value + Boolean = 3, // Boolean + Char = 4, // Unicode character + SByte = 5, // Signed 8-bit integer + Byte = 6, // Unsigned 8-bit integer + Int16 = 7, // Signed 16-bit integer + UInt16 = 8, // Unsigned 16-bit integer + Int32 = 9, // Signed 32-bit integer + UInt32 = 10, // Unsigned 32-bit integer + Int64 = 11, // Signed 64-bit integer + UInt64 = 12, // Unsigned 64-bit integer + Single = 13, // IEEE 32-bit float + Double = 14, // IEEE 64-bit double + Decimal = 15, // Decimal + DateTime = 16, // DateTime + String = 18, // Unicode character string + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/TypeInitializationException.cs b/external/corefx/src/Common/src/CoreLib/System/TypeInitializationException.cs new file mode 100644 index 0000000000..4bf2906217 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/TypeInitializationException.cs @@ -0,0 +1,80 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: The exception class to wrap exceptions thrown by +** a type's class initializer (.cctor). This is sufficiently +** distinct from a TypeLoadException, which means we couldn't +** find the type. +** +** +=============================================================================*/ + +using System.Globalization; +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class TypeInitializationException : SystemException + { + private String _typeName; + + // This exception is not creatable without specifying the + // inner exception. + private TypeInitializationException() + : base(SR.TypeInitialization_Default) + { + HResult = HResults.COR_E_TYPEINITIALIZATION; + } + + + public TypeInitializationException(String fullTypeName, Exception innerException) + : this(fullTypeName, SR.Format(SR.TypeInitialization_Type, fullTypeName), innerException) + { + } + + // This is called from within the runtime. I believe this is necessary + // for Interop only, though it's not particularly useful. + internal TypeInitializationException(String message) : base(message) + { + HResult = HResults.COR_E_TYPEINITIALIZATION; + } + + internal TypeInitializationException(String fullTypeName, String message, Exception innerException) + : base(message, innerException) + { + _typeName = fullTypeName; + HResult = HResults.COR_E_TYPEINITIALIZATION; + } + + internal TypeInitializationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + _typeName = info.GetString("TypeName"); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("TypeName", TypeName, typeof(string)); + } + + public String TypeName + { + get + { + if (_typeName == null) + { + return String.Empty; + } + return _typeName; + } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/TypeUnloadedException.cs b/external/corefx/src/Common/src/CoreLib/System/TypeUnloadedException.cs new file mode 100644 index 0000000000..a01ef37a8b --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/TypeUnloadedException.cs @@ -0,0 +1,37 @@ +// 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.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class TypeUnloadedException : SystemException + { + public TypeUnloadedException() + : base(SR.Arg_TypeUnloadedException) + { + HResult = HResults.COR_E_TYPEUNLOADED; + } + + public TypeUnloadedException(string message) + : base(message) + { + HResult = HResults.COR_E_TYPEUNLOADED; + } + + public TypeUnloadedException(string message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_TYPEUNLOADED; + } + + protected TypeUnloadedException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} + diff --git a/external/corert/src/System.Private.CoreLib/src/System/UInt16.cs b/external/corefx/src/Common/src/CoreLib/System/UInt16.cs similarity index 51% rename from external/corert/src/System.Private.CoreLib/src/System/UInt16.cs rename to external/corefx/src/Common/src/CoreLib/System/UInt16.cs index 576fe5d233..3047d18198 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/UInt16.cs +++ b/external/corefx/src/Common/src/CoreLib/System/UInt16.cs @@ -2,28 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -/*============================================================ -** -** -** Purpose: This class will encapsulate a short and provide an -** Object representation of it. -** -** -===========================================================*/ - using System.Globalization; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; -using System.Diagnostics.Contracts; namespace System { - // Wrapper for unsigned 16 bit integers. + [Serializable] [CLSCompliant(false)] - [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)] - public struct UInt16 : IComparable, IFormattable, IComparable, IEquatable, IConvertible + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct UInt16 : IComparable, IConvertible, IFormattable, IComparable, IEquatable, ISpanFormattable { - private ushort _value; + private ushort m_value; // Do not rename (binary serialization) public const ushort MaxValue = (ushort)0xFFFF; public const ushort MinValue = 0; @@ -43,14 +35,14 @@ namespace System } if (value is UInt16) { - return ((int)_value - (int)(((UInt16)value)._value)); + return ((int)m_value - (int)(((UInt16)value).m_value)); } throw new ArgumentException(SR.Arg_MustBeUInt16); } public int CompareTo(UInt16 value) { - return ((int)_value - (int)value); + return ((int)m_value - (int)value); } public override bool Equals(Object obj) @@ -59,75 +51,92 @@ namespace System { return false; } - return _value == ((UInt16)obj)._value; + return m_value == ((UInt16)obj).m_value; } [NonVersionable] public bool Equals(UInt16 obj) { - return _value == obj; + return m_value == obj; } // Returns a HashCode for the UInt16 public override int GetHashCode() { - return (int)_value; + return (int)m_value; } // Converts the current value to a String in base-10 with no extra padding. public override String ToString() { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatUInt32(_value, null, null); + return Number.FormatUInt32(m_value, null, null); } public String ToString(IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatUInt32(_value, null, provider); + return Number.FormatUInt32(m_value, null, provider); } public String ToString(String format) { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatUInt32(_value, format, null); + return Number.FormatUInt32(m_value, format, null); } public String ToString(String format, IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatUInt32(_value, format, provider); + return Number.FormatUInt32(m_value, format, provider); + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) + { + return Number.TryFormatUInt32(m_value, format, provider, destination, out charsWritten); } [CLSCompliant(false)] public static ushort Parse(String s) { - return Parse(s, NumberStyles.Integer, null); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } [CLSCompliant(false)] public static ushort Parse(String s, NumberStyles style) { - UInt32.ValidateParseStyleInteger(style); - return Parse(s, style, null); + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, style, NumberFormatInfo.CurrentInfo); } [CLSCompliant(false)] public static ushort Parse(String s, IFormatProvider provider) { - return Parse(s, NumberStyles.Integer, provider); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } [CLSCompliant(false)] public static ushort Parse(String s, NumberStyles style, IFormatProvider provider) { - UInt32.ValidateParseStyleInteger(style); + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static ushort Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Parse(s, style, NumberFormatInfo.GetInstance(provider)); + } + + private static ushort Parse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info) + { uint i = 0; try { - i = FormatProvider.ParseUInt32(s, style, provider); + i = Number.ParseUInt32(s, style, info); } catch (OverflowException e) { @@ -141,16 +150,47 @@ namespace System [CLSCompliant(false)] public static bool TryParse(String s, out UInt16 result) { - return TryParse(s, NumberStyles.Integer, null, out result); + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan s, out ushort result) + { + return TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); } [CLSCompliant(false)] public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out UInt16 result) { - UInt32.ValidateParseStyleInteger(style); + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out ushort result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + private static bool TryParse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out UInt16 result) + { result = 0; UInt32 i; - if (!FormatProvider.TryParseUInt32(s, style, provider, out i)) + if (!Number.TryParseUInt32(s, style, info, out i)) { return false; } @@ -173,72 +213,72 @@ namespace System bool IConvertible.ToBoolean(IFormatProvider provider) { - return Convert.ToBoolean(_value); + return Convert.ToBoolean(m_value); } char IConvertible.ToChar(IFormatProvider provider) { - return Convert.ToChar(_value); + return Convert.ToChar(m_value); } sbyte IConvertible.ToSByte(IFormatProvider provider) { - return Convert.ToSByte(_value); + return Convert.ToSByte(m_value); } byte IConvertible.ToByte(IFormatProvider provider) { - return Convert.ToByte(_value); + return Convert.ToByte(m_value); } short IConvertible.ToInt16(IFormatProvider provider) { - return Convert.ToInt16(_value); + return Convert.ToInt16(m_value); } ushort IConvertible.ToUInt16(IFormatProvider provider) { - return _value; + return m_value; } int IConvertible.ToInt32(IFormatProvider provider) { - return Convert.ToInt32(_value); + return Convert.ToInt32(m_value); } uint IConvertible.ToUInt32(IFormatProvider provider) { - return Convert.ToUInt32(_value); + return Convert.ToUInt32(m_value); } long IConvertible.ToInt64(IFormatProvider provider) { - return Convert.ToInt64(_value); + return Convert.ToInt64(m_value); } ulong IConvertible.ToUInt64(IFormatProvider provider) { - return Convert.ToUInt64(_value); + return Convert.ToUInt64(m_value); } float IConvertible.ToSingle(IFormatProvider provider) { - return Convert.ToSingle(_value); + return Convert.ToSingle(m_value); } double IConvertible.ToDouble(IFormatProvider provider) { - return Convert.ToDouble(_value); + return Convert.ToDouble(m_value); } Decimal IConvertible.ToDecimal(IFormatProvider provider) { - return Convert.ToDecimal(_value); + return Convert.ToDecimal(m_value); } DateTime IConvertible.ToDateTime(IFormatProvider provider) { - throw new InvalidCastException(String.Format(SR.InvalidCast_FromTo, "UInt16", "DateTime")); + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "UInt16", "DateTime")); } Object IConvertible.ToType(Type type, IFormatProvider provider) diff --git a/external/corert/src/System.Private.CoreLib/src/System/UInt32.cs b/external/corefx/src/Common/src/CoreLib/System/UInt32.cs similarity index 52% rename from external/corert/src/System.Private.CoreLib/src/System/UInt32.cs rename to external/corefx/src/Common/src/CoreLib/System/UInt32.cs index 0204551317..1e33dcf17b 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/UInt32.cs +++ b/external/corefx/src/Common/src/CoreLib/System/UInt32.cs @@ -2,29 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -/*============================================================ -** -** -** -** Purpose: This class will encapsulate an uint and -** provide an Object representation of it. -** -** -===========================================================*/ - using System.Globalization; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; -using System.Diagnostics.Contracts; namespace System { - // * Wrapper for unsigned 32 bit integers. + [Serializable] [CLSCompliant(false)] - [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)] - public struct UInt32 : IComparable, IFormattable, IComparable, IEquatable, IConvertible + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct UInt32 : IComparable, IConvertible, IFormattable, IComparable, IEquatable, ISpanFormattable { - private uint _value; + private uint m_value; // Do not rename (binary serialization) public const uint MaxValue = (uint)0xffffffff; public const uint MinValue = 0U; @@ -47,8 +38,8 @@ namespace System // Need to use compare because subtraction will wrap // to positive for very large neg numbers, etc. uint i = (uint)value; - if (_value < i) return -1; - if (_value > i) return 1; + if (m_value < i) return -1; + if (m_value > i) return 1; return 0; } throw new ArgumentException(SR.Arg_MustBeUInt32); @@ -58,8 +49,8 @@ namespace System { // Need to use compare because subtraction will wrap // to positive for very large neg numbers, etc. - if (_value < value) return -1; - if (_value > value) return 1; + if (m_value < value) return -1; + if (m_value > value) return 1; return 0; } @@ -69,101 +60,122 @@ namespace System { return false; } - return _value == ((UInt32)obj)._value; + return m_value == ((UInt32)obj).m_value; } [NonVersionable] public bool Equals(UInt32 obj) { - return _value == obj; + return m_value == obj; } // The absolute value of the int contained. public override int GetHashCode() { - return ((int)_value); + return ((int)m_value); } // The base 10 representation of the number with no extra padding. public override String ToString() { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatUInt32(_value, null, null); + return Number.FormatUInt32(m_value, null, null); } public String ToString(IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatUInt32(_value, null, provider); + return Number.FormatUInt32(m_value, null, provider); } public String ToString(String format) { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatUInt32(_value, format, null); + return Number.FormatUInt32(m_value, format, null); } public String ToString(String format, IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatUInt32(_value, format, provider); + return Number.FormatUInt32(m_value, format, provider); + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) + { + return Number.TryFormatUInt32(m_value, format, provider, destination, out charsWritten); } [CLSCompliant(false)] public static uint Parse(String s) { - return FormatProvider.ParseUInt32(s, NumberStyles.Integer, null); - } - - internal static void ValidateParseStyleInteger(NumberStyles style) - { - // Check for undefined flags - if ((style & Decimal.InvalidNumberStyles) != 0) - { - throw new ArgumentException(SR.Argument_InvalidNumberStyles, nameof(style)); - } - Contract.EndContractBlock(); - if ((style & NumberStyles.AllowHexSpecifier) != 0) - { // Check for hex number - if ((style & ~NumberStyles.HexNumber) != 0) - { - throw new ArgumentException(SR.Arg_InvalidHexStyle); - } - } + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } [CLSCompliant(false)] public static uint Parse(String s, NumberStyles style) { - ValidateParseStyleInteger(style); - return FormatProvider.ParseUInt32(s, style, null); + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt32(s, style, NumberFormatInfo.CurrentInfo); } [CLSCompliant(false)] public static uint Parse(String s, IFormatProvider provider) { - return FormatProvider.ParseUInt32(s, NumberStyles.Integer, provider); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt32(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } [CLSCompliant(false)] public static uint Parse(String s, NumberStyles style, IFormatProvider provider) { - ValidateParseStyleInteger(style); - return FormatProvider.ParseUInt32(s, style, provider); + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt32(s, style, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static uint Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.ParseUInt32(s, style, NumberFormatInfo.GetInstance(provider)); } [CLSCompliant(false)] public static bool TryParse(String s, out UInt32 result) { - return FormatProvider.TryParseUInt32(s, NumberStyles.Integer, null, out result); + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseUInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan s, out uint result) + { + return Number.TryParseUInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); } [CLSCompliant(false)] public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out UInt32 result) { - ValidateParseStyleInteger(style); - return FormatProvider.TryParseUInt32(s, style, provider, out result); + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseUInt32(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out uint result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseUInt32(s, style, NumberFormatInfo.GetInstance(provider), out result); } // @@ -177,72 +189,72 @@ namespace System bool IConvertible.ToBoolean(IFormatProvider provider) { - return Convert.ToBoolean(_value); + return Convert.ToBoolean(m_value); } char IConvertible.ToChar(IFormatProvider provider) { - return Convert.ToChar(_value); + return Convert.ToChar(m_value); } sbyte IConvertible.ToSByte(IFormatProvider provider) { - return Convert.ToSByte(_value); + return Convert.ToSByte(m_value); } byte IConvertible.ToByte(IFormatProvider provider) { - return Convert.ToByte(_value); + return Convert.ToByte(m_value); } short IConvertible.ToInt16(IFormatProvider provider) { - return Convert.ToInt16(_value); + return Convert.ToInt16(m_value); } ushort IConvertible.ToUInt16(IFormatProvider provider) { - return Convert.ToUInt16(_value); + return Convert.ToUInt16(m_value); } int IConvertible.ToInt32(IFormatProvider provider) { - return Convert.ToInt32(_value); + return Convert.ToInt32(m_value); } uint IConvertible.ToUInt32(IFormatProvider provider) { - return _value; + return m_value; } long IConvertible.ToInt64(IFormatProvider provider) { - return Convert.ToInt64(_value); + return Convert.ToInt64(m_value); } ulong IConvertible.ToUInt64(IFormatProvider provider) { - return Convert.ToUInt64(_value); + return Convert.ToUInt64(m_value); } float IConvertible.ToSingle(IFormatProvider provider) { - return Convert.ToSingle(_value); + return Convert.ToSingle(m_value); } double IConvertible.ToDouble(IFormatProvider provider) { - return Convert.ToDouble(_value); + return Convert.ToDouble(m_value); } Decimal IConvertible.ToDecimal(IFormatProvider provider) { - return Convert.ToDecimal(_value); + return Convert.ToDecimal(m_value); } DateTime IConvertible.ToDateTime(IFormatProvider provider) { - throw new InvalidCastException(String.Format(SR.InvalidCast_FromTo, "UInt32", "DateTime")); + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "UInt32", "DateTime")); } Object IConvertible.ToType(Type type, IFormatProvider provider) diff --git a/external/corert/src/System.Private.CoreLib/src/System/UInt64.cs b/external/corefx/src/Common/src/CoreLib/System/UInt64.cs similarity index 51% rename from external/corert/src/System.Private.CoreLib/src/System/UInt64.cs rename to external/corefx/src/Common/src/CoreLib/System/UInt64.cs index 7ff6874957..d30fbe1e42 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/UInt64.cs +++ b/external/corefx/src/Common/src/CoreLib/System/UInt64.cs @@ -2,28 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -/*============================================================ -** -** -** Purpose: This class will encapsulate an unsigned long and -** provide an Object representation of it. -** -** -===========================================================*/ - using System.Globalization; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; -using System.Diagnostics.Contracts; namespace System { - // Wrapper for unsigned 64 bit integers. + [Serializable] [CLSCompliant(false)] - [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)] - public struct UInt64 : IComparable, IFormattable, IComparable, IEquatable, IConvertible + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct UInt64 : IComparable, IConvertible, IFormattable, IComparable, IEquatable, ISpanFormattable { - private ulong _value; + private ulong m_value; // Do not rename (binary serialization) public const ulong MaxValue = (ulong)0xffffffffffffffffL; public const ulong MinValue = 0x0; @@ -45,8 +37,8 @@ namespace System // Need to use compare because subtraction will wrap // to positive for very large neg numbers, etc. ulong i = (ulong)value; - if (_value < i) return -1; - if (_value > i) return 1; + if (m_value < i) return -1; + if (m_value > i) return 1; return 0; } throw new ArgumentException(SR.Arg_MustBeUInt64); @@ -56,8 +48,8 @@ namespace System { // Need to use compare because subtraction will wrap // to positive for very large neg numbers, etc. - if (_value < value) return -1; - if (_value > value) return 1; + if (m_value < value) return -1; + if (m_value > value) return 1; return 0; } @@ -67,82 +59,120 @@ namespace System { return false; } - return _value == ((UInt64)obj)._value; + return m_value == ((UInt64)obj).m_value; } [NonVersionable] public bool Equals(UInt64 obj) { - return _value == obj; + return m_value == obj; } // The value of the lower 32 bits XORed with the uppper 32 bits. public override int GetHashCode() { - return ((int)_value) ^ (int)(_value >> 32); + return ((int)m_value) ^ (int)(m_value >> 32); } public override String ToString() { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatUInt64(_value, null, null); + return Number.FormatUInt64(m_value, null, null); } public String ToString(IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatUInt64(_value, null, provider); + return Number.FormatUInt64(m_value, null, provider); } public String ToString(String format) { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatUInt64(_value, format, null); + return Number.FormatUInt64(m_value, format, null); } public String ToString(String format, IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatUInt64(_value, format, provider); + return Number.FormatUInt64(m_value, format, provider); + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) + { + return Number.TryFormatUInt64(m_value, format, provider, destination, out charsWritten); } [CLSCompliant(false)] public static ulong Parse(String s) { - return FormatProvider.ParseUInt64(s, NumberStyles.Integer, null); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } [CLSCompliant(false)] public static ulong Parse(String s, NumberStyles style) { - UInt32.ValidateParseStyleInteger(style); - return FormatProvider.ParseUInt64(s, style, null); + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt64(s, style, NumberFormatInfo.CurrentInfo); } [CLSCompliant(false)] public static ulong Parse(string s, IFormatProvider provider) { - return FormatProvider.ParseUInt64(s, NumberStyles.Integer, provider); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt64(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } [CLSCompliant(false)] public static ulong Parse(String s, NumberStyles style, IFormatProvider provider) { - UInt32.ValidateParseStyleInteger(style); - return FormatProvider.ParseUInt64(s, style, provider); + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt64(s, style, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static ulong Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.ParseUInt64(s, style, NumberFormatInfo.GetInstance(provider)); } [CLSCompliant(false)] public static Boolean TryParse(String s, out UInt64 result) { - return FormatProvider.TryParseUInt64(s, NumberStyles.Integer, null, out result); + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseUInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan s, out ulong result) + { + return Number.TryParseUInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); } [CLSCompliant(false)] public static Boolean TryParse(String s, NumberStyles style, IFormatProvider provider, out UInt64 result) { - UInt32.ValidateParseStyleInteger(style); - return FormatProvider.TryParseUInt64(s, style, provider, out result); + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseUInt64(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out ulong result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseUInt64(s, style, NumberFormatInfo.GetInstance(provider), out result); } // @@ -156,72 +186,72 @@ namespace System bool IConvertible.ToBoolean(IFormatProvider provider) { - return Convert.ToBoolean(_value); + return Convert.ToBoolean(m_value); } char IConvertible.ToChar(IFormatProvider provider) { - return Convert.ToChar(_value); + return Convert.ToChar(m_value); } sbyte IConvertible.ToSByte(IFormatProvider provider) { - return Convert.ToSByte(_value); + return Convert.ToSByte(m_value); } byte IConvertible.ToByte(IFormatProvider provider) { - return Convert.ToByte(_value); + return Convert.ToByte(m_value); } short IConvertible.ToInt16(IFormatProvider provider) { - return Convert.ToInt16(_value); + return Convert.ToInt16(m_value); } ushort IConvertible.ToUInt16(IFormatProvider provider) { - return Convert.ToUInt16(_value); + return Convert.ToUInt16(m_value); } int IConvertible.ToInt32(IFormatProvider provider) { - return Convert.ToInt32(_value); + return Convert.ToInt32(m_value); } uint IConvertible.ToUInt32(IFormatProvider provider) { - return Convert.ToUInt32(_value); + return Convert.ToUInt32(m_value); } long IConvertible.ToInt64(IFormatProvider provider) { - return Convert.ToInt64(_value); + return Convert.ToInt64(m_value); } ulong IConvertible.ToUInt64(IFormatProvider provider) { - return _value; + return m_value; } float IConvertible.ToSingle(IFormatProvider provider) { - return Convert.ToSingle(_value); + return Convert.ToSingle(m_value); } double IConvertible.ToDouble(IFormatProvider provider) { - return Convert.ToDouble(_value); + return Convert.ToDouble(m_value); } Decimal IConvertible.ToDecimal(IFormatProvider provider) { - return Convert.ToDecimal(_value); + return Convert.ToDecimal(m_value); } DateTime IConvertible.ToDateTime(IFormatProvider provider) { - throw new InvalidCastException(String.Format(SR.InvalidCast_FromTo, "UInt64", "DateTime")); + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "UInt64", "DateTime")); } Object IConvertible.ToType(Type type, IFormatProvider provider) diff --git a/external/corert/src/System.Private.CoreLib/src/System/UIntPtr.cs b/external/corefx/src/Common/src/CoreLib/System/UIntPtr.cs similarity index 82% rename from external/corert/src/System.Private.CoreLib/src/System/UIntPtr.cs rename to external/corefx/src/Common/src/CoreLib/System/UIntPtr.cs index de9fdfe6d5..23750e95fa 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/UIntPtr.cs +++ b/external/corefx/src/Common/src/CoreLib/System/UIntPtr.cs @@ -7,16 +7,20 @@ using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Runtime.Versioning; +#if BIT64 +using nuint = System.UInt64; +#else +using nuint = System.UInt32; +#endif + namespace System { - // CONTRACT with Runtime - // The UIntPtr type is one of the primitives understood by the compilers and runtime - // Data Contract: Single field of type void * [Serializable] [CLSCompliant(false)] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public struct UIntPtr : IEquatable, ISerializable { - unsafe private void* _value; + unsafe private void* _value; // Do not rename (binary serialization) [Intrinsic] public static readonly UIntPtr Zero; @@ -61,14 +65,31 @@ namespace System if (info == null) throw new ArgumentNullException(nameof(info)); - info.AddValue("value", (ulong)_value); + info.AddValue("value", ToUInt64()); } - [Intrinsic] - [NonVersionable] - public unsafe void* ToPointer() + public unsafe override bool Equals(Object obj) { - return _value; + if (obj is UIntPtr) + { + return (_value == ((UIntPtr)obj)._value); + } + return false; + } + + unsafe bool IEquatable.Equals(UIntPtr value) + { + return _value == value._value; + } + + public unsafe override int GetHashCode() + { +#if BIT64 + ulong l = (ulong)_value; + return (unchecked((int)l) ^ (int)(l >> 32)); +#else + return unchecked((int)_value); +#endif } [Intrinsic] @@ -135,11 +156,6 @@ namespace System return (ulong)value._value; } - unsafe bool IEquatable.Equals(UIntPtr value) - { - return _value == value._value; - } - [Intrinsic] [NonVersionable] public static unsafe bool operator ==(UIntPtr value1, UIntPtr value2) @@ -154,48 +170,6 @@ namespace System return value1._value != value2._value; } - public static unsafe int Size - { - [Intrinsic] - [NonVersionable] - get - { -#if BIT64 - return 8; -#else - return 4; -#endif - } - } - - public unsafe override String ToString() - { -#if BIT64 - return ((ulong)_value).ToString(FormatProvider.InvariantCulture); -#else - return ((uint)_value).ToString(FormatProvider.InvariantCulture); -#endif - } - - public unsafe override bool Equals(Object obj) - { - if (obj is UIntPtr) - { - return (_value == ((UIntPtr)obj)._value); - } - return false; - } - - public unsafe override int GetHashCode() - { -#if BIT64 - ulong l = (ulong)_value; - return (unchecked((int)l) ^ (int)(l >> 32)); -#else - return unchecked((int)_value); -#endif - } - [NonVersionable] public static UIntPtr Add(UIntPtr pointer, int offset) { @@ -204,13 +178,9 @@ namespace System [Intrinsic] [NonVersionable] - public static UIntPtr operator +(UIntPtr pointer, int offset) + public static unsafe UIntPtr operator +(UIntPtr pointer, int offset) { -#if BIT64 - return new UIntPtr(pointer.ToUInt64() + (ulong)offset); -#else - return new UIntPtr(pointer.ToUInt32() + (uint)offset); -#endif + return new UIntPtr((nuint)pointer._value + (nuint)offset); } [NonVersionable] @@ -221,15 +191,31 @@ namespace System [Intrinsic] [NonVersionable] - public static UIntPtr operator -(UIntPtr pointer, int offset) + public static unsafe UIntPtr operator -(UIntPtr pointer, int offset) { -#if BIT64 - return new UIntPtr(pointer.ToUInt64() - (ulong)offset); -#else - return new UIntPtr(pointer.ToUInt32() - (uint)offset); -#endif + return new UIntPtr((nuint)pointer._value - (nuint)offset); + } + + public static unsafe int Size + { + [Intrinsic] + [NonVersionable] + get + { + return sizeof(nuint); + } + } + + [Intrinsic] + [NonVersionable] + public unsafe void* ToPointer() + { + return _value; + } + + public unsafe override string ToString() + { + return ((nuint)_value).ToString(CultureInfo.InvariantCulture); } } } - - diff --git a/external/corefx/src/Common/src/CoreLib/System/UnauthorizedAccessException.cs b/external/corefx/src/Common/src/CoreLib/System/UnauthorizedAccessException.cs new file mode 100644 index 0000000000..a28f6dd73c --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/UnauthorizedAccessException.cs @@ -0,0 +1,48 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: An exception for OS 'access denied' types of +** errors, including IO and limited security types +** of errors. +** +** +===========================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + // The UnauthorizedAccessException is thrown when access errors + // occur from IO or other OS methods. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class UnauthorizedAccessException : SystemException + { + public UnauthorizedAccessException() + : base(SR.Arg_UnauthorizedAccessException) + { + HResult = HResults.COR_E_UNAUTHORIZEDACCESS; + } + + public UnauthorizedAccessException(String message) + : base(message) + { + HResult = HResults.COR_E_UNAUTHORIZEDACCESS; + } + + public UnauthorizedAccessException(String message, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_UNAUTHORIZEDACCESS; + } + + protected UnauthorizedAccessException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/UnhandledExceptionEventArgs.cs b/external/corefx/src/Common/src/CoreLib/System/UnhandledExceptionEventArgs.cs new file mode 100644 index 0000000000..5cde572161 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/UnhandledExceptionEventArgs.cs @@ -0,0 +1,28 @@ +// 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. + +namespace System +{ + public class UnhandledExceptionEventArgs : EventArgs + { + private Object _exception; + private bool _isTerminating; + + public UnhandledExceptionEventArgs(Object exception, bool isTerminating) + { + _exception = exception; + _isTerminating = isTerminating; + } + + public Object ExceptionObject + { + get { return _exception; } + } + + public bool IsTerminating + { + get { return _isTerminating; } + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/UnhandledExceptionEventHandler.cs b/external/corefx/src/Common/src/CoreLib/System/UnhandledExceptionEventHandler.cs new file mode 100644 index 0000000000..14e31c7bbd --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/UnhandledExceptionEventHandler.cs @@ -0,0 +1,8 @@ +// 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. + +namespace System +{ + public delegate void UnhandledExceptionEventHandler(Object sender, UnhandledExceptionEventArgs e); +} diff --git a/external/corefx/src/Common/src/CoreLib/System/UnitySerializationHolder.cs b/external/corefx/src/Common/src/CoreLib/System/UnitySerializationHolder.cs new file mode 100644 index 0000000000..2f30356709 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/UnitySerializationHolder.cs @@ -0,0 +1,66 @@ +// 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.Runtime.Serialization; + +namespace System +{ + /// + /// Holds Null class for which we guarantee that there is only ever one instance of. + /// This only exists for compatibility with .NET Framework. + /// + [Serializable] +#if CORERT + public +#else + internal +#endif + sealed class UnitySerializationHolder : ISerializable, IObjectReference + { + internal const int NullUnity = 0x0002; + private readonly int _unityType; + private readonly string _data; + + /// + /// A helper method that returns the SerializationInfo that a class utilizing + /// UnitySerializationHelper should return from a call to GetObjectData. It contains + /// the unityType (defined above) and any optional data (used only for the reflection types). + /// + internal static void GetUnitySerializationInfo(SerializationInfo info, int unityType) + { + info.SetType(typeof(UnitySerializationHolder)); + info.AddValue("Data", null, typeof(string)); + info.AddValue("UnityType", unityType); + info.AddValue("AssemblyName", string.Empty); + } + + public UnitySerializationHolder(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + // We are ignoring any other serialization input as we are only concerned about DBNull. + // We also store data and use it for erorr logging. + _unityType = info.GetInt32("UnityType"); + _data = info.GetString("Data"); + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) => + throw new NotSupportedException(SR.NotSupported_UnitySerHolder); + + public object GetRealObject(StreamingContext context) + { + // We are only support deserializing DBNull and throwing for everything else. + if (_unityType != NullUnity) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidUnity, _data ?? "UnityType")); + } + + // We are always returning the same DBNull instance. + return DBNull.Value; + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/ValueTuple.cs.REMOVED.git-id b/external/corefx/src/Common/src/CoreLib/System/ValueTuple.cs.REMOVED.git-id new file mode 100644 index 0000000000..3140356503 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/ValueTuple.cs.REMOVED.git-id @@ -0,0 +1 @@ +8c1fe1a39b02cca44cc849fba0b0135c23d9d861 \ No newline at end of file diff --git a/external/corefx/src/Common/src/CoreLib/System/Version.cs b/external/corefx/src/Common/src/CoreLib/System/Version.cs new file mode 100644 index 0000000000..df16be2cd2 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Version.cs @@ -0,0 +1,445 @@ +// 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.Globalization; +using System.Diagnostics; +using System.Text; + +namespace System +{ + // A Version object contains four hierarchical numeric components: major, minor, + // build and revision. Build and revision may be unspecified, which is represented + // internally as a -1. By definition, an unspecified component matches anything + // (both unspecified and specified), and an unspecified component is "less than" any + // specified component. + + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class Version : ICloneable, IComparable, IComparable, IEquatable, ISpanFormattable + { + // AssemblyName depends on the order staying the same + private readonly int _Major; // Do not rename (binary serialization) + private readonly int _Minor; // Do not rename (binary serialization) + private readonly int _Build = -1; // Do not rename (binary serialization) + private readonly int _Revision = -1; // Do not rename (binary serialization) + + public Version(int major, int minor, int build, int revision) + { + if (major < 0) + throw new ArgumentOutOfRangeException(nameof(major), SR.ArgumentOutOfRange_Version); + + if (minor < 0) + throw new ArgumentOutOfRangeException(nameof(minor), SR.ArgumentOutOfRange_Version); + + if (build < 0) + throw new ArgumentOutOfRangeException(nameof(build), SR.ArgumentOutOfRange_Version); + + if (revision < 0) + throw new ArgumentOutOfRangeException(nameof(revision), SR.ArgumentOutOfRange_Version); + + _Major = major; + _Minor = minor; + _Build = build; + _Revision = revision; + } + + public Version(int major, int minor, int build) + { + if (major < 0) + throw new ArgumentOutOfRangeException(nameof(major), SR.ArgumentOutOfRange_Version); + + if (minor < 0) + throw new ArgumentOutOfRangeException(nameof(minor), SR.ArgumentOutOfRange_Version); + + if (build < 0) + throw new ArgumentOutOfRangeException(nameof(build), SR.ArgumentOutOfRange_Version); + + + _Major = major; + _Minor = minor; + _Build = build; + } + + public Version(int major, int minor) + { + if (major < 0) + throw new ArgumentOutOfRangeException(nameof(major), SR.ArgumentOutOfRange_Version); + + if (minor < 0) + throw new ArgumentOutOfRangeException(nameof(minor), SR.ArgumentOutOfRange_Version); + + _Major = major; + _Minor = minor; + } + + public Version(String version) + { + Version v = Version.Parse(version); + _Major = v.Major; + _Minor = v.Minor; + _Build = v.Build; + _Revision = v.Revision; + } + + public Version() + { + _Major = 0; + _Minor = 0; + } + + private Version(Version version) + { + Debug.Assert(version != null); + + _Major = version._Major; + _Minor = version._Minor; + _Build = version._Build; + _Revision = version._Revision; + } + + public object Clone() + { + return new Version(this); + } + + // Properties for setting and getting version numbers + public int Major + { + get { return _Major; } + } + + public int Minor + { + get { return _Minor; } + } + + public int Build + { + get { return _Build; } + } + + public int Revision + { + get { return _Revision; } + } + + public short MajorRevision + { + get { return (short)(_Revision >> 16); } + } + + public short MinorRevision + { + get { return (short)(_Revision & 0xFFFF); } + } + + public int CompareTo(Object version) + { + if (version == null) + { + return 1; + } + + Version v = version as Version; + if (v == null) + { + throw new ArgumentException(SR.Arg_MustBeVersion); + } + + return CompareTo(v); + } + + public int CompareTo(Version value) + { + return + object.ReferenceEquals(value, this) ? 0 : + object.ReferenceEquals(value, null) ? 1 : + _Major != value._Major ? (_Major > value._Major ? 1 : -1) : + _Minor != value._Minor ? (_Minor > value._Minor ? 1 : -1) : + _Build != value._Build ? (_Build > value._Build ? 1 : -1) : + _Revision != value._Revision ? (_Revision > value._Revision ? 1 : -1) : + 0; + } + + public override bool Equals(Object obj) + { + return Equals(obj as Version); + } + + public bool Equals(Version obj) + { + return object.ReferenceEquals(obj, this) || + (!object.ReferenceEquals(obj, null) && + _Major == obj._Major && + _Minor == obj._Minor && + _Build == obj._Build && + _Revision == obj._Revision); + } + + public override int GetHashCode() + { + // Let's assume that most version numbers will be pretty small and just + // OR some lower order bits together. + + int accumulator = 0; + + accumulator |= (_Major & 0x0000000F) << 28; + accumulator |= (_Minor & 0x000000FF) << 20; + accumulator |= (_Build & 0x000000FF) << 12; + accumulator |= (_Revision & 0x00000FFF); + + return accumulator; + } + + public override string ToString() => + ToString(DefaultFormatFieldCount); + + public string ToString(int fieldCount) => + fieldCount == 0 ? string.Empty : + fieldCount == 1 ? _Major.ToString() : + StringBuilderCache.GetStringAndRelease(ToCachedStringBuilder(fieldCount)); + + public bool TryFormat(Span destination, out int charsWritten) => + TryFormat(destination, DefaultFormatFieldCount, out charsWritten); + + public bool TryFormat(Span destination, int fieldCount, out int charsWritten) + { + if (fieldCount == 0) + { + charsWritten = 0; + return true; + } + else if (fieldCount == 1) + { + return _Major.TryFormat(destination, out charsWritten); + } + + StringBuilder sb = ToCachedStringBuilder(fieldCount); + if (sb.Length <= destination.Length) + { + sb.CopyTo(0, destination, sb.Length); + StringBuilderCache.Release(sb); + charsWritten = sb.Length; + return true; + } + + StringBuilderCache.Release(sb); + charsWritten = 0; + return false; + } + + bool ISpanFormattable.TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider provider) + { + // format and provider are ignored. + return TryFormat(destination, out charsWritten); + } + + private int DefaultFormatFieldCount => + _Build == -1 ? 2 : + _Revision == -1 ? 3 : + 4; + + private StringBuilder ToCachedStringBuilder(int fieldCount) + { + // Note: As we always have positive numbers then it is safe to convert the number to string + // regardless of the current culture as we'll not have any punctuation marks in the number. + + if (fieldCount == 2) + { + StringBuilder sb = StringBuilderCache.Acquire(); + sb.Append(_Major); + sb.Append('.'); + sb.Append(_Minor); + return sb; + } + else + { + if (_Build == -1) + { + throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "2"), nameof(fieldCount)); + } + + if (fieldCount == 3) + { + StringBuilder sb = StringBuilderCache.Acquire(); + sb.Append(_Major); + sb.Append('.'); + sb.Append(_Minor); + sb.Append('.'); + sb.Append(_Build); + return sb; + } + + if (_Revision == -1) + { + throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "3"), nameof(fieldCount)); + } + + if (fieldCount == 4) + { + StringBuilder sb = StringBuilderCache.Acquire(); + sb.Append(_Major); + sb.Append('.'); + sb.Append(_Minor); + sb.Append('.'); + sb.Append(_Build); + sb.Append('.'); + sb.Append(_Revision); + return sb; + } + + throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "4"), nameof(fieldCount)); + } + } + + public static Version Parse(string input) + { + if (input == null) + { + throw new ArgumentNullException(nameof(input)); + } + + return ParseVersion(input.AsReadOnlySpan(), throwOnFailure: true); + } + + public static Version Parse(ReadOnlySpan input) => + ParseVersion(input, throwOnFailure: true); + + public static bool TryParse(string input, out Version result) + { + if (input == null) + { + result = null; + return false; + } + + return (result = ParseVersion(input.AsReadOnlySpan(), throwOnFailure: false)) != null; + } + + public static bool TryParse(ReadOnlySpan input, out Version result) => + (result = ParseVersion(input, throwOnFailure: false)) != null; + + private static Version ParseVersion(ReadOnlySpan input, bool throwOnFailure) + { + // Find the separator between major and minor. It must exist. + int majorEnd = input.IndexOf('.'); + if (majorEnd < 0) + { + if (throwOnFailure) throw new ArgumentException(SR.Arg_VersionString, nameof(input)); + return null; + } + + // Find the ends of the optional minor and build portions. + // We musn't have any separators after build. + int buildEnd = -1; + int minorEnd = input.IndexOf('.', majorEnd + 1); + if (minorEnd != -1) + { + buildEnd = input.IndexOf('.', minorEnd + 1); + if (buildEnd != -1) + { + if (input.IndexOf('.', buildEnd + 1) != -1) + { + if (throwOnFailure) throw new ArgumentException(SR.Arg_VersionString, nameof(input)); + return null; + } + } + } + + int major, minor, build, revision; + + // Parse the major version + if (!TryParseComponent(input.Slice(0, majorEnd), nameof(input), throwOnFailure, out major)) + { + return null; + } + + if (minorEnd != -1) + { + // If there's more than a major and minor, parse the minor, too. + if (!TryParseComponent(input.Slice(majorEnd + 1, minorEnd - majorEnd - 1), nameof(input), throwOnFailure, out minor)) + { + return null; + } + + if (buildEnd != -1) + { + // major.minor.build.revision + return + TryParseComponent(input.Slice(minorEnd + 1, buildEnd - minorEnd - 1), nameof(build), throwOnFailure, out build) && + TryParseComponent(input.Slice(buildEnd + 1), nameof(revision), throwOnFailure, out revision) ? + new Version(major, minor, build, revision) : + null; + } + else + { + // major.minor.build + return TryParseComponent(input.Slice(minorEnd + 1), nameof(build), throwOnFailure, out build) ? + new Version(major, minor, build) : + null; + } + } + else + { + // major.minor + return TryParseComponent(input.Slice(majorEnd + 1), nameof(input), throwOnFailure, out minor) ? + new Version(major, minor) : + null; + } + } + + private static bool TryParseComponent(ReadOnlySpan component, string componentName, bool throwOnFailure, out int parsedComponent) + { + if (throwOnFailure) + { + if ((parsedComponent = int.Parse(component, NumberStyles.Integer, CultureInfo.InvariantCulture)) < 0) + { + throw new ArgumentOutOfRangeException(componentName, SR.ArgumentOutOfRange_Version); + } + return true; + } + + return int.TryParse(component, NumberStyles.Integer, CultureInfo.InvariantCulture, out parsedComponent) && parsedComponent >= 0; + } + + public static bool operator ==(Version v1, Version v2) + { + if (Object.ReferenceEquals(v1, null)) + { + return Object.ReferenceEquals(v2, null); + } + + return v1.Equals(v2); + } + + public static bool operator !=(Version v1, Version v2) + { + return !(v1 == v2); + } + + public static bool operator <(Version v1, Version v2) + { + if ((Object)v1 == null) + throw new ArgumentNullException(nameof(v1)); + return (v1.CompareTo(v2) < 0); + } + + public static bool operator <=(Version v1, Version v2) + { + if ((Object)v1 == null) + throw new ArgumentNullException(nameof(v1)); + return (v1.CompareTo(v2) <= 0); + } + + public static bool operator >(Version v1, Version v2) + { + return (v2 < v1); + } + + public static bool operator >=(Version v1, Version v2) + { + return (v2 <= v1); + } + } +} diff --git a/external/corefx/src/Common/src/CoreLib/System/Void.cs b/external/corefx/src/Common/src/CoreLib/System/Void.cs new file mode 100644 index 0000000000..5162e6ad02 --- /dev/null +++ b/external/corefx/src/Common/src/CoreLib/System/Void.cs @@ -0,0 +1,16 @@ +// 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. + +//////////////////////////////////////////////////////////////////////////////// +// Void +// This class represents the void return type +//////////////////////////////////////////////////////////////////////////////// + +namespace System +{ + // This class represents the void return type + public struct Void + { + } +} diff --git a/external/corefx/src/Common/src/Interop/BSD/System.Native/Interop.Sysctl.cs b/external/corefx/src/Common/src/Interop/BSD/System.Native/Interop.Sysctl.cs new file mode 100644 index 0000000000..d887b072bc --- /dev/null +++ b/external/corefx/src/Common/src/Interop/BSD/System.Native/Interop.Sysctl.cs @@ -0,0 +1,69 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; + +using size_t = System.IntPtr; + +// This implements shim for sysctl calls. +// They are available on BSD systems - FreeBSD, OSX and others. +// Linux has sysctl() but it is deprecated as well as it is missing sysctlbyname() + +internal static partial class Interop +{ + internal static partial class Sys + { + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Sysctl", SetLastError = true)] + private extern static unsafe int Sysctl(int* name, int namelen, void* value, size_t* len); + + // This is 'raw' sysctl call, only wrapped to allocate memory if needed + // caller always needs to free returned buffer using Marshal.FreeHGlobal() + + public static unsafe int Sysctl(Span name, ref byte* value, ref int len) + { + fixed (int * ptr = &MemoryMarshal.GetReference(name)) + { + return Sysctl(ptr, name.Length, ref value, ref len); + } + } + + public static unsafe int Sysctl(int* name, int name_len, ref byte* value, ref int len) + { + IntPtr bytesLength = (IntPtr)len; + byte * pBuffer = value; + value = null; + int ret=-1; + + if (value == null && len == 0) + { + // do one try to see how much data we need + ret = Sysctl(name, name_len, pBuffer, &bytesLength); + if (ret != 0) + { + throw new InvalidOperationException(string.Format("sysctl returned {0}", ret)); + } + pBuffer = (byte*)Marshal.AllocHGlobal((int)bytesLength); + } + ret = Sysctl(name, name_len, pBuffer, &bytesLength); + if (ret != 0) + { + if (value == null && len == 0) + { + // This is case we allocated memory for caller + Marshal.FreeHGlobal((IntPtr)pBuffer); + } + throw new InvalidOperationException(string.Format("sysctl returned {0}", ret)); + } + + value = pBuffer; + len = (int)bytesLength; + + return ret; + } + } +} diff --git a/external/corefx/src/Common/src/Interop/FreeBSD/Interop.Process.cs b/external/corefx/src/Common/src/Interop/FreeBSD/Interop.Process.cs new file mode 100644 index 0000000000..586fdf156a --- /dev/null +++ b/external/corefx/src/Common/src/Interop/FreeBSD/Interop.Process.cs @@ -0,0 +1,444 @@ +// 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.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Runtime.InteropServices; + + +internal static partial class Interop +{ + internal static partial class Process + { + private const ulong SecondsToNanoSeconds = 1000000000; + + // Constants from sys/syslimits.h + private const int PATH_MAX = 1024; + + // Constants from sys/user.h + private const int TDNAMLEN = 16; + private const int WMESGLEN = 8; + private const int LOGNAMELEN = 17; + private const int LOCKNAMELEN = 8; + private const int COMMLEN = 19; + private const int KI_EMULNAMELEN = 16; + private const int LOGINCLASSLEN = 17; + private const int KI_NGROUPS = 16; + + private const int KI_NSPARE_INT = 4; + private const int KI_NSPARE_LONG = 12; + private const int KI_NSPARE_PTR = 6; + + + // Constants from sys/_sigset.h + private const int _SIG_WORDS = 4; + + // Constants from sys/sysctl.h + private const int CTL_KERN = 1; + private const int KERN_PROC = 14; + private const int KERN_PROC_PATHNAME = 12; + private const int KERN_PROC_PROC = 8; + private const int KERN_PROC_ALL = 0; + private const int KERN_PROC_PID = 1; + private const int KERN_PROC_INC_THREAD = 16; + + // Constants from proc_info.h + private const int MAXTHREADNAMESIZE = 64; + private const int PROC_PIDTASKALLINFO = 2; + private const int PROC_PIDTHREADINFO = 5; + private const int PROC_PIDLISTTHREADS = 6; + private const int PROC_PIDPATHINFO_MAXSIZE = 4 * PATH_MAX; + + internal struct proc_stats + { + internal long startTime; /* time_t */ + internal int nice; + internal ulong userTime; /* in ticks */ + internal ulong systemTime; /* in ticks */ + } + + // From sys/_sigset.h + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct sigset_t + { + private fixed int bits[4]; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct uid_t + { + public uint id; + } + [StructLayout(LayoutKind.Sequential)] + internal struct gid_t + { + public uint id; + } + [StructLayout(LayoutKind.Sequential)] + public struct timeval + { + public IntPtr tv_sec; + public IntPtr tv_usec; + } + + [StructLayout(LayoutKind.Sequential)] + struct vnode + { + public long tv_sec; + public long tv_usec; + } + + // sys/resource.h + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct rusage + { + public timeval ru_utime; /* user time used */ + public timeval ru_stime; /* system time used */ + public long ru_maxrss; /* max resident set size */ + private long ru_ixrss; /* integral shared memory size */ + private long ru_idrss; /* integral unshared data " */ + private long ru_isrss; /* integral unshared stack " */ + private long ru_minflt; /* page reclaims */ + private long ru_majflt; /* page faults */ + private long ru_nswap; /* swaps */ + private long ru_inblock; /* block input operations */ + private long ru_oublock; /* block output operations */ + private long ru_msgsnd; /* messages sent */ + private long ru_msgrcv; /* messages received */ + private long ru_nsignals; /* signals received */ + private long ru_nvcsw; /* voluntary context switches */ + private long ru_nivcsw; /* involuntary " */ + } + + // From sys/user.h + [StructLayout(LayoutKind.Sequential)] + public unsafe struct kinfo_proc + { + public int ki_structsize; /* size of this structure */ + private int ki_layout; /* reserved: layout identifier */ + private void* ki_args; /* address of command arguments */ + private void* ki_paddr; /* address of proc */ + private void* ki_addr; /* kernel virtual addr of u-area */ + private vnode* ki_tracep; /* pointer to trace file */ + private vnode* ki_textvp; /* pointer to executable file */ + private void* ki_fd; /* pointer to open file info */ + private void* ki_vmspace; /* pointer to kernel vmspace struct */ + private void* ki_wchan; /* sleep address */ + public int ki_pid; /* Process identifier */ + public int ki_ppid; /* parent process id */ + private int ki_pgid; /* process group id */ + private int ki_tpgid; /* tty process group id */ + public int ki_sid; /* Process session ID */ + public int ki_tsid; /* Terminal session ID */ + private short ki_jobc; /* job control counter */ + private short ki_spare_short1; /* unused (just here for alignment) */ + private int ki_tdev; /* controlling tty dev */ + private sigset_t ki_siglist; /* Signals arrived but not delivered */ + private sigset_t ki_sigmask; /* Current signal mask */ + private sigset_t ki_sigignore; /* Signals being ignored */ + private sigset_t ki_sigcatch; /* Signals being caught by user */ + public uid_t ki_uid; /* effective user id */ + private uid_t ki_ruid; /* Real user id */ + private uid_t ki_svuid; /* Saved effective user id */ + private gid_t ki_rgid; /* Real group id */ + private gid_t ki_svgid; /* Saved effective group id */ + private short ki_ngroups; /* number of groups */ + private short ki_spare_short2; /* unused (just here for alignment) */ + private fixed uint ki_groups[KI_NGROUPS]; /* groups */ + public ulong ki_size; /* virtual size */ + public long ki_rssize; /* current resident set size in pages */ + private long ki_swrss; /* resident set size before last swap */ + private long ki_tsize; /* text size (pages) XXX */ + private long ki_dsize; /* data size (pages) XXX */ + private long ki_ssize; /* stack size (pages) */ + private ushort ki_xstat; /* Exit status for wait & stop signal */ + private ushort ki_acflag; /* Accounting flags */ + private uint ki_pctcpu; /* %cpu for process during ki_swtime */ + private uint ki_estcpu; /* Time averaged value of ki_cpticks */ + private uint ki_slptime; /* Time since last blocked */ + private uint ki_swtime; /* Time swapped in or out */ + private uint ki_cow; /* number of copy-on-write faults */ + private ulong ki_runtime; /* Real time in microsec */ + public timeval ki_start; /* starting time */ + private timeval ki_childtime; /* time used by process children */ + private long ki_flag; /* P_* flags */ + private long ki_kiflag; /* KI_* flags (below) */ + private int ki_traceflag; /* Kernel trace points */ + private byte ki_stat; /* S* process status */ + public byte ki_nice; /* Process "nice" value */ + private byte ki_lock; /* Process lock (prevent swap) count */ + private byte ki_rqindex; /* Run queue index */ + private byte ki_oncpu_old; /* Which cpu we are on (legacy) */ + private byte ki_lastcpu_old; /* Last cpu we were on (legacy) */ + public fixed byte ki_tdname[TDNAMLEN+1]; /* thread name */ + private fixed byte ki_wmesg[WMESGLEN+1]; /* wchan message */ + private fixed byte ki_login[LOGNAMELEN+1]; /* setlogin name */ + private fixed byte ki_lockname[LOCKNAMELEN+1]; /* lock name */ + public fixed byte ki_comm[COMMLEN+1]; /* command name */ + private fixed byte ki_emul[KI_EMULNAMELEN+1]; /* emulation name */ + private fixed byte ki_loginclass[LOGINCLASSLEN+1]; /* login class */ + private fixed byte ki_sparestrings[50]; /* spare string space */ + private fixed int ki_spareints[KI_NSPARE_INT]; /* spare room for growth */ + private int ki_oncpu; /* Which cpu we are on */ + private int ki_lastcpu; /* Last cpu we were on */ + private int ki_tracer; /* Pid of tracing process */ + private int ki_flag2; /* P2_* flags */ + private int ki_fibnum; /* Default FIB number */ + private uint ki_cr_flags; /* Credential flags */ + private int ki_jid; /* Process jail ID */ + public int ki_numthreads; /* XXXKSE number of threads in total */ + public int ki_tid; /* XXXKSE thread id */ + private fixed byte ki_pri[4]; /* process priority */ + public rusage ki_rusage; /* process rusage statistics */ + /* XXX - most fields in ki_rusage_ch are not (yet) filled in */ + private rusage ki_rusage_ch; /* rusage of children processes */ + private void* ki_pcb; /* kernel virtual addr of pcb */ + private void* ki_kstack; /* kernel virtual addr of stack */ + private void* ki_udata; /* User convenience pointer */ + public void* ki_tdaddr; /* address of thread */ + + private fixed long ki_spareptrs[KI_NSPARE_PTR]; /* spare room for growth */ + private fixed long ki_sparelongs[KI_NSPARE_LONG]; /* spare room for growth */ + private long ki_sflag; /* PS_* flags */ + private long ki_tdflags; /* XXXKSE kthread flag */ + } + + /// + /// Queries the OS for the list of all running processes and returns the PID for each + /// + /// Returns a list of PIDs corresponding to all running processes + internal static unsafe int[] ListAllPids() + { + int numProcesses = 0; + int[] pids; + kinfo_proc * entries = null; + int idx; + + try + { + entries = GetProcInfo(0, false, out numProcesses); + if (entries == null || numProcesses <= 0) + { + throw new Win32Exception(SR.CantGetAllPids); + } + + Span list = new Span(entries, numProcesses); + pids = new int[numProcesses]; + idx = 0; + // walk through process list and skip kernel threads + for (int i = 0; i < list.Length; i++) + { + if (list[i].ki_ppid == 0) + { + // skip kernel threads + numProcesses-=1; + } + else + { + pids[idx] = list[i].ki_pid; + idx += 1; + } + } + // Remove extra elements + Array.Resize(ref pids, numProcesses); + } + finally + { + Marshal.FreeHGlobal((IntPtr)entries); + } + return pids; + } + + + /// + /// Gets executable name for process given it's PID + /// + /// The PID of the process + public static unsafe string GetProcPath(int pid) + { + Span sysctlName = stackalloc int[4]; + byte* pBuffer = null; + int bytesLength = 0; + + sysctlName[0] = CTL_KERN; + sysctlName[1] = KERN_PROC; + sysctlName[2] = KERN_PROC_PATHNAME; + sysctlName[3] = pid; + + int ret = Interop.Sys.Sysctl(sysctlName, ref pBuffer, ref bytesLength); + if (ret != 0 ) { + return null; + } + return System.Text.Encoding.UTF8.GetString(pBuffer,(int)bytesLength-1); + } + + /// + /// Gets information about process or thread(s) + /// + /// The PID of the process. If PID is 0, this will return all processes + public static unsafe kinfo_proc* GetProcInfo(int pid, bool threads, out int count) + { + Span sysctlName = stackalloc int[4]; + int bytesLength = 0; + byte* pBuffer = null; + kinfo_proc* kinfo = null; + int ret; + + count = -1; + + if (pid == 0) + { + // get all processes + sysctlName[3] = 0; + sysctlName[2] = KERN_PROC_PROC; + } + else + { + // get specific process, possibly with threads + sysctlName[3] = pid; + sysctlName[2] = KERN_PROC_PID | (threads ? KERN_PROC_INC_THREAD : 0); + } + sysctlName[1] = KERN_PROC; + sysctlName[0] = CTL_KERN; + + try + { + ret = Interop.Sys.Sysctl(sysctlName, ref pBuffer, ref bytesLength); + if (ret != 0 ) { + throw new ArgumentOutOfRangeException(nameof(pid)); + } + + kinfo = (kinfo_proc*)pBuffer; + if (kinfo->ki_structsize != sizeof(kinfo_proc)) + { + // failed consistency check + throw new ArgumentOutOfRangeException(nameof(pid)); + } + + count = (int)bytesLength / sizeof(kinfo_proc); + } + catch + { + Marshal.FreeHGlobal((IntPtr)pBuffer); + throw; + } + + return kinfo; + } + + /// + /// Gets the process information for a given process + /// + /// The PID (process ID) of the process + /// + /// Returns a valid ProcessInfo struct for valid processes that the caller + /// has permission to access; otherwise, returns null + /// + static public unsafe ProcessInfo GetProcessInfoById(int pid) + { + kinfo_proc* kinfo = null; + int count; + ProcessInfo info; + + // Negative PIDs are invalid + if (pid < 0) + { + throw new ArgumentOutOfRangeException(nameof(pid)); + } + + try + { + kinfo = GetProcInfo(pid, true, out count); + if (kinfo == null || count < 1) + { + throw new ArgumentOutOfRangeException(nameof(pid)); + } + + Span process = new Span(kinfo, count); + + // Get the process information for the specified pid + info = new ProcessInfo(); + + info.ProcessName = Marshal.PtrToStringAnsi((IntPtr)kinfo->ki_comm); + info.BasePriority = kinfo->ki_nice; + info.VirtualBytes = (long)kinfo->ki_size; + info.WorkingSet = kinfo->ki_rssize; + info.SessionId = kinfo ->ki_sid; + + for(int i = 0; i < process.Length; i++) + { + var ti = new ThreadInfo() + { + _processId = pid, + _threadId = (ulong)process[i].ki_tid, + _basePriority = process[i].ki_nice, + _startAddress = (IntPtr)process[i].ki_tdaddr + }; + info._threadInfoList.Add(ti); + } + } + finally + { + Marshal.FreeHGlobal((IntPtr)kinfo); + } + + return info; + } + + /// + /// Gets the process information for a given process + /// + // + /// The PID (process ID) of the process + /// The TID (thread ID) of the process + /// + /// Returns basic info about thread. If tis is 0, it will return + /// info for process e.g. main thread. + /// + public unsafe static proc_stats GetThreadInfo(int pid, int tid) + { + proc_stats ret = new proc_stats(); + kinfo_proc* info = null; + int count; + + try + { + info = GetProcInfo(pid, (tid != 0), out count); + if (info != null && count >= 1) + { + if (tid == 0) + { + ret.startTime = (int)info->ki_start.tv_sec; + ret.nice = info->ki_nice; + ret.userTime = (ulong)info->ki_rusage.ru_utime.tv_sec * SecondsToNanoSeconds + (ulong)info->ki_rusage.ru_utime.tv_usec; + ret.systemTime = (ulong)info->ki_rusage.ru_stime.tv_sec * SecondsToNanoSeconds + (ulong)info->ki_rusage.ru_stime.tv_usec; + } + else + { + Span list = new Span(info, count); + for(int i = 0; i < list.Length; i++) + { + if (list[i].ki_tid == tid) + { + ret.startTime = (int)list[i].ki_start.tv_sec; + ret.nice = list[i].ki_nice; + ret.userTime = (ulong)list[i].ki_rusage.ru_utime.tv_sec * SecondsToNanoSeconds + (ulong)list[i].ki_rusage.ru_utime.tv_usec; + ret.systemTime = (ulong)list[i].ki_rusage.ru_stime.tv_sec * SecondsToNanoSeconds + (ulong)list[i].ki_rusage.ru_stime.tv_usec; + break; + } + } + } + } + } + finally + { + Marshal.FreeHGlobal((IntPtr)info); + } + + return ret; + } + } +} diff --git a/external/corefx/src/System.Data.Odbc/src/Common/System/Data/Common/UnsafeNativeMethods.cs b/external/corefx/src/Common/src/Interop/Interop.Odbc.cs similarity index 77% rename from external/corefx/src/System.Data.Odbc/src/Common/System/Data/Common/UnsafeNativeMethods.cs rename to external/corefx/src/Common/src/Interop/Interop.Odbc.cs index 77d55b02b5..913ff04eea 100644 --- a/external/corefx/src/System.Data.Odbc/src/Common/System/Data/Common/UnsafeNativeMethods.cs +++ b/external/corefx/src/Common/src/Interop/Interop.Odbc.cs @@ -1,7 +1,8 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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.Data.Odbc; using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; @@ -9,34 +10,27 @@ using System.Runtime.Versioning; using System.Security; using System.Text; -namespace System.Data.Common +internal static partial class Interop { - [SuppressUnmanagedCodeSecurity] - internal static partial class UnsafeNativeMethods + internal static partial class Odbc { // // ODBC32 // - [DllImport(ExternDll.Odbc32)] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLAllocHandle( /*SQLSMALLINT*/ODBC32.SQL_HANDLE HandleType, /*SQLHANDLE*/IntPtr InputHandle, /*SQLHANDLE* */out IntPtr OutputHandle); - [DllImport(ExternDll.Odbc32)] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLAllocHandle( /*SQLSMALLINT*/ODBC32.SQL_HANDLE HandleType, /*SQLHANDLE*/OdbcHandle InputHandle, /*SQLHANDLE* */out IntPtr OutputHandle); - - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLBindCol( /*SQLHSTMT*/OdbcStatementHandle StatementHandle, /*SQLUSMALLINT*/UInt16 ColumnNumber, @@ -45,8 +39,7 @@ namespace System.Data.Common /*SQLLEN*/IntPtr BufferLength, /*SQLLEN* */IntPtr StrLen_or_Ind); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLBindCol( /*SQLHSTMT*/OdbcStatementHandle StatementHandle, /*SQLUSMALLINT*/UInt16 ColumnNumber, @@ -55,8 +48,7 @@ namespace System.Data.Common /*SQLLEN*/IntPtr BufferLength, /*SQLLEN* */IntPtr StrLen_or_Ind); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLBindParameter( /*SQLHSTMT*/OdbcStatementHandle StatementHandle, /*SQLUSMALLINT*/UInt16 ParameterNumber, @@ -69,18 +61,15 @@ namespace System.Data.Common /*SQLLEN*/IntPtr BufferLength, /*SQLLEN* */HandleRef StrLen_or_Ind); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLCancel( /*SQLHSTMT*/OdbcStatementHandle StatementHandle); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLCloseCursor( /*SQLHSTMT*/OdbcStatementHandle StatementHandle); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLColAttributeW( /*SQLHSTMT*/OdbcStatementHandle StatementHandle, /*SQLUSMALLINT*/Int16 ColumnNumber, @@ -105,9 +94,7 @@ namespace System.Data.Common // SQLSMALLINT *StringLength, SQLPOINTER NumericAttribute); // #endif - - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLColumnsW( /*SQLHSTMT*/OdbcStatementHandle StatementHandle, [In, MarshalAs(UnmanagedType.LPWStr)] @@ -123,15 +110,11 @@ namespace System.Data.Common /*SQLCHAR* */string ColumnName, /*SQLSMALLINT*/Int16 NameLen4); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLDisconnect( /*SQLHDBC*/IntPtr ConnectionHandle); - [DllImport(ExternDll.Odbc32, CharSet = CharSet.Unicode)] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32, CharSet = CharSet.Unicode)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLDriverConnectW( /*SQLHDBC*/OdbcConnectionHandle hdbc, /*SQLHWND*/IntPtr hwnd, @@ -143,47 +126,38 @@ namespace System.Data.Common /*SQLSMALLINT* */out Int16 cbConnectionstringout, /*SQLUSMALLINT*/Int16 fDriverCompletion); - [DllImport(ExternDll.Odbc32)] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLEndTran( /*SQLSMALLINT*/ODBC32.SQL_HANDLE HandleType, /*SQLHANDLE*/IntPtr Handle, /*SQLSMALLINT*/Int16 CompletionType); - [DllImport(ExternDll.Odbc32, CharSet = CharSet.Unicode)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32, CharSet = CharSet.Unicode)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLExecDirectW( /*SQLHSTMT*/OdbcStatementHandle StatementHandle, [In, MarshalAs(UnmanagedType.LPWStr)] /*SQLCHAR* */string StatementText, /*SQLINTEGER*/Int32 TextLength); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLExecute( /*SQLHSTMT*/OdbcStatementHandle StatementHandle); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLFetch( /*SQLHSTMT*/OdbcStatementHandle StatementHandle); - [DllImport(ExternDll.Odbc32)] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLFreeHandle( /*SQLSMALLINT*/ODBC32.SQL_HANDLE HandleType, /*SQLHSTMT*/IntPtr StatementHandle); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLFreeStmt( /*SQLHSTMT*/OdbcStatementHandle StatementHandle, /*SQLUSMALLINT*/ODBC32.STMT Option); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLGetConnectAttrW( /*SQLHBDC*/OdbcConnectionHandle ConnectionHandle, /*SQLINTEGER*/ODBC32.SQL_ATTR Attribute, @@ -191,8 +165,7 @@ namespace System.Data.Common /*SQLINTEGER*/Int32 BufferLength, /*SQLINTEGER* */out Int32 StringLength); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLGetData( /*SQLHSTMT*/OdbcStatementHandle StatementHandle, /*SQLUSMALLINT*/UInt16 ColumnNumber, @@ -201,8 +174,7 @@ namespace System.Data.Common /*SQLLEN*/IntPtr BufferLength, // sql.h differs from MSDN /*SQLLEN* */out IntPtr StrLen_or_Ind); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLGetDescFieldW( /*SQLHSTMT*/OdbcDescriptorHandle StatementHandle, /*SQLUSMALLINT*/Int16 RecNumber, @@ -211,8 +183,7 @@ namespace System.Data.Common /*SQLINTEGER*/Int32 BufferLength, /*SQLINTEGER* */out Int32 StringLength); - [DllImport(ExternDll.Odbc32, CharSet = CharSet.Unicode)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32, CharSet = CharSet.Unicode)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLGetDiagRecW( /*SQLSMALLINT*/ODBC32.SQL_HANDLE HandleType, /*SQLHANDLE*/OdbcHandle Handle, @@ -223,8 +194,7 @@ namespace System.Data.Common /*SQLSMALLINT*/Int16 BufferLength, /*SQLSMALLINT* */out Int16 TextLength); - [DllImport(ExternDll.Odbc32, CharSet = CharSet.Unicode)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32, CharSet = CharSet.Unicode)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLGetDiagFieldW( /*SQLSMALLINT*/ ODBC32.SQL_HANDLE HandleType, /*SQLHANDLE*/ OdbcHandle Handle, @@ -235,15 +205,13 @@ namespace System.Data.Common /*SQLSMALLINT*/ Int16 BufferLength, /*SQLSMALLINT* */ out Int16 StringLength); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLGetFunctions( /*SQLHBDC*/OdbcConnectionHandle hdbc, /*SQLUSMALLINT*/ODBC32.SQL_API fFunction, /*SQLUSMALLINT* */out Int16 pfExists); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLGetInfoW( /*SQLHBDC*/OdbcConnectionHandle hdbc, /*SQLUSMALLINT*/ODBC32.SQL_INFO fInfoType, @@ -251,8 +219,7 @@ namespace System.Data.Common /*SQLSMALLINT*/Int16 cbInfoValueMax, /*SQLSMALLINT* */out Int16 pcbInfoValue); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLGetInfoW( /*SQLHBDC*/OdbcConnectionHandle hdbc, /*SQLUSMALLINT*/ODBC32.SQL_INFO fInfoType, @@ -260,8 +227,7 @@ namespace System.Data.Common /*SQLSMALLINT*/Int16 cbInfoValueMax, /*SQLSMALLINT* */IntPtr pcbInfoValue); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLGetStmtAttrW( /*SQLHSTMT*/OdbcStatementHandle StatementHandle, /*SQLINTEGER*/ODBC32.SQL_ATTR Attribute, @@ -269,33 +235,28 @@ namespace System.Data.Common /*SQLINTEGER*/Int32 BufferLength, /*SQLINTEGER*/out Int32 StringLength); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLGetTypeInfo( /*SQLHSTMT*/OdbcStatementHandle StatementHandle, /*SQLSMALLINT*/Int16 fSqlType); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLMoreResults( /*SQLHSTMT*/OdbcStatementHandle StatementHandle); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLNumResultCols( /*SQLHSTMT*/OdbcStatementHandle StatementHandle, /*SQLSMALLINT* */out Int16 ColumnCount); - [DllImport(ExternDll.Odbc32, CharSet = CharSet.Unicode)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32, CharSet = CharSet.Unicode)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLPrepareW( /*SQLHSTMT*/OdbcStatementHandle StatementHandle, [In, MarshalAs(UnmanagedType.LPWStr)] /*SQLCHAR* */string StatementText, /*SQLINTEGER*/Int32 TextLength); - [DllImport(ExternDll.Odbc32, CharSet = CharSet.Unicode)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32, CharSet = CharSet.Unicode)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLPrimaryKeysW( /*SQLHSTMT*/OdbcStatementHandle StatementHandle, [In, MarshalAs(UnmanagedType.LPWStr)] @@ -308,8 +269,7 @@ namespace System.Data.Common /*SQLCHAR* */string TableName, /*SQLSMALLINT*/Int16 NameLen3); - [DllImport(ExternDll.Odbc32, CharSet = CharSet.Unicode)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32, CharSet = CharSet.Unicode)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLProcedureColumnsW( /*SQLHSTMT*/OdbcStatementHandle StatementHandle, [In, MarshalAs(UnmanagedType.LPWStr)] /*SQLCHAR* */ string CatalogName, @@ -321,8 +281,7 @@ namespace System.Data.Common [In, MarshalAs(UnmanagedType.LPWStr)] /*SQLCHAR* */ string ColumnName, /*SQLSMALLINT*/Int16 NameLen4); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLProceduresW( /*SQLHSTMT*/OdbcStatementHandle StatementHandle, [In, MarshalAs(UnmanagedType.LPWStr)] /*SQLCHAR* */ string CatalogName, @@ -332,47 +291,40 @@ namespace System.Data.Common [In, MarshalAs(UnmanagedType.LPWStr)] /*SQLCHAR* */ string ProcName, /*SQLSMALLINT*/Int16 NameLen3); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLRowCount( /*SQLHSTMT*/OdbcStatementHandle StatementHandle, /*SQLLEN* */out IntPtr RowCount); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLSetConnectAttrW( /*SQLHBDC*/OdbcConnectionHandle ConnectionHandle, /*SQLINTEGER*/ODBC32.SQL_ATTR Attribute, /*SQLPOINTER*/System.Transactions.IDtcTransaction Value, /*SQLINTEGER*/Int32 StringLength); - [DllImport(ExternDll.Odbc32, CharSet = CharSet.Unicode)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32, CharSet = CharSet.Unicode)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLSetConnectAttrW( /*SQLHBDC*/OdbcConnectionHandle ConnectionHandle, /*SQLINTEGER*/ODBC32.SQL_ATTR Attribute, /*SQLPOINTER*/string Value, /*SQLINTEGER*/Int32 StringLength); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLSetConnectAttrW( /*SQLHBDC*/OdbcConnectionHandle ConnectionHandle, /*SQLINTEGER*/ODBC32.SQL_ATTR Attribute, /*SQLPOINTER*/IntPtr Value, /*SQLINTEGER*/Int32 StringLength); - [DllImport(ExternDll.Odbc32)] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLSetConnectAttrW( // used only for AutoCommitOn /*SQLHBDC*/IntPtr ConnectionHandle, /*SQLINTEGER*/ODBC32.SQL_ATTR Attribute, /*SQLPOINTER*/IntPtr Value, /*SQLINTEGER*/Int32 StringLength); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLSetDescFieldW( /*SQLHSTMT*/OdbcDescriptorHandle StatementHandle, /*SQLSMALLINT*/Int16 ColumnNumber, @@ -380,8 +332,7 @@ namespace System.Data.Common /*SQLPOINTER*/HandleRef CharacterAttribute, /*SQLINTEGER*/Int32 BufferLength); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLSetDescFieldW( /*SQLHSTMT*/OdbcDescriptorHandle StatementHandle, /*SQLSMALLINT*/Int16 ColumnNumber, @@ -389,25 +340,22 @@ namespace System.Data.Common /*SQLPOINTER*/IntPtr CharacterAttribute, /*SQLINTEGER*/Int32 BufferLength); - [DllImport(ExternDll.Odbc32)] + [DllImport(Interop.Libraries.Odbc32)] // user can set SQL_ATTR_CONNECTION_POOLING attribute with envHandle = null, this attribute is process-level attribute - [ResourceExposure(ResourceScope.Process)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLSetEnvAttr( /*SQLHENV*/OdbcEnvironmentHandle EnvironmentHandle, /*SQLINTEGER*/ODBC32.SQL_ATTR Attribute, /*SQLPOINTER*/IntPtr Value, /*SQLINTEGER*/ODBC32.SQL_IS StringLength); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLSetStmtAttrW( /*SQLHSTMT*/OdbcStatementHandle StatementHandle, /*SQLINTEGER*/Int32 Attribute, /*SQLPOINTER*/IntPtr Value, /*SQLINTEGER*/Int32 StringLength); - [DllImport(ExternDll.Odbc32, CharSet = CharSet.Unicode)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32, CharSet = CharSet.Unicode)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLSpecialColumnsW( /*SQLHSTMT*/OdbcStatementHandle StatementHandle, /*SQLUSMALLINT*/ODBC32.SQL_SPECIALCOLS IdentifierType, @@ -423,8 +371,7 @@ namespace System.Data.Common /*SQLUSMALLINT*/ODBC32.SQL_SCOPE Scope, /*SQLUSMALLINT*/ ODBC32.SQL_NULLABILITY Nullable); - [DllImport(ExternDll.Odbc32, CharSet = CharSet.Unicode)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32, CharSet = CharSet.Unicode)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLStatisticsW( /*SQLHSTMT*/OdbcStatementHandle StatementHandle, [In, MarshalAs(UnmanagedType.LPWStr)] @@ -439,8 +386,7 @@ namespace System.Data.Common /*SQLUSMALLINT*/Int16 Unique, /*SQLUSMALLINT*/Int16 Reserved); - [DllImport(ExternDll.Odbc32)] - [ResourceExposure(ResourceScope.None)] + [DllImport(Interop.Libraries.Odbc32)] internal static extern /*SQLRETURN*/ODBC32.RetCode SQLTablesW( /*SQLHSTMT*/OdbcStatementHandle StatementHandle, [In, MarshalAs(UnmanagedType.LPWStr)] @@ -456,4 +402,4 @@ namespace System.Data.Common /*SQLCHAR* */string TableType, /*SQLSMALLINT*/Int16 NameLen4); } -} \ No newline at end of file +} diff --git a/external/corefx/src/Common/src/Interop/Linux/Interop.Libraries.cs b/external/corefx/src/Common/src/Interop/Linux/Interop.Libraries.cs new file mode 100644 index 0000000000..1cec655872 --- /dev/null +++ b/external/corefx/src/Common/src/Interop/Linux/Interop.Libraries.cs @@ -0,0 +1,11 @@ +// 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. + +internal static partial class Interop +{ + internal static partial class Libraries + { + internal const string Odbc32 = "libodbc.so.2"; + } +} diff --git a/external/corefx/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFData.cs b/external/corefx/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFData.cs index 0e736a4d0a..5920e32038 100644 --- a/external/corefx/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFData.cs +++ b/external/corefx/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFData.cs @@ -62,7 +62,7 @@ internal static partial class Interop } byte* dataBytes = CFDataGetBytePtr(cfData); - fixed (byte* destinationPtr = &destination.DangerousGetPinnableReference()) + fixed (byte* destinationPtr = &MemoryMarshal.GetReference(destination)) { Buffer.MemoryCopy(dataBytes, destinationPtr, destination.Length, length); } diff --git a/external/corefx/src/Common/src/Interop/OSX/Interop.Libraries.cs b/external/corefx/src/Common/src/Interop/OSX/Interop.Libraries.cs index 2a79e57a6c..89f6a3f537 100644 --- a/external/corefx/src/Common/src/Interop/OSX/Interop.Libraries.cs +++ b/external/corefx/src/Common/src/Interop/OSX/Interop.Libraries.cs @@ -4,13 +4,14 @@ internal static partial class Interop { - private static partial class Libraries + internal static partial class Libraries { internal const string CoreFoundationLibrary = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"; internal const string CoreServicesLibrary = "/System/Library/Frameworks/CoreServices.framework/CoreServices"; internal const string libproc = "libproc"; internal const string LibSystemCommonCrypto = "/usr/lib/system/libcommonCrypto"; internal const string LibSystemKernel = "/usr/lib/system/libsystem_kernel"; + internal const string Odbc32 = "libodbc.2.dylib"; internal const string SystemConfigurationLibrary = "/System/Library/Frameworks/SystemConfiguration.framework/SystemConfiguration"; internal const string AppleCryptoNative = "System.Security.Cryptography.Native.Apple"; } diff --git a/external/corefx/src/Common/src/Interop/OSX/System.Native/Interop.ProtocolStatistics.cs b/external/corefx/src/Common/src/Interop/OSX/System.Native/Interop.ProtocolStatistics.cs index b1b9b6d873..8e1bf08deb 100644 --- a/external/corefx/src/Common/src/Interop/OSX/System.Native/Interop.ProtocolStatistics.cs +++ b/external/corefx/src/Common/src/Interop/OSX/System.Native/Interop.ProtocolStatistics.cs @@ -9,7 +9,7 @@ internal static partial class Interop internal static partial class Sys { [StructLayoutAttribute(LayoutKind.Sequential)] - public unsafe struct TcpGlobalStatistics + public unsafe readonly struct TcpGlobalStatistics { public readonly ulong ConnectionsAccepted; public readonly ulong ConnectionsInitiated; @@ -27,7 +27,7 @@ internal static partial class Interop public static extern unsafe int GetTcpGlobalStatistics(out TcpGlobalStatistics statistics); [StructLayoutAttribute(LayoutKind.Sequential)] - public unsafe struct IPv4GlobalStatistics + public unsafe readonly struct IPv4GlobalStatistics { public readonly ulong OutboundPackets; public readonly ulong OutputPacketsNoRoute; @@ -49,7 +49,7 @@ internal static partial class Interop public static extern unsafe int GetIPv4GlobalStatistics(out IPv4GlobalStatistics statistics); [StructLayoutAttribute(LayoutKind.Sequential)] - public unsafe struct UdpGlobalStatistics + public unsafe readonly struct UdpGlobalStatistics { public readonly ulong DatagramsReceived; public readonly ulong DatagramsSent; @@ -62,7 +62,7 @@ internal static partial class Interop public static extern unsafe int GetUdpGlobalStatistics(out UdpGlobalStatistics statistics); [StructLayoutAttribute(LayoutKind.Sequential)] - public unsafe struct Icmpv4GlobalStatistics + public unsafe readonly struct Icmpv4GlobalStatistics { public readonly ulong AddressMaskRepliesReceived; public readonly ulong AddressMaskRepliesSent; @@ -92,7 +92,7 @@ internal static partial class Interop public static extern unsafe int GetIcmpv4GlobalStatistics(out Icmpv4GlobalStatistics statistics); [StructLayoutAttribute(LayoutKind.Sequential)] - public unsafe struct Icmpv6GlobalStatistics + public unsafe readonly struct Icmpv6GlobalStatistics { public readonly ulong DestinationUnreachableMessagesReceived; public readonly ulong DestinationUnreachableMessagesSent; @@ -127,7 +127,7 @@ internal static partial class Interop [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetIcmpv6GlobalStatistics")] public static extern unsafe int GetIcmpv6GlobalStatistics(out Icmpv6GlobalStatistics statistics); - public struct NativeIPInterfaceStatistics + public readonly struct NativeIPInterfaceStatistics { public readonly ulong SendQueueLength; public readonly ulong Mtu; diff --git a/external/corefx/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Digest.cs b/external/corefx/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Digest.cs index 8d8b13aeb3..9135ee7e62 100644 --- a/external/corefx/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Digest.cs +++ b/external/corefx/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Digest.cs @@ -17,13 +17,13 @@ internal static partial class Interop internal static extern SafeDigestCtxHandle DigestCreate(PAL_HashAlgorithm algorithm, out int cbDigest); internal static int DigestUpdate(SafeDigestCtxHandle ctx, ReadOnlySpan pbData, int cbData) => - DigestUpdate(ctx, ref pbData.DangerousGetPinnableReference(), cbData); + DigestUpdate(ctx, ref MemoryMarshal.GetReference(pbData), cbData); [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_DigestUpdate")] private static extern int DigestUpdate(SafeDigestCtxHandle ctx, ref byte pbData, int cbData); internal static int DigestFinal(SafeDigestCtxHandle ctx, Span pbOutput, int cbOutput) => - DigestFinal(ctx, ref pbOutput.DangerousGetPinnableReference(), cbOutput); + DigestFinal(ctx, ref MemoryMarshal.GetReference(pbOutput), cbOutput); [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_DigestFinal")] private static extern int DigestFinal(SafeDigestCtxHandle ctx, ref byte pbOutput, int cbOutput); diff --git a/external/corefx/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Hmac.cs b/external/corefx/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Hmac.cs index 6dc7d97d71..1120aca177 100644 --- a/external/corefx/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Hmac.cs +++ b/external/corefx/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Hmac.cs @@ -20,13 +20,13 @@ internal static partial class Interop internal static extern unsafe int HmacInit(SafeHmacHandle ctx, [In] byte[] pbKey, int cbKey); internal static int HmacUpdate(SafeHmacHandle ctx, ReadOnlySpan pbData, int cbData) => - HmacUpdate(ctx, ref pbData.DangerousGetPinnableReference(), cbData); + HmacUpdate(ctx, ref MemoryMarshal.GetReference(pbData), cbData); [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_HmacUpdate")] private static extern int HmacUpdate(SafeHmacHandle ctx, ref byte pbData, int cbData); internal static int HmacFinal(SafeHmacHandle ctx, ReadOnlySpan pbOutput, int cbOutput) => - HmacFinal(ctx, ref pbOutput.DangerousGetPinnableReference(), cbOutput); + HmacFinal(ctx, ref MemoryMarshal.GetReference(pbOutput), cbOutput); [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_HmacFinal")] private static extern unsafe int HmacFinal(SafeHmacHandle ctx, ref byte pbOutput, int cbOutput); diff --git a/external/corefx/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.RSA.cs b/external/corefx/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.RSA.cs index 06d8685909..1c99e6c5e6 100644 --- a/external/corefx/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.RSA.cs +++ b/external/corefx/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.RSA.cs @@ -28,7 +28,7 @@ internal static partial class Interop PAL_HashAlgorithm mgfAlgorithm, out SafeCFDataHandle pEncryptedOut, out SafeCFErrorHandle pErrorOut) => - RsaEncryptOaep(publicKey, ref pbData.DangerousGetPinnableReference(), cbData, mgfAlgorithm, out pEncryptedOut, out pErrorOut); + RsaEncryptOaep(publicKey, ref MemoryMarshal.GetReference(pbData), cbData, mgfAlgorithm, out pEncryptedOut, out pErrorOut); [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_RsaEncryptOaep")] private static extern int RsaEncryptOaep( @@ -45,7 +45,7 @@ internal static partial class Interop int cbData, out SafeCFDataHandle pEncryptedOut, out SafeCFErrorHandle pErrorOut) => - RsaEncryptPkcs(publicKey, ref pbData.DangerousGetPinnableReference(), cbData, out pEncryptedOut, out pErrorOut); + RsaEncryptPkcs(publicKey, ref MemoryMarshal.GetReference(pbData), cbData, out pEncryptedOut, out pErrorOut); [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_RsaEncryptPkcs")] private static extern int RsaEncryptPkcs( @@ -62,7 +62,7 @@ internal static partial class Interop PAL_HashAlgorithm mgfAlgorithm, out SafeCFDataHandle pEncryptedOut, out SafeCFErrorHandle pErrorOut) => - RsaDecryptOaep(publicKey, ref pbData.DangerousGetPinnableReference(), cbData, mgfAlgorithm, out pEncryptedOut, out pErrorOut); + RsaDecryptOaep(publicKey, ref MemoryMarshal.GetReference(pbData), cbData, mgfAlgorithm, out pEncryptedOut, out pErrorOut); [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_RsaDecryptOaep")] private static extern int RsaDecryptOaep( @@ -79,7 +79,7 @@ internal static partial class Interop int cbData, out SafeCFDataHandle pEncryptedOut, out SafeCFErrorHandle pErrorOut) => - RsaDecryptPkcs(publicKey, ref pbData.DangerousGetPinnableReference(), cbData, out pEncryptedOut, out pErrorOut); + RsaDecryptPkcs(publicKey, ref MemoryMarshal.GetReference(pbData), cbData, out pEncryptedOut, out pErrorOut); [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_RsaDecryptPkcs")] private static extern int RsaDecryptPkcs( diff --git a/external/corefx/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SecKeyRef.cs b/external/corefx/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SecKeyRef.cs index 83fa6433ca..37dd66d5ac 100644 --- a/external/corefx/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SecKeyRef.cs +++ b/external/corefx/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SecKeyRef.cs @@ -28,7 +28,7 @@ internal static partial class Interop out SafeCFDataHandle pSignatureOut, out SafeCFErrorHandle pErrorOut) => AppleCryptoNative_GenerateSignature( - privateKey, ref pbDataHash.DangerousGetPinnableReference(), cbDataHash, out pSignatureOut, out pErrorOut); + privateKey, ref MemoryMarshal.GetReference(pbDataHash), cbDataHash, out pSignatureOut, out pErrorOut); [DllImport(Libraries.AppleCryptoNative)] private static extern int AppleCryptoNative_GenerateSignature( @@ -46,7 +46,7 @@ internal static partial class Interop out SafeCFDataHandle pSignatureOut, out SafeCFErrorHandle pErrorOut) => AppleCryptoNative_GenerateSignatureWithHashAlgorithm( - privateKey, ref pbDataHash.DangerousGetPinnableReference(), cbDataHash, hashAlgorithm, out pSignatureOut, out pErrorOut); + privateKey, ref MemoryMarshal.GetReference(pbDataHash), cbDataHash, hashAlgorithm, out pSignatureOut, out pErrorOut); [DllImport(Libraries.AppleCryptoNative)] private static extern int AppleCryptoNative_GenerateSignatureWithHashAlgorithm( @@ -66,9 +66,9 @@ internal static partial class Interop out SafeCFErrorHandle pErrorOut) => AppleCryptoNative_VerifySignature( publicKey, - ref pbDataHash.DangerousGetPinnableReference(), + ref MemoryMarshal.GetReference(pbDataHash), cbDataHash, - ref pbSignature.DangerousGetPinnableReference(), + ref MemoryMarshal.GetReference(pbSignature), cbSignature, out pErrorOut); @@ -91,9 +91,9 @@ internal static partial class Interop out SafeCFErrorHandle pErrorOut) => AppleCryptoNative_VerifySignatureWithHashAlgorithm( publicKey, - ref pbDataHash.DangerousGetPinnableReference(), + ref MemoryMarshal.GetReference(pbDataHash), cbDataHash, - ref pbSignature.DangerousGetPinnableReference(), + ref MemoryMarshal.GetReference(pbSignature), cbSignature, hashAlgorithm, out pErrorOut); diff --git a/external/corefx/src/Common/src/Interop/Unix/Interop.IOErrors.cs b/external/corefx/src/Common/src/Interop/Unix/Interop.IOErrors.cs index e9d6ce61d6..5babcd1d05 100644 --- a/external/corefx/src/Common/src/Interop/Unix/Interop.IOErrors.cs +++ b/external/corefx/src/Common/src/Interop/Unix/Interop.IOErrors.cs @@ -138,7 +138,9 @@ internal static partial class Interop new UnauthorizedAccessException(SR.UnauthorizedAccess_IODenied_NoPathName, inner); case Error.ENAMETOOLONG: - return new PathTooLongException(SR.IO_PathTooLong); + return !string.IsNullOrEmpty(path) ? + new PathTooLongException(SR.Format(SR.IO_PathTooLong_Path, path)) : + new PathTooLongException(SR.IO_PathTooLong); case Error.EWOULDBLOCK: return !string.IsNullOrEmpty(path) ? diff --git a/external/corefx/src/Common/src/Interop/Unix/Interop.Libraries.cs b/external/corefx/src/Common/src/Interop/Unix/Interop.Libraries.cs index df9955f9de..f33671958c 100644 --- a/external/corefx/src/Common/src/Interop/Unix/Interop.Libraries.cs +++ b/external/corefx/src/Common/src/Interop/Unix/Interop.Libraries.cs @@ -4,7 +4,7 @@ internal static partial class Interop { - private static partial class Libraries + internal static partial class Libraries { // Shims internal const string SystemNative = "System.Native"; diff --git a/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.Bind.cs b/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.Bind.cs index 8b6407b51b..03f68de856 100644 --- a/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.Bind.cs +++ b/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.Bind.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Net.Sockets; using System.Runtime.InteropServices; internal static partial class Interop @@ -10,6 +11,6 @@ internal static partial class Interop internal static partial class Sys { [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Bind")] - internal static extern unsafe Error Bind(SafeHandle socket, byte* socketAddress, int socketAddressLen); + internal static extern unsafe Error Bind(SafeHandle socket, ProtocolType socketProtocolType, byte* socketAddress, int socketAddressLen); } } diff --git a/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.GetBytesAvailable.cs b/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.GetBytesAvailable.cs index 819d0dbf73..ee42a483d3 100644 --- a/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.GetBytesAvailable.cs +++ b/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.GetBytesAvailable.cs @@ -11,5 +11,8 @@ internal static partial class Interop { [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetBytesAvailable")] internal static extern unsafe Error GetBytesAvailable(SafeHandle socket, int* available); + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetAtOutOfBandMark")] + internal static extern unsafe Error GetAtOutOfBandMark(SafeHandle socket, int* atMark); } } diff --git a/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.GetCwd.cs b/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.GetCwd.cs index f3c01f522f..c058784960 100644 --- a/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.GetCwd.cs +++ b/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.GetCwd.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Buffers; using System.Runtime.InteropServices; internal static partial class Interop @@ -25,15 +26,13 @@ internal static partial class Interop } // If that was too small, try increasing large buffer sizes - // until we get one that works or until we hit MaxPath. - int maxPath = Interop.Sys.MaxPath; - if (StackLimit < maxPath) + int bufferSize = StackLimit; + do { - int bufferSize = StackLimit; - do + checked { bufferSize *= 2; } + byte[] buf = ArrayPool.Shared.Rent(bufferSize); + try { - checked { bufferSize *= 2; } - var buf = new byte[Math.Min(bufferSize, maxPath)]; fixed (byte* ptr = &buf[0]) { result = GetCwdHelper(ptr, buf.Length); @@ -43,11 +42,12 @@ internal static partial class Interop } } } - while (bufferSize < maxPath); + finally + { + ArrayPool.Shared.Return(buf); + } } - - // If we couldn't get the cwd with a MaxPath-sized buffer, something's wrong. - throw Interop.GetExceptionForIoErrno(new ErrorInfo(Interop.Error.ENAMETOOLONG)); + while (true); } private static unsafe string GetCwdHelper(byte* ptr, int bufferSize) @@ -71,4 +71,4 @@ internal static partial class Interop throw Interop.GetExceptionForIoErrno(errorInfo); } } -} +} \ No newline at end of file diff --git a/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.GetNameInfo.cs b/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.GetNameInfo.cs index a3b672c44f..bd22e8c912 100644 --- a/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.GetNameInfo.cs +++ b/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.GetNameInfo.cs @@ -20,7 +20,7 @@ internal static partial class Interop internal static extern unsafe int GetNameInfo( byte* address, uint addressLength, - bool isIpv6, + byte isIpv6, byte* host, uint hostLength, byte* service, diff --git a/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.PathConf.cs b/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.PathConf.cs index 4a1fcf67d0..7213cb0264 100644 --- a/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.PathConf.cs +++ b/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.PathConf.cs @@ -9,8 +9,6 @@ internal static partial class Interop { internal static partial class Sys { - internal static int DEFAULT_PC_NAME_MAX = 255; - internal enum PathConfName : int { PC_LINK_MAX = 1, @@ -24,50 +22,7 @@ internal static partial class Interop PC_VDISABLE = 9, } - /// The maximum path length for the system. -1 if it hasn't yet been initialized. - private static int s_maxPath = -1; - - /// The maximum name length for the system. -1 if it hasn't yet been initialized. - private static int s_maxName = -1; - - internal static int MaxPath - { - get - { - // Benign race condition on cached value - if (s_maxPath < 0) - { - // GetMaximumPath returns a long from PathConf - // but our callers expect an int so we need to convert. - long temp = GetMaximumPath(); - if (temp > int.MaxValue) - s_maxPath = int.MaxValue; - else - s_maxPath = Convert.ToInt32(temp); - } - return s_maxPath; - } - } - - internal static int MaxName - { - get - { - // Benign race condition on cached value - if (s_maxName < 0) - { - int result = PathConf("/", PathConfName.PC_NAME_MAX); - s_maxName = result >= 0 ? result : DEFAULT_PC_NAME_MAX; - } - - return s_maxName; - } - } - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_PathConf", SetLastError = true)] private static extern int PathConf(string path, PathConfName name); - - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetMaximumPath")] - private static extern long GetMaximumPath(); } } diff --git a/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.ReadDir.cs b/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.ReadDir.cs index bcd29bc2da..d98c4285c0 100644 --- a/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.ReadDir.cs +++ b/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.ReadDir.cs @@ -4,13 +4,14 @@ using System; using System.Runtime.InteropServices; +using System.Threading; using Microsoft.Win32.SafeHandles; internal static partial class Interop { internal static partial class Sys { - private static readonly int s_direntSize = GetDirentSize(); + private static readonly int s_readBufferSize = GetReadDirRBufferSize(); internal enum NodeType : int { @@ -42,11 +43,11 @@ internal static partial class Interop [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_OpenDir", SetLastError = true)] internal static extern Microsoft.Win32.SafeHandles.SafeDirectoryHandle OpenDir(string path); - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetDirentSize", SetLastError = false)] - internal static extern int GetDirentSize(); + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetReadDirRBufferSize", SetLastError = false)] + internal static extern int GetReadDirRBufferSize(); [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_ReadDirR", SetLastError = false)] - private static extern unsafe int ReadDirR(SafeDirectoryHandle dir, byte* buffer, int bufferSize, out InternalDirectoryEntry outputEntry); + private static extern unsafe int ReadDirR(IntPtr dir, byte* buffer, int bufferSize, out InternalDirectoryEntry outputEntry); [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_CloseDir", SetLastError = true)] internal static extern int CloseDir(IntPtr dir); @@ -54,17 +55,39 @@ internal static partial class Interop // The calling pattern for ReadDir is described in src/Native/System.Native/pal_readdir.cpp internal static int ReadDir(SafeDirectoryHandle dir, out DirectoryEntry outputEntry) { - unsafe + bool addedRef = false; + try { - // To reduce strcpys, alloc a buffer here and get the result from OS, then copy it over for the caller. - byte* buffer = stackalloc byte[s_direntSize]; - InternalDirectoryEntry temp; - int ret = ReadDirR(dir, buffer, s_direntSize, out temp); - outputEntry = ret == 0 ? - new DirectoryEntry() { InodeName = GetDirectoryEntryName(temp), InodeType = temp.InodeType } : - default(DirectoryEntry); + // We avoid a native string copy into InternalDirectoryEntry. + // - If the platform suppors reading into a buffer, the data is read directly into the buffer. The + // data can be read as long as the buffer is valid. + // - If the platform does not support reading into a buffer, the information returned in + // InternalDirectoryEntry points to native memory owned by the SafeDirectoryHandle. The data is only + // valid until the next call to CloseDir/ReadDir. We extend the reference until we have copied all data + // to ensure it does not become invalid by a CloseDir; and we copy the data so our caller does not + // use the native memory held by the SafeDirectoryHandle. + dir.DangerousAddRef(ref addedRef); - return ret; + unsafe + { + // s_readBufferSize is zero when the native implementation does not support reading into a buffer. + byte* buffer = stackalloc byte[s_readBufferSize]; + InternalDirectoryEntry temp; + int ret = ReadDirR(dir.DangerousGetHandle(), buffer, s_readBufferSize, out temp); + // We copy data into DirectoryEntry to ensure there are no dangling references. + outputEntry = ret == 0 ? + new DirectoryEntry() { InodeName = GetDirectoryEntryName(temp), InodeType = temp.InodeType } : + default(DirectoryEntry); + + return ret; + } + } + finally + { + if (addedRef) + { + dir.DangerousRelease(); + } } } diff --git a/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.ReadLink.cs b/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.ReadLink.cs index a39650fd83..6397027ff3 100644 --- a/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.ReadLink.cs +++ b/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.ReadLink.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Runtime.InteropServices; +using System.Buffers; +using System.Text; internal static partial class Interop { @@ -16,9 +18,46 @@ internal static partial class Interop /// The buffer to hold the output path /// The size of the buffer /// - /// Returns the number of bytes placed into the buffer on success; otherwise, -1 is returned + /// Returns the number of bytes placed into the buffer on success; bufferSize if the buffer is too small; and -1 on error. /// [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_ReadLink", SetLastError = true)] - internal static extern unsafe int ReadLink(string path, byte[] buffer, int bufferSize); + private static extern unsafe int ReadLink(string path, byte[] buffer, int bufferSize); + + /// + /// Takes a path to a symbolic link and returns the link target path. + /// + /// The path to the symlink + /// + /// Returns the link to the target path on success; and null otherwise. + /// + public static string ReadLink(string path) + { + int bufferSize = 256; + do + { + byte[] buffer = ArrayPool.Shared.Rent(bufferSize); + try + { + int resultLength = Interop.Sys.ReadLink(path, buffer, buffer.Length); + if (resultLength < 0) + { + // error + return null; + } + else if (resultLength < buffer.Length) + { + // success + return Encoding.UTF8.GetString(buffer, 0, resultLength); + } + } + finally + { + ArrayPool.Shared.Return(buffer); + } + + // buffer was too small, loop around again and try with a larger buffer. + bufferSize *= 2; + } while (true); + } } } diff --git a/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.Stat.Pipe.cs b/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.Stat.Pipe.cs index b52f565127..7985cebaa6 100644 --- a/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.Stat.Pipe.cs +++ b/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.Stat.Pipe.cs @@ -10,7 +10,7 @@ internal static partial class Interop { internal static partial class Sys { - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_FStat", SetLastError = true)] + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_FStat2", SetLastError = true)] internal static extern int FStat(SafePipeHandle fd, out FileStatus output); } } diff --git a/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.Stat.cs b/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.Stat.cs index cda00ac6c3..0ca199b70d 100644 --- a/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.Stat.cs +++ b/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.Stat.cs @@ -24,9 +24,13 @@ internal static partial class Interop internal uint Gid; internal long Size; internal long ATime; + internal long ATimeNsec; internal long MTime; + internal long MTimeNsec; internal long CTime; + internal long CTimeNsec; internal long BirthTime; + internal long BirthTimeNsec; internal long Dev; internal long Ino; } @@ -49,13 +53,13 @@ internal static partial class Interop HasBirthTime = 1, } - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_FStat", SetLastError = true)] + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_FStat2", SetLastError = true)] internal static extern int FStat(SafeFileHandle fd, out FileStatus output); - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Stat", SetLastError = true)] + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Stat2", SetLastError = true)] internal static extern int Stat(string path, out FileStatus output); - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_LStat", SetLastError = true)] + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_LStat2", SetLastError = true)] internal static extern int LStat(string path, out FileStatus output); } } diff --git a/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.SysConf.cs b/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.SysConf.cs index 58566ae65a..be0c0dee7b 100644 --- a/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.SysConf.cs +++ b/external/corefx/src/Common/src/Interop/Unix/System.Native/Interop.SysConf.cs @@ -11,8 +11,7 @@ internal static partial class Interop internal enum SysConfName { _SC_CLK_TCK = 1, - _SC_PAGESIZE = 2, - _SC_NPROCESSORS_ONLN = 3, + _SC_PAGESIZE = 2 } [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_SysConf", SetLastError = true)] diff --git a/external/corefx/src/Common/src/Interop/Unix/System.Net.Http.Native/Interop.Easy.cs b/external/corefx/src/Common/src/Interop/Unix/System.Net.Http.Native/Interop.Easy.cs index 245980aa16..16588e48ab 100644 --- a/external/corefx/src/Common/src/Interop/Unix/System.Net.Http.Native/Interop.Easy.cs +++ b/external/corefx/src/Common/src/Interop/Unix/System.Net.Http.Native/Interop.Easy.cs @@ -127,6 +127,10 @@ internal static partial class Interop CURLOPT_COPYPOSTFIELDS = CurlOptionObjectPointBase + 165, CURLOPT_USERNAME = CurlOptionObjectPointBase + 173, CURLOPT_PASSWORD = CurlOptionObjectPointBase + 174, + CURLOPT_CAPATH = CurlOptionObjectPointBase + 97, + CURLOPT_PROXY_CAPATH = CurlOptionObjectPointBase + 247, + CURLOPT_CAINFO = CurlOptionObjectPointBase + 65, + CURLOPT_PROXY_CAINFO = CurlOptionObjectPointBase + 246, CURLOPT_INFILESIZE_LARGE = CurlOptionOffTBase + 115, CURLOPT_POSTFIELDSIZE_LARGE = CurlOptionOffTBase + 120, diff --git a/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Dsa.cs b/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Dsa.cs index 4144e9d710..bb610f60f3 100644 --- a/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Dsa.cs +++ b/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Dsa.cs @@ -67,14 +67,14 @@ internal static partial class Interop } internal static bool DsaSign(SafeDsaHandle dsa, ReadOnlySpan hash, int hashLength, ReadOnlySpan refSignature, out int outSignatureLength) => - DsaSign(dsa, ref hash.DangerousGetPinnableReference(), hashLength, ref refSignature.DangerousGetPinnableReference(), out outSignatureLength); + DsaSign(dsa, ref MemoryMarshal.GetReference(hash), hashLength, ref MemoryMarshal.GetReference(refSignature), out outSignatureLength); [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_DsaSign")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool DsaSign(SafeDsaHandle dsa, ref byte hash, int hashLength, ref byte refSignature, out int outSignatureLength); internal static bool DsaVerify(SafeDsaHandle dsa, ReadOnlySpan hash, int hashLength, ReadOnlySpan signature, int signatureLength) => - DsaVerify(dsa, ref hash.DangerousGetPinnableReference(), hashLength, ref signature.DangerousGetPinnableReference(), signatureLength); + DsaVerify(dsa, ref MemoryMarshal.GetReference(hash), hashLength, ref MemoryMarshal.GetReference(signature), signatureLength); [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_DsaVerify")] [return: MarshalAs(UnmanagedType.Bool)] diff --git a/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.cs b/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.cs index b2ce394c75..67a9b54230 100644 --- a/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.cs +++ b/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.cs @@ -20,7 +20,7 @@ internal static partial class Interop internal extern static int EvpDigestReset(SafeEvpMdCtxHandle ctx, IntPtr type); internal static int EvpDigestUpdate(SafeEvpMdCtxHandle ctx, ReadOnlySpan d, int cnt) => - EvpDigestUpdate(ctx, ref d.DangerousGetPinnableReference(), cnt); + EvpDigestUpdate(ctx, ref MemoryMarshal.GetReference(d), cnt); [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpDigestUpdate")] private extern static int EvpDigestUpdate(SafeEvpMdCtxHandle ctx, ref byte d, int cnt); diff --git a/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EcDsa.cs b/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EcDsa.cs index d466797e71..91c1beab99 100644 --- a/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EcDsa.cs +++ b/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EcDsa.cs @@ -11,14 +11,14 @@ internal static partial class Interop internal static partial class Crypto { internal static bool EcDsaSign(ReadOnlySpan dgst, int dlen, Span sig, [In, Out] ref int siglen, SafeEcKeyHandle ecKey) => - EcDsaSign(ref dgst.DangerousGetPinnableReference(), dlen, ref sig.DangerousGetPinnableReference(), ref siglen, ecKey); + EcDsaSign(ref MemoryMarshal.GetReference(dgst), dlen, ref MemoryMarshal.GetReference(sig), ref siglen, ecKey); [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EcDsaSign")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool EcDsaSign(ref byte dgst, int dlen, ref byte sig, [In, Out] ref int siglen, SafeEcKeyHandle ecKey); internal static unsafe int EcDsaVerify(ReadOnlySpan dgst, int dgst_len, ReadOnlySpan sigbuf, int sig_len, SafeEcKeyHandle ecKey) => - EcDsaVerify(ref dgst.DangerousGetPinnableReference(), dgst_len, ref sigbuf.DangerousGetPinnableReference(), sig_len, ecKey); + EcDsaVerify(ref MemoryMarshal.GetReference(dgst), dgst_len, ref MemoryMarshal.GetReference(sigbuf), sig_len, ecKey); /*- * returns diff --git a/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Hmac.cs b/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Hmac.cs index bf151fa0ae..6849b4f2e0 100644 --- a/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Hmac.cs +++ b/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Hmac.cs @@ -20,7 +20,7 @@ internal static partial class Interop internal extern static int HmacReset(SafeHmacCtxHandle ctx); internal static int HmacUpdate(SafeHmacCtxHandle ctx, ReadOnlySpan data, int len) => - HmacUpdate(ctx, ref data.DangerousGetPinnableReference(), len); + HmacUpdate(ctx, ref MemoryMarshal.GetReference(data), len); [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_HmacUpdate")] private extern static int HmacUpdate(SafeHmacCtxHandle ctx, ref byte data, int len); diff --git a/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs b/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs index 8ecb2c0ced..637aae916d 100644 --- a/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs +++ b/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs @@ -6,9 +6,11 @@ using System; using System.Buffers; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.IO; using System.Net.Http; using System.Net.Security; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Authentication; using System.Security.Authentication.ExtendedProtection; @@ -20,10 +22,11 @@ internal static partial class Interop { internal static partial class OpenSsl { - private static Ssl.SslCtxSetVerifyCallback s_verifyClientCertificate = VerifyClientCertificate; + private static readonly Ssl.SslCtxSetVerifyCallback s_verifyClientCertificate = VerifyClientCertificate; + private unsafe static readonly Ssl.SslCtxSetAlpnCallback s_alpnServerCallback = AlpnServerSelectCallback; + private static readonly IdnMapping s_idnMapping = new IdnMapping(); #region internal methods - internal static SafeChannelBindingHandle QueryChannelBinding(SafeSslHandle context, ChannelBindingKind bindingType) { Debug.Assert( @@ -47,7 +50,7 @@ internal static partial class Interop return bindingHandle; } - internal static SafeSslHandle AllocateSslContext(SslProtocols protocols, SafeX509Handle certHandle, SafeEvpPKeyHandle certKeyHandle, EncryptionPolicy policy, bool isServer, bool remoteCertRequired) + internal static SafeSslHandle AllocateSslContext(SslProtocols protocols, SafeX509Handle certHandle, SafeEvpPKeyHandle certKeyHandle, EncryptionPolicy policy, SslAuthenticationOptions sslAuthenticationOptions) { SafeSslHandle context = null; @@ -88,44 +91,79 @@ internal static partial class Interop SetSslCertificate(innerContext, certHandle, certKeyHandle); } - if (remoteCertRequired) + if (sslAuthenticationOptions.IsServer && sslAuthenticationOptions.RemoteCertRequired) { - Debug.Assert(isServer, "isServer flag should be true"); - Ssl.SslCtxSetVerify(innerContext, - s_verifyClientCertificate); - - //update the client CA list - UpdateCAListFromRootStore(innerContext); + Ssl.SslCtxSetVerify(innerContext, s_verifyClientCertificate); } - context = SafeSslHandle.Create(innerContext, isServer); - Debug.Assert(context != null, "Expected non-null return value from SafeSslHandle.Create"); - if (context.IsInvalid) + GCHandle alpnHandle = default; + try { - context.Dispose(); - throw CreateSslException(SR.net_allocate_ssl_context_failed); - } - - if (hasCertificateAndKey) - { - bool hasCertReference = false; - try + if (sslAuthenticationOptions.ApplicationProtocols != null) { - certHandle.DangerousAddRef(ref hasCertReference); - using (X509Certificate2 cert = new X509Certificate2(certHandle.DangerousGetHandle())) + if (sslAuthenticationOptions.IsServer) { - using (X509Chain chain = TLSCertificateExtensions.BuildNewChain(cert, includeClientApplicationPolicy: false)) + alpnHandle = GCHandle.Alloc(sslAuthenticationOptions.ApplicationProtocols); + Interop.Ssl.SslCtxSetAlpnSelectCb(innerContext, s_alpnServerCallback, GCHandle.ToIntPtr(alpnHandle)); + } + else + { + if (Interop.Ssl.SslCtxSetAlpnProtos(innerContext, sslAuthenticationOptions.ApplicationProtocols) != 0) { - if (chain != null && !Ssl.AddExtraChainCertificates(context, chain)) - throw CreateSslException(SR.net_ssl_use_cert_failed); + throw CreateSslException(SR.net_alpn_config_failed); } } } - finally + + context = SafeSslHandle.Create(innerContext, sslAuthenticationOptions.IsServer); + Debug.Assert(context != null, "Expected non-null return value from SafeSslHandle.Create"); + if (context.IsInvalid) { - if (hasCertReference) - certHandle.DangerousRelease(); + context.Dispose(); + throw CreateSslException(SR.net_allocate_ssl_context_failed); } + + if (!sslAuthenticationOptions.IsServer) + { + // The IdnMapping converts unicode input into the IDNA punycode sequence. + string punyCode = s_idnMapping.GetAscii(sslAuthenticationOptions.TargetHost); + + // Similar to windows behavior, set SNI on openssl by default for client context, ignore errors. + Ssl.SslSetTlsExtHostName(context, punyCode); + } + + if (hasCertificateAndKey) + { + bool hasCertReference = false; + try + { + certHandle.DangerousAddRef(ref hasCertReference); + using (X509Certificate2 cert = new X509Certificate2(certHandle.DangerousGetHandle())) + { + using (X509Chain chain = TLSCertificateExtensions.BuildNewChain(cert, includeClientApplicationPolicy: false)) + { + if (chain != null && !Ssl.AddExtraChainCertificates(context, chain)) + throw CreateSslException(SR.net_ssl_use_cert_failed); + } + } + } + finally + { + if (hasCertReference) + certHandle.DangerousRelease(); + } + } + + context.AlpnHandle = alpnHandle; + } + catch + { + if (alpnHandle.IsAllocated) + { + alpnHandle.Free(); + } + + throw; } } @@ -136,13 +174,17 @@ internal static partial class Interop { sendBuf = null; sendCount = 0; + if ((recvBuf != null) && (recvCount > 0)) { - BioWrite(context.InputBio, recvBuf, recvOffset, recvCount); + if (BioWrite(context.InputBio, recvBuf, recvOffset, recvCount) <= 0) + { + // Make sure we clear out the error that is stored in the queue + throw Crypto.CreateOpenSslCryptographicException(); + } } int retVal = Ssl.SslDoHandshake(context); - if (retVal != 1) { Exception innerError; @@ -155,7 +197,6 @@ internal static partial class Interop } sendCount = Crypto.BioCtrlPending(context.OutputBio); - if (sendCount > 0) { sendBuf = new byte[sendCount]; @@ -168,6 +209,8 @@ internal static partial class Interop { if (sendCount <= 0) { + // Make sure we clear out the error that is stored in the queue + Crypto.ErrGetError(); sendBuf = null; sendCount = 0; } @@ -191,7 +234,7 @@ internal static partial class Interop { using (MemoryHandle handle = input.Retain(pin: true)) { - retVal = Ssl.SslWrite(context, (byte*)handle.PinnedPointer, input.Length); + retVal = Ssl.SslWrite(context, (byte*)handle.Pointer, input.Length); } } @@ -221,6 +264,11 @@ internal static partial class Interop } retVal = BioRead(context.OutputBio, output, capacityNeeded); + if (retVal <= 0) + { + // Make sure we clear out the error that is stored in the queue + Crypto.ErrGetError(); + } } return retVal; @@ -314,57 +362,51 @@ internal static partial class Interop return OpenSslSuccess; } - private static void UpdateCAListFromRootStore(SafeSslContextHandle context) + private static unsafe int AlpnServerSelectCallback(IntPtr ssl, out byte* outp, out byte outlen, byte* inp, uint inlen, IntPtr arg) { - using (SafeX509NameStackHandle nameStack = Crypto.NewX509NameStack()) + outp = null; + outlen = 0; + + GCHandle protocolHandle = GCHandle.FromIntPtr(arg); + if (!(protocolHandle.Target is List protocolList)) { - //maintaining the HashSet of Certificate's issuer name to keep track of duplicates - HashSet issuerNameHashSet = new HashSet(); - - //Enumerate Certificates from LocalMachine and CurrentUser root store - AddX509Names(nameStack, StoreLocation.LocalMachine, issuerNameHashSet); - AddX509Names(nameStack, StoreLocation.CurrentUser, issuerNameHashSet); - - Ssl.SslCtxSetClientCAList(context, nameStack); - - // The handle ownership has been transferred into the CTX. - nameStack.SetHandleAsInvalid(); + return Ssl.SSL_TLSEXT_ERR_NOACK; } - } - - private static void AddX509Names(SafeX509NameStackHandle nameStack, StoreLocation storeLocation, HashSet issuerNameHashSet) - { - using (var store = new X509Store(StoreName.Root, storeLocation)) + try { - store.Open(OpenFlags.ReadOnly); - - foreach (var certificate in store.Certificates) + for (int i = 0; i < protocolList.Count; i++) { - //Check if issuer name is already present - //Avoiding duplicate names - if (!issuerNameHashSet.Add(certificate.Issuer)) + Span clientList = new Span(inp, (int)inlen); + while (clientList.Length > 0) { - continue; - } - - using (SafeX509Handle certHandle = Crypto.X509UpRef(certificate.Handle)) - { - using (SafeX509NameHandle nameHandle = Crypto.DuplicateX509Name(Crypto.X509GetIssuerName(certHandle))) + byte length = clientList[0]; + Span clientProto = clientList.Slice(1, length); + if (clientProto.SequenceEqual(protocolList[i].Protocol.Span)) { - if (Crypto.PushX509NameStackField(nameStack, nameHandle)) - { - // The handle ownership has been transferred into the STACK_OF(X509_NAME). - nameHandle.SetHandleAsInvalid(); - } - else - { - throw new CryptographicException(SR.net_ssl_x509Name_push_failed_error); - } + fixed (byte* p = &MemoryMarshal.GetReference(clientProto)) outp = p; + outlen = length; + return Ssl.SSL_TLSEXT_ERR_OK; } + + clientList = clientList.Slice(1 + length); } } } + catch + { + // No common application protocol was negotiated, set the target on the alpnHandle to null. + // It is ok to clear the handle value here, this results in handshake failure, so the SslStream object is disposed. + protocolHandle.Target = null; + + return Ssl.SSL_TLSEXT_ERR_NOACK; + } + + // No common application protocol was negotiated, set the target on the alpnHandle to null. + // It is ok to clear the handle value here, this results in handshake failure, so the SslStream object is disposed. + protocolHandle.Target = null; + + return Ssl.SSL_TLSEXT_ERR_NOACK; } private static int BioRead(SafeBioHandle bio, byte[] buffer, int count) diff --git a/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSslVersion.cs b/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSslVersion.cs new file mode 100644 index 0000000000..1f5e68c15c --- /dev/null +++ b/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSslVersion.cs @@ -0,0 +1,36 @@ +// 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.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class OpenSsl + { + private static Version s_opensslVersion; + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SSLEayVersion")] + private static extern string OpenSslVersionDescription(); + + internal static Version OpenSslVersion + { + get + { + if (s_opensslVersion == null) + { + const string OpenSSL = "OpenSSL "; + + // Skip OpenSSL part, and get the version string of format x.y.z + if (!Version.TryParse(OpenSslVersionDescription().AsReadOnlySpan().Slice(OpenSSL.Length, 5), out s_opensslVersion)) + { + s_opensslVersion = new Version(0, 0, 0); + } + } + + return s_opensslVersion; + } + } + } +} diff --git a/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs b/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs index 549727b31d..1748a66547 100644 --- a/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs +++ b/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs @@ -31,7 +31,7 @@ internal static partial class Interop Span to, SafeRsaHandle rsa, RsaPadding padding) => - RsaPublicEncrypt(flen, ref from.DangerousGetPinnableReference(), ref to.DangerousGetPinnableReference(), rsa, padding); + RsaPublicEncrypt(flen, ref MemoryMarshal.GetReference(from), ref MemoryMarshal.GetReference(to), rsa, padding); [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaPublicEncrypt")] private extern static int RsaPublicEncrypt( @@ -47,7 +47,7 @@ internal static partial class Interop Span to, SafeRsaHandle rsa, RsaPadding padding) => - RsaPrivateDecrypt(flen, ref from.DangerousGetPinnableReference(), ref to.DangerousGetPinnableReference(), rsa, padding); + RsaPrivateDecrypt(flen, ref MemoryMarshal.GetReference(from), ref MemoryMarshal.GetReference(to), rsa, padding); [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaPrivateDecrypt")] private extern static int RsaPrivateDecrypt( @@ -64,14 +64,14 @@ internal static partial class Interop internal static extern int RsaGenerateKeyEx(SafeRsaHandle rsa, int bits, SafeBignumHandle e); internal static bool RsaSign(int type, ReadOnlySpan m, int m_len, Span sigret, out int siglen, SafeRsaHandle rsa) => - RsaSign(type, ref m.DangerousGetPinnableReference(), m_len, ref sigret.DangerousGetPinnableReference(), out siglen, rsa); + RsaSign(type, ref MemoryMarshal.GetReference(m), m_len, ref MemoryMarshal.GetReference(sigret), out siglen, rsa); [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaSign")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool RsaSign(int type, ref byte m, int m_len, ref byte sigret, out int siglen, SafeRsaHandle rsa); internal static bool RsaVerify(int type, ReadOnlySpan m, int m_len, ReadOnlySpan sigbuf, int siglen, SafeRsaHandle rsa) => - RsaVerify(type, ref m.DangerousGetPinnableReference(), m_len, ref sigbuf.DangerousGetPinnableReference(), siglen, rsa); + RsaVerify(type, ref MemoryMarshal.GetReference(m), m_len, ref MemoryMarshal.GetReference(sigbuf), siglen, rsa); [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaVerify")] [return: MarshalAs(UnmanagedType.Bool)] diff --git a/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs b/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs index c468e1a34a..57f18a1e60 100644 --- a/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs +++ b/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs @@ -12,6 +12,10 @@ internal static partial class Interop { internal static partial class Ssl { + internal const int SSL_TLSEXT_ERR_OK = 0; + internal const int OPENSSL_NPN_NEGOTIATED = 1; + internal const int SSL_TLSEXT_ERR_NOACK = 3; + internal delegate int SslCtxSetVerifyCallback(int preverify_ok, IntPtr x509_ctx); [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EnsureLibSslInitialized")] @@ -44,6 +48,26 @@ internal static partial class Interop [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslGetVersion")] private static extern IntPtr SslGetVersion(SafeSslHandle ssl); + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslSetTlsExtHostName")] + internal static extern int SslSetTlsExtHostName(SafeSslHandle ssl, string host); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslGet0AlpnSelected")] + internal static extern void SslGetAlpnSelected(SafeSslHandle ssl, out IntPtr protocol, out int len); + + internal static byte[] SslGetAlpnSelected(SafeSslHandle ssl) + { + IntPtr protocol; + int len; + SslGetAlpnSelected(ssl, out protocol, out len); + + if (len == 0) + return null; + + byte[] result = new byte[len]; + Marshal.Copy(protocol, result, 0, len); + return result; + } + internal static string GetProtocolVersion(SafeSslHandle ssl) { return Marshal.PtrToStringAnsi(SslGetVersion(ssl)); @@ -156,12 +180,12 @@ internal static partial class Interop SSL_ERROR_WANT_WRITE = 3, SSL_ERROR_SYSCALL = 5, SSL_ERROR_ZERO_RETURN = 6, - + // NOTE: this SslErrorCode value doesn't exist in OpenSSL, but // we use it to distinguish when a renegotiation is pending. // Choosing an arbitrarily large value that shouldn't conflict // with any actual OpenSSL error codes - SSL_ERROR_RENEGOTIATE = 29304 + SSL_ERROR_RENEGOTIATE = 29304 } } } @@ -175,6 +199,8 @@ namespace Microsoft.Win32.SafeHandles private bool _isServer; private bool _handshakeCompleted = false; + public GCHandle AlpnHandle; + public bool IsServer { get { return _isServer; } @@ -255,6 +281,12 @@ namespace Microsoft.Win32.SafeHandles _readBio?.Dispose(); _writeBio?.Dispose(); } + + if (AlpnHandle.IsAllocated) + { + AlpnHandle.Free(); + } + base.Dispose(disposing); } diff --git a/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.SslCtx.cs b/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.SslCtx.cs index 7512efe609..b1996871d9 100644 --- a/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.SslCtx.cs +++ b/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.SslCtx.cs @@ -3,7 +3,10 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; +using System.Net.Security; using System.Runtime.InteropServices; +using System.Text; using Microsoft.Win32.SafeHandles; internal static partial class Interop @@ -12,6 +15,7 @@ internal static partial class Interop { internal delegate int AppVerifyCallback(IntPtr storeCtx, IntPtr arg); internal delegate int ClientCertCallback(IntPtr ssl, out IntPtr x509, out IntPtr pkey); + internal unsafe delegate int SslCtxSetAlpnCallback(IntPtr ssl, out byte* outp, out byte outlen, byte* inp, uint inlen, IntPtr arg); [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslCtxCreate")] internal static extern SafeSslContextHandle SslCtxCreate(IntPtr method); @@ -24,6 +28,46 @@ internal static partial class Interop [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslCtxSetClientCertCallback")] internal static extern void SslCtxSetClientCertCallback(IntPtr ctx, ClientCertCallback callback); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslCtxSetAlpnProtos")] + internal static extern int SslCtxSetAlpnProtos(SafeSslContextHandle ctx, IntPtr protos, int len); + + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslCtxSetAlpnSelectCb")] + internal static unsafe extern void SslCtxSetAlpnSelectCb(SafeSslContextHandle ctx, SslCtxSetAlpnCallback callback, IntPtr arg); + + internal static unsafe int SslCtxSetAlpnProtos(SafeSslContextHandle ctx, List protocols) + { + byte[] buffer = ConvertAlpnProtocolListToByteArray(protocols); + fixed (byte* b = buffer) + { + return SslCtxSetAlpnProtos(ctx, (IntPtr)b, buffer.Length); + } + } + + internal static byte[] ConvertAlpnProtocolListToByteArray(List applicationProtocols) + { + int protocolSize = 0; + foreach (SslApplicationProtocol protocol in applicationProtocols) + { + if (protocol.Protocol.Length == 0 || protocol.Protocol.Length > byte.MaxValue) + { + throw new ArgumentException(SR.net_ssl_app_protocols_invalid, nameof(applicationProtocols)); + } + + protocolSize += protocol.Protocol.Length + 1; + } + + byte[] buffer = new byte[protocolSize]; + var offset = 0; + foreach (SslApplicationProtocol protocol in applicationProtocols) + { + buffer[offset++] = (byte)(protocol.Protocol.Length); + protocol.Protocol.Span.CopyTo(new Span(buffer).Slice(offset)); + offset += protocol.Protocol.Length; + } + + return buffer; + } } } diff --git a/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.SslCtxOptions.cs b/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.SslCtxOptions.cs index 3ab95d491e..4c4721f412 100644 --- a/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.SslCtxOptions.cs +++ b/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.SslCtxOptions.cs @@ -32,8 +32,5 @@ internal static partial class Interop [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SetEncryptionPolicy")] internal static extern bool SetEncryptionPolicy(SafeSslContextHandle ctx, EncryptionPolicy policy); - - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslCtxSetClientCAList")] - internal static extern void SslCtxSetClientCAList(SafeSslContextHandle ctx, SafeX509NameStackHandle x509NameStackPtr); } } diff --git a/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509Name.cs b/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509Name.cs index 169a23b66f..0f0fecad24 100644 --- a/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509Name.cs +++ b/external/corefx/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509Name.cs @@ -25,9 +25,6 @@ internal static partial class Interop [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_NewX509NameStack")] internal static extern SafeX509NameStackHandle NewX509NameStack(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_DuplicateX509Name")] - internal static extern SafeX509NameHandle DuplicateX509Name(IntPtr x509Name); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetX509NameStackField")] private static extern SafeSharedX509NameHandle GetX509NameStackField_private(SafeSharedX509NameStackHandle sk, int loc); @@ -90,7 +87,6 @@ namespace Microsoft.Win32.SafeHandles } } - [SecurityCritical] internal sealed class SafeX509NameStackHandle : SafeHandle { private SafeX509NameStackHandle() : diff --git a/external/corefx/src/Common/src/Interop/Windows/BCrypt/Interop.BCryptFinishHash.cs b/external/corefx/src/Common/src/Interop/Windows/BCrypt/Interop.BCryptFinishHash.cs index 3bf2877685..7ffda6ca6c 100644 --- a/external/corefx/src/Common/src/Interop/Windows/BCrypt/Interop.BCryptFinishHash.cs +++ b/external/corefx/src/Common/src/Interop/Windows/BCrypt/Interop.BCryptFinishHash.cs @@ -11,7 +11,7 @@ internal partial class Interop internal partial class BCrypt { internal static NTSTATUS BCryptFinishHash(SafeBCryptHashHandle hHash, Span pbOutput, int cbOutput, int dwFlags) => - BCryptFinishHash(hHash, ref pbOutput.DangerousGetPinnableReference(), cbOutput, dwFlags); + BCryptFinishHash(hHash, ref MemoryMarshal.GetReference(pbOutput), cbOutput, dwFlags); [DllImport(Libraries.BCrypt, CharSet = CharSet.Unicode)] private static extern NTSTATUS BCryptFinishHash(SafeBCryptHashHandle hHash, ref byte pbOutput, int cbOutput, int dwFlags); diff --git a/external/corefx/src/Common/src/Interop/Windows/BCrypt/Interop.BCryptHashData.cs b/external/corefx/src/Common/src/Interop/Windows/BCrypt/Interop.BCryptHashData.cs index 1ea3ade6ec..2832c7b68c 100644 --- a/external/corefx/src/Common/src/Interop/Windows/BCrypt/Interop.BCryptHashData.cs +++ b/external/corefx/src/Common/src/Interop/Windows/BCrypt/Interop.BCryptHashData.cs @@ -12,7 +12,7 @@ internal partial class Interop internal partial class BCrypt { internal static NTSTATUS BCryptHashData(SafeBCryptHashHandle hHash, ReadOnlySpan pbInput, int cbInput, int dwFlags) => - BCryptHashData(hHash, ref pbInput.DangerousGetPinnableReference(), cbInput, dwFlags); + BCryptHashData(hHash, ref MemoryMarshal.GetReference(pbInput), cbInput, dwFlags); [DllImport(Libraries.BCrypt, CharSet = CharSet.Unicode)] private static extern NTSTATUS BCryptHashData(SafeBCryptHashHandle hHash, ref byte pbInput, int cbInput, int dwFlags); diff --git a/external/corefx/src/Common/src/Interop/Windows/HttpApi/Interop.HttpApi.cs b/external/corefx/src/Common/src/Interop/Windows/HttpApi/Interop.HttpApi.cs index a4654b51ab..38d8491d5a 100644 --- a/external/corefx/src/Common/src/Interop/Windows/HttpApi/Interop.HttpApi.cs +++ b/external/corefx/src/Common/src/Interop/Windows/HttpApi/Interop.HttpApi.cs @@ -786,75 +786,69 @@ internal static partial class Interop return GetVerb(request, 0); } - internal static unsafe string GetVerb(byte[] memoryBlob, IntPtr originalAddress) + internal static unsafe string GetVerb(IntPtr memoryBlob, IntPtr originalAddress) { - fixed (byte* pMemoryBlob = memoryBlob) - { - return GetVerb((HTTP_REQUEST*)pMemoryBlob, pMemoryBlob - (byte*)originalAddress); - } + return GetVerb((HTTP_REQUEST*)memoryBlob.ToPointer(), (byte*)memoryBlob - (byte*)originalAddress); } // Server API - internal static unsafe WebHeaderCollection GetHeaders(byte[] memoryBlob, IntPtr originalAddress) + internal static unsafe WebHeaderCollection GetHeaders(IntPtr memoryBlob, IntPtr originalAddress) { NetEventSource.Enter(null); // Return value. WebHeaderCollection headerCollection = new WebHeaderCollection(); - fixed (byte* pMemoryBlob = memoryBlob) + byte* pMemoryBlob = (byte*)memoryBlob; + HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; + long fixup = pMemoryBlob - (byte*)originalAddress; + int index; + + // unknown headers + if (request->Headers.UnknownHeaderCount != 0) { - HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; - long fixup = pMemoryBlob - (byte*)originalAddress; - int index; - - // unknown headers - if (request->Headers.UnknownHeaderCount != 0) + HTTP_UNKNOWN_HEADER* pUnknownHeader = (HTTP_UNKNOWN_HEADER*)(fixup + (byte*)request->Headers.pUnknownHeaders); + for (index = 0; index < request->Headers.UnknownHeaderCount; index++) { - HTTP_UNKNOWN_HEADER* pUnknownHeader = (HTTP_UNKNOWN_HEADER*)(fixup + (byte*)request->Headers.pUnknownHeaders); - for (index = 0; index < request->Headers.UnknownHeaderCount; index++) + // For unknown headers, when header value is empty, RawValueLength will be 0 and + // pRawValue will be null. + if (pUnknownHeader->pName != null && pUnknownHeader->NameLength > 0) { - // For unknown headers, when header value is empty, RawValueLength will be 0 and - // pRawValue will be null. - if (pUnknownHeader->pName != null && pUnknownHeader->NameLength > 0) + string headerName = new string(pUnknownHeader->pName + fixup, 0, pUnknownHeader->NameLength); + string headerValue; + if (pUnknownHeader->pRawValue != null && pUnknownHeader->RawValueLength > 0) { - string headerName = new string(pUnknownHeader->pName + fixup, 0, pUnknownHeader->NameLength); - string headerValue; - if (pUnknownHeader->pRawValue != null && pUnknownHeader->RawValueLength > 0) - { - headerValue = new string(pUnknownHeader->pRawValue + fixup, 0, pUnknownHeader->RawValueLength); - } - else - { - headerValue = string.Empty; - } - headerCollection.Add(headerName, headerValue); + headerValue = new string(pUnknownHeader->pRawValue + fixup, 0, pUnknownHeader->RawValueLength); } - pUnknownHeader++; + else + { + headerValue = string.Empty; + } + headerCollection.Add(headerName, headerValue); } + pUnknownHeader++; } + } - // known headers - HTTP_KNOWN_HEADER* pKnownHeader = &request->Headers.KnownHeaders; - for (index = 0; index < HttpHeaderRequestMaximum; index++) + // known headers + HTTP_KNOWN_HEADER* pKnownHeader = &request->Headers.KnownHeaders; + for (index = 0; index < HttpHeaderRequestMaximum; index++) + { + // For known headers, when header value is empty, RawValueLength will be 0 and + // pRawValue will point to empty string ("\0") + if (pKnownHeader->pRawValue != null) { - // For known headers, when header value is empty, RawValueLength will be 0 and - // pRawValue will point to empty string ("\0") - if (pKnownHeader->pRawValue != null) - { - string headerValue = new string(pKnownHeader->pRawValue + fixup, 0, pKnownHeader->RawValueLength); - headerCollection.Add(HTTP_REQUEST_HEADER_ID.ToString(index), headerValue); - } - pKnownHeader++; + string headerValue = new string(pKnownHeader->pRawValue + fixup, 0, pKnownHeader->RawValueLength); + headerCollection.Add(HTTP_REQUEST_HEADER_ID.ToString(index), headerValue); } + pKnownHeader++; } NetEventSource.Exit(null); return headerCollection; } - - internal static unsafe uint GetChunks(byte[] memoryBlob, IntPtr originalAddress, ref int dataChunkIndex, ref uint dataChunkOffset, byte[] buffer, int offset, int size) + internal static unsafe uint GetChunks(IntPtr memoryBlob, IntPtr originalAddress, ref int dataChunkIndex, ref uint dataChunkOffset, byte[] buffer, int offset, int size) { if (NetEventSource.IsEnabled) { @@ -863,53 +857,51 @@ internal static partial class Interop // Return value. uint dataRead = 0; - fixed (byte* pMemoryBlob = memoryBlob) + byte* pMemoryBlob = (byte*)memoryBlob; + HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; + long fixup = pMemoryBlob - (byte*)originalAddress; + + if (request->EntityChunkCount > 0 && dataChunkIndex < request->EntityChunkCount && dataChunkIndex != -1) { - HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; - long fixup = pMemoryBlob - (byte*)originalAddress; + HTTP_DATA_CHUNK* pDataChunk = (HTTP_DATA_CHUNK*)(fixup + (byte*)&request->pEntityChunks[dataChunkIndex]); - if (request->EntityChunkCount > 0 && dataChunkIndex < request->EntityChunkCount && dataChunkIndex != -1) + fixed (byte* pReadBuffer = buffer) { - HTTP_DATA_CHUNK* pDataChunk = (HTTP_DATA_CHUNK*)(fixup + (byte*)&request->pEntityChunks[dataChunkIndex]); + byte* pTo = &pReadBuffer[offset]; - fixed (byte* pReadBuffer = buffer) + while (dataChunkIndex < request->EntityChunkCount && dataRead < size) { - byte* pTo = &pReadBuffer[offset]; - - while (dataChunkIndex < request->EntityChunkCount && dataRead < size) + if (dataChunkOffset >= pDataChunk->BufferLength) { - if (dataChunkOffset >= pDataChunk->BufferLength) - { - dataChunkOffset = 0; - dataChunkIndex++; - pDataChunk++; - } - else - { - byte* pFrom = pDataChunk->pBuffer + dataChunkOffset + fixup; + dataChunkOffset = 0; + dataChunkIndex++; + pDataChunk++; + } + else + { + byte* pFrom = pDataChunk->pBuffer + dataChunkOffset + fixup; - uint bytesToRead = pDataChunk->BufferLength - (uint)dataChunkOffset; - if (bytesToRead > (uint)size) - { - bytesToRead = (uint)size; - } - for (uint i = 0; i < bytesToRead; i++) - { - *(pTo++) = *(pFrom++); - } - dataRead += bytesToRead; - dataChunkOffset += bytesToRead; + uint bytesToRead = pDataChunk->BufferLength - (uint)dataChunkOffset; + if (bytesToRead > (uint)size) + { + bytesToRead = (uint)size; } + for (uint i = 0; i < bytesToRead; i++) + { + *(pTo++) = *(pFrom++); + } + dataRead += bytesToRead; + dataChunkOffset += bytesToRead; } } } - //we're finished. - if (dataChunkIndex == request->EntityChunkCount) - { - dataChunkIndex = -1; - } } - + //we're finished. + if (dataChunkIndex == request->EntityChunkCount) + { + dataChunkIndex = -1; + } + if (NetEventSource.IsEnabled) { NetEventSource.Exit(null); @@ -917,38 +909,34 @@ internal static partial class Interop return dataRead; } - internal static unsafe HTTP_VERB GetKnownVerb(byte[] memoryBlob, IntPtr originalAddress) + internal static unsafe HTTP_VERB GetKnownVerb(IntPtr memoryBlob, IntPtr originalAddress) { NetEventSource.Enter(null); // Return value. HTTP_VERB verb = HTTP_VERB.HttpVerbUnknown; - fixed (byte* pMemoryBlob = memoryBlob) + + HTTP_REQUEST* request = (HTTP_REQUEST*)memoryBlob.ToPointer(); + if ((int)request->Verb > (int)HTTP_VERB.HttpVerbUnparsed && (int)request->Verb < (int)HTTP_VERB.HttpVerbMaximum) { - HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; - if ((int)request->Verb > (int)HTTP_VERB.HttpVerbUnparsed && (int)request->Verb < (int)HTTP_VERB.HttpVerbMaximum) - { - verb = request->Verb; - } + verb = request->Verb; } NetEventSource.Exit(null); return verb; } - internal static unsafe IPEndPoint GetRemoteEndPoint(byte[] memoryBlob, IntPtr originalAddress) + internal static unsafe IPEndPoint GetRemoteEndPoint(IntPtr memoryBlob, IntPtr originalAddress) { if (NetEventSource.IsEnabled) NetEventSource.Enter(null); SocketAddress v4address = new SocketAddress(AddressFamily.InterNetwork, IPv4AddressSize); SocketAddress v6address = new SocketAddress(AddressFamily.InterNetworkV6, IPv6AddressSize); - fixed (byte* pMemoryBlob = memoryBlob) - { - HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; - IntPtr address = request->Address.pRemoteAddress != null ? (IntPtr)(pMemoryBlob - (byte*)originalAddress + (byte*)request->Address.pRemoteAddress) : IntPtr.Zero; - CopyOutAddress(address, ref v4address, ref v6address); - } + byte* pMemoryBlob = (byte*)memoryBlob; + HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; + IntPtr address = request->Address.pRemoteAddress != null ? (IntPtr)(pMemoryBlob - (byte*)originalAddress + (byte*)request->Address.pRemoteAddress) : IntPtr.Zero; + CopyOutAddress(address, ref v4address, ref v6address); IPEndPoint endpoint = null; if (v4address != null) @@ -964,19 +952,17 @@ internal static partial class Interop return endpoint; } - internal static unsafe IPEndPoint GetLocalEndPoint(byte[] memoryBlob, IntPtr originalAddress) + internal static unsafe IPEndPoint GetLocalEndPoint(IntPtr memoryBlob, IntPtr originalAddress) { if (NetEventSource.IsEnabled) NetEventSource.Enter(null); SocketAddress v4address = new SocketAddress(AddressFamily.InterNetwork, IPv4AddressSize); SocketAddress v6address = new SocketAddress(AddressFamily.InterNetworkV6, IPv6AddressSize); - fixed (byte* pMemoryBlob = memoryBlob) - { - HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; - IntPtr address = request->Address.pLocalAddress != null ? (IntPtr)(pMemoryBlob - (byte*)originalAddress + (byte*)request->Address.pLocalAddress) : IntPtr.Zero; - CopyOutAddress(address, ref v4address, ref v6address); - } + byte* pMemoryBlob = (byte*)memoryBlob; + HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; + IntPtr address = request->Address.pLocalAddress != null ? (IntPtr)(pMemoryBlob - (byte*)originalAddress + (byte*)request->Address.pLocalAddress) : IntPtr.Zero; + CopyOutAddress(address, ref v4address, ref v6address); IPEndPoint endpoint = null; if (v4address != null) diff --git a/external/corefx/src/Common/src/Interop/Windows/Interop.BOOLEAN.cs b/external/corefx/src/Common/src/Interop/Windows/Interop.BOOLEAN.cs new file mode 100644 index 0000000000..4874734534 --- /dev/null +++ b/external/corefx/src/Common/src/Interop/Windows/Interop.BOOLEAN.cs @@ -0,0 +1,21 @@ +// 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. + +internal partial class Interop +{ + /// + /// Blittable version of Windows BOOLEAN type. It is convenient in situations where + /// manual marshalling is required, or to avoid overhead of regular bool marshalling. + /// + /// + /// Some Windows APIs return arbitrary integer values although the return type is defined + /// as BOOLEAN. It is best to never compare BOOLEAN to TRUE. Always use bResult != BOOLEAN.FALSE + /// or bResult == BOOLEAN.FALSE . + /// + internal enum BOOLEAN : byte + { + FALSE = 0, + TRUE = 1, + } +} diff --git a/external/corefx/src/Common/src/Interop/Windows/Interop.Errors.cs b/external/corefx/src/Common/src/Interop/Windows/Interop.Errors.cs index 695d9c1ed3..5581dd9d8e 100644 --- a/external/corefx/src/Common/src/Interop/Windows/Interop.Errors.cs +++ b/external/corefx/src/Common/src/Interop/Windows/Interop.Errors.cs @@ -26,6 +26,7 @@ internal partial class Interop internal const int ERROR_FILE_EXISTS = 0x50; internal const int ERROR_INVALID_PARAMETER = 0x57; internal const int ERROR_BROKEN_PIPE = 0x6D; + internal const int ERROR_SEM_TIMEOUT = 0x79; internal const int ERROR_CALL_NOT_IMPLEMENTED = 0x78; internal const int ERROR_INSUFFICIENT_BUFFER = 0x7A; internal const int ERROR_INVALID_NAME = 0x7B; diff --git a/external/corefx/src/Common/src/Interop/Windows/Interop.Libraries.cs b/external/corefx/src/Common/src/Interop/Windows/Interop.Libraries.cs index ecdb6076e0..293d9f8d48 100644 --- a/external/corefx/src/Common/src/Interop/Windows/Interop.Libraries.cs +++ b/external/corefx/src/Common/src/Interop/Windows/Interop.Libraries.cs @@ -4,20 +4,21 @@ internal static partial class Interop { - internal static class Libraries + internal static partial class Libraries { internal const string Advapi32 = "advapi32.dll"; internal const string BCrypt = "BCrypt.dll"; + internal const string CoreComm_L1_1_1 = "api-ms-win-core-comm-l1-1-1.dll"; internal const string Crypt32 = "crypt32.dll"; internal const string Error_L1 = "api-ms-win-core-winrt-error-l1-1-0.dll"; internal const string HttpApi = "httpapi.dll"; internal const string IpHlpApi = "iphlpapi.dll"; internal const string Kernel32 = "kernel32.dll"; - internal const string KernelBase = "kernelbase.dll"; internal const string Memory_L1_3 = "api-ms-win-core-memory-l1-1-3.dll"; internal const string Mswsock = "mswsock.dll"; internal const string NCrypt = "ncrypt.dll"; internal const string NtDll = "ntdll.dll"; + internal const string Odbc32 = "odbc32.dll"; internal const string OleAut32 = "oleaut32.dll"; internal const string PerfCounter = "perfcounter.dll"; internal const string RoBuffer = "api-ms-win-core-winrt-robuffer-l1-1-0.dll"; @@ -29,6 +30,6 @@ internal static partial class Interop internal const string WebSocket = "websocket.dll"; internal const string WinHttp = "winhttp.dll"; internal const string Ws2_32 = "ws2_32.dll"; - internal const string Zlib = "clrcompression.dll"; + internal const string CompressionNative = "clrcompression.dll"; } } diff --git a/external/corefx/src/Common/src/Interop/Windows/Interop.LongFileTime.cs b/external/corefx/src/Common/src/Interop/Windows/Interop.LongFileTime.cs new file mode 100644 index 0000000000..411ae9e4c8 --- /dev/null +++ b/external/corefx/src/Common/src/Interop/Windows/Interop.LongFileTime.cs @@ -0,0 +1,28 @@ +// 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; + +internal partial class Interop +{ + /// + /// 100-nanosecond intervals (ticks) since January 1, 1601 (UTC). + /// + /// + /// For NT times that are defined as longs (LARGE_INTEGER, etc.). + /// Do NOT use for FILETIME unless you are POSITIVE it will fall on an + /// 8 byte boundary. + /// + internal struct LongFileTime + { +#pragma warning disable CS0649 + /// + /// 100-nanosecond intervals (ticks) since January 1, 1601 (UTC). + /// + internal long TicksSince1601; +#pragma warning restore CS0649 + + internal DateTime ToDateTimeUtc() => DateTime.FromFileTimeUtc(TicksSince1601); + } +} diff --git a/external/corefx/src/Common/src/Interop/Windows/NCrypt/Interop.EncryptDecrypt.cs b/external/corefx/src/Common/src/Interop/Windows/NCrypt/Interop.EncryptDecrypt.cs index 1690876e8b..d9d9a397c7 100644 --- a/external/corefx/src/Common/src/Interop/Windows/NCrypt/Interop.EncryptDecrypt.cs +++ b/external/corefx/src/Common/src/Interop/Windows/NCrypt/Interop.EncryptDecrypt.cs @@ -11,13 +11,13 @@ internal static partial class Interop internal static partial class NCrypt { internal static unsafe ErrorCode NCryptEncrypt(SafeNCryptKeyHandle hKey, ReadOnlySpan pbInput, int cbInput, void* pPaddingInfo, Span pbOutput, int cbOutput, out int pcbResult, AsymmetricPaddingMode dwFlags) => - NCryptEncrypt(hKey, ref pbInput.DangerousGetPinnableReference(), cbInput, pPaddingInfo, ref pbOutput.DangerousGetPinnableReference(), cbOutput, out pcbResult, dwFlags); + NCryptEncrypt(hKey, ref MemoryMarshal.GetReference(pbInput), cbInput, pPaddingInfo, ref MemoryMarshal.GetReference(pbOutput), cbOutput, out pcbResult, dwFlags); [DllImport(Interop.Libraries.NCrypt, CharSet = CharSet.Unicode)] private static extern unsafe ErrorCode NCryptEncrypt(SafeNCryptKeyHandle hKey, ref byte pbInput, int cbInput, void* pPaddingInfo, ref byte pbOutput, int cbOutput, out int pcbResult, AsymmetricPaddingMode dwFlags); internal static unsafe ErrorCode NCryptDecrypt(SafeNCryptKeyHandle hKey, ReadOnlySpan pbInput, int cbInput, void* pPaddingInfo, Span pbOutput, int cbOutput, out int pcbResult, AsymmetricPaddingMode dwFlags) => - NCryptDecrypt(hKey, ref pbInput.DangerousGetPinnableReference(), cbInput, pPaddingInfo, ref pbOutput.DangerousGetPinnableReference(), cbOutput, out pcbResult, dwFlags); + NCryptDecrypt(hKey, ref MemoryMarshal.GetReference(pbInput), cbInput, pPaddingInfo, ref MemoryMarshal.GetReference(pbOutput), cbOutput, out pcbResult, dwFlags); [DllImport(Interop.Libraries.NCrypt, CharSet = CharSet.Unicode)] private static extern unsafe ErrorCode NCryptDecrypt(SafeNCryptKeyHandle hKey, ref byte pbInput, int cbInput, void* pPaddingInfo, ref byte pbOutput, int cbOutput, out int pcbResult, AsymmetricPaddingMode dwFlags); diff --git a/external/corefx/src/Common/src/Interop/Windows/NCrypt/Interop.SignVerify.cs b/external/corefx/src/Common/src/Interop/Windows/NCrypt/Interop.SignVerify.cs index ddbaa508c1..9626e0ba2e 100644 --- a/external/corefx/src/Common/src/Interop/Windows/NCrypt/Interop.SignVerify.cs +++ b/external/corefx/src/Common/src/Interop/Windows/NCrypt/Interop.SignVerify.cs @@ -11,13 +11,13 @@ internal static partial class Interop internal static partial class NCrypt { internal static unsafe ErrorCode NCryptSignHash(SafeNCryptKeyHandle hKey, void* pPaddingInfo, ReadOnlySpan pbHashValue, int cbHashValue, Span pbSignature, int cbSignature, out int pcbResult, AsymmetricPaddingMode dwFlags) => - NCryptSignHash(hKey, pPaddingInfo, ref pbHashValue.DangerousGetPinnableReference(), cbHashValue, ref pbSignature.DangerousGetPinnableReference(), cbSignature, out pcbResult, dwFlags); + NCryptSignHash(hKey, pPaddingInfo, ref MemoryMarshal.GetReference(pbHashValue), cbHashValue, ref MemoryMarshal.GetReference(pbSignature), cbSignature, out pcbResult, dwFlags); [DllImport(Libraries.NCrypt, CharSet = CharSet.Unicode)] private static extern unsafe ErrorCode NCryptSignHash(SafeNCryptKeyHandle hKey, void* pPaddingInfo, ref byte pbHashValue, int cbHashValue, ref byte pbSignature, int cbSignature, out int pcbResult, AsymmetricPaddingMode dwFlags); internal static unsafe ErrorCode NCryptVerifySignature(SafeNCryptKeyHandle hKey, void* pPaddingInfo, ReadOnlySpan pbHashValue, int cbHashValue, ReadOnlySpan pbSignature, int cbSignature, AsymmetricPaddingMode dwFlags) => - NCryptVerifySignature(hKey, pPaddingInfo, ref pbHashValue.DangerousGetPinnableReference(), cbHashValue, ref pbSignature.DangerousGetPinnableReference(), cbSignature, dwFlags); + NCryptVerifySignature(hKey, pPaddingInfo, ref MemoryMarshal.GetReference(pbHashValue), cbHashValue, ref MemoryMarshal.GetReference(pbSignature), cbSignature, dwFlags); [DllImport(Libraries.NCrypt, CharSet = CharSet.Unicode)] private static extern unsafe ErrorCode NCryptVerifySignature(SafeNCryptKeyHandle hKey, void* pPaddingInfo, ref byte pbHashValue, int cbHashValue, ref byte pbSignature, int cbSignature, AsymmetricPaddingMode dwFlags); diff --git a/external/corefx/src/Common/src/Interop/Windows/NtDll/Interop.FILE_FULL_DIR_INFORMATION.cs b/external/corefx/src/Common/src/Interop/Windows/NtDll/Interop.FILE_FULL_DIR_INFORMATION.cs new file mode 100644 index 0000000000..a1037696f6 --- /dev/null +++ b/external/corefx/src/Common/src/Interop/Windows/NtDll/Interop.FILE_FULL_DIR_INFORMATION.cs @@ -0,0 +1,73 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class NtDll + { + /// + /// FILE_FULL_DIR_INFORMATION structure. + /// Used with GetFileInformationByHandleEx and FileIdBothDirectoryInfo/RestartInfo as well as NtQueryFileInformation. + /// Equivalent to FILE_FULL_DIR_INFO structure. + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public unsafe struct FILE_FULL_DIR_INFORMATION + { + /// + /// Offset in bytes of the next entry, if any. + /// + public uint NextEntryOffset; + + /// + /// Byte offset within the parent directory, undefined for NTFS. + /// + public uint FileIndex; + public LongFileTime CreationTime; + public LongFileTime LastAccessTime; + public LongFileTime LastWriteTime; + public LongFileTime ChangeTime; + public long EndOfFile; + public long AllocationSize; + + /// + /// File attributes. + /// + /// + /// Note that MSDN documentation isn't correct for this- it can return + /// any FILE_ATTRIBUTE that is currently set on the file, not just the + /// ones documented. + /// + public FileAttributes FileAttributes; + + /// + /// The length of the file name in bytes (without null). + /// + public uint FileNameLength; + + /// + /// The extended attribute size OR the reparse tag if a reparse point. + /// + public uint EaSize; + + private char _fileName; + public ReadOnlySpan FileName { get { fixed (char* c = &_fileName) { return new ReadOnlySpan(c, (int)FileNameLength / sizeof(char)); } } } + + /// + /// Gets the next info pointer or null if there are no more. + /// + public unsafe static FILE_FULL_DIR_INFORMATION* GetNextInfo(FILE_FULL_DIR_INFORMATION* info) + { + uint nextOffset = (*info).NextEntryOffset; + if (nextOffset == 0) + return null; + + return (FILE_FULL_DIR_INFORMATION*)((byte*)info + nextOffset); + } + } + } +} diff --git a/external/corefx/src/Common/src/Interop/Windows/NtDll/Interop.FILE_INFORMATION_CLASS.cs b/external/corefx/src/Common/src/Interop/Windows/NtDll/Interop.FILE_INFORMATION_CLASS.cs new file mode 100644 index 0000000000..ec856e61af --- /dev/null +++ b/external/corefx/src/Common/src/Interop/Windows/NtDll/Interop.FILE_INFORMATION_CLASS.cs @@ -0,0 +1,85 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class NtDll + { + // https://msdn.microsoft.com/en-us/library/windows/hardware/ff728840.aspx + public enum FILE_INFORMATION_CLASS : uint + { + FileDirectoryInformation = 1, + FileFullDirectoryInformation = 2, + FileBothDirectoryInformation = 3, + FileBasicInformation = 4, + FileStandardInformation = 5, + FileInternalInformation = 6, + FileEaInformation = 7, + FileAccessInformation = 8, + FileNameInformation = 9, + FileRenameInformation = 10, + FileLinkInformation = 11, + FileNamesInformation = 12, + FileDispositionInformation = 13, + FilePositionInformation = 14, + FileFullEaInformation = 15, + FileModeInformation = 16, + FileAlignmentInformation = 17, + FileAllInformation = 18, + FileAllocationInformation = 19, + FileEndOfFileInformation = 20, + FileAlternateNameInformation = 21, + FileStreamInformation = 22, + FilePipeInformation = 23, + FilePipeLocalInformation = 24, + FilePipeRemoteInformation = 25, + FileMailslotQueryInformation = 26, + FileMailslotSetInformation = 27, + FileCompressionInformation = 28, + FileObjectIdInformation = 29, + FileCompletionInformation = 30, + FileMoveClusterInformation = 31, + FileQuotaInformation = 32, + FileReparsePointInformation = 33, + FileNetworkOpenInformation = 34, + FileAttributeTagInformation = 35, + FileTrackingInformation = 36, + FileIdBothDirectoryInformation = 37, + FileIdFullDirectoryInformation = 38, + FileValidDataLengthInformation = 39, + FileShortNameInformation = 40, + FileIoCompletionNotificationInformation = 41, + FileIoStatusBlockRangeInformation = 42, + FileIoPriorityHintInformation = 43, + FileSfioReserveInformation = 44, + FileSfioVolumeInformation = 45, + FileHardLinkInformation = 46, + FileProcessIdsUsingFileInformation = 47, + FileNormalizedNameInformation = 48, + FileNetworkPhysicalNameInformation = 49, + FileIdGlobalTxDirectoryInformation = 50, + FileIsRemoteDeviceInformation = 51, + FileUnusedInformation = 52, + FileNumaNodeInformation = 53, + FileStandardLinkInformation = 54, + FileRemoteProtocolInformation = 55, + FileRenameInformationBypassAccessCheck = 56, + FileLinkInformationBypassAccessCheck = 57, + FileVolumeNameInformation = 58, + FileIdInformation = 59, + FileIdExtdDirectoryInformation = 60, + FileReplaceCompletionInformation = 61, + FileHardLinkFullIdInformation = 62, + FileIdExtdBothDirectoryInformation = 63, + FileDispositionInformationEx = 64, + FileRenameInformationEx = 65, + FileRenameInformationExBypassAccessCheck = 66, + FileDesiredStorageClassInformation = 67, + FileStatInformation = 68 + } + } +} diff --git a/external/corefx/src/Common/src/Interop/Windows/NtDll/Interop.IO_STATUS_BLOCK.cs b/external/corefx/src/Common/src/Interop/Windows/NtDll/Interop.IO_STATUS_BLOCK.cs new file mode 100644 index 0000000000..f372a56c60 --- /dev/null +++ b/external/corefx/src/Common/src/Interop/Windows/NtDll/Interop.IO_STATUS_BLOCK.cs @@ -0,0 +1,47 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class NtDll + { + // https://msdn.microsoft.com/en-us/library/windows/hardware/ff550671.aspx + [StructLayout(LayoutKind.Sequential)] + public struct IO_STATUS_BLOCK + { + /// + /// Status + /// + public IO_STATUS Status; + + /// + /// Request dependent value. + /// + public IntPtr Information; + + // This isn't an actual Windows type, it is a union within IO_STATUS_BLOCK. We *have* to separate it out as + // the size of IntPtr varies by architecture and we can't specify the size at compile time to offset the + // Information pointer in the status block. + [StructLayout(LayoutKind.Explicit)] + public struct IO_STATUS + { + /// + /// The completion status, either STATUS_SUCCESS if the operation was completed successfully or + /// some other informational, warning, or error status. + /// + [FieldOffset(0)] + public uint Status; + + /// + /// Reserved for internal use. + /// + [FieldOffset(0)] + public IntPtr Pointer; + } + } + } +} diff --git a/external/corefx/src/Common/src/Interop/Windows/NtDll/Interop.NtQueryDirectoryFile.cs b/external/corefx/src/Common/src/Interop/Windows/NtDll/Interop.NtQueryDirectoryFile.cs new file mode 100644 index 0000000000..e648c0e87d --- /dev/null +++ b/external/corefx/src/Common/src/Interop/Windows/NtDll/Interop.NtQueryDirectoryFile.cs @@ -0,0 +1,28 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class NtDll + { + // https://msdn.microsoft.com/en-us/library/windows/hardware/ff556633.aspx + // https://msdn.microsoft.com/en-us/library/windows/hardware/ff567047.aspx + [DllImport(Libraries.NtDll, CharSet = CharSet.Unicode, ExactSpelling = true)] + public unsafe static extern int NtQueryDirectoryFile( + IntPtr FileHandle, + IntPtr Event, + IntPtr ApcRoutine, + IntPtr ApcContext, + out IO_STATUS_BLOCK IoStatusBlock, + byte[] FileInformation, + uint Length, + FILE_INFORMATION_CLASS FileInformationClass, + BOOLEAN ReturnSingleEntry, + UNICODE_STRING* FileName, + BOOLEAN RestartScan); + } +} diff --git a/external/corefx/src/Common/src/Interop/Windows/NtDll/Interop.NtStatus.cs b/external/corefx/src/Common/src/Interop/Windows/NtDll/Interop.NtStatus.cs index 232d0ea216..2d51c44f54 100644 --- a/external/corefx/src/Common/src/Interop/Windows/NtDll/Interop.NtStatus.cs +++ b/external/corefx/src/Common/src/Interop/Windows/NtDll/Interop.NtStatus.cs @@ -9,6 +9,7 @@ internal static partial class Interop // Error codes from ntstatus.h internal const uint STATUS_SUCCESS = 0x00000000; internal const uint STATUS_SOME_NOT_MAPPED = 0x00000107; + internal const uint STATUS_NO_MORE_FILES = 0x80000006; internal const uint STATUS_INVALID_PARAMETER = 0xC000000D; internal const uint STATUS_NO_MEMORY = 0xC0000017; internal const uint STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034; diff --git a/external/corefx/src/Common/src/Interop/Windows/NtDll/Interop.RtlNtStatusToDosError.cs b/external/corefx/src/Common/src/Interop/Windows/NtDll/Interop.RtlNtStatusToDosError.cs new file mode 100644 index 0000000000..05e8ce4cf4 --- /dev/null +++ b/external/corefx/src/Common/src/Interop/Windows/NtDll/Interop.RtlNtStatusToDosError.cs @@ -0,0 +1,17 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class NtDll + { + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680600(v=vs.85).aspx + [DllImport(Libraries.NtDll, ExactSpelling = true)] + public unsafe static extern uint RtlNtStatusToDosError( + int Status); + } +} diff --git a/external/corefx/src/Common/src/Interop/Windows/SChannel/Interop.SECURITY_STATUS.cs b/external/corefx/src/Common/src/Interop/Windows/SChannel/Interop.SECURITY_STATUS.cs index 5d27bddd51..435de74c59 100644 --- a/external/corefx/src/Common/src/Interop/Windows/SChannel/Interop.SECURITY_STATUS.cs +++ b/external/corefx/src/Common/src/Interop/Windows/SChannel/Interop.SECURITY_STATUS.cs @@ -48,7 +48,8 @@ internal static partial class Interop SmartcardLogonRequired = unchecked((int)0x8009033E), UnsupportedPreauth = unchecked((int)0x80090343), BadBinding = unchecked((int)0x80090346), - DowngradeDetected = unchecked((int)0x80090350) + DowngradeDetected = unchecked((int)0x80090350), + ApplicationProtocolMismatch = unchecked((int)0x80090367), } #if TRACE_VERBOSE diff --git a/external/corefx/src/Common/src/Interop/Windows/SChannel/Interop.SecPkgContext_ApplicationProtocol.cs b/external/corefx/src/Common/src/Interop/Windows/SChannel/Interop.SecPkgContext_ApplicationProtocol.cs new file mode 100644 index 0000000000..447afb98db --- /dev/null +++ b/external/corefx/src/Common/src/Interop/Windows/SChannel/Interop.SecPkgContext_ApplicationProtocol.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. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal enum ApplicationProtocolNegotiationStatus + { + None = 0, + Success, + SelectedClientOnly + } + + internal enum ApplicationProtocolNegotiationExt + { + None = 0, + NPN, + ALPN + } + + [StructLayout(LayoutKind.Sequential)] + internal class SecPkgContext_ApplicationProtocol + { + private const int MaxProtocolIdSize = 0xFF; + + public ApplicationProtocolNegotiationStatus ProtoNegoStatus; + public ApplicationProtocolNegotiationExt ProtoNegoExt; + public byte ProtocolIdSize; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = MaxProtocolIdSize)] + public byte[] ProtocolId; + public byte[] Protocol + { + get + { + return new Span(ProtocolId, 0, ProtocolIdSize).ToArray(); + } + } + } +} diff --git a/external/corefx/src/Common/src/Interop/Windows/SChannel/Interop.Sec_Application_Protocols.cs b/external/corefx/src/Common/src/Interop/Windows/SChannel/Interop.Sec_Application_Protocols.cs new file mode 100644 index 0000000000..a18dac2ad9 --- /dev/null +++ b/external/corefx/src/Common/src/Interop/Windows/SChannel/Interop.Sec_Application_Protocols.cs @@ -0,0 +1,65 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Net.Security; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal struct Sec_Application_Protocols + { + private static readonly int ProtocolListOffset = Marshal.SizeOf(); + private static readonly int ProtocolListConstSize = ProtocolListOffset - (int)Marshal.OffsetOf(nameof(ProtocolExtenstionType)); + public uint ProtocolListsSize; + public ApplicationProtocolNegotiationExt ProtocolExtenstionType; + public short ProtocolListSize; + + public static unsafe byte[] ToByteArray(List applicationProtocols) + { + long protocolListSize = 0; + for (int i = 0; i < applicationProtocols.Count; i++) + { + if (applicationProtocols[i].Protocol.Length == 0 || applicationProtocols[i].Protocol.Length > byte.MaxValue) + { + throw new ArgumentException(SR.net_ssl_app_protocols_invalid, nameof(applicationProtocols)); + } + + protocolListSize += applicationProtocols[i].Protocol.Length + 1; + + if (protocolListSize > short.MaxValue) + { + throw new ArgumentException(SR.net_ssl_app_protocols_invalid, nameof(applicationProtocols)); + } + } + + Sec_Application_Protocols protocols = new Sec_Application_Protocols(); + protocols.ProtocolListsSize = (uint)(ProtocolListConstSize + protocolListSize); + protocols.ProtocolExtenstionType = ApplicationProtocolNegotiationExt.ALPN; + protocols.ProtocolListSize = (short)protocolListSize; + + Span pBuffer = new byte[protocolListSize]; + int index = 0; + for (int i = 0; i < applicationProtocols.Count; i++) + { + pBuffer[index++] = (byte)applicationProtocols[i].Protocol.Length; + applicationProtocols[i].Protocol.Span.CopyTo(pBuffer.Slice(index)); + index += applicationProtocols[i].Protocol.Length; + } + + byte[] buffer = new byte[ProtocolListOffset + protocolListSize]; + fixed (byte* bufferPtr = buffer) + { + Marshal.StructureToPtr(protocols, new IntPtr(bufferPtr), false); + byte* pList = bufferPtr + ProtocolListOffset; + pBuffer.CopyTo(new Span(pList, index)); + } + + return buffer; + } + } +} diff --git a/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketAbortHandle.cs b/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketAbortHandle.cs index f0de5b2fe2..3026749ae9 100644 --- a/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketAbortHandle.cs +++ b/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketAbortHandle.cs @@ -10,7 +10,6 @@ internal static partial class Interop internal static partial class WebSocket { [DllImport(Libraries.WebSocket)] - [SuppressUnmanagedCodeSecurity] internal static extern void WebSocketAbortHandle([In] SafeHandle webSocketHandle); } } diff --git a/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketBeginClientHandshake.cs b/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketBeginClientHandshake.cs index 697e99a5c6..1c3224491c 100644 --- a/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketBeginClientHandshake.cs +++ b/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketBeginClientHandshake.cs @@ -11,7 +11,6 @@ internal static partial class Interop internal static partial class WebSocket { [DllImport(Libraries.WebSocket)] - [SuppressUnmanagedCodeSecurity] internal static extern int WebSocketBeginClientHandshake( [In] SafeHandle webSocketHandle, [In] IntPtr subProtocols, diff --git a/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketBeginServerHandshake.cs b/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketBeginServerHandshake.cs index 54ce430968..4d8d26dc0d 100644 --- a/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketBeginServerHandshake.cs +++ b/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketBeginServerHandshake.cs @@ -11,7 +11,6 @@ internal static partial class Interop internal static partial class WebSocket { [DllImport(Libraries.WebSocket)] - [SuppressUnmanagedCodeSecurity] internal static extern int WebSocketBeginServerHandshake( [In] SafeHandle webSocketHandle, [In] IntPtr subProtocol, diff --git a/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketCompleteAction.cs b/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketCompleteAction.cs index bc4080063c..fa098569fa 100644 --- a/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketCompleteAction.cs +++ b/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketCompleteAction.cs @@ -11,7 +11,6 @@ internal static partial class Interop internal static partial class WebSocket { [DllImport(Libraries.WebSocket)] - [SuppressUnmanagedCodeSecurity] internal static extern void WebSocketCompleteAction( [In] SafeHandle webSocketHandle, [In] IntPtr actionContext, diff --git a/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketCreateClientHandle.cs b/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketCreateClientHandle.cs index 55a20afa24..44bbf68204 100644 --- a/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketCreateClientHandle.cs +++ b/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketCreateClientHandle.cs @@ -12,7 +12,6 @@ internal static partial class Interop internal static partial class WebSocket { [DllImport(Libraries.WebSocket)] - [SuppressUnmanagedCodeSecurity] internal static extern int WebSocketCreateClientHandle( [In] Property[] properties, [In] uint propertyCount, diff --git a/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketCreateServerHandle.cs b/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketCreateServerHandle.cs index 46fb2f4679..995e9462e3 100644 --- a/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketCreateServerHandle.cs +++ b/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketCreateServerHandle.cs @@ -11,7 +11,6 @@ internal static partial class Interop internal static partial class WebSocket { [DllImport(Libraries.WebSocket)] - [SuppressUnmanagedCodeSecurity] internal static extern int WebSocketCreateServerHandle( [In]Property[] properties, [In] uint propertyCount, diff --git a/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketDeleteHandle.cs b/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketDeleteHandle.cs index 75e27a4f09..6f57d9ebdc 100644 --- a/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketDeleteHandle.cs +++ b/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketDeleteHandle.cs @@ -11,7 +11,6 @@ internal static partial class Interop internal static partial class WebSocket { [DllImport(Libraries.WebSocket)] - [SuppressUnmanagedCodeSecurity] internal static extern void WebSocketDeleteHandle( [In] IntPtr webSocketHandle); } diff --git a/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketEndServerHandshake.cs b/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketEndServerHandshake.cs index 8a1ff1bcaa..3323039a5a 100644 --- a/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketEndServerHandshake.cs +++ b/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketEndServerHandshake.cs @@ -13,7 +13,6 @@ internal static partial class Interop internal static partial class WebSocket { [DllImport(Libraries.WebSocket)] - [SuppressUnmanagedCodeSecurity] internal static extern int WebSocketEndServerHandshake([In] SafeHandle webSocketHandle); } } diff --git a/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketGetAction.cs b/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketGetAction.cs index 2c3c7ef632..114574a2db 100644 --- a/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketGetAction.cs +++ b/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketGetAction.cs @@ -12,7 +12,6 @@ internal static partial class Interop internal static partial class WebSocket { [DllImport(Libraries.WebSocket)] - [SuppressUnmanagedCodeSecurity] internal static extern int WebSocketGetAction( [In] SafeHandle webSocketHandle, [In] ActionQueue actionQueue, diff --git a/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketReceive.cs b/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketReceive.cs index d3566aba99..e29dedb4b7 100644 --- a/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketReceive.cs +++ b/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketReceive.cs @@ -11,7 +11,6 @@ internal static partial class Interop internal static partial class WebSocket { [DllImport(Libraries.WebSocket)] - [SuppressUnmanagedCodeSecurity] internal static extern int WebSocketReceive( [In] SafeHandle webSocketHandle, [In] IntPtr buffers, diff --git a/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketSend.cs b/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketSend.cs index 6b83a71f93..d3ecc8ff1b 100644 --- a/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketSend.cs +++ b/external/corefx/src/Common/src/Interop/Windows/WebSocket/Interop.WebSocketSend.cs @@ -12,7 +12,6 @@ internal static partial class Interop internal static partial class WebSocket { [DllImport(Libraries.WebSocket, EntryPoint = "WebSocketSend", ExactSpelling = true)] - [SuppressUnmanagedCodeSecurity] internal static extern int WebSocketSend_Raw( [In] SafeHandle webSocketHandle, [In] BufferType bufferType, @@ -20,7 +19,6 @@ internal static partial class Interop [In] IntPtr applicationContext); [DllImport(Libraries.WebSocket, EntryPoint = "WebSocketSend", ExactSpelling = true)] - [SuppressUnmanagedCodeSecurity] internal static extern int WebSocketSendWithoutBody_Raw( [In] SafeHandle webSocketHandle, [In] BufferType bufferType, diff --git a/external/corefx/src/Common/src/Interop/Windows/advapi32/Interop.CryptGetProvParam.cs b/external/corefx/src/Common/src/Interop/Windows/advapi32/Interop.CryptGetProvParam.cs new file mode 100644 index 0000000000..2ad575297e --- /dev/null +++ b/external/corefx/src/Common/src/Interop/Windows/advapi32/Interop.CryptGetProvParam.cs @@ -0,0 +1,67 @@ +// 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.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Advapi32 + { + internal enum CryptProvParam : int + { + PP_CLIENT_HWND = 1, + PP_IMPTYPE = 3, + PP_NAME = 4, + PP_CONTAINER = 6, + PP_PROVTYPE = 16, + PP_KEYSET_TYPE = 27, + PP_KEYEXCHANGE_PIN = 32, + PP_SIGNATURE_PIN = 33, + PP_UNIQUE_CONTAINER = 36 + } + + [DllImport(Libraries.Advapi32, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CryptGetProvParam( + SafeHandle safeProvHandle, + CryptProvParam dwParam, + IntPtr pbData, + ref int dwDataLen, + int dwFlags); + + [DllImport(Libraries.Advapi32, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CryptSetProvParam( + SafeHandle safeProvHandle, + CryptProvParam dwParam, + IntPtr pbData, + int dwFlags); + + public static bool CryptGetProvParam( + SafeHandle safeProvHandle, + CryptProvParam dwParam, + Span pbData, + ref int dwDataLen) + { + if (pbData.IsEmpty) + { + return CryptGetProvParam(safeProvHandle, dwParam, IntPtr.Zero, ref dwDataLen, 0); + } + + if (dwDataLen > pbData.Length) + { + throw new IndexOutOfRangeException(); + } + + unsafe + { + fixed (byte* bytePtr = &MemoryMarshal.GetReference(pbData)) + { + return CryptGetProvParam(safeProvHandle, dwParam, (IntPtr)bytePtr, ref dwDataLen, 0); + } + } + } + } +} diff --git a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs index e89fbff9dd..d7c5af958f 100644 --- a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs +++ b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs @@ -8,13 +8,13 @@ internal partial class Interop { internal partial class Kernel32 { - internal struct CREATEFILE2_EXTENDED_PARAMETERS + internal unsafe struct CREATEFILE2_EXTENDED_PARAMETERS { internal uint dwSize; internal uint dwFileAttributes; internal uint dwFileFlags; internal uint dwSecurityQosFlags; - internal IntPtr lpSecurityAttributes; + internal Interop.Kernel32.SECURITY_ATTRIBUTES* lpSecurityAttributes; internal IntPtr hTemplateFile; } } diff --git a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.CompareStringOrdinal.cs b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.CompareStringOrdinal.cs new file mode 100644 index 0000000000..b0cef5dafd --- /dev/null +++ b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.CompareStringOrdinal.cs @@ -0,0 +1,20 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + // https://msdn.microsoft.com/en-us/library/windows/desktop/dd317762.aspx + [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)] + public unsafe static extern int CompareStringOrdinal( + ref char lpString1, + int cchCount1, + ref char lpString2, + int cchCount2, + bool bIgnoreCase); + } +} diff --git a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.CreateFile.cs b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.CreateFile.cs index cc0585cf1e..9c3639d6e2 100644 --- a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.CreateFile.cs +++ b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.CreateFile.cs @@ -11,42 +11,73 @@ internal partial class Interop { internal partial class Kernel32 { + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858.aspx /// /// WARNING: The private methods do not implicitly handle long paths. Use CreateFile. /// [DllImport(Libraries.Kernel32, EntryPoint = "CreateFileW", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false, ExactSpelling = true)] - private static extern SafeFileHandle CreateFilePrivate( + private unsafe static extern IntPtr CreateFilePrivate( string lpFileName, int dwDesiredAccess, FileShare dwShareMode, - [In] ref SECURITY_ATTRIBUTES securityAttrs, + SECURITY_ATTRIBUTES* securityAttrs, FileMode dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile); - internal static SafeFileHandle CreateFile( + internal unsafe static SafeFileHandle CreateFile( string lpFileName, int dwDesiredAccess, FileShare dwShareMode, - [In] ref SECURITY_ATTRIBUTES securityAttrs, + ref SECURITY_ATTRIBUTES securityAttrs, FileMode dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile) { lpFileName = PathInternal.EnsureExtendedPrefixOverMaxPath(lpFileName); - return CreateFilePrivate(lpFileName, dwDesiredAccess, dwShareMode, ref securityAttrs, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); + fixed (SECURITY_ATTRIBUTES* sa = &securityAttrs) + { + IntPtr handle = CreateFilePrivate(lpFileName, dwDesiredAccess, dwShareMode, sa, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); + try + { + return new SafeFileHandle(handle, ownsHandle: true); + } + catch + { + CloseHandle(handle); + throw; + } + } } - internal static SafeFileHandle CreateFileDefaultSecurity( + internal unsafe static SafeFileHandle CreateFile( string lpFileName, int dwDesiredAccess, FileShare dwShareMode, FileMode dwCreationDisposition, int dwFlagsAndAttributes) { - // This is technically equivalent to passing a null SECURITY_ATTRIBUTES - SECURITY_ATTRIBUTES security = new SECURITY_ATTRIBUTES(); - return CreateFilePrivate(lpFileName, dwDesiredAccess, dwShareMode, ref security, dwCreationDisposition, dwFlagsAndAttributes, IntPtr.Zero); + IntPtr handle = CreateFile_IntPtr(lpFileName, dwDesiredAccess, dwShareMode, dwCreationDisposition, dwFlagsAndAttributes); + try + { + return new SafeFileHandle(handle, ownsHandle: true); + } + catch + { + CloseHandle(handle); + throw; + } + } + + internal unsafe static IntPtr CreateFile_IntPtr( + string lpFileName, + int dwDesiredAccess, + FileShare dwShareMode, + FileMode dwCreationDisposition, + int dwFlagsAndAttributes) + { + lpFileName = PathInternal.EnsureExtendedPrefixOverMaxPath(lpFileName); + return CreateFilePrivate(lpFileName, dwDesiredAccess, dwShareMode, null, dwCreationDisposition, dwFlagsAndAttributes, IntPtr.Zero); } } } diff --git a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.CreateFile2.cs b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.CreateFile2.cs index 7650364dd8..ed691ace46 100644 --- a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.CreateFile2.cs +++ b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.CreateFile2.cs @@ -4,18 +4,96 @@ using Microsoft.Win32.SafeHandles; using System; +using System.IO; using System.Runtime.InteropServices; internal partial class Interop { internal partial class Kernel32 { + // https://msdn.microsoft.com/en-us/library/windows/desktop/hh449422.aspx [DllImport(Libraries.Kernel32, EntryPoint = "CreateFile2", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)] - internal static extern SafeFileHandle CreateFile2( + internal unsafe static extern IntPtr CreateFile2( string lpFileName, int dwDesiredAccess, - System.IO.FileShare dwShareMode, - System.IO.FileMode dwCreationDisposition, + FileShare dwShareMode, + FileMode dwCreationDisposition, [In] ref CREATEFILE2_EXTENDED_PARAMETERS parameters); + + private static unsafe IntPtr CreateFile( + string lpFileName, + int dwDesiredAccess, + FileShare dwShareMode, + SECURITY_ATTRIBUTES* securityAttrs, + FileMode dwCreationDisposition, + int dwFlagsAndAttributes, + IntPtr hTemplateFile) + { + CREATEFILE2_EXTENDED_PARAMETERS parameters; + parameters.dwSize = (uint)Marshal.SizeOf(); + + parameters.dwFileAttributes = (uint)dwFlagsAndAttributes & 0x0000FFFF; + parameters.dwSecurityQosFlags = (uint)dwFlagsAndAttributes & 0x000F0000; + parameters.dwFileFlags = (uint)dwFlagsAndAttributes & 0xFFF00000; + + parameters.hTemplateFile = hTemplateFile; + parameters.lpSecurityAttributes = securityAttrs; + + return CreateFile2(lpFileName, dwDesiredAccess, dwShareMode, dwCreationDisposition, ref parameters); + } + + internal static unsafe SafeFileHandle CreateFile( + string lpFileName, + int dwDesiredAccess, + FileShare dwShareMode, + ref SECURITY_ATTRIBUTES securityAttrs, + FileMode dwCreationDisposition, + int dwFlagsAndAttributes, + IntPtr hTemplateFile) + { + fixed (SECURITY_ATTRIBUTES* lpSecurityAttributes = &securityAttrs) + { + IntPtr handle = CreateFile(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); + try + { + return new SafeFileHandle(handle, ownsHandle: true); + } + catch + { + CloseHandle(handle); + throw; + } + } + } + + internal static unsafe SafeFileHandle CreateFile( + string lpFileName, + int dwDesiredAccess, + FileShare dwShareMode, + FileMode dwCreationDisposition, + int dwFlagsAndAttributes) + { + IntPtr handle = CreateFile(lpFileName, dwDesiredAccess, dwShareMode, null, dwCreationDisposition, dwFlagsAndAttributes, IntPtr.Zero); + try + { + return new SafeFileHandle(handle, ownsHandle: true); + } + catch + { + CloseHandle(handle); + throw; + } + } + + internal unsafe static IntPtr CreateFile_IntPtr( + string lpFileName, + int dwDesiredAccess, + FileShare dwShareMode, + FileMode dwCreationDisposition, + int dwFlagsAndAttributes) + { + lpFileName = PathInternal.EnsureExtendedPrefixOverMaxPath(lpFileName); + return CreateFile(lpFileName, dwDesiredAccess, dwShareMode, null, dwCreationDisposition, dwFlagsAndAttributes, IntPtr.Zero); + } } } diff --git a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.CreateNamedPipeClient.Uap.cs b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.CreateNamedPipeClient.Uap.cs index e5890ce0b8..a27cc2407f 100644 --- a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.CreateNamedPipeClient.Uap.cs +++ b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.CreateNamedPipeClient.Uap.cs @@ -44,7 +44,7 @@ internal partial class Interop parameters.hTemplateFile = hTemplateFile; fixed (Interop.Kernel32.SECURITY_ATTRIBUTES* lpSecurityAttributes = &secAttrs) { - parameters.lpSecurityAttributes = (IntPtr)lpSecurityAttributes; + parameters.lpSecurityAttributes = lpSecurityAttributes; return CreateNamedPipeClientPrivate(lpFileName, dwDesiredAccess, dwShareMode, dwCreationDisposition, ref parameters); } } diff --git a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.FILE_INFO_BY_HANDLE_CLASS.cs b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.FILE_INFO_BY_HANDLE_CLASS.cs new file mode 100644 index 0000000000..53ed7484b0 --- /dev/null +++ b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.FILE_INFO_BY_HANDLE_CLASS.cs @@ -0,0 +1,153 @@ +// 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. + +internal partial class Interop +{ + internal partial class Kernel32 + { + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364228.aspx + internal enum FILE_INFO_BY_HANDLE_CLASS : uint + { + // Up to FileRemoteProtocolInfo available in Windows 7 + + /// + /// Returns basic information (timestamps and attributes). + /// + /// + /// Thunks to NtQueryInformationFile and FileBasicInformation. + /// + FileBasicInfo, + + /// + /// Returns file size, link count, pending delete status, and if it is a directory. + /// + /// + /// Thunks to NtQueryInformationFile and FileStandardInformation. + /// + FileStandardInfo, + + /// + /// Gets the file name. + /// + /// + /// Thunks to NtQueryInformationFile and FileNameInformation. + /// + FileNameInfo, + + /// + /// Renames a file. Allows renaming a file without having to specify a full path, if you have + /// a handle to the directory the file resides in. + /// + /// + /// Only valid for SetFileInformationByHandle. Thunks to NtSetInformationFile and FileRenameInformation. + /// MoveFileEx is effectively the same API. + /// + FileRenameInfo, + + /// + /// Allows marking a file handle for deletion. Handle must have been opened with Delete access. + /// You cannot change the state of a handle opened with DeleteOnClose. + /// + /// + /// Only valid for SetFileInformationByHandle. Thunks to NtSetInformationFile and FileDispositionInformation. + /// DeleteFile is effectively the same API. + /// + FileDispositionInfo, + + /// + /// Allows setting the allocated size of the file. + /// + /// + /// Only valid for SetFileInformationByHandle. Thunks to NtSetInformationFile. + /// SetEndOfFile sets this after setting the logical end of file to the current position via FileEndOfFileInfo. + /// + FileAllocationInfo, + + /// + /// Allows setting the end of file. + /// + /// + /// Only valid for SetFileInformationByHandle. Thunks to NtSetInformationFile. + /// SetEndOfFile calls this to set the logical end of file to whatever the current position is. + /// + FileEndOfFileInfo, + + /// + /// Gets stream information for the file. + /// + /// + /// Thunks to NtQueryInformationFile and FileStreamInformation. + /// + FileStreamInfo, + + /// + /// Gets compression information for the file. + /// + /// + /// Thunks to NtQueryInformationFile and FileCompressionInformation. + /// + FileCompressionInfo, + + /// + /// Gets the file attributes and reparse tag. + /// + /// + /// Thunks to NtQueryInformationFile and FileAttributeTagInformation. + /// + FileAttributeTagInfo, + + /// + /// Starts a query for for file information in a directory. + /// + /// + /// Thunks to NtQueryDirectoryFile and FileIdBothDirectoryInformation with RestartScan + /// set to false. + /// + FileIdBothDirectoryInfo, + + /// + /// Resumes a query for file information in a directory. + /// + /// + /// Thunks to NtQueryDirectoryFile and FileIdBothDirectoryInformation with RestartScan + /// set to true. + /// + FileIdBothDirectoryRestartInfo, + + /// + /// Allows setting the priority hint for a file. + /// + /// + /// Only valid for SetFileInformationByHandle. Thunks to NtSetInformationFile. + /// + FileIoPriorityHintInfo, + + /// + /// Gets the file remote protocol information. + /// + /// + /// Thunks to NtQueryInformationFile and FileRemoteProtocolInformation. + /// + FileRemoteProtocolInfo, + + /// + /// Starts a query for for file information in a directory. Uses FILE_FULL_DIR_INFO. + /// + /// + /// Thunks to NtQueryDirectoryFile and FileFullDirectoryInformation with RestartScan + /// set to false. Windows 8 and up. + /// + FileFullDirectoryInfo, + + /// + /// Resumes a query for file information in a directory. Uses FILE_FULL_DIR_INFO. + /// + /// + /// Thunks to NtQueryDirectoryFile and FileFullDirectoryInformation with RestartScan + /// set to true. Windows 8 and up. + /// + FileFullDirectoryRestartInfo + } + } +} diff --git a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.FILE_TIME.cs b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.FILE_TIME.cs index 02cdef199f..d3b055e287 100644 --- a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.FILE_TIME.cs +++ b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.FILE_TIME.cs @@ -2,10 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.Win32.SafeHandles; using System; -using System.IO; -using System.Runtime.InteropServices; internal partial class Interop { @@ -22,10 +19,9 @@ internal partial class Interop dwHighDateTime = (uint)(fileTime >> 32); } - internal long ToTicks() - { - return ((long)dwHighDateTime << 32) + dwLowDateTime; - } + internal long ToTicks() => ((long)dwHighDateTime << 32) + dwLowDateTime; + internal DateTime ToDateTimeUtc() => DateTime.FromFileTimeUtc(ToTicks()); + internal DateTimeOffset ToDateTimeOffset() => DateTimeOffset.FromFileTime(ToTicks()); } } } diff --git a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.GetFileInformationByHandleEx.cs b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.GetFileInformationByHandleEx.cs new file mode 100644 index 0000000000..b41d261703 --- /dev/null +++ b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.GetFileInformationByHandleEx.cs @@ -0,0 +1,20 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364953.aspx + [DllImport(Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)] + public unsafe static extern bool GetFileInformationByHandleEx( + IntPtr hFile, + FILE_INFO_BY_HANDLE_CLASS FileInformationClass, + byte[] lpFileInformation, + uint dwBufferSize); + } +} diff --git a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.GetLogicalProcessorInformationEx.cs b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.GetLogicalProcessorInformationEx.cs deleted file mode 100644 index 9110d5e8a3..0000000000 --- a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.GetLogicalProcessorInformationEx.cs +++ /dev/null @@ -1,46 +0,0 @@ -// 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.Runtime.InteropServices; - -#pragma warning disable 0649 // fields never explicitly assigned to -#pragma warning disable 0169 // fields never used - -internal partial class Interop -{ - internal partial class Kernel32 - { - [DllImport(Libraries.Kernel32, SetLastError = true)] - internal static extern bool GetLogicalProcessorInformationEx(LOGICAL_PROCESSOR_RELATIONSHIP RelationshipType, IntPtr Buffer, ref uint ReturnedLength); - - internal enum LOGICAL_PROCESSOR_RELATIONSHIP - { - RelationGroup = 4 - } - - internal struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX - { - public LOGICAL_PROCESSOR_RELATIONSHIP Relationship; - public uint Size; - public GROUP_RELATIONSHIP Group; // part of a union, but we only need the Group - } - - internal unsafe struct GROUP_RELATIONSHIP - { - private byte MaximumGroupCount; - public ushort ActiveGroupCount; - private fixed byte Reserved[20]; - public PROCESSOR_GROUP_INFO GroupInfo; // actually a GroupInfo[ANYSIZE_ARRAY], so used for its address - } - - internal unsafe struct PROCESSOR_GROUP_INFO - { - public byte MaximumProcessorCount; - public byte ActiveProcessorCount; - public fixed byte Reserved[38]; - public IntPtr ActiveProcessorMask; - } - } -} diff --git a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.GetSystemDirectoryW.cs b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.GetSystemDirectoryW.cs index 25c59d743b..cf1b31d62c 100644 --- a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.GetSystemDirectoryW.cs +++ b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.GetSystemDirectoryW.cs @@ -14,7 +14,7 @@ internal static partial class Interop internal static unsafe int GetSystemDirectoryW(Span buffer) { - fixed (char* bufferPtr = &buffer.DangerousGetPinnableReference()) + fixed (char* bufferPtr = &MemoryMarshal.GetReference(buffer)) { return GetSystemDirectoryW(bufferPtr, buffer.Length); } diff --git a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.SafeCreateFile.cs b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.SafeCreateFile.cs deleted file mode 100644 index 78f382cc01..0000000000 --- a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.SafeCreateFile.cs +++ /dev/null @@ -1,43 +0,0 @@ -// 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 Microsoft.Win32.SafeHandles; -using System; -using System.IO; - -internal partial class Interop -{ - internal partial class Kernel32 - { - /// - /// Does not allow access to non-file devices. This disallows DOS devices like "con:", "com1:", - /// "lpt1:", etc. Use this to avoid security problems, like allowing a web client asking a server - /// for "http://server/com1.aspx" and then causing a worker process to hang. - /// - [System.Security.SecurityCritical] // auto-generated - internal static SafeFileHandle SafeCreateFile( - string lpFileName, - int dwDesiredAccess, - System.IO.FileShare dwShareMode, - ref SECURITY_ATTRIBUTES securityAttrs, - FileMode dwCreationDisposition, - int dwFlagsAndAttributes, - IntPtr hTemplateFile) - { - SafeFileHandle handle = UnsafeCreateFile(lpFileName, dwDesiredAccess, dwShareMode, ref securityAttrs, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); - - if (!handle.IsInvalid) - { - int fileType = GetFileType(handle); - if (fileType != FileTypes.FILE_TYPE_DISK) - { - handle.Dispose(); - throw new NotSupportedException(SR.NotSupported_FileStreamOnNonFiles); - } - } - - return handle; - } - } -} diff --git a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.SetFileInformationByHandle.cs b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.SetFileInformationByHandle.cs index 0d7efe9d74..14a98a52ce 100644 --- a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.SetFileInformationByHandle.cs +++ b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.SetFileInformationByHandle.cs @@ -10,7 +10,8 @@ internal partial class Interop { internal partial class Kernel32 { - [DllImport(Libraries.Kernel32, SetLastError = true)] + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa365539.aspx + [DllImport(Libraries.Kernel32, SetLastError = true, ExactSpelling = true)] internal static extern bool SetFileInformationByHandle(SafeFileHandle hFile, FILE_INFO_BY_HANDLE_CLASS FileInformationClass, ref FILE_BASIC_INFO lpFileInformation, uint dwBufferSize); // Default values indicate "no change". Use defaults so that we don't force callsites to be aware of the default values @@ -42,31 +43,5 @@ internal partial class Interop internal long ChangeTime; internal uint FileAttributes; } - - internal enum FILE_INFO_BY_HANDLE_CLASS : uint - { - FileBasicInfo = 0x0u, - FileStandardInfo = 0x1u, - FileNameInfo = 0x2u, - FileRenameInfo = 0x3u, - FileDispositionInfo = 0x4u, - FileAllocationInfo = 0x5u, - FileEndOfFileInfo = 0x6u, - FileStreamInfo = 0x7u, - FileCompressionInfo = 0x8u, - FileAttributeTagInfo = 0x9u, - FileIdBothDirectoryInfo = 0xAu, - FileIdBothDirectoryRestartInfo = 0xBu, - FileIoPriorityHintInfo = 0xCu, - FileRemoteProtocolInfo = 0xDu, - FileFullDirectoryInfo = 0xEu, - FileFullDirectoryRestartInfo = 0xFu, - FileStorageInfo = 0x10u, - FileAlignmentInfo = 0x11u, - FileIdInfo = 0x12u, - FileIdExtdDirectoryInfo = 0x13u, - FileIdExtdDirectoryRestartInfo = 0x14u, - MaximumFileInfoByHandleClass = 0x15u, - } } } diff --git a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.UnsafeCreateFile.Uap.cs b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.UnsafeCreateFile.Uap.cs deleted file mode 100644 index 9dae42b879..0000000000 --- a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.UnsafeCreateFile.Uap.cs +++ /dev/null @@ -1,38 +0,0 @@ -// 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.Runtime.InteropServices; -using Microsoft.Win32.SafeHandles; - -internal partial class Interop -{ - internal partial class Kernel32 - { - internal static unsafe SafeFileHandle UnsafeCreateFile( - string lpFileName, - int dwDesiredAccess, - System.IO.FileShare dwShareMode, - ref Interop.Kernel32.SECURITY_ATTRIBUTES securityAttrs, - System.IO.FileMode dwCreationDisposition, - int dwFlagsAndAttributes, - IntPtr hTemplateFile) - { - Interop.Kernel32.CREATEFILE2_EXTENDED_PARAMETERS parameters; - parameters.dwSize = (uint)Marshal.SizeOf(); - - parameters.dwFileAttributes = (uint)dwFlagsAndAttributes & 0x0000FFFF; - parameters.dwSecurityQosFlags = (uint)dwFlagsAndAttributes & 0x000F0000; - parameters.dwFileFlags = (uint)dwFlagsAndAttributes & 0xFFF00000; - - parameters.hTemplateFile = hTemplateFile; - fixed (Interop.Kernel32.SECURITY_ATTRIBUTES* lpSecurityAttributes = &securityAttrs) - { - parameters.lpSecurityAttributes = (IntPtr)lpSecurityAttributes; - return Interop.Kernel32.CreateFile2(lpFileName, dwDesiredAccess, dwShareMode, dwCreationDisposition, ref parameters); - } - } - } -} diff --git a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.UnsafeCreateFile.cs b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.UnsafeCreateFile.cs deleted file mode 100644 index b0c2c082c3..0000000000 --- a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.UnsafeCreateFile.cs +++ /dev/null @@ -1,26 +0,0 @@ -// 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 Microsoft.Win32.SafeHandles; - -internal partial class Interop -{ - internal partial class Kernel32 - { - [System.Security.SecurityCritical] // auto-generated - internal static SafeFileHandle UnsafeCreateFile( - string lpFileName, - int dwDesiredAccess, - FileShare dwShareMode, - ref Interop.Kernel32.SECURITY_ATTRIBUTES securityAttrs, - FileMode dwCreationDisposition, - int dwFlagsAndAttributes, - IntPtr hTemplateFile) - { - return CreateFile(lpFileName, dwDesiredAccess, dwShareMode, ref securityAttrs, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); - } - } -} diff --git a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.WIN32_FILE_ATTRIBUTE_DATA.cs b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.WIN32_FILE_ATTRIBUTE_DATA.cs index 8e09555477..971b311244 100644 --- a/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.WIN32_FILE_ATTRIBUTE_DATA.cs +++ b/external/corefx/src/Common/src/Interop/Windows/kernel32/Interop.WIN32_FILE_ATTRIBUTE_DATA.cs @@ -8,28 +8,21 @@ internal partial class Interop { internal struct WIN32_FILE_ATTRIBUTE_DATA { - internal int fileAttributes; - internal uint ftCreationTimeLow; - internal uint ftCreationTimeHigh; - internal uint ftLastAccessTimeLow; - internal uint ftLastAccessTimeHigh; - internal uint ftLastWriteTimeLow; - internal uint ftLastWriteTimeHigh; - internal uint fileSizeHigh; - internal uint fileSizeLow; + internal int dwFileAttributes; + internal FILE_TIME ftCreationTime; + internal FILE_TIME ftLastAccessTime; + internal FILE_TIME ftLastWriteTime; + internal uint nFileSizeHigh; + internal uint nFileSizeLow; internal void PopulateFrom(ref WIN32_FIND_DATA findData) { - // Copy the information to data - fileAttributes = (int)findData.dwFileAttributes; - ftCreationTimeLow = findData.ftCreationTime.dwLowDateTime; - ftCreationTimeHigh = findData.ftCreationTime.dwHighDateTime; - ftLastAccessTimeLow = findData.ftLastAccessTime.dwLowDateTime; - ftLastAccessTimeHigh = findData.ftLastAccessTime.dwHighDateTime; - ftLastWriteTimeLow = findData.ftLastWriteTime.dwLowDateTime; - ftLastWriteTimeHigh = findData.ftLastWriteTime.dwHighDateTime; - fileSizeHigh = findData.nFileSizeHigh; - fileSizeLow = findData.nFileSizeLow; + dwFileAttributes = (int)findData.dwFileAttributes; + ftCreationTime = findData.ftCreationTime; + ftLastAccessTime = findData.ftLastAccessTime; + ftLastWriteTime = findData.ftLastWriteTime; + nFileSizeHigh = findData.nFileSizeHigh; + nFileSizeLow = findData.nFileSizeLow; } } } diff --git a/external/corefx/src/Common/src/Interop/Windows/kernelbase/Interop.OpenCommPort.cs b/external/corefx/src/Common/src/Interop/Windows/mincore/Interop.OpenCommPort.cs similarity index 83% rename from external/corefx/src/Common/src/Interop/Windows/kernelbase/Interop.OpenCommPort.cs rename to external/corefx/src/Common/src/Interop/Windows/mincore/Interop.OpenCommPort.cs index e1bbbc2388..faf37cfd50 100644 --- a/external/corefx/src/Common/src/Interop/Windows/kernelbase/Interop.OpenCommPort.cs +++ b/external/corefx/src/Common/src/Interop/Windows/mincore/Interop.OpenCommPort.cs @@ -9,9 +9,9 @@ using System.Runtime.InteropServices; internal partial class Interop { - internal partial class KernelBase + internal partial class mincore { - [DllImport(Libraries.KernelBase, SetLastError = true)] + [DllImport(Libraries.CoreComm_L1_1_1, SetLastError = true)] internal static extern SafeFileHandle OpenCommPort( uint uPortNumber, int dwDesiredAccess, diff --git a/external/corefx/src/Common/src/Interop/Windows/sspicli/Interop.SSPI.cs b/external/corefx/src/Common/src/Interop/Windows/sspicli/Interop.SSPI.cs index a08b49dba7..99457c511b 100644 --- a/external/corefx/src/Common/src/Interop/Windows/sspicli/Interop.SSPI.cs +++ b/external/corefx/src/Common/src/Interop/Windows/sspicli/Interop.SSPI.cs @@ -54,6 +54,7 @@ internal static partial class Interop SECPKG_ATTR_UNIQUE_BINDINGS = 25, SECPKG_ATTR_ENDPOINT_BINDINGS = 26, SECPKG_ATTR_CLIENT_SPECIFIED_TARGET = 27, + SECPKG_ATTR_APPLICATION_PROTOCOL = 35, // minschannel.h SECPKG_ATTR_REMOTE_CERT_CONTEXT = 0x53, // returns PCCERT_CONTEXT diff --git a/external/corefx/src/Common/src/Interop/Windows/sspicli/SSPIWrapper.cs b/external/corefx/src/Common/src/Interop/Windows/sspicli/SSPIWrapper.cs index 16e3f6231d..ab47ef5344 100644 --- a/external/corefx/src/Common/src/Interop/Windows/sspicli/SSPIWrapper.cs +++ b/external/corefx/src/Common/src/Interop/Windows/sspicli/SSPIWrapper.cs @@ -476,6 +476,10 @@ namespace System.Net nativeBlockSize = Marshal.SizeOf(); break; + case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_APPLICATION_PROTOCOL: + nativeBlockSize = Marshal.SizeOf(); + break; + default: throw new ArgumentException(SR.Format(SR.net_invalid_enum, nameof(contextAttribute)), nameof(contextAttribute)); } @@ -540,6 +544,17 @@ namespace System.Net case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_CONNECTION_INFO: attribute = new SecPkgContext_ConnectionInfo(nativeBuffer); break; + + case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_APPLICATION_PROTOCOL: + unsafe + { + fixed (void *ptr = nativeBuffer) + { + attribute = Marshal.PtrToStructure(new IntPtr(ptr)); + } + } + break; + default: // Will return null. break; diff --git a/external/corefx/src/Common/src/Microsoft/Internal/CommonStrings.Designer.cs b/external/corefx/src/Common/src/Microsoft/Internal/CommonStrings.Designer.cs index 38afaaabce..105f240909 100644 --- a/external/corefx/src/Common/src/Microsoft/Internal/CommonStrings.Designer.cs +++ b/external/corefx/src/Common/src/Microsoft/Internal/CommonStrings.Designer.cs @@ -8,11 +8,11 @@ // //------------------------------------------------------------------------------ -namespace Microsoft.Internal { - using System; +namespace Microsoft.Internal +{ using System.Reflection; - - + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// diff --git a/external/corefx/src/Common/src/Microsoft/Internal/Requires.cs b/external/corefx/src/Common/src/Microsoft/Internal/Requires.cs index e2e3a065eb..b5ddb4778d 100644 --- a/external/corefx/src/Common/src/Microsoft/Internal/Requires.cs +++ b/external/corefx/src/Common/src/Microsoft/Internal/Requires.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Composition; using System.Diagnostics; using System.Diagnostics.Contracts; using System.Globalization; @@ -12,7 +11,7 @@ using System.Reflection; namespace Microsoft.Internal { - internal static class Requires + internal static partial class Requires { [DebuggerStepThrough] [ContractArgumentValidator] diff --git a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/Asn1SafeHandles.Unix.cs b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/Asn1SafeHandles.Unix.cs index 22fa7da163..3fdde3f602 100644 --- a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/Asn1SafeHandles.Unix.cs +++ b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/Asn1SafeHandles.Unix.cs @@ -8,7 +8,6 @@ using System.Security; namespace Microsoft.Win32.SafeHandles { - [SecurityCritical] internal sealed class SafeAsn1ObjectHandle : SafeHandle { private SafeAsn1ObjectHandle() : @@ -29,7 +28,6 @@ namespace Microsoft.Win32.SafeHandles } } - [SecurityCritical] internal sealed class SafeAsn1BitStringHandle : SafeHandle { private SafeAsn1BitStringHandle() : @@ -50,7 +48,6 @@ namespace Microsoft.Win32.SafeHandles } } - [SecurityCritical] internal sealed class SafeAsn1OctetStringHandle : SafeHandle { private SafeAsn1OctetStringHandle() : @@ -71,7 +68,6 @@ namespace Microsoft.Win32.SafeHandles } } - [SecurityCritical] internal sealed class SafeAsn1StringHandle : SafeHandle { private SafeAsn1StringHandle() : diff --git a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeBignumHandle.Unix.cs b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeBignumHandle.Unix.cs index 46a266e961..f7afbff512 100644 --- a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeBignumHandle.Unix.cs +++ b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeBignumHandle.Unix.cs @@ -8,7 +8,6 @@ using System.Security; namespace Microsoft.Win32.SafeHandles { - [SecurityCritical] internal sealed class SafeBignumHandle : SafeHandle { private SafeBignumHandle() : diff --git a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeBioHandle.Unix.cs b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeBioHandle.Unix.cs index 0077a2c9e5..18079b7470 100644 --- a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeBioHandle.Unix.cs +++ b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeBioHandle.Unix.cs @@ -9,7 +9,6 @@ using System.Diagnostics; namespace Microsoft.Win32.SafeHandles { - [SecurityCritical] internal sealed class SafeBioHandle : SafeHandle { private SafeHandle _parent; diff --git a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeBrotliHandle.cs b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeBrotliHandle.cs new file mode 100644 index 0000000000..5907901f84 --- /dev/null +++ b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeBrotliHandle.cs @@ -0,0 +1,35 @@ +// 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.Runtime.InteropServices; + +namespace Microsoft.Win32.SafeHandles +{ + internal sealed class SafeBrotliEncoderHandle : SafeHandle + { + public SafeBrotliEncoderHandle() : base(IntPtr.Zero, true) { } + + protected override bool ReleaseHandle() + { + Interop.Brotli.BrotliEncoderDestroyInstance(handle); + return true; + } + + public override bool IsInvalid => handle == IntPtr.Zero; + } + + internal sealed class SafeBrotliDecoderHandle : SafeHandle + { + public SafeBrotliDecoderHandle() : base(IntPtr.Zero, true) { } + + protected override bool ReleaseHandle() + { + Interop.Brotli.BrotliDecoderDestroyInstance(handle); + return true; + } + + public override bool IsInvalid => handle == IntPtr.Zero; + } +} diff --git a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeCreateHandle.OSX.cs b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeCreateHandle.OSX.cs index 5cc4927c10..7eca7a0252 100644 --- a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeCreateHandle.OSX.cs +++ b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeCreateHandle.OSX.cs @@ -13,7 +13,6 @@ namespace Microsoft.Win32.SafeHandles /// if a Create* function is called, the caller must also CFRelease /// on the same pointer in order to correctly free the memory. /// - [System.Security.SecurityCritical] internal sealed partial class SafeCreateHandle : SafeHandle { internal SafeCreateHandle() : base(IntPtr.Zero, true) { } @@ -23,7 +22,6 @@ namespace Microsoft.Win32.SafeHandles this.SetHandle(ptr); } - [System.Security.SecurityCritical] protected override bool ReleaseHandle() { Interop.CoreFoundation.CFRelease(handle); @@ -33,7 +31,6 @@ namespace Microsoft.Win32.SafeHandles public override bool IsInvalid { - [System.Security.SecurityCritical] get { return handle == IntPtr.Zero; diff --git a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeDsaHandle.Unix.cs b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeDsaHandle.Unix.cs index 2c08f366e8..627cc75181 100644 --- a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeDsaHandle.Unix.cs +++ b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeDsaHandle.Unix.cs @@ -9,7 +9,6 @@ using System.Runtime.InteropServices; namespace Microsoft.Win32.SafeHandles { - [SecurityCritical] internal sealed class SafeDsaHandle : SafeHandle { private SafeDsaHandle() : diff --git a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeEcKeyHandle.Unix.cs b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeEcKeyHandle.Unix.cs index e0c30dbf14..1f470a9797 100644 --- a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeEcKeyHandle.Unix.cs +++ b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeEcKeyHandle.Unix.cs @@ -9,7 +9,6 @@ using System.Runtime.InteropServices; namespace Microsoft.Win32.SafeHandles { - [SecurityCritical] internal sealed class SafeEcKeyHandle : SafeHandle { private SafeEcKeyHandle() : diff --git a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeEventStreamHandle.OSX.cs b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeEventStreamHandle.OSX.cs index aadb7901b6..e6e8e1aa75 100644 --- a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeEventStreamHandle.OSX.cs +++ b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeEventStreamHandle.OSX.cs @@ -14,7 +14,6 @@ namespace Microsoft.Win32.SafeHandles /// and CFRelease to free; however, FSEventStream has it's own release /// function, so we need to extend the pattern to account for that. /// - [System.Security.SecurityCritical] internal sealed partial class SafeEventStreamHandle : SafeHandle { internal SafeEventStreamHandle() : base(IntPtr.Zero, true) { } @@ -24,7 +23,6 @@ namespace Microsoft.Win32.SafeHandles this.SetHandle(ptr); } - [System.Security.SecurityCritical] protected override bool ReleaseHandle() { Interop.EventStream.FSEventStreamStop(handle); @@ -36,7 +34,6 @@ namespace Microsoft.Win32.SafeHandles public override bool IsInvalid { - [System.Security.SecurityCritical] get { return handle == IntPtr.Zero; diff --git a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeEvpCipherCtxHandle.Unix.cs b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeEvpCipherCtxHandle.Unix.cs index 137bf6b179..b6e730228b 100644 --- a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeEvpCipherCtxHandle.Unix.cs +++ b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeEvpCipherCtxHandle.Unix.cs @@ -8,7 +8,6 @@ using System.Runtime.InteropServices; namespace Microsoft.Win32.SafeHandles { - [SecurityCritical] internal sealed class SafeEvpCipherCtxHandle : SafeHandle { private SafeEvpCipherCtxHandle() : @@ -16,7 +15,6 @@ namespace Microsoft.Win32.SafeHandles { } - [SecurityCritical] protected override bool ReleaseHandle() { Interop.Crypto.EvpCipherDestroy(handle); @@ -26,7 +24,6 @@ namespace Microsoft.Win32.SafeHandles public override bool IsInvalid { - [SecurityCritical] get { return handle == IntPtr.Zero; } } } diff --git a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeEvpMdCtxHandle.Unix.cs b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeEvpMdCtxHandle.Unix.cs index 5ca4dc2e2f..f59a88464c 100644 --- a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeEvpMdCtxHandle.Unix.cs +++ b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeEvpMdCtxHandle.Unix.cs @@ -8,7 +8,6 @@ using System.Runtime.InteropServices; namespace Microsoft.Win32.SafeHandles { - [SecurityCritical] internal sealed class SafeEvpMdCtxHandle : SafeHandle { private SafeEvpMdCtxHandle() : @@ -16,7 +15,6 @@ namespace Microsoft.Win32.SafeHandles { } - [SecurityCritical] protected override bool ReleaseHandle() { Interop.Crypto.EvpMdCtxDestroy(handle); @@ -25,7 +23,6 @@ namespace Microsoft.Win32.SafeHandles public override bool IsInvalid { - [SecurityCritical] get { return handle == IntPtr.Zero; } } } diff --git a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs index 120af69643..5340bb22c6 100644 --- a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs +++ b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs @@ -8,7 +8,6 @@ using System.Runtime.InteropServices; namespace Microsoft.Win32.SafeHandles { - [System.Security.SecurityCritical] sealed partial class SafeFileHandle : SafeHandle { /// A handle value of -1. @@ -80,7 +79,6 @@ namespace Microsoft.Win32.SafeHandles return handle; } - [System.Security.SecurityCritical] protected override bool ReleaseHandle() { // When the SafeFileHandle was opened, we likely issued an flock on the created descriptor in order to add @@ -108,7 +106,6 @@ namespace Microsoft.Win32.SafeHandles public override bool IsInvalid { - [System.Security.SecurityCritical] get { long h = (long)handle; diff --git a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeRsaHandle.Unix.cs b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeRsaHandle.Unix.cs index 7e7bc1b2ae..6e413d6236 100644 --- a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeRsaHandle.Unix.cs +++ b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeRsaHandle.Unix.cs @@ -9,7 +9,6 @@ using System.Runtime.InteropServices; namespace Microsoft.Win32.SafeHandles { - [SecurityCritical] internal sealed class SafeRsaHandle : SafeHandle { private SafeRsaHandle() : diff --git a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeX509Handles.Unix.cs b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeX509Handles.Unix.cs index 523381b569..f68ae62dc7 100644 --- a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeX509Handles.Unix.cs +++ b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeX509Handles.Unix.cs @@ -9,7 +9,6 @@ using System.Runtime.InteropServices; namespace Microsoft.Win32.SafeHandles { - [SecurityCritical] internal sealed class SafeX509Handle : SafeHandle { internal static readonly SafeX509Handle InvalidHandle = new SafeX509Handle(); @@ -19,7 +18,6 @@ namespace Microsoft.Win32.SafeHandles { } - [SecurityCritical] protected override bool ReleaseHandle() { Interop.Crypto.X509Destroy(handle); @@ -29,7 +27,6 @@ namespace Microsoft.Win32.SafeHandles public override bool IsInvalid { - [SecurityCritical] get { return handle == IntPtr.Zero; } } } @@ -54,7 +51,6 @@ namespace Microsoft.Win32.SafeHandles } } - [SecurityCritical] internal sealed class SafeX509StoreHandle : SafeHandle { private SafeX509StoreHandle() : diff --git a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeX509NameHandle.Unix.cs b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeX509NameHandle.Unix.cs index 45030c926f..ae22b92303 100644 --- a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeX509NameHandle.Unix.cs +++ b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/SafeX509NameHandle.Unix.cs @@ -8,7 +8,6 @@ using System.Runtime.InteropServices; namespace Microsoft.Win32.SafeHandles { - [SecurityCritical] internal sealed class SafeX509NameHandle : SafeHandle { private SafeX509NameHandle() : diff --git a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/X509ExtensionSafeHandles.Unix.cs b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/X509ExtensionSafeHandles.Unix.cs index ccd893b8a3..42a1a318b4 100644 --- a/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/X509ExtensionSafeHandles.Unix.cs +++ b/external/corefx/src/Common/src/Microsoft/Win32/SafeHandles/X509ExtensionSafeHandles.Unix.cs @@ -8,7 +8,6 @@ using System.Security; namespace Microsoft.Win32.SafeHandles { - [SecurityCritical] internal sealed class SafeX509ExtensionHandle : SafeHandle { private SafeX509ExtensionHandle() : @@ -29,7 +28,6 @@ namespace Microsoft.Win32.SafeHandles } } - [SecurityCritical] internal sealed class SafeEkuExtensionHandle : SafeHandle { private SafeEkuExtensionHandle() : diff --git a/external/corefx/src/Common/src/System/Collections/Concurrent/SingleProducerConsumerQueue.cs b/external/corefx/src/Common/src/System/Collections/Concurrent/SingleProducerConsumerQueue.cs new file mode 100644 index 0000000000..c28db22d13 --- /dev/null +++ b/external/corefx/src/Common/src/System/Collections/Concurrent/SingleProducerConsumerQueue.cs @@ -0,0 +1,315 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; + +namespace System.Collections.Concurrent +{ + /// + /// Provides a producer/consumer queue safe to be used by only one producer and one consumer concurrently. + /// + /// Specifies the type of data contained in the queue. + [DebuggerDisplay("Count = {Count}")] + [DebuggerTypeProxy(typeof(SingleProducerSingleConsumerQueue<>.SingleProducerSingleConsumerQueue_DebugView))] + internal sealed class SingleProducerSingleConsumerQueue : IEnumerable + { + // Design: + // + // SingleProducerSingleConsumerQueue (SPSCQueue) is a concurrent queue designed to be used + // by one producer thread and one consumer thread. SPSCQueue does not work correctly when used by + // multiple producer threads concurrently or multiple consumer threads concurrently. + // + // SPSCQueue is based on segments that behave like circular buffers. Each circular buffer is represented + // as an array with two indexes: _first and _last. _first is the index of the array slot for the consumer + // to read next, and _last is the slot for the producer to write next. The circular buffer is empty when + // (_first == _last), and full when ((_last+1) % _array.Length == _first). + // + // Since _first is only ever modified by the consumer thread and _last by the producer, the two indices can + // be updated without interlocked operations. As long as the queue size fits inside a single circular buffer, + // enqueues and dequeues simply advance the corresponding indices around the circular buffer. If an enqueue finds + // that there is no room in the existing buffer, however, a new circular buffer is allocated that is twice as big + // as the old buffer. From then on, the producer will insert values into the new buffer. The consumer will first + // empty out the old buffer and only then follow the producer into the new (larger) buffer. + // + // As described above, the enqueue operation on the fast path only modifies the _first field of the current segment. + // However, it also needs to read _last in order to verify that there is room in the current segment. Similarly, the + // dequeue operation on the fast path only needs to modify _last, but also needs to read _first to verify that the + // queue is non-empty. This results in true cache line sharing between the producer and the consumer. + // + // The cache line sharing issue can be mitigating by having a possibly stale copy of _first that is owned by the producer, + // and a possibly stale copy of _last that is owned by the consumer. So, the consumer state is described using + // (_first, _lastCopy) and the producer state using (_firstCopy, _last). The consumer state is separated from + // the producer state by padding, which allows fast-path enqueues and dequeues from hitting shared cache lines. + // _lastCopy is the consumer's copy of _last. Whenever the consumer can tell that there is room in the buffer + // simply by observing _lastCopy, the consumer thread does not need to read _last and thus encounter a cache miss. Only + // when the buffer appears to be empty will the consumer refresh _lastCopy from _last. _firstCopy is used by the producer + // in the same way to avoid reading _first on the hot path. + + /// The initial size to use for segments (in number of elements). + private const int InitialSegmentSize = 32; // must be a power of 2 + /// The maximum size to use for segments (in number of elements). + private const int MaxSegmentSize = 0x1000000; // this could be made as large as Int32.MaxValue / 2 + + /// The head of the linked list of segments. + private volatile Segment _head; + /// The tail of the linked list of segments. + private volatile Segment _tail; + + /// Initializes the queue. + public SingleProducerSingleConsumerQueue() + { + // Validate constants in ctor rather than in an explicit cctor that would cause perf degradation + Debug.Assert(InitialSegmentSize > 0, "Initial segment size must be > 0."); + Debug.Assert((InitialSegmentSize & (InitialSegmentSize - 1)) == 0, "Initial segment size must be a power of 2"); + Debug.Assert(InitialSegmentSize <= MaxSegmentSize, "Initial segment size should be <= maximum."); + Debug.Assert(MaxSegmentSize < int.MaxValue / 2, "Max segment size * 2 must be < Int32.MaxValue, or else overflow could occur."); + + // Initialize the queue + _head = _tail = new Segment(InitialSegmentSize); + } + + /// Enqueues an item into the queue. + /// The item to enqueue. + public void Enqueue(T item) + { + Segment segment = _tail; + T[] array = segment._array; + int last = segment._state._last; // local copy to avoid multiple volatile reads + + // Fast path: there's obviously room in the current segment + int tail2 = (last + 1) & (array.Length - 1); + if (tail2 != segment._state._firstCopy) + { + array[last] = item; + segment._state._last = tail2; + } + // Slow path: there may not be room in the current segment. + else EnqueueSlow(item, ref segment); + } + + /// Enqueues an item into the queue. + /// The item to enqueue. + /// The segment in which to first attempt to store the item. + private void EnqueueSlow(T item, ref Segment segment) + { + Debug.Assert(segment != null, "Expected a non-null segment."); + + if (segment._state._firstCopy != segment._state._first) + { + segment._state._firstCopy = segment._state._first; + Enqueue(item); // will only recur once for this enqueue operation + return; + } + + int newSegmentSize = _tail._array.Length << 1; // double size + Debug.Assert(newSegmentSize > 0, "The max size should always be small enough that we don't overflow."); + if (newSegmentSize > MaxSegmentSize) newSegmentSize = MaxSegmentSize; + + var newSegment = new Segment(newSegmentSize); + newSegment._array[0] = item; + newSegment._state._last = 1; + newSegment._state._lastCopy = 1; + + try { } + finally + { + // Finally block to protect against corruption due to a thread abort + // between setting _next and setting _tail. + Volatile.Write(ref _tail._next, newSegment); // ensure segment not published until item is fully stored + _tail = newSegment; + } + } + + /// Attempts to dequeue an item from the queue. + /// The dequeued item. + /// true if an item could be dequeued; otherwise, false. + public bool TryDequeue(out T result) + { + Segment segment = _head; + T[] array = segment._array; + int first = segment._state._first; // local copy to avoid multiple volatile reads + + // Fast path: there's obviously data available in the current segment + if (first != segment._state._lastCopy) + { + result = array[first]; + array[first] = default; // Clear the slot to release the element + segment._state._first = (first + 1) & (array.Length - 1); + return true; + } + // Slow path: there may not be data available in the current segment + else return TryDequeueSlow(ref segment, ref array, out result); + } + + /// Attempts to dequeue an item from the queue. + /// The array from which the item was dequeued. + /// The segment from which the item was dequeued. + /// The dequeued item. + /// true if an item could be dequeued; otherwise, false. + private bool TryDequeueSlow(ref Segment segment, ref T[] array, out T result) + { + Debug.Assert(segment != null, "Expected a non-null segment."); + Debug.Assert(array != null, "Expected a non-null item array."); + + if (segment._state._last != segment._state._lastCopy) + { + segment._state._lastCopy = segment._state._last; + return TryDequeue(out result); // will only recur once for this dequeue operation + } + + if (segment._next != null && segment._state._first == segment._state._last) + { + segment = segment._next; + array = segment._array; + _head = segment; + } + + int first = segment._state._first; // local copy to avoid extraneous volatile reads + + if (first == segment._state._last) + { + result = default; + return false; + } + + result = array[first]; + array[first] = default; // Clear the slot to release the element + segment._state._first = (first + 1) & (segment._array.Length - 1); + segment._state._lastCopy = segment._state._last; // Refresh _lastCopy to ensure that _first has not passed _lastCopy + + return true; + } + + /// Gets whether the collection is currently empty. + public bool IsEmpty + { + // This implementation is optimized for calls from the consumer. + get + { + Segment head = _head; + if (head._state._first != head._state._lastCopy) return false; // _first is volatile, so the read of _lastCopy cannot get reordered + if (head._state._first != head._state._last) return false; + return head._next == null; + } + } + + /// Gets an enumerable for the collection. + /// This method is not safe to use concurrently with any other members that may mutate the collection. + public IEnumerator GetEnumerator() + { + for (Segment segment = _head; segment != null; segment = segment._next) + { + for (int pt = segment._state._first; + pt != segment._state._last; + pt = (pt + 1) & (segment._array.Length - 1)) + { + yield return segment._array[pt]; + } + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + /// Gets the number of items in the collection. + /// This method is not safe to use concurrently with any other members that may mutate the collection. + internal int Count + { + get + { + int count = 0; + for (Segment segment = _head; segment != null; segment = segment._next) + { + int arraySize = segment._array.Length; + int first, last; + while (true) // Count is not meant to be used concurrently, but this helps to avoid issues if it is + { + first = segment._state._first; + last = segment._state._last; + if (first == segment._state._first) break; + } + count += (last - first) & (arraySize - 1); + } + return count; + } + } + + /// A segment in the queue containing one or more items. + [StructLayout(LayoutKind.Sequential)] + private sealed class Segment + { + /// The next segment in the linked list of segments. + internal Segment _next; + /// The data stored in this segment. + internal readonly T[] _array; + /// Details about the segment. + internal SegmentState _state; // separated out to enable StructLayout attribute to take effect + + /// Initializes the segment. + /// The size to use for this segment. + internal Segment(int size) + { + Debug.Assert((size & (size - 1)) == 0, "Size must be a power of 2"); + _array = new T[size]; + } + } + + /// Stores information about a segment. + [StructLayout(LayoutKind.Sequential)] // enforce layout so that padding reduces false sharing + private struct SegmentState + { + /// Padding to reduce false sharing between the segment's array and _first. + internal PaddingFor32 _pad0; + + /// The index of the current head in the segment. + internal volatile int _first; + /// A copy of the current tail index. + internal int _lastCopy; // not volatile as read and written by the producer, except for IsEmpty, and there _lastCopy is only read after reading the volatile _first + + /// Padding to reduce false sharing between the first and last. + internal PaddingFor32 _pad1; + + /// A copy of the current head index. + internal int _firstCopy; // not volatile as only read and written by the consumer thread + /// The index of the current tail in the segment. + internal volatile int _last; + + /// Padding to reduce false sharing with the last and what's after the segment. + internal PaddingFor32 _pad2; + } + + /// Debugger type proxy for a SingleProducerSingleConsumerQueue of T. + private sealed class SingleProducerSingleConsumerQueue_DebugView + { + /// The queue being visualized. + private readonly SingleProducerSingleConsumerQueue _queue; + + /// Initializes the debug view. + /// The queue being debugged. + public SingleProducerSingleConsumerQueue_DebugView(SingleProducerSingleConsumerQueue queue) + { + Debug.Assert(queue != null, "Expected a non-null queue."); + _queue = queue; + } + + /// Gets the contents of the list. + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public T[] Items => new List(_queue).ToArray(); + } + } + + + /// A placeholder class for common padding constants and eventually routines. + internal static class PaddingHelpers + { + /// A size greater than or equal to the size of the most common CPU cache lines. + internal const int CACHE_LINE_SIZE = 128; + } + + /// Padding structure used to minimize false sharing in SingleProducerSingleConsumerQueue{T}. + [StructLayout(LayoutKind.Explicit, Size = PaddingHelpers.CACHE_LINE_SIZE - sizeof(int))] // Based on common case of 64-byte cache lines + internal struct PaddingFor32 { } +} diff --git a/external/corefx/src/Common/src/System/Collections/Generic/LargeArrayBuilder.cs b/external/corefx/src/Common/src/System/Collections/Generic/LargeArrayBuilder.cs index a3337032e8..0b6ed7146e 100644 --- a/external/corefx/src/Common/src/System/Collections/Generic/LargeArrayBuilder.cs +++ b/external/corefx/src/Common/src/System/Collections/Generic/LargeArrayBuilder.cs @@ -11,7 +11,7 @@ namespace System.Collections.Generic /// Represents a position within a . /// [DebuggerDisplay("{DebuggerDisplay,nq}")] - internal struct CopyPosition + internal readonly struct CopyPosition { /// /// Constructs a new . @@ -200,7 +200,7 @@ namespace System.Collections.Generic arrayIndex += toCopy; } } - + /// /// Copies the contents of this builder to the specified array. /// diff --git a/external/corefx/src/Common/src/System/Collections/Generic/LowLevelDictionary.cs b/external/corefx/src/Common/src/System/Collections/Generic/LowLevelDictionary.cs index 075aac5c87..a5d65aa18b 100644 --- a/external/corefx/src/Common/src/System/Collections/Generic/LowLevelDictionary.cs +++ b/external/corefx/src/Common/src/System/Collections/Generic/LowLevelDictionary.cs @@ -62,7 +62,7 @@ namespace System.Collections.Generic Entry entry = Find(key); if (entry == null) - throw new KeyNotFoundException(); + throw new KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key.ToString())); return entry._value; } set diff --git a/external/corefx/src/Common/src/System/Collections/Generic/SparseArrayBuilder.cs b/external/corefx/src/Common/src/System/Collections/Generic/SparseArrayBuilder.cs index 4829a9ce46..a81cb96f06 100644 --- a/external/corefx/src/Common/src/System/Collections/Generic/SparseArrayBuilder.cs +++ b/external/corefx/src/Common/src/System/Collections/Generic/SparseArrayBuilder.cs @@ -10,7 +10,7 @@ namespace System.Collections.Generic /// Represents a reserved region within a . /// [DebuggerDisplay("{DebuggerDisplay,nq}")] - internal struct Marker + internal readonly struct Marker { /// /// Constructs a new marker. diff --git a/external/corefx/src/Common/src/System/Collections/HashHelpers.cs b/external/corefx/src/Common/src/System/Collections/HashHelpers.cs index e7f7ed2e56..661e9faf2e 100644 --- a/external/corefx/src/Common/src/System/Collections/HashHelpers.cs +++ b/external/corefx/src/Common/src/System/Collections/HashHelpers.cs @@ -14,7 +14,6 @@ using System; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.Serialization; @@ -50,7 +49,6 @@ namespace System.Collections { if (min < 0) throw new ArgumentException(SR.Arg_HTCapacityOverflow); - Contract.EndContractBlock(); for (int i = 0; i < primes.Length; i++) { diff --git a/external/corefx/src/Common/src/System/Data/Common/DbConnectionOptions.Common.cs b/external/corefx/src/Common/src/System/Data/Common/DbConnectionOptions.Common.cs index 02672243d8..41cc2a2ffb 100644 --- a/external/corefx/src/Common/src/System/Data/Common/DbConnectionOptions.Common.cs +++ b/external/corefx/src/Common/src/System/Data/Common/DbConnectionOptions.Common.cs @@ -1,3 +1,7 @@ +// 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.Collections.Generic; using System.Diagnostics; using System.Globalization; @@ -645,4 +649,4 @@ namespace System.Data.Common return head; } } -} \ No newline at end of file +} diff --git a/external/corefx/src/Common/src/System/Diagnostics/CodeAnalysis/ExcludeFromCodeCoverageAttribute.cs b/external/corefx/src/Common/src/System/Diagnostics/CodeAnalysis/ExcludeFromCodeCoverageAttribute.cs index 51de8396c8..d3ec27416c 100644 --- a/external/corefx/src/Common/src/System/Diagnostics/CodeAnalysis/ExcludeFromCodeCoverageAttribute.cs +++ b/external/corefx/src/Common/src/System/Diagnostics/CodeAnalysis/ExcludeFromCodeCoverageAttribute.cs @@ -4,7 +4,7 @@ namespace System.Diagnostics.CodeAnalysis { - [AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event, Inherited = false, AllowMultiple = false)] + [AttributeUsageAttribute(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event, Inherited = false, AllowMultiple = false)] #if SYSTEM_DIAGNOSTICS_TOOLS public #else diff --git a/external/corefx/src/Common/src/System/Drawing/ColorConverterCommon.cs b/external/corefx/src/Common/src/System/Drawing/ColorConverterCommon.cs index 48ccf59290..a0b987b69c 100644 --- a/external/corefx/src/Common/src/System/Drawing/ColorConverterCommon.cs +++ b/external/corefx/src/Common/src/System/Drawing/ColorConverterCommon.cs @@ -128,7 +128,7 @@ namespace System.Drawing } catch (Exception e) { - throw new Exception(SR.Format(SR.ConvertInvalidPrimitive, text, typeof(int).Name), e); + throw new ArgumentException(SR.Format(SR.ConvertInvalidPrimitive, text, typeof(int).Name), e); } } @@ -142,4 +142,4 @@ namespace System.Drawing return Int32.Parse(value, NumberStyles.Integer, formatInfo); } } -} \ No newline at end of file +} diff --git a/external/corefx/src/Common/src/System/Globalization/FormatProvider.Number.cs b/external/corefx/src/Common/src/System/Globalization/FormatProvider.Number.cs index ccd74e3a65..fa92c9cce9 100644 --- a/external/corefx/src/Common/src/System/Globalization/FormatProvider.Number.cs +++ b/external/corefx/src/Common/src/System/Globalization/FormatProvider.Number.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Runtime.InteropServices; using System.Text; namespace System.Globalization @@ -412,9 +413,14 @@ namespace System.Globalization if (digCount < maxParseDigits) { if (bigNumber) + { sb.Append(ch); + } else + { dig[digCount++] = ch; + } + if (ch != '0' || parseDecimal) { digEnd = digCount; @@ -559,7 +565,7 @@ namespace System.Globalization { Debug.Assert(numfmt != null); - fixed (char* stringPointer = &str.DangerousGetPinnableReference()) + fixed (char* stringPointer = &MemoryMarshal.GetReference(str)) { char* p = stringPointer; if (!ParseNumber(ref p, options, ref number, sb, numfmt, parseDecimal) @@ -627,53 +633,76 @@ namespace System.Globalization } } - internal static unsafe char ParseFormatSpecifier(string format, out int digits) + internal static unsafe char ParseFormatSpecifier(ReadOnlySpan format, out int digits) { - if (format != null) + char c = default; + if (format.Length > 0) { - fixed (char* pFormat = format) + // If the format begins with a symbol, see if it's a standard format + // with or without a specified number of digits. + c = format[0]; + if ((uint)(c - 'A') <= 'Z' - 'A' || + (uint)(c - 'a') <= 'z' - 'a') { - int i = 0; - char ch = pFormat[i]; - if (ch != 0) + // Fast path for sole symbol, e.g. "D" + if (format.Length == 1) { - if (((ch >= 'A') && (ch <= 'Z')) || ((ch >= 'a') && (ch <= 'z'))) - { - i++; - int n = -1; - if ((pFormat[i] >= '0') && (pFormat[i] <= '9')) - { - n = pFormat[i++] - '0'; - while ((pFormat[i] >= '0') && (pFormat[i] <= '9')) - { - n = (n * 10) + pFormat[i++] - '0'; - if (n >= 10) - break; - } - } - if (pFormat[i] == 0) - { - digits = n; - return ch; - } - } - digits = -1; - return '\0'; + return c; + } + + if (format.Length == 2) + { + // Fast path for symbol and single digit, e.g. "X4" + int d = format[1] - '0'; + if ((uint)d < 10) + { + digits = d; + return c; + } + } + else if (format.Length == 3) + { + // Fast path for symbol and double digit, e.g. "F12" + int d1 = format[1] - '0', d2 = format[2] - '0'; + if ((uint)d1 < 10 && (uint)d2 < 10) + { + digits = d1 * 10 + d2; + return c; + } + } + + // Fallback for symbol and any length digits. The digits value must be >= 0 && <= 99, + // but it can begin with any number of 0s, and thus we may need to check more than two + // digits. Further, for compat, we need to stop when we hit a null char. + int n = 0; + int i = 1; + while (i < format.Length && (((uint)format[i] - '0') < 10) && n < 10) + { + n = (n * 10) + format[i++] - '0'; + } + + // If we're at the end of the digits rather than having stopped because we hit something + // other than a digit or overflowed, return the standard format info. + if (i == format.Length || format[i] == '\0') + { + digits = n; + return c; } } } + // Default empty format to be "G"; custom format is signified with '\0'. digits = -1; - return 'G'; + return format.Length == 0 || c == '\0' ? // For compat, treat '\0' as the end of the specifier, even if the specifier extends beyond it. + 'G' : + '\0'; } - internal static unsafe string NumberToString(NumberBuffer number, char format, int nMaxDigits, NumberFormatInfo info, bool isDecimal) + internal static unsafe void NumberToString(ref ValueStringBuilder sb, ref NumberBuffer number, char format, int nMaxDigits, NumberFormatInfo info, bool isDecimal) { int nMinDigits = -1; - StringBuilder sb = new StringBuilder(MIN_SB_BUFFER_SIZE); - switch (format) { case 'C': @@ -681,11 +710,13 @@ namespace System.Globalization { nMinDigits = nMaxDigits >= 0 ? nMaxDigits : info.CurrencyDecimalDigits; if (nMaxDigits < 0) + { nMaxDigits = info.CurrencyDecimalDigits; + } RoundNumber(ref number, number.scale + nMaxDigits); // Don't change this line to use digPos since digCount could have its sign changed. - FormatCurrency(sb, number, nMinDigits, nMaxDigits, info); + FormatCurrency(ref sb, ref number, nMinDigits, nMaxDigits, info); break; } @@ -694,16 +725,22 @@ namespace System.Globalization case 'f': { if (nMaxDigits < 0) + { nMaxDigits = nMinDigits = info.NumberDecimalDigits; + } else + { nMinDigits = nMaxDigits; + } RoundNumber(ref number, number.scale + nMaxDigits); if (number.sign) + { sb.Append(info.NegativeSign); + } - FormatFixed(sb, number, nMinDigits, nMaxDigits, info, null, info.NumberDecimalSeparator, null); + FormatFixed(ref sb, ref number, nMinDigits, nMaxDigits, info, null, info.NumberDecimalSeparator, null); break; } @@ -712,13 +749,17 @@ namespace System.Globalization case 'n': { if (nMaxDigits < 0) + { nMaxDigits = nMinDigits = info.NumberDecimalDigits; // Since we are using digits in our calculation + } else + { nMinDigits = nMaxDigits; + } RoundNumber(ref number, number.scale + nMaxDigits); - FormatNumber(sb, number, nMinDigits, nMaxDigits, info); + FormatNumber(ref sb, ref number, nMinDigits, nMaxDigits, info); break; } @@ -727,17 +768,23 @@ namespace System.Globalization case 'e': { if (nMaxDigits < 0) + { nMaxDigits = nMinDigits = 6; + } else + { nMinDigits = nMaxDigits; + } nMaxDigits++; RoundNumber(ref number, nMaxDigits); if (number.sign) + { sb.Append(info.NegativeSign); + } - FormatScientific(sb, number, nMinDigits, nMaxDigits, info, format); + FormatScientific(ref sb, ref number, nMinDigits, nMaxDigits, info, format); break; } @@ -765,7 +812,9 @@ namespace System.Globalization nMinDigits = nMaxDigits; if (enableRounding) // Don't round for G formatting without precision + { RoundNumber(ref number, nMaxDigits); // This also fixes up the minus zero case + } else { if (isDecimal && (number.digits[0] == 0)) @@ -776,9 +825,11 @@ namespace System.Globalization } if (number.sign) + { sb.Append(info.NegativeSign); + } - FormatGeneral(sb, number, nMinDigits, nMaxDigits, info, (char)(format - ('G' - 'E')), !enableRounding); + FormatGeneral(ref sb, ref number, nMinDigits, nMaxDigits, info, (char)(format - ('G' - 'E')), !enableRounding); break; } @@ -787,14 +838,18 @@ namespace System.Globalization case 'p': { if (nMaxDigits < 0) + { nMaxDigits = nMinDigits = info.PercentDecimalDigits; + } else + { nMinDigits = nMaxDigits; + } number.scale += 2; RoundNumber(ref number, number.scale + nMaxDigits); - FormatPercent(sb, number, nMinDigits, nMaxDigits, info); + FormatPercent(ref sb, ref number, nMinDigits, nMaxDigits, info); break; } @@ -802,11 +857,9 @@ namespace System.Globalization default: throw new FormatException(SR.Argument_BadFormatSpecifier); } - - return sb.ToString(); } - private static void FormatCurrency(StringBuilder sb, NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info) + private static void FormatCurrency(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info) { string fmt = number.sign ? s_negCurrencyFormats[info.CurrencyNegativePattern] : @@ -817,7 +870,7 @@ namespace System.Globalization switch (ch) { case '#': - FormatFixed(sb, number, nMinDigits, nMaxDigits, info, info.CurrencyGroupSizes, info.CurrencyDecimalSeparator, info.CurrencyGroupSeparator); + FormatFixed(ref sb, ref number, nMinDigits, nMaxDigits, info, info.CurrencyGroupSizes, info.CurrencyDecimalSeparator, info.CurrencyGroupSeparator); break; case '-': sb.Append(info.NegativeSign); @@ -836,11 +889,13 @@ namespace System.Globalization { int result = 0; while (*s++ != '\0') + { result++; + } return result; } - private static unsafe void FormatFixed(StringBuilder sb, NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, int[] groupDigits, string sDecimal, string sGroup) + private static unsafe void FormatFixed(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, int[] groupDigits, string sDecimal, string sGroup) { int digPos = number.scale; char* dig = number.digits; @@ -864,20 +919,31 @@ namespace System.Globalization { groupSize = groupDigits[groupSizeIndex]; if (groupSize == 0) + { break; + } bufferSize += groupSeparatorLen; if (groupSizeIndex < groupSizeLen - 1) + { groupSizeIndex++; + } groupSizeCount += groupDigits[groupSizeIndex]; if (groupSizeCount < 0 || bufferSize < 0) + { throw new ArgumentOutOfRangeException(); // If we overflow + } } + if (groupSizeCount == 0) // If you passed in an array with one entry as 0, groupSizeCount == 0 + { groupSize = 0; + } else + { groupSize = groupDigits[0]; + } } char* tmpBuffer = stackalloc char[bufferSize]; @@ -896,7 +962,9 @@ namespace System.Globalization if ((digitCount == groupSize) && (i != 0)) { for (int j = groupSeparatorLen - 1; j >= 0; j--) + { *(p--) = sGroup[j]; + } if (groupSizeIndex < groupSizeLen - 1) { @@ -917,7 +985,9 @@ namespace System.Globalization sb.Append(dig, digits); dig += digits; if (digPos > digLength) + { sb.Append('0', digPos - digLength); + } } } else @@ -944,7 +1014,7 @@ namespace System.Globalization } } - private static void FormatNumber(StringBuilder sb, NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info) + private static void FormatNumber(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info) { string fmt = number.sign ? s_negNumberFormats[info.NumberNegativePattern] : @@ -955,7 +1025,7 @@ namespace System.Globalization switch (ch) { case '#': - FormatFixed(sb, number, nMinDigits, nMaxDigits, info, info.NumberGroupSizes, info.NumberDecimalSeparator, info.NumberGroupSeparator); + FormatFixed(ref sb, ref number, nMinDigits, nMaxDigits, info, info.NumberGroupSizes, info.NumberDecimalSeparator, info.NumberGroupSeparator); break; case '-': sb.Append(info.NegativeSign); @@ -967,23 +1037,27 @@ namespace System.Globalization } } - private static unsafe void FormatScientific(StringBuilder sb, NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, char expChar) + private static unsafe void FormatScientific(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, char expChar) { char* dig = number.digits; sb.Append((*dig != 0) ? *dig++ : '0'); if (nMaxDigits != 1) // For E0 we would like to suppress the decimal point + { sb.Append(info.NumberDecimalSeparator); + } while (--nMaxDigits > 0) + { sb.Append((*dig != 0) ? *dig++ : '0'); + } int e = number.digits[0] == 0 ? 0 : number.scale - 1; - FormatExponent(sb, info, e, expChar, 3, true); + FormatExponent(ref sb, info, e, expChar, 3, true); } - private static unsafe void FormatExponent(StringBuilder sb, NumberFormatInfo info, int value, char expChar, int minDigits, bool positiveSign) + private static unsafe void FormatExponent(ref ValueStringBuilder sb, NumberFormatInfo info, int value, char expChar, int minDigits, bool positiveSign) { sb.Append(expChar); @@ -995,7 +1069,9 @@ namespace System.Globalization else { if (positiveSign) + { sb.Append(info.PositiveSign); + } } char* digits = stackalloc char[11]; @@ -1003,10 +1079,12 @@ namespace System.Globalization Int32ToDecChars(digits, ref index, (uint)value, minDigits); int i = 10 - index; while (--i >= 0) + { sb.Append(digits[index++]); + } } - private static unsafe void FormatGeneral(StringBuilder sb, NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, char expChar, bool bSuppressScientific) + private static unsafe void FormatGeneral(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, char expChar, bool bSuppressScientific) { int digPos = number.scale; bool scientific = false; @@ -1046,14 +1124,18 @@ namespace System.Globalization } while (*dig != 0) + { sb.Append(*dig++); + } } if (scientific) - FormatExponent(sb, info, number.scale - 1, expChar, 2, true); + { + FormatExponent(ref sb, info, number.scale - 1, expChar, 2, true); + } } - private static void FormatPercent(StringBuilder sb, NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info) + private static void FormatPercent(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info) { string fmt = number.sign ? s_negPercentFormats[info.PercentNegativePattern] : @@ -1064,7 +1146,7 @@ namespace System.Globalization switch (ch) { case '#': - FormatFixed(sb, number, nMinDigits, nMaxDigits, info, info.PercentGroupSizes, info.PercentDecimalSeparator, info.PercentGroupSeparator); + FormatFixed(ref sb, ref number, nMinDigits, nMaxDigits, info, info.PercentGroupSizes, info.PercentDecimalSeparator, info.PercentGroupSeparator); break; case '-': sb.Append(info.NegativeSign); @@ -1085,12 +1167,16 @@ namespace System.Globalization int i = 0; while (i < pos && dig[i] != 0) + { i++; + } if (i == pos && dig[i] >= '5') { while (i > 0 && dig[i - 1] == '9') + { i--; + } if (i > 0) { @@ -1106,8 +1192,11 @@ namespace System.Globalization else { while (i > 0 && dig[i - 1] == '0') + { i--; + } } + if (i == 0) { number.scale = 0; @@ -1116,35 +1205,48 @@ namespace System.Globalization dig[i] = '\0'; } - private static unsafe int FindSection(string format, int section) + private static unsafe int FindSection(ReadOnlySpan format, int section) { - int src; - char ch; - if (section == 0) - return 0; - - fixed (char* pFormat = format) { - src = 0; + return 0; + } + + fixed (char* pFormat = &MemoryMarshal.GetReference(format)) + { + int src = 0; for (;;) { + if (src >= format.Length) + { + return 0; + } + + char ch; switch (ch = pFormat[src++]) { case '\'': case '"': - while (pFormat[src] != 0 && pFormat[src++] != ch) + while (src < format.Length && pFormat[src] != 0 && pFormat[src++] != ch) ; break; case '\\': - if (pFormat[src] != 0) + if (src < format.Length && pFormat[src] != 0) + { src++; + } break; case ';': if (--section != 0) + { break; - if (pFormat[src] != 0 && pFormat[src] != ';') + } + + if (src < format.Length && pFormat[src] != 0 && pFormat[src] != ';') + { return src; + } + goto case '\0'; case '\0': return 0; @@ -1153,7 +1255,7 @@ namespace System.Globalization } } - internal static unsafe string NumberToStringFormat(NumberBuffer number, string format, NumberFormatInfo info) + internal static unsafe void NumberToStringFormat(ref ValueStringBuilder sb, ref NumberBuffer number, ReadOnlySpan format, NumberFormatInfo info) { int digitCount; int decimalPos; @@ -1186,9 +1288,9 @@ namespace System.Globalization scaleAdjust = 0; src = section; - fixed (char* pFormat = format) + fixed (char* pFormat = &MemoryMarshal.GetReference(format)) { - while ((ch = pFormat[src++]) != 0 && ch != ';') + while (src < format.Length && (ch = pFormat[src++]) != 0 && ch != ';') { switch (ch) { @@ -1197,13 +1299,17 @@ namespace System.Globalization break; case '0': if (firstDigit == 0x7FFFFFFF) + { firstDigit = digitCount; + } digitCount++; lastDigit = digitCount; break; case '.': if (decimalPos < 0) + { decimalPos = digitCount; + } break; case ',': if (digitCount > 0 && decimalPos < 0) @@ -1229,19 +1335,21 @@ namespace System.Globalization break; case '\'': case '"': - while (pFormat[src] != 0 && pFormat[src++] != ch) + while (src < format.Length && pFormat[src] != 0 && pFormat[src++] != ch) ; break; case '\\': - if (pFormat[src] != 0) + if (src < format.Length && pFormat[src] != 0) + { src++; + } break; case 'E': case 'e': - if (pFormat[src] == '0' || ((pFormat[src] == '+' || pFormat[src] == '-') && pFormat[src + 1] == '0')) + if ((src < format.Length && pFormat[src] == '0') || + (src + 1 < format.Length && (pFormat[src] == '+' || pFormat[src] == '-') && pFormat[src + 1] == '0')) { - while (pFormat[++src] == '0') - ; + while (++src < format.Length && pFormat[src] == '0'); scientific = true; } break; @@ -1250,14 +1358,20 @@ namespace System.Globalization } if (decimalPos < 0) + { decimalPos = digitCount; + } if (thousandPos >= 0) { if (thousandPos == decimalPos) + { scaleAdjust -= thousandCount * 3; + } else + { thousandSeps = true; + } } if (dig[0] != 0) @@ -1302,7 +1416,7 @@ namespace System.Globalization // Adjust represents the number of characters over the formatting e.g. format string is "0000" and you are trying to // format 100000 (6 digits). Means adjust will be 2. On the other hand if you are trying to format 10 adjust will be // -2 and we'll need to fixup these digits with 0 padding if we have 0 formatting as in this example. - int[] thousandsSepPos = new int[4]; + Span thousandsSepPos = stackalloc int[4]; int thousandsSepCtr = -1; if (thousandSeps) @@ -1322,7 +1436,10 @@ namespace System.Globalization int groupTotalSizeCount = 0; int groupSizeLen = groupDigits.Length; // The length of groupDigits array. if (groupSizeLen != 0) + { groupTotalSizeCount = groupDigits[groupSizeIndex]; // The current running total of group size. + } + int groupSize = groupTotalSizeCount; int totalDigits = digPos + ((adjust < 0) ? adjust : 0); // Actual number of digits in o/p @@ -1330,10 +1447,18 @@ namespace System.Globalization while (numDigits > groupTotalSizeCount) { if (groupSize == 0) + { break; + } + ++thousandsSepCtr; if (thousandsSepCtr >= thousandsSepPos.Length) - Array.Resize(ref thousandsSepPos, thousandsSepPos.Length * 2); + { + var newThousandsSepPos = new int[thousandsSepPos.Length * 2]; + bool copied = thousandsSepPos.TryCopyTo(newThousandsSepPos); + Debug.Assert(copied, "Expect copy to succeed, as the new array is larger than the original"); + thousandsSepPos = newThousandsSepPos; + } thousandsSepPos[thousandsSepCtr] = groupTotalSizeCount; if (groupSizeIndex < groupSizeLen - 1) @@ -1346,18 +1471,18 @@ namespace System.Globalization } } - StringBuilder sb = new StringBuilder(MIN_SB_BUFFER_SIZE); - if (number.sign && section == 0) + { sb.Append(info.NegativeSign); + } bool decimalWritten = false; - fixed (char* pFormat = format) + fixed (char* pFormat = &MemoryMarshal.GetReference(format)) { char* cur = dig; - while ((ch = pFormat[src++]) != 0 && ch != ';') + while (src < format.Length && (ch = pFormat[src++]) != 0 && ch != ';') { if (adjust > 0) { @@ -1441,14 +1566,21 @@ namespace System.Globalization break; case '\'': case '"': - while (pFormat[src] != 0 && pFormat[src] != ch) + while (src < format.Length && pFormat[src] != 0 && pFormat[src] != ch) + { sb.Append(pFormat[src++]); - if (pFormat[src] != 0) + } + + if (src < format.Length && pFormat[src] != 0) + { src++; + } break; case '\\': - if (pFormat[src] != 0) + if (src < format.Length && pFormat[src] != 0) + { sb.Append(pFormat[src++]); + } break; case 'E': case 'e': @@ -1457,17 +1589,17 @@ namespace System.Globalization int i = 0; if (scientific) { - if (pFormat[src] == '0') + if (src < format.Length && pFormat[src] == '0') { // Handles E0, which should format the same as E-0 i++; } - else if (pFormat[src] == '+' && pFormat[src + 1] == '0') + else if (src+1 < format.Length && pFormat[src] == '+' && pFormat[src + 1] == '0') { // Handles E+0 positiveSign = true; } - else if (pFormat[src] == '-' && pFormat[src + 1] == '0') + else if (src+1 < format.Length && pFormat[src] == '-' && pFormat[src + 1] == '0') { // Handles E-0 // Do nothing, this is just a place holder s.t. we don't break out of the loop. @@ -1478,22 +1610,33 @@ namespace System.Globalization break; } - while (pFormat[++src] == '0') + while (++src < format.Length && pFormat[src] == '0') + { i++; + } if (i > 10) + { i = 10; + } int exp = dig[0] == 0 ? 0 : number.scale - decimalPos; - FormatExponent(sb, info, exp, ch, i, positiveSign); + FormatExponent(ref sb, info, exp, ch, i, positiveSign); scientific = false; } else { sb.Append(ch); // Copy E or e to output - if (pFormat[src] == '+' || pFormat[src] == '-') - sb.Append(pFormat[src++]); - while (pFormat[src] == '0') - sb.Append(pFormat[src++]); + if (src < format.Length) + { + if (pFormat[src] == '+' || pFormat[src] == '-') + { + sb.Append(pFormat[src++]); + } + while (src < format.Length && pFormat[src] == '0') + { + sb.Append(pFormat[src++]); + } + } } break; } @@ -1503,8 +1646,6 @@ namespace System.Globalization } } } - - return sb.ToString(); } } } diff --git a/external/corefx/src/Common/src/System/IO/DelegatingStream.cs b/external/corefx/src/Common/src/System/IO/DelegatingStream.cs index fb514e9b78..3bb864887f 100644 --- a/external/corefx/src/Common/src/System/IO/DelegatingStream.cs +++ b/external/corefx/src/Common/src/System/IO/DelegatingStream.cs @@ -16,11 +16,6 @@ namespace System.Net.Http #region Properties - protected Stream BaseStream - { - get { return _innerStream; } - } - public override bool CanRead { get { return _innerStream.CanRead; } @@ -122,7 +117,7 @@ namespace System.Net.Http { return _innerStream.EndRead(asyncResult); } - + #endregion Read #region Write diff --git a/external/corefx/src/Common/src/System/IO/Error.cs b/external/corefx/src/Common/src/System/IO/Error.cs deleted file mode 100644 index fbd88d0a58..0000000000 --- a/external/corefx/src/Common/src/System/IO/Error.cs +++ /dev/null @@ -1,27 +0,0 @@ -// 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.Security; - -namespace System.IO -{ - // Only static data; no need to serialize - internal static class __Error - { - internal static Exception GetStreamIsClosed() - { - return new ObjectDisposedException(null, SR.ObjectDisposed_StreamIsClosed); - } - - internal static Exception GetReadNotSupported() - { - return new NotSupportedException(SR.NotSupported_UnreadableStream); - } - - internal static Exception GetWriteNotSupported() - { - return new NotSupportedException(SR.NotSupported_UnwritableStream); - } - } -} diff --git a/external/corefx/src/Common/src/System/IO/PathInternal.Unix.cs b/external/corefx/src/Common/src/System/IO/PathInternal.Unix.cs index 06af7cb5f6..27424cb2fb 100644 --- a/external/corefx/src/Common/src/System/IO/PathInternal.Unix.cs +++ b/external/corefx/src/Common/src/System/IO/PathInternal.Unix.cs @@ -2,6 +2,7 @@ // 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.Diagnostics; using System.Text; @@ -14,18 +15,9 @@ namespace System.IO private const char InvalidPathChar = '\0'; internal static char[] GetInvalidPathChars() => new char[] { InvalidPathChar }; - internal static readonly int MaxComponentLength = Interop.Sys.MaxName; - internal const string ParentDirectoryPrefix = @"../"; - /// Returns a value indicating if the given path contains invalid characters. - internal static bool HasIllegalCharacters(string path) - { - Debug.Assert(path != null); - return path.IndexOf(InvalidPathChar) >= 0; - } - - internal static int GetRootLength(string path) + internal static int GetRootLength(ReadOnlySpan path) { return path.Length > 0 && IsDirectorySeparator(path[0]) ? 1 : 0; } @@ -76,7 +68,7 @@ namespace System.IO return builder.ToString(); } - + /// /// Returns true if the character is a directory or volume separator. /// @@ -89,5 +81,12 @@ namespace System.IO Debug.Assert(Path.DirectorySeparatorChar == Path.VolumeSeparatorChar); return ch == Path.DirectorySeparatorChar; } + + internal static bool IsPartiallyQualified(string path) + { + // This is much simpler than Windows where paths can be rooted, but not fully qualified (such as Drive Relative) + // As long as the path is rooted in Unix it doesn't use the current directory and therefore is fully qualified. + return string.IsNullOrEmpty(path) || path[0] != Path.DirectorySeparatorChar; + } } } diff --git a/external/corefx/src/Common/src/System/IO/PathInternal.Windows.cs b/external/corefx/src/Common/src/System/IO/PathInternal.Windows.cs index d29835b64b..1d0dcbaf72 100644 --- a/external/corefx/src/Common/src/System/IO/PathInternal.Windows.cs +++ b/external/corefx/src/Common/src/System/IO/PathInternal.Windows.cs @@ -56,7 +56,6 @@ namespace System.IO internal const int UncPrefixLength = 2; // \\?\UNC\, \\.\UNC\ internal const int UncExtendedPrefixLength = 8; - internal static readonly int MaxComponentLength = 255; internal static char[] GetInvalidPathChars() => new char[] { @@ -157,34 +156,6 @@ namespace System.IO && path[3] == '\\'; } - /// - /// Returns a value indicating if the given path contains invalid characters (", <, >, | - /// NUL, or any ASCII char whose integer representation is in the range of 1 through 31). - /// Does not check for wild card characters ? and *. - /// - internal static bool HasIllegalCharacters(string path) - { - // This is equivalent to IndexOfAny(InvalidPathChars) >= 0, - // except faster since IndexOfAny grows slower as the input - // array grows larger. - // Since we know that some of the characters we're looking - // for are contiguous in the alphabet-- the path cannot contain - // characters 0-31-- we can optimize this for our specific use - // case and use simple comparison operations. - - for (int i = 0; i < path.Length; i++) - { - char c = path[i]; - - if (c <= '\u001f' || c == '|') - { - return true; - } - } - - return false; - } - /// /// Check for known wildcard characters. '*' and '?' are the most common ones. /// diff --git a/external/corefx/src/Common/src/System/IO/PathInternal.cs b/external/corefx/src/Common/src/System/IO/PathInternal.cs index 59e8aeb7b1..4d221e01a2 100644 --- a/external/corefx/src/Common/src/System/IO/PathInternal.cs +++ b/external/corefx/src/Common/src/System/IO/PathInternal.cs @@ -10,22 +10,6 @@ namespace System.IO /// Contains internal path helpers that are shared between many projects. internal static partial class PathInternal { - /// - /// Checks for invalid path characters in the given path. - /// - /// Thrown if the path is null. - /// Thrown if the path has invalid characters. - /// The path to check for invalid characters. - internal static void CheckInvalidPathChars(string path) - { - if (path == null) - throw new ArgumentNullException(nameof(path)); - - if (HasIllegalCharacters(path)) - throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path)); - } - - /// /// Returns true if the given StringBuilder starts with the given value. /// @@ -82,33 +66,6 @@ namespace System.IO builder.Length = end + 1; return builder; } - - /// - /// Returns the start index of the filename - /// in the given path, or 0 if no directory - /// or volume separator is found. - /// - /// The path in which to find the index of the filename. - /// - /// This method returns path.Length for - /// inputs like "/usr/foo/" on Unix. As such, - /// it is not safe for being used to index - /// the string without additional verification. - /// - internal static int FindFileNameIndex(string path) - { - Debug.Assert(path != null); - CheckInvalidPathChars(path); - - for (int i = path.Length - 1; i >= 0; i--) - { - char ch = path[i]; - if (IsDirectoryOrVolumeSeparator(ch)) - return i + 1; - } - - return 0; // the whole path is the filename - } /// /// Returns true if the path ends in a directory separator. diff --git a/external/corefx/src/Common/src/System/IO/Win32Marshal.cs b/external/corefx/src/Common/src/System/IO/Win32Marshal.cs index e1afc9fc0c..d45b98bdc5 100644 --- a/external/corefx/src/Common/src/System/IO/Win32Marshal.cs +++ b/external/corefx/src/Common/src/System/IO/Win32Marshal.cs @@ -21,7 +21,7 @@ namespace System.IO } /// - /// Converts, resetting it, the last Win32 error into a corresponding object, optionally + /// Converts, resetting it, the last Win32 error into a corresponding object, optionally /// including the specified path in the error message. /// internal static Exception GetExceptionForLastWin32Error(string path) @@ -39,7 +39,7 @@ namespace System.IO } /// - /// Converts the specified Win32 error into a corresponding object, optionally + /// Converts the specified Win32 error into a corresponding object, optionally /// including the specified path in the error message. /// internal static Exception GetExceptionForWin32Error(int errorCode, string path) @@ -71,7 +71,9 @@ namespace System.IO return new IOException(SR.Format(SR.IO_AlreadyExists_Name, path), MakeHRFromErrorCode(errorCode)); case Interop.Errors.ERROR_FILENAME_EXCED_RANGE: - return new PathTooLongException(SR.IO_PathTooLong); + return !string.IsNullOrEmpty(path) ? + new PathTooLongException(SR.Format(SR.IO_PathTooLong_Path, path)) : + new PathTooLongException(SR.IO_PathTooLong); case Interop.Errors.ERROR_INVALID_PARAMETER: return new IOException(GetMessage(errorCode), MakeHRFromErrorCode(errorCode)); diff --git a/external/corefx/src/Common/src/System/Marvin.cs b/external/corefx/src/Common/src/System/Marvin.cs index fde7d2be57..ee22d27864 100644 --- a/external/corefx/src/Common/src/System/Marvin.cs +++ b/external/corefx/src/Common/src/System/Marvin.cs @@ -14,74 +14,53 @@ namespace System /// Convenience method to compute a Marvin hash and collapse it into a 32-bit hash. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int ComputeHash32(ref byte data, int count, ulong seed) + public static int ComputeHash32(ReadOnlySpan data, ulong seed) { - long hash64 = ComputeHash(ref data, count, seed); + long hash64 = ComputeHash(data, seed); return ((int)(hash64 >> 32)) ^ (int)hash64; } /// /// Computes a 64-hash using the Marvin algorithm. /// - public static long ComputeHash(ref byte data, int count, ulong seed) + public static long ComputeHash(ReadOnlySpan data, ulong seed) { - uint ucount = (uint)count; uint p0 = (uint)seed; uint p1 = (uint)(seed >> 32); - int byteOffset = 0; // declared as signed int so we don't have to cast everywhere (it's passed to Unsafe.Add() and used for nothing else.) - - while (ucount >= 8) + if (data.Length >= sizeof(uint)) { - p0 += Unsafe.As(ref Unsafe.Add(ref data, byteOffset)); - Block(ref p0, ref p1); + ReadOnlySpan uData = data.NonPortableCast(); - p0 += Unsafe.As(ref Unsafe.Add(ref data, byteOffset + 4)); - Block(ref p0, ref p1); + for (int i = 0; i < uData.Length; i++) + { + p0 += uData[i]; + Block(ref p0, ref p1); + } - byteOffset += 8; - ucount -= 8; + // byteOffset = data.Length - data.Length % 4 + // is equivalent to clearing last 2 bits of length + // Using it directly gives a perf hit for short strings making it at least 5% or more slower. + int byteOffset = data.Length & (~3); + data = data.Slice(byteOffset); } - switch (ucount) + switch (data.Length) { - case 4: - p0 += Unsafe.As(ref Unsafe.Add(ref data, byteOffset)); - Block(ref p0, ref p1); - goto case 0; - case 0: p0 += 0x80u; break; - case 5: - p0 += Unsafe.As(ref Unsafe.Add(ref data, byteOffset)); - byteOffset += 4; - Block(ref p0, ref p1); - goto case 1; - case 1: - p0 += 0x8000u | Unsafe.Add(ref data, byteOffset); + p0 += 0x8000u | data[0]; break; - case 6: - p0 += Unsafe.As(ref Unsafe.Add(ref data, byteOffset)); - byteOffset += 4; - Block(ref p0, ref p1); - goto case 2; - case 2: - p0 += 0x800000u | Unsafe.As(ref Unsafe.Add(ref data, byteOffset)); + p0 += 0x800000u | data.NonPortableCast()[0]; break; - case 7: - p0 += Unsafe.As(ref Unsafe.Add(ref data, byteOffset)); - byteOffset += 4; - Block(ref p0, ref p1); - goto case 3; - case 3: - p0 += 0x80000000u | (((uint)(Unsafe.Add(ref data, byteOffset + 2))) << 16)| (uint)(Unsafe.As(ref Unsafe.Add(ref data, byteOffset))); + p0 += 0x80000000u | (((uint)data[2]) << 16) | (uint)(data.NonPortableCast()[0]); break; default: diff --git a/external/corefx/src/Common/src/System/Memory/FixedBufferExtensions.cs b/external/corefx/src/Common/src/System/Memory/FixedBufferExtensions.cs index 08cb16fe1f..ce6b827afc 100644 --- a/external/corefx/src/Common/src/System/Memory/FixedBufferExtensions.cs +++ b/external/corefx/src/Common/src/System/Memory/FixedBufferExtensions.cs @@ -2,6 +2,8 @@ // 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.Runtime.InteropServices; + namespace System { internal static class FixedBufferExtensions @@ -11,7 +13,7 @@ namespace System /// internal unsafe static string GetStringFromFixedBuffer(this ReadOnlySpan span) { - fixed (char* c = &span.DangerousGetPinnableReference()) + fixed (char* c = &MemoryMarshal.GetReference(span)) { return new string(c, 0, span.GetFixedBufferStringLength()); } diff --git a/external/corefx/src/Common/src/System/Net/ContextAwareResult.cs b/external/corefx/src/Common/src/System/Net/ContextAwareResult.cs index 5d1d1037ca..f716f25820 100644 --- a/external/corefx/src/Common/src/System/Net/ContextAwareResult.cs +++ b/external/corefx/src/Common/src/System/Net/ContextAwareResult.cs @@ -393,5 +393,7 @@ namespace System.Net if (NetEventSource.IsEnabled) NetEventSource.Info(this, "Context set, calling callback."); base.Complete(IntPtr.Zero); } + + internal virtual EndPoint RemoteEndPoint => null; } } diff --git a/external/corefx/src/Common/src/System/Net/ContextFlagsAdapterPal.Unix.cs b/external/corefx/src/Common/src/System/Net/ContextFlagsAdapterPal.Unix.cs index d75cf97a2b..599bd4ab7d 100644 --- a/external/corefx/src/Common/src/System/Net/ContextFlagsAdapterPal.Unix.cs +++ b/external/corefx/src/Common/src/System/Net/ContextFlagsAdapterPal.Unix.cs @@ -8,7 +8,7 @@ namespace System.Net { internal static class ContextFlagsAdapterPal { - private struct ContextFlagMapping + private readonly struct ContextFlagMapping { public readonly Interop.NetSecurityNative.GssFlags GssFlags; public readonly ContextFlagsPal ContextFlag; @@ -26,7 +26,8 @@ namespace System.Net new ContextFlagMapping(Interop.NetSecurityNative.GssFlags.GSS_C_IDENTIFY_FLAG, ContextFlagsPal.InitIdentify), new ContextFlagMapping(Interop.NetSecurityNative.GssFlags.GSS_C_MUTUAL_FLAG, ContextFlagsPal.MutualAuth), new ContextFlagMapping(Interop.NetSecurityNative.GssFlags.GSS_C_REPLAY_FLAG, ContextFlagsPal.ReplayDetect), - new ContextFlagMapping(Interop.NetSecurityNative.GssFlags.GSS_C_SEQUENCE_FLAG, ContextFlagsPal.SequenceDetect) + new ContextFlagMapping(Interop.NetSecurityNative.GssFlags.GSS_C_SEQUENCE_FLAG, ContextFlagsPal.SequenceDetect), + new ContextFlagMapping(Interop.NetSecurityNative.GssFlags.GSS_C_DELEG_FLAG, ContextFlagsPal.Delegate) }; diff --git a/external/corefx/src/Common/src/System/Net/ContextFlagsAdapterPal.Windows.cs b/external/corefx/src/Common/src/System/Net/ContextFlagsAdapterPal.Windows.cs index c1c037a30a..5ed6e347d8 100644 --- a/external/corefx/src/Common/src/System/Net/ContextFlagsAdapterPal.Windows.cs +++ b/external/corefx/src/Common/src/System/Net/ContextFlagsAdapterPal.Windows.cs @@ -8,7 +8,7 @@ namespace System.Net { internal static class ContextFlagsAdapterPal { - private struct ContextFlagMapping + private readonly struct ContextFlagMapping { public readonly Interop.SspiCli.ContextFlags Win32Flag; public readonly ContextFlagsPal ContextFlag; diff --git a/external/corefx/src/Common/src/System/Net/CookieParser.cs b/external/corefx/src/Common/src/System/Net/CookieParser.cs index 3a8d5e104e..8f7fd3400d 100644 --- a/external/corefx/src/Common/src/System/Net/CookieParser.cs +++ b/external/corefx/src/Common/src/System/Net/CookieParser.cs @@ -905,5 +905,10 @@ namespace System.Net return value.Length == 2 ? string.Empty : value.Substring(1, value.Length - 2); } + + internal bool EndofHeader() + { + return _tokenizer.Eof; + } } } diff --git a/external/corefx/src/Common/src/System/Net/NetworkInformation/NetworkInformationException.cs b/external/corefx/src/Common/src/System/Net/NetworkInformation/NetworkInformationException.cs index aaa5ac4c1e..2252191f41 100644 --- a/external/corefx/src/Common/src/System/Net/NetworkInformation/NetworkInformationException.cs +++ b/external/corefx/src/Common/src/System/Net/NetworkInformation/NetworkInformationException.cs @@ -15,6 +15,9 @@ namespace System.Net.NetworkInformation /// /// [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class NetworkInformationException : Win32Exception { /// @@ -37,7 +40,6 @@ namespace System.Net.NetworkInformation protected NetworkInformationException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { - throw new PlatformNotSupportedException(); } internal NetworkInformationException(SocketError socketError) : base((int)socketError) diff --git a/external/corefx/src/Common/src/System/Net/SafeCloseSocket.cs b/external/corefx/src/Common/src/System/Net/SafeCloseSocket.cs index 6e0d5b8457..036ae305c8 100644 --- a/external/corefx/src/Common/src/System/Net/SafeCloseSocket.cs +++ b/external/corefx/src/Common/src/System/Net/SafeCloseSocket.cs @@ -182,7 +182,6 @@ namespace System.Net.Sockets // Now free it with blocking. innerSocket.BlockingRelease(); } - #if DEBUG } catch (Exception exception) when (!ExceptionCheck.IsFatal(exception)) diff --git a/external/corefx/src/Common/src/System/Net/Security/Unix/SafeDeleteSslContext.cs b/external/corefx/src/Common/src/System/Net/Security/Unix/SafeDeleteSslContext.cs index 5941fa5685..ded5784ae8 100644 --- a/external/corefx/src/Common/src/System/Net/Security/Unix/SafeDeleteSslContext.cs +++ b/external/corefx/src/Common/src/System/Net/Security/Unix/SafeDeleteSslContext.cs @@ -5,6 +5,7 @@ using Microsoft.Win32.SafeHandles; using System.Diagnostics; +using System.Net.Security; using System.Runtime.InteropServices; using System.Security.Authentication; using System.Security.Authentication.ExtendedProtection; @@ -25,7 +26,7 @@ namespace System.Net.Security } } - public SafeDeleteSslContext(SafeFreeSslCredentials credential, bool isServer, bool remoteCertRequired) + public SafeDeleteSslContext(SafeFreeSslCredentials credential, SslAuthenticationOptions sslAuthenticationOptions) : base(credential) { Debug.Assert((null != credential) && !credential.IsInvalid, "Invalid credential used in SafeDeleteSslContext"); @@ -37,8 +38,7 @@ namespace System.Net.Security credential.CertHandle, credential.CertKeyHandle, credential.Policy, - isServer, - remoteCertRequired); + sslAuthenticationOptions); } catch(Exception ex) { diff --git a/external/corefx/src/Common/src/System/Net/SecurityStatusAdapterPal.Windows.cs b/external/corefx/src/Common/src/System/Net/SecurityStatusAdapterPal.Windows.cs index f24ff1e182..b7ce1e6b1e 100644 --- a/external/corefx/src/Common/src/System/Net/SecurityStatusAdapterPal.Windows.cs +++ b/external/corefx/src/Common/src/System/Net/SecurityStatusAdapterPal.Windows.cs @@ -10,7 +10,7 @@ namespace System.Net { internal static class SecurityStatusAdapterPal { - private const int StatusDictionarySize = 40; + private const int StatusDictionarySize = 41; #if DEBUG static SecurityStatusAdapterPal() @@ -22,6 +22,7 @@ namespace System.Net private static readonly BidirectionalDictionary s_statusDictionary = new BidirectionalDictionary(StatusDictionarySize) { { Interop.SECURITY_STATUS.AlgorithmMismatch, SecurityStatusPalErrorCode.AlgorithmMismatch }, + { Interop.SECURITY_STATUS.ApplicationProtocolMismatch, SecurityStatusPalErrorCode.ApplicationProtocolMismatch }, { Interop.SECURITY_STATUS.BadBinding, SecurityStatusPalErrorCode.BadBinding }, { Interop.SECURITY_STATUS.BufferNotEnough, SecurityStatusPalErrorCode.BufferNotEnough }, { Interop.SECURITY_STATUS.CannotInstall, SecurityStatusPalErrorCode.CannotInstall }, diff --git a/external/corefx/src/Common/src/System/Net/SecurityStatusPal.cs b/external/corefx/src/Common/src/System/Net/SecurityStatusPal.cs index 3ebb633071..54b380e74f 100644 --- a/external/corefx/src/Common/src/System/Net/SecurityStatusPal.cs +++ b/external/corefx/src/Common/src/System/Net/SecurityStatusPal.cs @@ -4,7 +4,7 @@ namespace System.Net { - internal struct SecurityStatusPal + internal readonly struct SecurityStatusPal { public readonly SecurityStatusPalErrorCode ErrorCode; public readonly Exception Exception; @@ -67,6 +67,7 @@ namespace System.Net SmartcardLogonRequired, UnsupportedPreauth, BadBinding, - DowngradeDetected + DowngradeDetected, + ApplicationProtocolMismatch } } diff --git a/external/corefx/src/Common/src/System/Net/SocketAddressPal.Unix.cs b/external/corefx/src/Common/src/System/Net/SocketAddressPal.Unix.cs index 93f3377939..f7eaf04fbd 100644 --- a/external/corefx/src/Common/src/System/Net/SocketAddressPal.Unix.cs +++ b/external/corefx/src/Common/src/System/Net/SocketAddressPal.Unix.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.Net.Sockets; +using System.Runtime.InteropServices; using System.Text; namespace System.Net @@ -119,7 +120,7 @@ namespace System.Net uint localScope; Interop.Error err; fixed (byte* rawAddress = buffer) - fixed (byte* ipAddress = &address.DangerousGetPinnableReference()) + fixed (byte* ipAddress = &MemoryMarshal.GetReference(address)) { err = Interop.Sys.GetIPv6Address(rawAddress, buffer.Length, ipAddress, address.Length, &localScope); } @@ -147,7 +148,7 @@ namespace System.Net public static unsafe void SetIPv6Address(byte[] buffer, Span address, uint scope) { - fixed (byte* rawInput = &address.DangerousGetPinnableReference()) + fixed (byte* rawInput = &MemoryMarshal.GetReference(address)) { SetIPv6Address(buffer, rawInput, address.Length, scope); } diff --git a/external/corefx/src/Common/src/System/Net/WebSockets/ManagedWebSocket.cs b/external/corefx/src/Common/src/System/Net/WebSockets/ManagedWebSocket.cs index f4dcd9bbc3..b0938c5a6a 100644 --- a/external/corefx/src/Common/src/System/Net/WebSockets/ManagedWebSocket.cs +++ b/external/corefx/src/Common/src/System/Net/WebSockets/ManagedWebSocket.cs @@ -13,8 +13,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -// NOTE: This file is shared between CoreFX and ASP.NET. Be very thoughtful when changing it. - namespace System.Net.WebSockets { /// A managed implementation of a web socket that sends and receives data via a . @@ -26,7 +24,7 @@ namespace System.Net.WebSockets /// a send operation while another is in progress or a receive operation while another is in progress will /// result in an exception. /// - internal sealed class ManagedWebSocket : WebSocket + internal sealed partial class ManagedWebSocket : WebSocket { /// Creates a from a connected to a websocket endpoint. /// The connected Stream. @@ -37,9 +35,9 @@ namespace System.Net.WebSockets /// Optional buffer to use for receives. /// The created instance. public static ManagedWebSocket CreateFromConnectedStream( - Stream stream, bool isServer, string subprotocol, TimeSpan keepAliveInterval, int receiveBufferSize, ArraySegment? receiveBuffer = null) + Stream stream, bool isServer, string subprotocol, TimeSpan keepAliveInterval, Memory buffer) { - return new ManagedWebSocket(stream, isServer, subprotocol, keepAliveInterval, receiveBufferSize, receiveBuffer); + return new ManagedWebSocket(stream, isServer, subprotocol, keepAliveInterval, buffer); } /// Thread-safe random number generator used to generate masks for each send. @@ -56,12 +54,17 @@ namespace System.Net.WebSockets /// Valid states to be in when calling CloseAsync. private static readonly WebSocketState[] s_validCloseStates = { WebSocketState.Open, WebSocketState.CloseReceived, WebSocketState.CloseSent }; + /// Successfully completed task representing a close message. + private static readonly Task s_cachedCloseTask = Task.FromResult(new WebSocketReceiveResult(0, WebSocketMessageType.Close, true)); + /// The maximum size in bytes of a message frame header that includes mask bytes. - private const int MaxMessageHeaderLength = 14; + internal const int MaxMessageHeaderLength = 14; /// The maximum size of a control message payload. private const int MaxControlPayloadLength = 125; /// Length of the mask XOR'd with the payload data. private const int MaskLength = 4; + /// Default length of a receive buffer to create when an invalid scratch buffer is provided. + private const int DefaultReceiveBufferSize = 0x1000; /// The stream used to communicate with the remote server. private readonly Stream _stream; @@ -79,9 +82,7 @@ namespace System.Net.WebSockets /// CancellationTokenSource used to abort all current and future operations when anything is canceled or any error occurs. private readonly CancellationTokenSource _abortSource = new CancellationTokenSource(); /// Buffer used for reading data from the network. - private byte[] _receiveBuffer; - /// Gets whether the receive buffer came from the ArrayPool. - private readonly bool _receiveBufferFromPool; + private Memory _receiveBuffer; /// /// Tracks the state of the validity of the UTF8 encoding of text payloads. Text may be split across fragments. /// @@ -152,7 +153,7 @@ namespace System.Net.WebSockets /// The task returned from the last ReceiveAsync operation to not complete synchronously. /// If this is not null and not completed when a subsequent ReceiveAsync is issued, an exception occurs. /// - private Task _lastReceiveAsync; + private Task _lastReceiveAsync; /// Lock used to protect update and check-and-update operations on _state. private object StateUpdateLock => _abortSource; @@ -169,8 +170,8 @@ namespace System.Net.WebSockets /// The agreed upon subprotocol for the connection. /// The interval to use for keep-alive pings. /// The buffer size to use for received data. - /// Optional buffer to use for receives - private ManagedWebSocket(Stream stream, bool isServer, string subprotocol, TimeSpan keepAliveInterval, int receiveBufferSize, ArraySegment? receiveBuffer) + /// Optional buffer to use for receives + private ManagedWebSocket(Stream stream, bool isServer, string subprotocol, TimeSpan keepAliveInterval, Memory buffer) { Debug.Assert(StateUpdateLock != null, $"Expected {nameof(StateUpdateLock)} to be non-null"); Debug.Assert(ReceiveAsyncLock != null, $"Expected {nameof(ReceiveAsyncLock)} to be non-null"); @@ -180,26 +181,19 @@ namespace System.Net.WebSockets Debug.Assert(stream.CanRead, $"Expected readable stream"); Debug.Assert(stream.CanWrite, $"Expected writeable stream"); Debug.Assert(keepAliveInterval == Timeout.InfiniteTimeSpan || keepAliveInterval >= TimeSpan.Zero, $"Invalid keepalive interval: {keepAliveInterval}"); - Debug.Assert(receiveBufferSize >= MaxMessageHeaderLength, $"Receive buffer size {receiveBufferSize} is too small"); _stream = stream; _isServer = isServer; _subprotocol = subprotocol; - // If we were provided with a buffer to use, use it, as long as it's big enough for our needs, and for simplicity - // as long as we're not supposed to use only a portion of it. If it doesn't meet our criteria, just create a new one. - if (receiveBuffer.HasValue && - receiveBuffer.GetValueOrDefault().Array != null && - receiveBuffer.GetValueOrDefault().Offset == 0 && receiveBuffer.GetValueOrDefault().Count == receiveBuffer.GetValueOrDefault().Array.Length && - receiveBuffer.GetValueOrDefault().Count >= MaxMessageHeaderLength) - { - _receiveBuffer = receiveBuffer.Value.Array; - } - else - { - _receiveBufferFromPool = true; - _receiveBuffer = ArrayPool.Shared.Rent(Math.Max(receiveBufferSize, MaxMessageHeaderLength)); - } + // If we were provided with a buffer to use, use it as long as it's big enough for our needs. + // If it doesn't meet our criteria, just create a new one. If we need to create a new one, + // we avoid using the pool because often many web sockets will be used concurrently, and if each web + // socket rents a similarly sized buffer from the pool for its duration, we'll end up draining + // the pool, such that other web sockets will allocate anyway, as will anyone else in the process using the + // pool. If someone wants to pool, they can do so by passing in the buffer they want to use, and they can + // get it from whatever pool they like. + _receiveBuffer = buffer.Length >= MaxMessageHeaderLength ? buffer : new byte[DefaultReceiveBufferSize]; // Set up the abort source so that if it's triggered, we transition the instance appropriately. _abortSource.Token.Register(s => @@ -241,12 +235,6 @@ namespace System.Net.WebSockets _disposed = true; _keepAliveTimer?.Dispose(); _stream?.Dispose(); - if (_receiveBufferFromPool) - { - byte[] old = _receiveBuffer; - _receiveBuffer = null; - ArrayPool.Shared.Return(old); - } if (_state < WebSocketState.Aborted) { _state = WebSocketState.Closed; @@ -271,11 +259,25 @@ namespace System.Net.WebSockets nameof(WebSocketMessageType.Close), nameof(SendAsync), nameof(WebSocketMessageType.Binary), nameof(WebSocketMessageType.Text), nameof(CloseOutputAsync)), nameof(messageType)); } + WebSocketValidate.ValidateArraySegment(buffer, nameof(buffer)); + return SendPrivateAsync((ReadOnlyMemory)buffer, messageType, endOfMessage, cancellationToken); + } + + private Task SendPrivateAsync(ReadOnlyMemory buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken) + { + if (messageType != WebSocketMessageType.Text && messageType != WebSocketMessageType.Binary) + { + throw new ArgumentException(SR.Format( + SR.net_WebSockets_Argument_InvalidMessageType, + nameof(WebSocketMessageType.Close), nameof(SendAsync), nameof(WebSocketMessageType.Binary), nameof(WebSocketMessageType.Text), nameof(CloseOutputAsync)), + nameof(messageType)); + } + try { - WebSocketValidate.ThrowIfInvalidState(_state, _disposed, s_validSendStates); + WebSocketValidate.ThrowIfInvalidState(_state, _disposed, s_validSendStates); ThrowIfOperationInProgress(_lastSendAsync); } catch (Exception exc) @@ -306,7 +308,7 @@ namespace System.Net.WebSockets lock (ReceiveAsyncLock) // synchronize with receives in CloseAsync { ThrowIfOperationInProgress(_lastReceiveAsync); - Task t = ReceiveAsyncPrivate(buffer, cancellationToken); + Task t = ReceiveAsyncPrivate(buffer, cancellationToken).AsTask(); _lastReceiveAsync = t; return t; } @@ -360,7 +362,7 @@ namespace System.Net.WebSockets /// The value of the FIN bit for the message. /// The buffer containing the payload data fro the message. /// The CancellationToken to use to cancel the websocket. - private Task SendFrameAsync(MessageOpcode opcode, bool endOfMessage, ArraySegment payloadBuffer, CancellationToken cancellationToken) + private Task SendFrameAsync(MessageOpcode opcode, bool endOfMessage, ReadOnlyMemory payloadBuffer, CancellationToken cancellationToken) { // TODO: #4900 SendFrameAsync should in theory typically complete synchronously, making it fast and allocation free. // However, due to #4900, it almost always yields, resulting in all of the allocations involved in an async method @@ -384,7 +386,7 @@ namespace System.Net.WebSockets /// The opcode for the message. /// The value of the FIN bit for the message. /// The buffer containing the payload data fro the message. - private Task SendFrameLockAcquiredNonCancelableAsync(MessageOpcode opcode, bool endOfMessage, ArraySegment payloadBuffer) + private Task SendFrameLockAcquiredNonCancelableAsync(MessageOpcode opcode, bool endOfMessage, ReadOnlyMemory payloadBuffer) { Debug.Assert(_sendFrameAsyncLock.CurrentCount == 0, "Caller should hold the _sendFrameAsyncLock"); @@ -395,7 +397,7 @@ namespace System.Net.WebSockets try { // Write the payload synchronously to the buffer, then write that buffer out to the network. - int sendBytes = WriteFrameToSendBuffer(opcode, endOfMessage, payloadBuffer); + int sendBytes = WriteFrameToSendBuffer(opcode, endOfMessage, payloadBuffer.Span); writeTask = _stream.WriteAsync(_sendBuffer, 0, sendBytes, CancellationToken.None); // If the operation happens to complete synchronously (or, more specifically, by @@ -444,12 +446,12 @@ namespace System.Net.WebSockets }, this, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); } - private async Task SendFrameFallbackAsync(MessageOpcode opcode, bool endOfMessage, ArraySegment payloadBuffer, CancellationToken cancellationToken) + private async Task SendFrameFallbackAsync(MessageOpcode opcode, bool endOfMessage, ReadOnlyMemory payloadBuffer, CancellationToken cancellationToken) { await _sendFrameAsyncLock.WaitAsync().ConfigureAwait(false); try { - int sendBytes = WriteFrameToSendBuffer(opcode, endOfMessage, payloadBuffer); + int sendBytes = WriteFrameToSendBuffer(opcode, endOfMessage, payloadBuffer.Span); using (cancellationToken.Register(s => ((ManagedWebSocket)s).Abort(), this)) { await _stream.WriteAsync(_sendBuffer, 0, sendBytes, cancellationToken).ConfigureAwait(false); @@ -469,10 +471,10 @@ namespace System.Net.WebSockets } /// Writes a frame into the send buffer, which can then be sent over the network. - private int WriteFrameToSendBuffer(MessageOpcode opcode, bool endOfMessage, ArraySegment payloadBuffer) + private int WriteFrameToSendBuffer(MessageOpcode opcode, bool endOfMessage, ReadOnlySpan payloadBuffer) { // Ensure we have a _sendBuffer. - AllocateSendBuffer(payloadBuffer.Count + MaxMessageHeaderLength); + AllocateSendBuffer(payloadBuffer.Length + MaxMessageHeaderLength); // Write the message header data to the buffer. int headerLength; @@ -492,20 +494,20 @@ namespace System.Net.WebSockets } // Write the payload - if (payloadBuffer.Count > 0) + if (payloadBuffer.Length > 0) { - Buffer.BlockCopy(payloadBuffer.Array, payloadBuffer.Offset, _sendBuffer, headerLength, payloadBuffer.Count); + payloadBuffer.CopyTo(new Span(_sendBuffer, headerLength, payloadBuffer.Length)); // If we added a mask to the header, XOR the payload with the mask. We do the manipulation in the send buffer so as to avoid // changing the data in the caller-supplied payload buffer. if (maskOffset.HasValue) { - ApplyMask(_sendBuffer, headerLength, _sendBuffer, maskOffset.Value, 0, payloadBuffer.Count); + ApplyMask(new Span(_sendBuffer, headerLength, payloadBuffer.Length), _sendBuffer, maskOffset.Value, 0); } } // Return the number of bytes in the send buffer - return headerLength + payloadBuffer.Count; + return headerLength + payloadBuffer.Length; } private void SendKeepAliveFrameAsync() @@ -515,10 +517,10 @@ namespace System.Net.WebSockets { // This exists purely to keep the connection alive; don't wait for the result, and ignore any failures. // The call will handle releasing the lock. - Task t = SendFrameLockAcquiredNonCancelableAsync(MessageOpcode.Ping, true, new ArraySegment(Array.Empty())); + Task t = SendFrameLockAcquiredNonCancelableAsync(MessageOpcode.Ping, true, Memory.Empty); // "Observe" any exception, ignoring it to prevent the unobserved exception event from being raised. - if (!t.IsCompletedSuccessfully) + if (t.Status != TaskStatus.RanToCompletion) { t.ContinueWith(p => { Exception ignored = p.Exception; }, CancellationToken.None, @@ -533,7 +535,7 @@ namespace System.Net.WebSockets } } - private static int WriteHeader(MessageOpcode opcode, byte[] sendBuffer, ArraySegment payload, bool endOfMessage, bool useMask) + private static int WriteHeader(MessageOpcode opcode, byte[] sendBuffer, ReadOnlySpan payload, bool endOfMessage, bool useMask) { // Client header format: // 1 bit - FIN - 1 if this is the final fragment in the message (it could be the only fragment), otherwise 0 @@ -566,22 +568,22 @@ namespace System.Net.WebSockets // Store the payload length. int maskOffset; - if (payload.Count <= 125) + if (payload.Length <= 125) { - sendBuffer[1] = (byte)payload.Count; + sendBuffer[1] = (byte)payload.Length; maskOffset = 2; // no additional payload length } - else if (payload.Count <= ushort.MaxValue) + else if (payload.Length <= ushort.MaxValue) { sendBuffer[1] = 126; - sendBuffer[2] = (byte)(payload.Count / 256); - sendBuffer[3] = unchecked((byte)payload.Count); + sendBuffer[2] = (byte)(payload.Length / 256); + sendBuffer[3] = unchecked((byte)payload.Length); maskOffset = 2 + sizeof(ushort); // additional 2 bytes for 16-bit length } else { sendBuffer[1] = 127; - int length = payload.Count; + int length = payload.Length; for (int i = 9; i >= 2; i--) { sendBuffer[i] = unchecked((byte)length); @@ -614,8 +616,13 @@ namespace System.Net.WebSockets /// /// The buffer into which payload data should be written. /// The CancellationToken used to cancel the websocket. + /// Used to get the result. Allows the same method to be used with both and . /// Information about the received message. - private async Task ReceiveAsyncPrivate(ArraySegment payloadBuffer, CancellationToken cancellationToken) + private async ValueTask ReceiveAsyncPrivate( + Memory payloadBuffer, + CancellationToken cancellationToken, + TWebSocketReceiveResultGetter resultGetter = default) + where TWebSocketReceiveResultGetter : struct, IWebSocketReceiveResultGetter // constrained to avoid boxing and enable inlining { // This is a long method. While splitting it up into pieces would arguably help with readability, doing so would // also result in more allocations, as each async method that yields ends up with multiple allocations. The impact @@ -648,7 +655,7 @@ namespace System.Net.WebSockets // Then make sure we have the full header based on the payload length. // If this is the server, we also need room for the received mask. - long payloadLength = _receiveBuffer[_receiveBufferOffset + 1] & 0x7F; + long payloadLength = _receiveBuffer.Span[_receiveBufferOffset + 1] & 0x7F; if (_isServer || payloadLength > 125) { int minNeeded = @@ -676,7 +683,8 @@ namespace System.Net.WebSockets } else if (header.Opcode == MessageOpcode.Close) { - return await HandleReceivedCloseAsync(header, cancellationToken).ConfigureAwait(false); + await HandleReceivedCloseAsync(header, cancellationToken).ConfigureAwait(false); + return resultGetter.GetResult(0, WebSocketMessageType.Close, true, _closeStatus, _closeStatusDescription); } // If this is a continuation, replace the opcode with the one of the message it's continuing @@ -689,45 +697,46 @@ namespace System.Net.WebSockets Debug.Assert(header.Opcode == MessageOpcode.Binary || header.Opcode == MessageOpcode.Text, $"Unexpected opcode {header.Opcode}"); // If there's no data to read, return an appropriate result. - int bytesToRead = (int)Math.Min(payloadBuffer.Count, header.PayloadLength); - if (bytesToRead == 0) + if (header.PayloadLength == 0 || payloadBuffer.Length == 0) { _lastReceiveHeader = header; - return new WebSocketReceiveResult( + return resultGetter.GetResult( 0, header.Opcode == MessageOpcode.Text ? WebSocketMessageType.Text : WebSocketMessageType.Binary, - header.PayloadLength == 0 ? header.Fin : false); + header.Fin && header.PayloadLength == 0, + null, null); } // Otherwise, read as much of the payload as we can efficiently, and upate the header to reflect how much data // remains for future reads. - - if (_receiveBufferCount == 0) + int bytesToCopy = Math.Min(payloadBuffer.Length, (int)Math.Min(header.PayloadLength, _receiveBuffer.Length)); + Debug.Assert(bytesToCopy > 0, $"Expected {nameof(bytesToCopy)} > 0"); + if (_receiveBufferCount < bytesToCopy) { - await EnsureBufferContainsAsync(1, cancellationToken, throwOnPrematureClosure: false).ConfigureAwait(false); + await EnsureBufferContainsAsync(bytesToCopy, cancellationToken, throwOnPrematureClosure: true).ConfigureAwait(false); } - int bytesToCopy = Math.Min(bytesToRead, _receiveBufferCount); if (_isServer) { - _receivedMaskOffsetOffset = ApplyMask(_receiveBuffer, _receiveBufferOffset, header.Mask, _receivedMaskOffsetOffset, bytesToCopy); + _receivedMaskOffsetOffset = ApplyMask(_receiveBuffer.Span.Slice(_receiveBufferOffset, bytesToCopy), header.Mask, _receivedMaskOffsetOffset); } - Buffer.BlockCopy(_receiveBuffer, _receiveBufferOffset, payloadBuffer.Array, payloadBuffer.Offset, bytesToCopy); + _receiveBuffer.Span.Slice(_receiveBufferOffset, bytesToCopy).CopyTo(payloadBuffer.Span.Slice(0, bytesToCopy)); ConsumeFromBuffer(bytesToCopy); header.PayloadLength -= bytesToCopy; // If this a text message, validate that it contains valid UTF8. if (header.Opcode == MessageOpcode.Text && - !TryValidateUtf8(new ArraySegment(payloadBuffer.Array, payloadBuffer.Offset, bytesToCopy), header.Fin, _utf8TextState)) + !TryValidateUtf8(payloadBuffer.Span.Slice(0, bytesToCopy), header.Fin, _utf8TextState)) { await CloseWithReceiveErrorAndThrowAsync(WebSocketCloseStatus.InvalidPayloadData, WebSocketError.Faulted, cancellationToken).ConfigureAwait(false); } _lastReceiveHeader = header; - return new WebSocketReceiveResult( + return resultGetter.GetResult( bytesToCopy, header.Opcode == MessageOpcode.Text ? WebSocketMessageType.Text : WebSocketMessageType.Binary, - bytesToCopy == 0 || (header.Fin && header.PayloadLength == 0)); + header.Fin && header.PayloadLength == 0, + null, null); } } catch (Exception exc) @@ -748,7 +757,7 @@ namespace System.Net.WebSockets /// The message header. /// The cancellation token to use to cancel the websocket. /// The received result message. - private async Task HandleReceivedCloseAsync( + private async Task HandleReceivedCloseAsync( MessageHeader header, CancellationToken cancellationToken) { lock (StateUpdateLock) @@ -778,10 +787,10 @@ namespace System.Net.WebSockets if (_isServer) { - ApplyMask(_receiveBuffer, _receiveBufferOffset, header.Mask, 0, header.PayloadLength); + ApplyMask(_receiveBuffer.Span.Slice(_receiveBufferOffset, (int)header.PayloadLength), header.Mask, 0); } - closeStatus = (WebSocketCloseStatus)(_receiveBuffer[_receiveBufferOffset] << 8 | _receiveBuffer[_receiveBufferOffset + 1]); + closeStatus = (WebSocketCloseStatus)(_receiveBuffer.Span[_receiveBufferOffset] << 8 | _receiveBuffer.Span[_receiveBufferOffset + 1]); if (!IsValidCloseStatus(closeStatus)) { await CloseWithReceiveErrorAndThrowAsync(WebSocketCloseStatus.ProtocolError, WebSocketError.Faulted, cancellationToken).ConfigureAwait(false); @@ -791,7 +800,7 @@ namespace System.Net.WebSockets { try { - closeStatusDescription = s_textEncoding.GetString(_receiveBuffer, _receiveBufferOffset + 2, (int)header.PayloadLength - 2); + closeStatusDescription = s_textEncoding.GetString(_receiveBuffer.Span.Slice(_receiveBufferOffset + 2, (int)header.PayloadLength - 2)); } catch (DecoderFallbackException exc) { @@ -804,9 +813,6 @@ namespace System.Net.WebSockets // Store the close status and description onto the instance. _closeStatus = closeStatus; _closeStatusDescription = closeStatusDescription; - - // And return them as part of the result message. - return new WebSocketReceiveResult(0, WebSocketMessageType.Close, true, closeStatus, closeStatusDescription); } /// Processes a received ping or pong message. @@ -825,12 +831,12 @@ namespace System.Net.WebSockets { if (_isServer) { - ApplyMask(_receiveBuffer, _receiveBufferOffset, header.Mask, 0, header.PayloadLength); + ApplyMask(_receiveBuffer.Span.Slice(_receiveBufferOffset, (int)header.PayloadLength), header.Mask, 0); } await SendFrameAsync( MessageOpcode.Pong, true, - new ArraySegment(_receiveBuffer, _receiveBufferOffset, (int)header.PayloadLength), cancellationToken).ConfigureAwait(false); + _receiveBuffer.Slice(_receiveBufferOffset, (int)header.PayloadLength), cancellationToken).ConfigureAwait(false); } // Regardless of whether it was a ping or pong, we no longer need the payload. @@ -908,13 +914,14 @@ namespace System.Net.WebSockets Debug.Assert(_receiveBufferCount >= 2, $"Expected to at least have the first two bytes of the header."); var header = new MessageHeader(); + Span receiveBufferSpan = _receiveBuffer.Span; - header.Fin = (_receiveBuffer[_receiveBufferOffset] & 0x80) != 0; - bool reservedSet = (_receiveBuffer[_receiveBufferOffset] & 0x70) != 0; - header.Opcode = (MessageOpcode)(_receiveBuffer[_receiveBufferOffset] & 0xF); + header.Fin = (receiveBufferSpan[_receiveBufferOffset] & 0x80) != 0; + bool reservedSet = (receiveBufferSpan[_receiveBufferOffset] & 0x70) != 0; + header.Opcode = (MessageOpcode)(receiveBufferSpan[_receiveBufferOffset] & 0xF); - bool masked = (_receiveBuffer[_receiveBufferOffset + 1] & 0x80) != 0; - header.PayloadLength = _receiveBuffer[_receiveBufferOffset + 1] & 0x7F; + bool masked = (receiveBufferSpan[_receiveBufferOffset + 1] & 0x80) != 0; + header.PayloadLength = receiveBufferSpan[_receiveBufferOffset + 1] & 0x7F; ConsumeFromBuffer(2); @@ -922,7 +929,7 @@ namespace System.Net.WebSockets if (header.PayloadLength == 126) { Debug.Assert(_receiveBufferCount >= 2, $"Expected to have two bytes for the payload length."); - header.PayloadLength = (_receiveBuffer[_receiveBufferOffset] << 8) | _receiveBuffer[_receiveBufferOffset + 1]; + header.PayloadLength = (receiveBufferSpan[_receiveBufferOffset] << 8) | receiveBufferSpan[_receiveBufferOffset + 1]; ConsumeFromBuffer(2); } else if (header.PayloadLength == 127) @@ -931,7 +938,7 @@ namespace System.Net.WebSockets header.PayloadLength = 0; for (int i = 0; i < 8; i++) { - header.PayloadLength = (header.PayloadLength << 8) | _receiveBuffer[_receiveBufferOffset + i]; + header.PayloadLength = (header.PayloadLength << 8) | receiveBufferSpan[_receiveBufferOffset + i]; } ConsumeFromBuffer(8); } @@ -943,7 +950,7 @@ namespace System.Net.WebSockets { shouldFail = true; } - header.Mask = CombineMaskBytes(_receiveBuffer, _receiveBufferOffset); + header.Mask = CombineMaskBytes(receiveBufferSpan, _receiveBufferOffset); // Consume the mask bytes ConsumeFromBuffer(4); @@ -1019,7 +1026,7 @@ namespace System.Net.WebSockets while (!_receivedCloseFrame) { Debug.Assert(!Monitor.IsEntered(StateUpdateLock), $"{nameof(StateUpdateLock)} must never be held when acquiring {nameof(ReceiveAsyncLock)}"); - Task receiveTask; + Task receiveTask; lock (ReceiveAsyncLock) { // Now that we're holding the ReceiveAsyncLock, double-check that we've not yet received the close frame. @@ -1036,11 +1043,7 @@ namespace System.Net.WebSockets // a race condition here, e.g. if there's a in-flight receive that completes after we check, but that's fine: worst // case is we then await it, find that it's not what we need, and try again. receiveTask = _lastReceiveAsync; - if (receiveTask == null || - (receiveTask.Status == TaskStatus.RanToCompletion && receiveTask.Result.MessageType != WebSocketMessageType.Close)) - { - _lastReceiveAsync = receiveTask = ReceiveAsyncPrivate(new ArraySegment(closeBuffer), cancellationToken); - } + _lastReceiveAsync = receiveTask = ValidateAndReceiveAsync(receiveTask, closeBuffer, cancellationToken); } // Wait for whatever receive task we have. We'll then loop around again to re-check our state. @@ -1092,7 +1095,7 @@ namespace System.Net.WebSockets buffer[0] = (byte)(closeStatusValue >> 8); buffer[1] = (byte)(closeStatusValue & 0xFF); - await SendFrameAsync(MessageOpcode.Close, true, new ArraySegment(buffer, 0, count), cancellationToken).ConfigureAwait(false); + await SendFrameAsync(MessageOpcode.Close, true, new Memory(buffer, 0, count), cancellationToken).ConfigureAwait(false); } finally { @@ -1130,14 +1133,14 @@ namespace System.Net.WebSockets // If there's any data in the buffer, shift it down. if (_receiveBufferCount > 0) { - Buffer.BlockCopy(_receiveBuffer, _receiveBufferOffset, _receiveBuffer, 0, _receiveBufferCount); + _receiveBuffer.Span.Slice(_receiveBufferOffset, _receiveBufferCount).CopyTo(_receiveBuffer.Span); } _receiveBufferOffset = 0; // While we don't have enough data, read more. while (_receiveBufferCount < minimumRequiredBytes) { - int numRead = await _stream.ReadAsync(_receiveBuffer, _receiveBufferCount, _receiveBuffer.Length - _receiveBufferCount, cancellationToken).ConfigureAwait(false); + int numRead = await _stream.ReadAsync(_receiveBuffer.Slice(_receiveBufferCount, _receiveBuffer.Length - _receiveBufferCount), cancellationToken).ConfigureAwait(false); Debug.Assert(numRead >= 0, $"Expected non-negative bytes read, got {numRead}"); _receiveBufferCount += numRead; if (numRead == 0) @@ -1178,33 +1181,31 @@ namespace System.Net.WebSockets } } - private static unsafe int CombineMaskBytes(byte[] buffer, int maskOffset) => - BitConverter.ToInt32(buffer, maskOffset); + private static unsafe int CombineMaskBytes(Span buffer, int maskOffset) => + BitConverter.ToInt32(buffer.Slice(maskOffset)); /// Applies a mask to a portion of a byte array. /// The buffer to which the mask should be applied. - /// The offset into at which the mask should start to be applied. /// The array containing the mask to apply. /// The offset into of the mask to apply of length . /// The next position offset from of which by to apply next from the mask. - /// The number of bytes starting from to which the mask should be applied. /// The updated maskOffsetOffset value. - private static int ApplyMask(byte[] toMask, int toMaskOffset, byte[] mask, int maskOffset, int maskOffsetIndex, long count) + private static int ApplyMask(Span toMask, byte[] mask, int maskOffset, int maskOffsetIndex) { Debug.Assert(maskOffsetIndex < MaskLength, $"Unexpected {nameof(maskOffsetIndex)}: {maskOffsetIndex}"); Debug.Assert(mask.Length >= MaskLength + maskOffset, $"Unexpected inputs: {mask.Length}, {maskOffset}"); - return ApplyMask(toMask, toMaskOffset, CombineMaskBytes(mask, maskOffset), maskOffsetIndex, count); + return ApplyMask(toMask, CombineMaskBytes(mask, maskOffset), maskOffsetIndex); } /// Applies a mask to a portion of a byte array. /// The buffer to which the mask should be applied. - /// The offset into at which the mask should start to be applied. /// The four-byte mask, stored as an Int32. /// The index into the mask. - /// The number of bytes to mask. /// The next index into the mask to be used for future applications of the mask. - private static unsafe int ApplyMask(byte[] toMask, int toMaskOffset, int mask, int maskIndex, long count) + private static unsafe int ApplyMask(Span toMask, int mask, int maskIndex) { + Debug.Assert(maskIndex < sizeof(int)); + int maskShift = maskIndex * 8; int shiftedMask = (int)(((uint)mask >> maskShift) | ((uint)mask << (32 - maskShift))); #if MONO_FEATURE_SIMD @@ -1212,31 +1213,28 @@ namespace System.Net.WebSockets // as the width of a vector and if the width is an even multiple of the mask. if (Vector.IsHardwareAccelerated && Vector.Count % sizeof(int) == 0 && - count >= Vector.Count) + toMask.Length >= Vector.Count) { - // Mask bytes a vector at a time. Vector maskVector = Vector.AsVectorByte(new Vector(shiftedMask)); - while (count >= Vector.Count) + Span> toMaskVector = toMask.NonPortableCast>(); + for (int i = 0; i < toMaskVector.Length; i++) { - count -= Vector.Count; - (maskVector ^ new Vector(toMask, toMaskOffset)).CopyTo(toMask, toMaskOffset); - toMaskOffset += Vector.Count; + toMaskVector[i] ^= maskVector; } // Fall through to processing any remaining bytes that were less than a vector width. - // Since we processed full masks at a time, we don't need to update maskIndex, and - // toMaskOffset has already been updated to point to the correct location. + toMask = toMask.Slice(Vector.Count * toMaskVector.Length); } #endif // If there are any bytes remaining (either we couldn't use vectors, or the count wasn't // an even multiple of the vector width), process them without vectors. + int count = toMask.Length; if (count > 0) { - fixed (byte* toMaskPtr = toMask) + fixed (byte* toMaskPtr = &MemoryMarshal.GetReference(toMask)) { - // Get the location in the target array to continue processing. - byte* p = toMaskPtr + toMaskOffset; + byte* p = toMaskPtr; // Try to go an int at a time if the remaining data is 4-byte aligned and there's enough remaining. if (((long)p % sizeof(int)) == 0) @@ -1292,16 +1290,16 @@ namespace System.Net.WebSockets // From https://raw.githubusercontent.com/aspnet/WebSockets/dev/src/Microsoft.AspNetCore.WebSockets.Protocol/Utilities.cs // Performs a stateful validation of UTF-8 bytes. // It checks for valid formatting, overlong encodings, surrogates, and value ranges. - private static bool TryValidateUtf8(ArraySegment arraySegment, bool endOfMessage, Utf8MessageState state) + private static bool TryValidateUtf8(Span span, bool endOfMessage, Utf8MessageState state) { - for (int i = arraySegment.Offset; i < arraySegment.Offset + arraySegment.Count;) + for (int i = 0; i < span.Length;) { // Have we started a character sequence yet? if (!state.SequenceInProgress) { // The first byte tells us how many bytes are in the sequence. state.SequenceInProgress = true; - byte b = arraySegment.Array[i]; + byte b = span[i]; i++; if ((b & 0x80) == 0) // 0bbbbbbb, single byte { @@ -1337,9 +1335,9 @@ namespace System.Net.WebSockets return false; } } - while (state.AdditionalBytesExpected > 0 && i < arraySegment.Offset + arraySegment.Count) + while (state.AdditionalBytesExpected > 0 && i < span.Length) { - byte b = arraySegment.Array[i]; + byte b = span[i]; if ((b & 0xC0) != 0x80) { return false; @@ -1405,5 +1403,22 @@ namespace System.Net.WebSockets internal long PayloadLength; internal int Mask; } + + /// + /// Interface used by to enable it to return + /// different result types in an efficient manner. + /// + /// The type of the result + private interface IWebSocketReceiveResultGetter + { + TResult GetResult(int count, WebSocketMessageType messageType, bool endOfMessage, WebSocketCloseStatus? closeStatus, string closeDescription); + } + + /// implementation for . + private readonly struct WebSocketReceiveResultGetter : IWebSocketReceiveResultGetter + { + public WebSocketReceiveResult GetResult(int count, WebSocketMessageType messageType, bool endOfMessage, WebSocketCloseStatus? closeStatus, string closeDescription) => + new WebSocketReceiveResult(count, messageType, endOfMessage, closeStatus, closeDescription); + } } } diff --git a/external/corefx/src/Common/src/System/NotImplemented.cs b/external/corefx/src/Common/src/System/NotImplemented.cs index ffbec64447..fa0fe07fa7 100644 --- a/external/corefx/src/Common/src/System/NotImplemented.cs +++ b/external/corefx/src/Common/src/System/NotImplemented.cs @@ -26,6 +26,6 @@ namespace System /// Temporary NotImplementedException with no message shown to user. /// Example: Exception.ActiveIssue("https://github.com/dotnet/corefx/issues/xxxx") or Exception.ActiveIssue("TFS xxxxxx"). /// - internal static Exception ActiveIssue(string issue) => new NotImplementedException(); + internal static Exception ActiveIssue(string issue) => new NotImplementedException(); } } diff --git a/external/corefx/src/Common/src/System/Runtime/InteropServices/FunctionWrapper.cs b/external/corefx/src/Common/src/System/Runtime/InteropServices/FunctionWrapper.cs index c2723952a4..692e6f64b9 100644 --- a/external/corefx/src/Common/src/System/Runtime/InteropServices/FunctionWrapper.cs +++ b/external/corefx/src/Common/src/System/Runtime/InteropServices/FunctionWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. @@ -47,7 +47,7 @@ namespace System.Runtime.InteropServices public enum FunctionLoadResultKind { Success, LibraryNotFound, FunctionNotFound } - public struct FunctionLoadResult + public readonly struct FunctionLoadResult { public FunctionLoadResultKind ResultKind { get; } public T Delegate { get; } diff --git a/external/corefx/src/Common/src/System/Security/Cryptography/Asn1V2.Serializer.cs b/external/corefx/src/Common/src/System/Security/Cryptography/Asn1V2.Serializer.cs new file mode 100644 index 0000000000..0eeb6fe912 --- /dev/null +++ b/external/corefx/src/Common/src/System/Security/Cryptography/Asn1V2.Serializer.cs @@ -0,0 +1,1604 @@ +// 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.Buffers; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Numerics; +using System.Reflection; + +namespace System.Security.Cryptography.Asn1 +{ + internal class AsnSerializationConstraintException : CryptographicException + { + public AsnSerializationConstraintException() + { + } + + public AsnSerializationConstraintException(string message) + : base(message) + { + } + + public AsnSerializationConstraintException(string message, Exception inner) + : base(message, inner) + { + } + } + + internal class AsnAmbiguousFieldTypeException : AsnSerializationConstraintException + { + public AsnAmbiguousFieldTypeException(FieldInfo fieldInfo, Type ambiguousType) + : base(SR.Format(SR.Cryptography_AsnSerializer_AmbiguousFieldType, fieldInfo.Name, fieldInfo.DeclaringType.FullName, ambiguousType.Namespace)) + { + } + } + + internal class AsnSerializerInvalidDefaultException : AsnSerializationConstraintException + { + internal AsnSerializerInvalidDefaultException() + { + } + + internal AsnSerializerInvalidDefaultException(Exception innerException) + : base(string.Empty, innerException) + { + } + } + + internal static class AsnSerializer + { + private const BindingFlags FieldFlags = + BindingFlags.Public | + BindingFlags.NonPublic | + BindingFlags.Instance; + + private delegate void Serializer(object value, AsnWriter writer); + private delegate object Deserializer(AsnReader reader); + private delegate bool TryDeserializer(AsnReader reader, out T value); + + private static Deserializer TryOrFail(TryDeserializer tryDeserializer) + { + return reader => + { + if (tryDeserializer(reader, out T value)) + return value; + + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + }; + } + + private static ChoiceAttribute GetChoiceAttribute(Type typeT) + { + ChoiceAttribute attr = typeT.GetCustomAttribute(inherit: false); + + if (attr == null) + { + return null; + } + + if (attr.AllowNull) + { + if (!CanBeNull(typeT)) + { + throw new AsnSerializationConstraintException( + SR.Format(SR.Cryptography_AsnSerializer_Choice_AllowNullNonNullable, typeT.FullName)); + } + } + + return attr; + } + + private static bool CanBeNull(Type t) + { + return !t.IsValueType || + (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)); + } + + private static void PopulateChoiceLookup( + Dictionary<(TagClass, int), LinkedList> lookup, + Type typeT, + LinkedList currentSet) + { + FieldInfo[] fieldInfos = typeT.GetFields(FieldFlags); + + // https://github.com/dotnet/corefx/issues/14606 asserts that ordering by the metadata + // token on a SequentialLayout will produce the fields in their layout order. + // + // Some other alternatives: + // * Add an attribute for controlling the field read order. + // fieldInfos.Select(fi => (fi, fi.GetCustomAttribute(false)). + // Where(val => val.Item2 != null).OrderBy(val => val.Item2.OrderWeight).Select(val => val.Item1); + // + // * Use Marshal.OffsetOf as a sort key + // + // * Invent more alternatives + foreach (FieldInfo fieldInfo in fieldInfos.OrderBy(fi => fi.MetadataToken)) + { + Type fieldType = fieldInfo.FieldType; + + if (!CanBeNull(fieldType)) + { + throw new AsnSerializationConstraintException( + SR.Format( + SR.Cryptography_AsnSerializer_Choice_NonNullableField, + fieldInfo.Name, + fieldInfo.DeclaringType.FullName)); + } + + fieldType = UnpackIfNullable(fieldType); + + if (currentSet.Contains(fieldInfo)) + { + throw new AsnSerializationConstraintException( + SR.Format( + SR.Cryptography_AsnSerializer_Choice_TypeCycle, + fieldInfo.Name, + fieldInfo.DeclaringType.FullName)); + } + + LinkedListNode newNode = new LinkedListNode(fieldInfo); + currentSet.AddLast(newNode); + + if (GetChoiceAttribute(fieldType) != null) + { + PopulateChoiceLookup(lookup, fieldType, currentSet); + } + else + { + GetFieldInfo( + fieldType, + fieldInfo, + out SerializerFieldData fieldData); + + if (fieldData.DefaultContents != null) + { + // ITU-T-REC-X.680-201508 + // The application of the DEFAULT keyword is in SEQUENCE (sec 25, "ComponentType" grammar) + // There is no such keyword in the grammar for CHOICE. + throw new AsnSerializationConstraintException( + SR.Format( + SR.Cryptography_AsnSerializer_Choice_DefaultValueDisallowed, + fieldInfo.Name, + fieldInfo.DeclaringType.FullName)); + } + + var key = (fieldData.ExpectedTag.TagClass, fieldData.ExpectedTag.TagValue); + + if (lookup.TryGetValue(key, out LinkedList existingSet)) + { + FieldInfo existing = existingSet.Last.Value; + + throw new AsnSerializationConstraintException( + SR.Format( + SR.Cryptography_AsnSerializer_Choice_ConflictingTagMapping, + fieldData.ExpectedTag.TagClass, + fieldData.ExpectedTag.TagValue, + fieldInfo.Name, + fieldInfo.DeclaringType.FullName, + existing.Name, + existing.DeclaringType.FullName)); + } + + lookup.Add(key, new LinkedList(currentSet)); + } + + currentSet.RemoveLast(); + } + } + + private static void SerializeChoice(Type typeT, object value, AsnWriter writer) + { + // Ensure that the type is consistent with a Choice by using the same logic + // as the deserializer. + var lookup = new Dictionary<(TagClass, int), LinkedList>(); + LinkedList fields = new LinkedList(); + PopulateChoiceLookup(lookup, typeT, fields); + + FieldInfo relevantField = null; + object relevantObject = null; + + // If the value is itself null and the choice allows it, write null. + if (value == null) + { + if (GetChoiceAttribute(typeT).AllowNull) + { + writer.WriteNull(); + return; + } + } + else + { + FieldInfo[] fieldInfos = typeT.GetFields(FieldFlags); + + for (int i = 0; i < fieldInfos.Length; i++) + { + FieldInfo fieldInfo = fieldInfos[i]; + object fieldValue = fieldInfo.GetValue(value); + + if (fieldValue != null) + { + if (relevantField != null) + { + throw new AsnSerializationConstraintException( + SR.Format( + SR.Cryptography_AsnSerializer_Choice_TooManyValues, + fieldInfo.Name, + relevantField.Name, + typeT.FullName)); + } + + relevantField = fieldInfo; + relevantObject = fieldValue; + } + } + } + + // If the element in the SEQUENCE (class/struct) is non-null it must resolve to a value. + // If the SEQUENCE field was OPTIONAL then the element should have been null. + if (relevantField == null) + { + throw new AsnSerializationConstraintException( + SR.Format( + SR.Cryptography_AsnSerializer_Choice_NoChoiceWasMade, + typeT.FullName)); + } + + Serializer serializer = GetSerializer(relevantField.FieldType, relevantField); + serializer(relevantObject, writer); + } + + private static object DeserializeChoice(AsnReader reader, Type typeT) + { + var lookup = new Dictionary<(TagClass, int), LinkedList>(); + LinkedList fields = new LinkedList(); + PopulateChoiceLookup(lookup, typeT, fields); + + Asn1Tag next = reader.PeekTag(); + + if (next == Asn1Tag.Null) + { + ChoiceAttribute choiceAttr = GetChoiceAttribute(typeT); + + if (choiceAttr.AllowNull) + { + reader.ReadNull(); + return null; + } + + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + var key = (next.TagClass, next.TagValue); + + if (lookup.TryGetValue(key, out LinkedList fieldInfos)) + { + LinkedListNode currentNode = fieldInfos.Last; + FieldInfo currentField = currentNode.Value; + object currentObject = Activator.CreateInstance(currentField.DeclaringType); + Deserializer deserializer = GetDeserializer(currentField.FieldType, currentField); + object deserialized = deserializer(reader); + currentField.SetValue(currentObject, deserialized); + + while (currentNode.Previous != null) + { + currentNode = currentNode.Previous; + currentField = currentNode.Value; + + object nextObject = Activator.CreateInstance(currentField.DeclaringType); + currentField.SetValue(nextObject, currentObject); + + currentObject = nextObject; + } + + return currentObject; + } + + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + private static void SerializeCustomType(Type typeT, object value, AsnWriter writer, Asn1Tag tag) + { + writer.PushSequence(tag); + + foreach (FieldInfo fieldInfo in typeT.GetFields(FieldFlags)) + { + Serializer serializer = GetSerializer(fieldInfo.FieldType, fieldInfo); + serializer(fieldInfo.GetValue(value), writer); + } + + writer.PopSequence(tag); + } + + private static object DeserializeCustomType(AsnReader reader, Type typeT) + { + object target = Activator.CreateInstance(typeT); + + AsnReader sequence = reader.ReadSequence(); + + foreach (FieldInfo fieldInfo in typeT.GetFields(FieldFlags)) + { + Deserializer deserializer = GetDeserializer(fieldInfo.FieldType, fieldInfo); + try + { + fieldInfo.SetValue(target, deserializer(sequence)); + } + catch (Exception e) + { + throw new CryptographicException( + SR.Format( + SR.Cryptography_AsnSerializer_SetValueException, + fieldInfo.Name, + fieldInfo.DeclaringType.FullName), + e); + } + } + + sequence.ThrowIfNotEmpty(); + return target; + } + + private static Deserializer ExplicitValueDeserializer(Deserializer valueDeserializer, Asn1Tag expectedTag) + { + return reader => ExplicitValueDeserializer(reader, valueDeserializer, expectedTag); + } + + private static object ExplicitValueDeserializer( + AsnReader reader, + Deserializer valueDeserializer, + Asn1Tag expectedTag) + { + AsnReader innerReader = reader.ReadSequence(expectedTag); + object val = valueDeserializer(innerReader); + + innerReader.ThrowIfNotEmpty(); + return val; + } + + private static Deserializer DefaultValueDeserializer( + Deserializer valueDeserializer, + bool isOptional, + byte[] defaultContents, + Asn1Tag? expectedTag) + { + return reader => + DefaultValueDeserializer( + reader, + expectedTag, + valueDeserializer, + defaultContents, + isOptional); + } + + private static object DefaultValueDeserializer( + AsnReader reader, + Asn1Tag? expectedTag, + Deserializer valueDeserializer, + byte[] defaultContents, + bool isOptional) + { + if (reader.HasData) + { + Asn1Tag actualTag = reader.PeekTag(); + + if (expectedTag == null || + // Normalize the constructed bit so only class and value are compared + actualTag.AsPrimitive() == expectedTag.Value.AsPrimitive()) + { + return valueDeserializer(reader); + } + } + + if (isOptional) + { + return null; + } + + if (defaultContents != null) + { + return DefaultValue(defaultContents, valueDeserializer); + } + + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + private static Serializer GetSerializer(Type typeT, FieldInfo fieldInfo) + { + Serializer literalValueSerializer = GetSimpleSerializer( + typeT, + fieldInfo, + out byte[] defaultContents, + out bool isOptional, + out Asn1Tag? explicitTag); + + Serializer serializer = literalValueSerializer; + + if (isOptional) + { + serializer = (obj, writer) => + { + if (obj != null) + literalValueSerializer(obj, writer); + }; + } + else if (defaultContents != null) + { + serializer = (obj, writer) => + { + AsnReader reader; + + using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) + { + literalValueSerializer(obj, tmp); + reader = new AsnReader(tmp.Encode(), AsnEncodingRules.DER); + } + + ReadOnlySpan encoded = reader.GetEncodedValue().Span; + bool equal = false; + + if (encoded.Length == defaultContents.Length) + { + equal = true; + + for (int i = 0; i < encoded.Length; i++) + { + if (encoded[i] != defaultContents[i]) + { + equal = false; + break; + } + } + } + + if (!equal) + { + literalValueSerializer(obj, writer); + } + }; + } + + if (explicitTag.HasValue) + { + return (obj, writer) => + { + using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) + { + serializer(obj, tmp); + + if (tmp.Encode().Length > 0) + { + writer.PushSequence(explicitTag.Value); + serializer(obj, writer); + writer.PopSequence(explicitTag.Value); + } + } + }; + } + + return serializer; + } + + private static Serializer GetSimpleSerializer( + Type typeT, + FieldInfo fieldInfo, + out byte[] defaultContents, + out bool isOptional, + out Asn1Tag? explicitTag) + { + if (!typeT.IsSealed || typeT.ContainsGenericParameters) + { + throw new AsnSerializationConstraintException( + SR.Format(SR.Cryptography_AsnSerializer_NoOpenTypes, typeT.FullName)); + } + + GetFieldInfo( + typeT, + fieldInfo, + out SerializerFieldData fieldData); + + defaultContents = fieldData.DefaultContents; + isOptional = fieldData.IsOptional; + + typeT = UnpackIfNullable(typeT); + bool isChoice = GetChoiceAttribute(typeT) != null; + + Asn1Tag tag; + + if (fieldData.HasExplicitTag) + { + explicitTag = fieldData.ExpectedTag; + tag = new Asn1Tag(fieldData.TagType.GetValueOrDefault()); + } + else + { + explicitTag = null; + tag = fieldData.ExpectedTag; + } + + // EXPLICIT Any can result in a TagType of null, as can CHOICE types. + // In either of those cases we'll get a tag of EoC, but otherwise the tag should be valid. + Debug.Assert( + tag != Asn1Tag.EndOfContents || fieldData.IsAny || isChoice, + $"Unknown state for typeT={typeT.FullName} on field {fieldInfo?.Name} of type {fieldInfo?.DeclaringType.FullName}"); + + if (typeT.IsPrimitive) + { + // The AsnTypeAttribute resolved in GetFieldInfo currently doesn't allow + // type modification for any of the primitive types. If that changes, we + // need to either pass it through here or do some other form of rerouting. + Debug.Assert(!fieldData.WasCustomized); + return GetPrimitiveSerializer(typeT, tag); + } + + if (typeT.IsEnum) + { + if (typeT.GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0) + { + return (value, writer) => writer.WriteNamedBitList(tag, value); + } + + return (value, writer) => writer.WriteEnumeratedValue(tag, value); + } + + if (typeT == typeof(string)) + { + if (fieldData.TagType == UniversalTagNumber.ObjectIdentifier) + { + return (value, writer) => writer.WriteObjectIdentifier(tag, (string)value); + } + + // Because all string types require an attribute saying their type, we'll + // definitely have a value. + return (value, writer) => writer.WriteCharacterString(tag, fieldData.TagType.Value, (string)value); + } + + if (typeT == typeof(ReadOnlyMemory) && !fieldData.IsCollection) + { + if (fieldData.IsAny) + { + // If a tag was specified via [ExpectedTag] (other than because it's wrapped in an EXPLICIT) + // then don't let the serializer write a violation of that expectation. + if (fieldData.SpecifiedTag && !fieldData.HasExplicitTag) + { + return (value, writer) => + { + ReadOnlyMemory data = (ReadOnlyMemory)value; + + if (!Asn1Tag.TryParse(data.Span, out Asn1Tag actualTag, out _) || + actualTag.AsPrimitive() != fieldData.ExpectedTag.AsPrimitive()) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + writer.WriteEncodedValue(data); + }; + } + + return (value, writer) => writer.WriteEncodedValue((ReadOnlyMemory)value); + } + + if (fieldData.TagType == UniversalTagNumber.BitString) + { + return (value, writer) => writer.WriteBitString(tag, ((ReadOnlyMemory)value).Span); + } + + if (fieldData.TagType == UniversalTagNumber.OctetString) + { + return (value, writer) => writer.WriteOctetString(tag, ((ReadOnlyMemory)value).Span); + } + + if (fieldData.TagType == UniversalTagNumber.Integer) + { + return (value, writer) => + { + // TODO: split netstandard/netcoreapp for span usage? + ReadOnlyMemory valAsMemory = (ReadOnlyMemory)value; + byte[] tooBig = new byte[valAsMemory.Length + 1]; + valAsMemory.Span.CopyTo(tooBig.AsSpan().Slice(1)); + Array.Reverse(tooBig); + BigInteger bigInt = new BigInteger(tooBig); + writer.WriteInteger(bigInt); + }; + } + + Debug.Fail($"No ReadOnlyMemory handler for {fieldData.TagType}"); + throw new CryptographicException(); + } + + if (typeT == typeof(Oid)) + { + return (value, writer) => writer.WriteObjectIdentifier(fieldData.ExpectedTag, (Oid)value); + } + + if (typeT.IsArray) + { + if (typeT.GetArrayRank() != 1) + { + throw new AsnSerializationConstraintException( + SR.Format(SR.Cryptography_AsnSerializer_NoMultiDimensionalArrays, typeT.FullName)); + } + + Type baseType = typeT.GetElementType(); + + if (baseType.IsArray) + { + throw new AsnSerializationConstraintException( + SR.Format(SR.Cryptography_AsnSerializer_NoJaggedArrays, typeT.FullName)); + } + + Serializer serializer = GetSerializer(baseType, null); + + if (fieldData.TagType == UniversalTagNumber.SetOf) + { + return (value, writer) => + { + writer.PushSetOf(tag); + + foreach (var item in (Array)value) + { + serializer(item, writer); + } + + writer.PopSetOf(tag); + }; + } + + Debug.Assert(fieldData.TagType == 0 || fieldData.TagType == UniversalTagNumber.SequenceOf); + return (value, writer) => + { + writer.PushSequence(tag); + + foreach (var item in (Array)value) + { + serializer(item, writer); + } + + writer.PopSequence(tag); + }; + } + + if (typeT == typeof(DateTimeOffset)) + { + if (fieldData.TagType == UniversalTagNumber.UtcTime) + { + return (value, writer) => writer.WriteUtcTime(tag, (DateTimeOffset)value); + } + + if (fieldData.TagType == UniversalTagNumber.GeneralizedTime) + { + Debug.Assert(fieldData.DisallowGeneralizedTimeFractions != null); + + return (value, writer) => + writer.WriteGeneralizedTime(tag, (DateTimeOffset)value, fieldData.DisallowGeneralizedTimeFractions.Value); + } + + Debug.Fail($"No DateTimeOffset handler for {fieldData.TagType}"); + throw new CryptographicException(); + } + + if (typeT == typeof(BigInteger)) + { + return (value, writer) => writer.WriteInteger(tag, (BigInteger)value); + } + + if (typeT.IsLayoutSequential) + { + if (isChoice) + { + return (value, writer) => SerializeChoice(typeT, value, writer); + } + + if (fieldData.TagType == UniversalTagNumber.Sequence) + { + return (value, writer) => SerializeCustomType(typeT, value, writer, tag); + } + } + + throw new AsnSerializationConstraintException( + SR.Format(SR.Cryptography_AsnSerializer_UnhandledType, typeT.FullName)); + } + + private static Deserializer GetDeserializer(Type typeT, FieldInfo fieldInfo) + { + Deserializer literalValueDeserializer = GetSimpleDeserializer( + typeT, + fieldInfo, + out SerializerFieldData fieldData); + + Deserializer deserializer = literalValueDeserializer; + + if (fieldData.HasExplicitTag) + { + deserializer = ExplicitValueDeserializer(deserializer, fieldData.ExpectedTag); + } + + if (fieldData.IsOptional || fieldData.DefaultContents != null) + { + Asn1Tag? expectedTag = null; + + if (fieldData.SpecifiedTag || fieldData.TagType != null) + { + expectedTag = fieldData.ExpectedTag; + } + + deserializer = DefaultValueDeserializer( + deserializer, + fieldData.IsOptional, + fieldData.DefaultContents, + expectedTag); + } + + return deserializer; + } + + private static Deserializer GetSimpleDeserializer( + Type typeT, + FieldInfo fieldInfo, + out SerializerFieldData fieldData) + { + if (!typeT.IsSealed || typeT.ContainsGenericParameters) + { + throw new AsnSerializationConstraintException( + SR.Format(SR.Cryptography_AsnSerializer_NoOpenTypes, typeT.FullName)); + } + + GetFieldInfo(typeT, fieldInfo, out fieldData); + + SerializerFieldData localFieldData = fieldData; + typeT = UnpackIfNullable(typeT); + + if (fieldData.IsAny) + { + if (typeT == typeof(ReadOnlyMemory)) + { + Asn1Tag matchTag = fieldData.ExpectedTag; + + // EXPLICIT Any can't be validated, just return the value. + if (fieldData.HasExplicitTag || !fieldData.SpecifiedTag) + { + return reader => reader.GetEncodedValue(); + } + + // If it's not declared EXPLICIT but an [ExpectedTag] was provided, + // use it as a validation/filter. + return reader => + { + Asn1Tag nextTag = reader.PeekTag(); + + if (matchTag.TagClass != nextTag.TagClass || + matchTag.TagValue != nextTag.TagValue) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + return reader.GetEncodedValue(); + }; + } + + throw new AsnSerializationConstraintException( + SR.Format(SR.Cryptography_AsnSerializer_UnhandledType, typeT.FullName)); + } + + if (GetChoiceAttribute(typeT) != null) + { + return reader => DeserializeChoice(reader, typeT); + } + + Debug.Assert(fieldData.TagType != null); + + Asn1Tag expectedTag = fieldData.HasExplicitTag ? new Asn1Tag(fieldData.TagType.Value) : fieldData.ExpectedTag; + + if (typeT.IsPrimitive) + { + // The AsnTypeAttribute resolved in GetFieldInfo currently doesn't allow + // type modification for any of the primitive types. If that changes, we + // need to either pass it through here or do some other form of rerouting. + Debug.Assert(!fieldData.WasCustomized); + + return GetPrimitiveDeserializer(typeT, expectedTag); + } + + if (typeT.IsEnum) + { + if (typeT.GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0) + { + return reader => reader.GetNamedBitListValue(expectedTag, typeT); + } + + return reader => reader.GetEnumeratedValue(expectedTag, typeT); + } + + if (typeT == typeof(string)) + { + if (fieldData.TagType == UniversalTagNumber.ObjectIdentifier) + { + return reader => reader.ReadObjectIdentifierAsString(expectedTag); + } + + // Because all string types require an attribute saying their type, we'll + // definitely have a value. + Debug.Assert(localFieldData.TagType != null); + return reader => reader.GetCharacterString(expectedTag, localFieldData.TagType.Value); + } + + if (typeT == typeof(ReadOnlyMemory) && !fieldData.IsCollection) + { + if (fieldData.TagType == UniversalTagNumber.BitString) + { + return reader => + { + if (reader.TryGetPrimitiveBitStringValue(expectedTag, out _, out ReadOnlyMemory contents)) + { + return contents; + } + + // Guaranteed too big, because it has the tag and length. + int length = reader.PeekEncodedValue().Length; + byte[] rented = ArrayPool.Shared.Rent(length); + + try + { + if (reader.TryCopyBitStringBytes(rented, out _, out int bytesWritten)) + { + return new ReadOnlyMemory(rented.AsReadOnlySpan().Slice(0, bytesWritten).ToArray()); + } + + Debug.Fail("TryCopyBitStringBytes produced more data than the encoded size"); + throw new CryptographicException(); + } + finally + { + Array.Clear(rented, 0, length); + ArrayPool.Shared.Return(rented); + } + }; + } + + if (fieldData.TagType == UniversalTagNumber.OctetString) + { + return reader => + { + if (reader.TryGetPrimitiveOctetStringBytes(expectedTag, out ReadOnlyMemory contents)) + { + return contents; + } + + // Guaranteed too big, because it has the tag and length. + int length = reader.PeekEncodedValue().Length; + byte[] rented = ArrayPool.Shared.Rent(length); + + try + { + if (reader.TryCopyOctetStringBytes(rented, out int bytesWritten)) + { + return new ReadOnlyMemory(rented.AsReadOnlySpan().Slice(0, bytesWritten).ToArray()); + } + + Debug.Fail("TryCopyOctetStringBytes produced more data than the encoded size"); + throw new CryptographicException(); + } + finally + { + Array.Clear(rented, 0, length); + ArrayPool.Shared.Return(rented); + } + }; + } + + if (fieldData.TagType == UniversalTagNumber.Integer) + { + return reader => reader.GetIntegerBytes(expectedTag); + } + + Debug.Fail($"No ReadOnlyMemory handler for {fieldData.TagType}"); + throw new CryptographicException(); + } + + if (typeT == typeof(Oid)) + { + bool skipFriendlyName = !fieldData.PopulateOidFriendlyName.GetValueOrDefault(); + return reader => reader.ReadObjectIdentifier(expectedTag, skipFriendlyName); + } + + if (typeT.IsArray) + { + if (typeT.GetArrayRank() != 1) + { + throw new AsnSerializationConstraintException( + SR.Format(SR.Cryptography_AsnSerializer_NoMultiDimensionalArrays, typeT.FullName)); + } + + Type baseType = typeT.GetElementType(); + + if (baseType.IsArray) + { + throw new AsnSerializationConstraintException( + SR.Format(SR.Cryptography_AsnSerializer_NoJaggedArrays, typeT.FullName)); + } + + return reader => + { + LinkedList linkedList = new LinkedList(); + + AsnReader collectionReader; + + if (localFieldData.TagType == UniversalTagNumber.SetOf) + { + collectionReader = reader.ReadSetOf(expectedTag); + } + else + { + Debug.Assert(localFieldData.TagType == 0 || localFieldData.TagType == UniversalTagNumber.SequenceOf); + collectionReader = reader.ReadSequence(expectedTag); + } + + Deserializer deserializer = GetDeserializer(baseType, null); + + while (collectionReader.HasData) + { + object elem = deserializer(collectionReader); + LinkedListNode node = new LinkedListNode(elem); + linkedList.AddLast(node); + } + + object[] objArr = linkedList.ToArray(); + Array arr = Array.CreateInstance(baseType, objArr.Length); + Array.Copy(objArr, arr, objArr.Length); + return arr; + }; + } + + if (typeT == typeof(DateTimeOffset)) + { + if (fieldData.TagType == UniversalTagNumber.UtcTime) + { + if (fieldData.TwoDigitYearMax != null) + { + return reader => + reader.GetUtcTime(expectedTag, localFieldData.TwoDigitYearMax.Value); + } + + return reader => reader.GetUtcTime(expectedTag); + } + + if (fieldData.TagType == UniversalTagNumber.GeneralizedTime) + { + Debug.Assert(fieldData.DisallowGeneralizedTimeFractions != null); + bool disallowFractions = fieldData.DisallowGeneralizedTimeFractions.Value; + + return reader => reader.GetGeneralizedTime(expectedTag, disallowFractions); + } + + Debug.Fail($"No DateTimeOffset handler for {fieldData.TagType}"); + throw new CryptographicException(); + } + + if (typeT == typeof(BigInteger)) + { + return reader => reader.GetInteger(expectedTag); + } + + if (typeT.IsLayoutSequential) + { + if (fieldData.TagType == UniversalTagNumber.Sequence) + { + return reader => DeserializeCustomType(reader, typeT); + } + } + + throw new AsnSerializationConstraintException( + SR.Format(SR.Cryptography_AsnSerializer_UnhandledType, typeT.FullName)); + } + + private static object DefaultValue( + byte[] defaultContents, + Deserializer valueDeserializer) + { + Debug.Assert(defaultContents != null); + + try + { + AsnReader defaultValueReader = new AsnReader(defaultContents, AsnEncodingRules.DER); + + object obj = valueDeserializer(defaultValueReader); + + if (defaultValueReader.HasData) + { + throw new AsnSerializerInvalidDefaultException(); + } + + return obj; + } + catch (AsnSerializerInvalidDefaultException) + { + throw; + } + catch (CryptographicException e) + { + throw new AsnSerializerInvalidDefaultException(e); + } + } + + private static void GetFieldInfo( + Type typeT, + FieldInfo fieldInfo, + out SerializerFieldData serializerFieldData) + { + serializerFieldData = new SerializerFieldData(); + + object[] typeAttrs = fieldInfo?.GetCustomAttributes(typeof(AsnTypeAttribute), false) ?? + Array.Empty(); + + if (typeAttrs.Length > 1) + { + throw new AsnSerializationConstraintException( + SR.Format( + fieldInfo.Name, + fieldInfo.DeclaringType.FullName, + typeof(AsnTypeAttribute).FullName)); + } + + Type unpackedType = UnpackIfNullable(typeT); + + if (typeAttrs.Length == 1) + { + Type[] expectedTypes; + object attr = typeAttrs[0]; + serializerFieldData.WasCustomized = true; + + if (attr is AnyValueAttribute) + { + serializerFieldData.IsAny = true; + expectedTypes = new[] { typeof(ReadOnlyMemory) }; + } + else if (attr is IntegerAttribute) + { + expectedTypes = new[] { typeof(ReadOnlyMemory) }; + serializerFieldData.TagType = UniversalTagNumber.Integer; + } + else if (attr is BitStringAttribute) + { + expectedTypes = new[] { typeof(ReadOnlyMemory) }; + serializerFieldData.TagType = UniversalTagNumber.BitString; + } + else if (attr is OctetStringAttribute) + { + expectedTypes = new[] { typeof(ReadOnlyMemory) }; + serializerFieldData.TagType = UniversalTagNumber.OctetString; + } + else if (attr is ObjectIdentifierAttribute oid) + { + serializerFieldData.PopulateOidFriendlyName = oid.PopulateFriendlyName; + expectedTypes = new[] { typeof(Oid), typeof(string) }; + serializerFieldData.TagType = UniversalTagNumber.ObjectIdentifier; + + if (oid.PopulateFriendlyName && unpackedType == typeof(string)) + { + throw new AsnSerializationConstraintException( + SR.Format( + SR.Cryptography_AsnSerializer_PopulateFriendlyNameOnString, + fieldInfo.Name, + fieldInfo.DeclaringType.FullName, + typeof(Oid).FullName)); + } + } + else if (attr is BMPStringAttribute) + { + expectedTypes = new[] { typeof(string) }; + serializerFieldData.TagType = UniversalTagNumber.BMPString; + } + else if (attr is IA5StringAttribute) + { + expectedTypes = new[] { typeof(string) }; + serializerFieldData.TagType = UniversalTagNumber.IA5String; + } + else if (attr is UTF8StringAttribute) + { + expectedTypes = new[] { typeof(string) }; + serializerFieldData.TagType = UniversalTagNumber.UTF8String; + } + else if (attr is PrintableStringAttribute) + { + expectedTypes = new[] { typeof(string) }; + serializerFieldData.TagType = UniversalTagNumber.PrintableString; + } + else if (attr is VisibleStringAttribute) + { + expectedTypes = new[] { typeof(string) }; + serializerFieldData.TagType = UniversalTagNumber.VisibleString; + } + else if (attr is SequenceOfAttribute) + { + serializerFieldData.IsCollection = true; + expectedTypes = null; + serializerFieldData.TagType = UniversalTagNumber.SequenceOf; + } + else if (attr is SetOfAttribute) + { + serializerFieldData.IsCollection = true; + expectedTypes = null; + serializerFieldData.TagType = UniversalTagNumber.SetOf; + } + else if (attr is UtcTimeAttribute utcAttr) + { + expectedTypes = new[] { typeof(DateTimeOffset) }; + serializerFieldData.TagType = UniversalTagNumber.UtcTime; + + if (utcAttr.TwoDigitYearMax != 0) + { + serializerFieldData.TwoDigitYearMax = utcAttr.TwoDigitYearMax; + + if (serializerFieldData.TwoDigitYearMax < 99) + { + throw new AsnSerializationConstraintException( + SR.Format( + SR.Cryptography_AsnSerializer_UtcTimeTwoDigitYearMaxTooSmall, + fieldInfo.Name, + fieldInfo.DeclaringType.FullName, + serializerFieldData.TwoDigitYearMax)); + } + } + } + else if (attr is GeneralizedTimeAttribute genTimeAttr) + { + expectedTypes = new[] { typeof(DateTimeOffset) }; + serializerFieldData.TagType = UniversalTagNumber.GeneralizedTime; + serializerFieldData.DisallowGeneralizedTimeFractions = genTimeAttr.DisallowFractions; + } + else + { + Debug.Fail($"Unregistered {nameof(AsnTypeAttribute)} kind: {attr.GetType().FullName}"); + throw new CryptographicException(); + } + + Debug.Assert(serializerFieldData.IsCollection || expectedTypes != null); + + if (!serializerFieldData.IsCollection && Array.IndexOf(expectedTypes, unpackedType) < 0) + { + throw new AsnSerializationConstraintException( + SR.Format( + SR.Cryptography_AsnSerializer_UnexpectedTypeForAttribute, + fieldInfo.Name, + fieldInfo.DeclaringType.Namespace, + unpackedType.FullName, + string.Join(", ", expectedTypes.Select(t => t.FullName)))); + } + } + + var defaultValueAttr = fieldInfo?.GetCustomAttribute(false); + serializerFieldData.DefaultContents = defaultValueAttr?.EncodedBytes; + + if (serializerFieldData.TagType == null && !serializerFieldData.IsAny) + { + if (unpackedType == typeof(bool)) + { + serializerFieldData.TagType = UniversalTagNumber.Boolean; + } + else if (unpackedType == typeof(sbyte) || + unpackedType == typeof(byte) || + unpackedType == typeof(short) || + unpackedType == typeof(ushort) || + unpackedType == typeof(int) || + unpackedType == typeof(uint) || + unpackedType == typeof(long) || + unpackedType == typeof(ulong) || + unpackedType == typeof(BigInteger)) + { + serializerFieldData.TagType = UniversalTagNumber.Integer; + } + else if (unpackedType.IsLayoutSequential) + { + serializerFieldData.TagType = UniversalTagNumber.Sequence; + } + else if (unpackedType == typeof(ReadOnlyMemory) || + unpackedType == typeof(string) || + unpackedType == typeof(DateTimeOffset)) + { + throw new AsnAmbiguousFieldTypeException(fieldInfo, unpackedType); + } + else if (unpackedType == typeof(Oid)) + { + serializerFieldData.TagType = UniversalTagNumber.ObjectIdentifier; + } + else if (unpackedType.IsArray) + { + serializerFieldData.TagType = UniversalTagNumber.SequenceOf; + } + else if (unpackedType.IsEnum) + { + if (typeT.GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0) + { + serializerFieldData.TagType = UniversalTagNumber.BitString; + } + else + { + serializerFieldData.TagType = UniversalTagNumber.Enumerated; + } + } + else if (fieldInfo != null) + { + Debug.Fail($"No tag type bound for {fieldInfo.DeclaringType.FullName}.{fieldInfo.Name}"); + throw new AsnSerializationConstraintException(); + } + } + + serializerFieldData.IsOptional = fieldInfo?.GetCustomAttribute(false) != null; + + if (serializerFieldData.IsOptional && !CanBeNull(typeT)) + { + throw new AsnSerializationConstraintException( + SR.Format( + SR.Cryptography_AsnSerializer_Optional_NonNullableField, + fieldInfo.Name, + fieldInfo.DeclaringType.FullName)); + } + + bool isChoice = GetChoiceAttribute(typeT) != null; + + var tagOverride = fieldInfo?.GetCustomAttribute(false); + + if (tagOverride != null) + { + if (isChoice && !tagOverride.ExplicitTag) + { + throw new AsnSerializationConstraintException( + SR.Format( + SR.Cryptography_AsnSerializer_SpecificTagChoice, + fieldInfo.Name, + fieldInfo.DeclaringType.FullName, + typeT.FullName)); + } + + // This will throw for unmapped TagClass values + serializerFieldData.ExpectedTag = new Asn1Tag(tagOverride.TagClass, tagOverride.TagValue); + serializerFieldData.HasExplicitTag = tagOverride.ExplicitTag; + serializerFieldData.SpecifiedTag = true; + return; + } + + if (isChoice) + { + serializerFieldData.TagType = null; + } + + serializerFieldData.SpecifiedTag = false; + serializerFieldData.HasExplicitTag = false; + serializerFieldData.ExpectedTag = new Asn1Tag(serializerFieldData.TagType.GetValueOrDefault()); + } + + private static Type UnpackIfNullable(Type typeT) + { + return Nullable.GetUnderlyingType(typeT) ?? typeT; + } + + private static Deserializer GetPrimitiveDeserializer(Type typeT, Asn1Tag tag) + { + if (typeT == typeof(bool)) + return reader => reader.ReadBoolean(tag); + if (typeT == typeof(int)) + return TryOrFail((AsnReader reader, out int value) => reader.TryReadInt32(tag, out value)); + if (typeT == typeof(uint)) + return TryOrFail((AsnReader reader, out uint value) => reader.TryReadUInt32(tag, out value)); + if (typeT == typeof(short)) + return TryOrFail((AsnReader reader, out short value) => reader.TryReadInt16(tag, out value)); + if (typeT == typeof(ushort)) + return TryOrFail((AsnReader reader, out ushort value) => reader.TryReadUInt16(tag, out value)); + if (typeT == typeof(byte)) + return TryOrFail((AsnReader reader, out byte value) => reader.TryReadUInt8(tag, out value)); + if (typeT == typeof(sbyte)) + return TryOrFail((AsnReader reader, out sbyte value) => reader.TryReadInt8(tag, out value)); + if (typeT == typeof(long)) + return TryOrFail((AsnReader reader, out long value) => reader.TryReadInt64(tag, out value)); + if (typeT == typeof(ulong)) + return TryOrFail((AsnReader reader, out ulong value) => reader.TryReadUInt64(tag, out value)); + + throw new AsnSerializationConstraintException( + SR.Format( + SR.Cryptography_AsnSerializer_UnhandledType, + typeT.FullName)); + } + + private static Serializer GetPrimitiveSerializer(Type typeT, Asn1Tag primitiveTag) + { + if (typeT == typeof(bool)) + return (value, writer) => writer.WriteBoolean(primitiveTag, (bool)value); + if (typeT == typeof(int)) + return (value, writer) => writer.WriteInteger(primitiveTag, (int)value); + if (typeT == typeof(uint)) + return (value, writer) => writer.WriteInteger(primitiveTag, (uint)value); + if (typeT == typeof(short)) + return (value, writer) => writer.WriteInteger(primitiveTag, (short)value); + if (typeT == typeof(ushort)) + return (value, writer) => writer.WriteInteger(primitiveTag, (ushort)value); + if (typeT == typeof(byte)) + return (value, writer) => writer.WriteInteger(primitiveTag, (byte)value); + if (typeT == typeof(sbyte)) + return (value, writer) => writer.WriteInteger(primitiveTag, (sbyte)value); + if (typeT == typeof(long)) + return (value, writer) => writer.WriteInteger(primitiveTag, (long)value); + if (typeT == typeof(ulong)) + return (value, writer) => writer.WriteInteger(primitiveTag, (ulong)value); + + throw new AsnSerializationConstraintException( + SR.Format( + SR.Cryptography_AsnSerializer_UnhandledType, + typeT.FullName)); + } + + /// + /// Read ASN.1 data from encoded under the specified encoding rules into + /// the typed structure. + /// + /// + /// The type to deserialize as. + /// In order to be deserialized the type must have sequential layout, be sealed, and be composed of + /// members that are also able to be deserialized by this method. + /// + /// A view of the encoded bytes to be deserialized. + /// The ASN.1 encoding ruleset to use for reading . + /// A deserialized instance of . + /// + /// Except for where required to for avoiding ambiguity, this method does not check that there are + /// no cycles in the type graph for . If is a + /// reference type (class) which includes a cycle in the type graph, + /// then it is possible for the data in to cause + /// an arbitrary extension to the maximum stack depth of this routine, leading to a + /// . + /// + /// If is a value type (struct) the compiler will enforce that there are no + /// cycles in the type graph. + /// + /// When reference types are used the onus is on the caller of this method to prevent cycles, or to + /// mitigate the possibility of the stack overflow. + /// + /// + /// A portion of is invalid for deserialization. + /// + /// + /// Any of the data in is invalid for mapping to the return value, + /// or data remains after deserialization. + /// + public static T Deserialize(ReadOnlyMemory source, AsnEncodingRules ruleSet) + { + Deserializer deserializer = GetDeserializer(typeof(T), null); + + AsnReader reader = new AsnReader(source, ruleSet); + + T t = (T)deserializer(reader); + + reader.ThrowIfNotEmpty(); + return t; + } + + /// + /// Serialize into an ASN.1 writer under the specified encoding rules. + /// + /// + /// The type to serialize as. + /// In order to be serialized the type must have sequential layout, be sealed, and be composed of + /// members that are also able to be serialized by this method. + /// + /// The value to serialize. + /// The ASN.1 encoding ruleset to use for writing . + /// A deserialized instance of . + /// + /// Except for where required to for avoiding ambiguity, this method does not check that there are + /// no cycles in the type graph for . If is a + /// reference type (class) which includes a cycle in the type graph, and there is a cycle within the + /// object graph this method will consume memory and stack space until one is exhausted. + /// + /// If is a value type (struct) the compiler will enforce that there are no + /// cycles in the type graph. + /// + /// When reference types are used the onus is on the caller of this method to prevent object cycles, + /// or to mitigate the possibility of the stack overflow or memory exhaustion. + /// + /// + /// A portion of is invalid for deserialization. + /// + public static AsnWriter Serialize(T value, AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + try + { + Serialize(value, writer); + return writer; + } + catch + { + writer.Dispose(); + throw; + } + } + + /// + /// Serialize into an ASN.1 writer under the specified encoding rules. + /// + /// + /// The type to serialize as. + /// In order to be serialized the type must have sequential layout, be sealed, and be composed of + /// members that are also able to be serialized by this method. + /// + /// The value to serialize. + /// An existing writer into which should be written. + /// A deserialized instance of . + /// + /// Except for where required to for avoiding ambiguity, this method does not check that there are + /// no cycles in the type graph for . If is a + /// reference type (class) which includes a cycle in the type graph, and there is a cycle within the + /// object graph this method will consume memory and stack space until one is exhausted. + /// + /// If is a value type (struct) the compiler will enforce that there are no + /// cycles in the type graph. + /// + /// When reference types are used the onus is on the caller of this method to prevent object cycles, + /// or to mitigate the possibility of the stack overflow or memory exhaustion. + /// + /// + /// A portion of is invalid for deserialization. + /// + public static void Serialize(T value, AsnWriter existingWriter) + { + if (existingWriter == null) + { + throw new ArgumentNullException(nameof(existingWriter)); + } + + Serializer serializer = GetSerializer(typeof(T), null); + serializer(value, existingWriter); + } + + private struct SerializerFieldData + { + internal bool WasCustomized; + internal UniversalTagNumber? TagType; + internal bool? PopulateOidFriendlyName; + internal bool IsAny; + internal bool IsCollection; + internal byte[] DefaultContents; + internal bool HasExplicitTag; + internal bool SpecifiedTag; + internal bool IsOptional; + internal int? TwoDigitYearMax; + internal Asn1Tag ExpectedTag; + internal bool? DisallowGeneralizedTimeFractions; + } + } + + [AttributeUsage(AttributeTargets.Field)] + internal sealed class ExpectedTagAttribute : Attribute + { + public TagClass TagClass { get; } + public int TagValue { get; } + public bool ExplicitTag { get; set; } + + public ExpectedTagAttribute(int tagValue) + : this(TagClass.ContextSpecific, tagValue) + { + } + + public ExpectedTagAttribute(TagClass tagClass, int tagValue) + { + TagClass = tagClass; + TagValue = tagValue; + } + } + + internal abstract class AsnTypeAttribute : Attribute + { + internal AsnTypeAttribute() + { + } + } + + internal abstract class AsnEncodingRuleAttribute : Attribute + { + internal AsnEncodingRuleAttribute() + { + } + } + + [AttributeUsage(AttributeTargets.Field)] + internal sealed class OctetStringAttribute : AsnTypeAttribute + { + } + + [AttributeUsage(AttributeTargets.Field)] + internal sealed class BitStringAttribute : AsnTypeAttribute + { + } + + [AttributeUsage(AttributeTargets.Field)] + internal sealed class AnyValueAttribute : AsnTypeAttribute + { + } + + [AttributeUsage(AttributeTargets.Field)] + internal sealed class ObjectIdentifierAttribute : AsnTypeAttribute + { + public ObjectIdentifierAttribute() + { + } + + public bool PopulateFriendlyName { get; set; } + } + + [AttributeUsage(AttributeTargets.Field)] + internal sealed class BMPStringAttribute : AsnTypeAttribute + { + } + + [AttributeUsage(AttributeTargets.Field)] + internal sealed class IA5StringAttribute : AsnTypeAttribute + { + } + + [AttributeUsage(AttributeTargets.Field)] + internal sealed class UTF8StringAttribute : AsnTypeAttribute + { + } + + [AttributeUsage(AttributeTargets.Field)] + internal sealed class PrintableStringAttribute : AsnTypeAttribute + { + } + + [AttributeUsage(AttributeTargets.Field)] + internal sealed class VisibleStringAttribute : AsnTypeAttribute + { + } + + [AttributeUsage(AttributeTargets.Field)] + internal sealed class SequenceOfAttribute : AsnTypeAttribute + { + } + + [AttributeUsage(AttributeTargets.Field)] + internal sealed class SetOfAttribute : AsnTypeAttribute + { + } + + [AttributeUsage(AttributeTargets.Field)] + internal sealed class IntegerAttribute : AsnTypeAttribute + { + } + + [AttributeUsage(AttributeTargets.Field)] + internal sealed class UtcTimeAttribute : AsnTypeAttribute + { + public int TwoDigitYearMax { get; set; } + } + + [AttributeUsage(AttributeTargets.Field)] + internal sealed class GeneralizedTimeAttribute : AsnTypeAttribute + { + public bool DisallowFractions { get; set; } + } + + [AttributeUsage(AttributeTargets.Field)] + internal sealed class OptionalValueAttribute : AsnEncodingRuleAttribute + { + } + + [AttributeUsage(AttributeTargets.Field)] + internal sealed class DefaultValueAttribute : AsnEncodingRuleAttribute + { + internal byte[] EncodedBytes { get; } + + public DefaultValueAttribute(params byte[] encodedValue) + { + EncodedBytes = encodedValue; + } + + public ReadOnlyMemory EncodedValue => EncodedBytes; + } + + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] + internal sealed class ChoiceAttribute : Attribute + { + public bool AllowNull { get; set; } + } +} diff --git a/external/corefx/src/Common/src/System/Security/Cryptography/Asn1V2.cs b/external/corefx/src/Common/src/System/Security/Cryptography/Asn1V2.cs new file mode 100644 index 0000000000..6594846641 --- /dev/null +++ b/external/corefx/src/Common/src/System/Security/Cryptography/Asn1V2.cs @@ -0,0 +1,751 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace System.Security.Cryptography.Asn1 +{ + // ITU-T-REC.X.680-201508 sec 4. + internal enum AsnEncodingRules + { + BER, + CER, + DER, + } + + // Uses a masked overlay of the tag class encoding. + // T-REC-X.690-201508 sec 8.1.2.2 + internal enum TagClass : byte + { + Universal = 0, + Application = 0b0100_0000, + ContextSpecific = 0b1000_0000, + Private = 0b1100_0000, + } + + // ITU-T-REC.X.680-201508 sec 8.6 + internal enum UniversalTagNumber + { + EndOfContents = 0, + Boolean = 1, + Integer = 2, + BitString = 3, + OctetString = 4, + Null = 5, + ObjectIdentifier = 6, + ObjectDescriptor = 7, + External = 8, + InstanceOf = External, + Real = 9, + Enumerated = 10, + Embedded = 11, + UTF8String = 12, + RelativeObjectIdentifier = 13, + Time = 14, + // 15 is reserved + Sequence = 16, + SequenceOf = Sequence, + Set = 17, + SetOf = Set, + NumericString = 18, + PrintableString = 19, + TeletexString = 20, + T61String = TeletexString, + VideotexString = 21, + IA5String = 22, + UtcTime = 23, + GeneralizedTime = 24, + GraphicString = 25, + VisibleString = 26, + ISO646String = VisibleString, + GeneralString = 27, + UniversalString = 28, + UnrestrictedCharacterString = 29, + BMPString = 30, + Date = 31, + TimeOfDay = 32, + DateTime = 33, + Duration = 34, + ObjectIdentifierIRI = 35, + RelativeObjectIdentifierIRI = 36, + } + + // Represents a BER-family encoded tag. + // T-REC-X.690-201508 sec 8.1.2 + internal struct Asn1Tag : IEquatable + { + private const byte ClassMask = 0b1100_0000; + private const byte ConstructedMask = 0b0010_0000; + private const byte ControlMask = ClassMask | ConstructedMask; + private const byte TagNumberMask = 0b0001_1111; + + internal static readonly Asn1Tag EndOfContents = new Asn1Tag(0, (int)UniversalTagNumber.EndOfContents); + internal static readonly Asn1Tag Boolean = new Asn1Tag(0, (int)UniversalTagNumber.Boolean); + internal static readonly Asn1Tag Integer = new Asn1Tag(0, (int)UniversalTagNumber.Integer); + internal static readonly Asn1Tag PrimitiveBitString = new Asn1Tag(0, (int)UniversalTagNumber.BitString); + internal static readonly Asn1Tag ConstructedBitString = new Asn1Tag(ConstructedMask, (int)UniversalTagNumber.BitString); + internal static readonly Asn1Tag PrimitiveOctetString = new Asn1Tag(0, (int)UniversalTagNumber.OctetString); + internal static readonly Asn1Tag ConstructedOctetString = new Asn1Tag(ConstructedMask, (int)UniversalTagNumber.OctetString); + internal static readonly Asn1Tag Null = new Asn1Tag(0, (int)UniversalTagNumber.Null); + internal static readonly Asn1Tag ObjectIdentifier = new Asn1Tag(0, (int)UniversalTagNumber.ObjectIdentifier); + internal static readonly Asn1Tag Enumerated = new Asn1Tag(0, (int)UniversalTagNumber.Enumerated); + internal static readonly Asn1Tag Sequence = new Asn1Tag(ConstructedMask, (int)UniversalTagNumber.Sequence); + internal static readonly Asn1Tag SetOf = new Asn1Tag(ConstructedMask, (int)UniversalTagNumber.SetOf); + internal static readonly Asn1Tag UtcTime = new Asn1Tag(0, (int)UniversalTagNumber.UtcTime); + internal static readonly Asn1Tag GeneralizedTime = new Asn1Tag(0, (int)UniversalTagNumber.GeneralizedTime); + + private readonly byte _controlFlags; + private readonly int _tagValue; + + public TagClass TagClass => (TagClass)(_controlFlags & ClassMask); + public bool IsConstructed => (_controlFlags & ConstructedMask) != 0; + public int TagValue => _tagValue; + + private Asn1Tag(byte controlFlags, int tagValue) + { + _controlFlags = (byte)(controlFlags & ControlMask); + _tagValue = tagValue; + } + + public Asn1Tag(UniversalTagNumber universalTagNumber, bool isConstructed = false) + : this(isConstructed ? ConstructedMask : (byte)0, (int)universalTagNumber) + { + // T-REC-X.680-201508 sec 8.6 (Table 1) + const UniversalTagNumber ReservedIndex = (UniversalTagNumber)15; + + if (universalTagNumber < UniversalTagNumber.EndOfContents || + universalTagNumber > UniversalTagNumber.RelativeObjectIdentifierIRI || + universalTagNumber == ReservedIndex) + { + throw new ArgumentOutOfRangeException(nameof(universalTagNumber)); + } + } + + public Asn1Tag(TagClass tagClass, int tagValue, bool isConstructed = false) + : this((byte)((byte)tagClass | (isConstructed ? ConstructedMask : 0)), tagValue) + { + if (tagClass < TagClass.Universal || tagClass > TagClass.Private) + { + throw new ArgumentOutOfRangeException(nameof(tagClass)); + } + + if (tagValue < 0) + { + throw new ArgumentOutOfRangeException(nameof(tagValue)); + } + } + + public Asn1Tag AsConstructed() + { + return new Asn1Tag((byte)(_controlFlags | ConstructedMask), _tagValue); + } + + public Asn1Tag AsPrimitive() + { + return new Asn1Tag((byte)(_controlFlags & ~ConstructedMask), _tagValue); + } + + public static bool TryParse(ReadOnlySpan source, out Asn1Tag tag, out int bytesRead) + { + tag = default(Asn1Tag); + bytesRead = 0; + + if (source.IsEmpty) + { + return false; + } + + byte first = source[bytesRead]; + bytesRead++; + uint tagValue = (uint)(first & TagNumberMask); + + if (tagValue == TagNumberMask) + { + // Multi-byte encoding + // T-REC-X.690-201508 sec 8.1.2.4 + const byte ContinuationFlag = 0x80; + const byte ValueMask = ContinuationFlag - 1; + + tagValue = 0; + byte current; + + do + { + if (source.Length <= bytesRead) + { + bytesRead = 0; + return false; + } + + current = source[bytesRead]; + byte currentValue = (byte)(current & ValueMask); + bytesRead++; + + // If TooBigToShift is shifted left 7, the content bit shifts out. + // So any value greater than or equal to this cannot be shifted without loss. + const int TooBigToShift = 0b00000010_00000000_00000000_00000000; + + if (tagValue >= TooBigToShift) + { + bytesRead = 0; + return false; + } + + tagValue <<= 7; + tagValue |= currentValue; + + // The first byte cannot have the value 0 (T-REC-X.690-201508 sec 8.1.2.4.2.c) + if (tagValue == 0) + { + bytesRead = 0; + return false; + } + } + while ((current & ContinuationFlag) == ContinuationFlag); + + // This encoding is only valid for tag values greater than 30. + // (T-REC-X.690-201508 sec 8.1.2.3, 8.1.2.4) + if (tagValue <= 30) + { + bytesRead = 0; + return false; + } + + // There's not really any ambiguity, but prevent negative numbers from showing up. + if (tagValue > int.MaxValue) + { + bytesRead = 0; + return false; + } + } + + Debug.Assert(bytesRead > 0); + tag = new Asn1Tag(first, (int)tagValue); + return true; + } + + public int CalculateEncodedSize() + { + const int SevenBits = 0b0111_1111; + const int FourteenBits = 0b0011_1111_1111_1111; + const int TwentyOneBits = 0b0001_1111_1111_1111_1111_1111; + const int TwentyEightBits = 0b0000_1111_1111_1111_1111_1111_1111_1111; + + if (TagValue < TagNumberMask) + return 1; + if (TagValue <= SevenBits) + return 2; + if (TagValue <= FourteenBits) + return 3; + if (TagValue <= TwentyOneBits) + return 4; + if (TagValue <= TwentyEightBits) + return 5; + + return 6; + } + + public bool TryWrite(Span destination, out int bytesWritten) + { + int spaceRequired = CalculateEncodedSize(); + + if (destination.Length < spaceRequired) + { + bytesWritten = 0; + return false; + } + + if (spaceRequired == 1) + { + byte value = (byte)(_controlFlags | TagValue); + destination[0] = value; + bytesWritten = 1; + return true; + } + + byte firstByte = (byte)(_controlFlags | TagNumberMask); + destination[0] = firstByte; + + int remaining = TagValue; + int idx = spaceRequired - 1; + + while (remaining > 0) + { + int segment = remaining & 0x7F; + + // The last byte doesn't get the marker, which we write first. + if (remaining != TagValue) + { + segment |= 0x80; + } + + Debug.Assert(segment <= byte.MaxValue); + destination[idx] = (byte)segment; + remaining >>= 7; + idx--; + } + + Debug.Assert(idx == 0); + bytesWritten = spaceRequired; + return true; + } + + public bool Equals(Asn1Tag other) + { + return _controlFlags == other._controlFlags && _tagValue == other._tagValue; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + return obj is Asn1Tag && Equals((Asn1Tag)obj); + } + + public override int GetHashCode() + { + // Most TagValue values will be in the 0-30 range, + // the GetHashCode value only has collisions when TagValue is + // between 2^29 and uint.MaxValue + return (_controlFlags << 24) ^ _tagValue; + } + + public static bool operator ==(Asn1Tag left, Asn1Tag right) + { + return left.Equals(right); + } + + public static bool operator !=(Asn1Tag left, Asn1Tag right) + { + return !left.Equals(right); + } + + public override string ToString() + { + const string ConstructedPrefix = "Constructed "; + string classAndValue; + + if (TagClass == TagClass.Universal) + { + classAndValue = ((UniversalTagNumber)TagValue).ToString(); + } + else + { + classAndValue = TagClass + "-" + TagValue; + } + + if (IsConstructed) + { + return ConstructedPrefix + classAndValue; + } + + return classAndValue; + } + } + + internal static class AsnCharacterStringEncodings + { + private static readonly Text.Encoding s_utf8Encoding = new UTF8Encoding(false, throwOnInvalidBytes: true); + private static readonly Text.Encoding s_bmpEncoding = new BMPEncoding(); + private static readonly Text.Encoding s_ia5Encoding = new IA5Encoding(); + private static readonly Text.Encoding s_visibleStringEncoding = new VisibleStringEncoding(); + private static readonly Text.Encoding s_printableStringEncoding = new PrintableStringEncoding(); + + internal static Text.Encoding GetEncoding(UniversalTagNumber encodingType) + { + switch (encodingType) + { + case UniversalTagNumber.UTF8String: + return s_utf8Encoding; + case UniversalTagNumber.PrintableString: + return s_printableStringEncoding; + case UniversalTagNumber.IA5String: + return s_ia5Encoding; + case UniversalTagNumber.VisibleString: + return s_visibleStringEncoding; + case UniversalTagNumber.BMPString: + return s_bmpEncoding; + default: + throw new ArgumentOutOfRangeException(nameof(encodingType), encodingType, null); + } + } + } + + internal abstract class SpanBasedEncoding : Text.Encoding + { + protected SpanBasedEncoding() + : base(0, EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback) + { + } + + protected abstract int GetBytes(ReadOnlySpan chars, Span bytes, bool write); + protected abstract int GetChars(ReadOnlySpan bytes, Span chars, bool write); + + public override int GetByteCount(char[] chars, int index, int count) + { + return GetByteCount(new ReadOnlySpan(chars, index, count)); + } + + public override unsafe int GetByteCount(char* chars, int count) + { + return GetByteCount(new ReadOnlySpan(chars, count)); + } + + public override int GetByteCount(string s) + { + return GetByteCount(s.AsReadOnlySpan()); + } + + public +#if netcoreapp + override +#endif + int GetByteCount(ReadOnlySpan chars) + { + return GetBytes(chars, Span.Empty, write: false); + } + + public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) + { + return GetBytes( + new ReadOnlySpan(chars, charIndex, charCount), + new Span(bytes, byteIndex, bytes.Length - byteIndex), + write: true); + } + + public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount) + { + return GetBytes( + new ReadOnlySpan(chars, charCount), + new Span(bytes, byteCount), + write: true); + } + + public override int GetCharCount(byte[] bytes, int index, int count) + { + return GetCharCount(new ReadOnlySpan(bytes, index, count)); + } + + public override unsafe int GetCharCount(byte* bytes, int count) + { + return GetCharCount(new ReadOnlySpan(bytes, count)); + } + + public +#if netcoreapp + override +#endif + int GetCharCount(ReadOnlySpan bytes) + { + return GetChars(bytes, Span.Empty, write: false); + } + + public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) + { + return GetChars( + new ReadOnlySpan(bytes, byteIndex, byteCount), + new Span(chars, charIndex, chars.Length - charIndex), + write: true); + } + + public override unsafe int GetChars(byte* bytes, int byteCount, char* chars, int charCount) + { + return GetChars( + new ReadOnlySpan(bytes, byteCount), + new Span(chars, charCount), + write: true); + } + } + + internal class IA5Encoding : RestrictedAsciiStringEncoding + { + // T-REC-X.680-201508 sec 41, Table 8. + // ISO International Register of Coded Character Sets to be used with Escape Sequences 001 + // is ASCII 0x00 - 0x1F + // ISO International Register of Coded Character Sets to be used with Escape Sequences 006 + // is ASCII 0x21 - 0x7E + // Space is ASCII 0x20, delete is ASCII 0x7F. + // + // The net result is all of 7-bit ASCII + internal IA5Encoding() + : base(0x00, 0x7F) + { + } + } + + internal class VisibleStringEncoding : RestrictedAsciiStringEncoding + { + // T-REC-X.680-201508 sec 41, Table 8. + // ISO International Register of Coded Character Sets to be used with Escape Sequences 006 + // is ASCII 0x21 - 0x7E + // Space is ASCII 0x20. + internal VisibleStringEncoding() + : base(0x20, 0x7E) + { + } + } + + internal class PrintableStringEncoding : RestrictedAsciiStringEncoding + { + // T-REC-X.680-201508 sec 41.4 + internal PrintableStringEncoding() + : base("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 '()+,-./:=?") + { + } + } + + internal abstract class RestrictedAsciiStringEncoding : SpanBasedEncoding + { + private readonly bool[] _isAllowed; + + protected RestrictedAsciiStringEncoding(byte minCharAllowed, byte maxCharAllowed) + { + Debug.Assert(minCharAllowed <= maxCharAllowed); + Debug.Assert(maxCharAllowed <= 0x7F); + + bool[] isAllowed = new bool[0x80]; + + for (byte charCode = minCharAllowed; charCode <= maxCharAllowed; charCode++) + { + isAllowed[charCode] = true; + } + + _isAllowed = isAllowed; + } + + protected RestrictedAsciiStringEncoding(IEnumerable allowedChars) + { + bool[] isAllowed = new bool[0x7F]; + + foreach (char c in allowedChars) + { + if (c >= isAllowed.Length) + { + throw new ArgumentOutOfRangeException(nameof(allowedChars)); + } + + Debug.Assert(isAllowed[c] == false); + isAllowed[c] = true; + } + + _isAllowed = isAllowed; + } + + public override int GetMaxByteCount(int charCount) + { + return charCount; + } + + public override int GetMaxCharCount(int byteCount) + { + return byteCount; + } + + protected override int GetBytes(ReadOnlySpan chars, Span bytes, bool write) + { + if (chars.IsEmpty) + { + return 0; + } + + for (int i = 0; i < chars.Length; i++) + { + char c = chars[i]; + + if ((uint)c >= (uint)_isAllowed.Length || !_isAllowed[c]) + { + EncoderFallback.CreateFallbackBuffer().Fallback(c, i); + + Debug.Fail("Fallback should have thrown"); + throw new CryptographicException(); + } + + if (write) + { + bytes[i] = (byte)c; + } + } + + return chars.Length; + } + + protected override int GetChars(ReadOnlySpan bytes, Span chars, bool write) + { + if (bytes.IsEmpty) + { + return 0; + } + + for (int i = 0; i < bytes.Length; i++) + { + byte b = bytes[i]; + + if ((uint)b >= (uint)_isAllowed.Length || !_isAllowed[b]) + { + DecoderFallback.CreateFallbackBuffer().Fallback( + new[] { b }, + i); + + Debug.Fail("Fallback should have thrown"); + throw new CryptographicException(); + } + + if (write) + { + chars[i] = (char)b; + } + } + + return bytes.Length; + } + } + + /// + /// Big-Endian UCS-2 encoding (the same as UTF-16BE, but disallowing surrogate pairs to leave plane 0) + /// + // T-REC-X.690-201508 sec 8.23.8 says to see ISO/IEC 10646:2003 section 13.1. + // ISO/IEC 10646:2003 sec 13.1 says each character is represented by "two octets". + // ISO/IEC 10646:2003 sec 6.3 says that when serialized as octets to use big endian. + internal class BMPEncoding : SpanBasedEncoding + { + protected override int GetBytes(ReadOnlySpan chars, Span bytes, bool write) + { + if (chars.IsEmpty) + { + return 0; + } + + int writeIdx = 0; + + for (int i = 0; i < chars.Length; i++) + { + char c = chars[i]; + + if (char.IsSurrogate(c)) + { + EncoderFallback.CreateFallbackBuffer().Fallback(c, i); + + Debug.Fail("Fallback should have thrown"); + throw new CryptographicException(); + } + + ushort val16 = c; + + if (write) + { + bytes[writeIdx + 1] = (byte)val16; + bytes[writeIdx] = (byte)(val16 >> 8); + } + + writeIdx += 2; + } + + return writeIdx; + } + + protected override int GetChars(ReadOnlySpan bytes, Span chars, bool write) + { + if (bytes.IsEmpty) + { + return 0; + } + + if (bytes.Length % 2 != 0) + { + DecoderFallback.CreateFallbackBuffer().Fallback( + bytes.Slice(bytes.Length - 1).ToArray(), + bytes.Length - 1); + + Debug.Fail("Fallback should have thrown"); + throw new CryptographicException(); + } + + int writeIdx = 0; + + for (int i = 0; i < bytes.Length; i += 2) + { + int val = bytes[i] << 8 | bytes[i + 1]; + char c = (char)val; + + if (char.IsSurrogate(c)) + { + DecoderFallback.CreateFallbackBuffer().Fallback( + bytes.Slice(i, 2).ToArray(), + i); + + Debug.Fail("Fallback should have thrown"); + throw new CryptographicException(); + } + + if (write) + { + chars[writeIdx] = c; + } + + writeIdx++; + } + + return writeIdx; + } + + public override int GetMaxByteCount(int charCount) + { + checked + { + return charCount * 2; + } + } + + public override int GetMaxCharCount(int byteCount) + { + return byteCount / 2; + } + } + + internal class SetOfValueComparer : IComparer> + { + internal static SetOfValueComparer Instance { get; } = new SetOfValueComparer(); + + public int Compare(ReadOnlyMemory x, ReadOnlyMemory y) + { + ReadOnlySpan xSpan = x.Span; + ReadOnlySpan ySpan = y.Span; + + int min = Math.Min(x.Length, y.Length); + int diff; + + for (int i = 0; i < min; i++) + { + int xVal = xSpan[i]; + byte yVal = ySpan[i]; + diff = xVal - yVal; + + if (diff != 0) + { + return diff; + } + } + + // The sorting rules (T-REC-X.690-201508 sec 11.6) say that the shorter one + // counts as if it are padded with as many 0x00s on the right as required for + // comparison. + // + // But, since a shorter definite value will have already had the length bytes + // compared, it was already different. And a shorter indefinite value will + // have hit end-of-contents, making it already different. + // + // This is here because the spec says it should be, but no values are known + // which will make diff != 0. + diff = x.Length - y.Length; + + if (diff != 0) + { + return diff; + } + + return 0; + } + } +} diff --git a/external/corefx/src/Common/src/System/Security/Cryptography/AsnReader.cs.REMOVED.git-id b/external/corefx/src/Common/src/System/Security/Cryptography/AsnReader.cs.REMOVED.git-id new file mode 100644 index 0000000000..56bae151a6 --- /dev/null +++ b/external/corefx/src/Common/src/System/Security/Cryptography/AsnReader.cs.REMOVED.git-id @@ -0,0 +1 @@ +54601ff32117b47c27803a9942042d549d2616b8 \ No newline at end of file diff --git a/external/corefx/src/Common/src/System/Security/Cryptography/AsnWriter.cs b/external/corefx/src/Common/src/System/Security/Cryptography/AsnWriter.cs new file mode 100644 index 0000000000..8f2fc34369 --- /dev/null +++ b/external/corefx/src/Common/src/System/Security/Cryptography/AsnWriter.cs @@ -0,0 +1,1633 @@ +// 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. + +// Enable CHECK_ACCURATE_ENSURE to ensure that the AsnWriter is not ever +// abusing the normal EnsureWriteCapacity + ArrayPool behaviors of rounding up. +//#define CHECK_ACCURATE_ENSURE + +using System.Buffers; +using System.Buffers.Text; +using System.Collections.Generic; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.InteropServices; + +namespace System.Security.Cryptography.Asn1 +{ + internal sealed class AsnWriter : IDisposable + { + private byte[] _buffer; + private int _offset; + private Stack<(Asn1Tag,int)> _nestingStack; + + public AsnEncodingRules RuleSet { get; } + + public AsnWriter(AsnEncodingRules ruleSet) + { + if (ruleSet != AsnEncodingRules.BER && + ruleSet != AsnEncodingRules.CER && + ruleSet != AsnEncodingRules.DER) + { + throw new ArgumentOutOfRangeException(nameof(ruleSet)); + } + + RuleSet = ruleSet; + } + + public void Dispose() + { + _nestingStack = null; + + if (_buffer != null) + { + Array.Clear(_buffer, 0, _offset); + ArrayPool.Shared.Return(_buffer); + _buffer = null; + _offset = 0; + } + } + + private void EnsureWriteCapacity(int pendingCount) + { + if (pendingCount < 0) + { + throw new OverflowException(); + } + + if (_buffer == null || _buffer.Length - _offset < pendingCount) + { +#if CHECK_ACCURATE_ENSURE +// A debug paradigm to make sure that throughout the execution nothing ever writes +// past where the buffer was "allocated". This causes quite a number of reallocs +// and copies, so it's a #define opt-in. + byte[] newBytes = new byte[_offset + pendingCount]; + + if (_buffer != null) + { + Buffer.BlockCopy(_buffer, 0, newBytes, 0, _offset); + } +#else + const int BlockSize = 1024; + // While the ArrayPool may have similar logic, make sure we don't run into a lot of + // "grow a little" by asking in 1k steps. + int blocks = checked(_offset + pendingCount + (BlockSize - 1)) / BlockSize; + byte[] newBytes = ArrayPool.Shared.Rent(BlockSize * blocks); + + if (_buffer != null) + { + Buffer.BlockCopy(_buffer, 0, newBytes, 0, _offset); + Array.Clear(_buffer, 0, _offset); + ArrayPool.Shared.Return(_buffer); + } +#endif + +#if DEBUG + // Ensure no "implicit 0" is happening + for (int i = _offset; i < newBytes.Length; i++) + { + newBytes[i] ^= 0xFF; + } +#endif + + _buffer = newBytes; + } + } + + private void WriteTag(Asn1Tag tag) + { + int spaceRequired = tag.CalculateEncodedSize(); + EnsureWriteCapacity(spaceRequired); + + if (!tag.TryWrite(_buffer.AsSpan().Slice(_offset, spaceRequired), out int written) || + written != spaceRequired) + { + Debug.Fail($"TryWrite failed or written was wrong value ({written} vs {spaceRequired})"); + throw new CryptographicException(); + } + + _offset += spaceRequired; + } + + // T-REC-X.690-201508 sec 8.1.3 + private void WriteLength(int length) + { + const byte MultiByteMarker = 0x80; + Debug.Assert(length >= -1); + + // If the indefinite form has been requested. + // T-REC-X.690-201508 sec 8.1.3.6 + if (length == -1) + { + EnsureWriteCapacity(1); + _buffer[_offset] = MultiByteMarker; + _offset++; + return; + } + + Debug.Assert(length >= 0); + + // T-REC-X.690-201508 sec 8.1.3.3, 8.1.3.4 + if (length < MultiByteMarker) + { + // Pre-allocate the pending data since we know how much. + EnsureWriteCapacity(1 + length); + _buffer[_offset] = (byte)length; + _offset++; + return; + } + + // The rest of the method implements T-REC-X.680-201508 sec 8.1.3.5 + int lengthLength = GetEncodedLengthSubsequentByteCount(length); + + // Pre-allocate the pending data since we know how much. + EnsureWriteCapacity(lengthLength + 1 + length); + _buffer[_offset] = (byte)(MultiByteMarker | lengthLength); + + // No minus one because offset didn't get incremented yet. + int idx = _offset + lengthLength; + + int remaining = length; + + do + { + _buffer[idx] = (byte)remaining; + remaining >>= 8; + idx--; + } while (remaining > 0); + + Debug.Assert(idx == _offset); + _offset += lengthLength + 1; + } + + // T-REC-X.690-201508 sec 8.1.3.5 + private static int GetEncodedLengthSubsequentByteCount(int length) + { + if (length <= 0x7F) + return 0; + if (length <= byte.MaxValue) + return 1; + if (length <= ushort.MaxValue) + return 2; + if (length <= 0x00FFFFFF) + return 3; + + return 4; + } + + public void WriteEncodedValue(ReadOnlyMemory preEncodedValue) + { + AsnReader reader = new AsnReader(preEncodedValue, RuleSet); + + // Is it legal under the current rules? + ReadOnlyMemory parsedBack = reader.GetEncodedValue(); + + if (reader.HasData) + { + throw new ArgumentException(SR.Cryptography_WriteEncodedValue_OneValueAtATime, nameof(preEncodedValue)); + } + + Debug.Assert(parsedBack.Length == preEncodedValue.Length); + + EnsureWriteCapacity(preEncodedValue.Length); + preEncodedValue.Span.CopyTo(_buffer.AsSpan().Slice(_offset)); + _offset += preEncodedValue.Length; + } + + // T-REC-X.690-201508 sec 8.1.5 + private void WriteEndOfContents() + { + EnsureWriteCapacity(2); + _buffer[_offset++] = 0; + _buffer[_offset++] = 0; + } + + public void WriteBoolean(bool value) + { + WriteBooleanCore(Asn1Tag.Boolean, value); + } + + public void WriteBoolean(Asn1Tag tag, bool value) + { + CheckUniversalTag(tag, UniversalTagNumber.Boolean); + + WriteBooleanCore(tag.AsPrimitive(), value); + } + + // T-REC-X.690-201508 sec 11.1, 8.2 + private void WriteBooleanCore(Asn1Tag tag, bool value) + { + Debug.Assert(!tag.IsConstructed); + WriteTag(tag); + WriteLength(1); + // Ensured by WriteLength + Debug.Assert(_offset < _buffer.Length); + _buffer[_offset] = (byte)(value ? 0xFF : 0x00); + _offset++; + } + + public void WriteInteger(long value) + { + WriteIntegerCore(Asn1Tag.Integer, value); + } + + public void WriteInteger(ulong value) + { + WriteNonNegativeIntegerCore(Asn1Tag.Integer, value); + } + + public void WriteInteger(BigInteger value) + { + WriteIntegerCore(Asn1Tag.Integer, value); + } + + public void WriteInteger(Asn1Tag tag, long value) + { + CheckUniversalTag(tag, UniversalTagNumber.Integer); + + WriteIntegerCore(tag.AsPrimitive(), value); + } + + // T-REC-X.690-201508 sec 8.3 + private void WriteIntegerCore(Asn1Tag tag, long value) + { + if (value >= 0) + { + WriteNonNegativeIntegerCore(tag, (ulong)value); + return; + } + + int valueLength; + + if (value >= sbyte.MinValue) + valueLength = 1; + else if (value >= short.MinValue) + valueLength = 2; + else if (value >= unchecked((long)0xFFFFFFFF_FF800000)) + valueLength = 3; + else if (value >= int.MinValue) + valueLength = 4; + else if (value >= unchecked((long)0xFFFFFF80_00000000)) + valueLength = 5; + else if (value >= unchecked((long)0xFFFF8000_00000000)) + valueLength = 6; + else if (value >= unchecked((long)0xFF800000_00000000)) + valueLength = 7; + else + valueLength = 8; + + Debug.Assert(!tag.IsConstructed); + WriteTag(tag); + WriteLength(valueLength); + + long remaining = value; + int idx = _offset + valueLength - 1; + + do + { + _buffer[idx] = (byte)remaining; + remaining >>= 8; + idx--; + } while (idx >= _offset); + +#if DEBUG + if (valueLength > 1) + { + // T-REC-X.690-201508 sec 8.3.2 + // Cannot start with 9 bits of 1 (or 9 bits of 0, but that's not this method). + Debug.Assert(_buffer[_offset] != 0xFF || _buffer[_offset + 1] < 0x80); + } +#endif + + _offset += valueLength; + } + + public void WriteInteger(Asn1Tag tag, ulong value) + { + CheckUniversalTag(tag, UniversalTagNumber.Integer); + + WriteNonNegativeIntegerCore(tag.AsPrimitive(), value); + } + + // T-REC-X.690-201508 sec 8.3 + private void WriteNonNegativeIntegerCore(Asn1Tag tag, ulong value) + { + int valueLength; + + // 0x80 needs two bytes: 0x00 0x80 + if (value < 0x80) + valueLength = 1; + else if (value < 0x8000) + valueLength = 2; + else if (value < 0x800000) + valueLength = 3; + else if (value < 0x80000000) + valueLength = 4; + else if (value < 0x80_00000000) + valueLength = 5; + else if (value < 0x8000_00000000) + valueLength = 6; + else if (value < 0x800000_00000000) + valueLength = 7; + else if (value < 0x80000000_00000000) + valueLength = 8; + else + valueLength = 9; + + // Clear the constructed bit, if it was set. + Debug.Assert(!tag.IsConstructed); + WriteTag(tag); + WriteLength(valueLength); + + ulong remaining = value; + int idx = _offset + valueLength - 1; + + do + { + _buffer[idx] = (byte)remaining; + remaining >>= 8; + idx--; + } while (idx >= _offset); + +#if DEBUG + if (valueLength > 1) + { + // T-REC-X.690-201508 sec 8.3.2 + // Cannot start with 9 bits of 0 (or 9 bits of 1, but that's not this method). + Debug.Assert(_buffer[_offset] != 0 || _buffer[_offset + 1] > 0x7F); + } +#endif + + _offset += valueLength; + } + + public void WriteInteger(Asn1Tag tag, BigInteger value) + { + CheckUniversalTag(tag, UniversalTagNumber.Integer); + + WriteIntegerCore(tag.AsPrimitive(), value); + } + + // T-REC-X.690-201508 sec 8.3 + private void WriteIntegerCore(Asn1Tag tag, BigInteger value) + { + // TODO: Split this for netstandard vs netcoreapp for span-perf?. + byte[] encoded = value.ToByteArray(); + Array.Reverse(encoded); + + Debug.Assert(!tag.IsConstructed); + WriteTag(tag); + WriteLength(encoded.Length); + Buffer.BlockCopy(encoded, 0, _buffer, _offset, encoded.Length); + _offset += encoded.Length; + } + + public void WriteBitString(ReadOnlySpan bitString, int unusedBitCount=0) + { + WriteBitStringCore(Asn1Tag.PrimitiveBitString, bitString, unusedBitCount); + } + + public void WriteBitString(Asn1Tag tag, ReadOnlySpan bitString, int unusedBitCount=0) + { + CheckUniversalTag(tag, UniversalTagNumber.BitString); + + // Primitive or constructed, doesn't matter. + WriteBitStringCore(tag, bitString, unusedBitCount); + } + + // T-REC-X.690-201508 sec 8.6 + private void WriteBitStringCore(Asn1Tag tag, ReadOnlySpan bitString, int unusedBitCount) + { + // T-REC-X.690-201508 sec 8.6.2.2 + if (unusedBitCount < 0 || unusedBitCount > 7) + { + throw new ArgumentOutOfRangeException( + nameof(unusedBitCount), + unusedBitCount, + SR.Cryptography_Asn_UnusedBitCountRange); + } + + // T-REC-X.690-201508 sec 8.6.2.3 + if (bitString.Length == 0 && unusedBitCount != 0) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + // If 3 bits are "unused" then build a mask for them to check for 0. + // 1 << 3 => 0b0000_1000 + // subtract 1 => 0b000_0111 + int mask = (1 << unusedBitCount) - 1; + byte lastByte = bitString.IsEmpty ? (byte)0 : bitString[bitString.Length - 1]; + + if ((lastByte & mask) != 0) + { + // T-REC-X.690-201508 sec 11.2 + // + // This could be ignored for BER, but since DER is more common and + // it likely suggests a program error on the caller, leave it enabled for + // BER for now. + // TODO: Probably warrants a distinct message. + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + if (RuleSet == AsnEncodingRules.CER) + { + // T-REC-X.690-201508 sec 9.2 + // + // If it's not within a primitive segment, use the constructed encoding. + // (>= instead of > because of the unused bit count byte) + if (bitString.Length >= AsnReader.MaxCERSegmentSize) + { + WriteConstructedCerBitString(tag, bitString, unusedBitCount); + return; + } + } + + // Clear the constructed flag, if present. + WriteTag(tag.AsPrimitive()); + // The unused bits byte requires +1. + WriteLength(bitString.Length + 1); + _buffer[_offset] = (byte)unusedBitCount; + _offset++; + bitString.CopyTo(_buffer.AsSpan().Slice(_offset)); + _offset += bitString.Length; + } + + // T-REC-X.690-201508 sec 9.2, 8.6 + private void WriteConstructedCerBitString(Asn1Tag tag, ReadOnlySpan payload, int unusedBitCount) + { + const int MaxCERSegmentSize = AsnReader.MaxCERSegmentSize; + // Every segment has an "unused bit count" byte. + const int MaxCERContentSize = MaxCERSegmentSize - 1; + Debug.Assert(payload.Length > MaxCERContentSize); + + WriteTag(tag.AsConstructed()); + // T-REC-X.690-201508 sec 9.1 + // Constructed CER uses the indefinite form. + WriteLength(-1); + + int fullSegments = Math.DivRem(payload.Length, MaxCERContentSize, out int lastContentSize); + + // The tag size is 1 byte. + // The length will always be encoded as 82 03 E8 (3 bytes) + // And 1000 content octets (by T-REC-X.690-201508 sec 9.2) + const int FullSegmentEncodedSize = 1004; + Debug.Assert( + FullSegmentEncodedSize == 1 + 1 + MaxCERSegmentSize + GetEncodedLengthSubsequentByteCount(MaxCERSegmentSize)); + + int remainingEncodedSize; + + if (lastContentSize == 0) + { + remainingEncodedSize = 0; + } + else + { + // One byte of tag, minimum one byte of length, and one byte of unused bit count. + remainingEncodedSize = 3 + lastContentSize + GetEncodedLengthSubsequentByteCount(lastContentSize); + } + + // Reduce the number of copies by pre-calculating the size. + // +2 for End-Of-Contents + int expectedSize = fullSegments * FullSegmentEncodedSize + remainingEncodedSize + 2; + EnsureWriteCapacity(expectedSize); + + byte[] ensureNoExtraCopy = _buffer; + int savedOffset = _offset; + + ReadOnlySpan remainingData = payload; + Span dest; + Asn1Tag primitiveBitString = Asn1Tag.PrimitiveBitString; + + while (remainingData.Length > MaxCERContentSize) + { + // T-REC-X.690-201508 sec 8.6.4.1 + WriteTag(primitiveBitString); + WriteLength(MaxCERSegmentSize); + // 0 unused bits in this segment. + _buffer[_offset] = 0; + _offset++; + + dest = _buffer.AsSpan().Slice(_offset); + remainingData.Slice(0, MaxCERContentSize).CopyTo(dest); + + remainingData = remainingData.Slice(MaxCERContentSize); + _offset += MaxCERContentSize; + } + + WriteTag(primitiveBitString); + WriteLength(remainingData.Length + 1); + + _buffer[_offset] = (byte)unusedBitCount; + _offset++; + + dest = _buffer.AsSpan().Slice(_offset); + remainingData.CopyTo(dest); + _offset += remainingData.Length; + + WriteEndOfContents(); + + Debug.Assert(_offset - savedOffset == expectedSize, $"expected size was {expectedSize}, actual was {_offset - savedOffset}"); + Debug.Assert(_buffer == ensureNoExtraCopy, $"_buffer was replaced during {nameof(WriteConstructedCerBitString)}"); + } + + public void WriteNamedBitList(object enumValue) + { + if (enumValue == null) + throw new ArgumentNullException(nameof(enumValue)); + + WriteNamedBitList(Asn1Tag.PrimitiveBitString, enumValue); + } + + public void WriteNamedBitList(TEnum enumValue) where TEnum : struct + { + WriteNamedBitList(Asn1Tag.PrimitiveBitString, enumValue); + } + + public void WriteNamedBitList(Asn1Tag tag, object enumValue) + { + if (enumValue == null) + throw new ArgumentNullException(nameof(enumValue)); + + WriteNamedBitList(tag, enumValue.GetType(), enumValue); + } + + public void WriteNamedBitList(Asn1Tag tag, TEnum enumValue) where TEnum : struct + { + WriteNamedBitList(tag, typeof(TEnum), enumValue); + } + + private void WriteNamedBitList(Asn1Tag tag, Type tEnum, object enumValue) + { + Type backingType = tEnum.GetEnumUnderlyingType(); + + if (!tEnum.IsDefined(typeof(FlagsAttribute), false)) + { + throw new ArgumentException(SR.Cryptography_Asn_NamedBitListRequiresFlagsEnum, nameof(tEnum)); + } + + ulong integralValue; + + if (backingType == typeof(ulong)) + { + integralValue = Convert.ToUInt64(enumValue); + } + else + { + // All other types fit in a (signed) long. + long numericValue = Convert.ToInt64(enumValue); + integralValue = unchecked((ulong)numericValue); + } + + WriteNamedBitList(tag, integralValue); + } + + // T-REC-X.680-201508 sec 22 + // T-REC-X.690-201508 sec 8.6, 11.2.2 + private void WriteNamedBitList(Asn1Tag tag, ulong integralValue) + { + Span temp = stackalloc byte[sizeof(ulong)]; + // Reset to all zeros, since we're just going to or-in bits we need. + temp.Clear(); + + int indexOfHighestSetBit = -1; + + for (int i = 0; integralValue != 0; integralValue >>= 1, i++) + { + if ((integralValue & 1) != 0) + { + temp[i / 8] |= (byte)(0x80 >> (i % 8)); + indexOfHighestSetBit = i; + } + } + + if (indexOfHighestSetBit < 0) + { + // No bits were set; this is an empty bit string. + // T-REC-X.690-201508 sec 11.2.2-note2 + WriteBitString(tag, ReadOnlySpan.Empty); + } + else + { + // At least one bit was set. + // Determine the shortest length necessary to represent the bit string. + + // Since "bit 0" gets written down 0 => 1. + // Since "bit 8" is in the second byte 8 => 2. + // That makes the formula ((bit / 8) + 1) instead of ((bit + 7) / 8). + int byteLen = (indexOfHighestSetBit / 8) + 1; + int unusedBitCount = 7 - (indexOfHighestSetBit % 8); + + WriteBitString( + tag, + temp.Slice(0, byteLen), + unusedBitCount); + } + } + + public void WriteOctetString(ReadOnlySpan octetString) + { + WriteOctetString(Asn1Tag.PrimitiveOctetString, octetString); + } + + public void WriteOctetString(Asn1Tag tag, ReadOnlySpan octetString) + { + CheckUniversalTag(tag, UniversalTagNumber.OctetString); + + // Primitive or constructed, doesn't matter. + WriteOctetStringCore(tag, octetString); + } + + // T-REC-X.690-201508 sec 8.7 + private void WriteOctetStringCore(Asn1Tag tag, ReadOnlySpan octetString) + { + if (RuleSet == AsnEncodingRules.CER) + { + // If it's bigger than a primitive segment, use the constructed encoding + // T-REC-X.690-201508 sec 9.2 + if (octetString.Length > AsnReader.MaxCERSegmentSize) + { + WriteConstructedCerOctetString(tag, octetString); + return; + } + } + + // Clear the constructed flag, if present. + WriteTag(tag.AsPrimitive()); + WriteLength(octetString.Length); + octetString.CopyTo(_buffer.AsSpan().Slice(_offset)); + _offset += octetString.Length; + } + + // T-REC-X.690-201508 sec 9.2, 8.7 + private void WriteConstructedCerOctetString(Asn1Tag tag, ReadOnlySpan payload) + { + const int MaxCERSegmentSize = AsnReader.MaxCERSegmentSize; + Debug.Assert(payload.Length > MaxCERSegmentSize); + + WriteTag(tag.AsConstructed()); + WriteLength(-1); + + int fullSegments = Math.DivRem(payload.Length, MaxCERSegmentSize, out int lastSegmentSize); + + // The tag size is 1 byte. + // The length will always be encoded as 82 03 E8 (3 bytes) + // And 1000 content octets (by T-REC-X.690-201508 sec 9.2) + const int FullSegmentEncodedSize = 1004; + Debug.Assert( + FullSegmentEncodedSize == 1 + 1 + MaxCERSegmentSize + GetEncodedLengthSubsequentByteCount(MaxCERSegmentSize)); + + int remainingEncodedSize; + + if (lastSegmentSize == 0) + { + remainingEncodedSize = 0; + } + else + { + // One byte of tag, and minimum one byte of length. + remainingEncodedSize = 2 + lastSegmentSize + GetEncodedLengthSubsequentByteCount(lastSegmentSize); + } + + // Reduce the number of copies by pre-calculating the size. + // +2 for End-Of-Contents + int expectedSize = fullSegments * FullSegmentEncodedSize + remainingEncodedSize + 2; + EnsureWriteCapacity(expectedSize); + + byte[] ensureNoExtraCopy = _buffer; + int savedOffset = _offset; + + ReadOnlySpan remainingData = payload; + Span dest; + Asn1Tag primitiveOctetString = Asn1Tag.PrimitiveOctetString; + + while (remainingData.Length > MaxCERSegmentSize) + { + // T-REC-X.690-201508 sec 8.7.3.2-note2 + WriteTag(primitiveOctetString); + WriteLength(MaxCERSegmentSize); + + dest = _buffer.AsSpan().Slice(_offset); + remainingData.Slice(0, MaxCERSegmentSize).CopyTo(dest); + + _offset += MaxCERSegmentSize; + remainingData = remainingData.Slice(MaxCERSegmentSize); + } + + WriteTag(primitiveOctetString); + WriteLength(remainingData.Length); + dest = _buffer.AsSpan().Slice(_offset); + remainingData.CopyTo(dest); + _offset += remainingData.Length; + + WriteEndOfContents(); + + Debug.Assert(_offset - savedOffset == expectedSize, $"expected size was {expectedSize}, actual was {_offset - savedOffset}"); + Debug.Assert(_buffer == ensureNoExtraCopy, $"_buffer was replaced during {nameof(WriteConstructedCerOctetString)}"); + } + + public void WriteNull() + { + WriteNullCore(Asn1Tag.Null); + } + + public void WriteNull(Asn1Tag tag) + { + CheckUniversalTag(tag, UniversalTagNumber.Null); + + WriteNullCore(tag.AsPrimitive()); + } + + // T-REC-X.690-201508 sec 8.8 + private void WriteNullCore(Asn1Tag tag) + { + Debug.Assert(!tag.IsConstructed); + WriteTag(tag); + WriteLength(0); + } + + public void WriteObjectIdentifier(Oid oid) + { + if (oid == null) + throw new ArgumentNullException(nameof(oid)); + + WriteObjectIdentifier(oid.Value); + } + + public void WriteObjectIdentifier(string oidValue) + { + if (oidValue == null) + throw new ArgumentNullException(nameof(oidValue)); + + WriteObjectIdentifier(oidValue.AsReadOnlySpan()); + } + + public void WriteObjectIdentifier(ReadOnlySpan oidValue) + { + WriteObjectIdentifierCore(Asn1Tag.ObjectIdentifier, oidValue); + } + + public void WriteObjectIdentifier(Asn1Tag tag, Oid oid) + { + if (oid == null) + throw new ArgumentNullException(nameof(oid)); + + WriteObjectIdentifier(tag, oid.Value); + } + + public void WriteObjectIdentifier(Asn1Tag tag, string oidValue) + { + if (oidValue == null) + throw new ArgumentNullException(nameof(oidValue)); + + WriteObjectIdentifier(tag, oidValue.AsReadOnlySpan()); + } + + public void WriteObjectIdentifier(Asn1Tag tag, ReadOnlySpan oidValue) + { + CheckUniversalTag(tag, UniversalTagNumber.ObjectIdentifier); + + WriteObjectIdentifierCore(tag.AsPrimitive(), oidValue); + } + + // T-REC-X.690-201508 sec 8.19 + private void WriteObjectIdentifierCore(Asn1Tag tag, ReadOnlySpan oidValue) + { + // T-REC-X.690-201508 sec 8.19.4 + // The first character is in { 0, 1, 2 }, the second will be a '.', and a third (digit) + // will also exist. + if (oidValue.Length < 3) + throw new CryptographicException(SR.Argument_InvalidOidValue); + if (oidValue[1] != '.') + throw new CryptographicException(SR.Argument_InvalidOidValue); + + // The worst case is "1.1.1.1.1", which takes 4 bytes (5 components, with the first two condensed) + // Longer numbers get smaller: "2.1.127" is only 2 bytes. (81d (0x51) and 127 (0x7F)) + // So length / 2 should prevent any reallocations. + byte[] tmp = ArrayPool.Shared.Rent(oidValue.Length / 2); + int tmpOffset = 0; + + try + { + int firstComponent; + + switch (oidValue[0]) + { + case '0': + firstComponent = 0; + break; + case '1': + firstComponent = 1; + break; + case '2': + firstComponent = 2; + break; + default: + throw new CryptographicException(SR.Argument_InvalidOidValue); + } + + // The first two components are special: + // ITU X.690 8.19.4: + // The numerical value of the first subidentifier is derived from the values of the first two + // object identifier components in the object identifier value being encoded, using the formula: + // (X*40) + Y + // where X is the value of the first object identifier component and Y is the value of the + // second object identifier component. + // NOTE - This packing of the first two object identifier components recognizes that only + // three values are allocated from the root node, and at most 39 subsequent values from + // nodes reached by X = 0 and X = 1. + + // skip firstComponent and the trailing . + ReadOnlySpan remaining = oidValue.Slice(2); + + BigInteger subIdentifier = ParseSubIdentifier(ref remaining); + subIdentifier += 40 * firstComponent; + + int localLen = EncodeSubIdentifier(tmp.AsSpan().Slice(tmpOffset), ref subIdentifier); + tmpOffset += localLen; + + while (!remaining.IsEmpty) + { + subIdentifier = ParseSubIdentifier(ref remaining); + localLen = EncodeSubIdentifier(tmp.AsSpan().Slice(tmpOffset), ref subIdentifier); + tmpOffset += localLen; + } + + Debug.Assert(!tag.IsConstructed); + WriteTag(tag); + WriteLength(tmpOffset); + Buffer.BlockCopy(tmp, 0, _buffer, _offset, tmpOffset); + _offset += tmpOffset; + } + finally + { + Array.Clear(tmp, 0, tmpOffset); + ArrayPool.Shared.Return(tmp); + } + } + + private static BigInteger ParseSubIdentifier(ref ReadOnlySpan oidValue) + { + int endIndex = oidValue.IndexOf('.'); + + if (endIndex == -1) + { + endIndex = oidValue.Length; + } + else if (endIndex == oidValue.Length - 1) + { + throw new CryptographicException(SR.Argument_InvalidOidValue); + } + + // The following code is equivalent to + // BigInteger.TryParse(temp, NumberStyles.None, CultureInfo.InvariantCulture, out value) + // TODO: Split this for netstandard vs netcoreapp for span-perf?. + BigInteger value = BigInteger.Zero; + + for (int position = 0; position < endIndex; position++) + { + if (position > 0 && value == 0) + { + // T-REC X.680-201508 sec 12.26 + throw new CryptographicException(SR.Argument_InvalidOidValue); + } + + value *= 10; + value += AtoI(oidValue[position]); + } + + oidValue = oidValue.Slice(Math.Min(oidValue.Length, endIndex + 1)); + return value; + } + + private static int AtoI(char c) + { + if (c >= '0' && c <= '9') + { + return c - '0'; + } + + throw new CryptographicException(SR.Argument_InvalidOidValue); + } + + // ITU-T-X.690-201508 sec 8.19.5 + private static int EncodeSubIdentifier(Span dest, ref BigInteger subIdentifier) + { + Debug.Assert(dest.Length > 0); + + if (subIdentifier.IsZero) + { + dest[0] = 0; + return 1; + } + + BigInteger unencoded = subIdentifier; + int idx = 0; + + do + { + BigInteger cur = unencoded & 0x7F; + byte curByte = (byte)cur; + + if (subIdentifier != unencoded) + { + curByte |= 0x80; + } + + unencoded >>= 7; + dest[idx] = curByte; + idx++; + } + while (unencoded != BigInteger.Zero); + + Reverse(dest.Slice(0, idx)); + return idx; + } + + public void WriteEnumeratedValue(object enumValue) + { + if (enumValue == null) + throw new ArgumentNullException(nameof(enumValue)); + + WriteEnumeratedValue(Asn1Tag.Enumerated, enumValue); + } + + public void WriteEnumeratedValue(TEnum value) where TEnum : struct + { + WriteEnumeratedValue(Asn1Tag.Enumerated, value); + } + + public void WriteEnumeratedValue(Asn1Tag tag, object enumValue) + { + if (enumValue == null) + throw new ArgumentNullException(nameof(enumValue)); + + WriteEnumeratedValue(tag.AsPrimitive(), enumValue.GetType(), enumValue); + } + + public void WriteEnumeratedValue(Asn1Tag tag, TEnum value) where TEnum : struct + { + WriteEnumeratedValue(tag.AsPrimitive(), typeof(TEnum), value); + } + + // T-REC-X.690-201508 sec 8.4 + private void WriteEnumeratedValue(Asn1Tag tag, Type tEnum, object enumValue) + { + CheckUniversalTag(tag, UniversalTagNumber.Enumerated); + + Type backingType = tEnum.GetEnumUnderlyingType(); + + if (tEnum.IsDefined(typeof(FlagsAttribute), false)) + { + throw new ArgumentException( + SR.Cryptography_Asn_EnumeratedValueRequiresNonFlagsEnum, + nameof(tEnum)); + } + + if (backingType == typeof(ulong)) + { + ulong numericValue = Convert.ToUInt64(enumValue); + // T-REC-X.690-201508 sec 8.4 + WriteNonNegativeIntegerCore(tag, numericValue); + } + else + { + // All other types fit in a (signed) long. + long numericValue = Convert.ToInt64(enumValue); + // T-REC-X.690-201508 sec 8.4 + WriteIntegerCore(tag, numericValue); + } + } + + public void PushSequence() + { + PushSequenceCore(Asn1Tag.Sequence); + } + + public void PushSequence(Asn1Tag tag) + { + CheckUniversalTag(tag, UniversalTagNumber.Sequence); + + // Assert the constructed flag, in case it wasn't. + PushSequenceCore(tag.AsConstructed()); + } + + // T-REC-X.690-201508 sec 8.9, 8.10 + private void PushSequenceCore(Asn1Tag tag) + { + PushTag(tag.AsConstructed()); + } + + public void PopSequence() + { + PopSequence(Asn1Tag.Sequence); + } + + public void PopSequence(Asn1Tag tag) + { + // PopSequence shouldn't be used to pop a SetOf. + CheckUniversalTag(tag, UniversalTagNumber.Sequence); + + // Assert the constructed flag, in case it wasn't. + PopSequenceCore(tag.AsConstructed()); + } + + // T-REC-X.690-201508 sec 8.9, 8.10 + private void PopSequenceCore(Asn1Tag tag) + { + PopTag(tag); + } + + public void PushSetOf() + { + PushSetOf(Asn1Tag.SetOf); + } + + public void PushSetOf(Asn1Tag tag) + { + CheckUniversalTag(tag, UniversalTagNumber.SetOf); + + // Assert the constructed flag, in case it wasn't. + PushSetOfCore(tag.AsConstructed()); + } + + // T-REC-X.690-201508 sec 8.12 + // The writer claims SetOf, and not Set, so as to avoid the field + // ordering clause of T-REC-X.690-201508 sec 9.3 + private void PushSetOfCore(Asn1Tag tag) + { + PushTag(tag); + } + + public void PopSetOf() + { + PopSetOfCore(Asn1Tag.SetOf); + } + + public void PopSetOf(Asn1Tag tag) + { + CheckUniversalTag(tag, UniversalTagNumber.SetOf); + + // Assert the constructed flag, in case it wasn't. + PopSetOfCore(tag.AsConstructed()); + } + + // T-REC-X.690-201508 sec 8.12 + private void PopSetOfCore(Asn1Tag tag) + { + // T-REC-X.690-201508 sec 11.6 + bool sortContents = RuleSet == AsnEncodingRules.CER || RuleSet == AsnEncodingRules.DER; + + PopTag(tag, sortContents); + } + + public void WriteUtcTime(DateTimeOffset value) + { + WriteUtcTimeCore(Asn1Tag.UtcTime, value); + } + + public void WriteUtcTime(Asn1Tag tag, DateTimeOffset value) + { + CheckUniversalTag(tag, UniversalTagNumber.UtcTime); + + // Clear the constructed flag, if present. + WriteUtcTimeCore(tag.AsPrimitive(), value); + } + + // T-REC-X.680-201508 sec 47 + // T-REC-X.690-201508 sec 11.8 + private void WriteUtcTimeCore(Asn1Tag tag, DateTimeOffset value) + { + // Because UtcTime is IMPLICIT VisibleString it technically can have + // a constructed form. + // DER says character strings must be primitive. + // CER says character strings <= 1000 encoded bytes must be primitive. + // So we'll just make BER be primitive, too. + Debug.Assert(!tag.IsConstructed); + WriteTag(tag); + + // BER allows for omitting the seconds, but that's not an option we need to expose. + // BER allows for non-UTC values, but that's also not an option we need to expose. + // So the format is always yyMMddHHmmssZ (13) + const int UtcTimeValueLength = 13; + WriteLength(UtcTimeValueLength); + + DateTimeOffset normalized = value.ToUniversalTime(); + + int year = normalized.Year; + int month = normalized.Month; + int day = normalized.Day; + int hour = normalized.Hour; + int minute = normalized.Minute; + int second = normalized.Second; + + Span baseSpan = _buffer.AsSpan().Slice(_offset); + StandardFormat format = new StandardFormat('D', 2); + + if (!Utf8Formatter.TryFormat(year % 100, baseSpan.Slice(0, 2), out _, format) || + !Utf8Formatter.TryFormat(month, baseSpan.Slice(2, 2), out _, format) || + !Utf8Formatter.TryFormat(day, baseSpan.Slice(4, 2), out _, format) || + !Utf8Formatter.TryFormat(hour, baseSpan.Slice(6, 2), out _, format) || + !Utf8Formatter.TryFormat(minute, baseSpan.Slice(8, 2), out _, format) || + !Utf8Formatter.TryFormat(second, baseSpan.Slice(10, 2), out _, format)) + { + Debug.Fail($"Utf8Formatter.TryFormat failed to build components of {normalized:O}"); + throw new CryptographicException(); + } + + _buffer[_offset + 12] = (byte)'Z'; + + _offset += UtcTimeValueLength; + } + + public void WriteGeneralizedTime(DateTimeOffset value, bool omitFractionalSeconds = false) + { + WriteGeneralizedTimeCore(Asn1Tag.GeneralizedTime, value, omitFractionalSeconds); + } + + public void WriteGeneralizedTime(Asn1Tag tag, DateTimeOffset value, bool omitFractionalSeconds = false) + { + CheckUniversalTag(tag, UniversalTagNumber.GeneralizedTime); + + // Clear the constructed flag, if present. + WriteGeneralizedTimeCore(tag.AsPrimitive(), value, omitFractionalSeconds); + } + + // T-REC-X.680-201508 sec 46 + // T-REC-X.690-201508 sec 11.7 + private void WriteGeneralizedTimeCore(Asn1Tag tag, DateTimeOffset value, bool omitFractionalSeconds) + { + // GeneralizedTime under BER allows many different options: + // * (HHmmss), (HHmm), (HH) + // * "(value).frac", "(value),frac" + // * frac == 0 may be omitted or emitted + // non-UTC offset in various formats + // + // We're not allowing any of them. + // Just encode as the CER/DER common restrictions. + // + // This results in the following formats: + // yyyyMMddHHmmssZ + // yyyyMMddHHmmss.f?Z + // + // where "f?" is anything from "f" to "fffffff" (tenth of a second down to 100ns/1-tick) + // with no trailing zeros. + DateTimeOffset normalized = value.ToUniversalTime(); + + if (normalized.Year > 9999) + { + // This is unreachable since DateTimeOffset guards against this internally. + throw new ArgumentOutOfRangeException(nameof(value)); + } + + // We're only loading in sub-second ticks. + // Ticks are defined as 1e-7 seconds, so their printed form + // is at the longest "0.1234567", or 9 bytes. + Span fraction = stackalloc byte[0]; + + if (!omitFractionalSeconds) + { + long floatingTicks = normalized.Ticks % TimeSpan.TicksPerSecond; + + if (floatingTicks != 0) + { + // We're only loading in sub-second ticks. + // Ticks are defined as 1e-7 seconds, so their printed form + // is at the longest "0.1234567", or 9 bytes. + fraction = stackalloc byte[9]; + + decimal decimalTicks = floatingTicks; + decimalTicks /= TimeSpan.TicksPerSecond; + + if (!Utf8Formatter.TryFormat(decimalTicks, fraction, out int bytesWritten, new StandardFormat('G'))) + { + Debug.Fail($"Utf8Formatter.TryFormat could not format {floatingTicks} / TicksPerSecond"); + throw new CryptographicException(); + } + + Debug.Assert(bytesWritten > 2, $"{bytesWritten} should be > 2"); + Debug.Assert(fraction[0] == (byte)'0'); + Debug.Assert(fraction[1] == (byte)'.'); + + fraction = fraction.Slice(1, bytesWritten - 1); + } + } + + // yyyy, MM, dd, hh, mm, ss + const int IntegerPortionLength = 4 + 2 + 2 + 2 + 2 + 2; + // Z, and the optional fraction. + int totalLength = IntegerPortionLength + 1 + fraction.Length; + + // Because GeneralizedTime is IMPLICIT VisibleString it technically can have + // a constructed form. + // DER says character strings must be primitive. + // CER says character strings <= 1000 encoded bytes must be primitive. + // So we'll just make BER be primitive, too. + Debug.Assert(!tag.IsConstructed); + WriteTag(tag); + WriteLength(totalLength); + + int year = normalized.Year; + int month = normalized.Month; + int day = normalized.Day; + int hour = normalized.Hour; + int minute = normalized.Minute; + int second = normalized.Second; + + Span baseSpan = _buffer.AsSpan().Slice(_offset); + StandardFormat d4 = new StandardFormat('D', 4); + StandardFormat d2 = new StandardFormat('D', 2); + + if (!Utf8Formatter.TryFormat(year, baseSpan.Slice(0, 4), out _, d4) || + !Utf8Formatter.TryFormat(month, baseSpan.Slice(4, 2), out _, d2) || + !Utf8Formatter.TryFormat(day, baseSpan.Slice(6, 2), out _, d2) || + !Utf8Formatter.TryFormat(hour, baseSpan.Slice(8, 2), out _, d2) || + !Utf8Formatter.TryFormat(minute, baseSpan.Slice(10, 2), out _, d2) || + !Utf8Formatter.TryFormat(second, baseSpan.Slice(12, 2), out _, d2)) + { + Debug.Fail($"Utf8Formatter.TryFormat failed to build components of {normalized:O}"); + throw new CryptographicException(); + } + + _offset += IntegerPortionLength; + fraction.CopyTo(baseSpan.Slice(IntegerPortionLength)); + _offset += fraction.Length; + + _buffer[_offset] = (byte)'Z'; + _offset++; + } + + /// + /// Transfer the encoded representation of the data to . + /// + /// Write destination. + /// The number of bytes which were written to . + /// true if the encode succeeded, false if is too small. + /// + /// A or has not been closed via + /// or . + /// + public bool TryEncode(Span dest, out int bytesWritten) + { + if ((_nestingStack?.Count ?? 0) != 0) + throw new InvalidOperationException(SR.Cryptography_AsnWriter_EncodeUnbalancedStack); + + // If the stack is closed out then everything is a definite encoding (BER, DER) or a + // required indefinite encoding (CER). So we're correctly sized up, and ready to copy. + if (dest.Length < _offset) + { + bytesWritten = 0; + return false; + } + + if (_offset == 0) + { + bytesWritten = 0; + return true; + } + + bytesWritten = _offset; + _buffer.AsSpan().Slice(0, _offset).CopyTo(dest); + return true; + } + + public byte[] Encode() + { + if ((_nestingStack?.Count ?? 0) != 0) + { + throw new InvalidOperationException(SR.Cryptography_AsnWriter_EncodeUnbalancedStack); + } + + if (_offset == 0) + { + return Array.Empty(); + } + + // If the stack is closed out then everything is a definite encoding (BER, DER) or a + // required indefinite encoding (CER). So we're correctly sized up, and ready to copy. + return _buffer.AsSpan().Slice(0, _offset).ToArray(); + } + + public ReadOnlySpan EncodeAsSpan() + { + if ((_nestingStack?.Count ?? 0) != 0) + { + throw new InvalidOperationException(SR.Cryptography_AsnWriter_EncodeUnbalancedStack); + } + + if (_offset == 0) + { + return ReadOnlySpan.Empty; + } + + // If the stack is closed out then everything is a definite encoding (BER, DER) or a + // required indefinite encoding (CER). So we're correctly sized up, and ready to copy. + return new ReadOnlySpan(_buffer, 0, _offset); + } + + private void PushTag(Asn1Tag tag) + { + if (_nestingStack == null) + { + _nestingStack = new Stack<(Asn1Tag,int)>(); + } + + Debug.Assert(tag.IsConstructed); + WriteTag(tag); + _nestingStack.Push((tag, _offset)); + // Indicate that the length is indefinite. + // We'll come back and clean this up (as appropriate) in PopTag. + WriteLength(-1); + } + + private void PopTag(Asn1Tag tag, bool sortContents=false) + { + if (_nestingStack == null || _nestingStack.Count == 0) + { + throw new ArgumentException(SR.Cryptography_AsnWriter_PopWrongTag, nameof(tag)); + } + + (Asn1Tag stackTag, int lenOffset) = _nestingStack.Peek(); + + Debug.Assert(tag.IsConstructed); + if (stackTag != tag) + { + throw new ArgumentException(SR.Cryptography_AsnWriter_PopWrongTag, nameof(tag)); + } + + _nestingStack.Pop(); + + if (sortContents) + { + SortContents(_buffer, lenOffset + 1, _offset); + } + + // BER could use the indefinite encoding that CER does. + // But since the definite encoding form is easier to read (doesn't require a contextual + // parser to find the end-of-contents marker) some ASN.1 readers (including the previous + // incarnation of AsnReader) may choose not to support it. + // + // So, BER will use the DER rules here, in the interest of broader compatibility. + + // T-REC-X.690-201508 sec 9.1 (constructed CER => indefinite length) + // T-REC-X.690-201508 sec 8.1.3.6 + if (RuleSet == AsnEncodingRules.CER) + { + WriteEndOfContents(); + return; + } + + int containedLength = _offset - 1 - lenOffset; + Debug.Assert(containedLength >= 0); + + int shiftSize = GetEncodedLengthSubsequentByteCount(containedLength); + + // Best case, length fits in the compact byte + if (shiftSize == 0) + { + _buffer[lenOffset] = (byte)containedLength; + return; + } + + // We're currently at the end, so ensure we have room for N more bytes. + EnsureWriteCapacity(shiftSize); + + // Buffer.BlockCopy correctly does forward-overlapped, so use it. + int start = lenOffset + 1; + Buffer.BlockCopy(_buffer, start, _buffer, start + shiftSize, containedLength); + + int tmp = _offset; + _offset = lenOffset; + WriteLength(containedLength); + Debug.Assert(_offset - lenOffset - 1 == shiftSize); + _offset = tmp + shiftSize; + } + + public void WriteCharacterString(UniversalTagNumber encodingType, string str) + { + if (str == null) + throw new ArgumentNullException(nameof(str)); + + WriteCharacterString(encodingType, str.AsReadOnlySpan()); + } + + public void WriteCharacterString(UniversalTagNumber encodingType, ReadOnlySpan str) + { + Text.Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType); + + WriteCharacterStringCore(new Asn1Tag(encodingType), encoding, str); + } + + public void WriteCharacterString(Asn1Tag tag, UniversalTagNumber encodingType, string str) + { + if (str == null) + throw new ArgumentNullException(nameof(str)); + + WriteCharacterString(tag, encodingType, str.AsReadOnlySpan()); + } + + public void WriteCharacterString(Asn1Tag tag, UniversalTagNumber encodingType, ReadOnlySpan str) + { + CheckUniversalTag(tag, encodingType); + + Text.Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType); + WriteCharacterStringCore(tag, encoding, str); + } + + // T-REC-X.690-201508 sec 8.23 + private void WriteCharacterStringCore(Asn1Tag tag, Text.Encoding encoding, ReadOnlySpan str) + { + int size = -1; + + // T-REC-X.690-201508 sec 9.2 + if (RuleSet == AsnEncodingRules.CER) + { + // TODO: Split this for netstandard vs netcoreapp for span?. + unsafe + { + fixed (char* strPtr = &MemoryMarshal.GetReference(str)) + { + size = encoding.GetByteCount(strPtr, str.Length); + + // If it exceeds the primitive segment size, use the constructed encoding. + if (size > AsnReader.MaxCERSegmentSize) + { + WriteConstructedCerCharacterString(tag, encoding, str, size); + return; + } + } + } + } + + // TODO: Split this for netstandard vs netcoreapp for span?. + unsafe + { + fixed (char* strPtr = &MemoryMarshal.GetReference(str)) + { + if (size < 0) + { + size = encoding.GetByteCount(strPtr, str.Length); + } + + // Clear the constructed tag, if present. + WriteTag(tag.AsPrimitive()); + WriteLength(size); + Span dest = _buffer.AsSpan().Slice(_offset, size); + + fixed (byte* destPtr = &MemoryMarshal.GetReference(dest)) + { + int written = encoding.GetBytes(strPtr, str.Length, destPtr, dest.Length); + + if (written != size) + { + Debug.Fail($"Encoding produced different answer for GetByteCount ({size}) and GetBytes ({written})"); + throw new InvalidOperationException(); + } + } + + _offset += size; + } + } + } + + private void WriteConstructedCerCharacterString(Asn1Tag tag, Text.Encoding encoding, ReadOnlySpan str, int size) + { + Debug.Assert(size > AsnReader.MaxCERSegmentSize); + + byte[] tmp; + + // TODO: Split this for netstandard vs netcoreapp for span?. + unsafe + { + fixed (char* strPtr = &MemoryMarshal.GetReference(str)) + { + tmp = ArrayPool.Shared.Rent(size); + + fixed (byte* destPtr = tmp) + { + int written = encoding.GetBytes(strPtr, str.Length, destPtr, tmp.Length); + + if (written != size) + { + Debug.Fail( + $"Encoding produced different answer for GetByteCount ({size}) and GetBytes ({written})"); + throw new InvalidOperationException(); + } + } + } + } + + WriteConstructedCerOctetString(tag, tmp.AsSpan().Slice(0, size)); + Array.Clear(tmp, 0, size); + ArrayPool.Shared.Return(tmp); + } + + private static void SortContents(byte[] buffer, int start, int end) + { + Debug.Assert(buffer != null); + Debug.Assert(end >= start); + + int len = end - start; + + if (len == 0) + { + return; + } + + // Since BER can read everything and the reader does not mutate data + // just use a BER reader for identifying the positions of the values + // within this memory segment. + // + // Since it's not mutating, any restrictions imposed by CER or DER will + // still be maintained. + var reader = new AsnReader(new ReadOnlyMemory(buffer, start, len), AsnEncodingRules.BER); + + List<(int, int)> positions = new List<(int, int)>(); + + int pos = start; + + while (reader.HasData) + { + ReadOnlyMemory encoded = reader.GetEncodedValue(); + positions.Add((pos, encoded.Length)); + pos += encoded.Length; + } + + Debug.Assert(pos == end); + + var comparer = new ArrayIndexSetOfValueComparer(buffer); + positions.Sort(comparer); + + byte[] tmp = ArrayPool.Shared.Rent(len); + + pos = 0; + + foreach ((int offset, int length) in positions) + { + Buffer.BlockCopy(buffer, offset, tmp, pos, length); + pos += length; + } + + Debug.Assert(pos == len); + + Buffer.BlockCopy(tmp, 0, buffer, start, len); + Array.Clear(tmp, 0, len); + ArrayPool.Shared.Return(tmp); + } + + internal static void Reverse(Span span) + { + int i = 0; + int j = span.Length - 1; + + while (i < j) + { + byte tmp = span[i]; + span[i] = span[j]; + span[j] = tmp; + + i++; + j--; + } + } + + private static void CheckUniversalTag(Asn1Tag tag, UniversalTagNumber universalTagNumber) + { + if (tag.TagClass == TagClass.Universal && tag.TagValue != (int)universalTagNumber) + { + throw new ArgumentException( + SR.Cryptography_Asn_UniversalValueIsFixed, + nameof(tag)); + } + } + + private class ArrayIndexSetOfValueComparer : IComparer<(int, int)> + { + private readonly byte[] _data; + + public ArrayIndexSetOfValueComparer(byte[] data) + { + _data = data; + } + + public int Compare((int, int) x, (int, int) y) + { + (int xOffset, int xLength) = x; + (int yOffset, int yLength) = y; + + int value = + SetOfValueComparer.Instance.Compare( + new ReadOnlyMemory(_data, xOffset, xLength), + new ReadOnlyMemory(_data, yOffset, yLength)); + + if (value == 0) + { + // Whichever had the lowest index wins (once sorted, stay sorted) + return xOffset - yOffset; + } + + return value; + } + } + } +} diff --git a/external/corefx/src/Common/src/System/Security/Cryptography/DerSequenceReader.cs b/external/corefx/src/Common/src/System/Security/Cryptography/DerSequenceReader.cs index 2a963a0b7f..b68c9da093 100644 --- a/external/corefx/src/Common/src/System/Security/Cryptography/DerSequenceReader.cs +++ b/external/corefx/src/Common/src/System/Security/Cryptography/DerSequenceReader.cs @@ -59,20 +59,23 @@ namespace System.Security.Cryptography { } - internal DerSequenceReader(DerTag tagToEat, byte[] data, int offset, int length) + private DerSequenceReader(DerTag tagToEat, byte[] data, int offset, int length) { - _data = data; - _end = offset + length; - Debug.Assert(data != null, "Data is null"); - Debug.Assert(offset >= 0, "Offset is negative"); - if (length < 2 || length > data.Length - offset) + if (offset < 0 || length < 2 || length > data.Length - offset) throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + _data = data; + _end = offset + length; _position = offset; EatTag(tagToEat); - ContentLength = EatLength(); + int contentLength = EatLength(); + Debug.Assert(_end - contentLength >= _position); + ContentLength = contentLength; + + // If the sequence reports being smaller than the buffer, shrink the end-of-validity. + _end = _position + contentLength; } internal static DerSequenceReader CreateForPayload(byte[] payload) @@ -90,7 +93,14 @@ namespace System.Security.Cryptography if (!HasData) throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - return _data[_position]; + byte tag = _data[_position]; + + if ((tag & TagNumberMask) == TagNumberMask) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + return tag; } internal bool HasTag(DerTag expectedTag) @@ -170,11 +180,15 @@ namespace System.Security.Cryptography /// internal byte[] ReadNextEncodedValue() { + // Check that the tag is legal, but the value isn't relevant. + PeekTag(); + int lengthLength; - int contentLength = ScanContentLength(_data, _position + 1, out lengthLength); + int contentLength = ScanContentLength(_data, _position + 1, _end, out lengthLength); // Length of tag, encoded length, and the content int totalLength = 1 + lengthLength + contentLength; - + Debug.Assert(_end - totalLength >= _position); + byte[] encodedValue = new byte[totalLength]; Buffer.BlockCopy(_data, _position, encodedValue, 0, totalLength); @@ -326,7 +340,7 @@ namespace System.Security.Cryptography CheckTag(expected, _data, _position); int lengthLength; - int contentLength = ScanContentLength(_data, _position + 1, out lengthLength); + int contentLength = ScanContentLength(_data, _position + 1, _end, out lengthLength); int totalLength = 1 + lengthLength + contentLength; DerSequenceReader reader = new DerSequenceReader(expected, _data, _position, totalLength); @@ -496,6 +510,13 @@ namespace System.Security.Cryptography throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); byte actual = data[position]; + byte relevant = (byte)(actual & TagNumberMask); + + // Multi-byte tags are not supported by this implementation. + if (relevant == TagNumberMask) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } // Context-specific datatypes cannot be tag-verified if ((actual & ContextSpecificTagFlag) != 0) @@ -503,7 +524,6 @@ namespace System.Security.Cryptography return; } - byte relevant = (byte)(actual & TagNumberMask); byte expectedByte = (byte)((byte)expected & TagNumberMask); if (expectedByte != relevant) @@ -524,54 +544,69 @@ namespace System.Security.Cryptography private int EatLength() { int bytesConsumed; - int answer = ScanContentLength(_data, _position, out bytesConsumed); + int answer = ScanContentLength(_data, _position, _end, out bytesConsumed); _position += bytesConsumed; - - if (answer > _end - _position) - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - return answer; } - private static int ScanContentLength(byte[] data, int offset, out int bytesConsumed) + private static int ScanContentLength(byte[] data, int offset, int end, out int bytesConsumed) { - if (offset >= data.Length) + Debug.Assert(end <= data.Length); + + if (offset >= end) throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); byte lengthOrLengthLength = data[offset]; if (lengthOrLengthLength < 0x80) { - if (lengthOrLengthLength > data.Length - offset) + bytesConsumed = 1; + + if (lengthOrLengthLength > end - offset - bytesConsumed) throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - bytesConsumed = 1; return lengthOrLengthLength; } - // The one byte which was lengthLength, plus the number of bytes it said to consume. - bytesConsumed = 1 + (lengthOrLengthLength & 0x7F); + int lengthLength = (lengthOrLengthLength & 0x7F); - if (bytesConsumed > data.Length - offset) + if (lengthLength > sizeof(int)) + { + // .NET Arrays cannot exceed int.MaxValue in length. Since we're bounded by an + // array we know that this is invalid data. + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + // The one byte which was lengthLength, plus the number of bytes it said to consume. + bytesConsumed = 1 + lengthLength; + + if (bytesConsumed > end - offset) throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); // CER indefinite length is not supported. if (bytesConsumed == 1) throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - int end = offset + bytesConsumed; + int lengthEnd = offset + bytesConsumed; int accum = 0; // data[offset] is lengthLength, so start at data[offset + 1] and stop before // data[offset + 1 + lengthLength], aka data[end]. - for (int i = offset + 1; i < end; i++) + for (int i = offset + 1; i < lengthEnd; i++) { accum <<= 8; - accum += data[i]; + accum |= data[i]; } - if (accum > data.Length - offset - bytesConsumed) + if (accum < 0) + { + // .NET Arrays cannot exceed int.MaxValue in length. Since we're bounded by an + // array we know that this is invalid data. + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + if (accum > end - offset - bytesConsumed) throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); return accum; diff --git a/external/corefx/src/Common/src/System/Text/OSEncoding.Windows.cs b/external/corefx/src/Common/src/System/Text/OSEncoding.Windows.cs index 47397037a4..cfe4e31a6d 100644 --- a/external/corefx/src/Common/src/System/Text/OSEncoding.Windows.cs +++ b/external/corefx/src/Common/src/System/Text/OSEncoding.Windows.cs @@ -5,7 +5,6 @@ using System; using System.Text; using System.Collections.Generic; -using System.Diagnostics.Contracts; namespace System.Text { @@ -256,4 +255,4 @@ namespace System.Text return result; } } -} \ No newline at end of file +} diff --git a/external/corefx/src/Common/src/System/Text/StringOrCharArray.cs b/external/corefx/src/Common/src/System/Text/StringOrCharArray.cs index fa52138fe4..4979aef69e 100644 --- a/external/corefx/src/Common/src/System/Text/StringOrCharArray.cs +++ b/external/corefx/src/Common/src/System/Text/StringOrCharArray.cs @@ -12,7 +12,7 @@ namespace System.Text /// allocate/copy the chars into a new string. This comes at the expense of an extra /// reference field + two Int32s per key in the size of the dictionary's Entry array. /// - internal struct StringOrCharArray : IEquatable + internal readonly struct StringOrCharArray : IEquatable { public readonly string String; diff --git a/external/corefx/src/Common/src/System/Text/ValueStringBuilder.cs b/external/corefx/src/Common/src/System/Text/ValueStringBuilder.cs new file mode 100644 index 0000000000..84e5fa8de9 --- /dev/null +++ b/external/corefx/src/Common/src/System/Text/ValueStringBuilder.cs @@ -0,0 +1,200 @@ +// 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.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace System.Text +{ + internal ref struct ValueStringBuilder + { + private char[] _arrayToReturnToPool; + private Span _chars; + private int _pos; + + public ValueStringBuilder(Span initialBuffer) + { + _arrayToReturnToPool = null; + _chars = initialBuffer; + _pos = 0; + } + + public int Length + { + get => _pos; + set + { + int delta = value - _pos; + if (delta > 0) + { + Append('\0', delta); + } + else + { + _pos = value; + } + } + } + + public override string ToString() + { + var s = new string(_chars.Slice(0, _pos)); + Clear(); + return s; + } + + public bool TryCopyTo(Span destination, out int charsWritten) + { + if (_chars.Slice(0, _pos).TryCopyTo(destination)) + { + charsWritten = _pos; + Clear(); + return true; + } + else + { + charsWritten = 0; + Clear(); + return false; + } + } + + public void Insert(int index, char value, int count) + { + if (_pos > _chars.Length - count) + { + Grow(count); + } + + int remaining = _pos - index; + _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count)); + _chars.Slice(index, count).Fill(value); + _pos += count; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(char c) + { + int pos = _pos; + if (pos < _chars.Length) + { + _chars[pos] = c; + _pos = pos + 1; + } + else + { + GrowAndAppend(c); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(string s) + { + int pos = _pos; + if (s.Length == 1 && pos < _chars.Length) // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc. + { + _chars[pos] = s[0]; + _pos = pos + 1; + } + else + { + AppendSlow(s); + } + } + + private void AppendSlow(string s) + { + int pos = _pos; + if (pos > _chars.Length - s.Length) + { + Grow(s.Length); + } + + bool copied = s.AsReadOnlySpan().TryCopyTo(_chars.Slice(pos)); + Debug.Assert(copied, "Grow should have made enough room to successfully copy"); + _pos += s.Length; + } + + public void Append(char c, int count) + { + if (_pos > _chars.Length - count) + { + Grow(count); + } + + Span dst = _chars.Slice(_pos, count); + for (int i = 0; i < dst.Length; i++) + { + dst[i] = c; + } + _pos += count; + } + + public unsafe void Append(char* value, int length) + { + int pos = _pos; + if (pos > _chars.Length - length) + { + Grow(length); + } + + Span dst = _chars.Slice(_pos, length); + for (int i = 0; i < dst.Length; i++) + { + dst[i] = *value++; + } + _pos += length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span AppendSpan(int length) + { + int origPos = _pos; + if (origPos > _chars.Length - length) + { + Grow(length); + } + + _pos = origPos + length; + return _chars.Slice(origPos, length); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void GrowAndAppend(char c) + { + Grow(1); + Append(c); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void Grow(int requiredAdditionalCapacity) + { + Debug.Assert(requiredAdditionalCapacity > _chars.Length - _pos); + + char[] poolArray = ArrayPool.Shared.Rent(Math.Max(_pos + requiredAdditionalCapacity, _chars.Length * 2)); + + bool success = _chars.TryCopyTo(poolArray); + Debug.Assert(success); + + char[] toReturn = _arrayToReturnToPool; + _chars = _arrayToReturnToPool = poolArray; + if (toReturn != null) + { + ArrayPool.Shared.Return(toReturn); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Clear() + { + char[] toReturn = _arrayToReturnToPool; + this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again + if (toReturn != null) + { + ArrayPool.Shared.Return(toReturn); + } + } + } +} diff --git a/external/corefx/src/Common/src/System/Threading/Tasks/ForceAsyncAwaiter.cs b/external/corefx/src/Common/src/System/Threading/Tasks/ForceAsyncAwaiter.cs index d1bd27733c..1e8fc4c52b 100644 --- a/external/corefx/src/Common/src/System/Threading/Tasks/ForceAsyncAwaiter.cs +++ b/external/corefx/src/Common/src/System/Threading/Tasks/ForceAsyncAwaiter.cs @@ -21,7 +21,7 @@ namespace System.Threading.Tasks } } - internal struct ForceAsyncAwaiter : ICriticalNotifyCompletion + internal readonly struct ForceAsyncAwaiter : ICriticalNotifyCompletion { private readonly Task _task; diff --git a/external/corefx/src/Common/tests/Common.Tests.csproj b/external/corefx/src/Common/tests/Common.Tests.csproj index 17d4fd760d..e5c4206d4f 100644 --- a/external/corefx/src/Common/tests/Common.Tests.csproj +++ b/external/corefx/src/Common/tests/Common.Tests.csproj @@ -6,7 +6,6 @@ InnerLoop;OuterLoop true - @@ -63,6 +62,9 @@ Common\System\Text\ReusableTextReader.cs + + Common\System\Text\ValueStringBuilder.cs + Common\System\Security\IdentityHelper.cs @@ -72,6 +74,7 @@ + @@ -133,4 +136,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/Common/tests/Performance/Common.Performance.Tests.csproj b/external/corefx/src/Common/tests/Performance/Common.Performance.Tests.csproj new file mode 100644 index 0000000000..de2f73a943 --- /dev/null +++ b/external/corefx/src/Common/tests/Performance/Common.Performance.Tests.csproj @@ -0,0 +1,27 @@ + + + + + true + {B96198F5-9BF7-42DE-83E8-3EE39DA25F43} + true + + + + + + Common\System\Marvin.cs + + + + Common\System\PerfUtils.cs + + + + + {69e46a6f-9966-45a5-8945-2559fe337827} + PerfRunner + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Intrinsics.X86/ref/Configurations.props b/external/corefx/src/Common/tests/Performance/Configurations.props similarity index 100% rename from external/corefx/src/System.Runtime.Intrinsics.X86/ref/Configurations.props rename to external/corefx/src/Common/tests/Performance/Configurations.props diff --git a/external/corefx/src/Common/tests/Performance/Perf.Marvin.cs b/external/corefx/src/Common/tests/Performance/Perf.Marvin.cs new file mode 100644 index 0000000000..bfa5bc3820 --- /dev/null +++ b/external/corefx/src/Common/tests/Performance/Perf.Marvin.cs @@ -0,0 +1,48 @@ +// 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.Collections.Generic; +using System.Linq; +using Microsoft.Xunit.Performance; +using Xunit; + +namespace System +{ + public class Perf_Marvin + { + private static IEnumerable EnumerateRandomByteArrayTestCases() + { + var r = new Random(123); + foreach (int size in + Enumerable.Range(0, 25) + .Union(new int[] { 50, 100, 200, 2000, 20000, 200000, 2000000 })) + { + byte[] array = new byte[size]; + r.NextBytes(array); + + int iterations = 2000000 / Math.Max(1, size); + yield return new object[] { iterations, array }; + } + } + + [Benchmark] + [MemberData(nameof(EnumerateRandomByteArrayTestCases))] + public void Add(int iterations, byte[] data) + { + Span otherData = new byte[] { 1, 2, 3 }; + var bytes = new Span(data); + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < iterations; i++) + { + Marvin.ComputeHash(bytes, 123123123123UL); + Marvin.ComputeHash(otherData, 555888555888UL); + } + } + } + } + } +} diff --git a/external/corefx/src/Common/tests/Resources/Strings.resx b/external/corefx/src/Common/tests/Resources/Strings.resx index 26af9eb505..0cc24f087d 100644 --- a/external/corefx/src/Common/tests/Resources/Strings.resx +++ b/external/corefx/src/Common/tests/Resources/Strings.resx @@ -1,110 +1,129 @@  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + - Argument_InvalidPathChars + Argument_InvalidPathChars {0} IO_FileNotFound - + IO_FileNotFound_FileName {0} @@ -134,4 +153,7 @@ IO_SharingViolation_File {0} + + IO_PathTooLong_Path {0} + \ No newline at end of file diff --git a/external/corefx/src/Common/tests/System/Buffers/NativeOwnedMemory.cs b/external/corefx/src/Common/tests/System/Buffers/NativeOwnedMemory.cs index 31d90b35bf..f68a70e074 100644 --- a/external/corefx/src/Common/tests/System/Buffers/NativeOwnedMemory.cs +++ b/external/corefx/src/Common/tests/System/Buffers/NativeOwnedMemory.cs @@ -6,7 +6,7 @@ using System.Buffers; using System.Diagnostics; using System.Runtime.InteropServices; -namespace System.IO.Tests +namespace System.Buffers { internal sealed class NativeOwnedMemory : OwnedMemory { @@ -53,7 +53,12 @@ namespace System.IO.Tests public override unsafe Span Span => new Span((void*)_ptr, _length); - public override unsafe MemoryHandle Pin() => new MemoryHandle(this, (void*)_ptr); + public override unsafe MemoryHandle Pin(int offset = 0) + { + if (offset < 0 || offset > _length) throw new ArgumentOutOfRangeException(nameof(offset)); + void* pointer = (void*)((byte*)_ptr + offset); + return new MemoryHandle(this, pointer); + } public override bool Release() { diff --git a/external/corefx/src/Common/tests/System/Diagnostics/DebuggerAttributes.cs b/external/corefx/src/Common/tests/System/Diagnostics/DebuggerAttributes.cs index c53acc1a44..5d63ba4237 100644 --- a/external/corefx/src/Common/tests/System/Diagnostics/DebuggerAttributes.cs +++ b/external/corefx/src/Common/tests/System/Diagnostics/DebuggerAttributes.cs @@ -23,6 +23,15 @@ namespace System.Diagnostics return GetField(obj, fieldName).GetValue(obj); } + internal static void InvokeDebuggerTypeProxyProperties(object obj) + { + DebuggerAttributeInfo info = ValidateDebuggerTypeProxyProperties(obj.GetType(), obj); + foreach (PropertyInfo pi in info.Properties) + { + pi.GetValue(info.Instance, null); + } + } + internal static DebuggerAttributeInfo ValidateDebuggerTypeProxyProperties(object obj) { return ValidateDebuggerTypeProxyProperties(obj.GetType(), obj); @@ -236,4 +245,4 @@ namespace System.Diagnostics return null; } } -} \ No newline at end of file +} diff --git a/external/corefx/src/Common/tests/System/Diagnostics/RemoteExecutorConsoleApp/RemoteExecutorConsoleApp.cs b/external/corefx/src/Common/tests/System/Diagnostics/RemoteExecutorConsoleApp/RemoteExecutorConsoleApp.cs index 664edc34a1..4a700c209b 100644 --- a/external/corefx/src/Common/tests/System/Diagnostics/RemoteExecutorConsoleApp/RemoteExecutorConsoleApp.cs +++ b/external/corefx/src/Common/tests/System/Diagnostics/RemoteExecutorConsoleApp/RemoteExecutorConsoleApp.cs @@ -43,7 +43,7 @@ namespace RemoteExecutorConsoleApp Type t = null; MethodInfo mi = null; object instance = null; - int exitCode = int.MaxValue; + int exitCode = 0; try { // Create the test class if necessary @@ -57,9 +57,15 @@ namespace RemoteExecutorConsoleApp // Invoke the test object result = mi.Invoke(instance, additionalArgs); - exitCode = result is Task task ? - task.GetAwaiter().GetResult() : - (int)result; + + if (result is Task task) + { + exitCode = task.GetAwaiter().GetResult(); + } + else if (result is int exit) + { + exitCode = exit; + } } catch (Exception exc) { diff --git a/external/corefx/src/Common/tests/System/Diagnostics/RemoteExecutorConsoleApp/RemoteExecutorConsoleApp.csproj b/external/corefx/src/Common/tests/System/Diagnostics/RemoteExecutorConsoleApp/RemoteExecutorConsoleApp.csproj index 1c9c6d6ce7..72bbd1353d 100644 --- a/external/corefx/src/Common/tests/System/Diagnostics/RemoteExecutorConsoleApp/RemoteExecutorConsoleApp.csproj +++ b/external/corefx/src/Common/tests/System/Diagnostics/RemoteExecutorConsoleApp/RemoteExecutorConsoleApp.csproj @@ -9,7 +9,6 @@ true false - diff --git a/external/corefx/src/Common/tests/System/IO/Compression/CompressionStreamPerfTestBase.cs b/external/corefx/src/Common/tests/System/IO/Compression/CompressionStreamPerfTestBase.cs new file mode 100644 index 0000000000..43af3eb4df --- /dev/null +++ b/external/corefx/src/Common/tests/System/IO/Compression/CompressionStreamPerfTestBase.cs @@ -0,0 +1,88 @@ +// 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.Collections.Generic; +using Microsoft.Xunit.Performance; +using Xunit; + +namespace System.IO.Compression +{ + public abstract class CompressionStreamPerfTestBase : CompressionStreamTestBase + { + public static IEnumerable CanterburyCorpus_WithCompressionLevel() + { + foreach (CompressionLevel compressionLevel in Enum.GetValues(typeof(CompressionLevel))) + { + foreach (object[] canterburyWithoutLevel in CanterburyCorpus()) + { + yield return new object[] { canterburyWithoutLevel[0], canterburyWithoutLevel[1], compressionLevel }; + } + } + } + + public static IEnumerable CanterburyCorpus() + { + foreach (int innerIterations in new int[] { 1, 10 }) + { + foreach (var fileName in UncompressedTestFiles()) + { + yield return new object[] { innerIterations, fileName[0] }; + } + } + } + + + /// + /// Benchmark tests to measure the performance of individually compressing each file in the + /// Canterbury Corpus + /// + [Benchmark] + [MemberData(nameof(CanterburyCorpus_WithCompressionLevel))] + public void Compress_Canterbury(int innerIterations, string uncompressedFileName, CompressionLevel compressLevel) + { + byte[] bytes = File.ReadAllBytes(uncompressedFileName); + MemoryStream[] compressedDataStreams = new MemoryStream[innerIterations]; + foreach (var iteration in Benchmark.Iterations) + { + // resizing a memory stream during compression will throw off our results, so pre-size it to the + // size of our input + for (int i = 0; i < innerIterations; i++) + compressedDataStreams[i] = new MemoryStream(bytes.Length); + using (iteration.StartMeasurement()) + for (int i = 0; i < innerIterations; i++) + using (Stream compressor = CreateStream(compressedDataStreams[i], compressLevel)) + compressor.Write(bytes, 0, bytes.Length); + + for (int i = 0; i < innerIterations; i++) + compressedDataStreams[i].Dispose(); + } + } + + /// + /// Benchmark tests to measure the performance of individually compressing each file in the + /// Canterbury Corpus + /// + [Benchmark] + [MemberData(nameof(CanterburyCorpus))] + public void Decompress_Canterbury(int innerIterations, string uncompressedFilePath) + { + string compressedFilePath = CompressedTestFile(uncompressedFilePath); + byte[] outputRead = new byte[new FileInfo(uncompressedFilePath).Length]; + MemoryStream[] memories = new MemoryStream[innerIterations]; + foreach (var iteration in Benchmark.Iterations) + { + for (int i = 0; i < innerIterations; i++) + memories[i] = new MemoryStream(File.ReadAllBytes(compressedFilePath)); + + using (iteration.StartMeasurement()) + for (int i = 0; i < innerIterations; i++) + using (Stream decompressor = CreateStream(memories[i], CompressionMode.Decompress)) + decompressor.Read(outputRead, 0, outputRead.Length); + + for (int i = 0; i < innerIterations; i++) + memories[i].Dispose(); + } + } + } +} diff --git a/external/corefx/src/Common/tests/System/IO/Compression/CompressionStreamTestBase.cs b/external/corefx/src/Common/tests/System/IO/Compression/CompressionStreamTestBase.cs new file mode 100644 index 0000000000..8e057be262 --- /dev/null +++ b/external/corefx/src/Common/tests/System/IO/Compression/CompressionStreamTestBase.cs @@ -0,0 +1,44 @@ +// 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.Collections.Generic; + +namespace System.IO.Compression +{ + public abstract class CompressionTestBase + { + public static IEnumerable UncompressedTestFiles() + { + yield return new object[] { Path.Combine("UncompressedTestFiles", "TestDocument.doc") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "TestDocument.docx") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "TestDocument.pdf") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "TestDocument.txt") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "alice29.txt") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "asyoulik.txt") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "cp.html") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "fields.c") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "grammar.lsp") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "kennedy.xls") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "lcet10.txt") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "plrabn12.txt") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "ptt5") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "sum") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "xargs.1") }; + } + protected virtual string UncompressedTestFile() => Path.Combine("UncompressedTestFiles", "TestDocument.pdf"); + protected abstract string CompressedTestFile(string uncompressedPath); + } + + public abstract class CompressionStreamTestBase : CompressionTestBase + { + public abstract Stream CreateStream(Stream stream, CompressionMode mode); + public abstract Stream CreateStream(Stream stream, CompressionMode mode, bool leaveOpen); + public abstract Stream CreateStream(Stream stream, CompressionLevel level); + public abstract Stream CreateStream(Stream stream, CompressionLevel level, bool leaveOpen); + public abstract Stream BaseStream(Stream stream); + public virtual bool FlushCompletes { get => true; } + public virtual bool FlushNoOps { get => false; } + public virtual int BufferSize { get => 8192; } + } +} diff --git a/external/corefx/src/Common/tests/System/IO/Compression/CompressionStreamUnitTestBase.cs b/external/corefx/src/Common/tests/System/IO/Compression/CompressionStreamUnitTestBase.cs new file mode 100644 index 0000000000..e6585de33a --- /dev/null +++ b/external/corefx/src/Common/tests/System/IO/Compression/CompressionStreamUnitTestBase.cs @@ -0,0 +1,1395 @@ +// 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.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace System.IO.Compression +{ + public abstract class CompressionStreamUnitTestBase : CompressionStreamTestBase + { + [Fact] + public virtual void FlushAsync_DuringWriteAsync() + { + if (FlushNoOps) + return; + byte[] buffer = new byte[100000]; + Random rand = new Random(); + rand.NextBytes(buffer); + + using (var writeStream = new ManualSyncMemoryStream(false)) + using (var compressor = CreateStream(writeStream, CompressionMode.Compress)) + { + Task task = null; + try + { + // Write needs to be big enough to trigger a write to the underlying base stream so the WriteAsync call doesn't immediately complete. + task = compressor.WriteAsync(buffer, 0, buffer.Length); + while (task.IsCompleted) + { + rand.NextBytes(buffer); + task = compressor.WriteAsync(buffer, 0, buffer.Length); + } + Assert.Throws(() => { compressor.FlushAsync(); }); // "overlapping flushes" + } + finally + { + // Unblock Async operations + writeStream.manualResetEvent.Set(); + // The original WriteAsync should be able to complete + Assert.True(task.Wait(10 * 500), "Original WriteAsync Task did not complete in time"); + Assert.True(writeStream.WriteHit, "BaseStream Write function was not called"); + } + } + } + + [Fact] + public async Task FlushAsync_DuringReadAsync() + { + if (FlushNoOps) + return; + byte[] buffer = new byte[32]; + string testFilePath = CompressedTestFile(UncompressedTestFile()); + using (var readStream = await ManualSyncMemoryStream.GetStreamFromFileAsync(testFilePath, false)) + using (var decompressor = CreateStream(readStream, CompressionMode.Decompress, true)) + { + Task task = null; + try + { + readStream.manualResetEvent.Reset(); + readStream.ReadHit = false; + task = decompressor.ReadAsync(buffer, 0, 32); + Assert.True(readStream.ReadHit); + Assert.Throws(() => { decompressor.FlushAsync(); }); // "overlapping read" + } + finally + { + // Unblock Async operations + readStream.manualResetEvent.Set(); + // The original ReadAsync should be able to complete + Assert.True(task.Wait(10 * 500), "Original ReadAsync Task did not complete in time"); + } + } + } + + [Fact] + public async Task FlushAsync_DuringFlushAsync() + { + if (FlushNoOps) + return; + byte[] buffer = null; + string testFilePath = CompressedTestFile(UncompressedTestFile()); + using (var origStream = await LocalMemoryStream.readAppFileAsync(testFilePath)) + { + buffer = origStream.ToArray(); + } + + using (var writeStream = new ManualSyncMemoryStream(false)) + using (var zip = CreateStream(writeStream, CompressionMode.Compress)) + { + Task task = null; + try + { + writeStream.manualResetEvent.Set(); + await zip.WriteAsync(buffer, 0, buffer.Length); + writeStream.manualResetEvent.Reset(); + writeStream.WriteHit = false; + task = zip.FlushAsync(); + while (!writeStream.WriteHit && task.IsCompleted) + { + zip.Write(buffer, 0, 1); + task = zip.FlushAsync(); + } + + Assert.Throws(() => { zip.FlushAsync(); }); // "overlapping flushes" + } + finally + { + // Unblock Async operations + writeStream.manualResetEvent.Set(); + // The original WriteAsync should be able to complete + Assert.True(task.Wait(5000), "Original write Task did not complete in time"); + Assert.True(writeStream.WriteHit, "Underlying Writesync function was not called."); + + } + } + } + + [Fact] + public virtual void WriteAsync_DuringWriteAsync() + { + byte[] buffer = new byte[100000]; + Random rand = new Random(); + rand.NextBytes(buffer); + + using (var writeStream = new ManualSyncMemoryStream(false)) + using (var compressor = CreateStream(writeStream, CompressionMode.Compress)) + { + Task task = null; + try + { + // Write needs to be big enough to trigger a write to the underlying base stream so the WriteAsync call doesn't immediately complete. + task = compressor.WriteAsync(buffer, 0, buffer.Length); + while (task.IsCompleted) + { + rand.NextBytes(buffer); + task = compressor.WriteAsync(buffer, 0, buffer.Length); + } + Assert.Throws(() => { compressor.WriteAsync(buffer, 32, 32); }); // "overlapping write" + } + finally + { + // Unblock Async operations + writeStream.manualResetEvent.Set(); + // The original WriteAsync should be able to complete + Assert.True(task.Wait(10 * 500), "Original WriteAsync Task did not complete in time"); + Assert.True(writeStream.WriteHit, "BaseStream Write function was not called"); + } + } + } + + [Fact] + public async Task ReadAsync_DuringReadAsync() + { + byte[] buffer = new byte[32]; + string testFilePath = CompressedTestFile(UncompressedTestFile()); + using (var readStream = await ManualSyncMemoryStream.GetStreamFromFileAsync(testFilePath, false)) + using (var decompressor = CreateStream(readStream, CompressionMode.Decompress, true)) + { + Task task = null; + try + { + task = decompressor.ReadAsync(buffer, 0, 32); + Assert.Throws(() => { decompressor.ReadAsync(buffer, 0, 32); }); // "overlapping read" + } + finally + { + // Unblock Async operations + readStream.manualResetEvent.Set(); + // The original ReadAsync should be able to complete + Assert.True(task.Wait(10 * 500), "The original ReadAsync should be able to complete"); + Assert.True(readStream.ReadHit, "BaseStream ReadAsync should have been called"); + } + } + } + + [Fact] + public virtual void Dispose_WithUnfinishedWriteAsync() + { + byte[] buffer = new byte[100000]; + Random rand = new Random(); + rand.NextBytes(buffer); + + using (var writeStream = new ManualSyncMemoryStream(false)) + { + var compressor = CreateStream(writeStream, CompressionMode.Compress, leaveOpen: true); + compressor.Write(buffer, 0, buffer.Length); + int writesBeingFlushed = 2; + Task task = null; + try + { + // Write needs to be big enough to trigger a write to the underlying base stream so the WriteAsync call doesn't immediately complete. + task = compressor.WriteAsync(buffer, 0, buffer.Length); + while (task.IsCompleted) + { + rand.NextBytes(buffer); + task = compressor.WriteAsync(buffer, 0, buffer.Length); + writesBeingFlushed++; + } + + // WriteAsync will be blocked on writing the output to the underlying stream. Calling Dispose will trigger a Finish call with unwritten output + // still available. + Assert.InRange(writeStream.Length, 0, buffer.Length); + compressor.Dispose(); + Assert.InRange(writeStream.Length, 0, buffer.Length * writesBeingFlushed); + Assert.False(task.IsCompleted); + } + finally + { + // Unblock Async operations + writeStream.manualResetEvent.Set(); + // WriteAsync call will return to the compression stream's WriteAsync which will attempt to + // access members of the now disposed stream. + Assert.Throws(() => task.Wait(1000)); + } + } + } + + [Fact] + public virtual async Task Dispose_WithUnfinishedReadAsync() + { + string compressedPath = CompressedTestFile(UncompressedTestFile()); + int uncompressedSize = (int)new FileInfo(UncompressedTestFile()).Length; + byte[] uncompressedBytes = new byte[uncompressedSize]; + + using (var readStream = await ManualSyncMemoryStream.GetStreamFromFileAsync(compressedPath, false)) + { + var decompressor = CreateStream(readStream, CompressionMode.Decompress, true); + Task task = decompressor.ReadAsync(uncompressedBytes, 0, uncompressedBytes.Length); + decompressor.Dispose(); + readStream.manualResetEvent.Set(); + Assert.Throws(() => task.Wait(1000)); + } + } + + [Theory] + [MemberData(nameof(UncompressedTestFiles))] + public async Task Read(string testFile) + { + var uncompressedStream = await LocalMemoryStream.readAppFileAsync(testFile); + var compressedStream = await LocalMemoryStream.readAppFileAsync(CompressedTestFile(testFile)); + var decompressor = CreateStream(compressedStream, CompressionMode.Decompress); + var decompressorOutput = new MemoryStream(); + + int _bufferSize = 1024; + var bytes = new byte[_bufferSize]; + bool finished = false; + int retCount; + while (!finished) + { + retCount = await decompressor.ReadAsync(bytes, 0, _bufferSize); + + if (retCount != 0) + await decompressorOutput.WriteAsync(bytes, 0, retCount); + else + finished = true; + } + decompressor.Dispose(); + decompressorOutput.Position = 0; + uncompressedStream.Position = 0; + + byte[] uncompressedStreamBytes = uncompressedStream.ToArray(); + byte[] decompressorOutputBytes = decompressorOutput.ToArray(); + + Assert.Equal(uncompressedStreamBytes.Length, decompressorOutputBytes.Length); + for (int i = 0; i < uncompressedStreamBytes.Length; i++) + { + Assert.Equal(uncompressedStreamBytes[i], decompressorOutputBytes[i]); + } + } + + [Fact] + public async Task Read_EndOfStreamPosition() + { + var compressedStream = await LocalMemoryStream.readAppFileAsync(CompressedTestFile(UncompressedTestFile())); + int compressedEndPosition = (int)compressedStream.Length; + var rand = new Random(1024); + int _bufferSize = BufferSize * 2 - 568; + var bytes = new byte[_bufferSize]; + rand.NextBytes(bytes); + compressedStream.Position = compressedEndPosition; + compressedStream.Write(bytes, 0, _bufferSize); + compressedStream.Write(bytes, 0, _bufferSize); + compressedStream.Position = 0; + var decompressor = CreateStream(compressedStream, CompressionMode.Decompress); + + while (decompressor.Read(bytes, 0, _bufferSize) > 0); + Assert.Equal(((compressedEndPosition / BufferSize) + 1) * BufferSize, compressedStream.Position); + } + + [Fact] + public async Task Read_BaseStreamSlowly() + { + string testFile = UncompressedTestFile(); + var uncompressedStream = await LocalMemoryStream.readAppFileAsync(testFile); + var compressedStream = new BadWrappedStream(BadWrappedStream.Mode.ReadSlowly, File.ReadAllBytes(CompressedTestFile(testFile))); + var decompressor = CreateStream(compressedStream, CompressionMode.Decompress); + var decompressorOutput = new MemoryStream(); + + int _bufferSize = 1024; + var bytes = new byte[_bufferSize]; + bool finished = false; + int retCount; + while (!finished) + { + retCount = await decompressor.ReadAsync(bytes, 0, _bufferSize); + + if (retCount != 0) + await decompressorOutput.WriteAsync(bytes, 0, retCount); + else + finished = true; + } + decompressor.Dispose(); + decompressorOutput.Position = 0; + uncompressedStream.Position = 0; + + byte[] uncompressedStreamBytes = uncompressedStream.ToArray(); + byte[] decompressorOutputBytes = decompressorOutput.ToArray(); + + Assert.Equal(uncompressedStreamBytes.Length, decompressorOutputBytes.Length); + for (int i = 0; i < uncompressedStreamBytes.Length; i++) + { + Assert.Equal(uncompressedStreamBytes[i], decompressorOutputBytes[i]); + } + } + + [Theory] + [InlineData(CompressionMode.Compress)] + [InlineData(CompressionMode.Decompress)] + public void CanReadCanWrite(CompressionMode mode) + { + var ms = new MemoryStream(); + var compressor = CreateStream(ms, mode); + Assert.True(mode == CompressionMode.Compress ? compressor.CanWrite : compressor.CanRead); + + compressor.Dispose(); + Assert.False(compressor.CanRead); + Assert.False(compressor.CanWrite); + } + + [Theory] + [InlineData(CompressionMode.Compress)] + [InlineData(CompressionMode.Decompress)] + public void CanDisposeBaseStream(CompressionMode mode) + { + var ms = new MemoryStream(); + var compressor = CreateStream(ms, mode); + ms.Dispose(); // This would throw if this was invalid + } + + [Theory] + [InlineData(CompressionMode.Compress)] + [InlineData(CompressionMode.Decompress)] + public void Ctor_NullStream(CompressionMode mode) + { + Assert.Throws(() => CreateStream(null, mode)); + } + + [Theory] + [InlineData(CompressionMode.Compress)] + [InlineData(CompressionMode.Decompress)] + public void Ctor_DisposedBaseStream(CompressionMode mode) + { + MemoryStream ms = new MemoryStream(); + ms.Dispose(); + AssertExtensions.Throws("stream", () => CreateStream(ms, mode)); + } + + [Theory] + [InlineData(CompressionMode.Compress)] + [InlineData(CompressionMode.Decompress)] + public void Ctor_InvalidStreamCanReadCanWrite(CompressionMode mode) + { + LocalMemoryStream ms = new LocalMemoryStream(); + ms.SetCanRead(mode == CompressionMode.Compress); + ms.SetCanWrite(mode == CompressionMode.Decompress); + + AssertExtensions.Throws("stream", () => CreateStream(ms, mode)); + } + + public IEnumerable> CtorFunctions() + { + CompressionLevel[] legalValues = new CompressionLevel[] { CompressionLevel.Optimal, CompressionLevel.Fastest, CompressionLevel.NoCompression }; + yield return new Func((stream) => CreateStream(stream, CompressionMode.Compress)); + + foreach (CompressionLevel level in legalValues) + { + yield return new Func((stream) => CreateStream(stream, level)); + + bool[] boolValues = new bool[] { true, false }; + + foreach (bool remainsOpen in boolValues) + { + yield return new Func((stream) => CreateStream(stream, level, remainsOpen)); + } + } + } + + [Fact] + public void TestCompressCtor() + { + Assert.All(CtorFunctions(), (create) => + { + //Create the Stream + int _bufferSize = 1024; + var bytes = new byte[_bufferSize]; + var baseStream = new MemoryStream(bytes, writable: true); + Stream compressor = create(baseStream); + + //Write some data and Close the stream + string strData = "Test Data"; + var encoding = Encoding.UTF8; + byte[] data = encoding.GetBytes(strData); + compressor.Write(data, 0, data.Length); + compressor.Flush(); + compressor.Dispose(); + baseStream.Dispose(); + + //Read the data + byte[] data2 = new byte[_bufferSize]; + baseStream = new MemoryStream(bytes, writable: false); + var decompressor = CreateStream(baseStream, CompressionMode.Decompress); + int size = decompressor.Read(data2, 0, _bufferSize - 5); + + //Verify the data roundtripped + for (int i = 0; i < size + 5; i++) + { + if (i < data.Length) + { + Assert.Equal(data[i], data2[i]); + } + else + { + Assert.Equal(data2[i], (byte)0); + } + } + }); + } + + [Theory] + [InlineData(CompressionMode.Compress)] + [InlineData(CompressionMode.Decompress)] + public void TestLeaveOpen(CompressionMode mode) + { + //Create the Stream + var baseStream = new MemoryStream(); + Stream compressor = CreateStream(baseStream, mode, leaveOpen: false); + compressor.Dispose(); + + //Check that Close has really closed the underlying stream + Assert.Throws(() => baseStream.Write(new byte[] { }, 0, 0)); + } + + [Fact] + public void TestLeaveOpenAfterValidCompress() + { + //Create the Stream + int _bufferSize = 1024; + var bytes = new byte[_bufferSize]; + var baseStream = new MemoryStream(bytes, writable: true); + Stream compressor = CreateStream(baseStream, CompressionMode.Compress, leaveOpen: false); + + //Write some data and Close the stream + string strData = "Test Data"; + var encoding = Encoding.UTF8; + byte[] data = encoding.GetBytes(strData); + compressor.Write(data, 0, data.Length); + compressor.Flush(); + compressor.Dispose(); + + //Check that Close has really closed the underlying stream + Assert.Throws(() => baseStream.Write(bytes, 0, bytes.Length)); + } + + [Fact] + public async void TestLeaveOpenAfterValidDecompress() + { + //Create the Stream + int _bufferSize = 1024; + var bytes = new byte[_bufferSize]; + Stream compressedStream = await LocalMemoryStream.readAppFileAsync(CompressedTestFile(UncompressedTestFile())); + Stream decompressor = CreateStream(compressedStream, CompressionMode.Decompress, leaveOpen: false); + + //Read some data and Close the stream + decompressor.Read(bytes, 0, _bufferSize); + decompressor.Flush(); + decompressor.Dispose(); + + //Check that Close has really closed the underlying stream + Assert.Throws(() => compressedStream.Read(bytes, 0, bytes.Length)); + } + + [Fact] + public void Ctor_ArgumentValidation() + { + Assert.Throws(() => CreateStream(null, CompressionLevel.Fastest)); + Assert.Throws(() => CreateStream(null, CompressionMode.Decompress)); + Assert.Throws(() => CreateStream(null, CompressionMode.Compress)); + + Assert.Throws(() => CreateStream(null, CompressionLevel.Fastest, true)); + Assert.Throws(() => CreateStream(null, CompressionMode.Decompress, false)); + Assert.Throws(() => CreateStream(null, CompressionMode.Compress, true)); + + AssertExtensions.Throws("mode", () => CreateStream(new MemoryStream(), (CompressionMode)42)); + AssertExtensions.Throws("mode", () => CreateStream(new MemoryStream(), (CompressionMode)43, true)); + + AssertExtensions.Throws("stream", () => CreateStream(new MemoryStream(new byte[1], writable: false), CompressionLevel.Optimal)); + } + + [Theory] + [InlineData(CompressionMode.Compress)] + [InlineData(CompressionMode.Decompress)] + public async Task Flush(CompressionMode mode) + { + var ms = new MemoryStream(); + var compressor = CreateStream(ms, mode); + compressor.Flush(); + await compressor.FlushAsync(); + } + + [Theory] + [InlineData(CompressionMode.Compress)] + [InlineData(CompressionMode.Decompress)] + public void Flush_Double(CompressionMode mode) + { + var ms = new MemoryStream(); + var compressor = CreateStream(ms, mode); + compressor.Flush(); + compressor.Flush(); + } + + [Theory] + [InlineData(CompressionMode.Compress)] + [InlineData(CompressionMode.Decompress)] + public void Dispose_Double(CompressionMode mode) + { + var ms = new MemoryStream(); + var compressor = CreateStream(ms, mode); + compressor.Dispose(); + compressor.Dispose(); + } + + [Theory] + [InlineData(CompressionMode.Compress)] + [InlineData(CompressionMode.Decompress)] + public void Flush_FollowedByDispose(CompressionMode mode) + { + var ms = new MemoryStream(); + var compressor = CreateStream(ms, mode); + compressor.Flush(); + compressor.Dispose(); + } + + [Theory] + [InlineData(CompressionMode.Compress)] + [InlineData(CompressionMode.Decompress)] + public void Dispose_FollowedBySyncOperations(CompressionMode mode) + { + var ms = new MemoryStream(); + var compressor = CreateStream(ms, mode); + compressor.Dispose(); + + if (mode == CompressionMode.Compress) + Assert.Throws(() => compressor.Write(new byte[1], 0, 1)); + else + Assert.Throws(() => compressor.Read(new byte[1], 0, 1)); + Assert.Throws(() => compressor.Flush()); + Assert.Throws(() => compressor.CopyTo(new MemoryStream())); + } + + [Theory] + [InlineData(CompressionMode.Compress)] + [InlineData(CompressionMode.Decompress)] + public virtual async Task Dispose_FollowedByAsyncOperations(CompressionMode mode) + { + var ms = new MemoryStream(); + var compressor = CreateStream(ms, mode); + compressor.Dispose(); + + if (mode == CompressionMode.Compress) + await Assert.ThrowsAsync(async () => await compressor.WriteAsync(new byte[1], 0, 1)); + else + await Assert.ThrowsAsync(async () => await compressor.ReadAsync(new byte[1], 0, 1)); + await Assert.ThrowsAsync(async () => await compressor.FlushAsync()); + await Assert.ThrowsAsync(async () => await compressor.CopyToAsync(new MemoryStream())); + } + + [Theory] + [InlineData(CompressionMode.Compress)] + [InlineData(CompressionMode.Decompress)] + public void TestSeekMethods(CompressionMode mode) + { + var ms = new MemoryStream(); + var decompressor = CreateStream(ms, mode); + Assert.False(decompressor.CanSeek, "CanSeek should be false"); + Assert.Throws(() => decompressor.Length); + Assert.Throws(() => decompressor.SetLength(1)); + Assert.Throws(() => decompressor.Position); + Assert.Throws(() => decompressor.Position = 100L); + Assert.Throws(() => decompressor.Seek(100L, SeekOrigin.Begin)); + } + + [Theory] + [InlineData(true)] + [InlineData(true)] + public virtual void Write_ArgumentValidation(bool useAsync) + { + using (var decompressor = CreateStream(new MemoryStream(), CompressionMode.Compress)) + { + Assert.Throws(() => { if (useAsync) { decompressor.WriteAsync(null, 0, 0).Wait(); } else { decompressor.Write(null, 0, 0); } }); + Assert.Throws(() => { if (useAsync) { decompressor.WriteAsync(new byte[1], -1, 0).Wait(); } else { decompressor.Write(new byte[1], -1, 0); } }); + Assert.Throws(() => { if (useAsync) { decompressor.WriteAsync(new byte[1], 0, -1).Wait(); } else { decompressor.Write(new byte[1], 0, -1); } }); + Assert.Throws(null, () => { if (useAsync) { decompressor.WriteAsync(new byte[1], 0, 2).Wait(); } else { decompressor.Write(new byte[1], 0, 2); } }); + Assert.Throws(null, () => { if (useAsync) { decompressor.WriteAsync(new byte[1], 1, 1).Wait(); } else { decompressor.Write(new byte[1], 1, 1); } }); + Assert.Throws(() => useAsync ? decompressor.ReadAsync(new byte[1], 0, 1).Result : decompressor.Read(new byte[1], 0, 1)); + Assert.Throws(null, () => useAsync ? decompressor.ReadAsync(new byte[1], 1, 1).Result : decompressor.Read(new byte[1], 1, 1)); + if (useAsync) + { decompressor.WriteAsync(new byte[1], 0, 1).Wait(); } + else + { decompressor.Write(new byte[1], 0, 1); } + } + } + + [Theory] + [InlineData(true)] + [InlineData(true)] + public virtual void Read_ArgumentValidation(bool useAsync) + { + using (var decompressor = CreateStream(new MemoryStream(), CompressionMode.Decompress)) + { + Assert.Throws(() => useAsync ? decompressor.ReadAsync(null, 0, 0).Result : decompressor.Read(null, 0, 0)); + Assert.Throws(() => useAsync ? decompressor.ReadAsync(new byte[1], -1, 0).Result : decompressor.Read(new byte[1], -1, 0)); + Assert.Throws(() => useAsync ? decompressor.ReadAsync(new byte[1], 0, -1).Result : decompressor.Read(new byte[1], 0, -1)); + AssertExtensions.Throws(null, () => useAsync ? decompressor.ReadAsync(new byte[1], 0, 2).Result : decompressor.Read(new byte[1], 0, 2)); + AssertExtensions.Throws(null, () => useAsync ? decompressor.ReadAsync(new byte[1], 1, 1).Result : decompressor.Read(new byte[1], 1, 1)); + Assert.Throws(() => { if (useAsync) { decompressor.WriteAsync(new byte[1], 0, 1).Wait(); } else { decompressor.Write(new byte[1], 0, 1); } }); + Assert.Throws(null, () => { if (useAsync) { decompressor.WriteAsync(new byte[1], 1, 1).Wait(); } else { decompressor.Write(new byte[1], 1, 1); } }); + + var data = new byte[1] { 42 }; + Assert.Equal(0, useAsync ? decompressor.ReadAsync(data, 0, 0).Result : decompressor.Read(data, 0, 0)); + Assert.Equal(42, data[0]); + } + } + + [Theory] + [InlineData(CompressionMode.Compress)] + [InlineData(CompressionMode.Decompress)] + public void CopyToAsync_ArgumentValidation(CompressionMode mode) + { + using (Stream compressor = CreateStream(new MemoryStream(), mode)) + { + AssertExtensions.Throws("destination", () => { compressor.CopyToAsync(null); }); + AssertExtensions.Throws("bufferSize", () => { compressor.CopyToAsync(new MemoryStream(), 0); }); + Assert.Throws(() => { compressor.CopyToAsync(new MemoryStream(new byte[1], writable: false)); }); + compressor.Dispose(); + Assert.Throws(() => { compressor.CopyToAsync(new MemoryStream()); }); + } + } + + public enum ReadWriteMode + { + SyncArray, + SyncSpan, + AsyncArray, + AsyncMemory, + AsyncBeginEnd + } + + public static IEnumerable RoundtripCompressDecompressOuterData + { + get + { + foreach (ReadWriteMode readWriteMode in new[] { ReadWriteMode.SyncArray, ReadWriteMode.SyncSpan, ReadWriteMode.AsyncArray, ReadWriteMode.AsyncMemory, ReadWriteMode.AsyncBeginEnd }) + { + foreach (var level in new[] { CompressionLevel.Fastest, CompressionLevel.Optimal, CompressionLevel.NoCompression }) // compression level + { + yield return new object[] { readWriteMode, 1, 5, level }; // smallest possible writes + yield return new object[] { readWriteMode, 1023, 1023 * 10, level }; // overflowing internal buffer + yield return new object[] { readWriteMode, 1024 * 1024, 1024 * 1024, level }; // large single write + } + } + } + } + + [Fact] + public async Task CompressDecompress_RoundTrip() + { + await CompressDecompress_RoundTrip(ReadWriteMode.SyncArray, chunkSize: 1, totalSize: 10, level: CompressionLevel.Fastest); + await CompressDecompress_RoundTrip(ReadWriteMode.SyncSpan, chunkSize: 1, totalSize: 10, level: CompressionLevel.Fastest); + await CompressDecompress_RoundTrip(ReadWriteMode.AsyncArray, chunkSize: 1, totalSize: 10, level: CompressionLevel.Fastest); + await CompressDecompress_RoundTrip(ReadWriteMode.AsyncMemory, chunkSize: 1, totalSize: 10, level: CompressionLevel.Fastest); + await CompressDecompress_RoundTrip(ReadWriteMode.AsyncBeginEnd, chunkSize: 1, totalSize: 10, level: CompressionLevel.Fastest); + await CompressDecompress_RoundTrip(ReadWriteMode.AsyncArray, chunkSize: 1024, totalSize: 8192, level: CompressionLevel.Optimal); + } + + [OuterLoop] + [Theory] + [MemberData(nameof(RoundtripCompressDecompressOuterData))] + public async Task CompressDecompress_RoundTrip(ReadWriteMode readWriteMode, int chunkSize, int totalSize, CompressionLevel level) + { + byte[] data = new byte[totalSize]; + new Random(42).NextBytes(data); + + var compressed = new MemoryStream(); + using (var compressor = CreateStream(compressed, level, true)) + { + for (int i = 0; i < data.Length; i += chunkSize) // not using CopyTo{Async} due to optimizations in MemoryStream's implementation that avoid what we're trying to test + { + switch (readWriteMode) + { + case ReadWriteMode.AsyncArray: + await compressor.WriteAsync(data, i, chunkSize); + break; + case ReadWriteMode.SyncArray: + compressor.Write(data, i, chunkSize); + break; + case ReadWriteMode.SyncSpan: + compressor.Write(new ReadOnlySpan(data, i, chunkSize)); + break; + case ReadWriteMode.AsyncMemory: + await compressor.WriteAsync(new ReadOnlyMemory(data, i, chunkSize)); + break; + case ReadWriteMode.AsyncBeginEnd: + await Task.Factory.FromAsync(compressor.BeginWrite, compressor.EndWrite, data, i, chunkSize, null); + break; + } + } + } + compressed.Position = 0; + await ReadAndValidateCompressedData(readWriteMode, chunkSize, compressed, data); + compressed.Dispose(); + } + + [Fact] + public async Task Flush_RoundTrip() + { + if (FlushNoOps) + return; + await Flush_RoundTrip(ReadWriteMode.SyncArray, chunkSize: 1, totalSize: 10, level: CompressionLevel.Fastest); + await Flush_RoundTrip(ReadWriteMode.AsyncArray, chunkSize: 1024, totalSize: 8192, level: CompressionLevel.Optimal); + } + + [OuterLoop] + [Theory] + [MemberData(nameof(RoundtripCompressDecompressOuterData))] + public async Task Flush_RoundTrip(ReadWriteMode readWriteMode, int chunkSize, int totalSize, CompressionLevel level) + { + if (FlushNoOps) + return; + byte[] data = new byte[totalSize]; + new Random(42).NextBytes(data); + + using (var compressed = new MemoryStream()) + using (var compressor = CreateStream(compressed, level, true)) + { + for (int i = 0; i < data.Length; i += chunkSize) // not using CopyTo{Async} due to optimizations in MemoryStream's implementation that avoid what we're trying to test + { + switch (readWriteMode) + { + case ReadWriteMode.AsyncArray: + await compressor.WriteAsync(data, i, chunkSize); + break; + case ReadWriteMode.SyncArray: + compressor.Write(data, i, chunkSize); + break; + case ReadWriteMode.SyncSpan: + compressor.Write(new ReadOnlySpan(data, i, chunkSize)); + break; + case ReadWriteMode.AsyncMemory: + await compressor.WriteAsync(new ReadOnlyMemory(data, i, chunkSize)); + break; + case ReadWriteMode.AsyncBeginEnd: + await Task.Factory.FromAsync(compressor.BeginWrite, compressor.EndWrite, data, i, chunkSize, null); + break; + } + } + switch (readWriteMode) + { + case ReadWriteMode.AsyncArray: + case ReadWriteMode.AsyncMemory: + case ReadWriteMode.AsyncBeginEnd: + await compressor.FlushAsync(); + break; + case ReadWriteMode.SyncSpan: + case ReadWriteMode.SyncArray: + compressor.Flush(); + break; + } + if (!FlushCompletes) + compressor.Dispose(); + compressed.Position = 0; + await ReadAndValidateCompressedData(readWriteMode, chunkSize, compressed, data); + } + } + + [Fact] + public async Task Flush_Consecutive() + { + if (FlushNoOps) + return; + await Flush_Consecutive(ReadWriteMode.SyncArray, chunkSize: 1, totalSize: 10, level: CompressionLevel.Fastest); + await Flush_Consecutive(ReadWriteMode.AsyncArray, chunkSize: 1024, totalSize: 8192, level: CompressionLevel.Optimal); + } + + [OuterLoop] + [Theory] + [MemberData(nameof(RoundtripCompressDecompressOuterData))] + public async Task Flush_Consecutive(ReadWriteMode readWriteMode, int chunkSize, int totalSize, CompressionLevel level) + { + if (FlushNoOps) + return; + byte[] data = new byte[totalSize]; + List expected = new List(); + new Random(42).NextBytes(data); + + using (var compressed = new MemoryStream()) + using (var compressor = CreateStream(compressed, level, true)) + { + for (int i = 0; i < data.Length; i += chunkSize) // not using CopyTo{Async} due to optimizations in MemoryStream's implementation that avoid what we're trying to test + { + switch (readWriteMode) + { + case ReadWriteMode.AsyncArray: + await compressor.WriteAsync(data, i, chunkSize); + break; + case ReadWriteMode.SyncArray: + compressor.Write(data, i, chunkSize); + break; + case ReadWriteMode.SyncSpan: + compressor.Write(new ReadOnlySpan(data, i, chunkSize)); + break; + case ReadWriteMode.AsyncMemory: + await compressor.WriteAsync(new ReadOnlyMemory(data, i, chunkSize)); + break; + case ReadWriteMode.AsyncBeginEnd: + await Task.Factory.FromAsync(compressor.BeginWrite, compressor.EndWrite, data, i, chunkSize, null); + break; + } + for (int j = i; j < i + chunkSize; j++) + expected.Insert(j, data[j]); + + switch (readWriteMode) + { + case ReadWriteMode.AsyncArray: + case ReadWriteMode.AsyncMemory: + case ReadWriteMode.AsyncBeginEnd: + await compressor.FlushAsync(); + break; + case ReadWriteMode.SyncSpan: + case ReadWriteMode.SyncArray: + compressor.Flush(); + break; + } + + if (FlushCompletes) + { + MemoryStream partiallyCompressed = new MemoryStream(compressed.ToArray()); + partiallyCompressed.Position = 0; + await ReadAndValidateCompressedData(readWriteMode, chunkSize, partiallyCompressed, expected.ToArray()); + } + } + if (!FlushCompletes) + compressor.Dispose(); + MemoryStream fullyCompressed = new MemoryStream(compressed.ToArray()); + fullyCompressed.Position = 0; + await ReadAndValidateCompressedData(readWriteMode, chunkSize, fullyCompressed, expected.ToArray()); + } + } + + [Fact] + public async Task Flush_BeforeFirstWrites() + { + if (FlushNoOps) + return; + await Flush_BeforeFirstWrites(ReadWriteMode.SyncArray, chunkSize: 1, totalSize: 10, level: CompressionLevel.Fastest); + await Flush_BeforeFirstWrites(ReadWriteMode.AsyncArray, chunkSize: 1024, totalSize: 8192, level: CompressionLevel.Optimal); + } + + [OuterLoop] + [Theory] + [MemberData(nameof(RoundtripCompressDecompressOuterData))] + public async Task Flush_BeforeFirstWrites(ReadWriteMode readWriteMode, int chunkSize, int totalSize, CompressionLevel level) + { + if (FlushNoOps) + return; + byte[] data = new byte[totalSize]; + new Random(42).NextBytes(data); + + using (var compressed = new MemoryStream()) + using (var compressor = CreateStream(compressed, level, true)) + { + switch (readWriteMode) + { + case ReadWriteMode.AsyncArray: + case ReadWriteMode.AsyncMemory: + case ReadWriteMode.AsyncBeginEnd: + await compressor.FlushAsync(); + break; + case ReadWriteMode.SyncSpan: + case ReadWriteMode.SyncArray: + compressor.Flush(); + break; + } + + for (int i = 0; i < data.Length; i += chunkSize) // not using CopyTo{Async} due to optimizations in MemoryStream's implementation that avoid what we're trying to test + { + switch (readWriteMode) + { + case ReadWriteMode.AsyncArray: + await compressor.WriteAsync(data, i, chunkSize); + break; + case ReadWriteMode.SyncArray: + compressor.Write(data, i, chunkSize); + break; + case ReadWriteMode.SyncSpan: + compressor.Write(new ReadOnlySpan(data, i, chunkSize)); + break; + case ReadWriteMode.AsyncMemory: + await compressor.WriteAsync(new ReadOnlyMemory(data, i, chunkSize)); + break; + case ReadWriteMode.AsyncBeginEnd: + await Task.Factory.FromAsync(compressor.BeginWrite, compressor.EndWrite, data, i, chunkSize, null); + break; + } + } + + switch (readWriteMode) + { + case ReadWriteMode.AsyncArray: + case ReadWriteMode.AsyncMemory: + case ReadWriteMode.AsyncBeginEnd: + await compressor.FlushAsync(); + break; + case ReadWriteMode.SyncSpan: + case ReadWriteMode.SyncArray: + compressor.Flush(); + break; + } + if (!FlushCompletes) + compressor.Dispose(); + compressed.Position = 0; + await ReadAndValidateCompressedData(readWriteMode, chunkSize, compressed, data); + } + } + + /// + /// Given a MemoryStream of compressed data and a byte array of desired output, decompresses + /// the stream and validates that it is equal to the expected array. + /// + private async Task ReadAndValidateCompressedData(ReadWriteMode readWriteMode, int chunkSize, MemoryStream compressed, byte[] expected) + { + using (MemoryStream decompressed = new MemoryStream()) + using (Stream decompressor = CreateStream(compressed, CompressionMode.Decompress, true)) + { + int bytesRead; + var buffer = new byte[chunkSize]; + switch (readWriteMode) + { + case ReadWriteMode.SyncSpan: + while ((bytesRead = decompressor.Read(new Span(buffer))) != 0) + { + decompressed.Write(buffer, 0, bytesRead); + } + break; + case ReadWriteMode.SyncArray: + while ((bytesRead = decompressor.Read(buffer, 0, buffer.Length)) != 0) + { + decompressed.Write(buffer, 0, bytesRead); + } + break; + case ReadWriteMode.AsyncArray: + while ((bytesRead = await decompressor.ReadAsync(buffer, 0, buffer.Length)) != 0) + { + decompressed.Write(buffer, 0, bytesRead); + } + break; + case ReadWriteMode.AsyncMemory: + while ((bytesRead = await decompressor.ReadAsync(new Memory(buffer))) != 0) + { + decompressed.Write(buffer, 0, bytesRead); + } + break; + case ReadWriteMode.AsyncBeginEnd: + while ((bytesRead = await Task.Factory.FromAsync(decompressor.BeginRead, decompressor.EndRead, buffer, 0, buffer.Length, null)) != 0) + { + decompressed.Write(buffer, 0, bytesRead); + } + break; + } + Assert.Equal(expected, decompressed.ToArray()); + } + } + + [Theory] + [InlineData(ReadWriteMode.SyncArray)] + [InlineData(ReadWriteMode.AsyncArray)] + [InlineData(ReadWriteMode.SyncSpan)] + [InlineData(ReadWriteMode.AsyncMemory)] + [InlineData(ReadWriteMode.AsyncBeginEnd)] + public async Task Read_SequentialReadsOnMemoryStream_Return_SameBytes(ReadWriteMode readWriteMode) + { + byte[] data = new byte[1024 * 10]; + new Random(42).NextBytes(data); + + var compressed = new MemoryStream(); + using (var compressor = CreateStream(compressed, CompressionMode.Compress, true)) + { + for (int i = 0; i < data.Length; i += 1024) + { + switch (readWriteMode) + { + case ReadWriteMode.SyncArray: + compressor.Write(data, i, 1024); + break; + case ReadWriteMode.AsyncArray: + await compressor.WriteAsync(data, i, 1024); + break; + case ReadWriteMode.SyncSpan: + compressor.Write(new Span(data, i, 1024)); + break; + case ReadWriteMode.AsyncMemory: + await compressor.WriteAsync(new ReadOnlyMemory(data, i, 1024)); + break; + case ReadWriteMode.AsyncBeginEnd: + await Task.Factory.FromAsync(compressor.BeginWrite, compressor.EndWrite, data, i, 1024, null); + break; + } + } + } + compressed.Position = 0; + + using (var decompressor = CreateStream(compressed, CompressionMode.Decompress, true)) + { + int i, j; + byte[] array = new byte[100]; + byte[] array2 = new byte[100]; + + // only read in the first 100 bytes + switch (readWriteMode) + { + case ReadWriteMode.SyncArray: + decompressor.Read(array, 0, array.Length); + break; + case ReadWriteMode.AsyncArray: + await decompressor.ReadAsync(array, 0, array.Length); + break; + case ReadWriteMode.SyncSpan: + decompressor.Read(new Span(array)); + break; + case ReadWriteMode.AsyncMemory: + await decompressor.ReadAsync(new Memory(array)); + break; + case ReadWriteMode.AsyncBeginEnd: + await Task.Factory.FromAsync(decompressor.BeginRead, decompressor.EndRead, array, 0, array.Length, null); + break; + } + for (i = 0; i < array.Length; i++) + { + Assert.Equal(data[i], array[i]); + } + + // read in the next 100 bytes and make sure nothing is missing + switch (readWriteMode) + { + case ReadWriteMode.SyncArray: + decompressor.Read(array2, 0, array2.Length); + break; + case ReadWriteMode.AsyncArray: + await decompressor.ReadAsync(array2, 0, array2.Length); + break; + case ReadWriteMode.SyncSpan: + decompressor.Read(new Span(array2)); + break; + case ReadWriteMode.AsyncMemory: + await decompressor.ReadAsync(new Memory(array2)); + break; + case ReadWriteMode.AsyncBeginEnd: + await Task.Factory.FromAsync(decompressor.BeginRead, decompressor.EndRead, array2, 0, array2.Length, null); + break; + } + for (j = 0; j < array2.Length; j++) + { + Assert.Equal(data[j], array[j]); + } + } + } + + [Fact] + public void WriteByte_RountTrip() + { + byte[] data = new byte[1024 * 10]; + new Random(42).NextBytes(data); + + var compressed = new MemoryStream(); + using (var compressor = CreateStream(compressed, CompressionMode.Compress, true)) + { + compressor.Write(data, 0, data.Length); + } + compressed.Position = 0; + + using (var decompressor = CreateStream(compressed, CompressionMode.Decompress, true)) + { + for (int i = 0; i < data.Length; i++) + Assert.Equal(data[i], decompressor.ReadByte()); + } + } + + [Fact] + public async Task WrapNullReturningTasksStream() + { + using (var decompressor = CreateStream(new BadWrappedStream(BadWrappedStream.Mode.ReturnNullTasks), CompressionMode.Decompress)) + Assert.Equal(0, await decompressor.ReadAsync(new byte[1024], 0, 1024)); + } + + [Fact] + public async Task WrapStreamReturningBadReadValues() + { + using (var decompressor = CreateStream(new BadWrappedStream(BadWrappedStream.Mode.ReturnTooLargeCounts), CompressionMode.Decompress)) + Assert.Throws(() => decompressor.Read(new byte[1024], 0, 1024)); + using (var decompressor = CreateStream(new BadWrappedStream(BadWrappedStream.Mode.ReturnTooLargeCounts), CompressionMode.Decompress)) + await Assert.ThrowsAsync(() => decompressor.ReadAsync(new byte[1024], 0, 1024)); + using (var decompressor = CreateStream(new BadWrappedStream(BadWrappedStream.Mode.ReturnTooLargeCounts), CompressionMode.Decompress)) + await Assert.ThrowsAsync(async () => { await decompressor.ReadAsync(new Memory(new byte[1024])); }); + + if (!FlushNoOps) + { + using (var decompressor = CreateStream(new BadWrappedStream(BadWrappedStream.Mode.ReturnTooSmallCounts), CompressionMode.Decompress)) + Assert.Equal(0, decompressor.Read(new byte[1024], 0, 1024)); + using (var decompressor = CreateStream(new BadWrappedStream(BadWrappedStream.Mode.ReturnTooSmallCounts), CompressionMode.Decompress)) + Assert.Equal(0, await decompressor.ReadAsync(new byte[1024], 0, 1024)); + using (var decompressor = CreateStream(new BadWrappedStream(BadWrappedStream.Mode.ReturnTooSmallCounts), CompressionMode.Decompress)) + Assert.Equal(0, await decompressor.ReadAsync(new Memory(new byte[1024]))); + } + } + + [Theory] + [OuterLoop] + [InlineData(true)] + [InlineData(false)] + public async Task CopyTo_Roundtrip_OutputMatchesInput(bool useAsync) + { + var rand = new Random(); + foreach (int dataSize in new[] { 1, 1024, 4095, 1024 * 1024 }) + { + var data = new byte[dataSize]; + rand.NextBytes(data); + + var compressed = new MemoryStream(); + using (var ds = CreateStream(compressed, CompressionMode.Compress, leaveOpen: true)) + { + ds.Write(data, 0, data.Length); + } + byte[] compressedData = compressed.ToArray(); + + foreach (int copyBufferSize in new[] { 1, 4096, 80 * 1024 }) + { + // Memory source + var m = new MemoryStream(compressedData, writable: false); + await CopyTo_Roundtrip_OutputMatchesInput_Verify(data, copyBufferSize, m, useAsync); + + // File sources, sync and async + foreach (bool useAsyncFileOptions in new[] { true, false }) + { + string path = Path.GetTempFileName(); + File.WriteAllBytes(path, compressedData); + + FileOptions options = FileOptions.DeleteOnClose; + if (useAsyncFileOptions) + options |= FileOptions.Asynchronous; + await CopyTo_Roundtrip_OutputMatchesInput_Verify(data, copyBufferSize, new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x1000, options), useAsync); + } + } + } + } + + private async Task CopyTo_Roundtrip_OutputMatchesInput_Verify(byte[] expectedDecrypted, int copyBufferSize, Stream source, bool useAsync) + { + var m = new MemoryStream(); + using (Stream ds = CreateStream(source, CompressionMode.Decompress)) + { + if (useAsync) + await ds.CopyToAsync(m); + else + ds.CopyTo(m, copyBufferSize); + } + Assert.Equal(expectedDecrypted, m.ToArray()); + } + + [Theory] + [InlineData(CompressionMode.Compress)] + [InlineData(CompressionMode.Decompress)] + public void BaseStream(CompressionMode mode) + { + using (var baseStream = new MemoryStream()) + using (var compressor = CreateStream(baseStream, mode)) + { + Assert.Same(BaseStream(compressor), baseStream); + } + } + + [Theory] + [InlineData(CompressionMode.Compress)] + [InlineData(CompressionMode.Decompress)] + public async Task BaseStream_Modify(CompressionMode mode) + { + using (var baseStream = await LocalMemoryStream.readAppFileAsync(CompressedTestFile(UncompressedTestFile()))) + using (var compressor = CreateStream(baseStream, mode)) + { + int size = 1024; + byte[] bytes = new byte[size]; + if (mode == CompressionMode.Compress) + BaseStream(compressor).Write(bytes, 0, size); // This will throw if the underlying stream is not writable as expected + else + BaseStream(compressor).Read(bytes, 0, size); // This will throw if the underlying stream is not readable as expected + } + } + + [Theory] + [InlineData(CompressionMode.Compress)] + [InlineData(CompressionMode.Decompress)] + public void BaseStream_NullAfterDisposeWithFalseLeaveOpen(CompressionMode mode) + { + var ms = new MemoryStream(); + var compressor = CreateStream(ms, mode); + compressor.Dispose(); + + Assert.Null(BaseStream(compressor)); + + compressor.Dispose(); // Should be a no-op + } + + [Theory] + [InlineData(CompressionMode.Compress)] + [InlineData(CompressionMode.Decompress)] + public async Task BaseStream_ValidAfterDisposeWithTrueLeaveOpen(CompressionMode mode) + { + var ms = await LocalMemoryStream.readAppFileAsync(CompressedTestFile(UncompressedTestFile())); + var decompressor = CreateStream(ms, mode, leaveOpen: true); + var baseStream = BaseStream(decompressor); + Assert.Same(ms, baseStream); + decompressor.Dispose(); + + int size = 1024; + byte[] bytes = new byte[size]; + if (mode == CompressionMode.Compress) + baseStream.Write(bytes, 0, size); + else + baseStream.Read(bytes, 0, size); + } + } + + internal sealed class BadWrappedStream : MemoryStream + { + public enum Mode + { + Default, + ReturnNullTasks, + ReturnTooSmallCounts, + ReturnTooLargeCounts, + ReadSlowly + } + + private readonly Mode _mode; + + public BadWrappedStream(Mode mode) { _mode = mode; } + public BadWrappedStream(Mode mode, byte[] buffer) : base(buffer) { _mode = mode; } + + public override int Read(byte[] buffer, int offset, int count) + { + switch (_mode) + { + case Mode.ReturnTooSmallCounts: + return -1; + case Mode.ReturnTooLargeCounts: + return buffer.Length + 1; + case Mode.ReadSlowly: + return base.Read(buffer, offset, 1); + default: + return 0; + } + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + return _mode == Mode.ReturnNullTasks ? + null : + base.WriteAsync(buffer, offset, count, cancellationToken); + } + + public override void Write(byte[] buffer, int offset, int count) { } + public override void Flush() { } + public override bool CanRead { get { return true; } } + public override bool CanSeek { get { return false; } } + public override bool CanWrite { get { return true; } } + public override long Length { get { throw new NotSupportedException(); } } + public override long Position { get { throw new NotSupportedException(); } set { throw new NotSupportedException(); } } + public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } + public override void SetLength(long value) { throw new NotSupportedException(); } + } + + internal partial class ManualSyncMemoryStream : MemoryStream + { + private bool isSync; + public ManualResetEventSlim manualResetEvent = new ManualResetEventSlim(initialState: false); + + public bool ReadHit = false; // For validation of the async methods we want to ensure they correctly delegate the async + public bool WriteHit = false; // methods of the underlying stream. This bool acts as a toggle to check that they're being used. + + public static async Task GetStreamFromFileAsync(string testFile, bool sync = false) + { + var baseStream = await StreamHelpers.CreateTempCopyStream(testFile); + var ms = new ManualSyncMemoryStream(sync); + await baseStream.CopyToAsync(ms); + + ms.Position = 0; + return ms; + } + + public ManualSyncMemoryStream(bool sync = false) : base() + { + isSync = sync; + } + + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) => TaskToApm.Begin(ReadAsync(buffer, offset, count), callback, state); + public override int EndRead(IAsyncResult asyncResult) => TaskToApm.End(asyncResult); + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) => TaskToApm.Begin(WriteAsync(buffer, offset, count), callback, state); + public override void EndWrite(IAsyncResult asyncResult) => TaskToApm.End(asyncResult); + + public override async Task ReadAsync(byte[] array, int offset, int count, CancellationToken cancellationToken) + { + ReadHit = true; + if (isSync) + { + manualResetEvent.Wait(cancellationToken); + } + else + { + await Task.Run(() => manualResetEvent.Wait(cancellationToken)).ConfigureAwait(false); + } + + return await base.ReadAsync(array, offset, count, cancellationToken); + } + + public override async Task WriteAsync(byte[] array, int offset, int count, CancellationToken cancellationToken) + { + WriteHit = true; + if (isSync) + { + manualResetEvent.Wait(cancellationToken); + } + else + { + await Task.Run(() => manualResetEvent.Wait(cancellationToken)).ConfigureAwait(false); + } + + await base.WriteAsync(array, offset, count, cancellationToken); + } + #if STREAM_MEMORY_OVERLOADS_AVAILABLE + + public override async ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken) + { + ReadHit = true; + + if (isSync) + { + manualResetEvent.Wait(cancellationToken); + } + else + { + await Task.Run(() => manualResetEvent.Wait(cancellationToken)).ConfigureAwait(false); + } + return await base.ReadAsync(destination, cancellationToken); + } + + public override async Task WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken) + { + WriteHit = true; + + if (isSync) + { + manualResetEvent.Wait(cancellationToken); + } + else + { + await Task.Run(() => manualResetEvent.Wait(cancellationToken)).ConfigureAwait(false); + } + + await base.WriteAsync(source, cancellationToken); + } + #endif + } +} diff --git a/external/corefx/src/Common/tests/System/IO/Compression/ZipTestHelper.cs b/external/corefx/src/Common/tests/System/IO/Compression/ZipTestHelper.cs index 4ae8d40efa..53f6fb4fd9 100644 --- a/external/corefx/src/Common/tests/System/IO/Compression/ZipTestHelper.cs +++ b/external/corefx/src/Common/tests/System/IO/Compression/ZipTestHelper.cs @@ -115,7 +115,11 @@ namespace System.IO.Compression.Tests ac = ast.Read(ad, 0, 4096); bc = bst.Read(bd, 0, 4096); - Assert.Equal(ac, bc); + if (ac != bc) + { + bd = NormalizeLineEndings(bd); + } + Assert.True(ArraysEqual(ad, bd, ac), "Stream contents not equal: " + ast.ToString() + ", " + bst.ToString()); blocksRead++; @@ -133,6 +137,14 @@ namespace System.IO.Compression.Tests IsZipSameAsDir(s, directory, mode, requireExplicit, checkTimes); } + public static byte[] NormalizeLineEndings(byte[] str) + { + string rep = Text.Encoding.Default.GetString(str); + rep = rep.Replace("\r\n", "\n"); + rep = rep.Replace("\n", "\r\n"); + return Text.Encoding.Default.GetBytes(rep); + } + public static void IsZipSameAsDir(Stream archiveFile, string directory, ZipArchiveMode mode, bool requireExplicit, bool checkTimes) { int count = 0; @@ -160,8 +172,18 @@ namespace System.IO.Compression.Tests using (Stream entrystream = entry.Open()) { entrystream.Read(buffer, 0, buffer.Length); +#if netcoreapp + uint zipcrc = entry.Crc32; + Assert.Equal(CRC.CalculateCRC(buffer), zipcrc.ToString()); +#endif + + if (file.Length != givenLength) + { + buffer = NormalizeLineEndings(buffer); + } + + Assert.Equal(file.Length, buffer.Length); string crc = CRC.CalculateCRC(buffer); - Assert.Equal(file.Length, givenLength); Assert.Equal(file.CRC, crc); } @@ -279,7 +301,9 @@ namespace System.IO.Compression.Tests } } - public static async Task CreateFromDir(string directory, Stream archiveStream, ZipArchiveMode mode, bool useSpansForWriting = false) + /// Tests the Span overloads of Write + /// Writes in chunks of 5 to test Write with a nonzero offset + public static async Task CreateFromDir(string directory, Stream archiveStream, ZipArchiveMode mode, bool useSpansForWriting = false, bool writeInChunks = false) { var files = FileData.InPath(directory); using (ZipArchive archive = new ZipArchive(archiveStream, mode, true)) @@ -318,6 +342,14 @@ namespace System.IO.Compression.Tests entryStream.Write(new ReadOnlySpan(buffer, 0, bytesRead)); } } + else if (writeInChunks) + { + while ((bytesRead = installStream.Read(buffer, 0, buffer.Length)) != 0) + { + for (int k = 0; k < bytesRead; k += 5) + entryStream.Write(buffer, k, Math.Min(5, bytesRead - k)); + } + } else { while ((bytesRead = installStream.Read(buffer, 0, buffer.Length)) != 0) diff --git a/external/corefx/src/Common/tests/System/Net/Configuration.Security.cs b/external/corefx/src/Common/tests/System/Net/Configuration.Security.cs index d2273731a8..b5a3e5ccea 100644 --- a/external/corefx/src/Common/tests/System/Net/Configuration.Security.cs +++ b/external/corefx/src/Common/tests/System/Net/Configuration.Security.cs @@ -20,6 +20,8 @@ namespace System.Net.Test.Common public static Uri NegotiateServer => GetUriValue("COREFX_NET_SECURITY_NEGOSERVERURI"); + public static string TlsRenegotiationServer => GetValue("COREFX_NET_SECURITY_TLSREGOTIATIONSERVER", "corefx-net-tls.azurewebsites.net"); + // This should be set if hostnames for all certificates within corefx-testdata have been set to point to 127.0.0.1. // Example: // 127.0.0.1 testservereku.contoso.com diff --git a/external/corefx/src/Common/tests/System/Net/Http/LoopbackServer.cs b/external/corefx/src/Common/tests/System/Net/Http/LoopbackServer.cs index 25f6fcb451..af905435de 100644 --- a/external/corefx/src/Common/tests/System/Net/Http/LoopbackServer.cs +++ b/external/corefx/src/Common/tests/System/Net/Http/LoopbackServer.cs @@ -89,7 +89,7 @@ namespace System.Net.Test.Common return AcceptSocketAsync(server, (s, stream, reader, writer) => ReadWriteAcceptedAsync(s, reader, writer, response), options); } - public static async Task> ReadWriteAcceptedAsync(Socket s, StreamReader reader, StreamWriter writer, string response = null, bool shutdown = true) + public static async Task> ReadWriteAcceptedAsync(Socket s, StreamReader reader, StreamWriter writer, string response = null) { // Read request line and headers. Skip any request body. var lines = new List(); @@ -101,11 +101,6 @@ namespace System.Net.Test.Common await writer.WriteAsync(response ?? DefaultHttpResponse).ConfigureAwait(false); - if (shutdown) - { - s.Shutdown(SocketShutdown.Send); - } - return lines; } @@ -157,7 +152,8 @@ namespace System.Net.Test.Common public static async Task> AcceptSocketAsync(Socket server, Func>> funcAsync, Options options = null) { options = options ?? new Options(); - using (Socket s = await server.AcceptAsync().ConfigureAwait(false)) + Socket s = await server.AcceptAsync().ConfigureAwait(false); + try { Stream stream = new NetworkStream(s, ownsSocket: false); if (options.UseSsl) @@ -166,9 +162,9 @@ namespace System.Net.Test.Common using (var cert = Configuration.Certificates.GetServerCertificate()) { await sslStream.AuthenticateAsServerAsync( - cert, + cert, clientCertificateRequired: true, // allowed but not required - enabledSslProtocols: options.SslProtocols, + enabledSslProtocols: options.SslProtocols, checkCertificateRevocation: false).ConfigureAwait(false); } stream = sslStream; @@ -180,6 +176,18 @@ namespace System.Net.Test.Common return await funcAsync(s, stream, reader, writer).ConfigureAwait(false); } } + finally + { + try + { + s.Shutdown(SocketShutdown.Send); + s.Dispose(); + } + catch (ObjectDisposedException) + { + // In case the test itself disposes of the socket + } + } } public enum TransferType @@ -250,8 +258,6 @@ namespace System.Net.Test.Common await writer.WriteAsync($"{content}\r\n").ConfigureAwait(false); } - client.Shutdown(SocketShutdown.Both); - return null; }), out localEndPoint); } diff --git a/external/corefx/src/Common/tests/System/Net/RemoteServerQuery.cs b/external/corefx/src/Common/tests/System/Net/RemoteServerQuery.cs new file mode 100644 index 0000000000..4b40af4c09 --- /dev/null +++ b/external/corefx/src/Common/tests/System/Net/RemoteServerQuery.cs @@ -0,0 +1,65 @@ +// 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.Threading.Tasks; +using Xunit; + +namespace System.Net.Test.Common +{ + internal class RemoteServerException : Exception + { + private const string Description = "Likely external issue with remote server"; + + public RemoteServerException() + : this(null, null) { } + + public RemoteServerException(string server, Exception inner) + : base(GetMessage(server), inner) { } + + private static string GetMessage(string server) + { + if (server == null) + return Description; + + return $"{Description} : {server}"; + } + } + + internal static class RemoteServerQuery + { + internal static async Task Run(Func> testCode, Func remoteExceptionWrapper, string serverName) + { + try + { + return await testCode(); + } + catch (Exception actualException) + { + if (remoteExceptionWrapper(actualException)) + { + throw new RemoteServerException(serverName, actualException); + } + + throw; + } + } + + internal static async Task Run(Func testCode, Func remoteExceptionWrapper, string serverName) + { + try + { + await testCode(); + } + catch (Exception actualException) + { + if (remoteExceptionWrapper(actualException)) + { + throw new RemoteServerException(serverName, actualException); + } + + throw; + } + } + } +} diff --git a/external/corefx/src/Common/tests/System/Net/Sockets/TestSettings.cs b/external/corefx/src/Common/tests/System/Net/Sockets/TestSettings.cs index 1f20fc816e..2e51577417 100644 --- a/external/corefx/src/Common/tests/System/Net/Sockets/TestSettings.cs +++ b/external/corefx/src/Common/tests/System/Net/Sockets/TestSettings.cs @@ -2,6 +2,8 @@ // 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.Threading.Tasks; + namespace System.Net.Sockets.Tests { public static class TestSettings @@ -17,5 +19,7 @@ namespace System.Net.Sockets.Tests // have the same port #. // This occurs on *nix but not Windows because *nix uses random values (1024-65535) while Windows increments. public const int UDPRedundancy = 1; + + public static Task WhenAllOrAnyFailedWithTimeout(params Task[] tasks) => tasks.WhenAllOrAnyFailed(PassingTestTimeout); } } diff --git a/external/corefx/src/Common/tests/System/PerfUtils.cs b/external/corefx/src/Common/tests/System/PerfUtils.cs index 5c5918b4ea..f205f0bf91 100644 --- a/external/corefx/src/Common/tests/System/PerfUtils.cs +++ b/external/corefx/src/Common/tests/System/PerfUtils.cs @@ -4,7 +4,6 @@ using System.IO; using System.Runtime.CompilerServices; -using System.Text; namespace System { @@ -42,7 +41,7 @@ namespace System for (int i = 0; i < str.Length; i++) { // Add path separator so folders aren't too long. - if (i%20 == 0) + if (i % 20 == 0) { str[i] = Path.DirectorySeparatorChar; } diff --git a/external/corefx/src/Common/tests/System/Runtime/Serialization/DataContractSerializerHelper.cs b/external/corefx/src/Common/tests/System/Runtime/Serialization/DataContractSerializerHelper.cs new file mode 100644 index 0000000000..2eab9653fe --- /dev/null +++ b/external/corefx/src/Common/tests/System/Runtime/Serialization/DataContractSerializerHelper.cs @@ -0,0 +1,49 @@ +// 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.IO; +using System.Reflection; +using System.Runtime.Serialization.Formatters.Binary; + +using Xunit; + +namespace System.Runtime.Serialization.Tests +{ + public static class DataContractSerializerHelper + { + internal static T SerializeAndDeserialize(T value, string baseline, DataContractSerializerSettings settings = null, Func serializerFactory = null, bool skipStringCompare = false) + { + DataContractSerializer dcs; + if (serializerFactory != null) + { + dcs = serializerFactory(); + } + else + { + dcs = (settings != null) ? new DataContractSerializer(typeof(T), settings) : new DataContractSerializer(typeof(T)); + } + + using (MemoryStream ms = new MemoryStream()) + { + dcs.WriteObject(ms, value); + ms.Position = 0; + + string actualOutput = new StreamReader(ms).ReadToEnd(); + + if (!skipStringCompare) + { + Utils.CompareResult result = Utils.Compare(baseline, actualOutput); + Assert.True(result.Equal, string.Format("{1}{0}Test failed for input: {2}{0}Expected: {3}{0}Actual: {4}", + Environment.NewLine, result.ErrorMessage, value, baseline, actualOutput)); + } + + ms.Position = 0; + T deserialized = (T)dcs.ReadObject(ms); + + return deserialized; + } + } + } + +} \ No newline at end of file diff --git a/external/corefx/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/EncryptDecrypt.cs b/external/corefx/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/EncryptDecrypt.cs index 028a908597..b47b8df020 100644 --- a/external/corefx/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/EncryptDecrypt.cs +++ b/external/corefx/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/EncryptDecrypt.cs @@ -26,7 +26,7 @@ namespace System.Security.Cryptography.Rsa.Tests public abstract class EncryptDecrypt { - private static bool EphemeralKeysAreExportable => !PlatformDetection.IsFullFramework || PlatformDetection.IsNetfx462OrNewer(); + private static bool EphemeralKeysAreExportable => !PlatformDetection.IsFullFramework || PlatformDetection.IsNetfx462OrNewer; protected abstract byte[] Encrypt(RSA rsa, byte[] data, RSAEncryptionPadding padding); protected abstract byte[] Decrypt(RSA rsa, byte[] data, RSAEncryptionPadding padding); diff --git a/external/corefx/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/ImportExport.cs b/external/corefx/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/ImportExport.cs index 954c1a3420..6b21474745 100644 --- a/external/corefx/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/ImportExport.cs +++ b/external/corefx/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/ImportExport.cs @@ -10,7 +10,7 @@ namespace System.Security.Cryptography.Rsa.Tests { public partial class ImportExport { - private static bool EphemeralKeysAreExportable => !PlatformDetection.IsFullFramework || PlatformDetection.IsNetfx462OrNewer(); + private static bool EphemeralKeysAreExportable => !PlatformDetection.IsFullFramework || PlatformDetection.IsNetfx462OrNewer; [ConditionalFact(nameof(EphemeralKeysAreExportable))] public static void ExportAutoKey() diff --git a/external/corefx/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/SignVerify.cs b/external/corefx/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/SignVerify.cs index b2b7e1a459..557d3a020b 100644 --- a/external/corefx/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/SignVerify.cs +++ b/external/corefx/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/SignVerify.cs @@ -38,8 +38,8 @@ namespace System.Security.Cryptography.Rsa.Tests public abstract class SignVerify { - public static bool BadKeyFormatDoesntThrow => !PlatformDetection.IsFullFramework || PlatformDetection.IsNetfx462OrNewer(); - public static bool InvalidKeySizeDoesntThrow => !PlatformDetection.IsFullFramework || PlatformDetection.IsNetfx462OrNewer(); + public static bool BadKeyFormatDoesntThrow => !PlatformDetection.IsFullFramework || PlatformDetection.IsNetfx462OrNewer; + public static bool InvalidKeySizeDoesntThrow => !PlatformDetection.IsFullFramework || PlatformDetection.IsNetfx462OrNewer; protected abstract byte[] SignData(RSA rsa, byte[] data, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding); protected abstract byte[] SignHash(RSA rsa, byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding); diff --git a/external/corefx/src/Common/tests/System/Security/Cryptography/ByteUtils.cs b/external/corefx/src/Common/tests/System/Security/Cryptography/ByteUtils.cs index 38c8c5f918..f39df0409f 100644 --- a/external/corefx/src/Common/tests/System/Security/Cryptography/ByteUtils.cs +++ b/external/corefx/src/Common/tests/System/Security/Cryptography/ByteUtils.cs @@ -2,6 +2,7 @@ // 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.Globalization; using System.Text; @@ -35,12 +36,27 @@ namespace Test.Cryptography } internal static string ByteArrayToHex(this byte[] bytes) + { + return ByteArrayToHex(bytes.AsReadOnlySpan()); + } + + internal static string ByteArrayToHex(this Span bytes) + { + return ByteArrayToHex((ReadOnlySpan)bytes); + } + + internal static string ByteArrayToHex(this ReadOnlyMemory bytes) + { + return ByteArrayToHex(bytes.Span); + } + + internal static string ByteArrayToHex(this ReadOnlySpan bytes) { StringBuilder builder = new StringBuilder(bytes.Length * 2); - foreach (byte b in bytes) + for (int i = 0; i < bytes.Length; i++) { - builder.Append(b.ToString("X2")); + builder.Append(bytes[i].ToString("X2")); } return builder.ToString(); diff --git a/external/corefx/src/Common/tests/System/Threading/Tasks/TaskTimeoutExtensions.cs b/external/corefx/src/Common/tests/System/Threading/Tasks/TaskTimeoutExtensions.cs index 686dac68c9..3a72628f6e 100644 --- a/external/corefx/src/Common/tests/System/Threading/Tasks/TaskTimeoutExtensions.cs +++ b/external/corefx/src/Common/tests/System/Threading/Tasks/TaskTimeoutExtensions.cs @@ -20,7 +20,7 @@ namespace System.Threading.Tasks } else { - throw new TimeoutException(); + throw new TimeoutException($"Task timed out after {millisecondsTimeout}"); } } @@ -35,8 +35,50 @@ namespace System.Threading.Tasks } else { - throw new TimeoutException(); + throw new TimeoutException($"Task timed out after {millisecondsTimeout}"); } } + + public static async Task WhenAllOrAnyFailed(this Task[] tasks, int millisecondsTimeout) + { + var cts = new CancellationTokenSource(); + Task task = tasks.WhenAllOrAnyFailed(); + if (task == await Task.WhenAny(task, Task.Delay(millisecondsTimeout, cts.Token)).ConfigureAwait(false)) + { + cts.Cancel(); + await task; + } + else + { + throw new TimeoutException($"{nameof(WhenAllOrAnyFailed)} timed out after {millisecondsTimeout}"); + } + } + + public static Task WhenAllOrAnyFailed(this Task[] tasks) + { + int remaining = tasks.Length; + var tcs = new TaskCompletionSource(); + foreach (Task t in tasks) + { + t.ContinueWith(a => + { + if (a.IsFaulted) + { + tcs.TrySetException(a.Exception.InnerExceptions); + Interlocked.Decrement(ref remaining); + } + else if (a.IsCanceled) + { + tcs.TrySetCanceled(); + Interlocked.Decrement(ref remaining); + } + else if (Interlocked.Decrement(ref remaining) == 0) + { + tcs.TrySetResult(true); + } + }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default); + } + return tcs.Task; + } } } diff --git a/external/corefx/src/Common/tests/System/Xml/BaseLibManaged/BaseLibManaged.csproj b/external/corefx/src/Common/tests/System/Xml/BaseLibManaged/BaseLibManaged.csproj index 2462b3e3e7..73e311008d 100644 --- a/external/corefx/src/Common/tests/System/Xml/BaseLibManaged/BaseLibManaged.csproj +++ b/external/corefx/src/Common/tests/System/Xml/BaseLibManaged/BaseLibManaged.csproj @@ -5,10 +5,8 @@ {42F10363-C2A2-42B5-B7B9-1B5DBB0BC71E} WebData.BaseLib - - - - + + @@ -16,4 +14,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/Common/tests/System/Xml/BaseLibManaged/Configurations.props b/external/corefx/src/Common/tests/System/Xml/BaseLibManaged/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/Common/tests/System/Xml/BaseLibManaged/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/Common/tests/System/Xml/ModuleCore/XunitTestCase.cs b/external/corefx/src/Common/tests/System/Xml/ModuleCore/XunitTestCase.cs index f5bcaf0f86..eb47786eb7 100644 --- a/external/corefx/src/Common/tests/System/Xml/ModuleCore/XunitTestCase.cs +++ b/external/corefx/src/Common/tests/System/Xml/ModuleCore/XunitTestCase.cs @@ -1,4 +1,8 @@ -using System; +// 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.Collections.Generic; using System.Linq; using System.Text; diff --git a/external/corefx/src/Common/tests/System/Xml/ModuleCore/ctestexception.cs b/external/corefx/src/Common/tests/System/Xml/ModuleCore/ctestexception.cs index c25be7364b..36cfcfc0aa 100644 --- a/external/corefx/src/Common/tests/System/Xml/ModuleCore/ctestexception.cs +++ b/external/corefx/src/Common/tests/System/Xml/ModuleCore/ctestexception.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Text; namespace OLEDB.Test.ModuleCore { @@ -77,5 +78,18 @@ namespace OLEDB.Test.ModuleCore Actual = actual; Expected = expected; } + + public override string Message + { + get + { + StringBuilder text = new StringBuilder(); + text.AppendLine(base.Message); + text.AppendLine($"Expected: `{Expected.ToString()}`"); + text.AppendLine($"Actual: `{Actual.ToString()}`"); + text.AppendLine($"Result: {Result}"); + return text.ToString(); + } + } } } diff --git a/external/corefx/src/Common/tests/Tests/System/IO/PathInternal.Tests.cs b/external/corefx/src/Common/tests/Tests/System/IO/PathInternal.Tests.cs index b030fa800f..4435fb8830 100644 --- a/external/corefx/src/Common/tests/Tests/System/IO/PathInternal.Tests.cs +++ b/external/corefx/src/Common/tests/Tests/System/IO/PathInternal.Tests.cs @@ -11,12 +11,6 @@ namespace Tests.System.IO { public class PathInternalTests { - [Fact] - public void CheckInvalidPathChars_ThrowsOnNull() - { - Assert.Throws(() => PathInternal.CheckInvalidPathChars(null)); - } - [Theory, InlineData("Foo", "Foos"), InlineData("Foo", null)] diff --git a/external/corefx/src/Common/tests/Tests/System/IO/PathInternal.Windows.Tests.cs b/external/corefx/src/Common/tests/Tests/System/IO/PathInternal.Windows.Tests.cs index be77b533a2..799dfc8d89 100644 --- a/external/corefx/src/Common/tests/Tests/System/IO/PathInternal.Windows.Tests.cs +++ b/external/corefx/src/Common/tests/Tests/System/IO/PathInternal.Windows.Tests.cs @@ -165,21 +165,6 @@ public class PathInternal_Windows_Tests Assert.Same(path, result); } - [Theory] - [InlineData(@"\", 1)] - [InlineData("", 0)] - [InlineData(":", 1)] - [InlineData(";", 0)] - [InlineData("/", 1)] - [InlineData(@"Foo\/\/\", 8)] - [InlineData("Foo:Bar", 4)] - [InlineData(@"C:\Users\Foobar\", 16)] - [PlatformSpecific(TestPlatforms.Windows)] - public void FindFileNameIndexTests(string path, int expected) - { - Assert.Equal(expected, PathInternal.FindFileNameIndex(path)); - } - [Theory, InlineData(@"", @"", StringComparison.OrdinalIgnoreCase, true), InlineData(@"", @"", StringComparison.Ordinal, true), diff --git a/external/corefx/src/Common/tests/Tests/System/IO/PathInternal_Unix_Tests.cs b/external/corefx/src/Common/tests/Tests/System/IO/PathInternal_Unix_Tests.cs index 0eb37e27b9..5eac9168c3 100644 --- a/external/corefx/src/Common/tests/Tests/System/IO/PathInternal_Unix_Tests.cs +++ b/external/corefx/src/Common/tests/Tests/System/IO/PathInternal_Unix_Tests.cs @@ -39,20 +39,4 @@ public class PathInternal_Unix_Tests if (string.Equals(path, expected, StringComparison.Ordinal)) Assert.Same(path, result); } - - [Theory] - [InlineData(@"\", 0)] - [InlineData("", 0)] - [InlineData(":", 0)] - [InlineData(";", 0)] - [InlineData("/", 1)] - [InlineData(@"Foo\/\/\", 7)] - [InlineData("Foo:Bar", 0)] - [InlineData("/usr/foo/", 9)] - [InlineData("/home/bar", 6)] - [PlatformSpecific(TestPlatforms.AnyUnix)] - public void FindFileNameIndexTests(string path, int expected) - { - Assert.Equal(expected, PathInternal.FindFileNameIndex(path)); - } } diff --git a/external/corefx/src/Common/tests/Tests/System/IO/Win32Marshal.Tests.cs b/external/corefx/src/Common/tests/Tests/System/IO/Win32Marshal.Tests.cs index 0644bd9001..1e09d9f3fa 100644 --- a/external/corefx/src/Common/tests/Tests/System/IO/Win32Marshal.Tests.cs +++ b/external/corefx/src/Common/tests/Tests/System/IO/Win32Marshal.Tests.cs @@ -84,13 +84,18 @@ namespace Tests.System.IO } [Theory] - [InlineData("", Interop.Errors.ERROR_FILENAME_EXCED_RANGE)] - [InlineData("foo", Interop.Errors.ERROR_FILENAME_EXCED_RANGE)] - public void PathTooLongErrors(string path, int errorCode) + [InlineData("", Interop.Errors.ERROR_FILENAME_EXCED_RANGE, "IO_PathTooLong")] + [InlineData("foo", Interop.Errors.ERROR_FILENAME_EXCED_RANGE, "IO_PathTooLong_Path")] + public void PathTooLongErrors(string path, int errorCode, string error) { var exception = Win32Marshal.GetExceptionForWin32Error(errorCode, path); Assert.IsType(exception); - Assert.StartsWith("IO_PathTooLong", exception.Message); + + Assert.StartsWith(error, exception.Message); + if (!string.IsNullOrEmpty(path)) + { + Assert.EndsWith(path, exception.Message); + } } [Theory] diff --git a/external/corefx/src/Common/tests/Tests/System/MarvinTests.cs b/external/corefx/src/Common/tests/Tests/System/MarvinTests.cs index 5498b0a8e6..fe2a3a54fe 100644 --- a/external/corefx/src/Common/tests/Tests/System/MarvinTests.cs +++ b/external/corefx/src/Common/tests/Tests/System/MarvinTests.cs @@ -29,7 +29,7 @@ namespace Tests.System public void ComputeHash_Success(ulong seed, string testDataString, ulong expectedHash) { var testDataSpan = new Span(testDataString.HexToByteArray()); - long hash = Marvin.ComputeHash(ref testDataSpan.DangerousGetPinnableReference(), testDataSpan.Length, seed); + long hash = Marvin.ComputeHash(testDataSpan, seed); Assert.Equal((long)expectedHash, hash); } diff --git a/external/corefx/src/Common/tests/Tests/System/Text/ValueStringBuilderTests.cs b/external/corefx/src/Common/tests/Tests/System/Text/ValueStringBuilderTests.cs new file mode 100644 index 0000000000..d7878e8f55 --- /dev/null +++ b/external/corefx/src/Common/tests/Tests/System/Text/ValueStringBuilderTests.cs @@ -0,0 +1,244 @@ +// 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 Xunit; + +namespace System.Text.Tests +{ + public class ValueStringBuilderTests + { + [Fact] + public void Ctor_Default_CanAppend() + { + var vsb = default(ValueStringBuilder); + Assert.Equal(0, vsb.Length); + + vsb.Append('a'); + Assert.Equal(1, vsb.Length); + Assert.Equal("a", vsb.ToString()); + } + + [Fact] + public void Ctor_Span_CanAppend() + { + var vsb = new ValueStringBuilder(new char[1]); + Assert.Equal(0, vsb.Length); + + vsb.Append('a'); + Assert.Equal(1, vsb.Length); + Assert.Equal("a", vsb.ToString()); + } + + [Fact] + public void Append_Char_MatchesStringBuilder() + { + var sb = new StringBuilder(); + var vsb = new ValueStringBuilder(); + for (int i = 1; i <= 100; i++) + { + sb.Append((char)i); + vsb.Append((char)i); + } + + Assert.Equal(sb.Length, vsb.Length); + Assert.Equal(sb.ToString(), vsb.ToString()); + } + + [Fact] + public void Append_String_MatchesStringBuilder() + { + var sb = new StringBuilder(); + var vsb = new ValueStringBuilder(); + for (int i = 1; i <= 100; i++) + { + string s = i.ToString(); + sb.Append(s); + vsb.Append(s); + } + + Assert.Equal(sb.Length, vsb.Length); + Assert.Equal(sb.ToString(), vsb.ToString()); + } + + [Theory] + [InlineData(0, 4*1024*1024)] + [InlineData(1025, 4*1024*1024)] + [InlineData(3*1024*1024, 6*1024*1024)] + public void Append_String_Large_MatchesStringBuilder(int initialLength, int stringLength) + { + var sb = new StringBuilder(initialLength); + var vsb = new ValueStringBuilder(new char[initialLength]); + + string s = new string('a', stringLength); + sb.Append(s); + vsb.Append(s); + + Assert.Equal(sb.Length, vsb.Length); + Assert.Equal(sb.ToString(), vsb.ToString()); + } + + [Fact] + public void Append_CharInt_MatchesStringBuilder() + { + var sb = new StringBuilder(); + var vsb = new ValueStringBuilder(); + for (int i = 1; i <= 100; i++) + { + sb.Append((char)i, i); + vsb.Append((char)i, i); + } + + Assert.Equal(sb.Length, vsb.Length); + Assert.Equal(sb.ToString(), vsb.ToString()); + } + + [Fact] + public unsafe void Append_PtrInt_MatchesStringBuilder() + { + var sb = new StringBuilder(); + var vsb = new ValueStringBuilder(); + for (int i = 1; i <= 100; i++) + { + string s = i.ToString(); + fixed (char* p = s) + { + sb.Append(p, s.Length); + vsb.Append(p, s.Length); + } + } + + Assert.Equal(sb.Length, vsb.Length); + Assert.Equal(sb.ToString(), vsb.ToString()); + } + + [Fact] + public void AppendSpan_DataAppendedCorrectly() + { + var sb = new StringBuilder(); + var vsb = new ValueStringBuilder(); + + for (int i = 1; i <= 1000; i++) + { + string s = i.ToString(); + + sb.Append(s); + + Span span = vsb.AppendSpan(s.Length); + Assert.Equal(sb.Length, vsb.Length); + + s.AsReadOnlySpan().CopyTo(span); + } + + Assert.Equal(sb.Length, vsb.Length); + Assert.Equal(sb.ToString(), vsb.ToString()); + } + + [Fact] + public void Insert_IntCharInt_MatchesStringBuilder() + { + var sb = new StringBuilder(); + var vsb = new ValueStringBuilder(); + var rand = new Random(42); + + for (int i = 1; i <= 100; i++) + { + int index = rand.Next(sb.Length); + sb.Insert(index, new string((char)i, 1), i); + vsb.Insert(index, (char)i, i); + } + + Assert.Equal(sb.Length, vsb.Length); + Assert.Equal(sb.ToString(), vsb.ToString()); + } + + [Fact] + public void ToString_ClearsBuilder_ThenReusable() + { + const string Text1 = "test"; + var vsb = new ValueStringBuilder(); + + vsb.Append(Text1); + Assert.Equal(Text1.Length, vsb.Length); + + string s = vsb.ToString(); + Assert.Equal(Text1, s); + + Assert.Equal(0, vsb.Length); + Assert.Equal(string.Empty, vsb.ToString()); + Assert.True(vsb.TryCopyTo(Span.Empty, out _)); + + const string Text2 = "another test"; + vsb.Append(Text2); + Assert.Equal(Text2.Length, vsb.Length); + Assert.Equal(Text2, vsb.ToString()); + } + + [Fact] + public void TryCopyTo_FailsWhenDestinationIsTooSmall_SucceedsWhenItsLargeEnough() + { + var vsb = new ValueStringBuilder(); + + const string Text = "expected text"; + vsb.Append(Text); + Assert.Equal(Text.Length, vsb.Length); + + Span dst = new char[Text.Length - 1]; + Assert.False(vsb.TryCopyTo(dst, out int charsWritten)); + Assert.Equal(0, charsWritten); + Assert.Equal(0, vsb.Length); + } + + [Fact] + public void TryCopyTo_ClearsBuilder_ThenReusable() + { + const string Text1 = "test"; + var vsb = new ValueStringBuilder(); + + vsb.Append(Text1); + Assert.Equal(Text1.Length, vsb.Length); + + Span dst = new char[Text1.Length]; + Assert.True(vsb.TryCopyTo(dst, out int charsWritten)); + Assert.Equal(Text1.Length, charsWritten); + Assert.Equal(Text1, new string(dst)); + + Assert.Equal(0, vsb.Length); + Assert.Equal(string.Empty, vsb.ToString()); + Assert.True(vsb.TryCopyTo(Span.Empty, out _)); + + const string Text2 = "another test"; + vsb.Append(Text2); + Assert.Equal(Text2.Length, vsb.Length); + Assert.Equal(Text2, vsb.ToString()); + } + + [Fact] + public unsafe void Length_Growing_SetsNulls() + { + const string Text1 = "foobar"; + var vsb = new ValueStringBuilder(); + + // Shrink then grow within capacity + vsb.Append(Text1); + Assert.Equal(Text1.Length, vsb.Length); + vsb.Length = 3; + Assert.Equal(3, vsb.Length); + vsb.Length = 6; + Assert.Equal(6, vsb.Length); + Assert.Equal("foo\0\0\0", vsb.ToString()); + + // Grow over capacity + const string Text2 = "bar"; + Span stackSpace = stackalloc char[Text2.Length]; + var vsb2 = new ValueStringBuilder(stackSpace); + Assert.Equal(0, vsb2.Length); + vsb2.Append(Text2); + Assert.True(Text2.AsReadOnlySpan().SequenceEqual(stackSpace), "existing stack buffer should have been used"); + Assert.Equal(Text2.Length, vsb2.Length); + vsb2.Length = 6; + Assert.Equal(6, vsb2.Length); + Assert.Equal("bar\0\0\0", vsb2.ToString()); + } + } +} diff --git a/external/corefx/src/CoreFx.Private.TestUtilities/ref/CoreFx.Private.TestUtilities.cs b/external/corefx/src/CoreFx.Private.TestUtilities/ref/CoreFx.Private.TestUtilities.cs index 9f9f65465c..36e3c2430e 100644 --- a/external/corefx/src/CoreFx.Private.TestUtilities/ref/CoreFx.Private.TestUtilities.cs +++ b/external/corefx/src/CoreFx.Private.TestUtilities/ref/CoreFx.Private.TestUtilities.cs @@ -17,11 +17,12 @@ namespace System } public static partial class AssertExtensions { + public static void Contains(string value, string substring) { } public static void Equal(byte[] expected, byte[] actual) { } - public static void GreaterThanOrEqualTo(T actual, T greaterThanOrEqualTo, string userMessage=null) where T : System.IComparable { } - public static void GreaterThan(T actual, T greaterThan, string userMessage=null) where T : System.IComparable { } - public static void LessThanOrEqualTo(T actual, T lessThanOrEqualTo, string userMessage=null) where T : System.IComparable { } - public static void LessThan(T actual, T lessThan, string userMessage=null) where T : System.IComparable { } + public static void GreaterThanOrEqualTo(T actual, T greaterThanOrEqualTo, string userMessage = null) where T : System.IComparable { } + public static void GreaterThan(T actual, T greaterThan, string userMessage = null) where T : System.IComparable { } + public static void LessThanOrEqualTo(T actual, T lessThanOrEqualTo, string userMessage = null) where T : System.IComparable { } + public static void LessThan(T actual, T lessThan, string userMessage = null) where T : System.IComparable { } public static void ThrowsAny(System.Type firstExceptionType, System.Type secondExceptionType, System.Action action) { } public static void ThrowsAny(System.Action action) where TFirstExceptionType : System.Exception where TSecondExceptionType : System.Exception { } public static void ThrowsAny(System.Action action) where TFirstExceptionType : System.Exception where TSecondExceptionType : System.Exception where TThirdExceptionType : System.Exception { } @@ -32,16 +33,16 @@ namespace System public static T Throws(string paramName, System.Func testCode) where T : System.ArgumentException { throw null; } public static void Throws(string netCoreParamName, string netFxParamName, System.Action action) where T : System.ArgumentException { } public static void Throws(string paramName, System.Action action) where TNetCoreExceptionType : System.ArgumentException where TNetFxExceptionType : System.ArgumentException { } + public static Exception Throws(Action action) where TNetCoreExceptionType : Exception where TNetFxExceptionType : Exception { throw null; } public static void Throws(string netCoreParamName, string netFxParamName, System.Action action) where TNetCoreExceptionType : System.ArgumentException where TNetFxExceptionType : System.ArgumentException { } } public static partial class PlatformDetection { public static bool IsReflectionEmitSupported; public static bool ClientWebSocketPartialMessagesSupported { get { throw null; } } - public static System.PlatformDetection.Range[] FrameworkRanges { get { throw null; } } public static bool HasWindowsShell { get { throw null; } } public static bool IsArmProcess { get { throw null; } } - public static bool IsCentos7 { get { throw null; } } + public static bool IsCentos6 { get { throw null; } } public static bool IsDebian { get { throw null; } } public static bool IsDebian8 { get { throw null; } } public static bool IsDrawingSupported { get { throw null; } } @@ -50,13 +51,17 @@ namespace System public static bool IsInvokingStaticConstructorsSupported { get { throw null; } } public static bool IsMacOsHighSierraOrHigher { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } public static bool IsNetBSD { get { throw null; } } + public static bool IsFreeBSD { get { throw null; } } public static bool IsNetCore { get { throw null; } } + public static bool IsNetfx462OrNewer { get { throw null; } } + public static bool IsNetfx470OrNewer { get { throw null; } } + public static bool IsNetfx471OrNewer { get { throw null; } } public static bool IsNetNative { get { throw null; } } public static bool IsNonZeroLowerBoundArraySupported { get { throw null; } } public static bool IsNotArmProcess { get { throw null; } } - public static bool IsNotFedoraOrRedHatOrCentos { get { throw null; } } + public static bool IsNotFedoraOrRedHatFamily { get { throw null; } } public static bool IsNotMacOsHighSierraOrHigher { get { throw null; } } - public static bool IsNotNetNativeRunningAsConsoleApp { get { throw null; } } + public static bool IsNotNetNativeRunningAsConsoleApp { get { throw null; } } public static bool IsNotOneCoreUAP { get { throw null; } } public static bool IsNotWindows8x { get { throw null; } } public static bool IsNotWindowsIoTCore { get { throw null; } } @@ -69,23 +74,23 @@ namespace System public static bool IsOSX { get { throw null; } } public static bool IsSuperUser { get { throw null; } } public static bool IsTizen { get { throw null; } } - public static bool IsRedHat { get { throw null; } } - public static bool IsNotRedHat { get { throw null; } } - public static bool IsRedHat69 { get { throw null; } } - public static bool IsNotRedHat69 { get { throw null; } } + public static bool IsRedHatFamily { get { throw null; } } + public static bool IsNotRedHatFamily { get { throw null; } } + public static bool IsRedHatFamily6 { get { throw null; } } + public static bool IsRedHatFamily7 { get { throw null; } } + public static bool IsNotRedHatFamily6 { get { throw null; } } public static bool IsUap { get { throw null; } } public static Version ICUVersion { get { return null; } } + public static Version OpenSslVersion { get { return null; } } public static bool IsUbuntu { get { throw null; } } public static bool IsUbuntu1404 { get { throw null; } } public static bool IsUbuntu1604 { get { throw null; } } public static bool IsUbuntu1704 { get { throw null; } } public static bool IsUbuntu1710 { get { throw null; } } public static bool IsWindows { get { throw null; } } - public static bool IsWindows10InsiderPreviewBuild16215OrGreater { get { throw null; } } - public static bool IsWindows10Version1607OrGreater { get { throw null; } } - public static bool IsWindows10Version16251OrGreater { get { throw null; } } - public static bool IsWindows10Version1703OrGreater { get { throw null; } } - public static bool IsWindowsRedStone2 { get { throw null; } } + public static bool IsWindows10Version1607OrGreater { get { throw null; } } // >= Windows 10 Anniversary Update + public static bool IsWindows10Version1703OrGreater { get { throw null; } } // >= Windows 10 Creators Update + public static bool IsWindows10Version1709OrGreater { get { throw null; } } // >= Windows 10 Fall Creators Update public static bool IsWindows7 { get { throw null; } } public static bool IsWindows8x { get { throw null; } } public static bool IsWindowsAndElevated { get { throw null; } } @@ -96,20 +101,11 @@ namespace System public static bool IsInAppContainer { get { throw null; } } public static bool IsWinRTSupported { get { throw null; } } public static bool IsXmlDsigXsltTransformSupported { get { throw null; } } - public static System.Version OSXKernelVersion { get { throw null; } } + public static System.Version OSXVersion { get { throw null; } } public static int WindowsVersion { get { throw null; } } public static string GetDistroVersionString() { throw null; } - public static bool IsNetfx462OrNewer() { throw null; } - public static bool IsNetfx470OrNewer() { throw null; } - public static bool IsNetfx471OrNewer() { throw null; } - public partial class Range - { - public Range(System.Version start, System.Version finish, System.Version frameworkVersion) { } - public System.Version Finish { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Version FrameworkVersion { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Version Start { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public bool IsInRange(System.Version version) { throw null; } - } + public static bool TargetsNetFx452OrLower { get { throw null; } } + public static bool IsDomainJoinedMachine { get { throw null; } } } public static partial class TheoryExtensions { @@ -127,15 +123,16 @@ namespace System.Diagnostics public const int SuccessExitCode = 42; protected static readonly string TestConsoleApp; protected RemoteExecutorTestBase() { } - public static System.Diagnostics.RemoteExecutorTestBase.RemoteInvokeHandle RemoteInvoke(System.Func method, System.Diagnostics.RemoteInvokeOptions options=null) { throw null; } - public static System.Diagnostics.RemoteExecutorTestBase.RemoteInvokeHandle RemoteInvoke(System.Func method, string arg, System.Diagnostics.RemoteInvokeOptions options=null) { throw null; } - public static System.Diagnostics.RemoteExecutorTestBase.RemoteInvokeHandle RemoteInvoke(System.Func method, string arg1, string arg2, System.Diagnostics.RemoteInvokeOptions options=null) { throw null; } - public static System.Diagnostics.RemoteExecutorTestBase.RemoteInvokeHandle RemoteInvoke(System.Func method, string arg1, string arg2, string arg3, System.Diagnostics.RemoteInvokeOptions options=null) { throw null; } - public static System.Diagnostics.RemoteExecutorTestBase.RemoteInvokeHandle RemoteInvoke(System.Func method, string arg1, string arg2, string arg3, string arg4, System.Diagnostics.RemoteInvokeOptions options=null) { throw null; } - public static System.Diagnostics.RemoteExecutorTestBase.RemoteInvokeHandle RemoteInvoke(System.Func method, string arg1, string arg2, string arg3, string arg4, string arg5, System.Diagnostics.RemoteInvokeOptions options=null) { throw null; } - public static System.Diagnostics.RemoteExecutorTestBase.RemoteInvokeHandle RemoteInvoke(System.Func> method, System.Diagnostics.RemoteInvokeOptions options=null) { throw null; } - public static System.Diagnostics.RemoteExecutorTestBase.RemoteInvokeHandle RemoteInvoke(System.Func> method, string arg, System.Diagnostics.RemoteInvokeOptions options=null) { throw null; } - public static System.Diagnostics.RemoteExecutorTestBase.RemoteInvokeHandle RemoteInvokeRaw(System.Delegate method, string unparsedArg, System.Diagnostics.RemoteInvokeOptions options=null) { throw null; } + public static System.Diagnostics.RemoteExecutorTestBase.RemoteInvokeHandle RemoteInvoke(System.Action method, System.Diagnostics.RemoteInvokeOptions options = null) { throw null; } + public static System.Diagnostics.RemoteExecutorTestBase.RemoteInvokeHandle RemoteInvoke(System.Func method, System.Diagnostics.RemoteInvokeOptions options = null) { throw null; } + public static System.Diagnostics.RemoteExecutorTestBase.RemoteInvokeHandle RemoteInvoke(System.Func method, string arg, System.Diagnostics.RemoteInvokeOptions options = null) { throw null; } + public static System.Diagnostics.RemoteExecutorTestBase.RemoteInvokeHandle RemoteInvoke(System.Func method, string arg1, string arg2, System.Diagnostics.RemoteInvokeOptions options = null) { throw null; } + public static System.Diagnostics.RemoteExecutorTestBase.RemoteInvokeHandle RemoteInvoke(System.Func method, string arg1, string arg2, string arg3, System.Diagnostics.RemoteInvokeOptions options = null) { throw null; } + public static System.Diagnostics.RemoteExecutorTestBase.RemoteInvokeHandle RemoteInvoke(System.Func method, string arg1, string arg2, string arg3, string arg4, System.Diagnostics.RemoteInvokeOptions options = null) { throw null; } + public static System.Diagnostics.RemoteExecutorTestBase.RemoteInvokeHandle RemoteInvoke(System.Func method, string arg1, string arg2, string arg3, string arg4, string arg5, System.Diagnostics.RemoteInvokeOptions options = null) { throw null; } + public static System.Diagnostics.RemoteExecutorTestBase.RemoteInvokeHandle RemoteInvoke(System.Func> method, System.Diagnostics.RemoteInvokeOptions options = null) { throw null; } + public static System.Diagnostics.RemoteExecutorTestBase.RemoteInvokeHandle RemoteInvoke(System.Func> method, string arg, System.Diagnostics.RemoteInvokeOptions options = null) { throw null; } + public static System.Diagnostics.RemoteExecutorTestBase.RemoteInvokeHandle RemoteInvokeRaw(System.Delegate method, string unparsedArg, System.Diagnostics.RemoteInvokeOptions options = null) { throw null; } public sealed partial class RemoteInvokeHandle : System.IDisposable { public RemoteInvokeHandle(System.Diagnostics.Process process, System.Diagnostics.RemoteInvokeOptions options) { } @@ -165,7 +162,7 @@ namespace System.IO public void Dispose() { } protected virtual void Dispose(bool disposing) { } ~FileCleanupTestBase() { } - protected string GetTestFileName(System.Nullable index=default(System.Nullable), [System.Runtime.CompilerServices.CallerMemberNameAttribute]string memberName=null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber=0) { throw null; } - protected string GetTestFilePath(System.Nullable index=default(System.Nullable), [System.Runtime.CompilerServices.CallerMemberNameAttribute]string memberName=null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber=0) { throw null; } + protected string GetTestFileName(System.Nullable index = default(System.Nullable), [System.Runtime.CompilerServices.CallerMemberNameAttribute]string memberName = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } + protected string GetTestFilePath(System.Nullable index = default(System.Nullable), [System.Runtime.CompilerServices.CallerMemberNameAttribute]string memberName = null, [System.Runtime.CompilerServices.CallerLineNumberAttribute]int lineNumber = 0) { throw null; } } } diff --git a/external/corefx/src/CoreFx.Private.TestUtilities/src/CoreFx.Private.TestUtilities.csproj b/external/corefx/src/CoreFx.Private.TestUtilities/src/CoreFx.Private.TestUtilities.csproj index 92739a998d..038d20d89c 100644 --- a/external/corefx/src/CoreFx.Private.TestUtilities/src/CoreFx.Private.TestUtilities.csproj +++ b/external/corefx/src/CoreFx.Private.TestUtilities/src/CoreFx.Private.TestUtilities.csproj @@ -30,6 +30,8 @@ + + @@ -40,6 +42,7 @@ + @@ -57,6 +60,7 @@ + @@ -90,6 +94,9 @@ + + Common\Interop\Unix\System.Security.Cryptography.Native\Interop.OpenSslVersion.cs + Common\Interop\Unix\Interop.GetEUid.cs @@ -112,4 +119,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/CoreFx.Private.TestUtilities/src/System/AssertExtensions.cs b/external/corefx/src/CoreFx.Private.TestUtilities/src/System/AssertExtensions.cs index a2e25c6118..69c2b7fc2a 100644 --- a/external/corefx/src/CoreFx.Private.TestUtilities/src/System/AssertExtensions.cs +++ b/external/corefx/src/CoreFx.Private.TestUtilities/src/System/AssertExtensions.cs @@ -50,6 +50,14 @@ namespace System return exception; } + public static T Throws(Action action) + where T : Exception + { + T exception = Assert.Throws(action); + + return exception; + } + public static T Throws(string paramName, Func testCode) where T : ArgumentException { @@ -72,15 +80,29 @@ namespace System return exception; } - public static void Throws(string paramName, Action action) - where TNetCoreExceptionType : ArgumentException + public static void Throws(string paramName, Action action) + where TNetCoreExceptionType : ArgumentException where TNetFxExceptionType : ArgumentException { Throws(paramName, paramName, action); } + public static Exception Throws(Action action) + where TNetCoreExceptionType : Exception + where TNetFxExceptionType : Exception + { + if (IsFullFramework) + { + return Throws(action); + } + else + { + return Throws(action); + } + } + public static void Throws(string netCoreParamName, string netFxParamName, Action action) - where TNetCoreExceptionType : ArgumentException + where TNetCoreExceptionType : ArgumentException where TNetFxExceptionType : ArgumentException { if (IsFullFramework) @@ -151,6 +173,24 @@ namespace System else return $"{message} {userMessage}"; } + + /// + /// Tests whether the specified string contains the specified substring + /// and throws an exception if the substring does not occur within the + /// test string or if either string or substring is null. + /// + /// + /// The string that is expected to contain . + /// + /// + /// The string expected to occur within . + /// + public static void Contains(string value, string substring) + { + Assert.NotNull(value); + Assert.NotNull(substring); + Assert.Contains(substring, value, StringComparison.Ordinal); + } /// /// Validate that a given value is greater than another value. diff --git a/external/corefx/src/CoreFx.Private.TestUtilities/src/System/Diagnostics/RemoteExecutorTestBase.Process.cs b/external/corefx/src/CoreFx.Private.TestUtilities/src/System/Diagnostics/RemoteExecutorTestBase.Process.cs index 896f424dd6..0e3bc9fa38 100644 --- a/external/corefx/src/CoreFx.Private.TestUtilities/src/System/Diagnostics/RemoteExecutorTestBase.Process.cs +++ b/external/corefx/src/CoreFx.Private.TestUtilities/src/System/Diagnostics/RemoteExecutorTestBase.Process.cs @@ -24,9 +24,9 @@ namespace System.Diagnostics { options = options ?? new RemoteInvokeOptions(); - // Verify the specified method is and that it returns an int (the exit code), + // Verify the specified method returns an int (the exit code) or nothing, // and that if it accepts any arguments, they're all strings. - Assert.True(method.ReturnType == typeof(int) || method.ReturnType == typeof(Task)); + Assert.True(method.ReturnType == typeof(void) || method.ReturnType == typeof(int) || method.ReturnType == typeof(Task)); Assert.All(method.GetParameters(), pi => Assert.Equal(typeof(string), pi.ParameterType)); // And make sure it's in this assembly. This isn't critical, but it helps with deployment to know @@ -55,8 +55,8 @@ namespace System.Diagnostics string passedArgs = pasteArguments ? PasteArguments.Paste(args, pasteFirstArgumentUsingArgV0Rules: false) : string.Join(" ", args); string testConsoleAppArgs = ExtraParameter + " " + metadataArgs + " " + passedArgs; - if (!File.Exists(TestConsoleApp)) - throw new IOException("RemoteExecutorConsoleApp test app isn't present in the test runtime directory."); + if (!File.Exists(HostRunner)) + throw new IOException($"{HostRunner} test app isn't present in the test runtime directory."); psi.FileName = HostRunner; psi.Arguments = testConsoleAppArgs; diff --git a/external/corefx/src/CoreFx.Private.TestUtilities/src/System/Diagnostics/RemoteExecutorTestBase.cs b/external/corefx/src/CoreFx.Private.TestUtilities/src/System/Diagnostics/RemoteExecutorTestBase.cs index 9ff440256c..b70b606df2 100644 --- a/external/corefx/src/CoreFx.Private.TestUtilities/src/System/Diagnostics/RemoteExecutorTestBase.cs +++ b/external/corefx/src/CoreFx.Private.TestUtilities/src/System/Diagnostics/RemoteExecutorTestBase.cs @@ -22,6 +22,20 @@ namespace System.Diagnostics /// The name of the test console app. protected static readonly string TestConsoleApp = "RemoteExecutorConsoleApp.exe"; + /// Invokes the method from this assembly in another process using the specified arguments. + /// The method to invoke. + /// Options to use for the invocation. + public static RemoteInvokeHandle RemoteInvoke( + Action method, + RemoteInvokeOptions options = null) + { + // There's no exit code to check + options = options ?? new RemoteInvokeOptions(); + options.CheckExitCode = false; + + return RemoteInvoke(GetMethodInfo(method), Array.Empty(), options); + } + /// Invokes the method from this assembly in another process using the specified arguments. /// The method to invoke. /// Options to use for the invocation. diff --git a/external/corefx/src/CoreFx.Private.TestUtilities/src/System/Diagnostics/RemoteExecutorTestBase.uap.cs b/external/corefx/src/CoreFx.Private.TestUtilities/src/System/Diagnostics/RemoteExecutorTestBase.uap.cs index b87e65e03f..85649a6aba 100644 --- a/external/corefx/src/CoreFx.Private.TestUtilities/src/System/Diagnostics/RemoteExecutorTestBase.uap.cs +++ b/external/corefx/src/CoreFx.Private.TestUtilities/src/System/Diagnostics/RemoteExecutorTestBase.uap.cs @@ -27,9 +27,9 @@ namespace System.Diagnostics { options = options ?? new RemoteInvokeOptions(); - // Verify the specified method is and that it returns an int (the exit code), + // Verify the specified method returns an int (the exit code) or nothing, // and that if it accepts any arguments, they're all strings. - Assert.True(method.ReturnType == typeof(int) || method.ReturnType == typeof(Task)); + Assert.True(method.ReturnType == typeof(void) || method.ReturnType == typeof(int) || method.ReturnType == typeof(Task)); Assert.All(method.GetParameters(), pi => Assert.Equal(typeof(string), pi.ParameterType)); // And make sure it's in this assembly. This isn't critical, but it helps with deployment to know @@ -66,7 +66,7 @@ namespace System.Diagnostics Assert.True(response.Status == AppServiceResponseStatus.Success, $"response.Status = {response.Status}"); int res = (int)response.Message["Results"]; - Assert.True(res == options.ExpectedExitCode, (string)response.Message["Log"] + Environment.NewLine + $"Returned Error code: {res}"); + Assert.True(!options.CheckExitCode || res == options.ExpectedExitCode, (string)response.Message["Log"] + Environment.NewLine + $"Returned Error code: {res}"); } // RemoteInvokeHandle is not really needed in the UAP scenario but we use it just to have consistent interface as non UAP diff --git a/external/corefx/src/CoreFx.Private.TestUtilities/src/System/Net/PlatformDetection.Networking.cs b/external/corefx/src/CoreFx.Private.TestUtilities/src/System/Net/PlatformDetection.Networking.cs index fb96c895df..0aa9faf541 100644 --- a/external/corefx/src/CoreFx.Private.TestUtilities/src/System/Net/PlatformDetection.Networking.cs +++ b/external/corefx/src/CoreFx.Private.TestUtilities/src/System/Net/PlatformDetection.Networking.cs @@ -9,6 +9,6 @@ namespace System // Windows 10 Insider Preview Build 16215 introduced the necessary APIs for the UAP version of // ClientWebSocket.ReceiveAsync to consume partial message data as it arrives, without having to wait // for "end of message" to be signaled. - public static bool ClientWebSocketPartialMessagesSupported => !IsUap || IsWindows10InsiderPreviewBuild16215OrGreater; + public static bool ClientWebSocketPartialMessagesSupported => !IsUap || IsWindows10Version1709OrGreater; } } diff --git a/external/corefx/src/CoreFx.Private.TestUtilities/src/System/PlatformDetection.NetFx.cs b/external/corefx/src/CoreFx.Private.TestUtilities/src/System/PlatformDetection.NetFx.cs new file mode 100644 index 0000000000..6fb51b99ec --- /dev/null +++ b/external/corefx/src/CoreFx.Private.TestUtilities/src/System/PlatformDetection.NetFx.cs @@ -0,0 +1,61 @@ +// 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 Xunit; +using Microsoft.Win32; +using System.Runtime.Versioning; + +namespace System +{ + public static partial class PlatformDetection + { + private static string FrameworkName => AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName; + private static Version TargetVersion => String.IsNullOrEmpty(FrameworkName) ? new Version(4, 5, 0, 0) : new FrameworkName(FrameworkName).Version; + + // The current full framework xunit runner is targeting 4.5.2 so we expect TargetsNetFx452OrLower to be true. + // When we update xunit runner in the future which may target recent framework version, TargetsNetFx452OrLower can start return + // false but we don't expect any code change though. + public static bool TargetsNetFx452OrLower => TargetVersion.CompareTo(new Version(4, 5, 3, 0)) < 0; + + public static bool IsNetfx462OrNewer => GetFrameworkVersion() >= new Version(4, 6, 2); + + public static bool IsNetfx470OrNewer => GetFrameworkVersion() >= new Version(4, 7, 0); + + public static bool IsNetfx471OrNewer => GetFrameworkVersion() >= new Version(4, 7, 1); + + // To get the framework version we can do it throught the registry key and getting the Release value under the .NET Framework key. + // the mapping to each version can be found in: https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed + // everytime we ship a new version this method should be updated to include the new framework version. + private static Version GetFrameworkVersion() + { + using (RegistryKey ndpKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full")) + { + if (ndpKey != null) + { + int value = (int)(ndpKey.GetValue("Release") ?? 0); + if (value >= 461308) + return new Version(4, 7, 1); + if (value >= 460798) + return new Version(4, 7, 0); + if (value >= 394802) + return new Version(4, 6, 2); + if (value >= 394254) + return new Version(4, 6, 1); + if (value >= 393295) + return new Version(4, 6, 0); + if (value >= 379893) + return new Version(4, 5, 2); + if (value >= 378675) + return new Version(4, 5, 1); + if (value >= 378389) + return new Version(4, 5, 0); + + throw new NotSupportedException($"No 4.5 or later framework version detected, framework key value: {value}"); + } + + throw new NotSupportedException(@"No registry key found under 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' to determine running framework version"); + } + } + } +} diff --git a/external/corefx/src/CoreFx.Private.TestUtilities/src/System/PlatformDetection.NonNetFx.cs b/external/corefx/src/CoreFx.Private.TestUtilities/src/System/PlatformDetection.NonNetFx.cs new file mode 100644 index 0000000000..087f2cf9a0 --- /dev/null +++ b/external/corefx/src/CoreFx.Private.TestUtilities/src/System/PlatformDetection.NonNetFx.cs @@ -0,0 +1,16 @@ +// 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 Xunit; + +namespace System +{ + public static partial class PlatformDetection + { + public static bool TargetsNetFx452OrLower => false; + public static bool IsNetfx462OrNewer => false; + public static bool IsNetfx470OrNewer => false; + public static bool IsNetfx471OrNewer => false; + } +} diff --git a/external/corefx/src/CoreFx.Private.TestUtilities/src/System/PlatformDetection.Unix.cs b/external/corefx/src/CoreFx.Private.TestUtilities/src/System/PlatformDetection.Unix.cs index abb86f9399..505aa371de 100644 --- a/external/corefx/src/CoreFx.Private.TestUtilities/src/System/PlatformDetection.Unix.cs +++ b/external/corefx/src/CoreFx.Private.TestUtilities/src/System/PlatformDetection.Unix.cs @@ -19,38 +19,37 @@ namespace System public static bool IsWindows8x => false; public static bool IsWindows10Version1607OrGreater => false; public static bool IsWindows10Version1703OrGreater => false; - public static bool IsWindows10InsiderPreviewBuild16215OrGreater => false; - public static bool IsWindows10Version16251OrGreater => false; + public static bool IsWindows10Version1709OrGreater => false; public static bool IsNotOneCoreUAP => true; - public static bool IsNetfx462OrNewer() { return false; } - public static bool IsNetfx470OrNewer() { return false; } - public static bool IsNetfx471OrNewer() { return false; } public static bool IsInAppContainer => false; public static int WindowsVersion => -1; + public static bool IsCentos6 => IsDistroAndVersion("centos", 6); public static bool IsOpenSUSE => IsDistroAndVersion("opensuse"); public static bool IsUbuntu => IsDistroAndVersion("ubuntu"); public static bool IsDebian => IsDistroAndVersion("debian"); - public static bool IsDebian8 => IsDistroAndVersion("debian", "8"); - public static bool IsUbuntu1404 => IsDistroAndVersion("ubuntu", "14.04"); - public static bool IsUbuntu1604 => IsDistroAndVersion("ubuntu", "16.04"); - public static bool IsUbuntu1704 => IsDistroAndVersion("ubuntu", "17.04"); - public static bool IsUbuntu1710 => IsDistroAndVersion("ubuntu", "17.10"); - public static bool IsCentos7 => IsDistroAndVersion("centos", "7"); + public static bool IsDebian8 => IsDistroAndVersion("debian", 8); + public static bool IsUbuntu1404 => IsDistroAndVersion("ubuntu", 14, 4); + public static bool IsUbuntu1604 => IsDistroAndVersion("ubuntu", 16, 4); + public static bool IsUbuntu1704 => IsDistroAndVersion("ubuntu", 17, 4); + public static bool IsUbuntu1710 => IsDistroAndVersion("ubuntu", 17, 10); public static bool IsTizen => IsDistroAndVersion("tizen"); - public static bool IsNotFedoraOrRedHatOrCentos => !IsDistroAndVersion("fedora") && !IsDistroAndVersion("rhel") && !IsDistroAndVersion("centos"); public static bool IsFedora => IsDistroAndVersion("fedora"); public static bool IsWindowsNanoServer => false; public static bool IsWindowsServerCore => false; public static bool IsWindowsAndElevated => false; - public static bool IsWindowsRedStone2 => false; - public static bool IsRedHat => IsDistroAndVersion("rhel") || IsDistroAndVersion("rhl"); - public static bool IsNotRedHat => !IsRedHat; - public static bool IsRedHat69 => IsDistroAndVersion("rhel", "6.9") || IsDistroAndVersion("rhl", "6.9"); - public static bool IsNotRedHat69 => !IsRedHat69; + // RedHat family covers RedHat and CentOS + public static bool IsRedHatFamily => IsRedHatFamilyAndVersion(); + public static bool IsNotRedHatFamily => !IsRedHatFamily; + public static bool IsRedHatFamily6 => IsRedHatFamilyAndVersion(6); + public static bool IsNotRedHatFamily6 => !IsRedHatFamily6; + public static bool IsRedHatFamily7 => IsRedHatFamilyAndVersion(7); + public static bool IsNotFedoraOrRedHatFamily => !IsFedora && !IsRedHatFamily; - public static Version OSXKernelVersion { get; } = GetOSXKernelVersion(); + public static Version OSXVersion { get; } = ToVersion(Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystemVersion); + + public static Version OpenSslVersion => RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? Interop.OpenSsl.OpenSslVersion : throw new PlatformNotSupportedException(); public static string GetDistroVersionString() { @@ -59,9 +58,9 @@ namespace System return "OSX Version=" + s_osxProductVersion.ToString(); } - DistroInfo v = ParseOsReleaseFile(); + DistroInfo v = GetDistroInfo(); - return "Distro=" + v.Id + " VersionId=" + v.VersionId + " Pretty=" + v.PrettyName + " Version=" + v.Version; + return "Distro=" + v.Id + " VersionId=" + v.VersionId; } private static readonly Version s_osxProductVersion = GetOSXProductVersion(); @@ -81,131 +80,25 @@ namespace System ver >> 24); } - private static DistroInfo ParseOsReleaseFile() + static Version ToVersion(string versionString) { - Debug.Assert(RuntimeInformation.IsOSPlatform(OSPlatform.Linux)); + if (versionString.IndexOf('.') != -1) + return new Version(versionString); - DistroInfo ret = new DistroInfo(); - ret.Id = ""; - ret.VersionId = ""; - ret.Version = ""; - ret.PrettyName = ""; - - if (File.Exists("/etc/os-release")) - { - foreach (string line in File.ReadLines("/etc/os-release")) - { - if (line.StartsWith("ID=", System.StringComparison.Ordinal)) - { - ret.Id = RemoveQuotes(line.Substring("ID=".Length)); - } - else if (line.StartsWith("VERSION_ID=", System.StringComparison.Ordinal)) - { - ret.VersionId = RemoveQuotes(line.Substring("VERSION_ID=".Length)); - } - else if (line.StartsWith("VERSION=", System.StringComparison.Ordinal)) - { - ret.Version = RemoveQuotes(line.Substring("VERSION=".Length)); - } - else if (line.StartsWith("PRETTY_NAME=", System.StringComparison.Ordinal)) - { - ret.PrettyName = RemoveQuotes(line.Substring("PRETTY_NAME=".Length)); - } - } - } - else - { - string fileName = null; - if (File.Exists("/etc/redhat-release")) - fileName = "/etc/redhat-release"; - - if (fileName == null && File.Exists("/etc/system-release")) - fileName = "/etc/system-release"; - - if (fileName != null) - { - // Parse the format like the following line: - // Red Hat Enterprise Linux Server release 7.3 (Maipo) - using (StreamReader file = new StreamReader(fileName)) - { - string line = file.ReadLine(); - if (!String.IsNullOrEmpty(line)) - { - if (line.StartsWith("Red Hat Enterprise Linux", StringComparison.OrdinalIgnoreCase)) - { - ret.Id = "rhel"; - } - else if (line.StartsWith("Centos", StringComparison.OrdinalIgnoreCase)) - { - ret.Id = "centos"; - } - else if (line.StartsWith("Red Hat", StringComparison.OrdinalIgnoreCase)) - { - ret.Id = "rhl"; - } - else - { - // automatically generate the distro label - string [] words = line.Split(' '); - StringBuilder sb = new StringBuilder(); - - foreach (string word in words) - { - if (word.Length > 0) - { - if (Char.IsNumber(word[0]) || - word.Equals("release", StringComparison.OrdinalIgnoreCase) || - word.Equals("server", StringComparison.OrdinalIgnoreCase)) - { - break; - } - sb.Append(Char.ToLower(word[0])); - } - } - ret.Id = sb.ToString(); - } - - int i = 0; - while (i < line.Length && !Char.IsNumber(line[i])) // stop at first number - i++; - - if (i < line.Length) - { - int j = i + 1; - while (j < line.Length && (Char.IsNumber(line[j]) || line[j] == '.')) - j++; - - ret.VersionId = line.Substring(i, j - i); - ret.Version = line.Substring(i, line.Length - i); - } - - ret.PrettyName = line; - } - } - } - } - - return ret; + // minor version is required by Version + // let's default it to 0 + return new Version(int.Parse(versionString), 0); } - private static string RemoveQuotes(string s) + private static DistroInfo GetDistroInfo() => new DistroInfo() { - s = s.Trim(); - if (s.Length >= 2 && s[0] == '"' && s[s.Length - 1] == '"') - { - // Remove quotes. - s = s.Substring(1, s.Length - 2); - } + Id = Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystem, + VersionId = ToVersion(Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystemVersion) + }; - return s; - } - - private struct DistroInfo + private static bool IsRedHatFamilyAndVersion(int major = -1, int minor = -1, int build = -1, int revision = -1) { - public string Id { get; set; } - public string VersionId { get; set; } - public string Version { get; set; } - public string PrettyName { get; set; } + return IsDistroAndVersion((distro) => distro == "rhel" || distro == "centos", major, minor, build, revision); } /// @@ -214,12 +107,17 @@ namespace System /// The distribution id. /// The distro version. If omitted, compares the distro only. /// Whether the OS platform matches the given Linux distro and optional version. - private static bool IsDistroAndVersion(string distroId, string versionId = null) + private static bool IsDistroAndVersion(string distroId, int major = -1, int minor = -1, int build = -1, int revision = -1) + { + return IsDistroAndVersion((distro) => distro == distroId, major, minor, build, revision); + } + + private static bool IsDistroAndVersion(Predicate distroPredicate, int major = -1, int minor = -1, int build = -1, int revision = -1) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - DistroInfo v = ParseOsReleaseFile(); - if (v.Id == distroId && (versionId == null || v.VersionId == versionId)) + DistroInfo v = GetDistroInfo(); + if (distroPredicate(v.Id) && VersionEquivalentWith(major, minor, build, revision, v.VersionId)) { return true; } @@ -228,18 +126,12 @@ namespace System return false; } - private static Version GetOSXKernelVersion() + private static bool VersionEquivalentWith(int major, int minor, int build, int revision, Version actualVersionId) { - if (IsOSX) - { - byte[] bytes = new byte[256]; - IntPtr bytesLength = new IntPtr(bytes.Length); - Assert.Equal(0, sysctlbyname("kern.osrelease", bytes, ref bytesLength, null, IntPtr.Zero)); - string versionString = Encoding.UTF8.GetString(bytes); - return Version.Parse(versionString); - } - - return new Version(0, 0, 0); + return (major == -1 || major == actualVersionId.Major) + && (minor == -1 || minor == actualVersionId.Minor) + && (build == -1 || build == actualVersionId.Build) + && (revision == -1 || revision == actualVersionId.Revision); } private static Version GetOSXProductVersion() @@ -302,5 +194,11 @@ namespace System private static extern int GlobalizationNative_GetICUVersion(); public static bool IsSuperUser => geteuid() == 0; + + private struct DistroInfo + { + public string Id { get; set; } + public Version VersionId { get; set; } + } } } diff --git a/external/corefx/src/CoreFx.Private.TestUtilities/src/System/PlatformDetection.Windows.cs b/external/corefx/src/CoreFx.Private.TestUtilities/src/System/PlatformDetection.Windows.cs index ce31c9965a..0267fa2145 100644 --- a/external/corefx/src/CoreFx.Private.TestUtilities/src/System/PlatformDetection.Windows.cs +++ b/external/corefx/src/CoreFx.Private.TestUtilities/src/System/PlatformDetection.Windows.cs @@ -14,8 +14,10 @@ namespace System { public static partial class PlatformDetection { - public static Version OSXKernelVersion => throw new PlatformNotSupportedException(); + public static Version OSXVersion => throw new PlatformNotSupportedException(); + public static Version OpenSslVersion => throw new PlatformNotSupportedException(); public static bool IsSuperUser => throw new PlatformNotSupportedException(); + public static bool IsCentos6 => false; public static bool IsOpenSUSE => false; public static bool IsUbuntu => false; public static bool IsDebian => false; @@ -24,30 +26,26 @@ namespace System public static bool IsUbuntu1604 => false; public static bool IsUbuntu1704 => false; public static bool IsUbuntu1710 => false; - public static bool IsCentos7 => false; public static bool IsTizen => false; - public static bool IsNotFedoraOrRedHatOrCentos => true; + public static bool IsNotFedoraOrRedHatFamily => true; public static bool IsFedora => false; public static bool IsWindowsNanoServer => (IsNotWindowsIoTCore && GetInstallationType().Equals("Nano Server", StringComparison.OrdinalIgnoreCase)); public static bool IsWindowsServerCore => GetInstallationType().Equals("Server Core", StringComparison.OrdinalIgnoreCase); public static int WindowsVersion => GetWindowsVersion(); public static bool IsMacOsHighSierraOrHigher { get; } = false; public static Version ICUVersion => new Version(0, 0, 0, 0); - public static bool IsRedHat => false; - public static bool IsNotRedHat => true; - public static bool IsRedHat69 => false; - public static bool IsNotRedHat69 => true; + public static bool IsRedHatFamily => false; + public static bool IsNotRedHatFamily => true; + public static bool IsRedHatFamily6 => false; + public static bool IsRedHatFamily7 => false; + public static bool IsNotRedHatFamily6 => true; public static bool IsWindows10Version1607OrGreater => GetWindowsVersion() == 10 && GetWindowsMinorVersion() == 0 && GetWindowsBuildNumber() >= 14393; public static bool IsWindows10Version1703OrGreater => GetWindowsVersion() == 10 && GetWindowsMinorVersion() == 0 && GetWindowsBuildNumber() >= 15063; - public static bool IsWindows10InsiderPreviewBuild16215OrGreater => - GetWindowsVersion() == 10 && GetWindowsMinorVersion() == 0 && GetWindowsBuildNumber() >= 16215; - public static bool IsWindows10Version16251OrGreater => - GetWindowsVersion() == 10 && GetWindowsMinorVersion() == 0 && GetWindowsBuildNumber() >= 16251; - public static bool IsWindowsRedStone2 => // Creators Update version - GetWindowsVersion() == 10 && (GetWindowsBuildNumber() / 1000) == 15; // any build with 15xxx. e.g 15063 + public static bool IsWindows10Version1709OrGreater => + GetWindowsVersion() == 10 && GetWindowsMinorVersion() == 0 && GetWindowsBuildNumber() >= 16299; // Windows OneCoreUAP SKU doesn't have httpapi.dll public static bool IsNotOneCoreUAP => @@ -71,60 +69,6 @@ namespace System public static bool IsWindows7 => GetWindowsVersion() == 6 && GetWindowsMinorVersion() == 1; public static bool IsWindows8x => GetWindowsVersion() == 6 && (GetWindowsMinorVersion() == 2 || GetWindowsMinorVersion() == 3); - public static bool IsNetfx462OrNewer() - { - if (!IsFullFramework) - { - return false; - } - - Version net462 = new Version(4, 6, 2); - Version runningVersion = GetFrameworkVersion(); - return runningVersion != null && runningVersion >= net462; - } - - public static bool IsNetfx470OrNewer() - { - if (!IsFullFramework) - { - return false; - } - - Version net470 = new Version(4, 7, 0); - Version runningVersion = GetFrameworkVersion(); - return runningVersion != null && runningVersion >= net470; - } - - public static bool IsNetfx471OrNewer() - { - if (!IsFullFramework) - { - return false; - } - - Version net471 = new Version(4, 7, 1); - Version runningVersion = GetFrameworkVersion(); - return runningVersion != null && runningVersion >= net471; - } - - private static Version GetFrameworkVersion() - { - string[] descriptionArray = RuntimeInformation.FrameworkDescription.Split(' '); - if (descriptionArray.Length < 3) - return null; - - if (!Version.TryParse(descriptionArray[2], out Version actualVersion)) - return null; - - foreach (Range currentRange in FrameworkRanges) - { - if (currentRange.IsInRange(actualVersion)) - return currentRange.FrameworkVersion; - } - - return null; - } - public static string GetDistroVersionString() { return "ProductType=" + GetWindowsProductType() + "InstallationType=" + GetInstallationType(); } private static int s_isInAppContainer = -1; diff --git a/external/corefx/src/CoreFx.Private.TestUtilities/src/System/PlatformDetection.cs b/external/corefx/src/CoreFx.Private.TestUtilities/src/System/PlatformDetection.cs index db9816cdd7..23d2309c69 100644 --- a/external/corefx/src/CoreFx.Private.TestUtilities/src/System/PlatformDetection.cs +++ b/external/corefx/src/CoreFx.Private.TestUtilities/src/System/PlatformDetection.cs @@ -24,6 +24,7 @@ namespace System public static bool IsNetNative => RuntimeInformation.FrameworkDescription.StartsWith(".NET Native", StringComparison.OrdinalIgnoreCase); public static bool IsNetCore => RuntimeInformation.FrameworkDescription.StartsWith(".NET Core", StringComparison.OrdinalIgnoreCase); public static bool IsOSX => RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + public static bool IsFreeBSD => RuntimeInformation.IsOSPlatform(OSPlatform.Create("FREEBSD")); public static bool IsNetBSD => RuntimeInformation.IsOSPlatform(OSPlatform.Create("NETBSD")); public static bool IsNotWindows8x => !IsWindows8x; public static bool IsNotWindowsNanoServer => !IsWindowsNanoServer; @@ -38,7 +39,9 @@ namespace System public static bool IsNotWinRTSupported => !IsWinRTSupported; public static bool IsNotMacOsHighSierraOrHigher => !IsMacOsHighSierraOrHigher; - // Officially, .Net Native only supports processes running in an AppContainer. However, the majority of tests still work fine + public static bool IsDomainJoinedMachine => !Environment.MachineName.Equals(Environment.UserDomainName, StringComparison.OrdinalIgnoreCase); + + // Officially, .Net Native only supports processes running in an AppContainer. However, the majority of tests still work fine // in a normal Win32 process and we often do so as running in an AppContainer imposes a substantial tax in debuggability // and investigatability. This predicate is used in ConditionalFacts to disable the specific tests that really need to be // running in AppContainer when running on .NetNative. @@ -101,33 +104,5 @@ namespace System // System.Security.Cryptography.Xml.XmlDsigXsltTransform.GetOutput() relies on XslCompiledTransform which relies // heavily on Reflection.Emit public static bool IsXmlDsigXsltTransformSupported => !PlatformDetection.IsUap; - - public static Range[] FrameworkRanges => new Range[]{ - new Range(new Version(4, 7, 2500, 0), null, new Version(4, 7, 1)), - new Range(new Version(4, 6, 2000, 0), new Version(4, 7, 2090, 0), new Version(4, 7, 0)), - new Range(new Version(4, 6, 1500, 0), new Version(4, 6, 1999, 0), new Version(4, 6, 2)), - new Range(new Version(4, 6, 1000, 0), new Version(4, 6, 1499, 0), new Version(4, 6, 1)), - new Range(new Version(4, 6, 55, 0), new Version(4, 6, 999, 0), new Version(4, 6, 0)), - new Range(new Version(4, 0, 30319, 0), new Version(4, 0, 52313, 36313), new Version(4, 5, 2)) - }; - - public class Range - { - public Version Start { get; private set; } - public Version Finish { get; private set; } - public Version FrameworkVersion { get; private set; } - - public Range(Version start, Version finish, Version frameworkVersion) - { - Start = start; - Finish = finish; - FrameworkVersion = frameworkVersion; - } - - public bool IsInRange(Version version) - { - return version >= Start && (Finish == null || version <= Finish); - } - } } } diff --git a/external/corefx/src/Microsoft.CSharp/Microsoft.CSharp.sln b/external/corefx/src/Microsoft.CSharp/Microsoft.CSharp.sln index 814d7a6785..15f386abe3 100644 --- a/external/corefx/src/Microsoft.CSharp/Microsoft.CSharp.sln +++ b/external/corefx/src/Microsoft.CSharp/Microsoft.CSharp.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.27004.2009 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CSharp.Tests", "tests\Microsoft.CSharp.Tests.csproj", "{82B54697-0251-47A1-8546-FC507D0F3B08}" ProjectSection(ProjectDependencies) = postProject @@ -26,10 +26,10 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {82B54697-0251-47A1-8546-FC507D0F3B08}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU - {82B54697-0251-47A1-8546-FC507D0F3B08}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU - {82B54697-0251-47A1-8546-FC507D0F3B08}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU - {82B54697-0251-47A1-8546-FC507D0F3B08}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {82B54697-0251-47A1-8546-FC507D0F3B08}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {82B54697-0251-47A1-8546-FC507D0F3B08}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {82B54697-0251-47A1-8546-FC507D0F3B08}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {82B54697-0251-47A1-8546-FC507D0F3B08}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU {96AA2060-C846-4E56-9509-E8CB9C114C8F}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU {96AA2060-C846-4E56-9509-E8CB9C114C8F}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU {96AA2060-C846-4E56-9509-E8CB9C114C8F}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU @@ -47,4 +47,7 @@ Global {96AA2060-C846-4E56-9509-E8CB9C114C8F} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD} {1427906B-FF3D-422A-8278-F2B7E89DE12A} = {2E666815-2EDB-464B-9DF6-380BF4789AD4} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {699239FB-3677-4369-9E97-09ED5E232491} + EndGlobalSection EndGlobal diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft.CSharp.csproj b/external/corefx/src/Microsoft.CSharp/src/Microsoft.CSharp.csproj index c65e276ef8..1390478839 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft.CSharp.csproj +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft.CSharp.csproj @@ -8,7 +8,6 @@ true - @@ -42,7 +41,6 @@ - @@ -83,7 +81,6 @@ - @@ -108,7 +105,6 @@ - @@ -119,11 +115,10 @@ + - - @@ -132,8 +127,6 @@ - - @@ -146,7 +139,6 @@ - diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ArgumentObject.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ArgumentObject.cs index 87dabd6eab..b07d2cc4c3 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ArgumentObject.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ArgumentObject.cs @@ -1,4 +1,9 @@ +// 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.Diagnostics; namespace Microsoft.CSharp.RuntimeBinder { @@ -7,7 +12,7 @@ namespace Microsoft.CSharp.RuntimeBinder // value's type because unless the static time type was dynamic, we want to use the // static time type. Also, we may have null values, in which case we would not be // able to get the type. - internal struct ArgumentObject + internal readonly struct ArgumentObject { internal readonly object Value; internal readonly CSharpArgumentInfo Info; @@ -15,9 +20,10 @@ namespace Microsoft.CSharp.RuntimeBinder public ArgumentObject(object value, CSharpArgumentInfo info, Type type) { + Debug.Assert(type != null); Value = value; Info = info; Type = type; } } -} \ No newline at end of file +} diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/BinderHelper.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/BinderHelper.cs index f118d86f90..4d114c3ea0 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/BinderHelper.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/BinderHelper.cs @@ -384,7 +384,7 @@ namespace Microsoft.CSharp.RuntimeBinder ///////////////////////////////////////////////////////////////////////////////// - internal static CallInfo CreateCallInfo(IEnumerable argInfos, int discard) + internal static CallInfo CreateCallInfo(ref IEnumerable argInfos, int discard) { // This function converts the C# Binder's notion of argument information to the // DLR's notion. The DLR counts arguments differently than C#. Here are some @@ -402,8 +402,9 @@ namespace Microsoft.CSharp.RuntimeBinder int argCount = 0; List argNames = new List(); - - foreach (CSharpArgumentInfo info in argInfos) + CSharpArgumentInfo[] infoArray = ToArray(argInfos); + argInfos = infoArray; // Write back the array to allow single enumeration. + foreach (CSharpArgumentInfo info in infoArray) { if (info.NamedArgument) { diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpGetIndexBinder.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpGetIndexBinder.cs index 142210d121..be3f82853f 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpGetIndexBinder.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpGetIndexBinder.cs @@ -48,10 +48,10 @@ namespace Microsoft.CSharp.RuntimeBinder public CSharpGetIndexBinder( Type callingContext, IEnumerable argumentInfo) : - base(BinderHelper.CreateCallInfo(argumentInfo, 1)) // discard 1 argument: the target object + base(BinderHelper.CreateCallInfo(ref argumentInfo, 1)) // discard 1 argument: the target object { CallingContext = callingContext; - _argumentInfo = BinderHelper.ToArray(argumentInfo); + _argumentInfo = argumentInfo as CSharpArgumentInfo[]; _binder = RuntimeBinder.GetInstance(); } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpInvokeBinder.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpInvokeBinder.cs index edf6249cb2..4a7fa730bd 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpInvokeBinder.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpInvokeBinder.cs @@ -57,11 +57,11 @@ namespace Microsoft.CSharp.RuntimeBinder CSharpCallFlags flags, Type callingContext, IEnumerable argumentInfo) : - base(BinderHelper.CreateCallInfo(argumentInfo, 1)) // discard 1 argument: the target object (even if static, arg is type) + base(BinderHelper.CreateCallInfo(ref argumentInfo, 1)) // discard 1 argument: the target object (even if static, arg is type) { _flags = flags; CallingContext = callingContext; - _argumentInfo = BinderHelper.ToArray(argumentInfo); + _argumentInfo = argumentInfo as CSharpArgumentInfo[]; _binder = RuntimeBinder.GetInstance(); } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpInvokeMemberBinder.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpInvokeMemberBinder.cs index b12f3523cd..b5fbbd0d3d 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpInvokeMemberBinder.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpInvokeMemberBinder.cs @@ -65,7 +65,7 @@ namespace Microsoft.CSharp.RuntimeBinder Type callingContext, IEnumerable typeArguments, IEnumerable argumentInfo) : - base(name, false, BinderHelper.CreateCallInfo(argumentInfo, 1)) // discard 1 argument: the target object (even if static, arg is type) + base(name, false, BinderHelper.CreateCallInfo(ref argumentInfo, 1)) // discard 1 argument: the target object (even if static, arg is type) { Flags = flags; CallingContext = callingContext; diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpSetIndexBinder.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpSetIndexBinder.cs index 39228ad914..37bd58aba4 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpSetIndexBinder.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/CSharpSetIndexBinder.cs @@ -53,12 +53,12 @@ namespace Microsoft.CSharp.RuntimeBinder bool isChecked, Type callingContext, IEnumerable argumentInfo) : - base(BinderHelper.CreateCallInfo(argumentInfo, 2)) // discard 2 arguments: the target object and the value + base(BinderHelper.CreateCallInfo(ref argumentInfo, 2)) // discard 2 arguments: the target object and the value { IsCompoundAssignment = isCompoundAssignment; IsChecked = isChecked; CallingContext = callingContext; - _argumentInfo = BinderHelper.ToArray(argumentInfo); + _argumentInfo = argumentInfo as CSharpArgumentInfo[]; _binder = RuntimeBinder.GetInstance(); } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Error.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Error.cs index 0fd09edc81..34e6a16154 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Error.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Error.cs @@ -13,31 +13,6 @@ namespace Microsoft.CSharp.RuntimeBinder return new RuntimeBinderInternalCompilerException(SR.InternalCompilerError); } - internal static Exception BindRequireArguments() - { - return new ArgumentException(SR.BindRequireArguments); - } - - internal static Exception BindCallFailedOverloadResolution() - { - return new RuntimeBinderException(SR.BindCallFailedOverloadResolution); - } - - internal static Exception BindBinaryOperatorRequireTwoArguments() - { - return new ArgumentException(SR.BindBinaryOperatorRequireTwoArguments); - } - - internal static Exception BindUnaryOperatorRequireOneArgument() - { - return new ArgumentException(SR.BindUnaryOperatorRequireOneArgument); - } - - internal static Exception BindBinaryAssignmentRequireTwoArguments() - { - return new ArgumentException(SR.BindBinaryAssignmentRequireTwoArguments); - } - internal static Exception BindPropertyFailedMethodGroup(object p0) { return new RuntimeBinderException(SR.Format(SR.BindPropertyFailedMethodGroup, p0)); @@ -56,11 +31,6 @@ namespace Microsoft.CSharp.RuntimeBinder internal static Exception BindStaticRequiresType(string paramName) => new ArgumentException(SR.TypeArgumentRequiredForStaticCall, paramName); - internal static Exception BindBinaryAssignmentFailedNullReference() - { - return new RuntimeBinderException(SR.BindBinaryAssignmentFailedNullReference); - } - internal static Exception NullReferenceOnMemberException() { return new RuntimeBinderException(SR.NullReferenceOnMemberException); diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Errors/ErrorCode.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Errors/ErrorCode.cs index 4e6263721d..c762ace62d 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Errors/ErrorCode.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Errors/ErrorCode.cs @@ -21,17 +21,13 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors ERR_ObjectRequired = 120, ERR_AmbigCall = 121, ERR_BadAccess = 122, - ERR_MethDelegateMismatch = 123, ERR_AssgLvalueExpected = 131, ERR_NoConstructors = 143, ERR_PropertyLacksGet = 154, ERR_ObjectProhibited = 176, ERR_AssgReadonly = 191, - ERR_RefReadonly = 192, ERR_AssgReadonlyStatic = 198, - ERR_RefReadonlyStatic = 199, ERR_AssgReadonlyProp = 200, - ERR_RefProperty = 206, ERR_UnsafeNeeded = 214, ERR_BadBoolOp = 217, ERR_MustHaveOpTF = 218, @@ -58,27 +54,20 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors ERR_IncrementLvalueExpected = 1059, ERR_BadArgCount = 1501, ERR_BadArgTypes = 1502, - ERR_RefLvalueExpected = 1510, ERR_BadProtectedAccess = 1540, ERR_BindToBogusProp2 = 1545, ERR_BindToBogusProp1 = 1546, ERR_BadDelArgCount = 1593, ERR_BadDelArgTypes = 1594, - ERR_AssgReadonlyLocal = 1604, - ERR_RefReadonlyLocal = 1605, ERR_ReturnNotLValue = 1612, ERR_AssgReadonly2 = 1648, - ERR_RefReadonly2 = 1649, ERR_AssgReadonlyStatic2 = 1650, - ERR_RefReadonlyStatic2 = 1651, - ERR_AssgReadonlyLocalCause = 1656, - ERR_RefReadonlyLocalCause = 1657, ERR_BadCtorArgCount = 1729, + ERR_BadNamedArgument = 1739, + ERR_DuplicateNamedArgument = 1740, + ERR_NamedArgumentUsedInPositional = 1744, + ERR_BadNamedArgumentForDelegateInvoke = 1746, ERR_NonInvocableMemberCalled = 1955, - ERR_NamedArgumentSpecificationBeforeFixedArgument = 5002, - ERR_BadNamedArgument = 5003, - ERR_BadNamedArgumentForDelegateInvoke = 5004, - ERR_DuplicateNamedArgument = 5005, - ERR_NamedArgumentUsedInPositional = 5006, + ERR_BadNonTrailingNamedArgument = 8323 } } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Errors/ErrorFacts.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Errors/ErrorFacts.cs index 930ef573c6..81c4f736d2 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Errors/ErrorFacts.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Errors/ErrorFacts.cs @@ -59,9 +59,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors case ErrorCode.ERR_BadAccess: codeStr = SR.BadAccess; break; - case ErrorCode.ERR_MethDelegateMismatch: - codeStr = SR.MethDelegateMismatch; - break; case ErrorCode.ERR_AssgLvalueExpected: codeStr = SR.AssgLvalueExpected; break; @@ -77,21 +74,12 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors case ErrorCode.ERR_AssgReadonly: codeStr = SR.AssgReadonly; break; - case ErrorCode.ERR_RefReadonly: - codeStr = SR.RefReadonly; - break; case ErrorCode.ERR_AssgReadonlyStatic: codeStr = SR.AssgReadonlyStatic; break; - case ErrorCode.ERR_RefReadonlyStatic: - codeStr = SR.RefReadonlyStatic; - break; case ErrorCode.ERR_AssgReadonlyProp: codeStr = SR.AssgReadonlyProp; break; - case ErrorCode.ERR_RefProperty: - codeStr = SR.RefProperty; - break; case ErrorCode.ERR_UnsafeNeeded: codeStr = SR.UnsafeNeeded; break; @@ -170,9 +158,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors case ErrorCode.ERR_BadArgTypes: codeStr = SR.BadArgTypes; break; - case ErrorCode.ERR_RefLvalueExpected: - codeStr = SR.RefLvalueExpected; - break; case ErrorCode.ERR_BadProtectedAccess: codeStr = SR.BadProtectedAccess; break; @@ -188,42 +173,21 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors case ErrorCode.ERR_BadDelArgTypes: codeStr = SR.BadDelArgTypes; break; - case ErrorCode.ERR_AssgReadonlyLocal: - codeStr = SR.AssgReadonlyLocal; - break; - case ErrorCode.ERR_RefReadonlyLocal: - codeStr = SR.RefReadonlyLocal; - break; case ErrorCode.ERR_ReturnNotLValue: codeStr = SR.ReturnNotLValue; break; case ErrorCode.ERR_AssgReadonly2: codeStr = SR.AssgReadonly2; break; - case ErrorCode.ERR_RefReadonly2: - codeStr = SR.RefReadonly2; - break; case ErrorCode.ERR_AssgReadonlyStatic2: codeStr = SR.AssgReadonlyStatic2; break; - case ErrorCode.ERR_RefReadonlyStatic2: - codeStr = SR.RefReadonlyStatic2; - break; - case ErrorCode.ERR_AssgReadonlyLocalCause: - codeStr = SR.AssgReadonlyLocalCause; - break; - case ErrorCode.ERR_RefReadonlyLocalCause: - codeStr = SR.RefReadonlyLocalCause; - break; case ErrorCode.ERR_BadCtorArgCount: codeStr = SR.BadCtorArgCount; break; case ErrorCode.ERR_NonInvocableMemberCalled: codeStr = SR.NonInvocableMemberCalled; break; - case ErrorCode.ERR_NamedArgumentSpecificationBeforeFixedArgument: - codeStr = SR.NamedArgumentSpecificationBeforeFixedArgument; - break; case ErrorCode.ERR_BadNamedArgument: codeStr = SR.BadNamedArgument; break; @@ -236,6 +200,10 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors case ErrorCode.ERR_NamedArgumentUsedInPositional: codeStr = SR.NamedArgumentUsedInPositional; break; + case ErrorCode.ERR_BadNonTrailingNamedArgument: + codeStr = SR.BadNonTrailingNamedArgument; + break; + default: // means missing resources match the code entry Debug.Assert(false, "Missing resources for the error " + code.ToString()); diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Errors/ErrorFmt.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Errors/ErrorFmt.cs index 80beda422b..92f89f9fcd 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Errors/ErrorFmt.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Errors/ErrorFmt.cs @@ -17,20 +17,13 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors internal enum ErrArgKind { Int, - Hresult, - Ids, SymKind, Sym, Type, Name, Str, - PredefName, - LocNode, - Ptr, SymWithType, MethWithInst, - Expr, - Lim } [Flags] @@ -59,7 +52,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors { public ErrArgKind eak; public ErrArgFlags eaf; - internal MessageID ids; internal int n; internal SYMKIND sk; internal Name name; @@ -184,16 +176,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors } } - internal sealed class ErrArgIds : ErrArg - { - public ErrArgIds(MessageID ids) - { - this.eak = ErrArgKind.Ids; - this.eaf = ErrArgFlags.None; - this.ids = ids; - } - } - internal sealed class ErrArgSymKind : ErrArg { public ErrArgSymKind(Symbol sym) diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Errors/UserStringBuilder.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Errors/UserStringBuilder.cs index 0c9b94e586..ffa7518cb9 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Errors/UserStringBuilder.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Errors/UserStringBuilder.cs @@ -81,7 +81,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors * Does NOT include () */ - private void ErrAppendParamList(TypeArray @params, bool isVarargs, bool isParamArray) + private void ErrAppendParamList(TypeArray @params, bool isParamArray) { if (null == @params) return; @@ -101,16 +101,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors // parameter type name ErrAppendType(@params[i], null); } - - if (isVarargs) - { - if (@params.Count != 0) - { - ErrAppendString(", "); - } - - ErrAppendString("..."); - } } private void ErrAppendString(string str) @@ -139,12 +129,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors } } - private void ErrAppendMethodParentSym(MethodSymbol sym, SubstContext pcxt, out TypeArray substMethTyParams) - { - substMethTyParams = null; - ErrAppendParentSym(sym, pcxt); - } - private void ErrAppendParentSym(Symbol sym, SubstContext pctx) { ErrAppendParentCore(sym.parent, pctx); @@ -152,11 +136,10 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors private void ErrAppendParentCore(Symbol parent, SubstContext pctx) { - if (null == parent) - return; - - if (parent == getBSymmgr().GetRootNS()) + if (parent == null || parent == NamespaceSymbol.Root) + { return; + } if (pctx != null && !pctx.FNop() && parent is AggregateSymbol agg && 0 != agg.GetTypeVarsAll().Count) { @@ -192,7 +175,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors ErrAppendParentSym(meth, pctx); // Get the type args from the explicit impl type and substitute using pctx (if there is one). - SubstContext ctx = new SubstContext(GetTypeManager().SubstType(meth.swtSlot.GetType(), pctx) as AggregateType); + SubstContext ctx = new SubstContext(GetTypeManager().SubstType(meth.swtSlot.GetType(), pctx)); ErrAppendSym(meth.swtSlot.Sym, ctx, fArgs); // args already added @@ -243,8 +226,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors return; } - TypeArray replacementTypeArray = null; - ErrAppendMethodParentSym(meth, pctx, out replacementTypeArray); + ErrAppendParentSym(meth, pctx); if (meth.IsConstructor()) { // Use the name of the parent class instead of the name "". @@ -272,28 +254,20 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors ErrAppendString("operator "); ErrAppendString(Operators.OperatorOfMethodName(meth.name)); } - else if (meth.IsExpImpl()) - { - if (meth.errExpImpl != null) - ErrAppendType(meth.errExpImpl, pctx, fArgs); - } - else + else if (!meth.IsExpImpl()) { // regular method ErrAppendName(meth.name); } - if (null == replacementTypeArray) - { - ErrAppendTypeParameters(meth.typeVars, pctx, false); - } + ErrAppendTypeParameters(meth.typeVars, pctx, false); if (fArgs) { // append argument types ErrAppendChar('('); - ErrAppendParamList(GetTypeManager().SubstTypeArray(meth.Params, pctx), meth.isVarargs, meth.isParamArray); + ErrAppendParamList(GetTypeManager().SubstTypeArray(meth.Params, pctx), meth.isParamArray); ErrAppendChar(')'); } @@ -302,22 +276,20 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors private void ErrAppendIndexer(IndexerSymbol indexer, SubstContext pctx) { ErrAppendString("this["); - ErrAppendParamList(GetTypeManager().SubstTypeArray(indexer.Params, pctx), false, indexer.isParamArray); + ErrAppendParamList(GetTypeManager().SubstTypeArray(indexer.Params, pctx), indexer.isParamArray); ErrAppendChar(']'); } private void ErrAppendProperty(PropertySymbol prop, SubstContext pctx) { ErrAppendParentSym(prop, pctx); - if (prop.IsExpImpl() && prop.swtSlot.Sym != null) + if (prop.IsExpImpl()) { - SubstContext ctx = new SubstContext(GetTypeManager().SubstType(prop.swtSlot.GetType(), pctx) as AggregateType); - ErrAppendSym(prop.swtSlot.Sym, ctx); - } - else if (prop.IsExpImpl()) - { - if (prop.errExpImpl != null) - ErrAppendType(prop.errExpImpl, pctx, false); - if (prop is IndexerSymbol indexer) + if (prop.swtSlot.Sym != null) + { + SubstContext ctx = new SubstContext(GetTypeManager().SubstType(prop.swtSlot.GetType(), pctx)); + ErrAppendSym(prop.swtSlot.Sym, ctx); + } + else if (prop is IndexerSymbol indexer) { ErrAppendChar('.'); ErrAppendIndexer(indexer, pctx); @@ -391,9 +363,8 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors ErrAppendEvent((EventSymbol)sym, pctx); break; - case SYMKIND.SK_AssemblyQualifiedNamespaceSymbol: case SYMKIND.SK_NamespaceSymbol: - if (sym == getBSymmgr().GetRootNS()) + if (sym == NamespaceSymbol.Root) { ErrAppendId(MessageID.GlobalNamespace); } @@ -502,22 +473,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors } break; - case TypeKind.TK_ErrorType: - ErrorType err = (ErrorType)pType; - if (err.HasParent) - { - Debug.Assert(err.nameText != null && err.typeArgs != null); - ErrAppendName(err.nameText); - ErrAppendTypeParameters(err.typeArgs, pctx, true); - } - else - { - // Load the string "". - Debug.Assert(null == err.typeArgs); - ErrAppendId(MessageID.ERRORSYM); - } - break; - case TypeKind.TK_NullType: // Load the string "". ErrAppendId(MessageID.NULL); @@ -574,7 +529,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors } case TypeKind.TK_VoidType: - ErrAppendName(GetNameManager().Lookup(TokenFacts.GetText(TokenKind.Void))); + ErrAppendName(NameManager.GetPredefinedName(PredefinedName.PN_VOID)); break; case TypeKind.TK_ParameterModifierType: @@ -616,9 +571,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors switch (parg.eak) { - case ErrArgKind.Ids: - ErrId(out psz, parg.ids); - break; case ErrArgKind.SymKind: ErrSK(out psz, parg.sk); break; @@ -675,26 +627,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Errors return result; } - private NameManager GetNameManager() - { - return m_globalSymbols.GetNameManager(); - } - private TypeManager GetTypeManager() { return m_globalSymbols.GetTypes(); } - private BSYMMGR getBSymmgr() - { - return m_globalSymbols.GetGlobalSymbols(); - } - - private int GetTypeID(CType type) - { - return 0; - } - private void ErrId(out string s, MessageID id) { s = ErrorFacts.GetMessage(id); diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ExpressionTreeCallRewriter.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ExpressionTreeCallRewriter.cs index 8f3018d1dd..e2274d1bc3 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ExpressionTreeCallRewriter.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ExpressionTreeCallRewriter.cs @@ -45,19 +45,17 @@ namespace Microsoft.CSharp.RuntimeBinder ///////////////////////////////////////////////////////////////////////////////// - public static Expression Rewrite(TypeManager typeManager, Expr pExpr, Expression[] listOfParameters) + public static Expression Rewrite(TypeManager typeManager, ExprBinOp binOp, Expression[] listOfParameters) { ExpressionTreeCallRewriter rewriter = new ExpressionTreeCallRewriter(typeManager, listOfParameters); - // We should have a EXPRBINOP thats an EK_SEQUENCE. The RHS of our sequence + // We should have a ExprBinOp that's an EK_SEQUENCE. The RHS of our sequence // should be a call to PM_EXPRESSION_LAMBDA. The LHS of our sequence is the // set of declarations for the parameters that we'll need. // Assert all of these first, and then unwrap them. - Debug.Assert(pExpr != null); - Debug.Assert(pExpr.Kind == ExpressionKind.Sequence); - ExprBinOp binOp = (ExprBinOp)pExpr; Debug.Assert(binOp != null); + Debug.Assert(binOp.Kind == ExpressionKind.Sequence); Debug.Assert(binOp.OptionalRightChild is ExprCall); Debug.Assert(((ExprCall)binOp.OptionalRightChild).PredefinedMethod == PREDEFMETH.PM_EXPRESSION_LAMBDA); Debug.Assert(binOp.OptionalLeftChild != null); @@ -212,6 +210,10 @@ namespace Microsoft.CSharp.RuntimeBinder return new ExpressionExpr(exp); } + // ExpressionTreeRewriter has optimized away identity or up-cast conversions, leaving us with a bare parameter + // access. Just get the expression for that parameter so the lambda produced can be p0 => p0 + protected override Expr VisitWRAP(ExprWrap pExpr) => new ExpressionExpr(GetExpression(pExpr)); + #region Generators ///////////////////////////////////////////////////////////////////////////////// @@ -322,7 +324,7 @@ namespace Microsoft.CSharp.RuntimeBinder ExprList list = (ExprList)pExpr.OptionalArguments; ExprList list2 = (ExprList)list.OptionalNextListNode; e = GetExpression(list.OptionalElement); - t = ((ExprTypeOf)list2.OptionalElement).SourceType.Type.AssociatedSystemType; + t = ((ExprTypeOf)list2.OptionalElement).SourceType.AssociatedSystemType; if (e.Type.MakeByRefType() == t) { @@ -350,7 +352,7 @@ namespace Microsoft.CSharp.RuntimeBinder ExprList list = (ExprList)pExpr.OptionalArguments; e = GetExpression(list.OptionalElement); - t = ((ExprTypeOf)list.OptionalNextListNode).SourceType.Type.AssociatedSystemType; + t = ((ExprTypeOf)list.OptionalNextListNode).SourceType.AssociatedSystemType; if (e.Type.MakeByRefType() == t) { @@ -466,7 +468,7 @@ namespace Microsoft.CSharp.RuntimeBinder return Expression.Constant( GetObject(list.OptionalElement), - ((ExprTypeOf)list.OptionalNextListNode).SourceType.Type.AssociatedSystemType); + ((ExprTypeOf)list.OptionalNextListNode).SourceType.AssociatedSystemType); } ///////////////////////////////////////////////////////////////////////////////// @@ -780,7 +782,7 @@ namespace Microsoft.CSharp.RuntimeBinder ExprList list = (ExprList)call.OptionalArguments; return Expression.NewArrayInit( - ((ExprTypeOf)list.OptionalElement).SourceType.Type.AssociatedSystemType, + ((ExprTypeOf)list.OptionalElement).SourceType.AssociatedSystemType, GetArgumentsFromArrayInit((ExprArrayInit)list.OptionalNextListNode)); } @@ -881,7 +883,7 @@ namespace Microsoft.CSharp.RuntimeBinder } else if (pExpr is ExprTypeOf typeOf) { - return typeOf.SourceType.Type.AssociatedSystemType; + return typeOf.SourceType.AssociatedSystemType; } else if (pExpr is ExprMethodInfo methodInfo) { @@ -975,9 +977,9 @@ namespace Microsoft.CSharp.RuntimeBinder if (arrinit != null) { Expr list = arrinit.OptionalArguments; - Expr p = list; while (list != null) { + Expr p; if (list is ExprList pList) { p = pList.OptionalElement; @@ -988,6 +990,7 @@ namespace Microsoft.CSharp.RuntimeBinder p = list; list = null; } + expressions.Add(GetExpression(p)); } @@ -1136,7 +1139,6 @@ namespace Microsoft.CSharp.RuntimeBinder PropertySymbol propSym = propinfo.Property.Prop(); TypeArray genericInstanceParams = _typeManager.SubstTypeArray(propSym.Params, aggType, null); - CType genericInstanceReturn = _typeManager.SubstType(propSym.RetType, aggType, null); Type type = aggType.AssociatedSystemType; PropertyInfo propertyInfo = propSym.AssociatedPropertyInfo; diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ICSharpBinder.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ICSharpBinder.cs index 4f9d443f8a..e48f760fc2 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ICSharpBinder.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ICSharpBinder.cs @@ -1,3 +1,7 @@ +// 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 Microsoft.CSharp.RuntimeBinder.Semantics; @@ -21,4 +25,4 @@ namespace Microsoft.CSharp.RuntimeBinder BindingFlag BindingFlags { get; } string Name { get; } } -} \ No newline at end of file +} diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinder.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinder.cs index 86325352dd..bb248567e0 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinder.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinder.cs @@ -54,12 +54,10 @@ namespace Microsoft.CSharp.RuntimeBinder _semanticChecker = new CSemanticChecker(); BSYMMGR bsymmgr = _semanticChecker.getBSymmgr(); - NameManager nameManager = _semanticChecker.GetNameManager(); _symbolTable = new SymbolTable( bsymmgr.GetSymbolTable(), bsymmgr.GetSymFactory(), - nameManager, _semanticChecker.GetTypeManager(), bsymmgr, _semanticChecker); @@ -101,44 +99,11 @@ namespace Microsoft.CSharp.RuntimeBinder // than 1/1000 sec for the whole 4000 of them. ICSharpBinder binder = payload as ICSharpBinder; - if (binder == null) - { - Debug.Assert(false, "Unknown payload kind"); - throw Error.InternalCompilerError(); - } - + Debug.Assert(binder != null); lock (_bindLock) { - // this is a strategy for realizing correct binding when the symboltable - // finds a name collision across different types, e.g. one dynamic binding - // uses a type "N.T" and now a second binding uses a different type "N.T". - - // In order to make this work, we have to reset the symbol table and begin - // the second binding over again when we detect the collision. So this is - // something like a longjmp to the beginning of binding. For a single binding, - // if we have to do this more than once, we give an RBE--this would be a - // scenario that needs to know about both N.T's simultaneously to work. - - // See SymbolTable.LoadSymbolsFromType for more information. - - try - { - return BindCore(binder, parameters, args, out deferredBinding); - } - catch (ResetBindException) - { - Reset(); - try - { - return BindCore(binder, parameters, args, out deferredBinding); - } - catch (ResetBindException) - { - Reset(); - throw Error.BindingNameCollision(); - } - } + return BindCore(binder, parameters, args, out deferredBinding); } } @@ -148,10 +113,7 @@ namespace Microsoft.CSharp.RuntimeBinder DynamicMetaObject[] args, out DynamicMetaObject deferredBinding) { - if (args.Length < 1) - { - throw Error.BindRequireArguments(); - } + Debug.Assert(args.Length >= 1); InitializeCallingContext(payload); ArgumentObject[] arguments = CreateArgumentArray(payload, parameters, args); @@ -183,13 +145,12 @@ namespace Microsoft.CSharp.RuntimeBinder // Linq expression tree for the whole thing and return it. // (1) - Create the locals - Scope pScope = _semanticChecker.GetGlobalSymbolFactory().CreateScope(null); + Scope pScope = _semanticChecker.GetGlobalSymbolFactory().CreateScope(); LocalVariableSymbol[] locals = PopulateLocalScope(payload, pScope, arguments, parameters); // (1.5) - Check to see if we need to defer. - if (DeferBinding(payload, arguments, args, locals, out DynamicMetaObject o)) + if (DeferBinding(payload, arguments, args, locals, out deferredBinding)) { - deferredBinding = o; return null; } @@ -197,9 +158,7 @@ namespace Microsoft.CSharp.RuntimeBinder Expr pResult = payload.DispatchPayload(this, arguments, locals); Debug.Assert(pResult != null); - deferredBinding = null; - Expression e = CreateExpressionTreeFromResult(parameters, pScope, pResult); - return e; + return CreateExpressionTreeFromResult(parameters, pScope, pResult); } #region Helpers @@ -227,7 +186,6 @@ namespace Microsoft.CSharp.RuntimeBinder MemberLookup mem = new MemberLookup(); Expr callingObject = CreateCallingObjectForCall(callPayload, arguments, locals); - Debug.Assert(_bindingContext.ContextForMemberLookup != null); SymWithType swt = _symbolTable.LookupMember( callPayload.Name, callingObject, @@ -278,7 +236,7 @@ namespace Microsoft.CSharp.RuntimeBinder bindingContext.ContextForMemberLookup = null; } - bindingContext.CheckedConstant = bindingContext.CheckedNormal = payload.IsChecked; + bindingContext.Checked = payload.IsChecked; } ///////////////////////////////////////////////////////////////////////////////// @@ -288,11 +246,11 @@ namespace Microsoft.CSharp.RuntimeBinder Scope pScope, Expr pResult) { - // (3) - Place the result in a return statement and create the EXPRBOUNDLAMBDA. + // (3) - Place the result in a return statement and create the ExprBoundLambda. ExprBoundLambda boundLambda = GenerateBoundLambda(pScope, pResult); - // (4) - Rewrite the EXPRBOUNDLAMBDA into an expression tree. - Expr exprTree = ExpressionTreeRewriter.Rewrite(boundLambda, _exprFactory, SymbolLoader); + // (4) - Rewrite the ExprBoundLambda into an expression tree. + ExprBinOp exprTree = ExpressionTreeRewriter.Rewrite(boundLambda, _exprFactory, SymbolLoader); // (5) - Create the actual Expression Tree Expression e = ExpressionTreeCallRewriter.Rewrite(SymbolLoader.GetTypeManager(), exprTree, parameters); @@ -412,7 +370,7 @@ namespace Microsoft.CSharp.RuntimeBinder ///////////////////////////////////////////////////////////////////////////////// - internal Expr DispatchPayload(ICSharpInvokeOrInvokeMemberBinder payload, ArgumentObject[] arguments, LocalVariableSymbol[] locals) => + internal ExprWithArgs DispatchPayload(ICSharpInvokeOrInvokeMemberBinder payload, ArgumentObject[] arguments, LocalVariableSymbol[] locals) => BindCall(payload, CreateCallingObjectForCall(payload, arguments, locals), arguments, locals); ///////////////////////////////////////////////////////////////////////////////// @@ -457,8 +415,7 @@ namespace Microsoft.CSharp.RuntimeBinder } LocalVariableSymbol local = _semanticChecker.GetGlobalSymbolFactory() - .CreateLocalVar(_semanticChecker.GetNameManager().Add("p" + i), pScope, type); - local.fUsedInAnonMeth = true; + .CreateLocalVar(NameManager.Add("p" + i), pScope, type); locals[i] = local; } @@ -473,14 +430,8 @@ namespace Microsoft.CSharp.RuntimeBinder { // We don't actually need the real delegate type here - we just need SOME delegate type. // This is because we never attempt any conversions on the lambda itself. - AggregateType delegateType = _symbolTable.GetCTypeFromType(typeof(Func<>)) as AggregateType; - LocalVariableSymbol thisLocal = _semanticChecker.GetGlobalSymbolFactory().CreateLocalVar(_semanticChecker.GetNameManager().Add("this"), pScope, _symbolTable.GetCTypeFromType(typeof(object))); - thisLocal.isThis = true; - ExprBoundLambda boundLambda = _exprFactory.CreateAnonymousMethod(delegateType, pScope); - ExprReturn returnStatement = _exprFactory.CreateReturn(call); - ExprBlock block = _exprFactory.CreateBlock(returnStatement); - boundLambda.OptionalBody = block; - return boundLambda; + AggregateType delegateType = _semanticChecker.SymbolLoader.GetPredefindType(PredefinedType.PT_FUNC); + return _exprFactory.CreateAnonymousMethod(delegateType, pScope, call); } #region ExprCreation @@ -583,7 +534,7 @@ namespace Microsoft.CSharp.RuntimeBinder if (argument.Info.NamedArgument) { Debug.Assert(argument.Info.Name != null); - arg = _exprFactory.CreateNamedArgumentSpecification(_semanticChecker.GetNameManager().Add(argument.Info.Name), arg); + arg = _exprFactory.CreateNamedArgumentSpecification(NameManager.Add(argument.Info.Name), arg); } // If we have an object that was "dynamic" at compile time, we need @@ -618,7 +569,7 @@ namespace Microsoft.CSharp.RuntimeBinder Expr callingObject, SYMKIND kind) { - Name name = _semanticChecker.GetNameManager().Add(Name); + Name name = NameManager.Add(Name); AggregateType callingType; CType callingObjectType = callingObject.Type; @@ -683,8 +634,6 @@ namespace Microsoft.CSharp.RuntimeBinder // as well so that overload resolution can find them. if (callingType.IsWindowsRuntimeType()) { - TypeArray collectioniFaces = callingType.GetWinRTCollectionIfacesAll(SymbolLoader); - foreach (AggregateType t in callingType.GetWinRTCollectionIfacesAll(SymbolLoader).Items) { if (_symbolTable.AggregateContainsMethod(t.GetOwningAggregate(), Name, mask) && distinctCallingTypes.Add(t)) @@ -750,19 +699,18 @@ namespace Microsoft.CSharp.RuntimeBinder ExprMemberGroup pMemGroup = CreateMemberGroupEXPR(property.name.Text, null, callingObject, SYMKIND.SK_PropertySymbol); return _binder.BindToProperty(// For a static property instance, don't set the object. - callingObject is ExprClass ? null : callingObject, pwt, flags, null, null, pMemGroup); + callingObject is ExprClass ? null : callingObject, pwt, flags, null, pMemGroup); } ///////////////////////////////////////////////////////////////////////////////// - private Expr CreateIndexer(SymWithType swt, Expr callingObject, Expr arguments, BindingFlag bindFlags) + private ExprWithArgs CreateIndexer(SymWithType swt, Expr callingObject, Expr arguments, BindingFlag bindFlags) { IndexerSymbol index = swt.Sym as IndexerSymbol; - AggregateType ctype = swt.GetType(); ExprMemberGroup memgroup = CreateMemberGroupEXPR(index.name.Text, null, callingObject, SYMKIND.SK_PropertySymbol); - - Expr result = _binder.BindMethodGroupToArguments(bindFlags, memgroup, arguments); - return ReorderArgumentsForNamedAndOptional(callingObject, result); + ExprWithArgs result = _binder.BindMethodGroupToArguments(bindFlags, memgroup, arguments); + ReorderArgumentsForNamedAndOptional(callingObject, result); + return result; } ///////////////////////////////////////////////////////////////////////////////// @@ -781,7 +729,6 @@ namespace Microsoft.CSharp.RuntimeBinder // For a field, simply create the EXPRFIELD and our caller takes care of the rest. FieldSymbol fieldSymbol = swt.Field(); - CType returnType = fieldSymbol.GetType(); AggregateType fieldType = swt.GetType(); FieldWithType fwt = new FieldWithType(fieldSymbol, fieldType); @@ -836,7 +783,7 @@ namespace Microsoft.CSharp.RuntimeBinder ///////////////////////////////////////////////////////////////////////////////// - private Expr BindCall( + private ExprWithArgs BindCall( ICSharpInvokeOrInvokeMemberBinder payload, Expr callingObject, ArgumentObject[] arguments, @@ -850,7 +797,6 @@ namespace Microsoft.CSharp.RuntimeBinder int arity = payload.TypeArguments?.Length ?? 0; MemberLookup mem = new MemberLookup(); - Debug.Assert(_bindingContext.ContextForMemberLookup != null); SymWithType swt = _symbolTable.LookupMember( payload.Name, callingObject, @@ -944,20 +890,17 @@ namespace Microsoft.CSharp.RuntimeBinder memGroup.Flags &= ~EXPRFLAG.EXF_USERCALLABLE; } - Expr pResult = _binder.BindMethodGroupToArguments(// Tree - BindingFlag.BIND_RVALUEREQUIRED | BindingFlag.BIND_STMTEXPRONLY, memGroup, CreateArgumentListEXPR(arguments, locals, 1, arguments.Length)); + ExprCall result = _binder.BindMethodGroupToArguments(// Tree + BindingFlag.BIND_RVALUEREQUIRED | BindingFlag.BIND_STMTEXPRONLY, memGroup, CreateArgumentListEXPR(arguments, locals, 1, arguments.Length)) as ExprCall; - // If overload resolution failed, throw an error. - if (pResult == null || !pResult.IsOK) - { - throw Error.BindCallFailedOverloadResolution(); - } - CheckForConditionalMethodError(pResult); + Debug.Assert(result != null); - return ReorderArgumentsForNamedAndOptional(callingObject, pResult); + CheckForConditionalMethodError(result); + ReorderArgumentsForNamedAndOptional(callingObject, result); + return result; } - private Expr BindWinRTEventAccessor(EventWithType ewt, Expr callingObject, ArgumentObject[] arguments, LocalVariableSymbol[] locals, bool isAddAccessor) + private ExprWithArgs BindWinRTEventAccessor(EventWithType ewt, Expr callingObject, ArgumentObject[] arguments, LocalVariableSymbol[] locals, bool isAddAccessor) { // We want to generate either: // WindowsRuntimeMarshal.AddEventHandler(new Func(x.add_foo), new Action(x.remove_foo), value) @@ -1002,51 +945,31 @@ namespace Microsoft.CSharp.RuntimeBinder _symbolTable.PopulateSymbolTableWithName(methodName, new List { evtType }, windowsRuntimeMarshalType); ExprClass marshalClass = _exprFactory.CreateClass(_symbolTable.GetCTypeFromType(windowsRuntimeMarshalType)); ExprMemberGroup addEventGrp = CreateMemberGroupEXPR(methodName, new [] { evtType }, marshalClass, SYMKIND.SK_MethodSymbol); - Expr expr = _binder.BindMethodGroupToArguments( + return _binder.BindMethodGroupToArguments( BindingFlag.BIND_RVALUEREQUIRED | BindingFlag.BIND_STMTEXPRONLY, addEventGrp, args); - - return expr; } - private static void CheckForConditionalMethodError(Expr pExpr) + private static void CheckForConditionalMethodError(ExprCall call) { - if (pExpr is ExprCall call) + MethodSymbol method = call.MethWithInst.Meth(); + object[] conditions = method.AssociatedMemberInfo.GetCustomAttributes(typeof(ConditionalAttribute), true); + if (conditions.Length > 0) { - // This mimics the behavior of the native CompilerSymbolLoader in GetConditionalSymbols. Override - // methods cannot have the conditional attribute, but implicitly acquire it from their slot. - - MethodSymbol method = call.MethWithInst.Meth(); - if (method.isOverride) - { - method = method.swtSlot.Meth(); - } - - object[] conditions = method.AssociatedMemberInfo.GetCustomAttributes(typeof(ConditionalAttribute), false).ToArray(); - if (conditions.Length > 0) - { - throw Error.BindCallToConditionalMethod(method.name); - } - } - else - { - Debug.Fail("Should be unreachable"); + throw Error.BindCallToConditionalMethod(method.name); } } - private Expr ReorderArgumentsForNamedAndOptional(Expr callingObject, Expr pResult) + private void ReorderArgumentsForNamedAndOptional(Expr callingObject, ExprWithArgs result) { - IExprWithArgs result = pResult as IExprWithArgs; - Debug.Assert(result != null); - Expr arguments = result.OptionalArguments; AggregateType type; MethodOrPropertySymbol methprop; ExprMemberGroup memgroup; TypeArray typeArgs; - if (pResult is ExprCall call) + if (result is ExprCall call) { type = call.MethWithInst.Ats; methprop = call.MethWithInst.Meth(); @@ -1055,7 +978,7 @@ namespace Microsoft.CSharp.RuntimeBinder } else { - ExprProperty prop = pResult as ExprProperty; + ExprProperty prop = result as ExprProperty; Debug.Assert(prop != null); type = prop.PropWithTypeSlot.Ats; methprop = prop.PropWithTypeSlot.Prop(); @@ -1063,9 +986,10 @@ namespace Microsoft.CSharp.RuntimeBinder typeArgs = null; } - ArgInfos argInfo = new ArgInfos(); - bool b; - argInfo.carg = ExpressionBinder.CountArguments(arguments, out b); + ArgInfos argInfo = new ArgInfos + { + carg = ExpressionBinder.CountArguments(arguments) + }; _binder.FillInArgInfoFromArgList(argInfo, arguments); // We need to substitute type parameters BEFORE getting the most derived one because @@ -1109,7 +1033,6 @@ namespace Microsoft.CSharp.RuntimeBinder result.OptionalArguments = pList; } - return pResult; } private Expr StripNamedArgument(Expr pArg) @@ -1159,10 +1082,7 @@ namespace Microsoft.CSharp.RuntimeBinder ArgumentObject[] arguments, LocalVariableSymbol[] locals) { - if (arguments.Length != 1) - { - throw Error.BindUnaryOperatorRequireOneArgument(); - } + Debug.Assert(arguments.Length == 1); OperatorKind op = GetOperatorKind(payload.Operation); Expr arg1 = CreateArgumentEXPR(arguments[0], locals[0]); @@ -1206,10 +1126,7 @@ namespace Microsoft.CSharp.RuntimeBinder ArgumentObject[] arguments, LocalVariableSymbol[] locals) { - if (arguments.Length != 2) - { - throw Error.BindBinaryOperatorRequireTwoArguments(); - } + Debug.Assert(arguments.Length == 2); ExpressionKind ek = Operators.GetExpressionKind(GetOperatorKind(payload.Operation, payload.IsLogicalOperation)); Expr arg1 = CreateArgumentEXPR(arguments[0], locals[0]); @@ -1471,10 +1388,8 @@ namespace Microsoft.CSharp.RuntimeBinder ArgumentObject[] arguments, LocalVariableSymbol[] locals) { - if (arguments.Length < 2) - { - throw Error.BindBinaryAssignmentRequireTwoArguments(); - } + Debug.Assert(arguments.Length >= 2); + Debug.Assert(arguments.All(a => a.Type != null)); string name = payload.Name; @@ -1499,12 +1414,6 @@ namespace Microsoft.CSharp.RuntimeBinder int indexOfLast = arguments.Length - 1; Expr rhs = CreateArgumentEXPR(arguments[indexOfLast], locals[indexOfLast]); - - if (arguments[0].Type == null) - { - throw Error.BindBinaryAssignmentFailedNullReference(); - } - return _binder.BindAssignment(lhs, rhs, bIsCompound); } #endregion @@ -1530,7 +1439,6 @@ namespace Microsoft.CSharp.RuntimeBinder throw Error.NullReferenceOnMemberException(); } - Debug.Assert(_bindingContext.ContextForMemberLookup != null); SymWithType swt = _symbolTable.LookupMember( binder.Name, callingObject, @@ -1552,7 +1460,7 @@ namespace Microsoft.CSharp.RuntimeBinder // this is an event. This is due to the Dev10 design change around // the binding of +=, and the fact that the "IsEvent" binding question // is only ever asked about the LHS of a += or -=. - if (swt.Sym is FieldSymbol field && field.isEvent) + else if (swt.Sym is FieldSymbol field && field.isEvent) { result = true; } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinderException.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinderException.cs index e0bca6cefa..b807beed07 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinderException.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinderException.cs @@ -50,7 +50,6 @@ namespace Microsoft.CSharp.RuntimeBinder protected RuntimeBinderException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinderExtensions.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinderExtensions.cs index 53aaf42d98..4111a0dead 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinderExtensions.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinderExtensions.cs @@ -37,14 +37,9 @@ namespace Microsoft.CSharp.RuntimeBinder return true; } - if (mi1 is MethodInfo && mi2 is MethodInfo) + if (mi1 is MethodInfo method1) { - MethodInfo method1 = mi1 as MethodInfo; - MethodInfo method2 = mi2 as MethodInfo; - ParameterInfo[] pis1; - ParameterInfo[] pis2; - - if (method1.IsGenericMethod != method2.IsGenericMethod) + if (!(mi2 is MethodInfo method2) || method1.IsGenericMethod != method2.IsGenericMethod) { return false; } @@ -61,40 +56,49 @@ namespace Microsoft.CSharp.RuntimeBinder } return method1 != method2 + && method1.CallingConvention == method2.CallingConvention && method1.Name == method2.Name && method1.DeclaringType.IsGenericallyEqual(method2.DeclaringType) && method1.ReturnType.IsGenericallyEquivalentTo(method2.ReturnType, method1, method2) - && (pis1 = method1.GetParameters()).Length == (pis2 = method2.GetParameters()).Length - && Enumerable.All(Enumerable.Zip(pis1, pis2, (pi1, pi2) => pi1.IsEquivalentTo(pi2, method1, method2)), x => x); + && method1.AreParametersEquivalent(method2); } - if (mi1 is ConstructorInfo && mi2 is ConstructorInfo) + if (mi1 is ConstructorInfo ctor1) { - ConstructorInfo ctor1 = mi1 as ConstructorInfo; - ConstructorInfo ctor2 = mi2 as ConstructorInfo; - ParameterInfo[] pis1; - ParameterInfo[] pis2; - - return ctor1 != ctor2 + return mi2 is ConstructorInfo ctor2 + && ctor1 != ctor2 + && ctor1.CallingConvention == ctor2.CallingConvention && ctor1.DeclaringType.IsGenericallyEqual(ctor2.DeclaringType) - && (pis1 = ctor1.GetParameters()).Length == (pis2 = ctor2.GetParameters()).Length - && Enumerable.All(Enumerable.Zip(pis1, pis2, (pi1, pi2) => pi1.IsEquivalentTo(pi2, ctor1, ctor2)), x => x); + && ctor1.AreParametersEquivalent(ctor2); } - if (mi1 is PropertyInfo && mi2 is PropertyInfo) + return mi1 is PropertyInfo prop1 && mi2 is PropertyInfo prop2 + && prop1 != prop2 + && prop1.Name == prop2.Name + && prop1.DeclaringType.IsGenericallyEqual(prop2.DeclaringType) + && prop1.PropertyType.IsGenericallyEquivalentTo(prop2.PropertyType, prop1, prop2) + && prop1.GetGetMethod(true).IsEquivalentTo(prop2.GetGetMethod(true)) + && prop1.GetSetMethod(true).IsEquivalentTo(prop2.GetSetMethod(true)); + } + + private static bool AreParametersEquivalent(this MethodBase method1, MethodBase method2) + { + ParameterInfo[] pis1 = method1.GetParameters(); + ParameterInfo[] pis2 = method2.GetParameters(); + if (pis1.Length != pis2.Length) { - PropertyInfo prop1 = mi1 as PropertyInfo; - PropertyInfo prop2 = mi2 as PropertyInfo; - - return prop1 != prop2 - && prop1.Name == prop2.Name - && prop1.DeclaringType.IsGenericallyEqual(prop2.DeclaringType) - && prop1.PropertyType.IsGenericallyEquivalentTo(prop2.PropertyType, prop1, prop2) - && prop1.GetGetMethod(true).IsEquivalentTo(prop2.GetGetMethod(true)) - && prop1.GetSetMethod(true).IsEquivalentTo(prop2.GetSetMethod(true)); + return false; } - return false; + for (int i = 0; i < pis1.Length; ++i) + { + if (!pis1[i].IsEquivalentTo(pis2[i], method1, method2)) + { + return false; + } + } + + return true; } private static bool IsEquivalentTo(this ParameterInfo pi1, ParameterInfo pi2, MethodBase method1, MethodBase method2) diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinderInternalCompilerException.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinderInternalCompilerException.cs index 3bfe8a2b9b..436dde70f4 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinderInternalCompilerException.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinderInternalCompilerException.cs @@ -50,7 +50,6 @@ namespace Microsoft.CSharp.RuntimeBinder protected RuntimeBinderInternalCompilerException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/BinOpArgInfo.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/BinOpArgInfo.cs index 6f5936a0e9..17e1f6d029 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/BinOpArgInfo.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/BinOpArgInfo.cs @@ -59,26 +59,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { return (mask & BinOpMask.Enum) != 0; } - - public bool ValidForPointer() - { - return (mask & BinOpMask.Ptr) != 0; - } - - public bool ValidForVoidPointer() - { - return (mask & BinOpMask.VoidPtr) != 0; - } - - public bool ValidForPointerAndNumber() - { - return (mask & BinOpMask.PtrNum) != 0; - } - - public bool ValidForNumberAndPointer() - { - return (mask & BinOpMask.NumPtr) != 0; - } } } } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/BinOpKind.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/BinOpKind.cs index 6353784971..9465fb86ff 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/BinOpKind.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/BinOpKind.cs @@ -42,9 +42,5 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics Enum = Sub | Equal | Compare | Bitwise | BitXor, EnumUnder = Add | Sub, UnderEnum = Add, - Ptr = Sub, - PtrNum = Add | Sub, - NumPtr = Add, - VoidPtr = Equal | Compare, } } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Binding/Better.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Binding/Better.cs index 565294a4b4..6fe31c29fd 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Binding/Better.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Binding/Better.cs @@ -129,11 +129,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics private TypeArray RearrangeNamedArguments(TypeArray pta, MethPropWithInst mpwi, CType pTypeThrough, ArgInfos args) { - if (!args.fHasExprs) - { - return pta; - } - #if DEBUG // We never have a named argument that is in a position in the argument // list past the end of what would be the formal parameter list. @@ -142,6 +137,13 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics Debug.Assert(!(args.prgexpr[i] is ExprNamedArgumentSpecification)); } #endif + // If we've no args we can skip. If the last argument isn't named then either we + // have no named arguments, and we can skip, or we have non-trailing named arguments + // and we MUST skip! + if (args.carg == 0 || !(args.prgexpr[args.carg - 1] is ExprNamedArgumentSpecification)) + { + return pta; + } CType type = pTypeThrough != null ? pTypeThrough : mpwi.GetType(); CType[] typeList = new CType[pta.Count]; @@ -238,7 +240,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics int carg = args.carg; for (int i = 0; i < carg; i++) { - Expr arg = args.fHasExprs ? args.prgexpr[i] : null; + Expr arg = args.prgexpr[i]; CType p1 = pta1[i]; CType p2 = pta2[i]; diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Binding/ErrorReporting.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Binding/ErrorReporting.cs index 6c91d04060..b697f078b5 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Binding/ErrorReporting.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Binding/ErrorReporting.cs @@ -10,56 +10,32 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { internal sealed partial class ExpressionBinder { - private static readonly ErrorCode[] s_ReadOnlyLocalErrors = - { - ErrorCode.ERR_RefReadonlyLocal, - ErrorCode.ERR_AssgReadonlyLocal, - }; - - private RuntimeBinderException ReportLocalError(LocalVariableSymbol local, CheckLvalueKind kind, bool isNested) - { - Debug.Assert(local != null); - - int index = kind == CheckLvalueKind.OutParameter ? 0 : 1; - - Debug.Assert(index != 2 && index != 3); - // There is no way that we can have no cause AND a read-only local nested in a struct with a - // writable field. What would make the local read-only if not one of the causes above? (Const - // locals may not be structs, so we would already have errored out in that scenario.) - - ErrorCode err = s_ReadOnlyLocalErrors[index]; - - return ErrorContext.Error(err, local.name); - } - - private static readonly ErrorCode[] s_ReadOnlyErrors = - { - ErrorCode.ERR_RefReadonly, - ErrorCode.ERR_AssgReadonly, - ErrorCode.ERR_RefReadonlyStatic, - ErrorCode.ERR_AssgReadonlyStatic, - ErrorCode.ERR_RefReadonly2, - ErrorCode.ERR_AssgReadonly2, - ErrorCode.ERR_RefReadonlyStatic2, - ErrorCode.ERR_AssgReadonlyStatic2 - }; - - private RuntimeBinderException ReportReadOnlyError(ExprField field, CheckLvalueKind kind, bool isNested) + private RuntimeBinderException ReportReadOnlyError(ExprField field, bool isNested) { Debug.Assert(field != null); - bool isStatic = field.FieldWithType.Field().isStatic; + FieldWithType fieldWithType = field.FieldWithType; + bool isStatic = fieldWithType.Field().isStatic; + ErrArg[] args; + ErrorCode err; + if (isNested) + { + args = new ErrArg[]{ fieldWithType }; + err = isStatic ? ErrorCode.ERR_AssgReadonlyStatic2 : ErrorCode.ERR_AssgReadonly2; + } + else + { + args = Array.Empty(); + err = isStatic ? ErrorCode.ERR_AssgReadonlyStatic : ErrorCode.ERR_AssgReadonly; + } - int index = (isNested ? 4 : 0) + (isStatic ? 2 : 0) + (kind == CheckLvalueKind.OutParameter ? 0 : 1); - ErrorCode err = s_ReadOnlyErrors[index]; - - return ErrorContext.Error(err, isNested ? new ErrArg[]{field.FieldWithType} : Array.Empty()); + return ErrorContext.Error(err, args); } - // Return true if we actually report a failure. private void TryReportLvalueFailure(Expr expr, CheckLvalueKind kind) { Debug.Assert(expr != null); + Debug.Assert(!(expr is ExprLocal)); // We have a lvalue failure. Was the reason because this field // was marked readonly? Give special messages for this case. @@ -70,11 +46,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { Debug.Assert(expr != null); - if (expr is ExprLocal local && local.IsOK) - { - throw ReportLocalError(local.Local, kind, isNested); - } - Expr pObject = null; if (expr is ExprProperty prop) @@ -87,7 +58,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { if (field.FieldWithType.Field().isReadOnly) { - throw ReportReadOnlyError(field, kind, isNested); + throw ReportReadOnlyError(field, isNested); } if (!field.FieldWithType.Field().isStatic) { @@ -97,7 +68,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics if (pObject != null && pObject.Type.isStructOrEnum()) { - if (pObject is IExprWithArgs withArgs) + if (pObject is ExprWithArgs withArgs) { // assigning to RHS of method or property getter returning a value-type on the stack or // passing RHS of method or property getter returning a value-type on the stack, as ref or out @@ -118,9 +89,10 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } // everything else - if (pObject != null && !pObject.isLvalue() && (expr is ExprField || (!isNested && expr is ExprProperty))) + if (pObject != null && !pObject.isLvalue() && (expr is ExprField || !isNested)) { Debug.Assert(pObject.Type.isStructOrEnum()); + Debug.Assert(!(pObject is ExprLocal)); expr = pObject; } else diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/BindingContext.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/BindingContext.cs index de778f76ee..18783d2792 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/BindingContext.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/BindingContext.cs @@ -22,16 +22,12 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } public BindingContext(BindingContext parent) + : this(parent.SemanticChecker, parent.ExprFactory) { - Debug.Assert(parent.SemanticChecker != null); - ExprFactory = parent.ExprFactory; + // We copy the context object, but leave checking false. ContextForMemberLookup = parent.ContextForMemberLookup; - CheckedNormal = parent.CheckedNormal; - CheckedConstant = parent.CheckedConstant; - SymbolLoader = (SemanticChecker = parent.SemanticChecker).SymbolLoader; } - //The SymbolLoader can be retrieved from SemanticChecker, //but that is a virtual call that is showing up on the profiler. Retrieve //the SymbolLoader once at construction and return a cached copy. @@ -45,8 +41,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics public ExprFactory ExprFactory { get; } - public bool CheckedNormal { get; set; } - - public bool CheckedConstant { get; set; } + public bool Checked { get; set; } } } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ConstVal.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ConstVal.cs index a0228df71b..64b2f33974 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ConstVal.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ConstVal.cs @@ -24,7 +24,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics Boolean } - internal struct ConstVal + internal readonly struct ConstVal { // Pre-boxed common values. private static readonly object s_false = false; diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Conversion.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Conversion.cs index 2804a15311..584a151650 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Conversion.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Conversion.cs @@ -45,8 +45,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics private delegate bool ConversionFunc( Expr pSourceExpr, CType pSourceType, - ExprClass pDestinationTypeExpr, - CType pDestinationTypeForLambdaErrorReporting, + CType pDestinationType, bool needsExprDest, out Expr ppDestinationExpr, CONVERTTYPE flags); @@ -268,7 +267,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { return BetterType.Right; } - if ((int)pt1 <= NUM_EXT_TYPES && (int)pt2 <= NUM_EXT_TYPES) + if ((int)pt1 < NUM_EXT_TYPES && (int)pt2 < NUM_EXT_TYPES) { return WhichSimpleConversionIsBetter(pt1, pt2); } @@ -322,7 +321,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics PredefinedType pt1 = (type1 as NullableType).UnderlyingType.getPredefType(); PredefinedType pt2 = (type2 as NullableType).UnderlyingType.getPredefType(); - if ((int)pt1 <= NUM_EXT_TYPES && (int)pt2 <= NUM_EXT_TYPES) + if ((int)pt1 < NUM_EXT_TYPES && (int)pt2 < NUM_EXT_TYPES) { return WhichSimpleConversionIsBetter(pt1, pt2); } @@ -331,43 +330,25 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } // returns true if an implicit conversion exists from source type to dest type. flags is an optional parameter. - private bool canConvert(CType src, CType dest, CONVERTTYPE flags) - { - ExprClass exprDest = ExprFactory.CreateClass(dest); - return BindImplicitConversion(null, src, exprDest, dest, flags); - } + private bool canConvert(CType src, CType dest, CONVERTTYPE flags) => BindImplicitConversion(null, src, dest, flags); - public bool canConvert(CType src, CType dest) - { - return canConvert(src, dest, 0); - } + public bool canConvert(CType src, CType dest) => canConvert(src, dest, 0); // returns true if a implicit conversion exists from source expr to dest type. flags is an optional parameter. - private bool canConvert(Expr expr, CType dest) - { - return canConvert(expr, dest, 0); - } + private bool canConvert(Expr expr, CType dest) => canConvert(expr, dest, 0); - private bool canConvert(Expr expr, CType dest, CONVERTTYPE flags) - { - ExprClass exprDest = ExprFactory.CreateClass(dest); - return BindImplicitConversion(expr, expr.Type, exprDest, dest, flags); - } + private bool canConvert(Expr expr, CType dest, CONVERTTYPE flags) => + BindImplicitConversion(expr, expr.Type, dest, flags); // performs an implicit conversion if it's possible. otherwise displays an error. flags is an optional parameter. - private Expr mustConvertCore(Expr expr, ExprClass destExpr) - { - return mustConvertCore(expr, destExpr, 0); - } + private Expr mustConvertCore(Expr expr, CType destExpr) => mustConvertCore(expr, destExpr, 0); - private Expr mustConvertCore(Expr expr, ExprClass destExpr, CONVERTTYPE flags) + private Expr mustConvertCore(Expr expr, CType dest, CONVERTTYPE flags) { Debug.Assert(!(expr is ExprMemberGroup)); - Expr exprResult; - CType dest = destExpr.Type; - if (BindImplicitConversion(expr, expr.Type, destExpr, dest, out exprResult, flags)) + if (BindImplicitConversion(expr, expr.Type, dest, out Expr exprResult, flags)) { // Conversion works. checkUnsafe(expr.Type); // added to the binder so we don't bind to pointer ops @@ -375,40 +356,33 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return exprResult; } - if (expr.IsOK && !(dest is ErrorType)) + // don't report cascading error. + + // For certain situations, try to give a better error. + + FUNDTYPE ftSrc = expr.Type.fundType(); + FUNDTYPE ftDest = dest.fundType(); + + if (expr is ExprConstant constant && + expr.Type.isSimpleType() && dest.isSimpleType()) { - // don't report cascading error. - - // For certain situations, try to give a better error. - - FUNDTYPE ftSrc = expr.Type.fundType(); - FUNDTYPE ftDest = dest.fundType(); - - if (expr is ExprConstant constant && constant.IsOK && - expr.Type.isSimpleType() && dest.isSimpleType()) + if ((ftSrc == FUNDTYPE.FT_I4 && (ftDest <= FUNDTYPE.FT_LASTNONLONG || ftDest == FUNDTYPE.FT_U8)) || + (ftSrc == FUNDTYPE.FT_I8 && ftDest == FUNDTYPE.FT_U8)) { - if ((ftSrc == FUNDTYPE.FT_I4 && (ftDest <= FUNDTYPE.FT_LASTNONLONG || ftDest == FUNDTYPE.FT_U8)) || - (ftSrc == FUNDTYPE.FT_I8 && ftDest == FUNDTYPE.FT_U8)) - { - // Failed because value was out of range. Report nifty error message. - string value = constant.Int64Value.ToString(CultureInfo.InvariantCulture); - throw ErrorContext.Error(ErrorCode.ERR_ConstOutOfRange, value, dest); - } + // Failed because value was out of range. Report nifty error message. + string value = constant.Int64Value.ToString(CultureInfo.InvariantCulture); + throw ErrorContext.Error(ErrorCode.ERR_ConstOutOfRange, value, dest); } - - if (expr.Type is NullType && dest.fundType() != FUNDTYPE.FT_REF) - { - throw ErrorContext.Error(ErrorCode.ERR_ValueCantBeNull, dest); - } - - // canCast => can't convert, but explicit exists and can be specified by the user (no anonymous types). - // !canCast => Generic "can't convert" error. - throw ErrorContext.Error(canCast(expr.Type, dest, flags) ? ErrorCode.ERR_NoImplicitConvCast : ErrorCode.ERR_NoImplicitConv, new ErrArg(expr.Type, ErrArgFlags.Unique), new ErrArg(dest, ErrArgFlags.Unique)); } - exprResult = ExprFactory.CreateCast(0, destExpr, expr); - exprResult.SetError(); - return exprResult; + if (expr.Type is NullType && dest.fundType() != FUNDTYPE.FT_REF) + { + throw ErrorContext.Error(ErrorCode.ERR_ValueCantBeNull, dest); + } + + // canCast => can't convert, but explicit exists and can be specified by the user (no anonymous types). + // !canCast => Generic "can't convert" error. + throw ErrorContext.Error(canCast(expr.Type, dest, flags) ? ErrorCode.ERR_NoImplicitConvCast : ErrorCode.ERR_NoImplicitConv, new ErrArg(expr.Type, ErrArgFlags.Unique), new ErrArg(dest, ErrArgFlags.Unique)); } // performs an implicit conversion if its possible. otherwise returns null. flags is an optional parameter. @@ -424,31 +398,20 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics private Expr tryConvert(Expr expr, CType dest, CONVERTTYPE flags) { - Expr exprResult; - ExprClass exprDest = ExprFactory.CreateClass(dest); - if (BindImplicitConversion(expr, expr.Type, exprDest, dest, out exprResult, flags)) + if (BindImplicitConversion(expr, expr.Type, dest, out Expr exprResult, flags)) { checkUnsafe(expr.Type); // added to the binder so we don't bind to pointer ops checkUnsafe(dest); // added to the binder so we don't bind to pointer ops // Conversion works. return exprResult; } + return null; } - public Expr mustConvert(Expr expr, CType dest) - { - return mustConvert(expr, dest, (CONVERTTYPE)0); - } - private Expr mustConvert(Expr expr, CType dest, CONVERTTYPE flags) - { - ExprClass exprClass = ExprFactory.CreateClass(dest); - return mustConvert(expr, exprClass, flags); - } - private Expr mustConvert(Expr expr, ExprClass dest, CONVERTTYPE flags) - { - return mustConvertCore(expr, dest, flags); - } + public Expr mustConvert(Expr expr, CType dest) => mustConvert(expr, dest, (CONVERTTYPE)0); + + private Expr mustConvert(Expr expr, CType dest, CONVERTTYPE flags) => mustConvertCore(expr, dest, flags); // public bool canCast(Expr expr, CType dest) // { @@ -457,104 +420,83 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // } // performs an explicit conversion if its possible. otherwise displays an error. - private Expr mustCastCore(Expr expr, ExprClass destExpr, CONVERTTYPE flags) + private Expr mustCastCore(Expr expr, CType dest, CONVERTTYPE flags) { Debug.Assert(!(expr is ExprMemberGroup)); - Expr exprResult; - - CType dest = destExpr.Type; + Debug.Assert(dest != null); SemanticChecker.CheckForStaticClass(null, dest, ErrorCode.ERR_ConvertToStaticClass); - if (expr.IsOK) + if (BindExplicitConversion(expr, expr.Type, dest, out Expr exprResult, flags)) { - if (BindExplicitConversion(expr, expr.Type, destExpr, dest, out exprResult, flags)) + // Conversion works. + checkUnsafe(expr.Type); // added to the binder so we don't bind to pointer ops + checkUnsafe(dest); // added to the binder so we don't bind to pointer ops + return exprResult; + } + + // For certain situations, try to give a better error. + Expr exprConst = expr.GetConst(); + bool simpleConstToSimpleDestination = exprConst != null && expr.Type.isSimpleOrEnum() && + dest.isSimpleOrEnum(); + + if (simpleConstToSimpleDestination) + { + FUNDTYPE exprType = expr.Type.fundType(); + if (exprType == FUNDTYPE.FT_STRUCT) { - // Conversion works. - checkUnsafe(expr.Type); // added to the binder so we don't bind to pointer ops - checkUnsafe(dest); // added to the binder so we don't bind to pointer ops - return exprResult; + // We have a constant decimal that is out of range of the destination type. + // In both checked and unchecked contexts we issue an error. No need to recheck conversion in unchecked context. + // Decimal is a SimpleType represented in a FT_STRUCT + throw ErrorContext.Error( + ErrorCode.ERR_ConstOutOfRange, + ((ExprConstant)exprConst).Val.DecimalVal.ToString(CultureInfo.InvariantCulture), dest); } - if (dest != null && !(dest is ErrorType)) - { // don't report cascading error. - // For certain situations, try to give a better error. - string value = ""; - Expr exprConst = expr.GetConst(); - FUNDTYPE expr_type = expr.Type.fundType(); - bool simpleConstToSimpleDestination = exprConst != null && expr.Type.isSimpleOrEnum() && - dest.isSimpleOrEnum(); - if (simpleConstToSimpleDestination && expr_type == FUNDTYPE.FT_STRUCT) + if (Context.Checked) + { + // check if we failed because we are in checked mode... + if (!canExplicitConversionBeBoundInUncheckedContext(expr, expr.Type, dest, flags | CONVERTTYPE.NOUDC)) { - // We have a constant decimal that is out of range of the destination type. - // In both checked and unchecked contexts we issue an error. No need to recheck conversion in unchecked context. - // Decimal is a SimpleType represented in a FT_STRUCT - throw ErrorContext.Error(ErrorCode.ERR_ConstOutOfRange, ((ExprConstant)exprConst).Val.DecimalVal.ToString(CultureInfo.InvariantCulture), dest); - } - else if (simpleConstToSimpleDestination && Context.CheckedConstant) - { - // check if we failed because we are in checked mode... - bool okNow = canExplicitConversionBeBoundInUncheckedContext(expr, expr.Type, destExpr, flags | CONVERTTYPE.NOUDC); - - if (!okNow) - { - CantConvert(expr, dest); - goto CANTCONVERT; - } - - // Failed because value was out of range. Report nifty error message. - if (expr_type <= FUNDTYPE.FT_LASTINTEGRAL) - { - if (expr.Type.isUnsigned()) - value = ((ulong)((ExprConstant)exprConst).Int64Value).ToString(CultureInfo.InvariantCulture); - else - value = ((long)((ExprConstant)exprConst).Int64Value).ToString(CultureInfo.InvariantCulture); - } - else if (expr_type <= FUNDTYPE.FT_LASTNUMERIC) - { - value = ((ExprConstant)exprConst).Val.DoubleVal.ToString(CultureInfo.InvariantCulture); - } - else - { - // We should have taken care of constant decimal conversion errors - Debug.Assert(expr_type == FUNDTYPE.FT_STRUCT); - Debug.Assert(false, "Error in constant conversion logic!"); - } - - throw ErrorContext.Error(ErrorCode.ERR_ConstOutOfRangeChecked, value, dest); + throw CantConvert(expr, dest); } - if (expr.Type is NullType && dest.fundType() != FUNDTYPE.FT_REF) + // Failed because value was out of range. Report nifty error message. + string value; + if (exprType <= FUNDTYPE.FT_LASTINTEGRAL) { - throw ErrorContext.Error(ErrorCode.ERR_ValueCantBeNull, dest); + value = expr.Type.isUnsigned() + ? ((ulong)((ExprConstant)exprConst).Int64Value).ToString(CultureInfo.InvariantCulture) + : ((ExprConstant)exprConst).Int64Value.ToString(CultureInfo.InvariantCulture); + } + else + { + Debug.Assert(exprType <= FUNDTYPE.FT_LASTNUMERIC, "Error in constant conversion logic!"); + value = ((ExprConstant)exprConst).Val.DoubleVal.ToString(CultureInfo.InvariantCulture); } - CantConvert(expr, dest); + throw ErrorContext.Error(ErrorCode.ERR_ConstOutOfRangeChecked, value, dest); } } - CANTCONVERT: - exprResult = ExprFactory.CreateCast(0, destExpr, expr); - exprResult.SetError(); - return exprResult; + + if (expr.Type is NullType && dest.fundType() != FUNDTYPE.FT_REF) + { + throw ErrorContext.Error(ErrorCode.ERR_ValueCantBeNull, dest); + } + + throw CantConvert(expr, dest); } - private void CantConvert(Expr expr, CType dest) + private RuntimeBinderException CantConvert(Expr expr, CType dest) { // Generic "can't convert" error. - // Only report if we don't have an error type. - if (expr.Type != null && !(expr.Type is ErrorType)) - { - throw ErrorContext.Error(ErrorCode.ERR_NoExplicitConv, new ErrArg(expr.Type, ErrArgFlags.Unique), new ErrArg(dest, ErrArgFlags.Unique)); - } - } - public Expr mustCast(Expr expr, CType dest) - { - return mustCast(expr, dest, 0); - } - public Expr mustCast(Expr expr, CType dest, CONVERTTYPE flags) - { - ExprClass exprDest = ExprFactory.CreateClass(dest); - return mustCastCore(expr, exprDest, flags); + Debug.Assert(expr.Type != null); + return ErrorContext.Error(ErrorCode.ERR_NoExplicitConv, new ErrArg(expr.Type, ErrArgFlags.Unique), new ErrArg(dest, ErrArgFlags.Unique)); } + + public Expr mustCast(Expr expr, CType dest) => mustCast(expr, dest, 0); + + public Expr mustCast(Expr expr, CType dest, CONVERTTYPE flags) => mustCastCore(expr, dest, flags); + private Expr mustCastInUncheckedContext(Expr expr, CType dest, CONVERTTYPE flags) { BindingContext ctx = new BindingContext(Context); @@ -562,52 +504,48 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } // returns true if an explicit conversion exists from source type to dest type. flags is an optional parameter. - private bool canCast(CType src, CType dest, CONVERTTYPE flags) - { - ExprClass destExpr = ExprFactory.CreateClass(dest); - return BindExplicitConversion(null, src, destExpr, dest, flags); - } + private bool canCast(CType src, CType dest, CONVERTTYPE flags) => BindExplicitConversion(null, src, dest, flags); - private bool BindImplicitConversion(Expr pSourceExpr, CType pSourceType, ExprClass pDestinationTypeExpr, CType pDestinationTypeForLambdaErrorReporting, CONVERTTYPE flags) + private bool BindImplicitConversion(Expr pSourceExpr, CType pSourceType, CType destinationType, CONVERTTYPE flags) { - ImplicitConversion binder = new ImplicitConversion(this, pSourceExpr, pSourceType, pDestinationTypeExpr, false, flags); + ImplicitConversion binder = new ImplicitConversion(this, pSourceExpr, pSourceType, destinationType, false, flags); return binder.Bind(); } - private bool BindImplicitConversion(Expr pSourceExpr, CType pSourceType, ExprClass pDestinationTypeExpr, CType pDestinationTypeForLambdaErrorReporting, out Expr ppDestinationExpr, CONVERTTYPE flags) + private bool BindImplicitConversion(Expr pSourceExpr, CType pSourceType, CType destinationType, out Expr ppDestinationExpr, CONVERTTYPE flags) { - ImplicitConversion binder = new ImplicitConversion(this, pSourceExpr, pSourceType, pDestinationTypeExpr, true, flags); + ImplicitConversion binder = new ImplicitConversion(this, pSourceExpr, pSourceType, destinationType, true, flags); bool result = binder.Bind(); ppDestinationExpr = binder.ExprDest; return result; } - private bool BindImplicitConversion(Expr pSourceExpr, CType pSourceType, ExprClass pDestinationTypeExpr, CType pDestinationTypeForLambdaErrorReporting, bool needsExprDest, out Expr ppDestinationExpr, CONVERTTYPE flags) + private bool BindImplicitConversion(Expr pSourceExpr, CType pSourceType, CType destinationType, bool needsExprDest, out Expr ppDestinationExpr, CONVERTTYPE flags) { - ImplicitConversion binder = new ImplicitConversion(this, pSourceExpr, pSourceType, pDestinationTypeExpr, needsExprDest, flags); + ImplicitConversion binder = new ImplicitConversion(this, pSourceExpr, pSourceType, destinationType, needsExprDest, flags); bool result = binder.Bind(); ppDestinationExpr = needsExprDest ? binder.ExprDest : null; return result; } - private bool BindExplicitConversion(Expr pSourceExpr, CType pSourceType, ExprClass pDestinationTypeExpr, CType pDestinationTypeForLambdaErrorReporting, bool needsExprDest, out Expr ppDestinationExpr, CONVERTTYPE flags) + private bool BindExplicitConversion(Expr pSourceExpr, CType pSourceType, CType destinationType, bool needsExprDest, out Expr ppDestinationExpr, CONVERTTYPE flags) { - ExplicitConversion binder = new ExplicitConversion(this, pSourceExpr, pSourceType, pDestinationTypeExpr, pDestinationTypeForLambdaErrorReporting, needsExprDest, flags); + ExplicitConversion binder = new ExplicitConversion(this, pSourceExpr, pSourceType, destinationType, needsExprDest, flags); bool result = binder.Bind(); ppDestinationExpr = needsExprDest ? binder.ExprDest : null; return result; } - private bool BindExplicitConversion(Expr pSourceExpr, CType pSourceType, ExprClass pDestinationTypeExpr, CType pDestinationTypeForLambdaErrorReporting, out Expr ppDestinationExpr, CONVERTTYPE flags) + private bool BindExplicitConversion(Expr pSourceExpr, CType pSourceType, CType destinationType, out Expr ppDestinationExpr, CONVERTTYPE flags) { - ExplicitConversion binder = new ExplicitConversion(this, pSourceExpr, pSourceType, pDestinationTypeExpr, pDestinationTypeForLambdaErrorReporting, true, flags); + ExplicitConversion binder = new ExplicitConversion(this, pSourceExpr, pSourceType, destinationType, true, flags); bool result = binder.Bind(); ppDestinationExpr = binder.ExprDest; return result; } - private bool BindExplicitConversion(Expr pSourceExpr, CType pSourceType, ExprClass pDestinationTypeExpr, CType pDestinationTypeForLambdaErrorReporting, CONVERTTYPE flags) + private bool BindExplicitConversion(Expr pSourceExpr, CType pSourceType, CType destinationType, CONVERTTYPE flags) { - ExplicitConversion binder = new ExplicitConversion(this, pSourceExpr, pSourceType, pDestinationTypeExpr, pDestinationTypeForLambdaErrorReporting, false, flags); + ExplicitConversion binder = new ExplicitConversion(this, pSourceExpr, pSourceType, destinationType, false, flags); return binder.Bind(); } @@ -969,10 +907,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics Debug.Assert(n <= 0); if (n >= 0) { - if (!needExprDest) - return true; - iuciBestDst = iuci; - pexprDst = HandleAmbiguity(exprSrc, typeSrc, typeDst, prguci, iuciBestSrc, iuciBestDst); + if (needExprDest) + { + throw HandleAmbiguity(typeSrc, typeDst, prguci, iuciBestSrc, iuci); + } + return true; } } @@ -982,10 +921,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics Debug.Assert(n <= 0); if (n >= 0) { - if (!needExprDest) - return true; - iuciBestDst = iuci; - pexprDst = HandleAmbiguity(exprSrc, typeSrc, typeDst, prguci, iuciBestSrc, iuciBestDst); + if (needExprDest) + { + throw HandleAmbiguity(typeSrc, typeDst, prguci, iuciBestSrc, iuci); + } + return true; } } @@ -996,15 +936,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics if (iuciBest < 0) { - pexprDst = HandleAmbiguity(exprSrc, typeSrc, typeDst, prguci, iuciBestSrc, iuciBestDst); - return true; + throw HandleAmbiguity(typeSrc, typeDst, prguci, iuciBestSrc, iuciBestDst); } if (iuciAmbig >= 0) { - iuciBestSrc = iuciBest; - iuciBestDst = iuciAmbig; - pexprDst = HandleAmbiguity(exprSrc, typeSrc, typeDst, prguci, iuciBestSrc, iuciBestDst); - return true; + throw HandleAmbiguity(typeSrc, typeDst, prguci, iuciBest, iuciAmbig); } MethWithInst mwiBest = new MethWithInst(prguci[iuciBest].mwt.Meth(), prguci[iuciBest].mwt.GetType(), null); @@ -1040,7 +976,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // If lifting of the source is required, we need to figure out the intermediate conversion // from the type of the source to the type of the UD conversion parameter. Note that typeFrom // is not a nullable type. - Expr pConversionArgument = null; + Expr pConversionArgument; if (typeFrom != typeSrcBase) { // There is an intermediate conversion. @@ -1060,6 +996,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics pConversionArgument = exprSrc; } } + Debug.Assert(pConversionArgument != null); ExprCall pConversionCall = ExprFactory.CreateCall(0, typeDst, pConversionArgument, pMemGroup, mwiBest); pConversionCall.NullableCallLiftKind = NullableCallLiftKind.NotLiftedIntermediateConversion; @@ -1081,11 +1018,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return true; } - private Expr HandleAmbiguity(Expr exprSrc, CType typeSrc, CType typeDst, List prguci, int iuciBestSrc, int iuciBestDst) + private RuntimeBinderException HandleAmbiguity(CType typeSrc, CType typeDst, List prguci, int iuciBestSrc, int iuciBestDst) { Debug.Assert(0 <= iuciBestSrc && iuciBestSrc < prguci.Count); Debug.Assert(0 <= iuciBestDst && iuciBestDst < prguci.Count); - throw ErrorContext.Error(ErrorCode.ERR_AmbigUDConv, prguci[iuciBestSrc].mwt, prguci[iuciBestDst].mwt, typeSrc, typeDst); + return ErrorContext.Error(ErrorCode.ERR_AmbigUDConv, prguci[iuciBestSrc].mwt, prguci[iuciBestDst].mwt, typeSrc, typeDst); } private void MarkAsIntermediateConversion(Expr pExpr) @@ -1126,13 +1063,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics private Expr BindUDConversionCore(Expr pFrom, CType pTypeFrom, CType pTypeTo, CType pTypeDestination, MethWithInst mwiBest, out Expr ppTransformedArgument) { - ExprClass pClassFrom = ExprFactory.CreateClass(pTypeFrom); - Expr pTransformedArgument = mustCastCore(pFrom, pClassFrom, CONVERTTYPE.NOUDC); + Expr pTransformedArgument = mustCastCore(pFrom, pTypeFrom, CONVERTTYPE.NOUDC); Debug.Assert(pTransformedArgument != null); ExprMemberGroup pMemGroup = ExprFactory.CreateMemGroup(null, mwiBest); ExprCall pCall = ExprFactory.CreateCall(0, pTypeTo, pTransformedArgument, pMemGroup, mwiBest); - ExprClass pDestination = ExprFactory.CreateClass(pTypeDestination); - Expr pCast = mustCastCore(pCall, pDestination, CONVERTTYPE.NOUDC); + Expr pCast = mustCastCore(pCall, pTypeDestination, CONVERTTYPE.NOUDC); Debug.Assert(pCast != null); ppTransformedArgument = pTransformedArgument; return pCast; @@ -1141,12 +1076,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics /* * Fold a constant cast. Returns true if the constant could be folded. */ - private ConstCastResult bindConstantCast(Expr exprSrc, ExprClass exprTypeDest, bool needExprDest, out Expr pexprDest, bool explicitConversion) + private ConstCastResult bindConstantCast(Expr exprSrc, CType typeDest, bool needExprDest, out Expr pexprDest, bool explicitConversion) { pexprDest = null; long valueInt = 0; double valueFlt = 0; - CType typeDest = exprTypeDest.Type; FUNDTYPE ftSrc = exprSrc.Type.fundType(); FUNDTYPE ftDest = typeDest.fundType(); bool srcIntegral = (ftSrc <= FUNDTYPE.FT_LASTINTEGRAL); @@ -1157,7 +1091,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics if (ftSrc == FUNDTYPE.FT_STRUCT || ftDest == FUNDTYPE.FT_STRUCT) { // Do constant folding involving decimal constants. - Expr expr = bindDecimalConstCast(exprTypeDest, exprSrc.Type, constSrc); + Expr expr = bindDecimalConstCast(typeDest, exprSrc.Type, constSrc); if (expr == null) { @@ -1172,7 +1106,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return ConstCastResult.Success; } - if (explicitConversion && Context.CheckedConstant && !isConstantInRange(constSrc, typeDest, true)) + if (explicitConversion && Context.Checked && !isConstantInRange(constSrc, typeDest, true)) { return ConstCastResult.CheckFailure; } @@ -1399,9 +1333,8 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics /* * Bind a constant cast to or from decimal. Return null if cast can't be done. */ - private Expr bindDecimalConstCast(ExprClass exprDestType, CType srcType, ExprConstant src) + private Expr bindDecimalConstCast(CType destType, CType srcType, ExprConstant src) { - CType destType = exprDestType.Type; CType typeDecimal = SymbolLoader.GetPredefindType(PredefinedType.PT_DECIMAL); ConstVal cv; @@ -1508,12 +1441,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return null; } - private bool canExplicitConversionBeBoundInUncheckedContext(Expr exprSrc, CType typeSrc, ExprClass typeDest, CONVERTTYPE flags) + private bool canExplicitConversionBeBoundInUncheckedContext(Expr exprSrc, CType typeSrc, CType typeDest, CONVERTTYPE flags) { BindingContext ctx = new BindingContext(Context); Debug.Assert(typeDest != null); - Debug.Assert(typeDest.Type != null); - return (new ExpressionBinder(ctx)).BindExplicitConversion(exprSrc, typeSrc, typeDest, typeDest.Type, flags); + return (new ExpressionBinder(ctx)).BindExplicitConversion(exprSrc, typeSrc, typeDest, flags); } } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Conversions.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Conversions.cs index d16f7580d2..07114602cc 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Conversions.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Conversions.cs @@ -147,9 +147,9 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics AggregateSymbol aggIReadOnlyList = loader.GetPredefAgg(PredefinedType.PT_G_IREADONLYLIST); if ((aggIList == null || - !loader.IsBaseAggregate(aggIList, aggDst.getAggregate())) && + !SymbolLoader.IsBaseAggregate(aggIList, aggDst.getAggregate())) && (aggIReadOnlyList == null || - !loader.IsBaseAggregate(aggIReadOnlyList, aggDst.getAggregate()))) + !SymbolLoader.IsBaseAggregate(aggIReadOnlyList, aggDst.getAggregate()))) { return false; } @@ -179,9 +179,9 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics AggregateSymbol aggIReadOnlyList = loader.GetPredefAgg(PredefinedType.PT_G_IREADONLYLIST); if ((aggIList == null || - !loader.IsBaseAggregate(aggIList, aggtypeSrc.getAggregate())) && + !SymbolLoader.IsBaseAggregate(aggIList, aggtypeSrc.getAggregate())) && (aggIReadOnlyList == null || - !loader.IsBaseAggregate(aggIReadOnlyList, aggtypeSrc.getAggregate()))) + !SymbolLoader.IsBaseAggregate(aggIReadOnlyList, aggtypeSrc.getAggregate()))) { return false; } @@ -189,7 +189,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics CType typeArr = arrayDest.GetElementType(); CType typeLst = aggtypeSrc.GetTypeArgsAll()[0]; - Debug.Assert(!typeArr.IsNeverSameType()); + Debug.Assert(!(typeArr is MethodGroupType)); return typeArr == typeLst || FExpRefConv(loader, typeArr, typeLst); } if (HasGenericDelegateExplicitReferenceConversion(loader, typeSrc, typeDst)) @@ -248,10 +248,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // If they're identical then this one is automatically good, so skip it. // If we have an error type, then we're in some fault tolerance. Let it through. - if (pSourceArg == pTargetArg || pTargetArg is ErrorType || pSourceArg is ErrorType) + if (pSourceArg == pTargetArg) { continue; } + TypeParameterType pParam = (TypeParameterType)pTypeParams[iParam]; if (pParam.Invariant) { diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/EXPRExtensions.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/EXPRExtensions.cs index 075ed23a11..d22c42d33d 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/EXPRExtensions.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/EXPRExtensions.cs @@ -59,37 +59,27 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { return (expr == null) ? false : 0 != (expr.Flags & EXPRFLAG.EXF_CHECKOVERFLOW); } - public static bool isNull(this Expr expr) - { - return expr is ExprConstant constant && constant.IsOK && (expr.Type.fundType() == FUNDTYPE.FT_REF) && constant.Val.IsNullRef; - } - public static bool IsZero(this Expr expr) - { - return expr is ExprConstant constant && constant.IsOK && constant.IsZero; - } + public static bool isNull(this Expr expr) + => expr is ExprConstant constant && expr.Type.fundType() == FUNDTYPE.FT_REF && constant.Val.IsNullRef; + + public static bool IsZero(this Expr expr) => expr is ExprConstant constant && constant.IsZero; private static Expr GetSeqVal(this Expr expr) { // Scan through EK_SEQUENCE and EK_SEQREV exprs to get the real value. if (expr == null) + { return null; + } Expr exprVal = expr; - for (;;) + while (exprVal.Kind == ExpressionKind.Sequence) { - switch (exprVal.Kind) - { - default: - return exprVal; - case ExpressionKind.Sequence: - exprVal = ((ExprBinOp)exprVal).OptionalRightChild; - break; - case ExpressionKind.SequenceReverse: - exprVal = ((ExprBinOp)exprVal).OptionalLeftChild; - break; - } + exprVal = ((ExprBinOp)exprVal).OptionalRightChild; } + + return exprVal; } /*************************************************************************************************** @@ -100,11 +90,14 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics public static Expr GetConst(this Expr expr) { Expr exprVal = expr.GetSeqVal(); - if (null == exprVal || !exprVal.isCONSTANT_OK() && exprVal.Kind != ExpressionKind.ZeroInit) - return null; - return exprVal; - } + switch (exprVal?.Kind) + { + case ExpressionKind.Constant: + case ExpressionKind.ZeroInit: + return exprVal; + } - public static bool isCONSTANT_OK(this Expr expr) { return (expr == null) ? false : (expr.Kind == ExpressionKind.Constant && expr.IsOK); } + return null; + } } } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExplicitConversion.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExplicitConversion.cs index 78b895bfaf..53ab82cefb 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExplicitConversion.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExplicitConversion.cs @@ -19,7 +19,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics private Expr _exprSrc; private readonly CType _typeSrc; private readonly CType _typeDest; - private readonly ExprClass _exprTypeDest; // This is for lambda error reporting. The reason we have this is because we // store errors for lambda conversions, and then we don't bind the conversion @@ -36,7 +35,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // to report the error on, so that when the lambda conversion fails, it reports // errors on the correct type. - private readonly CType _pDestinationTypeForLambdaErrorReporting; private Expr _exprDest; private readonly bool _needsExprDest; private readonly CONVERTTYPE _flags; @@ -45,14 +43,12 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // BindExplicitConversion // ---------------------------------------------------------------------------- - public ExplicitConversion(ExpressionBinder binder, Expr exprSrc, CType typeSrc, ExprClass typeDest, CType pDestinationTypeForLambdaErrorReporting, bool needsExprDest, CONVERTTYPE flags) + public ExplicitConversion(ExpressionBinder binder, Expr exprSrc, CType typeSrc, CType typeDest, bool needsExprDest, CONVERTTYPE flags) { _binder = binder; _exprSrc = exprSrc; _typeSrc = typeSrc; - _typeDest = typeDest.Type; - _pDestinationTypeForLambdaErrorReporting = pDestinationTypeForLambdaErrorReporting; - _exprTypeDest = typeDest; + _typeDest = typeDest; _needsExprDest = needsExprDest; _flags = flags; _exprDest = null; @@ -100,13 +96,12 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // The set of explicit conversions includes all implicit conversions. // Don't try user-defined conversions now because we'll try them again later. - if (_binder.BindImplicitConversion(_exprSrc, _typeSrc, _exprTypeDest, _pDestinationTypeForLambdaErrorReporting, _needsExprDest, out _exprDest, _flags | CONVERTTYPE.ISEXPLICIT)) + if (_binder.BindImplicitConversion(_exprSrc, _typeSrc, _typeDest, _needsExprDest, out _exprDest, _flags | CONVERTTYPE.ISEXPLICIT)) { return true; } - if (_typeSrc == null || _typeDest == null || _typeSrc is ErrorType || - _typeDest is ErrorType || _typeDest.IsNeverSameType()) + if (_typeSrc == null || _typeDest == null || _typeDest is MethodGroupType) { return false; } @@ -194,7 +189,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // If S and T are value types and there is a builtin conversion from S => T then there is an // explicit conversion from S? => T that throws on null. - if (_typeDest.IsValType() && _binder.BindExplicitConversion(null, _typeSrc.StripNubs(), _exprTypeDest, _pDestinationTypeForLambdaErrorReporting, _flags | CONVERTTYPE.NOUDC)) + if (_typeDest.IsValType() && _binder.BindExplicitConversion(null, _typeSrc.StripNubs(), _typeDest, _flags | CONVERTTYPE.NOUDC)) { if (_needsExprDest) { @@ -205,7 +200,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } Debug.Assert(valueSrc.Type == _typeSrc.StripNubs()); - if (!_binder.BindExplicitConversion(valueSrc, valueSrc.Type, _exprTypeDest, _pDestinationTypeForLambdaErrorReporting, _needsExprDest, out _exprDest, _flags | CONVERTTYPE.NOUDC)) + if (!_binder.BindExplicitConversion(valueSrc, valueSrc.Type, _typeDest, _needsExprDest, out _exprDest, _flags | CONVERTTYPE.NOUDC)) { Debug.Fail("BindExplicitConversion failed unexpectedly"); return false; @@ -247,9 +242,9 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics AggregateSymbol aggIReadOnlyList = GetSymbolLoader().GetPredefAgg(PredefinedType.PT_G_IREADONLYLIST); if ((aggIList == null || - !GetSymbolLoader().IsBaseAggregate(aggIList, aggDest.getAggregate())) && + !SymbolLoader.IsBaseAggregate(aggIList, aggDest.getAggregate())) && (aggIReadOnlyList == null || - !GetSymbolLoader().IsBaseAggregate(aggIReadOnlyList, aggDest.getAggregate()))) + !SymbolLoader.IsBaseAggregate(aggIReadOnlyList, aggDest.getAggregate()))) { return false; } @@ -263,7 +258,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } if (_needsExprDest) - _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest, EXPRFLAG.EXF_REFCHECK); + _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest, EXPRFLAG.EXF_REFCHECK); return true; } @@ -288,9 +283,9 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics AggregateSymbol aggIReadOnlyList = GetSymbolLoader().GetPredefAgg(PredefinedType.PT_G_IREADONLYLIST); if ((aggIList == null || - !GetSymbolLoader().IsBaseAggregate(aggIList, aggSrc.getAggregate())) && + !SymbolLoader.IsBaseAggregate(aggIList, aggSrc.getAggregate())) && (aggIReadOnlyList == null || - !GetSymbolLoader().IsBaseAggregate(aggIReadOnlyList, aggSrc.getAggregate()))) + !SymbolLoader.IsBaseAggregate(aggIReadOnlyList, aggSrc.getAggregate()))) { return false; } @@ -298,13 +293,13 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics CType typeArr = arrayDest.GetElementType(); CType typeLst = aggSrc.GetTypeArgsAll()[0]; - Debug.Assert(!typeArr.IsNeverSameType()); + Debug.Assert(!(typeArr is MethodGroupType)); if (typeArr != typeLst && !CConversions.FExpRefConv(GetSymbolLoader(), typeArr, typeLst)) { return false; } if (_needsExprDest) - _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest, EXPRFLAG.EXF_REFCHECK); + _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest, EXPRFLAG.EXF_REFCHECK); return true; } @@ -330,7 +325,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics if (CConversions.FExpRefConv(GetSymbolLoader(), arraySrc.GetElementType(), arrayDest.GetElementType())) { if (_needsExprDest) - _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest, EXPRFLAG.EXF_REFCHECK); + _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest, EXPRFLAG.EXF_REFCHECK); return true; } @@ -361,7 +356,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics if (_binder.canConvert(_binder.GetPredefindType(PredefinedType.PT_ARRAY), _typeSrc, CONVERTTYPE.NOUDC)) { if (_needsExprDest) - _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest, EXPRFLAG.EXF_REFCHECK); + _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest, EXPRFLAG.EXF_REFCHECK); return true; } return false; @@ -380,7 +375,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics if (_typeSrc is PointerType || _typeSrc.fundType() <= FUNDTYPE.FT_LASTINTEGRAL && _typeSrc.isNumericType()) { if (_needsExprDest) - _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest); + _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest); return true; } return false; @@ -428,7 +423,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics if (_exprSrc.GetConst() != null) { - ConstCastResult result = _binder.bindConstantCast(_exprSrc, _exprTypeDest, _needsExprDest, out _exprDest, true); + ConstCastResult result = _binder.bindConstantCast(_exprSrc, _typeDest, _needsExprDest, out _exprDest, true); if (result == ConstCastResult.Success) { return AggCastResult.Success; @@ -440,7 +435,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } if (_needsExprDest) - _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest); + _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest); return AggCastResult.Success; } @@ -453,7 +448,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics if (_exprSrc.GetConst() != null) { // Fold the constant cast if possible. - ConstCastResult result = _binder.bindConstantCast(_exprSrc, _exprTypeDest, _needsExprDest, out _exprDest, true); + ConstCastResult result = _binder.bindConstantCast(_exprSrc, _typeDest, _needsExprDest, out _exprDest, true); if (result == ConstCastResult.Success) { return AggCastResult.Success; // else, don't fold and use a regular cast, below. @@ -478,7 +473,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics if (bIsConversionOK) { // upcast to the Enum type - _binder.bindSimpleCast(_exprDest, _exprTypeDest, out _exprDest); + _binder.bindSimpleCast(_exprDest, _typeDest, out _exprDest); } } return bIsConversionOK ? AggCastResult.Success : AggCastResult.Failure; @@ -502,8 +497,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } else { - ExprClass underlyingExpr = GetExprFactory().CreateClass(underlyingType); - _binder.bindSimpleCast(_exprSrc, underlyingExpr, out exprCast); + _binder.bindSimpleCast(_exprSrc, underlyingType, out exprCast); } // There is always an implicit conversion from any integral type to decimal. @@ -511,7 +505,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics if (exprCast.GetConst() != null) { // Fold the constant cast if possible. - ConstCastResult result = _binder.bindConstantCast(exprCast, _exprTypeDest, _needsExprDest, out _exprDest, true); + ConstCastResult result = _binder.bindConstantCast(exprCast, _typeDest, _needsExprDest, out _exprDest, true); if (result == ConstCastResult.Success) { return AggCastResult.Success; // else, don't fold and use a regular cast, below. @@ -558,7 +552,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // Transform constant to constant. if (_exprSrc.GetConst() != null) { - ConstCastResult result = _binder.bindConstantCast(_exprSrc, _exprTypeDest, _needsExprDest, out _exprDest, true); + ConstCastResult result = _binder.bindConstantCast(_exprSrc, _typeDest, _needsExprDest, out _exprDest, true); if (result == ConstCastResult.Success) { return AggCastResult.Success; @@ -569,14 +563,14 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } } if (_needsExprDest) - _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest); + _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest); return AggCastResult.Success; } else if (_typeSrc.isPredefined() && (_typeSrc.isPredefType(PredefinedType.PT_OBJECT) || _typeSrc.isPredefType(PredefinedType.PT_VALUE) || _typeSrc.isPredefType(PredefinedType.PT_ENUM))) { if (_needsExprDest) - _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest, EXPRFLAG.EXF_UNBOX); + _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest, EXPRFLAG.EXF_UNBOX); return AggCastResult.Success; } return AggCastResult.Failure; @@ -620,7 +614,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics if (_exprSrc.GetConst() != null) { // Fold the constant cast if possible. - ConstCastResult result = _binder.bindConstantCast(_exprSrc, _exprTypeDest, _needsExprDest, out _exprDest, true); + ConstCastResult result = _binder.bindConstantCast(_exprSrc, _typeDest, _needsExprDest, out _exprDest, true); if (result == ConstCastResult.Success) { return AggCastResult.Success; // else, don't fold and use a regular cast, below. @@ -644,7 +638,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } else { - _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest, (_flags & CONVERTTYPE.CHECKOVERFLOW) != 0 ? EXPRFLAG.EXF_CHECKOVERFLOW : 0); + _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest, (_flags & CONVERTTYPE.CHECKOVERFLOW) != 0 ? EXPRFLAG.EXF_CHECKOVERFLOW : 0); } } return bConversionOk ? AggCastResult.Success : AggCastResult.Failure; @@ -681,11 +675,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { if (aggDest.IsValueType() && aggSrc.getThisType().fundType() == FUNDTYPE.FT_REF) { - _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest, EXPRFLAG.EXF_UNBOX); + _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest, EXPRFLAG.EXF_UNBOX); } else { - _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest, EXPRFLAG.EXF_REFCHECK | (_exprSrc?.Flags & EXPRFLAG.EXF_CANTBENULL ?? 0)); + _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest, EXPRFLAG.EXF_REFCHECK | (_exprSrc?.Flags & EXPRFLAG.EXF_CANTBENULL ?? 0)); } } return AggCastResult.Success; @@ -697,7 +691,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics CConversions.HasGenericDelegateExplicitReferenceConversion(GetSymbolLoader(), _typeSrc, aggTypeDest)) { if (_needsExprDest) - _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest, EXPRFLAG.EXF_REFCHECK | (_exprSrc?.Flags & EXPRFLAG.EXF_CANTBENULL ?? 0)); + _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest, EXPRFLAG.EXF_REFCHECK | (_exprSrc?.Flags & EXPRFLAG.EXF_CANTBENULL ?? 0)); return AggCastResult.Success; } return AggCastResult.Failure; @@ -716,7 +710,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return AggCastResult.Failure; } if (_needsExprDest) - _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest); + _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest); return AggCastResult.Success; } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExprFactory.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExprFactory.cs index 99814e51ea..846de0e004 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExprFactory.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExprFactory.cs @@ -41,7 +41,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics Name name = method.Sym?.name; MethodOrPropertySymbol methProp = method.MethProp(); - CType type = method.GetType() ?? (CType)Types.GetErrorSym(); + CType type = method.GetType(); return CreateMemGroup( 0, name, method.TypeArgs, methProp?.getKind() ?? SYMKIND.SK_MethodSymbol, method.GetType(), methProp, @@ -51,19 +51,14 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics public ExprUserDefinedConversion CreateUserDefinedConversion(Expr arg, Expr call, MethWithInst method) => new ExprUserDefinedConversion(arg, call, method); - public ExprCast CreateCast(CType type, Expr argument) => CreateCast(0, CreateClass(type), argument); + public ExprCast CreateCast(CType type, Expr argument) => CreateCast(0, type, argument); - public ExprCast CreateCast(EXPRFLAG flags, ExprClass type, Expr argument) => new ExprCast(flags, type, argument); - - public ExprReturn CreateReturn(Expr optionalObject) => new ExprReturn(optionalObject); + public ExprCast CreateCast(EXPRFLAG flags, CType type, Expr argument) => new ExprCast(flags, type, argument); public ExprLocal CreateLocal(LocalVariableSymbol local) => new ExprLocal(local); - public ExprBoundLambda CreateAnonymousMethod(AggregateType delegateType, Scope argumentScope) => - new ExprBoundLambda(delegateType, argumentScope); - - public ExprHoistedLocalExpr CreateHoistedLocalInExpression() => - new ExprHoistedLocalExpr(Types.GetPredefAgg(PredefinedType.PT_EXPRESSION).getThisType()); + public ExprBoundLambda CreateAnonymousMethod(AggregateType delegateType, Scope argumentScope, Expr expression) => + new ExprBoundLambda(delegateType, argumentScope, expression); public ExprMethodInfo CreateMethodInfo(MethPropWithInst mwi) => CreateMethodInfo(mwi.Meth(), mwi.GetType(), mwi.TypeArgs); @@ -81,10 +76,9 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics public ExprFieldInfo CreateFieldInfo(FieldSymbol field, AggregateType fieldType) => new ExprFieldInfo(field, fieldType, Types.GetPredefAgg(PredefinedType.PT_FIELDINFO).getThisType()); - private ExprTypeOf CreateTypeOf(ExprClass sourceType) => + public ExprTypeOf CreateTypeOf(CType sourceType) => new ExprTypeOf(Types.GetPredefAgg(PredefinedType.PT_TYPE).getThisType(), sourceType); - public ExprTypeOf CreateTypeOf(CType sourceType) => CreateTypeOf(CreateClass(sourceType)); public ExprUserLogicalOp CreateUserLogOp(CType type, Expr trueFalseCall, ExprCall operatorCall) => new ExprUserLogicalOp(type, trueFalseCall, operatorCall); @@ -126,7 +120,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics case FUNDTYPE.FT_PTR: { // Just allocate a new node and fill it in. - return CreateCast(0, CreateClass(type), CreateNull()); + return CreateCast(0, type, CreateNull()); } case FUNDTYPE.FT_STRUCT: @@ -153,8 +147,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics public ExprConstant CreateBoolConstant(bool b) => CreateConstant(Types.GetPredefAgg(PredefinedType.PT_BOOL).getThisType(), ConstVal.Get(b)); - public ExprBlock CreateBlock(ExprStatement pOptionalStatements) => new ExprBlock(pOptionalStatements); - public ExprArrayIndex CreateArrayIndex(CType type, Expr array, Expr index) => new ExprArrayIndex(type, array, index); @@ -198,9 +190,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics //////////////////////////////////////////////////////////////////////////////// // Create a node that evaluates the first, evaluates the second, results in the first. - public ExprBinOp CreateReverseSequence(Expr first, Expr second) => - CreateBinop(ExpressionKind.SequenceReverse, first.Type, first, second); - public ExprAssignment CreateAssignment(Expr left, Expr right) => new ExprAssignment(left, right); //////////////////////////////////////////////////////////////////////////////// diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExpressionBinder.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExpressionBinder.cs index e604eca643..00ca1e2c6b 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExpressionBinder.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExpressionBinder.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using Microsoft.CSharp.RuntimeBinder.Errors; using Microsoft.CSharp.RuntimeBinder.Syntax; @@ -26,7 +27,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { public int carg; public TypeArray types; - public bool fHasExprs; public List prgexpr; } @@ -106,8 +106,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics internal enum CheckLvalueKind { Assignment, - OutParameter, - Increment, + Increment } internal enum BinOpFuncKind @@ -118,8 +117,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics DelBinOp, EnumBinOp, IntBinOp, - PtrBinOp, - PtrCmpOp, RealBinOp, RefCmpOp, ShiftOp, @@ -368,17 +365,8 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics private CType getVoidType() { return VoidType; } - private Expr GenerateAssignmentConversion(Expr op1, Expr op2, bool allowExplicit) - { - if (allowExplicit) - { - return mustCastCore(op2, GetExprFactory().CreateClass(op1.Type), 0); - } - else - { - return mustConvertCore(op2, GetExprFactory().CreateClass(op1.Type)); - } - } + private Expr GenerateAssignmentConversion(Expr op1, Expr op2, bool allowExplicit) => + allowExplicit ? mustCastCore(op2, op1.Type, 0) : mustConvertCore(op2, op1.Type); //////////////////////////////////////////////////////////////////////////////// // Bind the simple assignment operator =. @@ -388,25 +376,17 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics Debug.Assert(op1 is ExprCast || op1 is ExprArrayIndex || op1 is ExprCall - || op1 is ExprProperty + || op1 is ExprProperty || op1 is ExprClass || op1 is ExprField); - if (!checkLvalue(op1, CheckLvalueKind.Assignment)) - { - ExprAssignment rval = GetExprFactory().CreateAssignment(op1, op2); - rval.SetError(); - return rval; - } - + CheckLvalue(op1, CheckLvalueKind.Assignment); op2 = GenerateAssignmentConversion(op1, op2, allowExplicit); return GenerateOptimizedAssignment(op1, op2); } internal Expr BindArrayIndexCore(Expr pOp1, Expr pOp2) { - bool bIsError = !pOp1.IsOK || !pOp2.IsOK; - CType pIntType = GetPredefindType(PredefinedType.PT_INT); ArrayType pArrayType = pOp1.Type as ArrayType; @@ -421,37 +401,24 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics x => { Expr pTemp = mustConvert(x, pDestType); - if (pDestType == pIntType) - return pTemp; - ExprClass exprType = GetExprFactory().CreateClass(pDestType); - return GetExprFactory().CreateCast(EXPRFLAG.EXF_INDEXEXPR, exprType, pTemp); + return pDestType == pIntType + ? pTemp + : GetExprFactory().CreateCast(EXPRFLAG.EXF_INDEXEXPR, pDestType, pTemp); }); // Allocate a new expression, the type is the element type of the array. // Array index operations are always lvalues. - Expr pExpr = GetExprFactory().CreateArrayIndex(elementType, pOp1, transformedIndices); - - if (bIsError) - { - pExpr.SetError(); - } - - return pExpr; + return GetExprFactory().CreateArrayIndex(elementType, pOp1, transformedIndices); } //////////////////////////////////////////////////////////////////////////////// // Create a cast node with the given expression flags. - private void bindSimpleCast(Expr exprSrc, ExprClass typeDest, out Expr pexprDest) - { + private void bindSimpleCast(Expr exprSrc, CType typeDest, out Expr pexprDest) => bindSimpleCast(exprSrc, typeDest, out pexprDest, 0); - } - private void bindSimpleCast(Expr exprSrc, ExprClass exprTypeDest, out Expr pexprDest, EXPRFLAG exprFlags) + private void bindSimpleCast(Expr exprSrc, CType typeDest, out Expr pexprDest, EXPRFLAG exprFlags) { - Debug.Assert(exprTypeDest != null); - Debug.Assert(exprTypeDest.Type != null); - CType typeDest = exprTypeDest.Type; - pexprDest = null; + Debug.Assert(typeDest != null); // If the source is a constant, and cast is really simple (no change in fundamental // type, no flags), then create a new constant node with the new type instead of // creating a cast node. This allows compile-time constants to be easily recognized. @@ -460,8 +427,8 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // Make the cast expr anyway, and if we find that we have a constant, then set the cast expr // as the original tree for the constant. Otherwise, return the cast expr. - ExprCast exprCast = GetExprFactory().CreateCast(exprFlags, exprTypeDest, exprSrc); - if (Context.CheckedNormal) + ExprCast exprCast = GetExprFactory().CreateCast(exprFlags, typeDest, exprSrc); + if (Context.Checked) { exprCast.Flags |= EXPRFLAG.EXF_CHECKOVERFLOW; } @@ -480,7 +447,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics pexprDest = exprCast; Debug.Assert(exprCast.Argument != null); - return; } //////////////////////////////////////////////////////////////////////////////// @@ -500,11 +466,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics bool fConstrained; Expr pObject = pMemGroup.OptionalObject; CType callingObjectType = pObject?.Type; - PostBindMethod(ref mwi, pObject); + PostBindMethod(mwi); pObject = AdjustMemberObject(mwi, pObject, out fConstrained); pMemGroup.OptionalObject = pObject; - CType pReturnType = null; + CType pReturnType; if ((flags & (MemLookFlags.Ctor | MemLookFlags.NewObj)) == (MemLookFlags.Ctor | MemLookFlags.NewObj)) { pReturnType = mwi.Ats; @@ -516,11 +482,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics ExprCall pResult = GetExprFactory().CreateCall(0, pReturnType, pArguments, pMemGroup, mwi); - if (!pResult.IsOK) - { - return pResult; - } - // Set the return type and flags for constructors. if ((flags & MemLookFlags.Ctor) != 0) { @@ -554,26 +515,12 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics Debug.Assert(fwt.GetType() != null && fwt.Field().getClass() == fwt.GetType().getAggregate()); CType pFieldType = GetTypes().SubstType(fwt.Field().GetType(), fwt.GetType()); - if (pOptionalObject != null && !pOptionalObject.IsOK) - { - ExprField pField = GetExprFactory().CreateField(pFieldType, pOptionalObject, fwt, false); - pField.SetError(); - return pField; - } - - bool pfConstrained; - pOptionalObject = AdjustMemberObject(fwt, pOptionalObject, out pfConstrained); + pOptionalObject = AdjustMemberObject(fwt, pOptionalObject, out _); checkUnsafe(pFieldType); // added to the binder so we don't bind to pointer ops - bool isLValue = pOptionalObject?.Type is PointerType || objectIsLvalue(pOptionalObject); - - // Exception: a readonly field is not an lvalue unless we're in the constructor/static constructor appropriate - // for the field. - if (fwt.Field().isReadOnly) - { - isLValue = false; - } + // lvalue if the object is an lvalue (or it's static) and the field is not readonly. + bool isLValue = objectIsLvalue(pOptionalObject) && !fwt.Field().isReadOnly; AggregateType fieldType = null; // If this field is the backing field of a WindowsRuntime event then we need to bind to its @@ -594,11 +541,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics ExprField pResult = GetExprFactory() .CreateField(pFieldType, pOptionalObject, fwt, isLValue); - if (pFieldType is ErrorType) - { - pResult.SetError(); - } - Debug.Assert(BindingFlag.BIND_MEMBERSET == (BindingFlag)EXPRFLAG.EXF_MEMBERSET); pResult.Flags |= (EXPRFLAG)(bindFlags & BindingFlag.BIND_MEMBERSET); @@ -636,7 +578,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics ExprMemberGroup memGroup = GetExprFactory().CreateMemGroup(getOrCreateCall, mpwi); PropWithType pwt = new PropWithType(invocationList, fieldType); - Expr propertyExpr = BindToProperty(getOrCreateCall, pwt, bindFlags, null, null, memGroup); + Expr propertyExpr = BindToProperty(getOrCreateCall, pwt, bindFlags, null, memGroup); return propertyExpr; } @@ -645,28 +587,21 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics //////////////////////////////////////////////////////////////////////////////// - internal Expr BindToProperty(Expr pObject, PropWithType pwt, BindingFlag bindFlags, Expr args, AggregateType pOtherType, ExprMemberGroup pMemGroup) + internal ExprProperty BindToProperty(Expr pObject, PropWithType pwt, BindingFlag bindFlags, Expr args, ExprMemberGroup pMemGroup) { - Debug.Assert(pwt.Sym != null && - pwt.Sym is PropertySymbol && + Debug.Assert(pwt.Sym is PropertySymbol && pwt.GetType() != null && pwt.Prop().getClass() == pwt.GetType().getAggregate()); Debug.Assert(pwt.Prop().Params.Count == 0 || pwt.Prop() is IndexerSymbol); - Debug.Assert(pOtherType == null || - !(pwt.Prop() is IndexerSymbol) && - pOtherType.getAggregate() == pwt.Prop().RetType.getAggregate()); bool fConstrained; - MethWithType mwtGet; - MethWithType mwtSet; - Expr pObjectThrough = null; // We keep track of the type of the pObject which we're doing the call through so that we can report // protection access errors later, either below when binding the get, or later when checking that // the setter is actually an lvalue. - pObjectThrough = pObject; + Expr pObjectThrough = pObject; - PostBindProperty(pwt, pObject, out mwtGet, out mwtSet); + PostBindProperty(pwt, pObject, out MethWithType mwtGet, out MethWithType mwtSet); if (mwtGet && (!mwtSet || @@ -685,17 +620,9 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { pObject = AdjustMemberObject(pwt, pObject, out fConstrained); } + pMemGroup.OptionalObject = pObject; - CType pReturnType = GetTypes().SubstType(pwt.Prop().RetType, pwt.GetType()); - Debug.Assert(pOtherType == pReturnType || pOtherType == null); - - if (pObject != null && !pObject.IsOK) - { - ExprProperty pResult = GetExprFactory().CreateProperty(pReturnType, pObjectThrough, args, pMemGroup, pwt, null); - pResult.SetError(); - return pResult; - } // if we are doing a get on this thing, and there is no get, and // most importantly, we are not leaving the arguments to be bound by the array index @@ -704,39 +631,25 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { if (!mwtGet) { - if (pOtherType != null) - { - return GetExprFactory().CreateClass(pOtherType); - } - throw ErrorContext.Error(ErrorCode.ERR_PropertyLacksGet, pwt); } - else + + CType type = null; + if (pObjectThrough != null) { - CType type = null; - if (pObjectThrough != null) + type = pObjectThrough.Type; + } + + ACCESSERROR error = SemanticChecker.CheckAccess2(mwtGet.Meth(), mwtGet.GetType(), ContextForMemberLookup(), type); + if (error != ACCESSERROR.ACCESSERROR_NOERROR) + { + // if the get exists, but is not accessible, give an error. + if (error == ACCESSERROR.ACCESSERROR_NOACCESSTHRU) { - type = pObjectThrough.Type; + throw ErrorContext.Error(ErrorCode.ERR_BadProtectedAccess, pwt, type, ContextForMemberLookup()); } - ACCESSERROR error = SemanticChecker.CheckAccess2(mwtGet.Meth(), mwtGet.GetType(), ContextForMemberLookup(), type); - if (error != ACCESSERROR.ACCESSERROR_NOERROR) - { - // if the get exists, but is not accessible, give an error. - if (pOtherType != null) - { - return GetExprFactory().CreateClass(pOtherType); - } - - if (error == ACCESSERROR.ACCESSERROR_NOACCESSTHRU) - { - throw ErrorContext.Error(ErrorCode.ERR_BadProtectedAccess, pwt, type, ContextForMemberLookup()); - } - else - { - throw ErrorContext.Error(ErrorCode.ERR_InaccessibleGetter, pwt); - } - } + throw ErrorContext.Error(ErrorCode.ERR_InaccessibleGetter, pwt); } } @@ -756,10 +669,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { result.Flags |= EXPRFLAG.EXF_LVALUE; } - if (pOtherType != null) - { - result.Flags |= EXPRFLAG.EXF_SAMENAMETYPE; - } return result; } @@ -799,7 +708,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // Find the next operator. methCur = methCur == null ? GetSymbolLoader().LookupAggMember(pName, atsCur.getAggregate(), symbmask_t.MASK_MethodSymbol) as MethodSymbol - : GetSymbolLoader().LookupNextSym(methCur, atsCur.getAggregate(), symbmask_t.MASK_MethodSymbol) as MethodSymbol; + : SymbolLoader.LookupNextSym(methCur, atsCur.getAggregate(), symbmask_t.MASK_MethodSymbol) as MethodSymbol; if (methCur == null) { @@ -910,9 +819,8 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics //////////////////////////////////////////////////////////////////////////////// // Given a method group or indexer group, bind it to the arguments for an - // invocation. This method can change the arguments to bind with Extension - // Methods - private bool BindMethodGroupToArgumentsCore(out GroupToArgsBinderResult pResults, BindingFlag bindFlags, ExprMemberGroup grp, ref Expr args, int carg, bool bHasNamedArgumentSpecifiers) + // invocation. + private GroupToArgsBinderResult BindMethodGroupToArgumentsCore(BindingFlag bindFlags, ExprMemberGroup grp, Expr args, int carg, NamedArgumentsKind namedArgumentsKind) { ArgInfos pargInfo = new ArgInfos {carg = carg}; FillInArgInfoFromArgList(pargInfo, args); @@ -920,72 +828,50 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics ArgInfos pOriginalArgInfo = new ArgInfos {carg = carg}; FillInArgInfoFromArgList(pOriginalArgInfo, args); - GroupToArgsBinder binder = new GroupToArgsBinder(this, bindFlags, grp, pargInfo, pOriginalArgInfo, bHasNamedArgumentSpecifiers, null/*atsDelegate*/); - bool retval = binder.Bind(bReportErrors: true); + GroupToArgsBinder binder = new GroupToArgsBinder(this, bindFlags, grp, pargInfo, pOriginalArgInfo, namedArgumentsKind); + binder.Bind(); - pResults = binder.GetResultsOfBind(); - return retval; + return binder.GetResultsOfBind(); } //////////////////////////////////////////////////////////////////////////////// // Given a method group or indexer group, bind it to the arguments for an // invocation. - internal Expr BindMethodGroupToArguments(BindingFlag bindFlags, ExprMemberGroup grp, Expr args) + internal ExprWithArgs BindMethodGroupToArguments(BindingFlag bindFlags, ExprMemberGroup grp, Expr args) { Debug.Assert(grp.SymKind == SYMKIND.SK_MethodSymbol || grp.SymKind == SYMKIND.SK_PropertySymbol && ((grp.Flags & EXPRFLAG.EXF_INDEXER) != 0)); // Count the args. - bool argTypeErrors; - int carg = CountArguments(args, out argTypeErrors); - // We need to store the object because BindMethodGroupToArgumentsCore will - // null it out in the case of an extension method, which is then consumed - // by BindToMethod. After that, we want to set the object back. - Expr pObject = grp.OptionalObject; + int carg = CountArguments(args); - // If we weren't given a pName, then we couldn't bind the method pName, so we should - // just bail out of here. + Debug.Assert(grp.Name != null); - if (grp.Name == null) - { - ExprCall rval = GetExprFactory().CreateCall(0, GetTypes().GetErrorSym(), args, grp, null); - rval.SetError(); - return rval; - } - - // If we have named arguments specified, make sure we have them all appearing after - // fixed arguments. - bool seenNamed = VerifyNamedArgumentsAfterFixed(args); - - GroupToArgsBinderResult result; - if (!BindMethodGroupToArgumentsCore(out result, bindFlags, grp, ref args, carg, seenNamed)) - { - Debug.Assert(false, "Why didn't BindMethodGroupToArgumentsCore throw an error?"); - return null; - } - - Expr exprRes; - MethPropWithInst mpwiBest = result.GetBestResult(); + // Do we have named arguments specified, are they after fixed arguments (can position) or + // non-trailing (can rule out methods only). + NamedArgumentsKind namedKind = FindNamedArgumentsType(args); + MethPropWithInst mpwiBest = BindMethodGroupToArgumentsCore(bindFlags, grp, args, carg, namedKind).BestResult; if (grp.SymKind == SYMKIND.SK_PropertySymbol) { Debug.Assert((grp.Flags & EXPRFLAG.EXF_INDEXER) != 0); - //PropWithType pwt = new PropWithType(mpwiBest.Prop(), mpwiBest.GetType()); + return BindToProperty(grp.OptionalObject, new PropWithType(mpwiBest), bindFlags, args, grp); + } - exprRes = BindToProperty(grp.OptionalObject, new PropWithType(mpwiBest), bindFlags, args, null/*typeOther*/, grp); - } - else - { - exprRes = BindToMethod(new MethWithInst(mpwiBest), args, grp, (MemLookFlags)grp.Flags); - } - return exprRes; + return BindToMethod(new MethWithInst(mpwiBest), args, grp, (MemLookFlags)grp.Flags); } ///////////////////////////////////////////////////////////////////////////////// - private bool VerifyNamedArgumentsAfterFixed(Expr args) + public enum NamedArgumentsKind + { + None, + Positioning, + NonTrailing + } + + private static NamedArgumentsKind FindNamedArgumentsType(Expr args) { Expr list = args; - bool seenNamed = false; while (list != null) { Expr arg; @@ -1003,92 +889,58 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics Debug.Assert(arg != null); if (arg is ExprNamedArgumentSpecification) { - seenNamed = true; - } - else - { - if (seenNamed) + while (list != null) { - throw GetErrorContext().Error(ErrorCode.ERR_NamedArgumentSpecificationBeforeFixedArgument); + if (list is ExprList nextList) + { + arg = nextList.OptionalElement; + list = nextList.OptionalNextListNode; + } + else + { + arg = list; + list = null; + } + + if (!(arg is ExprNamedArgumentSpecification)) + { + return NamedArgumentsKind.NonTrailing; + } } + + return NamedArgumentsKind.Positioning; } } - return seenNamed; + return NamedArgumentsKind.None; } //////////////////////////////////////////////////////////////////////////////// // Report a bad operator types error to the user. - private ExprOperator BadOperatorTypesError(ExpressionKind ek, Expr pOperand1, Expr pOperand2) + private RuntimeBinderException BadOperatorTypesError(Expr pOperand1, Expr pOperand2) { - return BadOperatorTypesError(ek, pOperand1, pOperand2, null); - } - - private ExprOperator BadOperatorTypesError(ExpressionKind ek, Expr pOperand1, Expr pOperand2, CType pTypeErr) - { - // This is a hack, but we need to store the operation somewhere... the first argument's as + // This is a hack, but we need to store the operation somewhere... the first argument's as // good a place as any. string strOp = pOperand1.ErrorString; - pOperand1 = UnwrapExpression(pOperand1); + Debug.Assert(pOperand1 != null); + Debug.Assert(pOperand1.Type != null); - if (pOperand1 != null) + if (pOperand2 != null) { - if (pOperand2 != null) - { - pOperand2 = UnwrapExpression(pOperand2); - if (pOperand1.Type != null && - !(pOperand1.Type is ErrorType) && - pOperand2.Type != null && - !(pOperand2.Type is ErrorType)) - { - throw ErrorContext.Error(ErrorCode.ERR_BadBinaryOps, strOp, pOperand1.Type, pOperand2.Type); - } - } - else if (pOperand1.Type != null && !(pOperand1.Type is ErrorType)) - { - throw ErrorContext.Error(ErrorCode.ERR_BadUnaryOp, strOp, pOperand1.Type); - } + Debug.Assert(pOperand2.Type != null); + return ErrorContext.Error(ErrorCode.ERR_BadBinaryOps, strOp, pOperand1.Type, pOperand2.Type); } - if (pTypeErr == null) - { - pTypeErr = GetPredefindType(PredefinedType.PT_OBJECT); - } - - ExprOperator rval = GetExprFactory().CreateOperator(ek, pTypeErr, pOperand1, pOperand2); - rval.SetError(); - return rval; - } - - private Expr UnwrapExpression(Expr pExpression) - { - while (pExpression is ExprWrap wrap) - { - Expr wrapped = wrap.OptionalExpression; - if (wrapped == null) - { - break; - } - - pExpression = wrapped; - } - - return pExpression; + return ErrorContext.Error(ErrorCode.ERR_BadUnaryOp, strOp, pOperand1.Type); } private static ErrorCode GetStandardLvalueError(CheckLvalueKind kind) { Debug.Assert(kind >= CheckLvalueKind.Assignment && kind <= CheckLvalueKind.Increment); - switch (kind) - { - case CheckLvalueKind.OutParameter: - return ErrorCode.ERR_RefLvalueExpected; - case CheckLvalueKind.Increment: - return ErrorCode.ERR_IncrementLvalueExpected; - default: - return ErrorCode.ERR_AssgLvalueExpected; - } + return kind == CheckLvalueKind.Increment + ? ErrorCode.ERR_IncrementLvalueExpected + : ErrorCode.ERR_AssgLvalueExpected; } private void CheckLvalueProp(ExprProperty prop) @@ -1121,29 +973,25 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics //////////////////////////////////////////////////////////////////////////////// // A false return means not to process the expr any further - it's totally out // of place. For example - a method group or an anonymous method. - private bool checkLvalue(Expr expr, CheckLvalueKind kind) + private void CheckLvalue(Expr expr, CheckLvalueKind kind) { - if (!expr.IsOK) - return false; if (expr.isLvalue()) { if (expr is ExprProperty prop) { CheckLvalueProp(prop); } + markFieldAssigned(expr); - return true; + return; } + Debug.Assert(!(expr is ExprLocal)); + Debug.Assert(!(expr is ExprMemberGroup)); + switch (expr.Kind) { case ExpressionKind.Property: - if (kind == CheckLvalueKind.OutParameter) - { - // passing a property as ref or out - throw ErrorContext.Error(ErrorCode.ERR_RefProperty); - } - ExprProperty prop = (ExprProperty)expr; if (!prop.MethWithTypeSet) { @@ -1177,43 +1025,22 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics case ExpressionKind.BoundLambda: case ExpressionKind.Constant: throw ErrorContext.Error(GetStandardLvalueError(kind)); - - case ExpressionKind.MemberGroup: - ErrorCode err = (kind == CheckLvalueKind.OutParameter) ? ErrorCode.ERR_RefReadonlyLocalCause : ErrorCode.ERR_AssgReadonlyLocalCause; - throw ErrorContext.Error(err, ((ExprMemberGroup)expr).Name, new ErrArgIds(MessageID.MethodGroup)); } TryReportLvalueFailure(expr, kind); - return true; } - private void PostBindMethod(ref MethWithInst pMWI, Expr pObject) + private void PostBindMethod(MethWithInst pMWI) { - MethWithInst mwiOrig = pMWI; - - // If it is virtual, find a remap of the method to something more specific. This - // may alter where the method is found. - if (pObject != null && pObject.Type.isSimpleType()) + MethodSymbol meth = pMWI.Meth(); + if (meth.RetType != null) { - RemapToOverride(GetSymbolLoader(), pMWI, pObject.Type); - } - - if (pMWI.Meth().RetType != null) - { - checkUnsafe(pMWI.Meth().RetType); + checkUnsafe(meth.RetType); // We need to check unsafe on the parameters as well, since we cannot check in conversion. - TypeArray pParams = pMWI.Meth().Params; - - for (int i = 0; i < pParams.Count; i++) + foreach (CType type in meth.Params.Items) { - // This is an optimization: don't call this in the vast majority of cases - CType type = pParams[i]; - - if (type.isUnsafe()) - { - checkUnsafe(type); - } + checkUnsafe(type); } } } @@ -1391,53 +1218,8 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // non-struct types are lvalues (such as non-struct method returns) ); } - //////////////////////////////////////////////////////////////////////////////// - // For a base call we need to remap from the virtual to the specific override - // to invoke. This is also used to map a virtual on pObject (like ToString) to - // the specific override when the pObject is a simple type (int, bool, char, - // etc). In these cases it is safe to assume that any override won't later be - // removed.... We start searching from "typeObj" up the superclass hierarchy - // until we find a method with an exact signature match. - private static void RemapToOverride(SymbolLoader symbolLoader, SymWithType pswt, CType typeObj) - { - // For a property/indexer we remap the accessors, not the property/indexer. - // Since every event has both accessors we remap the event instead of the accessors. - Debug.Assert(pswt && (pswt.Sym is MethodSymbol || pswt.Sym is EventSymbol || pswt.Sym is MethodOrPropertySymbol)); - Debug.Assert(typeObj != null); - - // Don't remap static or interface methods. - if (typeObj is NullableType nubTypeObj) - { - typeObj = nubTypeObj.GetAts(); - } - - // Don't remap non-virtual members - if (!(typeObj is AggregateType atsObj) || atsObj.isInterfaceType() || !pswt.Sym.IsVirtual()) - { - return; - } - - symbmask_t mask = pswt.Sym.mask(); - - // Search for an override version of the method. - while (atsObj != null && atsObj.getAggregate() != pswt.Sym.parent) - { - for (Symbol symT = symbolLoader.LookupAggMember(pswt.Sym.name, atsObj.getAggregate(), mask); - symT != null; - symT = symbolLoader.LookupNextSym(symT, atsObj.getAggregate(), mask)) - { - if (symT.IsOverride() && (symT.SymBaseVirtual() == pswt.Sym || symT.SymBaseVirtual() == pswt.Sym.SymBaseVirtual())) - { - pswt.Set(symT, atsObj); - return; - } - } - atsObj = atsObj.GetBaseClass(); - } - } - - private void verifyMethodArgs(IExprWithArgs call, CType callingObjectType) + private void verifyMethodArgs(ExprWithArgs call, CType callingObjectType) { Debug.Assert(call != null); Expr argsPtr = call.OptionalArguments; @@ -1464,10 +1246,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics MethodSymbol m = mp as MethodSymbol; int argCount = ExpressionIterator.Count(argsPtr); - if (m != null && m.isVarargs) - { - paramCount--; // we don't care about the vararg sentinel - } + Debug.Assert(!@params.Items.Any(p => p is ArgumentListType)); // We should never have picked a varargs method to bind to. bool bDontFixParamArray = false; @@ -1513,6 +1292,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } index++; } + Debug.Assert(index != mp.Params.Count); CType substDestType = GetTypes().SubstType(@params[index], type, pTypeArgs); @@ -1712,7 +1492,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics internal void FillInArgInfoFromArgList(ArgInfos argInfo, Expr args) { CType[] prgtype = new CType[argInfo.carg]; - argInfo.fHasExprs = true; argInfo.prgexpr = new List(); int iarg = 0; @@ -1731,15 +1510,9 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } Debug.Assert(arg != null); + Debug.Assert(arg.Type != null); - if (arg.Type != null) - { - prgtype[iarg] = arg.Type; - } - else - { - prgtype[iarg] = GetTypes().GetErrorSym(); - } + prgtype[iarg] = arg.Type; argInfo.prgexpr.Add(arg); } @@ -1765,7 +1538,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics @params.CopyItems(0, @params.Count - 1, prgtype); CType type = @params[@params.Count - 1]; - CType elementType = null; if (!(type is ArrayType arr)) { @@ -1775,7 +1547,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } // At this point, we have an array sym. - elementType = arr.GetElementType(); + CType elementType = arr.GetElementType(); for (int itype = @params.Count - 1; itype < count; itype++) { @@ -2038,10 +1810,9 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return GetExprFactory().CreateAssignment(op1, op2); } - internal static int CountArguments(Expr args, out bool typeErrors) + internal static int CountArguments(Expr args) { int carg = 0; - typeErrors = false; for (Expr list = args; list != null; carg++) { Expr arg; @@ -2058,12 +1829,8 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } Debug.Assert(arg != null); - - if (arg.Type == null || arg.Type is ErrorType) - { - typeErrors = true; - } } + return carg; } } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExpressionKind.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExpressionKind.cs index ac62995cf8..cac3b3a87e 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExpressionKind.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExpressionKind.cs @@ -6,102 +6,34 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { internal enum ExpressionKind { - Block, - //EK_STMTAS, - Return, - //EK_DECL, - //EK_LABEL, - //EK_PLACEHOLDERLABEL, - //EK_GOTO, - //EK_GOTOIF, - //EK_FlatSwitch, - //EK_SWITCHLABEL, - //EK_ENDSWITCHLABEL, - //EK_TRY, - //EK_HANDLER, - //EK_THROW, NoOp, - //EK_DEBUGNOOP, - - //EK_For, - //EK_Foreach, - //EK_Using, - //EK_Switch, - //EK_SwitchSection, - //EK_SwitchSectionLabel, - //EK_Lock, - - //EK_Attribute, // Now expressions. Keep BINOP first! BinaryOp, UnaryOp, Assignment, List, - //QuestionMark, - //EK_MAKEREFANY, - //EK_TYPEREFANY, ArrayIndex, - //ArrayLength, - //EK_ARGUMENTHANDLE, Call, - //Event, Field, Local, - //EK_BASE, - //ThisPointer, Constant, - //EK_TYPEORSIMPLENAME, - // The following exprs are used to represent the results of typebinding. - // Look in exprnodes.h for a more detailed description. - //TypeArguments, - //EK_TYPEORNAMESPACE, - //EK_TYPEORNAMESPACEERROR, - //EK_ARRAYTYPE, - //EK_POINTERTYPE, - //EK_NULLABLETYPE, Class, - //EK_NSPACE, - //EK_ALIAS, - // End type exprs. - //EK_ERROR, - //FunctionPointer, Property, Multi, MultiGet, - //EK_STTMP, - //EK_LDTMP, - //EK_FREETMP, Wrap, Concat, ArrayInit, - //EK_ARRAYCREATION, Cast, - //EK_EXPLICITCAST, UserDefinedConversion, - //EK_ARGLIST, - //EK_NEWTYVAR, TypeOf, - //EK_SIZEOF, ZeroInit, UserLogicalOp, MemberGroup, BoundLambda, - //UnboundLambda, - //EK_LAMBDAPARAMETER, - HoistedLocalExpression, FieldInfo, MethodInfo, PropertyInfo, - //EK_DBLQMARK, - //EK_VALUERA, - //EK_INITIALIZER, - //EK_INITASSIGN, - //EK_LOCALLOC, - //EK_IS, - //EK_AS, - //EK_DELEGATECREATION, - //EK_COLLECTIONELEMENT, - //EK_METHODBODY, NamedArgumentSpecification, /*************************************************************************************************** @@ -143,7 +75,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics LogicalAnd, LogicalOr, Sequence, // p1 is side effects, p2 is values - SequenceReverse, // p1 is values, p2 is side effects Save, // p1 is expr, p2 is wrap to be saved into... Swap, Indir, diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/GlobalSymbolContext.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/GlobalSymbolContext.cs index 85bbceea22..5648093d61 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/GlobalSymbolContext.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/GlobalSymbolContext.cs @@ -14,22 +14,18 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics internal sealed class GlobalSymbolContext { private readonly PredefinedTypes _predefTypes; - private readonly NameManager _nameManager; - public GlobalSymbolContext(NameManager namemgr) + public GlobalSymbolContext() { - GlobalSymbols = new BSYMMGR(namemgr); + GlobalSymbols = new BSYMMGR(); _predefTypes = new PredefinedTypes(GlobalSymbols); TypeManager = new TypeManager(GlobalSymbols, _predefTypes); - - _nameManager = namemgr; } public TypeManager TypeManager { get; } public TypeManager GetTypes() { return TypeManager; } private BSYMMGR GlobalSymbols { get; } public BSYMMGR GetGlobalSymbols() { return GlobalSymbols; } - public NameManager GetNameManager() { return _nameManager; } public PredefinedTypes GetPredefTypes() { return _predefTypes; } public SymFactory GetGlobalSymbolFactory() diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/GroupToArgsBinder.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/GroupToArgsBinder.cs index e7a8694d12..ca14129b71 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/GroupToArgsBinder.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/GroupToArgsBinder.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using Microsoft.CSharp.RuntimeBinder.Errors; using Microsoft.CSharp.RuntimeBinder.Syntax; @@ -32,19 +33,18 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics private readonly ExprMemberGroup _pGroup; private readonly ArgInfos _pArguments; private readonly ArgInfos _pOriginalArguments; - private readonly bool _bHasNamedArguments; - private readonly AggregateType _pDelegate; + private readonly NamedArgumentsKind _namedArgumentsKind; private AggregateType _pCurrentType; private MethodOrPropertySymbol _pCurrentSym; private TypeArray _pCurrentTypeArgs; private TypeArray _pCurrentParameters; - private TypeArray _pBestParameters; private int _nArgBest; // end of current namespaces extension method list private readonly GroupToArgsBinderResult _results; private readonly List _methList; private readonly MethPropWithInst _mpwiParamTypeConstraints; private readonly MethPropWithInst _mpwiBogus; + private readonly MethPropWithInst _misnamed; private readonly MethPropWithInst _mpwiCantInferInstArg; private readonly MethWithType _mwtBadArity; private Name _pInvalidSpecifiedName; @@ -57,7 +57,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics private readonly List _HiddenTypes; private bool _bArgumentsChangedForNamedOrOptionalArguments; - public GroupToArgsBinder(ExpressionBinder exprBinder, BindingFlag bindFlags, ExprMemberGroup grp, ArgInfos args, ArgInfos originalArgs, bool bHasNamedArguments, AggregateType atsDelegate) + public GroupToArgsBinder(ExpressionBinder exprBinder, BindingFlag bindFlags, ExprMemberGroup grp, ArgInfos args, ArgInfos originalArgs, NamedArgumentsKind namedArgumentsKind) { Debug.Assert(grp != null); Debug.Assert(exprBinder != null); @@ -69,18 +69,17 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics _pGroup = grp; _pArguments = args; _pOriginalArguments = originalArgs; - _bHasNamedArguments = bHasNamedArguments; - _pDelegate = atsDelegate; + _namedArgumentsKind = namedArgumentsKind; _pCurrentType = null; _pCurrentSym = null; _pCurrentTypeArgs = null; _pCurrentParameters = null; - _pBestParameters = null; _nArgBest = -1; _results = new GroupToArgsBinderResult(); _methList = new List(); _mpwiParamTypeConstraints = new MethPropWithInst(); _mpwiBogus = new MethPropWithInst(); + _misnamed = new MethPropWithInst(); _mpwiCantInferInstArg = new MethPropWithInst(); _mwtBadArity = new MethWithType(); _HiddenTypes = new List(); @@ -90,25 +89,15 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // This method does the actual binding. // ---------------------------------------------------------------------------- - public bool Bind(bool bReportErrors) + public void Bind() { Debug.Assert(_pGroup.SymKind == SYMKIND.SK_MethodSymbol || _pGroup.SymKind == SYMKIND.SK_PropertySymbol && 0 != (_pGroup.Flags & EXPRFLAG.EXF_INDEXER)); - // We need the Exprs for error reporting for non-delegates - Debug.Assert(_pDelegate != null || _pArguments.fHasExprs); - LookForCandidates(); - if (!GetResultOfBind(bReportErrors)) + if (!GetResultOfBind()) { - if (bReportErrors) - { - throw ReportErrorsOnFailure(); - } - - return false; + throw ReportErrorsOnFailure(); } - - return true; } public GroupToArgsBinderResult GetResultsOfBind() @@ -148,9 +137,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // iterator will only return propsyms (or methsyms, or whatever) symbmask_t mask = (symbmask_t)(1 << (int)_pGroup.SymKind); - CType pTypeThrough = _pGroup.OptionalObject?.Type; - CMemberLookupResults.CMethodIterator iterator = _pGroup.MemberLookupResults.GetMethodIterator(GetSemanticChecker(), GetSymbolLoader(), pTypeThrough, GetTypeQualifier(_pGroup), _pExprBinder.ContextForMemberLookup(), true, // AllowBogusAndInaccessible - false, _pGroup.TypeArgs.Count, _pGroup.Flags, mask); + CMemberLookupResults.CMethodIterator iterator = _pGroup.MemberLookupResults.GetMethodIterator(GetSemanticChecker(), GetSymbolLoader(), GetTypeQualifier(_pGroup), _pExprBinder.ContextForMemberLookup(), _pGroup.TypeArgs.Count, _pGroup.Flags, mask, _namedArgumentsKind == NamedArgumentsKind.NonTrailing ? _pOriginalArguments : null); while (true) { bool bFoundExpanded; @@ -184,23 +171,20 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } // If we have named arguments, reorder them for this method. - if (_pArguments.fHasExprs) + // If we don't have Exprs, its because we're doing a method group conversion. + // In those scenarios, we never want to add named arguments or optional arguments. + if (_namedArgumentsKind == NamedArgumentsKind.Positioning) { - // If we don't have Exprs, its because we're doing a method group conversion. - // In those scenarios, we never want to add named arguments or optional arguments. - if (_bHasNamedArguments) + if (!ReOrderArgsForNamedArguments()) { - if (!ReOrderArgsForNamedArguments()) - { - continue; - } + continue; } - else if (HasOptionalParameters()) + } + else if (HasOptionalParameters()) + { + if (!AddArgumentsForOptionalParameters()) { - if (!AddArgumentsForOptionalParameters()) - { - continue; - } + continue; } } @@ -220,7 +204,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // If we cant use the current symbol, then we've filtered it, so get the next one. - if (!iterator.CanUseCurrentSymbol()) + if (!iterator.CanUseCurrentSymbol) { continue; } @@ -234,17 +218,25 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } // Check access. - bool fCanAccess = !iterator.IsCurrentSymbolInaccessible(); - if (!fCanAccess && (!_methList.IsEmpty() || _results.GetInaccessibleResult())) + bool fCanAccess = !iterator.IsCurrentSymbolInaccessible; + if (!fCanAccess && (!_methList.IsEmpty() || _results.InaccessibleResult)) { // We'll never use this one for error reporting anyway, so just skip it. bSearchForExpanded = false; continue; } + // Check misnamed. + bool misnamed = fCanAccess && iterator.IsCurrentSymbolMisnamed; + if (misnamed && (!_methList.IsEmpty() || _results.InaccessibleResult || _misnamed)) + { + bSearchForExpanded = false; + continue; + } + // Check bogus. - bool fBogus = fCanAccess && iterator.IsCurrentSymbolBogus(); - if (fBogus && (!_methList.IsEmpty() || _results.GetInaccessibleResult() || _mpwiBogus)) + bool fBogus = fCanAccess && !misnamed && iterator.IsCurrentSymbolBogus; + if (fBogus && (!_methList.IsEmpty() || _results.InaccessibleResult || _mpwiBogus || _misnamed)) { // We'll never use this one for error reporting anyway, so just skip it. bSearchForExpanded = false; @@ -263,8 +255,13 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { // In case we never get an accessible method, this will allow us to give // a better error... - Debug.Assert(!_results.GetInaccessibleResult()); - _results.GetInaccessibleResult().Set(_pCurrentSym, _pCurrentType, _pCurrentTypeArgs); + Debug.Assert(!_results.InaccessibleResult); + _results.InaccessibleResult.Set(_pCurrentSym, _pCurrentType, _pCurrentTypeArgs); + } + else if (misnamed) + { + Debug.Assert(!_misnamed); + _misnamed.Set(_pCurrentSym, _pCurrentType, _pCurrentTypeArgs); } else if (fBogus) { @@ -321,8 +318,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { dst.carg = src.carg; dst.types = src.types; - dst.fHasExprs = src.fHasExprs; - dst.prgexpr.Clear(); for (int i = 0; i < src.prgexpr.Count; i++) { @@ -330,7 +325,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } } - private bool GetResultOfBind(bool bReportErrors) + private bool GetResultOfBind() { // We looked at all the evidence, and we come to render the verdict: @@ -345,35 +340,23 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics else { // We have some ambiguities, lets sort them out. - CandidateFunctionMember pAmbig1 = null; - CandidateFunctionMember pAmbig2 = null; - CType pTypeThrough = _pGroup.OptionalObject?.Type; - pmethBest = _pExprBinder.FindBestMethod(_methList, pTypeThrough, _pArguments, out pAmbig1, out pAmbig2); + pmethBest = _pExprBinder.FindBestMethod(_methList, pTypeThrough, _pArguments, out CandidateFunctionMember pAmbig1, out CandidateFunctionMember pAmbig2); if (null == pmethBest) { - // Arbitrarily use the first one, but make sure to report errors or give the ambiguous one - // back to the caller. - pmethBest = pAmbig1; _results.AmbiguousResult = pAmbig2.mpwi; - - if (bReportErrors) + if (pAmbig1.@params != pAmbig2.@params || + pAmbig1.mpwi.MethProp().Params.Count != pAmbig2.mpwi.MethProp().Params.Count || + pAmbig1.mpwi.TypeArgs != pAmbig2.mpwi.TypeArgs || + pAmbig1.mpwi.GetType() != pAmbig2.mpwi.GetType() || + pAmbig1.mpwi.MethProp().Params == pAmbig2.mpwi.MethProp().Params) { - if (pAmbig1.@params != pAmbig2.@params || - pAmbig1.mpwi.MethProp().Params.Count != pAmbig2.mpwi.MethProp().Params.Count || - pAmbig1.mpwi.TypeArgs != pAmbig2.mpwi.TypeArgs || - pAmbig1.mpwi.GetType() != pAmbig2.mpwi.GetType() || - pAmbig1.mpwi.MethProp().Params == pAmbig2.mpwi.MethProp().Params) - { - throw GetErrorContext().Error(ErrorCode.ERR_AmbigCall, pAmbig1.mpwi, pAmbig2.mpwi); - } - else - { - // The two signatures are identical so don't use the type args in the error message. - throw GetErrorContext().Error(ErrorCode.ERR_AmbigCall, pAmbig1.mpwi.MethProp(), pAmbig2.mpwi.MethProp()); - } + throw GetErrorContext().Error(ErrorCode.ERR_AmbigCall, pAmbig1.mpwi, pAmbig2.mpwi); } + + // The two signatures are identical so don't use the type args in the error message. + throw GetErrorContext().Error(ErrorCode.ERR_AmbigCall, pAmbig1.mpwi.MethProp(), pAmbig2.mpwi.MethProp()); } } @@ -383,10 +366,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // Record our best match in the memgroup as well. This is temporary. - if (bReportErrors) + if (true) { ReportErrorsOnSuccess(); } + return true; } @@ -543,7 +527,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics CType pParamType = type; CType pRawParamType = type.StripNubs(); - Expr optionalArgument = null; + Expr optionalArgument; if (methprop.HasDefaultParameterValue(index)) { CType pConstValType = methprop.GetDefaultParameterValueConstValType(index); @@ -614,15 +598,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics FieldSymbol field = symbolLoader.LookupAggMember(name, agg, symbmask_t.MASK_FieldSymbol) as FieldSymbol; FieldWithType fwt = new FieldWithType(field, agg.getThisType()); ExprField exprField = exprFactory.CreateField(agg.getThisType(), null, fwt, false); - - if (agg.getThisType() != type) - { - optionalArgument = exprFactory.CreateCast(type, exprField); - } - else - { - optionalArgument = exprField; - } + optionalArgument = exprFactory.CreateCast(type, exprField); } } else @@ -694,7 +670,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { for (MethodOrPropertySymbol meth = symbolLoader.LookupAggMember(method.name, pAggregate, symbmask_t.MASK_MethodSymbol | symbmask_t.MASK_PropertySymbol) as MethodOrPropertySymbol; meth != null; - meth = symbolLoader.LookupNextSym(meth, pAggregate, symbmask_t.MASK_MethodSymbol | symbmask_t.MASK_PropertySymbol) as MethodOrPropertySymbol) + meth = SymbolLoader.LookupNextSym(meth, pAggregate, symbmask_t.MASK_MethodSymbol | symbmask_t.MASK_PropertySymbol) as MethodOrPropertySymbol) { if (!meth.isOverride) { @@ -862,12 +838,12 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // return false. private bool GetNextSym(CMemberLookupResults.CMethodIterator iterator) { - if (!iterator.MoveNext(_methList.IsEmpty())) + if (!iterator.MoveNext()) { return false; } - _pCurrentSym = iterator.GetCurrentSymbol(); - AggregateType type = iterator.GetCurrentType(); + _pCurrentSym = iterator.CurrentSymbol; + AggregateType type = iterator.CurrentType; // If our current type is null, this is our first iteration, so set the type. // If our current type is not null, and we've got a new type now, and we've already matched @@ -888,11 +864,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics while (_HiddenTypes.Contains(_pCurrentType)) { // Move through this type and get the next one. - for (; iterator.GetCurrentType() == _pCurrentType; iterator.MoveNext(_methList.IsEmpty())) ; - _pCurrentSym = iterator.GetCurrentSymbol(); - _pCurrentType = iterator.GetCurrentType(); + for (; iterator.CurrentType == _pCurrentType; iterator.MoveNext()) ; + _pCurrentSym = iterator.CurrentSymbol; + _pCurrentType = iterator.CurrentType; - if (iterator.AtEnd()) + if (iterator.AtEnd) { return false; } @@ -958,15 +934,12 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // Try to infer. If we have an errorsym in the type arguments, we know we cant infer, // but we want to attempt it anyway. We'll mark this as "cant infer" so that we can - // report the appropriate error, but we'll continue inferring, since we want + // report the appropriate error, but we'll continue inferring, since we want // error sym to go to any type. - bool inferenceSucceeded; - - inferenceSucceeded = MethodTypeInferrer.Infer( - _pExprBinder, GetSymbolLoader(), - methSym, _pCurrentType.GetTypeArgsAll(), _pCurrentParameters, - _pArguments, out _pCurrentTypeArgs); + bool inferenceSucceeded = MethodTypeInferrer.Infer( + _pExprBinder, GetSymbolLoader(), methSym, _pCurrentParameters, _pArguments, + out _pCurrentTypeArgs); if (!inferenceSucceeded) { @@ -1015,23 +988,16 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics containsErrorSym |= DoesTypeArgumentsContainErrorSym(var); bool fresult; - if (_pArguments.fHasExprs) - { - Expr pArgument = _pArguments.prgexpr[ivar]; + Expr pArgument = _pArguments.prgexpr[ivar]; - // If we have a named argument, strip it to do the conversion. - if (pArgument is ExprNamedArgumentSpecification named) - { - pArgument = named.Value; - } - - fresult = _pExprBinder.canConvert(pArgument, var); - } - else + // If we have a named argument, strip it to do the conversion. + if (pArgument is ExprNamedArgumentSpecification named) { - fresult = _pExprBinder.canConvert(_pArguments.types[ivar], var); + pArgument = named.Value; } + fresult = _pExprBinder.canConvert(pArgument, var); + // Mark this as a legitimate error if we didn't have any error syms. if (!fresult && !containsErrorSym) { @@ -1040,10 +1006,9 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics _nArgBest = ivar; // If we already have best method for instance methods don't overwrite with extensions - if (!_results.GetBestResult()) + if (!_results.BestResult) { - _results.GetBestResult().Set(_pCurrentSym, _pCurrentType, _pCurrentTypeArgs); - _pBestParameters = _pCurrentParameters; + _results.BestResult.Set(_pCurrentSym, _pCurrentType, _pCurrentTypeArgs); } } else if (ivar == _nArgBest && _pArguments.types[ivar] != var) @@ -1058,21 +1023,13 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics if (argStripped == varStripped) { // If we already have best method for instance methods don't overwrite with extensions - if (!_results.GetBestResult()) + if (!_results.BestResult) { - _results.GetBestResult().Set(_pCurrentSym, _pCurrentType, _pCurrentTypeArgs); - _pBestParameters = _pCurrentParameters; + _results.BestResult.Set(_pCurrentSym, _pCurrentType, _pCurrentTypeArgs); } } } - if (_pCurrentSym is MethodSymbol meth) - { - // Do not store the result if we have an extension method and the instance - // parameter isn't convertible. - - _results.AddInconvertibleResult(meth, _pCurrentType, _pCurrentTypeArgs); - } return false; } } @@ -1083,20 +1040,13 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics if (_results.IsBetterUninferableResult(_pCurrentTypeArgs) && _pCurrentSym is MethodSymbol meth) { // If we're an instance method then mark us down. - _results.GetUninferableResult().Set(meth, _pCurrentType, _pCurrentTypeArgs); + _results.UninferableResult.Set(meth, _pCurrentType, _pCurrentTypeArgs); } - } - else - { - if (_pCurrentSym is MethodSymbol meth) - { - // Do not store the result if we have an extension method and the instance - // parameter isn't convertible. - _results.AddInconvertibleResult(meth, _pCurrentType, _pCurrentTypeArgs); - } + return false; } - return !containsErrorSym; + + return true; } private void UpdateArguments() @@ -1158,7 +1108,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics for (int i = 0; i < typeVars.Count; i++) { CType type = typeVars[i]; - if (type is ErrorType) + if (type == null) { return true; } @@ -1182,21 +1132,21 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // used for Methods and Indexers Debug.Assert(_pGroup.SymKind == SYMKIND.SK_MethodSymbol || _pGroup.SymKind == SYMKIND.SK_PropertySymbol && 0 != (_pGroup.Flags & EXPRFLAG.EXF_INDEXER)); Debug.Assert(_pGroup.TypeArgs.Count == 0 || _pGroup.SymKind == SYMKIND.SK_MethodSymbol); - Debug.Assert(0 == (_pGroup.Flags & EXPRFLAG.EXF_USERCALLABLE) || _results.GetBestResult().MethProp().isUserCallable()); + Debug.Assert(0 == (_pGroup.Flags & EXPRFLAG.EXF_USERCALLABLE) || _results.BestResult.MethProp().isUserCallable()); if (_pGroup.SymKind == SYMKIND.SK_MethodSymbol) { - Debug.Assert(_results.GetBestResult().MethProp() is MethodSymbol); + Debug.Assert(_results.BestResult.MethProp() is MethodSymbol); - if (_results.GetBestResult().TypeArgs.Count > 0) + if (_results.BestResult.TypeArgs.Count > 0) { // Check method type variable constraints. - TypeBind.CheckMethConstraints(GetSemanticChecker(), GetErrorContext(), new MethWithInst(_results.GetBestResult())); + TypeBind.CheckMethConstraints(GetSemanticChecker(), GetErrorContext(), new MethWithInst(_results.BestResult)); } } } - private Exception ReportErrorsOnFailure() + private RuntimeBinderException ReportErrorsOnFailure() { // First and foremost, report if the user specified a name more than once. if (_pDuplicateSpecifiedName != null) @@ -1206,14 +1156,45 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics Debug.Assert(_methList.IsEmpty()); // Report inaccessible. - if (_results.GetInaccessibleResult()) + if (_results.InaccessibleResult) { // We might have called this, but it is inaccessible... - return GetSemanticChecker().ReportAccessError(_results.GetInaccessibleResult(), _pExprBinder.ContextForMemberLookup(), GetTypeQualifier(_pGroup)); + return GetSemanticChecker().ReportAccessError(_results.InaccessibleResult, _pExprBinder.ContextForMemberLookup(), GetTypeQualifier(_pGroup)); } - // Report bogus. - if (_mpwiBogus) + if (_misnamed) + { + // Get exception immediately for the non-trailing named argument being misplaced. + // Handle below for the name not being present at all. + List paramNames = FindMostDerivedMethod(_misnamed.MethProp(), _pGroup.OptionalObject).ParameterNames; + for (int i = 0; ; ++i) + { + if (i == _pOriginalArguments.carg) + { + // If we're here we had the correct name used for the first params argument. + // Report it as not matching the correct number of arguments below. + break; + } + + if (_pOriginalArguments.prgexpr[i] is ExprNamedArgumentSpecification named) + { + Name name = named.Name; + if (paramNames[i] != name) + { + // We have the bad name. Is it misplaced or absent? + if (paramNames.Contains(name)) + { + return GetErrorContext().Error(ErrorCode.ERR_BadNonTrailingNamedArgument, name); + } + + // Let this be handled by _pInvalidSpecifiedName handling. + _pInvalidSpecifiedName = name; + break; + } + } + } + } + else if (_mpwiBogus) { // We might have called this, but it is bogus... return GetErrorContext().Error(ErrorCode.ERR_BindToBogus, _mpwiBogus); @@ -1228,29 +1209,29 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics _pGroup.OptionalObject.Type.isDelegateType() && _pGroup.Name == NameManager.GetPredefinedName(PredefinedName.PN_INVOKE)) { - Debug.Assert(!_results.GetBestResult() || _results.GetBestResult().MethProp().getClass().IsDelegate()); - Debug.Assert(!_results.GetBestResult() || _results.GetBestResult().GetType().getAggregate().IsDelegate()); + Debug.Assert(!_results.BestResult || _results.BestResult.MethProp().getClass().IsDelegate()); + Debug.Assert(!_results.BestResult || _results.BestResult.GetType().getAggregate().IsDelegate()); bUseDelegateErrors = true; nameErr = _pGroup.OptionalObject.Type.getAggregate().name; } - if (_results.GetBestResult()) + if (_results.BestResult) { // If we had some invalid arguments for best matching. - return ReportErrorsForBestMatching(bUseDelegateErrors, nameErr); + return ReportErrorsForBestMatching(bUseDelegateErrors); } - if (_results.GetUninferableResult() || _mpwiCantInferInstArg) + if (_results.UninferableResult || _mpwiCantInferInstArg) { - if (!_results.GetUninferableResult()) + if (!_results.UninferableResult) { //copy the extension method for which instance argument type inference failed - _results.GetUninferableResult().Set(_mpwiCantInferInstArg.Sym as MethodSymbol, _mpwiCantInferInstArg.GetType(), _mpwiCantInferInstArg.TypeArgs); + _results.UninferableResult.Set(_mpwiCantInferInstArg.Sym as MethodSymbol, _mpwiCantInferInstArg.GetType(), _mpwiCantInferInstArg.TypeArgs); } - Debug.Assert(_results.GetUninferableResult().Sym is MethodSymbol); + Debug.Assert(_results.UninferableResult.Sym is MethodSymbol); MethWithType mwtCantInfer = new MethWithType(); - mwtCantInfer.Set(_results.GetUninferableResult().Meth(), _results.GetUninferableResult().GetType()); + mwtCantInfer.Set(_results.UninferableResult.Meth(), _results.UninferableResult.GetType()); return GetErrorContext().Error(ErrorCode.ERR_CantInferMethTypeArgs, mwtCantInfer); } @@ -1284,11 +1265,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return GetErrorContext().Error(ErrorCode.ERR_NamedArgumentUsedInPositional, _pNameUsedInPositionalArgument); } - if (_pDelegate != null) - { - return GetErrorContext().Error(ErrorCode.ERR_MethDelegateMismatch, nameErr, _pDelegate); - } - // The number of arguments must be wrong. if (_fCandidatesUnsupported) @@ -1311,22 +1287,15 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return GetErrorContext().Error(ErrorCode.ERR_BadArgCount, nameErr, _pArguments.carg); } - private RuntimeBinderException ReportErrorsForBestMatching(bool bUseDelegateErrors, Name nameErr) + private RuntimeBinderException ReportErrorsForBestMatching(bool bUseDelegateErrors) { - // Best matching overloaded method 'name' had some invalid arguments. - if (_pDelegate != null) - { - return GetErrorContext().Error( - ErrorCode.ERR_MethDelegateMismatch, nameErr, _pDelegate, _results.GetBestResult()); - } - if (bUseDelegateErrors) { // Point to the Delegate, not the Invoke method - return GetErrorContext().Error(ErrorCode.ERR_BadDelArgTypes, _results.GetBestResult().GetType()); + return GetErrorContext().Error(ErrorCode.ERR_BadDelArgTypes, _results.BestResult.GetType()); } - return GetErrorContext().Error(ErrorCode.ERR_BadArgTypes, _results.GetBestResult()); + return GetErrorContext().Error(ErrorCode.ERR_BadArgTypes, _results.BestResult); } } } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/GroupToArgsBinderResult.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/GroupToArgsBinderResult.cs index bb10caf48a..d6574fb85b 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/GroupToArgsBinderResult.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/GroupToArgsBinderResult.cs @@ -15,54 +15,33 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics internal sealed class GroupToArgsBinderResult { - public MethPropWithInst BestResult; - public MethPropWithInst GetBestResult() { return BestResult; } - public MethPropWithInst AmbiguousResult; - public MethPropWithInst GetAmbiguousResult() { return AmbiguousResult; } - private MethPropWithInst InaccessibleResult; - public MethPropWithInst GetInaccessibleResult() { return InaccessibleResult; } - private MethPropWithInst UninferableResult; - public MethPropWithInst GetUninferableResult() { return UninferableResult; } - private MethPropWithInst InconvertibleResult; + public MethPropWithInst BestResult { get; set; } + + public MethPropWithInst AmbiguousResult { get; set; } + + public MethPropWithInst InaccessibleResult { get; } + + public MethPropWithInst UninferableResult { get; } + public GroupToArgsBinderResult() { BestResult = new MethPropWithInst(); AmbiguousResult = new MethPropWithInst(); InaccessibleResult = new MethPropWithInst(); UninferableResult = new MethPropWithInst(); - InconvertibleResult = new MethPropWithInst(); - _inconvertibleResults = new List(); } - private readonly List _inconvertibleResults; - - ///////////////////////////////////////////////////////////////////////////////// - - public void AddInconvertibleResult( - MethodSymbol method, - AggregateType currentType, - TypeArray currentTypeArgs) - { - if (InconvertibleResult.Sym == null) - { - // This is the first one, so set it for error reporting usage. - InconvertibleResult.Set(method, currentType, currentTypeArgs); - } - _inconvertibleResults.Add(new MethPropWithInst(method, currentType, currentTypeArgs)); - } - - ///////////////////////////////////////////////////////////////////////////////// - private static int NumberOfErrorTypes(TypeArray pTypeArgs) { int nCount = 0; for (int i = 0; i < pTypeArgs.Count; i++) { - if (pTypeArgs[i] is ErrorType) + if (pTypeArgs[i] == null) { nCount++; } } + return nCount; } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ImplicitConversion.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ImplicitConversion.cs index a36dca6d7f..9d82a2f05f 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ImplicitConversion.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ImplicitConversion.cs @@ -15,13 +15,12 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics private sealed class ImplicitConversion { - public ImplicitConversion(ExpressionBinder binder, Expr exprSrc, CType typeSrc, ExprClass typeDest, bool needsExprDest, CONVERTTYPE flags) + public ImplicitConversion(ExpressionBinder binder, Expr exprSrc, CType typeSrc, CType typeDest, bool needsExprDest, CONVERTTYPE flags) { _binder = binder; _exprSrc = exprSrc; _typeSrc = typeSrc; - _typeDest = typeDest.Type; - _exprTypeDest = typeDest; + _typeDest = typeDest; _needsExprDest = needsExprDest; _flags = flags; _exprDest = null; @@ -32,7 +31,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics private readonly Expr _exprSrc; private readonly CType _typeSrc; private readonly CType _typeDest; - private readonly ExprClass _exprTypeDest; private readonly bool _needsExprDest; private CONVERTTYPE _flags; @@ -80,7 +78,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // (14.4.3), cast expressions (14.6.6), and assignments (14.14). // Can't convert to or from the error type. - if (_typeSrc == null || _typeDest == null || _typeDest.IsNeverSameType()) + if (_typeSrc == null || _typeDest == null || _typeDest is MethodGroupType) { return false; } @@ -91,17 +89,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics switch (_typeDest.GetTypeKind()) { - case TypeKind.TK_ErrorType: - Debug.Assert(((ErrorType)_typeDest).HasParent); - if (_typeSrc != _typeDest) - { - return false; - } - if (_needsExprDest) - { - _exprDest = _exprSrc; - } - return true; case TypeKind.TK_NullType: // Can only convert to the null type if src is null. if (!(_typeSrc is NullType)) @@ -113,9 +100,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics _exprDest = _exprSrc; } return true; - case TypeKind.TK_MethodGroupType: - Debug.Fail("Something is wrong with Type.IsNeverSameType()"); - return false; case TypeKind.TK_ArgumentListType: return _typeSrc == _typeDest; case TypeKind.TK_VoidType: @@ -124,12 +108,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics break; } - if (_typeSrc is ErrorType) - { - Debug.Assert(!(_typeDest is ErrorType)); - return false; - } - // 13.1.1 Identity conversion // // An identity conversion converts from any type to the same type. This conversion exists only @@ -170,7 +148,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics Debug.Fail($"Bad type symbol kind: {_typeSrc.GetTypeKind()}"); break; case TypeKind.TK_VoidType: - case TypeKind.TK_ErrorType: case TypeKind.TK_ParameterModifierType: case TypeKind.TK_ArgumentListType: return false; @@ -218,7 +195,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { if (_needsExprDest) { - _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest, _exprSrc.Flags & EXPRFLAG.EXF_CANTBENULL); + _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest, _exprSrc.Flags & EXPRFLAG.EXF_CANTBENULL); } return true; } @@ -307,7 +284,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics if (_needsExprDest) { - _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest, EXPRFLAG.EXF_UNBOX); + _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest, EXPRFLAG.EXF_UNBOX); } return true; } @@ -315,7 +292,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics bool dstWasNullable; bool srcWasNullable; CType typeDstBase = nubDst.StripNubs(out dstWasNullable); - ExprClass exprTypeDstBase = GetExprFactory().CreateClass(typeDstBase); CType typeSrcBase = _typeSrc.StripNubs(out srcWasNullable); ConversionFunc pfn = (_flags & CONVERTTYPE.ISEXPLICIT) != 0 ? (ConversionFunc)_binder.BindExplicitConversion : @@ -328,19 +304,14 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // The null type can be implicitly converted to T? as the default value. if (_typeSrc is NullType) { - // If we have the constant null, generate it as a default value of T?. If we have + // If we have the constant null, generate it as a default value of T?. If we have // some crazy expression which has been determined to be always null, like (null??null) // keep it in its expression form and transform it in the nullable rewrite pass. if (_needsExprDest) { - if (_exprSrc.isCONSTANT_OK()) - { - _exprDest = GetExprFactory().CreateZeroInit(nubDst); - } - else - { - _exprDest = GetExprFactory().CreateCast(_typeDest, _exprSrc); - } + _exprDest = _exprSrc is ExprConstant + ? GetExprFactory().CreateZeroInit(nubDst) + : GetExprFactory().CreateCast(_typeDest, _exprSrc); } return true; } @@ -348,7 +319,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics Expr exprTmp = _exprSrc; // If there is an implicit/explicit S => T then there is an implicit/explicit S => T? - if (_typeSrc == typeDstBase || pfn(_exprSrc, _typeSrc, exprTypeDstBase, nubDst, _needsExprDest, out exprTmp, _flags | CONVERTTYPE.NOUDC)) + if (_typeSrc == typeDstBase || pfn(_exprSrc, _typeSrc, typeDstBase, _needsExprDest, out exprTmp, _flags | CONVERTTYPE.NOUDC)) { if (_needsExprDest) { @@ -383,7 +354,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // Both are Nullable so there is only a conversion if there is a conversion between the base types. // That is, if there is an implicit/explicit S => T then there is an implicit/explicit S?+ => T?+. - if (typeSrcBase != typeDstBase && !pfn(null, typeSrcBase, exprTypeDstBase, nubDst, false, out _exprDest, _flags | CONVERTTYPE.NOUDC)) + if (typeSrcBase != typeDstBase && !pfn(null, typeSrcBase, typeDstBase, false, out _exprDest, _flags | CONVERTTYPE.NOUDC)) { // No builtin conversion. Maybe there is a user defined conversion.... return 0 == (_flags & CONVERTTYPE.NOUDC) && _binder.bindUserDefinedConversion(_exprSrc, _typeSrc, nubDst, _needsExprDest, out _exprDest, 0 == (_flags & CONVERTTYPE.ISEXPLICIT)); @@ -398,13 +369,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // Here we want to first check whether or not the conversions work on the base types. Expr arg1 = _binder.mustCast(_exprSrc, typeSrcBase); - ExprClass arg2 = GetExprFactory().CreateClass(typeDstBase); - bool convertible = (_flags & CONVERTTYPE.ISEXPLICIT) != 0 ? _binder.BindExplicitConversion( - arg1, arg1.Type, arg2, typeDstBase, out arg1, _flags | CONVERTTYPE.NOUDC) + arg1, arg1.Type, typeDstBase, out arg1, _flags | CONVERTTYPE.NOUDC) : _binder.BindImplicitConversion( - arg1, arg1.Type, arg2, typeDstBase, out arg1, _flags | CONVERTTYPE.NOUDC); + arg1, arg1.Type, typeDstBase, out arg1, _flags | CONVERTTYPE.NOUDC); if (!convertible) { @@ -435,17 +404,12 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } if (_needsExprDest) { - // If the conversion argument is a constant null then return a ZEROINIT. - // Otherwise, bind this as a cast to the destination type. In a later + // If the conversion argument is a constant null then return a ZEROINIT. + // Otherwise, bind this as a cast to the destination type. In a later // rewrite pass we will rewrite the cast as SEQ(side effects, ZEROINIT). - if (_exprSrc.isCONSTANT_OK()) - { - _exprDest = GetExprFactory().CreateZeroInit(_typeDest); - } - else - { - _exprDest = GetExprFactory().CreateCast(_typeDest, _exprSrc); - } + _exprDest = _exprSrc is ExprConstant + ? GetExprFactory().CreateZeroInit(_typeDest) + : GetExprFactory().CreateCast(_typeDest, _exprSrc); } return true; } @@ -480,13 +444,13 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { if (_needsExprDest) { - _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest, EXPRFLAG.EXF_BOX); + _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest, EXPRFLAG.EXF_BOX); if (!_typeDest.isPredefType(PredefinedType.PT_OBJECT)) { // The base type of a nullable is always a non-nullable value type, // therefore so is typeDest unless typeDest is PT_OBJECT. In this case the conversion // needs to be unboxed. We only need this if we actually will use the result. - _binder.bindSimpleCast(_exprDest, _exprTypeDest, out _exprDest, EXPRFLAG.EXF_FORCE_UNBOX); + _binder.bindSimpleCast(_exprDest, _typeDest, out _exprDest, EXPRFLAG.EXF_FORCE_UNBOX); } } return true; @@ -535,7 +499,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } if (_needsExprDest) { - _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest, grfex); + _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest, grfex); } return true; } @@ -552,7 +516,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics if (_typeDest is PointerType ptDest && ptDest.GetReferentType() == _binder.getVoidType()) { if (_needsExprDest) - _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest); + _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest); return true; } return false; @@ -617,7 +581,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics flags = _exprSrc.Flags & EXPRFLAG.EXF_CANTBENULL; } if (_needsExprDest) - _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest, flags); + _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest, flags); return true; } @@ -636,7 +600,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics if (_typeDest is AggregateType aggDest && GetSymbolLoader().HasBaseConversion(aggTypeSrc, aggDest)) { if (_needsExprDest) - _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest, EXPRFLAG.EXF_BOX | EXPRFLAG.EXF_CANTBENULL); + _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest, EXPRFLAG.EXF_BOX | EXPRFLAG.EXF_CANTBENULL); return true; } return false; @@ -697,7 +661,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics PredefinedType ptSrc = aggSrc.GetPredefType(); PredefinedType ptDest = _typeDest.getPredefType(); ConvKind convertKind; - bool fConstShrinkCast = false; Debug.Assert((int)ptSrc < NUM_SIMPLE_TYPES && (int)ptDest < NUM_SIMPLE_TYPES); @@ -710,14 +673,13 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // * A constant-expression of type long can be converted to type ulong, provided the value of // the constant-expression is not negative. // Note: Don't use GetConst here since the conversion only applies to bona-fide compile time constants. - if (_exprSrc is ExprConstant constant && _exprSrc.IsOK && + if (_exprSrc is ExprConstant constant && ((ptSrc == PredefinedType.PT_INT && ptDest != PredefinedType.PT_BOOL && ptDest != PredefinedType.PT_CHAR) || (ptSrc == PredefinedType.PT_LONG && ptDest == PredefinedType.PT_ULONG)) && isConstantInRange(constant, _typeDest)) { // Special case (CLR 6.1.6): if integral constant is in range, the conversion is a legal implicit conversion. convertKind = ConvKind.Implicit; - fConstShrinkCast = _needsExprDest && (GetConvKind(ptSrc, ptDest) != ConvKind.Implicit); } else if (ptSrc == ptDest) { @@ -742,7 +704,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics if (_exprSrc.GetConst() != null) { // Fold the constant cast if possible. - ConstCastResult result = _binder.bindConstantCast(_exprSrc, _exprTypeDest, _needsExprDest, out _exprDest, false); + ConstCastResult result = _binder.bindConstantCast(_exprSrc, _typeDest, _needsExprDest, out _exprDest, false); if (result == ConstCastResult.Success) { return true; // else, don't fold and use a regular cast, below. @@ -761,7 +723,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return _binder.bindUserDefinedConversion(_exprSrc, aggTypeSrc, _typeDest, _needsExprDest, out _exprDest, true); } if (_needsExprDest) - _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest); + _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest); return true; } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MemberLookup.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MemberLookup.cs index ce889a0964..748580e702 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MemberLookup.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MemberLookup.cs @@ -47,7 +47,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics private Name _name; private int _arity; private MemLookFlags _flags; - private CMemberLookupResults _results; // For maintaining the type array. We throw the first 8 or so here. private readonly List _rgtypeStart; @@ -64,25 +63,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics private readonly SymWithType _swtBad; // If we're looking for a constructor or indexer, this matched on name, but isn't the right thing. private readonly SymWithType _swtBogus; // A bogus member - such as an indexed property. private readonly SymWithType _swtBadArity; // An symbol with the wrong arity. - private SymWithType _swtAmbigWarn; // An ambiguous symbol, but only warn. - - // We have an override symbol, which we've errored on in SymbolPrepare. If we have nothing better, use this. - // This is because if we have: - // - // class C : D - // { - // public override int M() { } - // static void Main() - // { - // C c = new C(); - // c.M(); <-- - // - // We try to look up M, and find the M on C, but throw it out since its an override, and - // we want the virtual that it overrides. However, in this case, we'll never find that - // virtual, since it doesn't exist. We therefore want to use the override anyway, and - // continue on to give results with that. - - private readonly SymWithType _swtOverride; private bool _fMulti; // Whether symFirst is of a kind for which we collect multiples (methods and indexers). /*************************************************************************************************** @@ -132,10 +112,10 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return false; // Loop through symbols. - Symbol symCur = null; + Symbol symCur; for (symCur = GetSymbolLoader().LookupAggMember(_name, typeCur.getAggregate(), symbmask_t.MASK_ALL); symCur != null; - symCur = GetSymbolLoader().LookupNextSym(symCur, typeCur.getAggregate(), symbmask_t.MASK_ALL)) + symCur = SymbolLoader.LookupNextSym(symCur, typeCur.getAggregate(), symbmask_t.MASK_ALL)) { // Check for arity. switch (symCur.getKind()) @@ -176,10 +156,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // Check for user callability. if (symCur.IsOverride() && !symCur.IsHideByName()) { - if (!_swtOverride) - { - _swtOverride.Set(symCur, typeCur); - } continue; } @@ -313,7 +289,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // Give method groups priority. if (!(symCur is MethodSymbol)) goto LAmbig; - _swtAmbigWarn = _swtFirst; // Erase previous results so we'll record this method as the first. _prgtype = new List(); _csym = 0; @@ -327,8 +302,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // Give method groups priority. if (!(_swtFirst.Sym is MethodSymbol)) goto LAmbig; - if (!_swtAmbigWarn) - _swtAmbigWarn.Set(symCur, typeCur); } // This one is hidden by another. This one also hides any more in base types. pfHideByName = true; @@ -415,9 +388,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { Debug.Assert(!typeCur.isInterfaceType()); - bool fHideByName = false; - - SearchSingleType(typeCur, out fHideByName); + SearchSingleType(typeCur, out bool fHideByName); if (_swtFirst && !_fMulti) { @@ -485,9 +456,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { Debug.Assert(typeCur != null && typeCur.isInterfaceType()); - bool fHideByName = false; - - if (!typeCur.fAllHidden && SearchSingleType(typeCur, out fHideByName)) + if (!typeCur.fAllHidden && SearchSingleType(typeCur, out bool fHideByName)) { fHideByName |= !_fMulti; @@ -553,8 +522,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics _swtBad = new SymWithType(); _swtBogus = new SymWithType(); _swtBadArity = new SymWithType(); - _swtAmbigWarn = new SymWithType(); - _swtOverride = new SymWithType(); } /*************************************************************************************************** @@ -635,17 +602,9 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } } - // if we are requested with extension methods - _results = new CMemberLookupResults(GetAllTypes(), _name); - return !FError(); } - public CMemberLookupResults GetResults() - { - return _results; - } - // Whether there were errors. private bool FError() { diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MemberLookupResults.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MemberLookupResults.cs index 37f2649643..045a6cd9e8 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MemberLookupResults.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MemberLookupResults.cs @@ -22,19 +22,17 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics TypeArray containingTypes, Name name) { + Debug.Assert(containingTypes != null); + Debug.Assert(containingTypes.Count != 0); _pName = name; ContainingTypes = containingTypes; - if (ContainingTypes == null) - { - ContainingTypes = BSYMMGR.EmptyTypeArray(); - } } public CMethodIterator GetMethodIterator( - CSemanticChecker pChecker, SymbolLoader pSymLoader, CType pObject, CType pQualifyingType, AggregateDeclaration pContext, bool allowBogusAndInaccessible, bool allowExtensionMethods, int arity, EXPRFLAG flags, symbmask_t mask) + CSemanticChecker pChecker, SymbolLoader pSymLoader, CType pQualifyingType, AggregateDeclaration pContext, int arity, EXPRFLAG flags, symbmask_t mask, ArgInfos nonTrailingNamedArguments) { Debug.Assert(pSymLoader != null); - CMethodIterator iterator = new CMethodIterator(pChecker, pSymLoader, _pName, ContainingTypes, pObject, pQualifyingType, pContext, allowBogusAndInaccessible, allowExtensionMethods, arity, flags, mask); + CMethodIterator iterator = new CMethodIterator(pChecker, pSymLoader, _pName, ContainingTypes, pQualifyingType, pContext, arity, flags, mask, nonTrailingNamedArguments); return iterator; } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodIterator.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodIterator.cs index 3c49ad4573..4e86ea7448 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodIterator.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodIterator.cs @@ -2,6 +2,7 @@ // 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.Collections.Generic; using System.Diagnostics; using Microsoft.CSharp.RuntimeBinder.Syntax; @@ -11,256 +12,158 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { public partial class CMethodIterator { - private SymbolLoader _pSymbolLoader; - private CSemanticChecker _pSemanticChecker; + private readonly SymbolLoader _symbolLoader; + private readonly CSemanticChecker _semanticChecker; // Inputs. - private AggregateType _pCurrentType; - private MethodOrPropertySymbol _pCurrentSym; - private AggregateDeclaration _pContext; - private TypeArray _pContainingTypes; - private CType _pQualifyingType; - private Name _pName; - private int _nArity; - private symbmask_t _mask; - private EXPRFLAG _flags; + private readonly AggregateDeclaration _context; + private readonly TypeArray _containingTypes; + private readonly CType _qualifyingType; + private readonly Name _name; + private readonly int _arity; + private readonly symbmask_t _mask; + private readonly EXPRFLAG _flags; + private readonly ArgInfos _nonTrailingNamedArguments; // Internal state. - private int _nCurrentTypeCount; - private bool _bIsCheckingInstanceMethods; - private bool _bAtEnd; - private bool _bAllowBogusAndInaccessible; - // Flags for the current sym. - private bool _bCurrentSymIsBogus; - private bool _bCurrentSymIsInaccessible; - // if Extension can be part of the results that are returned by the iterator - // this may be false if an applicable instance method was found by bindgrptoArgs - private bool _bcanIncludeExtensionsInResults; + private int _currentTypeIndex; - public CMethodIterator(CSemanticChecker checker, SymbolLoader symLoader, Name name, TypeArray containingTypes, CType @object, CType qualifyingType, AggregateDeclaration context, bool allowBogusAndInaccessible, bool allowExtensionMethods, int arity, EXPRFLAG flags, symbmask_t mask) + public CMethodIterator(CSemanticChecker checker, SymbolLoader symLoader, Name name, TypeArray containingTypes, CType qualifyingType, AggregateDeclaration context, int arity, EXPRFLAG flags, symbmask_t mask, ArgInfos nonTrailingNamedArguments) { Debug.Assert(name != null); Debug.Assert(symLoader != null); Debug.Assert(checker != null); Debug.Assert(containingTypes != null); - _pSemanticChecker = checker; - _pSymbolLoader = symLoader; - _pCurrentType = null; - _pCurrentSym = null; - _pName = name; - _pContainingTypes = containingTypes; - _pQualifyingType = qualifyingType; - _pContext = context; - _bAllowBogusAndInaccessible = allowBogusAndInaccessible; - _nArity = arity; + Debug.Assert(containingTypes.Count != 0); + _semanticChecker = checker; + _symbolLoader = symLoader; + _name = name; + _containingTypes = containingTypes; + _qualifyingType = qualifyingType; + _context = context; + _arity = arity; _flags = flags; _mask = mask; - _nCurrentTypeCount = 0; - _bIsCheckingInstanceMethods = true; - _bAtEnd = false; - _bCurrentSymIsBogus = false; - _bCurrentSymIsInaccessible = false; - _bcanIncludeExtensionsInResults = allowExtensionMethods; + _nonTrailingNamedArguments = nonTrailingNamedArguments; } - public MethodOrPropertySymbol GetCurrentSymbol() - { - return _pCurrentSym; - } - public AggregateType GetCurrentType() - { - return _pCurrentType; - } - public bool IsCurrentSymbolInaccessible() - { - return _bCurrentSymIsInaccessible; - } - public bool IsCurrentSymbolBogus() - { - return _bCurrentSymIsBogus; - } - public bool MoveNext(bool canIncludeExtensionsInResults) - { - if (_bcanIncludeExtensionsInResults) - { - _bcanIncludeExtensionsInResults = canIncludeExtensionsInResults; - } - if (_bAtEnd) - { - return false; - } + public MethodOrPropertySymbol CurrentSymbol { get; private set; } - if (_pCurrentType == null) // First guy. + public AggregateType CurrentType { get; private set; } + + public bool IsCurrentSymbolInaccessible { get; private set; } + + public bool IsCurrentSymbolBogus { get; private set; } + + public bool IsCurrentSymbolMisnamed { get; private set; } + + public bool MoveNext() => (CurrentType != null || FindNextTypeForInstanceMethods()) && FindNextMethod(); + + public bool AtEnd => CurrentSymbol == null; + + public bool CanUseCurrentSymbol + { + get { - if (_pContainingTypes.Count == 0) + // Make sure that whether we're seeing a ctor is consistent with the flag. + // The only properties we handle are indexers. + if (_mask == symbmask_t.MASK_MethodSymbol && ( + 0 == (_flags & EXPRFLAG.EXF_CTOR) != !((MethodSymbol)CurrentSymbol).IsConstructor() || + 0 == (_flags & EXPRFLAG.EXF_OPERATOR) != !((MethodSymbol)CurrentSymbol).isOperator) || + _mask == symbmask_t.MASK_PropertySymbol && !(CurrentSymbol is IndexerSymbol)) { - // No instance methods, only extensions. - _bIsCheckingInstanceMethods = false; - _bAtEnd = true; + // Get the next symbol. return false; } - else - { - if (!FindNextTypeForInstanceMethods()) - { - // No instance or extensions. - _bAtEnd = true; - return false; + // If our arity is non-0, we must match arity with this symbol. + if (_arity > 0 & _mask == symbmask_t.MASK_MethodSymbol && ((MethodSymbol)CurrentSymbol).typeVars.Count != _arity) + { + return false; + } + + // If this guy's not callable, no good. + if (!ExpressionBinder.IsMethPropCallable(CurrentSymbol, (_flags & EXPRFLAG.EXF_USERCALLABLE) != 0)) + { + return false; + } + + // Check access. If Sym is not accessible, then let it through and mark it. + IsCurrentSymbolInaccessible = !_semanticChecker.CheckAccess(CurrentSymbol, CurrentType, _context, _qualifyingType); + + // Check bogus. If Sym is bogus, then let it through and mark it. + IsCurrentSymbolBogus = CSemanticChecker.CheckBogus(CurrentSymbol); + + IsCurrentSymbolMisnamed = CheckArgumentNames(); + + return true; + } + } + + private bool CheckArgumentNames() + { + ArgInfos args = _nonTrailingNamedArguments; + if (args != null) + { + List paramNames = ExpressionBinder.GroupToArgsBinder + .FindMostDerivedMethod(_symbolLoader, CurrentSymbol, _qualifyingType) + .ParameterNames; + + List argExpressions = args.prgexpr; + for (int i = 0; i < args.carg; i++) + { + if (argExpressions[i] is ExprNamedArgumentSpecification named) + { + // Either wrong name, or correct name but we have more params arguments to follow. + if (paramNames[i] != named.Name || i == paramNames.Count - 1 && i != args.carg - 1) + { + return true; + } } } } - if (!FindNextMethod()) - { - _bAtEnd = true; - return false; - } - return true; - } - public bool AtEnd() - { - return _pCurrentSym == null; - } - private CSemanticChecker GetSemanticChecker() - { - return _pSemanticChecker; - } - private SymbolLoader GetSymbolLoader() - { - return _pSymbolLoader; - } - public bool CanUseCurrentSymbol() - { - _bCurrentSymIsInaccessible = false; - _bCurrentSymIsBogus = false; - // Make sure that whether we're seeing a ctor is consistent with the flag. - // The only properties we handle are indexers. - if (_mask == symbmask_t.MASK_MethodSymbol && ( - 0 == (_flags & EXPRFLAG.EXF_CTOR) != !((MethodSymbol)_pCurrentSym).IsConstructor() || - 0 == (_flags & EXPRFLAG.EXF_OPERATOR) != !((MethodSymbol)_pCurrentSym).isOperator) || - _mask == symbmask_t.MASK_PropertySymbol && !(_pCurrentSym is IndexerSymbol)) - { - // Get the next symbol. - return false; - } - - // If our arity is non-0, we must match arity with this symbol. - if (_nArity > 0) - { - if (_mask == symbmask_t.MASK_MethodSymbol && ((MethodSymbol)_pCurrentSym).typeVars.Count != _nArity) - { - return false; - } - } - - // If this guy's not callable, no good. - if (!ExpressionBinder.IsMethPropCallable(_pCurrentSym, (_flags & EXPRFLAG.EXF_USERCALLABLE) != 0)) - { - return false; - } - - // Check access. - if (!GetSemanticChecker().CheckAccess(_pCurrentSym, _pCurrentType, _pContext, _pQualifyingType)) - { - // Sym is not accessible. However, if we're allowing inaccessible, then let it through and mark it. - if (_bAllowBogusAndInaccessible) - { - _bCurrentSymIsInaccessible = true; - } - else - { - return false; - } - } - - // Check bogus. - if (CSemanticChecker.CheckBogus(_pCurrentSym)) - { - // Sym is bogus, but if we're allow it, then let it through and mark it. - if (_bAllowBogusAndInaccessible) - { - _bCurrentSymIsBogus = true; - } - else - { - return false; - } - } - - return _bIsCheckingInstanceMethods; + return false; } private bool FindNextMethod() { - while (true) + for (;;) { - if (_pCurrentSym == null) + CurrentSymbol = (CurrentSymbol == null + ? _symbolLoader.LookupAggMember(_name, CurrentType.getAggregate(), _mask) + : SymbolLoader.LookupNextSym(CurrentSymbol, CurrentType.getAggregate(), _mask)) as MethodOrPropertySymbol; + + // If we couldn't find a sym, we look up the type chain and get the next type. + if (CurrentSymbol == null) { - _pCurrentSym = GetSymbolLoader().LookupAggMember( - _pName, _pCurrentType.getAggregate(), _mask) as MethodOrPropertySymbol; + if (!FindNextTypeForInstanceMethods()) + { + return false; + } } else { - _pCurrentSym = GetSymbolLoader().LookupNextSym( - _pCurrentSym, _pCurrentType.getAggregate(), _mask) as MethodOrPropertySymbol; + // Note that we do not filter the current symbol for the user. They must do that themselves. + // This is because for instance, BindGrpToArgs wants to filter on arguments before filtering + // on bogosity. + + // If we're here, we're good to go. + + return true; } - - // If we couldn't find a sym, we look up the type chain and get the next type. - if (_pCurrentSym == null) - { - if (_bIsCheckingInstanceMethods) - { - if (!FindNextTypeForInstanceMethods() && _bcanIncludeExtensionsInResults) - { - // We didn't find any more instance methods, set us into extension mode. - - _bIsCheckingInstanceMethods = false; - } - else if (_pCurrentType == null && !_bcanIncludeExtensionsInResults) - { - return false; - } - else - { - // Found an instance method. - continue; - } - } - continue; - } - - // Note that we do not filter the current symbol for the user. They must do that themselves. - // This is because for instance, BindGrpToArgs wants to filter on arguments before filtering - // on bogosity. - - // If we're here, we're good to go. - - break; } - return true; } private bool FindNextTypeForInstanceMethods() { - // Otherwise, search through other types listed as well as our base class. - if (_pContainingTypes.Count > 0) + if (_currentTypeIndex >= _containingTypes.Count) { - if (_nCurrentTypeCount >= _pContainingTypes.Count) - { - // No more types to check. - _pCurrentType = null; - } - else - { - _pCurrentType = _pContainingTypes[_nCurrentTypeCount++] as AggregateType; - } + // No more types to check. + CurrentType = null; + return false; } - else - { - // We have no more types to consider, so check out the base class. - _pCurrentType = _pCurrentType.GetBaseClass(); - } - return _pCurrentType != null; + CurrentType = _containingTypes[_currentTypeIndex++] as AggregateType; + return true; } } } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodTypeInferrer.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodTypeInferrer.cs index 011d482224..5e70a5c7f6 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodTypeInferrer.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodTypeInferrer.cs @@ -25,13 +25,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics Unknown = 0x00, NotDependent = 0x01, DependsMask = 0x10, - Direct = 0x11, Indirect = 0x12 } private readonly SymbolLoader _symbolLoader; private readonly ExpressionBinder _binder; private readonly TypeArray _pMethodTypeParameters; - private readonly TypeArray _pClassTypeArguments; private readonly TypeArray _pMethodFormalParameterTypes; private readonly ArgInfos _pMethodArguments; private readonly List[] _pExactBounds; @@ -85,7 +83,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics ExpressionBinder binder, SymbolLoader symbolLoader, MethodSymbol pMethod, - TypeArray pClassTypeArguments, TypeArray pMethodFormalParameterTypes, ArgInfos pMethodArguments, out TypeArray ppInferredTypeArguments) @@ -104,16 +101,8 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics var inferrer = new MethodTypeInferrer(binder, symbolLoader, pMethodFormalParameterTypes, pMethodArguments, - pMethod.typeVars, pClassTypeArguments); - bool success; - if (pMethodArguments.fHasExprs) - { - success = inferrer.InferTypeArgs(); - } - else - { - success = inferrer.InferForMethodGroupConversion(); - } + pMethod.typeVars); + bool success = inferrer.InferTypeArgs(); ppInferredTypeArguments = inferrer.GetResults(); return success; @@ -131,14 +120,13 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics private MethodTypeInferrer( ExpressionBinder exprBinder, SymbolLoader symLoader, TypeArray pMethodFormalParameterTypes, ArgInfos pMethodArguments, - TypeArray pMethodTypeParameters, TypeArray pClassTypeArguments) + TypeArray pMethodTypeParameters) { _binder = exprBinder; _symbolLoader = symLoader; _pMethodFormalParameterTypes = pMethodFormalParameterTypes; _pMethodArguments = pMethodArguments; _pMethodTypeParameters = pMethodTypeParameters; - _pClassTypeArguments = pClassTypeArguments; _pFixedResults = new CType[pMethodTypeParameters.Count]; _pLowerBounds = new List[pMethodTypeParameters.Count]; _pUpperBounds = new List[pMethodTypeParameters.Count]; @@ -154,52 +142,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics //////////////////////////////////////////////////////////////////////////////// - private TypeArray GetResults() - { - // Anything we didn't infer a CType for, give the error CType. - // Note: the error CType will have the same name as the name - // of the CType parameter we were trying to infer. This will give a - // nice user experience where by we will show something like - // the following: - // - // user types: customers.Select( - // we show : IE IE.Select(Func selector) - // - // Initially we thought we'd just show ?. i.e.: - // - // IE IE.Select(Func selector) - // - // This is nice and concise. However, it falls down if there are multiple - // CType params that we have left. - - for (int iParam = 0; iParam < _pMethodTypeParameters.Count; iParam++) - { - // We iterate through the resultant types and replace any that are - // null, or an error CType that has less information (e.g null name or - // PredefinedName.PN_MISSING name). - - // We get an ErrorType with a null nameText - // for a CType variable that we couldn't infer. - if (_pFixedResults[iParam] != null) - { - if (!(_pFixedResults[iParam] is ErrorType err)) - { - continue; - } - - Name pErrorTypeName = err.nameText; - if (pErrorTypeName != null) - { - continue; - } - } - - _pFixedResults[iParam] = GetTypeManager().GetErrorType( - ((TypeParameterType)_pMethodTypeParameters[iParam]).GetName(), - BSYMMGR.EmptyTypeArray()); - } - return GetGlobalSymbols().AllocParams(_pMethodTypeParameters.Count, _pFixedResults); - } + private TypeArray GetResults() => GetGlobalSymbols().AllocParams(_pFixedResults); //////////////////////////////////////////////////////////////////////////////// @@ -282,33 +225,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics !_pUpperBounds[iParam].IsEmpty(); } - //////////////////////////////////////////////////////////////////////////////// - - private TypeArray GetFixedDelegateParameters(AggregateType pDelegateType) - { - Debug.Assert(pDelegateType.isDelegateType()); - - // We have a delegate where the input types use no unfixed parameters. Create - // a substitution context; we can substitute unfixed parameters for themselves - // since they don't actually occur in the inputs. (They may occur in the outputs, - // or there may be input parameters fixed to _unfixed_ method CType variables. - // Both of those scenarios are legal.) - - CType[] ppMethodParameters = new CType[_pMethodTypeParameters.Count]; - for (int iParam = 0; iParam < _pMethodTypeParameters.Count; iParam++) - { - TypeParameterType pParam = (TypeParameterType)_pMethodTypeParameters[iParam]; - ppMethodParameters[iParam] = IsUnfixed(iParam) ? pParam : _pFixedResults[iParam]; - } - SubstContext subsctx = new SubstContext(_pClassTypeArguments.Items, _pClassTypeArguments.Count, - ppMethodParameters, _pMethodTypeParameters.Count); - AggregateType pFixedDelegateType = - GetTypeManager().SubstType(pDelegateType, subsctx) as AggregateType; - TypeArray pFixedDelegateParams = - pFixedDelegateType.GetDelegateParameters(GetSymbolLoader()); - return pFixedDelegateParams; - } - //////////////////////////////////////////////////////////////////////////////// // // Phases @@ -509,7 +425,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // SPEC: CType parameter Xj, // SPEC: then an output CType inference is made from all such Ei to Ti. - MakeOutputTypeInferences(); + // Irrelevant to dynamic binding. // SPEC: Whether or not the previous step actually made an inference, we // SPEC: must now fix at least one CType parameter, as follows: @@ -543,35 +459,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics //////////////////////////////////////////////////////////////////////////////// - private void MakeOutputTypeInferences() - { - // SPEC: Otherwise, for all arguments Ei with corresponding parameter CType Ti - // SPEC: where the output types contain unfixed CType parameters but the input - // SPEC: types do not, an output CType inference is made from Ei to Ti. - - for (int iArg = 0; iArg < _pMethodArguments.carg; iArg++) - { - CType pDest = _pMethodFormalParameterTypes[iArg]; - if (pDest is ParameterModifierType modDest) - { - pDest = modDest.GetParameterType(); - } - Expr pExpr = _pMethodArguments.prgexpr[iArg]; - if (HasUnfixedParamInOutputType(pExpr, pDest) && - !HasUnfixedParamInInputType(pExpr, pDest)) - { - CType pSource = _pMethodArguments.types[iArg]; - if (pSource is ParameterModifierType modSource) - { - pSource = modSource.GetParameterType(); - } - OutputTypeInference(pExpr, pSource, pDest); - } - } - } - - //////////////////////////////////////////////////////////////////////////////// - private NewInferenceResult FixNondependentParameters() { // SPEC: Otherwise, if there exists one or more CType parameters Xi such that @@ -648,149 +535,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return res; } - //////////////////////////////////////////////////////////////////////////////// - // - // Input types - // - private bool DoesInputTypeContain(Expr pSource, CType pDest, - TypeParameterType pParam) - { - // SPEC: If E is a method group or an anonymous function and T is a delegate - // SPEC: CType or expression tree CType then all the parameter types of T are - // SPEC: input types of E with CType T. - - pDest = pDest.GetDelegateTypeOfPossibleExpression(); - if (pDest.isDelegateType()) - { - switch (pSource.Kind) - { - case ExpressionKind.MemberGroup: - case ExpressionKind.BoundLambda: - TypeArray pDelegateParameters = (pDest as AggregateType).GetDelegateParameters(GetSymbolLoader()); - if (pDelegateParameters != null) - { - return TypeManager.ParametersContainTyVar(pDelegateParameters, pParam); - } - - break; - } - } - - return false; - } - - //////////////////////////////////////////////////////////////////////////////// - - private bool HasUnfixedParamInInputType(Expr pSource, CType pDest) - { - for (int iParam = 0; iParam < _pMethodTypeParameters.Count; iParam++) - { - if (IsUnfixed(iParam)) - { - if (DoesInputTypeContain(pSource, pDest, - _pMethodTypeParameters[iParam] as TypeParameterType)) - { - return true; - } - } - } - return false; - } - - //////////////////////////////////////////////////////////////////////////////// - // - // Output types - // - private bool DoesOutputTypeContain(Expr pSource, CType pDest, TypeParameterType pParam) - { - // SPEC: If E is a method group or an anonymous function and T is a delegate - // SPEC: CType or expression tree CType then the return CType of T is an output CType - // SPEC: of E with CType T. - - pDest = pDest.GetDelegateTypeOfPossibleExpression(); - if (pDest.isDelegateType()) - { - switch (pSource.Kind) - { - case ExpressionKind.MemberGroup: - case ExpressionKind.BoundLambda: - CType pDelegateReturn = ((AggregateType)pDest).GetDelegateReturnType(GetSymbolLoader()); - if (pDelegateReturn != null) - { - return TypeManager.TypeContainsType(pDelegateReturn, pParam); - } - - break; - } - } - - return false; - } - - //////////////////////////////////////////////////////////////////////////////// - - private bool HasUnfixedParamInOutputType(Expr pSource, CType pDest) - { - for (int iParam = 0; iParam < _pMethodTypeParameters.Count; iParam++) - { - if (IsUnfixed(iParam)) - { - if (DoesOutputTypeContain(pSource, pDest, - _pMethodTypeParameters[iParam] as TypeParameterType)) - { - return true; - } - } - } - return false; - } - - //////////////////////////////////////////////////////////////////////////////// - // - // Dependence - // - - private bool DependsDirectlyOn(int iParam, int jParam) - { - Debug.Assert(0 <= iParam && iParam < _pMethodTypeParameters.Count); - Debug.Assert(0 <= jParam && jParam < _pMethodTypeParameters.Count); - - // SPEC: An unfixed CType parameter Xi depends directly on an unfixed CType - // SPEC: parameter Xj if for some argument Ek with CType Tk, Xj occurs - // SPEC: in an input CType of Ek and Xi occurs in an output CType of Ek - // SPEC: with CType Tk. - - // We compute and record the Depends Directly On relationship once, in - // InitializeDependencies, below. - - // At this point, everything should be unfixed. - - Debug.Assert(IsUnfixed(iParam)); - Debug.Assert(IsUnfixed(jParam)); - - for (int iArg = 0; iArg < _pMethodArguments.carg; iArg++) - { - CType pDest = _pMethodFormalParameterTypes[iArg]; - if (pDest is ParameterModifierType modDest) - { - pDest = modDest.GetParameterType(); - } - - Expr pExpr = _pMethodArguments.prgexpr[iArg]; - - if (DoesInputTypeContain(pExpr, pDest, - _pMethodTypeParameters[jParam] as TypeParameterType) && - DoesOutputTypeContain(pExpr, pDest, - _pMethodTypeParameters[iParam] as TypeParameterType)) - { - return true; - } - } - return false; - } - - //////////////////////////////////////////////////////////////////////////////// - private void InitializeDependencies() { // We track dependencies by a two-d square array that gives the known @@ -833,13 +577,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics for (int iParam = 0; iParam < _pMethodTypeParameters.Count; ++iParam) { _ppDependencies[iParam] = new Dependency[_pMethodTypeParameters.Count]; - for (int jParam = 0; jParam < _pMethodTypeParameters.Count; ++jParam) - { - if (DependsDirectlyOn(iParam, jParam)) - { - _ppDependencies[iParam][jParam] = Dependency.Direct; - } - } } DeduceAllDependencies(); @@ -1010,102 +747,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return false; } - //////////////////////////////////////////////////////////////////////////////// - // - // Output CType inferences - // - - - - //////////////////////////////////////////////////////////////////////////////// - - private void OutputTypeInference(Expr pExpr, CType pSource, CType pDest) - { - // SPEC: An output CType inference is made from an expression E to a CType T - // SPEC: in the following way: - - // SPEC: If E is an anonymous function with inferred return CType U and - // SPEC: T is a delegate CType or expression tree with return CType Tb - // SPEC: then a lower bound inference is made from U to Tb. - - // SPEC: Otherwise, if E is a method group and T is a delegate CType or - // SPEC: expression tree CType with parameter types T1...Tk and return - // SPEC: CType Tb and overload resolution of E with the types T1...Tk - // SPEC: yields a single method with return CType U then a lower-bound - // SPEC: inference is made from U to Tb. - if (MethodGroupReturnTypeInference(pExpr, pDest)) - { - return; - } - // SPEC: Otherwise, if E is an expression with CType U then a lower-bound - // SPEC: inference is made from U to T. - if (IsReallyAType(pSource)) - { - LowerBoundInference(pSource, pDest); - } - // SPEC: Otherwise, no inferences are made. - } - - //////////////////////////////////////////////////////////////////////////////// - - private bool MethodGroupReturnTypeInference(Expr pSource, CType pType) - { - // SPEC: Otherwise, if E is a method group and T is a delegate CType or - // SPEC: expression tree CType with parameter types T1...Tk and return - // SPEC: CType Tb and overload resolution of E with the types T1...Tk - // SPEC: yields a single method with return CType U then a lower-bound - // SPEC: inference is made from U to Tb. - - if (!(pSource is ExprMemberGroup memGrp)) - { - return false; - } - pType = pType.GetDelegateTypeOfPossibleExpression(); - if (!pType.isDelegateType()) - { - return false; - } - AggregateType pDelegateType = pType as AggregateType; - CType pDelegateReturnType = pDelegateType.GetDelegateReturnType(GetSymbolLoader()); - if (pDelegateReturnType == null) - { - return false; - } - if (pDelegateReturnType is VoidType) - { - return false; - } - - // At this point we are in the second phase; we know that all the input types are fixed. - - TypeArray pDelegateParameters = GetFixedDelegateParameters(pDelegateType); - if (pDelegateParameters == null) - { - return false; - } - - ArgInfos argInfo = new ArgInfos() { carg = pDelegateParameters.Count, types = pDelegateParameters, fHasExprs = false, prgexpr = null }; - - var argsBinder = new ExpressionBinder.GroupToArgsBinder(_binder, 0/* flags */, memGrp, argInfo, null, false, pDelegateType); - - bool success = argsBinder.Bind(false); - if (!success) - { - return false; - } - - MethPropWithInst mwi = argsBinder.GetResultsOfBind().GetBestResult(); - CType pMethodReturnType = GetTypeManager().SubstType(mwi.Meth().RetType, - mwi.GetType(), mwi.TypeArgs); - if (pMethodReturnType is VoidType) - { - return false; - } - - LowerBoundInference(pMethodReturnType, pDelegateReturnType); - return true; - } - //////////////////////////////////////////////////////////////////////////////// // // Exact inferences @@ -2076,64 +1717,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return true; } - //////////////////////////////////////////////////////////////////////////////// - // - // CType inference for conversion of method groups - // - private bool InferForMethodGroupConversion() - { - // SPEC: Similar to calls of generic methods, CType inference must - // SPEC: also be applied when a method group M containing a generic - // SPEC: method is converted to a given delegate CType D. Given a method - // SPEC: Tr M(T1 x1, ... Tm xm) and the method group M being - // SPEC: assigned to the delegate CType D the task of CType inference is - // SPEC: to find CType arguments S1...Sn so that the expression M - // SPEC: becomes compatible with D. - // SPEC: Unlike the CType inference algorithm for generic method calls, in - // SPEC: this case there are only argument types, no argument expressions. - // SPEC: In particular, there are no anonymous functions and hence no need - // SPEC: for multiple phases of inference. - // SPEC: Instead, all Xi are considered unfixed and a lower-bound inference - // SPEC: is made from each argument CType Uj of D to the corresponding parameter - // SPEC: CType Tj of M. If for any of the Xi no bounds were found, CType - // SPEC: inference fails. Otherwise, all Xi are fixed to corresponding Si, - // SPEC: which are the result of CType inference. - - Debug.Assert(_pMethodFormalParameterTypes != null); - Debug.Assert(_pMethodArguments != null); - Debug.Assert(_pMethodArguments.carg <= _pMethodFormalParameterTypes.Count); - - for (int iArg = 0; iArg < _pMethodArguments.carg; iArg++) - { - CType pDest = _pMethodFormalParameterTypes[iArg]; - CType pSource = _pMethodArguments.types[iArg]; - if (pDest is ParameterModifierType modDest) - { - pDest = modDest.GetParameterType(); - } - if (pSource is ParameterModifierType modSource) - { - pSource = modSource.GetParameterType(); - } - - LowerBoundInference(pSource, pDest); - } - - bool success = true; - - // In the event of failure we still want to fix as much as we can, so - // that intellisense gives the best possible result. - - for (int iParam = 0; iParam < _pMethodTypeParameters.Count; iParam++) - { - if (!HasBound(iParam) || !Fix(iParam)) - { - success = false; - } - } - return success; - } - //////////////////////////////////////////////////////////////////////////////// // // Helper methods diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Nullable.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Nullable.cs index ce13b77d4b..4c514fbe59 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Nullable.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Nullable.cs @@ -25,18 +25,16 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { return _pErrorContext; } + private static bool IsNullableConstructor(Expr expr, out ExprCall call) { Debug.Assert(expr != null); - if (expr is ExprCall pCall && pCall.MemberGroup.OptionalObject == null) + if (expr is ExprCall pCall && pCall.MemberGroup.OptionalObject == null + && (pCall.MethWithInst?.Meth().IsNullableConstructor() ?? false)) { - MethodSymbol meth = pCall.MethWithInst.Meth(); - if (meth != null && meth.IsNullableConstructor()) - { - call = pCall; - return true; - } + call = pCall; + return true; } call = null; diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Operators.cs.REMOVED.git-id b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Operators.cs.REMOVED.git-id index 0924504142..2607dd185d 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Operators.cs.REMOVED.git-id +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Operators.cs.REMOVED.git-id @@ -1 +1 @@ -22e7de10f76f8d31b06b08fd259925a68fd6e9b4 \ No newline at end of file +5c0f280419f49460418080df8b75fd64f73476b1 \ No newline at end of file diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/PredefinedMembers.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/PredefinedMembers.cs index 510b88a34f..5c0d7b43cb 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/PredefinedMembers.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/PredefinedMembers.cs @@ -150,9 +150,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // start next value at PredefinedType.PT_VOID + 1, SIG_CLASS_TYVAR = (int)PredefinedType.PT_VOID + 1, // next element in signature is index of class tyvar SIG_METH_TYVAR, // next element in signature is index of method tyvar - SIG_SZ_ARRAY, // must be followed by signature type of array elements - SIG_REF, // must be followed by signature of ref type - SIG_OUT, // must be followed by signature of out type + SIG_SZ_ARRAY // must be followed by signature type of array elements } // A description of a method the compiler uses while compiling. @@ -210,7 +208,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics private AggregateSymbol GetMethParent(PREDEFMETH method) { - return GetOptPredefAgg(GetMethPredefType(method)); + return GetPredefAgg(GetMethPredefType(method)); } private PropertySymbol LoadProperty(PREDEFPROP property) @@ -234,7 +232,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics Debug.Assert(propertyGetter >= 0 && propertyGetter < PREDEFMETH.PM_COUNT); RuntimeBinderSymbolTable.AddPredefinedPropertyToSymbolTable( - GetOptPredefAgg(GetPropPredefType(predefProp)), propertyName); + GetPredefAgg(GetPropPredefType(predefProp)), propertyName); MethodSymbol getter = GetMethod(propertyGetter); getter.SetMethKind(MethodKindEnum.PropAccessor); @@ -266,7 +264,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { return NameManager.GetPredefinedName(pn); } - private AggregateSymbol GetOptPredefAgg(PredefinedType pt) + private AggregateSymbol GetPredefAgg(PredefinedType pt) { return GetSymbolLoader().GetPredefAgg(pt); } @@ -280,76 +278,38 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics switch (current) { - case MethodSignatureEnum.SIG_REF: - { - CType refType = LoadTypeFromSignature(signature, ref indexIntoSignatures, classTyVars); - if (refType == null) - { - return null; - } - return GetTypeManager().GetParameterModifier(refType, false); - } - case MethodSignatureEnum.SIG_OUT: - { - CType outType = LoadTypeFromSignature(signature, ref indexIntoSignatures, classTyVars); - if (outType == null) - { - return null; - } - return GetTypeManager().GetParameterModifier(outType, true); - } case MethodSignatureEnum.SIG_SZ_ARRAY: - { - CType elementType = LoadTypeFromSignature(signature, ref indexIntoSignatures, classTyVars); - if (elementType == null) - { - return null; - } - return GetTypeManager().GetArray(elementType, 1, true); - } + return GetTypeManager() + .GetArray(LoadTypeFromSignature(signature, ref indexIntoSignatures, classTyVars), 1, true); + case MethodSignatureEnum.SIG_METH_TYVAR: - { - int index = signature[indexIntoSignatures]; - indexIntoSignatures++; - return GetTypeManager().GetStdMethTypeVar(index); - } + return GetTypeManager().GetStdMethTypeVar(signature[indexIntoSignatures++]); + case MethodSignatureEnum.SIG_CLASS_TYVAR: - { - int index = signature[indexIntoSignatures]; - indexIntoSignatures++; - return classTyVars[index]; - } + return classTyVars[signature[indexIntoSignatures++]]; + case (MethodSignatureEnum)PredefinedType.PT_VOID: return GetTypeManager().GetVoid(); + default: + Debug.Assert(current >= 0 && (int)current < (int)PredefinedType.PT_COUNT); + AggregateSymbol agg = GetPredefAgg((PredefinedType)current); + int typeCount = agg.GetTypeVars().Count; + if (typeCount == 0) { - Debug.Assert(current >= 0 && (int)current < (int)PredefinedType.PT_COUNT); - AggregateSymbol agg = GetOptPredefAgg((PredefinedType)current); - if (agg != null) - { - CType[] typeArgs = new CType[agg.GetTypeVars().Count]; - for (int iTypeArg = 0; iTypeArg < agg.GetTypeVars().Count; iTypeArg++) - { - typeArgs[iTypeArg] = LoadTypeFromSignature(signature, ref indexIntoSignatures, classTyVars); - if (typeArgs[iTypeArg] == null) - { - return null; - } - } - AggregateType type = GetTypeManager().GetAggregate(agg, getBSymmgr().AllocParams(agg.GetTypeVars().Count, typeArgs)); - if (type.isPredefType(PredefinedType.PT_G_OPTIONAL)) - { - return GetTypeManager().GetNubFromNullable(type); - } - - return type; - } + return GetTypeManager().GetAggregate(agg, BSYMMGR.EmptyTypeArray()); } - break; - } - return null; + CType[] typeArgs = new CType[typeCount]; + for (int iTypeArg = 0; iTypeArg < typeArgs.Length; iTypeArg++) + { + typeArgs[iTypeArg] = LoadTypeFromSignature(signature, ref indexIntoSignatures, classTyVars); + } + + return GetTypeManager().GetAggregate(agg, getBSymmgr().AllocParams(typeArgs)); + } } + private TypeArray LoadTypeArrayFromSignature(int[] signature, ref int indexIntoSignatures, TypeArray classTyVars) { Debug.Assert(signature != null); @@ -360,14 +320,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics Debug.Assert(count >= 0); CType[] ptypes = new CType[count]; - for (int i = 0; i < count; i++) + for (int i = 0; i < ptypes.Length; i++) { ptypes[i] = LoadTypeFromSignature(signature, ref indexIntoSignatures, classTyVars); - if (ptypes[i] == null) - { - return null; - } } + return getBSymmgr().AllocParams(count, ptypes); } @@ -427,8 +384,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics TypeArray argumentTypes = LoadTypeArrayFromSignature(signature, ref index, classTyVars); Debug.Assert(argumentTypes != null); - TypeArray standardMethodTyVars = GetTypeManager().GetStdMethTyVarArray(cMethodTyVars); - MethodSymbol ret = LookupMethodWhileLoading(type, cMethodTyVars, methodName, methodAccess, isStatic, isVirtual, returnType, argumentTypes); if (ret == null) @@ -443,7 +398,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { for (Symbol sym = GetSymbolLoader().LookupAggMember(methodName, type, symbmask_t.MASK_ALL); sym != null; - sym = GetSymbolLoader().LookupNextSym(sym, type, symbmask_t.MASK_ALL)) + sym = SymbolLoader.LookupNextSym(sym, type, symbmask_t.MASK_ALL)) { if (sym is MethodSymbol methsym) { diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/SemanticChecker.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/SemanticChecker.cs index 5b8c6fce65..1a8c941a44 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/SemanticChecker.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/SemanticChecker.cs @@ -4,7 +4,6 @@ using System.Diagnostics; using Microsoft.CSharp.RuntimeBinder.Errors; -using Microsoft.CSharp.RuntimeBinder.Syntax; namespace Microsoft.CSharp.RuntimeBinder.Semantics { @@ -38,8 +37,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics typeThru is AggregateType || typeThru is TypeParameterType || typeThru is ArrayType || - typeThru is NullableType || - typeThru is ErrorType); + typeThru is NullableType); #if DEBUG @@ -88,7 +86,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics if (!(type is AggregateType ats)) { - Debug.Assert(type is VoidType || type is ErrorType || type is TypeParameterType); + Debug.Assert(type is VoidType || type is TypeParameterType); return true; } @@ -124,7 +122,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics public ErrorHandling ErrorContext => SymbolLoader.ErrorContext; - public NameManager GetNameManager() { return SymbolLoader.GetNameManager(); } public TypeManager GetTypeManager() { return SymbolLoader.GetTypeManager(); } public BSYMMGR getBSymmgr() { return SymbolLoader.getBSymmgr(); } public SymFactory GetGlobalSymbolFactory() { return SymbolLoader.GetGlobalSymbolFactory(); } @@ -147,8 +144,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics typeThru is AggregateType || typeThru is TypeParameterType || typeThru is ArrayType || - typeThru is NullableType || - typeThru is ErrorType); + typeThru is NullableType); switch (symCheck.GetAccess()) { @@ -186,6 +182,14 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return ACCESSERROR.ACCESSERROR_NOACCESS; } break; + + case ACCESS.ACC_INTERNAL_AND_PROTECTED: + if (symWhere == null || !symWhere.SameAssemOrFriend(symCheck)) + { + return ACCESSERROR.ACCESSERROR_NOACCESS; + } + + break; } // Find the inner-most enclosing AggregateSymbol. @@ -230,7 +234,9 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } // Handle the protected case - which is the only real complicated one. - Debug.Assert(symCheck.GetAccess() == ACCESS.ACC_PROTECTED || symCheck.GetAccess() == ACCESS.ACC_INTERNALPROTECTED); + Debug.Assert(symCheck.GetAccess() == ACCESS.ACC_PROTECTED + || symCheck.GetAccess() == ACCESS.ACC_INTERNALPROTECTED + || symCheck.GetAccess() == ACCESS.ACC_INTERNAL_AND_PROTECTED); // Check if symCheck is in aggWhere or a base of aggWhere, // or in an outer agg of aggWhere or a base of an outer agg of aggWhere. @@ -266,10 +272,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // the CType in which the method is being called has no relationship with the // CType on which the method is defined surely this is NOACCESS and not NOACCESSTHRU - if (found == false) - return ACCESSERROR.ACCESSERROR_NOACCESS; - - return (atsThru == null) ? ACCESSERROR.ACCESSERROR_NOACCESS : ACCESSERROR.ACCESSERROR_NOACCESSTHRU; + return found ? ACCESSERROR.ACCESSERROR_NOACCESSTHRU : ACCESSERROR.ACCESSERROR_NOACCESS; } public static bool CheckBogus(Symbol sym) diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/AssemblyQualifiedNamespaceSymbol.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/AssemblyQualifiedNamespaceSymbol.cs deleted file mode 100644 index 5f788924fe..0000000000 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/AssemblyQualifiedNamespaceSymbol.cs +++ /dev/null @@ -1,19 +0,0 @@ -// 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. - -namespace Microsoft.CSharp.RuntimeBinder.Semantics -{ - // ---------------------------------------------------------------------------- - // - // AssemblyQualifiedNamespaceSymbol - // - // Parented by an NamespaceSymbol. Represents an NamespaceSymbol within an aid (assembly/alias id). - // The name is a form of the aid. - // ---------------------------------------------------------------------------- - - internal sealed class AssemblyQualifiedNamespaceSymbol : ParentSymbol - { - public NamespaceSymbol GetNS() => parent as NamespaceSymbol; - } -} diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/LocalVariableSymbol.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/LocalVariableSymbol.cs index 6ffa154d4c..54e659dacf 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/LocalVariableSymbol.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/LocalVariableSymbol.cs @@ -14,10 +14,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics public ExprWrap wrap; - public bool isThis; // Is this the one and only pointer? - // movedToField should have iIteratorLocal set appropriately - public bool fUsedInAnonMeth; // Set if the local is ever used in an anon method - public void SetType(CType pType) { type = pType; diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/MethodOrPropertySymbol.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/MethodOrPropertySymbol.cs index 9b7bdd4283..c26e7ac3f5 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/MethodOrPropertySymbol.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/MethodOrPropertySymbol.cs @@ -2,6 +2,7 @@ // 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.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; @@ -47,7 +48,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // If this symbol is a property and an explicit interface member implementation, the swtSlot // may be an event. This is filled in during prepare. public SymWithType swtSlot; - public ErrorType errExpImpl; // If name == NULL but swtExpImpl couldn't be resolved, this contains error information. public CType RetType; // Return type. private TypeArray _Params; @@ -59,16 +59,27 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } set { - // Should only be set once! + Debug.Assert(_Params == null, "Should only be set once"); _Params = value; - _optionalParameterIndex = new bool[_Params.Count]; - _defaultParameterIndex = new bool[_Params.Count]; - _defaultParameters = new ConstVal[_Params.Count]; - _defaultParameterConstValTypes = new CType[_Params.Count]; - _marshalAsIndex = new bool[_Params.Count]; - _marshalAsBuffer = new UnmanagedType[_Params.Count]; + int count = value.Count; + if (count == 0) + { + _optionalParameterIndex = _defaultParameterIndex = _marshalAsIndex = Array.Empty(); + _defaultParameters = Array.Empty(); + _defaultParameterConstValTypes = Array.Empty(); + _marshalAsBuffer = Array.Empty(); + } + else + { + _optionalParameterIndex = new bool[count]; + _defaultParameterIndex = new bool[count]; + _defaultParameters = new ConstVal[count]; + _defaultParameterConstValTypes = new CType[count]; + _marshalAsIndex = new bool[count]; + _marshalAsBuffer = new UnmanagedType[count]; + } } - } // array of cParams parameter types. + } public MethodOrPropertySymbol() { @@ -157,14 +168,13 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics public bool MarshalAsObject(int index) { - UnmanagedType marshalAsType = default(UnmanagedType); - if (IsMarshalAsParameter(index)) { - marshalAsType = GetMarshalAsParameterValue(index); + UnmanagedType marshalAsType = GetMarshalAsParameterValue(index); + return marshalAsType == UnmanagedType.Interface || marshalAsType == UnmanagedType.IUnknown || marshalAsType == UnmanagedType.IDispatch; } - return marshalAsType == UnmanagedType.Interface || marshalAsType == UnmanagedType.IUnknown; + return false; } public AggregateSymbol getClass() => parent as AggregateSymbol; diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/MethodSymbol.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/MethodSymbol.cs index b233c6d414..6745b9af16 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/MethodSymbol.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/MethodSymbol.cs @@ -24,8 +24,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics private EventSymbol _evt; // For event accessors, this is the EventSymbol. public bool isVirtual; // Virtual member? - public bool isAbstract; // Abstract method? - public bool isVarargs; // has varargs public MemberInfo AssociatedMemberInfo; public TypeArray typeVars; // All the type variables for a generic method, as declarations. diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/NamespaceSymbol.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/NamespaceSymbol.cs index e3cb090bdf..831cda395e 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/NamespaceSymbol.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/NamespaceSymbol.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; +using Microsoft.CSharp.RuntimeBinder.Syntax; namespace Microsoft.CSharp.RuntimeBinder.Semantics { @@ -31,5 +32,18 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics internal sealed class NamespaceSymbol : NamespaceOrAggregateSymbol { + /// The "root" (unnamed) namespace. + public static readonly NamespaceSymbol Root = GetRootNamespaceSymbol(); + + private static NamespaceSymbol GetRootNamespaceSymbol() + { + NamespaceSymbol root = new NamespaceSymbol + { + name = NameManager.GetPredefinedName(PredefinedName.PN_VOID) + }; + + root.setKind(SYMKIND.SK_NamespaceSymbol); + return root; + } } } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/Scope.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/Scope.cs index bc49a43818..10086defa1 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/Scope.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/Scope.cs @@ -6,6 +6,5 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { internal sealed class Scope : ParentSymbol { - public uint nestingOrder; // the nesting order of this scopes. outermost == 0 } } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymFactory.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymFactory.cs index f2be6b9419..e684b73e48 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymFactory.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymFactory.cs @@ -28,10 +28,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics sym = new NamespaceSymbol(); sym.name = name; break; - case SYMKIND.SK_AssemblyQualifiedNamespaceSymbol: - sym = new AssemblyQualifiedNamespaceSymbol(); - sym.name = name; - break; case SYMKIND.SK_AggregateSymbol: sym = new AggregateSymbol(); sym.name = name; @@ -97,16 +93,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return (sym); } - public AssemblyQualifiedNamespaceSymbol CreateNamespaceAid(Name name, ParentSymbol parent) - { - Debug.Assert(name != null); - - AssemblyQualifiedNamespaceSymbol sym = NewBasicSymbol(SYMKIND.SK_AssemblyQualifiedNamespaceSymbol, name, parent) as AssemblyQualifiedNamespaceSymbol; - - Debug.Assert(sym != null); - return sym; - } - ///////////////////////////////////////////////////////////////////////////////// public AggregateSymbol CreateAggregate(Name name, NamespaceOrAggregateSymbol parent, TypeManager typeManager) { @@ -143,7 +129,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } // Members of aggs - public FieldSymbol CreateMemberVar(Name name, ParentSymbol parent) + public FieldSymbol CreateMemberVar(Name name, AggregateSymbol parent) { Debug.Assert(name != null); @@ -153,7 +139,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return (sym); } - public LocalVariableSymbol CreateLocalVar(Name name, ParentSymbol parent, CType type) + public LocalVariableSymbol CreateLocalVar(Name name, Scope parent, CType type) { LocalVariableSymbol sym = (LocalVariableSymbol)NewBasicSymbol(SYMKIND.SK_LocalVariableSymbol, name, parent); sym.SetType(type); @@ -163,17 +149,17 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return sym; } - public MethodSymbol CreateMethod(Name name, ParentSymbol parent) => + public MethodSymbol CreateMethod(Name name, AggregateSymbol parent) => NewBasicSymbol(SYMKIND.SK_MethodSymbol, name, parent) as MethodSymbol; - public PropertySymbol CreateProperty(Name name, ParentSymbol parent) + public PropertySymbol CreateProperty(Name name, AggregateSymbol parent) { PropertySymbol sym = NewBasicSymbol(SYMKIND.SK_PropertySymbol, name, parent) as PropertySymbol; Debug.Assert(sym != null); return (sym); } - public EventSymbol CreateEvent(Name name, ParentSymbol parent) + public EventSymbol CreateEvent(Name name, AggregateSymbol parent) { EventSymbol sym = NewBasicSymbol(SYMKIND.SK_EventSymbol, name, parent) as EventSymbol; @@ -205,16 +191,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return pResult; } - public Scope CreateScope(Scope parent) - { - Scope sym = (Scope)NewBasicSymbol(SYMKIND.SK_Scope, null, parent); - if (parent != null) - { - sym.nestingOrder = parent.nestingOrder + 1; - } - - return sym; - } + public Scope CreateScope() => (Scope)NewBasicSymbol(SYMKIND.SK_Scope, null, null); public IndexerSymbol CreateIndexer(Name name, ParentSymbol parent, Name realName) { diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/Symbol.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/Symbol.cs index 7da37bdba4..e9f0380145 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/Symbol.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/Symbol.cs @@ -16,6 +16,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { ACC_UNKNOWN, // Not yet determined. ACC_PRIVATE, + ACC_INTERNAL_AND_PROTECTED, ACC_INTERNAL, ACC_PROTECTED, ACC_INTERNALPROTECTED, // internal OR protected @@ -241,12 +242,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } } - // Returns the virtual that this sym overrides (if IsOverride() is true), null otherwise. - public Symbol SymBaseVirtual() - { - return (this as MethodOrPropertySymbol)?.swtSlot.Sym; - } - /* * returns true if this symbol is a normal symbol visible to the user */ diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolKind.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolKind.cs index 405193322b..a512d7ab0d 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolKind.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolKind.cs @@ -7,7 +7,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics internal enum SYMKIND { SK_NamespaceSymbol, - SK_AssemblyQualifiedNamespaceSymbol, SK_AggregateSymbol, SK_AggregateDeclaration, SK_TypeParameterSymbol, diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolLoader.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolLoader.cs index d115740a05..103678206c 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolLoader.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolLoader.cs @@ -10,17 +10,14 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { internal sealed class SymbolLoader { - private readonly NameManager _nameManager; - - public PredefinedMembers PredefinedMembers { get; } + private PredefinedMembers PredefinedMembers { get; } private GlobalSymbolContext GlobalSymbolContext { get; } public ErrorHandling ErrorContext { get; } public SymbolTable RuntimeBinderSymbolTable { get; private set; } public SymbolLoader() { - GlobalSymbolContext globalSymbols = new GlobalSymbolContext(new NameManager()); - _nameManager = globalSymbols.GetNameManager(); + GlobalSymbolContext globalSymbols = new GlobalSymbolContext(); PredefinedMembers = new PredefinedMembers(this); ErrorContext = new ErrorHandling(globalSymbols); GlobalSymbolContext = globalSymbols; @@ -52,11 +49,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return null; } - public NameManager GetNameManager() - { - return _nameManager; - } - public PredefinedTypes GetPredefindTypes() { return GlobalSymbolContext.GetPredefTypes(); @@ -96,10 +88,8 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return getBSymmgr().LookupAggMember(name, agg, mask); } - public Symbol LookupNextSym(Symbol sym, ParentSymbol parent, symbmask_t kindmask) - { - return BSYMMGR.LookupNextSym(sym, parent, kindmask); - } + public static Symbol LookupNextSym(Symbol sym, ParentSymbol parent, symbmask_t kindmask) + => BSYMMGR.LookupNextSym(sym, parent, kindmask); // It would be nice to make this a virtual method on typeSym. public AggregateType GetAggTypeSym(CType typeSym) @@ -122,7 +112,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return null; } - private bool IsBaseInterface(AggregateType atsDer, AggregateType pBase) + private static bool IsBaseInterface(AggregateType atsDer, AggregateType pBase) { Debug.Assert(atsDer != null); Debug.Assert(pBase != null); @@ -146,7 +136,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return false; } - public bool IsBaseClassOfClass(CType pDerived, CType pBase) + public static bool IsBaseClassOfClass(CType pDerived, CType pBase) { Debug.Assert(pDerived != null); Debug.Assert(pBase != null); @@ -160,7 +150,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return IsBaseClass(pDerived, pBase); } - private bool IsBaseClass(CType pDerived, CType pBase) + private static bool IsBaseClass(CType pDerived, CType pBase) { Debug.Assert(pDerived != null); Debug.Assert(pBase != null); @@ -215,10 +205,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return HasImplicitReferenceConversion(pSource, pDest); } - private bool AreTypesEqualForConversion(CType pType1, CType pType2) - { - return pType1.Equals(pType2); - } + private static bool AreTypesEqualForConversion(CType pType1, CType pType2) => pType1.Equals(pType2); private bool HasArrayConversionToInterface(ArrayType pSource, CType pDest) { @@ -506,7 +493,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return true; } - public bool HasImplicitBoxingConversion(CType pSource, CType pDest) + private bool HasImplicitBoxingConversion(CType pSource, CType pDest) { Debug.Assert(pSource != null); Debug.Assert(pDest != null); @@ -515,18 +502,22 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // The rest of the boxing conversions only operate when going from a value type // to a reference type. - if (!pSource.IsValType() || !pDest.IsRefType()) + if (!pDest.IsRefType()) { return false; } // A boxing conversion exists from a nullable type to a reference type // if and only if a boxing conversion exists from the underlying type. - if (pSource is NullableType nubSource) { - return HasImplicitBoxingConversion(nubSource.GetUnderlyingType(), pDest); + pSource = nubSource.UnderlyingType; // pSource.IsValType() known to be true. } + else if (!pSource.IsValType()) + { + return false; + } + // A boxing conversion exists from any non-nullable value type to object, // to System.ValueType, and to any interface type implemented by the @@ -536,15 +527,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // We set the base class of the structs to System.ValueType, System.Enum, etc, // so we can just check here. - if (IsBaseClass(pSource, pDest)) - { - return true; - } - if (HasAnyBaseInterfaceConversion(pSource, pDest)) - { - return true; - } - return false; + return IsBaseClass(pSource, pDest) || HasAnyBaseInterfaceConversion(pSource, pDest); } public bool HasBaseConversion(CType pSource, CType pDest) @@ -590,7 +573,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return HasIdentityOrImplicitReferenceConversion(pSource, pDest) || HasImplicitBoxingConversion(pSource, pDest); } - public bool IsBaseAggregate(AggregateSymbol derived, AggregateSymbol @base) + public static bool IsBaseAggregate(AggregateSymbol derived, AggregateSymbol @base) { Debug.Assert(!derived.IsEnum() && !@base.IsEnum()); diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolManagerBase.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolManagerBase.cs index a414e22e66..92ee054047 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolManagerBase.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolManagerBase.cs @@ -19,9 +19,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics private readonly SymFactory _symFactory; - private readonly NamespaceSymbol _rootNS; // The "root" (unnamed) namespace. - - private NameManager m_nameTable; private SYMTBL tableGlobal; // The hash table for type arrays. @@ -29,15 +26,12 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics private static readonly TypeArray s_taEmpty = new TypeArray(Array.Empty()); - public BSYMMGR(NameManager nameMgr) + public BSYMMGR() { - this.m_nameTable = nameMgr; this.tableGlobal = new SYMTBL(); _symFactory = new SymFactory(this.tableGlobal); this.tableTypeArrays = new Dictionary(); - _rootNS = _symFactory.CreateNamespace(m_nameTable.Lookup(""), null); - GetNsAid(_rootNS); //////////////////////////////////////////////////////////////////////////////// // Build the data structures needed to make FPreLoad fast. Make sure the @@ -46,7 +40,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics for (int i = 0; i < (int)PredefinedType.PT_COUNT; ++i) { - NamespaceSymbol ns = GetRootNS(); + NamespaceSymbol ns = NamespaceSymbol.Root; string name = PredefinedTypeFacts.GetName((PredefinedType)i); int start = 0; while (start < name.Length) @@ -55,19 +49,13 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics if (iDot == -1) break; string sub = (iDot > start) ? name.Substring(start, iDot - start) : name.Substring(start); - Name nm = this.GetNameManager().Add(sub); + Name nm = NameManager.Add(sub); ns = LookupGlobalSymCore(nm, ns, symbmask_t.MASK_NamespaceSymbol) as NamespaceSymbol ?? _symFactory.CreateNamespace(nm, ns); start += sub.Length + 1; } } } - - public NameManager GetNameManager() - { - return m_nameTable; - } - public SYMTBL GetSymbolTable() { return tableGlobal; @@ -78,16 +66,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return s_taEmpty; } - public AssemblyQualifiedNamespaceSymbol GetRootNsAid() - { - return GetNsAid(_rootNS); - } - - public NamespaceSymbol GetRootNS() - { - return _rootNS; - } - public BetterType CompareTypes(TypeArray ta1, TypeArray ta2) { if (ta1 == ta2) @@ -128,7 +106,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics Debug.Assert(false, "Bad kind in CompareTypes"); break; case TypeKind.TK_TypeParameterType: - case TypeKind.TK_ErrorType: break; case TypeKind.TK_PointerType: @@ -166,30 +143,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return _symFactory; } - //////////////////////////////////////////////////////////////////////////////// - // Build the data structures needed to make FPreLoad fast. Make sure the - // namespaces are created. Compute and sort hashes of the NamespaceSymbol * value and type - // name (sans arity indicator). - - private void InitPreLoad() - { - for (int i = 0; i < (int)PredefinedType.PT_COUNT; ++i) - { - NamespaceSymbol ns = GetRootNS(); - string name = PredefinedTypeFacts.GetName((PredefinedType)i); - int start = 0; - while (start < name.Length) - { - int iDot = name.IndexOf('.', start); - if (iDot == -1) break; - string sub = (iDot > start) ? name.Substring(start, iDot - start) : name.Substring(start); - Name nm = this.GetNameManager().Add(sub); - ns = LookupGlobalSymCore(nm, ns, symbmask_t.MASK_NamespaceSymbol) as NamespaceSymbol ?? _symFactory.CreateNamespace(nm, ns); - start += sub.Length + 1; - } - } - } - public Symbol LookupGlobalSymCore(Name name, ParentSymbol parent, symbmask_t kindmask) { return tableGlobal.LookupSym(name, parent, kindmask); @@ -220,33 +173,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return null; } - public Name GetNameFromPtrs(object u1, object u2) - { - // Note: this won't produce the same names as the native logic - if (u2 != null) - { - return this.m_nameTable.Add(string.Format(CultureInfo.InvariantCulture, "{0:X}-{1:X}", u1.GetHashCode(), u2.GetHashCode())); - } - else - { - return this.m_nameTable.Add(string.Format(CultureInfo.InvariantCulture, "{0:X}", u1.GetHashCode())); - } - } - - private AssemblyQualifiedNamespaceSymbol GetNsAid(NamespaceSymbol ns) - { - Name name = GetNameFromPtrs(0, 0); - Debug.Assert(name != null); - - AssemblyQualifiedNamespaceSymbol nsa = LookupGlobalSymCore(name, ns, symbmask_t.MASK_AssemblyQualifiedNamespaceSymbol) as AssemblyQualifiedNamespaceSymbol - // Create a new one. - ?? _symFactory.CreateNamespaceAid(name, ns); - - Debug.Assert(nsa.GetNS() == ns); - - return nsa; - } - //////////////////////////////////////////////////////////////////////////////// // Allocate a type array; used to represent a parameter list. // We use a hash table to make sure that allocating the same type array twice @@ -256,7 +182,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // 2) Make it so parameter lists can be compared by a simple pointer comparison // 3) Allow us to associate a token with each signature for faster metadata emit - private struct TypeArrayKey : IEquatable + private readonly struct TypeArrayKey : IEquatable { private readonly CType[] _types; private readonly int _hashCode; @@ -264,11 +190,17 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics public TypeArrayKey(CType[] types) { _types = types; - _hashCode = 0; - for (int i = 0, n = types.Length; i < n; i++) + int hashCode = 0x162A16FE; + foreach (CType type in types) { - _hashCode ^= types[i].GetHashCode(); + hashCode = (hashCode << 5) - hashCode; + if (type != null) + { + hashCode ^= type.GetHashCode(); + } } + + _hashCode = hashCode; } public bool Equals(TypeArrayKey other) @@ -287,7 +219,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics for (int i = 0; i < types.Length; i++) { - if (!types[i].Equals(otherTypes[i])) + if (types[i] != otherTypes[i]) { return false; } @@ -323,6 +255,16 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics public TypeArray AllocParams(int ctype, TypeArray array, int offset) { + if (ctype == 0) + { + return s_taEmpty; + } + + if (ctype == array.Count) + { + return array; + } + CType[] types = array.Items; CType[] newTypes = new CType[ctype]; Array.ConstrainedCopy(types, offset, newTypes, 0, ctype); diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolMask.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolMask.cs index 1e9a9302bd..bda131b546 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolMask.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolMask.cs @@ -10,7 +10,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics internal enum symbmask_t : long { MASK_NamespaceSymbol = 1 << SYMKIND.SK_NamespaceSymbol, - MASK_AssemblyQualifiedNamespaceSymbol = 1 << SYMKIND.SK_AssemblyQualifiedNamespaceSymbol, MASK_AggregateSymbol = 1 << SYMKIND.SK_AggregateSymbol, MASK_TypeParameterSymbol = 1 << SYMKIND.SK_TypeParameterSymbol, MASK_FieldSymbol = 1 << SYMKIND.SK_FieldSymbol, diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Block.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Block.cs deleted file mode 100644 index 25fdeab7df..0000000000 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Block.cs +++ /dev/null @@ -1,17 +0,0 @@ -// 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. - -namespace Microsoft.CSharp.RuntimeBinder.Semantics -{ - internal sealed class ExprBlock : ExprStatement - { - public ExprBlock(ExprStatement optionalStatements) - : base(ExpressionKind.Block) - { - OptionalStatements = optionalStatements; - } - - public ExprStatement OptionalStatements { get; set; } - } -} diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/BoundAnonymousFunction.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/BoundAnonymousFunction.cs index 9b0c05972e..e5de6dadc3 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/BoundAnonymousFunction.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/BoundAnonymousFunction.cs @@ -8,15 +8,17 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { internal sealed class ExprBoundLambda : ExprWithType { - public ExprBoundLambda(CType type, Scope argumentScope) + public ExprBoundLambda(AggregateType type, Scope argumentScope, Expr expression) : base(ExpressionKind.BoundLambda, type) { - Debug.Assert(type == null || type.isDelegateType()); + Debug.Assert(type != null); + Debug.Assert(type.isDelegateType()); Debug.Assert(argumentScope != null); ArgumentScope = argumentScope; + Expression = expression; } - public ExprBlock OptionalBody { get; set; } + public Expr Expression { get; } public AggregateType DelegateType => Type as AggregateType; diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Call.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Call.cs index a2180521ba..5b148ff09c 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Call.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Call.cs @@ -6,7 +6,7 @@ using System.Diagnostics; namespace Microsoft.CSharp.RuntimeBinder.Semantics { - internal sealed class ExprCall : ExprWithType, IExprWithArgs + internal sealed class ExprCall : ExprWithArgs { public ExprCall(CType type, EXPRFLAG flags, Expr arguments, ExprMemberGroup member, MethWithInst method) : base(ExpressionKind.Call, type) @@ -21,16 +21,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics MethWithInst = method; } - public Expr OptionalArguments { get; set; } - - public ExprMemberGroup MemberGroup { get; set; } - - public Expr OptionalObject - { - get => MemberGroup.OptionalObject; - set => MemberGroup.OptionalObject = value; - } - public MethWithInst MethWithInst { get; set; } public PREDEFMETH PredefinedMethod { get; set; } = PREDEFMETH.PM_COUNT; @@ -41,6 +31,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics public Expr CastOfNonLiftedResultToLiftedType { get; set; } - SymWithType IExprWithArgs.GetSymWithType() => MethWithInst; + public override SymWithType GetSymWithType() => MethWithInst; } } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Cast.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Cast.cs index 915ad374fc..eed4a2b3b8 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Cast.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Cast.cs @@ -6,29 +6,19 @@ using System.Diagnostics; namespace Microsoft.CSharp.RuntimeBinder.Semantics { - internal sealed class ExprCast : Expr + internal sealed class ExprCast : ExprWithType { - private ExprClass _destinationType; - - public ExprCast(EXPRFLAG flags, ExprClass destinationType, Expr argument) - : base(ExpressionKind.Cast) + public ExprCast(EXPRFLAG flags, CType type, Expr argument) + : base(ExpressionKind.Cast, type) { Debug.Assert(argument != null); - Debug.Assert(destinationType != null); Debug.Assert((flags & ~(EXPRFLAG.EXF_CAST_ALL | EXPRFLAG.EXF_MASK_ANY)) == 0); Argument = argument; Flags = flags; - DestinationType = destinationType; } public Expr Argument { get; set; } - public ExprClass DestinationType - { - get => _destinationType; - set => Type = (_destinationType = value).Type; - } - public bool IsBoxingCast => (Flags & (EXPRFLAG.EXF_BOX | EXPRFLAG.EXF_FORCE_BOX)) != 0; } } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/EXPR.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/EXPR.cs index 3d15312d9a..eb7223b6fc 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/EXPR.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/EXPR.cs @@ -2,10 +2,14 @@ // 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.Diagnostics; + namespace Microsoft.CSharp.RuntimeBinder.Semantics { internal abstract class Expr { + private CType _type; + protected Expr(ExpressionKind kind) { Kind = kind; @@ -23,15 +27,15 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics public string ErrorString { get; set; } - public CType Type { get; protected set; } - - public bool IsOK => !HasError; - - public bool HasError { get; private set; } - - public void SetError() + public CType Type { - HasError = true; + get + { + Debug.Assert(!(this is ExprList)); + Debug.Assert(_type != null); + return _type; + } + protected set { _type = value; } } } } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/ExprOperator.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/ExprOperator.cs index d6d846247d..4f338cc3dc 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/ExprOperator.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/ExprOperator.cs @@ -19,10 +19,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { OptionalUserDefinedCall = call; UserDefinedCallMethod = userDefinedMethod; - if (call.HasError) - { - SetError(); - } } public Expr OptionalUserDefinedCall { get; } @@ -31,4 +27,4 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics public MethPropWithInst UserDefinedCallMethod { get; set; } } -} \ No newline at end of file +} diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/ExprWithArgs.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/ExprWithArgs.cs new file mode 100644 index 0000000000..eb81fcb9e3 --- /dev/null +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/ExprWithArgs.cs @@ -0,0 +1,26 @@ +// 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. + +namespace Microsoft.CSharp.RuntimeBinder.Semantics +{ + internal abstract class ExprWithArgs : ExprWithType, IExprWithObject + { + protected ExprWithArgs(ExpressionKind kind, CType type) + : base(kind, type) + { + } + + public ExprMemberGroup MemberGroup { get; set; } + + public Expr OptionalObject + { + get => MemberGroup.OptionalObject; + set => MemberGroup.OptionalObject = value; + } + + public Expr OptionalArguments { get; set; } + + public abstract SymWithType GetSymWithType(); + } +} diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Property.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Property.cs index 6654cca006..1d119647d0 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Property.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Property.cs @@ -4,7 +4,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { - internal sealed class ExprProperty : ExprWithType, IExprWithArgs + internal sealed class ExprProperty : ExprWithArgs { // If we have this.prop = 123, but the implementation of the property is in the // base class, then the object is of the base class type. Note that to get @@ -32,22 +32,12 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } } - public Expr OptionalArguments { get; set; } - - public ExprMemberGroup MemberGroup { get; set; } - - public Expr OptionalObject - { - get => MemberGroup.OptionalObject; - set => MemberGroup.OptionalObject = value; - } - public Expr OptionalObjectThrough { get; } public PropWithType PropWithTypeSlot { get; } public MethWithType MethWithTypeSet { get; } - SymWithType IExprWithArgs.GetSymWithType() => PropWithTypeSlot; + public override SymWithType GetSymWithType() => PropWithTypeSlot; } } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Return.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Return.cs deleted file mode 100644 index 3610b60487..0000000000 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Return.cs +++ /dev/null @@ -1,18 +0,0 @@ -// 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. - -namespace Microsoft.CSharp.RuntimeBinder.Semantics -{ - internal sealed class ExprReturn : ExprStatement, IExprWithObject - { - public ExprReturn(Expr optionalObject) - : base(ExpressionKind.Return) - { - OptionalObject = optionalObject; - } - - // Return object is optional because of void returns. - public Expr OptionalObject { get; set; } - } -} diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Statement.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Statement.cs deleted file mode 100644 index c007a01641..0000000000 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Statement.cs +++ /dev/null @@ -1,16 +0,0 @@ -// 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. - -namespace Microsoft.CSharp.RuntimeBinder.Semantics -{ - internal abstract class ExprStatement : Expr - { - public ExprStatement(ExpressionKind kind) - : base(kind) - { - } - - public ExprStatement OptionalNextStatement { get; set; } - } -} diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/TypeOf.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/TypeOf.cs index 79128ae7b8..8e4bbe19fb 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/TypeOf.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/TypeOf.cs @@ -6,13 +6,13 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { internal sealed class ExprTypeOf : ExprWithType { - public ExprTypeOf(CType type, ExprClass pSourceType) + public ExprTypeOf(CType type, CType sourceType) : base(ExpressionKind.TypeOf, type) { Flags = EXPRFLAG.EXF_CANTBENULL; - SourceType = pSourceType; + SourceType = sourceType; } - public ExprClass SourceType { get; set; } + public CType SourceType { get; } } } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/UserDefinedConversion.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/UserDefinedConversion.cs index a5c72a1b5b..8dc4208bf5 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/UserDefinedConversion.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/UserDefinedConversion.cs @@ -18,10 +18,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics Argument = argument; UserDefinedCall = call; UserDefinedCallMethod = method; - if (call.HasError) - { - SetError(); - } } public Expr Argument { get; set; } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Visitors/ExprVisitorBase.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Visitors/ExprVisitorBase.cs index 19642eccc4..99991ef719 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Visitors/ExprVisitorBase.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Visitors/ExprVisitorBase.cs @@ -21,66 +21,9 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return pResult; } - if (pExpr is ExprStatement statement) - { - return CacheExprMapping(pExpr, DispatchStatementList(statement)); - } - return CacheExprMapping(pExpr, Dispatch(pExpr)); } - ///////////////////////////////////////////////////////////////////////////////// - - private ExprStatement DispatchStatementList(ExprStatement expr) - { - Debug.Assert(expr != null); - - ExprStatement first = expr; - ExprStatement pexpr = first; - - while (pexpr != null) - { - // If the processor replaces the statement -- potentially with - // null, another statement, or a list of statements -- then we - // make sure that the statement list is hooked together correctly. - - ExprStatement next = pexpr.OptionalNextStatement; - ExprStatement old = pexpr; - - // Unhook the next one. - pexpr.OptionalNextStatement = null; - - ExprStatement result = Dispatch(pexpr) as ExprStatement; - - if (pexpr == first) - { - first = result; - } - else - { - pexpr.OptionalNextStatement = result; - } - - // A transformation may return back a list of statements (or - // if the statements have been determined to be unnecessary, - // perhaps it has simply returned null.) - // - // Skip visiting the new list, then hook the tail of the old list - // up to the end of the new list. - - while (pexpr.OptionalNextStatement != null) - { - pexpr = pexpr.OptionalNextStatement; - } - - // Re-hook the next pointer. - pexpr.OptionalNextStatement = next; - } - return first; - } - - ///////////////////////////////////////////////////////////////////////////////// - private bool IsCachedExpr(Expr pExpr, out Expr pTransformedExpr) { pTransformedExpr = null; @@ -98,10 +41,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { switch (pExpr.Kind) { - case ExpressionKind.Block: - return VisitBLOCK(pExpr as ExprBlock); - case ExpressionKind.Return: - return VisitRETURN(pExpr as ExprReturn); case ExpressionKind.BinaryOp: return VisitBINOP(pExpr as ExprBinOp); case ExpressionKind.UnaryOp: @@ -121,7 +60,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics case ExpressionKind.Constant: return VisitCONSTANT(pExpr as ExprConstant); case ExpressionKind.Class: - return VisitCLASS(pExpr as ExprClass); + return pExpr; case ExpressionKind.Property: return VisitPROP(pExpr as ExprProperty); case ExpressionKind.Multi: @@ -146,10 +85,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return VisitUSERLOGOP(pExpr as ExprUserLogicalOp); case ExpressionKind.MemberGroup: return VisitMEMGRP(pExpr as ExprMemberGroup); - case ExpressionKind.BoundLambda: - return VisitBOUNDLAMBDA(pExpr as ExprBoundLambda); - case ExpressionKind.HoistedLocalExpression: - return VisitHOISTEDLOCALEXPR(pExpr as ExprHoistedLocalExpr); case ExpressionKind.FieldInfo: return VisitFIELDINFO(pExpr as ExprFieldInfo); case ExpressionKind.MethodInfo: @@ -196,8 +131,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return VisitLOGOR(pExpr as ExprBinOp); case ExpressionKind.Sequence: return VisitSEQUENCE(pExpr as ExprBinOp); - case ExpressionKind.SequenceReverse: - return VisitSEQREV(pExpr as ExprBinOp); case ExpressionKind.Save: return VisitSAVE(pExpr as ExprBinOp); case ExpressionKind.Swap: @@ -329,16 +262,12 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics break; case ExpressionKind.TypeOf: - exprRet = Visit((pExpr as ExprTypeOf).SourceType); - (pExpr as ExprTypeOf).SourceType = exprRet as ExprClass; break; case ExpressionKind.Cast: exprRet = Visit((pExpr as ExprCast).Argument); Debug.Assert(exprRet != null); (pExpr as ExprCast).Argument = exprRet; - exprRet = Visit((pExpr as ExprCast).DestinationType); - (pExpr as ExprCast).DestinationType = exprRet as ExprClass; break; case ExpressionKind.UserDefinedConversion: @@ -350,11 +279,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics case ExpressionKind.ZeroInit: break; - case ExpressionKind.Block: - exprRet = Visit((pExpr as ExprBlock).OptionalStatements); - (pExpr as ExprBlock).OptionalStatements = exprRet as ExprStatement; - break; - case ExpressionKind.MemberGroup: // The object expression. NULL for a static invocation. @@ -383,11 +307,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics (pExpr as ExprField).OptionalObject = exprRet; break; - case ExpressionKind.Return: - exprRet = Visit((pExpr as ExprReturn).OptionalObject); - (pExpr as ExprReturn).OptionalObject = exprRet; - break; - case ExpressionKind.Constant: // Used for when we zeroinit 0 parameter constructors for structs/enums. @@ -448,17 +367,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics (pExpr as ExprArrayInit).OptionalArgumentDimensions = exprRet; break; - case ExpressionKind.BoundLambda: - exprRet = Visit((pExpr as ExprBoundLambda).OptionalBody); - (pExpr as ExprBoundLambda).OptionalBody = exprRet as ExprBlock; - break; - case ExpressionKind.Local: case ExpressionKind.Class: case ExpressionKind.MultiGet: case ExpressionKind.Wrap: case ExpressionKind.NoOp: - case ExpressionKind.HoistedLocalExpression: case ExpressionKind.FieldInfo: case ExpressionKind.MethodInfo: break; @@ -478,22 +391,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics VisitChildren(pExpr); return pExpr; } - protected virtual Expr VisitBLOCK(ExprBlock pExpr) - { - return VisitSTMT(pExpr); - } - protected virtual Expr VisitRETURN(ExprReturn pExpr) - { - return VisitSTMT(pExpr); - } - protected virtual Expr VisitCLASS(ExprClass pExpr) - { - return VisitEXPR(pExpr); - } - protected virtual Expr VisitSTMT(ExprStatement pExpr) - { - return VisitEXPR(pExpr); - } protected virtual Expr VisitBINOP(ExprBinOp pExpr) { return VisitEXPR(pExpr); @@ -580,15 +477,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { return VisitEXPR(pExpr); } - protected virtual Expr VisitBOUNDLAMBDA(ExprBoundLambda pExpr) - { - return VisitEXPR(pExpr); - } - protected virtual Expr VisitHOISTEDLOCALEXPR(ExprHoistedLocalExpr pExpr) - { - return VisitEXPR(pExpr); - } protected virtual Expr VisitFIELDINFO(ExprFieldInfo pExpr) { return VisitEXPR(pExpr); @@ -694,10 +583,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { return VisitBINOP(pExpr); } - protected virtual Expr VisitSEQREV(ExprBinOp pExpr) - { - return VisitBINOP(pExpr); - } protected virtual Expr VisitSTRINGNE(ExprBinOp pExpr) { return VisitBINOP(pExpr); diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Visitors/ExpressionTreeRewriter.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Visitors/ExpressionTreeRewriter.cs index fff8ed3c4d..f85ed2595a 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Visitors/ExpressionTreeRewriter.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Visitors/ExpressionTreeRewriter.cs @@ -9,15 +9,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { internal sealed class ExpressionTreeRewriter : ExprVisitorBase { - public static Expr Rewrite(Expr expr, ExprFactory expressionFactory, SymbolLoader symbolLoader) - { - ExpressionTreeRewriter rewriter = new ExpressionTreeRewriter(expressionFactory, symbolLoader); - return rewriter.Visit(expr); - } + public static ExprBinOp Rewrite(ExprBoundLambda expr, ExprFactory expressionFactory, SymbolLoader symbolLoader) => + new ExpressionTreeRewriter(expressionFactory, symbolLoader).VisitBoundLambda(expr); private ExprFactory expressionFactory; private SymbolLoader symbolLoader; - private ExprBoundLambda currentAnonMeth; private ExprFactory GetExprFactory() { return expressionFactory; } @@ -94,42 +90,27 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics ///////////////////////////////////////////////////////////////////////////////// // Expression types. - protected override Expr VisitBOUNDLAMBDA(ExprBoundLambda anonmeth) + private ExprBinOp VisitBoundLambda(ExprBoundLambda anonmeth) { Debug.Assert(anonmeth != null); - ExprBoundLambda prevAnonMeth = currentAnonMeth; - currentAnonMeth = anonmeth; MethodSymbol lambdaMethod = GetPreDefMethod(PREDEFMETH.PM_EXPRESSION_LAMBDA); - - CType delegateType = anonmeth.DelegateType; + AggregateType delegateType = anonmeth.DelegateType; TypeArray lambdaTypeParams = GetSymbolLoader().getBSymmgr().AllocParams(1, new CType[] { delegateType }); AggregateType expressionType = GetSymbolLoader().GetPredefindType(PredefinedType.PT_EXPRESSION); MethWithInst mwi = new MethWithInst(lambdaMethod, expressionType, lambdaTypeParams); Expr createParameters = CreateWraps(anonmeth); - Expr body = RewriteLambdaBody(anonmeth); - Expr parameters = RewriteLambdaParameters(anonmeth); + Debug.Assert(createParameters != null); + Debug.Assert(anonmeth.Expression != null); + Expr body = Visit(anonmeth.Expression); + Debug.Assert(anonmeth.ArgumentScope.nextChild == null); + Expr parameters = GenerateParamsArray(null, PredefinedType.PT_PARAMETEREXPRESSION); Expr args = GetExprFactory().CreateList(body, parameters); CType typeRet = GetSymbolLoader().GetTypeManager().SubstType(mwi.Meth().RetType, mwi.GetType(), mwi.TypeArgs); ExprMemberGroup pMemGroup = GetExprFactory().CreateMemGroup(null, mwi); ExprCall call = GetExprFactory().CreateCall(0, typeRet, args, pMemGroup, mwi); - Expr callLambda = call; - call.PredefinedMethod = PREDEFMETH.PM_EXPRESSION_LAMBDA; - - currentAnonMeth = prevAnonMeth; - if (createParameters != null) - { - callLambda = GetExprFactory().CreateSequence(createParameters, callLambda); - } - Expr expr = DestroyWraps(anonmeth, callLambda); - // If we are already inside an expression tree rewrite and this is an expression tree lambda - // then it needs to be quoted. - if (currentAnonMeth != null) - { - expr = GenerateCall(PREDEFMETH.PM_EXPRESSION_QUOTE, expr); - } - return expr; + return GetExprFactory().CreateSequence(createParameters, call); } protected override Expr VisitCONSTANT(ExprConstant expr) { @@ -139,14 +120,8 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics protected override Expr VisitLOCAL(ExprLocal local) { Debug.Assert(local != null); - Debug.Assert(!local.Local.isThis); - // this is true for all parameters of an expression lambda - if (local.Local.wrap != null) - { - return local.Local.wrap; - } - Debug.Assert(local.Local.fUsedInAnonMeth); - return GetExprFactory().CreateHoistedLocalInExpression(); + Debug.Assert(local.Local.wrap != null); + return local.Local.wrap; } protected override Expr VisitFIELD(ExprField expr) { @@ -177,7 +152,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // If we have generated an identity cast or reference cast to a base class // we can omit the cast. if (pArgument.Type == pExpr.Type || - GetSymbolLoader().IsBaseClassOfClass(pArgument.Type, pExpr.Type) || + SymbolLoader.IsBaseClassOfClass(pArgument.Type, pExpr.Type) || CConversions.FImpRefConv(GetSymbolLoader(), pArgument.Type, pExpr.Type)) { return Visit(pArgument); @@ -499,28 +474,9 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } Expr origOp = expr.Child; - return GenerateBuiltInUnaryOperator(pdm, origOp, expr); - } - - private Expr GenerateBuiltInUnaryOperator(PREDEFMETH pdm, Expr pOriginalOperator, Expr pOperator) - { - Expr op = Visit(pOriginalOperator); - bool isNullableEnum = pOriginalOperator.Type is NullableType nub && nub.underlyingType().isEnumType(); - if (isNullableEnum) - { - Debug.Assert(pOperator.Kind == ExpressionKind.BitwiseNot); // The only built-in unary operator defined on nullable enum. - CType underlyingType = pOriginalOperator.Type.StripNubs().underlyingEnumType(); - CType nullableType = GetSymbolLoader().GetTypeManager().GetNullable(underlyingType); - op = GenerateCall(PREDEFMETH.PM_EXPRESSION_CONVERT, op, CreateTypeOf(nullableType)); - } - - Expr call = GenerateCall(pdm, op); - if (isNullableEnum) - { - call = GenerateCall(PREDEFMETH.PM_EXPRESSION_CONVERT, call, CreateTypeOf(pOperator.Type)); - } - - return call; + // Such operations are always already casts on operations on casts. + Debug.Assert(!(origOp.Type is NullableType nub) || !nub.UnderlyingType.isEnumType()); + return GenerateCall(pdm, Visit(origOp)); } private Expr GenerateUserDefinedBinaryOperator(ExprBinOp expr) @@ -691,54 +647,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return GenerateCall(pdm, p1, p2, lift, methodInfo); } - private Expr RewriteLambdaBody(ExprBoundLambda anonmeth) - { - Debug.Assert(anonmeth != null); - Debug.Assert(anonmeth.OptionalBody != null); - Debug.Assert(anonmeth.OptionalBody.OptionalStatements != null); - // There ought to be no way to get an empty statement block successfully converted into an expression tree. - Debug.Assert(anonmeth.OptionalBody.OptionalStatements.OptionalNextStatement== null); - - ExprBlock body = anonmeth.OptionalBody; - - // The most likely case: - if (body.OptionalStatements is ExprReturn ret) - { - Debug.Assert(ret.OptionalObject != null); - return Visit(ret.OptionalObject); - } - // This can only if it is a void delegate and this is a void expression, such as a call to a void method - // or something like Expression> e = (Foo f) => f.MyEvent += MyDelegate; - - throw Error.InternalCompilerError(); - } - - private Expr RewriteLambdaParameters(ExprBoundLambda anonmeth) - { - Debug.Assert(anonmeth != null); - - // new ParameterExpression[2] {Parameter(typeof(type1), name1), Parameter(typeof(type2), name2)} - - Expr paramArrayInitializerArgs = null; - Expr paramArrayInitializerArgsTail = paramArrayInitializerArgs; - - for (Symbol sym = anonmeth.ArgumentScope; sym != null; sym = sym.nextChild) - { - if (!(sym is LocalVariableSymbol local)) - { - continue; - } - - if (local.isThis) - { - continue; - } - GetExprFactory().AppendItemToList(local.wrap, ref paramArrayInitializerArgs, ref paramArrayInitializerArgsTail); - } - - return GenerateParamsArray(paramArrayInitializerArgs, PredefinedType.PT_PARAMETEREXPRESSION); - } - private Expr GenerateConversion(Expr arg, CType CType, bool bChecked) { return GenerateConversionWithSource(Visit(arg), CType, bChecked || arg.isChecked()); @@ -883,11 +791,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics continue; } - if (local.isThis) - { - continue; - } - Debug.Assert(anonmeth.OptionalBody != null); + Debug.Assert(anonmeth.Expression != null); Expr create = GenerateParameter(local.name.Text, local.GetType()); local.wrap = GetExprFactory().CreateWrap(create); Expr save = GetExprFactory().CreateSave(local.wrap); @@ -904,27 +808,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return sequence; } - private Expr DestroyWraps(ExprBoundLambda anonmeth, Expr sequence) - { - for (Symbol sym = anonmeth.ArgumentScope; sym != null; sym = sym.nextChild) - { - if (!(sym is LocalVariableSymbol local)) - { - continue; - } - - if (local.isThis) - { - continue; - } - Debug.Assert(local.wrap != null); - Debug.Assert(anonmeth.OptionalBody != null); - Expr freeWrap = GetExprFactory().CreateWrap(local.wrap); - sequence = GetExprFactory().CreateReverseSequence(sequence, freeWrap); - } - return sequence; - } - private Expr GenerateConstructor(ExprCall expr) { Debug.Assert(expr != null); @@ -958,8 +841,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics Expr newIndex = it.Current(); if (newIndex.Type != intType) { - ExprClass exprType = expressionFactory.CreateClass(intType); - newIndex = expressionFactory.CreateCast(EXPRFLAG.EXF_INDEXEXPR, exprType, newIndex); + newIndex = expressionFactory.CreateCast(EXPRFLAG.EXF_INDEXEXPR, intType, newIndex); newIndex.Flags |= EXPRFLAG.EXF_CHECKOVERFLOW; } Expr rewrittenIndex = Visit(newIndex); @@ -986,8 +868,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics flags = EXPRFLAG.EXF_BOX; } - ExprClass objectType = GetExprFactory().CreateClass(pObject); - ExprCast cast = GetExprFactory().CreateCast(flags, objectType, expr); + ExprCast cast = GetExprFactory().CreateCast(flags, pObject, expr); ExprTypeOf pTypeOf2 = CreateTypeOf(expr.Type); return GenerateCall(PREDEFMETH.PM_EXPRESSION_CONSTANT_OBJECT_TYPE, cast, pTypeOf2); diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/TypeBind.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/TypeBind.cs index 7b75b91031..40a3ac8ef6 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/TypeBind.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/TypeBind.cs @@ -146,12 +146,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics bool fReportErrors = 0 == (flags & CheckConstraintsFlags.NoErrors); - if (arg is ErrorType) - { - // Error should have been reported previously. - return false; - } - bool fError = false; if (var.HasRefConstraint() && !arg.IsRefType()) { @@ -303,7 +297,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics case TypeKind.TK_VoidType: case TypeKind.TK_PointerType: - case TypeKind.TK_ErrorType: return false; case TypeKind.TK_ArrayType: @@ -324,7 +317,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { default: return false; - case TypeKind.TK_ErrorType: case TypeKind.TK_PointerType: return false; case TypeKind.TK_NullableType: diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/AggregateType.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/AggregateType.cs index b97e65e958..cdc7501642 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/AggregateType.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/AggregateType.cs @@ -2,6 +2,7 @@ // 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.Collections.Generic; using System.Diagnostics; @@ -46,8 +47,35 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics public AggregateType GetBaseClass() { - return _baseType ?? - (_baseType = getAggregate().GetTypeManager().SubstType(getAggregate().GetBaseClass(), GetTypeArgsAll()) as AggregateType); + if (_baseType == null) + { + Type baseSysType = AssociatedSystemType.BaseType; + if (baseSysType == null) + { + return null; + } + + // If we have a generic type definition, then we need to set the + // base class to be our current base type, and use that to calculate + // our agg type and its base, then set it to be the generic version of the + // base type. This is because: + // + // Suppose we have Foo : IFoo + // + // Initially, the BaseType will be IFoo, which gives us the substitution + // that we want to use for our agg type's base type. However, in the Symbol chain, + // we want the base type to be IFoo. So we need to substitute. + // + // If we don't have a generic type definition, then we just need to set our base + // class. This is so that if we have a base type that's generic, we'll be + // getting the correctly instantiated base type. + TypeManager manager = GetOwningAggregate().GetTypeManager(); + AggregateType baseClass = manager.SymbolTable.GetCTypeFromType(baseSysType) as AggregateType; + Debug.Assert(baseClass != null); + _baseType = manager.SubstType(baseClass, GetTypeArgsAll()); + } + + return _baseType; } public IEnumerable TypeHierarchy @@ -170,30 +198,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } return _winrtifacesAll; } - - public TypeArray GetDelegateParameters(SymbolLoader pSymbolLoader) - { - Debug.Assert(isDelegateType()); - MethodSymbol invoke = pSymbolLoader.LookupInvokeMeth(getAggregate()); - if (invoke == null || !invoke.isInvoke()) - { - // This can happen if the delegate is internal to another assembly. - return null; - } - return getAggregate().GetTypeManager().SubstTypeArray(invoke.Params, this); - } - - public CType GetDelegateReturnType(SymbolLoader pSymbolLoader) - { - Debug.Assert(isDelegateType()); - MethodSymbol invoke = pSymbolLoader.LookupInvokeMeth(getAggregate()); - if (invoke == null || !invoke.isInvoke()) - { - // This can happen if the delegate is internal to another assembly. - return null; - } - return getAggregate().GetTypeManager().SubstType(invoke.RetType, this); - } } } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/ErrorType.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/ErrorType.cs deleted file mode 100644 index 7376f4ceaf..0000000000 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/ErrorType.cs +++ /dev/null @@ -1,28 +0,0 @@ -// 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 Microsoft.CSharp.RuntimeBinder.Syntax; - -namespace Microsoft.CSharp.RuntimeBinder.Semantics -{ - // ---------------------------------------------------------------------------- - // - // ErrorType - // - // ErrorType - a symbol representing an error that has been reported. - // ---------------------------------------------------------------------------- - - internal sealed class ErrorType : CType - { - public Name nameText; - public TypeArray typeArgs; - - // ErrorTypes are always either the per-TypeManager singleton ErrorType - // that has a null nameText and no namespace parent, or else have a - // non-null nameText and have the root namespace as the namespace parent, - // so checking that nameText isn't null is equivalent to checking if the - // type has a parent. - public bool HasParent => nameText != null; - } -} diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/PredefinedTypes.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/PredefinedTypes.cs index 5ea4cd818c..437a150b1f 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/PredefinedTypes.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/PredefinedTypes.cs @@ -210,6 +210,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics new PredefinedTypeInfo(PredefinedType.PT_MISSING, typeof(System.Reflection.Missing), "System.Reflection.Missing"), new PredefinedTypeInfo(PredefinedType.PT_G_IREADONLYLIST, typeof(IReadOnlyList<>), "System.Collections.Generic.IReadOnlyList`1"), new PredefinedTypeInfo(PredefinedType.PT_G_IREADONLYCOLLECTION, typeof(IReadOnlyCollection<>), "System.Collections.Generic.IReadOnlyCollection`1"), + new PredefinedTypeInfo(PredefinedType.PT_FUNC, typeof(Func<>), "System.Func`1") }; private static readonly Dictionary s_typesByName = CreatePredefinedTypeFacts(); diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/Type.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/Type.cs index 8a83c9a0d9..71477ffff7 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/Type.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/Type.cs @@ -15,8 +15,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics private TypeKind _typeKind; private Name _pName; - private bool _fHasErrors; // Whether anyituents have errors. This is immutable. - public bool IsWindowsRuntimeType() { return (AssociatedSystemType.Attributes & TypeAttributes.WindowsRuntime) == TypeAttributes.WindowsRuntime; @@ -176,22 +174,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } } - public void InitFromParent() - { - Debug.Assert(!(this is AggregateType)); - Debug.Assert(!(this is ErrorType)); - _fHasErrors = GetBaseOrParameterOrElementType().HasErrors(); - } - - public bool HasErrors() - { - return _fHasErrors; - } - public void SetErrors(bool fHasErrors) - { - _fHasErrors = fHasErrors; - } - //////////////////////////////////////////////////////////////////////////////// // Given a symbol, determine its fundamental type. This is the type that // indicate how the item is stored and what instructions are used to reference @@ -510,12 +492,5 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return false; } } - - // A few types can be the same pointer value and not actually - // be equivalent or convertible (like ANONMETHSYMs) - public bool IsNeverSameType() - { - return this is MethodGroupType || this is ErrorType err && !err.HasParent; - } } } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeFactory.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeFactory.cs index f737321150..bca41ac99f 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeFactory.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeFactory.cs @@ -11,7 +11,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { // Aggregate public AggregateType CreateAggregateType( - Name name, AggregateSymbol parent, TypeArray typeArgsThis, AggregateType outerType) @@ -21,8 +20,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics type.outerType = outerType; type.SetOwningAggregate(parent); type.SetTypeArgsThis(typeArgsThis); - type.SetName(name); - type.SetTypeKind(TypeKind.TK_AggregateType); return type; } @@ -69,20 +66,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return type; } - public ErrorType CreateError( - Name name, - Name nameText, - TypeArray typeArgs) - { - ErrorType e = new ErrorType(); - e.SetName(name); - e.nameText = nameText; - e.typeArgs = typeArgs; - - e.SetTypeKind(TypeKind.TK_ErrorType); - return e; - } - // Derived types - parent is base type public ArrayType CreateArray(Name name, CType pElementType, int rank, bool isSZArray) { diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeKind.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeKind.cs index 323d0ee88f..03e3b4fa7a 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeKind.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeKind.cs @@ -10,7 +10,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics TK_VoidType, TK_NullType, TK_MethodGroupType, - TK_ErrorType, TK_ArgumentListType, TK_ArrayType, TK_PointerType, diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeManager.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeManager.cs index f3113125dc..b0fdce88ea 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeManager.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeManager.cs @@ -26,7 +26,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics private readonly NullType _nullType; private readonly MethodGroupType _typeMethGrp; private readonly ArgumentListType _argListType; - private readonly ErrorType _errorType; private readonly StdTypeVarColl _stvcMethod; private readonly StdTypeVarColl _stvcClass; @@ -37,14 +36,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics _typeTable = new TypeTable(); // special types with their own symbol kind. - _errorType = _typeFactory.CreateError(null, null, null); _voidType = _typeFactory.CreateVoid(); _nullType = _typeFactory.CreateNull(); _typeMethGrp = _typeFactory.CreateMethodGroup(); _argListType = _typeFactory.CreateArgList(); - _errorType.SetErrors(true); - _stvcMethod = new StdTypeVarColl(); _stvcClass = new StdTypeVarColl(); _BSymmgr = bsymmgr; @@ -56,6 +52,8 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics _symbolTable = table; } + public SymbolTable SymbolTable => _symbolTable; + private sealed class StdTypeVarColl { private readonly List prgptvs; @@ -80,7 +78,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { Debug.Assert(iv >= 0); - TypeParameterType tpt = null; + TypeParameterType tpt; if (iv >= this.prgptvs.Count) { TypeParameterSymbol pTypeParameter = new TypeParameterSymbol(); @@ -122,7 +120,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics name = NameManager.GetPredefinedName(PredefinedName.PN_ARRAY0 + args); break; default: - name = _BSymmgr.GetNameManager().Add("[X" + args + 1); + name = NameManager.Add("[X" + args + 1); break; } @@ -132,14 +130,8 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { // No existing array symbol. Create a new one. pArray = _typeFactory.CreateArray(name, elementType, args, isSZArray); - pArray.InitFromParent(); - _typeTable.InsertArray(name, elementType, pArray); } - else - { - Debug.Assert(pArray.HasErrors() == elementType.HasErrors()); - } Debug.Assert(pArray.rank == args); Debug.Assert(pArray.GetElementType() == elementType); @@ -158,15 +150,10 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } Debug.Assert(agg.GetTypeVars().Count == typeArgs.Count); - - Name name = _BSymmgr.GetNameFromPtrs(typeArgs, atsOuter); - Debug.Assert(name != null); - - AggregateType pAggregate = _typeTable.LookupAggregate(name, agg); + AggregateType pAggregate = _typeTable.LookupAggregate(agg, atsOuter, typeArgs); if (pAggregate == null) { pAggregate = _typeFactory.CreateAggregateType( - name, agg, typeArgs, atsOuter @@ -174,39 +161,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics Debug.Assert(!pAggregate.fConstraintsChecked && !pAggregate.fConstraintError); - pAggregate.SetErrors(false); - _typeTable.InsertAggregate(name, agg, pAggregate); - - // If we have a generic type definition, then we need to set the - // base class to be our current base type, and use that to calculate - // our agg type and its base, then set it to be the generic version of the - // base type. This is because: - // - // Suppose we have Foo : IFoo - // - // Initially, the BaseType will be IFoo, which gives us the substitution - // that we want to use for our agg type's base type. However, in the Symbol chain, - // we want the base type to be IFoo. Thats why we need to do this little trick. - // - // If we don't have a generic type definition, then we just need to set our base - // class. This is so that if we have a base type that's generic, we'll be - // getting the correctly instantiated base type. - - var baseType = pAggregate.AssociatedSystemType?.BaseType; - if (baseType != null) - { - // Store the old base class. - - AggregateType oldBaseType = agg.GetBaseClass(); - agg.SetBaseClass(_symbolTable.GetCTypeFromType(baseType) as AggregateType); - pAggregate.GetBaseClass(); // Get the base type for the new agg type we're making. - - agg.SetBaseClass(oldBaseType); - } - } - else - { - Debug.Assert(!pAggregate.HasErrors()); + _typeTable.InsertAggregate(agg, atsOuter, typeArgs, pAggregate); } Debug.Assert(pAggregate.getAggregate() == agg); @@ -247,14 +202,8 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics Name namePtr = NameManager.GetPredefinedName(PredefinedName.PN_PTR); pPointer = _typeFactory.CreatePointer(namePtr, baseType); - pPointer.InitFromParent(); - _typeTable.InsertPointer(baseType, pPointer); } - else - { - Debug.Assert(pPointer.HasErrors() == baseType.HasErrors()); - } Debug.Assert(pPointer.GetReferentType() == baseType); @@ -275,8 +224,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics Name pName = NameManager.GetPredefinedName(PredefinedName.PN_NUB); pNullableType = _typeFactory.CreateNullable(pName, pUnderlyingType, _BSymmgr, this); - pNullableType.InitFromParent(); - _typeTable.InsertNullable(pUnderlyingType, pNullableType); } @@ -299,52 +246,14 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // No existing parammod symbol. Create a new one. pParamModifier = _typeFactory.CreateParameterModifier(name, paramType); pParamModifier.isOut = isOut; - pParamModifier.InitFromParent(); - _typeTable.InsertParameterModifier(name, paramType, pParamModifier); } - else - { - Debug.Assert(pParamModifier.HasErrors() == paramType.HasErrors()); - } Debug.Assert(pParamModifier.GetParameterType() == paramType); return pParamModifier; } - public ErrorType GetErrorType( - Name nameText, - TypeArray typeArgs) - { - Debug.Assert(nameText != null); - if (typeArgs == null) - { - typeArgs = BSYMMGR.EmptyTypeArray(); - } - - Name name = _BSymmgr.GetNameFromPtrs(nameText, typeArgs); - Debug.Assert(name != null); - - ErrorType pError = _typeTable.LookupError(name); - - if (pError == null) - { - // No existing error symbol. Create a new one. - pError = _typeFactory.CreateError(name, nameText, typeArgs); - pError.SetErrors(true); - _typeTable.InsertError(name, pError); - } - else - { - Debug.Assert(pError.HasErrors()); - Debug.Assert(pError.nameText == nameText); - Debug.Assert(pError.typeArgs == typeArgs); - } - - return pError; - } - public VoidType GetVoid() { return _voidType; @@ -365,11 +274,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return _argListType; } - public ErrorType GetErrorSym() - { - return _errorType; - } - public AggregateSymbol GetNullable() => GetPredefAgg(PredefinedType.PT_G_OPTIONAL); private CType SubstType(CType typeSrc, TypeArray typeArgsCls, TypeArray typeArgsMeth, SubstTypeFlags grfst) @@ -381,9 +285,18 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return ctx.FNop() ? typeSrc : SubstTypeCore(typeSrc, ctx); } - public CType SubstType(CType typeSrc, TypeArray typeArgsCls) + public AggregateType SubstType(AggregateType typeSrc, TypeArray typeArgsCls) { - return SubstType(typeSrc, typeArgsCls, null, SubstTypeFlags.NormNone); + if (typeSrc != null) + { + SubstContext ctx = new SubstContext(typeArgsCls, null, SubstTypeFlags.NormNone); + if (!ctx.FNop()) + { + return SubstTypeCore(typeSrc, ctx); + } + } + + return typeSrc; } private CType SubstType(CType typeSrc, TypeArray typeArgsCls, TypeArray typeArgsMeth) @@ -391,45 +304,53 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return SubstType(typeSrc, typeArgsCls, typeArgsMeth, SubstTypeFlags.NormNone); } - public TypeArray SubstTypeArray(TypeArray taSrc, SubstContext pctx) + public TypeArray SubstTypeArray(TypeArray taSrc, SubstContext ctx) { - if (taSrc == null || taSrc.Count == 0 || pctx == null || pctx.FNop()) - return taSrc; - - CType[] prgpts = new CType[taSrc.Count]; - for (int ipts = 0; ipts < taSrc.Count; ipts++) + if (taSrc != null && taSrc.Count != 0 && ctx != null && !ctx.FNop()) { - prgpts[ipts] = this.SubstTypeCore(taSrc[ipts], pctx); + CType[] srcs = taSrc.Items; + for (int i = 0; i < srcs.Length; i++) + { + CType src = srcs[i]; + CType dst = SubstTypeCore(src, ctx); + if (src != dst) + { + CType[] dsts = new CType[srcs.Length]; + Array.Copy(srcs, dsts, i); + dsts[i] = dst; + while (++i < srcs.Length) + { + dsts[i] = SubstTypeCore(srcs[i], ctx); + } + + return _BSymmgr.AllocParams(dsts); + } + } } - return _BSymmgr.AllocParams(taSrc.Count, prgpts); - } - private TypeArray SubstTypeArray(TypeArray taSrc, TypeArray typeArgsCls, TypeArray typeArgsMeth, SubstTypeFlags grfst) - { - if (taSrc == null || taSrc.Count == 0) - return taSrc; - - var ctx = new SubstContext(typeArgsCls, typeArgsMeth, grfst); - - if (ctx.FNop()) - return taSrc; - - CType[] prgpts = new CType[taSrc.Count]; - for (int ipts = 0; ipts < taSrc.Count; ipts++) - { - prgpts[ipts] = SubstTypeCore(taSrc[ipts], ctx); - } - return _BSymmgr.AllocParams(taSrc.Count, prgpts); + return taSrc; } public TypeArray SubstTypeArray(TypeArray taSrc, TypeArray typeArgsCls, TypeArray typeArgsMeth) - { - return this.SubstTypeArray(taSrc, typeArgsCls, typeArgsMeth, SubstTypeFlags.NormNone); - } + => taSrc == null || taSrc.Count == 0 + ? taSrc + : SubstTypeArray(taSrc, new SubstContext(typeArgsCls, typeArgsMeth, SubstTypeFlags.NormNone)); - public TypeArray SubstTypeArray(TypeArray taSrc, TypeArray typeArgsCls) + public TypeArray SubstTypeArray(TypeArray taSrc, TypeArray typeArgsCls) => SubstTypeArray(taSrc, typeArgsCls, null); + + private AggregateType SubstTypeCore(AggregateType type, SubstContext ctx) { - return this.SubstTypeArray(taSrc, typeArgsCls, (TypeArray)null, SubstTypeFlags.NormNone); + TypeArray args = type.GetTypeArgsAll(); + if (args.Count > 0) + { + TypeArray typeArgs = SubstTypeArray(args, ctx); + if (args != typeArgs) + { + return GetAggregate(type.getAggregate(), typeArgs); + } + } + + return type; } private CType SubstTypeCore(CType type, SubstContext pctx) @@ -468,28 +389,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return (typeDst == typeSrc) ? type : GetNullable(typeDst); case TypeKind.TK_AggregateType: - AggregateType ats = (AggregateType)type; - if (ats.GetTypeArgsAll().Count > 0) - { - TypeArray typeArgs = SubstTypeArray(ats.GetTypeArgsAll(), pctx); - if (ats.GetTypeArgsAll() != typeArgs) - return GetAggregate(ats.getAggregate(), typeArgs); - } - return type; - - case TypeKind.TK_ErrorType: - ErrorType err = (ErrorType)type; - if (err.HasParent) - { - Debug.Assert(err.nameText != null && err.typeArgs != null); - TypeArray typeArgs = SubstTypeArray(err.typeArgs, pctx); - if (typeArgs != err.typeArgs) - { - return GetErrorType(err.nameText, typeArgs); - } - } - - return type; + return SubstTypeCore((AggregateType)type, pctx); case TypeKind.TK_TypeParameterType: { @@ -626,31 +526,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } return true; - case TypeKind.TK_ErrorType: - ErrorType errSrc = (ErrorType)typeSrc; - if (!(typeDst is ErrorType errDst) || !errSrc.HasParent || !errDst.HasParent) - return false; - - { - Debug.Assert(errSrc.nameText != null && errSrc.typeArgs != null); - Debug.Assert(errDst.nameText != null && errDst.typeArgs != null); - - if (errSrc.nameText != errDst.nameText || errSrc.typeArgs.Count != errDst.typeArgs.Count - || errSrc.HasParent != errDst.HasParent) - { - return false; - } - - // All the args must unify. - for (int i = 0; i < errSrc.typeArgs.Count; i++) - { - if (!SubstEqualTypesCore(errDst.typeArgs[i], errSrc.typeArgs[i], pctx)) - return false; - } - } - - return true; - case TypeKind.TK_TypeParameterType: { // BLOCK TypeParameterSymbol tvs = ((TypeParameterType)typeSrc).GetTypeParameterSymbol(); @@ -732,21 +607,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } return false; - case TypeKind.TK_ErrorType: - ErrorType err = (ErrorType)type; - if (err.HasParent) - { - Debug.Assert(err.nameText != null && err.typeArgs != null); - - for (int i = 0; i < err.typeArgs.Count; i++) - { - if (TypeContainsType(err.typeArgs[i], typeFind)) - return true; - } - } - - return false; - case TypeKind.TK_TypeParameterType: return false; } @@ -787,23 +647,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } return false; - case TypeKind.TK_ErrorType: - ErrorType err = (ErrorType)type; - if (err.HasParent) - { - Debug.Assert(err.nameText != null && err.typeArgs != null); - - for (int i = 0; i < err.typeArgs.Count; i++) - { - if (TypeContainsTyVars(err.typeArgs[i], typeVars)) - { - return true; - } - } - } - - return false; - case TypeKind.TK_TypeParameterType: if (typeVars != null && typeVars.Count > 0) { @@ -848,6 +691,9 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return _BSymmgr.AllocParams(cTyVars, (CType[])prgvar); } + public AggregateType SubstType(AggregateType typeSrc, SubstContext ctx) => + ctx == null || ctx.FNop() ? typeSrc : SubstTypeCore(typeSrc, ctx); + public CType SubstType(CType typeSrc, SubstContext pctx) { return (pctx == null || pctx.FNop()) ? typeSrc : SubstTypeCore(typeSrc, pctx); @@ -903,18 +749,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics return _stvcClass.GetTypeVarSym(iv, this, false); } + // These are singletons for each. public TypeParameterType GetTypeParameter(TypeParameterSymbol pSymbol) { - // These guys should be singletons for each. - - TypeParameterType pTypeParameter = _typeTable.LookupTypeParameter(pSymbol); - if (pTypeParameter == null) - { - pTypeParameter = _typeFactory.CreateTypeParameter(pSymbol); - _typeTable.InsertTypeParameter(pSymbol, pTypeParameter); - } - - return pTypeParameter; + Debug.Assert(pSymbol.GetTypeParameterType() == null); // Should have been checked first before creating + return _typeFactory.CreateTypeParameter(pSymbol); } // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -944,7 +783,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics } // These guys have no accessibility concerns. - Debug.Assert(!(typeSrc is VoidType) && !(typeSrc is ErrorType) && !(typeSrc is TypeParameterType)); + Debug.Assert(!(typeSrc is VoidType) && !(typeSrc is TypeParameterType)); if (typeSrc is ParameterModifierType || typeSrc is PointerType) { @@ -1124,7 +963,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics var key = Tuple.Create(assemblyThatDefinesAttribute, assemblyToCheck); if (!_internalsVisibleToCalculated.TryGetValue(key, out result)) { - AssemblyName assyName = null; + AssemblyName assyName; // Assembly.GetName() requires FileIOPermission to FileIOPermissionAccess.PathDiscovery. // If we don't have that (we're in low trust), then we are going to effectively turn off diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeParameterType.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeParameterType_.cs similarity index 100% rename from external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeParameterType.cs rename to external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeParameterType_.cs diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeTable.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeTable.cs index 2329a3382d..0540f983ff 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeTable.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeTable.cs @@ -10,7 +10,7 @@ using Microsoft.CSharp.RuntimeBinder.Syntax; namespace Microsoft.CSharp.RuntimeBinder.Semantics { - internal struct KeyPair : IEquatable> + internal readonly struct KeyPair : IEquatable> { private readonly Key1 _pKey1; private readonly Key2 _pKey2; @@ -23,8 +23,8 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics public bool Equals(KeyPair other) { - return Equals(_pKey1, other._pKey1) - && Equals(_pKey2, other._pKey2); + return EqualityComparer.Default.Equals(_pKey1, other._pKey1) + && EqualityComparer.Default.Equals(_pKey2, other._pKey2); } #if DEBUG @@ -39,62 +39,45 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics public override int GetHashCode() { - return (_pKey1 == null ? 0 : _pKey1.GetHashCode()) - + (_pKey2 == null ? 0 : _pKey2.GetHashCode()); + int hash = _pKey1 == null ? 0 : _pKey1.GetHashCode(); + return (hash << 5) - hash + (_pKey2 == null ? 0 : _pKey2.GetHashCode()); } } internal sealed class TypeTable { // Two way hashes - private readonly Dictionary, AggregateType> _pAggregateTable; + private readonly Dictionary>, AggregateType> _aggregateTable; private readonly Dictionary, ArrayType> _pArrayTable; private readonly Dictionary, ParameterModifierType> _pParameterModifierTable; // One way hashes - private readonly Dictionary _pErrorWithNamespaceParentTable; private readonly Dictionary _pPointerTable; private readonly Dictionary _pNullableTable; - private readonly Dictionary _pTypeParameterTable; public TypeTable() { - _pAggregateTable = new Dictionary, AggregateType>(); - _pErrorWithNamespaceParentTable = new Dictionary(); + _aggregateTable = new Dictionary>, AggregateType>(); _pArrayTable = new Dictionary, ArrayType>(); _pParameterModifierTable = new Dictionary, ParameterModifierType>(); _pPointerTable = new Dictionary(); _pNullableTable = new Dictionary(); - _pTypeParameterTable = new Dictionary(); } - public AggregateType LookupAggregate(Name pName, AggregateSymbol pAggregate) + private static KeyPair MakeKey(TKey1 key1, TKey2 key2) => + new KeyPair(key1, key2); + + public AggregateType LookupAggregate(AggregateSymbol aggregate, AggregateType outer, TypeArray args) { - var key = new KeyPair(pAggregate, pName); - AggregateType result; - if (_pAggregateTable.TryGetValue(key, out result)) - { - return result; - } - return null; + _aggregateTable.TryGetValue(MakeKey(aggregate, MakeKey(outer, args)), out AggregateType result); + return result; } public void InsertAggregate( - Name pName, - AggregateSymbol pAggregateSymbol, - AggregateType pAggregate) + AggregateSymbol aggregate, AggregateType outer, TypeArray args, AggregateType pAggregate) { - Debug.Assert(LookupAggregate(pName, pAggregateSymbol) == null); - _pAggregateTable.Add(new KeyPair(pAggregateSymbol, pName), pAggregate); - } - - public ErrorType LookupError(Name pName) => - _pErrorWithNamespaceParentTable.TryGetValue(pName, out ErrorType result) ? result : null; - - public void InsertError(Name pName, ErrorType pError) - { - Debug.Assert(LookupError(pName) == null); - _pErrorWithNamespaceParentTable.Add(pName, pError); + Debug.Assert(LookupAggregate(aggregate, outer, args) == null); + _aggregateTable.Add(MakeKey(aggregate, MakeKey(outer, args)), pAggregate); } public ArrayType LookupArray(Name pName, CType pElementType) @@ -163,22 +146,5 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { _pNullableTable.Add(pUnderlyingType, pNullable); } - - public TypeParameterType LookupTypeParameter(TypeParameterSymbol pTypeParameterSymbol) - { - TypeParameterType result; - if (_pTypeParameterTable.TryGetValue(pTypeParameterSymbol, out result)) - { - return result; - } - return null; - } - - public void InsertTypeParameter( - TypeParameterSymbol pTypeParameterSymbol, - TypeParameterType pTypeParameter) - { - _pTypeParameterTable.Add(pTypeParameterSymbol, pTypeParameter); - } } } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/UnaOpSig.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/UnaOpSig.cs index cbbe443ca7..0542d12c84 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/UnaOpSig.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/UnaOpSig.cs @@ -41,6 +41,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics this.grfuom = UnaOpMask.None; this.cuosSkip = 0; this.pfn = pfn; + Debug.Assert(type != null); _type = type; _grflt = grflt; this.fnkind = fnkind; @@ -56,6 +57,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics this.cuosSkip = uos.cuosSkip; this.pfn = uos.pfn; this.fnkind = uos.fnkind; + Debug.Assert(pt != PredefinedType.PT_UNDEFINEDINDEX); _type = pt != PredefinedType.PT_UNDEFINEDINDEX ? fnc.GetPredefindType(pt) : null; _grflt = LiftFlags.None; } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/SymbolTable.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/SymbolTable.cs index 935b6991f5..af53592265 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/SymbolTable.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/SymbolTable.cs @@ -25,13 +25,10 @@ namespace Microsoft.CSharp.RuntimeBinder // Members from the managed binder. private readonly SYMTBL _symbolTable; private readonly SymFactory _symFactory; - private readonly NameManager _nameManager; private readonly TypeManager _typeManager; private readonly BSYMMGR _bsymmgr; private readonly CSemanticChecker _semanticChecker; - private NamespaceSymbol _rootNamespace; - ///////////////////////////////////////////////////////////////////////////////// private sealed class NameHashKey : IEquatable @@ -67,18 +64,15 @@ namespace Microsoft.CSharp.RuntimeBinder internal SymbolTable( SYMTBL symTable, SymFactory symFactory, - NameManager nameManager, TypeManager typeManager, BSYMMGR bsymmgr, CSemanticChecker semanticChecker) { _symbolTable = symTable; _symFactory = symFactory; - _nameManager = nameManager; _typeManager = typeManager; _bsymmgr = bsymmgr; _semanticChecker = semanticChecker; - _rootNamespace = _bsymmgr.GetRootNS(); // Now populate object. LoadSymbolsFromType(typeof(object)); @@ -341,7 +335,7 @@ namespace Microsoft.CSharp.RuntimeBinder private Name GetName(string p) { - return _nameManager.Add(p ?? ""); + return NameManager.Add(p ?? ""); } ///////////////////////////////////////////////////////////////////////////////// @@ -354,11 +348,11 @@ namespace Microsoft.CSharp.RuntimeBinder int idx = name.IndexOf('`'); if (idx >= 0) { - return _nameManager.Add(name, idx); + return NameManager.Add(name, idx); } } - return _nameManager.Add(name); + return NameManager.Add(name); } #endregion @@ -644,127 +638,66 @@ namespace Microsoft.CSharp.RuntimeBinder #region LoadTypeChain ///////////////////////////////////////////////////////////////////////////////// - private CType LoadSymbolsFromType(Type originalType) + private CType LoadSymbolsFromType(Type type) { - List declarationChain = BuildDeclarationChain(originalType); + List declarationChain = BuildDeclarationChain(type); - Type type = originalType; - CType ret = null; - bool bIsByRef = type.IsByRef; - if (bIsByRef) - { - type = type.GetElementType(); - } - - NamespaceOrAggregateSymbol current = _rootNamespace; + NamespaceOrAggregateSymbol current = NamespaceSymbol.Root; // Go through the declaration chain and add namespaces and types for // each element in the chain. for (int i = 0; i < declarationChain.Count; i++) { object o = declarationChain[i]; - NamespaceOrAggregateSymbol next; - if (o is Type) + if (o is Type t) { - Type t = o as Type; - Name name = null; - name = GetName(t); - next = _symbolTable.LookupSym(name, current, symbmask_t.MASK_AggregateSymbol) as AggregateSymbol; - - // Make sure we match arity as well when we find an aggregate. - if (next != null) + if (t.IsNullableType()) { - next = FindSymWithMatchingArity(next as AggregateSymbol, t); + return _typeManager.GetNullable(GetCTypeFromType(t.GetGenericArguments()[0])); } - // In the event that two different types exist that have the same name, they - // cannot both have entries in the symbol table with our current architecture. - // This can happen in dynamic, since the runtime binder lives across all - // call sites in an appdomain, and assemblies can have been loaded at runtime - // that have different types with the same name. - - // In the real compiler, this would have been an error and name lookup would - // be ambiguous, but here we never have to lookup names of types for real (only - // names of members). - - // The tactical fix is this: if we encounter this situation, where we have - // identically named types that are not the same, then we are going to clear - // the entire symbol table and restart this binding. This solution is not - // without its own problems, since it is possible to conceive of a single - // dynamic binding that needs to simultaneously know about both of the - // similarly named types, but we are not going to try to solve that - // scenario here. - - if (next != null) - { - Type existingType = (next as AggregateSymbol).AssociatedSystemType; - Type newType = t.IsGenericType ? t.GetGenericTypeDefinition() : t; - - // We use "IsEquivalentTo" so that unified local types for NoPIA do - // not trigger a reset. There are other mechanisms to make those sorts - // of types work in some scenarios. - if (!existingType.IsEquivalentTo(newType)) - { - throw new ResetBindException(); - } - } + AggregateSymbol next = FindSymForType( + _symbolTable.LookupSym(GetName(t), current, symbmask_t.MASK_AggregateSymbol), t); // If we haven't found this type yet, then add it to our symbol table. - if (next == null || t.IsNullableType()) + if (next == null) { // Note that if we have anything other than an AggregateSymbol, // we must be at the end of the line - that is, nothing else can // have children. - CType ctype = ProcessSpecialTypeInChain(current, t); if (ctype != null) { - // If we had an aggregate type, its possible we're not at the end. - // This will happen for nullable for instance. - if (ctype is AggregateType cat) - { - next = cat.GetOwningAggregate(); - } - else - { - ret = ctype; - break; - } - } - else - { - // This is a regular class. - next = AddAggregateToSymbolTable(current, t); + Debug.Assert(!(ctype is AggregateType)); + return ctype; } + + // This is a regular class. + next = AddAggregateToSymbolTable(current, t); } if (t == type) { - ret = GetConstructedType(type, next as AggregateSymbol); - break; + return GetConstructedType(type, next); } + + current = next; } - else if (o is MethodInfo) + else if (o is MethodInfo m) { // We cant be at the end. Debug.Assert(i + 1 < declarationChain.Count); - ret = ProcessMethodTypeParameter(o as MethodInfo, declarationChain[++i] as Type, current as AggregateSymbol); - break; + return ProcessMethodTypeParameter(m, declarationChain[++i] as Type, current as AggregateSymbol); } else { Debug.Assert(o is string); - next = AddNamespaceToSymbolTable(current, o as string); + current = AddNamespaceToSymbolTable(current, o as string); } - current = next; } - Debug.Assert(ret != null); - if (bIsByRef) - { - ret = _typeManager.GetParameterModifier(ret, false); - } - return ret; + Debug.Fail("Should be unreachable"); + return null; } ///////////////////////////////////////////////////////////////////////////////// @@ -812,18 +745,17 @@ namespace Microsoft.CSharp.RuntimeBinder private CType ProcessSpecialTypeInChain(NamespaceOrAggregateSymbol parent, Type t) { - CType ctype; if (t.IsGenericParameter) { AggregateSymbol agg = parent as AggregateSymbol; Debug.Assert(agg != null); - ctype = LoadClassTypeParameter(agg, t); - return ctype; + return LoadClassTypeParameter(agg, t); } - else if (t.IsArray) + + if (t.IsArray) { // Now we return an array of nesting level corresponding to the rank. - ctype = _typeManager.GetArray( + return _typeManager.GetArray( GetCTypeFromType(t.GetElementType()), t.GetArrayRank(), #if netcoreapp @@ -832,37 +764,14 @@ namespace Microsoft.CSharp.RuntimeBinder t.GetElementType().MakeArrayType() == t #endif ); - return ctype; } - else if (t.IsPointer) + + if (t.IsPointer) { // Now we return the pointer type that we want. - ctype = _typeManager.GetPointer(GetCTypeFromType(t.GetElementType())); - return ctype; - } - else if (t.IsNullableType()) - { - // Get a nullable type of the underlying type. - if (t.GetGenericArguments()[0].DeclaringType == t) - { - // If the generic argument for nullable is our child, then we're - // declaring the initial Nullable. - AggregateSymbol agg = _symbolTable.LookupSym( - GetName(t), parent, symbmask_t.MASK_AggregateSymbol) as AggregateSymbol; - if (agg != null) - { - agg = FindSymWithMatchingArity(agg, t); - if (agg != null) - { - Debug.Assert(agg.getThisType().AssociatedSystemType == t); - return agg.getThisType(); - } - } - return AddAggregateToSymbolTable(parent, t).getThisType(); - } - ctype = _typeManager.GetNullable(GetCTypeFromType(t.GetGenericArguments()[0])); - return ctype; + return _typeManager.GetPointer(GetCTypeFromType(t.GetElementType())); } + return null; } @@ -914,40 +823,41 @@ namespace Microsoft.CSharp.RuntimeBinder Debug.Assert(bAdded); } } + callChain.Reverse(); // Now take out the namespaces and add them to the end of the chain. - if (callingType.Namespace != null) { - string[] namespaces = callingType.Namespace.Split('.'); - int index = 0; - foreach (string s in namespaces) - { - callChain.Insert(index++, s); - } + callChain.InsertRange(0, callingType.Namespace.Split('.')); } return callChain; } - ///////////////////////////////////////////////////////////////////////////////// + // We have an aggregate symbol of the correct parent and full name, but it may have the wrong arity, or, due to + // dynamic loading or creation, two different types can exist that have the same name. - private AggregateSymbol FindSymWithMatchingArity(AggregateSymbol aggregateSymbol, Type type) + // In the static compiler, this would have been an error and name lookup would be ambiguous, but here we never have + // to lookup names of types for real (only names of members). + + // For either case, move onto the next symbol in the chain, and check again for appropriate type. + private AggregateSymbol FindSymForType(Symbol sym, Type t) { - for (AggregateSymbol agg = aggregateSymbol; - agg != null; - agg = BSYMMGR.LookupNextSym(agg, agg.Parent, symbmask_t.MASK_AggregateSymbol) as AggregateSymbol) + while (sym != null) { - if (agg.GetTypeVarsAll().Count == type.GetGenericArguments().Length) + // We use "IsEquivalentTo" so that unified local types match. + if (sym is AggregateSymbol agg) + if (agg.AssociatedSystemType.IsEquivalentTo(t.IsGenericType ? t.GetGenericTypeDefinition() : t)) { return agg; } + + sym = sym.nextSameName; } + return null; } - ///////////////////////////////////////////////////////////////////////////////// - private NamespaceSymbol AddNamespaceToSymbolTable(NamespaceOrAggregateSymbol parent, string sz) { Name name = GetName(sz); @@ -982,10 +892,10 @@ namespace Microsoft.CSharp.RuntimeBinder ///////////////////////////////////////////////////////////////////////////////// - internal CType GetCTypeFromType(Type t) - { - return LoadSymbolsFromType(t); - } + internal CType GetCTypeFromType(Type type) => type.IsByRef + ? _typeManager.GetParameterModifier(LoadSymbolsFromType(type.GetElementType()), false) + : LoadSymbolsFromType(type); + #endregion #region Aggregates @@ -1043,10 +953,8 @@ namespace Microsoft.CSharp.RuntimeBinder else if (type.IsNested) { // If its nested, we may have other accessibility options. - if (type.IsNestedAssembly || type.IsNestedFamANDAssem) + if (type.IsNestedAssembly) { - // Note that we don't directly support NestedFamANDAssem, but we're just - // going to default to internal. access = ACCESS.ACC_INTERNAL; } else if (type.IsNestedFamORAssem) @@ -1061,6 +969,10 @@ namespace Microsoft.CSharp.RuntimeBinder { access = ACCESS.ACC_PROTECTED; } + else if (type.IsNestedFamANDAssem) + { + access = ACCESS.ACC_INTERNAL_AND_PROTECTED; + } else { Debug.Assert(type.IsPublic || type.IsNestedPublic); @@ -1195,14 +1107,18 @@ namespace Microsoft.CSharp.RuntimeBinder { access = ACCESS.ACC_PROTECTED; } - else if (fieldInfo.IsAssembly || fieldInfo.IsFamilyAndAssembly) + else if (fieldInfo.IsAssembly) { access = ACCESS.ACC_INTERNAL; } + else if (fieldInfo.IsFamilyOrAssembly) + { + access = ACCESS.ACC_INTERNALPROTECTED; + } else { - Debug.Assert(fieldInfo.IsFamilyOrAssembly); - access = ACCESS.ACC_INTERNALPROTECTED; + Debug.Assert(fieldInfo.IsFamilyAndAssembly); + access = ACCESS.ACC_INTERNAL_AND_PROTECTED; } field.SetAccess(access); field.isReadOnly = fieldInfo.IsInitOnly; @@ -1385,7 +1301,7 @@ namespace Microsoft.CSharp.RuntimeBinder } prevProp = prop; - prop = _semanticChecker.SymbolLoader.LookupNextSym(prop, prop.parent, symbmask_t.MASK_PropertySymbol) as PropertySymbol; + prop = SymbolLoader.LookupNextSym(prop, prop.parent, symbmask_t.MASK_PropertySymbol) as PropertySymbol; } prop = prevProp; @@ -1589,15 +1505,18 @@ namespace Microsoft.CSharp.RuntimeBinder { access = ACCESS.ACC_INTERNALPROTECTED; } + else if (member.IsAssembly) + { + access = ACCESS.ACC_INTERNAL; + } else { - Debug.Assert(member.IsAssembly || member.IsFamilyAndAssembly); - access = ACCESS.ACC_INTERNAL; + Debug.Assert(member.IsFamilyAndAssembly); + access = ACCESS.ACC_INTERNAL_AND_PROTECTED; } methodSymbol.SetAccess(access); methodSymbol.isVirtual = member.IsVirtual; - methodSymbol.isAbstract = member.IsAbstract; methodSymbol.isStatic = member.IsStatic; if (method != null) @@ -1606,7 +1525,6 @@ namespace Microsoft.CSharp.RuntimeBinder methodSymbol.isOverride = method.IsVirtual && method.IsHideBySig && method.GetRuntimeBaseDefinition() != method; methodSymbol.isOperator = IsOperator(method); methodSymbol.swtSlot = GetSlotForOverride(method); - methodSymbol.isVarargs = (method.CallingConvention & CallingConventions.VarArgs) == CallingConventions.VarArgs; methodSymbol.RetType = GetCTypeFromType(method.ReturnType); } else @@ -1615,7 +1533,6 @@ namespace Microsoft.CSharp.RuntimeBinder methodSymbol.isOverride = false; methodSymbol.isOperator = false; methodSymbol.swtSlot = null; - methodSymbol.isVarargs = false; methodSymbol.RetType = _typeManager.GetVoid(); } @@ -1624,7 +1541,6 @@ namespace Microsoft.CSharp.RuntimeBinder methodSymbol.isParamArray = DoesMethodHaveParameterArray(parameters); methodSymbol.isHideByName = false; - methodSymbol.errExpImpl = null; methodSymbol.Params = CreateParameterArray(methodSymbol.AssociatedMemberInfo, parameters); SetParameterDataForMethProp(methodSymbol, parameters); @@ -1713,6 +1629,7 @@ namespace Microsoft.CSharp.RuntimeBinder { object defValue = parameter.DefaultValue; #endif + Debug.Assert(Type.GetTypeCode(defValue.GetType()) != TypeCode.Decimal); // Handled above switch (Type.GetTypeCode(defValue.GetType())) { @@ -1746,11 +1663,6 @@ namespace Microsoft.CSharp.RuntimeBinder cvType = _semanticChecker.SymbolLoader.GetPredefindType(PredefinedType.PT_DOUBLE); break; - case TypeCode.Decimal: - cv = ConstVal.Get((decimal)defValue); - cvType = _semanticChecker.SymbolLoader.GetPredefindType(PredefinedType.PT_DECIMAL); - break; - case TypeCode.Char: cv = ConstVal.Get((char)defValue); cvType = _semanticChecker.SymbolLoader.GetPredefindType(PredefinedType.PT_CHAR); @@ -1840,9 +1752,7 @@ namespace Microsoft.CSharp.RuntimeBinder types.Add(GetTypeOfParameter(p, associatedInfo)); } - MethodInfo mi = associatedInfo as MethodInfo; - - if (mi != null && (mi.CallingConvention & CallingConventions.VarArgs) == CallingConventions.VarArgs) + if (associatedInfo is MethodBase mb && (mb.CallingConvention & CallingConventions.VarArgs) != 0) { types.Add(_typeManager.GetArgListType()); } @@ -1918,16 +1828,10 @@ namespace Microsoft.CSharp.RuntimeBinder AggregateSymbol aggregate = GetCTypeFromType(baseMethodInfo.DeclaringType).getAggregate(); MethodSymbol baseMethod = FindMethodFromMemberInfo(baseMethodInfo); - - // This assert is temporarily disabled to improve testability of the area on .NetNative - //Debug.Assert(baseMethod != null); - if ((object)baseMethod == null) - { - throw Error.InternalCompilerError(); - } - + Debug.Assert(baseMethod != null); return new SymWithType(baseMethod, aggregate.getThisType()); } + return null; } @@ -1946,7 +1850,7 @@ namespace Microsoft.CSharp.RuntimeBinder symbmask_t.MASK_MethodSymbol) as MethodSymbol; for (; meth != null && !meth.AssociatedMemberInfo.IsEquivalentTo(baseMemberInfo); - meth = _semanticChecker.SymbolLoader.LookupNextSym(meth, aggregate, symbmask_t.MASK_MethodSymbol) as MethodSymbol) + meth = SymbolLoader.LookupNextSym(meth, aggregate, symbmask_t.MASK_MethodSymbol) as MethodSymbol) ; return meth; diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/NameManager.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/NameManager.cs index 1620e0813c..95433c6fca 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/NameManager.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/NameManager.cs @@ -6,7 +6,7 @@ using System.Diagnostics; namespace Microsoft.CSharp.RuntimeBinder.Syntax { - internal class NameManager + internal static class NameManager { private static readonly Name[] s_predefinedNames = { new Name(".ctor"), @@ -112,6 +112,8 @@ namespace Microsoft.CSharp.RuntimeBinder.Syntax new Name("RemoveEventHandler"), new Name("InvocationList"), new Name("GetOrCreateEventRegistrationTokenTable"), + new Name("void"), + new Name(""), /* Above here corresponds with PredefinedName enum */ new Name("true"), new Name("false"), @@ -128,12 +130,10 @@ namespace Microsoft.CSharp.RuntimeBinder.Syntax new Name("checked"), new Name("is"), new Name("typeof"), - new Name("unchecked"), - new Name("void"), - new Name("") + new Name("unchecked") }; - private static readonly NameTable s_knownNames = GetKnownNames(); + private static readonly NameTable s_names = GetKnownNames(); private static NameTable GetKnownNames() { @@ -147,33 +147,21 @@ namespace Microsoft.CSharp.RuntimeBinder.Syntax return table; } - private readonly NameTable _names = new NameTable(); - - internal Name Add(string key) + internal static Name Add(string key) { if (key == null) { throw Error.InternalCompilerError(); } - return s_knownNames.Lookup(key) ?? _names.Add(key); + return s_names.Add(key); } - internal Name Add(string key, int length) + internal static Name Add(string key, int length) { Debug.Assert(key != null); Debug.Assert(key.Length >= length); - return s_knownNames.Lookup(key, length) ?? _names.Add(key, length); - } - - internal Name Lookup(string key) - { - if (key == null) - { - throw Error.InternalCompilerError(); - } - - return s_knownNames.Lookup(key) ?? _names.Lookup(key); + return s_names.Add(key, length); } internal static Name GetPredefinedName(PredefinedName id) diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/NameTable.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/NameTable.cs index 7561581fe7..cebc308052 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/NameTable.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/NameTable.cs @@ -65,40 +65,11 @@ namespace Microsoft.CSharp.RuntimeBinder.Syntax { int hashCode = ComputeHashCode(name.Text); // make sure it doesn't already exist - Debug.Assert(Lookup(name.Text) == null); Debug.Assert(_entries.All(e => e?.Name.Text != name.Text)); AddEntry(name, hashCode); } - public Name Lookup(string key) - { - int hashCode = ComputeHashCode(key); - for (Entry e = _entries[hashCode & _mask]; e != null; e = e.Next) - { - if (e.HashCode == hashCode && e.Name.Text.Equals(key)) - { - return e.Name; - } - } - - return null; - } - - public Name Lookup(string key, int length) - { - int hashCode = ComputeHashCode(key, length); - for (Entry e = _entries[hashCode & _mask]; e != null; e = e.Next) - { - if (e.HashCode == hashCode && Equals(e.Name.Text, key, length)) - { - return e.Name; - } - } - - return null; - } - private static int ComputeHashCode(string key) { unchecked diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/PredefinedName.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/PredefinedName.cs index 7b86b71f35..f5eb28820b 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/PredefinedName.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/PredefinedName.cs @@ -117,6 +117,9 @@ namespace Microsoft.CSharp.RuntimeBinder.Syntax PN_INVOCATIONLIST, PN_GETORCREATEEVENTREGISTRATIONTOKENTABLE, + PN_VOID, + PN_EMPTY, + PN_COUNT, // Not a name, this is the total count of predefined names } } diff --git a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/PredefinedType.cs b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/PredefinedType.cs index de4c01615a..e4e3299c21 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/PredefinedType.cs +++ b/external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/PredefinedType.cs @@ -75,6 +75,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Syntax PT_G_IREADONLYLIST, PT_G_IREADONLYCOLLECTION, + PT_FUNC, PT_COUNT, PT_VOID, // (special case) diff --git a/external/corefx/src/Microsoft.CSharp/src/Resources/Strings.resx b/external/corefx/src/Microsoft.CSharp/src/Resources/Strings.resx index cfb5bb414c..462e5c137e 100644 --- a/external/corefx/src/Microsoft.CSharp/src/Resources/Strings.resx +++ b/external/corefx/src/Microsoft.CSharp/src/Resources/Strings.resx @@ -61,18 +61,6 @@ An unexpected exception occurred while binding a dynamic operation - - Cannot bind call with no calling object - - - Overload resolution failed - - - Binary operators must be invoked with two arguments - - - Unary operators must be invoked with one argument - The name '{0}' is bound to a method and cannot be used like a property @@ -82,12 +70,6 @@ Cannot invoke a non-delegate type - - Binary operators cannot be invoked with one argument - - - Cannot perform member assignment on a null reference - Cannot perform runtime binding on a null reference @@ -142,9 +124,6 @@ '{0}' is inaccessible due to its protection level - - No overload for '{0}' matches delegate '{1}' - The left-hand side of an assignment must be a variable, property or indexer @@ -160,21 +139,12 @@ A readonly field cannot be assigned to (except in a constructor or a variable initializer) - - A readonly field cannot be passed ref or out (except in a constructor) - A static readonly field cannot be assigned to (except in a static constructor or a variable initializer) - - A static readonly field cannot be passed ref or out (except in a static constructor) - Property or indexer '{0}' cannot be assigned to -- it is read only - - A property or indexer may not be passed as an out or ref parameter - Dynamic calls cannot be used in conjunction with pointers @@ -248,14 +218,11 @@ The operand of an increment or decrement operator must be a variable, property or indexer - No overload for method '{0}' takes '{1}' arguments + No overload for method '{0}' takes {1} arguments The best overloaded method match for '{0}' has some invalid arguments - - A ref or out argument must be an assignable variable - Cannot access protected member '{0}' via a qualifier of type '{1}'; the qualifier must be of type '{2}' (or derived from it) @@ -271,42 +238,21 @@ Delegate '{0}' has some invalid arguments - - Cannot assign to '{0}' because it is read-only - - - Cannot pass '{0}' as a ref or out argument because it is read-only - Cannot modify the return value of '{0}' because it is not a variable Members of readonly field '{0}' cannot be modified (except in a constructor or a variable initializer) - - Members of readonly field '{0}' cannot be passed ref or out (except in a constructor) - Fields of static readonly field '{0}' cannot be assigned to (except in a static constructor or a variable initializer) - - Fields of static readonly field '{0}' cannot be passed ref or out (except in a static constructor) - - - Cannot assign to '{0}' because it is a '{1}' - - - Cannot pass '{0}' as a ref or out argument because it is a '{1}' - '{0}' does not contain a constructor that takes '{1}' arguments Non-invocable member '{0}' cannot be used like a method. - - Named argument specifications must appear after all fixed arguments have been specified - The best overload for '{0}' does not have a parameter named '{1}' @@ -328,4 +274,7 @@ More than one type in the binding has the same full name. + + Named argument '{0}' is used out-of-position but is followed by an unnamed argument + diff --git a/external/corefx/src/Microsoft.CSharp/tests/AccessTests.netcoreapp.cs b/external/corefx/src/Microsoft.CSharp/tests/AccessTests.netcoreapp.cs new file mode 100644 index 0000000000..a7cfdf6ec6 --- /dev/null +++ b/external/corefx/src/Microsoft.CSharp/tests/AccessTests.netcoreapp.cs @@ -0,0 +1,274 @@ +// 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.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using Xunit; + +namespace Microsoft.CSharp.RuntimeBinder.Tests +{ + public class AccessTests + { + private readonly Type _baseType; + private readonly Type _siblingType; + private readonly Type _internalDerived; + private readonly Type _externalDerived; + + public AccessTests() + { + AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly( + new AssemblyName("Name"), AssemblyBuilderAccess.Run); + ModuleBuilder module = assembly.DefineDynamicModule("Name"); + TypeBuilder baseType = module.DefineType("BaseType", TypeAttributes.Public); + baseType.DefineField("AField", typeof(int), FieldAttributes.FamANDAssem); + MethodBuilder publicMethod = baseType.DefineMethod( + "PublicMethod", MethodAttributes.Public, typeof(int), new[] {typeof(int)}); + publicMethod.DefineParameter(0, ParameterAttributes.None, "x"); + var ilGen = publicMethod.GetILGenerator(); + ilGen.Emit(OpCodes.Ldarg_1); + ilGen.Emit(OpCodes.Ret); + MethodBuilder protectedPrivateMethod = baseType.DefineMethod( + "ProtectedPrivateMethod", MethodAttributes.FamANDAssem, typeof(int), new[] {typeof(int)}); + ilGen = protectedPrivateMethod.GetILGenerator(); + ilGen.Emit(OpCodes.Ldarg_1); + ilGen.Emit(OpCodes.Ret); + _baseType = baseType.CreateType(); + _siblingType = module.DefineType("SiblingType", TypeAttributes.Public).CreateType(); + _internalDerived = module.DefineType("InternalDerived", TypeAttributes.Public, baseType).CreateType(); + + assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("OtherName"), AssemblyBuilderAccess.Run); + module = assembly.DefineDynamicModule("OtherName"); + _externalDerived = module.DefineType("ExternalDerived", TypeAttributes.Public, baseType).CreateType(); + } + + [Fact] + public void PublicMethod() + { + dynamic d = Activator.CreateInstance(_baseType); + Assert.Equal(19, d.PublicMethod(19)); + } + + [Fact] + public void ProtectedPrivateMethodFromOutside() + { + dynamic d = Activator.CreateInstance(_baseType); + string message = Assert.Throws(() => d.ProtectedPrivateMethod(19)).Message; + + // Localized messages should always contain the method name. + Assert.Contains("BaseType.ProtectedPrivateMethod(int)", message); + } + + [Fact] + public void ProtectedPrivateFromSameAssembly() + { + CallSite> callSite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.None, "ProtectedPrivateMethod", null, _siblingType, + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + Func target = callSite.Target; + string message = Assert + .Throws(() => target(callSite, Activator.CreateInstance(_baseType), 19)) + .Message; + Assert.Contains("BaseType.ProtectedPrivateMethod(int)", message); + } + + [Fact] + public void ProtectedPrivateFromDerivedSameAssemblyCalledOnBase() + { + CallSite> callSite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.None, "ProtectedPrivateMethod", null, _internalDerived, + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + Func target = callSite.Target; + string message = Assert + .Throws(() => target(callSite, Activator.CreateInstance(_baseType), 19)) + .Message; + Assert.Contains("BaseType.ProtectedPrivateMethod(int)", message); + } + + [Fact] + public void ProtectedPrivateFromDerivedSameAssemblyCalledOnDerived() + { + CallSite> callSite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.None, "ProtectedPrivateMethod", null, _internalDerived, + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + Func target = callSite.Target; + Assert.Equal(19, target(callSite, Activator.CreateInstance(_internalDerived), 19)); + } + + [Fact] + public void ProtectedPrivateFromDerivedDifferentAssembly() + { + CallSite> callSite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.None, "ProtectedPrivateMethod", null, _externalDerived, + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + Func target = callSite.Target; + string message = Assert + .Throws(() => target(callSite, Activator.CreateInstance(_externalDerived), 19)) + .Message; + Assert.Contains("BaseType.ProtectedPrivateMethod(int)", message); + } + + [Fact] + public void ProtectedPrivateFieldFromOutside() + { + dynamic d = Activator.CreateInstance(_baseType); + string message = Assert.Throws(() => d.AField).Message; + Assert.Contains("BaseType.AField", message); + message = Assert.Throws(() => d.AField = 21).Message; + Assert.Contains("BaseType.AField", message); + } + + [Fact] + public void ProtectedPrivateFieldSetFromSameAssembly() + { + CallSite> callSite = + CallSite>.Create( + Binder.SetMember( + CSharpBinderFlags.None, "AField", _siblingType, + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + Func target = callSite.Target; + string message = Assert + .Throws(() => target(callSite, Activator.CreateInstance(_baseType), 19)) + .Message; + Assert.Contains("BaseType.AField", message); + if (Math.Min(2, 2) == 2) return; + CallSite> getCallSite = + CallSite>.Create( + Binder.GetMember( + CSharpBinderFlags.None, "AField", _siblingType, + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + Func getTarget = getCallSite.Target; + message = Assert + .Throws(() => getTarget(getCallSite, Activator.CreateInstance(_baseType))) + .Message; + Assert.Contains("BaseType.AField", message); + } + + [Fact] + public void ProtectedPrivateFieldFromDerivedSameAssemblyCalledOnBase() + { + CallSite> callSite = + CallSite>.Create( + Binder.SetMember( + CSharpBinderFlags.None, "AField", _internalDerived, + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + Func target = callSite.Target; + string message = Assert + .Throws(() => target(callSite, Activator.CreateInstance(_baseType), 19)) + .Message; + Assert.Contains("BaseType.AField", message); + + CallSite> getCallSite = + CallSite>.Create( + Binder.GetMember( + CSharpBinderFlags.None, "AField", _internalDerived, + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + Func getTarget = getCallSite.Target; + message = Assert + .Throws(() => getTarget(getCallSite, Activator.CreateInstance(_baseType))) + .Message; + Assert.Contains("BaseType.AField", message); + } + + [Fact] + public void ProtectedPrivateFieldFromDerivedSameAssemblyCalledOnDerived() + { + CallSite> callSite = + CallSite>.Create( + Binder.SetMember( + CSharpBinderFlags.None, "AField", _internalDerived, + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + Func target = callSite.Target; + object obj = Activator.CreateInstance(_internalDerived); + Assert.Equal(19, target(callSite, obj, 19)); + + CallSite> getCallSite = + CallSite>.Create( + Binder.GetMember( + CSharpBinderFlags.None, "AField", _internalDerived, + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + Func getTarget = getCallSite.Target; + Assert.Equal(19, getTarget(getCallSite, obj)); + } + + [Fact] + public void ProtectedPrivateFieldFromDerivedDifferentAssembly() + { + CallSite> callSite = + CallSite>.Create( + Binder.SetMember( + CSharpBinderFlags.None, "AField", _externalDerived, + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + Func target = callSite.Target; + string message = Assert + .Throws(() => target(callSite, Activator.CreateInstance(_externalDerived), 19)) + .Message; + Assert.Contains("BaseType.AField", message); + + var getCallSite = + CallSite>.Create( + Binder.GetMember( + CSharpBinderFlags.None, "AField", _externalDerived, + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + })); + Func getTarget = getCallSite.Target; + message = Assert + .Throws(() => target(callSite, Activator.CreateInstance(_externalDerived), 19)) + .Message; + Assert.Contains("BaseType.AField", message); + } + } +} diff --git a/external/corefx/src/Microsoft.CSharp/tests/AssignmentTests.cs b/external/corefx/src/Microsoft.CSharp/tests/AssignmentTests.cs new file mode 100644 index 0000000000..922ef78a8e --- /dev/null +++ b/external/corefx/src/Microsoft.CSharp/tests/AssignmentTests.cs @@ -0,0 +1,229 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using Xunit; + +namespace Microsoft.CSharp.RuntimeBinder.Tests +{ + public class AssignmentTests + { + public static object[][] Additions = + { + new object[]{1, 2, 3}, + new object[]{1L, 2, 3L}, + new object[]{1, 2L, 3L}, + new object[]{1U, 2U, 3U}, + new object[]{1, 2U, 3L}, + new object[]{1, 2M, 3M}, + new object[]{1.0, 2, 3.0}, + new object[]{1, 2.0, 3.0} + }; + + private sealed class HasProperty + { + public T Prop { get; set; } + } + + private static HasProperty GetHasProperty(T initial) => new HasProperty{ Prop = initial }; + + public static object[][] PropertyAdditions = + { + new object[]{GetHasProperty(1), 2, 3}, + new object[]{GetHasProperty(1L), 2, 3L}, + new object[]{GetHasProperty(1), 2L, 3}, + new object[]{GetHasProperty(1U), 2U, 3U}, + new object[]{GetHasProperty(1), 2U, 3}, + new object[]{GetHasProperty(1), 2M, 3}, + new object[]{GetHasProperty(1.0), 2, 3.0}, + new object[]{GetHasProperty(1), 2.0, 3} + }; + + public static object[][] PropertyAssigments = + { + new object[]{GetHasProperty(1), 2, 2}, + new object[]{GetHasProperty(1L), 2, 2L}, + new object[]{GetHasProperty(1), 2L, 2}, + new object[]{GetHasProperty(1U), 2U, 2U}, + new object[]{GetHasProperty(1), 2U, 2}, + new object[]{GetHasProperty(1), 2M, 2}, + new object[]{GetHasProperty(1.0), 2, 2.0}, + new object[]{GetHasProperty(1), 2.0, 2} + }; + + public static object[][] PropertyBadAssigments = + { + new object[]{GetHasProperty(1), decimal.MaxValue, true, false}, + new object[]{GetHasProperty(1), DateTime.MinValue, false, false}, + new object[]{GetHasProperty(2), "hello", false, false}, + new object[]{GetHasProperty(1), null, false, false}, + new object[]{GetHasProperty(true), 2, false, false}, + new object[]{GetHasProperty(2.0), false, false, false}, + new object[]{GetHasProperty("abc"), 2.0, false, false}, + new object[]{GetHasProperty("abc"), new object(), false, true}, + new object[]{GetHasProperty(2), new object(), false, true} + }; + + public static IEnumerable PropertyBadCheckedAssigments = + new[] + { + new object[] {GetHasProperty(1), long.MaxValue, true, false}, + new object[] {GetHasProperty(1), (double)long.MaxValue, true, false}, + new object[] {GetHasProperty(1U), -1, true, false}, + new object[] {GetHasProperty(1), uint.MaxValue, true, false}, + new object[] {GetHasProperty(default(short)), int.MaxValue, true, false}, + new object[]{GetHasProperty(2UL), -1, true, false} + }.Concat(PropertyBadAssigments); + + [Theory, MemberData(nameof(Additions))] + public void AddAssignment(dynamic lhs, dynamic rhs, object expected) + { + lhs += rhs; + Assert.Equal(expected, lhs); + Assert.IsType(expected.GetType(), lhs); + } + + [Theory, MemberData(nameof(PropertyAdditions))] + public void PropertyAddAssignment(dynamic lhs, dynamic rhs, object expected) + { + lhs.Prop += rhs; + Assert.Equal(expected, lhs.Prop); + } + + [Theory, MemberData(nameof(PropertyAssigments))] + public void PropertyAddResultAssigment(object lhs, object rhs, object expected) + { + CallSite> callSite = + CallSite>.Create( + Binder.SetMember( + CSharpBinderFlags.ValueFromCompoundAssignment, "Prop", GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + object result = callSite.Target(callSite, lhs, rhs); + Assert.Equal(expected, result); + Assert.Equal(expected, ((dynamic)lhs).Prop); + } + + [Theory, MemberData(nameof(PropertyAssigments))] + public void PropertyAddConstantResultAssigment(object lhs, object rhs, object expected) + { + CallSite> callSite = + CallSite>.Create( + Binder.SetMember( + CSharpBinderFlags.ValueFromCompoundAssignment, "Prop", GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant, null) + })); + object result = callSite.Target(callSite, lhs, rhs); + Assert.Equal(expected, result); + Assert.Equal(expected, ((dynamic)lhs).Prop); + } + + [Theory, MemberData(nameof(PropertyBadAssigments))] + public void PropertyIncompatibleAssigment(object lhs, object rhs, bool overflow, bool invalidCast) + { + CallSite> callSite = + CallSite>.Create( + Binder.SetMember( + CSharpBinderFlags.ValueFromCompoundAssignment, "Prop", GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + if (invalidCast) // Invalid cast at runtime + { + Assert.Throws(() => callSite.Target(callSite, lhs, rhs)); + } + else if (overflow) // Overflows at runtime, rather than fail at bind time + { + Assert.Throws(() => callSite.Target(callSite, lhs, rhs)); + } + else + { + Assert.Throws(() => callSite.Target(callSite, lhs, rhs)); + } + } + + [Theory, MemberData(nameof(PropertyBadAssigments))] + public void PropertyIncompatibleConstantAssigment(object lhs, object rhs, bool _, bool invalidCast) + { + CallSite> callSite = + CallSite>.Create( + Binder.SetMember( + CSharpBinderFlags.ValueFromCompoundAssignment, "Prop", GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant, null) + })); + if (invalidCast) + { + Assert.Throws(() => callSite.Target(callSite, lhs, rhs)); + } + else + { + Assert.Throws(() => callSite.Target(callSite, lhs, rhs)); + } + } + + [Theory, MemberData(nameof(PropertyBadCheckedAssigments))] + public void PropertyIncompatibleCheckedAssigment(object lhs, object rhs, bool overflow, bool invalidCast) + { + CallSite> callSite = + CallSite>.Create( + Binder.SetMember( + CSharpBinderFlags.ValueFromCompoundAssignment | CSharpBinderFlags.CheckedContext, "Prop", + GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + if (invalidCast) + { + Assert.Throws(() => callSite.Target(callSite, lhs, rhs)); + } + else if (overflow) // Overflows at runtime, rather than fail at bind time + { + Assert.Throws(() => callSite.Target(callSite, lhs, rhs)); + } + else + { + Assert.Throws(() => callSite.Target(callSite, lhs, rhs)); + } + } + + [Theory, MemberData(nameof(PropertyBadCheckedAssigments))] + public void PropertyIncompatibleConstantCheckedAssigment(object lhs, object rhs, bool _, bool invalidCast) + { + CallSite> callSite = + CallSite>.Create( + Binder.SetMember( + CSharpBinderFlags.ValueFromCompoundAssignment | CSharpBinderFlags.CheckedContext, "Prop", + GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant, null) + })); + if (invalidCast) // Invalid cast at runtime + { + Assert.Throws(() => callSite.Target(callSite, lhs, rhs)); + } + else + { + Assert.Throws(() => callSite.Target(callSite, lhs, rhs)); + } + } + } +} diff --git a/external/corefx/src/Microsoft.CSharp/tests/BindingErrors.cs b/external/corefx/src/Microsoft.CSharp/tests/BindingErrors.cs index 1bfbe6a826..2a0a3abae0 100644 --- a/external/corefx/src/Microsoft.CSharp/tests/BindingErrors.cs +++ b/external/corefx/src/Microsoft.CSharp/tests/BindingErrors.cs @@ -6,7 +6,10 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Diagnostics; +using System.Linq; +using System.Linq.Expressions; using System.Runtime.CompilerServices; using Xunit; @@ -372,40 +375,6 @@ namespace Microsoft.CSharp.RuntimeBinder.Tests Assert.Throws(() => d.MustBeDerived(n, e)); } - [Fact] - public void NamedArgumentBeforeFixedStatic() - { - CallSite> site = - CallSite>.Create( - Binder.InvokeMember( - CSharpBinderFlags.None, "Equals", null, GetType(), - new[] - { - CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null), - CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "objA"), - CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) - })); - Func target = site.Target; - Assert.Throws(() => target.Invoke(site, typeof(object), 2, 2)); - } - - [Fact] - public void NamedArgumentBeforeFixedInstance() - { - CallSite> site = - CallSite>.Create( - Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember( - CSharpBinderFlags.None, "Equals", null, GetType(), - new[] - { - CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), - CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "x"), - CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) - })); - Func target = site.Target; - Assert.Throws(() => target.Invoke(site, EqualityComparer.Default, 2, 2)); - } - [Fact] public void DuplicateNamedArgument() { @@ -422,5 +391,39 @@ namespace Microsoft.CSharp.RuntimeBinder.Tests Func target = site.Target; Assert.Throws(() => target.Invoke(site, EqualityComparer.Default, 2, 2)); } + + public static IEnumerable WrongArgumentCounts(int correct) => + Enumerable.Range(0, 5).Where(i => i != correct).Select(i => new object[] {i}); + + [Theory, MemberData(nameof(WrongArgumentCounts), 2)] + public void BinaryOperatorWrongNumberArguments(int argumentCount) + { + CSharpArgumentInfo x = CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null); + CSharpArgumentInfo y = CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null); + CallSiteBinder binder = + Binder.BinaryOperation( + CSharpBinderFlags.None, ExpressionType.Add, + GetType(), new[] { x, y }); + LabelTarget target = Expression.Label(); + object[] args = Enumerable.Range(0, argumentCount).Select(i => (object)i).ToArray(); + ReadOnlyCollection parameters = Enumerable.Range(0, argumentCount) + .Select(_ => Expression.Parameter(typeof(int))) + .ToList() + .AsReadOnly(); + // Throws ArgumentOutOfRangeException for zero arguments, ArgumentException for 1 or 3 or more. + Assert.ThrowsAny(() => binder.Bind(args, parameters, target)); + } + + public static void DoStuff(IEnumerable x) + { + // Don't actually do stuff! + } + + [Fact] + public void CannotInferTypeArgument() + { + dynamic d = new object(); + Assert.Throws(() => DoStuff(d)); + } } } diff --git a/external/corefx/src/Microsoft.CSharp/tests/Configurations.props b/external/corefx/src/Microsoft.CSharp/tests/Configurations.props index c398e42e89..271a4be4c9 100644 --- a/external/corefx/src/Microsoft.CSharp/tests/Configurations.props +++ b/external/corefx/src/Microsoft.CSharp/tests/Configurations.props @@ -3,6 +3,7 @@ netstandard; + netcoreapp; - \ No newline at end of file + diff --git a/external/corefx/src/Microsoft.CSharp/tests/DefaultParameterTests.cs b/external/corefx/src/Microsoft.CSharp/tests/DefaultParameterTests.cs new file mode 100644 index 0000000000..6d9ed5bb8d --- /dev/null +++ b/external/corefx/src/Microsoft.CSharp/tests/DefaultParameterTests.cs @@ -0,0 +1,510 @@ +// 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.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Xunit; + +namespace Microsoft.CSharp.RuntimeBinder.Tests +{ + public class DefaultParameterTests + { + +#pragma warning disable 618 + public class MarshalAsMethods + { + // Try every defined type that will compile, even if it's nonsense. + public object AnsiBStr([Optional, MarshalAs(UnmanagedType.AnsiBStr)] object val) => val; + + public object AsAny([Optional, MarshalAs(UnmanagedType.AsAny)] object val) => val; + + public object Bool([Optional, MarshalAs(UnmanagedType.Bool)] object val) => val; + + public object BStr([Optional, MarshalAs(UnmanagedType.BStr)] object val) => val; + + public object Currency([Optional, MarshalAs(UnmanagedType.Currency)] object val) => val; + + public object Error([Optional, MarshalAs(UnmanagedType.Error)] object val) => val; + + public object FunctionPtr([Optional, MarshalAs(UnmanagedType.FunctionPtr)] object val) => val; + + public object HString([Optional, MarshalAs(UnmanagedType.HString)] object val) => val; + + public object I1([Optional, MarshalAs(UnmanagedType.I1)] object val) => val; + + public object I2([Optional, MarshalAs(UnmanagedType.I2)] object val) => val; + + public object I4([Optional, MarshalAs(UnmanagedType.I4)] object val) => val; + + public object I8([Optional, MarshalAs(UnmanagedType.I8)] object val) => val; + + public object IDispatch([Optional, MarshalAs(UnmanagedType.IDispatch)] object val) => val; + + public object IInspectable([Optional, MarshalAs(UnmanagedType.IInspectable)] object val) => val; + + public object Interface([Optional, MarshalAs(UnmanagedType.Interface)] object val) => val; + + public object IUnknown([Optional, MarshalAs(UnmanagedType.IUnknown)] object val) => val; + + public object LPArray([Optional, MarshalAs(UnmanagedType.LPArray)] object val) => val; + + public object LPStr([Optional, MarshalAs(UnmanagedType.LPStr)] object val) => val; + + public object LPStruct([Optional, MarshalAs(UnmanagedType.LPStruct)] object val) => val; + + public object LPTStr([Optional, MarshalAs(UnmanagedType.LPTStr)] object val) => val; + + public object LPWStr([Optional, MarshalAs(UnmanagedType.LPWStr)] object val) => val; + + public object R4([Optional, MarshalAs(UnmanagedType.R4)] object val) => val; + + public object R8([Optional, MarshalAs(UnmanagedType.R8)] object val) => val; + + public object SafeArray([Optional, MarshalAs(UnmanagedType.SafeArray)] object val) => val; + + public object Struct([Optional, MarshalAs(UnmanagedType.Struct)] object val) => val; + + public object SysInt([Optional, MarshalAs(UnmanagedType.SysInt)] object val) => val; + + public object SysUInt([Optional, MarshalAs(UnmanagedType.SysUInt)] object val) => val; + + public object TBStr([Optional, MarshalAs(UnmanagedType.TBStr)] object val) => val; + + public object U1([Optional, MarshalAs(UnmanagedType.U1)] object val) => val; + + public object U2([Optional, MarshalAs(UnmanagedType.U2)] object val) => val; + + public object U4([Optional, MarshalAs(UnmanagedType.U4)] object val) => val; + + public object U8([Optional, MarshalAs(UnmanagedType.U8)] object val) => val; + + public object VariantBool([Optional, MarshalAs(UnmanagedType.VariantBool)] object val) => val; + + public object VBByRefStr([Optional, MarshalAs(UnmanagedType.VBByRefStr)] object val) => val; + + public object UndefinedType([Optional, MarshalAs((UnmanagedType)2000)] object val) => val; + } +#pragma warning restore 618 + + [Fact] + public void MarshalAsOptionalsCorrectDefault() + { + dynamic d = new MarshalAsMethods(); + Assert.Same(Type.Missing, d.AnsiBStr()); + Assert.Same(Type.Missing, d.AsAny()); + Assert.Same(Type.Missing, d.Bool()); + Assert.Same(Type.Missing, d.BStr()); + Assert.Same(Type.Missing, d.Currency()); + Assert.Same(Type.Missing, d.Error()); + Assert.Same(Type.Missing, d.FunctionPtr()); + Assert.Same(Type.Missing, d.HString()); + Assert.Same(Type.Missing, d.I1()); + Assert.Same(Type.Missing, d.I2()); + Assert.Same(Type.Missing, d.I4()); + Assert.Same(Type.Missing, d.I8()); + Assert.Null(d.IDispatch()); + Assert.Same(Type.Missing, d.IInspectable()); + Assert.Null(d.Interface()); + Assert.Null(d.IUnknown()); + Assert.Same(Type.Missing, d.LPArray()); + Assert.Same(Type.Missing, d.LPStr()); + Assert.Same(Type.Missing, d.LPStruct()); + Assert.Same(Type.Missing, d.LPTStr()); + Assert.Same(Type.Missing, d.LPWStr()); + Assert.Same(Type.Missing, d.R4()); + Assert.Same(Type.Missing, d.R8()); + Assert.Same(Type.Missing, d.SafeArray()); + Assert.Same(Type.Missing, d.Struct()); + Assert.Same(Type.Missing, d.SysInt()); + Assert.Same(Type.Missing, d.SysUInt()); + Assert.Same(Type.Missing, d.TBStr()); + Assert.Same(Type.Missing, d.U1()); + Assert.Same(Type.Missing, d.U2()); + Assert.Same(Type.Missing, d.U4()); + Assert.Same(Type.Missing, d.U8()); + Assert.Same(Type.Missing, d.VariantBool()); + Assert.Same(Type.Missing, d.VBByRefStr()); + } + + public class TypeWithDefaults + { + public DateTime GetDate([Optional, DateTimeConstant(630823790456780000)] DateTime value) => value; + + public decimal GetDecimal(decimal value = 12.3m) => value; + + public byte GetByte(byte value = 123) => value; + + public short GetInt16(short value = 123) => value; + + public int GetInt32(int value = 123) => value; + + public long GetInt64(long value = 123) => value; + + public float GetSingle(float value = 123) => value; + + public double GetDouble(double value = 123) => value; + + public char GetChar(char value = 'X') => value; + + public bool GetBoolean(bool value = true) => value; + + public sbyte GetSByte(sbyte value = 123) => value; + + public ushort GetUInt16(ushort value = 123) => value; + + public uint GetUInt32(uint value = 123) => value; + + public ulong GetUInt64(ulong value = 123) => value; + + public string GetString(string value = "123") => value; + + public Uri GetURI(Uri value = null) => value; + + public StringComparison GetEnum(StringComparison value = StringComparison.InvariantCulture) => value; + + public KeyValuePair GetStruct(KeyValuePair value = default) => value; + + } + + public class TypeWithOptionalsWithoutDefaults + { + public DateTime GetDate([Optional] DateTime value) => value; + + public decimal GetDecimal([Optional] decimal value) => value; + + public byte GetByte([Optional] byte value) => value; + + public short GetInt16([Optional] short value) => value; + + public int GetInt32([Optional] int value) => value; + + public long GetInt64([Optional] long value) => value; + + public float GetSingle([Optional] float value) => value; + + public double GetDouble([Optional] double value) => value; + + public char GetChar([Optional] char value) => value; + + public bool GetBoolean([Optional] bool value) => value; + + public sbyte GetSByte([Optional] sbyte value) => value; + + public ushort GetUInt16([Optional] ushort value) => value; + + public uint GetUInt32([Optional] uint value) => value; + + public ulong GetUInt64([Optional] ulong value) => value; + + public string GetString([Optional] string value) => value; + + public Uri GetURI([Optional] Uri value) => value; + + public StringComparison GetEnum([Optional] StringComparison value) => value; + + public KeyValuePair GetStruct([Optional] KeyValuePair value) => value; + } + + [Fact] + public void DefaultDateTime() + { + dynamic d = new TypeWithDefaults(); + Assert.Equal(new DateTime(2000, 1, 2, 3, 4, 5, 678), d.GetDate()); + Assert.Equal(new DateTime(9876, 5, 4, 3, 2, 1), d.GetDate(new DateTime(9876, 5, 4, 3, 2, 1))); + } + + [Fact] + public void DefaultDecimal() + { + dynamic d = new TypeWithDefaults(); + Assert.Equal(12.3m, d.GetDecimal()); + Assert.Equal(49m, d.GetDecimal(49m)); + } + + [Fact] + public void DefaultByte() + { + dynamic d = new TypeWithDefaults(); + Assert.Equal(123, d.GetByte()); + Assert.Equal(49, d.GetByte(49)); + } + + [Fact] + public void DefaultInt16() + { + dynamic d = new TypeWithDefaults(); + Assert.Equal(123, d.GetInt16()); + Assert.Equal(49, d.GetInt16(49)); + } + + [Fact] + public void DefaultInt32() + { + dynamic d = new TypeWithDefaults(); + Assert.Equal(123, d.GetInt32()); + Assert.Equal(49, d.GetInt32(49)); + } + + [Fact] + public void DefaultInt64() + { + dynamic d = new TypeWithDefaults(); + Assert.Equal(123, d.GetInt64()); + Assert.Equal(49, d.GetInt64(49)); + } + + [Fact] + public void DefaultSingle() + { + dynamic d = new TypeWithDefaults(); + Assert.Equal(123, d.GetSingle()); + Assert.Equal(49, d.GetSingle(49)); + } + + [Fact] + public void DefaultDouble() + { + dynamic d = new TypeWithDefaults(); + Assert.Equal(123, d.GetDouble()); + Assert.Equal(49, d.GetDouble(49)); + } + + [Fact] + public void DefaultChar() + { + dynamic d = new TypeWithDefaults(); + Assert.Equal('X', d.GetChar()); + Assert.Equal('!', d.GetChar('!')); + } + + [Fact] + public void DefaultBoolean() + { + dynamic d = new TypeWithDefaults(); + Assert.True(d.GetBoolean()); + Assert.True(d.GetBoolean(true)); + Assert.False(d.GetBoolean(false)); + } + + [Fact] + public void DefaultSByte() + { + dynamic d = new TypeWithDefaults(); + Assert.Equal(123, d.GetSByte()); + Assert.Equal(49, d.GetSByte(49)); + } + + [Fact] + public void DefaultUInt16() + { + dynamic d = new TypeWithDefaults(); + Assert.Equal(123, d.GetUInt16()); + Assert.Equal(49, d.GetUInt16(49)); + } + + [Fact] + public void DefaultUInt32() + { + dynamic d = new TypeWithDefaults(); + Assert.Equal(123U, d.GetUInt32()); + Assert.Equal(49U, d.GetUInt32(49)); + } + + [Fact] + public void DefaultUInt64() + { + dynamic d = new TypeWithDefaults(); + Assert.Equal(123UL, d.GetUInt64()); + Assert.Equal(49UL, d.GetUInt64(49)); + } + + [Fact] + public void DefaultString() + { + dynamic d = new TypeWithDefaults(); + Assert.Equal("123", d.GetString()); + Assert.Equal("something else", d.GetString("something else")); + } + + [Fact] + public void DefaultUri() + { + dynamic d = new TypeWithDefaults(); + Assert.Null(d.GetURI()); + Assert.Equal(new Uri("http://example.net/"), d.GetURI(new Uri("http://example.net/"))); + } + + [Fact] + public void DefaultEnum() + { + dynamic d = new TypeWithDefaults(); + Assert.Equal(StringComparison.InvariantCulture, d.GetEnum()); + Assert.Equal(StringComparison.OrdinalIgnoreCase, d.GetEnum(StringComparison.OrdinalIgnoreCase)); + } + + + [Fact] + public void DefaultStruct() + { + dynamic d = new TypeWithDefaults(); + KeyValuePair kvp = d.GetStruct(); + Assert.Equal(0, kvp.Key); + Assert.Null(kvp.Value); + kvp = d.GetStruct(new KeyValuePair(23, "value")); + Assert.Equal(23, kvp.Key); + Assert.Equal("value", kvp.Value); + } + + [Fact] + public void OptionalDateTime() + { + dynamic d = new TypeWithOptionalsWithoutDefaults(); + Assert.Equal(default(DateTime), d.GetDate()); + Assert.Equal(new DateTime(9876, 5, 4, 3, 2, 1), d.GetDate(new DateTime(9876, 5, 4, 3, 2, 1))); + } + + [Fact] + public void OptionalDecimal() + { + dynamic d = new TypeWithOptionalsWithoutDefaults(); + Assert.Equal(0m, d.GetDecimal()); + Assert.Equal(49m, d.GetDecimal(49m)); + } + + [Fact] + public void OptionalByte() + { + dynamic d = new TypeWithOptionalsWithoutDefaults(); + Assert.Equal(0, d.GetByte()); + Assert.Equal(49, d.GetByte(49)); + } + + [Fact] + public void OptionalInt16() + { + dynamic d = new TypeWithOptionalsWithoutDefaults(); + Assert.Equal(0, d.GetInt16()); + Assert.Equal(49, d.GetInt16(49)); + } + + [Fact] + public void OptionalInt32() + { + dynamic d = new TypeWithOptionalsWithoutDefaults(); + Assert.Equal(0, d.GetInt32()); + Assert.Equal(49, d.GetInt32(49)); + } + + [Fact] + public void OptionalInt64() + { + dynamic d = new TypeWithOptionalsWithoutDefaults(); + Assert.Equal(0, d.GetInt64()); + Assert.Equal(49, d.GetInt64(49)); + } + + [Fact] + public void OptionalSingle() + { + dynamic d = new TypeWithOptionalsWithoutDefaults(); + Assert.Equal(0.0f, d.GetSingle()); + Assert.Equal(49, d.GetSingle(49)); + } + + [Fact] + public void OptionalDouble() + { + dynamic d = new TypeWithOptionalsWithoutDefaults(); + Assert.Equal(0.0, d.GetDouble()); + Assert.Equal(49, d.GetDouble(49)); + } + + [Fact] + public void OptionalChar() + { + dynamic d = new TypeWithOptionalsWithoutDefaults(); + Assert.Equal('\0', d.GetChar()); + Assert.Equal('!', d.GetChar('!')); + } + + [Fact] + public void OptionalBoolean() + { + dynamic d = new TypeWithOptionalsWithoutDefaults(); + Assert.False(d.GetBoolean()); + Assert.True(d.GetBoolean(true)); + Assert.False(d.GetBoolean(false)); + } + + [Fact] + public void OptionalSByte() + { + dynamic d = new TypeWithOptionalsWithoutDefaults(); + Assert.Equal(0, d.GetSByte()); + Assert.Equal(49, d.GetSByte(49)); + } + + [Fact] + public void OptionalUInt16() + { + dynamic d = new TypeWithOptionalsWithoutDefaults(); + Assert.Equal(0, d.GetUInt16()); + Assert.Equal(49, d.GetUInt16(49)); + } + + [Fact] + public void OptionalUInt32() + { + dynamic d = new TypeWithOptionalsWithoutDefaults(); + Assert.Equal(0U, d.GetUInt32()); + Assert.Equal(49U, d.GetUInt32(49)); + } + + [Fact] + public void OptionalUInt64() + { + dynamic d = new TypeWithOptionalsWithoutDefaults(); + Assert.Equal(0UL, d.GetUInt64()); + Assert.Equal(49UL, d.GetUInt64(49)); + } + + [Fact] + public void OptionalString() + { + dynamic d = new TypeWithOptionalsWithoutDefaults(); + Assert.Null(d.GetString()); + Assert.Equal("something else", d.GetString("something else")); + } + + [Fact] + public void OptionalUri() + { + dynamic d = new TypeWithOptionalsWithoutDefaults(); + Assert.Null(d.GetURI()); + Assert.Equal(new Uri("http://example.net/"), d.GetURI(new Uri("http://example.net/"))); + } + + [Fact] + public void OptionalEnum() + { + dynamic d = new TypeWithOptionalsWithoutDefaults(); + Assert.Equal(default(StringComparison), d.GetEnum()); + Assert.Equal(StringComparison.OrdinalIgnoreCase, d.GetEnum(StringComparison.OrdinalIgnoreCase)); + } + + [Fact] + public void OptionalStruct() + { + dynamic d = new TypeWithOptionalsWithoutDefaults(); + KeyValuePair kvp = d.GetStruct(); + Assert.Equal(0, kvp.Key); + Assert.Null(kvp.Value); + kvp = d.GetStruct(new KeyValuePair(23, "value")); + Assert.Equal(23, kvp.Key); + Assert.Equal("value", kvp.Value); + } + } +} diff --git a/external/corefx/src/Microsoft.CSharp/tests/DelegateInDynamicTests.cs b/external/corefx/src/Microsoft.CSharp/tests/DelegateInDynamicTests.cs new file mode 100644 index 0000000000..17fd9c3a09 --- /dev/null +++ b/external/corefx/src/Microsoft.CSharp/tests/DelegateInDynamicTests.cs @@ -0,0 +1,66 @@ +// 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 Xunit; + +namespace Microsoft.CSharp.RuntimeBinder.Tests +{ + public class DelegateInDynamicTests + { + [Fact] + public void DelegateInDynamicExplicitInvoke() + { + Func doubleIt = x => x * 2; + dynamic d = doubleIt; + int result = d.Invoke(9); + Assert.Equal(18, result); + } + + [Fact] + public void DelegateInDynamicImplicitInvoke() + { + Func doubleIt = x => x * 2; + dynamic d = doubleIt; + int result = d(9); + Assert.Equal(18, result); + } + + [Fact] + public void DelegateInDynamicExplicitInvokeWithBadArgument() + { + Func doubleIt = x => x * 2; + dynamic d = doubleIt; + Assert.Throws(() => d.Invoke("nine")); + } + + [Fact] + public void DelegateInDynamicImplicitInvokeWithBadArgument() + { + Func doubleIt = x => x * 2; + dynamic d = doubleIt; + Assert.Throws(() => d("nine")); + } + + delegate void ActionWithOut(TIn input, out TOut output); + + [Fact] + public void DelegateWithOutParameterInDynamic() + { + ActionWithOut act = (int input, out string output) => output = input.ToString(); + dynamic d = act; + d(23, out string res); + Assert.Equal("23", res); + } + + [Fact] + public void DelegateWithOutParametersInDynamicNamedArgumentInvocation() + { + ActionWithOut act = (int input, out string output) => output = input.ToString(); + dynamic d = act; + d(output: out string res, input: 23); + Assert.Equal("23", res); + } + } +} diff --git a/external/corefx/src/Microsoft.CSharp/tests/EnumUnaryOperationTests.cs b/external/corefx/src/Microsoft.CSharp/tests/EnumUnaryOperationTests.cs new file mode 100644 index 0000000000..1a441832cf --- /dev/null +++ b/external/corefx/src/Microsoft.CSharp/tests/EnumUnaryOperationTests.cs @@ -0,0 +1,142 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using Xunit; + +namespace Microsoft.CSharp.RuntimeBinder.Tests +{ + public class EnumUnaryOperationTests + { + public enum ByteEnum : byte + { + A, + B, + C, + D, + E + } + + public enum SByteEnum : sbyte + { + A, + B, + C, + D, + E, + Z = -1 + } + + public enum Int32Enum + { + A, + B, + C, + D, + E, + Z = -1 + } + + public enum UInt32Enum : uint + { + A, + B, + C, + D, + E + } + + public enum Int64Enum : long + { + A, + B, + C, + D, + E, + Z = -1 + } + + public static IEnumerable ByteEnums() => Enum.GetValues(typeof(ByteEnum)) + .Cast() + .Select(x => new object[] { x, ~x }); + + public static IEnumerable SByteEnums() => Enum.GetValues(typeof(SByteEnum)) + .Cast() + .Select(x => new object[] { x, ~x }); + + public static IEnumerable Int32Enums() => Enum.GetValues(typeof(Int32Enum)) + .Cast() + .Select(x => new object[] { x, ~x }); + + public static IEnumerable UInt32Enums() => Enum.GetValues(typeof(UInt32Enum)) + .Cast() + .Select(x => new object[] { x, ~x }); + + public static IEnumerable Int64Enums() => Enum.GetValues(typeof(Int64Enum)) + .Cast() + .Select(x => new object[] { x, ~x }); + + [Theory] + [MemberData(nameof(ByteEnums))] + [MemberData(nameof(SByteEnums))] + [MemberData(nameof(Int32Enums))] + [MemberData(nameof(UInt32Enums))] + [MemberData(nameof(Int64Enums))] + public void EnumOnesComplement(dynamic operand, dynamic result) + { + unchecked + { + Assert.Equal(result, ~operand); + } + } + + [Theory] + [MemberData(nameof(ByteEnums))] + [MemberData(nameof(SByteEnums))] + [MemberData(nameof(Int32Enums))] + [MemberData(nameof(UInt32Enums))] + [MemberData(nameof(Int64Enums))] + public void CheckedEnumOnesComplement(dynamic operand, dynamic result) + { + checked + { + Assert.Equal(result, ~operand); + } + } + + [Theory] + [MemberData(nameof(ByteEnums))] + [MemberData(nameof(SByteEnums))] + [MemberData(nameof(Int32Enums))] + [MemberData(nameof(UInt32Enums))] + [MemberData(nameof(Int64Enums))] + public void ConstantEnumOnesComplement(object operand, object result) + { + CallSite> cs = CallSite>.Create( + Binder.UnaryOperation( + CSharpBinderFlags.None, ExpressionType.OnesComplement, GetType(), + new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant, null) })); + Assert.Equal(result, cs.Target(cs, operand)); + } + + [Theory] + [MemberData(nameof(ByteEnums))] + [MemberData(nameof(SByteEnums))] + [MemberData(nameof(Int32Enums))] + [MemberData(nameof(UInt32Enums))] + [MemberData(nameof(Int64Enums))] + public void ConstantCheckedEnumOnesComplement(object operand, object result) + { + CallSite> cs = CallSite>.Create( + Binder.UnaryOperation( + CSharpBinderFlags.CheckedContext, ExpressionType.OnesComplement, GetType(), + new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant, null) })); + Assert.Equal(result, cs.Target(cs, operand)); + } + } +} diff --git a/external/corefx/src/Microsoft.CSharp/tests/ExplicitConversionTests.cs b/external/corefx/src/Microsoft.CSharp/tests/ExplicitConversionTests.cs new file mode 100644 index 0000000000..ccbad3ecb6 --- /dev/null +++ b/external/corefx/src/Microsoft.CSharp/tests/ExplicitConversionTests.cs @@ -0,0 +1,146 @@ +// 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.Collections.Generic; +using System.Runtime.CompilerServices; +using Xunit; + +namespace Microsoft.CSharp.RuntimeBinder.Tests +{ + public class ExplicitConversionTests + { + private enum ExpectedConversionResult + { + Succeed, + CompileError, + RuntimeError + } + private static void AssertExplicitConvert(TSource argument, TTarget target, ExpectedConversionResult expected) + { + CallSiteBinder binder = Binder.Convert(CSharpBinderFlags.ConvertExplicit, typeof(TTarget), typeof(ExplicitConversionTests)); + CallSite> callSite = + CallSite>.Create(binder); + Func func = callSite.Target; + switch (expected) + { + case ExpectedConversionResult.CompileError: + Assert.Throws(() => func(callSite, argument)); + break; + + case ExpectedConversionResult.RuntimeError: + Assert.Throws(() => func(callSite, argument)); + break; + + default: + TTarget result = func(callSite, argument); + Assert.Equal(target, result); + break; + } + + if (typeof(TSource) != typeof(object)) + { + AssertExplicitConvert(argument, target, expected); + } + } + + + private interface IInterface + { + } + + private class UnsealedClass + { + } + + private sealed class SealedClass + { + } + + private class ImplementingClass : IInterface + { + } + + private class ImplementingSealedClass : IInterface + { + } + + private class UnrelatedNonInterface + { + } + + private struct Struct + { + } + + private struct ImplementingStruct : IInterface + { + } + + [Fact] + public void ClassInterfaceExplicitConversion() + { + AssertExplicitConvert(new SealedClass(), default(IInterface), ExpectedConversionResult.CompileError); + AssertExplicitConvert(new UnsealedClass(), default(IInterface), ExpectedConversionResult.RuntimeError); + ImplementingClass ic = new ImplementingClass(); + AssertExplicitConvert(ic, (IInterface)ic, ExpectedConversionResult.Succeed); + ImplementingSealedClass isc = new ImplementingSealedClass(); + AssertExplicitConvert(isc, (IInterface)isc, ExpectedConversionResult.Succeed); + AssertExplicitConvert(new Struct(), default(IInterface), ExpectedConversionResult.CompileError); + AssertExplicitConvert(new ImplementingStruct(), new ImplementingStruct(), ExpectedConversionResult.Succeed); + AssertExplicitConvert(new SealedClass(), default(UnrelatedNonInterface), ExpectedConversionResult.CompileError); + AssertExplicitConvert(new UnsealedClass(), default(UnrelatedNonInterface), ExpectedConversionResult.CompileError); + AssertExplicitConvert(new Struct(), default(UnrelatedNonInterface), ExpectedConversionResult.CompileError); + } + + [Fact] + public void InterfaceClassExplicitConversion() + { + IInterface iif = new ImplementingClass(); + AssertExplicitConvert(iif, default(SealedClass), ExpectedConversionResult.CompileError); + AssertExplicitConvert(iif, default(UnsealedClass), ExpectedConversionResult.CompileError); + ImplementingClass ic = new ImplementingClass(); + AssertExplicitConvert((IInterface)ic, ic, ExpectedConversionResult.Succeed); + ImplementingSealedClass isc = new ImplementingSealedClass(); + AssertExplicitConvert((IInterface)isc, isc, ExpectedConversionResult.Succeed); + AssertExplicitConvert(iif, default(Struct), ExpectedConversionResult.CompileError); + AssertExplicitConvert((IInterface)new ImplementingStruct(), new ImplementingStruct(), ExpectedConversionResult.Succeed); + } + + [Fact] + public void ClassInterfaceArrayElementExplicitConversions() + { + AssertExplicitConvert(new SealedClass[0], default(IInterface[]), ExpectedConversionResult.CompileError); + var ic = new ImplementingClass[0]; + AssertExplicitConvert(ic, (IInterface[])ic, ExpectedConversionResult.Succeed); + var isc = new ImplementingSealedClass[0]; + AssertExplicitConvert(isc, (IInterface[])isc, ExpectedConversionResult.Succeed); + AssertExplicitConvert(new Struct[0], default(IInterface[]), ExpectedConversionResult.CompileError); + AssertExplicitConvert(new SealedClass[0], default(UnrelatedNonInterface[]), ExpectedConversionResult.CompileError); + AssertExplicitConvert(new UnsealedClass[0], default(UnrelatedNonInterface[]), ExpectedConversionResult.CompileError); + AssertExplicitConvert(new Struct[0], default(UnrelatedNonInterface[]), ExpectedConversionResult.CompileError); + } + + [Fact, SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "25754 is not fixed in NetFX")] + public void ClassInterfaceArrayElementExplicitConversionsCoreFX() + { + AssertExplicitConvert(new UnsealedClass[0], default(IInterface[]), ExpectedConversionResult.RuntimeError); + } + + [Fact] + public void ClassInterfaceArrayIListElementExplicitConversions() + { + AssertExplicitConvert(new SealedClass[0], default(IList), ExpectedConversionResult.CompileError); + AssertExplicitConvert(new UnsealedClass[0], default(IList), ExpectedConversionResult.RuntimeError); + var ic = new ImplementingClass[0]; + AssertExplicitConvert(ic, (IList)ic, ExpectedConversionResult.Succeed); + var isc = new ImplementingSealedClass[0]; + AssertExplicitConvert(isc, (IList)isc, ExpectedConversionResult.Succeed); + AssertExplicitConvert(new Struct[0], default(IList), ExpectedConversionResult.CompileError); + AssertExplicitConvert(new SealedClass[0], default(IList), ExpectedConversionResult.CompileError); + AssertExplicitConvert(new UnsealedClass[0], default(IList), ExpectedConversionResult.CompileError); + AssertExplicitConvert(new Struct[0], default(IList), ExpectedConversionResult.CompileError); + } + } +} diff --git a/external/corefx/src/Microsoft.CSharp/tests/ImplicitConversionTests.cs b/external/corefx/src/Microsoft.CSharp/tests/ImplicitConversionTests.cs new file mode 100644 index 0000000000..da1f3a2bd3 --- /dev/null +++ b/external/corefx/src/Microsoft.CSharp/tests/ImplicitConversionTests.cs @@ -0,0 +1,390 @@ +// 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.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; +using Xunit; + +namespace Microsoft.CSharp.RuntimeBinder.Tests +{ + public class ImplicitConversionTests + { + private static void AssertImplicitConvert(TSource argument, TTarget expected) + { + CallSiteBinder binder = Binder.Convert(CSharpBinderFlags.None, typeof(TTarget), typeof(ImplicitConversionTests)); + CallSite> callSite = + CallSite>.Create(binder); + Func func = callSite.Target; + TTarget result = func(callSite, argument); + Assert.Equal(expected, result); + + if (typeof(TSource) != typeof(object)) + { + AssertImplicitConvert(argument, expected); + } + } + + private static void AssertBadImplicitConvert(TSource argument) + { + CallSiteBinder binder = Binder.Convert(CSharpBinderFlags.None, typeof(TTarget), typeof(ImplicitConversionTests)); + CallSite> callSite = + CallSite>.Create(binder); + Func func = callSite.Target; + Assert.Throws(() => func(callSite, argument)); + + if (typeof(TSource) != typeof(object)) + { + AssertBadImplicitConvert(argument); + } + } + + private static void TestTypeAsArgument(TTarget argument) + { + // Do nothing. Just test whether something can be accepted as an argument. + } + + private static void TestBadTypeAsArgument(dynamic argument) + { + Assert.Throws(() => TestTypeAsArgument(argument)); + } + + [Fact] + public void NullablesToValueTypeUp() + { + AssertImplicitConvert(2, 2); + AssertImplicitConvert(null, null); + AssertImplicitConvert(2, 2); + } + + + [Fact] + public void NullablesToBaseTypesInterfaces() + { + AssertImplicitConvert>(2, 2); + AssertImplicitConvert>(null, null); + AssertImplicitConvert>(2, 2); + AssertImplicitConvert(2, 2); + AssertImplicitConvert(2, 2); + AssertImplicitConvert(2, 2); + } + + [Fact] + public void NumericConversions() + { + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + + AssertImplicitConvert('a', 'a'); + AssertImplicitConvert('a', 'a'); + AssertImplicitConvert('a', 'a'); + AssertImplicitConvert('a', 'a'); + AssertImplicitConvert('a', 'a'); + AssertImplicitConvert('a', 'a'); + AssertImplicitConvert('a', 'a'); + AssertImplicitConvert('a', 'a'); + } + + [Fact] + public void NumericNullableConversions() + { + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + + AssertImplicitConvert('a', 'a'); + AssertImplicitConvert('a', 'a'); + AssertImplicitConvert('a', 'a'); + AssertImplicitConvert('a', 'a'); + AssertImplicitConvert('a', 'a'); + AssertImplicitConvert('a', 'a'); + AssertImplicitConvert('a', 'a'); + AssertImplicitConvert('a', 'a'); + } + + [Fact] + public void NumericNullableToNullableConversions() + { + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + AssertImplicitConvert(1, 1); + + AssertImplicitConvert(1, 1); + + AssertImplicitConvert('a', 'a'); + AssertImplicitConvert('a', 'a'); + AssertImplicitConvert('a', 'a'); + AssertImplicitConvert('a', 'a'); + AssertImplicitConvert('a', 'a'); + AssertImplicitConvert('a', 'a'); + AssertImplicitConvert('a', 'a'); + AssertImplicitConvert('a', 'a'); + + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + + AssertImplicitConvert(null, null); + + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + AssertImplicitConvert(null, null); + } + + [Fact] + public void StructToInterfacesAndBase() + { + DateTime now = DateTime.Now; + AssertImplicitConvert>(now, now); + AssertImplicitConvert>(now, now); + AssertImplicitConvert(now, now); + AssertImplicitConvert(now, now); + } + + [Fact] + public void ArraysToInterfaces() + { + int[] intArray = {1, 2, 3}; + AssertImplicitConvert(intArray, intArray); + AssertImplicitConvert>(intArray, intArray); + AssertImplicitConvert>(intArray, intArray); + AssertImplicitConvert>(intArray, intArray); + AssertImplicitConvert>(intArray, intArray); + AssertImplicitConvert>(intArray, intArray); + } + + [Fact] + public void ArraysToInterfacesAsArguments() + { + dynamic intArray = new[] {1, 2, 3}; + TestTypeAsArgument(intArray); + TestTypeAsArgument>(intArray); + TestTypeAsArgument>(intArray); + TestTypeAsArgument>(intArray); + TestTypeAsArgument>(intArray); + TestTypeAsArgument>(intArray); + } + + [Fact] + public void ArraysToIncompatibleInterfaces() + { + int[] intArray = { 1, 2, 3 }; + AssertBadImplicitConvert>(intArray); + AssertBadImplicitConvert>(intArray); + AssertBadImplicitConvert>(intArray); + AssertBadImplicitConvert>(intArray); + AssertBadImplicitConvert>(intArray); + AssertBadImplicitConvert>(intArray); + } + + [Fact] + public void ArraysToIncompatibleInterfacesAsArgument() + { + int[] intArray = { 1, 2, 3 }; + TestBadTypeAsArgument>(intArray); + TestBadTypeAsArgument>(intArray); + TestBadTypeAsArgument>(intArray); + TestBadTypeAsArgument>(intArray); + TestBadTypeAsArgument>(intArray); + TestBadTypeAsArgument>(intArray); + } + } +} diff --git a/external/corefx/src/Microsoft.CSharp/tests/IndexingTests.cs b/external/corefx/src/Microsoft.CSharp/tests/IndexingTests.cs index f18cc7e084..c2273494a2 100644 --- a/external/corefx/src/Microsoft.CSharp/tests/IndexingTests.cs +++ b/external/corefx/src/Microsoft.CSharp/tests/IndexingTests.cs @@ -2,6 +2,8 @@ // 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.Collections; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; @@ -99,5 +101,51 @@ namespace Microsoft.CSharp.RuntimeBinder.Tests dynamic ind = 2m; Assert.Throws(() => arr[ind]); } + + // Only gives results once. + private class ArgumentEnumerable : IEnumerable + { + private int _count; + + public ArgumentEnumerable(int count) + { + _count = count; + } + + public IEnumerator GetEnumerator() + { + while (_count > 0) + { + yield return CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null); + --_count; + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + [Fact] + public void GetIndexWithNonRepeatingArgumentInfos() + { + CallSiteBinder binder = Binder.GetIndex(CSharpBinderFlags.None, GetType(), new ArgumentEnumerable(2)); + CallSite> callSite = + CallSite>.Create(binder); + Func targ = callSite.Target; + object result = targ(callSite, new[] {1, 2, 3}, 1); + Assert.Equal(2, result); + } + + [Fact] + public void SetIndexWithNonRepeatingArgumentInfos() + { + CallSiteBinder binder = Binder.SetIndex(CSharpBinderFlags.None, GetType(), new ArgumentEnumerable(3)); + CallSite> callSite = + CallSite>.Create(binder); + Func targ = callSite.Target; + int[] array = {1, 2, 3}; + object result = targ(callSite, array, 1, 9); + Assert.Equal(9, result); + Assert.Equal(9, array[1]); + } } } diff --git a/external/corefx/src/Microsoft.CSharp/tests/IsEventTests.cs b/external/corefx/src/Microsoft.CSharp/tests/IsEventTests.cs new file mode 100644 index 0000000000..f426e35d29 --- /dev/null +++ b/external/corefx/src/Microsoft.CSharp/tests/IsEventTests.cs @@ -0,0 +1,130 @@ +// 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.Runtime.CompilerServices; +using Xunit; + +namespace Microsoft.CSharp.RuntimeBinder.Tests +{ + public class IsEventTests + { + public class TypeWithEvents + { + public event Action Event; + + public void Raise(int eventArg) => Event?.Invoke(eventArg); + + public int NonEventField; + + public int NonEventProperty { get; set; } + } + + private class PrivateTypeWithEvent + { +#pragma warning disable 67 + public event Action Event; +#pragma warning restore 67 + } + + [Fact] + public void BindToEvent() + { + dynamic d = new TypeWithEvents(); + int output = 0; + d.Event += (Action)(i => + { + output = i; + }); + d.Raise(49); + Assert.Equal(49, output); + } + + [Fact] + public void RemoveHandler() + { + TypeWithEvents with = new TypeWithEvents(); + int output = 0; + Action setOutput = i => + { + output = i; + }; + with.Event += setOutput; + with.Raise(1); + dynamic d = with; + d.Event -= setOutput; + with.Raise(2); + Assert.Equal(1, output); + d.Raise(3); + Assert.Equal(1, output); + } + + [Fact] + public void BindToNonExistent() + { + dynamic d = new TypeWithEvents(); + Action handler = i => + { + }; + Assert.Throws(() => d.NothingHere += handler); + } + + [Fact] + public void BindToNonEvents() + { + dynamic d = new TypeWithEvents(); + Action handler = i => + { + }; + Assert.Throws(() => d.NonEventField += handler); + Assert.Throws(() => d.NonEventProperty += handler); + Assert.Throws(() => d.Raise += handler); + } + + [Fact] + public void NullEventObject() + { + CallSiteBinder binder = Binder.IsEvent(CSharpBinderFlags.None, "Event", GetType()); + CallSite> callSite = CallSite>.Create(binder); + Func target = callSite.Target; + Assert.Throws(() => target(callSite, null)); + } + + [Fact] + public void NullContextAnswerTrue() + { + CallSiteBinder binder = Binder.IsEvent(CSharpBinderFlags.None, "Event", null); + CallSite> callSite = CallSite>.Create(binder); + Func target = callSite.Target; + Assert.True(target(callSite, new TypeWithEvents())); + } + + [Fact] + public void NullContextAnswerFalse() + { + CallSiteBinder binder = Binder.IsEvent(CSharpBinderFlags.None, "Nah", null); + CallSite> callSite = CallSite>.Create(binder); + Func target = callSite.Target; + Assert.False(target(callSite, new TypeWithEvents())); + } + + [Fact] + public void ContextWithVisibilityOnPrivateType() + { + CallSiteBinder binder = Binder.IsEvent(CSharpBinderFlags.None, "Event", GetType()); + CallSite> callSite = CallSite>.Create(binder); + Func target = callSite.Target; + Assert.True(target(callSite, new PrivateTypeWithEvent())); + } + + [Fact] + public void NullContextAndPrivateType() + { + CallSiteBinder binder = Binder.IsEvent(CSharpBinderFlags.None, "Event", null); + CallSite> callSite = CallSite>.Create(binder); + Func target = callSite.Target; + Assert.False(target(callSite, new PrivateTypeWithEvent())); + } + } +} diff --git a/external/corefx/src/Microsoft.CSharp/tests/Microsoft.CSharp.Tests.csproj b/external/corefx/src/Microsoft.CSharp/tests/Microsoft.CSharp.Tests.csproj index 6b4b80b182..db08891f7e 100644 --- a/external/corefx/src/Microsoft.CSharp/tests/Microsoft.CSharp.Tests.csproj +++ b/external/corefx/src/Microsoft.CSharp/tests/Microsoft.CSharp.Tests.csproj @@ -4,23 +4,35 @@ {82B54697-0251-47A1-8546-FC507D0F3B08} - - + + + + - - Common\System\Runtime\Serialization\Formatters\BinaryFormatterHelpers.cs - + + + + + + + + + + + + + - \ No newline at end of file + diff --git a/external/corefx/src/Microsoft.CSharp/tests/NamedArgumentTests.cs b/external/corefx/src/Microsoft.CSharp/tests/NamedArgumentTests.cs new file mode 100644 index 0000000000..989a50d62d --- /dev/null +++ b/external/corefx/src/Microsoft.CSharp/tests/NamedArgumentTests.cs @@ -0,0 +1,1315 @@ +// 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.Runtime.CompilerServices; +using Xunit; + +namespace Microsoft.CSharp.RuntimeBinder.Tests +{ + public class NamedArgumentTests + { + public class TypeWithMethods + { + public int DoStuff(int x, int y) => x + y; + + public long DoStuff(long z, long a) => z * a; + + public int DoStuff(string s, int i) => i; + + public int DoOtherStuff(int x, int y, int z) => x * y + z; + } + + [Fact] + public void OnlyNameFirstArgument() + { + CallSite> callsite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.None, nameof(TypeWithMethods.DoStuff), Type.EmptyTypes, GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "x"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + Func target = callsite.Target; + object res = target(callsite, new TypeWithMethods(), 9, 14); + Assert.Equal(23, res); + } + + [Fact] + public void OnlyNameFirstArgumentMatchesWrongType() + { + CallSite> callsite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.None, nameof(TypeWithMethods.DoStuff), Type.EmptyTypes, GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "s"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + Func target = callsite.Target; + string message = Assert.Throws(() => target(callsite, new TypeWithMethods(), 9, 14)) + .Message; + + // All but the (string, int) method should have been excluded from consideration, leaving the binder to + // complain about it not matching types. + Assert.Contains( + "'Microsoft.CSharp.RuntimeBinder.Tests.NamedArgumentTests.TypeWithMethods.DoStuff(string, int)'", + message); + } + + [Fact] + public void ResolveThroughNameOnlyFirstArgument() + { + CallSite> callsite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.None, nameof(TypeWithMethods.DoStuff), Type.EmptyTypes, GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "z"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + Func target = callsite.Target; + object res = target(callsite, new TypeWithMethods(), 9, 14); + Assert.Equal(126L, res); + } + + [Fact] + public void NonExistentNameOnlyFirstArgument() + { + CallSite> callsite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.None, nameof(TypeWithMethods.DoStuff), Type.EmptyTypes, GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "nada"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + Func target = callsite.Target; + string message = Assert.Throws(() => target(callsite, new TypeWithMethods(), 9, 14)) + .Message; + + // The best overload for 'DoStuff' does not have a parameter named 'nada' + Assert.Contains("'DoStuff'", message); + Assert.Contains("'nada'", message); + } + + [Fact] + public void NameOnlyFirstArgumentWrongPlace() + { + CallSite> callsite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.None, nameof(TypeWithMethods.DoStuff), Type.EmptyTypes, GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "y"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + Func target = callsite.Target; + string message = Assert.Throws(() => target(callsite, new TypeWithMethods(), 9, 14)) + .Message; + + // Named argument 'y' is used out-of-position but is followed by an unnamed argument + Assert.Contains("'y'", message); + } + + [Fact] + public void NameOnlyFirstAndSecondWithSecondArgumentWrongPlace() + { + CallSite> callsite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.None, nameof(TypeWithMethods.DoOtherStuff), Type.EmptyTypes, GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "x"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "z"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + Func target = callsite.Target; + string message = Assert + .Throws(() => target(callsite, new TypeWithMethods(), 9, 14, 13)) + .Message; + + // Named argument 'z' is used out-of-position but is followed by an unnamed argument + Assert.Contains("'z'", message); + } + + [Fact] + public void NameOnlyFirstAndSecondWithSecondArgumentNotFound() + { + CallSite> callsite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.None, nameof(TypeWithMethods.DoOtherStuff), Type.EmptyTypes, GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "x"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "nada"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + Func target = callsite.Target; + string message = Assert + .Throws(() => target(callsite, new TypeWithMethods(), 9, 14, 12)) + .Message; + + // The best overload for 'DoMoreStuff' does not have a parameter named 'nada' + Assert.Contains("'DoOtherStuff'", message); + Assert.Contains("'nada'", message); + } + + [Fact] + public void NameOnlySecondWithSecondArgumentWrongPlace() + { + CallSite> callsite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.None, nameof(TypeWithMethods.DoOtherStuff), Type.EmptyTypes, GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "z"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + Func target = callsite.Target; + string message = Assert + .Throws(() => target(callsite, new TypeWithMethods(), 9, 14, 13)) + .Message; + + // Named argument 'z' is used out-of-position but is followed by an unnamed argument + Assert.Contains("'z'", message); + } + + [Fact] + public void NameOnlySecondWithSecondArgumentNotFound() + { + CallSite> callsite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.None, nameof(TypeWithMethods.DoOtherStuff), Type.EmptyTypes, GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "nada"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + Func target = callsite.Target; + string message = Assert + .Throws(() => target(callsite, new TypeWithMethods(), 9, 14, 12)) + .Message; + + // The best overload for 'DoMoreStuff' does not have a parameter named 'nada' + Assert.Contains("'DoOtherStuff'", message); + Assert.Contains("'nada'", message); + } + + class BaseClass + { + public virtual int Adder(int x, int y) => x + y; + } + + class Derived : BaseClass + { + public override int Adder(int a, int b) => base.Adder(a, b); + } + + [Fact] + public void OverrideChangesName() + { + // Static compilation behavior is to use the name of the type of the variable the object is accessed through. + // Dynamic behavior matches that when the argument is UseCompileTimeType + // Otherwise it depends on the actual type of the object. + + // Defined in terms of base class. Using "x" + CallSite> callSite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.None, nameof(BaseClass.Adder), Type.EmptyTypes, GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, + "x"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + })); + Assert.Equal(9, callSite.Target(callSite, new Derived(), 4, 5)); + Assert.Equal(9, callSite.Target(callSite, new BaseClass(), 4, 5)); + + // Defined in terms of base class. Using "a" + callSite = CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.None, nameof(BaseClass.Adder), Type.EmptyTypes, GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "a"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + })); + string message = Assert.Throws(() => callSite.Target(callSite, new Derived(), 4, 5)) + .Message; + + // The best overload for 'Adder' does not have a parameter named 'a' + Assert.Contains("'Adder'", message); + Assert.Contains("'a'", message); + Assert.Equal( + message, + Assert.Throws(() => callSite.Target(callSite, new BaseClass(), 4, 5)).Message); + + // Defined in terms of Derived, using "a" + CallSite> callSiteDerived = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.None, nameof(BaseClass.Adder), Type.EmptyTypes, GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, + "a"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + })); + Assert.Equal(9, callSiteDerived.Target(callSiteDerived, new Derived(), 4, 5)); + + // Defined in terms of Derived, using "x" + callSiteDerived = CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.None, nameof(BaseClass.Adder), Type.EmptyTypes, GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "x"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + })); + message = Assert + .Throws(() => callSiteDerived.Target(callSiteDerived, new Derived(), 4, 5)) + .Message; + + // The best overload for 'Adder' does not have a parameter named 'x' + Assert.Contains("'Adder'", message); + Assert.Contains("'x'", message); + + // Using runtime types, and "a" + CallSite> callSiteRuntime = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.None, nameof(BaseClass.Adder), Type.EmptyTypes, GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "a"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + })); + + Assert.Equal(9, callSiteRuntime.Target(callSiteRuntime, new Derived(), 4, 5)); + message = Assert + .Throws(() => callSiteRuntime.Target(callSiteRuntime, new BaseClass(), 4, 5)) + .Message; + + // The best overload for 'Adder' does not have a parameter named 'a' + Assert.Contains("'Adder'", message); + Assert.Contains("'a'", message); + + // Using runtime types, and "x" + callSiteRuntime = CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.None, nameof(BaseClass.Adder), Type.EmptyTypes, GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "x"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + })); + + Assert.Equal(9, callSiteRuntime.Target(callSiteRuntime, new BaseClass(), 4, 5)); + message = Assert + .Throws(() => callSiteRuntime.Target(callSiteRuntime, new Derived(), 4, 5)) + .Message; + + // The best overload for 'Adder' does not have a parameter named 'x' + Assert.Contains("'Adder'", message); + Assert.Contains("'x'", message); + } + + // Tests based on the static tests for Roslyn. + class C1 + { + static void M(int a, int b) + { + Console.Write($"First {a} {b}. "); + } + + static void M(long b, long a) + { + Console.Write($"Second {b} {a}. "); + } + } + + [Fact] + public void TestSimple() + { + StringWriter console = new StringWriter(); + Console.SetOut(console); + CallSite> callsite = CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C1), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "a"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) + })); + callsite.Target(callsite, typeof(C1), 1, 2); + callsite = CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C1), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "a") + })); + callsite.Target(callsite, typeof(C1), 3, 4); + Assert.Equal("First 1 2. Second 3 4. ", console.ToString()); + } + + class C2 + { + C2(int a, int b) + { + Console.Write($"{a} {b}."); + } + } + + [Fact] + public void TestSimpleConstructor() + { + StringWriter console = new StringWriter(); + Console.SetOut(console); + CallSite> callsite = CallSite>.Create( + Binder.InvokeConstructor( + CSharpBinderFlags.None, typeof(C2), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.IsStaticType, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.NamedArgument, "a"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) + })); + callsite.Target(callsite, typeof(C2), 1, 2); + Assert.Equal("1 2.", console.ToString()); + } + + class C3 + { + public delegate void MyDelegate(int a, int b); + + public MyDelegate e; + + static void M(int a, int b) + { + Console.Write($"{a} {b}. "); + } + + public C3() + { + e += M; + } + } + + [Fact] + public void TestSimpleDelegate() + { + // This test differs from its static equivalent considerably, as some event-related matters aren't as directly + // applicable. We can use it for checking both direct and deferred delegate invocation. + + StringWriter console = new StringWriter(); + Console.SetOut(console); + C3 targetObject = new C3(); + CallSite> getCallSite = CallSite>.Create( + Binder.GetMember( + CSharpBinderFlags.None, "e", typeof(C3), + new[] {CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)})); + object dele = getCallSite.Target(getCallSite, targetObject); + CallSite> invokeCallSite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "Invoke", Type.EmptyTypes, typeof(C3), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, + "a"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + })); + invokeCallSite.Target(invokeCallSite, dele, 1, 2); + invokeCallSite = CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "e", Type.EmptyTypes, typeof(C3), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "a"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + })); + invokeCallSite.Target(invokeCallSite, targetObject, 1, 2); + Assert.Equal("1 2. 1 2. ", console.ToString()); + } + + class C4 + { + int this[int a, int b] + { + get + { + Console.Write($"Get {a} {b}. "); + return 0; + } + set { Console.Write($"Set {a} {b} {value}."); } + } + } + + [Fact] + public void TestSimpleIndexer() + { + StringWriter console = new StringWriter(); + Console.SetOut(console); + C4 targetObject = new C4(); + CallSite> getCallSite = + CallSite>.Create( + Binder.GetIndex( + CSharpBinderFlags.None, typeof(C4), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.NamedArgument, + "a"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + })); + Assert.Equal(0, getCallSite.Target(getCallSite, targetObject, 1, 2)); + CallSite> setCallSite = + CallSite>.Create( + Binder.SetIndex( + CSharpBinderFlags.None, typeof(C4), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.NamedArgument, + "a"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + })); + Assert.Equal(5, setCallSite.Target(setCallSite, targetObject, 3, 4, 5)); + Assert.Equal("Get 1 2. Set 3 4 5.", console.ToString()); + } + + + class C5 + { + public C5() + { + } + + int this[int a, int b] + { + get { throw null; } + } + + C5(int a, int b) + { + } + + // In the static test this is a local function on Main, but to test that without hunting + // for the name (likely C.
g__local|3_0, but that's implementation-dependent) to + // push in with reflection we need the support from the compiler that we have yet to make + // possible. Cut out the chicken-egg problem and just test on a non-local equivalent. + void Method(int a, int b) + { + } + } + + [Fact] + public void TestSimpleError() + { + CallSite> ctorCallsite = + CallSite>.Create( + Binder.InvokeConstructor( + CSharpBinderFlags.None, typeof(C5), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.IsStaticType, + null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.NamedArgument, + "b"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) + })); + string message = Assert + .Throws(() => ctorCallsite.Target(ctorCallsite, typeof(C5), 1, 2)) + .Message; + + // Named argument 'b' is used out-of-position but is followed by an unnamed argument + Assert.Contains("'b'", message); + CallSite> getCallSite = + CallSite>.Create( + Binder.GetIndex( + CSharpBinderFlags.None, typeof(C5), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.NamedArgument, + "b"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + })); + message = Assert.Throws(() => getCallSite.Target(getCallSite, new C5(), 1, 2)) + .Message; + Assert.Contains("'b'", message); + CallSite> invokeCallSite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.None, nameof(TypeWithMethods.DoStuff), Type.EmptyTypes, GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "b"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + message = Assert.Throws(() => getCallSite.Target(invokeCallSite, new C5(), 1, 2)) + .Message; + Assert.Contains("'b'", message); + } + + class C6 + { + static void M(int first, int other) + { + System.Console.Write($"{first} {other}"); + } + } + + [Fact] + public void TestPositionalUnaffected() + { + CallSite> invokeCallSite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.None, "M", Type.EmptyTypes, typeof(C6), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, + null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, + "first") + })); + string message = Assert + .Throws(() => invokeCallSite.Target(invokeCallSite, typeof(C6), 1, 2)) + .Message; + + // Named argument 'first' specifies a parameter for which a positional argument has already been given + Assert.Contains("'first'", message); + } + + class C7 + { + static void M(T1 a, T2 b) + { + System.Console.Write($"{a} {b}."); + } + } + + [Fact] + public void TestGenericInference() + { + StringWriter console = new StringWriter(); + Console.SetOut(console); + + // Test with types defined fully. + CallSite> compileTimeTypeCallSite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", new[] {typeof(int), typeof(string)}, typeof(C7), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, + null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, + "a"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) + })); + compileTimeTypeCallSite.Target(compileTimeTypeCallSite, typeof(C7), 1, "hi"); + Assert.Equal("1 hi.", console.ToString()); + + // Test with types defined fully in delegate but generic types deduced. + console.GetStringBuilder().Length = 0; + compileTimeTypeCallSite = CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C7), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "a"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) + })); + compileTimeTypeCallSite.Target(compileTimeTypeCallSite, typeof(C7), 1, "hi"); + Assert.Equal("1 hi.", console.ToString()); + + // Test with all types deduced by the dynamic binder. + console.GetStringBuilder().Length = 0; + CallSite> runtimeTypeCallSite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C7), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "a"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) + })); + runtimeTypeCallSite.Target(runtimeTypeCallSite, typeof(C7), 1, "hi"); + Assert.Equal("1 hi.", console.ToString()); + } + + class C8 + { + static void M(int a, int b, int c = 1) + { + System.Console.Write($"M {a} {b}"); + } + } + + [Fact] + public void TestPositionalUnaffected2() + { + CallSite> callSite = CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C8), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "c"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) + })); + string message = Assert.Throws(() => callSite.Target(callSite, typeof(C8), 1, 2)) + .Message; + + // Named argument 'c' is used out-of-position but is followed by an unnamed argument + Assert.Contains("'c'", message); + } + + class C9 + { + static void M(params int[] x) + { + } + } + + [Fact] + public void TestNamedParams() + { + CallSite> callSite = CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C9), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "x"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) + })); + + string message = Assert.Throws(() => callSite.Target(callSite, typeof(C9), 1, 2)) + .Message; + + // No overload for method 'M' takes 2 arguments + Assert.Contains("'M'", message); + Assert.Contains(" 2 ", message); + } + + [Fact] + public void TestNamedParams2() + { + CallSite> callSite = CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C9), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "x") + })); + + string message = Assert.Throws(() => callSite.Target(callSite, typeof(C9), 1, 2)) + .Message; + + // Named argument 'x' specifies a parameter for which a positional argument has already been given + Assert.Contains("'x'", message); + } + + class C10 + { + static void M(int x, params string[] y) + { + System.Console.Write($"{x} {string.Join(",", y)}. "); + } + } + + [Fact] + public void TestNamedParamsVariousForms() + { + // This extends the static test in also calling with no arguments for the params section. + StringWriter console = new StringWriter(); + Console.SetOut(console); + + CallSite> callSite0 = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C10), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, + null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, + "x"), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "y") + })); + callSite0.Target(callSite0, typeof(C10), 1, "2"); + + callSite0 = CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C10), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "x"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) + })); + callSite0.Target(callSite0, typeof(C10), 2, "3"); + + CallSite> callSite1 = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C10), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, + null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, + "x"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) + })); + callSite1.Target(callSite1, typeof(C10), 3, new[] {"4", "5"}); + + CallSite> callSite2 = CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C10), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "x") + })); + callSite2.Target(callSite2, typeof(C10), 4); + + Assert.Equal("1 2. 2 3. 3 4,5. 4 . ", console.ToString()); + } + + [Fact] + public void TestNamedParamsVariousFormsDynamicallyDeducedTypes() + { + // This extends the static test in also calling with no arguments for the params section. + StringWriter console = new StringWriter(); + Console.SetOut(console); + + CallSite> callSite0 = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C10), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "x"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "y") + })); + callSite0.Target(callSite0, typeof(C10), 1, "2"); + + callSite0 = CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C10), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "x"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + callSite0.Target(callSite0, typeof(C10), 2, "3"); + + CallSite> callSite1 = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C10), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "x"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + callSite1.Target(callSite1, typeof(C10), 3, new[] {"4", "5"}); + + CallSite> callSite2 = CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C10), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "x") + })); + callSite2.Target(callSite2, typeof(C10), 4); + + Assert.Equal("1 2. 2 3. 3 4,5. 4 . ", console.ToString()); + } + + [Fact] + public void TestTwiceNamedParams() + { + CallSite> callSite = CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C9), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "x"), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "x") + })); + + string message = Assert.Throws(() => callSite.Target(callSite, typeof(C9), 1, 2)) + .Message; + + // Named argument 'x' cannot be specified multiple times + Assert.Contains("'x'", message); + } + + class C11 + { + static void M(int x, params int[] y) + { + } + } + + [Fact] + public void TestNamedParams3() + { + CallSite> callSite = CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C11), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "y"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) + })); + + string message = Assert.Throws(() => callSite.Target(callSite, typeof(C11), 1, 2)) + .Message; + + // Named argument 'y' is used out-of-position but is followed by an unnamed argument + Assert.Contains("'y'", message); + } + + [Fact] + public void TestNamedParams4() + { + CallSite> callSite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C11), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, + null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, + "x"), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, + "y"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) + })); + + string message = Assert + .Throws(() => callSite.Target(callSite, typeof(C11), 1, 2, 3)) + .Message; + + // No overload for method 'M' takes 3 arguments + Assert.Contains("'M'", message); + Assert.Contains(" 3 ", message); + } + + class C12 + { + static void M(int x, params int[] y) + { + System.Console.Write($"x={x} y[0]={y[0]} y.Length={y.Length}"); + } + } + + [Fact] + public void TestNamedParams5() + { + StringWriter console = new StringWriter(); + Console.SetOut(console); + CallSite> callSite = CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C12), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "y"), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "x") + })); + callSite.Target(callSite, typeof(C12), 1, 2); + Assert.Equal("x=2 y[0]=1 y.Length=1", console.ToString()); + } + + class C13 + { + static void M(int a = 1, int b = 2, int c = 3) + { + } + } + + [Fact] + public void TestBadNonTrailing() + { + CallSite> callSite = CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C13), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "c"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) + })); + string message = Assert.Throws(() => callSite.Target(callSite, typeof(C13), 3, 2)).Message; + //Named argument 'c' is used out-of-position but is followed by an unnamed argument + Assert.Contains(" 'c' ", message); + } + + class C14 + { + static void M(int a = 1, int b = 2, int c = 3) + { + } + static void M(long c = 1, long b = 2) + { + System.Console.Write($"Second {c} {b}. "); + } + } + + [Fact] + public void TestPickGoodOverload() + { + StringWriter console = new StringWriter(); + Console.SetOut(console); + CallSite> callSite = CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C14), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "c"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) + })); + callSite.Target(callSite, typeof(C14), 3, 2); + Assert.Equal("Second 3 2. ", console.ToString()); + } + + [Fact] + public void TestPickGoodOverloadDynamicallyTyped() + { + StringWriter console = new StringWriter(); + Console.SetOut(console); + CallSite> callSite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C14), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "c"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + callSite.Target(callSite, typeof(C14), 3, 2); + Assert.Equal("Second 3 2. ", console.ToString()); + } + + class C15 + { + static void M(long a = 1, long b = 2, long c = 3) + { + } + static void M(int c = 1, int b = 2) + { + System.Console.Write($"Second {c} {b}."); + } + } + + [Fact] + public void TestPickGoodOverload2() + { + StringWriter console = new StringWriter(); + Console.SetOut(console); + CallSite> callSite = CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C15), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "c"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) + })); + callSite.Target(callSite, typeof(C15), 3, 2); + Assert.Equal("Second 3 2.", console.ToString()); + } + + [Fact] + public void TestPickGoodOverload2DynamicallyTyped() + { + StringWriter console = new StringWriter(); + Console.SetOut(console); + CallSite> callSite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C15), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "c"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + callSite.Target(callSite, typeof(C15), 3, 2); + Assert.Equal("Second 3 2.", console.ToString()); + } + + class C16 + { + static void M(int a, int b, int c = 42) + { + System.Console.Write(c); + } + } + + [Fact] + public void TestOptionalValues() + { + StringWriter console = new StringWriter(); + Console.SetOut(console); + CallSite> callSite = CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C16), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, "a"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) + })); + callSite.Target(callSite, typeof(C16), 1, 2); + Assert.Equal("42", console.ToString()); + } + + [Fact] + public void TestOptionalValuesRunTimeTypes() + { + StringWriter console = new StringWriter(); + Console.SetOut(console); + CallSite> callSite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C16), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "a"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + callSite.Target(callSite, typeof(C16), 1, 2); + Assert.Equal("42", console.ToString()); + } + + class C17 + { + static void M(int a, int b, params int[] c) + { + } + } + + [Fact] + public void TestParams() + { + CallSite> callSite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C17), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, + null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, + "b"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) + })); + string message = Assert + .Throws(() => callSite.Target(callSite, typeof(C17), 2, 3, 4)) + .Message; + // Named argument 'b' is used out-of-position but is followed by an unnamed argument + Assert.Contains(" 'b' ", message); + } + + [Fact] + public void TestParamsRuntimeTypes() + { + CallSite> callSite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C17), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "b"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + string message = Assert + .Throws(() => callSite.Target(callSite, typeof(C17), 2, 3, 4)) + .Message; + + // Named argument 'b' is used out-of-position but is followed by an unnamed argument + Assert.Contains(" 'b' ", message); + } + + class C18 + { + static void M(int a, int b, params int[] c) + { + System.Console.Write($"{a} {b} {c[0]} {c[1]} Length:{c.Length}"); + } + } + + [Fact] + public void TestParams2() + { + StringWriter console = new StringWriter(); + Console.SetOut(console); + CallSite> callSite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C18), + new[] + { + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, + null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create( + CSharpArgumentInfoFlags.NamedArgument | CSharpArgumentInfoFlags.UseCompileTimeType, + "b"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) + })); + callSite.Target(callSite, typeof(C18), 1, 2, 3, 4); + Assert.Equal("1 2 3 4 Length:2", console.ToString()); + } + + [Fact] + public void TestParams2RuntimeTypes() + { + StringWriter console = new StringWriter(); + Console.SetOut(console); + CallSite> callSite = + CallSite>.Create( + Binder.InvokeMember( + CSharpBinderFlags.ResultDiscarded, "M", Type.EmptyTypes, typeof(C18), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, "b"), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + callSite.Target(callSite, typeof(C18), 1, 2, 3, 4); + Assert.Equal("1 2 3 4 Length:2", console.ToString()); + } + } +} diff --git a/external/corefx/src/Microsoft.CSharp/tests/NullableEnumUnaryOperationTest.cs b/external/corefx/src/Microsoft.CSharp/tests/NullableEnumUnaryOperationTest.cs new file mode 100644 index 0000000000..2a5164c934 --- /dev/null +++ b/external/corefx/src/Microsoft.CSharp/tests/NullableEnumUnaryOperationTest.cs @@ -0,0 +1,186 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using Xunit; + +namespace Microsoft.CSharp.RuntimeBinder.Tests +{ + public class NullableEnumUnaryOperationTest + { + public enum Int8Enum : byte + { + A, + B, + C, + D, + E + } + + public enum Int32Enum + { + A, + B, + C, + D, + E + } + + public enum UInt32Enum : uint + { + A, + B, + C, + D, + E + } + + public enum Int64Enum : long + { + A, + B, + C, + D, + E + } + + public static IEnumerable Int8Enums() => Enum.GetValues(typeof(Int8Enum)) + .Cast() + .Select(x => new object[] { x }); + + public static IEnumerable Int8NullableEnums() => Int8Enums().Append(new object[] { null }); + + public static IEnumerable Int32Enums() => Enum.GetValues(typeof(Int32Enum)) + .Cast() + .Select(x => new object[] { x }); + + public static IEnumerable Int32NullableEnums() => Int32Enums().Append(new object[] { null }); + + public static IEnumerable UInt32Enums() => Enum.GetValues(typeof(UInt32Enum)) + .Cast() + .Select(x => new object[] { x }); + + public static IEnumerable UInt32NullableEnums() => UInt32Enums().Append(new object[] { null }); + + public static IEnumerable Int64Enums() => Enum.GetValues(typeof(Int64Enum)) + .Cast() + .Select(x => new object[] { x }); + + public static IEnumerable Int64NullableEnums() => Int64Enums().Append(new object[] { null }); + + [Theory, MemberData(nameof(Int8NullableEnums)), SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "25067 is not fixed in NetFX")] + public void ComplementInt8NullableEnum(Int8Enum? value) + { + CSharpArgumentInfo x = CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null); + CallSiteBinder binder = + Binder.UnaryOperation( + CSharpBinderFlags.None, ExpressionType.OnesComplement, + GetType(), new[] { x }); + CallSite> site = CallSite>.Create(binder); + Func targ = site.Target; + object result = targ(site, value); + Assert.Equal(~value, result); + } + + [Theory, MemberData(nameof(Int32NullableEnums)), SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "25067 is not fixed in NetFX")] + public void ComplementInt32NullableEnum(Int32Enum? value) + { + CSharpArgumentInfo x = CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null); + CallSiteBinder binder = + Binder.UnaryOperation( + CSharpBinderFlags.None, ExpressionType.OnesComplement, + GetType(), new[] { x }); + CallSite> site = CallSite>.Create(binder); + Func targ = site.Target; + object result = targ(site, value); + Assert.Equal(~value, result); + } + + [Theory, MemberData(nameof(UInt32NullableEnums)), SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "25067 is not fixed in NetFX")] + public void ComplementUInt32NullableEnum(UInt32Enum? value) + { + CSharpArgumentInfo x = CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null); + CallSiteBinder binder = + Binder.UnaryOperation( + CSharpBinderFlags.None, ExpressionType.OnesComplement, + GetType(), new[] { x }); + CallSite> site = CallSite>.Create(binder); + Func targ = site.Target; + object result = targ(site, value); + Assert.Equal(~value, result); + } + + [Theory, MemberData(nameof(Int64NullableEnums)), SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "25067 is not fixed in NetFX")] + public void ComplementInt64NullableEnum(Int64Enum? value) + { + CSharpArgumentInfo x = CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null); + CallSiteBinder binder = + Binder.UnaryOperation( + CSharpBinderFlags.None, ExpressionType.OnesComplement, + GetType(), new[] { x }); + CallSite> site = CallSite>.Create(binder); + Func targ = site.Target; + object result = targ(site, value); + Assert.Equal(~value, result); + } + + [Theory, MemberData(nameof(Int32Enums))] + public void IncrementInt32NullableEnum(Int32Enum? value) + { + CSharpArgumentInfo x = CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null); + CallSiteBinder binder = + Binder.UnaryOperation( + CSharpBinderFlags.None, ExpressionType.Increment, + GetType(), new[] { x }); + CallSite> site = CallSite>.Create(binder); + Func targ = site.Target; + object result = targ(site, value); + Assert.Equal(++value, result); // Note that there is no write-back to value. + } + + [Theory, MemberData(nameof(Int32Enums))] + public void DecrementInt32NullableEnum(Int32Enum? value) + { + CSharpArgumentInfo x = CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null); + CallSiteBinder binder = + Binder.UnaryOperation( + CSharpBinderFlags.None, ExpressionType.Decrement, + GetType(), new[] { x }); + CallSite> site = CallSite>.Create(binder); + Func targ = site.Target; + object result = targ(site, value); + Assert.Equal(--value, result); // Note that there is no write-back to value. + } + + [Fact] + public void CannotIncrementNullNullableEnum() + { + CSharpArgumentInfo x = CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null); + CallSiteBinder binder = + Binder.UnaryOperation( + CSharpBinderFlags.None, ExpressionType.Increment, + GetType(), new[] { x }); + CallSite> site = CallSite>.Create(binder); + Func targ = site.Target; + Assert.Throws(() => targ(site, default)); + } + + [Fact] + public void CannotDecrementNullNullableEnum() + { + CSharpArgumentInfo x = CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null); + CallSiteBinder binder = + Binder.UnaryOperation( + CSharpBinderFlags.None, ExpressionType.Decrement, + GetType(), new[] { x }); + CallSite> site = CallSite>.Create(binder); + Func targ = site.Target; + Assert.Throws(() => targ(site, default)); + } + } +} diff --git a/external/corefx/src/Microsoft.CSharp/tests/RuntimeBinderExceptionTests.cs b/external/corefx/src/Microsoft.CSharp/tests/RuntimeBinderExceptionTests.cs index d771a9ecf2..1ce617f68a 100644 --- a/external/corefx/src/Microsoft.CSharp/tests/RuntimeBinderExceptionTests.cs +++ b/external/corefx/src/Microsoft.CSharp/tests/RuntimeBinderExceptionTests.cs @@ -3,10 +3,7 @@ // See the LICENSE file in the project root for more information. using System; -using System.IO; using System.Runtime.CompilerServices; -using System.Runtime.Serialization.Formatters.Binary; -using System.Runtime.Serialization.Formatters.Tests; using Xunit; namespace Microsoft.CSharp.RuntimeBinder.Tests @@ -112,11 +109,5 @@ namespace Microsoft.CSharp.RuntimeBinder.Tests Func targ = site.Target; AssertExtensions.Throws("Type Argument", () => targ.Invoke(site, 23)); } - - [Fact] - public void AssertExceptionDeserializationFails() - { - BinaryFormatterHelpers.AssertExceptionDeserializationFails(); - } } } diff --git a/external/corefx/src/Microsoft.CSharp/tests/RuntimeBinderInternalCompilerExceptionTests.cs b/external/corefx/src/Microsoft.CSharp/tests/RuntimeBinderInternalCompilerExceptionTests.cs index 5a726c4cc3..7fbac74f47 100644 --- a/external/corefx/src/Microsoft.CSharp/tests/RuntimeBinderInternalCompilerExceptionTests.cs +++ b/external/corefx/src/Microsoft.CSharp/tests/RuntimeBinderInternalCompilerExceptionTests.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Runtime.Serialization.Formatters.Tests; using Xunit; namespace Microsoft.CSharp.RuntimeBinder.Tests @@ -42,11 +41,5 @@ namespace Microsoft.CSharp.RuntimeBinder.Tests RuntimeBinderInternalCompilerException rbe = new RuntimeBinderInternalCompilerException(message, inner); Assert.Same(inner, rbe.InnerException); } - - [Fact] - public void AssertExceptionDeserializationFails() - { - BinaryFormatterHelpers.AssertExceptionDeserializationFails(); - } } } diff --git a/external/corefx/src/Microsoft.CSharp/tests/RuntimeBinderTests.cs b/external/corefx/src/Microsoft.CSharp/tests/RuntimeBinderTests.cs index 48295c9fed..d065a16f37 100644 --- a/external/corefx/src/Microsoft.CSharp/tests/RuntimeBinderTests.cs +++ b/external/corefx/src/Microsoft.CSharp/tests/RuntimeBinderTests.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections; +using System.ComponentModel; using System.Runtime.CompilerServices; using Xunit; @@ -175,7 +177,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Tests Assert.True(res); } - [Fact, ActiveIssue(7527)] + [Fact] public void CyclicTypeDefinition() { dynamic x = new Third(); @@ -194,5 +196,199 @@ namespace Microsoft.CSharp.RuntimeBinder.Tests class Third : Second> { } + + public class Castable + { + public static implicit operator int(Castable _) => 2; + + public static implicit operator string(Castable _) => "abc"; + } + + [Fact] + public void ImplicitOperatorForPlus() + { + dynamic d = new Castable(); + dynamic result = d + 1; + Assert.Equal(3, result); + result = 5 + d; + Assert.Equal(7, result); + result = d + "def"; + Assert.Equal("abcdef", result); + result = "xyz" + d; + Assert.Equal("xyzabc", result); + } + + [Fact] + public void DynamicArgumentToCyclicTypeDefinition() + { + dynamic arg = 5; + new Builder().SomeMethod(arg); + } + + public class Builder : BuilderBaseEx> + { + public Builder SomeMethod(object arg) + { + return this; + } + } + public class BuilderBaseEx : BuilderBase where T : BuilderBaseEx { } + public class BuilderBase where T : BuilderBase { } + + [Fact] + public void CircularOnOwnNested() + { + dynamic d = new Generic.Inner + { + Foo = "expected" + }; + + Assert.Equal("expected", d.Foo); + } + + + [Fact] + public void CircularOnOwnNestedAbsentMember() + { + dynamic d = new Generic.Inner + { + Foo = "expected" + }; + + Assert.Throws(() => d.Bar); + } + + class Generic : BindingList.Inner> + { + public class Inner + { + public object Foo { get; set; } + } + } + + public class SomeType + { + public string SomeMethod(int i) => "ABC " + i; + } + + private class SomePrivateType + { + public string SomeMethod(int i) => "ABC " + i; + } + + internal class SomeInternalType + { + public string SomeMethod(int i) => "ABC " + i; + } + + protected class SomeProtectedType + { + public string SomeMethod(int i) => "ABC " + i; + } + + [Fact] + public void MethodCallWithNullContext() + { + CallSiteBinder binder = Binder.InvokeMember( + CSharpBinderFlags.None, nameof(SomeType.SomeMethod), Type.EmptyTypes, null, + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + }); + CallSite> site = + CallSite>.Create(binder); + Func targ = site.Target; + object res = targ(site, new SomeType(), 9); + Assert.Equal("ABC 9", res); + } + + [Fact] + public void MethodCallWithNullContextCannotSeeNonPublic() + { + CallSiteBinder binder = Binder.InvokeMember( + CSharpBinderFlags.None, nameof(SomePrivateType.SomeMethod), Type.EmptyTypes, null, + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + }); + CallSite> site = + CallSite>.Create(binder); + Func targ = site.Target; + Assert.Throws(() => targ(site, new SomePrivateType(), 9)); + Assert.Throws(() => targ(site, new SomeInternalType(), 9)); + Assert.Throws(() => targ(site, new SomeProtectedType(), 9)); + } + + interface ICounter1 + { + int Count(ICollection x); + double ExplicitCount { get; set; } + } + + interface ICounter2 + { + int Count(ICollection x); + int ExplicitCount { get; set; } + } + + interface ICounterBoth : ICounter1, ICounter2 { } + + [Fact] + public void AmbiguousInterfaceInheritedMethodError() + { + ICounterBoth icb = null; // Error on ambiguity should happen before error on null. + string message = Assert.Throws(() => icb.Count((dynamic)new int[3])).Message; + // The call is ambiguous between the following methods or properties: + // 'Microsoft.CSharp.RuntimeBinder.Tests.RuntimeBinderTests.ICounter1.Count(System.Collections.ICollection)' + // and 'Microsoft.CSharp.RuntimeBinder.Tests.RuntimeBinderTests.ICounter2.Count(System.Collections.ICollection)' + Assert.Contains("'Microsoft.CSharp.RuntimeBinder.Tests.RuntimeBinderTests.ICounter1.Count(System.Collections.ICollection)'", message); + Assert.Contains("'Microsoft.CSharp.RuntimeBinder.Tests.RuntimeBinderTests.ICounter2.Count(System.Collections.ICollection)'", message); + } + + [Fact] + public void AmbiguousMemberError() + { + CallSite> compileTimeTypeValueSetter = + CallSite>.Create( + Binder.SetMember( + CSharpBinderFlags.None, "ExplicitCount", GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) + })); + Func target0 = compileTimeTypeValueSetter.Target; + string message = Assert.Throws(() => target0(compileTimeTypeValueSetter, null, 2)).Message; + // Ambiguity between 'Microsoft.CSharp.RuntimeBinder.Tests.RuntimeBinderTests.ICounter1.ExplicitCount' + // and 'Microsoft.CSharp.RuntimeBinder.Tests.RuntimeBinderTests.ICounter2.ExplicitCount' + Assert.Contains("'Microsoft.CSharp.RuntimeBinder.Tests.RuntimeBinderTests.ICounter1.ExplicitCount'", message); + Assert.Contains("'Microsoft.CSharp.RuntimeBinder.Tests.RuntimeBinderTests.ICounter2.ExplicitCount'", message); + + CallSite> runTimeTypeValueSetter = + CallSite>.Create( + Binder.SetMember( + CSharpBinderFlags.None, "ExplicitCount", GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + })); + var target1 = runTimeTypeValueSetter.Target; + Assert.Equal(message, Assert.Throws(() => target1(runTimeTypeValueSetter, null, 2)).Message); + + CallSite> getter = + CallSite>.Create( + Binder.GetMember( + CSharpBinderFlags.None, "ExplicitCount", GetType(), + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) + })); + + Func target2 = getter.Target; + Assert.Equal(message, Assert.Throws(() => target2(getter, null)).Message); + } } } diff --git a/external/corefx/src/Microsoft.CSharp/tests/VarArgsTests.cs b/external/corefx/src/Microsoft.CSharp/tests/VarArgsTests.cs new file mode 100644 index 0000000000..a9a45f7690 --- /dev/null +++ b/external/corefx/src/Microsoft.CSharp/tests/VarArgsTests.cs @@ -0,0 +1,112 @@ +// 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.Security.Cryptography.X509Certificates; +using Xunit; + +namespace Microsoft.CSharp.RuntimeBinder.Tests +{ + public class VarArgsTests + { + // The inability to cast __arglist to object means it can't be used with dynamic bounding + // but we still need to be sure that the binder doesn't choke when it encounters a varargs + // method, or attempt to bind to it. + + public class HasVarargs + { + public void OnlyVarargs(__arglist) + { + } + + // Overloads where the varargs form could perhaps be confused with another + public int Nullary(__arglist) => 0; + + public int Nullary() => 1; + + public int Unary0(int i, __arglist) => 0; + + public int Unary0(int i) => i; + + public int Unary1(int i, __arglist) => 0; + + public int Unary1() => 1; + + public int Unary1(long l) => (int)l; + + public int Unary1(long l, string s) => 2; + + public int Binary(int i, __arglist) => 0; + + public int Binary(int i, int j, __arglist) => 0; + + public int Binary(int i, int j) => i + j; + } + + public class VarArgCtorOption + { + public int Value { get; } + public VarArgCtorOption(__arglist) + { + } + + public VarArgCtorOption(int i, __arglist) + { + } + + public VarArgCtorOption(int i) => Value = i; + } + + [Fact] + public void FailBindOnlyVarargsAvailable() + { + dynamic d = new HasVarargs(); + string errorMessage = Assert.Throws(() => d.OnlyVarargs()).Message; + // No overload for method 'OnlyVarargs' takes '0' arguments + // Localized forms should contain the name and count. + Assert.Contains("OnlyVarargs", errorMessage); + Assert.Contains("0", errorMessage); + errorMessage = Assert.Throws(() => d.OnlyVarargs(1)).Message; + // "The best overloaded method match for 'Microsoft.CSharp.RuntimeBinder.Tests.VarArgsTests.HasVarargs.OnlyVarargs(__arglist)' has some invalid arguments" + // Localized form should contain the name, + Assert.Contains("Microsoft.CSharp.RuntimeBinder.Tests.VarArgsTests.HasVarargs.OnlyVarargs(__arglist)", errorMessage); + } + + [Fact] + public void CorrectNullaryOverload() + { + dynamic d = new HasVarargs(); + Assert.Equal(1, d.Nullary()); + } + + [Fact] + public void CorrectUnaryOverload() + { + dynamic d = new HasVarargs(); + Assert.Equal(32, d.Unary0(32)); + } + + [Fact] + public void CorrectUnaryOverloadNeedingImplicitConversion() + { + dynamic d = new HasVarargs(); + Assert.Equal(9392, d.Unary1(9392)); + } + + [Fact] + public void CorrectBinaryOverload() + { + dynamic d = new HasVarargs(); + Assert.Equal(7, d.Binary(3, 4)); + Assert.Equal(8, d.Binary((byte)2, (byte)6)); + } + + [Fact] + public void CorrectCtor() + { + dynamic d = 19; + VarArgCtorOption vars = new VarArgCtorOption(d); + Assert.Equal(19, vars.Value); + } + } +} diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/Microsoft.Diagnostics.Tracing.EventSource.Redist.sln b/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/Microsoft.Diagnostics.Tracing.EventSource.Redist.sln deleted file mode 100644 index 314aec1c01..0000000000 --- a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/Microsoft.Diagnostics.Tracing.EventSource.Redist.sln +++ /dev/null @@ -1,21 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Diagnostics.EventSource.Redist", "src\Microsoft.Diagnostics.Tracing.EventSource.Redist.csproj", "{0CAF38F5-C7E7-46F2-8F39-C5D57492FF7F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - netcoreapp-Windows_NT-Debug|Any CPU = netcoreapp-Windows_NT-Debug|Any CPU - netcoreapp-Windows_NT-Release|Any CPU = netcoreapp-Windows_NT-Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0CAF38F5-C7E7-46F2-8F39-C5D57492FF7F}.netfx-Windows_NT-Debug|Any CPU.ActiveCfg = netfx-Windows_NT-Debug|Any CPU - {0CAF38F5-C7E7-46F2-8F39-C5D57492FF7F}.netfx-Windows_NT-Debug|Any CPU.Build.0 = netfx-Windows_NT-Debug|Any CPU - {0CAF38F5-C7E7-46F2-8F39-C5D57492FF7F}.netfx-Windows_NT-Release|Any CPU.ActiveCfg = netfx-Windows_NT-Release|Any CPU - {0CAF38F5-C7E7-46F2-8F39-C5D57492FF7F}.netfx-Windows_NT-Release|Any CPU.Build.0 = netfx-Windows_NT-Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/EventSource.cs.REMOVED.git-id b/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/EventSource.cs.REMOVED.git-id deleted file mode 100644 index 31d8db0aa0..0000000000 --- a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/shared/EventSource.cs.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -8bc6455cb929bc7200fb3b1e60723e88669c113e \ No newline at end of file diff --git a/external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/Microsoft.Diagnostics.Tracing.EventSource.Redist.sln b/external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/Microsoft.Diagnostics.Tracing.EventSource.Redist.sln new file mode 100644 index 0000000000..9f12544f8c --- /dev/null +++ b/external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/Microsoft.Diagnostics.Tracing.EventSource.Redist.sln @@ -0,0 +1,38 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Diagnostics.Tracing.EventSource.Redist.Tests", "tests\Microsoft.Diagnostics.Tracing.EventSource.Redist.Tests.csproj", "{0657A043-0AEE-445E-9BE4-0B3A9D80318F}" + ProjectSection(ProjectDependencies) = postProject + {0CAF38F5-C7E7-46F2-8F39-C5D57492FF7F} = {0CAF38F5-C7E7-46F2-8F39-C5D57492FF7F} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Diagnostics.Tracing.EventSource.Redist", "src\Microsoft.Diagnostics.Tracing.EventSource.Redist.csproj", "{0CAF38F5-C7E7-46F2-8F39-C5D57492FF7F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{1A2F9F4A-A032-433E-B914-ADD5992BB178}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E107E9C1-E893-4E87-987E-04EF0DCEAEFD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0657A043-0AEE-445E-9BE4-0B3A9D80318F}.Debug|Any CPU.ActiveCfg = netfx-Debug|Any CPU + {0657A043-0AEE-445E-9BE4-0B3A9D80318F}.Debug|Any CPU.Build.0 = netfx-Debug|Any CPU + {0657A043-0AEE-445E-9BE4-0B3A9D80318F}.Release|Any CPU.ActiveCfg = netfx-Release|Any CPU + {0657A043-0AEE-445E-9BE4-0B3A9D80318F}.Release|Any CPU.Build.0 = netfx-Release|Any CPU + {0CAF38F5-C7E7-46F2-8F39-C5D57492FF7F}.Debug|Any CPU.ActiveCfg = netfx-Windows_NT-Debug|Any CPU + {0CAF38F5-C7E7-46F2-8F39-C5D57492FF7F}.Debug|Any CPU.Build.0 = netfx-Windows_NT-Debug|Any CPU + {0CAF38F5-C7E7-46F2-8F39-C5D57492FF7F}.Release|Any CPU.ActiveCfg = netfx-Windows_NT-Release|Any CPU + {0CAF38F5-C7E7-46F2-8F39-C5D57492FF7F}.Release|Any CPU.Build.0 = netfx-Windows_NT-Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {0657A043-0AEE-445E-9BE4-0B3A9D80318F} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} + {0CAF38F5-C7E7-46F2-8F39-C5D57492FF7F} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD} + EndGlobalSection +EndGlobal diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/dir.props b/external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/dir.props similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/dir.props rename to external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/dir.props diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/pkg/Microsoft.Diagnostics.Tracing.EventSource.Redist.pkgproj b/external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/pkg/Microsoft.Diagnostics.Tracing.EventSource.Redist.pkgproj similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/pkg/Microsoft.Diagnostics.Tracing.EventSource.Redist.pkgproj rename to external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/pkg/Microsoft.Diagnostics.Tracing.EventSource.Redist.pkgproj diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/src/Configurations.props b/external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/src/Configurations.props similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/src/Configurations.props rename to external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/src/Configurations.props diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/src/ManifestEtw.cs b/external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/src/ManifestEtw.cs similarity index 100% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/src/ManifestEtw.cs rename to external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/src/ManifestEtw.cs diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/src/Microsoft.Diagnostics.Tracing.EventSource.Redist.csproj b/external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/src/Microsoft.Diagnostics.Tracing.EventSource.Redist.csproj similarity index 79% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/src/Microsoft.Diagnostics.Tracing.EventSource.Redist.csproj rename to external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/src/Microsoft.Diagnostics.Tracing.EventSource.Redist.csproj index 33e247edf7..fda6b6109b 100644 --- a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/src/Microsoft.Diagnostics.Tracing.EventSource.Redist.csproj +++ b/external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/src/Microsoft.Diagnostics.Tracing.EventSource.Redist.csproj @@ -4,7 +4,7 @@ Microsoft.Diagnostics.Tracing.EventSource {0CAF38F5-C7E7-46F2-8F39-C5D57492FF7F} - $(DefineConstants);NO_EVENTCOMMANDEXECUTED_SUPPORT;ES_BUILD_STANDALONE;FEATURE_MANAGED_ETW + $(DefineConstants);NO_EVENTCOMMANDEXECUTED_SUPPORT;ES_BUILD_STANDALONE;FEATURE_MANAGED_ETW;PLATFORM_WINDOWS true @@ -14,10 +14,10 @@ - + - - + + diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/src/Resources/Strings.resx b/external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/src/Resources/Strings.resx similarity index 91% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/src/Resources/Strings.resx rename to external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/src/Resources/Strings.resx index 412c756441..498fcb5070 100644 --- a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/src/Resources/Strings.resx +++ b/external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/src/Resources/Strings.resx @@ -117,6 +117,18 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + The given key '{0}' was not present in the dictionary. + + + The name of the type is invalid. + + + Non-negative number required. + + + The ID parameter must be in the range {0} through {1}. + Abstract event source must not declare event methods ({0} with ID {1}). @@ -126,6 +138,9 @@ Getting out of bounds during scalar addition. + + Bad Hexidecimal digit "{0}". + Channel {0} does not match event channel value {1}. @@ -138,6 +153,9 @@ The type of {0} is not expected in {1}. + + Must have an even number of Hexidecimal digits. + Channel {0} has a value of {1} which is outside the legal range (16-254). @@ -162,6 +180,9 @@ An instance of EventSource with Guid {0} already exists. + + The payload for a single event is too large. + Event {0} specifies an Admin channel {1}. It must specify a Message property. @@ -174,6 +195,9 @@ Task {0} has a value of {1} which is outside the legal range (1-65535). + + Illegal value "{0}" (prefix strings with @ to indicate a literal string). + Incorrectly-authored TypeInfo - a type should be serialized as one field or as one group @@ -202,7 +226,7 @@ Attempt to define more than the maximum limit of 8 channels for a provider. - Event {0} is givien event ID {1} but {2} was passed to WriteEvent. + Event {0} was assigned event ID {1} but {2} was passed to WriteEvent. The Guid of an EventSource must be non zero. @@ -270,6 +294,9 @@ EventSource({0}, {1}) + + There must be an even number of trait strings (they are key-value pairs). + Event source types must be sealed or abstract. @@ -285,6 +312,9 @@ Use of undefined opcode value {0} for event {1}. + + Unknown ETW trait "{0}". + Unsupported type {0} in event source. diff --git a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/src/RuntimeSpecific.cs b/external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/src/RuntimeSpecific.cs similarity index 98% rename from external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/src/RuntimeSpecific.cs rename to external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/src/RuntimeSpecific.cs index 014995117c..83f452cc83 100644 --- a/external/corefx/src/Microsoft.Diagnostics.EventSource.Tracing.Redist/src/RuntimeSpecific.cs +++ b/external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/src/RuntimeSpecific.cs @@ -200,7 +200,7 @@ namespace System.Diagnostics.Tracing else if ((type.IsArray || type.IsPointer) && type.GetElementType() == typeof(byte)) return "win:Binary"; - ManifestError(Resources.GetResourceString("EventSource_UnsupportedEventTypeInManifest", type.Name), true); + ManifestError(SR.Format(SR.EventSource_UnsupportedEventTypeInManifest, type.Name), true); return string.Empty; } } diff --git a/external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/tests/Configurations.props b/external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/tests/Configurations.props new file mode 100644 index 0000000000..064de9fd71 --- /dev/null +++ b/external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/tests/Configurations.props @@ -0,0 +1,8 @@ + + + + + netfx; + + + \ No newline at end of file diff --git a/external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/tests/Microsoft.Diagnostics.Tracing.EventSource.Redist.Tests.csproj b/external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/tests/Microsoft.Diagnostics.Tracing.EventSource.Redist.Tests.csproj new file mode 100644 index 0000000000..10dae84c0f --- /dev/null +++ b/external/corefx/src/Microsoft.Diagnostics.Tracing.EventSource.Redist/tests/Microsoft.Diagnostics.Tracing.EventSource.Redist.Tests.csproj @@ -0,0 +1,44 @@ + + + + + {0657A043-0AEE-445E-9BE4-0B3A9D80318F} + $(DefineConstants);FEATURE_ETLEVENTS + $(DefineConstants);FEATURE_EVENTCOUNTER_DISPOSE + $(DefineConstants);USE_MDT_EVENTSOURCE + true + + + ..\..\System.Diagnostics.Tracing\tests + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/Microsoft.VisualBasic/ref/Microsoft.VisualBasic.cs b/external/corefx/src/Microsoft.VisualBasic/ref/Microsoft.VisualBasic.cs index 3720cc7a2f..c7ced6c01d 100644 --- a/external/corefx/src/Microsoft.VisualBasic/ref/Microsoft.VisualBasic.cs +++ b/external/corefx/src/Microsoft.VisualBasic/ref/Microsoft.VisualBasic.cs @@ -198,11 +198,8 @@ namespace Microsoft.VisualBasic.CompilerServices public sealed partial class ProjectData { internal ProjectData() { } - [System.Security.SecuritySafeCriticalAttribute] public static void ClearProjectError() { } - [System.Security.SecuritySafeCriticalAttribute] public static void SetProjectError(System.Exception ex) { } - [System.Security.SecuritySafeCriticalAttribute] public static void SetProjectError(System.Exception ex, int lErl) { } } [System.AttributeUsageAttribute((System.AttributeTargets)(4), Inherited = false, AllowMultiple = false)] diff --git a/external/corefx/src/Microsoft.Win32.Primitives/src/System/ComponentModel/Win32Exception.cs b/external/corefx/src/Microsoft.Win32.Primitives/src/System/ComponentModel/Win32Exception.cs index b7957e21cd..a275f9092f 100644 --- a/external/corefx/src/Microsoft.Win32.Primitives/src/System/ComponentModel/Win32Exception.cs +++ b/external/corefx/src/Microsoft.Win32.Primitives/src/System/ComponentModel/Win32Exception.cs @@ -2,14 +2,18 @@ // 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.Globalization; using System.Runtime.InteropServices; using System.Runtime.Serialization; +using System.Text; namespace System.ComponentModel { /// /// The exception that is thrown for a Win32 error code. /// + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public partial class Win32Exception : ExternalException, ISerializable { private const int E_FAIL = unchecked((int)0x80004005); @@ -53,11 +57,69 @@ namespace System.ComponentModel NativeErrorCode = Marshal.GetLastWin32Error(); } - protected Win32Exception(SerializationInfo info, StreamingContext context) : base(info, context) { } + protected Win32Exception(SerializationInfo info, StreamingContext context) : base(info, context) + { + NativeErrorCode = info.GetInt32(nameof(NativeErrorCode)); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue(nameof(NativeErrorCode), NativeErrorCode); + } /// /// Represents the Win32 error code associated with this exception. This field is read-only. /// public int NativeErrorCode { get; } + + /// + /// Returns a string that contains the , or , or both. + /// + /// A string that represents the , or , or both. + public override string ToString() + { + if (NativeErrorCode == 0 || NativeErrorCode == HResult) + { + return base.ToString(); + } + + string message = Message; + string className = GetType().ToString(); + StringBuilder s = new StringBuilder(className); + string nativeErrorString = NativeErrorCode < 0 + ? string.Format(CultureInfo.InvariantCulture, "0x{0:X8}", NativeErrorCode) + : NativeErrorCode.ToString(CultureInfo.InvariantCulture); + if (HResult == E_FAIL) + { + s.AppendFormat(CultureInfo.InvariantCulture, " ({0})", nativeErrorString); + } + else + { + s.AppendFormat(CultureInfo.InvariantCulture, " ({0:X8}, {1})", HResult, nativeErrorString); + } + + if (!(String.IsNullOrEmpty(message))) + { + s.Append(": "); + s.Append(message); + } + + Exception innerException = InnerException; + if (innerException != null) + { + s.Append(" ---> "); + s.Append(innerException.ToString()); + } + + string stackTrace = StackTrace; + if (stackTrace != null) + { + s.AppendLine(); + s.Append(stackTrace); + } + + return s.ToString(); + } } } diff --git a/external/corefx/src/Microsoft.Win32.Primitives/tests/Win32ExceptionTests.cs b/external/corefx/src/Microsoft.Win32.Primitives/tests/Win32ExceptionTests.cs index f9956ac08b..8d5a11a407 100644 --- a/external/corefx/src/Microsoft.Win32.Primitives/tests/Win32ExceptionTests.cs +++ b/external/corefx/src/Microsoft.Win32.Primitives/tests/Win32ExceptionTests.cs @@ -2,6 +2,7 @@ // 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.Globalization; using System.Runtime.InteropServices; using System.Runtime.Serialization.Formatters.Tests; using System.Text; @@ -16,6 +17,7 @@ namespace System.ComponentModel.Tests private const int FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000; private const int ERROR_INSUFFICIENT_BUFFER = 0x7A; private const int FirstPassBufferSize = 256; + private const int E_FAIL = unchecked((int)0x80004005); [DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "FormatMessageW", SetLastError = true, BestFitMapping = true)] private static extern int FormatMessage( @@ -49,6 +51,12 @@ namespace System.ComponentModel.Tests { int error = 5; string message = "This is an error message."; + string toStringStart = string.Format( + CultureInfo.InvariantCulture, + "{0} ({1})", + typeof(Win32Exception).ToString(), + PlatformDetection.IsFullFramework ? $"0x{E_FAIL:X8}" : error.ToString(CultureInfo.InvariantCulture)); + Exception innerException = new FormatException(); // Test each of the constructors and validate the properties of the resulting instance @@ -59,6 +67,7 @@ namespace System.ComponentModel.Tests ex = new Win32Exception(error); Assert.Equal(expected: E_FAIL, actual: ex.HResult); Assert.Equal(expected: error, actual: ex.NativeErrorCode); + Assert.StartsWith(expectedStartString: toStringStart, actualString: ex.ToString(), comparisonType: StringComparison.Ordinal); ex = new Win32Exception(message); Assert.Equal(expected: E_FAIL, actual: ex.HResult); @@ -68,6 +77,7 @@ namespace System.ComponentModel.Tests Assert.Equal(expected: E_FAIL, actual: ex.HResult); Assert.Equal(expected: error, actual: ex.NativeErrorCode); Assert.Equal(expected: message, actual: ex.Message); + Assert.StartsWith(expectedStartString: toStringStart, actualString: ex.ToString(), comparisonType: StringComparison.Ordinal); ex = new Win32Exception(message, innerException); Assert.Equal(expected: E_FAIL, actual: ex.HResult); @@ -75,8 +85,6 @@ namespace System.ComponentModel.Tests Assert.Same(expected: innerException, actual: ex.InnerException); } - private const int E_FAIL = unchecked((int)0x80004005); - [Fact] [PlatformSpecific(TestPlatforms.Windows)] // Uses P/Invokes to check whether the exception resource length >256 chars public static void InstantiateExceptionWithLongErrorString() @@ -98,11 +106,5 @@ namespace System.ComponentModel.Tests Assert.Equal(expected: "Unknown error (0x23)", actual: ex.Message); } } - - [Fact] - public static void Deserialize_NetCore_ThrowsPlatformNotSupportedException() - { - BinaryFormatterHelpers.AssertExceptionDeserializationFails(); - } } } diff --git a/external/corefx/src/Microsoft.Win32.Registry.AccessControl/ref/Microsoft.Win32.Registry.AccessControl.cs b/external/corefx/src/Microsoft.Win32.Registry.AccessControl/ref/Microsoft.Win32.Registry.AccessControl.cs index 7f3797b6b0..687eefcb06 100644 --- a/external/corefx/src/Microsoft.Win32.Registry.AccessControl/ref/Microsoft.Win32.Registry.AccessControl.cs +++ b/external/corefx/src/Microsoft.Win32.Registry.AccessControl/ref/Microsoft.Win32.Registry.AccessControl.cs @@ -11,11 +11,10 @@ namespace Microsoft.Win32 { - [System.Security.SecurityCriticalAttribute] public static partial class RegistryAclExtensions { public static System.Security.AccessControl.RegistrySecurity GetAccessControl(this Microsoft.Win32.RegistryKey key) { throw null; } public static System.Security.AccessControl.RegistrySecurity GetAccessControl(this Microsoft.Win32.RegistryKey key, System.Security.AccessControl.AccessControlSections includeSections) { throw null; } public static void SetAccessControl(this Microsoft.Win32.RegistryKey key, System.Security.AccessControl.RegistrySecurity registrySecurity) { } } -} \ No newline at end of file +} diff --git a/external/corefx/src/Microsoft.Win32.Registry/Microsoft.Win32.Registry.sln b/external/corefx/src/Microsoft.Win32.Registry/Microsoft.Win32.Registry.sln index 75f92de41b..122533c637 100644 --- a/external/corefx/src/Microsoft.Win32.Registry/Microsoft.Win32.Registry.sln +++ b/external/corefx/src/Microsoft.Win32.Registry/Microsoft.Win32.Registry.sln @@ -30,10 +30,10 @@ Global {20A2BA2C-5517-483F-8FFE-643441A59852}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU {20A2BA2C-5517-483F-8FFE-643441A59852}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU {20A2BA2C-5517-483F-8FFE-643441A59852}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU - {D3F18ACC-D327-4ABB-BA6C-E9C34A041B2F}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU - {D3F18ACC-D327-4ABB-BA6C-E9C34A041B2F}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU - {D3F18ACC-D327-4ABB-BA6C-E9C34A041B2F}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU - {D3F18ACC-D327-4ABB-BA6C-E9C34A041B2F}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {D3F18ACC-D327-4ABB-BA6C-E9C34A041B2F}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {D3F18ACC-D327-4ABB-BA6C-E9C34A041B2F}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {D3F18ACC-D327-4ABB-BA6C-E9C34A041B2F}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {D3F18ACC-D327-4ABB-BA6C-E9C34A041B2F}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU {EEC02D4E-217E-4B4D-A7DA-5038FAD44A18}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU {EEC02D4E-217E-4B4D-A7DA-5038FAD44A18}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU {EEC02D4E-217E-4B4D-A7DA-5038FAD44A18}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU diff --git a/external/corefx/src/Microsoft.Win32.Registry/ref/Microsoft.Win32.Registry.cs b/external/corefx/src/Microsoft.Win32.Registry/ref/Microsoft.Win32.Registry.cs index 55c78d7b22..ace3e69ec6 100644 --- a/external/corefx/src/Microsoft.Win32.Registry/ref/Microsoft.Win32.Registry.cs +++ b/external/corefx/src/Microsoft.Win32.Registry/ref/Microsoft.Win32.Registry.cs @@ -32,7 +32,7 @@ namespace Microsoft.Win32 public sealed partial class RegistryKey : System.MarshalByRefObject, System.IDisposable { internal RegistryKey() { } - public Microsoft.Win32.SafeHandles.SafeRegistryHandle Handle {[System.Security.SecurityCriticalAttribute]get { throw null; } } + public Microsoft.Win32.SafeHandles.SafeRegistryHandle Handle { get { throw null; } } public string Name { get { throw null; } } public int SubKeyCount { get { throw null; } } public int ValueCount { get { throw null; } } @@ -53,9 +53,7 @@ namespace Microsoft.Win32 public void Dispose() { } public void Close() { throw null; } public void Flush() { } - [System.Security.SecurityCriticalAttribute] public static Microsoft.Win32.RegistryKey FromHandle(Microsoft.Win32.SafeHandles.SafeRegistryHandle handle) { throw null; } - [System.Security.SecurityCriticalAttribute] public static Microsoft.Win32.RegistryKey FromHandle(Microsoft.Win32.SafeHandles.SafeRegistryHandle handle, Microsoft.Win32.RegistryView view) { throw null; } public string[] GetSubKeyNames() { throw null; } public object GetValue(string name) { throw null; } @@ -72,9 +70,7 @@ namespace Microsoft.Win32 public Microsoft.Win32.RegistryKey OpenSubKey(string name, System.Security.AccessControl.RegistryRights rights) { throw null; } public Microsoft.Win32.RegistryKey OpenSubKey(string name, Microsoft.Win32.RegistryKeyPermissionCheck permissionCheck, System.Security.AccessControl.RegistryRights rights) { throw null; } public System.Security.AccessControl.RegistrySecurity GetAccessControl() { throw null; } - [System.Security.SecuritySafeCriticalAttribute] public System.Security.AccessControl.RegistrySecurity GetAccessControl(System.Security.AccessControl.AccessControlSections includeSections) { throw null; } - [System.Security.SecuritySafeCriticalAttribute] public void SetAccessControl(System.Security.AccessControl.RegistrySecurity registrySecurity) { throw null; } public void SetValue(string name, object value) { } public void SetValue(string name, object value, Microsoft.Win32.RegistryValueKind valueKind) { } @@ -118,13 +114,10 @@ namespace Microsoft.Win32 } namespace Microsoft.Win32.SafeHandles { - [System.Security.SecurityCriticalAttribute] public sealed partial class SafeRegistryHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid { - [System.Security.SecurityCriticalAttribute] public SafeRegistryHandle(System.IntPtr preexistingHandle, bool ownsHandle) : base(default(bool)) { } - public override bool IsInvalid { [System.Security.SecurityCriticalAttribute]get { throw null; } } - [System.Security.SecurityCriticalAttribute] + public override bool IsInvalid { get { throw null; } } protected override bool ReleaseHandle() { throw null; } } } @@ -148,7 +141,6 @@ namespace System.Security.AccessControl TakeOwnership = 524288, WriteKey = 131078, } - [System.Security.SecurityCriticalAttribute] public sealed partial class RegistryAccessRule : System.Security.AccessControl.AccessRule { public RegistryAccessRule(System.Security.Principal.IdentityReference identity, System.Security.AccessControl.RegistryRights registryRights, System.Security.AccessControl.AccessControlType type) : base(default(System.Security.Principal.IdentityReference), default(int), default(bool), default(System.Security.AccessControl.InheritanceFlags), default(System.Security.AccessControl.PropagationFlags), default(System.Security.AccessControl.AccessControlType)) { } @@ -157,14 +149,12 @@ namespace System.Security.AccessControl public RegistryAccessRule(string identity, System.Security.AccessControl.RegistryRights registryRights, System.Security.AccessControl.InheritanceFlags inheritanceFlags, System.Security.AccessControl.PropagationFlags propagationFlags, System.Security.AccessControl.AccessControlType type) : base(default(System.Security.Principal.IdentityReference), default(int), default(bool), default(System.Security.AccessControl.InheritanceFlags), default(System.Security.AccessControl.PropagationFlags), default(System.Security.AccessControl.AccessControlType)) { } public System.Security.AccessControl.RegistryRights RegistryRights { get { throw null; } } } - [System.Security.SecurityCriticalAttribute] public sealed partial class RegistryAuditRule : System.Security.AccessControl.AuditRule { public RegistryAuditRule(System.Security.Principal.IdentityReference identity, System.Security.AccessControl.RegistryRights registryRights, System.Security.AccessControl.InheritanceFlags inheritanceFlags, System.Security.AccessControl.PropagationFlags propagationFlags, System.Security.AccessControl.AuditFlags flags) : base(default(System.Security.Principal.IdentityReference), default(int), default(bool), default(System.Security.AccessControl.InheritanceFlags), default(System.Security.AccessControl.PropagationFlags), default(System.Security.AccessControl.AuditFlags)) { } public RegistryAuditRule(string identity, System.Security.AccessControl.RegistryRights registryRights, System.Security.AccessControl.InheritanceFlags inheritanceFlags, System.Security.AccessControl.PropagationFlags propagationFlags, System.Security.AccessControl.AuditFlags flags) : base(default(System.Security.Principal.IdentityReference), default(int), default(bool), default(System.Security.AccessControl.InheritanceFlags), default(System.Security.AccessControl.PropagationFlags), default(System.Security.AccessControl.AuditFlags)) { } public System.Security.AccessControl.RegistryRights RegistryRights { get { throw null; } } } - [System.Security.SecurityCriticalAttribute] public sealed partial class RegistrySecurity : System.Security.AccessControl.NativeObjectSecurity { public RegistrySecurity() : base(default(bool), default(System.Security.AccessControl.ResourceType)) { } diff --git a/external/corefx/src/Microsoft.Win32.Registry/src/Configurations.props b/external/corefx/src/Microsoft.Win32.Registry/src/Configurations.props index a109dc94d8..2e410e6ec3 100644 --- a/external/corefx/src/Microsoft.Win32.Registry/src/Configurations.props +++ b/external/corefx/src/Microsoft.Win32.Registry/src/Configurations.props @@ -1,11 +1,16 @@  - + + netstandard; netcoreapp2.0-Windows_NT; netcoreapp2.0-Unix; - netstandard; netfx-Windows_NT; + + + $(PackageConfigurations); + netcoreapp-Windows_NT; + netcoreapp-Unix; \ No newline at end of file diff --git a/external/corefx/src/Microsoft.Win32.Registry/src/Microsoft.Win32.Registry.csproj b/external/corefx/src/Microsoft.Win32.Registry/src/Microsoft.Win32.Registry.csproj index 6a0b6d8f82..97327470bc 100644 --- a/external/corefx/src/Microsoft.Win32.Registry/src/Microsoft.Win32.Registry.csproj +++ b/external/corefx/src/Microsoft.Win32.Registry/src/Microsoft.Win32.Registry.csproj @@ -19,11 +19,15 @@ + + + + - + Common\Interop\Windows\Interop.RegistryOptions.cs @@ -40,7 +44,7 @@ - + Common\Interop\Windows\Interop.Libraries.cs @@ -104,7 +108,7 @@ - + diff --git a/external/corefx/src/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.cs b/external/corefx/src/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.cs index aabcff9b61..578c732952 100644 --- a/external/corefx/src/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.cs +++ b/external/corefx/src/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.cs @@ -414,14 +414,12 @@ namespace Microsoft.Win32 return GetAccessControl(AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group); } - [SecuritySafeCritical] public RegistrySecurity GetAccessControl(AccessControlSections includeSections) { EnsureNotDisposed(); return new RegistrySecurity(Handle, Name, includeSections); } - [SecuritySafeCritical] public void SetAccessControl(RegistrySecurity registrySecurity) { EnsureWriteable(); diff --git a/external/corefx/src/Microsoft.Win32.Registry/src/System/Security/AccessControl/RegistrySecurity.FileSystem.cs b/external/corefx/src/Microsoft.Win32.Registry/src/System/Security/AccessControl/RegistrySecurity.FileSystem.cs index 5a1e57887d..d41a07e98f 100644 --- a/external/corefx/src/Microsoft.Win32.Registry/src/System/Security/AccessControl/RegistrySecurity.FileSystem.cs +++ b/external/corefx/src/Microsoft.Win32.Registry/src/System/Security/AccessControl/RegistrySecurity.FileSystem.cs @@ -8,7 +8,6 @@ namespace System.Security.AccessControl { public sealed partial class RegistrySecurity : NativeObjectSecurity { - [SecurityCritical] private static Exception _HandleErrorCodeCore(int errorCode, string name, SafeHandle handle, object context) { // TODO: Implement this diff --git a/external/corefx/src/Microsoft.Win32.Registry/src/System/Security/AccessControl/RegistrySecurity.Windows.cs b/external/corefx/src/Microsoft.Win32.Registry/src/System/Security/AccessControl/RegistrySecurity.Windows.cs index 40e4b91717..240a1c2bed 100644 --- a/external/corefx/src/Microsoft.Win32.Registry/src/System/Security/AccessControl/RegistrySecurity.Windows.cs +++ b/external/corefx/src/Microsoft.Win32.Registry/src/System/Security/AccessControl/RegistrySecurity.Windows.cs @@ -9,7 +9,6 @@ namespace System.Security.AccessControl { public sealed partial class RegistrySecurity : NativeObjectSecurity { - [SecurityCritical] private static Exception _HandleErrorCodeCore(int errorCode, string name, SafeHandle handle, object context) { Exception exception = null; diff --git a/external/corefx/src/Microsoft.Win32.Registry/src/System/Security/AccessControl/RegistrySecurity.cs b/external/corefx/src/Microsoft.Win32.Registry/src/System/Security/AccessControl/RegistrySecurity.cs index 382ab3bb62..fd8f244dfb 100644 --- a/external/corefx/src/Microsoft.Win32.Registry/src/System/Security/AccessControl/RegistrySecurity.cs +++ b/external/corefx/src/Microsoft.Win32.Registry/src/System/Security/AccessControl/RegistrySecurity.cs @@ -90,13 +90,11 @@ namespace System.Security.AccessControl { } - [SecurityCritical] internal RegistrySecurity(SafeRegistryHandle hKey, string name, AccessControlSections includeSections) : base(true, ResourceType.RegistryKey, hKey, includeSections, _HandleErrorCode, null) { } - [SecurityCritical] private static Exception _HandleErrorCode(int errorCode, string name, SafeHandle handle, object context) { return _HandleErrorCodeCore(errorCode, name, handle, context); @@ -138,7 +136,6 @@ namespace System.Security.AccessControl return persistRules; } - [SecurityCritical] internal void Persist(SafeRegistryHandle hKey, string keyName) { WriteLock(); diff --git a/external/corefx/src/Microsoft.Win32.Registry/tests/Microsoft.Win32.Registry.Tests.csproj b/external/corefx/src/Microsoft.Win32.Registry/tests/Microsoft.Win32.Registry.Tests.csproj index 83a3a3139d..f1591c192b 100644 --- a/external/corefx/src/Microsoft.Win32.Registry/tests/Microsoft.Win32.Registry.Tests.csproj +++ b/external/corefx/src/Microsoft.Win32.Registry/tests/Microsoft.Win32.Registry.Tests.csproj @@ -4,7 +4,6 @@ {20A2BA2C-5517-483F-8FFE-643441A59852} - @@ -65,4 +64,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/Microsoft.XmlSerializer.Generator/Microsoft.XmlSerializer.Generator.sln b/external/corefx/src/Microsoft.XmlSerializer.Generator/Microsoft.XmlSerializer.Generator.sln index 8245bf6092..40a342d07d 100644 --- a/external/corefx/src/Microsoft.XmlSerializer.Generator/Microsoft.XmlSerializer.Generator.sln +++ b/external/corefx/src/Microsoft.XmlSerializer.Generator/Microsoft.XmlSerializer.Generator.sln @@ -19,10 +19,10 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0D1E2954-A5C7-4B8C-932A-31EB4A96A737}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU - {0D1E2954-A5C7-4B8C-932A-31EB4A96A737}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU - {0D1E2954-A5C7-4B8C-932A-31EB4A96A737}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU - {0D1E2954-A5C7-4B8C-932A-31EB4A96A737}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {0D1E2954-A5C7-4B8C-932A-31EB4A96A737}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {0D1E2954-A5C7-4B8C-932A-31EB4A96A737}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {0D1E2954-A5C7-4B8C-932A-31EB4A96A737}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {0D1E2954-A5C7-4B8C-932A-31EB4A96A737}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU {80958E8B-2FEB-4F95-83F9-825CA1ED26F8}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU {80958E8B-2FEB-4F95-83F9-825CA1ED26F8}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU {80958E8B-2FEB-4F95-83F9-825CA1ED26F8}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU diff --git a/external/corefx/src/Microsoft.XmlSerializer.Generator/dir.props b/external/corefx/src/Microsoft.XmlSerializer.Generator/dir.props index b090298bf1..96d567877b 100644 --- a/external/corefx/src/Microsoft.XmlSerializer.Generator/dir.props +++ b/external/corefx/src/Microsoft.XmlSerializer.Generator/dir.props @@ -3,7 +3,7 @@ preview1 - 1.0.0 + 1.1.0 true 1.0.0.0 $(AssemblyVersion) diff --git a/external/corefx/src/Microsoft.XmlSerializer.Generator/pkg/Microsoft.XmlSerializer.Generator.pkgproj b/external/corefx/src/Microsoft.XmlSerializer.Generator/pkg/Microsoft.XmlSerializer.Generator.pkgproj index fe2f64b2e0..1278907b24 100644 --- a/external/corefx/src/Microsoft.XmlSerializer.Generator/pkg/Microsoft.XmlSerializer.Generator.pkgproj +++ b/external/corefx/src/Microsoft.XmlSerializer.Generator/pkg/Microsoft.XmlSerializer.Generator.pkgproj @@ -5,7 +5,10 @@ + https://go.microsoft.com/fwlink/?LinkID=863421 + https://go.microsoft.com/fwlink/?linkid=858594 true + false diff --git a/external/corefx/src/Microsoft.XmlSerializer.Generator/pkg/build/Microsoft.XmlSerializer.Generator.targets b/external/corefx/src/Microsoft.XmlSerializer.Generator/pkg/build/Microsoft.XmlSerializer.Generator.targets index 8ebc0d32c4..d1ae79fec1 100644 --- a/external/corefx/src/Microsoft.XmlSerializer.Generator/pkg/build/Microsoft.XmlSerializer.Generator.targets +++ b/external/corefx/src/Microsoft.XmlSerializer.Generator/pkg/build/Microsoft.XmlSerializer.Generator.targets @@ -1,27 +1,61 @@ - $(AssemblyName).XmlSerializers - $(IntermediateOutputPath)$(SerializerName).dll - $(IntermediateOutputPath)$(SerializerName).pdb - $(IntermediateOutputPath)$(SerializerName).cs - SGEN : warning SGEN1: Fail to generate the serializer for $(AssemblyName).dll. Please follow the instructions in https://go.microsoft.com/fwlink/?linkid=858594 and try again. + <_SerializationAssemblyName>$(AssemblyName).XmlSerializers + <_SerializerDllIntermediateFolder>$(IntermediateOutputPath)$(_SerializationAssemblyName).dll + <_SerializerPdbIntermediateFolder>$(IntermediateOutputPath)$(_SerializationAssemblyName).pdb + <_SerializerCsIntermediateFolder>$(IntermediateOutputPath)$(_SerializationAssemblyName).cs + <_SGenWarningText>SGEN : warning SGEN1: Fail to generate the serializer for $(AssemblyName)$(TargetExt). Please follow the instructions at https://go.microsoft.com/fwlink/?linkid=858594 and try again. + <_SerializationAssemblyDisabledWarnings>$(NoWarn);219;162;$(SerializationAssemblyDisabledWarnings) - - - + + + - - - - - + + + + + + - + - + + + + + + <_SearchSerializationAssembly Include="@(Reference)"> + %(SerializationAssembly.Identity) + %(SerializationAssembly.SerializationType) + + <_TargetSerializationAssembly Include="@(_SearchSerializationAssembly)" Condition="$([System.String]::new('%(_SearchSerializationAssembly.Identity)').EndsWith('%(_SearchSerializationAssembly.AssemblyName).dll'))" /> + <_ReferenceSerializationAssemblyName Include="%(SerializationAssembly.Identity).XmlSerializers" /> + <_ReferenceSerializerIntermediateFolder Include="$(IntermediateOutputPath)%(_ReferenceSerializationAssemblyName.Identity)" /> + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/Microsoft.XmlSerializer.Generator/src/Microsoft.XmlSerializer.Generator.csproj b/external/corefx/src/Microsoft.XmlSerializer.Generator/src/Microsoft.XmlSerializer.Generator.csproj index 5f67201f4c..71e1efe5da 100644 --- a/external/corefx/src/Microsoft.XmlSerializer.Generator/src/Microsoft.XmlSerializer.Generator.csproj +++ b/external/corefx/src/Microsoft.XmlSerializer.Generator/src/Microsoft.XmlSerializer.Generator.csproj @@ -6,8 +6,6 @@ dotnet-Microsoft.XmlSerializer.Generator true ..\..\System.Private.Xml\src\Resources\Strings.resx - $(DefineConstants);XMLSERIALIZERGENERATOR - ..\..\System.Private.Xml\src Exe .dll $(NoWarn);0169;0414;0649 @@ -15,144 +13,8 @@ - - - - - System\Xml\BinHexDecoder.cs - - - System\Xml\BinHexEncoder.cs - - - System\Xml\Core\LocalAppContextSwitches.cs - - - System\Xml\Extensions\ExtensionMethods.cs - - - System\Xml\Serialization\CodeGenerationoptions.cs - - - System\Xml\Serialization\CodeIdentifier.cs - - - System\Xml\Serialization\CodeIdentifiers.cs - - - System\Xml\Serialization\Compilation.cs - - - System\Xml\Serialization\Compiler.cs - - - System\Xml\Serialization\Configuration\DateTimeSerializationSection.cs - - - System\Xml\Serialization\Globals.cs - - - System\Xml\Serialization\indentedWriter.cs - - - System\Xml\Serialization\IXmlTextParser.cs - - - System\Xml\Serialization\Mappings.cs - - - System\Xml\Serialization\Models.cs - - - System\Xml\Serialization\NameTable.cs - - - System\Xml\Serialization\PrimitiveXmlSerializers.cs - - - System\Xml\Serialization\SoapReflectionImporter.cs - - - System\Xml\Serialization\SoapSchemamember.cs - - - System\Xml\Serialization\TypeCode.cs - - - System\Xml\Serialization\TypeExtensions.cs - - - System\Xml\Serialization\Types.cs - - - System\Xml\Serialization\XmlAttributeOverrides.cs - - - System\Xml\Serialization\XmlAttributes.cs - - - System\Xml\Serialization\XmlCountingReader.cs - - - System\Xml\Serialization\Xmlcustomformatter.cs - - - System\Xml\Serialization\XmlMapping.cs - - - System\Xml\Serialization\XmlMemberMapping.cs - - - System\Xml\Serialization\XmlMembersMapping.cs - - - System\Xml\Serialization\XmlReflectionImporter.cs - - - System\Xml\Serialization\XmlReflectionMember.cs - - - System\Xml\Serialization\XmlSerializationGeneratedCode.cs - - - System\Xml\Serialization\XmlSerializationReader.cs - - - System\Xml\Serialization\XmlSerializationWriter.cs - - - System\Xml\Serialization\XmlSerializer.cs - - - System\Xml\Serialization\XmlSerializerFactory.cs - - - System\Xml\Serialization\XmlSerializerNamespaces.cs - - - System\Xml\Serialization\XmlTypeMapping.cs - - - System\Xml\Serialization\_Events.cs - - - System\Xml\XmlCharType.cs - - - System\Xml\XmlReservedNs.cs - - - - - - - - - - - diff --git a/external/corefx/src/Microsoft.XmlSerializer.Generator/src/Microsoft/XmlSerializer/Generator/XmlSerializationGeneratedCode.cs b/external/corefx/src/Microsoft.XmlSerializer.Generator/src/Microsoft/XmlSerializer/Generator/XmlSerializationGeneratedCode.cs deleted file mode 100644 index 4c62f2ec61..0000000000 --- a/external/corefx/src/Microsoft.XmlSerializer.Generator/src/Microsoft/XmlSerializer/Generator/XmlSerializationGeneratedCode.cs +++ /dev/null @@ -1,399 +0,0 @@ -// 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. - -namespace Microsoft.XmlSerializer.Generator -{ - using System; - using System.IO; - using System.Collections; - using System.ComponentModel; - using System.Threading; - using System.Reflection; - using System.Security; - using System.Globalization; - using System.Xml; - using System.Xml.Serialization; - - internal class XmlSerializationCodeGen - { - private IndentedWriter _writer; - private int _nextMethodNumber = 0; - private Hashtable _methodNames = new Hashtable(); - private ReflectionAwareCodeGen _raCodeGen; - private TypeScope[] _scopes; - private TypeDesc _stringTypeDesc = null; - private TypeDesc _qnameTypeDesc = null; - private string _access; - private string _className; - private TypeMapping[] _referencedMethods; - private int _references = 0; - private Hashtable _generatedMethods = new Hashtable(); - - internal XmlSerializationCodeGen(IndentedWriter writer, TypeScope[] scopes, string access, string className) - { - _writer = writer; - _scopes = scopes; - if (scopes.Length > 0) - { - _stringTypeDesc = scopes[0].GetTypeDesc(typeof(string)); - _qnameTypeDesc = scopes[0].GetTypeDesc(typeof(XmlQualifiedName)); - } - _raCodeGen = new ReflectionAwareCodeGen(writer); - _className = className; - _access = access; - } - - internal IndentedWriter Writer { get { return _writer; } } - internal int NextMethodNumber { get { return _nextMethodNumber; } set { _nextMethodNumber = value; } } - internal ReflectionAwareCodeGen RaCodeGen { get { return _raCodeGen; } } - internal TypeDesc StringTypeDesc { get { return _stringTypeDesc; } } - internal TypeDesc QnameTypeDesc { get { return _qnameTypeDesc; } } - internal string ClassName { get { return _className; } } - internal string Access { get { return _access; } } - internal TypeScope[] Scopes { get { return _scopes; } } - internal Hashtable MethodNames { get { return _methodNames; } } - internal Hashtable GeneratedMethods { get { return _generatedMethods; } } - - internal virtual void GenerateMethod(TypeMapping mapping) { } - - internal void GenerateReferencedMethods() - { - while (_references > 0) - { - TypeMapping mapping = _referencedMethods[--_references]; - GenerateMethod(mapping); - } - } - - internal string ReferenceMapping(TypeMapping mapping) - { - if (!mapping.IsSoap) - { - if (_generatedMethods[mapping] == null) - { - _referencedMethods = EnsureArrayIndex(_referencedMethods, _references); - _referencedMethods[_references++] = mapping; - } - } - return (string)_methodNames[mapping]; - } - - private TypeMapping[] EnsureArrayIndex(TypeMapping[] a, int index) - { - if (a == null) return new TypeMapping[32]; - if (index < a.Length) return a; - TypeMapping[] b = new TypeMapping[a.Length + 32]; - Array.Copy(a, b, index); - return b; - } - - internal void WriteQuotedCSharpString(string value) - { - _raCodeGen.WriteQuotedCSharpString(value); - } - - internal void GenerateHashtableGetBegin(string privateName, string publicName) - { - _writer.Write(typeof(Hashtable).FullName); - _writer.Write(" "); - _writer.Write(privateName); - _writer.WriteLine(" = null;"); - _writer.Write("public override "); - _writer.Write(typeof(Hashtable).FullName); - - _writer.Write(" "); - _writer.Write(publicName); - _writer.WriteLine(" {"); - _writer.Indent++; - - _writer.WriteLine("get {"); - _writer.Indent++; - - _writer.Write("if ("); - _writer.Write(privateName); - _writer.WriteLine(" == null) {"); - _writer.Indent++; - - _writer.Write(typeof(Hashtable).FullName); - _writer.Write(" _tmp = new "); - _writer.Write(typeof(Hashtable).FullName); - _writer.WriteLine("();"); - } - - internal void GenerateHashtableGetEnd(string privateName) - { - _writer.Write("if ("); - _writer.Write(privateName); - _writer.Write(" == null) "); - _writer.Write(privateName); - _writer.WriteLine(" = _tmp;"); - _writer.Indent--; - _writer.WriteLine("}"); - - _writer.Write("return "); - _writer.Write(privateName); - _writer.WriteLine(";"); - _writer.Indent--; - _writer.WriteLine("}"); - - _writer.Indent--; - _writer.WriteLine("}"); - } - internal void GeneratePublicMethods(string privateName, string publicName, string[] methods, XmlMapping[] xmlMappings) - { - GenerateHashtableGetBegin(privateName, publicName); - if (methods != null && methods.Length != 0 && xmlMappings != null && xmlMappings.Length == methods.Length) - { - for (int i = 0; i < methods.Length; i++) - { - if (methods[i] == null) - continue; - _writer.Write("_tmp["); - WriteQuotedCSharpString(xmlMappings[i].Key); - _writer.Write("] = "); - WriteQuotedCSharpString(methods[i]); - _writer.WriteLine(";"); - } - } - GenerateHashtableGetEnd(privateName); - } - - internal void GenerateSupportedTypes(Type[] types) - { - _writer.Write("public override "); - _writer.Write(typeof(bool).FullName); - _writer.Write(" CanSerialize("); - _writer.Write(typeof(Type).FullName); - _writer.WriteLine(" type) {"); - _writer.Indent++; - Hashtable uniqueTypes = new Hashtable(); - for (int i = 0; i < types.Length; i++) - { - Type type = types[i]; - - if (type == null) - continue; - if (!type.IsPublic && !type.IsNestedPublic) - continue; - if (uniqueTypes[type] != null) - continue; - if (DynamicAssemblies.IsTypeDynamic(type)) - continue; - if (type.IsGenericType || type.ContainsGenericParameters && DynamicAssemblies.IsTypeDynamic(type.GetGenericArguments())) - continue; - uniqueTypes[type] = type; - _writer.Write("if (type == typeof("); - _writer.Write(CodeIdentifier.GetCSharpName(type)); - _writer.WriteLine(")) return true;"); - } - _writer.WriteLine("return false;"); - _writer.Indent--; - _writer.WriteLine("}"); - } - - internal string GenerateBaseSerializer(string baseSerializer, string readerClass, string writerClass, CodeIdentifiers classes) - { - baseSerializer = CodeIdentifier.MakeValid(baseSerializer); - baseSerializer = classes.AddUnique(baseSerializer, baseSerializer); - - _writer.WriteLine(); - _writer.Write("public abstract class "); - _writer.Write(CodeIdentifier.GetCSharpName(baseSerializer)); - _writer.Write(" : "); - _writer.Write(typeof(System.Xml.Serialization.XmlSerializer).FullName); - _writer.WriteLine(" {"); - _writer.Indent++; - - _writer.Write("protected override "); - _writer.Write(typeof(System.Xml.Serialization.XmlSerializationReader).FullName); - _writer.WriteLine(" CreateReader() {"); - _writer.Indent++; - _writer.Write("return new "); - _writer.Write(readerClass); - _writer.WriteLine("();"); - _writer.Indent--; - _writer.WriteLine("}"); - - _writer.Write("protected override "); - _writer.Write(typeof(System.Xml.Serialization.XmlSerializationWriter).FullName); - _writer.WriteLine(" CreateWriter() {"); - _writer.Indent++; - _writer.Write("return new "); - _writer.Write(writerClass); - _writer.WriteLine("();"); - _writer.Indent--; - _writer.WriteLine("}"); - - _writer.Indent--; - _writer.WriteLine("}"); - - return baseSerializer; - } - - internal string GenerateTypedSerializer(string readMethod, string writeMethod, XmlMapping mapping, CodeIdentifiers classes, string baseSerializer, string readerClass, string writerClass) - { - string serializerName = CodeIdentifier.MakeValid(Accessor.UnescapeName(mapping.Accessor.Mapping.TypeDesc.Name)); - serializerName = classes.AddUnique(serializerName + "Serializer", mapping); - - _writer.WriteLine(); - _writer.Write("public sealed class "); - _writer.Write(CodeIdentifier.GetCSharpName(serializerName)); - _writer.Write(" : "); - _writer.Write(baseSerializer); - _writer.WriteLine(" {"); - _writer.Indent++; - - _writer.WriteLine(); - _writer.Write("public override "); - _writer.Write(typeof(bool).FullName); - _writer.Write(" CanDeserialize("); - _writer.Write(typeof(XmlReader).FullName); - _writer.WriteLine(" xmlReader) {"); - _writer.Indent++; - - if (mapping.Accessor.Any) - { - _writer.WriteLine("return true;"); - } - else - { - _writer.Write("return xmlReader.IsStartElement("); - WriteQuotedCSharpString(mapping.Accessor.Name); - _writer.Write(", "); - WriteQuotedCSharpString(mapping.Accessor.Namespace); - _writer.WriteLine(");"); - } - _writer.Indent--; - _writer.WriteLine("}"); - - if (writeMethod != null) - { - _writer.WriteLine(); - _writer.Write("protected override void Serialize(object objectToSerialize, "); - _writer.Write(typeof(System.Xml.Serialization.XmlSerializationWriter).FullName); - _writer.WriteLine(" writer) {"); - _writer.Indent++; - _writer.Write("(("); - _writer.Write(writerClass); - _writer.Write(")writer)."); - _writer.Write(writeMethod); - _writer.Write("("); - if (mapping is XmlMembersMapping) - { - _writer.Write("(object[])"); - } - _writer.WriteLine("objectToSerialize);"); - _writer.Indent--; - _writer.WriteLine("}"); - } - if (readMethod != null) - { - _writer.WriteLine(); - _writer.Write("protected override object Deserialize("); - _writer.Write(typeof(System.Xml.Serialization.XmlSerializationReader).FullName); - _writer.WriteLine(" reader) {"); - _writer.Indent++; - _writer.Write("return (("); - _writer.Write(readerClass); - _writer.Write(")reader)."); - _writer.Write(readMethod); - _writer.WriteLine("();"); - _writer.Indent--; - _writer.WriteLine("}"); - } - _writer.Indent--; - _writer.WriteLine("}"); - - return serializerName; - } - - private void GenerateTypedSerializers(Hashtable serializers) - { - string privateName = "typedSerializers"; - GenerateHashtableGetBegin(privateName, "TypedSerializers"); - - foreach (string key in serializers.Keys) - { - _writer.Write("_tmp.Add("); - WriteQuotedCSharpString(key); - _writer.Write(", new "); - _writer.Write((string)serializers[key]); - _writer.WriteLine("());"); - } - GenerateHashtableGetEnd("typedSerializers"); - } - - //GenerateGetSerializer(serializers, xmlMappings); - private void GenerateGetSerializer(Hashtable serializers, XmlMapping[] xmlMappings) - { - _writer.Write("public override "); - _writer.Write(typeof(System.Xml.Serialization.XmlSerializer).FullName); - _writer.Write(" GetSerializer("); - _writer.Write(typeof(Type).FullName); - _writer.WriteLine(" type) {"); - _writer.Indent++; - - for (int i = 0; i < xmlMappings.Length; i++) - { - if (xmlMappings[i] is XmlTypeMapping) - { - Type type = xmlMappings[i].Accessor.Mapping.TypeDesc.Type; - if (type == null) - continue; - if (!type.IsPublic && !type.IsNestedPublic) - continue; - if (DynamicAssemblies.IsTypeDynamic(type)) - continue; - if (type.IsGenericType || type.ContainsGenericParameters && DynamicAssemblies.IsTypeDynamic(type.GetGenericArguments())) - continue; - _writer.Write("if (type == typeof("); - _writer.Write(CodeIdentifier.GetCSharpName(type)); - _writer.Write(")) return new "); - _writer.Write((string)serializers[xmlMappings[i].Key]); - _writer.WriteLine("();"); - } - } - _writer.WriteLine("return null;"); - _writer.Indent--; - _writer.WriteLine("}"); - } - - internal void GenerateSerializerContract(string className, XmlMapping[] xmlMappings, Type[] types, string readerType, string[] readMethods, string writerType, string[] writerMethods, Hashtable serializers) - { - _writer.WriteLine(); - _writer.Write("public class XmlSerializerContract : global::"); - _writer.Write(typeof(System.Xml.Serialization.XmlSerializerImplementation).FullName); - _writer.WriteLine(" {"); - _writer.Indent++; - - _writer.Write("public override global::"); - _writer.Write(typeof(System.Xml.Serialization.XmlSerializationReader).FullName); - _writer.Write(" Reader { get { return new "); - _writer.Write(readerType); - _writer.WriteLine("(); } }"); - - _writer.Write("public override global::"); - _writer.Write(typeof(System.Xml.Serialization.XmlSerializationWriter).FullName); - _writer.Write(" Writer { get { return new "); - _writer.Write(writerType); - _writer.WriteLine("(); } }"); - - GeneratePublicMethods(nameof(readMethods), "ReadMethods", readMethods, xmlMappings); - GeneratePublicMethods("writeMethods", "WriteMethods", writerMethods, xmlMappings); - GenerateTypedSerializers(serializers); - GenerateSupportedTypes(types); - GenerateGetSerializer(serializers, xmlMappings); - - _writer.Indent--; - _writer.WriteLine("}"); - } - - internal static bool IsWildcard(SpecialMapping mapping) - { - if (mapping is SerializableMapping) - return ((SerializableMapping)mapping).IsAny; - return mapping.TypeDesc.CanBeElementValue; - } - } -} diff --git a/external/corefx/src/Microsoft.XmlSerializer.Generator/src/Microsoft/XmlSerializer/Generator/XmlSerializationReader.cs.REMOVED.git-id b/external/corefx/src/Microsoft.XmlSerializer.Generator/src/Microsoft/XmlSerializer/Generator/XmlSerializationReader.cs.REMOVED.git-id deleted file mode 100644 index dcb64c5894..0000000000 --- a/external/corefx/src/Microsoft.XmlSerializer.Generator/src/Microsoft/XmlSerializer/Generator/XmlSerializationReader.cs.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -655dfaa141f8b465e43fe5658200a965c3be83e9 \ No newline at end of file diff --git a/external/corefx/src/Microsoft.XmlSerializer.Generator/src/Microsoft/XmlSerializer/Generator/XmlSerializationWriter.cs b/external/corefx/src/Microsoft.XmlSerializer.Generator/src/Microsoft/XmlSerializer/Generator/XmlSerializationWriter.cs deleted file mode 100644 index 22c8fe6336..0000000000 --- a/external/corefx/src/Microsoft.XmlSerializer.Generator/src/Microsoft/XmlSerializer/Generator/XmlSerializationWriter.cs +++ /dev/null @@ -1,2229 +0,0 @@ -// 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. - -namespace Microsoft.XmlSerializer.Generator -{ - using System; - using System.IO; - using System.Collections; - using System.Reflection; - using System.Reflection.Emit; - using System.Xml.Schema; - using System.ComponentModel; - using System.Diagnostics; - using System.Globalization; - using System.Text; - using System.Threading; - using System.Runtime.Versioning; - using System.Collections.Generic; - using System.Xml; - using System.Xml.Serialization; - - internal class XmlSerializationWriterCodeGen : XmlSerializationCodeGen - { - internal XmlSerializationWriterCodeGen(IndentedWriter writer, TypeScope[] scopes, string access, string className) : base(writer, scopes, access, className) - { - } - - internal void GenerateBegin() - { - Writer.Write(Access); - Writer.Write(" class "); - Writer.Write(ClassName); - Writer.Write(" : "); - Writer.Write(typeof(System.Xml.Serialization.XmlSerializationWriter).FullName); - Writer.WriteLine(" {"); - Writer.Indent++; - - foreach (TypeScope scope in Scopes) - { - foreach (TypeMapping mapping in scope.TypeMappings) - { - if (mapping is StructMapping || mapping is EnumMapping) - { - MethodNames.Add(mapping, NextMethodName(mapping.TypeDesc.Name)); - } - } - RaCodeGen.WriteReflectionInit(scope); - } - - // pre-generate write methods only for the encoded soap - foreach (TypeScope scope in Scopes) - { - foreach (TypeMapping mapping in scope.TypeMappings) - { - if (!mapping.IsSoap) - continue; - - if (mapping is StructMapping) - WriteStructMethod((StructMapping)mapping); - else if (mapping is EnumMapping) - WriteEnumMethod((EnumMapping)mapping); - } - } - } - - internal override void GenerateMethod(TypeMapping mapping) - { - if (GeneratedMethods.Contains(mapping)) - return; - - GeneratedMethods[mapping] = mapping; - if (mapping is StructMapping) - { - WriteStructMethod((StructMapping)mapping); - } - else if (mapping is EnumMapping) - { - WriteEnumMethod((EnumMapping)mapping); - } - } - internal void GenerateEnd() - { - GenerateReferencedMethods(); - GenerateInitCallbacksMethod(); - Writer.Indent--; - Writer.WriteLine("}"); - } - - internal string GenerateElement(XmlMapping xmlMapping) - { - if (!xmlMapping.IsWriteable) - return null; - if (!xmlMapping.GenerateSerializer) - throw new ArgumentException(SR.XmlInternalError, nameof(xmlMapping)); - if (xmlMapping is XmlTypeMapping) - return GenerateTypeElement((XmlTypeMapping)xmlMapping); - else if (xmlMapping is XmlMembersMapping) - return GenerateMembersElement((XmlMembersMapping)xmlMapping); - else - throw new ArgumentException(SR.XmlInternalError, nameof(xmlMapping)); - } - - private void GenerateInitCallbacksMethod() - { - Writer.WriteLine(); - Writer.WriteLine("protected override void InitCallbacks() {"); - Writer.Indent++; - - foreach (TypeScope scope in Scopes) - { - foreach (TypeMapping typeMapping in scope.TypeMappings) - { - if (typeMapping.IsSoap && - (typeMapping is StructMapping || typeMapping is EnumMapping) && - !typeMapping.TypeDesc.IsRoot) - { - string methodName = (string)MethodNames[typeMapping]; - Writer.Write("AddWriteCallback("); - Writer.Write(RaCodeGen.GetStringForTypeof(typeMapping.TypeDesc.CSharpName, typeMapping.TypeDesc.UseReflection)); - Writer.Write(", "); - WriteQuotedCSharpString(typeMapping.TypeName); - Writer.Write(", "); - WriteQuotedCSharpString(typeMapping.Namespace); - Writer.Write(", new "); - Writer.Write(typeof(System.Xml.Serialization.XmlSerializationWriteCallback).FullName); - Writer.Write("(this."); - Writer.Write(methodName); - Writer.WriteLine("));"); - } - } - } - Writer.Indent--; - Writer.WriteLine("}"); - } - - private void WriteQualifiedNameElement(string name, string ns, object defaultValue, string source, bool nullable, bool IsSoap, TypeMapping mapping) - { - bool hasDefault = defaultValue != null && defaultValue != DBNull.Value; - if (hasDefault) - { - WriteCheckDefault(source, defaultValue, nullable); - Writer.WriteLine(" {"); - Writer.Indent++; - } - string suffix = IsSoap ? "Encoded" : "Literal"; - Writer.Write(nullable ? ("WriteNullableQualifiedName" + suffix) : "WriteElementQualifiedName"); - Writer.Write("("); - WriteQuotedCSharpString(name); - if (ns != null) - { - Writer.Write(", "); - WriteQuotedCSharpString(ns); - } - Writer.Write(", "); - Writer.Write(source); - - if (IsSoap) - { - Writer.Write(", new System.Xml.XmlQualifiedName("); - WriteQuotedCSharpString(mapping.TypeName); - Writer.Write(", "); - WriteQuotedCSharpString(mapping.Namespace); - Writer.Write(")"); - } - - Writer.WriteLine(");"); - - if (hasDefault) - { - Writer.Indent--; - Writer.WriteLine("}"); - } - } - - private void WriteEnumValue(EnumMapping mapping, string source) - { - string methodName = ReferenceMapping(mapping); - -#if DEBUG - // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe - if (methodName == null) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorMethod, mapping.TypeDesc.Name) + Environment.StackTrace); -#endif - - Writer.Write(methodName); - Writer.Write("("); - Writer.Write(source); - Writer.Write(")"); - } - - private void WritePrimitiveValue(TypeDesc typeDesc, string source, bool isElement) - { - if (typeDesc == StringTypeDesc || typeDesc.FormatterName == "String") - { - Writer.Write(source); - } - else - { - if (!typeDesc.HasCustomFormatter) - { - Writer.Write(typeof(XmlConvert).FullName); - Writer.Write(".ToString(("); - Writer.Write(typeDesc.CSharpName); - Writer.Write(")"); - Writer.Write(source); - Writer.Write(")"); - } - else - { - Writer.Write("From"); - Writer.Write(typeDesc.FormatterName); - Writer.Write("("); - Writer.Write(source); - Writer.Write(")"); - } - } - } - - private void WritePrimitive(string method, string name, string ns, object defaultValue, string source, TypeMapping mapping, bool writeXsiType, bool isElement, bool isNullable) - { - TypeDesc typeDesc = mapping.TypeDesc; - bool hasDefault = defaultValue != null && defaultValue != DBNull.Value && mapping.TypeDesc.HasDefaultSupport; - if (hasDefault) - { - if (mapping is EnumMapping) - { -#if DEBUG - // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe - if (defaultValue.GetType() != typeof(string)) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorDetails, name + " has invalid default type " + defaultValue.GetType().Name)); -#endif - - Writer.Write("if ("); - if (mapping.TypeDesc.UseReflection) - Writer.Write(RaCodeGen.GetStringForEnumLongValue(source, mapping.TypeDesc.UseReflection)); - else - Writer.Write(source); - Writer.Write(" != "); - if (((EnumMapping)mapping).IsFlags) - { - Writer.Write("("); - string[] values = ((string)defaultValue).Split(null); - for (int i = 0; i < values.Length; i++) - { - if (values[i] == null || values[i].Length == 0) - continue; - if (i > 0) - Writer.WriteLine(" | "); - Writer.Write(RaCodeGen.GetStringForEnumCompare((EnumMapping)mapping, values[i], mapping.TypeDesc.UseReflection)); - } - Writer.Write(")"); - } - else - { - Writer.Write(RaCodeGen.GetStringForEnumCompare((EnumMapping)mapping, (string)defaultValue, mapping.TypeDesc.UseReflection)); - } - Writer.Write(")"); - } - else - { - WriteCheckDefault(source, defaultValue, isNullable); - } - Writer.WriteLine(" {"); - Writer.Indent++; - } - Writer.Write(method); - Writer.Write("("); - WriteQuotedCSharpString(name); - if (ns != null) - { - Writer.Write(", "); - WriteQuotedCSharpString(ns); - } - Writer.Write(", "); - - if (mapping is EnumMapping) - { - WriteEnumValue((EnumMapping)mapping, source); - } - else - { - WritePrimitiveValue(typeDesc, source, isElement); - } - - if (writeXsiType) - { - Writer.Write(", new System.Xml.XmlQualifiedName("); - WriteQuotedCSharpString(mapping.TypeName); - Writer.Write(", "); - WriteQuotedCSharpString(mapping.Namespace); - Writer.Write(")"); - } - - Writer.WriteLine(");"); - - if (hasDefault) - { - Writer.Indent--; - Writer.WriteLine("}"); - } - } - - private void WriteTag(string methodName, string name, string ns) - { - Writer.Write(methodName); - Writer.Write("("); - WriteQuotedCSharpString(name); - Writer.Write(", "); - if (ns == null) - { - Writer.Write("null"); - } - else - { - WriteQuotedCSharpString(ns); - } - Writer.WriteLine(");"); - } - - private void WriteTag(string methodName, string name, string ns, bool writePrefixed) - { - Writer.Write(methodName); - Writer.Write("("); - WriteQuotedCSharpString(name); - Writer.Write(", "); - if (ns == null) - { - Writer.Write("null"); - } - else - { - WriteQuotedCSharpString(ns); - } - Writer.Write(", null, "); - if (writePrefixed) - Writer.Write("true"); - else - Writer.Write("false"); - Writer.WriteLine(");"); - } - - private void WriteStartElement(string name, string ns, bool writePrefixed) - { - WriteTag("WriteStartElement", name, ns, writePrefixed); - } - - private void WriteEndElement() - { - Writer.WriteLine("WriteEndElement();"); - } - private void WriteEndElement(string source) - { - Writer.Write("WriteEndElement("); - Writer.Write(source); - Writer.WriteLine(");"); - } - - private void WriteEncodedNullTag(string name, string ns) - { - WriteTag("WriteNullTagEncoded", name, ns); - } - - private void WriteLiteralNullTag(string name, string ns) - { - WriteTag("WriteNullTagLiteral", name, ns); - } - - private void WriteEmptyTag(string name, string ns) - { - WriteTag("WriteEmptyTag", name, ns); - } - - private string GenerateMembersElement(XmlMembersMapping xmlMembersMapping) - { - ElementAccessor element = xmlMembersMapping.Accessor; - MembersMapping mapping = (MembersMapping)element.Mapping; - bool hasWrapperElement = mapping.HasWrapperElement; - bool writeAccessors = mapping.WriteAccessors; - bool isRpc = xmlMembersMapping.IsSoap && writeAccessors; - string methodName = NextMethodName(element.Name); - Writer.WriteLine(); - Writer.Write("public void "); - Writer.Write(methodName); - Writer.WriteLine("(object[] p) {"); - Writer.Indent++; - - Writer.WriteLine("WriteStartDocument();"); - - if (!mapping.IsSoap) - { - Writer.WriteLine("TopLevelElement();"); - } - - // in the top-level method add check for the parameters length, - // because visual basic does not have a concept of an parameter it uses instead - // so sometime we think that we have more parameters then supplied - Writer.WriteLine("int pLength = p.Length;"); - - if (hasWrapperElement) - { - WriteStartElement(element.Name, (element.Form == XmlSchemaForm.Qualified ? element.Namespace : ""), mapping.IsSoap); - - int xmlnsMember = FindXmlnsIndex(mapping.Members); - if (xmlnsMember >= 0) - { - MemberMapping member = mapping.Members[xmlnsMember]; - string source = "((" + typeof(System.Xml.Serialization.XmlSerializerNamespaces).FullName + ")p[" + xmlnsMember.ToString(CultureInfo.InvariantCulture) + "])"; - - Writer.Write("if (pLength > "); - Writer.Write(xmlnsMember.ToString(CultureInfo.InvariantCulture)); - Writer.WriteLine(") {"); - Writer.Indent++; - WriteNamespaces(source); - Writer.Indent--; - Writer.WriteLine("}"); - } - - for (int i = 0; i < mapping.Members.Length; i++) - { - MemberMapping member = mapping.Members[i]; - - if (member.Attribute != null && !member.Ignore) - { - string index = i.ToString(CultureInfo.InvariantCulture); - string source = "p[" + index + "]"; - - string specifiedSource = null; - int specifiedPosition = 0; - if (member.CheckSpecified != SpecifiedAccessor.None) - { - string memberNameSpecified = member.Name + "Specified"; - for (int j = 0; j < mapping.Members.Length; j++) - { - if (mapping.Members[j].Name == memberNameSpecified) - { - specifiedSource = "((bool) p[" + j.ToString(CultureInfo.InvariantCulture) + "])"; - specifiedPosition = j; - break; - } - } - } - - Writer.Write("if (pLength > "); - Writer.Write(index); - Writer.WriteLine(") {"); - Writer.Indent++; - - if (specifiedSource != null) - { - Writer.Write("if (pLength <= "); - Writer.Write(specifiedPosition.ToString(CultureInfo.InvariantCulture)); - Writer.Write(" || "); - Writer.Write(specifiedSource); - Writer.WriteLine(") {"); - Writer.Indent++; - } - - WriteMember(source, member.Attribute, member.TypeDesc, "p"); - - if (specifiedSource != null) - { - Writer.Indent--; - Writer.WriteLine("}"); - } - - Writer.Indent--; - Writer.WriteLine("}"); - } - } - } - - for (int i = 0; i < mapping.Members.Length; i++) - { - MemberMapping member = mapping.Members[i]; - if (member.Xmlns != null) - continue; - if (member.Ignore) - continue; - - string specifiedSource = null; - int specifiedPosition = 0; - if (member.CheckSpecified != SpecifiedAccessor.None) - { - string memberNameSpecified = member.Name + "Specified"; - - for (int j = 0; j < mapping.Members.Length; j++) - { - if (mapping.Members[j].Name == memberNameSpecified) - { - specifiedSource = "((bool) p[" + j.ToString(CultureInfo.InvariantCulture) + "])"; - specifiedPosition = j; - break; - } - } - } - - string index = i.ToString(CultureInfo.InvariantCulture); - Writer.Write("if (pLength > "); - Writer.Write(index); - Writer.WriteLine(") {"); - Writer.Indent++; - - if (specifiedSource != null) - { - Writer.Write("if (pLength <= "); - Writer.Write(specifiedPosition.ToString(CultureInfo.InvariantCulture)); - Writer.Write(" || "); - Writer.Write(specifiedSource); - Writer.WriteLine(") {"); - Writer.Indent++; - } - - string source = "p[" + index + "]"; - string enumSource = null; - if (member.ChoiceIdentifier != null) - { - for (int j = 0; j < mapping.Members.Length; j++) - { - if (mapping.Members[j].Name == member.ChoiceIdentifier.MemberName) - { - if (member.ChoiceIdentifier.Mapping.TypeDesc.UseReflection) - enumSource = "p[" + j.ToString(CultureInfo.InvariantCulture) + "]"; - else - enumSource = "((" + mapping.Members[j].TypeDesc.CSharpName + ")p[" + j.ToString(CultureInfo.InvariantCulture) + "]" + ")"; - break; - } - } - -#if DEBUG - // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe - if (enumSource == null) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorDetails, "Can not find " + member.ChoiceIdentifier.MemberName + " in the members mapping.")); -#endif - } - - if (isRpc && member.IsReturnValue && member.Elements.Length > 0) - { - Writer.Write("WriteRpcResult("); - WriteQuotedCSharpString(member.Elements[0].Name); - Writer.Write(", "); - WriteQuotedCSharpString(""); - Writer.WriteLine(");"); - } - - // override writeAccessors choice when we've written a wrapper element - WriteMember(source, enumSource, member.ElementsSortedByDerivation, member.Text, member.ChoiceIdentifier, member.TypeDesc, writeAccessors || hasWrapperElement); - - if (specifiedSource != null) - { - Writer.Indent--; - Writer.WriteLine("}"); - } - - Writer.Indent--; - Writer.WriteLine("}"); - } - - if (hasWrapperElement) - { - WriteEndElement(); - } - if (element.IsSoap) - { - if (!hasWrapperElement && !writeAccessors) - { - // doc/bare case -- allow extra members - Writer.Write("if (pLength > "); - Writer.Write(mapping.Members.Length.ToString(CultureInfo.InvariantCulture)); - Writer.WriteLine(") {"); - Writer.Indent++; - - WriteExtraMembers(mapping.Members.Length.ToString(CultureInfo.InvariantCulture), "pLength"); - - Writer.Indent--; - Writer.WriteLine("}"); - } - Writer.WriteLine("WriteReferencedElements();"); - } - Writer.Indent--; - Writer.WriteLine("}"); - return methodName; - } - - private string GenerateTypeElement(XmlTypeMapping xmlTypeMapping) - { - ElementAccessor element = xmlTypeMapping.Accessor; - TypeMapping mapping = element.Mapping; - string methodName = NextMethodName(element.Name); - Writer.WriteLine(); - Writer.Write("public void "); - Writer.Write(methodName); - Writer.WriteLine("(object o) {"); - Writer.Indent++; - - Writer.WriteLine("WriteStartDocument();"); - - Writer.WriteLine("if (o == null) {"); - Writer.Indent++; - if (element.IsNullable) - { - if (mapping.IsSoap) - WriteEncodedNullTag(element.Name, (element.Form == XmlSchemaForm.Qualified ? element.Namespace : "")); - else - WriteLiteralNullTag(element.Name, (element.Form == XmlSchemaForm.Qualified ? element.Namespace : "")); - } - else - WriteEmptyTag(element.Name, (element.Form == XmlSchemaForm.Qualified ? element.Namespace : "")); - Writer.WriteLine("return;"); - Writer.Indent--; - Writer.WriteLine("}"); - - if (!mapping.IsSoap && !mapping.TypeDesc.IsValueType && !mapping.TypeDesc.Type.IsPrimitive) - { - Writer.WriteLine("TopLevelElement();"); - } - - WriteMember("o", null, new ElementAccessor[] { element }, null, null, mapping.TypeDesc, !element.IsSoap); - - if (mapping.IsSoap) - { - Writer.WriteLine("WriteReferencedElements();"); - } - Writer.Indent--; - Writer.WriteLine("}"); - return methodName; - } - - private string NextMethodName(string name) - { - return "Write" + (++NextMethodNumber).ToString(null, NumberFormatInfo.InvariantInfo) + "_" + CodeIdentifier.MakeValidInternal(name); - } - - private void WriteEnumMethod(EnumMapping mapping) - { - string methodName = (string)MethodNames[mapping]; - Writer.WriteLine(); - string fullTypeName = mapping.TypeDesc.CSharpName; - if (mapping.IsSoap) - { - Writer.Write("void "); - Writer.Write(methodName); - Writer.WriteLine("(object e) {"); - WriteLocalDecl(fullTypeName, "v", "e", mapping.TypeDesc.UseReflection); - } - else - { - Writer.Write("string "); - Writer.Write(methodName); - Writer.Write("("); - Writer.Write(mapping.TypeDesc.UseReflection ? "object" : fullTypeName); - Writer.WriteLine(" v) {"); - } - Writer.Indent++; - Writer.WriteLine("string s = null;"); - ConstantMapping[] constants = mapping.Constants; - - if (constants.Length > 0) - { - Hashtable values = new Hashtable(); - if (mapping.TypeDesc.UseReflection) - Writer.WriteLine("switch (" + RaCodeGen.GetStringForEnumLongValue("v", mapping.TypeDesc.UseReflection) + " ){"); - else - Writer.WriteLine("switch (v) {"); - Writer.Indent++; - for (int i = 0; i < constants.Length; i++) - { - ConstantMapping c = constants[i]; - if (values[c.Value] == null) - { - WriteEnumCase(fullTypeName, c, mapping.TypeDesc.UseReflection); - Writer.Write("s = "); - WriteQuotedCSharpString(c.XmlName); - Writer.WriteLine("; break;"); - values.Add(c.Value, c.Value); - } - } - - - if (mapping.IsFlags) - { - Writer.Write("default: s = FromEnum("); - Writer.Write(RaCodeGen.GetStringForEnumLongValue("v", mapping.TypeDesc.UseReflection)); - Writer.Write(", new string[] {"); - Writer.Indent++; - for (int i = 0; i < constants.Length; i++) - { - ConstantMapping c = constants[i]; - if (i > 0) - Writer.WriteLine(", "); - WriteQuotedCSharpString(c.XmlName); - } - Writer.Write("}, new "); - Writer.Write(typeof(long).FullName); - Writer.Write("[] {"); - - for (int i = 0; i < constants.Length; i++) - { - ConstantMapping c = constants[i]; - if (i > 0) - Writer.WriteLine(", "); - Writer.Write("(long)"); - if (mapping.TypeDesc.UseReflection) - Writer.Write(c.Value.ToString(CultureInfo.InvariantCulture)); - else - { - Writer.Write(fullTypeName); - Writer.Write(".@"); - CodeIdentifier.CheckValidIdentifier(c.Name); - Writer.Write(c.Name); - } - } - Writer.Indent--; - Writer.Write("}, "); - WriteQuotedCSharpString(mapping.TypeDesc.FullName); - Writer.WriteLine("); break;"); - } - else - { - Writer.Write("default: throw CreateInvalidEnumValueException("); - Writer.Write(RaCodeGen.GetStringForEnumLongValue("v", mapping.TypeDesc.UseReflection)); - Writer.Write(".ToString(System.Globalization.CultureInfo.InvariantCulture), "); - WriteQuotedCSharpString(mapping.TypeDesc.FullName); - Writer.WriteLine(");"); - } - Writer.Indent--; - Writer.WriteLine("}"); - } - if (mapping.IsSoap) - { - Writer.Write("WriteXsiType("); - WriteQuotedCSharpString(mapping.TypeName); - Writer.Write(", "); - WriteQuotedCSharpString(mapping.Namespace); - Writer.WriteLine(");"); - Writer.WriteLine("Writer.WriteString(s);"); - } - else - { - Writer.WriteLine("return s;"); - } - Writer.Indent--; - Writer.WriteLine("}"); - } - - private void WriteDerivedTypes(StructMapping mapping) - { - for (StructMapping derived = mapping.DerivedMappings; derived != null; derived = derived.NextDerivedMapping) - { - string fullTypeName = derived.TypeDesc.CSharpName; - Writer.Write("else if ("); - WriteTypeCompare("t", fullTypeName, derived.TypeDesc.UseReflection); - Writer.WriteLine(") {"); - Writer.Indent++; - - string methodName = ReferenceMapping(derived); - -#if DEBUG - // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe - if (methodName == null) throw new InvalidOperationException("derived from " + mapping.TypeDesc.FullName + ", " + SR.Format(SR.XmlInternalErrorMethod, derived.TypeDesc.Name) + Environment.StackTrace); -#endif - - Writer.Write(methodName); - Writer.Write("(n, ns,"); - if (!derived.TypeDesc.UseReflection) Writer.Write("(" + fullTypeName + ")"); - Writer.Write("o"); - if (derived.TypeDesc.IsNullable) - Writer.Write(", isNullable"); - Writer.Write(", true"); - Writer.WriteLine(");"); - Writer.WriteLine("return;"); - Writer.Indent--; - Writer.WriteLine("}"); - - WriteDerivedTypes(derived); - } - } - - private void WriteEnumAndArrayTypes() - { - foreach (TypeScope scope in Scopes) - { - foreach (Mapping m in scope.TypeMappings) - { - if (m is EnumMapping && !m.IsSoap) - { - EnumMapping mapping = (EnumMapping)m; - string fullTypeName = mapping.TypeDesc.CSharpName; - Writer.Write("else if ("); - WriteTypeCompare("t", fullTypeName, mapping.TypeDesc.UseReflection); - Writer.WriteLine(") {"); - Writer.Indent++; - - string methodName = ReferenceMapping(mapping); - -#if DEBUG - // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe - if (methodName == null) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorMethod, mapping.TypeDesc.Name) + Environment.StackTrace); -#endif - Writer.WriteLine("Writer.WriteStartElement(n, ns);"); - Writer.Write("WriteXsiType("); - WriteQuotedCSharpString(mapping.TypeName); - Writer.Write(", "); - WriteQuotedCSharpString(mapping.Namespace); - Writer.WriteLine(");"); - Writer.Write("Writer.WriteString("); - Writer.Write(methodName); - Writer.Write("("); - if (!mapping.TypeDesc.UseReflection) Writer.Write("(" + fullTypeName + ")"); - Writer.WriteLine("o));"); - Writer.WriteLine("Writer.WriteEndElement();"); - Writer.WriteLine("return;"); - Writer.Indent--; - Writer.WriteLine("}"); - } - else if (m is ArrayMapping && !m.IsSoap) - { - ArrayMapping mapping = m as ArrayMapping; - if (mapping == null || m.IsSoap) continue; - string fullTypeName = mapping.TypeDesc.CSharpName; - Writer.Write("else if ("); - if (mapping.TypeDesc.IsArray) - WriteArrayTypeCompare("t", fullTypeName, mapping.TypeDesc.ArrayElementTypeDesc.CSharpName, mapping.TypeDesc.UseReflection); - else - WriteTypeCompare("t", fullTypeName, mapping.TypeDesc.UseReflection); - Writer.WriteLine(") {"); - Writer.Indent++; - - Writer.WriteLine("Writer.WriteStartElement(n, ns);"); - Writer.Write("WriteXsiType("); - WriteQuotedCSharpString(mapping.TypeName); - Writer.Write(", "); - WriteQuotedCSharpString(mapping.Namespace); - Writer.WriteLine(");"); - - WriteMember("o", null, mapping.ElementsSortedByDerivation, null, null, mapping.TypeDesc, true); - - Writer.WriteLine("Writer.WriteEndElement();"); - Writer.WriteLine("return;"); - Writer.Indent--; - Writer.WriteLine("}"); - } - } - } - } - - private void WriteStructMethod(StructMapping mapping) - { - if (mapping.IsSoap && mapping.TypeDesc.IsRoot) return; - string methodName = (string)MethodNames[mapping]; - - Writer.WriteLine(); - Writer.Write("void "); - Writer.Write(methodName); - - string fullTypeName = mapping.TypeDesc.CSharpName; - - if (mapping.IsSoap) - { - Writer.WriteLine("(object s) {"); - Writer.Indent++; - WriteLocalDecl(fullTypeName, "o", "s", mapping.TypeDesc.UseReflection); - } - else - { - Writer.Write("(string n, string ns, "); - Writer.Write(mapping.TypeDesc.UseReflection ? "object" : fullTypeName); - Writer.Write(" o"); - if (mapping.TypeDesc.IsNullable) - Writer.Write(", bool isNullable"); - Writer.WriteLine(", bool needType) {"); - Writer.Indent++; - if (mapping.TypeDesc.IsNullable) - { - Writer.WriteLine("if ((object)o == null) {"); - Writer.Indent++; - Writer.WriteLine("if (isNullable) WriteNullTagLiteral(n, ns);"); - Writer.WriteLine("return;"); - Writer.Indent--; - Writer.WriteLine("}"); - } - Writer.WriteLine("if (!needType) {"); - Writer.Indent++; - - Writer.Write(typeof(Type).FullName); - Writer.WriteLine(" t = o.GetType();"); - Writer.Write("if ("); - WriteTypeCompare("t", fullTypeName, mapping.TypeDesc.UseReflection); - Writer.WriteLine(") {"); - Writer.WriteLine("}"); - WriteDerivedTypes(mapping); - if (mapping.TypeDesc.IsRoot) - WriteEnumAndArrayTypes(); - Writer.WriteLine("else {"); - - Writer.Indent++; - if (mapping.TypeDesc.IsRoot) - { - Writer.WriteLine("WriteTypedPrimitive(n, ns, o, true);"); - Writer.WriteLine("return;"); - } - else - { - Writer.WriteLine("throw CreateUnknownTypeException(o);"); - } - Writer.Indent--; - Writer.WriteLine("}"); - Writer.Indent--; - Writer.WriteLine("}"); - } - - if (!mapping.TypeDesc.IsAbstract) - { - if (mapping.TypeDesc.Type != null && typeof(XmlSchemaObject).IsAssignableFrom(mapping.TypeDesc.Type)) - { - Writer.WriteLine("EscapeName = false;"); - } - - string xmlnsSource = null; - MemberMapping[] members = TypeScope.GetAllMembers(mapping); - int xmlnsMember = FindXmlnsIndex(members); - if (xmlnsMember >= 0) - { - MemberMapping member = members[xmlnsMember]; - CodeIdentifier.CheckValidIdentifier(member.Name); - xmlnsSource = RaCodeGen.GetStringForMember("o", member.Name, mapping.TypeDesc); - if (mapping.TypeDesc.UseReflection) - { - xmlnsSource = "((" + member.TypeDesc.CSharpName + ")" + xmlnsSource + ")"; - } - } - - if (!mapping.IsSoap) - { - Writer.Write("WriteStartElement(n, ns, o, false, "); - if (xmlnsSource == null) - Writer.Write("null"); - else - Writer.Write(xmlnsSource); - - Writer.WriteLine(");"); - if (!mapping.TypeDesc.IsRoot) - { - Writer.Write("if (needType) WriteXsiType("); - WriteQuotedCSharpString(mapping.TypeName); - Writer.Write(", "); - WriteQuotedCSharpString(mapping.Namespace); - Writer.WriteLine(");"); - } - } - else if (xmlnsSource != null) - { - WriteNamespaces(xmlnsSource); - } - for (int i = 0; i < members.Length; i++) - { - MemberMapping m = members[i]; - if (m.Attribute != null) - { - CodeIdentifier.CheckValidIdentifier(m.Name); - if (m.CheckShouldPersist) - { - Writer.Write("if ("); - string methodInvoke = RaCodeGen.GetStringForMethodInvoke("o", fullTypeName, "ShouldSerialize" + m.Name, mapping.TypeDesc.UseReflection); - if (mapping.TypeDesc.UseReflection) methodInvoke = "((" + typeof(bool).FullName + ")" + methodInvoke + ")"; - Writer.Write(methodInvoke); - Writer.WriteLine(") {"); - Writer.Indent++; - } - if (m.CheckSpecified != SpecifiedAccessor.None) - { - Writer.Write("if ("); - string memberGet = RaCodeGen.GetStringForMember("o", m.Name + "Specified", mapping.TypeDesc); - if (mapping.TypeDesc.UseReflection) memberGet = "((" + typeof(bool).FullName + ")" + memberGet + ")"; - Writer.Write(memberGet); - Writer.WriteLine(") {"); - Writer.Indent++; - } - WriteMember(RaCodeGen.GetStringForMember("o", m.Name, mapping.TypeDesc), m.Attribute, m.TypeDesc, "o"); - - if (m.CheckSpecified != SpecifiedAccessor.None) - { - Writer.Indent--; - Writer.WriteLine("}"); - } - if (m.CheckShouldPersist) - { - Writer.Indent--; - Writer.WriteLine("}"); - } - } - } - - for (int i = 0; i < members.Length; i++) - { - MemberMapping m = members[i]; - if (m.Xmlns != null) - continue; - CodeIdentifier.CheckValidIdentifier(m.Name); - bool checkShouldPersist = m.CheckShouldPersist && (m.Elements.Length > 0 || m.Text != null); - - if (checkShouldPersist) - { - Writer.Write("if ("); - string methodInvoke = RaCodeGen.GetStringForMethodInvoke("o", fullTypeName, "ShouldSerialize" + m.Name, mapping.TypeDesc.UseReflection); - if (mapping.TypeDesc.UseReflection) methodInvoke = "((" + typeof(bool).FullName + ")" + methodInvoke + ")"; - Writer.Write(methodInvoke); - Writer.WriteLine(") {"); - Writer.Indent++; - } - if (m.CheckSpecified != SpecifiedAccessor.None) - { - Writer.Write("if ("); - string memberGet = RaCodeGen.GetStringForMember("o", m.Name + "Specified", mapping.TypeDesc); - if (mapping.TypeDesc.UseReflection) memberGet = "((" + typeof(bool).FullName + ")" + memberGet + ")"; - Writer.Write(memberGet); - Writer.WriteLine(") {"); - Writer.Indent++; - } - - string choiceSource = null; - if (m.ChoiceIdentifier != null) - { - CodeIdentifier.CheckValidIdentifier(m.ChoiceIdentifier.MemberName); - choiceSource = RaCodeGen.GetStringForMember("o", m.ChoiceIdentifier.MemberName, mapping.TypeDesc); - } - WriteMember(RaCodeGen.GetStringForMember("o", m.Name, mapping.TypeDesc), choiceSource, m.ElementsSortedByDerivation, m.Text, m.ChoiceIdentifier, m.TypeDesc, true); - - if (m.CheckSpecified != SpecifiedAccessor.None) - { - Writer.Indent--; - Writer.WriteLine("}"); - } - if (checkShouldPersist) - { - Writer.Indent--; - Writer.WriteLine("}"); - } - } - if (!mapping.IsSoap) - { - WriteEndElement("o"); - } - } - Writer.Indent--; - Writer.WriteLine("}"); - } - - private bool CanOptimizeWriteListSequence(TypeDesc listElementTypeDesc) - { - // check to see if we can write values of the attribute sequentially - // currently we have only one data type (XmlQualifiedName) that we can not write "inline", - // because we need to output xmlns:qx="..." for each of the qnames - - return (listElementTypeDesc != null && listElementTypeDesc != QnameTypeDesc); - } - - private void WriteMember(string source, AttributeAccessor attribute, TypeDesc memberTypeDesc, string parent) - { - if (memberTypeDesc.IsAbstract) return; - if (memberTypeDesc.IsArrayLike) - { - Writer.WriteLine("{"); - Writer.Indent++; - string fullTypeName = memberTypeDesc.CSharpName; - WriteArrayLocalDecl(fullTypeName, "a", source, memberTypeDesc); - if (memberTypeDesc.IsNullable) - { - Writer.WriteLine("if (a != null) {"); - Writer.Indent++; - } - if (attribute.IsList) - { - if (CanOptimizeWriteListSequence(memberTypeDesc.ArrayElementTypeDesc)) - { - Writer.Write("Writer.WriteStartAttribute(null, "); - WriteQuotedCSharpString(attribute.Name); - Writer.Write(", "); - string ns = attribute.Form == XmlSchemaForm.Qualified ? attribute.Namespace : String.Empty; - if (ns != null) - { - WriteQuotedCSharpString(ns); - } - else - { - Writer.Write("null"); - } - Writer.WriteLine(");"); - } - else - { - Writer.Write(typeof(StringBuilder).FullName); - Writer.Write(" sb = new "); - Writer.Write(typeof(StringBuilder).FullName); - Writer.WriteLine("();"); - } - } - TypeDesc arrayElementTypeDesc = memberTypeDesc.ArrayElementTypeDesc; - - if (memberTypeDesc.IsEnumerable) - { - Writer.Write(" e = "); - Writer.Write(typeof(IEnumerator).FullName); - if (memberTypeDesc.IsPrivateImplementation) - { - Writer.Write("(("); - Writer.Write(typeof(IEnumerable).FullName); - Writer.WriteLine(").GetEnumerator();"); - } - else if (memberTypeDesc.IsGenericInterface) - { - if (memberTypeDesc.UseReflection) - { - // we use wildcard method name for generic GetEnumerator method, so we cannot use GetStringForMethodInvoke call here - Writer.Write("("); - Writer.Write(typeof(IEnumerator).FullName); - Writer.Write(")"); - Writer.Write(RaCodeGen.GetReflectionVariable(memberTypeDesc.CSharpName, "System.Collections.Generic.IEnumerable*")); - Writer.WriteLine(".Invoke(a, new object[0]);"); - } - else - { - Writer.Write("((System.Collections.Generic.IEnumerable<"); - Writer.Write(arrayElementTypeDesc.CSharpName); - Writer.WriteLine(">)a).GetEnumerator();"); - } - } - else - { - if (memberTypeDesc.UseReflection) - { - Writer.Write("("); - Writer.Write(typeof(IEnumerator).FullName); - Writer.Write(")"); - } - Writer.Write(RaCodeGen.GetStringForMethodInvoke("a", memberTypeDesc.CSharpName, "GetEnumerator", memberTypeDesc.UseReflection)); - Writer.WriteLine(";"); - } - Writer.WriteLine("if (e != null)"); - Writer.WriteLine("while (e.MoveNext()) {"); - Writer.Indent++; - - string arrayTypeFullName = arrayElementTypeDesc.CSharpName; - WriteLocalDecl(arrayTypeFullName, "ai", "e.Current", arrayElementTypeDesc.UseReflection); - } - else - { - Writer.Write("for (int i = 0; i < "); - if (memberTypeDesc.IsArray) - { - Writer.WriteLine("a.Length; i++) {"); - } - else - { - Writer.Write("(("); - Writer.Write(typeof(ICollection).FullName); - Writer.WriteLine(")a).Count; i++) {"); - } - Writer.Indent++; - string arrayTypeFullName = arrayElementTypeDesc.CSharpName; - WriteLocalDecl(arrayTypeFullName, "ai", RaCodeGen.GetStringForArrayMember("a", "i", memberTypeDesc), arrayElementTypeDesc.UseReflection); - } - if (attribute.IsList) - { - // check to see if we can write values of the attribute sequentially - if (CanOptimizeWriteListSequence(memberTypeDesc.ArrayElementTypeDesc)) - { - Writer.WriteLine("if (i != 0) Writer.WriteString(\" \");"); - Writer.Write("WriteValue("); - } - else - { - Writer.WriteLine("if (i != 0) sb.Append(\" \");"); - Writer.Write("sb.Append("); - } - if (attribute.Mapping is EnumMapping) - WriteEnumValue((EnumMapping)attribute.Mapping, "ai"); - else - WritePrimitiveValue(arrayElementTypeDesc, "ai", true); - Writer.WriteLine(");"); - } - else - { - WriteAttribute("ai", attribute, parent); - } - Writer.Indent--; - Writer.WriteLine("}"); - if (attribute.IsList) - { - // check to see if we can write values of the attribute sequentially - if (CanOptimizeWriteListSequence(memberTypeDesc.ArrayElementTypeDesc)) - { - Writer.WriteLine("Writer.WriteEndAttribute();"); - } - else - { - Writer.WriteLine("if (sb.Length != 0) {"); - Writer.Indent++; - - Writer.Write("WriteAttribute("); - WriteQuotedCSharpString(attribute.Name); - Writer.Write(", "); - string ns = attribute.Form == XmlSchemaForm.Qualified ? attribute.Namespace : String.Empty; - if (ns != null) - { - WriteQuotedCSharpString(ns); - Writer.Write(", "); - } - Writer.WriteLine("sb.ToString());"); - Writer.Indent--; - Writer.WriteLine("}"); - } - } - - if (memberTypeDesc.IsNullable) - { - Writer.Indent--; - Writer.WriteLine("}"); - } - Writer.Indent--; - Writer.WriteLine("}"); - } - else - { - WriteAttribute(source, attribute, parent); - } - } - - private void WriteAttribute(string source, AttributeAccessor attribute, string parent) - { - if (attribute.Mapping is SpecialMapping) - { - SpecialMapping special = (SpecialMapping)attribute.Mapping; - if (special.TypeDesc.Kind == TypeKind.Attribute || special.TypeDesc.CanBeAttributeValue) - { - Writer.Write("WriteXmlAttribute("); - Writer.Write(source); - Writer.Write(", "); - Writer.Write(parent); - Writer.WriteLine(");"); - } - else - throw new InvalidOperationException(SR.XmlInternalError); - } - else - { - TypeDesc typeDesc = attribute.Mapping.TypeDesc; - if (!typeDesc.UseReflection) source = "((" + typeDesc.CSharpName + ")" + source + ")"; - WritePrimitive("WriteAttribute", attribute.Name, attribute.Form == XmlSchemaForm.Qualified ? attribute.Namespace : "", attribute.Default, source, attribute.Mapping, false, false, false); - } - } - - private void WriteMember(string source, string choiceSource, ElementAccessor[] elements, TextAccessor text, ChoiceIdentifierAccessor choice, TypeDesc memberTypeDesc, bool writeAccessors) - { - if (memberTypeDesc.IsArrayLike && - !(elements.Length == 1 && elements[0].Mapping is ArrayMapping)) - WriteArray(source, choiceSource, elements, text, choice, memberTypeDesc); - else - WriteElements(source, choiceSource, elements, text, choice, "a", writeAccessors, memberTypeDesc.IsNullable); - } - - - private void WriteArray(string source, string choiceSource, ElementAccessor[] elements, TextAccessor text, ChoiceIdentifierAccessor choice, TypeDesc arrayTypeDesc) - { - if (elements.Length == 0 && text == null) return; - Writer.WriteLine("{"); - Writer.Indent++; - string arrayTypeName = arrayTypeDesc.CSharpName; - WriteArrayLocalDecl(arrayTypeName, "a", source, arrayTypeDesc); - if (arrayTypeDesc.IsNullable) - { - Writer.WriteLine("if (a != null) {"); - Writer.Indent++; - } - - if (choice != null) - { - bool choiceUseReflection = choice.Mapping.TypeDesc.UseReflection; - string choiceFullName = choice.Mapping.TypeDesc.CSharpName; - WriteArrayLocalDecl(choiceFullName + "[]", "c", choiceSource, choice.Mapping.TypeDesc); - // write check for the choice identifier array - Writer.WriteLine("if (c == null || c.Length < a.Length) {"); - Writer.Indent++; - Writer.Write("throw CreateInvalidChoiceIdentifierValueException("); - WriteQuotedCSharpString(choice.Mapping.TypeDesc.FullName); - Writer.Write(", "); - WriteQuotedCSharpString(choice.MemberName); - Writer.Write(");"); - Writer.Indent--; - Writer.WriteLine("}"); - } - - WriteArrayItems(elements, text, choice, arrayTypeDesc, "a", "c"); - if (arrayTypeDesc.IsNullable) - { - Writer.Indent--; - Writer.WriteLine("}"); - } - Writer.Indent--; - Writer.WriteLine("}"); - } - - private void WriteArrayItems(ElementAccessor[] elements, TextAccessor text, ChoiceIdentifierAccessor choice, TypeDesc arrayTypeDesc, string arrayName, string choiceName) - { - TypeDesc arrayElementTypeDesc = arrayTypeDesc.ArrayElementTypeDesc; - - if (arrayTypeDesc.IsEnumerable) - { - Writer.Write(typeof(IEnumerator).FullName); - Writer.Write(" e = "); - if (arrayTypeDesc.IsPrivateImplementation) - { - Writer.Write("(("); - Writer.Write(typeof(IEnumerable).FullName); - Writer.Write(")"); - Writer.Write(arrayName); - Writer.WriteLine(").GetEnumerator();"); - } - else if (arrayTypeDesc.IsGenericInterface) - { - if (arrayTypeDesc.UseReflection) - { - // we use wildcard method name for generic GetEnumerator method, so we cannot use GetStringForMethodInvoke call here - Writer.Write("("); - Writer.Write(typeof(IEnumerator).FullName); - Writer.Write(")"); - Writer.Write(RaCodeGen.GetReflectionVariable(arrayTypeDesc.CSharpName, "System.Collections.Generic.IEnumerable*")); - Writer.Write(".Invoke("); - Writer.Write(arrayName); - Writer.WriteLine(", new object[0]);"); - } - else - { - Writer.Write("((System.Collections.Generic.IEnumerable<"); - Writer.Write(arrayElementTypeDesc.CSharpName); - Writer.Write(">)"); - Writer.Write(arrayName); - Writer.WriteLine(").GetEnumerator();"); - } - } - else - { - if (arrayTypeDesc.UseReflection) - { - Writer.Write("("); - Writer.Write(typeof(IEnumerator).FullName); - Writer.Write(")"); - } - Writer.Write(RaCodeGen.GetStringForMethodInvoke(arrayName, arrayTypeDesc.CSharpName, "GetEnumerator", arrayTypeDesc.UseReflection)); - Writer.WriteLine(";"); - } - Writer.WriteLine("if (e != null)"); - Writer.WriteLine("while (e.MoveNext()) {"); - Writer.Indent++; - string arrayTypeFullName = arrayElementTypeDesc.CSharpName; - WriteLocalDecl(arrayTypeFullName, arrayName + "i", "e.Current", arrayElementTypeDesc.UseReflection); - WriteElements(arrayName + "i", choiceName + "i", elements, text, choice, arrayName + "a", true, true); - } - else - { - Writer.Write("for (int i"); - Writer.Write(arrayName); - Writer.Write(" = 0; i"); - Writer.Write(arrayName); - Writer.Write(" < "); - if (arrayTypeDesc.IsArray) - { - Writer.Write(arrayName); - Writer.Write(".Length"); - } - else - { - Writer.Write("(("); - Writer.Write(typeof(ICollection).FullName); - Writer.Write(")"); - Writer.Write(arrayName); - Writer.Write(").Count"); - } - Writer.Write("; i"); - Writer.Write(arrayName); - Writer.WriteLine("++) {"); - Writer.Indent++; - int count = elements.Length + (text == null ? 0 : 1); - if (count > 1) - { - string arrayTypeFullName = arrayElementTypeDesc.CSharpName; - WriteLocalDecl(arrayTypeFullName, arrayName + "i", RaCodeGen.GetStringForArrayMember(arrayName, "i" + arrayName, arrayTypeDesc), arrayElementTypeDesc.UseReflection); - if (choice != null) - { - string choiceFullName = choice.Mapping.TypeDesc.CSharpName; - WriteLocalDecl(choiceFullName, choiceName + "i", RaCodeGen.GetStringForArrayMember(choiceName, "i" + arrayName, choice.Mapping.TypeDesc), choice.Mapping.TypeDesc.UseReflection); - } - WriteElements(arrayName + "i", choiceName + "i", elements, text, choice, arrayName + "a", true, arrayElementTypeDesc.IsNullable); - } - else - { - WriteElements(RaCodeGen.GetStringForArrayMember(arrayName, "i" + arrayName, arrayTypeDesc), elements, text, choice, arrayName + "a", true, arrayElementTypeDesc.IsNullable); - } - } - Writer.Indent--; - Writer.WriteLine("}"); - } - - private void WriteElements(string source, ElementAccessor[] elements, TextAccessor text, ChoiceIdentifierAccessor choice, string arrayName, bool writeAccessors, bool isNullable) - { - WriteElements(source, null, elements, text, choice, arrayName, writeAccessors, isNullable); - } - - private void WriteElements(string source, string enumSource, ElementAccessor[] elements, TextAccessor text, ChoiceIdentifierAccessor choice, string arrayName, bool writeAccessors, bool isNullable) - { - if (elements.Length == 0 && text == null) return; - if (elements.Length == 1 && text == null) - { - TypeDesc td = elements[0].IsUnbounded ? elements[0].Mapping.TypeDesc.CreateArrayTypeDesc() : elements[0].Mapping.TypeDesc; - if (!elements[0].Any && !elements[0].Mapping.TypeDesc.UseReflection && !elements[0].Mapping.TypeDesc.IsOptionalValue) - source = "((" + td.CSharpName + ")" + source + ")"; - WriteElement(source, elements[0], arrayName, writeAccessors); - } - else - { - if (isNullable && choice == null) - { - Writer.Write("if ((object)("); - Writer.Write(source); - Writer.Write(") != null)"); - } - Writer.WriteLine("{"); - Writer.Indent++; - int anyCount = 0; - ArrayList namedAnys = new ArrayList(); - ElementAccessor unnamedAny = null; // can only have one - bool wroteFirstIf = false; - string enumTypeName = choice == null ? null : choice.Mapping.TypeDesc.FullName; - - for (int i = 0; i < elements.Length; i++) - { - ElementAccessor element = elements[i]; - - if (element.Any) - { - anyCount++; - if (element.Name != null && element.Name.Length > 0) - namedAnys.Add(element); - else if (unnamedAny == null) - unnamedAny = element; - } - else if (choice != null) - { - bool useReflection = element.Mapping.TypeDesc.UseReflection; - string fullTypeName = element.Mapping.TypeDesc.CSharpName; - bool enumUseReflection = choice.Mapping.TypeDesc.UseReflection; - string enumFullName = (enumUseReflection ? "" : enumTypeName + ".@") + FindChoiceEnumValue(element, (EnumMapping)choice.Mapping, enumUseReflection); - - if (wroteFirstIf) Writer.Write("else "); - else wroteFirstIf = true; - Writer.Write("if ("); - Writer.Write(enumUseReflection ? RaCodeGen.GetStringForEnumLongValue(enumSource, enumUseReflection) : enumSource); - Writer.Write(" == "); - Writer.Write(enumFullName); - if (isNullable && !element.IsNullable) - { - Writer.Write(" && ((object)("); - Writer.Write(source); - Writer.Write(") != null)"); - } - Writer.WriteLine(") {"); - Writer.Indent++; - - WriteChoiceTypeCheck(source, fullTypeName, useReflection, choice, enumFullName, element.Mapping.TypeDesc); - - string castedSource = source; - if (!useReflection) - castedSource = "((" + fullTypeName + ")" + source + ")"; - WriteElement(element.Any ? source : castedSource, element, arrayName, writeAccessors); - Writer.Indent--; - Writer.WriteLine("}"); - } - else - { - bool useReflection = element.Mapping.TypeDesc.UseReflection; - TypeDesc td = element.IsUnbounded ? element.Mapping.TypeDesc.CreateArrayTypeDesc() : element.Mapping.TypeDesc; - string fullTypeName = td.CSharpName; - if (wroteFirstIf) Writer.Write("else "); - else wroteFirstIf = true; - Writer.Write("if ("); - WriteInstanceOf(source, fullTypeName, useReflection); - Writer.WriteLine(") {"); - Writer.Indent++; - string castedSource = source; - if (!useReflection) - castedSource = "((" + fullTypeName + ")" + source + ")"; - WriteElement(element.Any ? source : castedSource, element, arrayName, writeAccessors); - Writer.Indent--; - Writer.WriteLine("}"); - } - } - if (anyCount > 0) - { - if (elements.Length - anyCount > 0) Writer.Write("else "); - - string fullTypeName = typeof(XmlElement).FullName; - - Writer.Write("if ("); - Writer.Write(source); - Writer.Write(" is "); - Writer.Write(fullTypeName); - Writer.WriteLine(") {"); - Writer.Indent++; - - Writer.Write(fullTypeName); - Writer.Write(" elem = ("); - Writer.Write(fullTypeName); - Writer.Write(")"); - Writer.Write(source); - Writer.WriteLine(";"); - - int c = 0; - - foreach (ElementAccessor element in namedAnys) - { - if (c++ > 0) Writer.Write("else "); - - string enumFullName = null; - - bool useReflection = element.Mapping.TypeDesc.UseReflection; - if (choice != null) - { - bool enumUseReflection = choice.Mapping.TypeDesc.UseReflection; - enumFullName = (enumUseReflection ? "" : enumTypeName + ".@") + FindChoiceEnumValue(element, (EnumMapping)choice.Mapping, enumUseReflection); - Writer.Write("if ("); - Writer.Write(enumUseReflection ? RaCodeGen.GetStringForEnumLongValue(enumSource, enumUseReflection) : enumSource); - Writer.Write(" == "); - Writer.Write(enumFullName); - if (isNullable && !element.IsNullable) - { - Writer.Write(" && ((object)("); - Writer.Write(source); - Writer.Write(") != null)"); - } - Writer.WriteLine(") {"); - Writer.Indent++; - } - Writer.Write("if (elem.Name == "); - WriteQuotedCSharpString(element.Name); - Writer.Write(" && elem.NamespaceURI == "); - WriteQuotedCSharpString(element.Namespace); - Writer.WriteLine(") {"); - Writer.Indent++; - WriteElement("elem", element, arrayName, writeAccessors); - - if (choice != null) - { - Writer.Indent--; - Writer.WriteLine("}"); - Writer.WriteLine("else {"); - Writer.Indent++; - - Writer.WriteLine("// throw Value '{0}' of the choice identifier '{1}' does not match element '{2}' from namespace '{3}'."); - - Writer.Write("throw CreateChoiceIdentifierValueException("); - WriteQuotedCSharpString(enumFullName); - Writer.Write(", "); - WriteQuotedCSharpString(choice.MemberName); - Writer.WriteLine(", elem.Name, elem.NamespaceURI);"); - Writer.Indent--; - Writer.WriteLine("}"); - } - Writer.Indent--; - Writer.WriteLine("}"); - } - if (c > 0) - { - Writer.WriteLine("else {"); - Writer.Indent++; - } - if (unnamedAny != null) - { - WriteElement("elem", unnamedAny, arrayName, writeAccessors); - } - else - { - Writer.WriteLine("throw CreateUnknownAnyElementException(elem.Name, elem.NamespaceURI);"); - } - if (c > 0) - { - Writer.Indent--; - Writer.WriteLine("}"); - } - Writer.Indent--; - Writer.WriteLine("}"); - } - if (text != null) - { - bool useReflection = text.Mapping.TypeDesc.UseReflection; - string fullTypeName = text.Mapping.TypeDesc.CSharpName; - if (elements.Length > 0) - { - Writer.Write("else "); - Writer.Write("if ("); - WriteInstanceOf(source, fullTypeName, useReflection); - Writer.WriteLine(") {"); - Writer.Indent++; - string castedSource = source; - if (!useReflection) - castedSource = "((" + fullTypeName + ")" + source + ")"; - WriteText(castedSource, text); - Writer.Indent--; - Writer.WriteLine("}"); - } - else - { - string castedSource = source; - if (!useReflection) - castedSource = "((" + fullTypeName + ")" + source + ")"; - WriteText(castedSource, text); - } - } - if (elements.Length > 0) - { - Writer.Write("else "); - - if (isNullable) - { - Writer.Write(" if ((object)("); - Writer.Write(source); - Writer.Write(") != null)"); - } - - Writer.WriteLine("{"); - Writer.Indent++; - - Writer.Write("throw CreateUnknownTypeException("); - Writer.Write(source); - Writer.WriteLine(");"); - - Writer.Indent--; - Writer.WriteLine("}"); - } - Writer.Indent--; - Writer.WriteLine("}"); - } - } - - private void WriteText(string source, TextAccessor text) - { - if (text.Mapping is PrimitiveMapping) - { - PrimitiveMapping mapping = (PrimitiveMapping)text.Mapping; - Writer.Write("WriteValue("); - if (text.Mapping is EnumMapping) - { - WriteEnumValue((EnumMapping)text.Mapping, source); - } - else - { - WritePrimitiveValue(mapping.TypeDesc, source, false); - } - Writer.WriteLine(");"); - } - else if (text.Mapping is SpecialMapping) - { - SpecialMapping mapping = (SpecialMapping)text.Mapping; - switch (mapping.TypeDesc.Kind) - { - case TypeKind.Node: - Writer.Write(source); - Writer.WriteLine(".WriteTo(Writer);"); - break; - default: - throw new InvalidOperationException(SR.XmlInternalError); - } - } - } - - private void WriteElement(string source, ElementAccessor element, string arrayName, bool writeAccessor) - { - string name = writeAccessor ? element.Name : element.Mapping.TypeName; - string ns = element.Any && element.Name.Length == 0 ? null : (element.Form == XmlSchemaForm.Qualified ? (writeAccessor ? element.Namespace : element.Mapping.Namespace) : ""); - if (element.Mapping is NullableMapping) - { - Writer.Write("if ("); - Writer.Write(source); - Writer.WriteLine(" != null) {"); - Writer.Indent++; - string fullTypeName = element.Mapping.TypeDesc.BaseTypeDesc.CSharpName; - string castedSource = source; - if (!element.Mapping.TypeDesc.BaseTypeDesc.UseReflection) - castedSource = "((" + fullTypeName + ")" + source + ")"; - ElementAccessor e = element.Clone(); - e.Mapping = ((NullableMapping)element.Mapping).BaseMapping; - WriteElement(e.Any ? source : castedSource, e, arrayName, writeAccessor); - Writer.Indent--; - Writer.WriteLine("}"); - if (element.IsNullable) - { - Writer.WriteLine("else {"); - Writer.Indent++; - WriteLiteralNullTag(element.Name, element.Form == XmlSchemaForm.Qualified ? element.Namespace : ""); - Writer.Indent--; - Writer.WriteLine("}"); - } - } - else if (element.Mapping is ArrayMapping) - { - ArrayMapping mapping = (ArrayMapping)element.Mapping; - if (mapping.IsSoap) - { - Writer.Write("WritePotentiallyReferencingElement("); - WriteQuotedCSharpString(name); - Writer.Write(", "); - WriteQuotedCSharpString(ns); - Writer.Write(", "); - Writer.Write(source); - if (!writeAccessor) - { - Writer.Write(", "); - Writer.Write(RaCodeGen.GetStringForTypeof(mapping.TypeDesc.CSharpName, mapping.TypeDesc.UseReflection)); - Writer.Write(", true, "); - } - else - { - Writer.Write(", null, false, "); - } - WriteValue(element.IsNullable); - Writer.WriteLine(");"); - } - else if (element.IsUnbounded) - { - TypeDesc td = mapping.TypeDesc.CreateArrayTypeDesc(); - string fullTypeName = td.CSharpName; - string elementArrayName = "el" + arrayName; - string arrayIndex = "c" + elementArrayName; - Writer.WriteLine("{"); - Writer.Indent++; - WriteArrayLocalDecl(fullTypeName, elementArrayName, source, mapping.TypeDesc); - if (element.IsNullable) - { - WriteNullCheckBegin(elementArrayName, element); - } - else - { - if (mapping.TypeDesc.IsNullable) - { - Writer.Write("if ("); - Writer.Write(elementArrayName); - Writer.Write(" != null)"); - } - Writer.WriteLine("{"); - Writer.Indent++; - } - - Writer.Write("for (int "); - Writer.Write(arrayIndex); - Writer.Write(" = 0; "); - Writer.Write(arrayIndex); - Writer.Write(" < "); - - if (td.IsArray) - { - Writer.Write(elementArrayName); - Writer.Write(".Length"); - } - else - { - Writer.Write("(("); - Writer.Write(typeof(ICollection).FullName); - Writer.Write(")"); - Writer.Write(elementArrayName); - Writer.Write(").Count"); - } - Writer.Write("; "); - Writer.Write(arrayIndex); - Writer.WriteLine("++) {"); - Writer.Indent++; - - element.IsUnbounded = false; - WriteElement(elementArrayName + "[" + arrayIndex + "]", element, arrayName, writeAccessor); - element.IsUnbounded = true; - - Writer.Indent--; - Writer.WriteLine("}"); - - Writer.Indent--; - Writer.WriteLine("}"); - Writer.Indent--; - Writer.WriteLine("}"); - } - else - { - string fullTypeName = mapping.TypeDesc.CSharpName; - Writer.WriteLine("{"); - Writer.Indent++; - WriteArrayLocalDecl(fullTypeName, arrayName, source, mapping.TypeDesc); - if (element.IsNullable) - { - WriteNullCheckBegin(arrayName, element); - } - else - { - if (mapping.TypeDesc.IsNullable) - { - Writer.Write("if ("); - Writer.Write(arrayName); - Writer.Write(" != null)"); - } - Writer.WriteLine("{"); - Writer.Indent++; - } - WriteStartElement(name, ns, false); - WriteArrayItems(mapping.ElementsSortedByDerivation, null, null, mapping.TypeDesc, arrayName, null); - WriteEndElement(); - Writer.Indent--; - Writer.WriteLine("}"); - Writer.Indent--; - Writer.WriteLine("}"); - } - } - else if (element.Mapping is EnumMapping) - { - if (element.Mapping.IsSoap) - { - string methodName = (string)MethodNames[element.Mapping]; - Writer.Write("Writer.WriteStartElement("); - WriteQuotedCSharpString(name); - Writer.Write(", "); - WriteQuotedCSharpString(ns); - Writer.WriteLine(");"); - Writer.Write(methodName); - Writer.Write("("); - Writer.Write(source); - Writer.WriteLine(");"); - WriteEndElement(); - } - else - { - WritePrimitive("WriteElementString", name, ns, element.Default, source, element.Mapping, false, true, element.IsNullable); - } - } - else if (element.Mapping is PrimitiveMapping) - { - PrimitiveMapping mapping = (PrimitiveMapping)element.Mapping; - if (mapping.TypeDesc == QnameTypeDesc) - WriteQualifiedNameElement(name, ns, element.Default, source, element.IsNullable, mapping.IsSoap, mapping); - else - { - string suffixNullable = mapping.IsSoap ? "Encoded" : "Literal"; - string suffixRaw = mapping.TypeDesc.XmlEncodingNotRequired ? "Raw" : ""; - WritePrimitive(element.IsNullable ? ("WriteNullableString" + suffixNullable + suffixRaw) : ("WriteElementString" + suffixRaw), - name, ns, element.Default, source, mapping, mapping.IsSoap, true, element.IsNullable); - } - } - else if (element.Mapping is StructMapping) - { - StructMapping mapping = (StructMapping)element.Mapping; - - if (mapping.IsSoap) - { - Writer.Write("WritePotentiallyReferencingElement("); - WriteQuotedCSharpString(name); - Writer.Write(", "); - WriteQuotedCSharpString(ns); - Writer.Write(", "); - Writer.Write(source); - if (!writeAccessor) - { - Writer.Write(", "); - Writer.Write(RaCodeGen.GetStringForTypeof(mapping.TypeDesc.CSharpName, mapping.TypeDesc.UseReflection)); - Writer.Write(", true, "); - } - else - { - Writer.Write(", null, false, "); - } - WriteValue(element.IsNullable); - } - else - { - string methodName = ReferenceMapping(mapping); - -#if DEBUG - // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe - if (methodName == null) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorMethod, mapping.TypeDesc.Name) + Environment.StackTrace); -#endif - Writer.Write(methodName); - Writer.Write("("); - WriteQuotedCSharpString(name); - Writer.Write(", "); - if (ns == null) - Writer.Write("null"); - else - { - WriteQuotedCSharpString(ns); - } - Writer.Write(", "); - Writer.Write(source); - if (mapping.TypeDesc.IsNullable) - { - Writer.Write(", "); - WriteValue(element.IsNullable); - } - Writer.Write(", false"); - } - Writer.WriteLine(");"); - } - else if (element.Mapping is SpecialMapping) - { - SpecialMapping mapping = (SpecialMapping)element.Mapping; - bool useReflection = mapping.TypeDesc.UseReflection; - TypeDesc td = mapping.TypeDesc; - string fullTypeName = td.CSharpName; - - - if (element.Mapping is SerializableMapping) - { - WriteElementCall("WriteSerializable", typeof(IXmlSerializable), source, name, ns, element.IsNullable, !element.Any); - } - else - { - // XmlNode, XmlElement - Writer.Write("if (("); - Writer.Write(source); - Writer.Write(") is "); - Writer.Write(typeof(XmlNode).FullName); - Writer.Write(" || "); - Writer.Write(source); - Writer.Write(" == null"); - Writer.WriteLine(") {"); - Writer.Indent++; - - WriteElementCall("WriteElementLiteral", typeof(XmlNode), source, name, ns, element.IsNullable, element.Any); - - Writer.Indent--; - Writer.WriteLine("}"); - Writer.WriteLine("else {"); - Writer.Indent++; - - Writer.Write("throw CreateInvalidAnyTypeException("); - Writer.Write(source); - Writer.WriteLine(");"); - - Writer.Indent--; - Writer.WriteLine("}"); - } - } - else - { - throw new InvalidOperationException(SR.XmlInternalError); - } - } - - private void WriteElementCall(string func, Type cast, string source, string name, string ns, bool isNullable, bool isAny) - { - Writer.Write(func); - Writer.Write("(("); - Writer.Write(cast.FullName); - Writer.Write(")"); - Writer.Write(source); - Writer.Write(", "); - WriteQuotedCSharpString(name); - Writer.Write(", "); - WriteQuotedCSharpString(ns); - Writer.Write(", "); - WriteValue(isNullable); - Writer.Write(", "); - WriteValue(isAny); - Writer.WriteLine(");"); - } - - private void WriteCheckDefault(string source, object value, bool isNullable) - { - Writer.Write("if ("); - - if (value is string && ((string)value).Length == 0) - { - // special case for string compare - Writer.Write("("); - Writer.Write(source); - if (isNullable) - Writer.Write(" == null) || ("); - else - Writer.Write(" != null) && ("); - Writer.Write(source); - Writer.Write(".Length != 0)"); - } - else - { - Writer.Write(source); - Writer.Write(" != "); - WriteValue(value); - } - Writer.Write(")"); - } - - private void WriteChoiceTypeCheck(string source, string fullTypeName, bool useReflection, ChoiceIdentifierAccessor choice, string enumName, TypeDesc typeDesc) - { - Writer.Write("if (((object)"); - Writer.Write(source); - Writer.Write(") != null && !("); - WriteInstanceOf(source, fullTypeName, useReflection); - Writer.Write(")) throw CreateMismatchChoiceException("); - WriteQuotedCSharpString(typeDesc.FullName); - Writer.Write(", "); - WriteQuotedCSharpString(choice.MemberName); - Writer.Write(", "); - WriteQuotedCSharpString(enumName); - Writer.WriteLine(");"); - } - - private void WriteNullCheckBegin(string source, ElementAccessor element) - { - Writer.Write("if ((object)("); - Writer.Write(source); - Writer.WriteLine(") == null) {"); - Writer.Indent++; - WriteLiteralNullTag(element.Name, element.Form == XmlSchemaForm.Qualified ? element.Namespace : ""); - Writer.Indent--; - Writer.WriteLine("}"); - Writer.WriteLine("else {"); - Writer.Indent++; - } - - private void WriteValue(object value) - { - if (value == null) - { - Writer.Write("null"); - } - else - { - Type type = value.GetType(); - - if (type == typeof(String)) - { - string s = (string)value; - WriteQuotedCSharpString(s); - } - else if (type == typeof(Char)) - { - Writer.Write('\''); - char ch = (char)value; - if (ch == '\'') - Writer.Write("\'"); - else - Writer.Write(ch); - Writer.Write('\''); - } - else if (type == typeof(Int32)) - Writer.Write(((Int32)value).ToString(null, NumberFormatInfo.InvariantInfo)); - else if (type == typeof(Double)) - { - if (double.IsNaN((Double)value)) - { - Writer.Write("double.NaN"); - } - else - { - Writer.Write(((Double)value).ToString("R", NumberFormatInfo.InvariantInfo)); - } - } - else if (type == typeof(Boolean)) - Writer.Write((bool)value ? "true" : "false"); - else if ((type == typeof(Int16)) || (type == typeof(Int64)) || (type == typeof(UInt16)) || (type == typeof(UInt32)) || (type == typeof(UInt64)) || (type == typeof(Byte)) || (type == typeof(SByte))) - { - Writer.Write("("); - Writer.Write(type.FullName); - Writer.Write(")"); - Writer.Write("("); - Writer.Write(Convert.ToString(value, NumberFormatInfo.InvariantInfo)); - Writer.Write(")"); - } - else if (type == typeof(Single)) - { - if (Single.IsNaN((Single)value)) - { - Writer.Write("System.Single.NaN"); - } - else - { - Writer.Write(((Single)value).ToString("R", NumberFormatInfo.InvariantInfo)); - Writer.Write("f"); - } - } - else if (type == typeof(Decimal)) - { - Writer.Write(((Decimal)value).ToString(null, NumberFormatInfo.InvariantInfo)); - Writer.Write("m"); - } - else if (type == typeof(DateTime)) - { - Writer.Write(" new "); - Writer.Write(type.FullName); - Writer.Write("("); - Writer.Write(((DateTime)value).Ticks.ToString(CultureInfo.InvariantCulture)); - Writer.Write(")"); - } - else if (type == typeof(TimeSpan)) - { - Writer.Write(" new "); - Writer.Write(type.FullName); - Writer.Write("("); - Writer.Write(((TimeSpan)value).Ticks.ToString(CultureInfo.InvariantCulture)); - Writer.Write(")"); - } - else - { - if (type.IsEnum) - { - Writer.Write(((int)value).ToString(null, NumberFormatInfo.InvariantInfo)); - } - - else - { - throw new InvalidOperationException(SR.Format(SR.XmlUnsupportedDefaultType, type.FullName)); - } - } - } - } - - private void WriteNamespaces(string source) - { - Writer.Write("WriteNamespaceDeclarations("); - Writer.Write(source); - Writer.WriteLine(");"); - } - - private int FindXmlnsIndex(MemberMapping[] members) - { - for (int i = 0; i < members.Length; i++) - { - if (members[i].Xmlns == null) - continue; - return i; - } - return -1; - } - - private void WriteExtraMembers(string loopStartSource, string loopEndSource) - { - Writer.Write("for (int i = "); - Writer.Write(loopStartSource); - Writer.Write("; i < "); - Writer.Write(loopEndSource); - Writer.WriteLine("; i++) {"); - Writer.Indent++; - Writer.WriteLine("if (p[i] != null) {"); - Writer.Indent++; - Writer.WriteLine("WritePotentiallyReferencingElement(null, null, p[i], p[i].GetType(), true, false);"); - Writer.Indent--; - Writer.WriteLine("}"); - Writer.Indent--; - Writer.WriteLine("}"); - } - - private void WriteLocalDecl(string typeName, string variableName, string initValue, bool useReflection) - { - RaCodeGen.WriteLocalDecl(typeName, variableName, initValue, useReflection); - } - - private void WriteArrayLocalDecl(string typeName, string variableName, string initValue, TypeDesc arrayTypeDesc) - { - RaCodeGen.WriteArrayLocalDecl(typeName, variableName, initValue, arrayTypeDesc); - } - private void WriteTypeCompare(string variable, string escapedTypeName, bool useReflection) - { - RaCodeGen.WriteTypeCompare(variable, escapedTypeName, useReflection); - } - private void WriteInstanceOf(string source, string escapedTypeName, bool useReflection) - { - RaCodeGen.WriteInstanceOf(source, escapedTypeName, useReflection); - } - private void WriteArrayTypeCompare(string variable, string escapedTypeName, string elementTypeName, bool useReflection) - { - RaCodeGen.WriteArrayTypeCompare(variable, escapedTypeName, elementTypeName, useReflection); - } - - private void WriteEnumCase(string fullTypeName, ConstantMapping c, bool useReflection) - { - RaCodeGen.WriteEnumCase(fullTypeName, c, useReflection); - } - - private string FindChoiceEnumValue(ElementAccessor element, EnumMapping choiceMapping, bool useReflection) - { - string enumValue = null; - - for (int i = 0; i < choiceMapping.Constants.Length; i++) - { - string xmlName = choiceMapping.Constants[i].XmlName; - - if (element.Any && element.Name.Length == 0) - { - if (xmlName == "##any:") - { - if (useReflection) - enumValue = choiceMapping.Constants[i].Value.ToString(CultureInfo.InvariantCulture); - else - enumValue = choiceMapping.Constants[i].Name; - break; - } - continue; - } - int colon = xmlName.LastIndexOf(':'); - string choiceNs = colon < 0 ? choiceMapping.Namespace : xmlName.Substring(0, colon); - string choiceName = colon < 0 ? xmlName : xmlName.Substring(colon + 1); - - if (element.Name == choiceName) - { - if ((element.Form == XmlSchemaForm.Unqualified && string.IsNullOrEmpty(choiceNs)) || element.Namespace == choiceNs) - { - if (useReflection) - enumValue = choiceMapping.Constants[i].Value.ToString(CultureInfo.InvariantCulture); - else - enumValue = choiceMapping.Constants[i].Name; - break; - } - } - } - if (enumValue == null || enumValue.Length == 0) - { - if (element.Any && element.Name.Length == 0) - { - // Type {0} is missing enumeration value '##any' for XmlAnyElementAttribute. - throw new InvalidOperationException(SR.Format(SR.XmlChoiceMissingAnyValue, choiceMapping.TypeDesc.FullName)); - } - // Type {0} is missing value for '{1}'. - throw new InvalidOperationException(SR.Format(SR.XmlChoiceMissingValue, choiceMapping.TypeDesc.FullName, element.Namespace + ":" + element.Name, element.Name, element.Namespace)); - } - if (!useReflection) - CodeIdentifier.CheckValidIdentifier(enumValue); - return enumValue; - } - } -} diff --git a/external/corefx/src/Microsoft.XmlSerializer.Generator/src/Sgen.cs b/external/corefx/src/Microsoft.XmlSerializer.Generator/src/Sgen.cs index df04c29a81..281ce4db31 100644 --- a/external/corefx/src/Microsoft.XmlSerializer.Generator/src/Sgen.cs +++ b/external/corefx/src/Microsoft.XmlSerializer.Generator/src/Sgen.cs @@ -9,6 +9,7 @@ using System.IO; using System.Linq; using System.Reflection; using System.Threading; +using System.Xml.Serialization; namespace Microsoft.XmlSerializer.Generator { @@ -79,7 +80,14 @@ namespace Microsoft.XmlSerializer.Generator } else if (ArgumentMatch(arg, "type")) { - types.Add(value); + if (value != string.Empty) + { + string[] typelist = value.Split(';'); + foreach (var type in typelist) + { + types.Add(type); + } + } } else if (ArgumentMatch(arg, "assembly")) { @@ -155,7 +163,7 @@ namespace Microsoft.XmlSerializer.Generator if(disableRun) { Console.WriteLine("This tool is not intended to be used directly."); - Console.WriteLine("Please refer to https://github.com/dotnet/core/blob/master/samples/xmlserializergenerator-instructions.md on how to use it."); + Console.WriteLine("Please refer to https://go.microsoft.com/fwlink/?linkid=858594 on how to use it."); return 0; } @@ -256,7 +264,7 @@ namespace Microsoft.XmlSerializer.Generator bool gac = assembly.GlobalAssemblyCache; outputDirectory = outputDirectory == null ? (gac ? Environment.CurrentDirectory : Path.GetDirectoryName(assembly.Location)) : outputDirectory; - string serializerName = XmlSerializer.GetXmlSerializerAssemblyName(serializableTypes[0], null); + string serializerName = GetXmlSerializerAssemblyName(serializableTypes[0], null); string codePath = Path.Combine(outputDirectory, serializerName + ".cs"); if (!force) @@ -287,7 +295,15 @@ namespace Microsoft.XmlSerializer.Generator using (FileStream fs = File.Create(codePath)) { - success = XmlSerializer.GenerateSerializer(serializableTypes, allMappings, fs); + MethodInfo method = typeof(System.Xml.Serialization.XmlSerializer).GetMethod("GenerateSerializer", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + if (method == null) + { + Console.Error.WriteLine(FormatMessage(parsableerrors: false, warning: false, message: SR.GenerateSerializerNotFound)); + } + else + { + success = (bool)method.Invoke(null, new object[] { serializableTypes, allMappings, fs }); + } } } catch (UnauthorizedAccessException) @@ -359,22 +375,6 @@ namespace Microsoft.XmlSerializer.Generator } if (xmlTypeMapping != null) { - if (xmlTypeMapping.Mapping != null && xmlTypeMapping.Mapping is StructMapping) - { - foreach (MemberMapping memberMapping in ((StructMapping)xmlTypeMapping.Mapping).Members) - { - MemberInfo memberInfo = memberMapping.MemberInfo; - if (memberInfo != null && memberInfo.MemberType == MemberTypes.Property) - { - PropertyInfo propertyInfo = memberInfo as PropertyInfo; - if (propertyInfo != null && (propertyInfo.SetMethod == null || !propertyInfo.SetMethod.IsPublic)) - { - return; - } - } - } - } - xmlTypeMapping = importer.ImportTypeMapping(type); mappings.Add(xmlTypeMapping); importedTypes.Add(type); @@ -384,7 +384,7 @@ namespace Microsoft.XmlSerializer.Generator private static Assembly LoadAssembly(string assemblyName, bool throwOnFail) { Assembly assembly = null; - string path = Path.GetFullPath(assemblyName); + string path = Path.IsPathRooted(assemblyName) ? assemblyName : Path.GetFullPath(assemblyName); assembly = Assembly.LoadFile(path); if (assembly == null) { @@ -448,5 +448,24 @@ namespace Microsoft.XmlSerializer.Generator WriteWarning(e.InnerException, parsableerrors); } } + + private static string GetXmlSerializerAssemblyName(Type type) + { + return GetXmlSerializerAssemblyName(type, null); + } + + private static string GetXmlSerializerAssemblyName(Type type, string defaultNamespace) + { + if (type == null) + { + throw new ArgumentNullException("type"); + } + return GetTempAssemblyName(type.Assembly.GetName(), defaultNamespace); + } + + private static string GetTempAssemblyName(AssemblyName parent, string ns) + { + return parent.Name + ".XmlSerializers" + (ns == null || ns.Length == 0 ? "" : "." + ns.GetHashCode()); + } } -} \ No newline at end of file +} diff --git a/external/corefx/src/Microsoft.XmlSerializer.Generator/tests/Configurations.props b/external/corefx/src/Microsoft.XmlSerializer.Generator/tests/Configurations.props index eb4a540a92..a822bbc0fa 100644 --- a/external/corefx/src/Microsoft.XmlSerializer.Generator/tests/Configurations.props +++ b/external/corefx/src/Microsoft.XmlSerializer.Generator/tests/Configurations.props @@ -3,6 +3,7 @@ netstandard; + netfx; uap; netcoreapp; diff --git a/external/corefx/src/Microsoft.XmlSerializer.Generator/tests/Microsoft.XmlSerializer.Generator.Tests.csproj b/external/corefx/src/Microsoft.XmlSerializer.Generator/tests/Microsoft.XmlSerializer.Generator.Tests.csproj index f4d52adb2f..3eaed25e3a 100644 --- a/external/corefx/src/Microsoft.XmlSerializer.Generator/tests/Microsoft.XmlSerializer.Generator.Tests.csproj +++ b/external/corefx/src/Microsoft.XmlSerializer.Generator/tests/Microsoft.XmlSerializer.Generator.Tests.csproj @@ -6,7 +6,7 @@ $(DefineConstants);XMLSERIALIZERGENERATORTESTS - true + true + + + + @@ -39,4 +42,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.CodeDom/src/System.CodeDom.csproj b/external/corefx/src/System.CodeDom/src/System.CodeDom.csproj index 2ba4158b54..a2bc5e0032 100644 --- a/external/corefx/src/System.CodeDom/src/System.CodeDom.csproj +++ b/external/corefx/src/System.CodeDom/src/System.CodeDom.csproj @@ -137,4 +137,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Collections.Concurrent/System.Collections.Concurrent.sln b/external/corefx/src/System.Collections.Concurrent/System.Collections.Concurrent.sln index ce3ca8163b..dd683b7df8 100644 --- a/external/corefx/src/System.Collections.Concurrent/System.Collections.Concurrent.sln +++ b/external/corefx/src/System.Collections.Concurrent/System.Collections.Concurrent.sln @@ -7,6 +7,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Collections.Concurre {EA610394-CBA3-4E5C-B3DB-AAEA7F640E89} = {EA610394-CBA3-4E5C-B3DB-AAEA7F640E89} EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Collections.Concurrent.Performance.Tests", "tests\Performance\System.Collections.Concurrent.Performance.Tests.csproj", "{16568C86-E97E-42C6-B683-65A1B5AF2EC8}" + ProjectSection(ProjectDependencies) = postProject + {EA610394-CBA3-4E5C-B3DB-AAEA7F640E89} = {EA610394-CBA3-4E5C-B3DB-AAEA7F640E89} + EndProjectSection +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Collections.Concurrent", "src\System.Collections.Concurrent.csproj", "{EA610394-CBA3-4E5C-B3DB-AAEA7F640E89}" ProjectSection(ProjectDependencies) = postProject {772CB0A7-3D45-4D3A-B975-671A1937C761} = {772CB0A7-3D45-4D3A-B975-671A1937C761} @@ -30,6 +35,10 @@ Global {9574CEEC-5554-411B-B44C-6CA9EC1CEB08}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {9574CEEC-5554-411B-B44C-6CA9EC1CEB08}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU {9574CEEC-5554-411B-B44C-6CA9EC1CEB08}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU + {16568C86-E97E-42C6-B683-65A1B5AF2EC8}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {16568C86-E97E-42C6-B683-65A1B5AF2EC8}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {16568C86-E97E-42C6-B683-65A1B5AF2EC8}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {16568C86-E97E-42C6-B683-65A1B5AF2EC8}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU {EA610394-CBA3-4E5C-B3DB-AAEA7F640E89}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU {EA610394-CBA3-4E5C-B3DB-AAEA7F640E89}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {EA610394-CBA3-4E5C-B3DB-AAEA7F640E89}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU @@ -44,6 +53,7 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {9574CEEC-5554-411B-B44C-6CA9EC1CEB08} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} + {16568C86-E97E-42C6-B683-65A1B5AF2EC8} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} {EA610394-CBA3-4E5C-B3DB-AAEA7F640E89} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD} {772CB0A7-3D45-4D3A-B975-671A1937C761} = {2E666815-2EDB-464B-9DF6-380BF4789AD4} EndGlobalSection diff --git a/external/corefx/src/System.Collections.Concurrent/src/Resources/Strings.resx b/external/corefx/src/System.Collections.Concurrent/src/Resources/Strings.resx index aed02ab0ff..e32a9a254c 100644 --- a/external/corefx/src/System.Collections.Concurrent/src/Resources/Strings.resx +++ b/external/corefx/src/System.Collections.Concurrent/src/Resources/Strings.resx @@ -181,4 +181,7 @@ Enumeration has either not started or has already finished. + + The given key '{0}' was not present in the dictionary. + \ No newline at end of file diff --git a/external/corefx/src/System.Collections.Concurrent/src/System.Collections.Concurrent.csproj b/external/corefx/src/System.Collections.Concurrent/src/System.Collections.Concurrent.csproj index d40c155704..e8c7b2cd6a 100644 --- a/external/corefx/src/System.Collections.Concurrent/src/System.Collections.Concurrent.csproj +++ b/external/corefx/src/System.Collections.Concurrent/src/System.Collections.Concurrent.csproj @@ -6,7 +6,6 @@ System.Collections.Concurrent System.Collections.Concurrent - @@ -26,7 +25,6 @@ - @@ -37,4 +35,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs b/external/corefx/src/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs index 3ccbdb7937..95a1e533e9 100644 --- a/external/corefx/src/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs +++ b/external/corefx/src/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs @@ -879,7 +879,7 @@ namespace System.Collections.Concurrent TValue value; if (!TryGetValue(key, out value)) { - ThrowKeyNotFoundException(); + ThrowKeyNotFoundException(key); } return value; } @@ -896,9 +896,9 @@ namespace System.Collections.Concurrent // of important methods like TryGetValue and ContainsKey. [MethodImpl(MethodImplOptions.NoInlining)] - private static void ThrowKeyNotFoundException() + private static void ThrowKeyNotFoundException(object key) { - throw new KeyNotFoundException(); + throw new KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key.ToString())); } [MethodImpl(MethodImplOptions.NoInlining)] diff --git a/external/corefx/src/System.Collections.Concurrent/tests/Performance/Configurations.props b/external/corefx/src/System.Collections.Concurrent/tests/Performance/Configurations.props new file mode 100644 index 0000000000..2845c11c54 --- /dev/null +++ b/external/corefx/src/System.Collections.Concurrent/tests/Performance/Configurations.props @@ -0,0 +1,8 @@ + + + + + netcoreapp; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Collections.Concurrent/tests/Performance/Perf.ConcurrentBag.cs b/external/corefx/src/System.Collections.Concurrent/tests/Performance/Perf.ConcurrentBag.cs new file mode 100644 index 0000000000..a92d138ae2 --- /dev/null +++ b/external/corefx/src/System.Collections.Concurrent/tests/Performance/Perf.ConcurrentBag.cs @@ -0,0 +1,192 @@ +// 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.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Xunit.Performance; +using Xunit; + +namespace System.Collections.Concurrent.Tests +{ + public class Perf_ConcurrentBag + { + [Benchmark(InnerIterationCount = 1_000_000), MeasureGCAllocations] + public void Ctor() + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long innerIters = Benchmark.InnerIterationCount / 10; + using (iteration.StartMeasurement()) + { + for (int i = 0; i < innerIters; i++) + { + new ConcurrentBag(); // 1 + new ConcurrentBag(); // 2 + new ConcurrentBag(); // 3 + new ConcurrentBag(); // 4 + new ConcurrentBag(); // 5 + new ConcurrentBag(); // 6 + new ConcurrentBag(); // 7 + new ConcurrentBag(); // 8 + new ConcurrentBag(); // 9 + new ConcurrentBag(); // 10 + } + } + } + } + + [Benchmark(InnerIterationCount = 10_000_000), MeasureGCAllocations] + public void Add() + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long size = Benchmark.InnerIterationCount; + var q = new ConcurrentBag(); + + using (iteration.StartMeasurement()) + { + for (int i = 0; i < size; i++) + { + q.Add(i); + } + } + } + } + + [Benchmark(InnerIterationCount = 1_000_000), MeasureGCAllocations] + [InlineData(false)] + [InlineData(true)] + public void TryTake(bool exists) + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long size = Benchmark.InnerIterationCount; + var b = new ConcurrentBag(); + + if (exists) + { + for (int i = 0; i < size; i++) + { + b.Add(i); + } + } + + using (iteration.StartMeasurement()) + { + for (int i = 0; i < size; i++) + { + b.TryTake(out int result); + } + } + } + } + + [Benchmark(InnerIterationCount = 1_000_000), MeasureGCAllocations] + public void AddTake() + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long size = Benchmark.InnerIterationCount; + var b = new ConcurrentBag(); + + using (iteration.StartMeasurement()) + { + for (int i = 0; i < size; i++) + { + b.Add(i); + b.TryTake(out int result); + } + } + } + } + + [Benchmark(InnerIterationCount = 1_000_000), MeasureGCAllocations] + [InlineData(0)] + [InlineData(3)] + public void Pooling_AddTakeFromSameThreads(int initialSize) + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + const int NumThreads = 2; + long size = Benchmark.InnerIterationCount; + var b = new ConcurrentBag(); + + using (var barrier = new Barrier(NumThreads + 1)) + { + Task[] tasks = Enumerable.Range(0, NumThreads).Select(_ => Task.Factory.StartNew(() => + { + for (int i = 0; i < initialSize; i++) + { + b.Add(i); + } + + barrier.SignalAndWait(); + barrier.SignalAndWait(); + + for (int i = 0; i < size; i++) + { + b.Add(i); + b.TryTake(out int result); + } + }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default)).ToArray(); + + barrier.SignalAndWait(); + using (iteration.StartMeasurement()) + { + barrier.SignalAndWait(); + Task.WaitAll(tasks); + } + } + } + } + + [Benchmark(InnerIterationCount = 1_000_000), MeasureGCAllocations] + public void ProducerConsumer_AddTakeFromDifferentThreads() + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + const int NumThreads = 2; + long size = Benchmark.InnerIterationCount; + var b = new ConcurrentBag(); + + using (var barrier = new Barrier(NumThreads + 1)) + { + Task p = Task.Factory.StartNew(() => + { + barrier.SignalAndWait(); + barrier.SignalAndWait(); + + for (int i = 0; i < size; i++) + { + b.Add(i); + } + }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); + + Task c = Task.Factory.StartNew(() => + { + barrier.SignalAndWait(); + barrier.SignalAndWait(); + + int count = 0; + while (count < size) + { + if (b.TryTake(out int result)) + { + count++; + } + } + }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); + + barrier.SignalAndWait(); + using (iteration.StartMeasurement()) + { + barrier.SignalAndWait(); + Task.WaitAll(p, c); + } + } + } + } + } +} diff --git a/external/corefx/src/System.Collections.Concurrent/tests/Performance/Perf.ConcurrentDictionary.cs b/external/corefx/src/System.Collections.Concurrent/tests/Performance/Perf.ConcurrentDictionary.cs new file mode 100644 index 0000000000..ad3970c457 --- /dev/null +++ b/external/corefx/src/System.Collections.Concurrent/tests/Performance/Perf.ConcurrentDictionary.cs @@ -0,0 +1,115 @@ +// 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 Microsoft.Xunit.Performance; +using Xunit; + +namespace System.Collections.Concurrent.Tests +{ + public class Perf_ConcurrentDictionary + { + [Benchmark(InnerIterationCount = 1_000_000), MeasureGCAllocations] + public void Ctor() + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long innerIters = Benchmark.InnerIterationCount / 10; + using (iteration.StartMeasurement()) + { + for (int i = 0; i < innerIters; i++) + { + new ConcurrentDictionary(); // 1 + new ConcurrentDictionary(); // 2 + new ConcurrentDictionary(); // 3 + new ConcurrentDictionary(); // 4 + new ConcurrentDictionary(); // 5 + new ConcurrentDictionary(); // 6 + new ConcurrentDictionary(); // 7 + new ConcurrentDictionary(); // 8 + new ConcurrentDictionary(); // 9 + new ConcurrentDictionary(); // 10 + } + } + } + } + + [Benchmark(InnerIterationCount = 1_000_000), MeasureGCAllocations] + [InlineData(false)] + [InlineData(true)] + public void TryAdd(bool exists) + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long size = Benchmark.InnerIterationCount; + + var d = new ConcurrentDictionary(); + if (exists) + { + for (int i = 0; i < size; i++) + { + d.TryAdd(i, i); + } + } + + using (iteration.StartMeasurement()) + { + for (int i = 0; i < size; i++) + { + d.TryAdd(i, i); + } + } + } + } + + [Benchmark(InnerIterationCount = 1_000_000), MeasureGCAllocations] + [InlineData(false)] + [InlineData(true)] + public void TryGetValue(bool exists) + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long size = Benchmark.InnerIterationCount; + + var d = new ConcurrentDictionary(); + for (int i = 0; i < size; i++) + { + d.TryAdd(exists ? i : i + size, i); + } + + using (iteration.StartMeasurement()) + { + for (int i = 0; i < size; i++) + { + d.TryGetValue(i, out long result); + } + } + } + } + + [Benchmark(InnerIterationCount = 1_000_000), MeasureGCAllocations] + [InlineData(false)] + [InlineData(true)] + public void GetOrAdd(bool exists) + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long size = Benchmark.InnerIterationCount; + + var d = new ConcurrentDictionary(); + for (int i = 0; i < size; i++) + { + d.TryAdd(exists ? i : i + size, i); + } + + using (iteration.StartMeasurement()) + { + for (int i = 0; i < size; i++) + { + d.GetOrAdd(i, i); + } + } + } + } + } +} diff --git a/external/corefx/src/System.Collections.Concurrent/tests/Performance/Perf.ConcurrentQueue.cs b/external/corefx/src/System.Collections.Concurrent/tests/Performance/Perf.ConcurrentQueue.cs new file mode 100644 index 0000000000..d74b194227 --- /dev/null +++ b/external/corefx/src/System.Collections.Concurrent/tests/Performance/Perf.ConcurrentQueue.cs @@ -0,0 +1,164 @@ +// 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.Threading; +using System.Threading.Tasks; +using Microsoft.Xunit.Performance; +using Xunit; + +namespace System.Collections.Concurrent.Tests +{ + public class Perf_ConcurrentQueue + { + [Benchmark(InnerIterationCount = 1_000_000), MeasureGCAllocations] + public void Ctor() + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long innerIters = Benchmark.InnerIterationCount / 10; + using (iteration.StartMeasurement()) + { + for (int i = 0; i < innerIters; i++) + { + new ConcurrentQueue(); // 1 + new ConcurrentQueue(); // 2 + new ConcurrentQueue(); // 3 + new ConcurrentQueue(); // 4 + new ConcurrentQueue(); // 5 + new ConcurrentQueue(); // 6 + new ConcurrentQueue(); // 7 + new ConcurrentQueue(); // 8 + new ConcurrentQueue(); // 9 + new ConcurrentQueue(); // 10 + } + } + } + } + + [Benchmark(InnerIterationCount = 10_000_000), MeasureGCAllocations] + public void Enqueue() + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long size = Benchmark.InnerIterationCount; + var q = new ConcurrentQueue(); + + using (iteration.StartMeasurement()) + { + for (int i = 0; i < size; i++) + { + q.Enqueue(i); + } + } + } + } + + [Benchmark(InnerIterationCount = 1_000_000), MeasureGCAllocations] + [InlineData(false)] + [InlineData(true)] + public void TryDequeue(bool exists) + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long size = Benchmark.InnerIterationCount; + var b = new ConcurrentQueue(); + + if (exists) + { + for (int i = 0; i < size; i++) + { + b.Enqueue(i); + } + } + + using (iteration.StartMeasurement()) + { + for (int i = 0; i < size; i++) + { + b.TryDequeue(out int result); + } + } + } + } + + [Benchmark(InnerIterationCount = 1_000_000), MeasureGCAllocations] + public void EnqueueTryDequeue() + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long size = Benchmark.InnerIterationCount; + var b = new ConcurrentQueue(); + + using (iteration.StartMeasurement()) + { + for (int i = 0; i < size; i++) + { + b.Enqueue(i); + b.TryDequeue(out int result); + } + } + } + } + + [Benchmark(InnerIterationCount = 1_000_000), MeasureGCAllocations] + [InlineData(false)] + [InlineData(true)] + public void ProducerConsumer_EnqueueTryDequeueFromDifferentThreads(bool presize) + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + const int NumThreads = 2; + long size = Benchmark.InnerIterationCount; + var q = new ConcurrentQueue(); + + if (presize) + { + for (int i = 0; i < size * 2; i++) + { + q.Enqueue(i); + } + while (q.TryDequeue(out _)) + { + } + } + + using (var barrier = new Barrier(NumThreads + 1)) + { + Task p = Task.Factory.StartNew(() => + { + barrier.SignalAndWait(); + barrier.SignalAndWait(); + + for (int i = 0; i < size; i++) + { + q.Enqueue(i); + } + }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); + + Task c = Task.Factory.StartNew(() => + { + barrier.SignalAndWait(); + barrier.SignalAndWait(); + + int count = 0; + while (count < size) + { + if (q.TryDequeue(out int result)) + { + count++; + } + } + }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); + + barrier.SignalAndWait(); + using (iteration.StartMeasurement()) + { + barrier.SignalAndWait(); + Task.WaitAll(p, c); + } + } + } + } + } +} diff --git a/external/corefx/src/System.Collections.Concurrent/tests/Performance/Perf.ConcurrentStack.cs b/external/corefx/src/System.Collections.Concurrent/tests/Performance/Perf.ConcurrentStack.cs new file mode 100644 index 0000000000..243dcdcf88 --- /dev/null +++ b/external/corefx/src/System.Collections.Concurrent/tests/Performance/Perf.ConcurrentStack.cs @@ -0,0 +1,192 @@ +// 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.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Xunit.Performance; +using Xunit; + +namespace System.Collections.Concurrent.Tests +{ + public class Perf_ConcurrentStack + { + [Benchmark(InnerIterationCount = 1_000_000), MeasureGCAllocations] + public void Ctor() + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long innerIters = Benchmark.InnerIterationCount / 10; + using (iteration.StartMeasurement()) + { + for (int i = 0; i < innerIters; i++) + { + new ConcurrentStack(); // 1 + new ConcurrentStack(); // 2 + new ConcurrentStack(); // 3 + new ConcurrentStack(); // 4 + new ConcurrentStack(); // 5 + new ConcurrentStack(); // 6 + new ConcurrentStack(); // 7 + new ConcurrentStack(); // 8 + new ConcurrentStack(); // 9 + new ConcurrentStack(); // 10 + } + } + } + } + + [Benchmark(InnerIterationCount = 10_000_000), MeasureGCAllocations] + public void Push() + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long size = Benchmark.InnerIterationCount; + var q = new ConcurrentStack(); + + using (iteration.StartMeasurement()) + { + for (int i = 0; i < size; i++) + { + q.Push(i); + } + } + } + } + + [Benchmark(InnerIterationCount = 1_000_000), MeasureGCAllocations] + [InlineData(false)] + [InlineData(true)] + public void TryPop(bool exists) + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long size = Benchmark.InnerIterationCount; + var b = new ConcurrentStack(); + + if (exists) + { + for (int i = 0; i < size; i++) + { + b.Push(i); + } + } + + using (iteration.StartMeasurement()) + { + for (int i = 0; i < size; i++) + { + b.TryPop(out int result); + } + } + } + } + + [Benchmark(InnerIterationCount = 1_000_000), MeasureGCAllocations] + public void PushTryPop() + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long size = Benchmark.InnerIterationCount; + var b = new ConcurrentStack(); + + using (iteration.StartMeasurement()) + { + for (int i = 0; i < size; i++) + { + b.Push(i); + b.TryPop(out int result); + } + } + } + } + + [Benchmark(InnerIterationCount = 1_000_000), MeasureGCAllocations] + [InlineData(0)] + [InlineData(3)] + public void Pooling_PushTakeFromSameThreads(int initialSize) + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + const int NumThreads = 2; + long size = Benchmark.InnerIterationCount; + var b = new ConcurrentStack(); + + using (var barrier = new Barrier(NumThreads + 1)) + { + Task[] tasks = Enumerable.Range(0, NumThreads).Select(_ => Task.Factory.StartNew(() => + { + for (int i = 0; i < initialSize; i++) + { + b.Push(i); + } + + barrier.SignalAndWait(); + barrier.SignalAndWait(); + + for (int i = 0; i < size; i++) + { + b.Push(i); + b.TryPop(out int result); + } + }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default)).ToArray(); + + barrier.SignalAndWait(); + using (iteration.StartMeasurement()) + { + barrier.SignalAndWait(); + Task.WaitAll(tasks); + } + } + } + } + + [Benchmark(InnerIterationCount = 1_000_000), MeasureGCAllocations] + public void ProducerConsumer_PushTakeFromDifferentThreads() + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + const int NumThreads = 2; + long size = Benchmark.InnerIterationCount; + var b = new ConcurrentStack(); + + using (var barrier = new Barrier(NumThreads + 1)) + { + Task p = Task.Factory.StartNew(() => + { + barrier.SignalAndWait(); + barrier.SignalAndWait(); + + for (int i = 0; i < size; i++) + { + b.Push(i); + } + }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); + + Task c = Task.Factory.StartNew(() => + { + barrier.SignalAndWait(); + barrier.SignalAndWait(); + + int count = 0; + while (count < size) + { + if (b.TryPop(out int result)) + { + count++; + } + } + }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); + + barrier.SignalAndWait(); + using (iteration.StartMeasurement()) + { + barrier.SignalAndWait(); + Task.WaitAll(p, c); + } + } + } + } + } +} diff --git a/external/corefx/src/System.Collections.Concurrent/tests/Performance/System.Collections.Concurrent.Performance.Tests.csproj b/external/corefx/src/System.Collections.Concurrent/tests/Performance/System.Collections.Concurrent.Performance.Tests.csproj new file mode 100644 index 0000000000..fc54ad7d71 --- /dev/null +++ b/external/corefx/src/System.Collections.Concurrent/tests/Performance/System.Collections.Concurrent.Performance.Tests.csproj @@ -0,0 +1,27 @@ + + + + + true + {16568C86-E97E-42C6-B683-65A1B5AF2EC8} + + + + + + + + + + + Common\System\PerfUtils.cs + + + + + {69e46a6f-9966-45a5-8945-2559fe337827} + PerfRunner + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Collections.Concurrent/tests/System.Collections.Concurrent.Tests.csproj b/external/corefx/src/System.Collections.Concurrent/tests/System.Collections.Concurrent.Tests.csproj index 505b528b69..5b3b083eaf 100644 --- a/external/corefx/src/System.Collections.Concurrent/tests/System.Collections.Concurrent.Tests.csproj +++ b/external/corefx/src/System.Collections.Concurrent/tests/System.Collections.Concurrent.Tests.csproj @@ -4,7 +4,6 @@ {9574CEEC-5554-411B-B44C-6CA9EC1CEB08} - @@ -109,4 +108,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Collections.Immutable/ref/Configurations.props b/external/corefx/src/System.Collections.Immutable/ref/Configurations.props index 4d2a1e0f0d..048bc45cf4 100644 --- a/external/corefx/src/System.Collections.Immutable/ref/Configurations.props +++ b/external/corefx/src/System.Collections.Immutable/ref/Configurations.props @@ -3,6 +3,7 @@ netstandard1.0; + netstandard1.3; netstandard; diff --git a/external/corefx/src/System.Collections.Immutable/ref/System.Collections.Immutable.cs.REMOVED.git-id b/external/corefx/src/System.Collections.Immutable/ref/System.Collections.Immutable.cs.REMOVED.git-id index a238da56c5..324c1967c7 100644 --- a/external/corefx/src/System.Collections.Immutable/ref/System.Collections.Immutable.cs.REMOVED.git-id +++ b/external/corefx/src/System.Collections.Immutable/ref/System.Collections.Immutable.cs.REMOVED.git-id @@ -1 +1 @@ -fb673f1728bdd7c1c8e0271070ae6a55ac6c61a5 \ No newline at end of file +d78033ec633eb7e9da94552e8d1746cf9bd5d81c \ No newline at end of file diff --git a/external/corefx/src/System.Collections.Immutable/ref/System.Collections.Immutable.csproj b/external/corefx/src/System.Collections.Immutable/ref/System.Collections.Immutable.csproj index d6f08ccb91..0c7d5cdeb1 100644 --- a/external/corefx/src/System.Collections.Immutable/ref/System.Collections.Immutable.csproj +++ b/external/corefx/src/System.Collections.Immutable/ref/System.Collections.Immutable.csproj @@ -10,18 +10,29 @@ + + + + $(DefineConstants);FEATURE_ITEMREFAPI + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Collections.Immutable/src/Configurations.props b/external/corefx/src/System.Collections.Immutable/src/Configurations.props index 4d2a1e0f0d..048bc45cf4 100644 --- a/external/corefx/src/System.Collections.Immutable/src/Configurations.props +++ b/external/corefx/src/System.Collections.Immutable/src/Configurations.props @@ -3,6 +3,7 @@ netstandard1.0; + netstandard1.3; netstandard; diff --git a/external/corefx/src/System.Collections.Immutable/src/PEVerifyCompat.rsp b/external/corefx/src/System.Collections.Immutable/src/PEVerifyCompat.rsp deleted file mode 100644 index 0a7805fa7a..0000000000 --- a/external/corefx/src/System.Collections.Immutable/src/PEVerifyCompat.rsp +++ /dev/null @@ -1 +0,0 @@ -/features:peverify-compat \ No newline at end of file diff --git a/external/corefx/src/System.Collections.Immutable/src/Resources/Strings.resx b/external/corefx/src/System.Collections.Immutable/src/Resources/Strings.resx index d871641f8c..c7caeb7f25 100644 --- a/external/corefx/src/System.Collections.Immutable/src/Resources/Strings.resx +++ b/external/corefx/src/System.Collections.Immutable/src/Resources/Strings.resx @@ -58,6 +58,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + The given key '{0}' was not present in the dictionary. + Object is not an array with the same initialization state as the array to compare it to. diff --git a/external/corefx/src/System.Collections.Immutable/src/System.Collections.Immutable.csproj b/external/corefx/src/System.Collections.Immutable/src/System.Collections.Immutable.csproj index e335340572..2f18d5d099 100644 --- a/external/corefx/src/System.Collections.Immutable/src/System.Collections.Immutable.csproj +++ b/external/corefx/src/System.Collections.Immutable/src/System.Collections.Immutable.csproj @@ -9,7 +9,6 @@ 512 $(OutputPath)$(MSBuildProjectName).xml False - $(MSBuildThisFileDirectory)PEVerifyCompat.rsp;$(CompilerResponseFile) netstandard1.0;portable-net45+win8+wp8+wpa81 @@ -18,12 +17,16 @@ + + + + $(DefineConstants);FEATURE_ITEMREFAPI + - @@ -103,7 +106,7 @@ Common\System\Runtime\Versioning\NonVersionableAttribute.cs - + Common\System\Diagnostics\CodeAnalysis\ExcludeFromCodeCoverageAttribute.cs @@ -121,5 +124,8 @@ + + + \ No newline at end of file diff --git a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs index 9b0d02231a..34c0e004a9 100644 --- a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs +++ b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs @@ -157,6 +157,25 @@ namespace System.Collections.Immutable } } +#if FEATURE_ITEMREFAPI + /// + /// Gets a read-only reference to the element at the specified index. + /// + /// The index. + /// + /// + /// + public ref readonly T ItemRef(int index) + { + if (index >= this.Count) + { + throw new IndexOutOfRangeException(); + } + + return ref this._elements[index]; + } +#endif + /// /// Gets a value indicating whether the is read-only. /// diff --git a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Minimal.cs b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Minimal.cs index b5a30ae825..46be74ea2d 100644 --- a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Minimal.cs +++ b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Minimal.cs @@ -129,6 +129,23 @@ namespace System.Collections.Immutable } } +#if FEATURE_ITEMREFAPI + /// + /// Gets a read-only reference to the element at the specified index in the read-only list. + /// + /// The zero-based index of the element to get a reference to. + /// A read-only reference to the element at the specified index in the read-only list. + public ref readonly T ItemRef(int index) + { + // We intentionally do not check this.array != null, and throw NullReferenceException + // if this is called while uninitialized. + // The reason for this is perf. + // Length and the indexer must be absolutely trivially implemented for the JIT optimization + // of removing array bounds checking to work. + return ref this.array[index]; + } +#endif + /// /// Gets a value indicating whether this collection is empty. /// diff --git a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs index 10df32e97c..85d5645ec8 100644 --- a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs +++ b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs @@ -419,7 +419,7 @@ namespace System.Collections.Immutable return value; } - throw new KeyNotFoundException(); + throw new KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key.ToString())); } set diff --git a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.HashBucket.cs b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.HashBucket.cs index 44376e9515..ac17c0038e 100644 --- a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.HashBucket.cs +++ b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.HashBucket.cs @@ -15,7 +15,7 @@ namespace System.Collections.Immutable /// /// Contains all the key/values in the collection that hash to the same value. /// - internal struct HashBucket : IEnumerable> + internal readonly struct HashBucket : IEnumerable> { /// /// One of the values in this bucket. @@ -186,7 +186,11 @@ namespace System.Collections.Immutable result = OperationResult.NoChangeRequired; return this; case KeyCollisionBehavior.ThrowIfValueDifferent: +#if FEATURE_ITEMREFAPI + ref readonly var existingEntry = ref _additionalElements.ItemRef(keyCollisionIndex); +#else var existingEntry = _additionalElements[keyCollisionIndex]; +#endif if (!valueComparer.Equals(existingEntry.Value, value)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, SR.DuplicateKey, key)); @@ -277,7 +281,11 @@ namespace System.Collections.Immutable return false; } +#if FEATURE_ITEMREFAPI + value = _additionalElements.ItemRef(index).Value; +#else value = _additionalElements[index].Value; +#endif return true; } @@ -316,7 +324,11 @@ namespace System.Collections.Immutable return false; } +#if FEATURE_ITEMREFAPI + actualKey = _additionalElements.ItemRef(index).Key; +#else actualKey = _additionalElements[index].Key; +#endif return true; } diff --git a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.MutationInput.cs b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.MutationInput.cs index 277ef993b0..22c3462aba 100644 --- a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.MutationInput.cs +++ b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.MutationInput.cs @@ -15,7 +15,7 @@ namespace System.Collections.Immutable /// Description of the current data structure as input into a /// mutating or query method. /// - private struct MutationInput + private readonly struct MutationInput { /// /// The root of the data structure for the collection. diff --git a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.MutationResult.cs b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.MutationResult.cs index b3fb207ea0..1872f38ceb 100644 --- a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.MutationResult.cs +++ b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.MutationResult.cs @@ -12,7 +12,7 @@ namespace System.Collections.Immutable /// /// Describes the result of a mutation on the immutable data structure. /// - private struct MutationResult + private readonly struct MutationResult { /// /// The root node of the data structure after the mutation. diff --git a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.HashBucket.cs b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.HashBucket.cs index dc0382dbcd..8f33e61760 100644 --- a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.HashBucket.cs +++ b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.HashBucket.cs @@ -31,7 +31,7 @@ namespace System.Collections.Immutable /// /// Contains all the keys in the collection that hash to the same value. /// - internal struct HashBucket + internal readonly struct HashBucket { /// /// One of the values in this bucket. @@ -182,7 +182,11 @@ namespace System.Collections.Immutable int index = _additionalElements.IndexOf(value, valueComparer); if (index >= 0) { +#if FEATURE_ITEMREFAPI + existingValue = _additionalElements.ItemRef(index); +#else existingValue = _additionalElements[index]; +#endif return true; } } diff --git a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.MutationInput.cs b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.MutationInput.cs index 5879ff1bb5..1c0ab7a81c 100644 --- a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.MutationInput.cs +++ b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.MutationInput.cs @@ -15,7 +15,7 @@ namespace System.Collections.Immutable /// Description of the current data structure as input into a /// mutating or query method. /// - private struct MutationInput + private readonly struct MutationInput { /// /// The root of the data structure for the collection. diff --git a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.MutationResult.cs b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.MutationResult.cs index 245c2934d2..89e88b72b0 100644 --- a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.MutationResult.cs +++ b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.MutationResult.cs @@ -28,7 +28,7 @@ namespace System.Collections.Immutable /// /// Describes the result of a mutation on the immutable data structure. /// - private struct MutationResult + private readonly struct MutationResult { /// /// The root node of the data structure after the mutation. diff --git a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.NodeEnumerable.cs b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.NodeEnumerable.cs index 8d6badb592..706bd1493e 100644 --- a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.NodeEnumerable.cs +++ b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.NodeEnumerable.cs @@ -15,7 +15,7 @@ namespace System.Collections.Immutable /// /// Enumerates over a sorted dictionary used for hash buckets. /// - private struct NodeEnumerable : IEnumerable + private readonly struct NodeEnumerable : IEnumerable { /// /// The root of the sorted dictionary to enumerate. diff --git a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Builder.cs b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Builder.cs index 2fb2004c54..6904efb73c 100644 --- a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Builder.cs +++ b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Builder.cs @@ -135,7 +135,11 @@ namespace System.Collections.Immutable { get { +#if FEATURE_ITEMREFAPI + return this.Root.ItemRef(index); +#else return this.Root[index]; +#endif } set @@ -155,6 +159,18 @@ namespace System.Collections.Immutable } } +#if FEATURE_ITEMREFAPI + /// + /// Gets a read-only reference to the value for a given index into the list. + /// + /// The index of the desired element. + /// A read-only reference to the value at the specified index. + public ref readonly T ItemRef(int index) + { + return ref this.Root.ItemRef(index); + } + #endif + #endregion #region IList Methods diff --git a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Node.cs b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Node.cs index aad42615b3..460e3d4558 100644 --- a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Node.cs +++ b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Node.cs @@ -187,6 +187,30 @@ namespace System.Collections.Immutable } } +#if FEATURE_ITEMREFAPI + /// + /// Gets a read-only reference to the element of the set at the given index. + /// + /// The 0-based index of the element in the set to return. + /// A read-only reference to the element at the given position. + internal ref readonly T ItemRef(int index) + { + Requires.Range(index >= 0 && index < this.Count, nameof(index)); + + if (index < _left._count) + { + return ref _left.ItemRef(index); + } + + if (index > _left._count) + { + return ref _right.ItemRef(index - _left._count - 1); + } + + return ref _key; + } +#endif + #region IEnumerable Members /// @@ -500,8 +524,13 @@ namespace System.Collections.Immutable int end = index + count - 1; while (start < end) { +#if FEATURE_ITEMREFAPI + T a = result.ItemRef(start); + T b = result.ItemRef(end); +#else T a = result[start]; T b = result[end]; +#endif result = result .ReplaceAt(end, a) .ReplaceAt(start, b); diff --git a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.cs b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.cs index 6f0eb3bc9b..50dfe618a1 100644 --- a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.cs +++ b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.cs @@ -169,7 +169,21 @@ namespace System.Collections.Immutable /// The 0-based index of the element in the set to return. /// The element at the given position. /// Thrown from getter when is negative or not less than . +#if FEATURE_ITEMREFAPI + public T this[int index] => _root.ItemRef(index); +#else public T this[int index] => _root[index]; +#endif + +#if FEATURE_ITEMREFAPI + /// + /// Gets a read-only reference to the element of the set at the given index. + /// + /// The 0-based index of the element in the set to return. + /// A read-only reference to the element at the given position. + /// Thrown when is negative or not less than . + public ref readonly T ItemRef(int index) => ref _root.ItemRef(index); +#endif #endregion diff --git a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableQueue_1.cs b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableQueue_1.cs index cd7117fb6c..e544973267 100644 --- a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableQueue_1.cs +++ b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableQueue_1.cs @@ -141,6 +141,23 @@ namespace System.Collections.Immutable return _forwards.Peek(); } +#if FEATURE_ITEMREFAPI + /// + /// Gets a read-only reference to the element at the front of the queue. + /// + /// Thrown when the queue is empty. + [Pure] + public ref readonly T PeekRef() + { + if (this.IsEmpty) + { + throw new InvalidOperationException(SR.InvalidEmptyOperation); + } + + return ref _forwards.PeekRef(); + } +#endif + /// /// Adds an element to the back of the queue. /// diff --git a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Builder.cs b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Builder.cs index dcb1f90dca..f896ab05b6 100644 --- a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Builder.cs +++ b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Builder.cs @@ -188,7 +188,7 @@ namespace System.Collections.Immutable return value; } - throw new KeyNotFoundException(); + throw new KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key.ToString())); } set @@ -202,6 +202,19 @@ namespace System.Collections.Immutable } } +#if FEATURE_ITEMREFAPI + /// + /// Returns a read-only reference to the value associated with the provided key. + /// + /// If the key is not present. + public ref readonly TValue ValueRef(TKey key) + { + Requires.NotNullAllowStructs(key, nameof(key)); + + return ref _root.ValueRef(key, _keyComparer); + } +#endif + #endregion #region IDictionary Properties diff --git a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Node.cs b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Node.cs index 45acf19e4b..8e6eb8a13c 100644 --- a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Node.cs +++ b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Node.cs @@ -1,3 +1,6 @@ +// 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.Collections.Generic; using System.Diagnostics; @@ -330,6 +333,27 @@ namespace System.Collections.Immutable return this.RemoveRecursive(key, keyComparer, out mutated); } +#if FEATURE_ITEMREFAPI + /// + /// Returns a read-only reference to the value associated with the provided key. + /// + /// If the key is not present. + [Pure] + internal ref readonly TValue ValueRef(TKey key, IComparer keyComparer) + { + Requires.NotNullAllowStructs(key, nameof(key)); + Requires.NotNull(keyComparer, nameof(keyComparer)); + + var match = this.Search(key, keyComparer); + if (match.IsEmpty) + { + throw new KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key.ToString())); + } + + return ref match._value; + } +#endif + /// /// Tries to get the value. /// diff --git a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.cs b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.cs index 8a78c25ae8..43ca73875d 100644 --- a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.cs +++ b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.cs @@ -208,10 +208,23 @@ namespace System.Collections.Immutable return value; } - throw new KeyNotFoundException(); + throw new KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key.ToString())); } } +#if FEATURE_ITEMREFAPI + /// + /// Returns a read-only reference to the value associated with the provided key. + /// + /// If the key is not present. + public ref readonly TValue ValueRef(TKey key) + { + Requires.NotNullAllowStructs(key, nameof(key)); + + return ref _root.ValueRef(key, _keyComparer); + } +#endif + #endregion /// diff --git a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Builder.cs b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Builder.cs index 890714ecd7..a6dde9fd99 100644 --- a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Builder.cs +++ b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Builder.cs @@ -105,9 +105,25 @@ namespace System.Collections.Immutable /// public T this[int index] { +#if FEATURE_ITEMREFAPI + get { return _root.ItemRef(index); } +#else get { return _root[index]; } +#endif } +#if FEATURE_ITEMREFAPI + /// + /// Gets a read-only reference to the element of the set at the given index. + /// + /// The 0-based index of the element in the set to return. + /// A read-only reference to the element at the given position. + public ref readonly T ItemRef(int index) + { + return ref _root.ItemRef(index); + } +#endif + /// /// Gets the maximum value in the collection, as defined by the comparer. /// diff --git a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Node.cs b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Node.cs index 7b89d6a209..f488c3dd16 100644 --- a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Node.cs +++ b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Node.cs @@ -253,6 +253,30 @@ namespace System.Collections.Immutable } } +#if FEATURE_ITEMREFAPI + /// + /// Gets a read-only reference to the element of the set at the given index. + /// + /// The 0-based index of the element in the set to return. + /// A read-only reference to the element at the given position. + internal ref readonly T ItemRef(int index) + { + Requires.Range(index >= 0 && index < this.Count, nameof(index)); + + if (index < _left._count) + { + return ref _left.ItemRef(index); + } + + if (index > _left._count) + { + return ref _right.ItemRef(index - _left._count - 1); + } + + return ref _key; + } +#endif + #region IEnumerable Members /// diff --git a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.cs b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.cs index 9d885be8e1..7d5fe120a9 100644 --- a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.cs +++ b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.cs @@ -150,10 +150,26 @@ namespace System.Collections.Immutable { get { +#if FEATURE_ITEMREFAPI + return _root.ItemRef(index); +#else return _root[index]; +#endif } } +#if FEATURE_ITEMREFAPI + /// + /// Gets a read-only reference of the element of the set at the given index. + /// + /// The 0-based index of the element in the set to return. + /// A read-only reference of the element at the given position. + public ref readonly T ItemRef(int index) + { + return ref _root.ItemRef(index); + } +#endif + #endregion #region Public methods diff --git a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableStack_1.cs b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableStack_1.cs index be4299e418..284cdc0811 100644 --- a/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableStack_1.cs +++ b/external/corefx/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableStack_1.cs @@ -121,6 +121,26 @@ namespace System.Collections.Immutable return _head; } +#if FEATURE_ITEMREFAPI + /// + /// Gets a read-only reference to the element on the top of the stack. + /// + /// + /// A read-only reference to the element on the top of the stack. + /// + /// Thrown when the stack is empty. + [Pure] + public ref readonly T PeekRef() + { + if (this.IsEmpty) + { + throw new InvalidOperationException(SR.InvalidEmptyOperation); + } + + return ref _head; + } +#endif + /// /// Pushes an element onto a stack and returns the new stack. /// diff --git a/external/corefx/src/System.Collections.Immutable/tests/ImmutableArrayBuilderTest.cs b/external/corefx/src/System.Collections.Immutable/tests/ImmutableArrayBuilderTest.cs index e21264659e..490a996f86 100644 --- a/external/corefx/src/System.Collections.Immutable/tests/ImmutableArrayBuilderTest.cs +++ b/external/corefx/src/System.Collections.Immutable/tests/ImmutableArrayBuilderTest.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using Xunit; namespace System.Collections.Immutable.Tests @@ -726,6 +727,35 @@ namespace System.Collections.Immutable.Tests Assert.IsType(tie.InnerException); } + [Fact] + public void ItemRef() + { + var builder = new ImmutableArray.Builder(); + builder.Add(1); + builder.Add(2); + builder.Add(3); + + ref readonly var safeRef = ref builder.ItemRef(1); + ref var unsafeRef = ref Unsafe.AsRef(safeRef); + + Assert.Equal(2, builder.ItemRef(1)); + + unsafeRef = 4; + + Assert.Equal(4, builder.ItemRef(1)); + } + + [Fact] + public void ItemRef_OutOfBounds() + { + var builder = new ImmutableArray.Builder(); + builder.Add(1); + builder.Add(2); + builder.Add(3); + + Assert.Throws(() => builder.ItemRef(5)); + } + private static ImmutableArray.Builder CreateBuilderWithCount(int count) { var builder = ImmutableArray.CreateBuilder(count); diff --git a/external/corefx/src/System.Collections.Immutable/tests/ImmutableArrayTest.cs.REMOVED.git-id b/external/corefx/src/System.Collections.Immutable/tests/ImmutableArrayTest.cs.REMOVED.git-id index fcc8cd0105..03dac7cbdb 100644 --- a/external/corefx/src/System.Collections.Immutable/tests/ImmutableArrayTest.cs.REMOVED.git-id +++ b/external/corefx/src/System.Collections.Immutable/tests/ImmutableArrayTest.cs.REMOVED.git-id @@ -1 +1 @@ -58cd6cde7942608a770e571fe5dd368d59d6a434 \ No newline at end of file +dbe75bfd0766c528ea80b32a0eb5c8579f9b20af \ No newline at end of file diff --git a/external/corefx/src/System.Collections.Immutable/tests/ImmutableListBuilderTest.cs b/external/corefx/src/System.Collections.Immutable/tests/ImmutableListBuilderTest.cs index c7ee4a67a8..831fce435b 100644 --- a/external/corefx/src/System.Collections.Immutable/tests/ImmutableListBuilderTest.cs +++ b/external/corefx/src/System.Collections.Immutable/tests/ImmutableListBuilderTest.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using Xunit; namespace System.Collections.Immutable.Tests @@ -364,6 +365,31 @@ namespace System.Collections.Immutable.Tests Assert.IsType(tie.InnerException); } + [Fact] + public void ItemRef() + { + var list = new[] { 1, 2, 3 }.ToImmutableList(); + var builder = new ImmutableList.Builder(list); + + ref readonly var safeRef = ref builder.ItemRef(1); + ref var unsafeRef = ref Unsafe.AsRef(safeRef); + + Assert.Equal(2, builder.ItemRef(1)); + + unsafeRef = 4; + + Assert.Equal(4, builder.ItemRef(1)); + } + + [Fact] + public void ItemRef_OutOfBounds() + { + var list = new[] { 1, 2, 3 }.ToImmutableList(); + var builder = new ImmutableList.Builder(list); + + Assert.Throws(() => builder.ItemRef(5)); + } + protected override IEnumerable GetEnumerableOf(params T[] contents) { return ImmutableList.Empty.AddRange(contents).ToBuilder(); diff --git a/external/corefx/src/System.Collections.Immutable/tests/ImmutableListTest.cs b/external/corefx/src/System.Collections.Immutable/tests/ImmutableListTest.cs index 513375ed3c..daa33c1115 100644 --- a/external/corefx/src/System.Collections.Immutable/tests/ImmutableListTest.cs +++ b/external/corefx/src/System.Collections.Immutable/tests/ImmutableListTest.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; +using System.Runtime.CompilerServices; using Xunit; namespace System.Collections.Immutable.Tests @@ -815,6 +816,29 @@ namespace System.Collections.Immutable.Tests } #endif // netcoreapp + [Fact] + public void ItemRef() + { + var list = new[] { 1, 2, 3 }.ToImmutableList(); + + ref readonly var safeRef = ref list.ItemRef(1); + ref var unsafeRef = ref Unsafe.AsRef(safeRef); + + Assert.Equal(2, list.ItemRef(1)); + + unsafeRef = 4; + + Assert.Equal(4, list.ItemRef(1)); + } + + [Fact] + public void ItemRef_OutOfBounds() + { + var list = new[] { 1, 2, 3 }.ToImmutableList(); + + Assert.Throws(() => list.ItemRef(5)); + } + protected override IEnumerable GetEnumerableOf(params T[] contents) { return ImmutableList.Empty.AddRange(contents); diff --git a/external/corefx/src/System.Collections.Immutable/tests/ImmutableQueueTest.cs b/external/corefx/src/System.Collections.Immutable/tests/ImmutableQueueTest.cs index 7c65177e6c..c172707d52 100644 --- a/external/corefx/src/System.Collections.Immutable/tests/ImmutableQueueTest.cs +++ b/external/corefx/src/System.Collections.Immutable/tests/ImmutableQueueTest.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using Xunit; namespace System.Collections.Immutable.Tests @@ -257,6 +258,32 @@ namespace System.Collections.Immutable.Tests Assert.IsType(tie.InnerException); } + [Fact] + public void PeekRef() + { + var queue = ImmutableQueue.Empty + .Enqueue(1) + .Enqueue(2) + .Enqueue(3); + + ref readonly var safeRef = ref queue.PeekRef(); + ref var unsafeRef = ref Unsafe.AsRef(safeRef); + + Assert.Equal(1, queue.PeekRef()); + + unsafeRef = 4; + + Assert.Equal(4, queue.PeekRef()); + } + + [Fact] + public void PeekRef_Empty() + { + var queue = ImmutableQueue.Empty; + + Assert.Throws(() => queue.PeekRef()); + } + protected override IEnumerable GetEnumerableOf(params T[] contents) { var queue = ImmutableQueue.Empty; diff --git a/external/corefx/src/System.Collections.Immutable/tests/ImmutableSortedDictionaryBuilderTest.cs b/external/corefx/src/System.Collections.Immutable/tests/ImmutableSortedDictionaryBuilderTest.cs index 019c49d7dc..6dc3fb3d32 100644 --- a/external/corefx/src/System.Collections.Immutable/tests/ImmutableSortedDictionaryBuilderTest.cs +++ b/external/corefx/src/System.Collections.Immutable/tests/ImmutableSortedDictionaryBuilderTest.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using Xunit; namespace System.Collections.Immutable.Tests @@ -278,6 +279,37 @@ namespace System.Collections.Immutable.Tests Assert.IsType(tie.InnerException); } + [Fact] + public void ValueRef() + { + var builder = new Dictionary() + { + { "a", 1 }, + { "b", 2 } + }.ToImmutableSortedDictionary().ToBuilder(); + + ref readonly var safeRef = ref builder.ValueRef("a"); + ref var unsafeRef = ref Unsafe.AsRef(safeRef); + + Assert.Equal(1, builder.ValueRef("a")); + + unsafeRef = 5; + + Assert.Equal(5, builder.ValueRef("a")); + } + + [Fact] + public void ValueRef_NonExistentKey() + { + var builder = new Dictionary() + { + { "a", 1 }, + { "b", 2 } + }.ToImmutableSortedDictionary().ToBuilder(); + + Assert.Throws(() => builder.ValueRef("c")); + } + protected override IImmutableDictionary GetEmptyImmutableDictionary() { return ImmutableSortedDictionary.Create(); diff --git a/external/corefx/src/System.Collections.Immutable/tests/ImmutableSortedDictionaryTest.cs b/external/corefx/src/System.Collections.Immutable/tests/ImmutableSortedDictionaryTest.cs index 6b9e471031..db19f3ce10 100644 --- a/external/corefx/src/System.Collections.Immutable/tests/ImmutableSortedDictionaryTest.cs +++ b/external/corefx/src/System.Collections.Immutable/tests/ImmutableSortedDictionaryTest.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using Xunit; namespace System.Collections.Immutable.Tests @@ -479,6 +480,37 @@ namespace System.Collections.Immutable.Tests Debug.WriteLine("Timing_Empty:{0}{1}", Environment.NewLine, timingText); } + [Fact] + public void ValueRef() + { + var dictionary = new Dictionary() + { + { "a", 1 }, + { "b", 2 } + }.ToImmutableSortedDictionary(); + + ref readonly var safeRef = ref dictionary.ValueRef("a"); + ref var unsafeRef = ref Unsafe.AsRef(safeRef); + + Assert.Equal(1, dictionary.ValueRef("a")); + + unsafeRef = 5; + + Assert.Equal(5, dictionary.ValueRef("a")); + } + + [Fact] + public void ValueRef_NonExistentKey() + { + var dictionary = new Dictionary() + { + { "a", 1 }, + { "b", 2 } + }.ToImmutableSortedDictionary(); + + Assert.Throws(() => dictionary.ValueRef("c")); + } + protected override IImmutableDictionary Empty() { return ImmutableSortedDictionaryTest.Empty(); diff --git a/external/corefx/src/System.Collections.Immutable/tests/ImmutableSortedSetBuilderTest.cs b/external/corefx/src/System.Collections.Immutable/tests/ImmutableSortedSetBuilderTest.cs index 11aa1fcdf1..dd78062363 100644 --- a/external/corefx/src/System.Collections.Immutable/tests/ImmutableSortedSetBuilderTest.cs +++ b/external/corefx/src/System.Collections.Immutable/tests/ImmutableSortedSetBuilderTest.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using Xunit; namespace System.Collections.Immutable.Tests @@ -375,5 +376,30 @@ namespace System.Collections.Immutable.Tests TargetInvocationException tie = Assert.Throws(() => Activator.CreateInstance(proxyType, (object)null)); Assert.IsType(tie.InnerException); } + + [Fact] + public void ItemRef() + { + var array = new[] { 1, 2, 3 }.ToImmutableSortedSet(); + var builder = new ImmutableSortedSet.Builder(array); + + ref readonly var safeRef = ref builder.ItemRef(1); + ref var unsafeRef = ref Unsafe.AsRef(safeRef); + + Assert.Equal(2, builder.ItemRef(1)); + + unsafeRef = 4; + + Assert.Equal(4, builder.ItemRef(1)); + } + + [Fact] + public void ItemRef_OutOfBounds() + { + var array = new[] { 1, 2, 3 }.ToImmutableSortedSet(); + var builder = new ImmutableSortedSet.Builder(array); + + Assert.Throws (() => builder.ItemRef(5)); + } } } diff --git a/external/corefx/src/System.Collections.Immutable/tests/ImmutableSortedSetTest.cs b/external/corefx/src/System.Collections.Immutable/tests/ImmutableSortedSetTest.cs index a7eb1332ca..0a0be26dec 100644 --- a/external/corefx/src/System.Collections.Immutable/tests/ImmutableSortedSetTest.cs +++ b/external/corefx/src/System.Collections.Immutable/tests/ImmutableSortedSetTest.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using Xunit; namespace System.Collections.Immutable.Tests @@ -407,6 +408,29 @@ namespace System.Collections.Immutable.Tests CollectionAssertAreEquivalent(expectedSet.ToList(), actualSet.ToList()); } + [Fact] + public void ItemRef() + { + var array = new[] { 1, 2, 3 }.ToImmutableSortedSet(); + + ref readonly var safeRef = ref array.ItemRef(1); + ref var unsafeRef = ref Unsafe.AsRef(safeRef); + + Assert.Equal(2, array.ItemRef(1)); + + unsafeRef = 4; + + Assert.Equal(4, array.ItemRef(1)); + } + + [Fact] + public void ItemRef_OutOfBounds() + { + var array = new[] { 1, 2, 3 }.ToImmutableSortedSet(); + + Assert.Throws(() => array.ItemRef(5)); + } + protected override IImmutableSet Empty() { return ImmutableSortedSet.Empty; diff --git a/external/corefx/src/System.Collections.Immutable/tests/ImmutableStackTest.cs b/external/corefx/src/System.Collections.Immutable/tests/ImmutableStackTest.cs index 407ae64362..2fdc683110 100644 --- a/external/corefx/src/System.Collections.Immutable/tests/ImmutableStackTest.cs +++ b/external/corefx/src/System.Collections.Immutable/tests/ImmutableStackTest.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using Xunit; namespace System.Collections.Immutable.Tests @@ -274,6 +275,32 @@ namespace System.Collections.Immutable.Tests Assert.IsType(tie.InnerException); } + [Fact] + public void PeekRef() + { + var stack = ImmutableStack.Empty + .Push(1) + .Push(2) + .Push(3); + + ref readonly var safeRef = ref stack.PeekRef(); + ref var unsafeRef = ref Unsafe.AsRef(safeRef); + + Assert.Equal(3, stack.PeekRef()); + + unsafeRef = 4; + + Assert.Equal(4, stack.PeekRef()); + } + + [Fact] + public void PeekRef_Empty() + { + var stack = ImmutableStack.Empty; + + Assert.Throws(() => stack.PeekRef()); + } + protected override IEnumerable GetEnumerableOf(params T[] contents) { var stack = ImmutableStack.Empty; diff --git a/external/corefx/src/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csproj b/external/corefx/src/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csproj index 55d43aa6d1..91b31e06e0 100644 --- a/external/corefx/src/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csproj +++ b/external/corefx/src/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csproj @@ -6,7 +6,6 @@ 0436 $(DefineConstants);netcoreapp - diff --git a/external/corefx/src/System.Collections.NonGeneric/System.Collections.NonGeneric.sln b/external/corefx/src/System.Collections.NonGeneric/System.Collections.NonGeneric.sln index 5d52e75f7c..74363db156 100644 --- a/external/corefx/src/System.Collections.NonGeneric/System.Collections.NonGeneric.sln +++ b/external/corefx/src/System.Collections.NonGeneric/System.Collections.NonGeneric.sln @@ -35,10 +35,10 @@ Global {EE95AE39-845A-42D3-86D0-8065DBE56612}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU {EE95AE39-845A-42D3-86D0-8065DBE56612}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU {EE95AE39-845A-42D3-86D0-8065DBE56612}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU - {93AB3799-BC91-4B27-B526-28FFFF0DB1B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {93AB3799-BC91-4B27-B526-28FFFF0DB1B1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {93AB3799-BC91-4B27-B526-28FFFF0DB1B1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {93AB3799-BC91-4B27-B526-28FFFF0DB1B1}.Release|Any CPU.Build.0 = Release|Any CPU + {93AB3799-BC91-4B27-B526-28FFFF0DB1B1}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {93AB3799-BC91-4B27-B526-28FFFF0DB1B1}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {93AB3799-BC91-4B27-B526-28FFFF0DB1B1}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {93AB3799-BC91-4B27-B526-28FFFF0DB1B1}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU {585E3764-534B-4A12-8BD5-8578CB826A45}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU {585E3764-534B-4A12-8BD5-8578CB826A45}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {585E3764-534B-4A12-8BD5-8578CB826A45}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU diff --git a/external/corefx/src/System.Collections.NonGeneric/src/System.Collections.NonGeneric.csproj b/external/corefx/src/System.Collections.NonGeneric/src/System.Collections.NonGeneric.csproj index 60a3eb27c5..8b7f9d0d90 100644 --- a/external/corefx/src/System.Collections.NonGeneric/src/System.Collections.NonGeneric.csproj +++ b/external/corefx/src/System.Collections.NonGeneric/src/System.Collections.NonGeneric.csproj @@ -7,7 +7,6 @@ System.Collections.NonGeneric true - @@ -28,7 +27,6 @@ - @@ -37,4 +35,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Collections.NonGeneric/src/System/Collections/CaseInsensitiveComparer.cs b/external/corefx/src/System.Collections.NonGeneric/src/System/Collections/CaseInsensitiveComparer.cs index 86b3afd2db..81e0f0b207 100644 --- a/external/corefx/src/System.Collections.NonGeneric/src/System/Collections/CaseInsensitiveComparer.cs +++ b/external/corefx/src/System.Collections.NonGeneric/src/System/Collections/CaseInsensitiveComparer.cs @@ -12,7 +12,6 @@ ============================================================*/ using System.Globalization; -using System.Diagnostics.Contracts; namespace System.Collections { @@ -33,7 +32,6 @@ namespace System.Collections { throw new ArgumentNullException(nameof(culture)); } - Contract.EndContractBlock(); _compareInfo = culture.CompareInfo; } @@ -41,8 +39,6 @@ namespace System.Collections { get { - Contract.Ensures(Contract.Result() != null); - return new CaseInsensitiveComparer(CultureInfo.CurrentCulture); } } @@ -51,8 +47,6 @@ namespace System.Collections { get { - Contract.Ensures(Contract.Result() != null); - if (s_InvariantCaseInsensitiveComparer == null) { s_InvariantCaseInsensitiveComparer = new CaseInsensitiveComparer(CultureInfo.InvariantCulture); diff --git a/external/corefx/src/System.Collections.NonGeneric/src/System/Collections/CollectionBase.cs b/external/corefx/src/System.Collections.NonGeneric/src/System/Collections/CollectionBase.cs index 0bb5c8a6b6..1e4166f246 100644 --- a/external/corefx/src/System.Collections.NonGeneric/src/System/Collections/CollectionBase.cs +++ b/external/corefx/src/System.Collections.NonGeneric/src/System/Collections/CollectionBase.cs @@ -10,8 +10,6 @@ ** =============================================================================*/ -using System.Diagnostics.Contracts; - namespace System.Collections { // Useful base class for typed read/write collections where items derive from object @@ -76,7 +74,6 @@ namespace System.Collections { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); Object temp = InnerList[index]; OnValidate(temp); OnRemove(index, temp); @@ -123,14 +120,12 @@ namespace System.Collections { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); return InnerList[index]; } set { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); OnValidate(value); Object temp = InnerList[index]; OnSet(index, temp, value); @@ -197,7 +192,6 @@ namespace System.Collections { if (index < 0 || index > Count) throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); OnValidate(value); OnInsert(index, value); InnerList.Insert(index, value); @@ -236,7 +230,6 @@ namespace System.Collections protected virtual void OnValidate(Object value) { if (value == null) throw new ArgumentNullException(nameof(value)); - Contract.EndContractBlock(); } protected virtual void OnSetComplete(int index, Object oldValue, Object newValue) diff --git a/external/corefx/src/System.Collections.NonGeneric/src/System/Collections/Queue.cs b/external/corefx/src/System.Collections.NonGeneric/src/System/Collections/Queue.cs index 4bfdeef453..29d8c5e538 100644 --- a/external/corefx/src/System.Collections.NonGeneric/src/System/Collections/Queue.cs +++ b/external/corefx/src/System.Collections.NonGeneric/src/System/Collections/Queue.cs @@ -12,7 +12,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; namespace System.Collections { @@ -62,7 +61,6 @@ namespace System.Collections throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_NeedNonNegNum); if (!(growFactor >= 1.0 && growFactor <= 10.0)) throw new ArgumentOutOfRangeException(nameof(growFactor), SR.Format(SR.ArgumentOutOfRange_QueueGrowFactor, 1, 10)); - Contract.EndContractBlock(); _array = new Object[capacity]; _head = 0; @@ -78,7 +76,7 @@ namespace System.Collections { if (col == null) throw new ArgumentNullException(nameof(col)); - Contract.EndContractBlock(); + IEnumerator en = col.GetEnumerator(); while (en.MoveNext()) Enqueue(en.Current); @@ -155,7 +153,7 @@ namespace System.Collections throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); if (index < 0) throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); + int arrayLen = array.Length; if (arrayLen - index < _size) throw new ArgumentException(SR.Argument_InvalidOffLen); @@ -204,7 +202,6 @@ namespace System.Collections { if (Count == 0) throw new InvalidOperationException(SR.InvalidOperation_EmptyQueue); - Contract.EndContractBlock(); Object removed = _array[_head]; _array[_head] = null; @@ -221,7 +218,6 @@ namespace System.Collections { if (Count == 0) throw new InvalidOperationException(SR.InvalidOperation_EmptyQueue); - Contract.EndContractBlock(); return _array[_head]; } @@ -234,7 +230,7 @@ namespace System.Collections { if (queue == null) throw new ArgumentNullException(nameof(queue)); - Contract.EndContractBlock(); + return new SynchronizedQueue(queue); } @@ -519,7 +515,6 @@ namespace System.Collections { if (queue == null) throw new ArgumentNullException(nameof(queue)); - Contract.EndContractBlock(); _queue = queue; } diff --git a/external/corefx/src/System.Collections.NonGeneric/src/System/Collections/SortedList.cs b/external/corefx/src/System.Collections.NonGeneric/src/System/Collections/SortedList.cs index 81cb92eef2..d44054713f 100644 --- a/external/corefx/src/System.Collections.NonGeneric/src/System/Collections/SortedList.cs +++ b/external/corefx/src/System.Collections.NonGeneric/src/System/Collections/SortedList.cs @@ -14,7 +14,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; using System.Globalization; namespace System.Collections @@ -111,7 +110,7 @@ namespace System.Collections { if (initialCapacity < 0) throw new ArgumentOutOfRangeException(nameof(initialCapacity), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); + keys = new Object[initialCapacity]; values = new Object[initialCapacity]; comparer = new Comparer(CultureInfo.CurrentCulture); @@ -172,7 +171,7 @@ namespace System.Collections { if (d == null) throw new ArgumentNullException(nameof(d), SR.ArgumentNull_Dictionary); - Contract.EndContractBlock(); + d.Keys.CopyTo(keys, 0); d.Values.CopyTo(values, 0); @@ -192,7 +191,7 @@ namespace System.Collections public virtual void Add(Object key, Object value) { if (key == null) throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); - Contract.EndContractBlock(); + int i = Array.BinarySearch(keys, 0, _size, key, comparer); if (i >= 0) throw new ArgumentException(SR.Format(SR.Argument_AddingDuplicate__, GetKey(i), key)); @@ -217,7 +216,6 @@ namespace System.Collections { throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_SmallCapacity); } - Contract.EndContractBlock(); if (value != keys.Length) { @@ -371,7 +369,7 @@ namespace System.Collections throw new ArgumentOutOfRangeException(nameof(arrayIndex), SR.ArgumentOutOfRange_NeedNonNegNum); if (array.Length - arrayIndex < Count) throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); - Contract.EndContractBlock(); + for (int i = 0; i < Count; i++) { DictionaryEntry entry = new DictionaryEntry(keys[i], values[i]); @@ -413,7 +411,6 @@ namespace System.Collections { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); return values[index]; } @@ -442,7 +439,6 @@ namespace System.Collections public virtual Object GetKey(int index) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); return keys[index]; } @@ -495,7 +491,6 @@ namespace System.Collections set { if (key == null) throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); - Contract.EndContractBlock(); int i = Array.BinarySearch(keys, 0, _size, key, comparer); if (i >= 0) { @@ -518,7 +513,6 @@ namespace System.Collections { if (key == null) throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); - Contract.EndContractBlock(); int ret = Array.BinarySearch(keys, 0, _size, key, comparer); return ret >= 0 ? ret : -1; } @@ -555,7 +549,6 @@ namespace System.Collections public virtual void RemoveAt(int index) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); _size--; if (index < _size) { @@ -584,7 +577,6 @@ namespace System.Collections public virtual void SetByIndex(int index, Object value) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); values[index] = value; version++; } @@ -595,7 +587,6 @@ namespace System.Collections { if (list == null) throw new ArgumentNullException(nameof(list)); - Contract.EndContractBlock(); return new SyncSortedList(list); } @@ -779,7 +770,6 @@ namespace System.Collections { if (key == null) throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); - Contract.EndContractBlock(); lock (_root) { @@ -995,7 +985,6 @@ namespace System.Collections { if (array != null && array.Rank != 1) throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); - Contract.EndContractBlock(); // defer error checking to Array.Copy Array.Copy(sortedList.keys, 0, array, arrayIndex, sortedList.Count); @@ -1027,7 +1016,6 @@ namespace System.Collections { if (key == null) throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); - Contract.EndContractBlock(); int i = Array.BinarySearch(sortedList.keys, 0, sortedList.Count, key, sortedList.comparer); @@ -1101,7 +1089,6 @@ namespace System.Collections { if (array != null && array.Rank != 1) throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); - Contract.EndContractBlock(); // defer error checking to Array.Copy Array.Copy(sortedList.values, 0, array, arrayIndex, sortedList.Count); @@ -1156,7 +1143,6 @@ namespace System.Collections { throw new ArgumentNullException(nameof(sortedList)); } - Contract.EndContractBlock(); _sortedList = sortedList; } diff --git a/external/corefx/src/System.Collections.NonGeneric/src/System/Collections/Stack.cs b/external/corefx/src/System.Collections.NonGeneric/src/System/Collections/Stack.cs index 5d40e432da..36d0679da7 100644 --- a/external/corefx/src/System.Collections.NonGeneric/src/System/Collections/Stack.cs +++ b/external/corefx/src/System.Collections.NonGeneric/src/System/Collections/Stack.cs @@ -13,8 +13,6 @@ =============================================================================*/ using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; namespace System.Collections { @@ -29,7 +27,6 @@ namespace System.Collections public class Stack : ICollection, ICloneable { private Object[] _array; // Storage for stack elements. Do not rename (binary serialization) - [ContractPublicPropertyName("Count")] private int _size; // Number of items in the stack. Do not rename (binary serialization) private int _version; // Used to keep enumerator in sync w/ collection. Do not rename (binary serialization) [NonSerialized] @@ -50,7 +47,7 @@ namespace System.Collections { if (initialCapacity < 0) throw new ArgumentOutOfRangeException(nameof(initialCapacity), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); + if (initialCapacity < _defaultCapacity) initialCapacity = _defaultCapacity; // Simplify doubling logic in Push. _array = new Object[initialCapacity]; @@ -65,7 +62,7 @@ namespace System.Collections { if (col == null) throw new ArgumentNullException(nameof(col)); - Contract.EndContractBlock(); + IEnumerator en = col.GetEnumerator(); while (en.MoveNext()) Push(en.Current); @@ -75,7 +72,6 @@ namespace System.Collections { get { - Contract.Ensures(Contract.Result() >= 0); return _size; } } @@ -107,8 +103,6 @@ namespace System.Collections public virtual Object Clone() { - Contract.Ensures(Contract.Result() != null); - Stack s = new Stack(_size); s._size = _size; Array.Copy(_array, 0, s._array, 0, _size); @@ -146,7 +140,6 @@ namespace System.Collections throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum); if (array.Length - index < _size) throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.EndContractBlock(); int i = 0; object[] objArray = array as object[]; @@ -171,7 +164,6 @@ namespace System.Collections // Returns an IEnumerator for this Stack. public virtual IEnumerator GetEnumerator() { - Contract.Ensures(Contract.Result() != null); return new StackEnumerator(this); } @@ -181,7 +173,7 @@ namespace System.Collections { if (_size == 0) throw new InvalidOperationException(SR.InvalidOperation_EmptyStack); - Contract.EndContractBlock(); + return _array[_size - 1]; } @@ -191,8 +183,7 @@ namespace System.Collections { if (_size == 0) throw new InvalidOperationException(SR.InvalidOperation_EmptyStack); - //Contract.Ensures(Count == Contract.OldValue(Count) - 1); - Contract.EndContractBlock(); + _version++; Object obj = _array[--_size]; _array[_size] = null; // Free memory quicker. @@ -203,7 +194,6 @@ namespace System.Collections // public virtual void Push(Object obj) { - //Contract.Ensures(Count == Contract.OldValue(Count) + 1); if (_size == _array.Length) { Object[] newArray = new Object[2 * _array.Length]; @@ -220,8 +210,7 @@ namespace System.Collections { if (stack == null) throw new ArgumentNullException(nameof(stack)); - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); + return new SyncStack(stack); } @@ -229,8 +218,6 @@ namespace System.Collections // Copies the Stack to an array, in the same order Pop would return the items. public virtual Object[] ToArray() { - Contract.Ensures(Contract.Result() != null); - if (_size == 0) return Array.Empty(); @@ -320,7 +307,6 @@ namespace System.Collections } } - [SuppressMessage("Microsoft.Contracts", "CC1055")] // Thread safety problems with precondition - can't express the precondition as of Dev10. public override Object Pop() { lock (_root) @@ -337,7 +323,6 @@ namespace System.Collections } } - [SuppressMessage("Microsoft.Contracts", "CC1055")] // Thread safety problems with precondition - can't express the precondition public override Object Peek() { lock (_root) @@ -424,7 +409,6 @@ namespace System.Collections { if (stack == null) throw new ArgumentNullException(nameof(stack)); - Contract.EndContractBlock(); _stack = stack; } diff --git a/external/corefx/src/System.Collections.NonGeneric/tests/Performance/System.Collections.NonGeneric.Performance.Tests.csproj b/external/corefx/src/System.Collections.NonGeneric/tests/Performance/System.Collections.NonGeneric.Performance.Tests.csproj index 364bdd2e5d..a6bb2fbd91 100644 --- a/external/corefx/src/System.Collections.NonGeneric/tests/Performance/System.Collections.NonGeneric.Performance.Tests.csproj +++ b/external/corefx/src/System.Collections.NonGeneric/tests/Performance/System.Collections.NonGeneric.Performance.Tests.csproj @@ -5,9 +5,8 @@ true {93AB3799-BC91-4B27-B526-28FFFF0DB1B1} - - - + + diff --git a/external/corefx/src/System.Collections.NonGeneric/tests/System.Collections.NonGeneric.Tests.csproj b/external/corefx/src/System.Collections.NonGeneric/tests/System.Collections.NonGeneric.Tests.csproj index 890fce1a45..3eb20f0828 100644 --- a/external/corefx/src/System.Collections.NonGeneric/tests/System.Collections.NonGeneric.Tests.csproj +++ b/external/corefx/src/System.Collections.NonGeneric/tests/System.Collections.NonGeneric.Tests.csproj @@ -4,7 +4,6 @@ {EE95AE39-845A-42D3-86D0-8065DBE56612} - diff --git a/external/corefx/src/System.Collections.Specialized/ref/System.Collections.Specialized.cs b/external/corefx/src/System.Collections.Specialized/ref/System.Collections.Specialized.cs index 45ea7f1f33..915e6cec09 100644 --- a/external/corefx/src/System.Collections.Specialized/ref/System.Collections.Specialized.cs +++ b/external/corefx/src/System.Collections.Specialized/ref/System.Collections.Specialized.cs @@ -5,12 +5,11 @@ // Changes to this file must follow the http://aka.ms/api-review process. // ------------------------------------------------------------------------------ - namespace System.Collections.Specialized { - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct BitVector32 { + private int _dummy; public BitVector32(System.Collections.Specialized.BitVector32 value) { throw null; } public BitVector32(int data) { throw null; } public int Data { get { throw null; } } @@ -24,9 +23,9 @@ namespace System.Collections.Specialized public override int GetHashCode() { throw null; } public override string ToString() { throw null; } public static string ToString(System.Collections.Specialized.BitVector32 value) { throw null; } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] - public partial struct Section + public readonly partial struct Section { + private readonly int _dummy; public short Mask { get { throw null; } } public short Offset { get { throw null; } } public bool Equals(System.Collections.Specialized.BitVector32.Section obj) { throw null; } @@ -87,7 +86,7 @@ namespace System.Collections.Specialized public void Remove(object key) { } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } - public abstract partial class NameObjectCollectionBase : System.Collections.ICollection, System.Collections.IEnumerable, System.Runtime.Serialization.ISerializable, System.Runtime.Serialization.IDeserializationCallback + public abstract partial class NameObjectCollectionBase : System.Collections.ICollection, System.Collections.IEnumerable, System.Runtime.Serialization.IDeserializationCallback, System.Runtime.Serialization.ISerializable { protected NameObjectCollectionBase() { } protected NameObjectCollectionBase(System.Collections.IEqualityComparer equalityComparer) { } @@ -162,7 +161,7 @@ namespace System.Collections.Specialized public virtual void Remove(string name) { } public virtual void Set(string name, string value) { } } - public partial class OrderedDictionary : System.Collections.ICollection, System.Collections.IDictionary, System.Collections.IEnumerable, System.Collections.Specialized.IOrderedDictionary, System.Runtime.Serialization.ISerializable, System.Runtime.Serialization.IDeserializationCallback + public partial class OrderedDictionary : System.Collections.ICollection, System.Collections.IDictionary, System.Collections.IEnumerable, System.Collections.Specialized.IOrderedDictionary, System.Runtime.Serialization.IDeserializationCallback, System.Runtime.Serialization.ISerializable { public OrderedDictionary() { } public OrderedDictionary(System.Collections.IEqualityComparer comparer) { } @@ -187,10 +186,10 @@ namespace System.Collections.Specialized public virtual void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public void Insert(int index, object key, object value) { } protected virtual void OnDeserialization(object sender) { } - void System.Runtime.Serialization.IDeserializationCallback.OnDeserialization(object sender) { } public void Remove(object key) { } public void RemoveAt(int index) { } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } + void System.Runtime.Serialization.IDeserializationCallback.OnDeserialization(object sender) { } } public partial class StringCollection : System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList { diff --git a/external/corefx/src/System.Collections.Specialized/src/System.Collections.Specialized.csproj b/external/corefx/src/System.Collections.Specialized/src/System.Collections.Specialized.csproj index c269f9333e..2c91049da0 100644 --- a/external/corefx/src/System.Collections.Specialized/src/System.Collections.Specialized.csproj +++ b/external/corefx/src/System.Collections.Specialized/src/System.Collections.Specialized.csproj @@ -6,7 +6,6 @@ System.Collections.Specialized System.Collections.Specialized - @@ -36,4 +35,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Collections.Specialized/src/System/Collections/Specialized/BitVector32.cs b/external/corefx/src/System.Collections.Specialized/src/System/Collections/Specialized/BitVector32.cs index e8d5df9284..086a987dd5 100644 --- a/external/corefx/src/System.Collections.Specialized/src/System/Collections/Specialized/BitVector32.cs +++ b/external/corefx/src/System.Collections.Specialized/src/System/Collections/Specialized/BitVector32.cs @@ -230,7 +230,7 @@ namespace System.Collections.Specialized /// /// Represents an section of the vector that can contain a integer number. /// - public struct Section + public readonly struct Section { private readonly short _mask; private readonly short _offset; diff --git a/external/corefx/src/System.Collections.Specialized/tests/StringDictionary/StringDictionary.GetEnumeratorTests.cs b/external/corefx/src/System.Collections.Specialized/tests/StringDictionary/StringDictionary.GetEnumeratorTests.cs index 5ee91c493b..97be2beae8 100644 --- a/external/corefx/src/System.Collections.Specialized/tests/StringDictionary/StringDictionary.GetEnumeratorTests.cs +++ b/external/corefx/src/System.Collections.Specialized/tests/StringDictionary/StringDictionary.GetEnumeratorTests.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements.--- +// 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. diff --git a/external/corefx/src/System.Collections.Specialized/tests/System.Collections.Specialized.Tests.csproj b/external/corefx/src/System.Collections.Specialized/tests/System.Collections.Specialized.Tests.csproj index 11183499e3..8b22d9cc38 100644 --- a/external/corefx/src/System.Collections.Specialized/tests/System.Collections.Specialized.Tests.csproj +++ b/external/corefx/src/System.Collections.Specialized/tests/System.Collections.Specialized.Tests.csproj @@ -4,7 +4,6 @@ {7F5F5134-00FE-4DE8-B20C-3DA8BA2EBA68} - @@ -95,4 +94,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Collections/System.Collections.sln b/external/corefx/src/System.Collections/System.Collections.sln index f4bc339464..0446203d85 100644 --- a/external/corefx/src/System.Collections/System.Collections.sln +++ b/external/corefx/src/System.Collections/System.Collections.sln @@ -7,7 +7,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Collections.Tests", {D5FF747F-7A0B-9003-885A-FE9A63E755E5} = {D5FF747F-7A0B-9003-885A-FE9A63E755E5} EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Collections.Performance.Tests", "tests\Performance\System.Collections.Performance.Tests.csproj", "{16568C86-E97E-42C6-B683-65A1B5AF2EC8}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Collections.Performance.Tests", "tests\Performance\System.Collections.Performance.Tests.csproj", "{F2B47C9D-477E-4EB2-B7F9-D7563FCED117}" ProjectSection(ProjectDependencies) = postProject {D5FF747F-7A0B-9003-885A-FE9A63E755E5} = {D5FF747F-7A0B-9003-885A-FE9A63E755E5} EndProjectSection @@ -35,10 +35,10 @@ Global {F5EB9630-AD29-4880-963F-F2D39C684D8A}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {F5EB9630-AD29-4880-963F-F2D39C684D8A}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU {F5EB9630-AD29-4880-963F-F2D39C684D8A}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU - {16568C86-E97E-42C6-B683-65A1B5AF2EC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {16568C86-E97E-42C6-B683-65A1B5AF2EC8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {16568C86-E97E-42C6-B683-65A1B5AF2EC8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {16568C86-E97E-42C6-B683-65A1B5AF2EC8}.Release|Any CPU.Build.0 = Release|Any CPU + {F2B47C9D-477E-4EB2-B7F9-D7563FCED117}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {F2B47C9D-477E-4EB2-B7F9-D7563FCED117}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {F2B47C9D-477E-4EB2-B7F9-D7563FCED117}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {F2B47C9D-477E-4EB2-B7F9-D7563FCED117}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU {D5FF747F-7A0B-9003-885A-FE9A63E755E5}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU {D5FF747F-7A0B-9003-885A-FE9A63E755E5}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU {D5FF747F-7A0B-9003-885A-FE9A63E755E5}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU @@ -53,7 +53,7 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {F5EB9630-AD29-4880-963F-F2D39C684D8A} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} - {16568C86-E97E-42C6-B683-65A1B5AF2EC8} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} + {F2B47C9D-477E-4EB2-B7F9-D7563FCED117} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} {D5FF747F-7A0B-9003-885A-FE9A63E755E5} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD} {C746D448-E7C3-4850-9CA7-D3F1FA49742F} = {2E666815-2EDB-464B-9DF6-380BF4789AD4} EndGlobalSection diff --git a/external/corefx/src/System.Collections/ref/System.Collections.cs b/external/corefx/src/System.Collections/ref/System.Collections.cs index b86af9e8e8..e8d4934236 100644 --- a/external/corefx/src/System.Collections/ref/System.Collections.cs +++ b/external/corefx/src/System.Collections/ref/System.Collections.cs @@ -108,9 +108,9 @@ namespace System.Collections.Generic void System.Collections.IDictionary.Remove(object key) { } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } public bool TryGetValue(TKey key, out TValue value) { throw null; } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Enumerator : System.Collections.Generic.IEnumerator>, System.Collections.IDictionaryEnumerator, System.Collections.IEnumerator, System.IDisposable { + private object _dummy; public System.Collections.Generic.KeyValuePair Current { get { throw null; } } System.Collections.DictionaryEntry System.Collections.IDictionaryEnumerator.Entry { get { throw null; } } object System.Collections.IDictionaryEnumerator.Key { get { throw null; } } @@ -136,9 +136,9 @@ namespace System.Collections.Generic System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() { throw null; } void System.Collections.ICollection.CopyTo(System.Array array, int index) { } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Enumerator : System.Collections.Generic.IEnumerator, System.Collections.IEnumerator, System.IDisposable { + private object _dummy; public TKey Current { get { throw null; } } object System.Collections.IEnumerator.Current { get { throw null; } } public void Dispose() { } @@ -162,9 +162,9 @@ namespace System.Collections.Generic System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() { throw null; } void System.Collections.ICollection.CopyTo(System.Array array, int index) { } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Enumerator : System.Collections.Generic.IEnumerator, System.Collections.IEnumerator, System.IDisposable { + private object _dummy; public TValue Current { get { throw null; } } object System.Collections.IEnumerator.Current { get { throw null; } } public void Dispose() { } @@ -221,9 +221,9 @@ namespace System.Collections.Generic public void TrimExcess() { } public bool TryGetValue(T equalValue, out T actualValue) { throw null; } public void UnionWith(System.Collections.Generic.IEnumerable other) { } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Enumerator : System.Collections.Generic.IEnumerator, System.Collections.IEnumerator, System.IDisposable { + private object _dummy; public T Current { get { throw null; } } object System.Collections.IEnumerator.Current { get { throw null; } } public void Dispose() { } @@ -266,9 +266,9 @@ namespace System.Collections.Generic System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() { throw null; } void System.Collections.ICollection.CopyTo(System.Array array, int index) { } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Enumerator : System.Collections.Generic.IEnumerator, System.Collections.IEnumerator, System.IDisposable, System.Runtime.Serialization.IDeserializationCallback, System.Runtime.Serialization.ISerializable { + private object _dummy; public T Current { get { throw null; } } object System.Collections.IEnumerator.Current { get { throw null; } } public void Dispose() { } @@ -354,9 +354,9 @@ namespace System.Collections.Generic public T[] ToArray() { throw null; } public void TrimExcess() { } public bool TrueForAll(System.Predicate match) { throw null; } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Enumerator : System.Collections.Generic.IEnumerator, System.Collections.IEnumerator, System.IDisposable { + private object _dummy; public T Current { get { throw null; } } object System.Collections.IEnumerator.Current { get { throw null; } } public void Dispose() { } @@ -386,9 +386,9 @@ namespace System.Collections.Generic public void TrimExcess() { } public bool TryDequeue(out T result) { throw null; } public bool TryPeek(out T result) { throw null; } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Enumerator : System.Collections.Generic.IEnumerator, System.Collections.IEnumerator, System.IDisposable { + private object _dummy; public T Current { get { throw null; } } object System.Collections.IEnumerator.Current { get { throw null; } } public void Dispose() { } @@ -437,9 +437,9 @@ namespace System.Collections.Generic void System.Collections.IDictionary.Remove(object key) { } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } public bool TryGetValue(TKey key, out TValue value) { throw null; } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Enumerator : System.Collections.Generic.IEnumerator>, System.Collections.IDictionaryEnumerator, System.Collections.IEnumerator, System.IDisposable { + private object _dummy; public System.Collections.Generic.KeyValuePair Current { get { throw null; } } System.Collections.DictionaryEntry System.Collections.IDictionaryEnumerator.Entry { get { throw null; } } object System.Collections.IDictionaryEnumerator.Key { get { throw null; } } @@ -465,9 +465,9 @@ namespace System.Collections.Generic System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() { throw null; } void System.Collections.ICollection.CopyTo(System.Array array, int index) { } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Enumerator : System.Collections.Generic.IEnumerator, System.Collections.IEnumerator, System.IDisposable { + private object _dummy; public TKey Current { get { throw null; } } object System.Collections.IEnumerator.Current { get { throw null; } } public void Dispose() { } @@ -491,9 +491,9 @@ namespace System.Collections.Generic System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() { throw null; } void System.Collections.ICollection.CopyTo(System.Array array, int index) { } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Enumerator : System.Collections.Generic.IEnumerator, System.Collections.IEnumerator, System.IDisposable { + private object _dummy; public TValue Current { get { throw null; } } object System.Collections.IEnumerator.Current { get { throw null; } } public void Dispose() { } @@ -597,9 +597,9 @@ namespace System.Collections.Generic System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } public bool TryGetValue(T equalValue, out T actualValue) { throw null; } public void UnionWith(System.Collections.Generic.IEnumerable other) { } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Enumerator : System.Collections.Generic.IEnumerator, System.Collections.IEnumerator, System.IDisposable, System.Runtime.Serialization.IDeserializationCallback, System.Runtime.Serialization.ISerializable { + private object _dummy; public T Current { get { throw null; } } object System.Collections.IEnumerator.Current { get { throw null; } } public void Dispose() { } @@ -631,9 +631,9 @@ namespace System.Collections.Generic public void TrimExcess() { } public bool TryPeek(out T result) { throw null; } public bool TryPop(out T result) { throw null; } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Enumerator : System.Collections.Generic.IEnumerator, System.Collections.IEnumerator, System.IDisposable { + private object _dummy; public T Current { get { throw null; } } object System.Collections.IEnumerator.Current { get { throw null; } } public void Dispose() { } diff --git a/external/corefx/src/System.Collections/src/Resources/Strings.resx b/external/corefx/src/System.Collections/src/Resources/Strings.resx index fc0992f359..e25845e8fe 100644 --- a/external/corefx/src/System.Collections/src/Resources/Strings.resx +++ b/external/corefx/src/System.Collections/src/Resources/Strings.resx @@ -160,4 +160,7 @@ The values for this dictionary are missing. - + + The given key '{0}' was not present in the dictionary. + + \ No newline at end of file diff --git a/external/corefx/src/System.Collections/src/System/Collections/BitArray.cs b/external/corefx/src/System.Collections/src/System/Collections/BitArray.cs index b050d00c40..c33fff93bb 100644 --- a/external/corefx/src/System.Collections/src/System/Collections/BitArray.cs +++ b/external/corefx/src/System.Collections/src/System/Collections/BitArray.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; namespace System.Collections { @@ -46,7 +45,6 @@ namespace System.Collections { throw new ArgumentOutOfRangeException(nameof(length), length, SR.ArgumentOutOfRange_NeedNonNegNum); } - Contract.EndContractBlock(); m_array = new int[GetArrayLength(length, BitsPerInt32)]; m_length = length; @@ -74,7 +72,7 @@ namespace System.Collections { throw new ArgumentNullException(nameof(bytes)); } - Contract.EndContractBlock(); + // this value is chosen to prevent overflow when computing m_length. // m_length is of type int32 and is exposed as a property, so // type of m_length can't be changed to accommodate. @@ -124,7 +122,6 @@ namespace System.Collections { throw new ArgumentNullException(nameof(values)); } - Contract.EndContractBlock(); m_array = new int[GetArrayLength(values.Length, BitsPerInt32)]; m_length = values.Length; @@ -152,7 +149,7 @@ namespace System.Collections { throw new ArgumentNullException(nameof(values)); } - Contract.EndContractBlock(); + // this value is chosen to prevent overflow when computing m_length if (values.Length > int.MaxValue / BitsPerInt32) { @@ -177,7 +174,6 @@ namespace System.Collections { throw new ArgumentNullException(nameof(bits)); } - Contract.EndContractBlock(); int arrayLength = GetArrayLength(bits.m_length, BitsPerInt32); @@ -212,7 +208,6 @@ namespace System.Collections { throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); } - Contract.EndContractBlock(); return (m_array[index / 32] & (1 << (index % 32))) != 0; } @@ -229,7 +224,6 @@ namespace System.Collections { throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); } - Contract.EndContractBlock(); if (value) { @@ -270,7 +264,6 @@ namespace System.Collections throw new ArgumentNullException(nameof(value)); if (Length != value.Length) throw new ArgumentException(SR.Arg_ArrayLengthsDiffer); - Contract.EndContractBlock(); int ints = GetArrayLength(m_length, BitsPerInt32); for (int i = 0; i < ints; i++) @@ -294,7 +287,6 @@ namespace System.Collections throw new ArgumentNullException(nameof(value)); if (Length != value.Length) throw new ArgumentException(SR.Arg_ArrayLengthsDiffer); - Contract.EndContractBlock(); int ints = GetArrayLength(m_length, BitsPerInt32); for (int i = 0; i < ints; i++) @@ -318,7 +310,6 @@ namespace System.Collections throw new ArgumentNullException(nameof(value)); if (Length != value.Length) throw new ArgumentException(SR.Arg_ArrayLengthsDiffer); - Contract.EndContractBlock(); int ints = GetArrayLength(m_length, BitsPerInt32); for (int i = 0; i < ints; i++) @@ -471,7 +462,6 @@ namespace System.Collections { get { - Contract.Ensures(Contract.Result() >= 0); return m_length; } set @@ -480,7 +470,6 @@ namespace System.Collections { throw new ArgumentOutOfRangeException(nameof(value), value, SR.ArgumentOutOfRange_NeedNonNegNum); } - Contract.EndContractBlock(); int newints = GetArrayLength(value, BitsPerInt32); if (newints > m_array.Length || newints + _ShrinkThreshold < m_array.Length) @@ -519,8 +508,6 @@ namespace System.Collections if (array.Rank != 1) throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); - Contract.EndContractBlock(); - int[] intArray = array as int[]; if (intArray != null) { @@ -585,8 +572,6 @@ namespace System.Collections { get { - Contract.Ensures(Contract.Result() >= 0); - return m_length; } } diff --git a/external/corefx/src/System.Collections/src/System/Collections/Generic/HashSet.cs b/external/corefx/src/System.Collections/src/System/Collections/Generic/HashSet.cs index 00cbafcc11..2e8c4a44f4 100644 --- a/external/corefx/src/System.Collections/src/System/Collections/Generic/HashSet.cs +++ b/external/corefx/src/System.Collections/src/System/Collections/Generic/HashSet.cs @@ -4,7 +4,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Runtime.Serialization; @@ -124,7 +123,6 @@ namespace System.Collections.Generic { throw new ArgumentNullException(nameof(collection)); } - Contract.EndContractBlock(); var otherAsHashSet = collection as HashSet; if (otherAsHashSet != null && AreEqualityComparersEqual(this, otherAsHashSet)) @@ -210,7 +208,6 @@ namespace System.Collections.Generic { throw new ArgumentOutOfRangeException(nameof(capacity)); } - Contract.EndContractBlock(); if (capacity > 0) { @@ -497,7 +494,6 @@ namespace System.Collections.Generic { throw new ArgumentNullException(nameof(other)); } - Contract.EndContractBlock(); foreach (T item in other) { @@ -525,7 +521,6 @@ namespace System.Collections.Generic { throw new ArgumentNullException(nameof(other)); } - Contract.EndContractBlock(); // intersection of anything with empty set is empty set, so return if count is 0 if (_count == 0) @@ -573,7 +568,6 @@ namespace System.Collections.Generic { throw new ArgumentNullException(nameof(other)); } - Contract.EndContractBlock(); // this is already the empty set; return if (_count == 0) @@ -605,7 +599,6 @@ namespace System.Collections.Generic { throw new ArgumentNullException(nameof(other)); } - Contract.EndContractBlock(); // if set is empty, then symmetric difference is other if (_count == 0) @@ -657,7 +650,6 @@ namespace System.Collections.Generic { throw new ArgumentNullException(nameof(other)); } - Contract.EndContractBlock(); // The empty set is a subset of any set if (_count == 0) @@ -714,7 +706,6 @@ namespace System.Collections.Generic { throw new ArgumentNullException(nameof(other)); } - Contract.EndContractBlock(); // no set is a proper subset of itself. if (other == this) @@ -773,7 +764,6 @@ namespace System.Collections.Generic { throw new ArgumentNullException(nameof(other)); } - Contract.EndContractBlock(); // a set is always a superset of itself if (other == this) @@ -831,7 +821,6 @@ namespace System.Collections.Generic { throw new ArgumentNullException(nameof(other)); } - Contract.EndContractBlock(); // the empty set isn't a proper superset of any set. if (_count == 0) @@ -882,7 +871,6 @@ namespace System.Collections.Generic { throw new ArgumentNullException(nameof(other)); } - Contract.EndContractBlock(); if (_count == 0) { @@ -917,7 +905,6 @@ namespace System.Collections.Generic { throw new ArgumentNullException(nameof(other)); } - Contract.EndContractBlock(); // a set is equal to itself if (other == this) @@ -967,7 +954,6 @@ namespace System.Collections.Generic { throw new ArgumentNullException(nameof(array)); } - Contract.EndContractBlock(); // check array index valid index into array if (arrayIndex < 0) @@ -1011,7 +997,6 @@ namespace System.Collections.Generic { throw new ArgumentNullException(nameof(match)); } - Contract.EndContractBlock(); int numRemoved = 0; for (int i = 0; i < _lastIndex; i++) diff --git a/external/corefx/src/System.Collections/src/System/Collections/Generic/SortedDictionary.cs b/external/corefx/src/System.Collections/src/System/Collections/Generic/SortedDictionary.cs index 63cbd0f243..451e667c6f 100644 --- a/external/corefx/src/System.Collections/src/System/Collections/Generic/SortedDictionary.cs +++ b/external/corefx/src/System.Collections/src/System/Collections/Generic/SortedDictionary.cs @@ -110,7 +110,7 @@ namespace System.Collections.Generic TreeSet>.Node node = _set.FindNode(new KeyValuePair(key, default(TValue))); if (node == null) { - throw new KeyNotFoundException(); + throw new KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key.ToString())); } return node.Item.Value; diff --git a/external/corefx/src/System.Collections/src/System/Collections/Generic/SortedList.cs b/external/corefx/src/System.Collections/src/System/Collections/Generic/SortedList.cs index 64d3065321..3531b2d4bc 100644 --- a/external/corefx/src/System.Collections/src/System/Collections/Generic/SortedList.cs +++ b/external/corefx/src/System.Collections/src/System/Collections/Generic/SortedList.cs @@ -599,7 +599,7 @@ namespace System.Collections.Generic if (i >= 0) return values[i]; - throw new KeyNotFoundException(); + throw new KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key.ToString())); } set { diff --git a/external/corefx/src/System.Collections/tests/Performance/System.Collections.Performance.Tests.csproj b/external/corefx/src/System.Collections/tests/Performance/System.Collections.Performance.Tests.csproj index 73413f6c85..f62a82db89 100644 --- a/external/corefx/src/System.Collections/tests/Performance/System.Collections.Performance.Tests.csproj +++ b/external/corefx/src/System.Collections/tests/Performance/System.Collections.Performance.Tests.csproj @@ -3,12 +3,11 @@ true - {16568C86-E97E-42C6-B683-65A1B5AF2EC8} + {F2B47C9D-477E-4EB2-B7F9-D7563FCED117} true - - - + + Common\System\Collections\DictionaryExtensions.cs diff --git a/external/corefx/src/System.Collections/tests/System.Collections.Tests.csproj b/external/corefx/src/System.Collections/tests/System.Collections.Tests.csproj index ebd8168cba..6ad8dfb2db 100644 --- a/external/corefx/src/System.Collections/tests/System.Collections.Tests.csproj +++ b/external/corefx/src/System.Collections/tests/System.Collections.Tests.csproj @@ -4,7 +4,6 @@ {F5EB9630-AD29-4880-963F-F2D39C684D8A} - @@ -164,4 +163,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.ComponentModel.Annotations/ref/System.ComponentModel.Annotations.cs b/external/corefx/src/System.ComponentModel.Annotations/ref/System.ComponentModel.Annotations.cs index 66a0e1801d..a6ef6b4734 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/ref/System.ComponentModel.Annotations.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/ref/System.ComponentModel.Annotations.cs @@ -81,7 +81,7 @@ namespace System.ComponentModel.DataAnnotations public virtual string GetDataTypeName() { throw null; } public override bool IsValid(object value) { throw null; } } - [System.AttributeUsageAttribute((System.AttributeTargets)(2496), AllowMultiple = false)] + [System.AttributeUsageAttribute(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)] public sealed partial class DisplayAttribute : System.Attribute { public DisplayAttribute() { } diff --git a/external/corefx/src/System.ComponentModel.Annotations/src/Resources/System.ComponentModel.Annotations.rd.xml b/external/corefx/src/System.ComponentModel.Annotations/src/Resources/System.ComponentModel.Annotations.rd.xml new file mode 100644 index 0000000000..7b939eaa15 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Annotations/src/Resources/System.ComponentModel.Annotations.rd.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/external/corefx/src/System.ComponentModel.Annotations/src/System.ComponentModel.Annotations.csproj b/external/corefx/src/System.ComponentModel.Annotations/src/System.ComponentModel.Annotations.csproj index 1eb8f23a6a..960e5e4c74 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/src/System.ComponentModel.Annotations.csproj +++ b/external/corefx/src/System.ComponentModel.Annotations/src/System.ComponentModel.Annotations.csproj @@ -64,5 +64,8 @@ + + + \ No newline at end of file diff --git a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/CreditCardAttribute.cs b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/CreditCardAttribute.cs index a9b166f9d2..ef54515c0c 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/CreditCardAttribute.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/CreditCardAttribute.cs @@ -23,8 +23,7 @@ namespace System.ComponentModel.DataAnnotations return true; } - var ccValue = value as string; - if (ccValue == null) + if (!(value is string ccValue)) { return false; } diff --git a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/DisplayAttribute.cs b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/DisplayAttribute.cs index f105a857b2..ed67be0acb 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/DisplayAttribute.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/DisplayAttribute.cs @@ -12,7 +12,7 @@ namespace System.ComponentModel.DataAnnotations /// /// [AttributeUsage( - AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Method, + AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)] public sealed class DisplayAttribute : Attribute { diff --git a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/EmailAddressAttribute.cs b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/EmailAddressAttribute.cs index 644ac842e2..1e1b0a9319 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/EmailAddressAttribute.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/EmailAddressAttribute.cs @@ -23,8 +23,7 @@ namespace System.ComponentModel.DataAnnotations return true; } - var valueAsString = value as string; - if (valueAsString == null) + if (!(value is string valueAsString)) { return false; } diff --git a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/EnumDataTypeAttribute.cs b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/EnumDataTypeAttribute.cs index 425b47e8bc..1ded655f6c 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/EnumDataTypeAttribute.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/EnumDataTypeAttribute.cs @@ -38,7 +38,7 @@ namespace System.ComponentModel.DataAnnotations return true; } var stringValue = value as string; - if (stringValue != null && string.IsNullOrEmpty(stringValue)) + if (stringValue?.Length == 0) { return true; } @@ -76,14 +76,9 @@ namespace System.ComponentModel.DataAnnotations { try { - if (stringValue != null) - { - convertedValue = Enum.Parse(EnumType, stringValue, false); - } - else - { - convertedValue = Enum.ToObject(EnumType, value); - } + convertedValue = stringValue != null + ? Enum.Parse(EnumType, stringValue, false) + : Enum.ToObject(EnumType, value); } catch (ArgumentException) { diff --git a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/FileExtensionsAttribute.cs b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/FileExtensionsAttribute.cs index 1fedd9df43..da2652e098 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/FileExtensionsAttribute.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/FileExtensionsAttribute.cs @@ -43,21 +43,8 @@ namespace System.ComponentModel.DataAnnotations public override string FormatErrorMessage(string name) => string.Format(CultureInfo.CurrentCulture, ErrorMessageString, name, ExtensionsFormatted); - public override bool IsValid(object value) - { - if (value == null) - { - return true; - } - - var valueAsString = value as string; - if (valueAsString != null) - { - return ValidateExtension(valueAsString); - } - - return false; - } + public override bool IsValid(object value) => + value == null || value is string valueAsString && ValidateExtension(valueAsString); private bool ValidateExtension(string fileName) { diff --git a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/FilterUIHintAttribute.cs b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/FilterUIHintAttribute.cs index e080c2a4e4..89f3d68c03 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/FilterUIHintAttribute.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/FilterUIHintAttribute.cs @@ -83,15 +83,7 @@ namespace System.ComponentModel.DataAnnotations /// An System.Object. /// true if obj is a FilterUIHintAttribute and its value is the same /// as this instance; otherwise, false. - public override bool Equals(object obj) - { - var otherAttribute = obj as FilterUIHintAttribute; - if (otherAttribute == null) - { - return false; - } - - return _implementation.Equals(otherAttribute._implementation); - } + public override bool Equals(object obj) => + obj is FilterUIHintAttribute otherAttribute && _implementation.Equals(otherAttribute._implementation); } } diff --git a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MaxLengthAttribute.cs b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MaxLengthAttribute.cs index 6f213d18b6..3fb9f21f28 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MaxLengthAttribute.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MaxLengthAttribute.cs @@ -65,7 +65,7 @@ namespace System.ComponentModel.DataAnnotations // Check the lengths for legality EnsureLegalLengths(); - var length = 0; + int length; // Automatically pass if value is null. RequiredAttribute should be used to assert a value is not null. if (value == null) { diff --git a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MinLengthAttribute.cs b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MinLengthAttribute.cs index e27c376aa0..67e59fe69d 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MinLengthAttribute.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MinLengthAttribute.cs @@ -49,7 +49,7 @@ namespace System.ComponentModel.DataAnnotations // Check the lengths for legality EnsureLegalLengths(); - var length = 0; + int length; // Automatically pass if value is null. RequiredAttribute should be used to assert a value is not null. if (value == null) { diff --git a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/PhoneAttribute.cs b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/PhoneAttribute.cs index 240c3ef6b9..aa508087d2 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/PhoneAttribute.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/PhoneAttribute.cs @@ -8,10 +8,10 @@ namespace System.ComponentModel.DataAnnotations AllowMultiple = false)] public sealed class PhoneAttribute : DataTypeAttribute { - private const string _additionalPhoneNumberCharacters = "-.()"; - private const string _extensionAbbreviationExtDot = "ext."; - private const string _extensionAbbreviationExt = "ext"; - private const string _extensionAbbreviationX = "x"; + private const string AdditionalPhoneNumberCharacters = "-.()"; + private const string ExtensionAbbreviationExtDot = "ext."; + private const string ExtensionAbbreviationExt = "ext"; + private const string ExtensionAbbreviationX = "x"; public PhoneAttribute() : base(DataType.PhoneNumber) @@ -28,8 +28,7 @@ namespace System.ComponentModel.DataAnnotations return true; } - var valueAsString = value as string; - if (valueAsString == null) + if (!(value is string valueAsString)) { return false; } @@ -56,7 +55,7 @@ namespace System.ComponentModel.DataAnnotations { if (!(Char.IsDigit(c) || Char.IsWhiteSpace(c) - || _additionalPhoneNumberCharacters.IndexOf(c) != -1)) + || AdditionalPhoneNumberCharacters.IndexOf(c) != -1)) { return false; } @@ -68,11 +67,11 @@ namespace System.ComponentModel.DataAnnotations private static string RemoveExtension(string potentialPhoneNumber) { var lastIndexOfExtension = potentialPhoneNumber - .LastIndexOf(_extensionAbbreviationExtDot, StringComparison.OrdinalIgnoreCase); + .LastIndexOf(ExtensionAbbreviationExtDot, StringComparison.OrdinalIgnoreCase); if (lastIndexOfExtension >= 0) { var extension = potentialPhoneNumber.Substring( - lastIndexOfExtension + _extensionAbbreviationExtDot.Length); + lastIndexOfExtension + ExtensionAbbreviationExtDot.Length); if (MatchesExtension(extension)) { return potentialPhoneNumber.Substring(0, lastIndexOfExtension); @@ -80,11 +79,11 @@ namespace System.ComponentModel.DataAnnotations } lastIndexOfExtension = potentialPhoneNumber - .LastIndexOf(_extensionAbbreviationExt, StringComparison.OrdinalIgnoreCase); + .LastIndexOf(ExtensionAbbreviationExt, StringComparison.OrdinalIgnoreCase); if (lastIndexOfExtension >= 0) { var extension = potentialPhoneNumber.Substring( - lastIndexOfExtension + _extensionAbbreviationExt.Length); + lastIndexOfExtension + ExtensionAbbreviationExt.Length); if (MatchesExtension(extension)) { return potentialPhoneNumber.Substring(0, lastIndexOfExtension); @@ -92,11 +91,11 @@ namespace System.ComponentModel.DataAnnotations } lastIndexOfExtension = potentialPhoneNumber - .LastIndexOf(_extensionAbbreviationX, StringComparison.OrdinalIgnoreCase); + .LastIndexOf(ExtensionAbbreviationX, StringComparison.OrdinalIgnoreCase); if (lastIndexOfExtension >= 0) { var extension = potentialPhoneNumber.Substring( - lastIndexOfExtension + _extensionAbbreviationX.Length); + lastIndexOfExtension + ExtensionAbbreviationX.Length); if (MatchesExtension(extension)) { return potentialPhoneNumber.Substring(0, lastIndexOfExtension); diff --git a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RangeAttribute.cs b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RangeAttribute.cs index d24b6a262a..4e210c4e37 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RangeAttribute.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RangeAttribute.cs @@ -98,17 +98,12 @@ namespace System.ComponentModel.DataAnnotations SetupConversion(); // Automatically pass if value is null or empty. RequiredAttribute should be used to assert a value is not empty. - if (value == null) - { - return true; - } - var s = value as string; - if (s != null && string.IsNullOrEmpty(s)) + if (value == null || (value as string)?.Length == 0) { return true; } - object convertedValue = null; + object convertedValue; try { diff --git a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RegularExpressionAttribute.cs b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RegularExpressionAttribute.cs index 15883c3960..d6f6ae71b7 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RegularExpressionAttribute.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RegularExpressionAttribute.cs @@ -103,7 +103,7 @@ namespace System.ComponentModel.DataAnnotations Regex = MatchTimeoutInMilliseconds == -1 ? new Regex(Pattern) - : new Regex(Pattern, default(RegexOptions), TimeSpan.FromMilliseconds((double)MatchTimeoutInMilliseconds)); + : new Regex(Pattern, default(RegexOptions), TimeSpan.FromMilliseconds(MatchTimeoutInMilliseconds)); } } } diff --git a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RequiredAttribute.cs b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RequiredAttribute.cs index 98b3c7853e..e9760e4802 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RequiredAttribute.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RequiredAttribute.cs @@ -45,13 +45,7 @@ namespace System.ComponentModel.DataAnnotations } // only check string length if empty strings are not allowed - var stringValue = value as string; - if (stringValue != null && !AllowEmptyStrings) - { - return stringValue.Trim().Length != 0; - } - - return true; + return AllowEmptyStrings || !(value is string stringValue) || stringValue.Trim().Length != 0; } } } diff --git a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/StringLengthAttribute.cs b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/StringLengthAttribute.cs index 2360617175..2a8cde8f04 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/StringLengthAttribute.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/StringLengthAttribute.cs @@ -50,8 +50,13 @@ namespace System.ComponentModel.DataAnnotations // Automatically pass if value is null. RequiredAttribute should be used to assert a value is not null. // We expect a cast exception if a non-string was passed in. - var length = value == null ? 0 : ((string)value).Length; - return value == null || (length >= MinimumLength && length <= MaximumLength); + if (value == null) + { + return true; + } + + int length = ((string)value).Length; + return length >= MinimumLength && length <= MaximumLength; } /// diff --git a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/UIHintAttribute.cs b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/UIHintAttribute.cs index befa796bc4..c518ba4667 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/UIHintAttribute.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/UIHintAttribute.cs @@ -66,15 +66,8 @@ namespace System.ComponentModel.DataAnnotations public override int GetHashCode() => _implementation.GetHashCode(); - public override bool Equals(object obj) - { - var otherAttribute = obj as UIHintAttribute; - if (otherAttribute == null) - { - return false; - } - return _implementation.Equals(otherAttribute._implementation); - } + public override bool Equals(object obj) => + obj is UIHintAttribute otherAttribute && _implementation.Equals(otherAttribute._implementation); internal class UIHintImplementation { @@ -102,20 +95,11 @@ namespace System.ComponentModel.DataAnnotations /// public string PresentationLayer { get; } - public IDictionary ControlParameters - { - get - { - if (_controlParameters == null) - { - // Lazy load the dictionary. It's fine if this method executes multiple times in stress scenarios. - // If the method throws (indicating that the input params are invalid) this property will throw - // every time it's accessed. - _controlParameters = BuildControlParametersDictionary(); - } - return _controlParameters; - } - } + // Lazy load the dictionary. It's fine if this method executes multiple times in stress scenarios. + // If the method throws (indicating that the input params are invalid) this property will throw + // every time it's accessed. + public IDictionary ControlParameters => + _controlParameters ?? (_controlParameters = BuildControlParametersDictionary()); /// /// Returns the hash code for this UIHintAttribute. @@ -203,8 +187,7 @@ namespace System.ComponentModel.DataAnnotations i)); } - var keyString = key as string; - if (keyString == null) + if (!(key is string keyString)) { throw new InvalidOperationException( string.Format( diff --git a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/UrlAttribute.cs b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/UrlAttribute.cs index dd56c99d16..fa2d3677a8 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/UrlAttribute.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/UrlAttribute.cs @@ -23,8 +23,7 @@ namespace System.ComponentModel.DataAnnotations return true; } - var valueAsString = value as string; - return valueAsString != null && + return value is string valueAsString && (valueAsString.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || valueAsString.StartsWith("https://", StringComparison.OrdinalIgnoreCase) || valueAsString.StartsWith("ftp://", StringComparison.OrdinalIgnoreCase)); diff --git a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationAttribute.cs b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationAttribute.cs index 75eef3cdad..05baa7bda1 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationAttribute.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationAttribute.cs @@ -231,11 +231,8 @@ namespace System.ComponentModel.DataAnnotations { // Here if not using resource type/name -- the accessor is just the error message string, // which we know is not empty to have gotten this far. - _errorMessageResourceAccessor = delegate - { - // We captured error message to local in case it changes before accessor runs - return localErrorMessage; - }; + // We captured error message to local in case it changes before accessor runs + _errorMessageResourceAccessor = () => localErrorMessage; } } } @@ -283,7 +280,7 @@ namespace System.ComponentModel.DataAnnotations _errorMessageResourceType.FullName)); } - _errorMessageResourceAccessor = delegate { return (string)property.GetValue(null, null); }; + _errorMessageResourceAccessor = () => (string)property.GetValue(null, null); } #endregion diff --git a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationAttributeStore.cs b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationAttributeStore.cs index 5f41a1ccdf..c0ae1542a7 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationAttributeStore.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationAttributeStore.cs @@ -116,14 +116,14 @@ namespace System.ComponentModel.DataAnnotations lock (_typeStoreItems) { - TypeStoreItem item = null; - if (!_typeStoreItems.TryGetValue(type, out item)) + if (!_typeStoreItems.TryGetValue(type, out TypeStoreItem item)) { // use CustomAttributeExtensions.GetCustomAttributes() to get inherited attributes as well as direct ones var attributes = CustomAttributeExtensions.GetCustomAttributes(type, true); item = new TypeStoreItem(type, attributes); _typeStoreItems[type] = item; } + return item; } } @@ -179,14 +179,13 @@ namespace System.ComponentModel.DataAnnotations internal PropertyStoreItem GetPropertyStoreItem(string propertyName) { - PropertyStoreItem item = null; - if (!TryGetPropertyStoreItem(propertyName, out item)) + if (!TryGetPropertyStoreItem(propertyName, out PropertyStoreItem item)) { throw new ArgumentException( string.Format(CultureInfo.CurrentCulture, - SR.AttributeStore_Unknown_Property, _type.Name, propertyName), -nameof(propertyName)); + SR.AttributeStore_Unknown_Property, _type.Name, propertyName), nameof(propertyName)); } + return item; } diff --git a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationContext.cs b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationContext.cs index fd5a0b5930..478fb3a42f 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationContext.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationContext.cs @@ -91,15 +91,7 @@ namespace System.ComponentModel.DataAnnotations InitializeServiceProvider(serviceType => serviceProvider.GetService(serviceType)); } - if (items != null) - { - _items = new Dictionary(items); - } - else - { - _items = new Dictionary(); - } - + _items = items != null ? new Dictionary(items) : new Dictionary(); ObjectInstance = instance; } diff --git a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationException.cs b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationException.cs index 0ae7ccfe7b..cae7f06b83 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationException.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationException.cs @@ -10,6 +10,9 @@ namespace System.ComponentModel.DataAnnotations /// Exception used for validation using . /// [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.ComponentModel.DataAnnotations, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")] +#endif public class ValidationException : Exception { private ValidationResult _validationResult; @@ -45,7 +48,6 @@ namespace System.ComponentModel.DataAnnotations /// /// The long form of this constructor is preferred because it gives better error reporting. public ValidationException() - : base() { } @@ -78,7 +80,6 @@ namespace System.ComponentModel.DataAnnotations protected ValidationException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } /// @@ -92,17 +93,8 @@ namespace System.ComponentModel.DataAnnotations /// /// This property will never be null. /// - public ValidationResult ValidationResult - { - get - { - if (_validationResult == null) - { - _validationResult = new ValidationResult(Message); - } - return _validationResult; - } - } + public ValidationResult ValidationResult => + _validationResult ?? (_validationResult = new ValidationResult(Message)); /// /// Gets the value that caused the validating attribute to trigger the exception diff --git a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/Validator.cs b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/Validator.cs index 1fdd87d466..faf3fe6fe3 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/Validator.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/Validator.cs @@ -433,9 +433,12 @@ nameof(value)); { var results = validatable.Validate(validationContext); - foreach (var result in results.Where(r => r != ValidationResult.Success)) + if (results != null) { - errors.Add(new ValidationError(null, instance, result)); + foreach (var result in results.Where(r => r != ValidationResult.Success)) + { + errors.Add(new ValidationError(null, instance, result)); + } } } @@ -472,7 +475,7 @@ nameof(value)); else { // only validate the Required attributes - var reqAttr = attributes.FirstOrDefault(a => a is RequiredAttribute) as RequiredAttribute; + var reqAttr = attributes.OfType().FirstOrDefault(); if (reqAttr != null) { // Note: we let the [Required] attribute do its own null testing, @@ -553,7 +556,7 @@ nameof(value)); ValidationError validationError; // Get the required validator if there is one and test it first, aborting on failure - var required = attributes.FirstOrDefault(a => a is RequiredAttribute) as RequiredAttribute; + var required = attributes.OfType().FirstOrDefault(); if (required != null) { if (!TryValidate(value, validationContext, required, out validationError)) @@ -618,21 +621,20 @@ nameof(value)); /// private class ValidationError { + private readonly object _value; + private readonly ValidationAttribute _validationAttribute; + internal ValidationError(ValidationAttribute validationAttribute, object value, ValidationResult validationResult) { - ValidationAttribute = validationAttribute; + _validationAttribute = validationAttribute; ValidationResult = validationResult; - Value = value; + _value = value; } - internal object Value { get; } - - internal ValidationAttribute ValidationAttribute { get; } - internal ValidationResult ValidationResult { get; } - internal Exception ThrowValidationException() => throw new ValidationException(ValidationResult, ValidationAttribute, Value); + internal void ThrowValidationException() => throw new ValidationException(ValidationResult, _validationAttribute, _value); } } } diff --git a/external/corefx/src/System.ComponentModel.Annotations/tests/RangeAttributeTests.cs b/external/corefx/src/System.ComponentModel.Annotations/tests/RangeAttributeTests.cs index 243d8aeb17..0a524a49c0 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/tests/RangeAttributeTests.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/tests/RangeAttributeTests.cs @@ -36,7 +36,7 @@ namespace System.ComponentModel.DataAnnotations.Tests yield return new TestCase(stringIntRange, "2"); yield return new TestCase(stringIntRange, 3); yield return new TestCase(stringIntRange, "3"); - + RangeAttribute stringDoubleRange = new RangeAttribute(typeof(double), (1.0).ToString("F1"), (3.0).ToString("F1")); yield return new TestCase(stringDoubleRange, null); yield return new TestCase(stringDoubleRange, string.Empty); @@ -91,8 +91,8 @@ namespace System.ComponentModel.DataAnnotations.Tests public static void Validate_CantConvertValueToTargetType_ThrowsException(Type type, string minimum, string maximum) { var attribute = new RangeAttribute(type, minimum, maximum); - Assert.Throws(() => attribute.Validate("abc", new ValidationContext(new object()))); - Assert.Throws(() => attribute.IsValid("abc")); + AssertExtensions.Throws(() => attribute.Validate("abc", new ValidationContext(new object()))); + AssertExtensions.Throws(() => attribute.IsValid("abc")); } [Fact] @@ -168,7 +168,7 @@ namespace System.ComponentModel.DataAnnotations.Tests public static void Validate_MinimumOrMaximumCantBeConvertedToIntegralType_ThrowsException(Type type, string minimum, string maximum) { RangeAttribute attribute = new RangeAttribute(type, minimum, maximum); - Assert.Throws(() => attribute.Validate("Any", new ValidationContext(new object()))); + AssertExtensions.Throws(() => attribute.Validate("Any", new ValidationContext(new object()))); } [Theory] diff --git a/external/corefx/src/System.ComponentModel.Annotations/tests/System.ComponentModel.Annotations.Tests.csproj b/external/corefx/src/System.ComponentModel.Annotations/tests/System.ComponentModel.Annotations.Tests.csproj index b938a63a15..3de451d744 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/tests/System.ComponentModel.Annotations.Tests.csproj +++ b/external/corefx/src/System.ComponentModel.Annotations/tests/System.ComponentModel.Annotations.Tests.csproj @@ -8,11 +8,7 @@ - - - Common\System\Runtime\Serialization\Formatters\BinaryFormatterHelpers.cs - @@ -50,4 +46,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.ComponentModel.Annotations/tests/ValidationExceptionTests.cs b/external/corefx/src/System.ComponentModel.Annotations/tests/ValidationExceptionTests.cs index 5d7a82cac5..7fa6a2b29e 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/tests/ValidationExceptionTests.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/tests/ValidationExceptionTests.cs @@ -2,7 +2,6 @@ // 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.Runtime.Serialization.Formatters.Tests; using Xunit; namespace System.ComponentModel.DataAnnotations.Tests @@ -79,13 +78,5 @@ namespace System.ComponentModel.DataAnnotations.Tests Assert.Same(validatingAttribute, ex.ValidationAttribute); Assert.Same(value, ex.Value); } - - - [Fact] - public void AssertExceptionDeserializationFails() - { - BinaryFormatterHelpers.AssertExceptionDeserializationFails(); - } - } } diff --git a/external/corefx/src/System.ComponentModel.Annotations/tests/ValidatorTests.cs b/external/corefx/src/System.ComponentModel.Annotations/tests/ValidatorTests.cs index 1d92d1db46..1997edbcda 100644 --- a/external/corefx/src/System.ComponentModel.Annotations/tests/ValidatorTests.cs +++ b/external/corefx/src/System.ComponentModel.Annotations/tests/ValidatorTests.cs @@ -190,6 +190,26 @@ namespace System.ComponentModel.DataAnnotations.Tests } } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Null check not present in .NET Framework. See https://github.com/dotnet/corefx/issues/25495")] + public void TryValidateObject_IValidatableObject_Null() + { + var instance = new ValidatableNull(); + var context = new ValidationContext(instance); + + var results = new List(); + Assert.True(Validator.TryValidateObject(instance, context, results)); + Assert.Equal(0, results.Count); + } + + public class ValidatableNull : IValidatableObject + { + public IEnumerable Validate(ValidationContext validationContext) + { + return null; + } + } + [Fact] public void TryValidateObject_RequiredNonNull_Success() { @@ -318,6 +338,34 @@ namespace System.ComponentModel.DataAnnotations.Tests Assert.Equal(objectToBeValidated, exception.Value); } + [Fact] + public void ValidateObject_IValidatableObject_Success() + { + var instance = new ValidatableSuccess(); + var context = new ValidationContext(instance); + + Validator.ValidateObject(instance, context); + } + + [Fact] + public void ValidateObject_IValidatableObject_Error() + { + var instance = new ValidatableError(); + var context = new ValidationContext(instance); + var exception = Assert.Throws( + () => Validator.ValidateObject(instance, context)); + Assert.Equal("error", exception.ValidationResult.ErrorMessage); + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Null check not present in .NET Framework. See https://github.com/dotnet/corefx/issues/25495")] + public void ValidateObject_IValidatableObject_Null() + { + var instance = new ValidatableNull(); + var context = new ValidationContext(instance); + + Validator.ValidateObject(instance, context); + } #endregion ValidateObject #region TryValidateProperty diff --git a/external/corefx/src/System.ComponentModel.Composition/System.ComponentModel.Composition.sln b/external/corefx/src/System.ComponentModel.Composition/System.ComponentModel.Composition.sln new file mode 100644 index 0000000000..8bfa5e8779 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/System.ComponentModel.Composition.sln @@ -0,0 +1,50 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.ComponentModel.Composition.Tests", "tests\System.ComponentModel.Composition.Tests.csproj", "{59F4682D-C41D-45A7-9798-16C75525BB1D}" + ProjectSection(ProjectDependencies) = postProject + {2D694AC8-A12F-4622-9405-74E20EC40C3A} = {2D694AC8-A12F-4622-9405-74E20EC40C3A} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.ComponentModel.Composition", "src\System.ComponentModel.Composition.csproj", "{2D694AC8-A12F-4622-9405-74E20EC40C3A}" + ProjectSection(ProjectDependencies) = postProject + {DD3B8052-CE03-4159-8311-1CE1382C51B0} = {DD3B8052-CE03-4159-8311-1CE1382C51B0} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.ComponentModel.Composition", "ref\System.ComponentModel.Composition.csproj", "{DD3B8052-CE03-4159-8311-1CE1382C51B0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{1A2F9F4A-A032-433E-B914-ADD5992BB178}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E107E9C1-E893-4E87-987E-04EF0DCEAEFD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{2E666815-2EDB-464B-9DF6-380BF4789AD4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {59F4682D-C41D-45A7-9798-16C75525BB1D}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {59F4682D-C41D-45A7-9798-16C75525BB1D}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {59F4682D-C41D-45A7-9798-16C75525BB1D}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {59F4682D-C41D-45A7-9798-16C75525BB1D}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU + {2D694AC8-A12F-4622-9405-74E20EC40C3A}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {2D694AC8-A12F-4622-9405-74E20EC40C3A}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {2D694AC8-A12F-4622-9405-74E20EC40C3A}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {2D694AC8-A12F-4622-9405-74E20EC40C3A}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU + {DD3B8052-CE03-4159-8311-1CE1382C51B0}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {DD3B8052-CE03-4159-8311-1CE1382C51B0}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {DD3B8052-CE03-4159-8311-1CE1382C51B0}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {DD3B8052-CE03-4159-8311-1CE1382C51B0}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {59F4682D-C41D-45A7-9798-16C75525BB1D} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} + {2D694AC8-A12F-4622-9405-74E20EC40C3A} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD} + {DD3B8052-CE03-4159-8311-1CE1382C51B0} = {2E666815-2EDB-464B-9DF6-380BF4789AD4} + EndGlobalSection +EndGlobal diff --git a/external/corefx/src/System.ComponentModel.Composition/dir.props b/external/corefx/src/System.ComponentModel.Composition/dir.props new file mode 100644 index 0000000000..55b60149d2 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/dir.props @@ -0,0 +1,8 @@ + + + + + 4.0.0.0 + MSFT + + diff --git a/external/corefx/src/System.ComponentModel.Composition/pkg/System.ComponentModel.Composition.pkgproj b/external/corefx/src/System.ComponentModel.Composition/pkg/System.ComponentModel.Composition.pkgproj new file mode 100644 index 0000000000..36f9f8d5f9 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/pkg/System.ComponentModel.Composition.pkgproj @@ -0,0 +1,18 @@ + + + + + + netcoreapp2.0;net45;$(AllXamarinFrameworks) + + + + true + + + + runtimes/win/lib/net45 + + + + diff --git a/external/corefx/src/System.ComponentModel.Composition/ref/Configurations.props b/external/corefx/src/System.ComponentModel.Composition/ref/Configurations.props new file mode 100644 index 0000000000..fc9ef9822c --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/ref/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + diff --git a/external/corefx/src/System.ComponentModel.Composition/ref/System.ComponentModel.Composition.cs b/external/corefx/src/System.ComponentModel.Composition/ref/System.ComponentModel.Composition.cs new file mode 100644 index 0000000000..aadd04f683 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/ref/System.ComponentModel.Composition.cs @@ -0,0 +1,640 @@ +// 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. +// ------------------------------------------------------------------------------ +// Changes to this file must follow the http://aka.ms/api-review process. +// ------------------------------------------------------------------------------ + +namespace System.ComponentModel.Composition +{ + public static partial class AdaptationConstants + { + public const string AdapterContractName = "System.ComponentModel.Composition.AdapterContract"; + public const string AdapterFromContractMetadataName = "FromContract"; + public const string AdapterToContractMetadataName = "ToContract"; + } + public static partial class AttributedModelServices + { + public static System.ComponentModel.Composition.Primitives.ComposablePart AddExportedValue(this System.ComponentModel.Composition.Hosting.CompositionBatch batch, string contractName, T exportedValue) { throw null; } + public static System.ComponentModel.Composition.Primitives.ComposablePart AddExportedValue(this System.ComponentModel.Composition.Hosting.CompositionBatch batch, T exportedValue) { throw null; } + public static System.ComponentModel.Composition.Primitives.ComposablePart AddPart(this System.ComponentModel.Composition.Hosting.CompositionBatch batch, object attributedPart) { throw null; } + public static void ComposeExportedValue(this System.ComponentModel.Composition.Hosting.CompositionContainer container, string contractName, T exportedValue) { } + public static void ComposeExportedValue(this System.ComponentModel.Composition.Hosting.CompositionContainer container, T exportedValue) { } + public static void ComposeParts(this System.ComponentModel.Composition.Hosting.CompositionContainer container, params object[] attributedParts) { } + public static System.ComponentModel.Composition.Primitives.ComposablePart CreatePart(System.ComponentModel.Composition.Primitives.ComposablePartDefinition partDefinition, object attributedPart) { throw null; } + public static System.ComponentModel.Composition.Primitives.ComposablePart CreatePart(object attributedPart) { throw null; } + public static System.ComponentModel.Composition.Primitives.ComposablePart CreatePart(object attributedPart, System.Reflection.ReflectionContext reflectionContext) { throw null; } + public static System.ComponentModel.Composition.Primitives.ComposablePartDefinition CreatePartDefinition(System.Type type, System.ComponentModel.Composition.Primitives.ICompositionElement origin) { throw null; } + public static System.ComponentModel.Composition.Primitives.ComposablePartDefinition CreatePartDefinition(System.Type type, System.ComponentModel.Composition.Primitives.ICompositionElement origin, bool ensureIsDiscoverable) { throw null; } + public static bool Exports(this System.ComponentModel.Composition.Primitives.ComposablePartDefinition part, System.Type contractType) { throw null; } + public static bool Exports(this System.ComponentModel.Composition.Primitives.ComposablePartDefinition part) { throw null; } + public static string GetContractName(System.Type type) { throw null; } + public static TMetadataView GetMetadataView(System.Collections.Generic.IDictionary metadata) { throw null; } + public static string GetTypeIdentity(System.Reflection.MethodInfo method) { throw null; } + public static string GetTypeIdentity(System.Type type) { throw null; } + public static bool Imports(this System.ComponentModel.Composition.Primitives.ComposablePartDefinition part, System.Type contractType) { throw null; } + public static bool Imports(this System.ComponentModel.Composition.Primitives.ComposablePartDefinition part, System.Type contractType, System.ComponentModel.Composition.Primitives.ImportCardinality importCardinality) { throw null; } + public static bool Imports(this System.ComponentModel.Composition.Primitives.ComposablePartDefinition part) { throw null; } + public static bool Imports(this System.ComponentModel.Composition.Primitives.ComposablePartDefinition part, System.ComponentModel.Composition.Primitives.ImportCardinality importCardinality) { throw null; } + public static System.ComponentModel.Composition.Primitives.ComposablePart SatisfyImportsOnce(this System.ComponentModel.Composition.ICompositionService compositionService, object attributedPart) { throw null; } + public static System.ComponentModel.Composition.Primitives.ComposablePart SatisfyImportsOnce(this System.ComponentModel.Composition.ICompositionService compositionService, object attributedPart, System.Reflection.ReflectionContext reflectionContext) { throw null; } + } + [System.AttributeUsageAttribute((System.AttributeTargets)(1), AllowMultiple=false, Inherited=true)] + public partial class CatalogReflectionContextAttribute : System.Attribute + { + public CatalogReflectionContextAttribute(System.Type reflectionContextType) { } + public System.Reflection.ReflectionContext CreateReflectionContext() { throw null; } + } + public partial class ChangeRejectedException : System.ComponentModel.Composition.CompositionException + { + public ChangeRejectedException() { } + public ChangeRejectedException(System.Collections.Generic.IEnumerable errors) { } + public ChangeRejectedException(string message) { } + public ChangeRejectedException(string message, System.Exception innerException) { } + public override string Message { get { throw null; } } + } + public partial class CompositionContractMismatchException : System.Exception + { + public CompositionContractMismatchException() { } + protected CompositionContractMismatchException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + public CompositionContractMismatchException(string message) { } + public CompositionContractMismatchException(string message, System.Exception innerException) { } + } + public partial class CompositionError + { + public CompositionError(string message) { } + public CompositionError(string message, System.ComponentModel.Composition.Primitives.ICompositionElement element) { } + public CompositionError(string message, System.ComponentModel.Composition.Primitives.ICompositionElement element, System.Exception exception) { } + public CompositionError(string message, System.Exception exception) { } + public string Description { get { throw null; } } + public System.ComponentModel.Composition.Primitives.ICompositionElement Element { get { throw null; } } + public System.Exception Exception { get { throw null; } } + public override string ToString() { throw null; } + } + public partial class CompositionException : System.Exception + { + public CompositionException() { } + public CompositionException(System.Collections.Generic.IEnumerable errors) { } + public CompositionException(string message) { } + public CompositionException(string message, System.Exception innerException) { } + public System.Collections.ObjectModel.ReadOnlyCollection Errors { get { throw null; } } + public override string Message { get { throw null; } } + public System.Collections.ObjectModel.ReadOnlyCollection RootCauses { get { throw null; } } + } + public enum CreationPolicy + { + Any = 0, + NonShared = 2, + Shared = 1, + } + [System.AttributeUsageAttribute((System.AttributeTargets)(452), AllowMultiple=true, Inherited=false)] + public partial class ExportAttribute : System.Attribute + { + public ExportAttribute() { } + public ExportAttribute(string contractName) { } + public ExportAttribute(string contractName, System.Type contractType) { } + public ExportAttribute(System.Type contractType) { } + public string ContractName { get { throw null; } } + public System.Type ContractType { get { throw null; } } + } + public partial class ExportFactory + { + public ExportFactory(System.Func> exportLifetimeContextCreator) { } + public System.ComponentModel.Composition.ExportLifetimeContext CreateExport() { throw null; } + } + public partial class ExportFactory : System.ComponentModel.Composition.ExportFactory + { + public ExportFactory(System.Func> exportLifetimeContextCreator, TMetadata metadata) : base (default(System.Func>)) { } + public TMetadata Metadata { get { throw null; } } + } + public sealed partial class ExportLifetimeContext : System.IDisposable + { + public ExportLifetimeContext(T value, System.Action disposeAction) { } + public T Value { get { throw null; } } + public void Dispose() { } + } + [System.AttributeUsageAttribute((System.AttributeTargets)(1476), AllowMultiple=true, Inherited=false)] + public sealed partial class ExportMetadataAttribute : System.Attribute + { + public ExportMetadataAttribute(string name, object value) { } + public bool IsMultiple { get { throw null; } set { } } + public string Name { get { throw null; } } + public object Value { get { throw null; } } + } + public partial interface ICompositionService + { + void SatisfyImportsOnce(System.ComponentModel.Composition.Primitives.ComposablePart part); + } + [System.AttributeUsageAttribute((System.AttributeTargets)(2432), AllowMultiple=false, Inherited=false)] + public partial class ImportAttribute : System.Attribute + { + public ImportAttribute() { } + public ImportAttribute(string contractName) { } + public ImportAttribute(string contractName, System.Type contractType) { } + public ImportAttribute(System.Type contractType) { } + public bool AllowDefault { get { throw null; } set { } } + public bool AllowRecomposition { get { throw null; } set { } } + public string ContractName { get { throw null; } } + public System.Type ContractType { get { throw null; } } + public System.ComponentModel.Composition.CreationPolicy RequiredCreationPolicy { get { throw null; } set { } } + public System.ComponentModel.Composition.ImportSource Source { get { throw null; } set { } } + } + public partial class ImportCardinalityMismatchException : System.Exception + { + public ImportCardinalityMismatchException() { } + protected ImportCardinalityMismatchException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + public ImportCardinalityMismatchException(string message) { } + public ImportCardinalityMismatchException(string message, System.Exception innerException) { } + } + [System.AttributeUsageAttribute((System.AttributeTargets)(32), AllowMultiple=false, Inherited=false)] + public partial class ImportingConstructorAttribute : System.Attribute + { + public ImportingConstructorAttribute() { } + } + [System.AttributeUsageAttribute((System.AttributeTargets)(2432), AllowMultiple=false, Inherited=false)] + public partial class ImportManyAttribute : System.Attribute + { + public ImportManyAttribute() { } + public ImportManyAttribute(string contractName) { } + public ImportManyAttribute(string contractName, System.Type contractType) { } + public ImportManyAttribute(System.Type contractType) { } + public bool AllowRecomposition { get { throw null; } set { } } + public string ContractName { get { throw null; } } + public System.Type ContractType { get { throw null; } } + public System.ComponentModel.Composition.CreationPolicy RequiredCreationPolicy { get { throw null; } set { } } + public System.ComponentModel.Composition.ImportSource Source { get { throw null; } set { } } + } + public enum ImportSource + { + Any = 0, + Local = 1, + NonLocal = 2, + } + [System.AttributeUsageAttribute((System.AttributeTargets)(1028), AllowMultiple=true, Inherited=true)] + public partial class InheritedExportAttribute : System.ComponentModel.Composition.ExportAttribute + { + public InheritedExportAttribute() { } + public InheritedExportAttribute(string contractName) { } + public InheritedExportAttribute(string contractName, System.Type contractType) { } + public InheritedExportAttribute(System.Type contractType) { } + } + public partial interface IPartImportsSatisfiedNotification + { + void OnImportsSatisfied(); + } + [System.AttributeUsageAttribute((System.AttributeTargets)(4), AllowMultiple=false, Inherited=true)] + public sealed partial class MetadataAttributeAttribute : System.Attribute + { + public MetadataAttributeAttribute() { } + } + [System.AttributeUsageAttribute((System.AttributeTargets)(1024), AllowMultiple=false, Inherited=false)] + public sealed partial class MetadataViewImplementationAttribute : System.Attribute + { + public MetadataViewImplementationAttribute(System.Type implementationType) { } + public System.Type ImplementationType { get { throw null; } } + } + [System.AttributeUsageAttribute((System.AttributeTargets)(4), AllowMultiple=false, Inherited=false)] + public sealed partial class PartCreationPolicyAttribute : System.Attribute + { + public PartCreationPolicyAttribute(System.ComponentModel.Composition.CreationPolicy creationPolicy) { } + public System.ComponentModel.Composition.CreationPolicy CreationPolicy { get { throw null; } } + } + [System.AttributeUsageAttribute((System.AttributeTargets)(4), AllowMultiple=true, Inherited=false)] + public sealed partial class PartMetadataAttribute : System.Attribute + { + public PartMetadataAttribute(string name, object value) { } + public string Name { get { throw null; } } + public object Value { get { throw null; } } + } + [System.AttributeUsageAttribute((System.AttributeTargets)(4), AllowMultiple=false, Inherited=false)] + public sealed partial class PartNotDiscoverableAttribute : System.Attribute + { + public PartNotDiscoverableAttribute() { } + } +} +namespace System.ComponentModel.Composition.Hosting +{ + public partial class AggregateCatalog : System.ComponentModel.Composition.Primitives.ComposablePartCatalog, System.ComponentModel.Composition.Hosting.INotifyComposablePartCatalogChanged + { + public AggregateCatalog() { } + public AggregateCatalog(System.Collections.Generic.IEnumerable catalogs) { } + public AggregateCatalog(params System.ComponentModel.Composition.Primitives.ComposablePartCatalog[] catalogs) { } + public System.Collections.Generic.ICollection Catalogs { get { throw null; } } + public event System.EventHandler Changed { add { } remove { } } + public event System.EventHandler Changing { add { } remove { } } + protected override void Dispose(bool disposing) { } + public override System.Collections.Generic.IEnumerator GetEnumerator() { throw null; } + public override System.Collections.Generic.IEnumerable> GetExports(System.ComponentModel.Composition.Primitives.ImportDefinition definition) { throw null; } + protected virtual void OnChanged(System.ComponentModel.Composition.Hosting.ComposablePartCatalogChangeEventArgs e) { } + protected virtual void OnChanging(System.ComponentModel.Composition.Hosting.ComposablePartCatalogChangeEventArgs e) { } + } + public partial class AggregateExportProvider : System.ComponentModel.Composition.Hosting.ExportProvider, System.IDisposable + { + public AggregateExportProvider(System.Collections.Generic.IEnumerable providers) { } + public AggregateExportProvider(params System.ComponentModel.Composition.Hosting.ExportProvider[] providers) { } + public System.Collections.ObjectModel.ReadOnlyCollection Providers { get { throw null; } } + public void Dispose() { } + protected virtual void Dispose(bool disposing) { } + protected override System.Collections.Generic.IEnumerable GetExportsCore(System.ComponentModel.Composition.Primitives.ImportDefinition definition, System.ComponentModel.Composition.Hosting.AtomicComposition atomicComposition) { throw null; } + } + public partial class ApplicationCatalog : System.ComponentModel.Composition.Primitives.ComposablePartCatalog, System.ComponentModel.Composition.Primitives.ICompositionElement + { + public ApplicationCatalog() { } + public ApplicationCatalog(System.ComponentModel.Composition.Primitives.ICompositionElement definitionOrigin) { } + public ApplicationCatalog(System.Reflection.ReflectionContext reflectionContext) { } + public ApplicationCatalog(System.Reflection.ReflectionContext reflectionContext, System.ComponentModel.Composition.Primitives.ICompositionElement definitionOrigin) { } + string System.ComponentModel.Composition.Primitives.ICompositionElement.DisplayName { get { throw null; } } + System.ComponentModel.Composition.Primitives.ICompositionElement System.ComponentModel.Composition.Primitives.ICompositionElement.Origin { get { throw null; } } + protected override void Dispose(bool disposing) { } + public override System.Collections.Generic.IEnumerator GetEnumerator() { throw null; } + public override System.Collections.Generic.IEnumerable> GetExports(System.ComponentModel.Composition.Primitives.ImportDefinition definition) { throw null; } + public override string ToString() { throw null; } + } + public partial class AssemblyCatalog : System.ComponentModel.Composition.Primitives.ComposablePartCatalog, System.ComponentModel.Composition.Primitives.ICompositionElement + { + public AssemblyCatalog(System.Reflection.Assembly assembly) { } + public AssemblyCatalog(System.Reflection.Assembly assembly, System.ComponentModel.Composition.Primitives.ICompositionElement definitionOrigin) { } + public AssemblyCatalog(System.Reflection.Assembly assembly, System.Reflection.ReflectionContext reflectionContext) { } + public AssemblyCatalog(System.Reflection.Assembly assembly, System.Reflection.ReflectionContext reflectionContext, System.ComponentModel.Composition.Primitives.ICompositionElement definitionOrigin) { } + public AssemblyCatalog(string codeBase) { } + public AssemblyCatalog(string codeBase, System.ComponentModel.Composition.Primitives.ICompositionElement definitionOrigin) { } + public AssemblyCatalog(string codeBase, System.Reflection.ReflectionContext reflectionContext) { } + public AssemblyCatalog(string codeBase, System.Reflection.ReflectionContext reflectionContext, System.ComponentModel.Composition.Primitives.ICompositionElement definitionOrigin) { } + public System.Reflection.Assembly Assembly { get { throw null; } } + string System.ComponentModel.Composition.Primitives.ICompositionElement.DisplayName { get { throw null; } } + System.ComponentModel.Composition.Primitives.ICompositionElement System.ComponentModel.Composition.Primitives.ICompositionElement.Origin { get { throw null; } } + protected override void Dispose(bool disposing) { } + public override System.Collections.Generic.IEnumerator GetEnumerator() { throw null; } + public override System.Collections.Generic.IEnumerable> GetExports(System.ComponentModel.Composition.Primitives.ImportDefinition definition) { throw null; } + public override string ToString() { throw null; } + } + public partial class AtomicComposition : System.IDisposable + { + public AtomicComposition() { } + public AtomicComposition(System.ComponentModel.Composition.Hosting.AtomicComposition outerAtomicComposition) { } + public void AddCompleteAction(System.Action completeAction) { } + public void AddRevertAction(System.Action revertAction) { } + public void Complete() { } + public void Dispose() { } + protected virtual void Dispose(bool disposing) { } + public void SetValue(object key, object value) { } + public bool TryGetValue(object key, bool localAtomicCompositionOnly, out T value) { throw null; } + public bool TryGetValue(object key, out T value) { throw null; } + } + public partial class CatalogExportProvider : System.ComponentModel.Composition.Hosting.ExportProvider, System.IDisposable + { + public CatalogExportProvider(System.ComponentModel.Composition.Primitives.ComposablePartCatalog catalog) { } + public CatalogExportProvider(System.ComponentModel.Composition.Primitives.ComposablePartCatalog catalog, bool isThreadSafe) { } + public CatalogExportProvider(System.ComponentModel.Composition.Primitives.ComposablePartCatalog catalog, System.ComponentModel.Composition.Hosting.CompositionOptions compositionOptions) { } + public System.ComponentModel.Composition.Primitives.ComposablePartCatalog Catalog { get { throw null; } } + public System.ComponentModel.Composition.Hosting.ExportProvider SourceProvider { get { throw null; } set { } } + public void Dispose() { } + protected virtual void Dispose(bool disposing) { } + protected override System.Collections.Generic.IEnumerable GetExportsCore(System.ComponentModel.Composition.Primitives.ImportDefinition definition, System.ComponentModel.Composition.Hosting.AtomicComposition atomicComposition) { throw null; } + } + public static partial class CatalogExtensions + { + public static System.ComponentModel.Composition.Hosting.CompositionService CreateCompositionService(this System.ComponentModel.Composition.Primitives.ComposablePartCatalog composablePartCatalog) { throw null; } + } + public partial class ComposablePartCatalogChangeEventArgs : System.EventArgs + { + public ComposablePartCatalogChangeEventArgs(System.Collections.Generic.IEnumerable addedDefinitions, System.Collections.Generic.IEnumerable removedDefinitions, System.ComponentModel.Composition.Hosting.AtomicComposition atomicComposition) { } + public System.Collections.Generic.IEnumerable AddedDefinitions { get { throw null; } } + public System.ComponentModel.Composition.Hosting.AtomicComposition AtomicComposition { get { throw null; } } + public System.Collections.Generic.IEnumerable RemovedDefinitions { get { throw null; } } + } + public partial class ComposablePartExportProvider : System.ComponentModel.Composition.Hosting.ExportProvider, System.IDisposable + { + public ComposablePartExportProvider() { } + public ComposablePartExportProvider(bool isThreadSafe) { } + public ComposablePartExportProvider(System.ComponentModel.Composition.Hosting.CompositionOptions compositionOptions) { } + public System.ComponentModel.Composition.Hosting.ExportProvider SourceProvider { get { throw null; } set { } } + public void Compose(System.ComponentModel.Composition.Hosting.CompositionBatch batch) { } + public void Dispose() { } + protected virtual void Dispose(bool disposing) { } + protected override System.Collections.Generic.IEnumerable GetExportsCore(System.ComponentModel.Composition.Primitives.ImportDefinition definition, System.ComponentModel.Composition.Hosting.AtomicComposition atomicComposition) { throw null; } + } + public partial class CompositionBatch + { + public CompositionBatch() { } + public CompositionBatch(System.Collections.Generic.IEnumerable partsToAdd, System.Collections.Generic.IEnumerable partsToRemove) { } + public System.Collections.ObjectModel.ReadOnlyCollection PartsToAdd { get { throw null; } } + public System.Collections.ObjectModel.ReadOnlyCollection PartsToRemove { get { throw null; } } + public System.ComponentModel.Composition.Primitives.ComposablePart AddExport(System.ComponentModel.Composition.Primitives.Export export) { throw null; } + public void AddPart(System.ComponentModel.Composition.Primitives.ComposablePart part) { } + public void RemovePart(System.ComponentModel.Composition.Primitives.ComposablePart part) { } + } + public static partial class CompositionConstants + { + public const string ExportTypeIdentityMetadataName = "ExportTypeIdentity"; + public const string GenericContractMetadataName = "System.ComponentModel.Composition.GenericContractName"; + public const string GenericParametersMetadataName = "System.ComponentModel.Composition.GenericParameters"; + public const string ImportSourceMetadataName = "System.ComponentModel.Composition.ImportSource"; + public const string IsGenericPartMetadataName = "System.ComponentModel.Composition.IsGenericPart"; + public const string PartCreationPolicyMetadataName = "System.ComponentModel.Composition.CreationPolicy"; + } + public partial class CompositionContainer : System.ComponentModel.Composition.Hosting.ExportProvider, System.ComponentModel.Composition.ICompositionService, System.IDisposable + { + public CompositionContainer() { } + public CompositionContainer(System.ComponentModel.Composition.Hosting.CompositionOptions compositionOptions, params System.ComponentModel.Composition.Hosting.ExportProvider[] providers) { } + public CompositionContainer(params System.ComponentModel.Composition.Hosting.ExportProvider[] providers) { } + public CompositionContainer(System.ComponentModel.Composition.Primitives.ComposablePartCatalog catalog, bool isThreadSafe, params System.ComponentModel.Composition.Hosting.ExportProvider[] providers) { } + public CompositionContainer(System.ComponentModel.Composition.Primitives.ComposablePartCatalog catalog, System.ComponentModel.Composition.Hosting.CompositionOptions compositionOptions, params System.ComponentModel.Composition.Hosting.ExportProvider[] providers) { } + public CompositionContainer(System.ComponentModel.Composition.Primitives.ComposablePartCatalog catalog, params System.ComponentModel.Composition.Hosting.ExportProvider[] providers) { } + public System.ComponentModel.Composition.Primitives.ComposablePartCatalog Catalog { get { throw null; } } + public System.Collections.ObjectModel.ReadOnlyCollection Providers { get { throw null; } } + public void Compose(System.ComponentModel.Composition.Hosting.CompositionBatch batch) { } + public void Dispose() { } + protected virtual void Dispose(bool disposing) { } + protected override System.Collections.Generic.IEnumerable GetExportsCore(System.ComponentModel.Composition.Primitives.ImportDefinition definition, System.ComponentModel.Composition.Hosting.AtomicComposition atomicComposition) { throw null; } + public void ReleaseExport(System.ComponentModel.Composition.Primitives.Export export) { } + public void ReleaseExports(System.Collections.Generic.IEnumerable exports) { } + public void ReleaseExports(System.Collections.Generic.IEnumerable> exports) { } + public void ReleaseExports(System.Collections.Generic.IEnumerable> exports) { } + public void ReleaseExport(System.Lazy export) { } + public void SatisfyImportsOnce(System.ComponentModel.Composition.Primitives.ComposablePart part) { } + } + [System.FlagsAttribute] + public enum CompositionOptions + { + Default = 0, + DisableSilentRejection = 1, + ExportCompositionService = 4, + IsThreadSafe = 2, + } + public partial class CompositionScopeDefinition : System.ComponentModel.Composition.Primitives.ComposablePartCatalog, System.ComponentModel.Composition.Hosting.INotifyComposablePartCatalogChanged + { + protected CompositionScopeDefinition() { } + public CompositionScopeDefinition(System.ComponentModel.Composition.Primitives.ComposablePartCatalog catalog, System.Collections.Generic.IEnumerable children) { } + public CompositionScopeDefinition(System.ComponentModel.Composition.Primitives.ComposablePartCatalog catalog, System.Collections.Generic.IEnumerable children, System.Collections.Generic.IEnumerable publicSurface) { } + public virtual System.Collections.Generic.IEnumerable Children { get { throw null; } } + public virtual System.Collections.Generic.IEnumerable PublicSurface { get { throw null; } } + public event System.EventHandler Changed { add { } remove { } } + public event System.EventHandler Changing { add { } remove { } } + protected override void Dispose(bool disposing) { } + public override System.Collections.Generic.IEnumerator GetEnumerator() { throw null; } + public override System.Collections.Generic.IEnumerable> GetExports(System.ComponentModel.Composition.Primitives.ImportDefinition definition) { throw null; } + protected virtual void OnChanged(System.ComponentModel.Composition.Hosting.ComposablePartCatalogChangeEventArgs e) { } + protected virtual void OnChanging(System.ComponentModel.Composition.Hosting.ComposablePartCatalogChangeEventArgs e) { } + } + public partial class CompositionService : System.ComponentModel.Composition.ICompositionService, System.IDisposable + { + internal CompositionService() { } + public void Dispose() { } + public void SatisfyImportsOnce(System.ComponentModel.Composition.Primitives.ComposablePart part) { } + } + public partial class DirectoryCatalog : System.ComponentModel.Composition.Primitives.ComposablePartCatalog, System.ComponentModel.Composition.Hosting.INotifyComposablePartCatalogChanged, System.ComponentModel.Composition.Primitives.ICompositionElement + { + public DirectoryCatalog(string path) { } + public DirectoryCatalog(string path, System.ComponentModel.Composition.Primitives.ICompositionElement definitionOrigin) { } + public DirectoryCatalog(string path, System.Reflection.ReflectionContext reflectionContext) { } + public DirectoryCatalog(string path, System.Reflection.ReflectionContext reflectionContext, System.ComponentModel.Composition.Primitives.ICompositionElement definitionOrigin) { } + public DirectoryCatalog(string path, string searchPattern) { } + public DirectoryCatalog(string path, string searchPattern, System.ComponentModel.Composition.Primitives.ICompositionElement definitionOrigin) { } + public DirectoryCatalog(string path, string searchPattern, System.Reflection.ReflectionContext reflectionContext) { } + public DirectoryCatalog(string path, string searchPattern, System.Reflection.ReflectionContext reflectionContext, System.ComponentModel.Composition.Primitives.ICompositionElement definitionOrigin) { } + public string FullPath { get { throw null; } } + public System.Collections.ObjectModel.ReadOnlyCollection LoadedFiles { get { throw null; } } + public string Path { get { throw null; } } + public string SearchPattern { get { throw null; } } + string System.ComponentModel.Composition.Primitives.ICompositionElement.DisplayName { get { throw null; } } + System.ComponentModel.Composition.Primitives.ICompositionElement System.ComponentModel.Composition.Primitives.ICompositionElement.Origin { get { throw null; } } + public event System.EventHandler Changed { add { } remove { } } + public event System.EventHandler Changing { add { } remove { } } + protected override void Dispose(bool disposing) { } + public override System.Collections.Generic.IEnumerator GetEnumerator() { throw null; } + public override System.Collections.Generic.IEnumerable> GetExports(System.ComponentModel.Composition.Primitives.ImportDefinition definition) { throw null; } + protected virtual void OnChanged(System.ComponentModel.Composition.Hosting.ComposablePartCatalogChangeEventArgs e) { } + protected virtual void OnChanging(System.ComponentModel.Composition.Hosting.ComposablePartCatalogChangeEventArgs e) { } + public void Refresh() { } + public override string ToString() { throw null; } + } + public abstract partial class ExportProvider + { + protected ExportProvider() { } + public event System.EventHandler ExportsChanged { add { } remove { } } + public event System.EventHandler ExportsChanging { add { } remove { } } + public T GetExportedValueOrDefault() { throw null; } + public T GetExportedValueOrDefault(string contractName) { throw null; } + public System.Collections.Generic.IEnumerable GetExportedValues() { throw null; } + public System.Collections.Generic.IEnumerable GetExportedValues(string contractName) { throw null; } + public T GetExportedValue() { throw null; } + public T GetExportedValue(string contractName) { throw null; } + public System.Collections.Generic.IEnumerable GetExports(System.ComponentModel.Composition.Primitives.ImportDefinition definition) { throw null; } + public System.Collections.Generic.IEnumerable GetExports(System.ComponentModel.Composition.Primitives.ImportDefinition definition, System.ComponentModel.Composition.Hosting.AtomicComposition atomicComposition) { throw null; } + public System.Collections.Generic.IEnumerable> GetExports(System.Type type, System.Type metadataViewType, string contractName) { throw null; } + protected abstract System.Collections.Generic.IEnumerable GetExportsCore(System.ComponentModel.Composition.Primitives.ImportDefinition definition, System.ComponentModel.Composition.Hosting.AtomicComposition atomicComposition); + public System.Collections.Generic.IEnumerable> GetExports() { throw null; } + public System.Collections.Generic.IEnumerable> GetExports(string contractName) { throw null; } + public System.Collections.Generic.IEnumerable> GetExports() { throw null; } + public System.Collections.Generic.IEnumerable> GetExports(string contractName) { throw null; } + public System.Lazy GetExport() { throw null; } + public System.Lazy GetExport(string contractName) { throw null; } + public System.Lazy GetExport() { throw null; } + public System.Lazy GetExport(string contractName) { throw null; } + protected virtual void OnExportsChanged(System.ComponentModel.Composition.Hosting.ExportsChangeEventArgs e) { } + protected virtual void OnExportsChanging(System.ComponentModel.Composition.Hosting.ExportsChangeEventArgs e) { } + public bool TryGetExports(System.ComponentModel.Composition.Primitives.ImportDefinition definition, System.ComponentModel.Composition.Hosting.AtomicComposition atomicComposition, out System.Collections.Generic.IEnumerable exports) { throw null; } + } + public partial class ExportsChangeEventArgs : System.EventArgs + { + public ExportsChangeEventArgs(System.Collections.Generic.IEnumerable addedExports, System.Collections.Generic.IEnumerable removedExports, System.ComponentModel.Composition.Hosting.AtomicComposition atomicComposition) { } + public System.Collections.Generic.IEnumerable AddedExports { get { throw null; } } + public System.ComponentModel.Composition.Hosting.AtomicComposition AtomicComposition { get { throw null; } } + public System.Collections.Generic.IEnumerable ChangedContractNames { get { throw null; } } + public System.Collections.Generic.IEnumerable RemovedExports { get { throw null; } } + } + public partial class FilteredCatalog : System.ComponentModel.Composition.Primitives.ComposablePartCatalog, System.ComponentModel.Composition.Hosting.INotifyComposablePartCatalogChanged + { + public FilteredCatalog(System.ComponentModel.Composition.Primitives.ComposablePartCatalog catalog, System.Func filter) { } + public System.ComponentModel.Composition.Hosting.FilteredCatalog Complement { get { throw null; } } + public event System.EventHandler Changed { add { } remove { } } + public event System.EventHandler Changing { add { } remove { } } + protected override void Dispose(bool disposing) { } + public override System.Collections.Generic.IEnumerator GetEnumerator() { throw null; } + public override System.Collections.Generic.IEnumerable> GetExports(System.ComponentModel.Composition.Primitives.ImportDefinition definition) { throw null; } + public System.ComponentModel.Composition.Hosting.FilteredCatalog IncludeDependencies() { throw null; } + public System.ComponentModel.Composition.Hosting.FilteredCatalog IncludeDependencies(System.Func importFilter) { throw null; } + public System.ComponentModel.Composition.Hosting.FilteredCatalog IncludeDependents() { throw null; } + public System.ComponentModel.Composition.Hosting.FilteredCatalog IncludeDependents(System.Func importFilter) { throw null; } + protected virtual void OnChanged(System.ComponentModel.Composition.Hosting.ComposablePartCatalogChangeEventArgs e) { } + protected virtual void OnChanging(System.ComponentModel.Composition.Hosting.ComposablePartCatalogChangeEventArgs e) { } + } + public partial class ImportEngine : System.ComponentModel.Composition.ICompositionService, System.IDisposable + { + public ImportEngine(System.ComponentModel.Composition.Hosting.ExportProvider sourceProvider) { } + public ImportEngine(System.ComponentModel.Composition.Hosting.ExportProvider sourceProvider, bool isThreadSafe) { } + public ImportEngine(System.ComponentModel.Composition.Hosting.ExportProvider sourceProvider, System.ComponentModel.Composition.Hosting.CompositionOptions compositionOptions) { } + public void Dispose() { } + protected virtual void Dispose(bool disposing) { } + public void PreviewImports(System.ComponentModel.Composition.Primitives.ComposablePart part, System.ComponentModel.Composition.Hosting.AtomicComposition atomicComposition) { } + public void ReleaseImports(System.ComponentModel.Composition.Primitives.ComposablePart part, System.ComponentModel.Composition.Hosting.AtomicComposition atomicComposition) { } + public void SatisfyImports(System.ComponentModel.Composition.Primitives.ComposablePart part) { } + public void SatisfyImportsOnce(System.ComponentModel.Composition.Primitives.ComposablePart part) { } + } + public partial interface INotifyComposablePartCatalogChanged + { + event System.EventHandler Changed; + event System.EventHandler Changing; + } + public static partial class ScopingExtensions + { + public static bool ContainsPartMetadataWithKey(this System.ComponentModel.Composition.Primitives.ComposablePartDefinition part, string key) { throw null; } + public static bool ContainsPartMetadata(this System.ComponentModel.Composition.Primitives.ComposablePartDefinition part, string key, T value) { throw null; } + public static bool Exports(this System.ComponentModel.Composition.Primitives.ComposablePartDefinition part, string contractName) { throw null; } + public static System.ComponentModel.Composition.Hosting.FilteredCatalog Filter(this System.ComponentModel.Composition.Primitives.ComposablePartCatalog catalog, System.Func filter) { throw null; } + public static bool Imports(this System.ComponentModel.Composition.Primitives.ComposablePartDefinition part, string contractName) { throw null; } + public static bool Imports(this System.ComponentModel.Composition.Primitives.ComposablePartDefinition part, string contractName, System.ComponentModel.Composition.Primitives.ImportCardinality importCardinality) { throw null; } + } + public partial class TypeCatalog : System.ComponentModel.Composition.Primitives.ComposablePartCatalog, System.ComponentModel.Composition.Primitives.ICompositionElement + { + public TypeCatalog(System.Collections.Generic.IEnumerable types) { } + public TypeCatalog(System.Collections.Generic.IEnumerable types, System.ComponentModel.Composition.Primitives.ICompositionElement definitionOrigin) { } + public TypeCatalog(System.Collections.Generic.IEnumerable types, System.Reflection.ReflectionContext reflectionContext) { } + public TypeCatalog(System.Collections.Generic.IEnumerable types, System.Reflection.ReflectionContext reflectionContext, System.ComponentModel.Composition.Primitives.ICompositionElement definitionOrigin) { } + public TypeCatalog(params System.Type[] types) { } + string System.ComponentModel.Composition.Primitives.ICompositionElement.DisplayName { get { throw null; } } + System.ComponentModel.Composition.Primitives.ICompositionElement System.ComponentModel.Composition.Primitives.ICompositionElement.Origin { get { throw null; } } + protected override void Dispose(bool disposing) { } + public override System.Collections.Generic.IEnumerator GetEnumerator() { throw null; } + public override string ToString() { throw null; } + } +} +namespace System.ComponentModel.Composition.Primitives +{ + public abstract partial class ComposablePart + { + protected ComposablePart() { } + public abstract System.Collections.Generic.IEnumerable ExportDefinitions { get; } + public abstract System.Collections.Generic.IEnumerable ImportDefinitions { get; } + public virtual System.Collections.Generic.IDictionary Metadata { get { throw null; } } + public virtual void Activate() { } + public abstract object GetExportedValue(System.ComponentModel.Composition.Primitives.ExportDefinition definition); + public abstract void SetImport(System.ComponentModel.Composition.Primitives.ImportDefinition definition, System.Collections.Generic.IEnumerable exports); + } + public abstract partial class ComposablePartCatalog : System.Collections.Generic.IEnumerable, System.Collections.IEnumerable, System.IDisposable + { + protected ComposablePartCatalog() { } + [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] + public virtual System.Linq.IQueryable Parts { get { throw null; } } + public void Dispose() { } + protected virtual void Dispose(bool disposing) { } + public virtual System.Collections.Generic.IEnumerator GetEnumerator() { throw null; } + public virtual System.Collections.Generic.IEnumerable> GetExports(System.ComponentModel.Composition.Primitives.ImportDefinition definition) { throw null; } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } + } + public abstract partial class ComposablePartDefinition + { + protected ComposablePartDefinition() { } + public abstract System.Collections.Generic.IEnumerable ExportDefinitions { get; } + public abstract System.Collections.Generic.IEnumerable ImportDefinitions { get; } + public virtual System.Collections.Generic.IDictionary Metadata { get { throw null; } } + public abstract System.ComponentModel.Composition.Primitives.ComposablePart CreatePart(); + } + public partial class ComposablePartException : System.Exception + { + public ComposablePartException() { } + public ComposablePartException(string message) { } + public ComposablePartException(string message, System.ComponentModel.Composition.Primitives.ICompositionElement element) { } + public ComposablePartException(string message, System.ComponentModel.Composition.Primitives.ICompositionElement element, System.Exception innerException) { } + public ComposablePartException(string message, System.Exception innerException) { } + public System.ComponentModel.Composition.Primitives.ICompositionElement Element { get { throw null; } } + } + public partial class ContractBasedImportDefinition : System.ComponentModel.Composition.Primitives.ImportDefinition + { + protected ContractBasedImportDefinition() { } + public ContractBasedImportDefinition(string contractName, string requiredTypeIdentity, System.Collections.Generic.IEnumerable> requiredMetadata, System.ComponentModel.Composition.Primitives.ImportCardinality cardinality, bool isRecomposable, bool isPrerequisite, System.ComponentModel.Composition.CreationPolicy requiredCreationPolicy) { } + public ContractBasedImportDefinition(string contractName, string requiredTypeIdentity, System.Collections.Generic.IEnumerable> requiredMetadata, System.ComponentModel.Composition.Primitives.ImportCardinality cardinality, bool isRecomposable, bool isPrerequisite, System.ComponentModel.Composition.CreationPolicy requiredCreationPolicy, System.Collections.Generic.IDictionary metadata) { } + public override System.Linq.Expressions.Expression> Constraint { get { throw null; } } + public virtual System.ComponentModel.Composition.CreationPolicy RequiredCreationPolicy { get { throw null; } } + public virtual System.Collections.Generic.IEnumerable> RequiredMetadata { get { throw null; } } + public virtual string RequiredTypeIdentity { get { throw null; } } + public override bool IsConstraintSatisfiedBy(System.ComponentModel.Composition.Primitives.ExportDefinition exportDefinition) { throw null; } + public override string ToString() { throw null; } + } + public partial class Export + { + protected Export() { } + public Export(System.ComponentModel.Composition.Primitives.ExportDefinition definition, System.Func exportedValueGetter) { } + public Export(string contractName, System.Collections.Generic.IDictionary metadata, System.Func exportedValueGetter) { } + public Export(string contractName, System.Func exportedValueGetter) { } + public virtual System.ComponentModel.Composition.Primitives.ExportDefinition Definition { get { throw null; } } + public System.Collections.Generic.IDictionary Metadata { get { throw null; } } + public object Value { get { throw null; } } + protected virtual object GetExportedValueCore() { throw null; } + } + public partial class ExportDefinition + { + protected ExportDefinition() { } + public ExportDefinition(string contractName, System.Collections.Generic.IDictionary metadata) { } + public virtual string ContractName { get { throw null; } } + public virtual System.Collections.Generic.IDictionary Metadata { get { throw null; } } + public override string ToString() { throw null; } + } + public partial class ExportedDelegate + { + protected ExportedDelegate() { } + public ExportedDelegate(object instance, System.Reflection.MethodInfo method) { } + public virtual System.Delegate CreateDelegate(System.Type delegateType) { throw null; } + } + public partial interface ICompositionElement + { + string DisplayName { get; } + System.ComponentModel.Composition.Primitives.ICompositionElement Origin { get; } + } + public enum ImportCardinality + { + ExactlyOne = 1, + ZeroOrMore = 2, + ZeroOrOne = 0, + } + public partial class ImportDefinition + { + protected ImportDefinition() { } + public ImportDefinition(System.Linq.Expressions.Expression> constraint, string contractName, System.ComponentModel.Composition.Primitives.ImportCardinality cardinality, bool isRecomposable, bool isPrerequisite) { } + public ImportDefinition(System.Linq.Expressions.Expression> constraint, string contractName, System.ComponentModel.Composition.Primitives.ImportCardinality cardinality, bool isRecomposable, bool isPrerequisite, System.Collections.Generic.IDictionary metadata) { } + public virtual System.ComponentModel.Composition.Primitives.ImportCardinality Cardinality { get { throw null; } } + public virtual System.Linq.Expressions.Expression> Constraint { get { throw null; } } + public virtual string ContractName { get { throw null; } } + public virtual bool IsPrerequisite { get { throw null; } } + public virtual bool IsRecomposable { get { throw null; } } + public virtual System.Collections.Generic.IDictionary Metadata { get { throw null; } } + public virtual bool IsConstraintSatisfiedBy(System.ComponentModel.Composition.Primitives.ExportDefinition exportDefinition) { throw null; } + public override string ToString() { throw null; } + } +} +namespace System.ComponentModel.Composition.ReflectionModel +{ + public partial struct LazyMemberInfo + { + private object _dummy; + public LazyMemberInfo(System.Reflection.MemberInfo member) { throw null; } + public LazyMemberInfo(System.Reflection.MemberTypes memberType, System.Func accessorsCreator) { throw null; } + public LazyMemberInfo(System.Reflection.MemberTypes memberType, params System.Reflection.MemberInfo[] accessors) { throw null; } + public System.Reflection.MemberTypes MemberType { get { throw null; } } + public override bool Equals(object obj) { throw null; } + public System.Reflection.MemberInfo[] GetAccessors() { throw null; } + public override int GetHashCode() { throw null; } + public static bool operator ==(System.ComponentModel.Composition.ReflectionModel.LazyMemberInfo left, System.ComponentModel.Composition.ReflectionModel.LazyMemberInfo right) { throw null; } + public static bool operator !=(System.ComponentModel.Composition.ReflectionModel.LazyMemberInfo left, System.ComponentModel.Composition.ReflectionModel.LazyMemberInfo right) { throw null; } + } + public static partial class ReflectionModelServices + { + public static System.ComponentModel.Composition.Primitives.ExportDefinition CreateExportDefinition(System.ComponentModel.Composition.ReflectionModel.LazyMemberInfo exportingMember, string contractName, System.Lazy> metadata, System.ComponentModel.Composition.Primitives.ICompositionElement origin) { throw null; } + public static System.ComponentModel.Composition.Primitives.ContractBasedImportDefinition CreateImportDefinition(System.ComponentModel.Composition.ReflectionModel.LazyMemberInfo importingMember, string contractName, string requiredTypeIdentity, System.Collections.Generic.IEnumerable> requiredMetadata, System.ComponentModel.Composition.Primitives.ImportCardinality cardinality, bool isRecomposable, bool isPreRequisite, System.ComponentModel.Composition.CreationPolicy requiredCreationPolicy, System.Collections.Generic.IDictionary metadata, bool isExportFactory, System.ComponentModel.Composition.Primitives.ICompositionElement origin) { throw null; } + public static System.ComponentModel.Composition.Primitives.ContractBasedImportDefinition CreateImportDefinition(System.ComponentModel.Composition.ReflectionModel.LazyMemberInfo importingMember, string contractName, string requiredTypeIdentity, System.Collections.Generic.IEnumerable> requiredMetadata, System.ComponentModel.Composition.Primitives.ImportCardinality cardinality, bool isRecomposable, System.ComponentModel.Composition.CreationPolicy requiredCreationPolicy, System.Collections.Generic.IDictionary metadata, bool isExportFactory, System.ComponentModel.Composition.Primitives.ICompositionElement origin) { throw null; } + public static System.ComponentModel.Composition.Primitives.ContractBasedImportDefinition CreateImportDefinition(System.ComponentModel.Composition.ReflectionModel.LazyMemberInfo importingMember, string contractName, string requiredTypeIdentity, System.Collections.Generic.IEnumerable> requiredMetadata, System.ComponentModel.Composition.Primitives.ImportCardinality cardinality, bool isRecomposable, System.ComponentModel.Composition.CreationPolicy requiredCreationPolicy, System.ComponentModel.Composition.Primitives.ICompositionElement origin) { throw null; } + public static System.ComponentModel.Composition.Primitives.ContractBasedImportDefinition CreateImportDefinition(System.Lazy parameter, string contractName, string requiredTypeIdentity, System.Collections.Generic.IEnumerable> requiredMetadata, System.ComponentModel.Composition.Primitives.ImportCardinality cardinality, System.ComponentModel.Composition.CreationPolicy requiredCreationPolicy, System.Collections.Generic.IDictionary metadata, bool isExportFactory, System.ComponentModel.Composition.Primitives.ICompositionElement origin) { throw null; } + public static System.ComponentModel.Composition.Primitives.ContractBasedImportDefinition CreateImportDefinition(System.Lazy parameter, string contractName, string requiredTypeIdentity, System.Collections.Generic.IEnumerable> requiredMetadata, System.ComponentModel.Composition.Primitives.ImportCardinality cardinality, System.ComponentModel.Composition.CreationPolicy requiredCreationPolicy, System.ComponentModel.Composition.Primitives.ICompositionElement origin) { throw null; } + public static System.ComponentModel.Composition.Primitives.ComposablePartDefinition CreatePartDefinition(System.Lazy partType, bool isDisposalRequired, System.Lazy> imports, System.Lazy> exports, System.Lazy> metadata, System.ComponentModel.Composition.Primitives.ICompositionElement origin) { throw null; } + public static System.ComponentModel.Composition.Primitives.ContractBasedImportDefinition GetExportFactoryProductImportDefinition(System.ComponentModel.Composition.Primitives.ImportDefinition importDefinition) { throw null; } + public static System.ComponentModel.Composition.ReflectionModel.LazyMemberInfo GetExportingMember(System.ComponentModel.Composition.Primitives.ExportDefinition exportDefinition) { throw null; } + public static System.ComponentModel.Composition.ReflectionModel.LazyMemberInfo GetImportingMember(System.ComponentModel.Composition.Primitives.ImportDefinition importDefinition) { throw null; } + public static System.Lazy GetImportingParameter(System.ComponentModel.Composition.Primitives.ImportDefinition importDefinition) { throw null; } + public static System.Lazy GetPartType(System.ComponentModel.Composition.Primitives.ComposablePartDefinition partDefinition) { throw null; } + public static bool IsDisposalRequired(System.ComponentModel.Composition.Primitives.ComposablePartDefinition partDefinition) { throw null; } + public static bool IsExportFactoryImportDefinition(System.ComponentModel.Composition.Primitives.ImportDefinition importDefinition) { throw null; } + public static bool IsImportingParameter(System.ComponentModel.Composition.Primitives.ImportDefinition importDefinition) { throw null; } + public static bool TryMakeGenericPartDefinition(System.ComponentModel.Composition.Primitives.ComposablePartDefinition partDefinition, System.Collections.Generic.IEnumerable genericParameters, out System.ComponentModel.Composition.Primitives.ComposablePartDefinition specialization) { throw null; } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/ref/System.ComponentModel.Composition.csproj b/external/corefx/src/System.ComponentModel.Composition/ref/System.ComponentModel.Composition.csproj new file mode 100644 index 0000000000..f6230f976b --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/ref/System.ComponentModel.Composition.csproj @@ -0,0 +1,13 @@ + + + + + {DD3B8052-CE03-4159-8311-1CE1382C51B0} + + + + + + + + diff --git a/external/corefx/src/System.ComponentModel.Composition/src/Configurations.props b/external/corefx/src/System.ComponentModel.Composition/src/Configurations.props new file mode 100644 index 0000000000..43f172907f --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/Configurations.props @@ -0,0 +1,14 @@ + + + + + netstandard; + netcoreapp2.0; + + + $(PackageConfigurations); + netcoreapp; + uap; + + + diff --git a/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/AdaptationHelpers.cs b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/AdaptationHelpers.cs new file mode 100644 index 0000000000..3eb99ddd4f --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/AdaptationHelpers.cs @@ -0,0 +1,50 @@ +// 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.ComponentModel.Composition; +using System.ComponentModel.Composition.Hosting; + +namespace Microsoft.Internal +{ + internal static class AdaptationHelpers + { + internal static CompositionResult TryInvoke(Action action) + { + try + { + action(); + return CompositionResult.SucceededResult; + } + catch (CompositionException ex) + { + return new CompositionResult(ex.Errors); + } + } + + internal static CompositionResult TryInvoke(Func action) + { + try + { + T value = action(); + return new CompositionResult(value); + } + catch (CompositionException ex) + { + return new CompositionResult(ex.Errors); + } + } + + internal static TValue GetValueFromAtomicComposition(AtomicComposition atomicComposition, object key, TValue defaultResult) + { + TValue result; + if (atomicComposition != null && atomicComposition.TryGetValue(key, out result)) + { + return result; + } + + return defaultResult; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/AttributeServices.cs b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/AttributeServices.cs new file mode 100644 index 0000000000..8f9ec5aee1 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/AttributeServices.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. +// See the LICENSE file in the project root for more information. + +using System.Linq; +using System.Reflection; + +namespace Microsoft.Internal +{ + internal static class AttributeServices + { + public static T[] GetAttributes(this ICustomAttributeProvider attributeProvider) where T : class + { + return (T[])attributeProvider.GetCustomAttributes(typeof(T), false); + } + + public static T[] GetAttributes(this ICustomAttributeProvider attributeProvider, bool inherit) where T : class + { + return (T[])attributeProvider.GetCustomAttributes(typeof(T), inherit); + } + + public static T GetFirstAttribute(this ICustomAttributeProvider attributeProvider) where T : class + { + return GetAttributes(attributeProvider).FirstOrDefault(); + } + + public static T GetFirstAttribute(this ICustomAttributeProvider attributeProvider, bool inherit) where T : class + { + return GetAttributes(attributeProvider, inherit).FirstOrDefault(); + } + + public static bool IsAttributeDefined(this ICustomAttributeProvider attributeProvider) where T : class + { + return attributeProvider.IsDefined(typeof(T), false); + } + + public static bool IsAttributeDefined(this ICustomAttributeProvider attributeProvider, bool inherit) where T : class + { + return attributeProvider.IsDefined(typeof(T), inherit); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Collections/CollectionServices.CollectionOfObject.cs b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Collections/CollectionServices.CollectionOfObject.cs new file mode 100644 index 0000000000..e5db4ec756 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Collections/CollectionServices.CollectionOfObject.cs @@ -0,0 +1,147 @@ +// 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.Collections; +using System.Collections.Generic; + +namespace Microsoft.Internal.Collections +{ + internal static partial class CollectionServices + { + public static ICollection GetCollectionWrapper(Type itemType, object collectionObject) + { + Assumes.NotNull(itemType, collectionObject); + + var underlyingItemType = itemType.UnderlyingSystemType; + + if (underlyingItemType == typeof(object)) + { + return (ICollection)collectionObject; + } + + // Most common .Net collections implement IList as well so for those + // cases we can optimize the wrapping instead of using reflection to create + // a generic type. + if (typeof(IList).IsAssignableFrom(collectionObject.GetType())) + { + return new CollectionOfObjectList((IList)collectionObject); + } + + Type collectionType = typeof(CollectionOfObject<>).MakeGenericType(underlyingItemType); + + return (ICollection)Activator.CreateInstance(collectionType, collectionObject); + } + + private class CollectionOfObjectList : ICollection + { + private readonly IList _list; + + public CollectionOfObjectList(IList list) + { + _list = list; + } + + public void Add(object item) + { + _list.Add(item); + } + + public void Clear() + { + _list.Clear(); + } + + public bool Contains(object item) + { + return Assumes.NotReachable(); + } + + public void CopyTo(object[] array, int arrayIndex) + { + Assumes.NotReachable(); + } + + public int Count + { + get { return Assumes.NotReachable(); } + } + + public bool IsReadOnly + { + get { return _list.IsReadOnly; } + } + + public bool Remove(object item) + { + return Assumes.NotReachable(); + } + + public IEnumerator GetEnumerator() + { + return Assumes.NotReachable>(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return Assumes.NotReachable(); + } + } + + private class CollectionOfObject : ICollection + { + private readonly ICollection _collectionOfT; + + public CollectionOfObject(object collectionOfT) + { + _collectionOfT = (ICollection)collectionOfT; + } + + public void Add(object item) + { + _collectionOfT.Add((T) item); + } + + public void Clear() + { + _collectionOfT.Clear(); + } + + public bool Contains(object item) + { + return Assumes.NotReachable(); + } + + public void CopyTo(object[] array, int arrayIndex) + { + Assumes.NotReachable(); + } + + public int Count + { + get { return Assumes.NotReachable(); } + } + + public bool IsReadOnly + { + get { return _collectionOfT.IsReadOnly; } + } + + public bool Remove(object item) + { + return Assumes.NotReachable(); + } + + public IEnumerator GetEnumerator() + { + return Assumes.NotReachable>(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return Assumes.NotReachable(); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Collections/CollectionServices.cs b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Collections/CollectionServices.cs new file mode 100644 index 0000000000..6f3c0eee2c --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Collections/CollectionServices.cs @@ -0,0 +1,293 @@ +// 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.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +namespace Microsoft.Internal.Collections +{ + internal static partial class CollectionServices + { + private static readonly Type StringType = typeof(string); + private static readonly Type IEnumerableType = typeof(IEnumerable); + private static readonly Type IEnumerableOfTType = typeof(IEnumerable<>); + private static readonly Type ICollectionOfTType = typeof(ICollection<>); + + public static bool IsEnumerableOfT(Type type) + { + if (type.IsGenericType) + { + Type genericType = type.GetGenericTypeDefinition().UnderlyingSystemType; + + if (genericType == IEnumerableOfTType) + { + return true; + } + } + return false; + } + + public static Type GetEnumerableElementType(Type type) + { + if (type.UnderlyingSystemType == StringType || !IEnumerableType.IsAssignableFrom(type)) + { + return null; + } + + Type closedType; + if (ReflectionServices.TryGetGenericInterfaceType(type, IEnumerableOfTType, out closedType)) + { + return closedType.GetGenericArguments()[0]; + } + + return null; + } + + public static Type GetCollectionElementType(Type type) + { + Type closedType; + if (ReflectionServices.TryGetGenericInterfaceType(type, ICollectionOfTType, out closedType)) + { + return closedType.GetGenericArguments()[0]; + } + + return null; + } + + public static ReadOnlyCollection ToReadOnlyCollection(this IEnumerable source) + { + Assumes.NotNull(source); + + return new ReadOnlyCollection(source.AsArray()); + } + + public static IEnumerable ConcatAllowingNull(this IEnumerable source, IEnumerable second) + { + if (second == null || !second.FastAny()) + { + return source; + } + + if (source == null || !source.FastAny()) + { + return second; + } + + return source.Concat(second); + } + + public static ICollection ConcatAllowingNull(this ICollection source, ICollection second) + { + if (second == null || (second.Count == 0)) + { + return source; + } + + if (source == null || (source.Count == 0)) + { + return second; + } + + List result = new List(source); + result.AddRange(second); + + return result; + } + + public static List FastAppendToListAllowNulls(this List source, IEnumerable second) + { + if (second == null) + { + return source; + } + + // if there's nothing in the source, return the second + if ((source == null) || (source.Count == 0)) + { + return second.AsList(); + } + + // if the second is List, and contains very few elements there's no need for AddRange + List secondAsList = second as List; + if (secondAsList != null) + { + if (secondAsList.Count == 0) + { + return source; + } + else if (secondAsList.Count == 1) + { + source.Add(secondAsList[0]); + return source; + } + } + + // last resort - nothing is null, need to append + source.AddRange(second); + return source; + + } + + private static List FastAppendToListAllowNulls(this List source, T value) + { + if (source == null) + { + source = new List(); + } + source.Add(value); + + return source; + } + + public static List FastAppendToListAllowNulls( + this List source, T value, + IEnumerable second) + { + if (second == null) + { + source = source.FastAppendToListAllowNulls(value); + } + else + { + source = source.FastAppendToListAllowNulls(second); + } + return source; + } + + public static void ForEach(this IEnumerable source, Action action) + { + foreach (T t in source) + { + action.Invoke(t); + } + } + + public static EnumerableCardinality GetCardinality(this IEnumerable source) + { + Assumes.NotNull(source); + + // Cast to ICollection instead of ICollection for performance reasons. + ICollection collection = source as ICollection; + if (collection != null) + { + switch (collection.Count) + { + case 0: + return EnumerableCardinality.Zero; + case 1: + return EnumerableCardinality.One; + default: + return EnumerableCardinality.TwoOrMore; + } + } + + using (var enumerator = source.GetEnumerator()) + { + if (!enumerator.MoveNext()) + { + return EnumerableCardinality.Zero; + } + + if (!enumerator.MoveNext()) + { + return EnumerableCardinality.One; + } + + return EnumerableCardinality.TwoOrMore; + } + } + + public static bool FastAny(this IEnumerable source) + { + // Enumerable.Any underneath doesn't cast to ICollection, + // like it does with many of the other LINQ methods. + // Below is significantly (4x) when mainly working with ICollection + // sources and a little slower if working with mainly IEnumerable + // sources. + + // Cast to ICollection instead of ICollection for performance reasons. + ICollection collection = source as ICollection; + if (collection != null) + { + return collection.Count > 0; + } + + return source.Any(); + } + + public static Stack Copy(this Stack stack) + { + Assumes.NotNull(stack); + + // Stack.GetEnumerator walks from top to bottom + // of the stack, whereas Stack(IEnumerable) + // pushes to bottom from top, so we need to reverse + // the stack to get them in the right order. + return new Stack(stack.Reverse()); + } + + public static T[] AsArray(this IEnumerable enumerable) + { + T[] array = enumerable as T[]; + + if (array != null) + { + return array; + } + + return enumerable.ToArray(); + } + + public static List AsList(this IEnumerable enumerable) + { + List list = enumerable as List; + + if (list != null) + { + return list; + } + + return enumerable.ToList(); + } + + public static bool IsArrayEqual(this T[] thisArray, T[] thatArray) + { + if (thisArray.Length != thatArray.Length) + { + return false; + } + + for (int i = 0; i < thisArray.Length; i++) + { + if (!thisArray[i].Equals(thatArray[i])) + { + return false; + } + } + + return true; + } + + public static bool IsCollectionEqual(this IList thisList, IList thatList) + { + if (thisList.Count != thatList.Count) + { + return false; + } + + for (int i = 0; i < thisList.Count; i++) + { + if (!thisList[i].Equals(thatList[i])) + { + return false; + } + } + + return true; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Collections/EnumerableCardinality.cs b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Collections/EnumerableCardinality.cs new file mode 100644 index 0000000000..7c03faaa10 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Collections/EnumerableCardinality.cs @@ -0,0 +1,13 @@ +// 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. + +namespace Microsoft.Internal.Collections +{ + internal enum EnumerableCardinality : int + { + Zero = 0, + One = 1, + TwoOrMore = 2, + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Collections/WeakReferenceCollection.cs b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Collections/WeakReferenceCollection.cs new file mode 100644 index 0000000000..e335ec2ad1 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Collections/WeakReferenceCollection.cs @@ -0,0 +1,82 @@ +// 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.Collections.Generic; + +namespace Microsoft.Internal.Collections +{ + internal class WeakReferenceCollection where T : class + { + private readonly List _items = new List(); + + public void Add(T item) + { + // Only cleanup right before we need to reallocate space. + if (_items.Capacity == _items.Count) + { + CleanupDeadReferences(); + } + + _items.Add(new WeakReference(item)); + } + + public void Remove(T item) + { + int index = IndexOf(item); + + if (index != -1) + { + _items.RemoveAt(index); + } + } + + public bool Contains(T item) + { + return IndexOf(item) >= 0; + } + + public void Clear() + { + _items.Clear(); + } + + // Should be executed under at least a read lock. + private int IndexOf(T item) + { + int count = _items.Count; + for (int i = 0; i < count; i++) + { + if (object.ReferenceEquals(_items[i].Target, item)) + { + return i; + } + } + return -1; + } + + // Should be executed under a write lock + private void CleanupDeadReferences() + { + _items.RemoveAll(w => !w.IsAlive); + } + + public List AliveItemsToList() + { + List aliveItems = new List(); + + foreach (var weakItem in _items) + { + T item = weakItem.Target as T; + + if (item != null) + { + aliveItems.Add(item); + } + } + + return aliveItems; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/ContractServices.cs b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/ContractServices.cs new file mode 100644 index 0000000000..16ddb15c33 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/ContractServices.cs @@ -0,0 +1,41 @@ +// 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.ComponentModel.Composition.Primitives; + +namespace Microsoft.Internal +{ + internal static class ContractServices + { + public static bool TryCast(Type contractType, object value, out object result) + { + if (value == null) + { + result = null; + return true; + } + if (contractType.IsInstanceOfType(value)) + { + result = value; + return true; + } + + // We couldn't cast see if a delegate works for us. + if (typeof(Delegate).IsAssignableFrom(contractType)) + { + ExportedDelegate exportedDelegate = value as ExportedDelegate; + if (exportedDelegate != null) + { + result = exportedDelegate.CreateDelegate(contractType.UnderlyingSystemType); + return (result != null); + } + } + + result = null; + return false; + } + } +} + diff --git a/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/GenerationServices.cs b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/GenerationServices.cs new file mode 100644 index 0000000000..3055ce3a51 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/GenerationServices.cs @@ -0,0 +1,327 @@ +// 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.Collections; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; + +namespace Microsoft.Internal +{ + internal static class GenerationServices + { + // Type.GetTypeFromHandle + private static readonly MethodInfo _typeGetTypeFromHandleMethod = typeof(Type).GetMethod("GetTypeFromHandle"); + + // typeofs are pretty expensive, so we cache them statically + private static readonly Type TypeType = typeof(System.Type); + private static readonly Type StringType = typeof(System.String); + private static readonly Type CharType = typeof(System.Char); + private static readonly Type BooleanType = typeof(System.Boolean); + private static readonly Type ByteType = typeof(System.Byte); + private static readonly Type SByteType = typeof(System.SByte); + private static readonly Type Int16Type = typeof(System.Int16); + private static readonly Type UInt16Type = typeof(System.UInt16); + private static readonly Type Int32Type = typeof(System.Int32); + private static readonly Type UInt32Type = typeof(System.UInt32); + private static readonly Type Int64Type = typeof(System.Int64); + private static readonly Type UInt64Type = typeof(System.UInt64); + private static readonly Type DoubleType = typeof(System.Double); + private static readonly Type SingleType = typeof(System.Single); + private static readonly Type IEnumerableTypeofT = typeof(System.Collections.Generic.IEnumerable<>); + private static readonly Type IEnumerableType = typeof(System.Collections.IEnumerable); + + private static readonly MethodInfo ExceptionGetData = typeof(Exception).GetProperty("Data").GetGetMethod(); + private static readonly MethodInfo DictionaryAdd = typeof(IDictionary).GetMethod("Add"); + private static readonly ConstructorInfo ObjectCtor = typeof(object).GetConstructor(Type.EmptyTypes); + + public static ILGenerator CreateGeneratorForPublicConstructor(this TypeBuilder typeBuilder, Type[] ctrArgumentTypes) + { + ConstructorBuilder ctorBuilder = typeBuilder.DefineConstructor( + MethodAttributes.Public, + CallingConventions.Standard, + ctrArgumentTypes); + + ILGenerator ctorIL = ctorBuilder.GetILGenerator(); + ctorIL.Emit(OpCodes.Ldarg_0); + ctorIL.Emit(OpCodes.Call, ObjectCtor); + + return ctorIL; + } + + /// Generates the code that loads the supplied value on the stack + /// This is not as simple as it seems, as different instructions need to be generated depending + /// on its type. + /// We support: + /// 1. All primitive types + /// 2. Strings + /// 3. Enums + /// 4. typeofs + /// 5. nulls + /// 6. Enumerables + /// 7. Delegates on static functions or any of the above + /// Everything else cannot be represented as literals + /// + /// + /// + /// + /// + public static void LoadValue(this ILGenerator ilGenerator, object value) + { + Assumes.NotNull(ilGenerator); + + // + // Get nulls out of the way - they are basically typeless, so we just load null + // + if (value == null) + { + ilGenerator.LoadNull(); + return; + } + + // + // Prepare for literal loading - decide whether we should box, and handle enums properly + // + Type valueType = value.GetType(); + object rawValue = value; + if (valueType.IsEnum) + { + // enums are special - we need to load the underlying constant on the stack + rawValue = Convert.ChangeType(value, Enum.GetUnderlyingType(valueType), null); + valueType = rawValue.GetType(); + } + + // + // Generate IL depending on the valueType - this is messier than it should ever be, but sadly necessary + // + if (valueType == GenerationServices.StringType) + { + // we need to check for strings before enumerables, because strings are IEnumerable + ilGenerator.LoadString((string)rawValue); + } + else if (GenerationServices.TypeType.IsAssignableFrom(valueType)) + { + ilGenerator.LoadTypeOf((Type)rawValue); + } + else if (GenerationServices.IEnumerableType.IsAssignableFrom(valueType)) + { + // NOTE : strings and dictionaries are also enumerables, but we have already handled those + ilGenerator.LoadEnumerable((IEnumerable)rawValue); + } + else if ( + (valueType == GenerationServices.CharType) || + (valueType == GenerationServices.BooleanType) || + (valueType == GenerationServices.ByteType) || + (valueType == GenerationServices.SByteType) || + (valueType == GenerationServices.Int16Type) || + (valueType == GenerationServices.UInt16Type) || + (valueType == GenerationServices.Int32Type) + ) + { + // NOTE : Everything that is 32 bit or less uses ldc.i4. We need to pass int32, even if the actual types is shorter - this is IL memory model + // direct casting to (int) won't work, because the value is boxed, thus we need to use Convert. + // Sadly, this will not work for all cases - namely large uint32 - because they can't semantically fit into 32 signed bits + // We have a special case for that next + ilGenerator.LoadInt((int)Convert.ChangeType(rawValue, typeof(int), CultureInfo.InvariantCulture)); + } + else if (valueType == GenerationServices.UInt32Type) + { + // NOTE : This one is a bit tricky. Ldc.I4 takes an Int32 as an argument, although it really treats it as a 32bit number + // That said, some UInt32 values are larger that Int32.MaxValue, so the Convert call above will fail, which is why + // we need to treat this case individually and cast to uint, and then - unchecked - to int. + ilGenerator.LoadInt(unchecked((int)((uint)rawValue))); + } + else if (valueType == GenerationServices.Int64Type) + { + ilGenerator.LoadLong((long)rawValue); + } + else if (valueType == GenerationServices.UInt64Type) + { + // NOTE : This one is a bit tricky. Ldc.I8 takes an Int64 as an argument, although it really treats it as a 64bit number + // That said, some UInt64 values are larger that Int64.MaxValue, so the direct case we use above (or Convert, for that matter)will fail, which is why + // we need to treat this case individually and cast to ulong, and then - unchecked - to long. + ilGenerator.LoadLong(unchecked((long)((ulong)rawValue))); + } + else if (valueType == GenerationServices.SingleType) + { + ilGenerator.LoadFloat((float)rawValue); + } + else if (valueType == GenerationServices.DoubleType) + { + ilGenerator.LoadDouble((double)rawValue); + } + else + { + throw new InvalidOperationException( + string.Format(CultureInfo.CurrentCulture, SR.InvalidMetadataValue, value.GetType().FullName)); + } + } + + /// Generates the code that adds an object to a dictionary stored in a local variable + /// + /// + /// + /// + /// + public static void AddItemToLocalDictionary(this ILGenerator ilGenerator, LocalBuilder dictionary, object key, object value) + { + Assumes.NotNull(ilGenerator); + Assumes.NotNull(dictionary); + Assumes.NotNull(key); + Assumes.NotNull(value); + + ilGenerator.Emit(OpCodes.Ldloc, dictionary); + ilGenerator.LoadValue(key); + ilGenerator.LoadValue(value); + ilGenerator.Emit(OpCodes.Callvirt, DictionaryAdd); + } + + /// Generates the code that adds an object from a local variable to a dictionary also stored in a local + /// + /// + /// + /// + /// + public static void AddLocalToLocalDictionary(this ILGenerator ilGenerator, LocalBuilder dictionary, object key, LocalBuilder value) + { + Assumes.NotNull(ilGenerator); + Assumes.NotNull(dictionary); + Assumes.NotNull(key); + Assumes.NotNull(value); + + ilGenerator.Emit(OpCodes.Ldloc, dictionary); + ilGenerator.LoadValue(key); + ilGenerator.Emit(OpCodes.Ldloc, value); + ilGenerator.Emit(OpCodes.Callvirt, DictionaryAdd); + } + + /// Generates the code to get the type of an object and store it in a local + /// + /// + /// + /// + /// + public static void GetExceptionDataAndStoreInLocal(this ILGenerator ilGenerator, LocalBuilder exception, LocalBuilder dataStore) + { + Assumes.NotNull(ilGenerator); + Assumes.NotNull(exception); + Assumes.NotNull(dataStore); + + ilGenerator.Emit(OpCodes.Ldloc, exception); + ilGenerator.Emit(OpCodes.Callvirt, ExceptionGetData); + ilGenerator.Emit(OpCodes.Stloc, dataStore); + } + + private static void LoadEnumerable(this ILGenerator ilGenerator, IEnumerable enumerable) + { + Assumes.NotNull(ilGenerator); + Assumes.NotNull(enumerable); + + // We load enumerable as an array - this is the most compact and efficient way of representing it + Type elementType = null; + Type closedType = null; + if (ReflectionServices.TryGetGenericInterfaceType(enumerable.GetType(), GenerationServices.IEnumerableTypeofT, out closedType)) + { + elementType = closedType.GetGenericArguments()[0]; + } + else + { + elementType = typeof(object); + } + + // + // elem[] array = new elem[] + // + Type generatedArrayType = elementType.MakeArrayType(); + LocalBuilder generatedArrayLocal = ilGenerator.DeclareLocal(generatedArrayType); + + ilGenerator.LoadInt(enumerable.Cast().Count()); + ilGenerator.Emit(OpCodes.Newarr, elementType); + ilGenerator.Emit(OpCodes.Stloc, generatedArrayLocal); + + int index = 0; + foreach (object value in enumerable) + { + // + //array[] = value; + // + ilGenerator.Emit(OpCodes.Ldloc, generatedArrayLocal); + ilGenerator.LoadInt(index); + ilGenerator.LoadValue(value); + if (GenerationServices.IsBoxingRequiredForValue(value) && !elementType.IsValueType) + { + ilGenerator.Emit(OpCodes.Box, value.GetType()); + } + ilGenerator.Emit(OpCodes.Stelem, elementType); + index++; + } + + ilGenerator.Emit(OpCodes.Ldloc, generatedArrayLocal); + } + + private static bool IsBoxingRequiredForValue(object value) + { + if (value == null) + { + return false; + } + else + { + return value.GetType().IsValueType; + } + } + + private static void LoadNull(this ILGenerator ilGenerator) + { + ilGenerator.Emit(OpCodes.Ldnull); + } + + private static void LoadString(this ILGenerator ilGenerator, string s) + { + Assumes.NotNull(ilGenerator); + if (s == null) + { + ilGenerator.LoadNull(); + } + else + { + ilGenerator.Emit(OpCodes.Ldstr, s); + } + } + + private static void LoadInt(this ILGenerator ilGenerator, int value) + { + Assumes.NotNull(ilGenerator); + ilGenerator.Emit(OpCodes.Ldc_I4, value); + } + + private static void LoadLong(this ILGenerator ilGenerator, long value) + { + Assumes.NotNull(ilGenerator); + ilGenerator.Emit(OpCodes.Ldc_I8, value); + } + + private static void LoadFloat(this ILGenerator ilGenerator, float value) + { + Assumes.NotNull(ilGenerator); + ilGenerator.Emit(OpCodes.Ldc_R4, value); + } + + private static void LoadDouble(this ILGenerator ilGenerator, double value) + { + Assumes.NotNull(ilGenerator); + ilGenerator.Emit(OpCodes.Ldc_R8, value); + } + + private static void LoadTypeOf(this ILGenerator ilGenerator, Type type) + { + Assumes.NotNull(ilGenerator); + //typeofs() translate into ldtoken and Type::GetTypeFromHandle call + ilGenerator.Emit(OpCodes.Ldtoken, type); + ilGenerator.EmitCall(OpCodes.Call, GenerationServices._typeGetTypeFromHandleMethod, null); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/LazyServices.cs b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/LazyServices.cs new file mode 100644 index 0000000000..7ee721d119 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/LazyServices.cs @@ -0,0 +1,26 @@ +// 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.Globalization; + +namespace Microsoft.Internal +{ + internal static class LazyServices + { + public static T GetNotNullValue(this Lazy lazy, string argument) + where T : class + { + Assumes.NotNull(lazy); + T value = lazy.Value; + if (value == null) + { + throw new InvalidOperationException( + string.Format(CultureInfo.CurrentCulture, SR.LazyServices_LazyResolvesToNull, typeof(T), argument)); + } + + return value; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Lock.Reader.cs b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Lock.Reader.cs new file mode 100644 index 0000000000..cfdf7fbb90 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Lock.Reader.cs @@ -0,0 +1,30 @@ +// 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.Threading; + +namespace Microsoft.Internal +{ + internal struct ReadLock : IDisposable + { + private readonly Lock _lock; + private int _isDisposed; + + public ReadLock(Lock @lock) + { + _isDisposed = 0; + _lock = @lock; + _lock.EnterReadLock(); + } + + public void Dispose() + { + if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0) + { + _lock.ExitReadLock(); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Lock.Writer.cs b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Lock.Writer.cs new file mode 100644 index 0000000000..d1a1e51014 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Lock.Writer.cs @@ -0,0 +1,30 @@ +// 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.Threading; + +namespace Microsoft.Internal +{ + internal struct WriteLock : IDisposable + { + private readonly Lock _lock; + private int _isDisposed; + + public WriteLock(Lock @lock) + { + _isDisposed = 0; + _lock = @lock; + _lock.EnterWriteLock(); + } + + public void Dispose() + { + if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0) + { + _lock.ExitWriteLock(); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Lock.cs b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Lock.cs new file mode 100644 index 0000000000..1b008b4a2b --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Lock.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. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading; + +namespace Microsoft.Internal +{ + internal sealed class Lock : IDisposable + { + private ReaderWriterLockSlim _thisLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); + private int _isDisposed = 0; + public void EnterReadLock() + { + _thisLock.EnterReadLock(); + } + + public void EnterWriteLock() + { + _thisLock.EnterWriteLock(); + } + + public void ExitReadLock() + { + _thisLock.ExitReadLock(); + } + + public void ExitWriteLock() + { + _thisLock.ExitWriteLock(); + } + + public void Dispose() + { + if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0) + { + _thisLock.Dispose(); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/ReflectionInvoke.cs b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/ReflectionInvoke.cs new file mode 100644 index 0000000000..45caab23ae --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/ReflectionInvoke.cs @@ -0,0 +1,37 @@ +// 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.Reflection; + +namespace Microsoft.Internal +{ + internal static class ReflectionInvoke + { + public static object SafeCreateInstance(this Type type, params object[] arguments) + { + return Activator.CreateInstance(type, arguments); + } + + public static object SafeInvoke(this ConstructorInfo constructor, params object[] arguments) + { + return constructor.Invoke(arguments); + } + + public static object SafeInvoke(this MethodInfo method, object instance, params object[] arguments) + { + return method.Invoke(instance, arguments); + } + + public static object SafeGetValue(this FieldInfo field, object instance) + { + return field.GetValue(instance); + } + + public static void SafeSetValue(this FieldInfo field, object instance, object value) + { + field.SetValue(instance, value); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/ReflectionServices.cs b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/ReflectionServices.cs new file mode 100644 index 0000000000..62d2f29d47 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/ReflectionServices.cs @@ -0,0 +1,186 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition; +using System.Linq; +using System.Reflection; + +namespace Microsoft.Internal +{ + internal static class ReflectionServices + { + public static Assembly Assembly(this MemberInfo member) + { + Type type = member as Type; + if (type != null) + { + return type.Assembly; + } + + return member.DeclaringType.Assembly; + } + + public static bool IsVisible(this ConstructorInfo constructor) + { + return constructor.DeclaringType.IsVisible && constructor.IsPublic; + } + + public static bool IsVisible(this FieldInfo field) + { + return field.DeclaringType.IsVisible && field.IsPublic; + } + + public static bool IsVisible(this MethodInfo method) + { + if (!method.DeclaringType.IsVisible) + return false; + + if (!method.IsPublic) + return false; + + if (method.IsGenericMethod) + { + // Check type arguments, for example if we're passed 'Activator.CreateInstance()' + foreach (Type typeArgument in method.GetGenericArguments()) + { + if (!typeArgument.IsVisible) + return false; + } + } + + return true; + } + + public static string GetDisplayName(Type declaringType, string name) + { + Assumes.NotNull(declaringType); + + return declaringType.GetDisplayName() + "." + name; + } + + public static string GetDisplayName(this MemberInfo member) + { + Assumes.NotNull(member); + + switch (member.MemberType) + { + case MemberTypes.TypeInfo: + case MemberTypes.NestedType: + + return AttributedModelServices.GetTypeIdentity(((Type)member)); + } + + return GetDisplayName(member.DeclaringType, member.Name); + } + + internal static bool TryGetGenericInterfaceType(Type instanceType, Type targetOpenInterfaceType, out Type targetClosedInterfaceType) + { + // The interface must be open + Assumes.IsTrue(targetOpenInterfaceType.IsInterface); + Assumes.IsTrue(targetOpenInterfaceType.IsGenericTypeDefinition); + Assumes.IsTrue(!instanceType.IsGenericTypeDefinition); + + // if instanceType is an interface, we must first check it directly + if (instanceType.IsInterface && + instanceType.IsGenericType && + instanceType.UnderlyingSystemType.GetGenericTypeDefinition() == targetOpenInterfaceType.UnderlyingSystemType) + { + targetClosedInterfaceType = instanceType; + return true; + } + + try + { + // Purposefully not using FullName here because it results in a significantly + // more expensive implementation of GetInterface, this does mean that we're + // takign the chance that there aren't too many types which implement multiple + // interfaces by the same name... + Type targetInterface = instanceType.GetInterface(targetOpenInterfaceType.Name, false); + if (targetInterface != null && + targetInterface.UnderlyingSystemType.GetGenericTypeDefinition() == targetOpenInterfaceType.UnderlyingSystemType) + { + targetClosedInterfaceType = targetInterface; + return true; + } + } + catch (AmbiguousMatchException) + { + // If there are multiple with the same name we should not pick any + } + + targetClosedInterfaceType = null; + return false; + } + + internal static IEnumerable GetAllProperties(this Type type) + { + return type.GetInterfaces().Concat(new Type[] { type }).SelectMany(itf => itf.GetProperties()); + } + + internal static IEnumerable GetAllMethods(this Type type) + { + IEnumerable declaredMethods = type.GetDeclaredMethods(); + + Type baseType = type.BaseType; + if (baseType.UnderlyingSystemType != typeof(object)) + { + return declaredMethods.Concat(baseType.GetAllMethods()); + } + else + { + return declaredMethods; + } + } + + private static IEnumerable GetDeclaredMethods(this Type type) + { + foreach (MethodInfo method in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)) + { + yield return method; + } + } + + public static IEnumerable GetAllFields(this Type type) + { + IEnumerable declaredFields = type.GetDeclaredFields(); + + Type baseType = type.BaseType; + if (baseType.UnderlyingSystemType != typeof(object)) + { + return declaredFields.Concat(baseType.GetAllFields()); + } + else + { + return declaredFields; + } + } + + private static IEnumerable GetDeclaredFields(this Type type) + { + foreach (FieldInfo m in type.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)) + { + yield return m; + } + } + + internal static bool HasBaseclassOf(this Type type, Type baseClass) + { + if (type == baseClass) + { + return false; + } + + while (type != null) + { + if (type == baseClass) + return true; + type = type.BaseType; + } + return false; + } + + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Requires.cs b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Requires.cs new file mode 100644 index 0000000000..e9475baa15 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/Requires.cs @@ -0,0 +1,100 @@ +// 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.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Reflection; + +namespace Microsoft.Internal +{ + internal static partial class Requires + { + [DebuggerStepThrough] + public static void NotNullOrNullElements(IEnumerable values, string parameterName) + where T : class + { + NotNull(values, parameterName); + NotNullElements(values, parameterName); + Contract.EndContractBlock(); + } + + [DebuggerStepThrough] + public static void NullOrNotNullElements(IEnumerable> values, string parameterName) + where TKey : class + where TValue : class + { + NotNullElements(values, parameterName); + Contract.EndContractBlock(); + } + + [DebuggerStepThrough] + public static void NullOrNotNullElements(IEnumerable values, string parameterName) + where T : class + { + NotNullElements(values, parameterName); + Contract.EndContractBlock(); + } + + [DebuggerStepThrough] + private static void NotNullElements(IEnumerable values, string parameterName) + where T : class + { + if (values != null && !Contract.ForAll(values, (value) => value != null)) + { + throw ExceptionBuilder.CreateContainsNullElement(parameterName); + } + Contract.EndContractBlock(); + } + + [DebuggerStepThrough] + public static void NullOrNotNullElements(T[] values, string parameterName) + where T : class + { + NotNullElements(values, parameterName); + } + + [DebuggerStepThrough] + private static void NotNullElements(T[] values, string parameterName) + where T : class + { + if (values != null) + { + foreach (var value in values) + { + if (value == null) + { + throw ExceptionBuilder.CreateContainsNullElement(parameterName); + } + } + } + } + + [DebuggerStepThrough] + private static void NotNullElements(IEnumerable> values, string parameterName) + where TKey : class + where TValue : class + { + if (values != null && !Contract.ForAll(values, (keyValue) => keyValue.Key != null && keyValue.Value != null)) + { + throw ExceptionBuilder.CreateContainsNullElement(parameterName); + } + Contract.EndContractBlock(); + } + + [DebuggerStepThrough] + public static void IsInMembertypeSet(MemberTypes value, string parameterName, MemberTypes enumFlagSet) + { + if ((value & enumFlagSet) != value || // Ensure the member is in the set + (value & (value - 1)) != 0) // Ensure that there is only one flag in the value (i.e. value is a power of 2). + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.ArgumentOutOfRange_InvalidEnumInSet, parameterName, value, enumFlagSet.ToString()), parameterName); + } + Contract.EndContractBlock(); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/StringComparers.cs b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/StringComparers.cs new file mode 100644 index 0000000000..91b11f7447 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/Microsoft/Internal/StringComparers.cs @@ -0,0 +1,21 @@ +// 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; + +namespace Microsoft.Internal +{ + internal static class StringComparers + { + public static StringComparer ContractName + { + get { return StringComparer.Ordinal; } + } + + public static StringComparer MetadataKeyNames + { + get { return StringComparer.Ordinal; } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/Properties/InternalsVisibleTo.cs b/external/corefx/src/System.ComponentModel.Composition/src/Properties/InternalsVisibleTo.cs new file mode 100644 index 0000000000..8f8842445c --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/Properties/InternalsVisibleTo.cs @@ -0,0 +1,7 @@ +// 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.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("System.ComponentModel.Composition.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010015c01ae1f50e8cc09ba9eac9147cf8fd9fce2cfe9f8dce4f7301c4132ca9fb50ce8cbf1df4dc18dd4d210e4345c744ecb3365ed327efdbc52603faa5e21daa11234c8c4a73e51f03bf192544581ebe107adee3a34928e39d04e524a9ce729d5090bfd7dad9d10c722c0def9ccc08ff0a03790e48bcd1f9b6c476063e1966a1c4")] diff --git a/external/corefx/src/System.ComponentModel.Composition/src/Resources/Strings.resx b/external/corefx/src/System.ComponentModel.Composition/src/Resources/Strings.resx new file mode 100644 index 0000000000..29339ea396 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/Resources/Strings.resx @@ -0,0 +1,391 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + '{0}' cannot be an empty string (""). + + + The value of argument '{0}' ({1}) is invalid for Enum type '{2}'. + + + The argument was a value type which is not supported. + + + '{0}' is a reflection-only assembly which is not supported. + + + '{0}' cannot contain a null (Nothing in Visual Basic) element. + + + Assembly file {0} is either not found or not a dll or exe file. + + + No exports were found that match the constraint: {0} + + + More than one export was found that matches the constraint: {0} + + + The composition failed because it did not complete within '{0:N0}' iterations. This is most likely caused by a cycle in the dependency graph of a part which is marked with a non-shared creation policy. + + + Cannot cast the underlying exported value of type '{0}' to type '{1}'. + + + Directory '{0}' could not be found. + + + An exception occurred while trying to create an instance of type '{0}'. + + + An exception occurred while trying to get the value of property '{0}'. + + + An exception occurred while calling the 'OnImportsSatisfied' method on type '{0}'. + + + An exception occurred while trying to set the value of property '{0}'. + + + {0} did not originate from the ExportDefinitions property on this ComposablePart or its ComposablePartDefinition. + + + Cannot populate the collection '{0}' because it does not implement ICollection<T> or is read-only. If the collection is not IEnumerable<T> or T[] it must implement ICollection<T> and be either pre-initialized or be writable with a default constructor. + + + Cannot populate the value of enumerable member '{0}' because it is null (Nothing in Visual Basic). If the collection is not IEnumerable<T> or T[] it must implement ICollection<T> and be either pre-initialized or be writable with a default constructor. + + + Cannot compose part '{0}' because a cycle exists in the dependencies between the exports being composed. To break this cycle, consider changing some imports from constructor to property injection. + + + {0} did not originate from the ImportDefinitions property on this ComposablePart or its ComposablePartDefinition. + + + Could not finishing composing object of type '{0}'. The import '{1}' was not satisfied. + + + Cannot set the value of '{0}' because the member is not writable. If the member is a property, it must have an accessible setter; otherwise, if it is a field, it must not be read-only. + + + Internal error occurred. Additional information: '{0}'. + + + The Type '{0}' supplied is not a valid Metadata View. + + + A call to Compose occurred during a call to Compose on the same CompositionContainer object. Use the IsComposing property on CompositionContainer to ensure a composition is not already in progress before calling Compose. + + + This export does not support the metadata item '{0}'. + + + Interface '{0}' is not a valid MetadataView; MetadataViews do not support non-public interfaces, and interfaces that contain members that are not properties. + + + Cannot create an instance of type '{0}' because a constructor could not be selected for construction. Ensure that the type either has a default constructor, or a single constructor marked with the 'System.ComponentModel.Composition.ImportingConstructorAttribute'. + + + The {0} member must be overridden by a derived class. + + + The underlying dictionary is read-only. + + + This property cannot be set after the object's public surface has been accessed. + + + This object has not been initialized - the property '{0}' must be set. + + + The export '{0}' is not assignable to type '{1}'. + + + Cannot get the value of property '{0}', because the member is not readable. The property must have an accessible getter. + + + '{0}' contains a reflection-only type which is not supported. + + + 'definition' cannot be set after Activate has been called because ImportDefinition.IsRecomposable is false. + + + 'exports' cannot be empty when ImportDefinition.ImportCardinality is ImportCardinality.ExactlyOne. + + + 'exports' cannot contain more than one element when ImportDefinition.ImportCardinality is ImportCardinality.ZeroOrOne or ImportCardinality.ExactlyOne. + + + Unknown Origin + + + Cannot activate part '{0}'. + + + Cannot set import '{0}' on part '{1}'. + + + Cannot get export '{0}' from part '{1}'. + + + <Empty> + + + GetExportedValue cannot be called before prerequisite import '{0}' has been set. + + + Resulting in: + + + The composition produced multiple composition errors, with {0:N0} root causes. The root causes are provided below. + + + Review the CompositionException.Errors property for more detailed information. + + + The composition produced a single composition error, with {0:N0} root causes. The root causes are provided below. + + + The composition produced a single composition error. The root cause is provided below. + + + Cannot populate the collection '{0}' because an exception occurred while trying to access the collection value. If the collection is not IEnumerable<T> or T[] it must implement ICollection<T> and be either pre-initialized or be writable with a default constructor. + + + Cannot populate the collection '{0}' because an exception occurred while calling the Add method on the type '{1}'. + + + Cannot populate the collection '{0}' because an exception occurred while calling the Clear method on the type '{1}'. + + + Cannot populate the collection '{0}' because an exception occurred while reading the IsReadOnly property on the type '{1}'. + + + Cannot populate the collection '{0}' because an exception occurred while calling the default constructor on the type '{1}'. + + + The member or parameter '{0}' is marked with multiple Import and ImportMany attributes. Only the first attribute encountered will be respected. + + + Property '{0}' has type '{1}' which is an invalid metadata type. Metadata can only contain values with a type that is available to be embedded at compile-time into attributes. For more details of what types are valid reference section 17.1.3 in the C# specification. + + + Member or Type '{0}' contains multiple metadata entries with the name '{1}'. The metadata entries could be coming from the ExportMetadataAttribute or from a property of a custom metadata attribute. Either remove the duplicate entries or enable the metadata entry with name '{1}' to allow multiple entries via the IsMultiple property on ExportMetadataAttribute or AttributeUsage.AllowMultiple on custom metadata attributes. + + + Member or Type '{0}' contains a metadata entry with the name '{1}', which is a reserved metadata key name. Either remove this metadata entry or change the name associated with the entry. + + + ExportDefinition of type '{0}' cannot be used in this context. Only export definitions produced by the ReflectionModelServices.CreateExportDefinition are supported. + + + Change in exports prevented by non-recomposable import '{0}' on part '{1}'. + + + ImportDefinition of type '{0}' cannot be used in this context. Only import definitions produced by the ReflectionModelServices.CreateImportDefinition are supported. + + + ComposablePartDefinition of type '{0}' cannot be used in this context. Only part definitions produced by the ReflectionModelServices.CreatePartDefinition are supported. + + + The value of argument '{0}' ({1}) is not supported. Allowed values are : '{2}'. + + + ImportDefinition of type '{0}' cannot be used in this context. Only import definitions produced by the ReflectionModelServices.CreateImportDefinition based on members are supported. Use ReflectionModelServices.IsImportingParameter to determine whether a given import definition is based on a member or a parameter. + + + ImportDefinition of type '{0}' cannot be used in this context. Only import definitions produced by the ReflectionModelServices.CreateImportDefinition based on parameters are supported. Use ReflectionModelServices.IsImportingParameter to determine whether a given import definition is based on a member or a parameter. + + + Accessors must not be null (Nothing in Visual Basic). + + + A member of type '{0}' must have exactly a single accessor of type '{0}' + + + All event accessors must be methods. + + + An event must have exactly three accessors. + + + All property accessors must be methods. + + + A property must have exactly two accessors. + + + A member must have at least one accessor. + + + The lazily evaluated value of type '{0}' passed to the ReflectionModelServices API as part of the argument '{1}' must not return null (Nothing in Visual Basic). + + + Metadata can only contain values with a type that is available to be embedded at compile-time into attributes. For more details of what types are valid reference section 17.1.3 in the C# specification. + + + Unable to create an Instance of the Metadata view '{0}' because the exporter exported the metadata for the item '{1}' with the value '{2}' as type '{3}' but the view imports it as type '{4}'. + + + Unable to create an Instance of the Metadata view '{0}' because the exporter exported the metadata for the item '{1}' with a null value and null is not a valid value for type '{2}'. + + + The MetadataView '{0}' is invalid because property '{1}' has a property set method. + + + The composition remains unchanged. The changes were rejected because of the following error(s): {0} + + + The ComposablePart of type '{0}' cannot be recomposed because it is in an invalid state. It can only be recomposed if it has already been fully previewed or composed. + + + The atomicComposition can no longer be changed because the atomicComposition has already been completed. + + + The atomicComposition contains another inner atomicComposition and cannot be changed until the that inner atomicComposition has been completed. + + + The atomicComposition already contains an inner atomicComposition and cannot contain more than one atomicComposition at a time. + + + Currently composing another batch in this ComposablePartExportProvider. Only one batch can be composed at a time. + + + The importing constructor on type '{0}' is using ImportManyAttribute on parameter '{1}' with a non-assignable type. On constructor parameters the ImportManyAttribute only supports importing into types T[] or IEnumerable<T>. + + + Element: {0} + + + --> + + + The ComposablePartDefinition '{0}' has been rejected. {1} + + + The ComposablePartDefinition '{0}' that was previously rejected has been resurrected. + + + The catalog '{0}' could not load assembly '{1}'. {2} + + + The ComposablePartDefinition '{0}' was ignored because it contains no exports. + + + The ComposablePartDefinition '{0}' was ignored because it was marked with PartNotDiscoverableAttribute. + + + Unable to create an instance of the Metadata view '{0}' because a constructor could not be selected. Ensure that the type implements a constructor which takes an argument of type IDictionary<string, object>. + + + ) + + + {0} {1} + + + {0} (Types='{1}'). + + + Import is not valid on an Indexer property. The import '{0}' was not satisfied. + + + Export is not valid on an Indexer property. The export '{0}' was not retrieved. + + + A ReflectionContext must have a default constructor. + + + The type specified in the ReflectionContextDiscoveryAttribute must be assignable to System.Reflection.ReflectionContext. + + + The composable part definition '{0}' was ignored because the export '{1}' has different generic parameters than the part type. + + + 'reflectionContext' must be a type that is assignable from System.Reflection.ReflectionContext. + + + '{0}' returns a mapped type that is a reflection-only type which is not supported. + + + Unable to create an Instance of the Metadata view '{0}' because the implementation class : '{0}' does not implement the MetadataView interface '{1}'. + + + The implementation type for the MetadataView '{0} can not be null. + + + A CreationPolicy of '(0)' can not be applied to an Import that is not an ExportFactory. + + + ExportFactory subclass '{0}' can not have more than two generic parameters. + + + ScopingPolicyCatalog does not support catalog mutation. + + + This CompositionService does not support catalog changes. + + + AtomicComposition encountered an unexpected Exception, review InnerException for details. + + + System.ComponentModel.Composition APIs are not supported on this platform. + + diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System.ComponentModel.Composition.csproj b/external/corefx/src/System.ComponentModel.Composition/src/System.ComponentModel.Composition.csproj new file mode 100644 index 0000000000..a69aa4f1f2 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System.ComponentModel.Composition.csproj @@ -0,0 +1,239 @@ + + + + + {2D694AC8-A12F-4622-9405-74E20EC40C3A} + System.ComponentModel.Composition + SR.PlatformNotSupported_ComponentModel_Composition + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Microsoft\Internal\Assumes.cs + + + Microsoft\Internal\Assumes.InternalErrorException.cs + + + Microsoft\Internal\CommonStrings.Designer.cs + True + True + CommonStrings.resx + + + Microsoft\Internal\Requires.cs + + + + + Microsoft\Internal\CommonStrings.resx + ResXFileCodeGenerator + CommonStrings.Designer.cs + Microsoft.Internal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AdaptationConstants.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AdaptationConstants.cs new file mode 100644 index 0000000000..0416595d81 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AdaptationConstants.cs @@ -0,0 +1,14 @@ +// 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. + +namespace System.ComponentModel.Composition +{ + public static class AdaptationConstants + { + private const string CompositionNamespace = "System.ComponentModel.Composition"; + public const string AdapterContractName = CompositionNamespace + ".AdapterContract"; + public const string AdapterFromContractMetadataName = "FromContract"; + public const string AdapterToContractMetadataName = "ToContract"; + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AttributedModel/AttributedExportDefinition.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AttributedModel/AttributedExportDefinition.cs new file mode 100644 index 0000000000..5c5c14a3cd --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AttributedModel/AttributedExportDefinition.cs @@ -0,0 +1,69 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.ComponentModel.Composition.ReflectionModel; +using System.Reflection; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.AttributedModel +{ + internal class AttributedExportDefinition : ExportDefinition + { + private readonly AttributedPartCreationInfo _partCreationInfo; + private readonly MemberInfo _member; + private readonly ExportAttribute _exportAttribute; + private readonly Type _typeIdentityType; + + private IDictionary _metadata; + + public AttributedExportDefinition(AttributedPartCreationInfo partCreationInfo, MemberInfo member, ExportAttribute exportAttribute, Type typeIdentityType, string contractName) + : base(contractName, (IDictionary)null) + { + Assumes.NotNull(partCreationInfo); + Assumes.NotNull(member); + Assumes.NotNull(exportAttribute); + + _partCreationInfo = partCreationInfo; + _member = member; + _exportAttribute = exportAttribute; + _typeIdentityType = typeIdentityType; + } + + public override IDictionary Metadata + { + get + { + if (_metadata == null) + { + IDictionary metadata; + _member.TryExportMetadataForMember(out metadata); + + string typeIdentity = _exportAttribute.IsContractNameSameAsTypeIdentity() ? + ContractName : + _member.GetTypeIdentityFromExport(_typeIdentityType); + + metadata.Add(CompositionConstants.ExportTypeIdentityMetadataName, typeIdentity); + + var partMetadata = _partCreationInfo.GetMetadata(); + if (partMetadata != null && partMetadata.ContainsKey(CompositionConstants.PartCreationPolicyMetadataName)) + { + metadata.Add(CompositionConstants.PartCreationPolicyMetadataName, partMetadata[CompositionConstants.PartCreationPolicyMetadataName]); + } + + if ((_typeIdentityType != null) && (_member.MemberType != MemberTypes.Method) && _typeIdentityType.ContainsGenericParameters) + { + metadata.Add(CompositionConstants.GenericExportParametersOrderMetadataName, GenericServices.GetGenericParametersOrder(_typeIdentityType)); + } + + _metadata = metadata; + } + return _metadata; + } + } + } + +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AttributedModel/AttributedModelDiscovery.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AttributedModel/AttributedModelDiscovery.cs new file mode 100644 index 0000000000..cfa058064d --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AttributedModel/AttributedModelDiscovery.cs @@ -0,0 +1,169 @@ +// 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.ComponentModel.Composition.Diagnostics; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.ComponentModel.Composition.ReflectionModel; +using System.Globalization; +using System.Reflection; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.AttributedModel +{ + internal static class AttributedModelDiscovery + { + public static ComposablePartDefinition CreatePartDefinitionIfDiscoverable(Type type, ICompositionElement origin) + { + AttributedPartCreationInfo creationInfo = new AttributedPartCreationInfo(type, null, false, origin); + if (!creationInfo.IsPartDiscoverable()) + { + return null; + } + + return new ReflectionComposablePartDefinition(creationInfo); + } + + public static ReflectionComposablePartDefinition CreatePartDefinition(Type type, PartCreationPolicyAttribute partCreationPolicy, bool ignoreConstructorImports, ICompositionElement origin) + { + Assumes.NotNull(type); + + AttributedPartCreationInfo creationInfo = new AttributedPartCreationInfo(type, partCreationPolicy, ignoreConstructorImports, origin); + + return new ReflectionComposablePartDefinition(creationInfo); + } + + public static ReflectionComposablePart CreatePart(object attributedPart) + { + Assumes.NotNull(attributedPart); + + // If given an instance then we want to pass the default composition options because we treat it as a shared part + ReflectionComposablePartDefinition definition = AttributedModelDiscovery.CreatePartDefinition(attributedPart.GetType(), PartCreationPolicyAttribute.Shared, true, (ICompositionElement)null); + + return new ReflectionComposablePart(definition, attributedPart); + } + + public static ReflectionComposablePart CreatePart(object attributedPart, ReflectionContext reflectionContext) + { + Assumes.NotNull(attributedPart); + Assumes.NotNull(reflectionContext); + + // If given an instance then we want to pass the default composition options because we treat it as a shared part + var mappedType = reflectionContext.MapType(IntrospectionExtensions.GetTypeInfo(attributedPart.GetType())); + if (mappedType.Assembly.ReflectionOnly) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.Argument_ReflectionContextReturnsReflectionOnlyType, "reflectionContext"), "reflectionContext"); + } + + ReflectionComposablePartDefinition definition = AttributedModelDiscovery.CreatePartDefinition(mappedType, PartCreationPolicyAttribute.Shared, true, (ICompositionElement)null); + + return CreatePart(definition, attributedPart); + } + + public static ReflectionComposablePart CreatePart(ComposablePartDefinition partDefinition, object attributedPart) + { + Assumes.NotNull(partDefinition); + Assumes.NotNull(attributedPart); + + return new ReflectionComposablePart((ReflectionComposablePartDefinition)partDefinition, attributedPart); + } + + public static ReflectionParameterImportDefinition CreateParameterImportDefinition(ParameterInfo parameter, ICompositionElement origin) + { + Requires.NotNull(parameter, nameof(parameter)); + + ReflectionParameter reflectionParameter = parameter.ToReflectionParameter(); + IAttributedImport attributedImport = AttributedModelDiscovery.GetAttributedImport(reflectionParameter, parameter); + ImportType importType = new ImportType(reflectionParameter.ReturnType, attributedImport.Cardinality); + + if (importType.IsPartCreator) + { + return new PartCreatorParameterImportDefinition( + new Lazy(() => parameter), + origin, + new ContractBasedImportDefinition( + attributedImport.GetContractNameFromImport(importType), + attributedImport.GetTypeIdentityFromImport(importType), + CompositionServices.GetRequiredMetadata(importType.MetadataViewType), + attributedImport.Cardinality, + false, + true, + CreationPolicy.NonShared, + CompositionServices.GetImportMetadata(importType, attributedImport))); + } + else + { + return new ReflectionParameterImportDefinition( + new Lazy(() => parameter), + attributedImport.GetContractNameFromImport(importType), + attributedImport.GetTypeIdentityFromImport(importType), + CompositionServices.GetRequiredMetadata(importType.MetadataViewType), + attributedImport.Cardinality, + attributedImport.RequiredCreationPolicy, + CompositionServices.GetImportMetadata(importType, attributedImport), + origin); + } + } + + public static ReflectionMemberImportDefinition CreateMemberImportDefinition(MemberInfo member, ICompositionElement origin) + { + Requires.NotNull(member, nameof(member)); + + ReflectionWritableMember reflectionMember = member.ToReflectionWritableMember(); + IAttributedImport attributedImport = AttributedModelDiscovery.GetAttributedImport(reflectionMember, member); + ImportType importType = new ImportType(reflectionMember.ReturnType, attributedImport.Cardinality); + + if (importType.IsPartCreator) + { + return new PartCreatorMemberImportDefinition( + new LazyMemberInfo(member), + origin, + new ContractBasedImportDefinition( + attributedImport.GetContractNameFromImport(importType), + attributedImport.GetTypeIdentityFromImport(importType), + CompositionServices.GetRequiredMetadata(importType.MetadataViewType), + attributedImport.Cardinality, + attributedImport.AllowRecomposition, + false, + CreationPolicy.NonShared, + CompositionServices.GetImportMetadata(importType, attributedImport))); + } + else + { + //Does this Import re-export the value if so, make it a rpe-requisite + bool isPrerequisite = member.GetAttributes().Length > 0; + return new ReflectionMemberImportDefinition( + new LazyMemberInfo(member), + attributedImport.GetContractNameFromImport(importType), + attributedImport.GetTypeIdentityFromImport(importType), + CompositionServices.GetRequiredMetadata(importType.MetadataViewType), + attributedImport.Cardinality, + attributedImport.AllowRecomposition, + isPrerequisite, + attributedImport.RequiredCreationPolicy, + CompositionServices.GetImportMetadata(importType, attributedImport), + origin); + } + } + + private static IAttributedImport GetAttributedImport(ReflectionItem item, ICustomAttributeProvider attributeProvider) + { + IAttributedImport[] imports = attributeProvider.GetAttributes(false); + + // For constructor parameters they may not have an ImportAttribute + if (imports.Length == 0) + { + return new ImportAttribute(); + } + + if (imports.Length > 1) + { + CompositionTrace.MemberMarkedWithMultipleImportAndImportMany(item); + } + + // Regardless of how many imports, always return the first one + return imports[0]; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AttributedModel/AttributedPartCreationInfo.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AttributedModel/AttributedPartCreationInfo.cs new file mode 100644 index 0000000000..c93d6a1148 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AttributedModel/AttributedPartCreationInfo.cs @@ -0,0 +1,506 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Diagnostics; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.ComponentModel.Composition.ReflectionModel; +using System.Linq; +using System.Reflection; +using System.Threading; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.AttributedModel +{ + internal class AttributedPartCreationInfo : IReflectionPartCreationInfo + { + private readonly Type _type; + private readonly bool _ignoreConstructorImports = false; + private readonly ICompositionElement _origin; + private PartCreationPolicyAttribute _partCreationPolicy = null; + private ConstructorInfo _constructor; + private IEnumerable _exports; + private IEnumerable _imports; + private HashSet _contractNamesOnNonInterfaces; + + public AttributedPartCreationInfo(Type type, PartCreationPolicyAttribute partCreationPolicy, bool ignoreConstructorImports, ICompositionElement origin) + { + Assumes.NotNull(type); + + _type = type; + _ignoreConstructorImports = ignoreConstructorImports; + _partCreationPolicy = partCreationPolicy; + _origin = origin; + } + + public Type GetPartType() + { + return _type; + } + + public Lazy GetLazyPartType() + { + return new Lazy(GetPartType, LazyThreadSafetyMode.PublicationOnly); + } + + public ConstructorInfo GetConstructor() + { + if (_constructor == null && !_ignoreConstructorImports) + { + _constructor = SelectPartConstructor(_type); + } + return _constructor; + } + + public IDictionary GetMetadata() + { + return _type.GetPartMetadataForType(CreationPolicy); + } + + public IEnumerable GetExports() + { + DiscoverExportsAndImports(); + return _exports; + } + + public IEnumerable GetImports() + { + DiscoverExportsAndImports(); + return _imports; + } + + public bool IsDisposalRequired + { + get + { + return typeof(IDisposable).IsAssignableFrom(GetPartType()); + } + } + + public bool IsIdentityComparison + { + get + { + return true; + } + } + + public bool IsPartDiscoverable() + { + // The part should not be marked with the "NonDiscoverable" + if (_type.IsAttributeDefined()) + { + CompositionTrace.DefinitionMarkedWithPartNotDiscoverableAttribute(_type); + return false; + } + + // The part should have exports + if (!HasExports()) + { + CompositionTrace.DefinitionContainsNoExports(_type); + return false; + } + + // If the part is generic, all exports should have the same number of generic parameters + // (otherwise we have no way to specialize the part based on an export) + if (!AllExportsHaveMatchingArity()) + { + // The function has already reported all violations via tracing + return false; + } + + return true; + } + + private bool HasExports() + { + return GetExportMembers(_type).Any() || + GetInheritedExports(_type).Any(); + } + + private bool AllExportsHaveMatchingArity() + { + bool isArityMatched = true; + if (_type.ContainsGenericParameters) + { + int partGenericArity = _type.GetPureGenericArity(); + + // each member should have the same arity + foreach (MemberInfo member in GetExportMembers(_type).Concat(GetInheritedExports(_type))) + { + if (member.MemberType == MemberTypes.Method) + { + // open generics are unsupported on methods + if (((MethodInfo)member).ContainsGenericParameters) + { + isArityMatched = false; + CompositionTrace.DefinitionMismatchedExportArity(_type, member); + continue; + } + } + + if (member.GetDefaultTypeFromMember().GetPureGenericArity() != partGenericArity) + { + isArityMatched = false; + CompositionTrace.DefinitionMismatchedExportArity(_type, member); + } + } + } + + return isArityMatched; + } + +string ICompositionElement.DisplayName + { + get { return GetDisplayName(); } + } + + ICompositionElement ICompositionElement.Origin + { + get { return _origin; } + } + + public override string ToString() + { + return GetDisplayName(); + } + + private string GetDisplayName() + { + return GetPartType().GetDisplayName(); + } + + private CreationPolicy CreationPolicy + { + get + { + if (_partCreationPolicy == null) + { + _partCreationPolicy = _type.GetFirstAttribute() ?? PartCreationPolicyAttribute.Default; + } + + return _partCreationPolicy.CreationPolicy; + } + } + + private static ConstructorInfo SelectPartConstructor(Type type) + { + Assumes.NotNull(type); + + if (type.IsAbstract) + { + return null; + } + + // Only deal with non-static constructors + BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + + ConstructorInfo[] constructors = type.GetConstructors(flags); + + // Should likely only happen for static or abstract types + if (constructors.Length == 0) + { + return null; + } + + // Optimize single default constructor. + if (constructors.Length == 1 && constructors[0].GetParameters().Length == 0) + { + return constructors[0]; + } + + // Select the marked constructor if there is exactly one marked + ConstructorInfo importingConstructor = null; + ConstructorInfo defaultConstructor = null; + foreach (ConstructorInfo constructor in constructors) + { + // an importing constructor found + if (constructor.IsAttributeDefined()) + { + if (importingConstructor != null) + { + // more that one importing constructor - return null ot error out on creation + return null; + } + else + { + importingConstructor = constructor; + } + } + // otherwise if we havent seen the default constructor yet, check if this one is it + else if (defaultConstructor == null) + { + if (constructor.GetParameters().Length == 0) + { + defaultConstructor = constructor; + } + } + } + + return importingConstructor ?? defaultConstructor; + } + + private void DiscoverExportsAndImports() + { + // NOTE : in most cases both of these will be null or not null at the same time + // the only situation when that is not the case is when there was a failure during the previous discovery + // and one of them ended up not being set. In that case we will force the discovery again so that the same exception is thrown. + if ((_exports != null) && (_imports != null)) + { + return; + } + + _exports = GetExportDefinitions(); + _imports = GetImportDefinitions(); + } + + private IEnumerable GetExportDefinitions() + { + List exports = new List(); + + _contractNamesOnNonInterfaces = new HashSet(); + + // GetExportMembers should only contain the type itself along with the members declared on it, + // it should not contain any base types, members on base types or interfaces on the type. + foreach (MemberInfo member in GetExportMembers(_type)) + { + foreach (ExportAttribute exportAttribute in member.GetAttributes()) + { + var attributedExportDefinition = CreateExportDefinition(member, exportAttribute); + + if (exportAttribute.GetType() == CompositionServices.InheritedExportAttributeType) + { + // Any InheritedExports on the type itself are contributed during this pass + // and we need to do the book keeping for those. + if (!_contractNamesOnNonInterfaces.Contains(attributedExportDefinition.ContractName)) + { + exports.Add(new ReflectionMemberExportDefinition(member.ToLazyMember(), attributedExportDefinition, this)); + _contractNamesOnNonInterfaces.Add(attributedExportDefinition.ContractName); + } + } + else + { + exports.Add(new ReflectionMemberExportDefinition(member.ToLazyMember(), attributedExportDefinition, this)); + } + } + } + + // GetInheritedExports should only contain InheritedExports on base types or interfaces. + // The order of types returned here is important because it is used as a + // priority list of which InhertedExport to choose if multiple exists with + // the same contract name. Therefore ensure that we always return the types + // in the hiearchy from most derived to the lowest base type, followed + // by all the interfaces that this type implements. + foreach (Type type in GetInheritedExports(_type)) + { + foreach (InheritedExportAttribute exportAttribute in type.GetAttributes()) + { + var attributedExportDefinition = CreateExportDefinition(type, exportAttribute); + + if (!_contractNamesOnNonInterfaces.Contains(attributedExportDefinition.ContractName)) + { + exports.Add(new ReflectionMemberExportDefinition(type.ToLazyMember(), attributedExportDefinition, this)); + + if (!type.IsInterface) + { + _contractNamesOnNonInterfaces.Add(attributedExportDefinition.ContractName); + } + } + } + } + + _contractNamesOnNonInterfaces = null; // No need to hold this state around any longer + + return exports; + } + + private AttributedExportDefinition CreateExportDefinition(MemberInfo member, ExportAttribute exportAttribute) + { + string contractName = null; + Type typeIdentityType = null; + member.GetContractInfoFromExport(exportAttribute, out typeIdentityType, out contractName); + + return new AttributedExportDefinition(this, member, exportAttribute, typeIdentityType, contractName); + } + + private IEnumerable GetExportMembers(Type type) + { + BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public | + BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static; + + // If the type is abstract only find local static exports + if (type.IsAbstract) + { + flags &= ~BindingFlags.Instance; + } + else if (IsExport(type)) + { + yield return type; + } + + // Walk the fields + foreach (var member in type.GetFields(flags)) + { + if (IsExport(member)) + { + yield return member; + } + } + + // Walk the properties + foreach (var member in type.GetProperties(flags)) + { + if (IsExport(member)) + { + yield return member; + } + } + + // Walk the methods + foreach (var member in type.GetMethods(flags)) + { + if (IsExport(member)) + { + yield return member; + } + } + } + + private IEnumerable GetInheritedExports(Type type) + { + // If the type is abstract we aren't interested in type level exports + if (type.IsAbstract) + { + yield break; + } + + // The order of types returned here is important because it is used as a + // priority list of which InhertedExport to choose if multiple exists with + // the same contract name. Therefore ensure that we always return the types + // in the hiearchy from most derived to the lowest base type, followed + // by all the interfaces that this type implements. + + Type currentType = type.BaseType; + + if (currentType == null) + { + yield break; + } + + // Stopping at object instead of null to help with performance. It is a noticable performance + // gain (~5%) if we don't have to try and pull the attributes we know don't exist on object. + // We also need the null check in case we're passed a type that doesn't live in the runtime context. + while (currentType != null && currentType.UnderlyingSystemType != CompositionServices.ObjectType) + { + if (IsInheritedExport(currentType)) + { + yield return currentType; + } + currentType = currentType.BaseType; + } + + foreach (Type iface in type.GetInterfaces()) + { + if (IsInheritedExport(iface)) + { + yield return iface; + } + } + } + + private static bool IsExport(ICustomAttributeProvider attributeProvider) + { + return attributeProvider.IsAttributeDefined(false); + } + + private static bool IsInheritedExport(ICustomAttributeProvider attributedProvider) + { + return attributedProvider.IsAttributeDefined(false); + } + + private IEnumerable GetImportDefinitions() + { + List imports = new List(); + + foreach (MemberInfo member in GetImportMembers(_type)) + { + ReflectionMemberImportDefinition importDefinition = AttributedModelDiscovery.CreateMemberImportDefinition(member, this); + imports.Add(importDefinition); + } + + var constructor = GetConstructor(); + + if (constructor != null) + { + foreach (ParameterInfo parameter in constructor.GetParameters()) + { + ReflectionParameterImportDefinition importDefinition = AttributedModelDiscovery.CreateParameterImportDefinition(parameter, this); + imports.Add(importDefinition); + } + } + + return imports; + } + + private IEnumerable GetImportMembers(Type type) + { + if (type.IsAbstract) + { + yield break; + } + + foreach (MemberInfo member in GetDeclaredOnlyImportMembers(type)) + { + yield return member; + } + + // Walk up the type chain until you hit object. + if (type.BaseType != null) + { + Type baseType = type.BaseType; + + // Stopping at object instead of null to help with performance. It is a noticable performance + // gain (~5%) if we don't have to try and pull the attributes we know don't exist on object. + // We also need the null check in case we're passed a type that doesn't live in the runtime context. + while (baseType != null && baseType.UnderlyingSystemType != CompositionServices.ObjectType) + { + foreach (MemberInfo member in GetDeclaredOnlyImportMembers(baseType)) + { + yield return member; + } + baseType = baseType.BaseType; + } + } + } + + private IEnumerable GetDeclaredOnlyImportMembers(Type type) + { + BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + + // Walk the fields + foreach (var member in type.GetFields(flags)) + { + if (IsImport(member)) + { + yield return member; + } + } + + // Walk the properties + foreach (var member in type.GetProperties(flags)) + { + if (IsImport(member)) + { + yield return member; + } + } + } + + private static bool IsImport(ICustomAttributeProvider attributeProvider) + { + return attributeProvider.IsAttributeDefined(false); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AttributedModelServices.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AttributedModelServices.cs new file mode 100644 index 0000000000..b146ab2c8e --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AttributedModelServices.cs @@ -0,0 +1,326 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.AttributedModel; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.ComponentModel.Composition.ReflectionModel; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; +using System.Linq; +using System.Reflection; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition +{ + public static class AttributedModelServices + { + [SuppressMessage("Microsoft.Design", "CA1004")] + public static TMetadataView GetMetadataView(IDictionary metadata) + { + Requires.NotNull(metadata, nameof(metadata)); + Contract.Ensures(Contract.Result() != null); + + return MetadataViewProvider.GetMetadataView(metadata); + } + + public static ComposablePart CreatePart(object attributedPart) + { + Requires.NotNull(attributedPart, nameof(attributedPart)); + Contract.Ensures(Contract.Result() != null); + + return AttributedModelDiscovery.CreatePart(attributedPart); + } + + public static ComposablePart CreatePart(object attributedPart, ReflectionContext reflectionContext) + { + Requires.NotNull(attributedPart, "attributedPart"); + Requires.NotNull(reflectionContext, "reflectionContext"); + Contract.Ensures(Contract.Result() != null); + + return AttributedModelDiscovery.CreatePart(attributedPart, reflectionContext); + } + + public static ComposablePart CreatePart(ComposablePartDefinition partDefinition, object attributedPart) + { + Requires.NotNull(partDefinition, nameof(partDefinition)); + Requires.NotNull(attributedPart, nameof(attributedPart)); + Contract.Ensures(Contract.Result() != null); + + var reflectionComposablePartDefinition = partDefinition as ReflectionComposablePartDefinition; + if(reflectionComposablePartDefinition == null) + { + throw ExceptionBuilder.CreateReflectionModelInvalidPartDefinition("partDefinition", partDefinition.GetType()); + } + + return AttributedModelDiscovery.CreatePart(reflectionComposablePartDefinition, attributedPart); + } + + public static ComposablePartDefinition CreatePartDefinition(Type type, ICompositionElement origin) + { + Requires.NotNull(type, nameof(type)); + Contract.Ensures(Contract.Result() != null); + + return AttributedModelServices.CreatePartDefinition(type, origin, false); + } + + public static ComposablePartDefinition CreatePartDefinition(Type type, ICompositionElement origin, bool ensureIsDiscoverable) + { + Requires.NotNull(type, nameof(type)); + if (ensureIsDiscoverable) + { + return AttributedModelDiscovery.CreatePartDefinitionIfDiscoverable(type, origin); + } + else + { + return AttributedModelDiscovery.CreatePartDefinition(type, null, false, origin); + } + } + + public static string GetTypeIdentity(Type type) + { + Requires.NotNull(type, nameof(type)); + Contract.Ensures(!string.IsNullOrEmpty(Contract.Result())); + + return ContractNameServices.GetTypeIdentity(type); + } + + public static string GetTypeIdentity(MethodInfo method) + { + Requires.NotNull(method, nameof(method)); + Contract.Ensures(!string.IsNullOrEmpty(Contract.Result())); + + return ContractNameServices.GetTypeIdentityFromMethod(method); + } + + public static string GetContractName(Type type) + { + Requires.NotNull(type, nameof(type)); + Contract.Ensures(!string.IsNullOrEmpty(Contract.Result())); + + return AttributedModelServices.GetTypeIdentity(type); + } + + public static ComposablePart AddExportedValue(this CompositionBatch batch, T exportedValue) + { + Requires.NotNull(batch, nameof(batch)); + Contract.Ensures(Contract.Result() != null); + + string contractName = AttributedModelServices.GetContractName(typeof(T)); + + return batch.AddExportedValue(contractName, exportedValue); + } + + public static void ComposeExportedValue(this CompositionContainer container, T exportedValue) + { + Requires.NotNull(container, nameof(container)); + + CompositionBatch batch = new CompositionBatch(); + batch.AddExportedValue(exportedValue); + container.Compose(batch); + } + + public static ComposablePart AddExportedValue(this CompositionBatch batch, string contractName, T exportedValue) + { + Requires.NotNull(batch, nameof(batch)); + Contract.Ensures(Contract.Result() != null); + + string typeIdentity = AttributedModelServices.GetTypeIdentity(typeof(T)); + + IDictionary metadata = new Dictionary(); + metadata.Add(CompositionConstants.ExportTypeIdentityMetadataName, typeIdentity); + + return batch.AddExport(new Export(contractName, metadata, () => exportedValue)); + } + + public static void ComposeExportedValue(this CompositionContainer container, string contractName, T exportedValue) + { + Requires.NotNull(container, nameof(container)); + + CompositionBatch batch = new CompositionBatch(); + batch.AddExportedValue(contractName, exportedValue); + container.Compose(batch); + } + + public static ComposablePart AddPart(this CompositionBatch batch, object attributedPart) + { + Requires.NotNull(batch, nameof(batch)); + Requires.NotNull(attributedPart, nameof(attributedPart)); + Contract.Ensures(Contract.Result() != null); + + ComposablePart part = AttributedModelServices.CreatePart(attributedPart); + + batch.AddPart(part); + + return part; + } + + public static void ComposeParts(this CompositionContainer container, params object[] attributedParts) + { + Requires.NotNull(container, nameof(container)); + Requires.NotNullOrNullElements(attributedParts, "attributedParts"); + + CompositionBatch batch = new CompositionBatch( + attributedParts.Select(attributedPart => AttributedModelServices.CreatePart(attributedPart)).ToArray(), + Enumerable.Empty()); + + container.Compose(batch); + } + + /// + /// Satisfies the imports of the specified attributed object exactly once and they will not + /// ever be recomposed. + /// + /// + /// The attributed object to set the imports. + /// + /// + /// or is . + /// + /// + /// An error occurred during composition. will + /// contain a collection of errors that occurred. + /// + /// + /// The has been disposed of. + /// + public static ComposablePart SatisfyImportsOnce(this ICompositionService compositionService, object attributedPart) + { + Requires.NotNull(compositionService, nameof(compositionService)); + Requires.NotNull(attributedPart, nameof(attributedPart)); + Contract.Ensures(Contract.Result() != null); + + ComposablePart part = AttributedModelServices.CreatePart(attributedPart); + compositionService.SatisfyImportsOnce(part); + + return part; + } + + /// + /// Satisfies the imports of the specified attributed object exactly once and they will not + /// ever be recomposed. + /// + /// + /// The attributed object to set the imports. + /// + /// + /// or or is . + /// + /// + /// An error occurred during composition. will + /// contain a collection of errors that occurred. + /// + /// + /// The has been disposed of. + /// + public static ComposablePart SatisfyImportsOnce(this ICompositionService compositionService, object attributedPart, ReflectionContext reflectionContext) + { + Requires.NotNull(compositionService, "compositionService"); + Requires.NotNull(attributedPart, "attributedPart"); + Requires.NotNull(reflectionContext, "reflectionContext"); + Contract.Ensures(Contract.Result() != null); + + ComposablePart part = AttributedModelServices.CreatePart(attributedPart, reflectionContext); + compositionService.SatisfyImportsOnce(part); + + return part; + } + + /// + /// Determines whether the specified part exports the specified contract. + /// + /// The part. + /// Type of the contract. + /// + /// true if the specified part exports the specified contract; otherwise, false. + /// + public static bool Exports(this ComposablePartDefinition part, Type contractType) + { + Requires.NotNull(part, nameof(part)); + Requires.NotNull(contractType, nameof(contractType)); + + return part.Exports(AttributedModelServices.GetContractName(contractType)); + } + + /// + /// Determines whether the specified part exports the specified contract. + /// + /// Type of the contract. + /// The part. + /// + /// true if the specified part exports the specified contract; otherwise, false. + /// + public static bool Exports(this ComposablePartDefinition part) + { + Requires.NotNull(part, nameof(part)); + + return part.Exports(typeof(T)); + } + + /// + /// Determines whether the specified part imports the specified contract. + /// + /// The part. + /// Type of the contract. + /// + /// true if the specified part imports the specified contract; otherwise, false. + /// + public static bool Imports(this ComposablePartDefinition part, Type contractType) + { + Requires.NotNull(part, nameof(part)); + Requires.NotNull(contractType, nameof(contractType)); + + return part.Imports(AttributedModelServices.GetContractName(contractType)); + } + + /// + /// Determines whether the specified part imports the specified contract. + /// + /// The part. + /// Type of the contract. + /// + /// true if the specified part imports the specified contract; otherwise, false. + /// + public static bool Imports(this ComposablePartDefinition part) + { + Requires.NotNull(part, nameof(part)); + + return part.Imports(typeof(T)); + } + + /// + /// Determines whether the specified part imports the specified contract with the given cardinality. + /// + /// The part. + /// Type of the contract. + /// The import cardinality. + /// + /// true if the specified part imports the specified contract with the given cardinality; otherwise, false. + /// + public static bool Imports(this ComposablePartDefinition part, Type contractType, ImportCardinality importCardinality) + { + Requires.NotNull(part, nameof(part)); + Requires.NotNull(contractType, nameof(contractType)); + + return part.Imports(AttributedModelServices.GetContractName(contractType), importCardinality); + } + + /// + /// Determines whether the specified part imports the specified contract with the given cardinality. + /// + /// The part. + /// Type of the contract. + /// The import cardinality. + /// + /// true if the specified part imports the specified contract with the given cardinality; otherwise, false. + /// + public static bool Imports(this ComposablePartDefinition part, ImportCardinality importCardinality) + { + Requires.NotNull(part, nameof(part)); + + return part.Imports(typeof(T), importCardinality); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CatalogReflectionContextAttribute.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CatalogReflectionContextAttribute.cs new file mode 100644 index 0000000000..b10c5a419d --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CatalogReflectionContextAttribute.cs @@ -0,0 +1,56 @@ +// 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.Diagnostics.CodeAnalysis; +using System.Reflection; +using Microsoft.Internal; + +using PermissionSet = System.Security.PermissionSet; +using PermissionState = System.Security.Permissions.PermissionState; + +namespace System.ComponentModel.Composition +{ + /// + /// Enables the AssemblyCatalog to discover user provided ReflectionContexts. + /// + [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes")] + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false,Inherited = true)] + public class CatalogReflectionContextAttribute : Attribute + { + Type _reflectionContextType; + + public CatalogReflectionContextAttribute(Type reflectionContextType) + { + Requires.NotNull(reflectionContextType, "reflectionContextType"); + + _reflectionContextType = reflectionContextType; + } + + public ReflectionContext CreateReflectionContext() + { + Assumes.NotNull(_reflectionContextType); + + if (!_reflectionContextType.IsPublic) + { + new PermissionSet(PermissionState.Unrestricted).Demand(); + } + + ReflectionContext reflectionContext = null; + try + { + reflectionContext = (ReflectionContext)Activator.CreateInstance(_reflectionContextType); + } + catch (InvalidCastException invalidCastException) + { + throw new InvalidOperationException(SR.ReflectionContext_Type_Required, invalidCastException); + } + catch (MissingMethodException missingMethodException) + { + throw new MissingMethodException(SR.ReflectionContext_Requires_DefaultConstructor, missingMethodException); + } + + return reflectionContext; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ChangeRejectedException.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ChangeRejectedException.cs new file mode 100644 index 0000000000..91b4a3cbe7 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ChangeRejectedException.cs @@ -0,0 +1,66 @@ +// 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.Collections.Generic; +using System.Globalization; + +namespace System.ComponentModel.Composition +{ + /// + /// The exception that is thrown when one or more recoverable errors occur during + /// composition which results in those changes being rejected. + /// + public class ChangeRejectedException : CompositionException + { + /// + /// Initializes a new instance of the class. + /// + public ChangeRejectedException() + : this((string)null, (Exception)null) + { + } + + /// + /// Initializes a new instance of the class. + /// + public ChangeRejectedException(string message) + : this(message, (Exception)null) + { + } + + /// + /// Initializes a new instance of the class. + /// + public ChangeRejectedException(string message, Exception innerException) + : base(message, innerException, (IEnumerable)null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// List of errors that occured while applying the changes. + public ChangeRejectedException(IEnumerable errors) + : base((string)null, (Exception)null, errors) + { + } + + /// + /// Gets a message that describes the exception. + /// + /// + /// A containing a message that describes the + /// . + /// + public override string Message + { + get + { + return string.Format(CultureInfo.CurrentCulture, + SR.CompositionException_ChangesRejected, + base.Message); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionContractMismatchException.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionContractMismatchException.cs new file mode 100644 index 0000000000..3771f9d226 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionContractMismatchException.cs @@ -0,0 +1,85 @@ +// 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.Runtime.Serialization; + +namespace System.ComponentModel.Composition +{ + /// + /// The exception that is thrown when the underlying exported value or metadata of an + /// or object cannot be + /// cast to T or TMetadataView, respectively. + /// + [Serializable] + public class CompositionContractMismatchException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public CompositionContractMismatchException() + : this((string)null, (Exception)null) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified error message. + /// + /// + /// A containing a message that describes the + /// ; or to set + /// the property to its default value. + /// + public CompositionContractMismatchException(string message) + : this(message, (Exception)null) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified error message and exception that is the cause of the + /// exception. + /// + /// + /// A containing a message that describes the + /// ; or to set + /// the property to its default value. + /// + /// + /// The that is the underlying cause of the + /// ; or to set + /// the property to . + /// + public CompositionContractMismatchException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified serialization data. + /// + /// + /// The that holds the serialized object data about the + /// . + /// + /// + /// The that contains contextual information about the + /// source or destination. + /// + /// + /// is . + /// + /// + /// is missing a required value. + /// + /// + /// contains a value that cannot be cast to the correct type. + /// + protected CompositionContractMismatchException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionError.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionError.cs new file mode 100644 index 0000000000..167951932b --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionError.cs @@ -0,0 +1,184 @@ +// 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.ComponentModel.Composition.Primitives; +using System.Diagnostics; +using System.Globalization; + +namespace System.ComponentModel.Composition +{ + /// + /// Represents an error that occurs during composition. + /// + [DebuggerTypeProxy(typeof(CompositionErrorDebuggerProxy))] + public class CompositionError + { + private readonly CompositionErrorId _id; + private readonly string _description; + private readonly Exception _exception; + + private readonly ICompositionElement _element; + + /// + /// Initializes a new instance of the class + /// with the specified error message. + /// + /// + /// A containing a message that describes the + /// ; or to set the + /// property to an empty string (""). + /// + public CompositionError(string message) + : this(CompositionErrorId.Unknown, message, (ICompositionElement)null, (Exception)null) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified error message and composition element that is the + /// cause of the composition error. + /// + /// + /// The that is the cause of the + /// ; or to set + /// the property to + /// . + /// + /// + /// A containing a message that describes the + /// ; or to set the + /// property to an empty string (""). + /// + public CompositionError(string message, ICompositionElement element) + : this(CompositionErrorId.Unknown, message, element, (Exception)null) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified error message and exception that is the cause of the + /// composition error. + /// + /// + /// A containing a message that describes the + /// ; or to set the + /// property to an empty string (""). + /// + /// + /// The that is the underlying cause of the + /// ; or to set + /// the property to . + /// + public CompositionError(string message, Exception exception) + : this(CompositionErrorId.Unknown, message, (ICompositionElement)null, exception) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified error message, and composition element and exception that + /// is the cause of the composition error. + /// + /// + /// A containing a message that describes the + /// ; or to set the + /// property to an empty string (""). + /// + /// + /// The that is the cause of the + /// ; or to set + /// the property to + /// . + /// + /// + /// The that is the underlying cause of the + /// ; or to set + /// the property to . + /// + public CompositionError(string message, ICompositionElement element, Exception exception) + : this(CompositionErrorId.Unknown, message, element, exception) + { + } + + internal CompositionError(CompositionErrorId id, string description, ICompositionElement element, Exception exception) + { + _id = id; + _description = description ?? string.Empty; + _element = element; + _exception = exception; + } + + /// + /// Gets the composition element that is the cause of the error. + /// + /// + /// The that is the cause of the + /// . The default is . + /// + public ICompositionElement Element + { + get { return _element; } + } + + /// + /// Gets the message that describes the composition error. + /// + /// + /// A containing a message that describes the + /// . + /// + public string Description + { + get { return _description; } + } + + /// + /// Gets the exception that is the underlying cause of the composition error. + /// + /// + /// The that is the underlying cause of the + /// . The default is . + /// + public Exception Exception + { + get { return _exception; } + } + + internal CompositionErrorId Id + { + get { return _id; } + } + + internal Exception InnerException + { + get { return Exception; } + } + + /// + /// Returns a string representation of the composition error. + /// + /// + /// A containing the property. + /// + public override string ToString() + { + return Description; + } + + internal static CompositionError Create(CompositionErrorId id, string format, params object[] parameters) + { + return Create(id, (ICompositionElement)null, (Exception)null, format, parameters); + } + + internal static CompositionError Create(CompositionErrorId id, ICompositionElement element, string format, params object[] parameters) + { + return Create(id, element, (Exception)null, format, parameters); + } + + internal static CompositionError Create(CompositionErrorId id, ICompositionElement element, Exception exception, string format, params object[] parameters) + { + return new CompositionError(id, string.Format(CultureInfo.CurrentCulture, format, parameters), element, exception); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionErrorDebuggerProxy.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionErrorDebuggerProxy.cs new file mode 100644 index 0000000000..b88721db35 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionErrorDebuggerProxy.cs @@ -0,0 +1,36 @@ +// 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.ComponentModel.Composition.Primitives; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition +{ + internal class CompositionErrorDebuggerProxy + { + private readonly CompositionError _error; + + public CompositionErrorDebuggerProxy(CompositionError error) + { + Requires.NotNull(error, nameof(error)); + + _error = error; + } + + public string Description + { + get { return _error.Description; } + } + + public Exception Exception + { + get { return _error.Exception; } + } + + public ICompositionElement Element + { + get { return _error.Element; } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionErrorId.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionErrorId.cs new file mode 100644 index 0000000000..25377d5abe --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionErrorId.cs @@ -0,0 +1,31 @@ +// 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. + +namespace System.ComponentModel.Composition +{ + internal enum CompositionErrorId : int + { + Unknown = 0, + InvalidExportMetadata, + ImportNotSetOnPart, + ImportEngine_ComposeTookTooManyIterations, + ImportEngine_ImportCardinalityMismatch, + ImportEngine_PartCycle, + ImportEngine_PartCannotSetImport, + ImportEngine_PartCannotGetExportedValue, + ImportEngine_PartCannotActivate, + ImportEngine_PreventedByExistingImport, + ImportEngine_InvalidStateForRecomposition, + ReflectionModel_ImportThrewException, + ReflectionModel_ImportNotAssignableFromExport, + ReflectionModel_ImportCollectionNull, + ReflectionModel_ImportCollectionNotWritable, + ReflectionModel_ImportCollectionConstructionThrewException, + ReflectionModel_ImportCollectionGetThrewException, + ReflectionModel_ImportCollectionIsReadOnlyThrewException, + ReflectionModel_ImportCollectionClearThrewException, + ReflectionModel_ImportCollectionAddThrewException, + ReflectionModel_ImportManyOnParameterCanOnlyBeAssigned, + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionException.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionException.cs new file mode 100644 index 0000000000..05dd9e7113 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionException.cs @@ -0,0 +1,330 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using Microsoft.Internal; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition +{ + + /// + /// The exception that is thrown when one or more errors occur during composition. + /// + [DebuggerTypeProxy(typeof(CompositionExceptionDebuggerProxy))] + [DebuggerDisplay("{Message}")] + public class CompositionException : Exception + { + private const string ErrorsKey = "Errors"; + private ReadOnlyCollection _errors; + + /// + /// Initializes a new instance of the class. + /// + public CompositionException() + : this((string)null, (Exception)null, (IEnumerable)null) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified error message. + /// + /// + /// A containing a message that describes the + /// ; or to set + /// the property to its default value. + /// + public CompositionException(string message) + : this(message, (Exception)null, (IEnumerable)null) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified error message and exception that is the cause of the + /// exception. + /// + /// + /// A containing a message that describes the + /// ; or to set + /// the property to its default value. + /// + /// + /// The that is the underlying cause of the + /// ; or to set + /// the property to . + /// + public CompositionException(string message, Exception innerException) + : this(message, innerException, (IEnumerable)null) + { + } + + internal CompositionException(CompositionError error) + : this(new CompositionError[] { error }) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified errors. + /// + /// + /// An of objects + /// representing the errors that are the cause of the + /// ; or to set the + /// property to an empty . + /// + /// + /// contains an element that is . + /// + public CompositionException(IEnumerable errors) + : this((string)null, (Exception)null, errors) + { + } + + internal CompositionException(string message, Exception innerException, IEnumerable errors) + : base(message, innerException) + { + Requires.NullOrNotNullElements(errors, "errors"); + _errors = new ReadOnlyCollection(errors == null ? new CompositionError[0] : errors.ToArray()); + } + + /// + /// Gets the errors that are the cause of the exception. + /// + /// + /// An of objects + /// representing the errors that are the cause of the + /// . + /// + public ReadOnlyCollection Errors + { + get { return _errors; } + } + + /// + /// Gets a message that describes the exception. + /// + /// + /// A containing a message that describes the + /// . + /// + public override string Message + { + get + { + if (Errors.Count == 0) + { // If there are no errors, then we simply return base.Message, + // which will either use the default Exception message, or if + // one was specified; the user supplied message. + + return base.Message; + } + + return BuildDefaultMessage(); + } + } + + public ReadOnlyCollection RootCauses + { + get + { + var errors = new List(); + + // In here return a collection of all of the exceptions in the Errors collection + foreach (var error in Errors) + { + if (error.Exception != null) + { + var ce = error.Exception as CompositionException; + if (ce != null) + { + if (ce.RootCauses.Count > 0) + { + errors.AddRange(ce.RootCauses); + continue; + } + } + errors.Add(error.Exception); + } + } + return errors.ToReadOnlyCollection(); + } + } + + private string BuildDefaultMessage() + { + IEnumerable> paths = CalculatePaths(this); + + StringBuilder writer = new StringBuilder(); + + WriteHeader(writer, Errors.Count, paths.Count()); + WritePaths(writer, paths); + + return writer.ToString(); + } + + private static void WriteHeader(StringBuilder writer, int errorsCount, int pathCount) + { + if (errorsCount > 1 && pathCount > 1) + { + // The composition produced multiple composition errors, with {0} root causes. The root causes are provided below. + writer.AppendFormat( + CultureInfo.CurrentCulture, + SR.CompositionException_MultipleErrorsWithMultiplePaths, + pathCount); + } + else if (errorsCount == 1 && pathCount > 1) + { + // The composition produced a single composition error, with {0} root causes. The root causes are provided below. + writer.AppendFormat( + CultureInfo.CurrentCulture, + SR.CompositionException_SingleErrorWithMultiplePaths, + pathCount); + } + else + { + Assumes.IsTrue(errorsCount == 1); + Assumes.IsTrue(pathCount == 1); + + // The composition produced a single composition error. The root cause is provided below. + writer.AppendFormat( + CultureInfo.CurrentCulture, + SR.CompositionException_SingleErrorWithSinglePath, + pathCount); + } + + writer.Append(' '); + writer.AppendLine(SR.CompositionException_ReviewErrorProperty); + } + + private static void WritePaths(StringBuilder writer, IEnumerable> paths) + { + int ordinal = 0; + foreach (IEnumerable path in paths) + { + ordinal++; + WritePath(writer, path, ordinal); + } + } + + private static void WritePath(StringBuilder writer, IEnumerable path, int ordinal) + { + writer.AppendLine(); + writer.Append(ordinal.ToString(CultureInfo.CurrentCulture)); + writer.Append(SR.CompositionException_PathsCountSeparator); + writer.Append(' '); + + WriteError(writer, path.First()); + + foreach (CompositionError error in path.Skip(1)) + { + writer.AppendLine(); + writer.Append(SR.CompositionException_ErrorPrefix); + writer.Append(' '); + WriteError(writer, error); + } + } + + private static void WriteError(StringBuilder writer, CompositionError error) + { + writer.AppendLine(error.Description); + + if (error.Element != null) + { + WriteElementGraph(writer, error.Element); + } + } + + private static void WriteElementGraph(StringBuilder writer, ICompositionElement element) + { + // Writes the composition element and its origins in the format: + // Element: Export --> Part --> PartDefinition --> Catalog + + writer.AppendFormat(CultureInfo.CurrentCulture, SR.CompositionException_ElementPrefix, element.DisplayName); + + while ((element = element.Origin) != null) + { + writer.AppendFormat(CultureInfo.CurrentCulture, SR.CompositionException_OriginFormat, SR.CompositionException_OriginSeparator, element.DisplayName); + } + + writer.AppendLine(); + } + + private static IEnumerable> CalculatePaths(CompositionException exception) + { + List> paths = new List>(); + + VisitContext context = new VisitContext(); + context.Path = new Stack(); + context.LeafVisitor = path => + { + // Take a snapshot of the path + paths.Add(path.Copy()); + }; + + VisitCompositionException(exception, context); + + return paths; + } + + private static void VisitCompositionException(CompositionException exception, VisitContext context) + { + foreach (CompositionError error in exception.Errors) + { + VisitError(error, context); + } + + if (exception.InnerException != null) + { + VisitException(exception.InnerException, context); + } + } + + private static void VisitError(CompositionError error, VisitContext context) + { + context.Path.Push(error); + + if (error.Exception == null) + { // This error is a root cause, so write + // out the stack from this point + + context.LeafVisitor(context.Path); + } + else + { + VisitException(error.Exception, context); + } + + context.Path.Pop(); + } + + private static void VisitException(Exception exception, VisitContext context) + { + CompositionException composition = exception as CompositionException; + if (composition != null) + { + VisitCompositionException(composition, context); + } + else + { + VisitError(new CompositionError(exception.Message, exception.InnerException), context); + } + } + + private struct VisitContext + { + public Stack Path; + public Action> LeafVisitor; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionExceptionDebuggerProxy.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionExceptionDebuggerProxy.cs new file mode 100644 index 0000000000..1b0b8bd245 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionExceptionDebuggerProxy.cs @@ -0,0 +1,52 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; + +using Microsoft.Internal; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition +{ + internal class CompositionExceptionDebuggerProxy + { + private readonly CompositionException _exception; + + public CompositionExceptionDebuggerProxy(CompositionException exception) + { + Requires.NotNull(exception, nameof(exception)); + + _exception = exception; + } + + public ReadOnlyCollection Exceptions + { + get + { + var errors = new List(); + + // In here return a collection of all of the exceptions in the Errors collection + foreach (var error in _exception.Errors) + { + if (error.Exception != null) + { + errors.Add(error.Exception); + } + } + return errors.ToReadOnlyCollection(); + } + } + + public string Message + { + get { return _exception.Message; } + } + + public ReadOnlyCollection RootCauses + { + get { return _exception.RootCauses; } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionResult.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionResult.cs new file mode 100644 index 0000000000..566b49cbc9 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionResult.cs @@ -0,0 +1,85 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Hosting; +using System.Linq; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition +{ + internal struct CompositionResult + { + public static readonly CompositionResult SucceededResult = new CompositionResult(); + private readonly IEnumerable _errors; + + public CompositionResult(params CompositionError[] errors) + : this((IEnumerable)errors) + { + } + + public CompositionResult(IEnumerable errors) + { + _errors = errors; + } + + public bool Succeeded + { + get { return _errors == null || !_errors.FastAny(); } + } + + public IEnumerable Errors + { + get { return _errors ?? Enumerable.Empty(); } + } + + public CompositionResult MergeResult(CompositionResult result) + { + if (Succeeded) + { + return result; + } + if (result.Succeeded) + { + return this; + } + return MergeErrors(result._errors); + } + + public CompositionResult MergeError(CompositionError error) + { + return MergeErrors(new CompositionError[] { error }); + } + + public CompositionResult MergeErrors(IEnumerable errors) + { + return new CompositionResult(_errors.ConcatAllowingNull(errors)); + } + + public CompositionResult ToResult(T value) + { + return new CompositionResult(value, _errors); + } + + public void ThrowOnErrors() + { + ThrowOnErrors(null); + } + + public void ThrowOnErrors(AtomicComposition atomicComposition) + { + if (!Succeeded) + { + if (atomicComposition == null) + { + throw new CompositionException(_errors); + } + else + { + throw new ChangeRejectedException(_errors); + } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionResultOfT.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionResultOfT.cs new file mode 100644 index 0000000000..ee1de88cac --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionResultOfT.cs @@ -0,0 +1,78 @@ +// 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.Collections.Generic; +using System.Linq; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition +{ + internal struct CompositionResult + { + private readonly IEnumerable _errors; + private readonly T _value; + + public CompositionResult(T value) + : this(value, (CompositionError[])null) + { + } + + public CompositionResult(params CompositionError[] errors) + : this(default(T), (IEnumerable)errors) + { + } + + public CompositionResult(IEnumerable errors) + : this(default(T), errors) + { + } + + internal CompositionResult(T value, IEnumerable errors) + { + _errors = errors; + _value = value; + } + + public bool Succeeded + { + get { return _errors == null || !_errors.FastAny(); } + } + + public IEnumerable Errors + { + get { return _errors ?? Enumerable.Empty(); } + } + + /// + /// Gets the value from the result, throwing a CompositionException if there are any errors. + /// + public T Value + { + get + { + ThrowOnErrors(); + + return _value; + } + } + + internal CompositionResult ToResult() + { + return new CompositionResult(_errors); + } + + internal CompositionResult ToResult() + { + return new CompositionResult(_errors); + } + + private void ThrowOnErrors() + { + if (!Succeeded) + { + throw new CompositionException(_errors); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ConstraintServices.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ConstraintServices.cs new file mode 100644 index 0000000000..d45dc42f45 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ConstraintServices.cs @@ -0,0 +1,195 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition +{ + internal static class ConstraintServices + { + // NOTE : these are here as Reflection member search is pretty expensive, and we want that to be done once. + // Also, making these static would cause this class to fail loading if we rename members of ExportDefinition. + private static readonly PropertyInfo _exportDefinitionContractNameProperty = typeof(ExportDefinition).GetProperty("ContractName"); + private static readonly PropertyInfo _exportDefinitionMetadataProperty = typeof(ExportDefinition).GetProperty("Metadata"); + private static readonly MethodInfo _metadataContainsKeyMethod = typeof(IDictionary).GetMethod("ContainsKey"); + private static readonly MethodInfo _metadataItemMethod = typeof(IDictionary).GetMethod("get_Item"); + private static readonly MethodInfo _metadataEqualsMethod = typeof(object).GetMethod("Equals", new Type[] { typeof(object) }); + private static readonly MethodInfo _typeIsInstanceOfTypeMethod = typeof(Type).GetMethod("IsInstanceOfType"); + + public static Expression> CreateConstraint(string contractName, string requiredTypeIdentity, IEnumerable> requiredMetadata, CreationPolicy requiredCreationPolicy) + { + ParameterExpression parameter = Expression.Parameter(typeof(ExportDefinition), "exportDefinition"); + + Expression constraintBody = ConstraintServices.CreateContractConstraintBody(contractName, parameter); + + if (!string.IsNullOrEmpty(requiredTypeIdentity)) + { + Expression typeIdentityConstraintBody = ConstraintServices.CreateTypeIdentityContraint(requiredTypeIdentity, parameter); + + constraintBody = Expression.AndAlso(constraintBody, typeIdentityConstraintBody); + } + + if (requiredMetadata != null) + { + Expression metadataConstraintBody = ConstraintServices.CreateMetadataConstraintBody(requiredMetadata, parameter); + if (metadataConstraintBody != null) + { + constraintBody = Expression.AndAlso(constraintBody, metadataConstraintBody); + } + } + + if (requiredCreationPolicy != CreationPolicy.Any) + { + Expression policyConstraintBody = ConstraintServices.CreateCreationPolicyContraint(requiredCreationPolicy, parameter); + + constraintBody = Expression.AndAlso(constraintBody, policyConstraintBody); + } + + Expression> constraint = Expression.Lambda>(constraintBody, parameter); + return constraint; + } + + private static Expression CreateContractConstraintBody(string contractName, ParameterExpression parameter) + { + Assumes.NotNull(parameter); + + // export.ContractName=; + return Expression.Equal( + Expression.Property(parameter, ConstraintServices._exportDefinitionContractNameProperty), + Expression.Constant(contractName ?? string.Empty, typeof(string))); + } + + private static Expression CreateMetadataConstraintBody(IEnumerable> requiredMetadata, ParameterExpression parameter) + { + Assumes.NotNull(requiredMetadata); + Assumes.NotNull(parameter); + + Expression body = null; + foreach (KeyValuePair requiredMetadataItem in requiredMetadata) + { + // export.Metadata.ContainsKey() + Expression metadataItemExpression = CreateMetadataContainsKeyExpression(parameter, requiredMetadataItem.Key); + + body = (body != null) ? Expression.AndAlso(body, metadataItemExpression) : metadataItemExpression; + body = Expression.AndAlso(body, CreateMetadataOfTypeExpression(parameter, requiredMetadataItem.Key, requiredMetadataItem.Value)); + } + + return body; + } + + private static Expression CreateCreationPolicyContraint(CreationPolicy policy, ParameterExpression parameter) + { + Assumes.IsTrue(policy != CreationPolicy.Any); + Assumes.NotNull(parameter); + + // !definition.Metadata.ContainsKey(CompositionConstants.PartCreationPolicyMetadataName) || + // CreationPolicy.Any.Equals(definition.Metadata[CompositionConstants.PartCreationPolicyMetadataName]) || + // policy.Equals(definition.Metadata[CompositionConstants.PartCreationPolicyMetadataName]); + + return Expression.MakeBinary(ExpressionType.OrElse, + Expression.MakeBinary(ExpressionType.OrElse, + Expression.Not(CreateMetadataContainsKeyExpression(parameter, CompositionConstants.PartCreationPolicyMetadataName)), + CreateMetadataValueEqualsExpression(parameter, CreationPolicy.Any, CompositionConstants.PartCreationPolicyMetadataName)), + CreateMetadataValueEqualsExpression(parameter, policy, CompositionConstants.PartCreationPolicyMetadataName)); + } + + private static Expression CreateTypeIdentityContraint(string requiredTypeIdentity, ParameterExpression parameter) + { + Assumes.NotNull(requiredTypeIdentity); + Assumes.NotNull(parameter); + + // definition.Metadata.ContainsKey(CompositionServices.ExportTypeIdentity) && + // requiredTypeIdentity.Equals(definition.Metadata[CompositionConstants.ExportTypeIdentityMetadataName]); + + return Expression.MakeBinary(ExpressionType.AndAlso, + CreateMetadataContainsKeyExpression(parameter, CompositionConstants.ExportTypeIdentityMetadataName), + CreateMetadataValueEqualsExpression(parameter, requiredTypeIdentity, CompositionConstants.ExportTypeIdentityMetadataName)); + } + + private static Expression CreateMetadataContainsKeyExpression(ParameterExpression parameter, string constantKey) + { + Assumes.NotNull(parameter, constantKey); + + // definition.Metadata.ContainsKey(constantKey) + return Expression.Call( + Expression.Property(parameter, ConstraintServices._exportDefinitionMetadataProperty), + ConstraintServices._metadataContainsKeyMethod, + Expression.Constant(constantKey)); + } + + private static Expression CreateMetadataOfTypeExpression(ParameterExpression parameter, string constantKey, Type constantType) + { + Assumes.NotNull(parameter, constantKey); + Assumes.NotNull(parameter, constantType); + + // constantType.IsInstanceOfType(definition.Metadata[constantKey]) + return Expression.Call( + Expression.Constant(constantType, typeof(Type)), + ConstraintServices._typeIsInstanceOfTypeMethod, + Expression.Call( + Expression.Property(parameter, ConstraintServices._exportDefinitionMetadataProperty), + ConstraintServices._metadataItemMethod, + Expression.Constant(constantKey)) + ); + } + + private static Expression CreateMetadataValueEqualsExpression(ParameterExpression parameter, object constantValue, string metadataName) + { + Assumes.NotNull(parameter, constantValue); + + // constantValue.Equals(definition.Metadata[CompositionServices.PartCreationPolicyMetadataName]) + return Expression.Call( + Expression.Constant(constantValue), + ConstraintServices._metadataEqualsMethod, + Expression.Call( + Expression.Property(parameter, ConstraintServices._exportDefinitionMetadataProperty), + ConstraintServices._metadataItemMethod, + Expression.Constant(metadataName))); + } + + public static Expression> CreatePartCreatorConstraint(Expression> baseConstraint, ImportDefinition productImportDefinition) + { + ParameterExpression exportDefinitionParameter = baseConstraint.Parameters[0]; + + // exportDefinition.Metadata + Expression metadataExpression = Expression.Property(exportDefinitionParameter, ConstraintServices._exportDefinitionMetadataProperty); + + // exportDefinition.Metadata.ContainsKey("ProductDefinition") + Expression containsProductExpression = Expression.Call( + metadataExpression, + ConstraintServices._metadataContainsKeyMethod, + Expression.Constant(CompositionConstants.ProductDefinitionMetadataName)); + + // exportDefinition.Metadata["ProductDefinition"] + Expression productExportDefinitionExpression = Expression.Call( + metadataExpression, + ConstraintServices._metadataItemMethod, + Expression.Constant(CompositionConstants.ProductDefinitionMetadataName)); + + // ProductImportDefinition.Contraint((ExportDefinition)exportDefinition.Metadata["ProductDefinition"]) + Expression productMatchExpression = + Expression.Invoke(productImportDefinition.Constraint, + Expression.Convert(productExportDefinitionExpression, typeof(ExportDefinition))); + + // baseContraint(exportDefinition) && + // exportDefinition.Metadata.ContainsKey("ProductDefinition") && + // ProductImportDefinition.Contraint((ExportDefinition)exportDefinition.Metadata["ProductDefinition"]) + Expression> constraint = + Expression.Lambda>( + Expression.AndAlso( + baseConstraint.Body, + Expression.AndAlso( + containsProductExpression, + productMatchExpression)), + exportDefinitionParameter); + + return constraint; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ContractNameServices.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ContractNameServices.cs new file mode 100644 index 0000000000..bbac735bde --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ContractNameServices.cs @@ -0,0 +1,341 @@ +// 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.Collections.Generic; +using System.Reflection; +using System.Text; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition +{ + internal static class ContractNameServices + { + private const char NamespaceSeparator = '.'; + private const char ArrayOpeningBracket = '['; + private const char ArrayClosingBracket = ']'; + private const char ArraySeparator = ','; + private const char PointerSymbol = '*'; + private const char ReferenceSymbol = '&'; + private const char GenericArityBackQuote = '`'; + private const char NestedClassSeparator = '+'; + private const char ContractNameGenericOpeningBracket = '('; + private const char ContractNameGenericClosingBracket = ')'; + private const char ContractNameGenericArgumentSeparator = ','; + private const char CustomModifiersSeparator = ' '; + private const char GenericFormatOpeningBracket = '{'; + private const char GenericFormatClosingBracket = '}'; + + [ThreadStatic] + private static Dictionary typeIdentityCache; + + private static Dictionary TypeIdentityCache + { + get + { + return typeIdentityCache = typeIdentityCache ?? new Dictionary(); + } + } + + internal static string GetTypeIdentity(Type type) + { + return GetTypeIdentity(type, true); + } + + internal static string GetTypeIdentity(Type type, bool formatGenericName) + { + Assumes.NotNull(type); + string typeIdentity = null; + + if (!TypeIdentityCache.TryGetValue(type, out typeIdentity)) + { + if (!type.IsAbstract && type.HasBaseclassOf(typeof(Delegate))) + { + MethodInfo method = type.GetMethod("Invoke"); + typeIdentity = ContractNameServices.GetTypeIdentityFromMethod(method); + } + else if (type.IsGenericParameter) + { + StringBuilder typeIdentityStringBuilder = new StringBuilder(); + WriteTypeArgument(typeIdentityStringBuilder, false, type, formatGenericName); + typeIdentityStringBuilder.Remove(typeIdentityStringBuilder.Length - 1, 1); + typeIdentity = typeIdentityStringBuilder.ToString(); + } + else + { + StringBuilder typeIdentityStringBuilder = new StringBuilder(); + WriteTypeWithNamespace(typeIdentityStringBuilder, type, formatGenericName); + typeIdentity = typeIdentityStringBuilder.ToString(); + } + + Assumes.IsTrue(!string.IsNullOrEmpty(typeIdentity)); + TypeIdentityCache.Add(type, typeIdentity); + } + + return typeIdentity; + } + + internal static string GetTypeIdentityFromMethod(MethodInfo method) + { + return GetTypeIdentityFromMethod(method, true); + } + + internal static string GetTypeIdentityFromMethod(MethodInfo method, bool formatGenericName) + { + StringBuilder methodNameStringBuilder = new StringBuilder(); + + WriteTypeWithNamespace(methodNameStringBuilder, method.ReturnType, formatGenericName); + + methodNameStringBuilder.Append("("); + + ParameterInfo[] parameters = method.GetParameters(); + + for (int i = 0; i < parameters.Length; i++) + { + if (i != 0) + { + methodNameStringBuilder.Append(","); + } + + WriteTypeWithNamespace(methodNameStringBuilder, parameters[i].ParameterType, formatGenericName); + } + methodNameStringBuilder.Append(")"); + + return methodNameStringBuilder.ToString(); + } + + private static void WriteTypeWithNamespace(StringBuilder typeName, Type type, bool formatGenericName) + { + // Writes type with namesapce + if (!string.IsNullOrEmpty(type.Namespace)) + { + typeName.Append(type.Namespace); + typeName.Append(NamespaceSeparator); + } + WriteType(typeName, type, formatGenericName); + } + + private static void WriteType(StringBuilder typeName, Type type, bool formatGenericName) + { + // Writes type name + if (type.IsGenericType) + { + // + // Reflection format stores all the generic arguments (including the ones for parent types) on the leaf type. + // These arguments are placed in a queue and are written out based on generic arity (`X) of each type + // + Queue genericTypeArguments = new Queue(type.GetGenericArguments()); + WriteGenericType(typeName, type, type.IsGenericTypeDefinition, genericTypeArguments, formatGenericName); + Assumes.IsTrue(genericTypeArguments.Count == 0, "Expecting genericTypeArguments queue to be empty."); + } + else + { + WriteNonGenericType(typeName, type, formatGenericName); + } + } + + private static void WriteNonGenericType(StringBuilder typeName, Type type, bool formatGenericName) + { + // + // Writes non-generic type + // + if (type.DeclaringType != null) + { + WriteType(typeName, type.DeclaringType, formatGenericName); + typeName.Append(NestedClassSeparator); + } + if (type.IsArray) + { + WriteArrayType(typeName, type, formatGenericName); + } + else if (type.IsPointer) + { + WritePointerType(typeName, type, formatGenericName); + } + else if (type.IsByRef) + { + WriteByRefType(typeName, type, formatGenericName); + } + else + { + typeName.Append(type.Name); + } + } + + private static void WriteArrayType(StringBuilder typeName, Type type, bool formatGenericName) + { + // + // Writes array type e.g [] + // Note that jagged arrays are stored in reverse order + // e.g. C#: Int32[][,] Reflection: Int32[,][] + // we are following C# order for arrays + // + Type rootElementType = FindArrayElementType(type); + WriteType(typeName, rootElementType, formatGenericName); + Type elementType = type; + do + { + WriteArrayTypeDimensions(typeName, elementType); + } + while ((elementType = elementType.GetElementType()) != null && elementType.IsArray); + } + + private static void WritePointerType(StringBuilder typeName, Type type, bool formatGenericName) + { + // + // Writes pointer type e.g * + // + WriteType(typeName, type.GetElementType(), formatGenericName); + typeName.Append(PointerSymbol); + } + + private static void WriteByRefType(StringBuilder typeName, Type type, bool formatGenericName) + { + // + // Writes by ref type e.g & + // + WriteType(typeName, type.GetElementType(), formatGenericName); + typeName.Append(ReferenceSymbol); + } + + private static void WriteArrayTypeDimensions(StringBuilder typeName, Type type) + { + // + // Writes array type dimensions e.g. [,,] + // + typeName.Append(ArrayOpeningBracket); + int rank = type.GetArrayRank(); + for (int i = 1; i < rank; i++) + { + typeName.Append(ArraySeparator); + } + typeName.Append(ArrayClosingBracket); + } + + private static void WriteGenericType(StringBuilder typeName, Type type, bool isDefinition, Queue genericTypeArguments, bool formatGenericName) + { + // + // Writes generic type including parent generic types + // genericTypeArguments contains type arguments obtained from the most nested type + // isDefinition parameter indicates if we are dealing with generic type definition + // + if (type.DeclaringType != null) + { + if (type.DeclaringType.IsGenericType) + { + WriteGenericType(typeName, type.DeclaringType, isDefinition, genericTypeArguments, formatGenericName); + } + else + { + WriteNonGenericType(typeName, type.DeclaringType, formatGenericName); + } + typeName.Append(NestedClassSeparator); + } + WriteGenericTypeName(typeName, type, isDefinition, genericTypeArguments, formatGenericName); + } + + private static void WriteGenericTypeName(StringBuilder typeName, Type type, bool isDefinition, Queue genericTypeArguments, bool formatGenericName) + { + // + // Writes generic type name, e.g. generic name and generic arguments + // + Assumes.IsTrue(type.IsGenericType, "Expecting type to be a generic type"); + int genericArity = GetGenericArity(type); + string genericTypeName = FindGenericTypeName(type.GetGenericTypeDefinition().Name); + typeName.Append(genericTypeName); + WriteTypeArgumentsString(typeName, genericArity, isDefinition, genericTypeArguments, formatGenericName); + } + + private static void WriteTypeArgumentsString(StringBuilder typeName, int argumentsCount, bool isDefinition, Queue genericTypeArguments, bool formatGenericName) + { + // + // Writes type arguments in brackets, e.g. (, , ...) + // + if (argumentsCount == 0) + { + return; + } + typeName.Append(ContractNameGenericOpeningBracket); + for (int i = 0; i < argumentsCount; i++) + { + Assumes.IsTrue(genericTypeArguments.Count > 0, "Expecting genericTypeArguments to contain at least one Type"); + Type genericTypeArgument = genericTypeArguments.Dequeue(); + WriteTypeArgument(typeName, isDefinition, genericTypeArgument, formatGenericName); + } + typeName.Remove(typeName.Length - 1, 1); + typeName.Append(ContractNameGenericClosingBracket); + } + + private static void WriteTypeArgument(StringBuilder typeName, bool isDefinition, Type genericTypeArgument, bool formatGenericName) + { + if (!isDefinition && !genericTypeArgument.IsGenericParameter) + { + WriteTypeWithNamespace(typeName, genericTypeArgument, formatGenericName); + } + + if (formatGenericName && genericTypeArgument.IsGenericParameter) + { + typeName.Append(GenericFormatOpeningBracket); + typeName.Append(genericTypeArgument.GenericParameterPosition); + typeName.Append(GenericFormatClosingBracket); + } + typeName.Append(ContractNameGenericArgumentSeparator); + } + + //internal for testability + internal static void WriteCustomModifiers(StringBuilder typeName, string customKeyword, Type[] types, bool formatGenericName) + { + // + // Writes custom modifiers in the format: customKeyword(,,...) + // + typeName.Append(CustomModifiersSeparator); + typeName.Append(customKeyword); + Queue typeArguments = new Queue(types); + WriteTypeArgumentsString(typeName, types.Length, false, typeArguments, formatGenericName); + Assumes.IsTrue(typeArguments.Count == 0, "Expecting genericTypeArguments queue to be empty."); + } + + private static Type FindArrayElementType(Type type) + { + // + // Gets array element type by calling GetElementType() until the element is not an array + // + Type elementType = type; + while ((elementType = elementType.GetElementType()) != null && elementType.IsArray) { } + return elementType; + } + + private static string FindGenericTypeName(string genericName) + { + // + // Gets generic type name omitting the backquote and arity indicator + // List`1 -> List + // Arity indicator is returned as output parameter + // + int indexOfBackQuote = genericName.IndexOf(GenericArityBackQuote); + if (indexOfBackQuote > -1) + { + genericName = genericName.Substring(0, indexOfBackQuote); + } + return genericName; + } + + private static int GetGenericArity(Type type) + { + if (type.DeclaringType == null) + { + return type.GetGenericArguments().Length; + } + + // The generic arity is equal to the difference in the number of generic arguments + // from the type and the declaring type. + + int delclaringTypeGenericArguments = type.DeclaringType.GetGenericArguments().Length; + int typeGenericArguments = type.GetGenericArguments().Length; + + Assumes.IsTrue(typeGenericArguments >= delclaringTypeGenericArguments); + + return typeGenericArguments - delclaringTypeGenericArguments; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CreationPolicy.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CreationPolicy.cs new file mode 100644 index 0000000000..4e3334cd3b --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CreationPolicy.cs @@ -0,0 +1,33 @@ +// 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. + +namespace System.ComponentModel.Composition +{ + /// + /// Option placed on a type that controls when the creates + /// a new instance of a . + /// + public enum CreationPolicy : int + { + /// + /// Let the choose the most appropriate + /// for the part given the current context. This is the default , with + /// the choosing by default + /// unless the or importer requests . + /// + Any = 0, + + /// + /// A single shared instance of the associated will be created + /// by the and shared by all requestors. + /// + Shared = 1, + + /// + /// A new non-shared instance of the associated will be created + /// by the for every requestor. + /// + NonShared = 2, + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Diagnostics/CompositionTrace.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Diagnostics/CompositionTrace.cs new file mode 100644 index 0000000000..e387f85112 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Diagnostics/CompositionTrace.cs @@ -0,0 +1,104 @@ +// 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.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.ComponentModel.Composition.ReflectionModel; +using System.Reflection; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Diagnostics +{ + internal static class CompositionTrace + { + internal static void PartDefinitionResurrected(ComposablePartDefinition definition) + { + Assumes.NotNull(definition); + + if (CompositionTraceSource.CanWriteInformation) + { + CompositionTraceSource.WriteInformation(CompositionTraceId.Rejection_DefinitionResurrected, + SR.CompositionTrace_Rejection_DefinitionResurrected, + definition.GetDisplayName()); + } + } + + internal static void PartDefinitionRejected(ComposablePartDefinition definition, ChangeRejectedException exception) + { + Assumes.NotNull(definition, exception); + + if (CompositionTraceSource.CanWriteWarning) + { + CompositionTraceSource.WriteWarning(CompositionTraceId.Rejection_DefinitionRejected, + SR.CompositionTrace_Rejection_DefinitionRejected, + definition.GetDisplayName(), + exception.Message); + } + } + + internal static void AssemblyLoadFailed(DirectoryCatalog catalog, string fileName, Exception exception) + { + Assumes.NotNull(catalog, exception); + Assumes.NotNullOrEmpty(fileName); + + if (CompositionTraceSource.CanWriteWarning) + { + CompositionTraceSource.WriteWarning(CompositionTraceId.Discovery_AssemblyLoadFailed, + SR.CompositionTrace_Discovery_AssemblyLoadFailed, + catalog.GetDisplayName(), + fileName, + exception.Message); + } + } + + internal static void DefinitionMarkedWithPartNotDiscoverableAttribute(Type type) + { + Assumes.NotNull(type); + + if (CompositionTraceSource.CanWriteInformation) + { + CompositionTraceSource.WriteInformation(CompositionTraceId.Discovery_DefinitionMarkedWithPartNotDiscoverableAttribute, + SR.CompositionTrace_Discovery_DefinitionMarkedWithPartNotDiscoverableAttribute, + type.GetDisplayName()); + } + } + + internal static void DefinitionMismatchedExportArity(Type type, MemberInfo member) + { + Assumes.NotNull(type); + Assumes.NotNull(member); + + if (CompositionTraceSource.CanWriteInformation) + { + CompositionTraceSource.WriteInformation(CompositionTraceId.Discovery_DefinitionMismatchedExportArity, + SR.CompositionTrace_Discovery_DefinitionMismatchedExportArity, + type.GetDisplayName(), member.GetDisplayName()); + } + } + + internal static void DefinitionContainsNoExports(Type type) + { + Assumes.NotNull(type); + + if (CompositionTraceSource.CanWriteInformation) + { + CompositionTraceSource.WriteInformation(CompositionTraceId.Discovery_DefinitionContainsNoExports, + SR.CompositionTrace_Discovery_DefinitionContainsNoExports, + type.GetDisplayName()); + } + } + + internal static void MemberMarkedWithMultipleImportAndImportMany(ReflectionItem item) + { + Assumes.NotNull(item); + + if (CompositionTraceSource.CanWriteError) + { + CompositionTraceSource.WriteError(CompositionTraceId.Discovery_MemberMarkedWithMultipleImportAndImportMany, + SR.CompositionTrace_Discovery_MemberMarkedWithMultipleImportAndImportMany, + item.GetDisplayName()); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Diagnostics/CompositionTraceId.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Diagnostics/CompositionTraceId.cs new file mode 100644 index 0000000000..4ca7b7218e --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Diagnostics/CompositionTraceId.cs @@ -0,0 +1,23 @@ +// 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. + +namespace System.ComponentModel.Composition.Diagnostics +{ + // NOTE: Do not change the trace ids of values that have already shipped, + // these leak out to TraceListerners which could take a dependency on them. + // This enum is a ushort deliberately, the maximum value of a trace id is 65535. + internal enum CompositionTraceId : ushort + { + // Rejection + + Rejection_DefinitionRejected = 1, + Rejection_DefinitionResurrected = 2, + + Discovery_AssemblyLoadFailed = 3, + Discovery_DefinitionMarkedWithPartNotDiscoverableAttribute = 4, + Discovery_DefinitionMismatchedExportArity = 5, + Discovery_DefinitionContainsNoExports = 6, + Discovery_MemberMarkedWithMultipleImportAndImportMany = 7, + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Diagnostics/CompositionTraceSource.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Diagnostics/CompositionTraceSource.cs new file mode 100644 index 0000000000..334a333c8e --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Diagnostics/CompositionTraceSource.cs @@ -0,0 +1,54 @@ +// 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 Microsoft.Internal; + +namespace System.ComponentModel.Composition.Diagnostics +{ + internal static class CompositionTraceSource + { + private static readonly DebuggerTraceWriter Source = new DebuggerTraceWriter(); + + public static bool CanWriteInformation + { + get { return Source.CanWriteInformation; } + } + + public static bool CanWriteWarning + { + get { return Source.CanWriteWarning; } + } + + public static bool CanWriteError + { + get { return Source.CanWriteError; } + } + + public static void WriteInformation(CompositionTraceId traceId, string format, params object[] arguments) + { + EnsureEnabled(CanWriteInformation); + + Source.WriteInformation(traceId, format, arguments); + } + + public static void WriteWarning(CompositionTraceId traceId, string format, params object[] arguments) + { + EnsureEnabled(CanWriteWarning); + + Source.WriteWarning(traceId, format, arguments); + } + + public static void WriteError(CompositionTraceId traceId, string format, params object[] arguments) + { + EnsureEnabled(CanWriteError); + + Source.WriteError(traceId, format, arguments); + } + + private static void EnsureEnabled(bool condition) + { + Assumes.IsTrue(condition, "To avoid unnecessary work when a trace level has not been enabled, check CanWriteXXX before calling this method."); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Diagnostics/SilverlightTraceWriter.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Diagnostics/SilverlightTraceWriter.cs new file mode 100644 index 0000000000..aec3bcb7de --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Diagnostics/SilverlightTraceWriter.cs @@ -0,0 +1,86 @@ +// 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.Diagnostics; +using System.Globalization; +using System.Text; + +namespace System.ComponentModel.Composition.Diagnostics +{ + internal sealed class DebuggerTraceWriter : TraceWriter + { + private static readonly string SourceName = "System.ComponentModel.Composition"; + + public override bool CanWriteInformation + { + get { return false; } + } + + public override bool CanWriteWarning + { + get { return Debugger.IsLogging(); } + } + + public override bool CanWriteError + { + get { return Debugger.IsLogging(); } + } + + public override void WriteInformation(CompositionTraceId traceId, string format, params object[] arguments) + { + WriteEvent(TraceEventType.Information, traceId, format, arguments); + } + + public override void WriteWarning(CompositionTraceId traceId, string format, params object[] arguments) + { + WriteEvent(TraceEventType.Warning, traceId, format, arguments); + } + + public override void WriteError(CompositionTraceId traceId, string format, params object[] arguments) + { + WriteEvent(TraceEventType.Error, traceId, format, arguments); + } + + private static void WriteEvent(TraceEventType eventType, CompositionTraceId traceId, string format, params object[] arguments) + { + if (!Debugger.IsLogging()) + { + return; + } + + string logMessage = CreateLogMessage(eventType, traceId, format, arguments); + Debugger.Log(0, null, logMessage); + } + + internal static string CreateLogMessage(TraceEventType eventType, CompositionTraceId traceId, string format, params object[] arguments) + { + StringBuilder messageBuilder = new StringBuilder(); + + // Format taken from TraceListener.TraceEvent in full framework + messageBuilder.AppendFormat(CultureInfo.InvariantCulture, "{0} {1}: {2} : ", + SourceName, eventType.ToString(), (int)traceId); + + if (arguments == null) + { + messageBuilder.Append(format); + } + else + { + messageBuilder.AppendFormat(CultureInfo.InvariantCulture, format, arguments); + } + + messageBuilder.AppendLine(); + + return messageBuilder.ToString(); + } + + // Copied from TraceEventType in full framework + internal enum TraceEventType + { + Error = 2, + Warning = 4, + Information = 8, + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Diagnostics/TraceWriter.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Diagnostics/TraceWriter.cs new file mode 100644 index 0000000000..965fcf5c05 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Diagnostics/TraceWriter.cs @@ -0,0 +1,31 @@ +// 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. + +namespace System.ComponentModel.Composition.Diagnostics +{ + internal abstract class TraceWriter + { + public abstract bool CanWriteInformation + { + get; + } + + public abstract bool CanWriteWarning + { + get; + } + + public abstract bool CanWriteError + { + get; + } + + public abstract void WriteInformation(CompositionTraceId traceId, string format, params object[] arguments); + + public abstract void WriteWarning(CompositionTraceId traceId, string format, params object[] arguments); + + public abstract void WriteError(CompositionTraceId traceId, string format, params object[] arguments); + } +} + diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ErrorBuilder.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ErrorBuilder.cs new file mode 100644 index 0000000000..05af206787 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ErrorBuilder.cs @@ -0,0 +1,101 @@ +// 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.ComponentModel.Composition.Primitives; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition +{ + internal static class ErrorBuilder + { + public static CompositionError PreventedByExistingImport(ComposablePart part, ImportDefinition import) + { + return CompositionError.Create( + CompositionErrorId.ImportEngine_PreventedByExistingImport, + SR.ImportEngine_PreventedByExistingImport, + import.ToElement().DisplayName, + part.ToElement().DisplayName); + } + + public static CompositionError InvalidStateForRecompposition(ComposablePart part) + { + return CompositionError.Create( + CompositionErrorId.ImportEngine_InvalidStateForRecomposition, + SR.ImportEngine_InvalidStateForRecomposition, + part.ToElement().DisplayName); + } + + public static CompositionError ComposeTookTooManyIterations(int maximumNumberOfCompositionIterations) + { + return CompositionError.Create( + CompositionErrorId.ImportEngine_ComposeTookTooManyIterations, + SR.ImportEngine_ComposeTookTooManyIterations, + maximumNumberOfCompositionIterations); + } + + public static CompositionError CreateImportCardinalityMismatch(ImportCardinalityMismatchException exception, ImportDefinition definition) + { + Assumes.NotNull(exception, definition); + + return CompositionError.Create( + CompositionErrorId.ImportEngine_ImportCardinalityMismatch, + exception.Message, + definition.ToElement(), + (Exception)null); + } + + public static CompositionError CreatePartCannotActivate(ComposablePart part, Exception innerException) + { + Assumes.NotNull(part, innerException); + + ICompositionElement element = part.ToElement(); + return CompositionError.Create( + CompositionErrorId.ImportEngine_PartCannotActivate, + element, + innerException, + SR.ImportEngine_PartCannotActivate, + element.DisplayName); + } + + public static CompositionError CreatePartCannotSetImport(ComposablePart part, ImportDefinition definition, Exception innerException) + { + Assumes.NotNull(part, definition, innerException); + + ICompositionElement element = definition.ToElement(); + return CompositionError.Create( + CompositionErrorId.ImportEngine_PartCannotSetImport, + element, + innerException, + SR.ImportEngine_PartCannotSetImport, + element.DisplayName, + part.ToElement().DisplayName); + } + + public static CompositionError CreateCannotGetExportedValue(ComposablePart part, ExportDefinition definition, Exception innerException) + { + Assumes.NotNull(part, definition, innerException); + + ICompositionElement element = definition.ToElement(); + return CompositionError.Create( + CompositionErrorId.ImportEngine_PartCannotGetExportedValue, + element, + innerException, + SR.ImportEngine_PartCannotGetExportedValue, + element.DisplayName, + part.ToElement().DisplayName); + } + + public static CompositionError CreatePartCycle(ComposablePart part) + { + Assumes.NotNull(part); + + ICompositionElement element = part.ToElement(); + return CompositionError.Create( + CompositionErrorId.ImportEngine_PartCycle, + element, + SR.ImportEngine_PartCycle, + element.DisplayName); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExceptionBuilder.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExceptionBuilder.cs new file mode 100644 index 0000000000..8f8fad40cb --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExceptionBuilder.cs @@ -0,0 +1,94 @@ +// 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.ComponentModel.Composition; +using System.ComponentModel.Composition.Primitives; +using System.Globalization; +using Microsoft.Internal; + +namespace System.ComponentModel +{ + internal static class ExceptionBuilder // UNDONE combine with other one + { + public static Exception CreateDiscoveryException(string messageFormat, params string[] arguments) + { + // DiscoveryError (Dev10:602872): This should go through the discovery error reporting when + // we add a way to report discovery errors properly. + return new InvalidOperationException(Format(messageFormat, arguments)); + } + + public static ArgumentException CreateContainsNullElement(string parameterName) + { + Assumes.NotNull(parameterName); + + string message = Format(SR.Argument_NullElement, parameterName); + + return new ArgumentException(message, parameterName); + } + + public static ObjectDisposedException CreateObjectDisposed(object instance) + { + Assumes.NotNull(instance); + + return new ObjectDisposedException(instance.GetType().ToString()); + } + + public static NotImplementedException CreateNotOverriddenByDerived(string memberName) + { + Assumes.NotNullOrEmpty(memberName); + + string message = Format(SR.NotImplemented_NotOverriddenByDerived, memberName); + + return new NotImplementedException(message); + } + + public static ArgumentException CreateExportDefinitionNotOnThisComposablePart(string parameterName) + { + Assumes.NotNullOrEmpty(parameterName); + + string message = Format(SR.ExportDefinitionNotOnThisComposablePart, parameterName); + + return new ArgumentException(message, parameterName); + } + + public static ArgumentException CreateImportDefinitionNotOnThisComposablePart(string parameterName) + { + Assumes.NotNullOrEmpty(parameterName); + + string message = Format(SR.ImportDefinitionNotOnThisComposablePart, parameterName); + + return new ArgumentException(message, parameterName); + } + + public static CompositionException CreateCannotGetExportedValue(ComposablePart part, ExportDefinition definition, Exception innerException) + { + Assumes.NotNull(part, definition, innerException); + + return new CompositionException( + ErrorBuilder.CreateCannotGetExportedValue(part, definition, innerException)); + } + + public static ArgumentException CreateReflectionModelInvalidPartDefinition(string parameterName, Type partDefinitionType) + { + Assumes.NotNullOrEmpty(parameterName); + Assumes.NotNull(partDefinitionType); + + return new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.ReflectionModel_InvalidPartDefinition, partDefinitionType), parameterName); + } + + public static ArgumentException ExportFactory_TooManyGenericParameters(string typeName) + { + Assumes.NotNullOrEmpty(typeName); + + string message = Format(SR.ExportFactory_TooManyGenericParameters, typeName); + + return new ArgumentException(message, typeName); + } + + private static string Format(string format, params string[] arguments) + { + return String.Format(CultureInfo.CurrentCulture, format, arguments); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportAttribute.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportAttribute.cs new file mode 100644 index 0000000000..508cf287c6 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportAttribute.cs @@ -0,0 +1,143 @@ +// 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.Diagnostics.CodeAnalysis; + +namespace System.ComponentModel.Composition +{ + /// + /// Specifies that a type, property, field, or method provides a particular export. + /// + [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes")] + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method, + AllowMultiple = true, Inherited = false)] + public class ExportAttribute : Attribute + { + /// + /// Initializes a new instance of the class, exporting the + /// type or member marked with this attribute under the default contract name. + /// + /// + /// + /// The default contract name is the result of calling + /// on the type of the + /// property or field, or the type itself, that is marked with this attribute. + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + public ExportAttribute() + : this((string)null, (Type)null) + { + } + + /// + /// Initializes a new instance of the class, exporting the + /// type or member marked with this attribute under a contract name derived from the + /// specified type. + /// + /// + /// A of which to derive the contract name to export the type or + /// member marked with this attribute, under; or to use the + /// default contract name. + /// + /// + /// + /// The contract name is the result of calling + /// on + /// . + /// + /// + /// The default contract name is the result of calling + /// on the type of the + /// property or field, or the type itself, that is marked with this attribute. + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + public ExportAttribute(Type contractType) + : this((string)null, contractType) + { + } + + /// + /// Initializes a new instance of the class, exporting the + /// type or member marked with this attribute under the specified contract name. + /// + /// + /// A containing the contract name to export the type or member + /// marked with this attribute, under; or or an empty string + /// ("") to use the default contract name. + /// + /// + /// + /// The default contract name is the result of calling + /// on the property or field + /// type, or the type itself that this is marked with this attribute. + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + public ExportAttribute(string contractName) + : this(contractName, (Type)null) + { + } + + /// + /// Initializes a new instance of the class, exporting the + /// type or member marked with this attribute under the specified contract name. + /// + /// + /// A containing the contract name to export the type or member + /// marked with this attribute, under; or or an empty string + /// ("") to use the default contract name. + /// + /// + /// A of which to derive the contract name to export the type or + /// member marked with this attribute, under; or to use the + /// default contract name. + /// + /// + /// + /// The default contract name is the result of calling + /// on the property or field + /// type, or the type itself that this is marked with this attribute. + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + public ExportAttribute(string contractName, Type contractType) + { + ContractName = contractName; + ContractType = contractType; + } + + /// + /// Gets the contract name to export the type or member under. + /// + /// + /// A containing the contract name to export the type or member + /// marked with this attribute, under. The default value is an empty string (""). + /// + public string ContractName { get; private set; } + + /// + /// Get the contract type that is exported by the member that this attribute is attached to. + /// + /// + /// A of the export that is be provided. The default value is + /// which means that the type will be obtained by looking at the type on + /// the member that this export is attached to. + /// + public Type ContractType { get; private set; } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportCardinalityCheckResult.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportCardinalityCheckResult.cs new file mode 100644 index 0000000000..f34f445f0a --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportCardinalityCheckResult.cs @@ -0,0 +1,13 @@ +// 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. + +namespace System.ComponentModel.Composition +{ + internal enum ExportCardinalityCheckResult : int + { + Match, + NoExports, + TooManyExports + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportFactoryOfT.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportFactoryOfT.cs new file mode 100644 index 0000000000..4e20aedd0d --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportFactoryOfT.cs @@ -0,0 +1,27 @@ +// 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. + +namespace System.ComponentModel.Composition +{ + public class ExportFactory + { + private Func> _exportLifetimeContextCreator; + + public ExportFactory(Func> exportLifetimeContextCreator) + { + if (exportLifetimeContextCreator == null) + { + throw new ArgumentNullException("exportLifetimeContextCreator"); + } + + _exportLifetimeContextCreator = exportLifetimeContextCreator; + } + + public ExportLifetimeContext CreateExport() + { + Tuple untypedLifetimeContext = _exportLifetimeContextCreator.Invoke(); + return new ExportLifetimeContext(untypedLifetimeContext.Item1, untypedLifetimeContext.Item2); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportFactoryOfTTMetadata.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportFactoryOfTTMetadata.cs new file mode 100644 index 0000000000..942e634e40 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportFactoryOfTTMetadata.cs @@ -0,0 +1,23 @@ +// 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. + +namespace System.ComponentModel.Composition +{ + public class ExportFactory : ExportFactory + { + private readonly TMetadata _metadata; + + public ExportFactory(Func> exportLifetimeContextCreator, TMetadata metadata) + : base(exportLifetimeContextCreator) + { + _metadata = metadata; + } + + public TMetadata Metadata + { + get { return _metadata; } + } + } +} + diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportLifetimeContextOfT.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportLifetimeContextOfT.cs new file mode 100644 index 0000000000..282f529688 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportLifetimeContextOfT.cs @@ -0,0 +1,35 @@ +// 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. + +namespace System.ComponentModel.Composition +{ + public sealed class ExportLifetimeContext : IDisposable + { + private readonly T _value; + private readonly Action _disposeAction; + + public ExportLifetimeContext(T value, Action disposeAction) + { + _value = value; + _disposeAction = disposeAction; + } + + public T Value + { + get + { + return _value; + } + } + + public void Dispose() + { + if (_disposeAction != null) + { + _disposeAction.Invoke(); + } + } + } +} + diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportMetadataAttribute.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportMetadataAttribute.cs new file mode 100644 index 0000000000..566c400ff6 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportMetadataAttribute.cs @@ -0,0 +1,64 @@ +// 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. + +namespace System.ComponentModel.Composition +{ + /// + /// Specifies metadata for a type, property, field, or method marked with the + /// . + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Field, + AllowMultiple = true, Inherited = false)] + public sealed class ExportMetadataAttribute : Attribute + { + /// + /// Initializes a new instance of the with the + /// specified name and metadata value. + /// + /// + /// A containing the name of the metadata value; or + /// to set the property to an empty + /// string (""). + /// + /// + /// An containing the metadata value. This can be + /// . + /// + public ExportMetadataAttribute(string name, object value) + { + Name = name ?? string.Empty; + Value = value; + } + + /// + /// Gets the name of the metadata value. + /// + /// + /// A containing the name of the metadata value. + /// + public string Name + { + get; + private set; + } + + /// + /// Gets the metadata value. + /// + /// + /// An containing the metadata value. + /// + public object Value + { + get; + private set; + } + + public bool IsMultiple + { + get; + set; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportServices.DisposableLazy.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportServices.DisposableLazy.cs new file mode 100644 index 0000000000..2cbc3f9946 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportServices.DisposableLazy.cs @@ -0,0 +1,48 @@ +// 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.Threading; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition +{ + partial class ExportServices + { + private sealed class DisposableLazy : Lazy, IDisposable + { + private IDisposable _disposable; + + public DisposableLazy(Func valueFactory, TMetadataView metadataView, IDisposable disposable, LazyThreadSafetyMode mode) + : base(valueFactory, metadataView, mode) + { + Assumes.NotNull(disposable); + + _disposable = disposable; + } + + void IDisposable.Dispose() + { + _disposable.Dispose(); + } + } + + private sealed class DisposableLazy : Lazy, IDisposable + { + private IDisposable _disposable; + + public DisposableLazy(Func valueFactory, IDisposable disposable, LazyThreadSafetyMode mode) + : base(valueFactory, mode) + { + Assumes.NotNull(disposable); + + _disposable = disposable; + } + + void IDisposable.Dispose() + { + _disposable.Dispose(); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportServices.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportServices.cs new file mode 100644 index 0000000000..ba3401c156 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportServices.cs @@ -0,0 +1,187 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; +using System.Threading; +using Microsoft.Internal; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition +{ + // Provides helpers for creating and dealing with Exports + internal static partial class ExportServices + { + private static readonly MethodInfo _createStronglyTypedLazyOfTM = typeof(ExportServices).GetMethod("CreateStronglyTypedLazyOfTM", BindingFlags.NonPublic | BindingFlags.Static); + private static readonly MethodInfo _createStronglyTypedLazyOfT = typeof(ExportServices).GetMethod("CreateStronglyTypedLazyOfT", BindingFlags.NonPublic | BindingFlags.Static); + private static readonly MethodInfo _createSemiStronglyTypedLazy = typeof(ExportServices).GetMethod("CreateSemiStronglyTypedLazy", BindingFlags.NonPublic | BindingFlags.Static); + + internal static readonly Type DefaultMetadataViewType = typeof(IDictionary); + internal static readonly Type DefaultExportedValueType = typeof(object); + + internal static bool IsDefaultMetadataViewType(Type metadataViewType) + { + Assumes.NotNull(metadataViewType); + + // Consider all types that IDictionary derives from, such + // as ICollection>, IEnumerable> + // and IEnumerable, as default metadata view + return metadataViewType.IsAssignableFrom(DefaultMetadataViewType); + } + + internal static bool IsDictionaryConstructorViewType(Type metadataViewType) + { + Assumes.NotNull(metadataViewType); + + // Does the view type have a constructor that is a Dictionary + return metadataViewType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, + Type.DefaultBinder, + new Type[] { typeof(IDictionary) }, + new ParameterModifier[0]) != null; + } + + internal static Func CreateStronglyTypedLazyFactory(Type exportType, Type metadataViewType) + { + MethodInfo genericMethod = null; + if (metadataViewType != null) + { + genericMethod = _createStronglyTypedLazyOfTM.MakeGenericMethod(exportType ?? ExportServices.DefaultExportedValueType, metadataViewType); + } + else + { + genericMethod = _createStronglyTypedLazyOfT.MakeGenericMethod(exportType ?? ExportServices.DefaultExportedValueType); + } + Assumes.NotNull(genericMethod); + return (Func)Delegate.CreateDelegate(typeof(Func), genericMethod); + } + + internal static Func> CreateSemiStronglyTypedLazyFactory(Type exportType, Type metadataViewType) + { + MethodInfo genericMethod = _createSemiStronglyTypedLazy.MakeGenericMethod( + exportType ?? ExportServices.DefaultExportedValueType, + metadataViewType ?? ExportServices.DefaultMetadataViewType); + Assumes.NotNull(genericMethod); + return (Func>)Delegate.CreateDelegate(typeof(Func>), genericMethod); + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] + internal static Lazy CreateStronglyTypedLazyOfTM(Export export) + { + IDisposable disposable = export as IDisposable; + if (disposable != null) + { + return new DisposableLazy( + () => ExportServices.GetCastedExportedValue(export), + AttributedModelServices.GetMetadataView(export.Metadata), + disposable, + LazyThreadSafetyMode.PublicationOnly); + } + else + { + return new Lazy( + () => ExportServices.GetCastedExportedValue(export), + AttributedModelServices.GetMetadataView(export.Metadata), + LazyThreadSafetyMode.PublicationOnly); + } + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] + internal static Lazy CreateStronglyTypedLazyOfT(Export export) + { + IDisposable disposable = export as IDisposable; + if (disposable != null) + { + return new DisposableLazy( + () => ExportServices.GetCastedExportedValue(export), + disposable, + LazyThreadSafetyMode.PublicationOnly); + } + else + { + return new Lazy(() => ExportServices.GetCastedExportedValue(export), LazyThreadSafetyMode.PublicationOnly); + + } + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] + internal static Lazy CreateSemiStronglyTypedLazy(Export export) + { + IDisposable disposable = export as IDisposable; + if (disposable != null) + { + return new DisposableLazy( + () => ExportServices.GetCastedExportedValue(export), + AttributedModelServices.GetMetadataView(export.Metadata), + disposable, + LazyThreadSafetyMode.PublicationOnly); + } + else + { + return new Lazy( + () => ExportServices.GetCastedExportedValue(export), + AttributedModelServices.GetMetadataView(export.Metadata), + LazyThreadSafetyMode.PublicationOnly); + } + } + + internal static T GetCastedExportedValue(Export export) + { + return CastExportedValue(export.ToElement(), export.Value); + } + + internal static T CastExportedValue(ICompositionElement element, object exportedValue) + { + object typedExportedValue = null; + + bool succeeded = ContractServices.TryCast(typeof(T), exportedValue, out typedExportedValue); + if (!succeeded) + { + throw new CompositionContractMismatchException(string.Format(CultureInfo.CurrentCulture, + SR.ContractMismatch_ExportedValueCannotBeCastToT, + element.DisplayName, + typeof(T))); + } + + return (T)typedExportedValue; + } + + internal static ExportCardinalityCheckResult CheckCardinality(ImportDefinition definition, IEnumerable enumerable) + { + EnumerableCardinality actualCardinality = (enumerable != null) ? enumerable.GetCardinality() : EnumerableCardinality.Zero; + + return MatchCardinality(actualCardinality, definition.Cardinality); + } + + private static ExportCardinalityCheckResult MatchCardinality(EnumerableCardinality actualCardinality, ImportCardinality importCardinality) + { + switch (actualCardinality) + { + case EnumerableCardinality.Zero: + if (importCardinality == ImportCardinality.ExactlyOne) + { + return ExportCardinalityCheckResult.NoExports; + } + break; + + case EnumerableCardinality.TwoOrMore: + if (importCardinality.IsAtMostOne()) + { + return ExportCardinalityCheckResult.TooManyExports; + } + break; + + default: + Assumes.IsTrue(actualCardinality == EnumerableCardinality.One); + break; + + } + + return ExportCardinalityCheckResult.Match; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AdaptationConstants.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AdaptationConstants.cs new file mode 100644 index 0000000000..a255e0b3b3 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AdaptationConstants.cs @@ -0,0 +1,27 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Microsoft.Internal; +using Microsoft.Internal.Collections; +using System.Threading; + +namespace System.ComponentModel.Composition +{ + public static class AdaptationConstants + { + private const string CompositionNamespace = "System.ComponentModel.Composition"; + + public const string AdapterContractName = CompositionNamespace + ".AdapterContract"; + public const string AdapterFromContractMetadataName = "FromContract"; + public const string AdapterToContractMetadataName = "ToContract"; + + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AggregateCatalog.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AggregateCatalog.cs new file mode 100644 index 0000000000..2936c2d666 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AggregateCatalog.cs @@ -0,0 +1,235 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Linq; +using System.Threading; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + /// + /// A mutable collection of s. + /// + /// + /// This type is thread safe. + /// + public class AggregateCatalog : ComposablePartCatalog, INotifyComposablePartCatalogChanged + { + private ComposablePartCatalogCollection _catalogs = null; + private volatile int _isDisposed = 0; + + /// + /// Initializes a new instance of the class. + /// + public AggregateCatalog() + : this((IEnumerable)null) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified catalogs. + /// + /// + /// An of objects to add to the + /// . + /// + /// + /// is . + /// + /// + /// contains an element that is . + /// + public AggregateCatalog(params ComposablePartCatalog[] catalogs) + : this((IEnumerable)catalogs) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified catalogs. + /// + /// + /// An of objects to add + /// to the ; or to + /// create an that is empty. + /// + /// + /// contains an element that is . + /// + public AggregateCatalog(IEnumerable catalogs) + { + Requires.NullOrNotNullElements(catalogs, "catalogs"); + + _catalogs = new ComposablePartCatalogCollection(catalogs, OnChanged, OnChanging); + } + + /// + /// Notify when the contents of the Catalog has changed. + /// + public event EventHandler Changed + { + add + { + _catalogs.Changed += value; + } + remove + { + _catalogs.Changed -= value; + } + } + + /// + /// Notify when the contents of the Catalog has changing. + /// + public event EventHandler Changing + { + add + { + _catalogs.Changing += value; + } + remove + { + _catalogs.Changing -= value; + } + } + + /// + /// Returns the export definitions that match the constraint defined by the specified definition. + /// + /// + /// The that defines the conditions of the + /// objects to return. + /// + /// + /// An of containing the + /// objects and their associated + /// for objects that match the constraint defined + /// by . + /// + /// + /// is . + /// + /// + /// The has been disposed of. + /// + public override IEnumerable> GetExports(ImportDefinition definition) + { + ThrowIfDisposed(); + + Requires.NotNull(definition, nameof(definition)); + + // We optimize for the case where the result is comparible with the requested cardinality, though we do remain correct in all cases. + // We do so to avoid any unnecessary allocations + IEnumerable> result = null; + List> aggregateResult = null; + + foreach (var catalog in _catalogs) + { + var catalogExports = catalog.GetExports(definition); + if (catalogExports != ComposablePartCatalog._EmptyExportsList) + { + // ideally this is is the case we will always hit + if (result == null) + { + result = catalogExports; + } + else + { + // sadly the result has already been assigned, which means we are in the aggregate case + if (aggregateResult == null) + { + aggregateResult = new List>(result); + result = aggregateResult; + } + aggregateResult.AddRange(catalogExports); + } + } + } + return result ?? ComposablePartCatalog._EmptyExportsList; + } + + /// + /// Gets the underlying catalogs of the catalog. + /// + /// + /// An of underlying objects + /// of the . + /// + /// + /// The has been disposed of. + /// + public ICollection Catalogs + { + get + { + ThrowIfDisposed(); + Contract.Ensures(Contract.Result>() != null); + + return _catalogs; + } + } + + protected override void Dispose(bool disposing) + { + try + { + if (disposing) + { + // NOTE : According to http://msdn.microsoft.com/en-us/library/4bw5ewxy.aspx, the warning is bogus when used with Interlocked API. +#pragma warning disable 420 + if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0) +#pragma warning restore 420 + { + _catalogs.Dispose(); + } + } + } + finally + { + base.Dispose(disposing); + } + } + + public override IEnumerator GetEnumerator() + { + return _catalogs.SelectMany(catalog => catalog).GetEnumerator(); + } + + /// + /// Raises the event. + /// + /// + /// An containing the data for the event. + /// + protected virtual void OnChanged(ComposablePartCatalogChangeEventArgs e) + { + _catalogs.OnChanged(this, e); + } + + /// + /// Raises the event. + /// + /// + /// An containing the data for the event. + /// + protected virtual void OnChanging(ComposablePartCatalogChangeEventArgs e) + { + _catalogs.OnChanging(this, e); + } + + [DebuggerStepThrough] + private void ThrowIfDisposed() + { + if (_isDisposed == 1) + { + throw ExceptionBuilder.CreateObjectDisposed(this); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AggregateExportProvider.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AggregateExportProvider.cs new file mode 100644 index 0000000000..34f38d52d9 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AggregateExportProvider.cs @@ -0,0 +1,230 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Linq; +using System.Threading; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition.Hosting +{ + public class AggregateExportProvider : ExportProvider , IDisposable + { + private readonly ReadOnlyCollection _readOnlyProviders; + private readonly ExportProvider[] _providers; + private volatile int _isDisposed = 0; + + /// + /// Initializes a new instance of the class. + /// + /// The prioritized list of export providers. + /// + /// contains an element that is . + /// + /// + /// + /// The will consult the providers in the order they have been specfied when + /// executing . + /// + /// + /// The does not take ownership of the specified providers. + /// That is, it will not try to dispose of any of them when it gets disposed. + /// + /// + public AggregateExportProvider(params ExportProvider[] providers) + { + // NOTE : we optimize for the array case here, because the collection of providers is typically tiny + // Arrays are much more compact to store and much faster to create and enumerate + ExportProvider[] copiedProviders = null; + if (providers != null) + { + copiedProviders = new ExportProvider[providers.Length]; + for (int i = 0; i < providers.Length; i++) + { + ExportProvider provider = providers[i]; + if (provider == null) + { + throw ExceptionBuilder.CreateContainsNullElement("providers"); + } + + copiedProviders[i] = provider; + + provider.ExportsChanged += OnExportChangedInternal; + provider.ExportsChanging += OnExportChangingInternal; + } + } + else + { + copiedProviders = new ExportProvider[] { }; + } + + _providers = copiedProviders; + _readOnlyProviders = new ReadOnlyCollection(_providers); + } + + /// + /// Initializes a new instance of the class. + /// + /// The prioritized list of export providers. The providers are consulted in order in which they are supplied. + /// + /// + /// The will consult the providers in the order they have been specfied when + /// executing . + /// + /// + /// The does not take ownership of the specified providers. + /// That is, it will not try to dispose of any of them when it gets disposed. + /// + /// + public AggregateExportProvider(IEnumerable providers) + : this((providers!= null) ? providers.AsArray() : null) + { + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + // NOTE : According to http://msdn.microsoft.com/en-us/library/4bw5ewxy.aspx, the warning is bogus when used with Interlocked API. +#pragma warning disable 420 + if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0) +#pragma warning restore 420 + { + foreach (ExportProvider provider in _providers) + { + provider.ExportsChanged -= OnExportChangedInternal; + provider.ExportsChanging -= OnExportChangingInternal; + } + } + } + } + + /// + /// Gets the export providers which the aggregate export provider aggregates. + /// + /// + /// A of objects + /// which the aggregates. + /// + /// + /// The has been disposed of. + /// + public ReadOnlyCollection Providers + { + get + { + ThrowIfDisposed(); + Contract.Ensures(Contract.Result>() != null); + + return _readOnlyProviders; + } + } + + /// + /// Returns all exports that match the conditions of the specified import. + /// + /// The that defines the conditions of the + /// to get. + /// + /// + /// An of objects that match + /// the conditions defined by , if found; otherwise, an + /// empty . + /// + /// + /// + /// The implementers should not treat the cardinality-related mismatches as errors, and are not + /// expected to throw exceptions in those cases. + /// For instance, if the import requests exactly one export and the provider has no matching exports or more than one, + /// it should return an empty of . + /// + /// + protected override IEnumerable GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition) + { + ThrowIfDisposed(); + + if (definition.Cardinality == ImportCardinality.ZeroOrMore) + { + var exports = new List(); + foreach (var provider in _providers) + { + foreach (var export in provider.GetExports(definition, atomicComposition)) + { + exports.Add(export); + } + } + return exports; + } + else + { + IEnumerable allExports = null; + + // if asked for "one or less", the prioriry is at play - the first provider that agrees to return the value + // which best complies with the request, wins. + foreach (ExportProvider provider in _providers) + { + IEnumerable exports; + bool cardinalityCheckResult = provider.TryGetExports(definition, atomicComposition, out exports); + bool anyExports = exports.FastAny(); + if (cardinalityCheckResult && anyExports) + { + // NOTE : if the provider returned nothing, we need to proceed, even if it indicated that the + // cardinality is correct - when asked for "one or less", the provider might - correctly - + // return an empty sequence, but we shouldn't be satisfied with that as providers down the list + // might have a value we are interested in. + return exports; + } + else + { + // This is a sneaky thing that we do - if in the end no provider returns the exports with the right cardinality + // we simply return the aggregation of all exports they have returned. This way the end result is still not what we want + // but no information is lost. + if (anyExports) + { + allExports = (allExports != null) ? allExports.Concat(exports) : exports; + } + } + } + + return allExports; + } + } + + private void OnExportChangedInternal(object sender, ExportsChangeEventArgs e) + { + OnExportsChanged(e); + } + + private void OnExportChangingInternal(object sender, ExportsChangeEventArgs e) + { + OnExportsChanging(e); + } + + [DebuggerStepThrough] + private void ThrowIfDisposed() + { + if (_isDisposed == 1) + { + throw ExceptionBuilder.CreateObjectDisposed(this); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ApplicationCatalog.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ApplicationCatalog.cs new file mode 100644 index 0000000000..429376df2a --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ApplicationCatalog.cs @@ -0,0 +1,221 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; +using System.Globalization; +using System.IO; +using System.Reflection; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + public partial class ApplicationCatalog : ComposablePartCatalog, ICompositionElement + { + private bool _isDisposed = false; + private volatile AggregateCatalog _innerCatalog = null; + private readonly object _thisLock = new object(); + private ICompositionElement _definitionOrigin = null; + private ReflectionContext _reflectionContext = null; + + public ApplicationCatalog() {} + + public ApplicationCatalog(ICompositionElement definitionOrigin) + { + Requires.NotNull(definitionOrigin, "definitionOrigin"); + + _definitionOrigin = definitionOrigin; + } + + public ApplicationCatalog(ReflectionContext reflectionContext) + { + Requires.NotNull(reflectionContext, "reflectionContext"); + + _reflectionContext = reflectionContext; + } + + public ApplicationCatalog(ReflectionContext reflectionContext, ICompositionElement definitionOrigin) + { + Requires.NotNull(reflectionContext, "reflectionContext"); + Requires.NotNull(definitionOrigin, "definitionOrigin"); + + _reflectionContext = reflectionContext; + _definitionOrigin = definitionOrigin; + } + + internal ComposablePartCatalog CreateCatalog(string location, string pattern) + { + if(_reflectionContext != null) + { + return (_definitionOrigin != null) + ? new DirectoryCatalog(location, pattern, _reflectionContext, _definitionOrigin) + : new DirectoryCatalog(location, pattern, _reflectionContext); + } + return (_definitionOrigin != null) + ? new DirectoryCatalog(location, pattern, _definitionOrigin) + : new DirectoryCatalog(location, pattern); + } + +// Note: +// Creating a catalog does not cause change notifications to propagate, For some reason the DeploymentCatalog did, but that is a bug. +// InnerCatalog is delay evaluated, from data supplied at construction time and so does not propagate change notifications + private AggregateCatalog InnerCatalog + { + get + { + if(_innerCatalog == null) + { + lock(_thisLock) + { + if(_innerCatalog == null) + { + var location = AppDomain.CurrentDomain.BaseDirectory; + Assumes.NotNull(location); + + var catalogs = new List(); + catalogs.Add(CreateCatalog(location, "*.exe")); + catalogs.Add(CreateCatalog(location, "*.dll")); + + string relativeSearchPath = AppDomain.CurrentDomain.RelativeSearchPath; + if(!string.IsNullOrEmpty(relativeSearchPath)) + { + string[] probingPaths = relativeSearchPath.Split(new char[] {';'}, StringSplitOptions.RemoveEmptyEntries); + foreach(var probingPath in probingPaths) + { + var path = Path.Combine(location, probingPath); + if(Directory.Exists(path)) + { + catalogs.Add(CreateCatalog(path, "*.dll")); + } + } + } + var innerCatalog = new AggregateCatalog(catalogs); + _innerCatalog = innerCatalog; + } + } + } + + return _innerCatalog; + } + } + + protected override void Dispose(bool disposing) + { + try + { + if (!_isDisposed) + { + IDisposable innerCatalog = null; + lock (_thisLock) + { + innerCatalog = _innerCatalog as IDisposable; + _innerCatalog = null; + _isDisposed = true; + } + if(innerCatalog != null) + { + innerCatalog.Dispose(); + } + } + } + finally + { + base.Dispose(disposing); + } + } + + public override IEnumerator GetEnumerator() + { + ThrowIfDisposed(); + + return InnerCatalog.GetEnumerator(); + } + + /// + /// Returns the export definitions that match the constraint defined by the specified definition. + /// + /// + /// The that defines the conditions of the + /// objects to return. + /// + /// + /// An of containing the + /// objects and their associated + /// for objects that match the constraint defined + /// by . + /// + /// + /// is . + /// + /// + /// The has been disposed of. + /// + public override IEnumerable> GetExports(ImportDefinition definition) + { + ThrowIfDisposed(); + + Requires.NotNull(definition, "definition"); + + return InnerCatalog.GetExports(definition); + } + + [DebuggerStepThrough] + [ContractArgumentValidator] + [SuppressMessage("Microsoft.Contracts", "CC1053", Justification = "Suppressing warning because this validator has no public contract")] + private void ThrowIfDisposed() + { + if (_isDisposed) + { + throw ExceptionBuilder.CreateObjectDisposed(this); + } + } + + private string GetDisplayName() + { + return string.Format(CultureInfo.CurrentCulture, + "{0} (Path=\"{1}\") (PrivateProbingPath=\"{2}\")", // NOLOC + GetType().Name, + AppDomain.CurrentDomain.BaseDirectory, + AppDomain.CurrentDomain.RelativeSearchPath); + } + + /// + /// Returns a string representation of the directory catalog. + /// + /// + /// A containing the string representation of the . + /// + public override string ToString() + { + return GetDisplayName(); + } + + /// + /// Gets the display name of the ApplicationCatalog. + /// + /// + /// A containing a human-readable display name of the . + /// + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] + string ICompositionElement.DisplayName + { + get { return GetDisplayName(); } + } + + /// + /// Gets the composition element from which the ApplicationCatalog originated. + /// + /// + /// This property always returns . + /// + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] + ICompositionElement ICompositionElement.Origin + { + get { return null; } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AssemblyCatalog.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AssemblyCatalog.cs new file mode 100644 index 0000000000..d501f5c850 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AssemblyCatalog.cs @@ -0,0 +1,572 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Reflection; +using System.Threading; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + /// + /// An immutable ComposablePartCatalog created from a managed code assembly. + /// + /// + /// This type is thread safe. + /// + [DebuggerTypeProxy(typeof(AssemblyCatalogDebuggerProxy))] + public class AssemblyCatalog : ComposablePartCatalog, ICompositionElement + { + private readonly object _thisLock = new object(); + private readonly ICompositionElement _definitionOrigin; + private volatile Assembly _assembly = null; + private volatile ComposablePartCatalog _innerCatalog = null; + private int _isDisposed = 0; + + private ReflectionContext _reflectionContext = default(ReflectionContext); + + /// + /// Initializes a new instance of the class + /// with the specified code base. + /// + /// + /// A containing the code base of the assembly containing the + /// attributed objects to add to the . + /// + /// + /// is . + /// + /// + /// is a zero-length string, contains only white space, + /// or contains one or more invalid characters. />. + /// + /// + /// The specified path, file name, or both exceed the system-defined maximum length. + /// + /// + /// The caller does not have path discovery permission. + /// + /// + /// is not found. + /// + /// + /// could not be loaded. + /// + /// -or- + /// + /// specified a directory. + /// + /// + /// is not a valid assembly + /// -or- + /// Version 2.0 or later of the common language runtime is currently loaded + /// and was compiled with a later version. + /// + /// + /// The assembly referenced by is loaded into the Load context. + /// + public AssemblyCatalog(string codeBase) + { + Requires.NotNullOrEmpty(codeBase, "codeBase"); + + InitializeAssemblyCatalog(LoadAssembly(codeBase)); + _definitionOrigin = this; + } + + /// + /// Initializes a new instance of the class + /// with the specified code base. + /// + /// + /// A containing the code base of the assembly containing the + /// attributed objects to add to the . + /// + /// + /// The a context used by the catalog when + /// interpreting the types to inject attributes into the type definition. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// + /// is a zero-length string, contains only white space, + /// or contains one or more invalid characters. />. + /// + /// + /// The specified path, file name, or both exceed the system-defined maximum length. + /// + /// + /// The caller does not have path discovery permission. + /// + /// + /// is not found. + /// + /// + /// could not be loaded. + /// + /// -or- + /// + /// specified a directory. + /// + /// + /// is not a valid assembly + /// -or- + /// Version 2.0 or later of the common language runtime is currently loaded + /// and was compiled with a later version. + /// + /// + /// The assembly referenced by is loaded into the Load context. + /// + public AssemblyCatalog(string codeBase, ReflectionContext reflectionContext) + { + Requires.NotNullOrEmpty(codeBase, "codeBase"); + Requires.NotNull(reflectionContext, "reflectionContext"); + + InitializeAssemblyCatalog(LoadAssembly(codeBase)); + _reflectionContext = reflectionContext; + _definitionOrigin = this; + } + + /// + /// Initializes a new instance of the class + /// with the specified code base. + /// + /// + /// A containing the code base of the assembly containing the + /// attributed objects to add to the . + /// + /// + /// The CompositionElement used by Diagnostics to identify the source for parts. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// + /// is a zero-length string, contains only white space, + /// or contains one or more invalid characters. />. + /// + /// + /// The specified path, file name, or both exceed the system-defined maximum length. + /// + /// + /// The caller does not have path discovery permission. + /// + /// + /// is not found. + /// + /// + /// could not be loaded. + /// + /// -or- + /// + /// specified a directory. + /// + /// + /// is not a valid assembly + /// -or- + /// Version 2.0 or later of the common language runtime is currently loaded + /// and was compiled with a later version. + /// + /// + /// The assembly referenced by is loaded into the Load context. + /// + public AssemblyCatalog(string codeBase, ICompositionElement definitionOrigin) + { + Requires.NotNullOrEmpty(codeBase, "codeBase"); + Requires.NotNull(definitionOrigin, nameof(definitionOrigin)); + + InitializeAssemblyCatalog(LoadAssembly(codeBase)); + _definitionOrigin = definitionOrigin; + } + + /// + /// Initializes a new instance of the class + /// with the specified code base. + /// + /// + /// A containing the code base of the assembly containing the + /// attributed objects to add to the . + /// + /// + /// The a context used by the catalog when + /// interpreting the types to inject attributes into the type definition. + /// + /// + /// The CompositionElement used by Diagnostics to identify the source for parts. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// + /// is a zero-length string, contains only white space, + /// or contains one or more invalid characters. />. + /// + /// + /// The specified path, file name, or both exceed the system-defined maximum length. + /// + /// + /// The caller does not have path discovery permission. + /// + /// + /// is not found. + /// + /// + /// could not be loaded. + /// + /// -or- + /// + /// specified a directory. + /// + /// + /// is not a valid assembly + /// -or- + /// Version 2.0 or later of the common language runtime is currently loaded + /// and was compiled with a later version. + /// + /// + /// The assembly referenced by is loaded into the Load context. + /// + public AssemblyCatalog(string codeBase, ReflectionContext reflectionContext, ICompositionElement definitionOrigin) + { + Requires.NotNullOrEmpty(codeBase, "codeBase"); + Requires.NotNull(reflectionContext, "reflectionContext"); + Requires.NotNull(definitionOrigin, "definitionOrigin"); + + InitializeAssemblyCatalog(LoadAssembly(codeBase)); + _reflectionContext = reflectionContext; + _definitionOrigin = definitionOrigin; + } + + /// + /// Initializes a new instance of the class + /// with the specified assembly and reflection context. + /// + /// + /// The containing the attributed objects to + /// add to the . + /// + /// + /// The a context used by the catalog when + /// interpreting the types to inject attributes into the type definition. + /// + /// + /// is . + /// + /// -or- + /// + /// was loaded in the reflection-only context. + /// + /// -or- + /// + /// is . + /// + public AssemblyCatalog(Assembly assembly, ReflectionContext reflectionContext) + { + Requires.NotNull(assembly, "assembly"); + Requires.NotNull(reflectionContext, "reflectionContext"); + + InitializeAssemblyCatalog(assembly); + _reflectionContext = reflectionContext; + _definitionOrigin = this; + } + + /// + /// Initializes a new instance of the class + /// with the specified assembly, reflectionContext and definitionOrigin. + /// + /// + /// The containing the attributed objects to + /// add to the . + /// + /// + /// The a context used by the catalog when + /// interpreting the types to inject attributes into the type definition. + /// + /// + /// The CompositionElement used by Diagnostics to identify the source for parts. + /// + /// + /// is . + /// + /// -or- + /// + /// was loaded in the reflection-only context. + /// + /// -or- + /// + /// is . + /// + /// -or- + /// + /// is . + /// + public AssemblyCatalog(Assembly assembly, ReflectionContext reflectionContext, ICompositionElement definitionOrigin) + { + Requires.NotNull(assembly, "assembly"); + Requires.NotNull(reflectionContext, "reflectionContext"); + Requires.NotNull(definitionOrigin, "definitionOrigin"); + + InitializeAssemblyCatalog(assembly); + _reflectionContext = reflectionContext; + _definitionOrigin = definitionOrigin; + } + + /// + /// Initializes a new instance of the class + /// with the specified assembly. + /// + /// + /// The containing the attributed objects to + /// add to the . + /// + /// + /// is . + /// + /// -or- + /// + /// was loaded in the reflection-only context. + /// + public AssemblyCatalog(Assembly assembly) + { + Requires.NotNull(assembly, nameof(assembly)); + + InitializeAssemblyCatalog(assembly); + _definitionOrigin = this; + } + + /// + /// Initializes a new instance of the class + /// with the specified assembly. + /// + /// + /// The containing the attributed objects to + /// add to the . + /// + /// + /// The CompositionElement used by Diagnostics to identify the source for parts. + /// + /// + /// is . + /// + /// -or- + /// + /// was loaded in the reflection-only context. + /// + /// -or- + /// + /// is . + /// + public AssemblyCatalog(Assembly assembly, ICompositionElement definitionOrigin) + { + Requires.NotNull(assembly, nameof(assembly)); + Requires.NotNull(definitionOrigin, nameof(definitionOrigin)); + + InitializeAssemblyCatalog(assembly); + _definitionOrigin = definitionOrigin; + } + + private void InitializeAssemblyCatalog(Assembly assembly) + { + if (assembly.ReflectionOnly) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.Argument_AssemblyReflectionOnly, "assembly"), "assembly"); + } + _assembly = assembly; + } + + /// + /// Returns the export definitions that match the constraint defined by the specified definition. + /// + /// + /// The that defines the conditions of the + /// objects to return. + /// + /// + /// An of containing the + /// objects and their associated + /// for objects that match the constraint defined + /// by . + /// + /// + /// is . + /// + /// + /// The has been disposed of. + /// + /// + /// + /// Overriders of this property should never return , if no + /// match the conditions defined by + /// , return an empty . + /// + /// + public override IEnumerable> GetExports(ImportDefinition definition) + { + return InnerCatalog.GetExports(definition); + } + + private ComposablePartCatalog InnerCatalog + { + get + { + ThrowIfDisposed(); + + if (_innerCatalog == null) + { + var catalogReflectionContextAttribute = _assembly.GetFirstAttribute(); + var assembly = (catalogReflectionContextAttribute != null) + ? catalogReflectionContextAttribute.CreateReflectionContext().MapAssembly(_assembly) + : _assembly; + lock (_thisLock) + { + if (_innerCatalog == null) + { + var catalog = (_reflectionContext != null) + ? new TypeCatalog(assembly.GetTypes(), _reflectionContext, _definitionOrigin) + : new TypeCatalog(assembly.GetTypes(), _definitionOrigin); + Thread.MemoryBarrier(); + _innerCatalog = catalog; + } + } + } + return _innerCatalog; + } + } + + /// + /// Gets the assembly containing the attributed types contained within the assembly + /// catalog. + /// + /// + /// The containing the attributed objects + /// contained within the . + /// + public Assembly Assembly + { + get + { + Contract.Ensures(Contract.Result() != null); + + return _assembly; + } + } + + /// + /// Gets the display name of the assembly catalog. + /// + /// + /// A containing a human-readable display name of the . + /// + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] + string ICompositionElement.DisplayName + { + get { return GetDisplayName(); } + } + + /// + /// Gets the composition element from which the assembly catalog originated. + /// + /// + /// This property always returns . + /// + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] + ICompositionElement ICompositionElement.Origin + { + get { return null; } + } + + /// + /// Returns a string representation of the assembly catalog. + /// + /// + /// A containing the string representation of the . + /// + public override string ToString() + { + return GetDisplayName(); + } + + protected override void Dispose(bool disposing) + { + try + { + if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0) + { + if (disposing) + { + if (_innerCatalog != null) + { + _innerCatalog.Dispose(); + } + } + } + } + finally + { + base.Dispose(disposing); + } + } + + public override IEnumerator GetEnumerator() + { + return InnerCatalog.GetEnumerator(); + } + + private void ThrowIfDisposed() + { + if (_isDisposed == 1) + { + throw ExceptionBuilder.CreateObjectDisposed(this); + } + } + + private string GetDisplayName() + { + return string.Format(CultureInfo.CurrentCulture, + "{0} (Assembly=\"{1}\")", // NOLOC + GetType().Name, + Assembly.FullName); + } + + private static Assembly LoadAssembly(string codeBase) + { + Requires.NotNullOrEmpty(codeBase, "codeBase"); + + AssemblyName assemblyName; + + try + { + assemblyName = AssemblyName.GetAssemblyName(codeBase); + } + catch (ArgumentException) + { + assemblyName = new AssemblyName(); + assemblyName.CodeBase = codeBase; + } + + return Assembly.Load(assemblyName); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AssemblyCatalogDebuggerProxy.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AssemblyCatalogDebuggerProxy.cs new file mode 100644 index 0000000000..1b197a5586 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AssemblyCatalogDebuggerProxy.cs @@ -0,0 +1,38 @@ +// 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.Collections.ObjectModel; +using System.ComponentModel.Composition.Primitives; +using System.Reflection; +using Microsoft.Internal; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition.Hosting +{ + internal class AssemblyCatalogDebuggerProxy + { + private readonly AssemblyCatalog _catalog; + + public AssemblyCatalogDebuggerProxy(AssemblyCatalog catalog) + { + Requires.NotNull(catalog, nameof(catalog)); + + _catalog = catalog; + } + + public Assembly Assembly + { + get { return _catalog.Assembly; } + } + + public ReadOnlyCollection Parts + { + // NOTE: This shouldn't be cached, so that on every query of + // the current value of the underlying catalog is respected. + // We use ReadOnlyCollection as arrays do not have the + // appropriate debugger display attributes applied to them. + get { return _catalog.Parts.ToReadOnlyCollection(); } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AtomicComposition.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AtomicComposition.cs new file mode 100644 index 0000000000..39ee345c86 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AtomicComposition.cs @@ -0,0 +1,357 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + /// + /// AtomicComposition provides lightweight atomicCompositional semantics to enable temporary + /// state to be managed for a series of nested atomicCompositions. Each atomicComposition maintains + /// queryable state along with a sequence of actions necessary to complete the state when + /// the atomicComposition is no longer in danger of being rolled back. State is completed or + /// rolled back when the atomicComposition is disposed, depending on the state of the + /// CompleteOnDipose property which defaults to false. The using(...) pattern in C# is a + /// convenient mechanism for defining atomicComposition scopes. + /// + /// The least obvious aspects of AtomicComposition deal with nesting. + /// + /// Firstly, no complete actions are actually performed until the outermost atomicComposition is + /// completed. Completeting or rolling back nested atomicCompositions serves only to change which + /// actions would be completed the outer atomicComposition. + /// + /// Secondly, state is added in the form of queries associated with an object key. The + /// key represents a unique object the state is being held on behalf of. The quieries are + /// accessed throught the Query methods which provide automatic chaining to execute queries + /// across the target atomicComposition and its inner atomicComposition as appropriate. + /// + /// Lastly, when a nested atomicComposition is created for a given outer the outer atomicComposition is locked. + /// It remains locked until the inner atomicComposition is disposed or completeed preventing the addition of + /// state, actions or other inner atomicCompositions. + /// + public class AtomicComposition : IDisposable + { + private readonly AtomicComposition _outerAtomicComposition; + private KeyValuePair[] _values; + private int _valueCount = 0; + private List _completeActionList; + private List _revertActionList; + private bool _isDisposed = false; + private bool _isCompleted = false; + private bool _containsInnerAtomicComposition = false; + + public AtomicComposition() + : this(null) + { + } + + public AtomicComposition(AtomicComposition outerAtomicComposition) + { + // Lock the inner atomicComposition so that we can assume nothing changes except on + // the innermost scope, and thereby optimize the query path + if (outerAtomicComposition != null) + { + _outerAtomicComposition = outerAtomicComposition; + _outerAtomicComposition.ContainsInnerAtomicComposition = true; + } + } + + public void SetValue(object key, object value) + { + ThrowIfDisposed(); + ThrowIfCompleted(); + ThrowIfContainsInnerAtomicComposition(); + + Requires.NotNull(key, nameof(key)); + + SetValueInternal(key, value); + } + + public bool TryGetValue(object key, out T value) + { + return TryGetValue(key, false, out value); + } + + [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters")] + public bool TryGetValue(object key, bool localAtomicCompositionOnly, out T value) + { + ThrowIfDisposed(); + ThrowIfCompleted(); + + Requires.NotNull(key, nameof(key)); + + return TryGetValueInternal(key, localAtomicCompositionOnly, out value); + } + + public void AddCompleteAction(Action completeAction) + { + ThrowIfDisposed(); + ThrowIfCompleted(); + ThrowIfContainsInnerAtomicComposition(); + + Requires.NotNull(completeAction, nameof(completeAction)); + + if (_completeActionList == null) + { + _completeActionList = new List(); + } + _completeActionList.Add(completeAction); + } + + public void AddRevertAction(Action revertAction) + { + ThrowIfDisposed(); + ThrowIfCompleted(); + ThrowIfContainsInnerAtomicComposition(); + + Requires.NotNull(revertAction, nameof(revertAction)); + + if (_revertActionList == null) + { + _revertActionList = new List(); + } + _revertActionList.Add(revertAction); + } + + public void Complete() + { + ThrowIfDisposed(); + ThrowIfCompleted(); + + if (_outerAtomicComposition == null) + { // Execute all the complete actions + FinalComplete(); + } + else + { // Copy the actions and state to the outer atomicComposition + CopyComplete(); + } + + _isCompleted = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + ThrowIfDisposed(); + _isDisposed = true; + + if (_outerAtomicComposition != null) + { + _outerAtomicComposition.ContainsInnerAtomicComposition = false; + } + + // Revert is always immediate and involves forgetting information and + // exceuting any appropriate revert actions + if (!_isCompleted) + { + if (_revertActionList != null) + { + List exceptions = null; + + // Execute the revert actions in reverse order to ensure + // everything incrementally rollsback its state. + for (int i = _revertActionList.Count - 1; i >= 0; i--) + { + Action action = _revertActionList[i]; + try + { + action(); + } + catch(CompositionException) + { + // This can only happen after preview is completed, so ... abandon remainder of events is correct + throw; + } + catch(Exception e) + { + if (exceptions == null) + { + //If any exceptions leak through the actions we will swallow them for now + // complete processing the list + // and we will throw InvalidOperationException with an AggregateException as it's innerException + exceptions = new List(); + } + exceptions.Add(e); + } + } + _revertActionList = null; + if(exceptions != null) + { + throw new InvalidOperationException(SR.InvalidOperation_RevertAndCompleteActionsMustNotThrow, new AggregateException(exceptions)); + } + } + } + } + + private void FinalComplete() + { + // Completeting the outer most scope is easy, just execute all the actions + if (_completeActionList != null) + { + List exceptions = null; + + foreach (Action action in _completeActionList) + { + try + { + action(); + } + catch(CompositionException) + { + // This can only happen after preview is completed, so ... abandon remainder of events is correct + throw; + } + catch(Exception e) + { + if (exceptions == null) + { + //If any exceptions leak through the actions we will swallow them for now complete processing the list + // and we will throw InvalidOperationException with an AggregateException as it's innerException + exceptions = new List(); + } + exceptions.Add(e); + } + } + _completeActionList = null; + if(exceptions != null) + { + throw new InvalidOperationException(SR.InvalidOperation_RevertAndCompleteActionsMustNotThrow, new AggregateException(exceptions)); + } + } + } + + private void CopyComplete() + { + Assumes.NotNull(_outerAtomicComposition); + + _outerAtomicComposition.ContainsInnerAtomicComposition = false; + + // Inner scopes are much odder, because completeting them means coalescing them into the + // outer scope - the complete or revert actions are deferred until the outermost scope completes + // or any intermediate rolls back + if (_completeActionList != null) + { + foreach (Action action in _completeActionList) + { + _outerAtomicComposition.AddCompleteAction(action); + } + } + + if (_revertActionList != null) + { + foreach (Action action in _revertActionList) + { + _outerAtomicComposition.AddRevertAction(action); + } + } + + // We can copy over existing atomicComposition entries because they're either already chained or + // overwrite by design and can now be completed or rolled back together + for (var index = 0; index < _valueCount; index++) + { + _outerAtomicComposition.SetValueInternal( + _values[index].Key, _values[index].Value); + } + } + + private bool ContainsInnerAtomicComposition + { + set + { + if (value == true && _containsInnerAtomicComposition == true) + { + throw new InvalidOperationException(SR.AtomicComposition_AlreadyNested); + } + _containsInnerAtomicComposition = value; + } + } + + private bool TryGetValueInternal(object key, bool localAtomicCompositionOnly, out T value) + { + for (var index = 0; index < _valueCount; index++) + { + if (_values[index].Key == key) + { + value = (T)_values[index].Value; + return true; + } + } + + // If there's no atomicComposition available then recurse until we hit the outermost + // scope, where upon we go ahead and return null + if (!localAtomicCompositionOnly && _outerAtomicComposition != null) + { + return _outerAtomicComposition.TryGetValueInternal(key, localAtomicCompositionOnly, out value); + } + + value = default(T); + return false; + } + + private void SetValueInternal(object key, object value) + { + // Handle overwrites quickly + for (var index = 0; index < _valueCount; index++) + { + if (_values[index].Key == key) + { + _values[index] = new KeyValuePair(key, value); + return; + } + } + + // Expand storage when needed + if (_values == null || _valueCount == _values.Length) + { + var newQueries = new KeyValuePair[_valueCount == 0 ? 5 : _valueCount * 2]; + if (_values != null) + { + Array.Copy(_values, newQueries, _valueCount); + } + _values = newQueries; + } + + // Store a new entry + _values[_valueCount] = new KeyValuePair(key, value); + _valueCount++; + return; + } + + [DebuggerStepThrough] + private void ThrowIfContainsInnerAtomicComposition() + { + if (_containsInnerAtomicComposition) + { + throw new InvalidOperationException(SR.AtomicComposition_PartOfAnotherAtomicComposition); + } + } + + [DebuggerStepThrough] + private void ThrowIfCompleted() + { + if (_isCompleted) + { + throw new InvalidOperationException(SR.AtomicComposition_AlreadyCompleted); + } + } + + [DebuggerStepThrough] + private void ThrowIfDisposed() + { + if (_isDisposed) + { + throw ExceptionBuilder.CreateObjectDisposed(this); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AtomicCompositionExtensions.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AtomicCompositionExtensions.cs new file mode 100644 index 0000000000..447fa463f4 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AtomicCompositionExtensions.cs @@ -0,0 +1,57 @@ +// 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 Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + internal static class AtomicCompositionExtensions + { + internal static T GetValueAllowNull(this AtomicComposition atomicComposition, T defaultResultAndKey) where T : class + { + Assumes.NotNull(defaultResultAndKey); + + return GetValueAllowNull(atomicComposition, defaultResultAndKey, defaultResultAndKey); + } + + internal static T GetValueAllowNull(this AtomicComposition atomicComposition, object key, T defaultResult) + { + T result; + if (atomicComposition != null && atomicComposition.TryGetValue(key, out result)) + { + return result; + } + + return defaultResult; + } + + internal static void AddRevertActionAllowNull(this AtomicComposition atomicComposition, Action action) + { + Assumes.NotNull(action); + + if (atomicComposition == null) + { + action(); + } + else + { + atomicComposition.AddRevertAction(action); + } + } + + internal static void AddCompleteActionAllowNull(this AtomicComposition atomicComposition, Action action) + { + Assumes.NotNull(action); + + if (atomicComposition == null) + { + action(); + } + else + { + atomicComposition.AddCompleteAction(action); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.CatalogChangeProxy.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.CatalogChangeProxy.cs new file mode 100644 index 0000000000..196c670afc --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.CatalogChangeProxy.cs @@ -0,0 +1,58 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + public partial class CatalogExportProvider : ExportProvider, IDisposable + { + private class CatalogChangeProxy : ComposablePartCatalog + { + private ComposablePartCatalog _originalCatalog; + private List _addedParts; + private HashSet _removedParts; + + public CatalogChangeProxy(ComposablePartCatalog originalCatalog, + IEnumerable addedParts, + IEnumerable removedParts) + { + _originalCatalog = originalCatalog; + _addedParts = new List(addedParts); + _removedParts = new HashSet(removedParts); + } + + public override IEnumerator GetEnumerator() + { + return _originalCatalog.Concat(_addedParts).Except(_removedParts).GetEnumerator(); + } + + public override IEnumerable> GetExports( + ImportDefinition definition) + { + Requires.NotNull(definition, nameof(definition)); + + var originalExports = _originalCatalog.GetExports(definition); + var trimmedExports = originalExports.Where(partAndExport => + !_removedParts.Contains(partAndExport.Item1)); + + var addedExports = new List>(); + foreach (var part in _addedParts) + { + foreach (var export in part.ExportDefinitions) + { + if (definition.IsConstraintSatisfiedBy(export)) + { + addedExports.Add(new Tuple(part, export)); + } + } + } + return trimmedExports.Concat(addedExports); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.CatalogExport.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.CatalogExport.cs new file mode 100644 index 0000000000..5940d29861 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.CatalogExport.cs @@ -0,0 +1,172 @@ +// 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.ComponentModel.Composition.Primitives; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + public partial class CatalogExportProvider + { + private class CatalogExport : Export + { + protected readonly CatalogExportProvider _catalogExportProvider; + protected readonly ComposablePartDefinition _partDefinition; + protected readonly ExportDefinition _definition; + + public CatalogExport(CatalogExportProvider catalogExportProvider, + ComposablePartDefinition partDefinition, ExportDefinition definition) + { + _catalogExportProvider = catalogExportProvider; + _partDefinition = partDefinition; + _definition = definition; + } + + public override ExportDefinition Definition + { + get + { + return _definition; + } + } + + protected virtual bool IsSharedPart + { + get + { + return true; + } + } + + protected CatalogPart GetPartCore() + { + return _catalogExportProvider.GetComposablePart(_partDefinition, IsSharedPart); + } + + protected void DisposePartCore(CatalogPart part, object value) + { + _catalogExportProvider.DisposePart(value, part, null); + } + + protected virtual CatalogPart GetPart() + { + return GetPartCore(); + } + + protected override object GetExportedValueCore() + { + return _catalogExportProvider.GetExportedValue(GetPart(), _definition, IsSharedPart); + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] + public static CatalogExport CreateExport(CatalogExportProvider catalogExportProvider, + ComposablePartDefinition partDefinition, ExportDefinition definition, CreationPolicy importCreationPolicy) + { + CreationPolicy partPolicy = partDefinition.Metadata.GetValue(CompositionConstants.PartCreationPolicyMetadataName); + bool isSharedPart = ShouldUseSharedPart(partPolicy, importCreationPolicy); + + if (isSharedPart) + { + return new CatalogExport(catalogExportProvider, partDefinition, definition); + } + else + { + return new NonSharedCatalogExport(catalogExportProvider, partDefinition, definition); + } + } + + private static bool ShouldUseSharedPart(CreationPolicy partPolicy, CreationPolicy importPolicy) + { + // Matrix that details which policy to use for a given part to satisfy a given import. + // Part.Any Part.Shared Part.NonShared + // Import.Any Shared Shared NonShared + // Import.Shared Shared Shared N/A + // Import.NonShared NonShared N/A NonShared + + switch (partPolicy) + { + case CreationPolicy.Any: + { + if (importPolicy == CreationPolicy.Any || + importPolicy == CreationPolicy.Shared) + { + return true; + } + return false; + } + + case CreationPolicy.NonShared: + { + Assumes.IsTrue(importPolicy != CreationPolicy.Shared); + return false; + } + + default: + { + Assumes.IsTrue(partPolicy == CreationPolicy.Shared); + Assumes.IsTrue(importPolicy != CreationPolicy.NonShared); + return true; + } + } + } + } + + private sealed class NonSharedCatalogExport : CatalogExport, IDisposable + { + private CatalogPart _part; + private readonly object _lock = new object(); + + public NonSharedCatalogExport(CatalogExportProvider catalogExportProvider, + ComposablePartDefinition partDefinition, ExportDefinition definition) + : base(catalogExportProvider, partDefinition, definition) + { + } + + protected override CatalogPart GetPart() + { + // we need to ensure that the part gets created only once, as the export contract requires that the same value be returned on subsequent calls + if (_part == null) + { + CatalogPart part = GetPartCore(); + + lock (_lock) + { + if (_part == null) + { + Thread.MemoryBarrier(); + _part = part; + part = null; + } + } + + if (part != null) + { + DisposePartCore(part, null); + } + } + + return _part; + } + + protected override bool IsSharedPart + { + get + { + return false; + } + } + + void IDisposable.Dispose() + { + if (_part != null) + { + DisposePartCore(_part, Value); + _part = null; + } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.FactoryExport.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.FactoryExport.cs new file mode 100644 index 0000000000..6570253abb --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.FactoryExport.cs @@ -0,0 +1,143 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.ComponentModel.Composition.ReflectionModel; +using System.Linq; + +namespace System.ComponentModel.Composition.Hosting +{ + public partial class CatalogExportProvider + { + internal abstract class FactoryExport : Export + { + private readonly ComposablePartDefinition _partDefinition; + private readonly ExportDefinition _exportDefinition; + private ExportDefinition _factoryExportDefinition; + private FactoryExportPartDefinition _factoryExportPartDefinition; + + public FactoryExport(ComposablePartDefinition partDefinition, ExportDefinition exportDefinition) + { + _partDefinition = partDefinition; + _exportDefinition = exportDefinition; + _factoryExportDefinition = new PartCreatorExportDefinition(_exportDefinition); + } + + public override ExportDefinition Definition + { + get { return _factoryExportDefinition; } + } + + protected override object GetExportedValueCore() + { + if (_factoryExportPartDefinition == null) + { + _factoryExportPartDefinition = new FactoryExportPartDefinition(this); + } + return _factoryExportPartDefinition; + } + + protected ComposablePartDefinition UnderlyingPartDefinition + { + get + { + return _partDefinition; + } + } + + protected ExportDefinition UnderlyingExportDefinition + { + get + { + return _exportDefinition; + } + } + + public abstract Export CreateExportProduct(); + + private class FactoryExportPartDefinition : ComposablePartDefinition + { + private readonly FactoryExport _FactoryExport; + + public FactoryExportPartDefinition(FactoryExport FactoryExport) + { + _FactoryExport = FactoryExport; + } + + public override IEnumerable ExportDefinitions + { + get { return new ExportDefinition[] { _FactoryExport.Definition }; } + } + + public override IEnumerable ImportDefinitions + { + get { return Enumerable.Empty(); } + } + + public ExportDefinition FactoryExportDefinition + { + get { return _FactoryExport.Definition; } + } + + public Export CreateProductExport() + { + return _FactoryExport.CreateExportProduct(); + } + + public override ComposablePart CreatePart() + { + return new FactoryExportPart(this); + } + } + + private sealed class FactoryExportPart : ComposablePart, IDisposable + { + private readonly FactoryExportPartDefinition _definition; + private readonly Export _export; + + public FactoryExportPart(FactoryExportPartDefinition definition) + { + _definition = definition; + _export = definition.CreateProductExport(); + } + + public override IEnumerable ExportDefinitions + { + get { return _definition.ExportDefinitions; } + } + + public override IEnumerable ImportDefinitions + { + get { return _definition.ImportDefinitions; } + } + + public override object GetExportedValue(ExportDefinition definition) + { + if (definition != _definition.FactoryExportDefinition) + { + throw ExceptionBuilder.CreateExportDefinitionNotOnThisComposablePart("definition"); + } + + return _export.Value; + } + + public override void SetImport(ImportDefinition definition, IEnumerable exports) + { + throw ExceptionBuilder.CreateImportDefinitionNotOnThisComposablePart("definition"); + } + + public void Dispose() + { + IDisposable disposable = _export as IDisposable; + + if (disposable != null) + { + disposable.Dispose(); + } + } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.PartCreatorExport.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.PartCreatorExport.cs new file mode 100644 index 0000000000..4382d27cd0 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.PartCreatorExport.cs @@ -0,0 +1,27 @@ +// 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.ComponentModel.Composition.Primitives; + +namespace System.ComponentModel.Composition.Hosting +{ + public partial class CatalogExportProvider + { + internal class PartCreatorExport : FactoryExport + { + private readonly CatalogExportProvider _catalogExportProvider; + + public PartCreatorExport(CatalogExportProvider catalogExportProvider, ComposablePartDefinition partDefinition, ExportDefinition exportDefinition) : + base(partDefinition, exportDefinition) + { + _catalogExportProvider = catalogExportProvider; + } + + public override Export CreateExportProduct() + { + return new NonSharedCatalogExport(_catalogExportProvider, UnderlyingPartDefinition, UnderlyingExportDefinition); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.ScopeFactoryExport.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.ScopeFactoryExport.cs new file mode 100644 index 0000000000..de3e8ceee2 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.ScopeFactoryExport.cs @@ -0,0 +1,103 @@ +// 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.ComponentModel.Composition.Primitives; +using System.Threading; + +namespace System.ComponentModel.Composition.Hosting +{ + public partial class CatalogExportProvider + { + internal class ScopeFactoryExport : FactoryExport + { + private readonly ScopeManager _scopeManager; + private readonly CompositionScopeDefinition _catalog; + + internal ScopeFactoryExport(ScopeManager scopeManager, CompositionScopeDefinition catalog, ComposablePartDefinition partDefinition, ExportDefinition exportDefinition) : + base(partDefinition, exportDefinition) + { + _scopeManager = scopeManager; + _catalog = catalog; + } + + public override Export CreateExportProduct() + { + return new ScopeCatalogExport(this); + } + + private sealed class ScopeCatalogExport : Export, IDisposable + { + private readonly ScopeFactoryExport _scopeFactoryExport; + private CompositionContainer _childContainer; + private Export _export; + private readonly object _lock = new object(); + + public ScopeCatalogExport(ScopeFactoryExport scopeFactoryExport) + { + _scopeFactoryExport = scopeFactoryExport; + } + + public override ExportDefinition Definition + { + get + { + return _scopeFactoryExport.UnderlyingExportDefinition; + } + } + + protected override object GetExportedValueCore() + { + if (_export == null) + { + var childContainer = _scopeFactoryExport._scopeManager.CreateChildContainer(_scopeFactoryExport._catalog); + + var export = childContainer.CatalogExportProvider.CreateExport(_scopeFactoryExport.UnderlyingPartDefinition, _scopeFactoryExport.UnderlyingExportDefinition, false, CreationPolicy.Any); + lock (_lock) + { + if (_export == null) + { + _childContainer = childContainer; + Thread.MemoryBarrier(); + _export = export; + + childContainer = null; + export = null; + } + } + if (childContainer != null) + { + childContainer.Dispose(); + } + } + + return _export.Value; + } + + public void Dispose() + { + CompositionContainer childContainer = null; + Export export = null; + + if (_export != null) + { + lock (_lock) + { + export = _export; + childContainer = _childContainer; + + _childContainer = null; + Thread.MemoryBarrier(); + _export = null; + } + } + + if(childContainer != null) + { + childContainer.Dispose(); + } + } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.ScopeManager.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.ScopeManager.cs new file mode 100644 index 0000000000..24136e2528 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.ScopeManager.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. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + public partial class CatalogExportProvider + { + internal class ScopeManager : ExportProvider + { + private CompositionScopeDefinition _scopeDefinition; + private CatalogExportProvider _catalogExportProvider; + + public ScopeManager(CatalogExportProvider catalogExportProvider, CompositionScopeDefinition scopeDefinition) + { + Assumes.NotNull(catalogExportProvider); + Assumes.NotNull(scopeDefinition); + + _scopeDefinition = scopeDefinition; + _catalogExportProvider = catalogExportProvider; + } + + protected override IEnumerable GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition) + { + List exports = new List(); + + ImportDefinition queryImport = TranslateImport(definition); + if (queryImport == null) + { + return exports; + } + + // go through the catalogs and see if there's anything there of interest + foreach (CompositionScopeDefinition childCatalog in _scopeDefinition.Children) + { + foreach (var partDefinitionAndExportDefinition in childCatalog.GetExportsFromPublicSurface(queryImport)) + { + // We found a match in the child catalog. Now we need to check that it doesn't get rejected. + // if the rejetecion is enabled and atomic composition is present, we will actually have to do the work, if not - we just use what we have + bool isChildPartRejected = false; + + if (_catalogExportProvider.EnsureRejection(atomicComposition)) + { + using (var container = CreateChildContainer(childCatalog)) + { + // We create a nested AtomicComposition() because the container will be Disposed and + // the RevertActions need to operate before we Dispose the child container + using (var localAtomicComposition = new AtomicComposition(atomicComposition)) + { + isChildPartRejected = container.CatalogExportProvider.DetermineRejection(partDefinitionAndExportDefinition.Item1, localAtomicComposition); + } + } + } + + // If the child part has not been rejected, we will add it to the result set. + if (!isChildPartRejected) + { + exports.Add(CreateScopeExport(childCatalog, partDefinitionAndExportDefinition.Item1, partDefinitionAndExportDefinition.Item2)); + } + } + } + + return exports; + } + + private Export CreateScopeExport(CompositionScopeDefinition childCatalog, ComposablePartDefinition partDefinition, ExportDefinition exportDefinition) + { + return new ScopeFactoryExport(this, childCatalog, partDefinition, exportDefinition); + } + + internal CompositionContainer CreateChildContainer(ComposablePartCatalog childCatalog) + { + return new CompositionContainer(childCatalog, _catalogExportProvider._compositionOptions, _catalogExportProvider._sourceProvider); + } + + private static ImportDefinition TranslateImport(ImportDefinition definition) + { + IPartCreatorImportDefinition factoryDefinition = definition as IPartCreatorImportDefinition; + if (factoryDefinition == null) + { + return null; + } + + // Now we need to make sure that the creation policy is handled correctly + // We will always create a new child CatalogEP to satsify the request, so from the perspecitive of the caller, the policy should + // always be NonShared (or Any). From teh perspective of the callee, it's the otehr way around. + ContractBasedImportDefinition productImportDefinition = factoryDefinition.ProductImportDefinition; + ImportDefinition result = null; + + switch (productImportDefinition.RequiredCreationPolicy) + { + case CreationPolicy.NonShared: + { + // we need to recreate the import definition with the policy "Any", so that we can + // pull singletons from the inner CatalogEP. teh "non-sharedness" is achieved through + // the creation of the new EPs already. + result = new ContractBasedImportDefinition( + productImportDefinition.ContractName, + productImportDefinition.RequiredTypeIdentity, + productImportDefinition.RequiredMetadata, + productImportDefinition.Cardinality, + productImportDefinition.IsRecomposable, + productImportDefinition.IsPrerequisite, + CreationPolicy.Any, + productImportDefinition.Metadata); + break; + } + case CreationPolicy.Any: + { + // "Any" works every time + result = productImportDefinition; + break; + } + } + + return result; + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.cs new file mode 100644 index 0000000000..d282e8630b --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.cs @@ -0,0 +1,1055 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Diagnostics; +using System.ComponentModel.Composition.Primitives; +using System.ComponentModel.Composition.ReflectionModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using Microsoft.Internal; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition.Hosting +{ + public partial class CatalogExportProvider : ExportProvider, IDisposable + { + private class InnerCatalogExportProvider : ExportProvider + { + private CatalogExportProvider _outerExportProvider; + + public InnerCatalogExportProvider(CatalogExportProvider outerExportProvider) + { + Assumes.NotNull(outerExportProvider); + _outerExportProvider = outerExportProvider; + } + + protected override IEnumerable GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition) + { + return _outerExportProvider.InternalGetExportsCore(definition, atomicComposition); + } + } + + private readonly CompositionLock _lock; + private Dictionary _activatedParts = new Dictionary(); + private HashSet _rejectedParts = new HashSet(); + private ConditionalWeakTable> _gcRoots; + private HashSet _partsToDispose = new HashSet(); + private ComposablePartCatalog _catalog; + private volatile bool _isDisposed = false; + private volatile bool _isRunning = false; + private bool _disableSilentRejection = false; + private ExportProvider _sourceProvider; + private ImportEngine _importEngine; + private CompositionOptions _compositionOptions; + private ExportProvider _innerExportProvider; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The that the + /// uses to produce objects. + /// + /// + /// is . + /// + public CatalogExportProvider(ComposablePartCatalog catalog) + : this(catalog, CompositionOptions.Default) + { + } + + public CatalogExportProvider(ComposablePartCatalog catalog, bool isThreadSafe) + : this(catalog, isThreadSafe ? CompositionOptions.IsThreadSafe : CompositionOptions.Default) + { + } + + public CatalogExportProvider(ComposablePartCatalog catalog, CompositionOptions compositionOptions) + { + Requires.NotNull(catalog, nameof(catalog)); + if (compositionOptions > (CompositionOptions.DisableSilentRejection | CompositionOptions.IsThreadSafe | CompositionOptions.ExportCompositionService)) + { + throw new ArgumentOutOfRangeException("compositionOptions"); + } + + _catalog = catalog; + _compositionOptions = compositionOptions; + var notifyCatalogChanged = _catalog as INotifyComposablePartCatalogChanged; + if (notifyCatalogChanged != null) + { + notifyCatalogChanged.Changing += OnCatalogChanging; + } + + CompositionScopeDefinition scopeDefinition = _catalog as CompositionScopeDefinition; + if (scopeDefinition != null) + { + _innerExportProvider = new AggregateExportProvider(new ScopeManager(this, scopeDefinition), new InnerCatalogExportProvider(this)); + } + else + { + _innerExportProvider = new InnerCatalogExportProvider(this); + } + _lock = new CompositionLock(compositionOptions.HasFlag(CompositionOptions.IsThreadSafe)); + _disableSilentRejection = compositionOptions.HasFlag(CompositionOptions.DisableSilentRejection); + } + + /// + /// Gets the composable part catalog that the provider users to + /// produce exports. + /// + /// + /// The that the + /// + /// uses to produce objects. + /// + /// + /// The has been disposed of. + /// + public ComposablePartCatalog Catalog + { + get + { + ThrowIfDisposed(); + Contract.Ensures(Contract.Result() != null); + + return _catalog; + } + } + + /// + /// Gets the export provider which provides the provider access to additional + /// exports. + /// + /// + /// The which provides the + /// access to additional + /// objects. The default is . + /// + /// + /// is . + /// + /// + /// This property has already been set. + /// + /// -or- + /// + /// The methods on the + /// have already been accessed. + /// + /// + /// The has been disposed of. + /// + /// + /// This property must be set before accessing any methods on the + /// . + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EnsureCanSet ensures that the property is set only once, Dispose is not required")] + public ExportProvider SourceProvider + { + get + { + ThrowIfDisposed(); + using (_lock.LockStateForRead()) + { + return _sourceProvider; + } + } + set + { + ThrowIfDisposed(); + + Requires.NotNull(value, nameof(value)); + + ImportEngine newImportEngine = null; + AggregateExportProvider aggregateExportProvider = null; + ExportProvider sourceProvider = value; + + bool isThrowing = true; + try + { + newImportEngine = new ImportEngine(sourceProvider, _compositionOptions); + + sourceProvider.ExportsChanging += OnExportsChangingInternal; + + using (_lock.LockStateForWrite()) + { + EnsureCanSet(_sourceProvider); + + _sourceProvider = sourceProvider; + _importEngine = newImportEngine; + + isThrowing = false; + } + } + finally + { + if (isThrowing) + { + sourceProvider.ExportsChanging -= OnExportsChangingInternal; + newImportEngine.Dispose(); + if (aggregateExportProvider != null) + { + aggregateExportProvider.Dispose(); + } + } + } + } + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (!_isDisposed) + { + //Note: We do not dispose _lock on dispose because DisposePart needs it to check isDisposed state + // to eliminate race conditions between it and Dispose + INotifyComposablePartCatalogChanged catalogToUnsubscribeFrom = null; + HashSet partsToDispose = null; + ImportEngine importEngine = null; + ExportProvider sourceProvider = null; + AggregateExportProvider aggregateExportProvider = null; + try + { + using (_lock.LockStateForWrite()) + { + if (!_isDisposed) + { + catalogToUnsubscribeFrom = _catalog as INotifyComposablePartCatalogChanged; + _catalog = null; + + aggregateExportProvider = _innerExportProvider as AggregateExportProvider; + _innerExportProvider = null; + + sourceProvider = _sourceProvider; + _sourceProvider = null; + + importEngine = _importEngine; + _importEngine = null; + + partsToDispose = _partsToDispose; + _gcRoots = null; + _isDisposed = true; + } + } + } + finally + { + if (catalogToUnsubscribeFrom != null) + { + catalogToUnsubscribeFrom.Changing -= OnCatalogChanging; + } + + if (aggregateExportProvider != null) + { + aggregateExportProvider.Dispose(); + } + + if (sourceProvider != null) + { + sourceProvider.ExportsChanging -= OnExportsChangingInternal; + } + + if (importEngine != null) + { + importEngine.Dispose(); + } + + if (partsToDispose != null) + { + foreach (var part in partsToDispose) + { + part.Dispose(); + } + } + } + } + } + } + + /// + /// Returns all exports that match the conditions of the specified import. + /// + /// The that defines the conditions of the + /// to get. + /// + /// + /// An of objects that match + /// the conditions defined by , if found; otherwise, an + /// empty . + /// + /// + /// + /// The implementers should not treat the cardinality-related mismatches as errors, and are not + /// expected to throw exceptions in those cases. + /// For instance, if the import requests exactly one export and the provider has no matching exports or more than one, + /// it should return an empty of . + /// + /// + protected override IEnumerable GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition) + { + ThrowIfDisposed(); + EnsureRunning(); + + Assumes.NotNull(_innerExportProvider); + + IEnumerable exports; + _innerExportProvider.TryGetExports(definition, atomicComposition, out exports); + return exports; + } + + private IEnumerable InternalGetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition) + { + ThrowIfDisposed(); + EnsureRunning(); + + // Use the version of the catalog appropriate to this atomicComposition + ComposablePartCatalog currentCatalog = atomicComposition.GetValueAllowNull(_catalog); + + IPartCreatorImportDefinition partCreatorDefinition = definition as IPartCreatorImportDefinition; + bool isExportFactory = false; + + if (partCreatorDefinition != null) + { + definition = partCreatorDefinition.ProductImportDefinition; + isExportFactory = true; + } + + CreationPolicy importPolicy = definition.GetRequiredCreationPolicy(); + + List exports = new List(); + bool ensureRejection = EnsureRejection(atomicComposition); + foreach (var partDefinitionAndExportDefinition in currentCatalog.GetExports(definition)) + { + bool isPartRejected = ensureRejection && IsRejected(partDefinitionAndExportDefinition.Item1, atomicComposition); + if (!isPartRejected) + { + exports.Add(CreateExport(partDefinitionAndExportDefinition.Item1, partDefinitionAndExportDefinition.Item2, isExportFactory, importPolicy)); + } + } + + return exports; + } + + private Export CreateExport(ComposablePartDefinition partDefinition, ExportDefinition exportDefinition, bool isExportFactory, CreationPolicy importPolicy) + { + if (isExportFactory) + { + return new PartCreatorExport(this, + partDefinition, + exportDefinition); + } + else + { + return CatalogExport.CreateExport(this, + partDefinition, + exportDefinition, + importPolicy); + } + } + + private void OnExportsChangingInternal(object sender, ExportsChangeEventArgs e) + { + UpdateRejections(e.AddedExports.Concat(e.RemovedExports), e.AtomicComposition); + } + + private static ExportDefinition[] GetExportsFromPartDefinitions(IEnumerable partDefinitions) + { + List exports = new List(); + + foreach (var partDefinition in partDefinitions) + { + foreach (var export in partDefinition.ExportDefinitions) + { + exports.Add(export); + + // While creating a PartCreatorExportDefinition for every changed definition may not be the most + // efficient way to do this the PartCreatorExportDefinition is very efficient and doesn't do any + // real work unless its metadata is pulled on. If this turns out to be a bottleneck then we + // will need to start tracking all the PartCreator's we hand out and only send those which we + // have handed out. In fact we could do the same thing for all the Exports if we wished but + // that requires a cache management which we don't want to do at this point. + exports.Add(new PartCreatorExportDefinition(export)); + } + } + + return exports.ToArray(); + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] + private void OnCatalogChanging(object sender, ComposablePartCatalogChangeEventArgs e) + { + using (var atomicComposition = new AtomicComposition(e.AtomicComposition)) + { + // Save the preview catalog to use in place of the original while handling + // this event + atomicComposition.SetValue(_catalog, + new CatalogChangeProxy(_catalog, e.AddedDefinitions, e.RemovedDefinitions)); + + IEnumerable addedExports = GetExportsFromPartDefinitions(e.AddedDefinitions); + IEnumerable removedExports = GetExportsFromPartDefinitions(e.RemovedDefinitions); + + // Remove any parts based on eliminated definitions (in a atomicComposition-friendly + // fashion) + foreach (var definition in e.RemovedDefinitions) + { + CatalogPart removedPart = null; + bool removed = false; + + using (_lock.LockStateForRead()) + { + removed = _activatedParts.TryGetValue(definition, out removedPart); + } + + if (removed) + { + var capturedDefinition = definition; + DisposePart(null, removedPart, atomicComposition); + atomicComposition.AddCompleteActionAllowNull(() => + { + using (_lock.LockStateForWrite()) + { + _activatedParts.Remove(capturedDefinition); + } + }); + } + } + + UpdateRejections(addedExports.ConcatAllowingNull(removedExports), atomicComposition); + + OnExportsChanging( + new ExportsChangeEventArgs(addedExports, removedExports, atomicComposition)); + + atomicComposition.AddCompleteAction(() => OnExportsChanged( + new ExportsChangeEventArgs(addedExports, removedExports, null))); + + atomicComposition.Complete(); + } + } + + private CatalogPart GetComposablePart(ComposablePartDefinition partDefinition, bool isSharedPart) + { + ThrowIfDisposed(); + EnsureRunning(); + + CatalogPart catalogPart = null; + + if (isSharedPart) + { + catalogPart = GetSharedPart(partDefinition); + } + else + { + ComposablePart part = partDefinition.CreatePart(); + catalogPart = new CatalogPart(part); + + IDisposable disposablePart = part as IDisposable; + if (disposablePart != null) + { + using (_lock.LockStateForWrite()) + { + _partsToDispose.Add(disposablePart); + } + } + } + + return catalogPart; + } + + private CatalogPart GetSharedPart(ComposablePartDefinition partDefinition) + { + CatalogPart catalogPart = null; + + // look up the part + using (_lock.LockStateForRead()) + { + if (_activatedParts.TryGetValue(partDefinition, out catalogPart)) + { + return catalogPart; + } + } + + // create a part outside of the lock + ComposablePart newPart = partDefinition.CreatePart(); + IDisposable disposableNewPart = newPart as IDisposable; + + using (_lock.LockStateForWrite()) + { + // check if the part is still not there + if (!_activatedParts.TryGetValue(partDefinition, out catalogPart)) + { + catalogPart = new CatalogPart(newPart); + _activatedParts.Add(partDefinition, catalogPart); + if (disposableNewPart != null) + { + _partsToDispose.Add(disposableNewPart); + } + + // indiacate the the part has been added + newPart = null; + disposableNewPart = null; + } + } + + // if disposableNewPart != null, this means we have created a new instance of something disposable and not used it + // Dispose of it now + if (disposableNewPart != null) + { + disposableNewPart.Dispose(); + } + + return catalogPart; + } + + private object GetExportedValue(CatalogPart part, ExportDefinition export, bool isSharedPart) + { + ThrowIfDisposed(); + EnsureRunning(); + + Assumes.NotNull(part, export); + + // We don't protect against thread racing here, as "importsSatisfied" is merely an optimization + // if two threads satisfy imports twice, the results is the same, just the perf hit is heavier. + + bool importsSatisfied = part.ImportsSatisfied; + ImportEngine importEngine = importsSatisfied ? null : _importEngine; + + object exportedValue = CompositionServices.GetExportedValueFromComposedPart( + importEngine, part.Part, export); + + if (!importsSatisfied) + { + // and set "ImportsSatisfied" to true + part.ImportsSatisfied = true; + } + + // Only hold conditional references for recomposable non-shared parts because we are + // already holding strong references to the shared parts. + if (exportedValue != null && !isSharedPart && part.Part.IsRecomposable()) + { + PreventPartCollection(exportedValue, part.Part); + } + + return exportedValue; + } + + private void ReleasePart(object exportedValue, CatalogPart catalogPart, AtomicComposition atomicComposition) + { + ThrowIfDisposed(); + EnsureRunning(); + + DisposePart(exportedValue, catalogPart, atomicComposition); + } + + private void DisposePart(object exportedValue, CatalogPart catalogPart, AtomicComposition atomicComposition) + { + Assumes.NotNull(catalogPart); + + if (_isDisposed) + return; + + ImportEngine importEngine = null; + using (_lock.LockStateForWrite()) + { + if (_isDisposed) + return; + + importEngine = _importEngine; + } + if (importEngine != null) + { + importEngine.ReleaseImports(catalogPart.Part, atomicComposition); + } + if (exportedValue != null) + { + atomicComposition.AddCompleteActionAllowNull(() => + { + AllowPartCollection(exportedValue); + }); + } + + IDisposable diposablePart = catalogPart.Part as IDisposable; + if (diposablePart != null) + { + atomicComposition.AddCompleteActionAllowNull(() => + { + bool removed = false; + + if (_isDisposed) + return; + using (_lock.LockStateForWrite()) + { + if (_isDisposed) + return; + + removed = _partsToDispose.Remove(diposablePart); + } + + if (removed) + { + diposablePart.Dispose(); + } + }); + } + } + + private void PreventPartCollection(object exportedValue, ComposablePart part) + { + Assumes.NotNull(exportedValue, part); + + using (_lock.LockStateForWrite()) + { + List partList; + + ConditionalWeakTable> gcRoots = _gcRoots; + if (gcRoots == null) + { + gcRoots = new ConditionalWeakTable>(); + } + + if (!gcRoots.TryGetValue(exportedValue, out partList)) + { + partList = new List(); + gcRoots.Add(exportedValue, partList); + } + + partList.Add(part); + + if (_gcRoots == null) + { + Thread.MemoryBarrier(); + _gcRoots = gcRoots; + } + } + } + + private void AllowPartCollection(object gcRoot) + { + if (_gcRoots != null) + { + using (_lock.LockStateForWrite()) + { + _gcRoots.Remove(gcRoot); + } + } + } + + private bool IsRejected(ComposablePartDefinition definition, AtomicComposition atomicComposition) + { + // Check to see if we're currently working on the definition in question. + // Recursive queries always answer optimistically, as if the definition hasn't + // been rejected - because if it is we can discard all decisions that were based + // on the faulty assumption in the first place. + var forceRejectionTest = false; + if (atomicComposition != null) + { + AtomicCompositionQueryState state = QueryPartState(atomicComposition, definition); + switch (state) + { + case AtomicCompositionQueryState.TreatAsRejected: + return true; + case AtomicCompositionQueryState.TreatAsValidated: + return false; + case AtomicCompositionQueryState.NeedsTesting: + forceRejectionTest = true; + break; + default: + Assumes.IsTrue(state == AtomicCompositionQueryState.Unknown); + // Need to do the work to determine the state + break; + } + } + + if (!forceRejectionTest) + { + // Next, anything that has been activated is not rejected + using (_lock.LockStateForRead()) + { + if (_activatedParts.ContainsKey(definition)) + { + return false; + } + + // Last stop before doing the hard work: check a specific registry of rejected parts + if (_rejectedParts.Contains(definition)) + { + return true; + } + } + } + + // Determine whether or not the definition's imports can be satisfied + return DetermineRejection(definition, atomicComposition); + } + + private bool EnsureRejection(AtomicComposition atomicComposition) + { + return !(_disableSilentRejection && (atomicComposition == null)); + } + + private bool DetermineRejection(ComposablePartDefinition definition, AtomicComposition parentAtomicComposition) + { + ChangeRejectedException exception = null; + + // if there is no active atomic composition and rejection is disabled, there's no need to do any of the below + if (!EnsureRejection(parentAtomicComposition)) + { + return false; + } + + using (var localAtomicComposition = new AtomicComposition(parentAtomicComposition)) + { + // The part definition we're currently working on is treated optimistically + // as if we know it hasn't been rejected. This handles recursion, and if we + // later decide that it has been rejected we'll discard all nested progress so + // all side-effects of the mistake are erased. + // + // Note that this means that recursive failures that would be detected by the + // import engine are not discovered by rejection currently. Loops among + // prerequisites, runaway import chains involving factories, and prerequisites + // that cannot be fully satisfied still result in runtime errors. Doing + // otherwise would be possible but potentially expensive - and could be a v2 + // improvement if deemed worthwhile. + UpdateAtomicCompositionQueryForPartEquals(localAtomicComposition, + definition, AtomicCompositionQueryState.TreatAsValidated); + + var newPart = definition.CreatePart(); + try + { + _importEngine.PreviewImports(newPart, localAtomicComposition); + + // Reuse the partially-fleshed out part the next time we need a shared + // instance to keep the expense of pre-validation to a minimum. Note that + // _activatedParts holds references to both shared and non-shared parts. + // The non-shared parts will only be used for rejection purposes only but + // the shared parts will be handed out when requested via GetExports as + // well as be used for rejection purposes. + localAtomicComposition.AddCompleteActionAllowNull(() => + { + using (_lock.LockStateForWrite()) + { + if (!_activatedParts.ContainsKey(definition)) + { + _activatedParts.Add(definition, new CatalogPart(newPart)); + IDisposable newDisposablePart = newPart as IDisposable; + if (newDisposablePart != null) + { + _partsToDispose.Add(newDisposablePart); + } + } + } + }); + + // Success! Complete any recursive work that was conditioned on this part's validation + localAtomicComposition.Complete(); + + return false; + } + catch (ChangeRejectedException ex) + { + exception = ex; + } + } + + // If we've reached this point then this part has been rejected so we need to + // record the rejection in our parent composition or execute it immediately if + // one doesn't exist. + parentAtomicComposition.AddCompleteActionAllowNull(() => + { + using (_lock.LockStateForWrite()) + { + _rejectedParts.Add(definition); + } + + CompositionTrace.PartDefinitionRejected(definition, exception); + + }); + if (parentAtomicComposition != null) + { + UpdateAtomicCompositionQueryForPartEquals(parentAtomicComposition, + definition, AtomicCompositionQueryState.TreatAsRejected); + } + + return true; + } + + private void UpdateRejections(IEnumerable changedExports, AtomicComposition atomicComposition) + { + using (var localAtomicComposition = new AtomicComposition(atomicComposition)) + { + // Reconsider every part definition that has been previously + // rejected to see if any of them can be added back. + var affectedRejections = new HashSet(); + + ComposablePartDefinition[] rejectedParts; + using (_lock.LockStateForRead()) + { + rejectedParts = _rejectedParts.ToArray(); + } + foreach (var definition in rejectedParts) + { + if (QueryPartState(localAtomicComposition, definition) == AtomicCompositionQueryState.TreatAsValidated) + { + continue; + } + + foreach (var import in definition.ImportDefinitions.Where(ImportEngine.IsRequiredImportForPreview)) + { + if (changedExports.Any(export => import.IsConstraintSatisfiedBy(export))) + { + affectedRejections.Add(definition); + break; + } + } + } + UpdateAtomicCompositionQueryForPartInHashSet(localAtomicComposition, + affectedRejections, AtomicCompositionQueryState.NeedsTesting); + + // Determine if any of the resurrectable parts is now available so that we can + // notify listeners of the exact changes to exports + var resurrectedExports = new List(); + + foreach (var partDefinition in affectedRejections) + { + if (!IsRejected(partDefinition, localAtomicComposition)) + { + // Notify listeners of the newly available exports and + // prepare to remove the rejected part from the list of rejections + resurrectedExports.AddRange(partDefinition.ExportDefinitions); + + // Capture the local so that the closure below refers to the current definition + // in the loop and not the value of 'partDefinition' when the closure executes + var capturedPartDefinition = partDefinition; + localAtomicComposition.AddCompleteAction(() => + { + using (_lock.LockStateForWrite()) + { + _rejectedParts.Remove(capturedPartDefinition); + } + + CompositionTrace.PartDefinitionResurrected(capturedPartDefinition); + }); + } + } + + // Notify anyone sourcing exports that the resurrected exports have appeared + if (resurrectedExports.Any()) + { + OnExportsChanging( + new ExportsChangeEventArgs(resurrectedExports, new ExportDefinition[0], localAtomicComposition)); + + localAtomicComposition.AddCompleteAction(() => OnExportsChanged( + new ExportsChangeEventArgs(resurrectedExports, new ExportDefinition[0], null))); + } + + localAtomicComposition.Complete(); + } + } + + [DebuggerStepThrough] + private void ThrowIfDisposed() + { + if (_isDisposed) + { + throw ExceptionBuilder.CreateObjectDisposed(this); + } + } + + /// + /// EnsureCanRun must be called from within a lock. + /// + [DebuggerStepThrough] + private void EnsureCanRun() + { + if ((_sourceProvider == null) || (_importEngine == null)) + { + throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.ObjectMustBeInitialized, "SourceProvider")); // NOLOC + } + } + + [DebuggerStepThrough] + private void EnsureRunning() + { + if (!_isRunning) + { + using (_lock.LockStateForWrite()) + { + if (!_isRunning) + { + EnsureCanRun(); + _isRunning = true; + } + } + } + } + + /// + /// EnsureCanSet must be called from within a lock. + /// + /// + /// + [DebuggerStepThrough] + private void EnsureCanSet(T currentValue) + where T : class + { + if ((_isRunning) || (currentValue != null)) + { + throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.ObjectAlreadyInitialized)); + } + } + + private AtomicCompositionQueryState QueryPartState(AtomicComposition atomicComposition, ComposablePartDefinition definition) + { + PartQueryStateNode node = GetPartQueryStateNode(atomicComposition); + if (node == null) + { + return AtomicCompositionQueryState.Unknown; + } + else + { + return node.GetQueryState(definition); + } + } + + private PartQueryStateNode GetPartQueryStateNode(AtomicComposition atomicComposition) + { + PartQueryStateNode node; + atomicComposition.TryGetValue(this, out node); + return node; + } + + private void UpdateAtomicCompositionQueryForPartEquals( + AtomicComposition atomicComposition, + ComposablePartDefinition part, + AtomicCompositionQueryState state) + { + PartQueryStateNode previousNode = GetPartQueryStateNode(atomicComposition); + atomicComposition.SetValue(this, new PartEqualsQueryStateNode(part, previousNode, state)); + } + + private void UpdateAtomicCompositionQueryForPartInHashSet( + AtomicComposition atomicComposition, + HashSet hashset, + AtomicCompositionQueryState state) + { + PartQueryStateNode previousNode = GetPartQueryStateNode(atomicComposition); + atomicComposition.SetValue(this, new PartInHashSetQueryStateNode(hashset, previousNode, state)); + } + + private enum AtomicCompositionQueryState + { + Unknown, + TreatAsRejected, + TreatAsValidated, + NeedsTesting + }; + + private abstract class PartQueryStateNode + { + private readonly PartQueryStateNode _previousNode; + private readonly AtomicCompositionQueryState _state; + + protected PartQueryStateNode(PartQueryStateNode previousNode, AtomicCompositionQueryState state) + { + _previousNode = previousNode; + _state = state; + } + + protected abstract bool IsMatchingDefinition(ComposablePartDefinition part, int partHashCode); + + public AtomicCompositionQueryState GetQueryState(ComposablePartDefinition definition) + { + int hashCode = definition.GetHashCode(); + PartQueryStateNode node = this; + do + { + if (node.IsMatchingDefinition(definition, hashCode)) + { + return node._state; + } + node = node._previousNode; + } + while (node != null); + + return AtomicCompositionQueryState.Unknown; + } + } + + private class PartEqualsQueryStateNode : PartQueryStateNode + { + private ComposablePartDefinition _part; + private int _hashCode; + public PartEqualsQueryStateNode(ComposablePartDefinition part, PartQueryStateNode previousNode, AtomicCompositionQueryState state) : + base(previousNode, state) + { + _part = part; + _hashCode = part.GetHashCode(); + } + + protected override bool IsMatchingDefinition(ComposablePartDefinition part, int partHashCode) + { + if (partHashCode != _hashCode) + { + return false; + } + return _part.Equals(part); + } + } + + private class PartInHashSetQueryStateNode : PartQueryStateNode + { + private HashSet _parts; + public PartInHashSetQueryStateNode(HashSet parts, PartQueryStateNode previousNode, AtomicCompositionQueryState state) : + base(previousNode, state) + { + _parts = parts; + } + + protected override bool IsMatchingDefinition(ComposablePartDefinition part, int partHashCode) + { + return _parts.Contains(part); + } + } + + private class CatalogPart + { + private volatile bool _importsSatisfied = false; + public CatalogPart(ComposablePart part) + { + Part = part; + } + public ComposablePart Part { get; private set; } + + public bool ImportsSatisfied + { + get + { + return _importsSatisfied; + } + set + { + _importsSatisfied = value; + } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExtensions.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExtensions.cs new file mode 100644 index 0000000000..4492d0b1a2 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExtensions.cs @@ -0,0 +1,24 @@ +// 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.ComponentModel.Composition.Primitives; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + public static class CatalogExtensions + { + /// + /// Creates a . + /// + /// The catalog. + /// The newly created + public static CompositionService CreateCompositionService(this ComposablePartCatalog composablePartCatalog) + { + Requires.NotNull(composablePartCatalog, nameof(composablePartCatalog)); + + return new CompositionService(composablePartCatalog); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ComposablePartCatalogChangeEventArgs.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ComposablePartCatalogChangeEventArgs.cs new file mode 100644 index 0000000000..f1ea899e01 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ComposablePartCatalogChangeEventArgs.cs @@ -0,0 +1,101 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics.Contracts; +using Microsoft.Internal; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition.Hosting +{ + /// + /// Provides data for the and + /// events. + /// + public class ComposablePartCatalogChangeEventArgs : EventArgs + { + private readonly IEnumerable _addedDefinitions; + private readonly IEnumerable _removedDefinitions; + + /// + /// Initializes a new instance of the . + /// + /// + /// An of objects that + /// are being added to the . + /// + /// + /// An of objects that + /// are being removed from the . + /// + /// + /// A representing all tentative changes that will + /// be completed if the change is successful, or discarded if it is not. + /// if being applied outside a + /// or during a event. + /// + /// + /// or is . + /// + public ComposablePartCatalogChangeEventArgs(IEnumerable addedDefinitions, + IEnumerable removedDefinitions, AtomicComposition atomicComposition) + { + Requires.NotNull(addedDefinitions, nameof(addedDefinitions)); + Requires.NotNull(removedDefinitions, nameof(removedDefinitions)); + + _addedDefinitions = addedDefinitions.AsArray(); + _removedDefinitions = removedDefinitions.AsArray(); + AtomicComposition = atomicComposition; + } + + /// + /// Gets the identifiers of the parts that have been added. + /// + /// + /// An of objects that + /// have been added to the . + /// + public IEnumerable AddedDefinitions + { + get + { + Contract.Ensures(Contract.Result>() != null); + + return _addedDefinitions; + } + } + + /// + /// Gets the identifiers of the parts that have been removed. + /// + /// + /// An of objects that + /// have been removed from from the . + /// + public IEnumerable RemovedDefinitions + { + get + { + Contract.Ensures(Contract.Result>() != null); + + return _removedDefinitions; + } + } + + /// + /// Gets the atomicComposition, if any, that this change applies to. + /// + /// + /// A that this set of changes applies too. + /// It can be if the changes are being applied outside a + /// or during a + /// event. + /// + /// When the value is non-null it should be used to record temporary changed state + /// and actions that will be executed when the atomicComposition is completeed. + /// + public AtomicComposition AtomicComposition { get; private set; } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ComposablePartCatalogCollection.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ComposablePartCatalogCollection.cs new file mode 100644 index 0000000000..fda081443c --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ComposablePartCatalogCollection.cs @@ -0,0 +1,407 @@ +// 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.Collections; +using System.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using System.Threading; +using Microsoft.Internal; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition.Hosting +{ + /// + /// This class implements a threadsafe ICollection{T} of ComposablePartCatalog. + /// It is exposed as an ICollection(ComposablePartCatalog) + /// It is threadsafe, notifications are not marshalled using a SynchronizationContext. + /// It is Disposable. + /// + internal class ComposablePartCatalogCollection : ICollection, INotifyComposablePartCatalogChanged, IDisposable + { + private readonly Lock _lock = new Lock(); + private Action _onChanged; + private Action _onChanging; + private List _catalogs = new List(); + private volatile bool _isCopyNeeded = false; + private volatile bool _isDisposed = false; + private bool _hasChanged = false; + + public ComposablePartCatalogCollection( + IEnumerable catalogs, + Action onChanged, + Action onChanging) + { + catalogs = catalogs ?? Enumerable.Empty(); + _catalogs = new List(catalogs); + _onChanged = onChanged; + _onChanging = onChanging; + + SubscribeToCatalogNotifications(catalogs); + } + + public void Add(ComposablePartCatalog item) + { + Requires.NotNull(item, nameof(item)); + + ThrowIfDisposed(); + + var addedParts = new Lazy>(() => item.ToArray(), LazyThreadSafetyMode.PublicationOnly); + + using (var atomicComposition = new AtomicComposition()) + { + RaiseChangingEvent(addedParts, null, atomicComposition); + + using (new WriteLock(_lock)) + { + if (_isCopyNeeded) + { + _catalogs = new List(_catalogs); + _isCopyNeeded = false; + } + _hasChanged = true; + _catalogs.Add(item); + } + + SubscribeToCatalogNotifications(item); + + // Complete after the catalog changes are written + atomicComposition.Complete(); + } + + RaiseChangedEvent(addedParts, null); + } + + /// + /// Notify when the contents of the Catalog has changed. + /// + public event EventHandler Changed; + + /// + /// Notify when the contents of the Catalog has changing. + /// + public event EventHandler Changing; + + public void Clear() + { + ThrowIfDisposed(); + + // No action is required if we are already empty + ComposablePartCatalog[] catalogs = null; + using (new ReadLock(_lock)) + { + if (_catalogs.Count == 0) + { + return; + } + catalogs = _catalogs.ToArray(); + } + + // We are doing this outside of the lock, so it's possible that the catalog will continute propagating events from things + // we are about to unsubscribe from. Given the non-specificity of our event, in the worst case scenario we would simply fire + // unnecessary events. + var removedParts = new Lazy>(() => catalogs.SelectMany(catalog => catalog).ToArray(), LazyThreadSafetyMode.PublicationOnly); + + // Validate the changes before applying them + using (var atomicComposition = new AtomicComposition()) + { + RaiseChangingEvent(null, removedParts, atomicComposition); + UnsubscribeFromCatalogNotifications(catalogs); + + using (new WriteLock(_lock)) + { + _catalogs = new List(); + + _isCopyNeeded = false; + _hasChanged = true; + } + + // Complete after the catalog changes are written + atomicComposition.Complete(); + } + + RaiseChangedEvent(null, removedParts); + } + + public bool Contains(ComposablePartCatalog item) + { + Requires.NotNull(item, nameof(item)); + + ThrowIfDisposed(); + + using (new ReadLock(_lock)) + { + return _catalogs.Contains(item); + } + } + + public void CopyTo(ComposablePartCatalog[] array, int arrayIndex) + { + ThrowIfDisposed(); + + using (new ReadLock(_lock)) + { + _catalogs.CopyTo(array, arrayIndex); + } + } + + public int Count + { + get + { + ThrowIfDisposed(); + + using (new ReadLock(_lock)) + { + return _catalogs.Count; + } + } + } + + public bool IsReadOnly + { + get + { + ThrowIfDisposed(); + + return false; + } + } + + public bool Remove(ComposablePartCatalog item) + { + Requires.NotNull(item, nameof(item)); + + ThrowIfDisposed(); + + using (new ReadLock(_lock)) + { + if (!_catalogs.Contains(item)) + { + return false; + } + } + + bool isSuccessfulRemoval = false; + + var removedParts = new Lazy>(() => item.ToArray(), LazyThreadSafetyMode.PublicationOnly); + using (var atomicComposition = new AtomicComposition()) + { + RaiseChangingEvent(null, removedParts, atomicComposition); + + using (new WriteLock(_lock)) + { + if (_isCopyNeeded) + { + _catalogs = new List(_catalogs); + _isCopyNeeded = false; + } + + isSuccessfulRemoval = _catalogs.Remove(item); + if (isSuccessfulRemoval) + { + _hasChanged = true; + } + } + + UnsubscribeFromCatalogNotifications(item); + + // Complete after the catalog changes are written + atomicComposition.Complete(); + } + + RaiseChangedEvent(null, removedParts); + + return isSuccessfulRemoval; + } + + internal bool HasChanged + { + get + { + ThrowIfDisposed(); + + using (new ReadLock(_lock)) + { + return _hasChanged; + } + } + } + + public IEnumerator GetEnumerator() + { + ThrowIfDisposed(); + + using (new WriteLock(_lock)) + { + IEnumerator enumerator = _catalogs.GetEnumerator(); + _isCopyNeeded = true; + return enumerator; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (!_isDisposed) + { + bool disposeLock = false; + IEnumerable catalogs = null; + try + { + using (new WriteLock(_lock)) + { + if (!_isDisposed) + { + disposeLock = true; + + catalogs = _catalogs; + _catalogs = null; + + _isDisposed = true; + } + } + } + finally + { + if (catalogs != null) + { + UnsubscribeFromCatalogNotifications(catalogs); + catalogs.ForEach(catalog => catalog.Dispose()); + } + + if (disposeLock) + { + _lock.Dispose(); + } + } + } + } + } + + private void RaiseChangedEvent( + Lazy> addedDefinitions, + Lazy> removedDefinitions) + { + if (_onChanged == null || Changed == null) + { + return; + } + + var added = (addedDefinitions == null ? Enumerable.Empty() : addedDefinitions.Value); + var removed = (removedDefinitions == null ? Enumerable.Empty() : removedDefinitions.Value); + + _onChanged.Invoke(new ComposablePartCatalogChangeEventArgs(added, removed, null)); + } + + public void OnChanged(object sender, ComposablePartCatalogChangeEventArgs e) + { + var changedEvent = Changed; + if (changedEvent != null) + { + changedEvent(sender, e); + } + } + + private void RaiseChangingEvent( + Lazy> addedDefinitions, + Lazy> removedDefinitions, + AtomicComposition atomicComposition) + { + if (_onChanging == null || Changing == null) + { + return; + } + var added = (addedDefinitions == null ? Enumerable.Empty() : addedDefinitions.Value); + var removed = (removedDefinitions == null ? Enumerable.Empty() : removedDefinitions.Value); + + _onChanging.Invoke(new ComposablePartCatalogChangeEventArgs(added, removed, atomicComposition)); + } + + public void OnChanging(object sender, ComposablePartCatalogChangeEventArgs e) + { + var changingEvent = Changing; + if (changingEvent != null) + { + changingEvent(sender, e); + } + } + + private void OnContainedCatalogChanged(object sender, ComposablePartCatalogChangeEventArgs e) + { + if (_onChanged == null || Changed == null) + { + return; + } + + _onChanged.Invoke(e); + } + + private void OnContainedCatalogChanging(object sender, ComposablePartCatalogChangeEventArgs e) + { + if (_onChanging == null || Changing == null) + { + return; + } + + _onChanging.Invoke(e); + } + + private void SubscribeToCatalogNotifications(ComposablePartCatalog catalog) + { + INotifyComposablePartCatalogChanged notifyCatalog = catalog as INotifyComposablePartCatalogChanged; + if (notifyCatalog != null) + { + notifyCatalog.Changed += OnContainedCatalogChanged; + notifyCatalog.Changing += OnContainedCatalogChanging; + } + } + + private void SubscribeToCatalogNotifications(IEnumerable catalogs) + { + foreach (var catalog in catalogs) + { + SubscribeToCatalogNotifications(catalog); + } + } + + private void UnsubscribeFromCatalogNotifications(ComposablePartCatalog catalog) + { + INotifyComposablePartCatalogChanged notifyCatalog = catalog as INotifyComposablePartCatalogChanged; + if (notifyCatalog != null) + { + notifyCatalog.Changed -= OnContainedCatalogChanged; + notifyCatalog.Changing -= OnContainedCatalogChanging; + } + } + + private void UnsubscribeFromCatalogNotifications(IEnumerable catalogs) + { + foreach (var catalog in catalogs) + { + UnsubscribeFromCatalogNotifications(catalog); + } + } + + private void ThrowIfDisposed() + { + if (_isDisposed) + { + throw ExceptionBuilder.CreateObjectDisposed(this); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ComposablePartExportProvider.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ComposablePartExportProvider.cs new file mode 100644 index 0000000000..b5ba0929d3 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ComposablePartExportProvider.cs @@ -0,0 +1,442 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Threading; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + public class ComposablePartExportProvider : ExportProvider, IDisposable + { + private List _parts = new List(); + private volatile bool _isDisposed = false; + private volatile bool _isRunning = false; + private CompositionLock _lock = null; + private ExportProvider _sourceProvider; + private ImportEngine _importEngine; + private volatile bool _currentlyComposing; + private CompositionOptions _compositionOptions; + + /// + /// Initializes a new instance of the class. + /// + public ComposablePartExportProvider() : + this(false) + { + } + + public ComposablePartExportProvider(bool isThreadSafe) + :this(isThreadSafe ? CompositionOptions.IsThreadSafe : CompositionOptions.Default) + { + } + + public ComposablePartExportProvider(CompositionOptions compositionOptions) + { + if (compositionOptions > (CompositionOptions.DisableSilentRejection | CompositionOptions.IsThreadSafe | CompositionOptions.ExportCompositionService)) + { + throw new ArgumentOutOfRangeException("compositionOptions"); + } + + _compositionOptions = compositionOptions; + _lock = new CompositionLock(compositionOptions.HasFlag(CompositionOptions.IsThreadSafe)); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (!_isDisposed) + { + bool disposeLock = false; + ImportEngine importEngine = null; + try + { + using (_lock.LockStateForWrite()) + { + if (!_isDisposed) + { + importEngine = _importEngine; + _importEngine = null; + + _sourceProvider = null; + _isDisposed = true; + disposeLock = true; + } + } + } + finally + { + if (importEngine != null) + { + importEngine.Dispose(); + } + + if (disposeLock) + { + _lock.Dispose(); + } + } + } + } + } + + /// + /// Gets the export provider which provides the provider access to + /// exports. + /// + /// + /// The which provides the + /// access to objects. + /// The default is . + /// + /// + /// is . + /// + /// + /// This property has already been set. + /// + /// -or- + /// + /// The methods on the + /// have already been accessed. + /// + /// + /// The has been disposed of. + /// + /// + /// This property must be set before accessing any methods on the + /// . + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EnsureCanSet ensures that the property is set only once, Dispose is not required")] + public ExportProvider SourceProvider + { + get + { + ThrowIfDisposed(); + + return _sourceProvider; + } + set + { + ThrowIfDisposed(); + + Requires.NotNull(value, nameof(value)); + using (_lock.LockStateForWrite()) + { + EnsureCanSet(_sourceProvider); + _sourceProvider = value; + } + } + } + + private ImportEngine ImportEngine + { + get + { + if (_importEngine == null) + { + Assumes.NotNull(_sourceProvider); + ImportEngine importEngine = new ImportEngine(_sourceProvider, _compositionOptions); + using (_lock.LockStateForWrite()) + { + if (_importEngine == null) + { + Thread.MemoryBarrier(); + _importEngine = importEngine; + importEngine = null; + } + } + + // if we have created an engine and didn't set it because of a race condition, we need to dispose of it + if (importEngine != null) + { + importEngine.Dispose(); + } + } + + return _importEngine; + } + } + +/// + /// Returns all exports that match the conditions of the specified import. + /// + /// The that defines the conditions of the + /// to get. + /// + /// + /// An of objects that match + /// the conditions defined by , if found; otherwise, an + /// empty . + /// + /// + /// + /// The implementers should not treat the cardinality-related mismatches as errors, and are not + /// expected to throw exceptions in those cases. + /// For instance, if the import requests exactly one export and the provider has no matching exports or more than one, + /// it should return an empty of . + /// + /// + protected override IEnumerable GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition) + { + ThrowIfDisposed(); + EnsureRunning(); + + // Determine whether there is a composition atomicComposition-specific list of parts to use, + // failing that use the usual list. We never change the list of parts in place, + // but rather copy, change and write a new list atomically. Therefore all we need + // to do here is to read the _parts member. + List parts = null; + using (_lock.LockStateForRead()) + { + parts = atomicComposition.GetValueAllowNull(this, _parts); + } + + if (parts.Count == 0) + { + return null; + } + + List exports = new List(); + foreach (var part in parts) + { + foreach (var exportDefinition in part.ExportDefinitions) + { + if (definition.IsConstraintSatisfiedBy(exportDefinition)) + { + exports.Add(CreateExport(part, exportDefinition)); + } + } + } + return exports; + } + + public void Compose(CompositionBatch batch) + { + ThrowIfDisposed(); + EnsureRunning(); + + Requires.NotNull(batch, nameof(batch)); + + // Quick exit test can be done prior to cloning since it's just an optimization, not a + // change in behavior + if ((batch.PartsToAdd.Count == 0) && (batch.PartsToRemove.Count == 0)) + { + return; + } + + CompositionResult result = CompositionResult.SucceededResult; + + // Get updated parts list and a cloned batch + var newParts = GetUpdatedPartsList(ref batch); + + // Allow only recursive calls from the import engine to see the changes until + // they've been verified ... + using (var atomicComposition = new AtomicComposition()) + { + // Don't allow reentrant calls to compose during previewing to prevent + // corrupted state. + if (_currentlyComposing) + { + throw new InvalidOperationException(SR.ReentrantCompose); + } + + _currentlyComposing = true; + + try + { + // In the meantime recursive calls need to be able to see the list as well + atomicComposition.SetValue(this, newParts); + + // Recompose any existing imports effected by the these changes first so that + // adapters, resurrected parts, etc. can all play their role in satisfying + // imports for added parts + Recompose(batch, atomicComposition); + + // Ensure that required imports can be satisfied + foreach (ComposablePart part in batch.PartsToAdd) + { + // collect the result of previewing all the adds in the batch + try + { + ImportEngine.PreviewImports(part, atomicComposition); + } + catch (ChangeRejectedException ex) + { + result = result.MergeResult(new CompositionResult(ex.Errors)); + } + } + + result.ThrowOnErrors(atomicComposition); + + // Complete the new parts since they passed previewing.` + using (_lock.LockStateForWrite()) + { + _parts = newParts; + } + + atomicComposition.Complete(); + } + finally + { + _currentlyComposing = false; + } + } + + // Satisfy Imports + // - Satisfy imports on all newly added component parts + foreach (ComposablePart part in batch.PartsToAdd) + { + result = result.MergeResult(CompositionServices.TryInvoke(() => + ImportEngine.SatisfyImports(part))); + } + + // return errors + result.ThrowOnErrors(); + } + + private List GetUpdatedPartsList(ref CompositionBatch batch) + { + Assumes.NotNull(batch); + + // Copy the current list of parts - we are about to modify it + // This is an OK thing to do as this is the only method that can modify the List AND Compose can + // only be executed on one thread at a time - thus two different threads cannot tramp over each other + List parts = null; + using (_lock.LockStateForRead()) + { + parts = _parts.ToList(); // this copies the list + } + + foreach (ComposablePart part in batch.PartsToAdd) + { + parts.Add(part); + } + + List partsToRemove = null; + + foreach (ComposablePart part in batch.PartsToRemove) + { + if (parts.Remove(part)) + { + if (partsToRemove == null) + { + partsToRemove = new List(); + } + partsToRemove.Add(part); + } + } + + // Clone the batch, so that the external changes wouldn't happen half-way thorugh compose + // NOTE : this does not guarantee the atomicity of cloning, which is not the goal anyway, + // rather the fact that all subsequent calls will deal with an unchanging batch + batch = new CompositionBatch(batch.PartsToAdd, partsToRemove); + + return parts; + } + + private void Recompose(CompositionBatch batch, AtomicComposition atomicComposition) + { + Assumes.NotNull(batch); + + // Unregister any removed component parts + foreach (ComposablePart part in batch.PartsToRemove) + { + ImportEngine.ReleaseImports(part, atomicComposition); + } + + // Recompose any imports effected by the these changes (the changes are + // observable through GetExports in the appropriate atomicComposition, thus we can fire + // the event + IEnumerable addedExports = batch.PartsToAdd.Count != 0 ? + batch.PartsToAdd.SelectMany(part => part.ExportDefinitions).ToArray() : + new ExportDefinition[0]; + + IEnumerable removedExports = batch.PartsToRemove.Count != 0 ? + batch.PartsToRemove.SelectMany(part => part.ExportDefinitions).ToArray() : + new ExportDefinition[0]; + + OnExportsChanging( + new ExportsChangeEventArgs(addedExports, removedExports, atomicComposition)); + + atomicComposition.AddCompleteAction(() => OnExportsChanged( + new ExportsChangeEventArgs(addedExports, removedExports, null))); + } + + private Export CreateExport(ComposablePart part, ExportDefinition export) + { + return new Export(export, () => GetExportedValue(part, export)); + } + + private object GetExportedValue(ComposablePart part, ExportDefinition export) + { + ThrowIfDisposed(); + EnsureRunning(); + + return CompositionServices.GetExportedValueFromComposedPart(ImportEngine, part, export); + } + + [DebuggerStepThrough] + private void ThrowIfDisposed() + { + if (_isDisposed) + { + throw new ObjectDisposedException(GetType().Name); + } + } + + [DebuggerStepThrough] + private void EnsureCanRun() + { + if (_sourceProvider == null) + { + throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.ObjectMustBeInitialized, "SourceProvider")); // NOLOC + } + } + + [DebuggerStepThrough] + private void EnsureRunning() + { + if (!_isRunning) + { + using (_lock.LockStateForWrite()) + { + if (!_isRunning) + { + EnsureCanRun(); + _isRunning = true; + } + } + } + } + + [DebuggerStepThrough] + private void EnsureCanSet(T currentValue) + where T : class + { + if ((_isRunning) || (currentValue != null)) + { + throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.ObjectAlreadyInitialized)); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionBatch.SingleExportComposablePart.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionBatch.SingleExportComposablePart.cs new file mode 100644 index 0000000000..3ff5e74034 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionBatch.SingleExportComposablePart.cs @@ -0,0 +1,62 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + partial class CompositionBatch + { + // Represents a part that exports a single export + private class SingleExportComposablePart : ComposablePart + { + private readonly Export _export; + + public SingleExportComposablePart(Export export) + { + Assumes.NotNull(export); + + _export = export; + } + + public override IDictionary Metadata + { + get { return MetadataServices.EmptyMetadata; } + } + + public override IEnumerable ExportDefinitions + { + get { return new ExportDefinition[] { _export.Definition }; } + } + + public override IEnumerable ImportDefinitions + { + get { return Enumerable.Empty(); } + } + + public override object GetExportedValue(ExportDefinition definition) + { + Requires.NotNull(definition, nameof(definition)); + + if (definition != _export.Definition) + { + throw ExceptionBuilder.CreateExportDefinitionNotOnThisComposablePart("definition"); + } + + return _export.Value; + } + + public override void SetImport(ImportDefinition definition, IEnumerable exports) + { + Requires.NotNull(definition, nameof(definition)); + Requires.NotNullOrNullElements(exports, "exports"); + + throw ExceptionBuilder.CreateImportDefinitionNotOnThisComposablePart("definition"); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionBatch.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionBatch.cs new file mode 100644 index 0000000000..aea7b5a760 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionBatch.cs @@ -0,0 +1,178 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics.Contracts; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + public partial class CompositionBatch + { + private object _lock = new object(); + private bool _copyNeededForAdd; + private bool _copyNeededForRemove; + private List _partsToAdd; + private ReadOnlyCollection _readOnlyPartsToAdd; + private List _partsToRemove; + private ReadOnlyCollection _readOnlyPartsToRemove; + + /// + /// Initializes a new instance of the class. + /// + public CompositionBatch() : + this(null, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The parts to add. + /// The parts to remove. + public CompositionBatch(IEnumerable partsToAdd, IEnumerable partsToRemove) + { + _partsToAdd = new List(); + if (partsToAdd != null) + { + foreach (var part in partsToAdd) + { + if (part == null) + { + throw ExceptionBuilder.CreateContainsNullElement("partsToAdd"); + } + _partsToAdd.Add(part); + } + } + _readOnlyPartsToAdd = _partsToAdd.AsReadOnly(); + + _partsToRemove = new List(); + if (partsToRemove != null) + { + foreach (var part in partsToRemove) + { + if (part == null) + { + throw ExceptionBuilder.CreateContainsNullElement("partsToRemove"); + } + _partsToRemove.Add(part); + } + } + _readOnlyPartsToRemove = _partsToRemove.AsReadOnly(); + } + + /// + /// Returns the collection of parts that will be added. + /// + /// The parts to be added. + public ReadOnlyCollection PartsToAdd + { + get + { + Contract.Ensures(Contract.Result>() != null); + + lock (_lock) + { + _copyNeededForAdd = true; + return _readOnlyPartsToAdd; + } + } + } + + /// + /// Returns the collection of parts that will be removed. + /// + /// The parts to be removed. + public ReadOnlyCollection PartsToRemove + { + get + { + Contract.Ensures(Contract.Result >() != null); + + lock (_lock) + { + _copyNeededForRemove = true; + return _readOnlyPartsToRemove; + } + } + } + + /// + /// Adds the specified part to the . + /// + /// + /// The part. + /// + /// + /// is . + /// + public void AddPart(ComposablePart part) + { + Requires.NotNull(part, nameof(part)); + lock (_lock) + { + if (_copyNeededForAdd) + { + _partsToAdd = new List(_partsToAdd); + _readOnlyPartsToAdd = _partsToAdd.AsReadOnly(); + _copyNeededForAdd = false; + } + _partsToAdd.Add(part); + } + } + + /// + /// Removes the specified part from the . + /// + /// + /// The part. + /// + /// + /// is . + /// + public void RemovePart(ComposablePart part) + { + Requires.NotNull(part, nameof(part)); + lock (_lock) + { + if (_copyNeededForRemove) + { + _partsToRemove = new List(_partsToRemove); + _readOnlyPartsToRemove = _partsToRemove.AsReadOnly(); + _copyNeededForRemove = false; + } + _partsToRemove.Add(part); + } + } + + /// + /// Adds the specified export to the . + /// + /// + /// The to add to the . + /// + /// + /// A that can be used remove the + /// from the . + /// + /// + /// is . + /// + /// + /// + public ComposablePart AddExport(Export export) + { + Requires.NotNull(export, nameof(export)); + Contract.Ensures(Contract.Result() != null); + + ComposablePart part = new SingleExportComposablePart(export); + + AddPart(part); + + return part; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionConstants.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionConstants.cs new file mode 100644 index 0000000000..958c470c44 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionConstants.cs @@ -0,0 +1,31 @@ +// 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.ComponentModel.Composition.Primitives; + +namespace System.ComponentModel.Composition.Hosting +{ + public static class CompositionConstants + { + private const string CompositionNamespace = "System.ComponentModel.Composition"; + + public const string PartCreationPolicyMetadataName = CompositionNamespace + ".CreationPolicy"; + public const string ImportSourceMetadataName = CompositionNamespace + ".ImportSource"; + public const string IsGenericPartMetadataName = CompositionNamespace + ".IsGenericPart"; + public const string GenericContractMetadataName = CompositionNamespace + ".GenericContractName"; + public const string GenericParametersMetadataName = CompositionNamespace + ".GenericParameters"; + public const string ExportTypeIdentityMetadataName = "ExportTypeIdentity"; + + internal const string GenericImportParametersOrderMetadataName = CompositionNamespace + ".GenericImportParametersOrderMetadataName"; + internal const string GenericExportParametersOrderMetadataName = CompositionNamespace + ".GenericExportParametersOrderMetadataName"; + internal const string GenericPartArityMetadataName = CompositionNamespace + ".GenericPartArity"; + internal const string GenericParameterConstraintsMetadataName = CompositionNamespace + ".GenericParameterConstraints"; + internal const string GenericParameterAttributesMetadataName = CompositionNamespace + ".GenericParameterAttributes"; + + internal const string ProductDefinitionMetadataName = "ProductDefinition"; + + internal const string PartCreatorContractName = CompositionNamespace + ".Contracts.ExportFactory"; + internal static readonly string PartCreatorTypeIdentity = AttributedModelServices.GetTypeIdentity(typeof(ComposablePartDefinition)); + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionContainer.CompositionServiceShim.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionContainer.CompositionServiceShim.cs new file mode 100644 index 0000000000..99d79b4eae --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionContainer.CompositionServiceShim.cs @@ -0,0 +1,28 @@ +// 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.ComponentModel.Composition.Primitives; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + public partial class CompositionContainer + { + private class CompositionServiceShim : ICompositionService + { + CompositionContainer _innerContainer = null; + + public CompositionServiceShim(CompositionContainer innerContainer) + { + Assumes.NotNull(innerContainer); + _innerContainer = innerContainer; + } + + void ICompositionService.SatisfyImportsOnce(ComposablePart part) + { + _innerContainer.SatisfyImportsOnce(part); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionContainer.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionContainer.cs new file mode 100644 index 0000000000..69375e8066 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionContainer.cs @@ -0,0 +1,642 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; +using System.Threading; +using Microsoft.Internal; + +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(destination: typeof(System.Lazy<,>))] + +namespace System.ComponentModel.Composition.Hosting +{ + public partial class CompositionContainer : ExportProvider, ICompositionService, IDisposable + { + private CompositionOptions _compositionOptions; + private ImportEngine _importEngine; + private ComposablePartExportProvider _partExportProvider; + private ExportProvider _rootProvider; + private IDisposable _disposableRootProvider; + private CatalogExportProvider _catalogExportProvider; + private ExportProvider _localExportProvider; + private IDisposable _disposableLocalExportProvider; + private ExportProvider _ancestorExportProvider; + private IDisposable _disposableAncestorExportProvider; + + private readonly ReadOnlyCollection _providers; + private volatile bool _isDisposed = false; + private object _lock = new object(); + private static ReadOnlyCollection EmptyProviders = new ReadOnlyCollection(new ExportProvider[]{}); + + /// + /// Initializes a new instance of the class. + /// + public CompositionContainer() + : this((ComposablePartCatalog)null) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified export providers. + /// + /// + /// A of objects which provide + /// the access to objects, + /// or to set to an empty + /// . + /// + /// + /// contains an element that is . + /// + public CompositionContainer(params ExportProvider[] providers) : + this((ComposablePartCatalog)null, providers) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified export providers. + /// + /// + /// enumeration with flags controlling the composition. + /// + /// + /// A of objects which provide + /// the access to objects, + /// or to set to an empty + /// . + /// + /// + /// contains an element that is . + /// + public CompositionContainer(CompositionOptions compositionOptions, params ExportProvider[] providers) : + this((ComposablePartCatalog)null, compositionOptions, providers) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified catalog and export providers. + /// + /// + /// A of objects which provide + /// the access to objects, + /// or to set to an empty + /// . + /// + /// + /// contains an element that is . + /// + public CompositionContainer(ComposablePartCatalog catalog, params ExportProvider[] providers): + this(catalog, false, providers) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified catalog and export providers. + /// + /// + /// indicates whether container instances are threadsafe. + /// + /// + /// A of objects which provide + /// the access to objects, + /// or to set to an empty + /// . + /// + /// + /// contains an element that is . + /// + public CompositionContainer(ComposablePartCatalog catalog, bool isThreadSafe, params ExportProvider[] providers) + : this(catalog, isThreadSafe ? CompositionOptions.IsThreadSafe : CompositionOptions.Default, providers) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified catalog and export providers. + /// + /// + /// enumeration with flags controlling the composition. + /// + /// + /// A of objects which provide + /// the access to objects, + /// or to set to an empty + /// . + /// + /// + /// contains an element that is . + /// + public CompositionContainer(ComposablePartCatalog catalog, CompositionOptions compositionOptions, params ExportProvider[] providers) + { + if (compositionOptions > (CompositionOptions.DisableSilentRejection | CompositionOptions.IsThreadSafe | CompositionOptions.ExportCompositionService)) + { + throw new ArgumentOutOfRangeException("compositionOptions"); + } + _compositionOptions = compositionOptions; + + // We always create the mutable provider + _partExportProvider = new ComposablePartExportProvider(compositionOptions); + _partExportProvider.SourceProvider = this; + + // Create the catalog export provider, only if necessary + if (catalog != null) + { + _catalogExportProvider = new CatalogExportProvider(catalog, compositionOptions); + _catalogExportProvider.SourceProvider = this; + } + + // Set the local export provider + if (_catalogExportProvider != null) + { + _localExportProvider = new AggregateExportProvider(_partExportProvider, _catalogExportProvider); + _disposableLocalExportProvider = _localExportProvider as IDisposable; + } + else + { + _localExportProvider = _partExportProvider; + } + + // Set the ancestor export provider, if ancestors are supplied + if ((providers != null) && (providers.Length > 0)) + { + // Aggregate ancestors if and only if more than one passed + if (providers.Length > 1) + { + _ancestorExportProvider = new AggregateExportProvider(providers); + _disposableAncestorExportProvider = _ancestorExportProvider as IDisposable; + } + else + { + if (providers[0] == null) + { + throw ExceptionBuilder.CreateContainsNullElement("providers"); + } + _ancestorExportProvider = providers[0]; + } + } + + // finally set the root provider + if (_ancestorExportProvider == null) + { + // if no ancestors are passed, the local and the root are the same + _rootProvider = _localExportProvider; + } + else + { + int exportProviderCount = 1 + ((catalog != null) ? 1 : 0) + ((providers != null) ? providers.Length : 0); + ExportProvider[] rootProviders = new ExportProvider[exportProviderCount]; + + rootProviders[0] = _partExportProvider; + int customProviderStartIndex = 1; + if (catalog != null) + { + rootProviders[1] = _catalogExportProvider; + customProviderStartIndex = 2; + } + + if (providers != null) + { + for (int i = 0; i < providers.Length; i++) + { + rootProviders[customProviderStartIndex + i] = providers[i]; + } + } + + _rootProvider = new AggregateExportProvider(rootProviders); + _disposableRootProvider = _rootProvider as IDisposable; + } + +//Insert Composition Service + if(compositionOptions.HasFlag(CompositionOptions.ExportCompositionService)) + { + this.ComposeExportedValue(new CompositionServiceShim(this)); + } + + _rootProvider.ExportsChanged += OnExportsChangedInternal; + _rootProvider.ExportsChanging += OnExportsChangingInternal; + + _providers = (providers != null) ? new ReadOnlyCollection((ExportProvider[])providers.Clone()) : EmptyProviders; + } + + internal CompositionOptions CompositionOptions + { + get + { + ThrowIfDisposed(); + return _compositionOptions; + } + } + + /// + /// Gets the catalog which provides the container access to exports produced + /// from composable parts. + /// + /// + /// The which provides the + /// access to exports produced from + /// objects. The default is . + /// + /// + /// The has been disposed of. + /// + public ComposablePartCatalog Catalog + { + get + { + ThrowIfDisposed(); + + return (_catalogExportProvider != null) ? _catalogExportProvider.Catalog : null; + } + } + + internal CatalogExportProvider CatalogExportProvider + { + get + { + ThrowIfDisposed(); + + return _catalogExportProvider; + } + } + + /// + /// Gets the export providers which provide the container access to additional exports. + /// + /// + /// A of objects + /// which provide the access to additional + /// objects. The default is an empty + /// . + /// + /// + /// The has been disposed of. + /// + public ReadOnlyCollection Providers + { + get + { + ThrowIfDisposed(); + Contract.Ensures(Contract.Result>() != null); + + return _providers; + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (!_isDisposed) + { + ExportProvider rootProvider = null; + IDisposable disposableAncestorExportProvider = null; + IDisposable disposableLocalExportProvider = null; + IDisposable disposableRootProvider = null; + ComposablePartExportProvider partExportProvider = null; + CatalogExportProvider catalogExportProvider = null; + ImportEngine importEngine = null; + + lock(_lock) + { + if (!_isDisposed) + { + rootProvider = _rootProvider; + _rootProvider = null; + + disposableRootProvider = _disposableRootProvider; + _disposableRootProvider = null; + + disposableLocalExportProvider = _disposableLocalExportProvider ; + _disposableLocalExportProvider = null; + _localExportProvider = null; + + disposableAncestorExportProvider = _disposableAncestorExportProvider; + _disposableAncestorExportProvider = null; + _ancestorExportProvider = null; + + partExportProvider = _partExportProvider; + _partExportProvider = null; + + catalogExportProvider = _catalogExportProvider; + _catalogExportProvider = null; + + importEngine = _importEngine; + _importEngine = null; + + _isDisposed = true; + } + } + + if (rootProvider != null) + { + rootProvider.ExportsChanged -= OnExportsChangedInternal; + rootProvider.ExportsChanging -= OnExportsChangingInternal; + } + + if (disposableRootProvider != null) + { + disposableRootProvider.Dispose(); + } + + if (disposableAncestorExportProvider != null) + { + disposableAncestorExportProvider.Dispose(); + } + + if (disposableLocalExportProvider != null) + { + disposableLocalExportProvider.Dispose(); + } + + if (catalogExportProvider != null) + { + catalogExportProvider.Dispose(); + } + + if (partExportProvider != null) + { + partExportProvider.Dispose(); + } + + if (importEngine != null) + { + importEngine.Dispose(); + } + } + + } + } + + public void Compose(CompositionBatch batch) + { + Requires.NotNull(batch, nameof(batch)); + ThrowIfDisposed(); + + _partExportProvider.Compose(batch); + } + + /// + /// Releases the from the . The behavior + /// may vary depending on the implementation of the that produced + /// the instance. As a general rule non shared exports should be early + /// released causing them to be detached from the container. + /// + /// For example the will only release + /// an if it comes from a that was constructed + /// under a context. Release in this context means walking + /// the dependency chain of the s, detaching references from the container and + /// calling Dispose on the s as needed. If the + /// was constructed under a context the + /// will do nothing as it may be in use by other requestors. + /// Those will only be detached when the container is itself disposed. + /// + /// that needs to be released. + /// + /// is . + /// + [SuppressMessage("Microsoft.Performance", "CA1822")] + public void ReleaseExport(Export export) + { + Requires.NotNull(export, nameof(export)); + + IDisposable dependency = export as IDisposable; + + if (dependency != null) + { + dependency.Dispose(); + } + } + + /// + /// Releases the from the . The behavior + /// may vary depending on the implementation of the that produced + /// the instance. As a general rule non shared exports should be early + /// released causing them to be detached from the container. + /// + /// For example the will only release + /// an if it comes from a that was constructed + /// under a context. Release in this context means walking + /// the dependency chain of the s, detaching references from the container and + /// calling Dispose on the s as needed. If the + /// was constructed under a context the + /// will do nothing as it may be in use by other requestors. + /// Those will only be detached when the container is itself disposed. + /// + /// that needs to be released. + /// + /// is . + /// + [SuppressMessage("Microsoft.Performance", "CA1822")] + public void ReleaseExport(Lazy export) + { + Requires.NotNull(export, nameof(export)); + + IDisposable dependency = export as IDisposable; + + if (dependency != null) + { + dependency.Dispose(); + } + } + + /// + /// Releases a set of s from the . + /// See also . + /// + /// s that need to be released. + /// + /// is . + /// + /// + /// contains an element that is . + /// + public void ReleaseExports(IEnumerable exports) + { + Requires.NotNullOrNullElements(exports, "exports"); + + foreach (Export export in exports) + { + ReleaseExport(export); + } + } + + /// + /// Releases a set of s from the . + /// See also . + /// + /// s that need to be released. + /// + /// is . + /// + /// + /// contains an element that is . + /// + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public void ReleaseExports(IEnumerable> exports) + { + Requires.NotNullOrNullElements(exports, "exports"); + + foreach (Lazy export in exports) + { + ReleaseExport(export); + } + } + + /// + /// Releases a set of s from the . + /// See also . + /// + /// s that need to be released. + /// + /// is . + /// + /// + /// contains an element that is . + /// + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public void ReleaseExports(IEnumerable> exports) + { + Requires.NotNullOrNullElements(exports, "exports"); + + foreach (Lazy export in exports) + { + ReleaseExport(export); + } + } + + /// + /// Sets the imports of the specified composable part exactly once and they will not + /// ever be recomposed. + /// + /// + /// The to set the imports. + /// + /// + /// is . + /// + /// + /// An error occurred during composition. will + /// contain a collection of errors that occurred. + /// + /// + /// The has been disposed of. + /// + public void SatisfyImportsOnce(ComposablePart part) + { + ThrowIfDisposed(); + + if (_importEngine == null) + { + ImportEngine importEngine = new ImportEngine(this, _compositionOptions); + + lock(_lock) + { + if (_importEngine == null) + { + Thread.MemoryBarrier(); + _importEngine = importEngine; + importEngine = null; + } + } + if(importEngine != null) + { + importEngine.Dispose(); + } + } + _importEngine.SatisfyImportsOnce(part); + } + + internal void OnExportsChangedInternal(object sender, ExportsChangeEventArgs e) + { + OnExportsChanged(e); + } + + internal void OnExportsChangingInternal(object sender, ExportsChangeEventArgs e) + { + OnExportsChanging(e); + } + + /// + /// Returns all exports that match the conditions of the specified import. + /// + /// The that defines the conditions of the + /// to get. + /// + /// + /// An of objects that match + /// the conditions defined by , if found; otherwise, an + /// empty . + /// + /// + /// + /// The implementers should not treat the cardinality-related mismatches as errors, and are not + /// expected to throw exceptions in those cases. + /// For instance, if the import requests exactly one export and the provider has no matching exports or more than one, + /// it should return an empty of . + /// + /// + protected override IEnumerable GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition) + { + ThrowIfDisposed(); + + IEnumerable exports = null; + + object source; + if(!definition.Metadata.TryGetValue(CompositionConstants.ImportSourceMetadataName, out source)) + { + source = ImportSource.Any; + } + + switch((ImportSource)source) + { + case ImportSource.Any: + Assumes.NotNull(_rootProvider); + _rootProvider.TryGetExports(definition, atomicComposition, out exports); + break; + case ImportSource.Local: + Assumes.NotNull(_localExportProvider); + _localExportProvider.TryGetExports(definition.RemoveImportSource(), atomicComposition, out exports); + break; + case ImportSource.NonLocal: + if(_ancestorExportProvider != null) + { + _ancestorExportProvider.TryGetExports(definition.RemoveImportSource(), atomicComposition, out exports); + } + break; + } + + return exports; + } + + [DebuggerStepThrough] + private void ThrowIfDisposed() + { + if (_isDisposed) + { + throw ExceptionBuilder.CreateObjectDisposed(this); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionLock.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionLock.cs new file mode 100644 index 0000000000..8ab09d0150 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionLock.cs @@ -0,0 +1,147 @@ +// 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. + +#define SINGLETHREADEDLOCKENFORCEMENT +using System.Threading; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + // This a a lock class that needs to be held in order to perform any mutation of the parts/parts state in the composition + // Today's implementation relies on the AppDomain-wide re-entrant lock for changes on the composition, and a narrow lock for changes in + // the state of the specific ImportEngine + // Today we make several assumptions to ensure thread-safety: + // 1. Each composition doesn't change lock affinity + // 2. Every part of the system that updates the status of the parts (in our case ImportEngine) needs to hold the same wide - lock + // 3. State of the import engine that gets accessed outside of the wide lock needs to be accessed in the context of the narrow lock + // 4. Narrow lock CAN be taken inside the wide lock + // 5. Wide lock CANNOT be taken inside the narrow lock + // 6. No 3rd party code will EVER get called inside the narrow lock + // Sadly, this means that we WILL be calling 3rd party code under a lock, but as long as the lock is re-entrant and they can't invoke us on anotehr thread + // we have no issue, other than potential overlocking + internal sealed class CompositionLock : IDisposable + { + // narrow lock + private readonly Lock _stateLock = null; + // wide lock + private static object _compositionLock = new object(); + + private int _isDisposed = 0; + private bool _isThreadSafe = false; + + private static readonly EmptyLockHolder _EmptyLockHolder = new EmptyLockHolder(); + + public CompositionLock(bool isThreadSafe) + { + _isThreadSafe = isThreadSafe; + if (isThreadSafe) + { + _stateLock = new Lock(); + } + } + + public void Dispose() + { + if (_isThreadSafe) + { + if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0) + { + _stateLock.Dispose(); + } + } + } + + public bool IsThreadSafe + { + get + { + return _isThreadSafe; + } + } + + private void EnterCompositionLock() + { +#pragma warning disable 618 + if (_isThreadSafe) + { + Monitor.Enter(_compositionLock); + } +#pragma warning restore 618 + } + + private void ExitCompositionLock() + { + if (_isThreadSafe) + { + Monitor.Exit(_compositionLock); + } + } + + public IDisposable LockComposition() + { + if (_isThreadSafe) + { + return new CompositionLockHolder(this); + } + else + { + return _EmptyLockHolder; + } + } + + public IDisposable LockStateForRead() + { + if (_isThreadSafe) + { + return new ReadLock(_stateLock); + } + else + { + return _EmptyLockHolder; + } + } + + public IDisposable LockStateForWrite() + { + if (_isThreadSafe) + { + return new WriteLock(_stateLock); + } + else + { + return _EmptyLockHolder; + } + } + + // NOTE : this should NOT be changed to a struct as ImportEngine relies on it + public sealed class CompositionLockHolder : IDisposable + { + private CompositionLock _lock; + private int _isDisposed; + + public CompositionLockHolder(CompositionLock @lock) + { + _lock = @lock; + + _isDisposed = 0; + _lock.EnterCompositionLock(); + } + + public void Dispose() + { + if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0) + { + _lock.ExitCompositionLock(); + } + } + } + + private sealed class EmptyLockHolder : IDisposable + { + public void Dispose() + { + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionOptions.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionOptions.cs new file mode 100644 index 0000000000..3a5d19054c --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionOptions.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.ComponentModel.Composition.Hosting +{ + /// + /// Defines the Constructor settings for export providers. + /// + [Flags] + public enum CompositionOptions + { + Default = 0x0000, + DisableSilentRejection = 0x0001, + IsThreadSafe = 0x0002, + ExportCompositionService = 0x0004 + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionScopeDefinition.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionScopeDefinition.cs new file mode 100644 index 0000000000..6bd6af2df4 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionScopeDefinition.cs @@ -0,0 +1,268 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + [DebuggerTypeProxy(typeof(CompositionScopeDefinitionDebuggerProxy))] + public class CompositionScopeDefinition : ComposablePartCatalog, INotifyComposablePartCatalogChanged + { + private ComposablePartCatalog _catalog; + private IEnumerable _publicSurface = null; + private IEnumerable _children = Enumerable.Empty(); + private volatile int _isDisposed = 0; + + /// + /// Initializes a new instance of the class. + /// + protected CompositionScopeDefinition() { } + + /// + /// Initializes a new instance of the class. + /// + /// The catalog. + /// The children. + public CompositionScopeDefinition(ComposablePartCatalog catalog, IEnumerable children) + { + Requires.NotNull(catalog, nameof(catalog)); + Requires.NullOrNotNullElements(children, "children"); + + InitializeCompositionScopeDefinition(catalog, children, null); + } + +/// + /// Initializes a new instance of the class. + /// + /// The catalog. + /// The children. + /// The exports that can be used to create new scopes. + public CompositionScopeDefinition(ComposablePartCatalog catalog, IEnumerable children, IEnumerable publicSurface) + { + Requires.NotNull(catalog, nameof(catalog)); + Requires.NullOrNotNullElements(children, "children"); + Requires.NullOrNotNullElements(publicSurface, "publicSurface"); + + InitializeCompositionScopeDefinition(catalog, children, publicSurface); + } + +/// + /// Initializes a new instance of the class. + /// + /// The catalog. + /// The children. + private void InitializeCompositionScopeDefinition(ComposablePartCatalog catalog, IEnumerable children, IEnumerable publicSurface) + { + _catalog = catalog; + if (children != null) + { + _children = children.ToArray(); + } + if(publicSurface != null) + { + _publicSurface = publicSurface; + } + + INotifyComposablePartCatalogChanged notifyCatalog = _catalog as INotifyComposablePartCatalogChanged; + if (notifyCatalog != null) + { + notifyCatalog.Changed += OnChangedInternal; + notifyCatalog.Changing += OnChangingInternal; + } + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected override void Dispose(bool disposing) + { + try + { + if (disposing) + { + // NOTE : According to http://msdn.microsoft.com/en-us/library/4bw5ewxy.aspx, the warning is bogus when used with Interlocked API. +#pragma warning disable 420 + if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0) +#pragma warning restore 420 + { + INotifyComposablePartCatalogChanged notifyCatalog = _catalog as INotifyComposablePartCatalogChanged; + if (notifyCatalog != null) + { + notifyCatalog.Changed -= OnChangedInternal; + notifyCatalog.Changing -= OnChangingInternal; + } + } + } + } + finally + { + base.Dispose(disposing); + } + } + + /// + /// Gets the children. + /// + /// The children. + public virtual IEnumerable Children + { + get + { + ThrowIfDisposed(); + + return _children; + } + } + + /// + /// Gets the export definitions that describe the exports surfaced by the CompositionScopedefinition. + /// + /// + /// An of objects describing + /// the exports surfaced by the . + /// + /// + /// + /// Overriders of this property must not return . + /// + /// + public virtual IEnumerable PublicSurface + { + get + { + ThrowIfDisposed(); + if(_publicSurface == null) + { + return this.SelectMany( (p) => p.ExportDefinitions ); + } + + return _publicSurface; + } + } + + /// + /// Gets an Enumerator for the ComposablePartDefinitions + /// + /// The children. + public override IEnumerator GetEnumerator() + { + return _catalog.GetEnumerator(); + } + + /// + /// Returns the export definitions that match the constraint defined by the specified definition. + /// + /// The that defines the conditions of the + /// objects to return. + /// + /// An of containing the + /// objects and their associated + /// for objects that match the constraint defined + /// by . + /// + /// + /// is . + /// + /// + /// The has been disposed of. + /// + /// + /// + /// Overriders of this property should never return , if no + /// match the conditions defined by + /// , return an empty . + /// + /// + public override IEnumerable> GetExports(ImportDefinition definition) + { + ThrowIfDisposed(); + + return _catalog.GetExports(definition); + } + + internal IEnumerable> GetExportsFromPublicSurface(ImportDefinition definition) + { + Assumes.NotNull(definition, "definition"); + + var exports = new List>(); + + foreach(var exportDefinition in PublicSurface) + { + if (definition.IsConstraintSatisfiedBy(exportDefinition)) + { + foreach (var export in GetExports(definition)) + { + if(export.Item2 == exportDefinition) + { + exports.Add(export); + break; + } + } + } + } + return exports; + } + + /// + /// Notify when the contents of the Catalog has changed. + /// + public event EventHandler Changed; + + /// + /// Notify when the contents of the Catalog is changing. + /// + public event EventHandler Changing; + + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected virtual void OnChanged(ComposablePartCatalogChangeEventArgs e) + { + EventHandler changedEvent = Changed; + if (changedEvent != null) + { + changedEvent.Invoke(this, e); + } + } + + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected virtual void OnChanging(ComposablePartCatalogChangeEventArgs e) + { + EventHandler changingEvent = Changing; + if (changingEvent != null) + { + changingEvent.Invoke(this, e); + } + } + + private void OnChangedInternal(object sender, ComposablePartCatalogChangeEventArgs e) + { + OnChanged(e); + } + + private void OnChangingInternal(object sender, ComposablePartCatalogChangeEventArgs e) + { + OnChanging(e); + } + + [DebuggerStepThrough] + private void ThrowIfDisposed() + { + if (_isDisposed == 1) + { + throw ExceptionBuilder.CreateObjectDisposed(this); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionScopeDefinitionDebuggerProxy.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionScopeDefinitionDebuggerProxy.cs new file mode 100644 index 0000000000..9a25927d84 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionScopeDefinitionDebuggerProxy.cs @@ -0,0 +1,48 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.Composition.Primitives; +using Microsoft.Internal; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition.Hosting +{ + // This proxy is needed to pretty up CompositionScopeDefinitionCatalog.Parts; IQueryable + // instances are not displayed in a very friendly way in the debugger. + internal class CompositionScopeDefinitionDebuggerProxy + { + private readonly CompositionScopeDefinition _compositionScopeDefinition; + + public CompositionScopeDefinitionDebuggerProxy(CompositionScopeDefinition compositionScopeDefinition) + { + Requires.NotNull(compositionScopeDefinition, nameof(compositionScopeDefinition)); + + _compositionScopeDefinition = compositionScopeDefinition; + } + + public ReadOnlyCollection Parts + { + get { return _compositionScopeDefinition.Parts.ToReadOnlyCollection(); } + } + + public IEnumerable PublicSurface + { + get + { + return _compositionScopeDefinition.PublicSurface.ToReadOnlyCollection(); + } + } + + public virtual IEnumerable Children + { + get + { + return _compositionScopeDefinition.Children.ToReadOnlyCollection(); + } + } + +} +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionService.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionService.cs new file mode 100644 index 0000000000..e4139e092a --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionService.cs @@ -0,0 +1,71 @@ +// 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.ComponentModel.Composition.Primitives; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + /// + /// A mutable collection of s. + /// + /// + /// This type is thread safe. + /// + public class CompositionService : ICompositionService, IDisposable + { + private CompositionContainer _compositionContainer = null; + private INotifyComposablePartCatalogChanged _notifyCatalog = null; + + internal CompositionService(ComposablePartCatalog composablePartCatalog) + { + Assumes.NotNull(composablePartCatalog); + _notifyCatalog = composablePartCatalog as INotifyComposablePartCatalogChanged; + try + { + if(_notifyCatalog != null) + { + _notifyCatalog.Changing += OnCatalogChanging; + } + + var compositionOptions = CompositionOptions.DisableSilentRejection | CompositionOptions.IsThreadSafe | CompositionOptions.ExportCompositionService; + var compositionContainer = new CompositionContainer(composablePartCatalog, compositionOptions); + + _compositionContainer = compositionContainer; + } + catch + { + if(_notifyCatalog != null) + { + _notifyCatalog.Changing -= OnCatalogChanging; + } + throw; + } + } + + public void SatisfyImportsOnce(ComposablePart part) + { + Requires.NotNull(part, nameof(part)); + Assumes.NotNull(_compositionContainer); + _compositionContainer.SatisfyImportsOnce(part); + } + + public void Dispose() + { + Assumes.NotNull(_compositionContainer); + + // Delegates are cool there is no concern if you try to remove an item from them and they don't exist + if (_notifyCatalog != null) + { + _notifyCatalog.Changing -= OnCatalogChanging; + } + _compositionContainer.Dispose(); + } + + private void OnCatalogChanging(object sender, ComposablePartCatalogChangeEventArgs e) + { + throw new ChangeRejectedException(SR.NotSupportedCatalogChanges); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionServices.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionServices.cs new file mode 100644 index 0000000000..4074acc4f4 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionServices.cs @@ -0,0 +1,657 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.Composition.Primitives; +using System.ComponentModel.Composition.ReflectionModel; +using System.Linq; +using System.Reflection; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + internal static class CompositionServices + { + internal static readonly Type InheritedExportAttributeType = typeof(InheritedExportAttribute); + internal static readonly Type ExportAttributeType = typeof(ExportAttribute); + internal static readonly Type AttributeType = typeof(Attribute); + internal static readonly Type ObjectType = typeof(object); + + private static readonly string[] reservedMetadataNames = new string[] + { + CompositionConstants.PartCreationPolicyMetadataName + }; + + internal static Type GetDefaultTypeFromMember(this MemberInfo member) + { + Assumes.NotNull(member); + + switch (member.MemberType) + { + case MemberTypes.Property: + return ((PropertyInfo)member).PropertyType; + + case MemberTypes.NestedType: + case MemberTypes.TypeInfo: + return ((Type)member); + + case MemberTypes.Field: + default: + Assumes.IsTrue(member.MemberType == MemberTypes.Field); + return ((FieldInfo)member).FieldType; + } + } + + internal static Type AdjustSpecifiedTypeIdentityType(this Type specifiedContractType, MemberInfo member) + { + if (member.MemberType == MemberTypes.Method) + { + return specifiedContractType; + } + else + { + return specifiedContractType.AdjustSpecifiedTypeIdentityType(member.GetDefaultTypeFromMember()); + } + } + + internal static Type AdjustSpecifiedTypeIdentityType(this Type specifiedContractType, Type memberType) + { + Assumes.NotNull(specifiedContractType); + + if ((memberType != null) && memberType.IsGenericType && specifiedContractType.IsGenericType) + { + // if the memeber type is closed and the specified contract type is open and they have exatly the same number of parameters + // we will close the specfied contract type + if (specifiedContractType.ContainsGenericParameters && !memberType.ContainsGenericParameters) + { + var typeGenericArguments = memberType.GetGenericArguments(); + var metadataTypeGenericArguments = specifiedContractType.GetGenericArguments(); + + if (typeGenericArguments.Length == metadataTypeGenericArguments.Length) + { + return specifiedContractType.MakeGenericType(typeGenericArguments); + } + } + // if both member type and the contract type are open generic types, make sure that their parameters are ordered the same way + else if(specifiedContractType.ContainsGenericParameters && memberType.ContainsGenericParameters) + { + var memberGenericParameters = memberType.GetPureGenericParameters(); + if (specifiedContractType.GetPureGenericArity() == memberGenericParameters.Count) + { + return specifiedContractType.GetGenericTypeDefinition().MakeGenericType(memberGenericParameters.ToArray()); + } + } + } + + return specifiedContractType; + } + + private static string AdjustTypeIdentity(string originalTypeIdentity, Type typeIdentityType) + { + return GenericServices.GetGenericName(originalTypeIdentity, GenericServices.GetGenericParametersOrder(typeIdentityType), GenericServices.GetPureGenericArity(typeIdentityType)); + } + + internal static void GetContractInfoFromExport(this MemberInfo member, ExportAttribute export, out Type typeIdentityType, out string contractName) + { + typeIdentityType = member.GetTypeIdentityTypeFromExport(export); + if (!string.IsNullOrEmpty(export.ContractName)) + { + contractName = export.ContractName; + } + else + { + contractName = member.GetTypeIdentityFromExport(typeIdentityType); + } + } + +internal static string GetTypeIdentityFromExport(this MemberInfo member, Type typeIdentityType) + { + if (typeIdentityType != null) + { + string typeIdentity = AttributedModelServices.GetTypeIdentity(typeIdentityType); + if (typeIdentityType.ContainsGenericParameters) + { + typeIdentity = AdjustTypeIdentity(typeIdentity, typeIdentityType); + } + return typeIdentity; + } + else + { + MethodInfo method = member as MethodInfo; + Assumes.NotNull(method); + return AttributedModelServices.GetTypeIdentity(method); + } + } + + private static Type GetTypeIdentityTypeFromExport(this MemberInfo member, ExportAttribute export) + { + if (export.ContractType != null) + { + return export.ContractType.AdjustSpecifiedTypeIdentityType(member); + } + else + { + return (member.MemberType != MemberTypes.Method) ? member.GetDefaultTypeFromMember() : null; + } + } + + internal static bool IsContractNameSameAsTypeIdentity(this ExportAttribute export) + { + return string.IsNullOrEmpty(export.ContractName); + } + +internal static Type GetContractTypeFromImport(this IAttributedImport import, ImportType importType) + { + if (import.ContractType != null) + { + return import.ContractType.AdjustSpecifiedTypeIdentityType(importType.ContractType); + } + + return importType.ContractType; + } + + internal static string GetContractNameFromImport(this IAttributedImport import, ImportType importType) + { + if (!string.IsNullOrEmpty(import.ContractName)) + { + return import.ContractName; + } + + Type contractType = import.GetContractTypeFromImport(importType); + + return AttributedModelServices.GetContractName(contractType); + } + + internal static string GetTypeIdentityFromImport(this IAttributedImport import, ImportType importType) + { + Type contractType = import.GetContractTypeFromImport(importType); + + // For our importers we treat object as not having a type identity + if (contractType == CompositionServices.ObjectType) + { + return null; + } + + return AttributedModelServices.GetTypeIdentity(contractType); + } + + internal static IDictionary GetPartMetadataForType(this Type type, CreationPolicy creationPolicy) + { + IDictionary dictionary = new Dictionary(StringComparers.MetadataKeyNames); + + if (creationPolicy != CreationPolicy.Any) + { + dictionary.Add(CompositionConstants.PartCreationPolicyMetadataName, creationPolicy); + } + + foreach (PartMetadataAttribute partMetadata in type.GetAttributes()) + { + if (reservedMetadataNames.Contains(partMetadata.Name, StringComparers.MetadataKeyNames) + || dictionary.ContainsKey(partMetadata.Name)) + { + // Perhaps we should log an error here so that people know this value is being ignored. + continue; + } + + dictionary.Add(partMetadata.Name, partMetadata.Value); + } + + // metadata for generic types + if (type.ContainsGenericParameters) + { + // Register the part as generic + dictionary.Add(CompositionConstants.IsGenericPartMetadataName, true); + + // Add arity + Type[] genericArguments = type.GetGenericArguments(); + dictionary.Add(CompositionConstants.GenericPartArityMetadataName, genericArguments.Length); + + // add constraints + bool hasConstraints = false; + object[] genericParameterConstraints = new object[genericArguments.Length]; + GenericParameterAttributes[] genericParameterAttributes = new GenericParameterAttributes[genericArguments.Length]; + for (int i=0; i< genericArguments.Length ; i++) + { + Type genericArgument = genericArguments[i]; + + Type[] constraints = genericArgument.GetGenericParameterConstraints(); + if (constraints.Length == 0) + { + constraints = null; + } + + GenericParameterAttributes attributes = genericArgument.GenericParameterAttributes; + + if ((constraints != null) || (attributes != GenericParameterAttributes.None)) + { + genericParameterConstraints[i] = constraints; + genericParameterAttributes[i] = attributes; + hasConstraints = true; + } + } + + if (hasConstraints) + { + dictionary.Add(CompositionConstants.GenericParameterConstraintsMetadataName, genericParameterConstraints); + dictionary.Add(CompositionConstants.GenericParameterAttributesMetadataName, genericParameterAttributes); + } + } + + if (dictionary.Count == 0) + { + return MetadataServices.EmptyMetadata; + } + else + { + return dictionary; + } + } + + internal static void TryExportMetadataForMember(this MemberInfo member, out IDictionary dictionary) + { + dictionary = new Dictionary(); + + foreach (var attr in member.GetAttributes()) + { + var provider = attr as ExportMetadataAttribute; + + if (provider != null) + { + if (reservedMetadataNames.Contains(provider.Name, StringComparers.MetadataKeyNames)) + { + throw ExceptionBuilder.CreateDiscoveryException(SR.Discovery_ReservedMetadataNameUsed, member.GetDisplayName(), provider.Name); + } + + // we pass "null" for valueType which would make it inferred. We don;t have additional type information when metadata + // goes through the ExportMetadataAttribute path + if (!dictionary.TryContributeMetadataValue(provider.Name, provider.Value, null, provider.IsMultiple)) + { + throw ExceptionBuilder.CreateDiscoveryException(SR.Discovery_DuplicateMetadataNameValues, member.GetDisplayName(), provider.Name); + } + } + else + { + Type attrType = attr.GetType(); + // Perf optimization, relies on short circuit evaluation, often a property attribute is an ExportAttribute + if ((attrType != CompositionServices.ExportAttributeType) && attrType.IsAttributeDefined(true)) + { + bool allowsMultiple = false; + AttributeUsageAttribute usage = attrType.GetFirstAttribute(true); + + if (usage != null) + { + allowsMultiple = usage.AllowMultiple; + } + + foreach (PropertyInfo pi in attrType.GetProperties()) + { + if (pi.DeclaringType == CompositionServices.ExportAttributeType || pi.DeclaringType == CompositionServices.AttributeType) + { + // Don't contribute metadata properies from the base attribute types. + continue; + } + + if (reservedMetadataNames.Contains(pi.Name, StringComparers.MetadataKeyNames)) + { + throw ExceptionBuilder.CreateDiscoveryException(SR.Discovery_ReservedMetadataNameUsed, member.GetDisplayName(), provider.Name); + } + + object value = pi.GetValue(attr, null); + + if (value != null && !IsValidAttributeType(value.GetType())) + { + throw ExceptionBuilder.CreateDiscoveryException(SR.Discovery_MetadataContainsValueWithInvalidType, pi.GetDisplayName(), value.GetType().GetDisplayName()); + } + + if (!dictionary.TryContributeMetadataValue(pi.Name, value, pi.PropertyType, allowsMultiple)) + { + throw ExceptionBuilder.CreateDiscoveryException(SR.Discovery_DuplicateMetadataNameValues, member.GetDisplayName(), pi.Name); + } + } + } + } + } + + // Need Keys.ToArray because we alter the dictionary in the loop + foreach (var key in dictionary.Keys.ToArray()) + { + var list = dictionary[key] as MetadataList; + if (list != null) + { + dictionary[key] = list.ToArray(); + } + } + + return; + } + + private static bool TryContributeMetadataValue(this IDictionary dictionary, string name, object value, Type valueType, bool allowsMultiple) + { + object metadataValue; + if (!dictionary.TryGetValue(name, out metadataValue)) + { + if (allowsMultiple) + { + var list = new MetadataList(); + list.Add(value, valueType); + value = list; + } + + dictionary.Add(name, value); + } + else + { + var list = metadataValue as MetadataList; + if (!allowsMultiple || list == null) + { + // Either single value already found when should be multiple + // or a duplicate name already exists + dictionary.Remove(name); + return false; + } + + list.Add(value, valueType); + } + return true; + } + + private class MetadataList + { + private Type _arrayType = null; + private bool _containsNulls = false; + private static readonly Type ObjectType = typeof(object); + private static readonly Type TypeType = typeof(Type); + private Collection _innerList = new Collection(); + + public void Add(object item, Type itemType) + { + _containsNulls |= (item == null); + + // if we've been passed typeof(object), we basically have no type inmformation + if (itemType == ObjectType) + { + itemType = null; + } + + // if we have no type information, get it from the item, if we can + if ((itemType == null) && (item != null)) + { + itemType = item.GetType(); + } + + // Types are special, because the are abstract classes, so if the item casts to Type, we assume System.Type + if (item is Type) + { + itemType = TypeType; + } + + // only try to call this if we got a meaningful type + if (itemType != null) + { + InferArrayType(itemType); + } + + _innerList.Add(item); + } + + private void InferArrayType(Type itemType) + { + Assumes.NotNull(itemType); + + if (_arrayType == null) + { + // this is the first typed element we've been given, it sets the type of the array + _arrayType = itemType; + } + else + { + // if there's a disagreement on the array type, we flip to Object + // NOTE : we can try to do better in the future to find common base class, but given that we support very limited set of types + // in metadata right now, it's a moot point + if (_arrayType != itemType) + { + _arrayType = ObjectType; + } + } + } + + public Array ToArray() + { + if (_arrayType == null) + { + // if the array type has not been set, assume Object + _arrayType = ObjectType; + } + else if (_containsNulls && _arrayType.IsValueType) + { + // if the array type is a value type and we have seen nulls, then assume Object + _arrayType = ObjectType; + } + + Array array = Array.CreateInstance(_arrayType, _innerList.Count); + + for(int i = 0; i < array.Length; i++) + { + array.SetValue(_innerList[i], i); + } + return array; + } + } + + //UNDONE: Need to add these warnings somewhere...Dev10:472538 should address + //internal static CompositionResult MatchRequiredMetadata(this IDictionary metadata, IEnumerable requiredMetadata, string contractName) + //{ + // Assumes.IsTrue(metadata != null); + + // var result = CompositionResult.SucceededResult; + + // var missingMetadata = (requiredMetadata == null) ? null : requiredMetadata.Except(metadata.Keys); + // if (missingMetadata != null && missingMetadata.Any()) + // { + // result = result.MergeIssue( + // CompositionError.CreateIssueAsWarning(CompositionErrorId.RequiredMetadataNotFound, + // SR.RequiredMetadataNotFound, + // contractName, + // string.Join(", ", missingMetadata.ToArray()))); + + // return new CompositionResult(false, result.Issues); + // } + + // return result; + //} + + internal static IEnumerable> GetRequiredMetadata(Type metadataViewType) + { + if ((metadataViewType == null) || + ExportServices.IsDefaultMetadataViewType(metadataViewType) || + ExportServices.IsDictionaryConstructorViewType(metadataViewType) || + !metadataViewType.IsInterface) + { + return Enumerable.Empty>(); + } + + // A metadata view is required to be an Intrerface, and therefore only properties are allowed + List properties = metadataViewType.GetAllProperties(). + Where(property => property.GetFirstAttribute() == null). + ToList(); + + // NOTE : this is a carefully found balance between eager and delay-evaluation - the properties are filtered once and upfront + // whereas the key/Type pairs are created every time. The latter is fine as KVPs are structs and as such copied on access regardless. + // This also allows us to avoid creation of List which - at least according to FxCop - leads to isues with NGEN + return properties.Select(property => new KeyValuePair(property.Name, property.PropertyType)); + } + + internal static IDictionary GetImportMetadata(ImportType importType, IAttributedImport attributedImport) + { + return GetImportMetadata(importType.ContractType, attributedImport); + } + + internal static IDictionary GetImportMetadata(Type type, IAttributedImport attributedImport) + { + Dictionary metadata = null; + + //Prior to V4.5 MEF did not support ImportMetadata + if (type.IsGenericType) + { + metadata = new Dictionary(); + + if (type.ContainsGenericParameters) + { + metadata[CompositionConstants.GenericImportParametersOrderMetadataName] = GenericServices.GetGenericParametersOrder(type); + } + else + { + metadata[CompositionConstants.GenericContractMetadataName] = ContractNameServices.GetTypeIdentity(type.GetGenericTypeDefinition()); + metadata[CompositionConstants.GenericParametersMetadataName] = type.GetGenericArguments(); + } + } + + // Default value is ImportSource.Any + if (attributedImport != null && attributedImport.Source != ImportSource.Any) + { + if (metadata == null) + { + metadata = new Dictionary(); + } + metadata[CompositionConstants.ImportSourceMetadataName] = attributedImport.Source; + } + + if (metadata != null) + { + return metadata.AsReadOnly(); + } + else + { + return MetadataServices.EmptyMetadata; + } + } + + internal static object GetExportedValueFromComposedPart(ImportEngine engine, ComposablePart part, ExportDefinition definition) + { + if (engine != null) + { + try + { + engine.SatisfyImports(part); + } + catch (CompositionException ex) + { + throw ExceptionBuilder.CreateCannotGetExportedValue(part, definition, ex); + } + } + + try + { + return part.GetExportedValue(definition); + } + catch (ComposablePartException ex) + { + throw ExceptionBuilder.CreateCannotGetExportedValue(part, definition, ex); + } + } + + internal static bool IsRecomposable(this ComposablePart part) + { + return part.ImportDefinitions.Any(import => import.IsRecomposable); + } + + internal static CompositionResult TryInvoke(Action action) + { + try + { + action(); + return CompositionResult.SucceededResult; + } + catch (CompositionException ex) + { + return new CompositionResult(ex.Errors); + } + } + + internal static CompositionResult TryFire(EventHandler _delegate, object sender, TEventArgs e) + where TEventArgs : EventArgs + { + CompositionResult result = CompositionResult.SucceededResult; + foreach (EventHandler _subscriber in _delegate.GetInvocationList()) + { + try + { + _subscriber.Invoke(sender, e); + } + catch (CompositionException ex) + { + result = result.MergeErrors(ex.Errors); + } + } + + return result; + } + + internal static CreationPolicy GetRequiredCreationPolicy(this ImportDefinition definition) + { + ContractBasedImportDefinition contractDefinition = definition as ContractBasedImportDefinition; + + if (contractDefinition != null) + { + return contractDefinition.RequiredCreationPolicy; + } + + return CreationPolicy.Any; + } + + /// + /// Returns a value indicating whether cardinality is + /// or + /// . + /// + internal static bool IsAtMostOne(this ImportCardinality cardinality) + { + return cardinality == ImportCardinality.ZeroOrOne || cardinality == ImportCardinality.ExactlyOne; + } + + private static bool IsValidAttributeType(Type type) + { + return IsValidAttributeType(type, true); + } + + private static bool IsValidAttributeType(Type type, bool arrayAllowed) + { + Assumes.NotNull(type); + // Definitions of valid attribute type taken from C# 3.0 Specification section 17.1.3. + + // One of the following types: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort. + if (type.IsPrimitive) + { + return true; + } + + if (type == typeof(string)) + { + return true; + } + + // An enum type, provided it has public accessibility and the types in which it is nested (if any) also have public accessibility + if (type.IsEnum && type.IsVisible) + { + return true; + } + + if (typeof(Type).IsAssignableFrom(type)) + { + return true; + } + + // Single-dimensional arrays of the above types. + if (arrayAllowed && type.IsArray && + type.GetArrayRank() == 1 && + IsValidAttributeType(type.GetElementType(), false)) + { + return true; + } + + return false; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/DirectoryCatalog.DirectoryCatalogDebuggerProxy.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/DirectoryCatalog.DirectoryCatalogDebuggerProxy.cs new file mode 100644 index 0000000000..e72c112aad --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/DirectoryCatalog.DirectoryCatalogDebuggerProxy.cs @@ -0,0 +1,74 @@ +// 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.Collections.ObjectModel; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using System.Reflection; +using Microsoft.Internal; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition.Hosting +{ + partial class DirectoryCatalog + { + internal class DirectoryCatalogDebuggerProxy + { + private readonly DirectoryCatalog _catalog; + + public DirectoryCatalogDebuggerProxy(DirectoryCatalog catalog) + { + Requires.NotNull(catalog, nameof(catalog)); + + _catalog = catalog; + } + + public ReadOnlyCollection Assemblies + { + get + { + return _catalog._assemblyCatalogs.Values.Select(catalog => catalog.Assembly) + .ToReadOnlyCollection(); + } + } + + public ReflectionContext ReflectionContext + { + get + { + return _catalog._reflectionContext; + } + } + + public string SearchPattern + { + get { return _catalog.SearchPattern; } + } + + public string Path + { + get { return _catalog._path; } + } + + public string FullPath + { + get { return _catalog._fullPath; } + } + + public ReadOnlyCollection LoadedFiles + { + get { return _catalog._loadedFiles; } + } + + public ReadOnlyCollection Parts + { + // NOTE: This shouldn't be cached, so that on every query of + // the current value of the underlying catalog is respected. + // We use ReadOnlyCollection as arrays do not have the + // appropriate debugger display attributes applied to them. + get { return _catalog.Parts.ToReadOnlyCollection(); } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/DirectoryCatalog.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/DirectoryCatalog.cs new file mode 100644 index 0000000000..812341f93b --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/DirectoryCatalog.cs @@ -0,0 +1,804 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.Composition.Diagnostics; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using Microsoft.Internal; +using Microsoft.Internal.Collections; +using IOPath = System.IO.Path; + +namespace System.ComponentModel.Composition.Hosting +{ + [DebuggerTypeProxy(typeof(DirectoryCatalogDebuggerProxy))] + public partial class DirectoryCatalog : ComposablePartCatalog, INotifyComposablePartCatalogChanged, ICompositionElement + { + private readonly Lock _thisLock = new Lock(); + private readonly ICompositionElement _definitionOrigin = null; + private ComposablePartCatalogCollection _catalogCollection; + private Dictionary _assemblyCatalogs; + private volatile bool _isDisposed = false; + private string _path; + private string _fullPath; + private string _searchPattern; + private ReadOnlyCollection _loadedFiles; + + private readonly ReflectionContext _reflectionContext = null; + + /// + /// Creates a catalog of s based on all the *.dll files + /// in the given directory path. + /// + /// Possible exceptions that can be thrown are any that or + /// can throw. + /// + /// + /// Path to the directory to scan for assemblies to add to the catalog. + /// The path needs to be absolute or relative to + /// + /// + /// If is a zero-length string, contains only white space, or + /// contains one or more implementation-specific invalid characters. + /// + /// + /// is . + /// + /// + /// The specified is invalid (for example, it is on an unmapped drive). + /// + /// + /// The specified , file name, or both exceed the system-defined maximum length. + /// For example, on Windows-based platforms, paths must be less than 248 characters and file names must + /// be less than 260 characters. + /// + /// + /// The caller does not have the required permission. + /// + public DirectoryCatalog(string path) + : this(path, "*.dll") + { + } + + /// + /// Creates a catalog of s based on all the *.dll files + /// in the given directory path. + /// + /// Possible exceptions that can be thrown are any that or + /// can throw. + /// + /// + /// Path to the directory to scan for assemblies to add to the catalog. + /// The path needs to be absolute or relative to + /// + /// + /// The a context used by the catalog when + /// interpreting the types to inject attributes into the type definition. + /// + /// + /// If is a zero-length string, contains only white space, or + /// contains one or more implementation-specific invalid characters. + /// + /// + /// is or + /// is . + /// + /// + /// The specified is invalid (for example, it is on an unmapped drive). + /// + /// + /// The specified , file name, or both exceed the system-defined maximum length. + /// For example, on Windows-based platforms, paths must be less than 248 characters and file names must + /// be less than 260 characters. + /// + /// + /// The caller does not have the required permission. + /// + public DirectoryCatalog(string path, ReflectionContext reflectionContext) + : this(path, "*.dll", reflectionContext) + { + } + + /// + /// Creates a catalog of s based on all the *.dll files + /// in the given directory path. + /// + /// Possible exceptions that can be thrown are any that or + /// can throw. + /// + /// + /// Path to the directory to scan for assemblies to add to the catalog. + /// The path needs to be absolute or relative to + /// + /// + /// The CompositionElement used by Diagnostics to identify the source for parts. + /// + /// + /// If is a zero-length string, contains only white space, or + /// contains one or more implementation-specific invalid characters. + /// + /// + /// is or + /// is . + /// + /// + /// The specified is invalid (for example, it is on an unmapped drive). + /// + /// + /// The specified , file name, or both exceed the system-defined maximum length. + /// For example, on Windows-based platforms, paths must be less than 248 characters and file names must + /// be less than 260 characters. + /// + /// + /// The caller does not have the required permission. + /// + public DirectoryCatalog(string path, ICompositionElement definitionOrigin) + : this(path, "*.dll", definitionOrigin) + { + } + + /// + /// Creates a catalog of s based on all the given searchPattern + /// over the files in the given directory path. + /// + /// Possible exceptions that can be thrown are any that or + /// can throw. + /// + /// + /// Path to the directory to scan for assemblies to add to the catalog. + /// The path needs to be absolute or relative to + /// + /// + /// The a context used by the catalog when + /// interpreting the types to inject attributes into the type definition. + /// + /// + /// The CompositionElement used by Diagnostics to identify the source for parts. + /// + /// + /// If is a zero-length string, contains only white space + /// does not contain a valid pattern. + /// + /// + /// is or + /// is or + /// is . + /// + /// + /// The specified is invalid (for example, it is on an unmapped drive). + /// + /// + /// The specified , file name, or both exceed the system-defined maximum length. + /// For example, on Windows-based platforms, paths must be less than 248 characters and file names must + /// be less than 260 characters. + /// + /// + /// The caller does not have the required permission. + /// + public DirectoryCatalog(string path, ReflectionContext reflectionContext, ICompositionElement definitionOrigin) + : this(path, "*.dll", reflectionContext, definitionOrigin) + { + } + + /// + /// Creates a catalog of s based on all the *.dll files + /// in the given directory path. + /// + /// Possible exceptions that can be thrown are any that or + /// can throw. + /// + /// + /// Path to the directory to scan for assemblies to add to the catalog. + /// The path needs to be absolute or relative to + /// + /// + /// Any valid searchPattern that will accept. + /// + /// + /// If is a zero-length string, contains only white space, or + /// contains one or more implementation-specific invalid characters. Or + /// does not contain a valid pattern. + /// + /// + /// is . + /// + /// + /// The specified is invalid (for example, it is on an unmapped drive). + /// + /// + /// The specified , file name, or both exceed the system-defined maximum length. + /// For example, on Windows-based platforms, paths must be less than 248 characters and file names must + /// be less than 260 characters. + /// + /// + /// The caller does not have the required permission. + /// + public DirectoryCatalog(string path, string searchPattern) + { + Requires.NotNullOrEmpty(path, "path"); + Requires.NotNullOrEmpty(searchPattern, "searchPattern"); + + _definitionOrigin = this; + Initialize(path, searchPattern); + } + + /// + /// Creates a catalog of s based on all the *.dll files + /// in the given directory path. + /// + /// Possible exceptions that can be thrown are any that or + /// can throw. + /// + /// + /// Path to the directory to scan for assemblies to add to the catalog. + /// The path needs to be absolute or relative to + /// + /// + /// The CompositionElement used by Diagnostics to identify the source for parts. + /// + /// + /// If is a zero-length string, contains only white space, or + /// contains one or more implementation-specific invalid characters. Or + /// does not contain a valid pattern. + /// + /// + /// is . + /// is . + /// + /// + /// The specified is invalid (for example, it is on an unmapped drive). + /// + /// + /// The specified , file name, or both exceed the system-defined maximum length. + /// For example, on Windows-based platforms, paths must be less than 248 characters and file names must + /// be less than 260 characters. + /// + /// + /// The caller does not have the required permission. + /// + public DirectoryCatalog(string path, string searchPattern, ICompositionElement definitionOrigin) + { + Requires.NotNullOrEmpty(path, "path"); + Requires.NotNullOrEmpty(searchPattern, "searchPattern"); + Requires.NotNull(definitionOrigin, nameof(definitionOrigin)); + + _definitionOrigin = definitionOrigin; + Initialize(path, searchPattern); + } + + /// + /// Creates a catalog of s based on all the given searchPattern + /// over the files in the given directory path. + /// + /// Possible exceptions that can be thrown are any that or + /// can throw. + /// + /// + /// Path to the directory to scan for assemblies to add to the catalog. + /// The path needs to be absolute or relative to + /// + /// + /// Any valid searchPattern that will accept. + /// + /// + /// The a context used by the catalog when + /// interpreting the types to inject attributes into the type definition. + /// + /// + /// If is a zero-length string, contains only white space, or + /// contains one or more implementation-specific invalid characters. Or + /// does not contain a valid pattern. + /// + /// + /// is + /// or is . + /// or is . + /// + /// + /// The specified is invalid (for example, it is on an unmapped drive). + /// + /// + /// The specified , file name, or both exceed the system-defined maximum length. + /// For example, on Windows-based platforms, paths must be less than 248 characters and file names must + /// be less than 260 characters. + /// + /// + /// The caller does not have the required permission. + /// + public DirectoryCatalog(string path, string searchPattern, ReflectionContext reflectionContext) + { + Requires.NotNullOrEmpty(path, "path"); + Requires.NotNullOrEmpty(searchPattern, "searchPattern"); + Requires.NotNull(reflectionContext, "reflectionContext"); + + _reflectionContext = reflectionContext; + _definitionOrigin = this; + Initialize(path, searchPattern); + } + + /// + /// Creates a catalog of s based on all the given searchPattern + /// over the files in the given directory path. + /// + /// Possible exceptions that can be thrown are any that or + /// can throw. + /// + /// + /// Path to the directory to scan for assemblies to add to the catalog. + /// The path needs to be absolute or relative to + /// + /// + /// Any valid searchPattern that will accept. + /// + /// + /// The a context used by the catalog when + /// interpreting the types to inject attributes into the type definition. + /// + /// + /// The CompositionElement used by Diagnostics to identify the source for parts. + /// + /// + /// If is a zero-length string, contains only white space, or + /// contains one or more implementation-specific invalid characters. Or + /// does not contain a valid pattern. + /// + /// + /// is + /// or is . + /// or is . + /// or is . + /// + /// + /// The specified is invalid (for example, it is on an unmapped drive). + /// + /// + /// The specified , file name, or both exceed the system-defined maximum length. + /// For example, on Windows-based platforms, paths must be less than 248 characters and file names must + /// be less than 260 characters. + /// + /// + /// The caller does not have the required permission. + /// + public DirectoryCatalog(string path, string searchPattern, ReflectionContext reflectionContext, ICompositionElement definitionOrigin) + { + Requires.NotNullOrEmpty(path, "path"); + Requires.NotNullOrEmpty(searchPattern, "searchPattern"); + Requires.NotNull(reflectionContext, "reflectionContext"); + Requires.NotNull(definitionOrigin, "definitionOrigin"); + + _reflectionContext = reflectionContext; + _definitionOrigin = definitionOrigin; + Initialize(path, searchPattern); + } + + /// + /// Translated absolute path of the path passed into the constructor of . + /// + public string FullPath + { + get + { + Contract.Ensures(Contract.Result() != null); + + return _fullPath; + } + } + + /// + /// Set of files that have currently been loaded into the catalog. + /// + public ReadOnlyCollection LoadedFiles + { + get + { + Contract.Ensures(Contract.Result>() != null); + + using (new ReadLock(_thisLock)) + { + return _loadedFiles; + } + } + } + + /// + /// Path passed into the constructor of . + /// + public string Path + { + get + { + Contract.Ensures(Contract.Result() != null); + + return _path; + } + } + + /// + /// SearchPattern passed into the constructor of , or the default *.dll. + /// + public string SearchPattern + { + get + { + return _searchPattern; + } + } + + /// + /// Notify when the contents of the Catalog has changed. + /// + public event EventHandler Changed; + + /// + /// Notify when the contents of the Catalog has changing. + /// + public event EventHandler Changing; + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected override void Dispose(bool disposing) + { + try + { + if (disposing) + { + if (!_isDisposed) + { + bool disposeLock = false; + ComposablePartCatalogCollection catalogs = null; + + try + { + using (new WriteLock(_thisLock)) + { + if (!_isDisposed) + { + disposeLock = true; + catalogs = _catalogCollection; + _catalogCollection = null; + _assemblyCatalogs = null; + _isDisposed = true; + } + } + } + finally + { + if (catalogs != null) + { + catalogs.Dispose(); + } + + if (disposeLock) + { + _thisLock.Dispose(); + } + } + } + } + } + finally + { + base.Dispose(disposing); + } + } + + public override IEnumerator GetEnumerator() + { + return _catalogCollection.SelectMany(catalog => catalog as IEnumerable).GetEnumerator(); + } + + /// + /// Returns the export definitions that match the constraint defined by the specified definition. + /// + /// + /// The that defines the conditions of the + /// objects to return. + /// + /// + /// An of containing the + /// objects and their associated + /// for objects that match the constraint defined + /// by . + /// + /// + /// is . + /// + /// + /// The has been disposed of. + /// + public override IEnumerable> GetExports(ImportDefinition definition) + { + ThrowIfDisposed(); + + Requires.NotNull(definition, nameof(definition)); + + return _catalogCollection.SelectMany(catalog => catalog.GetExports(definition)); + } + + /// + /// Raises the event. + /// + /// + /// An containing the data for the event. + /// + protected virtual void OnChanged(ComposablePartCatalogChangeEventArgs e) + { + EventHandler changedEvent = Changed; + if (changedEvent != null) + { + changedEvent(this, e); + } + } + + /// + /// Raises the event. + /// + /// + /// An containing the data for the event. + /// + protected virtual void OnChanging(ComposablePartCatalogChangeEventArgs e) + { + EventHandler changingEvent = Changing; + if (changingEvent != null) + { + changingEvent(this, e); + } + } + + /// + /// Refreshes the s with the latest files in the directory that match + /// the searchPattern. If any files have been added they will be added to the catalog and if any files were + /// removed they will be removed from the catalog. For files that have been removed keep in mind that the + /// assembly cannot be unloaded from the process so s for those files + /// will simply be removed from the catalog. + /// + /// Possible exceptions that can be thrown are any that or + /// can throw. + /// + /// + /// The specified path has been removed since object construction. + /// + public void Refresh() + { + ThrowIfDisposed(); + Assumes.NotNull(_loadedFiles); + + List> catalogsToAdd; + List> catalogsToRemove; + ComposablePartDefinition[] addedDefinitions; + ComposablePartDefinition[] removedDefinitions; + object changeReferenceObject; + string[] afterFiles; + string[] beforeFiles; + + while (true) + { + afterFiles = GetFiles(); + + using (new ReadLock(_thisLock)) + { + changeReferenceObject = _loadedFiles; + beforeFiles = _loadedFiles.ToArray(); + } + + DiffChanges(beforeFiles, afterFiles, out catalogsToAdd, out catalogsToRemove); + + // Don't go any further if there's no work to do + if (catalogsToAdd.Count == 0 && catalogsToRemove.Count == 0) + { + return; + } + + // Notify listeners to give them a preview before completeting the changes + addedDefinitions = catalogsToAdd + .SelectMany(cat => cat.Item2 as IEnumerable) + .ToArray(); + + removedDefinitions = catalogsToRemove + .SelectMany(cat => cat.Item2 as IEnumerable) + .ToArray(); + + using (var atomicComposition = new AtomicComposition()) + { + var changingArgs = new ComposablePartCatalogChangeEventArgs(addedDefinitions, removedDefinitions, atomicComposition); + OnChanging(changingArgs); + + // if the change went through then write the catalog changes + using (new WriteLock(_thisLock)) + { + if (changeReferenceObject != _loadedFiles) + { + // Someone updated the list while we were diffing so we need to try the diff again + continue; + } + + foreach (var catalogToAdd in catalogsToAdd) + { + _assemblyCatalogs.Add(catalogToAdd.Item1, catalogToAdd.Item2); + _catalogCollection.Add(catalogToAdd.Item2); + } + + foreach (var catalogToRemove in catalogsToRemove) + { + _assemblyCatalogs.Remove(catalogToRemove.Item1); + _catalogCollection.Remove(catalogToRemove.Item2); + } + + _loadedFiles = afterFiles.ToReadOnlyCollection(); + + // Lastly complete any changes added to the atomicComposition during the change event + atomicComposition.Complete(); + + // Break out of the while(true) + break; + } // WriteLock + } // AtomicComposition + } // while (true) + + var changedArgs = new ComposablePartCatalogChangeEventArgs(addedDefinitions, removedDefinitions, null); + OnChanged(changedArgs); + } + + /// + /// Returns a string representation of the directory catalog. + /// + /// + /// A containing the string representation of the . + /// + public override string ToString() + { + return GetDisplayName(); + } + + private AssemblyCatalog CreateAssemblyCatalogGuarded(string assemblyFilePath) + { + Exception exception = null; + + try + { + return (_reflectionContext != null) + ? new AssemblyCatalog(assemblyFilePath, _reflectionContext, this) + : new AssemblyCatalog(assemblyFilePath, this); + } + catch (FileNotFoundException ex) + { // Files should always exists but don't blow up here if they don't + exception = ex; + } + catch (FileLoadException ex) + { // File was found but could not be loaded + exception = ex; + } + catch (BadImageFormatException ex) + { // Dlls that contain native code are not loaded, but do not invalidate the Directory + exception = ex; + } + catch (ReflectionTypeLoadException ex) + { // Dlls that have missing Managed dependencies are not loaded, but do not invalidate the Directory + exception = ex; + } + + CompositionTrace.AssemblyLoadFailed(this, assemblyFilePath, exception); + + return null; + } + + private void DiffChanges(string[] beforeFiles, string[] afterFiles, + out List> catalogsToAdd, + out List> catalogsToRemove) + { + catalogsToAdd = new List>(); + catalogsToRemove = new List>(); + + IEnumerable filesToAdd = afterFiles.Except(beforeFiles); + foreach (string file in filesToAdd) + { + AssemblyCatalog catalog = CreateAssemblyCatalogGuarded(file); + + if (catalog != null) + { + catalogsToAdd.Add(new Tuple(file, catalog)); + } + } + + IEnumerable filesToRemove = beforeFiles.Except(afterFiles); + using (new ReadLock(_thisLock)) + { + foreach (string file in filesToRemove) + { + AssemblyCatalog catalog; + if (_assemblyCatalogs.TryGetValue(file, out catalog)) + { + catalogsToRemove.Add(new Tuple(file, catalog)); + } + } + } + } + + private string GetDisplayName() + { + return string.Format(CultureInfo.CurrentCulture, + "{0} (Path=\"{1}\")", // NOLOC + GetType().Name, + _path); + } + + private string[] GetFiles() + { + string[] files = Directory.GetFiles(_fullPath, _searchPattern); + return Array.ConvertAll(files, (file) => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? file.ToUpperInvariant() : file); + } + + private static string GetFullPath(string path) + { + var fullPath = IOPath.GetFullPath(path); + return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? fullPath.ToUpperInvariant() : fullPath; + } + + private void Initialize(string path, string searchPattern) + { + _path = path; + _fullPath = GetFullPath(path); + _searchPattern = searchPattern; + _assemblyCatalogs = new Dictionary(); + _catalogCollection = new ComposablePartCatalogCollection(null, null, null); + + _loadedFiles = GetFiles().ToReadOnlyCollection(); + + foreach (string file in _loadedFiles) + { + AssemblyCatalog assemblyCatalog = null; + assemblyCatalog = CreateAssemblyCatalogGuarded(file); + + if (assemblyCatalog != null) + { + _assemblyCatalogs.Add(file, assemblyCatalog); + _catalogCollection.Add(assemblyCatalog); + } + } + } + + [DebuggerStepThrough] + private void ThrowIfDisposed() + { + if (_isDisposed) + { + throw ExceptionBuilder.CreateObjectDisposed(this); + } + } + + /// + /// Gets the display name of the directory catalog. + /// + /// + /// A containing a human-readable display name of the . + /// + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] + string ICompositionElement.DisplayName + { + get { return GetDisplayName(); } + } + + /// + /// Gets the composition element from which the directory catalog originated. + /// + /// + /// This property always returns . + /// + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] + ICompositionElement ICompositionElement.Origin + { + get { return null; } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ExportProvider.GetExportOverrides.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ExportProvider.GetExportOverrides.cs new file mode 100644 index 0000000000..0fb680cc07 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ExportProvider.GetExportOverrides.cs @@ -0,0 +1,816 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + public abstract partial class ExportProvider + { + /// + /// Returns the export with the contract name derived from the specified type parameter, + /// throwing an exception if there is not exactly one matching export. + /// + /// + /// The type of the object to return. The contract name is also + /// derived from this type parameter. + /// + /// + /// The object with the contract name derived from + /// . + /// + /// + /// + /// The returned object is an instance of + /// underneath, where + /// TMetadataView + /// is and where TKey + /// is and TValue is . + /// + /// + /// The contract name is the result of calling + /// on . + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + /// + /// + /// There are zero objects with the contract name derived + /// from in the . + /// + /// -or- + /// + /// There are more than one objects with the contract name + /// derived from in the . + /// + /// + /// + /// The has been disposed of. + /// + public Lazy GetExport() + { + return GetExport((string)null); + } + + /// + /// Returns the export with the specified contract name, throwing an exception if there + /// is not exactly one matching export. + /// + /// + /// The type of the object to return. + /// + /// + /// A containing the contract name of the + /// object to return; or or an empty string ("") to use the + /// default contract name. + /// + /// + /// The object with the specified contract name. + /// + /// + /// + /// The returned object is an instance of + /// underneath, where + /// TMetadataView + /// is and where TKey + /// is and TValue is . + /// + /// + /// The contract name is the result of calling + /// on . + /// + /// + /// The default contract name is compared using a case-sensitive, non-linguistic + /// comparison using . + /// + /// + /// + /// + /// There are zero objects with the specified contract name + /// in the . + /// + /// -or- + /// + /// There are more than one objects with the specified contract + /// name in the . + /// + /// + /// + /// The has been disposed of. + /// + public Lazy GetExport(string contractName) + { + return GetExportCore(contractName); + } + + /// + /// Returns the export with the contract name derived from the specified type parameter, + /// throwing an exception if there is not exactly one matching export. + /// + /// + /// The type of the object to return. The + /// contract name is also derived from this type parameter. + /// + /// + /// The type of the metadata view of the object + /// to return. + /// + /// + /// The object with the contract name derived + /// from . + /// + /// + /// + /// The contract name is the result of calling + /// on . + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + /// + /// + /// There are zero objects with the contract + /// name derived from in the + /// . + /// + /// -or- + /// + /// There are more than one objects with the + /// contract name derived from in the + /// . + /// + /// + /// + /// is not a valid metadata view type. + /// + /// + /// The has been disposed of. + /// + public Lazy GetExport() + { + return GetExport((string)null); + } + + /// + /// Returns the export with the specified contract name, throwing an exception if there + /// is not exactly one matching export. + /// + /// + /// The type of the object to return. + /// + /// + /// The type of the metadata view of the object + /// to return. + /// + /// + /// A containing the contract name of the + /// object to return; or + /// or an empty string ("") to use the default contract name. + /// + /// + /// The object with the specified contract name. + /// + /// + /// + /// The default contract name is the result of calling + /// on . + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + /// + /// + /// There are zero objects with the + /// specified contract name in the . + /// + /// -or- + /// + /// There are more than one objects with the + /// specified contract name in the . + /// + /// + /// + /// is not a valid metadata view type. + /// + /// + /// The has been disposed of. + /// + public Lazy GetExport(string contractName) + { + return GetExportCore(contractName); + } + + /// + /// Returns the exports with the specified contract name. + /// + /// + /// The of the objects to return. + /// + /// + /// The of the metadata view of the objects to + /// return. + /// + /// + /// A containing the contract name of the + /// object to return; or + /// or an empty string ("") to use the default contract name. + /// + /// + /// An containing the objects + /// with the specified contract name, if found; otherwise, an empty + /// . + /// + /// + /// + /// The returned objects are instances of + /// underneath, where T + /// is and TMetadataView is + /// . + /// + /// + /// The default contract name is the result of calling + /// on . + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + /// + /// is . + /// + /// + /// is not a valid metadata view type. + /// + /// + /// The has been disposed of. + /// + [SuppressMessage("Microsoft.Design", "CA1006")] + public IEnumerable> GetExports(Type type, Type metadataViewType, string contractName) + { + IEnumerable exports = GetExportsCore(type, metadataViewType, contractName, ImportCardinality.ZeroOrMore); + Collection> result = new Collection>(); + + Func> typedExportFactory = ExportServices.CreateSemiStronglyTypedLazyFactory(type, metadataViewType); + foreach (Export export in exports) + { + result.Add(typedExportFactory.Invoke(export)); + } + + return result; + } + + /// + /// Returns the exports with the contract name derived from the specified type parameter. + /// + /// + /// The type of the objects to return. The contract name is also + /// derived from this type parameter. + /// + /// + /// An containing the objects + /// with the contract name derived from , if found; otherwise, + /// an empty . + /// + /// + /// + /// The returned objects are instances of + /// underneath, where + /// TMetadataView + /// is and where TKey + /// is and TValue is . + /// + /// + /// The contract name is the result of calling + /// on . + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + /// + /// The has been disposed of. + /// + [SuppressMessage("Microsoft.Design", "CA1006")] + public IEnumerable> GetExports() + { + return GetExports((string)null); + } + + /// + /// Returns the exports with the specified contract name. + /// + /// + /// The type of the objects to return. + /// + /// + /// A containing the contract name of the + /// objects to return; or or an empty string ("") to use the + /// default contract name. + /// + /// + /// An containing the objects + /// with the specified contract name, if found; otherwise, an empty + /// . + /// + /// + /// + /// The returned objects are instances of + /// underneath, where + /// TMetadataView + /// is and where TKey + /// is and TValue is . + /// + /// + /// The default contract name is the result of calling + /// on . + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + /// + /// The has been disposed of. + /// + [SuppressMessage("Microsoft.Design", "CA1006")] + public IEnumerable> GetExports(string contractName) + { + return GetExportsCore(contractName); + } + + /// + /// Returns the exports with the contract name derived from the specified type parameter. + /// + /// + /// The type of the objects to return. The + /// contract name is also derived from this type parameter. + /// + /// + /// The type of the metadata view of the objects + /// to return. + /// + /// + /// An containing the + /// objects with the contract name derived from + /// , if found; otherwise, an empty + /// . + /// + /// + /// + /// The contract name is the result of calling + /// on . + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + /// + /// is not a valid metadata view type. + /// + /// + /// The has been disposed of. + /// + [SuppressMessage("Microsoft.Design", "CA1006")] + public IEnumerable> GetExports() + { + return GetExports((string)null); + } + + /// + /// Returns the exports with the specified contract name. + /// + /// + /// The type of the objects to return. The + /// contract name is also derived from this type parameter. + /// + /// + /// The type of the metadata view of the objects + /// to return. + /// + /// + /// A containing the contract name of the + /// objects to return; or + /// or an empty string ("") to use the default contract name. + /// + /// + /// An containing the + /// objects with the specified contract name if + /// found; otherwise, an empty . + /// + /// + /// + /// The default contract name is the result of calling + /// on . + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + /// + /// is not a valid metadata view type. + /// + /// + /// The has been disposed of. + /// + [SuppressMessage("Microsoft.Design", "CA1006")] + public IEnumerable> GetExports(string contractName) + { + return GetExportsCore(contractName); + } + + /// + /// Returns the exported value with the contract name derived from the specified type + /// parameter, throwing an exception if there is not exactly one matching exported value. + /// + /// + /// The type of the exported value to return. The contract name is also + /// derived from this type parameter. + /// + /// + /// The exported with the contract name derived from + /// . + /// + /// + /// + /// The contract name is the result of calling + /// on . + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + /// + /// The underlying exported value cannot be cast to . + /// + /// + /// + /// There are zero exported values with the contract name derived from + /// in the . + /// + /// -or- + /// + /// There are more than one exported values with the contract name derived from + /// in the . + /// + /// + /// + /// The has been disposed of. + /// + /// + /// An error occurred during composition. will + /// contain a collection of errors that occurred. + /// + public T GetExportedValue() + { + return GetExportedValue((string)null); + } + + /// + /// Returns the exported value with the specified contract name, throwing an exception + /// if there is not exactly one matching exported value. + /// + /// + /// The type of the exported value to return. + /// + /// + /// A containing the contract name of the exported value to return, + /// or or an empty string ("") to use the default contract name. + /// + /// + /// The exported with the specified contract name. + /// + /// + /// + /// The default contract name is the result of calling + /// on . + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + /// + /// The underlying exported value cannot be cast to . + /// + /// + /// + /// There are zero exported values with the specified contract name in the + /// . + /// + /// -or- + /// + /// There are more than one exported values with the specified contract name in the + /// . + /// + /// + /// + /// The has been disposed of. + /// + /// + /// An error occurred during composition. will + /// contain a collection of errors that occurred. + /// + public T GetExportedValue(string contractName) + { + return GetExportedValueCore(contractName, ImportCardinality.ExactlyOne); + } + + /// + /// Returns the exported value with the contract name derived from the specified type + /// parameter, throwing an exception if there is more than one matching exported value. + /// + /// + /// The type of the exported value to return. The contract name is also + /// derived from this type parameter. + /// + /// + /// The exported with the contract name derived from + /// , if found; otherwise, the default value for + /// . + /// + /// + /// + /// If the exported value is not found, then this method returns the appropriate + /// default value for ; for example, 0 (zero) for integer + /// types, for Boolean types, and + /// for reference types. + /// + /// + /// The contract name is the result of calling + /// on . + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + /// + /// The underlying exported value cannot be cast to . + /// + /// + /// + /// There are more than one exported values with the contract name derived from + /// in the . + /// + /// + /// + /// The has been disposed of. + /// + /// + /// An error occurred during composition. will + /// contain a collection of errors that occurred. + /// + public T GetExportedValueOrDefault() + { + return GetExportedValueOrDefault((string)null); + } + + /// + /// Returns the exported value with the specified contract name, throwing an exception + /// if there is more than one matching exported value. + /// + /// + /// The type of the exported value to return. + /// + /// + /// A containing the contract name of the exported value to return, + /// or or an empty string ("") to use the default contract name. + /// + /// + /// The exported with the specified contract name, if found; + /// otherwise, the default value for . + /// + /// + /// + /// If the exported value is not found, then this method returns the appropriate + /// default value for ; for example, 0 (zero) for integer + /// types, for Boolean types, and + /// for reference types. + /// + /// + /// The default contract name is the result of calling + /// on . + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + /// + /// The underlying exported value cannot be cast to . + /// + /// + /// There are more than one exported values with the specified contract name in the + /// . + /// + /// + /// The has been disposed of. + /// + /// + /// An error occurred during composition. will + /// contain a collection of errors that occurred. + /// + public T GetExportedValueOrDefault(string contractName) + { + return GetExportedValueCore(contractName, ImportCardinality.ZeroOrOne); + } + + /// + /// Returns the exported values with the contract name derived from the specified type + /// parameter. + /// + /// + /// The type of the exported value to return. The contract name is also + /// derived from this type parameter. + /// + /// + /// An containing the exported values with the contract name + /// derived from the specified type parameter, if found; otherwise, an empty + /// . + /// + /// + /// + /// The contract name is the result of calling + /// on . + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + /// + /// One or more of the underlying exported values cannot be cast to + /// . + /// + /// + /// The has been disposed of. + /// + /// + /// An error occurred during composition. will + /// contain a collection of errors that occurred. + /// + public IEnumerable GetExportedValues() + { + return GetExportedValues((string)null); + } + + /// + /// Returns the exported values with the specified contract name. + /// + /// + /// The type of the exported value to return. + /// + /// + /// A containing the contract name of the exported values to + /// return; or or an empty string ("") to use the default + /// contract name. + /// + /// + /// An containing the exported values with the specified + /// contract name, if found; otherwise, an empty . + /// + /// + /// + /// The default contract name is the result of calling + /// on . + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + /// + /// One or more of the underlying exported values cannot be cast to + /// . + /// + /// + /// The has been disposed of. + /// + /// + /// An error occurred during composition. will + /// contain a collection of errors that occurred. + /// + public IEnumerable GetExportedValues(string contractName) + { + return GetExportedValuesCore(contractName); + } + + private IEnumerable GetExportedValuesCore(string contractName) + { + IEnumerable exports = GetExportsCore(typeof(T), (Type)null, contractName, ImportCardinality.ZeroOrMore); + + Collection result = new Collection(); + foreach (Export export in exports) + { + result.Add(ExportServices.GetCastedExportedValue(export)); + } + return result; + } + + private T GetExportedValueCore(string contractName, ImportCardinality cardinality) + { + Assumes.IsTrue(cardinality.IsAtMostOne()); + + Export export = GetExportsCore(typeof(T), (Type)null, contractName, cardinality).SingleOrDefault(); + + return (export != null) ? ExportServices.GetCastedExportedValue(export) : default(T); + } + + private IEnumerable> GetExportsCore(string contractName) + { + IEnumerable exports = GetExportsCore(typeof(T), (Type)null, contractName, ImportCardinality.ZeroOrMore); + + Collection> result = new Collection>(); + foreach (Export export in exports) + { + result.Add(ExportServices.CreateStronglyTypedLazyOfT(export)); + } + return result; + } + + private IEnumerable> GetExportsCore(string contractName) + { + IEnumerable exports = GetExportsCore(typeof(T), typeof(TMetadataView), contractName, ImportCardinality.ZeroOrMore); + + Collection> result = new Collection>(); + foreach (Export export in exports) + { + result.Add(ExportServices.CreateStronglyTypedLazyOfTM(export)); + } + return result; + } + + private Lazy GetExportCore(string contractName) + { + Export export = GetExportsCore(typeof(T), typeof(TMetadataView), contractName, ImportCardinality.ExactlyOne).SingleOrDefault(); + + return (export != null) ? ExportServices.CreateStronglyTypedLazyOfTM(export) : null; + } + + private Lazy GetExportCore(string contractName) + { + Export export = GetExportsCore(typeof(T), null, contractName, ImportCardinality.ExactlyOne).SingleOrDefault(); + + return (export != null) ? ExportServices.CreateStronglyTypedLazyOfT(export) : null; + } + + private IEnumerable GetExportsCore(Type type, Type metadataViewType, string contractName, ImportCardinality cardinality) + { + // Only 'type' cannot be null - the other parameters have sensible defaults. + Requires.NotNull(type, nameof(type)); + + if (string.IsNullOrEmpty(contractName)) + { + contractName = AttributedModelServices.GetContractName(type); + } + + if (metadataViewType == null) + { + metadataViewType = ExportServices.DefaultMetadataViewType; + } + + if (!MetadataViewProvider.IsViewTypeValid(metadataViewType)) + { + throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.InvalidMetadataView, metadataViewType.Name)); + } + + ImportDefinition importDefinition = BuildImportDefinition(type, metadataViewType, contractName, cardinality); + return GetExports(importDefinition, null); + } + + private static ImportDefinition BuildImportDefinition(Type type, Type metadataViewType, string contractName, ImportCardinality cardinality) + { + Assumes.NotNull(type, metadataViewType, contractName); + + IEnumerable> requiredMetadata = CompositionServices.GetRequiredMetadata(metadataViewType); + IDictionary metadata = CompositionServices.GetImportMetadata(type, null); + + string requiredTypeIdentity = null; + if (type != typeof(object)) + { + requiredTypeIdentity = AttributedModelServices.GetTypeIdentity(type); + } + + return new ContractBasedImportDefinition(contractName, requiredTypeIdentity, requiredMetadata, cardinality, false, true, CreationPolicy.Any, metadata); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ExportProvider.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ExportProvider.cs new file mode 100644 index 0000000000..bc1004690a --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ExportProvider.cs @@ -0,0 +1,234 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics.Contracts; +using System.Globalization; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + /// + /// Defines the base class for export providers, which provide + /// methods for retrieving objects. + /// + public abstract partial class ExportProvider + { + private static readonly Export[] EmptyExports = new Export[] { }; + + /// + /// Initializes a new instance of the class. + /// + protected ExportProvider() + { + } + + /// + /// Occurs when the exports in the have changed. + /// + public event EventHandler ExportsChanged; + + /// + /// Occurs when the exports in the are changing. + /// + public event EventHandler ExportsChanging; + + /// + /// Returns all exports that match the conditions of the specified import. + /// + /// + /// The that defines the conditions of the + /// objects to get. + /// + /// + /// An of objects that match + /// the conditions defined by , if found; otherwise, an + /// empty . + /// + /// + /// is . + /// + /// + /// + /// is and + /// there are zero objects that match the conditions of the specified + /// . + /// + /// -or- + /// + /// is or + /// and there are more than one + /// objects that match the conditions of the specified . + /// + /// + public IEnumerable GetExports(ImportDefinition definition) + { + return GetExports(definition, null); + } + + /// + /// Returns all exports that match the conditions of the specified import. + /// + /// + /// The that defines the conditions of the + /// objects to get. + /// + /// + /// An of objects that match + /// the conditions defined by , if found; otherwise, an + /// empty . + /// + /// + /// is . + /// + /// + /// + /// is and + /// there are zero objects that match the conditions of the specified + /// . + /// + /// -or- + /// + /// is or + /// and there are more than one + /// objects that match the conditions of the specified . + /// + /// + public IEnumerable GetExports(ImportDefinition definition, AtomicComposition atomicComposition) + { + Requires.NotNull(definition, nameof(definition)); + Contract.Ensures(Contract.Result>() != null); + + IEnumerable exports; + ExportCardinalityCheckResult result = TryGetExportsCore(definition, atomicComposition, out exports); + switch(result) + { + case ExportCardinalityCheckResult.Match: + return exports; + case ExportCardinalityCheckResult.NoExports: + throw new ImportCardinalityMismatchException(string.Format(CultureInfo.CurrentCulture, SR.CardinalityMismatch_NoExports, definition.ToString())); + default: + Assumes.IsTrue(result == ExportCardinalityCheckResult.TooManyExports); + throw new ImportCardinalityMismatchException(string.Format(CultureInfo.CurrentCulture, SR.CardinalityMismatch_TooManyExports, definition.ToString())); + } + } + + /// + /// Returns all exports that match the conditions of the specified import. + /// + /// + /// The that defines the conditions of the + /// objects to get. + /// + /// + /// When this method returns, contains an of + /// objects that match the conditions defined by , if found; + /// otherwise, an empty . + /// + /// + /// if is + /// or and + /// there are zero objects that match the conditions of the specified + /// . if + /// is or + /// and there is exactly one + /// that matches the conditions of the specified ; otherwise, + /// . + /// + /// + /// is . + /// + public bool TryGetExports(ImportDefinition definition, AtomicComposition atomicComposition, out IEnumerable exports) + { + Requires.NotNull(definition, nameof(definition)); + + exports = null; + ExportCardinalityCheckResult result = TryGetExportsCore(definition, atomicComposition, out exports); + return (result == ExportCardinalityCheckResult.Match); + } + + /// + /// Returns all exports that match the constraint defined by the specified definition. + /// + /// + /// The that defines the conditions of the + /// objects to return. + /// + /// + /// An of objects that match + /// the conditions defined by , if found; otherwise, an + /// empty . + /// + /// + /// + /// Overriders of this method should not treat cardinality-related mismatches + /// as errors, and should not throw exceptions in those cases. For instance, + /// if is + /// and there are zero objects that match the conditions of the + /// specified , an should be returned. + /// + /// + protected abstract IEnumerable GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition); + + /// + /// Raises the event. + /// + /// + /// An containing the data for the event. + /// + protected virtual void OnExportsChanged(ExportsChangeEventArgs e) + { + EventHandler changedEvent = ExportsChanged; + if (changedEvent != null) + { + CompositionResult result = CompositionServices.TryFire(changedEvent, this, e); + result.ThrowOnErrors(e.AtomicComposition); + } + } + + /// + /// Raises the event. + /// + /// + /// An containing the data for the event. + /// + protected virtual void OnExportsChanging(ExportsChangeEventArgs e) + { + EventHandler changingEvent = ExportsChanging; + if (changingEvent != null) + { + CompositionResult result = CompositionServices.TryFire(changingEvent, this, e); + result.ThrowOnErrors(e.AtomicComposition); + } + } + + private ExportCardinalityCheckResult TryGetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition, out IEnumerable exports) + { + Assumes.NotNull(definition); + + exports = GetExportsCore(definition, atomicComposition); + + var checkResult = ExportServices.CheckCardinality(definition, exports); + + // Export providers treat >1 match as zero for cardinality 0-1 imports + // If this policy is moved we need to revisit the assumption that the + // ImportEngine made during previewing the only required imports to + // now also preview optional imports. + if (checkResult == ExportCardinalityCheckResult.TooManyExports && + definition.Cardinality == ImportCardinality.ZeroOrOne) + { + checkResult = ExportCardinalityCheckResult.Match; + exports = null; + } + + if (exports == null) + { + exports = EmptyExports; + } + + return checkResult; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ExportsChangeEventArgs.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ExportsChangeEventArgs.cs new file mode 100644 index 0000000000..840e693e0b --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ExportsChangeEventArgs.cs @@ -0,0 +1,128 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics.Contracts; +using System.Linq; +using Microsoft.Internal; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition.Hosting +{ + /// + /// Provides data for the and + /// events. + /// + public class ExportsChangeEventArgs : EventArgs + { + private readonly IEnumerable _addedExports; + private readonly IEnumerable _removedExports; + private IEnumerable _changedContractNames = null; + + /// + /// Initializes a new instance of the class with + /// the specified changed export definitions. + /// + /// + /// An of s of the exports + /// that have been added. + /// + /// + /// An of s of the exports + /// that have been removed. + /// + /// + /// A representing all tentative changes that will + /// be completed if the change is successful, or discarded if it is not. + /// if being applied outside a + /// or during a event. + /// + /// + /// or is . + /// + public ExportsChangeEventArgs(IEnumerable addedExports, + IEnumerable removedExports, AtomicComposition atomicComposition) + { + Requires.NotNull(addedExports, nameof(addedExports)); + Requires.NotNull(removedExports, nameof(removedExports)); + + _addedExports = addedExports.AsArray(); + _removedExports = removedExports.AsArray(); + AtomicComposition = atomicComposition; + } + + /// + /// Gets the export definitions for the exports that have been added. + /// + /// + /// A of ExportDefinitions representing + /// the exports that have been added to the . + /// + public IEnumerable AddedExports + { + get + { + Contract.Ensures(Contract.Result>() != null); + + return _addedExports; + } + } + + /// + /// Gets the export definitions for the exports that have been removed. + /// + /// + /// A of ExportDefinitions representing + /// the exports that have been added to the . + /// + public IEnumerable RemovedExports + { + get + { + Contract.Ensures(Contract.Result>() != null); + + return _removedExports; + } + } + + /// + /// Gets the contract names of the exports that have changed. + /// + /// + /// A of strings representing the contract names of + /// the exports that have changed in the . + /// + public IEnumerable ChangedContractNames + { + get + { + if (_changedContractNames == null) + { + _changedContractNames = AddedExports + .Concat(RemovedExports) + .Select(export => export.ContractName) + .Distinct() + .ToArray(); + } + return _changedContractNames; + } + } + + /// + /// Gets the atomicComposition, if any, that this change applies to. + /// + /// + /// A that this set of changes applies too. + /// + /// It can be if the changes are being applied outside a + /// or during a + /// event. + /// + /// When the value is non-null it should be used to record temporary changed state + /// and actions that will be executed when the atomicComposition is completeed. + /// + public AtomicComposition AtomicComposition { get; private set; } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.DependenciesTraversal.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.DependenciesTraversal.cs new file mode 100644 index 0000000000..a149639fca --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.DependenciesTraversal.cs @@ -0,0 +1,95 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + public partial class FilteredCatalog + { + internal class DependenciesTraversal : IComposablePartCatalogTraversal + { + private IEnumerable _parts; + private Func _importFilter; + private Dictionary> _exportersIndex; + + public DependenciesTraversal(FilteredCatalog catalog, Func importFilter) + { + Assumes.NotNull(catalog); + Assumes.NotNull(importFilter); + + _parts = catalog._innerCatalog; + _importFilter = importFilter; + } + + public void Initialize() + { + BuildExportersIndex(); + } + + private void BuildExportersIndex() + { + _exportersIndex = new Dictionary>(); + foreach (ComposablePartDefinition part in _parts) + { + foreach (var export in part.ExportDefinitions) + { + AddToExportersIndex(export.ContractName, part); + } + } + } + + private void AddToExportersIndex(string contractName, ComposablePartDefinition part) + { + List parts = null; + if (!_exportersIndex.TryGetValue(contractName, out parts)) + { + parts = new List(); + _exportersIndex.Add(contractName, parts); + } + parts.Add(part); + } + + public bool TryTraverse(ComposablePartDefinition part, out IEnumerable reachableParts) + { + reachableParts = null; + List reachablePartList = null; + + // Go through all part imports + foreach (ImportDefinition import in part.ImportDefinitions.Where(_importFilter)) + { + // Find all parts that we know will import each export + List candidateReachableParts = null; + foreach (var contractName in import.GetCandidateContractNames(part)) + { + if (_exportersIndex.TryGetValue(contractName, out candidateReachableParts)) + { + // find if they actually match + foreach (var candidateReachablePart in candidateReachableParts) + { + foreach (ExportDefinition export in candidateReachablePart.ExportDefinitions) + { + if (import.IsImportDependentOnPart(candidateReachablePart, export, part.IsGeneric() != candidateReachablePart.IsGeneric())) + { + if (reachablePartList == null) + { + reachablePartList = new List(); + } + reachablePartList.Add(candidateReachablePart); + } + } + } + } + } + } + + reachableParts = reachablePartList; + return (reachableParts != null); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.DependentsTraversal.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.DependentsTraversal.cs new file mode 100644 index 0000000000..e927078c96 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.DependentsTraversal.cs @@ -0,0 +1,101 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + public partial class FilteredCatalog + { + /// + /// Implementation of IComposablePartTraversal supporting the Dependents traveral pattern. + /// The implementation is optimized for a situation when the traversal is expected to be rather short-lived - that is, + /// if the chains of dependecies are rather small. To achieve that we do a very minimal structure prep upfront - merely creating a contract-based + /// index of imports - and the verify the full match of imports during the traversal. Given that most parts have a very few imports this should perform well. + /// + internal class DependentsTraversal : IComposablePartCatalogTraversal + { + private IEnumerable _parts; + private Func _importFilter; + private Dictionary> _importersIndex; + + public DependentsTraversal(FilteredCatalog catalog, Func importFilter) + { + Assumes.NotNull(catalog); + Assumes.NotNull(importFilter); + + _parts = catalog._innerCatalog; + _importFilter = importFilter; + } + + public void Initialize() + { + BuildImportersIndex(); + } + + private void BuildImportersIndex() + { + _importersIndex = new Dictionary>(); + foreach (ComposablePartDefinition part in _parts) + { + foreach (var import in part.ImportDefinitions) + { + foreach (var contractName in import.GetCandidateContractNames(part)) + { + AddToImportersIndex(contractName, part); + } + } + } + } + + private void AddToImportersIndex(string contractName, ComposablePartDefinition part) + { + List parts = null; + if (!_importersIndex.TryGetValue(contractName, out parts)) + { + parts = new List(); + _importersIndex.Add(contractName, parts); + } + parts.Add(part); + } + + public bool TryTraverse(ComposablePartDefinition part, out IEnumerable reachableParts) + { + reachableParts = null; + List reachablePartList = null; + + // Go through all part exports + foreach (ExportDefinition export in part.ExportDefinitions) + { + // Find all parts that we know will import each export + List candidateReachableParts = null; + if (_importersIndex.TryGetValue(export.ContractName, out candidateReachableParts)) + { + // find if they actually match + foreach (var candidateReachablePart in candidateReachableParts) + { + foreach (ImportDefinition import in candidateReachablePart.ImportDefinitions.Where(_importFilter)) + { + if (import.IsImportDependentOnPart(part, export, part.IsGeneric() != candidateReachablePart.IsGeneric())) + { + if (reachablePartList == null) + { + reachablePartList = new List(); + } + reachablePartList.Add(candidateReachablePart); + } + } + } + } + } + + reachableParts = reachablePartList; + return (reachableParts != null); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.IComposablePartCatalogTraversal.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.IComposablePartCatalogTraversal.cs new file mode 100644 index 0000000000..ecb42e8123 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.IComposablePartCatalogTraversal.cs @@ -0,0 +1,25 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; + +namespace System.ComponentModel.Composition.Hosting +{ + public partial class FilteredCatalog + { + /// + /// This is designed to traverse a set of parts based on whatever pattern. There are no real expectations + /// as to what the pattern is and what properties is posseses + /// NOTE : we both with this interface - as opposed to just a simple delegate - due to minute performance reasons, + /// as this will be invoked very often. Also, each traversal is typically associated with a big state bag, which is + /// easier to associte with an explicit implementation as opposed to an implicit closure. + /// + internal interface IComposablePartCatalogTraversal + { + void Initialize(); + bool TryTraverse(ComposablePartDefinition part, out IEnumerable reachableParts); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.Traversal.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.Traversal.cs new file mode 100644 index 0000000000..942b20c299 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.Traversal.cs @@ -0,0 +1,128 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + public partial class FilteredCatalog + { + /// + /// Creates a new instance of the that conatains all the parts from the orignal filtered catalog and all their dependecies. + /// + /// + public FilteredCatalog IncludeDependencies() + { + return IncludeDependencies(i => i.Cardinality == ImportCardinality.ExactlyOne); + } + + /// + /// Creates a new instance of the that conatains all the parts from the orignal filtered catalog and all their dependencies that + /// can be reached via imports that match the specified filter. + /// + /// The import filter. + /// + public FilteredCatalog IncludeDependencies(Func importFilter) + { + Requires.NotNull(importFilter, nameof(importFilter)); + ThrowIfDisposed(); + + return Traverse(new DependenciesTraversal(this, importFilter)); + } + + /// + /// Creates a new instance of the that conatains all the parts from the orignal filtered catalog and all their dependents. + /// + /// + public FilteredCatalog IncludeDependents() + { + return IncludeDependents(i => i.Cardinality == ImportCardinality.ExactlyOne); + } + + /// + /// Creates a new instance of the that conatains all the parts from the orignal filtered catalog and all their dependents that + /// can be reached via imports that match the specified filter. + /// + /// The import filter. + /// + public FilteredCatalog IncludeDependents(Func importFilter) + { + Requires.NotNull(importFilter, nameof(importFilter)); + ThrowIfDisposed(); + + return Traverse(new DependentsTraversal(this, importFilter)); + } + + private FilteredCatalog Traverse(IComposablePartCatalogTraversal traversal) + { + Assumes.NotNull(traversal); + + // we make sure that the underlyiong catalog cannot change while we are doing the trasversal + // After thaty traversal is done, the freeze is lifted, and the catalog is free to change, but the changes + // cannot affect partitioning + FreezeInnerCatalog(); + + try + { + traversal.Initialize(); + var traversalClosure = GetTraversalClosure(_innerCatalog.Where(_filter), traversal); + return new FilteredCatalog(_innerCatalog, p => traversalClosure.Contains(p)); + } + finally + { + UnfreezeInnerCatalog(); + } + } + + private static HashSet GetTraversalClosure(IEnumerable parts, IComposablePartCatalogTraversal traversal) + { + Assumes.NotNull(traversal); + + var traversedParts = new HashSet(); + GetTraversalClosure(parts, traversedParts, traversal); + return traversedParts; + } + + private static void GetTraversalClosure(IEnumerable parts, HashSet traversedParts, IComposablePartCatalogTraversal traversal) + { + foreach (var part in parts) + { + if (traversedParts.Add(part)) + { + IEnumerable partsToTraverse = null; + if (traversal.TryTraverse(part, out partsToTraverse)) + { + GetTraversalClosure(partsToTraverse, traversedParts, traversal); + } + } + } + } + + private void FreezeInnerCatalog() + { + INotifyComposablePartCatalogChanged innerNotifyCatalog = _innerCatalog as INotifyComposablePartCatalogChanged; + if (innerNotifyCatalog != null) + { + innerNotifyCatalog.Changing += ThrowOnRecomposition; + } + } + + private void UnfreezeInnerCatalog() + { + INotifyComposablePartCatalogChanged innerNotifyCatalog = _innerCatalog as INotifyComposablePartCatalogChanged; + if (innerNotifyCatalog != null) + { + innerNotifyCatalog.Changing -= ThrowOnRecomposition; + } + } + + private static void ThrowOnRecomposition(object sender, ComposablePartCatalogChangeEventArgs e) + { + throw new ChangeRejectedException(); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.cs new file mode 100644 index 0000000000..28b6216ecc --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.cs @@ -0,0 +1,253 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using Microsoft.Internal; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition.Hosting +{ + public partial class FilteredCatalog : ComposablePartCatalog, INotifyComposablePartCatalogChanged + { + private Func _filter; + private ComposablePartCatalog _innerCatalog; + private FilteredCatalog _complement; + private object _lock = new object(); + private volatile bool _isDisposed = false; + + /// + /// Initializes a new instance of the class. + /// + /// The catalog. + /// The filter. + public FilteredCatalog(ComposablePartCatalog catalog, Func filter) : + this(catalog, filter, null) + { + } + + internal FilteredCatalog(ComposablePartCatalog catalog, Func filter, FilteredCatalog complement) + { + Requires.NotNull(catalog, nameof(catalog)); + Requires.NotNull(filter, nameof(filter)); + + _innerCatalog = catalog; + _filter = (p) => filter.Invoke(p.GetGenericPartDefinition() ?? p); + _complement = complement; + + INotifyComposablePartCatalogChanged notifyCatalog = _innerCatalog as INotifyComposablePartCatalogChanged; + if (notifyCatalog != null) + { + notifyCatalog.Changed += OnChangedInternal; + notifyCatalog.Changing += OnChangingInternal; + } + } + +/// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected override void Dispose(bool disposing) + { + try + { + if (disposing) + { + if(!_isDisposed) + { + INotifyComposablePartCatalogChanged notifyCatalog = null; + try + { + lock (_lock) + { + if (!_isDisposed) + { + _isDisposed = true; + notifyCatalog = _innerCatalog as INotifyComposablePartCatalogChanged; + _innerCatalog = null; + } + } + } + finally + { + if (notifyCatalog != null) + { + notifyCatalog.Changed -= OnChangedInternal; + notifyCatalog.Changing -= OnChangingInternal; + } + } + } + } + } + finally + { + base.Dispose(disposing); + } + } + + public override IEnumerator GetEnumerator() + { + return _innerCatalog.Where(_filter).GetEnumerator(); + } + + /// + /// Gets the complement. + /// + /// The complement. + public FilteredCatalog Complement + { + get + { + ThrowIfDisposed(); + + if (_complement == null) + { + FilteredCatalog complement = new FilteredCatalog(_innerCatalog, p => !_filter(p), this); + lock (_lock) + { + if (_complement == null) + { + Thread.MemoryBarrier(); + _complement = complement; + complement = null; + } + } + + if (complement != null) + { + complement.Dispose(); + } + } + + return _complement; + } + } + + /// + /// Returns the export definitions that match the constraint defined by the specified definition. + /// + /// The that defines the conditions of the + /// objects to return. + /// + /// An of containing the + /// objects and their associated + /// for objects that match the constraint defined + /// by . + /// + /// + /// is . + /// + /// + /// The has been disposed of. + /// + /// + /// + /// Overriders of this property should never return , if no + /// match the conditions defined by + /// , return an empty . + /// + /// + public override IEnumerable> GetExports(ImportDefinition definition) + { + ThrowIfDisposed(); + Requires.NotNull(definition, nameof(definition)); + + var exports = new List>(); + foreach(var export in _innerCatalog.GetExports(definition)) + { + if (_filter(export.Item1)) + { + exports.Add(export); + } + } + + return exports; + } + + /// + /// Notify when the contents of the Catalog has changed. + /// + public event EventHandler Changed; + +/// + /// Notify when the contents of the Catalog is changing. + /// + public event EventHandler Changing; + +/// + /// Raises the event. + /// + /// The instance containing the event data. + protected virtual void OnChanged(ComposablePartCatalogChangeEventArgs e) + { + EventHandler changedEvent = Changed; + if (changedEvent != null) + { + changedEvent.Invoke(this, e); + } + } + + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected virtual void OnChanging(ComposablePartCatalogChangeEventArgs e) + { + EventHandler changingEvent = Changing; + if (changingEvent != null) + { + changingEvent.Invoke(this, e); + } + } + + private void OnChangedInternal(object sender, ComposablePartCatalogChangeEventArgs e) + { + var processedArgs = ProcessEventArgs(e); + if (processedArgs != null) + { + OnChanged(ProcessEventArgs(processedArgs)); + } + } + + private void OnChangingInternal(object sender, ComposablePartCatalogChangeEventArgs e) + { + var processedArgs = ProcessEventArgs(e); + if (processedArgs != null) + { + OnChanging(ProcessEventArgs(processedArgs)); + } + } + + private ComposablePartCatalogChangeEventArgs ProcessEventArgs(ComposablePartCatalogChangeEventArgs e) + { + // the constructor for ComposablePartCatalogChangeEventArgs takes a snapshot of the arguments, so we don't have to + var result = new ComposablePartCatalogChangeEventArgs( + e.AddedDefinitions.Where(_filter), + e.RemovedDefinitions.Where(_filter), + e.AtomicComposition); + + // Only fire if we need to + if (result.AddedDefinitions.FastAny() || result.RemovedDefinitions.FastAny()) + { + return result; + } + else + { + return null; + } + } + + [DebuggerStepThrough] + private void ThrowIfDisposed() + { + if (_isDisposed) + { + throw ExceptionBuilder.CreateObjectDisposed(this); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/INotifyComposablePartCatalogChanged.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/INotifyComposablePartCatalogChanged.cs new file mode 100644 index 0000000000..d827af22fb --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/INotifyComposablePartCatalogChanged.cs @@ -0,0 +1,15 @@ +// 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. + +namespace System.ComponentModel.Composition.Hosting +{ + /// + /// Notifications when a ComposablePartCatalog changes. + /// + public interface INotifyComposablePartCatalogChanged + { + event EventHandler Changed; + event EventHandler Changing; + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.EngineContext.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.EngineContext.cs new file mode 100644 index 0000000000..9610878358 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.EngineContext.cs @@ -0,0 +1,80 @@ +// 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.Collections.Generic; +using Microsoft.Internal; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition.Hosting +{ + public partial class ImportEngine + { + /// + /// Used to wrap the start and stop of enforcing export changes don't + /// break required imports. This context is stored in a AtomicComposition. + /// + private class EngineContext + { + private ImportEngine _importEngine; + private List _addedPartManagers = new List(); + private List _removedPartManagers = new List(); + private EngineContext _parentEngineContext; + + public EngineContext(ImportEngine importEngine, EngineContext parentEngineContext) + { + _importEngine = importEngine; + _parentEngineContext = parentEngineContext; + } + + public void AddPartManager(PartManager part) + { + Assumes.NotNull(part); + if (!_removedPartManagers.Remove(part)) + { + _addedPartManagers.Add(part); + } + } + + public void RemovePartManager(PartManager part) + { + Assumes.NotNull(part); + if (!_addedPartManagers.Remove(part)) + { + _removedPartManagers.Add(part); + } + } + + public IEnumerable GetAddedPartManagers() + { + if (_parentEngineContext != null) + { + return _addedPartManagers.ConcatAllowingNull(_parentEngineContext.GetAddedPartManagers()); + } + return _addedPartManagers; + } + + public IEnumerable GetRemovedPartManagers() + { + if (_parentEngineContext != null) + { + return _removedPartManagers.ConcatAllowingNull(_parentEngineContext.GetRemovedPartManagers()); + } + return _removedPartManagers; + } + + public void Complete() + { + foreach (var partManager in _addedPartManagers) + { + _importEngine.StartSatisfyingImports(partManager, null); + } + + foreach (var partManager in _removedPartManagers) + { + _importEngine.StopSatisfyingImports(partManager, null); + } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.PartManager.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.PartManager.cs new file mode 100644 index 0000000000..0771179b85 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.PartManager.cs @@ -0,0 +1,212 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition.Hosting +{ + public partial class ImportEngine + { + /// + /// Used by the to manage the composition of a given part. + /// It stores things like the list of disposable exports used to satisfy the imports as + /// well as the caching of the exports discovered during previewing of a part. + /// + private class PartManager + { + private Dictionary> _importedDisposableExports; + private Dictionary _importCache; + private string[] _importedContractNames; + private ComposablePart _part; + private ImportState _state = ImportState.NoImportsSatisfied; + private readonly ImportEngine _importEngine; + + public PartManager(ImportEngine importEngine, ComposablePart part) + { + _importEngine = importEngine; + _part = part; + } + + public ComposablePart Part + { + get + { + return _part; + } + } + + public ImportState State + { + get + { + using (_importEngine._lock.LockStateForRead()) + { + return _state; + } + } + set + { + using (_importEngine._lock.LockStateForWrite()) + { + _state = value; + } + } + } + + public bool TrackingImports { get; set; } + + public IEnumerable GetImportedContractNames() + { + if (Part == null) + { + return Enumerable.Empty(); + } + + if (_importedContractNames == null) + { + _importedContractNames = Part.ImportDefinitions.Select(import => import.ContractName ?? ImportDefinition.EmptyContractName).Distinct().ToArray(); + } + return _importedContractNames; + } + + public CompositionResult TrySetImport(ImportDefinition import, Export[] exports) + { + try + { + Part.SetImport(import, exports); + UpdateDisposableDependencies(import, exports); + return CompositionResult.SucceededResult; + } + catch (CompositionException ex) + { // Pulling on one of the exports failed + + return new CompositionResult( + ErrorBuilder.CreatePartCannotSetImport(Part, import, ex)); + } + catch (ComposablePartException ex) + { // Type mismatch between export and import + + return new CompositionResult( + ErrorBuilder.CreatePartCannotSetImport(Part, import, ex)); + } + } + + public void SetSavedImport(ImportDefinition import, Export[] exports, AtomicComposition atomicComposition) + { + if (atomicComposition != null) + { + var savedExports = GetSavedImport(import); + + // Add a revert action to revert the stored exports + // in the case that this atomicComposition gets rolled back. + atomicComposition.AddRevertAction(() => + SetSavedImport(import, savedExports, null)); + } + + if (_importCache == null) + { + _importCache = new Dictionary(); + } + + _importCache[import] = exports; + } + + public Export[] GetSavedImport(ImportDefinition import) + { + Export[] exports = null; + if (_importCache != null) + { + // We don't care about the return value we just want the exports + // and if it isn't present we just return the initialized null value + _importCache.TryGetValue(import, out exports); + } + return exports; + } + + public void ClearSavedImports() + { + _importCache = null; + } + + public CompositionResult TryOnComposed() + { + try + { + Part.Activate(); + return CompositionResult.SucceededResult; + } + catch (ComposablePartException ex) + { // Type failed to be constructed, imports could not be set, etc + return new CompositionResult( + ErrorBuilder.CreatePartCannotActivate(Part, ex)); + } + } + + public void UpdateDisposableDependencies(ImportDefinition import, Export[] exports) + { + // Determine if there are any new disposable exports, optimizing for the most + // likely case, which is that there aren't any + List disposableExports = null; + foreach (var export in exports) + { + IDisposable disposableExport = export as IDisposable; + if (disposableExport != null) + { + if (disposableExports == null) + { + disposableExports = new List(); + } + disposableExports.Add(disposableExport); + } + } + + // Dispose any existing references previously set on this import + List oldDisposableExports = null; + if (_importedDisposableExports != null && + _importedDisposableExports.TryGetValue(import, out oldDisposableExports)) + { + oldDisposableExports.ForEach(disposable => disposable.Dispose()); + + // If there aren't any replacements, get rid of the old storage + if (disposableExports == null) + { + _importedDisposableExports.Remove(import); + if (!_importedDisposableExports.FastAny()) + { + _importedDisposableExports = null; + } + + return; + } + } + + // Record the new collection + if (disposableExports != null) + { + if (_importedDisposableExports == null) + { + _importedDisposableExports = new Dictionary>(); + } + _importedDisposableExports[import] = disposableExports; + } + } + + public void DisposeAllDependencies() + { + if (_importedDisposableExports != null) + { + IEnumerable dependencies = _importedDisposableExports.Values + .SelectMany(exports => exports); + + _importedDisposableExports = null; + + dependencies.ForEach(disposableExport => disposableExport.Dispose()); + } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.RecompositionManager.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.RecompositionManager.cs new file mode 100644 index 0000000000..01cd7777bc --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.RecompositionManager.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. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition.Hosting +{ + public partial class ImportEngine + { + /// + /// Used by the to effiecently store and retrieve the list of parts + /// that will be affected by changes to exports. This allows the to properly + /// block breaking changes and also recompose imports as appropriate. + /// + private class RecompositionManager + { + private WeakReferenceCollection _partsToIndex = new WeakReferenceCollection(); + private WeakReferenceCollection _partsToUnindex = new WeakReferenceCollection(); + private Dictionary> _partManagerIndex = new Dictionary>(); + + public void AddPartToIndex(PartManager partManager) + { + _partsToIndex.Add(partManager); + } + + public void AddPartToUnindex(PartManager partManager) + { + _partsToUnindex.Add(partManager); + } + + public IEnumerable GetAffectedParts(IEnumerable changedContractNames) + { + UpdateImportIndex(); + + List parts = new List(); + + parts.AddRange(GetPartsImporting(ImportDefinition.EmptyContractName)); + + foreach (string contractName in changedContractNames) + { + parts.AddRange(GetPartsImporting(contractName)); + } + + return parts; + } + + public static IEnumerable GetAffectedImports(ComposablePart part, IEnumerable changedExports) + { + return part.ImportDefinitions.Where(import => IsAffectedImport(import, changedExports)); + } + + private static bool IsAffectedImport(ImportDefinition import, IEnumerable changedExports) + { + // This could be more efficient still if the export definitions were indexed by contract name, + // only worth revisiting if we need to squeeze more performance out of recomposition + foreach (var export in changedExports) + { + if (import.IsConstraintSatisfiedBy(export)) + { + return true; + } + } + + return false; + } + + public IEnumerable GetPartsImporting(string contractName) + { + WeakReferenceCollection partManagerList; + if (!_partManagerIndex.TryGetValue(contractName, out partManagerList)) + { + return Enumerable.Empty(); + } + + return partManagerList.AliveItemsToList(); + } + + private void AddIndexEntries(PartManager partManager) + { + foreach (string contractName in partManager.GetImportedContractNames()) + { + WeakReferenceCollection indexEntries; + if (!_partManagerIndex.TryGetValue(contractName, out indexEntries)) + { + indexEntries = new WeakReferenceCollection(); + _partManagerIndex.Add(contractName, indexEntries); + } + + if (!indexEntries.Contains(partManager)) + { + indexEntries.Add(partManager); + } + } + } + + private void RemoveIndexEntries(PartManager partManager) + { + foreach (string contractName in partManager.GetImportedContractNames()) + { + WeakReferenceCollection indexEntries; + if (_partManagerIndex.TryGetValue(contractName, out indexEntries)) + { + indexEntries.Remove(partManager); + var aliveItems = indexEntries.AliveItemsToList(); + + if (aliveItems.Count == 0) + { + _partManagerIndex.Remove(contractName); + } + } + } + } + + private void UpdateImportIndex() + { + var partsToIndex = _partsToIndex.AliveItemsToList(); + _partsToIndex.Clear(); + + var partsToUnindex = _partsToUnindex.AliveItemsToList(); + _partsToUnindex.Clear(); + + if (partsToIndex.Count == 0 && partsToUnindex.Count == 0) + { + return; + } + + foreach (var partManager in partsToIndex) + { + var index = partsToUnindex.IndexOf(partManager); + + // If the same part is being added and removed we can ignore both + if (index >= 0) + { + partsToUnindex[index] = null; + } + else + { + AddIndexEntries(partManager); + } + } + + foreach (var partManager in partsToUnindex) + { + if (partManager != null) + { + RemoveIndexEntries(partManager); + } + } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.cs new file mode 100644 index 0000000000..9c013fcffb --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.cs @@ -0,0 +1,780 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics; +using System.Linq; +using System.Runtime.CompilerServices; +using Microsoft.Internal; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition.Hosting +{ + // This class guarantees thread-safety under the following conditions: + // - Each composition is executed on a single thread + // - No recomposition ever takes place + // - The class is created with isThreadSafe=true + public partial class ImportEngine : ICompositionService, IDisposable + { + private const int MaximumNumberOfCompositionIterations = 100; + + private volatile bool _isDisposed; + private ExportProvider _sourceProvider; + private Stack _recursionStateStack = new Stack(); + private ConditionalWeakTable _partManagers = new ConditionalWeakTable(); + private RecompositionManager _recompositionManager = new RecompositionManager(); + private readonly CompositionLock _lock = null; + private readonly CompositionOptions _compositionOptions; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The which provides the + /// access to s. + /// + public ImportEngine(ExportProvider sourceProvider) + : this(sourceProvider, CompositionOptions.Default) + { + } + + public ImportEngine(ExportProvider sourceProvider, bool isThreadSafe) + : this(sourceProvider, isThreadSafe ? CompositionOptions.IsThreadSafe : CompositionOptions.Default) + { + } + + public ImportEngine(ExportProvider sourceProvider, CompositionOptions compositionOptions) + { + Requires.NotNull(sourceProvider, nameof(sourceProvider)); + + _compositionOptions = compositionOptions; + _sourceProvider = sourceProvider; + _sourceProvider.ExportsChanging += OnExportsChanging; + _lock = new CompositionLock(compositionOptions.HasFlag(CompositionOptions.IsThreadSafe)); + } + + /// + /// Previews all the required imports for the given to + /// ensure they can all be satisified. The preview does not actually set the imports + /// only ensures that they exist in the source provider. If the preview succeeds then + /// the also enforces that changes to exports in the source + /// provider will not break any of the required imports. If this enforcement needs to be + /// lifted for this part then needs to be called for this + /// . + /// + /// + /// The to preview the required imports. + /// + /// + /// + /// An error occurred during previewing and is null. + /// will contain a collection of errors that occurred. + /// The pre-existing composition is in an unknown state, depending on the errors that occured. + /// + /// + /// An error occurred during the previewing and is not null. + /// will contain a collection of errors that occurred. + /// The pre-existing composition remains in valid state. + /// + /// + /// The has been disposed of. + /// + public void PreviewImports(ComposablePart part, AtomicComposition atomicComposition) + { + ThrowIfDisposed(); + + Requires.NotNull(part, nameof(part)); + + // Do not do any previewing if SilentRejection is disabled. + if (_compositionOptions.HasFlag(CompositionOptions.DisableSilentRejection)) + { + return; + } + + // NOTE : this is a very intricate area threading-wise, please use caution when changing, otherwise state corruption or deadlocks will ensue + // The gist of what we are doing is as follows: + // We need to lock the composition, as we will proceed modifying our internal state. The tricky part is when we release the lock + // Due to the fact that some actions will take place AFTER we leave this method, we need to KEEP THAT LOCK HELD until the transation is commiited or rolled back + // This is the reason we CAN'T use "using here. + // Instead, if the transaction is present we will queue up the release of the lock, otherwise we will release it when we exit this method + // We add the "release" lock to BOTH Commit and Revert queues, because they are mutually exclusive, and we need to release the lock regardless. + + // This will take the lock, if necesary + IDisposable compositionLockHolder = _lock.IsThreadSafe ? _lock.LockComposition() : null; + bool compositionLockTaken = (compositionLockHolder != null); + try + { + // revert actions are processed in the reverse order, so we have to add the "release lock" action now + if (compositionLockTaken && (atomicComposition != null)) + { + atomicComposition.AddRevertAction(() => compositionLockHolder.Dispose()); + } + + var partManager = GetPartManager(part, true); + var result = TryPreviewImportsStateMachine(partManager, part, atomicComposition); + result.ThrowOnErrors(atomicComposition); + + StartSatisfyingImports(partManager, atomicComposition); + + // Add the "release lock" to the commit actions + if (compositionLockTaken && (atomicComposition != null)) + { + atomicComposition.AddCompleteAction(() => compositionLockHolder.Dispose()); + } + } + finally + { + // We haven't updated the queues, so we can release the lock now + if (compositionLockTaken && (atomicComposition == null)) + { + compositionLockHolder.Dispose(); + } + } + } + + /// + /// Satisfies the imports of the specified composable part. If the satisfy succeeds then + /// the also enforces that changes to exports in the source + /// provider will not break any of the required imports. If this enforcement needs to be + /// lifted for this part then needs to be called for this + /// . + /// + /// + /// The to set the imports. + /// + /// + /// is . + /// + /// + /// An error occurred during composition. will + /// contain a collection of errors that occurred. + /// + /// + /// The has been disposed of. + /// + public void SatisfyImports(ComposablePart part) + { + ThrowIfDisposed(); + + Requires.NotNull(part, nameof(part)); + + // NOTE : the following two calls use the state lock + PartManager partManager = GetPartManager(part, true); + if (partManager.State == ImportState.Composed) + { + return; + } + + using (_lock.LockComposition()) + { + var result = TrySatisfyImports(partManager, part, true); + result.ThrowOnErrors(); // throw CompositionException not ChangeRejectedException + } + } + + /// + /// Sets the imports of the specified composable part exactly once and they will not + /// ever be recomposed. + /// + /// + /// The to set the imports. + /// + /// + /// is . + /// + /// + /// An error occurred during composition. will + /// contain a collection of errors that occurred. + /// + /// + /// The has been disposed of. + /// + public void SatisfyImportsOnce(ComposablePart part) + { + ThrowIfDisposed(); + + Requires.NotNull(part, nameof(part)); + + // NOTE : the following two calls use the state lock + PartManager partManager = GetPartManager(part, true); + if (partManager.State == ImportState.Composed) + { + return; + } + + using (_lock.LockComposition()) + { + var result = TrySatisfyImports(partManager, part, false); + result.ThrowOnErrors(); // throw CompositionException not ChangeRejectedException + } + } + + /// + /// Removes any state stored in the for the associated + /// and releases all the s used to + /// satisfy the imports on the . + /// + /// Also removes the enforcement for changes that would break a required import on + /// . + /// + /// + /// The to release the imports on. + /// + /// + /// The that the release imports is running under. + /// + public void ReleaseImports(ComposablePart part, AtomicComposition atomicComposition) + { + ThrowIfDisposed(); + + Requires.NotNull(part, nameof(part)); + + using (_lock.LockComposition()) + { + PartManager partManager = GetPartManager(part, false); + if (partManager != null) + { + StopSatisfyingImports(partManager, atomicComposition); + } + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (!_isDisposed) + { + bool disposeLock = false; + ExportProvider sourceProviderToUnsubscribeFrom = null; + using (_lock.LockStateForWrite()) + { + if (!_isDisposed) + { + sourceProviderToUnsubscribeFrom = _sourceProvider; + _sourceProvider = null; + _recompositionManager = null; + _partManagers = null; + _isDisposed = true; + disposeLock = true; + } + } + + if (sourceProviderToUnsubscribeFrom != null) + { + sourceProviderToUnsubscribeFrom.ExportsChanging -= OnExportsChanging; + } + + if (disposeLock) + { + _lock.Dispose(); + } + } + } + } + + private CompositionResult TryPreviewImportsStateMachine(PartManager partManager, + ComposablePart part, AtomicComposition atomicComposition) + { + var result = CompositionResult.SucceededResult; + + if (partManager.State == ImportState.ImportsPreviewing) + { + // We shouldn't nomally ever hit this case but if we do + // then we should just error with a cycle error. + return new CompositionResult(ErrorBuilder.CreatePartCycle(part)); + } + + // Transition from NoImportsStatisified to ImportsPreviewed + if (partManager.State == ImportState.NoImportsSatisfied) + { + partManager.State = ImportState.ImportsPreviewing; + + var requiredImports = part.ImportDefinitions.Where(IsRequiredImportForPreview); + + // If this atomicComposition gets rolledback for any reason we need to reset our state + atomicComposition.AddRevertActionAllowNull(() => partManager.State = ImportState.NoImportsSatisfied); + + result = result.MergeResult( + TrySatisfyImportSubset(partManager, requiredImports, atomicComposition)); + + if (!result.Succeeded) + { + partManager.State = ImportState.NoImportsSatisfied; + return result; + } + + partManager.State = ImportState.ImportsPreviewed; + } + + return result; + } + + private CompositionResult TrySatisfyImportsStateMachine(PartManager partManager, ComposablePart part) + { + var result = CompositionResult.SucceededResult; + + while (partManager.State < ImportState.Composed) + { + var previousState = partManager.State; + + switch (partManager.State) + { + // "ed" states which represent a some sort of steady state and will + // attempt to do a state transition + case ImportState.NoImportsSatisfied: + case ImportState.ImportsPreviewed: + { + partManager.State = ImportState.PreExportImportsSatisfying; + + var prereqImports = part.ImportDefinitions.Where(import => import.IsPrerequisite); + result = result.MergeResult( + TrySatisfyImportSubset(partManager, prereqImports, null)); + + partManager.State = ImportState.PreExportImportsSatisfied; + break; + } + case ImportState.PreExportImportsSatisfied: + { + partManager.State = ImportState.PostExportImportsSatisfying; + + var requiredImports = part.ImportDefinitions.Where(import => !import.IsPrerequisite); + + result = result.MergeResult( + TrySatisfyImportSubset(partManager, requiredImports, null)); + + partManager.State = ImportState.PostExportImportsSatisfied; + break; + } + case ImportState.PostExportImportsSatisfied: + { + partManager.State = ImportState.ComposedNotifying; + + partManager.ClearSavedImports(); + result = result.MergeResult(partManager.TryOnComposed()); + + partManager.State = ImportState.Composed; + break; + } + + // "ing" states which represent some sort of cycle + // These state should always return, error or not, instead of breaking + case ImportState.ImportsPreviewing: + { + // We shouldn't nomally ever hit this case but if we do + // then we should just error with a cycle error. + return new CompositionResult(ErrorBuilder.CreatePartCycle(part)); + } + case ImportState.PreExportImportsSatisfying: + case ImportState.PostExportImportsSatisfying: + { + if (InPrerequisiteLoop()) + { + return result.MergeError(ErrorBuilder.CreatePartCycle(part)); + } + // Cycles in post export imports are allowed so just return in that case + return result; + } + case ImportState.ComposedNotifying: + { + // We are currently notifying so don't notify again just return + return result; + } + } + + // if an error occured while doing a state transition + if (!result.Succeeded) + { + // revert to the previous state and return the error + partManager.State = previousState; + return result; + } + } + return result; + } + + private CompositionResult TrySatisfyImports(PartManager partManager, ComposablePart part, bool shouldTrackImports) + { + Assumes.NotNull(part); + + var result = CompositionResult.SucceededResult; + + // get out if the part is already composed + if (partManager.State == ImportState.Composed) + { + return result; + } + + // Track number of recursive iterations and throw an exception before the stack + // fills up and debugging the root cause becomes tricky + if (_recursionStateStack.Count >= MaximumNumberOfCompositionIterations) + { + return result.MergeError( + ErrorBuilder.ComposeTookTooManyIterations(MaximumNumberOfCompositionIterations)); + } + + // Maintain the stack to detect whether recursive loops cross prerequisites + _recursionStateStack.Push(partManager); + try + { + result = result.MergeResult( + TrySatisfyImportsStateMachine(partManager, part)); + } + finally + { + _recursionStateStack.Pop(); + } + + if (shouldTrackImports) + { + StartSatisfyingImports(partManager, null); + } + + return result; + } + + private CompositionResult TrySatisfyImportSubset(PartManager partManager, + IEnumerable imports, AtomicComposition atomicComposition) + { + CompositionResult result = CompositionResult.SucceededResult; + + var part = partManager.Part; + foreach (ImportDefinition import in imports) + { + var exports = partManager.GetSavedImport(import); + + if (exports == null) + { + CompositionResult> exportsResult = TryGetExports( + _sourceProvider, part, import, atomicComposition); + + if (!exportsResult.Succeeded) + { + result = result.MergeResult(exportsResult.ToResult()); + continue; + } + exports = exportsResult.Value.AsArray(); + } + + if (atomicComposition == null) + { + result = result.MergeResult( + partManager.TrySetImport(import, exports)); + } + else + { + partManager.SetSavedImport(import, exports, atomicComposition); + } + } + return result; + } + + private void OnExportsChanging(object sender, ExportsChangeEventArgs e) + { + CompositionResult result = CompositionResult.SucceededResult; + + // Prepare for the recomposition effort by minimizing the amount of work we'll have to do later + AtomicComposition atomicComposition = e.AtomicComposition; + + IEnumerable affectedParts = _recompositionManager.GetAffectedParts(e.ChangedContractNames); + + // When in a atomicComposition account for everything that isn't yet reflected in the + // index + if (atomicComposition != null) + { + EngineContext engineContext; + if (atomicComposition.TryGetValue(this, out engineContext)) + { + // always added the new part managers to see if they will also be + // affected by these changes + affectedParts = affectedParts.ConcatAllowingNull(engineContext.GetAddedPartManagers()) + .Except(engineContext.GetRemovedPartManagers()); + } + } + + var changedExports = e.AddedExports.ConcatAllowingNull(e.RemovedExports); + + foreach (var partManager in affectedParts) + { + result = result.MergeResult(TryRecomposeImports(partManager, changedExports, atomicComposition)); + } + + result.ThrowOnErrors(atomicComposition); + } + + private CompositionResult TryRecomposeImports(PartManager partManager, + IEnumerable changedExports, AtomicComposition atomicComposition) + { + var result = CompositionResult.SucceededResult; + + switch (partManager.State) + { + case ImportState.ImportsPreviewed: + case ImportState.Composed: + // Validate states to continue. + break; + + default: + { + // All other states are invalid and for recomposition. + return new CompositionResult(ErrorBuilder.InvalidStateForRecompposition(partManager.Part)); + } + } + + var affectedImports = RecompositionManager.GetAffectedImports(partManager.Part, changedExports); + bool partComposed = (partManager.State == ImportState.Composed); + + bool recomposedImport = false; + foreach (var import in affectedImports) + { + result = result.MergeResult( + TryRecomposeImport(partManager, partComposed, import, atomicComposition)); + + recomposedImport = true; + } + + // Knowing that the part has already been composed before and that the only possible + // changes are to recomposable imports, we can safely go ahead and do this now or + // schedule it for later + if (result.Succeeded && recomposedImport && partComposed) + { + if (atomicComposition == null) + { + result = result.MergeResult(partManager.TryOnComposed()); + } + else + { + atomicComposition.AddCompleteAction(() => partManager.TryOnComposed().ThrowOnErrors()); + } + } + + return result; + } + + private CompositionResult TryRecomposeImport(PartManager partManager, bool partComposed, + ImportDefinition import, AtomicComposition atomicComposition) + { + if (partComposed && !import.IsRecomposable) + { + return new CompositionResult(ErrorBuilder.PreventedByExistingImport(partManager.Part, import)); + } + + // During recomposition you must always requery with the new atomicComposition you cannot use any + // cached value in the part manager + var exportsResult = TryGetExports(_sourceProvider, partManager.Part, import, atomicComposition); + if (!exportsResult.Succeeded) + { + return exportsResult.ToResult(); + } + var exports = exportsResult.Value.AsArray(); + + if (partComposed) + { + // Knowing that the part has already been composed before and that the only possible + // changes are to recomposable imports, we can safely go ahead and do this now or + // schedule it for later + if (atomicComposition == null) + { + return partManager.TrySetImport(import, exports); + } + else + { + atomicComposition.AddCompleteAction(() => partManager.TrySetImport(import, exports).ThrowOnErrors()); + } + } + else + { + partManager.SetSavedImport(import, exports, atomicComposition); + } + + return CompositionResult.SucceededResult; + } + + private void StartSatisfyingImports(PartManager partManager, AtomicComposition atomicComposition) + { + // When not running in a atomicCompositional state, schedule reindexing after ensuring + // that this isn't a redundant addition + if (atomicComposition == null) + { + if (!partManager.TrackingImports) + { + partManager.TrackingImports = true; + _recompositionManager.AddPartToIndex(partManager); + } + } + else + { + // While in a atomicCompositional state use a less efficient but effective means + // of achieving the same results + GetEngineContext(atomicComposition).AddPartManager(partManager); + } + } + + private void StopSatisfyingImports(PartManager partManager, AtomicComposition atomicComposition) + { + // When not running in a atomicCompositional state, schedule reindexing after ensuring + // that this isn't a redundant removal + if (atomicComposition == null) + { + ConditionalWeakTable partManagers = null; + RecompositionManager recompositionManager = null; + + using (_lock.LockStateForRead()) + { + partManagers = _partManagers; + recompositionManager = _recompositionManager; + } + if (partManagers != null) // Disposal race may have been won by dispose + { + partManagers.Remove(partManager.Part); + + // Take care of lifetime requirements + partManager.DisposeAllDependencies(); + + if (partManager.TrackingImports) + { + partManager.TrackingImports = false; + recompositionManager.AddPartToUnindex(partManager); + } + } + } + else + { + // While in a atomicCompositional state use a less efficient but effective means + // of achieving the same results + GetEngineContext(atomicComposition).RemovePartManager(partManager); + } + } + + private PartManager GetPartManager(ComposablePart part, bool createIfNotpresent) + { + PartManager partManager = null; + using (_lock.LockStateForRead()) + { + if (_partManagers.TryGetValue(part, out partManager)) + { + return partManager; + } + } + + if (createIfNotpresent) + { + using (_lock.LockStateForWrite()) + { + if (!_partManagers.TryGetValue(part, out partManager)) + { + partManager = new PartManager(this, part); + _partManagers.Add(part, partManager); + } + } + } + return partManager; + } + + private EngineContext GetEngineContext(AtomicComposition atomicComposition) + { + Assumes.NotNull(atomicComposition); + + EngineContext engineContext; + if (!atomicComposition.TryGetValue(this, true, out engineContext)) + { + EngineContext parentContext; + atomicComposition.TryGetValue(this, false, out parentContext); + engineContext = new EngineContext(this, parentContext); + atomicComposition.SetValue(this, engineContext); + atomicComposition.AddCompleteAction(engineContext.Complete); + } + return engineContext; + } + + private bool InPrerequisiteLoop() + { + PartManager firstPart = _recursionStateStack.First(); + PartManager lastPart = null; + + foreach (PartManager testPart in _recursionStateStack.Skip(1)) + { + if (testPart.State == ImportState.PreExportImportsSatisfying) + { + return true; + } + + if (testPart == firstPart) + { + lastPart = testPart; + break; + } + } + + // This should only be called when a loop has been detected - so it should always be on the stack + Assumes.IsTrue(lastPart == firstPart); + return false; + } + + [DebuggerStepThrough] + private void ThrowIfDisposed() + { + if (_isDisposed) + { + throw ExceptionBuilder.CreateObjectDisposed(this); + } + } + + private static CompositionResult> TryGetExports(ExportProvider provider, + ComposablePart part, ImportDefinition definition, AtomicComposition atomicComposition) + { + try + { + IEnumerable exports = null; + if (provider != null) + { + exports = provider.GetExports(definition, atomicComposition).AsArray(); + } + return new CompositionResult>(exports); + } + catch (ImportCardinalityMismatchException ex) + { + // Either not enough or too many exports that match the definition + CompositionException exception = new CompositionException(ErrorBuilder.CreateImportCardinalityMismatch(ex, definition)); + + return new CompositionResult>( + ErrorBuilder.CreatePartCannotSetImport(part, definition, exception)); + } + } + + internal static bool IsRequiredImportForPreview(ImportDefinition import) + { + return import.Cardinality == ImportCardinality.ExactlyOne; + } + + // Ordering of this enum is important so be sure to use caution if you + // try to reorder them. + private enum ImportState + { + NoImportsSatisfied = 0, + ImportsPreviewing = 1, + ImportsPreviewed = 2, + PreExportImportsSatisfying = 3, + PreExportImportsSatisfied = 4, + PostExportImportsSatisfying = 5, + PostExportImportsSatisfied = 6, + ComposedNotifying = 7, + Composed = 8, + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportSourceImportDefinitionHelpers.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportSourceImportDefinitionHelpers.cs new file mode 100644 index 0000000000..7399b0d649 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportSourceImportDefinitionHelpers.cs @@ -0,0 +1,115 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics.Contracts; +using System.Linq.Expressions; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + + internal static class ImportSourceImportDefinitionHelpers + { + public static ImportDefinition RemoveImportSource(this ImportDefinition definition) + { + var contractBasedDefinition = definition as ContractBasedImportDefinition; + if(contractBasedDefinition == null) + { + return definition; + } + else + { + return new NonImportSourceImportDefinition(contractBasedDefinition); + } + } + + internal class NonImportSourceImportDefinition : ContractBasedImportDefinition + { + private ContractBasedImportDefinition _sourceDefinition; + private IDictionary _metadata; + + public NonImportSourceImportDefinition(ContractBasedImportDefinition sourceDefinition) + { + Assumes.NotNull(sourceDefinition); + _sourceDefinition = sourceDefinition; + _metadata = null; + } + + public override string ContractName + { + get { return _sourceDefinition.ContractName; } + } + + public override IDictionary Metadata + { + get + { + Contract.Ensures(Contract.Result>() != null); + + var reply = _metadata; + if(reply == null) + { + reply = new Dictionary (_sourceDefinition.Metadata); + reply.Remove(CompositionConstants.ImportSourceMetadataName); + _metadata = reply; + } + + return reply; + } + } + + public override ImportCardinality Cardinality + { + get { return _sourceDefinition.Cardinality; } + } + + public override Expression> Constraint + { + get { return _sourceDefinition.Constraint; } + } + + public override bool IsPrerequisite + { + get { return _sourceDefinition.IsPrerequisite; } + } + + public override bool IsRecomposable + { + get { return _sourceDefinition.IsRecomposable; } + } + + public override bool IsConstraintSatisfiedBy(ExportDefinition exportDefinition) + { + Requires.NotNull(exportDefinition, nameof(exportDefinition)); + + return _sourceDefinition.IsConstraintSatisfiedBy(exportDefinition); + } + + public override string ToString() + { + return _sourceDefinition.ToString(); + } + + public override string RequiredTypeIdentity + { + get { return _sourceDefinition.RequiredTypeIdentity; } + } + + public override IEnumerable> RequiredMetadata + { + get + { + return _sourceDefinition.RequiredMetadata; + } + } + + public override CreationPolicy RequiredCreationPolicy + { + get { return _sourceDefinition.RequiredCreationPolicy; } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ScopingExtensions.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ScopingExtensions.cs new file mode 100644 index 0000000000..9d43859773 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ScopingExtensions.cs @@ -0,0 +1,145 @@ +// 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.ComponentModel.Composition.Primitives; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Hosting +{ + public static class ScopingExtensions + { + /// + /// Determines whether the specified part exports the specified contract. + /// + /// The part. + /// Name of the contract. + /// + /// true if the specified part exports the specified contract; otherwise, false. + /// + public static bool Exports(this ComposablePartDefinition part, string contractName) + { + Requires.NotNull(part, nameof(part)); + Requires.NotNull(contractName, nameof(contractName)); + + foreach (ExportDefinition export in part.ExportDefinitions) + { + if (StringComparers.ContractName.Equals(contractName, export.ContractName)) + { + return true; + } + } + return false; + } + + /// + /// Determines whether the specified part imports the specified contract. + /// + /// The part. + /// Name of the contract. + /// + /// true if the specified part imports the specified contract; otherwise, false. + /// + public static bool Imports(this ComposablePartDefinition part, string contractName) + { + Requires.NotNull(part, nameof(part)); + Requires.NotNull(contractName, nameof(contractName)); + + foreach (ImportDefinition import in part.ImportDefinitions) + { + if (StringComparers.ContractName.Equals(contractName, import.ContractName)) + { + return true; + } + } + + return false; + } + + /// + /// Determines whether the specified part imports the specified contract with the given cardinality. + /// + /// The part. + /// Name of the contract. + /// The import cardinality. + /// + /// true if the specified part imports the specified contract with the given cardinality; otherwise, false. + /// + public static bool Imports(this ComposablePartDefinition part, string contractName, ImportCardinality importCardinality) + { + Requires.NotNull(part, nameof(part)); + Requires.NotNull(contractName, nameof(contractName)); + + foreach (ImportDefinition import in part.ImportDefinitions) + { + if (StringComparers.ContractName.Equals(contractName, import.ContractName) && (import.Cardinality == importCardinality)) + { + return true; + } + } + + return false; + } + +/// + /// Determines whether the part contains a metadata entry with the specified key. + /// + /// The part. + /// The key. + /// + /// true if the part contains a metadata entry with the specified key; otherwise, false. + /// + public static bool ContainsPartMetadataWithKey(this ComposablePartDefinition part, string key) + { + Requires.NotNull(part, nameof(part)); + Requires.NotNull(key, nameof(key)); + + return part.Metadata.ContainsKey(key); + } + + /// + /// Determines whether the specified part contains a metadata entry with the specified key and value. + /// + /// + /// The part. + /// The key. + /// The value. + /// + /// true the specified part contains a metadata entry with the specified key and value; otherwise, false. + /// + public static bool ContainsPartMetadata(this ComposablePartDefinition part, string key, T value) + { + Requires.NotNull(part, nameof(part)); + Requires.NotNull(key, nameof(key)); + + object untypedValue = null; + if (part.Metadata.TryGetValue(key, out untypedValue)) + { + if (value == null) + { + return (untypedValue == null); + } + else + { + return value.Equals(untypedValue); + } + } + + return false; + } + + /// + /// Filters the specified catalog. + /// + /// The catalog. + /// The filter. + /// + public static FilteredCatalog Filter(this ComposablePartCatalog catalog, Func filter) + { + Requires.NotNull(catalog, nameof(catalog)); + Requires.NotNull(filter, nameof(filter)); + + return new FilteredCatalog(catalog, filter); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/TypeCatalog.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/TypeCatalog.cs new file mode 100644 index 0000000000..dce1781f1d --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/TypeCatalog.cs @@ -0,0 +1,403 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.AttributedModel; +using System.ComponentModel.Composition.Primitives; +using System.ComponentModel.Composition.ReflectionModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading; +using Microsoft.Internal; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition.Hosting +{ + /// + /// An immutable ComposablePartCatalog created from a type array or a list of managed types. This class is threadsafe. + /// It is Disposable. + /// + [DebuggerTypeProxy(typeof(ComposablePartCatalogDebuggerProxy))] + public class TypeCatalog : ComposablePartCatalog, ICompositionElement + { + private readonly object _thisLock = new object(); + private Type[] _types = null; + private volatile List _parts; + private volatile bool _isDisposed = false; + private readonly ICompositionElement _definitionOrigin; + private readonly Lazy>> _contractPartIndex; + + /// + /// Initializes a new instance of the class + /// with the specified types. + /// + /// + /// An of attributed objects to add to the + /// . + /// + /// + /// is . + /// + /// + /// contains an element that is . + /// + /// -or- + /// + /// contains an element that was loaded in the Reflection-only context. + /// + public TypeCatalog(params Type[] types) : this((IEnumerable)types) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified types. + /// + /// + /// An of attributed objects to add + /// to the . + /// + /// + /// is . + /// + /// + /// contains an element that is . + /// + /// -or- + /// + /// contains an element that was loaded in the reflection-only context. + /// + public TypeCatalog(IEnumerable types) + { + Requires.NotNull(types, nameof(types)); + + InitializeTypeCatalog(types); + + _definitionOrigin = this; + _contractPartIndex = new Lazy>>(CreateIndex, true); + } + + /// + /// Initializes a new instance of the class + /// with the specified types. + /// + /// + /// An of attributed objects to add + /// to the . + /// + /// + /// is . + /// + /// + /// The CompositionElement used by Diagnostics to identify the source for parts. + /// + /// + /// contains an element that is . + /// + public TypeCatalog(IEnumerable types, ICompositionElement definitionOrigin) + { + Requires.NotNull(types, nameof(types)); + Requires.NotNull(definitionOrigin, nameof(definitionOrigin)); + + InitializeTypeCatalog(types); + + _definitionOrigin = definitionOrigin; + _contractPartIndex = new Lazy>>(CreateIndex, true); + } + + /// + /// Initializes a new instance of the class + /// with the specified types. + /// + /// + /// An of attributed objects to add + /// to the . + /// + /// + /// is . + /// + /// + /// The a context used by the catalog when + /// interpreting the types to inject attributes into the type definition. + /// + /// + /// contains an element that is . + /// + public TypeCatalog(IEnumerable types, ReflectionContext reflectionContext) + { + Requires.NotNull(types, "types"); + Requires.NotNull(reflectionContext, "reflectionContext"); + + InitializeTypeCatalog(types, reflectionContext); + + _definitionOrigin = this; + _contractPartIndex = new Lazy>>(CreateIndex, true); + } + + /// + /// Initializes a new instance of the class + /// with the specified types. + /// + /// + /// An of attributed objects to add + /// to the . + /// + /// + /// The a context used by the catalog when + /// interpreting the types to inject attributes into the type definition. + /// + /// + /// The CompositionElement used by Diagnostics to identify the source for parts. + /// + /// + /// is . + /// + /// + /// contains an element that is . + /// + public TypeCatalog(IEnumerable types, ReflectionContext reflectionContext, ICompositionElement definitionOrigin) + { + Requires.NotNull(types, "types"); + Requires.NotNull(reflectionContext, "reflectionContext"); + Requires.NotNull(definitionOrigin, "definitionOrigin"); + + InitializeTypeCatalog(types, reflectionContext); + + _definitionOrigin = definitionOrigin; + _contractPartIndex = new Lazy>>(CreateIndex, true); + } + + private void InitializeTypeCatalog(IEnumerable types, ReflectionContext reflectionContext) + { + var typesList = new List(); + foreach (var type in types) + { + if (type == null) + { + throw ExceptionBuilder.CreateContainsNullElement("types"); + } + if (type.Assembly.ReflectionOnly) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.Argument_ElementReflectionOnlyType, "types"), "types"); + } + var typeInfo = type.GetTypeInfo(); + var lclType = (reflectionContext != null) ? reflectionContext.MapType(typeInfo) : typeInfo; + + // It is valid for the reflectionContext to delete types by mapping them to null + if (lclType != null) + { + // The final mapped type may be activated so we check to see if it is in a reflect only assembly + if (lclType.Assembly.ReflectionOnly) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.Argument_ReflectionContextReturnsReflectionOnlyType, "reflectionContext"), "reflectionContext"); + } + typesList.Add(lclType); + } + } + _types = typesList.ToArray(); + } + + private void InitializeTypeCatalog(IEnumerable types) + { + foreach (var type in types) + { + if (type == null) + { + throw ExceptionBuilder.CreateContainsNullElement("types"); + } + else if (type.Assembly.ReflectionOnly) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.Argument_ElementReflectionOnlyType, "types"), "types"); + } + } + _types = types.ToArray(); + } + + public override IEnumerator GetEnumerator() + { + ThrowIfDisposed(); + return PartsInternal.GetEnumerator(); + } + + /// + /// Gets the display name of the type catalog. + /// + /// + /// A containing a human-readable display name of the . + /// + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] + string ICompositionElement.DisplayName + { + get { return GetDisplayName(); } + } + + /// + /// Gets the composition element from which the type catalog originated. + /// + /// + /// This property always returns . + /// + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] + ICompositionElement ICompositionElement.Origin + { + get { return null; } + } + + private IEnumerable PartsInternal + { + get + { + if (_parts == null) + { + lock (_thisLock) + { + if (_parts == null) + { + Assumes.NotNull(_types); + + var collection = new List(); + foreach (Type type in _types) + { + var definition = AttributedModelDiscovery.CreatePartDefinitionIfDiscoverable(type, _definitionOrigin); + if (definition != null) + { + collection.Add(definition); + } + } + Thread.MemoryBarrier(); + + _types = null; + _parts = collection; + } + } + } + + return _parts; + } + } + + internal override IEnumerable GetCandidateParts(ImportDefinition definition) + { + Assumes.NotNull(definition); + + string contractName = definition.ContractName; + if (string.IsNullOrEmpty(contractName)) + { + return PartsInternal; + } + + string genericContractName = definition.Metadata.GetValue(CompositionConstants.GenericContractMetadataName); + + List nonGenericMatches = GetCandidateParts(contractName); + List genericMatches = GetCandidateParts(genericContractName); + + return nonGenericMatches.ConcatAllowingNull(genericMatches); + } + + private List GetCandidateParts(string contractName) + { + if (contractName == null) + { + return null; + } + + List contractCandidateParts = null; + _contractPartIndex.Value.TryGetValue(contractName, out contractCandidateParts); + return contractCandidateParts; + } + + private IDictionary> CreateIndex() + { + Dictionary> index = new Dictionary>(StringComparers.ContractName); + + foreach (var part in PartsInternal) + { + foreach (string contractName in part.ExportDefinitions.Select(export => export.ContractName).Distinct()) + { + List contractParts = null; + if (!index.TryGetValue(contractName, out contractParts)) + { + contractParts = new List(); + index.Add(contractName, contractParts); + } + contractParts.Add(part); + } + } + return index; + } + + /// + /// Returns a string representation of the type catalog. + /// + /// + /// A containing the string representation of the . + /// + public override string ToString() + { + return GetDisplayName(); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _isDisposed = true; + } + + base.Dispose(disposing); + } + + private string GetDisplayName() + { + return String.Format(CultureInfo.CurrentCulture, + SR.TypeCatalog_DisplayNameFormat, + GetType().Name, + GetTypesDisplay()); + } + + private string GetTypesDisplay() + { + int count = PartsInternal.Count(); + if (count == 0) + { + return SR.TypeCatalog_Empty; + } + + const int displayCount = 2; + StringBuilder builder = new StringBuilder(); + foreach (ReflectionComposablePartDefinition definition in PartsInternal.Take(displayCount)) + { + if (builder.Length > 0) + { + builder.Append(CultureInfo.CurrentCulture.TextInfo.ListSeparator); + builder.Append(" "); + } + + builder.Append(definition.GetPartType().GetDisplayName()); + } + + if (count > displayCount) + { // Add an elipse to indicate that there + // are more types than actually listed + builder.Append(CultureInfo.CurrentCulture.TextInfo.ListSeparator); + builder.Append(" ..."); + } + + return builder.ToString(); + } + + [DebuggerStepThrough] + private void ThrowIfDisposed() + { + if (_isDisposed) + { + throw ExceptionBuilder.CreateObjectDisposed(this); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/IAttributedImport.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/IAttributedImport.cs new file mode 100644 index 0000000000..b44c13f542 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/IAttributedImport.cs @@ -0,0 +1,18 @@ +// 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.ComponentModel.Composition.Primitives; + +namespace System.ComponentModel.Composition +{ + internal interface IAttributedImport + { + string ContractName { get; } + Type ContractType { get; } + bool AllowRecomposition { get; } + CreationPolicy RequiredCreationPolicy { get; } + ImportCardinality Cardinality { get; } + ImportSource Source { get; } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ICompositionService.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ICompositionService.cs new file mode 100644 index 0000000000..ed45fbb325 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ICompositionService.cs @@ -0,0 +1,33 @@ +// 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.ComponentModel.Composition.Primitives; + +namespace System.ComponentModel.Composition +{ + /// + /// Provides methods for composing objects. + /// + public interface ICompositionService + { + /// + /// Sets the imports of the specified composable part exactly once and they will not + /// ever be recomposed. + /// + /// + /// The to set the imports. + /// + /// + /// is . + /// + /// + /// An error occurred during composition. will + /// contain a collection of errors that occurred. + /// + /// + /// The has been disposed of. + /// + void SatisfyImportsOnce(ComposablePart part); + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/IPartImportsSatisfiedNotification.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/IPartImportsSatisfiedNotification.cs new file mode 100644 index 0000000000..6bc5ac39e6 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/IPartImportsSatisfiedNotification.cs @@ -0,0 +1,11 @@ +// 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. + +namespace System.ComponentModel.Composition +{ + public interface IPartImportsSatisfiedNotification + { + void OnImportsSatisfied(); + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ImportAttribute.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ImportAttribute.cs new file mode 100644 index 0000000000..5f4a11d660 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ImportAttribute.cs @@ -0,0 +1,196 @@ +// 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.ComponentModel.Composition.Primitives; +using System.Diagnostics.CodeAnalysis; + +namespace System.ComponentModel.Composition +{ + /// + /// Specifies that a property, field, or parameter imports a particular export. + /// + [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes")] + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, + AllowMultiple = false, Inherited = false)] + public class ImportAttribute : Attribute, IAttributedImport + { + /// + /// Initializes a new instance of the class, importing the + /// export with the default contract name. + /// + /// + /// + /// The default contract name is the result of calling + /// on the property, field, + /// or parameter type that this is marked with this attribute. + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + public ImportAttribute() + : this((string)null) + { + } + + /// + /// Initializes a new instance of the class, importing the + /// export with the contract name derived from the specified type. + /// + /// + /// A of which to derive the contract name of the export to import, or + /// to use the default contract name. + /// + /// + /// + /// The contract name is the result of calling + /// on + /// . + /// + /// + /// The default contract name is the result of calling + /// on the property, field, + /// or parameter type that is marked with this attribute. + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + public ImportAttribute(Type contractType) + : this((string)null, contractType) + { + } + + /// + /// Initializes a new instance of the class, importing the + /// export with the specified contract name. + /// + /// + /// A containing the contract name of the export to import, or + /// or an empty string ("") to use the default contract name. + /// + /// + /// + /// The default contract name is the result of calling + /// on the property, field, + /// or parameter type that is marked with this attribute. + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + public ImportAttribute(string contractName) + : this(contractName, (Type)null) + { + } + + public ImportAttribute(string contractName, Type contractType) + { + ContractName = contractName; + ContractType = contractType; + } + + /// + /// Gets the contract name of the export to import. + /// + /// + /// A containing the contract name of the export to import. The + /// default value is an empty string (""). + /// + public string ContractName { get; private set; } + + /// + /// Get the contract type of the export to import. + /// + /// + /// A of the export that this import is expecting. The default value is + /// which means that the type will be obtained by looking at the type on + /// the member that this import is attached to. If the type is then the + /// importer is delaring they can accept any exported type. + /// + public Type ContractType { get; private set; } + + /// + /// Gets or sets a value indicating whether the property, field or parameter will be set + /// to its type's default value when an export with the contract name is not present in + /// the container. + /// + /// + /// if the property, field or parameter will be set + /// its type's default value when an export with the is not + /// present in the ; otherwise, . + /// The default value is . + /// + /// + /// + /// The default value of a property's, field's or parameter's type is + /// for reference types and 0 for numeric value types. For + /// other value types, the default value will be each field of the value type + /// initialized to zero, if the field is a value type or if + /// the field is a reference type. + /// + /// + public bool AllowDefault { get; set; } + + /// + /// Gets or sets a value indicating whether the property or field will be recomposed + /// when exports that provide the same contract that this import expects, have changed + /// in the container. + /// + /// + /// if the property or field allows for recomposition when exports + /// that provide the same are added or removed from the + /// ; otherwise, . + /// The default value is . + /// + public bool AllowRecomposition { get; set; } + + /// + /// Gets or sets a value indicating that the importer requires a specific + /// for the exports used to satisfy this import. T + /// + /// + /// - default value, used if the importer doesn't + /// require a specific . + /// + /// - Requires that all exports used should be shared + /// by everyone in the container. + /// + /// - Requires that all exports used should be + /// non-shared in a container and thus everyone gets their own instance. + /// + public CreationPolicy RequiredCreationPolicy { get; set; } + + /// + /// Gets or sets a value indicating that the importer indicating that the composition engine + /// either should satisfy exports from the local or no local scope. + /// + /// + /// - indicates that importer does not + /// require a specific satisfaction scope"/>. + /// + /// - indicates the importer requires satisfaction to be + /// from the current container. + /// + /// - indicates the importer requires satisfaction to be + /// from one of the ancestor containers. + /// + public ImportSource Source { get; set; } + + ImportCardinality IAttributedImport.Cardinality + { + get + { + if (AllowDefault == true) + { + return ImportCardinality.ZeroOrOne; + } + return ImportCardinality.ExactlyOne; + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ImportCardinalityMismatchException.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ImportCardinalityMismatchException.cs new file mode 100644 index 0000000000..436b1d1cb3 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ImportCardinalityMismatchException.cs @@ -0,0 +1,88 @@ +// 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.Diagnostics; +using System.Runtime.Serialization; + +namespace System.ComponentModel.Composition +{ + /// + /// The exception that is thrown when the cardinality of a + /// does not match the cardinality of the objects available in an + /// . + /// + [DebuggerTypeProxy(typeof(ImportCardinalityMismatchExceptionDebuggerProxy))] + [DebuggerDisplay("{Message}")] + [Serializable] + public class ImportCardinalityMismatchException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public ImportCardinalityMismatchException() + : this((string)null, (Exception)null) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified error message. + /// + /// + /// A containing a message that describes the + /// ; or to set + /// the property to its default value. + /// + public ImportCardinalityMismatchException(string message) + : this(message, (Exception)null) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified error message and exception that is the cause of the + /// exception. + /// + /// + /// A containing a message that describes the + /// ; or to set + /// the property to its default value. + /// + /// + /// The that is the underlying cause of the + /// ; or to set + /// the property to . + /// + public ImportCardinalityMismatchException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified serialization data. + /// + /// + /// The that holds the serialized object data about the + /// . + /// + /// + /// The that contains contextual information about the + /// source or destination. + /// + /// + /// is . + /// + /// + /// is missing a required value. + /// + /// + /// contains a value that cannot be cast to the correct type. + /// + protected ImportCardinalityMismatchException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ImportCardinalityMismatchExceptionDebuggerProxy.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ImportCardinalityMismatchExceptionDebuggerProxy.cs new file mode 100644 index 0000000000..58d051536d --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ImportCardinalityMismatchExceptionDebuggerProxy.cs @@ -0,0 +1,30 @@ +// 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 Microsoft.Internal; + +namespace System.ComponentModel.Composition +{ + internal class ImportCardinalityMismatchExceptionDebuggerProxy + { + private readonly ImportCardinalityMismatchException _exception; + + public ImportCardinalityMismatchExceptionDebuggerProxy(ImportCardinalityMismatchException exception) + { + Requires.NotNull(exception, nameof(exception)); + + _exception = exception; + } + + public Exception InnerException + { + get { return _exception.InnerException; } + } + + public string Message + { + get { return _exception.Message; } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ImportManyAttribute.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ImportManyAttribute.cs new file mode 100644 index 0000000000..c13cc38073 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ImportManyAttribute.cs @@ -0,0 +1,167 @@ +// 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.ComponentModel.Composition.Primitives; +using System.Diagnostics.CodeAnalysis; + +namespace System.ComponentModel.Composition +{ + /// + /// Specifies that a property, field, or parameter imports a particular set of exports. + /// + [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes")] + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, + AllowMultiple = false, Inherited = false)] + public class ImportManyAttribute : Attribute, IAttributedImport + { + /// + /// Initializes a new instance of the class, importing the + /// set of exports with the default contract name. + /// + /// + /// + /// The default contract name is the result of calling + /// on the element\item type of + /// theproperty, field, or parameter type that this is marked with this attribute. + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + public ImportManyAttribute() + : this((string)null) + { + } + + /// + /// Initializes a new instance of the class, importing the + /// set of exports with the contract name derived from the specified type. + /// + /// + /// A of which to derive the contract name of the exports to import, or + /// to use the default contract name. + /// + /// + /// + /// The contract name is the result of calling + /// on + /// . + /// + /// + /// The default contract name is the result of calling + /// on the property, field, + /// or parameter type that is marked with this attribute. + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + public ImportManyAttribute(Type contractType) + : this((string)null, contractType) + { + } + + /// + /// Initializes a new instance of the class, importing the + /// set of exports with the specified contract name. + /// + /// + /// A containing the contract name of the exports to import, or + /// or an empty string ("") to use the default contract name. + /// + /// + /// + /// The default contract name is the result of calling + /// on the property, field, + /// or parameter type that is marked with this attribute. + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + public ImportManyAttribute(string contractName) + : this(contractName, (Type)null) + { + } + + public ImportManyAttribute(string contractName, Type contractType) + { + ContractName = contractName; + ContractType = contractType; + } + + /// + /// Gets the contract name of the exports to import. + /// + /// + /// A containing the contract name of the exports to import. The + /// default value is an empty string (""). + /// + public string ContractName { get; private set; } + + /// + /// Get the contract type of the export to import. + /// + /// + /// A of the export that this import is expecting. The default value is + /// which means that the type will be obtained by looking at the type on + /// the member that this import is attached to. If the type is then the + /// importer is delaring they can accept any exported type. + /// + public Type ContractType { get; private set; } + + /// + /// Gets or sets a value indicating whether the property or field will be recomposed + /// when exports that provide the same contract that this import expects, have changed + /// in the container. + /// + /// + /// if the property or field allows for recomposition when exports + /// that provide the same are added or removed from the + /// ; otherwise, . + /// The default value is . + /// + public bool AllowRecomposition { get; set; } + + /// + /// Gets or sets a value indicating that the importer requires a specific + /// for the exports used to satisfy this import. T + /// + /// + /// - default value, used if the importer doesn't + /// require a specific . + /// + /// - Requires that all exports used should be shared + /// by everyone in the container. + /// + /// - Requires that all exports used should be + /// non-shared in a container and thus everyone gets their own instance. + /// + public CreationPolicy RequiredCreationPolicy { get; set; } + + /// + /// Gets or sets a value indicating that the importer indicating that the composition engine + /// either should satisfy exports from the local or no local scope. + /// + /// + /// - indicates that importer does not + /// require a specific satisfaction scope"/>. + /// + /// - indicates the importer requires satisfaction to be + /// from the current container. + /// + /// - indicates the importer requires satisfaction to be + /// from one of the ancestor containers. + /// + public ImportSource Source { get; set; } + + ImportCardinality IAttributedImport.Cardinality + { + get { return ImportCardinality.ZeroOrMore; } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ImportSource.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ImportSource.cs new file mode 100644 index 0000000000..bc178a8813 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ImportSource.cs @@ -0,0 +1,28 @@ +// 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. + +namespace System.ComponentModel.Composition +{ + /// + /// Option placed on an import to determine how composition searches for exports. + /// + public enum ImportSource : int + { + /// + /// The import can be satisfied with values from the current or parent (or other ancestor) containers (scopes) + /// + Any = 0, + + /// + /// The import can be satisfied with values from the current container (scope) + /// + Local = 1, + + /// + /// The import can only be satisfied with values from the parent container (or other ancestor containers) (scopes) + /// + NonLocal = 2 + + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ImportingConstructorAttribute.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ImportingConstructorAttribute.cs new file mode 100644 index 0000000000..5f6d4214a9 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ImportingConstructorAttribute.cs @@ -0,0 +1,28 @@ +// 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.Diagnostics.CodeAnalysis; + +namespace System.ComponentModel.Composition +{ + /// + /// Specifies that a constructor should be used when constructing an attributed part. + /// + /// + /// By default, only a default parameter-less constructor, if available, is used to + /// construct an attributed part. Use this attribute to indicate that a specific constructor + /// should be used. + /// + [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes")] + [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)] + public class ImportingConstructorAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + public ImportingConstructorAttribute() + { + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/InheritedExportAttribute.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/InheritedExportAttribute.cs new file mode 100644 index 0000000000..057339e397 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/InheritedExportAttribute.cs @@ -0,0 +1,121 @@ +// 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.Diagnostics.CodeAnalysis; + +namespace System.ComponentModel.Composition +{ + /// + /// Specifies that a type or interface that provides a particular export. + /// + [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes")] + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = true)] + public class InheritedExportAttribute : ExportAttribute + { + /// + /// Initializes a new instance of the class, exporting the + /// type marked with this attribute under the default contract name. + /// + /// + /// + /// The default contract name is the result of calling + /// on the type itself, + /// that is marked with this attribute. + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + public InheritedExportAttribute() + : this((string)null, (Type)null) + { + } + + /// + /// Initializes a new instance of the class, exporting the + /// type marked with this attribute under a contract name derived from the specified type. + /// + /// + /// A of which to derive the contract name to export the type + /// marked with this attribute, under; or to use the + /// default contract name. + /// + /// + /// + /// The contract name is the result of calling + /// on + /// . + /// + /// + /// The default contract name is the result of calling + /// on the type of the + /// itself, that is marked with this attribute. + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + public InheritedExportAttribute(Type contractType) + : this((string)null, contractType) + { + } + + /// + /// Initializes a new instance of the class, exporting the + /// type or member marked with this attribute under the specified contract name. + /// + /// + /// A containing the contract name to export the type + /// marked with this attribute, under; or or an empty string + /// ("") to use the default contract name. + /// + /// + /// + /// The default contract name is the result of calling + /// on + /// the type itself that this is marked with this attribute. + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + public InheritedExportAttribute(string contractName) + : this(contractName, (Type)null) + { + } + + /// + /// Initializes a new instance of the class, exporting the + /// type or member marked with this attribute under the specified contract name. + /// + /// + /// A containing the contract name to export the type + /// marked with this attribute, under; or or an empty string + /// ("") to use the default contract name. + /// + /// + /// A of which to derive the contract name to export the type + /// marked with this attribute, under; or to use the + /// default contract name. + /// + /// + /// + /// The default contract name is the result of calling + /// on + /// the type itself that this is marked with this attribute. + /// + /// + /// The contract name is compared using a case-sensitive, non-linguistic comparison + /// using . + /// + /// + public InheritedExportAttribute(string contractName, Type contractType) + : base(contractName, contractType) + { + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/MetadataAttributeAttribute.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/MetadataAttributeAttribute.cs new file mode 100644 index 0000000000..b08f97e98e --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/MetadataAttributeAttribute.cs @@ -0,0 +1,22 @@ +// 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. + +namespace System.ComponentModel.Composition +{ + /// + /// Specifies that an attribute can be used to provide metadata for a type, property, field, + /// or method marked with the . + /// + [AttributeUsage(AttributeTargets.Class, + AllowMultiple=false, Inherited=true)] + public sealed class MetadataAttributeAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + public MetadataAttributeAttribute() + { + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/MetadataServices.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/MetadataServices.cs new file mode 100644 index 0000000000..71b697a12d --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/MetadataServices.cs @@ -0,0 +1,50 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition +{ + internal static class MetadataServices + { + public static readonly IDictionary EmptyMetadata = new ReadOnlyDictionary(new Dictionary(0)); + + public static IDictionary AsReadOnly(this IDictionary metadata) + { + if (metadata == null) + { + return EmptyMetadata; + } + + if (metadata is ReadOnlyDictionary) + { + return metadata; + } + + return new ReadOnlyDictionary(metadata); + } + + public static T GetValue(this IDictionary metadata, string key) + { + Assumes.NotNull(metadata, "metadata"); + + object untypedValue = null; + if (!metadata.TryGetValue(key, out untypedValue)) + { + return default(T); + } + + if (untypedValue is T) + { + return (T)untypedValue; + } + else + { + return default(T); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/MetadataViewGenerator.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/MetadataViewGenerator.cs new file mode 100644 index 0000000000..a300b3c82f --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/MetadataViewGenerator.cs @@ -0,0 +1,373 @@ +// 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.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using System.Reflection.Emit; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition +{ + // // Assume TMetadataView is + // //interface Foo + // //{ + // // public typeRecord1 Record1 { get; } + // // public typeRecord2 Record2 { get; } + // // public typeRecord3 Record3 { get; } + // // public typeRecord4 Record4 { get; } + // //} + // // The class to be generated will look approximately like: + // public class __Foo__MedataViewProxy : TMetadataView + // { + // public static object Create(IDictionary metadata) + // { + // return new __Foo__MedataViewProxy(metadata); + // } + // + // public __Foo__MedataViewProxy (IDictionary metadata) + // { + // if(metadata == null) + // { + // throw InvalidArgumentException("metadata"); + // } + // try + // { + // Record1 = (typeRecord1)Record1; + // Record2 = (typeRecord1)Record2; + // Record3 = (typeRecord1)Record3; + // Record4 = (typeRecord1)Record4; + // } + // catch(InvalidCastException ice) + // { + // //Annotate exception .Data with diagnostic info + // } + // catch(NulLReferenceException ice) + // { + // //Annotate exception .Data with diagnostic info + // } + // } + // // Interface + // public typeRecord1 Record1 { get; } + // public typeRecord2 Record2 { get; } + // public typeRecord3 Record3 { get; } + // public typeRecord4 Record4 { get; } + // } + internal static class MetadataViewGenerator + { + public delegate object MetadataViewFactory(IDictionary metadata); + + public const string MetadataViewType = "MetadataViewType"; + public const string MetadataItemKey = "MetadataItemKey"; + public const string MetadataItemTargetType = "MetadataItemTargetType"; + public const string MetadataItemSourceType = "MetadataItemSourceType"; + public const string MetadataItemValue = "MetadataItemValue"; + public const string MetadataViewFactoryName= "Create"; + + private static Lock _lock = new Lock(); + private static Dictionary _metadataViewFactories = new Dictionary(); + private static AssemblyName ProxyAssemblyName = new AssemblyName(string.Format(CultureInfo.InvariantCulture, "MetadataViewProxies_{0}", Guid.NewGuid())); + private static ModuleBuilder transparentProxyModuleBuilder; + + private static Type[] CtorArgumentTypes = new Type[] { typeof(IDictionary) }; + private static MethodInfo _mdvDictionaryTryGet = CtorArgumentTypes[0].GetMethod("TryGetValue"); + private static readonly MethodInfo ObjectGetType = typeof(object).GetMethod("GetType", Type.EmptyTypes); + private static readonly ConstructorInfo ObjectCtor = typeof(object).GetConstructor(Type.EmptyTypes); + + // Must be called with _lock held + private static ModuleBuilder GetProxyModuleBuilder(bool requiresCritical) + { + if (transparentProxyModuleBuilder == null) + { + // make a new assemblybuilder and modulebuilder + var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(ProxyAssemblyName, AssemblyBuilderAccess.Run); + transparentProxyModuleBuilder = assemblyBuilder.DefineDynamicModule("MetadataViewProxiesModule"); + } + + return transparentProxyModuleBuilder; + } + + public static MetadataViewFactory GetMetadataViewFactory(Type viewType) + { + Assumes.NotNull(viewType); + Assumes.IsTrue(viewType.IsInterface); + + MetadataViewFactory metadataViewFactory; + bool foundMetadataViewFactory; + + using (new ReadLock(_lock)) + { + foundMetadataViewFactory = _metadataViewFactories.TryGetValue(viewType, out metadataViewFactory); + } + + // No factory exists + if(!foundMetadataViewFactory) + { + // Try again under a write lock if still none generate the proxy + Type generatedProxyType = GenerateInterfaceViewProxyType(viewType); + Assumes.NotNull(generatedProxyType); + + MetadataViewFactory generatedMetadataViewFactory = (MetadataViewFactory)Delegate.CreateDelegate( + typeof(MetadataViewFactory), generatedProxyType.GetMethod(MetadataViewGenerator.MetadataViewFactoryName, BindingFlags.Public | BindingFlags.Static)); + Assumes.NotNull(generatedMetadataViewFactory); + + using (new WriteLock(_lock)) + { + if (!_metadataViewFactories.TryGetValue(viewType, out metadataViewFactory)) + { + metadataViewFactory = generatedMetadataViewFactory; + _metadataViewFactories.Add(viewType, metadataViewFactory); + } + } + } + return metadataViewFactory; + } + + public static TMetadataView CreateMetadataView(MetadataViewFactory metadataViewFactory, IDictionary metadata) + { + Assumes.NotNull(metadataViewFactory); + // we are simulating the Activator.CreateInstance behavior by wrapping everything in a TargetInvocationException + try + { + return (TMetadataView)metadataViewFactory.Invoke(metadata); + } + catch(Exception e) + { + throw new TargetInvocationException(e); + } + } + + private static void GenerateLocalAssignmentFromDefaultAttribute(this ILGenerator IL, DefaultValueAttribute[] attrs, LocalBuilder local) + { + if (attrs.Length > 0) + { + DefaultValueAttribute defaultAttribute = attrs[0]; + IL.LoadValue(defaultAttribute.Value); + if ((defaultAttribute.Value != null) && (defaultAttribute.Value.GetType().IsValueType)) + { + IL.Emit(OpCodes.Box, defaultAttribute.Value.GetType()); + } + IL.Emit(OpCodes.Stloc, local); + } + } + + private static void GenerateFieldAssignmentFromLocalValue(this ILGenerator IL, LocalBuilder local, FieldBuilder field) + { + IL.Emit(OpCodes.Ldarg_0); + IL.Emit(OpCodes.Ldloc, local); + IL.Emit(field.FieldType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, field.FieldType); + IL.Emit(OpCodes.Stfld, field); + } + + private static void GenerateLocalAssignmentFromFlag(this ILGenerator IL, LocalBuilder local, bool flag) + { + IL.Emit(flag ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); + IL.Emit(OpCodes.Stloc, local); + } + + // This must be called with _readerWriterLock held for Write + private static Type GenerateInterfaceViewProxyType(Type viewType) + { + // View type is an interface let's cook an implementation + Type proxyType; + TypeBuilder proxyTypeBuilder; + Type[] interfaces = { viewType }; + bool requiresCritical = false; + + var proxyModuleBuilder = GetProxyModuleBuilder(requiresCritical); + proxyTypeBuilder = proxyModuleBuilder.DefineType( + string.Format(CultureInfo.InvariantCulture, "_proxy_{0}_{1}", viewType.FullName, Guid.NewGuid()), + TypeAttributes.Public, + typeof(object), + interfaces); + // Implement Constructor + ConstructorBuilder proxyCtor = proxyTypeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, CtorArgumentTypes); + ILGenerator proxyCtorIL = proxyCtor.GetILGenerator(); + proxyCtorIL.Emit(OpCodes.Ldarg_0); + proxyCtorIL.Emit(OpCodes.Call, ObjectCtor); + + LocalBuilder exception = proxyCtorIL.DeclareLocal(typeof(Exception)); + LocalBuilder exceptionData = proxyCtorIL.DeclareLocal(typeof(IDictionary)); + LocalBuilder sourceType = proxyCtorIL.DeclareLocal(typeof(Type)); + LocalBuilder value = proxyCtorIL.DeclareLocal(typeof(object)); + LocalBuilder usesExportedMD = proxyCtorIL.DeclareLocal(typeof(bool)); + + Label tryConstructView = proxyCtorIL.BeginExceptionBlock(); + + // Implement interface properties + foreach (PropertyInfo propertyInfo in viewType.GetAllProperties()) + { + string fieldName = string.Format(CultureInfo.InvariantCulture, "_{0}_{1}", propertyInfo.Name, Guid.NewGuid()); + + // Cache names and type for exception + string propertyName = string.Format(CultureInfo.InvariantCulture, "{0}", propertyInfo.Name); + + Type[] propertyTypeArguments = new Type[] { propertyInfo.PropertyType }; + Type[] optionalModifiers = null; + Type[] requiredModifiers = null; + + // PropertyInfo does not support GetOptionalCustomModifiers and GetRequiredCustomModifiers on Silverlight + optionalModifiers = propertyInfo.GetOptionalCustomModifiers(); + requiredModifiers = propertyInfo.GetRequiredCustomModifiers(); + Array.Reverse(optionalModifiers); + Array.Reverse(requiredModifiers); + + // Generate field + FieldBuilder proxyFieldBuilder = proxyTypeBuilder.DefineField( + fieldName, + propertyInfo.PropertyType, + FieldAttributes.Private); + + // Generate property + PropertyBuilder proxyPropertyBuilder = proxyTypeBuilder.DefineProperty( + propertyName, + PropertyAttributes.None, + propertyInfo.PropertyType, + propertyTypeArguments); + + // Generate constructor code for retrieving the metadata value and setting the field + Label tryCastValue = proxyCtorIL.BeginExceptionBlock(); + Label innerTryCastValue; + + DefaultValueAttribute[] attrs = propertyInfo.GetAttributes(false); + if(attrs.Length > 0) + { + innerTryCastValue = proxyCtorIL.BeginExceptionBlock(); + } + + // In constructor set the backing field with the value from the dictionary + Label doneGettingDefaultValue = proxyCtorIL.DefineLabel(); + GenerateLocalAssignmentFromFlag(proxyCtorIL, usesExportedMD, true); + + proxyCtorIL.Emit(OpCodes.Ldarg_1); + proxyCtorIL.Emit(OpCodes.Ldstr, propertyInfo.Name); + proxyCtorIL.Emit(OpCodes.Ldloca, value); + proxyCtorIL.Emit(OpCodes.Callvirt, _mdvDictionaryTryGet); + proxyCtorIL.Emit(OpCodes.Brtrue, doneGettingDefaultValue); + + proxyCtorIL.GenerateLocalAssignmentFromFlag(usesExportedMD, false); + proxyCtorIL.GenerateLocalAssignmentFromDefaultAttribute(attrs, value); + + proxyCtorIL.MarkLabel(doneGettingDefaultValue); + proxyCtorIL.GenerateFieldAssignmentFromLocalValue(value, proxyFieldBuilder); + proxyCtorIL.Emit(OpCodes.Leave, tryCastValue); + + // catch blocks for innerTryCastValue start here + if (attrs.Length > 0) + { + proxyCtorIL.BeginCatchBlock(typeof(InvalidCastException)); + { + Label notUsesExportedMd = proxyCtorIL.DefineLabel(); + proxyCtorIL.Emit(OpCodes.Ldloc, usesExportedMD); + proxyCtorIL.Emit(OpCodes.Brtrue, notUsesExportedMd); + proxyCtorIL.Emit(OpCodes.Rethrow); + proxyCtorIL.MarkLabel(notUsesExportedMd); + proxyCtorIL.GenerateLocalAssignmentFromDefaultAttribute(attrs, value); + proxyCtorIL.GenerateFieldAssignmentFromLocalValue(value, proxyFieldBuilder); + } + proxyCtorIL.EndExceptionBlock(); + } + + // catch blocks for tryCast start here + proxyCtorIL.BeginCatchBlock(typeof(NullReferenceException)); + { + proxyCtorIL.Emit(OpCodes.Stloc, exception); + + proxyCtorIL.GetExceptionDataAndStoreInLocal(exception, exceptionData); + proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataItemKey, propertyName); + proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataItemTargetType, propertyInfo.PropertyType); + proxyCtorIL.Emit(OpCodes.Rethrow); + } + + proxyCtorIL.BeginCatchBlock(typeof(InvalidCastException)); + { + proxyCtorIL.Emit(OpCodes.Stloc, exception); + + proxyCtorIL.GetExceptionDataAndStoreInLocal(exception, exceptionData); + proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataItemKey, propertyName); + proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataItemTargetType, propertyInfo.PropertyType); + proxyCtorIL.Emit(OpCodes.Rethrow); + } + + proxyCtorIL.EndExceptionBlock(); + + if (propertyInfo.CanWrite) + { + // The MetadataView '{0}' is invalid because property '{1}' has a property set method. + throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, + SR.InvalidSetterOnMetadataField, + viewType, + propertyName)); + } + if (propertyInfo.CanRead) + { + // Generate "get" method implementation. + MethodBuilder getMethodBuilder = proxyTypeBuilder.DefineMethod( + string.Format(CultureInfo.InvariantCulture, "get_{0}", propertyName), + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final, + CallingConventions.HasThis, + propertyInfo.PropertyType, + requiredModifiers, + optionalModifiers, + Type.EmptyTypes, null, null); + + proxyTypeBuilder.DefineMethodOverride(getMethodBuilder, propertyInfo.GetGetMethod()); + ILGenerator getMethodIL = getMethodBuilder.GetILGenerator(); + getMethodIL.Emit(OpCodes.Ldarg_0); + getMethodIL.Emit(OpCodes.Ldfld, proxyFieldBuilder); + getMethodIL.Emit(OpCodes.Ret); + + proxyPropertyBuilder.SetGetMethod(getMethodBuilder); + } + } + + proxyCtorIL.Emit(OpCodes.Leave, tryConstructView); + + // catch blocks for constructView start here + proxyCtorIL.BeginCatchBlock(typeof(NullReferenceException)); + { + proxyCtorIL.Emit(OpCodes.Stloc, exception); + + proxyCtorIL.GetExceptionDataAndStoreInLocal(exception, exceptionData); + proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataViewType, viewType); + proxyCtorIL.Emit(OpCodes.Rethrow); + } + proxyCtorIL.BeginCatchBlock(typeof(InvalidCastException)); + { + proxyCtorIL.Emit(OpCodes.Stloc, exception); + + proxyCtorIL.GetExceptionDataAndStoreInLocal(exception, exceptionData); + proxyCtorIL.Emit(OpCodes.Ldloc, value); + proxyCtorIL.Emit(OpCodes.Call, ObjectGetType); + proxyCtorIL.Emit(OpCodes.Stloc, sourceType); + proxyCtorIL.AddItemToLocalDictionary(exceptionData, MetadataViewType, viewType); + proxyCtorIL.AddLocalToLocalDictionary(exceptionData, MetadataItemSourceType, sourceType); + proxyCtorIL.AddLocalToLocalDictionary(exceptionData, MetadataItemValue, value); + proxyCtorIL.Emit(OpCodes.Rethrow); + } + proxyCtorIL.EndExceptionBlock(); + + // Finished implementing the constructor + proxyCtorIL.Emit(OpCodes.Ret); + + // Implemet the static factory + // public object Create(IDictionary) + // { + // return new (dictionary); + // } + MethodBuilder factoryMethodBuilder = proxyTypeBuilder.DefineMethod(MetadataViewGenerator.MetadataViewFactoryName, MethodAttributes.Public | MethodAttributes.Static, typeof(object), CtorArgumentTypes); + ILGenerator factoryIL = factoryMethodBuilder.GetILGenerator(); + factoryIL.Emit(OpCodes.Ldarg_0); + factoryIL.Emit(OpCodes.Newobj, proxyCtor); + factoryIL.Emit(OpCodes.Ret); + + // Finished implementing the type + proxyType = proxyTypeBuilder.CreateType(); + + return proxyType; + } + + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/MetadataViewImplementationAttribute.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/MetadataViewImplementationAttribute.cs new file mode 100644 index 0000000000..d425aec841 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/MetadataViewImplementationAttribute.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. +// See the LICENSE file in the project root for more information. + +namespace System.ComponentModel.Composition +{ + /// + /// Specifies that a type, property, field, or method provides a particular export. + /// + [AttributeUsage(AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] + public sealed class MetadataViewImplementationAttribute : Attribute + { + /// + /// Initializes a new instance of the class, declaring the + /// type that holds the implementation for the view. + /// + /// + /// A for the implementation of the MetadataView. + /// + /// + /// + /// By default MetadataViews are generated using reflection emit. This attribute + /// allows the developer to specify the ttype that implements the view rather than + /// using a generated type. + /// + /// + public MetadataViewImplementationAttribute(Type implementationType) + { + ImplementationType = implementationType; + } + + /// + /// Get the type that is used to implement the view to which this attribute is attached. + /// + /// + /// A of the export that is be provided. The default value is + /// which means that the type will be obtained by looking at the type on + /// the member that this export is attached to. + /// + public Type ImplementationType { get; private set; } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/MetadataViewProvider.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/MetadataViewProvider.cs new file mode 100644 index 0000000000..f6cfb74b2f --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/MetadataViewProvider.cs @@ -0,0 +1,139 @@ +// 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.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition +{ + internal static class MetadataViewProvider + { + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + public static TMetadataView GetMetadataView(IDictionary metadata) + { + Assumes.NotNull(metadata); + + Type metadataViewType = typeof(TMetadataView); + + // If the Metadata dictionary is cast compatible with the passed in type + if (metadataViewType.IsAssignableFrom(typeof(IDictionary))) + { + return (TMetadataView)metadata; + } + // otherwise is it a metadata view + else + { + Type proxyType = null; + MetadataViewGenerator.MetadataViewFactory metadataViewFactory = null; + if (metadataViewType.IsInterface) + { + if(!metadataViewType.IsAttributeDefined()) + { + try + { + metadataViewFactory = MetadataViewGenerator.GetMetadataViewFactory(metadataViewType); + } + catch (TypeLoadException ex) + { + throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, SR.NotSupportedInterfaceMetadataView, metadataViewType.FullName), ex); + } + } + else + { + var implementationAttribute = metadataViewType.GetFirstAttribute(); + proxyType = implementationAttribute.ImplementationType; + if(proxyType == null) + { + throw new CompositionContractMismatchException(string.Format(CultureInfo.CurrentCulture, + SR.ContractMismatch_MetadataViewImplementationCanNotBeNull, + metadataViewType.FullName, + proxyType.FullName)); + } + else + { + if(!metadataViewType.IsAssignableFrom(proxyType)) + { + throw new CompositionContractMismatchException(string.Format(CultureInfo.CurrentCulture, + SR.ContractMismatch_MetadataViewImplementationDoesNotImplementViewInterface, + metadataViewType.FullName, + proxyType.FullName)); + } + } + } + } + else + { + proxyType = metadataViewType; + } + + // Now we have the type for the proxy create it + try + { + if (metadataViewFactory != null) + { + return MetadataViewGenerator.CreateMetadataView(metadataViewFactory, metadata); + } + else + { + Assumes.NotNull(proxyType); + return (TMetadataView)proxyType.SafeCreateInstance(metadata); + } + } + catch (MissingMethodException ex) + { + // Unable to create an Instance of the Metadata view '{0}' because a constructor could not be selected. Ensure that the type implements a constructor which takes an argument of type IDictionary. + throw new CompositionContractMismatchException(string.Format(CultureInfo.CurrentCulture, + SR.CompositionException_MetadataViewInvalidConstructor, + proxyType.AssemblyQualifiedName), ex); + } + catch (TargetInvocationException ex) + { + //Unwrap known failures that we want to present as CompositionContractMismatchException + if(metadataViewType.IsInterface) + { + if(ex.InnerException.GetType() == typeof(InvalidCastException)) + { + // Unable to create an Instance of the Metadata view {0} because the exporter exported the metadata for the item {1} with the value {2} as type {3} but the view imports it as type {4}. + throw new CompositionContractMismatchException(string.Format(CultureInfo.CurrentCulture, + SR.ContractMismatch_InvalidCastOnMetadataField, + ex.InnerException.Data[MetadataViewGenerator.MetadataViewType], + ex.InnerException.Data[MetadataViewGenerator.MetadataItemKey], + ex.InnerException.Data[MetadataViewGenerator.MetadataItemValue], + ex.InnerException.Data[MetadataViewGenerator.MetadataItemSourceType], + ex.InnerException.Data[MetadataViewGenerator.MetadataItemTargetType]), ex); + } + else if (ex.InnerException.GetType() == typeof(NullReferenceException)) + { + // Unable to create an Instance of the Metadata view {0} because the exporter exported the metadata for the item {1} with a null value and null is not a valid value for type {2}. + throw new CompositionContractMismatchException(string.Format(CultureInfo.CurrentCulture, + SR.ContractMismatch_NullReferenceOnMetadataField, + ex.InnerException.Data[MetadataViewGenerator.MetadataViewType], + ex.InnerException.Data[MetadataViewGenerator.MetadataItemKey], + ex.InnerException.Data[MetadataViewGenerator.MetadataItemTargetType]), ex); + } + } + throw; + } + } + } + + public static bool IsViewTypeValid(Type metadataViewType) + { + Assumes.NotNull(metadataViewType); + + // If the Metadata dictionary is cast compatible with the passed in type + if (ExportServices.IsDefaultMetadataViewType(metadataViewType) + || metadataViewType.IsInterface + || ExportServices.IsDictionaryConstructorViewType(metadataViewType)) + { + return true; + } + + return false; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/PartCreationPolicyAttribute.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/PartCreationPolicyAttribute.cs new file mode 100644 index 0000000000..b4996edbbb --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/PartCreationPolicyAttribute.cs @@ -0,0 +1,34 @@ +// 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. + +namespace System.ComponentModel.Composition +{ + /// + /// Specifies for a given . + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public sealed class PartCreationPolicyAttribute : Attribute + { + internal static PartCreationPolicyAttribute Default = new PartCreationPolicyAttribute(CreationPolicy.Any); + internal static PartCreationPolicyAttribute Shared = new PartCreationPolicyAttribute(CreationPolicy.Shared); + + /// + /// Initializes a new instance of the class. + /// + public PartCreationPolicyAttribute(CreationPolicy creationPolicy) + { + CreationPolicy = creationPolicy; + } + + /// + /// Gets or sets a value indicating the creation policy of the attributed part. + /// + /// + /// One of the values indicating the creation policy of the + /// attributed part. The default is + /// . + /// + public CreationPolicy CreationPolicy { get; private set; } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/PartMetadataAttribute.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/PartMetadataAttribute.cs new file mode 100644 index 0000000000..78f178d982 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/PartMetadataAttribute.cs @@ -0,0 +1,56 @@ +// 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. + +namespace System.ComponentModel.Composition +{ + /// + /// Specifies metadata for a type to be used as a and + /// . + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] + public sealed class PartMetadataAttribute : Attribute + { + /// + /// Initializes a new instance of the with the + /// specified name and metadata value. + /// + /// + /// A containing the name of the metadata value; or + /// to use an empty string (""). + /// + /// + /// An containing the metadata value. This can be + /// . + /// + public PartMetadataAttribute(string name, object value) + { + Name = name ?? string.Empty; + Value = value; + } + + /// + /// Gets the name of the metadata value. + /// + /// + /// A containing the name of the metadata value. + /// + public string Name + { + get; + private set; + } + + /// + /// Gets the metadata value. + /// + /// + /// An containing the metadata value. + /// + public object Value + { + get; + private set; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/PartNotDiscoverableAttribute.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/PartNotDiscoverableAttribute.cs new file mode 100644 index 0000000000..6ac11a917d --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/PartNotDiscoverableAttribute.cs @@ -0,0 +1,21 @@ +// 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. + +namespace System.ComponentModel.Composition +{ + /// + /// Place on a type that should not be discovered as a in + /// a . + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public sealed class PartNotDiscoverableAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + public PartNotDiscoverableAttribute() + { + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePart.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePart.cs new file mode 100644 index 0000000000..f30ccdca45 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePart.cs @@ -0,0 +1,209 @@ +// 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.Collections.Generic; + +namespace System.ComponentModel.Composition.Primitives +{ + /// + /// Defines the base class for composable parts, which + /// import and produce exported values. + /// + public abstract class ComposablePart + { + /// + /// Initializes a new instance of the class. + /// + protected ComposablePart() + { + } + + /// + /// Gets the export definitions that describe the exported values provided by the part. + /// + /// + /// An of objects describing + /// the exported values provided by the . + /// + /// + /// The has been disposed of. + /// + /// + /// + /// + /// If the was created from a + /// , this property should return the result of + /// . + /// + /// + /// + /// + /// Overriders of this property should never return . + /// If the does not have exports, return an empty + /// instead. + /// + /// + /// + public abstract IEnumerable ExportDefinitions { get; } + + /// + /// Gets the import definitions that describe the imports required by the part. + /// + /// + /// An of objects describing + /// the imports required by the . + /// + /// + /// The has been disposed of. + /// + /// + /// + /// + /// If the was created from a + /// , this property should return the result of + /// . + /// + /// + /// + /// + /// Overrides of this property should never return . + /// If the does not have imports, return an empty + /// instead. + /// + /// + /// + public abstract IEnumerable ImportDefinitions { get; } + + /// + /// Gets the metadata of the part. + /// + /// + /// An containing the metadata of the + /// . The default is an empty, read-only + /// . + /// + /// + /// The has been disposed of. + /// + /// + /// + /// + /// If the was created from a + /// , this property should return the result of + /// . + /// + /// + /// + /// + /// Overriders of this property should return a read-only + /// object with a case-sensitive, + /// non-linguistic comparer, such as , + /// and should never return . If the + /// does not contain metadata, return an + /// empty instead. + /// + /// + /// + public virtual IDictionary Metadata + { + get + { + return MetadataServices.EmptyMetadata; + } + } + + /// + /// Called by the composition engine when all required imports on the part have been + /// satisfied. + /// + /// + /// The has been disposed of. + /// + /// + /// An error occurred activating the . + /// + public virtual void Activate() + { + } + + /// + /// Gets the exported value described by the specified definition. + /// + /// + /// One of the objects from the + /// property describing the exported value + /// to return. + /// + /// + /// The exported value described by . + /// + /// + /// is . + /// + /// + /// did not originate from the + /// property on the . + /// + /// + /// One or more pre-requisite imports, indicated by , + /// have not been set. + /// + /// + /// The has been disposed of. + /// + /// + /// An error occurred getting the exported value described by the . + /// + public abstract object GetExportedValue(ExportDefinition definition); + + /// + /// Sets the import described by the specified definition with the specified exports. + /// + /// + /// One of the objects from the + /// property describing the import to be set. + /// + /// + /// An of objects of which + /// to set the import described by . + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// + /// did not originate from the + /// property on the . + /// + /// -or- + /// + /// contains an element that is . + /// + /// -or- + /// + /// is empty and is + /// . + /// + /// -or- + /// + /// contains more than one element and + /// is or + /// . + /// + /// + /// has been previously called and + /// is . + /// + /// + /// The has been disposed of. + /// + /// + /// An error occurred setting the import described by the . + /// + public abstract void SetImport(ImportDefinition definition, IEnumerable exports); + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartCatalog.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartCatalog.cs new file mode 100644 index 0000000000..1d1b532173 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartCatalog.cs @@ -0,0 +1,184 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; +using System.Linq; +using System.Threading; +using Microsoft.Internal; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition.Primitives +{ + /// + /// Defines the base class for composable part catalogs, which produce + /// and return objects. + /// + /// + /// This type is thread safe. + /// + [DebuggerTypeProxy(typeof(ComposablePartCatalogDebuggerProxy))] + public abstract class ComposablePartCatalog : IEnumerable, IDisposable + { + private bool _isDisposed; + private volatile IQueryable _queryableParts = null; + + internal static readonly List> _EmptyExportsList = new List>(); + + /// + /// Initializes a new instance of the class. + /// + protected ComposablePartCatalog() + { + } + + /// + /// Gets the part definitions of the catalog. + /// + /// + /// A of objects of the + /// . + /// + /// + /// The has been disposed of. + /// + /// + /// + /// Overriders of this property should never return . + /// + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + public virtual IQueryable Parts + { + get + { + ThrowIfDisposed(); + if(_queryableParts == null) + { + // Guarantee one time only set _queryableParts + var p = this.AsQueryable(); + // NOTE : According to http://msdn.microsoft.com/en-us/library/4bw5ewxy.aspx, the warning is bogus when used with Interlocked API. +#pragma warning disable 420 + Interlocked.CompareExchange(ref _queryableParts, p, null); +#pragma warning restore 420 + Assumes.NotNull(_queryableParts); + } + return _queryableParts; + } + } + + /// + /// Returns the export definitions that match the constraint defined by the specified definition. + /// + /// + /// The that defines the conditions of the + /// objects to return. + /// + /// + /// An of containing the + /// objects and their associated + /// for objects that match the constraint defined + /// by . + /// + /// + /// is . + /// + /// + /// The has been disposed of. + /// + /// + /// + /// Overriders of this property should never return , if no + /// match the conditions defined by + /// , return an empty . + /// + /// + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public virtual IEnumerable> GetExports(ImportDefinition definition) + { + ThrowIfDisposed(); + + Requires.NotNull(definition, nameof(definition)); + Contract.Ensures(Contract.Result>>() != null); + + List> exports = null; + var candidateParts = GetCandidateParts(definition); + if (candidateParts != null) + { + foreach (var part in candidateParts) + { + Tuple singleMatch; + IEnumerable> multipleMatches; + + if (part.TryGetExports(definition, out singleMatch, out multipleMatches)) + { + exports = exports.FastAppendToListAllowNulls(singleMatch, multipleMatches); + } + } + } + + return exports ?? _EmptyExportsList; + } + +internal virtual IEnumerable GetCandidateParts(ImportDefinition definition) + { + return this; + } + +/// + /// Releases the unmanaged resources used by the and + /// optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + _isDisposed = true; + } + + [DebuggerStepThrough] + private void ThrowIfDisposed() + { + if (_isDisposed) + { + throw ExceptionBuilder.CreateObjectDisposed(this); + } + } + + // + // If neither Parts nor GetEnumerator() is overriden then return an empty list + // If GetEnumerator is overridden this code should not be invoked: ReferenceAssemblies mark it as Abstract or Not present + // We verify whether Parts is overriden by seeing if the object returns matched the one cached for this instance + // Note: a query object is only cached if Parts is invoked on a catalog which did not implement it + // Because reference assemblies do not expose Parts and we no longer use it, it should not get invoked by 3rd parties + // Because the reference assemblies mark GetEnumerator as Abstract 3rd party code should not lack an implementation + // That implementation should not try to call this implementation + // Our code doies delegate to Parts in the DebuggerProxies of course. + // + public virtual IEnumerator GetEnumerator() + { + var parts = Parts; + if(object.ReferenceEquals(parts, _queryableParts)) + { + return Enumerable.Empty().GetEnumerator(); + } + return parts.GetEnumerator(); + } + + Collections.IEnumerator Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartCatalogDebuggerProxy.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartCatalogDebuggerProxy.cs new file mode 100644 index 0000000000..f733504ee3 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartCatalogDebuggerProxy.cs @@ -0,0 +1,33 @@ +// 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.Collections.ObjectModel; +using Microsoft.Internal; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition.Primitives +{ + // This proxy is needed to pretty up ComposablePartCatalog.Parts; IQueryable + // instances are not displayed in a very friendly way in the debugger. + internal class ComposablePartCatalogDebuggerProxy + { + private readonly ComposablePartCatalog _catalog; + + public ComposablePartCatalogDebuggerProxy(ComposablePartCatalog catalog) + { + Requires.NotNull(catalog, nameof(catalog)); + + _catalog = catalog; + } + + public ReadOnlyCollection Parts + { + // NOTE: This shouldn't be cached, so that on every query of + // the current value of the underlying catalog is respected. + // We use ReadOnlyCollection as arrays do not have the + // appropriate debugger display attributes applied to them. + get { return _catalog.Parts.ToReadOnlyCollection(); } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartDefinition.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartDefinition.cs new file mode 100644 index 0000000000..76812113de --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartDefinition.cs @@ -0,0 +1,154 @@ +// 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.Collections.Generic; +using System.Linq; + +namespace System.ComponentModel.Composition.Primitives +{ + /// + /// Defines the base class for composable part definitions, which + /// describe, and allow the creation of, objects. + /// + public abstract class ComposablePartDefinition + { + static internal readonly IEnumerable> _EmptyExports = Enumerable.Empty>(); + /// + /// Initializes a new instance of the class. + /// + protected ComposablePartDefinition() + { + } + + /// + /// Gets the export definitions that describe the exported values provided by parts + /// created by the definition. + /// + /// + /// An of objects describing + /// the exported values provided by objects created by the + /// . + /// + /// + /// + /// Overrides of this property should never return . + /// If the objects created by the + /// do not provide exported values, return + /// an empty instead. + /// + /// + public abstract IEnumerable ExportDefinitions { get; } + + /// + /// Gets the import definitions that describe the imports required by parts created + /// by the definition. + /// + /// + /// An of objects describing + /// the imports required by objects created by the + /// . + /// + /// + /// + /// Overriders of this property should never return . + /// If the objects created by the + /// do not have imports, return an empty + /// instead. + /// + /// + public abstract IEnumerable ImportDefinitions { get; } + + /// + /// Gets the metadata of the definition. + /// + /// + /// An containing the metadata of the + /// . The default is an empty, read-only + /// . + /// + /// + /// + /// + /// Overriders of this property should return a read-only + /// object with a case-sensitive, + /// non-linguistic comparer, such as , + /// and should never return . If the + /// does contain metadata, + /// return an empty instead. + /// + /// + /// + public virtual IDictionary Metadata + { + get { return MetadataServices.EmptyMetadata; } + } + + /// + /// Creates a new instance of a part that the definition describes. + /// + /// + /// The created . + /// + /// + /// + /// + /// Derived types overriding this method should return a new instance of a + /// on every invoke and should never return + /// . + /// + /// + /// + public abstract ComposablePart CreatePart(); + + internal virtual bool TryGetExports(ImportDefinition definition, out Tuple singleMatch, out IEnumerable> multipleMatches) + { + singleMatch = null; + multipleMatches = null; + + List> multipleExports = null; + Tuple singleExport = null; + bool matchesFound = false; + foreach (var export in ExportDefinitions) + { + if (definition.IsConstraintSatisfiedBy(export)) + { + matchesFound = true; + if (singleExport == null) + { + singleExport = new Tuple(this, export); + } + else + { + if (multipleExports == null) + { + multipleExports = new List>(); + multipleExports.Add(singleExport); + } + multipleExports.Add(new Tuple(this, export)); + } + } + } + + if (!matchesFound) + { + return false; + } + + if (multipleExports != null) + { + multipleMatches = multipleExports; + } + else + { + singleMatch = singleExport; + } + return true; + } + + internal virtual ComposablePartDefinition GetGenericPartDefinition() + { + return null; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartException.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartException.cs new file mode 100644 index 0000000000..f6c2a49ff7 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartException.cs @@ -0,0 +1,122 @@ +// 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.Diagnostics; +using System.Runtime.Serialization; + +namespace System.ComponentModel.Composition.Primitives +{ + /// + /// The exception that is thrown when an error occurs when calling methods on a + /// . + /// + [DebuggerTypeProxy(typeof(ComposablePartExceptionDebuggerProxy))] + [DebuggerDisplay("{Message}")] + public class ComposablePartException : Exception + { + private readonly ICompositionElement _element; + + /// + /// Initializes a new instance of the class. + /// + public ComposablePartException() + : this((string)null, (ICompositionElement)null, (Exception)null) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified error message. + /// + /// + /// A containing a message that describes the + /// ; or to set + /// the property to its default value. + /// + /// + /// The that is the cause of the + /// ; or to set + /// the property to + /// . + /// + public ComposablePartException(string message) + : this(message, (ICompositionElement)null, (Exception)null) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified error message and composition element that is the cause of + /// the exception. + /// + /// + /// A containing a message that describes the + /// ; or to set + /// the property to its default value. + /// + public ComposablePartException(string message, ICompositionElement element) + : this(message, element, (Exception)null) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified error message and exception that is the cause of the + /// exception. + /// + /// + /// A containing a message that describes the + /// ; or to set + /// the property to its default value. + /// + /// + /// The that is the underlying cause of the + /// ; or to set + /// the property to . + /// + public ComposablePartException(string message, Exception innerException) + : this(message, (ICompositionElement)null, innerException) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified error message, and composition element and exception that + /// are the cause of the exception. + /// + /// + /// A containing a message that describes the + /// ; or to set + /// the property to its default value. + /// + /// + /// The that is the cause of the + /// ; or to set + /// the property to + /// . + /// + /// + /// The that is the underlying cause of the + /// ; or to set + /// the property to . + /// + public ComposablePartException(string message, ICompositionElement element, Exception innerException) + : base(message, innerException) + { + _element = element; + } + + /// + /// Gets the composition element that is the cause of the exception. + /// + /// + /// The that is the cause of the + /// . The default is . + /// + public ICompositionElement Element + { + get { return _element; } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartExceptionDebuggerProxy.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartExceptionDebuggerProxy.cs new file mode 100644 index 0000000000..ba1e842133 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartExceptionDebuggerProxy.cs @@ -0,0 +1,35 @@ +// 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 Microsoft.Internal; + +namespace System.ComponentModel.Composition.Primitives +{ + internal class ComposablePartExceptionDebuggerProxy + { + private readonly ComposablePartException _exception; + + public ComposablePartExceptionDebuggerProxy(ComposablePartException exception) + { + Requires.NotNull(exception, nameof(exception)); + + _exception = exception; + } + + public ICompositionElement Element + { + get { return _exception.Element; } + } + + public Exception InnerException + { + get { return _exception.InnerException; } + } + + public string Message + { + get { return _exception.Message; } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/CompositionElement.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/CompositionElement.cs new file mode 100644 index 0000000000..1e76dab043 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/CompositionElement.cs @@ -0,0 +1,51 @@ +// 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.Diagnostics; + +namespace System.ComponentModel.Composition.Primitives +{ + // Represents the ICompositionElement placeholder for an + // object that does not implement ICompositionElement + [DebuggerTypeProxy(typeof(CompositionElementDebuggerProxy))] + internal class CompositionElement : ICompositionElement + { + private readonly string _displayName; + private readonly ICompositionElement _origin; + private readonly object _underlyingObject; + private static readonly ICompositionElement UnknownOrigin = new CompositionElement(SR.CompositionElement_UnknownOrigin, (ICompositionElement)null); + + public CompositionElement(object underlyingObject) + : this(underlyingObject.ToString(), UnknownOrigin) + { + _underlyingObject = underlyingObject; + } + + public CompositionElement(string displayName, ICompositionElement origin) + { + _displayName = displayName ?? string.Empty; + _origin = origin; + } + + public string DisplayName + { + get { return _displayName; } + } + + public ICompositionElement Origin + { + get { return _origin; } + } + + public override string ToString() + { + return DisplayName; + } + + public object UnderlyingObject + { + get { return _underlyingObject; } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/CompositionElementDebuggerProxy.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/CompositionElementDebuggerProxy.cs new file mode 100644 index 0000000000..241066f21c --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/CompositionElementDebuggerProxy.cs @@ -0,0 +1,39 @@ +// 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 Microsoft.Internal; + +namespace System.ComponentModel.Composition.Primitives +{ + // Because the debugger displays only the members available on ICompositionElement + // when viewing CompositionError.Element in the watch and data tips windows, we + // need this proxy so that the underlying object wrapped by the CompositionElement + // placeholder is displayed by default. + internal class CompositionElementDebuggerProxy + { + private readonly CompositionElement _element; + + public CompositionElementDebuggerProxy(CompositionElement element) + { + Requires.NotNull(element, nameof(element)); + + _element = element; + } + + public string DisplayName + { + get { return _element.DisplayName; } + } + + public ICompositionElement Origin + { + get { return _element.Origin; } + } + + public object UnderlyingObject + { + get { return _element.UnderlyingObject; } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/CompositionElementExtensions.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/CompositionElementExtensions.cs new file mode 100644 index 0000000000..9891d496ff --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/CompositionElementExtensions.cs @@ -0,0 +1,74 @@ +// 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. + +namespace System.ComponentModel.Composition.Primitives +{ + internal static class CompositionElementExtensions + { + public static ICompositionElement ToElement(this Export export) + { + // First try the export + ICompositionElement element = export as ICompositionElement; + if (element != null) + { + return element; + } + + // Otherwise, try the definition + return ToElement(export.Definition); + } + + public static ICompositionElement ToElement(this ExportDefinition definition) + { + return ToElementCore(definition); + } + + public static ICompositionElement ToElement(this ImportDefinition definition) + { + return ToElementCore(definition); + } + + public static ICompositionElement ToElement(this ComposablePart part) + { + return ToElementCore(part); + } + + public static ICompositionElement ToElement(this ComposablePartDefinition definition) + { + return ToElementCore(definition); + } + + public static string GetDisplayName(this ComposablePartDefinition definition) + { + return GetDisplayNameCore(definition); + } + + public static string GetDisplayName(this ComposablePartCatalog catalog) + { + return GetDisplayNameCore(catalog); + } + + private static string GetDisplayNameCore(object value) + { + ICompositionElement element = value as ICompositionElement; + if (element != null) + { + return element.DisplayName; + } + + return value.ToString(); + } + + private static ICompositionElement ToElementCore(object value) + { + ICompositionElement element = value as ICompositionElement; + if (element != null) + { + return element; + } + + return new CompositionElement(value); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ContractBasedImportDefinition.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ContractBasedImportDefinition.cs new file mode 100644 index 0000000000..7352d47bae --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ContractBasedImportDefinition.cs @@ -0,0 +1,397 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Hosting; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Primitives +{ + /// + /// Represents a contract name and metadata-based import + /// required by a object. + /// + public class ContractBasedImportDefinition : ImportDefinition + { + // Unlike contract name, both metadata and required metadata has a sensible default; set it to an empty + // enumerable, so that derived definitions only need to override ContractName by default. + private readonly IEnumerable> _requiredMetadata = Enumerable.Empty>(); + private Expression> _constraint; + private readonly CreationPolicy _requiredCreationPolicy = CreationPolicy.Any; + private readonly string _requiredTypeIdentity = null; + private bool _isRequiredMetadataValidated = false; + + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// Derived types calling this constructor can optionally override the + /// , , + /// , , + /// , + /// and properties. + /// + /// + protected ContractBasedImportDefinition() + { + } + + /// + /// Initializes a new instance of the class + /// with the specified contract name, required metadataq, cardinality, value indicating + /// if the import definition is recomposable and a value indicating if the import definition + /// is a prerequisite. + /// + /// + /// A containing the contract name of the + /// required by the . + /// + /// + /// The type identity of the export type expected. Use + /// to generate a type identity for a given type. If no specific type is required pass . + /// + /// + /// An of objects containing + /// the metadata names of the required by the + /// ; or to + /// set the property to an empty . + /// + /// + /// One of the values indicating the + /// cardinality of the objects required by the + /// . + /// + /// + /// if the can be satisfied + /// multiple times throughout the lifetime of a , otherwise, + /// . + /// + /// + /// if the is required to be + /// satisfied before a can start producing exported + /// objects; otherwise, . + /// + /// + /// A value indicating that the importer requires a specific for + /// the exports used to satisfy this import. If no specific is needed + /// pass the default . + /// + /// + /// is . + /// + /// + /// is an empty string (""). + /// + /// -or- + /// + /// contains an element that is . + /// + /// -or- + /// + /// is not one of the + /// values. + /// + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public ContractBasedImportDefinition(string contractName, string requiredTypeIdentity, IEnumerable> requiredMetadata, + ImportCardinality cardinality, bool isRecomposable, bool isPrerequisite, CreationPolicy requiredCreationPolicy) + : this(contractName, requiredTypeIdentity, requiredMetadata, cardinality, isRecomposable, isPrerequisite, requiredCreationPolicy, MetadataServices.EmptyMetadata) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified contract name, required metadataq, cardinality, value indicating + /// if the import definition is recomposable and a value indicating if the import definition + /// is a prerequisite. + /// + /// + /// A containing the contract name of the + /// required by the . + /// + /// + /// The type identity of the export type expected. Use + /// to generate a type identity for a given type. If no specific type is required pass . + /// + /// + /// An of objects containing + /// the metadata names of the required by the + /// ; or to + /// set the property to an empty . + /// + /// + /// One of the values indicating the + /// cardinality of the objects required by the + /// . + /// + /// + /// if the can be satisfied + /// multiple times throughout the lifetime of a , otherwise, + /// . + /// + /// + /// if the is required to be + /// satisfied before a can start producing exported + /// objects; otherwise, . + /// + /// + /// A value indicating that the importer requires a specific for + /// the exports used to satisfy this import. If no specific is needed + /// pass the default . + /// + /// + /// is . + /// + /// + /// is an empty string (""). + /// + /// -or- + /// + /// contains an element that is . + /// + /// -or- + /// + /// is not one of the + /// values. + /// + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public ContractBasedImportDefinition(string contractName, string requiredTypeIdentity, IEnumerable> requiredMetadata, + ImportCardinality cardinality, bool isRecomposable, bool isPrerequisite, CreationPolicy requiredCreationPolicy, IDictionary metadata) + : base(contractName, cardinality, isRecomposable, isPrerequisite, metadata) + { + Requires.NotNullOrEmpty(contractName, "contractName"); + + _requiredTypeIdentity = requiredTypeIdentity; + + if (requiredMetadata != null) + { + _requiredMetadata = requiredMetadata; + } + + _requiredCreationPolicy = requiredCreationPolicy; + } + + /// + /// The type identity of the export type expected. + /// + /// + /// A that is generated by + /// on the type that this import expects. If the value is then this import + /// doesn't expect a particular type. + /// + public virtual string RequiredTypeIdentity + { + get { return _requiredTypeIdentity; } + } + + /// + /// Gets the metadata names of the export required by the import definition. + /// + /// + /// An of pairs of metadata keys and types of the required by the + /// . The default is an empty + /// . + /// + /// + /// + /// Overriders of this property should never return + /// or return an that contains an element that is + /// . If the definition does not contain required metadata, + /// return an empty instead. + /// + /// + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public virtual IEnumerable> RequiredMetadata + { + get + { + Contract.Ensures(Contract.Result>>() != null); + + // NOTE : unlike other arguments, we validate this one as late as possible, because its validation may lead to type loading + ValidateRequiredMetadata(); + + return _requiredMetadata; + } + } + + private void ValidateRequiredMetadata() + { + if (!_isRequiredMetadataValidated) + { + foreach (KeyValuePair metadataItem in _requiredMetadata) + { + if ((metadataItem.Key == null) || (metadataItem.Value == null)) + { + throw new InvalidOperationException( + string.Format(CultureInfo.CurrentCulture, SR.Argument_NullElement, "requiredMetadata")); + } + } + _isRequiredMetadataValidated = true; + } + } + + /// + /// Gets or sets a value indicating that the importer requires a specific + /// for the exports used to satisfy this import. T + /// + /// + /// - default value, used if the importer doesn't + /// require a specific . + /// + /// - Requires that all exports used should be shared + /// by everyone in the container. + /// + /// - Requires that all exports used should be + /// non-shared in a container and thus everyone gets their own instance. + /// + public virtual CreationPolicy RequiredCreationPolicy + { + get { return _requiredCreationPolicy; } + } + + /// + /// Gets an expression that defines conditions that must be matched for the import + /// described by the import definition to be satisfied. + /// + /// + /// A containing a + /// that defines the conditions that must be matched for the + /// to be satisfied by an . + /// + /// + /// + /// This property returns an expression that defines conditions based on the + /// , , + /// , and + /// properties. + /// + /// + public override Expression> Constraint + { + get + { + if (_constraint == null) + { + _constraint = ConstraintServices.CreateConstraint(ContractName, RequiredTypeIdentity, RequiredMetadata, RequiredCreationPolicy); + } + + return _constraint; + } + } + + /// + /// Executes an optimized version of the contraint given by the property + /// + /// + /// A definition for a used to determine if it satisfies the + /// requirements for this . + /// + /// + /// if the satisfies the requirements for + /// this , otherwise returns . + /// + /// + /// + /// Overrides of this method can provide a more optimized execution of the + /// property but the result should remain consistent. + /// + /// + /// + /// is . + /// + public override bool IsConstraintSatisfiedBy(ExportDefinition exportDefinition) + { + Requires.NotNull(exportDefinition, nameof(exportDefinition)); + + if (!StringComparers.ContractName.Equals(ContractName, exportDefinition.ContractName)) + { + return false; + } + + return MatchRequiredMetadata(exportDefinition); + } + + private bool MatchRequiredMetadata(ExportDefinition definition) + { + if (!string.IsNullOrEmpty(RequiredTypeIdentity)) + { + string exportTypeIdentity = definition.Metadata.GetValue(CompositionConstants.ExportTypeIdentityMetadataName); + + if (!StringComparers.ContractName.Equals(RequiredTypeIdentity, exportTypeIdentity)) + { + return false; + } + } + + foreach (KeyValuePair metadataItem in RequiredMetadata) + { + string metadataKey = metadataItem.Key; + Type metadataValueType = metadataItem.Value; + + object metadataValue = null; + if (!definition.Metadata.TryGetValue(metadataKey, out metadataValue)) + { + return false; + } + + if (metadataValue != null) + { + // the metadata value is not null, we can rely on IsInstanceOfType to do the right thing + if (!metadataValueType.IsInstanceOfType(metadataValue)) + { + return false; + } + } + else + { + // this is an unfortunate special case - typeof(object).IsInstanceofType(null) == false + // basically nulls are not considered valid values for anything + // We want them to match anything that is a reference type + if (metadataValueType.IsValueType) + { + // this is a pretty expensive check, but we only invoke it when metadata values are null, which is very rare + return false; + } + } + } + + if (RequiredCreationPolicy == CreationPolicy.Any) + { + return true; + } + + CreationPolicy exportPolicy = definition.Metadata.GetValue(CompositionConstants.PartCreationPolicyMetadataName); + return exportPolicy == CreationPolicy.Any || + exportPolicy == RequiredCreationPolicy; + } + + public override string ToString() + { + var sb = new StringBuilder(); + + sb.Append(string.Format("\n\tContractName\t{0}", ContractName)); + sb.Append(string.Format("\n\tRequiredTypeIdentity\t{0}", RequiredTypeIdentity)); + if(_requiredCreationPolicy != CreationPolicy.Any) + { + sb.Append(string.Format("\n\tRequiredCreationPolicy\t{0}", RequiredCreationPolicy)); + } + + if(_requiredMetadata.Count() > 0) + { + sb.Append(string.Format("\n\tRequiredMetadata")); + foreach (KeyValuePair metadataItem in _requiredMetadata) + { + sb.Append(string.Format("\n\t\t{0}\t({1})", metadataItem.Key, metadataItem.Value)); + } + } + return sb.ToString(); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/Export.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/Export.cs new file mode 100644 index 0000000000..4bcdf83794 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/Export.cs @@ -0,0 +1,253 @@ +// 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.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; +using System.Threading; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Primitives +{ + /// + /// Represents an export. That is, a type that is made up of a delay-created exported value + /// and metadata that describes that object. + /// + public class Export + { + private readonly ExportDefinition _definition; + private readonly Func _exportedValueGetter; + private static readonly object _EmptyValue = new object(); + private volatile object _exportedValue = Export._EmptyValue; + + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// Derived types calling this constructor must override + /// and . + /// + /// + protected Export() + { + } + + /// + /// Initializes a new instance of the class + /// with the specified contract name and exported value getter. + /// + /// + /// A containing the contract name of the + /// . + /// + /// + /// A that is called to create the exported value of the + /// . This allows the creation of the object to be delayed + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// + /// is an empty string (""). + /// + public Export(string contractName, Func exportedValueGetter) + : this(new ExportDefinition(contractName, (IDictionary)null), exportedValueGetter) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified contract name, metadata and exported value getter. + /// + /// + /// A containing the contract name of the + /// . + /// + /// + /// An containing the metadata of the + /// ; or to set the + /// property to an empty, read-only + /// . + /// + /// + /// A that is called to create the exported value of the + /// . This allows the creation of the object to be delayed. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// + /// is an empty string (""). + /// + public Export(string contractName, IDictionary metadata, Func exportedValueGetter) + : this(new ExportDefinition(contractName, metadata), exportedValueGetter) + { + } + + /// + /// Initializes a new instance of the class + /// with the specified export definition and exported value getter. + /// + /// + /// An that describes the contract that the + /// satisfies. + /// + /// + /// A that is called to create the exported value of the + /// . This allows the creation of the object to be delayed. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + public Export(ExportDefinition definition, Func exportedValueGetter) + { + Requires.NotNull(definition, nameof(definition)); + Requires.NotNull(exportedValueGetter, nameof(exportedValueGetter)); + + _definition = definition; + _exportedValueGetter = exportedValueGetter; + } + + /// + /// Gets the definition that describes the contract that the export satisfies. + /// + /// + /// An that describes the contract that + /// the satisfies. + /// + /// + /// This property was not overridden by a derived class. + /// + /// + /// + /// Overriders of this property should never return + /// . + /// + /// + public virtual ExportDefinition Definition + { + get + { + Contract.Ensures(Contract.Result() != null); + + if (_definition != null) + { + return _definition; + } + + throw ExceptionBuilder.CreateNotOverriddenByDerived("Definition"); + } + } + + /// + /// Gets the metadata of the export. + /// + /// + /// An containing the metadata of the + /// . + /// + /// + /// The property was not overridden by a derived class. + /// + /// + /// + /// This property returns the value of + /// of the property. + /// + /// + public IDictionary Metadata + { + get + { + Contract.Ensures(Contract.Result>() != null); + + return Definition.Metadata; + } + } + + /// + /// Returns the exported value of the export. + /// + /// + /// The exported of the . + /// + /// + /// An error occurred during composition. will + /// contain a collection of errors that occurred. + /// + /// + /// The current instance is an instance of and the underlying + /// exported value cannot be cast to T. + /// + /// + /// The method was not overridden by a derived class. + /// + public object Value + { + get + { + // NOTE : the logic below guarantees that the value will be set exactly once. It DOES NOT, however, guarantee that GetExportedValueCore() will be executed + // more than once, as locking would be required for that. The said locking is problematic, as we can't reliable call 3rd party code under a lock. + if (_exportedValue == Export._EmptyValue) + { + object exportedValue = GetExportedValueCore(); + + // NOTE : According to http://msdn.microsoft.com/en-us/library/4bw5ewxy.aspx, the warning is bogus when used with Interlocked API. +#pragma warning disable 420 + Interlocked.CompareExchange(ref _exportedValue, exportedValue, Export._EmptyValue); +#pragma warning restore 420 + } + + return _exportedValue; + } + } + + /// + /// Returns the exported value of the export. + /// + /// + /// The exported of the . + /// + /// + /// An error occurred during composition. will + /// contain a collection of errors that occurred. + /// + /// + /// The current instance is an instance of and the underlying + /// exported value cannot be cast to T. + /// + /// + /// The method was not overridden by a derived class. + /// + /// + /// + /// Overriders of this method should never return + /// . + /// + /// + [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + protected virtual object GetExportedValueCore() + { + if (_exportedValueGetter != null) + { + return _exportedValueGetter.Invoke(); + } + + throw ExceptionBuilder.CreateNotOverriddenByDerived("GetExportedValueCore"); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ExportDefinition.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ExportDefinition.cs new file mode 100644 index 0000000000..231b7f89b7 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ExportDefinition.cs @@ -0,0 +1,139 @@ +// 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.Collections.Generic; +using System.Diagnostics.Contracts; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Primitives +{ + /// + /// Describes the contract that an object satisfies. + /// + public class ExportDefinition + { + // Unlike contract name, metadata has a sensible default; set it to an empty bag, + // so that derived definitions only need to override ContractName by default. + private readonly IDictionary _metadata = MetadataServices.EmptyMetadata; + private readonly string _contractName; + + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// Derived types calling this constructor must override + /// and optionally, . By default, + /// returns an empty, read-only dictionary. + /// + /// + protected ExportDefinition() + { + } + + /// + /// Initializes a new instance of the class with + /// the specified contract name and metadata. + /// + /// + /// A containing the contract name of the + /// . + /// + /// + /// An containing the metadata of the + /// ; or to set the + /// property to an empty, read-only + /// . + /// + /// + /// is . + /// + /// + /// is an empty string (""). + /// + public ExportDefinition(string contractName, IDictionary metadata) + { + Requires.NotNullOrEmpty(contractName, "contractName"); + + _contractName = contractName; + + if (metadata != null) + { + _metadata = metadata.AsReadOnly(); + } + } + + /// + /// Gets the contract name of the export definition. + /// + /// + /// A containing the contract name of the + /// . + /// + /// + /// The property was not overridden by a derived class. + /// + /// + /// + /// Overriders of this property should never return + /// or an empty string (""). + /// + /// + public virtual string ContractName + { + get + { + Contract.Ensures(!string.IsNullOrEmpty(Contract.Result())); + + if (_contractName != null) + { + return _contractName; + } + + throw ExceptionBuilder.CreateNotOverriddenByDerived("ContractName"); + } + } + + /// + /// Gets the metadata of the export definition. + /// + /// + /// An containing the metadata of the + /// . The default is an empty, read-only + /// . + /// + /// + /// + /// + /// Overriders of this property should return a read-only + /// object with a case-sensitive, + /// non-linguistic comparer, such as , + /// and should never return . + /// If the does not contain metadata + /// return an empty instead. + /// + /// + /// + public virtual IDictionary Metadata + { + get + { + Contract.Ensures(Contract.Result>() != null); + + return _metadata; + } + } + + /// + /// Returns a string representation of the export definition. + /// + /// + /// A containing the value of the property. + /// + public override string ToString() + { + return ContractName; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ExportedDelegate.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ExportedDelegate.cs new file mode 100644 index 0000000000..8481d83ac5 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ExportedDelegate.cs @@ -0,0 +1,62 @@ +// 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.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Primitives +{ + [SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")] + public class ExportedDelegate + { + private object _instance; + private MethodInfo _method; + + protected ExportedDelegate() { } + + public ExportedDelegate(object instance, MethodInfo method) + { + Requires.NotNull(method, nameof(method)); + + _instance = instance; + _method = method; + } + + public virtual Delegate CreateDelegate(Type delegateType) + { + Requires.NotNull(delegateType, nameof(delegateType)); + + if (delegateType == typeof(Delegate) || delegateType == typeof(MulticastDelegate)) + { + delegateType = CreateStandardDelegateType(); + } + try + { + return _method.CreateDelegate(delegateType, _instance); + } + catch(ArgumentException) + { + //Bind failure occurs return null; + return null; + } + } + + private Type CreateStandardDelegateType() + { + ParameterInfo[] parameters = _method.GetParameters(); + + // This array should contains a lit of all argument types, and the last one is the return type (could be void) + Type[] parameterTypes = new Type[parameters.Length + 1]; + parameterTypes[parameters.Length] = _method.ReturnType; + for (int i = 0; i < parameters.Length; i++ ) + { + parameterTypes[i] = parameters[i].ParameterType; + } + + return Expression.GetDelegateType(parameterTypes); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ICompositionElement.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ICompositionElement.cs new file mode 100644 index 0000000000..ef69b282e3 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ICompositionElement.cs @@ -0,0 +1,43 @@ +// 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. + +namespace System.ComponentModel.Composition.Primitives +{ + /// + /// Represents an element that participates in composition. + /// + public interface ICompositionElement + { + /// + /// Gets the display name of the composition element. + /// + /// + /// A containing a human-readable display name of the . + /// + /// + /// + /// Implementors of this property should never return or an empty + /// string (""). + /// + /// + string DisplayName + { + get; + } + + /// + /// Gets the composition element from which the current composition element + /// originated. + /// + /// + /// A from which the current + /// originated, or + /// if the is the root composition element. + /// + ICompositionElement Origin + { + get; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/IPartCreatorImportDefinition.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/IPartCreatorImportDefinition.cs new file mode 100644 index 0000000000..6eb946f88d --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/IPartCreatorImportDefinition.cs @@ -0,0 +1,11 @@ +// 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. + +namespace System.ComponentModel.Composition.Primitives +{ + internal interface IPartCreatorImportDefinition + { + ContractBasedImportDefinition ProductImportDefinition { get; } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ImportCardinality.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ImportCardinality.cs new file mode 100644 index 0000000000..097bfe03e9 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ImportCardinality.cs @@ -0,0 +1,27 @@ +// 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. + +namespace System.ComponentModel.Composition.Primitives +{ + /// + /// Indicates the cardinality of the objects required by an . + /// + public enum ImportCardinality + { + /// + /// Zero or one objects are required by an . + /// + ZeroOrOne = 0, + + /// + /// Exactly one object is required by an . + /// + ExactlyOne = 1, + + /// + /// Zero or more objects are required by an . + /// + ZeroOrMore = 2, + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ImportDefinition.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ImportDefinition.cs new file mode 100644 index 0000000000..c4a46ef833 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ImportDefinition.cs @@ -0,0 +1,291 @@ +// 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.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Linq.Expressions; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.Primitives +{ + /// + /// Represents an import required by a object. + /// + public class ImportDefinition + { + internal static readonly string EmptyContractName = string.Empty; + private readonly Expression> _constraint; + private readonly ImportCardinality _cardinality = ImportCardinality.ExactlyOne; + private readonly string _contractName = EmptyContractName; + private readonly bool _isRecomposable; + private readonly bool _isPrerequisite = true; + private Func _compiledConstraint; + private readonly IDictionary _metadata = MetadataServices.EmptyMetadata; + + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// Derived types calling this constructor must override the + /// property, and optionally, the , + /// and + /// properties. + /// + /// + protected ImportDefinition() + { + } + + /// + /// Initializes a new instance of the class + /// with the specified constraint, cardinality, value indicating if the import + /// definition is recomposable and a value indicating if the import definition + /// is a prerequisite. + /// + /// + /// A containing a + /// that defines the conditions that must be matched for the + /// to be satisfied by an . + /// + /// + /// The contract name of the export that this import is interested in. The contract name + /// property is used as guidance and not automatically enforced in the constraint. If + /// the contract name is a required in the constraint then it should be added to the constraint + /// by the caller of this constructor. + /// + /// + /// One of the values indicating the + /// cardinality of the objects required by the + /// . + /// + /// + /// if the can be satisfied + /// multiple times throughout the lifetime of a , otherwise, + /// . + /// + /// + /// if the is required to be + /// satisfied before a can start producing exported + /// objects; otherwise, . + /// + /// + /// is . + /// + /// + /// is not one of the + /// values. + /// + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public ImportDefinition(Expression> constraint, string contractName, ImportCardinality cardinality, bool isRecomposable, bool isPrerequisite) + : this(contractName, cardinality, isRecomposable, isPrerequisite, MetadataServices.EmptyMetadata) + { + Requires.NotNull(constraint, nameof(constraint)); + + _constraint = constraint; + } + + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public ImportDefinition(Expression> constraint, string contractName, ImportCardinality cardinality, bool isRecomposable, bool isPrerequisite, IDictionary metadata) + : this(contractName, cardinality, isRecomposable, isPrerequisite, metadata) + { + Requires.NotNull(constraint, nameof(constraint)); + + _constraint = constraint; + } + + internal ImportDefinition(string contractName, ImportCardinality cardinality, bool isRecomposable, bool isPrerequisite, IDictionary metadata) + { + if ( + (cardinality != ImportCardinality.ExactlyOne) && + (cardinality != ImportCardinality.ZeroOrMore) && + (cardinality != ImportCardinality.ZeroOrOne) + ) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.ArgumentOutOfRange_InvalidEnum, "cardinality", cardinality, typeof(ImportCardinality).Name), "cardinality"); + } + + _contractName = contractName ?? EmptyContractName; + _cardinality = cardinality; + _isRecomposable = isRecomposable; + _isPrerequisite = isPrerequisite; + + //Metadata on imports was added in 4.5, prior to that it was ignored. + if (metadata != null) + { + _metadata = metadata; + } + } + + /// + /// Gets the contract name of the export required by the import definition. + /// + /// + /// A containing the contract name of the + /// required by the . This property should + /// return for imports that do not require a specific + /// contract name. + /// + public virtual string ContractName + { + get + { + Contract.Ensures(Contract.Result() != null); + + return _contractName; + } + } + + /// + /// Gets the metadata of the import definition. + /// + /// + /// An containing the metadata of the + /// . The default is an empty, read-only + /// . + /// + /// + /// + /// + /// Overriders of this property should return a read-only + /// object with a case-sensitive, + /// non-linguistic comparer, such as , + /// and should never return . + /// If the does not contain metadata + /// return an empty instead. + /// + /// + /// + public virtual IDictionary Metadata + { + get + { + Contract.Ensures(Contract.Result>() != null); + + return _metadata; + } + } + + /// + /// Gets the cardinality of the exports required by the import definition. + /// + /// + /// One of the values indicating the + /// cardinality of the objects required by the + /// . The default is + /// + /// + public virtual ImportCardinality Cardinality + { + get { return _cardinality; } + } + + /// + /// Gets an expression that defines conditions that must be matched for the import + /// described by the import definition to be satisfied. + /// + /// + /// A containing a + /// that defines the conditions that must be matched for the + /// to be satisfied by an . + /// + /// + /// The property was not overridden by a derived class. + /// + /// + /// + /// Overriders of this property should never return . + /// + /// + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public virtual Expression> Constraint + { + get + { + Contract.Ensures(Contract.Result>>() != null); + + if (_constraint != null) + { + return _constraint; + } + + throw ExceptionBuilder.CreateNotOverriddenByDerived("Constraint"); + } + } + + /// + /// Gets a value indicating whether the import definition is required to be + /// satisfied before a part can start producing exported values. + /// + /// + /// if the is required to be + /// satisfied before a can start producing exported + /// objects; otherwise, . The default is . + /// + public virtual bool IsPrerequisite + { + get { return _isPrerequisite; } + } + + /// + /// Gets a value indicating whether the import definition can be satisfied multiple times. + /// + /// + /// if the can be satisfied + /// multiple times throughout the lifetime of a , otherwise, + /// . The default is . + /// + public virtual bool IsRecomposable + { + get { return _isRecomposable; } + } + + /// + /// Executes of the constraint provided by the property + /// against a given to determine if this + /// can be satisfied by the given . + /// + /// + /// A definition for a used to determine if it satisfies the + /// requirements for this . + /// + /// + /// if the satisfies the requirements for + /// this , otherwise returns . + /// + /// + /// + /// Overrides of this method can provide a more optimized execution of the + /// property but the result should remain consistent. + /// + /// + /// + /// is . + /// + public virtual bool IsConstraintSatisfiedBy(ExportDefinition exportDefinition) + { + Requires.NotNull(exportDefinition, nameof(exportDefinition)); + + if (_compiledConstraint == null) + { + _compiledConstraint = Constraint.Compile(); + } + + return _compiledConstraint.Invoke(exportDefinition); + } + + /// + /// Returns a string representation of the import definition. + /// + /// + /// A containing the value of the property. + /// + public override string ToString() + { + return Constraint.Body.ToString(); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/PrimitivesServices.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/PrimitivesServices.cs new file mode 100644 index 0000000000..6213a5822c --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/PrimitivesServices.cs @@ -0,0 +1,102 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.ReflectionModel; + +namespace System.ComponentModel.Composition.Primitives +{ + internal static class PrimitivesServices + { + public static bool IsGeneric(this ComposablePartDefinition part) + { + return part.Metadata.GetValue(CompositionConstants.IsGenericPartMetadataName); + } + + public static ImportDefinition GetProductImportDefinition(this ImportDefinition import) + { + IPartCreatorImportDefinition partCreatorDefinition = import as IPartCreatorImportDefinition; + + if (partCreatorDefinition != null) + { + return partCreatorDefinition.ProductImportDefinition; + } + else + { + return import; + } + } + + internal static IEnumerable GetCandidateContractNames(this ImportDefinition import, ComposablePartDefinition part) + { + import = import.GetProductImportDefinition(); + string contractName = import.ContractName; + string genericContractName = import.Metadata.GetValue(CompositionConstants.GenericContractMetadataName); + int[] importParametersOrder = import.Metadata.GetValue(CompositionConstants.GenericImportParametersOrderMetadataName); + if (importParametersOrder != null) + { + int partArity = part.Metadata.GetValue(CompositionConstants.GenericPartArityMetadataName); + if (partArity > 0) + { + contractName = GenericServices.GetGenericName(contractName, importParametersOrder, partArity); + } + } + + yield return contractName; + if (!string.IsNullOrEmpty(genericContractName)) + { + yield return genericContractName; + } + } + +internal static bool IsImportDependentOnPart(this ImportDefinition import, ComposablePartDefinition part, ExportDefinition export, bool expandGenerics) + { + import = import.GetProductImportDefinition(); + if (expandGenerics) + { + Tuple singleMatch; + IEnumerable> multipleMatches; + return part.TryGetExports(import, out singleMatch, out multipleMatches); + } + else + { + return TranslateImport(import, part).IsConstraintSatisfiedBy(export); + } + } + + private static ImportDefinition TranslateImport(ImportDefinition import, ComposablePartDefinition part) + { + ContractBasedImportDefinition contractBasedImport = import as ContractBasedImportDefinition; + if (contractBasedImport == null) + { + return import; + } + + int[] importParametersOrder = contractBasedImport.Metadata.GetValue(CompositionConstants.GenericImportParametersOrderMetadataName); + if (importParametersOrder == null) + { + return import; + } + + int partArity = part.Metadata.GetValue(CompositionConstants.GenericPartArityMetadataName); + if (partArity == 0) + { + return import; + } + + string contractName = GenericServices.GetGenericName(contractBasedImport.ContractName, importParametersOrder, partArity); + string requiredTypeIdentity = GenericServices.GetGenericName(contractBasedImport.RequiredTypeIdentity, importParametersOrder, partArity); + return new ContractBasedImportDefinition( + contractName, + requiredTypeIdentity, + contractBasedImport.RequiredMetadata, + contractBasedImport.Cardinality, + contractBasedImport.IsRecomposable, + false, + contractBasedImport.RequiredCreationPolicy, + contractBasedImport.Metadata); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/DisposableReflectionComposablePart.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/DisposableReflectionComposablePart.cs new file mode 100644 index 0000000000..4f353eee0b --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/DisposableReflectionComposablePart.cs @@ -0,0 +1,47 @@ +// 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.Threading; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal sealed class DisposableReflectionComposablePart : ReflectionComposablePart, IDisposable + { + private volatile int _isDisposed = 0; + + public DisposableReflectionComposablePart(ReflectionComposablePartDefinition definition) + : base(definition) + { + } + + protected override void ReleaseInstanceIfNecessary(object instance) + { + IDisposable disposable = instance as IDisposable; + if (disposable != null) + { + disposable.Dispose(); + } + } + + protected override void EnsureRunning() + { + base.EnsureRunning(); + if (_isDisposed == 1) + { + throw ExceptionBuilder.CreateObjectDisposed(this); + } + } + + void IDisposable.Dispose() + { + // NOTE : According to http://msdn.microsoft.com/en-us/library/4bw5ewxy.aspx, the warning is bogus when used with Interlocked API. +#pragma warning disable 420 + if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0) +#pragma warning restore 420 + { + ReleaseInstanceIfNecessary(CachedInstance); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ExportfactoryCreator.LifetimeContext.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ExportfactoryCreator.LifetimeContext.cs new file mode 100644 index 0000000000..e5e1dd7e92 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ExportfactoryCreator.LifetimeContext.cs @@ -0,0 +1,68 @@ +// 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.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal sealed partial class ExportFactoryCreator + { + private class LifetimeContext + { + private static Type[] types = { typeof(ComposablePartDefinition) }; + + public Tuple GetExportLifetimeContextFromExport(Export export) + { + T exportedValue; + Action disposeAction; + IDisposable disposable = null; + + CatalogExportProvider.ScopeFactoryExport scopeFactoryExport = export as CatalogExportProvider.ScopeFactoryExport; + + if (scopeFactoryExport != null) + { + // Scoped PartCreatorExport + Export exportProduct = scopeFactoryExport.CreateExportProduct(); + exportedValue = ExportServices.GetCastedExportedValue(exportProduct); + disposable = exportProduct as IDisposable; + } + else + { + CatalogExportProvider.FactoryExport factoryExport = export as CatalogExportProvider.FactoryExport; + + if (factoryExport != null) + { + // PartCreatorExport is the more optimized route + Export exportProduct = factoryExport.CreateExportProduct(); + exportedValue = ExportServices.GetCastedExportedValue(exportProduct); + disposable = exportProduct as IDisposable; + } + else + { + // If it comes from somewhere else we walk through the ComposablePartDefinition + var factoryPartDefinition = ExportServices.GetCastedExportedValue(export); + var part = factoryPartDefinition.CreatePart(); + var exportDef = factoryPartDefinition.ExportDefinitions.Single(); + + exportedValue = ExportServices.CastExportedValue(part.ToElement(), part.GetExportedValue(exportDef)); + disposable = part as IDisposable; + } + } + + if (disposable != null) + { + disposeAction = () => disposable.Dispose(); + } + else + { + disposeAction = () => { }; + } + + return new Tuple(exportedValue, disposeAction); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ExportfactoryCreator.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ExportfactoryCreator.cs new file mode 100644 index 0000000000..0fb131e438 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ExportfactoryCreator.cs @@ -0,0 +1,74 @@ +// 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.ComponentModel.Composition.Primitives; +using System.Reflection; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal sealed partial class ExportFactoryCreator + { + private static readonly MethodInfo _createStronglyTypedExportFactoryOfT = typeof(ExportFactoryCreator).GetMethod("CreateStronglyTypedExportFactoryOfT", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + private static readonly MethodInfo _createStronglyTypedExportFactoryOfTM = typeof(ExportFactoryCreator).GetMethod("CreateStronglyTypedExportFactoryOfTM", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + + private Type _exportFactoryType; + + public ExportFactoryCreator(Type exportFactoryType) + { + Assumes.NotNull(exportFactoryType); + + _exportFactoryType = exportFactoryType; + } + + public Func CreateStronglyTypedExportFactoryFactory(Type exportType, Type metadataViewType) + { + MethodInfo genericMethod = null; + if (metadataViewType == null) + { + genericMethod = _createStronglyTypedExportFactoryOfT.MakeGenericMethod(exportType); + } + else + { + genericMethod = _createStronglyTypedExportFactoryOfTM.MakeGenericMethod(exportType, metadataViewType); + } + + Assumes.NotNull(genericMethod); + Func exportFactoryFactory = (Func)Delegate.CreateDelegate(typeof(Func), this, genericMethod); + return (e) => exportFactoryFactory.Invoke(e); + } + + private object CreateStronglyTypedExportFactoryOfT(Export export) + { + Type[] typeArgs = { typeof(T) }; + Type constructed = _exportFactoryType.MakeGenericType(typeArgs); + + var lifetimeContext = new LifetimeContext(); + + Func> exportLifetimeContextCreator = () => lifetimeContext.GetExportLifetimeContextFromExport(export); + object[] args = { exportLifetimeContextCreator }; + + var instance = Activator.CreateInstance(constructed, args); + + return instance; + } + + private object CreateStronglyTypedExportFactoryOfTM(Export export) + { + Type[] typeArgs = { typeof(T), typeof(M) }; + Type constructed = _exportFactoryType.MakeGenericType(typeArgs); + + var lifetimeContext = new LifetimeContext(); + + Func> exportLifetimeContextCreator = () => lifetimeContext.GetExportLifetimeContextFromExport(export); + var metadataView = AttributedModelServices.GetMetadataView(export.Metadata); + object[] args = { exportLifetimeContextCreator, metadataView }; + + var instance = Activator.CreateInstance(constructed, args); + + return instance; + } + + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ExportingMember.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ExportingMember.cs new file mode 100644 index 0000000000..6efe5f9acc --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ExportingMember.cs @@ -0,0 +1,102 @@ +// 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.ComponentModel.Composition.Primitives; +using System.Globalization; +using System.Reflection; +using System.Threading; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal class ExportingMember + { + private readonly ExportDefinition _definition; + private readonly ReflectionMember _member; + private object _cachedValue = null; + private volatile bool _isValueCached = false; + + public ExportingMember(ExportDefinition definition, ReflectionMember member) + { + Assumes.NotNull(definition, member); + + _definition = definition; + _member = member; + } + + public bool RequiresInstance + { + get { return _member.RequiresInstance; } + } + + public ExportDefinition Definition + { + get { return _definition; } + } + + public object GetExportedValue(object instance, object @lock) + { + EnsureReadable(); + + if (!_isValueCached) + { + object exportedValue; + try + { + exportedValue = _member.GetValue(instance); + } + catch (TargetInvocationException exception) + { // Member threw an exception. Avoid letting this + // leak out as a 'raw' unhandled exception, instead, + // we'll add some context and rethrow. + + throw new ComposablePartException( + String.Format(CultureInfo.CurrentCulture, + SR.ReflectionModel_ExportThrewException, + _member.GetDisplayName()), + Definition.ToElement(), + exception.InnerException); + } + catch (TargetParameterCountException exception) + { + // Exception was a TargetParameterCountException this occurs when we try to get an Indexer that has an Export + // this is not supported in MEF currently. Ideally we would validate against it, however, we already shipped + // so we will turn it into a ComposablePartException instead, that they should already be prepared for + throw new ComposablePartException( + String.Format(CultureInfo.CurrentCulture, + SR.ExportNotValidOnIndexers, + _member.GetDisplayName()), + Definition.ToElement(), + exception.InnerException); + } + + lock (@lock) + { + if (!_isValueCached) + { + _cachedValue = exportedValue; + Thread.MemoryBarrier(); + + _isValueCached = true; + } + } + } + + return _cachedValue; + } + + private void EnsureReadable() + { + if (!_member.CanRead) + { // Property does not have a getter + + throw new ComposablePartException( + String.Format(CultureInfo.CurrentCulture, + SR.ReflectionModel_ExportNotReadable, + _member.GetDisplayName()), + Definition.ToElement()); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/GenericServices.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/GenericServices.cs new file mode 100644 index 0000000000..ed2dac54e3 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/GenericServices.cs @@ -0,0 +1,210 @@ +// 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.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal static class GenericServices + { + internal static IList GetPureGenericParameters(this Type type) + { + Assumes.NotNull(type); + + if (type.IsGenericType && type.ContainsGenericParameters) + { + List pureGenericParameters = new List(); + TraverseGenericType(type, (Type t) => + { + if (t.IsGenericParameter) + { + pureGenericParameters.Add(t); + } + }); + return pureGenericParameters; + } + else + { + return Type.EmptyTypes; + } + } + + internal static int GetPureGenericArity(this Type type) + { + Assumes.NotNull(type); + + int genericArity = 0; + if (type.IsGenericType && type.ContainsGenericParameters) + { + List pureGenericParameters = new List(); + TraverseGenericType(type, (Type t) => + { + if (t.IsGenericParameter) + { + genericArity++; + } + }); + } + return genericArity; + } + + private static void TraverseGenericType(Type type, Action onType) + { + if (type.IsGenericType) + { + foreach (Type genericArgument in type.GetGenericArguments()) + { + TraverseGenericType(genericArgument, onType); + } + } + onType(type); + } + + public static int[] GetGenericParametersOrder(Type type) + { + return type.GetPureGenericParameters().Select(parameter => parameter.GenericParameterPosition).ToArray(); + } + + public static string GetGenericName(string originalGenericName, int[] genericParametersOrder, int genericArity) + { + string[] genericFormatArgs = new string[genericArity]; + for (int i = 0; i < genericParametersOrder.Length; i++) + { + genericFormatArgs[genericParametersOrder[i]] = string.Format(CultureInfo.InvariantCulture, "{{{0}}}", i); + } + return string.Format(CultureInfo.InvariantCulture, originalGenericName, genericFormatArgs); + } + + public static T[] Reorder(T[] original, int[] genericParametersOrder) + { + T[] genericSpecialization = new T[genericParametersOrder.Length]; + for (int i = 0; i < genericParametersOrder.Length; i++) + { + genericSpecialization[i] = original[genericParametersOrder[i]]; + } + return genericSpecialization; + } + + public static IEnumerable CreateTypeSpecializations(this Type[] types, Type[] specializationTypes) + { + if (types == null) + { + return null; + } + else + { + return types.Select(type => type.CreateTypeSpecialization(specializationTypes)); + } + } + + public static Type CreateTypeSpecialization(this Type type, Type[] specializationTypes) + { + if (!type.ContainsGenericParameters) + { + return type; + } + + if (type.IsGenericParameter) + { + // the only case when MakeGenericType won't work is when the 'type' represents a "naked" generic type + // in this case we simply grab the type with the proper index from the specializtion + return specializationTypes[type.GenericParameterPosition]; + } + else + { + Type[] typeGenericArguments = type.GetGenericArguments(); + Type[] subSpecialization = new Type[typeGenericArguments.Length]; + + for (int i = 0; i < typeGenericArguments.Length; i++) + { + Type typeGenericArgument = typeGenericArguments[i]; + subSpecialization[i] = typeGenericArgument.IsGenericParameter ? + specializationTypes[typeGenericArgument.GenericParameterPosition] : typeGenericArgument; + + } + + // and "close" the generic + return type.GetGenericTypeDefinition().MakeGenericType(subSpecialization); + } + + } + + public static bool CanSpecialize(Type type, IEnumerable constraints, GenericParameterAttributes attributes) + { + return CanSpecialize(type, constraints) && CanSpecialize(type, attributes); + } + + public static bool CanSpecialize(Type type, IEnumerable constraintTypes) + { + if (constraintTypes == null) + { + return true; + } + + // where T : IFoo + // a part of where T : struct is also handled here as T : ValueType + foreach (Type constraintType in constraintTypes) + { + if ((constraintType != null) && !constraintType.IsAssignableFrom(type)) + { + return false; + } + } + + return true; + } + + public static bool CanSpecialize(Type type, GenericParameterAttributes attributes) + { + if (attributes == GenericParameterAttributes.None) + { + return true; + } + + // where T : class + if ((attributes & GenericParameterAttributes.ReferenceTypeConstraint) != 0) + { + if (type.IsValueType) + { + return false; + } + } + + // where T : new + if ((attributes & GenericParameterAttributes.DefaultConstructorConstraint) != 0) + { + // value types always have default constructors + if (!type.IsValueType && (type.GetConstructor(Type.EmptyTypes) == null)) + { + return false; + } + } + + // where T : struct + if ((attributes & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0) + { + // must be a value type + if (!type.IsValueType) + { + return false; + } + + // Make sure that the type is not nullable + // this is salways guaranteed in C#, but other languages may be different + if (Nullable.GetUnderlyingType(type) != null) + { + return false; + } + } + + // all other fals indicate variance and don't place any actual restrictions on the generic parameters + // but rather how they should be used by the compiler + return true; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/GenericSpecializationPartCreationInfo.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/GenericSpecializationPartCreationInfo.cs new file mode 100644 index 0000000000..121e56cf70 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/GenericSpecializationPartCreationInfo.cs @@ -0,0 +1,584 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Globalization; +using System.Reflection; +using System.Threading; +using Microsoft.Internal; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal class GenericSpecializationPartCreationInfo : IReflectionPartCreationInfo + { + private readonly IReflectionPartCreationInfo _originalPartCreationInfo; + private readonly ReflectionComposablePartDefinition _originalPart; + private readonly Type[] _specialization; + private readonly string[] _specializationIdentities; + private IEnumerable _exports; + private IEnumerable _imports; + private readonly Lazy _lazyPartType; + private List _members; + private List> _parameters; + private Dictionary _membersTable; + private Dictionary, ParameterInfo> _parametersTable; + private ConstructorInfo _constructor; + private object _lock = new object(); + + public GenericSpecializationPartCreationInfo(IReflectionPartCreationInfo originalPartCreationInfo, ReflectionComposablePartDefinition originalPart, Type[] specialization) + { + Assumes.NotNull(originalPartCreationInfo); + Assumes.NotNull(specialization); + Assumes.NotNull(originalPart); + + _originalPartCreationInfo = originalPartCreationInfo; + _originalPart = originalPart; + _specialization = specialization; + _specializationIdentities = new string[_specialization.Length]; + for (int i = 0; i < _specialization.Length; i++) + { + _specializationIdentities[i] = AttributedModelServices.GetTypeIdentity(_specialization[i]); + } + _lazyPartType = new Lazy( + () => _originalPartCreationInfo.GetPartType().MakeGenericType(specialization), + LazyThreadSafetyMode.PublicationOnly); + + } + + public ReflectionComposablePartDefinition OriginalPart + { + get + { + return _originalPart; + } + } + + public Type GetPartType() + { + return _lazyPartType.Value; + } + + public Lazy GetLazyPartType() + { + return _lazyPartType; + } + + public ConstructorInfo GetConstructor() + { + if (_constructor == null) + { + ConstructorInfo genericConstuctor = _originalPartCreationInfo.GetConstructor(); + ConstructorInfo result = null; + if (genericConstuctor != null) + { + foreach (ConstructorInfo constructor in GetPartType().GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) + { + if (constructor.MetadataToken == genericConstuctor.MetadataToken) + { + result = constructor; + break; + } + } + } + + Thread.MemoryBarrier(); + lock (_lock) + { + if (_constructor == null) + { + _constructor = result; + } + } + } + + return _constructor; + } + + public IDictionary GetMetadata() + { + var originalMetadata = new Dictionary(_originalPartCreationInfo.GetMetadata(), StringComparers.MetadataKeyNames); + originalMetadata.Remove(CompositionConstants.IsGenericPartMetadataName); + originalMetadata.Remove(CompositionConstants.GenericPartArityMetadataName); + originalMetadata.Remove(CompositionConstants.GenericParameterConstraintsMetadataName); + originalMetadata.Remove(CompositionConstants.GenericParameterAttributesMetadataName); + + return originalMetadata; + } + + private MemberInfo[] GetAccessors(LazyMemberInfo originalLazyMember) + { + BuildTables(); + Assumes.NotNull(_membersTable); + + return _membersTable[originalLazyMember]; + } + + private ParameterInfo GetParameter(Lazy originalParameter) + { + BuildTables(); + Assumes.NotNull(_parametersTable); + + return _parametersTable[originalParameter]; + } + + private void BuildTables() + { + if (_membersTable != null) + { + return; + } + + PopulateImportsAndExports(); + + List members = null; + List> parameters = null; + lock (_lock) + { + if (_membersTable == null) + { + members = _members; + parameters = _parameters; + + Assumes.NotNull(members); + } + } + + // + // Get all members that can be of interest and extract their MetadataTokens + // + Dictionary membersTable = BuildMembersTable(members); + Dictionary, ParameterInfo> parametersTable = BuildParametersTable(parameters); + + lock (_lock) + { + if (_membersTable == null) + { + _membersTable = membersTable; + _parametersTable = parametersTable; + + Thread.MemoryBarrier(); + + _parameters = null; + _members = null; + } + } + } + + private Dictionary BuildMembersTable(List members) + { + Assumes.NotNull(members); + + Dictionary membersTable = new Dictionary(); + Dictionary specializedPartMembers = new Dictionary(); + + Type closedGenericPartType = GetPartType(); + + specializedPartMembers[closedGenericPartType.MetadataToken] = closedGenericPartType; + foreach (MethodInfo method in closedGenericPartType.GetAllMethods()) + { + specializedPartMembers[method.MetadataToken] = method; + } + + foreach (FieldInfo field in closedGenericPartType.GetAllFields()) + { + specializedPartMembers[field.MetadataToken] = field; + } + + foreach (var iface in closedGenericPartType.GetInterfaces()) + { + specializedPartMembers[iface.MetadataToken] = iface; + } + + foreach (var type in closedGenericPartType.GetNestedTypes()) + { + specializedPartMembers[type.MetadataToken] = type; + } + + //Walk the base class list + var baseType = closedGenericPartType.BaseType; + while (baseType != null && baseType != typeof(object)) + { + specializedPartMembers[baseType.MetadataToken] = baseType; + baseType = baseType.BaseType; + } + + // + // Now go through the members table and resolve them into the new closed type based on the tokens + // + foreach (LazyMemberInfo lazyMemberInfo in members) + { + MemberInfo[] genericAccessors = lazyMemberInfo.GetAccessors(); + MemberInfo[] accessors = new MemberInfo[genericAccessors.Length]; + + for (int i = 0; i < genericAccessors.Length; i++) + { + if (genericAccessors[i] != null) + { + specializedPartMembers.TryGetValue(genericAccessors[i].MetadataToken, out accessors[i]); + Assumes.NotNull(accessors[i]); + } + } + + membersTable[lazyMemberInfo] = accessors; + } + + return membersTable; + } + + private Dictionary, ParameterInfo> BuildParametersTable(List> parameters) + { + if (parameters != null) + { + Dictionary, ParameterInfo> parametersTable = new Dictionary, ParameterInfo>(); + // GENTODO - error case + ParameterInfo[] constructorParameters = GetConstructor().GetParameters(); + foreach (var lazyParameter in parameters) + { + parametersTable[lazyParameter] = constructorParameters[lazyParameter.Value.Position]; + } + return parametersTable; + } + else + { + return null; + } + + } + + private List PopulateImports(List members, List> parameters) + { + List imports = new List(); + + foreach (ImportDefinition originalImport in _originalPartCreationInfo.GetImports()) + { + ReflectionImportDefinition reflectionImport = originalImport as ReflectionImportDefinition; + if (reflectionImport == null) + { + // we always ignore these + continue; + } + + imports.Add(TranslateImport(reflectionImport, members, parameters)); + } + + return imports; + } + + private ImportDefinition TranslateImport(ReflectionImportDefinition reflectionImport, List members, List> parameters) + { + bool isExportFactory = false; + ContractBasedImportDefinition productImport = reflectionImport; + + IPartCreatorImportDefinition exportFactoryImportDefinition = reflectionImport as IPartCreatorImportDefinition; + if (exportFactoryImportDefinition != null) + { + productImport = exportFactoryImportDefinition.ProductImportDefinition; + isExportFactory = true; + } + + string contractName = Translate(productImport.ContractName); + string requiredTypeIdentity = Translate(productImport.RequiredTypeIdentity); + IDictionary metadata = TranslateImportMetadata(productImport); + + ReflectionMemberImportDefinition memberImport = reflectionImport as ReflectionMemberImportDefinition; + ImportDefinition import = null; + if (memberImport != null) + { + LazyMemberInfo lazyMember = memberImport.ImportingLazyMember; + LazyMemberInfo importingMember = new LazyMemberInfo(lazyMember.MemberType, () => GetAccessors(lazyMember)); + + if (isExportFactory) + { + import = new PartCreatorMemberImportDefinition( + importingMember, + ((ICompositionElement)memberImport).Origin, + new ContractBasedImportDefinition( + contractName, + requiredTypeIdentity, + productImport.RequiredMetadata, + productImport.Cardinality, + productImport.IsRecomposable, + false, + CreationPolicy.NonShared, + metadata)); + } + else + { + import = new ReflectionMemberImportDefinition( + importingMember, + contractName, + requiredTypeIdentity, + productImport.RequiredMetadata, + productImport.Cardinality, + productImport.IsRecomposable, + false, + productImport.RequiredCreationPolicy, + metadata, + ((ICompositionElement)memberImport).Origin); + } + + members.Add(lazyMember); + } + else + { + ReflectionParameterImportDefinition parameterImport = reflectionImport as ReflectionParameterImportDefinition; + Assumes.NotNull(parameterImport); + + Lazy lazyParameter = parameterImport.ImportingLazyParameter; + Lazy parameter = new Lazy(() => GetParameter(lazyParameter)); + + if (isExportFactory) + { + import = new PartCreatorParameterImportDefinition( + parameter, + ((ICompositionElement)parameterImport).Origin, + new ContractBasedImportDefinition( + contractName, + requiredTypeIdentity, + productImport.RequiredMetadata, + productImport.Cardinality, + false, + true, + CreationPolicy.NonShared, + metadata)); + } + else + { + import = new ReflectionParameterImportDefinition( + parameter, + contractName, + requiredTypeIdentity, + productImport.RequiredMetadata, + productImport.Cardinality, + productImport.RequiredCreationPolicy, + metadata, + ((ICompositionElement)parameterImport).Origin); + } + + parameters.Add(lazyParameter); + } + + return import; + } + + private List PopulateExports(List members) + { + List exports = new List(); + + foreach (ExportDefinition originalExport in _originalPartCreationInfo.GetExports()) + { + ReflectionMemberExportDefinition reflectionExport = originalExport as ReflectionMemberExportDefinition; + if (reflectionExport == null) + { + // we always ignore these + continue; + } + + exports.Add(TranslateExpot(reflectionExport, members)); + } + + return exports; + } + + public ExportDefinition TranslateExpot(ReflectionMemberExportDefinition reflectionExport, List members) + { + ExportDefinition export = null; + LazyMemberInfo lazyMember = reflectionExport.ExportingLazyMember; + var capturedLazyMember = lazyMember; + var capturedReflectionExport = reflectionExport; + + string contractName = Translate(reflectionExport.ContractName, reflectionExport.Metadata.GetValue(CompositionConstants.GenericExportParametersOrderMetadataName)); + + LazyMemberInfo exportingMember = new LazyMemberInfo(capturedLazyMember.MemberType, () => GetAccessors(capturedLazyMember)); + Lazy> lazyMetadata = new Lazy>(() => TranslateExportMetadata(capturedReflectionExport)); + + export = new ReflectionMemberExportDefinition( + exportingMember, + new LazyExportDefinition(contractName, lazyMetadata), + ((ICompositionElement)reflectionExport).Origin); + + members.Add(capturedLazyMember); + + return export; + } + + private string Translate(string originalValue, int[] genericParametersOrder) + { + if (genericParametersOrder != null) + { + string[] specializationIdentities = GenericServices.Reorder(_specializationIdentities, genericParametersOrder); + return string.Format(CultureInfo.InvariantCulture, originalValue, specializationIdentities); + } + else + { + return Translate(originalValue); + } + } + + private string Translate(string originalValue) + { + return string.Format(CultureInfo.InvariantCulture, originalValue, _specializationIdentities); + } + + private IDictionary TranslateImportMetadata(ContractBasedImportDefinition originalImport) + { + int[] importParametersOrder = originalImport.Metadata.GetValue(CompositionConstants.GenericImportParametersOrderMetadataName); + if (importParametersOrder != null) + { + Dictionary metadata = new Dictionary(originalImport.Metadata, StringComparers.MetadataKeyNames); + + // Get the newly re-qualified name of the generic contract and the subset of applicable types from the specialization + metadata[CompositionConstants.GenericContractMetadataName] = GenericServices.GetGenericName(originalImport.ContractName, importParametersOrder, _specialization.Length); + metadata[CompositionConstants.GenericParametersMetadataName] = GenericServices.Reorder(_specialization, importParametersOrder); + metadata.Remove(CompositionConstants.GenericImportParametersOrderMetadataName); + + return metadata.AsReadOnly(); + } + else + { + return originalImport.Metadata; + } + } + + private IDictionary TranslateExportMetadata(ReflectionMemberExportDefinition originalExport) + { + Dictionary metadata = new Dictionary(originalExport.Metadata, StringComparers.MetadataKeyNames); + + string exportTypeIdentity = originalExport.Metadata.GetValue(CompositionConstants.ExportTypeIdentityMetadataName); + if (!string.IsNullOrEmpty(exportTypeIdentity)) + { + metadata[CompositionConstants.ExportTypeIdentityMetadataName] = Translate(exportTypeIdentity, originalExport.Metadata.GetValue(CompositionConstants.GenericExportParametersOrderMetadataName)); + } + metadata.Remove(CompositionConstants.GenericExportParametersOrderMetadataName); + + return metadata; + } + + private void PopulateImportsAndExports() + { + if ((_exports == null) || (_imports == null)) + { + List members = new List(); + List> parameters = new List>(); + + // we are very careful to not call any 3rd party code in either of these + var exports = PopulateExports(members); + var imports = PopulateImports(members, parameters); + Thread.MemoryBarrier(); + + lock (_lock) + { + if ((_exports == null) || (_imports == null)) + { + _members = members; + if (parameters.Count > 0) + { + _parameters = parameters; + } + + _exports = exports; + _imports = imports; + } + } + } + } + + public IEnumerable GetExports() + { + PopulateImportsAndExports(); + return _exports; + } + + public IEnumerable GetImports() + { + PopulateImportsAndExports(); + return _imports; + } + + public bool IsDisposalRequired + { + get { return _originalPartCreationInfo.IsDisposalRequired; } + } + + public bool IsIdentityComparison + { + get + { + return false; + } + } + + public string DisplayName + { + get { return Translate(_originalPartCreationInfo.DisplayName); } + } + + public ICompositionElement Origin + { + get { return _originalPartCreationInfo.Origin; } + } + + public override bool Equals(object obj) + { + GenericSpecializationPartCreationInfo that = obj as GenericSpecializationPartCreationInfo; + if (that == null) + { + return false; + } + + return (_originalPartCreationInfo.Equals(that._originalPartCreationInfo)) && + (_specialization.IsArrayEqual(that._specialization)); + } + + public override int GetHashCode() + { + return _originalPartCreationInfo.GetHashCode(); + } + + public static bool CanSpecialize(IDictionary partMetadata, Type[] specialization) + { + int partArity = partMetadata.GetValue(CompositionConstants.GenericPartArityMetadataName); + + if (partArity != specialization.Length) + { + return false; + } + + object[] genericParameterConstraints = partMetadata.GetValue(CompositionConstants.GenericParameterConstraintsMetadataName); + GenericParameterAttributes[] genericParameterAttributes = partMetadata.GetValue(CompositionConstants.GenericParameterAttributesMetadataName); + + // if no constraints and attributes been specifed, anything can be created + if ((genericParameterConstraints == null) && (genericParameterAttributes == null)) + { + return true; + } + + if ((genericParameterConstraints != null) && (genericParameterConstraints.Length != partArity)) + { + return false; + } + + if ((genericParameterAttributes != null) && (genericParameterAttributes.Length != partArity)) + { + return false; + } + + for (int i = 0; i < partArity; i++) + { + if (!GenericServices.CanSpecialize( + specialization[i], + (genericParameterConstraints[i] as Type[]).CreateTypeSpecializations(specialization), + genericParameterAttributes[i])) + { + return false; + } + } + + return true; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/IReflectionPartCreationInfo.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/IReflectionPartCreationInfo.cs new file mode 100644 index 0000000000..974891f482 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/IReflectionPartCreationInfo.cs @@ -0,0 +1,22 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Reflection; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal interface IReflectionPartCreationInfo : ICompositionElement + { + Type GetPartType(); + Lazy GetLazyPartType(); + ConstructorInfo GetConstructor(); + IDictionary GetMetadata(); + IEnumerable GetExports(); + IEnumerable GetImports(); + bool IsDisposalRequired { get; } + bool IsIdentityComparison { get; } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportType.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportType.cs new file mode 100644 index 0000000000..192dfc8146 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportType.cs @@ -0,0 +1,220 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using Microsoft.Internal; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + // Describes the import type of a Reflection-based import definition + internal class ImportType + { + private static readonly Type LazyOfTType = typeof(Lazy<>); + private static readonly Type LazyOfTMType = typeof(Lazy<,>); + private static readonly Type ExportFactoryOfTType = typeof(ExportFactory<>); + private static readonly Type ExportFactoryOfTMType = typeof(ExportFactory<,>); + + private readonly Type _type; + private readonly bool _isAssignableCollectionType; + private Type _contractType; + private Func _castSingleValue; + private bool _isOpenGeneric = false; + + [ThreadStatic] + internal static Dictionary> _castSingleValueCache; + + private static Dictionary> CastSingleValueCache + { + get + { + return _castSingleValueCache = _castSingleValueCache ?? new Dictionary>(); + } + } + + public ImportType(Type type, ImportCardinality cardinality) + { + Assumes.NotNull(type); + + _type = type; + Type contractType = type; + + if (cardinality == ImportCardinality.ZeroOrMore) + { + _isAssignableCollectionType = IsTypeAssignableCollectionType(type); + contractType = CheckForCollection(type); + } + + // This sets contract type, metadata and the cast function + _isOpenGeneric = type.ContainsGenericParameters; + Initialize(contractType); + } + + public bool IsAssignableCollectionType + { + get { return _isAssignableCollectionType; } + } + + public Type ElementType { get; private set; } + + public Type ActualType + { + get { return _type; } + } + + public bool IsPartCreator { get; private set; } + + public Type ContractType { get { return _contractType; } } + + public Func CastExport + { + get + { + Assumes.IsTrue(!_isOpenGeneric); + return _castSingleValue; + } + } + + public Type MetadataViewType { get; private set; } + + private Type CheckForCollection(Type type) + { + ElementType = CollectionServices.GetEnumerableElementType(type); + if (ElementType != null) + { + return ElementType; + } + return type; + } + + private static bool IsGenericDescendentOf(Type type, Type baseGenericTypeDefinition) + { + if (type == typeof(object) || type == null) + { + return false; + } + + if (type.IsGenericType && type.GetGenericTypeDefinition() == baseGenericTypeDefinition) + { + return true; + } + + return IsGenericDescendentOf(type.BaseType, baseGenericTypeDefinition); + } + + public static bool IsDescendentOf(Type type, Type baseType) + { + Assumes.NotNull(type); + Assumes.NotNull(baseType); + + if (!baseType.IsGenericTypeDefinition) + { + return baseType.IsAssignableFrom(type); + } + + return IsGenericDescendentOf(type, baseType.GetGenericTypeDefinition()); + } + + private void Initialize(Type type) + { + if (!type.IsGenericType) + { + // no cast function, the original type is the contract type + _contractType = type; + return; + } + + Type[] arguments = type.GetGenericArguments(); + Type genericType = type.GetGenericTypeDefinition().UnderlyingSystemType; + + // Look up the cast function + if (!CastSingleValueCache.TryGetValue(type, out _castSingleValue)) + { + if (!TryGetCastFunction(genericType, _isOpenGeneric, arguments, out _castSingleValue)) + { + // in this case, even though the type is generic, it's nothing we have recognized, + // thereforeit's the same as the non-generic case + _contractType = type; + return; + } + + CastSingleValueCache.Add(type, _castSingleValue); + } + + // we have found the cast function, which means, that we have found either Lazy of EF + // in this case the contract is always argument[0] and the metadata view is always argument[1] + IsPartCreator = !IsLazyGenericType(genericType) && (genericType != null); + _contractType = arguments[0]; + if (arguments.Length == 2) + { + MetadataViewType = arguments[1]; + } + } + + private static bool IsLazyGenericType(Type genericType) + { + return (genericType == LazyOfTType) || (genericType == LazyOfTMType); + } + + private static bool TryGetCastFunction(Type genericType, bool isOpenGeneric, Type[] arguments, out Func castFunction) + { + castFunction = null; + + if (genericType == LazyOfTType) + { + if (!isOpenGeneric) + { + castFunction = ExportServices.CreateStronglyTypedLazyFactory(arguments[0].UnderlyingSystemType, null); + } + return true; + } + + if (genericType == LazyOfTMType) + { + if (!isOpenGeneric) + { + castFunction = ExportServices.CreateStronglyTypedLazyFactory(arguments[0].UnderlyingSystemType, arguments[1].UnderlyingSystemType); + } + return true; + } + + if (genericType != null && IsDescendentOf(genericType, ExportFactoryOfTType)) + { + if (arguments.Length == 1) + { + if (!isOpenGeneric) + { + castFunction = new ExportFactoryCreator(genericType).CreateStronglyTypedExportFactoryFactory(arguments[0].UnderlyingSystemType, null); + } + return true; + } + else if (arguments.Length == 2) + { + if (!isOpenGeneric) + { + castFunction = new ExportFactoryCreator(genericType).CreateStronglyTypedExportFactoryFactory(arguments[0].UnderlyingSystemType, arguments[1].UnderlyingSystemType); + } + return true; + } + else + { + throw ExceptionBuilder.ExportFactory_TooManyGenericParameters(genericType.FullName); + } + } + + return false; + } + + private static bool IsTypeAssignableCollectionType(Type type) + { + if (type.IsArray || CollectionServices.IsEnumerableOfT(type)) + { + return true; + } + + return false; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportingItem.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportingItem.cs new file mode 100644 index 0000000000..dda1297c41 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportingItem.cs @@ -0,0 +1,107 @@ +// 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.ComponentModel.Composition.Primitives; +using System.Globalization; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal abstract class ImportingItem + { + private readonly ContractBasedImportDefinition _definition; + private readonly ImportType _importType; + + protected ImportingItem(ContractBasedImportDefinition definition, ImportType importType) + { + Assumes.NotNull(definition); + + _definition = definition; + _importType = importType; + } + + public ContractBasedImportDefinition Definition + { + get { return _definition; } + } + + public ImportType ImportType + { + get { return _importType; } + } + + public object CastExportsToImportType(Export[] exports) + { + if (Definition.Cardinality == ImportCardinality.ZeroOrMore) + { + return CastExportsToCollectionImportType(exports); + } + else + { + return CastExportsToSingleImportType(exports); + } + } + + private object CastExportsToCollectionImportType(Export[] exports) + { + Assumes.NotNull(exports); + + // Element type could be null if the actually import type of the member is not a collection + // This particular case will end up failing when we set the member. + Type elementType = ImportType.ElementType ?? typeof(object); + + Array array = Array.CreateInstance(elementType, exports.Length); + + for (int i = 0; i < array.Length; i++) + { + object value = CastSingleExportToImportType(elementType, exports[i]); + + array.SetValue(value, i); + } + + return array; + } + + private object CastExportsToSingleImportType(Export[] exports) + { + Assumes.NotNull(exports); + Assumes.IsTrue(exports.Length < 2); + + if (exports.Length == 0) + { + return null; + } + + return CastSingleExportToImportType(ImportType.ActualType, exports[0]); + } + + private object CastSingleExportToImportType(Type type, Export export) + { + if (ImportType.CastExport != null) + { + return ImportType.CastExport(export); + } + + return Cast(type, export); + } + + private object Cast(Type type, Export export) + { + object value = export.Value; + + object result; + if (!ContractServices.TryCast(type, value, out result)) + { + throw new ComposablePartException( + String.Format(CultureInfo.CurrentCulture, + SR.ReflectionModel_ImportNotAssignableFromExport, + export.ToElement().DisplayName, + type.FullName), + Definition.ToElement()); + } + + return result; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportingMember.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportingMember.cs new file mode 100644 index 0000000000..7e77e64a91 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportingMember.cs @@ -0,0 +1,254 @@ +// 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.Collections; +using System.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; +using Microsoft.Internal; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal class ImportingMember : ImportingItem + { + private readonly ReflectionWritableMember _member; + + public ImportingMember(ContractBasedImportDefinition definition, ReflectionWritableMember member, ImportType importType) + : base(definition, importType) + { + Assumes.NotNull(definition, member); + + _member = member; + } + + public void SetExportedValue(object instance, object value) + { + if (RequiresCollectionNormalization()) + { + SetCollectionMemberValue(instance, (IEnumerable)value); + } + else + { + SetSingleMemberValue(instance, value); + } + } + + private bool RequiresCollectionNormalization() + { + if (Definition.Cardinality != ImportCardinality.ZeroOrMore) + { // If we're not looking at a collection import, then don't + // 'normalize' the collection. + + return false; + } + + if (_member.CanWrite && ImportType.IsAssignableCollectionType) + { // If we can simply replace the entire value of the property/field, then + // we don't need to 'normalize' the collection. + + return false; + } + + return true; + } + + private void SetSingleMemberValue(object instance, object value) + { + EnsureWritable(); + + try + { + _member.SetValue(instance, value); + } + catch (TargetInvocationException exception) + { // Member threw an exception. Avoid letting this + // leak out as a 'raw' unhandled exception, instead, + // we'll add some context and rethrow. + throw new ComposablePartException( + String.Format(CultureInfo.CurrentCulture, + SR.ReflectionModel_ImportThrewException, + _member.GetDisplayName()), + Definition.ToElement(), + exception.InnerException); + } + catch (TargetParameterCountException exception) + { + // Exception was a TargetParameterCountException this occurs when we try to set an Indexer that has an Import + // this is not supported in MEF currently. Ideally we would validate against it, however, we already shipped + // so we will turn it into a ComposablePartException instead, that they should already be prepared for + throw new ComposablePartException( + String.Format(CultureInfo.CurrentCulture, + SR.ImportNotValidOnIndexers, + _member.GetDisplayName()), + Definition.ToElement(), + exception.InnerException); + } + } + + private void EnsureWritable() + { + if (!_member.CanWrite) + { // Property does not have a setter, or + // field is marked as read-only. + + throw new ComposablePartException( + String.Format(CultureInfo.CurrentCulture, + SR.ReflectionModel_ImportNotWritable, + _member.GetDisplayName()), + Definition.ToElement()); + } + } + + private void SetCollectionMemberValue(object instance, IEnumerable values) + { + Assumes.NotNull(values); + + ICollection collection = null; + Type itemType = CollectionServices.GetCollectionElementType(ImportType.ActualType); + if (itemType != null) + { + collection = GetNormalizedCollection(itemType, instance); + } + + EnsureCollectionIsWritable(collection); + PopulateCollection(collection, values); + } + + private ICollection GetNormalizedCollection(Type itemType, object instance) + { + Assumes.NotNull(itemType); + + object collectionObject = null; + + if (_member.CanRead) + { + try + { + collectionObject = _member.GetValue(instance); + } + catch (TargetInvocationException exception) + { + throw new ComposablePartException( + String.Format(CultureInfo.CurrentCulture, + SR.ReflectionModel_ImportCollectionGetThrewException, + _member.GetDisplayName()), + Definition.ToElement(), + exception.InnerException); + } + } + + if (collectionObject == null) + { + ConstructorInfo constructor = ImportType.ActualType.GetConstructor(Type.EmptyTypes); + + // If it contains a default public constructor create a new instance. + if (constructor != null) + { + try + { + collectionObject = constructor.SafeInvoke(); + } + catch (TargetInvocationException exception) + { + throw new ComposablePartException( + String.Format(CultureInfo.CurrentCulture, + SR.ReflectionModel_ImportCollectionConstructionThrewException, + _member.GetDisplayName(), + ImportType.ActualType.FullName), + Definition.ToElement(), + exception.InnerException); + } + + SetSingleMemberValue(instance, collectionObject); + } + } + + if (collectionObject == null) + { + throw new ComposablePartException( + String.Format(CultureInfo.CurrentCulture, + SR.ReflectionModel_ImportCollectionNull, + _member.GetDisplayName()), + Definition.ToElement()); + } + + return CollectionServices.GetCollectionWrapper(itemType, collectionObject); + } + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private void EnsureCollectionIsWritable(ICollection collection) + { + bool isReadOnly = true; + + try + { + if (collection != null) + { + isReadOnly = collection.IsReadOnly; + } + } + catch (Exception exception) + { + throw new ComposablePartException( + String.Format(CultureInfo.CurrentCulture, + SR.ReflectionModel_ImportCollectionIsReadOnlyThrewException, + _member.GetDisplayName(), + collection.GetType().FullName), + Definition.ToElement(), + exception); + } + + if (isReadOnly) + { + throw new ComposablePartException( + String.Format(CultureInfo.CurrentCulture, + SR.ReflectionModel_ImportCollectionNotWritable, + _member.GetDisplayName()), + Definition.ToElement()); + } + } + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private void PopulateCollection(ICollection collection, IEnumerable values) + { + Assumes.NotNull(collection, values); + + try + { + collection.Clear(); + } + catch (Exception exception) + { + throw new ComposablePartException( + String.Format(CultureInfo.CurrentCulture, + SR.ReflectionModel_ImportCollectionClearThrewException, + _member.GetDisplayName(), + collection.GetType().FullName), + Definition.ToElement(), + exception); + } + + foreach (object value in values) + { + try + { + collection.Add(value); + } + catch (Exception exception) + { + throw new ComposablePartException( + String.Format(CultureInfo.CurrentCulture, + SR.ReflectionModel_ImportCollectionAddThrewException, + _member.GetDisplayName(), + collection.GetType().FullName), + Definition.ToElement(), + exception); + } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportingParameter.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportingParameter.cs new file mode 100644 index 0000000000..f7f9a6f405 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportingParameter.cs @@ -0,0 +1,16 @@ +// 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.ComponentModel.Composition.Primitives; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal class ImportingParameter : ImportingItem + { + public ImportingParameter(ContractBasedImportDefinition definition, ImportType importType) + : base(definition, importType) + { + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/LazyMemberInfo.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/LazyMemberInfo.cs new file mode 100644 index 0000000000..aa9d73cbef --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/LazyMemberInfo.cs @@ -0,0 +1,204 @@ +// 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.Globalization; +using System.Linq; +using System.Reflection; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + public struct LazyMemberInfo + { + private readonly MemberTypes _memberType; + private MemberInfo[] _accessors; + private readonly Func _accessorsCreator; + + public LazyMemberInfo(MemberInfo member) + { + Requires.NotNull(member, nameof(member)); + EnsureSupportedMemberType(member.MemberType, "member"); + + _accessorsCreator = null; + _memberType = member.MemberType; + + switch(_memberType) + { + case MemberTypes.Property: + PropertyInfo property = (PropertyInfo)member; + Assumes.NotNull(property); + _accessors = new MemberInfo[] { property.GetGetMethod(true), property.GetSetMethod(true) }; + break; + case MemberTypes.Event: + EventInfo event_ = (EventInfo)member; + _accessors = new MemberInfo[] { event_.GetRaiseMethod(true), event_.GetAddMethod(true), event_.GetRemoveMethod(true) }; + break; + default: + _accessors = new MemberInfo[] { member }; + break; + } + } + + public LazyMemberInfo(MemberTypes memberType, params MemberInfo[] accessors) + { + EnsureSupportedMemberType(memberType, "memberType"); + Requires.NotNull(accessors, nameof(accessors)); + + string errorMessage; + if (!LazyMemberInfo.AreAccessorsValid(memberType, accessors, out errorMessage)) + { + throw new ArgumentException(errorMessage, "accessors"); + } + + _memberType = memberType; + _accessors = accessors; + _accessorsCreator = null; + } + + public LazyMemberInfo(MemberTypes memberType, Func accessorsCreator) + { + EnsureSupportedMemberType(memberType, "memberType"); + Requires.NotNull(accessorsCreator, nameof(accessorsCreator)); + + _memberType = memberType; + _accessors = null; + _accessorsCreator = accessorsCreator; + } + + public MemberTypes MemberType + { + get { return _memberType; } + } + + public MemberInfo[] GetAccessors() + { + if ((_accessors == null) && (_accessorsCreator != null)) + { + MemberInfo[] accessors = _accessorsCreator.Invoke(); + + string errorMessage; + if (!LazyMemberInfo.AreAccessorsValid(MemberType, accessors, out errorMessage)) + { + throw new InvalidOperationException(errorMessage); + } + + _accessors = accessors; + } + + return _accessors; + } + + public override int GetHashCode() + { + if (_accessorsCreator != null) + { + return MemberType.GetHashCode() ^ _accessorsCreator.GetHashCode(); + } + else + { + Assumes.NotNull(_accessors); + Assumes.NotNull(_accessors[0]); + return MemberType.GetHashCode() ^ _accessors[0].GetHashCode(); + } + } + + public override bool Equals(object obj) + { + LazyMemberInfo that = (LazyMemberInfo)obj; + + // Difefrent member types mean different members + if (_memberType != that._memberType) + { + return false; + } + + // if any of the lazy memebers create accessors in a delay-loaded fashion, we simply compare the creators + if ((_accessorsCreator != null) || (that._accessorsCreator != null)) + { + return object.Equals(_accessorsCreator, that._accessorsCreator); + } + + // we are dealing with explicitly passed accessors in both cases + Assumes.NotNull(_accessors); + Assumes.NotNull(that._accessors); + return _accessors.SequenceEqual(that._accessors); + } + + public static bool operator ==(LazyMemberInfo left, LazyMemberInfo right) + { + return left.Equals(right); + } + + public static bool operator !=(LazyMemberInfo left, LazyMemberInfo right) + { + return !left.Equals(right); + } + private static void EnsureSupportedMemberType(MemberTypes memberType, string argument) + { + MemberTypes supportedTypes = MemberTypes.TypeInfo | MemberTypes.NestedType | MemberTypes.Constructor | MemberTypes.Field | MemberTypes.Method | MemberTypes.Property | MemberTypes.Event; + Requires.IsInMembertypeSet(memberType, argument, supportedTypes); + } + + private static bool AreAccessorsValid(MemberTypes memberType, MemberInfo[] accessors, out string errorMessage) + { + errorMessage = string.Empty; + if (accessors == null) + { + errorMessage = SR.LazyMemberInfo_AccessorsNull; + return false; + } + + if (accessors.All(accessor => accessor == null)) + { + errorMessage = SR.LazyMemberInfo_NoAccessors; + return false; + } + + switch (memberType) + { + case MemberTypes.Property: + if (accessors.Length != 2) + { + errorMessage = SR.LazyMemberInfo_InvalidPropertyAccessors_Cardinality; + return false; + } + + if (accessors.Where(accessor => (accessor != null) && (accessor.MemberType != MemberTypes.Method)).Any()) + { + errorMessage = SR.LazyMemberinfo_InvalidPropertyAccessors_AccessorType; + return false; + } + + break; + + case MemberTypes.Event: + if (accessors.Length != 3) + { + errorMessage = SR.LazyMemberInfo_InvalidEventAccessors_Cardinality; + return false; + } + + if (accessors.Where(accessor => (accessor != null) && (accessor.MemberType != MemberTypes.Method)).Any()) + { + errorMessage = SR.LazyMemberinfo_InvalidEventAccessors_AccessorType; + return false; + } + + break; + + default: + if ( + (accessors.Length != 1) || + ((accessors.Length == 1) && (accessors[0].MemberType != memberType))) + { + errorMessage = string.Format(CultureInfo.CurrentCulture, SR.LazyMemberInfo_InvalidAccessorOnSimpleMember, memberType); + return false; + } + + break; + } + return true; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/PartCreatorExportDefinition.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/PartCreatorExportDefinition.cs new file mode 100644 index 0000000000..8c6fd35b18 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/PartCreatorExportDefinition.cs @@ -0,0 +1,62 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal class PartCreatorExportDefinition : ExportDefinition + { + private readonly ExportDefinition _productDefinition; + private IDictionary _metadata; + + public PartCreatorExportDefinition(ExportDefinition productDefinition) + : base() + { + _productDefinition = productDefinition; + } + + public override string ContractName + { + get + { + return CompositionConstants.PartCreatorContractName; + } + } + + public override IDictionary Metadata + { + get + { + if (_metadata == null) + { + var metadata = new Dictionary(_productDefinition.Metadata); + metadata[CompositionConstants.ExportTypeIdentityMetadataName] = CompositionConstants.PartCreatorTypeIdentity; + metadata[CompositionConstants.ProductDefinitionMetadataName] = _productDefinition; + + _metadata = metadata.AsReadOnly(); + } + return _metadata; + } + } + + internal static bool IsProductConstraintSatisfiedBy(ImportDefinition productImportDefinition, ExportDefinition exportDefinition) + { + object productValue = null; + if (exportDefinition.Metadata.TryGetValue(CompositionConstants.ProductDefinitionMetadataName, out productValue)) + { + ExportDefinition productDefinition = productValue as ExportDefinition; + + if (productDefinition != null) + { + return productImportDefinition.IsConstraintSatisfiedBy(productDefinition); + } + } + + return false; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/PartCreatorMemberImportDefinition.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/PartCreatorMemberImportDefinition.cs new file mode 100644 index 0000000000..b67295fd45 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/PartCreatorMemberImportDefinition.cs @@ -0,0 +1,56 @@ +// 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.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq.Expressions; +using System.Text; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal class PartCreatorMemberImportDefinition : ReflectionMemberImportDefinition, IPartCreatorImportDefinition + { + private readonly ContractBasedImportDefinition _productImportDefinition; + + public PartCreatorMemberImportDefinition( + LazyMemberInfo importingLazyMember, + ICompositionElement origin, + ContractBasedImportDefinition productImportDefinition) + : base(importingLazyMember, CompositionConstants.PartCreatorContractName, CompositionConstants.PartCreatorTypeIdentity, + productImportDefinition.RequiredMetadata, productImportDefinition.Cardinality, productImportDefinition.IsRecomposable, false, productImportDefinition.RequiredCreationPolicy, MetadataServices.EmptyMetadata, origin) + { + Assumes.NotNull(productImportDefinition); + _productImportDefinition = productImportDefinition; + } + + public ContractBasedImportDefinition ProductImportDefinition { get { return _productImportDefinition; } } + public override bool IsConstraintSatisfiedBy(ExportDefinition exportDefinition) + { + if (!base.IsConstraintSatisfiedBy(exportDefinition)) + { + return false; + } + + return PartCreatorExportDefinition.IsProductConstraintSatisfiedBy(_productImportDefinition, exportDefinition); + } + + public override Expression> Constraint + { + get + { + return ConstraintServices.CreatePartCreatorConstraint(base.Constraint, _productImportDefinition); + } + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + + sb.Append(string.Format("\n\tExportFactory of: {0}", ProductImportDefinition.ToString())); + + return sb.ToString(); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/PartCreatorParameterImportDefinition.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/PartCreatorParameterImportDefinition.cs new file mode 100644 index 0000000000..b9efa11ce9 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/PartCreatorParameterImportDefinition.cs @@ -0,0 +1,57 @@ +// 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.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal class PartCreatorParameterImportDefinition : ReflectionParameterImportDefinition, IPartCreatorImportDefinition + { + private readonly ContractBasedImportDefinition _productImportDefinition; + + public PartCreatorParameterImportDefinition( + Lazy importingLazyParameter, + ICompositionElement origin, + ContractBasedImportDefinition productImportDefinition) + : base(importingLazyParameter, CompositionConstants.PartCreatorContractName, CompositionConstants.PartCreatorTypeIdentity, + productImportDefinition.RequiredMetadata, productImportDefinition.Cardinality, CreationPolicy.Any, MetadataServices.EmptyMetadata, origin) + { + Assumes.NotNull(productImportDefinition); + _productImportDefinition = productImportDefinition; + } + + public ContractBasedImportDefinition ProductImportDefinition { get { return _productImportDefinition; } } + public override bool IsConstraintSatisfiedBy(ExportDefinition exportDefinition) + { + if (!base.IsConstraintSatisfiedBy(exportDefinition)) + { + return false; + } + return PartCreatorExportDefinition.IsProductConstraintSatisfiedBy(_productImportDefinition, exportDefinition); + } + + public override Expression> Constraint + { + get + { + return ConstraintServices.CreatePartCreatorConstraint(base.Constraint, _productImportDefinition); + } + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + + sb.Append(string.Format("\n\tExportFactory of: {0}", ProductImportDefinition.ToString())); + + return sb.ToString(); + } + + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionComposablePart.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionComposablePart.cs new file mode 100644 index 0000000000..44fd244064 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionComposablePart.cs @@ -0,0 +1,627 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Reflection; +using Microsoft.Internal; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal class ReflectionComposablePart : ComposablePart, ICompositionElement + { + private readonly ReflectionComposablePartDefinition _definition; + private volatile Dictionary _importValues = null; + private volatile Dictionary _importsCache = null; + private volatile Dictionary _exportsCache = null; + private volatile bool _invokeImportsSatisfied = true; + private bool _initialCompositionComplete = false; + private volatile object _cachedInstance; + private object _lock = new object(); + + public ReflectionComposablePart(ReflectionComposablePartDefinition definition) + { + Requires.NotNull(definition, nameof(definition)); + + _definition = definition; + } + + public ReflectionComposablePart(ReflectionComposablePartDefinition definition, object attributedPart) + { + Requires.NotNull(definition, nameof(definition)); + Requires.NotNull(attributedPart, nameof(attributedPart)); + + _definition = definition; + + if (attributedPart is ValueType) + { + throw new ArgumentException(SR.ArgumentValueType, "attributedPart"); + } + _cachedInstance = attributedPart; + } + + protected virtual void EnsureRunning() + { + } + protected void RequiresRunning() + { + EnsureRunning(); + } + + protected virtual void ReleaseInstanceIfNecessary(object instance) + { + } + + private Dictionary ImportValues + { + get + { + var value = _importValues; + if(value == null) + { + lock(_lock) + { + value = _importValues; + if(value == null) + { + value = new Dictionary(); + _importValues = value; + } + } + } + return value; + } + } + + private Dictionary ImportsCache + { + get + { + var value = _importsCache; + if(value == null) + { + lock(_lock) + { + if(value == null) + { + value = new Dictionary(); + _importsCache = value; + } + } + } + return value; + } + } + + protected object CachedInstance + { + get + { + lock (_lock) + { + return _cachedInstance; + } + } + } + + public ReflectionComposablePartDefinition Definition + { + get + { + RequiresRunning(); + return _definition; + } + } + + public override IDictionary Metadata + { + get + { + RequiresRunning(); + return Definition.Metadata; + } + } + + public sealed override IEnumerable ImportDefinitions + { + get + { + RequiresRunning(); + return Definition.ImportDefinitions; + } + } + + public sealed override IEnumerable ExportDefinitions + { + get + { + RequiresRunning(); + return Definition.ExportDefinitions; + } + } + + string ICompositionElement.DisplayName + { + get { return GetDisplayName(); } + } + + ICompositionElement ICompositionElement.Origin + { + get { return Definition; } + } + + // This is the ONLY method which is not executed under the ImportEngine composition lock. + // We need to protect all state that is accesses + public override object GetExportedValue(ExportDefinition definition) + { + RequiresRunning(); + // given the implementation of the ImportEngine, this iwll be called under a lock if the part is still being composed + // This is only called outside of the lock when the part is fully composed + // based on that we only protect: + // _exportsCache - and thus all calls to GetExportingMemberFromDefinition + // access to _importValues + // access to _initialCompositionComplete + // access to _instance + Requires.NotNull(definition, nameof(definition)); + + ExportingMember member = null; + lock (_lock) + { + member = GetExportingMemberFromDefinition(definition); + if (member == null) + { + throw ExceptionBuilder.CreateExportDefinitionNotOnThisComposablePart("definition"); + } + EnsureGettable(); + } + + return GetExportedValue(member); + } + + public override void SetImport(ImportDefinition definition, IEnumerable exports) + { + RequiresRunning(); + Requires.NotNull(definition, nameof(definition)); + Requires.NotNull(exports, nameof(exports));; + + ImportingItem item = GetImportingItemFromDefinition(definition); + if (item == null) + { + throw ExceptionBuilder.CreateImportDefinitionNotOnThisComposablePart("definition"); + } + + EnsureSettable(definition); + + // Avoid walking over exports many times + Export[] exportsAsArray = exports.AsArray(); + EnsureCardinality(definition, exportsAsArray); + + SetImport(item, exportsAsArray); + } + + public override void Activate() + { + RequiresRunning(); + + SetNonPrerequisiteImports(); + + // Whenever we are composed/recomposed notify the instance + NotifyImportSatisfied(); + lock (_lock) + { + _initialCompositionComplete = true; + _importValues = null; + _importsCache = null; + } + } + + public override string ToString() + { + return GetDisplayName(); + } + + private object GetExportedValue(ExportingMember member) + { + object instance = null; + if (member.RequiresInstance) + { // Only activate the instance if we actually need to + + instance = GetInstanceActivatingIfNeeded(); + } + + return member.GetExportedValue(instance, _lock); + } + + private void SetImport(ImportingItem item, Export[] exports) + { + object value = item.CastExportsToImportType(exports); + + lock (_lock) + { + _invokeImportsSatisfied = true; + ImportValues[item.Definition] = value; + } + } + + private object GetInstanceActivatingIfNeeded() + { + var cachedInstance = _cachedInstance; + + if (cachedInstance != null) + { + return cachedInstance; + } + else + { + ConstructorInfo constructor = null; + object[] arguments = null; + // determine whether activation is required, and collect necessary information for activation + // we need to do that under a lock + lock (_lock) + { + if (!RequiresActivation()) + { + return null; + } + + constructor = Definition.GetConstructor(); + if (constructor == null) + { + throw new ComposablePartException( + String.Format(CultureInfo.CurrentCulture, + SR.ReflectionModel_PartConstructorMissing, + Definition.GetPartType().FullName), + Definition.ToElement()); + } + arguments = GetConstructorArguments(); + } + + // create instance outside of the lock + object createdInstance = CreateInstance(constructor, arguments); + + SetPrerequisiteImports(); + + // set the created instance + if (_cachedInstance == null) + { + lock (_lock) + { + if (_cachedInstance == null) + { + _cachedInstance = createdInstance; + createdInstance = null; + } + } + } + + // if the instance has been already set + if (createdInstance == null) + { + ReleaseInstanceIfNecessary(createdInstance); + } + } + + return _cachedInstance; + } + + private object[] GetConstructorArguments() + { + ReflectionParameterImportDefinition[] parameterImports = ImportDefinitions.OfType().ToArray(); + object[] arguments = new object[parameterImports.Length]; + + UseImportedValues( + parameterImports, + (import, definition, value) => + { + if (definition.Cardinality == ImportCardinality.ZeroOrMore && !import.ImportType.IsAssignableCollectionType) + { + throw new ComposablePartException( + String.Format(CultureInfo.CurrentCulture, + SR.ReflectionModel_ImportManyOnParameterCanOnlyBeAssigned, + Definition.GetPartType().FullName, + definition.ImportingLazyParameter.Value.Name), + Definition.ToElement()); + } + + arguments[definition.ImportingLazyParameter.Value.Position] = value; + }, + true); + + return arguments; + } + + // alwayc called under a lock + private bool RequiresActivation() + { + // If we have any imports then we need activation + // (static imports are not supported) + if (ImportDefinitions.Any()) + { + return true; + } + + // If we have any instance exports, then we also + // need activation. + return ExportDefinitions.Any(definition => + { + ExportingMember member = GetExportingMemberFromDefinition(definition); + + return member.RequiresInstance; + }); + } + + // this is called under a lock + private void EnsureGettable() + { + // If we're already composed then we know that + // all pre-req imports have been satisfied + if (_initialCompositionComplete) + { + return; + } + + // Make sure all pre-req imports have been set + foreach (ImportDefinition definition in ImportDefinitions.Where(definition => definition.IsPrerequisite)) + { + if (_importValues == null || !ImportValues.ContainsKey(definition)) + { + throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, + SR.InvalidOperation_GetExportedValueBeforePrereqImportSet, + definition.ToElement().DisplayName)); + } + } + } + + private void EnsureSettable(ImportDefinition definition) + { + lock (_lock) + { + if (_initialCompositionComplete && !definition.IsRecomposable) + { + throw new InvalidOperationException(SR.InvalidOperation_DefinitionCannotBeRecomposed); + } + } + } + + private static void EnsureCardinality(ImportDefinition definition, Export[] exports) + { + Requires.NullOrNotNullElements(exports, "exports"); + + ExportCardinalityCheckResult result = ExportServices.CheckCardinality(definition, exports); + + switch (result) + { + case ExportCardinalityCheckResult.NoExports: + throw new ArgumentException(SR.Argument_ExportsEmpty, "exports"); + + case ExportCardinalityCheckResult.TooManyExports: + throw new ArgumentException(SR.Argument_ExportsTooMany, "exports"); + + default: + Assumes.IsTrue(result == ExportCardinalityCheckResult.Match); + break; + } + } + + private object CreateInstance(ConstructorInfo constructor, object[] arguments) + { + Exception exception = null; + object instance = null; + + try + { + instance = constructor.SafeInvoke(arguments); + } + catch (TypeInitializationException ex) + { + exception = ex; + } + catch (TargetInvocationException ex) + { + exception = ex.InnerException; + } + + if (exception != null) + { + throw new ComposablePartException( + String.Format(CultureInfo.CurrentCulture, + SR.ReflectionModel_PartConstructorThrewException, + Definition.GetPartType().FullName), + Definition.ToElement(), + exception); + } + + return instance; + } + + private void SetNonPrerequisiteImports() + { + IEnumerable members = ImportDefinitions.Where(import => !import.IsPrerequisite); + + // NOTE: Dev10 484204 The validation is turned off for post imports because of it broke declarative composition + UseImportedValues(members, SetExportedValueForImport, false); + } + + private void SetPrerequisiteImports() + { + IEnumerable members = ImportDefinitions.Where(import => import.IsPrerequisite); + + // NOTE: Dev10 484204 The validation is turned off for post imports because of it broke declarative composition + UseImportedValues(members, SetExportedValueForImport, false); + } + + private void SetExportedValueForImport(ImportingItem import, ImportDefinition definition, object value) + { + ImportingMember importMember = (ImportingMember)import; + + object instance = GetInstanceActivatingIfNeeded(); + + importMember.SetExportedValue(instance, value); + } + + private void UseImportedValues(IEnumerable definitions, Action useImportValue, bool errorIfMissing) + where TImportDefinition : ImportDefinition + { + var result = CompositionResult.SucceededResult; + + foreach (var definition in definitions) + { + ImportingItem import = GetImportingItemFromDefinition(definition); + + object value; + if (!TryGetImportValue(definition, out value)) + { + if (!errorIfMissing) + { + continue; + } + + if (definition.Cardinality == ImportCardinality.ExactlyOne) + { + var error = CompositionError.Create( + CompositionErrorId.ImportNotSetOnPart, + SR.ImportNotSetOnPart, + Definition.GetPartType().FullName, + definition.ToString()); + result = result.MergeError(error); + continue; + } + else + { + value = import.CastExportsToImportType(new Export[0]); + } + } + + useImportValue(import, definition, value); + } + + result.ThrowOnErrors(); + } + + private bool TryGetImportValue(ImportDefinition definition, out object value) + { + lock (_lock) + { + if (_importValues != null && ImportValues.TryGetValue(definition, out value)) + { + ImportValues.Remove(definition); + return true; + } + } + + value = null; + return false; + } + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private void NotifyImportSatisfied() + { + if (_invokeImportsSatisfied) + { + IPartImportsSatisfiedNotification notify = GetInstanceActivatingIfNeeded() as IPartImportsSatisfiedNotification; + + lock (_lock) + { + if (!_invokeImportsSatisfied) + { + //Already notified on another thread + return; + } + _invokeImportsSatisfied = false; + } + + if (notify != null) + { + try + { + notify.OnImportsSatisfied(); + } + catch (Exception exception) + { + throw new ComposablePartException( + String.Format(CultureInfo.CurrentCulture, + SR.ReflectionModel_PartOnImportsSatisfiedThrewException, + Definition.GetPartType().FullName), + Definition.ToElement(), + exception); + } + } + } + } + + // this is always called under a lock + private ExportingMember GetExportingMemberFromDefinition(ExportDefinition definition) + { + ExportingMember result; + ReflectionMemberExportDefinition reflectionExport = definition as ReflectionMemberExportDefinition; + if (reflectionExport == null) + { + return null; + } + + int exportIndex = reflectionExport.GetIndex(); + if(_exportsCache == null) + { + _exportsCache = new Dictionary(); + } + if (!_exportsCache.TryGetValue(exportIndex, out result)) + { + result = GetExportingMember(definition); + if (result != null) + { + _exportsCache[exportIndex] = result; + } + } + + return result; + } + + private ImportingItem GetImportingItemFromDefinition(ImportDefinition definition) + { + ImportingItem result; + if (!ImportsCache.TryGetValue(definition, out result)) + { + result = GetImportingItem(definition); + if (result != null) + { + ImportsCache[definition] = result; + } + } + + return result; + } + + private static ImportingItem GetImportingItem(ImportDefinition definition) + { + ReflectionImportDefinition reflectionDefinition = definition as ReflectionImportDefinition; + if (reflectionDefinition != null) + { + return reflectionDefinition.ToImportingItem(); + } + // Don't recognize it + return null; + } + + private static ExportingMember GetExportingMember(ExportDefinition definition) + { + ReflectionMemberExportDefinition exportDefinition = definition as ReflectionMemberExportDefinition; + if (exportDefinition != null) + { + return exportDefinition.ToExportingMember(); + } + + // Don't recognize it + return null; + } + + private string GetDisplayName() + { + return _definition.GetPartType().GetDisplayName(); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionComposablePartDefinition.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionComposablePartDefinition.cs new file mode 100644 index 0000000000..cddb6dbfb8 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionComposablePartDefinition.cs @@ -0,0 +1,381 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Reflection; +using Microsoft.Internal; +using Microsoft.Internal.Collections; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal class ReflectionComposablePartDefinition : ComposablePartDefinition, ICompositionElement + { + private readonly IReflectionPartCreationInfo _creationInfo; + + private volatile ImportDefinition[] _imports; + private volatile ExportDefinition[] _exports; + private volatile IDictionary _metadata; + private volatile ConstructorInfo _constructor; + private object _lock = new object(); + + public ReflectionComposablePartDefinition(IReflectionPartCreationInfo creationInfo) + { + Assumes.NotNull(creationInfo); + _creationInfo = creationInfo; + } + + public Type GetPartType() + { + return _creationInfo.GetPartType(); + } + + public Lazy GetLazyPartType() + { + return _creationInfo.GetLazyPartType(); + } + + public ConstructorInfo GetConstructor() + { + if (_constructor == null) + { + ConstructorInfo constructor = _creationInfo.GetConstructor(); + lock (_lock) + { + if (_constructor == null) + { + _constructor = constructor; + } + } + } + + return _constructor; + } + + private ExportDefinition[] ExportDefinitionsInternal + { + get + { + if (_exports == null) + { + ExportDefinition[] exports = _creationInfo.GetExports().ToArray(); + lock (_lock) + { + if (_exports == null) + { + _exports = exports; + } + } + } + return _exports; + } + } + + public override IEnumerable ExportDefinitions + { + get + { + return ExportDefinitionsInternal; + } + } + + public override IEnumerable ImportDefinitions + { + get + { + if (_imports == null) + { + ImportDefinition[] imports = _creationInfo.GetImports().ToArray(); + lock (_lock) + { + if (_imports == null) + { + _imports = imports; + } + } + } + return _imports; + } + } + + public override IDictionary Metadata + { + get + { + if (_metadata == null) + { + IDictionary metadata = _creationInfo.GetMetadata().AsReadOnly(); + lock (_lock) + { + if (_metadata == null) + { + _metadata = metadata; + } + } + } + return _metadata; + } + } + + internal bool IsDisposalRequired + { + get + { + return _creationInfo.IsDisposalRequired; + } + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] + public override ComposablePart CreatePart() + { + if (IsDisposalRequired) + { + return new DisposableReflectionComposablePart(this); + } + else + { + return new ReflectionComposablePart(this); + } + } + + internal override ComposablePartDefinition GetGenericPartDefinition() + { + GenericSpecializationPartCreationInfo genericCreationInfo = _creationInfo as GenericSpecializationPartCreationInfo; + if (genericCreationInfo != null) + { + return genericCreationInfo.OriginalPart; + } + + return null; + } + + internal override bool TryGetExports(ImportDefinition definition, out Tuple singleMatch, out IEnumerable> multipleMatches) + { + if (this.IsGeneric()) + { + singleMatch = null; + multipleMatches = null; + + List> exports = null; + + var genericParameters = (definition.Metadata.Count > 0) ? definition.Metadata.GetValue>(CompositionConstants.GenericParametersMetadataName) : null; + // if and only if generic parameters have been supplied can we attempt to "close" the generic + if (genericParameters != null) + { + Type[] genericTypeParameters = null; + // we only understand types + if (TryGetGenericTypeParameters(genericParameters, out genericTypeParameters)) + { + HashSet candidates = null; + ComposablePartDefinition candidatePart = null; + ComposablePartDefinition previousPart = null; + + // go through all orders of generic parameters that part exports allows + foreach (Type[] candidateParameters in GetCandidateParameters(genericTypeParameters)) + { + if (TryMakeGenericPartDefinition(candidateParameters, out candidatePart)) + { + bool alreadyProcessed = false; + if (candidates == null) + { + if (previousPart != null) + { + if (candidatePart.Equals(previousPart)) + { + alreadyProcessed = true; + } + else + { + candidates = new HashSet(); + candidates.Add(previousPart); + candidates.Add(candidatePart); + } + } + else + { + previousPart = candidatePart; + } + } + else + { + if (candidates.Contains(candidatePart)) + { + alreadyProcessed = true; + } + else + { + candidates.Add(candidatePart); + } + } + if (!alreadyProcessed) + { + Tuple candidateSingleMatch; + IEnumerable> candidateMultipleMatches; + if (candidatePart.TryGetExports(definition, out candidateSingleMatch, out candidateMultipleMatches)) + { + exports = exports.FastAppendToListAllowNulls(candidateSingleMatch, candidateMultipleMatches); + } + } + } + } + } + } + if (exports != null) + { + multipleMatches = exports; + return true; + } + else + { + return false; + } + } + else + { + return TryGetNonGenericExports(definition, out singleMatch, out multipleMatches); + } + } + + // Optimised for local as array case + private bool TryGetNonGenericExports(ImportDefinition definition, out Tuple singleMatch, out IEnumerable> multipleMatches) + { + singleMatch = null; + multipleMatches = null; + + List> multipleExports = null; + Tuple singleExport = null; + bool matchesFound = false; + + foreach (var export in ExportDefinitionsInternal) + { + if (definition.IsConstraintSatisfiedBy(export)) + { + matchesFound = true; + if (singleExport == null) + { + singleExport = new Tuple(this, export); + } + else + { + if (multipleExports == null) + { + multipleExports = new List>(); + multipleExports.Add(singleExport); + } + multipleExports.Add(new Tuple(this, export)); + } + } + } + + if (!matchesFound) + { + return false; + } + + if (multipleExports != null) + { + multipleMatches = multipleExports; + } + else + { + singleMatch = singleExport; + } + return true; + } + + private IEnumerable GetCandidateParameters(Type[] genericParameters) + { + // we iterate over all exports and find only generic ones. Assuming the arity matches, we reorder the original parameters + foreach (ExportDefinition export in ExportDefinitionsInternal) + { + var genericParametersOrder = export.Metadata.GetValue(CompositionConstants.GenericExportParametersOrderMetadataName); + if ((genericParametersOrder != null) && (genericParametersOrder.Length == genericParameters.Length)) + { + yield return GenericServices.Reorder(genericParameters, genericParametersOrder); + } + } + + } + + private static bool TryGetGenericTypeParameters(IEnumerable genericParameters, out Type[] genericTypeParameters) + { + genericTypeParameters = genericParameters as Type[]; + if (genericTypeParameters == null) + { + object[] genericParametersAsArray = genericParameters.AsArray(); + genericTypeParameters = new Type[genericParametersAsArray.Length]; + for (int i = 0; i < genericParametersAsArray.Length; i++) + { + genericTypeParameters[i] = genericParametersAsArray[i] as Type; + if (genericTypeParameters[i] == null) + { + return false; + } + } + } + return true; + } + + internal bool TryMakeGenericPartDefinition(Type[] genericTypeParameters, out ComposablePartDefinition genericPartDefinition) + { + genericPartDefinition = null; + + if (!GenericSpecializationPartCreationInfo.CanSpecialize(Metadata, genericTypeParameters)) + { + return false; + } + + genericPartDefinition = new ReflectionComposablePartDefinition(new GenericSpecializationPartCreationInfo(_creationInfo, this, genericTypeParameters)); + return true; + } + + string ICompositionElement.DisplayName + { + get { return _creationInfo.DisplayName; } + } + + ICompositionElement ICompositionElement.Origin + { + get { return _creationInfo.Origin; } + } + + public override string ToString() + { + return _creationInfo.DisplayName; + } + + public override bool Equals(object obj) + { + if (_creationInfo.IsIdentityComparison) + { + return object.ReferenceEquals(this, obj); + } + else + { + ReflectionComposablePartDefinition that = obj as ReflectionComposablePartDefinition; + if (that == null) + { + return false; + } + + return _creationInfo.Equals(that._creationInfo); + } + } + + public override int GetHashCode() + { + if (_creationInfo.IsIdentityComparison) + { + return base.GetHashCode(); + } + else + { + return _creationInfo.GetHashCode(); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionExtensions.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionExtensions.cs new file mode 100644 index 0000000000..b6f4a7bbb4 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionExtensions.cs @@ -0,0 +1,113 @@ +// 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.Reflection; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal static class ReflectionExtensions + { + public static ReflectionMember ToReflectionMember(this LazyMemberInfo lazyMember) + { + MemberInfo[] accessors = lazyMember.GetAccessors(); + MemberTypes memberType = lazyMember.MemberType; + + switch (memberType) + { + case MemberTypes.Field: + Assumes.IsTrue(accessors.Length == 1); + return ((FieldInfo)accessors[0]).ToReflectionField(); + + case MemberTypes.Property: + Assumes.IsTrue(accessors.Length == 2); + return ReflectionExtensions.CreateReflectionProperty((MethodInfo)accessors[0], (MethodInfo)accessors[1]); + + case MemberTypes.NestedType: + case MemberTypes.TypeInfo: + return ((Type)accessors[0]).ToReflectionType(); + + default: + Assumes.IsTrue(memberType == MemberTypes.Method); + return ((MethodInfo)accessors[0]).ToReflectionMethod(); + } + } + + public static LazyMemberInfo ToLazyMember(this MemberInfo member) + { + Assumes.NotNull(member); + + if (member.MemberType == MemberTypes.Property) + { + PropertyInfo property = member as PropertyInfo; + Assumes.NotNull(property); + + MemberInfo[] accessors = new MemberInfo[] { property.GetGetMethod(true), property.GetSetMethod(true) }; + return new LazyMemberInfo(MemberTypes.Property, accessors); + } + else + { + return new LazyMemberInfo(member); + } + } + + public static ReflectionWritableMember ToReflectionWriteableMember(this LazyMemberInfo lazyMember) + { + Assumes.IsTrue((lazyMember.MemberType == MemberTypes.Field) || (lazyMember.MemberType == MemberTypes.Property)); + + ReflectionWritableMember reflectionMember = lazyMember.ToReflectionMember() as ReflectionWritableMember; + Assumes.NotNull(reflectionMember); + + return reflectionMember; + } + + public static ReflectionProperty ToReflectionProperty(this PropertyInfo property) + { + Assumes.NotNull(property); + return CreateReflectionProperty(property.GetGetMethod(true), property.GetSetMethod(true)); + } + + public static ReflectionProperty CreateReflectionProperty(MethodInfo getMethod, MethodInfo setMethod) + { + Assumes.IsTrue(getMethod != null || setMethod != null); + + return new ReflectionProperty(getMethod, setMethod); + } + + public static ReflectionParameter ToReflectionParameter(this ParameterInfo parameter) + { + Assumes.NotNull(parameter); + return new ReflectionParameter(parameter); + } + + public static ReflectionMethod ToReflectionMethod(this MethodInfo method) + { + Assumes.NotNull(method); + return new ReflectionMethod(method); + } + + public static ReflectionField ToReflectionField(this FieldInfo field) + { + Assumes.NotNull(field); + return new ReflectionField(field); + } + + public static ReflectionType ToReflectionType(this Type type) + { + Assumes.NotNull(type); + return new ReflectionType(type); + } + + public static ReflectionWritableMember ToReflectionWritableMember(this MemberInfo member) + { + Assumes.NotNull(member); + if (member.MemberType == MemberTypes.Property) + { + return ((PropertyInfo)member).ToReflectionProperty(); + } + + return ((FieldInfo)member).ToReflectionField(); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionField.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionField.cs new file mode 100644 index 0000000000..64a02823ac --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionField.cs @@ -0,0 +1,66 @@ +// 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.Reflection; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal class ReflectionField : ReflectionWritableMember + { + private readonly FieldInfo _field; + + public ReflectionField(FieldInfo field) + { + Assumes.NotNull(field); + + _field = field; + } + + public FieldInfo UndelyingField + { + get { return _field; } + } + + public override MemberInfo UnderlyingMember + { + get { return UndelyingField; } + } + + public override bool CanRead + { + get { return true; } + } + + public override bool CanWrite + { + get { return !UndelyingField.IsInitOnly; } + } + + public override bool RequiresInstance + { + get { return !UndelyingField.IsStatic; } + } + + public override Type ReturnType + { + get { return UndelyingField.FieldType; } + } + + public override ReflectionItemType ItemType + { + get { return ReflectionItemType.Field; } + } + + public override object GetValue(object instance) + { + return UndelyingField.SafeGetValue(instance); + } + + public override void SetValue(object instance, object value) + { + UndelyingField.SafeSetValue(instance, value); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionImportDefinition.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionImportDefinition.cs new file mode 100644 index 0000000000..e20975945d --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionImportDefinition.cs @@ -0,0 +1,43 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal abstract class ReflectionImportDefinition : ContractBasedImportDefinition, ICompositionElement + { + private readonly ICompositionElement _origin; + + public ReflectionImportDefinition( + string contractName, + string requiredTypeIdentity, + IEnumerable> requiredMetadata, + ImportCardinality cardinality, + bool isRecomposable, + bool isPrerequisite, + CreationPolicy requiredCreationPolicy, + IDictionary metadata, + ICompositionElement origin) + : base(contractName, requiredTypeIdentity, requiredMetadata, cardinality, isRecomposable, isPrerequisite, requiredCreationPolicy, metadata) + { + _origin = origin; + } + + string ICompositionElement.DisplayName + { + get { return GetDisplayName(); } + } + + ICompositionElement ICompositionElement.Origin + { + get { return _origin; } + } + + public abstract ImportingItem ToImportingItem(); + + protected abstract string GetDisplayName(); + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionItem.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionItem.cs new file mode 100644 index 0000000000..6de13a509d --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionItem.cs @@ -0,0 +1,14 @@ +// 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. + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal abstract class ReflectionItem + { + public abstract string Name { get; } + public abstract string GetDisplayName(); + public abstract Type ReturnType { get; } + public abstract ReflectionItemType ItemType { get; } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionItemType.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionItemType.cs new file mode 100644 index 0000000000..fa7f23a7dc --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionItemType.cs @@ -0,0 +1,15 @@ +// 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. + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal enum ReflectionItemType : int + { + Parameter = 0, + Field = 1, + Property = 2, + Method = 3, + Type = 4, + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMember.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMember.cs new file mode 100644 index 0000000000..730708b555 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMember.cs @@ -0,0 +1,41 @@ +// 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.Reflection; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal abstract class ReflectionMember : ReflectionItem + { + public abstract bool CanRead + { + get; + } + + public Type DeclaringType + { + get { return UnderlyingMember.DeclaringType; } + } + + public override string Name + { + get { return UnderlyingMember.Name; } + } + + public override string GetDisplayName() + { + return UnderlyingMember.GetDisplayName(); + } + + public abstract bool RequiresInstance + { + get; + } + + public abstract MemberInfo UnderlyingMember { get; } + + public abstract object GetValue(object instance); + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMemberExportDefinition.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMemberExportDefinition.cs new file mode 100644 index 0000000000..9c25965b25 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMemberExportDefinition.cs @@ -0,0 +1,88 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Globalization; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal class ReflectionMemberExportDefinition : ExportDefinition, ICompositionElement + { + private readonly LazyMemberInfo _member; + private readonly ExportDefinition _exportDefinition; + private readonly ICompositionElement _origin; + private IDictionary _metadata; + + public ReflectionMemberExportDefinition(LazyMemberInfo member, ExportDefinition exportDefinition, ICompositionElement origin) + { + Assumes.NotNull(exportDefinition); + + _member = member; + _exportDefinition = exportDefinition; + _origin = origin; + } + + public override string ContractName + { + get { return _exportDefinition.ContractName; } + } + + public LazyMemberInfo ExportingLazyMember + { + get { return _member; } + } + + public override IDictionary Metadata + { + get + { + if (_metadata == null) + { + _metadata = _exportDefinition.Metadata.AsReadOnly(); + } + return _metadata; + } + } + + string ICompositionElement.DisplayName + { + get { return GetDisplayName(); } + } + + ICompositionElement ICompositionElement.Origin + { + get { return _origin; } + } + + public override string ToString() + { + return GetDisplayName(); + } + + public int GetIndex() + { + return ExportingLazyMember.ToReflectionMember().UnderlyingMember.MetadataToken; + } + + public ExportingMember ToExportingMember() + { + return new ExportingMember(this, ToReflectionMember()); + } + + private ReflectionMember ToReflectionMember() + { + return ExportingLazyMember.ToReflectionMember(); + } + + private string GetDisplayName() + { + return string.Format(CultureInfo.CurrentCulture, + "{0} (ContractName=\"{1}\")", // NOLOC + ToReflectionMember().GetDisplayName(), + ContractName); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMemberImportDefinition.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMemberImportDefinition.cs new file mode 100644 index 0000000000..d1d40fc966 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMemberImportDefinition.cs @@ -0,0 +1,54 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Globalization; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal class ReflectionMemberImportDefinition : ReflectionImportDefinition + { + private LazyMemberInfo _importingLazyMember; + + public ReflectionMemberImportDefinition( + LazyMemberInfo importingLazyMember, + string contractName, + string requiredTypeIdentity, + IEnumerable> requiredMetadata, + ImportCardinality cardinality, + bool isRecomposable, + bool isPrerequisite, + CreationPolicy requiredCreationPolicy, + IDictionary metadata, + ICompositionElement origin) + : base(contractName, requiredTypeIdentity, requiredMetadata, cardinality, isRecomposable, isPrerequisite, requiredCreationPolicy, metadata, origin) + { + Assumes.NotNull(contractName); + + _importingLazyMember = importingLazyMember; + } + + public override ImportingItem ToImportingItem() + { + ReflectionWritableMember member = ImportingLazyMember.ToReflectionWriteableMember(); + return new ImportingMember(this, member, new ImportType(member.ReturnType, Cardinality)); + } + + public LazyMemberInfo ImportingLazyMember + { + get { return _importingLazyMember; } + } + + protected override string GetDisplayName() + { + return string.Format( + CultureInfo.CurrentCulture, + "{0} (ContractName=\"{1}\")", // NOLOC + ImportingLazyMember.ToReflectionMember().GetDisplayName(), + ContractName); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMethod.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMethod.cs new file mode 100644 index 0000000000..56ab1bc9c8 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMethod.cs @@ -0,0 +1,62 @@ +// 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.ComponentModel.Composition.Primitives; +using System.Reflection; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal partial class ReflectionMethod : ReflectionMember + { + private readonly MethodInfo _method; + + public ReflectionMethod(MethodInfo method) + { + Assumes.NotNull(method); + + _method = method; + } + + public MethodInfo UnderlyingMethod + { + get { return _method; } + } + + public override MemberInfo UnderlyingMember + { + get { return UnderlyingMethod; } + } + + public override bool CanRead + { + get { return true; } + } + + public override bool RequiresInstance + { + get { return !UnderlyingMethod.IsStatic; } + } + + public override Type ReturnType + { + get { return UnderlyingMethod.ReturnType; } + } + + public override ReflectionItemType ItemType + { + get { return ReflectionItemType.Method; } + } + + public override object GetValue(object instance) + { + return SafeCreateExportedDelegate(instance, _method); + } + + private static ExportedDelegate SafeCreateExportedDelegate(object instance, MethodInfo method) + { + return new ExportedDelegate(instance, method); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionModelServices.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionModelServices.cs new file mode 100644 index 0000000000..e8616d8a33 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionModelServices.cs @@ -0,0 +1,495 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Linq; +using System.Reflection; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + public static class ReflectionModelServices + { + public static Lazy GetPartType(ComposablePartDefinition partDefinition) + { + Requires.NotNull(partDefinition, nameof(partDefinition)); + Contract.Ensures(Contract.Result>() != null); + + ReflectionComposablePartDefinition reflectionPartDefinition = partDefinition as ReflectionComposablePartDefinition; + if (reflectionPartDefinition == null) + { + throw ExceptionBuilder.CreateReflectionModelInvalidPartDefinition("partDefinition", partDefinition.GetType()); + } + + return reflectionPartDefinition.GetLazyPartType(); + } + + public static bool IsDisposalRequired(ComposablePartDefinition partDefinition) + { + Requires.NotNull(partDefinition, nameof(partDefinition)); + + ReflectionComposablePartDefinition reflectionPartDefinition = partDefinition as ReflectionComposablePartDefinition; + if (reflectionPartDefinition == null) + { + throw ExceptionBuilder.CreateReflectionModelInvalidPartDefinition("partDefinition", partDefinition.GetType()); + } + + return reflectionPartDefinition.IsDisposalRequired; + } + + public static LazyMemberInfo GetExportingMember(ExportDefinition exportDefinition) + { + Requires.NotNull(exportDefinition, nameof(exportDefinition)); + + ReflectionMemberExportDefinition reflectionExportDefinition = exportDefinition as ReflectionMemberExportDefinition; + if (reflectionExportDefinition == null) + { + throw new ArgumentException( + string.Format(CultureInfo.CurrentCulture, SR.ReflectionModel_InvalidExportDefinition, exportDefinition.GetType()), + "exportDefinition"); + } + + return reflectionExportDefinition.ExportingLazyMember; + } + + public static LazyMemberInfo GetImportingMember(ImportDefinition importDefinition) + { + Requires.NotNull(importDefinition, nameof(importDefinition)); + + ReflectionMemberImportDefinition reflectionMemberImportDefinition = importDefinition as ReflectionMemberImportDefinition; + if (reflectionMemberImportDefinition == null) + { + throw new ArgumentException( + string.Format(CultureInfo.CurrentCulture, SR.ReflectionModel_InvalidMemberImportDefinition, importDefinition.GetType()), + "importDefinition"); + } + + return reflectionMemberImportDefinition.ImportingLazyMember; + } + + public static Lazy GetImportingParameter(ImportDefinition importDefinition) + { + Requires.NotNull(importDefinition, nameof(importDefinition)); + Contract.Ensures(Contract.Result>() != null); + + ReflectionParameterImportDefinition reflectionParameterImportDefinition = importDefinition as ReflectionParameterImportDefinition; + if (reflectionParameterImportDefinition == null) + { + throw new ArgumentException( + string.Format(CultureInfo.CurrentCulture, SR.ReflectionModel_InvalidParameterImportDefinition, importDefinition.GetType()), + "importDefinition"); + } + + return reflectionParameterImportDefinition.ImportingLazyParameter; + } + + public static bool IsImportingParameter(ImportDefinition importDefinition) + { + Requires.NotNull(importDefinition, nameof(importDefinition)); + + ReflectionImportDefinition reflectionImportDefinition = importDefinition as ReflectionImportDefinition; + if (reflectionImportDefinition == null) + { + throw new ArgumentException( + string.Format(CultureInfo.CurrentCulture, SR.ReflectionModel_InvalidImportDefinition, importDefinition.GetType()), + "importDefinition"); + } + + return (importDefinition is ReflectionParameterImportDefinition); + } + + public static bool IsExportFactoryImportDefinition(ImportDefinition importDefinition) + { + Requires.NotNull(importDefinition, nameof(importDefinition)); + + return (importDefinition is IPartCreatorImportDefinition); + } + + public static ContractBasedImportDefinition GetExportFactoryProductImportDefinition(ImportDefinition importDefinition) + { + Requires.NotNull(importDefinition, nameof(importDefinition)); + Contract.Ensures(Contract.Result() != null); + + IPartCreatorImportDefinition partCreatorImportDefinition = importDefinition as IPartCreatorImportDefinition; + if (partCreatorImportDefinition == null) + { + throw new ArgumentException( + string.Format(CultureInfo.CurrentCulture, SR.ReflectionModel_InvalidImportDefinition, importDefinition.GetType()), + "importDefinition"); + } + + return partCreatorImportDefinition.ProductImportDefinition; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public static ComposablePartDefinition CreatePartDefinition( + Lazy partType, + bool isDisposalRequired, + Lazy> imports, + Lazy> exports, + Lazy> metadata, + ICompositionElement origin) + { + Requires.NotNull(partType, nameof(partType)); + Contract.Ensures(Contract.Result() != null); + + return new ReflectionComposablePartDefinition( + new ReflectionPartCreationInfo( + partType, + isDisposalRequired, + imports, + exports, + metadata, + origin)); + } + + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public static ExportDefinition CreateExportDefinition( + LazyMemberInfo exportingMember, + string contractName, + Lazy> metadata, + ICompositionElement origin) + { + Requires.NotNullOrEmpty(contractName, "contractName"); + Requires.IsInMembertypeSet(exportingMember.MemberType, "exportingMember", MemberTypes.Field | MemberTypes.Property | MemberTypes.NestedType | MemberTypes.TypeInfo | MemberTypes.Method); + Contract.Ensures(Contract.Result() != null); + + return new ReflectionMemberExportDefinition( + exportingMember, + new LazyExportDefinition(contractName, metadata), + origin); + } + + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public static ContractBasedImportDefinition CreateImportDefinition( + LazyMemberInfo importingMember, + string contractName, + string requiredTypeIdentity, + IEnumerable> requiredMetadata, + ImportCardinality cardinality, + bool isRecomposable, + CreationPolicy requiredCreationPolicy, + ICompositionElement origin) + { + return CreateImportDefinition(importingMember, contractName, requiredTypeIdentity, requiredMetadata, cardinality, isRecomposable, requiredCreationPolicy, MetadataServices.EmptyMetadata, false, origin); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public static ContractBasedImportDefinition CreateImportDefinition( + LazyMemberInfo importingMember, + string contractName, + string requiredTypeIdentity, + IEnumerable> requiredMetadata, + ImportCardinality cardinality, + bool isRecomposable, + CreationPolicy requiredCreationPolicy, + IDictionary metadata, + bool isExportFactory, + ICompositionElement origin) + { + return CreateImportDefinition( + importingMember, + contractName, + requiredTypeIdentity, + requiredMetadata, + cardinality, + isRecomposable, + false, + requiredCreationPolicy, + metadata, + isExportFactory, + origin); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public static ContractBasedImportDefinition CreateImportDefinition( + LazyMemberInfo importingMember, + string contractName, + string requiredTypeIdentity, + IEnumerable> requiredMetadata, + ImportCardinality cardinality, + bool isRecomposable, + bool isPreRequisite, + CreationPolicy requiredCreationPolicy, + IDictionary metadata, + bool isExportFactory, + ICompositionElement origin) + { + Requires.NotNullOrEmpty(contractName, "contractName"); + Requires.IsInMembertypeSet(importingMember.MemberType, "importingMember", MemberTypes.Property | MemberTypes.Field); + Contract.Ensures(Contract.Result() != null); + + if (isExportFactory) + { + return new PartCreatorMemberImportDefinition( + importingMember, + origin, + new ContractBasedImportDefinition( + contractName, + requiredTypeIdentity, + requiredMetadata, + cardinality, + isRecomposable, + isPreRequisite, + CreationPolicy.NonShared, + metadata)); + } + else + { + return new ReflectionMemberImportDefinition( + importingMember, + contractName, + requiredTypeIdentity, + requiredMetadata, + cardinality, + isRecomposable, + isPreRequisite, + requiredCreationPolicy, + metadata, + origin); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public static ContractBasedImportDefinition CreateImportDefinition( + Lazy parameter, + string contractName, + string requiredTypeIdentity, + IEnumerable> requiredMetadata, + ImportCardinality cardinality, + CreationPolicy requiredCreationPolicy, + ICompositionElement origin) + { + return CreateImportDefinition(parameter, contractName, requiredTypeIdentity, requiredMetadata, cardinality, requiredCreationPolicy, MetadataServices.EmptyMetadata, false, origin); + } + + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public static ContractBasedImportDefinition CreateImportDefinition( + Lazy parameter, + string contractName, + string requiredTypeIdentity, + IEnumerable> requiredMetadata, + ImportCardinality cardinality, + CreationPolicy requiredCreationPolicy, + IDictionary metadata, + bool isExportFactory, + ICompositionElement origin) + { + Requires.NotNull(parameter, nameof(parameter)); + Requires.NotNullOrEmpty(contractName, "contractName"); + Contract.Ensures(Contract.Result() != null); + + if (isExportFactory) + { + return new PartCreatorParameterImportDefinition( + parameter, + origin, + new ContractBasedImportDefinition( + contractName, + requiredTypeIdentity, + requiredMetadata, + cardinality, + false, + true, + CreationPolicy.NonShared, + metadata)); + } + else + { + return new ReflectionParameterImportDefinition( + parameter, + contractName, + requiredTypeIdentity, + requiredMetadata, + cardinality, + requiredCreationPolicy, + metadata, + origin); + } + } + + public static bool TryMakeGenericPartDefinition(ComposablePartDefinition partDefinition, IEnumerable genericParameters, out ComposablePartDefinition specialization) + { + Requires.NotNull(partDefinition, nameof(partDefinition)); + + specialization = null; + ReflectionComposablePartDefinition reflectionPartDefinition = partDefinition as ReflectionComposablePartDefinition; + if (reflectionPartDefinition == null) + { + throw ExceptionBuilder.CreateReflectionModelInvalidPartDefinition("partDefinition", partDefinition.GetType()); + } + + return reflectionPartDefinition.TryMakeGenericPartDefinition(genericParameters.ToArray(), out specialization); + } + } + +internal class ReflectionPartCreationInfo : IReflectionPartCreationInfo + { + private readonly Lazy _partType; + private readonly Lazy> _imports; + private readonly Lazy> _exports; + private readonly Lazy> _metadata; + private readonly ICompositionElement _origin; + private ConstructorInfo _constructor; + private bool _isDisposalRequired; + + public ReflectionPartCreationInfo( + Lazy partType, + bool isDisposalRequired, + Lazy> imports, + Lazy> exports, + Lazy> metadata, + ICompositionElement origin) + { + Assumes.NotNull(partType); + + _partType = partType; + _isDisposalRequired = isDisposalRequired; + _imports = imports; + _exports = exports; + _metadata = metadata; + _origin = origin; + } + + public Type GetPartType() + { + return _partType.GetNotNullValue("type"); + } + + public Lazy GetLazyPartType() + { + return _partType; + } + + public ConstructorInfo GetConstructor() + { + if (_constructor == null) + { + ConstructorInfo[] constructors = null; + constructors = GetImports() + .OfType() + .Select(parameterImport => parameterImport.ImportingLazyParameter.Value.Member) + .OfType() + .Distinct() + .ToArray(); + + if (constructors.Length == 1) + { + _constructor = constructors[0]; + } + else if (constructors.Length == 0) + { + _constructor = GetPartType().GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null); + } + } + return _constructor; + } + + public bool IsDisposalRequired + { + get + { + return _isDisposalRequired; + } + } + + public bool IsIdentityComparison + { + get + { + return true; + } + } + + public IDictionary GetMetadata() + { + return (_metadata != null) ? _metadata.Value : null; + } + + public IEnumerable GetExports() + { + if (_exports == null) + { + yield break; + } + + IEnumerable exports = _exports.Value; + + if (exports == null) + { + yield break; + } + + foreach (ExportDefinition export in exports) + { + ReflectionMemberExportDefinition reflectionExport = export as ReflectionMemberExportDefinition; + if (reflectionExport == null) + { + throw new InvalidOperationException( + string.Format(CultureInfo.CurrentCulture, SR.ReflectionModel_InvalidExportDefinition, export.GetType())); + } + yield return reflectionExport; + } + } + + public IEnumerable GetImports() + { + if (_imports == null) + { + yield break; + } + + IEnumerable imports = _imports.Value; + + if (imports == null) + { + yield break; + } + + foreach (ImportDefinition import in imports) + { + ReflectionImportDefinition reflectionImport = import as ReflectionImportDefinition; + if (reflectionImport == null) + { + throw new InvalidOperationException( + string.Format(CultureInfo.CurrentCulture, SR.ReflectionModel_InvalidMemberImportDefinition, import.GetType())); + } + yield return reflectionImport; + } + } + + public string DisplayName + { + get { return GetPartType().GetDisplayName(); } + } + + public ICompositionElement Origin + { + get { return _origin; } + } + } + + internal class LazyExportDefinition : ExportDefinition + { + private readonly Lazy> _metadata; + + public LazyExportDefinition(string contractName, Lazy> metadata) + : base(contractName, (IDictionary)null) + { + _metadata = metadata; + } + + public override IDictionary Metadata + { + get + { + return _metadata.Value ?? MetadataServices.EmptyMetadata; + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionParameter.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionParameter.cs new file mode 100644 index 0000000000..905528d76d --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionParameter.cs @@ -0,0 +1,51 @@ +// 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.Globalization; +using System.Reflection; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal class ReflectionParameter : ReflectionItem + { + private readonly ParameterInfo _parameter; + + public ReflectionParameter(ParameterInfo parameter) + { + Assumes.NotNull(parameter); + + _parameter = parameter; + } + + public ParameterInfo UnderlyingParameter + { + get { return _parameter; } + } + + public override string Name + { + get { return UnderlyingParameter.Name; } + } + + public override string GetDisplayName() + { + return string.Format( + CultureInfo.CurrentCulture, + "{0} (Parameter=\"{1}\")", // NOLOC + UnderlyingParameter.Member.GetDisplayName(), + UnderlyingParameter.Name); + } + + public override Type ReturnType + { + get { return UnderlyingParameter.ParameterType; } + } + + public override ReflectionItemType ItemType + { + get { return ReflectionItemType.Parameter; } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionParameterImportDefinition.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionParameterImportDefinition.cs new file mode 100644 index 0000000000..8b55873a2d --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionParameterImportDefinition.cs @@ -0,0 +1,54 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Globalization; +using System.Reflection; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal class ReflectionParameterImportDefinition : ReflectionImportDefinition + { + private Lazy _importingLazyParameter; + + public ReflectionParameterImportDefinition( + Lazy importingLazyParameter, + string contractName, + string requiredTypeIdentity, + IEnumerable> requiredMetadata, + ImportCardinality cardinality, + CreationPolicy requiredCreationPolicy, + IDictionary metadata, + ICompositionElement origin) + : base(contractName, requiredTypeIdentity, requiredMetadata, cardinality, false, true, requiredCreationPolicy, metadata, origin) + { + Assumes.NotNull(importingLazyParameter); + + _importingLazyParameter = importingLazyParameter; + } + + public override ImportingItem ToImportingItem() + { + return new ImportingParameter(this, new ImportType(ImportingLazyParameter.GetNotNullValue("parameter").ParameterType, Cardinality)); + } + + public Lazy ImportingLazyParameter + { + get { return _importingLazyParameter; } + } + + protected override string GetDisplayName() + { + ParameterInfo parameter = ImportingLazyParameter.GetNotNullValue("parameter"); + return string.Format( + CultureInfo.CurrentCulture, + "{0} (Parameter=\"{1}\", ContractName=\"{2}\")", // NOLOC + parameter.Member.GetDisplayName(), + parameter.Name, + ContractName); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionProperty.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionProperty.cs new file mode 100644 index 0000000000..ee482ba36b --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionProperty.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. +// See the LICENSE file in the project root for more information. + +using System.Reflection; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + // Instead of representing properties as an actual PropertyInfo, we need to + // represent them as two MethodInfo objects one for each accessor. This is so + // that cached attribute part can go from a metadata token -> XXXInfo without + // needing to walk all members of a particular type. Unfortunately, (probably + // because you never see one of them in an IL stream), Reflection does not allow + // you to go from a metadata token -> PropertyInfo like it does with types, + // fields, and methods. + + internal class ReflectionProperty : ReflectionWritableMember + { + private readonly MethodInfo _getMethod; + private readonly MethodInfo _setMethod; + + public ReflectionProperty(MethodInfo getMethod, MethodInfo setMethod) + { + Assumes.IsTrue(getMethod != null || setMethod != null); + + _getMethod = getMethod; + _setMethod = setMethod; + } + + public override MemberInfo UnderlyingMember + { + get { return UnderlyingGetMethod ?? UnderlyingSetMethod; } + } + + public override bool CanRead + { + get { return UnderlyingGetMethod != null; } + } + + public override bool CanWrite + { + get { return UnderlyingSetMethod != null; } + } + + public MethodInfo UnderlyingGetMethod + { + get { return _getMethod; } + } + + public MethodInfo UnderlyingSetMethod + { + get { return _setMethod; } + } + + public override string Name + { + get + { + MethodInfo method = UnderlyingGetMethod ?? UnderlyingSetMethod; + + string name = method.Name; + + Assumes.IsTrue(name.Length > 4); + + // Remove 'get_' or 'set_' + return name.Substring(4); + } + } + + public override string GetDisplayName() + { + return ReflectionServices.GetDisplayName(DeclaringType, Name); + } + + public override bool RequiresInstance + { + get + { + MethodInfo method = UnderlyingGetMethod ?? UnderlyingSetMethod; + + return !method.IsStatic; + } + } + + public override Type ReturnType + { + get + { + if (UnderlyingGetMethod != null) + { + return UnderlyingGetMethod.ReturnType; + } + + ParameterInfo[] parameters = UnderlyingSetMethod.GetParameters(); + + Assumes.IsTrue(parameters.Length > 0); + + return parameters[parameters.Length - 1].ParameterType; + } + } + + public override ReflectionItemType ItemType + { + get { return ReflectionItemType.Property; } + } + + public override object GetValue(object instance) + { + Assumes.NotNull(_getMethod); + + return UnderlyingGetMethod.SafeInvoke(instance); + } + + public override void SetValue(object instance, object value) + { + Assumes.NotNull(_setMethod); + + UnderlyingSetMethod.SafeInvoke(instance, value); + } + + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionType.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionType.cs new file mode 100644 index 0000000000..e03c48ee02 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionType.cs @@ -0,0 +1,51 @@ +// 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.Reflection; +using Microsoft.Internal; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal class ReflectionType : ReflectionMember + { + private Type _type; + + public ReflectionType(Type type) + { + Assumes.NotNull(type); + + _type = type; + } + + public override MemberInfo UnderlyingMember + { + get { return _type; } + } + + public override bool CanRead + { + get { return true; } + } + + public override bool RequiresInstance + { + get { return true; } + } + + public override Type ReturnType + { + get { return _type; } + } + + public override ReflectionItemType ItemType + { + get { return ReflectionItemType.Type; } + } + + public override object GetValue(object instance) + { + return instance; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionWritableMember.cs b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionWritableMember.cs new file mode 100644 index 0000000000..8d0eba4715 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionWritableMember.cs @@ -0,0 +1,16 @@ +// 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. + +namespace System.ComponentModel.Composition.ReflectionModel +{ + internal abstract class ReflectionWritableMember : ReflectionMember + { + public abstract bool CanWrite + { + get; + } + + public abstract void SetValue(object instance, object value); + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/CacheTestsMetadataViews.cs b/external/corefx/src/System.ComponentModel.Composition/tests/CacheTestsMetadataViews.cs new file mode 100644 index 0000000000..e28c7880ba --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/CacheTestsMetadataViews.cs @@ -0,0 +1,18 @@ +// 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.Collections.Generic; + +namespace System.ComponentModel.Composition +{ + public interface ITrans_CacheTests_SimpleMetadataView + { + string String { get; } + int Int { get; } + float Float { get; } + Type Type { get; } + object Object { get; } + IEnumerable Collection { get; } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/Configurations.props b/external/corefx/src/System.ComponentModel.Composition/tests/Configurations.props new file mode 100644 index 0000000000..02721ef2a9 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/Configurations.props @@ -0,0 +1,9 @@ + + + + + netcoreapp; + uap; + + + diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/ICollectionOfStrings.cs b/external/corefx/src/System.ComponentModel.Composition/tests/ICollectionOfStrings.cs new file mode 100644 index 0000000000..5b01950ee5 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/ICollectionOfStrings.cs @@ -0,0 +1,13 @@ +// 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.Collections.Generic; + +namespace System.ComponentModel.Composition +{ + public interface ITrans_CollectionOfStrings + { + IEnumerable Values { get; } + } +} diff --git a/mcs/class/System.Data/corefx/DbConnection.cs b/external/corefx/src/System.ComponentModel.Composition/tests/IExportableTest.cs similarity index 61% rename from mcs/class/System.Data/corefx/DbConnection.cs rename to external/corefx/src/System.ComponentModel.Composition/tests/IExportableTest.cs index 4b5f1439c5..8d67b186e1 100644 --- a/mcs/class/System.Data/corefx/DbConnection.cs +++ b/external/corefx/src/System.ComponentModel.Composition/tests/IExportableTest.cs @@ -2,10 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace System.Data.Common +namespace System.ComponentModel.Composition { - partial class DbConnection - { - internal DbProviderFactory ProviderFactory => DbProviderFactory; - } -} \ No newline at end of file + public interface ITrans_ExportableTest + { + string Var1 { get; } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/IMetadataView.cs b/external/corefx/src/System.ComponentModel.Composition/tests/IMetadataView.cs new file mode 100644 index 0000000000..5cfbb11376 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/IMetadataView.cs @@ -0,0 +1,54 @@ +// 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. + +namespace System.ComponentModel.Composition +{ + public interface ITrans_MetadataView + { + string Foo { get; } + + [System.ComponentModel.DefaultValue(null)] + string OptionalFoo { get; } + } + + public interface ITrans_MetadataViewWithDefaultedInt64 + { + [DefaultValue(Int64.MaxValue)] + Int64 MyInt64 { get; } + } + + public interface ITrans_MetadataViewWithTypeMismatchDefaultValue + { + [DefaultValue("Strings can't cast to numbers")] + int MyInt { get; } + } + + public interface ITrans_MetadataViewWithDefaultedInt + { + [DefaultValue(120)] + int MyInt { get; } + } + + public interface ITrans_MetadataViewWithDefaultedBool + { + [DefaultValue(false)] + bool MyBool { get; } + } + + public interface ITrans_MetadataViewWithDefaultedString + { + [DefaultValue("MyString")] + string MyString { get; } + } + + public interface ITrans_MetadataViewUnboxAsInt + { + int Value { get; } + } + + public interface ITrans_HasInt64 + { + Int32 Value { get; } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/ISimpleMetadataView.cs b/external/corefx/src/System.ComponentModel.Composition/tests/ISimpleMetadataView.cs new file mode 100644 index 0000000000..a4a5dcd051 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/ISimpleMetadataView.cs @@ -0,0 +1,16 @@ +// 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. + +namespace System.ComponentModel.Composition +{ + + public interface ITrans_SimpleMetadataView + { + string String { get; } + int Int { get; } + float Float { get; } + Type Type { get; } + object Object { get; } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/MetadataTests_IMetadataView.cs b/external/corefx/src/System.ComponentModel.Composition/tests/MetadataTests_IMetadataView.cs new file mode 100644 index 0000000000..cc96ccd1b1 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/MetadataTests_IMetadataView.cs @@ -0,0 +1,92 @@ +// 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. + +namespace System.ComponentModel.Composition +{ + public interface ITrans_RecompositionTest_RelationshipView + { + string Relationship { get; } + } + + public interface ITrans_StronglyTypedStructure + { + string String1 { get; } + string String2 { get; } + int[] Numbers { get; } + CreationPolicy Policy { get; } + Type Type { get; } + } + + public interface ITrans_AddinMetadata + { + string Name { get; } + string Version { get; } + string Id { get; } + } + + public interface ITrans_MetadataTests_CustomMetadata + { + bool PropertyName { get; } + } + + public interface ITrans_MetadataTests_MetadataView + { + string Value + { + get; + } + } + + public interface ITrans_MetadataTests_MetadataView2 : ITrans_MetadataTests_MetadataView + { + new int Value + { + get; + } + } + + public interface ITrans_MetadataTests_MetadataView3 : ITrans_MetadataTests_MetadataView + { + string Value2 + { + get; + } + } + + public interface ITrans_MetadataTests_MetadataViewWithPropertySetter + { + string Value + { + get; + set; + } + } + + public interface ITrans_MetadataTests_MetadataViewWithMethod + { + string Value + { + get; + } + void Method(); + } + + public interface ITrans_MetadataTests_MetadataViewWithEvent + { + string Value + { + get; + } + event EventHandler TestEvent; + } + + public interface ITrans_MetadataTests_MetadataViewWithIndexer + { + string Value + { + get; + } + string this[object o] { get; } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/Microsoft/Internal/LazyServices.cs b/external/corefx/src/System.ComponentModel.Composition/tests/Microsoft/Internal/LazyServices.cs new file mode 100644 index 0000000000..480cd80488 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/Microsoft/Internal/LazyServices.cs @@ -0,0 +1,18 @@ +// 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.Threading; + +namespace Microsoft.Internal +{ + internal static class LazyServices + { + public static Lazy AsLazy(this T t) + where T : class + { + return new Lazy(() => t, LazyThreadSafetyMode.PublicationOnly); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/StringsTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/StringsTests.cs new file mode 100644 index 0000000000..b2af800249 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/StringsTests.cs @@ -0,0 +1,52 @@ +// 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.Linq; +using System.Reflection; +using Microsoft.VisualBasic; +using Xunit; + +namespace System +{ + public class StringsTests + { + [Fact] + [ActiveIssue(25498)] + public void PropertiesAreInsyncWithResources() + { + var properties = GetStringProperties(); + + Assert.True(properties.Length > 0, "Expected to find at least one string property in Strings.cs."); + + Assert.All(properties, property => + { + object value = property.GetValue(null, (object[])null); + Assert.NotNull(value); + }); + } + + private static PropertyInfo[] GetStringProperties() + { + PropertyInfo[] properties = typeof(Strings).GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); + + return properties.Where(property => + { + return !CanIgnore(property); + + }).ToArray(); + } + + private static bool CanIgnore(PropertyInfo property) + { + switch (property.Name) + { + case "Culture": + case "ResourceManager": + return true; + } + + return false; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System.ComponentModel.Composition.Tests.csproj b/external/corefx/src/System.ComponentModel.Composition/tests/System.ComponentModel.Composition.Tests.csproj new file mode 100644 index 0000000000..72fe4fde46 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System.ComponentModel.Composition.Tests.csproj @@ -0,0 +1,191 @@ + + + + + + + + + + {59F4682D-C41D-45A7-9798-16C75525BB1D} + + + + + + + + + Common\System\IO\TempDirectory.cs + + + Common\System\IO\TempFile.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/AdvancedValueComposition.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/AdvancedValueComposition.cs new file mode 100644 index 0000000000..a9bd0fd391 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/AdvancedValueComposition.cs @@ -0,0 +1,278 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.Linq; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class AdvancedValueComposition + { + [Fact] + public void RepeatedContainerUse() + { + var container = ContainerFactory.Create(); + TrivialExporter e = new TrivialExporter(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(e); + container.Compose(batch); + + batch = new CompositionBatch(); + batch.AddPart(new TrivialImporter()); + container.Compose(batch); + + Assert.True(e.done, "Initialization of importer should have set the done flag on E"); + } + + [Fact] + public void FunctionsFieldsAndProperties() + { + Consumer c; + var container = ContainerFactory.Create(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(new RealAddProvider()); + batch.AddPart(c = new Consumer()); + container.Compose(batch); + + Assert.Equal(3, c.op(c.a, c.b)); + } + + [Fact] + public void FunctionsFieldsAndProperties2() + { + Consumer c; + var container = ContainerFactory.Create(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(new SubtractProvider()); + batch.AddPart(c = new Consumer()); + container.Compose(batch); + + Assert.Equal(-1, c.op(c.a, c.b)); + } + + [Fact] + [ActiveIssue(25498)] + public void FunctionsFieldsAndProperties2_WithCatalog() + { + var container = ContainerFactory.CreateWithDefaultAttributedCatalog(); + + ConsumerOfMultiple c = new ConsumerOfMultiple(); + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(c); + container.Compose(batch); + + foreach (Lazy, IDictionary> export in c.opInfo) + { + if ((string)export.Metadata["Var1"] == "add") + { + Assert.Equal(3, export.Value(1, 2)); + } + else if ((string)export.Metadata["Var1"] == "sub") + { + Assert.Equal(-1, export.Value(1, 2)); + } + else + { + throw new NotImplementedException(); + } + } + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void FunctionsFieldsAndProperties2_StronglyTypedMetadata() + { + var container = ContainerFactory.CreateWithDefaultAttributedCatalog(); + + var exports = container.GetExports, ITrans_ExportableTest>("Add"); + + foreach (var export in exports) + { + if (export.Metadata.Var1 == "add") + { + Assert.Equal(3, export.Value(1, 2)); + } + else if (export.Metadata.Var1 == "sub") + { + Assert.Equal(-1, export.Value(1, 2)); + } + else + { + throw new NotImplementedException(); + } + } + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void InAdditionToCatalogTest() + { + var container = ContainerFactory.CreateWithDefaultAttributedCatalog(); + + IDictionary multMetadata = new Dictionary(); + multMetadata["Var1"] = "mult"; + multMetadata[CompositionConstants.ExportTypeIdentityMetadataName] = AttributedModelServices.GetTypeIdentity(typeof(Func)); + var basicValue = ExportFactory.Create("Add", multMetadata, (() => (Func)delegate (int a, int b) + { return a * b; })); + + CompositionBatch batch = new CompositionBatch(); + batch.AddExport(basicValue); + container.Compose(batch); + + var exports = container.GetExports, ITrans_ExportableTest>("Add"); + + Assert.Equal(3, exports.Count()); + + foreach (var export in exports) + { + if (export.Metadata.Var1 == "mult") + { + Assert.Equal(2, export.Value(1, 2)); + } + else if (export.Metadata.Var1 == "add") + { + Assert.Equal(3, export.Value(1, 2)); + } + else if (export.Metadata.Var1 == "sub") + { + Assert.Equal(-1, export.Value(1, 2)); + } + else + { + throw new NotImplementedException(); + } + } + } + + [Fact] + [ActiveIssue(25498)] + public void CollectionMetadataPropertyTest() + { + var container = ContainerFactory.CreateWithDefaultAttributedCatalog(); + var export = container.GetExport(); + + Assert.NotNull(export.Metadata); + Assert.NotNull(export.Metadata.Values); + Assert.Equal(export.Metadata.Values.Count(), 3); + Assert.Equal(export.Metadata.Values.First(), "One"); + Assert.Equal(export.Metadata.Values.Skip(1).First(), "two"); + Assert.Equal(export.Metadata.Values.Skip(2).First(), "3"); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void ImportExportSansNameTest() + { + var container = ContainerFactory.CreateWithDefaultAttributedCatalog(); + + UnnamedImportAndExport unnamed = container.GetExportedValue(); + Assert.NotNull(unnamed); + Assert.NotNull(unnamed.ImportedValue); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void MultipleInstantiationOfStaticCatalogItem() + { + var container = ContainerFactory.CreateWithDefaultAttributedCatalog(); + var unnamedVI = container.GetExport(); + + StaticExport first = unnamedVI.Value; + StaticExport second = unnamedVI.Value; + + Assert.NotNull(first); + Assert.NotNull(second); + Assert.True(object.ReferenceEquals(first, second), "Instances should be the same"); + + var exports = container.GetExports(); + + Assert.Equal(1, exports.Count()); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void MultipleInstantiationOfNonStaticCatalogItem() + { + var container = ContainerFactory.CreateWithDefaultAttributedCatalog(); + var export1 = container.GetExport(); + var export2 = container.GetExport(); + + NonStaticExport first = export1.Value; + NonStaticExport second = export2.Value; + + Assert.NotNull(first); + Assert.NotNull(second); + Assert.False(object.ReferenceEquals(first, second), "Instances should be different"); + } + + [Fact] + [ActiveIssue(25498)] + public void ImportIntoUntypedExportTest() + { + var container = ContainerFactory.Create(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddExportedValue("untyped", 42); + + var u = new UntypedExportImporter(); + var rb = AttributedModelServices.CreatePart(u); + + batch.AddPart(rb); + container.Compose(batch); + + Assert.Equal(42, u.Export.Value); + + var us = new UntypedExportsImporter(); + batch = new CompositionBatch(); + batch.AddExportedValue("untyped", 19); + batch.RemovePart(rb); + batch.AddPart(us); + container.Compose(batch); + + Assert.NotNull(us.Exports); + Assert.Equal(2, us.Exports.Count()); + } + + [Fact] + public void ImportIntoDerivationOfExportException() + { + var container = ContainerFactory.Create(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddExportedValue("derived", typeof(DerivedExport), 42); + var d = new DerivedExportImporter(); + batch.AddPart(d); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotSetImport, + ErrorId.ReflectionModel_ImportNotAssignableFromExport, RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + + [Fact] + public void ImportIntoDerivationOfExportsException() + { + var container = ContainerFactory.Create(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddExportedValue("derived", typeof(DerivedExport), 42); + var d = new DerivedExportsImporter(); + batch.AddPart(d); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotSetImport, + ErrorId.ReflectionModel_ImportNotAssignableFromExport, RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/AggregateExportProviderTest.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/AggregateExportProviderTest.cs new file mode 100644 index 0000000000..8f1387350f --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/AggregateExportProviderTest.cs @@ -0,0 +1,94 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.Linq; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class AggregateExportProviderTests + { + [Fact] + public void Constructor1_NullAsProvidersArgument_ShouldSetProvidersPropertyToEmpty() + { + var provider = new AggregateExportProvider((ExportProvider[])null); + + Assert.Empty(provider.Providers); + } + + [Fact] + public void Constructor2_NullAsProvidersArgument_ShouldSetProvidersPropertyToEmpty() + { + var provider = new AggregateExportProvider((IEnumerable)null); + + Assert.Empty(provider.Providers); + } + + [Fact] + public void Constructor1_EmptyArrayAsProvidersArgument_ShouldSetProvidersPropertyToEmpty() + { + var provider = new AggregateExportProvider(new ExportProvider[0]); + + Assert.Empty(provider.Providers); + } + + [Fact] + public void Constructor2_EmptyArrayAsProvidersArgument_ShouldSetProvidersPropertyToEmpty() + { + var provider = new AggregateExportProvider((IEnumerable)new ExportProvider[0]); + + Assert.Empty(provider.Providers); + } + + [Fact] + public void Constructor2_EmptyEnumerableAsProvidersArgument_ShouldSetProvidersPropertyToEmpty() + { + var provider = new AggregateExportProvider(Enumerable.Empty()); + + Assert.Empty(provider.Providers); + } + + [Fact] + public void Constructor1_ArrayAsProvidersArgument_ShouldNotAllowModificationAfterConstruction() + { + var providers = new ExportProvider[] { ExportProviderFactory.Create() }; + var provider = new AggregateExportProvider(providers); + + providers[0] = null; + + Assert.NotNull(provider.Providers[0]); + } + + [Fact] + public void Constructor2_ArrayAsProvidersArgument_ShouldNotAllowModificationAfterConstruction() + { + var providers = new ExportProvider[] { ExportProviderFactory.Create() }; + var provider = new AggregateExportProvider((IEnumerable)providers); + + providers[0] = null; + + Assert.NotNull(provider.Providers[0]); + } + + [Fact] + public void Providers_WhenDisposed_ShouldThrowObjectDisposed() + { + var provider = CreateAggregateExportProvider(); + provider.Dispose(); + + Assert.Throws(() => + { + var providers = provider.Providers; + }); + } + + private AggregateExportProvider CreateAggregateExportProvider() + { + return new AggregateExportProvider(Enumerable.Empty()); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/AttributedModel/AllowNonPublicCompositionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/AttributedModel/AllowNonPublicCompositionTests.cs new file mode 100644 index 0000000000..afb8f7b894 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/AttributedModel/AllowNonPublicCompositionTests.cs @@ -0,0 +1,307 @@ +// 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.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class AllowNonPublicCompositionTests + { + [Fact] + public void PublicFromPublic() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + var importer = new AllPublicImportOnly(); + batch.AddPart(importer); + batch.AddPart(new AllPublicExportOnly() { ExportA = 5, ExportB = 10 }); + container.Compose(batch); + + Assert.Equal(5, importer.ImportA); + Assert.Equal(10, importer.ImportB); + } + + [Fact] + public void PublicToSelf() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + var importer = new AllPublic() { ExportA = 5, ExportB = 10 }; + batch.AddPart(importer); + container.Compose(batch); + + Assert.Equal(5, importer.ImportA); + Assert.Equal(10, importer.ImportB); + } + + [Fact] + public void PublicFromPrivate() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + var importer = new AllPublicImportOnly(); + batch.AddPart(importer); + batch.AddPart(new AllPrivateExportOnly(5, 10)); + container.Compose(batch); + + Assert.Equal(5, importer.ImportA); + Assert.Equal(10, importer.ImportB); + } + + [Fact] + public void PrivateFromPublic() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + var importer = new AllPrivateImportOnly(); + batch.AddPart(importer); + batch.AddPart(new AllPublicExportOnly() { ExportA = 5, ExportB = 10 }); + container.Compose(batch); + + Assert.Equal(5, importer.PublicImportA); + Assert.Equal(10, importer.PublicImportB); + } + + [Fact] + public void PrivateToSelf() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + var importer = new AllPrivate(5, 10); + batch.AddPart(importer); + container.Compose(batch); + + Assert.Equal(5, importer.PublicImportA); + Assert.Equal(10, importer.PublicImportB); + } + + [Fact] + public void PrivateData() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + var importer = new PrivateDataImportExport(5); + batch.AddPart(importer); + container.Compose(batch); + + Assert.Equal(5, importer.X); + } + + [Fact] + [ActiveIssue(25498)] + public void TestPublicImportsExpectingPublicExportsFromCatalog() + { + var cat = CatalogFactory.CreateDefaultAttributed(); + var container = new CompositionContainer(cat); + container.GetExportedValue().VerifyIsBound(); + } + + [Fact] + [ActiveIssue(25498)] + public void TestInternalImportsExpectingPublicExportsFromCatalog() + { + var cat = CatalogFactory.CreateDefaultAttributed(); + var container = new CompositionContainer(cat); + container.GetExportedValue().VerifyIsBound(); + } + + [Fact] + [ActiveIssue(25498)] + public void TestPublicImportsExpectingInternalExportsFromCatalog() + { + var cat = CatalogFactory.CreateDefaultAttributed(); + var container = new CompositionContainer(cat); + container.GetExportedValue().VerifyIsBound(); + } + + [Fact] + [ActiveIssue(25498)] + public void TestInternalImportsExpectingInternalExportsFromCatalog() + { + var cat = CatalogFactory.CreateDefaultAttributed(); + var container = new CompositionContainer(cat); + container.GetExportedValue().VerifyIsBound(); + } + + [Fact] + [ActiveIssue(25498)] + public void TestPublicImportsExpectingProtectedExportsFromCatalog() + { + var cat = CatalogFactory.CreateDefaultAttributed(); + var container = new CompositionContainer(cat); + container.GetExportedValue().VerifyIsBound(); + } + + [Fact] + [ActiveIssue(25498)] + public void TestInternalImportsExpectingProtectedExportsFromCatalog() + { + var cat = CatalogFactory.CreateDefaultAttributed(); + var container = new CompositionContainer(cat); + container.GetExportedValue().VerifyIsBound(); + } + + [Fact] + [ActiveIssue(25498)] + public void TestPublicImportsExpectingProtectedInternalExportsFromCatalog() + { + var cat = CatalogFactory.CreateDefaultAttributed(); + var container = new CompositionContainer(cat); + container.GetExportedValue().VerifyIsBound(); + } + + [Fact] + [ActiveIssue(25498)] + public void TestInternalImportsExpectingProtectedInternalExportsFromCatalog() + { + var cat = CatalogFactory.CreateDefaultAttributed(); + var container = new CompositionContainer(cat); + container.GetExportedValue().VerifyIsBound(); + } + + [Fact] + [ActiveIssue(25498)] + public void TestPublicImportsExpectingPrivateExportsFromCatalog() + { + var cat = CatalogFactory.CreateDefaultAttributed(); + var container = new CompositionContainer(cat); + container.GetExportedValue().VerifyIsBound(); + } + + [Fact] + [ActiveIssue(25498)] + public void TestInternalImportsExpectingPrivateExportsFromCatalog() + { + var cat = CatalogFactory.CreateDefaultAttributed(); + var container = new CompositionContainer(cat); + container.GetExportedValue().VerifyIsBound(); + } + } + + public interface IData { int X { get; set; } } + + public class PrivateDataImportExportWithContract + { + public PrivateDataImportExportWithContract(int x) + { + Out = new PrivateDataType() { X = x }; + } + public int X { get { return In.X; } } + [Export("a")] + PrivateDataType Out { get; set; } + [Import("a")] + IData In { get; set; } + } + + public class PrivateDataImportExport + { + public PrivateDataImportExport(int x) + { + Out = new PrivateDataType() { X = x }; + } + public int X { get { return In.X; } } + [Export] + PrivateDataType Out { get; set; } + [Import] + PrivateDataType In { get; set; } + } + + class PrivateDataType + { + public int X { get; set; } + } + + public class AllPrivateNoAttribute + { + public AllPrivateNoAttribute(int exportA, int exportB) { ExportA = exportA; ExportB = exportB; } + + public int PublicImportA { get { return ImportA; } } + public int PublicImportB { get { return ImportB; } } + + [Import("a")] + int ImportA { get; set; } + [Import("b")] + int ImportB { get; set; } + [Export("a")] + int ExportA { get; set; } + [Export("b")] + int ExportB { get; set; } + } + public class AllPrivateNoAttributeImportOnly + { + public int PublicImportA { get { return ImportA; } } + public int PublicImportB { get { return ImportB; } } + + [Import("a")] + int ImportA { get; set; } + [Import("b")] + int ImportB { get; set; } + } + + public class AllPrivate + { + public AllPrivate(int exportA, int exportB) { ExportA = exportA; ExportB = exportB; } + + public int PublicImportA { get { return ImportA; } } + public int PublicImportB { get { return ImportB; } } + + [Import("a")] + int ImportA { get; set; } + [Import("b")] + int ImportB { get; set; } + [Export("a")] + int ExportA { get; set; } + [Export("b")] + int ExportB { get; set; } + } + + public class AllPrivateImportOnly + { + public int PublicImportA { get { return ImportA; } } + public int PublicImportB { get { return ImportB; } } + + [Import("a")] + int ImportA { get; set; } + [Import("b")] + int ImportB { get; set; } + } + + public class AllPrivateExportOnly + { + public AllPrivateExportOnly(int exportA, int exportB) { ExportA = exportA; ExportB = exportB; } + + [Export("a")] + int ExportA { get; set; } + [Export("b")] + int ExportB { get; set; } + } + + public class AllPublic + { + [Import("a")] + public int ImportA { get; set; } + [Import("b")] + public int ImportB { get; set; } + [Export("a")] + public int ExportA { get; set; } + [Export("b")] + public int ExportB { get; set; } + } + public class AllPublicImportOnly + { + [Import("a")] + public int ImportA { get; set; } + [Import("b")] + public int ImportB { get; set; } + } + public class AllPublicExportOnly + { + [Export("a")] + public int ExportA { get; set; } + [Export("b")] + public int ExportB { get; set; } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/AttributedModel/AttributedModelCompositionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/AttributedModel/AttributedModelCompositionTests.cs new file mode 100644 index 0000000000..663898fca1 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/AttributedModel/AttributedModelCompositionTests.cs @@ -0,0 +1,461 @@ +// 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.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.Linq; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition.AttributedModel +{ + public class AttributedModelCompositionTests + { + [Fact] + public void PartContainingOnlyStaticExports_ShouldNotCauseInstanceToBeCreated() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(HasOnlyStaticExports)); + + Assert.Equal("Field", container.GetExportedValue("Field")); + Assert.Equal("Property", container.GetExportedValue("Property")); + Assert.NotNull(container.GetExportedValue>("Method")()); + + Assert.False(HasOnlyStaticExports.InstanceCreated); + } + + [Fact] + public void ExportOnAbstractBase_DoesNotReturnNull() + { // 499393 - Classes inheriting from an exported + // abstract class are exported as 'null' + + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + + var definition = PartDefinitionFactory.CreateAttributed(typeof(Derived)); + batch.AddPart(definition.CreatePart()); + container.Compose(batch); + + Assert.NotNull(container.GetExportedValueOrDefault()); + } + + [Fact] + public void ReadOnlyFieldImport_ShouldThrowComposition() + { + var importer = PartFactory.CreateAttributed(new ReadOnlyPropertyImport()); + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + batch.AddExportedValue("readonly", "new value"); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotActivate, + RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + + [Fact] + public void ReadOnlyPropertyImport_ShouldThrowComposition() + { + var importer = PartFactory.CreateAttributed(new ReadOnlyPropertyImport()); + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + batch.AddExportedValue("readonly", "new value"); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotActivate, + RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + + [Fact] + public void WriteOnlyPropertyExport_ShouldThrowComposition() + { + var importer = PartFactory.CreateAttributed(new WriteOnlyPropertyExport()); + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + container.Compose(batch); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotGetExportedValue, () => + { + container.GetExportedValue("writeonly"); + }); + } + + [Fact] + public void ImportValueMismatchFromInt32ToDateTime() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(new ImportValueTypes()); + batch.AddExportedValue("DateTime", typeof(DateTime), 42); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotSetImport, + ErrorId.ReflectionModel_ImportNotAssignableFromExport, + RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + + [Fact] + public void ImportValueMismatchFromInt16ToInt32() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + + batch.AddPart(new ImportValueTypes()); + batch.AddExportedValue("Int32", typeof(Int32), (short)10); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotSetImport, + ErrorId.ReflectionModel_ImportNotAssignableFromExport, + RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + + } + + [Fact] + public void ImportValueMismatchFromInt32ToInt16() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + + batch.AddPart(new ImportValueTypes()); + batch.AddExportedValue("Int16", typeof(Int16), (int)10); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotSetImport, + ErrorId.ReflectionModel_ImportNotAssignableFromExport, + RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + + [Fact] + public void ImportValueMismatchFromInt32ToUInt32() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + + batch.AddPart(new ImportValueTypes()); + batch.AddExportedValue("UInt32", typeof(UInt32), (int)10); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotSetImport, + ErrorId.ReflectionModel_ImportNotAssignableFromExport, + RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + + [Fact] + public void ImportValueMismatchFromUInt32ToInt32() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + + batch.AddPart(new ImportValueTypes()); + batch.AddExportedValue("Int32", typeof(Int32), (uint)10); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotSetImport, + ErrorId.ReflectionModel_ImportNotAssignableFromExport, + RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + + [Fact] + public void ImportValueMismatchFromInt32ToInt64() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + + batch.AddPart(new ImportValueTypes()); + batch.AddExportedValue("Int64", typeof(Int64), (int)10); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotSetImport, + ErrorId.ReflectionModel_ImportNotAssignableFromExport, + RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + + [Fact] + public void ImportValueMismatchFromSingleToDouble() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + + batch.AddPart(new ImportValueTypes()); + batch.AddExportedValue("Double", typeof(Double), (float)10); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotSetImport, + ErrorId.ReflectionModel_ImportNotAssignableFromExport, + RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + + [Fact] + public void ImportValueMismatchFromDoubleToSingle() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + + batch.AddPart(new ImportValueTypes()); + batch.AddExportedValue("Single", typeof(Single), (double)10); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotSetImport, + ErrorId.ReflectionModel_ImportNotAssignableFromExport, + RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + + [Fact] + public void ImportValueMismatchFromSingleToInt32() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + + batch.AddPart(new ImportValueTypes()); + batch.AddExportedValue("Int32", typeof(Int32), (float)10); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotSetImport, + ErrorId.ReflectionModel_ImportNotAssignableFromExport, + RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + + [Fact] + public void ImportValueMismatchFromInt32ToSingle() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + + batch.AddPart(new ImportValueTypes()); + batch.AddExportedValue("Single", typeof(Single), (int)10); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotSetImport, + ErrorId.ReflectionModel_ImportNotAssignableFromExport, + RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + + [Fact] + [ActiveIssue(25498)] + public void MemberExports() + { + var exporter = PartFactory.CreateAttributed(new ObjectWithMemberExports()); + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(exporter); + container.Compose(batch); + + var filedExports = container.GetExports("field"); + ExportsAssert.AreEqual(filedExports, 1, 5); + + var readonlyExports = container.GetExports("readonly"); + ExportsAssert.AreEqual(readonlyExports, 2, 6); + + var propertyExports = container.GetExports("property"); + ExportsAssert.AreEqual(propertyExports, 3, 7); + + var methodExports = container.GetExportedValues>("func").Select(f => f(0)); + EnumerableAssert.AreEqual(methodExports, 4, 8); + } + + [Fact] + public void TestExportedValueCachesValue() + { + var container = ContainerFactory.Create(); + var exporter = new ExportsMutableProperty(); + exporter.Property = "Value1"; + + container.ComposeParts(exporter); + + Assert.Equal("Value1", container.GetExportedValue("Property")); + + exporter.Property = "Value2"; + + // Exported value should have been cached and so it shouldn't change + Assert.Equal("Value1", container.GetExportedValue("Property")); + } + + [Fact] + [ActiveIssue(739354)] + public void TestExportedValueCachesNullValue() + { + var container = ContainerFactory.Create(); + var exporter = new ExportsMutableProperty(); + exporter.Property = null; + + container.ComposeParts(exporter); + + Assert.Null(container.GetExportedValue("Property")); + + exporter.Property = "Value1"; + + // Exported value should have been cached and so it shouldn't change + Assert.Null(container.GetExportedValue("Property")); + } + + public class ExportsMutableProperty + { + [Export("Property")] + public string Property { get; set; } + } + + public class ReadOnlyFieldImport + { + [Import("readonly")] + public readonly string Value = "Value"; + } + + public class ReadOnlyPropertyImport + { + [Import("readonly")] + public string Value + { + get { return "Value"; } + } + } + + public class WriteOnlyPropertyExport + { + [Export("writeonly")] + public string Value + { + set { } + } + } + + public class ObjectWithMemberExports + { + [Export("field")] + public static int staticField = 1; + + [Export("readonly")] + public static readonly int staticReadonly = 2; + + [Export("property")] + public static int StaticProperty { get { return 3; } } + + [Export("func")] + public static int StaticMethod(int arg1) { return 4; } + + [Export("field")] + public int instanceField = 5; + + [Export("readonly")] + public readonly int instanceReadonly = 6; + + [Export("property")] + public int InstanceProperty { get { return 7; } } + + [Export("func")] + public int InstanceMethod(int arg1) { return 8; } + } + + public class HasOnlyStaticExports + { + public static bool InstanceCreated; + + public HasOnlyStaticExports() + { + InstanceCreated = true; + } + + [Export("Field")] + public static string Field = "Field"; + + [Export("Property")] + public static string Property + { + get { return "Property"; } + } + + [Export("Method")] + public static string Method() + { + return "Method"; + } + } + + [InheritedExport(typeof(Base))] + public abstract class Base + { + } + + [Export(typeof(Derived))] + public class Derived : Base + { + } + + public class ImportValueTypes + { + [Import("Int16", AllowDefault = true)] + public short Int16 + { + get; + set; + } + + [Import("Int32", AllowDefault = true)] + public int Int32 + { + get; + set; + } + + [Import("UInt32", AllowDefault = true)] + public uint UInt32 + { + get; + set; + } + + [Import("Int64", AllowDefault = true)] + public long Int64 + { + get; + set; + } + + [Import("Single", AllowDefault = true)] + public float Single + { + get; + set; + } + + [Import("Double", AllowDefault = true)] + public double Double + { + get; + set; + } + + [Import("DateTime", AllowDefault = true)] + public DateTime DateTime + { + get; + set; + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/AttributedModel/AttributedModelDiscoveryTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/AttributedModel/AttributedModelDiscoveryTests.cs new file mode 100644 index 0000000000..06d093ffef --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/AttributedModel/AttributedModelDiscoveryTests.cs @@ -0,0 +1,341 @@ +// 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.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.ComponentModel.Composition.ReflectionModel; +using System.Linq; +using System.Reflection; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition.AttributedModel +{ + public class AttributedModelDiscoveryTests + { + [Fact] + public void CreatePartDefinition_TypeWithExports_ShouldHaveMultipleExports() + { + var definition = CreateDefinition(typeof(PublicComponentWithPublicExports)); + EnumerableAssert.AreEqual(definition.ExportDefinitions.Select(e => e.ContractName), "PublicField", "PublicProperty", "PublicDelegate"); + } + + public abstract class BaseClassWithPropertyExports + { + [Export("MyPropBase")] + public virtual int MyProp { get; set; } + } + + public class DerivedClassWithInheritedPropertyExports : BaseClassWithPropertyExports + { + public override int MyProp { get; set; } + } + + [ActiveIssue(551341)] + [Fact] + public void ShowIssueWithVirtualPropertiesInReflectionAPI() + { + PropertyInfo propInfo = typeof(BaseClassWithPropertyExports).GetProperty("MyProp"); + + // pi.GetCustomAttributes does not find the inherited attributes + var c1 = propInfo.GetCustomAttributes(true); + + // Attribute.GetCustomAttributes does find the inherited attributes + var c2 = Attribute.GetCustomAttributes(propInfo, true); + + // This seems like it should be a bug in the reflection API's... + Assert.NotEqual(c1, c2); + } + + [Fact] + public void CreatePartDefinition_TypeWithImports_ShouldHaveMultipleImports() + { + var definition = CreateDefinition(typeof(PublicImportsExpectingPublicExports)); + EnumerableAssert.AreEqual(definition.ImportDefinitions.Cast() + .Select(i => i.ContractName), "PublicField", "PublicProperty", "PublicDelegate", "PublicIGetString"); + } + + public class AnyImplicitExport + { + + } + + [Fact] + public void CreatePartDefinition_AnyType_ShouldHaveMetadataWithAnyImplicitCreationPolicy() + { + var definition = CreateDefinition(typeof(AnyImplicitExport)); + + Assert.Equal(CreationPolicy.Any, definition.Metadata.GetValue(CompositionConstants.PartCreationPolicyMetadataName)); + } + + [PartCreationPolicy(CreationPolicy.Any)] + public class AnyExport + { + + } + + [Fact] + public void CreatePartDefinition_AnyType_ShouldHaveMetadataWithAnyCreationPolicy() + { + var definition = CreateDefinition(typeof(AnyExport)); + + Assert.Equal(CreationPolicy.Any, definition.Metadata.GetValue(CompositionConstants.PartCreationPolicyMetadataName)); + } + + [PartCreationPolicy(CreationPolicy.Shared)] + public class SharedExport + { + + } + + [Fact] + public void CreatePartDefinition_SharedType_ShouldHaveMetadataWithSharedCreationPolicy() + { + var definition = CreateDefinition(typeof(SharedExport)); + + Assert.Equal(CreationPolicy.Shared, definition.Metadata.GetValue(CompositionConstants.PartCreationPolicyMetadataName)); + } + + [PartCreationPolicy(CreationPolicy.NonShared)] + public class NonSharedExport + { + + } + + [Fact] + public void CreatePartDefinition_NonSharedType_ShouldHaveMetadataWithNonSharedCreationPolicy() + { + var definition = CreateDefinition(typeof(NonSharedExport)); + + Assert.Equal(CreationPolicy.NonShared, definition.Metadata.GetValue(CompositionConstants.PartCreationPolicyMetadataName)); + } + + [PartMetadata(CompositionConstants.PartCreationPolicyMetadataName, CreationPolicy.NonShared)] + [PartMetadata("ShouldNotBeIgnored", "Value")] + public class PartWithIgnoredMetadata + { + } + + [Fact] + public void CreatePartDefinition_SharedTypeMarkedWithNonSharedMetadata_ShouldHaveMetadatWithSharedCreationPolicy() + { + // Type should just contain all the default settings of Shared + var definition = CreateDefinition(typeof(PartWithIgnoredMetadata)); + + // CompositionConstants.PartCreationPolicyMetadataName should be ignored + Assert.NotEqual(CreationPolicy.NonShared, definition.Metadata.GetValue(CompositionConstants.PartCreationPolicyMetadataName)); + + // Key ShouldNotBeIgnored should actully be in the dictionary + Assert.Equal("Value", definition.Metadata["ShouldNotBeIgnored"]); + } + + [PartMetadata("BaseOnlyName", 1)] + [PartMetadata("OverrideName", 2)] + public class BasePartWithMetdata + { + + } + + [PartMetadata("DerivedOnlyName", 3)] + [PartMetadata("OverrideName", 4)] + public class DerivedPartWithMetadata : BasePartWithMetdata + { + + } + + [Fact] + public void CreatePartDefinition_InheritedPartMetadata_ShouldNotContainPartMetadataFromBase() + { + var definition = CreateDefinition(typeof(DerivedPartWithMetadata)); + + Assert.False(definition.Metadata.ContainsKey("BaseOnlyName"), "Should not inherit part metadata from base."); + Assert.Equal(3, definition.Metadata["DerivedOnlyName"]); + Assert.Equal(4, definition.Metadata["OverrideName"]); + } + + [Fact] + public void CreatePartDefinition_NoMarkedOrDefaultConstructorAsPartTypeArgument_ShouldSetConstructorToNull() + { + var definition = CreateDefinition(typeof(ClassWithNoMarkedOrDefaultConstructor)); + + Assert.Null(definition.GetConstructor()); + } + + [Fact] + public void CreatePartDefinition_MultipleMarkedConstructorsAsPartTypeArgument_ShouldSetConstructors() + { + var definition = CreateDefinition(typeof(ClassWithMultipleMarkedConstructors)); + + Assert.Null(definition.GetConstructor()); + } + + [Fact] + public void CreatePartDefinition_OneMarkedConstructorsAsPartTypeArgument_ShouldSetConstructorToMarked() + { + var definition = CreateDefinition(typeof(SimpleConstructorInjectedObject)); + + ConstructorInfo constructor = definition.GetConstructor(); + Assert.NotNull(constructor); + Assert.Equal(typeof(SimpleConstructorInjectedObject).GetConstructors()[0], constructor); + Assert.Equal(constructor.GetParameters().Length, definition.ImportDefinitions.OfType().Count()); + } + + [Fact] + public void CreatePartDefinition_OneDefaultConstructorAsPartTypeArgument_ShouldSetConstructorToDefault() + { + var definition = CreateDefinition(typeof(PublicComponentWithPublicExports)); + + ConstructorInfo constructor = definition.GetConstructor(); + Assert.NotNull(constructor); + + Assert.Empty(constructor.GetParameters()); + Assert.Empty(definition.ImportDefinitions.OfType()); + } + + [Fact] + public void CreatePartDefinition_OneMarkedAndOneDefaultConstructorsAsPartTypeArgument_ShouldSetConstructorToMarked() + { + var definition = CreateDefinition(typeof(ClassWithOneMarkedAndOneDefaultConstructor)); + var marked = typeof(ClassWithOneMarkedAndOneDefaultConstructor).GetConstructors()[0]; + Assert.True(marked.IsDefined(typeof(ImportingConstructorAttribute), false)); + + ConstructorInfo constructor = definition.GetConstructor(); + Assert.NotNull(constructor); + + Assert.Equal(marked, constructor); + Assert.Equal(marked.GetParameters().Length, definition.ImportDefinitions.OfType().Count()); + } + + [Fact] + public void CreatePartDefinition_NoConstructorBecauseStatic_ShouldHaveNullConstructor() + { + var definition = CreateDefinition(typeof(StaticExportClass)); + + ConstructorInfo constructor = definition.GetConstructor(); + Assert.Null(constructor); + + Assert.Empty(definition.ImportDefinitions.OfType()); + } + + [Fact] + public void CreatePartDefinition_TwoZeroParameterConstructors_ShouldPickNonStaticOne() + { + var definition = CreateDefinition(typeof(ClassWithTwoZeroParameterConstructors)); + + ConstructorInfo constructor = definition.GetConstructor(); + Assert.NotNull(constructor); + Assert.False(constructor.IsStatic); + + Assert.Empty(definition.ImportDefinitions.OfType()); + } + + [Fact] + [ActiveIssue(25498)] + public void IsDiscoverable() + { + var expectations = new ExpectationCollection(); + expectations.Add(typeof(ClassWithTwoZeroParameterConstructors), true); + expectations.Add(typeof(SimpleConstructorInjectedObject), true); + expectations.Add(typeof(StaticExportClass), true); + expectations.Add(typeof(PublicComponentWithPublicExports), true); + expectations.Add(typeof(ClassWithMultipleMarkedConstructors), true); + expectations.Add(typeof(ClassWithNoMarkedOrDefaultConstructor), true); + expectations.Add(typeof(ClassWhichOnlyHasDefaultConstructor), false); + expectations.Add(typeof(ClassWithOnlyHasImportingConstructorButInherits), true); + expectations.Add(typeof(ClassWithOnlyHasMultipleImportingConstructorButInherits), true); + + foreach (var e in expectations) + { + var definition = AttributedModelDiscovery.CreatePartDefinitionIfDiscoverable(e.Input, (ICompositionElement)null); + + bool result = (definition != null); + + Assert.Equal(e.Output, result); + } + } + + [Fact] + [ActiveIssue(25498)] + public void CreatePartDefinition_EnsureIsDiscoverable() + { + var expectations = new ExpectationCollection(); + expectations.Add(typeof(ClassWithTwoZeroParameterConstructors), true); + expectations.Add(typeof(SimpleConstructorInjectedObject), true); + expectations.Add(typeof(StaticExportClass), true); + expectations.Add(typeof(PublicComponentWithPublicExports), true); + expectations.Add(typeof(ClassWithMultipleMarkedConstructors), true); + expectations.Add(typeof(ClassWithNoMarkedOrDefaultConstructor), true); + expectations.Add(typeof(ClassWhichOnlyHasDefaultConstructor), false); + expectations.Add(typeof(ClassWithOnlyHasImportingConstructorButInherits), true); + expectations.Add(typeof(ClassWithOnlyHasMultipleImportingConstructorButInherits), true); + + foreach (var e in expectations) + { + var definition = AttributedModelServices.CreatePartDefinition(e.Input, null, true); + + bool result = (definition != null); + + Assert.Equal(e.Output, result); + } + } + + [Fact] + public void CreatePartDefinition_NotEnsureIsDiscoverable() + { + var expectations = new ExpectationCollection(); + expectations.Add(typeof(ClassWithTwoZeroParameterConstructors), true); + expectations.Add(typeof(SimpleConstructorInjectedObject), true); + expectations.Add(typeof(StaticExportClass), true); + expectations.Add(typeof(PublicComponentWithPublicExports), true); + expectations.Add(typeof(ClassWithMultipleMarkedConstructors), true); + expectations.Add(typeof(ClassWithNoMarkedOrDefaultConstructor), true); + expectations.Add(typeof(ClassWhichOnlyHasDefaultConstructor), false); + expectations.Add(typeof(ClassWithOnlyHasImportingConstructorButInherits), true); + expectations.Add(typeof(ClassWithOnlyHasMultipleImportingConstructorButInherits), true); + + foreach (var e in expectations) + { + var definition = AttributedModelServices.CreatePartDefinition(e.Input, null, false); + Assert.NotNull(definition); + } + } + + [Fact] + public void CreatePart_ObjectInstance_ShouldProduceSharedPart() + { + var part = AttributedModelServices.CreatePart(typeof(MyExport)); + + Assert.Equal(CreationPolicy.Shared, part.Metadata.GetValue(CompositionConstants.PartCreationPolicyMetadataName)); + } + + private ReflectionComposablePartDefinition CreateDefinition(Type type) + { + var definition = AttributedModelDiscovery.CreatePartDefinition(type, null, false, ElementFactory.Create()); + + Assert.Equal(type, definition.GetPartType()); + + return definition; + } + + [InheritedExport] + [InheritedExport] + [InheritedExport] + [Export] + [InheritedExport] + [InheritedExport] + [InheritedExport] + public class DuplicateMixedExporter1 + { + } + + [Fact] + [ActiveIssue(710352)] + public void MixedDuplicateExports_ShouldOnlyCollapseInheritedExport() + { + var def = AttributedModelServices.CreatePartDefinition(typeof(DuplicateMixedExporter1), null); + Assert.Equal(2, def.ExportDefinitions.Count()); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/AttributedModel/INotifyImportTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/AttributedModel/INotifyImportTests.cs new file mode 100644 index 0000000000..914a044d4c --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/AttributedModel/INotifyImportTests.cs @@ -0,0 +1,610 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using Xunit; + +namespace System.ComponentModel.Composition.AttributedModel +{ + public class INotifyImportTests + { + [Export(typeof(PartWithoutImports))] + public class PartWithoutImports : IPartImportsSatisfiedNotification + { + public bool ImportsSatisfiedInvoked { get; private set; } + public void OnImportsSatisfied() + { + this.ImportsSatisfiedInvoked = true; + } + } + + [Fact] + public void ImportsSatisfiedOnComponentWithoutImports() + { + CompositionContainer container = ContainerFactory.CreateWithAttributedCatalog(typeof(PartWithoutImports)); + + PartWithoutImports partWithoutImports = container.GetExportedValue(); + Assert.NotNull(partWithoutImports); + + Assert.True(partWithoutImports.ImportsSatisfiedInvoked); + + } + + [Fact] + public void ImportCompletedTest() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + var entrypoint = new UpperCaseStringComponent(); + + batch.AddParts(new LowerCaseString("abc"), entrypoint); + container.Compose(batch); + + batch = new CompositionBatch(); + batch.AddParts(new object()); + container.Compose(batch); + + Assert.Equal(entrypoint.LowerCaseStrings.Count, 1); + Assert.Equal(entrypoint.ImportCompletedCallCount, 1); + Assert.Equal(entrypoint.UpperCaseStrings.Count, 1); + Assert.Equal(entrypoint.LowerCaseStrings[0].Value.String, "abc"); + Assert.Equal(entrypoint.UpperCaseStrings[0], "ABC"); + } + + [Fact] + public void ImportCompletedWithRecomposing() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + var entrypoint = new UpperCaseStringComponent(); + + batch.AddParts(new LowerCaseString("abc"), entrypoint); + container.Compose(batch); + + Assert.Equal(entrypoint.LowerCaseStrings.Count, 1); + Assert.Equal(entrypoint.ImportCompletedCallCount, 1); + Assert.Equal(entrypoint.UpperCaseStrings.Count, 1); + Assert.Equal(entrypoint.LowerCaseStrings[0].Value.String, "abc"); + Assert.Equal(entrypoint.UpperCaseStrings[0], "ABC"); + + // Add another component to verify recomposing + batch = new CompositionBatch(); + batch.AddParts(new LowerCaseString("def")); + container.Compose(batch); + + Assert.Equal(entrypoint.LowerCaseStrings.Count, 2); + Assert.Equal(entrypoint.ImportCompletedCallCount, 2); + Assert.Equal(entrypoint.UpperCaseStrings.Count, 2); + Assert.Equal(entrypoint.LowerCaseStrings[1].Value.String, "def"); + Assert.Equal(entrypoint.UpperCaseStrings[1], "DEF"); + + // Verify that adding a random component doesn't cause + // the OnImportsSatisfied to be called again. + batch = new CompositionBatch(); + batch.AddParts(new object()); + container.Compose(batch); + + Assert.Equal(entrypoint.LowerCaseStrings.Count, 2); + Assert.Equal(entrypoint.ImportCompletedCallCount, 2); + Assert.Equal(entrypoint.UpperCaseStrings.Count, 2); + } + + [Fact] + [ActiveIssue(700940)] + public void ImportCompletedUsingSatisfyImportsOnce() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + var entrypoint = new UpperCaseStringComponent(); + var entrypointPart = AttributedModelServices.CreatePart(entrypoint); + + batch.AddParts(new LowerCaseString("abc")); + container.Compose(batch); + container.SatisfyImportsOnce(entrypointPart); + + Assert.Equal(1, entrypoint.LowerCaseStrings.Count); + Assert.Equal(1, entrypoint.ImportCompletedCallCount); + Assert.Equal(1, entrypoint.UpperCaseStrings.Count); + Assert.Equal("abc", entrypoint.LowerCaseStrings[0].Value.String); + Assert.Equal("ABC", entrypoint.UpperCaseStrings[0]); + + batch = new CompositionBatch(); + batch.AddParts(new object()); + container.Compose(batch); + container.SatisfyImportsOnce(entrypointPart); + + Assert.Equal(1, entrypoint.LowerCaseStrings.Count); + Assert.Equal(1, entrypoint.ImportCompletedCallCount); + Assert.Equal(1, entrypoint.UpperCaseStrings.Count); + Assert.Equal("abc", entrypoint.LowerCaseStrings[0].Value.String); + Assert.Equal("ABC", entrypoint.UpperCaseStrings[0]); + + batch.AddParts(new LowerCaseString("def")); + container.Compose(batch); + container.SatisfyImportsOnce(entrypointPart); + + Assert.Equal(2, entrypoint.LowerCaseStrings.Count); + Assert.Equal(2, entrypoint.ImportCompletedCallCount); + Assert.Equal(2, entrypoint.UpperCaseStrings.Count); + Assert.Equal("abc", entrypoint.LowerCaseStrings[0].Value.String); + Assert.Equal("ABC", entrypoint.UpperCaseStrings[0]); + Assert.Equal("def", entrypoint.LowerCaseStrings[1].Value.String); + Assert.Equal("DEF", entrypoint.UpperCaseStrings[1]); + } + + [Fact] + [ActiveIssue(654513)] + public void ImportCompletedCalledAfterAllImportsAreFullyComposed() + { + int importSatisfationCount = 0; + var importer1 = new MyEventDrivenFullComposedNotifyImporter1(); + var importer2 = new MyEventDrivenFullComposedNotifyImporter2(); + + Action verificationAction = (object sender, EventArgs e) => + { + Assert.True(importer1.AreAllImportsFullyComposed); + Assert.True(importer2.AreAllImportsFullyComposed); + ++importSatisfationCount; + }; + + importer1.ImportsSatisfied += new EventHandler(verificationAction); + importer2.ImportsSatisfied += new EventHandler(verificationAction); + + // importer1 added first + var batch = new CompositionBatch(); + batch.AddParts(importer1, importer2); + + var container = ContainerFactory.Create(); + container.ComposeExportedValue(container); + container.Compose(batch); + Assert.Equal(2, importSatisfationCount); + + // importer2 added first + importSatisfationCount = 0; + batch = new CompositionBatch(); + batch.AddParts(importer2, importer1); + + container = ContainerFactory.Create(); + container.ComposeExportedValue(container); + container.Compose(batch); + Assert.Equal(2, importSatisfationCount); + } + + [Fact] + public void ImportCompletedAddPartAndBindComponent() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + batch.AddParts(new CallbackImportNotify(delegate + { + batch = new CompositionBatch(); + batch.AddPart(new object()); + container.Compose(batch); + })); + + container.Compose(batch); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void ImportCompletedChildNeedsParentContainer() + { + var cat = CatalogFactory.CreateDefaultAttributed(); + var parent = new CompositionContainer(cat); + + CompositionBatch parentBatch = new CompositionBatch(); + CompositionBatch childBatch = new CompositionBatch(); + CompositionBatch child2Batch = new CompositionBatch(); + + parentBatch.AddExportedValue(parent); + parent.Compose(parentBatch); + var child = new CompositionContainer(parent); + var child2 = new CompositionContainer(parent); + + var parentImporter = new MyNotifyImportImporter(parent); + var childImporter = new MyNotifyImportImporter(child); + var child2Importer = new MyNotifyImportImporter(child2); + + parentBatch = new CompositionBatch(); + parentBatch.AddPart(parentImporter); + childBatch.AddPart(childImporter); + child2Batch.AddPart(child2Importer); + + parent.Compose(parentBatch); + child.Compose(childBatch); + child2.Compose(child2Batch); + + Assert.Equal(1, parentImporter.ImportCompletedCallCount); + Assert.Equal(1, childImporter.ImportCompletedCallCount); + Assert.Equal(1, child2Importer.ImportCompletedCallCount); + + MyNotifyImportExporter parentExporter = parent.GetExportedValue("MyNotifyImportExporter"); + Assert.Equal(1, parentExporter.ImportCompletedCallCount); + + MyNotifyImportExporter childExporter = child.GetExportedValue("MyNotifyImportExporter"); + Assert.Equal(1, childExporter.ImportCompletedCallCount); + + MyNotifyImportExporter child2Exporter = child2.GetExportedValue("MyNotifyImportExporter"); + Assert.Equal(1, child2Exporter.ImportCompletedCallCount); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void ImportCompletedChildDoesnotNeedParentContainer() + { + var cat = CatalogFactory.CreateDefaultAttributed(); + var parent = new CompositionContainer(cat); + + CompositionBatch parentBatch = new CompositionBatch(); + CompositionBatch childBatch = new CompositionBatch(); + + parentBatch.AddExportedValue(parent); + parent.Compose(parentBatch); + + var child = new CompositionContainer(parent); + + var parentImporter = new MyNotifyImportImporter(parent); + var childImporter = new MyNotifyImportImporter(child); + + parentBatch = new CompositionBatch(); + parentBatch.AddPart(parentImporter); + + childBatch.AddParts(childImporter, new MyNotifyImportExporter()); + + child.Compose(childBatch); + + Assert.Equal(0, parentImporter.ImportCompletedCallCount); + Assert.Equal(1, childImporter.ImportCompletedCallCount); + + // Parent will become bound at this point. + MyNotifyImportExporter parentExporter = parent.GetExportedValue("MyNotifyImportExporter"); + parent.Compose(parentBatch); + Assert.Equal(1, parentImporter.ImportCompletedCallCount); + Assert.Equal(1, parentExporter.ImportCompletedCallCount); + + MyNotifyImportExporter childExporter = child.GetExportedValue("MyNotifyImportExporter"); + Assert.Equal(1, childExporter.ImportCompletedCallCount); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void ImportCompletedBindChildIndirectlyThroughParentContainerBind() + { + var cat = CatalogFactory.CreateDefaultAttributed(); + var parent = new CompositionContainer(cat); + + CompositionBatch parentBatch = new CompositionBatch(); + CompositionBatch childBatch = new CompositionBatch(); + + parentBatch.AddExportedValue(parent); + parent.Compose(parentBatch); + var child = new CompositionContainer(parent); + + var parentImporter = new MyNotifyImportImporter(parent); + var childImporter = new MyNotifyImportImporter(child); + + parentBatch = new CompositionBatch(); + parentBatch.AddPart(parentImporter); + childBatch.AddParts(childImporter, new MyNotifyImportExporter()); + + parent.Compose(parentBatch); + child.Compose(childBatch); + + Assert.Equal(1, parentImporter.ImportCompletedCallCount); + Assert.Equal(1, childImporter.ImportCompletedCallCount); + + MyNotifyImportExporter parentExporter = parent.GetExportedValue("MyNotifyImportExporter"); + Assert.Equal(1, parentExporter.ImportCompletedCallCount); + + MyNotifyImportExporter childExporter = child.GetExportedValue("MyNotifyImportExporter"); + Assert.Equal(1, childExporter.ImportCompletedCallCount); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void ImportCompletedGetExportedValueLazy() + { + var cat = CatalogFactory.CreateDefaultAttributed(); + CompositionContainer container = new CompositionContainer(cat); + + NotifyImportExportee.InstanceCount = 0; + NotifyImportExportsLazy notifyee = container.GetExportedValue("NotifyImportExportsLazy"); + Assert.NotNull(notifyee); + Assert.NotNull(notifyee.Imports); + Assert.True(notifyee.NeedRefresh); + Assert.Equal(3, notifyee.Imports.Count); + Assert.Equal(0, NotifyImportExportee.InstanceCount); + Assert.Equal(0, notifyee.realImports.Count); + Assert.Equal(2, notifyee.RealImports.Count); + Assert.Equal(1, notifyee.RealImports[0].Id); + Assert.Equal(3, notifyee.RealImports[1].Id); + Assert.Equal(2, NotifyImportExportee.InstanceCount); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void ImportCompletedGetExportedValueEager() + { + var cat = CatalogFactory.CreateDefaultAttributed(); + CompositionContainer container = new CompositionContainer(cat); + + NotifyImportExportee.InstanceCount = 0; + var notifyee = container.GetExportedValue("NotifyImportExportsEager"); + Assert.NotNull(notifyee); + Assert.NotNull(notifyee.Imports); + Assert.Equal(3, notifyee.Imports.Count); + Assert.Equal(2, NotifyImportExportee.InstanceCount); + Assert.Equal(2, notifyee.realImports.Count); + Assert.Equal(2, notifyee.RealImports.Count); + Assert.Equal(1, notifyee.RealImports[0].Id); + Assert.Equal(3, notifyee.RealImports[1].Id); + Assert.Equal(2, NotifyImportExportee.InstanceCount); + } + } + + public class NotifyImportExportee + { + public NotifyImportExportee(int id) + { + Id = id; + InstanceCount++; + } + + public int Id { get; set; } + + public static int InstanceCount { get; set; } + } + + public class NotifyImportExporter + { + + public NotifyImportExporter() + { + } + + [Export()] + [ExportMetadata("Filter", false)] + public NotifyImportExportee Export1 + { + get + { + return new NotifyImportExportee(1); + } + } + + [Export()] + [ExportMetadata("Filter", true)] + public NotifyImportExportee Export2 + { + get + { + return new NotifyImportExportee(2); + } + } + + [Export()] + [ExportMetadata("Filter", false)] + public NotifyImportExportee Export3 + { + get + { + return new NotifyImportExportee(3); + } + } + + } + + [Export("NotifyImportExportsLazy")] + public class NotifyImportExportsLazy : IPartImportsSatisfiedNotification + { + public NotifyImportExportsLazy() + { + NeedRefresh = false; + } + + [ImportMany(typeof(NotifyImportExportee))] + public Collection>> Imports { get; set; } + + public bool NeedRefresh { get; set; } + + public void OnImportsSatisfied() + { + NeedRefresh = true; + } + + internal Collection realImports = new Collection(); + + public Collection RealImports + { + get + { + if (NeedRefresh) + { + realImports.Clear(); + foreach (var import in Imports) + { + if (!((bool)import.Metadata["Filter"])) + { + realImports.Add(import.Value); + } + } + NeedRefresh = false; + } + return realImports; + } + } + } + + [Export("NotifyImportExportsEager")] + public class NotifyImportExportsEager : IPartImportsSatisfiedNotification + { + public NotifyImportExportsEager() + { + } + + [ImportMany] + public Collection>> Imports { get; set; } + + public void OnImportsSatisfied() + { + realImports.Clear(); + foreach (var import in Imports) + { + if (!((bool)import.Metadata["Filter"])) + { + realImports.Add(import.Value); + } + } + } + + internal Collection realImports = new Collection(); + + public Collection RealImports + { + get + { + return realImports; + } + } + } + + public class MyEventDrivenNotifyImporter : IPartImportsSatisfiedNotification + { + [Import] + public ICompositionService ImportSomethingSoIGetImportCompletedCalled { get; set; } + + public event EventHandler ImportsSatisfied; + + public void OnImportsSatisfied() + { + if (this.ImportsSatisfied != null) + { + this.ImportsSatisfied(this, new EventArgs()); + } + } + } + + [Export] + public class MyEventDrivenFullComposedNotifyImporter1 : MyEventDrivenNotifyImporter + { + [Import] + public MyEventDrivenFullComposedNotifyImporter2 FullyComposedImport { get; set; } + + public bool AreAllImportsSet + { + get + { + return (this.ImportSomethingSoIGetImportCompletedCalled != null) + && (this.FullyComposedImport != null); + } + } + + public bool AreAllImportsFullyComposed + { + get + { + return this.AreAllImportsSet && this.FullyComposedImport.AreAllImportsSet; + } + } + } + + [Export] + public class MyEventDrivenFullComposedNotifyImporter2 : MyEventDrivenNotifyImporter + { + [Import] + public MyEventDrivenFullComposedNotifyImporter1 FullyComposedImport { get; set; } + + public bool AreAllImportsSet + { + get + { + return (this.ImportSomethingSoIGetImportCompletedCalled != null) + && (this.FullyComposedImport != null); + } + } + + public bool AreAllImportsFullyComposed + { + get + { + return this.AreAllImportsSet && this.FullyComposedImport.AreAllImportsSet; + } + } + } + + [Export("MyNotifyImportExporter")] + public class MyNotifyImportExporter : IPartImportsSatisfiedNotification + { + [Import] + public ICompositionService ImportSomethingSoIGetImportCompletedCalled { get; set; } + + public int ImportCompletedCallCount { get; set; } + public void OnImportsSatisfied() + { + ImportCompletedCallCount++; + } + } + + public class MyNotifyImportImporter : IPartImportsSatisfiedNotification + { + private CompositionContainer container; + public MyNotifyImportImporter(CompositionContainer container) + { + this.container = container; + } + + [Import("MyNotifyImportExporter")] + public MyNotifyImportExporter MyNotifyImportExporter { get; set; } + + public int ImportCompletedCallCount { get; set; } + public void OnImportsSatisfied() + { + ImportCompletedCallCount++; + } + } + + [Export("LowerCaseString")] + public class LowerCaseString + { + public string String { get; private set; } + public LowerCaseString(string s) + { + String = s.ToLower(); + } + } + + public class UpperCaseStringComponent : IPartImportsSatisfiedNotification + { + public UpperCaseStringComponent() + { + UpperCaseStrings = new List(); + } + Collection> lowerCaseString = new Collection>(); + + [ImportMany("LowerCaseString", AllowRecomposition = true)] + public Collection> LowerCaseStrings + { + get { return lowerCaseString; } + set { lowerCaseString = value; } + } + + public List UpperCaseStrings { get; set; } + + public int ImportCompletedCallCount { get; set; } + + // This method gets called whenever a bind is completed and any of + // of the imports have changed, but ar safe to use now. + public void OnImportsSatisfied() + { + UpperCaseStrings.Clear(); + foreach (var i in LowerCaseStrings) + UpperCaseStrings.Add(i.Value.String.ToUpper()); + + ImportCompletedCallCount++; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/AttributedModelServicesTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/AttributedModelServicesTests.cs new file mode 100644 index 0000000000..3f13e9555d --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/AttributedModelServicesTests.cs @@ -0,0 +1,299 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Primitives; +using Xunit; + +namespace System.ComponentModel.Composition.AttributedModel +{ + class ConcreteCPD : ComposablePartDefinition + { + public override ComposablePart CreatePart() + { + throw new NotImplementedException(); + } + + public override IEnumerable ExportDefinitions + { + get { throw new NotImplementedException(); } + } + + public override IEnumerable ImportDefinitions + { + get { throw new NotImplementedException(); } + } + } + + public class CPDTest { } + + public class AttributedModelServicesTests + { + [Fact] + public void CreatePartDefinition1_NullAsType_ShouldThrowArgumentNull() + { + var origin = ElementFactory.Create(); + + Assert.Throws("type", () => + { + AttributedModelServices.CreatePartDefinition((Type)null, origin); + }); + } + + [Fact] + public void CreatePartDefinition2_NullAsType_ShouldThrowArgumentNull() + { + var origin = ElementFactory.Create(); + + Assert.Throws("type", () => + { + AttributedModelServices.CreatePartDefinition((Type)null, origin, false); + }); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.ArgumentException: ComposablePartDefinition of type 'System.ComponentModel.Composition.AttributedModel.ConcreteCPD' cannot be used in this context. Only part definitions produced by the ReflectionModelServices.CreatePartDefinition are supported. + public void CreatePart_From_InvalidPartDefiniton_ShouldThrowArgumentException() + { + Assert.Throws("partDefinition", () => + { + try + { + var partDefinition = new ConcreteCPD(); + var instance = new CPDTest(); + var part = AttributedModelServices.CreatePart(partDefinition, instance); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + + }); + } + + [Fact] + public void Exports_Throws_OnNullPart() + { + ComposablePartDefinition part = null; + Type contractType = typeof(IContract1); + Assert.Throws("part", () => + { + part.Exports(contractType); + }); + } + + [Fact] + public void Exports_Throws_OnNullContractType() + { + ComposablePartDefinition part = typeof(PartExportingContract1).AsPart(); + Type contractType = null; + Assert.Throws("contractType", () => + { + part.Exports(contractType); + }); + } + + [Fact] + public void Exports() + { + ComposablePartDefinition part1 = typeof(PartExportingContract1).AsPart(); + ComposablePartDefinition part2 = typeof(PartExportingContract2).AsPart(); + + Assert.True(part1.Exports(typeof(IContract1))); + Assert.True(part2.Exports(typeof(IContract2))); + + Assert.False(part2.Exports(typeof(IContract1))); + Assert.False(part1.Exports(typeof(IContract2))); + } + + [Fact] + public void ExportsGeneric_Throws_OnNullPart() + { + ComposablePartDefinition part = null; + Assert.Throws("part", () => + { + part.Exports(); + }); + } + + [Fact] + public void ExportsGeneric() + { + ComposablePartDefinition part1 = typeof(PartExportingContract1).AsPart(); + ComposablePartDefinition part2 = typeof(PartExportingContract2).AsPart(); + + Assert.True(part1.Exports()); + Assert.True(part2.Exports()); + + Assert.False(part2.Exports()); + Assert.False(part1.Exports()); + } + + [Fact] + public void Imports_Throws_OnNullPart() + { + ComposablePartDefinition part = null; + Type contractType = typeof(IContract1); + Assert.Throws("part", () => + { + part.Imports(contractType); + }); + } + + [Fact] + public void Imports_Throws_OnNullContractName() + { + ComposablePartDefinition part = typeof(PartImportingContract1).AsPart(); + Type contractType = null; + Assert.Throws("contractType", () => + { + part.Imports(contractType); + }); + } + + [Fact] + public void Imports() + { + ComposablePartDefinition part1 = typeof(PartImportingContract1).AsPart(); + ComposablePartDefinition part2 = typeof(PartImportingContract2).AsPart(); + + Assert.True(part1.Imports(typeof(IContract1))); + Assert.True(part2.Imports(typeof(IContract2))); + + Assert.False(part2.Imports(typeof(IContract1))); + Assert.False(part1.Imports(typeof(IContract2))); + } + + [Fact] + public void Imports_CardinalityIgnored_WhenNotSpecified() + { + ComposablePartDefinition part1 = typeof(PartImportingContract1).AsPart(); + ComposablePartDefinition part1Multiple = typeof(PartImportingContract1Multiple).AsPart(); + ComposablePartDefinition part1Optional = typeof(PartImportingContract1Optionally).AsPart(); + + Assert.True(part1.Imports(typeof(IContract1))); + Assert.True(part1Optional.Imports(typeof(IContract1))); + Assert.True(part1Multiple.Imports(typeof(IContract1))); + } + + [Fact] + public void Imports_CardinalityNotIgnored_WhenSpecified() + { + ComposablePartDefinition part1 = typeof(PartImportingContract1).AsPart(); + ComposablePartDefinition part1Multiple = typeof(PartImportingContract1Multiple).AsPart(); + ComposablePartDefinition part1Optional = typeof(PartImportingContract1Optionally).AsPart(); + + Assert.True(part1.Imports(typeof(IContract1), ImportCardinality.ExactlyOne)); + Assert.False(part1.Imports(typeof(IContract1), ImportCardinality.ZeroOrMore)); + Assert.False(part1.Imports(typeof(IContract1), ImportCardinality.ZeroOrOne)); + + Assert.False(part1Multiple.Imports(typeof(IContract1), ImportCardinality.ExactlyOne)); + Assert.True(part1Multiple.Imports(typeof(IContract1), ImportCardinality.ZeroOrMore)); + Assert.False(part1Multiple.Imports(typeof(IContract1), ImportCardinality.ZeroOrOne)); + + Assert.False(part1Optional.Imports(typeof(IContract1), ImportCardinality.ExactlyOne)); + Assert.False(part1Optional.Imports(typeof(IContract1), ImportCardinality.ZeroOrMore)); + Assert.True(part1Optional.Imports(typeof(IContract1), ImportCardinality.ZeroOrOne)); + } + + [Fact] + public void ImportsGeneric_Throws_OnNullPart() + { + ComposablePartDefinition part = null; + Assert.Throws("part", () => + { + part.Imports(); + }); + } + + [Fact] + public void ImportsGeneric() + { + ComposablePartDefinition part1 = typeof(PartImportingContract1).AsPart(); + ComposablePartDefinition part2 = typeof(PartImportingContract2).AsPart(); + + Assert.True(part1.Imports()); + Assert.True(part2.Imports()); + + Assert.False(part2.Imports()); + Assert.False(part1.Imports()); + } + + [Fact] + public void ImportsGeneric_CardinalityIgnored_WhenNotSpecified() + { + ComposablePartDefinition part1 = typeof(PartImportingContract1).AsPart(); + ComposablePartDefinition part1Multiple = typeof(PartImportingContract1Multiple).AsPart(); + ComposablePartDefinition part1Optional = typeof(PartImportingContract1Optionally).AsPart(); + + Assert.True(part1.Imports()); + Assert.True(part1Optional.Imports()); + Assert.True(part1Multiple.Imports()); + } + + [Fact] + public void ImportsGeneric_CardinalityNotIgnored_WhenSpecified() + { + ComposablePartDefinition part1 = typeof(PartImportingContract1).AsPart(); + ComposablePartDefinition part1Multiple = typeof(PartImportingContract1Multiple).AsPart(); + ComposablePartDefinition part1Optional = typeof(PartImportingContract1Optionally).AsPart(); + + Assert.True(part1.Imports(ImportCardinality.ExactlyOne)); + Assert.False(part1.Imports(ImportCardinality.ZeroOrMore)); + Assert.False(part1.Imports(ImportCardinality.ZeroOrOne)); + + Assert.False(part1Multiple.Imports(ImportCardinality.ExactlyOne)); + Assert.True(part1Multiple.Imports(ImportCardinality.ZeroOrMore)); + Assert.False(part1Multiple.Imports(ImportCardinality.ZeroOrOne)); + + Assert.False(part1Optional.Imports(ImportCardinality.ExactlyOne)); + Assert.False(part1Optional.Imports(ImportCardinality.ZeroOrMore)); + Assert.True(part1Optional.Imports(ImportCardinality.ZeroOrOne)); + } + + public interface IContract1 + { + } + + public interface IContract2 + { + } + + [Export(typeof(IContract1))] + public class PartExportingContract1 : IContract1 + { + } + + [Export(typeof(IContract2))] + public class PartExportingContract2 : IContract2 + { + } + + public class PartImportingContract1 + { + [Import] + public IContract1 import; + } + + public class PartImportingContract2 + { + [Import] + public IContract2 import; + } + + public class PartImportingContract1Optionally + { + [Import(AllowDefault = true)] + public IContract1 import; + } + + public class PartImportingContract1Multiple + { + [ImportMany] + public IEnumerable import; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CardinalityMismatchExceptionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CardinalityMismatchExceptionTests.cs new file mode 100644 index 0000000000..b783a56781 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CardinalityMismatchExceptionTests.cs @@ -0,0 +1,119 @@ +// 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.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class CardinalityMismatchExceptionTests + { + [Fact] + public void Constructor1_ShouldSetMessagePropertyToDefault() + { + var exception = new ImportCardinalityMismatchException(); + + ExceptionAssert.HasDefaultMessage(exception); + } + + [Fact] + public void Constructor2_NullAsMessageArgument_ShouldSetMessagePropertyToDefault() + { + var exception = new ImportCardinalityMismatchException((string)null); + + ExceptionAssert.HasDefaultMessage(exception); + } + + [Fact] + public void Constructor3_NullAsMessageArgument_ShouldSetMessagePropertyToDefault() + { + var exception = new ImportCardinalityMismatchException((string)null, new Exception()); + + ExceptionAssert.HasDefaultMessage(exception); + } + + [Fact] + public void Constructor2_ValueAsMessageArgument_ShouldSetMessageProperty() + { + var expectations = Expectations.GetExceptionMessages(); + + foreach (var e in expectations) + { + var exception = new ImportCardinalityMismatchException(e); + + Assert.Equal(e, exception.Message); + } + } + + [Fact] + public void Constructor3_ValueAsMessageArgument_ShouldSetMessageProperty() + { + var expectations = Expectations.GetExceptionMessages(); + + foreach (var e in expectations) + { + var exception = new ImportCardinalityMismatchException(e, new Exception()); + + Assert.Equal(e, exception.Message); + } + } + + [Fact] + public void Constructor1_ShouldSetInnerExceptionPropertyToNull() + { + var exception = new ImportCardinalityMismatchException(); + + Assert.Null(exception.InnerException); + } + + [Fact] + public void Constructor2_ShouldSetInnerExceptionPropertyToNull() + { + var exception = new ImportCardinalityMismatchException("Message"); + + Assert.Null(exception.InnerException); + } + + [Fact] + public void Constructor3_NullAsInnerExceptionArgument_ShouldSetInnerExceptionPropertyToNull() + { + var exception = new ImportCardinalityMismatchException("Message", (Exception)null); + + Assert.Null(exception.InnerException); + } + + [Fact] + public void Constructor3_ValueAsInnerExceptionArgument_ShouldSetInnerExceptionProperty() + { + var expectations = Expectations.GetInnerExceptions(); + + foreach (var e in expectations) + { + var exception = new ImportCardinalityMismatchException("Message", e); + + Assert.Same(e, exception.InnerException); + } + } + + private static ImportCardinalityMismatchException CreateCardinalityMismatchException() + { + return CreateCardinalityMismatchException((string)null, (Exception)null); + } + + private static ImportCardinalityMismatchException CreateCardinalityMismatchException(string message) + { + return CreateCardinalityMismatchException(message, (Exception)null); + } + + private static ImportCardinalityMismatchException CreateCardinalityMismatchException(Exception innerException) + { + return CreateCardinalityMismatchException((string)null, innerException); + } + + private static ImportCardinalityMismatchException CreateCardinalityMismatchException(string message, Exception innerException) + { + return new ImportCardinalityMismatchException(message, innerException); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CatalogExtensionsTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CatalogExtensionsTests.cs new file mode 100644 index 0000000000..9a963071c3 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CatalogExtensionsTests.cs @@ -0,0 +1,32 @@ +// 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.ComponentModel.Composition.Hosting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class CatalogExtensionsTests + { + [Fact] + public void CreateCompositionService_NullCatalog_ShouldThrowArgumentNullException() + { + Assert.Throws("composablePartCatalog", () => + { + CatalogExtensions.CreateCompositionService(null); + }); + } + + [Fact] + public void CreateCompositionService_ImmutableCatalog_ShouldSucceed() + { + //Create and dispose an empty immutable catalog, I.e no INotifyComposablePartCatalogChanged interface + var catalog = new TypeCatalog(); + using(var cs = catalog.CreateCompositionService()) + { + //Do nothing + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ComponentModelTestTypes.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ComponentModelTestTypes.cs new file mode 100644 index 0000000000..bf72afc565 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ComponentModelTestTypes.cs @@ -0,0 +1,69 @@ +// 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. + +namespace System.ComponentModel.Composition +{ + public class CallbackExecuteCodeDuringCompose + { + public CallbackExecuteCodeDuringCompose(Action callback) + { + this.callback = callback; + } + + [Export("MyOwnCallbackContract")] + public string ExportValue + { + get + { + callback(); + return string.Empty; + } + } + + [Import("MyOwnCallbackContract")] + public string ImportValue { get; set; } + private Action callback; + } + + public class CallbackImportNotify : IPartImportsSatisfiedNotification + { + private Action callback; + public CallbackImportNotify(Action callback) + { + this.callback = callback; + } + + [Import(AllowDefault = true)] + public ICompositionService ImportSomethingSoIGetImportCompletedCalled { get; set; } + + public void OnImportsSatisfied() + { + this.callback(); + } + } + + public class ExportValueTypeFactory + { + [Export("{AssemblyCatalogResolver}FactoryValueType")] + public int Value + { + get + { + return 18; + } + } + } + + public class ExportValueTypeSingleton + { + [Export("{AssemblyCatalogResolver}SingletonValueType")] + public int Value + { + get + { + return 17; + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ComponentServicesTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ComponentServicesTests.cs new file mode 100644 index 0000000000..6528a6de68 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ComponentServicesTests.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. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.Linq; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class ComponentServicesTests + { + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void GetValuesByType() + { + var cat = CatalogFactory.CreateDefaultAttributed(); + + var container = new CompositionContainer(cat); + + string itestName = AttributedModelServices.GetContractName(typeof(ITest)); + + var e1 = container.GetExportedValues(); + var e2 = container.GetExports(itestName); + + Assert.IsType(e1.First()); + Assert.IsType(e1.Skip(1).First()); + + Assert.IsType(e2.First().Value); + Assert.IsType(e2.Skip(1).First().Value); + + CompositionContainer childContainer = new CompositionContainer(container); + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(new T1()); + container.Compose(batch); + var t1 = childContainer.GetExportedValue(); + var t2 = childContainer.GetExport(itestName); + + Assert.IsType(t1); + Assert.IsType(t2.Value); + } + + [Fact] + public void GetValueTest() + { + var container = ContainerFactory.Create(); + ITest t = new T1(); + string name = AttributedModelServices.GetContractName(typeof(ITest)); + CompositionBatch batch = new CompositionBatch(); + batch.AddExportedValue(name, t); + container.Compose(batch); + ITest value = container.GetExportedValue(); + Assert.Equal(t, value); + value = container.GetExportedValue(name); + Assert.Equal(t, value); + } + + [Fact] + public void GetValuesTest() + { + var container = ContainerFactory.Create(); + ITest t = new T1(); + string name = AttributedModelServices.GetContractName(typeof(ITest)); + CompositionBatch batch = new CompositionBatch(); + batch.AddExportedValue(name, t); + container.Compose(batch); + IEnumerable values = container.GetExportedValues(); + Assert.Equal(t, values.First()); + values = container.GetExportedValues(name); + Assert.Equal(t, values.First()); + } + + [Fact] + public void NoResolverExceptionTest() + { + var container = ContainerFactory.Create(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(new MultiExport()); + batch.AddPart(new SingleImport()); + + CompositionAssert.ThrowsChangeRejectedError(ErrorId.ImportEngine_PartCannotSetImport, ErrorId.ImportEngine_ImportCardinalityMismatch, RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + } + + public class MultiExport + { + [Export("Multi")] + [ExportMetadata("Value", "One")] + public int M1 { get { return 1; } } + + [Export("Multi")] + [ExportMetadata("Value", "Two")] + public int M2 { get { return 2; } } + + [Export("Multi")] + [ExportMetadata("Value", "Three")] + public int M3 { get { return 3; } } + } + + public interface ITest + { + void Do(); + } + + [Export(typeof(ITest))] + public class T1 : ITest + { + public void Do() { } + } + + [Export(typeof(ITest))] + public class T2 : ITest + { + public void Do() { } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ComposablePartCatalogExtensions.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ComposablePartCatalogExtensions.cs new file mode 100644 index 0000000000..beef982072 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ComposablePartCatalogExtensions.cs @@ -0,0 +1,31 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using System.Linq.Expressions; + +namespace System.ComponentModel.Composition +{ + internal static class ComposablePartCatalogExtensions + { + public static IEnumerable> GetExports(this ComposablePartCatalog catalog, Expression> constraint) + { + var import = ImportDefinitionFactory.Create(constraint); + return catalog.GetExports(import); + } + + public static Tuple[] GetExports(this ComposablePartCatalog catalog) + { + return catalog.GetExports(ImportDefinitionFactory.Create(typeof(T), ImportCardinality.ZeroOrMore)).ToArray(); + } + + public static Tuple GetExport(this ComposablePartCatalog catalog) + { + return catalog.GetExports(ImportDefinitionFactory.Create(typeof(T), ImportCardinality.ExactlyOne)).Single(); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ComposablePartDefinitionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ComposablePartDefinitionTests.cs new file mode 100644 index 0000000000..5ddaebc66b --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ComposablePartDefinitionTests.cs @@ -0,0 +1,70 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public interface IContract {} + public class ContractImpl : IContract {} + public class MyEmptyClass + { + public ExportFactory MyFactoryProperty { get; set; } + public IContract MyProperty { get; set; } + } + + public class MyEmptyClassWithFactoryConstructor : MyEmptyClass + { + [ImportingConstructor] + public MyEmptyClassWithFactoryConstructor([Import]ExportFactory myFactoryProperty) { this.MyFactoryProperty = myFactoryProperty; } + } + public class MyEmptyClassWithStandardConstructor : MyEmptyClass + { + [ImportingConstructor] + public MyEmptyClassWithStandardConstructor([Import]IContract myProperty) { this.MyProperty = myProperty; } + } + + internal static class Helpers + { + public static IEnumerable GetEnumerableOfTypes(params Type[] types) + { + return types; + } + + public const string ComImportAvailable = nameof(Helpers) + "." + nameof(CheckComImportAvailable); + + private static bool CheckComImportAvailable() => PlatformDetection.IsWindows && PlatformDetection.IsNotWindowsNanoServer && !PlatformDetection.IsUap; + } + + public class ComposablePartDefinitionTests + { + [Fact] + public void Constructor1_ShouldNotThrow() + { + PartDefinitionFactory.Create(); + } + + [Fact] + public void Constructor1_ShouldSetMetadataPropertyToEmptyDictionary() + { + var definition = PartDefinitionFactory.Create(); + + Assert.Empty(definition.Metadata); + } + + [Fact] + public void Constructor1_ShouldSetMetadataPropertyToReadOnlyDictionary() + { + var definition = PartDefinitionFactory.Create(); + + Assert.Throws(() => + { + definition.Metadata["Value"] = "Value"; + }); + } + } +} + diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ComposablePartExceptionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ComposablePartExceptionTests.cs new file mode 100644 index 0000000000..4ec6396463 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ComposablePartExceptionTests.cs @@ -0,0 +1,468 @@ +// 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.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Primitives; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class CompositionPartExceptionTests + { + [Fact] + public void Constructor1_ShouldSetMessagePropertyToDefault() + { + var exception = new ComposablePartException(); + + ExceptionAssert.HasDefaultMessage(exception); + } + + [Fact] + public void Constructor2_NullAsMessageArgument_ShouldSetMessagePropertyToDefault() + { + var exception = new ComposablePartException((string)null); + + ExceptionAssert.HasDefaultMessage(exception); + } + + [Fact] + public void Constructor3_NullAsMessageArgument_ShouldSetMessagePropertyToDefault() + { + var exception = new ComposablePartException((string)null, ElementFactory.Create()); + + ExceptionAssert.HasDefaultMessage(exception); + } + + [Fact] + public void Constructor4_NullAsMessageArgument_ShouldSetMessagePropertyToDefault() + { + var exception = new ComposablePartException((string)null, new Exception()); + + ExceptionAssert.HasDefaultMessage(exception); + } + + [Fact] + public void Constructor5_NullAsMessageArgument_ShouldSetMessagePropertyToDefault() + { + var exception = new ComposablePartException((string)null, ElementFactory.Create(), new Exception()); + + ExceptionAssert.HasDefaultMessage(exception); + } + + [Fact] + public void Constructor6_NullAsMessageArgument_ShouldSetMessagePropertyToDefault() + { + var exception = new ComposablePartException((string)null); + + ExceptionAssert.HasDefaultMessage(exception); + } + + [Fact] + public void Constructor7_NullAsMessageArgument_ShouldSetMessagePropertyToDefault() + { + var exception = new ComposablePartException((string)null, new Exception()); + + ExceptionAssert.HasDefaultMessage(exception); + } + + [Fact] + public void Constructor8_NullAsMessageArgument_ShouldSetMessagePropertyToDefault() + { + var exception = new ComposablePartException((string)null, ElementFactory.Create()); + + ExceptionAssert.HasDefaultMessage(exception); + } + + [Fact] + public void Constructor9_NullAsMessageArgument_ShouldSetMessagePropertyToDefault() + { + var exception = new ComposablePartException((string)null, ElementFactory.Create(), new Exception()); + + ExceptionAssert.HasDefaultMessage(exception); + } + + [Fact] + public void Constructor2_ValueAsMessageArgument_ShouldSetMessageProperty() + { + var expectations = Expectations.GetExceptionMessages(); + + foreach (var e in expectations) + { + var exception = new ComposablePartException(e); + + Assert.Equal(e, exception.Message); + } + } + + [Fact] + public void Constructor3_ValueAsMessageArgument_ShouldSetMessageProperty() + { + var expectations = Expectations.GetExceptionMessages(); + + foreach (var e in expectations) + { + var exception = new ComposablePartException(e, ElementFactory.Create()); + + Assert.Equal(e, exception.Message); + } + } + + [Fact] + public void Constructor4_ValueAsMessageArgument_ShouldSetMessageProperty() + { + var expectations = Expectations.GetExceptionMessages(); + + foreach (var e in expectations) + { + var exception = new ComposablePartException(e, new Exception()); + + Assert.Equal(e, exception.Message); + } + } + + [Fact] + public void Constructor5_ValueAsMessageArgument_ShouldSetMessageProperty() + { + var expectations = Expectations.GetExceptionMessages(); + + foreach (var e in expectations) + { + var exception = new ComposablePartException(e, ElementFactory.Create(), new Exception()); + + Assert.Equal(e, exception.Message); + } + } + + [Fact] + public void Constructor6_ValueAsMessageArgument_ShouldSetMessageProperty() + { + var expectations = Expectations.GetExceptionMessages(); + + foreach (var e in expectations) + { + var exception = new ComposablePartException(e); + + Assert.Equal(e, exception.Message); + } + } + + [Fact] + public void Constructor7_ValueAsMessageArgument_ShouldSetMessageProperty() + { + var expectations = Expectations.GetExceptionMessages(); + + foreach (var e in expectations) + { + var exception = new ComposablePartException(e, new Exception()); + + Assert.Equal(e, exception.Message); + } + } + + [Fact] + public void Constructor8_ValueAsMessageArgument_ShouldSetMessageProperty() + { + var expectations = Expectations.GetExceptionMessages(); + + foreach (var e in expectations) + { + var exception = new ComposablePartException(e, ElementFactory.Create()); + + Assert.Equal(e, exception.Message); + } + } + + [Fact] + public void Constructor9_ValueAsMessageArgument_ShouldSetMessageProperty() + { + var expectations = Expectations.GetExceptionMessages(); + + foreach (var e in expectations) + { + var exception = new ComposablePartException(e, ElementFactory.Create(), new Exception()); + + Assert.Equal(e, exception.Message); + } + } + + [Fact] + public void Constructor1_ShouldSetInnerExceptionPropertyToNull() + { + var exception = new ComposablePartException(); + + Assert.Null(exception.InnerException); + } + + [Fact] + public void Constructor2_ShouldSetInnerExceptionPropertyToNull() + { + var exception = new ComposablePartException("Message"); + + Assert.Null(exception.InnerException); + } + + [Fact] + public void Constructor3_ShouldSetInnerExceptionPropertyToNull() + { + var exception = new ComposablePartException("Message", ElementFactory.Create()); + + Assert.Null(exception.InnerException); + } + + [Fact] + public void Constructor4_NullAsInnerExceptionArgument_ShouldSetInnerExceptionPropertyToNull() + { + var exception = new ComposablePartException("Message", (Exception)null); + + Assert.Null(exception.InnerException); + } + + [Fact] + public void Constructor5_NullAsInnerExceptionArgument_ShouldSetInnerExceptionPropertyToNull() + { + var exception = new ComposablePartException("Message", ElementFactory.Create(), (Exception)null); + + Assert.Null(exception.InnerException); + } + + [Fact] + public void Constructor6_ShouldSetInnerExceptionPropertyToNull() + { + var exception = new ComposablePartException("Message"); + + Assert.Null(exception.InnerException); + } + + [Fact] + public void Constructor7_NullAsInnerExceptionArgument_ShouldSetInnerExceptionPropertyToNull() + { + var exception = new ComposablePartException("Message", (Exception)null); + + Assert.Null(exception.InnerException); + } + + [Fact] + public void Constructor8_ShouldSetInnerExceptionPropertyToNull() + { + var exception = new ComposablePartException("Message", ElementFactory.Create()); + + Assert.Null(exception.InnerException); + } + + [Fact] + public void Constructor9_NullAsInnerExceptionArgument_ShouldSetInnerExceptionPropertyToNull() + { + var exception = new ComposablePartException("Message", ElementFactory.Create(), (Exception)null); + + Assert.Null(exception.InnerException); + } + + [Fact] + public void Constructor4_ValueAsInnerExceptionArgument_ShouldSetInnerExceptionProperty() + { + var expectations = Expectations.GetInnerExceptions(); + + foreach (var e in expectations) + { + var exception = new ComposablePartException("Message", e); + + Assert.Same(e, exception.InnerException); + } + } + + [Fact] + public void Constructor5_ValueAsInnerExceptionArgument_ShouldSetInnerExceptionProperty() + { + var expectations = Expectations.GetInnerExceptions(); + + foreach (var e in expectations) + { + var exception = new ComposablePartException("Message", ElementFactory.Create(), e); + + Assert.Same(e, exception.InnerException); + } + } + + [Fact] + public void Constructor7_ValueAsInnerExceptionArgument_ShouldSetInnerExceptionProperty() + { + var expectations = Expectations.GetInnerExceptions(); + + foreach (var e in expectations) + { + var exception = new ComposablePartException("Message", e); + + Assert.Same(e, exception.InnerException); + } + } + + [Fact] + public void Constructor9_ValueAsInnerExceptionArgument_ShouldSetInnerExceptionProperty() + { + var expectations = Expectations.GetInnerExceptions(); + + foreach (var e in expectations) + { + var exception = new ComposablePartException("Message", ElementFactory.Create(), e); + + Assert.Same(e, exception.InnerException); + } + } + + [Fact] + public void Constructor1_ShouldSetElementPropertyToNull() + { + var exception = new ComposablePartException(); + + Assert.Null(exception.Element); + } + + [Fact] + public void Constructor2_ShouldSetElementPropertyToNull() + { + var exception = new ComposablePartException("Message"); + + Assert.Null(exception.Element); + } + + [Fact] + public void Constructor3_NullAsElementArgument_ShouldSetElementPropertyToNull() + { + var exception = new ComposablePartException("Message", (ICompositionElement)null); + + Assert.Null(exception.Element); + } + + [Fact] + public void Constructor4_ShouldSetElementPropertyToNull() + { + var exception = new ComposablePartException("Message", new Exception()); + + Assert.Null(exception.Element); + } + + [Fact] + public void Constructor5_NullAsElementArgument_ShouldSetElementPropertyToNull() + { + var exception = new ComposablePartException("Message", (ICompositionElement)null, new Exception()); + + Assert.Null(exception.Element); + } + + [Fact] + public void Constructor6_ShouldSetElementPropertyToNull() + { + var exception = new ComposablePartException("Message"); + + Assert.Null(exception.Element); + } + + [Fact] + public void Constructor7_ShouldSetElementPropertyToNull() + { + var exception = new ComposablePartException("Message", new Exception()); + + Assert.Null(exception.Element); + } + + [Fact] + public void Constructor8_NullAsElementArgument_ShouldSetElementPropertyToNull() + { + var exception = new ComposablePartException("Message", (ICompositionElement)null); + + Assert.Null(exception.Element); + } + + [Fact] + public void Constructor9_NullAsElementArgument_ShouldSetElementPropertyToNull() + { + var exception = new ComposablePartException("Message", (ICompositionElement)null, new Exception()); + + Assert.Null(exception.Element); + } + + [Fact] + public void Constructor3_ValueAsElementArgument_ShouldSetElementProperty() + { + var expectations = Expectations.GetCompositionElements(); + + foreach (var e in expectations) + { + var exception = new ComposablePartException("Message", (ICompositionElement)e); + + Assert.Same(e, exception.Element); + } + } + + [Fact] + public void Constructor5_ValueAsElementArgument_ShouldSetElementProperty() + { + var expectations = Expectations.GetCompositionElements(); + + foreach (var e in expectations) + { + var exception = new ComposablePartException("Message", (ICompositionElement)e, new Exception()); + + Assert.Same(e, exception.Element); + } + } + + [Fact] + public void Constructor8_ValueAsElementArgument_ShouldSetElementProperty() + { + var expectations = Expectations.GetCompositionElements(); + + foreach (var e in expectations) + { + var exception = new ComposablePartException("Message", (ICompositionElement)e); + + Assert.Same(e, exception.Element); + } + } + + [Fact] + public void Constructor9_ValueAsElementArgument_ShouldSetElementProperty() + { + var expectations = Expectations.GetCompositionElements(); + + foreach (var e in expectations) + { + var exception = new ComposablePartException("Message", (ICompositionElement)e, new Exception()); + + Assert.Same(e, exception.Element); + } + } + + private static ComposablePartException CreateComposablePartException() + { + return CreateComposablePartException(CompositionErrorId.Unknown, (string)null, (ICompositionElement)null, (Exception)null); + } + + private static ComposablePartException CreateComposablePartException(string message) + { + return CreateComposablePartException(CompositionErrorId.Unknown, message, (ICompositionElement)null, (Exception)null); + } + + private static ComposablePartException CreateComposablePartException(CompositionErrorId id) + { + return CreateComposablePartException(CompositionErrorId.Unknown, (string)null, (ICompositionElement)null, (Exception)null); + } + + private static ComposablePartException CreateComposablePartException(ICompositionElement element) + { + return CreateComposablePartException(CompositionErrorId.Unknown, (string)null, element, (Exception)null); + } + + private static ComposablePartException CreateComposablePartException(Exception innerException) + { + return CreateComposablePartException(CompositionErrorId.Unknown, (string)null, (ICompositionElement)null, innerException); + } + + private static ComposablePartException CreateComposablePartException(CompositionErrorId id, string message, ICompositionElement element, Exception innerException) + { + return new ComposablePartException(message, element, innerException); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ComposablePartExtensibilityTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ComposablePartExtensibilityTests.cs new file mode 100644 index 0000000000..111a081505 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ComposablePartExtensibilityTests.cs @@ -0,0 +1,285 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using System.Reflection; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class ComposablePartExtensibilityTests + { + [Fact] + public void PhaseTest() + { + CompositionContainer container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + + var part = new OrderingTestComposablePart(); + part.AddImport("Import1", ImportCardinality.ExactlyOne, true, false); + part.AddExport("Export1", 1); + part.CallOrder.Enqueue("Import:Import1"); + part.CallOrder.Enqueue("OnComposed"); + + batch.AddExportedValue("Import1", 20); + batch.AddPart(part); + container.Compose(batch); + + // Export shouldn't be called until it is pulled on by someone. + var export = container.GetExport("Export1"); + + part.CallOrder.Enqueue("Export:Export1"); + Assert.Equal(1, export.Value); + + Assert.True(part.CallOrder.Count == 0); + } + + [Fact] + public void ImportTest() + { + var exporter = new TestExportBinder(); + var importer = new TestImportBinder(); + + CompositionContainer container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + + batch.AddPart(importer); + batch.AddPart(exporter); + container.Compose(batch); + + ExportsAssert.AreEqual(importer.SetImports["single"], 42); + ExportsAssert.AreEqual(importer.SetImports["multi"], 1, 2, 3); + } + + [Fact] + public void ConstructorInjectionSimpleCase() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + + batch.AddPart(new ConstructorInjectionComposablePart(typeof(Foo))); + batch.AddExportedValue(new Bar("Bar Value")); + container.Compose(batch); + + var import = container.GetExport(); + var foo = import.Value; + + Assert.Equal("Bar Value", foo.Bar.Value); + } + + [Fact] + public void ConstructorInjectionCycle() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + + batch.AddPart(new ConstructorInjectionComposablePart(typeof(AClass))); + batch.AddPart(new ConstructorInjectionComposablePart(typeof(BClass))); + + CompositionAssert.ThrowsErrors(ErrorId.ImportEngine_PartCannotSetImport, + ErrorId.ImportEngine_PartCannotSetImport, RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + } + + internal class OrderingTestComposablePart : ConcreteComposablePart + { + public Queue CallOrder = new Queue(); + + public OrderingTestComposablePart() + { + } + + public new void AddExport(string contractName, object value) + { + var export = ExportFactory.Create(contractName, () => + { + this.OnGetExport(contractName); return value; + }); + + base.AddExport(export); + } + + private void OnGetExport(string contractName) + { + Assert.Equal("Export:" + contractName, CallOrder.Dequeue()); + } + + public override void SetImport(ImportDefinition definition, IEnumerable exports) + { + ContractBasedImportDefinition contractBasedImportDefinition = (ContractBasedImportDefinition)definition; + Assert.Equal("Import:" + contractBasedImportDefinition.ContractName, CallOrder.Dequeue()); + base.SetImport(definition, exports); + } + + public override void Activate() + { + Assert.Equal("OnComposed", CallOrder.Dequeue()); + base.Activate(); + } + } + + internal class TestExportBinder : ConcreteComposablePart + { + public TestExportBinder() + { + AddExport("single", 42); + AddExport("multi", 1); + AddExport("multi", 2); + AddExport("multi", 3); + } + } + + internal class TestImportBinder : ConcreteComposablePart + { + public TestImportBinder() + { + AddImport("single", ImportCardinality.ExactlyOne, true, false); + AddImport("multi", ImportCardinality.ZeroOrMore, true, false); + } + } + + public class Foo + { + public Foo(IBar bar) + { + Bar = bar; + } + + public IBar Bar { get; private set; } + } + + public interface IBar + { + string Value { get; } + } + + public class Bar : IBar + { + public Bar(string value) + { + Value = value; + } + public string Value { get; private set; } + } + + public class FooBar + { + [Import("Foo")] + public Foo Foo { get; set; } + } + + public class AClass + { + public AClass(BClass b) + { + } + + public BClass B { get; private set; } + } + + public class BClass + { + public BClass(AClass a) + { + this.A = a; + } + + public AClass A { get; private set; } + } + + internal class ConstructorInjectionComposablePart : ConcreteComposablePart + { + private Type _type; + private ConstructorInfo _constructor; + private Dictionary _imports; + + public ConstructorInjectionComposablePart(Type type) + { + this._type = type; + + // Note that this just blindly takes the first constructor... + this._constructor = this._type.GetConstructors().FirstOrDefault(); + Assert.NotNull(this._constructor); + + foreach (var param in this._constructor.GetParameters()) + { + string name = AttributedModelServices.GetContractName(param.ParameterType); + AddImport(name, ImportCardinality.ExactlyOne, true, true); + } + + string contractName = AttributedModelServices.GetContractName(type); + string typeIdentity = AttributedModelServices.GetTypeIdentity(type); + var metadata = new Dictionary(); + metadata.Add(CompositionConstants.ExportTypeIdentityMetadataName, typeIdentity); + + Export composableExport = ExportFactory.Create( + contractName, + metadata, + GetInstance); + this.AddExport(composableExport); + + this._imports = new Dictionary(); + } + + private object GetInstance() + { + var result = CompositionResult.SucceededResult; + + // We only need this guard if we are pulling on the lazy exports during this call + // but if we do the pulling in SetImport it isn't needed. + //if (currentlyExecuting) + //{ + // var issue = CompositionError.Create("CM:CreationCycleDetected", + // "This failed because there is a creation cycle"); + // return result.MergeIssue(issue).ToResult(null); + //} + + try + { + List constructorArgs = new List(); + + foreach (ImportDefinition import in this.ImportDefinitions + .Where(i => i.IsPrerequisite)) + { + object importValue; + if (!this._imports.TryGetValue(import, out importValue)) + { + result = result.MergeError(CompositionError.Create(CompositionErrorId.ImportNotSetOnPart, + "The import '{0}' is required for construction of '{1}'", import.ToString(), _type.FullName)); + + continue; + } + + constructorArgs.Add(importValue); + } + + if (!result.Succeeded) + { + throw new CompositionException(result.Errors); + } + + object obj = this._constructor.Invoke(constructorArgs.ToArray()); + + return obj; + } + finally + { + } + } + + public override void SetImport(ImportDefinition definition, IEnumerable exports) + { + _imports[definition] = exports.First().Value; + } + } +} + diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ComposablePartTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ComposablePartTests.cs new file mode 100644 index 0000000000..ef739fbb0b --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ComposablePartTests.cs @@ -0,0 +1,39 @@ +// 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.ComponentModel.Composition.Factories; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class ComposablePartTests + { + [Fact] + public void Constructor1_ShouldSetMetadataPropertyToEmptyDictionary() + { + var part = PartFactory.Create(); + + Assert.Empty(part.Metadata); + } + + [Fact] + public void Constructor1_ShouldSetMetadataPropertyToReadOnlyDictionary() + { + var part = PartFactory.Create(); + + Assert.Throws(() => + { + part.Metadata["Value"] = "Value"; + }); + } + + [Fact] + public void OnComposed_DoesNotThrow() + { + var part = PartFactory.Create(); + part.Activate(); + } + + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionContainerCollectionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionContainerCollectionTests.cs new file mode 100644 index 0000000000..d7482b7382 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionContainerCollectionTests.cs @@ -0,0 +1,910 @@ +// 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.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.Linq; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class CompositionContainerCollectionTests + { + public class SupportedImportCollectionAssignments + { + // Fields + [ImportMany("Value")] + public IEnumerable IEnumerableOfTField; + + [ImportMany("Value")] + public IEnumerable IEnumerableOfObjectField; + + [ImportMany("Value")] + public T[] ArrayOfTField; + + [ImportMany("Value")] + public object[] ArrayOfObjectField; + + [ImportMany("Value")] + public IEnumerable IEnumerableOfTProperty { get; set; } + + [ImportMany("Value")] + public IEnumerable IEnumerableOfObjectProperty { get; set; } + + [ImportMany("Value")] + public T[] ArrayOfTProperty { get; set; } + + [ImportMany("Value")] + public object[] ArrayOfObjectProperty { get; set; } + + public void VerifyImports(params T[] expectedValues) + { + // Fields + EnumerableAssert.AreEqual(IEnumerableOfTField, expectedValues); + EnumerableAssert.AreEqual(IEnumerableOfObjectField, expectedValues.Cast()); + EnumerableAssert.AreEqual(ArrayOfTField, expectedValues); + EnumerableAssert.AreEqual(ArrayOfObjectField, expectedValues.Cast()); + + // Properties + EnumerableAssert.AreEqual(IEnumerableOfTProperty, expectedValues); + EnumerableAssert.AreEqual(IEnumerableOfObjectProperty, expectedValues.Cast()); + EnumerableAssert.AreEqual(ArrayOfTProperty, expectedValues); + EnumerableAssert.AreEqual(ArrayOfObjectProperty, expectedValues.Cast()); + } + } + + [Fact] + public void ValidateSupportedImportCollectionAssignments() + { + var container = ContainerFactory.Create(); + var batch = new CompositionBatch(); + + var importer = new SupportedImportCollectionAssignments(); + + batch.AddPart(importer); + batch.AddExportedValue("Value", 21); + batch.AddExportedValue("Value", 32); + batch.AddExportedValue("Value", 43); + + container.Compose(batch); + + importer.VerifyImports(21, 32, 43); + } + + public class SupportedImportCollectionMutation + { + public SupportedImportCollectionMutation() + { + ICollectionOfTReadOnlyField = new List(); + ListOfTReadOnlyField = new List(); + CollectionOfTField = new Collection(); + CollectionOfTReadOnlyField = new Collection(); + + _iCollectionOfTReadOnlyProperty = new List(); + _listOfTReadOnlyProperty = new List(); + CollectionOfTProperty = new Collection(); + _collectionOfTReadOnlyProperty = new Collection(); + + ObservableCollectionOfTReadOnlyField = new ObservableCollection(); + _observableCollectionOfTReadOnlyProperty = new ObservableCollection(); + } + + [ImportMany("Value")] + public readonly ICollection ICollectionOfTReadOnlyField; + + [ImportMany("Value")] + public List ListOfTField; + + [ImportMany("Value")] + public readonly List ListOfTReadOnlyField; + + [ImportMany("Value")] + public Collection CollectionOfTField; + + [ImportMany("Value")] + public Collection CollectionOfObjectField; + + [ImportMany("Value")] + public readonly Collection CollectionOfTReadOnlyField; + + [ImportMany("Value")] + public ICollection ICollectionOfTReadOnlyProperty { get { return _iCollectionOfTReadOnlyProperty; } } + private ICollection _iCollectionOfTReadOnlyProperty; + + [ImportMany("Value")] + public List ListOfTProperty { get; set; } + + [ImportMany("Value")] + public List ListOfTReadOnlyProperty { get { return _listOfTReadOnlyProperty; } } + private readonly List _listOfTReadOnlyProperty; + + [ImportMany("Value")] + public Collection CollectionOfTProperty { get; set; } + + [ImportMany("Value")] + public Collection CollectionOfTReadOnlyProperty { get { return _collectionOfTReadOnlyProperty; } } + private readonly Collection _collectionOfTReadOnlyProperty; + + [ImportMany("Value")] + public ObservableCollection ObservableCollectionOfTField; + + [ImportMany("Value")] + public readonly ObservableCollection ObservableCollectionOfTReadOnlyField; + + [ImportMany("Value")] + public ObservableCollection ObservableCollectionOfTProperty { get; set; } + + [ImportMany("Value")] + public ObservableCollection ObservableCollectionOfTReadOnlyProperty { get { return _observableCollectionOfTReadOnlyProperty; } } + private readonly ObservableCollection _observableCollectionOfTReadOnlyProperty; + + public void VerifyImports(params T[] expectedValues) + { + EnumerableAssert.AreEqual(ICollectionOfTReadOnlyField, expectedValues); + EnumerableAssert.AreEqual(ListOfTField, expectedValues); + EnumerableAssert.AreEqual(ListOfTReadOnlyField, expectedValues); + EnumerableAssert.AreEqual(CollectionOfTField, expectedValues); + EnumerableAssert.AreEqual(CollectionOfTReadOnlyField, expectedValues); + + EnumerableAssert.AreEqual(ICollectionOfTReadOnlyProperty, expectedValues); + EnumerableAssert.AreEqual(ListOfTProperty, expectedValues); + EnumerableAssert.AreEqual(ListOfTReadOnlyProperty, expectedValues); + EnumerableAssert.AreEqual(CollectionOfTProperty, expectedValues); + EnumerableAssert.AreEqual(CollectionOfTReadOnlyProperty, expectedValues); + + EnumerableAssert.AreEqual(ObservableCollectionOfTField, expectedValues); + EnumerableAssert.AreEqual(ObservableCollectionOfTReadOnlyField, expectedValues); + EnumerableAssert.AreEqual(ObservableCollectionOfTProperty, expectedValues); + EnumerableAssert.AreEqual(ObservableCollectionOfTReadOnlyProperty, expectedValues); + } + } + + [Fact] + public void ValidateSupportedImportCollectionMutation() + { + var container = ContainerFactory.Create(); + var batch = new CompositionBatch(); + + var importer = new SupportedImportCollectionMutation(); + + batch.AddPart(importer); + batch.AddExportedValue("Value", 21); + batch.AddExportedValue("Value", 32); + batch.AddExportedValue("Value", 43); + + container.Compose(batch); + + importer.VerifyImports(21, 32, 43); + } + + public class ImportCollectionNullValue + { + [ImportMany("Value")] + public List NullValue { get; set; } + } + + public class NamelessImporter + { + [ImportMany] + public int[] ReadWriteIList { get; set; } + + [ImportMany] + public Collection> ReadWriteMetadata { get; set; } + } + + public class NamelessExporter + { + public NamelessExporter(int value) + { + Value = value; + } + + [Export] + public int Value { get; set; } + } + + [Fact] + public void ImportCollectionsNameless() + { + // Verifing that the contract name gets the correct value + var container = ContainerFactory.Create(); + NamelessImporter importer = new NamelessImporter(); + NamelessExporter exporter42 = new NamelessExporter(42); + NamelessExporter exporter0 = new NamelessExporter(0); + + CompositionBatch batch = new CompositionBatch(); + batch.AddParts(importer, exporter42, exporter0); + container.Compose(batch); + + EnumerableAssert.AreEqual(importer.ReadWriteIList, 42, 0); + } + + public class InvalidImporterReadOnlyEnumerable + { + IEnumerable readOnlyEnumerable = new List(); + + [ImportMany("Value")] + public IEnumerable ReadOnlyEnumerable + { + get + { + return readOnlyEnumerable; + } + } + } + + [Fact] + public void ImportCollectionsExceptionReadOnlyEnumerable() + { + ExpectedErrorOnPartActivate(new InvalidImporterReadOnlyEnumerable(), + ErrorId.ReflectionModel_ImportCollectionNotWritable); + } + + public class ImporterWriteOnlyExportCollection + { + [ImportMany("Value")] + public Collection> WriteOnlyExportCollection + { + set { PublicExportCollection = value; } + } + + public Collection> PublicExportCollection { get; set; } + } + + [Fact] + public void ImportCollections_WriteOnlyExportCollection() + { + var container = ContainerFactory.Create(); + var batch = new CompositionBatch(); + + var importer = new ImporterWriteOnlyExportCollection(); + + List values = new List() { 21, 32, 43 }; + + batch.AddPart(importer); + values.ForEach(v => batch.AddExportedValue("Value", v)); + + container.Compose(batch); + + EnumerableAssert.AreEqual(values, importer.PublicExportCollection.Select(export => export.Value)); + } + + public class ImporterWriteOnlyIEnumerableOfT + { + [ImportMany("Value")] + public IEnumerable WriteOnlyIEnumerable + { + set { PublicIEnumerable = value; } + } + + public IEnumerable PublicIEnumerable { get; set; } + } + + [Fact] + public void ImportCollections_WriteOnlyIEnumerableOfT() + { + var container = ContainerFactory.Create(); + var batch = new CompositionBatch(); + + var importer = new ImporterWriteOnlyIEnumerableOfT(); + + List values = new List() { 21, 32, 43 }; + + batch.AddPart(importer); + values.ForEach(v => batch.AddExportedValue("Value", v)); + + container.Compose(batch); + + EnumerableAssert.AreEqual(values, importer.PublicIEnumerable); + } + + public class ImporterWriteOnlyArray + { + [ImportMany("Value")] + public int[] WriteOnlyArray + { + set { PublicArray = value; } + } + + public int[] PublicArray { get; set; } + } + + [Fact] + public void ImportCollections_WriteOnlyArray() + { + var container = ContainerFactory.Create(); + var batch = new CompositionBatch(); + + var importer = new ImporterWriteOnlyArray(); + + List values = new List() { 21, 32, 43 }; + + batch.AddPart(importer); + values.ForEach(v => batch.AddExportedValue("Value", v)); + + container.Compose(batch); + + EnumerableAssert.AreEqual(values, importer.PublicArray); + } + + public class InvalidImporterNonCollection + { + [Import("Value")] + public int Value { get; set; } + } + + [Fact] + public void ImportCollectionsExceptionNonCollection() + { + ExpectedChangeRejectedErrorOnSetImport(new InvalidImporterNonCollection(), + ErrorId.ImportEngine_ImportCardinalityMismatch); + } + + public class InvalidImporterNonAssignableCollection + { + [ImportMany("Value", typeof(int))] + public IEnumerable StringCollection { get; set; } + } + + [Fact] + public void ImportCollectionsExceptionNonAssignableCollection() + { + ExpectedErrorOnSetImport(new InvalidImporterNonAssignableCollection(), + ErrorId.ReflectionModel_ImportNotAssignableFromExport); + } + + public class InvalidImporterNullReadOnlyICollection + { + ICollection readOnlyICollection = null; + + [ImportMany("Value")] + public ICollection Values { get { return readOnlyICollection; } } + } + + [Fact] + public void ImportCollectionsExceptionNullReadOnlyICollection() + { + ExpectedErrorOnPartActivate(new InvalidImporterNullReadOnlyICollection(), + ErrorId.ReflectionModel_ImportCollectionNull); + } + + public class ImporterWeakIEnumerable + { + public ImporterWeakIEnumerable() + { + ReadWriteEnumerable = new IntCollection(); + } + + [ImportMany("Value")] + public IntCollection ReadWriteEnumerable { get; set; } + + public class IntCollection : IEnumerable + { + List ints = new List(); + public void Add(int item) { ints.Add(item); } + public void Clear() { ints.Clear(); } + public bool Remove(int item) { return ints.Remove(item); } + public IEnumerator GetEnumerator() { return ints.GetEnumerator(); } + } + } + + [Fact] + public void ImportCollectionsExceptionWeakCollectionNotSupportingICollectionOfT() + { + ExpectedErrorOnPartActivate(new ImporterWeakIEnumerable(), + ErrorId.ReflectionModel_ImportCollectionNotWritable); + } + + public class ImporterThrowsOnGetting + { + [ImportMany("Value")] + public List Value + { + get + { + throw new NotSupportedException(); + } + } + } + + [Fact] + public void ImportCollectionsExceptionGettingValue() + { + var container = ContainerFactory.Create(); + ImporterThrowsOnGetting importer = new ImporterThrowsOnGetting(); + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + batch.AddExportedValue("Value", 42); + batch.AddExportedValue("Value", 0); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotActivate, + ErrorId.ReflectionModel_ImportCollectionGetThrewException, RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + + public class CustomCollectionThrowsDuringConstruction : Collection + { + public CustomCollectionThrowsDuringConstruction() + { + throw new NotSupportedException(); + } + } + + public class ImportCustomCollectionThrowsDuringConstruction + { + public ImportCustomCollectionThrowsDuringConstruction() + { + } + + [ImportMany("Value")] + public CustomCollectionThrowsDuringConstruction Values { get; set; } + } + + [Fact] + public void ImportCollections_ImportTypeThrowsOnConstruction() + { + ExpectedErrorOnPartActivate(new ImportCustomCollectionThrowsDuringConstruction(), + ErrorId.ReflectionModel_ImportCollectionConstructionThrewException); + } + + public class CustomCollectionThrowsDuringClear : Collection + { + protected override void ClearItems() + { + throw new NotSupportedException(); + } + } + + public class ImportCustomCollectionThrowsDuringClear + { + public ImportCustomCollectionThrowsDuringClear() + { + } + + [ImportMany("Value")] + public CustomCollectionThrowsDuringClear Values { get; set; } + } + + [Fact] + public void ImportCollections_ImportTypeThrowsOnClear() + { + ExpectedErrorOnPartActivate(new ImportCustomCollectionThrowsDuringClear(), + ErrorId.ReflectionModel_ImportCollectionClearThrewException); + } + + public class CustomCollectionThrowsDuringAdd : Collection + { + protected override void InsertItem(int index, int item) + { + throw new NotSupportedException(); + } + } + + public class ImportCustomCollectionThrowsDuringAdd + { + public ImportCustomCollectionThrowsDuringAdd() + { + } + + [ImportMany("Value")] + public CustomCollectionThrowsDuringAdd Values { get; set; } + } + + [Fact] + public void ImportCollections_ImportTypeThrowsOnAdd() + { + ExpectedErrorOnPartActivate(new ImportCustomCollectionThrowsDuringAdd(), + ErrorId.ReflectionModel_ImportCollectionAddThrewException); + } + + public class CustomCollectionThrowsDuringIsReadOnly : ICollection + { + void ICollection.Add(int item) + { + throw new NotImplementedException(); + } + + void ICollection.Clear() + { + throw new NotImplementedException(); + } + + bool ICollection.Contains(int item) + { + throw new NotImplementedException(); + } + + void ICollection.CopyTo(int[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + int ICollection.Count + { + get { throw new NotImplementedException(); } + } + + bool ICollection.IsReadOnly + { + get { throw new NotSupportedException(); } + } + + bool ICollection.Remove(int item) + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } + + public class ImportCustomCollectionThrowsDuringIsReadOnly + { + public ImportCustomCollectionThrowsDuringIsReadOnly() + { + Values = new CustomCollectionThrowsDuringIsReadOnly(); + } + + [ImportMany("Value")] + public CustomCollectionThrowsDuringIsReadOnly Values { get; set; } + } + + [Fact] + public void ImportCollections_ImportTypeThrowsOnIsReadOnly() + { + ExpectedErrorOnPartActivate(new ImportCustomCollectionThrowsDuringIsReadOnly(), + ErrorId.ReflectionModel_ImportCollectionIsReadOnlyThrewException); + } + + public class CollectionTypeWithNoIList : ICollection + { + private int _count = 0; + public CollectionTypeWithNoIList() + { + + } + + public void Add(T item) + { + // Do Nothing + this._count++; + } + + public void Clear() + { + // Do Nothings + } + + public bool Contains(T item) + { + throw new NotImplementedException(); + } + + public void CopyTo(T[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + public int Count + { + get { return this._count; } + } + + public bool IsReadOnly + { + get { return false; } + } + + public bool Remove(T item) + { + throw new NotImplementedException(); + } + + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } + + public class ImportCollectionWithNoIList + { + [ImportMany("Value")] + public CollectionTypeWithNoIList Values { get; set; } + } + + [Fact] + public void ImportCollections_NoIList_ShouldWorkFine() + { + var container = ContainerFactory.Create(); + var batch = new CompositionBatch(); + var importer = new ImportCollectionWithNoIList(); + batch.AddPart(importer); + batch.AddExportedValue("Value", 42); + batch.AddExportedValue("Value", 0); + + container.Compose(batch); + + Assert.Equal(2, importer.Values.Count); + } + + public class CollectionWithMultipleInterfaces : ICollection, ICollection + { + public CollectionWithMultipleInterfaces() + { + + } + + #region ICollection Members + + void ICollection.Add(int item) + { + throw new NotImplementedException(); + } + + void ICollection.Clear() + { + throw new NotImplementedException(); + } + + bool ICollection.Contains(int item) + { + throw new NotImplementedException(); + } + + void ICollection.CopyTo(int[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + int ICollection.Count + { + get { throw new NotImplementedException(); } + } + + bool ICollection.IsReadOnly + { + get { throw new NotImplementedException(); } + } + + bool ICollection.Remove(int item) + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + + #endregion + + #region IEnumerable Members + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + + #endregion + + #region ICollection Members + + void ICollection.Add(string item) + { + throw new NotImplementedException(); + } + + void ICollection.Clear() + { + throw new NotImplementedException(); + } + + bool ICollection.Contains(string item) + { + throw new NotImplementedException(); + } + + void ICollection.CopyTo(string[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + int ICollection.Count + { + get { throw new NotImplementedException(); } + } + + bool ICollection.IsReadOnly + { + get { throw new NotImplementedException(); } + } + + bool ICollection.Remove(string item) + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + + #endregion + } + + public class ImportCollectionWithMultipleInterfaces + { + [ImportMany("Value")] + public CollectionWithMultipleInterfaces Values { get; set; } + } + + [Fact] + public void ImportCollections_MultipleICollections_ShouldCauseNotWriteable() + { + ExpectedErrorOnPartActivate(new ImportCollectionWithMultipleInterfaces(), + ErrorId.ReflectionModel_ImportCollectionNotWritable); + } + + public class ImportManyNonCollectionTypeString + { + [ImportMany("Value")] + public string Foo { get; set; } + } + + [Fact] + public void ImportManyOnNonCollectionTypeString_ShouldCauseNotWritable() + { + ExpectedErrorOnPartActivate(new ImportManyNonCollectionTypeString(), + ErrorId.ReflectionModel_ImportCollectionNotWritable); + } + + public class ImportManyNonCollectionTypeObject + { + [ImportMany("Value")] + public object Foo { get; set; } + } + + [Fact] + public void ImportManyOnNonCollectionTypeObject_ShouldCauseNotWritable() + { + ExpectedErrorOnPartActivate(new ImportManyNonCollectionTypeObject(), + ErrorId.ReflectionModel_ImportCollectionNotWritable); + } + + public class ExportADictionaryObject + { + [Export] + public IDictionary MyDictionary + { + get + { + var dictionary = new Dictionary(); + dictionary.Add("a", 42); + dictionary.Add("b", "c"); + return dictionary; + } + } + } + + public class ImportADictionaryObject + { + [Import] + public IDictionary MyDictionary { get; set; } + } + + [Fact] + public void ImportDictionaryAsSingleObject() + { + var container = ContainerFactory.Create(); + var batch = new CompositionBatch(); + var importer = new ImportADictionaryObject(); + var exporter = new ExportADictionaryObject(); + + batch.AddPart(importer); + batch.AddPart(exporter); + container.Compose(batch); + + Assert.Equal(2, importer.MyDictionary.Count); + } + + public class ExportACollectionObject + { + [Export] + public Collection MyCollection + { + get + { + var collection = new Collection(); + collection.Add("a"); + collection.Add("b"); + return collection; + } + } + } + + public class ImportACollectionObject + { + [Import] + public Collection MyCollection { get; set; } + } + + [Fact] + public void ImportCollectionAsSingleObject() + { + var container = ContainerFactory.Create(); + var batch = new CompositionBatch(); + var importer = new ImportACollectionObject(); + var exporter = new ExportACollectionObject(); + + batch.AddPart(importer); + batch.AddPart(exporter); + container.Compose(batch); + + Assert.Equal(2, importer.MyCollection.Count); + } + + public void ExpectedErrorOnPartActivate(object importer, ErrorId expectedErrorId) + { + var container = ContainerFactory.Create(); + var batch = new CompositionBatch(); + batch.AddPart(importer); + batch.AddExportedValue("Value", 42); + batch.AddExportedValue("Value", 0); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotActivate, + expectedErrorId, RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + + public void ExpectedErrorOnSetImport(object importer, ErrorId expectedErrorId) + { + var container = ContainerFactory.Create(); + var batch = new CompositionBatch(); + batch.AddPart(importer); + batch.AddExportedValue("Value", 42); + batch.AddExportedValue("Value", 0); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotSetImport, + expectedErrorId, RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + + public void ExpectedChangeRejectedErrorOnSetImport(object importer, ErrorId expectedErrorId) + { + var container = ContainerFactory.Create(); + var batch = new CompositionBatch(); + batch.AddPart(importer); + batch.AddExportedValue("Value", 42); + batch.AddExportedValue("Value", 0); + + CompositionAssert.ThrowsChangeRejectedError(ErrorId.ImportEngine_PartCannotSetImport, + expectedErrorId, RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionContainerCycleTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionContainerCycleTests.cs new file mode 100644 index 0000000000..3542ae60e1 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionContainerCycleTests.cs @@ -0,0 +1,166 @@ +// 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.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class CompositionContainerCycleTests + { + // There are nine possible scenarios that cause a part to have a dependency on another part, some of which + // are legal and some not. For example, below, is not legal for a part, A, to have a prerequisite dependency + // on a part, B, which has also has a prerequisite dependency on A. In contrast, however, it is legal for + // part A and B to have a non-prerequisite (Post) dependency on each other. + // + // ------------------------------ + // | | B | + // | | Pre | Post | None | + // |--------|-----|------|------| + // | Pre | X | X | v | + // | A Post | X | v | v | + // | None | v | v | v | + // ------------------------------ + // + + [Fact] + public void APrerequisiteDependsOnBPrerequisite_ShouldThrowComposition() + { + AssertCycle(Dependency.Prerequisite, + Dependency.Prerequisite); + } + + [Fact] + public void APrerequisiteDependsOnBPost_ShouldThrowComposition() + { + AssertCycle(Dependency.Prerequisite, + Dependency.Post); + } + + [Fact] + public void APrerequisiteDependsOnBNone_ShouldNotThrow() + { + AssertNotCycle(Dependency.Prerequisite, + Dependency.None); + } + + [Fact] + public void APostDependsOnBPrerequisite_ShouldThrowComposition() + { + AssertCycle(Dependency.Post, + Dependency.Prerequisite); + } + + [Fact] + public void APostDependsOnBPost_ShouldNotThrow() + { + AssertNotCycle(Dependency.Post, + Dependency.Post); + } + + [Fact] + public void APostDependsOnBNone_ShouldNotThrow() + { + AssertNotCycle(Dependency.Post, + Dependency.None); + } + + [Fact] + public void BPrerequisiteDependsOnANone_ShouldNotThrow() + { + AssertNotCycle(Dependency.None, + Dependency.Prerequisite); + } + + [Fact] + public void BPostDependsOnANone_ShouldNotThrow() + { + AssertNotCycle(Dependency.None, + Dependency.Post); + } + + [Fact] + public void ANoneWithBNone_ShouldNotThrow() + { + AssertNotCycle(Dependency.None, + Dependency.None); + } + + private static void AssertCycle(Dependency partADependency, Dependency partBDependency) + { + var exportA = GetExport("A", partADependency, partBDependency); + + Assert.Throws(() => + { + var value = exportA.Value; + }); + + var exportB = GetExport("B", partADependency, partBDependency); + + Assert.Throws(() => + { + var value = exportB.Value; + }); + } + + private static void AssertNotCycle(Dependency partADependency, Dependency partBDependency) + { + var exportA = GetExport("A", partADependency, partBDependency); + var exportB = GetExport("B", partADependency, partBDependency); + + Assert.Equal("A", exportA.Value); + Assert.Equal("B", exportB.Value); + } + + private static Lazy GetExport(string contractName, Dependency partADependency, Dependency partBDependency) + { + var container = GetContainer(partADependency, partBDependency); + + return container.GetExports(typeof(object), null, contractName).Single(); + } + + private static CompositionContainer GetContainer(Dependency partADependency, Dependency partBDependency) + { + var partA = CreatePartA(partADependency); + var partB = CreatePartB(partBDependency); + + var catalog = CatalogFactory.Create(partA, partB); + + return ContainerFactory.Create(catalog); + } + + private static ComposablePart CreatePartA(Dependency dependency) + { + return CreatePart(dependency, "A", "B"); + } + + private static ComposablePart CreatePartB(Dependency dependency) + { + return CreatePart(dependency, "B", "A"); + } + + private static ComposablePart CreatePart(Dependency dependency, string exportContractName, string importContractName) + { + ConcreteComposablePart part = new ConcreteComposablePart(); + part.AddExport(exportContractName, exportContractName); + + if (dependency != Dependency.None) + { + part.AddImport(importContractName, ImportCardinality.ExactlyOne, false, dependency == Dependency.Prerequisite); + } + + return part; + } + + private enum Dependency + { + Prerequisite, + Post, + None, + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionContainerExtensibilityTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionContainerExtensibilityTests.cs new file mode 100644 index 0000000000..aa4773ff91 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionContainerExtensibilityTests.cs @@ -0,0 +1,79 @@ +// 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.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class CompositionContainerExtensibilityTests + { + [Fact] + public void Dispose_DoesNotThrow() + { + var container = CreateCustomCompositionContainer(); + container.Dispose(); + } + + [Fact] + public void DerivedCompositionContainer_CanExportItself() + { + var container = CreateCustomCompositionContainer(); + container.AddAndComposeExportedValue(container); + + Assert.Same(container, container.GetExportedValue()); + } + + [Fact] + public void ICompositionService_CanBeExported() + { + var container = CreateCustomCompositionContainer(); + container.AddAndComposeExportedValue(container); + + Assert.Same(container, container.GetExportedValue()); + } + + [Fact] + public void CompositionContainer_CanBeExported() + { + var container = CreateCustomCompositionContainer(); + container.AddAndComposeExportedValue(container); + + Assert.Same(container, container.GetExportedValue()); + } + + [Fact] + [ActiveIssue(25498)] + public void CanBeCollectedAfterDispose() + { + AggregateExportProvider exportProvider = new AggregateExportProvider(); + var catalog = new AggregateCatalog(CatalogFactory.CreateDefaultAttributed()); + var container = new CompositionContainer(catalog, exportProvider); + + WeakReference weakContainer = new WeakReference(container); + container.Dispose(); + container = null; + + GC.Collect(); + GC.WaitForPendingFinalizers(); + + Assert.False(weakContainer.IsAlive); + + GC.KeepAlive(exportProvider); + GC.KeepAlive(catalog); + } + + private CustomCompositionContainer CreateCustomCompositionContainer() + { + return new CustomCompositionContainer(); + } + + // Type needs to be public otherwise container.GetExportedValue + // fails on Silverlight because it cannot construct a Lazy factory. + public class CustomCompositionContainer : CompositionContainer + { + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionContainerExtensions.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionContainerExtensions.cs new file mode 100644 index 0000000000..3355645ad5 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionContainerExtensions.cs @@ -0,0 +1,110 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; + +namespace System.ComponentModel.Composition +{ + internal static class CompositionContainerExtensions + { + public static bool IsPresent(this CompositionContainer container) + { + try + { + container.GetExportedValue(); + return true; + } + catch (ImportCardinalityMismatchException) + { + return false; + } + } + + public static bool IsPresent(this CompositionContainer container, string contractName) + { + try + { + container.GetExportedValue(contractName); + return true; + } + catch (ImportCardinalityMismatchException) + { + return false; + } + } + + public static bool IsPresent(this ExportProvider container) + { + try + { + container.GetExportedValue(); + return true; + } + catch (ImportCardinalityMismatchException) + { + return false; + } + } + + public static bool IsPresent(this ExportProvider container, string contractName) + { + try + { + container.GetExportedValue(contractName); + return true; + } + catch (ImportCardinalityMismatchException) + { + return false; + } + } + + public static void AddAndComposeExportedValue(this CompositionContainer container, T exportedValue) + { + var batch = new CompositionBatch(); + batch.AddExportedValue(exportedValue); + container.Compose(batch); + } + + public static void AddAndComposeExportedValue(this CompositionContainer container, string contractName, T exportedValue) + { + var batch = new CompositionBatch(); + batch.AddExportedValue(contractName, exportedValue); + container.Compose(batch); + } + + public static void AddParts(this CompositionBatch batch, params object[] parts) + { + foreach (object instance in parts) + { + ComposablePart part = instance as ComposablePart; + if (part != null) + { + batch.AddPart(part); + } + else + { + batch.AddPart((object)instance); + } + } + } + + public static ComposablePart AddExportedValue(this CompositionBatch batch, string contractName, Type contractType, object exportedValue) + { + string typeIdentity = AttributedModelServices.GetTypeIdentity(contractType); + + IDictionary metadata = null; + + if (typeIdentity != null) + { + metadata = new Dictionary(); + metadata.Add(CompositionConstants.ExportTypeIdentityMetadataName, typeIdentity); + } + + return batch.AddExport(new Export(contractName, metadata, () => exportedValue)); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionContainerImportTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionContainerImportTests.cs new file mode 100644 index 0000000000..a70486d167 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionContainerImportTests.cs @@ -0,0 +1,762 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using System.Runtime.InteropServices; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class CompositionContainerImportTests + { + // Exporting collectin values is not supported + [Fact] + public void ImportValues() + { + var container = ContainerFactory.Create(); + Importer importer = new Importer(); + Exporter exporter42 = new Exporter(42); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + batch.AddPart(exporter42); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotSetImport, + ErrorId.ReflectionModel_ImportNotAssignableFromExport, RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + + [Fact] + public void ImportSingle() + { + var container = ContainerFactory.Create(); + var importer = new Int32Importer(); + var exporter = new Int32Exporter(42); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + batch.AddPart(exporter); + container.Compose(batch); + + Assert.Equal(42, importer.Value); + + } + + [Fact] + public void ImportSingleFromInternal() + { + var container = ContainerFactory.Create(); + var importer = new Int32Importer(); + var exporter = new Int32ExporterInternal(42); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + batch.AddPart(exporter); + container.Compose(batch); + + Assert.Equal(42, importer.Value); + } + + [Fact] + public void ImportSingleToInternal() + { + var container = ContainerFactory.Create(); + var importer = new Int32ImporterInternal(); + var exporter = new Int32Exporter(42); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + batch.AddPart(exporter); + container.Compose(batch); + + Assert.Equal(42, importer.Value); + } + + [Fact] + public void ImportSingleIntoCollection() + { + var container = ContainerFactory.Create(); + var importer = new Int32CollectionImporter(); + var exporter = new Int32Exporter(42); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + batch.AddPart(exporter); + container.Compose(batch); + + EnumerableAssert.AreEqual(importer.Values, 42); + } + + [Fact] + public void ImportValuesNameless() + { + var container = ContainerFactory.Create(); + ImporterNameless importer; + ExporterNameless exporter42 = new ExporterNameless(42); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer = new ImporterNameless()); + batch.AddPart(exporter42); + container.Compose(batch); + + Assert.Equal(42, importer.ValueReadWrite); + Assert.Equal(42, importer.MetadataReadWrite.Value); + } + + [Fact] + public void ImportValueExceptionMissing() + { + var container = ContainerFactory.Create(); + Importer importer; + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer = new Importer()); + + CompositionAssert.ThrowsChangeRejectedError(ErrorId.ImportEngine_PartCannotSetImport, + ErrorId.ImportEngine_ImportCardinalityMismatch, + RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + + [Fact] + public void ImportValueExceptionMultiple() + { + var container = ContainerFactory.Create(); + Importer importer = new Importer(); + Exporter exporter42 = new Exporter(42); + Exporter exporter6 = new Exporter(6); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + batch.AddPart(exporter42); + batch.AddPart(exporter6); + + CompositionAssert.ThrowsChangeRejectedError(ErrorId.ImportEngine_PartCannotSetImport, + RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + + [Fact] + public void ImportValueExceptionSetterException() + { + var container = ContainerFactory.Create(); + ImporterInvalidSetterException importer = new ImporterInvalidSetterException(); + Exporter exporter42 = new Exporter(42); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + batch.AddPart(exporter42); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotActivate, + ErrorId.ReflectionModel_ImportThrewException, + RetryMode.DoNotRetry, () => + { + container.Compose(batch); + }); + } + + [Fact] + public void ImportValueExceptionLazily() + { + var catalog = new AssemblyCatalog(typeof(ImportImporterInvalidSetterExceptionLazily).Assembly); + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(ImportImporterInvalidSetterExceptionLazily), typeof(ImporterInvalidSetterException)); + var invalidLazy = container.GetExportedValue(); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotGetExportedValue, + ErrorId.ImportEngine_PartCannotActivate, + ErrorId.ReflectionModel_ImportThrewException, RetryMode.DoNotRetry, () => + { + var value = invalidLazy.Value.Value; + }); + } + + [ConditionalFact(Helpers.ComImportAvailable)] + [PlatformSpecific(TestPlatforms.Windows)] + [ActiveIssue(25498)] + public void ImportValueComComponent() + { + CTaskScheduler scheduler = new CTaskScheduler(); + + try + { + var container = ContainerFactory.Create(); + var importer = new ImportComComponent(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddParts(importer); + batch.AddExportedValue("TaskScheduler", (ITaskScheduler)scheduler); + + container.Compose(batch); + + Assert.Equal(scheduler, importer.TaskScheduler); + } + finally + { + Marshal.ReleaseComObject(scheduler); + } + } + + [ConditionalFact(Helpers.ComImportAvailable)] + [PlatformSpecific(TestPlatforms.Windows)] + [ActiveIssue(25498)] + public void DelayImportValueComComponent() + { + CTaskScheduler scheduler = new CTaskScheduler(); + + try + { + var container = ContainerFactory.Create(); + var importer = new DelayImportComComponent(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddParts(importer); + batch.AddExportedValue("TaskScheduler", (ITaskScheduler)scheduler); + + container.Compose(batch); + + Assert.Equal(scheduler, importer.TaskScheduler.Value); + } + finally + { + Marshal.ReleaseComObject(scheduler); + } + } + + [Fact] + [Trait("Type", "Integration")] + public void OptionalImportsOfValueTypesAreBoundToDefaultWhenNotSatisfied() + { + var container = ContainerFactory.Create(); + var importer = new OptionalImport(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + container.Compose(batch); + + Assert.Equal(1, importer.ValueTypeSetCount); + Assert.Equal(0, importer.ValueType); + } + + [Fact] + [Trait("Type", "Integration")] + public void OptionalImportsOfNullableValueTypesAreBoundToDefaultWhenNotSatisfied() + { + var container = ContainerFactory.Create(); + var importer = new OptionalImport(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + container.Compose(batch); + + Assert.Equal(1, importer.NullableValueTypeSetCount); + Assert.Null(importer.NullableValueType); + } + + [Fact] + [Trait("Type", "Integration")] + public void OptionalImportsOfReferenceTypesAreBoundToDefaultWhenNotSatisfied() + { + var container = ContainerFactory.Create(); + var importer = new OptionalImport(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + container.Compose(batch); + + Assert.Equal(1, importer.ReferenceTypeSetCount); + Assert.Null(importer.ReferenceType); + } + + [Fact] + [Trait("Type", "Integration")] + public void OptionalImportsOfExportValueTypesAreBoundToDefaultWhenNotSatisfied() + { + var container = ContainerFactory.Create(); + var importer = new OptionalExport(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + container.Compose(batch); + + Assert.Equal(1, importer.ValueTypeSetCount); + Assert.Null(importer.ValueType); + } + + [Fact] + [Trait("Type", "Integration")] + public void OptionalImportsOfExportNullableValueTypesAreBoundToDefaultWhenNotSatisfied() + { + var container = ContainerFactory.Create(); + var importer = new OptionalExport(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + container.Compose(batch); + + Assert.Equal(1, importer.NullableValueTypeSetCount); + Assert.Null(importer.NullableValueType); + } + + [Fact] + [Trait("Type", "Integration")] + public void OptionalImportsOfExportReferenceTypesAreBoundToDefaultWhenNotSatisfied() + { + var container = ContainerFactory.Create(); + var importer = new OptionalExport(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + container.Compose(batch); + + Assert.Equal(1, importer.ReferenceTypeSetCount); + Assert.Null(importer.ReferenceType); + } + + [Fact] + [Trait("Type", "Integration")] + public void OptionalImportsOfValueTypesAreReboundToDefaultWhenExportIsRemoved() + { + var container = ContainerFactory.Create(); + var importer = new OptionalImport(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + var key = batch.AddExportedValue("ValueType", 10); + + container.Compose(batch); + + Assert.Equal(1, importer.ValueTypeSetCount); + Assert.Equal(10, importer.ValueType); + + batch = new CompositionBatch(); + batch.RemovePart(key); + container.Compose(batch); + + Assert.Equal(2, importer.ValueTypeSetCount); + Assert.Equal(0, importer.ValueType); + } + + [Fact] + [Trait("Type", "Integration")] + public void OptionalImportsOfNullableValueTypesAreReboundToDefaultWhenExportIsRemoved() + { + var container = ContainerFactory.Create(); + var importer = new OptionalImport(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + var key = batch.AddExportedValue("NullableValueType", 10); + + container.Compose(batch); + Assert.Equal(1, importer.NullableValueTypeSetCount); + Assert.Equal(10, importer.NullableValueType); + + batch = new CompositionBatch(); + batch.RemovePart(key); + container.Compose(batch); + + Assert.Equal(2, importer.NullableValueTypeSetCount); + Assert.Null(importer.NullableValueType); + } + + [Fact] + [Trait("Type", "Integration")] + public void OptionalImportsOfReferenceTypesAreReboundToDefaultWhenExportIsRemoved() + { + var container = ContainerFactory.Create(); + var importer = new OptionalImport(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + var key = batch.AddExportedValue("ReferenceType", "Bar"); + + container.Compose(batch); + Assert.Equal(1, importer.ReferenceTypeSetCount); + Assert.Equal("Bar", importer.ReferenceType); + + batch = new CompositionBatch(); + batch.RemovePart(key); + container.Compose(batch); + + Assert.Equal(2, importer.ReferenceTypeSetCount); + Assert.Null(importer.ReferenceType); + } + + [Fact] + [Trait("Type", "Integration")] + public void OptionalImportsOfExportValueTypesAreReboundToDefaultWhenExportIsRemoved() + { + var container = ContainerFactory.Create(); + var importer = new OptionalExport(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + var key = batch.AddExportedValue("ValueType", 10); + + container.Compose(batch); + + Assert.Equal(1, importer.ValueTypeSetCount); + Assert.Equal(10, importer.ValueType.Value); + + batch = new CompositionBatch(); + batch.RemovePart(key); + container.Compose(batch); + + Assert.Equal(2, importer.ValueTypeSetCount); + Assert.Null(importer.ValueType); + } + + [Fact] + [Trait("Type", "Integration")] + public void OptionalImportsOfExportNullableValueTypesAreReboundToDefaultWhenExportIsRemoved() + { + var container = ContainerFactory.Create(); + var importer = new OptionalExport(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + var key = batch.AddExportedValue("NullableValueType", 10); + + container.Compose(batch); + Assert.Equal(1, importer.NullableValueTypeSetCount); + Assert.Equal(10, importer.NullableValueType.Value); + + batch = new CompositionBatch(); + batch.RemovePart(key); + container.Compose(batch); + + Assert.Equal(2, importer.NullableValueTypeSetCount); + Assert.Null(importer.NullableValueType); + } + + [Fact] + [Trait("Type", "Integration")] + public void OptionalImportsOfExportReferenceTypesAreReboundToDefaultWhenExportIsRemoved() + { + var container = ContainerFactory.Create(); + var importer = new OptionalExport(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + var key = batch.AddExportedValue("ReferenceType", "Bar"); + + container.Compose(batch); + Assert.Equal(1, importer.ReferenceTypeSetCount); + Assert.Equal("Bar", importer.ReferenceType.Value); + + batch = new CompositionBatch(); + batch.RemovePart(key); + container.Compose(batch); + + Assert.Equal(2, importer.ReferenceTypeSetCount); + Assert.Null(importer.ReferenceType); + } + + public class OptionalImport + { + public int ValueTypeSetCount; + public int NullableValueTypeSetCount; + public int ReferenceTypeSetCount; + + private int _valueType; + private int? _nullableValueType; + private string _referenceType; + + [Import("ValueType", AllowDefault = true, AllowRecomposition = true)] + public int ValueType + { + get { return _valueType; } + set + { + ValueTypeSetCount++; + _valueType = value; + } + } + + [Import("NullableValueType", AllowDefault = true, AllowRecomposition = true)] + public int? NullableValueType + { + get { return _nullableValueType; } + set + { + NullableValueTypeSetCount++; + _nullableValueType = value; + } + } + + [Import("ReferenceType", AllowDefault = true, AllowRecomposition = true)] + public string ReferenceType + { + get { return _referenceType; } + set + { + ReferenceTypeSetCount++; + _referenceType = value; + } + } + } + + public class OptionalExport + { + public int ValueTypeSetCount; + public int NullableValueTypeSetCount; + public int ReferenceTypeSetCount; + + private Lazy _valueType; + private Lazy _nullableValueType; + private Lazy _referenceType; + + [Import("ValueType", AllowDefault = true, AllowRecomposition = true)] + public Lazy ValueType + { + get { return _valueType; } + set + { + ValueTypeSetCount++; + _valueType = value; + } + } + + [Import("NullableValueType", AllowDefault = true, AllowRecomposition = true)] + public Lazy NullableValueType + { + get { return _nullableValueType; } + set + { + NullableValueTypeSetCount++; + _nullableValueType = value; + } + } + + [Import("ReferenceType", AllowDefault = true, AllowRecomposition = true)] + public Lazy ReferenceType + { + get { return _referenceType; } + set + { + ReferenceTypeSetCount++; + _referenceType = value; + } + } + } + + private class DelayDuckImporter + { + [Import("Duck")] + public Lazy Duck + { + get; + set; + } + } + + private class DuckImporter + { + [Import("Duck")] + public IDuck Duck + { + get; + set; + } + } + + public class QuackLikeADuck + { + public virtual string Quack() + { + return "Quack"; + } + } + + public interface IDuck + { + string Quack(); + } + + [ComImport] + [Guid("148BD52A-A2AB-11CE-B11F-00AA00530503")] + private class CTaskScheduler + { // This interface doesn't implement + // ITaskScheduler deliberately + } + + [Guid("148BD527-A2AB-11CE-B11F-00AA00530503")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + private interface ITaskScheduler + { + void FakeMethod(); + } + + private class ImportComComponent + { + [Import("TaskScheduler")] + public ITaskScheduler TaskScheduler + { + get; + set; + } + } + + private class DelayImportComComponent + { + [Import("TaskScheduler")] + public Lazy TaskScheduler + { + get; + set; + } + } + + public class Importer + { + public Importer() + { + } + + [Import("Value")] + public int ValueReadWrite { get; set; } + + [ImportMany("Value")] + public IList SingleValueCollectionReadWrite { get; set; } + + [Import("EmptyValue", AllowDefault = true)] + public int ValueEmptyOptional { get; set; } + + [ImportMany("CollectionValue", typeof(IList))] + public IList ValueCollection { get; set; } + + } + + public class ImporterNameless + { + + public ImporterNameless() + { + } + + [Import] + public int ValueReadWrite { get; set; } + + [Import] + public Lazy MetadataReadWrite { get; set; } + + } + + public class ImporterInvalidWrongType + { + [Import("Value")] + public DateTime ValueReadWrite { get; set; } + } + + [Export] + public class ImporterInvalidSetterException + { + [ImportMany("Value")] + public IEnumerable ValueReadWrite { get { return null; } set { throw new InvalidOperationException(); } } + } + + [Export] + public class ImportImporterInvalidSetterExceptionLazily + { + [Import] + public Lazy Value { get; set; } + } + + [PartNotDiscoverable] + public class Exporter + { + List collectionValue = new List(); + + public Exporter(int value) + { + Value = value; + } + + [Export("Value")] + public int Value { get; set; } + + [Export("CollectionValue")] + public IList CollectionValue { get { return collectionValue; } } + + } + + public class ExporterNameless + { + + public ExporterNameless(int value) + { + Value = value; + } + + [Export] + public int Value { get; set; } + + } + + public class ExportsString + { + [Export] + public string ExportedString = "Test"; + } + + public class ExportsInvalidListOfExportOfString + { + [Export(typeof(List>))] + public string ExportedString = "Test"; + } + + public class ExportsValidListOfExportOfString + { + [Export(typeof(List>))] + public List> ExportedString = new List>(); + } + + [Export] + public class ImportsListOfExportOfString + { + [Import(AllowDefault = true)] + public List> ExportedList { get; set; } + } + + [Fact] + public void ImportListOfExportWithOnlySingleElementsAvailable_ShouldNotFindExport() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(ExportsString), typeof(ImportsListOfExportOfString)); + var importer = container.GetExportedValue(); + Assert.Null(importer.ExportedList); + + var part = AttributedModelServices.CreatePartDefinition(typeof(ImportsListOfExportOfString), null); + var contract = AttributedModelServices.GetContractName(typeof(List>)); + Assert.Equal(contract, ((ContractBasedImportDefinition)part.ImportDefinitions.First()).ContractName); + } + + [Fact] + public void ImportListOfExportWithInvalidCollectionAvailable_ShouldThrowMismatch() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(ExportsInvalidListOfExportOfString), typeof(ImportsListOfExportOfString)); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotGetExportedValue, () => + container.GetExportedValue()); + } + + [Fact] + public void ImportListOfExportWithValidCollectionAvailable_ShouldSatisfyImport() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(ExportsValidListOfExportOfString), typeof(ImportsListOfExportOfString)); + var importer = container.GetExportedValue(); + Assert.Equal(0, importer.ExportedList.Count); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionContainerTests.cs.REMOVED.git-id b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionContainerTests.cs.REMOVED.git-id new file mode 100644 index 0000000000..f9814242ae --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionContainerTests.cs.REMOVED.git-id @@ -0,0 +1 @@ +6c53aba88198595f75a9121aebc3956a65d093c9 \ No newline at end of file diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionErrorDebuggerProxyTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionErrorDebuggerProxyTests.cs new file mode 100644 index 0000000000..fa78fe1384 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionErrorDebuggerProxyTests.cs @@ -0,0 +1,67 @@ +// 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.ComponentModel.Composition.Factories; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class CompositionErrorDebuggerProxyTests + { + [Fact] + public void Constructor_NullAsErrorArgument_ShouldThrowArgumentNull() + { + Assert.Throws("error", () => + { + new CompositionErrorDebuggerProxy((CompositionError)null); + }); + } + + [Fact] + public void Constructor_ValueAsErrorArgument_ShouldSetExceptionProperty() + { + var expectations = Expectations.GetInnerExceptionsWithNull(); + + foreach (var e in expectations) + { + var error = ErrorFactory.Create(e); + + var proxy = new CompositionErrorDebuggerProxy(error); + + Assert.Same(error.Exception, proxy.Exception); + } + } + + [Fact] + public void Constructor_ValueAsErrorArgument_ShouldSetMessageProperty() + { + var expectations = Expectations.GetExceptionMessages(); + + foreach (var e in expectations) + { + var error = ErrorFactory.Create(e); + + var proxy = new CompositionErrorDebuggerProxy(error); + + Assert.Same(error.Description, proxy.Description); + } + } + + [Fact] + public void Constructor_ValueAsErrorArgument_ShouldSetElementProperty() + { + var expectations = Expectations.GetCompositionElementsWithNull(); + + foreach (var e in expectations) + { + var error = ErrorFactory.Create(e); + + var proxy = new CompositionErrorDebuggerProxy(error); + + Assert.Same(error.Element, proxy.Element); + } + } + + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionErrorIdTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionErrorIdTests.cs new file mode 100644 index 0000000000..119e82a65e --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionErrorIdTests.cs @@ -0,0 +1,19 @@ +// 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.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class CompositionErrorIdTests + { + [Fact] + [ActiveIssue(25498, TargetFrameworkMonikers.UapAot)] + public void CompositionErrorIdsAreInSyncWithErrorIds() + { + ExtendedAssert.EnumsContainSameValues(); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionErrorTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionErrorTests.cs new file mode 100644 index 0000000000..ca2b495d77 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionErrorTests.cs @@ -0,0 +1,462 @@ +// 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.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class CompositionErrorTests + { + [Fact] + public void Constructor1_NullAsMessageArgument_ShouldSetMessagePropertyToEmptyString() + { + var error = new CompositionError((string)null); + + Assert.Equal("", error.Description); + } + + [Fact] + public void Constructor2_NullAsMessageArgument_ShouldSetMessagePropertyToEmptyString() + { + var error = new CompositionError((string)null, ElementFactory.Create()); + + Assert.Equal("", error.Description); + } + + [Fact] + public void Constructor3_NullAsMessageArgument_ShouldSetMessagePropertyToEmptyString() + { + var error = new CompositionError((string)null, new Exception()); + + Assert.Equal("", error.Description); + } + + [Fact] + public void Constructor4_NullAsMessageArgument_ShouldSetMessagePropertyToEmptyString() + { + var error = new CompositionError((string)null, ElementFactory.Create(), new Exception()); + + Assert.Equal("", error.Description); + } + + [Fact] + public void Constructor5_NullAsMessageArgument_ShouldSetMessagePropertyToEmptyString() + { + var error = new CompositionError(CompositionErrorId.Unknown, (string)null, ElementFactory.Create(), new Exception()); + + Assert.Equal("", error.Description); + } + + [Fact] + public void Constructor1_ValueAsMessageArgument_ShouldSetMessageProperty() + { + var expectations = Expectations.GetExceptionMessages(); + + Assert.All(expectations, e => + { + var exception = new CompositionError(e); + Assert.Equal(e, exception.Description); + }); + } + + [Fact] + public void Constructor2_ValueAsMessageArgument_ShouldSetMessageProperty() + { + var expectations = Expectations.GetExceptionMessages(); + + Assert.All(expectations, e => + { + var exception = new CompositionError(e, ElementFactory.Create()); + + Assert.Equal(e, exception.Description); + }); + } + + [Fact] + public void Constructor3_ValueAsMessageArgument_ShouldSetMessageProperty() + { + var expectations = Expectations.GetExceptionMessages(); + + Assert.All(expectations, e => + { + var exception = new CompositionError(e, new Exception()); + Assert.Equal(e, exception.Description); + }); + } + + [Fact] + public void Constructor4_ValueAsMessageArgument_ShouldSetMessageProperty() + { + var expectations = Expectations.GetExceptionMessages(); + + Assert.All(expectations, e => + { + var exception = new CompositionError(e, ElementFactory.Create(), new Exception()); + Assert.Equal(e, exception.Description); + }); + } + + [Fact] + public void Constructor5_ValueAsMessageArgument_ShouldSetMessageProperty() + { + var expectations = Expectations.GetExceptionMessages(); + + Assert.All(expectations, e => + { + var exception = new CompositionError(CompositionErrorId.Unknown, e, ElementFactory.Create(), new Exception()); + Assert.Equal(e, exception.Description); + }); + } + + [Fact] + public void Constructor1_ShouldSetExceptionPropertyToNull() + { + var error = new CompositionError("Description"); + + Assert.Null(error.Exception); + } + + [Fact] + public void Constructor2_ShouldSetExceptionPropertyToNull() + { + var error = new CompositionError("Description", ElementFactory.Create()); + + Assert.Null(error.Exception); + } + + [Fact] + public void Constructor3_NullAsExceptionArgument_ShouldSetExceptionPropertyToNull() + { + var error = new CompositionError("Description", (Exception)null); + + Assert.Null(error.Exception); + } + + [Fact] + public void Constructor4_NullAsExceptionArgument_ShouldSetExceptionPropertyToNull() + { + var error = new CompositionError("Description", ElementFactory.Create(), (Exception)null); + + Assert.Null(error.Exception); + } + + [Fact] + public void Constructor5_NullAsExceptionArgument_ShouldSetExceptionPropertyToNull() + { + var error = new CompositionError(CompositionErrorId.Unknown, "Description", ElementFactory.Create(), (Exception)null); + + Assert.Null(error.Exception); + } + + [Fact] + public void Constructor3_ValueAsExceptionArgument_ShouldSetExceptionProperty() + { + var expectations = Expectations.GetInnerExceptions(); + + Assert.All(expectations, e => + { + var error = new CompositionError("Description", e); + Assert.Same(e, error.Exception); + }); + } + + [Fact] + public void Constructor4_ValueAsExceptionArgument_ShouldSetExceptionProperty() + { + var expectations = Expectations.GetInnerExceptions(); + + Assert.All(expectations, e => + { + var error = new CompositionError("Description", ElementFactory.Create(), e); + Assert.Same(e, error.Exception); + }); + } + + [Fact] + public void Constructor5_ValueAsExceptionArgument_ShouldSetExceptionProperty() + { + var expectations = Expectations.GetInnerExceptions(); + + Assert.All(expectations, e => + { + var error = new CompositionError(CompositionErrorId.Unknown, "Description", ElementFactory.Create(), e); + Assert.Same(e, error.Exception); + }); + } + + [Fact] + public void Constructor1_ShouldSetInnerExceptionPropertyToNull() + { + var error = new CompositionError("Description"); + + Assert.Null(error.InnerException); + } + + [Fact] + public void Constructor2_ShouldSetInnerExceptionPropertyToNull() + { + var error = new CompositionError("Description", ElementFactory.Create()); + + Assert.Null(error.InnerException); + } + + [Fact] + public void Constructor3_NullAsExceptionArgument_ShouldSetInnerExceptionPropertyToNull() + { + var error = new CompositionError("Description", (Exception)null); + + Assert.Null(error.InnerException); + } + + [Fact] + public void Constructor4_NullAsExceptionArgument_ShouldSetInnerExceptionPropertyToNull() + { + var error = new CompositionError("Description", ElementFactory.Create(), (Exception)null); + + Assert.Null(error.InnerException); + } + + [Fact] + public void Constructor5_NullAsExceptionArgument_ShouldSetInnerExceptionPropertyToNull() + { + var error = new CompositionError(CompositionErrorId.Unknown, "Description", ElementFactory.Create(), (Exception)null); + + Assert.Null(error.InnerException); + } + + [Fact] + public void Constructor3_ValueAsExceptionArgument_ShouldSetInnerExceptionProperty() + { + var expectations = Expectations.GetInnerExceptions(); + + Assert.All(expectations, e => + { + var error = new CompositionError("Description", e); + Assert.Same(e, error.InnerException); + }); + } + + [Fact] + public void Constructor4_ValueAsExceptionArgument_ShouldSetInnerExceptionProperty() + { + var expectations = Expectations.GetInnerExceptions(); + + Assert.All(expectations, e => + { + var error = new CompositionError("Description", ElementFactory.Create(), e); + Assert.Same(e, error.InnerException); + }); + } + + [Fact] + public void Constructor1_ShouldSetICompositionErrorIdPropertyToCompositionErrorIdUnknown() + { + var error = new CompositionError("Description"); + + Assert.Equal(CompositionErrorId.Unknown, error.Id); + } + + [Fact] + public void Constructor2_ShouldSetICompositionErrorIdPropertyToCompositionErrorIdUnknown() + { + var error = new CompositionError("Description", ElementFactory.Create()); + + Assert.Equal(CompositionErrorId.Unknown, error.Id); + } + + [Fact] + public void Constructor3_ShouldSetICompositionErrorIdPropertyToCompositionErrorIdUnknown() + { + var error = new CompositionError("Description", new Exception()); + + Assert.Equal(CompositionErrorId.Unknown, error.Id); + } + + [Fact] + public void Constructor4_ShouldSetICompositionErrorIdPropertyToCompositionErrorIdUnknown() + { + var error = new CompositionError("Description", ElementFactory.Create(), new Exception()); + + Assert.Equal(CompositionErrorId.Unknown, error.Id); + } + + [Fact] + public void Constructor5_ValueAsIdArgument_ShouldSetICompositionErrorIdProperty() + { + var expectations = Expectations.GetEnumValues(); + + Assert.All(expectations, e => + { + var error = new CompositionError(e, "Description", ElementFactory.Create(), new Exception()); + Assert.Equal(e, error.Id); + }); + } + + [Fact] + public void Constructor1_ShouldSetElementPropertyToNull() + { + var exception = new CompositionError("Description"); + + Assert.Null(exception.Element); + } + + [Fact] + public void Constructor2_NullAsElementArgument_ShouldSetElementPropertyToNull() + { + var exception = new CompositionError("Description", (ICompositionElement)null); + + Assert.Null(exception.Element); + } + + [Fact] + public void Constructor3_ShouldSetElementPropertyToNull() + { + var exception = new CompositionError("Description", new Exception()); + + Assert.Null(exception.Element); + } + + [Fact] + public void Constructor4_NullAsElementArgument_ShouldSetElementPropertyToNull() + { + var exception = new CompositionError("Description", (ICompositionElement)null, new Exception()); + + Assert.Null(exception.Element); + } + + [Fact] + public void Constructor5_NullAsElementArgument_ShouldSetElementPropertyToNull() + { + var exception = new CompositionError(CompositionErrorId.Unknown, "Description", (ICompositionElement)null, new Exception()); + + Assert.Null(exception.Element); + } + + [Fact] + public void Constructor2_ValueAsElementArgument_ShouldSetElementProperty() + { + var expectations = Expectations.GetCompositionElements(); + + Assert.All(expectations, e => + { + var exception = new CompositionError("Description", (ICompositionElement)e); + Assert.Same(e, exception.Element); + }); + } + + [Fact] + public void Constructor4_ValueAsElementArgument_ShouldSetElementProperty() + { + var expectations = Expectations.GetCompositionElements(); + + Assert.All(expectations, e => + { + var exception = new CompositionError("Description", (ICompositionElement)e, new Exception()); + Assert.Same(e, exception.Element); + }); + } + + [Fact] + public void Constructor5_ValueAsElementArgument_ShouldSetElementProperty() + { + var expectations = Expectations.GetCompositionElements(); + + Assert.All(expectations, e => + { + var exception = new CompositionError(CompositionErrorId.Unknown, "Description", (ICompositionElement)e, new Exception()); + Assert.Same(e, exception.Element); + }); + } + + [Fact] + public void ToString_ShouldReturnMessageProperty() + { + var expectations = Expectations.GetExceptionMessages(); + + Assert.All(expectations, e => + { + var error = CreateCompositionError(e); + Assert.Equal(error.Description, error.ToString()); + }); + } + + private static CompositionError CreateCompositionError() + { + return CreateCompositionError(CompositionErrorId.Unknown, (string)null, (ICompositionElement)null, (Exception)null); + } + + private static CompositionError CreateCompositionError(string message) + { + return CreateCompositionError(CompositionErrorId.Unknown, message, (ICompositionElement)null, (Exception)null); + } + + private static CompositionError CreateCompositionError(CompositionErrorId id) + { + return CreateCompositionError(id, (string)null, (ICompositionElement)null, (Exception)null); + } + + private static CompositionError CreateCompositionError(Exception exception) + { + return CreateCompositionError(CompositionErrorId.Unknown, (string)null, (ICompositionElement)null, exception); + } + + private static CompositionError CreateCompositionError(ICompositionElement element) + { + return CreateCompositionError(CompositionErrorId.Unknown, (string)null, element, (Exception)null); + } + + private static CompositionError CreateCompositionError(CompositionErrorId id, string message, ICompositionElement element, Exception exception) + { + return new CompositionError(id, message, element, exception); + } + + private static CompositionError CreateCompositionError(params string[] messages) + { + CompositionError error = null; + foreach (string message in messages.Reverse()) + { + CompositionException exception = null; + if (error != null) + { + exception = CreateCompositionException(error); + } + + error = ErrorFactory.Create(message, exception); + } + + return error; + } + + private static CompositionError CreateCompositionErrorWithException(params string[] messages) + { + Exception innerException = null; + foreach (string message in messages.Skip(1).Reverse()) + { + innerException = new Exception(message, innerException); + } + + return new CompositionError(messages[0], innerException); + } + + private static CompositionError CreateCompostionErrorWithCompositionException(string message1, string message2) + { + var exception = CreateCompositionException(new Exception(message2)); + + return new CompositionError(message1, exception); + } + + private static CompositionException CreateCompositionException(CompositionError error) + { + return new CompositionException(error); + } + + private static CompositionException CreateCompositionException(Exception innerException) + { + return new CompositionException("Description", innerException, null); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionExceptionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionExceptionTests.cs new file mode 100644 index 0000000000..0504111f91 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionExceptionTests.cs @@ -0,0 +1,496 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Primitives; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + [Serializable] + public class CompositionExceptionTests + { + [Fact] + public void Constructor1_ShouldSetMessagePropertyToDefault() + { + var exception = new CompositionException(); + + ExceptionAssert.HasDefaultMessage(exception); + } + + [Fact] + public void Constructor2_NullAsMessageArgument_ShouldSetMessagePropertyToDefault() + { + var exception = new CompositionException((string)null); + + ExceptionAssert.HasDefaultMessage(exception); + } + + [Fact] + public void Constructor3_EmptyEnumerableAsErrorsArgument_ShouldSetMessagePropertyToDefault() + { + var exception = new CompositionException(Enumerable.Empty()); + + ExceptionAssert.HasDefaultMessage(exception); + } + + [Fact] + public void Constructor4_NullAsMessageArgument_ShouldSetMessagePropertyToDefault() + { + var exception = new CompositionException((string)null, new Exception()); + + ExceptionAssert.HasDefaultMessage(exception); + } + + [Fact] + public void Constructor5_NullAsMessageArgument_ShouldSetMessagePropertyToDefault() + { + var exception = new CompositionException((string)null, new Exception(), Enumerable.Empty()); + + ExceptionAssert.HasDefaultMessage(exception); + } + + [Fact] + public void Constructor2_ValueAsMessageArgument_ShouldSetMessageProperty() + { + var expectations = Expectations.GetExceptionMessages(); + + foreach (var e in expectations) + { + var exception = new CompositionException(e); + + Assert.Equal(e, exception.Message); + } + } + + [Fact] + public void Constructor4_ValueAsMessageArgument_ShouldSetMessageProperty() + { + var expectations = Expectations.GetExceptionMessages(); + + foreach (var e in expectations) + { + var exception = new CompositionException(e, new Exception()); + + Assert.Equal(e, exception.Message); + } + } + + [Fact] + public void Constructor5_ValueAsMessageArgument_ShouldSetMessageProperty() + { + var expectations = Expectations.GetExceptionMessages(); + + foreach (var e in expectations) + { + var exception = new CompositionException(e, new Exception(), Enumerable.Empty()); + + Assert.Equal(e, exception.Message); + } + } + + [Fact] + public void Constructor1_ShouldSetInnerExceptionPropertyToNull() + { + var exception = new CompositionException(); + + Assert.Null(exception.InnerException); + } + + [Fact] + public void Constructor2_ShouldSetInnerExceptionPropertyToNull() + { + var exception = new CompositionException("Message"); + + Assert.Null(exception.InnerException); + } + + [Fact] + public void Constructor3_ShouldSetInnerExceptionPropertyToNull() + { + var exception = new CompositionException(Enumerable.Empty()); + + Assert.Null(exception.InnerException); + } + + [Fact] + public void Constructor4_NullAsInnerExceptionArgument_ShouldSetInnerExceptionPropertyToNull() + { + var exception = new CompositionException("Message", (Exception)null); + + Assert.Null(exception.InnerException); + } + + [Fact] + public void Constructor5_NullAsInnerExceptionArgument_ShouldSetInnerExceptionPropertyToNull() + { + var exception = new CompositionException("Message", (Exception)null, Enumerable.Empty()); + + Assert.Null(exception.InnerException); + } + + [Fact] + public void Constructor4_ValueAsInnerExceptionArgument_ShouldSetInnerExceptionProperty() + { + var expectations = Expectations.GetInnerExceptions(); + + foreach (var e in expectations) + { + var exception = new CompositionException("Message", e); + + Assert.Same(e, exception.InnerException); + } + } + + [Fact] + public void Constructor5_ValueAsInnerExceptionArgument_ShouldSetInnerExceptionProperty() + { + var expectations = Expectations.GetInnerExceptions(); + + foreach (var e in expectations) + { + var exception = new CompositionException("Message", e, Enumerable.Empty()); + + Assert.Same(e, exception.InnerException); + } + } + + [Fact] + public void Constructor2_ArrayWithNullAsErrorsArgument_ShouldThrowArgument() + { + var errors = new CompositionError[] { null }; + + Assert.Throws("errors", () => + { + new CompositionException(errors); + }); + } + + [Fact] + public void Constructor5_ArrayWithNullAsErrorsArgument_ShouldThrowArgument() + { + var errors = new CompositionError[] { null }; + + Assert.Throws("errors", () => + { + new CompositionException("Message", new Exception(), errors); + }); + } + + [Fact] + public void Constructor1_ShouldSetErrorsPropertyToEmpty() + { + var exception = new CompositionException(); + + Assert.Empty(exception.Errors); + } + + [Fact] + public void Constructor2_NullAsErrorsArgument_ShouldSetErrorsPropertyToEmptyEnumerable() + { + var exception = new CompositionException((IEnumerable)null); + + Assert.Empty(exception.Errors); + } + + [Fact] + public void Constructor2_EmptyEnumerableAsErrorsArgument_ShouldSetErrorsPropertyToEmptyEnumerable() + { + var exception = new CompositionException(Enumerable.Empty()); + + Assert.Empty(exception.Errors); + } + + [Fact] + public void Constructor2_ValueAsErrorsArgument_ShouldSetErrorsProperty() + { + var expectations = Expectations.GetCompositionErrors(); + + foreach (var e in expectations) + { + var exception = new CompositionException(e); + + EqualityExtensions.CheckEquals(e, exception.Errors); + } + } + + [Fact] + public void Constructor2_ArrayAsAsErrorsArgument_ShouldNotAllowModificationAfterConstruction() + { + var error = CreateCompositionError(); + var errors = new CompositionError[] { error }; + + var exception = new CompositionException(errors); + + errors[0] = null; + + EnumerableAssert.AreEqual(exception.Errors, error); + } + + [Fact] + public void Constructor3_ShouldSetErrorsPropertyToEmpty() + { + var exception = new CompositionException(); + + Assert.Empty(exception.Errors); + } + + [Fact] + public void Constructor4_ShouldSetErrorsPropertyToEmptyEnumerable() + { + var exception = new CompositionException("Message", new Exception()); + + Assert.Empty(exception.Errors); + } + + [Fact] + public void Constructor5_NullAsErrorsArgument_ShouldSetErrorsPropertyToEmptyEnumerable() + { + var exception = new CompositionException("Message", new Exception(), (IEnumerable)null); + + Assert.Empty(exception.Errors); + } + + [Fact] + public void Constructor5_EmptyEnumerableAsErrorsArgument_ShouldSetErrorsPropertyToEmptyEnumerable() + { + var exception = new CompositionException("Message", new Exception(), Enumerable.Empty()); + + Assert.Empty(exception.Errors); + } + + [Fact] + public void Constructor5_ValueAsErrorsArgument_ShouldSetErrorsProperty() + { + var expectations = Expectations.GetCompositionErrors(); + + foreach (var e in expectations) + { + var exception = new CompositionException("Message", new Exception(), e); + + EqualityExtensions.CheckEquals(e, exception.Errors); + } + } + + [Fact] + public void Constructor5_ArrayAsAsErrorsArgument_ShouldNotAllowModificationAfterConstruction() + { + var error = CreateCompositionError(); + var errors = new CompositionError[] { error }; + + var exception = new CompositionException("Message", new Exception(), errors); + + errors[0] = null; + + EnumerableAssert.AreEqual(exception.Errors, error); + } + + [Fact] + public void Message_ShouldIncludeElementGraph() + { + var expectations = new ExpectationCollection(); + CompositionError error = null; + + error = CreateCompositionErrorWithElementChain(1); + expectations.Add(error, GetElementGraphString(error)); + + error = CreateCompositionErrorWithElementChain(2); + expectations.Add(error, GetElementGraphString(error)); + + error = CreateCompositionErrorWithElementChain(3); + expectations.Add(error, GetElementGraphString(error)); + + error = CreateCompositionErrorWithElementChain(10); + expectations.Add(error, GetElementGraphString(error)); + + foreach (var e in expectations) + { + var exception = CreateCompositionException(new CompositionError[] { e.Input }); + + string result = exception.ToString(); + string expected = FixMessage(e.Output); + AssertExtensions.Contains(result, expected); + } + } + + [Fact] + public void Message_ShouldIncludeErrors() + { + var expectations = new ExpectationCollection, string>(); + expectations.Add(ErrorFactory.CreateFromDsl("Error"), "1 Error"); + expectations.Add(ErrorFactory.CreateFromDsl("Error|Error"), "1 Error|2 Error"); + expectations.Add(ErrorFactory.CreateFromDsl("Error|Error|Error"), "1 Error|2 Error|3 Error"); + expectations.Add(ErrorFactory.CreateFromDsl("Error(Error)"), "1 Error|Error"); + expectations.Add(ErrorFactory.CreateFromDsl("Error(Error|Error)"), "1 Error|Error|2 Error|Error"); + expectations.Add(ErrorFactory.CreateFromDsl("Error(Error|Error|Error)"), "1 Error|Error|2 Error|Error|3 Error|Error"); + expectations.Add(ErrorFactory.CreateFromDsl("Error(Error(Exception))"), "1 Exception|Error|Error"); + expectations.Add(ErrorFactory.CreateFromDsl("Error(Error|Exception)"), "1 Error|Error|2 Exception|Error"); + expectations.Add(ErrorFactory.CreateFromDsl("Error(Exception)"), "1 Exception|Error"); + expectations.Add(ErrorFactory.CreateFromDsl("Error(Exception(Exception))"), "1 Exception|Exception|Error"); + expectations.Add(ErrorFactory.CreateFromDsl("Error(Error(Exception)|Error)"), "1 Exception|Error|Error|2 Error|Error"); + + foreach (var e in expectations) + { + var exception = CreateCompositionException(e.Input); + + AssertMessage(exception, e.Output.Split('|')); + } + } + + [Fact] + public void Messsage_ShouldIncludeCountOfRootCauses() + { + var expectations = new ExpectationCollection, int>(); + expectations.Add(ErrorFactory.CreateFromDsl("Error"), 1); + expectations.Add(ErrorFactory.CreateFromDsl("Error|Error"), 2); + expectations.Add(ErrorFactory.CreateFromDsl("Error|Error|Error"), 3); + expectations.Add(ErrorFactory.CreateFromDsl("Error(Error)"), 1); + expectations.Add(ErrorFactory.CreateFromDsl("Error(Error)|Error(Error)"), 2); + expectations.Add(ErrorFactory.CreateFromDsl("Error(Error|Error)"), 2); + expectations.Add(ErrorFactory.CreateFromDsl("Error(Error|Error|Exception)"), 3); + + foreach (var e in expectations) + { + var exception = CreateCompositionException(e.Input); + + AssertMessage(exception, e.Output, CultureInfo.CurrentCulture); + } + } + + [Fact] + public void Message_ShouldFormatCountOfRootCausesUsingTheCurrentCulture() + { + var cultures = Expectations.GetCulturesForFormatting(); + + foreach (var culture in cultures) + { + using (new CurrentCultureContext(culture)) + { + var errors = CreateCompositionErrors(1000); + var exception = CreateCompositionException(errors); + AssertMessage(exception, 1000, culture); + + errors = CreateCompositionErrors(1); + exception = CreateCompositionException(errors); + AssertMessage(exception, 1, culture); + } + } + } + + private string GetElementGraphString(CompositionError error) + { + StringBuilder writer = new StringBuilder(); + ICompositionElement element = error.Element; + writer.AppendFormat(CultureInfo.CurrentCulture, SR.CompositionException_ElementPrefix, element.DisplayName); + + while ((element = element.Origin) != null) + { + writer.AppendFormat(CultureInfo.CurrentCulture, SR.CompositionException_OriginFormat, SR.CompositionException_OriginSeparator, element.DisplayName); + } + + return writer.ToString(); + } + + private void AssertMessage(CompositionException exception, int rootCauseCount, CultureInfo culture) + { + using (StringReader reader = new StringReader(exception.Message)) + { + string line = reader.ReadLine(); + + if (rootCauseCount == 1) + { + Assert.True(line.Contains(SR.CompositionException_SingleErrorWithSinglePath)); + } + else + { + Assert.True( + line.Contains(string.Format(CultureInfo.CurrentCulture, SR.CompositionException_SingleErrorWithMultiplePaths, rootCauseCount)) || + line.Contains(string.Format(CultureInfo.CurrentCulture, SR.CompositionException_MultipleErrorsWithMultiplePaths, rootCauseCount)) + ); + } + } + } + + private void AssertMessage(CompositionException exception, string[] expected) + { + using (StringReader reader = new StringReader(exception.Message)) + { + // Skip header + reader.ReadLine(); + + foreach (string expect in expected) + { + // Skip blank line + reader.ReadLine(); + Assert.Equal(FixMessage(expect), reader.ReadLine()); + } + } + } + + private string FixMessage(string expect) + { + string fixedPrefix = expect.Replace("", SR.CompositionException_ErrorPrefix + " "); + string fixedSeparator = fixedPrefix.Replace("", SR.CompositionException_PathsCountSeparator); + return fixedSeparator.Replace("", SR.CompositionException_OriginSeparator); + } + + private static CompositionError CreateCompositionError() + { + return CreateCompositionError("Description"); + } + + private static CompositionError CreateCompositionError(string message) + { + return new CompositionError(message); + } + + private static CompositionError CreateCompositionErrorWithElementChain(int count) + { + return new CompositionError("Description", ElementFactory.CreateChain(count)); + } + + private static CompositionError[] CreateCompositionErrors(int count) + { + CompositionError[] errors = new CompositionError[count]; + + for (int i = 0; i < count; i++) + { + errors[i] = CreateCompositionError("Description" + (i + 1)); + } + + return errors; + } + + private static CompositionException CreateCompositionException() + { + return CreateCompositionException((string)null, (Exception)null, (IEnumerable)null); + } + + private static CompositionException CreateCompositionException(string message) + { + return CreateCompositionException(message, (Exception)null, (IEnumerable)null); + } + + private static CompositionException CreateCompositionException(IEnumerable errors) + { + return CreateCompositionException((string)null, (Exception)null, errors); + } + + private static CompositionException CreateCompositionException(Exception innerException) + { + return CreateCompositionException((string)null, innerException, (IEnumerable)null); + } + + private static CompositionException CreateCompositionException(string message, Exception innerException, IEnumerable errors) + { + return new CompositionException(message, innerException, errors); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionResultOfTTest.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionResultOfTTest.cs new file mode 100644 index 0000000000..990f3d7da5 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionResultOfTTest.cs @@ -0,0 +1,457 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using System.Linq; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class CompositionResultOfTTest + { + [Fact] + public void Constructor1_ShouldSetErrorsPropertyToEmptyEnumerable() + { + var result = new CompositionResult(); + + Assert.Empty(result.Errors); + } + + [Fact] + public void Constructor2_ShouldSetErrorsPropertyToEmptyEnumerable() + { + var result = new CompositionResult("Value"); + + Assert.Empty(result.Errors); + } + + [Fact] + public void Constructor3_NullAsErrorsArgument_ShouldSetErrorsPropertyToEmptyEnumerable() + { + var result = new CompositionResult((CompositionError[])null); + + Assert.Empty(result.Errors); + } + + [Fact] + public void Constructor4_NullAsErrorsArgument_ShouldSetErrorsPropertyToEmptyEnumerable() + { + var result = new CompositionResult((IEnumerable)null); + + Assert.Empty(result.Errors); + } + + [Fact] + public void Constructor5_NullAsErrorsArgument_ShouldSetErrorsPropertyToEmptyEnumerable() + { + var result = new CompositionResult("Value", (IEnumerable)null); + + Assert.Empty(result.Errors); + } + + [Fact] + public void Constructor3_EmptyAsErrorsArgument_ShouldSetErrorsPropertyToEmptyEnumerable() + { + var result = new CompositionResult(new CompositionError[0]); + + Assert.Empty(result.Errors); + } + + [Fact] + public void Constructor4_EmptyAsErrorsArgument_ShouldSetErrorsPropertyToEmptyEnumerable() + { + var result = new CompositionResult(Enumerable.Empty()); + + Assert.Empty(result.Errors); + } + + [Fact] + public void Constructor5_EmptyAsErrorsArgument_ShouldSetErrorsPropertyToEmptyEnumerable() + { + var result = new CompositionResult("Value", Enumerable.Empty()); + + Assert.Empty(result.Errors); + } + + [Fact] + public void Constructor3_ValuesAsErrorsArgument_ShouldSetErrorsProperty() + { + var errors = Expectations.GetCompositionErrors(); + + foreach (var e in errors) + { + var result = new CompositionResult(e.ToArray()); + + Assert.Equal(e, result.Errors); + } + } + + [Fact] + public void Constructor4_ValuesAsErrorsArgument_ShouldSetErrorsProperty() + { + var errors = Expectations.GetCompositionErrors(); + + foreach (var e in errors) + { + var result = new CompositionResult(e); + + Assert.Equal(e, result.Errors); + } + } + + [Fact] + public void Constructor5_ValuesAsErrorsArgument_ShouldSetErrorsProperty() + { + var errors = Expectations.GetCompositionErrors(); + + foreach (var e in errors) + { + var result = new CompositionResult("Value", e); + + Assert.Equal(e, result.Errors); + } + } + + [Fact] + public void Constructor1_ShouldSetSucceededPropertyToTrue() + { + var result = new CompositionResult(); + + Assert.True(result.Succeeded); + } + +[Fact] + public void Constructor2_ShouldSetSucceededPropertyToTrue() + { + var result = new CompositionResult("Value"); + + Assert.True(result.Succeeded); + } + + [Fact] + public void Constructor3_NullAsErrorsArgument_ShouldSetSucceededPropertyToTrue() + { + var result = new CompositionResult((CompositionError[])null); + + Assert.True(result.Succeeded); + } + + [Fact] + public void Constructor4_NullAsErrorsArgument_ShouldSetSucceededPropertyToTrue() + { + var result = new CompositionResult((IEnumerable)null); + + Assert.True(result.Succeeded); + } + + [Fact] + public void Constructor5_NullAsErrorsArgument_ShouldSetSucceededPropertyToTrue() + { + var result = new CompositionResult("Value", (IEnumerable)null); + + Assert.True(result.Succeeded); + } + + [Fact] + public void Constructor3_EmptyAsErrorsArgument_ShouldSetSucceededPropertyToTrue() + { + var result = new CompositionResult(new CompositionError[0]); + + Assert.True(result.Succeeded); + } + + [Fact] + public void Constructor4_EmptyAsErrorsArgument_ShouldSetSucceededPropertyToTrue() + { + var result = new CompositionResult(Enumerable.Empty()); + + Assert.True(result.Succeeded); + } + + [Fact] + public void Constructor5_EmptyAsErrorsArgument_ShouldSetSucceededPropertyToTrue() + { + var result = new CompositionResult("Value", Enumerable.Empty()); + + Assert.True(result.Succeeded); + } + + [Fact] + public void Constructor3_ValuesAsErrorsArgument_ShouldSetSucceededPropertyToTrueIfThereAreErrors() + { + var errors = Expectations.GetCompositionErrors(); + + foreach (var e in errors) + { + var result = new CompositionResult(e.ToArray()); + + if (e.Count() > 0) + { + Assert.False(result.Succeeded); + } + else + { + Assert.True(result.Succeeded); + } + } + } + + [Fact] + public void Constructor4_ValuesAsErrorsArgument_ShouldSetSucceededPropertyToTrueIfThereAreErrors() + { + var errors = Expectations.GetCompositionErrors(); + + foreach (var e in errors) + { + var result = new CompositionResult(e); + + if (e.Count() > 0) + { + Assert.False(result.Succeeded); + } + else + { + Assert.True(result.Succeeded); + } + } + } + + [Fact] + public void Constructor5_ValuesAsErrorsArgument_ShouldSetSucceededPropertyToTrueIfThereAreErrors() + { + var errors = Expectations.GetCompositionErrors(); + + foreach (var e in errors) + { + var result = new CompositionResult("Value", e); + + if (e.Count() > 0) + { + Assert.False(result.Succeeded); + } + else + { + Assert.True(result.Succeeded); + } + } + } + + [Fact] + public void ToResult_NullAsErrorsArgument_ShouldReturnResultWithEmptyErrorsProperty() + { + var result = CreateCompositionResult((IEnumerable)null); + + var copy = result.ToResult(); + + Assert.Empty(copy.Errors); + } + + [Fact] + public void ToResult_EmptyEnumerableAsErrorsArgument_ShouldReturnResultWithEmptyErrorsProperty() + { + var result = CreateCompositionResult(Enumerable.Empty()); + + var copy = result.ToResult(); + + Assert.Empty(copy.Errors); + } + + [Fact] + public void ToResult_ValueAsErrorsArgument_ShouldReturnResultWithErrorsPropertySet() + { + var expectations = Expectations.GetCompositionErrors(); + + foreach (var e in expectations) + { + var result = CreateCompositionResult(e); + + var copy = result.ToResult(); + + EqualityExtensions.CheckEquals(result.Errors, copy.Errors); + } + } + + [Fact] + public void ToResultOfT_NullAsErrorsArgument_ShouldReturnResultWithEmptyErrorsProperty() + { + var result = CreateCompositionResult((IEnumerable)null); + + var copy = result.ToResult(); + + Assert.Empty(copy.Errors); + } + + [Fact] + public void ToResultOfT_EmptyEnumerableAsErrorsArgument_ShouldReturnResultWithEmptyErrorsProperty() + { + var result = CreateCompositionResult(Enumerable.Empty()); + + var copy = result.ToResult(); + + Assert.Empty(copy.Errors); + } + + [Fact] + public void ToResultOfT_ValueAsErrorsArgument_ShouldReturnResultWithErrorsPropertySet() + { + var expectations = Expectations.GetCompositionErrors(); + + foreach (var e in expectations) + { + var result = CreateCompositionResult(e); + + var copy = result.ToResult(); + + EqualityExtensions.CheckEquals(result.Errors, copy.Errors); + } + } + + [Fact] + public void ToResultOfT_ReferenceValueAsValueArgument_ShouldReturnResultWithNullValueProperty() + { + var expectations = Expectations.GetObjectsReferenceTypes(); + + foreach (var e in expectations) + { + var result = CreateCompositionResult(e); + + var copy = result.ToResult(); + + Assert.Null(copy.Value); + } + } + + [Fact] + public void ToResultOfT_ValueTypeValueAsValueArgument_ShouldReturnResultWithNullValueProperty() + { + var expectations = Expectations.GetObjectsValueTypes(); + + foreach (var e in expectations) + { + var result = CreateCompositionResult(e); + + var copy = result.ToResult(); + + Assert.Null(copy.Value); + } + } + + [Fact] + public void Value_NullAsErrorsArgumentAndValueAsValueArgument_ShouldReturnValue() + { + var expectations = Expectations.GetObjectsReferenceTypes(); + + foreach (var e in expectations) + { + var result = CreateCompositionResult(e, (IEnumerable)null); + + Assert.Equal(e, result.Value); + } + } + + [Fact] + public void Value_EmptyEnumerableAsErrorsArgumentAndValueAsValueArgument_ShouldReturnValue() + { + var expectations = Expectations.GetObjectsReferenceTypes(); + + foreach (var e in expectations) + { + var result = CreateCompositionResult(e, Enumerable.Empty()); + + Assert.Equal(e, result.Value); + } + } + + [Fact] + public void Value_SingleValueAsErrorsArgument_ShouldThrowComposition() + { + var errorIds = Expectations.GetEnumValues(); + + foreach (var errorId in errorIds) + { + var result = CreateCompositionResult(errorId); + + CompositionAssert.ThrowsError((ErrorId)errorId, () => + { + var value = result.Value; + }); + } + } + + [Fact] + public void Value_TwoSameValuesAsErrorsArgument_ShouldThrowComposition() + { + var errorIds = Expectations.GetEnumValues(); + + foreach (var errorId in errorIds) + { + var result = CreateCompositionResult(errorId, errorId); + + CompositionAssert.ThrowsErrors((ErrorId)errorId, (ErrorId)errorId, () => + { + var value = result.Value; + }); + } + } + + [Fact] + public void Value_TwoDifferentValuesAsErrorsArgument_ShouldThrowComposition() + { + var errorIds1 = Expectations.GetEnumValues(); + var errorIds2 = Expectations.GetEnumValues(); + + for (int i = 0; i < errorIds1.Count(); i++) + { + var errorId1 = errorIds1.ElementAt(i); + var errorId2 = errorIds1.ElementAt(errorIds2.Count() - 1 - i); + + var result = CreateCompositionResult(errorId1, errorId2); + + CompositionAssert.ThrowsErrors((ErrorId)errorId1, (ErrorId)errorId2, () => + { + var value = result.Value; + }); + } + } + + private CompositionResult CreateCompositionResult(params CompositionErrorId[] errorIds) + { + return new CompositionResult(errorIds.Select(id => + { + return ErrorFactory.Create(id); + })); + } + + private CompositionResult CreateCompositionResult(int count) + { + var expectations = Expectations.GetEnumValues(); + + List errors = new List(); + + foreach (var e in expectations.Take(count)) + { + errors.Add(ErrorFactory.Create(e)); + } + + return CreateCompositionResult(errors); + } + + private CompositionResult CreateCompositionResult(IEnumerable errors) + { + return new CompositionResult(errors); + } + + private CompositionResult CreateCompositionResult(T value) + { + return new CompositionResult(value); + } + + private CompositionResult CreateCompositionResult(T value, IEnumerable errors) + { + return new CompositionResult(value, errors); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionResultTest.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionResultTest.cs new file mode 100644 index 0000000000..d604b93d45 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionResultTest.cs @@ -0,0 +1,493 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using System.Linq; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class CompositionResultTest + { + [Fact] + public void Constructor1_ShouldSetErrorsPropertyToEmptyEnumerable() + { + var result = new CompositionResult(); + + Assert.Empty(result.Errors); + } + + [Fact] + public void Constructor2_NullAsErrorsArgument_ShouldSetErrorsPropertyToEmptyEnumerable() + { + var result = new CompositionResult((CompositionError[])null); + + Assert.Empty(result.Errors); + } + + [Fact] + public void Constructor3_NullAsErrorsArgument_ShouldSetErrorsPropertyToEmptyEnumerable() + { + var result = new CompositionResult((IEnumerable)null); + + Assert.Empty(result.Errors); + } + + [Fact] + public void Constructor2_EmptyAsErrorsArgument_ShouldSetErrorsPropertyToEmptyEnumerable() + { + var result = new CompositionResult(new CompositionError[0]); + + Assert.Empty(result.Errors); + } + + [Fact] + public void Constructor3_EmptyAsErrorsArgument_ShouldSetErrorsPropertyToEmptyEnumerable() + { + var result = new CompositionResult(Enumerable.Empty()); + + Assert.Empty(result.Errors); + } + + [Fact] + public void Constructor2_ValuesAsErrorsArgument_ShouldSetErrorsProperty() + { + var errors = Expectations.GetCompositionErrors(); + + foreach (var e in errors) + { + var result = new CompositionResult(e.ToArray()); + + Assert.Equal(e, result.Errors); + } + } + + [Fact] + public void Constructor3_ValuesAsErrorsArgument_ShouldSetErrorsProperty() + { + var errors = Expectations.GetCompositionErrors(); + + foreach (var e in errors) + { + var result = new CompositionResult(e); + + Assert.Equal(e, result.Errors); + } + } + + [Fact] + public void Constructor1_ShouldSetSucceededPropertyToTrue() + { + var result = new CompositionResult(); + + Assert.True(result.Succeeded); + } + + [Fact] + public void Constructor2_NullAsErrorsArgument_ShouldSetSucceededPropertyToTrue() + { + var result = new CompositionResult((CompositionError[])null); + + Assert.True(result.Succeeded); + } + + [Fact] + public void Constructor3_NullAsErrorsArgument_ShouldSetSucceededPropertyToTrue() + { + var result = new CompositionResult((IEnumerable)null); + + Assert.True(result.Succeeded); + } + + [Fact] + public void Constructor2_EmptyAsErrorsArgument_ShouldSetSucceededPropertyToTrue() + { + var result = new CompositionResult(new CompositionError[0]); + + Assert.True(result.Succeeded); + } + + [Fact] + public void Constructor3_EmptyAsErrorsArgument_ShouldSetSucceededPropertyToTrue() + { + var result = new CompositionResult(Enumerable.Empty()); + + Assert.True(result.Succeeded); + } + + [Fact] + public void Constructor2_ValuesAsErrorsArgument_ShouldSetSucceededPropertyToTrueIfThereAreErrors() + { + var errors = Expectations.GetCompositionErrors(); + + foreach (var e in errors) + { + var result = new CompositionResult(e.ToArray()); + + if (e.Count() > 0) + { + Assert.False(result.Succeeded); + } + else + { + Assert.True(result.Succeeded); + } + } + } + + [Fact] + public void Constructor3_ValuesAsErrorsArgument_ShouldSetSucceededPropertyToTrueIfThereAreErrors() + { + var errors = Expectations.GetCompositionErrors(); + + foreach (var e in errors) + { + var result = new CompositionResult(e); + + if (e.Count() > 0) + { + Assert.False(result.Succeeded); + } + else + { + Assert.True(result.Succeeded); + } + } + } + + [Fact] + [ActiveIssue(25498)] + public void MergeResult_ResultWithNullErrorsAsResultArgument_ShouldReturnResultWithSameErrors() + { + var emptyResult = CreateCompositionResult((IEnumerable)null); + + var expectations = Expectations.GetCompositionErrors(); + + foreach (var e in expectations) + { + var result = CreateCompositionResult(e); + + var mergedResult = result.MergeResult(emptyResult); + + Assert.Equal(result, mergedResult); + } + } + + [Fact] + [ActiveIssue(25498)] + public void MergeResult_ResultWithEmptyErrorsAsResultArgument_ShouldReturnResultWithSameErrors() + { + var emptyResult = CreateCompositionResult(Enumerable.Empty()); + + var expectations = Expectations.GetCompositionErrors(); + + foreach (var e in expectations) + { + var result = CreateCompositionResult(e); + + var mergedResult = result.MergeResult(emptyResult); + + Assert.Equal(result, mergedResult); + } + } + + [Fact] + public void MergeResult_ResultWithErrorsAsResultArgument_ShouldReturnResultWithCombinedErrors() + { + var expectations = Expectations.GetCompositionErrors(); + + foreach (var e in expectations) + { + var result1 = CreateCompositionResult(e); + var result2 = CreateCompositionResult(e); + + var mergedResult = result1.MergeResult(result2); + var mergedErrors = result1.Errors.Concat(result2.Errors); + + EqualityExtensions.CheckEquals(mergedErrors, mergedResult.Errors); + Assert.Equal(mergedResult.Succeeded, result1.Succeeded | result2.Succeeded); + } + } + + [Fact] + public void MergeError_ValueAsErrorArgumentWhenMergedWithResultWithNullErrors_ShouldReturnResultWithCombinedErrors() + { + var result = CreateCompositionResult((IEnumerable)null); + var expectations = Expectations.GetEnumValues(); + + foreach (var e in expectations) + { + var error = ErrorFactory.Create(e); + + var mergedResult = result.MergeError(error); + var mergedErrors = result.Errors.Concat(new CompositionError[] { error }); + + EqualityExtensions.CheckEquals(mergedErrors, mergedResult.Errors); + Assert.False(mergedResult.Succeeded); + } + } + + [Fact] + public void MergeError_ValueAsErrorArgumentWhenMergedWithResultWithEmptyErrors_ShouldReturnResultWithCombinedErrors() + { + var result = CreateCompositionResult(Enumerable.Empty()); + var expectations = Expectations.GetEnumValues(); + + foreach (var e in expectations) + { + var error = ErrorFactory.Create(e); + + var mergedResult = result.MergeError(error); + var mergedErrors = result.Errors.Concat(new CompositionError[] { error }); + + EqualityExtensions.CheckEquals(mergedErrors, mergedResult.Errors); + Assert.False(mergedResult.Succeeded); + } + } + + [Fact] + public void MergeError_ValueAsErrorArgumentWhenMergedWithResultWithErrors_ShouldReturnResultWithCombinedErrors() + { + var result = CreateCompositionResult(2); + var expectations = Expectations.GetEnumValues(); + + foreach (var e in expectations) + { + var error = ErrorFactory.Create(e); + + var mergedResult = result.MergeError(error); + var mergedErrors = result.Errors.Concat(new CompositionError[] { error }); + + EqualityExtensions.CheckEquals(mergedErrors, mergedResult.Errors); + Assert.False(mergedResult.Succeeded); + } + } + + [Fact] + public void MergeErrors_ValueAsErrorArgumentWhenMergedWithResultWithNullErrors_ShouldReturnResultWithCombinedErrors() + { + var result = CreateCompositionResult((IEnumerable)null); + var expectations = Expectations.GetCompositionErrors(); + + foreach (var e in expectations) + { + var mergedResult = result.MergeErrors(e); + var mergedErrors = result.Errors.Concat(e); + + EqualityExtensions.CheckEquals(mergedErrors, mergedResult.Errors); + Assert.Equal(!e.Any(), mergedResult.Succeeded); + } + } + + [Fact] + public void MergeErrors_ValueAsErrorArgumentWhenMergedWithResultWithEmptyErrors_ShouldReturnResultWithCombinedErrors() + { + var result = CreateCompositionResult(Enumerable.Empty()); + var expectations = Expectations.GetCompositionErrors(); + + foreach (var e in expectations) + { + var mergedResult = result.MergeErrors(e); + var mergedErrors = result.Errors.Concat(e); + + EqualityExtensions.CheckEquals(mergedErrors, mergedResult.Errors); + Assert.Equal(!e.Any(), mergedResult.Succeeded); + } + } + + [Fact] + public void MergeErrors_ValueAsErrorArgumentWhenMergedWithResultWithErrors_ShouldReturnResultWithCombinedErrors() + { + var result = CreateCompositionResult(2); + var expectations = Expectations.GetCompositionErrors(); + + foreach (var e in expectations) + { + var mergedResult = result.MergeErrors(e); + var mergedErrors = result.Errors.Concat(e); + + EqualityExtensions.CheckEquals(mergedErrors, mergedResult.Errors); + Assert.False(mergedResult.Succeeded); + } + } + + [Fact] + public void ThrowOnErrors_NullAsErrorsArgument_ShouldNotThrow() + { + var result = CreateCompositionResult((IEnumerable)null); + + result.ThrowOnErrors(); + } + + [Fact] + public void ThrowOnErrors_EmptyEnumerableAsErrorsArgument_ShouldNotThrow() + { + var result = CreateCompositionResult(Enumerable.Empty()); + + result.ThrowOnErrors(); + } + + [Fact] + public void ThrowOnErrors_SingleValueAsErrorsArgument_ShouldThrowComposition() + { + var errorIds = Expectations.GetEnumValues(); + + foreach (var errorId in errorIds) + { + var result = CreateCompositionResult(errorId); + + CompositionAssert.ThrowsError((ErrorId)errorId, () => + { + result.ThrowOnErrors(); + }); + } + } + + [Fact] + public void ThrowOnErrors_TwoSameValuesAsErrorsArgument_ShouldThrowComposition() + { + var errorIds = Expectations.GetEnumValues(); + + foreach (var errorId in errorIds) + { + var result = CreateCompositionResult(errorId, errorId); + + CompositionAssert.ThrowsErrors((ErrorId)errorId, (ErrorId)errorId, () => + { + result.ThrowOnErrors(); + }); + } + } + + [Fact] + public void ThrowOnErrors_TwoDifferentValuesAsErrorsArgument_ShouldThrowComposition() + { + var errorIds1 = Expectations.GetEnumValues(); + var errorIds2 = Expectations.GetEnumValues(); + + for (int i = 0; i < errorIds1.Count(); i++) + { + var errorId1 = errorIds1.ElementAt(i); + var errorId2 = errorIds1.ElementAt(errorIds2.Count() - 1 - i); + + var result = CreateCompositionResult(errorId1, errorId2); + + CompositionAssert.ThrowsErrors((ErrorId)errorId1, (ErrorId)errorId2, () => + { + result.ThrowOnErrors(); + }); + } + } + + [Fact] + public void ToResultOfT_NullAsErrorsArgument_ShouldReturnResultWithEmptyErrorsProperty() + { + var result = CreateCompositionResult((IEnumerable)null); + + var copy = result.ToResult("Value"); + + Assert.Empty(copy.Errors); + } + + [Fact] + public void ToResultOfT_EmptyEnumerableAsErrorsArgument_ShouldReturnResultWithEmptyErrorsProperty() + { + var result = CreateCompositionResult(Enumerable.Empty()); + + var copy = result.ToResult("Value"); + + Assert.Empty(copy.Errors); + } + + [Fact] + public void ToResultOfT_ValueAsErrorsArgument_ShouldReturnResultWithErrorsPropertySet() + { + var expectations = Expectations.GetCompositionErrors(); + + foreach (var e in expectations) + { + var result = CreateCompositionResult(e); + + var copy = result.ToResult("Value"); + + EqualityExtensions.CheckEquals(result.Errors, copy.Errors); + } + } + + [Fact] + public void ToResultOfT_ReferenceValueAsValueArgument_ShouldReturnResultWithValuePropertySet() + { + var expectations = Expectations.GetObjectsReferenceTypes(); + + foreach (var e in expectations) + { + var result = CreateCompositionResult(); + + var copy = result.ToResult(e); + + Assert.Same(e, copy.Value); + } + } + + [Fact] + public void ToResultOfT_ValueTypeValueAsValueArgument_ShouldReturnResultWithValuePropertySet() + { + var expectations = Expectations.GetObjectsValueTypes(); + + foreach (var e in expectations) + { + var result = CreateCompositionResult(); + + var copy = result.ToResult(e); + + Assert.Equal(e, copy.Value); + } + } + + [Fact] + public void SucceededResult_ShouldSetSuccessPropertyToTrue() + { + var succeeded = CompositionResult.SucceededResult.Succeeded; + + Assert.True(succeeded); + } + + [Fact] + public void SucceededResult_ShouldSetErrorsPropertyToEmptyEnumerable() + { + var errors = CompositionResult.SucceededResult.Errors; + + Assert.Empty(errors); + } + + private CompositionResult CreateCompositionResult(params CompositionErrorId[] errorIds) + { + return new CompositionResult(errorIds.Select(id => + { + return ErrorFactory.Create(id); + })); + } + + private CompositionResult CreateCompositionResult(int count) + { + var expectations = Expectations.GetEnumValues(); + + List errors = new List(); + + foreach (var e in expectations.Take(count)) + { + errors.Add(ErrorFactory.Create(e)); + } + + return CreateCompositionResult(errors); + } + + private CompositionResult CreateCompositionResult(IEnumerable errors) + { + return new CompositionResult(errors); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionServiceExportFactoryTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionServiceExportFactoryTests.cs new file mode 100644 index 0000000000..1d17a31c36 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionServiceExportFactoryTests.cs @@ -0,0 +1,139 @@ +// 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.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class CompositionServiceExportFactoryTests + { + public interface IFoo + { + void DoWork(); + Child FooChild { get; set; } + } + + [Export(typeof(IFoo))] + class Foo1 : IFoo + { + public void DoWork() + { + Console.WriteLine("HelloWorld : {0}", FooChild.FooValue); + } + + [Import("FooChild")] + public Child FooChild { get; set; } + } + + [Export("FooChild")] + public class Child + { + public int FooValue { get; set; } + } + + [Export] + public class App + { + [Import] + public ExportFactory FooFactory { get; set; } + } + + [Fact] + [Description("Verifies CompositionServices.SatisfyImportsOne with Scoped ExportFactories")] + public void ComposeAppInNewScopeChildrenInRoot_ShouldSucceed() + { + var childCatalog = new CompositionScopeDefinition(new TypeCatalog(typeof(Foo1)), new CompositionScopeDefinition[] { }); + var rootCatalog = new CompositionScopeDefinition(new TypeCatalog(typeof(Child)), new CompositionScopeDefinition[] { childCatalog }); + + var cs = rootCatalog.CreateCompositionService(); + var app = new App(); + + cs.SatisfyImportsOnce(app); + + var e1 = app.FooFactory.CreateExport(); + var e2 = app.FooFactory.CreateExport(); + var e3 = app.FooFactory.CreateExport(); + e1.Value.FooChild.FooValue = 10; + e2.Value.FooChild.FooValue = 20; + e3.Value.FooChild.FooValue = 30; + + Assert.Equal(e1.Value.FooChild.FooValue, 30); + Assert.Equal(e2.Value.FooChild.FooValue, 30); + Assert.Equal(e3.Value.FooChild.FooValue, 30); + } + + [Fact] + [Description("Verifies CompositionServices.SatisfyImportsOne with Scoped ExportFactories")] + public void ComposeAppInNewScopeChildrenInScope_ShouldSucceed() + { + var childCatalog = new CompositionScopeDefinition(new TypeCatalog(typeof(Foo1), typeof(Child)), new CompositionScopeDefinition[] { }); + var rootCatalog = new CompositionScopeDefinition(new TypeCatalog(), new CompositionScopeDefinition[] { childCatalog }); + + var cs = rootCatalog.CreateCompositionService(); + var app = new App(); + + cs.SatisfyImportsOnce(app); + + var e1 = app.FooFactory.CreateExport(); + var e2 = app.FooFactory.CreateExport(); + var e3 = app.FooFactory.CreateExport(); + e1.Value.FooChild.FooValue = 10; + e2.Value.FooChild.FooValue = 20; + e3.Value.FooChild.FooValue = 30; + + Assert.Equal(e1.Value.FooChild.FooValue, 10); + Assert.Equal(e2.Value.FooChild.FooValue, 20); + Assert.Equal(e3.Value.FooChild.FooValue, 30); + } + + [Fact] + [Description("Verifies CompositionServices.SatisfyImportsOne with Scoped ExportFactories")] + public void ComposeAppInNewScopeChildrenInBoth_ShouldSucceed() + { + var childCatalog = new CompositionScopeDefinition(new TypeCatalog(typeof(Foo1), typeof(Child)), new CompositionScopeDefinition[] { }); + var rootCatalog = new CompositionScopeDefinition(new TypeCatalog(typeof(Child)), new CompositionScopeDefinition[] { childCatalog }); + + var cs = rootCatalog.CreateCompositionService(); + var app = new App(); + + cs.SatisfyImportsOnce(app); + + var e1 = app.FooFactory.CreateExport(); + var e2 = app.FooFactory.CreateExport(); + var e3 = app.FooFactory.CreateExport(); + e1.Value.FooChild.FooValue = 10; + e2.Value.FooChild.FooValue = 20; + e3.Value.FooChild.FooValue = 30; + + Assert.Equal(e1.Value.FooChild.FooValue, 10); + Assert.Equal(e2.Value.FooChild.FooValue, 20); + Assert.Equal(e3.Value.FooChild.FooValue, 30); + } + + [Fact] + [Description("Verifies CompositionServices.SatisfyImportsOne with NonScoped ExportFactories")] + public void ComposeAppInRootScope_ShouldSucceed() + { + var catalog = new TypeCatalog(typeof(Foo1), typeof(Child)); + + var cs = catalog.CreateCompositionService(); + var app = new App(); + + cs.SatisfyImportsOnce(app); + + var e1 = app.FooFactory.CreateExport(); + var e2 = app.FooFactory.CreateExport(); + var e3 = app.FooFactory.CreateExport(); + e1.Value.FooChild.FooValue = 10; + e2.Value.FooChild.FooValue = 20; + e3.Value.FooChild.FooValue = 30; + + Assert.Equal(e1.Value.FooChild.FooValue, 30); + Assert.Equal(e2.Value.FooChild.FooValue, 30); + Assert.Equal(e3.Value.FooChild.FooValue, 30); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionServiceExtensionsTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionServiceExtensionsTests.cs new file mode 100644 index 0000000000..46023b7522 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionServiceExtensionsTests.cs @@ -0,0 +1,144 @@ +// 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.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Primitives; +using System.ComponentModel.Composition.ReflectionModel; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class CompositionServiceExtensionsTests + { + [Fact] + public void SatisfyImports_BooleanOverride_PartAndFalseHaveBeenPassed() + { + MockCompositionService compositionService = new MockCompositionService(); + ComposablePart part = PartFactory.Create(); + + bool importsSatisfiedCalled = false; + compositionService.ImportsSatisfied += delegate (object sender, SatisfyImportsEventArgs e) + { + Assert.False(importsSatisfiedCalled); + Assert.Equal(part, e.Part); + importsSatisfiedCalled = true; + }; + + compositionService.SatisfyImportsOnce(part); + Assert.True(importsSatisfiedCalled); + } + + [Fact] + public void SatisfyImports_AttributedOverride_NullAsCompositionService() + { + ICompositionService compositionService = null; + Assert.Throws("compositionService", () => + { + compositionService.SatisfyImportsOnce(new MockAttributedPart()); + }); + } + + [Fact] + public void SatisfyImports_AttributedOverride_NullAsAttributedPart() + { + MockCompositionService compositionService = new MockCompositionService(); + Assert.Throws("attributedPart", () => + { + compositionService.SatisfyImportsOnce((object)null); + }); + } + + [Fact] + public void SatisfyImports_AttributedOverride_PartAndFalseHaveBeenPassed() + { + MockCompositionService compositionService = new MockCompositionService(); + object attributedPart = new MockAttributedPart(); + + bool importsSatisfiedCalled = false; + compositionService.ImportsSatisfied += delegate (object sender, SatisfyImportsEventArgs e) + { + Assert.False(importsSatisfiedCalled); + Assert.True(e.Part is ReflectionComposablePart); + Assert.True(((ReflectionComposablePart)e.Part).Definition.GetPartType() == typeof(MockAttributedPart)); + importsSatisfiedCalled = true; + }; + + compositionService.SatisfyImportsOnce(attributedPart); + Assert.True(importsSatisfiedCalled); + } + + [Fact] + public void SatisfyImports_AttributedAndBooleanOverride_NullAsCompositionService() + { + ICompositionService compositionService = null; + Assert.Throws("compositionService", () => + { + compositionService.SatisfyImportsOnce(new MockAttributedPart()); + }); + } + + [Fact] + public void SatisfyImports_AttributedAndBooleanOverride_NullAsAttributedPart() + { + MockCompositionService compositionService = new MockCompositionService(); + Assert.Throws("attributedPart", () => + { + compositionService.SatisfyImportsOnce((object)null); + }); + } + + [Fact] + public void SatisfyImports_AttributedAndBooleanOverride_PartAndFalseHaveBeenPassed() + { + MockCompositionService compositionService = new MockCompositionService(); + object attributedPart = new MockAttributedPart(); + + bool importsSatisfiedCalled = false; + compositionService.ImportsSatisfied += delegate (object sender, SatisfyImportsEventArgs e) + { + Assert.False(importsSatisfiedCalled); + Assert.True(e.Part is ReflectionComposablePart); + Assert.True(((ReflectionComposablePart)e.Part).Definition.GetPartType() == typeof(MockAttributedPart)); + importsSatisfiedCalled = true; + }; + + compositionService.SatisfyImportsOnce(attributedPart); + Assert.True(importsSatisfiedCalled); + } + + internal class SatisfyImportsEventArgs : EventArgs + { + public SatisfyImportsEventArgs(ComposablePart part) + { + this.Part = part; + } + + public ComposablePart Part { get; private set; } + } + + internal class MockCompositionService : ICompositionService + { + public MockCompositionService() + { + } + + public void SatisfyImportsOnce(ComposablePart part) + { + if (this.ImportsSatisfied != null) + { + this.ImportsSatisfied.Invoke(this, new SatisfyImportsEventArgs(part)); + } + } + + public event EventHandler ImportsSatisfied; + } + + public class MockAttributedPart + { + public MockAttributedPart() + { + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionServiceProxy.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionServiceProxy.cs new file mode 100644 index 0000000000..9d540a08d5 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionServiceProxy.cs @@ -0,0 +1,24 @@ +// 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.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; + +namespace System.ComponentModel.Composition +{ + internal class CompositionServiceProxy : ICompositionService + { + private readonly CompositionContainer _container; + + public CompositionServiceProxy(CompositionContainer container) + { + this._container = container; + } + + public void SatisfyImportsOnce(ComposablePart part) + { + this._container.SatisfyImportsOnce(part); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionServiceTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionServiceTests.cs new file mode 100644 index 0000000000..c3584b685e --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionServiceTests.cs @@ -0,0 +1,36 @@ +// 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.ComponentModel.Composition.Hosting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class CompositionServiceTests + { + public interface IFoo { } + + public class CFoo : IFoo { } + + public class FooImporter + { + [Import] + public ICompositionService CompositionService; + + [Import] + public IFoo fooImporter { get; set; } + } + + [Fact] + public void SatisfyParts_NullArgument_ShouldThrowArgumentNullException() + { + Assert.Throws("part", () => + { + var compositionService = new TypeCatalog().CreateCompositionService(); + compositionService.SatisfyImportsOnce(null); + }); + } + } +} + diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionServicesTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionServicesTests.cs new file mode 100644 index 0000000000..1d7f8707a6 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionServicesTests.cs @@ -0,0 +1,111 @@ +// 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.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; +using System.UnitTesting; +using Tests.Integration; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class CompositionServicesTests + { + private static readonly Type NonGenericType = typeof(int); + + [Fact] + public void GetContractName_NullAsTypeArgument_ThrowsArgumentNull() + { + Assert.Throws("type", () => + { + AttributedModelServices.GetContractName((Type)null); + }); + } + + [Fact] + [Description("Verifies adding custom modifiers to contract name.")] + public void ContractNameServicesAddCustomModifiersTest() + { + Type[] modifiers = new Type[] { typeof(int), typeof(List), typeof(double) }; + StringBuilder typeName = new StringBuilder(); + ContractNameServices.WriteCustomModifiers(typeName, "test", modifiers, false); + Assert.Equal( + string.Format(" {0}(System.Int32,System.Collections.Generic.List(System.Int32),System.Double)", "test"), + typeName.ToString()); + } + + [Fact] + [Description("Verifies CompositionServices.GetDefaultContractName method.")] + public void GetDefaultContractNameTest() + { + ExpectationCollection expectations = new ExpectationCollection(); + + expectations.Add(typeof(string), "System.String"); + expectations.Add(typeof(int?), "System.Nullable(System.Int32)"); + expectations.Add(typeof(NestedParent.NestedChild), "System.ComponentModel.Composition.NestedParent+NestedChild"); + expectations.Add(typeof(int[,]), "System.Int32[,]"); + expectations.Add(typeof(int[,][][]), "System.Int32[,][][]"); + expectations.Add(typeof(Array[,][][]).MakePointerType(), "System.Array[,][][]*"); + expectations.Add(typeof(int).MakePointerType(), "System.Int32*"); + expectations.Add(typeof(int).MakePointerType().MakeArrayType(3).MakeArrayType().MakePointerType(), "System.Int32*[][,,]*"); + expectations.Add(typeof(int).MakeByRefType(), "System.Int32&"); + expectations.Add(typeof(int).MakePointerType().MakeArrayType(4).MakeArrayType().MakeByRefType(), "System.Int32*[][,,,]&"); + expectations.Add(typeof(List<>), "System.Collections.Generic.List({0})"); + expectations.Add(typeof(Dictionary), "System.Collections.Generic.Dictionary(System.Int32,System.Double)"); + expectations.Add(typeof(Dictionary,double>), "System.Collections.Generic.Dictionary(System.Collections.Generic.Dictionary(System.Int32,System.Double),System.Double)"); + expectations.Add(typeof(GenericContract1.GenericContract2.GenericContract3), "System.ComponentModel.Composition.GenericContract1(System.Int32)+GenericContract2+GenericContract3(System.Double)"); + expectations.Add(typeof(GenericContract4.GenericContract5.GenericContract6), "System.ComponentModel.Composition.GenericContract4(System.Int32,System.Double)+GenericContract5(System.Double,System.Int32)+GenericContract6(System.Int32,System.Double)"); + expectations.Add(typeof(GenericContract4<,>.GenericContract5<,>.GenericContract6<,>), "System.ComponentModel.Composition.GenericContract4({0},{1})+GenericContract5({2},{3})+GenericContract6({4},{5})"); + expectations.Add(typeof(GenericContract7), "System.ComponentModel.Composition.GenericContract7"); + expectations.Add(typeof(GenericContract8), "System.ComponentModel.Composition.GenericContract8(System.Int32)"); + expectations.Add(typeof(GenericContract4.GenericContract5[][,,,,]), "System.ComponentModel.Composition.GenericContract4(System.Int32,System.Double)+GenericContract5(System.Double,System.Int32)[][,,,,]"); + expectations.Add(typeof(GenericContract4.GenericContract5).MakePointerType(), "System.ComponentModel.Composition.GenericContract4(System.Int32,System.Double)+GenericContract5(System.Double,System.Int32)*"); + expectations.Add(typeof(GenericContract4.GenericContract5[][,]).MakePointerType().MakeArrayType(4), "System.ComponentModel.Composition.GenericContract4(System.Int32,System.Double)+GenericContract5(System.Double,System.Int32)[][,]*[,,,]"); + expectations.Add(typeof(OuterClassWithGenericNested.GenericNested), "System.ComponentModel.Composition.OuterClassWithGenericNested+GenericNested(System.Int32)"); + expectations.Add(typeof(Delegate), "System.Delegate"); + expectations.Add(typeof(MulticastDelegate), "System.MulticastDelegate"); + expectations.Add(typeof(Action), "System.Void()"); + expectations.Add(typeof(Func>), "System.Func(System.String)()"); + expectations.Add(typeof(DelegateCompositionTests.DoWorkDelegate), "System.Object(System.Int32,System.Object&,System.String&)"); + expectations.Add(typeof(DelegateCompositionTests.DelegateOneArg), "System.Int32(System.Int32)"); + expectations.Add(typeof(DelegateCompositionTests.DelegateTwoArgs), "System.Int32(System.Int32,System.Int32)"); + expectations.Add(typeof(DelegateCompositionTests.SimpleDelegate), "System.Int32()"); + expectations.Add(EmitGenericType("MyGeneratedType", typeof(int)), "MyGeneratedType(System.Int32)"); + expectations.Add(EmitGenericType("MyGeneratedType`1", typeof(string)), "MyGeneratedType(System.String)"); + expectations.Add(EmitGenericType("_name_", typeof(List<>)), "_name_(System.Collections.Generic.List({0}))"); + + // This particular case can clash with the name generation for methods because we use () for both generics and methods + // we may want to fix this clash by changing the generic markers but it is likely to be very rare. + expectations.Add(EmitGenericType("System.Void", typeof(int)), "System.Void(System.Int32)"); + + foreach (var e in expectations) + { + string typeIdentity = AttributedModelServices.GetTypeIdentity(e.Input); + Assert.Equal(e.Output, typeIdentity); + } + + // Do it again to excerise the cache. + foreach (var e in expectations) + { + string typeIdentity = AttributedModelServices.GetTypeIdentity(e.Input); + Assert.Equal(e.Output, typeIdentity); + } + } + + public Type EmitGenericType(string typeName, Type genericArgumentType) + { + var assemblyName = new AssemblyName("EmittedTestAssembly"); + var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); + var moduleBuilder = assemblyBuilder.DefineDynamicModule("EmittedTestModule"); + var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public); + + GenericTypeParameterBuilder T = typeBuilder.DefineGenericParameters(new string[] { "T" })[0]; + typeBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(ExportAttribute).GetConstructor(Type.EmptyTypes), new object[0])); + + return typeBuilder.CreateType().MakeGenericType(genericArgumentType); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ConcreteComposablePart.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ConcreteComposablePart.cs new file mode 100644 index 0000000000..008175d35d --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ConcreteComposablePart.cs @@ -0,0 +1,93 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Primitives; +using System.Linq; + +namespace System.ComponentModel.Composition +{ + internal class ConcreteComposablePart : ComposablePart, IDisposable + { + private readonly List _exports = new List(); + private readonly List _imports = new List(); + private readonly IDictionary> _setImports = new Dictionary>(); + private static IDictionary EmptyMetadata = new Dictionary(); + + public ConcreteComposablePart() + { + } + + public override IDictionary Metadata + { + get { return EmptyMetadata; } + } + + public IDictionary> SetImports + { + get { return this._setImports; } + } + + public override IEnumerable ExportDefinitions + { + get { return this._exports.Select(export => export.Definition); } + } + + public override IEnumerable ImportDefinitions + { + get { return this._imports; } + } + + public ImportDefinition AddImport(string contractName, ImportCardinality cardinality, bool isRecomposable, bool isPrerequisite) + { + var import = ImportDefinitionFactory.CreateDefault(contractName, cardinality, isRecomposable, isPrerequisite); + + this.AddImport(import); + return import; + } + + public void AddImport(ImportDefinition import) + { + this._imports.Add(import); + } + + public void AddExport(string contractName, object value) + { + this.AddExport(ExportFactory.Create(contractName, () => value)); + } + + public void AddExport(Export export) + { + this._exports.Add(export); + } + + public override object GetExportedValue(ExportDefinition definition) + { + Export export = _exports.First(e => e.Definition == definition); + + return export.Value; + } + + public override void SetImport(ImportDefinition definition, IEnumerable exports) + { + ContractBasedImportDefinition contractBasedDefinition = (ContractBasedImportDefinition)definition; + this._setImports[contractBasedDefinition.ContractName] = exports; + + foreach (Export export in exports) + { + var value = export.Value; + } + } + + void IDisposable.Dispose() + { + foreach (var disposable in _exports.Select(export => export.Value).OfType()) + { + disposable.Dispose(); + } + } + } +} + diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ConstraintParser.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ConstraintParser.cs new file mode 100644 index 0000000000..de6f5c587a --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ConstraintParser.cs @@ -0,0 +1,223 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.Internal; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class ContraintParser + { + private static readonly PropertyInfo _exportDefinitionContractNameProperty = typeof(ExportDefinition).GetProperty("ContractName"); + private static readonly PropertyInfo _exportDefinitionMetadataProperty = typeof(ExportDefinition).GetProperty("Metadata"); + private static readonly MethodInfo _metadataContainsKeyMethod = typeof(IDictionary).GetMethod("ContainsKey"); + private static readonly MethodInfo _metadataItemMethod = typeof(IDictionary).GetMethod("get_Item"); + private static readonly MethodInfo _typeIsInstanceOfTypeMethod = typeof(Type).GetMethod("IsInstanceOfType"); + + public static bool TryParseConstraint(Expression> constraint, out string contractName, out IEnumerable> requiredMetadata) + { + contractName = null; + requiredMetadata = null; + + List> requiredMetadataList = new List>(); + foreach (Expression expression in SplitConstraintBody(constraint.Body)) + { + // First try to parse as a contract, if we don't have one already + if (contractName == null && TryParseExpressionAsContractConstraintBody(expression, constraint.Parameters[0], out contractName)) + { + continue; + } + + // Then try to parse as a required metadata item name + string requiredMetadataItemName = null; + Type requiredMetadataItemType = null; + if (TryParseExpressionAsMetadataConstraintBody(expression, constraint.Parameters[0], out requiredMetadataItemName, out requiredMetadataItemType)) + { + requiredMetadataList.Add(new KeyValuePair(requiredMetadataItemName, requiredMetadataItemType)); + } + + // Just skip the expressions we don't understand + } + + // ContractName should have been set already, just need to set metadata + requiredMetadata = requiredMetadataList; + return true; + } + + private static IEnumerable SplitConstraintBody(Expression expression) + { + Assert.NotNull(expression); + + // The expression we know about should be a set of nested AndAlso's, we + // need to flatten them into one list. we do this iteratively, as + // recursion will create too much of a memory churn. + Stack expressions = new Stack(); + expressions.Push(expression); + + while (expressions.Count > 0) + { + Expression current = expressions.Pop(); + if (current.NodeType == ExpressionType.AndAlso) + { + BinaryExpression andAlso = (BinaryExpression)current; + // Push right first - this preserves the ordering of the expression, which will force + // the contract constraint to come up first as the callers are optimized for this form + expressions.Push(andAlso.Right); + expressions.Push(andAlso.Left); + continue; + } + + yield return current; + } + } + + private static bool TryParseExpressionAsContractConstraintBody(Expression expression, Expression parameter, out string contractName) + { + contractName = null; + + // The expression should be an '==' expression + if (expression.NodeType != ExpressionType.Equal) + { + return false; + } + + BinaryExpression contractConstraintExpression = (BinaryExpression)expression; + + // First try item.ContractName == "Value" + if (TryParseContractNameFromEqualsExpression(contractConstraintExpression.Left, contractConstraintExpression.Right, parameter, out contractName)) + { + return true; + } + + // Then try "Value == item.ContractName + if (TryParseContractNameFromEqualsExpression(contractConstraintExpression.Right, contractConstraintExpression.Left, parameter, out contractName)) + { + return true; + } + + return false; + } + + private static bool TryParseContractNameFromEqualsExpression(Expression left, Expression right, Expression parameter, out string contractName) + { + contractName = null; + + // The left should be access to property "Contract" applied to the parameter + MemberExpression targetMember = left as MemberExpression; + if (targetMember == null) + { + return false; + } + + if ((targetMember.Member != _exportDefinitionContractNameProperty) || (targetMember.Expression != parameter)) + { + return false; + } + + // Right should a constant expression containing the contract name + ConstantExpression contractNameConstant = right as ConstantExpression; + if (contractNameConstant == null) + { + return false; + } + + if (!TryParseConstant(contractNameConstant, out contractName)) + { + return false; + } + + return true; + } + + private static bool TryParseExpressionAsMetadataConstraintBody(Expression expression, Expression parameter, out string requiredMetadataKey, out Type requiredMetadataType) + { + Assumes.NotNull(expression, parameter); + + requiredMetadataKey = null; + requiredMetadataType = null; + + // Should be a call to Type.IsInstanceofType on definition.Metadata[key] + MethodCallExpression outerMethodCall = expression as MethodCallExpression; + if (outerMethodCall == null) + { + return false; + } + + // Make sure that the right method ie being called + if (outerMethodCall.Method != _typeIsInstanceOfTypeMethod) + { + return false; + } + Assumes.IsTrue(outerMethodCall.Arguments.Count == 1); + + // 'this' should be a constant expression pointing at a Type object + ConstantExpression targetType = outerMethodCall.Object as ConstantExpression; + if (!TryParseConstant(targetType, out requiredMetadataType)) + { + return false; + } + + // SHould be a call to get_Item + MethodCallExpression methodCall = outerMethodCall.Arguments[0] as MethodCallExpression; + if (methodCall == null) + { + return false; + } + + if (methodCall.Method != _metadataItemMethod) + { + return false; + } + + // Make sure the method is being called on the right object + MemberExpression targetMember = methodCall.Object as MemberExpression; + if (targetMember == null) + { + return false; + } + + if ((targetMember.Expression != parameter) || (targetMember.Member != _exportDefinitionMetadataProperty)) + { + return false; + } + + // There should only ever be one argument; otherwise, + // we've got the wrong IDictionary.get_Item method. + Assumes.IsTrue(methodCall.Arguments.Count == 1); + + // Argument should a constant expression containing the metadata key + ConstantExpression requiredMetadataKeyConstant = methodCall.Arguments[0] as ConstantExpression; + if (requiredMetadataKeyConstant == null) + { + return false; + } + + if (!TryParseConstant(requiredMetadataKeyConstant, out requiredMetadataKey)) + { + return false; + } + + return true; + } + + private static bool TryParseConstant(ConstantExpression constant, out T result) + where T : class + { + Assumes.NotNull(constant); + + if (constant.Type == typeof(T) && constant.Value != null) + { + result = (T)constant.Value; + return true; + } + + result = default(T); + return false; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ConstraintServicesTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ConstraintServicesTests.cs new file mode 100644 index 0000000000..366bcfe75d --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ConstraintServicesTests.cs @@ -0,0 +1,311 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.ComponentModel.Composition.ReflectionModel; +using System.Linq.Expressions; +using System.Reflection; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class ConstraintServicesTests + { + [Fact] + public void TypeIdentityConstraint_ValidMatchingExportDef_ShouldMatch() + { + var contractName = "MyContract"; + var typeIdentity = AttributedModelServices.GetTypeIdentity(typeof(ConstraintServicesTests)); + var metadata = new Dictionary(); + metadata.Add(CompositionConstants.ExportTypeIdentityMetadataName, typeIdentity); + + var exportDefinition = new ExportDefinition(contractName, metadata); + + var constraint = ConstraintServices.CreateConstraint(contractName, typeIdentity, null, CreationPolicy.Any); + + var predicate = constraint.Compile(); + + Assert.True(predicate(exportDefinition)); + } + + [Fact] + public void TypeIdentityConstraint_ValidNonMatchingExportDef_ShouldNotMatch() + { + var contractName = "MyContract"; + var typeIdentity = AttributedModelServices.GetTypeIdentity(typeof(ConstraintServicesTests)); + var metadata = new Dictionary(); + metadata.Add(CompositionConstants.ExportTypeIdentityMetadataName, typeIdentity + "Another Identity"); + + var exportDefinition = new ExportDefinition(contractName, metadata); + + var constraint = ConstraintServices.CreateConstraint(contractName, typeIdentity, null, CreationPolicy.Any); + + var predicate = constraint.Compile(); + + Assert.False(predicate(exportDefinition)); + } + + [Fact] + public void TypeIdentityConstraint_InvalidExportDef_ShouldNotMatch() + { + var contractName = "MyContract"; + var typeIdentity = AttributedModelServices.GetTypeIdentity(typeof(ConstraintServicesTests)); + var metadata = new Dictionary(); + + var exportDefinition = new ExportDefinition(contractName, metadata); + + var constraint = ConstraintServices.CreateConstraint(contractName, typeIdentity, null, CreationPolicy.Any); + + var predicate = constraint.Compile(); + + Assert.False(predicate(exportDefinition)); + } + + [Fact] + public void CreationPolicyConstraint_ValidMatchingCreationPolicy_ShouldMatch() + { + var contractName = "MyContract"; + var metadata = new Dictionary(); + metadata.Add(CompositionConstants.PartCreationPolicyMetadataName, CreationPolicy.Shared); + + var exportDefinition = new ExportDefinition(contractName, metadata); + + var constraint = ConstraintServices.CreateConstraint(contractName, null, null, CreationPolicy.Shared); + + var predicate = constraint.Compile(); + + Assert.True(predicate(exportDefinition)); + } + + [Fact] + public void CreationPolicyConstraint_ValidNonMatchingCreationPolicy_ShouldNotMatch() + { + var contractName = "MyContract"; + var metadata = new Dictionary(); + metadata.Add(CompositionConstants.PartCreationPolicyMetadataName, CreationPolicy.NonShared); + + var exportDefinition = new ExportDefinition(contractName, metadata); + + var constraint = ConstraintServices.CreateConstraint(contractName, null, null, CreationPolicy.Shared); + + var predicate = constraint.Compile(); + + Assert.False(predicate(exportDefinition)); + } + + [Fact] + public void CreationPolicyConstraint_InvalidCreationPolicy_ShouldNotMatch() + { + var contractName = "MyContract"; + var metadata = new Dictionary(); + metadata.Add(CompositionConstants.PartCreationPolicyMetadataName, "Shared"); + + var exportDefinition = new ExportDefinition(contractName, metadata); + + var constraint = ConstraintServices.CreateConstraint(contractName, null, null, CreationPolicy.Shared); + + var predicate = constraint.Compile(); + + Assert.False(predicate(exportDefinition)); + } + + [Fact] + public void CreationPolicyConstraint_NoCreationPolicy_ShouldNotMatch() + { + var contractName = "MyContract"; + var metadata = new Dictionary(); + + var exportDefinition = new ExportDefinition(contractName, metadata); + + var constraint = ConstraintServices.CreateConstraint(contractName, null, null, CreationPolicy.Shared); + + var predicate = constraint.Compile(); + + Assert.True(predicate(exportDefinition)); + } + + [Fact] + public void PartCreatorConstraint_ShouldMatchPartCreatorExportDefinition() + { + var partCreatorImportDef = ReflectionModelServices.CreateImportDefinition( + new LazyMemberInfo(MemberTypes.Field, () => new MemberInfo[] { typeof(ConstraintServicesTests) }), + "Foo", + "Foo", + new KeyValuePair[] { new KeyValuePair("MDKey", typeof(string)) }, + ImportCardinality.ZeroOrMore, + false, + CreationPolicy.Any, + MetadataServices.EmptyMetadata, + true, // IsPartCreator + null); + + var metadata = new Dictionary(); + metadata["MDKey"] = "MDValue"; + metadata[CompositionConstants.ExportTypeIdentityMetadataName] = "Foo"; + + var productExportDefinition = new ExportDefinition("Foo", metadata); + + metadata = new Dictionary(metadata); + metadata[CompositionConstants.ExportTypeIdentityMetadataName] = CompositionConstants.PartCreatorTypeIdentity; + metadata[CompositionConstants.ProductDefinitionMetadataName] = productExportDefinition; + + var exportDefinition = new ExportDefinition(CompositionConstants.PartCreatorContractName, metadata); + + var predicate = partCreatorImportDef.Constraint.Compile(); + Assert.True(partCreatorImportDef.IsConstraintSatisfiedBy(exportDefinition)); + Assert.True(predicate(exportDefinition)); + } + + [Fact] + public void TryParseConstraint_ConstraintFromCreateConstraintAsConstraintArgument1_CanParse() + { + var expectations = Expectations.GetContractNamesWithEmpty(); + + foreach (var e in expectations) + { + var constraint = ConstraintServices.CreateConstraint((string)e, null, null, CreationPolicy.Any); + + AssertCanParse(constraint, e, new Dictionary()); + } + } + + [Fact] + public void TryParseConstraint_ConstraintFromCreateConstraintAsConstraintArgument3_CanParse() + { + var contractNames = Expectations.GetContractNames(); + var metadataValues = Expectations.GetRequiredMetadata(); + + foreach (var contractName in contractNames) + { + foreach (var metadataValue in metadataValues) + { + var constraint = ConstraintServices.CreateConstraint(contractName, null, metadataValue, CreationPolicy.Any); + + AssertCanParse(constraint, contractName, metadataValue); + } + } + } + + [Fact] + public void TryParseConstraint_ContractNameOperatorEqualsAsConstraintArgument_CanParse() + { + var expectations = new ExpectationCollection>, string>(); + expectations.Add(item => item.ContractName == "", ""); + expectations.Add(item => item.ContractName == " ", " "); + expectations.Add(item => item.ContractName == " ", " "); + expectations.Add(item => item.ContractName == "ContractName", "ContractName"); + expectations.Add(item => item.ContractName == "contractName", "contractName"); + expectations.Add(item => item.ContractName == "{ContractName}", "{ContractName}"); + expectations.Add(item => item.ContractName == "{ContractName}Name", "{ContractName}Name"); + expectations.Add(item => item.ContractName == "System.Windows.Forms.Control", "System.Windows.Forms.Control"); + expectations.Add(item => item.ContractName == "{System.Windows.Forms}Control", "{System.Windows.Forms}Control"); + + foreach (var e in expectations) + { + AssertCanParse(e.Input, e.Output, new Dictionary()); + } + } + + [Fact] + public void TryParseConstraint_MetadataContainsKeyAsConstraintArgument_CanParse() + { + var expectations = new ExpectationCollection>, Dictionary>(); + expectations.Add( + item => typeof(string).IsInstanceOfType(item.Metadata[""]), + new Dictionary { { "", typeof(string) } }); + expectations.Add( + item => typeof(string).IsInstanceOfType(item.Metadata["value"]), + new Dictionary { { "value", typeof(string) } }); + expectations.Add( + item => typeof(string).IsInstanceOfType(item.Metadata["Value"]), + new Dictionary { { "Value", typeof(string) } }); + expectations.Add( + item => typeof(string).IsInstanceOfType(item.Metadata["Value"]) && typeof(int).IsInstanceOfType(item.Metadata["value"]), + new Dictionary { { "Value", typeof(string) }, { "value", typeof(int) } }); + expectations.Add( + item => typeof(string).IsInstanceOfType(item.Metadata["Value"]) && typeof(int).IsInstanceOfType(item.Metadata["value"]) && typeof(object).IsInstanceOfType(item.Metadata["Metadata"]), + new Dictionary { { "Value", typeof(string) }, { "value", typeof(int) }, { "Metadata", typeof(object) } }); + + foreach (var e in expectations) + { + AssertCanParse(e.Input, (string)null, e.Output); + } + } + + [Fact] + public void TryParseConstraint_ContractNameOperatorEqualsAndMetadataContainsKeyAsConstraintArgumen_CanParse() + { + var expectations = new ExpectationCollection>, KeyValuePair[]>(); + expectations.Add( + item => item.ContractName == "ContractName" && typeof(string).IsInstanceOfType(item.Metadata[""]), + new KeyValuePair[] { new KeyValuePair("", typeof(string)) }); + expectations.Add( + item => item.ContractName == "ContractName" && typeof(string).IsInstanceOfType(item.Metadata["value"]), + new KeyValuePair[] { new KeyValuePair("value", typeof(string)) }); + expectations.Add( + item => item.ContractName == "ContractName" && typeof(string).IsInstanceOfType(item.Metadata["Value"]), + new KeyValuePair[] { new KeyValuePair("Value", typeof(string)) }); + expectations.Add( + item => item.ContractName == "ContractName" && typeof(string).IsInstanceOfType(item.Metadata["Value"]) && typeof(int).IsInstanceOfType(item.Metadata["value"]), + new KeyValuePair[] { new KeyValuePair("Value", typeof(string)), new KeyValuePair("value", typeof(int)) }); + expectations.Add( + item => item.ContractName == "ContractName" && typeof(string).IsInstanceOfType(item.Metadata["Value"]) && typeof(int).IsInstanceOfType(item.Metadata["value"]) && typeof(object).IsInstanceOfType(item.Metadata["Metadata"]), + new KeyValuePair[] { new KeyValuePair("Value", typeof(string)), new KeyValuePair("value", typeof(int)), new KeyValuePair("Metadata", typeof(object)) }); + + foreach (var e in expectations) + { + AssertCanParse(e.Input, "ContractName", e.Output); + } + } + + [Fact] + public void TryParseConstraint_ContractNameReverseOperatorEqualsAsConstraintArgument_CanParse() + { + var expectations = new ExpectationCollection>, string>(); + expectations.Add(item => "" == item.ContractName, ""); + expectations.Add(item => " " == item.ContractName, " "); + expectations.Add(item => " " == item.ContractName, " "); + expectations.Add(item => "ContractName" == item.ContractName, "ContractName"); + expectations.Add(item => "contractName" == item.ContractName, "contractName"); + expectations.Add(item => "{ContractName}" == item.ContractName, "{ContractName}"); + expectations.Add(item => "{ContractName}Name" == item.ContractName, "{ContractName}Name"); + expectations.Add(item => "System.Windows.Forms.Control" == item.ContractName, "System.Windows.Forms.Control"); + expectations.Add(item => "{System.Windows.Forms}Control" == item.ContractName, "{System.Windows.Forms}Control"); + + foreach (var e in expectations) + { + AssertCanParse(e.Input, e.Output, new Dictionary()); + } + } + + private static void AssertCanParse(Expression> constraint, string contractName, IEnumerable> requiredMetadata) + { + Assert.NotNull(constraint); + + string contractNameResult = null; + IEnumerable> requiredMetadataResult = null; + bool success = ContraintParser.TryParseConstraint(constraint, out contractNameResult, out requiredMetadataResult); + + Assert.True(success); + Assert.Equal(contractName, contractNameResult); + EnumerableAssert.AreEqual(requiredMetadata, requiredMetadataResult); + } + + private static void AssertCanNotParse(Expression> constraint) + { + Assert.NotNull(constraint); + + string contractNameResult; + IEnumerable> requiredMetadataResult; + + var success = ContraintParser.TryParseConstraint(constraint, out contractNameResult, out requiredMetadataResult); + Assert.False(success); + Assert.Null(contractNameResult); + Assert.Null(requiredMetadataResult); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ContractBasedImportDefinitionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ContractBasedImportDefinitionTests.cs new file mode 100644 index 0000000000..ff72d852ab --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ContractBasedImportDefinitionTests.cs @@ -0,0 +1,394 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class ContractBasedImportDefinitionTests + { + [Fact] + public void Constructor1_ShouldSetRequiredMetadataPropertyToEmptyEnumerable() + { + var definition = new NoOverridesContractBasedImportDefinition(); + + Assert.Empty(definition.RequiredMetadata); + } + + [Fact] + public void Constructor1_ShouldSetMetadataPropertyToEmptyEnumerable() + { + var definition = new NoOverridesContractBasedImportDefinition(); + + Assert.Empty(definition.Metadata); + } + + [Fact] + public void Constructor1_ShouldSetCardinalityPropertyToExactlyOne() + { + var definition = new NoOverridesContractBasedImportDefinition(); + + Assert.Equal(ImportCardinality.ExactlyOne, definition.Cardinality); + } + + [Fact] + public void Constructor1_ShouldSetIsPrerequisitePropertyToTrue() + { + var definition = new NoOverridesContractBasedImportDefinition(); + + Assert.True(definition.IsPrerequisite); + } + + [Fact] + public void Constructor1_ShouldSetIsRecomposablePropertyToFalse() + { + var definition = new NoOverridesContractBasedImportDefinition(); + + Assert.False(definition.IsRecomposable); + } + + [Fact] + public void Constructor1_ShouldSetRequiredCreationPolicyToAny() + { + var definition = new NoOverridesContractBasedImportDefinition(); + + Assert.Equal(CreationPolicy.Any, definition.RequiredCreationPolicy); + } + + [Fact] + public void Constructor1_ShouldSetRequiredTypeIdentityToNull() + { + var definition = new NoOverridesContractBasedImportDefinition(); + + Assert.Null(definition.RequiredTypeIdentity); + } + + [Fact] + public void Constructor2_NullAsContractNameArgument_ShouldThrowArgumentNull() + { + Assert.Throws("contractName", () => + { + new ContractBasedImportDefinition((string)null, (string)null, Enumerable.Empty>(), ImportCardinality.ExactlyOne, false, false, CreationPolicy.Any); + }); + } + + [Fact] + public void Constructor2_EmptyStringAsContractNameArgument_ShouldThrowArgument() + { + Assert.Throws("contractName", () => + { + new ContractBasedImportDefinition("", (string)null, Enumerable.Empty>(), ImportCardinality.ExactlyOne, false, false, CreationPolicy.Any); + }); + } + + [Fact] + public void RequiredMetadata_ArrayWithNullKeyAsRequiredMetadataArgument_ShouldThrowInvalidOperation() + { + var requiredMetadata = new KeyValuePair[] { new KeyValuePair(null, typeof(object)) }; + + var import = new ContractBasedImportDefinition("requiredMetadata", (string)null, requiredMetadata, ImportCardinality.ExactlyOne, false, false, CreationPolicy.Any); + + ExceptionAssert.Throws(() => + { + var m = import.RequiredMetadata; + }); + } + + [Fact] + public void RequiredMetadata_ArrayWithNullValueAsRequiredMetadataArgument_ShouldThrowInvalidOperation() + { + var requiredMetadata = new KeyValuePair[] { new KeyValuePair("key", null) }; + var import = new ContractBasedImportDefinition("requiredMetadata", (string)null, requiredMetadata, ImportCardinality.ExactlyOne, false, false, CreationPolicy.Any); + + ExceptionAssert.Throws(() => + { + var m = import.RequiredMetadata; + }); + } + + [Fact] + public void Constructor2_NullAsMetadataArgument_ShouldSetRequiredMetadataToEmptyEnumerable() + { + var requiredMetadata = new KeyValuePair[] { new KeyValuePair("key", null) }; + var definition = new ContractBasedImportDefinition("metadata", (string)null, requiredMetadata, ImportCardinality.ExactlyOne, false, false, CreationPolicy.Any, null); + + Assert.Empty(definition.Metadata); + } + + [Fact] + public void Constructor2_NullAsRequiredMetadataArgument_ShouldSetRequiredMetadataToEmptyEnumerable() + { + var definition = new ContractBasedImportDefinition("requiredMetadata", (string)null, (IEnumerable>)null, ImportCardinality.ExactlyOne, false, false, CreationPolicy.Any); + + Assert.Empty(definition.RequiredMetadata); + } + + [Fact] + public void Constructor2_OutOfRangeValueAsCardinalityArgument_ShouldThrowArgument() + { + var expectations = Expectations.GetInvalidEnumValues(); + + foreach (var e in expectations) + { + Assert.Throws("cardinality", () => + { + new ContractBasedImportDefinition("ContractName", (string)null, Enumerable.Empty>(), e, false, false, CreationPolicy.Any); + }); + } + } + + [Fact] + public void Constructor2_ValueAsCardinalityArgument_ShouldSetCardinalityProperty() + { + var expectations = Expectations.GetEnumValues(); + + foreach (var e in expectations) + { + var definition = new ContractBasedImportDefinition("ContractName", (string)null, Enumerable.Empty>(), e, false, false, CreationPolicy.Any); + + Assert.Equal(e, definition.Cardinality); + } + } + + [Fact] + public void Constructor2_ValueAsContractNameArgument_ShouldSetContractNameProperty() + { + var expectations = Expectations.GetContractNames(); + + foreach (var e in expectations) + { + var definition = new ContractBasedImportDefinition(e, (string)null, Enumerable.Empty>(), ImportCardinality.ExactlyOne, false, false, CreationPolicy.Any); + + Assert.Equal(e, definition.ContractName); + } + } + + [Fact] + public void Constructor2_ValueAsRequiredMetadataArgument_ShouldSetRequiredMetadataProperty() + { + var expectations = Expectations.GetRequiredMetadataWithEmpty(); + + foreach (var e in expectations) + { + var definition = new ContractBasedImportDefinition("ContractName", (string)null, e, ImportCardinality.ExactlyOne, false, false, CreationPolicy.Any); + + Assert.Equal(e, definition.RequiredMetadata); + } + } + + [Fact] + public void Constructor2_ValueAsMetadataArgument_ShouldSetMetadataProperty() + { + var expectations = Expectations.GetMetadata(); + + foreach (var e in expectations) + { + var definition = new ContractBasedImportDefinition("ContractName", (string)null, null, ImportCardinality.ExactlyOne, false, false, CreationPolicy.Any, e); + + Assert.Equal(e, definition.Metadata); + } + } + + [Fact] + public void Constructor2_ValueAsIsRecomposableArgument_ShouldSetIsRecomposableProperty() + { + var expectations = Expectations.GetBooleans(); + + foreach (var e in expectations) + { + var definition = new ContractBasedImportDefinition("ContractName", (string)null, Enumerable.Empty>(), ImportCardinality.ExactlyOne, e, false, CreationPolicy.Any); + + Assert.Equal(e, definition.IsRecomposable); + } + } + + [Fact] + public void Constructor2_ValueAsIsPrerequisiteArgument_ShouldSetIsPrerequisiteProperty() + { + var expectations = Expectations.GetBooleans(); + + foreach (var e in expectations) + { + var definition = new ContractBasedImportDefinition("ContractName", (string)null, Enumerable.Empty>(), ImportCardinality.ExactlyOne, false, e, CreationPolicy.Any); + + Assert.Equal(e, definition.IsPrerequisite); + } + } + + [Fact] + public void Constructor2_ShouldSetRequiredCreationPolicyToAny() + { + var expectations = Expectations.GetEnumValues(); + + foreach (var e in expectations) + { + var definition = new ContractBasedImportDefinition("ContractName", (string)null, Enumerable.Empty>(), ImportCardinality.ExactlyOne, false, false, e); + + Assert.Equal(e, definition.RequiredCreationPolicy); + } + } + + [Fact] + public void Constraint_ShouldIncludeContractNameProperty() + { + var expectations = Expectations.GetContractNames(); + + foreach (var e in expectations) + { + var definition = new ContractBasedImportDefinition(e, (string)null, (IEnumerable>)null, ImportCardinality.ExactlyOne, true, false, CreationPolicy.Any); + + ConstraintAssert.Contains(definition.Constraint, e); + } + } + + [Fact] + public void Constraint_ShouldIncludeRequiredMetadataProperty() + { + var expectations = Expectations.GetRequiredMetadataWithEmpty(); + + foreach (var e in expectations) + { + var definition = new ContractBasedImportDefinition("ContractName", (string)null, e, ImportCardinality.ExactlyOne, true, false, CreationPolicy.Any); + + ConstraintAssert.Contains(definition.Constraint, "ContractName", e); + } + } + + [Fact] + public void Constraint_ShouldIncludeOverriddenContractNameProperty() + { + var expectations = Expectations.GetContractNames(); + + foreach (var e in expectations) + { + var definition = new DerivedContractBasedImportDefinition(e); + + ConstraintAssert.Contains(definition.Constraint, e); + } + } + + [Fact] + public void Constraint_ShouldIncludeOverriddenRequiredMetadata() + { + var expectations = Expectations.GetRequiredMetadataWithEmpty(); + + foreach (var e in expectations) + { + var definition = new DerivedContractBasedImportDefinition("ContractName", e); + + ConstraintAssert.Contains(definition.Constraint, "ContractName", e); + } + } + + [Fact] + public void IsConstraintSatisfiedBy_ContractNameMatch() + { + var export = CreateSimpleExport(); + var import = CreateSimpleImport("ContractName", "ContractName", new string[0], new Type[0]); + Assert.True(import.IsConstraintSatisfiedBy(export)); + } + + [Fact] + public void IsConstraintSatisfiedBy_ContractNameMismatch() + { + var export = CreateSimpleExport(); + var import = CreateSimpleImport("NonContractName", "ContractName", new string[0], new Type[0]); + Assert.False(import.IsConstraintSatisfiedBy(export)); + } + + [Fact] + public void IsConstraintSatisfiedBy_TypeIdentityMismatch() + { + var export = CreateSimpleExport(); + var import = CreateSimpleImport("ContractName", "NonContractName", new string[0], new Type[0]); + Assert.False(import.IsConstraintSatisfiedBy(export)); + } + + [Fact] + public void IsConstraintSatisfiedBy_MetadataMatch() + { + var export = CreateSimpleExport(); + var import = CreateSimpleImport("ContractName", "ContractName", new string[]{"Int", "String", "Type"}, new Type[]{typeof(int), typeof(string), typeof(Type)}); + Assert.True(import.IsConstraintSatisfiedBy(export)); + } + + [Fact] + public void IsConstraintSatisfiedBy_MetadataKeyMismatch() + { + var export = CreateSimpleExport(); + var import = CreateSimpleImport("ContractName", "ContractName", new string[] { "Int", "String1", "Type" }, new Type[] { typeof(int), typeof(string), typeof(Type) }); + Assert.False(import.IsConstraintSatisfiedBy(export)); + } + + [Fact] + public void IsConstraintSatisfiedBy_MetadataTypeMatch() + { + var export = CreateSimpleExport(); + var import = CreateSimpleImport("ContractName", "ContractName", new string[] { "Int", "String", "Type" }, new Type[] { typeof(int), typeof(string), typeof(int) }); + Assert.False(import.IsConstraintSatisfiedBy(export)); + } + + private static ExportDefinition CreateSimpleExport() + { + var metadata = new Dictionary(); + metadata.Add("Int", 42); + metadata.Add("String", "42"); + metadata.Add("Type", typeof(string)); + metadata.Add(CompositionConstants.ExportTypeIdentityMetadataName, "ContractName"); + return new ExportDefinition("ContractName", metadata); + } + + private static ContractBasedImportDefinition CreateSimpleImport(string contractName, string typeIdentity, string[] metadataKeys, Type[] metadataTypes) + { + Dictionary requiredMetadata = new Dictionary(); + Assert.Equal(metadataKeys.Length, metadataTypes.Length); + for(int i=0; i< metadataKeys.Length; i++) + { + requiredMetadata[metadataKeys[i]] = metadataTypes[i]; + } + return new ContractBasedImportDefinition(contractName, typeIdentity, requiredMetadata, ImportCardinality.ZeroOrMore, false, false, CreationPolicy.Any); + + } + + private class NoOverridesContractBasedImportDefinition : ContractBasedImportDefinition + { + public NoOverridesContractBasedImportDefinition() + { + } + } + + private class DerivedContractBasedImportDefinition : ContractBasedImportDefinition + { + private readonly string _contractName; + private readonly IEnumerable> _requiredMetadata; + + public DerivedContractBasedImportDefinition(string contractName) + { + _contractName = contractName; + } + + public DerivedContractBasedImportDefinition(string contractName, IEnumerable> requiredMetadata) + { + _contractName = contractName; + _requiredMetadata = requiredMetadata; + } + + public override string ContractName + { + get { return _contractName; } + } + + public override IEnumerable> RequiredMetadata + { + get { return _requiredMetadata; } + } + } + } +} + diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ContractMismatchExceptionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ContractMismatchExceptionTests.cs new file mode 100644 index 0000000000..37ecb3f7c9 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ContractMismatchExceptionTests.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. +// See the LICENSE file in the project root for more information. + +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class ContractMismatchExceptionTests + { + [Fact] + public void Constructor1_ShouldSetMessagePropertyToDefault() + { + var exception = new CompositionContractMismatchException(); + + ExceptionAssert.HasDefaultMessage(exception); + } + + [Fact] + public void Constructor2_NullAsMessageArgument_ShouldSetMessagePropertyToDefault() + { + var exception = new CompositionContractMismatchException((string)null); + + ExceptionAssert.HasDefaultMessage(exception); + } + + [Fact] + public void Constructor3_NullAsMessageArgument_ShouldSetMessagePropertyToDefault() + { + var exception = new CompositionContractMismatchException((string)null, new Exception()); + + ExceptionAssert.HasDefaultMessage(exception); + } + + [Fact] + public void Constructor2_ValueAsMessageArgument_ShouldSetMessageProperty() + { + var expectations = Expectations.GetExceptionMessages(); + + foreach (var e in expectations) + { + var exception = new CompositionContractMismatchException(e); + + Assert.Equal(e, exception.Message); + } + } + + [Fact] + public void Constructor3_ValueAsMessageArgument_ShouldSetMessageProperty() + { + var expectations = Expectations.GetExceptionMessages(); + + foreach (var e in expectations) + { + var exception = new CompositionContractMismatchException(e, new Exception()); + + Assert.Equal(e, exception.Message); + } + } + + [Fact] + public void Constructor1_ShouldSetInnerExceptionPropertyToNull() + { + var exception = new CompositionContractMismatchException(); + + Assert.Null(exception.InnerException); + } + + [Fact] + public void Constructor2_ShouldSetInnerExceptionPropertyToNull() + { + var exception = new CompositionContractMismatchException("Message"); + + Assert.Null(exception.InnerException); + } + + [Fact] + public void Constructor3_NullAsInnerExceptionArgument_ShouldSetInnerExceptionPropertyToNull() + { + var exception = new CompositionContractMismatchException("Message", (Exception)null); + + Assert.Null(exception.InnerException); + } + + [Fact] + public void Constructor3_ValueAsInnerExceptionArgument_ShouldSetInnerExceptionProperty() + { + var expectations = Expectations.GetInnerExceptions(); + + foreach (var e in expectations) + { + var exception = new CompositionContractMismatchException("Message", e); + + Assert.Same(e, exception.InnerException); + } + } + + private static CompositionContractMismatchException CreateContractMismatchException() + { + return CreateContractMismatchException((string)null, (Exception)null); + } + + private static CompositionContractMismatchException CreateContractMismatchException(string message) + { + return CreateContractMismatchException(message, (Exception)null); + } + + private static CompositionContractMismatchException CreateContractMismatchExceptionFromId(string id) + { + return CreateContractMismatchException((string)null, (Exception)null); + } + + private static CompositionContractMismatchException CreateContractMismatchException(Exception innerException) + { + return CreateContractMismatchException((string)null, innerException); + } + + private static CompositionContractMismatchException CreateContractMismatchException(string message, Exception innerException) + { + return new CompositionContractMismatchException(message, innerException); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Diagnostics/CompositionTraceIdTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Diagnostics/CompositionTraceIdTests.cs new file mode 100644 index 0000000000..6921c4f16a --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Diagnostics/CompositionTraceIdTests.cs @@ -0,0 +1,19 @@ +// 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.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition.Diagnostics +{ + public class CompositionTraceIdTests + { + [Fact] + [ActiveIssue(25498, TargetFrameworkMonikers.UapAot)] + public void CompositionTraceIdsAreInSyncWithTraceIds() + { + ExtendedAssert.EnumsContainSameValues(); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Diagnostics/TraceEventDetails.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Diagnostics/TraceEventDetails.cs new file mode 100644 index 0000000000..10626679fc --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Diagnostics/TraceEventDetails.cs @@ -0,0 +1,57 @@ +// 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.Diagnostics; + +namespace System.ComponentModel.Composition.Diagnostics +{ + public class TraceEventDetails + { + public TraceEventDetails(TraceEventCache eventCache, string source, TraceEventType eventType, TraceId id, string format, params object[] args) + { + EventCache = eventCache; + Source = source; + EventType = eventType; + Id = id; + Format = format; + Args = args; + } + + public TraceEventCache EventCache + { + get; + private set; + } + + public string Source + { + get; + private set; + } + + public TraceEventType EventType + { + get; + private set; + } + + public TraceId Id + { + get; + private set; + } + + public string Format + { + get; + private set; + } + + public object[] Args + { + get; + private set; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Diagnostics/TraceId.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Diagnostics/TraceId.cs new file mode 100644 index 0000000000..317a15aeaa --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Diagnostics/TraceId.cs @@ -0,0 +1,19 @@ +// 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. + +namespace System.ComponentModel.Composition.Diagnostics +{ + // We need a public version of CompositionTraceId, so that the QA tests can access and verify the trace. + public enum TraceId : ushort + { + Rejection_DefinitionRejected = CompositionTraceId.Rejection_DefinitionRejected, + Rejection_DefinitionResurrected = CompositionTraceId.Rejection_DefinitionResurrected, + + Discovery_AssemblyLoadFailed = CompositionTraceId.Discovery_AssemblyLoadFailed, + Discovery_DefinitionMarkedWithPartNotDiscoverableAttribute = CompositionTraceId.Discovery_DefinitionMarkedWithPartNotDiscoverableAttribute, + Discovery_DefinitionMismatchedExportArity = CompositionTraceId.Discovery_DefinitionMismatchedExportArity, + Discovery_DefinitionContainsNoExports = CompositionTraceId.Discovery_DefinitionContainsNoExports, + Discovery_MemberMarkedWithMultipleImportAndImportMany = CompositionTraceId.Discovery_MemberMarkedWithMultipleImportAndImportMany, + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/DynamicMetadata.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/DynamicMetadata.cs new file mode 100644 index 0000000000..31d5d0fb77 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/DynamicMetadata.cs @@ -0,0 +1,329 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Hosting; +using System.Linq; +using System.Reflection; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class DynamicMetadata : IDisposable + { + [Fact] + public void SimpleAttachment() + { + MetadataStore.Container = new CompositionContainer(); + DynamicMetadataTestClass val = DynamicMetadataTestClass.Get("42"); + + var notYetAttached = TypeDescriptor.GetConverter(val); + Assert.False(notYetAttached.CanConvertFrom(typeof(string)), "The default type converter for DynamicMetadataTestClass shouldn't support round tripping"); + + MetadataStore.AddAttribute( + typeof(DynamicMetadataTestClass), + (type, attributes) => + Enumerable.Concat( + attributes, + new Attribute[] { new TypeConverterAttribute(typeof(DynamicMetadataTestClassConverter)) } + ) + ); + var attached = TypeDescriptor.GetConverter(val); + Assert.True(attached.CanConvertFrom(typeof(string)), "The new type converter for DynamicMetadataTestClass should support round tripping"); + } + + [Fact] + public void LocalContainer() + { + var container1 = new CompositionContainer(); + TypeDescriptorServices dat = new TypeDescriptorServices(); + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(dat); + container1.Compose(batch); + MetadataStore.AddAttribute( + typeof(DynamicMetadataTestClass), + (type, attributes) => + Enumerable.Concat( + attributes, + new Attribute[] { new TypeConverterAttribute(typeof(DynamicMetadataTestClassConverter)) } + ), + container1 + ); + DynamicMetadataTestClass val = DynamicMetadataTestClass.Get("42"); + + var notYetAttached = TypeDescriptor.GetConverter(val.GetType()); + Assert.False(notYetAttached.CanConvertFrom(typeof(string)), "The default type converter for DynamicMetadataTestClass shouldn't support round tripping"); + + var attached = dat.GetConverter(val.GetType()); + Assert.True(attached.CanConvertFrom(typeof(string)), "The new type converter for DynamicMetadataTestClass should support round tripping"); + } + + [Fact] + public void DualContainers() + { + var container1 = new CompositionContainer(); + TypeDescriptorServices dat1 = new TypeDescriptorServices(); + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(dat1); + container1.Compose(batch); + MetadataStore.AddAttribute( + typeof(DynamicMetadataTestClass), + (type, attributes) => + Enumerable.Concat( + attributes, + new Attribute[] { new TypeConverterAttribute(typeof(DynamicMetadataTestClassConverter)) } + ), + container1 + ); + + var container2 = new CompositionContainer(); + CompositionBatch batch2 = new CompositionBatch(); + TypeDescriptorServices dat2 = new TypeDescriptorServices(); + batch2.AddPart(dat2); + container2.Compose(batch2); + + DynamicMetadataTestClass val = DynamicMetadataTestClass.Get("42"); + + var attached1 = dat1.GetConverter(val.GetType()); + Assert.True(attached1.CanConvertFrom(typeof(string)), "The new type converter for DynamicMetadataTestClass should support round tripping"); + + var attached2 = dat2.GetConverter(val.GetType()); + Assert.False(attached2.CanConvertFrom(typeof(string)), "The default type converter for DynamicMetadataTestClass shouldn't support round tripping"); + } + + public void Dispose() + { + MetadataStore.Container = null; + } + } + + [Export] + public class TypeDescriptorServices + { + Dictionary providers = new Dictionary(); + + internal Dictionary Providers + { + get { return providers; } + set { providers = value; } + } + + public ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) + { + if (Providers.ContainsKey(objectType)) + { + return Providers[objectType].GetTypeDescriptor(objectType); + } + else + { + return null; + } + } + public void AddProvider(TypeDescriptionProvider provider, Type type) + { + Providers[type] = provider; + } + public TypeConverter GetConverter(Type type) + { + var ictd = GetTypeDescriptor(type, null); + if (ictd != null) + { + return ictd.GetConverter(); + } + else + { + return TypeDescriptor.GetConverter(type); + } + } + } + + public static class MetadataStore + { + public static CompositionContainer Container { get; set; } + static Dictionary registeredRedirect = new Dictionary(); + + public static void AddAttribute(Type target, Func, IEnumerable> provider) + { + AddAttribute(target, provider, MetadataStore.Container); + } + public static void AddAttribute(Type target, Func, IEnumerable> provider, CompositionContainer container) + { + ContainerUnawareProviderRedirect.GetRedirect(container)[target] = new MetadataStoreProvider(target, provider); + RegisterTypeDescriptorInterop(target); + } + private static void RegisterTypeDescriptorInterop(Type target) + { + if (!registeredRedirect.ContainsKey(target)) + { + var r = new ContainerUnawareProviderRedirect(target); + TypeDescriptor.AddProvider(r, target); + registeredRedirect[target] = r; + } + else + { + // force a uncache of the information from TypeDescriptor + // + TypeDescriptor.RemoveProvider(registeredRedirect[target], target); + TypeDescriptor.AddProvider(registeredRedirect[target], target); + } + } + public static TypeDescriptorServices GetTypeDescriptorServicesForContainer(CompositionContainer container) + { + if (container != null) + { + var result = container.GetExportedValueOrDefault(); + if (result == null) + { + var v = new TypeDescriptorServices(); + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(v); + container.Compose(batch); + return v; + } + + return result; + } + return null; + } + + private class ContainerUnawareProviderRedirect : TypeDescriptionProvider + { + public ContainerUnawareProviderRedirect(Type forType) + : base(TypeDescriptor.GetProvider(forType)) + { + } + public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) + { + var datd = GetTypeDescriptorServicesForContainer(MetadataStore.Container); + if (datd == null || !datd.Providers.ContainsKey(objectType)) + { + return base.GetTypeDescriptor(objectType, instance); + } + else + { + return datd.GetTypeDescriptor(objectType, instance); + } + } + + internal static Dictionary GetRedirect(CompositionContainer container) + { + TypeDescriptorServices v = GetTypeDescriptorServicesForContainer(container); + return v != null ? v.Providers : null; + } + } + + private class MetadataStoreProvider : TypeDescriptionProvider + { + Func, IEnumerable> provider; + public MetadataStoreProvider(Type forType, Func, IEnumerable> provider) + : base(TypeDescriptor.GetProvider(forType)) + { + this.provider = provider; + } + public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) + { + ICustomTypeDescriptor descriptor = base.GetTypeDescriptor(objectType, instance); + descriptor = new MetadataStoreTypeDescriptor(objectType, descriptor, provider); + return descriptor; + } + + } + + private class MetadataStoreTypeDescriptor : CustomTypeDescriptor + { + Type targetType; + Func, IEnumerable> provider; + public MetadataStoreTypeDescriptor(Type targetType, ICustomTypeDescriptor parent, Func, IEnumerable> provider) + : base(parent) + { + this.targetType = targetType; + this.provider = provider; + } + public override TypeConverter GetConverter() + { + TypeConverterAttribute attribute = (TypeConverterAttribute)GetAttributes()[typeof(TypeConverterAttribute)]; + if (attribute != null) + { + Type c = this.GetTypeFromName(attribute.ConverterTypeName); + if ((c != null) && typeof(TypeConverter).IsAssignableFrom(c)) + { + return (TypeConverter)Activator.CreateInstance(c); + } + } + return base.GetConverter(); + } + private Type GetTypeFromName(string typeName) + { + if ((typeName == null) || (typeName.Length == 0)) + { + return null; + } + int length = typeName.IndexOf(','); + Type type = null; + if (length == -1) + { + type = targetType.Assembly.GetType(typeName); + } + if (type == null) + { + type = Type.GetType(typeName); + } + if ((type == null) && (length != -1)) + { + type = Type.GetType(typeName.Substring(0, length)); + } + return type; + } + public override AttributeCollection GetAttributes() + { + var n = new List(); + foreach (var attr in provider(targetType, base.GetAttributes().OfType())) + { + n.Add(attr); + } + return new AttributeCollection(n.ToArray()); + } + } + } + + public class DynamicMetadataTestClass + { + int i; + + private DynamicMetadataTestClass(int i) + { + this.i = i; + } + + public override string ToString() + { + return i.ToString(); + } + + public static DynamicMetadataTestClass Get(string s) + { + return new DynamicMetadataTestClass(Int32.Parse(s)); + } + } + + public class DynamicMetadataTestClassConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string); + } + public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) + { + return ((DynamicMetadataTestClass)value).ToString(); + } + public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) + { + return DynamicMetadataTestClass.Get((string)value); + } + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + { + return destinationType == typeof(string); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Expectations.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Expectations.cs new file mode 100644 index 0000000000..c92f3490e4 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Expectations.cs @@ -0,0 +1,326 @@ +// 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.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.Composition.AttributedModel; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.ComponentModel.Composition.ReflectionModel; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.UnitTesting; + +namespace System.ComponentModel.Composition +{ + internal static class Expectations + { + public static IEnumerable GetCulturesForFormatting() + { + yield return new CultureInfo("en-US"); // English (US) + yield return new CultureInfo("en-AU"); // English (Australian) + yield return new CultureInfo("en-CA"); // English (Canada) + yield return new CultureInfo("en-NZ"); // English (Great Britain) + yield return new CultureInfo("en-JM"); // English (Jamaica) + yield return new CultureInfo("pt-BR"); // Portuguese (Brazil) + yield return new CultureInfo("es-AR"); // Spanish (Argentina) + yield return new CultureInfo("ja-JP"); // Japanese (Japan) + yield return new CultureInfo("fr-FR"); // French (France) + yield return new CultureInfo("it-IT"); // Italian (Italy) + yield return new CultureInfo("de-DE"); // German (Germany) + yield return new CultureInfo("es-ES"); // Spanish (Spain) + yield return new CultureInfo("ko-KR"); // Korean (Korea) + yield return new CultureInfo("zh-TW"); // Chinese (Taiwan) + yield return new CultureInfo("zh-CN"); // Chinese (People's Republic of China) + } + + public static IEnumerable GetAssemblies() + { + yield return Assembly.GetExecutingAssembly(); + yield return Assembly.Load("mscorlib"); + } + + public static IEnumerable GetDisplayNames() + { + yield return " "; + yield return " "; + yield return "DisplayName"; + yield return "displayname"; + yield return "This is the display name."; + } + + public static IEnumerable GetCompositionElements() + { + yield return ElementFactory.CreateChain(1); + yield return ElementFactory.CreateChain(2); + yield return ElementFactory.CreateChain(3); + yield return ElementFactory.CreateChain(5); + yield return ElementFactory.CreateChain(10); + } + + public static IEnumerable GetCompositionElementsWithNull() + { + foreach (var element in GetCompositionElements()) + { + yield return element; + } + + yield return null; + } + + public static IEnumerable GetCatalogs() + { + yield return CatalogFactory.Create(); + yield return CatalogFactory.CreateDefaultAttributed(); + } + + public static IEnumerable> GetCompositionErrors() + { + foreach (var value in GetEmptyCollections()) + { + yield return value; + } + + yield return new CompositionError[] { new CompositionError("") }; + yield return new CompositionError[] { new CompositionError(""), new CompositionError("Description") }; + yield return new CompositionError[] { new CompositionError(""), new CompositionError("Description"), ErrorFactory.Create(CompositionErrorId.InvalidExportMetadata, "Description", (Exception)null), ErrorFactory.Create(CompositionErrorId.Unknown, "Description", new Exception()) }; + } + + public static IEnumerable GetContractNames() + { + yield return " "; + yield return " "; + yield return "ContractName"; + yield return "contractName"; + yield return "{ContractName}"; + yield return "{ContractName}Name"; + yield return "System.Windows.Forms.Control"; + yield return "{System.Windows.Forms}Control"; + yield return "{9}Control"; + } + + public static IEnumerable GetContractNamesWithEmpty() + { + foreach (string contractName in GetContractNames()) + { + yield return contractName; + } + + yield return string.Empty; + } + + public static IEnumerable GetObjectsReferenceTypes() + { + yield return "Value"; + yield return new Collection(); + yield return new IEnumerable[0]; + } + + public static IEnumerable GetObjectsValueTypes() + { + yield return 10; + yield return 10.0; + yield return DayOfWeek.Wednesday; + } + + public static IEnumerable GetMetadataNames() + { + return GetContractNamesWithEmpty(); + } + + public static IEnumerable GetMetadataValues() + { + yield return null; + yield return string.Empty; + yield return ""; + yield return " "; + yield return " "; + yield return (Int32)1; + yield return (Byte)1; + yield return (Single)1.1; + yield return (Double)1.1; + yield return DayOfWeek.Wednesday; + } + + public static IEnumerable> GetRequiredMetadata() + { + yield return new Dictionary { { "", typeof(object) } }; + yield return new Dictionary { { " ", typeof(object) } }; + yield return new Dictionary { { " ", typeof(object) } }; + yield return new Dictionary { { " ", typeof(object) } }; + yield return new Dictionary { { "A", typeof(object) } }; + yield return new Dictionary { { "A", typeof(object) }, { "B", typeof(object) } }; + yield return new Dictionary { { "A", typeof(object) }, { "B", typeof(object) }, { "C", typeof(object) } }; + yield return new Dictionary { { "a", typeof(object) } }; + yield return new Dictionary { { "a", typeof(object) }, { "b", typeof(object) } }; + yield return new Dictionary { { "a", typeof(object) }, { "b", typeof(object) }, { "c", typeof(object) } }; + yield return new Dictionary { { "Metadata1", typeof(object) }, { "Metadata2", typeof(object) }, { "Metadata3", typeof(object) } }; + } + + public static IEnumerable> GetRequiredMetadataWithEmpty() + { + foreach (var requiredMetadata in GetRequiredMetadata()) + { + yield return requiredMetadata; + } + + yield return new Dictionary(); + } + + public static IEnumerable> GetMetadata() + { + yield return new ReadOnlyDictionary(new Dictionary(0)); + yield return new Dictionary(); + yield return new SortedDictionary(); + yield return new SortedList(); + var metadata = new Dictionary(); + metadata.Add("One", "Value"); + metadata.Add("Two", true); + metadata.Add("Three", 10); + metadata.Add("Four", 1.0); + metadata.Add("Five", null); + + yield return metadata; + } + + public static IEnumerable GetExceptionMessages() + { + yield return ""; + yield return " "; + yield return " "; + yield return "message"; + yield return "This is an error message."; + yield return "Line One." + Environment.NewLine + "Line Two."; + } + + public static IEnumerable GetInnerExceptions() + { + yield return new Exception(); + yield return new ArgumentException(); + yield return new SystemException(); + yield return new CompositionException(); + yield return new ImportCardinalityMismatchException(); + } + + public static IEnumerable GetInnerExceptionsWithNull() + { + foreach (var exception in GetInnerExceptions()) + { + yield return exception; + } + + yield return null; + } + + public static IEnumerable GetBooleans() + { + yield return false; + yield return true; + yield return false; + yield return true; + } + + public static IEnumerable GetAttributedDefinitions() + { + foreach (var type in GetAttributedTypes()) + { + yield return PartDefinitionFactory.CreateAttributed(type); + } + } + + public static IEnumerable GetTypes() + { + yield return typeof(void); + yield return typeof(Type); + yield return typeof(double); + yield return typeof(string); + yield return typeof(int); + yield return typeof(CompositionServices); + yield return typeof(ICompositionService); + } + + public static IEnumerable GetAttributedTypes() + { + foreach (Type type in typeof(Expectations).Assembly.GetTypes()) + { + var definition = AttributedModelDiscovery.CreatePartDefinitionIfDiscoverable(type, (ICompositionElement)null); + if (definition != null) + { + yield return type; + } + + } + } + + public static IEnumerable GetMembers() + { + yield return typeof(String).GetSingleMember("Length"); + yield return typeof(Int32).GetSingleMember("MaxValue"); + } + + public static IEnumerable GetEnumValues() where TEnum : struct + { + var values = TestServices.GetEnumValues(); + + foreach (TEnum value in values) + { + yield return value; + } + } + + public static IEnumerable GetInvalidEnumValues() where TEnum : struct + { + var bounds = GetEnumBounds(); + + yield return AddEnum(bounds.Item1, -3); + yield return AddEnum(bounds.Item1, -2); + yield return AddEnum(bounds.Item1, -1); + yield return AddEnum(bounds.Item2, 1); + yield return AddEnum(bounds.Item2, 2); + yield return AddEnum(bounds.Item2, 3); + yield return (TEnum)(object)(int.MinValue + 1); + yield return (TEnum)(object)int.MinValue; + yield return (TEnum)(object)int.MaxValue; + yield return (TEnum)(object)(int.MaxValue - 1); + } + + public static IEnumerable GetObjectArraysWithNull() + { + yield return null; + yield return new object[0]; + yield return new object[] { null }; + yield return new string[] { null }; + yield return new object[] { 1, "2", 3.0 }; + yield return new string[] { "1" }; + yield return new string[] { "1", "2" }; + yield return new string[] { "1", "2", "3" }; + } + + private static TEnum AddEnum(TEnum left, int right) where TEnum : struct + { + int intRight = (int)(object)left; + + return (TEnum)(object)(intRight + right); + } + + private static Tuple GetEnumBounds() where TEnum : struct + { + var values = TestServices.GetEnumValues(); + + return new Tuple(values.First(), values.Last()); + } + + private static IEnumerable> GetEmptyCollections() + { + yield return new T[0]; + yield return Enumerable.Empty(); + yield return new List(); + yield return new Collection(); + yield return new Dictionary().Keys; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportAttributeTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportAttributeTests.cs new file mode 100644 index 0000000000..85259fec3c --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportAttributeTests.cs @@ -0,0 +1,90 @@ +// 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.ComponentModel.Composition.Hosting; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + class ExportImplementer + { + [Export] + public ExportOnIndexer this[int index] + { + get { return new ExportOnIndexer(); } + } + } + + public class ExportOnIndexer { } + + public class ExportAttributeTests + { + [Fact] + public void Constructor1_ShouldSetContractNamePropertyToEmptyString() + { + var attribute = new ExportAttribute(); + + Assert.Null(attribute.ContractName); + Assert.Null(attribute.ContractType); + } + + [Fact] + public void Constructor2_NullAsContractNameArgument_ShouldSetContractNamePropertyToEmptyString() + { + var attribute = new ExportAttribute((string)null); + + Assert.Null(attribute.ContractName); + Assert.Null(attribute.ContractType); + } + + [Fact] + public void Constructor3_NullAsContractTypeArgument_ShouldSetContractNamePropertyToEmptyString() + { + var attribute = new ExportAttribute((Type)null); + + Assert.Null(attribute.ContractName); + Assert.Null(attribute.ContractType); + } + + [Fact] + public void Constructor4_NullAsContractTypeArgument_ShouldSetContractNamePropertyToEmptyString() + { + var attribute = new ExportAttribute((string)null, (Type)null); + + Assert.Null(attribute.ContractName); + Assert.Null(attribute.ContractType); + } + + [Fact] + public void Constructor2_ValueAsContractNameArgument_ShouldSetContractNameProperty() + { + var expectations = Expectations.GetContractNamesWithEmpty(); + + foreach (var e in expectations) + { + var attribute = new ExportAttribute(e); + + Assert.Equal(e, attribute.ContractName); + } + } + + [Fact] + public void ExportIndexers_ShouldThrowSomething() + { + var con = new CompositionContainer( + new TypeCatalog(typeof(WorkingType), typeof(Constants), typeof(ExportImplementer), typeof(ExportOnIndexer)) + ); + + var v1 = con.GetExportedValue(); + + ExceptionAssert.Throws(RetryMode.DoNotRetry, () => + { + var v2 = con.GetExportedValue(); + Console.WriteLine(v2.ToString()); + }); + } + + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportCollectionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportCollectionTests.cs new file mode 100644 index 0000000000..35e15a96f3 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportCollectionTests.cs @@ -0,0 +1,198 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class ExportCollectionTests + { + public class Importer + { + [ImportMany("Value")] + public Collection> CollectionPlain { get; set; } + + [ImportMany("Value")] + public Collection>> CollectionPlainRawMetadata { get; set; } + + [ImportMany("EmptyValue")] + public Collection> CollectionPlainEmpty { get; set; } + + [ImportMany("EmptyValue")] + public Collection>> CollectionPlainEmptyRawMetadata { get; set; } + + [ImportMany("Value")] + public Collection> CollectionTyped { get; set; } + + [ImportMany("Value")] + public Collection>> CollectionTypedRawMetadata { get; set; } + + [ImportMany("EmptyValue")] + public Collection> CollectionTypedEmpty { get; set; } + + [ImportMany("Value")] + public Collection> CollectionTypedMetadata { get; set; } + + [ImportMany("EmptyValue")] + public Collection> CollectionTypedMetadataEmpty { get; set; } + + [ImportMany("Value")] + public IEnumerable ReadWriteEnumerable { get; set; } + + [ImportMany("EmptyValue")] + public IEnumerable ReadWriteEnumerableEmpty { get; set; } + + [ImportMany("Value")] + public IEnumerable> MetadataUntypedEnumerable { get; set; } + + [ImportMany("Value")] + public IEnumerable>> MetadataUntypedEnumerableRawMetadata { get; set; } + + [ImportMany("EmptyValue")] + public IEnumerable> MetadataUntypedEnumerableEmpty { get; set; } + + [ImportMany("EmptyValue")] + public IEnumerable>> MetadataUntypedEnumerableEmptyRawMetadata { get; set; } + + [ImportMany("Value")] + public IEnumerable> MetadataTypedEnumerable { get; set; } + + [ImportMany("Value")] + public IEnumerable>> MetadataTypedEnumerableRawMetadata { get; set; } + + [ImportMany("EmptyValue")] + public IEnumerable> MetadataTypedEnumerableEmpty { get; set; } + + [ImportMany("Value")] + public IEnumerable> MetadataFullyTypedEnumerable { get; set; } + + [ImportMany("EmptyValue")] + public IEnumerable> MetadataFullyTypedEnumerableEmpty { get; set; } + + public void VerifyImport(params int[] expectedValues) + { + object[] untypedExpectedValues = expectedValues.Cast().ToArray(); + + ExportsAssert.AreEqual(CollectionPlain, untypedExpectedValues); + ExportsAssert.AreEqual(CollectionPlainRawMetadata, untypedExpectedValues); + EqualityExtensions.IsTrueForAll(CollectionPlainRawMetadata, i => true.Equals(i.Metadata["PropertyName"])); + Assert.Empty(CollectionPlainEmpty); + Assert.Empty(CollectionPlainEmptyRawMetadata); + + // Add a new Export to this collection to ensure that it doesn't + // modifiy the other collections because they should each have there + // own collection instance + CollectionPlain.Add(ExportFactory.Create("Value")); + + ExportsAssert.AreEqual(CollectionTyped, expectedValues); + ExportsAssert.AreEqual(CollectionTypedRawMetadata, expectedValues); + EqualityExtensions.IsTrueForAll(CollectionTypedRawMetadata, i => true.Equals(i.Metadata["PropertyName"])); + Assert.Empty(CollectionTypedEmpty); + + ExportsAssert.AreEqual(CollectionTypedMetadata, expectedValues); + EqualityExtensions.IsTrueForAll(CollectionTypedMetadata, i => true == i.Metadata.PropertyName); + Assert.Empty(CollectionTypedMetadataEmpty); + + EnumerableAssert.AreEqual(ReadWriteEnumerable, expectedValues); + Assert.Empty(ReadWriteEnumerableEmpty); + + ExportsAssert.AreEqual(MetadataUntypedEnumerable, untypedExpectedValues); + ExportsAssert.AreEqual(MetadataUntypedEnumerableRawMetadata, untypedExpectedValues); + EqualityExtensions.IsTrueForAll(MetadataUntypedEnumerableRawMetadata, i => true.Equals(i.Metadata["PropertyName"])); + Assert.Empty(MetadataUntypedEnumerableEmpty); + Assert.Empty(MetadataUntypedEnumerableEmptyRawMetadata); + + ExportsAssert.AreEqual(MetadataTypedEnumerable, expectedValues); + ExportsAssert.AreEqual(MetadataTypedEnumerableRawMetadata, expectedValues); + EqualityExtensions.IsTrueForAll(MetadataTypedEnumerableRawMetadata, i => true.Equals(i.Metadata["PropertyName"])); + Assert.Empty(MetadataTypedEnumerableEmpty); + + ExportsAssert.AreEqual(MetadataFullyTypedEnumerable, expectedValues); + EqualityExtensions.IsTrueForAll(MetadataFullyTypedEnumerable, i => true == i.Metadata.PropertyName); + Assert.Empty(MetadataFullyTypedEnumerableEmpty); + } + } + + public class ExporterDefault21 + { + public ExporterDefault21() { Value = 21; } + public ExporterDefault21(int v) { Value = v; } + + [Export("Value")] + [ExportMetadata("PropertyName", true)] + public int Value { get; set; } + } + + public class ExporterDefault42 + { + public ExporterDefault42() { Value = 42; } + public ExporterDefault42(int v) { Value = v; } + + [Export("Value")] + [ExportMetadata("PropertyName", true)] + public int Value { get; set; } + } + + [Fact] + [Trait("Type", "Integration")] + public void ImportCollectionsFromContainerOnly() + { + var container = ContainerFactory.Create(); + Importer importer = new Importer(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddParts(importer + , new ExporterDefault21() + , new ExporterDefault21(22) + , new ExporterDefault42() + , new ExporterDefault42(43)); + + container.Compose(batch); + + importer.VerifyImport(21, 22, 42, 43); + } + + [Fact] + [Trait("Type", "Integration")] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void ImportCollectionsFromCatalogOnly() + { + var cat = CatalogFactory.CreateDefaultAttributed(); + var container = new CompositionContainer(cat); + Importer importer = new Importer(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddParts(importer); + container.Compose(batch); + + importer.VerifyImport(21, 42); + } + + [Fact] + [Trait("Type", "Integration")] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void ImportCollectionsFormContainerAndCatalog() + { + var cat = CatalogFactory.CreateDefaultAttributed(); + var container = new CompositionContainer(cat); + Importer importer = new Importer(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddParts(importer + , new ExporterDefault21(22) + , new ExporterDefault42(43)); + + container.Compose(batch); + + importer.VerifyImport(22, 43, 21, 42); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportDefinitionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportDefinitionTests.cs new file mode 100644 index 0000000000..6858277634 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportDefinitionTests.cs @@ -0,0 +1,183 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class ExportDefinitionTests + { + [Fact] + public void Constructor1_ShouldSetMetadataPropertyToEmptyDictionary() + { + var definition = new DerivedExportDefinition(); + + Assert.Empty(definition.Metadata); + } + + [Fact] + public void Constructor1_ShouldSetMetadataPropertyToReadOnlyDictionary() + { + var definition = new DerivedExportDefinition(); + + ExceptionAssert.Throws(() => + { + definition.Metadata["Value"] = "Value"; + }); + } + + [Fact] + public void Constructor2_NullAsContractNameArgument_ShouldThrowArgumentNull() + { + Assert.Throws("contractName", () => + { + new ExportDefinition((string)null, new Dictionary()); + }); + } + + [Fact] + public void Constructor2_EmptyStringAsContractNameArgument_ShouldThrowArgument() + { + Assert.Throws("contractName", () => + { + new ExportDefinition(string.Empty, new Dictionary()); + }); + } + + [Fact] + public void Constructor2_ValueAsContractNameArgument_ShouldSetContractNameProperty() + { + var expectations = Expectations.GetContractNames(); + + foreach (var e in expectations) + { + var definition = new ExportDefinition(e, new Dictionary()); + + Assert.Equal(e, definition.ContractName); + } + } + + [Fact] + public void Constructor2_NullAsMetadataArgument_ShouldSetMetadataPropertyToEmptyDictionary() + { + var definition = new ExportDefinition("Contract", (IDictionary)null); ; + + Assert.Empty(definition.Metadata); + } + + [Fact] + public void Constructor2_NullAsMetadataArgument_ShouldSetMetadataPropertyToReadOnlyDictionary() + { + var definition = new ExportDefinition("Contract", (IDictionary)null); + + ExceptionAssert.Throws(() => + { + definition.Metadata["Value"] = "Value"; + }); + } + + [Fact] + public void Constructor2_WritableDictionaryAsMetadataArgument_ShouldSetMetadataPropertyToReadOnlyDictionary() + { + var definition = new ExportDefinition("Contract", new Dictionary()); + + ExceptionAssert.Throws(() => + { + definition.Metadata["Value"] = "Value"; + }); + } + + [Fact] + public void Constructor2_DictionaryAsMetadataArgument_ShouldSetMetadataProperty() + { + var expectations = Expectations.GetMetadata(); + + foreach (var e in expectations) + { + var definition = new ExportDefinition("Contract", e); + + EnumerableAssert.AreEqual(e, definition.Metadata); + } + } + + [Fact] + public void ContractName_WhenNotOverridden_ShouldThrowNotImplemented() + { + var definition = new DerivedExportDefinition(); + + ExceptionAssert.Throws(() => + { + var contractName = definition.ContractName; + }); + } + + [Fact] + public void ToString_WhenContractNameNotOverridden_ShouldThrowNotImplemented() + { + var definition = new DerivedExportDefinition(); + + ExceptionAssert.Throws(() => + { + definition.ToString(); + }); + } + + [Fact] + public void ToString_ShouldReturnContractNameProperty() + { + var expectations = Expectations.GetContractNames(); + + foreach (var e in expectations) + { + var definition = new ExportDefinition(e, new Dictionary()); + + Assert.Equal(e, definition.ToString()); + } + } + + [Fact] + public void ToString_ShouldReturnOverriddenContractNameProperty() + { + var expectations = Expectations.GetContractNamesWithEmpty(); + + foreach (var e in expectations) + { + var definition = new DerivedExportDefinition(() => e); + + Assert.Equal(e, definition.ToString()); + } + } + + private class DerivedExportDefinition : ExportDefinition + { + private readonly Func _contractNameGetter; + + public DerivedExportDefinition() + { + } + + public DerivedExportDefinition(Func contractNameGetter) + { + _contractNameGetter = contractNameGetter; + } + + public override string ContractName + { + get + { + if (_contractNameGetter != null) + { + return _contractNameGetter(); + } + + return base.ContractName; + } + } + } + } +} + diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportFactoryTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportFactoryTests.cs new file mode 100644 index 0000000000..8e9f4f3261 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportFactoryTests.cs @@ -0,0 +1,406 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.ComponentModel.Composition.ReflectionModel; +using System.Linq; +using System.Reflection; +using Xunit; + +namespace Tests.Integration +{ + public class ExportFactoryTests + { + public interface IId + { + int Id { get; } + } + + public interface IIdTypeMetadata + { + string IdType { get; } + string ExportTypeIdentity { get; } + } + + [Export(typeof(IId))] + [ExportMetadata("IdType", "PostiveIncrement")] + public class UniqueExport : IId, IDisposable + { + private static int lastId = 0; + + public UniqueExport() + { + Id = lastId++; + } + + public int Id { get; private set; } + + public void Dispose() + { + Id = -1; + } + } + + [Export] + public class ExportFactoryImporter + { + [ImportingConstructor] + public ExportFactoryImporter( + ExportFactory idCreatorTCtor, + ExportFactory idCreatorTMCtor) + { + this._idCreatorTCtor = idCreatorTCtor; + this._idCreatorTMCtor = idCreatorTMCtor; + } + + private ExportFactory _idCreatorTCtor; + private ExportFactory _idCreatorTMCtor; + + [Import(typeof(IId))] + public ExportFactory _idCreatorTField = null; // public so these can work on SL + + [Import] + public ExportFactory _idCreatorTMField = null; // public so these can work on SL + + [Import] + public ExportFactory IdCreatorTProperty { get; set; } + + [Import(typeof(IId))] + public ExportFactory IdCreatorTMProperty { get; set; } + + [ImportMany] + public ExportFactory[] IdCreatorsTProperty { get; set; } + + [ImportMany] + public ExportFactory[] IdCreatorsTMProperty { get; set; } + + public void AssertValid() + { + var ids = new int[] + { + VerifyExportFactory(this._idCreatorTCtor), + VerifyExportFactory(this._idCreatorTMCtor), + VerifyExportFactory(this._idCreatorTField), + VerifyExportFactory(this._idCreatorTMField), + VerifyExportFactory(this.IdCreatorTProperty), + VerifyExportFactory(this.IdCreatorTMProperty), + VerifyExportFactory(this.IdCreatorsTProperty[0]), + VerifyExportFactory(this.IdCreatorsTMProperty[0]) + }; + + Assert.Equal(1, this.IdCreatorsTProperty.Length); + Assert.Equal(1, this.IdCreatorsTMProperty.Length); + + Assert.Equal(ids.Count(), new HashSet(ids).Count()); + } + + private int VerifyExportFactory(ExportFactory creator) + { + var val1 = creator.CreateExport(); + var val2 = creator.CreateExport(); + + Assert.NotEqual(val1.Value, val2.Value); + Assert.NotEqual(val1.Value.Id, val2.Value.Id); + + Assert.True(val1.Value.Id >= 0, "Id should be positive"); + + val1.Dispose(); + + Assert.True(val1.Value.Id < 0, "Disposal of the value should set the id to negative"); + + return creator.CreateExport().Value.Id; + } + + private int VerifyExportFactory(ExportFactory creator) + { + var val = VerifyExportFactory((ExportFactory)creator); + + Assert.Equal("PostiveIncrement", creator.Metadata.IdType); + Assert.Equal(AttributedModelServices.GetTypeIdentity(typeof(ComposablePartDefinition)), creator.Metadata.ExportTypeIdentity); + + return val; + } + } + + [Fact] + public void ExportFactoryStandardImports_ShouldWorkProperly() + { + var container = CreateWithAttributedCatalog(typeof(UniqueExport), typeof(ExportFactoryImporter)); + var partCreatorImporter = container.GetExportedValue(); + + partCreatorImporter.AssertValid(); + } + + [Export] + public class Foo : IDisposable + { + public bool IsDisposed { get; private set; } + + public void Dispose() + { + this.IsDisposed = true; + } + } + + [Export] + public class SimpleExportFactoryImporter + { + [Import] + public ExportFactory FooFactory { get; set; } + } + + [Fact] + public void ExportFactoryOfT_RecompositionSingle_ShouldBlockChanges() + { + var aggCat = new AggregateCatalog(); + var typeCat = new TypeCatalog(typeof(Foo)); + aggCat.Catalogs.Add(new TypeCatalog(typeof(SimpleExportFactoryImporter))); + aggCat.Catalogs.Add(typeCat); + + var container = new CompositionContainer(aggCat); + + var fooFactory = container.GetExportedValue(); + + Assert.Throws(() => + aggCat.Catalogs.Remove(typeCat)); + + Assert.Throws(() => + aggCat.Catalogs.Add(new TypeCatalog(typeof(Foo)))); + } + + [Export] + public class ManyExportFactoryImporter + { + [ImportMany(AllowRecomposition = true)] + public ExportFactory[] FooFactories { get; set; } + } + + [Fact] + public void FactoryOfT_RecompositionImportMany_ShouldSucceed() + { + var aggCat = new AggregateCatalog(); + var typeCat = new TypeCatalog(typeof(Foo)); + aggCat.Catalogs.Add(new TypeCatalog(typeof(ManyExportFactoryImporter))); + aggCat.Catalogs.Add(typeCat); + + var container = new CompositionContainer(aggCat); + + var fooFactories = container.GetExportedValue(); + + Assert.Equal(1, fooFactories.FooFactories.Length); + + aggCat.Catalogs.Add(new TypeCatalog(typeof(Foo))); + + Assert.Equal(2, fooFactories.FooFactories.Length); + } + + public class ExportFactoryExplicitCP + { + [Import(RequiredCreationPolicy = CreationPolicy.Any)] + public ExportFactory FooCreatorAny { get; set; } + + [Import(RequiredCreationPolicy = CreationPolicy.NonShared)] + public ExportFactory FooCreatorNonShared { get; set; } + + [Import(RequiredCreationPolicy = CreationPolicy.Shared)] + public ExportFactory FooCreatorShared { get; set; } + + [ImportMany(RequiredCreationPolicy = CreationPolicy.Any)] + public ExportFactory[] FooCreatorManyAny { get; set; } + + [ImportMany(RequiredCreationPolicy = CreationPolicy.NonShared)] + public ExportFactory[] FooCreatorManyNonShared { get; set; } + + [ImportMany(RequiredCreationPolicy = CreationPolicy.Shared)] + public ExportFactory[] FooCreatorManyShared { get; set; } + } + + [Fact] + public void ExportFactory_ExplicitCreationPolicy_CPShouldBeIgnored() + { + var container = CreateWithAttributedCatalog(typeof(Foo)); + + var part = new ExportFactoryExplicitCP(); + + container.SatisfyImportsOnce(part); + + // specifying the required creation policy explicit on the import + // of a ExportFactory will be ignored because the ExportFactory requires + // the part it wraps to be either Any or NonShared to work properly. + Assert.NotNull(part.FooCreatorAny); + Assert.NotNull(part.FooCreatorNonShared); + Assert.NotNull(part.FooCreatorShared); + + Assert.Equal(1, part.FooCreatorManyAny.Length); + Assert.Equal(1, part.FooCreatorManyNonShared.Length); + Assert.Equal(1, part.FooCreatorManyShared.Length); + } + + public class ExportFactoryImportRequiredMetadata + { + [ImportMany] + public ExportFactory[] FooCreator { get; set; } + + [ImportMany] + public ExportFactory[] FooCreatorWithMetadata { get; set; } + } + + [Fact] + public void ExportFactory_ImportRequiredMetadata_MissingMetadataShouldCauseImportToBeExcluded() + { + var container = CreateWithAttributedCatalog(typeof(Foo)); + + var part = new ExportFactoryImportRequiredMetadata(); + + container.SatisfyImportsOnce(part); + + Assert.Equal(1, part.FooCreator.Length); + Assert.Equal(0, part.FooCreatorWithMetadata.Length); + } + + [Export(typeof(Foo))] + [PartCreationPolicy(CreationPolicy.Shared)] + public class SharedFoo : Foo + { + } + + [Fact] + public void ExportFactory_ImportShouldNotImportSharedPart() + { + var container = CreateWithAttributedCatalog(typeof(SharedFoo)); + + var foo = container.GetExportedValue(); + Assert.NotNull(foo); + + var part = new ExportFactoryImportRequiredMetadata(); + + container.SatisfyImportsOnce(part); + + Assert.Equal(0, part.FooCreator.Length); + } + + [Fact] + public void ExportFactory_QueryContainerDirectly_ShouldWork() + { + var container = CreateWithAttributedCatalog(typeof(Foo)); + + var importDef = ReflectionModelServicesEx.CreateImportDefinition( + new LazyMemberInfo(MemberTypes.Field, () => new MemberInfo[] { typeof(ExportFactoryTests) }), // Give it a bogus member + AttributedModelServices.GetContractName(typeof(Foo)), + AttributedModelServices.GetTypeIdentity(typeof(Foo)), + Enumerable.Empty>(), + ImportCardinality.ZeroOrMore, + true, + CreationPolicy.Any, + true, // isExportFactory + null); + + var exports = container.GetExports(importDef); + + var partCreator = exports.Single(); + + // Manually walk the steps of using a raw part creator which is modeled as a PartDefinition with + // a single ExportDefinition. + var partDef = (ComposablePartDefinition)partCreator.Value; + var part = partDef.CreatePart(); + var foo = (Foo)part.GetExportedValue(partDef.ExportDefinitions.Single()); + + Assert.NotNull(foo); + + var foo1 = (Foo)part.GetExportedValue(partDef.ExportDefinitions.Single()); + Assert.Equal(foo, foo1); + + // creating a new part should result in getting a new exported value + var part2 = partDef.CreatePart(); + var foo2 = (Foo)part2.GetExportedValue(partDef.ExportDefinitions.Single()); + + Assert.NotEqual(foo, foo2); + + // Disposing of part should cause foo to be disposed + ((IDisposable)part).Dispose(); + Assert.True(foo.IsDisposed); + } + + [Export] + public class PartImporter + { + [Import] + public ExportFactory Creator { get; set; } + } + + [Export] + public class SimpleExport + { + } + + [Fact] + public void ExportFactory_SimpleRejectionRecurrection_ShouldWork() + { + var importTypeCat = new TypeCatalog(typeof(PartImporter)); + var aggCatalog = new AggregateCatalog(importTypeCat); + var container = new CompositionContainer(aggCatalog); + var exports = container.GetExports>(); + Assert.Equal(0, exports.Count()); + + aggCatalog.Catalogs.Add(new TypeCatalog(typeof(SimpleExport))); + + exports = container.GetExports>(); + Assert.Equal(1, exports.Count()); + } + + private static CompositionContainer CreateWithAttributedCatalog(params Type[] types) + { + var catalog = new TypeCatalog(types); + return new CompositionContainer(catalog); + } + + [Export] + class Apple { } + + [Export] + class Tree : IDisposable + { + private List> grownApples = new List>(); + + [Import] + private ExportFactory AppleFactory { get; set; } + internal Apple GrowApple() + { + var apple = this.AppleFactory.CreateExport(); + this.grownApples.Add(apple); + return apple.Value; + } + + internal void DisposeApples() + { + foreach (var apple in this.grownApples) + { + apple.Dispose(); + } + this.grownApples.Clear(); + } + + public void Dispose() + { + this.DisposeApples(); + } + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void ExportFactory_SimpleDispose() + { + var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); + var container = new CompositionContainer(catalog); + var tree = container.GetExportedValue(); + var apple = tree.GrowApple(); + container.Dispose(); + } + + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportMetadataAttributeTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportMetadataAttributeTests.cs new file mode 100644 index 0000000000..0dff964953 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportMetadataAttributeTests.cs @@ -0,0 +1,67 @@ +// 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 Xunit; + +namespace System.ComponentModel.Composition +{ + public class ExportMetadataAttributeTests + { + [Fact] + public void Constructor_NullAsNameArgument_ShouldSetNamePropertyToEmptyString() + { + var attribute = new ExportMetadataAttribute((string)null, "Value"); + + Assert.Equal(string.Empty, attribute.Name); + } + + [Fact] + public void Constructor_ShouldSetIsMultiplePropertyToFalse() + { + var attribute = new ExportMetadataAttribute("Name", "Value"); + + Assert.False(attribute.IsMultiple); + } + + [Fact] + public void Constructor_ValueAsNameArgument_ShouldSetNameProperty() + { + var expectations = Expectations.GetMetadataNames(); + + foreach (var e in expectations) + { + var attribute = new ExportMetadataAttribute(e, "Value"); + + Assert.Equal(e, attribute.Name); + } + } + + [Fact] + public void Constructor_ValueAsValueArgument_ShouldSetValueProperty() + { + var expectations = Expectations.GetMetadataValues(); + + foreach (var e in expectations) + { + var attribute = new ExportMetadataAttribute("Name", e); + + Assert.Equal(e, attribute.Value); + } + } + + [Fact] + public void IsMultiple_ValueAsValueArgument_ShouldSetPropert() + { + var expectations = Expectations.GetBooleans(); + + var attribute = new ExportMetadataAttribute("Name", "Value"); + + foreach (var e in expectations) + { + attribute.IsMultiple = e; + Assert.Equal(e, attribute.IsMultiple); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportProviderTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportProviderTests.cs new file mode 100644 index 0000000000..18ad492048 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportProviderTests.cs @@ -0,0 +1,53 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Primitives; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class ExportProviderTests + { + [Fact] + public void GetExports2_NullAsDefinitionArgument_ShouldThrowArgumentNull() + { + var provider = ExportProviderFactory.Create(); + + Assert.Throws(() => + { + provider.GetExports((ImportDefinition)null); + }); + } + + [Fact] + public void TryGetExports_NullAsDefinitionArgument_ShouldThrowArgumentNull() + { + var provider = ExportProviderFactory.Create(); + + Assert.Throws(() => + { + IEnumerable exports; + provider.TryGetExports((ImportDefinition)null, null, out exports); + }); + } + + [Fact] + public void TryGetExports_NullAsDefinitionArgument_ShouldNotSetExportsArgument() + { + var provider = ExportProviderFactory.Create(); + + IEnumerable exports = new Export[0]; + IEnumerable results = exports; + + Assert.Throws(() => + { + provider.TryGetExports((ImportDefinition)null, null, out results); + }); + + Assert.Same(exports, results); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportTests.cs new file mode 100644 index 0000000000..812b0f9231 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportTests.cs @@ -0,0 +1,435 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Primitives; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class ExportTests + { + [Fact] + public void Constructor1_ShouldNotThrow() + { + new NoOverridesExport(); + } + + [Fact] + public void Constructor2_NullAsExportedValueGetterArgument_ShouldThrowArgumentNull() + { + var definition = ExportDefinitionFactory.Create(); + + Assert.Throws("exportedValueGetter", () => + { + new Export(definition, (Func)null); + }); + } + + [Fact] + public void Constructor3_NullAsExportedValueGetterArgument_ShouldThrowArgumentNull() + { + Assert.Throws("exportedValueGetter", () => + { + new Export("ContractName", (Func)null); + }); + } + + [Fact] + public void Constructor4_NullAsExportedValueGetterArgument_ShouldThrowArgumentNull() + { + var metadata = new Dictionary(); + + Assert.Throws("exportedValueGetter", () => + { + new Export("ContractName", metadata, (Func)null); + }); + } + + [Fact] + public void Constructor2_NullAsDefinitionArgument_ShouldThrowArgumentNull() + { + Assert.Throws("definition", () => + { + new Export((ExportDefinition)null, () => null); + }); + } + + [Fact] + public void Constructor2_DefinitionAsDefinitionArgument_ShouldSetDefinitionProperty() + { + var definition = ExportDefinitionFactory.Create(); + + var export = new Export(definition, () => null); + + Assert.Same(definition, export.Definition); + } + + [Fact] + public void Constructor3_NullAsContractNameArgument_ShouldThrowArgumentNull() + { + Assert.Throws("contractName", () => + { + new Export((string)null, () => null); + }); + } + + [Fact] + public void Constructor4_NullAsContractNameArgument_ShouldThrowArgumentNull() + { + Assert.Throws("contractName", () => + { + new Export((string)null, new Dictionary(), () => null); + }); + } + + [Fact] + public void Constructor3_EmptyStringAsContractNameArgument_ShouldThrowArgument() + { + Assert.Throws("contractName", () => + { + new Export(string.Empty, () => null); + }); + } + + [Fact] + public void Constructor4_EmptyStringAsContractNameArgument_ShouldThrowArgument() + { + Assert.Throws("contractName", () => + { + new Export(string.Empty, new Dictionary(), () => null); + }); + } + + [Fact] + public void Constructor3_ValueAsContractNameArgument_ShouldSetDefinitionContractNameProperty() + { + var expectations = Expectations.GetContractNames(); + + foreach (var e in expectations) + { + var export = new Export(e, () => null); + + Assert.Equal(e, export.Definition.ContractName); + } + } + + [Fact] + public void Constructor4_ValueAsContractNameArgument_ShouldSetDefinitionContractNameProperty() + { + var expectations = Expectations.GetContractNames(); + + foreach (var e in expectations) + { + var export = new Export(e, new Dictionary(), () => null); + + Assert.Equal(e, export.Definition.ContractName); + } + } + + [Fact] + public void Constructor3_ShouldSetMetadataPropertyToEmptyDictionary() + { + var export = new Export("ContractName", () => null); + + Assert.Empty(export.Metadata); + } + + [Fact] + public void Constructor4_NullAsMetadataArgument_ShouldSetMetadataPropertyToEmptyDictionary() + { + var export = new Export("ContractName", (IDictionary)null, () => null); + + Assert.Empty(export.Metadata); + } + + [Fact] + public void Constructor3_NullAsMetadataArgument_ShouldSetMetadataPropertyToReadOnlyDictionary() + { + var export = new Export("ContractName", () => null); + + ExceptionAssert.Throws(() => + { + export.Metadata["Value"] = "Value"; + }); + } + + [Fact] + public void Constructor4_NullAsMetadataArgument_ShouldSetMetadataPropertyToReadOnlyDictionary() + { + var export = new Export("ContractName", (IDictionary)null, () => null); + + ExceptionAssert.Throws(() => + { + export.Metadata["Value"] = "Value"; + }); + } + + [Fact] + public void Constructor4_WritableDictionaryAsMetadataArgument_ShouldSetMetadataPropertyToReadOnlyDictionary() + { + var export = new Export("ContractName", new Dictionary(), () => null); + + ExceptionAssert.Throws(() => + { + export.Metadata["Value"] = "Value"; + }); + } + + [Fact] + public void Constructor4_DictionaryAsMetadataArgument_ShouldSetMetadataProperty() + { + var expectations = Expectations.GetMetadata(); + + foreach (var e in expectations) + { + var export = new Export("ContractName", e, () => null); + + EnumerableAssert.AreEqual(e, export.Metadata); + } + } + + [Fact] + public void Constructor3_ShouldSetDefinitionMetadataPropertyToEmptyDictionary() + { + var export = new Export("ContractName", () => null); + + Assert.Empty(export.Definition.Metadata); + } + + [Fact] + public void Constructor4_NullAsMetadataArgument_ShouldSetDefinitionMetadataPropertyToEmptyDictionary() + { + var export = new Export("ContractName", (IDictionary)null, () => null); + + Assert.Empty(export.Definition.Metadata); + } + + [Fact] + public void Constructor3_ShouldSetDefinitionMetadataPropertyToReadOnlyDictionary() + { + var export = new Export("ContractName", () => null); + + ExceptionAssert.Throws(() => + { + export.Definition.Metadata["Value"] = "Value"; + }); + } + + [Fact] + public void Constructor4_NullAsMetadataArgument_ShouldSetDefinitionMetadataPropertyToReadOnlyDictionary() + { + var export = new Export("ContractName", (IDictionary)null, () => null); + + ExceptionAssert.Throws(() => + { + export.Definition.Metadata["Value"] = "Value"; + }); + } + + [Fact] + public void Constructor4_WritableDictionaryAsMetadataArgument_ShouldSetDefinitionMetadataPropertyToReadOnlyDictionary() + { + var export = new Export("ContractName", new Dictionary(), () => null); + + ExceptionAssert.Throws(() => + { + export.Definition.Metadata["Value"] = "Value"; + }); + } + + [Fact] + public void Constructor4_DictionaryAsMetadataArgument_ShouldSetDefinitionMetadataProperty() + { + var expectations = Expectations.GetMetadata(); + + foreach (var e in expectations) + { + var export = new Export("ContractName", e, () => null); + + EnumerableAssert.AreEqual(e, export.Definition.Metadata); + } + } + + [Fact] + public void Constructor2_FuncReturningAStringAsExportedValueGetter_ShouldBeReturnedByGetExportedValue() + { + var definition = ExportDefinitionFactory.Create(); + + var export = new Export(definition, () => "Value"); + + Assert.Equal("Value", export.Value); + } + + [Fact] + public void Constructor3_FuncReturningAStringAsExportedValueGetter_ShouldBeReturnedByGetExportedValue() + { + var export = new Export("ContractName", () => "Value"); + + Assert.Equal("Value", export.Value); + } + + [Fact] + public void Constructor4_FuncReturningAStringAsExportedValueGetter_ShouldBeReturnedByGetExportedValue() + { + var export = new Export("ContractName", new Dictionary(), () => "Value"); + + Assert.Equal("Value", export.Value); + } + + [Fact] + public void Constructor2_FuncReturningNullAsExportedValueGetter_ShouldBeReturnedByGetExportedValue() + { + var definition = ExportDefinitionFactory.Create(); + + var export = new Export(definition, () => null); + + Assert.Null(export.Value); + } + + [Fact] + public void Constructor3_FuncReturningNullAsExportedValueGetter_ShouldBeReturnedByGetExportedValue() + { + var export = new Export("ContractName", () => null); + + Assert.Null(export.Value); + } + + [Fact] + public void Constructor4_FuncReturningNullAsExportedValueGetter_ShouldBeReturnedByGetExportedValue() + { + var export = new Export("ContractName", new Dictionary(), () => null); + + Assert.Null(export.Value); + } + + [Fact] + public void Metadata_DerivedExportDefinition_ShouldReturnDefinitionMetadata() + { + var expectations = Expectations.GetMetadata(); + + foreach (var e in expectations) + { + var definition = ExportDefinitionFactory.Create("ContractName", e); + + var export = new DerivedExport(definition); + + EnumerableAssert.AreEqual(e, export.Metadata); + } + } + + [Fact] + public void Definition_WhenNotOverridden_ShouldThrowNotImplemented() + { + var export = new NoOverridesExport(); + + ExceptionAssert.Throws(() => + { + var definition = export.Definition; + }); + } + + [Fact] + public void Metadata_WhenDefinitionNotOverridden_ShouldThrowNotImplemented() + { + var export = new NoOverridesExport(); + + ExceptionAssert.Throws(() => + { + var definition = export.Metadata; + }); + } + + [Fact] + public void GetExportedValue_WhenGetExportedValueCoreNotOverridden_ShouldThrowNotImplemented() + { + var export = new NoOverridesExport(); + + ExceptionAssert.Throws(() => + { + var value = export.Value; + }); + } + + [Fact] + public void GetExportedValue_ShouldCacheExportedValueGetter() + { + int count = 0; + + var export = new Export("ContractName", () => + { + count++; + return count; + }); + + Assert.Equal(1, export.Value); + Assert.Equal(1, export.Value); + Assert.Equal(1, export.Value); + } + + [Fact] + public void GetExportedValue_ShouldCacheOverrideGetExportedValueCore() + { + int count = 0; + + var export = new DerivedExport(() => + { + count++; + return count; + }); + + Assert.Equal(1, export.Value); + Assert.Equal(1, export.Value); + Assert.Equal(1, export.Value); + } + + [Fact] + public void GetExportedValue_ThrowingFuncAsObjectGetterArgument_ShouldThrow() + { + var exceptionToThrow = new Exception(); + + var export = new Export("ContractName", new Dictionary(), () => + { + throw exceptionToThrow; + }); + + ExceptionAssert.Throws(exceptionToThrow, RetryMode.Retry, () => + { + var value = export.Value; + }); + } + + private class NoOverridesExport : Export + { + } + + private class DerivedExport : Export + { + private readonly Func _exportedValueGetter; + private readonly ExportDefinition _definition; + + public DerivedExport(ExportDefinition definition) + { + _definition = definition; + } + + public DerivedExport(Func exportedValueGetter) + { + _exportedValueGetter = exportedValueGetter; + } + + public override ExportDefinition Definition + { + get { return _definition; } + } + + protected override object GetExportedValueCore() + { + return _exportedValueGetter(); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportableAttributeTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportableAttributeTests.cs new file mode 100644 index 0000000000..7081b801db --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportableAttributeTests.cs @@ -0,0 +1,237 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class MetadataAttributeTests + { + [Fact] + [Trait("Type", "Integration")] + public void UntypedStructureTest() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(AttributedModelServices.CreatePart(new BasicTestComponent())); + container.Compose(batch); + var export = container.GetExport>(); + + Assert.NotNull(export.Metadata); + Assert.Equal("One", export.Metadata["String1"]); + Assert.Equal("Two", export.Metadata["String2"]); + var e = export.Metadata["Numbers"] as IList; + Assert.NotNull(e); + Assert.True(e.Contains(1), "Should have 1 in the list"); + Assert.True(e.Contains(2), "Should have 2 in the list"); + Assert.True(e.Contains(3), "Should have 3 in the list"); + Assert.Equal(3, e.Count); + } + + // Silverlight doesn't support strongly typed metadata + [Fact] + [Trait("Type", "Integration")] + public void StronglyTypedStructureTest() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(AttributedModelServices.CreatePart(new BasicTestComponent())); + container.Compose(batch); + + var export = container.GetExport(); + + Assert.NotNull(export.Metadata); + Assert.Equal("One", export.Metadata.String1); + Assert.Equal("Two", export.Metadata.String2); + Assert.True(export.Metadata.Numbers.Contains(1), "Should have 1 in the list"); + Assert.True(export.Metadata.Numbers.Contains(2), "Should have 2 in the list"); + Assert.True(export.Metadata.Numbers.Contains(3), "Should have 3 in the list"); + Assert.Equal(3, export.Metadata.Numbers.Length); + } + + [Fact] + [Trait("Type", "Integration")] + public void StronglyTypedStructureTestWithTransparentViews() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(AttributedModelServices.CreatePart(new BasicTestComponent())); + container.Compose(batch); + + var export = container.GetExport(); + + Assert.NotNull(export.Metadata); + Assert.Equal("One", export.Metadata.String1); + Assert.Equal("Two", export.Metadata.String2); + Assert.True(export.Metadata.Numbers.Contains(1), "Should have 1 in the list"); + Assert.True(export.Metadata.Numbers.Contains(2), "Should have 2 in the list"); + Assert.True(export.Metadata.Numbers.Contains(3), "Should have 3 in the list"); + Assert.Equal(3, export.Metadata.Numbers.Length); + } + + [Export] + // Should cause a conflict with the multiple nature of Name.Bar because + // it isn't marked with IsMultiple=true + [ExportMetadata("Bar", "Blah")] + [Name("MEF")] + [Name("MEF2")] + [PartNotDiscoverable] + public class BasicTestComponentWithInvalidMetadata + { + } + + [Fact] + [Trait("Type", "Integration")] + public void InvalidMetadataAttributeTest() + { + ComposablePart part = AttributedModelServices.CreatePart(new BasicTestComponentWithInvalidMetadata()); + ExportDefinition export = part.ExportDefinitions.First(); + + var ex = Assert.Throws(() => + { + var metadata = export.Metadata; + }); + + Assert.True(ex.Message.Contains("Bar")); + } + + [AttributeUsage(AttributeTargets.All)] + [MetadataAttribute] + public class MetadataWithInvalidCustomAttributeType : Attribute + { + public PersonClass Person { get { return new PersonClass(); } } + + public class PersonClass + { + public string First { get { return "George"; } } + public string Last { get { return "Washington"; } } + } + } + + [Export] + [MetadataWithInvalidCustomAttributeType] + [PartNotDiscoverable] + public class ClassWithInvalidCustomAttributeType + { + + } + + [Fact] + public void InvalidAttributType_CustomType_ShouldThrow() + { + ComposablePart part = AttributedModelServices.CreatePart(new ClassWithInvalidCustomAttributeType()); + ExportDefinition export = part.ExportDefinitions.First(); + + // Should throw InvalidOperationException during discovery because + // the person class is an invalid metadata type + Assert.Throws(() => + { + var metadata = export.Metadata; + }); + } + + [AttributeUsage(AttributeTargets.All)] + [MetadataAttribute] + public class MetadataWithInvalidVersionPropertyAttributeType : Attribute + { + public MetadataWithInvalidVersionPropertyAttributeType() + { + this.Version = new Version(1, 1); + } + public Version Version { get; set; } + } + + [Export] + [MetadataWithInvalidVersionPropertyAttributeType] + [PartNotDiscoverable] + public class ClassWithInvalidVersionPropertyAttributeType + { + + } + + [Fact] + public void InvalidAttributType_VersionPropertyType_ShouldThrow() + { + ComposablePart part = AttributedModelServices.CreatePart(new ClassWithInvalidVersionPropertyAttributeType()); + ExportDefinition export = part.ExportDefinitions.First(); + + // Should throw InvalidOperationException during discovery because + // the person class is an invalid metadata type + Assert.Throws(() => + { + var metadata = export.Metadata; + }); + } + + [MetadataAttribute] + public class BaseMetadataAttribute : Attribute + { + public string BaseKey { get { return "BaseValue"; } } + } + + public class DerivedMetadataAttribute : BaseMetadataAttribute + { + public string DerivedKey { get { return "DerivedValue"; } } + } + + [Export] + [DerivedMetadata] + public class ExportWithDerivedMetadataAttribute { } + + [Fact] + public void DerivedMetadataAttributeAttribute_ShouldSupplyMetadata() + { + ComposablePart part = AttributedModelServices.CreatePart(new ExportWithDerivedMetadataAttribute()); + ExportDefinition export = part.ExportDefinitions.Single(); + + Assert.Equal("BaseValue", export.Metadata["BaseKey"]); + Assert.Equal("DerivedValue", export.Metadata["DerivedKey"]); + } + } + + [AttributeUsage(AttributeTargets.All)] + [MetadataAttribute] + public class BasicMetadataAttribute : Attribute + { + public string String1 { get { return "One"; } } + + public string String2 { get { return "Two"; } } + + public int[] Numbers { get { return new int[] { 1, 2, 3 }; } } + + public CreationPolicy Policy { get { return CreationPolicy.NonShared; } } + + public Type Type { get { return typeof(BasicMetadataAttribute); } } + } + + public interface IStronglyTypedStructure + { + string String1 { get; } + string String2 { get; } + int[] Numbers { get; } + CreationPolicy Policy { get; } + Type Type { get; } + } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + [MetadataAttribute] + public class Name : Attribute + { + public Name(string name) { Bar = name; } + + public string Bar { set; get; } + } + + [PartNotDiscoverable] + [Export] + [BasicMetadata] + public class BasicTestComponent + { + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Extensibility/CustomImportAttribute.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Extensibility/CustomImportAttribute.cs new file mode 100644 index 0000000000..542f3636f2 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Extensibility/CustomImportAttribute.cs @@ -0,0 +1,19 @@ +// 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. + +namespace System.ComponentModel.Composition.Extensibility +{ + [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)] + public class CustomImportAttribute : ImportAttribute + { + public CustomImportAttribute() + { + } + + public CustomImportAttribute(Type type) + : base(type) + { + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Extensibility/CustomImportManyAttribute.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Extensibility/CustomImportManyAttribute.cs new file mode 100644 index 0000000000..bd02b9e080 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Extensibility/CustomImportManyAttribute.cs @@ -0,0 +1,19 @@ +// 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. + +namespace System.ComponentModel.Composition.Extensibility +{ + [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)] + public class CustomImportManyAttribute : ImportManyAttribute + { + public CustomImportManyAttribute() + { + } + + public CustomImportManyAttribute(Type type) + : base(type) + { + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/CatalogFactory.DerivedComposablePartCatalog.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/CatalogFactory.DerivedComposablePartCatalog.cs new file mode 100644 index 0000000000..c03ccbdad8 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/CatalogFactory.DerivedComposablePartCatalog.cs @@ -0,0 +1,28 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Linq; + +namespace System.ComponentModel.Composition.Factories +{ + partial class CatalogFactory + { + private class DerivedComposablePartCatalog : ComposablePartCatalog + { + private readonly IEnumerable _definitions; + + public DerivedComposablePartCatalog(IEnumerable definitions) + { + _definitions = definitions; + } + + public override IQueryable Parts + { + get { return _definitions.AsQueryable(); } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/CatalogFactory.DisposableComposablePartCatalog.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/CatalogFactory.DisposableComposablePartCatalog.cs new file mode 100644 index 0000000000..b2cedbfe05 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/CatalogFactory.DisposableComposablePartCatalog.cs @@ -0,0 +1,44 @@ +// 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.ComponentModel.Composition.Primitives; +using System.Linq; +using Xunit; + +namespace System.ComponentModel.Composition.Factories +{ + partial class CatalogFactory + { + // NOTE: Do not add any more behavior to this class, as ComposablePartCatalogTests.cs + // uses this to verify default behavior of the base class. + private class DisposableComposablePartCatalog : ComposablePartCatalog + { + private readonly Action _disposeCallback; + + public DisposableComposablePartCatalog(Action disposeCallback) + { + Assert.NotNull(disposeCallback); + + _disposeCallback = disposeCallback; + } + + ~DisposableComposablePartCatalog() + { + Dispose(false); + } + + public override IQueryable Parts + { + get { throw new NotImplementedException(); } + } + + protected override void Dispose(bool disposing) + { + _disposeCallback(disposing); + + base.Dispose(disposing); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/CatalogFactory.FilteredComposablePartCatalog.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/CatalogFactory.FilteredComposablePartCatalog.cs new file mode 100644 index 0000000000..1a8069a788 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/CatalogFactory.FilteredComposablePartCatalog.cs @@ -0,0 +1,66 @@ +// 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.ComponentModel.Composition.Primitives; +using System.ComponentModel.Composition.Hosting; +using System.Linq; +using System.Linq.Expressions; + +namespace System.ComponentModel.Composition.Factories +{ + partial class CatalogFactory + { + public class FilteredCatalog : ComposablePartCatalog, INotifyComposablePartCatalogChanged + { + private readonly ComposablePartCatalog _inner; + private readonly INotifyComposablePartCatalogChanged _innerNotifyChange; + private readonly IQueryable _partsQuery; + + public FilteredCatalog(ComposablePartCatalog inner, + Func filter) + { + _inner = inner; + _innerNotifyChange = inner as INotifyComposablePartCatalogChanged; + _partsQuery = inner.Parts.Where(filter).AsQueryable(); + } + + public override IQueryable Parts + { + get + { + return _partsQuery; + } + } + + public event EventHandler Changed + { + add + { + if (_innerNotifyChange != null) + _innerNotifyChange.Changed += value; + } + remove + { + if (_innerNotifyChange != null) + _innerNotifyChange.Changed -= value; + } + } + + public event EventHandler Changing + { + add + { + if (_innerNotifyChange != null) + _innerNotifyChange.Changing += value; + } + remove + { + if (_innerNotifyChange != null) + _innerNotifyChange.Changing -= value; + } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/CatalogFactory.MutableComposablePartCatalog.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/CatalogFactory.MutableComposablePartCatalog.cs new file mode 100644 index 0000000000..a661e84040 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/CatalogFactory.MutableComposablePartCatalog.cs @@ -0,0 +1,66 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq; + +namespace System.ComponentModel.Composition.Factories +{ + partial class CatalogFactory + { + public class MutableComposablePartCatalog : ComposablePartCatalog, INotifyComposablePartCatalogChanged + { + private readonly HashSet _definitions; + + public MutableComposablePartCatalog(IEnumerable definitions) + { + _definitions = new HashSet(definitions); + } + + public void AddDefinition(ComposablePartDefinition definition) + { + OnDefinitionsChanged(definition, true); + } + + public void RemoveDefinition(ComposablePartDefinition definition) + { + OnDefinitionsChanged(definition, false); + } + + public override IQueryable Parts + { + get { return _definitions.AsQueryable(); } + } + + private void OnDefinitionsChanged(ComposablePartDefinition definition, bool added) + { + ComposablePartDefinition[] addedDefinitions = added ? new ComposablePartDefinition[] { definition } : new ComposablePartDefinition[0]; + ComposablePartDefinition[] removeDefinitions = added ? new ComposablePartDefinition[0] : new ComposablePartDefinition[] { definition }; + + var e = new ComposablePartCatalogChangeEventArgs(addedDefinitions, removeDefinitions, null); + Changing(this, e); + + if (added) + { + _definitions.Add(definition); + } + else + { + _definitions.Remove(definition); + } + + if (Changed != null) + { + Changed(this, e); + } + } + + public event EventHandler Changed; + + public event EventHandler Changing; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/CatalogFactory.NoOverridesComposablePartCatalog.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/CatalogFactory.NoOverridesComposablePartCatalog.cs new file mode 100644 index 0000000000..83686ab707 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/CatalogFactory.NoOverridesComposablePartCatalog.cs @@ -0,0 +1,26 @@ +// 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.ComponentModel.Composition.Primitives; +using System.Linq; + +namespace System.ComponentModel.Composition.Factories +{ + partial class CatalogFactory + { + // NOTE: Do not add any more behavior to this class, as ComposablePartCatalogTests.cs + // uses this to verify default behavior of the base class. + private class NoOverridesComposablePartCatalog : ComposablePartCatalog + { + public NoOverridesComposablePartCatalog() + { + } + + public override IQueryable Parts + { + get { return Enumerable.Empty().AsQueryable(); } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/CatalogFactory.NonFilteringTypeCatalog.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/CatalogFactory.NonFilteringTypeCatalog.cs new file mode 100644 index 0000000000..13343a914b --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/CatalogFactory.NonFilteringTypeCatalog.cs @@ -0,0 +1,32 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Linq; + +namespace System.ComponentModel.Composition.Factories +{ + partial class CatalogFactory + { + private class NonFilteringTypeCatalog : ComposablePartCatalog + { + private readonly List _definitions; + + public NonFilteringTypeCatalog(params Type[] types) + { + this._definitions = new List(); + foreach (Type type in types) + { + this._definitions.Add(AttributedModelServices.CreatePartDefinition(type, null)); + } + } + + public override IQueryable Parts + { + get { return this._definitions.AsQueryable(); } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/CatalogFactory.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/CatalogFactory.cs new file mode 100644 index 0000000000..a14317ce5e --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/CatalogFactory.cs @@ -0,0 +1,76 @@ +// 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.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using System.Reflection; + +namespace System.ComponentModel.Composition.Factories +{ + public static partial class CatalogFactory + { + public static AggregateCatalog CreateAggregateCatalog() + { + return new AggregateCatalog(); + } + + public static AggregateCatalog CreateAggregateCatalog(params ComposablePartCatalog[] catalogs) + { + return new AggregateCatalog(catalogs); + } + + public static ComposablePartCatalog Create() + { + return new NoOverridesComposablePartCatalog(); + } + + public static ComposablePartCatalog Create(params ComposablePart[] parts) + { + var definitions = parts.Select(part => PartDefinitionFactory.Create(part)); + + return Create(definitions.ToArray()); + } + + public static ComposablePartCatalog Create(params ComposablePartDefinition[] definitions) + { + return new DerivedComposablePartCatalog(definitions); + } + + public static ComposablePartCatalog CreateDefaultAttributed() + { + return CreateAttributed(typeof(CatalogFactory).Assembly); + } + + public static ComposablePartCatalog CreateDisposable(Action disposeCallback) + { + return new DisposableComposablePartCatalog(disposeCallback); + } + + public static ComposablePartCatalog CreateAttributed(Assembly assembly) + { + return new AssemblyCatalog(assembly); + } + + public static ComposablePartCatalog CreateAttributed(params Type[] types) + { + return new TypeCatalog(types); + } + + public static ComposablePartCatalog CreateNonFilteredAttributed(params Type[] types) + { + return new NonFilteringTypeCatalog(types); + } + + public static MutableComposablePartCatalog CreateMutable(params ComposablePartDefinition[] definitions) + { + return new MutableComposablePartCatalog(definitions); + } + + public static ComposablePartCatalog CreateFiltered(ComposablePartCatalog catalog, Func filter) + { + return new FilteredCatalog(catalog, filter); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ConstraintFactory.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ConstraintFactory.cs new file mode 100644 index 0000000000..55081787b7 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ConstraintFactory.cs @@ -0,0 +1,17 @@ +// 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.ComponentModel.Composition.Primitives; +using System.Linq.Expressions; + +namespace System.ComponentModel.Composition.Factories +{ + internal static class ConstraintFactory + { + public static Expression> Create(string contractName) + { + return definition => definition.ContractName.Equals(contractName); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ContainerFactory.DisposableCompositionContainer.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ContainerFactory.DisposableCompositionContainer.cs new file mode 100644 index 0000000000..20dd833246 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ContainerFactory.DisposableCompositionContainer.cs @@ -0,0 +1,38 @@ +// 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.ComponentModel.Composition.Hosting; +using Xunit; + +namespace System.ComponentModel.Composition.Factories +{ + partial class ContainerFactory + { + // NOTE: Do not add any more behavior to this class, as CompositionContainerTests.cs + // uses this to verify default behavior of the base class. + private class DisposableCompositionContainer : CompositionContainer + { + private readonly Action _disposeCallback; + + public DisposableCompositionContainer(Action disposeCallback) + { + Assert.NotNull(disposeCallback); + + _disposeCallback = disposeCallback; + } + + ~DisposableCompositionContainer() + { + Dispose(false); + } + + protected override void Dispose(bool disposing) + { + _disposeCallback(disposing); + + base.Dispose(disposing); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ContainerFactory.NoOverridesCompositionContainer.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ContainerFactory.NoOverridesCompositionContainer.cs new file mode 100644 index 0000000000..5d5a131116 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ContainerFactory.NoOverridesCompositionContainer.cs @@ -0,0 +1,24 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Hosting; +using System.Linq; +using Microsoft.CLR.UnitTesting; + +namespace System.ComponentModel.Composition.Factories +{ + partial class ContainerFactory + { + // NOTE: Do not add any more behavior to this class, as CompositionContainerTests.cs + // uses this to verify default behavior of the base class. + private class NoOverridesCompositionContainer : CompositionContainer + { + public NoOverridesCompositionContainer() + { + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ContainerFactory.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ContainerFactory.cs new file mode 100644 index 0000000000..22a10dd00f --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ContainerFactory.cs @@ -0,0 +1,102 @@ +// 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.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using Xunit; + +namespace System.ComponentModel.Composition.Factories +{ + internal static partial class ContainerFactory + { + public static CompositionContainer Create() + { + return Create((ComposablePart[])null); + } + + public static CompositionContainer Create(ComposablePartCatalog catalog) + { + return new CompositionContainer(catalog); + } + + public static CompositionContainer Create(CompositionContainer parent) + { + return new CompositionContainer(parent); + } + + public static CompositionContainer Create(params ComposablePart[] parts) + { + return Create((CompositionContainer)null, parts); + } + + public static CompositionContainer CreateWithDefaultAttributedCatalog() + { + var catalog = CatalogFactory.CreateDefaultAttributed(); + + return Create(catalog); + } + + public static CompositionContainer CreateWithAttributedCatalog(params Type[] types) + { + var catalog = CatalogFactory.CreateAttributed(types); + + return Create(catalog); + } + + public static CompositionContainer CreateAttributed(params object[] parts) + { + var container = new CompositionContainer(); + var partsArray = new ComposablePart[parts.Length]; + + for (int i = 0; i < parts.Length; i++) + { + Assert.IsNotType(parts[i]); + partsArray[i] = PartFactory.CreateAttributed(parts[i]); + } + + return Create(partsArray); + } + + public static CompositionContainer Create(CompositionContainer parent, params ComposablePart[] parts) + { + CompositionContainer container; + if (parent == null) + { + container = new CompositionContainer(); + } + else + { + container = new CompositionContainer(parent); + } + + if (parts != null) + { + CompositionBatch batch = new CompositionBatch(parts, Enumerable.Empty()); + container.Compose(batch); + } + + return container; + } + + public static CompositionContainer Create(params MicroExport[] exports) + { + var part = PartFactory.CreateExporter(exports); + + return Create(part); + } + + public static CompositionContainer Create(CompositionContainer parent, params MicroExport[] exports) + { + var part = PartFactory.CreateExporter(exports); + + return Create(parent, part); + } + + public static CompositionContainer CreateDisposable(Action disposeCallback) + { + return new DisposableCompositionContainer(disposeCallback); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ElementFactory.CompositionElement.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ElementFactory.CompositionElement.cs new file mode 100644 index 0000000000..8124ee7b18 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ElementFactory.CompositionElement.cs @@ -0,0 +1,33 @@ +// 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.ComponentModel.Composition.Primitives; + +namespace System.ComponentModel.Composition.Factories +{ + partial class ElementFactory + { + private class CompositionElement : ICompositionElement + { + private readonly string _displayName; + private readonly ICompositionElement _origin; + + public CompositionElement(string displayName, ICompositionElement origin) + { + _displayName = displayName; + _origin = origin; + } + + public string DisplayName + { + get { return _displayName; } + } + + public ICompositionElement Origin + { + get { return _origin; } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ElementFactory.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ElementFactory.cs new file mode 100644 index 0000000000..bca7467cfe --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ElementFactory.cs @@ -0,0 +1,43 @@ +// 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.ComponentModel.Composition.Primitives; + +namespace System.ComponentModel.Composition.Factories +{ + internal static partial class ElementFactory + { + public static ICompositionElement Create() + { + return Create("Unknown Display Name", (ICompositionElement)null); + } + + public static ICompositionElement Create(string displayName) + { + return Create(displayName, (ICompositionElement)null); + } + + public static ICompositionElement Create(ICompositionElement origin) + { + return Create("Unknown Display Name", origin); + } + + public static ICompositionElement Create(string displayName, ICompositionElement origin) + { + return new CompositionElement(displayName, origin); + } + + public static ICompositionElement CreateChain(int count) + { + ICompositionElement previousElement = null; + + for (int i = 0; i < count; i++) + { + previousElement = Create((count - i).ToString(), previousElement); + } + + return previousElement; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ErrorFactory.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ErrorFactory.cs new file mode 100644 index 0000000000..6c9b298bec --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ErrorFactory.cs @@ -0,0 +1,180 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.IO; +using System.Text; + +namespace System.ComponentModel.Composition.Factories +{ + internal static partial class ErrorFactory + { + public static CompositionError Create(ICompositionElement element) + { + return Create(CompositionErrorId.Unknown, (string)null, element, (Exception)null); + } + + public static CompositionError Create(Exception exception) + { + return Create(CompositionErrorId.Unknown, (string)null, (ICompositionElement)null, exception); + } + + public static CompositionError Create(string message) + { + return Create(CompositionErrorId.Unknown, message, (ICompositionElement)null, (Exception)null); + } + + public static CompositionError Create(string message, Exception exception) + { + return Create(CompositionErrorId.Unknown, message, (ICompositionElement)null, exception); + } + + public static CompositionError Create(CompositionErrorId errorId) + { + return Create(errorId, errorId.ToString(), (ICompositionElement)null, (Exception)null); + } + + public static CompositionError Create(CompositionErrorId errorId, string message, Exception exception) + { + return new CompositionError(errorId, message, (ICompositionElement)null, exception); + } + + public static CompositionError Create(CompositionErrorId errorId, string message, ICompositionElement element, Exception exception) + { + return new CompositionError(errorId, message, element, exception); + } + + public static IEnumerable CreateFromDsl(string format) + { + CompositionException exception = (CompositionException)CreateFromDslCore(format); + + return exception.Errors; + } + + private static Exception CreateFromDslCore(string format) + { + List> identifiers = new List>(); + + StringBuilder token = new StringBuilder(); + StringReader reader = new StringReader(format); + + while (reader.Peek() != -1) + { + char c = (char)reader.Read(); + if (c == '|') + { + AddIdentifier(identifiers, token); + continue; + } + + if (c == '(') + { + string dsl = ReadToNextMatchingParenthesis(reader); + + AddIdentifier(identifiers, token, dsl); + continue; + } + + token.Append(c); + } + + AddIdentifier(identifiers, token); + + return CreateFromList(identifiers); + } + + private static Exception CreateFromList(List> identifiers) + { + List errors = new List(); + Exception exception = null; + foreach (var identifier in identifiers) + { + Exception innerException = null; + if (identifier.Item2 != null) + { + innerException = CreateFromDslCore(identifier.Item2); + } + + if (identifier.Item1 == "Exception") + { + //Assert.IsNull(exception); + exception = new Exception(identifier.Item1, innerException); + } + else + { + //Assert.AreEqual("Error", identifier.Item1); + + errors.Add(Create(identifier.Item1, innerException)); + } + } + + if (errors.Count == 0) + { + return exception; + } + + return new CompositionException("", exception, errors); + } + + private static void AddIdentifier(List> identifiers, StringBuilder identifier) + { + AddIdentifier(identifiers, identifier, (string)null); + } + + private static void AddIdentifier(List> identifiers, StringBuilder identifier, string dsl) + { + if (identifier.Length == 0) + return; + + identifiers.Add(new Tuple(identifier.ToString(), dsl)); + + identifier.Length = 0; + } + + private static IEnumerable CreateFromList(List errors) + { + foreach (string value in errors) + { + yield return Create(value); + } + } + + private static string ReadToNextMatchingParenthesis(StringReader reader) + { + Stack parenthesis = new Stack(); + parenthesis.Push('('); + + StringBuilder builder = new StringBuilder(); + while (reader.Peek() != -1) + { + char c = (char)reader.Read(); + if (c == '(') + { + parenthesis.Push(c); + + if (parenthesis.Count == 1) + { + continue; + } + } + + if (c == ')') + { + char pop = parenthesis.Pop(); + //Assert.AreEqual('(', pop); + + if (parenthesis.Count == 0) + { + break; + } + } + + builder.Append(c); + } + + return builder.ToString(); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ExportDefinitionFactory.DerivedExportDefinition.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ExportDefinitionFactory.DerivedExportDefinition.cs new file mode 100644 index 0000000000..9454e6d692 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ExportDefinitionFactory.DerivedExportDefinition.cs @@ -0,0 +1,44 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; + +namespace System.ComponentModel.Composition.Factories +{ + partial class ExportDefinitionFactory + { + private class DerivedExportDefinition : ExportDefinition, ICompositionElement + { + private readonly string _contractName; + private readonly IDictionary _metadata; + + public DerivedExportDefinition(string contractName, IDictionary metadata) + { + _contractName = contractName; + _metadata = metadata ?? new Dictionary(); + } + + public override string ContractName + { + get { return _contractName; } + } + + public override IDictionary Metadata + { + get { return _metadata; } + } + + public string DisplayName + { + get { return base.ToString(); } + } + + public ICompositionElement Origin + { + get { return null; } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ExportDefinitionFactory.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ExportDefinitionFactory.cs new file mode 100644 index 0000000000..5c55491dbd --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ExportDefinitionFactory.cs @@ -0,0 +1,29 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; + +namespace System.ComponentModel.Composition.Factories +{ + // This class deliberately does not create instances of ExportDefinition, + // so as to test other derived classes from ImportDefinition. + internal static partial class ExportDefinitionFactory + { + public static ExportDefinition Create() + { + return Create((string)null, (IDictionary)null); + } + + public static ExportDefinition Create(string contractName) + { + return Create(contractName, (IDictionary)null); + } + + public static ExportDefinition Create(string contractName, IDictionary metadata) + { + return new DerivedExportDefinition(contractName, metadata); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ExportFactory.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ExportFactory.cs new file mode 100644 index 0000000000..5cd8ffe0f9 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ExportFactory.cs @@ -0,0 +1,66 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; + +namespace System.ComponentModel.Composition.Factories +{ + // This class deliberately does not create instances of Lazy, + // so as to test other derived classes from Lazy. + internal static partial class ExportFactory + { + public static IEnumerable Create(string contractName, int count) + { + Export[] exports = new Export[count]; + + for (int i = 0; i < count; i++) + { + exports[i] = Create(contractName, (object)null); + } + + return exports; + } + + public static Lazy Create(T value) + { + return new Lazy(() => value, false); + } + + public static Lazy> Create(T value, IDictionary metadata) + { + return Create>(() => value, metadata); + } + + public static Export Create(string contractName, Func exportedValueGetter) + { + return Create(contractName,(IDictionary)null, exportedValueGetter); + } + + public static Export Create(string contractName) + { + return Create(contractName, null, (IDictionary)null); + } + + public static Export Create(string contractName, object value) + { + return Create(contractName, value, (IDictionary)null); + } + + public static Export Create(string contractName, object value, IDictionary metadata) + { + return Create(contractName, metadata, () => value); + } + + public static Export Create(string contractName, IDictionary metadata, Func exportedValueGetter) + { + return new Export(ExportDefinitionFactory.Create(contractName, metadata), exportedValueGetter); + } + + private static Lazy Create(Func exportedValueGetter, IDictionary metadata) + { + return new Lazy(exportedValueGetter, AttributedModelServices.GetMetadataView(metadata), false); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ExportProviderFactory.NoOverridesExportProvider.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ExportProviderFactory.NoOverridesExportProvider.cs new file mode 100644 index 0000000000..6a82bf61d9 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ExportProviderFactory.NoOverridesExportProvider.cs @@ -0,0 +1,28 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; + +namespace System.ComponentModel.Composition.Factories +{ + partial class ExportProviderFactory + { + // NOTE: Do not add any more behavior to this class, as ExportProviderTests.cs + // uses this to verify default behavior of the base class. + private class NoOverridesExportProvider : ExportProvider + { + public NoOverridesExportProvider() + { + } + + protected override IEnumerable GetExportsCore(ImportDefinition definition, AtomicComposition context) + { + throw new NotImplementedException(); + } + } + } + +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ExportProviderFactory.RecomposableExportProvider.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ExportProviderFactory.RecomposableExportProvider.cs new file mode 100644 index 0000000000..447df44ff0 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ExportProviderFactory.RecomposableExportProvider.cs @@ -0,0 +1,112 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using Xunit; + +namespace System.ComponentModel.Composition.Factories +{ + partial class ExportProviderFactory + { + public class RecomposableExportProvider : ExportProvider + { + public static Dictionary EmptyMetadataDictionary = new Dictionary(); + public List _exports = new List(); + + public void AddExport(string contractName, object value) + { + Export export = CreateExport(contractName, value); + var exports = this._exports.ToList(); + + exports.Add(export); + ChangeExports(exports); + } + + public void RemoveExport(string contractName) + { + int index = FindExport(contractName); + Assert.True(index >= 0); + + var exports = this._exports.ToList(); + + exports.RemoveAt(index); + + ChangeExports(exports); + } + + public void ReplaceExportValue(string contractName, object newValue) + { + int index = FindExport(contractName); + Assert.True(index >= 0); + + var exports = this._exports.ToList(); + + exports.RemoveAt(index); + exports.Add(CreateExport(contractName, newValue)); + + ChangeExports(exports); + } + + private void ChangeExports(List newExports) + { + using (var atomicComposition = new AtomicComposition()) + { + atomicComposition.AddCompleteAction(() => this._exports = newExports); + atomicComposition.SetValue(this, newExports); + + var addedExports = newExports.Except(this._exports).Select(export => export.Definition); + var removedExports = this._exports.Except(newExports).Select(export => export.Definition); + + this.OnExportsChanging(new ExportsChangeEventArgs(addedExports, removedExports, atomicComposition)); + + atomicComposition.AddCompleteAction(() => this.OnExportsChanged( + new ExportsChangeEventArgs(addedExports, removedExports, null))); + + atomicComposition.Complete(); + } + } + + private int FindExport(string contractName) + { + for (int i = 0; i < _exports.Count; i++) + { + if (_exports[i].Definition.ContractName == contractName) + { + return i; + } + } + return -1; + } + + private Export CreateExport(string contractName, object value) + { + return new Export(new ExportDefinition(contractName, EmptyMetadataDictionary), () => value); + } + + protected override IEnumerable GetExportsCore(ImportDefinition importDefinition, AtomicComposition context) + { + IEnumerable contextExports; + + if (context == null || !context.TryGetValue(this, out contextExports)) + { + contextExports = this._exports; + } + + List exports = new List(); + var func = importDefinition.Constraint.Compile(); + foreach (Export export in contextExports) + { + if (func(export.Definition)) + { + exports.Add(export); + } + } + return exports; + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ExportProviderFactory.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ExportProviderFactory.cs new file mode 100644 index 0000000000..1251dd75ed --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ExportProviderFactory.cs @@ -0,0 +1,21 @@ +// 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.ComponentModel.Composition.Hosting; + +namespace System.ComponentModel.Composition.Factories +{ + internal partial class ExportProviderFactory + { + public static ExportProvider Create() + { + return new NoOverridesExportProvider(); + } + + public static RecomposableExportProvider CreateRecomposable() + { + return new RecomposableExportProvider(); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ImportDefinitionFactory.DerivedContractBasedImportDefinition.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ImportDefinitionFactory.DerivedContractBasedImportDefinition.cs new file mode 100644 index 0000000000..cdd1ef1dbc --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ImportDefinitionFactory.DerivedContractBasedImportDefinition.cs @@ -0,0 +1,55 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; + +namespace System.ComponentModel.Composition.Factories +{ + partial class ImportDefinitionFactory + { + private class DerivedContractBasedImportDefinition : ContractBasedImportDefinition + { + private readonly string _contractName; + private readonly ImportCardinality _cardinality; + private readonly bool _isRecomposable; + private readonly bool _isPrerequisite; + private readonly IEnumerable> _requiredMetadata; + + public DerivedContractBasedImportDefinition(string contractName, IEnumerable> requiredMetadata, ImportCardinality cardinality, bool isRecomposable, bool isPrerequisite) + { + _contractName = contractName; + _cardinality = cardinality; + _isRecomposable = isRecomposable; + _isPrerequisite = isPrerequisite; + _requiredMetadata = requiredMetadata; + } + + public override IEnumerable> RequiredMetadata + { + get { return _requiredMetadata ?? base.RequiredMetadata; } + } + + public override ImportCardinality Cardinality + { + get { return _cardinality; } + } + + public override bool IsPrerequisite + { + get { return _isPrerequisite; } + } + + public override bool IsRecomposable + { + get { return _isRecomposable; } + } + + public override string ContractName + { + get { return _contractName; } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ImportDefinitionFactory.DerivedImportDefinition.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ImportDefinitionFactory.DerivedImportDefinition.cs new file mode 100644 index 0000000000..e11806ac75 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ImportDefinitionFactory.DerivedImportDefinition.cs @@ -0,0 +1,48 @@ +// 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.ComponentModel.Composition.Primitives; +using System.Linq.Expressions; + +namespace System.ComponentModel.Composition.Factories +{ + partial class ImportDefinitionFactory + { + private class DerivedImportDefinition : ImportDefinition + { + private readonly Expression> _constraint; + private readonly ImportCardinality _cardinality; + private readonly bool _isRecomposable; + private readonly bool _isPrerequisite; + + public DerivedImportDefinition(Expression> constraint, ImportCardinality cardinality, bool isRecomposable, bool isPrerequisite) + { + _constraint = constraint ?? (export => false); + _cardinality = cardinality; + _isRecomposable = isRecomposable; + _isPrerequisite = isPrerequisite; + } + + public override ImportCardinality Cardinality + { + get { return _cardinality; } + } + + public override bool IsPrerequisite + { + get { return _isPrerequisite; } + } + + public override bool IsRecomposable + { + get { return _isRecomposable; } + } + + public override Expression> Constraint + { + get { return _constraint; } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ImportDefinitionFactory.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ImportDefinitionFactory.cs new file mode 100644 index 0000000000..920b0a7d96 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ImportDefinitionFactory.cs @@ -0,0 +1,80 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Linq.Expressions; + +namespace System.ComponentModel.Composition.Factories +{ + // This class deliberately does not create instances of ImportDefinition, + // so as to test other derived classes from ImportDefinition. + internal static partial class ImportDefinitionFactory + { + public static ImportDefinition Create(Type contractType, ImportCardinality cardinality) + { + return Create(AttributedModelServices.GetContractName(contractType), cardinality); + } + + public static ImportDefinition Create(string contractName) + { + return Create(contractName, (IEnumerable>)null, ImportCardinality.ExactlyOne, true, false); + } + + public static ImportDefinition Create(string contractName, IEnumerable> requiredMetadata) + { + return Create(contractName, requiredMetadata, ImportCardinality.ExactlyOne, true, false); + } + + public static ImportDefinition Create(string contractName, ImportCardinality cardinality, bool isRecomposable, bool isPrerequisite) + { + return Create(contractName, (IEnumerable>)null, cardinality, isRecomposable, isPrerequisite); + } + + public static ImportDefinition Create(string contractName, ImportCardinality cardinality) + { + return Create(contractName, (IEnumerable>)null, cardinality, false, false); + } + + public static ImportDefinition Create(string contractName, bool isRecomposable) + { + return Create(contractName, (IEnumerable>)null, ImportCardinality.ExactlyOne, isRecomposable, false); + } + + public static ImportDefinition Create(string contractName, bool isRecomposable, bool isPrerequisite) + { + return Create(contractName, (IEnumerable>)null, ImportCardinality.ExactlyOne, isRecomposable, isPrerequisite); + } + + public static ImportDefinition Create(string contractName, IEnumerable> requiredMetadata, ImportCardinality cardinality, bool isRecomposable, bool isPrerequisite) + { + return new DerivedContractBasedImportDefinition(contractName, requiredMetadata, cardinality, isRecomposable, isPrerequisite); + } + + public static ImportDefinition Create() + { + return Create((Expression>)null); + } + + public static ImportDefinition Create(Expression> constraint) + { + return Create(constraint, ImportCardinality.ExactlyOne, true, false); + } + + public static ImportDefinition Create(Expression> constraint, ImportCardinality cardinality) + { + return Create(constraint, cardinality, true, false); + } + + public static ImportDefinition Create(Expression> constraint, ImportCardinality cardinality, bool isRecomposable, bool isPrerequisite) + { + return new DerivedImportDefinition(constraint, cardinality, isRecomposable, isPrerequisite); + } + + public static ImportDefinition CreateDefault(string contractName, ImportCardinality cardinality, bool isRecomposable, bool isPrerequisite) + { + return new ContractBasedImportDefinition(contractName, (string)null, (IEnumerable>)null, cardinality, isRecomposable, isPrerequisite, CreationPolicy.Any); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/PartDefinitionFactory.DerivedComposablePartDefinition.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/PartDefinitionFactory.DerivedComposablePartDefinition.cs new file mode 100644 index 0000000000..5618e4d057 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/PartDefinitionFactory.DerivedComposablePartDefinition.cs @@ -0,0 +1,69 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Linq; + +namespace System.ComponentModel.Composition.Factories +{ + partial class PartDefinitionFactory + { + private class DerivedComposablePartDefinition : ComposablePartDefinition + { + private readonly Func _partCreator; + private readonly IDictionary _metadata; + private IEnumerable _importDefinitions; + private IEnumerable _exportDefinitions; + private readonly Func> _importsCreator; + private readonly Func> _exportsCreator; + + public DerivedComposablePartDefinition( + IDictionary metadata, + Func partCreator, + Func> importsCreator, + Func> exportsCreator) + { + this._metadata = metadata.AsReadOnly(); + this._partCreator = partCreator; + this._importsCreator = importsCreator; + this._exportsCreator = exportsCreator; + } + + public override IDictionary Metadata + { + get { return this._metadata; } + } + + public override IEnumerable ExportDefinitions + { + get + { + if (this._exportDefinitions == null) + { + this._exportDefinitions = this._exportsCreator.Invoke() ?? Enumerable.Empty(); + } + return this._exportDefinitions; + } + } + + public override IEnumerable ImportDefinitions + { + get + { + if (this._importDefinitions == null) + { + this._importDefinitions = this._importsCreator.Invoke() ?? Enumerable.Empty(); + } + return this._importDefinitions; + } + } + + public override ComposablePart CreatePart() + { + return this._partCreator(); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/PartDefinitionFactory.NoOverridesComposablePartDefinition.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/PartDefinitionFactory.NoOverridesComposablePartDefinition.cs new file mode 100644 index 0000000000..278e8eb7e7 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/PartDefinitionFactory.NoOverridesComposablePartDefinition.cs @@ -0,0 +1,34 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using Xunit; + +namespace System.ComponentModel.Composition.Factories +{ + partial class PartDefinitionFactory + { + // NOTE: Do not add any more behavior to this class, as ComposablePartDefinitionTests.cs + // uses this to verify default behavior of the base class. + private class NoOverridesComposablePartDefinition : ComposablePartDefinition + { + public override IEnumerable ExportDefinitions + { + get { return Enumerable.Empty(); } + } + + public override IEnumerable ImportDefinitions + { + get { return Enumerable.Empty(); } + } + + public override ComposablePart CreatePart() + { + throw new NotImplementedException(); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/PartDefinitionFactory.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/PartDefinitionFactory.cs new file mode 100644 index 0000000000..ff14927aa0 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/PartDefinitionFactory.cs @@ -0,0 +1,55 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.AttributedModel; +using System.ComponentModel.Composition.Primitives; +using System.ComponentModel.Composition.ReflectionModel; + +namespace System.ComponentModel.Composition.Factories +{ + internal static partial class PartDefinitionFactory + { + public static ComposablePartDefinition AsPart(this Type type) + { + return CreateAttributed(type); + } + + public static ReflectionComposablePartDefinition CreateAttributed() + { + return CreateAttributed(typeof(ComposablePart)); + } + + public static ReflectionComposablePartDefinition CreateAttributed(Type type) + { + return AttributedModelDiscovery.CreatePartDefinition(type, null, false, (ICompositionElement)null); + } + + public static ComposablePartDefinition Create() + { + return new NoOverridesComposablePartDefinition(); + } + + public static ComposablePartDefinition Create(ComposablePart part) + { + return Create(part.Metadata, () => part, part.ImportDefinitions, part.ExportDefinitions); + } + + public static ComposablePartDefinition Create(IDictionary metadata, + Func partCreator, + IEnumerable imports, + IEnumerable exports) + { + return Create(metadata, partCreator, () => imports, () => exports); + } + + public static ComposablePartDefinition Create(IDictionary metadata, + Func partCreator, + Func> importsCreator, + Func> exportsCreator) + { + return new DerivedComposablePartDefinition(metadata, partCreator, importsCreator, exportsCreator); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/PartFactory.DisposableComposablePart.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/PartFactory.DisposableComposablePart.cs new file mode 100644 index 0000000000..e0dcfd763a --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/PartFactory.DisposableComposablePart.cs @@ -0,0 +1,68 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using Xunit; + +namespace System.ComponentModel.Composition.Factories +{ + partial class PartFactory + { + // NOTE: Do not add any more behavior to this class, as ComposablePartTests.cs + // uses this to verify default behavior of the base class. + private class DisposableComposablePart : ComposablePart, IDisposable + { + private readonly Action _disposeCallback; + + public DisposableComposablePart(Action disposeCallback) + { + Assert.NotNull(disposeCallback); + + _disposeCallback = disposeCallback; + } + + public void Dispose() + { + this.Dispose(true); + } + + ~DisposableComposablePart() + { + Dispose(false); + } + + protected virtual void Dispose(bool disposing) + { + _disposeCallback(disposing); + } + + public override IEnumerable ImportDefinitions + { + get { return Enumerable.Empty(); } + } + + public override IEnumerable ExportDefinitions + { + get { return Enumerable.Empty(); } + } + + public override object GetExportedValue(ExportDefinition definition) + { + throw new NotImplementedException(); + } + + public override void SetImport(ImportDefinition definition, IEnumerable exports) + { + throw new NotImplementedException(); + } + + public override IDictionary Metadata + { + get { return new Dictionary(); } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/PartFactory.NoOverridesComposablePart.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/PartFactory.NoOverridesComposablePart.cs new file mode 100644 index 0000000000..959d7b691a --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/PartFactory.NoOverridesComposablePart.cs @@ -0,0 +1,43 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using Xunit; + +namespace System.ComponentModel.Composition.Factories +{ + partial class PartFactory + { + // NOTE: Do not add any more behavior to this class, as ComposablePartTests.cs + // uses this to verify default behavior of the base class. + private class NoOverridesComposablePart : ComposablePart + { + public NoOverridesComposablePart() + { + } + + public override IEnumerable ExportDefinitions + { + get { return Enumerable.Empty(); } + } + + public override IEnumerable ImportDefinitions + { + get { return Enumerable.Empty(); } + } + + public override object GetExportedValue(ExportDefinition definition) + { + throw new NotImplementedException(); + } + + public override void SetImport(ImportDefinition definition, IEnumerable exports) + { + throw new NotImplementedException(); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/PartFactory.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/PartFactory.cs new file mode 100644 index 0000000000..611e6995e3 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/PartFactory.cs @@ -0,0 +1,108 @@ +// 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.ComponentModel.Composition.Primitives; + +namespace System.ComponentModel.Composition.Factories +{ + internal static partial class PartFactory + { + public static Type GetAttributedExporterType() + { + return typeof(ExportValueTypeFactory); + } + + public static ComposablePart CreateAttributed(Type type) + { + var definition = PartDefinitionFactory.CreateAttributed(type); + + return definition.CreatePart(); + } + + public static ComposablePart CreateAttributed(object instance) + { + return AttributedModelServices.CreatePart(instance); + } + + public static ComposablePart Create() + { + return new NoOverridesComposablePart(); + } + + public static ComposablePart CreateExporter(string contractName, object value) + { + return CreateExporter(new MicroExport(contractName, value)); + } + + public static ComposablePart CreateExporter(params MicroExport[] exports) + { + ConcreteComposablePart part = new ConcreteComposablePart(); + + if (exports != null) + { + foreach (var export in exports) + { + foreach (object exportedValue in export.ExportedValues) + { + part.AddExport(ExportFactory.Create(export.ContractName, exportedValue, export.Metadata)); + } + } + } + + return part; + } + + public static ImportingComposablePart CreateImporter() + { + string contractName = AttributedModelServices.GetContractName(typeof(T)); + + return CreateImporter(contractName); + } + + public static ImportingComposablePart CreateImporterExporter(string exportContractName, string importContractName) + { + return new ImportingComposablePart(exportContractName, ImportCardinality.ExactlyOne, false, importContractName); + } + + public static ImportingComposablePart CreateImporter(params string[] contractNames) + { + return new ImportingComposablePart(ImportCardinality.ZeroOrMore, false, contractNames); + } + + public static ImportingComposablePart CreateImporter(bool isRecomposable, params string[] contractNames) + { + return new ImportingComposablePart(ImportCardinality.ZeroOrMore, isRecomposable, contractNames); + } + + public static ImportingComposablePart CreateImporter(string contractName) + { + return CreateImporter(contractName, ImportCardinality.ZeroOrMore); + } + + public static ImportingComposablePart CreateImporter(string contractName, bool isRecomposable) + { + return CreateImporter(contractName, ImportCardinality.ZeroOrMore, isRecomposable); + } + + public static ImportingComposablePart CreateImporter(string contractName, ImportCardinality cardinality) + { + return CreateImporter(contractName, cardinality, false); + } + + public static ImportingComposablePart CreateImporter(string contractName, ImportCardinality cardinality, bool isRecomposable) + { + return new ImportingComposablePart(cardinality, isRecomposable, contractName); + } + + public static ImportingComposablePart CreateImporter(params ImportDefinition[] importDefinitions) + { + return new ImportingComposablePart(importDefinitions); + } + + public static ComposablePart CreateDisposable(Action disposeCallback) + { + return new DisposableComposablePart(disposeCallback); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ReflectionFactory.MockParameterInfo.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ReflectionFactory.MockParameterInfo.cs new file mode 100644 index 0000000000..19979d3490 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ReflectionFactory.MockParameterInfo.cs @@ -0,0 +1,48 @@ +// 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.Linq; +using System.Reflection; + +namespace System.ComponentModel.Composition.Factories +{ + partial class ReflectionFactory + { + private class MockParameterInfo : ParameterInfo + { + private readonly string _name; + private readonly Type _parameterType = typeof(string); + + public MockParameterInfo(Type parameterType) + { + _parameterType = parameterType; + } + + public MockParameterInfo(string name) + { + _name = name; + } + + public override string Name + { + get { return _name; } + } + + public override Type ParameterType + { + get { return _parameterType; } + } + + public override MemberInfo Member + { + get { return typeof(object).GetConstructors().First(); } + } + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + return (object[])Array.CreateInstance(attributeType, 0); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ReflectionFactory.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ReflectionFactory.cs new file mode 100644 index 0000000000..f968e460ad --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Factories/ReflectionFactory.cs @@ -0,0 +1,26 @@ +// 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.Reflection; + +namespace System.ComponentModel.Composition.Factories +{ + internal static partial class ReflectionFactory + { + public static ParameterInfo CreateParameter() + { + return CreateParameter((string)null); + } + + public static ParameterInfo CreateParameter(Type parameterType) + { + return new MockParameterInfo(parameterType); + } + + public static ParameterInfo CreateParameter(string name) + { + return new MockParameterInfo(name); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/GenerationServicesTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/GenerationServicesTests.cs new file mode 100644 index 0000000000..b6146e62a2 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/GenerationServicesTests.cs @@ -0,0 +1,167 @@ +// 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.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection.Emit; +using System.UnitTesting; +using Xunit; + +namespace Microsoft.Internal +{ + public class GenerationServicesTests + { + // Has to be public, otherwise the dynamic method doesn't see it + public enum TestEnum + { + First = 1, + Second = 2 + } + + public static class DelegateTestClass + { + public static int Method(int i) + { + return i; + } + } + + private Func CreateValueGenerator(T value) + { + DynamicMethod methodBuilder = new DynamicMethod(TestServices.GenerateRandomString(), typeof(T), Type.EmptyTypes); + // Generate the method body that simply returns the dictionary + ILGenerator ilGenerator = methodBuilder.GetILGenerator(); + GenerationServices.LoadValue(ilGenerator, value); + ilGenerator.Emit(OpCodes.Ret); + return (Func)methodBuilder.CreateDelegate(typeof(Func)); + } + + private void TestSuccessfulValueGeneration(T value) + { + Func result = this.CreateValueGenerator(value); + T generatedValue = result.Invoke(); + Assert.Equal(value, generatedValue); + } + + private void TestSuccessfulDictionaryGeneration(IDictionary dictionary) + { + Func> result = this.CreateValueGenerator>(dictionary); + IDictionary generatedDictionary = result.Invoke(); + Assert.Equal(dictionary, generatedDictionary); + } + + private void TestSuccessfulEnumerableGeneration(IEnumerable enumerable) + { + Func result = this.CreateValueGenerator(enumerable); + IEnumerable generatedEnumerable = result.Invoke(); + Assert.True(generatedEnumerable.Cast().SequenceEqual(enumerable.Cast())); + } + + [Fact] + public void PrimitiveTypes() + { + this.TestSuccessfulValueGeneration(Char.MinValue); + this.TestSuccessfulValueGeneration(Char.MaxValue); + this.TestSuccessfulValueGeneration((Char)42); + + this.TestSuccessfulValueGeneration(true); + this.TestSuccessfulValueGeneration(false); + + this.TestSuccessfulValueGeneration(Byte.MinValue); + this.TestSuccessfulValueGeneration(Byte.MaxValue); + this.TestSuccessfulValueGeneration((Byte)42); + + this.TestSuccessfulValueGeneration(SByte.MinValue); + this.TestSuccessfulValueGeneration(SByte.MaxValue); + this.TestSuccessfulValueGeneration((SByte)42); + + this.TestSuccessfulValueGeneration(Int16.MinValue); + this.TestSuccessfulValueGeneration(Int16.MaxValue); + this.TestSuccessfulValueGeneration((Int16)42); + + this.TestSuccessfulValueGeneration(UInt16.MinValue); + this.TestSuccessfulValueGeneration(UInt16.MaxValue); + this.TestSuccessfulValueGeneration((UInt16)42); + + this.TestSuccessfulValueGeneration(Int32.MinValue); + this.TestSuccessfulValueGeneration(Int32.MaxValue); + this.TestSuccessfulValueGeneration((Int32)42); + + this.TestSuccessfulValueGeneration(UInt32.MinValue); + this.TestSuccessfulValueGeneration(UInt32.MaxValue); + this.TestSuccessfulValueGeneration((UInt32)42); + + this.TestSuccessfulValueGeneration(Int64.MinValue); + this.TestSuccessfulValueGeneration(Int64.MaxValue); + this.TestSuccessfulValueGeneration((Int64)42); + + this.TestSuccessfulValueGeneration(UInt64.MinValue); + this.TestSuccessfulValueGeneration(UInt64.MaxValue); + this.TestSuccessfulValueGeneration((UInt64)42); + + this.TestSuccessfulValueGeneration(Single.MinValue); + this.TestSuccessfulValueGeneration(Single.MaxValue); + this.TestSuccessfulValueGeneration((Single)42.42); + + this.TestSuccessfulValueGeneration(Double.MinValue); + this.TestSuccessfulValueGeneration(Double.MaxValue); + this.TestSuccessfulValueGeneration((Double)42.42); + } + + [Fact] + public void StringType() + { + this.TestSuccessfulValueGeneration("42"); + } + + [Fact] + public void EnumType() + { + this.TestSuccessfulValueGeneration(TestEnum.Second); + } + + [Fact] + public void TypeType() + { + this.TestSuccessfulValueGeneration(typeof(TestEnum)); + } + + [Fact] + public void PrimitiveTypeEnumerable() + { + int[] enumerable = new int[] { 1, 2, 3, 4, 5 }; + this.TestSuccessfulEnumerableGeneration(enumerable); + } + + [Fact] + public void StringTypeEnumerable() + { + string[] enumerable = new string[] { "1", "2", "3", "4", "5" }; + this.TestSuccessfulEnumerableGeneration(enumerable); + } + + [Fact] + [ActiveIssue(507696)] + public void EnumTypeEnumerable() + { + TestEnum[] enumerable = new TestEnum[] { TestEnum.First, TestEnum.Second }; + this.TestSuccessfulEnumerableGeneration(enumerable); + } + + [Fact] + public void MixedEnumerable() + { + List list = new List(); + list.Add(42); + list.Add("42"); + list.Add(typeof(TestEnum)); + list.Add(TestEnum.Second); + list.Add(null); + + this.TestSuccessfulEnumerableGeneration(list); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/GenericsTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/GenericsTests.cs new file mode 100644 index 0000000000..0eb4967277 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/GenericsTests.cs @@ -0,0 +1,802 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Hosting; +using System.Linq; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class GenericsTests + { + public class Bar + { + } + + public class Bar2 : Bar + { + } + + public struct FooStruct + { } + + public interface IFoo { } + public interface IFoo2 : IFoo { } + public interface IBar { } + public interface IExport { } + public class ExportImpl : IExport { } + + public interface IExport { } + public interface IImport { } + public interface IImport { } + + [Export(typeof(IFoo))] + public class Foo : IFoo + { + } + + public interface IPartWithImport + { + object GetValue(); + } + + [Export(typeof(IImport<,>))] + public class SelfImport : IImport + { + + } + + [Export(typeof(IImport<>))] + public class SelfImport : IImport + { + + } + + [Export(typeof(IExport<,>))] + public class SelfExport : IExport + { + + } + + public class PropertyExport : IExport + { + [Export(typeof(IExport<,>))] + IExport Property { get { return this; } } + } + + public class PropertyExportWithContractInferred : IExport + { + [Export] + IExport PropertyExport { get { return this; } } + } + + [Export(typeof(IExport<,>))] + public class SelfExportWithPropertyImport : IExport, IPartWithImport + { + [Import(typeof(IImport<,>))] + IImport Value { get; set; } + + public object GetValue() + { + return this.Value; + } + } + + public class PropertyExportWithChangedParameterOrder + { + public PropertyExportWithChangedParameterOrder() + { + this.Export = new ExportImpl(); + } + + [Export] + public IExport Export { get; set; } + } + + [Export(typeof(IExport<,>))] + public class SelfExportWithLazyPropertyImport : IExport, IPartWithImport + { + [Import] + Lazy> Value { get; set; } + + public object GetValue() + { + return this.Value; + } + } + + [Export(typeof(IExport<>))] + public class SelfExportWithNakedLazyPropertyImport : IExport, IPartWithImport + { + [Import] + Lazy Value { get; set; } + + public object GetValue() + { + return this.Value; + } + } + + [Export(typeof(IExport<,>))] + public class SelfExportWithExportFactoryPropertyImport : IExport, IPartWithImport + { + [Import] + ExportFactory> Value { get; set; } + + public object GetValue() + { + return this.Value; + } + } + + [Export(typeof(IExport<>))] + public class SelfExportWithNakedExportFactoryPropertyImport : IExport, IPartWithImport + { + [Import] + ExportFactory Value { get; set; } + + public object GetValue() + { + return this.Value; + } + } + + [Export(typeof(IExport<,>))] + public class SelfExportWithExportFactoryParameterImport : IExport, IPartWithImport + { + [ImportingConstructor] + SelfExportWithExportFactoryParameterImport(ExportFactory> value) + { + this.Value = value; + } + + private ExportFactory> Value { get; set; } + + public object GetValue() + { + return this.Value; + } + } + + [Export(typeof(IExport<,>))] + public class SelfExportWithCollectionPropertyImport : IExport, IPartWithImport + { + [ImportMany] + IEnumerable> Value { get; set; } + + public object GetValue() + { + return this.Value; + } + } + + [Export(typeof(IExport<,>))] + public class SelfExportWithLazyCollectionPropertyImport : IExport, IPartWithImport + { + [ImportMany] + IEnumerable>> Value { get; set; } + + public object GetValue() + { + return this.Value; + } + } + + [Export(typeof(IExport<,>))] + public class SelfExportWithParameterImport : IExport, IPartWithImport + { + [ImportingConstructor] + SelfExportWithParameterImport(IImport import) + { + this.Value = import; + } + + IImport Value { get; set; } + + public object GetValue() + { + return this.Value; + } + } + + [Export(typeof(IExport<,>))] + public class SelfExportWithPropertyImportWithContractInferred : IExport, IPartWithImport + { + [Import] + IImport Value { get; set; } + + public object GetValue() + { + return this.Value; + } + } + + [Export(typeof(IExport<,>))] + public class SelfExportWithMultipleGenericImports : IExport + { + [Import] + public IImport Import1 { get; set; } + + [Import] + public IImport Import2 { get; set; } + + [Import] + public IImport Import3 { get; set; } + + [Import] + public IImport Import4 { get; set; } + + [Import] + public IFoo Import5 { get; set; } + + [Import] + public T1 Import6 { get; set; } + + } + + [Export(typeof(IExport))] + public class ExportFooBar : IExport + { + } + + public static class SingletonExportExportCount + { + public static int Count { get; set; } + } + + [Export(typeof(IExport<,>))] + public class SingletonExport : IExport + { + public SingletonExport() + { + SingletonExportExportCount.Count++; + } + } + + public class SingletonImport + { + [Import] + public IExport Import { get; set; } + } + + [Export(typeof(IExport<>))] + public class PartWithTypeConstraint : IExport where T : IFoo + { + } + + [Export(typeof(IExport<>))] + public class PartWithBaseTypeConstraint : IExport where T : Bar + { + } + + [Export(typeof(IExport<>))] + public class PartWithRefTypeConstraint : IExport where T : class + { + } + + [Export(typeof(IExport<>))] + public class PartWithStructTypeConstraint : IExport where T : struct + { + } + + [Export(typeof(IExport<>))] + public class PartWithNewableTypeConstraint : IExport where T : new() + { + } + + [Export(typeof(IExport<,>))] + public class PartWithGenericConstraint : IExport where T2 : IDictionary + { + } + + [Export(typeof(IExport<,>))] + public class PartWithNakedConstraint : IExport where T2 : T1 + { + } + + [Export(typeof(IExport<>))] + public class OpenGenericPartWithClosedGenericImport : IExport + { + [Import] + public IImport ClosedImport { get; set; } + } + + [Fact] + public void SelfExportWithClosedGenericImportTest() + { + TypeCatalog catalog = new TypeCatalog(typeof(SelfImport<>), typeof(OpenGenericPartWithClosedGenericImport<>)); + CompositionContainer container = new CompositionContainer(catalog); + + var export = container.GetExportedValueOrDefault>(); + Assert.NotNull(export); + + OpenGenericPartWithClosedGenericImport impl = export as OpenGenericPartWithClosedGenericImport; + Assert.NotNull(impl); + Assert.NotNull(impl.ClosedImport); + } + + [Fact] + public void SelfExportTest() + { + TypeCatalog catalog = new TypeCatalog(typeof(SelfExport<,>)); + CompositionContainer container = new CompositionContainer(catalog); + + var export = container.GetExportedValueOrDefault>(); + Assert.NotNull(export); + + var export2 = container.GetExportedValueOrDefault>(); + Assert.NotNull(export2); + } + + [Fact] + public void PropertyExportTest() + { + TypeCatalog catalog = new TypeCatalog(typeof(PropertyExport<,>)); + CompositionContainer container = new CompositionContainer(catalog); + + var export = container.GetExportedValueOrDefault>(); + Assert.NotNull(export); + + var export2 = container.GetExportedValueOrDefault>(); + Assert.NotNull(export2); + } + + [Fact] + public void PropertyExportWithContractInferredTest() + { + TypeCatalog catalog = new TypeCatalog(typeof(PropertyExportWithContractInferred<,>)); + CompositionContainer container = new CompositionContainer(catalog); + + var export = container.GetExportedValueOrDefault>(); + Assert.NotNull(export); + + var export2 = container.GetExportedValueOrDefault>(); + Assert.NotNull(export2); + } + + [Fact] + public void SelfExportWithPropertyImportTest() + { + TypeCatalog catalog = new TypeCatalog(typeof(SelfExportWithPropertyImport<,>), typeof(SelfImport<,>)); + CompositionContainer container = new CompositionContainer(catalog); + + var export = container.GetExportedValueOrDefault>(); + Assert.NotNull(export); + + var partWithImport = export as IPartWithImport; + Assert.NotNull(partWithImport); + Assert.NotNull(partWithImport.GetValue()); + } + + [Fact] + public void SelfExportWithLazyPropertyImportTest() + { + TypeCatalog catalog = new TypeCatalog(typeof(SelfExportWithLazyPropertyImport<,>), typeof(SelfImport<,>)); + CompositionContainer container = new CompositionContainer(catalog); + + var export = container.GetExportedValueOrDefault>(); + Assert.NotNull(export); + + var partWithImport = export as IPartWithImport; + Assert.NotNull(partWithImport); + Assert.NotNull(partWithImport.GetValue()); + } + + [Fact] + public void SelfExportWithNakedLazyPropertyImportTest() + { + TypeCatalog catalog = new TypeCatalog(typeof(SelfExportWithNakedLazyPropertyImport<>), typeof(Foo)); + CompositionContainer container = new CompositionContainer(catalog); + + var export = container.GetExportedValueOrDefault>(); + Assert.NotNull(export); + + var partWithImport = export as IPartWithImport; + Assert.NotNull(partWithImport); + Assert.NotNull(partWithImport.GetValue()); + } + + [Fact] + public void SelfExportWithExportFactoryPropertyImportTest() + { + TypeCatalog catalog = new TypeCatalog(typeof(SelfExportWithExportFactoryPropertyImport<,>), typeof(SelfImport<,>)); + CompositionContainer container = new CompositionContainer(catalog); + + var export = container.GetExportedValueOrDefault>(); + Assert.NotNull(export); + + var partWithImport = export as IPartWithImport; + Assert.NotNull(partWithImport); + + var value = partWithImport.GetValue() as ExportFactory>; + Assert.NotNull(value); + + using (var efv = value.CreateExport()) + { + Assert.NotNull(efv.Value); + } + } + + [Fact] + public void SelfExportWithNakedExportFactoryPropertyImportTest() + { + TypeCatalog catalog = new TypeCatalog(typeof(SelfExportWithNakedExportFactoryPropertyImport<>), typeof(Foo)); + CompositionContainer container = new CompositionContainer(catalog); + + var export = container.GetExportedValueOrDefault>(); + Assert.NotNull(export); + + var partWithImport = export as IPartWithImport; + Assert.NotNull(partWithImport); + + var value = partWithImport.GetValue() as ExportFactory; + Assert.NotNull(value); + + using (var efv = value.CreateExport()) + { + Assert.NotNull(efv.Value); + } + } + + [Fact] + public void SelfExportWithExportFactoryParameterImportTest() + { + TypeCatalog catalog = new TypeCatalog(typeof(SelfExportWithExportFactoryParameterImport<,>), typeof(SelfImport<,>)); + CompositionContainer container = new CompositionContainer(catalog); + + var export = container.GetExportedValueOrDefault>(); + Assert.NotNull(export); + + var partWithImport = export as IPartWithImport; + Assert.NotNull(partWithImport); + + var value = partWithImport.GetValue() as ExportFactory>; + Assert.NotNull(value); + + using (var efv = value.CreateExport()) + { + Assert.NotNull(efv.Value); + } + } + + [Fact] + public void SelfExportWithCollectionPropertyImportTest() + { + TypeCatalog catalog = new TypeCatalog(typeof(SelfExportWithCollectionPropertyImport<,>), typeof(SelfImport<,>)); + CompositionContainer container = new CompositionContainer(catalog); + + var export = container.GetExportedValueOrDefault>(); + Assert.NotNull(export); + + var partWithImport = export as IPartWithImport; + Assert.NotNull(partWithImport); + Assert.NotNull(partWithImport.GetValue()); + } + + [Fact] + public void SelfExportWithLazyCollectionPropertyImportTest() + { + TypeCatalog catalog = new TypeCatalog(typeof(SelfExportWithLazyCollectionPropertyImport<,>), typeof(SelfImport<,>)); + CompositionContainer container = new CompositionContainer(catalog); + + var export = container.GetExportedValueOrDefault>(); + Assert.NotNull(export); + + var partWithImport = export as IPartWithImport; + Assert.NotNull(partWithImport); + Assert.NotNull(partWithImport.GetValue()); + } + + [Fact] + public void SelfExportWithPropertyImportWithContractInferredTest() + { + TypeCatalog catalog = new TypeCatalog(typeof(SelfExportWithPropertyImportWithContractInferred<,>), typeof(SelfImport<,>)); + CompositionContainer container = new CompositionContainer(catalog); + + var export = container.GetExportedValueOrDefault>(); + Assert.NotNull(export); + + var partWithImport = export as IPartWithImport; + Assert.NotNull(partWithImport); + Assert.NotNull(partWithImport.GetValue()); + } + + [Fact] + public void SelfExportWithParameterImportTest() + { + TypeCatalog catalog = new TypeCatalog(typeof(SelfExportWithParameterImport<,>), typeof(SelfImport<,>)); + CompositionContainer container = new CompositionContainer(catalog); + + var export = container.GetExportedValueOrDefault>(); + Assert.NotNull(export); + + var partWithImport = export as IPartWithImport; + Assert.NotNull(partWithImport); + Assert.NotNull(partWithImport.GetValue()); + } + + [Fact] + public void SelfExportWithMultipleGenericImportsTest() + { + TypeCatalog catalog = new TypeCatalog(typeof(SelfExportWithMultipleGenericImports<,>), typeof(SelfImport<,>), typeof(SelfImport<>), typeof(Foo)); + CompositionContainer container = new CompositionContainer(catalog); + + var export = container.GetExportedValueOrDefault>(); + Assert.NotNull(export); + + var part = export as SelfExportWithMultipleGenericImports; + + Assert.NotNull(part); + Assert.NotNull(part.Import1); + Assert.NotNull(part.Import2); + Assert.NotNull(part.Import3); + Assert.NotNull(part.Import4); + Assert.NotNull(part.Import5); + Assert.NotNull(part.Import6); + } + + [Fact] + public void SpecilzationMakesGeneric() + { + TypeCatalog catalog = new TypeCatalog(typeof(SelfExport<,>), typeof(ExportFooBar), typeof(SelfExport)); + CompositionContainer container = new CompositionContainer(catalog); + + // we are expecting 3 - one from the open generic, one from the closed generic and one from the specialization + var exports = container.GetExportedValues>().ToArray(); + Assert.Equal(3, exports.Length); + } + + [Fact] + public void SingletonBehavior() + { + TypeCatalog catalog = new TypeCatalog(typeof(SingletonExport<,>)); + CompositionContainer container = new CompositionContainer(catalog); + + SingletonExportExportCount.Count = 0; + + var exports = container.GetExportedValues>(); + Assert.Equal(1, exports.Count()); + // only one instance of the SingletonExport<,> is created + Assert.Equal(1, SingletonExportExportCount.Count); + + exports = container.GetExportedValues>(); + Assert.Equal(1, exports.Count()); + // still only one instance of the SingletonExport<,> is created + Assert.Equal(1, SingletonExportExportCount.Count); + + var import = new SingletonImport(); + container.SatisfyImportsOnce(import); + // still only one instance of the SingletonExport<,> is created + Assert.Equal(1, SingletonExportExportCount.Count); + + import = new SingletonImport(); + container.SatisfyImportsOnce(import); + // still only one instance of the SingletonExport<,> is created + Assert.Equal(1, SingletonExportExportCount.Count); + + var import2 = new SingletonImport(); + container.SatisfyImportsOnce(import2); + // two instances of the SingletonExport<,> is created + Assert.Equal(2, SingletonExportExportCount.Count); + } + + [Fact] + public void PartWithTypeConstraintTest() + { + TypeCatalog catalog = new TypeCatalog(typeof(PartWithTypeConstraint<>)); + CompositionContainer container = new CompositionContainer(catalog); + + // IFoo should work + Assert.Equal(1, container.GetExportedValues>().Count()); + + // IFoo2 should work + Assert.Equal(1, container.GetExportedValues>().Count()); + + // IBar shouldn't + Assert.Equal(0, container.GetExportedValues>().Count()); + } + + [Fact] + public void PartWithBaseTypeConstraintTest() + { + TypeCatalog catalog = new TypeCatalog(typeof(PartWithBaseTypeConstraint<>)); + CompositionContainer container = new CompositionContainer(catalog); + + // bar should work + Assert.Equal(1, container.GetExportedValues>().Count()); + + // bar2 should work + Assert.Equal(1, container.GetExportedValues>().Count()); + + // IFoo shouldn't + Assert.Equal(0, container.GetExportedValues>().Count()); + } + + [Fact] + public void PartWithRefTypeConstraintTest() + { + TypeCatalog catalog = new TypeCatalog(typeof(PartWithRefTypeConstraint<>)); + CompositionContainer container = new CompositionContainer(catalog); + + // IFoo should work + Assert.Equal(1, container.GetExportedValues>().Count()); + + // Bar should work + Assert.Equal(1, container.GetExportedValues>().Count()); + + // int shouldn't + Assert.Equal(0, container.GetExportedValues>().Count()); + + // FooStruct shouldn't + Assert.Equal(0, container.GetExportedValues>().Count()); + } + + [Fact] + public void PartWithStructTypeConstraintTest() + { + TypeCatalog catalog = new TypeCatalog(typeof(PartWithStructTypeConstraint<>)); + CompositionContainer container = new CompositionContainer(catalog); + + // int should work + Assert.Equal(1, container.GetExportedValues>().Count()); + + // FooStruct should work + Assert.Equal(1, container.GetExportedValues>().Count()); + + // IFoo shouldn't + Assert.Equal(0, container.GetExportedValues>().Count()); + + // Bar shouldn't + Assert.Equal(0, container.GetExportedValues>().Count()); + + } + + [Fact] + public void PartWithNewableTypeConstraintTest() + { + TypeCatalog catalog = new TypeCatalog(typeof(PartWithNewableTypeConstraint<>)); + CompositionContainer container = new CompositionContainer(catalog); + + // int should work + Assert.Equal(1, container.GetExportedValues>().Count()); + + // FooStruct should work + Assert.Equal(1, container.GetExportedValues>().Count()); + + // IFoo shouldn't + Assert.Equal(0, container.GetExportedValues>().Count()); + + // Bar should + Assert.Equal(1, container.GetExportedValues>().Count()); + + } + + [Fact] + public void PartWithGenericConstraintTest() + { + TypeCatalog catalog = new TypeCatalog(typeof(PartWithGenericConstraint<,>)); + CompositionContainer container = new CompositionContainer(catalog); + + // int, Dictionary should work + Assert.Equal(1, container.GetExportedValues>>().Count()); + + // int, Dictionary should not work + Assert.Equal(0, container.GetExportedValues>>().Count()); + + // FooStruct, FooStruct[] should work + Assert.Equal(1, container.GetExportedValues>>().Count()); + + // FooStruct, IFoo should not + Assert.Equal(0, container.GetExportedValues>().Count()); + + } + + [Fact] + public void PartWithNakedConstraintTest() + { + TypeCatalog catalog = new TypeCatalog(typeof(PartWithNakedConstraint<,>)); + CompositionContainer container = new CompositionContainer(catalog); + + // Bar, Bar2 should work + Assert.Equal(1, container.GetExportedValues>().Count()); + + // Bar2, Bar should not work + Assert.Equal(0, container.GetExportedValues>().Count()); + } + + [Fact] + public void PartWithExportParametersInReverseOrder() + { + TypeCatalog catalog = new TypeCatalog(typeof(PropertyExportWithChangedParameterOrder<,>)); + CompositionContainer container = new CompositionContainer(catalog); + + Assert.Equal(1, container.GetExportedValues>().Count()); + } + + public interface IA { } + public interface IB { } + + [Export(typeof(IA<>)), Export(typeof(IB<>))] + public class AB : IA, IB { } + + [Fact] + public void PartWithMultipleExportsOfASingleOrder_One_ShouldSucceed() + { + var tc = new TypeCatalog(typeof(AB<>)); + var cc = new CompositionContainer(tc); + var ia = cc.GetExportedValue>(); + var ib = cc.GetExportedValue>(); + var ia1 = cc.GetExportedValue>(); + var ib1 = cc.GetExportedValue>(); + } + + [Export(typeof(IA<>)), Export(typeof(IBar))] + class ANonGenericB : IA, IBar { } + + [Fact] + public void NonGenericExportOnGenericPartIsNotAllowed() + { + var catalog = new TypeCatalog(typeof(ANonGenericB<>)); + var container = new CompositionContainer(catalog); + Assert.Throws(() => + { + var b = container.GetExportedValue(); + }); + + } + + [Fact] + public void MultipleGenericExportsFromTheSameSharedPartProvideTheSameInstance() + { + var catalog = new TypeCatalog(typeof(AB<>)); + var container = new CompositionContainer(catalog); + var x = container.GetExportedValue>(); + var y = container.GetExportedValue>(); + Assert.Same(x, y); + } + + [InheritedExport] + interface ITest1 + { + void Execute(); + } + + class TestClass1 : ITest1 + { + public void Execute() { } + } + + [InheritedExport] + abstract class BaseClass + { + public abstract void Execute(); + } + + class TestClass2 : BaseClass + { + public override void Execute() { } + } + + [Fact] + public void InheritedExportOnGenericInterface() + { + var catalog = new TypeCatalog(typeof(TestClass1<>)); + var container = new CompositionContainer(catalog); + var test = container.GetExportedValues>(); + + Assert.True(test.Count() == 1); + } + + [Fact] + public void InheritedExportOnGenericBaseClass() + { + var catalog = new TypeCatalog(typeof(TestClass2<>)); + var container = new CompositionContainer(catalog); + var test = container.GetExportedValues>(); + + Assert.True(test.Count() == 1); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/AggregateCatalogTest.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/AggregateCatalogTest.cs new file mode 100644 index 0000000000..099fb954dc --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/AggregateCatalogTest.cs @@ -0,0 +1,974 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Primitives; +using System.IO; +using System.Linq; +using System.Threading; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition.Hosting +{ + public class AggregateCatalogTest + { + [Fact] + public void Constructor1_ShouldNotThrow() + { + new AggregateCatalog(); + } + + [Fact] + public void Constructor1_ShouldSetCatalogsPropertyToEmpty() + { + var catalog = new AggregateCatalog(); + + Assert.Empty(catalog.Catalogs); + } + + [Fact] + [ActiveIssue(812029)] + public void Constructor1_ShouldSetPartsPropertyToEmpty() + { + var catalog = new AggregateCatalog(); + + Assert.Empty(catalog.Parts); + } + + [Fact] + public void Constructor3_NullAsCatalogsArgument_ShouldSetCatalogsPropertyToEmpty() + { + var catalog = new AggregateCatalog((IEnumerable)null); + + Assert.Empty(catalog.Catalogs); + } + + [Fact] + public void Constructor3_EmptyIEnumerableAsCatalogsArgument_ShouldSetCatalogsPropertyToEmpty() + { + var catalog = new AggregateCatalog(Enumerable.Empty()); + + Assert.Empty(catalog.Catalogs); + } + + [Fact] + [ActiveIssue(812029)] + public void Constructor3_NullAsCatalogsArgument_ShouldSetPartsPropertyToEmpty() + { + var catalog = new AggregateCatalog((IEnumerable)null); + + Assert.Empty(catalog.Parts); + } + + [Fact] + [ActiveIssue(812029)] + public void Constructor3_EmptyIEnumerableAsCatalogsArgument_ShouldSetPartsPropertyToEmpty() + { + var catalog = new AggregateCatalog(Enumerable.Empty()); + + Assert.Empty(catalog.Parts); + } + + [Fact] + [ActiveIssue(25498)] + public void Constructor3_ArrayWithNullAsCatalogsArgument_ShouldThrowArgument() + { + var catalogs = new ComposablePartCatalog[] { null }; + + AssertExtensions.Throws("catalogs", () => + { + new AggregateCatalog(catalogs); + }); + } + + [Fact] + public void Catalogs_WhenCatalogDisposed_ShouldThrowObjectDisposed() + { + var catalog = CreateAggregateCatalog(); + catalog.Dispose(); + + ExceptionAssert.ThrowsDisposed(catalog, () => + { + var catalogs = catalog.Catalogs; + }); + } + + [Fact] + public void Parts_WhenCatalogDisposed_ShouldThrowObjectDisposed() + { + var catalog = CreateAggregateCatalog(); + catalog.Dispose(); + + ExceptionAssert.ThrowsDisposed(catalog, () => + { + var parts = catalog.Parts; + }); + } + + [Fact] + public void GetExports_WhenCatalogDisposed_ShouldThrowObjectDisposed() + { + var catalog = CreateAggregateCatalog(); + catalog.Dispose(); + var definition = ImportDefinitionFactory.Create(); + + ExceptionAssert.ThrowsDisposed(catalog, () => + { + catalog.GetExports(definition); + }); + } + + [Fact] + [ActiveIssue(25498)] + public void GetExports_NullAsConstraintArgument_ShouldThrowArgumentNull() + { + var catalog = CreateAggregateCatalog(); + + AssertExtensions.Throws("definition", () => + { + catalog.GetExports((ImportDefinition)null); + }); + } + + [Fact] + public void Dispose_ShouldNotThrow() + { + using (var catalog = CreateAggregateCatalog()) + { + } + } + + [Fact] + public void Dispose_CanBeCalledMultipleTimes() + { + var catalog = CreateAggregateCatalog(); + catalog.Dispose(); + catalog.Dispose(); + catalog.Dispose(); + } + + [Fact] + public void EnumeratePartsProperty_ShouldSucceed() + { + using (var catalog = new AggregateCatalog( + new TypeCatalog(typeof(SharedPartStuff)), + new TypeCatalog(typeof(SharedPartStuff)), + new TypeCatalog(typeof(SharedPartStuff)), + new TypeCatalog(typeof(SharedPartStuff)), + new TypeCatalog(typeof(SharedPartStuff)), + new TypeCatalog(typeof(SharedPartStuff)))) + { + Assert.True(catalog.Catalogs.Count() == 6); + Assert.True(catalog.Parts.Count() == 6); + } + } + + [Fact] + public void MutableCatalogNotifications() + { + int step = 0; + int changedStep = 0; + var catalog = new AggregateCatalog(); + + var typePartCatalog = new TypeCatalog(typeof(SharedPartStuff)); + var typePartCatalog1 = new TypeCatalog(typeof(SharedPartStuff)); + var typePartCatalog2 = new TypeCatalog(typeof(SharedPartStuff)); + var typePartCatalog3 = new TypeCatalog(typeof(SharedPartStuff)); + var typePartCatalog4 = new TypeCatalog(typeof(SharedPartStuff)); + var typePartCatalog5 = new TypeCatalog(typeof(SharedPartStuff)); + + // Smoke test on inner collection + catalog.Catalogs.Add(typePartCatalog); + catalog.Catalogs.Remove(typePartCatalog); + catalog.Catalogs.Clear(); + Assert.True(catalog.Catalogs.Count == 0); + + // Add notifications + catalog.Changed += delegate (object source, ComposablePartCatalogChangeEventArgs args) + { + // Local code + ++step; + ++step; + changedStep = step; + }; + + //Add something then verify counters + catalog.Catalogs.Add(typePartCatalog); + Assert.True(catalog.Catalogs.Count == 1); + Assert.True(changedStep == 2); + + // Reset counters + step = changedStep = 0; + + // Remove something then verify counters + catalog.Catalogs.Remove(typePartCatalog); + Assert.True(catalog.Catalogs.Count == 0); + Assert.True(changedStep == 2); + + //Now Add it back + catalog.Catalogs.Add(typePartCatalog); + Assert.True(catalog.Catalogs.Count == 1); + + step = changedStep = 0; + // Now clear the collection and verify counters + catalog.Catalogs.Clear(); + Assert.True(catalog.Catalogs.Count == 0); + Assert.True(changedStep == 2); + + // Now remove a non existent item and verify counters + step = changedStep = 0; + bool removed = catalog.Catalogs.Remove(typePartCatalog); + Assert.True(removed == false); + Assert.True(changedStep == 0); + + // Add a bunch + step = changedStep = 0; + catalog.Catalogs.Add(typePartCatalog); + Assert.True(catalog.Catalogs.Count == 1); + Assert.True(changedStep == 2); + + catalog.Catalogs.Add(typePartCatalog1); + Assert.True(catalog.Catalogs.Count == 2); + Assert.True(changedStep == 4); + + catalog.Catalogs.Add(typePartCatalog2); + catalog.Catalogs.Add(typePartCatalog3); + catalog.Catalogs.Add(typePartCatalog4); + catalog.Catalogs.Add(typePartCatalog5); + Assert.True(catalog.Catalogs.Count == 6); + Assert.True(changedStep == 12); + + removed = catalog.Catalogs.Remove(typePartCatalog3); + Assert.True(catalog.Catalogs.Count == 5); + Assert.True(removed == true); + Assert.True(changedStep == 14); + removed = catalog.Catalogs.Remove(typePartCatalog2); + removed = catalog.Catalogs.Remove(typePartCatalog1); + removed = catalog.Catalogs.Remove(typePartCatalog4); + removed = catalog.Catalogs.Remove(typePartCatalog); + removed = catalog.Catalogs.Remove(typePartCatalog5); + Assert.True(catalog.Catalogs.Count == 0); + Assert.True(removed == true); + Assert.True(changedStep == 24); + + // Add and then clear a lot + step = changedStep = 0; + catalog.Catalogs.Add(typePartCatalog); + catalog.Catalogs.Add(typePartCatalog1); + catalog.Catalogs.Add(typePartCatalog2); + catalog.Catalogs.Add(typePartCatalog3); + catalog.Catalogs.Add(typePartCatalog4); + catalog.Catalogs.Add(typePartCatalog5); + Assert.True(catalog.Catalogs.Count == 6); + Assert.True(changedStep == 12); + + catalog.Catalogs.Clear(); + Assert.True(catalog.Catalogs.Count == 0); + + step = changedStep = 0; + int step2 = 100; + int changedStep2 = 0; + + catalog.Changed += delegate (object source, ComposablePartCatalogChangeEventArgs args) + { + // Local code + --step2; + --step2; + changedStep2 = step2; + }; + + catalog.Catalogs.Add(typePartCatalog); + Assert.True(catalog.Catalogs.Count == 1); + Assert.True(changedStep == 2); + Assert.True(changedStep2 == 98); + + catalog.Catalogs.Add(typePartCatalog1); + Assert.True(catalog.Catalogs.Count == 2); + Assert.True(changedStep == 4); + Assert.True(changedStep2 == 96); + + catalog.Catalogs.Remove(typePartCatalog); + Assert.True(catalog.Catalogs.Count == 1); + Assert.True(changedStep == 6); + Assert.True(changedStep2 == 94); + + catalog.Catalogs.Clear(); + Assert.True(catalog.Catalogs.Count == 0); + Assert.True(changedStep == 8); + Assert.True(changedStep2 == 92); + + } + + [Fact] + public void DisposeAggregatingCatalog() + { + int changedNotification = 0; + + var typePartCatalog1 = new TypeCatalog(typeof(SharedPartStuff)); + var typePartCatalog2 = new TypeCatalog(typeof(SharedPartStuff)); + var typePartCatalog3 = new TypeCatalog(typeof(SharedPartStuff)); + + var assemblyPartCatalog1 = new AssemblyCatalog(typeof(SharedPartStuff).Assembly); + var assemblyPartCatalog2 = new AssemblyCatalog(typeof(SharedPartStuff).Assembly); + var assemblyPartCatalog3 = new AssemblyCatalog(typeof(SharedPartStuff).Assembly); + + var dirPartCatalog1 = new DirectoryCatalog(Path.GetTempPath()); + var dirPartCatalog2 = new DirectoryCatalog(Path.GetTempPath()); + var dirPartCatalog3 = new DirectoryCatalog(Path.GetTempPath()); + + using (var catalog = new AggregateCatalog()) + { + catalog.Catalogs.Add(typePartCatalog1); + catalog.Catalogs.Add(typePartCatalog2); + catalog.Catalogs.Add(typePartCatalog3); + + catalog.Catalogs.Add(assemblyPartCatalog1); + catalog.Catalogs.Add(assemblyPartCatalog2); + catalog.Catalogs.Add(assemblyPartCatalog3); + + catalog.Catalogs.Add(dirPartCatalog1); + catalog.Catalogs.Add(dirPartCatalog2); + catalog.Catalogs.Add(dirPartCatalog3); + + // Add notifications + catalog.Changed += delegate (object source, ComposablePartCatalogChangeEventArgs args) + { + // Local code + ++changedNotification; + }; + + } + + Assert.True(changedNotification == 0); + + //Ensure that the other catalogs are + ExceptionAssert.ThrowsDisposed(typePartCatalog1, () => + { + var iEnum = typePartCatalog1.Parts.GetEnumerator(); + }); + + ExceptionAssert.ThrowsDisposed(typePartCatalog2, () => + { + var iEnum = typePartCatalog2.Parts.GetEnumerator(); + }); + + ExceptionAssert.ThrowsDisposed(typePartCatalog3, () => + { + var iEnum = typePartCatalog3.Parts.GetEnumerator(); + }); + + //Ensure that the other catalogs are + ExceptionAssert.ThrowsDisposed(assemblyPartCatalog1, () => + { + var iEnum = assemblyPartCatalog1.Parts.GetEnumerator(); + }); + + ExceptionAssert.ThrowsDisposed(assemblyPartCatalog2, () => + { + var iEnum = assemblyPartCatalog2.Parts.GetEnumerator(); + }); + + ExceptionAssert.ThrowsDisposed(assemblyPartCatalog3, () => + { + var iEnum = assemblyPartCatalog3.Parts.GetEnumerator(); + }); + + //Ensure that the other catalogs are + ExceptionAssert.ThrowsDisposed(dirPartCatalog1, () => + { + var iEnum = dirPartCatalog1.Parts.GetEnumerator(); + }); + + ExceptionAssert.ThrowsDisposed(dirPartCatalog2, () => + { + var iEnum = dirPartCatalog2.Parts.GetEnumerator(); + }); + + ExceptionAssert.ThrowsDisposed(dirPartCatalog3, () => + { + var iEnum = dirPartCatalog3.Parts.GetEnumerator(); + }); + } + + [Fact] + [ActiveIssue(514749)] + public void MutableMultithreadedEnumerations() + { + var catalog = new AggregateCatalog(); + + ThreadStart func = delegate () + { + var typePart = new TypeCatalog(typeof(SharedPartStuff)); + var typePart1 = new TypeCatalog(typeof(SharedPartStuff)); + var typePart2 = new TypeCatalog(typeof(SharedPartStuff)); + var typePart3 = new TypeCatalog(typeof(SharedPartStuff)); + var typePart4 = new TypeCatalog(typeof(SharedPartStuff)); + var typePart5 = new TypeCatalog(typeof(SharedPartStuff)); + + for (int i = 0; i < 100; i++) + { + catalog.Catalogs.Add(typePart); + catalog.Catalogs.Add(typePart1); + catalog.Catalogs.Add(typePart2); + catalog.Catalogs.Add(typePart3); + catalog.Catalogs.Add(typePart4); + catalog.Catalogs.Add(typePart5); + + Assert.True(catalog.Catalogs.Count >= 6); + + for (int k = 0; k < 5; k++) + { + int j; + // Ensure that iterating the returned queryable is okay even though there are many threads mutationg it + // We are really just looking to ensure that ollection changed exceptions are not thrown + j = 0; + var iq = catalog.Parts.GetEnumerator(); + while (iq.MoveNext()) + { + ++j; + } + + Assert.True(j >= 6); + + // Ensure that iterating the returned enumerator is okay even though there are many threads mutationg it + // We are really just looking to ensure that collection changed exceptions are not thrown + j = 0; + var ie = catalog.Catalogs.GetEnumerator(); + while (ie.MoveNext()) + { + ++j; + } + Assert.True(j >= 6); + } + + catalog.Catalogs.Remove(typePart); + catalog.Catalogs.Remove(typePart1); + catalog.Catalogs.Remove(typePart2); + catalog.Catalogs.Remove(typePart3); + catalog.Catalogs.Remove(typePart4); + catalog.Catalogs.Remove(typePart5); + } + }; + + Thread[] threads = new Thread[100]; + for (int i = 0; i < threads.Length; i++) + { + threads[i] = new Thread(func); + } + + for (int i = 0; i < threads.Length; i++) + { + threads[i].Start(); + } + for (int i = 0; i < threads.Length; i++) + { + threads[i].Join(); + } + + Assert.True(catalog.Catalogs.Count == 0); + } + + public void CreateMainAndOtherChildren( + out AggregateCatalog[] mainChildren, + out AggregateCatalog[] otherChildren, + out TypeCatalog[] componentCatalogs) + { + componentCatalogs = new TypeCatalog[] + { + new TypeCatalog(typeof(SharedPartStuff)), + new TypeCatalog(typeof(SharedPartStuff)), + new TypeCatalog(typeof(SharedPartStuff)) + }; + + // Create our child catalogs + mainChildren = new AggregateCatalog[5]; + for (int i = 0; i < mainChildren.Length; i++) + { + mainChildren[i] = new AggregateCatalog(componentCatalogs); + } + + otherChildren = new AggregateCatalog[5]; + for (int i = 0; i < otherChildren.Length; i++) + { + otherChildren[i] = new AggregateCatalog(componentCatalogs); + } + } + + [Fact] + [ActiveIssue(812029)] + public void AggregatingCatalogAddAndRemoveChildren() + { + int changedCount = 0; + int typesChanged = 0; + EventHandler onChanged = delegate (object sender, ComposablePartCatalogChangeEventArgs e) + { + ++changedCount; + typesChanged += e.AddedDefinitions.Concat(e.RemovedDefinitions).Count(); + }; + + // Create our child catalogs + AggregateCatalog[] mainChildren; + AggregateCatalog[] otherChildren; + TypeCatalog[] componentCatalogs; + + CreateMainAndOtherChildren(out mainChildren, out otherChildren, out componentCatalogs); + + var parent = new AggregateCatalog(mainChildren); + parent.Changed += onChanged; + + for (int i = 0; i < otherChildren.Length; i++) + { + parent.Catalogs.Add(otherChildren[i]); + } + + Assert.Equal(otherChildren.Length, changedCount); + Assert.Equal(otherChildren.Length * 3, typesChanged); + + changedCount = 0; + typesChanged = 0; + + parent.Catalogs.Remove(otherChildren[0]); + parent.Catalogs.Remove(otherChildren[1]); + + Assert.Equal(2, changedCount); + Assert.Equal(2 * 3, typesChanged); + + changedCount = 0; + typesChanged = 0; + + parent.Catalogs.Add(otherChildren[0]); + parent.Catalogs.Add(otherChildren[1]); + + Assert.Equal(2, changedCount); + Assert.Equal(2 * 3, typesChanged); + + changedCount = 0; + typesChanged = 0; + + parent.Catalogs.Clear(); + Assert.Equal(1, changedCount); + Assert.Equal((mainChildren.Length + otherChildren.Length) * 3, typesChanged); + + changedCount = 0; + typesChanged = 0; + + // These have already been removed and so I should be able remove components from them without recieving notifications + otherChildren[0].Catalogs.Remove(componentCatalogs[0]); + otherChildren[1].Catalogs.Remove(componentCatalogs[1]); + Assert.Equal(0, changedCount); + Assert.Equal(0, typesChanged); + + // These have already been Cleared and so I should be able remove components from them without recieving notifications + otherChildren[3].Catalogs.Remove(componentCatalogs[0]); + otherChildren[4].Catalogs.Remove(componentCatalogs[1]); + Assert.Equal(0, changedCount); + } + + [Fact] + [ActiveIssue(812029)] + public void AggregatingCatalogAddAndRemoveNestedChildren() + { + int changedCount = 0; + int typesChanged = 0; + + EventHandler onChanged = delegate (object sender, ComposablePartCatalogChangeEventArgs e) + { + ++changedCount; + typesChanged += e.AddedDefinitions.Concat(e.RemovedDefinitions).Count(); + }; + + // Create our child catalogs + AggregateCatalog[] mainChildren; + AggregateCatalog[] otherChildren; + TypeCatalog[] componentCatalogs; + CreateMainAndOtherChildren(out mainChildren, out otherChildren, out componentCatalogs); + + var parent = new AggregateCatalog(mainChildren); + parent.Changed += onChanged; + + for (int i = 0; i < otherChildren.Length; i++) + { + parent.Catalogs.Add(otherChildren[i]); + } + + Assert.Equal(otherChildren.Length, changedCount); + Assert.Equal(otherChildren.Length * 3, typesChanged); + + changedCount = 0; + typesChanged = 0; + + otherChildren[0].Catalogs.Remove(componentCatalogs[0]); + otherChildren[1].Catalogs.Remove(componentCatalogs[1]); + + Assert.Equal(2, changedCount); + Assert.Equal(2, typesChanged); + + changedCount = 0; + typesChanged = 0; + + otherChildren[0].Catalogs.Add(componentCatalogs[0]); + otherChildren[1].Catalogs.Add(componentCatalogs[1]); + + Assert.Equal(2, changedCount); + Assert.Equal(2, typesChanged); + + changedCount = 0; + typesChanged = 0; + otherChildren[1].Catalogs.Clear(); + Assert.Equal(1, changedCount); + Assert.Equal(componentCatalogs.Length, typesChanged); + } + + [Fact] + [ActiveIssue(812029)] + public void AggregatingDisposedAndNotifications() + { + int changedCount = 0; + int typesChanged = 0; + + EventHandler onChanged = delegate (object sender, ComposablePartCatalogChangeEventArgs e) + { + ++changedCount; + typesChanged += e.AddedDefinitions.Concat(e.RemovedDefinitions).Count(); + }; + + // Create our child catalogs + AggregateCatalog[] mainChildren; + AggregateCatalog[] otherChildren; + TypeCatalog[] componentCatalogs; + CreateMainAndOtherChildren(out mainChildren, out otherChildren, out componentCatalogs); + + var parent = new AggregateCatalog(mainChildren); + parent.Changed += onChanged; + + for (int i = 0; i < otherChildren.Length; i++) + { + parent.Catalogs.Add(otherChildren[i]); + } + + Assert.Equal(otherChildren.Length, changedCount); + Assert.Equal(otherChildren.Length * 3, typesChanged); + + changedCount = 0; + typesChanged = 0; + + parent.Dispose(); + + Assert.Equal(0, changedCount); + Assert.Equal(0, typesChanged); + + //Ensure that the children are also disposed + ExceptionAssert.ThrowsDisposed(otherChildren[0], () => + { + otherChildren[0].Catalogs.Remove(componentCatalogs[0]); + }); + + //Ensure that the children are also disposed + ExceptionAssert.ThrowsDisposed(otherChildren[4], () => + { + otherChildren[4].Catalogs.Remove(componentCatalogs[0]); + }); + + Assert.Equal(0, changedCount); + Assert.Equal(0, typesChanged); + } + + [Fact] + public void AggregatingCatalogParmsConstructorAggregateAggregateCatalogs() + { + var aggCatalog1 = new AggregateCatalog(); + var aggCatalog2 = new AggregateCatalog(); + var aggCatalog3 = new AggregateCatalog(); + + // Construct with one catalog parameter + var catalog = new AggregateCatalog(aggCatalog1); + Assert.True(catalog.Catalogs.Count == 1); + + // Construct with two catalog parameters + catalog = new AggregateCatalog(aggCatalog1, aggCatalog2); + Assert.True(catalog.Catalogs.Count == 2); + + // Construct with three catalog parameters + catalog = new AggregateCatalog(aggCatalog1, aggCatalog2, aggCatalog3); + Assert.True(catalog.Catalogs.Count == 3); + } + + [Fact] + public void AggregatingCatalogParmsConstructorAggregateAssemblyCatalogs() + { + var assemblyCatalog1 = new AssemblyCatalog(typeof(SharedPartStuff).Assembly); + var assemblyCatalog2 = new AssemblyCatalog(typeof(SharedPartStuff).Assembly); + var assemblyCatalog3 = new AssemblyCatalog(typeof(SharedPartStuff).Assembly); + + // Construct with one catalog parameter + var catalog = new AggregateCatalog(assemblyCatalog1); + Assert.True(catalog.Catalogs.Count == 1); + + // Construct with two catalog parameters + catalog = new AggregateCatalog(assemblyCatalog1, assemblyCatalog2); + Assert.True(catalog.Catalogs.Count == 2); + + // Construct with three catalog parameters + catalog = new AggregateCatalog(assemblyCatalog1, assemblyCatalog2, assemblyCatalog3); + Assert.True(catalog.Catalogs.Count == 3); + } + + [Fact] + public void AggregatingCatalogParmsConstructorMixedCatalogs() + { + var typePartCatalog1 = new TypeCatalog(typeof(SharedPartStuff)); + var assemblyCatalog2 = new AssemblyCatalog(typeof(SharedPartStuff).Assembly); + var typePartCatalog3 = new TypeCatalog(typeof(SharedPartStuff)); + + // Construct with three catalog parameters + var catalog = new AggregateCatalog(typePartCatalog1, assemblyCatalog2, typePartCatalog3); + Assert.True(catalog.Catalogs.Count == 3); + } + + [Fact] + public void AggregatingCatalogRaisesChangesForCatalogsPassedToConstructor() + { + var subCatalog = CreateAggregateCatalog(); + var testCatalog = new AggregateCatalog(subCatalog); + + bool changedCalled = false; + testCatalog.Changed += delegate + { + changedCalled = true; + }; + + subCatalog.Catalogs.Add(new TypeCatalog(typeof(SharedPartStuff))); + + Assert.True(changedCalled); + } + + private AggregateCatalog CreateAggregateCatalog() + { + return new AggregateCatalog(); + } + + [Fact] + [ActiveIssue(812029)] + public void CatalogEvents_AggregateAddRemove() + { + var catalog = new AggregateCatalog(); + AggregateTests(catalog, catalog); + } + + [Fact] + [ActiveIssue(812029)] + public void CatalogEvents_DeepAggregateAddRemove() + { + var deepCatalog = new AggregateCatalog(); + var midCatalog = new AggregateCatalog(new ComposablePartCatalog[] { deepCatalog }); + var topCatalog = new AggregateCatalog(new ComposablePartCatalog[] { midCatalog }); + AggregateTests(topCatalog, deepCatalog); + } + + private void AggregateTests(AggregateCatalog watchedCatalog, AggregateCatalog modifiedCatalog) + { + var fooCatalog = new TypeCatalog(new Type[] { typeof(FooExporter) }); + var barCatalog = new TypeCatalog(new Type[] { typeof(BarExporter) }); + var bothCatalog = new TypeCatalog(new Type[] { typeof(FooExporter), typeof(BarExporter) }); + + var catalogListener = new CatalogListener(watchedCatalog, modifiedCatalog); + + catalogListener.VerifyAdd(fooCatalog, typeof(FooExporter)); + catalogListener.VerifyAdd(barCatalog, typeof(BarExporter)); + catalogListener.VerifyRemove(fooCatalog, typeof(FooExporter)); + catalogListener.VerifyRemove(barCatalog, typeof(BarExporter)); + + catalogListener.VerifyAdd(bothCatalog, typeof(FooExporter), typeof(BarExporter)); + catalogListener.VerifyClear(typeof(FooExporter), typeof(BarExporter)); + + catalogListener.VerifyAdd(bothCatalog, typeof(FooExporter), typeof(BarExporter)); + catalogListener.VerifyRemove(bothCatalog, typeof(FooExporter), typeof(BarExporter)); + } + + public interface IFoo { } + public interface IBar { } + + [Export(typeof(IFoo))] + public class FooExporter : IFoo + { + } + + [Export(typeof(IBar))] + public class BarExporter : IBar + { + } + + public class CatalogListener + { + private AggregateCatalog _watchedCatalog; + private AggregateCatalog _modifiedCatalog; + private string[] _expectedAdds; + private string[] _expectedRemoves; + private int _changedEventCount; + private int _changingEventCount; + + public CatalogListener(AggregateCatalog watchCatalog, AggregateCatalog modifiedCatalog) + { + watchCatalog.Changing += OnChanging; + watchCatalog.Changed += OnChanged; + this._watchedCatalog = watchCatalog; + this._modifiedCatalog = modifiedCatalog; + } + + public void VerifyAdd(ComposablePartCatalog catalogToAdd, params Type[] expectedTypesAdded) + { + this._expectedAdds = GetDisplayNames(expectedTypesAdded); + + this._modifiedCatalog.Catalogs.Add(catalogToAdd); + + Assert.True(this._changingEventCount == 1); + Assert.True(this._changedEventCount == 1); + + ResetState(); + } + + public void VerifyRemove(ComposablePartCatalog catalogToRemove, params Type[] expectedTypesRemoved) + { + this._expectedAdds = null; + this._expectedRemoves = GetDisplayNames(expectedTypesRemoved); + + this._modifiedCatalog.Catalogs.Remove(catalogToRemove); + + Assert.True(this._changingEventCount == 1); + Assert.True(this._changedEventCount == 1); + + ResetState(); + } + + public void VerifyClear(params Type[] expectedTypesRemoved) + { + this._expectedAdds = null; + this._expectedRemoves = GetDisplayNames(expectedTypesRemoved); + + this._modifiedCatalog.Catalogs.Clear(); + + Assert.True(this._changingEventCount == 1); + Assert.True(this._changedEventCount == 1); + + ResetState(); + } + + public void OnChanging(object sender, ComposablePartCatalogChangeEventArgs args) + { + Assert.True(this._expectedAdds != null || this._expectedRemoves != null); + + if (this._expectedAdds == null) + { + Assert.Empty(args.AddedDefinitions); + } + else + { + EqualityExtensions.CheckSequenceEquals(this._expectedAdds, GetDisplayNames(args.AddedDefinitions)); + } + + if (this._expectedRemoves == null) + { + Assert.Empty(args.RemovedDefinitions); + } + else + { + EqualityExtensions.CheckSequenceEquals(this._expectedRemoves, GetDisplayNames(args.RemovedDefinitions)); + } + + Assert.False(ContainsChanges(), "The catalog should NOT contain the changes yet"); + + this._changingEventCount++; + } + + public void OnChanged(object sender, ComposablePartCatalogChangeEventArgs args) + { + Assert.True(this._expectedAdds != null || this._expectedRemoves != null); + + if (this._expectedAdds == null) + { + Assert.Empty(args.AddedDefinitions); + } + else + { + EqualityExtensions.CheckSequenceEquals(this._expectedAdds, GetDisplayNames(args.AddedDefinitions)); + } + + if (this._expectedRemoves == null) + { + Assert.Empty(args.RemovedDefinitions); + } + else + { + EqualityExtensions.CheckSequenceEquals(this._expectedRemoves, GetDisplayNames(args.RemovedDefinitions)); + } + + Assert.Null(args.AtomicComposition); + Assert.True(ContainsChanges()); + + this._changedEventCount++; + } + + private bool ContainsChanges() + { + var allParts = GetDisplayNames(this._watchedCatalog.Parts); + + if (this._expectedAdds != null) + { + foreach (var add in this._expectedAdds) + { + if (!allParts.Contains(add)) + { + return false; + } + } + } + + if (this._expectedRemoves != null) + { + foreach (var remove in this._expectedRemoves) + { + if (allParts.Contains(remove)) + { + return false; + } + } + } + + return true; + } + + private void ResetState() + { + this._expectedAdds = null; + this._expectedRemoves = null; + this._changedEventCount = 0; + this._changingEventCount = 0; + } + + private static string[] GetDisplayNames(IEnumerable definitions) + { + return definitions.OfType().Select(p => p.DisplayName).ToArray(); + } + + private static string[] GetDisplayNames(IEnumerable types) + { + return GetDisplayNames(types.Select(t => AttributedModelServices.CreatePartDefinition(t, null))); + } + } + + [Export] + [PartCreationPolicy(CreationPolicy.Shared)] + public class SharedPartStuff + { + Guid id = Guid.NewGuid(); + + public override string ToString() + { + return id.ToString(); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/ApplicationCatalogTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/ApplicationCatalogTests.cs new file mode 100644 index 0000000000..269ad20130 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/ApplicationCatalogTests.cs @@ -0,0 +1,83 @@ +// 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.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.IO; +using System.Linq; +using System.Reflection; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class ApplicationCatalogTests + { + // This is a glorious do nothing ReflectionContext + public class ApplicationCatalogTestsReflectionContext : ReflectionContext + { + public override Assembly MapAssembly(Assembly assembly) + { + return assembly; + } + + public override TypeInfo MapType(TypeInfo type) + + { + return type; + } + } + + public class Worker : MarshalByRefObject + { + internal void DoWork(Action work) + { + work(); + } + } + + [Fact] + public void Constructor1_NullReflectionContextArgument_ShouldThrowArgumentNull() + { + Assert.Throws("reflectionContext", () => + { + new ApplicationCatalog((ReflectionContext)null); + }); + } + + [Fact] + public void Constructor3_NullBothArguments_ShouldThrowArgumentNull() + { + Assert.Throws("reflectionContext", () => + { + new ApplicationCatalog((ReflectionContext)null, (ICompositionElement)null); + }); + } + + [Fact] + public void Constructor2_NullDefinitionOriginArgument_ShouldThrowArgumentNull() + { + Assert.Throws("definitionOrigin", () => + { + new ApplicationCatalog((ICompositionElement)null); + }); + } + + [Fact] + public void Constructor3_NullDefinitionOriginArgument_ShouldThrowArgumentNull() + { + Assert.Throws("definitionOrigin", () => + { + new ApplicationCatalog((ICompositionElement)null); + }); + } + + [Fact] + public void ExecuteOnCreationThread() + { + // Add a proper test for event notification on caller thread + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/AssemblyCatalogDebuggerProxyTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/AssemblyCatalogDebuggerProxyTests.cs new file mode 100644 index 0000000000..4601170667 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/AssemblyCatalogDebuggerProxyTests.cs @@ -0,0 +1,64 @@ +// 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.Reflection; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition.Hosting +{ + public class AssemblyCatalogDebuggerProxyTests + { + [Fact] + [ActiveIssue(25498)] + public void Constructor_NullAsCatalogArgument_ShouldThrowArgumentNull() + { + AssertExtensions.Throws("catalog", () => + { + new AssemblyCatalogDebuggerProxy((AssemblyCatalog)null); + }); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void Constructor_ValueAsCatalogArgument_ShouldSetPartsProperty() + { + var expectations = Expectations.GetAssemblies(); + + foreach (var e in expectations) + { + var catalog = CreateAssemblyCatalog(e); + + var proxy = new AssemblyCatalogDebuggerProxy(catalog); + + EqualityExtensions.CheckSequenceEquals(catalog.Parts, proxy.Parts); + } + } + + [Fact] + public void Constructor_ValueAsCatalogArgument_ShouldSetAssemblyProperty() + { + var expectations = Expectations.GetAssemblies(); + + foreach (var e in expectations) + { + var catalog = CreateAssemblyCatalog(e); + + var proxy = new AssemblyCatalogDebuggerProxy(catalog); + + Assert.Same(catalog.Assembly, proxy.Assembly); + } + } + + private AssemblyCatalogDebuggerProxy CreateAssemblyDebuggerProxy(AssemblyCatalog catalog) + { + return new AssemblyCatalogDebuggerProxy(catalog); + } + + private AssemblyCatalog CreateAssemblyCatalog(Assembly assembly) + { + return new AssemblyCatalog(assembly); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/AssemblyCatalogTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/AssemblyCatalogTests.cs new file mode 100644 index 0000000000..b3d0ff6e98 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/AssemblyCatalogTests.cs @@ -0,0 +1,1064 @@ +// 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.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.IO; +using System.Linq; +using System.Reflection; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + // This is a glorious do nothing ReflectionContext + public class AssemblyCatalogTestsReflectionContext : ReflectionContext + { + public override Assembly MapAssembly(Assembly assembly) + { + return assembly; + } + + public override TypeInfo MapType(TypeInfo type) + { + return type; + } + } + + public class AssemblyCatalogTestsHelper + { + protected string GetAttributedAssemblyCodeBase() + { + return Assembly.GetExecutingAssembly().CodeBase; + } + + protected Assembly GetAttributedAssembly() + { + return Assembly.GetExecutingAssembly(); + } + + protected AssemblyCatalog CreateAssemblyCatalog() + { + return CreateAssemblyCatalog(GetAttributedAssembly()); + } + + protected AssemblyCatalog CreateAssemblyCatalog(Assembly assembly) + { + return new AssemblyCatalog(assembly); + } + + protected class DerivedAssemblyCatalog : AssemblyCatalog + { + public DerivedAssemblyCatalog(Assembly assembly) + : base(assembly) + { + } + } + } + + public class AssemblyCatalogConstructorTests : AssemblyCatalogTestsHelper + { + // Test Codebase variant of the APIs + public static void Constructor_ValueAsCodebaseArgument_ShouldSetAssemblyProperty(Func catalogCreator) + { + var expectations = Expectations.GetAssemblies(); + + foreach (var e in expectations) + { + var catalog = catalogCreator(e.CodeBase); + + Assert.Same(e, catalog.Assembly); + } + } + + public static void Constructor_LockedFileAsCodeBaseArgument_ShouldThrowFileLoad(Func catalogCreator) + { + string filename = Path.GetTempFileName(); + using (FileStream stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.None)) + { + Assert.Throws(() => + { + var catalog = catalogCreator(filename); + }); + } + } + + public static void Constructor_NullFileNameAsCodeBaseArgument_ShouldThrowArgumentNull(Func catalogCreator) + { + Assert.Throws("codeBase", () => + { + var catalog = catalogCreator((string)null); + }); + } + + public static void Constructor_EmptyFileNameAsCodeBaseArgument_ShouldThrowArgument(Func catalogCreator) + { + Assert.Throws("codeBase", () => + { + var catalog = catalogCreator(""); + }); + } + + public static void Constructor_InvalidFileNameAsCodeBaseArgument_ShouldThrowArgument(Func catalogCreator) + { + Assert.Throws(() => + { + var catalog = catalogCreator("??||>"); + }); + } + + public static void Constructor_DirectoryAsCodeBaseArgument_ShouldThrowFileLoad(Func catalogCreator) + { + string directory = Environment.GetFolderPath(Environment.SpecialFolder.System); + Assert.True(Directory.Exists(directory)); + + Assert.Throws(() => + { + var catalog = catalogCreator(directory); + }); + } + + public static void Constructor_TooLongFileNameAsCodeBaseArgument_ShouldThrowPathTooLong(Func catalogCreator) + { + Assert.Throws(() => + { + var catalog = catalogCreator(@"c:\This is a very long path\And Just to make sure\We will continue to make it very long\This is a very long path\And Just to make sure\We will continue to make it very long\This is a very long path\And Just to make sure\We will continue to make it very long\myassembly.dll"); + }); + } + + public static void Constructor_NonAssemblyFileNameAsCodeBaseArgument_ShouldThrowBadImageFormat(Func catalogCreator) + { + string filename = Path.GetTempFileName(); + Assert.Throws(() => + { + var catalog = catalogCreator(filename); + }); + } + + public static void Constructor_NonExistentFileNameAsCodeBaseArgument_ShouldThrowFileNotFound(Func catalogCreator) + { + Assert.Throws(() => + { + var catalog = catalogCreator(@"FileThat should not ever exist"); + }); + } + + // Test Assembly variant of the APIs + public static void Constructor_ValueAsAssemblyArgument_ShouldSetAssemblyProperty(Func catalogCreator) + { + var expectations = Expectations.GetAssemblies(); + + foreach (var e in expectations) + { + var catalog = catalogCreator(e); + + Assert.Same(e, catalog.Assembly); + } + } + + public static void Constructor_NullReflectionContextArgument_ShouldThrowArgumentNull(Func catalogCreator) + { + AssertExtensions.Throws("reflectionContext", () => + { + var catalog = catalogCreator(null); + }); + } + + public static void Constructor_NullDefinitionOriginArgument_ShouldThrowArgumentNull(Func catalogCreator) + { + AssertExtensions.Throws("definitionOrigin", () => + { + var catalog = catalogCreator(null); + }); + } + + //========================================================================================================================================= + // Test cases for AssemblyCatalog(string codebase) constructor + //========================================================================================================================================= + [Fact] + [ActiveIssue(25498)] + public void Constructor1_ValueAsCodebaseArgument_ShouldSetAssemblyProperty() + { + AssemblyCatalogConstructorTests.Constructor_ValueAsCodebaseArgument_ShouldSetAssemblyProperty((s) => + { + return new AssemblyCatalog(s); + }); + } + + [Fact] + public void Constructor1_LockedFileAsCodeBaseArgument_ShouldThrowFileLoad() + { + AssemblyCatalogConstructorTests.Constructor_LockedFileAsCodeBaseArgument_ShouldThrowFileLoad((s) => + { + return new AssemblyCatalog(s); + }); + } + + [Fact] + public void Constructor1_NullFileNameAsCodeBaseArgument_ShouldThrowArgumentNull() + { + AssemblyCatalogConstructorTests.Constructor_NullFileNameAsCodeBaseArgument_ShouldThrowArgumentNull((s) => + { + return new AssemblyCatalog(s); + }); + } + + [Fact] + public void Constructor1_EmptyFileNameAsCodeBaseArgument_ShouldThrowArgument() + { + AssemblyCatalogConstructorTests.Constructor_EmptyFileNameAsCodeBaseArgument_ShouldThrowArgument((s) => + { + return new AssemblyCatalog(s); + }); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] + public void Constructor1_InvalidFileNameAsCodeBaseArgument_ShouldThrowArgument() + { + AssemblyCatalogConstructorTests.Constructor_InvalidFileNameAsCodeBaseArgument_ShouldThrowArgument((s) => + { + return new AssemblyCatalog(s); + }); + } + + [Fact] + [ActiveIssue(25498)] + public void Constructor1_DirectoryAsCodeBaseArgument_ShouldThrowFileLoad() + { + AssemblyCatalogConstructorTests.Constructor_DirectoryAsCodeBaseArgument_ShouldThrowFileLoad((s) => + { + return new AssemblyCatalog(s); + }); + } + + [Fact] + [ActiveIssue(25498)] + public void Constructor1_TooLongFileNameAsCodeBaseArgument_ShouldThrowPathTooLong() + { + AssemblyCatalogConstructorTests.Constructor_TooLongFileNameAsCodeBaseArgument_ShouldThrowPathTooLong((s) => + { + return new AssemblyCatalog(s); + }); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // fault segmentation - AnyUnix + public void Constructor1_NonAssemblyFileNameAsCodeBaseArgument_ShouldThrowBadImageFormat() + { + AssemblyCatalogConstructorTests.Constructor_NonAssemblyFileNameAsCodeBaseArgument_ShouldThrowBadImageFormat((s) => + { + return new AssemblyCatalog(s); + }); + } + + [Fact] + public void Constructor1_NonExistentFileNameAsCodeBaseArgument_ShouldThrowFileNotFound() + { + AssemblyCatalogConstructorTests.Constructor_NonExistentFileNameAsCodeBaseArgument_ShouldThrowFileNotFound((s) => + { + return new AssemblyCatalog(s); + }); + } + + [Fact] + [ActiveIssue(25498)] + public void Constructor1_ShouldSetOriginToNull() + { + var catalog = (ICompositionElement)new AssemblyCatalog(GetAttributedAssemblyCodeBase()); + + Assert.Null(catalog.Origin); + } + + //========================================================================================================================================= + // Test cases for AssemblyCatalog(string codebase, ReflectionContext reflectionContext) constructor + //========================================================================================================================================= + + [Fact] + [ActiveIssue(25498)] + public void Constructor2_ValueAsCodebaseArgument_ShouldSetAssemblyProperty() + { + AssemblyCatalogConstructorTests.Constructor_ValueAsCodebaseArgument_ShouldSetAssemblyProperty((s) => + { + return new AssemblyCatalog(s, new AssemblyCatalogTestsReflectionContext()); + }); + } + + [Fact] + public void Constructor2_LockedFileAsCodeBaseArgument_ShouldThrowFileLoad() + { + AssemblyCatalogConstructorTests.Constructor_LockedFileAsCodeBaseArgument_ShouldThrowFileLoad((s) => + { + return new AssemblyCatalog(s, new AssemblyCatalogTestsReflectionContext()); + }); + } + + [Fact] + public void Constructor2_NullFileNameAsCodeBaseArgument_ShouldThrowArgumentNull() + { + AssemblyCatalogConstructorTests.Constructor_NullFileNameAsCodeBaseArgument_ShouldThrowArgumentNull((s) => + { + return new AssemblyCatalog(s, new AssemblyCatalogTestsReflectionContext()); + }); + } + + [Fact] + public void Constructor2_EmptyFileNameAsCodeBaseArgument_ShouldThrowArgument() + { + AssemblyCatalogConstructorTests.Constructor_EmptyFileNameAsCodeBaseArgument_ShouldThrowArgument((s) => + { + return new AssemblyCatalog(s, new AssemblyCatalogTestsReflectionContext()); + }); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] + public void Constructor2_InvalidFileNameAsCodeBaseArgument_ShouldThrowArgument() + { + AssemblyCatalogConstructorTests.Constructor_InvalidFileNameAsCodeBaseArgument_ShouldThrowArgument((s) => + { + return new AssemblyCatalog(s, new AssemblyCatalogTestsReflectionContext()); + }); + } + + [Fact] + [ActiveIssue(25498)] + public void Constructor2_DirectoryAsCodeBaseArgument_ShouldThrowFileLoad() + { + AssemblyCatalogConstructorTests.Constructor_DirectoryAsCodeBaseArgument_ShouldThrowFileLoad((s) => + { + return new AssemblyCatalog(s, new AssemblyCatalogTestsReflectionContext()); + }); + } + + [Fact] + [ActiveIssue(25498)] + public void Constructor2_TooLongFileNameAsCodeBaseArgument_ShouldThrowPathTooLong() + { + AssemblyCatalogConstructorTests.Constructor_TooLongFileNameAsCodeBaseArgument_ShouldThrowPathTooLong((s) => + { + return new AssemblyCatalog(s, new AssemblyCatalogTestsReflectionContext()); + }); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // fault segmentation - AnyUnix + public void Constructor2_NonAssemblyFileNameAsCodeBaseArgument_ShouldThrowBadImageFormat() + { + AssemblyCatalogConstructorTests.Constructor_NonAssemblyFileNameAsCodeBaseArgument_ShouldThrowBadImageFormat((s) => + { + return new AssemblyCatalog(s, new AssemblyCatalogTestsReflectionContext()); + }); + } + + [Fact] + public void Constructor2_NonExistentFileNameAsCodeBaseArgument_ShouldThrowFileNotFound() + { + AssemblyCatalogConstructorTests.Constructor_NonExistentFileNameAsCodeBaseArgument_ShouldThrowFileNotFound((s) => + { + return new AssemblyCatalog(s, new AssemblyCatalogTestsReflectionContext()); + }); + } + + [Fact] + public void Constructor2_NullReflectionContextArgument_ShouldThrowArgumentNull() + { + AssemblyCatalogConstructorTests.Constructor_NullReflectionContextArgument_ShouldThrowArgumentNull((rc) => + { + return new AssemblyCatalog(GetAttributedAssemblyCodeBase(), rc); + }); + } + + //========================================================================================================================================= + // Test cases for AssemblyCatalog(string codebase, ICompositionElement definitonOrigin) constructor + //========================================================================================================================================= + [Fact] + [ActiveIssue(25498)] + public void Constructor3_ValueAsCodebaseArgument_ShouldSetAssemblyProperty() + { + AssemblyCatalogConstructorTests.Constructor_ValueAsCodebaseArgument_ShouldSetAssemblyProperty((s) => + { + return new AssemblyCatalog(s, (ICompositionElement)new AssemblyCatalog(s)); + }); + } + + [Fact] + public void Constructor3_LockedFileAsCodeBaseArgument_ShouldThrowFileLoad() + { + AssemblyCatalogConstructorTests.Constructor_LockedFileAsCodeBaseArgument_ShouldThrowFileLoad((s) => + { + return new AssemblyCatalog(s, (ICompositionElement)new AssemblyCatalog(s)); + }); + } + + [Fact] + public void Constructor3_NullFileNameAsCodeBaseArgument_ShouldThrowArgumentNull() + { + AssemblyCatalogConstructorTests.Constructor_NullFileNameAsCodeBaseArgument_ShouldThrowArgumentNull((s) => + { + return new AssemblyCatalog(s, (ICompositionElement)new AssemblyCatalog(s)); + }); + } + + [Fact] + public void Constructor3_EmptyFileNameAsCodeBaseArgument_ShouldThrowArgument() + { + AssemblyCatalogConstructorTests.Constructor_EmptyFileNameAsCodeBaseArgument_ShouldThrowArgument((s) => + { + return new AssemblyCatalog(s, (ICompositionElement)new AssemblyCatalog(s)); + }); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] + public void Constructor3_InvalidFileNameAsCodeBaseArgument_ShouldThrowArgument() + { + AssemblyCatalogConstructorTests.Constructor_InvalidFileNameAsCodeBaseArgument_ShouldThrowArgument((s) => + { + return new AssemblyCatalog(s, (ICompositionElement)new AssemblyCatalog(s)); + }); + } + + [Fact] + [ActiveIssue(25498)] + public void Constructor3_DirectoryAsCodeBaseArgument_ShouldThrowFileLoad() + { + AssemblyCatalogConstructorTests.Constructor_DirectoryAsCodeBaseArgument_ShouldThrowFileLoad((s) => + { + return new AssemblyCatalog(s, (ICompositionElement)new AssemblyCatalog(s)); + }); + } + + [Fact] + [ActiveIssue(25498)] + public void Constructor3_TooLongFileNameAsCodeBaseArgument_ShouldThrowPathTooLong() + { + AssemblyCatalogConstructorTests.Constructor_TooLongFileNameAsCodeBaseArgument_ShouldThrowPathTooLong((s) => + { + return new AssemblyCatalog(s, (ICompositionElement)new AssemblyCatalog(s)); + }); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // fault segmentation - AnyUnix + public void Constructor3_NonAssemblyFileNameAsCodeBaseArgument_ShouldThrowBadImageFormat() + { + AssemblyCatalogConstructorTests.Constructor_NonAssemblyFileNameAsCodeBaseArgument_ShouldThrowBadImageFormat((s) => + { + return new AssemblyCatalog(s, (ICompositionElement)new AssemblyCatalog(s)); + }); + } + + [Fact] + public void Constructor3_NonExistentFileNameAsCodeBaseArgument_ShouldThrowFileNotFound() + { + AssemblyCatalogConstructorTests.Constructor_NonExistentFileNameAsCodeBaseArgument_ShouldThrowFileNotFound((s) => + { + return new AssemblyCatalog(s, (ICompositionElement)new AssemblyCatalog(s)); + }); + } + + [Fact] + public void Constructor3_NullDefinitionOriginArgument_ShouldThrowArgumentNull() + { + AssemblyCatalogConstructorTests.Constructor_NullDefinitionOriginArgument_ShouldThrowArgumentNull((dO) => + { + return new AssemblyCatalog(GetAttributedAssemblyCodeBase(), dO); + }); + } + + //========================================================================================================================================= + // Test cases for AssemblyCatalog(string codebase, ICompositionElement definitonOrigin, ReflectionContext reflectionContext) constructor + //========================================================================================================================================= + [Fact] + public void Constructor4_ValueAsCodebaseArgument_ShouldSetAssemblyProperty() + { + AssemblyCatalogConstructorTests.Constructor_ValueAsAssemblyArgument_ShouldSetAssemblyProperty((s) => + { + return new AssemblyCatalog(s, new AssemblyCatalogTestsReflectionContext(), (ICompositionElement)new AssemblyCatalog(s)); + }); + } + + [Fact] + public void Constructor4_LockedFileAsCodeBaseArgument_ShouldThrowFileLoad() + { + AssemblyCatalogConstructorTests.Constructor_LockedFileAsCodeBaseArgument_ShouldThrowFileLoad((s) => + { + return new AssemblyCatalog(s, new AssemblyCatalogTestsReflectionContext(), (ICompositionElement)new AssemblyCatalog(s)); + }); + } + + [Fact] + public void Constructor4_NullFileNameAsCodeBaseArgument_ShouldThrowArgumentNull() + { + AssemblyCatalogConstructorTests.Constructor_NullFileNameAsCodeBaseArgument_ShouldThrowArgumentNull((s) => + { + return new AssemblyCatalog(s, new AssemblyCatalogTestsReflectionContext(), (ICompositionElement)new AssemblyCatalog(s)); + }); + } + + [Fact] + public void Constructor4_EmptyFileNameAsCodeBaseArgument_ShouldThrowArgument() + { + AssemblyCatalogConstructorTests.Constructor_EmptyFileNameAsCodeBaseArgument_ShouldThrowArgument((s) => + { + return new AssemblyCatalog(s, new AssemblyCatalogTestsReflectionContext(), (ICompositionElement)new AssemblyCatalog(s)); + }); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] + public void Constructor4_InvalidFileNameAsCodeBaseArgument_ShouldThrowArgument() + { + AssemblyCatalogConstructorTests.Constructor_InvalidFileNameAsCodeBaseArgument_ShouldThrowArgument((s) => + { + return new AssemblyCatalog(s, new AssemblyCatalogTestsReflectionContext(), (ICompositionElement)new AssemblyCatalog(s)); + }); + } + + [Fact] + [ActiveIssue(25498)] + public void Constructor4_DirectoryAsCodeBaseArgument_ShouldThrowFileLoad() + { + AssemblyCatalogConstructorTests.Constructor_DirectoryAsCodeBaseArgument_ShouldThrowFileLoad((s) => + { + return new AssemblyCatalog(s, new AssemblyCatalogTestsReflectionContext(), (ICompositionElement)new AssemblyCatalog(s)); + }); + } + + [Fact] + [ActiveIssue(25498)] + public void Constructor4_TooLongFileNameAsCodeBaseArgument_ShouldThrowPathTooLong() + { + AssemblyCatalogConstructorTests.Constructor_TooLongFileNameAsCodeBaseArgument_ShouldThrowPathTooLong((s) => + { + return new AssemblyCatalog(s, new AssemblyCatalogTestsReflectionContext(), (ICompositionElement)new AssemblyCatalog(s)); + }); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // fault segmentation - AnyUnix + public void Constructor4_NonAssemblyFileNameAsCodeBaseArgument_ShouldThrowBadImageFormat() + { + AssemblyCatalogConstructorTests.Constructor_NonAssemblyFileNameAsCodeBaseArgument_ShouldThrowBadImageFormat((s) => + { + return new AssemblyCatalog(s, new AssemblyCatalogTestsReflectionContext(), (ICompositionElement)new AssemblyCatalog(s)); + }); + } + + [Fact] + public void Constructor4_NonExistentFileNameAsCodeBaseArgument_ShouldThrowFileNotFound() + { + AssemblyCatalogConstructorTests.Constructor_NonExistentFileNameAsCodeBaseArgument_ShouldThrowFileNotFound((s) => + { + return new AssemblyCatalog(s, new AssemblyCatalogTestsReflectionContext(), (ICompositionElement)new AssemblyCatalog(s)); + }); + } + + [Fact] + public void Constructor4_NullReflectionContextArgument_ShouldThrowArgumentNull() + { + AssemblyCatalogConstructorTests.Constructor_NullReflectionContextArgument_ShouldThrowArgumentNull((rc) => + { + return new AssemblyCatalog(GetAttributedAssemblyCodeBase(), rc, (ICompositionElement)new AssemblyCatalog(GetAttributedAssembly())); + }); + } + + [Fact] + public void Constructor4_NullDefinitionOriginArgument_ShouldThrowArgumentNull() + { + AssemblyCatalogConstructorTests.Constructor_NullDefinitionOriginArgument_ShouldThrowArgumentNull((dO) => + { + return new AssemblyCatalog(GetAttributedAssemblyCodeBase(), new AssemblyCatalogTestsReflectionContext(), dO); + }); + } + //========================================================================================================================================= + // Test cases for AssemblyCatalog(string codebase, ICompositionElement definitonOrigin) constructor + //========================================================================================================================================= + [Fact] + [ActiveIssue(25498)] + public void Constructor7_ValueAsAssemblyArgument_ShouldSetAssemblyProperty() + { + AssemblyCatalogConstructorTests.Constructor_ValueAsCodebaseArgument_ShouldSetAssemblyProperty((a) => + { + return new AssemblyCatalog(a, (ICompositionElement)new AssemblyCatalog(GetAttributedAssembly())); + }); + } + + //========================================================================================================================================= + // Test cases for AssemblyCatalog(Assembly assembly) constructor + //========================================================================================================================================= + [Fact] + public void Constructor5_ValueAsAssemblyArgument_ShouldSetAssemblyProperty() + { + AssemblyCatalogConstructorTests.Constructor_ValueAsAssemblyArgument_ShouldSetAssemblyProperty((a) => + { + return new AssemblyCatalog(a); + }); + } + + //========================================================================================================================================= + // Test cases for AssemblyCatalog(Assembly assembly, ReflectionContext reflectionContext) constructor + //========================================================================================================================================= + [Fact] + public void Constructor6_ValueAsAssemblyArgument_ShouldSetAssemblyProperty() + { + AssemblyCatalogConstructorTests.Constructor_ValueAsAssemblyArgument_ShouldSetAssemblyProperty((a) => + { + return new AssemblyCatalog(a, new AssemblyCatalogTestsReflectionContext()); + }); + } + + [Fact] + [ActiveIssue(25498)] + public void Constructor6_NullReflectionContextArgument_ShouldThrowArgumentNull() + { + AssemblyCatalogConstructorTests.Constructor_NullReflectionContextArgument_ShouldThrowArgumentNull((rc) => + { + return new AssemblyCatalog(GetAttributedAssembly(), rc); + }); + } + + [Fact] + [ActiveIssue(25498)] + public void Constructor7_NullDefinitionOriginArgument_ShouldThrowArgumentNull() + { + AssemblyCatalogConstructorTests.Constructor_NullDefinitionOriginArgument_ShouldThrowArgumentNull((dO) => + { + return new AssemblyCatalog(GetAttributedAssembly(), dO); + }); + } + + [Fact] + public void Constructor7_ShouldSetOriginToNull() + { + var catalog = (ICompositionElement)new AssemblyCatalog(GetAttributedAssembly()); + + Assert.Null(catalog.Origin); + } + + //========================================================================================================================================= + // Test cases for AssemblyCatalog(string codebase, ICompositionElement definitonOrigin, ReflectionContext reflectionContext) constructor + //========================================================================================================================================= + [Fact] + public void Constructor8_ValueAsAssemblyArgument_ShouldSetAssemblyProperty() + { + AssemblyCatalogConstructorTests.Constructor_ValueAsAssemblyArgument_ShouldSetAssemblyProperty((a) => + { + return new AssemblyCatalog(a, new AssemblyCatalogTestsReflectionContext(), (ICompositionElement)new AssemblyCatalog(GetAttributedAssembly())); + }); + } + + [Fact] + public void Constructor8_NullReflectionContextArgument_ShouldThrowArgumentNull() + { + AssemblyCatalogConstructorTests.Constructor_NullReflectionContextArgument_ShouldThrowArgumentNull((rc) => + { + return new AssemblyCatalog(GetAttributedAssemblyCodeBase(), rc, (ICompositionElement)new AssemblyCatalog(GetAttributedAssembly())); + }); + } + + [Fact] + public void Constructor8_NullDefinitionOriginArgument_ShouldThrowArgumentNull() + { + AssemblyCatalogConstructorTests.Constructor_NullDefinitionOriginArgument_ShouldThrowArgumentNull((dO) => + { + return new AssemblyCatalog(GetAttributedAssembly().CodeBase, new AssemblyCatalogTestsReflectionContext(), dO); + }); + } + + //========================================================================================================================================= + // Test cases for Assemblies decorated with the CatalogDiscoveryAttribute + //========================================================================================================================================= + + [Fact] + [ActiveIssue(25498)] + public void DiscoverCatalogUsingReflectionContextCatalogDiscoveryAttribute() + { + var catalog = new AssemblyCatalog(typeof(TestAssemblyOne).Assembly); + Assert.True(catalog.Parts.Count() > 0); + + foreach (ICompositionElement definition in catalog.Parts) + { + Assert.Same(catalog, definition.Origin); + } + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void DiscoverCatalogUsingDerivedReflectionContextCatalogDiscoveryAttribute() + { + var catalog = new AssemblyCatalog(typeof(TestAssemblyTwo).Assembly); + Assert.True(catalog.Parts.Count() > 0); + + foreach (ICompositionElement definition in catalog.Parts) + { + Assert.Same(catalog, definition.Origin); + } + } + + [Fact] + [ActiveIssue(25498)] + public void DiscoverCatalogUsingNoDefaultConstructorReflectionContextCatalogDiscoveryAttribute_ShouldThrowArgumentException() + { + AssertExtensions.Throws(() => + { + var catalog = new AssemblyCatalog(typeof(TestAssemblyThree).Assembly); + Assert.True(catalog.Parts.Count() > 0); + }, string.Empty); + } + + [Fact] + [ActiveIssue(25498)] + public void DiscoverCatalogUsingDerivedReflectionContextCatalogDiscoveryAttribute_ShouldThrowArgumentException() + { + + AssertExtensions.Throws(() => + { + var catalog = new AssemblyCatalog(typeof(TestAssemblyFour).Assembly); + Assert.True(catalog.Parts.Count() > 0); + }, string.Empty); + } + } + + public class AssemblyCatalogTests : AssemblyCatalogTestsHelper + { + [Fact] + public void Assembly_WhenCatalogDisposed_ShouldNotThrow() + { + var catalog = CreateAssemblyCatalog(); + catalog.Dispose(); + + Assert.NotNull(catalog.Assembly); + } + + [Fact] + public void ICompositionElementDisplayName_WhenCatalogDisposed_ShouldNotThrow() + { + var catalog = CreateAssemblyCatalog(); + catalog.Dispose(); + + var displayName = ((ICompositionElement)catalog).DisplayName; + } + + [Fact] + public void ICompositionElementOrigin_WhenCatalogDisposed_ShouldNotThrow() + { + var catalog = CreateAssemblyCatalog(); + catalog.Dispose(); + + var origin = ((ICompositionElement)catalog).Origin; + } + + [Fact] + public void ToString_WhenCatalogDisposed_ShouldNotThrow() + { + var catalog = CreateAssemblyCatalog(); + catalog.Dispose(); + + catalog.ToString(); + } + + [Fact] + public void Parts_WhenCatalogDisposed_ShouldThrowObjectDisposed() + { + var catalog = CreateAssemblyCatalog(); + catalog.Dispose(); + + ExceptionAssert.ThrowsDisposed(catalog, () => + { + var parts = catalog.Parts; + }); + } + + [Fact] + public void GetExports_WhenCatalogDisposed_ShouldThrowObjectDisposed() + { + var catalog = CreateAssemblyCatalog(); + catalog.Dispose(); + var definition = ImportDefinitionFactory.Create(); + + ExceptionAssert.ThrowsDisposed(catalog, () => + { + catalog.GetExports(definition); + }); + } + + [Fact] + [ActiveIssue(25498)] + public void GetExports_NullAsConstraintArgument_ShouldThrowArgumentNull() + { + var catalog = CreateAssemblyCatalog(); + + AssertExtensions.Throws("definition", () => + { + catalog.GetExports((ImportDefinition)null); + }); + } + + [Fact] + public void Dispose_ShouldNotThrow() + { + using (var catalog = CreateAssemblyCatalog()) + { + } + } + + [Fact] + public void Dispose_CanBeCalledMultipleTimes() + { + var catalog = CreateAssemblyCatalog(); + catalog.Dispose(); + catalog.Dispose(); + catalog.Dispose(); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void Parts() + { + var catalog = new AssemblyCatalog(typeof(AssemblyCatalogTests).Assembly); + Assert.NotNull(catalog.Parts); + Assert.True(catalog.Parts.Count() > 0); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void Parts_ShouldSetDefinitionOriginToCatalogItself() + { + var catalog = CreateAssemblyCatalog(); + Assert.True(catalog.Parts.Count() > 0); + + foreach (ICompositionElement definition in catalog.Parts) + { + Assert.Same(catalog, definition.Origin); + } + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void AddAssemblyUsingFile() + { + var catalog = new AssemblyCatalog(typeof(AssemblyCatalogTests).Assembly.Location); + var container = new CompositionContainer(catalog); + + Assert.NotNull(container.GetExportedValue()); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void TwoTypesWithSameSimpleName() + { + var catalog = new AssemblyCatalog(typeof(AssemblyCatalogTests).Assembly); + var container = new CompositionContainer(catalog); + + NotSoUniqueName unique1 = container.GetExportedValue(); + Assert.NotNull(unique1); + + Assert.Equal(23, unique1.MyIntProperty); + + NotSoUniqueName2.NotSoUniqueName nestedUnique = container.GetExportedValue(); + + Assert.NotNull(nestedUnique); + Assert.Equal("MyStringProperty", nestedUnique.MyStringProperty); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void GettingFunctionExports() + { + var catalog = new AssemblyCatalog(typeof(AssemblyCatalogTests).Assembly); + var container = new CompositionContainer(catalog); + + ImportDefaultFunctions import = container.GetExportedValue("ImportDefaultFunctions"); + import.VerifyIsBound(); + } + + [Fact] + [ActiveIssue(25498)] + public void AnExportOfAnInstanceThatFailsToCompose() + { + var catalog = new AssemblyCatalog(typeof(AssemblyCatalogTests).Assembly); + var container = new CompositionContainer(catalog); + + // Rejection causes the part in the catalog whose imports cannot be + // satisfied to be ignored, resulting in a cardinality mismatch instead of a + // composition exception + AssertExtensions.Throws(() => + { + container.GetExportedValue("ExportMyString"); + }, string.Empty); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void SharedPartCreation() + { + var catalog = new AssemblyCatalog(typeof(AssemblyCatalogTests).Assembly); + var container = new CompositionContainer(catalog); + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(new Int32Exporter(41)); + container.Compose(batch); + + var sharedPart1 = container.GetExportedValue(); + Assert.Equal(41, sharedPart1.Value); + var sharedPart2 = container.GetExportedValue(); + Assert.Equal(41, sharedPart2.Value); + + Assert.Equal(sharedPart1, sharedPart2); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void NonSharedPartCreation() + { + var catalog = new AssemblyCatalog(typeof(AssemblyCatalogTests).Assembly); + var container = new CompositionContainer(catalog); + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(new Int32Exporter(41)); + container.Compose(batch); + + var nonSharedPart1 = container.GetExportedValue(); + Assert.Equal(41, nonSharedPart1.Value); + var nonSharedPart2 = container.GetExportedValue(); + Assert.Equal(41, nonSharedPart2.Value); + + Assert.NotSame(nonSharedPart1, nonSharedPart2); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void RecursiveNonSharedPartCreation() + { + var catalog = new AssemblyCatalog(typeof(AssemblyCatalogTests).Assembly); + var container = new CompositionContainer(catalog); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotGetExportedValue, () => + { + container.GetExportedValue(); + }); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotGetExportedValue, () => + { + container.GetExportedValue(); + }); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotGetExportedValue, () => + { + container.GetExportedValue(); + }); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotGetExportedValue, () => + { + container.GetExportedValue(); + }); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotGetExportedValue, () => + { + container.GetExportedValue(); + }); + + Assert.NotNull(container.GetExportedValue()); + Assert.NotNull(container.GetExportedValue()); + Assert.NotNull(container.GetExportedValue()); + Assert.NotNull(container.GetExportedValue()); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // typeof(System.Reflection.ReflectionTypeLoadException) + public void RecursiveNonSharedPartCreationDisableSilentRejection() + { + var catalog = new AssemblyCatalog(typeof(AssemblyCatalogTests).Assembly); + var container = new CompositionContainer(catalog, CompositionOptions.DisableSilentRejection); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotGetExportedValue, () => + { + container.GetExportedValue(); + }); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotGetExportedValue, () => + { + container.GetExportedValue(); + }); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotGetExportedValue, () => + { + container.GetExportedValue(); + }); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotGetExportedValue, () => + { + container.GetExportedValue(); + }); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotGetExportedValue, () => + { + container.GetExportedValue(); + }); + + Assert.NotNull(container.GetExportedValue()); + Assert.NotNull(container.GetExportedValue()); + Assert.NotNull(container.GetExportedValue()); + Assert.NotNull(container.GetExportedValue()); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void TryToDiscoverExportWithGenericParameter() + { + var catalog = new AssemblyCatalog(typeof(AssemblyCatalogTests).Assembly); + var container = new CompositionContainer(catalog); + + // Should find a type that inherits from an export + Assert.NotNull(container.GetExportedValueOrDefault(AttributedModelServices.GetContractName(typeof(ExportWhichInheritsFromGeneric)))); + + // This should be exported because it is inherited by ExportWhichInheritsFromGeneric + Assert.NotNull(container.GetExportedValueOrDefault(AttributedModelServices.GetContractName(typeof(ExportWithGenericParameter)))); + } + + [Fact] + public void ICompositionElementDisplayName_ShouldIncludeCatalogTypeNameAndAssemblyFullName() + { + var expectations = Expectations.GetAssemblies(); + + foreach (var e in expectations) + { + var catalog = (ICompositionElement)CreateAssemblyCatalog(e); + + string expected = string.Format("AssemblyCatalog (Assembly=\"{0}\")", e.FullName); + + Assert.Equal(expected, catalog.DisplayName); + } + } + + [Fact] + public void ICompositionElementDisplayName_ShouldIncludeDerivedCatalogTypeNameAndAssemblyFullName() + { + var expectations = Expectations.GetAssemblies(); + + foreach (var e in expectations) + { + var catalog = (ICompositionElement)new DerivedAssemblyCatalog(e); + + string expected = string.Format("DerivedAssemblyCatalog (Assembly=\"{0}\")", e.FullName); + + Assert.Equal(expected, catalog.DisplayName); + } + } + + [Fact] + public void ToString_ShouldReturnICompositionElementDisplayName() + { + var expectations = Expectations.GetAssemblies(); + + foreach (var e in expectations) + { + var catalog = (ICompositionElement)CreateAssemblyCatalog(e); + + Assert.Equal(catalog.DisplayName, catalog.ToString()); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/CatalogExportProviderTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/CatalogExportProviderTests.cs new file mode 100644 index 0000000000..597a36014e --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/CatalogExportProviderTests.cs @@ -0,0 +1,541 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class ComposablePartCatalogExportProviderTests + { + [Fact] + public void Constructor_NullAsCatalogArgument_ShouldThrowArgumentNull() + { + Assert.Throws("catalog", () => + { + new CatalogExportProvider((ComposablePartCatalog)null); + }); + } + + [Fact] + public void CompositionOptionsInvalidValueCatalogExportProvider() + { + Assert.Throws("compositionOptions", + () => new CatalogExportProvider(new TypeCatalog(), (CompositionOptions)0x0400)); + } + + [Fact] + public void CompositionOptionsInvalidValueComposablePartExportProvider() + { + Assert.Throws("compositionOptions", + () => new ComposablePartExportProvider((CompositionOptions)0x0400)); + } + + [Fact] + public void Constructor_ValueAsCatalogArgument_ShouldSetCatalogPropertyToEmpty() + { + var expectations = Expectations.GetCatalogs(); + + foreach (var e in expectations) + { + var provider = new CatalogExportProvider(e); + + Assert.Same(e, provider.Catalog); + } + } + + [Fact] + public void Catalog_WhenDisposed_ShouldThrowObjectDisposed() + { + var provider = CreateCatalogExportProvider(); + provider.Dispose(); + + ExceptionAssert.ThrowsDisposed(provider, () => + { + var catalog = provider.Catalog; + }); + } + + [Fact] + public void SourceProvider_NullAsValueArgument_ShouldThrowArgumentNull() + { + var provider = CreateCatalogExportProvider(); + + Assert.Throws("value", () => + { + provider.SourceProvider = null; + }); + } + + [Fact] + public void GetExports_WhenRejectedDefinitionRequiredImportIsAdded_ShouldBeResurrected() + { + var part = PartFactory.CreateImporterExporter("Export", "Import"); + + var provider = CreateCatalogExportProvider(part); + var sourceProvider = ExportProviderFactory.CreateRecomposable(); + provider.SourceProvider = sourceProvider; + + var exports = provider.GetExports("Export"); + + Assert.Empty(exports); + + // Resurrect the definition + sourceProvider.AddExport("Import", new object()); + + exports = provider.GetExports("Export"); + + Assert.Equal(1, exports.Count()); + } + + [Fact] + public void GetExports_WhenMultipleRejectedDefinitionsRequiredImportsAreAdded_ShouldBeResurrected() + { + var part1 = PartFactory.CreateImporterExporter("Export", "Import"); + var part2 = PartFactory.CreateImporterExporter("Export", "Import"); + + var provider = CreateCatalogExportProvider(part1, part2); + var sourceProvider = ExportProviderFactory.CreateRecomposable(); + provider.SourceProvider = sourceProvider; + + var exports = provider.GetExports("Export"); + + Assert.Empty(exports); + + // Resurrect both definitions + sourceProvider.AddExport("Import", new object()); + + exports = provider.GetExports("Export"); + + Assert.Equal(2, exports.Count()); + } + + [Fact] + [ActiveIssue(743740)] + public void GetExports_AfterResurrectedDefinitionHasBeenRemovedAndReaddedToCatalog_ShouldNotBeTreatedAsRejected() + { + var definition1 = PartDefinitionFactory.Create(PartFactory.CreateImporterExporter("Export", "Import")); + var definition2 = PartDefinitionFactory.Create(PartFactory.CreateImporterExporter("Export", "Import")); + var catalog = CatalogFactory.CreateMutable(definition1, definition2); + + var provider = CreateCatalogExportProvider(catalog); + var sourceProvider = ExportProviderFactory.CreateRecomposable(); + provider.SourceProvider = sourceProvider; + + var exports = provider.GetExports("Export"); + + Assert.Empty(exports); + + // Resurrect both definitions + sourceProvider.AddExport("Import", new object()); + + exports = provider.GetExports("Export"); + + Assert.Equal(2, exports.Count()); + + catalog.RemoveDefinition(definition1); + + exports = provider.GetExports("Export"); + Assert.Equal(1, exports.Count()); + + catalog.AddDefinition(definition1); + + exports = provider.GetExports("Export"); + + Assert.Equal(2, exports.Count()); + } + + [Fact] + [Trait("Type", "Integration")] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void BasicTest() + { + var catalog = CatalogFactory.CreateDefaultAttributed(); + var catalogExportProvider = new CatalogExportProvider(catalog); + catalogExportProvider.SourceProvider = catalogExportProvider; + var testName = AttributedModelServices.GetContractName(typeof(CatalogComponentTest)); + var testNameNonComponent = AttributedModelServices.GetContractName(typeof(CatalogComponentTestNonComponentPart)); + var testInterfaceName = AttributedModelServices.GetContractName(typeof(ICatalogComponentTest)); + + Assert.Equal(1, catalogExportProvider.GetExports(ImportFromContract(testName)).Count()); + Assert.Equal(0, catalogExportProvider.GetExports(ImportFromContract(testNameNonComponent)).Count()); + + var exports = catalogExportProvider.GetExports(ImportFromContract(testInterfaceName)); + Assert.Equal(2, exports.Count()); + + foreach (var i in exports) + Assert.NotNull(i.Value); + + } + + [Fact] + [Trait("Type", "Integration")] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void BasicTestWithRequiredMetadata_NoTypeConstraint() + { + var catalog = CatalogFactory.CreateDefaultAttributed(); + var catalogExportProvider = new CatalogExportProvider(catalog); + catalogExportProvider.SourceProvider = catalogExportProvider; + + Assert.Equal(0, catalogExportProvider.GetExports(ImportFromContractAndMetadata("MyExporterWithNoFoo", new string[] { "Foo" }, new Type[] { typeof(object) })).Count()); + + Assert.Equal(1, catalogExportProvider.GetExports(ImportFromContractAndMetadata("MyExporterWithFoo", new string[] { "Foo" }, new Type[] { typeof(object) })).Count()); + Assert.Equal(0, catalogExportProvider.GetExports(ImportFromContractAndMetadata("MyExporterWithFoo", new string[] { "Foo", "Bar" }, new Type[] { typeof(object), typeof(object) })).Count()); + + Assert.Equal(0, catalogExportProvider.GetExports(ImportFromContractAndMetadata("MyExporterWithNoFoo", new string[] { "Foo" }, new Type[] { typeof(object) })).Count()); + Assert.Equal(1, catalogExportProvider.GetExports(ImportFromContractAndMetadata("MyExporterWithFoo", new string[] { "Foo" }, new Type[] { typeof(object) })).Count()); + } + + [Fact] + [Trait("Type", "Integration")] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void BasicTestWithRequiredMetadata_TypeConstraint() + { + var catalog = CatalogFactory.CreateDefaultAttributed(); + var catalogExportProvider = new CatalogExportProvider(catalog); + catalogExportProvider.SourceProvider = catalogExportProvider; + + Assert.Equal(0, catalogExportProvider.GetExports(ImportFromContractAndMetadata("MyExporterWithNoFoo", new string[] { "Foo" }, new Type[] { typeof(string) })).Count()); + + Assert.Equal(1, catalogExportProvider.GetExports(ImportFromContractAndMetadata("MyExporterWithFoo", new string[] { "Foo" }, new Type[] { typeof(string) })).Count()); + Assert.Equal(0, catalogExportProvider.GetExports(ImportFromContractAndMetadata("MyExporterWithFoo", new string[] { "Foo", "Bar" }, new Type[] { typeof(string), typeof(string) })).Count()); + + Assert.Equal(0, catalogExportProvider.GetExports(ImportFromContractAndMetadata("MyExporterWithNoFoo", new string[] { "Foo" }, new Type[] { typeof(string) })).Count()); + Assert.Equal(1, catalogExportProvider.GetExports(ImportFromContractAndMetadata("MyExporterWithFoo", new string[] { "Foo" }, new Type[] { typeof(string) })).Count()); + } + + [Fact] + [Trait("Type", "Integration")] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void BasicTestWithRequiredMetadata_WrongTypeConstraint() + { + var catalog = CatalogFactory.CreateDefaultAttributed(); + var catalogExportProvider = new CatalogExportProvider(catalog); + catalogExportProvider.SourceProvider = catalogExportProvider; + + Assert.Equal(0, catalogExportProvider.GetExports(ImportFromContractAndMetadata("MyExporterWithNoFoo", new string[] { "Foo" }, new Type[] { typeof(int) })).Count()); + + Assert.Equal(0, catalogExportProvider.GetExports(ImportFromContractAndMetadata("MyExporterWithFoo", new string[] { "Foo" }, new Type[] { typeof(int) })).Count()); + Assert.Equal(0, catalogExportProvider.GetExports(ImportFromContractAndMetadata("MyExporterWithFoo", new string[] { "Foo", "Bar" }, new Type[] { typeof(int), typeof(int) })).Count()); + + Assert.Equal(0, catalogExportProvider.GetExports(ImportFromContractAndMetadata("MyExporterWithNoFoo", new string[] { "Foo" }, new Type[] { typeof(int) })).Count()); + Assert.Equal(0, catalogExportProvider.GetExports(ImportFromContractAndMetadata("MyExporterWithFoo", new string[] { "Foo" }, new Type[] { typeof(int) })).Count()); + } + + [Fact] + [Trait("Type", "Integration")] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void ComponentCatalogResolverGetStaticExport() + { + var catalog = CatalogFactory.CreateDefaultAttributed(); + var catalogExportProvider = new CatalogExportProvider(catalog); + catalogExportProvider.SourceProvider = catalogExportProvider; + + var exports = catalogExportProvider.GetExports(ImportFromContract("StaticString")); + Assert.Equal(1, exports.Count()); + Assert.Equal("StaticString", exports.First().Value); + } + + [Fact] + [Trait("Type", "Integration")] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void ComponentCatalogResolverComponentCatalogExportReference() + { + var catalog = CatalogFactory.CreateDefaultAttributed(); + var catalogExportProvider = new CatalogExportProvider(catalog); + catalogExportProvider.SourceProvider = catalogExportProvider; + + var exports = catalogExportProvider.GetExports(ImportFromContract(AttributedModelServices.GetContractName(typeof(MyExporterWithValidMetadata)))); + + Assert.Equal(1, exports.Count()); + + var export = exports.First(); + Assert.Equal("world", export.Metadata["hello"]); + + Assert.IsType(export.Value); + } + + [Fact] + [Trait("Type", "Integration")] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void ValueTypeFromCatalog() + { + var catalog = CatalogFactory.CreateDefaultAttributed(); + var container = new CompositionContainer(catalog); + int singletonResult = container.GetExportedValue("{AssemblyCatalogResolver}SingletonValueType"); + Assert.Equal(17, singletonResult); + int factoryResult = container.GetExportedValue("{AssemblyCatalogResolver}FactoryValueType"); + Assert.Equal(18, factoryResult); + } + + [Export] + [PartCreationPolicy(CreationPolicy.Any)] + public class CreationPolicyAny + { + + } + + [Fact] + public void CreationPolicyAny_MultipleCallsReturnSameInstance() + { + var catalog = CatalogFactory.CreateAttributed(typeof(CreationPolicyAny)); + var provider = new CatalogExportProvider(catalog); + provider.SourceProvider = ContainerFactory.Create(); + + var export = provider.GetExportedValue(); + + for (int i = 0; i < 5; i++) // 5 is arbitrarily chosen + { + var export1 = provider.GetExportedValue(); + + Assert.Equal(export, export1); + } + } + + [Export] + [PartCreationPolicy(CreationPolicy.Shared)] + public class CreationPolicyShared + { + + } + + [Fact] + public void CreationPolicyShared_MultipleCallsReturnSameInstance() + { + var catalog = CatalogFactory.CreateAttributed(typeof(CreationPolicyShared)); + var provider = new CatalogExportProvider(catalog); + provider.SourceProvider = ContainerFactory.Create(); + + var export = provider.GetExportedValue(); + + for (int i = 0; i < 5; i++) // 5 is arbitrarily chosen + { + var export1 = provider.GetExportedValue(); + + Assert.Equal(export, export1); + } + } + + [Export] + [PartCreationPolicy(CreationPolicy.NonShared)] + public class CreationPolicyNonShared + { + + } + + [Fact] + [ActiveIssue(25498)] + public void CreationPolicyNonShared_MultipleCallsReturnsDifferentInstances() + { + var catalog = CatalogFactory.CreateAttributed(typeof(CreationPolicyNonShared)); + var provider = new CatalogExportProvider(catalog); + provider.SourceProvider = ContainerFactory.Create(); + + List list = new List(); + var export = provider.GetExportedValue(); + list.Add(export); + + for (int i = 0; i < 5; i++) // 5 is arbitrarily chosen + { + export = provider.GetExportedValue(); + + Assert.DoesNotContain(export, list); + Assert.Contains(export, list); + list.Add(export); + } + } + + [Fact] + [ActiveIssue(684514)] + public void GetExports_NoSourceProvider_ShouldThrowInvalidOperation() + { + var catalog = CatalogFactory.CreateAttributed(); + var provider = new CatalogExportProvider(catalog); + + ExceptionAssert.Throws(() => + provider.GetExports(ImportFromContract("Foo"))); + } + + [Fact] + [Trait("Type", "Integration")] + [ActiveIssue(561310)] + public void Recomposition_PartDefWithRecomposableImportIsRemoved_ExportsMatchingImportChanged_ShouldNotBeRecomposed() + { + string dependencyContractName = "dependency"; + var exportValue = new object(); + + var exporterPart = PartFactory.CreateExporter(dependencyContractName, exportValue); + var importerPart = PartFactory.CreateImporter(dependencyContractName, true); + + var exporterCatalog = CatalogFactory.Create(exporterPart); + var importerCatalog = CatalogFactory.Create(importerPart); + + var aggregateCatalog = CatalogFactory.CreateAggregateCatalog(importerCatalog, exporterCatalog); + + var provider = new CatalogExportProvider(aggregateCatalog); + provider.SourceProvider = provider; + + var exports = provider.GetExports(importerPart.ImportDefinitions.Single()); + Assert.Equal(exportValue, importerPart.Value); + + aggregateCatalog.Catalogs.Remove(importerCatalog); + aggregateCatalog.Catalogs.Remove(exporterCatalog); + + Assert.Equal(exportValue, importerPart.Value); + } + + [Fact] + [Trait("Type", "Integration")] + [ActiveIssue(561310)] + public void Recomposition_PartDefWithNonRecomposableImportIsRemoved_ExportsMatchingImportChanged_ShouldNotBeRejected() + { + string dependencyContractName = "dependency"; + var exportValue = new object(); + + var exporterPart = PartFactory.CreateExporter(dependencyContractName, exportValue); + var importerPart = PartFactory.CreateImporter(dependencyContractName, false); + + var exporterCatalog = CatalogFactory.Create(exporterPart); + var importerCatalog = CatalogFactory.Create(importerPart); + + var aggregateCatalog = CatalogFactory.CreateAggregateCatalog(importerCatalog, exporterCatalog); + + var provider = new CatalogExportProvider(aggregateCatalog); + provider.SourceProvider = provider; + + var exports = provider.GetExports(importerPart.ImportDefinitions.Single()); + Assert.Equal(exportValue, importerPart.Value); + + aggregateCatalog.Catalogs.Remove(importerCatalog); + aggregateCatalog.Catalogs.Remove(exporterCatalog); + + Assert.Equal(exportValue, importerPart.Value); + } + + [Fact] + [ActiveIssue(25498)] + public void CanBeCollectedAfterDispose() + { + AggregateExportProvider sourceExportProvider = new AggregateExportProvider(); + var catalog = new AggregateCatalog(CatalogFactory.CreateDefaultAttributed()); + var catalogExportProvider = new CatalogExportProvider(catalog); + catalogExportProvider.SourceProvider = sourceExportProvider; + + WeakReference weakCatalogExportProvider = new WeakReference(catalogExportProvider); + catalogExportProvider.Dispose(); + catalogExportProvider = null; + + GC.Collect(); + GC.WaitForPendingFinalizers(); + + Assert.False(weakCatalogExportProvider.IsAlive); + + GC.KeepAlive(sourceExportProvider); + GC.KeepAlive(catalog); + } + + [Fact] + public void RemovingAndReAddingMultipleDefinitionsFromCatalog() + { + var fixedParts = new TypeCatalog(typeof(RootMultipleImporter), typeof(ExportedService)); + var changingParts = new TypeCatalog(typeof(Exporter1), typeof(Exporter2)); + var catalog = new AggregateCatalog(); + catalog.Catalogs.Add(fixedParts); + catalog.Catalogs.Add(changingParts); + var catalogExportProvider = new CatalogExportProvider(catalog); + catalogExportProvider.SourceProvider = catalogExportProvider; + + var root = catalogExportProvider.GetExport().Value; + Assert.Equal(2, root.Imports.Length); + + catalog.Catalogs.Remove(changingParts); + Assert.Equal(0, root.Imports.Length); + + catalog.Catalogs.Add(changingParts); + Assert.Equal(2, root.Imports.Length); + } + + [Export] + public class RootMultipleImporter + { + [ImportMany(AllowRecomposition = true)] + public IExportedInterface[] Imports { get; set; } + } + + public interface IExportedInterface + { + } + + [Export(typeof(IExportedInterface))] + public class Exporter1 : IExportedInterface + { + [Import] + public ExportedService Service { get; set; } + } + + [Export(typeof(IExportedInterface))] + public class Exporter2 : IExportedInterface + { + [Import] + public ExportedService Service { get; set; } + } + + [Export] + public class ExportedService + { + } + + private static ImportDefinition ImportFromContract(string contractName) + { + return ImportDefinitionFactory.CreateDefault(contractName, + + ImportCardinality.ZeroOrMore, + false, + false); + } + + private static ImportDefinition ImportFromContractAndMetadata(string contractName, string[] metadataKeys, Type[] metadataValues) + { + Assert.Equal(metadataKeys.Length, metadataValues.Length); + Dictionary requiredMetadata = new Dictionary(); + for (int i = 0; i < metadataKeys.Length; i++) + { + requiredMetadata.Add(metadataKeys[i], metadataValues[i]); + } + + return new ContractBasedImportDefinition(contractName, + (string)null, + requiredMetadata, + ImportCardinality.ZeroOrMore, + false, + false, + CreationPolicy.Any); + } + + private static CatalogExportProvider CreateCatalogExportProvider() + { + return CreateCatalogExportProvider(CatalogFactory.Create()); + } + + private static CatalogExportProvider CreateCatalogExportProvider(params ComposablePartDefinition[] definitions) + { + return CreateCatalogExportProvider(CatalogFactory.Create(definitions)); + } + + private static CatalogExportProvider CreateCatalogExportProvider(params ComposablePart[] parts) + { + return CreateCatalogExportProvider(CatalogFactory.Create(parts)); + } + + private static CatalogExportProvider CreateCatalogExportProvider(ComposablePartCatalog catalog) + { + return new CatalogExportProvider(catalog); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/CompositionBatchTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/CompositionBatchTests.cs new file mode 100644 index 0000000000..5e2536e5c3 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/CompositionBatchTests.cs @@ -0,0 +1,860 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class CompositionBatchTests + { + [Fact] + public void Constructor1_PropertiesShouldBeSetAndEmpty() + { + CompositionBatch batch = new CompositionBatch(); + + Assert.NotNull(batch.PartsToAdd); + Assert.Empty(batch.PartsToAdd); + + Assert.NotNull(batch.PartsToRemove); + Assert.Empty(batch.PartsToRemove); + } + + [Fact] + public void Constructor2_PropertiesShouldBeSetAndMatchArguments() + { + ComposablePart[] partsToAdd = new ComposablePart[] { PartFactory.Create(), PartFactory.Create(), PartFactory.Create() }; + ComposablePart[] partsToRemove = new ComposablePart[] { PartFactory.Create(), PartFactory.Create(), PartFactory.Create() }; + + CompositionBatch batch = new CompositionBatch(partsToAdd, partsToRemove); + + Assert.NotNull(batch.PartsToAdd); + Assert.NotNull(batch.PartsToRemove); + + EqualityExtensions.CheckEquals(batch.PartsToAdd, partsToAdd); + EqualityExtensions.CheckEquals(batch.PartsToRemove, partsToRemove); + } + + [Fact] + public void Constructor2_PartsToAddAsNull_PartsToAddShouldBeEmpty() + { + ComposablePart[] partsToRemove = new ComposablePart[] { PartFactory.Create(), PartFactory.Create(), PartFactory.Create() }; + + var batch = new CompositionBatch(null, partsToRemove); + + Assert.Equal(0, batch.PartsToAdd.Count); + Assert.Equal(partsToRemove.Length, batch.PartsToRemove.Count); + } + + [Fact] + public void Constructor2_PartsToRemoveAsNull_PartsToRemoveShouldBeEmpty() + { + ComposablePart[] partsToAdd = new ComposablePart[] { PartFactory.Create(), PartFactory.Create(), PartFactory.Create() }; + + var batch = new CompositionBatch(partsToAdd, null); + + Assert.Equal(partsToAdd.Length, batch.PartsToAdd.Count); + Assert.Equal(0, batch.PartsToRemove.Count); + } + + [Fact] + public void Constructor2_PartsToAddHasNull_ShouldThrowArgumentNullException() + { + ComposablePart[] partsToAdd = new ComposablePart[] { PartFactory.Create(), null, PartFactory.Create() }; + ComposablePart[] partsToRemove = new ComposablePart[] { PartFactory.Create(), PartFactory.Create(), PartFactory.Create() }; + + Assert.Throws("partsToAdd", () => + { + new CompositionBatch(partsToAdd, partsToRemove); + }); + } + + [Fact] + public void Constructor2_PartsToRemoveHasNull_ShouldThrowArgumentNullException() + { + ComposablePart[] partsToAdd = new ComposablePart[] { PartFactory.Create(), PartFactory.Create(), PartFactory.Create() }; + ComposablePart[] partsToRemove = new ComposablePart[] { PartFactory.Create(), null, PartFactory.Create() }; + + Assert.Throws("partsToRemove", () => + { + new CompositionBatch(partsToAdd, partsToRemove); + }); + } + + [Fact] + public void AddPart_PartIsInPartsToAdd() + { + CompositionBatch batch = new CompositionBatch(); + ComposablePart part = PartFactory.Create(); + + batch.AddPart(part); + + Assert.Equal(1, batch.PartsToAdd.Count); + Assert.Same(part, batch.PartsToAdd[0]); + + Assert.Empty(batch.PartsToRemove); + } + + [Fact] + public void AddPart_PartAsNull_ShouldThrowArgumentNullException() + { + CompositionBatch batch = new CompositionBatch(); + + Assert.Throws("part", () => + { + batch.AddPart(null); + }); + } + + [Fact] + public void RemovePart_PartIsInPartsToRemove() + { + CompositionBatch batch = new CompositionBatch(); + ComposablePart part = PartFactory.Create(); + + batch.RemovePart(part); + + Assert.Equal(1, batch.PartsToRemove.Count); + Assert.Same(part, batch.PartsToRemove[0]); + + Assert.Empty(batch.PartsToAdd); + } + + [Fact] + public void RemovePart_PartAsNull_ShouldThrowArgumentNullException() + { + CompositionBatch batch = new CompositionBatch(); + + Assert.Throws("part", () => + { + batch.RemovePart(null); + }); + } + + [Fact] + public void PartsToAdd_ShouldGetCopiedAfterAdd() + { + CompositionBatch batch = new CompositionBatch(); + ComposablePart part1 = PartFactory.Create(); + ComposablePart part2 = PartFactory.Create(); + + batch.AddPart(part1); + Assert.True(batch.PartsToAdd.Contains(part1)); + + ReadOnlyCollection partsToAddBeforeCopy = batch.PartsToAdd; + Assert.Same(partsToAddBeforeCopy, batch.PartsToAdd); + + Assert.Equal(1, partsToAddBeforeCopy.Count); + Assert.True(partsToAddBeforeCopy.Contains(part1)); + + batch.AddPart(part2); + + ReadOnlyCollection partsToAddAfterCopy = batch.PartsToAdd; + Assert.Same(partsToAddAfterCopy, batch.PartsToAdd); + + Assert.Equal(2, partsToAddAfterCopy.Count); + Assert.True(partsToAddAfterCopy.Contains(part1)); + Assert.True(partsToAddAfterCopy.Contains(part2)); + Assert.NotSame(partsToAddBeforeCopy, partsToAddAfterCopy); + } + + [Fact] + public void PartsToRemove_ShouldGetCopiedAfterRemove() + { + CompositionBatch batch = new CompositionBatch(); + ComposablePart part1 = PartFactory.Create(); + ComposablePart part2 = PartFactory.Create(); + + batch.RemovePart(part1); + Assert.True(batch.PartsToRemove.Contains(part1)); + + ReadOnlyCollection partsToRemoveBeforeCopy = batch.PartsToRemove; + Assert.Same(partsToRemoveBeforeCopy, batch.PartsToRemove); + + Assert.Equal(1, partsToRemoveBeforeCopy.Count); + Assert.True(partsToRemoveBeforeCopy.Contains(part1)); + + batch.RemovePart(part2); + + ReadOnlyCollection partsToRemoveAfterCopy = batch.PartsToRemove; + Assert.Same(partsToRemoveAfterCopy, batch.PartsToRemove); + + Assert.Equal(2, partsToRemoveAfterCopy.Count); + Assert.True(partsToRemoveAfterCopy.Contains(part1)); + Assert.True(partsToRemoveAfterCopy.Contains(part2)); + Assert.NotSame(partsToRemoveBeforeCopy, partsToRemoveAfterCopy); + } + + [Fact] + public void AddExportedValue_NullAsContractNameArgument_ShouldThrowArgumentNull() + { + CompositionBatch batch = new CompositionBatch(); + + Assert.Throws("contractName", () => + { + batch.AddExportedValue((string)null, "Value"); + }); + } + + [Fact] + public void AddExportedValue_EmptyStringAsContractNameArgument_ShouldThrowArgument() + { + CompositionBatch batch = new CompositionBatch(); + + Assert.Throws("contractName", () => + { + batch.AddExportedValue("", "Value"); + }); + } + + [Fact] + public void AddExport_NullAsExportArgument_ShouldThrowArgumentNull() + { + CompositionBatch batch = new CompositionBatch(); + + Assert.Throws("export", () => + { + batch.AddExport((Export)null); + }); + } + + [Fact] + public void AddExport_ExportWithNullExportedValueAsExportArgument_CanBeExported() + { + CompositionBatch batch = new CompositionBatch(); + var export = ExportFactory.Create("Contract", (object)null); + + batch.AddExport(export); + Assert.Equal(1, batch.PartsToAdd.Count); + + var result = this.GetSingleExport(batch.PartsToAdd[0], "Contract"); + + Assert.NotNull(result); + Assert.Null(result.Value); + } + + [Fact] + public void AddExportedValueOfT_NullAsExportedValueArgument_CanBeExported() + { + CompositionBatch batch = new CompositionBatch(); + + batch.AddExportedValue((string)null); + + Assert.Equal(1, batch.PartsToAdd.Count); + var result = this.GetSingleLazy(batch.PartsToAdd[0]); + + Assert.NotNull(result); + Assert.Null(result.Value); + } + + [Fact] + public void AddExportedValue_NullAsExportedValueArgument_CanBeExported() + { + CompositionBatch batch = new CompositionBatch(); + + batch.AddExportedValue("Contract", (string)null); + + Assert.Equal(1, batch.PartsToAdd.Count); + var result = this.GetSingleExport(batch.PartsToAdd[0], "Contract"); + + Assert.NotNull(result); + Assert.Null(result.Value); + } + + [Fact] + public void AddExport_ExportWithEmptyMetadata_IsExportedWithEmptyMetadata() + { + CompositionBatch batch = new CompositionBatch(); + var export = ExportFactory.Create("Contract", "Value", new Dictionary()); + + Assert.Equal(0, export.Metadata.Count); + + batch.AddExport(export); + Assert.Equal(1, batch.PartsToAdd.Count); + + var result = this.GetSingleExport(batch.PartsToAdd[0], "Contract"); + + Assert.Equal(0, result.Metadata.Count); + } + + [Fact] + public void AddExportedValueOfT_IsExportedWithEmptyMetadata() + { + CompositionBatch batch = new CompositionBatch(); + + batch.AddExportedValue("Value"); + Assert.Equal(1, batch.PartsToAdd.Count); + + var result = this.GetSingleLazy(batch.PartsToAdd[0]); + + Assert.Equal(1, result.Metadata.Count); // contains type identity + } + + [Fact] + public void AddExportedValue_IsExportedWithEmptyMetadata() + { + CompositionBatch batch = new CompositionBatch(); + + batch.AddExportedValue("Contract", "Value"); + Assert.Equal(1, batch.PartsToAdd.Count); + + var result = this.GetSingleExport(batch.PartsToAdd[0], "Contract"); + + Assert.Equal(1, result.Metadata.Count); // contains type identity + } + + [Fact] + public void AddExport_ReturnedComposablePart_IsInAddedPartsCollection() + { + CompositionBatch batch = new CompositionBatch(); + + var export = ExportFactory.Create("Contract", "Value"); + var part = batch.AddExport(export); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Equal("Value", this.GetSingleExport(batch.PartsToAdd[0], "Contract").Value); + Assert.True(batch.PartsToAdd.Contains(part)); + } + + [Fact] + public void AddExportedValueOfT_ReturnedComposablePart_IsInAddedPartsCollection() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddExportedValue("Value"); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Equal("Value", this.GetSingleLazy(batch.PartsToAdd[0]).Value); + Assert.True(batch.PartsToAdd.Contains(part)); + } + + [Fact] + public void AddExportedValue_ReturnedComposablePart_IsInAddedPartsCollection() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddExportedValue("Contract", "Value"); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Equal("Value", this.GetSingleExport(batch.PartsToAdd[0], "Contract").Value); + Assert.True(batch.PartsToAdd.Contains(part)); + } + + [Fact] + public void AddExportedValueOfT_ExportAsExportedValueArgument_ShouldBeWrappedInExport() + { + CompositionBatch batch = new CompositionBatch(); + var export = ExportFactory.Create("Contract", "Value"); + + batch.AddExportedValue(export); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Same(export, this.GetSingleLazy(batch.PartsToAdd[0]).Value); + } + + [Fact] + public void AddExportedValue_ExportAsExportedValueArgument_ShouldBeWrappedInExport() + { + CompositionBatch batch = new CompositionBatch(); + var export = ExportFactory.Create("Contract", "Value"); + + batch.AddExportedValue(export); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Same(export, this.GetSingleLazy(batch.PartsToAdd[0]).Value); + } + + [Fact] + public void AddExport_ReturnedComposablePart_NullAsDefinitionArgumentToGetExportedValue_ShouldThrowArgumentNull() + { + CompositionBatch batch = new CompositionBatch(); + var export = ExportFactory.Create("Contract", "Value"); + + var part = batch.AddExport(export); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("definition", () => + { + part.GetExportedValue((ExportDefinition)null); + }); + } + + [Fact] + public void AddExport_ReturnedComposablePart_WrongDefinitionAsDefinitionArgumentToGetExportedValue_ShouldThrowArgument() + { + CompositionBatch batch = new CompositionBatch(); + var export = ExportFactory.Create("Contract", "Value"); + + var part = batch.AddExport(export); + var definition = ExportDefinitionFactory.Create(); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("definition", () => + { + part.GetExportedValue(definition); + }); + } + + [Fact] + public void AddExport_ReturnedComposablePart_NullAsDefinitionArgumentToSetImports_ShouldThrowArgumentNull() + { + CompositionBatch batch = new CompositionBatch(); + var export = ExportFactory.Create("Contract", "Value"); + + var part = batch.AddExport(export); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("definition", () => + { + part.SetImport((ImportDefinition)null, Enumerable.Empty()); + }); + } + + [Fact] + public void AddExport_ReturnedComposablePart_NullAsExportsArgumentToSetImports_ShouldThrowArgumentNull() + { + CompositionBatch batch = new CompositionBatch(); + var export = ExportFactory.Create("Contract", "Value"); + + var part = batch.AddExport(export); + var definition = ImportDefinitionFactory.Create(); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("exports", () => + { + part.SetImport(definition, (IEnumerable)null); + }); + } + + [Fact] + public void AddExport_ReturnedComposablePart_ExportsArrayWithNullElementAsExportsArgumentToSetImports_ShouldThrowArgument() + { + CompositionBatch batch = new CompositionBatch(); + var export = ExportFactory.Create("Contract", "Value"); + + var part = batch.AddExport(export); + var definition = ImportDefinitionFactory.Create(); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("exports", () => + { + part.SetImport(definition, new Export[] { null }); + }); + } + + [Fact] + public void AddExport_ReturnedComposablePart_SetImports_ShouldThrowArgument() + { + CompositionBatch batch = new CompositionBatch(); + var export = ExportFactory.Create("Contract", "Value"); + + var part = batch.AddExport(export); + var definition = ImportDefinitionFactory.Create(); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("definition", () => + { + part.SetImport(definition, Enumerable.Empty()); + }); + } + + [Fact] + public void AddExport_ReturnedComposablePart_ContainsExportDefinitionRepresentingExport() + { + var metadata = new Dictionary(); + metadata["Name"] = "Value"; + + CompositionBatch batch = new CompositionBatch(); + var export = ExportFactory.Create("Contract", "Value", metadata); + + var part = batch.AddExport(export); + Assert.Equal(1, batch.PartsToAdd.Count); + + var definition = part.ExportDefinitions.Single(); + + Assert.Equal("Contract", definition.ContractName); + Assert.Equal("Value", part.GetExportedValue(definition)); + EnumerableAssert.AreEqual(metadata, definition.Metadata); + } + + [Fact] + public void AddExportedValueOfT_ReturnedComposablePart_ImportDefinitionsPropertyIsEmpty() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddExportedValue("Value"); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Equal(0, part.ImportDefinitions.Count()); + } + + [Fact] + public void AddExportedValueOfT_ReturnedComposablePart_MetadataPropertyIsEmpty() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddExportedValue("Value"); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Equal(0, part.Metadata.Count); + } + + [Fact] + public void AddExportedValueOfT_ReturnedComposablePart_NullAsDefinitionArgumentToGetExportedValue_ShouldThrowArgumentNull() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddExportedValue("Value"); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("definition", () => + { + part.GetExportedValue((ExportDefinition)null); + }); + } + + [Fact] + public void AddExportedValueOfT_ReturnedComposablePart_WrongDefinitionAsDefinitionArgumentToGetExportedValue_ShouldThrowArgument() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddExportedValue("Value"); + var definition = ExportDefinitionFactory.Create(); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("definition", () => + { + part.GetExportedValue(definition); + }); + } + + [Fact] + public void AddExportedValueOfT_ReturnedComposablePart_NullAsDefinitionArgumentToSetImports_ShouldThrowArgumentNull() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddExportedValue("Value"); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("definition", () => + { + part.SetImport((ImportDefinition)null, Enumerable.Empty()); + }); + } + + [Fact] + public void AddExportedValueOfT_ReturnedComposablePart_NullAsExportsArgumentToSetImports_ShouldThrowArgumentNull() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddExportedValue("Value"); + var definition = ImportDefinitionFactory.Create(); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("exports", () => + { + part.SetImport(definition, (IEnumerable)null); + }); + } + + [Fact] + public void AddExportedValueOfT_ReturnedComposablePart_ExportsArrayWithNullElementAsExportsArgumentToSetImports_ShouldThrowArgument() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddExportedValue("Value"); + var definition = ImportDefinitionFactory.Create(); + + Assert.Throws("exports", () => + { + part.SetImport(definition, new Export[] { null }); + }); + } + + [Fact] + public void AddExportedValueOfT_ReturnedComposablePart_SetImports_ShouldThrowArgument() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddExportedValue("Value"); + var definition = ImportDefinitionFactory.Create(); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("definition", () => + { + part.SetImport(definition, Enumerable.Empty()); + }); + } + + [Fact] + public void AddExportedValueOfT_ReturnedComposablePart_ContainsExportDefinitionRepresentingExport() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddExportedValue("Value"); + Assert.Equal(1, batch.PartsToAdd.Count); + + var definition = part.ExportDefinitions.Single(); + + Assert.Equal(NameForType(), definition.ContractName); + Assert.Equal("Value", part.GetExportedValue(definition)); + Assert.Equal(1, definition.Metadata.Count); // contains type identity + } + + [Fact] + public void AddExportedValue_ReturnedComposablePart_ImportDefinitionsPropertyIsEmpty() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddExportedValue("Contract", "Value"); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Equal(0, part.ImportDefinitions.Count()); + } + + [Fact] + public void AddExportedValue_ReturnedComposablePart_MetadataPropertyIsEmpty() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddExportedValue("Contract", "Value"); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Equal(0, part.Metadata.Count); + } + + [Fact] + public void AddExportedValue_ReturnedComposablePart_NullAsDefinitionArgumentToGetExportedValue_ShouldThrowArgumentNull() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddExportedValue("Contract", "Value"); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("definition", () => + { + part.GetExportedValue((ExportDefinition)null); + }); + } + + [Fact] + public void AddExportedValue_ReturnedComposablePart_WrongDefinitionAsDefinitionArgumentToGetExportedValue_ShouldThrowArgument() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddExportedValue("Contract", "Value"); + var definition = ExportDefinitionFactory.Create(); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("definition", () => + { + part.GetExportedValue(definition); + }); + } + + [Fact] + public void AddExportedValue_ReturnedComposablePart_NullAsDefinitionArgumentToSetImports_ShouldThrowArgumentNull() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddExportedValue("Contract", "Value"); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("definition", () => + { + part.SetImport((ImportDefinition)null, Enumerable.Empty()); + }); + } + + [Fact] + public void AddExportedValue_ReturnedComposablePart_NullAsExportsArgumentToSetImports_ShouldThrowArgumentNull() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddExportedValue("Contract", "Value"); + var definition = ImportDefinitionFactory.Create(); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("exports", () => + { + part.SetImport(definition, (IEnumerable)null); + }); + } + + [Fact] + public void AddExportedValue_ReturnedComposablePart_ExportsArrayWithNullElementAsExportsArgumentToSetImports_ShouldThrowArgument() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddExportedValue("Contract", "Value"); + var definition = ImportDefinitionFactory.Create(); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("exports", () => + { + part.SetImport(definition, new Export[] { null }); + }); + } + + [Fact] + public void AddExportedValue_ReturnedComposablePart_WrongDefinitionAsDefinitionArgumentToSetImports_ShouldThrowArgument() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddExportedValue("Contract", "Value"); + var definition = ImportDefinitionFactory.Create(); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("definition", () => + { + part.SetImport(definition, Enumerable.Empty()); + }); + } + + [Fact] + public void AddExportedValue_ReturnedComposablePart_ContainsExportDefinitionRepresentingExport() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddExportedValue("Contract", "Value"); + Assert.Equal(1, batch.PartsToAdd.Count); + + var definition = part.ExportDefinitions.Single(); + + Assert.Equal("Contract", definition.ContractName); + Assert.Equal("Value", part.GetExportedValue(definition)); + Assert.Equal(1, definition.Metadata.Count); // containts type identity + } + + [Fact] + public void AddPart_Int32ValueTypeAsAttributedPartArgument_ShouldThrowArgument() + { + CompositionBatch batch = new CompositionBatch(); + + Assert.Throws("attributedPart", () => + { + batch.AddPart((object)10); + }); + } + + [Fact] + public void AddPart_ReturnedComposablePart_NullAsDefinitionArgumentToGetExportedValue_ShouldThrowArgumentNull() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddPart(new Int32Importer()); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("definition", () => + { + part.GetExportedValue((ExportDefinition)null); + }); + } + + [Fact] + public void AddPart_ReturnedComposablePart_WrongDefinitionAsDefinitionArgumentToGetExportedValue_ShouldThrowArgument() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddPart(new Int32Importer()); + var definition = ExportDefinitionFactory.Create(); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("definition", () => + { + part.GetExportedValue(definition); + }); + } + + [Fact] + public void AddPart_ReturnedComposablePart_NullAsDefinitionArgumentToSetImports_ShouldThrowArgumentNull() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddPart(new Int32Importer()); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("definition", () => + { + part.SetImport((ImportDefinition)null, Enumerable.Empty()); + }); + } + + [Fact] + public void AddPart_ReturnedComposablePart_NullAsExportsArgumentToSetImports_ShouldThrowArgumentNull() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddPart(new Int32Importer()); + var definition = ImportDefinitionFactory.Create(); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("exports", () => + { + part.SetImport(definition, (IEnumerable)null); + }); + } + + [Fact] + public void AddPart_ReturnedComposablePart_ExportsArrayWithNullElementAsExportsArgumentToSetImports_ShouldThrowArgument() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddPart(new Int32Importer()); + var definition = part.ImportDefinitions.First(); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("exports", () => + { + part.SetImport(definition, new Export[] { null }); + }); + } + + [Fact] + public void AddPart_ReturnedComposablePart_WrongDefinitionAsDefinitionArgumentToSetImports_ShouldThrowArgument() + { + CompositionBatch batch = new CompositionBatch(); + + var part = batch.AddPart(new Int32Importer()); + var definition = ImportDefinitionFactory.Create(); + Assert.Equal(1, batch.PartsToAdd.Count); + + Assert.Throws("definition", () => + { + part.SetImport(definition, Enumerable.Empty()); + }); + } + + private Export GetSingleLazy(ComposablePart part) + { + return this.GetSingleExport(part, AttributedModelServices.GetContractName(typeof(T))); + } + + private Export GetSingleExport(ComposablePart part, string contractName) + { + Assert.NotNull(part); + Assert.Equal(0, part.Metadata.Count); + Assert.Equal(1, part.ExportDefinitions.Count()); + Assert.Equal(0, part.ImportDefinitions.Count()); + ExportDefinition exportDefinition = part.ExportDefinitions.First(); + Assert.Equal(contractName, exportDefinition.ContractName); + + part.Activate(); + + return new Export(exportDefinition, () => part.GetExportedValue(exportDefinition)); + } + + private static string NameForType() + { + return AttributedModelServices.GetContractName(typeof(T)); + } + } + +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/CompositionTransactionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/CompositionTransactionTests.cs new file mode 100644 index 0000000000..9d754dd254 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/CompositionTransactionTests.cs @@ -0,0 +1,508 @@ +// 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.Collections.Generic; +using System.Text; +using Xunit; + +namespace System.ComponentModel.Composition.Hosting +{ + public class AtomicCompositionTests + { + [Fact] + public void Constructor1() + { + var ct = new AtomicComposition(); + } + + [Fact] + public void Constructor2() + { + // Null should be allowed + var ct = new AtomicComposition(null); + + // Another AtomicComposition should be allowed + var ct2 = new AtomicComposition(ct); + } + + [Fact] + public void Constructor2_MultipleTimes() + { + var outer = new AtomicComposition(); + + var ct1 = new AtomicComposition(outer); + + Assert.Throws(() => new AtomicComposition(outer)); + } + + [Fact] + public void Dispose_AllMethodsShouldThrow() + { + var ct = new AtomicComposition(); + + ct.Dispose(); + + Assert.Throws(() => ct.AddCompleteAction(() => ct = null)); + Assert.Throws(() => ct.Complete()); + Assert.Throws(() => ct.SetValue(ct, 10)); + object value; + Assert.Throws(() => ct.TryGetValue(ct, out value)); + } + + [Fact] + public void AfterComplete_AllMethodsShouldThrow() + { + var ct = new AtomicComposition(); + + ct.Complete(); + + Assert.Throws(() => ct.AddCompleteAction(() => ct = null)); + Assert.Throws(() => ct.Complete()); + Assert.Throws(() => ct.SetValue(ct, 10)); + object value; + Assert.Throws(() => ct.TryGetValue(ct, out value)); + } + + [Fact] + public void SetValue_ToNull_ShouldBeAllowed() + { + var ct = new AtomicComposition(); + + ct.SetValue(ct, null); + + object value = new object(); + + Assert.True(ct.TryGetValue(ct, out value)); + Assert.Null(value); + } + + [Fact] + public void SetValue_ValueType_ShouldBeAllowed() + { + var ct = new AtomicComposition(); + + ct.SetValue(ct, 45); + + int value; + + Assert.True(ct.TryGetValue(ct, out value)); + Assert.Equal(45, value); + } + + [Fact] + public void SetValue_Reference_ShouldBeAllowed() + { + var ct = new AtomicComposition(); + + var sb = new StringBuilder(); + ct.SetValue(ct, sb); + + StringBuilder value; + + Assert.True(ct.TryGetValue(ct, out value)); + Assert.Equal(sb, value); + } + + [Fact] + public void SetValue_CauseResize_ShouldWorkFine() + { + var ct = new AtomicComposition(); + + var keys = new List(); + var values = new List(); + + for (int i = 0; i < 20; i++) + { + var key = new object(); + keys.Add(key); + values.Add(i); + ct.SetValue(key, i); + } + + for (int i = 0; i < keys.Count; i++) + { + object value; + Assert.True(ct.TryGetValue(keys[i], out value)); + Assert.Equal(i, value); + } + } + + [Fact] + public void SetValue_ChangeOuterValuesWhileHaveInner_ShouldThrow() + { + var ct = new AtomicComposition(); + + var ct2 = new AtomicComposition(ct); + + var key = new object(); + Assert.Throws(() => ct.SetValue(key, 1)); + + object value; + Assert.False(ct2.TryGetValue(key, out value)); + Assert.False(ct.TryGetValue(key, out value)); + + // remove the inner atomicComposition so the outer one becomes unlocked. + ct2.Dispose(); + + ct.SetValue(key, 2); + Assert.True(ct.TryGetValue(key, out value)); + Assert.Equal(2, value); + } + + [Fact] + public void Complete_ShouldExecuteActions() + { + bool executedAction = false; + + var ct = new AtomicComposition(); + + ct.AddCompleteAction(() => executedAction = true); + + ct.Complete(); + + Assert.True(executedAction); + } + + [Fact] + public void Complete_ShouldCopyActionsToInner() + { + bool executedAction = false; + + var innerAtomicComposition = new AtomicComposition(); + + using (var ct = new AtomicComposition(innerAtomicComposition)) + { + ct.AddCompleteAction(() => executedAction = true); + + ct.Complete(); + Assert.False(executedAction, "Action should not have been exectued yet"); + } + + innerAtomicComposition.Complete(); + Assert.True(executedAction); + } + + [Fact] + public void Complete_ShouldCopyValuesToInner() + { + var innerAtomicComposition = new AtomicComposition(); + + object value; + using (var ct = new AtomicComposition(innerAtomicComposition)) + { + ct.SetValue(this, 21); + + Assert.False(innerAtomicComposition.TryGetValue(this, out value)); + + ct.Complete(); + + Assert.True(innerAtomicComposition.TryGetValue(this, out value)); + Assert.Equal(21, value); + } + + // reverify after dispose + Assert.True(innerAtomicComposition.TryGetValue(this, out value)); + Assert.Equal(21, value); + } + + [Fact] + public void NoComplete_ShouldNotCopyActionsToInner() + { + bool executedAction = false; + + var innerAtomicComposition = new AtomicComposition(); + + using (var ct = new AtomicComposition(innerAtomicComposition)) + { + ct.AddCompleteAction(() => executedAction = true); + + Assert.False(executedAction, "Action should not have been exectued yet"); + + // Do not complete + } + + innerAtomicComposition.Complete(); + Assert.False(executedAction); + } + + [Fact] + public void NoComplete_ShouldNotCopyValuesToInner() + { + var innerAtomicComposition = new AtomicComposition(); + + object value; + using (var ct = new AtomicComposition(innerAtomicComposition)) + { + ct.SetValue(this, 21); + + Assert.False(innerAtomicComposition.TryGetValue(this, out value)); + + // Do not call complete + } + + // reverify after dispose + Assert.False(innerAtomicComposition.TryGetValue(this, out value)); + } + + [Fact] + public void AtomicComposition_CompleteActions() + { + var setMe = false; + var setMeToo = false; + var dontSetMe = false; + using (var contextA = new AtomicComposition()) + { + contextA.AddCompleteAction(() => setMe = true); + using (var contextB = new AtomicComposition(contextA)) + { + contextB.AddCompleteAction(() => setMeToo = true); + contextB.Complete(); + } + + using (var contextC = new AtomicComposition(contextA)) + { + contextC.AddCompleteAction(() => dontSetMe = true); + // Don't complete + } + Assert.False(setMe); + Assert.False(setMeToo); + Assert.False(dontSetMe); + + contextA.Complete(); + + Assert.True(setMe); + Assert.True(setMeToo); + Assert.False(dontSetMe); + } + } + + private void TestNoValue(AtomicComposition context, object key) + { + string value; + Assert.False(context.TryGetValue(key, out value)); + } + + private void TestValue(AtomicComposition context, object key, string expectedValue) + { + string value; + Assert.True(context.TryGetValue(key, out value)); + Assert.Equal(expectedValue, value); + } + + [Fact] + public void AtomicComposition_CompleteValues() + { + object key1 = new Object(); + object key2 = new Object(); + + using (var contextA = new AtomicComposition()) + { + TestNoValue(contextA, key1); + TestNoValue(contextA, key2); + contextA.SetValue(key1, "Hello"); + TestValue(contextA, key1, "Hello"); + TestNoValue(contextA, key2); + + // Try overwriting + using (var contextB = new AtomicComposition(contextA)) + { + TestValue(contextB, key1, "Hello"); + TestNoValue(contextB, key2); + contextB.SetValue(key1, "Greetings"); + TestValue(contextB, key1, "Greetings"); + TestNoValue(contextB, key2); + + contextB.Complete(); + } + TestValue(contextA, key1, "Greetings"); + TestNoValue(contextA, key2); + + // Try overwrite with revert + using (var contextC = new AtomicComposition(contextA)) + { + TestValue(contextC, key1, "Greetings"); + TestNoValue(contextC, key2); + contextC.SetValue(key1, "Goodbye"); + contextC.SetValue(key2, "Goodbye, Again"); + TestValue(contextC, key1, "Goodbye"); + TestValue(contextC, key2, "Goodbye, Again"); + + // Don't complete + } + TestValue(contextA, key1, "Greetings"); + TestNoValue(contextA, key2); + + contextA.Complete(); + } + } + + private void TestQuery(AtomicComposition context, object key, int parameter, bool expectation) + { + Func query; + if (context.TryGetValue(key, out query)) + Assert.Equal(expectation, query(parameter)); + } + + private void SetQuery(AtomicComposition context, object key, Func, bool> query) + { + Func parentQuery; + context.TryGetValue(key, out parentQuery); + Func queryFunction = parameter => { return query(parameter, parentQuery); }; + context.SetValue(key, queryFunction); + } + + [Fact] + public void AtomicComposition_NestedQueries() + { + // This is a rather convoluted test that exercises the way AtomicComposition used to work to + // ensure consistency of the newer design + var key = new Object(); + + using (var contextA = new AtomicComposition()) + { + SetQuery(contextA, key, (int parameter, Func parentQuery) => + { + if (parameter == 22) + return true; + if (parentQuery != null) + return parentQuery(parameter); + return false; + }); + TestQuery(contextA, key, 22, true); + + using (var contextB = new AtomicComposition(contextA)) + { + TestQuery(contextB, key, 22, true); + SetQuery(contextB, key, (int parameter, Func parentQuery) => + { + if (parentQuery != null) + return !parentQuery(parameter); + throw new NotImplementedException(); + }); + TestQuery(contextB, key, 21, true); + TestQuery(contextB, key, 22, false); + + using (var contextC = new AtomicComposition(contextB)) + { + SetQuery(contextC, key, (int parameter, Func parentQuery) => + { + if (parameter == 23) + return true; + if (parentQuery != null) + return !parentQuery(parameter); + throw new NotImplementedException(); + }); + TestQuery(contextC, key, 21, false); + TestQuery(contextC, key, 22, true); + TestQuery(contextC, key, 23, true); + contextC.Complete(); + } + + using (var contextD = new AtomicComposition(contextB)) + { + SetQuery(contextD, key, (int parameter, Func parentQuery) => + { + if (parentQuery != null) + return parentQuery(parameter + 1); + throw new NotImplementedException(); + }); + TestQuery(contextD, key, 21, true); + TestQuery(contextD, key, 22, true); + TestQuery(contextD, key, 23, false); + // No complete + } + + contextB.Complete(); + } + TestQuery(contextA, key, 21, false); + TestQuery(contextA, key, 22, true); + TestQuery(contextA, key, 23, true); + contextA.Complete(); + } + } + + [Fact] + public void AddRevertAction_ShouldExecuteWhenDisposedAndNotCompleteted() + { + var ct = new AtomicComposition(); + bool executed = false; + + ct.AddRevertAction(() => executed = true); + + ct.Dispose(); + + Assert.True(executed); + } + + [Fact] + public void AddRevertAction_ShouldNotExecuteWhenCompleteted() + { + var ct = new AtomicComposition(); + bool executed = false; + + ct.AddRevertAction(() => executed = true); + + ct.Complete(); + + Assert.False(executed); + + ct.Dispose(); + + Assert.False(executed); + } + + [Fact] + public void AddRevertAction_ShouldExecuteInReverseOrder() + { + var ct = new AtomicComposition(); + Stack stack = new Stack(); + stack.Push(1); + stack.Push(2); + stack.Push(3); + + ct.AddRevertAction(() => Assert.Equal(1, stack.Pop())); + ct.AddRevertAction(() => Assert.Equal(2, stack.Pop())); + ct.AddRevertAction(() => Assert.Equal(3, stack.Pop())); + + ct.Dispose(); + + Assert.True(stack.Count == 0); + } + + [Fact] + public void AddRevertAction_ShouldBeCopiedWhenCompleteed() + { + Stack stack = new Stack(); + stack.Push(1); + stack.Push(2); + stack.Push(11); + stack.Push(12); + stack.Push(3); + + using (var ct = new AtomicComposition()) + { + ct.AddRevertAction(() => Assert.Equal(1, stack.Pop())); + ct.AddRevertAction(() => Assert.Equal(2, stack.Pop())); + + using (var ct2 = new AtomicComposition(ct)) + { + ct2.AddRevertAction(() => Assert.Equal(11, stack.Pop())); + ct2.AddRevertAction(() => Assert.Equal(12, stack.Pop())); + + // completeting should move those revert actions to ct + ct2.Complete(); + + Assert.Equal(5, stack.Count); + } + + ct.AddRevertAction(() => Assert.Equal(3, stack.Pop())); + + // Do not complete let ct dispose and revert + } + + Assert.True(stack.Count == 0); + } + + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/DirectoryCatalogDebuggerProxyTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/DirectoryCatalogDebuggerProxyTests.cs new file mode 100644 index 0000000000..3f74fe87c3 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/DirectoryCatalogDebuggerProxyTests.cs @@ -0,0 +1,208 @@ +// 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.ComponentModel.Composition.Hosting; +using System.IO; +using System.Linq; +using System.UnitTesting; +using System.Reflection; +using Xunit; + +namespace System.ComponentModel.Composition.Primitives +{ + public class DirectoryCatalogDebuggerProxyTests + { + [Fact] + public void Constructor_NullAsCatalogArgument_ShouldThrowArgumentNull() + { + Assert.Throws("catalog", () => + { + new DirectoryCatalog.DirectoryCatalogDebuggerProxy((DirectoryCatalog)null); + }); + } + + [Fact] + public void Constructor_ValueAsCatalogArgument_ShouldSetPartsProperty() + { + var expectations = Expectations.GetAssemblies(); + + foreach (var e in expectations) + { + string directoryPath = GetTemporaryDirectory(e.Location); + var catalog = CreateDirectoryCatalog(directoryPath); + + var proxy = new DirectoryCatalog.DirectoryCatalogDebuggerProxy(catalog); + + EqualityExtensions.CheckEquals(catalog.Parts, proxy.Parts); + } + } + + [Fact] + [ActiveIssue(25498)] + public void Constructor_ValueAsCatalogArgument_ShouldSetAssemblyProperty() + { + string directoryPath = GetTemporaryDirectory(); + var expectations = Expectations.GetAssemblies(); + + foreach (string fileName in expectations.Select(assembly => assembly.Location).ToArray()) + { + File.Copy(fileName, Path.Combine(directoryPath, Path.GetFileName(fileName))); + } + var catalog = CreateDirectoryCatalog(directoryPath); + var proxy = new DirectoryCatalog.DirectoryCatalogDebuggerProxy(catalog); + + Assert.Equal(expectations, proxy.Assemblies); + + } + + [Fact] + public void Constuctor_ValueAsCatalogArgument_ShouldSetPathProperty() + { + string path = GetTemporaryDirectory(); + + var catalog = CreateDirectoryCatalog(path); + var proxy = new DirectoryCatalog.DirectoryCatalogDebuggerProxy(catalog); + + Assert.Equal(path, proxy.Path); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void Constuctor_ValueAsCatalogArgument_ShouldSetSearchPatternProperty() + { + string directoryPath = GetTemporaryDirectory(); + var expectations = new ExpectationCollection(); + + expectations.Add("*.*", "*.*"); + expectations.Add("*.doc", "*.doc"); + expectations.Add("*.exe", "*.exe"); + expectations.Add("*.dll", "*.dll"); + + foreach (var e in expectations) + { + var catalog = CreateDirectoryCatalog(directoryPath, e.Input); + var proxy = new DirectoryCatalog.DirectoryCatalogDebuggerProxy(catalog); + + Assert.Equal(e.Output, proxy.SearchPattern); + } + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + [PlatformSpecific(TestPlatforms.Windows)] + [ActiveIssue(25498)] + public void FullPath_ValidPath_ShouldBeFine() + { + string directoryPath = GetTemporaryDirectory(); + var expectations = new ExpectationCollection(); + + // Ensure the path is always normalized properly. + string rootTempPath = Path.GetFullPath(TemporaryFileCopier.GetRootTemporaryDirectory()).ToUpperInvariant(); + + // Note: These relative paths work properly because the unit test temporary directories are always + // created as a subfolder off the AppDomain.CurrentDomain.BaseDirectory. + expectations.Add(".", Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ".")).ToUpperInvariant()); + expectations.Add(TemporaryFileCopier.RootTemporaryDirectoryName, rootTempPath); + expectations.Add(TemporaryFileCopier.GetRootTemporaryDirectory(), rootTempPath); + expectations.Add(directoryPath, Path.GetFullPath(directoryPath).ToUpperInvariant()); + + foreach (var e in expectations) + { + var cat = CreateDirectoryCatalog(e.Input, DirectoryCatalogTests.NonExistentSearchPattern); + var proxy = new DirectoryCatalog.DirectoryCatalogDebuggerProxy(cat); + + Assert.Equal(e.Output, proxy.FullPath); + } + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void LoadedFiles_EmptyDirectory_ShouldBeFine() + { + string directoryPath = GetTemporaryDirectory(); + var cat = CreateDirectoryCatalog(directoryPath); + var proxy = new DirectoryCatalog.DirectoryCatalogDebuggerProxy(cat); + + Assert.Equal(0, proxy.LoadedFiles.Count); + } + + [Fact] + public void LoadedFiles_ContainsMultipleDllsAndSomeNonDll_ShouldOnlyContainDlls() + { + string directoryPath = GetTemporaryDirectory(); + // Add one text file + using (File.CreateText(Path.Combine(directoryPath, "Test.txt"))) + { } + + // Add two dll's + string dll1 = Path.Combine(directoryPath, "Test1.dll"); + string dll2 = Path.Combine(directoryPath, "Test2.dll"); + File.Copy(Assembly.GetExecutingAssembly().Location, dll1); + File.Copy(Assembly.GetExecutingAssembly().Location, dll2); + + var cat = CreateDirectoryCatalog(directoryPath); + var proxy = new DirectoryCatalog.DirectoryCatalogDebuggerProxy(cat); + + EqualityExtensions.CheckEquals(new string[] { dll1.ToUpperInvariant(), dll2.ToUpperInvariant() }, + proxy.LoadedFiles); + } + + private DirectoryCatalog.DirectoryCatalogDebuggerProxy CreateAssemblyDebuggerProxy(DirectoryCatalog catalog) + { + return new DirectoryCatalog.DirectoryCatalogDebuggerProxy(catalog); + } + + private DirectoryCatalog CreateDirectoryCatalog(string path) + { + return new DirectoryCatalog(path); + } + + private DirectoryCatalog CreateDirectoryCatalog(string path, string filter) + { + return new DirectoryCatalog(path, filter); + } + + private string GetTemporaryDirectory(string location = null) + { + string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(tempDirectory); + return tempDirectory; + } + } + + public class TemporaryFileCopier + { + public const string RootTemporaryDirectoryName = "RootTempDirectory"; + private static string _temporaryDirectory; + public static string GetRootTemporaryDirectory() + { + if (_temporaryDirectory == null) + { + string path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), RootTemporaryDirectoryName); + + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + + _temporaryDirectory = path; + } + + return _temporaryDirectory; + } + + public static string GetNewTemporaryDirectory() + { + string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(tempDirectory); + return tempDirectory; + } + + public static string GetTemporaryDirectory() + { + string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(tempDirectory); + return tempDirectory; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/DirectoryCatalogTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/DirectoryCatalogTests.cs new file mode 100644 index 0000000000..794e77943c --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/DirectoryCatalogTests.cs @@ -0,0 +1,566 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Factories; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.UnitTesting; +using System.ComponentModel.Composition.Primitives; +using System.Reflection; +using Xunit; + +namespace System.ComponentModel.Composition +{ + // This is a glorious do nothing ReflectionContext + public class DirectoryCatalogTestsReflectionContext : ReflectionContext + { + public override Assembly MapAssembly(Assembly assembly) + { + return assembly; + } + +#if FEATURE_INTERNAL_REFLECTIONCONTEXT + public override Type MapType(Type type) +#else + public override TypeInfo MapType(TypeInfo type) +#endif + { + return type; + } + } + + public class DirectoryCatalogTests + { + internal const string NonExistentSearchPattern = "*.NonExistentSearchPattern"; + + public static void Constructor_NullReflectionContextArgument_ShouldThrowArgumentNull(Func catalogCreator) + { + Assert.Throws("reflectionContext", () => + { + var catalog = catalogCreator(null); + }); + } + + public static void Constructor_NullDefinitionOriginArgument_ShouldThrowArgumentNull(Func catalogCreator) + { + Assert.Throws("definitionOrigin", () => + { + var catalog = catalogCreator(null); + }); + } + + [Fact] + public void Constructor2_NullReflectionContextArgument_ShouldThrowArgumentNull() + { + DirectoryCatalogTests.Constructor_NullReflectionContextArgument_ShouldThrowArgumentNull((rc) => + { + return new DirectoryCatalog(TemporaryFileCopier.GetNewTemporaryDirectory(), rc); + }); + } + + [Fact] + public void Constructor3_NullDefinitionOriginArgument_ShouldThrowArgumentNull() + { + DirectoryCatalogTests.Constructor_NullDefinitionOriginArgument_ShouldThrowArgumentNull((dO) => + { + return new DirectoryCatalog(TemporaryFileCopier.GetNewTemporaryDirectory(), dO); + }); + } + + [Fact] + public void Constructor4_NullReflectionContextArgument_ShouldThrowArgumentNull() + { + DirectoryCatalogTests.Constructor_NullReflectionContextArgument_ShouldThrowArgumentNull((rc) => + { + return new DirectoryCatalog(TemporaryFileCopier.GetNewTemporaryDirectory(), rc, CreateDirectoryCatalog()); + }); + } + + [Fact] + public void Constructor4_NullDefinitionOriginArgument_ShouldThrowArgumentNull() + { + DirectoryCatalogTests.Constructor_NullDefinitionOriginArgument_ShouldThrowArgumentNull((dO) => + { + return new DirectoryCatalog(TemporaryFileCopier.GetNewTemporaryDirectory(), new DirectoryCatalogTestsReflectionContext(), dO); + }); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.IO.DirectoryNotFoundException : Could not find a part of the path '/HOME/HELIXBOT/DOTNETBUILD/WORK/E77C2FB6-5244-4437-8E27-6DD709101152/WORK/D9EBA0EA-A511-4F42-AC8B-AC8054AAF606/UNZIP/'. + public void ICompositionElementDisplayName_ShouldIncludeCatalogTypeNameAndDirectoryPath() + { + var paths = GetPathExpectations(); + + foreach (var path in paths) + { + var catalog = (ICompositionElement)CreateDirectoryCatalog(path, NonExistentSearchPattern); + + string expected = string.Format("DirectoryCatalog (Path=\"{0}\")", path); + + Assert.Equal(expected, catalog.DisplayName); + } + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.IO.DirectoryNotFoundException : Could not find a part of the path '/HOME/HELIXBOT/DOTNETBUILD/WORK/E77C2FB6-5244-4437-8E27-6DD709101152/WORK/D9EBA0EA-A511-4F42-AC8B-AC8054AAF606/UNZIP/'. + public void ICompositionElementDisplayName_ShouldIncludeDerivedCatalogTypeNameAndAssemblyFullName() + { + var paths = GetPathExpectations(); + + foreach (var path in paths) + { + var catalog = (ICompositionElement)new DerivedDirectoryCatalog(path, NonExistentSearchPattern); + + string expected = string.Format("DerivedDirectoryCatalog (Path=\"{0}\")", path); + + Assert.Equal(expected, catalog.DisplayName); + } + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.IO.DirectoryNotFoundException : Could not find a part of the path '/HOME/HELIXBOT/DOTNETBUILD/WORK/E77C2FB6-5244-4437-8E27-6DD709101152/WORK/D9EBA0EA-A511-4F42-AC8B-AC8054AAF606/UNZIP/'. + public void ToString_ShouldReturnICompositionElementDisplayName() + { + var paths = GetPathExpectations(); + + foreach (var path in paths) + { + var catalog = (ICompositionElement)CreateDirectoryCatalog(path, NonExistentSearchPattern); + + Assert.Equal(catalog.DisplayName, catalog.ToString()); + } + } + + [Fact] + public void ICompositionElementDisplayName_WhenCatalogDisposed_ShouldNotThrow() + { + var catalog = CreateDirectoryCatalog(); + catalog.Dispose(); + + var displayName = ((ICompositionElement)catalog).DisplayName; + } + + [Fact] + public void ICompositionElementOrigin_WhenCatalogDisposed_ShouldNotThrow() + { + var catalog = CreateDirectoryCatalog(); + catalog.Dispose(); + + var origin = ((ICompositionElement)catalog).Origin; + } + + [Fact] + public void Parts_WhenCatalogDisposed_ShouldThrowObjectDisposed() + { + var catalog = CreateDirectoryCatalog(); + catalog.Dispose(); + + ExceptionAssert.ThrowsDisposed(catalog, () => + { + var parts = catalog.Parts; + }); + } + + [Fact] + public void GetExports_WhenCatalogDisposed_ShouldThrowObjectDisposed() + { + var catalog = CreateDirectoryCatalog(); + catalog.Dispose(); + var definition = ImportDefinitionFactory.Create(); + + ExceptionAssert.ThrowsDisposed(catalog, () => + { + catalog.GetExports(definition); + }); + } + + [Fact] + public void Refresh_WhenCatalogDisposed_ShouldThrowObjectDisposed() + { + var catalog = CreateDirectoryCatalog(); + catalog.Dispose(); + + ExceptionAssert.ThrowsDisposed(catalog, () => + { + catalog.Refresh(); + }); + } + + [Fact] + public void ToString_WhenCatalogDisposed_ShouldNotThrow() + { + var catalog = CreateDirectoryCatalog(); + catalog.Dispose(); + + catalog.ToString(); + } + + [Fact] + public void GetExports_NullAsConstraintArgument_ShouldThrowArgumentNull() + { + var catalog = CreateDirectoryCatalog(); + + Assert.Throws("definition", () => + { + catalog.GetExports((ImportDefinition)null); + }); + } + + [Fact] + public void Dispose_ShouldNotThrow() + { + using (var catalog = CreateDirectoryCatalog()) + { + } + } + + [Fact] + public void Dispose_CanBeCalledMultipleTimes() + { + var catalog = CreateDirectoryCatalog(); + catalog.Dispose(); + catalog.Dispose(); + catalog.Dispose(); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // typeof(System.IO.DirectoryNotFoundException): Could not find a part of the path '/HOME/HELIXBOT/DOTNETBUILD/WORK/E77C2FB6-5244-4437-8E27-6DD709101152/WORK/D9EBA0EA-A511-4F42-AC8B-AC8054AAF606/UNZIP/HTTP:/MICROSOFT.COM/MYASSEMBLY.DLL'. + public void AddAssembly1_NonExistentUriAsAssemblyFileNameArgument_ShouldNotSupportedException() + { + Assert.Throws(() => + { + var catalog = new DirectoryCatalog("http://microsoft.com/myassembly.dll"); + }); + } + + [Fact] + public void AddAssembly1_NullPathArgument_ShouldThrowArugmentNull() + { + Assert.Throws(() => + new DirectoryCatalog((string)null)); + } + + [Fact] + public void AddAssembly1_EmptyPathArgument_ShouldThrowArugment() + { + Assert.Throws(() => + new DirectoryCatalog("")); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // typeof(System.IO.DirectoryNotFoundException): Could not find a part of the path '/HOME/HELIXBOT/DOTNETBUILD/WORK/E77C2FB6-5244-4437-8E27-6DD709101152/WORK/D9EBA0EA-A511-4F42-AC8B-AC8054AAF606/UNZIP/*'. + public void AddAssembly1_InvalidPathName_ShouldThrowDirectoryNotFound() + { + Assert.Throws(() => + { + var c1 = new DirectoryCatalog("*"); + }); + } + + [Fact] + [ActiveIssue(25498)] + public void AddAssembly1_TooLongPathNameArgument_ShouldThrowPathTooLongException() + { + Assert.Throws(() => + { + var c1 = new DirectoryCatalog(@"c:\This is a very long path\And Just to make sure\We will continue to make it very long\This is a very long path\And Just to make sure\We will continue to make it very long\This is a very long path\And Just to make sure\We will continue to make it very long\myassembly.dll"); + }); + } + + [Fact] + [ActiveIssue(25498)] + public void Parts() + { + var catalog = new DirectoryCatalog(TemporaryFileCopier.GetTemporaryDirectory()); + Assert.NotNull(catalog.Parts); + Assert.True(catalog.Parts.Count() > 0); + } + + [Fact] + [ActiveIssue(25498)] + public void Parts_ShouldSetDefinitionOriginToCatalogItself() + { + var catalog = new DirectoryCatalog(TemporaryFileCopier.GetTemporaryDirectory()); + Assert.True(catalog.Parts.Count() > 0); + + foreach (ICompositionElement definition in catalog.Parts) + { + Assert.Same(catalog, definition.Origin); + } + } + + [Fact] + [ActiveIssue(25498)] + public void Path_ValidPath_ShouldBeFine() + { + var expectations = new ExpectationCollection(); + + expectations.Add(".", "."); + expectations.Add(TemporaryFileCopier.RootTemporaryDirectoryName, TemporaryFileCopier.RootTemporaryDirectoryName); + expectations.Add(TemporaryFileCopier.GetRootTemporaryDirectory(), TemporaryFileCopier.GetRootTemporaryDirectory()); + expectations.Add(TemporaryFileCopier.GetTemporaryDirectory(), TemporaryFileCopier.GetTemporaryDirectory()); + + foreach (var e in expectations) + { + var cat = CreateDirectoryCatalog(e.Input, NonExistentSearchPattern); + + Assert.Equal(e.Output, cat.Path); + } + } + + [Fact] + [ActiveIssue(25498)] + public void FullPath_ValidPath_ShouldBeFine() + { + var expectations = new ExpectationCollection(); + + // Ensure the path is always normalized properly. + string rootTempPath = Path.GetFullPath(TemporaryFileCopier.GetRootTemporaryDirectory()).ToUpperInvariant(); + + // Note: These relative paths work properly because the unit test temporary directories are always + // created as a subfolder off the AppDomain.CurrentDomain.BaseDirectory. + expectations.Add(".", Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ".")).ToUpperInvariant()); + expectations.Add(TemporaryFileCopier.RootTemporaryDirectoryName, rootTempPath); + expectations.Add(TemporaryFileCopier.GetRootTemporaryDirectory(), rootTempPath); + expectations.Add(TemporaryFileCopier.GetTemporaryDirectory(), Path.GetFullPath(TemporaryFileCopier.GetTemporaryDirectory()).ToUpperInvariant()); + + foreach (var e in expectations) + { + var cat = CreateDirectoryCatalog(e.Input, NonExistentSearchPattern); + + Assert.Equal(e.Output, cat.FullPath); + } + } + + [Fact] + public void LoadedFiles_EmptyDirectory_ShouldBeFine() + { + var cat = new DirectoryCatalog(TemporaryFileCopier.GetTemporaryDirectory()); + + Assert.Equal(0, cat.LoadedFiles.Count); + } + + [Fact] + public void LoadedFiles_ContainsMultipleDllsAndSomeNonDll_ShouldOnlyContainDlls() + { + // Add one text file + using (File.CreateText(Path.Combine(TemporaryFileCopier.GetTemporaryDirectory(), "Test.txt"))) { } + + // Add two dll's + string dll1 = Path.Combine(TemporaryFileCopier.GetTemporaryDirectory(), "Test1.dll"); + string dll2 = Path.Combine(TemporaryFileCopier.GetTemporaryDirectory(), "Test2.dll"); + File.Copy(Assembly.GetExecutingAssembly().Location, dll1); + File.Copy(Assembly.GetExecutingAssembly().Location, dll2); + + var cat = new DirectoryCatalog(TemporaryFileCopier.GetTemporaryDirectory()); + + EqualityExtensions.CheckEquals(new string[] { dll1.ToUpperInvariant(), dll2.ToUpperInvariant() }, + cat.LoadedFiles); + } + + [Fact] + public void Constructor_InvalidAssembly_ShouldBeFine() + { + using (File.CreateText(Path.Combine(TemporaryFileCopier.GetTemporaryDirectory(), "Test.dll"))) { } + var cat = new DirectoryCatalog(TemporaryFileCopier.GetTemporaryDirectory()); + } + + [Fact] + public void Constructor_NonExistentDirectory_ShouldThrow() + { + Assert.Throws(() => + new DirectoryCatalog(TemporaryFileCopier.GetTemporaryDirectory() + @"\NonexistentDirectoryWithoutEndingSlash")); + + Assert.Throws(() => + new DirectoryCatalog(TemporaryFileCopier.GetTemporaryDirectory() + @"\NonexistentDirectoryWithEndingSlash\")); + + } + + [Fact] + [ActiveIssue(25498)] + public void Constructor_PassExistingFileName_ShouldThrow() + { + using (File.CreateText(Path.Combine(TemporaryFileCopier.GetTemporaryDirectory(), "Test.txt"))) { } + Assert.Throws(() => + new DirectoryCatalog(Path.Combine(TemporaryFileCopier.GetTemporaryDirectory(), "Test.txt"))); + } + + [Fact] + public void Constructor_PassNonExistingFileName_ShouldThrow() + { + Assert.Throws(() => + new DirectoryCatalog(Path.Combine(TemporaryFileCopier.GetTemporaryDirectory(), "NonExistingFile.txt"))); + } + + [Fact] + [ActiveIssue(25498)] + public void Refresh_AssemblyAdded_ShouldFireOnChanged() + { + bool changedFired = false; + bool changingFired = false; + var cat = new DirectoryCatalog(TemporaryFileCopier.GetTemporaryDirectory()); + + Assert.Equal(0, cat.Parts.Count()); + + cat.Changing += new EventHandler((o, e) => + { + Assert.Equal(0, cat.Parts.Count()); + changingFired = true; + }); + + cat.Changed += new EventHandler((o, e) => + { + Assert.NotEqual(0, cat.Parts.Count()); + changedFired = true; + }); + + File.Copy(Assembly.GetExecutingAssembly().Location, Path.Combine(TemporaryFileCopier.GetTemporaryDirectory(), "Test.dll")); + + cat.Refresh(); + + Assert.True(changingFired); + Assert.True(changedFired); + } + + [Fact] + [ActiveIssue(25498)] + public void Refresh_AssemblyRemoved_ShouldFireOnChanged() + { + string file = Path.Combine(TemporaryFileCopier.GetTemporaryDirectory(), "Test.dll"); + File.Copy(Assembly.GetExecutingAssembly().Location, file); + bool changedFired = false; + var cat = new DirectoryCatalog(TemporaryFileCopier.GetTemporaryDirectory()); + + cat.Changed += new EventHandler((o, e) => + changedFired = true); + + // This assembly can be deleted because it was already loaded by the CLR in another context + // in another location so it isn't locked on disk. + File.Delete(file); + + cat.Refresh(); + + Assert.True(changedFired); + } + + [Fact] + public void Refresh_NoChanges_ShouldNotFireOnChanged() + { + var cat = new DirectoryCatalog(TemporaryFileCopier.GetTemporaryDirectory()); + + cat.Changed += new EventHandler((o, e) => + Assert.False(true)); + + cat.Refresh(); + } + + [Fact] + [ActiveIssue(25498)] + public void Refresh_DirectoryRemoved_ShouldThrowDirectoryNotFound() + { + DirectoryCatalog cat; + cat = new DirectoryCatalog(TemporaryFileCopier.GetTemporaryDirectory()); + + ExceptionAssert.Throws(RetryMode.DoNotRetry, () => + cat.Refresh()); + } + + [Fact] + public void GetExports() + { + var catalog = new AggregateCatalog(); + Expression> constraint = (ExportDefinition exportDefinition) => exportDefinition.ContractName == AttributedModelServices.GetContractName(typeof(MyExport)); + IEnumerable> matchingExports = null; + + matchingExports = catalog.GetExports(constraint); + Assert.NotNull(matchingExports); + Assert.True(matchingExports.Count() == 0); + + var testsDirectoryCatalog = new DirectoryCatalog(TemporaryFileCopier.GetTemporaryDirectory()); + catalog.Catalogs.Add(testsDirectoryCatalog); + matchingExports = catalog.GetExports(constraint); + + Assert.NotNull(matchingExports); + Assert.True(matchingExports.Count() >= 0); + + IEnumerable> expectedMatchingExports = catalog.Parts + .SelectMany(part => part.ExportDefinitions, (part, export) => new Tuple(part, export)) + .Where(partAndExport => partAndExport.Item2.ContractName == AttributedModelServices.GetContractName(typeof(MyExport))); + + Assert.True(matchingExports.SequenceEqual(expectedMatchingExports)); + + catalog.Catalogs.Remove(testsDirectoryCatalog); + matchingExports = catalog.GetExports(constraint); + Assert.NotNull(matchingExports); + Assert.True(matchingExports.Count() == 0); + } + + [Fact] + [ActiveIssue(25498)] + public void AddAndRemoveDirectory() + { + var cat = new AggregateCatalog(); + var container = new CompositionContainer(cat); + + Assert.False(container.IsPresent()); + + var dir1 = new DirectoryCatalog(TemporaryFileCopier.GetTemporaryDirectory()); + cat.Catalogs.Add(dir1); + Assert.True(container.IsPresent()); + + cat.Catalogs.Remove(dir1); + + Assert.False(container.IsPresent()); + } + + [Fact] + public void AddDirectoryNotFoundException() + { + Assert.Throws(() => + { + var cat = new DirectoryCatalog("Directory That Should Never Exist tadfasdfasdfsdf"); + }); + } + + [Fact] + public void ExecuteOnCreationThread() + { + // Add a proper test for event notification on caller thread + } + + private DirectoryCatalog CreateDirectoryCatalog() + { + return CreateDirectoryCatalog(TemporaryFileCopier.GetNewTemporaryDirectory()); + } + + private DirectoryCatalog CreateDirectoryCatalog(string path) + { + return new DirectoryCatalog(path); + } + + private DirectoryCatalog CreateDirectoryCatalog(string path, string searchPattern) + { + return new DirectoryCatalog(path, searchPattern); + } + + public IEnumerable GetPathExpectations() + { + yield return AppDomain.CurrentDomain.BaseDirectory; + yield return AppDomain.CurrentDomain.BaseDirectory + @"\"; + yield return "."; + } + + private class DerivedDirectoryCatalog : DirectoryCatalog + { + public DerivedDirectoryCatalog(string path, string searchPattern) + : base(path, searchPattern) + { + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/FilteredCatalogTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/FilteredCatalogTests.cs new file mode 100644 index 0000000000..48f1071588 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/FilteredCatalogTests.cs @@ -0,0 +1,422 @@ +// 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.ComponentModel.Composition.Primitives; +using System.Linq; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition.Hosting +{ + public class FilteredCatalogTests + { + [Fact] + public void Constructor_ThrowsOnNullCatalog() + { + Assert.Throws("catalog", () => + { + new FilteredCatalog(null, p => true); + }); + } + + [Fact] + public void Constructor_ThrowsOnNullFilter() + { + Assert.Throws("filter", () => + { + new FilteredCatalog(CreateCatalog(), null); + }); + } + + [Fact] + public void Parts_Throws_WhenDisposed() + { + var originalCatalog = this.CreateCatalog(); + FilteredCatalog catalog = new FilteredCatalog(originalCatalog, p => p.Exports()); + catalog.Dispose(); + + ExceptionAssert.ThrowsDisposed(catalog, () => + { + var p = catalog.Parts; + }); + } + + [Fact] + public void Parts() + { + var originalCatalog = this.CreateCatalog(); + using (FilteredCatalog catalog = new FilteredCatalog(originalCatalog, p => p.Exports())) + { + var parts = catalog.Parts; + Assert.Equal(2, parts.Count()); + } + } + + [Fact] + public void GetExports_Throws_WhenDisposed() + { + var originalCatalog = this.CreateCatalog(); + FilteredCatalog catalog = new FilteredCatalog(originalCatalog, p => p.Exports()); + catalog.Dispose(); + + ExceptionAssert.ThrowsDisposed(catalog, () => + { + var p = catalog.GetExports(); + }); + } + + [Fact] + public void GetExports() + { + var originalCatalog = this.CreateCatalog(); + using (FilteredCatalog catalog = new FilteredCatalog(originalCatalog, p => p.Exports())) + { + var parts1 = catalog.GetExports(); + Assert.Equal(2, parts1.Count()); + + var parts2 = catalog.GetExports(); + Assert.Equal(0, parts2.Count()); + } + } + + [Fact] + public void GetExportsWithGenerics() + { + var originalCatalog = new TypeCatalog(typeof(GenericExporter<,>), typeof(Exporter11), typeof(Exporter22)); + using (FilteredCatalog catalog = new FilteredCatalog(originalCatalog, p => p.Exports()).IncludeDependents()) + { + var parts1 = catalog.GetExports(); + Assert.Equal(1, parts1.Count()); + + using (var container = new CompositionContainer(catalog)) + { + var results = container.GetExports>(); + Assert.Equal(1, results.Count()); + } + } + } + + [Fact] + public void Complement_Throws_WhenDisposed() + { + var originalCatalog = this.CreateCatalog(); + FilteredCatalog catalog = new FilteredCatalog(originalCatalog, p => p.Exports()); + catalog.Dispose(); + + ExceptionAssert.ThrowsDisposed(catalog, () => + { + var c = catalog.Complement; + }); + } + + [Fact] + public void Complement() + { + var originalCatalog = this.CreateCatalog(); + using (FilteredCatalog catalog = new FilteredCatalog(originalCatalog, p => p.Exports())) + { + var c = catalog.Complement; + Assert.NotNull(c); + + var parts1 = c.GetExports(); + Assert.Equal(2, parts1.Count()); + + var parts2 = c.GetExports(); + Assert.Equal(0, parts2.Count()); + } + } + + [Fact] + public void Complement_Repeatable_Read() + { + var originalCatalog = this.CreateCatalog(); + using (FilteredCatalog catalog = new FilteredCatalog(originalCatalog, p => p.Exports())) + { + var c1 = catalog.Complement; + var c2 = catalog.Complement; + + Assert.Same(c1, c2); + } + } + + [Fact] + public void Complement_ComplementOfComplement() + { + var originalCatalog = this.CreateCatalog(); + using (FilteredCatalog catalog = new FilteredCatalog(originalCatalog, p => p.Exports())) + { + var c1 = catalog.Complement; + var c2 = c1.Complement; + + Assert.Same(catalog, c2); + } + } + + [Fact] + public void FilteredNotifications() + { + var catalog1 = CreateSubCatalog1(); + var catalog2 = CreateSubCatalog2(); + var catalog = new AggregateCatalog(); + + var filter1 = catalog.Filter(p => p.Exports()); + var filter2 = catalog.Filter(p => p.Exports()); + + bool filter1Ing = false; + bool filter1Ed = false; + bool filter2Ing = false; + bool filter2Ed = false; + + ComposablePartCatalogChangeEventArgs edArgs = null; + ComposablePartCatalogChangeEventArgs ingArgs = null; + + filter1.Changing += (object s, ComposablePartCatalogChangeEventArgs a) => + { + Assert.Same(filter1, s); + Assert.False(filter1Ing); + Assert.False(filter1Ed); + Assert.Null(ingArgs); + Assert.Null(edArgs); + + filter1Ing = true; + ingArgs = a; + }; + + filter1.Changed += (object s, ComposablePartCatalogChangeEventArgs a) => + { + Assert.True(filter1Ing); + Assert.False(filter1Ed); + Assert.NotNull(ingArgs); + Assert.Null(edArgs); + + filter1Ed = true; + edArgs = a; + EqualityExtensions.CheckEquals(ingArgs.AddedDefinitions, edArgs.AddedDefinitions); + EqualityExtensions.CheckEquals(ingArgs.RemovedDefinitions, edArgs.RemovedDefinitions); + }; + + filter2.Changing += (object s, ComposablePartCatalogChangeEventArgs a) => + { + Assert.Same(filter2, s); + Assert.False(filter2Ing); + Assert.False(filter2Ed); + Assert.Null(ingArgs); + Assert.Null(edArgs); + + filter2Ing = true; + ingArgs = a; + }; + + filter2.Changed += (object s, ComposablePartCatalogChangeEventArgs a) => + { + Assert.True(filter2Ing); + Assert.False(filter2Ed); + Assert.NotNull(ingArgs); + Assert.Null(edArgs); + + filter2Ed = true; + edArgs = a; + EqualityExtensions.CheckEquals(ingArgs.AddedDefinitions, edArgs.AddedDefinitions); + EqualityExtensions.CheckEquals(ingArgs.RemovedDefinitions, edArgs.RemovedDefinitions); + }; + + //at first everything is empty + + // add the first one + filter1Ing = false; + filter1Ed = false; + filter2Ing = false; + filter2Ed = false; + ingArgs = null; + edArgs = null; + + catalog.Catalogs.Add(catalog1); + Assert.True(filter1Ing); + Assert.True(filter1Ed); + Assert.False(filter2Ing); + Assert.False(filter2Ed); + + Assert.Equal(edArgs.AddedDefinitions.Count(), 2); + Assert.Equal(edArgs.RemovedDefinitions.Count(), 0); + Assert.Equal(0, filter2.Parts.Count()); + Assert.Equal(2, filter1.Parts.Count()); + + EqualityExtensions.CheckEquals(ingArgs.AddedDefinitions, catalog1.Parts); + EqualityExtensions.CheckEquals(edArgs.AddedDefinitions, catalog1.Parts); + + // add the second one + filter1Ing = false; + filter1Ed = false; + filter2Ing = false; + filter2Ed = false; + ingArgs = null; + edArgs = null; + + catalog.Catalogs.Add(catalog2); + Assert.True(filter2Ing); + Assert.True(filter2Ed); + Assert.False(filter1Ing); + Assert.False(filter1Ed); + + Assert.Equal(edArgs.AddedDefinitions.Count(), 2); + Assert.Equal(edArgs.RemovedDefinitions.Count(), 0); + Assert.Equal(2, filter2.Parts.Count()); + Assert.Equal(2, filter1.Parts.Count()); + + EqualityExtensions.CheckEquals(ingArgs.AddedDefinitions, catalog2.Parts); + EqualityExtensions.CheckEquals(edArgs.AddedDefinitions, catalog2.Parts); + + // remove the second one + filter1Ing = false; + filter1Ed = false; + filter2Ing = false; + filter2Ed = false; + ingArgs = null; + edArgs = null; + + catalog.Catalogs.Remove(catalog2); + Assert.True(filter2Ing); + Assert.True(filter2Ed); + Assert.False(filter1Ing); + Assert.False(filter1Ed); + + Assert.Equal(edArgs.AddedDefinitions.Count(), 0); + Assert.Equal(edArgs.RemovedDefinitions.Count(), 2); + Assert.Equal(0, filter2.Parts.Count()); + Assert.Equal(2, filter1.Parts.Count()); + + EqualityExtensions.CheckEquals(ingArgs.RemovedDefinitions, catalog2.Parts); + EqualityExtensions.CheckEquals(edArgs.RemovedDefinitions, catalog2.Parts); + + // remove the first one + filter1Ing = false; + filter1Ed = false; + filter2Ing = false; + filter2Ed = false; + ingArgs = null; + edArgs = null; + + catalog.Catalogs.Remove(catalog1); + Assert.True(filter1Ing); + Assert.True(filter1Ed); + Assert.False(filter2Ing); + Assert.False(filter2Ed); + + Assert.Equal(edArgs.AddedDefinitions.Count(), 0); + Assert.Equal(edArgs.RemovedDefinitions.Count(), 2); + Assert.Equal(0, filter2.Parts.Count()); + Assert.Equal(0, filter1.Parts.Count()); + + EqualityExtensions.CheckEquals(ingArgs.RemovedDefinitions, catalog1.Parts); + EqualityExtensions.CheckEquals(edArgs.RemovedDefinitions, catalog1.Parts); + } + + [Fact] + public void NoNotificationsAfterDispose() + { + var catalog1 = CreateSubCatalog1(); + var catalog2 = CreateSubCatalog2(); + var catalog = new AggregateCatalog(catalog1, catalog2); + + var filter1 = catalog.Filter(p => p.Exports()); + + filter1.Changing += (s, e) => + { + throw new NotImplementedException(); + }; + + filter1.Changed += (s, e) => + { + throw new NotImplementedException(); + }; + + filter1.Dispose(); + + Assert.True(catalog.Catalogs.Remove(catalog1)); + Assert.True(catalog.Catalogs.Remove(catalog2)); + } + + [Fact] + public void DoubleDispose() + { + var catalog1 = CreateSubCatalog1(); + var catalog2 = CreateSubCatalog2(); + var catalog = new AggregateCatalog(catalog1, catalog2); + + var filter1 = catalog.Filter(p => p.Exports()); + + filter1.Changing += (s, e) => + { + throw new NotImplementedException(); + }; + + filter1.Changed += (s, e) => + { + throw new NotImplementedException(); + }; + + filter1.Dispose(); + filter1.Dispose(); + + Assert.True(catalog.Catalogs.Remove(catalog1)); + Assert.True(catalog.Catalogs.Remove(catalog2)); + } + + private ComposablePartCatalog CreateCatalog() + { + return new TypeCatalog( + typeof(Exporter11), + typeof(Exporter12), + typeof(Exporter21), + typeof(Exporter22)); + + } + + private ComposablePartCatalog CreateSubCatalog1() + { + return new TypeCatalog( + typeof(Exporter11), + typeof(Exporter12)); + } + + private ComposablePartCatalog CreateSubCatalog2() + { + return new TypeCatalog( + typeof(Exporter21), + typeof(Exporter22)); + } + + public interface IContract1 { } + public interface IContract2 { } + public interface IGenericContract { } + + [Export(typeof(IGenericContract<,>))] + public class GenericExporter : IGenericContract + { + [Import] + IContract1 Import { get; set; } + } + + [Export(typeof(IContract1))] + public class Exporter11 : IContract1 + { + } + + [Export(typeof(IContract1))] + public class Exporter12 : IContract1 + { + } + + [Export(typeof(IContract2))] + public class Exporter21 : IContract2 + { + } + + [Export(typeof(IContract2))] + public class Exporter22 : IContract2 + { + } + + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/FilteredCatalogTransitiveClosureTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/FilteredCatalogTransitiveClosureTests.cs new file mode 100644 index 0000000000..ccf27708e5 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/FilteredCatalogTransitiveClosureTests.cs @@ -0,0 +1,505 @@ +// 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.ComponentModel.Composition.Primitives; +using System.Linq; +using Xunit; + +namespace System.ComponentModel.Composition.Hosting +{ + public class FilteredCatalogTransitiveClosureTests + { + public interface IContract1 { } + public interface IContract2 { } + public interface IContract3 { } + public interface IOther { } + + [Fact] + public void IncludeDependentsSimpleChain() + { + var catalog = CreateCatalog( + typeof(Exporter1), + typeof(Exporter2), + typeof(Exporter2Import1), + typeof(Exporter3Import2), + typeof(Other)); + var filteredCatalog = catalog.Filter(p => p.Exports()); + Assert.Equal(1, filteredCatalog.Parts.Count()); + + var dependentsCatalog = filteredCatalog.IncludeDependents(); + Assert.Equal(3, dependentsCatalog.Parts.Count()); + } + + [Fact] + public void IncludeDependentsExportFactory() + { + var catalog = CreateCatalog( + typeof(Exporter1), + typeof(Exporter2Import1AsExportFactory), + typeof(Other)); + var filteredCatalog = catalog.Filter(p => p.Exports()); + Assert.Equal(1, filteredCatalog.Parts.Count()); + + var dependentsCatalog = filteredCatalog.IncludeDependents(); + Assert.Equal(2, dependentsCatalog.Parts.Count()); + } + + [Fact] + public void IncludeDependentsChainWithCycles() + { + var catalog = CreateCatalog( + typeof(Exporter1), + typeof(Exporter2), + typeof(Exporter2Import1), + typeof(Exporter1Import2), + typeof(Exporter3Import2), + typeof(Exporter2Import3), + typeof(Other)); + var filteredCatalog = catalog.Filter(p => p.Exports()); + Assert.Equal(2, filteredCatalog.Parts.Count()); + + var dependentsCatalog = filteredCatalog.IncludeDependents(); + Assert.Equal(5, dependentsCatalog.Parts.Count()); + } + + [Fact] + public void IncludeDependentsSimpleChainOptional() + { + var catalog = CreateCatalog( + typeof(Exporter2OptionalImport1), + typeof(Exporter1), + typeof(Exporter2), + typeof(Exporter2Import1), + typeof(Exporter3Import2), + typeof(Other)); + var filteredCatalog = catalog.Filter(p => p.Exports()); + Assert.Equal(1, filteredCatalog.Parts.Count()); + + var dependentsCatalog = filteredCatalog.IncludeDependents(); + Assert.Equal(3, dependentsCatalog.Parts.Count()); + } + + [Fact] + public void IncludeDependentsChainWithCyclesOptional() + { + var catalog = CreateCatalog( + typeof(Exporter2OptionalImport1), + typeof(Exporter1), + typeof(Exporter2), + typeof(Exporter2Import1), + typeof(Exporter1Import2), + typeof(Exporter3Import2), + typeof(Exporter2Import3), + typeof(Other)); + var filteredCatalog = catalog.Filter(p => p.Exports()); + Assert.Equal(2, filteredCatalog.Parts.Count()); + + var dependentsCatalog = filteredCatalog.IncludeDependents(); + Assert.Equal(5, dependentsCatalog.Parts.Count()); + } + + [Fact] + public void IncludeDependentsSimpleChainOptionalOnly() + { + var catalog = CreateCatalog( + typeof(Exporter2OptionalImport1), + typeof(Exporter1), + typeof(Exporter2), + typeof(Exporter2Import1), + typeof(Exporter3Import2), + typeof(Other)); + var filteredCatalog = catalog.Filter(p => p.Exports()); + Assert.Equal(1, filteredCatalog.Parts.Count()); + + var dependentsCatalog = filteredCatalog.IncludeDependents(i => i.Cardinality == ImportCardinality.ZeroOrOne); + Assert.Equal(2, dependentsCatalog.Parts.Count()); + } + + [Fact] + public void IncludeDependentsChainWithCyclesOptionalOnly() + { + var catalog = CreateCatalog( + typeof(Exporter2OptionalImport1), + typeof(Exporter1), + typeof(Exporter2), + typeof(Exporter2Import1), + typeof(Exporter1Import2), + typeof(Exporter3Import2), + typeof(Exporter2Import3), + typeof(Other)); + var filteredCatalog = catalog.Filter(p => p.Exports()); + Assert.Equal(2, filteredCatalog.Parts.Count()); + + var dependentsCatalog = filteredCatalog.IncludeDependents(i => i.Cardinality == ImportCardinality.ZeroOrOne); + Assert.Equal(3, dependentsCatalog.Parts.Count()); + } + + [Fact] + public void IncludeDependentsOpenGenericToOpenGeneric() + { + var catalog = CreateCatalog( + typeof(OpenGenericExporter<,>), + typeof(OpenGenericImporter<,>), + typeof(Other)); + var filteredCatalog = catalog.Filter(p => p.Imports()); + Assert.Equal(1, filteredCatalog.Parts.Count()); + + var dependentsCatalog = filteredCatalog.IncludeDependents(); + Assert.Equal(2, dependentsCatalog.Parts.Count()); + } + + [Fact] + public void IncludeDependentsOpenGenericToOpenGenericReverse() + { + var catalog = CreateCatalog( + typeof(OpenGenericExporter<,>), + typeof(OpenGenericImporterReverseOrder<,>), + typeof(Other)); + var filteredCatalog = catalog.Filter(p => p.Imports()); + Assert.Equal(1, filteredCatalog.Parts.Count()); + + var dependentsCatalog = filteredCatalog.IncludeDependents(); + Assert.Equal(2, dependentsCatalog.Parts.Count()); + } + + [Fact] + public void IncludeDependentsOpenGenericToClosedGeneric() + { + var catalog = CreateCatalog( + typeof(OpenGenericExporter<,>), + typeof(ClosedGenericImporter), + typeof(Other)); + var filteredCatalog = catalog.Filter(p => p.Imports()); + Assert.Equal(1, filteredCatalog.Parts.Count()); + + var dependentsCatalog = filteredCatalog.IncludeDependents(); + Assert.Equal(2, dependentsCatalog.Parts.Count()); + } + + [Fact] + public void IncludeDependentsClosedToClosed() + { + var catalog = CreateCatalog( + typeof(SpecificGenericExporter), + typeof(ClosedGenericImporter), + typeof(Other)); + var filteredCatalog = catalog.Filter(p => p.Imports()); + Assert.Equal(1, filteredCatalog.Parts.Count()); + + var dependentsCatalog = filteredCatalog.IncludeDependents(); + Assert.Equal(2, dependentsCatalog.Parts.Count()); + } + + [Fact] + public void IncludeDependenciesSimpleChain() + { + var catalog = CreateCatalog( + typeof(Exporter1), + typeof(Exporter2), + typeof(Exporter2Import1), + typeof(Exporter3Import2), + typeof(Other)); + var filteredCatalog = catalog.Filter(p => p.Exports()); + Assert.Equal(2, filteredCatalog.Parts.Count()); + + var dependentsCatalog = filteredCatalog.IncludeDependencies(); + Assert.Equal(3, dependentsCatalog.Parts.Count()); + } + + [Fact] + public void IncludeDependenciesExportFactory() + { + var catalog = CreateCatalog( + typeof(Exporter1), + typeof(Exporter2Import1AsExportFactory), + typeof(Other)); + var filteredCatalog = catalog.Filter(p => p.Exports()); + Assert.Equal(1, filteredCatalog.Parts.Count()); + + var dependenciesCatalog = filteredCatalog.IncludeDependencies(); + Assert.Equal(2, dependenciesCatalog.Parts.Count()); + } + + [Fact] + [ActiveIssue(25498, TargetFrameworkMonikers.UapAot)] + public void IncludeDependenciesChainWithCycles() + { + var catalog = CreateCatalog( + typeof(Exporter1), + typeof(Exporter2), + typeof(Exporter2Import1), + typeof(Exporter1Import2), + typeof(Exporter3Import2), + typeof(Exporter2Import3), + typeof(Other)); + var filteredCatalog = catalog.Filter(p => p.Exports()); + Assert.Equal(3, filteredCatalog.Parts.Count()); + + var dependentsCatalog = filteredCatalog.IncludeDependents(); + Assert.Equal(5, dependentsCatalog.Parts.Count()); + } + + [Fact] + public void IncludeDependenciesSimpleChainOptional() + { + var catalog = CreateCatalog( + typeof(Exporter3OptionalImport2), + typeof(Exporter1), + typeof(Exporter2), + typeof(Exporter3), + typeof(Exporter2Import1), + typeof(Exporter3Import2), + typeof(Other)); + var filteredCatalog = catalog.Filter(p => p.Exports()); + Assert.Equal(2, filteredCatalog.Parts.Count()); + + var dependentsCatalog = filteredCatalog.IncludeDependencies(); + Assert.Equal(3, dependentsCatalog.Parts.Count()); + } + + [Fact] + public void IncludeDependenciesChainWithCyclesOptional() + { + var catalog = CreateCatalog( + typeof(Exporter1OptionalImport3), + typeof(Exporter1), + typeof(Exporter2), + typeof(Exporter3), + typeof(Exporter2Import1), + typeof(Exporter1Import2), + typeof(Other)); + var filteredCatalog = catalog.Filter(p => p.Exports()); + Assert.Equal(2, filteredCatalog.Parts.Count()); + + var dependentsCatalog = filteredCatalog.IncludeDependencies(); + Assert.Equal(5, dependentsCatalog.Parts.Count()); + } + + [Fact] + public void IncludeDependenciesSimpleChainOptionalOnly() + { + var catalog = CreateCatalog( + typeof(Exporter2OptionalImport3), + typeof(Exporter1), + typeof(Exporter2), + typeof(Exporter3), + typeof(Exporter2Import1), + typeof(Exporter1Import2), + typeof(Other)); + var filteredCatalog = catalog.Filter(p => p.Exports()); + Assert.Equal(3, filteredCatalog.Parts.Count()); + + var dependentsCatalog = filteredCatalog.IncludeDependencies(i => i.Cardinality == ImportCardinality.ZeroOrOne); + Assert.Equal(4, dependentsCatalog.Parts.Count()); + } + + [Fact] + public void IncludeDependenciesChainWithCyclesOptionalOnly() + { + var catalog = CreateCatalog( + typeof(Exporter3OptionalImport2), + typeof(Exporter2OptionalImport3), + typeof(Exporter1), + typeof(Exporter2), + typeof(Exporter3), + typeof(Exporter1Import2), + typeof(IOther)); + var filteredCatalog = catalog.Filter(p => p.Exports()); + Assert.Equal(2, filteredCatalog.Parts.Count()); + + var dependentsCatalog = filteredCatalog.IncludeDependencies(i => i.Cardinality == ImportCardinality.ZeroOrOne); + Assert.Equal(4, dependentsCatalog.Parts.Count()); + } + + [Fact] + public void IncludeDependciesOpenGenericToOpenGeneric() + { + var catalog = CreateCatalog( + typeof(OpenGenericExporter<,>), + typeof(OpenGenericImporter<,>), + typeof(Other)); + var filteredCatalog = catalog.Filter(p => p.Exports()); + Assert.Equal(1, filteredCatalog.Parts.Count()); + + var dependentsCatalog = filteredCatalog.IncludeDependencies(); + Assert.Equal(2, dependentsCatalog.Parts.Count()); + } + + [Fact] + public void IncludeDependenciesOpenGenericToOpenGenericReverse() + { + var catalog = CreateCatalog( + typeof(OpenGenericExporter<,>), + typeof(OpenGenericImporterReverseOrder<,>), + typeof(Other)); + var filteredCatalog = catalog.Filter(p => p.Exports()); + Assert.Equal(1, filteredCatalog.Parts.Count()); + + var dependentsCatalog = filteredCatalog.IncludeDependencies(); + Assert.Equal(2, dependentsCatalog.Parts.Count()); + } + + [Fact] + public void IncludeDependenciesOpenGenericToClosedGeneric() + { + var catalog = CreateCatalog( + typeof(OpenGenericExporter<,>), + typeof(ClosedGenericImporter), + typeof(Other)); + var filteredCatalog = catalog.Filter(p => p.Exports()); + Assert.Equal(1, filteredCatalog.Parts.Count()); + + var dependentsCatalog = filteredCatalog.IncludeDependencies(); + Assert.Equal(2, dependentsCatalog.Parts.Count()); + } + + [Fact] + public void IncludeDependenciesClosedToClosed() + { + var catalog = CreateCatalog( + typeof(SpecificGenericExporter), + typeof(ClosedGenericImporter), + typeof(Other)); + var filteredCatalog = catalog.Filter(p => p.Exports()); + Assert.Equal(1, filteredCatalog.Parts.Count()); + + var dependentsCatalog = filteredCatalog.IncludeDependencies(); + Assert.Equal(2, dependentsCatalog.Parts.Count()); + } + + [Export(typeof(IOther))] + public class Other : IOther + { + [Import] + public IOther Import { get; set; } + } + + [Export(typeof(IContract1))] + public class Exporter1 : IContract1 + { + } + + [Export(typeof(IContract2))] + public class Exporter2 : IContract2 + { + } + + [Export(typeof(IContract3))] + public class Exporter3 : IContract3 + { + } + + [Export(typeof(IContract2))] + public class Exporter2Import1 : IContract2 + { + [Import] + public IContract1 Import { get; set; } + } + + [Export(typeof(IContract2))] + public class Exporter2Import1AsExportFactory : IContract2 + { + [Import] + public ExportFactory Import { get; set; } + } + + [Export(typeof(IContract2))] + public class Exporter2OptionalImport1 : IContract2 + { + [Import(AllowDefault = true)] + public IContract1 Import { get; set; } + } + + [Export(typeof(IContract1))] + public class Exporter1OptionalImport3 : IContract1 + { + [Import(AllowDefault = true)] + public IContract3 Import { get; set; } + } + + [Export(typeof(IContract3))] + public class Exporter3OptionalImport2 : IContract3 + { + [Import(AllowDefault = true)] + public IContract2 Import { get; set; } + } + + [Export(typeof(IContract3))] + public class Exporter3OptionalImport1 : IContract3 + { + [Import(AllowDefault = true)] + public IContract1 Import { get; set; } + } + + [Export(typeof(IContract2))] + public class Exporter2OptionalImport3 : IContract2 + { + [Import(AllowDefault = true)] + public IContract3 Import { get; set; } + } + + [Export(typeof(IContract1))] + public class Exporter1Import2 : IContract1 + { + [Import] + public IContract2 Import { get; set; } + } + + [Export(typeof(IContract3))] + public class Exporter3Import2 : IContract3 + { + [Import] + public IContract2 Import { get; set; } + } + + [Export(typeof(IContract2))] + public class Exporter2Import3 : IContract2 + { + [Import] + public IContract3 Import { get; set; } + } + + public interface IContract { } + + [Export(typeof(IContract<,>))] + public class OpenGenericExporter : IContract + { + [Import] + public IContract2 Import { get; set; } + } + + [Export(typeof(IContract3))] + public class OpenGenericImporter : IContract3 + { + [Import] + IContract Import { get; set; } + } + + [Export(typeof(IContract3))] + public class OpenGenericImporterReverseOrder : IContract3 + { + [Import] + IContract Import { get; set; } + } + + [Export(typeof(IContract1))] + public class ClosedGenericImporter : IContract1 + { + [Import] + IContract Import { get; set; } + } + + [Export(typeof(IContract))] + public class SpecificGenericExporter : IContract + { + [Import] + public IContract2 Import { get; set; } + } + + public ComposablePartCatalog CreateCatalog(params Type[] types) + { + return new TypeCatalog(types); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/ImportEngineTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/ImportEngineTests.cs new file mode 100644 index 0000000000..669417a7d7 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/ImportEngineTests.cs @@ -0,0 +1,565 @@ +// 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.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class ImportEngineTests + { + [Fact] + public void PreviewImports_Successful_NoAtomicComposition_ShouldBlockChanges() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + var import = ImportDefinitionFactory.Create("Value"); + var importer = PartFactory.CreateImporter(import); + + exportProvider.AddExport("Value", 21); + + engine.PreviewImports(importer, null); + + Assert.Throws(() => + exportProvider.AddExport("Value", 22)); + + Assert.Throws(() => + exportProvider.RemoveExport("Value")); + + GC.KeepAlive(importer); + } + + [Fact] + public void PreviewImports_Unsuccessful_NoAtomicComposition_ShouldNotBlockChanges() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + var import = ImportDefinitionFactory.Create("Value"); + var importer = PartFactory.CreateImporter(import); + + Assert.Throws(() => + engine.PreviewImports(importer, null)); + + exportProvider.AddExport("Value", 22); + exportProvider.AddExport("Value", 23); + exportProvider.RemoveExport("Value"); + + GC.KeepAlive(importer); + } + + [Fact] + public void PreviewImports_Successful_AtomicComposition_Completeted_ShouldBlockChanges() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + var import = ImportDefinitionFactory.Create("Value"); + var importer = PartFactory.CreateImporter(import); + + exportProvider.AddExport("Value", 21); + + using (var atomicComposition = new AtomicComposition()) + { + engine.PreviewImports(importer, atomicComposition); + atomicComposition.Complete(); + } + + Assert.Throws(() => + exportProvider.AddExport("Value", 22)); + + Assert.Throws(() => + exportProvider.RemoveExport("Value")); + + GC.KeepAlive(importer); + } + + [Fact] + public void PreviewImports_Successful_AtomicComposition_RolledBack_ShouldNotBlockChanges() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + var import = ImportDefinitionFactory.Create("Value"); + var importer = PartFactory.CreateImporter(import); + + exportProvider.AddExport("Value", 21); + + using (var atomicComposition = new AtomicComposition()) + { + engine.PreviewImports(importer, atomicComposition); + + // Let atomicComposition get disposed thus rolledback + } + + exportProvider.AddExport("Value", 22); + exportProvider.RemoveExport("Value"); + + GC.KeepAlive(importer); + } + + [Fact] + public void PreviewImports_Unsuccessful_AtomicComposition_ShouldNotBlockChanges() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + var import = ImportDefinitionFactory.Create("Value"); + var importer = PartFactory.CreateImporter(import); + + using (var atomicComposition = new AtomicComposition()) + { + Assert.Throws(() => + engine.PreviewImports(importer, atomicComposition)); + } + + exportProvider.AddExport("Value", 22); + exportProvider.AddExport("Value", 23); + exportProvider.RemoveExport("Value"); + + GC.KeepAlive(importer); + } + + [Fact] + [ActiveIssue(25498, TargetFrameworkMonikers.UapAot)] + public void PreviewImports_ReleaseImports_ShouldNotBlockChanges() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + var import = ImportDefinitionFactory.Create("Value"); + var importer = PartFactory.CreateImporter(import); + + exportProvider.AddExport("Value", 21); + + engine.PreviewImports(importer, null); + + Assert.Throws(() => + exportProvider.AddExport("Value", 22)); + + Assert.Throws(() => + exportProvider.RemoveExport("Value")); + + engine.ReleaseImports(importer, null); + + exportProvider.AddExport("Value", 22); + exportProvider.RemoveExport("Value"); + + GC.KeepAlive(importer); + } + + [Fact] + public void PreviewImports_MissingOptionalImport_ShouldSucceed() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + var import = ImportDefinitionFactory.Create("Value", ImportCardinality.ZeroOrOne); + var importer = PartFactory.CreateImporter(import); + + engine.PreviewImports(importer, null); + + GC.KeepAlive(importer); + } + + [Fact] + public void PreviewImports_ZeroCollectionImport_ShouldSucceed() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + var import = ImportDefinitionFactory.Create("Value", ImportCardinality.ZeroOrMore); + var importer = PartFactory.CreateImporter(import); + + engine.PreviewImports(importer, null); + + GC.KeepAlive(importer); + } + + [Fact] + public void PreviewImports_MissingOptionalImport_NonRecomposable_ShouldNotBlockChanges() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + var import = ImportDefinitionFactory.Create("Value", ImportCardinality.ZeroOrOne, false, false); + var importer = PartFactory.CreateImporter(import); + + engine.PreviewImports(importer, null); + + exportProvider.AddExport("Value", 21); + exportProvider.AddExport("Value", 22); + exportProvider.RemoveExport("Value"); + + GC.KeepAlive(importer); + } + + [Fact] + public void PreviewImports_ZeroCollectionImport_NonRecomposable_ShouldNotBlockChanges() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + var import = ImportDefinitionFactory.Create("Value", ImportCardinality.ZeroOrMore, false, false); + var importer = PartFactory.CreateImporter(import); + + engine.PreviewImports(importer, null); + + exportProvider.AddExport("Value", 21); + exportProvider.AddExport("Value", 22); + exportProvider.RemoveExport("Value"); + + GC.KeepAlive(importer); + } + + [Fact] + public void SatisfyImports_NonRecomposable_ValueShouldNotChange() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + exportProvider.AddExport("Value", 21); + + var import = ImportDefinitionFactory.Create("Value", false); + var importer = PartFactory.CreateImporter(import); + + engine.SatisfyImports(importer); + + Assert.Equal(21, importer.GetImport(import)); + + // After rejection batch failures throw ChangeRejectedException to indicate that + // the failure did not affect the container + Assert.Throws(() => + exportProvider.ReplaceExportValue("Value", 42)); + + Assert.Equal(21, importer.GetImport(import)); + + GC.KeepAlive(importer); + } + + [Fact] + public void SatisfyImports_Recomposable_ValueShouldChange() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + exportProvider.AddExport("Value", 21); + + var import = ImportDefinitionFactory.Create("Value", true); + var importer = PartFactory.CreateImporter(import); + + engine.SatisfyImports(importer); + + Assert.Equal(21, importer.GetImport(import)); + + exportProvider.ReplaceExportValue("Value", 42); + + Assert.Equal(42, importer.GetImport(import)); + + GC.KeepAlive(importer); + } + + [Fact] + public void SatisfyImports_NonRecomposable_Prerequisite_ValueShouldNotChange() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + var import = ImportDefinitionFactory.Create("Value", false, true); + var importer = PartFactory.CreateImporter(import); + + exportProvider.AddExport("Value", 21); + + engine.SatisfyImports(importer); + + Assert.Equal(21, importer.GetImport(import)); + + Assert.Throws(() => + exportProvider.ReplaceExportValue("Value", 42)); + + Assert.Equal(21, importer.GetImport(import)); + + GC.KeepAlive(importer); + } + + [Fact] + public void SatisfyImports_Recomposable_Prerequisite_ValueShouldChange() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + var import = ImportDefinitionFactory.Create("Value", true, true); + var importer = PartFactory.CreateImporter(import); + + exportProvider.AddExport("Value", 21); + + engine.SatisfyImports(importer); + + Assert.Equal(21, importer.GetImport(import)); + + exportProvider.ReplaceExportValue("Value", 42); + + Assert.Equal(42, importer.GetImport(import)); + + GC.KeepAlive(importer); + } + + [Fact] + public void SatisfyImports_OneRecomposable_OneNotRecomposable() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + var import1 = ImportDefinitionFactory.Create("Value", true); + var import2 = ImportDefinitionFactory.Create("Value", false); + var importer = PartFactory.CreateImporter(import1, import2); + + exportProvider.AddExport("Value", 21); + + engine.SatisfyImports(importer); + + // Initial compose values should be 21 + Assert.Equal(21, importer.GetImport(import1)); + Assert.Equal(21, importer.GetImport(import2)); + + // Reset value to ensure it doesn't get set to same value again + importer.ResetImport(import1); + importer.ResetImport(import2); + + Assert.Throws(() => + exportProvider.ReplaceExportValue("Value", 42)); + + Assert.Equal(null, importer.GetImport(import1)); + Assert.Equal(null, importer.GetImport(import2)); + + GC.KeepAlive(importer); + } + + [Fact] + public void SatisfyImports_TwoRecomposables_SingleExportValueChanged() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + var import1 = ImportDefinitionFactory.Create("Value1", true); + var import2 = ImportDefinitionFactory.Create("Value2", true); + var importer = PartFactory.CreateImporter(import1, import2); + + exportProvider.AddExport("Value1", 21); + exportProvider.AddExport("Value2", 23); + + engine.SatisfyImports(importer); + + Assert.Equal(21, importer.GetImport(import1)); + Assert.Equal(23, importer.GetImport(import2)); + + importer.ResetImport(import1); + importer.ResetImport(import2); + + // Only change Value1 + exportProvider.ReplaceExportValue("Value1", 42); + + Assert.Equal(42, importer.GetImport(import1)); + + Assert.Equal(null, importer.GetImport(import2)); + + GC.KeepAlive(importer); + } + + [Fact] + public void SatisfyImports_Recomposable_Unregister_ValueShouldChangeOnce() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + exportProvider.AddExport("Value", 21); + + var import = ImportDefinitionFactory.Create("Value", true); + var importer = PartFactory.CreateImporter(import); + + engine.SatisfyImports(importer); + + Assert.Equal(21, importer.GetImport(import)); + + exportProvider.ReplaceExportValue("Value", 42); + + Assert.Equal(42, importer.GetImport(import)); + + engine.ReleaseImports(importer, null); + + exportProvider.ReplaceExportValue("Value", 666); + + Assert.Equal(42, importer.GetImport(import)); + + GC.KeepAlive(importer); + } + + [Fact] + public void SatisfyImports_MissingOptionalImport_NonRecomposable_ShouldNotBlockChanges() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + var import = ImportDefinitionFactory.Create("Value", ImportCardinality.ZeroOrOne, false, false); + var importer = PartFactory.CreateImporter(import); + + exportProvider.AddExport("Value", 20); + + engine.SatisfyImports(importer); + + Assert.Throws(() => + exportProvider.AddExport("Value", 21)); + + Assert.Throws(() => + exportProvider.RemoveExport("Value")); + + GC.KeepAlive(importer); + } + + [Fact] + public void SatisfyImports_ZeroCollectionImport_NonRecomposable_ShouldNotBlockChanges() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + var import = ImportDefinitionFactory.Create("Value", ImportCardinality.ZeroOrMore, false, false); + var importer = PartFactory.CreateImporter(import); + + exportProvider.AddExport("Value", 20); + + engine.SatisfyImports(importer); + + Assert.Throws(() => + exportProvider.AddExport("Value", 21)); + + Assert.Throws(() => + exportProvider.RemoveExport("Value")); + + GC.KeepAlive(importer); + } + + [Fact] + public void SatisfyImports_MissingOptionalImport_Recomposable_ShouldNotBlockChanges() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + var import = ImportDefinitionFactory.Create("Value", ImportCardinality.ZeroOrOne, true, false); + var importer = PartFactory.CreateImporter(import); + + exportProvider.AddExport("Value", 20); + + engine.SatisfyImports(importer); + + exportProvider.AddExport("Value", 21); + exportProvider.RemoveExport("Value"); + + GC.KeepAlive(importer); + } + + [Fact] + public void SatisfyImports_ZeroCollectionImport_Recomposable_ShouldNotBlockChanges() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + var import = ImportDefinitionFactory.Create("Value", ImportCardinality.ZeroOrMore, true, false); + var importer = PartFactory.CreateImporter(import); + + exportProvider.AddExport("Value", 20); + + engine.SatisfyImports(importer); + + exportProvider.AddExport("Value", 21); + exportProvider.RemoveExport("Value"); + + GC.KeepAlive(importer); + } + + [Fact] + public void SatisfyImportsOnce_Recomposable_ValueShouldNotChange_NoRecompositionRequested() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + exportProvider.AddExport("Value", 21); + + var import = ImportDefinitionFactory.Create("Value", true); + var importer = PartFactory.CreateImporter(import); + + engine.SatisfyImportsOnce(importer); + + Assert.Equal(21, importer.GetImport(import)); + + exportProvider.ReplaceExportValue("Value", 42); + + Assert.Equal(21, importer.GetImport(import)); + + GC.KeepAlive(importer); + } + + [Fact] + public void SatisifyImportsOnce_Recomposable_ValueShouldNotChange_NoRecompositionRequested_ViaNonArgumentSignature() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + exportProvider.AddExport("Value", 21); + + var import = ImportDefinitionFactory.Create("Value", true); + var importer = PartFactory.CreateImporter(import); + + engine.SatisfyImportsOnce(importer); + + Assert.Equal(21, importer.GetImport(import)); + + exportProvider.ReplaceExportValue("Value", 42); + + Assert.Equal(21, importer.GetImport(import)); + + GC.KeepAlive(importer); + } + + [Fact] + public void SatisfyImportsOnce_Successful_ShouldNotBlockChanges() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + var import = ImportDefinitionFactory.Create("Value"); + var importer = PartFactory.CreateImporter(import); + + exportProvider.AddExport("Value", 21); + + engine.SatisfyImportsOnce(importer); + + exportProvider.AddExport("Value", 22); + exportProvider.RemoveExport("Value"); + + GC.KeepAlive(importer); + } + + [Fact] + public void SatisfyImportsOnce_Unsuccessful_ShouldNotBlockChanges() + { + var exportProvider = ExportProviderFactory.CreateRecomposable(); + var engine = new ImportEngine(exportProvider); + + var import = ImportDefinitionFactory.Create("Value"); + var importer = PartFactory.CreateImporter(import); + + Assert.Throws(() => + engine.SatisfyImportsOnce(importer)); + + exportProvider.AddExport("Value", 22); + exportProvider.AddExport("Value", 23); + exportProvider.RemoveExport("Value"); + + GC.KeepAlive(importer); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/ScopeExtensionsTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/ScopeExtensionsTests.cs new file mode 100644 index 0000000000..08100595b7 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/ScopeExtensionsTests.cs @@ -0,0 +1,230 @@ +// 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.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Primitives; +using Xunit; + +namespace System.ComponentModel.Composition.Hosting +{ + public class ScopeExtensionsTests + { + [Fact] + public void Exports_Throws_OnNullPart() + { + ComposablePartDefinition part = null; + string contractName = "Contract1"; + Assert.Throws("part", () => + { + part.Exports(contractName); + }); + } + + [Fact] + public void Exports_Throws_OnNullContractName() + { + ComposablePartDefinition part = typeof(PartExportingContract1).AsPart(); + string contractName = null; + Assert.Throws("contractName", () => + { + part.Exports(contractName); + }); + } + + [Fact] + public void Exports() + { + ComposablePartDefinition part1 = typeof(PartExportingContract1).AsPart(); + ComposablePartDefinition part2 = typeof(PartExportingContract2).AsPart(); + + Assert.True(part1.Exports("Contract1")); + Assert.True(part2.Exports("Contract2")); + + Assert.False(part2.Exports("Contract1")); + Assert.False(part1.Exports("Contract2")); + } + + [Fact] + public void Imports_Throws_OnNullPart() + { + ComposablePartDefinition part = null; + string contractName = "Contract1"; + Assert.Throws("part", () => + { + part.Imports(contractName); + }); + } + + [Fact] + public void Imports_Throws_OnNullContractName() + { + ComposablePartDefinition part = typeof(PartImportingContract1).AsPart(); + string contractName = null; + Assert.Throws("contractName", () => + { + part.Imports(contractName); + }); + } + + [Fact] + public void Imports() + { + ComposablePartDefinition part1 = typeof(PartImportingContract1).AsPart(); + ComposablePartDefinition part2 = typeof(PartImportingContract2).AsPart(); + + Assert.True(part1.Imports("Contract1")); + Assert.True(part2.Imports("Contract2")); + + Assert.False(part2.Imports("Contract1")); + Assert.False(part1.Imports("Contract2")); + } + + [Fact] + public void Imports_CardinalityIgnored_WhenNotSpecified() + { + ComposablePartDefinition part1 = typeof(PartImportingContract1).AsPart(); + ComposablePartDefinition part1Multiple = typeof(PartImportingContract1Multiple).AsPart(); + ComposablePartDefinition part1Optional = typeof(PartImportingContract1Optionally).AsPart(); + + Assert.True(part1.Imports("Contract1")); + Assert.True(part1Optional.Imports("Contract1")); + Assert.True(part1Multiple.Imports("Contract1")); + } + + [Fact] + public void Imports_CardinalityNotIgnored_WhenSpecified() + { + ComposablePartDefinition part1 = typeof(PartImportingContract1).AsPart(); + ComposablePartDefinition part1Multiple = typeof(PartImportingContract1Multiple).AsPart(); + ComposablePartDefinition part1Optional = typeof(PartImportingContract1Optionally).AsPart(); + + Assert.True(part1.Imports("Contract1", ImportCardinality.ExactlyOne)); + Assert.False(part1.Imports("Contract1", ImportCardinality.ZeroOrMore)); + Assert.False(part1.Imports("Contract1", ImportCardinality.ZeroOrOne)); + + Assert.False(part1Multiple.Imports("Contract1", ImportCardinality.ExactlyOne)); + Assert.True(part1Multiple.Imports("Contract1", ImportCardinality.ZeroOrMore)); + Assert.False(part1Multiple.Imports("Contract1", ImportCardinality.ZeroOrOne)); + + Assert.False(part1Optional.Imports("Contract1", ImportCardinality.ExactlyOne)); + Assert.False(part1Optional.Imports("Contract1", ImportCardinality.ZeroOrMore)); + Assert.True(part1Optional.Imports("Contract1", ImportCardinality.ZeroOrOne)); + + } + + [Fact] + public void ContainsMetadataWithKey_Throws_OnNullPart() + { + ComposablePartDefinition part = null; + Assert.Throws("part", () => + { + part.ContainsPartMetadataWithKey("Name"); + }); + } + + [Fact] + public void ContainsMetadataWithKey_Throws_OnNullKey() + { + ComposablePartDefinition part = typeof(PartImportingContract1).AsPart(); + Assert.Throws("key", () => + { + part.ContainsPartMetadataWithKey(null); + }); + } + + [Fact] + public void ContainsMetadataWithKey() + { + ComposablePartDefinition part1 = typeof(PartWithMetadada).AsPart(); + + Assert.True(part1.ContainsPartMetadataWithKey("Name")); + Assert.True(part1.ContainsPartMetadataWithKey("Spores")); + Assert.True(part1.ContainsPartMetadataWithKey("Adds")); + + Assert.False(part1.ContainsPartMetadataWithKey("Description")); + } + + [Fact] + public void ContainsMetadata_Throws_OnNullPart() + { + ComposablePartDefinition part = null; + Assert.Throws("part", () => + { + part.ContainsPartMetadata("Name", "Festergut"); + }); + } + + [Fact] + public void ContainsMetadata_Throws_OnNullKey() + { + ComposablePartDefinition part = typeof(PartImportingContract1).AsPart(); + Assert.Throws("key", () => + { + part.ContainsPartMetadata(null, "Festergut"); + }); + } + + [Fact] + public void ContainsMetadata() + { + ComposablePartDefinition part1 = typeof(PartWithMetadada).AsPart(); + + Assert.True(part1.ContainsPartMetadata("Name", "Festergut")); + Assert.False(part1.ContainsPartMetadata("Name", "Rotface")); + Assert.False(part1.ContainsPartMetadata("Name", null)); + + Assert.True(part1.ContainsPartMetadata("Spores", 3)); + Assert.False(part1.ContainsPartMetadata("Spores", 3L)); + Assert.False(part1.ContainsPartMetadata("Spores", 5)); + Assert.False(part1.ContainsPartMetadata("Spores", null)); + + Assert.True(part1.ContainsPartMetadata("Adds", null)); + Assert.False(part1.ContainsPartMetadata("Adds", "Ooze")); + + Assert.False(part1.ContainsPartMetadata("Ability", "Pungent Blight")); + } + + [Export("Contract1")] + public class PartExportingContract1 + { + } + + [Export("Contract2")] + public class PartExportingContract2 + { + } + + public class PartImportingContract1 + { + [Import("Contract1")] + public object import; + } + + public class PartImportingContract2 + { + [Import("Contract2")] + public object import; + } + + public class PartImportingContract1Optionally + { + [Import("Contract1", AllowDefault = true)] + public object import; + } + + public class PartImportingContract1Multiple + { + [ImportMany("Contract1")] + public object import; + } + + [PartMetadata("Name", "Festergut")] + [PartMetadata("Spores", 3)] + [PartMetadata("Adds", null)] + [Export("Contract1")] + public class PartWithMetadada + { + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/TypeCatalogTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/TypeCatalogTests.cs new file mode 100644 index 0000000000..281ddcf756 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Hosting/TypeCatalogTests.cs @@ -0,0 +1,567 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Globalization; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + [Export] + public class TypeCatalogTestsExporter { } + + // This is a glorious do nothing ReflectionContext + public class TypeCatalogTestsReflectionContext : ReflectionContext + { + public override Assembly MapAssembly(Assembly assembly) + { + return assembly; + } + + public override TypeInfo MapType(TypeInfo type) + { + return type; + } + } + + public class TypeCatalogTests + { + public static void Constructor_NullReflectionContextArgument_ShouldThrowArgumentNull(Func catalogCreator) + { + Assert.Throws("reflectionContext", () => + { + var catalog = catalogCreator(null); + }); + } + + public static void Constructor_NullDefinitionOriginArgument_ShouldThrowArgumentNull(Func catalogCreator) + { + Assert.Throws("definitionOrigin", () => + { + var catalog = catalogCreator(null); + }); + } + + [Fact] + public void Constructor1_ReflectOnlyTypes_ShouldThrowArgumentNull() + { + TypeCatalogTests.Constructor_NullReflectionContextArgument_ShouldThrowArgumentNull((rc) => + { + return new TypeCatalog(new Type[0], rc); + }); + } + + [Fact] + public void Constructor3_NullDefinitionOriginArgument_ShouldThrowArgumentNull() + { + TypeCatalogTests.Constructor_NullDefinitionOriginArgument_ShouldThrowArgumentNull((dO) => + { + return new TypeCatalog(new Type[0], dO); + }); + } + + [Fact] + public void Constructor4_NullReflectionContextArgument_ShouldThrowArgumentNull() + { + TypeCatalogTests.Constructor_NullReflectionContextArgument_ShouldThrowArgumentNull((rc) => + { + return new TypeCatalog(new Type[0], rc, new TypeCatalog(new Type[0])); + }); + } + + [Fact] + public void Constructor4_NullDefinitionOriginArgument_ShouldThrowArgumentNull() + { + TypeCatalogTests.Constructor_NullDefinitionOriginArgument_ShouldThrowArgumentNull((dO) => + { + return new TypeCatalog(new Type[0], new TypeCatalogTestsReflectionContext(), dO); + }); + } + + [Fact] + public void Constructor2_NullAsTypesArgument_ShouldThrowArgumentNull() + { + Assert.Throws("types", () => + { + new TypeCatalog((Type[])null); + }); + } + + [Fact] + public void Constructor3_NullAsTypesArgument_ShouldThrowArgumentNull() + { + Assert.Throws("types", () => + { + new TypeCatalog((IEnumerable)null); + }); + } + + [Fact] + public void Constructor2_ArrayWithNullAsTypesArgument_ShouldThrowArgument() + { + Assert.Throws("types", () => + { + new TypeCatalog(new Type[] { null }); + }); + } + + [Fact] + public void Constructor3_ArrayWithNullAsTypesArgument_ShouldThrowArgument() + { + Assert.Throws("types", () => + { + new TypeCatalog((IEnumerable)new Type[] { null }); + }); + } + + [Fact] + public void Constructor2_EmptyEnumerableAsTypesArgument_ShouldSetPartsPropertyToEmptyEnumerable() + { + var catalog = new TypeCatalog(Enumerable.Empty()); + + Assert.Empty(catalog.Parts); + } + + [Fact] + public void Constructor3_EmptyArrayAsTypesArgument_ShouldSetPartsPropertyToEmpty() + { + var catalog = new TypeCatalog(new Type[0]); + + Assert.Empty(catalog.Parts); + } + + [Fact] + public void Constructor2_ArrayAsTypesArgument_ShouldNotAllowModificationAfterConstruction() + { + var types = new Type[] { PartFactory.GetAttributedExporterType() }; + var catalog = new TypeCatalog(types); + + types[0] = null; + + Assert.NotNull(catalog.Parts.First()); + } + + [Fact] + public void Constructor3_ArrayAsTypesArgument_ShouldNotAllowModificationAfterConstruction() + { + var types = new Type[] { PartFactory.GetAttributedExporterType() }; + var catalog = new TypeCatalog((IEnumerable)types); + + types[0] = null; + + Assert.NotNull(catalog.Parts.First()); + } + + [Fact] + public void Constructor2_ShouldSetOriginToNull() + { + var catalog = (ICompositionElement)new TypeCatalog(PartFactory.GetAttributedExporterType()); + + Assert.Null(catalog.Origin); + } + + [Fact] + public void Constructor3_ShouldSetOriginToNull() + { + var catalog = (ICompositionElement)new TypeCatalog((IEnumerable)new Type[] { PartFactory.GetAttributedExporterType() }); + + Assert.Null(catalog.Origin); + } + + [Fact] + public void DisplayName_WhenCatalogDisposed_ShouldNotThrow() + { + var catalog = CreateTypeCatalog(); + catalog.Dispose(); + + var displayName = ((ICompositionElement)catalog).DisplayName; + } + + [Fact] + public void Origin_WhenCatalogDisposed_ShouldNotThrow() + { + var catalog = CreateTypeCatalog(); + catalog.Dispose(); + + var origin = ((ICompositionElement)catalog).Origin; + } + + [Fact] + public void Parts_WhenCatalogDisposed_ShouldThrowObjectDisposed() + { + var catalog = CreateTypeCatalog(); + catalog.Dispose(); + + ExceptionAssert.ThrowsDisposed(catalog, () => + { + var parts = catalog.Parts; + }); + } + + [Fact] + public void ToString_WhenCatalogDisposed_ShouldNotThrow() + { + var catalog = CreateTypeCatalog(); + catalog.Dispose(); + + catalog.ToString(); + } + + [Fact] + public void GetExports_WhenCatalogDisposed_ShouldThrowObjectDisposed() + { + var catalog = CreateTypeCatalog(); + catalog.Dispose(); + var definition = ImportDefinitionFactory.Create(); + + ExceptionAssert.ThrowsDisposed(catalog, () => + { + catalog.GetExports(definition); + }); + } + + [Fact] + public void GetExports_NullAsConstraintArgument_ShouldThrowArgumentNull() + { + var catalog = CreateTypeCatalog(); + + Assert.Throws("definition", () => + { + catalog.GetExports((ImportDefinition)null); + }); + } + + [Fact] + public void Dispose_ShouldNotThrow() + { + using (var catalog = CreateTypeCatalog()) + { + } + } + + [Fact] + public void Dispose_CanBeCalledMultipleTimes() + { + var catalog = CreateTypeCatalog(); + catalog.Dispose(); + catalog.Dispose(); + catalog.Dispose(); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void Parts() + { + var catalog = new TypeCatalog(Assembly.GetExecutingAssembly().GetTypes()); + Assert.NotNull(catalog.Parts); + Assert.True(catalog.Parts.Count() > 0); + } + + [Fact] + public void Parts_ShouldSetDefinitionOriginToCatalogItself() + { + var catalog = CreateTypeCatalog(); + Assert.True(catalog.Parts.Count() > 0); + + foreach (ICompositionElement definition in catalog.Parts) + { + Assert.Same(catalog, definition.Origin); + } + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void ICompositionElementDisplayName_SingleTypeAsTypesArgument_ShouldIncludeCatalogTypeNameAndTypeFullName() + { + var expectations = Expectations.GetAttributedTypes(); + + foreach (var e in expectations) + { + var catalog = (ICompositionElement)CreateTypeCatalog(e); + + string expected = string.Format(SR.TypeCatalog_DisplayNameFormat, typeof(TypeCatalog).Name, AttributedModelServices.GetTypeIdentity(e)); + + Assert.Equal(expected, catalog.DisplayName); + } + } + + [Fact] + public void ICompositionElementDisplayName_ValueAsTypesArgument_ShouldIncludeCatalogTypeNameAndTypeFullNames() + { + var expectations = new ExpectationCollection(); + expectations.Add(new Type[] { typeof(Type) }, + GetDisplayName(false, typeof(TypeCatalog))); + + expectations.Add(new Type[] { typeof(ExportValueTypeSingleton) }, + GetDisplayName(false, typeof(TypeCatalog), typeof(ExportValueTypeSingleton))); + + expectations.Add(new Type[] { typeof(ExportValueTypeSingleton), typeof(ExportValueTypeSingleton) }, + GetDisplayName(false, typeof(TypeCatalog), typeof(ExportValueTypeSingleton), typeof(ExportValueTypeSingleton))); + + expectations.Add(new Type[] { typeof(ExportValueTypeSingleton), typeof(string), typeof(ExportValueTypeSingleton) }, + GetDisplayName(false, typeof(TypeCatalog), typeof(ExportValueTypeSingleton), typeof(ExportValueTypeSingleton))); + + expectations.Add(new Type[] { typeof(ExportValueTypeSingleton), typeof(ExportValueTypeFactory) }, + GetDisplayName(false, typeof(TypeCatalog), typeof(ExportValueTypeSingleton), typeof(ExportValueTypeFactory))); + + expectations.Add(new Type[] { typeof(ExportValueTypeSingleton), typeof(ExportValueTypeFactory), typeof(CallbackExecuteCodeDuringCompose) }, + GetDisplayName(true, typeof(TypeCatalog), typeof(ExportValueTypeSingleton), typeof(ExportValueTypeFactory))); + + foreach (var e in expectations) + { + var catalog = (ICompositionElement)CreateTypeCatalog(e.Input); + + Assert.Equal(e.Output, catalog.DisplayName); + } + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void ICompositionElementDisplayName_ShouldIncludeDerivedCatalogTypeNameAndTypeFullNames() + { + var expectations = Expectations.GetAttributedTypes(); + + foreach (var e in expectations) + { + var catalog = (ICompositionElement)new DerivedTypeCatalog(e); + + string expected = string.Format(SR.TypeCatalog_DisplayNameFormat, typeof(DerivedTypeCatalog).Name, AttributedModelServices.GetTypeIdentity(e)); + + Assert.Equal(expected, catalog.DisplayName); + } + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void ToString_ShouldReturnICompositionElementDisplayName() + { + var expectations = Expectations.GetAttributedTypes(); + + foreach (var e in expectations) + { + var catalog = (ICompositionElement)CreateTypeCatalog(e); + + Assert.Equal(catalog.DisplayName, catalog.ToString()); + } + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void GetExports() + { + var catalog = new TypeCatalog(Assembly.GetExecutingAssembly().GetTypes()); + Expression> constraint = (ExportDefinition exportDefinition) => exportDefinition.ContractName == AttributedModelServices.GetContractName(typeof(MyExport)); + IEnumerable> matchingExports = catalog.GetExports(constraint); + Assert.NotNull(matchingExports); + Assert.True(matchingExports.Count() >= 0); + + IEnumerable> expectedMatchingExports = catalog.Parts + .SelectMany(part => part.ExportDefinitions, (part, export) => new Tuple(part, export)) + .Where(partAndExport => partAndExport.Item2.ContractName == AttributedModelServices.GetContractName(typeof(MyExport))); + Assert.True(matchingExports.SequenceEqual(expectedMatchingExports)); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void TwoTypesWithSameSimpleName() + { + var catalog = new TypeCatalog(Assembly.GetExecutingAssembly().GetTypes()); + var container = new CompositionContainer(catalog); + + NotSoUniqueName unique1 = container.GetExportedValue(); + + Assert.NotNull(unique1); + + Assert.Equal(23, unique1.MyIntProperty); + + NotSoUniqueName2.NotSoUniqueName nestedUnique = container.GetExportedValue(); + + Assert.NotNull(nestedUnique); + + Assert.Equal("MyStringProperty", nestedUnique.MyStringProperty); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void GettingFunctionExports() + { + var catalog = new TypeCatalog(Assembly.GetExecutingAssembly().GetTypes()); + var container = new CompositionContainer(catalog); + + ImportDefaultFunctions import = container.GetExportedValue("ImportDefaultFunctions"); + import.VerifyIsBound(); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void AnExportOfAnInstanceThatFailsToCompose() + { + var catalog = new TypeCatalog(Assembly.GetExecutingAssembly().GetTypes()); + var container = new CompositionContainer(catalog); + + // Rejection causes the part in the catalog whose imports cannot be + // satisfied to be ignored, resulting in a cardinality mismatch instead of a + // composition exception + ExceptionAssert.Throws(() => + { + container.GetExportedValue("ExportMyString"); + }); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void SharedPartCreation() + { + var catalog = new TypeCatalog(Assembly.GetExecutingAssembly().GetTypes()); + var container = new CompositionContainer(catalog); + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(new Int32Exporter(41)); + container.Compose(batch); + + var sharedPart1 = container.GetExportedValue(); + Assert.Equal(41, sharedPart1.Value); + var sharedPart2 = container.GetExportedValue(); + Assert.Equal(41, sharedPart2.Value); + + Assert.Equal(sharedPart1, sharedPart2); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void NonSharedPartCreation() + { + var catalog = new TypeCatalog(Assembly.GetExecutingAssembly().GetTypes()); + var container = new CompositionContainer(catalog); + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(new Int32Exporter(41)); + container.Compose(batch); + + var nonSharedPart1 = container.GetExportedValue(); + Assert.Equal(41, nonSharedPart1.Value); + var nonSharedPart2 = container.GetExportedValue(); + Assert.Equal(41, nonSharedPart2.Value); + + Assert.NotEqual(nonSharedPart1, nonSharedPart2); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void RecursiveNonSharedPartCreation() + { + var catalog = new TypeCatalog(Assembly.GetExecutingAssembly().GetTypes()); + var container = new CompositionContainer(catalog); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotGetExportedValue, () => + { + container.GetExportedValue(); + }); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotGetExportedValue, () => + { + container.GetExportedValue(); + }); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotGetExportedValue, () => + { + container.GetExportedValue(); + }); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotGetExportedValue, () => + { + container.GetExportedValue(); + }); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotGetExportedValue, () => + { + container.GetExportedValue(); + }); + + Assert.NotNull(container.GetExportedValue()); + Assert.NotNull(container.GetExportedValue()); + Assert.NotNull(container.GetExportedValue()); + Assert.NotNull(container.GetExportedValue()); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void TryToDiscoverExportWithGenericParameter() + { + var catalog = new TypeCatalog(Assembly.GetExecutingAssembly().GetTypes()); + var container = new CompositionContainer(catalog); + + // Should find a type that inherits from an export + Assert.NotNull(container.GetExportedValueOrDefault(AttributedModelServices.GetContractName(typeof(ExportWhichInheritsFromGeneric)))); + + // This should be exported because it is inherited by ExportWhichInheritsFromGeneric + Assert.NotNull(container.GetExportedValueOrDefault(AttributedModelServices.GetContractName(typeof(ExportWithGenericParameter)))); + } + + private string GetDisplayName(bool useEllipses, Type catalogType, params Type[] types) + { + return String.Format(CultureInfo.CurrentCulture, + SR.TypeCatalog_DisplayNameFormat, + catalogType.Name, + this.GetTypesDisplay(useEllipses, types)); + } + + private string GetTypesDisplay(bool useEllipses, Type[] types) + { + int count = types.Length; + if (count == 0) + { + return SR.TypeCatalog_Empty; + } + + StringBuilder builder = new StringBuilder(); + foreach (Type type in types) + { + if (builder.Length > 0) + { + builder.Append(CultureInfo.CurrentCulture.TextInfo.ListSeparator); + builder.Append(" "); + } + + builder.Append(type.FullName); + } + + if (useEllipses) + { // Add an elipse to indicate that there + // are more types than actually listed + builder.Append(CultureInfo.CurrentCulture.TextInfo.ListSeparator); + builder.Append(" ..."); + } + + return builder.ToString(); + } + + private TypeCatalog CreateTypeCatalog() + { + var type = PartFactory.GetAttributedExporterType(); + + return CreateTypeCatalog(type); + } + + private TypeCatalog CreateTypeCatalog(params Type[] types) + { + return new TypeCatalog(types); + } + + private class DerivedTypeCatalog : TypeCatalog + { + public DerivedTypeCatalog(params Type[] types) + : base(types) + { + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ImportAttributeTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ImportAttributeTests.cs new file mode 100644 index 0000000000..b4390e415f --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ImportAttributeTests.cs @@ -0,0 +1,187 @@ +// 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.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + [Export] + public class WorkingType + { + } + + [Export] + public class Constants + { + [Export("Seven")] + int seven; + + public Constants() + { + seven = 7; + if (seven == default) + throw new ArgumentException(); + } + } + + [Export] + public class ExportWithIndexer + { + private int[] data = new int[10]; + + [Import("Seven")] + public int this[int index] + { + get { return data[index]; } + set { data[index] = value; } + } + } + + public class ImportAttributeTests + { + [Fact] + public void Constructor1_ShouldSetContractNamePropertyToEmptyString() + { + var attribute = new ImportAttribute(); + + Assert.Null(attribute.ContractName); + Assert.Null(attribute.ContractType); + } + + [Fact] + public void Constructor2_NullAsContractNameArgument_ShouldSetContractNamePropertyToEmptyString() + { + var attribute = new ImportAttribute((string)null); + + Assert.Null(attribute.ContractName); + Assert.Null(attribute.ContractType); + } + + [Fact] + public void Constructor3_NullAsContractTypeArgument_ShouldSetContractNamePropertyToEmptyString() + { + var attribute = new ImportAttribute((Type)null); + + Assert.Null(attribute.ContractName); + Assert.Null(attribute.ContractType); + } + + [Fact] + public void Constructor4_NullAsContractTypeArgument_ShouldSetContractNamePropertyToEmptyString() + { + var attribute = new ImportAttribute((string)null, (Type)null); + + Assert.Null(attribute.ContractName); + Assert.Null(attribute.ContractType); + } + + [Fact] + public void Constructor2_ValueAsContractNameArgument_ShouldSetContractNameProperty() + { + var expectations = Expectations.GetContractNamesWithEmpty(); + + foreach (var e in expectations) + { + var attribute = new ImportAttribute(e); + + Assert.Equal(e, attribute.ContractName); + } + } + + [Fact] + public void Constructor1_ShouldSetAllowDefaultPropertyToFalse() + { + var attribute = new ImportAttribute(); + + Assert.False(attribute.AllowDefault); + } + + [Fact] + public void Constructor2_ShouldSetAllowDefaultPropertyToFalse() + { + var attribute = new ImportAttribute("ContractName"); + + Assert.False(attribute.AllowDefault); + } + + [Fact] + public void Constructor3_ShouldSetAllowDefaultPropertyToFalse() + { + var attribute = new ImportAttribute(typeof(String)); + + Assert.False(attribute.AllowDefault); + } + + [Fact] + public void Constructor1_ShouldSetAllowRecompositionPropertyToFalse() + { + var attribute = new ImportAttribute(); + + Assert.False(attribute.AllowRecomposition); + } + + [Fact] + public void Constructor2_ShouldSetAllowRecompositionPropertyToFalse() + { + var attribute = new ImportAttribute("ContractName"); + + Assert.False(attribute.AllowRecomposition); + } + + [Fact] + public void Constructor3_ShouldSetAllowRecompositionPropertyToFalse() + { + var attribute = new ImportAttribute(typeof(String)); + + Assert.False(attribute.AllowRecomposition); + } + + [Fact] + public void AllowDefault_ValueAsValueArgument_ShouldSetProperty() + { + var expectations = Expectations.GetBooleans(); + + var attribute = new ImportAttribute(); + + foreach (var e in expectations) + { + attribute.AllowDefault = e; + Assert.Equal(e, attribute.AllowDefault); + } + } + + [Fact] + public void AllowRecomposition_ValueAsValueArgument_ShouldSetProperty() + { + var expectations = Expectations.GetBooleans(); + + var attribute = new ImportAttribute(); + + foreach (var e in expectations) + { + attribute.AllowRecomposition = e; + Assert.Equal(e, attribute.AllowRecomposition); + } + } + + [Fact] + public void ImportIndexers_ShouldThrowSomething() + { + var con = new CompositionContainer( + new TypeCatalog(typeof(WorkingType), typeof(Constants), typeof(ExportWithIndexer)) + ); + + var v1 = con.GetExportedValue(); + + ExceptionAssert.Throws(RetryMode.DoNotRetry, () => + { + var v2 = con.GetExportedValue(); + Console.WriteLine(v2.ToString()); + }); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ImportDefinitionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ImportDefinitionTests.cs new file mode 100644 index 0000000000..14c401cf30 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ImportDefinitionTests.cs @@ -0,0 +1,242 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Linq.Expressions; +using System.Text.RegularExpressions; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class ImportDefinitionTests + { + [Fact] + public void Constructor1_ShouldSetCardinalityPropertyToExactlyOne() + { + var definition = new NoOverridesImportDefinition(); + + Assert.Equal(ImportCardinality.ExactlyOne, definition.Cardinality); + } + + [Fact] + public void Constructor1_ShouldSetIsPrerequisitePropertyToTrue() + { + var definition = new NoOverridesImportDefinition(); + + Assert.True(definition.IsPrerequisite); + } + + [Fact] + public void Constructor1_ShouldSetIsRecomposablePropertyToFalse() + { + var definition = new NoOverridesImportDefinition(); + + Assert.False(definition.IsRecomposable); + } + + [Fact] + public void Constructor2_NullAsConstraintArgument_ShouldThrowArgumentNull() + { + Assert.Throws("constraint", () => + { + new ImportDefinition((Expression>)null, "", ImportCardinality.ExactlyOne, false, false); + }); + } + + [Fact] + public void Constructor2_OutOfRangeValueAsCardinalityArgument_ShouldThrowArgument() + { + var expectations = Expectations.GetInvalidEnumValues(); + + foreach (var e in expectations) + { + Assert.Throws("cardinality", () => + { + new ImportDefinition(d => true, "", e, false, false); + }); + } + } + + [Fact] + public void Constructor2_ValueAsCardinalityArgument_ShouldSetCardinalityProperty() + { + var expectations = Expectations.GetEnumValues(); + + foreach (var e in expectations) + { + var definition = new ImportDefinition(d => true, "", e, false, false); + + Assert.Equal(e, definition.Cardinality); + } + } + + [Fact] + public void Constructor2_ValueAsConstraintArgument_ShouldSetConstraintProperty() + { + var expectations = new List>>(); + expectations.Add(d => d.ContractName == "ContractName"); + expectations.Add(d => d.ContractName.Equals("ContractName")); + expectations.Add(d => (string)d.Metadata["Name"] == "Value"); + expectations.Add(d => true); + + foreach (var e in expectations) + { + var definition = new ImportDefinition(e, "", ImportCardinality.ExactlyOne, false, false); + + Assert.Equal(e, definition.Constraint); + } + } + + [Fact] + public void Constructor2_ValueAsIsRecomposableArgument_ShouldSetIsRecomposableProperty() + { + var expectations = Expectations.GetBooleans(); + + foreach (var e in expectations) + { + var definition = new ImportDefinition(d => true, "", ImportCardinality.ExactlyOne, e, false); + + Assert.Equal(e, definition.IsRecomposable); + } + } + + [Fact] + public void Constructor2_ValueAsIsPrerequisiteArgument_ShouldSetIsPrerequisiteProperty() + { + var expectations = Expectations.GetBooleans(); + + foreach (var e in expectations) + { + var definition = new ImportDefinition(d => true, "", ImportCardinality.ExactlyOne, false, e); + + Assert.Equal(e, definition.IsPrerequisite); + } + } + + [Fact] + public void Constructor2_ContractName_ShouldSetAppropriately() + { + var expectations = new ExpectationCollection(); + + expectations.Add(null, string.Empty); + expectations.Add(string.Empty, string.Empty); + expectations.Add("Contract", "Contract"); + + string cn = AttributedModelServices.GetContractName(typeof(ImportDefinitionTests)); + expectations.Add(cn, cn); + + foreach (var e in expectations) + { + var definition = new ImportDefinition(d => true, e.Input, ImportCardinality.ExactlyOne, false, false); + + Assert.Equal(e.Output, definition.ContractName); + } + } + + [Fact] + public void Constraint_WhenNotOverridden_ShouldThrowNotImplemented() + { + var definition = new NoOverridesImportDefinition(); + + ExceptionAssert.Throws(() => + { + var constraint = definition.Constraint; + }); + } + + [Fact] + public void ToString_WhenConstraintPropertyNotOverridden_ShouldThrowNotImplemented() + { + var definition = new NoOverridesImportDefinition(); + + ExceptionAssert.Throws(() => + { + definition.ToString(); + }); + } + + [Fact] + [ActiveIssue(25498)] + public void ToString_ValueAsConstraintArgument_ShouldReturnConstraintProperty() + { + var expectations = new ExpectationCollection>, string>(); + expectations.Add(d => d.ContractName == "ContractName", @"d.ContractName ==? ""ContractName"""); + expectations.Add(d => d.ContractName.Equals("ContractName"), @"d.ContractName.Equals\(""ContractName""\)"); + expectations.Add(d => (string)d.Metadata["Name"] == "Value", @"Convert\(d.Metadata.get_Item\(""Name""\)\) ==? ""Value"""); + expectations.Add(d => true, "True"); + + foreach (var e in expectations) + { + var item = new ImportDefinition(e.Input, "", ImportCardinality.ExactlyOne, false, false); + + Assert.True(Regex.IsMatch(item.ToString(), e.Output)); + } + } + + [Fact] + [ActiveIssue(25498)] + public void ToString_DerivedImportDefinition_ShouldReturnOverriddenConstraintProperty() + { + var expectations = new ExpectationCollection>, string>(); + expectations.Add(d => d.ContractName == "ContractName", @"d.ContractName ==? ""ContractName"""); + expectations.Add(d => d.ContractName.Equals("ContractName"), @"d.ContractName.Equals\(""ContractName""\)"); + expectations.Add(d => (string)d.Metadata["Name"] == "Value", @"Convert\(d.Metadata.get_Item\(""Name""\)\) ==? ""Value"""); + expectations.Add(d => true, "True"); + + foreach (var e in expectations) + { + var item = new DerivedImportDefinition(e.Input); + + Assert.True(Regex.IsMatch(item.ToString(), e.Output)); + } + } + + [Fact] + [ActiveIssue(738535)] + public void ContractName_ShouldBeIncludedInConstraintAutomatically() + { + string testContractName = "TestContractName"; + var contractImportDefinition = new ImportDefinition(ed => true, testContractName, ImportCardinality.ZeroOrMore, false, false); + + var shouldMatch = new ExportDefinition(testContractName, null); + var shouldNotMatch = new ExportDefinition(testContractName + testContractName, null); + + Assert.True(contractImportDefinition.IsConstraintSatisfiedBy(shouldMatch)); + Assert.False(contractImportDefinition.IsConstraintSatisfiedBy(shouldNotMatch)); + } + + [Fact] + public void EmptyContractName_ShouldMatchAllContractNames() + { + var importDefinition = new ImportDefinition(ed => true, string.Empty, ImportCardinality.ZeroOrMore, false, false); + + var shouldMatch1 = new ExportDefinition("contract1", null); + var shouldMatch2 = new ExportDefinition("contract2", null); + + Assert.True(importDefinition.IsConstraintSatisfiedBy(shouldMatch1)); + Assert.True(importDefinition.IsConstraintSatisfiedBy(shouldMatch2)); + } + + private class NoOverridesImportDefinition : ImportDefinition + { + } + + private class DerivedImportDefinition : ImportDefinition + { + private readonly Expression> _constraint; + + public DerivedImportDefinition(Expression> constraint) + { + _constraint = constraint; + } + + public override Expression> Constraint + { + get { return _constraint ?? base.Constraint; } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ImportSourceImportDefinitionHelpers.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ImportSourceImportDefinitionHelpers.cs new file mode 100644 index 0000000000..059e491b17 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ImportSourceImportDefinitionHelpers.cs @@ -0,0 +1,119 @@ +// 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.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq.Expressions; +using Microsoft.Internal; +using System.Diagnostics.Contracts; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel.Composition.Primitives; + +namespace System.ComponentModel.Composition.Hosting +{ + + internal static class ImportSourceImportDefinitionHelpers + { + public static ImportDefinition RemoveImportSource(this ImportDefinition definition) + { + var contractBasedDefinition = definition as ContractBasedImportDefinition; + if(contractBasedDefinition == null) + { + return definition; + } + else + { + return new NonImportSourceImportDefinition(contractBasedDefinition); + } + } + + internal class NonImportSourceImportDefinition : ContractBasedImportDefinition + { + private ContractBasedImportDefinition _sourceDefinition; + private IDictionary _metadata; + + public NonImportSourceImportDefinition(ContractBasedImportDefinition sourceDefinition) + { + Assumes.NotNull(sourceDefinition); + this._sourceDefinition = sourceDefinition; + this._metadata = null; + } + + public override string ContractName + { + get { return this._sourceDefinition.ContractName; } + } + + public override IDictionary Metadata + { + get + { + Contract.Ensures(Contract.Result>() != null); + + var reply = this._metadata; + if(reply == null) + { + reply = new Dictionary (this._sourceDefinition.Metadata); + reply.Remove(CompositionConstants.ImportSourceMetadataName); + this._metadata = reply; + } + + return reply; + } + } + + public override ImportCardinality Cardinality + { + get { return this._sourceDefinition.Cardinality; } + } + + public override Expression> Constraint + { + get { return this._sourceDefinition.Constraint; } + } + + public override bool IsPrerequisite + { + get { return this._sourceDefinition.IsPrerequisite; } + } + + public override bool IsRecomposable + { + get { return this._sourceDefinition.IsRecomposable; } + } + + public override bool IsConstraintSatisfiedBy(ExportDefinition exportDefinition) + { + Requires.NotNull(exportDefinition, "exportDefinition"); + + return this._sourceDefinition.IsConstraintSatisfiedBy(exportDefinition); + } + + public override string ToString() + { + return this._sourceDefinition.ToString(); + } + + public override string RequiredTypeIdentity + { + get { return this._sourceDefinition.RequiredTypeIdentity; } + } + + public override IEnumerable> RequiredMetadata + { + get + { + return this._sourceDefinition.RequiredMetadata; + } + } + + public override CreationPolicy RequiredCreationPolicy + { + get { return this._sourceDefinition.RequiredCreationPolicy; } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ImportingComposablePart.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ImportingComposablePart.cs new file mode 100644 index 0000000000..bab74f8187 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ImportingComposablePart.cs @@ -0,0 +1,143 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using Xunit; + +namespace System.ComponentModel.Composition +{ + internal class ImportingComposablePart : ComposablePart + { + private readonly List _importDefinitions = new List(); + private readonly List _exportDefinitions = new List(); + private Dictionary _importValues = new Dictionary(); + + public ImportingComposablePart(ImportCardinality cardinality, bool isRecomposable, params string[] contractNames) + : this((string)null, cardinality, isRecomposable, contractNames) + { + } + + public ImportingComposablePart(string exportContractName, ImportCardinality cardinality, bool isRecomposable, params string[] contractNames) + { + if (exportContractName != null) + { + var definition = ExportDefinitionFactory.Create(exportContractName); + + _exportDefinitions.Add(definition); + } + + foreach (string contractName in contractNames) + { + var definition = ImportDefinitionFactory.Create(contractName, + cardinality, + isRecomposable, + false); + + _importDefinitions.Add(definition); + } + } + + public ImportingComposablePart(params ImportDefinition[] importDefintions) + { + _importDefinitions.AddRange(importDefintions); + } + + public override IEnumerable ExportDefinitions + { + get { return this._exportDefinitions; } + } + + public override IEnumerable ImportDefinitions + { + get { return this._importDefinitions; } + } + + public override IDictionary Metadata + { + get { return new Dictionary(); } + } + + public int ImportSatisfiedCount + { + get; + private set; + } + + public void ResetImportSatisfiedCount() + { + ImportSatisfiedCount = 0; + } + + public object Value + { + get + { + Assert.Equal(1, _importValues.Count); + + return _importValues.Values.First(); + } + } + + public object GetImport(string contractName) + { + foreach (var pair in _importValues) + { + var definition = (ContractBasedImportDefinition)pair.Key; + if (definition.ContractName == contractName) + { + return pair.Value; + } + } + + return null; + } + + public object GetImport(ImportDefinition definition) + { + Assert.True(_importValues.ContainsKey(definition)); + return _importValues[definition]; + } + + public override object GetExportedValue(ExportDefinition definition) + { + throw new NotImplementedException(); + } + + public override void SetImport(ImportDefinition definition, IEnumerable exports) + { + Assert.True(_importDefinitions.Contains(definition)); + + ImportSatisfiedCount++; + + _importValues[definition] = GetExportValue(exports); + } + + public void ResetImport(ImportDefinition definition) + { + Assert.True(_importDefinitions.Contains(definition)); + _importValues[definition] = null; + } + + private object GetExportValue(IEnumerable exports) + { + var exportedValues = exports.Select(export => export.Value); + + int count = exportedValues.Count(); + if (count == 0) + { + return null; + } + else if (count == 1) + { + return exportedValues.First(); + } + + return exportedValues.ToArray(); + } + } + +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ImportingConstructorAttributeTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ImportingConstructorAttributeTests.cs new file mode 100644 index 0000000000..1e88bdf88e --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ImportingConstructorAttributeTests.cs @@ -0,0 +1,19 @@ +// 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 Xunit; + +namespace System.ComponentModel.Composition +{ + public class ImportingConstructorAttributeTests + { + [Fact] + public void Constructor_ShouldNotThrow() + { + var attribute = new ImportingConstructorAttribute(); + + Assert.NotNull(attribute); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/InitializationScopeTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/InitializationScopeTests.cs new file mode 100644 index 0000000000..a4fa7232fd --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/InitializationScopeTests.cs @@ -0,0 +1,146 @@ +// 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.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class InitializationScopeTests + { + [Fact] + public void SingleContainerSimpleCompose() + { + var container = ContainerFactory.Create(); + ImportingComposablePart importPart; + CompositionBatch batch = new CompositionBatch(); + + batch.AddExportedValue("value1", "Hello"); + batch.AddExportedValue("value2", "World"); + batch.AddPart(importPart = PartFactory.CreateImporter("value1", "value2")); + container.Compose(batch); + + Assert.Equal(2, importPart.ImportSatisfiedCount); + Assert.Equal("Hello", importPart.GetImport("value1")); + Assert.Equal("World", importPart.GetImport("value2")); + } + + [Fact] + public void ParentedContainerSimpleCompose() + { + var container = ContainerFactory.Create(); + var importPart = PartFactory.CreateImporter("value1", "value2"); + + CompositionBatch batch = new CompositionBatch(); + batch.AddExportedValue("value1", "Parent"); + + var childContainer = new CompositionContainer(container); + CompositionBatch childBatch = new CompositionBatch(); + childBatch.AddExportedValue("value2", "Child"); + childBatch.AddPart(importPart); + + Assert.Equal(0, importPart.ImportSatisfiedCount); + + container.Compose(batch); + childContainer.Compose(childBatch); + + Assert.Equal(2, importPart.ImportSatisfiedCount); + Assert.Equal("Parent", importPart.GetImport("value1")); + Assert.Equal("Child", importPart.GetImport("value2")); + } + + [Fact] + public void SingleContainerPartReplacement() + { + var container = ContainerFactory.Create(); + var importPart = PartFactory.CreateImporter(true, "value1", "value2"); + + CompositionBatch batch = new CompositionBatch(); + var export1Key = batch.AddExportedValue("value1", "Hello"); + batch.AddExportedValue("value2", "World"); + batch.AddPart(importPart); + container.Compose(batch); + + Assert.Equal(2, importPart.ImportSatisfiedCount); + Assert.Equal("Hello", importPart.GetImport("value1")); + Assert.Equal("World", importPart.GetImport("value2")); + + importPart.ResetImportSatisfiedCount(); + + batch = new CompositionBatch(); + batch.RemovePart(export1Key); + batch.AddExportedValue("value1", "Goodbye"); + container.Compose(batch); + + Assert.Equal(1, importPart.ImportSatisfiedCount); + Assert.Equal("Goodbye", importPart.GetImport("value1")); + Assert.Equal("World", importPart.GetImport("value2")); + } + + [Fact] + public void ParentedContainerPartReplacement() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + var importPart = PartFactory.CreateImporter(true, "value1", "value2"); + var exportKey = batch.AddExportedValue("value1", "Parent"); + + var childContainer = new CompositionContainer(container); + CompositionBatch childBatch = new CompositionBatch(); + childBatch.AddExportedValue("value2", "Child"); + childBatch.AddPart(importPart); + + Assert.Equal(0, importPart.ImportSatisfiedCount); + container.Compose(batch); + childContainer.Compose(childBatch); + + Assert.Equal(2, importPart.ImportSatisfiedCount); + Assert.Equal("Parent", importPart.GetImport("value1")); + Assert.Equal("Child", importPart.GetImport("value2")); + + importPart.ResetImportSatisfiedCount(); + batch = new CompositionBatch(); + batch.RemovePart(exportKey); + batch.AddExportedValue("value1", "New Parent"); + container.Compose(batch); + + Assert.Equal(1, importPart.ImportSatisfiedCount); + Assert.Equal("New Parent", importPart.GetImport("value1")); + Assert.Equal("Child", importPart.GetImport("value2")); + } + + [Fact] + public void SelectiveRecompose() + { + var container = ContainerFactory.Create(); + var stableImporter = PartFactory.CreateImporter("stable"); + var dynamicImporter = PartFactory.CreateImporter("dynamic", true); + CompositionBatch batch = new CompositionBatch(); + + batch.AddPart(stableImporter); + batch.AddPart(dynamicImporter); + var exportKey = batch.AddExportedValue("dynamic", 1); + batch.AddExportedValue("stable", 42); + container.Compose(batch); + + Assert.Equal(1, stableImporter.ImportSatisfiedCount); + Assert.Equal(stableImporter.GetImport("stable"), 42); + Assert.Equal(1, dynamicImporter.ImportSatisfiedCount); + Assert.Equal(dynamicImporter.GetImport("dynamic"), 1); + + batch = new CompositionBatch(); + stableImporter.ResetImportSatisfiedCount(); + dynamicImporter.ResetImportSatisfiedCount(); + batch.RemovePart(exportKey); + batch.AddExportedValue("dynamic", 2); + container.Compose(batch); + + Assert.Equal(0, stableImporter.ImportSatisfiedCount); + Assert.Equal(stableImporter.GetImport("stable"), 42); + Assert.Equal(1, dynamicImporter.ImportSatisfiedCount); + Assert.Equal(dynamicImporter.GetImport("dynamic"), 2); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/MetadataAttributeAttributeTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/MetadataAttributeAttributeTests.cs new file mode 100644 index 0000000000..3497d7f17e --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/MetadataAttributeAttributeTests.cs @@ -0,0 +1,19 @@ +// 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 Xunit; + +namespace System.ComponentModel.Composition +{ + public class MetadataAttributeAttributeTests + { + [Fact] + public void Constructor_ShouldNotThrow() + { + var attribute = new MetadataAttributeAttribute(); + + Assert.NotNull(attribute); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/MetadataTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/MetadataTests.cs new file mode 100644 index 0000000000..1fd1ea7c41 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/MetadataTests.cs @@ -0,0 +1,1237 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class MetadataTests + { + #region Tests for metadata on exports + + public enum SimpleEnum + { + First + } + + [PartNotDiscoverable] + [Export] + [ExportMetadata("String", "42")] + [ExportMetadata("Int", 42)] + [ExportMetadata("Float", 42.0f)] + [ExportMetadata("Enum", SimpleEnum.First)] + [ExportMetadata("Type", typeof(string))] + [ExportMetadata("Object", 42)] + public class SimpleMetadataExporter + { + } + + [PartNotDiscoverable] + [Export] + [ExportMetadata("String", null)] // null + [ExportMetadata("Int", 42)] + [ExportMetadata("Float", 42.0f)] + [ExportMetadata("Enum", SimpleEnum.First)] + [ExportMetadata("Type", typeof(string))] + [ExportMetadata("Object", 42)] + public class SimpleMetadataExporterWithNullReferenceValue + { + } + + [PartNotDiscoverable] + [Export] + [ExportMetadata("String", "42")] + [ExportMetadata("Int", null)] //null + [ExportMetadata("Float", 42.0f)] + [ExportMetadata("Enum", SimpleEnum.First)] + [ExportMetadata("Type", typeof(string))] + [ExportMetadata("Object", 42)] + public class SimpleMetadataExporterWithNullNonReferenceValue + { + } + + [PartNotDiscoverable] + [Export] + [ExportMetadata("String", "42")] + [ExportMetadata("Int", "42")] // wrong type + [ExportMetadata("Float", 42.0f)] + [ExportMetadata("Enum", SimpleEnum.First)] + [ExportMetadata("Type", typeof(string))] + [ExportMetadata("Object", 42)] + public class SimpleMetadataExporterWithTypeMismatch + { + } + + public interface ISimpleMetadataView + { + string String { get; } + int Int { get; } + float Float { get; } + SimpleEnum Enum { get; } + Type Type { get; } + object Object { get; } + } + + [Fact] + public void SimpleMetadataTest() + { + var container = ContainerFactory.Create(); + container.ComposeParts(new SimpleMetadataExporter()); + + var export = container.GetExport(); + + Assert.Equal("42", export.Metadata.String); + Assert.Equal(42, export.Metadata.Int); + Assert.Equal(42.0f, export.Metadata.Float); + Assert.Equal(SimpleEnum.First, export.Metadata.Enum); + Assert.Equal(typeof(string), export.Metadata.Type); + Assert.Equal(42, export.Metadata.Object); + } + + [Fact] + public void SimpleMetadataTestWithNullReferenceValue() + { + var container = ContainerFactory.Create(); + container.ComposeParts(new SimpleMetadataExporterWithNullReferenceValue()); + + var export = container.GetExport(); + + Assert.Equal(null, export.Metadata.String); + Assert.Equal(42, export.Metadata.Int); + Assert.Equal(42.0f, export.Metadata.Float); + Assert.Equal(SimpleEnum.First, export.Metadata.Enum); + Assert.Equal(typeof(string), export.Metadata.Type); + Assert.Equal(42, export.Metadata.Object); + } + + [Fact] + public void SimpleMetadataTestWithNullNonReferenceValue() + { + var container = ContainerFactory.Create(); + container.ComposeParts(new SimpleMetadataExporterWithNullNonReferenceValue()); + + var exports = container.GetExports(); + Assert.False(exports.Any()); + } + + [Fact] + public void SimpleMetadataTestWithTypeMismatch() + { + var container = ContainerFactory.Create(); + container.ComposeParts(new SimpleMetadataExporterWithTypeMismatch()); + + var exports = container.GetExports(); + Assert.False(exports.Any()); + } + + [Fact] + public void ValidMetadataTest() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(new MyExporterWithValidMetadata()); + container.Compose(batch); + + var typeVi = container.GetExport>(); + var metadataFoo = typeVi.Metadata["foo"] as IList; + Assert.Equal(2, metadataFoo.Count()); + Assert.True(metadataFoo.Contains("bar1"), "The metadata collection should include value 'bar1'"); + Assert.True(metadataFoo.Contains("bar2"), "The metadata collection should include value 'bar2'"); + Assert.Equal("world", typeVi.Metadata["hello"]); + Assert.Equal("GoodOneValue2", typeVi.Metadata["GoodOne2"]); + + var metadataAcme = typeVi.Metadata["acme"] as IList; + Assert.Equal(2, metadataAcme.Count()); + Assert.True(metadataAcme.Contains("acmebar"), "The metadata collection should include value 'bar'"); + Assert.True(metadataAcme.Contains(2.0), "The metadata collection should include value 2"); + + var memberVi = container.GetExport, IDictionary>("ContractForValidMetadata"); + var metadataBar = memberVi.Metadata["bar"] as IList; + Assert.Equal(2, metadataBar.Count()); + Assert.True(metadataBar.Contains("foo1"), "The metadata collection should include value 'foo1'"); + Assert.True(metadataBar.Contains("foo2"), "The metadata collection should include value 'foo2'"); + Assert.Equal("hello", memberVi.Metadata["world"]); + Assert.Equal("GoodOneValue2", memberVi.Metadata["GoodOne2"]); + + var metadataStuff = memberVi.Metadata["stuff"] as IList; + Assert.Equal(2, metadataAcme.Count()); + Assert.True(metadataStuff.Contains("acmebar"), "The metadata collection should include value 'acmebar'"); + Assert.True(metadataStuff.Contains(2.0), "The metadata collection should include value 2"); + + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void ValidMetadataDiscoveredByComponentCatalogTest() + { + var container = ContainerFactory.CreateWithDefaultAttributedCatalog(); + ValidMetadataDiscoveredByCatalog(container); + } + + private void ValidMetadataDiscoveredByCatalog(CompositionContainer container) + { + var export1 = container.GetExport>(); + + var metadataFoo = export1.Metadata["foo"] as IList; + Assert.Equal(2, metadataFoo.Count()); + Assert.True(metadataFoo.Contains("bar1"), "The metadata collection should include value 'bar1'"); + Assert.True(metadataFoo.Contains("bar2"), "The metadata collection should include value 'bar2'"); + Assert.Equal("world", export1.Metadata["hello"]); + Assert.Equal("GoodOneValue2", export1.Metadata["GoodOne2"]); + + var metadataAcme = export1.Metadata["acme"] as IList; + Assert.Equal(2, metadataAcme.Count()); + Assert.True(metadataAcme.Contains("acmebar"), "The metadata collection should include value 'bar'"); + Assert.True(metadataAcme.Contains(2.0), "The metadata collection should include value 2"); + + var export2 = container.GetExport, IDictionary>("ContractForValidMetadata"); + var metadataBar = export2.Metadata["bar"] as IList; + Assert.Equal(2, metadataBar.Count()); + Assert.True(metadataBar.Contains("foo1"), "The metadata collection should include value 'foo1'"); + Assert.True(metadataBar.Contains("foo2"), "The metadata collection should include value 'foo2'"); + Assert.Equal("hello", export2.Metadata["world"]); + Assert.Equal("GoodOneValue2", export2.Metadata["GoodOne2"]); + + var metadataStuff = export2.Metadata["stuff"] as IList; + Assert.Equal(2, metadataAcme.Count()); + Assert.True(metadataStuff.Contains("acmebar"), "The metadata collection should include value 'acmebar'"); + Assert.True(metadataStuff.Contains(2.0), "The metadata collection should include value 2"); + } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + [MetadataAttribute] + public class BadStrongMetadata : Attribute + { + public string SelfConflicted { get { return "SelfConflictedValue"; } } + } + + [Export] + [BadStrongMetadata] + [ExportMetadata("InvalidCollection", "InvalidCollectionValue1")] + [ExportMetadata("InvalidCollection", "InvalidCollectionValue2", IsMultiple = true)] + [BadStrongMetadata] + [ExportMetadata("RepeatedMetadata", "RepeatedMetadataValue1")] + [ExportMetadata("RepeatedMetadata", "RepeatedMetadataValue2")] + [ExportMetadata("GoodOne1", "GoodOneValue1")] + [ExportMetadata("ConflictedOne1", "ConfilictedOneValue1")] + [GoodStrongMetadata] + [ExportMetadata("ConflictedOne2", "ConflictedOne2Value2")] + [PartNotDiscoverable] + public class MyExporterWithInvalidMetadata + { + [Export("ContractForInvalidMetadata")] + [ExportMetadata("ConflictedOne1", "ConfilictedOneValue1")] + [GoodStrongMetadata] + [ExportMetadata("ConflictedOne2", "ConflictedOne2Value2")] + [ExportMetadata("RepeatedMetadata", "RepeatedMetadataValue1")] + [ExportMetadata("RepeatedMetadata", "RepeatedMetadataValue2")] + [BadStrongMetadata] + [ExportMetadata("InvalidCollection", "InvalidCollectionValue1")] + [ExportMetadata("InvalidCollection", "InvalidCollectionValue2", IsMultiple = true)] + [BadStrongMetadata] + [ExportMetadata("GoodOne1", "GoodOneValue1")] + public double DoSomething() { return 0.618; } + } + + [Export] + [ExportMetadata("DuplicateMetadataName", "My Name")] + [ExportMetadata("DuplicateMetadataName", "Your Name")] + [PartNotDiscoverable] + public class ClassWithInvalidDuplicateMetadataOnType + { + + } + + [Fact] + public void InvalidDuplicateMetadataOnType_ShouldThrow() + { + var part = AttributedModelServices.CreatePart(new ClassWithInvalidDuplicateMetadataOnType()); + var export = part.ExportDefinitions.First(); + var ex = ExceptionAssert.Throws(RetryMode.DoNotRetry, () => + { + var metadata = export.Metadata; + }); + + Assert.True(ex.Message.Contains("DuplicateMetadataName")); + } + + [PartNotDiscoverable] + public class ClassWithInvalidDuplicateMetadataOnMember + { + [Export] + [ExportMetadata("DuplicateMetadataName", "My Name")] + [ExportMetadata("DuplicateMetadataName", "Your Name")] + public ClassWithDuplicateMetadataOnMember Member { get; set; } + } + + [Fact] + public void InvalidDuplicateMetadataOnMember_ShouldThrow() + { + var part = AttributedModelServices.CreatePart(new ClassWithInvalidDuplicateMetadataOnMember()); + var export = part.ExportDefinitions.First(); + + var ex = ExceptionAssert.Throws(RetryMode.DoNotRetry, () => + { + var metadata = export.Metadata; + }); + + Assert.True(ex.Message.Contains("DuplicateMetadataName")); + } + + [Export] + [ExportMetadata("DuplicateMetadataName", "My Name", IsMultiple = true)] + [ExportMetadata("DuplicateMetadataName", "Your Name", IsMultiple = true)] + public class ClassWithValidDuplicateMetadataOnType + { + + } + + [Fact] + public void ValidDuplicateMetadataOnType_ShouldDiscoverAllMetadata() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(new ClassWithValidDuplicateMetadataOnType()); + + container.Compose(batch); + + var export = container.GetExport>(); + + var names = export.Metadata["DuplicateMetadataName"] as string[]; + + Assert.Equal(2, names.Length); + } + + public class ClassWithDuplicateMetadataOnMember + { + [Export] + [ExportMetadata("DuplicateMetadataName", "My Name", IsMultiple = true)] + [ExportMetadata("DuplicateMetadataName", "Your Name", IsMultiple = true)] + public ClassWithDuplicateMetadataOnMember Member { get; set; } + } + + [Fact] + public void ValidDuplicateMetadataOnMember_ShouldDiscoverAllMetadata() + { + var container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(new ClassWithDuplicateMetadataOnMember()); + + container.Compose(batch); + + var export = container.GetExport>(); + + var names = export.Metadata["DuplicateMetadataName"] as string[]; + + Assert.Equal(2, names.Length); + } + + [Export] + [ExportMetadata(CompositionConstants.PartCreationPolicyMetadataName, "My Policy")] + [PartNotDiscoverable] + public class ClassWithReservedMetadataValue + { + + } + + [Fact] + public void InvalidMetadata_UseOfReservedName_ShouldThrow() + { + var part = AttributedModelServices.CreatePart(new ClassWithReservedMetadataValue()); + var export = part.ExportDefinitions.First(); + + var ex = ExceptionAssert.Throws(RetryMode.DoNotRetry, () => + { + var metadata = export.Metadata; + }); + + Assert.True(ex.Message.Contains(CompositionConstants.PartCreationPolicyMetadataName)); + } + + #endregion + + #region Tests for weakly supported metadata as part of contract + + [Fact] + [ActiveIssue(468388)] + public void FailureImportForNoRequiredMetadatForExportCollection() + { + CompositionContainer container = ContainerFactory.Create(); + + MyImporterWithExportCollection importer; + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(new MyExporterWithNoMetadata()); + batch.AddPart(importer = new MyImporterWithExportCollection()); + + throw new NotImplementedException(); + + //var result = container.TryCompose(); + + //Assert.True(result.Succeeded, "Composition should be successful because collection import is not required"); + //Assert.Equal(1, result.Issues.Count); + //Assert.True(result.Issues[0].Description.Contains("Foo"), "The missing required metadata is 'Foo'"); + } + + [Fact] + [ActiveIssue(472538)] + public void FailureImportForNoRequiredMetadataThroughComponentCatalogTest() + { + var container = ContainerFactory.CreateWithDefaultAttributedCatalog(); + FailureImportForNoRequiredMetadataThroughCatalog(container); + } + + private void FailureImportForNoRequiredMetadataThroughCatalog(CompositionContainer container) + { + throw new NotImplementedException(); + + //var export1 = container.GetExport(); + + //export1.TryGetExportedValue().VerifyFailure(CompositionIssueId.RequiredMetadataNotFound, CompositionIssueId.CardinalityMismatch); + + //var export2 = container.GetExport(); + //export2.TryGetExportedValue().VerifySuccess(CompositionIssueId.RequiredMetadataNotFound); + + //container.TryGetExportedValue().VerifyFailure(CompositionIssueId.RequiredMetadataNotFound, CompositionIssueId.CardinalityMismatch); + } + + [Fact] + [ActiveIssue(468388)] + public void SelectiveImportBasedOnMetadataForExport() + { + CompositionContainer container = ContainerFactory.Create(); + + MyImporterWithExportForSelectiveImport importer; + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(new MyExporterWithNoMetadata()); + batch.AddPart(new MyExporterWithMetadata()); + batch.AddPart(importer = new MyImporterWithExportForSelectiveImport()); + + throw new NotImplementedException(); + //var result = container.TryCompose(); + + //Assert.True(result.Succeeded, "Composition should be successfull because one of two exports meets both the contract name and metadata requirement"); + //Assert.Equal(1, result.Issues.Count); + //Assert.True(result.Issues[0].Description.Contains("Foo"), "The missing required metadata is 'Foo'"); + //Assert.NotNull(importer.ValueInfo, "The import should really get bound"); + } + + [Fact] + [ActiveIssue(468388)] + public void SelectiveImportBasedOnMetadataForExportCollection() + { + CompositionContainer container = ContainerFactory.Create(); + + MyImporterWithExportCollectionForSelectiveImport importer; + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(new MyExporterWithNoMetadata()); + batch.AddPart(new MyExporterWithMetadata()); + batch.AddPart(importer = new MyImporterWithExportCollectionForSelectiveImport()); + + throw new NotImplementedException(); + + //var result = container.TryCompose(); + + //Assert.True(result.Succeeded, "Composition should be successfull in anyway for collection import"); + //Assert.Equal(1, result.Issues.Count); + //Assert.True(result.Issues[0].Description.Contains("Foo"), "The missing required metadata is 'Foo'"); + //Assert.Equal(1, importer.ValueInfoCol.Count); + //Assert.NotNull(importer.ValueInfoCol[0], "The import should really get bound"); + } + + [Fact] + [ActiveIssue(472538)] + public void SelectiveImportBasedOnMetadataThruoughComponentCatalogTest() + { + var container = ContainerFactory.CreateWithDefaultAttributedCatalog(); + SelectiveImportBasedOnMetadataThruoughCatalog(container); + } + + private void SelectiveImportBasedOnMetadataThruoughCatalog(CompositionContainer container) + { + throw new NotImplementedException(); + + //var export1 = container.GetExport(); + //export1.TryGetExportedValue().VerifySuccess(CompositionIssueId.RequiredMetadataNotFound); + + //var export2 = container.GetExport(); + //export2.TryGetExportedValue().VerifySuccess(CompositionIssueId.RequiredMetadataNotFound); + } + + [Fact] + public void ChildParentContainerTest1() + { + CompositionContainer parent = ContainerFactory.Create(); + CompositionContainer child = new CompositionContainer(parent); + + CompositionBatch childBatch = new CompositionBatch(); + CompositionBatch parentBatch = new CompositionBatch(); + parentBatch.AddPart(new MyExporterWithNoMetadata()); + childBatch.AddPart(new MyExporterWithMetadata()); + parent.Compose(parentBatch); + child.Compose(childBatch); + + var exports = child.GetExports(CreateImportDefinition(typeof(IMyExporter), "Foo")); + + Assert.Equal(1, exports.Count()); + } + + [Fact] + public void ChildParentContainerTest2() + { + CompositionContainer parent = ContainerFactory.Create(); + CompositionContainer child = new CompositionContainer(parent); + + CompositionBatch childBatch = new CompositionBatch(); + CompositionBatch parentBatch = new CompositionBatch(); + parentBatch.AddPart(new MyExporterWithMetadata()); + childBatch.AddPart(new MyExporterWithNoMetadata()); + parent.Compose(parentBatch); + + var exports = child.GetExports(CreateImportDefinition(typeof(IMyExporter), "Foo")); + + Assert.Equal(1, exports.Count()); + } + + [Fact] + public void ChildParentContainerTest3() + { + CompositionContainer parent = ContainerFactory.Create(); + CompositionContainer child = new CompositionContainer(parent); + + CompositionBatch childBatch = new CompositionBatch(); + CompositionBatch parentBatch = new CompositionBatch(); + + parentBatch.AddPart(new MyExporterWithMetadata()); + childBatch.AddPart(new MyExporterWithMetadata()); + parent.Compose(parentBatch); + child.Compose(childBatch); + + var exports = child.GetExports(CreateImportDefinition(typeof(IMyExporter), "Foo")); + + Assert.Equal(2, exports.Count()); + } + + private static ImportDefinition CreateImportDefinition(Type type, string metadataKey) + { + return new ContractBasedImportDefinition(AttributedModelServices.GetContractName(typeof(IMyExporter)), null, new KeyValuePair[] { new KeyValuePair(metadataKey, typeof(object)) }, ImportCardinality.ZeroOrMore, true, true, CreationPolicy.Any); + } + + #endregion + + #region Tests for strongly typed metadata as part of contract + + [Fact] + [ActiveIssue(468388)] + public void SelectiveImportBySTM_Export() + { + CompositionContainer container = ContainerFactory.Create(); + CompositionBatch batch = new CompositionBatch(); + + MyImporterWithExportStronglyTypedMetadata importer; + batch.AddPart(new MyExporterWithNoMetadata()); + batch.AddPart(new MyExporterWithMetadata()); + batch.AddPart(importer = new MyImporterWithExportStronglyTypedMetadata()); + + throw new NotImplementedException(); + + //var result = container.TryCompose(); + + //Assert.True(result.Succeeded, "Composition should be successful becasue one of two exports does not have required metadata"); + //Assert.Equal(1, result.Issues.Count); + //Assert.NotNull(importer.ValueInfo, "The valid export should really get bound"); + //Assert.Equal("Bar", importer.ValueInfo.Metadata.Foo); + } + + [Fact] + [ActiveIssue(468388)] + public void SelectiveImportBySTM_ExportCollection() + { + CompositionContainer container = ContainerFactory.Create(); + + MyImporterWithExportCollectionStronglyTypedMetadata importer; + CompositionBatch batch = new CompositionBatch(); + + batch.AddPart(new MyExporterWithNoMetadata()); + batch.AddPart(new MyExporterWithMetadata()); + batch.AddPart(importer = new MyImporterWithExportCollectionStronglyTypedMetadata()); + + throw new NotImplementedException(); + + //var result = container.TryCompose(); + + //Assert.True(result.Succeeded, "Collection import should be successful in anyway"); + //Assert.Equal(1, result.Issues.Count); + //Assert.Equal(1, importer.ValueInfoCol.Count); + //Assert.Equal("Bar", importer.ValueInfoCol.First().Metadata.Foo); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void SelectiveImportBySTMThroughComponentCatalog1() + { + var container = ContainerFactory.CreateWithDefaultAttributedCatalog(); + SelectiveImportBySTMThroughCatalog1(container); + } + + public void SelectiveImportBySTMThroughCatalog1(CompositionContainer container) + { + Assert.NotNull(container.GetExport()); + var result2 = container.GetExports(); + } + + [Fact] + [ActiveIssue(468388)] + public void SelectiveImportBySTMThroughComponentCatalog2() + { + var container = ContainerFactory.CreateWithDefaultAttributedCatalog(); + SelectiveImportBySTMThroughCatalog2(container); + } + + public void SelectiveImportBySTMThroughCatalog2(CompositionContainer container) + { + throw new NotImplementedException(); + + //var export1 = container.GetExport(); + //var result1 = export1.TryGetExportedValue().VerifySuccess(CompositionIssueId.RequiredMetadataNotFound); + //Assert.NotNull(result1.Value.ValueInfo, "The valid export should really get bound"); + //Assert.Equal("Bar", result1.Value.ValueInfo.Metadata.Foo); + + //var export2 = container.GetExport(); + //var result2 = export2.TryGetExportedValue().VerifySuccess(CompositionIssueId.RequiredMetadataNotFound); + //Assert.Equal(1, result2.Value.ValueInfoCol.Count); + //Assert.Equal("Bar", result2.Value.ValueInfoCol.First().Metadata.Foo); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void TestMultipleStronglyTypedAttributes() + { + var container = ContainerFactory.CreateWithDefaultAttributedCatalog(); + + var export = container.GetExport(); + EnumerableAssert.AreEqual(export.Metadata.OptionNames.OrderBy(s => s), "name1", "name2", "name3"); + EnumerableAssert.AreEqual(export.Metadata.OptionValues.OrderBy(o => o.ToString()), "value1", "value2", "value3"); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void TestMultipleStronglyTypedAttributesAsIEnumerable() + { + var container = ContainerFactory.CreateWithDefaultAttributedCatalog(); + + var export = container.GetExport(); + EnumerableAssert.AreEqual(export.Metadata.OptionNames.OrderBy(s => s), "name1", "name2", "name3"); + EnumerableAssert.AreEqual(export.Metadata.OptionValues.OrderBy(o => o.ToString()), "value1", "value2", "value3"); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void TestMultipleStronglyTypedAttributesAsArray() + { + var container = ContainerFactory.CreateWithDefaultAttributedCatalog(); + + var export = container.GetExport(); + EnumerableAssert.AreEqual(export.Metadata.OptionNames.OrderBy(s => s), "name1", "name2", "name3"); + EnumerableAssert.AreEqual(export.Metadata.OptionValues.OrderBy(o => o.ToString()), "value1", "value2", "value3"); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void TestMultipleStronglyTypedAttributesWithInvalidType() + { + var container = ContainerFactory.CreateWithDefaultAttributedCatalog(); + + // IMyOption2 actually contains all the correct properties but just the wrong types. This should cause us to not match the exports by metadata + var exports = container.GetExports(); + Assert.Equal(0, exports.Count()); + } + + [Fact] + public void TestOptionalMetadataValueTypeMismatch() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(OptionalFooIsInt)); + var exports = container.GetExports(); + Assert.Equal(1, exports.Count()); + var export = exports.Single(); + Assert.Equal(null, export.Metadata.OptionalFoo); + } + + #endregion + + [ExportMetadata("Name", "FromBaseType")] + public abstract class BaseClassWithMetadataButNoExport + { + } + + [Export(typeof(BaseClassWithMetadataButNoExport))] + public class DerivedClassWithExportButNoMetadata : BaseClassWithMetadataButNoExport + { + } + + [Fact] + public void Metadata_BaseClassWithMetadataButNoExport() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(BaseClassWithMetadataButNoExport), + typeof(DerivedClassWithExportButNoMetadata)); + + var export = container.GetExport>(); + + Assert.False(export.Metadata.ContainsKey("Name"), "Export should only contain metadata from the derived!"); + } + + [InheritedExport(typeof(BaseClassWithExportButNoMetadata))] + public abstract class BaseClassWithExportButNoMetadata + { + } + + [ExportMetadata("Name", "FromDerivedType")] + public class DerivedClassMetadataButNoExport : BaseClassWithExportButNoMetadata + { + } + + [Fact] + public void Metadata_BaseClassWithExportButNoMetadata() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(BaseClassWithExportButNoMetadata), + typeof(DerivedClassMetadataButNoExport)); + + var export = container.GetExport>(); + + Assert.False(export.Metadata.ContainsKey("Name"), "Export should only contain metadata from the base!"); + } + + [Export(typeof(BaseClassWithExportAndMetadata))] + [ExportMetadata("Name", "FromBaseType")] + public class BaseClassWithExportAndMetadata + { + } + + [Export(typeof(DerivedClassWithExportAndMetadata))] + [ExportMetadata("Name", "FromDerivedType")] + public class DerivedClassWithExportAndMetadata : BaseClassWithExportAndMetadata + { + } + + [Fact] + public void Metadata_BaseAndDerivedWithExportAndMetadata() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(BaseClassWithExportAndMetadata), + typeof(DerivedClassWithExportAndMetadata)); + + var exportBase = container.GetExport>(); + + Assert.Equal("FromBaseType", exportBase.Metadata["Name"]); + + var exportDerived = container.GetExport>(); + Assert.Equal("FromDerivedType", exportDerived.Metadata["Name"]); + } + + [Export] + [ExportMetadata("Data", null, IsMultiple = true)] + [ExportMetadata("Data", false, IsMultiple = true)] + [ExportMetadata("Data", Int16.MaxValue, IsMultiple = true)] + [ExportMetadata("Data", Int32.MaxValue, IsMultiple = true)] + [ExportMetadata("Data", Int64.MaxValue, IsMultiple = true)] + [ExportMetadata("Data", UInt16.MaxValue, IsMultiple = true)] + [ExportMetadata("Data", UInt32.MaxValue, IsMultiple = true)] + [ExportMetadata("Data", UInt64.MaxValue, IsMultiple = true)] + [ExportMetadata("Data", "String", IsMultiple = true)] + [ExportMetadata("Data", typeof(ClassWithLotsOfDifferentMetadataTypes), IsMultiple = true)] + [ExportMetadata("Data", CreationPolicy.NonShared, IsMultiple = true)] + [ExportMetadata("Data", new object[] { 1, 2, null }, IsMultiple = true)] + public class ClassWithLotsOfDifferentMetadataTypes + { + } + + [Fact] + public void ExportWithValidCollectionOfMetadata_ShouldDiscoverAllMetadata() + { + var catalog = CatalogFactory.CreateAttributed(typeof(ClassWithLotsOfDifferentMetadataTypes)); + + var export = catalog.Parts.First().ExportDefinitions.First(); + + var data = (object[])export.Metadata["Data"]; + + Assert.Equal(12, data.Length); + } + + [Export] + [ExportMetadata("Data", null, IsMultiple = true)] + [ExportMetadata("Data", 1, IsMultiple = true)] + [ExportMetadata("Data", 2, IsMultiple = true)] + [ExportMetadata("Data", 3, IsMultiple = true)] + public class ClassWithIntCollectionWithNullValue + { + } + + [Fact] + public void ExportWithIntCollectionPlusNullValueOfMetadata_ShouldDiscoverAllMetadata() + { + var catalog = CatalogFactory.CreateAttributed(typeof(ClassWithIntCollectionWithNullValue)); + + var export = catalog.Parts.First().ExportDefinitions.First(); + + var data = (object[])export.Metadata["Data"]; + + Assert.IsNotType(data); + + Assert.Equal(4, data.Length); + } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + [MetadataAttribute] + public class DataAttribute : Attribute + { + public object Object { get; set; } + } + + [Export] + [Data(Object = "42")] + [Data(Object = "10")] + public class ExportWithMultipleMetadata_ExportStringsAsObjects + { + } + + [Export] + [Data(Object = "42")] + [Data(Object = "10")] + [Data(Object = null)] + public class ExportWithMultipleMetadata_ExportStringsAsObjects_WithNull + { + } + + [Export] + [Data(Object = 42)] + [Data(Object = 10)] + public class ExportWithMultipleMetadata_ExportIntsAsObjects + { + } + + [Export] + [Data(Object = null)] + [Data(Object = 42)] + [Data(Object = 10)] + public class ExportWithMultipleMetadata_ExportIntsAsObjects_WithNull + { + } + + public interface IObjectView_AsStrings + { + string[] Object { get; } + } + + public interface IObjectView_AsInts + { + int[] Object { get; } + } + + public interface IObjectView + { + object[] Object { get; } + } + + [Fact] + public void ExportWithMultipleMetadata_ExportStringsAsObjects_ShouldDiscoverMetadataAsStrings() + { + var container = ContainerFactory.Create(); + container.ComposeParts(new ExportWithMultipleMetadata_ExportStringsAsObjects()); + + var export = container.GetExport(); + Assert.NotNull(export); + + Assert.NotNull(export.Metadata); + Assert.NotNull(export.Metadata.Object); + Assert.Equal(2, export.Metadata.Object.Length); + } + + [Fact] + public void ExportWithMultipleMetadata_ExportStringsAsObjects_With_Null_ShouldDiscoverMetadataAsStrings() + { + var container = ContainerFactory.Create(); + container.ComposeParts(new ExportWithMultipleMetadata_ExportStringsAsObjects_WithNull()); + + var export = container.GetExport(); + Assert.NotNull(export); + + Assert.NotNull(export.Metadata); + Assert.NotNull(export.Metadata.Object); + Assert.Equal(3, export.Metadata.Object.Length); + } + + [Fact] + public void ExportWithMultipleMetadata_ExportIntsAsObjects_ShouldDiscoverMetadataAsInts() + { + var container = ContainerFactory.Create(); + container.ComposeParts(new ExportWithMultipleMetadata_ExportIntsAsObjects()); + + var export = container.GetExport(); + Assert.NotNull(export); + + Assert.NotNull(export.Metadata); + Assert.NotNull(export.Metadata.Object); + Assert.Equal(2, export.Metadata.Object.Length); + } + + [Fact] + public void ExportWithMultipleMetadata_ExportIntsAsObjects_With_Null_ShouldDiscoverMetadataAsObjects() + { + var container = ContainerFactory.Create(); + container.ComposeParts(new ExportWithMultipleMetadata_ExportIntsAsObjects_WithNull()); + + var exports = container.GetExports(); + Assert.False(exports.Any()); + + var export = container.GetExport(); + + Assert.NotNull(export.Metadata); + Assert.NotNull(export.Metadata.Object); + Assert.Equal(3, export.Metadata.Object.Length); + } + + [MetadataAttribute] + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public class OrderAttribute : Attribute + { + public string Before { get; set; } + public string After { get; set; } + } + + public interface IOrderMetadataView + { + string[] Before { get; } + string[] After { get; } + } + + [Export] + [Order(Before = "Step3")] + [Order(Before = "Step2")] + public class OrderedItemBeforesOnly + { + } + + [Fact] + public void ExportWithMultipleMetadata_ExportStringsAndNulls_ThroughMetadataAttributes() + { + var container = ContainerFactory.Create(); + container.ComposeParts(new OrderedItemBeforesOnly()); + + var export = container.GetExport(); + Assert.NotNull(export); + + Assert.NotNull(export.Metadata); + + Assert.NotNull(export.Metadata.Before); + Assert.NotNull(export.Metadata.After); + + Assert.Equal(2, export.Metadata.Before.Length); + Assert.Equal(2, export.Metadata.After.Length); + + Assert.NotNull(export.Metadata.Before[0]); + Assert.NotNull(export.Metadata.Before[1]); + + Assert.Null(export.Metadata.After[0]); + Assert.Null(export.Metadata.After[1]); + } + + [MetadataAttribute] + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public class DataTypeAttribute : Attribute + { + public Type Type { get; set; } + } + + public interface ITypesMetadataView + { + Type[] Type { get; } + } + + [Export] + [DataType(Type = typeof(int))] + [DataType(Type = typeof(string))] + public class ItemWithTypeExports + { + } + + [Export] + [DataType(Type = typeof(int))] + [DataType(Type = typeof(string))] + [DataType(Type = null)] + public class ItemWithTypeExports_WithNulls + { + } + + [Export] + [DataType(Type = null)] + [DataType(Type = null)] + [DataType(Type = null)] + public class ItemWithTypeExports_WithAllNulls + { + } + + [Fact] + public void ExportWithMultipleMetadata_ExportTypes() + { + var container = ContainerFactory.Create(); + container.ComposeParts(new ItemWithTypeExports()); + + var export = container.GetExport(); + + Assert.NotNull(export.Metadata); + Assert.NotNull(export.Metadata.Type); + Assert.Equal(2, export.Metadata.Type.Length); + } + + [Fact] + public void ExportWithMultipleMetadata_ExportTypes_WithNulls() + { + var container = ContainerFactory.Create(); + container.ComposeParts(new ItemWithTypeExports_WithNulls()); + + var export = container.GetExport(); + + Assert.NotNull(export.Metadata); + Assert.NotNull(export.Metadata.Type); + Assert.Equal(3, export.Metadata.Type.Length); + } + + [Fact] + public void ExportWithMultipleMetadata_ExportTypes_WithAllNulls() + { + var container = ContainerFactory.Create(); + container.ComposeParts(new ItemWithTypeExports_WithAllNulls()); + + var export = container.GetExport(); + + Assert.NotNull(export.Metadata); + Assert.NotNull(export.Metadata.Type); + Assert.Equal(3, export.Metadata.Type.Length); + + Assert.Null(export.Metadata.Type[0]); + Assert.Null(export.Metadata.Type[1]); + Assert.Null(export.Metadata.Type[2]); + } + + [Export] + [ExportMetadata(null, "ValueOfNullKey")] + public class ClassWithNullMetadataKey + { + } + + [Fact] + public void ExportMetadataWithNullKey_ShouldUseEmptyString() + { + var nullMetadataCatalog = CatalogFactory.CreateAttributed(typeof(ClassWithNullMetadataKey)); + var nullMetadataExport = nullMetadataCatalog.Parts.Single().ExportDefinitions.Single(); + + Assert.True(nullMetadataExport.Metadata.ContainsKey(string.Empty)); + Assert.Equal("ValueOfNullKey", nullMetadataExport.Metadata[string.Empty]); + } + + } + + // Tests for metadata issues on export + [Export] + [ExportMetadata("foo", "bar1", IsMultiple = true)] + [ExportMetadata("foo", "bar2", IsMultiple = true)] + [ExportMetadata("acme", "acmebar", IsMultiple = true)] + [ExportMetadata("acme", 2.0, IsMultiple = true)] + [ExportMetadata("hello", "world")] + [GoodStrongMetadata] + public class MyExporterWithValidMetadata + { + [Export("ContractForValidMetadata")] + [ExportMetadata("bar", "foo1", IsMultiple = true)] + [ExportMetadata("bar", "foo2", IsMultiple = true)] + [ExportMetadata("stuff", "acmebar", IsMultiple = true)] + [ExportMetadata("stuff", 2.0, IsMultiple = true)] + [ExportMetadata("world", "hello")] // the order of the attribute should not affect the result + [GoodStrongMetadata] + public double DoSomething() { return 0.618; } + } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] + [MetadataAttribute] + public class GoodStrongMetadata : Attribute + { + public string GoodOne2 { get { return "GoodOneValue2"; } } + public string ConflictedOne1 { get { return "ConflictedOneValue1"; } } + public string ConflictedOne2 { get { return "ConflictedOneValue2"; } } + } + + // Tests for metadata as part of contract + + public interface IMyExporter { } + + [Export] + [Export(typeof(IMyExporter))] + public class MyExporterWithNoMetadata : IMyExporter + { + } + + [Export] + [Export(typeof(IMyExporter))] + [ExportMetadata("Foo", "Bar")] + public class MyExporterWithMetadata : IMyExporter + { + } + + public interface IMetadataFoo + { + string Foo { get; } + } + + public interface IMetadataBar + { + string Bar { get; } + } + + [Export] + public class MyImporterWithExport + { + [Import(typeof(MyExporterWithNoMetadata))] + public Lazy ValueInfo { get; set; } + } + + [Export] + public class SingleImportWithAllowDefault + { + [Import("Import", AllowDefault = true)] + public Lazy Import { get; set; } + } + + [Export] + public class SingleImport + { + [Import("Import")] + public Lazy Import { get; set; } + } + + public interface IFooMetadataView + { + string Foo { get; } + } + + [Export] + public class MyImporterWithExportCollection + { + [ImportMany(typeof(MyExporterWithNoMetadata))] + public IEnumerable> ValueInfoCol { get; set; } + } + + [Export] + public class MyImporterWithExportForSelectiveImport + { + [Import] + public Lazy ValueInfo { get; set; } + } + + [Export] + public class MyImporterWithExportCollectionForSelectiveImport + { + [ImportMany] + public Collection> ValueInfoCol { get; set; } + } + + public interface IMetadataView + { + string Foo { get; } + + [System.ComponentModel.DefaultValue(null)] + string OptionalFoo { get; } + } + + [Export] + [ExportMetadata("Foo", "fooValue3")] + [ExportMetadata("OptionalFoo", 42)] + public class OptionalFooIsInt { } + + [Export] + public class MyImporterWithExportStronglyTypedMetadata + { + [Import] + public Lazy ValueInfo { get; set; } + } + + [Export] + public class MyImporterWithExportCollectionStronglyTypedMetadata + { + [ImportMany] + public Collection> ValueInfoCol { get; set; } + } + + public class MyExporterWithFullMetadata + { + [Export("MyStringContract")] + public string String1 { get { return "String1"; } } + + [Export("MyStringContract")] + [ExportMetadata("Foo", "fooValue")] + public string String2 { get { return "String2"; } } + + [Export("MyStringContract")] + [ExportMetadata("Bar", "barValue")] + public string String3 { get { return "String3"; } } + + [Export("MyStringContract")] + [ExportMetadata("Foo", "fooValue")] + [ExportMetadata("Bar", "barValue")] + public string String4 { get { return "String4"; } } + } + + [MetadataAttribute] + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class MyOption : Attribute + { + public MyOption(string name, object value) + { + OptionNames = name; + OptionValues = value; + } + public string OptionNames { get; set; } + public object OptionValues { get; set; } + } + + public interface IMyOptions + { + IList OptionNames { get; } + ICollection OptionValues { get; } + } + + public interface IMyOptionsAsIEnumerable + { + IEnumerable OptionNames { get; } + IEnumerable OptionValues { get; } + } + + public interface IMyOptionsAsArray + { + string[] OptionNames { get; } + string[] OptionValues { get; } + } + + [Export] + [MyOption("name1", "value1")] + [MyOption("name2", "value2")] + [ExportMetadata("OptionNames", "name3", IsMultiple = true)] + [ExportMetadata("OptionValues", "value3", IsMultiple = true)] + public class ExportMultiple + { + } + + public interface IMyOption2 + { + string OptionNames { get; } + string OptionValues { get; } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/MetadataViewProviderTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/MetadataViewProviderTests.cs new file mode 100644 index 0000000000..cda05b4517 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/MetadataViewProviderTests.cs @@ -0,0 +1,365 @@ +// 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.Collections.Generic; +using System.Reflection; +using Xunit; + +namespace System.ComponentModel.Composition +{ + [MetadataViewImplementation(typeof(MetadataViewWithImplementation))] + public interface IMetadataViewWithImplementation + { + string String1 { get; } + string String2 { get; } + } + + public class MetadataViewWithImplementation : IMetadataViewWithImplementation + { + public MetadataViewWithImplementation(IDictionary metadata) + { + this.String1 = (string)metadata["String1"]; + this.String2 = (string)metadata["String2"]; + } + + public string String1 { get; private set; } + public string String2 { get; private set; } + } + + [MetadataViewImplementation(typeof(MetadataViewWithImplementationNoInterface))] + public interface IMetadataViewWithImplementationNoInterface + { + string String1 { get; } + string String2 { get; } + } + public class MetadataViewWithImplementationNoInterface + { + public MetadataViewWithImplementationNoInterface(IDictionary metadata) + { + this.String1 = (string)metadata["String1"]; + this.String2 = (string)metadata["String2"]; + } + + public string String1 { get; private set; } + public string String2 { get; private set; } + } + + public class MetadataViewProviderTests + { + + [Fact] + public void GetMetadataView_InterfaceWithPropertySetter_ShouldThrowNotSupported() + { + var metadata = new Dictionary(); + metadata["Value"] = "value"; + + Assert.Throws(() => + { + MetadataViewProvider.GetMetadataView(metadata); + }); + } + + [Fact] + public void GetMetadataView_InterfaceWithMethod_ShouldThrowNotSupportedException() + { + var metadata = new Dictionary(); + metadata["Value"] = "value"; + + Assert.Throws(() => + { + MetadataViewProvider.GetMetadataView(metadata); + }); + } + + [Fact] + public void GetMetadataView_InterfaceWithEvent_ShouldThrowNotSupportedException() + { + var metadata = new Dictionary(); + metadata["Value"] = "value"; + + Assert.Throws(() => + { + MetadataViewProvider.GetMetadataView(metadata); + }); + } + + [Fact] + public void GetMetadataView_InterfaceWithIndexer_ShouldThrowNotSupportedException() + { + var metadata = new Dictionary(); + metadata["Value"] = "value"; + + Assert.Throws(() => + { + MetadataViewProvider.GetMetadataView(metadata); + }); + } + + [Fact] + public void GetMetadataView_AbstractClass_ShouldThrowMissingMethodException() + { + var metadata = new Dictionary(); + metadata["Value"] = "value"; + + Assert.Throws(() => + { + MetadataViewProvider.GetMetadataView(metadata); + }); + } + + [Fact] + [ActiveIssue(25498, TargetFrameworkMonikers.UapAot)] + public void GetMetadataView_AbstractClassWithConstructor_ShouldThrowMemberAccessException() + { + var metadata = new Dictionary(); + metadata["Value"] = "value"; + + Assert.Throws(() => + { + MetadataViewProvider.GetMetadataView(metadata); + }); + } + + [Fact] + public void GetMetadataView_IDictionaryAsTMetadataViewTypeArgument_ShouldReturnMetadata() + { + var metadata = new Dictionary(); + + var result = MetadataViewProvider.GetMetadataView>(metadata); + + Assert.Same(metadata, result); + } + + [Fact] + public void GetMetadataView_IEnumerableAsTMetadataViewTypeArgument_ShouldReturnMetadata() + { + var metadata = new Dictionary(); + + var result = MetadataViewProvider.GetMetadataView>>(metadata); + + Assert.Same(metadata, result); + } + + [Fact] + public void GetMetadataView_DictionaryAsTMetadataViewTypeArgument_ShouldNotThrow() + { + var metadata = new Dictionary(); + MetadataViewProvider.GetMetadataView>(metadata); + } + + [Fact] + public void GetMetadataView_PrivateInterfaceAsTMetadataViewTypeArgument_ShouldhrowNotSupportedException() + { + var metadata = new Dictionary(); + metadata["CanActivate"] = true; + + Assert.Throws(() => + { + MetadataViewProvider.GetMetadataView(metadata); + }); + } + + [Fact] + public void GetMetadataView_DictionaryWithUncastableValueAsMetadataArgument_ShouldThrowCompositionContractMismatchException() + { + var metadata = new Dictionary(); + metadata["Value"] = true; + + Assert.Throws(() => + { + MetadataViewProvider.GetMetadataView(metadata); + }); + } + + [Fact] + public void GetMetadataView_InterfaceWithTwoPropertiesWithSameNameDifferentTypeAsTMetadataViewArgument_ShouldThrowContractMismatch() + { + var metadata = new Dictionary(); + metadata["Value"] = 10; + + Assert.Throws(() => + { + MetadataViewProvider.GetMetadataView(metadata); + }); + } + + [Fact] + public void GetMetadataView_RawMetadata() + { + var metadata = new Dictionary(); + metadata["Value"] = 10; + + var view = MetadataViewProvider.GetMetadataView(new Dictionary(metadata)); + + Assert.True(view.Count == metadata.Count); + Assert.True(view["Value"] == metadata["Value"]); + } + + [Fact] + public void GetMetadataView_InterfaceInheritance() + { + var metadata = new Dictionary(); + metadata["Value"] = "value"; + metadata["Value2"] = "value2"; + + var view = MetadataViewProvider.GetMetadataView(metadata); + Assert.Equal("value", view.Value); + Assert.Equal("value2", view.Value2); + } + + [Fact] + public void GetMetadataView_CachesViewType() + { + var metadata1 = new Dictionary(); + metadata1["Value"] = "value1"; + var view1 = MetadataViewProvider.GetMetadataView(metadata1); + Assert.Equal("value1", view1.Value); + + var metadata2 = new Dictionary(); + metadata2["Value"] = "value2"; + var view2 = MetadataViewProvider.GetMetadataView(metadata2); + Assert.Equal("value2", view2.Value); + + Assert.Equal(view1.GetType(), view2.GetType()); + } + + private interface IActivator + { + bool CanActivate + { + get; + } + } + + public class RawMetadata : Dictionary + { + public RawMetadata(IDictionary dictionary) : base(dictionary) { } + } + + public abstract class AbstractClassMetadataView + { + public abstract object Value { get; } + } + + public abstract class AbstractClassWithConstructorMetadataView + { + public AbstractClassWithConstructorMetadataView(IDictionary metadata) { } + public abstract object Value { get; } + } + + [Fact] + public void GetMetadataView_IMetadataViewWithDefaultedInt() + { + var view = MetadataViewProvider.GetMetadataView(new Dictionary()); + Assert.Equal(120, view.MyInt); + } + + [Fact] + public void GetMetadataView_IMetadataViewWithDefaultedIntInTranparentType() + { + var view = MetadataViewProvider.GetMetadataView(new Dictionary()); + int result = TransparentTestCase.GetMetadataView_IMetadataViewWithDefaultedIntInTranparentType(view); + Assert.Equal(120, result); + } + + [Fact] + public void GetMetadataView_IMetadataViewWithDefaultedIntAndInvalidMetadata() + { + Dictionary metadata = new Dictionary(); + metadata = new Dictionary(); + metadata.Add("MyInt", 1.2); + var view1 = MetadataViewProvider.GetMetadataView(metadata); + Assert.Equal(120, view1.MyInt); + + metadata = new Dictionary(); + metadata.Add("MyInt", "Hello, World"); + var view2 = MetadataViewProvider.GetMetadataView(metadata); + Assert.Equal(120, view2.MyInt); + } + + [Fact] + public void GetMetadataView_MetadataViewWithImplementation() + { + Dictionary metadata = new Dictionary(); + metadata = new Dictionary(); + metadata.Add("String1", "One"); + metadata.Add("String2", "Two"); + var view1 = MetadataViewProvider.GetMetadataView(metadata); + Assert.Equal("One", view1.String1); + Assert.Equal("Two", view1.String2); + Assert.Equal(view1.GetType(), typeof(MetadataViewWithImplementation)); + } + + [Fact] + public void GetMetadataView_MetadataViewWithImplementationNoInterface() + { + var exception = Assert.Throws(() => + { + Dictionary metadata = new Dictionary(); + metadata = new Dictionary(); + metadata.Add("String1", "One"); + metadata.Add("String2", "Two"); + var view1 = MetadataViewProvider.GetMetadataView(metadata); + }); + } + + [Fact] + public void GetMetadataView_IMetadataViewWithDefaultedBool() + { + var view = MetadataViewProvider.GetMetadataView(new Dictionary()); + Assert.Equal(false, view.MyBool); + } + + [Fact] + public void GetMetadataView_IMetadataViewWithDefaultedInt64() + { + var view = MetadataViewProvider.GetMetadataView(new Dictionary()); + Assert.Equal(Int64.MaxValue, view.MyInt64); + } + + [Fact] + public void GetMetadataView_IMetadataViewWithDefaultedString() + { + var view = MetadataViewProvider.GetMetadataView(new Dictionary()); + Assert.Equal("MyString", view.MyString); + } + + [Fact] + public void GetMetadataView_IMetadataViewWithTypeMismatchDefaultValue() + { + var exception = Assert.Throws(() => + { + MetadataViewProvider.GetMetadataView(new Dictionary()); + }); + + Assert.IsType(exception.InnerException); + } + + [Fact] + public void GetMetadataView_IMetadataViewWithTypeMismatchOnUnbox() + { + var metadata = new Dictionary(); + metadata["Value"] = (short)9999; + + var exception = Assert.Throws(() => + { + MetadataViewProvider.GetMetadataView(new Dictionary()); + }); + + Assert.IsType(exception.InnerException); + } + + [Fact] + public void TestMetadataIntConversion() + { + var metadata = new Dictionary(); + metadata["Value"] = (Int64)45; + + var exception = Assert.Throws(() => + { + MetadataViewProvider.GetMetadataView(metadata); + }); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/MicroExport.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/MicroExport.cs new file mode 100644 index 0000000000..6d5cc66882 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/MicroExport.cs @@ -0,0 +1,78 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Hosting; + +namespace System.ComponentModel.Composition +{ + internal class MicroExport + { + public MicroExport(Type contractType, params object[] exportedValues) + : this(AttributedModelServices.GetContractName(contractType), contractType, (IDictionary)null, exportedValues) + { + } + + public MicroExport(string contractName, params object[] exportedValues) + : this(contractName, exportedValues[0].GetType(), (IDictionary)null, exportedValues) + { + } + + public MicroExport(Type contractType, IDictionary metadata, params object[] exportedValues) + : this(AttributedModelServices.GetContractName(contractType), exportedValues[0].GetType(), metadata, exportedValues) + { + } + + public MicroExport(string contractName, Type contractType, params object[] exportedValues) + : this(contractName, contractType, (IDictionary)null, exportedValues) + { + } + + public MicroExport(string contractName, IDictionary metadata, params object[] exportedValues) + : this(contractName, exportedValues[0].GetType(), metadata, exportedValues) + { + } + + public MicroExport(string contractName, Type contractType, IDictionary metadata, params object[] exportedValues) + { + this.ContractName = contractName; + this.ExportedValues = exportedValues; + + if (contractType != null) + { + string typeIdentity = AttributedModelServices.GetTypeIdentity(contractType); + + if (metadata == null) + { + metadata = new Dictionary(); + } + + object val; + if (!metadata.TryGetValue(CompositionConstants.ExportTypeIdentityMetadataName, out val)) + { + metadata.Add(CompositionConstants.ExportTypeIdentityMetadataName, AttributedModelServices.GetTypeIdentity(contractType)); + } + } + this.Metadata = metadata; + } + + public string ContractName + { + get; + private set; + } + + public object[] ExportedValues + { + get; + private set; + } + + public IDictionary Metadata + { + get; + private set; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/PartCreationPolicyAttributeTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/PartCreationPolicyAttributeTests.cs new file mode 100644 index 0000000000..613773bc30 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/PartCreationPolicyAttributeTests.cs @@ -0,0 +1,38 @@ +// 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 Xunit; + +namespace System.ComponentModel.Composition +{ + public class PartCreationPolicyAttributeTests + { + [Fact] + public void Constructor_ShouldSetCreationPolicyToGivenValue() + { + var expectations = Expectations.GetEnumValues(); + + foreach (var e in expectations) + { + var attribute = new PartCreationPolicyAttribute(e); + + Assert.Equal(e, attribute.CreationPolicy); + } + } + + [Fact] + public void Constructor_OutOfRangeValueAsCreationPolicyArgument_ShouldSetCreationPolicy() + { // Attributes should not throw exceptions + + var expectations = Expectations.GetInvalidEnumValues(); + + foreach (var e in expectations) + { + var attribute = new PartCreationPolicyAttribute(e); + + Assert.Equal(e, attribute.CreationPolicy); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/PartMetadataAttributeTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/PartMetadataAttributeTests.cs new file mode 100644 index 0000000000..c4c6e05b61 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/PartMetadataAttributeTests.cs @@ -0,0 +1,45 @@ +// 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 Xunit; + +namespace System.ComponentModel.Composition +{ + public class PartMetadataAttributeTests + { + [Fact] + public void Constructor_NullAsNameArgument_ShouldSetNamePropertyToEmptyString() + { + var attribute = new PartMetadataAttribute((string)null, "Value"); + + Assert.Equal(string.Empty, attribute.Name); + } + + [Fact] + public void Constructor_ValueAsNameArgument_ShouldSetNameProperty() + { + var expectations = Expectations.GetMetadataNames(); + + foreach (var e in expectations) + { + var attribute = new PartMetadataAttribute(e, "Value"); + + Assert.Equal(e, attribute.Name); + } + } + + [Fact] + public void Constructor_ValueAsValueArgument_ShouldSetValueProperty() + { + var expectations = Expectations.GetMetadataValues(); + + foreach (var e in expectations) + { + var attribute = new PartMetadataAttribute("Name", e); + + Assert.Equal(e, attribute.Value); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Primitives/ComposablePartCatalogDebuggerProxyTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Primitives/ComposablePartCatalogDebuggerProxyTests.cs new file mode 100644 index 0000000000..8da84756f1 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Primitives/ComposablePartCatalogDebuggerProxyTests.cs @@ -0,0 +1,60 @@ +// 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.ComponentModel.Composition.Factories; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition.Primitives +{ + public class ComposablePartCatalogDebuggerProxyTests + { + [Fact] + public void Constructor_NullAsCatalogArgument_ShouldThrowArgumentNull() + { + Assert.Throws("catalog", () => + { + new ComposablePartCatalogDebuggerProxy((ComposablePartCatalog)null); + }); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void Constructor_ValueAsCatalogArgument_ShouldSetPartsProperty() + { + var expectations = Expectations.GetCatalogs(); + foreach (var e in expectations) + { + var proxy = new ComposablePartCatalogDebuggerProxy(e); + + EqualityExtensions.CheckEquals(e.Parts, proxy.Parts); + } + } + + [Fact] + [ActiveIssue(812029)] + public void Parts_ShouldNotCacheUnderlyingParts() + { + var catalog = CatalogFactory.CreateAggregateCatalog(); + var proxy = CreateComposablePartCatalogDebuggerProxy(catalog); + + Assert.Empty(proxy.Parts); + + var expectations = Expectations.GetCatalogs(); + foreach (var e in expectations) + { + catalog.Catalogs.Add(e); + + EqualityExtensions.CheckEquals(catalog.Parts, proxy.Parts); + + catalog.Catalogs.Remove(e); + } + } + + private ComposablePartCatalogDebuggerProxy CreateComposablePartCatalogDebuggerProxy(ComposablePartCatalog catalog) + { + return new ComposablePartCatalogDebuggerProxy(catalog); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Primitives/ComposablePartCatalogTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Primitives/ComposablePartCatalogTests.cs new file mode 100644 index 0000000000..99553d1d46 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Primitives/ComposablePartCatalogTests.cs @@ -0,0 +1,111 @@ +// 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.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class ComposablePartCatalogTests + { + [Fact] + public void GetExports_WhenDisposed_ShouldThrowObjectDisposed() + { + var catalog = CatalogFactory.Create(); + catalog.Dispose(); + var definition = ImportDefinitionFactory.Create(); + + ExceptionAssert.ThrowsDisposed(catalog, () => + { + catalog.GetExports(definition); + }); + } + + [Fact] + public void GetExports_NullAsConstraintArgument_ShouldThrowArgumentNull() + { + var catalog = CatalogFactory.Create(); + + Assert.Throws("definition", () => + { + catalog.GetExports((ImportDefinition)null); + }); + } + + [Fact] + public void Dispose_ShouldNotThrow() + { + var catalog = CatalogFactory.Create(); + catalog.Dispose(); + } + + [Fact] + public void Dispose_CanBeCalledMultipleTimes() + { + var catalog = CatalogFactory.Create(); + catalog.Dispose(); + catalog.Dispose(); + catalog.Dispose(); + } + + [Fact] + public void Dispose_CallsGCSuppressFinalize() + { + bool finalizerCalled = false; + + var catalog = CatalogFactory.CreateDisposable(disposing => + { + if (!disposing) + { + finalizerCalled = true; + } + + }); + + catalog.Dispose(); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + + Assert.False(finalizerCalled); + } + + [Fact] + public void Dispose_CallsDisposeBoolWithTrue() + { + var catalog = CatalogFactory.CreateDisposable(disposing => + { + Assert.True(disposing); + }); + + catalog.Dispose(); + } + + [Fact] + public void Dispose_CallsDisposeBoolOnce() + { + int disposeCount = 0; + var catalog = CatalogFactory.CreateDisposable(disposing => + { + disposeCount++; + }); + + catalog.Dispose(); + + Assert.Equal(1, disposeCount); + } + + private IQueryable GetPartDefinitions(ExportDefinition definition) + { + var partDefinition = PartDefinitionFactory.Create(null, () => null, Enumerable.Empty(), new ExportDefinition[] { definition }); + + return new ComposablePartDefinition[] { partDefinition }.AsQueryable(); + } + } +} + diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Primitives/CompositionElementDebuggerProxyTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Primitives/CompositionElementDebuggerProxyTests.cs new file mode 100644 index 0000000000..592a7f5e5b --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Primitives/CompositionElementDebuggerProxyTests.cs @@ -0,0 +1,71 @@ +// 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.ComponentModel.Composition.Primitives; +using Xunit; + +namespace System.ComponentModel.Composition.Hosting +{ + public class CompositionElementDebuggerProxyTests + { + [Fact] + public void Constructor_NullAsElementArgument_ShouldThrowArgumentNull() + { + Assert.Throws("element", () => + { + new CompositionElementDebuggerProxy((CompositionElement)null); + }); + } + + [Fact] + public void Constructor_ValueAsElementArgument_ShouldSetDisplayNameProperty() + { + var expectations = Expectations.GetObjectsReferenceTypes(); + + foreach (var e in expectations) + { + var element = CreateCompositionElement(e); + + var proxy = new CompositionElementDebuggerProxy(element); + + Assert.Same(element.DisplayName, proxy.DisplayName); + } + } + + [Fact] + public void Constructor_ValueAsElementArgument_ShouldSetOriginProperty() + { + var expectations = Expectations.GetObjectsReferenceTypes(); + + foreach (var e in expectations) + { + var element = CreateCompositionElement(e); + + var proxy = new CompositionElementDebuggerProxy(element); + + Assert.Same(element.Origin, proxy.Origin); + } + } + + [Fact] + public void Constructor_ValueAsElementArgument_ShouldSetUnderlyingObjectProperty() + { + var expectations = Expectations.GetObjectsReferenceTypes(); + + foreach (var e in expectations) + { + var element = CreateCompositionElement(e); + + var proxy = new CompositionElementDebuggerProxy(element); + + Assert.Same(element.UnderlyingObject, proxy.UnderlyingObject); + } + } + + private static CompositionElement CreateCompositionElement(object underlyingObject) + { + return new CompositionElement(underlyingObject); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Primitives/CompositionElementTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Primitives/CompositionElementTests.cs new file mode 100644 index 0000000000..193648f0e4 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Primitives/CompositionElementTests.cs @@ -0,0 +1,70 @@ +// 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.ComponentModel.Composition.Primitives; +using Xunit; + +namespace System.ComponentModel.Composition.Hosting +{ + public class CompositionElementTests + { + [Fact] + public void Constructor_ValueAsUnderlyingObjectArgument_ShouldSetUnderlyingObjectProperty() + { + var expectations = Expectations.GetObjectsReferenceTypes(); + + foreach (var e in expectations) + { + var element = new CompositionElement(e); + + Assert.Same(e, element.UnderlyingObject); + } + } + + [Fact] + public void Constructor_ValueAsUnderlyingObjectArgument_ShouldSetDisplayNamePropertyToUnderlyingObjectToString() + { + var expectations = Expectations.GetObjectsReferenceTypes(); + + foreach (var e in expectations) + { + var element = new CompositionElement(e); + + Assert.Equal(e.ToString(), element.DisplayName); + } + } + + [Fact] + public void Constructor_ValueAsUnderlyingObjectArgument_ShouldSetOriginToUnknown() + { + var expectations = Expectations.GetObjectsReferenceTypes(); + + foreach (var e in expectations) + { + var element = new CompositionElement(e); + + Assert.NotNull(element.Origin); + Assert.Null(element.Origin.Origin); + } + } + + [Fact] + public void ToString_ShouldReturnDisplayNameProperty() + { + var expectations = Expectations.GetObjectsReferenceTypes(); + + foreach (var e in expectations) + { + var element = CreateCompositionElement(e); + + Assert.Equal(element.DisplayName, element.ToString()); + } + } + + private static CompositionElement CreateCompositionElement(object underlyingObject) + { + return new CompositionElement(underlyingObject); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Primitives/CompositionScopeDefinitionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Primitives/CompositionScopeDefinitionTests.cs new file mode 100644 index 0000000000..d5aa317052 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/Primitives/CompositionScopeDefinitionTests.cs @@ -0,0 +1,263 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition.Hosting +{ + public class CompositionScopeDefinitionTests + { + public interface IFooContract + { + } + + [Export(typeof(IFooContract))] + public class FooImpl : IFooContract + { + [ImportMany] + public IEnumerable Foo { get; set; } + } + + [Export(typeof(IFooContract))] + public class FooImpl2 : IFooContract + { + } + + [Export(typeof(IFooContract))] + public class FooImpl3 : IFooContract + { + } + + [Export("One", typeof(IFooContract))] + [Export("Two", typeof(IFooContract))] + [Export("Three", typeof(IFooContract))] + public class FooImpl4 : IFooContract + { + } + + public class TestCatalog : ComposablePartCatalog, INotifyComposablePartCatalogChanged + { + private Func> _partFunc; + private Func>> _exportsFunc; + public TestCatalog(Func> partFunc, Func>> exportsFunc) + { + this._partFunc = partFunc; + this._exportsFunc = exportsFunc; + } + + public override IQueryable Parts + { + get { return this._partFunc.Invoke(); } + } + + public override IEnumerable> GetExports(ImportDefinition definition) + { + return this._exportsFunc.Invoke(); + } + + public event EventHandler Changed; + + public event EventHandler Changing; + + public void OnChanged(ComposablePartCatalogChangeEventArgs e) + { + if (this.Changed != null) + { + this.Changed.Invoke(this, e); + } + } + + public void OnChanging(ComposablePartCatalogChangeEventArgs e) + { + if (this.Changing != null) + { + this.Changing.Invoke(this, e); + } + } + } + + [Fact] + public void Constructor() + { + TypeCatalog catalog = new TypeCatalog(typeof(FooImpl)); + TypeCatalog catalog1 = new TypeCatalog(typeof(FooImpl2)); + TypeCatalog catalog2 = new TypeCatalog(typeof(FooImpl3)); + + CompositionScopeDefinition scope1 = new CompositionScopeDefinition(catalog1, Enumerable.Empty()); + CompositionScopeDefinition scope2 = new CompositionScopeDefinition(catalog1, Enumerable.Empty()); + CompositionScopeDefinition scope = new CompositionScopeDefinition(catalog, new CompositionScopeDefinition[] { scope1, scope2 }); + + Assert.NotNull(scope); + Assert.NotNull(scope.Parts); + Assert.Equal(1, scope.Parts.Count()); + Assert.Equal(2, scope.Children.Count()); + Assert.Same(scope1, scope.Children.ToArray()[0]); + Assert.Same(scope2, scope.Children.ToArray()[1]); + } + + [Fact] + public void Constructor_NullChildren() + { + TypeCatalog catalog = new TypeCatalog(typeof(FooImpl)); + CompositionScopeDefinition scope = new CompositionScopeDefinition(catalog, null); + + Assert.NotNull(scope.Children); + Assert.Equal(0, scope.Children.Count()); + } + + [Fact] + public void Constructor_NullCatalog_ShowThrowNullArgument() + { + var ex = ExceptionAssert.Throws(RetryMode.DoNotRetry, () => + { + CompositionScopeDefinition scope = new CompositionScopeDefinition(null, Enumerable.Empty()); + }); + } + + [Fact] + public void Parts_DelegateToCatalog() + { + var parts = new TypeCatalog(typeof(FooImpl), typeof(FooImpl2), typeof(FooImpl2)).Parts; + var exports = parts.Select(p => Tuple.Create(p, p.ExportDefinitions.First())); + TestCatalog catalog = new TestCatalog( + () => parts, + () => exports); + + CompositionScopeDefinition scope = new CompositionScopeDefinition(catalog, null); + EqualityExtensions.CheckEquals(parts, scope.Parts); + } + + [Fact] + public void Constructor_PublicSurface() + { + var catalog = new TypeCatalog(typeof(FooImpl), typeof(FooImpl2), typeof(FooImpl2)); + var exports = catalog.Parts.Select(p => p.ExportDefinitions.First()); + CompositionScopeDefinition scope = new CompositionScopeDefinition(catalog, null, exports); + Assert.Equal(catalog.Parts.Count(), scope.Parts.Count()); + Assert.Equal(exports.Count(), scope.PublicSurface.Count()); + } + + [Fact] + public void Constructor_PublicSurface_MultipleExportsPerPart() + { + var catalog = new TypeCatalog(typeof(FooImpl4)); + var exports = catalog.Parts.SelectMany(p => p.ExportDefinitions); + CompositionScopeDefinition scope = new CompositionScopeDefinition(catalog, null, exports); + Assert.Equal(3, scope.PublicSurface.Count()); + } + + [Fact] + public void GetExports_DelegateToCatalog() + { + var parts = new TypeCatalog(typeof(FooImpl), typeof(FooImpl2), typeof(FooImpl2)).Parts; + var exports = parts.Select(p => Tuple.Create(p, p.ExportDefinitions.First())); + var import = parts.SelectMany(p => p.ImportDefinitions).First(); + TestCatalog catalog = new TestCatalog( + () => parts, + () => exports); + + CompositionScopeDefinition scope = new CompositionScopeDefinition(catalog, null); + Assert.Same(exports, scope.GetExports(import)); + } + + [Fact] + public void Notifications() + { + var parts = new TypeCatalog(typeof(FooImpl), typeof(FooImpl2), typeof(FooImpl2)).Parts; + var exports = parts.Select(p => Tuple.Create(p, p.ExportDefinitions.First())); + TestCatalog catalog = new TestCatalog( + () => parts, + () => exports); + + CompositionScopeDefinition scope = new CompositionScopeDefinition(catalog, null); + + ComposablePartCatalogChangeEventArgs args = new ComposablePartCatalogChangeEventArgs(Enumerable.Empty(), Enumerable.Empty(), null); + + bool changedFired = false; + scope.Changed += new EventHandler((o, e) => + { + Assert.Same(args, e); + Assert.Same(scope, o); + changedFired = true; + }); + + bool changingFired = false; + scope.Changing += new EventHandler((o, e) => + { + Assert.Same(args, e); + Assert.Same(scope, o); + changingFired = true; + }); + + catalog.OnChanged(args); + Assert.True(changedFired); + + catalog.OnChanging(args); + Assert.True(changingFired); + + changedFired = false; + changingFired = false; + + scope.Dispose(); + + catalog.OnChanged(args); + catalog.OnChanging(args); + + Assert.False(changedFired); + Assert.False(changingFired); + + } + + [Fact] + public void Dispose() + { + var parts = new TypeCatalog(typeof(FooImpl), typeof(FooImpl2), typeof(FooImpl2)).Parts; + var exports = parts.Select(p => Tuple.Create(p, p.ExportDefinitions.First())); + TestCatalog catalog = new TestCatalog( + () => parts, + () => exports); + var import = parts.SelectMany(p => p.ImportDefinitions).First(); + + CompositionScopeDefinition scope = new CompositionScopeDefinition(catalog, null); + + scope.Dispose(); + var ex = ExceptionAssert.Throws(RetryMode.DoNotRetry, () => + { + var ps = scope.Parts; + }); + + ex = ExceptionAssert.Throws(RetryMode.DoNotRetry, () => + { + var es = scope.GetExports(import); + }); + + scope.Dispose(); + ex = ExceptionAssert.Throws(RetryMode.DoNotRetry, () => + { + var ps = scope.Parts; + }); + + ex = ExceptionAssert.Throws(RetryMode.DoNotRetry, () => + { + var es = scope.GetExports(import); + }); + + } + + [Fact] + public void SimpleComposition() + { + var catalog = new TypeCatalog(typeof(FooImpl), typeof(FooImpl2), typeof(FooImpl2)); + var scope = new CompositionScopeDefinition(catalog, null); + var container = new CompositionContainer(scope); + + var foos = container.GetExportedValues(); + Assert.Equal(3, foos.Count()); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/LazyMemberInfoTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/LazyMemberInfoTests.cs new file mode 100644 index 0000000000..471d25537f --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/LazyMemberInfoTests.cs @@ -0,0 +1,242 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.UnitTesting; +using Xunit; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + public class LazyMemberInfoTests + { + [Fact] + public void Constructor_PassMember() + { + foreach (var memberAndAccessorsInfo in GetMembersAndAccessors(typeof(LazyMemberTestClass))) + { + MemberInfo member = memberAndAccessorsInfo.Item1; + MemberTypes memberType = memberAndAccessorsInfo.Item2.Item1; + MemberInfo[] accessors = memberAndAccessorsInfo.Item2.Item2; + + LazyMemberInfo lazy = new LazyMemberInfo(member); + Assert.Equal(memberType, lazy.MemberType); + Assert.Equal(accessors.Length, lazy.GetAccessors().Length); + Assert.True(accessors.SequenceEqual(lazy.GetAccessors())); + } + } + + [Fact] + public void Constructor_PassNullMember() + { + Assert.Throws("member", () => + { + LazyMemberInfo lazy = new LazyMemberInfo((MemberInfo)null); + }); + } + + [Fact] + public void Constructor_PassAccessors() + { + foreach (var memberAndAccessorsInfo in GetMembersAndAccessors(typeof(LazyMemberTestClass))) + { + MemberInfo member = memberAndAccessorsInfo.Item1; + MemberTypes memberType = memberAndAccessorsInfo.Item2.Item1; + MemberInfo[] accessors = memberAndAccessorsInfo.Item2.Item2; + + LazyMemberInfo lazy = new LazyMemberInfo(memberType, accessors); + Assert.Equal(memberType, lazy.MemberType); + Assert.Equal(accessors.Length, lazy.GetAccessors().Length); + Assert.True(accessors.SequenceEqual(lazy.GetAccessors())); + } + } + + [Fact] + public void Constructor_PassInvalidAccessors() + { + foreach (var memberAndAccessorsInfo in GetMembersAndAccessors(typeof(LazyMemberTestClass))) + { + MemberInfo member = memberAndAccessorsInfo.Item1; + MemberTypes memberType = memberAndAccessorsInfo.Item2.Item1; + MemberInfo[] accessors = memberAndAccessorsInfo.Item2.Item2; + + foreach (MemberTypes wrongMemberType in GetValidMemberTypes()) + { + if (wrongMemberType == memberType) + { + continue; + } + Assert.Throws("accessors", () => + { + LazyMemberInfo lazy = new LazyMemberInfo(wrongMemberType, accessors); + }); + } + } + } + + [Fact] + public void Constructor_PassAccessorsInvalidMemberType() + { + MemberTypes[] validMemberTypes = GetValidMemberTypes().ToArray(); + foreach (MemberTypes memberType in GetMemberTypeValues()) + { + if (!validMemberTypes.Contains(memberType)) + { + Assert.Throws("memberType", () => + { + LazyMemberInfo lazy = new LazyMemberInfo(memberType, typeof(LazyMemberTestClass)); + }); + } + } + } + + [Fact] + public void Constructor_PassNullAccessors() + { + Assert.Throws("accessors", () => + { + LazyMemberInfo lazy = new LazyMemberInfo(MemberTypes.Field, (MemberInfo[])null); + }); + } + + [Fact] + public void Constructor_PassAccessorsWithNulls() + { + Assert.Throws("accessors", () => + { + LazyMemberInfo lazy = new LazyMemberInfo(MemberTypes.Field, new MemberInfo[] { null, null }); + }); + } + + [Fact] + public void Constructor_PassAccessorCreators() + { + foreach (var memberAndAccessorsInfo in GetMembersAndAccessors(typeof(LazyMemberTestClass))) + { + MemberInfo member = memberAndAccessorsInfo.Item1; + MemberTypes memberType = memberAndAccessorsInfo.Item2.Item1; + MemberInfo[] accessors = memberAndAccessorsInfo.Item2.Item2; + + LazyMemberInfo lazy = new LazyMemberInfo(memberType, () => accessors); + Assert.Equal(memberType, lazy.MemberType); + Assert.Equal(accessors.Length, lazy.GetAccessors().Length); + Assert.True(accessors.SequenceEqual(lazy.GetAccessors())); + } + } + + [Fact] + public void Constructor_PassInvalidAccessorCreators() + { + foreach (var memberAndAccessorsInfo in GetMembersAndAccessors(typeof(LazyMemberTestClass))) + { + MemberInfo member = memberAndAccessorsInfo.Item1; + MemberTypes memberType = memberAndAccessorsInfo.Item2.Item1; + MemberInfo[] accessors = memberAndAccessorsInfo.Item2.Item2; + + foreach (MemberTypes wrongMemberType in GetValidMemberTypes()) + { + if (wrongMemberType == memberType) + { + continue; + } + LazyMemberInfo lazy = new LazyMemberInfo(wrongMemberType, () => accessors); + ExceptionAssert.Throws(() => + { + lazy.GetAccessors(); + }); + } + } + } + + [Fact] + public void Constructor_PassAccessorCreatorsWithInvalidMemberType() + { + MemberTypes[] validMemberTypes = GetValidMemberTypes().ToArray(); + foreach (MemberTypes memberType in GetMemberTypeValues()) + { + if (!validMemberTypes.Contains(memberType)) + { + Assert.Throws("memberType", () => + { + LazyMemberInfo lazy = new LazyMemberInfo(memberType, () => new MemberInfo[] { typeof(LazyMemberTestClass) }); + }); + } + } + } + + [Fact] + public void Constructor_PassNullAccessorCreators() + { + Assert.Throws("accessorsCreator", () => + { + LazyMemberInfo lazy = new LazyMemberInfo(MemberTypes.Field, (Func)null); + }); + } + + private static IEnumerable>> GetMembersAndAccessors(Type type) + { + yield return new Tuple>( + type, new Tuple(type.MemberType, new MemberInfo[] { type })); + + foreach (MemberInfo member in type.GetMembers()) + { + MemberInfo[] accessors = null; + if (member.MemberType == MemberTypes.Property) + { + PropertyInfo property = (PropertyInfo)member; + accessors = new MemberInfo[] { property.GetGetMethod(true), property.GetSetMethod(true) }; + } + else if (member.MemberType == MemberTypes.Event) + { + EventInfo event_ = (EventInfo)member; + accessors = new MemberInfo[] { event_.GetRaiseMethod(true), event_.GetAddMethod(true), event_.GetRemoveMethod(true) }; + } + else + { + accessors = new MemberInfo[] { member }; + } + + yield return new Tuple>( + member, new Tuple(member.MemberType, accessors)); + } + } + + private static IEnumerable GetMemberTypeValues() + { + yield return MemberTypes.All; + yield return MemberTypes.Constructor; + yield return MemberTypes.Custom; + yield return MemberTypes.Event; + yield return MemberTypes.Field; + yield return MemberTypes.Method; + yield return MemberTypes.NestedType; + yield return MemberTypes.Property; + yield return MemberTypes.TypeInfo; + } + + private static IEnumerable GetValidMemberTypes() + { + yield return MemberTypes.TypeInfo; + yield return MemberTypes.NestedType; + yield return MemberTypes.Constructor; + yield return MemberTypes.Field; + yield return MemberTypes.Method; + yield return MemberTypes.Property; + yield return MemberTypes.Event; + } + + public class LazyMemberTestClass + { + public LazyMemberTestClass() { } + public string Property { get; set; } + public string SetProperty { set { } } + public string GetProperty { get { return null; } } + public string Field; + public void Method() { this.Event(this, new EventArgs()); } + public event EventHandler Event; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/ReflectionComposablePartDefinitionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/ReflectionComposablePartDefinitionTests.cs new file mode 100644 index 0000000000..230dd38ddd --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/ReflectionComposablePartDefinitionTests.cs @@ -0,0 +1,337 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using System.Reflection; +using Microsoft.Internal; +using Xunit; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + public class ReflectionComposablePartDefinitionTests + { + private ReflectionComposablePartDefinition CreateReflectionPartDefinition( + Lazy partType, + bool requiresDisposal, + Func> imports, + Func>exports, + IDictionary metadata, + ICompositionElement origin) + { + return (ReflectionComposablePartDefinition)ReflectionModelServices.CreatePartDefinition(partType, requiresDisposal, + new Lazy>(imports, false), + new Lazy>(exports, false), + metadata.AsLazy(), origin); + } + + [Fact] + public void Constructor() + { + Type expectedType = typeof(TestPart); + Lazy expectedLazyType = expectedType.AsLazy(); + IDictionary expectedMetadata = new Dictionary(); + expectedMetadata["Key1"] = 1; + expectedMetadata["Key2"] = "Value2"; + + IEnumerable expectedImports = CreateImports(expectedType); + IEnumerable expectedExports = CreateExports(expectedType); + + ICompositionElement expectedOrigin = new MockOrigin(); + + ReflectionComposablePartDefinition definition = CreateReflectionPartDefinition( + expectedLazyType, + false, + () => expectedImports, + () => expectedExports, + expectedMetadata, + expectedOrigin); + + Assert.Same(expectedType, definition.GetPartType()); + Assert.True(definition.Metadata.Keys.SequenceEqual(expectedMetadata.Keys)); + Assert.True(definition.Metadata.Values.SequenceEqual(expectedMetadata.Values)); + Assert.True(definition.ExportDefinitions.SequenceEqual(expectedExports.Cast())); + Assert.True(definition.ImportDefinitions.SequenceEqual(expectedImports.Cast())); + Assert.Same(expectedOrigin, ((ICompositionElement)definition).Origin); + Assert.NotNull(((ICompositionElement)definition).DisplayName); + Assert.False(definition.IsDisposalRequired); + } + + [Fact] + public void Constructor_DisposablePart() + { + Type expectedType = typeof(TestPart); + Lazy expectedLazyType = expectedType.AsLazy(); + IDictionary expectedMetadata = new Dictionary(); + expectedMetadata["Key1"] = 1; + expectedMetadata["Key2"] = "Value2"; + + IEnumerable expectedImports = CreateImports(expectedType); + IEnumerable expectedExports = CreateExports(expectedType); + + ICompositionElement expectedOrigin = new MockOrigin(); + + ReflectionComposablePartDefinition definition = CreateReflectionPartDefinition( + expectedLazyType, + true, + () => expectedImports, + () => expectedExports, + expectedMetadata, + expectedOrigin); + + Assert.Same(expectedType, definition.GetPartType()); + Assert.True(definition.Metadata.Keys.SequenceEqual(expectedMetadata.Keys)); + Assert.True(definition.Metadata.Values.SequenceEqual(expectedMetadata.Values)); + Assert.True(definition.ExportDefinitions.SequenceEqual(expectedExports.Cast())); + Assert.True(definition.ImportDefinitions.SequenceEqual(expectedImports.Cast())); + Assert.Same(expectedOrigin, ((ICompositionElement)definition).Origin); + Assert.NotNull(((ICompositionElement)definition).DisplayName); + Assert.True(definition.IsDisposalRequired); + } + + [Fact] + public void CreatePart() + { + Type expectedType = typeof(TestPart); + Lazy expectedLazyType = expectedType.AsLazy(); + IDictionary expectedMetadata = new Dictionary(); + expectedMetadata["Key1"] = 1; + expectedMetadata["Key2"] = "Value2"; + + IEnumerable expectedImports = CreateImports(expectedType); + IEnumerable expectedExports = CreateExports(expectedType); + + ICompositionElement expectedOrigin = new MockOrigin(); + + ReflectionComposablePartDefinition definition = CreateReflectionPartDefinition( + expectedLazyType, + false, + () => expectedImports, + () => expectedExports, + expectedMetadata, + expectedOrigin); + + var part = definition.CreatePart(); + Assert.NotNull(part); + Assert.False(part is IDisposable); + } + + [Fact] + public void CreatePart_Disposable() + { + Type expectedType = typeof(TestPart); + Lazy expectedLazyType = expectedType.AsLazy(); + IDictionary expectedMetadata = new Dictionary(); + expectedMetadata["Key1"] = 1; + expectedMetadata["Key2"] = "Value2"; + + IEnumerable expectedImports = CreateImports(expectedType); + IEnumerable expectedExports = CreateExports(expectedType); + + ICompositionElement expectedOrigin = new MockOrigin(); + + ReflectionComposablePartDefinition definition = CreateReflectionPartDefinition( + expectedLazyType, + true, + () => expectedImports, + () => expectedExports, + expectedMetadata, + expectedOrigin); + + var part = definition.CreatePart(); + Assert.NotNull(part); + Assert.True(part is IDisposable); + } + + [Fact] + public void CreatePart_DoesntLoadType() + { + Type expectedType = typeof(TestPart); + Lazy expectedLazyType = new Lazy(() => { throw new NotImplementedException(); /*"Part should not be loaded" */ }); + IDictionary expectedMetadata = new Dictionary(); + expectedMetadata["Key1"] = 1; + expectedMetadata["Key2"] = "Value2"; + + IEnumerable expectedImports = CreateImports(expectedType); + IEnumerable expectedExports = CreateExports(expectedType); + + ICompositionElement expectedOrigin = new MockOrigin(); + + ReflectionComposablePartDefinition definition = CreateReflectionPartDefinition( + expectedLazyType, + true, + () => expectedImports, + () => expectedExports, + expectedMetadata, + expectedOrigin); + + var part = definition.CreatePart(); + Assert.NotNull(part); + Assert.True(part is IDisposable); + } + + [Fact] + public void Constructor_NullMetadata_ShouldSetMetadataPropertyToEmpty() + { + ReflectionComposablePartDefinition definition = CreateEmptyDefinition(typeof(object), typeof(object).GetConstructors().First(), null, new MockOrigin()); + Assert.NotNull(definition.Metadata); + Assert.Equal(0, definition.Metadata.Count); + } + + [Fact] + public void Constructor_NullOrigin_ShouldSetOriginPropertyToNull() + { + ReflectionComposablePartDefinition definition = CreateEmptyDefinition(typeof(object), typeof(object).GetConstructors().First(), MetadataServices.EmptyMetadata, null); + Assert.NotNull(((ICompositionElement)definition).DisplayName); + Assert.Null(((ICompositionElement)definition).Origin); + } + + [Fact] + public void ImportaAndExports_CreatorsShouldBeCalledLazilyAndOnce() + { + Type expectedType = typeof(TestPart); + + IEnumerable expectedImports = CreateImports(expectedType); + IEnumerable expectedExports = CreateExports(expectedType); + + bool importsCreatorCalled = false; + Func> importsCreator = () => + { + Assert.False(importsCreatorCalled); + importsCreatorCalled = true; + return expectedImports.Cast(); + }; + + bool exportsCreatorCalled = false; + Func> exportsCreator = () => + { + Assert.False(exportsCreatorCalled); + exportsCreatorCalled = true; + return expectedExports.Cast(); + }; + + ReflectionComposablePartDefinition definition = CreateReflectionPartDefinition( + expectedType.AsLazy(), + false, + importsCreator, + exportsCreator, + null, + null); + + IEnumerable exports; + Assert.False(exportsCreatorCalled); + exports = definition.ExportDefinitions; + Assert.True(exportsCreatorCalled); + exports = definition.ExportDefinitions; + +IEnumerable imports; + Assert.False(importsCreatorCalled); + imports = definition.ImportDefinitions; + Assert.True(importsCreatorCalled); + imports = definition.ImportDefinitions; + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void ICompositionElementDisplayName_ShouldReturnTypeDisplayName() + { + var expectations = Expectations.GetAttributedTypes(); + foreach (var e in expectations) + { + var definition = (ICompositionElement)CreateEmptyDefinition(e, null, null, null); + + Assert.Equal(e.GetDisplayName(), definition.DisplayName); + } + } + + private ReflectionComposablePartDefinition CreateEmptyDefinition(Type type, ConstructorInfo constructor, IDictionary metadata, ICompositionElement origin) + { + return (ReflectionComposablePartDefinition)ReflectionModelServices.CreatePartDefinition( + (type != null) ? type.AsLazy() : null, + false, + Enumerable.Empty().AsLazy(), + Enumerable.Empty().AsLazy(), + metadata.AsLazy(), + origin); + } + + private static List CreateImports(Type type) + { + List imports = new List(); + foreach (PropertyInfo property in type.GetProperties()) + { + imports.Add(new ReflectionMemberImportDefinition(new LazyMemberInfo(property), "Contract", (string)null, new KeyValuePair[] { new KeyValuePair("Key1", typeof(object)) }, ImportCardinality.ZeroOrOne, true, false, CreationPolicy.Any, MetadataServices.EmptyMetadata, new TypeOrigin(type))); + } + + return imports; + } + + private static List CreateExports(Type type) + { + List exports = new List(); + foreach (PropertyInfo property in type.GetProperties()) + { + exports.Add(ReflectionModelServices.CreateExportDefinition(new LazyMemberInfo(property), "Contract", new Lazy>(() => null, false), new TypeOrigin(type))); + } + + return exports; + } + + public class TestPart + { + public int field1; + public string field2; + public int Property1 { get; set; } + public string Property2 { get; set; } + } + + private class TypeOrigin : ICompositionElement + { + private readonly Type _type; + private readonly ICompositionElement _orgin; + + public TypeOrigin(Type type) + : this(type, null) + { + } + + public TypeOrigin(Type type, ICompositionElement origin) + { + this._type = type; + this._orgin = origin; + } + + public string DisplayName + { + get + { + return this._type.GetDisplayName(); + } + } + + public ICompositionElement Origin + { + get + { + return this._orgin; + } + } + } + + private class MockOrigin : ICompositionElement + { + public string DisplayName + { + get { throw new NotImplementedException(); } + } + + public ICompositionElement Origin + { + get { throw new NotImplementedException(); } + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/ReflectionComposablePartTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/ReflectionComposablePartTests.cs new file mode 100644 index 0000000000..4f6fd1536a --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/ReflectionComposablePartTests.cs @@ -0,0 +1,848 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Diagnostics; +using System.ComponentModel.Composition.Extensibility; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Primitives; +using System.Diagnostics; +using System.Linq; +using System.UnitTesting; +using Microsoft.Internal; +using Xunit; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + public class ReflectionComposablePartTests + { + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types.Retrieve the LoaderExceptions property for more information. + public void Constructor1_DefinitionAsDefinitionArgument_ShouldSetOriginProperty() + { + var expectations = Expectations.GetAttributedDefinitions(); + + foreach (var e in expectations) + { + var definition = (ICompositionElement)new ReflectionComposablePart(e); + + Assert.Same(e, definition.Origin); + } + } + + [Fact] + public void Constructor1_NullAsDefinitionArgument_ShouldThrowArgumentNull() + { + Assert.Throws("definition", () => + { + new ReflectionComposablePart((ReflectionComposablePartDefinition)null); + }); + } + + [Fact] + public void Constructor2_NullAsAttributedPartArgument_ShouldThrowArgumentNull() + { + Assert.Throws("attributedPart", () => + { + new ReflectionComposablePart(PartDefinitionFactory.CreateAttributed(), (object)null); + }); + } + + [Fact] + public void Constructor2_ValueTypeAsAttributedPartArgument_ShouldThrowArgument() + { + Assert.Throws("attributedPart", () => + { + new ReflectionComposablePart(PartDefinitionFactory.CreateAttributed(), 42); + }); + } + + [Fact] + public void Constructor1_AttributedComposablePartDefintion_ShouldProduceValidObject() + { + var definition = PartDefinitionFactory.CreateAttributed(typeof(MyExport)); + var part = new ReflectionComposablePart(definition); + + Assert.Equal(definition, part.Definition); + Assert.NotNull(part.Metadata); + + Assert.False(part is IDisposable); + } + + [Fact] + public void Constructor1_AttributedComposablePartDefintion_Disposable_ShouldProduceValidObject() + { + var definition = PartDefinitionFactory.CreateAttributed(typeof(DisposablePart)); + var part = new DisposableReflectionComposablePart(definition); + + Assert.Equal(definition, part.Definition); + Assert.NotNull(part.Metadata); + + Assert.True(part is IDisposable); + } + + [Fact] + public void Constructor1_Type_ShouldProduceValidObject() + { + var part = new ReflectionComposablePart(PartDefinitionFactory.CreateAttributed(typeof(MyExport))); + } + + [Fact] + public void Constructor1_Object_ShouldProduceValidObject() + { + var part = new ReflectionComposablePart(PartDefinitionFactory.CreateAttributed(typeof(MyExport)), new MyExport()); + } + + [Fact] + public void Metadata_WhenDisposed_ShouldThrowObjectDisposed() + { + var part = CreateDefaultDisposablePart(); + ((IDisposable)part).Dispose(); + + ExceptionAssert.ThrowsDisposed(part, () => + { + var metadata = part.Metadata; + }); + } + + [Fact] + public void ImportDefinitions_WhenDisposed_ShouldThrowObjectDisposed() + { + var part = CreateDefaultDisposablePart(); + ((IDisposable)part).Dispose(); + + ExceptionAssert.ThrowsDisposed(part, () => + { + var definitions = part.ImportDefinitions; + }); + } + + [Fact] + public void ExportDefinitions_WhenDisposed_ShouldThrowObjectDisposed() + { + var part = CreateDefaultDisposablePart(); + ((IDisposable)part).Dispose(); + + ExceptionAssert.ThrowsDisposed(part, () => + { + var definitions = part.ExportDefinitions; + }); + } + + [Fact] + public void OnComposed_WhenDisposed_ShouldThrowObjectDisposed() + { + var part = CreateDefaultDisposablePart(); + ((IDisposable)part).Dispose(); + + ExceptionAssert.ThrowsDisposed(part, () => + { + part.Activate(); + }); + } + + [Fact] + public void OnComposed_MissingPostImportsOnInstance_ShouldThrowComposition() + { + var part = CreatePart(new MySharedPartExport()); + + // Dev10:484204 - This used to cause a failure but after we made + // ReflectionComposablePart internal we needed to back remove this + // validation for post imports to make declarative composition work. + //part.Activate().VerifyFailure(CompositionIssueId.ImportNotSetOnPart); + part.Activate(); + } + + [Fact] + public void OnComposed_ProperlyComposed_ShouldSucceed() + { + var import = new TrivialImporter(); + var export = new TrivialExporter(); + + var part = CreatePart(import); + + var importDef = part.ImportDefinitions.First(); + part.SetImport(importDef, CreateSimpleExports(export)); + part.Activate(); + Assert.True(export.done, "OnImportsSatisfied should have been called"); + } + + [Fact] + public void OnComposed_UnhandledExceptionThrowInOnImportsSatisfied_ShouldThrowComposablePart() + { + var part = CreatePart(typeof(ExceptionDuringINotifyImport)); + var definition = part.ImportDefinitions.First(); + part.SetImport(definition, CreateSimpleExports(21)); + + CompositionAssert.ThrowsPart(RetryMode.DoNotRetry, () => + { + part.Activate(); + }); + } + + [Fact] + public void SetImport_WhenDisposed_ShouldThrowObjectDisposed() + { + var part = CreateDefaultDisposablePart(); + var definition = part.ImportDefinitions.First(); + + ((IDisposable)part).Dispose(); + + ExceptionAssert.ThrowsDisposed(part, () => + { + part.SetImport(definition, Enumerable.Empty()); + }); + } + + [Fact] + public void SetImport_NullAsImportDefinitionArgument_ShouldThrowArgumentNull() + { + var part = CreateDefaultPart(); + + Assert.Throws("definition", () => + { + part.SetImport((ImportDefinition)null, Enumerable.Empty()); + }); + } + + [Fact] + public void SetImport_NullAsExportsArgument_ShouldThrowArgumentNull() + { + var part = CreatePart(typeof(MySharedPartExport)); + var import = part.ImportDefinitions.First(); + + Assert.Throws("exports", () => + { + part.SetImport(import, (IEnumerable)null); + }); + } + + [Fact] + public void SetImport_ExportsArrayWithNullElementAsExportsArgument_ShouldThrowArgument() + { + var part = CreatePart(typeof(MySharedPartExport)); + var definition = part.ImportDefinitions.First(); + + Assert.Throws("exports", () => + { + part.SetImport(definition, new Export[] { null }); + }); + } + + [Fact] + public void SetImport_WrongDefinitionAsDefinitionArgument_ShouldThrowArgument() + { + var part = CreateDefaultPart(); + + var definition = ImportDefinitionFactory.Create(); + + Assert.Throws("definition", () => + { + part.SetImport(definition, Enumerable.Empty()); + }); + } + + [Fact] + public void SetImport_SetNonRecomposableDefinitionAsDefinitionArgumentAfterOnComposed_ShouldThrowInvalidOperation() + { + var part = CreatePartWithNonRecomposableImport(); + var definition = part.ImportDefinitions.First(); + + part.SetImport(definition, Enumerable.Empty()); + part.Activate(); + + ExceptionAssert.Throws(() => + { + part.SetImport(definition, Enumerable.Empty()); + }); + } + + [Fact] + public void SetImport_ZeroOrOneDefinitionAsDefinitionArgumentAndTwoExportsAsExportsArgument_ShouldThrowArgument() + { + var part = CreatePartWithZeroOrOneImport(); + var definition = part.ImportDefinitions.First(); + + var exports = ExportFactory.Create("Import", 2); + + Assert.Throws("exports", () => + { + part.SetImport(definition, exports); + }); + } + + [Fact] + public void SetImport_ExactlyOneDefinitionAsDefinitionArgumentAndTwoExportsAsExportsArgument_ShouldThrowArgument() + { + var part = CreatePartWithExactlyOneImport(); + var definition = part.ImportDefinitions.First(); + + var exports = ExportFactory.Create("Import", 2); + + Assert.Throws("exports", () => + { + part.SetImport(definition, exports); + }); + } + + [Fact] + public void SetImport_ExactlyOneDefinitionAsDefinitionArgumentAndEmptyExportsAsExportsArgument_ShouldThrowArgument() + { + var part = CreatePartWithExactlyOneImport(); + var definition = part.ImportDefinitions.First(); + + var exports = Enumerable.Empty(); + + Assert.Throws("exports", () => + { + part.SetImport(definition, exports); + }); + } + + [Fact] + public void SetImport_WrongTypeExportGiven_ShouldThrowComposablePart() + { + var part = CreatePart(new MySharedPartExport()); + var import = part.ImportDefinitions.First(); + + CompositionAssert.ThrowsPart(() => + { + part.SetImport(import, CreateSimpleExports("21")); + }); + } + + [Fact] + public void SetImport_SetPostValueAndSetAgainOnInstance_ShouldSetProperty() + { + var import = new MySharedPartExport(); + var part = CreatePart(import); + var importDef = part.ImportDefinitions.First(); + + part.SetImport(importDef, CreateSimpleExports(21)); + + Assert.NotEqual(import.Value, 21); + part.Activate(); + + Assert.Equal(import.Value, 21); + + part.SetImport(importDef, CreateSimpleExports(42)); + + Assert.NotEqual(import.Value, 42); + + part.Activate(); + + Assert.Equal(import.Value, 42); + } + + [Fact] + public void GetExportedValue_WhenDisposed_ShouldThrowObjectDisposed() + { + var part = CreateDefaultDisposablePart(); + var definition = part.ExportDefinitions.First(); + + ((IDisposable)part).Dispose(); + + ExceptionAssert.ThrowsDisposed(part, () => + { + part.GetExportedValue(definition); + }); + } + + [Fact] + public void GetExportedValue_NullAsDefinitionArgument_ShouldThrowArgumentNull() + { + var part = CreateDefaultPart(); + + Assert.Throws("definition", () => + { + part.GetExportedValue((ExportDefinition)null); + }); + } + + [Fact] + public void GetExportedValue_WrongDefinitionAsDefinitionArgument_ShouldThrowArgument() + { + var part = CreateDefaultPart(); + var definition = ExportDefinitionFactory.Create(); + + Assert.Throws("definition", () => + { + part.GetExportedValue(definition); + }); + } + + [Fact] + public void GetExportedValue_MissingPrerequisiteImport_ShouldThrowInvalidOperation() + { + var part = CreatePart(typeof(SimpleConstructorInjectedObject)); + var definition = part.ExportDefinitions.First(); + + ExceptionAssert.Throws(() => + { + part.GetExportedValue(definition); + }); + } + + [Fact] + [ActiveIssue(484204)] + public void GetExportedValue_MissingPostImports_ShouldThrowComposition() + { + var part = CreatePart(typeof(MySharedPartExport)); + + // Signal that the composition should be finished + part.Activate(); + + var definition = part.ExportDefinitions.First(); + + // Dev10:484204 - This used to cause a failure but after we made + // ReflectionComposablePart internal we needed to back remove this + // validation for post imports to make declarative composition work. + CompositionAssert.ThrowsError(ErrorId.ImportNotSetOnPart, () => + { + part.GetExportedValue(definition); + }); + } + + [Fact] + public void GetExportedValue_NoConstructorOnDefinition_ShouldThrowComposablePart() + { + var part = CreatePart(typeof(ClassWithNoMarkedOrDefaultConstructor)); + + var definition = part.ExportDefinitions.First(); + + CompositionAssert.ThrowsPart(() => + { + part.GetExportedValue(definition); + }); + } + + [Fact] + public void GetExportedValue_UnhandledExceptionThrowInConstructor_ShouldThrowComposablePart() + { + var part = CreatePart(typeof(ExportWithExceptionDuringConstruction)); + + var definition = part.ExportDefinitions.First(); + + CompositionAssert.ThrowsPart(() => + { + part.GetExportedValue(definition); + }); + } + + [Fact] + public void GetExportedValue_GetObjectAfterSetPreImport_ShouldGetValue() + { + var part = CreatePart(typeof(SimpleConstructorInjectedObject)); + + var import = part.ImportDefinitions.First(); + part.SetImport(import, CreateSimpleExports(21)); + + part.Activate(); + + var definition = part.ExportDefinitions.First(); + var exportObject = (SimpleConstructorInjectedObject)part.GetExportedValue(definition); + + Assert.Equal(21, exportObject.CISimpleValue); + } + + [Fact] + public void GetExportedValue_GetObjectAfterSetPostImport_ShouldGetValue() + { + var part = CreatePart(typeof(MySharedPartExport)); + + var import = part.ImportDefinitions.First(); + part.SetImport(import, CreateSimpleExports(21)); + + part.Activate(); + + var definition = part.ExportDefinitions.First(); + var exportObject = (MySharedPartExport)part.GetExportedValue(definition); + + Assert.NotNull(exportObject); + Assert.Equal(21, exportObject.Value); + } + + [Fact] + public void GetExportedValue_CallMultipleTimes_ShouldReturnSame() + { + var part = CreatePart(typeof(MySharedPartExport)); + + var import = part.ImportDefinitions.First(); + part.SetImport(import, CreateSimpleExports(21)); + + part.Activate(); + + var definition = part.ExportDefinitions.First(); + var exportedValue1 = part.GetExportedValue(definition); + var exportedValue2 = part.GetExportedValue(definition); + + Assert.Same(exportedValue1, exportedValue2); + } + + [Fact] + public void GetExportedValue_FromStaticClass_ShouldReturnExport() + { + var part = CreatePart(typeof(StaticExportClass)); + + var definition = part.ExportDefinitions.First(); + + var exportObject = (string)part.GetExportedValue(definition); + + Assert.Equal("StaticString", exportObject); + } + + [Fact] + public void GetExportedValue_OptionalPostNotGiven_ShouldReturnValidObject() + { + var part = CreatePart(typeof(ClassWithOptionalPostImport)); + part.Activate(); + + var definition = part.ExportDefinitions.First(); + var exportObject = (ClassWithOptionalPostImport)part.GetExportedValue(definition); + + Assert.Null(exportObject.Formatter); + } + + [Fact] + public void GetExportedValue_OptionalPreNotGiven_ShouldReturnValidObject() + { + var part = CreatePart(typeof(ClassWithOptionalPreImport)); + part.Activate(); + + var definition = part.ExportDefinitions.First(); + + var exportedValue = (ClassWithOptionalPreImport)part.GetExportedValue(definition); + Assert.Null(exportedValue.Formatter); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types.Retrieve the LoaderExceptions property for more information. + public void ICompositionElementDisplayName_ShouldReturnTypeDisplayName() + { + var expectations = Expectations.GetAttributedTypes(); + foreach (var e in expectations) + { + var part = (ICompositionElement)CreatePart(e); + + Assert.Equal(e.GetDisplayName(), part.DisplayName); + } + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void ToString_ShouldReturnICompositionElementDisplayName() + { + var expectations = Expectations.GetAttributedTypes(); + foreach (var e in expectations) + { + var part = (ICompositionElement)CreatePart(e); + + Assert.Equal(part.DisplayName, part.ToString()); + } + } + + [PartNotDiscoverable] + public class PropertyExporter + { + [Export] + public object Property { get { return new object(); } } + } + + [PartNotDiscoverable] + public class FieldExporter + { + [Export] + public object Field = null; + } + + [PartNotDiscoverable] + public class MethodExporter + { + [Export("Method")] + public void Method() { } + } + + [PartNotDiscoverable] + [Export] + public class TypeExporter + { + } + + [Fact] + public void GetExportedObjectAlwaysReturnsSameReference_ForProperty() + { + var cp = CreatePart(new PropertyExporter()); + var ed = cp.ExportDefinitions.Single(); + var eo1 = cp.GetExportedValue(ed); + var eo2 = cp.GetExportedValue(ed); + Assert.Same(eo1, eo2); + } + + [Fact] + public void GetExportedObjectAlwaysReturnsSameReference_ForField() + { + var exporter = new FieldExporter(); + var cp = CreatePart(new FieldExporter()); + var ed = cp.ExportDefinitions.Single(); + + exporter.Field = new object(); + var eo1 = cp.GetExportedValue(ed); + exporter.Field = new object(); + var eo2 = cp.GetExportedValue(ed); + Assert.Same(eo1, eo2); + } + + [Fact] + public void GetExportedObjectAlwaysReturnsSameReference_ForMethod() + { + var cp = CreatePart(new MethodExporter()); + var ed = cp.ExportDefinitions.Single(); + var eo1 = cp.GetExportedValue(ed); + var eo2 = cp.GetExportedValue(ed); + Assert.Same(eo1, eo2); + } + + [Fact] + public void GetExportedObjectAlwaysReturnsSameReference_ForType() + { + var cp = CreatePart(new TypeExporter()); + var ed = cp.ExportDefinitions.Single(); + var eo1 = cp.GetExportedValue(ed); + var eo2 = cp.GetExportedValue(ed); + Assert.Same(eo1, eo2); + } + + [PartNotDiscoverable] + public class MethodWithoutContractName + { + [Export] + public void MethodWithoutContractNameNotAllowed() + { + } + } + + public interface IContract + { + } + + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public class CustomImportAttributeInvalidTarget : ImportAttribute + { + public CustomImportAttributeInvalidTarget() + : base(typeof(IContract)) + { + } + } + + [PartNotDiscoverable] + public class ImportWithCustomImport + { + [CustomImport] + IContract ImportWithCustomAttributeImport { get; set; } + } + + [PartNotDiscoverable] + public class ImportWithCustomImportInvalidTarget + { + [CustomImportAttributeInvalidTarget] + void InvalidImport() { } + } + + [Fact] + public void ImportDefinitions_ImportWithCustomAttributeImports() + { + var part = CreatePart(typeof(ImportWithCustomImport)); + Assert.Equal(part.ImportDefinitions.Count(), 1); + ContractBasedImportDefinition import = part.ImportDefinitions.First() as ContractBasedImportDefinition; + Assert.NotNull(import); + + Assert.Equal(AttributedModelServices.GetContractName(typeof(IContract)), import.ContractName); + Assert.Equal(AttributedModelServices.GetTypeIdentity(typeof(IContract)), import.RequiredTypeIdentity); + } + + [Fact] + public void ImportDefinitions_ImportWithCustomImportInvalidTarget_ShouldbeIgnored() + { + var part = CreatePart(typeof(ImportWithCustomImportInvalidTarget)); + Assert.Equal(part.ImportDefinitions.Count(), 0); + } + + [PartNotDiscoverable] + public class ImportManyWithCustomImportMany + { + [CustomImportMany] + IContract ImportManyWithCustomAttributeImportMany { get; set; } + } + + [PartNotDiscoverable] + public class ImportManyWithCustomImportManyInvalidTarget + { + [CustomImportMany] + void InvalidImportMany() { } + } + + [Fact] + public void ImportDefinitions_ImportManyWithCustomAttributeImportManys() + { + var part = CreatePart(typeof(ImportManyWithCustomImportMany)); + Assert.Equal(part.ImportDefinitions.Count(), 1); + ContractBasedImportDefinition import = part.ImportDefinitions.First() as ContractBasedImportDefinition; + Assert.NotNull(import); + + Assert.Equal(AttributedModelServices.GetContractName(typeof(IContract)), import.ContractName); + Assert.Equal(AttributedModelServices.GetTypeIdentity(typeof(IContract)), import.RequiredTypeIdentity); + } + + [Fact] + public void ImportDefinitions_ImportManyWithCustomImportManyInvalidTarget_ShouldbeIgnored() + { + var part = CreatePart(typeof(ImportManyWithCustomImportManyInvalidTarget)); + Assert.Equal(part.ImportDefinitions.Count(), 0); + } + + [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)] + public class CustomImportingConstructorAttribute : ImportingConstructorAttribute + { + public CustomImportingConstructorAttribute() + : base() + { + } + } + + [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = true, Inherited = false)] + public class CustomImportingConstructorAllowMultipleAttribute : ImportingConstructorAttribute + { + public CustomImportingConstructorAllowMultipleAttribute() + : base() + { + } + } + + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public class CustomImportingConstructorInvalidTargetAttribute : ImportingConstructorAttribute + { + public CustomImportingConstructorInvalidTargetAttribute() + : base() + { + } + } + + [PartNotDiscoverable] + public class ImportingConstructorWithCustomImportingConstructor + { + [CustomImportingConstructor] + ImportingConstructorWithCustomImportingConstructor([Import] IContract argument) { } + } + + [PartNotDiscoverable] + public class ImportingConstructorWithCustomImportingConstructorAllowMultiple + { + [CustomImportingConstructorAllowMultiple] + [CustomImportingConstructorAllowMultiple] + ImportingConstructorWithCustomImportingConstructorAllowMultiple([Import] IContract argument) { } + } + + [PartNotDiscoverable] + public class ImportingConstructorWithCustomImportingConstructorInvalidTarget + { + [CustomImportingConstructorInvalidTarget] + void InvalidImportingConstructor() { } + } + + [Fact] + public void ImportDefinitions_ImportingConstructorWithCustomAttributeImportingConstructors() + { + var part = CreatePart(typeof(ImportingConstructorWithCustomImportingConstructor)); + Assert.Equal(part.ImportDefinitions.Count(), 1); + ContractBasedImportDefinition import = part.ImportDefinitions.First() as ContractBasedImportDefinition; + Assert.NotNull(import); + + Assert.Equal(AttributedModelServices.GetContractName(typeof(IContract)), import.ContractName); + Assert.Equal(AttributedModelServices.GetTypeIdentity(typeof(IContract)), import.RequiredTypeIdentity); + } + + [Fact] + public void ImportDefinitions_ImportingConstructorWithCustomAttributeImportingConstructorsWithAllowMultiple_ShouldNotThrowInvalidOperation() + { + var part = CreatePart(typeof(ImportingConstructorWithCustomImportingConstructorAllowMultiple)); + + Assert.Equal(part.ImportDefinitions.Count(), 1); + ContractBasedImportDefinition import = part.ImportDefinitions.First() as ContractBasedImportDefinition; + Assert.NotNull(import); + + Assert.Equal(AttributedModelServices.GetContractName(typeof(IContract)), import.ContractName); + Assert.Equal(AttributedModelServices.GetTypeIdentity(typeof(IContract)), import.RequiredTypeIdentity); + } + + [Fact] + public void ImportDefinitions_ImportingConstructorWithCustomImportingConstructorInvalidTarget_ShouldbeIgnored() + { + var part = CreatePart(typeof(ImportingConstructorWithCustomImportingConstructorInvalidTarget)); + Assert.Equal(part.ImportDefinitions.Count(), 0); + } + + private Export[] CreateSimpleExports(object value) + { + var export = ExportFactory.Create("NoContract", () => value); + + return new Export[] { export }; + } + + private ReflectionComposablePart CreatePartWithExport() + { + return CreatePart(typeof(StaticExportClass)); + } + + private ReflectionComposablePart CreatePartWithNonRecomposableImport() + { + return CreatePart(typeof(SingleImportWithAllowDefault)); + } + + private ReflectionComposablePart CreatePartWithZeroOrOneImport() + { + return CreatePart(typeof(SingleImportWithAllowDefault)); + } + + private ReflectionComposablePart CreatePartWithExactlyOneImport() + { + return CreatePart(typeof(SingleImport)); + } + + private ReflectionComposablePart CreateDefaultPart() + { + return CreatePart(new object()); + } + + [PartNotDiscoverable] + [Export] + public class DisposablePart : IDisposable + { + [Import(AllowDefault = true)] + public int Foo { get; set; } + + public void Dispose() { } + } + + private ReflectionComposablePart CreateDefaultDisposablePart() + { + return CreatePart(typeof(DisposablePart)); + } + + private ReflectionComposablePart CreatePart(object instance) + { + if (instance is Type) + { + var definition = PartDefinitionFactory.CreateAttributed((Type)instance); + + return (ReflectionComposablePart)definition.CreatePart(); + } + else + { + var definition = PartDefinitionFactory.CreateAttributed(instance.GetType()); + + return new ReflectionComposablePart(definition, instance); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/ReflectionMemberExportDefinitionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/ReflectionMemberExportDefinitionTests.cs new file mode 100644 index 0000000000..9a12c8cb1b --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/ReflectionMemberExportDefinitionTests.cs @@ -0,0 +1,171 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using System.Reflection; +using Microsoft.Internal; +using Xunit; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + public class ReflectionMemberExportDefinitionTests + { + private static ReflectionMemberExportDefinition CreateReflectionExportDefinition(LazyMemberInfo exportMember, string contractname, IDictionary metadata) + { + return CreateReflectionExportDefinition(exportMember, contractname, metadata, null); + } + + private static ReflectionMemberExportDefinition CreateReflectionExportDefinition(LazyMemberInfo exportMember, string contractname, IDictionary metadata, ICompositionElement origin) + { + return (ReflectionMemberExportDefinition)ReflectionModelServices.CreateExportDefinition( + exportMember, contractname, CreateLazyMetadata(metadata), origin); + } + + private static Lazy> CreateLazyMetadata(IDictionary metadata) + { + return new Lazy>(() => metadata, false); + } + + [Fact] + public void Constructor() + { + MemberInfo expectedMember = this.GetType(); + LazyMemberInfo expectedExportingMemberInfo = new LazyMemberInfo(expectedMember); + + string expectedContractName = "Contract"; + IDictionary expectedMetadata = new Dictionary(); + expectedMetadata["Key1"] = 1; + expectedMetadata["Key2"] = "Value2"; + + ReflectionMemberExportDefinition definition = CreateReflectionExportDefinition(expectedExportingMemberInfo, expectedContractName, expectedMetadata); + + Assert.Equal(expectedExportingMemberInfo, definition.ExportingLazyMember); + Assert.Same(expectedMember, definition.ExportingLazyMember.GetAccessors()[0]); + Assert.Equal(MemberTypes.TypeInfo, definition.ExportingLazyMember.MemberType); + + Assert.Same(expectedContractName, definition.ContractName); + + Assert.NotNull(definition.Metadata); + Assert.True(definition.Metadata.Keys.SequenceEqual(expectedMetadata.Keys)); + Assert.True(definition.Metadata.Values.SequenceEqual(expectedMetadata.Values)); + + Assert.Null(((ICompositionElement)definition).Origin); + } + + [Fact] + public void Constructor_NullMetadata() + { + MemberInfo expectedMember = this.GetType(); + LazyMemberInfo expectedExportingMemberInfo = new LazyMemberInfo(expectedMember); + + string expectedContractName = "Contract"; + + ReflectionMemberExportDefinition definition = CreateReflectionExportDefinition(expectedExportingMemberInfo, expectedContractName, null); + + Assert.Equal(expectedExportingMemberInfo, definition.ExportingLazyMember); + Assert.Same(expectedMember, definition.ExportingLazyMember.GetAccessors()[0]); + Assert.Equal(MemberTypes.TypeInfo, definition.ExportingLazyMember.MemberType); + + Assert.Same(expectedContractName, definition.ContractName); + + Assert.NotNull(definition.Metadata); + Assert.Equal(0, definition.Metadata.Count); + + Assert.Null(((ICompositionElement)definition).Origin); + } + + [Fact] + public void SetDefinition_OriginIsSet() + { + var expectedPartDefinition = PartDefinitionFactory.CreateAttributed(typeof(object)); + var exportDefinition = CreateReflectionExportDefinition(new LazyMemberInfo(this.GetType()), "ContractName", null, expectedPartDefinition); + + Assert.Same(expectedPartDefinition, ((ICompositionElement)exportDefinition).Origin); + } + + [Fact] + public void SetDefinition_PartDefinitionDoesNotContainCreationPolicy_CreationPolicyShouldNotBeInMetadata() + { + var expectedPartDefinition = PartDefinitionFactory.CreateAttributed(typeof(object)); + var exportDefinition = CreateReflectionExportDefinition(new LazyMemberInfo(this.GetType()), "ContractName", null); + + Assert.False(exportDefinition.Metadata.ContainsKey(CompositionConstants.PartCreationPolicyMetadataName)); + } + + [Fact] + public void ICompositionElementDisplayName_ValueAsContractName_ShouldIncludeContractName() + { + var contractNames = Expectations.GetContractNamesWithEmpty(); + + foreach (var contractName in contractNames) + { + if (string.IsNullOrEmpty(contractName)) continue; + var definition = (ICompositionElement)CreateReflectionExportDefinition(new LazyMemberInfo(typeof(string)), contractName, null); + + var e = CreateDisplayNameExpectation(contractName); + + Assert.Equal(e, definition.DisplayName); + } + } + + [Fact] + public void ICompositionElementDisplayName_TypeAsMember_ShouldIncludeMemberDisplayName() + { + var types = Expectations.GetTypes(); + + foreach (var type in types) + { + var definition = (ICompositionElement)CreateReflectionExportDefinition(new LazyMemberInfo(type), "Contract", null); + + var e = CreateDisplayNameExpectation(type); + + Assert.Equal(e, definition.DisplayName); + } + } + + [Fact] + public void ICompositionElementDisplayName_ValueAsMember_ShouldIncludeMemberDisplayName() + { + var members = Expectations.GetMembers(); + + foreach (var member in members) + { + var definition = (ICompositionElement)CreateReflectionExportDefinition(new LazyMemberInfo(member), "Contract", null); + + var e = CreateDisplayNameExpectation(member); + + Assert.Equal(e, definition.DisplayName); + } + } + + [Fact] + public void ToString_ShouldReturnDisplayName() + { + var members = Expectations.GetMembers(); + + foreach (var member in members) + { + var definition = (ICompositionElement)CreateReflectionExportDefinition(new LazyMemberInfo(member), "Contract", null); + + Assert.Equal(definition.DisplayName, definition.ToString()); + } + } + + private static string CreateDisplayNameExpectation(string contractName) + { + return String.Format("System.String (ContractName=\"{0}\")", contractName); + } + + private static string CreateDisplayNameExpectation(MemberInfo member) + { + return String.Format("{0} (ContractName=\"Contract\")", member.GetDisplayName()); + } + + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/ReflectionMemberImportDefinitionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/ReflectionMemberImportDefinitionTests.cs new file mode 100644 index 0000000000..94cc899c84 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/ReflectionMemberImportDefinitionTests.cs @@ -0,0 +1,71 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using System.Reflection; +using Microsoft.Internal; +using Xunit; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + public class ReflectionMemberImportDefinitionTests + { + [Fact] + public void Constructor() + { + PropertyInfo expectedMember = typeof(PublicImportsExpectingPublicExports).GetProperty("PublicImportPublicProperty"); + LazyMemberInfo expectedImportingMemberInfo = new LazyMemberInfo(expectedMember); + IEnumerable> requiredMetadata = new KeyValuePair[] { new KeyValuePair("Foo", typeof(object)) }; + IDictionary metadata = new Dictionary(); + metadata["Key"] = "value"; + + ReflectionMemberImportDefinition definition = new ReflectionMemberImportDefinition( + expectedImportingMemberInfo, "Contract", (string)null, requiredMetadata, ImportCardinality.ZeroOrMore, true, false, CreationPolicy.NonShared, metadata, null); + + Assert.Equal(expectedImportingMemberInfo, definition.ImportingLazyMember); + + Assert.Equal("Contract", definition.ContractName); + Assert.Same(requiredMetadata, definition.RequiredMetadata); + Assert.Same(metadata, definition.Metadata); + Assert.Equal(CreationPolicy.NonShared, definition.RequiredCreationPolicy); + Assert.Equal(true, definition.IsRecomposable); + Assert.Equal(false, definition.IsPrerequisite); + Assert.Null(((ICompositionElement)definition).Origin); + Assert.NotNull(((ICompositionElement)definition).DisplayName); + Assert.True(((ICompositionElement)definition).DisplayName.Contains(expectedMember.GetDisplayName())); + } + + [Fact] + public void Constructor_WithNullRequiredMetadata() + { + LazyMemberInfo member = CreateLazyMemberInfo(); + + ReflectionMemberImportDefinition definition = new ReflectionMemberImportDefinition( + member, "Contract", (string)null, null, ImportCardinality.ZeroOrMore, true, false, CreationPolicy.NonShared, null, null); + + Assert.NotNull(definition.RequiredMetadata); + Assert.Equal(0, definition.RequiredMetadata.Count()); + } + + [Fact] + public void SetDefinition_OriginIsSet() + { + LazyMemberInfo member = CreateLazyMemberInfo(); + var expectedPartDefinition = PartDefinitionFactory.CreateAttributed(typeof(object)); + ReflectionMemberImportDefinition definition = new ReflectionMemberImportDefinition( + member, "Contract", (string)null, null, ImportCardinality.ZeroOrMore, true, false, CreationPolicy.NonShared, null, expectedPartDefinition); + + Assert.Same(expectedPartDefinition, ((ICompositionElement)definition).Origin); + } + + private static LazyMemberInfo CreateLazyMemberInfo() + { + PropertyInfo expectedMember = typeof(PublicImportsExpectingPublicExports).GetProperty("PublicImportPublicProperty"); + return new LazyMemberInfo(expectedMember); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/ReflectionModelServicesEx.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/ReflectionModelServicesEx.cs new file mode 100644 index 0000000000..18640455ee --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/ReflectionModelServicesEx.cs @@ -0,0 +1,180 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq.Expressions; +using System.Reflection; + +// NOTE : this is a helper class for exosig the EditorFactory functionality to tests until ExportFactory can be moved where it belongs +namespace System.ComponentModel.Composition.ReflectionModel +{ + public static class ReflectionModelServicesEx + { + public static ContractBasedImportDefinition CreateImportDefinition( + Lazy parameter, + string contractName, + string requiredTypeIdentity, + IEnumerable> requiredMetadata, + ImportCardinality cardinality, + CreationPolicy requiredCreationPolicy, + bool isExportFactory, + ICompositionElement origin) + { + return ReflectionModelServicesEx.CreateImportDefinition(parameter, contractName, requiredTypeIdentity, requiredMetadata, cardinality, requiredCreationPolicy, MetadataServices.EmptyMetadata, isExportFactory, origin); + } + + public static ContractBasedImportDefinition CreateImportDefinition( + Lazy parameter, + string contractName, + string requiredTypeIdentity, + IEnumerable> requiredMetadata, + ImportCardinality cardinality, + CreationPolicy requiredCreationPolicy, + IDictionary metadata, + bool isExportFactory, + ICompositionElement origin) + { + return ReflectionModelServices.CreateImportDefinition(parameter, contractName, requiredTypeIdentity, requiredMetadata, cardinality, requiredCreationPolicy, metadata, isExportFactory, origin); + } + + public static ContractBasedImportDefinition CreateImportDefinition( + LazyMemberInfo importingMember, + string contractName, + string requiredTypeIdentity, + IEnumerable> requiredMetadata, + ImportCardinality cardinality, + bool isRecomposable, + CreationPolicy requiredCreationPolicy, + bool isExportFactory, + ICompositionElement origin) + { + return ReflectionModelServicesEx.CreateImportDefinition(importingMember, contractName, requiredTypeIdentity, requiredMetadata, cardinality, isRecomposable, requiredCreationPolicy, MetadataServices.EmptyMetadata, isExportFactory, origin); + } + + public static ContractBasedImportDefinition CreateImportDefinition( + LazyMemberInfo importingMember, + string contractName, + string requiredTypeIdentity, + IEnumerable> requiredMetadata, + ImportCardinality cardinality, + bool isRecomposable, + CreationPolicy requiredCreationPolicy, + IDictionary metadata, + bool isExportFactory, + ICompositionElement origin) + { + return ReflectionModelServices.CreateImportDefinition(importingMember, contractName, requiredTypeIdentity, requiredMetadata, cardinality, isRecomposable, requiredCreationPolicy, metadata, isExportFactory, origin); + } + + public static bool IsExportFactoryImportDefinition(ImportDefinition importDefinition) + { + return ReflectionModelServices.IsExportFactoryImportDefinition(importDefinition); + } + + public static ContractBasedImportDefinition CreateExportFactoryImportDefinition(ContractBasedImportDefinition productImportDefinition) + { + return new ExportFactoryImportDefinition(productImportDefinition); + } + + private class ExportFactoryImportDefinition : ContractBasedImportDefinition, IPartCreatorImportDefinition + { + private readonly ContractBasedImportDefinition _productImportDefinition; + + public ExportFactoryImportDefinition(ContractBasedImportDefinition productImportDefinition) + : base(CompositionConstants.PartCreatorContractName, CompositionConstants.PartCreatorTypeIdentity, productImportDefinition.RequiredMetadata, + productImportDefinition.Cardinality, productImportDefinition.IsRecomposable, false, CreationPolicy.Any) + { + _productImportDefinition = productImportDefinition; + } + + public ContractBasedImportDefinition ProductImportDefinition + { + get + { + return _productImportDefinition; + } + } + + public override Expression> Constraint + { + get + { + return CreateExportFactoryConstraint(base.Constraint, this._productImportDefinition); + } + } + + public override bool IsConstraintSatisfiedBy(ExportDefinition exportDefinition) + { + if (!base.IsConstraintSatisfiedBy(exportDefinition)) + { + return false; + } + + return IsProductConstraintSatisfiedBy(this._productImportDefinition, exportDefinition); + } + + private static bool IsProductConstraintSatisfiedBy(ImportDefinition productImportDefinition, ExportDefinition exportDefinition) + { + object productValue = null; + if (exportDefinition.Metadata.TryGetValue(CompositionConstants.ProductDefinitionMetadataName, out productValue)) + { + ExportDefinition productDefinition = productValue as ExportDefinition; + + if (productDefinition != null) + { + return productImportDefinition.IsConstraintSatisfiedBy(productDefinition); + } + } + + return false; + } + + private static readonly PropertyInfo _exportDefinitionMetadataProperty = typeof(ExportDefinition).GetProperty("Metadata"); + private static readonly MethodInfo _metadataContainsKeyMethod = typeof(IDictionary).GetMethod("ContainsKey"); + private static readonly MethodInfo _metadataItemMethod = typeof(IDictionary).GetMethod("get_Item"); + + private static Expression> CreateExportFactoryConstraint(Expression> baseConstraint, ImportDefinition productImportDefinition) + { + ParameterExpression exportDefinitionParameter = baseConstraint.Parameters[0]; + + // exportDefinition.Metadata + Expression metadataExpression = Expression.Property(exportDefinitionParameter, _exportDefinitionMetadataProperty); + + // exportDefinition.Metadata.ContainsKey("ProductDefinition") + Expression containsProductExpression = Expression.Call( + metadataExpression, + _metadataContainsKeyMethod, + Expression.Constant(CompositionConstants.ProductDefinitionMetadataName)); + + // exportDefinition.Metadata["ProductDefinition"] + Expression productExportDefinitionExpression = Expression.Call( + metadataExpression, + _metadataItemMethod, + Expression.Constant(CompositionConstants.ProductDefinitionMetadataName)); + + // ProductImportDefinition.Contraint((ExportDefinition)exportDefinition.Metadata["ProductDefinition"]) + Expression productMatchExpression = + Expression.Invoke(productImportDefinition.Constraint, + Expression.Convert(productExportDefinitionExpression, typeof(ExportDefinition))); + + // baseContraint(exportDefinition) && + // exportDefinition.Metadata.ContainsKey("ProductDefinition") && + // ProductImportDefinition.Contraint((ExportDefinition)exportDefinition.Metadata["ProductDefinition"]) + Expression> constraint = + Expression.Lambda>( + Expression.AndAlso( + baseConstraint.Body, + Expression.AndAlso( + containsProductExpression, + productMatchExpression)), + exportDefinitionParameter); + + return constraint; + } + } + + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/ReflectionModelServicesTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/ReflectionModelServicesTests.cs new file mode 100644 index 0000000000..ce62f3c57d --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/ReflectionModelServicesTests.cs @@ -0,0 +1,1089 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using System.Reflection; +using System.UnitTesting; +using Microsoft.Internal; +using Xunit; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + public class ReflectionModelServicesTests + { + [Fact] + public void CreatePartDefinition() + { + Type expectedType = typeof(TestPart); + Lazy expectedLazyType = expectedType.AsLazy(); + IDictionary expectedMetadata = new Dictionary(); + expectedMetadata["Key1"] = 1; + expectedMetadata["Key2"] = "Value2"; + + IEnumerable expectedImports = CreateImports(expectedType); + IEnumerable expectedExports = CreateExports(expectedType); + + ICompositionElement expectedOrigin = new MockOrigin(); + + ComposablePartDefinition partDefinition = ReflectionModelServices.CreatePartDefinition(expectedLazyType, false, + new Lazy>(() => expectedImports), + new Lazy>(() => expectedExports), + expectedMetadata.AsLazy(), expectedOrigin); + Assert.NotNull(partDefinition); + + ReflectionComposablePartDefinition definition = partDefinition as ReflectionComposablePartDefinition; + Assert.NotNull(definition); + + Assert.Same(expectedType, definition.GetPartType()); + Assert.True(definition.Metadata.Keys.SequenceEqual(expectedMetadata.Keys)); + Assert.True(definition.Metadata.Values.SequenceEqual(expectedMetadata.Values)); + Assert.True(definition.ExportDefinitions.SequenceEqual(expectedExports.Cast())); + Assert.True(definition.ImportDefinitions.SequenceEqual(expectedImports.Cast())); + Assert.Same(expectedOrigin, ((ICompositionElement)definition).Origin); + Assert.NotNull(((ICompositionElement)definition).DisplayName); + Assert.False(definition.IsDisposalRequired); + } + + [Fact] + public void CreatePartDefinition_Disposable() + { + Type expectedType = typeof(TestPart); + Lazy expectedLazyType = expectedType.AsLazy(); + IDictionary expectedMetadata = new Dictionary(); + expectedMetadata["Key1"] = 1; + expectedMetadata["Key2"] = "Value2"; + + IEnumerable expectedImports = CreateImports(expectedType); + IEnumerable expectedExports = CreateExports(expectedType); + + ICompositionElement expectedOrigin = new MockOrigin(); + + ComposablePartDefinition partDefinition = ReflectionModelServices.CreatePartDefinition(expectedLazyType, true, + new Lazy>(() => expectedImports), + new Lazy>(() => expectedExports), + expectedMetadata.AsLazy(), expectedOrigin); + Assert.NotNull(partDefinition); + + ReflectionComposablePartDefinition definition = partDefinition as ReflectionComposablePartDefinition; + Assert.NotNull(definition); + + Assert.Same(expectedType, definition.GetPartType()); + Assert.True(definition.Metadata.Keys.SequenceEqual(expectedMetadata.Keys)); + Assert.True(definition.Metadata.Values.SequenceEqual(expectedMetadata.Values)); + Assert.True(definition.ExportDefinitions.SequenceEqual(expectedExports.Cast())); + Assert.True(definition.ImportDefinitions.SequenceEqual(expectedImports.Cast())); + Assert.Same(expectedOrigin, ((ICompositionElement)definition).Origin); + Assert.NotNull(((ICompositionElement)definition).DisplayName); + Assert.True(definition.IsDisposalRequired); + } + + [Fact] + public void CreatePartDefinition_NullMetadataAllowed() + { + Type expectedType = typeof(TestPart); + Lazy expectedLazyType = expectedType.AsLazy(); + + IEnumerable expectedImports = CreateImports(expectedType); + IEnumerable expectedExports = CreateExports(expectedType); + + ICompositionElement expectedOrigin = new MockOrigin(); + + ComposablePartDefinition partDefinition = ReflectionModelServices.CreatePartDefinition(expectedLazyType, false, + new Lazy>(() => expectedImports), + new Lazy>(() => expectedExports), + null, expectedOrigin); + Assert.NotNull(partDefinition); + + ReflectionComposablePartDefinition definition = partDefinition as ReflectionComposablePartDefinition; + Assert.NotNull(definition); + Assert.NotNull(definition.Metadata); + Assert.Equal(0, definition.Metadata.Count); + } + + [Fact] + public void CreatePartDefinition_EvaluatedNullMetadataAllowed() + { + Type expectedType = typeof(TestPart); + Lazy expectedLazyType = expectedType.AsLazy(); + IDictionary expectedMetadata = null; + + IEnumerable expectedImports = CreateImports(expectedType); + IEnumerable expectedExports = CreateExports(expectedType); + + ICompositionElement expectedOrigin = new MockOrigin(); + + ComposablePartDefinition partDefinition = ReflectionModelServices.CreatePartDefinition(expectedLazyType, false, + new Lazy>(() => expectedImports), + new Lazy>(() => expectedExports), + expectedMetadata.AsLazy(), expectedOrigin); + Assert.NotNull(partDefinition); + + ReflectionComposablePartDefinition definition = partDefinition as ReflectionComposablePartDefinition; + Assert.NotNull(definition); + Assert.NotNull(definition.Metadata); + Assert.Equal(0, definition.Metadata.Count); + } + + [Fact] + public void CreatePartDefinition_NullExportsAllowed() + { + Type expectedType = typeof(TestPart); + Lazy expectedLazyType = expectedType.AsLazy(); + + IEnumerable expectedImports = CreateImports(expectedType); + IEnumerable expectedExports = CreateExports(expectedType); + IDictionary expectedMetadata = new Dictionary(); + + ICompositionElement expectedOrigin = new MockOrigin(); + + ComposablePartDefinition partDefinition = ReflectionModelServices.CreatePartDefinition(expectedLazyType, false, + new Lazy>(() => expectedImports), + null, + expectedMetadata.AsLazy(), expectedOrigin); + Assert.NotNull(partDefinition); + + ReflectionComposablePartDefinition definition = partDefinition as ReflectionComposablePartDefinition; + Assert.NotNull(definition); + Assert.NotNull(definition.ExportDefinitions); + Assert.Equal(0, definition.ExportDefinitions.Count()); + } + + [Fact] + public void CreatePartDefinition_EvaluatedNullExportsAllowed() + { + Type expectedType = typeof(TestPart); + Lazy expectedLazyType = expectedType.AsLazy(); + + IEnumerable expectedImports = CreateImports(expectedType); + IEnumerable expectedExports = CreateExports(expectedType); + IDictionary expectedMetadata = new Dictionary(); + + ICompositionElement expectedOrigin = new MockOrigin(); + + ComposablePartDefinition partDefinition = ReflectionModelServices.CreatePartDefinition(expectedLazyType, false, + new Lazy>(() => expectedImports), + new Lazy>(() => null), + expectedMetadata.AsLazy(), expectedOrigin); + Assert.NotNull(partDefinition); + + ReflectionComposablePartDefinition definition = partDefinition as ReflectionComposablePartDefinition; + Assert.NotNull(definition); + Assert.NotNull(definition.ExportDefinitions); + Assert.Equal(0, definition.ExportDefinitions.Count()); + } + + [Fact] + public void CreatePartDefinition_ExportsMustBeOfRightType() + { + Type expectedType = typeof(TestPart); + Lazy expectedLazyType = expectedType.AsLazy(); + + IEnumerable expectedImports = CreateImports(expectedType); + IEnumerable expectedExports = CreateExports(expectedType); + IDictionary expectedMetadata = new Dictionary(); + + ICompositionElement expectedOrigin = new MockOrigin(); + + ComposablePartDefinition partDefinition = ReflectionModelServices.CreatePartDefinition(expectedLazyType, false, + new Lazy>(() => expectedImports), + new Lazy>(() => CreateInvalidExports()), + expectedMetadata.AsLazy(), expectedOrigin); + Assert.NotNull(partDefinition); + + ReflectionComposablePartDefinition definition = partDefinition as ReflectionComposablePartDefinition; + Assert.NotNull(definition); + + ExceptionAssert.Throws(() => + { + definition.ExportDefinitions.Count(); + }); + } + + [Fact] + public void CreatePartDefinition_NullImportsAllowed() + { + Type expectedType = typeof(TestPart); + Lazy expectedLazyType = expectedType.AsLazy(); + + IEnumerable expectedImports = CreateImports(expectedType); + IEnumerable expectedExports = CreateExports(expectedType); + IDictionary expectedMetadata = new Dictionary(); + + ICompositionElement expectedOrigin = new MockOrigin(); + + ComposablePartDefinition partDefinition = ReflectionModelServices.CreatePartDefinition(expectedLazyType, false, + null, + new Lazy>(() => expectedExports), + expectedMetadata.AsLazy(), expectedOrigin); + Assert.NotNull(partDefinition); + + ReflectionComposablePartDefinition definition = partDefinition as ReflectionComposablePartDefinition; + Assert.NotNull(definition); + Assert.NotNull(definition.ImportDefinitions); + Assert.Equal(0, definition.ImportDefinitions.Count()); + } + + [Fact] + public void CreatePartDefinition_EvaluatedNullImportsAllowed() + { + Type expectedType = typeof(TestPart); + Lazy expectedLazyType = expectedType.AsLazy(); + + IEnumerable expectedImports = CreateImports(expectedType); + IEnumerable expectedExports = CreateExports(expectedType); + IDictionary expectedMetadata = new Dictionary(); + + ICompositionElement expectedOrigin = new MockOrigin(); + + ComposablePartDefinition partDefinition = ReflectionModelServices.CreatePartDefinition(expectedLazyType, false, + new Lazy>(() => null), + new Lazy>(() => expectedExports), + expectedMetadata.AsLazy(), expectedOrigin); + Assert.NotNull(partDefinition); + + ReflectionComposablePartDefinition definition = partDefinition as ReflectionComposablePartDefinition; + Assert.NotNull(definition); + Assert.NotNull(definition.ImportDefinitions); + Assert.Equal(0, definition.ImportDefinitions.Count()); + } + + [Fact] + public void CreatePartDefinition_ImportsMustBeOfRightType() + { + Type expectedType = typeof(TestPart); + Lazy expectedLazyType = expectedType.AsLazy(); + + IEnumerable expectedImports = CreateImports(expectedType); + IEnumerable expectedExports = CreateExports(expectedType); + IDictionary expectedMetadata = new Dictionary(); + + ICompositionElement expectedOrigin = new MockOrigin(); + + ComposablePartDefinition partDefinition = ReflectionModelServices.CreatePartDefinition(expectedLazyType, false, + new Lazy>(() => CreateInvalidImports()), + new Lazy>(() => expectedExports), + expectedMetadata.AsLazy(), expectedOrigin); + Assert.NotNull(partDefinition); + + ReflectionComposablePartDefinition definition = partDefinition as ReflectionComposablePartDefinition; + Assert.NotNull(definition); + ExceptionAssert.Throws(() => + { + definition.ImportDefinitions.Count(); + }); + + } + + [Fact] + public void CreatePartDefinition_NullTypeNotAllowed() + { + Type expectedType = typeof(TestPart); + Lazy expectedLazyType = expectedType.AsLazy(); + IDictionary expectedMetadata = new Dictionary(); + expectedMetadata["Key1"] = 1; + expectedMetadata["Key2"] = "Value2"; + + IEnumerable expectedImports = CreateImports(expectedType); + IEnumerable expectedExports = CreateExports(expectedType); + + ICompositionElement expectedOrigin = new MockOrigin(); + + Assert.Throws("partType", () => + { + ComposablePartDefinition partDefinition = ReflectionModelServices.CreatePartDefinition(null, false, + new Lazy>(() => expectedImports), + new Lazy>(() => expectedExports), + expectedMetadata.AsLazy(), expectedOrigin); + }); + } + + [Fact] + public void CreatePartDefinition_NullEvaluatedTypeNotAllowed() + { + Type expectedType = typeof(TestPart); + Lazy expectedLazyType = expectedType.AsLazy(); + + IDictionary expectedMetadata = new Dictionary(); + expectedMetadata["Key1"] = 1; + expectedMetadata["Key2"] = "Value2"; + + IEnumerable expectedImports = CreateImports(expectedType); + IEnumerable expectedExports = CreateExports(expectedType); + + ICompositionElement expectedOrigin = new MockOrigin(); + + ComposablePartDefinition partDefinition = ReflectionModelServices.CreatePartDefinition(new Lazy(() => null), false, + new Lazy>(() => expectedImports), + new Lazy>(() => expectedExports), + expectedMetadata.AsLazy(), expectedOrigin); + + ReflectionComposablePartDefinition definition = partDefinition as ReflectionComposablePartDefinition; + Assert.NotNull(definition); + + ExceptionAssert.Throws(() => + { + definition.GetPartType(); + }); + } + + [Fact] + public void GetPartType() + { + Type expectedType = typeof(TestPart); + Lazy expectedLazyType = expectedType.AsLazy(); + IDictionary expectedMetadata = new Dictionary(); + expectedMetadata["Key1"] = 1; + expectedMetadata["Key2"] = "Value2"; + + IEnumerable expectedImports = CreateImports(expectedType); + IEnumerable expectedExports = CreateExports(expectedType); + + ICompositionElement expectedOrigin = new MockOrigin(); + + ComposablePartDefinition partDefinition = ReflectionModelServices.CreatePartDefinition(expectedLazyType, false, + new Lazy>(() => expectedImports), + new Lazy>(() => expectedExports), + expectedMetadata.AsLazy(), expectedOrigin); + Assert.NotNull(partDefinition); + + Lazy lazyPartType = ReflectionModelServices.GetPartType(partDefinition); + Assert.Equal(expectedLazyType, lazyPartType); + } + + [Fact] + public void GetPartType_NullAsPart_ShouldThrowArgumentNull() + { + Assert.Throws("partDefinition", () => + { + ReflectionModelServices.GetPartType(null); + }); + } + + [Fact] + public void GetPartType_InvalidPart_ShouldThrowArgument() + { + Assert.Throws("partDefinition", () => + { + ReflectionModelServices.GetPartType(new InvalidPartDefinition()); + }); + } + + [Fact] + public void IsDisposalRequired_ForNonDisposable() + { + Type expectedType = typeof(TestPart); + Lazy expectedLazyType = expectedType.AsLazy(); + IDictionary expectedMetadata = new Dictionary(); + expectedMetadata["Key1"] = 1; + expectedMetadata["Key2"] = "Value2"; + + IEnumerable expectedImports = CreateImports(expectedType); + IEnumerable expectedExports = CreateExports(expectedType); + + ICompositionElement expectedOrigin = new MockOrigin(); + + ComposablePartDefinition partDefinition = ReflectionModelServices.CreatePartDefinition(expectedLazyType, false, + new Lazy>(() => expectedImports), + new Lazy>(() => expectedExports), + expectedMetadata.AsLazy(), expectedOrigin); + Assert.NotNull(partDefinition); + + bool isDisposalRequired = ReflectionModelServices.IsDisposalRequired(partDefinition); + Assert.False(isDisposalRequired); + } + + [Fact] + public void IsDisposalRequired_ForDisposable() + { + Type expectedType = typeof(TestPart); + Lazy expectedLazyType = expectedType.AsLazy(); + IDictionary expectedMetadata = new Dictionary(); + expectedMetadata["Key1"] = 1; + expectedMetadata["Key2"] = "Value2"; + + IEnumerable expectedImports = CreateImports(expectedType); + IEnumerable expectedExports = CreateExports(expectedType); + + ICompositionElement expectedOrigin = new MockOrigin(); + + ComposablePartDefinition partDefinition = ReflectionModelServices.CreatePartDefinition(expectedLazyType, true, + new Lazy>(() => expectedImports), + new Lazy>(() => expectedExports), + expectedMetadata.AsLazy(), expectedOrigin); + Assert.NotNull(partDefinition); + + bool isDisposalRequired = ReflectionModelServices.IsDisposalRequired(partDefinition); + Assert.True(isDisposalRequired); + } + + [Fact] + public void IsDisposalRequired_NullAsPart_ShouldThrowArgumentNull() + { + Assert.Throws("partDefinition", () => + { + ReflectionModelServices.IsDisposalRequired(null); + }); + } + + [Fact] + public void IsDisposalRequired_InvalidPart_ShouldThrowArgument() + { + Assert.Throws("partDefinition", () => + { + ReflectionModelServices.IsDisposalRequired(new InvalidPartDefinition()); + }); + } + + [Fact] + public void CreateExportDefinition() + { + PropertyInfo property = typeof(TestPart).GetProperties().First(); + LazyMemberInfo expectedLazyMember = new LazyMemberInfo(property); + + IDictionary expectedMetadata = new Dictionary(); + expectedMetadata["Key1"] = 1; + expectedMetadata["Key2"] = "Value2"; + + string expectedContractName = "Foo"; + + ICompositionElement expectedOrigin = new MockOrigin(); + + ExportDefinition exportDefinition = ReflectionModelServices.CreateExportDefinition(expectedLazyMember, expectedContractName, expectedMetadata.AsLazy(), expectedOrigin); + Assert.NotNull(exportDefinition); + ReflectionMemberExportDefinition definition = exportDefinition as ReflectionMemberExportDefinition; + Assert.NotNull(definition); + + Assert.Equal(expectedContractName, definition.ContractName); + Assert.True(definition.Metadata.Keys.SequenceEqual(expectedMetadata.Keys)); + Assert.True(definition.Metadata.Values.SequenceEqual(expectedMetadata.Values)); + Assert.Equal(expectedOrigin, ((ICompositionElement)definition).Origin); + Assert.Equal(expectedLazyMember, definition.ExportingLazyMember); + } + + [Fact] + public void CreateExportDefinition_NullAsContractName_ThrowsNullArgument() + { + PropertyInfo property = typeof(TestPart).GetProperties().First(); + LazyMemberInfo expectedLazyMember = new LazyMemberInfo(property); + + IDictionary expectedMetadata = new Dictionary(); + expectedMetadata["Key1"] = 1; + expectedMetadata["Key2"] = "Value2"; + + ICompositionElement expectedOrigin = new MockOrigin(); + + Assert.Throws("contractName", () => + { + ReflectionModelServices.CreateExportDefinition(expectedLazyMember, null, expectedMetadata.AsLazy(), expectedOrigin); + }); + } + + public void CreateExportDefinition_NullAsMetadata_Allowed() + { + PropertyInfo property = typeof(TestPart).GetProperties().First(); + LazyMemberInfo expectedLazyMember = new LazyMemberInfo(property); + + IDictionary expectedMetadata = new Dictionary(); + expectedMetadata["Key1"] = 1; + expectedMetadata["Key2"] = "Value2"; + + string expectedContractName = "Foo"; + ICompositionElement expectedOrigin = new MockOrigin(); + + ExportDefinition definition = ReflectionModelServices.CreateExportDefinition(expectedLazyMember, expectedContractName, expectedMetadata.AsLazy(), expectedOrigin); + Assert.NotNull(definition.Metadata); + Assert.Equal(0, definition.Metadata.Count); + } + + [Fact] + public void CreateExportDefinition_InvalidLazymemberInfo_ShouldThrowArtument() + { + EventInfo _event = typeof(TestPart).GetEvents().First(); + LazyMemberInfo expectedLazyMember = new LazyMemberInfo(_event); + + IDictionary expectedMetadata = new Dictionary(); + expectedMetadata["Key1"] = 1; + expectedMetadata["Key2"] = "Value2"; + + string expectedContractName = "Foo"; + + ICompositionElement expectedOrigin = new MockOrigin(); + + Assert.Throws("exportingMember", () => + { + ReflectionModelServices.CreateExportDefinition(expectedLazyMember, expectedContractName, expectedMetadata.AsLazy(), expectedOrigin); + }); + } + + [Fact] + public void GetExportingMember() + { + PropertyInfo property = typeof(TestPart).GetProperties().First(); + LazyMemberInfo expectedLazyMember = new LazyMemberInfo(property); + + IDictionary expectedMetadata = new Dictionary(); + expectedMetadata["Key1"] = 1; + expectedMetadata["Key2"] = "Value2"; + + string expectedContractName = "Foo"; + + ICompositionElement expectedOrigin = new MockOrigin(); + + ExportDefinition exportDefinition = ReflectionModelServices.CreateExportDefinition(expectedLazyMember, expectedContractName, expectedMetadata.AsLazy(), expectedOrigin); + Assert.NotNull(exportDefinition); + + LazyMemberInfo lazyMember = ReflectionModelServices.GetExportingMember(exportDefinition); + Assert.Equal(expectedLazyMember, lazyMember); + } + + [Fact] + public void GetExportingMember_NullAsExportDefinition_ShouldThrowArhumentNull() + { + Assert.Throws("exportDefinition", () => + { + ReflectionModelServices.GetExportingMember(null); + }); + } + + [Fact] + public void GetExportingMember_InvalidExportDefinition_ShouldThrowArhumentNull() + { + Assert.Throws("exportDefinition", () => + { + ReflectionModelServices.GetExportingMember(new ExportDefinition("Foo", null)); + }); + } + + [Fact] + public void CreateImportDefinition_Member() + { + PropertyInfo property = typeof(TestPart).GetProperties().First(); + LazyMemberInfo expectedLazyMember = new LazyMemberInfo(property); + + string expectedContractName = "Foo"; + string expectedRequiredTypeIdentity = "Bar"; + KeyValuePair[] expectedRequiredMetadata = new KeyValuePair[] { new KeyValuePair("Key1", typeof(string)), new KeyValuePair("Key2", typeof(int)) }; + ImportCardinality expectedCardinality = ImportCardinality.ExactlyOne; + CreationPolicy expectedCreationPolicy = CreationPolicy.NonShared; + bool expectedRecomposable = true; + + ICompositionElement expectedOrigin = new MockOrigin(); + + ImportDefinition importDefinition = ReflectionModelServices.CreateImportDefinition( + expectedLazyMember, + expectedContractName, + expectedRequiredTypeIdentity, + expectedRequiredMetadata, + expectedCardinality, + expectedRecomposable, + expectedCreationPolicy, + expectedOrigin); + Assert.NotNull(importDefinition); + + ReflectionMemberImportDefinition definition = importDefinition as ReflectionMemberImportDefinition; + Assert.NotNull(definition); + + Assert.Equal(expectedLazyMember, definition.ImportingLazyMember); + Assert.Equal(definition.ContractName, expectedContractName); + Assert.Equal(definition.RequiredTypeIdentity, expectedRequiredTypeIdentity); + Assert.True(definition.RequiredMetadata.SequenceEqual(expectedRequiredMetadata)); + Assert.Equal(definition.Cardinality, expectedCardinality); + Assert.Equal(definition.RequiredCreationPolicy, expectedCreationPolicy); + Assert.Equal(definition.IsRecomposable, expectedRecomposable); + Assert.Same(expectedOrigin, ((ICompositionElement)definition).Origin); + Assert.False(definition.IsPrerequisite); + } + + [Fact] + public void CreateImportDefinition_Member_InvalidMember_ShouldThrowArgument() + { + MethodInfo method = typeof(TestPart).GetMethods().First(); + LazyMemberInfo expectedLazyMember = new LazyMemberInfo(method); + + string expectedContractName = "Foo"; + string expectedRequiredTypeIdentity = "Bar"; + KeyValuePair[] expectedRequiredMetadata = new KeyValuePair[] { new KeyValuePair("Key1", typeof(string)), new KeyValuePair("Key2", typeof(int)) }; + ImportCardinality expectedCardinality = ImportCardinality.ExactlyOne; + CreationPolicy expectedCreationPolicy = CreationPolicy.NonShared; + bool expectedRecomposable = true; + + ICompositionElement expectedOrigin = new MockOrigin(); + + Assert.Throws("importingMember", () => + { + ReflectionModelServices.CreateImportDefinition( + expectedLazyMember, + expectedContractName, + expectedRequiredTypeIdentity, + expectedRequiredMetadata, + expectedCardinality, + expectedRecomposable, + expectedCreationPolicy, + expectedOrigin); + }); + } + + [Fact] + public void GetImporingMember() + { + PropertyInfo property = typeof(TestPart).GetProperties().First(); + LazyMemberInfo expectedLazyMember = new LazyMemberInfo(property); + + string expectedContractName = "Foo"; + string expectedRequiredTypeIdentity = "Bar"; + KeyValuePair[] expectedRequiredMetadata = new KeyValuePair[] { new KeyValuePair("Key1", typeof(string)), new KeyValuePair("Key2", typeof(int)) }; + ImportCardinality expectedCardinality = ImportCardinality.ExactlyOne; + CreationPolicy expectedCreationPolicy = CreationPolicy.NonShared; + bool expectedRecomposable = true; + + ICompositionElement expectedOrigin = new MockOrigin(); + + ImportDefinition importDefinition = ReflectionModelServices.CreateImportDefinition( + expectedLazyMember, + expectedContractName, + expectedRequiredTypeIdentity, + expectedRequiredMetadata, + expectedCardinality, + expectedRecomposable, + expectedCreationPolicy, + expectedOrigin); + Assert.NotNull(importDefinition); + + LazyMemberInfo lazyMember = ReflectionModelServices.GetImportingMember(importDefinition); + Assert.Equal(expectedLazyMember, lazyMember); + } + + [Fact] + public void GetImporingMember_NullAsImport_ShouldThrowArgumentNull() + { + Assert.Throws("importDefinition", () => + { + ReflectionModelServices.GetImportingMember(null); + }); + } + + [Fact] + public void GetImporingMember_InvalidImport_ShouldThrowArgument() + { + Assert.Throws("importDefinition", () => + { + ReflectionModelServices.GetImportingMember(new ContractBasedImportDefinition("Foo", "Foo", null, ImportCardinality.ZeroOrMore, false, false, CreationPolicy.Any)); + }); + } + + [Fact] + public void CreateImportDefinition_Parameter() + { + + ParameterInfo parameter = typeof(TestPart).GetConstructor(new Type[] { typeof(int) }).GetParameters()[0]; + Lazy expectedLazyParameter = parameter.AsLazy(); + + string expectedContractName = "Foo"; + string expectedRequiredTypeIdentity = "Bar"; + KeyValuePair[] expectedRequiredMetadata = new KeyValuePair[] { new KeyValuePair("Key1", typeof(string)), new KeyValuePair("Key2", typeof(int)) }; + ImportCardinality expectedCardinality = ImportCardinality.ExactlyOne; + CreationPolicy expectedCreationPolicy = CreationPolicy.NonShared; + + ICompositionElement expectedOrigin = new MockOrigin(); + + ImportDefinition importDefinition = ReflectionModelServices.CreateImportDefinition( + expectedLazyParameter, + expectedContractName, + expectedRequiredTypeIdentity, + expectedRequiredMetadata, + expectedCardinality, + expectedCreationPolicy, + expectedOrigin); + Assert.NotNull(importDefinition); + + ReflectionParameterImportDefinition definition = importDefinition as ReflectionParameterImportDefinition; + Assert.NotNull(definition); + + Assert.Equal(expectedLazyParameter, definition.ImportingLazyParameter); + Assert.Equal(definition.ContractName, expectedContractName); + Assert.Equal(definition.RequiredTypeIdentity, expectedRequiredTypeIdentity); + Assert.True(definition.RequiredMetadata.SequenceEqual(expectedRequiredMetadata)); + Assert.Equal(definition.Cardinality, expectedCardinality); + Assert.Equal(definition.RequiredCreationPolicy, expectedCreationPolicy); + Assert.False(definition.IsRecomposable); + Assert.Same(expectedOrigin, ((ICompositionElement)definition).Origin); + Assert.True(definition.IsPrerequisite); + } + + [Fact] + public void CreateImportDefinition_Parameter_NullAsParamater_ShouldThrowArgumentNull() + { + ParameterInfo parameter = typeof(TestPart).GetConstructor(new Type[] { typeof(int) }).GetParameters()[0]; + Lazy expectedLazyParameter = parameter.AsLazy(); + + string expectedContractName = "Foo"; + string expectedRequiredTypeIdentity = "Bar"; + KeyValuePair[] expectedRequiredMetadata = new KeyValuePair[] { new KeyValuePair("Key1", typeof(string)), new KeyValuePair("Key2", typeof(int)) }; + ImportCardinality expectedCardinality = ImportCardinality.ExactlyOne; + CreationPolicy expectedCreationPolicy = CreationPolicy.NonShared; + + ICompositionElement expectedOrigin = new MockOrigin(); + + Assert.Throws("parameter", () => + { + ReflectionModelServices.CreateImportDefinition( + null, + expectedContractName, + expectedRequiredTypeIdentity, + expectedRequiredMetadata, + expectedCardinality, + expectedCreationPolicy, + expectedOrigin); + }); + } + + [Fact] + public void GetImportingParameter() + { + ParameterInfo parameter = typeof(TestPart).GetConstructor(new Type[] { typeof(int) }).GetParameters()[0]; + Lazy expectedLazyParameter = parameter.AsLazy(); + + string expectedContractName = "Foo"; + string expectedRequiredTypeIdentity = "Bar"; + KeyValuePair[] expectedRequiredMetadata = new KeyValuePair[] { new KeyValuePair("Key1", typeof(string)), new KeyValuePair("Key2", typeof(int)) }; + ImportCardinality expectedCardinality = ImportCardinality.ExactlyOne; + CreationPolicy expectedCreationPolicy = CreationPolicy.NonShared; + + ICompositionElement expectedOrigin = new MockOrigin(); + ImportDefinition importDefinition = ReflectionModelServices.CreateImportDefinition( + expectedLazyParameter, + expectedContractName, + expectedRequiredTypeIdentity, + expectedRequiredMetadata, + expectedCardinality, + expectedCreationPolicy, + expectedOrigin); + Assert.NotNull(importDefinition); + + Lazy lazyParameter = ReflectionModelServices.GetImportingParameter(importDefinition); + Assert.Equal(expectedLazyParameter, lazyParameter); + } + + [Fact] + public void GetImportingParameter_NullAsImport_ShouldThrowArgumentNull() + { + Assert.Throws("importDefinition", () => + { + ReflectionModelServices.GetImportingParameter(null); + }); + } + + [Fact] + public void GetImportingParameter_InvalidImport_ShouldThrowArgument() + { + Assert.Throws("importDefinition", () => + { + ReflectionModelServices.GetImportingParameter(new ContractBasedImportDefinition("Foo", "Foo", null, ImportCardinality.ZeroOrMore, false, false, CreationPolicy.Any)); + }); + } + + [Fact] + public void IsImportingParameter_OnParameterImport() + { + ParameterInfo parameter = typeof(TestPart).GetConstructor(new Type[] { typeof(int) }).GetParameters()[0]; + Lazy expectedLazyParameter = parameter.AsLazy(); + + string expectedContractName = "Foo"; + string expectedRequiredTypeIdentity = "Bar"; + KeyValuePair[] expectedRequiredMetadata = new KeyValuePair[] { new KeyValuePair("Key1", typeof(string)), new KeyValuePair("Key2", typeof(int)) }; + ImportCardinality expectedCardinality = ImportCardinality.ExactlyOne; + CreationPolicy expectedCreationPolicy = CreationPolicy.NonShared; + + ICompositionElement expectedOrigin = new MockOrigin(); + ImportDefinition importDefinition = ReflectionModelServices.CreateImportDefinition( + expectedLazyParameter, + expectedContractName, + expectedRequiredTypeIdentity, + expectedRequiredMetadata, + expectedCardinality, + expectedCreationPolicy, + expectedOrigin); + Assert.NotNull(importDefinition); + + Assert.True(ReflectionModelServices.IsImportingParameter(importDefinition)); + } + + [Fact] + public void IsImportingParameter_OnMemberImport() + { + PropertyInfo property = typeof(TestPart).GetProperties().First(); + LazyMemberInfo expectedLazyMember = new LazyMemberInfo(property); + + string expectedContractName = "Foo"; + string expectedRequiredTypeIdentity = "Bar"; + KeyValuePair[] expectedRequiredMetadata = new KeyValuePair[] { new KeyValuePair("Key1", typeof(string)), new KeyValuePair("Key2", typeof(int)) }; + ImportCardinality expectedCardinality = ImportCardinality.ExactlyOne; + CreationPolicy expectedCreationPolicy = CreationPolicy.NonShared; + bool expectedRecomposable = true; + + ICompositionElement expectedOrigin = new MockOrigin(); + + ImportDefinition importDefinition = ReflectionModelServices.CreateImportDefinition( + expectedLazyMember, + expectedContractName, + expectedRequiredTypeIdentity, + expectedRequiredMetadata, + expectedCardinality, + expectedRecomposable, + expectedCreationPolicy, + expectedOrigin); + Assert.NotNull(importDefinition); + + Assert.False(ReflectionModelServices.IsImportingParameter(importDefinition)); + } + + [Fact] + public void IsImportingParameter_NullAsImport_ShouldThrowArgumentNull() + { + Assert.Throws("importDefinition", () => + { + ReflectionModelServices.IsImportingParameter(null); + }); + } + + [Fact] + public void IsImportingParameter_InvalidImport_ShouldThrowArgument() + { + Assert.Throws("importDefinition", () => + { + ReflectionModelServices.IsImportingParameter(new ContractBasedImportDefinition("Foo", "Foo", null, ImportCardinality.ZeroOrMore, false, false, CreationPolicy.Any)); + }); + } + + [Fact] + public void IsExportFactoryImportDefinition_NullImport_ShouldThrowArgumentNull() + { + Assert.Throws("importDefinition", () => + ReflectionModelServices.IsExportFactoryImportDefinition(null)); + } + + [Fact] + public void IsExportFactoryImportDefinition_InvalidImport_ShouldReturnFalse() + { + Assert.False(ReflectionModelServices.IsExportFactoryImportDefinition(CreateInvalidImport())); + } + + [Fact] + public void IsExportFactoryImportDefinition_NonPartCreatorImport_ShouldReturnFalse() + { + var import = ReflectionModelServices.CreateImportDefinition( + new LazyMemberInfo(MemberTypes.Field, () => new MemberInfo[] { typeof(ReflectionModelServicesTests) }), // bogus member + "Foo", + "Foo", + Enumerable.Empty>(), + ImportCardinality.ZeroOrMore, + false, + CreationPolicy.Any, + null); + + Assert.False(ReflectionModelServices.IsExportFactoryImportDefinition(import)); + } + + [Fact] + public void IsExportFactoryImportDefinition_PartCreatorImport_ShouldReturnTrue() + { + var import = ReflectionModelServices.CreateImportDefinition( + new LazyMemberInfo(MemberTypes.Field, () => new MemberInfo[] { typeof(ReflectionModelServicesTests) }), // bogus member + "Foo", + "Foo", + Enumerable.Empty>(), + ImportCardinality.ZeroOrMore, + false, + CreationPolicy.Any, + MetadataServices.EmptyMetadata, + true, //isPartCreator + null); + + Assert.True(ReflectionModelServices.IsExportFactoryImportDefinition(import)); + } + + [Fact] + public void GetExportFactoryProductImportDefinition_NullImport_ShouldThrowArgumentNull() + { + Assert.Throws("importDefinition", () => + ReflectionModelServices.GetExportFactoryProductImportDefinition(null)); + } + + [Fact] + public void GetExportFactoryProductImportDefinition_InvalidImport_ShouldThrowArgument() + { + Assert.Throws("importDefinition", () => + ReflectionModelServices.GetExportFactoryProductImportDefinition(CreateInvalidImport())); + } + + [Fact] + public void GetExportFactoryProductImportDefinition_() + { + + } + + [Fact] + public void GetExportFactoryProductImportDefinition_PartCreatorImport_() + { + LazyMemberInfo bogusMember = new LazyMemberInfo(MemberTypes.Field, () => new MemberInfo[] { typeof(ReflectionModelServicesTests) }); + var import = ReflectionModelServices.CreateImportDefinition( + bogusMember, + "Foo", + "Foo", + Enumerable.Empty>(), + ImportCardinality.ZeroOrMore, + false, + CreationPolicy.Any, + null, + true, //isPartCreator + null); + + var productImport = ReflectionModelServices.GetExportFactoryProductImportDefinition(import); + + var import2 = ReflectionModelServices.CreateImportDefinition( + bogusMember, + productImport.ContractName, + productImport.RequiredTypeIdentity, + productImport.RequiredMetadata, + productImport.Cardinality, + productImport.IsRecomposable, + productImport.RequiredCreationPolicy, + productImport.Metadata, + true, //isPartCreator + null); + + Assert.Equal(import.ContractName, import2.ContractName); + Assert.Equal(import.Cardinality, import2.Cardinality); + Assert.Equal(import.IsRecomposable, import2.IsRecomposable); + Assert.Equal(import.RequiredCreationPolicy, import2.RequiredCreationPolicy); + Assert.Equal(import.RequiredTypeIdentity, import2.RequiredTypeIdentity); + EnumerableAssert.AreEqual(import.RequiredMetadata, import2.RequiredMetadata); + } + + private static IEnumerable CreateInvalidImports() + { + yield return new ContractBasedImportDefinition("Foo", "Foo", null, ImportCardinality.ZeroOrMore, false, false, CreationPolicy.Any); + } + + private static ImportDefinition CreateInvalidImport() + { + return new ContractBasedImportDefinition("Foo", "Foo", null, ImportCardinality.ZeroOrMore, false, false, CreationPolicy.Any); + } + + private static IEnumerable CreateInvalidExports() + { + yield return new ExportDefinition("Foo", null); + } + + class InvalidPartDefinition : ComposablePartDefinition + { + public override ComposablePart CreatePart() + { + throw new NotImplementedException(); + } + + public override IEnumerable ExportDefinitions + { + get { throw new NotImplementedException(); } + } + + public override IEnumerable ImportDefinitions + { + get { throw new NotImplementedException(); } + } + } + + private static List CreateImports(Type type) + { + List imports = new List(); + foreach (PropertyInfo property in type.GetProperties()) + { + imports.Add(new ReflectionMemberImportDefinition(new LazyMemberInfo(property), "Contract", (string)null, new KeyValuePair[] { new KeyValuePair("Key1", typeof(string)), new KeyValuePair("Key2", typeof(int)) }, ImportCardinality.ZeroOrOne, true, false, CreationPolicy.Any, MetadataServices.EmptyMetadata, new TypeOrigin(type))); + } + + return imports; + } + + private static List CreateExports(Type type) + { + List exports = new List(); + foreach (PropertyInfo property in type.GetProperties()) + { + exports.Add(ReflectionModelServices.CreateExportDefinition(new LazyMemberInfo(property), "Contract", new Lazy>(() => null), new TypeOrigin(type))); + } + + return exports; + } + + public class TestPart + { + public TestPart(int arg1) + { + } + + public int field1; + public string field2; + public int Property1 { get; set; } + public string Property2 + { + get { return null; } + set + { + this.Event.Invoke(this, null); + } + } + public event EventHandler Event; + } + + private class TypeOrigin : ICompositionElement + { + private readonly Type _type; + private readonly ICompositionElement _orgin; + + public TypeOrigin(Type type) + : this(type, null) + { + } + + public TypeOrigin(Type type, ICompositionElement origin) + { + this._type = type; + this._orgin = origin; + } + + public string DisplayName + { + get + { + return this._type.GetDisplayName(); + } + } + + public ICompositionElement Origin + { + get + { + return this._orgin; + } + } + } + + private class MockOrigin : ICompositionElement + { + public string DisplayName + { + get { throw new NotImplementedException(); } + } + + public ICompositionElement Origin + { + get { throw new NotImplementedException(); } + } + } + + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/ReflectionParameterImportDefinitionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/ReflectionParameterImportDefinitionTests.cs new file mode 100644 index 0000000000..a9fb84cc0a --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ReflectionModel/ReflectionParameterImportDefinitionTests.cs @@ -0,0 +1,130 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using System.Reflection; +using Microsoft.Internal; +using Xunit; + +namespace System.ComponentModel.Composition.ReflectionModel +{ + public class ReflectionParameterImportDefinitionTests + { + [Fact] + public void Constructor() + { + Lazy parameter = CreateLazyParameter(); + IEnumerable> requiredMetadata = new KeyValuePair[] { new KeyValuePair("Foo", typeof(object)) }; + IDictionary metadata = new Dictionary(); + metadata["Key"] = "value"; + + ReflectionParameterImportDefinition definition = new ReflectionParameterImportDefinition( + parameter, "Contract", (string)null, requiredMetadata, ImportCardinality.ZeroOrMore, CreationPolicy.NonShared, metadata, null); + + Assert.Same(parameter, definition.ImportingLazyParameter); + Assert.Equal("Contract", definition.ContractName); + Assert.Same(requiredMetadata, definition.RequiredMetadata); + Assert.Same(metadata, definition.Metadata); + Assert.Equal(CreationPolicy.NonShared, definition.RequiredCreationPolicy); + Assert.Equal(false, definition.IsRecomposable); + Assert.Equal(true, definition.IsPrerequisite); + Assert.Null(((ICompositionElement)definition).Origin); + Assert.NotNull(((ICompositionElement)definition).DisplayName); + } + + [Fact] + public void Constructor_WithNullRequiredMetadata() + { + Lazy parameter = CreateLazyParameter(); + + ReflectionParameterImportDefinition definition = new ReflectionParameterImportDefinition( + parameter, "Contract", (string)null, null, ImportCardinality.ZeroOrMore, CreationPolicy.NonShared, null, null); + + Assert.NotNull(definition.RequiredMetadata); + Assert.Equal(0, definition.RequiredMetadata.Count()); + } + + [Fact] + public void SetDefinition_OriginIsSet() + { + Lazy parameter = CreateLazyParameter(); + var expectedPartDefinition = PartDefinitionFactory.CreateAttributed(typeof(object)); + + ReflectionParameterImportDefinition definition = new ReflectionParameterImportDefinition( + parameter, "Contract", (string)null, null, ImportCardinality.ZeroOrMore, CreationPolicy.NonShared, null, expectedPartDefinition); + + Assert.Same(expectedPartDefinition, ((ICompositionElement)definition).Origin); + } + + [Fact] + public void ICompositionElementDisplayName_ValueAsParameter_ShouldIncludeParameterName() + { + var names = Expectations.GetContractNamesWithEmpty(); + + Assert.All(names, name => + { + var definition = CreateReflectionParameterImportDefinition(name); + + var e = CreateDisplayNameExpectationFromParameterName(definition, name); + + Assert.Equal(e, ((ICompositionElement)definition).DisplayName); + }); + } + + [Fact] + public void ICompositionElementDisplayName_ValueAsParameter_ShouldIncludeContractName() + { + var types = Expectations.GetTypes(); + + Assert.All(types, type => + { + var definition = CreateReflectionParameterImportDefinition(type); + + var e = CreateDisplayNameExpectationFromContractName(definition, type); + + Assert.Equal(e, ((ICompositionElement)definition).DisplayName); + }); + } + + private Lazy CreateLazyParameter() + { + return typeof(SimpleConstructorInjectedObject).GetConstructors().First().GetParameters().First().AsLazy(); + } + + private static string CreateDisplayNameExpectationFromContractName(ReflectionParameterImportDefinition definition, Type type) + { + string contractName = AttributedModelServices.GetContractName(type); + + return String.Format("{0} (Parameter=\"\", ContractName=\"{1}\")", definition.ImportingLazyParameter.Value.Member.GetDisplayName(), contractName); + } + + private static string CreateDisplayNameExpectationFromParameterName(ReflectionParameterImportDefinition definition, string name) + { + return String.Format("{0} (Parameter=\"{1}\", ContractName=\"System.String\")", definition.ImportingLazyParameter.Value.Member.GetDisplayName(), name); + } + + private static ReflectionParameterImportDefinition CreateReflectionParameterImportDefinition(Type parameterType) + { + var parameter = ReflectionFactory.CreateParameter(parameterType); + + return CreateReflectionParameterImportDefinition(parameter); + } + + private static ReflectionParameterImportDefinition CreateReflectionParameterImportDefinition(string name) + { + var parameter = ReflectionFactory.CreateParameter(name); + + return CreateReflectionParameterImportDefinition(parameter); + } + + private static ReflectionParameterImportDefinition CreateReflectionParameterImportDefinition(ParameterInfo parameter) + { + return new ReflectionParameterImportDefinition( + parameter.AsLazy(), AttributedModelServices.GetContractName(parameter.ParameterType), (string)null, null, ImportCardinality.ZeroOrMore, CreationPolicy.NonShared, null, null); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/SampleComponents.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/SampleComponents.cs new file mode 100644 index 0000000000..66cdff3b37 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/SampleComponents.cs @@ -0,0 +1,1147 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.Composition.Primitives; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public interface IGetString + { + string GetString(); + } + + public class PublicComponentWithPublicExports + { + public const string PublicFieldExpectedValue = "PublicField"; + [Export("PublicField")] + public string PublicField = PublicFieldExpectedValue; + public const string PublicPropertyExpectedValue = "PublicProperty"; + [Export("PublicProperty")] + public string PublicProperty { get { return PublicPropertyExpectedValue; } } + public const string PublicMethodExpectedValue = "PublicMethod"; + [Export("PublicDelegate")] + public string PublicMethod() { return PublicMethodExpectedValue; } + public const string PublicNestedClassExpectedValue = "PublicNestedClass"; + [Export("PublicIGetString")] + public class PublicNestedClass : IGetString + { + public string GetString() { return PublicNestedClassExpectedValue; } + } + } + + [Export] + public class PublicImportsExpectingPublicExports + { + [Import("PublicField")] + public string PublicImportPublicField { get; set; } + [Import("PublicProperty")] + public string PublicImportPublicProperty { get; set; } + [Import("PublicDelegate")] + public Func PublicImportPublicMethod { get; set; } + [Import("PublicIGetString")] + public IGetString PublicImportPublicNestedClass { get; set; } + + public void VerifyIsBound() + { + Assert.Equal(PublicComponentWithPublicExports.PublicFieldExpectedValue, PublicImportPublicField); + Assert.Equal(PublicComponentWithPublicExports.PublicPropertyExpectedValue, PublicImportPublicProperty); + Assert.Equal(PublicComponentWithPublicExports.PublicMethodExpectedValue, PublicImportPublicMethod()); + Assert.Equal(PublicComponentWithPublicExports.PublicNestedClassExpectedValue, PublicImportPublicNestedClass.GetString()); + } + } + + [Export] + internal class InternalImportsExpectingPublicExports + { + [Import("PublicField")] + internal string InternalImportPublicField { get; set; } + [Import("PublicProperty")] + internal string InternalImportPublicProperty { get; set; } + [Import("PublicDelegate")] + internal Func InternalImportPublicMethod { get; set; } + [Import("PublicIGetString")] + internal IGetString InternalImportPublicNestedClass { get; set; } + + public void VerifyIsBound() + { + Assert.Equal(PublicComponentWithPublicExports.PublicFieldExpectedValue, InternalImportPublicField); + Assert.Equal(PublicComponentWithPublicExports.PublicPropertyExpectedValue, InternalImportPublicProperty); + Assert.Equal(PublicComponentWithPublicExports.PublicMethodExpectedValue, InternalImportPublicMethod()); + Assert.Equal(PublicComponentWithPublicExports.PublicNestedClassExpectedValue, InternalImportPublicNestedClass.GetString()); + } + } + + public class PublicComponentWithInternalExports + { + public const string InternalFieldExpectedValue = "InternalField"; + [Export("InternalField")] + internal string InternalField = InternalFieldExpectedValue; + public const string InternalPropertyExpectedValue = "InternalProperty"; + [Export("InternalProperty")] + internal string InternalProperty { get { return InternalPropertyExpectedValue; } } + public const string InternalMethodExpectedValue = "InternalMethod"; + [Export("InternalDelegate")] + internal string InternalMethod() { return InternalMethodExpectedValue; } + public const string InternalNestedClassExpectedValue = "InternalNestedClass"; + [Export("InternalIGetString")] + internal class InternalNestedClass : IGetString + { + public string GetString() { return InternalNestedClassExpectedValue; } + } + } + + [Export] + public class PublicImportsExpectingInternalExports + { + [Import("InternalField")] + public string PublicImportInternalField { get; set; } + [Import("InternalProperty")] + public string PublicImportInternalProperty { get; set; } + [Import("InternalDelegate")] + public Func PublicImportInternalMethod { get; set; } + [Import("InternalIGetString")] + public IGetString PublicImportInternalNestedClass { get; set; } + + public void VerifyIsBound() + { + Assert.Equal(PublicComponentWithInternalExports.InternalFieldExpectedValue, PublicImportInternalField); + Assert.Equal(PublicComponentWithInternalExports.InternalPropertyExpectedValue, PublicImportInternalProperty); + Assert.Equal(PublicComponentWithInternalExports.InternalMethodExpectedValue, PublicImportInternalMethod()); + Assert.Equal(PublicComponentWithInternalExports.InternalNestedClassExpectedValue, PublicImportInternalNestedClass.GetString()); + } + } + + [Export] + internal class InternalImportsExpectingInternalExports + { + [Import("InternalField")] + internal string InternalImportInternalField { get; set; } + [Import("InternalProperty")] + internal string InternalImportInternalProperty { get; set; } + [Import("InternalDelegate")] + internal Func InternalImportInternalMethod { get; set; } + [Import("InternalIGetString")] + internal IGetString InternalImportInternalNestedClass { get; set; } + + public void VerifyIsBound() + { + Assert.Equal(PublicComponentWithInternalExports.InternalFieldExpectedValue, InternalImportInternalField); + Assert.Equal(PublicComponentWithInternalExports.InternalPropertyExpectedValue, InternalImportInternalProperty); + Assert.Equal(PublicComponentWithInternalExports.InternalMethodExpectedValue, InternalImportInternalMethod()); + Assert.Equal(PublicComponentWithInternalExports.InternalNestedClassExpectedValue, InternalImportInternalNestedClass.GetString()); + } + } + + public class PublicComponentWithProtectedExports + { + public const string ProtectedFieldExpectedValue = "ProtectedField"; + [Export("ProtectedField")] + protected string ProtectedField = ProtectedFieldExpectedValue; + public const string ProtectedPropertyExpectedValue = "ProtectedProperty"; + [Export("ProtectedProperty")] + protected string ProtectedProperty { get { return ProtectedPropertyExpectedValue; } } + public const string ProtectedMethodExpectedValue = "ProtectedMethod"; + [Export("ProtectedDelegate")] + protected string ProtectedMethod() { return ProtectedMethodExpectedValue; } + public const string ProtectedNestedClassExpectedValue = "ProtectedNestedClass"; + [Export("ProtectedIGetString")] + protected class ProtectedNestedClass : IGetString + { + public string GetString() { return ProtectedNestedClassExpectedValue; } + } + } + + [Export] + public class PublicImportsExpectingProtectedExports + { + [Import("ProtectedField")] + public string PublicImportProtectedField { get; set; } + [Import("ProtectedProperty")] + public string PublicImportProtectedProperty { get; set; } + [Import("ProtectedDelegate")] + public Func PublicImportProtectedMethod { get; set; } + [Import("ProtectedIGetString")] + public IGetString PublicImportProtectedNestedClass { get; set; } + + public void VerifyIsBound() + { + Assert.Equal(PublicComponentWithProtectedExports.ProtectedFieldExpectedValue, PublicImportProtectedField); + Assert.Equal(PublicComponentWithProtectedExports.ProtectedPropertyExpectedValue, PublicImportProtectedProperty); + Assert.Equal(PublicComponentWithProtectedExports.ProtectedMethodExpectedValue, PublicImportProtectedMethod()); + Assert.Equal(PublicComponentWithProtectedExports.ProtectedNestedClassExpectedValue, PublicImportProtectedNestedClass.GetString()); + } + } + + [Export] + internal class InternalImportsExpectingProtectedExports + { + [Import("ProtectedField")] + internal string InternalImportProtectedField { get; set; } + [Import("ProtectedProperty")] + internal string InternalImportProtectedProperty { get; set; } + [Import("ProtectedDelegate")] + internal Func InternalImportProtectedMethod { get; set; } + [Import("ProtectedIGetString")] + internal IGetString InternalImportProtectedNestedClass { get; set; } + + public void VerifyIsBound() + { + Assert.Equal(PublicComponentWithProtectedExports.ProtectedFieldExpectedValue, InternalImportProtectedField); + Assert.Equal(PublicComponentWithProtectedExports.ProtectedPropertyExpectedValue, InternalImportProtectedProperty); + Assert.Equal(PublicComponentWithProtectedExports.ProtectedMethodExpectedValue, InternalImportProtectedMethod()); + Assert.Equal(PublicComponentWithProtectedExports.ProtectedNestedClassExpectedValue, InternalImportProtectedNestedClass.GetString()); + } + } + + public class PublicComponentWithProtectedInternalExports + { + public const string ProtectedInternalFieldExpectedValue = "ProtectedInternalField"; + [Export("ProtectedInternalField")] + protected internal string ProtectedInternalField = ProtectedInternalFieldExpectedValue; + public const string ProtectedInternalPropertyExpectedValue = "ProtectedInternalProperty"; + [Export("ProtectedInternalProperty")] + protected internal string ProtectedInternalProperty { get { return ProtectedInternalPropertyExpectedValue; } } + public const string ProtectedInternalMethodExpectedValue = "ProtectedInternalMethod"; + [Export("ProtectedInternalDelegate")] + protected internal string ProtectedInternalMethod() { return ProtectedInternalMethodExpectedValue; } + public const string ProtectedInternalNestedClassExpectedValue = "ProtectedInternalNestedClass"; + [Export("ProtectedInternalIGetString")] + protected internal class ProtectedInternalNestedClass : IGetString + { + public string GetString() { return ProtectedInternalNestedClassExpectedValue; } + } + } + + [Export] + public class PublicImportsExpectingProtectedInternalExports + { + [Import("ProtectedInternalField")] + public string PublicImportProtectedInternalField { get; set; } + [Import("ProtectedInternalProperty")] + public string PublicImportProtectedInternalProperty { get; set; } + [Import("ProtectedInternalDelegate")] + public Func PublicImportProtectedInternalMethod { get; set; } + [Import("ProtectedInternalIGetString")] + public IGetString PublicImportProtectedInternalNestedClass { get; set; } + + public void VerifyIsBound() + { + Assert.Equal(PublicComponentWithProtectedInternalExports.ProtectedInternalFieldExpectedValue, PublicImportProtectedInternalField); + Assert.Equal(PublicComponentWithProtectedInternalExports.ProtectedInternalPropertyExpectedValue, PublicImportProtectedInternalProperty); + Assert.Equal(PublicComponentWithProtectedInternalExports.ProtectedInternalMethodExpectedValue, PublicImportProtectedInternalMethod()); + Assert.Equal(PublicComponentWithProtectedInternalExports.ProtectedInternalNestedClassExpectedValue, PublicImportProtectedInternalNestedClass.GetString()); + } + } + + [Export] + internal class InternalImportsExpectingProtectedInternalExports + { + [Import("ProtectedInternalField")] + internal string InternalImportProtectedInternalField { get; set; } + [Import("ProtectedInternalProperty")] + internal string InternalImportProtectedInternalProperty { get; set; } + [Import("ProtectedInternalDelegate")] + internal Func InternalImportProtectedInternalMethod { get; set; } + [Import("ProtectedInternalIGetString")] + internal IGetString InternalImportProtectedInternalNestedClass { get; set; } + + public void VerifyIsBound() + { + Assert.Equal(PublicComponentWithProtectedInternalExports.ProtectedInternalFieldExpectedValue, InternalImportProtectedInternalField); + Assert.Equal(PublicComponentWithProtectedInternalExports.ProtectedInternalPropertyExpectedValue, InternalImportProtectedInternalProperty); + Assert.Equal(PublicComponentWithProtectedInternalExports.ProtectedInternalMethodExpectedValue, InternalImportProtectedInternalMethod()); + Assert.Equal(PublicComponentWithProtectedInternalExports.ProtectedInternalNestedClassExpectedValue, InternalImportProtectedInternalNestedClass.GetString()); + } + } + + public class PublicComponentWithPrivateExports + { + public const string PrivateFieldExpectedValue = "PrivateField"; + public const string PrivatePropertyExpectedValue = "PrivateProperty"; + [Export("PrivateProperty")] + private string PrivateProperty { get { return PrivatePropertyExpectedValue; } } + public const string PrivateMethodExpectedValue = "PrivateMethod"; + [Export("PrivateDelegate")] + private string PrivateMethod() { return PrivateMethodExpectedValue; } + public const string PrivateNestedClassExpectedValue = "PrivateNestedClass"; + [Export("PrivateIGetString")] + private class PrivateNestedClass : IGetString + { + public string GetString() { return PrivateNestedClassExpectedValue; } + } + } + + [Export] + public class PublicImportsExpectingPrivateExports + { + [Import("PrivateField")] + public string PublicImportPrivateField { get; set; } + [Import("PrivateProperty")] + public string PublicImportPrivateProperty { get; set; } + [Import("PrivateDelegate")] + public Func PublicImportPrivateMethod { get; set; } + [Import("PrivateIGetString")] + public IGetString PublicImportPrivateNestedClass { get; set; } + + public void VerifyIsBound() + { + Assert.Equal(PublicComponentWithPrivateExports.PrivateFieldExpectedValue, PublicImportPrivateField); + Assert.Equal(PublicComponentWithPrivateExports.PrivatePropertyExpectedValue, PublicImportPrivateProperty); + Assert.Equal(PublicComponentWithPrivateExports.PrivateMethodExpectedValue, PublicImportPrivateMethod()); + Assert.Equal(PublicComponentWithPrivateExports.PrivateNestedClassExpectedValue, PublicImportPrivateNestedClass.GetString()); + } + } + + [Export] + internal class InternalImportsExpectingPrivateExports + { + [Import("PrivateField")] + internal string InternalImportPrivateField { get; set; } + [Import("PrivateProperty")] + internal string InternalImportPrivateProperty { get; set; } + [Import("PrivateDelegate")] + internal Func InternalImportPrivateMethod { get; set; } + [Import("PrivateIGetString")] + internal IGetString InternalImportPrivateNestedClass { get; set; } + + public void VerifyIsBound() + { + Assert.Equal(PublicComponentWithPrivateExports.PrivateFieldExpectedValue, InternalImportPrivateField); + Assert.Equal(PublicComponentWithPrivateExports.PrivatePropertyExpectedValue, InternalImportPrivateProperty); + Assert.Equal(PublicComponentWithPrivateExports.PrivateMethodExpectedValue, InternalImportPrivateMethod()); + Assert.Equal(PublicComponentWithPrivateExports.PrivateNestedClassExpectedValue, InternalImportPrivateNestedClass.GetString()); + } + } + + public class TestContractType + { + public const string TestContractName = "TestContractName"; + } + + public class TestContractTypeWithoutName { } + + public class TypeDerivingFromTestContractType : TestContractType { } + + [Export(typeof(TestContractType))] + public class ExportedTypeWithContractType { } + + public class TypeDerivingFromExportedTypeWithContractType : ExportedTypeWithContractType { } + + [Export("ImportDefaultFunctions")] + public class ImportDefaultFunctions + { + [Import("FunctionWith0Args")] + public Func MyFunction0; + + [Import("FunctionWith1Arg")] + public Func MyFunction1; + + [Import("FunctionWith2Args")] + public Func MyFunction2; + + [Import("FunctionWith3Args")] + public Func MyFunction3; + + [Import("FunctionWith4Args")] + public Func MyFunction4; + + [Import("ActionWith0Args")] + public Action MyAction0; + + [Import("ActionWith1Arg")] + public Action MyAction1; + + [Import("ActionWith2Args")] + public Action MyAction2; + + [Import("ActionWith3Args")] + public Action MyAction3; + + [Import("ActionWith4Args")] + public Action MyAction4; + + public void VerifyIsBound() + { + Assert.Equal(0, MyFunction0.Invoke()); + Assert.Equal(1, MyFunction1.Invoke(1)); + Assert.Equal(3, MyFunction2.Invoke(1, 2)); + Assert.Equal(6, MyFunction3.Invoke(1, 2, 3)); + Assert.Equal(10, MyFunction4.Invoke(1, 2, 3, 4)); + + MyAction0.Invoke(); + MyAction1.Invoke(1); + MyAction2.Invoke(1, 2); + MyAction3.Invoke(1, 2, 3); + MyAction4.Invoke(1, 2, 3, 4); + } + } + + public class ExportDefaultFunctions + { + [Export("FunctionWith0Args")] + public int MyFunction0() + { + return 0; + } + + [Export("FunctionWith1Arg")] + public int MyFunction1(int i1) + { + return i1; + } + + [Export("FunctionWith2Args")] + public int MyFunction2(int i1, int i2) + { + return i1 + i2; + } + + [Export("FunctionWith3Args")] + public int MyFunction3(int i1, int i2, int i3) + { + return i1 + i2 + i3; + } + + [Export("FunctionWith4Args")] + public int MyFunction4(int i1, int i2, int i3, int i4) + { + return i1 + i2 + i3 + i4; + } + + [Export("ActionWith0Args")] + public void MyAction0() + { + } + + [Export("ActionWith1Arg")] + public void MyAction1(int i1) + { + Assert.Equal(i1, 1); + } + + [Export("ActionWith2Args")] + public void MyAction2(int i1, int i2) + { + Assert.Equal(i1, 1); + Assert.Equal(i2, 2); + } + + [Export("ActionWith3Args")] + public void MyAction3(int i1, int i2, int i3) + { + Assert.Equal(i1, 1); + Assert.Equal(i2, 2); + Assert.Equal(i3, 3); + } + + [Export("ActionWith4Args")] + public void MyAction4(int i1, int i2, int i3, int i4) + { + Assert.Equal(i1, 1); + Assert.Equal(i2, 2); + Assert.Equal(i3, 3); + Assert.Equal(i4, 4); + } + } + + [Export] + public class CatalogComponentTest + { + } + + [Export] + [PartNotDiscoverable] + public class CatalogComponentTestNonComponentPart + { + } + + public interface ICatalogComponentTest + { + } + + [Export(typeof(ICatalogComponentTest))] + public class CatalogComponentInterfaceTest1 : ICatalogComponentTest + { + } + + public class CatalogComponentInterfaceTest2 + { + [Export] + public ICatalogComponentTest ExportedInterface + { + get { return new CatalogComponentInterfaceTest1(); } + } + } + + public static class StaticExportClass + { + [Export("StaticString")] + public static string StaticString { get { return "StaticString"; } } + } + + [Export] + public class DisposableExportClass : IDisposable + { + public bool IsDisposed { get; set; } + public void Dispose() + { + Assert.False(IsDisposed); + IsDisposed = true; + } + } + + public interface IServiceView + { + int GetSomeInt(); + } + + [Export("service1")] + public class Service + { + public int GetSomeInt() + { + return 5; + } + } + + public class Client + { + private IServiceView mySerivce; + + [Import("service1")] + public IServiceView MyService + { + get { return mySerivce; } + set { mySerivce = value; } + } + public int GetSomeValue() + { + return MyService.GetSomeInt() * 2; + } + } + + [Export] + public class TrivialExporter + { + public bool done = false; + } + + [Export] + public class TrivialImporter : IPartImportsSatisfiedNotification + { + [Import] + public TrivialExporter checker; + + public void OnImportsSatisfied() + { + checker.done = true; + } + } + + [Export] + public class UnnamedImportAndExport + { + [Import] + public IUnnamedExport ImportedValue; + } + + [Export] + public class StaticExport + { + } + + [Export] + [PartCreationPolicy(CreationPolicy.NonShared)] + public class NonStaticExport + { + } + + public interface IUnnamedExport + { + } + + [Export(typeof(IUnnamedExport))] + public class UnnamedExport : IUnnamedExport + { + } + + public interface IExportableTest + { + string Var1 { get; } + } + + [AttributeUsage(AttributeTargets.All)] + [MetadataAttribute] + public class ExportableTestAttribute : Attribute + { + private string var1; + + public string Var1 + { + get { return var1; } + set { var1 = value; } + } + } + + [AttributeUsage(AttributeTargets.All)] + [MetadataAttribute] + public class MetadataWithCollectionPropertyAttribute : Attribute + { + private List values; + + public IEnumerable Values + { + get { return values; } + } + + public MetadataWithCollectionPropertyAttribute(params string[] values) + { + this.values = new List(values); + } + } + + [Export] + [MetadataWithCollectionProperty("One", "two", "3")] + [PartNotDiscoverable] + public class ComponentWithCollectionProperty + { + } + + public interface ICollectionOfStrings + { + IEnumerable Values { get; } + } + + public class SubtractProvider + { + [Export("One")] + public int One = 1; + + [Export("Two")] + public int Two { get { return 2; } } + + [Export("Add")] + [ExportableTest(Var1 = "sub")] + public Func Subtract = (x, y) => x - y; + } + + public class RealAddProvider + { + [Export("One")] + public int One = 1; + + [Export("Two")] + public int Two { get { return 2; } } + + [Export("Add")] + [ExportMetadata("Var1", "add")] + public int Add(int x, int y) + { + return x + y; + } + } + + public class Consumer + { + [Import("One")] + public int a; + [Import("Two")] + public int b; + [Import("Add")] + public Func op; + [Import("Add", AllowDefault = true)] + public Lazy> opInfo; + } + + public class ConsumerOfMultiple + { + [ImportMany("Add")] + public IEnumerable>> opInfo; + } + + public interface IStrongValueMetadata + { + int value { get; set; } + } + + public class UntypedExportImporter + { + [Import("untyped")] + public Export Export; + } + + public class UntypedExportsImporter + { + [ImportMany("untyped")] + public IEnumerable Exports; + } + + public class DerivedExport : Export + { + } + + public class DerivedExportImporter + { + [Import("derived")] + public DerivedExport Export; + + } + + public class DerivedExportsImporter + { + [ImportMany("derived")] + public IEnumerable Exports; + } + + [Export] + public class NotSoUniqueName + { + public int MyIntProperty { get { return 23; } } + } + + public class NotSoUniqueName2 + { + [Export] + public class NotSoUniqueName + { + public virtual string MyStringProperty { get { return "MyStringProperty"; } } + } + } + + [Export] + public class MyExport + { + } + + [Export] + public class MySharedPartExport + { + [Import("Value", AllowRecomposition = true)] + public int Value { get; set; } + } + + [Export] + [PartCreationPolicy(CreationPolicy.NonShared)] + public class MyNonSharedPartExport + { + [Import("Value")] + public int Value { get; set; } + } + + public class ExportThatCantBeActivated + { + [Export("ExportMyString")] + public string MyString { get { return "MyString"; } } + + [Import("ContractThatShouldNotexist")] + public string MissingImport { get; set; } + } + + public class GenericContract1 + { + public class GenericContract2 + { + public class GenericContract3 + { + } + } + } + + public class GenericContract4 + { + public class GenericContract5 + { + public class GenericContract6 + { + } + } + } + + public class OuterClassWithGenericNested + { + public class GenericNested + { + } + } + + public class GenericContract7 : + GenericContract4.GenericContract5.GenericContract6 + { } + + public class GenericContract8 : GenericContract1.GenericContract2.GenericContract3 { } + + public class NestedParent + { + public class NestedChild { } + } + + public class ImporterOfExporterNotifyPropertyChanged + { + [Import("value", AllowRecomposition = true)] + public int Value; + + [Import("secondValue")] + public int SecondValue; + } + + public class ExporterNotifyPropertyChanged : System.ComponentModel.INotifyPropertyChanged + { + int theValue = 42; + + [Export("value")] + public int Value + { + get { return theValue; } + set + { + theValue = value; + FirePropertyChange("Value"); + } + } + + [Export("secondValue")] + public int second = 2; + + public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; + + public void FirePropertyChange(string prop) + { + if (PropertyChanged != null) + { + PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(prop)); + } + } + } + + [Export] + [PartCreationPolicy(CreationPolicy.NonShared)] + public class DirectCycleNonSharedPart + { + [Import] + public DirectCycleNonSharedPart NonSharedPart { get; set; } + } + + [Export] + [PartCreationPolicy(CreationPolicy.NonShared)] + public class CycleNonSharedPart1 + { + [Import] + public CycleNonSharedPart2 NonSharedPart2 { get; set; } + } + + [Export] + [PartCreationPolicy(CreationPolicy.NonShared)] + public class CycleNonSharedPart2 + { + [Import] + public CycleNonSharedPart1 NonSharedPart1 { get; set; } + } + + [Export] + public class CycleNonSharedPart + { + [Import] + public CycleNonSharedPart1 NonSharedPart1 { get; set; } + } + + [Export] + public class CycleSharedPart1 + { + [Import] + public CycleSharedPart2 SharedPart2 { get; set; } + } + + [Export] + public class CycleSharedPart2 + { + [Import] + public CycleSharedPart1 SharedPart2 { get; set; } + } + + [Export] + public class CycleSharedPart + { + [Import] + public CycleSharedPart1 SharedPart1 { get; set; } + + [Import] + public CycleSharedPart2 SharedPart2 { get; set; } + } + + [Export] + [PartCreationPolicy(CreationPolicy.NonShared)] + public class NoCycleNonSharedPart + { + [Import] + public SharedPartWithNoCycleNonSharedPart SharedPart { get; set; } + } + + [Export] + public class SharedPartWithNoCycleNonSharedPart + { + [Import] + public NoCycleNonSharedPart NonSharedPart { get; set; } + } + + [Export] + [PartCreationPolicy(CreationPolicy.NonShared)] + public class CycleWithSharedPartAndNonSharedPart + { + [Import] + public SharedPartWithNoCycleNonSharedPart BeforeNonSharedPart { get; set; } + + [Import] + public CycleWithNonSharedPartOnly NonSharedPart { get; set; } + + [Import] + public SharedPartWithNoCycleNonSharedPart SharedPart { get; set; } + } + + [Export] + [PartCreationPolicy(CreationPolicy.NonShared)] + public class CycleWithNonSharedPartOnly + { + [Import] + public CycleWithSharedPartAndNonSharedPart NonSharedPart { get; set; } + + } + + [InheritedExport] + public class ExportWithGenericParameter + { + } + + public class ExportWithGenericParameterOfInt + { + [Export] + public ExportWithGenericParameter GenericExport { get { return new ExportWithGenericParameter(); } } + } + + [Export] + public static class StaticExportWithGenericParameter + { + } + + [Export] + public class ExportWhichInheritsFromGeneric : ExportWithGenericParameter + { + + } + + [Export] + public class ExportWithExceptionDuringConstruction + { + public ExportWithExceptionDuringConstruction() + { + throw new NotImplementedException(); + } + } + + [Export] + public class SimpleConstructorInjectedObject + { + [ImportingConstructor] + public SimpleConstructorInjectedObject([Import("CISimpleValue")]int value) + { + CISimpleValue = value; + } + + public int CISimpleValue { get; private set; } + } + + [Export] + public class ClassWithNoMarkedOrDefaultConstructor + { + public ClassWithNoMarkedOrDefaultConstructor(int blah) { } + } + + public class ClassWhichOnlyHasImportingConstructorWithOneArgument + { + [ImportingConstructor] + public ClassWhichOnlyHasImportingConstructorWithOneArgument(int blah) { } + } + + public class ClassWhichOnlyHasImportingConstructor + { + [ImportingConstructor] + public ClassWhichOnlyHasImportingConstructor() { } + } + + public class ClassWhichOnlyHasDefaultConstructor + { + public ClassWhichOnlyHasDefaultConstructor() { } + } + + [Export] + public class BaseExportForImportingConstructors + { + + } + + public class ClassWithOnlyHasImportingConstructorButInherits : BaseExportForImportingConstructors + { + [ImportingConstructor] + public ClassWithOnlyHasImportingConstructorButInherits(int blah) { } + } + + public class ClassWithOnlyHasMultipleImportingConstructorButInherits : BaseExportForImportingConstructors + { + [ImportingConstructor] + public ClassWithOnlyHasMultipleImportingConstructorButInherits(int blah) { } + + [ImportingConstructor] + public ClassWithOnlyHasMultipleImportingConstructorButInherits(string blah) { } + } + + [Export] + public class ClassWithMultipleMarkedConstructors + { + [ImportingConstructor] + public ClassWithMultipleMarkedConstructors(int i) { } + + [ImportingConstructor] + public ClassWithMultipleMarkedConstructors(string s) { } + + public ClassWithMultipleMarkedConstructors() { } + } + + [Export] + public class ClassWithOneMarkedAndOneDefaultConstructor + { + [ImportingConstructor] + public ClassWithOneMarkedAndOneDefaultConstructor(int i) { } + + public ClassWithOneMarkedAndOneDefaultConstructor() { } + } + + [Export] + public class ClassWithTwoZeroParameterConstructors + { + public ClassWithTwoZeroParameterConstructors() { } + + static ClassWithTwoZeroParameterConstructors() { } + } + + [Export] + public class ExceptionDuringINotifyImport : IPartImportsSatisfiedNotification + { + [ImportMany("Value")] + public IEnumerable ValuesJustUsedToGetImportCompletedCalled { get; set; } + + public void OnImportsSatisfied() + { + throw new NotImplementedException(); + } + } + + [Export] + public class ClassWithOptionalPostImport + { + [Import(AllowDefault = true)] + public IFormattable Formatter { get; set; } + } + + [Export] + public class ClassWithOptionalPreImport + { + [ImportingConstructor] + public ClassWithOptionalPreImport([Import(AllowDefault = true)] IFormattable formatter) + { + this.Formatter = formatter; + } + + public IFormattable Formatter { get; private set; } + } + + [MetadataAttribute] + public class ThisIsMyMetadataMetadataAttribute : Attribute + { + public string Argument1 { get; set; } + public int Argument2 { get; set; } + public double Argument3 { get; set; } + public string Argument4 { get; set; } + + public ThisIsMyMetadataMetadataAttribute() + { + } + + public ThisIsMyMetadataMetadataAttribute(string Argument1, int Argument2) + { + this.Argument1 = Argument1; + this.Argument2 = Argument2; + } + } + + [Export] + [ThisIsMyMetadataMetadataAttribute("One", 2, Argument3 = 3.0)] + public class ExportedTypeWithConcreteMetadata + { + } + + public class Int32CollectionImporter + { + + public Int32CollectionImporter() + { + Values = new Collection(); + } + + [ImportMany("Value")] + public Collection Values { get; private set; } + } + + [PartNotDiscoverable] + public class Int32Exporter + { + + public Int32Exporter(int value) + { + Value = value; + } + + [Export("Value")] + public int Value { get; set; } + + } + + [PartNotDiscoverable] + public class Int32ExporterInternal + { + + public Int32ExporterInternal(int value) + { + Value = value; + } + + [Export("Value")] + public int Value { get; set; } + + } + + public class Int32Importer + { + + public Int32Importer() + { + } + + [Import("Value", AllowRecomposition = true)] + public int Value { get; set; } + } + + public class Int32ImporterInternal + { + + public Int32ImporterInternal() + { + } + + [Import("Value")] + public int Value { get; set; } + } + +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ScopeExportFactoryTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ScopeExportFactoryTests.cs new file mode 100644 index 0000000000..15e8c016a8 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ScopeExportFactoryTests.cs @@ -0,0 +1,772 @@ +// 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.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class ScopeExportFactoryTests + { + public interface IFooContract + { + } + + public interface IFooMetadata + { + string Name { get; } + } + + public interface IBarContract + { + IFooContract CreateFoo(); + } + + public interface IBlahContract + { + } + + [Export(typeof(IFooContract))] + [ExportMetadata("Name", "Foo")] + public class FooImpl : IFooContract + { + } + + [Export(typeof(IFooContract))] + [ExportMetadata("Name", "Foo")] + public class Foo2Impl : IFooContract + { + } + + [Export(typeof(IFooContract))] + public class Foo3Impl : IFooContract + { + [Import] + public IBlahContract Blah { get; set; } + } + + [Export(typeof(IFooContract))] + public class Foo4Impl : IFooContract + { + [Import] + public ExportFactory Blah { get; set; } + } + + [Export(typeof(IBlahContract))] + public class BlahImpl : IBlahContract + { + [Import] + public IBlahContract Blah { get; set; } + } + + [Export(typeof(IBarContract))] + public class BarImpl : IBarContract + { + [Import] + public ExportFactory FooFactory { get; set; } + + public IFooContract CreateFoo() + { + var efv = this.FooFactory.CreateExport(); + var value = efv.Value; + efv.Dispose(); + return value; + } + } + + [Export(typeof(IBarContract))] + public class BarWithMany : IBarContract + { + [ImportMany] + public ExportFactory[] FooFactories { get; set; } + + public IFooContract CreateFoo() + { + var efv = this.FooFactories[0].CreateExport(); + var value = efv.Value; + efv.Dispose(); + return value; + } + } + + [Export(typeof(IBarContract))] + public class BarImplWithMetadata : IBarContract + { + [Import] + public ExportFactory FooFactory { get; set; } + + public IFooContract CreateFoo() + { + Assert.Equal("Foo", this.FooFactory.Metadata.Name); + var efv = this.FooFactory.CreateExport(); + var value = efv.Value; + efv.Dispose(); + return value; + } + } + + [Fact] + public void SimpleChain() + { + var parentCatalog = new TypeCatalog(typeof(BarImpl)); + var childCatalog = new TypeCatalog(typeof(FooImpl)); + + var scope = parentCatalog.AsScope(childCatalog.AsScope()); + var container = new CompositionContainer(scope); + + var bar = container.GetExportedValue(); + Assert.NotNull(bar); + + var foo1 = bar.CreateFoo(); + Assert.NotNull(foo1); + Assert.True(foo1 is FooImpl); + + var foo2 = bar.CreateFoo(); + Assert.NotNull(foo1); + Assert.True(foo2 is FooImpl); + + Assert.NotEqual(foo1, foo2); + } + + [Fact] + public void SimpleChainWithTwoChildren() + { + var parentCatalog = new TypeCatalog(typeof(BarWithMany)); + var childCatalog1 = new TypeCatalog(typeof(FooImpl)); + var childCatalog2 = new TypeCatalog(typeof(Foo2Impl)); + + var scope = parentCatalog.AsScope(childCatalog1.AsScope(), childCatalog2.AsScope()); + var container = new CompositionContainer(scope); + + var bar = container.GetExportedValue() as BarWithMany; + Assert.NotNull(bar); + + Assert.Equal(2, bar.FooFactories.Length); + + IFooContract foo1 = null; + using (var efFoo1 = bar.FooFactories[0].CreateExport()) + { + foo1 = efFoo1.Value; + } + + IFooContract foo2 = null; + using (var efFoo2 = bar.FooFactories[1].CreateExport()) + { + foo2 = efFoo2.Value; + } + + Assert.True(((foo1 is FooImpl) && (foo2 is Foo2Impl)) || ((foo2 is FooImpl) && (foo1 is Foo2Impl))); + } + + [Fact] + public void SimpleChainWithMetadata() + { + var parentCatalog = new TypeCatalog(typeof(BarImplWithMetadata)); + var childCatalog = new TypeCatalog(typeof(FooImpl)); + + var scope = parentCatalog.AsScope(childCatalog.AsScope()); + var container = new CompositionContainer(scope); + + var bar = container.GetExportedValue(); + Assert.NotNull(bar); + + var foo1 = bar.CreateFoo(); + Assert.NotNull(foo1); + Assert.True(foo1 is FooImpl); + + var foo2 = bar.CreateFoo(); + Assert.NotNull(foo1); + Assert.True(foo2 is FooImpl); + + Assert.NotEqual(foo1, foo2); + } + + [Fact] + public void SimpleChainWithLowerLoop() + { + var parentCatalog = new TypeCatalog(typeof(BarImpl)); + var childCatalog = new TypeCatalog(typeof(Foo3Impl), typeof(BlahImpl)); + + var scope = parentCatalog.AsScope(childCatalog.AsScope()); + var container = new CompositionContainer(scope); + + var bar = container.GetExportedValue(); + Assert.NotNull(bar); + + var foo1 = bar.CreateFoo(); + Assert.NotNull(foo1); + Assert.True(foo1 is Foo3Impl); + + var foo2 = bar.CreateFoo(); + Assert.NotNull(foo1); + Assert.True(foo2 is Foo3Impl); + + Assert.NotEqual(foo1, foo2); + } + + [Fact] + public void SimpleChainWithCrossLoop() + { + var parentCatalog = new TypeCatalog(typeof(BarImpl)); + var childCatalog = new TypeCatalog(typeof(Foo4Impl)); + + var scope = parentCatalog.AsScope(childCatalog.AsScope()); + var container = new CompositionContainer(scope); + + var bar = container.GetExportedValue(); + Assert.NotNull(bar); + + var foo1 = bar.CreateFoo(); + Assert.NotNull(foo1); + Assert.True(foo1 is Foo4Impl); + + var foo2 = bar.CreateFoo(); + Assert.NotNull(foo1); + Assert.True(foo2 is Foo4Impl); + + Assert.NotEqual(foo1, foo2); + } + + [Fact] + public void SimpleChainWithLowerLoopRejection() + { + var parentCatalog = new TypeCatalog(typeof(BarImpl)); + var childCatalog = new TypeCatalog(typeof(Foo3Impl)); + + var scope = parentCatalog.AsScope(childCatalog.AsScope()); + var container = new CompositionContainer(scope); + + var bar = container.GetExportedValueOrDefault(); + Assert.Null(bar); + } + + [Fact] + public void ExportFactoryCausesRejectionBasedOnContract() + { + var parentCatalog = new TypeCatalog(typeof(BarImpl)); + var childCatalog = new TypeCatalog(typeof(BarImpl)); + + var scope = parentCatalog.AsScope(childCatalog.AsScope()); + var container = new CompositionContainer(scope); + + var bar = container.GetExportedValueOrDefault(); + Assert.Null(bar); + } + + [Fact] + public void ExportFactoryCausesRejectionBasedOnCardinality() + { + var parentCatalog = new TypeCatalog(typeof(BarImpl)); + var childCatalog = new TypeCatalog(typeof(FooImpl), typeof(Foo2Impl)); + + var scope = parentCatalog.AsScope(childCatalog.AsScope()); + var container = new CompositionContainer(scope); + + var bar = container.GetExportedValueOrDefault(); + Assert.Null(bar); + } + } + + public class ScopeExportFactoryWithPublicSurface + { + [Export] public class ClassA { } + + [Export] public class ClassB { } + + [Export] public class ClassC { } + + [Export] + public class ClassRoot + { + [ImportAttribute] + public ExportFactory classA; + + [ImportAttribute] + public ExportFactory classB; + + [ImportAttribute] + public ExportFactory classC; + } + + [Fact] + public void FilteredScopeFactoryOfTM_ShouldSucceed() + { + var c1 = new TypeCatalog(typeof(ClassRoot), typeof(ClassA)); + var c2 = new TypeCatalog(typeof(ClassA), typeof(ClassB), typeof(ClassC)); + var c3 = new TypeCatalog(typeof(ClassA), typeof(ClassB), typeof(ClassC)); + var c4 = new TypeCatalog(typeof(ClassA), typeof(ClassB), typeof(ClassC)); + var sd = c1.AsScope(c2.AsScopeWithPublicSurface(), + c3.AsScopeWithPublicSurface(), + c4.AsScopeWithPublicSurface()); + + var container = new CompositionContainer(sd); + + var fromRoot = container.GetExportedValue(); + var a = fromRoot.classA.CreateExport().Value; + var b = fromRoot.classB.CreateExport().Value; + var c = fromRoot.classC.CreateExport().Value; + + } + } + + public class ScopeFactoryAutoResolveFromAncestorScope + { + [Export] public class Root { } + + [Export] public class Child { } + + [Export] + public class ClassRoot + { + [ImportAttribute] + public ExportFactory classA; + + [ImportAttribute] + public ClassA localClassA; + } + + [Export] + public class ClassA + { + [Import] + public ICompositionService CompositionService; + + [ImportAttribute] + public Root classRoot; + + public int InstanceValue; + } + + public class ImportA + { + [Import] + public ClassA classA; + } + + [Fact] + public void ScopeFactoryAutoResolveFromAncestorScopeShouldSucceed() + { + var c1 = new TypeCatalog(typeof(ClassRoot), typeof(ClassA), typeof(Root)); + var c2 = new TypeCatalog(typeof(ClassRoot), typeof(ClassA), typeof(Child)); + var sd = c1.AsScope(c2.AsScope()); + + var container = new CompositionContainer(sd, CompositionOptions.ExportCompositionService); + + var fromRoot = container.GetExportedValue(); + var a1 = fromRoot.classA.CreateExport().Value; + var a2 = fromRoot.classA.CreateExport().Value; + fromRoot.localClassA.InstanceValue = 101; + a1.InstanceValue = 202; + a2.InstanceValue = 303; + + Assert.NotEqual(a1.InstanceValue, a2.InstanceValue); + Assert.NotNull(fromRoot.localClassA.classRoot); + Assert.NotNull(a1.classRoot); + Assert.NotNull(a2.classRoot); + } + } + + public class DeeplyNestedCatalog + { + [Export] + public class ClassRoot + { + [ImportAttribute] + public ExportFactory classA; + } + + [Export] + public class ClassA + { + [ImportAttribute] + public ExportFactory classB; + } + + [Export] + public class ClassB + { + [ImportAttribute] + public ExportFactory classC; + + public int InstanceValue; + } + + [Export] + public class ClassC + { + [ImportAttribute] + public ExportFactory classD; + } + + [Export] + public class ClassD + { + } + + [Fact] + public void DeeplyNestedCatalogPartitionedCatalog_ShouldWork() + { + var cat1 = new TypeCatalog(typeof(ClassRoot)); + var cat2 = new TypeCatalog(typeof(ClassA)); + var cat3 = new TypeCatalog(typeof(ClassB)); + var cat4 = new TypeCatalog(typeof(ClassC)); + var cat5 = new TypeCatalog(typeof(ClassD)); + var sd = cat1.AsScope(cat2.AsScope(cat3.AsScope(cat4.AsScope(cat5.AsScope())))); + + var container = new CompositionContainer(sd); + + var fromRoot = container.GetExportedValue(); + + var a1 = fromRoot.classA.CreateExport().Value; + var b1 = a1.classB.CreateExport().Value; + var c1 = b1.classC.CreateExport().Value; + var d1 = c1.classD.CreateExport().Value; + } + + [Fact] + public void DeeplyNestedCatalogOverlappedCatalog_ShouldWork() + { + var cat1 = new TypeCatalog(typeof(ClassRoot), typeof(ClassA), typeof(ClassB), typeof(ClassC), typeof(ClassD)); + var cat2 = cat1; + var cat3 = cat1; + var cat4 = cat1; + var cat5 = cat1; + var sd = cat1.AsScope(cat2.AsScope(cat3.AsScope(cat4.AsScope(cat5.AsScope())))); + + var container = new CompositionContainer(sd); + + var fromRoot = container.GetExportedValue(); + + var a1 = fromRoot.classA.CreateExport().Value; + var b1 = a1.classB.CreateExport().Value; + var c1 = b1.classC.CreateExport().Value; + var d1 = c1.classD.CreateExport().Value; + } + } + + public class LocalSharedNonLocalInSameContainer + { + [Export] + public class ClassRoot + { + [ImportAttribute] + public ExportFactory classA; + + [ImportAttribute] + public ClassXXXX xxxx; + } + + [Export] + public class ClassA + { + [ImportAttribute] + public ExportFactory classB; + + [ImportAttribute] + public ClassXXXX xxxx; + } + + [Export] + public class ClassB + { + [ImportAttribute] + public ExportFactory classC; + + [ImportAttribute] + public ClassXXXX xxxx; + } + + [Export] + public class ClassC + { + [ImportAttribute(Source = ImportSource.NonLocal)] + public ClassXXXX xxxx; + + [Import] + public ClassD classD; + } + + [Export] + public class ClassD + { + [ImportAttribute] + public ClassXXXX xxxx; + } + + [Export] + public class ClassXXXX + { + public int InstanceValue; + } + + [Fact] + public void LocalSharedNonLocalInSameContainer_ShouldSucceed() + { + var cat1 = new TypeCatalog(typeof(ClassRoot), typeof(ClassXXXX)); + var cat2 = new TypeCatalog(typeof(ClassA)); + var cat3 = new TypeCatalog(typeof(ClassB)); + var cat4 = new TypeCatalog(typeof(ClassC), typeof(ClassD), typeof(ClassXXXX)); + var sd = cat1.AsScope(cat2.AsScope(cat3.AsScope(cat4.AsScope()))); + + var container = new CompositionContainer(sd); + + var fromRoot = container.GetExportedValue(); + var a1 = fromRoot.classA.CreateExport().Value; + fromRoot.xxxx.InstanceValue = 16; + var b1 = a1.classB.CreateExport().Value; + var c1 = b1.classC.CreateExport().Value; + + Assert.Equal(16, fromRoot.xxxx.InstanceValue); + Assert.Equal(16, a1.xxxx.InstanceValue); + Assert.Equal(16, b1.xxxx.InstanceValue); + Assert.Equal(16, c1.xxxx.InstanceValue); + Assert.Equal(0, c1.classD.xxxx.InstanceValue); + + c1.xxxx.InstanceValue = 8; + + Assert.Equal(8, fromRoot.xxxx.InstanceValue); + Assert.Equal(8, a1.xxxx.InstanceValue); + Assert.Equal(8, b1.xxxx.InstanceValue); + Assert.Equal(8, c1.xxxx.InstanceValue); + Assert.Equal(0, c1.classD.xxxx.InstanceValue); + + c1.classD.xxxx.InstanceValue = 2; + Assert.Equal(8, fromRoot.xxxx.InstanceValue); + Assert.Equal(8, a1.xxxx.InstanceValue); + Assert.Equal(8, b1.xxxx.InstanceValue); + Assert.Equal(8, c1.xxxx.InstanceValue); + Assert.Equal(2, c1.classD.xxxx.InstanceValue); + + } + } + + public class ScopeBridgingAdaptersConstructorInjection + { + [Export] + public class ClassRoot + { + [ImportAttribute] + public ExportFactory classC; + + [ImportAttribute] + public ClassXXXX xxxx; + } + + [Export] + public class ClassC + { + [ImportingConstructor] + public ClassC([ImportAttribute(RequiredCreationPolicy = CreationPolicy.NonShared, Source = ImportSource.NonLocal)]ClassXXXX xxxx) + { + this.xxxx = xxxx; + } + + [Export] + public ClassXXXX xxxx; + + [Import] + public ClassD classD; + } + + [Export] + public class ClassD + { + [Import] + public ClassXXXX xxxx; + } + + [Export] + public class ClassXXXX + { + public int InstanceValue; + } + + [Fact] + public void ScopeBridgingAdapters_ShouldSucceed() + { + var cat1 = new TypeCatalog(typeof(ClassRoot), typeof(ClassXXXX)); + var cat2 = new TypeCatalog(typeof(ClassC), typeof(ClassD)); + var sd = cat1.AsScope(cat2.AsScope()); + var container = new CompositionContainer(sd); + + var fromRoot = container.GetExportedValue(); + var c1 = fromRoot.classC.CreateExport().Value; + var c2 = fromRoot.classC.CreateExport().Value; + var c3 = fromRoot.classC.CreateExport().Value; + var c4 = fromRoot.classC.CreateExport().Value; + var c5 = fromRoot.classC.CreateExport().Value; + + Assert.Equal(0, fromRoot.xxxx.InstanceValue); + Assert.Equal(0, c1.xxxx.InstanceValue); + Assert.Equal(0, c1.classD.xxxx.InstanceValue); + Assert.Equal(0, c2.xxxx.InstanceValue); + Assert.Equal(0, c2.classD.xxxx.InstanceValue); + Assert.Equal(0, c3.xxxx.InstanceValue); + Assert.Equal(0, c3.classD.xxxx.InstanceValue); + Assert.Equal(0, c4.xxxx.InstanceValue); + Assert.Equal(0, c4.classD.xxxx.InstanceValue); + Assert.Equal(0, c5.xxxx.InstanceValue); + Assert.Equal(0, c5.classD.xxxx.InstanceValue); + + c1.xxxx.InstanceValue = 1; + c2.xxxx.InstanceValue = 2; + c3.xxxx.InstanceValue = 3; + c4.xxxx.InstanceValue = 4; + c5.xxxx.InstanceValue = 5; + + Assert.Equal(0, fromRoot.xxxx.InstanceValue); + Assert.Equal(1, c1.xxxx.InstanceValue); + Assert.Equal(1, c1.classD.xxxx.InstanceValue); + Assert.Equal(2, c2.xxxx.InstanceValue); + Assert.Equal(2, c2.classD.xxxx.InstanceValue); + Assert.Equal(3, c3.xxxx.InstanceValue); + Assert.Equal(3, c3.classD.xxxx.InstanceValue); + Assert.Equal(4, c4.xxxx.InstanceValue); + Assert.Equal(4, c4.classD.xxxx.InstanceValue); + Assert.Equal(5, c5.xxxx.InstanceValue); + Assert.Equal(5, c5.classD.xxxx.InstanceValue); + } + } + + public class ScopeBridgingAdaptersImportExportProperty + { + [Export] + public class ClassRoot + { + [ImportAttribute] + public ExportFactory classC; + + [ImportAttribute] + public ClassXXXX xxxx; + } + + [Export] + public class ClassC + { + [Export] + [ImportAttribute(RequiredCreationPolicy = CreationPolicy.NonShared, Source = ImportSource.NonLocal)] + public ClassXXXX xxxx; + + [Import] + public ClassD classD; + } + + [Export] + public class ClassD + { + [Import] + public ClassXXXX xxxx; + } + + [Export] + public class ClassXXXX + { + public int InstanceValue; + } + + [Fact] + public void ScopeBridgingAdaptersImportExportProperty_ShouldSucceed() + { + var cat1 = new TypeCatalog(typeof(ClassRoot), typeof(ClassXXXX)); + var cat2 = new TypeCatalog(typeof(ClassC), typeof(ClassD)); + var sd = cat1.AsScope(cat2.AsScope()); + var container = new CompositionContainer(sd); + + var fromRoot = container.GetExportedValue(); + var c1 = fromRoot.classC.CreateExport().Value; + var c2 = fromRoot.classC.CreateExport().Value; + var c3 = fromRoot.classC.CreateExport().Value; + var c4 = fromRoot.classC.CreateExport().Value; + var c5 = fromRoot.classC.CreateExport().Value; + + Assert.Equal(0, fromRoot.xxxx.InstanceValue); + Assert.Equal(0, c1.xxxx.InstanceValue); + Assert.Equal(0, c1.classD.xxxx.InstanceValue); + Assert.Equal(0, c2.xxxx.InstanceValue); + Assert.Equal(0, c2.classD.xxxx.InstanceValue); + Assert.Equal(0, c3.xxxx.InstanceValue); + Assert.Equal(0, c3.classD.xxxx.InstanceValue); + Assert.Equal(0, c4.xxxx.InstanceValue); + Assert.Equal(0, c4.classD.xxxx.InstanceValue); + Assert.Equal(0, c5.xxxx.InstanceValue); + Assert.Equal(0, c5.classD.xxxx.InstanceValue); + + c1.xxxx.InstanceValue = 1; + c2.xxxx.InstanceValue = 2; + c3.xxxx.InstanceValue = 3; + c4.xxxx.InstanceValue = 4; + c5.xxxx.InstanceValue = 5; + + Assert.Equal(0, fromRoot.xxxx.InstanceValue); + Assert.Equal(1, c1.xxxx.InstanceValue); + Assert.Equal(1, c1.classD.xxxx.InstanceValue); + Assert.Equal(2, c2.xxxx.InstanceValue); + Assert.Equal(2, c2.classD.xxxx.InstanceValue); + Assert.Equal(3, c3.xxxx.InstanceValue); + Assert.Equal(3, c3.classD.xxxx.InstanceValue); + Assert.Equal(4, c4.xxxx.InstanceValue); + Assert.Equal(4, c4.classD.xxxx.InstanceValue); + Assert.Equal(5, c5.xxxx.InstanceValue); + Assert.Equal(5, c5.classD.xxxx.InstanceValue); + } + } + + public class SelfExportFromExportFactory + { + [Export] + public class ClassRoot + { + [ImportAttribute] + public ExportFactory classA; + } + + [Export] + public class ClassA + { + [ImportAttribute] public ClassB classB; + [ImportAttribute] public ClassC classC; + [ImportAttribute] public ClassD classD; + + public int InstanceValue; + } + + [Export] + public class ClassB + { + [ImportAttribute] public ClassA classA; + } + + [Export] + public class ClassC + { + [ImportAttribute] public ClassA classA; + } + + [Export] + public class ClassD + { + [ImportAttribute] public ClassA classA; + } + + [Fact] + public void SelfExportFromExportFactory_ShouldSucceed() + { + var cat1 = new TypeCatalog(typeof(ClassRoot)); + var cat2 = new TypeCatalog(typeof(ClassA), typeof(ClassB), typeof(ClassC), typeof(ClassD)); + var sd = cat1.AsScope(cat2.AsScope()); + + var container = new CompositionContainer(sd); + + var fromRoot = container.GetExportedValue(); + var a1 = fromRoot.classA.CreateExport().Value; + + a1.InstanceValue = 8; + + Assert.Equal(8, a1.classB.classA.InstanceValue); + Assert.Equal(8, a1.classC.classA.InstanceValue); + Assert.Equal(8, a1.classD.classA.InstanceValue); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ScopedCompositionServicetests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ScopedCompositionServicetests.cs new file mode 100644 index 0000000000..11be8999e5 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ScopedCompositionServicetests.cs @@ -0,0 +1,199 @@ +// 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.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class ScopedCompositionServiceTests + { + [Export] + public class ClassRoot + { + [ImportAttribute] + public ExportFactory classA; + + [ImportAttribute] + public ClassA localClassA; + } + + [Export] + public class ClassA + { + [Import] + public ICompositionService CompositionService; + public int InstanceValue; + } + + public class ImportA + { + [Import] + public ClassA classA; + } + + [Export] + public class FromRoot + { + [Import] + public ExportFactory Required { get; set; } + + [Import] + public ExportFactory Optional { get; set; } + } + + [Export] + public class ClassRequiresICompositionService + { + [Import(AllowDefault=false)] + public ICompositionService CompositionService { get; set; } + } + + [Export] + public class ClassOptionallyImportsICompositionService + { + [Import(AllowDefault=true)] + public ICompositionService CompositionService { get; set; } + } + + [Fact] + public void DontExportICompositionServiceFromRootRequiredImportShouldThrowCompositionException() + { + var rootCatalog = new TypeCatalog(typeof(ClassRequiresICompositionService), typeof(ClassOptionallyImportsICompositionService)); + var container = new CompositionContainer(rootCatalog); + + Assert.Throws(() => + { + var service = container.GetExportedValue(); + Assert.Null(service.CompositionService); + }); + } + + [Fact] + public void DontExportICompositionServiceFromRootOptionalImportShouldSucceed() + { + var rootCatalog = new TypeCatalog(typeof(ClassRequiresICompositionService),typeof(ClassOptionallyImportsICompositionService)); + var container = new CompositionContainer(rootCatalog); + + var service = container.GetExportedValue(); + Assert.Null(service.CompositionService); + } + + [Fact] + public void ExportICompositionServiceFromRootRequiredImportShouldsucceed() + { + var rootCatalog = new TypeCatalog(typeof(ClassRequiresICompositionService),typeof(ClassOptionallyImportsICompositionService)); + var container = new CompositionContainer(rootCatalog, CompositionOptions.ExportCompositionService); + + var service = container.GetExportedValue(); + Assert.NotNull(service.CompositionService); + } + + [Fact] + public void ExportICompositionServiceFromRootOptionalImportShouldSucceed() + { + var rootCatalog = new TypeCatalog(typeof(ClassRequiresICompositionService),typeof(ClassOptionallyImportsICompositionService)); + var container = new CompositionContainer(rootCatalog, CompositionOptions.ExportCompositionService); + + var service = container.GetExportedValue(); + Assert.NotNull(service.CompositionService); + } + + [Fact] + public void DontExportICompositionServiceFromChildImportShouldShouldThrowCompositionException() + { + var rootCatalog = new TypeCatalog( typeof(FromRoot) ); + var childCatalog = new TypeCatalog( typeof(ClassRequiresICompositionService), typeof(ClassOptionallyImportsICompositionService) ); + var scope = rootCatalog.AsScope(childCatalog.AsScope()); + var container = new CompositionContainer(scope); + + Assert.Throws(() => + { + var fromRoot = container.GetExportedValue(); + Assert.Null(fromRoot); + }); + } + + [Fact] + public void ExportICompositionServiceFromChildImportShouldShouldSucceed() + { + var childCatalog = new TypeCatalog( typeof(ClassRequiresICompositionService), typeof(ClassOptionallyImportsICompositionService) ); + var rootCatalog = new TypeCatalog( typeof(FromRoot) ); + var scope = rootCatalog.AsScope(childCatalog.AsScope()); + var container = new CompositionContainer(scope, CompositionOptions.ExportCompositionService); + + var fromRoot = container.GetExportedValue(); + + var requiredService = fromRoot.Required.CreateExport(); + Console.WriteLine("requiredService: {0}", requiredService.Value.CompositionService); + Assert.NotNull(requiredService.Value.CompositionService); + + var optionalService = fromRoot.Optional.CreateExport(); + Console.WriteLine("optionalService: {0}", optionalService.Value.CompositionService); + Assert.NotNull(optionalService.Value.CompositionService); + } + + [Fact] + public void ScopingEndToEndWithCompositionService_MatchingCatalogsShouldSucceed() + { + var c = new TypeCatalog( typeof(ClassRoot), typeof(ClassA) ); + var sd = c.AsScope( c.AsScope() ); + + var container = new CompositionContainer(sd, CompositionOptions.ExportCompositionService); + + var fromRoot = container.GetExportedValue(); + var a1 = fromRoot.classA.CreateExport().Value; + var a2 = fromRoot.classA.CreateExport().Value; + fromRoot.localClassA.InstanceValue = 101; + a1.InstanceValue = 202; + a2.InstanceValue = 303; + + if (a1.InstanceValue == a2.InstanceValue) { throw new Exception("Incorrect sharing, a1 is shared with a2"); } + + var xroot = new ImportA(); + var x1 = new ImportA(); + var x2 = new ImportA(); + + fromRoot.localClassA.CompositionService.SatisfyImportsOnce(xroot); + a1.CompositionService.SatisfyImportsOnce(x1); + a2.CompositionService.SatisfyImportsOnce(x2); + Assert.Equal(xroot.classA.InstanceValue, fromRoot.localClassA.InstanceValue); + Assert.Equal(x1.classA.InstanceValue, a1.InstanceValue); + Assert.Equal(x2.classA.InstanceValue, a2.InstanceValue); + + } + + [Fact] + public void ScopingEndToEndWithCompositionService_PartitionedCatalogsShouldSucceed() + { + var c1 = new TypeCatalog( typeof(ClassRoot), typeof(ClassA) ); + var c2 = new TypeCatalog( typeof(ClassA) ); + var sd = c1.AsScope( c2.AsScope() ); + + var container = new CompositionContainer(sd, CompositionOptions.ExportCompositionService); + + var fromRoot = container.GetExportedValue(); + var a1 = fromRoot.classA.CreateExport().Value; + var a2 = fromRoot.classA.CreateExport().Value; + fromRoot.localClassA.InstanceValue = 101; + a1.InstanceValue = 202; + a2.InstanceValue = 303; + + if (a1.InstanceValue == a2.InstanceValue) { throw new Exception("Incorrect sharing, a1 is shared with a2"); } + + var xroot = new ImportA(); + var x1 = new ImportA(); + var x2 = new ImportA(); + + fromRoot.localClassA.CompositionService.SatisfyImportsOnce(xroot); + a1.CompositionService.SatisfyImportsOnce(x1); + a2.CompositionService.SatisfyImportsOnce(x2); + Assert.Equal(xroot.classA.InstanceValue, fromRoot.localClassA.InstanceValue); + Assert.Equal(x1.classA.InstanceValue, a1.InstanceValue); + Assert.Equal(x2.classA.InstanceValue, a2.InstanceValue); + } + + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ScopingHelpers.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ScopingHelpers.cs new file mode 100644 index 0000000000..b939679642 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ScopingHelpers.cs @@ -0,0 +1,25 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq; + +namespace System.ComponentModel.Composition +{ + static class ScopingHelpers + { + public static CompositionScopeDefinition AsScope(this ComposablePartCatalog catalog, params CompositionScopeDefinition[] children) + { + return new CompositionScopeDefinition(catalog, children); + } + + public static CompositionScopeDefinition AsScopeWithPublicSurface(this ComposablePartCatalog catalog, params CompositionScopeDefinition[] children) + { + IEnumerable definitions = catalog.Parts.SelectMany( (p) => p.ExportDefinitions.Where( (e) => e.ContractName == AttributedModelServices.GetContractName(typeof(T)) ) ); + return new CompositionScopeDefinition(catalog, children, definitions); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/Globalization/CurrentCultureContext.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/Globalization/CurrentCultureContext.cs new file mode 100644 index 0000000000..0ed6e68b43 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/Globalization/CurrentCultureContext.cs @@ -0,0 +1,28 @@ +// 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.Threading; + +namespace System.Globalization +{ + public class CurrentCultureContext : IDisposable + { + private CultureInfo _previousCulture; + + public CurrentCultureContext(CultureInfo culture) + { + _previousCulture = Thread.CurrentThread.CurrentCulture; + + Thread.CurrentThread.CurrentCulture = culture; + } + + public void Dispose() + { + if (_previousCulture != null) + { + Thread.CurrentThread.CurrentCulture = _previousCulture; + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/AdaptingCollectionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/AdaptingCollectionTests.cs new file mode 100644 index 0000000000..2dc22e0e13 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/AdaptingCollectionTests.cs @@ -0,0 +1,403 @@ +// 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.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class FilteringCollection : AdaptingCollection + { + public FilteringCollection(Func, bool> filter) + : base(e => e.Where(filter)) + { + } + } + + public class OrderingCollection : AdaptingCollection + { + public OrderingCollection(Func, object> keySelector) + : this(keySelector, false) + { + } + + public OrderingCollection(Func, object> keySelector, bool descending) + : base(e => descending ? e.OrderByDescending(keySelector) : e.OrderBy(keySelector)) + { + } + } + + public class AdaptingCollection : AdaptingCollection> + { + public AdaptingCollection(Func>>, + IEnumerable>>> adaptor) + : base(adaptor) + { + } + } + + public class AdaptingCollection : ICollection>, INotifyCollectionChanged + { + private readonly List> _allItems = new List>(); + private readonly Func>, IEnumerable>> _adaptor = null; + private List> _adaptedItems = null; + + public AdaptingCollection() : this(null) + { + } + + public AdaptingCollection(Func>, IEnumerable>> adaptor) + { + this._adaptor = adaptor; + } + + public event NotifyCollectionChangedEventHandler CollectionChanged; + + public void ReapplyAdaptor() + { + if (this._adaptedItems != null) + { + this._adaptedItems = null; + this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + } + + protected virtual IEnumerable> Adapt(IEnumerable> collection) + { + if (this._adaptor != null) + { + return this._adaptor.Invoke(collection); + } + + return collection; + } + + protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) + { + NotifyCollectionChangedEventHandler collectionChanged = this.CollectionChanged; + + if (collectionChanged != null) + { + collectionChanged.Invoke(this, e); + } + } + + private List> AdaptedItems + { + get + { + if (this._adaptedItems == null) + { + this._adaptedItems = Adapt(this._allItems).ToList(); + } + + return this._adaptedItems; + } + } + + #region ICollection Implementation + // Accessors work directly against adapted collection + public bool Contains(Lazy item) + { + return this.AdaptedItems.Contains(item); + } + + public void CopyTo(Lazy[] array, int arrayIndex) + { + this.AdaptedItems.CopyTo(array, arrayIndex); + } + + public int Count + { + get { return this.AdaptedItems.Count; } + } + + public bool IsReadOnly + { + get { return false; } + } + + public IEnumerator> GetEnumerator() + { + return this.AdaptedItems.GetEnumerator(); + } + + Collections.IEnumerator Collections.IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + // Mutation methods work against complete collection + // and then force a reset of the adapted collection + public void Add(Lazy item) + { + this._allItems.Add(item); + ReapplyAdaptor(); + } + + public void Clear() + { + this._allItems.Clear(); + ReapplyAdaptor(); + } + + public bool Remove(Lazy item) + { + bool removed = this._allItems.Remove(item); + ReapplyAdaptor(); + return removed; + } + #endregion + } + + public class AdaptingCollectionTests + { + public interface IContract { } + public interface INetworkAwareMetadata + { + [DefaultValue(false)] + bool RequiresOnline { get; } + } + + [Export(typeof(IContract))] + [ExportMetadata("RequiresOnline", true)] + public class NetworkExport : IContract { } + + [Export(typeof(IContract))] + public class NonNetworkExport : IContract { } + + public class FilterExports + { + public FilterExports() + { + this.OnlineOnly = new AdaptingCollection(e => + e.Where(p => p.Metadata.RequiresOnline)); + + this.OnlineOnly2 = new FilteringCollection(p => p.Metadata.RequiresOnline); + } + + [ImportMany] + public AdaptingCollection OnlineOnly { get; set; } + + [ImportMany] + public FilteringCollection OnlineOnly2 { get; set; } + } + + [Fact] + public void TestFilteringImports() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(NetworkExport), typeof(NonNetworkExport)); + + var filterExports = new FilterExports(); + container.ComposeParts(filterExports); + + Assert.Equal(1, filterExports.OnlineOnly.Count); + Assert.Equal(1, filterExports.OnlineOnly2.Count); + } + + public interface IOrderMetadata + { + [DefaultValue(Int32.MaxValue)] + int Order { get; } + } + + [Export(typeof(IContract))] + [ExportMetadata("Order", 2)] + public class BExport : IContract { } + + [Export(typeof(IContract))] + [ExportMetadata("Order", 1)] + public class AExport : IContract { } + + [Export(typeof(IContract))] + public class CExport : IContract { } + + public class OrderExportsByMetadata + { + public OrderExportsByMetadata() + { + this.OrderedItems = new AdaptingCollection(e => + e.OrderBy(p => p.Metadata.Order)); + + this.OrderedItems2 = new OrderingCollection(p => p.Metadata.Order); + } + + [ImportMany] + public AdaptingCollection OrderedItems { get; set; } + + [ImportMany] + public OrderingCollection OrderedItems2 { get; set; } + } + + [Fact] + public void TestOrderingImportsByMetadata() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(BExport), typeof(AExport), typeof(CExport)); + var orderExports = new OrderExportsByMetadata(); + + container.ComposeParts(orderExports); + + Assert.IsType(orderExports.OrderedItems.ElementAt(0).Value); + Assert.IsType(orderExports.OrderedItems.ElementAt(1).Value); + Assert.IsType(orderExports.OrderedItems.ElementAt(2).Value); + + Assert.IsType(orderExports.OrderedItems2.ElementAt(0).Value); + Assert.IsType(orderExports.OrderedItems2.ElementAt(1).Value); + Assert.IsType(orderExports.OrderedItems2.ElementAt(2).Value); + } + + public class OrderExportsByName + { + public OrderExportsByName(bool descending) + { + if (descending) + { + this.OrderedItems = new AdaptingCollection(e => + e.OrderByDescending(p => p.Value.GetType().FullName)); + } + else + { + this.OrderedItems = new AdaptingCollection(e => + e.OrderBy(p => p.Value.GetType().FullName)); + } + } + + [ImportMany] + public AdaptingCollection OrderedItems { get; set; } + } + + [Fact] + public void TestOrderingImportsByTypeName() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(BExport), typeof(AExport), typeof(CExport)); + var orderExports = new OrderExportsByName(false); + + container.ComposeParts(orderExports); + + Assert.IsType(orderExports.OrderedItems.ElementAt(0).Value); + Assert.IsType(orderExports.OrderedItems.ElementAt(1).Value); + Assert.IsType(orderExports.OrderedItems.ElementAt(2).Value); + + orderExports = new OrderExportsByName(true); + + container.ComposeParts(orderExports); + + Assert.IsType(orderExports.OrderedItems.ElementAt(0).Value); + Assert.IsType(orderExports.OrderedItems.ElementAt(1).Value); + Assert.IsType(orderExports.OrderedItems.ElementAt(2).Value); + } + + public interface IDynamicFilteredMetadata + { + bool Dynamic { get; } + } + + [Export(typeof(IContract))] + [ExportMetadata("Dynamic", true)] + public class Dynamic1 : IContract { } + + [Export(typeof(IContract))] + [ExportMetadata("Dynamic", true)] + public class Dynamic2 : IContract { } + + [Export(typeof(IContract))] + [ExportMetadata("Dynamic", false)] + public class NonDynamic1 : IContract { } + + public class DynamicFilteredCollection : AdaptingCollection where M : IDynamicFilteredMetadata + { + public DynamicFilteredCollection() + { + } + + private bool _includeDynamic = false; + public bool IncludeDynamic + { + get { return this._includeDynamic; } + set + { + if (this._includeDynamic != value) + { + this.ReapplyAdaptor(); + } + + this._includeDynamic = value; + } + } + + protected override IEnumerable> Adapt(IEnumerable> collection) + { + return collection.Where(p => !p.Metadata.Dynamic || IncludeDynamic); + } + } + + public class DynamicExports + { + [ImportMany] + public DynamicFilteredCollection DynamicCollection { get; set; } + } + + [Fact] + [ActiveIssue(25498, TargetFrameworkMonikers.UapAot)] + public void TestDyamicallyFilteringImports() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(Dynamic1), typeof(Dynamic2), typeof(NonDynamic1)); + var dynamicExports = new DynamicExports(); + + container.ComposeParts(dynamicExports); + + Assert.Equal(1, dynamicExports.DynamicCollection.Count); + + dynamicExports.DynamicCollection.IncludeDynamic = true; + + Assert.Equal(3, dynamicExports.DynamicCollection.Count); + } + + public class DynamicExportsNoSubType + { + public DynamicExportsNoSubType() + { + this.DynamicCollection = new AdaptingCollection(e => + e.Where(p => !p.Metadata.Dynamic || this.IncludeDynamic)); + } + + private bool _includeDynamic = false; + public bool IncludeDynamic + { + get { return this._includeDynamic; } + set + { + if (this._includeDynamic != value) + { + this.DynamicCollection.ReapplyAdaptor(); + } + + this._includeDynamic = value; + } + } + + [ImportMany] + public AdaptingCollection DynamicCollection { get; set; } + } + + [Fact] + public void TestDyamicallyFilteringNoSubTypeImports() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(Dynamic1), typeof(Dynamic2), typeof(NonDynamic1)); + var dynamicExports = new DynamicExportsNoSubType(); + + container.ComposeParts(dynamicExports); + + Assert.Equal(1, dynamicExports.DynamicCollection.Count); + + dynamicExports.IncludeDynamic = true; + + Assert.Equal(3, dynamicExports.DynamicCollection.Count); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/CatalogFilteringTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/CatalogFilteringTests.cs new file mode 100644 index 0000000000..6e252930e7 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/CatalogFilteringTests.cs @@ -0,0 +1,127 @@ +// 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.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class CatalogFilteringTests + { + [Fact] + public void FilteredCatalog_ScopeA() + { + var cat = GetCatalog(); + var contA = new CompositionContainer(ScopeCatalog(cat, "A")); + + Assert.True(contA.IsPresent()); + Assert.True(contA.IsPresent()); + Assert.False(contA.IsPresent()); + Assert.False(contA.IsPresent()); + } + + [Fact] + [ActiveIssue(25498, TargetFrameworkMonikers.UapAot)] + public void FilteredCatalog_ScopeB() + { + var cat = GetCatalog(); + var contA = new CompositionContainer(ScopeCatalog(cat, "A")); + var contB = new CompositionContainer(ScopeCatalog(cat, "B"), contA); + + Assert.True(contB.IsPresent()); + Assert.True(contB.IsPresent()); + Assert.True(contB.IsPresent()); + Assert.False(contB.IsPresent()); + } + + [Fact] + public void FilteredCatalog_ScopeC() + { + var cat = GetCatalog(); + var contA = new CompositionContainer(ScopeCatalog(cat, "A")); + var contB = new CompositionContainer(ScopeCatalog(cat, "B"), contA); + var contC = new CompositionContainer(ScopeCatalog(cat, "C"), contB); + + Assert.True(contC.IsPresent()); + Assert.True(contC.IsPresent()); + Assert.True(contC.IsPresent()); + Assert.True(contC.IsPresent()); + } + + [Fact] + [ActiveIssue(812029)] + public void FilteredCatalog_EventsFired() + { + var aggCatalog = CatalogFactory.CreateAggregateCatalog(); + var cat1 = CatalogFactory.CreateAttributed(typeof(ScopeAComponent1), typeof(ScopeBComponent)); + + var filteredCatalog = CatalogFactory.CreateFiltered(aggCatalog, + partDef => partDef.Metadata.ContainsKey("Scope") && + partDef.Metadata["Scope"].ToString() == "A"); + + var container = ContainerFactory.Create(filteredCatalog); + + Assert.False(container.IsPresent(), "sa before add"); + Assert.False(container.IsPresent(), "sb before add"); + + aggCatalog.Catalogs.Add(cat1); + + Assert.True(container.IsPresent(), "sa after add"); + Assert.False(container.IsPresent(), "sb after add"); + + aggCatalog.Catalogs.Remove(cat1); + + Assert.False(container.IsPresent(), "sa after remove"); + Assert.False(container.IsPresent(), "sb after remove"); + } + + private ComposablePartCatalog GetCatalog() + { + return CatalogFactory.CreateAttributed( + typeof(ScopeAComponent1), + typeof(ScopeAComponent2), + typeof(ScopeBComponent), + typeof(ScopeCComponent)); + } + + private ComposablePartCatalog ScopeCatalog(ComposablePartCatalog catalog, string scope) + { + return CatalogFactory.CreateFiltered(catalog, + partDef => partDef.Metadata.ContainsKey("Scope") && + partDef.Metadata["Scope"].ToString() == scope); + } + + [Export] + [PartMetadata("Scope", "A")] + public class ScopeAComponent1 + { + } + + [Export] + [PartMetadata("Scope", "A")] + public class ScopeAComponent2 + { + [Import] + public ScopeAComponent1 ScopeA { get; set; } + } + + [Export] + [PartMetadata("Scope", "B")] + public class ScopeBComponent + { + [Import] + public ScopeAComponent1 ScopeA { get; set; } + } + + [Export] + [PartMetadata("Scope", "C")] + public class ScopeCComponent + { + [Import] + public ScopeBComponent ScopeB { get; set; } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/CompositionContainerAttributedModelCycleTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/CompositionContainerAttributedModelCycleTests.cs new file mode 100644 index 0000000000..52eb766500 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/CompositionContainerAttributedModelCycleTests.cs @@ -0,0 +1,278 @@ +// 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.ComponentModel.Composition; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using System.UnitTesting; +using Xunit; + +namespace Tests.Integration +{ + public class CompositionContainerAttributedModelCycleTests + { + // There are nine possible scenarios that cause a part to have a dependency on another part, some of which + // are legal and some not. For example, below, is not legal for a part, A, to have a prerequisite dependency + // on a part, B, which has also has a prerequisite dependency on A. In contrast, however, it is legal for + // part A and B to have a non-prerequisite (Post) dependency on each other. + // + // ------------------------------ + // | | B | + // | | Pre | Post | None | + // |--------|-----|------|------| + // | Pre | X | X | v | + // | A Post | X | v | v | + // | None | v | v | v | + // ------------------------------ + // + + [Fact] + public void APrerequisiteDependsOnBPrerequisite_ShouldThrowComposition() + { + AssertCycle(typeof(APrerequisiteDependsOnBPrerequisite), + typeof(BPrerequisiteDependsOnAPrerequisite)); + } + + [Fact] + public void APrerequisiteDependsOnBPost_ShouldThrowComposition() + { + AssertCycle(typeof(APrerequisiteDependsOnBPost), + typeof(BPostDependsOnAPrerequisite)); + } + + [Fact] + [ActiveIssue(25498)] + public void APrerequisiteDependsOnBNone_ShouldNotThrow() + { + AssertNotCycle(typeof(APrerequisiteDependsOnBNone), + typeof(BNone)); + } + + [Fact] + public void APostDependsOnBPrerequisite_ShouldThrowComposition() + { + AssertCycle(typeof(APostDependsOnBPrerequisite), + typeof(BPrerequisiteDependsOnAPost)); + } + + [Fact] + [ActiveIssue(25498)] + public void APostDependsOnBPost_ShouldNotThrow() + { + AssertNotCycle(typeof(APostDependsOnBPost), + typeof(BPostDependsOnAPost)); + } + + [Fact] + [ActiveIssue(25498)] + public void APostDependsOnBNone_ShouldNotThrow() + { + AssertNotCycle(typeof(APostDependsOnBNone), + typeof(BNone)); + } + + [Fact] + [ActiveIssue(25498)] + public void BPrerequisiteDependsOnANone_ShouldNotThrow() + { + AssertNotCycle(typeof(ANone), + typeof(BPrerequisiteDependsOnANone)); + } + + [Fact] + [ActiveIssue(25498)] + public void BPostDependsOnANone_ShouldNotThrow() + { + AssertNotCycle(typeof(ANone), + typeof(BPostDependsOnANone)); + } + + [Fact] + [ActiveIssue(25498)] + public void ANoneWithBNone_ShouldNotThrow() + { + AssertNotCycle(typeof(ANone), + typeof(BNone)); + } + + [Fact] + [ActiveIssue(25498)] + public void PartWithHasPrerequisteImportThatIsInAPostCycle_ShouldNotThrow() + { + AssertNotCycle(typeof(PartWithHasPrerequisteImportThatIsInAPostCycle) + , typeof(APostDependsOnBPost), typeof(BPostDependsOnAPost)); + } + + private static void AssertCycle(params Type[] types) + { + foreach (Type type in types) + { + var export = GetExport(type, types); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotGetExportedValue, () => + { + var value = export.Value; + }); + } + } + + private static void AssertNotCycle(params Type[] types) + { + foreach (Type type in types) + { + var export = GetExport(type, types); + + Assert.IsType(export.Value); + } + } + + private static Lazy GetExport(Type type, Type[] partTypes) + { + var container = ContainerFactory.CreateWithAttributedCatalog(partTypes); + + return container.GetExports(type, null, null).Single(); + } + + [Export] + public class APrerequisiteDependsOnBPrerequisite + { + [ImportingConstructor] + public APrerequisiteDependsOnBPrerequisite(BPrerequisiteDependsOnAPrerequisite b) + { + } + } + + [Export] + public class BPrerequisiteDependsOnAPrerequisite + { + [ImportingConstructor] + public BPrerequisiteDependsOnAPrerequisite(APrerequisiteDependsOnBPrerequisite a) + { + } + } + + [Export] + public class APrerequisiteDependsOnBPost + { + [ImportingConstructor] + public APrerequisiteDependsOnBPost(BPostDependsOnAPrerequisite b) + { + } + } + + [Export] + public class BPostDependsOnAPrerequisite + { + [Import] + public APrerequisiteDependsOnBPost A + { + get; + set; + } + } + + [Export] + public class APrerequisiteDependsOnBNone + { + [ImportingConstructor] + public APrerequisiteDependsOnBNone(BNone b) + { + } + } + + [Export] + public class BNone + { + } + + [Export] + public class ANone + { + } + + [Export] + public class APostDependsOnBPrerequisite + { + [Import] + public BPrerequisiteDependsOnAPost B + { + get; + set; + } + } + + [Export] + public class BPrerequisiteDependsOnAPost + { + [ImportingConstructor] + public BPrerequisiteDependsOnAPost(APostDependsOnBPrerequisite a) + { + } + } + + [Export] + public class APostDependsOnBPost + { + [Import] + public BPostDependsOnAPost B + { + get; + set; + } + } + + [Export] + public class BPostDependsOnAPost + { + [Import] + public APostDependsOnBPost A + { + get; + set; + } + } + + [Export] + public class APostDependsOnBNone + { + [Import] + public BNone B + { + get; + set; + } + } + + [Export] + public class BPrerequisiteDependsOnANone + { + [ImportingConstructor] + public BPrerequisiteDependsOnANone(ANone a) + { + } + } + + [Export] + public class BPostDependsOnANone + { + [Import] + public ANone A + { + get; + set; + } + } + + [Export] + public class PartWithHasPrerequisteImportThatIsInAPostCycle + { + [ImportingConstructor] + public PartWithHasPrerequisteImportThatIsInAPostCycle(APostDependsOnBPost a) + { + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/ConstructorInjectionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/ConstructorInjectionTests.cs new file mode 100644 index 0000000000..896e52d293 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/ConstructorInjectionTests.cs @@ -0,0 +1,188 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using Xunit; + +namespace Tests.Integration +{ + public class ConstructorInjectionTests + { + [Fact] + public void SimpleConstructorInjection() + { + var container = ContainerFactory.Create(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(PartFactory.CreateAttributed(typeof(SimpleConstructorInjectedObject))); + batch.AddExportedValue("CISimpleValue", 42); + container.Compose(batch); + + SimpleConstructorInjectedObject simple = container.GetExportedValue(); + + Assert.Equal(42, simple.CISimpleValue); + } + + public interface IOptionalRef { } + + [Export] + public class OptionalExportProvided { } + + [Export] + public class AWithOptionalParameter + { + [ImportingConstructor] + public AWithOptionalParameter([Import(AllowDefault = true)]IOptionalRef import, + [Import("ContractThatShouldNotBeFound", AllowDefault = true)]int value, + [Import(AllowDefault=true)]OptionalExportProvided provided) + { + Assert.Null(import); + Assert.Equal(0, value); + Assert.NotNull(provided); + } + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // Actual: typeof(System.Reflection.ReflectionTypeLoadException): Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void OptionalConstructorArgument() + { + var container = GetContainerWithCatalog(); + var a = container.GetExportedValue(); + + // A should verify that it receieved optional arugments properly + Assert.NotNull(a); + } + + [Export] + public class AWithCollectionArgument + { + private IEnumerable _values; + + [ImportingConstructor] + public AWithCollectionArgument([ImportMany("MyConstructorCollectionItem")]IEnumerable values) + { + this._values = values; + } + + public IEnumerable Values { get { return this._values; } } + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // Actual: typeof(System.Reflection.ReflectionTypeLoadException): Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void RebindingShouldNotHappenForConstructorArguments() + { + var container = GetContainerWithCatalog(); + CompositionBatch batch = new CompositionBatch(); + + var p1 = batch.AddExportedValue("MyConstructorCollectionItem", 1); + batch.AddExportedValue("MyConstructorCollectionItem", 2); + batch.AddExportedValue("MyConstructorCollectionItem", 3); + container.Compose(batch); + + var a = container.GetExportedValue(); + Assert.Equal(a.Values, new int[3] { 1, 2, 3 }); + + batch = new CompositionBatch(); + batch.AddExportedValue("MyConstructorCollectionItem", 4); + batch.AddExportedValue("MyConstructorCollectionItem", 5); + batch.AddExportedValue("MyConstructorCollectionItem", 6); + // After rejection changes that are incompatible with existing assumptions are no + // longer silently ignored. The batch attempting to make this change is rejected + // with a ChangeRejectedException + Assert.Throws(() => + { + container.Compose(batch); + }); + + // The collection which is a constructor import should not be rebound + Assert.Equal(a.Values, new int[3] { 1, 2, 3 }); + + batch.RemovePart(p1); + // After rejection changes that are incompatible with existing assumptions are no + // longer silently ignored. The batch attempting to make this change is rejected + // with a ChangeRejectedException + Assert.Throws(() => + { + container.Compose(batch); + }); + + // The collection which is a constructor import should not be rebound + Assert.Equal(a.Values, new int[3] { 1, 2, 3 }); + } + + [Fact] + public void MissingConstructorArgsWithAlreadyCreatedInstance() + { + var container = GetContainerWithCatalog(); + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(new ClassWithNotFoundConstructorArgs(21)); + container.Compose(batch); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // Actual: typeof(System.Reflection.ReflectionTypeLoadException): Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void MissingConstructorArgsWithTypeFromCatalogMissingArg() + { + var container = GetContainerWithCatalog(); + + // After rejection part definitions in catalogs whose dependencies cannot be + // satisfied are now silently ignored, turning this into a cardinality + // exception for the GetExportedValue call + Assert.Throws(() => + { + container.GetExportedValue(); + }); + } + + [Fact] + [ActiveIssue(25498, TestPlatforms.AnyUnix)] // Actual: typeof(System.Reflection.ReflectionTypeLoadException): Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. + public void MissingConstructorArgsWithWithTypeFromCatalogWithArg() + { + var container = GetContainerWithCatalog(); + CompositionBatch batch = new CompositionBatch(); + batch.AddExportedValue("ContractThatDoesntExist", 21); + container.Compose(batch); + + Assert.True(container.IsPresent()); + } + + [Export] + public class ClassWithNotFoundConstructorArgs + { + [ImportingConstructor] + public ClassWithNotFoundConstructorArgs([Import("ContractThatDoesntExist")]int i) + { + } + } + + private CompositionContainer GetContainerWithCatalog() + { + var catalog = new AssemblyCatalog(typeof(ConstructorInjectionTests).Assembly); + + return new CompositionContainer(catalog); + } + + [Export] + public class InvalidImportManyCI + { + [ImportingConstructor] + public InvalidImportManyCI( + [ImportMany]List exports) + { + } + } + + [Fact] + public void ImportMany_ConstructorParameter_OnNonAssiganbleType_ShouldThrowCompositionException() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(InvalidImportManyCI)); + + Assert.Throws( + () => container.GetExportedValue()); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/DelayLoadingTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/DelayLoadingTests.cs new file mode 100644 index 0000000000..80482afd77 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/DelayLoadingTests.cs @@ -0,0 +1,300 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.ComponentModel.Composition.ReflectionModel; +using System.Linq; +using Microsoft.Internal; +using Xunit; + +namespace Tests.Integration +{ + public class DelayLoadingTests + { + [Fact] + public void PartTypeLoadedLazily() + { + var catalog = new TypeLoadNotifyingCatalog(typeof(ExportingPart)); + var container = new CompositionContainer(catalog); + catalog.AssertNotLoaded(typeof(ExportingPart)); + Assert.Equal(0, catalog.LoadedTypes.Count()); + + Lazy lazyContract = container.GetExport(); + Assert.NotNull(lazyContract); + catalog.AssertNotLoaded(typeof(ExportingPart)); + Assert.Equal(0, catalog.LoadedTypes.Count()); + + IExporter value = lazyContract.Value; + + catalog.AssertLoaded(typeof(ExportingPart)); + Assert.Equal(1, catalog.LoadedTypes.Count()); + } + + [Fact] + public void PartTypeLoadedLazilyEagerDependeciesLoadEagerly() + { + var catalog = new TypeLoadNotifyingCatalog(typeof(ExportingPart), typeof(PartImportingEagerly)); + var container = new CompositionContainer(catalog); + catalog.AssertNotLoaded(typeof(ExportingPart)); + catalog.AssertNotLoaded(typeof(PartImportingEagerly)); + Assert.Equal(0, catalog.LoadedTypes.Count()); + + Lazy lazyContract = container.GetExport(); + Assert.NotNull(lazyContract); + catalog.AssertNotLoaded(typeof(PartImportingEagerly)); + catalog.AssertNotLoaded(typeof(ExportingPart)); + Assert.Equal(0, catalog.LoadedTypes.Count()); + + IImporter value = lazyContract.Value; + catalog.AssertLoaded(typeof(PartImportingEagerly)); + catalog.AssertLoaded(typeof(ExportingPart)); + Assert.Equal(2, catalog.LoadedTypes.Count()); + } + + [Fact] + public void PartTypeLoadedLazilyLazyDependeciesLoadLazily() + { + var catalog = new TypeLoadNotifyingCatalog(typeof(ExportingPart), typeof(PartImportingLazily)); + var container = new CompositionContainer(catalog); + catalog.AssertNotLoaded(typeof(ExportingPart)); + catalog.AssertNotLoaded(typeof(PartImportingLazily)); + Assert.Equal(0, catalog.LoadedTypes.Count()); + + Lazy lazyContract = container.GetExport(); + Assert.NotNull(lazyContract); + catalog.AssertNotLoaded(typeof(PartImportingLazily)); + catalog.AssertNotLoaded(typeof(ExportingPart)); + Assert.Equal(0, catalog.LoadedTypes.Count()); + + IImporter value = lazyContract.Value; + catalog.AssertLoaded(typeof(PartImportingLazily)); + catalog.AssertNotLoaded(typeof(ExportingPart)); + Assert.Equal(1, catalog.LoadedTypes.Count()); + } + + [Fact] + public void PartTypeLoadedLazilyEagerCollectionDependeciesLoadEagerly() + { + var catalog = new TypeLoadNotifyingCatalog(typeof(ExportingPart), typeof(PartImportingCollectionEagerly)); + var container = new CompositionContainer(catalog); + catalog.AssertNotLoaded(typeof(ExportingPart)); + catalog.AssertNotLoaded(typeof(PartImportingCollectionEagerly)); + Assert.Equal(0, catalog.LoadedTypes.Count()); + + Lazy lazyContract = container.GetExport(); + Assert.NotNull(lazyContract); + catalog.AssertNotLoaded(typeof(PartImportingCollectionEagerly)); + catalog.AssertNotLoaded(typeof(ExportingPart)); + Assert.Equal(0, catalog.LoadedTypes.Count()); + + IImporter value = lazyContract.Value; + catalog.AssertLoaded(typeof(PartImportingCollectionEagerly)); + catalog.AssertLoaded(typeof(ExportingPart)); + Assert.Equal(2, catalog.LoadedTypes.Count()); + } + + [Fact] + public void PartTypeLoadedLazilyLazyCollectionDependeciesLoadLazily() + { + var catalog = new TypeLoadNotifyingCatalog(typeof(ExportingPart), typeof(PartImportingCollectionLazily)); + var container = new CompositionContainer(catalog); + catalog.AssertNotLoaded(typeof(ExportingPart)); + catalog.AssertNotLoaded(typeof(PartImportingCollectionLazily)); + Assert.Equal(0, catalog.LoadedTypes.Count()); + + Lazy lazyContract = container.GetExport(); + Assert.NotNull(lazyContract); + catalog.AssertNotLoaded(typeof(PartImportingCollectionLazily)); + catalog.AssertNotLoaded(typeof(ExportingPart)); + Assert.Equal(0, catalog.LoadedTypes.Count()); + + IImporter value = lazyContract.Value; + catalog.AssertLoaded(typeof(PartImportingCollectionLazily)); + catalog.AssertNotLoaded(typeof(ExportingPart)); + Assert.Equal(1, catalog.LoadedTypes.Count()); + } + + [Fact] + public void PartTypeLoadedLazilyLazyLoopLoadsLazily() + { + var catalog = new TypeLoadNotifyingCatalog(typeof(LazyLoopImporter), typeof(LazyLoopExporter)); + var container = new CompositionContainer(catalog); + catalog.AssertNotLoaded(typeof(LazyLoopImporter)); + catalog.AssertNotLoaded(typeof(LazyLoopExporter)); + Assert.Equal(0, catalog.LoadedTypes.Count()); + + Lazy lazyContract = container.GetExport(); + Assert.NotNull(lazyContract); + catalog.AssertNotLoaded(typeof(LazyLoopImporter)); + catalog.AssertNotLoaded(typeof(LazyLoopExporter)); + Assert.Equal(0, catalog.LoadedTypes.Count()); + + IImporter value = lazyContract.Value; + catalog.AssertLoaded(typeof(LazyLoopImporter)); + catalog.AssertNotLoaded(typeof(LazyLoopExporter)); + Assert.Equal(1, catalog.LoadedTypes.Count()); + } + + public class IExporter + { + } + + public class IImporter + { + } + + [Export(typeof(IExporter))] + public class ExportingPart : IExporter + { + } + + [Export(typeof(IImporter))] + public class PartImportingLazily : IImporter + { + [Import] + public Lazy Exporter { get; set; } + } + + [Export(typeof(IImporter))] + public class PartImportingCollectionLazily : IImporter + { + [ImportMany] + public IEnumerable> Exporters { get; set; } + } + + [Export(typeof(IImporter))] + public class PartImportingEagerly : IImporter + { + [Import] + public IExporter Exporter { get; set; } + } + + [Export(typeof(IImporter))] + public class PartImportingCollectionEagerly : IImporter + { + [ImportMany] + public IEnumerable Exporters { get; set; } + } + + [Export(typeof(IImporter))] + public class LazyLoopImporter : IImporter + { + [Import] + public Lazy Exporter { get; set; } + } + + [Export(typeof(IExporter))] + public class LazyLoopExporter : IExporter + { + [Import] + public Lazy Importer { get; set; } + } + + private class TypeLoadNotifyingCatalog : ComposablePartCatalog + { + ComposablePartDefinition[] _definitions; + public HashSet LoadedTypes { get; private set; } + + public TypeLoadNotifyingCatalog(params Type[] types) + { + this._definitions = types.Select(type => this.CreatePartDefinition(type)).ToArray(); + this.LoadedTypes = new HashSet(); + } + + public override IQueryable Parts + { + get { return this._definitions.AsQueryable(); } + } + + private ComposablePartDefinition CreatePartDefinition(Type type) + { + ComposablePartDefinition partDefinition = AttributedModelServices.CreatePartDefinition(type, null); + return this.CreateWrapped(partDefinition, type); + } + + private ComposablePartDefinition CreateWrapped(ComposablePartDefinition partDefinition, Type type) + { + IEnumerable exports = partDefinition.ExportDefinitions.Select(e => this.CreateWrapped(e, type)).ToArray(); + IEnumerable imports = partDefinition.ImportDefinitions.Cast().Select(i => this.CreateWrapped(i, type)).ToArray(); + + return ReflectionModelServices.CreatePartDefinition( + this.CreateWrapped(ReflectionModelServices.GetPartType(partDefinition), type), + ReflectionModelServices.IsDisposalRequired(partDefinition), + imports.AsLazy(), + exports.AsLazy(), + partDefinition.Metadata.AsLazy(), + null); + } + + private Lazy CreateWrapped(Lazy lazy, Type type) + { + return new Lazy( + () => { this.OnTypeLoaded(type); return lazy.Value; }); + } + + private LazyMemberInfo CreateWrapped(LazyMemberInfo lazyMember, Type type) + { + return new LazyMemberInfo( + lazyMember.MemberType, + () => { this.OnTypeLoaded(type); return lazyMember.GetAccessors(); }); + } + + private ExportDefinition CreateWrapped(ExportDefinition export, Type type) + { + return ReflectionModelServices.CreateExportDefinition( + this.CreateWrapped(ReflectionModelServices.GetExportingMember(export), type), + export.ContractName, + export.Metadata.AsLazy(), + null); + } + + private ImportDefinition CreateWrapped(ContractBasedImportDefinition import, Type type) + { + if (ReflectionModelServices.IsImportingParameter(import)) + { + return ReflectionModelServices.CreateImportDefinition( + this.CreateWrapped(ReflectionModelServices.GetImportingParameter(import), type), + import.ContractName, + import.RequiredTypeIdentity, + import.RequiredMetadata, + import.Cardinality, + import.RequiredCreationPolicy, + null); + } + else + { + return ReflectionModelServices.CreateImportDefinition( + this.CreateWrapped(ReflectionModelServices.GetImportingMember(import), type), + import.ContractName, + import.RequiredTypeIdentity, + import.RequiredMetadata, + import.Cardinality, + import.IsRecomposable, + import.RequiredCreationPolicy, + null); + } + } + + private void OnTypeLoaded(Type type) + { + this.LoadedTypes.Add(type); + } + + public void AssertLoaded(Type type) + { + Assert.True(this.LoadedTypes.Contains(type)); + } + + public void AssertNotLoaded(Type type) + { + Assert.False(this.LoadedTypes.Contains(type)); + } + + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/DelegateCompositionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/DelegateCompositionTests.cs new file mode 100644 index 0000000000..7bd54f8be7 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/DelegateCompositionTests.cs @@ -0,0 +1,222 @@ +// 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.ComponentModel.Composition; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Primitives; +using Xunit; + +namespace Tests.Integration +{ + public class DelegateCompositionTests + { + public delegate int SimpleDelegate(); + public delegate object DoWorkDelegate(int i, ref object o, out string s); + + public class MethodExporter + { + [Export] + public int SimpleMethod() + { + return 1; + } + + [Export(typeof(DoWorkDelegate))] + public object DoWork(int i, ref object o, out string s) + { + s = ""; + return o; + } + + [Export("ActionWith8Arguments")] + [Export("ActionWith8Arguments", typeof(Delegate))] + public void Action(int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8) + { + } + + [Export("FunctionWith8Arguments")] + [Export("FunctionWith8Arguments", typeof(Delegate))] + public int Function(int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8) + { + return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8; + } + + [Export("FunctionWithDefaultValue")] + public int FunctionWithDefaultValue(int i, string s = "") + { + return i; + } + } + + [Fact] + public void Export_SimpleCustomDelegate_ShouldWork() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(MethodExporter)); + + var contractName = AttributedModelServices.GetContractName(typeof(SimpleDelegate)); + + var export1 = container.GetExportedValue(); + Assert.Equal(1, export1()); + + var export2 = container.GetExportedValue>(); + Assert.Equal(1, export1()); + + var export3 = (ExportedDelegate)container.GetExportedValue(contractName); + var export4 = (SimpleDelegate)export3.CreateDelegate(typeof(SimpleDelegate)); + Assert.Equal(1, export4()); + } + + [Fact] + public void Export_CustomDelegateWithOutRefParams_ShouldWork() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(MethodExporter)); + + var export1 = container.GetExportedValue(); + + int i = 0; + object o = new object(); + string s; + + export1(i, ref o, out s); + } + + [Fact] + public void Export_FunctionWith8Arguments_ShouldWorkFine() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(MethodExporter)); + + Assert.NotNull(container.GetExportedValue("FunctionWith8Arguments")); + } + + [Fact] + public void Export_ActionWith8Arguments_ShouldWorkFine() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(MethodExporter)); + + Assert.NotNull(container.GetExportedValue("ActionWith8Arguments")); + } + + [Fact] + public void Export_FunctionWithDefaultValue_ShouldWorkFine() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(MethodExporter)); + + var export = container.GetExportedValue>("FunctionWithDefaultValue"); + Assert.Equal(3, export(3, "a")); + + // Even though the string argument is optional it still cannot be cast to Func. + var export2 = (ExportedDelegate)container.GetExportedValue("FunctionWithDefaultValue"); + var export3 = export2.CreateDelegate(typeof(Func)); + + Assert.Null(export3); + } + + public delegate int DelegateOneArg(int i); + public delegate int DelegateTwoArgs(int i, int j); + + public class CustomExportedDelegate : ExportedDelegate + { + private Func _func; + + public CustomExportedDelegate(Func func) + { + this._func = func; + } + + public override Delegate CreateDelegate(Type delegateType) + { + if (delegateType == typeof(DelegateOneArg)) + { + return (DelegateOneArg)((i) => this._func(i, 0)); + } + else if (delegateType == typeof(DelegateTwoArgs)) + { + return (DelegateTwoArgs)((i, j) => this._func(i, j)); + } + + return null; + } + } + + public class ExportCustomExportedDelegates + { + [Export("CustomExportedDelegate", typeof(DelegateOneArg))] + [Export("CustomExportedDelegate", typeof(DelegateTwoArgs))] + public ExportedDelegate MyExportedDelegate + { + get + { + return new CustomExportedDelegate(DoWork); + } + } + + public int DoWork(int i, int j) + { + return i + j; + } + } + + [Export] + public class ImportCustomExportedDelegates + { + [Import("CustomExportedDelegate")] + public DelegateOneArg DelegateOneArg { get; set; } + + [Import("CustomExportedDelegate")] + public DelegateTwoArgs DelegateTwoArgs { get; set; } + + } + + [Fact] + public void CustomExportedDelegate_ShouldWork() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(ExportCustomExportedDelegates), + typeof(ImportCustomExportedDelegates)); + + var importer = container.GetExportedValue(); + + Assert.Equal(1, importer.DelegateOneArg(1)); + Assert.Equal(2, importer.DelegateTwoArgs(1, 1)); + } + + public delegate void GetRef(ref int i); + public delegate void GetOut(out int i); + + public class RefOutMethodExporter + { + [Export] + public void MyGetOut(out int i) + { + i = 29; + } + } + + [Fact] + public void MethodWithOutParam_ShouldWorkWithRefParam() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(RefOutMethodExporter)); + + int i = 0; + + var export1 = container.GetExportedValue(); + + export1(ref i); + Assert.Equal(29, i); + i = 0; + + var export2 = container.GetExportedValue(); + + export2(out i); + Assert.Equal(29, i); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/DiscoveryTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/DiscoveryTests.cs new file mode 100644 index 0000000000..d5726e2d53 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/DiscoveryTests.cs @@ -0,0 +1,856 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using System.UnitTesting; +using Xunit; + +namespace Tests.Integration +{ + public class DiscoveryTests + { + public abstract class AbstractClassWithExports + { + [Export("StaticExport")] + public static string StaticExport { get { return "ExportedValue"; } } + + [Export("InstanceExport")] + public string InstanceExport { get { return "InstanceExportedValue"; } } + } + + [Fact] + public void Export_StaticOnAbstractClass_ShouldExist() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(AbstractClassWithExports)); + + Assert.True(container.IsPresent("StaticExport")); + Assert.False(container.IsPresent("InstanceExport")); + } + + public class ClassWithStaticImport + { + [Import("StaticImport")] + public static string MyImport + { + get; set; + } + } + + [Fact] + public void Import_StaticImport_ShouldNotBeSet() + { + var container = ContainerFactory.Create(); + container.AddAndComposeExportedValue("StaticImport", "String that shouldn't be imported"); + + var importer = new ClassWithStaticImport(); + + container.SatisfyImportsOnce(importer); + + Assert.Null(ClassWithStaticImport.MyImport); + } + + [Export] + public class BaseWithNonPublicImportAndExport + { + [Import("BasePrivateImport")] + private string _basePrivateImport = null; + + public string BasePrivateImport { get { return this._basePrivateImport; } } + } + + [Export] + public class DerivedBaseWithNonPublicImportAndExport : BaseWithNonPublicImportAndExport + { + + } + + [Fact] + public void Import_PrivateOnClass_ShouldSetImport() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(BaseWithNonPublicImportAndExport)); + container.AddAndComposeExportedValue("BasePrivateImport", "Imported String"); + + var importer = container.GetExportedValue(); + Assert.Equal("Imported String", importer.BasePrivateImport); + } + + [Fact] + public void Import_PrivateOnBase_ShouldSetImport() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(DerivedBaseWithNonPublicImportAndExport)); + container.AddAndComposeExportedValue("BasePrivateImport", "Imported String"); + + var importer = container.GetExportedValue(); + Assert.Equal("Imported String", importer.BasePrivateImport); + } + + public interface InterfaceWithImport + { + [Import("InterfaceImport")] + int MyImport { get; set; } + } + + public interface InterfaceWithExport + { + [Export("InterfaceExport")] + int MyExport { get; set; } + } + + [Fact] + public void AttributesOnInterface_ShouldNotBeConsiderAPart() + { + var catalog = CatalogFactory.CreateAttributed( + typeof(InterfaceWithImport), + typeof(InterfaceWithExport)); + + Assert.Equal(0, catalog.Parts.Count()); + } + + [Export] + public class ClassWithInterfaceInheritedImport : InterfaceWithImport + { + public int MyImport { get; set; } + } + + [Fact] + public void Import_InheritImportFromInterface_ShouldExposeImport() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(ClassWithInterfaceInheritedImport)); + + container.AddAndComposeExportedValue("InterfaceImport", 42); + + var importer = container.GetExportedValue(); + + Assert.True(importer.MyImport == default(int), "Imports declared on interfaces should not be discovered"); + } + + public class ClassWithInterfaceInheritedExport : InterfaceWithExport + { + public ClassWithInterfaceInheritedExport() + { + MyExport = 42; + } + + public int MyExport { get; set; } + } + + [Fact] + public void Import_InheritExportFromInterface_ShouldNotExposeExport() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(ClassWithInterfaceInheritedExport)); + + Assert.False(container.IsPresent("InterfaceExport"), "Export defined on interface should not be discovered!"); + } + + public interface IFoo { } + + [InheritedExport] + public abstract class BaseWithVirtualExport + { + [Export] + public virtual IFoo MyProp { get; set; } + } + + [InheritedExport(typeof(BaseWithVirtualExport))] + public class DerivedWithOverrideExport : BaseWithVirtualExport + { + [Export] + public override IFoo MyProp { get; set; } + } + + [Fact] + public void Export_BaseAndDerivedShouldAmountInTwoExports() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(BaseWithVirtualExport), + typeof(DerivedWithOverrideExport)); + + var exports1 = container.GetExportedValues(); + Assert.Equal(1, exports1.Count()); + + var exports2 = container.GetExportedValues(); + Assert.Equal(1, exports2.Count()); + } + + public interface IDocument { } + + [Export(typeof(IDocument))] + [ExportMetadata("Name", "TextDocument")] + public class TextDocument : IDocument + { + } + + [Export(typeof(IDocument))] + [ExportMetadata("Name", "XmlDocument")] + public class XmlDocument : TextDocument + { + } + + [Fact] + public void Export_ExportingSameContractInDerived_ShouldResultInHidingBaseExport() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(IDocument), + typeof(XmlDocument)); + + var export = container.GetExport>(); + + Assert.Equal("XmlDocument", export.Metadata["Name"]); + } + + [Fact] + public void Export_ExportingBaseAndDerivedSameContract_ShouldResultInOnlyTwoExports() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(IDocument), + typeof(TextDocument), + typeof(XmlDocument)); + + var exports = container.GetExports>(); + + Assert.Equal(2, exports.Count()); + Assert.Equal("TextDocument", exports.ElementAt(0).Metadata["Name"]); + Assert.IsType(exports.ElementAt(0).Value); + + Assert.Equal("XmlDocument", exports.ElementAt(1).Metadata["Name"]); + Assert.IsType(exports.ElementAt(1).Value); + } + + public interface IObjectSerializer { } + + [Export(typeof(IDocument))] + [Export(typeof(IObjectSerializer))] + [ExportMetadata("Name", "XamlDocument")] + public class XamlDocument : XmlDocument, IObjectSerializer + { + } + + [Fact] + public void Export_ExportingSameContractInDerivedAndNewContract_ShouldResultInHidingBaseAndExportingNewContract() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(XamlDocument)); + + var export = container.GetExport>(); + + Assert.Equal("XamlDocument", export.Metadata["Name"]); + + var export2 = container.GetExport>(); + + Assert.Equal("XamlDocument", export2.Metadata["Name"]); + } + + [Export(typeof(IDocument))] + [ExportMetadata("Name", "WPFDocument")] + public class WPFDocument : XamlDocument + { + } + + [Fact] + public void Export_ExportingSameContractInDerivedAndAnotherContractInBase_ShouldResultInHidingOneBaseAndInheritingNewContract() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(WPFDocument)); + + var export = container.GetExport>(); + + Assert.Equal("WPFDocument", export.Metadata["Name"]); + + var export2 = container.GetExportedValueOrDefault(); + + Assert.Null(export2); + } + + [InheritedExport] + public abstract class Plugin + { + public virtual string GetLocation() + { + return "NoWhere"; + } + + public virtual int Version + { + get + { + return 0; + } + } + } + + private void VerifyValidPlugin(CompositionContainer container, int version, string location) + { + var plugins = container.GetExports(); + Assert.Equal(1, plugins.Count()); + + var plugin = plugins.Single().Value; + + Assert.Equal(location, plugin.GetLocation()); + Assert.Equal(version, plugin.Version); + } + + public class Plugin1 : Plugin + { + } + + [Fact] + public void Export_Plugin1() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(Plugin1)); + + VerifyValidPlugin(container, 0, "NoWhere"); + } + + public class Plugin2 : Plugin + { + public override string GetLocation() + { + return "SomeWhere"; + } + public override int Version + { + get + { + return 1; + } + } + } + + [Fact] + public void Export_Plugin2() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(Plugin2)); + + VerifyValidPlugin(container, 1, "SomeWhere"); + } + + public class Plugin3 : Plugin + { + [Export("PluginLocation")] + public override string GetLocation() + { + return "SomeWhere3"; + } + + [Export("PluginVersion")] + public override int Version + { + get + { + return 3; + } + } + } + + [Fact] + public void Export_Plugin3() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(Plugin3)); + + VerifyValidPlugin(container, 3, "SomeWhere3"); + + var plVer = container.GetExportedValue("PluginVersion"); + Assert.Equal(3, plVer); + + var plLoc = container.GetExportedValue>("PluginLocation"); + Assert.Equal("SomeWhere3", plLoc()); + } + + [InheritedExport(typeof(Plugin))] + public class Plugin4 : Plugin + { + public override string GetLocation() + { + return "SomeWhere4"; + } + + public override int Version + { + get + { + return 4; + } + } + } + + [Fact] + public void Export_Plugin4() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(Plugin4)); + + VerifyValidPlugin(container, 4, "SomeWhere4"); + } + + public interface IPlugin + { + int Id { get; } + } + + public class MyPlugin : IPlugin + { + [Export("PluginId")] + public int Id { get { return 0; } } + } + + [Fact] + public void Export_MyPlugin() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(MyPlugin)); + + var export = container.GetExportedValue("PluginId"); + } + + [InheritedExport] + public interface IApplicationPlugin + { + string Name { get; } + + object Application { get; set; } + } + + [InheritedExport] + public interface IToolbarPlugin : IApplicationPlugin + { + object ToolBar { get; set; } + } + + public class MyToolbarPlugin : IToolbarPlugin + { + [Export("ApplicationPluginNames")] + public string Name { get { return "MyToolbarPlugin"; } } + + [Import("Application")] + public object Application { get; set; } + + [Import("ToolBar")] + public object ToolBar { get; set; } + } + + [Fact] + public void TestInterfaces() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(MyToolbarPlugin)); + + var app = new object(); + container.AddAndComposeExportedValue("Application", app); + + var toolbar = new object(); + container.AddAndComposeExportedValue("ToolBar", toolbar); + + var export = container.GetExportedValue(); + + Assert.Equal(app, export.Application); + Assert.Equal(toolbar, export.ToolBar); + Assert.Equal("MyToolbarPlugin", export.Name); + + var pluginNames = container.GetExportedValues("ApplicationPluginNames"); + Assert.Equal(1, pluginNames.Count()); + } + + public class ImportOnVirtualProperty + { + public int ImportSetCount = 0; + private int _value; + + [Import("VirtualImport")] + public virtual int VirtualImport + { + get + { + return this._value; + } + set + { + this._value = value; + ImportSetCount++; + } + } + } + + public class ImportOnOverridenPropertyWithSameContract : ImportOnVirtualProperty + { + [Import("VirtualImport")] + public override int VirtualImport + { + get + { + return base.VirtualImport; + } + set + { + base.VirtualImport = value; + } + } + } + + [Fact] + public void Import_VirtualPropertyOverrideWithSameContract_ShouldSucceed() + { + var container = ContainerFactory.Create(); + container.AddAndComposeExportedValue("VirtualImport", 21); + + var import = new ImportOnOverridenPropertyWithSameContract(); + + container.SatisfyImportsOnce(import); + + // Import will get set twice because there are 2 imports on the same property. + // We would really like to either elminate it getting set twice or error in this case + // but we figure it is a rare enough corner case that it doesn't warrented the run time cost + // and can be covered by an FxCop rule. + + Assert.Equal(2, import.ImportSetCount); + Assert.Equal(21, import.VirtualImport); + } + + public class ImportOnOverridenPropertyWithDifferentContract : ImportOnVirtualProperty + { + [Import("OverriddenImport")] + public override int VirtualImport + { + set + { + base.VirtualImport = value; + } + } + } + + [Fact] + public void Import_VirtualPropertyOverrideWithDifferentContract_ShouldSucceed() + { + var container = ContainerFactory.Create(); + container.AddAndComposeExportedValue("VirtualImport", 21); + container.AddAndComposeExportedValue("OverriddenImport", 42); + + var import = new ImportOnOverridenPropertyWithSameContract(); + + container.SatisfyImportsOnce(import); + + // Import will get set twice because there are 2 imports on the same property. + // We would really like to either elminate it getting set twice or error in this case + // but we figure it is a rare enough corner case that it doesn't warrented the run time cost + // and can be covered by an FxCop rule. + + Assert.Equal(2, import.ImportSetCount); + + // The derived most import should be discovered first and so it will get set first + // and thus the value should be the base import which is 21. + Assert.Equal(21, import.VirtualImport); + } + + [InheritedExport] + public interface IOrderScreen { } + + public class NorthwindOrderScreen : IOrderScreen + { + } + + public class SouthsandOrderScreen : IOrderScreen + { + } + + [Fact] + public void Export_ExportOnlyOnBaseInterfacewithInheritedMarked_ShouldFindAllImplementers() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(NorthwindOrderScreen), + typeof(SouthsandOrderScreen)); + + var exports = container.GetExportedValues(); + + Assert.Equal(2, exports.Count()); + Assert.IsType(exports.ElementAt(0)); + Assert.IsType(exports.ElementAt(1)); + } + + [Export] + public class PartWithStaticConstructor + { + static PartWithStaticConstructor() + { + throw new Exception(); + } + } + + [Fact] + public void StaticConstructor() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(PartWithStaticConstructor)); + + CompositionAssert.ThrowsError(ErrorId.ImportEngine_PartCannotGetExportedValue, + ErrorId.ImportEngine_PartCannotActivate, + () => container.GetExportedValue()); + } + + public interface IAddin + { + void LoadAddin(object application); + void Shutdown(); + } + + [MetadataAttribute] + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class AddinAttribute : ExportAttribute, ITrans_AddinMetadata + { + private string _name; + private string _version; + private string _id; + + public AddinAttribute(string name, string version, string id) + : base(typeof(IAddin)) + { + this._name = name; + this._version = version; + this._id = id; + } + + public string Name { get { return this._name; } } + public string Version { get { return this._version; } } + public string Id { get { return this._id; } } + } + + [Addin("Addin1", "1.0", "{63D1B00F-AD2F-4F14-8A36-FFA59E4A101C}")] + public class Addin1 : IAddin + { + public void LoadAddin(object application) + { + } + public void Shutdown() + { + } + } + + [Addin("Addin2", "1.0", "{63D1B00F-AD2F-4F14-8A36-FFA59E4A101D}")] + public class Addin2 : IAddin + { + public void LoadAddin(object application) + { + } + public void Shutdown() + { + } + } + + [Addin("Addin3", "1.0", "{63D1B00F-AD2F-4F14-8A36-FFA59E4A101E}")] + public class Addin3 : IAddin + { + public void LoadAddin(object application) + { + } + public void Shutdown() + { + } + } + + [Fact] + public void DiscoverAddinsWithCombinedCustomExportAndMetadataAttribute() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(Addin1), typeof(Addin2), typeof(Addin3)); + + var addins = container.GetExports().ToArray(); + + Assert.Equal(3, addins.Length); + + var values = new AddinAttribute[] + { + new AddinAttribute("Addin1", "1.0", "{63D1B00F-AD2F-4F14-8A36-FFA59E4A101C}"), + new AddinAttribute("Addin2", "1.0", "{63D1B00F-AD2F-4F14-8A36-FFA59E4A101D}"), + new AddinAttribute("Addin3", "1.0", "{63D1B00F-AD2F-4F14-8A36-FFA59E4A101E}"), + }; + + for (int i = 0; i < values.Length; i++) + { + var addinMetadata = addins[i].Metadata; + + Assert.Equal(values[i].Name, addinMetadata.Name); + Assert.Equal(values[i].Version, addinMetadata.Version); + Assert.Equal(values[i].Id, addinMetadata.Id); + } + } + + [Fact] + public void CombinedCustomExportMetadataAttribute_ShouldNotContainMetadataFromExportAttribute() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(Addin1)); + var addin = container.GetExport>(); + + Assert.Equal(4, addin.Metadata.Count); // 3 metadata values and type identity + + Assert.Equal(AttributedModelServices.GetTypeIdentity(typeof(IAddin)), addin.Metadata[CompositionConstants.ExportTypeIdentityMetadataName]); + Assert.Equal("Addin1", addin.Metadata["Name"]); + Assert.Equal("1.0", addin.Metadata["Version"]); + Assert.Equal("{63D1B00F-AD2F-4F14-8A36-FFA59E4A101C}", addin.Metadata["Id"]); + } + + public class CustomInheritedExportAttribute : InheritedExportAttribute + { + } + + [CustomInheritedExport] + public interface IUsesCustomInheritedExport + { + int Property { get; } + } + + public class UsesCustomInheritedExportOnInterface : IUsesCustomInheritedExport + { + public int Property + { + get { return 42; } + } + } + + [Fact] + public void Test_CustomInheritedExportAttribute_OnInterface() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(UsesCustomInheritedExportOnInterface)); + var exporter = container.GetExportedValue(); + Assert.Equal(42, exporter.Property); + } + + [CustomInheritedExport] + public class BaseClassWithCustomInheritedExport + { + public int Property { get; set; } + } + + public class DerivedFromBaseWithCustomInheritedExport : BaseClassWithCustomInheritedExport + { + public DerivedFromBaseWithCustomInheritedExport() + { + Property = 43; + } + } + + [Fact] + public void Test_CustomInheritedExportAttribute_OnBaseClass() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(DerivedFromBaseWithCustomInheritedExport)); + var exporter = container.GetExportedValue(); + Assert.Equal(43, exporter.Property); + } + + [InheritedExport("Foo")] + [ExportMetadata("Name", "IFoo1")] + public interface IFoo1 { } + + [InheritedExport("Foo")] + [ExportMetadata("Name", "IFoo2")] + public interface IFoo2 { } + + [InheritedExport("Foo")] + [ExportMetadata("Name", "FooWithOneFoo")] + public class FooWithOneFoo : IFoo1 + { + } + + [Fact] + public void InheritedExport_OnTypeAndInterface() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(FooWithOneFoo)); + + var foos = container.GetExports>("Foo").ToArray(); + + Assert.Equal(1, foos.Length); + Assert.Equal("FooWithOneFoo", foos[0].Metadata["Name"]); + } + + public class FooWithTwoFoos : IFoo1, IFoo2 { } + + [Fact] + public void InheritedExport_TwoInterfaces() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(FooWithTwoFoos)); + + var foos = container.GetExports>("Foo").ToArray(); + + Assert.Equal(2, foos.Length); + + Assert.Equal(new string[] { "IFoo1", "IFoo2" }, foos.Select(e => (string)e.Metadata["Name"])); + } + + public class FooWithIfaceByOneFoo : FooWithOneFoo, IFoo1 { } + + [Fact] + public void InheritedExport_BaseAndInterface() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(FooWithIfaceByOneFoo)); + + var foos = container.GetExports>("Foo").ToArray(); + + Assert.Equal(1, foos.Length); + + Assert.Equal("FooWithOneFoo", foos[0].Metadata["Name"]); + } + + [InheritedExport("Foo")] + [ExportMetadata("Name", "FooWithInheritedOnSelf")] + public class FooWithInheritedOnSelf : FooWithOneFoo, IFoo1 { } + + [Fact] + public void InheritedExport_BaseInterfaceAndSelf() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(FooWithInheritedOnSelf)); + + var foos = container.GetExports>("Foo").ToArray(); + + Assert.Equal(1, foos.Length); + + Assert.Equal("FooWithInheritedOnSelf", foos[0].Metadata["Name"]); + } + + [InheritedExport("Foo")] + [ExportMetadata("Name", "IFoo3")] + public interface IFoo3 : IFoo1 { } + + public class FooWithInterfaceWithMultipleFoos : IFoo3 { } + + [Fact] + [ActiveIssue(25498)] + public void InheritedExport_InterfaceHiearchy() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(FooWithInterfaceWithMultipleFoos)); + + var foos = container.GetExports>("Foo").ToArray(); + Assert.Equal(2, foos.Length); + + Assert.Equal(new string[] { "IFoo1", "IFoo3" }, foos.Select(e => (string)e.Metadata["Name"])); + } + + [InheritedExport("Foo2")] + [ExportMetadata("Name", "FooWithMultipleInheritedExports")] + public class FooWithMultipleInheritedExports : IFoo1 { } + + [Fact] + public void InheritedExport_MultipleDifferentContracts() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(FooWithMultipleInheritedExports)); + + var foos = container.GetExports>("Foo").ToArray(); + + Assert.Equal(1, foos.Length); + + Assert.Equal("IFoo1", foos[0].Metadata["Name"]); + + var foo2s = container.GetExports>("Foo2").ToArray(); + + Assert.Equal(1, foo2s.Length); + + Assert.Equal("FooWithMultipleInheritedExports", foo2s[0].Metadata["Name"]); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/ExportProviderEventTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/ExportProviderEventTests.cs new file mode 100644 index 0000000000..799af5feeb --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/ExportProviderEventTests.cs @@ -0,0 +1,281 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using Xunit; + +namespace System.ComponentModel.Composition +{ + public class ExportProviderEventTests + { + [Fact] + public void BatchAdd_ShouldFireEvents() + { + var container = ContainerFactory.Create(); + var eventListener = new ExportProviderListener(container, container); + + var batch = new CompositionBatch(); + batch.AddExportedValue("MyExport", new object()); + eventListener.VerifyCompose(batch); + } + + [Fact] + public void BatchRemove_ShouldFireEvents() + { + var container = ContainerFactory.Create(); + var batch = new CompositionBatch(); + var exportPart = batch.AddExportedValue("MyExport", new object()); + container.Compose(batch); + + var eventListener = new ExportProviderListener(container, container); + + batch = new CompositionBatch(); + batch.RemovePart(exportPart); + eventListener.VerifyCompose(batch); + } + + [Fact] + public void BatchAddRemove_ShouldFireEvents() + { + var container = ContainerFactory.Create(); + var batch = new CompositionBatch(); + var exportPart = batch.AddExportedValue("MyExport", new object()); + container.Compose(batch); + + var eventListener = new ExportProviderListener(container, container); + + batch = new CompositionBatch(); + batch.RemovePart(exportPart); + batch.AddExportedValue("MyExport2", new object()); + eventListener.VerifyCompose(batch); + } + + [Fact] + public void BatchMultipleAdds_ShouldFireEvents() + { + var container = ContainerFactory.Create(); + var eventListener = new ExportProviderListener(container, container); + + var batch = new CompositionBatch(); + batch.AddExportedValue("MyExport", new object()); + batch.AddExportedValue("MyExport2", new object()); + batch.AddExportedValue("MyExport3", new object()); + eventListener.VerifyCompose(batch); + } + + [Fact] + public void BatchNestedContainerAdds_ShouldFireEvents() + { + var parentContainer = ContainerFactory.Create(); + var container = ContainerFactory.Create(parentContainer); + var eventListener = new ExportProviderListener(parentContainer, container); + + var batch = new CompositionBatch(); + batch.AddExportedValue("MyExport", new object()); + eventListener.VerifyCompose(batch); + } + + [Export] + public class SampleCatalogExport { } + + [Fact] + public void CatalogAdd_ShouldFireEvents() + { + var catalog = new TypeCatalog(typeof(SampleCatalogExport)); + var aggCat = new AggregateCatalog(); + var container = ContainerFactory.Create(aggCat); + var eventListener = new ExportProviderListener(container, container); + + eventListener.VerifyCatalogAdd(() => aggCat.Catalogs.Add(catalog), typeof(SampleCatalogExport)); + } + + [Fact] + public void CatalogRemove_ShouldFireEvents() + { + var catalog = new TypeCatalog(typeof(SampleCatalogExport)); + var aggCat = new AggregateCatalog(); + var container = ContainerFactory.Create(aggCat); + + aggCat.Catalogs.Add(catalog); + var eventListener = new ExportProviderListener(container, container); + + eventListener.VerifyCatalogRemove(() => aggCat.Catalogs.Remove(catalog), typeof(SampleCatalogExport)); + } + + [Export] + public class SampleCatalogExport2 { } + + [Fact] + [ActiveIssue(812029)] + public void CatalogMultipleAdds_ShouldFireEvents() + { + var catalog = new TypeCatalog(typeof(SampleCatalogExport)); + var aggCat = new AggregateCatalog(); + var container = ContainerFactory.Create(aggCat); + var eventListener = new ExportProviderListener(container, container); + + var otherAggCat = new AggregateCatalog(new TypeCatalog(typeof(SampleCatalogExport)), new TypeCatalog(typeof(SampleCatalogExport2))); + + eventListener.VerifyCatalogAdd(() => aggCat.Catalogs.Add(otherAggCat), typeof(SampleCatalogExport), typeof(SampleCatalogExport2)); + } + + [Fact] + public void CatalogNestedContainerAdds_ShouldFireEvents() + { + var catalog = new TypeCatalog(typeof(SampleCatalogExport)); + var aggCat = new AggregateCatalog(); + var parentContainer = ContainerFactory.Create(aggCat); + var container = ContainerFactory.Create(parentContainer); + var eventListener = new ExportProviderListener(parentContainer, container); + + eventListener.VerifyCatalogAdd(() => aggCat.Catalogs.Add(catalog), typeof(SampleCatalogExport)); + } + + public class ExportProviderListener + { + private CompositionContainer _container; + private ExportProvider _watchedProvider; + private string[] _expectedAdds; + private string[] _expectedRemoves; + private int _changedEventCount; + private int _changingEventCount; + + public ExportProviderListener(CompositionContainer container, ExportProvider watchExportProvider) + { + watchExportProvider.ExportsChanged += OnExportsChanged; + watchExportProvider.ExportsChanging += OnExportsChanging; + this._watchedProvider = watchExportProvider; + this._container = container; + } + + public void VerifyCompose(CompositionBatch batch) + { + this._expectedAdds = GetContractNames(batch.PartsToAdd); + this._expectedRemoves = GetContractNames(batch.PartsToRemove); + + this._container.Compose(batch); + + Assert.True(this._changingEventCount == 1); + Assert.True(this._changedEventCount == 1); + + ResetState(); + } + + public void VerifyCatalogAdd(Action doAdd, params Type[] expectedTypesAdded) + { + this._expectedAdds = GetContractNames(expectedTypesAdded); + + doAdd(); + + Assert.True(this._changingEventCount == 1); + Assert.True(this._changedEventCount == 1); + + ResetState(); + } + + public void VerifyCatalogRemove(Action doRemove, params Type[] expectedTypesRemoved) + { + this._expectedRemoves = GetContractNames(expectedTypesRemoved); + + doRemove(); + + Assert.True(this._changingEventCount == 1); + Assert.True(this._changedEventCount == 1); + + ResetState(); + } + + public void OnExportsChanging(object sender, ExportsChangeEventArgs args) + { + Assert.True(this._expectedAdds != null || this._expectedRemoves != null); + + if (this._expectedAdds == null) + { + Assert.Empty(args.AddedExports); + } + else + { + Assert.All(_expectedAdds, add => + { + Assert.False(this._container.IsPresent(add)); + }); + } + + if (this._expectedRemoves == null) + { + Assert.Empty(args.RemovedExports); + } + else + { + Assert.All(_expectedRemoves, remove => + { + Assert.True(this._container.IsPresent(remove)); + }); + } + + this._changingEventCount++; + } + + public void OnExportsChanged(object sender, ExportsChangeEventArgs args) + { + Assert.True(this._expectedAdds != null || this._expectedRemoves != null); + + if (this._expectedAdds == null) + { + Assert.Empty(args.AddedExports); + } + else + { + Assert.All(_expectedAdds, add => + { + Assert.True(this._container.IsPresent(add)); + }); + } + + if (this._expectedRemoves == null) + { + Assert.Empty(args.RemovedExports); + } + else + { + Assert.All(_expectedRemoves, remove => + { + Assert.False(this._container.IsPresent(remove)); + }); + } + + Assert.Null(args.AtomicComposition); + + this._changedEventCount++; + } + + private void ResetState() + { + this._expectedAdds = null; + this._expectedRemoves = null; + this._changedEventCount = 0; + this._changingEventCount = 0; + } + + private static string[] GetContractNames(IEnumerable definitions) + { + return definitions.Select(e => e.ContractName).ToArray(); + } + + private static string[] GetContractNames(IEnumerable parts) + { + return GetContractNames(parts.SelectMany(p => p.ExportDefinitions)); + } + + private static string[] GetContractNames(IEnumerable types) + { + return GetContractNames(types.Select(t => AttributedModelServices.CreatePartDefinition(t, null)).SelectMany(p => p.ExportDefinitions)); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/LifetimeTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/LifetimeTests.cs new file mode 100644 index 0000000000..af28b5037a --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/LifetimeTests.cs @@ -0,0 +1,1282 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.Linq; +using System.Reflection; +using System.UnitTesting; +using Xunit; + +namespace Tests.Integration +{ + public class LifetimeTests + { + [Export] + public class AnyPartSimple + { + + } + + [Export] + public class AnyPartDisposable : IDisposable + { + public bool IsDisposed { get; set; } + public void Dispose() + { + Assert.False(IsDisposed); + IsDisposed = true; + } + } + + [Export] + public class AnyPartRecomposable + { + [Import("Value", AllowRecomposition = true)] + public int Value { get; set; } + } + + [Export] + public class AnyPartDisposableRecomposable : IDisposable + { + [Import("Value", AllowRecomposition = true)] + public int Value { get; set; } + + public bool IsDisposed { get; set; } + public void Dispose() + { + Assert.False(IsDisposed); + IsDisposed = true; + } + } + + [Fact] + public void PartAddedViaAddExportedValue_ShouldNotBeDisposedWithContainer() + { + var container = new CompositionContainer(); + var disposablePart = new AnyPartDisposable(); + var batch = new CompositionBatch(); + batch.AddPart(batch); + container.Compose(batch); + + container.Dispose(); + Assert.False(disposablePart.IsDisposed); + } + + [Fact] + public void PartAddedTwice_AppearsTwice() + { + // You probably shouldn't be adding a part to the container twice, but it's not something we're going to check for and throw an exception on + var container = new CompositionContainer(); + var disposable = new AnyPartDisposable(); + var part = AttributedModelServices.CreatePart(disposable); + var batch = new CompositionBatch(); + batch.AddPart(part); + container.Compose(batch); + + batch = new CompositionBatch(); + batch.AddPart(part); + container.Compose(batch); + + var exports = container.GetExports(); + Assert.Equal(2, exports.Count()); + + container.Dispose(); + } + + [Fact] + public void AnyPart_Simple_ShouldNotBeCollected() + { + var catalog = new TypeCatalog(typeof(AnyPartSimple)); + var container = new CompositionContainer(catalog); + + var refTracker = new ReferenceTracker(); + + refTracker.AddReferencesNotExpectedToBeCollected( + container.GetExportedValue()); + + refTracker.CollectAndAssert(); + + GC.KeepAlive(container); + } + + [Fact] + public void AnyPart_Disposable_ShouldNotBeCollected() + { + var catalog = new TypeCatalog(typeof(AnyPartDisposable)); + var container = new CompositionContainer(catalog); + + var refTracker = new ReferenceTracker(); + + refTracker.AddReferencesNotExpectedToBeCollected( + container.GetExportedValue()); + + GC.KeepAlive(container); + } + + [Fact] + public void AnyPart_Disposable_ShouldBeDisposedWithContainer() + { + var catalog = new TypeCatalog(typeof(AnyPartDisposable)); + var container = new CompositionContainer(catalog); + + var exportedValue = container.GetExportedValue(); + + Assert.False(exportedValue.IsDisposed); + + container.Dispose(); + + Assert.True(exportedValue.IsDisposed, "AnyPart should be disposed with the container!"); + } + + [Fact] + public void AnyPart_RecomposabeImport_ShouldNotBeCollected() + { + var catalog = new TypeCatalog(typeof(AnyPartRecomposable)); + var container = new CompositionContainer(catalog); + + // Setup dependency + CompositionBatch batch = new CompositionBatch(); + var valueKey = batch.AddExportedValue("Value", 21); + container.Compose(batch); + batch = null; + + var refTracker = new ReferenceTracker(); + + refTracker.AddReferencesNotExpectedToBeCollected( + container.GetExportedValue()); + refTracker.CollectAndAssert(); + + // Lets make sure recomposition doesn't blow anything up here. + batch = new CompositionBatch(); + batch.RemovePart(valueKey); + batch.AddExportedValue("Value", 42); + container.Compose(batch); + batch = null; + + var exportedValue = (AnyPartRecomposable)refTracker.ReferencesNotExpectedToBeCollected[0].Target; + Assert.Equal(42, exportedValue.Value); + + GC.KeepAlive(container); + } + + [Fact] + public void AnyPart_DisposableRecomposabeImport_ShouldNotBeCollected() + { + var catalog = new TypeCatalog(typeof(AnyPartDisposableRecomposable)); + var container = new CompositionContainer(catalog); + + // Setup dependency + CompositionBatch batch = new CompositionBatch(); + var valueKey = batch.AddExportedValue("Value", 21); + container.Compose(batch); + batch = null; + + var refTracker = new ReferenceTracker(); + + refTracker.AddReferencesNotExpectedToBeCollected( + container.GetExportedValue()); + + refTracker.CollectAndAssert(); + + // Lets make sure recomposition doesn't blow anything up here. + batch = new CompositionBatch(); + batch.RemovePart(valueKey); + batch.AddExportedValue("Value", 42); + container.Compose(batch); + batch = null; + + var exportedValue = (AnyPartDisposableRecomposable)refTracker.ReferencesNotExpectedToBeCollected[0].Target; + Assert.Equal(42, exportedValue.Value); + + GC.KeepAlive(container); + + container.Dispose(); + + Assert.True(exportedValue.IsDisposed, "Any parts should be disposed with the container!"); + } + + [Export] + [PartCreationPolicy(CreationPolicy.Shared)] + public class SharedPartSimple + { + + } + + [Export] + [PartCreationPolicy(CreationPolicy.Shared)] + public class SharedPartDisposable : IDisposable + { + public bool IsDisposed { get; set; } + public void Dispose() + { + Assert.False(IsDisposed); + IsDisposed = true; + } + } + + [Export] + [PartCreationPolicy(CreationPolicy.Shared)] + public class SharedPartRecomposable + { + [Import("Value", AllowRecomposition = true)] + public int Value { get; set; } + } + + [Export] + [PartCreationPolicy(CreationPolicy.Shared)] + public class SharedPartDisposableRecomposable : IDisposable + { + [Import("Value", AllowRecomposition = true)] + public int Value { get; set; } + + public bool IsDisposed { get; set; } + public void Dispose() + { + Assert.False(IsDisposed); + IsDisposed = true; + } + } + + [Fact] + public void SharedPart_Simple_ShouldNotBeCollected() + { + var catalog = new TypeCatalog(typeof(SharedPartSimple)); + var container = new CompositionContainer(catalog); + + var refTracker = new ReferenceTracker(); + + refTracker.AddReferencesNotExpectedToBeCollected( + container.GetExportedValue()); + + refTracker.CollectAndAssert(); + + GC.KeepAlive(container); + } + + [Fact] + public void SharedPart_Disposable_ShouldNotBeCollected() + { + var catalog = new TypeCatalog(typeof(SharedPartDisposable)); + var container = new CompositionContainer(catalog); + + var refTracker = new ReferenceTracker(); + + refTracker.AddReferencesNotExpectedToBeCollected( + container.GetExportedValue()); + + refTracker.CollectAndAssert(); + + GC.KeepAlive(container); + } + + [Fact] + public void SharedPart_Disposable_ShouldBeDisposedWithContainer() + { + var catalog = new TypeCatalog(typeof(SharedPartDisposable)); + var container = new CompositionContainer(catalog); + + var export = container.GetExportedValue(); + + Assert.False(export.IsDisposed); + + container.Dispose(); + + Assert.True(export.IsDisposed, "SharedPart should be disposed with the container!"); + } + + [Fact] + public void SharedPart_RecomposabeImport_ShouldNotBeCollected() + { + var catalog = new TypeCatalog(typeof(SharedPartRecomposable)); + var container = new CompositionContainer(catalog); + + // Setup dependency + CompositionBatch batch = new CompositionBatch(); + var valueKey = batch.AddExportedValue("Value", 21); + container.Compose(batch); + batch = null; + + var refTracker = new ReferenceTracker(); + + refTracker.AddReferencesNotExpectedToBeCollected( + container.GetExportedValue()); + + refTracker.CollectAndAssert(); + + // Lets make sure recomposition doesn't blow anything up here. + batch = new CompositionBatch(); + batch.RemovePart(valueKey); + batch.AddExportedValue("Value", 42); + container.Compose(batch); + batch = null; + + var exportedValue = (SharedPartRecomposable)refTracker.ReferencesNotExpectedToBeCollected[0].Target; + Assert.Equal(42, exportedValue.Value); + + GC.KeepAlive(container); + } + + [Fact] + public void SharedPart_DisposableRecomposabeImport_ShouldNotBeCollected() + { + var catalog = new TypeCatalog(typeof(SharedPartDisposableRecomposable)); + var container = new CompositionContainer(catalog); + + // Setup dependency + CompositionBatch batch = new CompositionBatch(); + var valueKey = batch.AddExportedValue("Value", 21); + container.Compose(batch); + batch = null; + + var refTracker = new ReferenceTracker(); + + refTracker.AddReferencesNotExpectedToBeCollected( + container.GetExportedValue()); + + refTracker.CollectAndAssert(); + + // Lets make sure recomposition doesn't blow anything up here. + batch = new CompositionBatch(); + batch.RemovePart(valueKey); + batch.AddExportedValue("Value", 42); + container.Compose(batch); + batch = null; + + var exportedValue = (SharedPartDisposableRecomposable)refTracker.ReferencesNotExpectedToBeCollected[0].Target; + Assert.Equal(42, exportedValue.Value); + + container.Dispose(); + + Assert.True(exportedValue.IsDisposed, "Any parts should be disposed with the container!"); + } + + [Export] + [PartCreationPolicy(CreationPolicy.NonShared)] + public class NonSharedPartSimple + { + + } + + [Export] + [PartCreationPolicy(CreationPolicy.NonShared)] + public class NonSharedPartRecomposable + { + [Import("Value", AllowRecomposition = true)] + public int Value { get; set; } + } + + [Export] + [PartCreationPolicy(CreationPolicy.NonShared)] + public class NonSharedPartDisposable : IDisposable + { + public bool IsDisposed { get; set; } + public void Dispose() + { + Assert.False(IsDisposed); + IsDisposed = true; + } + } + + [Export] + [PartCreationPolicy(CreationPolicy.NonShared)] + public class NonSharedPartDisposableRecomposable : IDisposable + { + private int _value; + + [Import("Value", AllowRecomposition = true)] + public int Value + { + get + { + if (this.IsDisposed) + throw new ObjectDisposedException(this.GetType().Name); + return this._value; + } + set + { + if (this.IsDisposed) + throw new ObjectDisposedException(this.GetType().Name); + this._value = value; + } + } + + public bool IsDisposed { get; set; } + public void Dispose() + { + Assert.False(IsDisposed); + IsDisposed = true; + } + } + + [Fact] + public void NonSharedPart_Disposable_ShouldNotBeCollected() + { + var catalog = new TypeCatalog(typeof(NonSharedPartDisposable)); + var container = new CompositionContainer(catalog); + + var refTracker = new ReferenceTracker(); + + refTracker.AddReferencesNotExpectedToBeCollected( + container.GetExportedValue()); + + refTracker.CollectAndAssert(); + + GC.KeepAlive(container); + } + + [Fact] + public void NonSharedPart_Disposable_ShouldBeDisposedWithContainer() + { + var catalog = new TypeCatalog(typeof(NonSharedPartDisposable)); + var container = new CompositionContainer(catalog); + + var export = container.GetExportedValue(); + + Assert.False(export.IsDisposed); + + container.Dispose(); + + Assert.True(export.IsDisposed, "NonSharedParts should be disposed with the container!"); + } + + [Fact] + public void NonSharedPart_RecomposableImport_WithReference_ShouldNotBeCollected() + { + var catalog = new TypeCatalog(typeof(NonSharedPartRecomposable)); + var container = new CompositionContainer(catalog); + + // Setup dependency + CompositionBatch batch = new CompositionBatch(); + var valueKey = batch.AddExportedValue("Value", 21); + container.Compose(batch); + batch = null; + + var exportedValue = container.GetExportedValue(); + + var refTracker = new ReferenceTracker(); + + refTracker.AddReferencesNotExpectedToBeCollected(exportedValue); + + refTracker.CollectAndAssert(); + + // Recompose should work because we are still holding a reference to the exported value. + batch = new CompositionBatch(); + batch.RemovePart(valueKey); + batch.AddExportedValue("Value", 42); + container.Compose(batch); + batch = null; + + Assert.Equal(42, exportedValue.Value); + + GC.KeepAlive(container); + } + + [Fact] + public void NonSharedPart_DisposableRecomposabeImport_NoReference_ShouldNotBeCollected() + { + var catalog = new TypeCatalog(typeof(NonSharedPartDisposableRecomposable)); + var container = new CompositionContainer(catalog); + + // Setup dependency + CompositionBatch batch = new CompositionBatch(); + var valueKey = batch.AddExportedValue("Value", 21); + container.Compose(batch); + batch = null; + + var refTracker = new ReferenceTracker(); + + refTracker.AddReferencesNotExpectedToBeCollected( + container.GetExportedValue()); + + refTracker.CollectAndAssert(); + + // Recompose just to ensure we don't blow up, even though we don't expect anything to happen. + batch = new CompositionBatch(); + batch.RemovePart(valueKey); + batch.AddExportedValue("Value", 42); + container.Compose(batch); + batch = null; + + var exportedValue = (NonSharedPartDisposableRecomposable)refTracker.ReferencesNotExpectedToBeCollected[0].Target; + Assert.Equal(42, exportedValue.Value); + + GC.KeepAlive(container); + } + + [Export] + public class SharedState + { + public static int instanceNumber = 0; + public SharedState() + { + MyInstanceNumber = instanceNumber++; + } + + public int MyInstanceNumber { get; private set; } + } + + [PartCreationPolicy(CreationPolicy.NonShared)] + public class NonSharedState + { + [Import(AllowRecomposition = true)] + public SharedState State { set { ExportState = value; } } + + [Export("SharedFromNonShared")] + public SharedState ExportState { get; private set; } + } + + [Fact] + public void NonSharedPart_TwoRecomposablePartsSameExportedValue() + { + // This test is primarily used to ensure that we allow for multiple parts to be associated + // with the same exported value. + var catalog = new TypeCatalog(typeof(SharedState), typeof(NonSharedState)); + var container = new CompositionContainer(catalog); + + var export1 = container.GetExportedValue("SharedFromNonShared"); + var export2 = container.GetExportedValue("SharedFromNonShared"); + + // Same exported value that comes from two different recomposable part instances. + Assert.Equal(export1.MyInstanceNumber, export2.MyInstanceNumber); + } + + [Export] + [PartCreationPolicy(CreationPolicy.NonShared)] + public class SharedImporter + { + [Import(RequiredCreationPolicy = CreationPolicy.Shared)] + public AnyPartSimple AnyPartSimple { get; set; } + + [Import(RequiredCreationPolicy = CreationPolicy.Shared)] + public AnyPartDisposable AnyPartDisposable { get; set; } + + [Import(RequiredCreationPolicy = CreationPolicy.Shared)] + public AnyPartRecomposable AnyPartRecomposable { get; set; } + + [Import(RequiredCreationPolicy = CreationPolicy.Shared)] + public AnyPartDisposableRecomposable AnyPartDisposableRecomposable { get; set; } + } + + [Export] + [PartCreationPolicy(CreationPolicy.NonShared)] + public class NonSharedImporter + { + [Import(RequiredCreationPolicy = CreationPolicy.NonShared)] + public AnyPartSimple AnyPartSimple { get; set; } + + [Import(RequiredCreationPolicy = CreationPolicy.NonShared)] + public AnyPartDisposable AnyPartDisposable { get; set; } + + [Import(RequiredCreationPolicy = CreationPolicy.NonShared)] + public AnyPartRecomposable AnyPartRecomposable { get; set; } + + [Import(RequiredCreationPolicy = CreationPolicy.NonShared)] + public AnyPartDisposableRecomposable AnyPartDisposableRecomposable { get; set; } + } + + private static CompositionContainer GetContainer() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(LifetimeTests).GetNestedTypes(BindingFlags.Public)); + CompositionBatch batch = new CompositionBatch(); + batch.AddExportedValue("Value", 21); + container.Compose(batch); + return container; + } + + [Fact] + public void GetReleaseExport_SharedRoot_ShouldNotDisposeChain() + { + var container = GetContainer(); + + var export = container.GetExport>(); + var exportedValue = export.Value; + + container.ReleaseExport(export); + + Assert.False(exportedValue.AnyPartDisposable.IsDisposed); + Assert.False(exportedValue.AnyPartDisposableRecomposable.IsDisposed); + } + + [Fact] + public void AddRemovePart_SharedRoot_ShouldNotDisposeChain() + { + var container = GetContainer(); + + var exportedValue = new SharedImporter(); + + CompositionBatch batch = new CompositionBatch(); + var part = batch.AddPart(exportedValue); + container.Compose(batch); + + batch = new CompositionBatch(); + batch.RemovePart(part); + container.Compose(batch); + + Assert.False(exportedValue.AnyPartDisposable.IsDisposed); + Assert.False(exportedValue.AnyPartDisposableRecomposable.IsDisposed); + } + + [Fact] + public void ContainerDispose_SharedRoot_ShouldDisposeChain() + { + var container = GetContainer(); + + var export = container.GetExport(); + var exportedValue = export.Value; + + container.Dispose(); + + Assert.True(exportedValue.AnyPartDisposable.IsDisposed); + Assert.True(exportedValue.AnyPartDisposableRecomposable.IsDisposed); + } + + [Fact] + public void GetReleaseExport_NonSharedRoot_ShouldDisposeChain() + { + var container = GetContainer(); + + var exports = new List>(); + var exportedValues = new List(); + + // Executing this 100 times to help uncover any GC bugs + for (int i = 0; i < 100; i++) + { + var export = container.GetExport(); + var exportedValue = export.Value; + + exports.Add(export); + exportedValues.Add(exportedValue); + } + + for (int i = 0; i < 100; i++) + { + var export = exports[i]; + var exportedValue = exportedValues[i]; + + container.ReleaseExport(export); + + Assert.True(exportedValue.AnyPartDisposable.IsDisposed); + Assert.True(exportedValue.AnyPartDisposableRecomposable.IsDisposed); + } + } + + public void GetReleaseExport_NonSharedRoot_ShouldDisposeChain_WithMetadata() + { + var container = GetContainer(); + + var exports = new List>>(); + var exportedValues = new List(); + + // Executing this 100 times to help uncover any GC bugs + for (int i = 0; i < 100; i++) + { + var export = container.GetExport>(); + var exportedValue = export.Value; + + exports.Add(export); + exportedValues.Add(exportedValue); + } + + for (int i = 0; i < 100; i++) + { + var export = exports[i]; + var exportedValue = exportedValues[i]; + + container.ReleaseExport(export); + + Assert.True(exportedValue.AnyPartDisposable.IsDisposed); + Assert.True(exportedValue.AnyPartDisposableRecomposable.IsDisposed); + } + } + + [Fact] + public void ReleaseExports_ShouldDispose_NonSharedParts() + { + var container = GetContainer(); + + var export1 = container.GetExport(); + var exportedValue1 = export1.Value; + + var export2 = container.GetExport(); + var exportedValue2 = export2.Value; + + container.ReleaseExports(new[] { export1, export2 }); + + Assert.True(exportedValue1.AnyPartDisposable.IsDisposed); + Assert.True(exportedValue1.AnyPartDisposableRecomposable.IsDisposed); + + Assert.True(exportedValue2.AnyPartDisposable.IsDisposed); + Assert.True(exportedValue2.AnyPartDisposableRecomposable.IsDisposed); + } + + [Fact] + public void AddRemovePart_NonSharedRoot_ShouldDisposeChain() + { + var container = GetContainer(); + + var exportedValue = new NonSharedImporter(); + + CompositionBatch batch = new CompositionBatch(); + var part = batch.AddPart(exportedValue); + container.Compose(batch); + + batch = new CompositionBatch(); + batch.RemovePart(part); + container.Compose(batch); + + Assert.True(exportedValue.AnyPartDisposable.IsDisposed); + Assert.True(exportedValue.AnyPartDisposableRecomposable.IsDisposed); + } + + [Fact] + public void ContainerDispose_NonSharedRoot_ShouldNotDisposeChain() + { + var container = GetContainer(); + + var export = container.GetExport(); + var exportedValue = export.Value; + + container.Dispose(); + + Assert.True(exportedValue.AnyPartDisposable.IsDisposed); + Assert.True(exportedValue.AnyPartDisposableRecomposable.IsDisposed); + } + + [Fact] + public void GetReleaseExport_NonSharedPart_ShouldNotRecomposeAfterRelease() + { + var catalog = new TypeCatalog(typeof(NonSharedPartRecomposable)); + var container = new CompositionContainer(catalog); + + // Setup dependency + CompositionBatch batch = new CompositionBatch(); + var valueKey = batch.AddExportedValue("Value", 21); + container.Compose(batch); + + var export = container.GetExport(); + var exportedValue = export.Value; + + Assert.Equal(21, exportedValue.Value); + + container.ReleaseExport(export); + + // Recompose just to ensure we don't blow up, even though we don't expect anything to happen. + batch = new CompositionBatch(); + batch.RemovePart(valueKey); + batch.AddExportedValue("Value", 42); + container.Compose(batch); + + Assert.Equal(21, exportedValue.Value); + } + + [Fact] + public void GetExportManualDisposeThenRecompose_NonSharedDisposableRecomposablePart_ShouldThrowComposition() + { + var catalog = new TypeCatalog(typeof(NonSharedPartDisposableRecomposable)); + var container = new CompositionContainer(catalog); + + // Setup dependency + CompositionBatch batch = new CompositionBatch(); + var valueKey = batch.AddExportedValue("Value", 21); + container.Compose(batch); + + var export = container.GetExport(); + var exportedValue = export.Value; + + Assert.Equal(21, exportedValue.Value); + + exportedValue.Dispose(); + + // Recompose should cause a ObjectDisposedException. + batch = new CompositionBatch(); + batch.RemovePart(valueKey); + batch.AddExportedValue("Value", 42); + + CompositionAssert.ThrowsError( + ErrorId.ImportEngine_PartCannotActivate, // Cannot activate part because + ErrorId.ReflectionModel_ImportThrewException, // Import threw an exception + RetryMode.DoNotRetry, + () => + { + container.Compose(batch); + }); + } + + [Export] + public class MyImporter + { + [Import(AllowDefault = true, AllowRecomposition = true, RequiredCreationPolicy = CreationPolicy.NonShared)] + public AnyPartDisposable AnyPartDisposable { get; set; } + } + + [Fact] + public void RecomposeCausesOldImportedValuesToBeDisposed() + { + var cat = new AggregateCatalog(); + var cat1 = new TypeCatalog(typeof(AnyPartDisposable)); + + cat.Catalogs.Add(new TypeCatalog(typeof(MyImporter))); + cat.Catalogs.Add(cat1); + + var container = new CompositionContainer(cat); + + var importer = container.GetExportedValue(); + + var anyPart = importer.AnyPartDisposable; + + Assert.False(anyPart.IsDisposed); + Assert.IsType(anyPart); + + // Remove the instance of MyClass1 + cat.Catalogs.Remove(cat1); + + Assert.Null(importer.AnyPartDisposable); + Assert.True(anyPart.IsDisposed); + } + + private static CompositionContainer CreateParentChildContainerWithNonSharedImporter() + { + var parentCat = CatalogFactory.CreateAttributed(typeof(AnyPartDisposable), + typeof(AnyPartDisposableRecomposable), + typeof(AnyPartRecomposable), + typeof(AnyPartSimple)); + var parent = new CompositionContainer(parentCat); + CompositionBatch batch = new CompositionBatch(); + batch.AddExportedValue("Value", 21); + parent.Compose(batch); + + var childCat = CatalogFactory.CreateAttributed(typeof(NonSharedImporter)); + var child = new CompositionContainer(childCat, parent); + + return child; + } + + [Fact] + public void ChildContainerGetReleaseExport_NonSharedRoot_ShouldDisposeChain() + { + var child = CreateParentChildContainerWithNonSharedImporter(); + + var export = child.GetExport(); + var exportedValue = export.Value; + + child.ReleaseExport(export); + + Assert.True(exportedValue.AnyPartDisposable.IsDisposed); + Assert.True(exportedValue.AnyPartDisposableRecomposable.IsDisposed); + } + + [Fact] + public void ChildContainerAddRemovePart_NonSharedRoot_ShouldDisposeChain() + { + var child = CreateParentChildContainerWithNonSharedImporter(); + + var exportedValue = new NonSharedImporter(); + + CompositionBatch batch = new CompositionBatch(); + var part = batch.AddPart(exportedValue); + child.Compose(batch); + + batch = new CompositionBatch(); + batch.RemovePart(part); + child.Compose(batch); + + Assert.True(exportedValue.AnyPartDisposable.IsDisposed); + Assert.True(exportedValue.AnyPartDisposableRecomposable.IsDisposed); + } + + [Fact] + public void ChildContainerAddRemovePart_NonSharedRoot_ShouldNotDisposeChain() + { + var child = CreateParentChildContainerWithNonSharedImporter(); + + var exportedValue = child.GetExportedValue(); + + child.Dispose(); + + Assert.False(exportedValue.AnyPartDisposable.IsDisposed); + Assert.False(exportedValue.AnyPartDisposableRecomposable.IsDisposed); + } + + [Fact] + [ActiveIssue(25498)] + public void NonSharedPart_Simple_ShouldBeCollected() + { + var catalog = new TypeCatalog(typeof(NonSharedPartSimple)); + var container = new CompositionContainer(catalog); + + var refTracker = new ReferenceTracker(); + + refTracker.AddReferencesExpectedToBeCollected( + container.GetExportedValue()); + + refTracker.CollectAndAssert(); + + GC.KeepAlive(container); + } + + [Fact] + [ActiveIssue(25498)] + public void ContainerDispose_SharedPart_ShouldCollectWholeObjectChain() + { + // Test only works properly with while using the real ConditionalWeakTable + var container = GetContainer(); + + var export = container.GetExport(); + var exportedValue = export.Value; + + container.Dispose(); + + var refTracker = new ReferenceTracker(); + + refTracker.AddReferencesExpectedToBeCollected( + exportedValue, + exportedValue.AnyPartDisposable, + exportedValue.AnyPartDisposableRecomposable, + exportedValue.AnyPartRecomposable, + exportedValue.AnyPartSimple); + + export = null; + exportedValue = null; + + refTracker.CollectAndAssert(); + + GC.KeepAlive(container); + } + + [Fact] + [ActiveIssue(25498)] + public void AddRemovePart_SharedPart_ShouldCollectOnlyRoot() + { + var container = GetContainer(); + + var exportedValue = new SharedImporter(); + + CompositionBatch batch = new CompositionBatch(); + var part = batch.AddPart(exportedValue); + container.Compose(batch); + batch = null; + + batch = new CompositionBatch(); + batch.RemovePart(part); + container.Compose(batch); + batch = null; + + var refTracker = new ReferenceTracker(); + + refTracker.AddReferencesExpectedToBeCollected( + exportedValue); + + refTracker.AddReferencesNotExpectedToBeCollected( + exportedValue.AnyPartDisposable, + exportedValue.AnyPartDisposableRecomposable, + exportedValue.AnyPartRecomposable, + exportedValue.AnyPartSimple); + + part = null; + exportedValue = null; + + refTracker.CollectAndAssert(); + + GC.KeepAlive(container); + } + + [Fact] + [ActiveIssue(25498)] + public void AddRemovePart_NonSharedPart_ShouldCollectWholeObjectChain() + { + var container = GetContainer(); + + var exportedValue = new NonSharedImporter(); + + CompositionBatch batch = new CompositionBatch(); + var part = batch.AddPart(exportedValue); + container.Compose(batch); + batch = null; + + batch = new CompositionBatch(); + batch.RemovePart(part); + container.Compose(batch); + batch = null; + + var refTracker = new ReferenceTracker(); + + refTracker.AddReferencesExpectedToBeCollected( + exportedValue, + exportedValue.AnyPartDisposable, + exportedValue.AnyPartDisposableRecomposable, + exportedValue.AnyPartRecomposable, + exportedValue.AnyPartSimple); + + part = null; + exportedValue = null; + + refTracker.CollectAndAssert(); + + GC.KeepAlive(container); + } + + [Fact] + [ActiveIssue(25498)] + public void ContainerDispose_NonSharedPart_ShouldCollectWholeObjectChain() + { + // Test only works properly with while using the real ConditionalWeakTable + var container = GetContainer(); + + var export = container.GetExport(); + var exportedValue = export.Value; + + container.Dispose(); + + var refTracker = new ReferenceTracker(); + + refTracker.AddReferencesExpectedToBeCollected( + exportedValue, + exportedValue.AnyPartDisposable, + exportedValue.AnyPartDisposableRecomposable, + exportedValue.AnyPartRecomposable, + exportedValue.AnyPartSimple); + + export = null; + exportedValue = null; + + refTracker.CollectAndAssert(); + + GC.KeepAlive(container); + } + + [Fact] + [ActiveIssue(25498)] + public void NonSharedImporter_ReleaseReference_ShouldCollectWholeChain() + { + var container = GetContainer(); + + var export = container.GetExport(); + var exportedValue = export.Value; + + var refTracker = new ReferenceTracker(); + + // Non-Disposable references in the chain should be GC'ed + refTracker.AddReferencesExpectedToBeCollected( + exportedValue, + exportedValue.AnyPartRecomposable, + exportedValue.AnyPartSimple); + + // Disposable references in the chain should NOT be GC'ed + refTracker.AddReferencesNotExpectedToBeCollected( + exportedValue.AnyPartDisposable, + exportedValue.AnyPartDisposableRecomposable); + + export = null; + exportedValue = null; + + refTracker.CollectAndAssert(); + + GC.KeepAlive(container); + } + + [Fact] + [ActiveIssue(25498)] + public void ChildContainerDispose_NonSharedPart_ShouldOnlyCleanupChildAndSimpleNonShared() + { + var child = CreateParentChildContainerWithNonSharedImporter(); + + var exportedValue = child.GetExportedValue(); + + child.Dispose(); + + var refTracker = new ReferenceTracker(); + + refTracker.AddReferencesExpectedToBeCollected( + exportedValue, // object in child + exportedValue.AnyPartSimple, // No reference parent so collected. + exportedValue.AnyPartRecomposable); + + // These are in the parent and will not be cleaned out + refTracker.AddReferencesNotExpectedToBeCollected( + exportedValue.AnyPartDisposable, + exportedValue.AnyPartDisposableRecomposable); + + exportedValue = null; + + refTracker.CollectAndAssert(); + + GC.KeepAlive(child); + } + + [Fact] + [ActiveIssue(25498)] + public void ChildContainerGetReleaseExport_NonSharedPart_ShouldCollectWholeObjectChain() + { + var child = CreateParentChildContainerWithNonSharedImporter(); + + var export = child.GetExport(); + var exportedValue = export.Value; + + child.ReleaseExport(export); + + var refTracker = new ReferenceTracker(); + + refTracker.AddReferencesExpectedToBeCollected( + exportedValue, + exportedValue.AnyPartDisposable, + exportedValue.AnyPartDisposableRecomposable, + exportedValue.AnyPartRecomposable, + exportedValue.AnyPartSimple); + + export = null; + exportedValue = null; + + refTracker.CollectAndAssert(); + + GC.KeepAlive(child); + } + + [Fact] + [ActiveIssue(25498)] + public void NonSharedPart_RecomposableImport_NoReference_ShouldBeCollected() + { + var catalog = new TypeCatalog(typeof(NonSharedPartRecomposable)); + var container = new CompositionContainer(catalog); + + // Setup dependency + CompositionBatch batch = new CompositionBatch(); + var valueKey = batch.AddExportedValue("Value", 21); + container.Compose(batch); + batch = null; + + var refTracker = new ReferenceTracker(); + + refTracker.AddReferencesExpectedToBeCollected( + container.GetExportedValue()); + + refTracker.CollectAndAssert(); + + // Recompose just to ensure we don't blow up, even though we don't expect anything to happen. + batch = new CompositionBatch(); + batch.RemovePart(valueKey); + batch.AddExportedValue("Value", 42); + container.Compose(batch); + batch = null; + + GC.KeepAlive(container); + } + + [Fact] + [ActiveIssue(25498)] + public void ChildContainerAddRemovePart_NonSharedPart_ShouldCollectWholeObjectChain() + { + var child = CreateParentChildContainerWithNonSharedImporter(); + + var exportedValue = new NonSharedImporter(); + + CompositionBatch batch = new CompositionBatch(); + var part = batch.AddPart(exportedValue); + child.Compose(batch); + batch = null; + + batch = new CompositionBatch(); + batch.RemovePart(part); + child.Compose(batch); + batch = null; + + var refTracker = new ReferenceTracker(); + + refTracker.AddReferencesExpectedToBeCollected( + exportedValue, + exportedValue.AnyPartDisposable, + exportedValue.AnyPartDisposableRecomposable, + exportedValue.AnyPartRecomposable, + exportedValue.AnyPartSimple); + + part = null; + exportedValue = null; + + refTracker.CollectAndAssert(); + + GC.KeepAlive(child); + } + + [Fact] + [ActiveIssue(25498)] + public void GetReleaseExport_SharedPart_ShouldCollectOnlyRoot() + { + var container = GetContainer(); + + var export = container.GetExport(); + var exportedValue = export.Value; + + container.ReleaseExport(export); + + var refTracker = new ReferenceTracker(); + + refTracker.AddReferencesExpectedToBeCollected( + exportedValue); + + refTracker.AddReferencesNotExpectedToBeCollected( + exportedValue.AnyPartDisposable, + exportedValue.AnyPartDisposableRecomposable, + exportedValue.AnyPartRecomposable, + exportedValue.AnyPartSimple); + + export = null; + exportedValue = null; + + refTracker.CollectAndAssert(); + + GC.KeepAlive(container); + } + + [Fact] + [ActiveIssue(25498)] + public void GetReleaseExport_NonSharedPart_ShouldCollectWholeObjectChain() + { + var container = GetContainer(); + + var export = container.GetExport(); + var exportedValue = export.Value; + + container.ReleaseExport(export); + + var refTracker = new ReferenceTracker(); + + refTracker.AddReferencesExpectedToBeCollected( + exportedValue, + exportedValue.AnyPartDisposable, + exportedValue.AnyPartDisposableRecomposable, + exportedValue.AnyPartRecomposable, + exportedValue.AnyPartSimple); + + export = null; + exportedValue = null; + + refTracker.CollectAndAssert(); + + GC.KeepAlive(container); + } + + [Fact] + public void ReleaseExports_ShouldWorkWithExportCollection() + { + var container = GetContainer(); + var exports = container.GetExports(); + + Assert.True(exports.Count() > 0); + + var exportedValues = exports.Select(export => export.Value).ToList(); + + container.ReleaseExports(exports); + + foreach (var obj in exportedValues) + { + Assert.True(obj.AnyPartDisposable.IsDisposed); + Assert.True(obj.AnyPartDisposableRecomposable.IsDisposed); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/RecompositionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/RecompositionTests.cs new file mode 100644 index 0000000000..a32cb4e8af --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/RecompositionTests.cs @@ -0,0 +1,558 @@ +// 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.ComponentModel.Composition; +using System.ComponentModel.Composition.Hosting; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using Xunit; + +namespace Tests.Integration +{ + public class RecompositionTests + { + public class Class_OptIn_AllowRecompositionImports + { + [Import("Value", AllowRecomposition = true)] + public int Value { get; set; } + } + + [Fact] + public void Import_OptIn_AllowRecomposition() + { + var container = new CompositionContainer(); + var importer = new Class_OptIn_AllowRecompositionImports(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + var valueKey = batch.AddExportedValue("Value", 21); + container.Compose(batch); + + // Initial compose Value should be 21 + Assert.Equal(21, importer.Value); + + // Recompose Value to be 42 + batch = new CompositionBatch(); + batch.RemovePart(valueKey); + batch.AddExportedValue("Value", 42); + container.Compose(batch); + + Assert.Equal(42, importer.Value); + } + + public class Class_OptOut_AllowRecompositionImports + { + [Import("Value", AllowRecomposition = false)] + public int Value { get; set; } + } + + [Fact] + public void Import_OptOut_AllowRecomposition() + { + var container = new CompositionContainer(); + var importer = new Class_OptOut_AllowRecompositionImports(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + var valueKey = batch.AddExportedValue("Value", 21); + container.Compose(batch); + + // Initial compose Value should be 21 + Assert.Equal(21, importer.Value); + + // Reset value to ensure it doesn't get set to same value again + importer.Value = -21; + + // Recompose Value to be 42 + batch = new CompositionBatch(); + batch.RemovePart(valueKey); + batch.AddExportedValue("Value", 42); + // After rejection batch failures throw ChangeRejectedException to indicate that + // the failure did not affect the container + Assert.Throws(() => + { + container.Compose(batch); + }); + + Assert.Equal(-21, importer.Value); + } + + public class Class_Default_AllowRecompositionImports + { + [Import("Value")] + public int Value { get; set; } + } + + [Fact] + public void Import_Default_AllowRecomposition() + { + var container = new CompositionContainer(); + var importer = new Class_Default_AllowRecompositionImports(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + var valueKey = batch.AddExportedValue("Value", 21); + container.Compose(batch); + + // Initial compose Value should be 21 + Assert.Equal(21, importer.Value); + + // Reset value to ensure it doesn't get set to same value again + importer.Value = -21; + + // Recompose Value to be 42 + batch = new CompositionBatch(); + batch.RemovePart(valueKey); + batch.AddExportedValue("Value", 42); + // After rejection batch failures throw ChangeRejectedException to indicate that + // the failure did not affect the container + Assert.Throws(() => + { + container.Compose(batch); + }); + + Assert.Equal(-21, importer.Value); + } + + public class Class_BothOptInAndOptOutRecompositionImports + { + [Import("Value", AllowRecomposition = true)] + public int RecomposableValue { get; set; } + + [Import("Value", AllowRecomposition = false)] + public int NonRecomposableValue { get; set; } + } + + [Fact] + public void Import_BothOptInAndOptOutRecomposition() + { + var container = new CompositionContainer(); + var importer = new Class_BothOptInAndOptOutRecompositionImports(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + var valueKey = batch.AddExportedValue("Value", 21); + container.Compose(batch); + + // Initial compose values should be 21 + Assert.Equal(21, importer.RecomposableValue); + Assert.Equal(21, importer.NonRecomposableValue); + + // Reset value to ensure it doesn't get set to same value again + importer.NonRecomposableValue = -21; + importer.RecomposableValue = -21; + + // Recompose Value to be 42 + batch = new CompositionBatch(); + batch.RemovePart(valueKey); + batch.AddExportedValue("Value", 42); + // After rejection batch failures throw ChangeRejectedException to indicate that + // the failure did not affect the container + Assert.Throws(() => + { + container.Compose(batch); + }); + + Assert.Equal(-21, importer.NonRecomposableValue); + // The batch rejection means that the recomposable value shouldn't change either + Assert.Equal(-21, importer.RecomposableValue); + } + + public class Class_MultipleOptInRecompositionImportsWithDifferentContracts + { + [Import("Value1", AllowRecomposition = true)] + public int Value1 { get; set; } + + [Import("Value2", AllowRecomposition = true)] + public int Value2 { get; set; } + } + + [Fact] + public void Import_OptInRecomposition_Multlple() + { + var container = new CompositionContainer(); + var importer = new Class_MultipleOptInRecompositionImportsWithDifferentContracts(); + + CompositionBatch batch = new CompositionBatch(); + batch.AddPart(importer); + var value1Key = batch.AddExportedValue("Value1", 21); + var value2Key = batch.AddExportedValue("Value2", 23); + container.Compose(batch); + + Assert.Equal(21, importer.Value1); + Assert.Equal(23, importer.Value2); + + // Reset value to ensure it doesn't get set to same value again + importer.Value1 = -21; + importer.Value2 = -23; + + // Recompose Value to be 42 + batch = new CompositionBatch(); + batch.RemovePart(value1Key); + batch.AddExportedValue("Value1", 42); + container.Compose(batch); + + Assert.Equal(42, importer.Value1); + Assert.Equal(-23, importer.Value2); + } + + [PartNotDiscoverable] + public class MyName + { + public MyName(string name) + { + this.Name = name; + } + + [Export("Name")] + public string Name { get; private set; } + } + + [PartNotDiscoverable] + public class Spouse + { + public Spouse(string name) + { + this.Name = name; + } + + [Export("Friend")] + [ExportMetadata("Relationship", "Wife")] + public string Name { get; private set; } + } + + [PartNotDiscoverable] + public class Child + { + public Child(string name) + { + this.Name = name; + } + + [Export("Child")] + public string Name { get; private set; } + } + + [PartNotDiscoverable] + public class Job + { + public Job(string name) + { + this.Name = name; + } + + [Export("Job")] + public string Name { get; private set; } + } + + [PartNotDiscoverable] + public class Friend + { + public Friend(string name) + { + this.Name = name; + } + + [Export("Friend")] + public string Name { get; private set; } + } + + public interface IRelationshipView + { + string Relationship { get; } + } + + [PartNotDiscoverable] + public class Me + { + [Import("Name", AllowRecomposition = true)] + public string Name { get; set; } + + [Import("Job", AllowDefault = true, AllowRecomposition = true)] + public string Job { get; set; } + + [ImportMany("Child")] + public string[] Children { get; set; } + + [ImportMany("Friend")] + public Lazy[] Relatives { get; set; } + + [ImportMany("Friend", AllowRecomposition = true)] + public string[] Friends { get; set; } + } + + [Fact] + public void Recomposition_IntegrationTest() + { + var container = new CompositionContainer(); + var batch = new CompositionBatch(); + + var me = new Me(); + batch.AddPart(me); + var namePart = batch.AddPart(new MyName("Blake")); + batch.AddPart(new Spouse("Barbara")); + batch.AddPart(new Friend("Steve")); + batch.AddPart(new Friend("Joyce")); + container.Compose(batch); + Assert.Equal(me.Name, "Blake"); + Assert.Equal(me.Job, null); + Assert.Equal(me.Friends.Length, 3); + Assert.Equal(me.Relatives.Length, 1); + Assert.Equal(me.Children.Length, 0); + + // Can only have one name + Assert.Throws(() => + container.ComposeParts(new MyName("Blayke"))); + + batch = new CompositionBatch(); + batch.AddPart(new MyName("Blayke")); + batch.RemovePart(namePart); + container.Compose(batch); + Assert.Equal(me.Name, "Blayke"); + + batch = new CompositionBatch(); + var jobPart = batch.AddPart(new Job("Architect")); + container.Compose(batch); + Assert.Equal(me.Job, "Architect"); + + batch = new CompositionBatch(); + batch.AddPart(new Job("Chimney Sweep")); + container.Compose(batch); + Assert.True(me.Job == null, "More than one of an optional import should result in the default value"); + + batch = new CompositionBatch(); + batch.RemovePart(jobPart); + container.Compose(batch); + Assert.Equal(me.Job, "Chimney Sweep"); + + batch = new CompositionBatch(); + + // Can only have one spouse because they aren't recomposable + Assert.Throws(() => + container.ComposeParts(new Spouse("Cameron"))); + + Assert.Equal(me.Relatives.Length, 1); + + batch = new CompositionBatch(); + batch.AddPart(new Friend("Graham")); + container.Compose(batch); + Assert.Equal(me.Friends.Length, 4); + Assert.Equal(me.Relatives.Length, 1); + } + + public class FooWithOptionalImport + { + private FooWithSimpleImport _optionalImport; + + [Import(AllowDefault = true, AllowRecomposition = true)] + public FooWithSimpleImport OptionalImport + { + get + { + return this._optionalImport; + } + set + { + if (value != null) + { + this._optionalImport = value; + + Assert.True(!string.IsNullOrEmpty(this._optionalImport.SimpleValue), "Value should have it's imports satisfied"); + } + } + } + } + + [Export] + public class FooWithSimpleImport + { + [Import("FooSimpleImport")] + public string SimpleValue { get; set; } + } + + [Fact] + public void PartsShouldHaveImportsSatisfiedBeforeBeingUsedToSatisfyRecomposableImports() + { + var container = new CompositionContainer(); + var fooOptional = new FooWithOptionalImport(); + + container.ComposeParts(fooOptional); + container.ComposeExportedValue("FooSimpleImport", "NotNullOrEmpty"); + container.ComposeParts(new FooWithSimpleImport()); + + Assert.True(!string.IsNullOrEmpty(fooOptional.OptionalImport.SimpleValue)); + } + + [Export] + public class RootImportRecomposable + { + [Import(AllowDefault = true, AllowRecomposition = true)] + public NonSharedImporter Importer { get; set; } + } + + [Export] + [PartCreationPolicy(CreationPolicy.NonShared)] + public class NonSharedImporter + { + [Import] + public SimpleImport Import { get; set; } + } + + [Export] + public class RootImporter + { + [Import] + public SimpleImport Import { get; set; } + } + + [Export] + public class SimpleImport + { + public int Property { get { return 42; } } + } + + [Fact] + [ActiveIssue(733533)] + public void RemoveCatalogWithNonSharedPartWithRequiredImport() + { + var typeCatalog = new TypeCatalog(typeof(NonSharedImporter), typeof(SimpleImport)); + var aggCatalog = new AggregateCatalog(); + var container = new CompositionContainer(aggCatalog); + + aggCatalog.Catalogs.Add(typeCatalog); + aggCatalog.Catalogs.Add(new TypeCatalog(typeof(RootImportRecomposable))); + + var rootExport = container.GetExport(); + var root = rootExport.Value; + + Assert.Equal(42, root.Importer.Import.Property); + + aggCatalog.Catalogs.Remove(typeCatalog); + + Assert.Null(root.Importer); + } + + [Fact] + [ActiveIssue(734123)] + public void GetExportResultShouldBePromise() + { + var typeCatalog = new TypeCatalog(typeof(RootImporter), typeof(SimpleImport)); + var aggCatalog = new AggregateCatalog(); + var container = new CompositionContainer(aggCatalog); + + aggCatalog.Catalogs.Add(typeCatalog); + + var root = container.GetExport(); + + Assert.Throws(() => + aggCatalog.Catalogs.Remove(typeCatalog) + ); + + var value = root.Value; + Assert.Equal(42, value.Import.Property); + } + + [Fact] + // [WorkItem(789269)] + public void TestRemovingAndReAddingMultipleDefinitionsFromCatalog() + { + var fixedParts = new TypeCatalog(typeof(RootMultipleImporter), typeof(ExportedService)); + var changingParts = new TypeCatalog(typeof(Exporter1), typeof(Exporter2)); + var catalog = new AggregateCatalog(); + catalog.Catalogs.Add(fixedParts); + catalog.Catalogs.Add(changingParts); + + var container = new CompositionContainer(catalog); + + var root = container.GetExport().Value; + + Assert.Equal(2, root.Imports.Length); + + catalog.Catalogs.Remove(changingParts); + + Assert.Equal(0, root.Imports.Length); + + catalog.Catalogs.Add(changingParts); + + Assert.Equal(2, root.Imports.Length); + } + + [Export] + public class RootMultipleImporter + { + [ImportMany(AllowRecomposition = true)] + public IExportedInterface[] Imports { get; set; } + } + + public interface IExportedInterface + { + + } + + [Export(typeof(IExportedInterface))] + public class Exporter1 : IExportedInterface + { + [Import] + public ExportedService Service { get; set; } + } + + [Export(typeof(IExportedInterface))] + public class Exporter2 : IExportedInterface + { + [Import] + public ExportedService Service { get; set; } + } + + [Export] + public class ExportedService + { + + } + + [Fact] + [ActiveIssue(762215)] + public void TestPartCreatorResurrection() + { + var container = new CompositionContainer(new TypeCatalog(typeof(NonDisposableImportsDisposable), typeof(PartImporter))); + var exports = container.GetExports>(); + Assert.Equal(0, exports.Count()); + container.ComposeParts(new DisposablePart()); + exports = container.GetExports>(); + Assert.Equal(1, exports.Count()); + } + + [Export] + public class PartImporter + { + [Import] + public PartType Creator { get; set; } + } + + [Export] + public class NonDisposableImportsDisposable + { + [Import] + public DisposablePart Part { get; set; } + } + + [Export] + public class Part + { + + } + + [Export] + [PartCreationPolicy(CreationPolicy.NonShared)] + public class DisposablePart : Part, IDisposable + { + public bool Disposed { get; private set; } + public void Dispose() + { + Disposed = true; + } + } + + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/RejectionTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/RejectionTests.cs new file mode 100644 index 0000000000..03a36679d0 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/RejectionTests.cs @@ -0,0 +1,508 @@ +// 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.ComponentModel.Composition; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.Linq; +using System.UnitTesting; +using Xunit; + +namespace Tests.Integration +{ + public class RejectionTests + { + public interface IExtension + { + int Id { get; set; } + } + + [Export] + public class MyImporter + { + [ImportMany(AllowRecomposition = true)] + public IExtension[] Extensions { get; set; } + } + + [Export(typeof(IExtension))] + public class Extension1 : IExtension + { + [Import("IExtension.IdValue")] + public int Id { get; set; } + } + + [Export(typeof(IExtension))] + public class Extension2 : IExtension + { + [Import("IExtension.IdValue2")] + public int Id { get; set; } + } + + [Fact] + public void Rejection_ExtensionLightUp_AddedViaBatch() + { + var container = ContainerFactory.CreateWithAttributedCatalog( + typeof(MyImporter), + typeof(Extension1), + typeof(Extension2)); + + var importer = container.GetExportedValue(); + + Assert.Equal(0, importer.Extensions.Length); + + container.ComposeExportedValue("IExtension.IdValue", 10); + + Assert.Equal(1, importer.Extensions.Length); + Assert.Equal(10, importer.Extensions[0].Id); + + container.ComposeExportedValue("IExtension.IdValue2", 20); + + Assert.Equal(2, importer.Extensions.Length); + Assert.Equal(10, importer.Extensions[0].Id); + Assert.Equal(20, importer.Extensions[1].Id); + } + + public class ExtensionValues + { + [Export("IExtension.IdValue")] + public int Value = 10; + + [Export("IExtension.IdValue2")] + public int Value2 = 20; + } + + [Fact] + public void Rejection_ExtensionLightUp_AddedViaCatalog() + { + var ext1Cat = CatalogFactory.CreateAttributed(typeof(Extension1)); + var ext2Cat = CatalogFactory.CreateAttributed(typeof(Extension2)); + var hostCat = CatalogFactory.CreateAttributed(typeof(MyImporter)); + var valueCat = CatalogFactory.CreateAttributed(typeof(ExtensionValues)); + + var catalog = new AggregateCatalog(); + catalog.Catalogs.Add(hostCat); + + var container = ContainerFactory.Create(catalog); + + var importer = container.GetExportedValue(); + + Assert.Equal(0, importer.Extensions.Length); + + catalog.Catalogs.Add(ext1Cat); + + Assert.Equal(0, importer.Extensions.Length); + + catalog.Catalogs.Add(ext2Cat); + + Assert.Equal(0, importer.Extensions.Length); + + catalog.Catalogs.Add(valueCat); + + Assert.Equal(2, importer.Extensions.Length); + Assert.Equal(10, importer.Extensions[0].Id); + Assert.Equal(20, importer.Extensions[1].Id); + } + + public interface IMissing { } + public interface ISingle { } + public interface IMultiple { } + public interface IConditional { } + public class SingleImpl : ISingle { } + public class MultipleImpl : IMultiple { } + + public class NoImportPart + { + public NoImportPart() + { + SingleExport = new SingleImpl(); + MultipleExport1 = new MultipleImpl(); + MultipleExport2 = new MultipleImpl(); + } + + [Export] + public ISingle SingleExport { private set; get; } + + [Export] + public IMultiple MultipleExport1 { private set; get; } + + [Export] + public IMultiple MultipleExport2 { private set; get; } + } + + [Export] + public class Needy + { + public Needy() { } + + [Import] + public ISingle SingleImport { get; set; } + } + + [Fact] + public void Rejection_Resurrection() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(Needy)); + + var exports1 = container.GetExportedValues(); + + Assert.Equal(0, exports1.Count()); + + container.ComposeParts(new NoImportPart()); + + var exports2 = container.GetExportedValues(); + Assert.Equal(1, exports2.Count()); + } + + [Fact] + public void Rejection_BatchSatisfiesBatch() + { + var container = ContainerFactory.Create(); + var needy = new Needy(); + container.ComposeParts(needy, new NoImportPart()); + Assert.IsType(needy.SingleImport); + } + + [Fact] + public void Rejection_BatchSatisfiesBatchReversed() + { + var container = ContainerFactory.Create(); + var needy = new Needy(); + container.ComposeParts(new NoImportPart(), needy); + Assert.IsType(needy.SingleImport); + } + + [Fact] + public void Rejection_CatalogSatisfiesBatch() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(NoImportPart)); + var needy = new Needy(); + container.ComposeParts(needy); + Assert.IsType(needy.SingleImport); + } + + [Fact] + public void Rejection_TransitiveDependenciesSatisfied() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(Needy), typeof(NoImportPart)); + var needy = container.GetExportedValue(); + Assert.NotNull(needy); + Assert.IsType(needy.SingleImport); + } + + [Fact] + public void Rejection_TransitiveDependenciesUnsatisfied_ShouldThrowCardinalityMismatch() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(Needy), typeof(MissingImportPart)); + + ExceptionAssert.Throws(() => + container.GetExportedValue()); + } + + public class MissingImportPart : NoImportPart + { + [Import] + public IMissing MissingImport { set; get; } + } + + [Fact] + public void Rejection_BatchRevert() + { + var container = ContainerFactory.Create(); + + ExceptionAssert.Throws(() => + container.ComposeParts(new MissingImportPart())); + } + + [Fact] + public void Rejection_DefendPromisesOnceMade() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(Needy)); + + var addBatch = new CompositionBatch(); + var removeBatch = new CompositionBatch(); + var addedPart = addBatch.AddPart(new NoImportPart()); + removeBatch.RemovePart(addedPart); + + // Add then remove should be fine as long as exports aren't used yet. + container.Compose(addBatch); + container.Compose(removeBatch); + + // Add the dependencies + container.Compose(addBatch); + + // Retrieve needy which uses an export from addedPart + var export = container.GetExportedValue(); + + // Should not be able to remove the addedPart because someone depends on it. + ExceptionAssert.Throws(() => + container.Compose(removeBatch)); + } + + [Fact] + public void Rejection_DefendPromisesLazily() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(Needy)); + + // Add the missing dependency for Needy + container.ComposeParts(new NoImportPart()); + + // This change should succeed since the component "Needy" hasn't been fully composed + // and one way of satisfying its needs is as good ask another + var export = container.GetExport(); + + // Cannot add another import because it would break existing promised compositions + ExceptionAssert.Throws(() => + container.ComposeParts(new NoImportPart())); + + // Instansitate the object + var needy = export.Value; + + // Cannot add another import because it would break existing compositions + ExceptionAssert.Throws(() => + container.ComposeParts(new NoImportPart())); + } + + [Fact] + public void Rejection_SwitchPromiseFromManualToCatalog() + { + // This test shows how the priority list in the AggregateCatalog can actually play with + // the rejection work. Until the actual object is actually pulled on and satisfied the + // promise can be moved around even for not-recomposable imports but once the object is + // pulled on it is fixed from that point on. + + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(Needy), typeof(NoImportPart)); + + // Add the missing dependency for Needy + container.ComposeParts(new NoImportPart()); + + // This change should succeed since the component "Needy" hasn't been fully composed + // and one way of satisfying its needs is as good as another + var export = container.GetExport(); + + // Adding more exports doesn't fail because we push the promise to use the NoImportPart from the catalog + // using the priorities from the AggregateExportProvider + container.ComposeParts(new NoImportPart()); + + // Instansitate the object + var needy = export.Value; + + // Cannot add another import because it would break existing compositions + ExceptionAssert.Throws(() => + container.ComposeParts(new NoImportPart())); + } + + public interface ILoopA { } + public interface ILoopB { } + + [Export(typeof(ILoopA))] + public class LoopA1 : ILoopA + { + [Import] + public ILoopB LoopB { set; get; } + } + + [Export(typeof(ILoopA))] + public class LoopA2 : ILoopA + { + [Import] + public ILoopB LoopB { set; get; } + } + + [Export(typeof(ILoopB))] + public class LoopB1 : ILoopB + { + [Import] + public ILoopA LoopA { set; get; } + } + + [Export(typeof(ILoopB))] + public class LoopB2 : ILoopB + { + [Import] + public ILoopA LoopA { set; get; } + } + + // This is an interesting situation. There are several possible self-consistent outcomes: + // - All parts involved in the loop are rejected + // - A consistent subset are not rejected (exactly one of LoopA1/LoopA2 and one of LoopB1/LoopB2 + // + // Both have desireable and undesirable characteristics. The first case is non-discriminatory but + // rejects more parts than are necessary, the second minimizes rejection but must choose a subset + // on somewhat arbitary grounds. + [Fact] + public void Rejection_TheClemensLoop() + { + var catalog = new TypeCatalog(new Type[] { typeof(LoopA1), typeof(LoopA2), typeof(LoopB1), typeof(LoopB2) }); + var container = new CompositionContainer(catalog); + var exportsA = container.GetExportedValues(); + var exportsB = container.GetExportedValues(); + + // These assertions would prove solution one + Assert.Equal(0, exportsA.Count()); + Assert.Equal(0, exportsB.Count()); + + // These assertions would prove solution two + //Assert.Equal(1, exportsA.Count); + //Assert.Equal(1, exportsB.Count); + } + + public interface IWorkItem + { + string Id { get; set; } + } + + [Export] + public class AllWorkItems + { + [ImportMany(AllowRecomposition = true)] + public Lazy[] WorkItems { get; set; } + } + + [Export(typeof(IWorkItem))] + public class WorkItem : IWorkItem + { + [Import("WorkItem.Id", AllowRecomposition = true)] + public string Id { get; set; } + } + + public class Ids + { + [Export("WorkItem.Id")] + public string Id = "MyId"; + + } + + [Fact] + public void AppliedStateNotCompleteedYet() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(AllWorkItems)); + + container.ComposeExportedValue("WorkItem.Id", "A"); + + var workItems = container.GetExportedValue(); + + Assert.Equal(0, workItems.WorkItems.Length); + + container.ComposeParts(new WorkItem()); + + Assert.Equal(1, workItems.WorkItems.Length); + Assert.Equal("A", workItems.WorkItems[0].Value.Id); + } + + [Export] + public class ClassWithMissingImport + { + [Import] + private string _importNotFound = null; + + public ClassWithMissingImport() + { + if (_importNotFound != null) + throw new ArgumentException(); + } + } + + [Fact] + public void AppliedStateStored_ShouldRevertStateOnFailure() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(AllWorkItems), typeof(WorkItem), typeof(Ids)); + + var workItems = container.GetExportedValue(); + + Assert.Equal(1, workItems.WorkItems.Length); + + var batch = new CompositionBatch(); + + batch.AddExportedValue("WorkItem.Id", "B"); + batch.AddPart(new ClassWithMissingImport()); + + ExceptionAssert.Throws(() => + container.Compose(batch)); + + Assert.Equal("MyId", workItems.WorkItems[0].Value.Id); + } + + [Export] + public class OptionalImporter + { + [Import(AllowDefault = true)] + public ClassWithMissingImport Import { get; set; } + } + + [Fact] + public void OptionalImportWithMissingDependency_ShouldRejectAndComposeFine() + { + var container = ContainerFactory.CreateWithAttributedCatalog(typeof(OptionalImporter), typeof(ClassWithMissingImport)); + + var importer = container.GetExportedValue(); + + Assert.Null(importer.Import); + } + + [Export] + public class PartA + { + [Import(AllowDefault = true, AllowRecomposition = true)] + public PartB ImportB { get; set; } + } + + [Export] + public class PartB + { + [Import] + public PartC ImportC { get; set; } + } + + [Export] + public class PartC + { + [Import] + public PartB ImportB { get; set; } + } + + [Fact] + [ActiveIssue(684510)] + public void PartAOptionalDependsOnPartB_PartBGetAddedLater() + { + var container = new CompositionContainer(new TypeCatalog(typeof(PartC), typeof(PartA))); + var partA = container.GetExportedValue(); + + Assert.Null(partA.ImportB); + + var partB = new PartB(); + container.ComposeParts(partB); + + Assert.Equal(partA.ImportB, partB); + Assert.NotNull(partB.ImportC); + } + + [Export] + public class PartA2 + { + [Import(AllowDefault = true, AllowRecomposition = true)] + public PartB ImportB { get; set; } + + [Import(AllowDefault = true, AllowRecomposition = true)] + public PartC ImportC { get; set; } + } + + [Fact] + [ActiveIssue(684510)] + public void PartAOptionalDependsOnPartBAndPartC_PartCGetRecurrected() + { + var container = new CompositionContainer(new TypeCatalog(typeof(PartA2), typeof(PartB))); + var partA = container.GetExportedValue(); + + Assert.Null(partA.ImportB); + Assert.Null(partA.ImportC); + + var partC = new PartC(); + container.ComposeParts(partC); + + Assert.Equal(partA.ImportB, partC.ImportB); + Assert.Equal(partA.ImportC, partC); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/RequiredCreationPolicyTests.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/RequiredCreationPolicyTests.cs new file mode 100644 index 0000000000..74505c8891 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/Integration/RequiredCreationPolicyTests.cs @@ -0,0 +1,158 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition; +using System.ComponentModel.Composition.Factories; +using System.ComponentModel.Composition.Hosting; +using System.Linq; +using Xunit; + +namespace Tests.Integration +{ + public class RequiredCreationPolicyTests + { + // Matrix that details which policy to use for a given part to satisfy a given import. + // Part.Any Part.Shared Part.NonShared + // Import.Any Shared Shared NonShared + // Import.Shared Shared Shared N/A + // Import.NonShared NonShared N/A NonShared + + public interface ICreationPolicyExport + { + + } + + [Export(typeof(ICreationPolicyExport))] + public class CreationPolicyAnyExportImplicit : ICreationPolicyExport + { + + } + + [Export(typeof(ICreationPolicyExport))] + [PartCreationPolicy(CreationPolicy.Any)] + public class CreationPolicyAnyExportExplicit : ICreationPolicyExport + { + + } + + [Export(typeof(ICreationPolicyExport))] + [PartCreationPolicy(CreationPolicy.Shared)] + public class CreationPolicySharedExport : ICreationPolicyExport + { + + } + + [Export(typeof(ICreationPolicyExport))] + [PartCreationPolicy(CreationPolicy.NonShared)] + public class CreationPolicyNonSharedExport : ICreationPolicyExport + { + + } + + [Export] + public class RequiredAnyImporterImplicit + { + [ImportMany] + public IEnumerable Exports { get; set; } + } + + [Export] + public class RequiredAnyImporterExplicit + { + [ImportMany(RequiredCreationPolicy = CreationPolicy.Any)] + public IEnumerable Exports { get; set; } + } + + [Export] + public class RequiredSharedImporter + { + [ImportMany(RequiredCreationPolicy = CreationPolicy.Shared)] + public IEnumerable Exports { get; set; } + } + + [Export] + public class RequiredNonSharedImporter + { + [ImportMany(RequiredCreationPolicy = CreationPolicy.NonShared)] + public IEnumerable Exports { get; set; } + } + + private static CompositionContainer CreateDefaultContainer() + { + return ContainerFactory.CreateWithAttributedCatalog( + typeof(ICreationPolicyExport), + + typeof(CreationPolicyAnyExportImplicit), + typeof(CreationPolicyAnyExportExplicit), + typeof(CreationPolicySharedExport), + typeof(CreationPolicyNonSharedExport), + + typeof(RequiredAnyImporterImplicit), + typeof(RequiredAnyImporterExplicit), + typeof(RequiredSharedImporter), + typeof(RequiredNonSharedImporter)); + } + + [Fact] + public void RequiredAnyImporterImplicit_ShouldIncludeAll() + { + var container = CreateDefaultContainer(); + + var importer = container.GetExportedValue(); + + Assert.Equal(new Type[] { + typeof(CreationPolicyAnyExportImplicit), + typeof(CreationPolicyAnyExportExplicit), + typeof(CreationPolicySharedExport), + typeof(CreationPolicyNonSharedExport) }, + importer.Exports.Select(obj => obj.GetType())); + } + + [Fact] + public void RequiredAnyImporterExplicit_ShouldIncludeAll() + { + var container = CreateDefaultContainer(); + + var importer = container.GetExportedValue(); + + Assert.Equal(new Type[] { + typeof(CreationPolicyAnyExportImplicit), + typeof(CreationPolicyAnyExportExplicit), + typeof(CreationPolicySharedExport), + typeof(CreationPolicyNonSharedExport) }, + importer.Exports.Select(obj => obj.GetType())); + } + + [Fact] + public void RequiredSharedImporter_ShouldFilterNonShared() + { + var container = CreateDefaultContainer(); + + var importer = container.GetExportedValue(); + + Assert.Equal(new Type[] { + typeof(CreationPolicyAnyExportImplicit), + typeof(CreationPolicyAnyExportExplicit), + typeof(CreationPolicySharedExport) }, + importer.Exports.Select(obj => obj.GetType())); + + } + + [Fact] + public void RequiredNonSharedImporter_ShouldFilterShared() + { + var container = CreateDefaultContainer(); + + var importer = container.GetExportedValue(); + + Assert.Equal(new Type[] { + typeof(CreationPolicyAnyExportImplicit), + typeof(CreationPolicyAnyExportExplicit), + typeof(CreationPolicyNonSharedExport) }, + importer.Exports.Select(obj => obj.GetType())); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/ReferenceTracker.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/ReferenceTracker.cs new file mode 100644 index 0000000000..2de80e5e1e --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/ReferenceTracker.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. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.UnitTesting; + +namespace System +{ + public class ReferenceTracker + { + public readonly List ReferencesExpectedToBeCollected = new List(); + public readonly List ReferencesNotExpectedToBeCollected = new List(); + + public void AddReferencesExpectedToBeCollected(params object[] objects) + { + for (int i = 0; i < objects.Length; i++) + { + ReferencesExpectedToBeCollected.Add(new WeakReference(objects[i])); + objects[i] = null; + } + } + + public void AddReferencesNotExpectedToBeCollected(params object[] objects) + { + for (int i = 0; i < objects.Length; i++) + { + ReferencesNotExpectedToBeCollected.Add(new WeakReference(objects[i])); + objects[i] = null; + } + } + + public void CollectAndAssert() + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + + EqualityExtensions.IsTrueForAll(ReferencesExpectedToBeCollected, wr => wr.Target == null, "Object should have been collected."); + EqualityExtensions.IsTrueForAll(ReferencesNotExpectedToBeCollected, wr => wr.Target != null, "Object should be have NOT been collected."); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/TypeExtensions.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/TypeExtensions.cs new file mode 100644 index 0000000000..08b6b9639c --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/TypeExtensions.cs @@ -0,0 +1,21 @@ +// 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.Linq; +using System.Reflection; +using Xunit; + +namespace System +{ + public static class TypeExtensions + { + public static MemberInfo GetSingleMember(this Type type, string name) + { + Assert.NotNull(type); + Assert.NotNull(name); + + return type.GetMember(name).Single(); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/CompositionAssert.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/CompositionAssert.cs new file mode 100644 index 0000000000..b3b9c224f2 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/CompositionAssert.cs @@ -0,0 +1,366 @@ +// 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.ComponentModel.Composition; +using System.ComponentModel.Composition.Primitives; +using System.UnitTesting; +using Xunit; + +namespace System.UnitTesting +{ + public static class CompositionAssert + { + public static void ThrowsPart(Action action) + { + ThrowsPart(RetryMode.Retry, action); + } + + public static void ThrowsPart(RetryMode retry, Action action) + { + ThrowsPart(new CompositionErrorExpectation { Id = (ErrorId)CompositionErrorId.Unknown }, retry, action); + } + + public static void ThrowsPart(ICompositionElement element, RetryMode retry, Action action) + { + ThrowsPart(new CompositionErrorExpectation { Id = (ErrorId)CompositionErrorId.Unknown, Element = element }, retry, action); + } + + public static void ThrowsPart(Action action) + where TInner : Exception + { + ThrowsPart(RetryMode.Retry, action); + } + + public static void ThrowsPart(RetryMode retry, Action action) + where TInner : Exception + { + ThrowsPart(new CompositionErrorExpectation { Id = (ErrorId)CompositionErrorId.Unknown, InnerExceptionType = typeof(TInner) }, retry, action); + } + + private static void ThrowsPart(CompositionErrorExpectation expectation, RetryMode retry, Action action) + { + ExceptionAssert.Throws(retry, action, (thrownException, retryCount) => + { + AssertCore(retryCount, "ComposablePartException", thrownException, expectation); + }); + } + + public static void ThrowsError(ErrorId id, Action action) + { + ThrowsError(new CompositionErrorExpectation { Id = id }, RetryMode.Retry, action); + } + + public static void ThrowsError(ErrorId id, ErrorId innerId, Action action) + { + ThrowsError(id, innerId, RetryMode.Retry, action); + } + + public static void ThrowsError(ErrorId id, ErrorId innerId, RetryMode retry, Action action) + { + ThrowsError(GetExpectation(id, innerId), retry, action); + } + + public static void ThrowsError(ErrorId id, ErrorId innerId, ErrorId innerInnerId, RetryMode retry, Action action) + { + ThrowsError(GetExpectation(id, innerId, innerInnerId), retry, action); + } + + public static void ThrowsError(ErrorId id, RetryMode retry, Action action) + { + ThrowsError(new CompositionErrorExpectation { Id = id, }, retry, action); + } + + private static void ThrowsError(CompositionErrorExpectation expectation, RetryMode retry, Action action) + { + ThrowsErrors(new CompositionErrorExpectation[] { expectation }, retry, action); + } + + public static void ThrowsErrors(ErrorId id1, ErrorId id2, Action action) + { + ThrowsErrors(id1, id2, RetryMode.Retry, action); + } + + public static void ThrowsErrors(ErrorId id1, ErrorId id2, RetryMode retry, Action action) + { + ThrowsErrors(new ErrorId[] { id1, id2 }, retry, action); + } + + public static void ThrowsErrors(ErrorId[] ids, RetryMode retry, Action action) + { + CompositionErrorExpectation[] expectations = new CompositionErrorExpectation[ids.Length]; + for (int i = 0; i < expectations.Length; i++) + { + expectations[i] = new CompositionErrorExpectation { Id = ids[i] }; + } + + ThrowsErrors(expectations, retry, action); + } + + public static void ThrowsChangeRejectedError(ErrorId id, RetryMode retry, Action action) + { + ThrowsChangeRejectedError(new CompositionErrorExpectation { Id = id, }, retry, action); + } + + public static void ThrowsChangeRejectedError(ErrorId id, ErrorId innerId, RetryMode retry, Action action) + { + ThrowsChangeRejectedError(GetExpectation(id, innerId), retry, action); + } + + public static void ThrowsChangeRejectedError(ErrorId id, ErrorId innerId, ErrorId innerInnerId, RetryMode retry, Action action) + { + ThrowsChangeRejectedError(GetExpectation(id, innerId, innerInnerId), retry, action); + } + + private static void ThrowsChangeRejectedError(CompositionErrorExpectation expectation, RetryMode retry, Action action) + { + ThrowsChangeRejectedErrors(new CompositionErrorExpectation[] { expectation }, retry, action); + } + + private static void ThrowsChangeRejectedErrors(CompositionErrorExpectation[] expectations, RetryMode retry, Action action) + { + ExceptionAssert.Throws(retry, action, (thrownException, retryCount) => + { + AssertCore(retryCount, "CompositionException", thrownException, expectations); + }); + } + + private static void ThrowsErrors(CompositionErrorExpectation[] expectations, RetryMode retry, Action action) + { + ExceptionAssert.Throws(retry, action, (thrownException, retryCount) => + { + AssertCore(retryCount, "CompositionException", thrownException, expectations); + }); + } + + private static void AssertCore(int retryCount, string prefix, CompositionException exception, CompositionErrorExpectation[] expectations) + { + Assert.Equal(exception.Errors.Count, expectations.Length); + + for (int i = 0; i < exception.Errors.Count; i++) + { + AssertCore(retryCount, prefix + ".Errors[" + i + "]", exception.Errors[i], expectations[i]); + } + } + + private static void AssertCore(int retryCount, string prefix, ComposablePartException error, CompositionErrorExpectation expectation) + { + if (expectation.ElementSpecified) + { + AssertCore(retryCount, prefix, "Element", expectation.Element, error.Element); + } + + if (expectation.InnerExceptionSpecified) + { + AssertCore(retryCount, prefix, "InnerException", expectation.InnerException, error.InnerException); + } + + if (expectation.InnerExceptionTypeSpecified) + { + AssertCore(retryCount, prefix, "InnerException.GetType()", expectation.InnerExceptionType, error.InnerException == null ? null : error.InnerException.GetType()); + } + + if (expectation.InnerExpectationsSpecified) + { + var innerError = error.InnerException as ComposablePartException; + if (innerError != null) + { + Assert.Equal(1, expectation.InnerExpectations.Length); + AssertCore(retryCount, prefix + ".InnerException", innerError, expectation.InnerExpectations[0]); + } + else + { + AssertCore(retryCount, prefix + ".InnerException", (CompositionException)error.InnerException, expectation.InnerExpectations); + } + } + } + + private static void AssertCore(int retryCount, string prefix, CompositionError error, CompositionErrorExpectation expectation) + { + if (expectation.IdSpecified) + { + AssertCore(retryCount, prefix, "Id", expectation.Id, (ErrorId)error.Id); + } + + if (expectation.ElementSpecified) + { + AssertCore(retryCount, prefix, "Element", expectation.Element, error.Element); + } + + if (expectation.InnerExceptionSpecified) + { + AssertCore(retryCount, prefix, "InnerException", expectation.InnerException, error.InnerException); + } + + if (expectation.InnerExceptionTypeSpecified) + { + AssertCore(retryCount, prefix, "InnerException.GetType()", expectation.InnerExceptionType, error.InnerException == null ? null : error.InnerException.GetType()); + } + + if (expectation.InnerExpectationsSpecified) + { + var innerError = error.InnerException as ComposablePartException; + if (innerError != null) + { + Assert.Equal(1, expectation.InnerExpectations.Length); + AssertCore(retryCount, prefix + ".InnerException", innerError, expectation.InnerExpectations[0]); + } + else + { + AssertCore(retryCount, prefix + ".InnerException", (CompositionException)error.InnerException, expectation.InnerExpectations); + } + } + } + + private static void AssertCore(int retryCount, string prefix, string propertyName, T expected, T actual) + { + Assert.Equal(expected, actual); + } + + private static CompositionErrorExpectation GetExpectation(params ErrorId[] ids) + { + var parent = new CompositionErrorExpectation() { Id = ids[0] }; + var expectation = parent; + + for (int i = 1; i < ids.Length; i++) + { + expectation.InnerExpectations = new CompositionErrorExpectation[] { new CompositionErrorExpectation() { Id = ids[i] } }; + expectation = expectation.InnerExpectations[0]; + } + + return parent; + } + + private static ErrorId GetRootErrorId(CompositionException exception) + { + Assert.True(exception.Errors.Count == 1); + + return GetRootErrorId(exception.Errors[0]); + } + + private static ErrorId GetRootErrorId(object error) + { + // + // Get the InnerException from the error object + // Can be one of two types currently a ComposablePartException or a CompostionError + // Done this clunky way to avoid shipping dead code to retrieve it from ICompositionException + // + Exception exception = null; + if (error is ComposablePartException) + { + exception = ((ComposablePartException)error).InnerException; + } + else if (error is CompositionException) + { + exception = ((CompositionException)error).InnerException; + } + else + { + throw new NotImplementedException(); + } + + ComposablePartException composablePartException = exception as ComposablePartException; + if (composablePartException != null) + { + return GetRootErrorId(composablePartException); + } + + CompositionException composition = exception as CompositionException; + if (composition != null) + { + return GetRootErrorId(composition); + } + + throw new NotImplementedException(); + } + + private class CompositionErrorExpectation + { + private ErrorId _id; + private Exception _innerException; + private Type _innerExceptionType; + private ICompositionElement _element; + private CompositionErrorExpectation[] _innerExpectations; + + public ErrorId Id + { + get { return _id; } + set + { + _id = value; + IdSpecified = true; + } + } + + public Exception InnerException + { + get { return _innerException; } + set + { + _innerException = value; + InnerExceptionSpecified = true; + } + } + + public Type InnerExceptionType + { + get { return _innerExceptionType; } + set + { + _innerExceptionType = value; + InnerExceptionTypeSpecified = true; + } + } + + public ICompositionElement Element + { + get { return _element; } + set + { + _element = value; + ElementSpecified = true; + } + } + + public CompositionErrorExpectation[] InnerExpectations + { + get { return _innerExpectations; } + set + { + _innerExpectations = value; + InnerExpectationsSpecified = true; + } + } + + public bool IdSpecified + { + get; + private set; + } + + public bool InnerExceptionSpecified + { + get; + private set; + } + + public bool InnerExceptionTypeSpecified + { + get; + private set; + } + + public bool ElementSpecified + { + get; + private set; + } + + public bool InnerExpectationsSpecified + { + get; + private set; + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ConstraintAssert.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ConstraintAssert.cs new file mode 100644 index 0000000000..fcf2d6160b --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ConstraintAssert.cs @@ -0,0 +1,37 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using System.Linq.Expressions; +using Xunit; + +namespace System.UnitTesting +{ + internal static class ConstraintAssert + { + public static void Contains(Expression> constraint, string contractName) + { + Contains(constraint, contractName, Enumerable.Empty>()); + } + + public static void Contains(Expression> constraint, string contractName, IEnumerable> requiredMetadata) + { + string actualContractName; + IEnumerable> actualRequiredMetadata; + bool success = TryParseConstraint(constraint, out actualContractName, out actualRequiredMetadata); + + Assert.True(success); + Assert.Equal(contractName, actualContractName); + EnumerableAssert.AreEqual(requiredMetadata, actualRequiredMetadata); + } + + private static bool TryParseConstraint(Expression> constraint, out string contractName, out IEnumerable> requiredMetadata) + { + return ContraintParser.TryParseConstraint(constraint, out contractName, out requiredMetadata); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/EnumerableAssert.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/EnumerableAssert.cs new file mode 100644 index 0000000000..3050985c99 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/EnumerableAssert.cs @@ -0,0 +1,75 @@ +// 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.Collections; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace System.UnitTesting +{ + public static class EnumerableAssert + { + public static void AreEqual(IEnumerable actual, params T[] expected) + { + AreEqual((IEnumerable)expected, (IEnumerable)actual); + } + + private static void AreEqual(IEnumerable expected, IList actual) + { + foreach (object value in expected) + { + bool removed = actual.Remove((T)value); + + Assert.True(removed); + } + + Assert.Equal(0, actual.Count); + } + + public static void AreEqual(IEnumerable expected, IEnumerable actual) + { + // First, test the IEnumerable implementation + Assert.Equal(expected.Count(), actual.Count()); + AreEqual((IEnumerable)expected, actual.ToList()); + + // Second, test the IEnumerable implementation + Assert.Equal(expected.Count(), actual.Count()); + + List actualList = actual.ToList(); + + foreach (T value in expected) + { + bool removed = actualList.Remove(value); + + Assert.True(removed); + } + + Assert.Equal(0, actualList.Count); + } + + public static void AreEqual(IDictionary expected, IDictionary actual) + { + Assert.Equal(expected.Count, actual.Count); + + foreach (KeyValuePair kvp in expected) + { + TValue firstValue = kvp.Value; + TValue secondValue = default(TValue); + if (!actual.TryGetValue(kvp.Key, out secondValue)) + { + throw new NotImplementedException(); + } + + if ((firstValue is IDictionary) && (secondValue is IDictionary)) + { + AreEqual((IDictionary)firstValue, (IDictionary)secondValue); + continue; + } + + Assert.Equal(kvp.Value, secondValue); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/EqualityExtensions.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/EqualityExtensions.cs new file mode 100644 index 0000000000..61b3ab7a91 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/EqualityExtensions.cs @@ -0,0 +1,136 @@ +// 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.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Xunit; + +namespace System.UnitTesting +{ + public static class EqualityExtensions + { + public static void IsTrueForAll(IEnumerable source, Predicate predicate) + { + IsTrueForAll(source, predicate, "IsTrueForAll Failed"); + } + + public static void IsTrueForAll(IEnumerable source, Predicate predicate, string message) + { + Assert.NotNull(source); + + foreach (T t in source) + { + Assert.True(predicate(t), message); + } + } + + private static MethodInfo GetExtensionMethod(Type extendedType) + { + if (extendedType.IsGenericType) + { + var x = typeof(EqualityExtensions).GetMethods() + ?.Where(m => + m.Name == "IsEqual" && + m.GetParameters().Length == 2 && + m.IsGenericMethodDefinition); + + MethodInfo method = typeof(EqualityExtensions).GetMethods() + ?.SingleOrDefault(m => + m.Name == "IsEqual" && + m.GetParameters().Length == 2 && + m.GetParameters()[0].ParameterType.Name == extendedType.Name && + m.IsGenericMethodDefinition); + + // If extension method found, make it generic and return + if (method != null) + return method.MakeGenericMethod(extendedType.GenericTypeArguments[0]); + } + + return typeof(EqualityExtensions).GetMethod("IsEqual", new[] { extendedType, extendedType }); + } + + public static bool CheckEquals(object objA, object objB) + { + if (objA == null && objB == null) + return true; + + if (objA != null && objB != null) + { + object equalityResult = null; + Type objType = objA.GetType(); + + // Check if custom equality extension method is available + MethodInfo customEqualityCheck = GetExtensionMethod(objType); + if (customEqualityCheck != null) + { + equalityResult = customEqualityCheck.Invoke(objA, new object[] { objA, objB }); + } + else + { + // Check if object.Equals(object) is overridden and if not check if there is a more concrete equality check implementation + bool equalsNotOverridden = objType.GetMethod("Equals", new Type[] { typeof(object) }).DeclaringType == typeof(object); + if (equalsNotOverridden) + { + // If type doesn't override Equals(object) method then check if there is a more concrete implementation + // e.g. if type implements IEquatable. + MethodInfo equalsMethod = objType.GetMethod("Equals", new Type[] { objType }); + if (equalsMethod.DeclaringType != typeof(object)) + { + equalityResult = equalsMethod.Invoke(objA, new object[] { objB }); + } + } + } + + if (equalityResult != null) + { + return (bool)equalityResult; + } + } + + if (objA is IEnumerable objAEnumerable && objB is IEnumerable objBEnumerable) + { + return CheckSequenceEquals(objAEnumerable, objBEnumerable); + } + + return objA.Equals(objB); + } + + public static bool CheckSequenceEquals(this IEnumerable @this, IEnumerable other) + { + if (@this == null || other == null) + return @this == other; + + if (@this.GetType() != other.GetType()) + return false; + + IEnumerator eA = null; + IEnumerator eB = null; + + try + { + eA = (@this as IEnumerable).GetEnumerator(); + eB = (@this as IEnumerable).GetEnumerator(); + while (true) + { + bool moved = eA.MoveNext(); + if (moved != eB.MoveNext()) + return false; + if (!moved) + return true; + if (eA.Current == null && eB.Current == null) + return true; + if (!CheckEquals(eA.Current, eB.Current)) + return true; + } + } + finally + { + (eA as IDisposable)?.Dispose(); + (eB as IDisposable)?.Dispose(); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ErrorId.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ErrorId.cs new file mode 100644 index 0000000000..d6f0229d84 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ErrorId.cs @@ -0,0 +1,32 @@ +// 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. + +namespace System.ComponentModel.Composition +{ + // We need a public version of CompositionErrorId, so that the QA tests can access and verify the errors. + public enum ErrorId : int + { + Unknown = CompositionErrorId.Unknown, + InvalidExportMetadata = CompositionErrorId.InvalidExportMetadata, + ImportNotSetOnPart = CompositionErrorId.ImportNotSetOnPart, + ImportEngine_ComposeTookTooManyIterations = CompositionErrorId.ImportEngine_ComposeTookTooManyIterations, + ImportEngine_ImportCardinalityMismatch = CompositionErrorId.ImportEngine_ImportCardinalityMismatch, + ImportEngine_PartCycle = CompositionErrorId.ImportEngine_PartCycle, + ImportEngine_PartCannotSetImport = CompositionErrorId.ImportEngine_PartCannotSetImport, + ImportEngine_PartCannotGetExportedValue = CompositionErrorId.ImportEngine_PartCannotGetExportedValue, + ImportEngine_PartCannotActivate = CompositionErrorId.ImportEngine_PartCannotActivate, + ImportEngine_PreventedByExistingImport = CompositionErrorId.ImportEngine_PreventedByExistingImport, + ImportEngine_InvalidStateForRecomposition = CompositionErrorId.ImportEngine_InvalidStateForRecomposition, + ReflectionModel_ImportThrewException = CompositionErrorId.ReflectionModel_ImportThrewException, + ReflectionModel_ImportNotAssignableFromExport = CompositionErrorId.ReflectionModel_ImportNotAssignableFromExport, + ReflectionModel_ImportCollectionNull = CompositionErrorId.ReflectionModel_ImportCollectionNull, + ReflectionModel_ImportCollectionNotWritable = CompositionErrorId.ReflectionModel_ImportCollectionNotWritable, + ReflectionModel_ImportCollectionConstructionThrewException = CompositionErrorId.ReflectionModel_ImportCollectionConstructionThrewException, + ReflectionModel_ImportCollectionGetThrewException = CompositionErrorId.ReflectionModel_ImportCollectionGetThrewException, + ReflectionModel_ImportCollectionIsReadOnlyThrewException = CompositionErrorId.ReflectionModel_ImportCollectionIsReadOnlyThrewException, + ReflectionModel_ImportCollectionClearThrewException = CompositionErrorId.ReflectionModel_ImportCollectionClearThrewException, + ReflectionModel_ImportCollectionAddThrewException = CompositionErrorId.ReflectionModel_ImportCollectionAddThrewException, + ReflectionModel_ImportManyOnParameterCanOnlyBeAssigned = CompositionErrorId.ReflectionModel_ImportManyOnParameterCanOnlyBeAssigned, + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ExceptionAssert.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ExceptionAssert.cs new file mode 100644 index 0000000000..b1b03f0542 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ExceptionAssert.cs @@ -0,0 +1,145 @@ +// 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 Xunit; + +namespace System.UnitTesting +{ + public static class ExceptionAssert + { + /// + /// Verifies that the exception has the default message generated by the base Exception class. + /// + public static void HasDefaultMessage(Exception exception) + { + Assert.NotNull(exception); + + // Exception of type '[typename]' was thrown + // However, don't check for single-quotes as they can be localized + AssertExtensions.Contains(exception.Message, exception.GetType().FullName); + } + + /// + /// Verifies that the specified action throws an exception of type . + /// + public static T Throws(Action action) + where T : Exception + { + return Throws(RetryMode.Retry, action, (Action)null); + } + + /// + /// Verifies that the specified action throws an ObjectDisposedException. + /// + public static ObjectDisposedException ThrowsDisposed(object instance, Action action) + { + var exception = Throws(RetryMode.Retry, action, (actual, retryCount) => + { + AssertObjectDisposed(instance, actual, retryCount); + }); + + return exception; + } + + /// + /// Verifies that the specified action throws an exception of type , + /// indicating whether to retry and running the specified validator. + /// + public static T Throws(RetryMode retry, Action action, Action validator) + where T : Exception + { + var exception = (T)Run(retry, action, (actual, retryCount) => + { + AssertIsExactInstanceOf(typeof(T), actual, retryCount); + + if (validator != null) + { + validator((T)actual, retryCount); + } + }); + + return exception; + } + + /// + /// Verifies that the specified action throws an exception of type , + /// indicating whether to retry. + /// + public static T Throws(RetryMode retry, Action action) + where T : Exception + { + return Throws(retry, action, (Action)null); + } + + /// + /// Verifies that the specified action throws the specified exception, + /// indicating whether to retry. + /// + public static void Throws(Exception expected, RetryMode retry, Action action) + { + Throws(expected, retry, action, (Action)null); + } + + /// + /// Verifies that the specified action throws the specified exception, + /// indicating whether to retry and running the specified validator. + /// + public static void Throws(Exception expected, RetryMode retry, Action action, Action validator) + { + Run(retry, action, (actual, retryCount) => + { + Assert.Same(expected, actual); + + if (validator != null) + { + validator(actual, retryCount); + } + }); + } + + private static Exception Run(RetryMode retry, Action action, Action validator) + { + Exception exception = null; + + for (int i = -1; i < (int)retry; i++) + { + exception = Run(action); + + validator(exception, i + 2); + } + + return exception; + } + + private static Exception Run(Action action) + { + try + { + action(); + return null; + } + catch (Exception ex) + { + return ex; + } + } + + private static void AssertObjectDisposed(object instance, ObjectDisposedException actual, int retryCount) + { + string objectName = instance.GetType().FullName; + + Assert.Equal(objectName, actual.ObjectName); + } + + private static void AssertIsExactInstanceOf(Type expectedType, Exception actual, int retryCount) + { + if (actual == null) + throw new NotImplementedException(); + + Type actualType = actual.GetType(); + + Assert.Same(expectedType, actualType); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ExpectationCollectionOfI.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ExpectationCollectionOfI.cs new file mode 100644 index 0000000000..5cbacbc572 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ExpectationCollectionOfI.cs @@ -0,0 +1,16 @@ +// 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.Collections.ObjectModel; + +namespace System.UnitTesting +{ + public class ExpectationCollection : Collection> + { + public void Add(TInputAndOutput input, TInputAndOutput output) + { + Add(new Expectation(input, output)); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ExpectationCollectionOfIO.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ExpectationCollectionOfIO.cs new file mode 100644 index 0000000000..9c08eb3d3d --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ExpectationCollectionOfIO.cs @@ -0,0 +1,16 @@ +// 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.Collections.ObjectModel; + +namespace System.UnitTesting +{ + public class ExpectationCollection : Collection> + { + public void Add(TInput input, TOutput output) + { + Add(new Expectation(input, output)); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ExpectationOfI.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ExpectationOfI.cs new file mode 100644 index 0000000000..c162262107 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ExpectationOfI.cs @@ -0,0 +1,15 @@ +// 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. + +namespace System.UnitTesting +{ + public class Expectation : Expectation + { + public Expectation(TInputAndOutput input, TInputAndOutput output) + : base(input, output) + { + } + } + +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ExpectationOfIO.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ExpectationOfIO.cs new file mode 100644 index 0000000000..3116037709 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ExpectationOfIO.cs @@ -0,0 +1,27 @@ +// 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. + +namespace System.UnitTesting +{ + public class Expectation + { + public Expectation(TInput input, TOutput output) + { + Input = input; + Output = output; + } + + public TInput Input + { + get; + private set; + } + + public TOutput Output + { + get; + private set; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ExportsAssert.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ExportsAssert.cs new file mode 100644 index 0000000000..f7fd047a15 --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ExportsAssert.cs @@ -0,0 +1,30 @@ +// 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.Collections; +using System.Collections.Generic; +using System.ComponentModel.Composition.Primitives; +using System.Linq; +using Xunit; + +namespace System.UnitTesting +{ + public static class ExportsAssert + { + public static void AreEqual(IEnumerable actual, params T[] expected) + { + Assert.Equal((IEnumerable)expected, (IEnumerable)actual.Select(export => (T)export.Value).ToArray()); + } + + public static void AreEqual(IEnumerable> actual, params T[] expected) + { + Assert.Equal((IEnumerable)expected, (IEnumerable)actual.Select(export => export.Value)); + } + + public static void AreEqual(IEnumerable> actual, params T[] expected) + { + Assert.Equal((IEnumerable)expected, (IEnumerable)actual.Select(export => export.Value)); + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ExtendedAssert.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ExtendedAssert.cs new file mode 100644 index 0000000000..d4de49586f --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/ExtendedAssert.cs @@ -0,0 +1,34 @@ +// 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 Xunit; + +namespace System.UnitTesting +{ + public static class ExtendedAssert + { + public static void EnumsContainSameValues() + where TEnum1 : struct + where TEnum2 : struct + { + EnumsContainSameValuesCore(); + EnumsContainSameValuesCore(); + } + + private static void EnumsContainSameValuesCore() + where TEnum1 : struct + where TEnum2 : struct + { + var values = TestServices.GetEnumValues(); + + foreach (TEnum1 value in values) + { + string name1 = Enum.GetName(typeof(TEnum1), value); + string name2 = Enum.GetName(typeof(TEnum2), value); + + Assert.Equal(name1, name2); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/RetryMode.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/RetryMode.cs new file mode 100644 index 0000000000..6b8816ac9a --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/RetryMode.cs @@ -0,0 +1,12 @@ +// 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. + +namespace System.UnitTesting +{ + public enum RetryMode : int + { + DoNotRetry = 0, + Retry = 1, + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/TestServices.cs b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/TestServices.cs new file mode 100644 index 0000000000..c03d9cfa7c --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/System/UnitTesting/TestServices.cs @@ -0,0 +1,30 @@ +// 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.Collections.Generic; +using System.Reflection; + +namespace System.UnitTesting +{ + public static class TestServices + { + public static string GenerateRandomString() + { + return Guid.NewGuid().ToString().Replace('-', '_'); + } + + public static IEnumerable GetEnumValues() where TEnum : struct + { // Silverlight 2.0 does not have Enum.GetValues() + // so we need to write our own + + foreach (FieldInfo field in typeof(TEnum).GetFields()) + { + if (!field.IsLiteral) + continue; + + yield return (TEnum)field.GetRawConstantValue(); + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/TestAssembly.cs b/external/corefx/src/System.ComponentModel.Composition/tests/TestAssembly.cs new file mode 100644 index 0000000000..6fe15e68df --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/TestAssembly.cs @@ -0,0 +1,72 @@ +// 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.Reflection; + +namespace System.ComponentModel.Composition +{ + public class TestAssemblyOne { } + + public class TestAssemblyTwo { } + + public class TestAssemblyThree { } + + public class TestAssemblyFour { } + + [Export] + public class TestAssemblyOneExport { } + + // This is a glorious do nothing ReflectionContext + public class ReflectionContextTestAssemblyThreeReflectionContext : ReflectionContext + { + private ReflectionContextTestAssemblyThreeReflectionContext() {} + public override Assembly MapAssembly(Assembly assembly) + { + return assembly; + } + + public override TypeInfo MapType(TypeInfo type) + { + return type; + } + } + + // This is a glorious do nothing ReflectionContext + public class ReflectionContextTestAssemblyOneReflectionContext : ReflectionContext + { + public override Assembly MapAssembly(Assembly assembly) + { + return assembly; + } + + public override TypeInfo MapType(TypeInfo type) + { + return type; + } + } + + [Export] + public class TestAssemblyTwoExport { } + + public class MyLittleConventionAttribute : CatalogReflectionContextAttribute + { + public MyLittleConventionAttribute() : base(typeof(ReflectionContextTestAssemblyTwo)) + { + } + + // This is a glorious do nothing ReflectionContext + public class ReflectionContextTestAssemblyTwo : ReflectionContext + { + public override Assembly MapAssembly(Assembly assembly) + { + return assembly; + } + + public override TypeInfo MapType(TypeInfo type) + { + return type; + } + } + } +} diff --git a/external/corefx/src/System.ComponentModel.Composition/tests/TransparentTestCase.cs b/external/corefx/src/System.ComponentModel.Composition/tests/TransparentTestCase.cs new file mode 100644 index 0000000000..20483480aa --- /dev/null +++ b/external/corefx/src/System.ComponentModel.Composition/tests/TransparentTestCase.cs @@ -0,0 +1,14 @@ +// 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. + +namespace System.ComponentModel.Composition +{ + static public class TransparentTestCase + { + static public int GetMetadataView_IMetadataViewWithDefaultedIntInTranparentType(ITrans_MetadataViewWithDefaultedInt view) + { + return view.MyInt; + } + } +} diff --git a/external/corefx/src/System.ComponentModel.EventBasedAsync/src/System.ComponentModel.EventBasedAsync.csproj b/external/corefx/src/System.ComponentModel.EventBasedAsync/src/System.ComponentModel.EventBasedAsync.csproj index 6af7eef189..7c60c001e9 100644 --- a/external/corefx/src/System.ComponentModel.EventBasedAsync/src/System.ComponentModel.EventBasedAsync.csproj +++ b/external/corefx/src/System.ComponentModel.EventBasedAsync/src/System.ComponentModel.EventBasedAsync.csproj @@ -6,7 +6,6 @@ System.ComponentModel.EventBasedAsync System.ComponentModel.EventBasedAsync - @@ -23,7 +22,6 @@ - diff --git a/external/corefx/src/System.ComponentModel.EventBasedAsync/tests/System.ComponentModel.EventBasedAsync.Tests.csproj b/external/corefx/src/System.ComponentModel.EventBasedAsync/tests/System.ComponentModel.EventBasedAsync.Tests.csproj index b4a20c142f..df8b7c8bb5 100644 --- a/external/corefx/src/System.ComponentModel.EventBasedAsync/tests/System.ComponentModel.EventBasedAsync.Tests.csproj +++ b/external/corefx/src/System.ComponentModel.EventBasedAsync/tests/System.ComponentModel.EventBasedAsync.Tests.csproj @@ -7,7 +7,6 @@ true {59E9B218-81D0-4A80-A4B7-66C716136D82} - diff --git a/external/corefx/src/System.ComponentModel.Primitives/src/System.ComponentModel.Primitives.csproj b/external/corefx/src/System.ComponentModel.Primitives/src/System.ComponentModel.Primitives.csproj index 4f41e765d6..afb1d87236 100644 --- a/external/corefx/src/System.ComponentModel.Primitives/src/System.ComponentModel.Primitives.csproj +++ b/external/corefx/src/System.ComponentModel.Primitives/src/System.ComponentModel.Primitives.csproj @@ -6,7 +6,6 @@ System.ComponentModel.Primitives System.ComponentModel.Primitives - diff --git a/external/corefx/src/System.ComponentModel.Primitives/src/System/ComponentModel/InvalidEnumArgumentException.cs b/external/corefx/src/System.ComponentModel.Primitives/src/System/ComponentModel/InvalidEnumArgumentException.cs index 203b8439fc..432a6f1c31 100644 --- a/external/corefx/src/System.ComponentModel.Primitives/src/System/ComponentModel/InvalidEnumArgumentException.cs +++ b/external/corefx/src/System.ComponentModel.Primitives/src/System/ComponentModel/InvalidEnumArgumentException.cs @@ -11,6 +11,9 @@ namespace System.ComponentModel /// The exception that is thrown when using invalid arguments that are enumerators. /// [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class InvalidEnumArgumentException : ArgumentException { /// @@ -58,7 +61,6 @@ namespace System.ComponentModel /// protected InvalidEnumArgumentException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corefx/src/System.ComponentModel.Primitives/tests/CategoryAttributeTests.cs b/external/corefx/src/System.ComponentModel.Primitives/tests/CategoryAttributeTests.cs index 6d7d85c0f9..ba82499549 100644 --- a/external/corefx/src/System.ComponentModel.Primitives/tests/CategoryAttributeTests.cs +++ b/external/corefx/src/System.ComponentModel.Primitives/tests/CategoryAttributeTests.cs @@ -102,6 +102,7 @@ namespace System.ComponentModel.Tests [Theory] [MemberData(nameof(DefaultProperties_TestData))] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] // NetFX does not have fix for #21369 public void CategoryProperties_GetCategory_ReturnsExpected(Func attributeThunk, string expectedCategory) { CategoryAttribute attribute = attributeThunk(); diff --git a/external/corefx/src/System.ComponentModel.Primitives/tests/InvalidEnumArgumentExceptionTests.cs b/external/corefx/src/System.ComponentModel.Primitives/tests/InvalidEnumArgumentExceptionTests.cs index 433fcd89ca..3a7311404e 100644 --- a/external/corefx/src/System.ComponentModel.Primitives/tests/InvalidEnumArgumentExceptionTests.cs +++ b/external/corefx/src/System.ComponentModel.Primitives/tests/InvalidEnumArgumentExceptionTests.cs @@ -88,21 +88,6 @@ namespace System.ComponentModel.Tests Assert.Throws(() => new InvalidEnumArgumentException("argumentName", 1, null)); } - [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] - [ActiveIssue("https://github.com/dotnet/corefx/issues/21214", TargetFrameworkMonikers.UapAot)] - public void Deserialize_ThrowsPlatformNotSupportedException() - { - var binaryFormatter = new BinaryFormatter(); - using (var stream = new MemoryStream()) - { - binaryFormatter.Serialize(stream, new SubException()); - stream.Position = 0; - Exception ex = Assert.Throws(() => binaryFormatter.Deserialize(stream)); - Assert.IsType(ex.InnerException); - } - } - [Serializable] public class SubException : InvalidEnumArgumentException { diff --git a/external/corefx/src/System.ComponentModel.Primitives/tests/System.ComponentModel.Primitives.Tests.csproj b/external/corefx/src/System.ComponentModel.Primitives/tests/System.ComponentModel.Primitives.Tests.csproj index 9de390cd4b..84f3c9d7d8 100644 --- a/external/corefx/src/System.ComponentModel.Primitives/tests/System.ComponentModel.Primitives.Tests.csproj +++ b/external/corefx/src/System.ComponentModel.Primitives/tests/System.ComponentModel.Primitives.Tests.csproj @@ -4,7 +4,6 @@ {C9534425-93FB-494F-8DD8-1E4E3E626FDE} - diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/System.ComponentModel.TypeConverter.sln b/external/corefx/src/System.ComponentModel.TypeConverter/System.ComponentModel.TypeConverter.sln index 823bb8a6ee..10a87a9849 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/System.ComponentModel.TypeConverter.sln +++ b/external/corefx/src/System.ComponentModel.TypeConverter/System.ComponentModel.TypeConverter.sln @@ -35,10 +35,10 @@ Global {3F0326A1-9E19-4A6C-95CE-63E65C9D2030}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {3F0326A1-9E19-4A6C-95CE-63E65C9D2030}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU {3F0326A1-9E19-4A6C-95CE-63E65C9D2030}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU - {89C76728-ECAF-4905-A33F-BD6BFED5E91D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {89C76728-ECAF-4905-A33F-BD6BFED5E91D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {89C76728-ECAF-4905-A33F-BD6BFED5E91D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {89C76728-ECAF-4905-A33F-BD6BFED5E91D}.Release|Any CPU.Build.0 = Release|Any CPU + {89C76728-ECAF-4905-A33F-BD6BFED5E91D}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {89C76728-ECAF-4905-A33F-BD6BFED5E91D}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {89C76728-ECAF-4905-A33F-BD6BFED5E91D}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {89C76728-ECAF-4905-A33F-BD6BFED5E91D}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU {AF3EBF3B-526A-4B51-9F3D-62B0113CD01F}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU {AF3EBF3B-526A-4B51-9F3D-62B0113CD01F}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {AF3EBF3B-526A-4B51-9F3D-62B0113CD01F}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/ref/System.ComponentModel.cs b/external/corefx/src/System.ComponentModel.TypeConverter/ref/System.ComponentModel.cs index 2a6ada4171..817c6c13fb 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/ref/System.ComponentModel.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/ref/System.ComponentModel.cs @@ -1438,8 +1438,9 @@ namespace System.ComponentModel.Design.Serialization public object Invoke() { throw null; } } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] - public partial struct MemberRelationship + public readonly partial struct MemberRelationship { + private readonly object _dummy; public static readonly System.ComponentModel.Design.Serialization.MemberRelationship Empty; public MemberRelationship(object owner, System.ComponentModel.MemberDescriptor member) { throw null; } public bool IsEmpty { get { throw null; } } diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj b/external/corefx/src/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj index ef86c10b9d..2d7b02cd61 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj +++ b/external/corefx/src/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj @@ -10,7 +10,6 @@ false - @@ -260,11 +259,11 @@ - + @@ -282,4 +281,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/BaseNumberConverter.cs b/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/BaseNumberConverter.cs index 69b7487cf7..7de41df285 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/BaseNumberConverter.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/BaseNumberConverter.cs @@ -34,15 +34,7 @@ namespace System.ComponentModel /// Convert the given value to a string using the given formatInfo /// internal abstract object FromString(string value, NumberFormatInfo formatInfo); - - /// - /// Create an error based on the failed text and the exception thrown. - /// - internal virtual Exception FromStringError(string failedText, Exception innerException) - { - return new Exception(SR.Format(SR.ConvertInvalidPrimitive, failedText, TargetType.Name), innerException); - } - + /// /// Convert the given value from a string using the given formatInfo /// @@ -90,7 +82,7 @@ namespace System.ComponentModel } catch (Exception e) { - throw FromStringError(text, e); + throw new ArgumentException(SR.Format(SR.ConvertInvalidPrimitive, text, TargetType.Name), nameof(value), e); } } return base.ConvertFrom(context, culture, value); diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/CheckoutException.cs b/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/CheckoutException.cs index 9fb219a884..e04b406e60 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/CheckoutException.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/CheckoutException.cs @@ -19,6 +19,9 @@ namespace System.ComponentModel.Design /// /// [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class CheckoutException : ExternalException { private const int E_ABORT = unchecked((int)0x80004004); @@ -71,7 +74,6 @@ namespace System.ComponentModel.Design /// protected CheckoutException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } /// diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/MemberRelationshipService.cs b/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/MemberRelationshipService.cs index 6ed8701261..2168fff3e9 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/MemberRelationshipService.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/Serialization/MemberRelationshipService.cs @@ -180,7 +180,7 @@ namespace System.ComponentModel.Design.Serialization /// /// This class represents a single relationship between an object and a member. /// - public struct MemberRelationship + public readonly struct MemberRelationship { public static readonly MemberRelationship Empty = new MemberRelationship(); diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/InvalidAsynchronousStateException.cs b/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/InvalidAsynchronousStateException.cs index 2bcd4edf61..e5cbf08658 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/InvalidAsynchronousStateException.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/InvalidAsynchronousStateException.cs @@ -10,6 +10,9 @@ namespace System.ComponentModel /// The exception that is thrown when a thread that an operation should execute on no longer exists or is not pumping messages /// [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class InvalidAsynchronousStateException : ArgumentException { /// @@ -39,7 +42,6 @@ namespace System.ComponentModel protected InvalidAsynchronousStateException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseException.cs b/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseException.cs index a60ae92ae3..37f2898336 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseException.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseException.cs @@ -13,6 +13,8 @@ namespace System.ComponentModel /// /// Represents the exception thrown when a component cannot be granted a license. /// + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors")] // must not, a Type is required in all constructors. [Serializable] public class LicenseException : SystemException @@ -59,7 +61,6 @@ namespace System.ComponentModel /// protected LicenseException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } /// @@ -73,6 +74,8 @@ namespace System.ComponentModel public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + info.AddValue("type", null); // Type is not serializable. + info.AddValue("instance", _instance); } } } diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs b/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs index 285f68dc7d..ee0f28fc89 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs @@ -6,9 +6,10 @@ using System.Collections; using System.Collections.Generic; using System.ComponentModel.Design; using System.Diagnostics; +using System.Drawing; +using System.Globalization; using System.Linq; using System.Reflection; -using System.Drawing; using System.Threading; namespace System.ComponentModel @@ -118,6 +119,7 @@ namespace System.ComponentModel [typeof(UInt64)] = typeof(UInt64Converter), [typeof(object)] = typeof(TypeConverter), [typeof(void)] = typeof(TypeConverter), + [typeof(CultureInfo)] = typeof(CultureInfoConverter), [typeof(DateTime)] = typeof(DateTimeConverter), [typeof(DateTimeOffset)] = typeof(DateTimeOffsetConverter), [typeof(Decimal)] = typeof(DecimalConverter), @@ -135,7 +137,7 @@ namespace System.ComponentModel [typeof(ICollection)] = typeof(CollectionConverter), [typeof(Enum)] = typeof(EnumConverter), [s_intrinsicNullableKey] = typeof(NullableConverter), - }); + }); private static Hashtable PropertyCache => LazyInitializer.EnsureInitialized(ref s_propertyCache, () => new Hashtable()); diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs.REMOVED.git-id b/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs.REMOVED.git-id index 1c0331f62f..541ae4ea9a 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs.REMOVED.git-id +++ b/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs.REMOVED.git-id @@ -1 +1 @@ -9a2df4f09b14cc71f5feafd11a05d52d48fc2617 \ No newline at end of file +13607f00315827d545f53022193a2500427b7eab \ No newline at end of file diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/WarningException.cs b/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/WarningException.cs index 0d0414786a..73fb14960a 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/WarningException.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/WarningException.cs @@ -15,6 +15,9 @@ namespace System.ComponentModel /// Specifies an exception that is handled as a warning instead of an error. /// [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class WarningException : SystemException { /// @@ -67,7 +70,8 @@ namespace System.ComponentModel /// protected WarningException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); + HelpUrl = (string)info.GetValue("helpUrl", typeof(string)); + HelpTopic = (string)info.GetValue("helpTopic", typeof(string)); } /// @@ -82,12 +86,11 @@ namespace System.ComponentModel /// public string HelpTopic { get; } - /// - /// Need this since Exception implements ISerializable. - /// public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + info.AddValue("helpUrl", HelpUrl); + info.AddValue("helpTopic", HelpTopic); } } } diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/tests/ByteConvertersTests.cs b/external/corefx/src/System.ComponentModel.TypeConverter/tests/ByteConvertersTests.cs index e4efa9563c..fcc13e4ce0 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/tests/ByteConvertersTests.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/tests/ByteConvertersTests.cs @@ -53,7 +53,7 @@ namespace System.ComponentModel.Tests [Fact] public static void ConvertFrom_WithContext_Negative() { - Assert.Throws( + AssertExtensions.Throws( () => ByteConverterTests.s_converter.ConvertFrom(TypeConverterTests.s_context, null, "8.0")); } diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/tests/CultureInfoConverterTests.cs b/external/corefx/src/System.ComponentModel.TypeConverter/tests/CultureInfoConverterTests.cs index d4426cc59c..5581e52c23 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/tests/CultureInfoConverterTests.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/tests/CultureInfoConverterTests.cs @@ -56,8 +56,8 @@ namespace System.ComponentModel.Tests c = (CultureInfo)converter.ConvertFrom(null, CultureInfo.InvariantCulture, "nl-BE"); Assert.Equal(new CultureInfo("nl-BE"), c); - - try + + try { // Linux can create such cultures var cul = new CultureInfo("Dutch (Bel"); @@ -85,7 +85,7 @@ namespace System.ComponentModel.Tests { ArgumentException ex; - try + try { // Linux can create such cultures var cul = new CultureInfo("(default)"); @@ -107,7 +107,7 @@ namespace System.ComponentModel.Tests } } - try + try { // Linux can create such cultures var cul = new CultureInfo(" "); @@ -128,7 +128,7 @@ namespace System.ComponentModel.Tests } } - try + try { // Linux can create such cultures var cul = new CultureInfo("\r\n"); diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/tests/DecimalConverterTests.cs b/external/corefx/src/System.ComponentModel.TypeConverter/tests/DecimalConverterTests.cs index 298e6b7ac7..f579e30089 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/tests/DecimalConverterTests.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/tests/DecimalConverterTests.cs @@ -25,8 +25,8 @@ namespace System.ComponentModel.Tests [Fact] public static void ConvertFrom_WithContext_Negative() { - Assert.Throws( - () => DecimalConverterTests.s_converter.ConvertFrom(TypeConverterTests.s_context, null, "0x8")); + AssertExtensions.Throws( + () => DecimalConverterTests.s_converter.ConvertFrom(TypeConverterTests.s_context, null, "0x8")); } [Fact] diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/tests/DoubleConverterTests.cs b/external/corefx/src/System.ComponentModel.TypeConverter/tests/DoubleConverterTests.cs index 9b1536b7da..315f2f1a80 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/tests/DoubleConverterTests.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/tests/DoubleConverterTests.cs @@ -25,7 +25,7 @@ namespace System.ComponentModel.Tests [Fact] public static void ConvertFrom_WithContext_Negative() { - Assert.Throws( + AssertExtensions.Throws( () => DoubleConverterTests.s_converter.ConvertFrom(TypeConverterTests.s_context, null, "0x8")); } diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/tests/Drawing/ColorConverterTests.cs b/external/corefx/src/System.ComponentModel.TypeConverter/tests/Drawing/ColorConverterTests.cs index fef8e7bddc..ed463c4752 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/tests/Drawing/ColorConverterTests.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/tests/Drawing/ColorConverterTests.cs @@ -76,7 +76,7 @@ namespace System.ComponentModel.TypeConverterTests { for (int b = 0; b < 256; b += 67) { - yield return new object[] {a, r, g, b}; + yield return new object[] { a, r, g, b }; } } } @@ -86,25 +86,25 @@ namespace System.ComponentModel.TypeConverterTests public static IEnumerable ColorNames => typeof(Color).GetProperties() .Where(p => p.PropertyType == typeof(Color)) - .Select(p => new object[] { p.Name} ); + .Select(p => new object[] { p.Name }); [Theory] [MemberData(nameof(ColorData))] public void ConvertFrom(int a, int r, int g, int b) { var conv = new ColorConverter(); - Color color = (Color) conv.ConvertFrom(null, CultureInfo.InvariantCulture, $"#0x{a:x2}{r:x2}{g:x2}{b:x2}"); + Color color = (Color)conv.ConvertFrom(null, CultureInfo.InvariantCulture, $"#0x{a:x2}{r:x2}{g:x2}{b:x2}"); Assert.Equal(a, color.A); Assert.Equal(r, color.R); Assert.Equal(g, color.G); Assert.Equal(b, color.B); Assert.Equal(color, - (Color) conv.ConvertFrom(null, CultureInfo.InvariantCulture, $"#0X{a:x2}{r:x2}{g:x2}{b:x2}")); + (Color)conv.ConvertFrom(null, CultureInfo.InvariantCulture, $"#0X{a:x2}{r:x2}{g:x2}{b:x2}")); Assert.Equal(color, - (Color) conv.ConvertFrom(null, CultureInfo.InvariantCulture, $"0x{a:x2}{r:x2}{g:x2}{b:x2}")); + (Color)conv.ConvertFrom(null, CultureInfo.InvariantCulture, $"0x{a:x2}{r:x2}{g:x2}{b:x2}")); Assert.Equal(color, - (Color) conv.ConvertFrom(null, CultureInfo.InvariantCulture, $"0X{a:x2}{r:x2}{g:x2}{b:x2}")); + (Color)conv.ConvertFrom(null, CultureInfo.InvariantCulture, $"0X{a:x2}{r:x2}{g:x2}{b:x2}")); } [Theory] @@ -146,8 +146,8 @@ namespace System.ComponentModel.TypeConverterTests { var conv = new ColorConverter(); var color = Color.FromName(name); - Assert.Equal(color, (Color) conv.ConvertFrom(name)); - Assert.Equal(color, (Color) conv.ConvertFrom(" " + name + " ")); + Assert.Equal(color, (Color)conv.ConvertFrom(name)); + Assert.Equal(color, (Color)conv.ConvertFrom(" " + name + " ")); } [Fact] @@ -155,8 +155,8 @@ namespace System.ComponentModel.TypeConverterTests { var conv = new ColorConverter(); var color = Color.Empty; - Assert.Equal(color, (Color) conv.ConvertFrom(string.Empty)); - Assert.Equal(color, (Color) conv.ConvertFrom(" ")); + Assert.Equal(color, (Color)conv.ConvertFrom(string.Empty)); + Assert.Equal(color, (Color)conv.ConvertFrom(" ")); } [Theory] @@ -177,7 +177,7 @@ namespace System.ComponentModel.TypeConverterTests public void ConvertFrom_Exception(string value) { var conv = new ColorConverter(); - Assert.Throws(() => + AssertExtensions.Throws(() => { conv.ConvertFrom(null, CultureInfo.InvariantCulture, value); }); @@ -208,7 +208,7 @@ namespace System.ComponentModel.TypeConverterTests public void ConvertFrom_NullCulture() { var conv = new ColorConverter(); - var color = (Color) conv.ConvertFrom(null, null, "#0x23190A44"); + var color = (Color)conv.ConvertFrom(null, null, "#0x23190A44"); Assert.Equal(35, color.A); Assert.Equal(25, color.R); Assert.Equal(10, color.G); @@ -221,7 +221,7 @@ namespace System.ComponentModel.TypeConverterTests { var conv = new ColorConverter(); Assert.Equal($"{a}, {r}, {g}, {b}", - (string) conv.ConvertTo(null, CultureInfo.InvariantCulture, Color.FromArgb(a, r, g, b), typeof(string))); + (string)conv.ConvertTo(null, CultureInfo.InvariantCulture, Color.FromArgb(a, r, g, b), typeof(string))); } [Theory] @@ -230,7 +230,7 @@ namespace System.ComponentModel.TypeConverterTests { var conv = new ColorConverter(); Assert.Equal(name, - (string) conv.ConvertTo(null, CultureInfo.InvariantCulture, Color.FromName(name), typeof(string))); + (string)conv.ConvertTo(null, CultureInfo.InvariantCulture, Color.FromName(name), typeof(string))); } [Fact] @@ -238,7 +238,7 @@ namespace System.ComponentModel.TypeConverterTests { var conv = new ColorConverter(); Assert.Equal(string.Empty, - (string) conv.ConvertTo(null, CultureInfo.InvariantCulture, Color.Empty, typeof(string))); + (string)conv.ConvertTo(null, CultureInfo.InvariantCulture, Color.Empty, typeof(string))); } [Theory] @@ -306,7 +306,7 @@ namespace System.ComponentModel.TypeConverterTests public void ConvertFromInvariantString(int a, int r, int g, int b) { var conv = new ColorConverter(); - var color = (Color) conv.ConvertFromInvariantString($"{a}, {r}, {g}, {b}"); + var color = (Color)conv.ConvertFromInvariantString($"{a}, {r}, {g}, {b}"); Assert.Equal(a, color.A); Assert.Equal(r, color.R); Assert.Equal(g, color.G); @@ -319,7 +319,7 @@ namespace System.ComponentModel.TypeConverterTests { var conv = new ColorConverter(); var color = Color.FromName(name); - Assert.Equal(color, (Color) conv.ConvertFromInvariantString(name)); + Assert.Equal(color, (Color)conv.ConvertFromInvariantString(name)); } [Fact] @@ -336,7 +336,7 @@ namespace System.ComponentModel.TypeConverterTests public void ConvertFromInvariantString_NotNumber() { var conv = new ColorConverter(); - var ex = Assert.Throws(() => + var ex = AssertExtensions.Throws(() => { conv.ConvertFromInvariantString("hello"); }); @@ -365,7 +365,7 @@ namespace System.ComponentModel.TypeConverterTests { var conv = new ColorConverter(); var color = Color.FromName(name); - Assert.Equal(color, (Color) conv.ConvertFromString(name)); + Assert.Equal(color, (Color)conv.ConvertFromString(name)); } [Fact] @@ -382,7 +382,7 @@ namespace System.ComponentModel.TypeConverterTests public void ConvertFromString_NotNumber() { var conv = new ColorConverter(); - var ex = Assert.Throws(() => + var ex = AssertExtensions.Throws(() => { conv.ConvertFromString("hello"); }); diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/tests/Drawing/StringTypeConverterTestBase.cs b/external/corefx/src/System.ComponentModel.TypeConverter/tests/Drawing/StringTypeConverterTestBase.cs index 5bb03f7bb6..f8c7f2182c 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/tests/Drawing/StringTypeConverterTestBase.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/tests/Drawing/StringTypeConverterTestBase.cs @@ -88,7 +88,7 @@ namespace System.ComponentModel.TypeConverterTests protected void ConvertFromThrowsFormatInnerExceptionForString(string value) { - var ex = Assert.Throws(() => + var ex = AssertExtensions.Throws(() => { Converter.ConvertFrom(null, CultureInfo.InvariantCulture, value); }); @@ -145,7 +145,7 @@ namespace System.ComponentModel.TypeConverterTests protected void ConvertFromInvariantStringThrowsFormatInnerException(string str) { - var ex = Assert.Throws(() => + var ex = AssertExtensions.Throws(() => { Converter.ConvertFromInvariantString(str); }); @@ -163,7 +163,7 @@ namespace System.ComponentModel.TypeConverterTests protected void ConvertFromStringThrowsFormatInnerException(string str) { - var ex = Assert.Throws(() => + var ex = AssertExtensions.Throws(() => { Converter.ConvertFromString(str); }); diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/tests/Int16ConverterTests.cs b/external/corefx/src/System.ComponentModel.TypeConverter/tests/Int16ConverterTests.cs index 863ee193df..b8c7ecfe21 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/tests/Int16ConverterTests.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/tests/Int16ConverterTests.cs @@ -26,8 +26,8 @@ namespace System.ComponentModel.Tests [Fact] public static void ConvertFrom_WithContext_Negative() { - Assert.Throws( - () => Int16ConverterTests.s_converter.ConvertFrom(TypeConverterTests.s_context, null, "8.0")); + AssertExtensions.Throws( + () => Int16ConverterTests.s_converter.ConvertFrom(TypeConverterTests.s_context, null, "8.0")); } [Fact] diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/tests/Int32ConverterTests.cs b/external/corefx/src/System.ComponentModel.TypeConverter/tests/Int32ConverterTests.cs index 5cdb8bfdf4..2626f83768 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/tests/Int32ConverterTests.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/tests/Int32ConverterTests.cs @@ -26,7 +26,7 @@ namespace System.ComponentModel.Tests [Fact] public static void ConvertFrom_WithContext_Negative() { - Assert.Throws( + AssertExtensions.Throws( () => Int32ConverterTests.s_converter.ConvertFrom(TypeConverterTests.s_context, null, "8.0")); } diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/tests/Int64ConverterTests.cs b/external/corefx/src/System.ComponentModel.TypeConverter/tests/Int64ConverterTests.cs index fe00955951..5ca1b6ccc3 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/tests/Int64ConverterTests.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/tests/Int64ConverterTests.cs @@ -26,7 +26,7 @@ namespace System.ComponentModel.Tests [Fact] public static void ConvertFrom_WithContext_Negative() { - Assert.Throws( + AssertExtensions.Throws( () => Int64ConverterTests.s_converter.ConvertFrom(TypeConverterTests.s_context, null, "8.0")); } diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/tests/Performance/System.ComponentModel.TypeConverter.Performance.Tests.csproj b/external/corefx/src/System.ComponentModel.TypeConverter/tests/Performance/System.ComponentModel.TypeConverter.Performance.Tests.csproj index b97a8bedad..8de88c3244 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/tests/Performance/System.ComponentModel.TypeConverter.Performance.Tests.csproj +++ b/external/corefx/src/System.ComponentModel.TypeConverter/tests/Performance/System.ComponentModel.TypeConverter.Performance.Tests.csproj @@ -10,9 +10,8 @@ {2E36F1D4-B23C-435D-AB41-18E608940038} - - - + + diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/tests/PropertyDescriptorTests.cs b/external/corefx/src/System.ComponentModel.TypeConverter/tests/PropertyDescriptorTests.cs index 956188736d..cf6a7bec7a 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/tests/PropertyDescriptorTests.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/tests/PropertyDescriptorTests.cs @@ -2,6 +2,7 @@ // 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.Reflection; using Xunit; namespace System.ComponentModel.Tests @@ -104,7 +105,7 @@ namespace System.ComponentModel.Tests var properties = TypeDescriptor.GetProperties(component.GetType()); PropertyDescriptor propertyDescriptor = properties.Find(nameof(component.PropertyWhichThrows), false); - Assert.ThrowsAny(() => propertyDescriptor.GetValue(component)); + Assert.Throws(() => propertyDescriptor.GetValue(component)); } [Fact] diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/tests/ReferenceConverterTests.cs b/external/corefx/src/System.ComponentModel.TypeConverter/tests/ReferenceConverterTests.cs index 257c780d34..24b49ef60f 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/tests/ReferenceConverterTests.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/tests/ReferenceConverterTests.cs @@ -246,4 +246,4 @@ namespace System.ComponentModel.Tests Assert.Equal(3, values.Count); } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/tests/SByteConverterTests.cs b/external/corefx/src/System.ComponentModel.TypeConverter/tests/SByteConverterTests.cs index d208e72ca3..75a7a39ce5 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/tests/SByteConverterTests.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/tests/SByteConverterTests.cs @@ -26,7 +26,7 @@ namespace System.ComponentModel.Tests [Fact] public static void ConvertFrom_WithContext_Negative() { - Assert.Throws( + AssertExtensions.Throws( () => SByteConverterTests.s_converter.ConvertFrom(TypeConverterTests.s_context, null, "8.0")); } diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/tests/SingleConverterTests.cs b/external/corefx/src/System.ComponentModel.TypeConverter/tests/SingleConverterTests.cs index 094551c132..2498af08be 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/tests/SingleConverterTests.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/tests/SingleConverterTests.cs @@ -25,7 +25,7 @@ namespace System.ComponentModel.Tests [Fact] public static void ConvertFrom_WithContext_Negative() { - Assert.Throws( + AssertExtensions.Throws( () => SingleConverterTests.s_converter.ConvertFrom(TypeConverterTests.s_context, null, "0x8")); } diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj b/external/corefx/src/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj index 74c80d8690..7883fd5176 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj +++ b/external/corefx/src/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj @@ -7,7 +7,6 @@ {2E36F1D4-B23C-435D-AB41-18E608940038} 9.9.9.9 - diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs b/external/corefx/src/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs index 74246647ef..9c78e5ed3a 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections; +using System.Globalization; using Xunit; namespace System.ComponentModel.Tests @@ -255,7 +256,8 @@ namespace System.ComponentModel.Tests new Tuple (typeof(IDerived), typeof(IBaseConverter)), new Tuple (typeof(ClassIBase), typeof(IBaseConverter)), new Tuple (typeof(ClassIDerived), typeof(IBaseConverter)), - new Tuple (typeof(Uri), typeof(UriTypeConverter)) + new Tuple (typeof(Uri), typeof(UriTypeConverter)), + new Tuple (typeof(CultureInfo), typeof(CultureInfoConverter)) }; } } diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/tests/UInt16ConverterTests.cs b/external/corefx/src/System.ComponentModel.TypeConverter/tests/UInt16ConverterTests.cs index 4cc91bb931..6f2ac18a0b 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/tests/UInt16ConverterTests.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/tests/UInt16ConverterTests.cs @@ -26,7 +26,7 @@ namespace System.ComponentModel.Tests [Fact] public static void ConvertFrom_WithContext_Negative() { - Assert.Throws( + AssertExtensions.Throws( () => UInt16ConverterTests.s_converter.ConvertFrom(TypeConverterTests.s_context, null, "-8")); } diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/tests/UInt32ConverterTests.cs b/external/corefx/src/System.ComponentModel.TypeConverter/tests/UInt32ConverterTests.cs index 0198f9f28e..a1d1eeff4d 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/tests/UInt32ConverterTests.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/tests/UInt32ConverterTests.cs @@ -26,7 +26,7 @@ namespace System.ComponentModel.Tests [Fact] public static void ConvertFrom_WithContext_Negative() { - Assert.Throws( + AssertExtensions.Throws( () => UInt32ConverterTests.s_converter.ConvertFrom(TypeConverterTests.s_context, null, "-8")); } diff --git a/external/corefx/src/System.ComponentModel.TypeConverter/tests/UInt64ConverterTests.cs b/external/corefx/src/System.ComponentModel.TypeConverter/tests/UInt64ConverterTests.cs index f8ffbd2df2..bdbb9ad888 100644 --- a/external/corefx/src/System.ComponentModel.TypeConverter/tests/UInt64ConverterTests.cs +++ b/external/corefx/src/System.ComponentModel.TypeConverter/tests/UInt64ConverterTests.cs @@ -26,7 +26,7 @@ namespace System.ComponentModel.Tests [Fact] public static void ConvertFrom_WithContext_Negative() { - Assert.Throws( + AssertExtensions.Throws( () => UInt64ConverterTests.s_converter.ConvertFrom(TypeConverterTests.s_context, null, "-8")); } diff --git a/external/corefx/src/System.ComponentModel/src/System.ComponentModel.csproj b/external/corefx/src/System.ComponentModel/src/System.ComponentModel.csproj index a64c83fe1c..9894ee5a3f 100644 --- a/external/corefx/src/System.ComponentModel/src/System.ComponentModel.csproj +++ b/external/corefx/src/System.ComponentModel/src/System.ComponentModel.csproj @@ -7,7 +7,6 @@ System.ComponentModel true - diff --git a/external/corefx/src/System.ComponentModel/tests/System.ComponentModel.Tests.csproj b/external/corefx/src/System.ComponentModel/tests/System.ComponentModel.Tests.csproj index 3b424d1136..0959a0f5c4 100644 --- a/external/corefx/src/System.ComponentModel/tests/System.ComponentModel.Tests.csproj +++ b/external/corefx/src/System.ComponentModel/tests/System.ComponentModel.Tests.csproj @@ -4,7 +4,6 @@ {40C01084-DAB1-4F24-8729-85523BC9F04E} - diff --git a/external/corefx/src/System.Composition.Convention/src/System.Composition.Convention.csproj b/external/corefx/src/System.Composition.Convention/src/System.Composition.Convention.csproj index 7a527198f3..5b4e0639f9 100644 --- a/external/corefx/src/System.Composition.Convention/src/System.Composition.Convention.csproj +++ b/external/corefx/src/System.Composition.Convention/src/System.Composition.Convention.csproj @@ -76,4 +76,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.Composition.Convention/tests/ImportBuilderTests.cs b/external/corefx/src/System.Composition.Convention/tests/ImportBuilderTests.cs index 5b5d876710..933a57cb09 100644 --- a/external/corefx/src/System.Composition.Convention/tests/ImportBuilderTests.cs +++ b/external/corefx/src/System.Composition.Convention/tests/ImportBuilderTests.cs @@ -10,7 +10,7 @@ using System.Linq; using System.Reflection; using Xunit; -namespace System.ComponentModel.Composition +namespace System.Composition.Convention { public class ImportBuilderTests { diff --git a/external/corefx/src/System.Composition.Convention/tests/PartBuilderInheritanceTests.cs b/external/corefx/src/System.Composition.Convention/tests/PartBuilderInheritanceTests.cs index 989d3077e1..849e3bf8d2 100644 --- a/external/corefx/src/System.Composition.Convention/tests/PartBuilderInheritanceTests.cs +++ b/external/corefx/src/System.Composition.Convention/tests/PartBuilderInheritanceTests.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Composition.Convention.UnitTests; -using System.Linq; using System.Reflection; using Xunit; diff --git a/external/corefx/src/System.Composition.Convention/tests/PartBuilderInterfaceTests.cs b/external/corefx/src/System.Composition.Convention/tests/PartBuilderInterfaceTests.cs index 4fb6fda918..6293b0527e 100644 --- a/external/corefx/src/System.Composition.Convention/tests/PartBuilderInterfaceTests.cs +++ b/external/corefx/src/System.Composition.Convention/tests/PartBuilderInterfaceTests.cs @@ -3,14 +3,12 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Composition; -using System.Composition.Convention; using System.Composition.Hosting; using System.Linq; using System.Reflection; using Xunit; -namespace System.ComponentModel.Composition +namespace System.Composition.Convention { public class PartBuilderInterfaceTests { diff --git a/external/corefx/src/System.Composition.Hosting/tests/System/Composition/Hosting/Core/CompositionHostTests.cs b/external/corefx/src/System.Composition.Hosting/tests/System/Composition/Hosting/Core/CompositionHostTests.cs index 02b6874286..f319b9d182 100644 --- a/external/corefx/src/System.Composition.Hosting/tests/System/Composition/Hosting/Core/CompositionHostTests.cs +++ b/external/corefx/src/System.Composition.Hosting/tests/System/Composition/Hosting/Core/CompositionHostTests.cs @@ -11,6 +11,7 @@ namespace System.Composition.Hosting.Core.Tests public class CompositionHostTests { [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void GetExport_CompositionContextContract_ReturnsExpected() { using (CompositionHost host = CompositionHost.CreateCompositionHost(new ExportDescriptorProvider[0])) @@ -21,6 +22,7 @@ namespace System.Composition.Hosting.Core.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void GetExport_MultipleDependencies_ReturnsExpected() { using (CompositionHost host = CompositionHost.CreateCompositionHost(new MultipleDependency())) @@ -55,6 +57,7 @@ namespace System.Composition.Hosting.Core.Tests } [Theory] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] [InlineData(typeof(int), new Type[] { typeof(int) })] [InlineData(typeof(IList<>), new Type[] { typeof(IList<>) })] [InlineData(typeof(ICollection<>), new Type[] { typeof(ICollection<>) })] @@ -84,6 +87,7 @@ namespace System.Composition.Hosting.Core.Tests } [Theory] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] [InlineData(typeof(string[]), new Type[] { typeof(string[]), typeof(string) })] [InlineData(typeof(IList), new Type[] { typeof(IList), typeof(string) })] [InlineData(typeof(ICollection), new Type[] { typeof(ICollection), typeof(string) })] @@ -102,6 +106,7 @@ namespace System.Composition.Hosting.Core.Tests } [Theory] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] [InlineData(typeof(ExportFactory), null, new Type[] { typeof(int), typeof(ExportFactory) })] [InlineData(typeof(ExportFactory), new string[] { "1", "2", "3" }, new Type[] { typeof(int), typeof(ExportFactory) })] [InlineData(typeof(ExportFactory), null, new Type[] { typeof(int), typeof(ExportFactory) })] @@ -130,6 +135,7 @@ namespace System.Composition.Hosting.Core.Tests } [Theory] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] [InlineData(typeof(Lazy))] [InlineData(typeof(Lazy>))] [InlineData(typeof(Lazy))] @@ -143,6 +149,7 @@ namespace System.Composition.Hosting.Core.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void GetExport_AbstractMetadata_ThrowsInvalidOperationException() { using (CompositionHost host = CompositionHost.CreateCompositionHost(new ExportDescriptorProvider[0])) @@ -152,6 +159,7 @@ namespace System.Composition.Hosting.Core.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void GetExport_NullProviderInProviders_ThrowsNullReferenceException() { using (CompositionHost host = CompositionHost.CreateCompositionHost(new ExportDescriptorProvider[] { null })) @@ -170,6 +178,7 @@ namespace System.Composition.Hosting.Core.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void GetExport_MultipleReturns_ThrowsCompositionFailedException() { using (CompositionHost host = CompositionHost.CreateCompositionHost(new MultiplePromises())) @@ -192,6 +201,7 @@ namespace System.Composition.Hosting.Core.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void GetExport_FailedDependency_ThrowsCompositionFailedException() { using (CompositionHost host = CompositionHost.CreateCompositionHost(new FailedDependency())) diff --git a/external/corefx/src/System.Composition.Hosting/tests/System/Composition/Hosting/Core/CompositionOperationTests.cs b/external/corefx/src/System.Composition.Hosting/tests/System/Composition/Hosting/Core/CompositionOperationTests.cs index ce49c58dd4..41366b9aed 100644 --- a/external/corefx/src/System.Composition.Hosting/tests/System/Composition/Hosting/Core/CompositionOperationTests.cs +++ b/external/corefx/src/System.Composition.Hosting/tests/System/Composition/Hosting/Core/CompositionOperationTests.cs @@ -10,6 +10,7 @@ namespace System.Composition.Hosting.Core.Tests public class CompositionOperationTests { [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void Run_ValidContextAndAction_ReturnsExpected() { using (CompositionHost host = CompositionHost.CreateCompositionHost(new ExportDescriptorProvider[0])) @@ -49,6 +50,7 @@ namespace System.Composition.Hosting.Core.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void Run_NullActivator_ThrowsArgumentNullException() { using (CompositionHost host = CompositionHost.CreateCompositionHost(new ExportDescriptorProvider[0])) @@ -61,6 +63,7 @@ namespace System.Composition.Hosting.Core.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void AddNonPrequisiteAction_NullAction_ThrowsArgumentNullException() { object Activator(LifetimeContext context, CompositionOperation operation) @@ -79,6 +82,7 @@ namespace System.Composition.Hosting.Core.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void AddPostCompositionAction_NullAction_ThrowsArgumentNullException() { object Activator(LifetimeContext context, CompositionOperation operation) diff --git a/external/corefx/src/System.Composition.Hosting/tests/System/Composition/Hosting/Core/LifetimeContextTests.cs b/external/corefx/src/System.Composition.Hosting/tests/System/Composition/Hosting/Core/LifetimeContextTests.cs index 04f403ffab..a616b8abaf 100644 --- a/external/corefx/src/System.Composition.Hosting/tests/System/Composition/Hosting/Core/LifetimeContextTests.cs +++ b/external/corefx/src/System.Composition.Hosting/tests/System/Composition/Hosting/Core/LifetimeContextTests.cs @@ -15,6 +15,7 @@ namespace System.Composition.Hosting.Core.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void AddBoundInstance_NonNullInstance_DisposesInstanceOnDisposal() { using (CompositionHost host = CompositionHost.CreateCompositionHost(new ExportDescriptorProvider[0])) @@ -35,6 +36,7 @@ namespace System.Composition.Hosting.Core.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void AddBoundInstance_NullInstance_ThrowsNullReferenceExceptionOnDisposal() { CompositionHost host = CompositionHost.CreateCompositionHost(new ExportDescriptorProvider[0]); @@ -46,6 +48,7 @@ namespace System.Composition.Hosting.Core.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void AddBoundInstance_Disposed_ThrowsObjectDisposedException() { using (CompositionHost host = CompositionHost.CreateCompositionHost(new ExportDescriptorProvider[0])) @@ -59,6 +62,7 @@ namespace System.Composition.Hosting.Core.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void GetOrCreate_ValidActivatorDuringInitialization_Success() { using (CompositionHost host = CompositionHost.CreateCompositionHost(new ExportDescriptorProvider[0])) @@ -87,6 +91,7 @@ namespace System.Composition.Hosting.Core.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void GetOrCreate_ValidActivatorAfterInitialization_Success() { using (CompositionHost host = CompositionHost.CreateCompositionHost(new ExportDescriptorProvider[0])) @@ -116,6 +121,7 @@ namespace System.Composition.Hosting.Core.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void GetOrCreate_NullActivator_ThrowsNullReferenceException() { using (CompositionHost host = CompositionHost.CreateCompositionHost(new ExportDescriptorProvider[0])) @@ -134,6 +140,7 @@ namespace System.Composition.Hosting.Core.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void GetOrCreate_NullOperation_ThrowsNullReferenceException() { using (CompositionHost host = CompositionHost.CreateCompositionHost(new ExportDescriptorProvider[0])) @@ -146,6 +153,7 @@ namespace System.Composition.Hosting.Core.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void FindContextWithin_NullSharingBoundary_ReturnsRoot() { using (CompositionHost host = CompositionHost.CreateCompositionHost(new ExportDescriptorProvider[0])) @@ -158,6 +166,7 @@ namespace System.Composition.Hosting.Core.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void FindContextWithin_UnknownSharingBoundary_ThrowsCompositionFailedException() { using (CompositionHost host = CompositionHost.CreateCompositionHost(new ExportDescriptorProvider[0])) @@ -170,6 +179,7 @@ namespace System.Composition.Hosting.Core.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ToString_NoParent_ReturnsExpected() { using (CompositionHost host = CompositionHost.CreateCompositionHost(new ExportDescriptorProvider[0])) diff --git a/external/corefx/src/System.Composition.TypedParts/tests/ContainerConfigurationTests.cs b/external/corefx/src/System.Composition.TypedParts/tests/ContainerConfigurationTests.cs index 79bd9c28bf..cf54bc7d03 100644 --- a/external/corefx/src/System.Composition.TypedParts/tests/ContainerConfigurationTests.cs +++ b/external/corefx/src/System.Composition.TypedParts/tests/ContainerConfigurationTests.cs @@ -16,6 +16,7 @@ namespace System.Composition.Hosting.Tests public class ContainerConfigurationTests { [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void WithProvider_ValidProvider_RegistersProvider() { var configuration = new ContainerConfiguration(); @@ -62,6 +63,7 @@ namespace System.Composition.Hosting.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void WithDefaultConventions_PartWithNoMatchingConvention_Success() { var conventions = new ConventionBuilder(); @@ -76,6 +78,7 @@ namespace System.Composition.Hosting.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void WithDefaultConventions_IEnumerablePartsWithNoMatchingConvention_Success() { var conventions = new ConventionBuilder(); @@ -90,6 +93,7 @@ namespace System.Composition.Hosting.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void WithDefaultConventions_PartsArrayWithNoMatchingConvention_Success() { var conventions = new ConventionBuilder(); @@ -104,6 +108,7 @@ namespace System.Composition.Hosting.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void WithDefaultConventions_PartTNoMatchingConvention_Success() { var conventions = new ConventionBuilder(); @@ -139,6 +144,7 @@ namespace System.Composition.Hosting.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void WithPartT_Convention_Success() { var conventions = new ConventionBuilder(); @@ -152,6 +158,7 @@ namespace System.Composition.Hosting.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void WithPart_Convention_Success() { var conventions = new ConventionBuilder(); @@ -173,6 +180,7 @@ namespace System.Composition.Hosting.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void WithParts_Convention_Success() { var conventions = new ConventionBuilder(); @@ -255,6 +263,7 @@ namespace System.Composition.Hosting.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CreateContainer_ExportedSubClass_Success() { CompositionHost container = new ContainerConfiguration() @@ -275,6 +284,7 @@ namespace System.Composition.Hosting.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CreateContainer_OpenGenericTypes_Success() { var conventions = new ConventionBuilder(); @@ -304,6 +314,7 @@ namespace System.Composition.Hosting.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CreateContainer_ImportConventionsWithInheritedProperties_Success() { var conventions = new ConventionBuilder(); @@ -331,6 +342,7 @@ namespace System.Composition.Hosting.Tests public class DerivedFromBaseWithImport : BaseWithImport { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CreateContainer_ExportConventionsWithInheritedProperties_Success() { var conventions = new ConventionBuilder(); @@ -352,6 +364,7 @@ namespace System.Composition.Hosting.Tests public class DerivedFromBaseWithExport : BaseWithExport { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CreateContainer_ExportsToInheritedProperties_DontInterfereWithBase() { var conventions = new ConventionBuilder(); @@ -375,6 +388,7 @@ namespace System.Composition.Hosting.Tests public class DerivedFromBaseWithExport2 : BaseWithExport { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CreateContainer_HasConventions_ClassExportsAreNotInherited() { CompositionHost container = new ContainerConfiguration() @@ -384,6 +398,7 @@ namespace System.Composition.Hosting.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CreateContainer_HasConventions_PropertyExportsAreNotInherited() { CompositionHost container = new ContainerConfiguration() @@ -406,6 +421,7 @@ namespace System.Composition.Hosting.Tests public class CustomExport : ExportAttribute { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CreateContainer_HasConventions_CustomAttributesAreNotInherited() { CompositionHost container = new ContainerConfiguration() @@ -420,6 +436,7 @@ namespace System.Composition.Hosting.Tests public class DerivedFromBaseWithCustomExport : BaseWithCustomExport { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CreateContainer_OpenGenericTypePart_Success() { ContainerConfiguration configuration = new ContainerConfiguration().WithParts(typeof(GenericExportedType<>)); @@ -473,6 +490,7 @@ namespace System.Composition.Hosting.Tests public class ContractExportedType { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CreateContainer_AbstractOrStructType_Success() { ContainerConfiguration configuration = new ContainerConfiguration().WithParts(typeof(AbstractClass), typeof(StructType)); @@ -486,6 +504,7 @@ namespace System.Composition.Hosting.Tests public struct StructType { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CreateContainer_MetadataProperty_Success() { ContainerConfiguration configuration = new ContainerConfiguration().WithPart(typeof(MetadataProperty)); @@ -523,6 +542,7 @@ namespace System.Composition.Hosting.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CreateContainer_MetadataClass_Success() { ContainerConfiguration configuration = new ContainerConfiguration().WithPart(typeof(MetadataClass)); diff --git a/external/corefx/src/System.Composition.TypedParts/tests/ReflectionTests.cs b/external/corefx/src/System.Composition.TypedParts/tests/ReflectionTests.cs index 4795ccd1b2..054c877387 100644 --- a/external/corefx/src/System.Composition.TypedParts/tests/ReflectionTests.cs +++ b/external/corefx/src/System.Composition.TypedParts/tests/ReflectionTests.cs @@ -1,4 +1,8 @@ -using System.Collections.Concurrent; +// 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.Collections.Concurrent; using System.Composition.Hosting; using System.Threading; using Xunit; @@ -23,6 +27,7 @@ namespace System.Composition.TypedParts.Tests /// revert to a default value during GetExport. /// [ConditionalFact(nameof(HasMultiplerProcessors))] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void MultiThreadedGetExportsWorkWithImportingConstuctor() { var errors = new ConcurrentBag(); diff --git a/external/corefx/src/System.Composition/tests/ActivationEventOrderingTests.cs b/external/corefx/src/System.Composition/tests/ActivationEventOrderingTests.cs index a0b043e37f..ff6f87afba 100644 --- a/external/corefx/src/System.Composition/tests/ActivationEventOrderingTests.cs +++ b/external/corefx/src/System.Composition/tests/ActivationEventOrderingTests.cs @@ -32,6 +32,7 @@ namespace System.Composition.UnitTests public class ActivationEventOrderingTests : ContainerTests { [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void OnImportsSatisfiedIsCalledAfterPropertyInjection() { var cc = CreateContainer(typeof(TracksImportSatisfaction), typeof(Imported)); diff --git a/external/corefx/src/System.Composition/tests/CardinalityTests.cs b/external/corefx/src/System.Composition/tests/CardinalityTests.cs index 69d74e7180..c0aff2fb9b 100644 --- a/external/corefx/src/System.Composition/tests/CardinalityTests.cs +++ b/external/corefx/src/System.Composition/tests/CardinalityTests.cs @@ -32,6 +32,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void RequestingOneWhereMultipleArePresentFails() { var c = CreateContainer(typeof(LogA), typeof(LogB)); @@ -42,6 +43,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ImportingOneWhereMultipleArePresentFails() { var c = CreateContainer(typeof(LogA), typeof(LogB), typeof(UsesLog)); diff --git a/external/corefx/src/System.Composition/tests/CircularityTests.cs b/external/corefx/src/System.Composition/tests/CircularityTests.cs index 1f54933904..6b30491040 100644 --- a/external/corefx/src/System.Composition/tests/CircularityTests.cs +++ b/external/corefx/src/System.Composition/tests/CircularityTests.cs @@ -117,6 +117,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CanHandleDefinitionCircularity() { var cc = CreateContainer(typeof(ACircular), typeof(BLazy)); @@ -126,6 +127,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CanHandleDefinitionCircularity2() { var cc = CreateContainer(typeof(ACircular), typeof(BLazy)); @@ -134,6 +136,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void HandlesPropertyPropertyCircularity() { var cc = CreateContainer(typeof(PropertyPropertyA), typeof(PropertyPropertyB)); @@ -142,6 +145,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void HandlesPropertyPropertyCircularityReversed() { var cc = CreateContainer(typeof(PropertyPropertyA), typeof(PropertyPropertyB)); @@ -150,6 +154,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void HandlesConstructorPropertyCircularity() { var cc = CreateContainer(typeof(ConstructorPropertyA), typeof(ConstructorPropertyB)); @@ -158,6 +163,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void HandlesConstructorPropertyCircularityReversed() { var cc = CreateContainer(typeof(ConstructorPropertyA), typeof(ConstructorPropertyB)); @@ -166,6 +172,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void HandlesMetadataCircularity() { var cc = CreateContainer(typeof(MetadataCircularityA), typeof(MetadataCircularityB)); @@ -176,6 +183,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void SharedPartCanHaveNonPrereqDependencyOnSelf() { var cc = CreateContainer(typeof(NonPrereqSelfDependency)); @@ -184,6 +192,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void PrerequisiteCircularitiesAreDetected() { var cc = CreateContainer(typeof(PrDepA), typeof(PrDepB)); diff --git a/external/corefx/src/System.Composition/tests/CompositionContextExtensionsTests.cs b/external/corefx/src/System.Composition/tests/CompositionContextExtensionsTests.cs index fa096895d8..40270315d9 100644 --- a/external/corefx/src/System.Composition/tests/CompositionContextExtensionsTests.cs +++ b/external/corefx/src/System.Composition/tests/CompositionContextExtensionsTests.cs @@ -16,6 +16,7 @@ namespace System.Composition.UnitTests public interface IUnregistered { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void GettingAnOptionalExportThatDoesntExistReturnsNull() { var c = CreateContainer(); diff --git a/external/corefx/src/System.Composition/tests/ConcurrencyTests.cs b/external/corefx/src/System.Composition/tests/ConcurrencyTests.cs index 7d3c106c92..d9c96f6f7e 100644 --- a/external/corefx/src/System.Composition/tests/ConcurrencyTests.cs +++ b/external/corefx/src/System.Composition/tests/ConcurrencyTests.cs @@ -29,6 +29,7 @@ namespace System.Composition.UnitTests // This does not test the desired behaviour deterministically, // but is close enough to be repeatable at least on my machine :) [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void SharedInstancesAreNotVisibleUntilActivationCompletes() { var c = CreateContainer(typeof(PausesDuringActivation)); diff --git a/external/corefx/src/System.Composition/tests/ConstraintTests.cs b/external/corefx/src/System.Composition/tests/ConstraintTests.cs index d8b77f22a7..ef26d31153 100644 --- a/external/corefx/src/System.Composition/tests/ConstraintTests.cs +++ b/external/corefx/src/System.Composition/tests/ConstraintTests.cs @@ -27,6 +27,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void GenericPartDiscoveryIgnoresAPartAndDoesntThrowAnExceptionWhenItsConstraintOnTypeParameterIsNotAssignableFromTheExportTarget() { var container = CreateContainer(typeof(ThingHandler<>), typeof(BookHandler<>)); @@ -38,6 +39,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void GenericPartDiscoveryIncludesAPartWhenItsConstraintOnTypeParameterIsAssignableFromTheExportTarget() { var container = CreateContainer(typeof(ThingHandler<>), typeof(BookHandler<>)); diff --git a/external/corefx/src/System.Composition/tests/CustomerReportedMetadataBug.cs b/external/corefx/src/System.Composition/tests/CustomerReportedMetadataBug.cs index 9639e3e52a..bb2fbac230 100644 --- a/external/corefx/src/System.Composition/tests/CustomerReportedMetadataBug.cs +++ b/external/corefx/src/System.Composition/tests/CustomerReportedMetadataBug.cs @@ -38,6 +38,7 @@ namespace System.Composition.Lightweight.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void SampleServicesCorrectlyImported() { var container = new ContainerConfiguration() diff --git a/external/corefx/src/System.Composition/tests/DictionaryImportTests.cs b/external/corefx/src/System.Composition/tests/DictionaryImportTests.cs index fc17e17c32..c6b0861b9c 100644 --- a/external/corefx/src/System.Composition/tests/DictionaryImportTests.cs +++ b/external/corefx/src/System.Composition/tests/DictionaryImportTests.cs @@ -69,6 +69,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void DictionaryImportsKeyedByMetadata() { var container = CreateContainer(new[] { typeof(ValueA), typeof(ValueB), typeof(Consumer) }); @@ -81,6 +82,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void DictionaryImportsRecieveMetadataFromNestedAdapters() { var container = CreateContainer(new[] { typeof(ValueA), typeof(ValueB), typeof(LazyConsumer) }); @@ -92,6 +94,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void WhenAMetadataKeyIsDuplicatedAnInformativeExceptionIsThrown() { var container = CreateContainer(typeof(ValueA), typeof(ValueA), typeof(Consumer)); @@ -100,6 +103,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void WhenAMetadataKeyIsMissingAnInformativeExceptionIsThrown() { var container = CreateContainer(typeof(ValueA), typeof(ValueMissing), typeof(Consumer)); @@ -108,6 +112,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void WhenAMetadataValueIsOfTheWrongTypeAnInformativeExceptionIsThrown() { var container = CreateContainer(typeof(ValueA), typeof(NonStringValue), typeof(Consumer)); @@ -116,6 +121,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void DictionaryImportsCompatibleWithConventionBuilder() { var rb = new ConventionBuilder(); diff --git a/external/corefx/src/System.Composition/tests/DiscoveryTests.cs b/external/corefx/src/System.Composition/tests/DiscoveryTests.cs index 9aec3aff43..01add73278 100644 --- a/external/corefx/src/System.Composition/tests/DiscoveryTests.cs +++ b/external/corefx/src/System.Composition/tests/DiscoveryTests.cs @@ -41,6 +41,7 @@ namespace System.Composition.UnitTests public class NotDiscoverable { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void DiscoversCustomExportAttributes() { var container = CreateContainer(typeof(UnfairRule)); @@ -49,6 +50,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void DiscoversCustomExportAttributesUnderConventions() { var container = CreateContainer(new ConventionBuilder(), typeof(UnfairRule)); @@ -58,6 +60,7 @@ namespace System.Composition.UnitTests [Fact] [ActiveIssue("https://github.com/dotnet/corefx/issues/20656", TargetFrameworkMonikers.UapAot)] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void InstanceExportsOfIncompatibleContractsAreDetected() { var x = Assert.Throws(() => CreateContainer(typeof(IncompatibleRule))); @@ -66,6 +69,7 @@ namespace System.Composition.UnitTests [Fact] [ActiveIssue("https://github.com/dotnet/corefx/issues/20656", TargetFrameworkMonikers.UapAot)] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void PropertyExportsOfIncompatibleContractsAreDetected() { var x = Assert.Throws(() => CreateContainer(typeof(IncompatibleRuleProperty))); @@ -73,6 +77,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ANonDiscoverablePartIsIgnored() { var container = CreateContainer(typeof(NotDiscoverable)); @@ -88,6 +93,7 @@ namespace System.Composition.UnitTests public class SpecialCloudBus : CloudBus { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void DoesNotDiscoverExportAttributesFromBase() { var container = CreateContainer(typeof(SpecialCloudBus)); @@ -108,6 +114,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void SatisfiesImportsAppliedToBase() { var container = CreateContainer(typeof(HomeController), typeof(CloudBus)); @@ -126,6 +133,7 @@ namespace System.Composition.UnitTests [Fact] [ActiveIssue("https://github.com/dotnet/corefx/issues/20656", TargetFrameworkMonikers.UapAot)] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void MultipleImportAttributesAreDetected() { var c = new ContainerConfiguration() diff --git a/external/corefx/src/System.Composition/tests/ErrorMessageQualityTests.cs b/external/corefx/src/System.Composition/tests/ErrorMessageQualityTests.cs index 68c53fe33e..adcb87d845 100644 --- a/external/corefx/src/System.Composition/tests/ErrorMessageQualityTests.cs +++ b/external/corefx/src/System.Composition/tests/ErrorMessageQualityTests.cs @@ -65,6 +65,7 @@ namespace System.Composition.UnitTests [Fact] [ActiveIssue("https://github.com/dotnet/corefx/issues/20656", TargetFrameworkMonikers.UapAot)] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void MissingTopLevelExportMessageIsInformative() { var cc = CreateContainer(); @@ -74,6 +75,7 @@ namespace System.Composition.UnitTests [Fact] [ActiveIssue("https://github.com/dotnet/corefx/issues/20656", TargetFrameworkMonikers.UapAot)] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void MissingTopLevelNamedExportMessageIsInformative() { var cc = CreateContainer(); @@ -82,6 +84,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void MissingDependencyMessageIsInformative() { var cc = CreateContainer(typeof(UserOfUnregistered)); @@ -92,6 +95,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CycleMessageIsInformative() { var cc = CreateContainer(typeof(CycleA), typeof(CycleB), typeof(CycleC)); @@ -105,6 +109,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CardinalityViolationMessageIsInformative() { var cc = CreateContainer(typeof(ShouldBeOne), typeof(ButThereIsAnother), typeof(RequiresOnlyOne)); diff --git a/external/corefx/src/System.Composition/tests/ExportDescriptorProviderTests.cs b/external/corefx/src/System.Composition/tests/ExportDescriptorProviderTests.cs index 24ec717eb5..57da575d85 100644 --- a/external/corefx/src/System.Composition/tests/ExportDescriptorProviderTests.cs +++ b/external/corefx/src/System.Composition/tests/ExportDescriptorProviderTests.cs @@ -41,6 +41,7 @@ namespace System.Composition.Lightweight.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ProvidersCanLocateImplementationsOfAContractItSupports() { var container = new ContainerConfiguration() @@ -53,6 +54,7 @@ namespace System.Composition.Lightweight.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ProvidersCanDetectAbsenceOfAContractItSupports() { var container = new ContainerConfiguration() diff --git a/external/corefx/src/System.Composition/tests/ExportFactoryTests.cs b/external/corefx/src/System.Composition/tests/ExportFactoryTests.cs index 2f73f6e5ce..2e384d211c 100644 --- a/external/corefx/src/System.Composition/tests/ExportFactoryTests.cs +++ b/external/corefx/src/System.Composition/tests/ExportFactoryTests.cs @@ -106,6 +106,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void SharedPartsAreSharedBetweenAllScopes() { var cc = CreateContainer(typeof(SharedUnbounded), typeof(DataConsistencyBoundaryProvider)); @@ -117,6 +118,7 @@ namespace System.Composition.UnitTests [Fact] [ActiveIssue("https://github.com/dotnet/corefx/issues/20656", TargetFrameworkMonikers.UapAot)] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void TheSameSharedInstanceIsReusedWithinItsSharingBoundary() { var cc = CreateContainer(typeof(SharedBoundedByDC), typeof(SharedPartConsumer), typeof(DataConsistencyBoundaryProvider)); @@ -130,6 +132,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void NonSharedInstancesCreatedByAnExportFactoryAreControlledByTheirExportLifetimeContext() { var cc = CreateContainer(typeof(A), typeof(UseExportFactory)); @@ -142,6 +145,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void DependenciesOfSharedPartsAreResolvedInTheGlobalScope() { var cc = new ContainerConfiguration() @@ -157,6 +161,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void WhenABoundaryIsPresentBoundedPartsCannotBeCreatedOutsideIt() { var container = CreateContainer(typeof(DataConsistencyBoundaryProvider), typeof(SharedBoundedByDC)); @@ -164,6 +169,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void TheProductOfAnExportFactoryCanBeDisposedDuringDisposalOfTheParent() { var container = new ContainerConfiguration() @@ -195,6 +201,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ExportFactoryCanBeComposedWithImportManyAndNames() { var cc = CreateContainer(typeof(AConsumer), typeof(A1), typeof(A2)); @@ -228,6 +235,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void WhenReleasingAnExportFromAnExportFactoryItsNonSharedDependenciesAreDisposed() { var cc = CreateContainer(typeof(Disposable), typeof(HasDisposableDependency), typeof(HasFactory)); diff --git a/external/corefx/src/System.Composition/tests/ExportMetadataDiscoveryTests.cs b/external/corefx/src/System.Composition/tests/ExportMetadataDiscoveryTests.cs index b1110763cf..38de796a43 100644 --- a/external/corefx/src/System.Composition/tests/ExportMetadataDiscoveryTests.cs +++ b/external/corefx/src/System.Composition/tests/ExportMetadataDiscoveryTests.cs @@ -52,6 +52,7 @@ namespace System.Composition.UnitTests public class MultipleNames { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void DiscoversMetadataSpecifiedUsingMetadataAttributeOnExportAttribute() { var cc = CreateContainer(typeof(SingleNamedExport)); @@ -60,6 +61,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void IfMetadataIsSpecifiedOnAnExportAttributeOtherExportsDoNotHaveIt() { var cc = CreateContainer(typeof(MultipleExportsOneNamedAndBothPrioritized)); @@ -69,6 +71,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void DiscoversStandaloneExportMetadata() { var cc = CreateContainer(typeof(NamedAndPrioritized)); @@ -77,6 +80,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void DiscoversStandaloneExportMetadataUsingMetadataAttributes() { var cc = CreateContainer(typeof(NamedWithCustomMetadata)); @@ -85,6 +89,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void StandaloneExportMetadataAppliesToAllExportsOnAMember() { var cc = CreateContainer(typeof(MultipleExportsOneNamedAndBothPrioritized)); @@ -94,6 +99,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void MultiplePiecesOfMetadataAreCombinedIntoAnArray() { var cc = CreateContainer(typeof(MultipleNames)); @@ -114,6 +120,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void MultipleExportsCanBeRetrievedWhenANonDefaultConstructorExists() { var c = CreateContainer(typeof(ConstructorImported), typeof(MultipleExportsNonDefaultConstructor)); diff --git a/external/corefx/src/System.Composition/tests/ImportManyTests.cs b/external/corefx/src/System.Composition/tests/ImportManyTests.cs index cc7365292c..21720aef5d 100644 --- a/external/corefx/src/System.Composition/tests/ImportManyTests.cs +++ b/external/corefx/src/System.Composition/tests/ImportManyTests.cs @@ -42,6 +42,7 @@ namespace System.Composition.UnitTests } } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ImportsMany() { var cc = CreateContainer(typeof(A), typeof(A2), typeof(ImportManyIA)); @@ -50,6 +51,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ImportsManyProperties() { var cc = CreateContainer(typeof(A), typeof(A2), typeof(ImportManyPropsOfA)); diff --git a/external/corefx/src/System.Composition/tests/ImportOrderingTests.cs b/external/corefx/src/System.Composition/tests/ImportOrderingTests.cs index 543d87426b..8ac1de9e80 100644 --- a/external/corefx/src/System.Composition/tests/ImportOrderingTests.cs +++ b/external/corefx/src/System.Composition/tests/ImportOrderingTests.cs @@ -49,6 +49,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CollectionsImportedWithAnOrderingAttributeComeInOrder() { var container = CreateExtendedContainer(typeof(HasImportedItems), typeof(Item1), typeof(Item4), typeof(Item2), typeof(Item3)); @@ -62,6 +63,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void IfAnItemIsMissingMetadataAnInformativeExceptionIsThrown() { var container = CreateExtendedContainer(typeof(HasImportedItems), typeof(Item1), typeof(ItemWithoutOrder)); diff --git a/external/corefx/src/System.Composition/tests/InheritanceTests.cs b/external/corefx/src/System.Composition/tests/InheritanceTests.cs index bc9c704dfd..ba40e844d0 100644 --- a/external/corefx/src/System.Composition/tests/InheritanceTests.cs +++ b/external/corefx/src/System.Composition/tests/InheritanceTests.cs @@ -26,6 +26,7 @@ namespace System.Composition.Lightweight.UnitTests public class Derived : Base { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ClassExportsAreNotInherited() { var cc = CreateContainer(typeof(Derived)); @@ -34,6 +35,7 @@ namespace System.Composition.Lightweight.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void PropertyExportsAreNotInherited() { var cc = CreateContainer(typeof(Derived)); @@ -45,6 +47,7 @@ namespace System.Composition.Lightweight.UnitTests public class ExportingDerived : Base { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ExportsAtTheClassLevelAreAppliedIgnoringBaseExports() { var cc = CreateContainer(typeof(ExportingDerived)); @@ -85,6 +88,7 @@ namespace System.Composition.Lightweight.UnitTests public class NonOverridingImporter : BaseImporter { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ImportsOnOverriddenPropertiesOverrideImportsOnTheBase() { var c = CreateContainer(typeof(Exporter), typeof(OverridingImporter), typeof(NonOverridingImporter)); @@ -95,6 +99,7 @@ namespace System.Composition.Lightweight.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void LooseImportsOnDerivedPropertiesOverrideImportsOnTheBase() { var c = CreateContainer(typeof(Exporter)); @@ -107,6 +112,7 @@ namespace System.Composition.Lightweight.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ImportsOnBaseAreInherited() { var c = CreateContainer(typeof(Exporter), typeof(NonOverridingImporter)); @@ -116,6 +122,7 @@ namespace System.Composition.Lightweight.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void LooseImportsOnBaseAreInherited() { var c = CreateContainer(typeof(Exporter)); @@ -131,6 +138,7 @@ namespace System.Composition.Lightweight.UnitTests public class DiscoverableDerived : NotDiscoverableBase { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void PartNotDiscoverableAttributeIsNotInherited() { var c = CreateContainer(typeof(DiscoverableDerived)); @@ -145,6 +153,7 @@ namespace System.Composition.Lightweight.UnitTests public class SharedDerived : SharedBase { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void PartMetadataIsNotInherited() { var c = CreateContainer(typeof(SharedDerived)); @@ -165,6 +174,7 @@ namespace System.Composition.Lightweight.UnitTests public class InheritsImportsSatisfied : HasImportsSatisfied { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void OnImportsSatisfiedAttributeIsInherited() { var c = CreateContainer(typeof(InheritsImportsSatisfied)); @@ -183,6 +193,7 @@ namespace System.Composition.Lightweight.UnitTests public class ABHandler : AHandler { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void MetadataIsOnlyDrawnFromTheTypeToWhichItIsApplied() { var c = CreateContainer(typeof(ABHandler)); @@ -204,6 +215,7 @@ namespace System.Composition.Lightweight.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ExportsOnOverridePropertiesOverrideExportsOnTheBase() { var c = CreateContainer(typeof(DerivedOverrideExporter)); diff --git a/external/corefx/src/System.Composition/tests/LazyTests.cs b/external/corefx/src/System.Composition/tests/LazyTests.cs index cc8c50729d..3004def1c1 100644 --- a/external/corefx/src/System.Composition/tests/LazyTests.cs +++ b/external/corefx/src/System.Composition/tests/LazyTests.cs @@ -36,6 +36,7 @@ namespace System.Composition.UnitTests public class Named { public string Name { get; set; } } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ComposesLazily() { var cc = CreateContainer(typeof(A), typeof(BLazy)); @@ -44,6 +45,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void SupportsExportMetadata() { var cc = CreateContainer(typeof(NamedFred)); @@ -52,6 +54,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ReturnsExportMetadataAsADictionary() { var cc = CreateContainer(typeof(NamedFred)); @@ -73,6 +76,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void LazyCanBeComposedWithImportManyAndNames() { var cc = CreateContainer(typeof(AConsumer), typeof(A1), typeof(A2)); diff --git a/external/corefx/src/System.Composition/tests/LightContainerTests.cs b/external/corefx/src/System.Composition/tests/LightContainerTests.cs index 64ce43afa3..c8a34c84c6 100644 --- a/external/corefx/src/System.Composition/tests/LightContainerTests.cs +++ b/external/corefx/src/System.Composition/tests/LightContainerTests.cs @@ -54,6 +54,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CreatesInstanceWithNoDependencies() { var cc = CreateContainer(typeof(A)); @@ -62,6 +63,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void DefaultLifetimeIsNonShared() { var cc = CreateContainer(typeof(A)); @@ -71,6 +73,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void Composes() { var cc = CreateContainer(typeof(A), typeof(B)); @@ -79,6 +82,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CanSpecifyExportsWithConventionBuilder() { var rb = new ConventionBuilder(); @@ -89,6 +93,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CanSpecifyLifetimeWithConventionBuilder() { var rb = new ConventionBuilder(); @@ -100,6 +105,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void InjectsPropertyImports() { var rb = new ConventionBuilder(); @@ -110,6 +116,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void VerifyAssemblyNameCanBeUsedWithContainer() { var test = new ContainerConfiguration() @@ -121,6 +128,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void VerifyAssemblyWithTwoBaseTypeWithOnlyOneExportedWorks() { var test = new ContainerConfiguration() diff --git a/external/corefx/src/System.Composition/tests/LooseImportsTests.cs b/external/corefx/src/System.Composition/tests/LooseImportsTests.cs index 214f055440..2052458500 100644 --- a/external/corefx/src/System.Composition/tests/LooseImportsTests.cs +++ b/external/corefx/src/System.Composition/tests/LooseImportsTests.cs @@ -24,6 +24,7 @@ namespace System.Composition.Lightweight.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void SatisfyImportsSetsLooseImportsOnAttributedPart() { var container = CreateContainer(typeof(Transaction)); diff --git a/external/corefx/src/System.Composition/tests/MetadataConstraintTests.cs b/external/corefx/src/System.Composition/tests/MetadataConstraintTests.cs index 324ddb0ca6..915b03455f 100644 --- a/external/corefx/src/System.Composition/tests/MetadataConstraintTests.cs +++ b/external/corefx/src/System.Composition/tests/MetadataConstraintTests.cs @@ -32,6 +32,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void AnImportMetadataConstraintMatchesMetadataOnTheExport() { var cc = CreateContainer(typeof(SomeSetting), typeof(SomeSettingUser)); @@ -40,6 +41,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void AnImportMetadataConstraintMatchesMetadataOnTheExportEvenIfDiscoveryHasCompletedForTheExport() { var cc = CreateContainer(typeof(SomeSetting), typeof(SomeSettingUser)); @@ -49,6 +51,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ImportMetadataConstraintsComposeWithOtherRelationshipTypes() { var cc = CreateContainer(typeof(SomeSetting), typeof(ManySettingUser)); @@ -60,6 +63,7 @@ namespace System.Composition.UnitTests public class SomeSetting { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ConstraintsCanBeAppliedToGenerics() { var contract = new CompositionContract(typeof(SomeSetting), null, new Dictionary @@ -83,6 +87,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ItemEqualityIsUsedWhenMatchingMetadataValuesThatAreArrays() { var c = CreateContainer(typeof(Presenter), typeof(Controller)); diff --git a/external/corefx/src/System.Composition/tests/MetadataTests.cs b/external/corefx/src/System.Composition/tests/MetadataTests.cs index d3e2af0537..318d8a49dd 100644 --- a/external/corefx/src/System.Composition/tests/MetadataTests.cs +++ b/external/corefx/src/System.Composition/tests/MetadataTests.cs @@ -32,6 +32,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void HandlesMetadataCircularity() { var cc = CreateContainer(typeof(MetadataCircularityA), typeof(MetadataCircularityB)); @@ -90,6 +91,7 @@ namespace System.Composition.UnitTests public class Prioritized {[DefaultValue(0)] public int Priority { get; set; } } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void MultipleMetadataAttributesWithAPropertyThatReturnsNull() { var cc = CreateContainer(typeof(NameNullTwiceExport)); @@ -98,6 +100,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void DiscoversMetadataSpecifiedUsingMetadataAttributeOnExportAttribute() { var cc = CreateContainer(typeof(SingleNamedExport)); @@ -106,6 +109,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void IfMetadataIsSpecifiedOnAnExportAttributeOtherExportsDoNotHaveIt() { var cc = CreateContainer(typeof(MultipleExportsOneNamedAndBothPrioritized)); @@ -115,6 +119,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void DiscoversStandaloneExportMetadata() { var cc = CreateContainer(typeof(NamedAndPrioritized)); @@ -123,6 +128,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void DiscoversStandaloneExportMetadataUsingMetadataAttributes() { var cc = CreateContainer(typeof(NamedWithCustomMetadata)); @@ -131,6 +137,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void StandaloneExportMetadataAppliesToAllExportsOnAMember() { var cc = CreateContainer(typeof(MultipleExportsOneNamedAndBothPrioritized)); @@ -140,6 +147,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void MultiplePiecesOfMetadataAreCombinedIntoAnArray() { var cc = CreateContainer(typeof(MultipleNames)); @@ -153,6 +161,7 @@ namespace System.Composition.UnitTests public class NamedFred { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void SupportsExportMetadata() { var cc = CreateContainer(typeof(NamedFred)); diff --git a/external/corefx/src/System.Composition/tests/MetadataViewGenerationTests.cs b/external/corefx/src/System.Composition/tests/MetadataViewGenerationTests.cs index 2eb06d056b..9887fd4fe6 100644 --- a/external/corefx/src/System.Composition/tests/MetadataViewGenerationTests.cs +++ b/external/corefx/src/System.Composition/tests/MetadataViewGenerationTests.cs @@ -22,6 +22,7 @@ namespace System.Composition.Lightweight.UnitTests public class Named { public string Name { get; set; } } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void AConcreteTypeWithWritablePropertiesIsAMetadataView() { var cc = new ContainerConfiguration() @@ -39,6 +40,7 @@ namespace System.Composition.Lightweight.UnitTests public class OptionallyNamed {[DefaultValue("B")] public string Name { get; set; } } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void MetadataViewsCanCarryDefaultValues() { var cc = new ContainerConfiguration() @@ -61,6 +63,7 @@ namespace System.Composition.Lightweight.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void AConcreteTypeWithDictionaryConstructorIsAMetadataView() { var cc = new ContainerConfiguration() @@ -79,6 +82,7 @@ namespace System.Composition.Lightweight.UnitTests [Fact] [ActiveIssue("https://github.com/dotnet/corefx/issues/20656", TargetFrameworkMonikers.UapAot)] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void AConcreteTypeWithUnsupportedConstructorsCannotBeUsedAsAMetadataView() { var cc = new ContainerConfiguration() @@ -104,6 +108,7 @@ namespace System.Composition.Lightweight.UnitTests [Fact] [ActiveIssue("https://github.com/dotnet/corefx/issues/20656", TargetFrameworkMonikers.UapAot)] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void UnsupportedMetadataViewMessageIsInformative() { var cc = new ContainerConfiguration().WithParts(typeof(ImportsWithMetadataInterface), typeof(ExportsWithMetadata)).CreateContainer(); @@ -121,6 +126,7 @@ namespace System.Composition.Lightweight.UnitTests public class HasOrder { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ReadOnlyPropertiesOnMetadataViewsAreIgnored() { var c = new ContainerConfiguration() diff --git a/external/corefx/src/System.Composition/tests/OpenGenericsTests.cs b/external/corefx/src/System.Composition/tests/OpenGenericsTests.cs index c676c67d5b..6457793f2c 100644 --- a/external/corefx/src/System.Composition/tests/OpenGenericsTests.cs +++ b/external/corefx/src/System.Composition/tests/OpenGenericsTests.cs @@ -65,6 +65,7 @@ namespace System.Composition.UnitTests private class ExportsBase : SomeGenericType { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CanExportBasicOpenGeneric() { var cc = CreateContainer(typeof(BasicRepository<>)); @@ -73,6 +74,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void OpenGenericProvidesMultipleInstantiations() { var cc = CreateContainer(typeof(BasicRepository<>)); @@ -82,6 +84,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CanExportOpenGenericProperty() { var cc = CreateContainer(typeof(RepositoryProperty<>)); @@ -90,6 +93,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ASharedOpenGenericWithTwoExportsIsProvidedByASingleInstance() { var cc = CreateContainer(typeof(TwoGenericExports<>)); @@ -101,6 +105,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void APartWithMultipleGenericExportsIsOnlyDiscoveredOnce() { var cc = CreateContainer(typeof(BasicRepository<>), typeof(TwoGenericExports<>)); @@ -114,6 +119,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void MultipleGenericExportsCanBeSpecifiedAtTheClassLevel() { var cc = CreateContainer(typeof(FirstAndSecond<>)); @@ -125,6 +131,7 @@ namespace System.Composition.UnitTests // ignored tests above). [Fact] [ActiveIssue("https://github.com/dotnet/corefx/issues/20656", TargetFrameworkMonikers.UapAot)] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void TypesWithMismatchedGenericParameterListsAreDetectedDuringDiscovery() { var x = Assert.Throws(() => CreateContainer(typeof(RepositoryWithKey<,>))); @@ -133,6 +140,7 @@ namespace System.Composition.UnitTests [Fact] [ActiveIssue("https://github.com/dotnet/corefx/issues/20656", TargetFrameworkMonikers.UapAot)] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void TypesWithNonGenericExportsAreDetectedDuringDiscovery() { var x = Assert.Throws(() => CreateContainer(typeof(RepositoryWithNonGenericExport<>))); @@ -140,6 +148,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void OpenGenericsCanExportSelf() { var cc = CreateContainer(typeof(ExportSelf<>)); @@ -148,6 +157,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void OpenGenericsCanExportBase() { var cc = CreateContainer(typeof(ExportsBase<>)); diff --git a/external/corefx/src/System.Composition/tests/OptionalImportTests.cs b/external/corefx/src/System.Composition/tests/OptionalImportTests.cs index 932060f992..5b95e1a62f 100644 --- a/external/corefx/src/System.Composition/tests/OptionalImportTests.cs +++ b/external/corefx/src/System.Composition/tests/OptionalImportTests.cs @@ -55,6 +55,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void MissingOptionalConstructorParametersAreSuppliedTheirDefaultValue() { var cc = CreateContainer(typeof(Supplied), typeof(HasOptionalConstructorParameter)); @@ -64,6 +65,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void MissingOptionalPropertyImportsAreIgnored() { var cc = CreateContainer(typeof(Supplied), typeof(HasOptionalProperty)); diff --git a/external/corefx/src/System.Composition/tests/PropertyExportTests.cs b/external/corefx/src/System.Composition/tests/PropertyExportTests.cs index bb482c2745..501da87c1f 100644 --- a/external/corefx/src/System.Composition/tests/PropertyExportTests.cs +++ b/external/corefx/src/System.Composition/tests/PropertyExportTests.cs @@ -20,6 +20,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CanExportProperty() { var cc = CreateContainer(typeof(Messenger)); @@ -44,6 +45,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ExportedPropertiesShareTheSameSharedPartInstance() { var cc = CreateContainer(typeof(SelfObsessed), typeof(Selfless)); diff --git a/external/corefx/src/System.Composition/tests/SharingTests.cs b/external/corefx/src/System.Composition/tests/SharingTests.cs index 65cfbc9d32..9d9d9c4602 100644 --- a/external/corefx/src/System.Composition/tests/SharingTests.cs +++ b/external/corefx/src/System.Composition/tests/SharingTests.cs @@ -319,6 +319,7 @@ namespace System.Composition.UnitTests /// we fail only when we create instance of B.. is that correct. /// [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void BoundaryExposedBoundaryButNoneImported() { try @@ -339,6 +340,7 @@ namespace System.Composition.UnitTests /// [Fact] [ActiveIssue("https://github.com/dotnet/corefx/issues/20656", TargetFrameworkMonikers.UapAot)] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void BoundarySharingTest() { var cc = CreateContainer(typeof(A), typeof(B), typeof(C), typeof(D)); @@ -361,6 +363,7 @@ namespace System.Composition.UnitTests /// [Fact] [ActiveIssue("https://github.com/dotnet/corefx/issues/20656", TargetFrameworkMonikers.UapAot)] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void CircularBoundarySharingTest() { var cc = CreateContainer(typeof(CirA), typeof(CirB), typeof(CirC)); @@ -378,6 +381,7 @@ namespace System.Composition.UnitTests /// [Fact] [ActiveIssue("https://github.com/dotnet/corefx/issues/20656", TargetFrameworkMonikers.UapAot)] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void MultipleBoundarySpecified() { var cc = CreateContainer(typeof(ProjA), typeof(ProjB), typeof(SolA), typeof(DocA), typeof(DocB), typeof(ColA), typeof(ColB)); @@ -387,6 +391,7 @@ namespace System.Composition.UnitTests [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void SharedPartExportingMultipleContractsSharesAnInstance() { var cc = CreateContainer(typeof(XY)); @@ -396,6 +401,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void GetExportsCreatesInstancedObjectByDefault() { var cc = CreateContainer(typeof(NonSharedClass)); @@ -405,6 +411,7 @@ namespace System.Composition.UnitTests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void GetExportsCreatesSharedObjectsWhenSpecified() { var cc = CreateContainer(typeof(SharedClass)); @@ -419,6 +426,7 @@ namespace System.Composition.UnitTests /// verify that On Method call different instances are returned. /// [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ExportFactoryCreatesNewInstances() { var cc = CreateContainer(typeof(ClassWithExportFactoryShared), typeof(NonSharedClass)); @@ -434,6 +442,7 @@ namespace System.Composition.UnitTests /// ExportFactory should be importable as a property /// [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ClassWithExportFactoryAsAProperty() { var cc = CreateContainer(typeof(ClassWithExportFactoryAsAProperty), typeof(NonSharedClass)); @@ -450,6 +459,7 @@ namespace System.Composition.UnitTests /// is creating a part which is shared, it will return back the same instance of the part. /// [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ClassWithExportFactoryAndSharedExport() { var cc = CreateContainer(typeof(ClassWithExportFactoryShared), typeof(SharedClass)); @@ -466,6 +476,7 @@ namespace System.Composition.UnitTests /// Two instances of the root class are created , the part created using export factory should not be shared /// [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ClassWithNonSharedExportFactoryCreatesSharedInstances() { var cc = CreateContainer(typeof(ClassWithExportFactoryNonShared), typeof(SharedClass)); @@ -481,6 +492,7 @@ namespace System.Composition.UnitTests public class ASharedPart { } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void ConsistentResultsAreReturneWhenResolvingLargeNumbersOfSharedParts() { var config = new ContainerConfiguration(); diff --git a/external/corefx/src/System.Configuration.ConfigurationManager/ref/System.Configuration.cs.REMOVED.git-id b/external/corefx/src/System.Configuration.ConfigurationManager/ref/System.Configuration.cs.REMOVED.git-id index a54aa6324a..f118ce1e9a 100644 --- a/external/corefx/src/System.Configuration.ConfigurationManager/ref/System.Configuration.cs.REMOVED.git-id +++ b/external/corefx/src/System.Configuration.ConfigurationManager/ref/System.Configuration.cs.REMOVED.git-id @@ -1 +1 @@ -e7ec34477d033511dee8f3189b17d4727d180f0e \ No newline at end of file +5124846c60d750bcb536bb4802a339dee73b9d28 \ No newline at end of file diff --git a/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationErrorsException.cs b/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationErrorsException.cs index 43031cd564..467e2f9b22 100644 --- a/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationErrorsException.cs +++ b/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationErrorsException.cs @@ -14,6 +14,9 @@ using System.Xml; namespace System.Configuration { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] +#endif public class ConfigurationErrorsException : ConfigurationException { // Constants @@ -104,7 +107,34 @@ namespace System.Configuration protected ConfigurationErrorsException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); + int firstLine; + int count; + + // Retrieve out members + string firstFilename = info.GetString(SerializationParamFilename); + firstLine = info.GetInt32(SerializationParamLine); + + Init(firstFilename, firstLine); + + // Retrieve errors for _errors object + count = info.GetInt32(SerializationParamErrorCount); + + if (count == 0) return; + _errors = new ConfigurationException[count]; + + for (int i = 0; i < count; i++) + { + string numPrefix = i.ToString(CultureInfo.InvariantCulture); + string currentType = info.GetString(numPrefix + SerializationParamErrorType); + Type currentExceptionType = Type.GetType(currentType, true); + + // Only allow our exception types + if ((currentExceptionType != typeof(ConfigurationException)) && + (currentExceptionType != typeof(ConfigurationErrorsException))) + throw ExceptionUtil.UnexpectedError("ConfigurationErrorsException"); + + _errors[i] = (ConfigurationException)info.GetValue(numPrefix + SerializationParamErrorData, currentExceptionType); + } } // The message includes the file/line number information. @@ -168,6 +198,28 @@ namespace System.Configuration public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + + // Serialize our members + info.AddValue(SerializationParamFilename, Filename); + info.AddValue(SerializationParamLine, Line); + + // Serialize rest of errors, along with count + // (since first error duplicates this error, only worry if + // there is more than one) + int subErrors = 0; + if ((_errors != null) && (_errors.Length > 1)) + { + subErrors = _errors.Length; + + for (int i = 0; i < _errors.Length; i++) + { + string numPrefix = i.ToString(CultureInfo.InvariantCulture); + info.AddValue(numPrefix + SerializationParamErrorData, _errors[i]); + info.AddValue(numPrefix + SerializationParamErrorType, _errors[i].GetType()); + } + } + + info.AddValue(SerializationParamErrorCount, subErrors); } // Get file and linenumber from an XML Node in a DOM diff --git a/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationException.cs b/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationException.cs index 69e63b71d3..dd9725a993 100644 --- a/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationException.cs +++ b/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationException.cs @@ -17,6 +17,9 @@ namespace System.Configuration /// number information where possible. /// [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class ConfigurationException : SystemException { private string _filename; @@ -26,7 +29,7 @@ namespace System.Configuration protected ConfigurationException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); + Init(info.GetString("filename"), info.GetInt32("line")); } [Obsolete("This class is obsolete, to create a new exception create a System.Configuration!System.Configuration.ConfigurationErrorsException")] @@ -93,6 +96,8 @@ namespace System.Configuration public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + info.AddValue("filename", _filename); + info.AddValue("line", _line); } [Obsolete("This class is obsolete, use System.Configuration!System.Configuration.ConfigurationErrorsException.GetFilename instead")] diff --git a/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/GenericEnumConverter.cs b/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/GenericEnumConverter.cs index 1d5f1fb08d..5319cbbe8d 100644 --- a/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/GenericEnumConverter.cs +++ b/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/GenericEnumConverter.cs @@ -14,7 +14,8 @@ namespace System.Configuration public GenericEnumConverter(Type typeEnum) { - if (typeEnum == null) throw new ArgumentNullException(nameof(typeEnum)); + if (typeEnum == null) + throw new ArgumentNullException(nameof(typeEnum)); _enumType = typeEnum; } @@ -26,41 +27,39 @@ namespace System.Configuration public override object ConvertFrom(ITypeDescriptorContext ctx, CultureInfo ci, object data) { - object result; - // For any error, throw the ArgumentException with SR.Invalid_enum_value - try + if ((data is string value) && (value.Length > 0)) { - string value = (string)data; - if (string.IsNullOrEmpty(value)) throw new Exception(); - - // Disallow numeric values for enums. - if (!string.IsNullOrEmpty(value) && - (char.IsDigit(value[0]) || - (value[0] == '-') || - (value[0] == '+'))) - throw new Exception(); - - if (value != value.Trim()) + // Disallow numeric values and whitespace at start and end. + if ((!char.IsDigit(value[0])) && (value[0] != '-') && (value[0] != '+') && + (!char.IsWhiteSpace(value[0])) && (!char.IsWhiteSpace(value[value.Length - 1]))) { - // throw if the value has whitespace - throw new Exception(); + try + { + return Enum.Parse(_enumType, value); + } + catch + { + // Exception from parse. Will throw more appropriate exception below. + } } - - result = Enum.Parse(_enumType, value); } - catch + throw CreateExceptionForInvalidValue(); + } + + private ArgumentException CreateExceptionForInvalidValue() + { + StringBuilder names = new StringBuilder(); + + foreach (string name in Enum.GetNames(_enumType)) { - StringBuilder names = new StringBuilder(); - - foreach (string name in Enum.GetNames(_enumType)) + if (names.Length != 0) { - if (names.Length != 0) names.Append(", "); - names.Append(name); + names.Append(", "); } - throw new ArgumentException(string.Format(SR.Invalid_enum_value, names.ToString())); + names.Append(name); } - return result; + return new ArgumentException(string.Format(SR.Invalid_enum_value, names.ToString())); } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/IConfigurationSystem.cs b/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/IConfigurationSystem.cs index 44542dd9c6..1ba3f1a4da 100644 --- a/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/IConfigurationSystem.cs +++ b/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/IConfigurationSystem.cs @@ -7,7 +7,6 @@ using System.Runtime.InteropServices; namespace System.Configuration { // obsolete - [ComVisible(false)] public interface IConfigurationSystem { // Returns the config object for the specified key. diff --git a/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/Internal/IInternalConfigHostPaths.cs b/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/Internal/IInternalConfigHostPaths.cs index 328a75de2c..0bec91956a 100644 --- a/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/Internal/IInternalConfigHostPaths.cs +++ b/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/Internal/IInternalConfigHostPaths.cs @@ -6,7 +6,6 @@ using System.Runtime.InteropServices; namespace System.Configuration.Internal { - [ComVisible(false)] public interface IInternalConfigHostPaths { void RefreshConfigPaths(); diff --git a/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/ProtectedConfigurationSection.cs b/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/ProtectedConfigurationSection.cs index ff32415d83..2d86713558 100644 --- a/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/ProtectedConfigurationSection.cs +++ b/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/ProtectedConfigurationSection.cs @@ -34,7 +34,7 @@ namespace System.Configuration s_properties = new ConfigurationPropertyCollection { s_propProviders, s_propDefaultProvider }; } - public ProtectedConfigurationSection(){} + public ProtectedConfigurationSection(){} protected internal override ConfigurationPropertyCollection Properties => s_properties; @@ -55,7 +55,7 @@ namespace System.Configuration ProviderSettings ps = Providers[providerName]; if (ps == null) - throw new Exception(string.Format(SR.ProtectedConfigurationProvider_not_found, providerName)); + throw new ArgumentException(string.Format(SR.ProtectedConfigurationProvider_not_found, providerName), nameof(providerName)); return InstantiateProvider(ps); } @@ -85,7 +85,7 @@ namespace System.Configuration { Type t = TypeUtil.GetType(pn.Type, true); if (!typeof(ProtectedConfigurationProvider).IsAssignableFrom(t)) - throw new Exception(SR.WrongType_of_Protected_provider); + throw new ArgumentException(SR.WrongType_of_Protected_provider, nameof(pn)); return CreateAndInitializeProviderWithAssert(t, pn); } @@ -116,4 +116,4 @@ namespace System.Configuration return encNode.OuterXml; } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/Provider/ProviderException.cs b/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/Provider/ProviderException.cs index 337026a0ec..8843115d18 100644 --- a/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/Provider/ProviderException.cs +++ b/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/Provider/ProviderException.cs @@ -7,6 +7,9 @@ using System.Runtime.Serialization; namespace System.Configuration.Provider { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] +#endif public class ProviderException : Exception { public ProviderException() { } @@ -22,7 +25,6 @@ namespace System.Configuration.Provider protected ProviderException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } \ No newline at end of file diff --git a/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/SettingsPropertyIsReadOnlyException.cs b/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/SettingsPropertyIsReadOnlyException.cs index 5535b7c2f3..d34df745ea 100644 --- a/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/SettingsPropertyIsReadOnlyException.cs +++ b/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/SettingsPropertyIsReadOnlyException.cs @@ -7,6 +7,9 @@ using System.Runtime.Serialization; namespace System.Configuration { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class SettingsPropertyIsReadOnlyException : Exception { public SettingsPropertyIsReadOnlyException(String message) @@ -22,10 +25,10 @@ namespace System.Configuration protected SettingsPropertyIsReadOnlyException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public SettingsPropertyIsReadOnlyException() - { } + { + } } } diff --git a/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/SettingsPropertyNotFoundException.cs b/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/SettingsPropertyNotFoundException.cs index 94072127e5..8b95024fb5 100644 --- a/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/SettingsPropertyNotFoundException.cs +++ b/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/SettingsPropertyNotFoundException.cs @@ -7,6 +7,9 @@ using System.Runtime.Serialization; namespace System.Configuration { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class SettingsPropertyNotFoundException : Exception { public SettingsPropertyNotFoundException(String message) @@ -22,11 +25,11 @@ namespace System.Configuration protected SettingsPropertyNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public SettingsPropertyNotFoundException() - { } + { + } } } diff --git a/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/SettingsPropertyWrongTypeException.cs b/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/SettingsPropertyWrongTypeException.cs index 47a343c0da..7bedfeb002 100644 --- a/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/SettingsPropertyWrongTypeException.cs +++ b/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/SettingsPropertyWrongTypeException.cs @@ -7,6 +7,9 @@ using System.Runtime.Serialization; namespace System.Configuration { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class SettingsPropertyWrongTypeException : Exception { public SettingsPropertyWrongTypeException(String message) @@ -22,10 +25,10 @@ namespace System.Configuration protected SettingsPropertyWrongTypeException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public SettingsPropertyWrongTypeException() - { } + { + } } } diff --git a/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/SpecialSettingAttribute.cs b/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/SpecialSettingAttribute.cs index fd81bf389f..97f3bb84e2 100644 --- a/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/SpecialSettingAttribute.cs +++ b/external/corefx/src/System.Configuration.ConfigurationManager/src/System/Configuration/SpecialSettingAttribute.cs @@ -1,8 +1,9 @@ -using System; -// Licensed to the .NET Foundation under one or more agreements. +// 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; + namespace System.Configuration { /// diff --git a/external/corefx/src/System.Configuration.ConfigurationManager/tests/Mono/GenericEnumConverterTest.cs b/external/corefx/src/System.Configuration.ConfigurationManager/tests/Mono/GenericEnumConverterTest.cs index 0f7648d81f..f17bd1697b 100644 --- a/external/corefx/src/System.Configuration.ConfigurationManager/tests/Mono/GenericEnumConverterTest.cs +++ b/external/corefx/src/System.Configuration.ConfigurationManager/tests/Mono/GenericEnumConverterTest.cs @@ -16,10 +16,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -101,6 +101,56 @@ namespace MonoTests.System.Configuration Assert.Null(o); } + [Fact] + public void ConvertFrom_InvalidString_WhiteSpaceAtTheBeginning() + { + GenericEnumConverter cv = new GenericEnumConverter(typeof(FooEnum)); + object o = null; + + AssertExtensions.Throws(null, () => o = cv.ConvertFrom(null, null, " Foo")); + Assert.Null(o); + } + + [Fact] + public void ConvertFrom_InvalidString_WhiteSpaceAtTheEnd() + { + GenericEnumConverter cv = new GenericEnumConverter(typeof(FooEnum)); + object o = null; + + AssertExtensions.Throws(null, () => o = cv.ConvertFrom(null, null, "Foo ")); + Assert.Null(o); + } + + [Fact] + public void ConvertFrom_InvalidString_DigitAtTheBeginning() + { + GenericEnumConverter cv = new GenericEnumConverter(typeof(FooEnum)); + object o = null; + + AssertExtensions.Throws(null, () => o = cv.ConvertFrom(null, null, "1Foo")); + Assert.Null(o); + } + + [Fact] + public void ConvertFrom_InvalidString_PlusSignAtTheBeginning() + { + GenericEnumConverter cv = new GenericEnumConverter(typeof(FooEnum)); + object o = null; + + AssertExtensions.Throws(null, () => o = cv.ConvertFrom(null, null, "+Foo")); + Assert.Null(o); + } + + [Fact] + public void ConvertFrom_InvalidString_MinusSignAtTheBeginning() + { + GenericEnumConverter cv = new GenericEnumConverter(typeof(FooEnum)); + object o = null; + + AssertExtensions.Throws(null, () => o = cv.ConvertFrom(null, null, "-Foo")); + Assert.Null(o); + } + [Fact] public void ConvertFrom_Null() { @@ -111,6 +161,16 @@ namespace MonoTests.System.Configuration Assert.Null(o); } + [Fact] + public void ConvertFrom_EmptyString() + { + GenericEnumConverter cv = new GenericEnumConverter(typeof(FooEnum)); + object o = null; + + AssertExtensions.Throws(null, () => o = cv.ConvertFrom(null, null, string.Empty)); + Assert.Null(o); + } + [Fact] public void ConvertTo() { diff --git a/external/corefx/src/System.Console/System.Console.sln b/external/corefx/src/System.Console/System.Console.sln index eb56e13dea..179bfe7819 100644 --- a/external/corefx/src/System.Console/System.Console.sln +++ b/external/corefx/src/System.Console/System.Console.sln @@ -44,10 +44,10 @@ Global {99E5069D-241F-48A6-8F29-404B4AED72BF}.Debug|Any CPU.Build.0 = Debug|Any CPU {99E5069D-241F-48A6-8F29-404B4AED72BF}.Release|Any CPU.ActiveCfg = Release|Any CPU {99E5069D-241F-48A6-8F29-404B4AED72BF}.Release|Any CPU.Build.0 = Release|Any CPU - {14BE0BA2-28BC-467A-AA76-C6B86D21FDAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {14BE0BA2-28BC-467A-AA76-C6B86D21FDAE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {14BE0BA2-28BC-467A-AA76-C6B86D21FDAE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {14BE0BA2-28BC-467A-AA76-C6B86D21FDAE}.Release|Any CPU.Build.0 = Release|Any CPU + {14BE0BA2-28BC-467A-AA76-C6B86D21FDAE}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {14BE0BA2-28BC-467A-AA76-C6B86D21FDAE}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {14BE0BA2-28BC-467A-AA76-C6B86D21FDAE}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {14BE0BA2-28BC-467A-AA76-C6B86D21FDAE}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU {F9DF2357-81B4-4317-908E-512DA9395583}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU {F9DF2357-81B4-4317-908E-512DA9395583}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU {F9DF2357-81B4-4317-908E-512DA9395583}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU diff --git a/external/corefx/src/System.Console/ref/System.Console.cs b/external/corefx/src/System.Console/ref/System.Console.cs index 21432d7026..079b96f1fd 100644 --- a/external/corefx/src/System.Console/ref/System.Console.cs +++ b/external/corefx/src/System.Console/ref/System.Console.cs @@ -5,47 +5,52 @@ // Changes to this file must follow the http://aka.ms/api-review process. // ------------------------------------------------------------------------------ - namespace System { public static partial class Console { public static System.ConsoleColor BackgroundColor { get { throw null; } set { } } - public static void Beep() { } - public static void Beep(int frequency, int duration) { } public static int BufferHeight { get { throw null; } set { } } public static int BufferWidth { get { throw null; } set { } } public static bool CapsLock { get { throw null; } } - public static event System.ConsoleCancelEventHandler CancelKeyPress { add { } remove { } } - public static void Clear() { } public static int CursorLeft { get { throw null; } set { } } public static int CursorSize { get { throw null; } set { } } public static int CursorTop { get { throw null; } set { } } public static bool CursorVisible { get { throw null; } set { } } public static System.IO.TextWriter Error { get { throw null; } } public static System.ConsoleColor ForegroundColor { get { throw null; } set { } } + public static System.IO.TextReader In { get { throw null; } } public static System.Text.Encoding InputEncoding { get { throw null; } set { } } public static bool IsErrorRedirected { get { throw null; } } public static bool IsInputRedirected { get { throw null; } } public static bool IsOutputRedirected { get { throw null; } } - public static System.IO.TextReader In { get { throw null; } } - public static bool KeyAvailable { get { throw null; }} + public static bool KeyAvailable { get { throw null; } } + public static int LargestWindowHeight { get { throw null; } } public static int LargestWindowWidth { get { throw null; } } - public static int LargestWindowHeight { get { throw null; }} + public static bool NumberLock { get { throw null; } } + public static System.IO.TextWriter Out { get { throw null; } } + public static System.Text.Encoding OutputEncoding { get { throw null; } set { } } + public static string Title { get { throw null; } set { } } + public static bool TreatControlCAsInput { get { throw null; } set { } } + public static int WindowHeight { get { throw null; } set { } } + public static int WindowLeft { get { throw null; } set { } } + public static int WindowTop { get { throw null; } set { } } + public static int WindowWidth { get { throw null; } set { } } + public static event System.ConsoleCancelEventHandler CancelKeyPress { add { } remove { } } + public static void Beep() { } + public static void Beep(int frequency, int duration) { } + public static void Clear() { } public static void MoveBufferArea(int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight, int targetLeft, int targetTop) { } - public static void MoveBufferArea(int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight, int targetLeft, int targetTop, char sourceChar, ConsoleColor sourceForeColor, ConsoleColor sourceBackColor) { } - public static bool NumberLock { get { throw null; }} + public static void MoveBufferArea(int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight, int targetLeft, int targetTop, char sourceChar, System.ConsoleColor sourceForeColor, System.ConsoleColor sourceBackColor) { } public static System.IO.Stream OpenStandardError() { throw null; } public static System.IO.Stream OpenStandardError(int bufferSize) { throw null; } public static System.IO.Stream OpenStandardInput() { throw null; } public static System.IO.Stream OpenStandardInput(int bufferSize) { throw null; } public static System.IO.Stream OpenStandardOutput() { throw null; } public static System.IO.Stream OpenStandardOutput(int bufferSize) { throw null; } - public static System.IO.TextWriter Out { get { throw null; } } - public static System.Text.Encoding OutputEncoding { get { throw null; } set { } } public static int Read() { throw null; } - public static ConsoleKeyInfo ReadKey() { throw null; } - public static ConsoleKeyInfo ReadKey(bool intercept) { throw null; } + public static System.ConsoleKeyInfo ReadKey() { throw null; } + public static System.ConsoleKeyInfo ReadKey(bool intercept) { throw null; } public static string ReadLine() { throw null; } public static void ResetColor() { } public static void SetBufferSize(int width, int height) { } @@ -55,12 +60,6 @@ namespace System public static void SetOut(System.IO.TextWriter newOut) { } public static void SetWindowPosition(int left, int top) { } public static void SetWindowSize(int width, int height) { } - public static string Title { get { throw null; } set { } } - public static bool TreatControlCAsInput { get { throw null; } set { } } - public static int WindowHeight { get { throw null; } set { } } - public static int WindowWidth { get { throw null; } set { } } - public static int WindowLeft { get { throw null; } set { } } - public static int WindowTop { get { throw null; } set { } } public static void Write(bool value) { } public static void Write(char value) { } public static void Write(char[] buffer) { } @@ -127,171 +126,172 @@ namespace System White = 15, Yellow = 14, } - public partial struct ConsoleKeyInfo - { - public ConsoleKeyInfo(char keyChar, ConsoleKey key, bool shift, bool alt, bool control) { } - public char KeyChar { get { throw null; } } - public ConsoleKey Key { get { throw null; } } - public ConsoleModifiers Modifiers { get { throw null; } } - public bool Equals(ConsoleKeyInfo obj) { throw null; } - public override bool Equals(object value) { throw null; } - public override int GetHashCode() { throw null; } - public static bool operator ==(ConsoleKeyInfo a, ConsoleKeyInfo b) { throw null; } - public static bool operator !=(ConsoleKeyInfo a, ConsoleKeyInfo b) { throw null; } - } public enum ConsoleKey { - Backspace = 0x8, - Tab = 0x9, - Clear = 0xC, - Enter = 0xD, - Pause = 0x13, - Escape = 0x1B, - Spacebar = 0x20, - PageUp = 0x21, - PageDown = 0x22, - End = 0x23, - Home = 0x24, - LeftArrow = 0x25, - UpArrow = 0x26, - RightArrow = 0x27, - DownArrow = 0x28, - Select = 0x29, - Print = 0x2A, - Execute = 0x2B, - PrintScreen = 0x2C, - Insert = 0x2D, - Delete = 0x2E, - Help = 0x2F, - D0 = 0x30, // 0 through 9 - D1 = 0x31, - D2 = 0x32, - D3 = 0x33, - D4 = 0x34, - D5 = 0x35, - D6 = 0x36, - D7 = 0x37, - D8 = 0x38, - D9 = 0x39, - A = 0x41, - B = 0x42, - C = 0x43, - D = 0x44, - E = 0x45, - F = 0x46, - G = 0x47, - H = 0x48, - I = 0x49, - J = 0x4A, - K = 0x4B, - L = 0x4C, - M = 0x4D, - N = 0x4E, - O = 0x4F, - P = 0x50, - Q = 0x51, - R = 0x52, - S = 0x53, - T = 0x54, - U = 0x55, - V = 0x56, - W = 0x57, - X = 0x58, - Y = 0x59, - Z = 0x5A, - LeftWindows = 0x5B, - RightWindows = 0x5C, - Applications = 0x5D, - Sleep = 0x5F, - NumPad0 = 0x60, - NumPad1 = 0x61, - NumPad2 = 0x62, - NumPad3 = 0x63, - NumPad4 = 0x64, - NumPad5 = 0x65, - NumPad6 = 0x66, - NumPad7 = 0x67, - NumPad8 = 0x68, - NumPad9 = 0x69, - Multiply = 0x6A, - Add = 0x6B, - Separator = 0x6C, - Subtract = 0x6D, - Decimal = 0x6E, - Divide = 0x6F, - F1 = 0x70, - F2 = 0x71, - F3 = 0x72, - F4 = 0x73, - F5 = 0x74, - F6 = 0x75, - F7 = 0x76, - F8 = 0x77, - F9 = 0x78, - F10 = 0x79, - F11 = 0x7A, - F12 = 0x7B, - F13 = 0x7C, - F14 = 0x7D, - F15 = 0x7E, - F16 = 0x7F, - F17 = 0x80, - F18 = 0x81, - F19 = 0x82, - F20 = 0x83, - F21 = 0x84, - F22 = 0x85, - F23 = 0x86, - F24 = 0x87, - BrowserBack = 0xA6, - BrowserForward = 0xA7, - BrowserRefresh = 0xA8, - BrowserStop = 0xA9, - BrowserSearch = 0xAA, - BrowserFavorites = 0xAB, - BrowserHome = 0xAC, - VolumeMute = 0xAD, - VolumeDown = 0xAE, - VolumeUp = 0xAF, - MediaNext = 0xB0, - MediaPrevious = 0xB1, - MediaStop = 0xB2, - MediaPlay = 0xB3, - LaunchMail = 0xB4, - LaunchMediaSelect = 0xB5, - LaunchApp1 = 0xB6, - LaunchApp2 = 0xB7, - Oem1 = 0xBA, - OemPlus = 0xBB, - OemComma = 0xBC, - OemMinus = 0xBD, - OemPeriod = 0xBE, - Oem2 = 0xBF, - Oem3 = 0xC0, - Oem4 = 0xDB, - Oem5 = 0xDC, - Oem6 = 0xDD, - Oem7 = 0xDE, - Oem8 = 0xDF, - Oem102 = 0xE2, - Process = 0xE5, - Packet = 0xE7, - Attention = 0xF6, - CrSel = 0xF7, - ExSel = 0xF8, - EraseEndOfFile = 0xF9, - Play = 0xFA, - Zoom = 0xFB, - NoName = 0xFC, - Pa1 = 0xFD, - OemClear = 0xFE, + A = 65, + Add = 107, + Applications = 93, + Attention = 246, + B = 66, + Backspace = 8, + BrowserBack = 166, + BrowserFavorites = 171, + BrowserForward = 167, + BrowserHome = 172, + BrowserRefresh = 168, + BrowserSearch = 170, + BrowserStop = 169, + C = 67, + Clear = 12, + CrSel = 247, + D = 68, + D0 = 48, + D1 = 49, + D2 = 50, + D3 = 51, + D4 = 52, + D5 = 53, + D6 = 54, + D7 = 55, + D8 = 56, + D9 = 57, + Decimal = 110, + Delete = 46, + Divide = 111, + DownArrow = 40, + E = 69, + End = 35, + Enter = 13, + EraseEndOfFile = 249, + Escape = 27, + Execute = 43, + ExSel = 248, + F = 70, + F1 = 112, + F10 = 121, + F11 = 122, + F12 = 123, + F13 = 124, + F14 = 125, + F15 = 126, + F16 = 127, + F17 = 128, + F18 = 129, + F19 = 130, + F2 = 113, + F20 = 131, + F21 = 132, + F22 = 133, + F23 = 134, + F24 = 135, + F3 = 114, + F4 = 115, + F5 = 116, + F6 = 117, + F7 = 118, + F8 = 119, + F9 = 120, + G = 71, + H = 72, + Help = 47, + Home = 36, + I = 73, + Insert = 45, + J = 74, + K = 75, + L = 76, + LaunchApp1 = 182, + LaunchApp2 = 183, + LaunchMail = 180, + LaunchMediaSelect = 181, + LeftArrow = 37, + LeftWindows = 91, + M = 77, + MediaNext = 176, + MediaPlay = 179, + MediaPrevious = 177, + MediaStop = 178, + Multiply = 106, + N = 78, + NoName = 252, + NumPad0 = 96, + NumPad1 = 97, + NumPad2 = 98, + NumPad3 = 99, + NumPad4 = 100, + NumPad5 = 101, + NumPad6 = 102, + NumPad7 = 103, + NumPad8 = 104, + NumPad9 = 105, + O = 79, + Oem1 = 186, + Oem102 = 226, + Oem2 = 191, + Oem3 = 192, + Oem4 = 219, + Oem5 = 220, + Oem6 = 221, + Oem7 = 222, + Oem8 = 223, + OemClear = 254, + OemComma = 188, + OemMinus = 189, + OemPeriod = 190, + OemPlus = 187, + P = 80, + Pa1 = 253, + Packet = 231, + PageDown = 34, + PageUp = 33, + Pause = 19, + Play = 250, + Print = 42, + PrintScreen = 44, + Process = 229, + Q = 81, + R = 82, + RightArrow = 39, + RightWindows = 92, + S = 83, + Select = 41, + Separator = 108, + Sleep = 95, + Spacebar = 32, + Subtract = 109, + T = 84, + Tab = 9, + U = 85, + UpArrow = 38, + V = 86, + VolumeDown = 174, + VolumeMute = 173, + VolumeUp = 175, + W = 87, + X = 88, + Y = 89, + Z = 90, + Zoom = 251, } - [Flags] + public readonly partial struct ConsoleKeyInfo + { + private readonly int _dummy; + public ConsoleKeyInfo(char keyChar, System.ConsoleKey key, bool shift, bool alt, bool control) { throw null; } + public System.ConsoleKey Key { get { throw null; } } + public char KeyChar { get { throw null; } } + public System.ConsoleModifiers Modifiers { get { throw null; } } + public bool Equals(System.ConsoleKeyInfo obj) { throw null; } + public override bool Equals(object value) { throw null; } + public override int GetHashCode() { throw null; } + public static bool operator ==(System.ConsoleKeyInfo a, System.ConsoleKeyInfo b) { throw null; } + public static bool operator !=(System.ConsoleKeyInfo a, System.ConsoleKeyInfo b) { throw null; } + } + [System.FlagsAttribute] public enum ConsoleModifiers { Alt = 1, + Control = 4, Shift = 2, - Control = 4 } public enum ConsoleSpecialKey { diff --git a/external/corefx/src/System.Console/src/Resources/Strings.resx b/external/corefx/src/System.Console/src/Resources/Strings.resx index bcc68a27f7..a094a27e1a 100644 --- a/external/corefx/src/System.Console/src/Resources/Strings.resx +++ b/external/corefx/src/System.Console/src/Resources/Strings.resx @@ -1,5 +1,64 @@  + @@ -196,4 +255,7 @@ String contains invalid Unicode code points. - + + The path '{0}' is too long, or a component of the specified path is too long. + + \ No newline at end of file diff --git a/external/corefx/src/System.Console/src/System.Console.csproj b/external/corefx/src/System.Console/src/System.Console.csproj index 7ab6906fba..27638b70f8 100644 --- a/external/corefx/src/System.Console/src/System.Console.csproj +++ b/external/corefx/src/System.Console/src/System.Console.csproj @@ -273,7 +273,6 @@ - diff --git a/external/corefx/src/System.Console/src/System/ConsoleKeyInfo.cs b/external/corefx/src/System.Console/src/System/ConsoleKeyInfo.cs index 87bd61fa4f..8a1db80b6d 100644 --- a/external/corefx/src/System.Console/src/System/ConsoleKeyInfo.cs +++ b/external/corefx/src/System.Console/src/System/ConsoleKeyInfo.cs @@ -4,8 +4,7 @@ namespace System { - [Serializable] - public struct ConsoleKeyInfo + public readonly struct ConsoleKeyInfo { private readonly char _keyChar; private readonly ConsoleKey _key; diff --git a/external/corefx/src/System.Console/src/System/ConsolePal.Windows.cs b/external/corefx/src/System.Console/src/System/ConsolePal.Windows.cs index 605898b966..2f07290e00 100644 --- a/external/corefx/src/System.Console/src/System/ConsolePal.Windows.cs +++ b/external/corefx/src/System.Console/src/System/ConsolePal.Windows.cs @@ -4,7 +4,6 @@ using System.IO; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.InteropServices; using System.Text; using System.Threading; @@ -550,7 +549,6 @@ namespace System // Value should be a percentage from [1, 100]. if (value < 1 || value > 100) throw new ArgumentOutOfRangeException(nameof(value), value, SR.ArgumentOutOfRange_CursorSize); - Contract.EndContractBlock(); Interop.Kernel32.CONSOLE_CURSOR_INFO cci; if (!Interop.Kernel32.GetConsoleCursorInfo(OutputHandle, out cci)) @@ -652,7 +650,6 @@ namespace System if (duration <= 0) throw new ArgumentOutOfRangeException(nameof(duration), duration, SR.ArgumentOutOfRange_NeedPosNum); - Contract.EndContractBlock(); Interop.Kernel32.Beep(frequency, duration); } @@ -665,7 +662,6 @@ namespace System throw new ArgumentException(SR.Arg_InvalidConsoleColor, nameof(sourceForeColor)); if (sourceBackColor < ConsoleColor.Black || sourceBackColor > ConsoleColor.White) throw new ArgumentException(SR.Arg_InvalidConsoleColor, nameof(sourceBackColor)); - Contract.EndContractBlock(); Interop.Kernel32.CONSOLE_SCREEN_BUFFER_INFO csbi = GetBufferInfo(); Interop.Kernel32.COORD bufferSize = csbi.dwSize; @@ -1015,7 +1011,6 @@ namespace System { if ((((int)color) & ~0xf) != 0) throw new ArgumentException(SR.Arg_InvalidConsoleColor); - Contract.EndContractBlock(); Interop.Kernel32.Color c = (Interop.Kernel32.Color)color; @@ -1153,7 +1148,6 @@ namespace System // to this stream simultaneously. if (bytes.Length - offset < count) throw new IndexOutOfRangeException(SR.IndexOutOfRange_IORaceCondition); - Contract.EndContractBlock(); // You can't use the fixed statement on an array of length 0. if (bytes.Length == 0) diff --git a/external/corefx/src/System.Console/src/System/IO/ConsoleStream.cs b/external/corefx/src/System.Console/src/System/IO/ConsoleStream.cs index 134e2e1f2f..d2eda09de1 100644 --- a/external/corefx/src/System.Console/src/System/IO/ConsoleStream.cs +++ b/external/corefx/src/System.Console/src/System/IO/ConsoleStream.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.InteropServices; using System.Text; @@ -32,19 +31,16 @@ namespace System.IO public sealed override bool CanRead { - [Pure] get { return _canRead; } } public sealed override bool CanWrite { - [Pure] get { return _canWrite; } } public sealed override bool CanSeek { - [Pure] get { return false; } } @@ -82,7 +78,7 @@ namespace System.IO throw new ArgumentOutOfRangeException(offset < 0 ? nameof(offset) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); if (buffer.Length - offset < count) throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.EndContractBlock(); + if (!_canRead) throw Error.GetReadNotSupported(); } @@ -94,7 +90,7 @@ namespace System.IO throw new ArgumentOutOfRangeException(offset < 0 ? nameof(offset) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); if (buffer.Length - offset < count) throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.EndContractBlock(); + if (!_canWrite) throw Error.GetWriteNotSupported(); } } diff --git a/external/corefx/src/System.Console/src/System/IO/Error.cs b/external/corefx/src/System.Console/src/System/IO/Error.cs index 199e9792ce..c27eb501ab 100644 --- a/external/corefx/src/System.Console/src/System/IO/Error.cs +++ b/external/corefx/src/System.Console/src/System/IO/Error.cs @@ -2,11 +2,8 @@ // 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.Diagnostics.Contracts; - namespace System.IO { - [Pure] internal static class Error { internal static Exception GetFileNotOpen() diff --git a/external/corefx/src/System.Console/src/System/IO/SyncTextReader.cs b/external/corefx/src/System.Console/src/System/IO/SyncTextReader.cs index ec4af00cd9..7f4764a468 100644 --- a/external/corefx/src/System.Console/src/System/IO/SyncTextReader.cs +++ b/external/corefx/src/System.Console/src/System/IO/SyncTextReader.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.InteropServices; using System.Threading.Tasks; @@ -111,7 +110,6 @@ namespace System.IO throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); if (buffer.Length - index < count) throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.EndContractBlock(); return Task.FromResult(ReadBlock(buffer, index, count)); } @@ -124,7 +122,6 @@ namespace System.IO throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); if (buffer.Length - index < count) throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.EndContractBlock(); return Task.FromResult(Read(buffer, index, count)); } diff --git a/external/corefx/src/System.Console/src/System/TermInfo.cs b/external/corefx/src/System.Console/src/System/TermInfo.cs index a3d989a975..c84aaf9d03 100644 --- a/external/corefx/src/System.Console/src/System/TermInfo.cs +++ b/external/corefx/src/System.Console/src/System/TermInfo.cs @@ -897,7 +897,7 @@ namespace System /// It is a discriminated union of either an integer or a string, /// with characters represented as integers. /// - public struct FormatParam + public readonly struct FormatParam { /// The integer stored in the parameter. private readonly int _int32; diff --git a/external/corefx/src/System.Console/tests/ManualTests/System.Console.Manual.Tests.csproj b/external/corefx/src/System.Console/tests/ManualTests/System.Console.Manual.Tests.csproj index 587501873c..af596117d1 100644 --- a/external/corefx/src/System.Console/tests/ManualTests/System.Console.Manual.Tests.csproj +++ b/external/corefx/src/System.Console/tests/ManualTests/System.Console.Manual.Tests.csproj @@ -5,7 +5,6 @@ {99E5069D-241F-48A6-8F29-404B4AED72BF} true - diff --git a/external/corefx/src/System.Console/tests/Performance/System.Console.Performance.Tests.csproj b/external/corefx/src/System.Console/tests/Performance/System.Console.Performance.Tests.csproj index bc4156a0da..ffafbf70d6 100644 --- a/external/corefx/src/System.Console/tests/Performance/System.Console.Performance.Tests.csproj +++ b/external/corefx/src/System.Console/tests/Performance/System.Console.Performance.Tests.csproj @@ -6,9 +6,8 @@ true {14BE0BA2-28BC-467A-AA76-C6B86D21FDAE} - - - + + @@ -28,4 +27,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Console/tests/System.Console.Tests.csproj b/external/corefx/src/System.Console/tests/System.Console.Tests.csproj index ec59ce57e9..f78bd81fcf 100644 --- a/external/corefx/src/System.Console/tests/System.Console.Tests.csproj +++ b/external/corefx/src/System.Console/tests/System.Console.Tests.csproj @@ -5,7 +5,6 @@ {3ED7BCF1-34B9-49B7-9C25-0BC3304C0858} true - @@ -37,10 +36,10 @@ - + - + @@ -57,4 +56,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Console/tests/WindowAndCursorProps.cs b/external/corefx/src/System.Console/tests/WindowAndCursorProps.cs index 00e63a3ce2..8fe8b5bce6 100644 --- a/external/corefx/src/System.Console/tests/WindowAndCursorProps.cs +++ b/external/corefx/src/System.Console/tests/WindowAndCursorProps.cs @@ -155,7 +155,7 @@ public class WindowAndCursorProps : RemoteExecutorTestBase string newTitle = new string('a', int.Parse(lengthOfTitleString)); Console.Title = newTitle; - if (newTitle.Length > 513 && PlatformDetection.IsWindowsRedStone2) + if (newTitle.Length > 513 && PlatformDetection.IsWindows10Version1703OrGreater && !PlatformDetection.IsWindows10Version1709OrGreater) { // RS2 has a bug when getting the window title when the title length is longer than 513 character Assert.Throws(() => Console.Title); diff --git a/external/corefx/src/System.Data.Common/System.Data.Common.sln b/external/corefx/src/System.Data.Common/System.Data.Common.sln index 849ecb5f5f..81fabb728f 100644 --- a/external/corefx/src/System.Data.Common/System.Data.Common.sln +++ b/external/corefx/src/System.Data.Common/System.Data.Common.sln @@ -26,10 +26,10 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B473F77D-4168-4123-932A-E88020B768FA}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU - {B473F77D-4168-4123-932A-E88020B768FA}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU - {B473F77D-4168-4123-932A-E88020B768FA}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU - {B473F77D-4168-4123-932A-E88020B768FA}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {B473F77D-4168-4123-932A-E88020B768FA}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {B473F77D-4168-4123-932A-E88020B768FA}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {B473F77D-4168-4123-932A-E88020B768FA}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {B473F77D-4168-4123-932A-E88020B768FA}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU {29EF8D53-8E84-4E49-B90F-5950A2FE7D54}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU {29EF8D53-8E84-4E49-B90F-5950A2FE7D54}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {29EF8D53-8E84-4E49-B90F-5950A2FE7D54}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU diff --git a/external/corefx/src/System.Data.Common/ref/System.Data.Common.cs.REMOVED.git-id b/external/corefx/src/System.Data.Common/ref/System.Data.Common.cs.REMOVED.git-id index 9c5350e34d..fec9f79f26 100644 --- a/external/corefx/src/System.Data.Common/ref/System.Data.Common.cs.REMOVED.git-id +++ b/external/corefx/src/System.Data.Common/ref/System.Data.Common.cs.REMOVED.git-id @@ -1 +1 @@ -854d72c0bb4c8ba220c066b5fff5c9f02b8ddee9 \ No newline at end of file +64dd4c072a27ee8181a8b82d38ddca159b09ec9c \ No newline at end of file diff --git a/external/corefx/src/System.Data.Common/ref/System.Data.Common.csproj b/external/corefx/src/System.Data.Common/ref/System.Data.Common.csproj index f3525231a9..6dc2f123b3 100644 --- a/external/corefx/src/System.Data.Common/ref/System.Data.Common.csproj +++ b/external/corefx/src/System.Data.Common/ref/System.Data.Common.csproj @@ -12,6 +12,9 @@ + + + diff --git a/external/corefx/src/System.Data.Common/ref/System.Data.Common.netcoreapp.cs b/external/corefx/src/System.Data.Common/ref/System.Data.Common.netcoreapp.cs new file mode 100644 index 0000000000..94df2006ad --- /dev/null +++ b/external/corefx/src/System.Data.Common/ref/System.Data.Common.netcoreapp.cs @@ -0,0 +1,22 @@ +// 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. + +// ------------------------------------------------------------------------------ +// Changes to this file must follow the http://aka.ms/api-review process. +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; + +namespace System.Data.Common +{ + public static partial class DbProviderFactories + { + public static void RegisterFactory(string providerInvariantName, string factoryTypeAssemblyQualifiedName) { throw null; } + public static void RegisterFactory(string providerInvariantName, Type factoryType) { throw null; } + public static void RegisterFactory(string providerInvariantName, DbProviderFactory factory) { throw null; } + public static bool TryGetFactory(string providerInvariantName, out DbProviderFactory factory) { throw null; } + public static bool UnregisterFactory(string providerInvariantName) { throw null; } + public static IEnumerable GetProviderInvariantNames() { throw null; } + } +} diff --git a/external/corefx/src/System.Data.Common/src/Resources/Strings.resx b/external/corefx/src/System.Data.Common/src/Resources/Strings.resx index 8ac6107e7f..f13b9de2d8 100644 --- a/external/corefx/src/System.Data.Common/src/Resources/Strings.resx +++ b/external/corefx/src/System.Data.Common/src/Resources/Strings.resx @@ -512,4 +512,9 @@ Cannot remove this column, because it is part of an expression: {0} = {1}. The rowOrder value={0} has been found twice for table named '{1}'. Cannot find ElementType name='{0}'. + The specified invariant name '{0}' wasn't found in the list of registered .NET Data Providers. + The requested .NET Data Provider's implementation does not have an Instance field of a System.Data.Common.DbProviderFactory derived type. + The registered .NET Data Provider's DbProviderFactory implementation type '{0}' couldn't be loaded. + The missing .NET Data Provider's assembly qualified name is required. + The type '{0}' doesn't inherit from DbProviderFactory. diff --git a/external/corefx/src/System.Data.Common/src/System.Data.Common.csproj b/external/corefx/src/System.Data.Common/src/System.Data.Common.csproj index f510044d98..5e3d53dee5 100644 --- a/external/corefx/src/System.Data.Common/src/System.Data.Common.csproj +++ b/external/corefx/src/System.Data.Common/src/System.Data.Common.csproj @@ -57,7 +57,9 @@ - + + Component + @@ -78,10 +80,14 @@ - + + Component + - + + Component + @@ -91,9 +97,13 @@ - + + Component + - + + Component + @@ -135,7 +145,9 @@ - + + Component + @@ -160,7 +172,9 @@ - + + Component + @@ -170,9 +184,15 @@ - - - + + Component + + + Component + + + Component + System\Data\Common\DbConnectionOptions.Common.cs @@ -183,7 +203,9 @@ - + + Component + @@ -195,6 +217,7 @@ + diff --git a/external/corefx/src/System.Data.Common/src/System/Data/Common/DbConnection.cs b/external/corefx/src/System.Data.Common/src/System/Data/Common/DbConnection.cs index 14bc4280ac..9dba89aeff 100644 --- a/external/corefx/src/System.Data.Common/src/System/Data/Common/DbConnection.cs +++ b/external/corefx/src/System.Data.Common/src/System/Data/Common/DbConnection.cs @@ -37,6 +37,8 @@ namespace System.Data.Common /// protected virtual DbProviderFactory DbProviderFactory => null; + internal DbProviderFactory ProviderFactory => DbProviderFactory; + [Browsable(false)] public abstract string ServerVersion { get; } diff --git a/external/corefx/src/System.Data.Common/src/System/Data/Common/DbException.cs b/external/corefx/src/System.Data.Common/src/System/Data/Common/DbException.cs index 75560aec28..48b861447c 100644 --- a/external/corefx/src/System.Data.Common/src/System/Data/Common/DbException.cs +++ b/external/corefx/src/System.Data.Common/src/System/Data/Common/DbException.cs @@ -5,6 +5,9 @@ namespace System.Data.Common { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public abstract class DbException : System.Runtime.InteropServices.ExternalException { protected DbException() : base() { } @@ -17,7 +20,6 @@ namespace System.Data.Common protected DbException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corefx/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs b/external/corefx/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs new file mode 100644 index 0000000000..bc6c482faa --- /dev/null +++ b/external/corefx/src/System.Data.Common/src/System/Data/Common/DbProviderFactories.cs @@ -0,0 +1,200 @@ +// 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.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Threading; + +namespace System.Data.Common +{ + public static partial class DbProviderFactories + { + private struct ProviderRegistration + { + internal ProviderRegistration(string factoryTypeAssemblyQualifiedName, DbProviderFactory factoryInstance) + { + this.FactoryTypeAssemblyQualifiedName = factoryTypeAssemblyQualifiedName; + this.FactoryInstance = factoryInstance; + } + + internal string FactoryTypeAssemblyQualifiedName { get; } + /// + /// The cached instance of the type in . If null, this registation is seen as a deferred registration + /// and is checked the first time when this registration is requested through GetFactory(). + /// + internal DbProviderFactory FactoryInstance { get; } + } + + private static ConcurrentDictionary _registeredFactories = new ConcurrentDictionary(); + private const string AssemblyQualifiedNameColumnName = "AssemblyQualifiedName"; + private const string InvariantNameColumnName = "InvariantName"; + private const string NameColumnName = "Name"; + private const string DescriptionColumnName = "Description"; + private const string ProviderGroupColumnName = "DbProviderFactories"; + private const string InstanceFieldName = "Instance"; + + public static bool TryGetFactory(string providerInvariantName, out DbProviderFactory factory) + { + factory = GetFactory(providerInvariantName, throwOnError: false); + return factory != null; + } + + public static DbProviderFactory GetFactory(string providerInvariantName) + { + return GetFactory(providerInvariantName, throwOnError: true); + } + + public static DbProviderFactory GetFactory(DataRow providerRow) + { + ADP.CheckArgumentNull(providerRow, nameof(providerRow)); + + DataColumn assemblyQualifiedNameColumn = providerRow.Table.Columns[AssemblyQualifiedNameColumnName]; + if (null == assemblyQualifiedNameColumn) + { + throw ADP.Argument(SR.ADP_DbProviderFactories_NoAssemblyQualifiedName); + } + + string assemblyQualifiedName = providerRow[assemblyQualifiedNameColumn] as string; + if (string.IsNullOrWhiteSpace(assemblyQualifiedName)) + { + throw ADP.Argument(SR.ADP_DbProviderFactories_NoAssemblyQualifiedName); + } + + return GetFactoryInstance(GetProviderTypeFromTypeName(assemblyQualifiedName)); + } + + + public static DbProviderFactory GetFactory(DbConnection connection) + { + ADP.CheckArgumentNull(connection, nameof(connection)); + + return connection.ProviderFactory; + } + + public static DataTable GetFactoryClasses() + { + DataColumn nameColumn = new DataColumn(NameColumnName, typeof(string)) { ReadOnly = true }; + DataColumn descriptionColumn = new DataColumn(DescriptionColumnName, typeof(string)) { ReadOnly = true }; + DataColumn invariantNameColumn = new DataColumn(InvariantNameColumnName, typeof(string)) { ReadOnly = true }; + DataColumn assemblyQualifiedNameColumn = new DataColumn(AssemblyQualifiedNameColumnName, typeof(string)) { ReadOnly = true }; + + DataTable toReturn = new DataTable(ProviderGroupColumnName) { Locale = CultureInfo.InvariantCulture }; + toReturn.Columns.AddRange(new[] { nameColumn, descriptionColumn, invariantNameColumn, assemblyQualifiedNameColumn }); + toReturn.PrimaryKey = new[] { invariantNameColumn }; + foreach(var kvp in _registeredFactories) + { + DataRow newRow = toReturn.NewRow(); + newRow[InvariantNameColumnName] = kvp.Key; + newRow[AssemblyQualifiedNameColumnName] = kvp.Value.FactoryTypeAssemblyQualifiedName; + newRow[NameColumnName] = string.Empty; + newRow[DescriptionColumnName] = string.Empty; + toReturn.AddRow(newRow); + } + return toReturn; + } + + public static IEnumerable GetProviderInvariantNames() + { + return _registeredFactories.Keys.ToList(); + } + + public static void RegisterFactory(string providerInvariantName, string factoryTypeAssemblyQualifiedName) + { + ADP.CheckArgumentLength(providerInvariantName, nameof(providerInvariantName)); + ADP.CheckArgumentLength(factoryTypeAssemblyQualifiedName, nameof(factoryTypeAssemblyQualifiedName)); + + // this method performs a deferred registration: the type name specified is checked when the factory is requested for the first time. + _registeredFactories[providerInvariantName] = new ProviderRegistration(factoryTypeAssemblyQualifiedName, null); + } + + public static void RegisterFactory(string providerInvariantName, Type providerFactoryClass) + { + RegisterFactory(providerInvariantName, GetFactoryInstance(providerFactoryClass)); + } + + public static void RegisterFactory(string providerInvariantName, DbProviderFactory factory) + { + ADP.CheckArgumentLength(providerInvariantName, nameof(providerInvariantName)); + ADP.CheckArgumentNull(factory, nameof(factory)); + + _registeredFactories[providerInvariantName] = new ProviderRegistration(factory.GetType().AssemblyQualifiedName, factory); + } + + public static bool UnregisterFactory(string providerInvariantName) + { + return !string.IsNullOrWhiteSpace(providerInvariantName) && _registeredFactories.TryRemove(providerInvariantName, out _); + } + + private static DbProviderFactory GetFactory(string providerInvariantName, bool throwOnError) + { + if (throwOnError) + { + ADP.CheckArgumentLength(providerInvariantName, nameof(providerInvariantName)); + } + else + { + if (string.IsNullOrWhiteSpace(providerInvariantName)) + { + return null; + } + } + bool wasRegistered = _registeredFactories.TryGetValue(providerInvariantName, out ProviderRegistration registration); + if (!wasRegistered) + { + return throwOnError ? throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_InvariantNameNotFound, providerInvariantName)) : (DbProviderFactory)null; + } + DbProviderFactory toReturn = registration.FactoryInstance; + if (toReturn == null) + { + // Deferred registration, do checks now on the type specified and register instance in storage. + // Even in the case of throwOnError being false, this will throw when an exception occurs checking the registered type as the user has to be notified the + // registration is invalid, even though the registration is there. + toReturn = GetFactoryInstance(GetProviderTypeFromTypeName(registration.FactoryTypeAssemblyQualifiedName)); + RegisterFactory(providerInvariantName, toReturn); + } + return toReturn; + } + + private static DbProviderFactory GetFactoryInstance(Type providerFactoryClass) + { + ADP.CheckArgumentNull(providerFactoryClass, nameof(providerFactoryClass)); + if (!providerFactoryClass.IsSubclassOf(typeof(DbProviderFactory))) + { + throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_NotAFactoryType, providerFactoryClass.FullName)); + } + + FieldInfo providerInstance = providerFactoryClass.GetField(InstanceFieldName, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static); + if (null == providerInstance) + { + throw ADP.InvalidOperation(SR.ADP_DbProviderFactories_NoInstance); + } + if (!providerInstance.FieldType.IsSubclassOf(typeof(DbProviderFactory))) + { + throw ADP.InvalidOperation(SR.ADP_DbProviderFactories_NoInstance); + } + object factory = providerInstance.GetValue(null); + if (null == factory) + { + throw ADP.InvalidOperation(SR.ADP_DbProviderFactories_NoInstance); + } + return (DbProviderFactory)factory; + } + + + private static Type GetProviderTypeFromTypeName(string assemblyQualifiedName) + { + Type providerType = Type.GetType(assemblyQualifiedName); + if (null == providerType) + { + throw ADP.Argument(SR.Format(SR.ADP_DbProviderFactories_FactoryNotLoadable, assemblyQualifiedName)); + } + return providerType; + } + } +} diff --git a/external/corefx/src/System.Data.Common/src/System/Data/DBConcurrencyException.cs b/external/corefx/src/System.Data.Common/src/System/Data/DBConcurrencyException.cs index f250b21e3d..173233dd4e 100644 --- a/external/corefx/src/System.Data.Common/src/System/Data/DBConcurrencyException.cs +++ b/external/corefx/src/System.Data.Common/src/System/Data/DBConcurrencyException.cs @@ -7,6 +7,9 @@ using System.Runtime.Serialization; namespace System.Data { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public sealed class DBConcurrencyException : SystemException { private DataRow[] _dataRows; @@ -30,9 +33,13 @@ namespace System.Data _dataRows = dataRows; } - public override void GetObjectData(SerializationInfo si, StreamingContext context) + private DBConcurrencyException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) { - base.GetObjectData(si, context); + base.GetObjectData(info, context); } public DataRow Row diff --git a/external/corefx/src/System.Data.Common/src/System/Data/DataException.cs b/external/corefx/src/System.Data.Common/src/System/Data/DataException.cs index 95060a4823..e0279efeeb 100644 --- a/external/corefx/src/System.Data.Common/src/System/Data/DataException.cs +++ b/external/corefx/src/System.Data.Common/src/System/Data/DataException.cs @@ -10,11 +10,13 @@ using System.Runtime.Serialization; namespace System.Data { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class DataException : SystemException { protected DataException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public DataException() : base(SR.DataSet_DefaultDataException) @@ -31,11 +33,13 @@ namespace System.Data }; [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class ConstraintException : DataException { protected ConstraintException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public ConstraintException() : base(SR.DataSet_DefaultConstraintException) @@ -55,11 +59,13 @@ namespace System.Data } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class DeletedRowInaccessibleException : DataException { protected DeletedRowInaccessibleException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } /// @@ -85,11 +91,13 @@ namespace System.Data } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class DuplicateNameException : DataException { protected DuplicateNameException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public DuplicateNameException() : base(SR.DataSet_DefaultDuplicateNameException) @@ -109,11 +117,13 @@ namespace System.Data } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class InRowChangingEventException : DataException { protected InRowChangingEventException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public InRowChangingEventException() : base(SR.DataSet_DefaultInRowChangingEventException) @@ -133,11 +143,13 @@ namespace System.Data } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class InvalidConstraintException : DataException { protected InvalidConstraintException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public InvalidConstraintException() : base(SR.DataSet_DefaultInvalidConstraintException) @@ -157,11 +169,13 @@ namespace System.Data } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class MissingPrimaryKeyException : DataException { protected MissingPrimaryKeyException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public MissingPrimaryKeyException() : base(SR.DataSet_DefaultMissingPrimaryKeyException) @@ -179,13 +193,15 @@ namespace System.Data HResult = HResults.DataMissingPrimaryKey; } } - + [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class NoNullAllowedException : DataException { protected NoNullAllowedException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public NoNullAllowedException() : base(SR.DataSet_DefaultNoNullAllowedException) @@ -203,13 +219,15 @@ namespace System.Data HResult = HResults.DataNoNullAllowed; } } - + [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class ReadOnlyException : DataException { protected ReadOnlyException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public ReadOnlyException() : base(SR.DataSet_DefaultReadOnlyException) @@ -227,13 +245,15 @@ namespace System.Data HResult = HResults.DataReadOnly; } } - + [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class RowNotInTableException : DataException { protected RowNotInTableException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public RowNotInTableException() : base(SR.DataSet_DefaultRowNotInTableException) @@ -253,11 +273,13 @@ namespace System.Data } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class VersionNotFoundException : DataException { protected VersionNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public VersionNotFoundException() : base(SR.DataSet_DefaultVersionNotFoundException) diff --git a/external/corefx/src/System.Data.Common/src/System/Data/DataKey.cs b/external/corefx/src/System.Data.Common/src/System/Data/DataKey.cs index f7c4f58852..f8587f2024 100644 --- a/external/corefx/src/System.Data.Common/src/System/Data/DataKey.cs +++ b/external/corefx/src/System.Data.Common/src/System/Data/DataKey.cs @@ -6,7 +6,7 @@ using System.Diagnostics; namespace System.Data { - internal struct DataKey + internal readonly struct DataKey { private const int maxColumns = 32; diff --git a/external/corefx/src/System.Data.Common/src/System/Data/Filter/ExpressionParser.cs b/external/corefx/src/System.Data.Common/src/System/Data/Filter/ExpressionParser.cs index 3713eb01bd..e3fcfb3da5 100644 --- a/external/corefx/src/System.Data.Common/src/System/Data/Filter/ExpressionParser.cs +++ b/external/corefx/src/System.Data.Common/src/System/Data/Filter/ExpressionParser.cs @@ -47,7 +47,7 @@ namespace System.Data private const int Expr = 2; /* The previous operand was a complex expression */ - private struct ReservedWords + private readonly struct ReservedWords { internal readonly string _word; // the word internal readonly Tokens _token; diff --git a/external/corefx/src/System.Data.Common/src/System/Data/Filter/FilterException.cs b/external/corefx/src/System.Data.Common/src/System/Data/Filter/FilterException.cs index 9a22d427d3..673d91ba87 100644 --- a/external/corefx/src/System.Data.Common/src/System/Data/Filter/FilterException.cs +++ b/external/corefx/src/System.Data.Common/src/System/Data/Filter/FilterException.cs @@ -9,12 +9,14 @@ using System.Runtime.Serialization; namespace System.Data { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class InvalidExpressionException : DataException { protected InvalidExpressionException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public InvalidExpressionException() : base() { } @@ -24,12 +26,14 @@ namespace System.Data } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class EvaluateException : InvalidExpressionException { protected EvaluateException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public EvaluateException() : base() { } @@ -39,12 +43,14 @@ namespace System.Data } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class SyntaxErrorException : InvalidExpressionException { protected SyntaxErrorException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public SyntaxErrorException() : base() { } diff --git a/external/corefx/src/System.Data.Common/src/System/Data/RbTree.cs b/external/corefx/src/System.Data.Common/src/System/Data/RbTree.cs index eadded0727..d0c1507a50 100644 --- a/external/corefx/src/System.Data.Common/src/System/Data/RbTree.cs +++ b/external/corefx/src/System.Data.Common/src/System/Data/RbTree.cs @@ -1893,7 +1893,7 @@ namespace System.Data /// Represents the node in the tree and the satellite branch it took to get there. - private struct NodePath + private readonly struct NodePath { /// Represents the node in the tree internal readonly int _nodeID; diff --git a/external/corefx/src/System.Data.Common/src/System/Data/SQLTypes/SQLUtility.cs b/external/corefx/src/System.Data.Common/src/System/Data/SQLTypes/SQLUtility.cs index f74bdeb5a6..e2cd37691b 100644 --- a/external/corefx/src/System.Data.Common/src/System/Data/SQLTypes/SQLUtility.cs +++ b/external/corefx/src/System.Data.Common/src/System/Data/SQLTypes/SQLUtility.cs @@ -25,6 +25,9 @@ namespace System.Data.SqlTypes } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class SqlTypeException : SystemException { public SqlTypeException() : this(SR.SqlMisc_SqlTypeMessage, null) @@ -41,11 +44,8 @@ namespace System.Data.SqlTypes HResult = HResults.SqlType; } - // runtime will call even if private... - // protected SqlTypeException(SerializationInfo si, StreamingContext sc) : base(SqlTypeExceptionSerialization(si, sc), sc) { - throw new PlatformNotSupportedException(); } private static SerializationInfo SqlTypeExceptionSerialization(SerializationInfo si, StreamingContext sc) @@ -58,9 +58,12 @@ namespace System.Data.SqlTypes } return si; } - } // SqlTypeException + } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public sealed class SqlNullValueException : SqlTypeException { // Creates a new SqlNullValueException with its message string set to the common string. @@ -78,6 +81,10 @@ namespace System.Data.SqlTypes HResult = HResults.SqlNullValue; } + private SqlNullValueException(SerializationInfo si, StreamingContext sc) : base(SqlNullValueExceptionSerialization(si, sc), sc) + { + } + private static SerializationInfo SqlNullValueExceptionSerialization(SerializationInfo si, StreamingContext sc) { if ((null != si) && (1 == si.MemberCount)) @@ -88,9 +95,12 @@ namespace System.Data.SqlTypes } return si; } - } // NullValueException + } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public sealed class SqlTruncateException : SqlTypeException { // Creates a new SqlTruncateException with its message string set to the empty string. @@ -108,6 +118,10 @@ namespace System.Data.SqlTypes HResult = HResults.SqlTruncate; } + private SqlTruncateException(SerializationInfo si, StreamingContext sc) : base(SqlTruncateExceptionSerialization(si, sc), sc) + { + } + private static SerializationInfo SqlTruncateExceptionSerialization(SerializationInfo si, StreamingContext sc) { if ((null != si) && (1 == si.MemberCount)) @@ -118,9 +132,12 @@ namespace System.Data.SqlTypes } return si; } - } // SqlTruncateException + } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public sealed class SqlNotFilledException : SqlTypeException { // Creates a new SqlNotFilledException with its message string set to the common string. @@ -137,9 +154,16 @@ namespace System.Data.SqlTypes { HResult = HResults.SqlNullValue; } - } // SqlNotFilledException + + private SqlNotFilledException(SerializationInfo si, StreamingContext sc) : base(si, sc) + { + } + } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public sealed class SqlAlreadyFilledException : SqlTypeException { // Creates a new SqlNotFilledException with its message string set to the common string. @@ -156,5 +180,9 @@ namespace System.Data.SqlTypes { HResult = HResults.SqlNullValue; } - } // SqlNotFilledException + + private SqlAlreadyFilledException(SerializationInfo si, StreamingContext sc) : base(si, sc) + { + } + } } diff --git a/external/corefx/src/System.Data.Common/src/System/Data/Selection.cs b/external/corefx/src/System.Data.Common/src/System/Data/Selection.cs index 6d1bd1a6da..cae5a6c83a 100644 --- a/external/corefx/src/System.Data.Common/src/System/Data/Selection.cs +++ b/external/corefx/src/System.Data.Common/src/System/Data/Selection.cs @@ -9,7 +9,7 @@ using System.Threading; namespace System.Data { - internal struct IndexField + internal readonly struct IndexField { public readonly DataColumn Column; public readonly bool IsDescending; // false = Asc; true = Desc what is default value for this? diff --git a/external/corefx/src/System.Data.Common/src/System/Data/StrongTypingException.cs b/external/corefx/src/System.Data.Common/src/System/Data/StrongTypingException.cs index be2c317408..970c0735bb 100644 --- a/external/corefx/src/System.Data.Common/src/System/Data/StrongTypingException.cs +++ b/external/corefx/src/System.Data.Common/src/System/Data/StrongTypingException.cs @@ -7,15 +7,17 @@ using System.Runtime.Serialization; namespace System.Data { /// - /// DEV: The exception that is throwing from strong typed DataSet when user access to DBNull value. + /// The exception that is throwing from strong typed DataSet when user access to DBNull value. /// [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class StrongTypingException : DataException { protected StrongTypingException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public StrongTypingException() : base() diff --git a/external/corefx/src/System.Data.Common/src/System/Data/XMLSchema.cs.REMOVED.git-id b/external/corefx/src/System.Data.Common/src/System/Data/XMLSchema.cs.REMOVED.git-id index d3be6a070d..a0509e0023 100644 --- a/external/corefx/src/System.Data.Common/src/System/Data/XMLSchema.cs.REMOVED.git-id +++ b/external/corefx/src/System.Data.Common/src/System/Data/XMLSchema.cs.REMOVED.git-id @@ -1 +1 @@ -e8dc35c658433a250413209c3cddb8268abda9b1 \ No newline at end of file +b0a3c9adb0b7ea5542b463d564e8be08cd718146 \ No newline at end of file diff --git a/external/corefx/src/System.Data.Common/tests/Configurations.props b/external/corefx/src/System.Data.Common/tests/Configurations.props index c398e42e89..8b803e0772 100644 --- a/external/corefx/src/System.Data.Common/tests/Configurations.props +++ b/external/corefx/src/System.Data.Common/tests/Configurations.props @@ -2,6 +2,7 @@ + netcoreapp; netstandard; diff --git a/external/corefx/src/System.Data.Common/tests/System.Data.Common.Tests.csproj b/external/corefx/src/System.Data.Common/tests/System.Data.Common.Tests.csproj index ab5a69e6d0..14b5102724 100644 --- a/external/corefx/src/System.Data.Common/tests/System.Data.Common.Tests.csproj +++ b/external/corefx/src/System.Data.Common/tests/System.Data.Common.Tests.csproj @@ -4,7 +4,10 @@ {B473F77D-4168-4123-932A-E88020B768FA} 0168,0169,0414,0219,0649 + $(DefineConstants);netcoreapp + + @@ -74,7 +77,9 @@ - + + Component + @@ -109,6 +114,9 @@ System\Runtime\Serialization\Formatters\BinaryFormatterHelpers.cs + + + {69e46a6f-9966-45a5-8945-2559fe337827} diff --git a/external/corefx/src/System.Data.Common/tests/System/Data/Common/DataColumnMappingTest.cs b/external/corefx/src/System.Data.Common/tests/System/Data/Common/DataColumnMappingTest.cs index d3237debd6..4a4912334c 100644 --- a/external/corefx/src/System.Data.Common/tests/System/Data/Common/DataColumnMappingTest.cs +++ b/external/corefx/src/System.Data.Common/tests/System/Data/Common/DataColumnMappingTest.cs @@ -1,3 +1,7 @@ +// 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.Data.Common; using Xunit; diff --git a/external/corefx/src/System.Data.Common/tests/System/Data/Common/DbCommandTests.cs b/external/corefx/src/System.Data.Common/tests/System/Data/Common/DbCommandTests.cs index 5c59d01238..dc06d62ab8 100644 --- a/external/corefx/src/System.Data.Common/tests/System/Data/Common/DbCommandTests.cs +++ b/external/corefx/src/System.Data.Common/tests/System/Data/Common/DbCommandTests.cs @@ -154,6 +154,7 @@ namespace System.Data.Common.Tests } [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.Mono, "GC has different behavior on Mono")] public void CanBeFinalized() { FinalizingCommand.CreateAndRelease(); diff --git a/external/corefx/src/System.Data.Common/tests/System/Data/Common/DbConnectionTests.cs b/external/corefx/src/System.Data.Common/tests/System/Data/Common/DbConnectionTests.cs index e64901dc38..daac63de95 100644 --- a/external/corefx/src/System.Data.Common/tests/System/Data/Common/DbConnectionTests.cs +++ b/external/corefx/src/System.Data.Common/tests/System/Data/Common/DbConnectionTests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // See the LICENSE file in the project root for more information. +using System.Reflection; using Xunit; namespace System.Data.Common.Tests @@ -94,7 +95,49 @@ namespace System.Data.Common.Tests } } + private class DbProviderFactoryConnection : DbConnection + { + protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) + { + throw new NotImplementedException(); + } + + public override void ChangeDatabase(string databaseName) + { + throw new NotImplementedException(); + } + + public override void Close() + { + throw new NotImplementedException(); + } + + public override void Open() + { + throw new NotImplementedException(); + } + + public override string ConnectionString { get; set; } + public override string Database { get; } + public override ConnectionState State { get; } + public override string DataSource { get; } + public override string ServerVersion { get; } + + protected override DbCommand CreateDbCommand() + { + throw new NotImplementedException(); + } + + protected override DbProviderFactory DbProviderFactory => TestDbProviderFactory.Instance; + } + + private class TestDbProviderFactory : DbProviderFactory + { + public static DbProviderFactory Instance = new TestDbProviderFactory(); + } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.Mono, "GC has different behavior on Mono")] public void CanBeFinalized() { FinalizingConnection.CreateAndRelease(); @@ -102,5 +145,17 @@ namespace System.Data.Common.Tests GC.WaitForPendingFinalizers(); Assert.True(_wasFinalized); } + + [Fact] + public void ProviderFactoryTest() + { + DbProviderFactoryConnection con = new DbProviderFactoryConnection(); + PropertyInfo providerFactoryProperty = con.GetType().GetProperty("ProviderFactory", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(providerFactoryProperty); + DbProviderFactory factory = providerFactoryProperty.GetValue(con) as DbProviderFactory; + Assert.NotNull(factory); + Assert.Same(typeof(TestDbProviderFactory), factory.GetType()); + Assert.Same(TestDbProviderFactory.Instance, factory); + } } } diff --git a/external/corefx/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs b/external/corefx/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs new file mode 100644 index 0000000000..2045238238 --- /dev/null +++ b/external/corefx/src/System.Data.Common/tests/System/Data/Common/DbProviderFactoriesTests.netcoreapp.cs @@ -0,0 +1,207 @@ +// Licensed to the .NET Foundation under one or more agreements. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Reflection; +using System.Data.Common; +using System.Linq; +using Xunit; + +namespace System.Data.Common +{ + public sealed class TestProviderFactory : DbProviderFactory + { + public static readonly TestProviderFactory Instance = new TestProviderFactory(); + private TestProviderFactory() { } + } + + public class DbProviderFactoriesTests + { + [Fact] + public void GetFactoryClassesDataTableShapeTest() + { + DataTable initializedTable = DbProviderFactories.GetFactoryClasses(); + Assert.NotNull(initializedTable); + Assert.Equal(4, initializedTable.Columns.Count); + Assert.Equal("Name", initializedTable.Columns[0].ColumnName); + Assert.Equal("Description", initializedTable.Columns[1].ColumnName); + Assert.Equal("InvariantName", initializedTable.Columns[2].ColumnName); + Assert.Equal("AssemblyQualifiedName", initializedTable.Columns[3].ColumnName); + } + + [Fact] + public void GetFactoryNoRegistrationTest() + { + ClearRegisteredFactories(); + Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); + } + + [Fact] + public void GetFactoryWithInvariantNameTest() + { + ClearRegisteredFactories(); + RegisterSqlClientAndTestRegistration(()=>DbProviderFactories.RegisterFactory("System.Data.SqlClient", typeof(System.Data.SqlClient.SqlClientFactory))); + DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.SqlClient"); + Assert.NotNull(factory); + Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); + Assert.Equal(System.Data.SqlClient.SqlClientFactory.Instance, factory); + } + + [Fact] + public void GetFactoryWithDbConnectionTest() + { + ClearRegisteredFactories(); + RegisterSqlClientAndTestRegistration(()=>DbProviderFactories.RegisterFactory("System.Data.SqlClient", typeof(System.Data.SqlClient.SqlClientFactory))); + DbProviderFactory factory = DbProviderFactories.GetFactory(new System.Data.SqlClient.SqlConnection()); + Assert.NotNull(factory); + Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); + Assert.Equal(System.Data.SqlClient.SqlClientFactory.Instance, factory); + } + + [Fact] + public void GetFactoryWithDataRowTest() + { + ClearRegisteredFactories(); + RegisterSqlClientAndTestRegistration(()=> DbProviderFactories.RegisterFactory("System.Data.SqlClient", typeof(System.Data.SqlClient.SqlClientFactory))); + } + + [Fact] + public void RegisterFactoryWithTypeNameTest() + { + ClearRegisteredFactories(); + RegisterSqlClientAndTestRegistration(()=>DbProviderFactories.RegisterFactory("System.Data.SqlClient", typeof(System.Data.SqlClient.SqlClientFactory).AssemblyQualifiedName)); + } + + [Fact] + public void RegisterFactoryWithTypeTest() + { + ClearRegisteredFactories(); + RegisterSqlClientAndTestRegistration(()=>DbProviderFactories.RegisterFactory("System.Data.SqlClient", typeof(System.Data.SqlClient.SqlClientFactory))); + } + + [Fact] + public void RegisterFactoryWithInstanceTest() + { + ClearRegisteredFactories(); + RegisterSqlClientAndTestRegistration(()=>DbProviderFactories.RegisterFactory("System.Data.SqlClient", System.Data.SqlClient.SqlClientFactory.Instance)); + } + + [Fact] + public void RegisterFactoryWithWrongTypeTest() + { + ClearRegisteredFactories(); + Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); + Assert.Throws(() => DbProviderFactories.RegisterFactory("System.Data.SqlClient", typeof(System.Data.SqlClient.SqlConnection))); + } + + [Fact] + public void RegisterFactoryWithBadInvariantNameTest() + { + ClearRegisteredFactories(); + Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); + Assert.Throws(() => DbProviderFactories.RegisterFactory(string.Empty, typeof(System.Data.SqlClient.SqlClientFactory))); + } + + [Fact] + public void RegisterFactoryWithAssemblyQualifiedNameTest() + { + ClearRegisteredFactories(); + RegisterSqlClientAndTestRegistration(()=>DbProviderFactories.RegisterFactory("System.Data.SqlClient", typeof(System.Data.SqlClient.SqlClientFactory).AssemblyQualifiedName)); + } + + [Fact] + public void RegisterFactoryWithWrongAssemblyQualifiedNameTest() + { + ClearRegisteredFactories(); + Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); + DataTable providerTable = DbProviderFactories.GetFactoryClasses(); + Assert.Equal(0, providerTable.Rows.Count); + // register the connection type which is the wrong type. Registraton should succeed, as type registration/checking is deferred. + DbProviderFactories.RegisterFactory("System.Data.SqlClient", typeof(System.Data.SqlClient.SqlConnection).AssemblyQualifiedName); + providerTable = DbProviderFactories.GetFactoryClasses(); + Assert.Equal(1, providerTable.Rows.Count); + // obtaining the factory will kick in the checks of the registered type name, which will cause exceptions. The checks were deferred till the GetFactory() call. + Assert.Throws(() => DbProviderFactories.GetFactory(providerTable.Rows[0])); + Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); + } + + [Fact] + public void UnregisterFactoryTest() + { + ClearRegisteredFactories(); + RegisterSqlClientAndTestRegistration(()=>DbProviderFactories.RegisterFactory("System.Data.SqlClient", System.Data.SqlClient.SqlClientFactory.Instance)); + Assert.True(DbProviderFactories.UnregisterFactory("System.Data.SqlClient")); + DataTable providerTable = DbProviderFactories.GetFactoryClasses(); + Assert.Equal(0, providerTable.Rows.Count); + } + + [Fact] + public void TryGetFactoryTest() + { + ClearRegisteredFactories(); + Assert.False(DbProviderFactories.TryGetFactory("System.Data.SqlClient", out DbProviderFactory f)); + RegisterSqlClientAndTestRegistration(() => DbProviderFactories.RegisterFactory("System.Data.SqlClient", System.Data.SqlClient.SqlClientFactory.Instance)); + Assert.True(DbProviderFactories.TryGetFactory("System.Data.SqlClient", out DbProviderFactory factory)); + Assert.NotNull(factory); + Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); + Assert.Equal(System.Data.SqlClient.SqlClientFactory.Instance, factory); + } + + [Fact] + public void ReplaceFactoryWithRegisterFactoryWithTypeTest() + { + ClearRegisteredFactories(); + RegisterSqlClientAndTestRegistration(()=>DbProviderFactories.RegisterFactory("System.Data.SqlClient", typeof(System.Data.SqlClient.SqlClientFactory))); + DbProviderFactories.RegisterFactory("System.Data.SqlClient", typeof(TestProviderFactory)); + DataTable providerTable = DbProviderFactories.GetFactoryClasses(); + Assert.Equal(1, providerTable.Rows.Count); + DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.SqlClient"); + Assert.NotNull(factory); + Assert.Equal(typeof(TestProviderFactory), factory.GetType()); + Assert.Equal(TestProviderFactory.Instance, factory); + } + + [Fact] + public void GetProviderInvariantNamesTest() + { + ClearRegisteredFactories(); + RegisterSqlClientAndTestRegistration(() => DbProviderFactories.RegisterFactory("System.Data.SqlClient", typeof(System.Data.SqlClient.SqlClientFactory))); + DbProviderFactories.RegisterFactory("System.Data.Common.TestProvider", typeof(TestProviderFactory)); + DataTable providerTable = DbProviderFactories.GetFactoryClasses(); + Assert.Equal(2, providerTable.Rows.Count); + List invariantNames = DbProviderFactories.GetProviderInvariantNames().ToList(); + Assert.Equal(invariantNames.Count, 2); + Assert.True(invariantNames.Contains("System.Data.Common.TestProvider")); + Assert.True(invariantNames.Contains("System.Data.SqlClient")); + } + + private void ClearRegisteredFactories() + { + // as the DbProviderFactories table is shared, for tests we need a clean one before a test starts to make sure the tests always succeed. + Type type = typeof(DbProviderFactories); + FieldInfo info = type.GetField("_registeredFactories", BindingFlags.NonPublic | BindingFlags.Static); + IDictionary providerStorage = info.GetValue(null) as IDictionary; + Assert.NotNull(providerStorage); + providerStorage.Clear(); + Assert.Equal(0, providerStorage.Count); + } + + + private void RegisterSqlClientAndTestRegistration(Action registrationFunc) + { + Assert.NotNull(registrationFunc); + Assert.Throws(() => DbProviderFactories.GetFactory("System.Data.SqlClient")); + DataTable providerTable = DbProviderFactories.GetFactoryClasses(); + Assert.Equal(0, providerTable.Rows.Count); + registrationFunc(); + providerTable = DbProviderFactories.GetFactoryClasses(); + Assert.Equal(1, providerTable.Rows.Count); + DbProviderFactory factory = DbProviderFactories.GetFactory(providerTable.Rows[0]); + Assert.NotNull(factory); + Assert.Equal(typeof(System.Data.SqlClient.SqlClientFactory), factory.GetType()); + Assert.Equal(System.Data.SqlClient.SqlClientFactory.Instance, factory); + } + } +} diff --git a/external/corefx/src/System.Data.Common/tests/System/Data/DataTableReadXmlSchemaTest.cs b/external/corefx/src/System.Data.Common/tests/System/Data/DataTableReadXmlSchemaTest.cs index 0e953a816c..54b6234667 100644 --- a/external/corefx/src/System.Data.Common/tests/System/Data/DataTableReadXmlSchemaTest.cs +++ b/external/corefx/src/System.Data.Common/tests/System/Data/DataTableReadXmlSchemaTest.cs @@ -25,12 +25,15 @@ using System.IO; +using System.Diagnostics; using System.Globalization; +using System.Reflection; +using System.Runtime.Serialization.Formatters.Binary; using Xunit; namespace System.Data.Tests { - public class DataTableReadXmlSchemaTest + public class DataTableReadXmlSchemaTest : RemoteExecutorTestBase { private DataSet CreateTestSet() { @@ -422,5 +425,251 @@ namespace System.Data.Tests Assert.Equal(0, ds.Tables[1].Constraints.Count); Assert.Equal("Constraint1", ds.Tables[0].Constraints[0].ConstraintName); } + + [Fact] + public void XsdSchemaSerializationIgnoresLocale() + { + RemoteInvoke(() => + { + var serializer = new BinaryFormatter(); + var table = new DataTable(); + table.Columns.Add(new DataColumn("RowID", typeof(int)) + { + AutoIncrement = true, + AutoIncrementSeed = -1, // These lines produce attributes within the schema portion of the underlying XML representation of the DataTable with the values "-1" and "-2". + AutoIncrementStep = -2, + }); + table.Columns.Add("Value", typeof(string)); + table.Rows.Add(1, "Test"); + table.Rows.Add(2, "Data"); + + var buffer = new MemoryStream(); + var savedCulture = CultureInfo.CurrentCulture; + try + { + // Before serializing, update the culture to use a weird negative number format. This test is ensuring that this is ignored. + CultureInfo.CurrentCulture = new CultureInfo("en-US") + { + NumberFormat = new NumberFormatInfo() + { + NegativeSign = "()" + } + }; + serializer.Serialize(buffer, table); + } + finally + { + CultureInfo.CurrentCulture = savedCulture; + } + + // The raw serialized data now contains an embedded XML schema. We need to verify that this embedded schema used "-1" for the numeric value + // negative 1, instead of "()1" as indicated by the current culture. + + string rawSerializedData = System.Text.Encoding.ASCII.GetString(buffer.ToArray()); + + const string SchemaStartTag = "= 0); + Assert.True(schemaEnd > schemaStart); + Assert.True(rawSerializedData.IndexOf(" + { + var serializer = new BinaryFormatter(); + + /* + + Test data generator: + + var table = new DataTable(); + table.Columns.Add(new DataColumn("RowID", typeof(int)) + { + AutoIncrement = true, + AutoIncrementSeed = -1, // These lines produce attributes within the schema portion of the underlying XML representation of the DataTable with the value "-1". + AutoIncrementStep = -2, + }); + table.Columns.Add("Value", typeof(string)); + table.Rows.Add(1, "Test"); + table.Rows.Add(2, "Data"); + + var buffer = new MemoryStream(); + serializer.Serialize(buffer, table); + + This test data (binary serializer output) embeds the following XML schema: + + + + + + + + + + + + + + + + + + + The bug being tested here is that the negative integer values in AutoInecrementSeed and AutoIncrementStep fail to parse because the deserialization code + incorrectly uses the current culture instead of the invariant culture when parsing strings like "-1" and "-2". + + */ + + var buffer = new MemoryStream(new byte[] + { + 0,1,0,0,0,255,255,255,255,1,0,0,0,0,0,0,0,12,2,0,0,0,78,83,121,115,116,101,109,46,68,97,116,97,44,32,86,101,114,115,105,111,110,61,52,46,48,46,48,46,48,44,32,67,117, + 108,116,117,114,101,61,110,101,117,116,114,97,108,44,32,80,117,98,108,105,99,75,101,121,84,111,107,101,110,61,98,55,55,97,53,99,53,54,49,57,51,52,101,48,56,57,5,1,0, + 0,0,21,83,121,115,116,101,109,46,68,97,116,97,46,68,97,116,97,84,97,98,108,101,3,0,0,0,25,68,97,116,97,84,97,98,108,101,46,82,101,109,111,116,105,110,103,86,101,114, + 115,105,111,110,9,88,109,108,83,99,104,101,109,97,11,88,109,108,68,105,102,102,71,114,97,109,3,1,1,14,83,121,115,116,101,109,46,86,101,114,115,105,111,110,2,0,0,0,9, + 3,0,0,0,6,4,0,0,0,177,6,60,63,120,109,108,32,118,101,114,115,105,111,110,61,34,49,46,48,34,32,101,110,99,111,100,105,110,103,61,34,117,116,102,45,49,54,34,63,62,13, + 10,60,120,115,58,115,99,104,101,109,97,32,120,109,108,110,115,61,34,34,32,120,109,108,110,115,58,120,115,61,34,104,116,116,112,58,47,47,119,119,119,46,119,51,46,111, + 114,103,47,50,48,48,49,47,88,77,76,83,99,104,101,109,97,34,32,120,109,108,110,115,58,109,115,100,97,116,97,61,34,117,114,110,58,115,99,104,101,109,97,115,45,109,105, + 99,114,111,115,111,102,116,45,99,111,109,58,120,109,108,45,109,115,100,97,116,97,34,62,13,10,32,32,60,120,115,58,101,108,101,109,101,110,116,32,110,97,109,101,61,34, + 84,97,98,108,101,49,34,62,13,10,32,32,32,32,60,120,115,58,99,111,109,112,108,101,120,84,121,112,101,62,13,10,32,32,32,32,32,32,60,120,115,58,115,101,113,117,101,110, + 99,101,62,13,10,32,32,32,32,32,32,32,32,60,120,115,58,101,108,101,109,101,110,116,32,110,97,109,101,61,34,82,111,119,73,68,34,32,109,115,100,97,116,97,58,65,117,116, + 111,73,110,99,114,101,109,101,110,116,61,34,116,114,117,101,34,32,109,115,100,97,116,97,58,65,117,116,111,73,110,99,114,101,109,101,110,116,83,101,101,100,61,34,45, + 49,34,32,109,115,100,97,116,97,58,65,117,116,111,73,110,99,114,101,109,101,110,116,83,116,101,112,61,34,45,50,34,32,116,121,112,101,61,34,120,115,58,105,110,116,34, + 32,109,115,100,97,116,97,58,116,97,114,103,101,116,78,97,109,101,115,112,97,99,101,61,34,34,32,109,105,110,79,99,99,117,114,115,61,34,48,34,32,47,62,13,10,32,32,32, + 32,32,32,32,32,60,120,115,58,101,108,101,109,101,110,116,32,110,97,109,101,61,34,86,97,108,117,101,34,32,116,121,112,101,61,34,120,115,58,115,116,114,105,110,103,34, + 32,109,115,100,97,116,97,58,116,97,114,103,101,116,78,97,109,101,115,112,97,99,101,61,34,34,32,109,105,110,79,99,99,117,114,115,61,34,48,34,32,47,62,13,10,32,32,32, + 32,32,32,60,47,120,115,58,115,101,113,117,101,110,99,101,62,13,10,32,32,32,32,60,47,120,115,58,99,111,109,112,108,101,120,84,121,112,101,62,13,10,32,32,60,47,120,115, + 58,101,108,101,109,101,110,116,62,13,10,32,32,60,120,115,58,101,108,101,109,101,110,116,32,110,97,109,101,61,34,116,109,112,68,97,116,97,83,101,116,34,32,109,115,100, + 97,116,97,58,73,115,68,97,116,97,83,101,116,61,34,116,114,117,101,34,32,109,115,100,97,116,97,58,77,97,105,110,68,97,116,97,84,97,98,108,101,61,34,84,97,98,108,101, + 49,34,32,109,115,100,97,116,97,58,85,115,101,67,117,114,114,101,110,116,76,111,99,97,108,101,61,34,116,114,117,101,34,62,13,10,32,32,32,32,60,120,115,58,99,111,109, + 112,108,101,120,84,121,112,101,62,13,10,32,32,32,32,32,32,60,120,115,58,99,104,111,105,99,101,32,109,105,110,79,99,99,117,114,115,61,34,48,34,32,109,97,120,79,99,99, + 117,114,115,61,34,117,110,98,111,117,110,100,101,100,34,32,47,62,13,10,32,32,32,32,60,47,120,115,58,99,111,109,112,108,101,120,84,121,112,101,62,13,10,32,32,60,47, + 120,115,58,101,108,101,109,101,110,116,62,13,10,60,47,120,115,58,115,99,104,101,109,97,62,6,5,0,0,0,221,3,60,100,105,102,102,103,114,58,100,105,102,102,103,114,97, + 109,32,120,109,108,110,115,58,109,115,100,97,116,97,61,34,117,114,110,58,115,99,104,101,109,97,115,45,109,105,99,114,111,115,111,102,116,45,99,111,109,58,120,109,108, + 45,109,115,100,97,116,97,34,32,120,109,108,110,115,58,100,105,102,102,103,114,61,34,117,114,110,58,115,99,104,101,109,97,115,45,109,105,99,114,111,115,111,102,116,45, + 99,111,109,58,120,109,108,45,100,105,102,102,103,114,97,109,45,118,49,34,62,13,10,32,32,60,116,109,112,68,97,116,97,83,101,116,62,13,10,32,32,32,32,60,84,97,98,108, + 101,49,32,100,105,102,102,103,114,58,105,100,61,34,84,97,98,108,101,49,49,34,32,109,115,100,97,116,97,58,114,111,119,79,114,100,101,114,61,34,48,34,32,100,105,102, + 102,103,114,58,104,97,115,67,104,97,110,103,101,115,61,34,105,110,115,101,114,116,101,100,34,62,13,10,32,32,32,32,32,32,60,82,111,119,73,68,62,49,60,47,82,111,119,73, + 68,62,13,10,32,32,32,32,32,32,60,86,97,108,117,101,62,84,101,115,116,60,47,86,97,108,117,101,62,13,10,32,32,32,32,60,47,84,97,98,108,101,49,62,13,10,32,32,32,32,60, + 84,97,98,108,101,49,32,100,105,102,102,103,114,58,105,100,61,34,84,97,98,108,101,49,50,34,32,109,115,100,97,116,97,58,114,111,119,79,114,100,101,114,61,34,49,34,32, + 100,105,102,102,103,114,58,104,97,115,67,104,97,110,103,101,115,61,34,105,110,115,101,114,116,101,100,34,62,13,10,32,32,32,32,32,32,60,82,111,119,73,68,62,50,60,47, + 82,111,119,73,68,62,13,10,32,32,32,32,32,32,60,86,97,108,117,101,62,68,97,116,97,60,47,86,97,108,117,101,62,13,10,32,32,32,32,60,47,84,97,98,108,101,49,62,13,10,32, + 32,60,47,116,109,112,68,97,116,97,83,101,116,62,13,10,60,47,100,105,102,102,103,114,58,100,105,102,102,103,114,97,109,62,4,3,0,0,0,14,83,121,115,116,101,109,46,86, + 101,114,115,105,111,110,4,0,0,0,6,95,77,97,106,111,114,6,95,77,105,110,111,114,6,95,66,117,105,108,100,9,95,82,101,118,105,115,105,111,110,0,0,0,0,8,8,8,8,2,0,0,0,0, + 0,0,0,255,255,255,255,255,255,255,255,11 + }); + + DataTable table; + var savedCulture = CultureInfo.CurrentCulture; + try + { + // Before deserializing, update the culture to use a weird negative number format. This test is ensuring that this is ignored. + // The bug this test is testing would cause "-1" to no longer be treated as a valid representation of the value -1, instead + // only accepting the string "()1". + CultureInfo.CurrentCulture = new CultureInfo("en-US") + { + NumberFormat = new NumberFormatInfo() + { + NegativeSign = "()" + } + }; + table = (DataTable)serializer.Deserialize(buffer); // BUG: System.Exception: "-1 is not a valid value for Int64." } + } + finally + { + CultureInfo.CurrentCulture = savedCulture; + } + + DataColumn rowIDColumn = table.Columns["RowID"]; + Assert.Equal(-1, rowIDColumn.AutoIncrementSeed); + Assert.Equal(-2, rowIDColumn.AutoIncrementStep); + }); + } + + [Fact] + [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework, "Exists to provide notification of when the full framework gets the bug fix, at which point the preceding test can be re-enabled")] + public void XsdSchemaDeserializationOnFullFrameworkStillHasBug() + { + var serializer = new BinaryFormatter(); + var buffer = new MemoryStream(new byte[] + { + 0,1,0,0,0,255,255,255,255,1,0,0,0,0,0,0,0,12,2,0,0,0,78,83,121,115,116,101,109,46,68,97,116,97,44,32,86,101,114,115,105,111,110,61,52,46,48,46,48,46,48,44,32,67,117, + 108,116,117,114,101,61,110,101,117,116,114,97,108,44,32,80,117,98,108,105,99,75,101,121,84,111,107,101,110,61,98,55,55,97,53,99,53,54,49,57,51,52,101,48,56,57,5,1,0, + 0,0,21,83,121,115,116,101,109,46,68,97,116,97,46,68,97,116,97,84,97,98,108,101,3,0,0,0,25,68,97,116,97,84,97,98,108,101,46,82,101,109,111,116,105,110,103,86,101,114, + 115,105,111,110,9,88,109,108,83,99,104,101,109,97,11,88,109,108,68,105,102,102,71,114,97,109,3,1,1,14,83,121,115,116,101,109,46,86,101,114,115,105,111,110,2,0,0,0,9, + 3,0,0,0,6,4,0,0,0,177,6,60,63,120,109,108,32,118,101,114,115,105,111,110,61,34,49,46,48,34,32,101,110,99,111,100,105,110,103,61,34,117,116,102,45,49,54,34,63,62,13, + 10,60,120,115,58,115,99,104,101,109,97,32,120,109,108,110,115,61,34,34,32,120,109,108,110,115,58,120,115,61,34,104,116,116,112,58,47,47,119,119,119,46,119,51,46,111, + 114,103,47,50,48,48,49,47,88,77,76,83,99,104,101,109,97,34,32,120,109,108,110,115,58,109,115,100,97,116,97,61,34,117,114,110,58,115,99,104,101,109,97,115,45,109,105, + 99,114,111,115,111,102,116,45,99,111,109,58,120,109,108,45,109,115,100,97,116,97,34,62,13,10,32,32,60,120,115,58,101,108,101,109,101,110,116,32,110,97,109,101,61,34, + 84,97,98,108,101,49,34,62,13,10,32,32,32,32,60,120,115,58,99,111,109,112,108,101,120,84,121,112,101,62,13,10,32,32,32,32,32,32,60,120,115,58,115,101,113,117,101,110, + 99,101,62,13,10,32,32,32,32,32,32,32,32,60,120,115,58,101,108,101,109,101,110,116,32,110,97,109,101,61,34,82,111,119,73,68,34,32,109,115,100,97,116,97,58,65,117,116, + 111,73,110,99,114,101,109,101,110,116,61,34,116,114,117,101,34,32,109,115,100,97,116,97,58,65,117,116,111,73,110,99,114,101,109,101,110,116,83,101,101,100,61,34,45, + 49,34,32,109,115,100,97,116,97,58,65,117,116,111,73,110,99,114,101,109,101,110,116,83,116,101,112,61,34,45,50,34,32,116,121,112,101,61,34,120,115,58,105,110,116,34, + 32,109,115,100,97,116,97,58,116,97,114,103,101,116,78,97,109,101,115,112,97,99,101,61,34,34,32,109,105,110,79,99,99,117,114,115,61,34,48,34,32,47,62,13,10,32,32,32, + 32,32,32,32,32,60,120,115,58,101,108,101,109,101,110,116,32,110,97,109,101,61,34,86,97,108,117,101,34,32,116,121,112,101,61,34,120,115,58,115,116,114,105,110,103,34, + 32,109,115,100,97,116,97,58,116,97,114,103,101,116,78,97,109,101,115,112,97,99,101,61,34,34,32,109,105,110,79,99,99,117,114,115,61,34,48,34,32,47,62,13,10,32,32,32, + 32,32,32,60,47,120,115,58,115,101,113,117,101,110,99,101,62,13,10,32,32,32,32,60,47,120,115,58,99,111,109,112,108,101,120,84,121,112,101,62,13,10,32,32,60,47,120,115, + 58,101,108,101,109,101,110,116,62,13,10,32,32,60,120,115,58,101,108,101,109,101,110,116,32,110,97,109,101,61,34,116,109,112,68,97,116,97,83,101,116,34,32,109,115,100, + 97,116,97,58,73,115,68,97,116,97,83,101,116,61,34,116,114,117,101,34,32,109,115,100,97,116,97,58,77,97,105,110,68,97,116,97,84,97,98,108,101,61,34,84,97,98,108,101, + 49,34,32,109,115,100,97,116,97,58,85,115,101,67,117,114,114,101,110,116,76,111,99,97,108,101,61,34,116,114,117,101,34,62,13,10,32,32,32,32,60,120,115,58,99,111,109, + 112,108,101,120,84,121,112,101,62,13,10,32,32,32,32,32,32,60,120,115,58,99,104,111,105,99,101,32,109,105,110,79,99,99,117,114,115,61,34,48,34,32,109,97,120,79,99,99, + 117,114,115,61,34,117,110,98,111,117,110,100,101,100,34,32,47,62,13,10,32,32,32,32,60,47,120,115,58,99,111,109,112,108,101,120,84,121,112,101,62,13,10,32,32,60,47, + 120,115,58,101,108,101,109,101,110,116,62,13,10,60,47,120,115,58,115,99,104,101,109,97,62,6,5,0,0,0,221,3,60,100,105,102,102,103,114,58,100,105,102,102,103,114,97, + 109,32,120,109,108,110,115,58,109,115,100,97,116,97,61,34,117,114,110,58,115,99,104,101,109,97,115,45,109,105,99,114,111,115,111,102,116,45,99,111,109,58,120,109,108, + 45,109,115,100,97,116,97,34,32,120,109,108,110,115,58,100,105,102,102,103,114,61,34,117,114,110,58,115,99,104,101,109,97,115,45,109,105,99,114,111,115,111,102,116,45, + 99,111,109,58,120,109,108,45,100,105,102,102,103,114,97,109,45,118,49,34,62,13,10,32,32,60,116,109,112,68,97,116,97,83,101,116,62,13,10,32,32,32,32,60,84,97,98,108, + 101,49,32,100,105,102,102,103,114,58,105,100,61,34,84,97,98,108,101,49,49,34,32,109,115,100,97,116,97,58,114,111,119,79,114,100,101,114,61,34,48,34,32,100,105,102, + 102,103,114,58,104,97,115,67,104,97,110,103,101,115,61,34,105,110,115,101,114,116,101,100,34,62,13,10,32,32,32,32,32,32,60,82,111,119,73,68,62,49,60,47,82,111,119,73, + 68,62,13,10,32,32,32,32,32,32,60,86,97,108,117,101,62,84,101,115,116,60,47,86,97,108,117,101,62,13,10,32,32,32,32,60,47,84,97,98,108,101,49,62,13,10,32,32,32,32,60, + 84,97,98,108,101,49,32,100,105,102,102,103,114,58,105,100,61,34,84,97,98,108,101,49,50,34,32,109,115,100,97,116,97,58,114,111,119,79,114,100,101,114,61,34,49,34,32, + 100,105,102,102,103,114,58,104,97,115,67,104,97,110,103,101,115,61,34,105,110,115,101,114,116,101,100,34,62,13,10,32,32,32,32,32,32,60,82,111,119,73,68,62,50,60,47, + 82,111,119,73,68,62,13,10,32,32,32,32,32,32,60,86,97,108,117,101,62,68,97,116,97,60,47,86,97,108,117,101,62,13,10,32,32,32,32,60,47,84,97,98,108,101,49,62,13,10,32, + 32,60,47,116,109,112,68,97,116,97,83,101,116,62,13,10,60,47,100,105,102,102,103,114,58,100,105,102,102,103,114,97,109,62,4,3,0,0,0,14,83,121,115,116,101,109,46,86, + 101,114,115,105,111,110,4,0,0,0,6,95,77,97,106,111,114,6,95,77,105,110,111,114,6,95,66,117,105,108,100,9,95,82,101,118,105,115,105,111,110,0,0,0,0,8,8,8,8,2,0,0,0,0, + 0,0,0,255,255,255,255,255,255,255,255,11 + }); + + Exception exception; + CultureInfo savedCulture = CultureInfo.CurrentCulture; + try + { + exception = Assert.Throws(() => + { + // Before deserializing, update the culture to use a weird negative number format. The bug this test is testing causes "-1" to no + // longer be treated as a valid representation of the value -1, instead only accepting the string "()1". + CultureInfo.CurrentCulture = new CultureInfo("en-US") + { + NumberFormat = new NumberFormatInfo() + { + NegativeSign = "()" + } + }; + serializer.Deserialize(buffer); // BUG: System.Exception: "-1 is not a valid value for Int64." + }); + } + finally + { + CultureInfo.CurrentCulture = savedCulture; + } + + Assert.IsAssignableFrom(exception.InnerException.InnerException); + } } } diff --git a/external/corefx/src/System.Data.Common/tests/System/Data/DataTableTest.cs.REMOVED.git-id b/external/corefx/src/System.Data.Common/tests/System/Data/DataTableTest.cs.REMOVED.git-id index 2092698b11..bf9eb46b9a 100644 --- a/external/corefx/src/System.Data.Common/tests/System/Data/DataTableTest.cs.REMOVED.git-id +++ b/external/corefx/src/System.Data.Common/tests/System/Data/DataTableTest.cs.REMOVED.git-id @@ -1 +1 @@ -90ac29b27c18e67a2c878cff5fd1fa4872e0babd \ No newline at end of file +92f507d88a8a1d43b46a919429994cd922f2f405 \ No newline at end of file diff --git a/external/corefx/src/System.Data.Odbc/System.Data.Odbc.sln b/external/corefx/src/System.Data.Odbc/System.Data.Odbc.sln index 41d6fa4d0a..362d3cbc04 100644 --- a/external/corefx/src/System.Data.Odbc/System.Data.Odbc.sln +++ b/external/corefx/src/System.Data.Odbc/System.Data.Odbc.sln @@ -26,14 +26,14 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {F3E72F35-0351-4D67-2209-725BCAD807BA}.Debug|Any CPU.ActiveCfg = netstandard-Windows_NT-Debug|Any CPU - {F3E72F35-0351-4D67-2209-725BCAD807BA}.Debug|Any CPU.Build.0 = netstandard-Windows_NT-Debug|Any CPU - {F3E72F35-0351-4D67-2209-725BCAD807BA}.Release|Any CPU.ActiveCfg = netstandard-Windows_NT-Release|Any CPU - {F3E72F35-0351-4D67-2209-725BCAD807BA}.Release|Any CPU.Build.0 = netstandard-Windows_NT-Release|Any CPU - {7BAD100F-AD6B-490A-AF7C-8E3854E812C0}.Debug|Any CPU.ActiveCfg = netstandard-Windows_NT-Debug|Any CPU - {7BAD100F-AD6B-490A-AF7C-8E3854E812C0}.Debug|Any CPU.Build.0 = netstandard-Windows_NT-Debug|Any CPU - {7BAD100F-AD6B-490A-AF7C-8E3854E812C0}.Release|Any CPU.ActiveCfg = netstandard-Windows_NT-Release|Any CPU - {7BAD100F-AD6B-490A-AF7C-8E3854E812C0}.Release|Any CPU.Build.0 = netstandard-Windows_NT-Release|Any CPU + {F3E72F35-0351-4D67-2209-725BCAD807BA}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {F3E72F35-0351-4D67-2209-725BCAD807BA}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {F3E72F35-0351-4D67-2209-725BCAD807BA}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {F3E72F35-0351-4D67-2209-725BCAD807BA}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU + {7BAD100F-AD6B-490A-AF7C-8E3854E812C0}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {7BAD100F-AD6B-490A-AF7C-8E3854E812C0}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {7BAD100F-AD6B-490A-AF7C-8E3854E812C0}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {7BAD100F-AD6B-490A-AF7C-8E3854E812C0}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU {D589374B-3331-4660-8DFB-512D66F8EC63}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU {D589374B-3331-4660-8DFB-512D66F8EC63}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU {D589374B-3331-4660-8DFB-512D66F8EC63}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU diff --git a/external/corefx/src/System.Data.Odbc/ref/System.Data.Odbc.cs b/external/corefx/src/System.Data.Odbc/ref/System.Data.Odbc.cs index 34b1c2ffde..88ca92594d 100644 --- a/external/corefx/src/System.Data.Odbc/ref/System.Data.Odbc.cs +++ b/external/corefx/src/System.Data.Odbc/ref/System.Data.Odbc.cs @@ -1,3 +1,7 @@ +// 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. + namespace System.Data.Odbc { public sealed partial class OdbcCommand : System.Data.Common.DbCommand, System.ICloneable @@ -198,7 +202,6 @@ namespace System.Data.Odbc internal OdbcException() { } public System.Data.Odbc.OdbcErrorCollection Errors { get { throw null; } } public override string Source { get { throw null; } } - [System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags=(System.Security.Permissions.SecurityPermissionFlag)(128))] public override void GetObjectData(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext context) { } } public sealed partial class OdbcFactory : System.Data.Common.DbProviderFactory diff --git a/external/corefx/src/System.Data.Odbc/src/Common/System/Data/Common/SafeNativeMethods.cs b/external/corefx/src/System.Data.Odbc/src/Common/System/Data/Common/SafeNativeMethods.cs index e619e7159c..a0bc8c9bc4 100644 --- a/external/corefx/src/System.Data.Odbc/src/Common/System/Data/Common/SafeNativeMethods.cs +++ b/external/corefx/src/System.Data.Odbc/src/Common/System/Data/Common/SafeNativeMethods.cs @@ -1,4 +1,8 @@ -using System.Runtime.InteropServices; +// 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.Runtime.InteropServices; namespace System.Data { diff --git a/external/corefx/src/System.Data.Odbc/src/Configurations.props b/external/corefx/src/System.Data.Odbc/src/Configurations.props index 8c227f14fc..75dbf04a7a 100644 --- a/external/corefx/src/System.Data.Odbc/src/Configurations.props +++ b/external/corefx/src/System.Data.Odbc/src/Configurations.props @@ -1,12 +1,20 @@  - + + netcoreapp2.0-FreeBSD; + netcoreapp2.0-Linux; + netcoreapp2.0-OSX; + netcoreapp2.0-Windows_NT; netfx-Windows_NT; netstandard; - netstandard-Linux; - netstandard-OSX; - netstandard-Windows_NT; + + + $(PackageConfigurations) + netcoreapp-FreeBSD; + netcoreapp-Linux; + netcoreapp-OSX; + netcoreapp-Windows_NT; diff --git a/external/corefx/src/System.Data.Odbc/src/DatabaseSetupInstructions.md b/external/corefx/src/System.Data.Odbc/src/DatabaseSetupInstructions.md new file mode 100644 index 0000000000..967bdac3e4 --- /dev/null +++ b/external/corefx/src/System.Data.Odbc/src/DatabaseSetupInstructions.md @@ -0,0 +1,98 @@ +# Instructions on how to setup database + +## In Fedora 24 container: +- `docker ps` shows _id of existing Fedora 24 container +- `docker exec -it _id /bin/sh` +- `dnf install findutils` need to install findutils for building corefx to add missing xargs +- `find / -name libsqlite3odbc.so` to be used in odbcinst.ini +- `odbcinst -j` to show version and location of ini files +- `ldconfig -p | grep sqlite` +- `ldconfig -p | grep odbc` +- `dnf list | grep unixODBC` +- `dnf install unixODBC.x86_64` +- `dnf install unixODBC-devel.x86_64` +- `dnf install sqliteodbc.x86_64` + +## Notes on commands used in Debian 8.2 +- followed [dockerfile](https://devdiv.visualstudio.com/DevDiv/_git/DotNetCore?path=%2Fdockerfiles%2Fdebian%2F8.2%2FDockerfile&version=GBmaster) instructions for debian 8.2 +- dependencies: libkrb5-dev, cmake + +Get the tag name from https://hub.docker.com/r/microsoft/dotnet-buildtools-prereqs/tags/ and use in docker run below +- `docker run -it microsoft/dotnet-buildtools-prereqs:debian-8.2-SHA-YMD.. /bin/sh` +- `docker images` shows _id for Debian 8.2 to use in command below +- `docker exec -it _id /bin/sh` + +## Notes on commands used in Linux 14.04 +- Not supported. The minimum required unixODBC version should be 2.3.1, but is not available on Ubuntu 14.04. + +## Notes on commands used in Linux 16.04 +- `wget "ftp://ftp.unixodbc.org/pub/unixODBC/unixODBC-2.3.4.tar.gz"` download and install unixODBC +- `gunzip unixODBC-2.3.4.tar.gz` +- `tar xvf unixODBC-2.3.4.tar` +- `cd unixODBC-2.3.4/` +- `./configure` +- `make` +- `sudo make install` +- `cd ..` +- `wget "http://www.ch-werner.de/sqliteodbc/sqliteodbc-0.9995.tar.gz"` download and install SQLite ODBC Driver +- `gunzip sqliteodbc-0.9995.tar.gz` +- `tar xvf sqliteodbc-0.9995.tar` +- `cd sqliteodbc-0.9995/` +- `./configure` +- `make` +- `sudo make install` +- `sudo nano /usr/local/etc/odbcinst.ini` + +``` +[ODBC Drivers] +SQLite3 ODBC Driver=Installed + +[SQLite3 ODBC Driver] +Description=SQLite3 ODBC Driver +Driver=/usr/local/lib/libsqlite3odbc.so +Setup=/usr/local/lib/libsqlite3odbc.so +``` + +- `sudo nano /etc/odbcinst.ini` + +``` +[SQLite3 ODBC Driver] +Description=SQLite ODBC Driver +Driver=/usr/local/lib/libsqlite3odbc.so +Setup=/usr/local/lib/libsqlite3odbc.so +Threading=4 +``` + +## Notes on commands used in Mac +- `gunzip unixODBC-2.3.4.tar.gz` download unixodbc +- `tar xvf unixODBC-2.3.4.tar` +- `cd unix...` +- `./configure` +- `make` +- `make install` +- `/usr/local/bin/odbcinst` try out odbcinst using the command below +- `sudo nano /etc/odbcinst.ini` + +``` + [SQLite3 ODBC Driver] + Description=SQLite ODBC Driver + Driver=/usr/local/lib/libsqlite3odbc.so + Setup=/usr/local/lib/libsqlite3odbc.so + Threading=4 +``` + +- `sudo nano find / -name odbcinst.ini` +- `sudo nano /usr/local/etc/odbcinst.ini` + +``` + [ODBC Drivers] + SQLite3 ODBC Driver=Installed + + [SQLite3 ODBC Driver] + Driver=/usr/local/lib/libsqlite3odbc.dylib + Setup=/usr/local/lib/libsqlite3odbc.dylib +``` + +## Notes on commands used in Windows7-10, 64 bit machine +- odbc32.dll already available, just needs to install sqlite64.exe + diff --git a/external/corefx/src/System.Data.Odbc/src/System.Data.Odbc.csproj b/external/corefx/src/System.Data.Odbc/src/System.Data.Odbc.csproj index e24e87e76c..9f9a3db2aa 100644 --- a/external/corefx/src/System.Data.Odbc/src/System.Data.Odbc.csproj +++ b/external/corefx/src/System.Data.Odbc/src/System.Data.Odbc.csproj @@ -6,17 +6,29 @@ System.Data.Odbc true true - SR.Odbc_PlatformNotSupported + SR.Odbc_PlatformNotSupported + + + + + + + + + + + + + + + + - - - - - - - + + + Common\System\Data\Common\AdapterUtil.cs @@ -55,7 +67,6 @@ - @@ -108,16 +119,24 @@ Common\System\Data\Common\MultipartIdentifier.cs - + + Common\Interop\Interop.Odbc.cs + - - + + + Common\Interop\Linux\Interop.Libraries.cs + - - + + + Common\Interop\OSX\Interop.Libraries.cs + - - + + + Common\Interop\Windows\Interop.Libraries.cs + @@ -131,7 +150,6 @@ - @@ -156,6 +174,13 @@ + + + + + + + - \ No newline at end of file + diff --git a/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/Odbc32.cs b/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/Odbc32.cs index 49dcaf9804..e54e95f498 100644 --- a/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/Odbc32.cs +++ b/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/Odbc32.cs @@ -123,8 +123,11 @@ namespace System.Data.Odbc } } - - internal static class ODBC32 +#if !MONO + // Class needs to be public to support serialization with type forwarding from Desktop to Core. + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif + public static class ODBC32 { internal enum SQL_HANDLE : short { @@ -136,6 +139,9 @@ namespace System.Data.Odbc // from .\public\sdk\inc\sqlext.h: and .\public\sdk\inc\sql.h // must be public because it is serialized by OdbcException +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public enum RETCODE : int { // must be int instead of short for Everett OdbcException Serializablity. SUCCESS = 0, diff --git a/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcConnectionHandle.cs b/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcConnectionHandle.cs index f21844c328..3f57b5d1cd 100644 --- a/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcConnectionHandle.cs +++ b/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcConnectionHandle.cs @@ -64,7 +64,7 @@ namespace System.Data.Odbc try { } finally { - retcode = UnsafeNativeMethods.SQLSetConnectAttrW(this, ODBC32.SQL_ATTR.AUTOCOMMIT, ODBC32.SQL_AUTOCOMMIT_OFF, (Int32)ODBC32.SQL_IS.UINTEGER); + retcode = Interop.Odbc.SQLSetConnectAttrW(this, ODBC32.SQL_ATTR.AUTOCOMMIT, ODBC32.SQL_AUTOCOMMIT_OFF, (Int32)ODBC32.SQL_IS.UINTEGER); switch (retcode) { case ODBC32.RetCode.SUCCESS: @@ -172,7 +172,7 @@ namespace System.Data.Odbc { if (HandleState.TransactionInProgress == _handleState) { - retcode = UnsafeNativeMethods.SQLEndTran(HandleType, handle, transactionOperation); + retcode = Interop.Odbc.SQLEndTran(HandleType, handle, transactionOperation); if ((ODBC32.RetCode.SUCCESS == retcode) || (ODBC32.RetCode.SUCCESS_WITH_INFO == retcode)) { _handleState = HandleState.Transacted; @@ -181,7 +181,7 @@ namespace System.Data.Odbc if (HandleState.Transacted == _handleState) { // AutoCommitOn - retcode = UnsafeNativeMethods.SQLSetConnectAttrW(handle, ODBC32.SQL_ATTR.AUTOCOMMIT, ODBC32.SQL_AUTOCOMMIT_ON, (Int32)ODBC32.SQL_IS.UINTEGER); + retcode = Interop.Odbc.SQLSetConnectAttrW(handle, ODBC32.SQL_ATTR.AUTOCOMMIT, ODBC32.SQL_AUTOCOMMIT_ON, (Int32)ODBC32.SQL_IS.UINTEGER); _handleState = HandleState.Connected; } } @@ -202,7 +202,7 @@ namespace System.Data.Odbc finally { short cbActualSize; - retcode = UnsafeNativeMethods.SQLDriverConnectW(this, ADP.PtrZero, connectionString, ODBC32.SQL_NTS, ADP.PtrZero, 0, out cbActualSize, (short)ODBC32.SQL_DRIVER.NOPROMPT); + retcode = Interop.Odbc.SQLDriverConnectW(this, ADP.PtrZero, connectionString, ODBC32.SQL_NTS, ADP.PtrZero, 0, out cbActualSize, (short)ODBC32.SQL_DRIVER.NOPROMPT); switch (retcode) { case ODBC32.RetCode.SUCCESS: @@ -225,7 +225,7 @@ namespace System.Data.Odbc if ((HandleState.Connected == _handleState) || (HandleState.TransactionInProgress == _handleState)) { - retcode = UnsafeNativeMethods.SQLDisconnect(handle); + retcode = Interop.Odbc.SQLDisconnect(handle); _handleState = HandleState.Allocated; } Debug.Assert(HandleState.Allocated == _handleState, "not expected HandleState.Allocated"); @@ -234,45 +234,45 @@ namespace System.Data.Odbc internal ODBC32.RetCode GetConnectionAttribute(ODBC32.SQL_ATTR attribute, byte[] buffer, out int cbActual) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLGetConnectAttrW(this, attribute, buffer, buffer.Length, out cbActual); + ODBC32.RetCode retcode = Interop.Odbc.SQLGetConnectAttrW(this, attribute, buffer, buffer.Length, out cbActual); return retcode; } internal ODBC32.RetCode GetFunctions(ODBC32.SQL_API fFunction, out Int16 fExists) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLGetFunctions(this, fFunction, out fExists); + ODBC32.RetCode retcode = Interop.Odbc.SQLGetFunctions(this, fFunction, out fExists); ODBC.TraceODBC(3, "SQLGetFunctions", retcode); return retcode; } internal ODBC32.RetCode GetInfo2(ODBC32.SQL_INFO info, byte[] buffer, out short cbActual) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLGetInfoW(this, info, buffer, checked((short)buffer.Length), out cbActual); + ODBC32.RetCode retcode = Interop.Odbc.SQLGetInfoW(this, info, buffer, checked((short)buffer.Length), out cbActual); return retcode; } internal ODBC32.RetCode GetInfo1(ODBC32.SQL_INFO info, byte[] buffer) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLGetInfoW(this, info, buffer, checked((short)buffer.Length), ADP.PtrZero); + ODBC32.RetCode retcode = Interop.Odbc.SQLGetInfoW(this, info, buffer, checked((short)buffer.Length), ADP.PtrZero); return retcode; } internal ODBC32.RetCode SetConnectionAttribute2(ODBC32.SQL_ATTR attribute, IntPtr value, Int32 length) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLSetConnectAttrW(this, attribute, value, length); + ODBC32.RetCode retcode = Interop.Odbc.SQLSetConnectAttrW(this, attribute, value, length); ODBC.TraceODBC(3, "SQLSetConnectAttrW", retcode); return retcode; } internal ODBC32.RetCode SetConnectionAttribute3(ODBC32.SQL_ATTR attribute, string buffer, Int32 length) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLSetConnectAttrW(this, attribute, buffer, length); + ODBC32.RetCode retcode = Interop.Odbc.SQLSetConnectAttrW(this, attribute, buffer, length); return retcode; } internal ODBC32.RetCode SetConnectionAttribute4(ODBC32.SQL_ATTR attribute, System.Transactions.IDtcTransaction transaction, Int32 length) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLSetConnectAttrW(this, attribute, transaction, length); + ODBC32.RetCode retcode = Interop.Odbc.SQLSetConnectAttrW(this, attribute, transaction, length); ODBC.TraceODBC(3, "SQLSetConnectAttrW", retcode); return retcode; } diff --git a/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcEnvironmentHandle.cs b/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcEnvironmentHandle.cs index 1c471e2b72..1f990cad8d 100644 --- a/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcEnvironmentHandle.cs +++ b/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcEnvironmentHandle.cs @@ -18,7 +18,7 @@ namespace System.Data.Odbc //Set the expected driver manager version // - retcode = UnsafeNativeMethods.SQLSetEnvAttr( + retcode = Interop.Odbc.SQLSetEnvAttr( this, ODBC32.SQL_ATTR.ODBC_VERSION, ODBC32.SQL_OV_ODBC3, @@ -30,7 +30,7 @@ namespace System.Data.Odbc //handle are pooled. So we have to keep it alive and not create a new environment //for every connection. // - retcode = UnsafeNativeMethods.SQLSetEnvAttr( + retcode = Interop.Odbc.SQLSetEnvAttr( this, ODBC32.SQL_ATTR.CONNECTION_POOLING, ODBC32.SQL_CP_ONE_PER_HENV, diff --git a/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcError.cs b/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcError.cs index bf7508d23b..6bab2aeaf5 100644 --- a/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcError.cs +++ b/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcError.cs @@ -5,6 +5,9 @@ namespace System.Data.Odbc { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public sealed class OdbcError { //Data diff --git a/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcErrorCollection.cs b/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcErrorCollection.cs index e6fa0ad5ba..285e587a5f 100644 --- a/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcErrorCollection.cs +++ b/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcErrorCollection.cs @@ -7,15 +7,18 @@ using System.Collections; namespace System.Data.Odbc { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public sealed class OdbcErrorCollection : ICollection { - private ArrayList _items = new ArrayList(); + private ArrayList _items = new ArrayList(); // Do not rename (binary serialization) internal OdbcErrorCollection() { } - Object System.Collections.ICollection.SyncRoot + object System.Collections.ICollection.SyncRoot { get { return this; } } diff --git a/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcException.cs b/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcException.cs index 724ab8068d..0645406036 100644 --- a/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcException.cs +++ b/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcException.cs @@ -2,13 +2,15 @@ // 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.Data.Common; using System.Runtime.Serialization; using System.Text; namespace System.Data.Odbc { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public sealed class OdbcException : System.Data.Common.DbException { private OdbcErrorCollection _odbcErrors = new OdbcErrorCollection(); @@ -35,6 +37,13 @@ namespace System.Data.Odbc HResult = HResults.OdbcException; } + private OdbcException(SerializationInfo si, StreamingContext sc) : base(si, sc) + { + // Ignoring ODBC32.RETCODE + _odbcErrors = (OdbcErrorCollection)si.GetValue("odbcErrors", typeof(OdbcErrorCollection)); + HResult = HResults.OdbcException; + } + public OdbcErrorCollection Errors { get @@ -46,6 +55,8 @@ namespace System.Data.Odbc public override void GetObjectData(SerializationInfo si, StreamingContext context) { base.GetObjectData(si, context); + si.AddValue("odbcRetcode", default(ODBC32.RETCODE), typeof(ODBC32.RETCODE)); // Using default value of ODBC32.RETCODE + si.AddValue("odbcErrors", _odbcErrors, typeof(OdbcErrorCollection)); } // mdac bug 62559 - if we don't have it return nothing (empty string) diff --git a/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcHandle.cs b/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcHandle.cs index 799e9e4b1c..b1490520e0 100644 --- a/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcHandle.cs +++ b/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcHandle.cs @@ -31,7 +31,7 @@ namespace System.Data.Odbc { case ODBC32.SQL_HANDLE.ENV: Debug.Assert(null == parentHandle, "did not expect a parent handle"); - retcode = UnsafeNativeMethods.SQLAllocHandle(handleType, IntPtr.Zero, out base.handle); + retcode = Interop.Odbc.SQLAllocHandle(handleType, IntPtr.Zero, out base.handle); break; case ODBC32.SQL_HANDLE.DBC: case ODBC32.SQL_HANDLE.STMT: @@ -39,7 +39,7 @@ namespace System.Data.Odbc Debug.Assert(null != parentHandle, "expected a parent handle"); // safehandle can't be null parentHandle.DangerousAddRef(ref mustRelease); - retcode = UnsafeNativeMethods.SQLAllocHandle(handleType, parentHandle, out base.handle); + retcode = Interop.Odbc.SQLAllocHandle(handleType, parentHandle, out base.handle); break; // case ODBC32.SQL_HANDLE.DESC: default: @@ -151,7 +151,7 @@ namespace System.Data.Odbc // Disconnect happens in OdbcConnectionHandle.ReleaseHandle case ODBC32.SQL_HANDLE.ENV: case ODBC32.SQL_HANDLE.STMT: - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLFreeHandle(handleType, handle); + ODBC32.RetCode retcode = Interop.Odbc.SQLFreeHandle(handleType, handle); break; case ODBC32.SQL_HANDLE.DESC: @@ -182,7 +182,7 @@ namespace System.Data.Odbc short cbActual; // ODBC (MSDN) documents it expects a buffer large enough to hold 5(+L'\0') unicode characters StringBuilder sb = new StringBuilder(6); - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLGetDiagFieldW( + ODBC32.RetCode retcode = Interop.Odbc.SQLGetDiagFieldW( HandleType, this, (short)1, @@ -206,7 +206,7 @@ namespace System.Data.Odbc { // ODBC (MSDN) documents it expects a buffer large enough to hold 4(+L'\0') unicode characters StringBuilder sb = new StringBuilder(5); - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLGetDiagRecW(HandleType, this, record, sb, out nativeError, message, checked((short)message.Capacity), out cchActual); + ODBC32.RetCode retcode = Interop.Odbc.SQLGetDiagRecW(HandleType, this, record, sb, out nativeError, message, checked((short)message.Capacity), out cchActual); ODBC.TraceODBC(3, "SQLGetDiagRecW", retcode); if ((retcode == ODBC32.RetCode.SUCCESS) || (retcode == ODBC32.RetCode.SUCCESS_WITH_INFO)) @@ -229,21 +229,21 @@ namespace System.Data.Odbc internal ODBC32.RetCode GetDescriptionField(int i, ODBC32.SQL_DESC attribute, CNativeBuffer buffer, out int numericAttribute) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLGetDescFieldW(this, checked((short)i), attribute, buffer, buffer.ShortLength, out numericAttribute); + ODBC32.RetCode retcode = Interop.Odbc.SQLGetDescFieldW(this, checked((short)i), attribute, buffer, buffer.ShortLength, out numericAttribute); ODBC.TraceODBC(3, "SQLGetDescFieldW", retcode); return retcode; } internal ODBC32.RetCode SetDescriptionField1(short ordinal, ODBC32.SQL_DESC type, IntPtr value) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLSetDescFieldW(this, ordinal, type, value, 0); + ODBC32.RetCode retcode = Interop.Odbc.SQLSetDescFieldW(this, ordinal, type, value, 0); ODBC.TraceODBC(3, "SQLSetDescFieldW", retcode); return retcode; } internal ODBC32.RetCode SetDescriptionField2(short ordinal, ODBC32.SQL_DESC type, HandleRef value) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLSetDescFieldW(this, ordinal, type, value, 0); + ODBC32.RetCode retcode = Interop.Odbc.SQLSetDescFieldW(this, ordinal, type, value, 0); ODBC.TraceODBC(3, "SQLSetDescFieldW", retcode); return retcode; } diff --git a/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcMetaDataFactory.cs b/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcMetaDataFactory.cs index 09d031001d..1061b1dd33 100644 --- a/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcMetaDataFactory.cs +++ b/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcMetaDataFactory.cs @@ -12,7 +12,7 @@ namespace System.Data.Odbc { internal class OdbcMetaDataFactory : DbMetaDataFactory { - private struct SchemaFunctionName + private readonly struct SchemaFunctionName { internal SchemaFunctionName(string schemaName, ODBC32.SQL_API odbcFunction) { diff --git a/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcStatementHandle.cs b/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcStatementHandle.cs index f8a938256e..8372905cfd 100644 --- a/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcStatementHandle.cs +++ b/external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcStatementHandle.cs @@ -69,21 +69,21 @@ namespace System.Data.Odbc internal ODBC32.RetCode BindColumn2(int columnNumber, ODBC32.SQL_C targetType, HandleRef buffer, IntPtr length, IntPtr srLen_or_Ind) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLBindCol(this, checked((ushort)columnNumber), targetType, buffer, length, srLen_or_Ind); + ODBC32.RetCode retcode = Interop.Odbc.SQLBindCol(this, checked((ushort)columnNumber), targetType, buffer, length, srLen_or_Ind); ODBC.TraceODBC(3, "SQLBindCol", retcode); return retcode; } internal ODBC32.RetCode BindColumn3(int columnNumber, ODBC32.SQL_C targetType, IntPtr srLen_or_Ind) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLBindCol(this, checked((ushort)columnNumber), targetType, ADP.PtrZero, ADP.PtrZero, srLen_or_Ind); + ODBC32.RetCode retcode = Interop.Odbc.SQLBindCol(this, checked((ushort)columnNumber), targetType, ADP.PtrZero, ADP.PtrZero, srLen_or_Ind); ODBC.TraceODBC(3, "SQLBindCol", retcode); return retcode; } internal ODBC32.RetCode BindParameter(short ordinal, short parameterDirection, ODBC32.SQL_C sqlctype, ODBC32.SQL_TYPE sqltype, IntPtr cchSize, IntPtr scale, HandleRef buffer, IntPtr bufferLength, HandleRef intbuffer) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLBindParameter(this, + ODBC32.RetCode retcode = Interop.Odbc.SQLBindParameter(this, checked((ushort)ordinal), // Parameter Number parameterDirection, // InputOutputType sqlctype, // ValueType @@ -101,14 +101,14 @@ namespace System.Data.Odbc { // In ODBC3.0 ... a call to SQLCancel when no processing is done has no effect at all // (ODBC Programmer's Reference ...) - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLCancel(this); + ODBC32.RetCode retcode = Interop.Odbc.SQLCancel(this); ODBC.TraceODBC(3, "SQLCancel", retcode); return retcode; } internal ODBC32.RetCode CloseCursor() { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLCloseCursor(this); + ODBC32.RetCode retcode = Interop.Odbc.SQLCloseCursor(this); ODBC.TraceODBC(3, "SQLCloseCursor", retcode); return retcode; } @@ -116,7 +116,7 @@ namespace System.Data.Odbc internal ODBC32.RetCode ColumnAttribute(int columnNumber, short fieldIdentifier, CNativeBuffer characterAttribute, out short stringLength, out SQLLEN numericAttribute) { IntPtr result; - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLColAttributeW(this, checked((short)columnNumber), fieldIdentifier, characterAttribute, characterAttribute.ShortLength, out stringLength, out result); + ODBC32.RetCode retcode = Interop.Odbc.SQLColAttributeW(this, checked((short)columnNumber), fieldIdentifier, characterAttribute, characterAttribute.ShortLength, out stringLength, out result); numericAttribute = new SQLLEN(result); ODBC.TraceODBC(3, "SQLColAttributeW", retcode); return retcode; @@ -127,7 +127,7 @@ namespace System.Data.Odbc string tableName, string columnName) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLColumnsW(this, + ODBC32.RetCode retcode = Interop.Odbc.SQLColumnsW(this, tableCatalog, ODBC.ShortStringLength(tableCatalog), tableSchema, @@ -143,35 +143,35 @@ namespace System.Data.Odbc internal ODBC32.RetCode Execute() { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLExecute(this); + ODBC32.RetCode retcode = Interop.Odbc.SQLExecute(this); ODBC.TraceODBC(3, "SQLExecute", retcode); return retcode; } internal ODBC32.RetCode ExecuteDirect(string commandText) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLExecDirectW(this, commandText, ODBC32.SQL_NTS); + ODBC32.RetCode retcode = Interop.Odbc.SQLExecDirectW(this, commandText, ODBC32.SQL_NTS); ODBC.TraceODBC(3, "SQLExecDirectW", retcode); return retcode; } internal ODBC32.RetCode Fetch() { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLFetch(this); + ODBC32.RetCode retcode = Interop.Odbc.SQLFetch(this); ODBC.TraceODBC(3, "SQLFetch", retcode); return retcode; } internal ODBC32.RetCode FreeStatement(ODBC32.STMT stmt) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLFreeStmt(this, stmt); + ODBC32.RetCode retcode = Interop.Odbc.SQLFreeStmt(this, stmt); ODBC.TraceODBC(3, "SQLFreeStmt", retcode); return retcode; } internal ODBC32.RetCode GetData(int index, ODBC32.SQL_C sqlctype, CNativeBuffer buffer, int cb, out IntPtr cbActual) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLGetData(this, + ODBC32.RetCode retcode = Interop.Odbc.SQLGetData(this, checked((ushort)index), sqlctype, buffer, @@ -183,42 +183,42 @@ namespace System.Data.Odbc internal ODBC32.RetCode GetStatementAttribute(ODBC32.SQL_ATTR attribute, out IntPtr value, out int stringLength) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLGetStmtAttrW(this, attribute, out value, ADP.PtrSize, out stringLength); + ODBC32.RetCode retcode = Interop.Odbc.SQLGetStmtAttrW(this, attribute, out value, ADP.PtrSize, out stringLength); ODBC.TraceODBC(3, "SQLGetStmtAttrW", retcode); return retcode; } internal ODBC32.RetCode GetTypeInfo(Int16 fSqlType) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLGetTypeInfo(this, fSqlType); + ODBC32.RetCode retcode = Interop.Odbc.SQLGetTypeInfo(this, fSqlType); ODBC.TraceODBC(3, "SQLGetTypeInfo", retcode); return retcode; } internal ODBC32.RetCode MoreResults() { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLMoreResults(this); + ODBC32.RetCode retcode = Interop.Odbc.SQLMoreResults(this); ODBC.TraceODBC(3, "SQLMoreResults", retcode); return retcode; } internal ODBC32.RetCode NumberOfResultColumns(out short columnsAffected) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLNumResultCols(this, out columnsAffected); + ODBC32.RetCode retcode = Interop.Odbc.SQLNumResultCols(this, out columnsAffected); ODBC.TraceODBC(3, "SQLNumResultCols", retcode); return retcode; } internal ODBC32.RetCode Prepare(string commandText) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLPrepareW(this, commandText, ODBC32.SQL_NTS); + ODBC32.RetCode retcode = Interop.Odbc.SQLPrepareW(this, commandText, ODBC32.SQL_NTS); ODBC.TraceODBC(3, "SQLPrepareW", retcode); return retcode; } internal ODBC32.RetCode PrimaryKeys(string catalogName, string schemaName, string tableName) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLPrimaryKeysW(this, + ODBC32.RetCode retcode = Interop.Odbc.SQLPrimaryKeysW(this, catalogName, ODBC.ShortStringLength(catalogName), // CatalogName schemaName, ODBC.ShortStringLength(schemaName), // SchemaName tableName, ODBC.ShortStringLength(tableName) // TableName @@ -231,7 +231,7 @@ namespace System.Data.Odbc string procedureSchema, string procedureName) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLProceduresW(this, + ODBC32.RetCode retcode = Interop.Odbc.SQLProceduresW(this, procedureCatalog, ODBC.ShortStringLength(procedureCatalog), procedureSchema, @@ -248,7 +248,7 @@ namespace System.Data.Odbc string procedureName, string columnName) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLProcedureColumnsW(this, + ODBC32.RetCode retcode = Interop.Odbc.SQLProcedureColumnsW(this, procedureCatalog, ODBC.ShortStringLength(procedureCatalog), procedureSchema, @@ -265,7 +265,7 @@ namespace System.Data.Odbc internal ODBC32.RetCode RowCount(out SQLLEN rowCount) { IntPtr result; - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLRowCount(this, out result); + ODBC32.RetCode retcode = Interop.Odbc.SQLRowCount(this, out result); rowCount = new SQLLEN(result); ODBC.TraceODBC(3, "SQLRowCount", retcode); return retcode; @@ -273,14 +273,14 @@ namespace System.Data.Odbc internal ODBC32.RetCode SetStatementAttribute(ODBC32.SQL_ATTR attribute, IntPtr value, ODBC32.SQL_IS stringLength) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLSetStmtAttrW(this, (int)attribute, value, (int)stringLength); + ODBC32.RetCode retcode = Interop.Odbc.SQLSetStmtAttrW(this, (int)attribute, value, (int)stringLength); ODBC.TraceODBC(3, "SQLSetStmtAttrW", retcode); return retcode; } internal ODBC32.RetCode SpecialColumns(string quotedTable) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLSpecialColumnsW(this, + ODBC32.RetCode retcode = Interop.Odbc.SQLSpecialColumnsW(this, ODBC32.SQL_SPECIALCOLS.ROWVER, null, 0, null, 0, quotedTable, ODBC.ShortStringLength(quotedTable), ODBC32.SQL_SCOPE.SESSION, ODBC32.SQL_NULLABILITY.NO_NULLS); @@ -294,7 +294,7 @@ namespace System.Data.Odbc Int16 unique, Int16 accuracy) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLStatisticsW(this, + ODBC32.RetCode retcode = Interop.Odbc.SQLStatisticsW(this, tableCatalog, ODBC.ShortStringLength(tableCatalog), tableSchema, @@ -310,7 +310,7 @@ namespace System.Data.Odbc internal ODBC32.RetCode Statistics(string tableName) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLStatisticsW(this, + ODBC32.RetCode retcode = Interop.Odbc.SQLStatisticsW(this, null, 0, null, 0, tableName, ODBC.ShortStringLength(tableName), (Int16)ODBC32.SQL_INDEX.UNIQUE, @@ -324,7 +324,7 @@ namespace System.Data.Odbc string tableName, string tableType) { - ODBC32.RetCode retcode = UnsafeNativeMethods.SQLTablesW(this, + ODBC32.RetCode retcode = Interop.Odbc.SQLTablesW(this, tableCatalog, ODBC.ShortStringLength(tableCatalog), tableSchema, diff --git a/external/corefx/src/System.Data.Odbc/tests/Configurations.props b/external/corefx/src/System.Data.Odbc/tests/Configurations.props index c0146cddf3..912e1c5a3f 100644 --- a/external/corefx/src/System.Data.Odbc/tests/Configurations.props +++ b/external/corefx/src/System.Data.Odbc/tests/Configurations.props @@ -2,8 +2,9 @@ - netstandard-Unix; - netstandard-Windows_NT; + netcoreapp-Unix; + netcoreapp-Windows_NT; + netfx-Windows_NT; diff --git a/external/corefx/src/System.Data.Odbc/tests/ConnectionStrings.Unix.cs b/external/corefx/src/System.Data.Odbc/tests/ConnectionStrings.Unix.cs index b9623e8b91..ffa09cc3ae 100644 --- a/external/corefx/src/System.Data.Odbc/tests/ConnectionStrings.Unix.cs +++ b/external/corefx/src/System.Data.Odbc/tests/ConnectionStrings.Unix.cs @@ -1,3 +1,7 @@ +// 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. + namespace System.Data.Odbc.Tests { public static class ConnectionStrings diff --git a/external/corefx/src/System.Data.Odbc/tests/ConnectionStrings.Windows.cs b/external/corefx/src/System.Data.Odbc/tests/ConnectionStrings.Windows.cs index d759c70912..c50d032303 100644 --- a/external/corefx/src/System.Data.Odbc/tests/ConnectionStrings.Windows.cs +++ b/external/corefx/src/System.Data.Odbc/tests/ConnectionStrings.Windows.cs @@ -1,3 +1,7 @@ +// 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. + namespace System.Data.Odbc.Tests { public static class ConnectionStrings diff --git a/external/corefx/src/System.Data.Odbc/tests/Helpers.cs b/external/corefx/src/System.Data.Odbc/tests/Helpers.cs index f7d92db218..0ab71a1910 100644 --- a/external/corefx/src/System.Data.Odbc/tests/Helpers.cs +++ b/external/corefx/src/System.Data.Odbc/tests/Helpers.cs @@ -1,3 +1,7 @@ +// 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.Collections.Generic; using System.Linq; @@ -14,13 +18,11 @@ namespace System.Data.Odbc.Tests public static bool CheckOdbcNotAvailable() => !CheckOdbcIsAvailable(); - private static bool CheckOdbcIsAvailable() => - PlatformDetection.IsWindows ? - !PlatformDetection.IsWindowsNanoServer && (!PlatformDetection.IsWindowsServerCore || Environment.Is64BitProcess ) : - Interop.Libdl.dlopen(( - PlatformDetection.IsOSX ? - "libodbc.2.dylib" : - "libodbc.so.2" - ), Interop.Libdl.RTLD_NOW) != IntPtr.Zero; + private static bool CheckOdbcIsAvailable() => +#if TargetsWindows + !PlatformDetection.IsWindowsNanoServer && (!PlatformDetection.IsWindowsServerCore || Environment.Is64BitProcess); +#else + Interop.Libdl.dlopen(Interop.Libraries.Odbc32, Interop.Libdl.RTLD_NOW) != IntPtr.Zero; +#endif } } diff --git a/external/corefx/src/System.Data.Odbc/tests/IntegrationTestBase.cs b/external/corefx/src/System.Data.Odbc/tests/IntegrationTestBase.cs index 1a5e60d4d5..9e4e0bbf54 100644 --- a/external/corefx/src/System.Data.Odbc/tests/IntegrationTestBase.cs +++ b/external/corefx/src/System.Data.Odbc/tests/IntegrationTestBase.cs @@ -1,4 +1,8 @@ -using Xunit; +// 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 Xunit; namespace System.Data.Odbc.Tests { diff --git a/external/corefx/src/System.Data.Odbc/tests/OdbcParameterTests.cs b/external/corefx/src/System.Data.Odbc/tests/OdbcParameterTests.cs index 5aab7c759f..c1f9fbac91 100644 --- a/external/corefx/src/System.Data.Odbc/tests/OdbcParameterTests.cs +++ b/external/corefx/src/System.Data.Odbc/tests/OdbcParameterTests.cs @@ -1,3 +1,7 @@ +// 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.Data.SqlClient; using System.Text; using Xunit; diff --git a/external/corefx/src/System.Data.Odbc/tests/ReaderTests.cs b/external/corefx/src/System.Data.Odbc/tests/ReaderTests.cs index 4d067e0573..011c0b3fc0 100644 --- a/external/corefx/src/System.Data.Odbc/tests/ReaderTests.cs +++ b/external/corefx/src/System.Data.Odbc/tests/ReaderTests.cs @@ -1,4 +1,8 @@ -using Xunit; +// 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 Xunit; namespace System.Data.Odbc.Tests { diff --git a/external/corefx/src/System.Data.Odbc/tests/SmokeTest.cs b/external/corefx/src/System.Data.Odbc/tests/SmokeTest.cs index 81a85e58d9..73ab2b1930 100644 --- a/external/corefx/src/System.Data.Odbc/tests/SmokeTest.cs +++ b/external/corefx/src/System.Data.Odbc/tests/SmokeTest.cs @@ -1,4 +1,8 @@ -using Xunit; +// 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 Xunit; namespace System.Data.Odbc.Tests { diff --git a/external/corefx/src/System.Data.Odbc/tests/System.Data.Odbc.Tests.csproj b/external/corefx/src/System.Data.Odbc/tests/System.Data.Odbc.Tests.csproj index e0565935f3..287eec667c 100644 --- a/external/corefx/src/System.Data.Odbc/tests/System.Data.Odbc.Tests.csproj +++ b/external/corefx/src/System.Data.Odbc/tests/System.Data.Odbc.Tests.csproj @@ -3,11 +3,14 @@ {F3E72F35-0351-4D67-2209-725BCAD807BA} + $(DefineConstants);TargetsWindows - - - - + + + + + + @@ -15,17 +18,32 @@ + + + + + Common\Interop\Unix\Interop.Libraries.cs Common\Interop\Unix\libdl\Interop.dlopen.cs - - - + + + + Common\Interop\Linux\Interop.Libraries.cs + + + + + Common\Interop\OSX\Interop.Libraries.cs + + + Common\Interop\Windows\Interop.Libraries.cs + diff --git a/external/corefx/src/System.Data.SqlClient/System.Data.SqlClient.sln b/external/corefx/src/System.Data.SqlClient/System.Data.SqlClient.sln index 5b3290371b..6e06223605 100644 --- a/external/corefx/src/System.Data.SqlClient/System.Data.SqlClient.sln +++ b/external/corefx/src/System.Data.SqlClient/System.Data.SqlClient.sln @@ -7,6 +7,26 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Data.SqlClient.Tests {D4550556-4745-457F-BA8F-3EBF3836D6B4} = {D4550556-4745-457F-BA8F-3EBF3836D6B4} EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Address", "tests\ManualTests\SQL\UdtTest\UDTs\Address\Address.csproj", "{D1392B54-998A-4F27-BC17-4CE149117BCC}" + ProjectSection(ProjectDependencies) = postProject + {D4550556-4745-457F-BA8F-3EBF3836D6B4} = {D4550556-4745-457F-BA8F-3EBF3836D6B4} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Circle", "tests\ManualTests\SQL\UdtTest\UDTs\Circle\Circle.csproj", "{6C88F00F-9597-43AD-9E5F-9B344DA3B16F}" + ProjectSection(ProjectDependencies) = postProject + {D4550556-4745-457F-BA8F-3EBF3836D6B4} = {D4550556-4745-457F-BA8F-3EBF3836D6B4} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shapes", "tests\ManualTests\SQL\UdtTest\UDTs\Shapes\Shapes.csproj", "{B73A7063-37C3-415D-AD53-BB3DA20ABD6E}" + ProjectSection(ProjectDependencies) = postProject + {D4550556-4745-457F-BA8F-3EBF3836D6B4} = {D4550556-4745-457F-BA8F-3EBF3836D6B4} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Utf8String", "tests\ManualTests\SQL\UdtTest\UDTs\Utf8String\Utf8String.csproj", "{E0A6BB21-574B-43D9-890D-6E1144F2EE9E}" + ProjectSection(ProjectDependencies) = postProject + {D4550556-4745-457F-BA8F-3EBF3836D6B4} = {D4550556-4745-457F-BA8F-3EBF3836D6B4} + EndProjectSection +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Data.SqlClient.ManualTesting.Tests", "tests\ManualTests\System.Data.SqlClient.ManualTesting.Tests.csproj", "{45DB5F86-7AE3-45C6-870D-F9357B66BDB5}" ProjectSection(ProjectDependencies) = postProject {D4550556-4745-457F-BA8F-3EBF3836D6B4} = {D4550556-4745-457F-BA8F-3EBF3836D6B4} @@ -70,6 +90,22 @@ Global {F3E72F35-0351-4D67-9388-725BCAD807BA}.Debug|Any CPU.Build.0 = netstandard-Windows_NT-Debug|Any CPU {F3E72F35-0351-4D67-9388-725BCAD807BA}.Release|Any CPU.ActiveCfg = netstandard-Windows_NT-Release|Any CPU {F3E72F35-0351-4D67-9388-725BCAD807BA}.Release|Any CPU.Build.0 = netstandard-Windows_NT-Release|Any CPU + {D1392B54-998A-4F27-BC17-4CE149117BCC}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {D1392B54-998A-4F27-BC17-4CE149117BCC}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {D1392B54-998A-4F27-BC17-4CE149117BCC}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {D1392B54-998A-4F27-BC17-4CE149117BCC}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU @@ -116,6 +152,10 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {F3E72F35-0351-4D67-9388-725BCAD807BA} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} + {D1392B54-998A-4F27-BC17-4CE149117BCC} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} + {6C88F00F-9597-43AD-9E5F-9B344DA3B16F} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} + {B73A7063-37C3-415D-AD53-BB3DA20ABD6E} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} + {E0A6BB21-574B-43D9-890D-6E1144F2EE9E} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} {45DB5F86-7AE3-45C6-870D-F9357B66BDB5} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} {AF78BA88-6428-47EA-8682-442DAF8E9656} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} {B94B8E6D-3E41-4046-B758-4A2E281F74EE} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} diff --git a/external/corefx/src/System.Data.SqlClient/dir.props b/external/corefx/src/System.Data.SqlClient/dir.props index f763a56920..fbff37a79f 100644 --- a/external/corefx/src/System.Data.SqlClient/dir.props +++ b/external/corefx/src/System.Data.SqlClient/dir.props @@ -2,7 +2,7 @@ - 4.3.0.0 + 4.3.1.0 MSFT true diff --git a/external/corefx/src/System.Data.SqlClient/pkg/System.Data.SqlClient.pkgproj b/external/corefx/src/System.Data.SqlClient/pkg/System.Data.SqlClient.pkgproj index 1a8bf629ed..f02abb8b86 100644 --- a/external/corefx/src/System.Data.SqlClient/pkg/System.Data.SqlClient.pkgproj +++ b/external/corefx/src/System.Data.SqlClient/pkg/System.Data.SqlClient.pkgproj @@ -21,12 +21,7 @@ - - - - - - + \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/ref/System.Data.SqlClient.cs b/external/corefx/src/System.Data.SqlClient/ref/System.Data.SqlClient.cs index 731959fa3f..f84f25ce4f 100644 --- a/external/corefx/src/System.Data.SqlClient/ref/System.Data.SqlClient.cs +++ b/external/corefx/src/System.Data.SqlClient/ref/System.Data.SqlClient.cs @@ -32,6 +32,10 @@ namespace Microsoft.SqlServer.Server { + public sealed partial class InvalidUdtException : System.SystemException + { + internal InvalidUdtException() { } + } public partial class SqlDataRecord : System.Data.IDataRecord { public SqlDataRecord(params Microsoft.SqlServer.Server.SqlMetaData[] metaData) { } @@ -118,6 +122,28 @@ namespace Microsoft.SqlServer.Server public virtual void SetValue(int ordinal, object value) { } public virtual int SetValues(params object[] values) { throw null; } } + public enum DataAccessKind + { + None = 0, + Read = 1 + } + public enum SystemDataAccessKind + { + None = 0, + Read = 1 + } + [System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = false, Inherited = false), System.SerializableAttribute] + public partial class SqlFunctionAttribute : System.Attribute + { + public SqlFunctionAttribute() { } + public bool IsDeterministic { get { throw null; } set { } } + public DataAccessKind DataAccess { get { throw null; } set { } } + public SystemDataAccessKind SystemDataAccess { get { throw null; } set { } } + public bool IsPrecise { get { throw null; } set { } } + public string Name { get { throw null; } set { } } + public string TableDefinition { get { throw null; } set { } } + public string FillRowMethodName { get { throw null; } set { } } + } public sealed partial class SqlMetaData { public SqlMetaData(string name, System.Data.SqlDbType dbType) { } @@ -183,6 +209,49 @@ namespace Microsoft.SqlServer.Server public System.TimeSpan Adjust(System.TimeSpan value) { throw null; } public static Microsoft.SqlServer.Server.SqlMetaData InferFromValue(object value, string name) { throw null; } } + [System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = false, Inherited = false), System.SerializableAttribute] + public sealed partial class SqlMethodAttribute : SqlFunctionAttribute + { + public SqlMethodAttribute() { } + public bool OnNullCall { get { throw null; } set { } } + public bool IsMutator { get { throw null; } set { } } + public bool InvokeIfReceiverIsNull { get { throw null; } set { } } + } + public enum Format + { + Unknown = 0, + Native = 1, + UserDefined = 2 + } + [System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Struct, AllowMultiple = false, Inherited = false)] + public sealed partial class SqlUserDefinedAggregateAttribute : System.Attribute + { + public const int MaxByteSizeValue = 8000; + public SqlUserDefinedAggregateAttribute(Format format) { } + public int MaxByteSize { get { throw null; } set { } } + public bool IsInvariantToDuplicates { get { throw null; } set { } } + public bool IsInvariantToNulls { get { throw null; } set { } } + public bool IsInvariantToOrder { get { throw null; } set { } } + public bool IsNullIfEmpty { get { throw null; } set { } } + public Format Format { get { throw null; } } + public string Name { get { throw null; } set { } } + } + [System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Struct, AllowMultiple = false, Inherited = true)] + public sealed partial class SqlUserDefinedTypeAttribute : System.Attribute + { + public SqlUserDefinedTypeAttribute(Format format) { } + public int MaxByteSize { get { throw null; } set { } } + public bool IsFixedLength { get { throw null; } set { } } + public bool IsByteOrdered { get { throw null; } set { } } + public Format Format { get { throw null; } } + public string ValidationMethodName { get { throw null; } set { } } + public string Name { get { throw null; } set { } } + } + public interface IBinarySerialize + { + void Read(System.IO.BinaryReader r); + void Write(System.IO.BinaryWriter w); + } } namespace System.Data.Sql { @@ -333,6 +402,9 @@ namespace System.Data.SqlClient protected override System.Threading.Tasks.Task ExecuteDbDataReaderAsync(System.Data.CommandBehavior behavior, System.Threading.CancellationToken cancellationToken) { throw null; } public override int ExecuteNonQuery() { throw null; } public override System.Threading.Tasks.Task ExecuteNonQueryAsync(System.Threading.CancellationToken cancellationToken) { throw null; } + public IAsyncResult BeginExecuteNonQuery() { throw null; } + public IAsyncResult BeginExecuteNonQuery(AsyncCallback callback, object stateObject) { throw null; } + public int EndExecuteNonQuery(IAsyncResult asyncResult) { throw null; } public new System.Data.SqlClient.SqlDataReader ExecuteReader() { throw null; } public new System.Data.SqlClient.SqlDataReader ExecuteReader(System.Data.CommandBehavior behavior) { throw null; } public new System.Threading.Tasks.Task ExecuteReaderAsync() { throw null; } @@ -693,6 +765,7 @@ namespace System.Data.SqlClient public override DataRowVersion SourceVersion { get { throw null; } set { } } public System.Data.SqlDbType SqlDbType { get { throw null; } set { } } public object SqlValue { get { throw null; } set { } } + public string UdtTypeName { get { throw null; } set { } } public string TypeName { get { throw null; } set { } } public override object Value { get { throw null; } set { } } public string XmlSchemaCollectionDatabase { get { throw null; } set { } } diff --git a/external/corefx/src/System.Data.SqlClient/ref/System.Data.SqlClient.csproj b/external/corefx/src/System.Data.SqlClient/ref/System.Data.SqlClient.csproj index 18ecba930a..4388a63c5a 100644 --- a/external/corefx/src/System.Data.SqlClient/ref/System.Data.SqlClient.csproj +++ b/external/corefx/src/System.Data.SqlClient/ref/System.Data.SqlClient.csproj @@ -6,7 +6,7 @@ true - 4.3.0.0 + 4.3.1.0 diff --git a/external/corefx/src/System.Data.SqlClient/src/Microsoft/SqlServer/Server/MetadataUtilsSmi.cs b/external/corefx/src/System.Data.SqlClient/src/Microsoft/SqlServer/Server/MetadataUtilsSmi.cs index 1c7f65b1f6..139ad20865 100644 --- a/external/corefx/src/System.Data.SqlClient/src/Microsoft/SqlServer/Server/MetadataUtilsSmi.cs +++ b/external/corefx/src/System.Data.SqlClient/src/Microsoft/SqlServer/Server/MetadataUtilsSmi.cs @@ -183,8 +183,8 @@ namespace Microsoft.SqlServer.Server internal static ExtendedClrTypeCode DetermineExtendedTypeCodeForUseWithSqlDbType( SqlDbType dbType, bool isMultiValued, - object value - ) + object value, + Type udtType) { ExtendedClrTypeCode extendedCode = ExtendedClrTypeCode.Invalid; @@ -315,7 +315,16 @@ namespace Microsoft.SqlServer.Server } break; case SqlDbType.Udt: - throw ADP.DbTypeNotSupported(SqlDbType.Udt.ToString()); + // Validate UDT type if caller gave us a type to validate against + if (null == udtType || value.GetType() == udtType) + { + extendedCode = ExtendedClrTypeCode.Object; + } + else + { + extendedCode = ExtendedClrTypeCode.Invalid; + } + break; case SqlDbType.Time: if (value.GetType() == typeof(TimeSpan)) extendedCode = ExtendedClrTypeCode.TimeSpan; @@ -430,8 +439,8 @@ namespace Microsoft.SqlServer.Server source.TypeSpecificNamePart1, source.TypeSpecificNamePart2, source.TypeSpecificNamePart3, - true - ); + true, + source.Type); } return new SqlMetaData(source.Name, @@ -460,7 +469,40 @@ namespace Microsoft.SqlServer.Server } else if (SqlDbType.Udt == source.SqlDbType) { - throw ADP.DbTypeNotSupported(SqlDbType.Udt.ToString()); + // Split the input name. UdtTypeName is specified as single 3 part name. + // NOTE: ParseUdtTypeName throws if format is incorrect + string typeName = source.ServerTypeName; + if (null != typeName) + { + string[] names = SqlParameter.ParseTypeName(typeName, true /* isUdtTypeName */); + + if (1 == names.Length) + { + typeSpecificNamePart3 = names[0]; + } + else if (2 == names.Length) + { + typeSpecificNamePart2 = names[0]; + typeSpecificNamePart3 = names[1]; + } + else if (3 == names.Length) + { + typeSpecificNamePart1 = names[0]; + typeSpecificNamePart2 = names[1]; + typeSpecificNamePart3 = names[2]; + } + else + { + throw ADP.ArgumentOutOfRange(nameof(typeName)); + } + + if ((!string.IsNullOrEmpty(typeSpecificNamePart1) && TdsEnums.MAX_SERVERNAME < typeSpecificNamePart1.Length) + || (!string.IsNullOrEmpty(typeSpecificNamePart2) && TdsEnums.MAX_SERVERNAME < typeSpecificNamePart2.Length) + || (!string.IsNullOrEmpty(typeSpecificNamePart3) && TdsEnums.MAX_SERVERNAME < typeSpecificNamePart3.Length)) + { + throw ADP.ArgumentOutOfRange(nameof(typeName)); + } + } } return new SmiExtendedMetaData(source.SqlDbType, @@ -469,6 +511,7 @@ namespace Microsoft.SqlServer.Server source.Scale, source.LocaleId, source.CompareOptions, + null, source.Name, typeSpecificNamePart1, typeSpecificNamePart2, @@ -603,6 +646,7 @@ namespace Microsoft.SqlServer.Server scale, columnLocale.LCID, SmiMetaData.DefaultNVarChar.CompareOptions, + null, false, // no support for multi-valued columns in a TVP yet null, // no support for structured columns yet null, // no support for structured columns yet @@ -907,6 +951,7 @@ namespace Microsoft.SqlServer.Server scale, System.Globalization.CultureInfo.CurrentCulture.LCID, SmiMetaData.GetDefaultForType(colDbType).CompareOptions, + null, false, // no support for multi-valued columns in a TVP yet null, // no support for structured columns yet null, // no support for structured columns yet diff --git a/external/corefx/src/System.Data.SqlClient/src/Microsoft/SqlServer/Server/SmiMetaData.cs b/external/corefx/src/System.Data.SqlClient/src/Microsoft/SqlServer/Server/SmiMetaData.cs index f5d7593819..d06d2bc22a 100644 --- a/external/corefx/src/System.Data.SqlClient/src/Microsoft/SqlServer/Server/SmiMetaData.cs +++ b/external/corefx/src/System.Data.SqlClient/src/Microsoft/SqlServer/Server/SmiMetaData.cs @@ -49,14 +49,12 @@ namespace Microsoft.SqlServer.Server private byte _scale; // Varies for SqlDbType.Decimal, others are fixed value per type private long _localeId; // Valid only for character types, others are 0 private SqlCompareOptions _compareOptions; // Valid only for character types, others are SqlCompareOptions.Default + private Type _clrType; // Varies for SqlDbType.Udt, others are fixed value per type. + private string _udtAssemblyQualifiedName; // Valid only for UDT types when _clrType is not available private bool _isMultiValued; // Multiple instances per value? (I.e. tables, arrays) private IList _fieldMetaData; // Metadata of fields for structured types private SmiMetaDataPropertyCollection _extendedProperties; // Extended properties, Key columns, sort order, etc. - // DevNote: For now, since the list of extended property types is small, we can handle them in a simple list. - // In the future, we may need to create a more performant storage & lookup mechanism, such as a hash table - // of lists indexed by type of property or an array of lists with a well-known index for each type. - // Limits for attributes (SmiMetaData will assert that these limits as applicable in constructor) internal const long UnlimitedMaxLengthIndicator = -1; // unlimited (except by implementation) max-length. internal const long MaxUnicodeCharacters = 4000; // Maximum for limited type @@ -73,8 +71,7 @@ namespace Microsoft.SqlServer.Server | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth; internal const long MaxNameLength = 128; // maximum length in the server is 128. - private static readonly IList s_emptyFieldList = new SmiExtendedMetaData[0]; - + private static readonly IList s_emptyFieldList = new List().AsReadOnly(); // Precision to max length lookup table private static byte[] s_maxLenFromPrecision = new byte[] {5,5,5,5,5,5,5,5,5,9,9,9,9,9, @@ -84,7 +81,6 @@ namespace Microsoft.SqlServer.Server private static byte[] s_maxVarTimeLenOffsetFromScale = new byte[] { 2, 2, 2, 1, 1, 0, 0, 0 }; // Defaults - // SmiMetaData(SqlDbType, MaxLen, Prec, Scale, CompareOptions) internal static readonly SmiMetaData DefaultBigInt = new SmiMetaData(SqlDbType.BigInt, 8, 19, 0, SqlCompareOptions.None); // SqlDbType.BigInt internal static readonly SmiMetaData DefaultBinary = new SmiMetaData(SqlDbType.Binary, 1, 0, 0, SqlCompareOptions.None); // SqlDbType.Binary internal static readonly SmiMetaData DefaultBit = new SmiMetaData(SqlDbType.Bit, 1, 1, 0, SqlCompareOptions.None); // SqlDbType.Bit @@ -110,6 +106,7 @@ namespace Microsoft.SqlServer.Server internal static readonly SmiMetaData DefaultVarChar_NoCollation = new SmiMetaData(SqlDbType.VarChar, MaxANSICharacters, 0, 0, DefaultStringCompareOptions);// SqlDbType.VarChar internal static readonly SmiMetaData DefaultVariant = new SmiMetaData(SqlDbType.Variant, 8016, 0, 0, SqlCompareOptions.None); // SqlDbType.Variant internal static readonly SmiMetaData DefaultXml = new SmiMetaData(SqlDbType.Xml, UnlimitedMaxLengthIndicator, 0, 0, DefaultStringCompareOptions);// SqlDbType.Xml + internal static readonly SmiMetaData DefaultUdt_NoType = new SmiMetaData(SqlDbType.Udt, 0, 0, 0, SqlCompareOptions.None); // SqlDbType.Udt internal static readonly SmiMetaData DefaultStructured = new SmiMetaData(SqlDbType.Structured, 0, 0, 0, SqlCompareOptions.None); // SqlDbType.Structured internal static readonly SmiMetaData DefaultDate = new SmiMetaData(SqlDbType.Date, 3, 10, 0, SqlCompareOptions.None); // SqlDbType.Date internal static readonly SmiMetaData DefaultTime = new SmiMetaData(SqlDbType.Time, 5, 0, 7, SqlCompareOptions.None); // SqlDbType.Time @@ -117,50 +114,167 @@ namespace Microsoft.SqlServer.Server internal static readonly SmiMetaData DefaultDateTimeOffset = new SmiMetaData(SqlDbType.DateTimeOffset, 10, 0, 7, SqlCompareOptions.None); // SqlDbType.DateTimeOffset // No default for generic UDT + // character defaults hook thread-local culture to get collation + internal static SmiMetaData DefaultChar + => new SmiMetaData( + DefaultChar_NoCollation.SqlDbType, + DefaultChar_NoCollation.MaxLength, + DefaultChar_NoCollation.Precision, + DefaultChar_NoCollation.Scale, + CultureInfo.CurrentCulture.LCID, + SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth, + null); + + internal static SmiMetaData DefaultNChar + => new SmiMetaData( + DefaultNChar_NoCollation.SqlDbType, + DefaultNChar_NoCollation.MaxLength, + DefaultNChar_NoCollation.Precision, + DefaultNChar_NoCollation.Scale, + CultureInfo.CurrentCulture.LCID, + SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth, + null); + + internal static SmiMetaData DefaultNText + => new SmiMetaData( + DefaultNText_NoCollation.SqlDbType, + DefaultNText_NoCollation.MaxLength, + DefaultNText_NoCollation.Precision, + DefaultNText_NoCollation.Scale, + CultureInfo.CurrentCulture.LCID, + SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth, + null); + internal static SmiMetaData DefaultNVarChar + => new SmiMetaData( + DefaultNVarChar_NoCollation.SqlDbType, + DefaultNVarChar_NoCollation.MaxLength, + DefaultNVarChar_NoCollation.Precision, + DefaultNVarChar_NoCollation.Scale, + CultureInfo.CurrentCulture.LCID, + SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth, + null); + + internal static SmiMetaData DefaultText + => new SmiMetaData( + DefaultText_NoCollation.SqlDbType, + DefaultText_NoCollation.MaxLength, + DefaultText_NoCollation.Precision, + DefaultText_NoCollation.Scale, + CultureInfo.CurrentCulture.LCID, + SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth, + null); + + internal static SmiMetaData DefaultVarChar + => new SmiMetaData( + DefaultVarChar_NoCollation.SqlDbType, + DefaultVarChar_NoCollation.MaxLength, + DefaultVarChar_NoCollation.Precision, + DefaultVarChar_NoCollation.Scale, + CultureInfo.CurrentCulture.LCID, + SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth, + null); + + // The one and only constructor for use by outside code. + // + // Parameters that matter for given values of dbType (other parameters are ignored in favor of internal defaults). + // Thus, if dbType parameter value is SqlDbType.Decimal, the values of precision and scale passed in are used, but + // maxLength, localeId, compareOptions, etc are set to defaults for the Decimal type: + // SqlDbType.BigInt: dbType + // SqlDbType.Binary: dbType, maxLength + // SqlDbType.Bit: dbType + // SqlDbType.Char: dbType, maxLength, localeId, compareOptions + // SqlDbType.DateTime: dbType + // SqlDbType.Decimal: dbType, precision, scale + // SqlDbType.Float: dbType + // SqlDbType.Image: dbType + // SqlDbType.Int: dbType + // SqlDbType.Money: dbType + // SqlDbType.NChar: dbType, maxLength, localeId, compareOptions + // SqlDbType.NText: dbType, localeId, compareOptions + // SqlDbType.NVarChar: dbType, maxLength, localeId, compareOptions + // SqlDbType.Real: dbType + // SqlDbType.UniqueIdentifier: dbType + // SqlDbType.SmallDateTime: dbType + // SqlDbType.SmallInt: dbType + // SqlDbType.SmallMoney: dbType + // SqlDbType.Text: dbType, localeId, compareOptions + // SqlDbType.Timestamp: dbType + // SqlDbType.TinyInt: dbType + // SqlDbType.VarBinary: dbType, maxLength + // SqlDbType.VarChar: dbType, maxLength, localeId, compareOptions + // SqlDbType.Variant: dbType + // PlaceHolder for value 24 + // SqlDbType.Xml: dbType + // Placeholder for value 26 + // Placeholder for value 27 + // Placeholder for value 28 + // SqlDbType.Udt: dbType, userDefinedType + // + + // SMI V100 (aka V3) constructor. Superceded in V200. + internal SmiMetaData( + SqlDbType dbType, + long maxLength, + byte precision, + byte scale, + long localeId, + SqlCompareOptions compareOptions, + Type userDefinedType) + : this( + dbType, + maxLength, + precision, + scale, + localeId, + compareOptions, + userDefinedType, + false, + null, + null) { - get - { - return new SmiMetaData( - DefaultNVarChar_NoCollation.SqlDbType, - DefaultNVarChar_NoCollation.MaxLength, - DefaultNVarChar_NoCollation.Precision, - DefaultNVarChar_NoCollation.Scale, - CultureInfo.CurrentCulture.LCID, - SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth); - } } + // SMI V200 ctor. internal SmiMetaData( - SqlDbType dbType, - long maxLength, - byte precision, - byte scale, - long localeId, - SqlCompareOptions compareOptions) : - this(dbType, - maxLength, - precision, - scale, - localeId, - compareOptions, - false, - null, - null) + SqlDbType dbType, + long maxLength, + byte precision, + byte scale, + long localeId, + SqlCompareOptions compareOptions, + Type userDefinedType, + bool isMultiValued, + IList fieldTypes, + SmiMetaDataPropertyCollection extendedProperties) + : this( + dbType, + maxLength, + precision, + scale, + localeId, + compareOptions, + userDefinedType, + null, + isMultiValued, + fieldTypes, + extendedProperties) { } // SMI V220 ctor. internal SmiMetaData( - SqlDbType dbType, - long maxLength, - byte precision, - byte scale, - long localeId, - SqlCompareOptions compareOptions, - bool isMultiValued, - IList fieldTypes, - SmiMetaDataPropertyCollection extendedProperties) + SqlDbType dbType, + long maxLength, + byte precision, + byte scale, + long localeId, + SqlCompareOptions compareOptions, + Type userDefinedType, + string udtAssemblyQualifiedName, + bool isMultiValued, + IList fieldTypes, + SmiMetaDataPropertyCollection extendedProperties) { Debug.Assert(IsSupportedDbType(dbType), "Invalid SqlDbType: " + dbType); @@ -213,11 +327,26 @@ namespace Microsoft.SqlServer.Server _maxLength = s_maxLenFromPrecision[precision - 1]; break; case SqlDbType.Udt: - throw System.Data.Common.ADP.DbTypeNotSupported(SqlDbType.Udt.ToString()); + // For SqlParameter, both userDefinedType and udtAssemblyQualifiedName can be NULL, + // so we are checking only maxLength if it will be used (i.e. userDefinedType is NULL) + Debug.Assert((null != userDefinedType) || (0 <= maxLength || UnlimitedMaxLengthIndicator == maxLength), + string.Format(null, "SmiMetaData.ctor: Udt name={0}, maxLength={1}", udtAssemblyQualifiedName, maxLength)); + // Type not validated until matched to a server. Could be null if extended metadata supplies three-part name! + _clrType = userDefinedType; + if (null != userDefinedType) + { + _maxLength = SerializationHelperSql9.GetUdtMaxLength(userDefinedType); + } + else + { + _maxLength = maxLength; + } + _udtAssemblyQualifiedName = udtAssemblyQualifiedName; + break; case SqlDbType.Structured: if (null != fieldTypes) { - _fieldMetaData = new System.Collections.ObjectModel.ReadOnlyCollection(fieldTypes); + _fieldMetaData = (new List(fieldTypes)).AsReadOnly(); } _isMultiValued = isMultiValued; _maxLength = _fieldMetaData.Count; @@ -261,84 +390,152 @@ namespace Microsoft.SqlServer.Server #endif } - // Sql-style compare options for character types. - internal SqlCompareOptions CompareOptions + internal bool IsValidMaxLengthForCtorGivenType(SqlDbType dbType, long maxLength) { - get + bool result = true; + switch (dbType) { - return _compareOptions; + case SqlDbType.BigInt: + case SqlDbType.Bit: + case SqlDbType.DateTime: + case SqlDbType.Float: + case SqlDbType.Image: + case SqlDbType.Int: + case SqlDbType.Money: + case SqlDbType.Real: + case SqlDbType.SmallDateTime: + case SqlDbType.SmallInt: + case SqlDbType.SmallMoney: + case SqlDbType.Timestamp: + case SqlDbType.TinyInt: + case SqlDbType.UniqueIdentifier: + case SqlDbType.Variant: + case SqlDbType.Xml: + case SqlDbType.NText: + case SqlDbType.Text: + case SqlDbType.Decimal: + case SqlDbType.Udt: + case SqlDbType.Structured: + case SqlDbType.Date: + case SqlDbType.Time: + case SqlDbType.DateTime2: + case SqlDbType.DateTimeOffset: + break; + case SqlDbType.Binary: + result = 0 < maxLength && MaxBinaryLength >= maxLength; + break; + case SqlDbType.VarBinary: + result = UnlimitedMaxLengthIndicator == maxLength || (0 < maxLength && MaxBinaryLength >= maxLength); + break; + case SqlDbType.Char: + result = 0 < maxLength && MaxANSICharacters >= maxLength; + break; + case SqlDbType.NChar: + result = 0 < maxLength && MaxUnicodeCharacters >= maxLength; + break; + case SqlDbType.NVarChar: + result = UnlimitedMaxLengthIndicator == maxLength || (0 < maxLength && MaxUnicodeCharacters >= maxLength); + break; + case SqlDbType.VarChar: + result = UnlimitedMaxLengthIndicator == maxLength || (0 < maxLength && MaxANSICharacters >= maxLength); + break; + default: + Debug.Fail("How in the world did we get here? :" + dbType); + break; } + + return result; } + // Sql-style compare options for character types. + internal SqlCompareOptions CompareOptions => _compareOptions; + // LCID for type. 0 for non-character types. - internal long LocaleId - { - get - { - return _localeId; - } - } + internal long LocaleId => _localeId; // Units of length depend on type. // NVarChar, NChar, NText: # of Unicode characters // Everything else: # of bytes - internal long MaxLength + internal long MaxLength => _maxLength; + + internal byte Precision => _precision; + + internal byte Scale => _scale; + + internal SqlDbType SqlDbType => _databaseType; + + // Clr Type instance for user-defined types + internal Type Type { get { - return _maxLength; + // Fault-in UDT clr types on access if have assembly-qualified name + if (null == _clrType && SqlDbType.Udt == _databaseType && _udtAssemblyQualifiedName != null) + { + _clrType = Type.GetType(_udtAssemblyQualifiedName, true); + } + return _clrType; } } - internal byte Precision + // Clr Type instance for user-defined types in cases where we don't want to throw if the assembly isn't available + internal Type TypeWithoutThrowing { get { - return _precision; + // Fault-in UDT clr types on access if have assembly-qualified name + if (null == _clrType && SqlDbType.Udt == _databaseType && _udtAssemblyQualifiedName != null) + { + _clrType = Type.GetType(_udtAssemblyQualifiedName, false); + } + return _clrType; } } - internal byte Scale + internal string TypeName { get { - return _scale; + string result = null; + if (SqlDbType.Udt == _databaseType) + { + Debug.Assert(string.Empty == s_typeNameByDatabaseType[(int)_databaseType], "unexpected udt?"); + result = Type.FullName; + } + else + { + result = s_typeNameByDatabaseType[(int)_databaseType]; + Debug.Assert(null != result, "unknown type name?"); + } + return result; } } - internal SqlDbType SqlDbType + internal string AssemblyQualifiedName { get { - return _databaseType; + string result = null; + if (SqlDbType.Udt == _databaseType) + { + // Fault-in assembly-qualified name if type is available + if (_udtAssemblyQualifiedName == null && _clrType != null) + { + _udtAssemblyQualifiedName = _clrType.AssemblyQualifiedName; + } + result = _udtAssemblyQualifiedName; + } + return result; } } - internal bool IsMultiValued - { - get - { - return _isMultiValued; - } - } + internal bool IsMultiValued => _isMultiValued; // Returns read-only list of field metadata - internal IList FieldMetaData - { - get - { - return _fieldMetaData; - } - } + internal IList FieldMetaData => _fieldMetaData; // Returns read-only list of extended properties - internal SmiMetaDataPropertyCollection ExtendedProperties - { - get - { - return _extendedProperties; - } - } + internal SmiMetaDataPropertyCollection ExtendedProperties => _extendedProperties; internal static bool IsSupportedDbType(SqlDbType dbType) { @@ -351,7 +548,6 @@ namespace Microsoft.SqlServer.Server internal static SmiMetaData GetDefaultForType(SqlDbType dbType) { Debug.Assert(IsSupportedDbType(dbType), "Unsupported SqlDbtype: " + dbType); - Debug.Assert(dbType != SqlDbType.Udt, "UDT not supported"); return s_defaultValues[(int)dbType]; } @@ -373,6 +569,7 @@ namespace Microsoft.SqlServer.Server // defaults are the same for all types for the following attributes. _localeId = 0; + _clrType = null; _isMultiValued = false; _fieldMetaData = s_emptyFieldList; _extendedProperties = SmiMetaDataPropertyCollection.EmptyInstance; @@ -412,7 +609,7 @@ namespace Microsoft.SqlServer.Server DefaultNVarChar_NoCollation, // Placeholder for value 26 DefaultNVarChar_NoCollation, // Placeholder for value 27 DefaultNVarChar_NoCollation, // Placeholder for value 28 - null, // Generic Udt (not supported) + DefaultUdt_NoType, // Generic Udt DefaultStructured, // Generic structured type DefaultDate, // SqlDbType.Date DefaultTime, // SqlDbType.Time @@ -420,6 +617,48 @@ namespace Microsoft.SqlServer.Server DefaultDateTimeOffset, // SqlDbType.DateTimeOffset }; + // static array of type names ordered by corresponding SqlDbType. + // NOTE: INDEXED BY SqlDbType ENUM! MUST UPDATE THIS ARRAY WHEN UPDATING SqlDbType! + // ONLY ACCESS THIS GLOBAL FROM get_TypeName! + private static string[] s_typeNameByDatabaseType = + { + "bigint", // SqlDbType.BigInt + "binary", // SqlDbType.Binary + "bit", // SqlDbType.Bit + "char", // SqlDbType.Char + "datetime", // SqlDbType.DateTime + "decimal", // SqlDbType.Decimal + "float", // SqlDbType.Float + "image", // SqlDbType.Image + "int", // SqlDbType.Int + "money", // SqlDbType.Money + "nchar", // SqlDbType.NChar + "ntext", // SqlDbType.NText + "nvarchar", // SqlDbType.NVarChar + "real", // SqlDbType.Real + "uniqueidentifier", // SqlDbType.UniqueIdentifier + "smalldatetime", // SqlDbType.SmallDateTime + "smallint", // SqlDbType.SmallInt + "smallmoney", // SqlDbType.SmallMoney + "text", // SqlDbType.Text + "timestamp", // SqlDbType.Timestamp + "tinyint", // SqlDbType.TinyInt + "varbinary", // SqlDbType.VarBinary + "varchar", // SqlDbType.VarChar + "sql_variant", // SqlDbType.Variant + null, // placeholder for 24 + "xml", // SqlDbType.Xml + null, // placeholder for 26 + null, // placeholder for 27 + null, // placeholder for 28 + string.Empty, // SqlDbType.Udt -- get type name from Type.FullName instead. + string.Empty, // Structured types have user-defined type names. + "date", // SqlDbType.Date + "time", // SqlDbType.Time + "datetime2", // SqlDbType.DateTime2 + "datetimeoffset", // SqlDbType.DateTimeOffset + }; + // Internal setter to be used by constructors only! Modifies state! private void SetDefaultsForType(SqlDbType dbType) { @@ -430,6 +669,7 @@ namespace Microsoft.SqlServer.Server _scale = smdDflt.Scale; _localeId = smdDflt.LocaleId; _compareOptions = smdDflt.CompareOptions; + _clrType = null; _isMultiValued = smdDflt._isMultiValued; _fieldMetaData = smdDflt._fieldMetaData; // This is ok due to immutability _extendedProperties = smdDflt._extendedProperties; // This is ok due to immutability @@ -442,7 +682,6 @@ namespace Microsoft.SqlServer.Server // internal class SmiExtendedMetaData : SmiMetaData { - private string _name; // context-dependent identifier, i.e. parameter name for parameters, column name for columns, etc. // three-part name for typed xml schema and for udt names @@ -457,17 +696,19 @@ namespace Microsoft.SqlServer.Server byte scale, long localeId, SqlCompareOptions compareOptions, + Type userDefinedType, string name, string typeSpecificNamePart1, string typeSpecificNamePart2, - string typeSpecificNamePart3) : - this( + string typeSpecificNamePart3) + : this( dbType, maxLength, precision, scale, localeId, compareOptions, + userDefinedType, false, null, null, @@ -478,6 +719,41 @@ namespace Microsoft.SqlServer.Server { } + // SMI V200 ctor. + internal SmiExtendedMetaData( + SqlDbType dbType, + long maxLength, + byte precision, + byte scale, + long localeId, + SqlCompareOptions compareOptions, + Type userDefinedType, + bool isMultiValued, + IList fieldMetaData, + SmiMetaDataPropertyCollection extendedProperties, + string name, + string typeSpecificNamePart1, + string typeSpecificNamePart2, + string typeSpecificNamePart3) + : this( + dbType, + maxLength, + precision, + scale, + localeId, + compareOptions, + userDefinedType, + null, + isMultiValued, + fieldMetaData, + extendedProperties, + name, + typeSpecificNamePart1, + typeSpecificNamePart2, + typeSpecificNamePart3) + { + } + // SMI V220 ctor. internal SmiExtendedMetaData( SqlDbType dbType, @@ -486,19 +762,24 @@ namespace Microsoft.SqlServer.Server byte scale, long localeId, SqlCompareOptions compareOptions, + Type userDefinedType, + string udtAssemblyQualifiedName, bool isMultiValued, IList fieldMetaData, SmiMetaDataPropertyCollection extendedProperties, string name, string typeSpecificNamePart1, string typeSpecificNamePart2, - string typeSpecificNamePart3) : - base(dbType, + string typeSpecificNamePart3) + : base( + dbType, maxLength, precision, scale, localeId, compareOptions, + userDefinedType, + udtAssemblyQualifiedName, isMultiValued, fieldMetaData, extendedProperties) @@ -511,37 +792,13 @@ namespace Microsoft.SqlServer.Server _typeSpecificNamePart3 = typeSpecificNamePart3; } - internal string Name - { - get - { - return _name; - } - } + internal string Name => _name; - internal string TypeSpecificNamePart1 - { - get - { - return _typeSpecificNamePart1; - } - } + internal string TypeSpecificNamePart1 => _typeSpecificNamePart1; - internal string TypeSpecificNamePart2 - { - get - { - return _typeSpecificNamePart2; - } - } + internal string TypeSpecificNamePart2 => _typeSpecificNamePart2; - internal string TypeSpecificNamePart3 - { - get - { - return _typeSpecificNamePart3; - } - } + internal string TypeSpecificNamePart3 => _typeSpecificNamePart3; } // SmiParameterMetaData @@ -552,6 +809,43 @@ namespace Microsoft.SqlServer.Server { private ParameterDirection _direction; + // SMI V200 ctor. + internal SmiParameterMetaData( + SqlDbType dbType, + long maxLength, + byte precision, + byte scale, + long localeId, + SqlCompareOptions compareOptions, + Type userDefinedType, + bool isMultiValued, + IList fieldMetaData, + SmiMetaDataPropertyCollection extendedProperties, + string name, + string typeSpecificNamePart1, + string typeSpecificNamePart2, + string typeSpecificNamePart3, + ParameterDirection direction) + : this( + dbType, + maxLength, + precision, + scale, + localeId, + compareOptions, + userDefinedType, + null, + isMultiValued, + fieldMetaData, + extendedProperties, + name, + typeSpecificNamePart1, + typeSpecificNamePart2, + typeSpecificNamePart3, + direction) + { + } + // SMI V220 ctor. internal SmiParameterMetaData( SqlDbType dbType, @@ -560,6 +854,8 @@ namespace Microsoft.SqlServer.Server byte scale, long localeId, SqlCompareOptions compareOptions, + Type userDefinedType, + string udtAssemblyQualifiedName, bool isMultiValued, IList fieldMetaData, SmiMetaDataPropertyCollection extendedProperties, @@ -567,13 +863,16 @@ namespace Microsoft.SqlServer.Server string typeSpecificNamePart1, string typeSpecificNamePart2, string typeSpecificNamePart3, - ParameterDirection direction) : - base(dbType, + ParameterDirection direction) + : base( + dbType, maxLength, precision, scale, localeId, compareOptions, + userDefinedType, + udtAssemblyQualifiedName, isMultiValued, fieldMetaData, extendedProperties, @@ -589,13 +888,7 @@ namespace Microsoft.SqlServer.Server _direction = direction; } - internal ParameterDirection Direction - { - get - { - return _direction; - } - } + internal ParameterDirection Direction => _direction; } // SmiStorageMetaData @@ -620,6 +913,104 @@ namespace Microsoft.SqlServer.Server private bool _isIdentity; // Is this from an identity column private bool _isColumnSet; // Is this column the XML representation of a columnset? + internal SmiStorageMetaData( + SqlDbType dbType, + long maxLength, + byte precision, + byte scale, + long localeId, + SqlCompareOptions compareOptions, + Type userDefinedType, + string name, + string typeSpecificNamePart1, + string typeSpecificNamePart2, + string typeSpecificNamePart3, + bool allowsDBNull, + string serverName, + string catalogName, + string schemaName, + string tableName, + string columnName, + SqlBoolean isKey, + bool isIdentity) + : this( + dbType, + maxLength, + precision, + scale, + localeId, + compareOptions, + userDefinedType, + false, + null, + null, + name, + typeSpecificNamePart1, + typeSpecificNamePart2, + typeSpecificNamePart3, + allowsDBNull, + serverName, + catalogName, + schemaName, + tableName, + columnName, + isKey, + isIdentity) + { + } + + // SMI V200 ctor. + internal SmiStorageMetaData( + SqlDbType dbType, + long maxLength, + byte precision, + byte scale, + long localeId, + SqlCompareOptions compareOptions, + Type userDefinedType, + bool isMultiValued, + IList fieldMetaData, + SmiMetaDataPropertyCollection extendedProperties, + string name, + string typeSpecificNamePart1, + string typeSpecificNamePart2, + string typeSpecificNamePart3, + bool allowsDBNull, + string serverName, + string catalogName, + string schemaName, + string tableName, + string columnName, + SqlBoolean isKey, + bool isIdentity) + : this( + dbType, + maxLength, + precision, + scale, + localeId, + compareOptions, + userDefinedType, + null, + isMultiValued, + fieldMetaData, + extendedProperties, + name, + typeSpecificNamePart1, + typeSpecificNamePart2, + typeSpecificNamePart3, + allowsDBNull, + serverName, + catalogName, + schemaName, + tableName, + columnName, + isKey, + isIdentity, + false) + { + } + // SMI V220 ctor. internal SmiStorageMetaData( SqlDbType dbType, @@ -628,6 +1019,8 @@ namespace Microsoft.SqlServer.Server byte scale, long localeId, SqlCompareOptions compareOptions, + Type userDefinedType, + string udtAssemblyQualifiedName, bool isMultiValued, IList fieldMetaData, SmiMetaDataPropertyCollection extendedProperties, @@ -643,13 +1036,16 @@ namespace Microsoft.SqlServer.Server string columnName, SqlBoolean isKey, bool isIdentity, - bool isColumnSet) : - base(dbType, + bool isColumnSet) + : base( + dbType, maxLength, precision, scale, localeId, compareOptions, + userDefinedType, + udtAssemblyQualifiedName, isMultiValued, fieldMetaData, extendedProperties, @@ -669,13 +1065,23 @@ namespace Microsoft.SqlServer.Server _isColumnSet = isColumnSet; } - internal SqlBoolean IsKey - { - get - { - return _isKey; - } - } + internal bool AllowsDBNull => _allowsDBNull; + + internal string ServerName => _serverName; + + internal string CatalogName => _catalogName; + + internal string SchemaName => _schemaName; + + internal string TableName => _tableName; + + internal string ColumnName => _columnName; + + internal SqlBoolean IsKey => _isKey; + + internal bool IsIdentity => _isIdentity; + + internal bool IsColumnSet => _isColumnSet; } // SmiQueryMetaData @@ -690,6 +1096,60 @@ namespace Microsoft.SqlServer.Server private SqlBoolean _isAliased; private SqlBoolean _isHidden; + internal SmiQueryMetaData( + SqlDbType dbType, + long maxLength, + byte precision, + byte scale, + long localeId, + SqlCompareOptions compareOptions, + Type userDefinedType, + string name, + string typeSpecificNamePart1, + string typeSpecificNamePart2, + string typeSpecificNamePart3, + bool allowsDBNull, + string serverName, + string catalogName, + string schemaName, + string tableName, + string columnName, + SqlBoolean isKey, + bool isIdentity, + bool isReadOnly, + SqlBoolean isExpression, + SqlBoolean isAliased, + SqlBoolean isHidden) + : this( + dbType, + maxLength, + precision, + scale, + localeId, + compareOptions, + userDefinedType, + false, + null, + null, + name, + typeSpecificNamePart1, + typeSpecificNamePart2, + typeSpecificNamePart3, + allowsDBNull, + serverName, + catalogName, + schemaName, + tableName, + columnName, + isKey, + isIdentity, + isReadOnly, + isExpression, + isAliased, + isHidden) + { + } + // SMI V200 ctor. internal SmiQueryMetaData( SqlDbType dbType, @@ -698,6 +1158,7 @@ namespace Microsoft.SqlServer.Server byte scale, long localeId, SqlCompareOptions compareOptions, + Type userDefinedType, bool isMultiValued, IList fieldMetaData, SmiMetaDataPropertyCollection extendedProperties, @@ -716,13 +1177,16 @@ namespace Microsoft.SqlServer.Server bool isReadOnly, SqlBoolean isExpression, SqlBoolean isAliased, - SqlBoolean isHidden) : - this(dbType, + SqlBoolean isHidden) + : this( + dbType, maxLength, precision, scale, localeId, compareOptions, + userDefinedType, + null, isMultiValued, fieldMetaData, extendedProperties, @@ -754,6 +1218,8 @@ namespace Microsoft.SqlServer.Server byte scale, long localeId, SqlCompareOptions compareOptions, + Type userDefinedType, + string udtAssemblyQualifiedName, bool isMultiValued, IList fieldMetaData, SmiMetaDataPropertyCollection extendedProperties, @@ -773,13 +1239,16 @@ namespace Microsoft.SqlServer.Server bool isReadOnly, SqlBoolean isExpression, SqlBoolean isAliased, - SqlBoolean isHidden) : - base(dbType, + SqlBoolean isHidden) + : base( + dbType, maxLength, precision, scale, localeId, compareOptions, + userDefinedType, + udtAssemblyQualifiedName, isMultiValued, fieldMetaData, extendedProperties, @@ -802,5 +1271,13 @@ namespace Microsoft.SqlServer.Server _isAliased = isAliased; _isHidden = isHidden; } + + internal bool IsReadOnly => _isReadOnly; + + internal SqlBoolean IsExpression => _isExpression; + + internal SqlBoolean IsAliased => _isAliased; + + internal SqlBoolean IsHidden => _isHidden; } -} +} \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/src/Microsoft/SqlServer/Server/SqlDataRecord.cs b/external/corefx/src/System.Data.SqlClient/src/Microsoft/SqlServer/Server/SqlDataRecord.cs index 4b128f2add..d295e65d6d 100644 --- a/external/corefx/src/System.Data.SqlClient/src/Microsoft/SqlServer/Server/SqlDataRecord.cs +++ b/external/corefx/src/System.Data.SqlClient/src/Microsoft/SqlServer/Server/SqlDataRecord.cs @@ -31,8 +31,8 @@ namespace Microsoft.SqlServer.Server SmiMetaData.DefaultNVarChar_NoCollation.Precision, SmiMetaData.DefaultNVarChar_NoCollation.Scale, SmiMetaData.DefaultNVarChar.LocaleId, - SmiMetaData.DefaultNVarChar.CompareOptions - ); + SmiMetaData.DefaultNVarChar.CompareOptions, + null); public virtual int FieldCount { @@ -55,8 +55,7 @@ namespace Microsoft.SqlServer.Server SqlMetaData metaData = GetSqlMetaData(ordinal); if (SqlDbType.Udt == metaData.SqlDbType) { - Debug.Assert(false, "Udt is not supported"); - return null; + return metaData.UdtTypeName; } else { @@ -408,8 +407,7 @@ namespace Microsoft.SqlServer.Server { SqlMetaData metaData = GetSqlMetaData(i); typeCodes[i] = MetaDataUtilsSmi.DetermineExtendedTypeCodeForUseWithSqlDbType( - metaData.SqlDbType, false /* isMultiValued */, values[i] - ); + metaData.SqlDbType, false /* isMultiValued */, values[i], metaData.Type); if (ExtendedClrTypeCode.Invalid == typeCodes[i]) { throw ADP.InvalidCast(); @@ -431,8 +429,7 @@ namespace Microsoft.SqlServer.Server EnsureSubclassOverride(); SqlMetaData metaData = GetSqlMetaData(ordinal); ExtendedClrTypeCode typeCode = MetaDataUtilsSmi.DetermineExtendedTypeCodeForUseWithSqlDbType( - metaData.SqlDbType, false /* isMultiValued */, value - ); + metaData.SqlDbType, false /* isMultiValued */, value, metaData.Type); if (ExtendedClrTypeCode.Invalid == typeCode) { throw ADP.InvalidCast(); diff --git a/external/corefx/src/System.Data.SqlClient/src/Microsoft/SqlServer/Server/SqlRecordBuffer.cs b/external/corefx/src/System.Data.SqlClient/src/Microsoft/SqlServer/Server/SqlRecordBuffer.cs index b977d652d6..0cd7f15812 100644 --- a/external/corefx/src/System.Data.SqlClient/src/Microsoft/SqlServer/Server/SqlRecordBuffer.cs +++ b/external/corefx/src/System.Data.SqlClient/src/Microsoft/SqlServer/Server/SqlRecordBuffer.cs @@ -446,7 +446,7 @@ namespace Microsoft.SqlServer.Server case StorageType.Int64: return _metadata ?? SmiMetaData.DefaultBigInt; case StorageType.Single: return SmiMetaData.DefaultReal; case StorageType.String: return _metadata ?? SmiMetaData.DefaultNVarChar; - case StorageType.SqlDecimal: return new SmiMetaData(SqlDbType.Decimal, 17, ((SqlDecimal)_object).Precision, ((SqlDecimal)_object).Scale, 0, SqlCompareOptions.None); + case StorageType.SqlDecimal: return new SmiMetaData(SqlDbType.Decimal, 17, ((SqlDecimal)_object).Precision, ((SqlDecimal)_object).Scale, 0, SqlCompareOptions.None, null); case StorageType.TimeSpan: return SmiMetaData.DefaultTime; } return null; diff --git a/external/corefx/src/System.Data.SqlClient/src/Microsoft/SqlServer/Server/ValueUtilsSmi.cs.REMOVED.git-id b/external/corefx/src/System.Data.SqlClient/src/Microsoft/SqlServer/Server/ValueUtilsSmi.cs.REMOVED.git-id index 7a932cd89c..f3da010ae4 100644 --- a/external/corefx/src/System.Data.SqlClient/src/Microsoft/SqlServer/Server/ValueUtilsSmi.cs.REMOVED.git-id +++ b/external/corefx/src/System.Data.SqlClient/src/Microsoft/SqlServer/Server/ValueUtilsSmi.cs.REMOVED.git-id @@ -1 +1 @@ -02610b6a63ac39998eb7438555538320b2eb7cff \ No newline at end of file +f4c8b4f1d1901f995102985e2a069ea119843996 \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/src/Resources/Strings.resx b/external/corefx/src/System.Data.SqlClient/src/Resources/Strings.resx index 097998c31a..cbac126e53 100644 --- a/external/corefx/src/System.Data.SqlClient/src/Resources/Strings.resx +++ b/external/corefx/src/System.Data.SqlClient/src/Resources/Strings.resx @@ -167,7 +167,7 @@ Expecting argument of type {1}, but received type {0}. - {0} DeriveParameters only supports CommandType.StoredProcedure, not CommandType.{1}. + {0} DeriveParameters only supports CommandType.StoredProcedure, not CommandType. {1}. The stored procedure '{0}' doesn't exist. @@ -286,6 +286,9 @@ No mapping exists from object type {0} to a known managed provider native type. + + Unable to handle an unknown TypeCode {0} returned by Type {1}. + No mapping exists from DbType {0} to a known {1}. @@ -343,6 +346,9 @@ Unsupported SQL Server version. The .Net Framework SqlClient Data Provider can only be used with SQL Server versions 7.0 and later. + + Cannot create normalizer for '{0}'. + {0} cannot be changed while async operation is in progress. @@ -676,6 +682,12 @@ Subclass did not override a required method. + + no UDT attribute + + + Specified type is not registered on the target server. {0}. + Internal Error @@ -766,6 +778,27 @@ The sort ordinal {0} on field {1} exceeds the total number of fields. + + range: 0-8000 + + + unexpected error encountered in SqlClient data provider. {0} + + + UdtTypeName property must be set only for UDT parameters. + + + UdtTypeName property must be set for UDT parameters. + + + '{0}' is an invalid user defined type, reason: {1}. + + + SqlParameter.UdtTypeName is an invalid multipart name + + + Invalid 3 part name format for UdtTypeName. + There are no records in the SqlDataRecord enumeration. To send a table-valued parameter with no rows, use a null reference for the value instead. diff --git a/external/corefx/src/System.Data.SqlClient/src/System.Data.SqlClient.csproj b/external/corefx/src/System.Data.SqlClient/src/System.Data.SqlClient.csproj index 458751a21c..86915fc824 100644 --- a/external/corefx/src/System.Data.SqlClient/src/System.Data.SqlClient.csproj +++ b/external/corefx/src/System.Data.SqlClient/src/System.Data.SqlClient.csproj @@ -89,8 +89,16 @@ + + + + + + + + @@ -133,7 +141,7 @@ - + @@ -154,6 +162,7 @@ + @@ -228,6 +237,10 @@ + + + Common\Interop\Windows\SChannel\Interop.SecPkgContext_ApplicationProtocol.cs + Common\System\Net\Security\NegotiateStreamPal.Windows.cs diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/Common/AdapterUtil.SqlClient.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/Common/AdapterUtil.SqlClient.cs index 12bc23e463..8b52cac677 100644 --- a/external/corefx/src/System.Data.SqlClient/src/System/Data/Common/AdapterUtil.SqlClient.cs +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/Common/AdapterUtil.SqlClient.cs @@ -63,7 +63,12 @@ namespace System.Data.Common OverflowException e = new OverflowException(error, inner); return e; } - + internal static TypeLoadException TypeLoad(string error) + { + TypeLoadException e = new TypeLoadException(error); + TraceExceptionAsReturnValue(e); + return e; + } internal static PlatformNotSupportedException DbTypeNotSupported(string dbType) { PlatformNotSupportedException e = new PlatformNotSupportedException(SR.GetString(SR.SQL_DbTypeNotSupportedOnThisPlatform, dbType)); @@ -461,10 +466,11 @@ namespace System.Data.Common return IO(SR.GetString(SR.SqlMisc_StreamErrorMessage), internalException); } - internal static ArgumentException InvalidDataType(string typeName) + internal static ArgumentException InvalidDataType(TypeCode typecode) { - return Argument(SR.GetString(SR.ADP_InvalidDataType, typeName)); + return Argument(SR.GetString(SR.ADP_InvalidDataType, typecode.ToString())); } + internal static ArgumentException UnknownDataType(Type dataType) { return Argument(SR.GetString(SR.ADP_UnknownDataType, dataType.FullName)); @@ -474,6 +480,10 @@ namespace System.Data.Common { return Argument(SR.GetString(SR.ADP_DbTypeNotSupported, type.ToString(), enumtype.Name)); } + internal static ArgumentException UnknownDataTypeCode(Type dataType, TypeCode typeCode) + { + return Argument(SR.GetString(SR.ADP_UnknownDataTypeCode, ((int)typeCode).ToString(CultureInfo.InvariantCulture), dataType.FullName)); + } internal static ArgumentException InvalidOffsetValue(int value) { return Argument(SR.GetString(SR.ADP_InvalidOffsetValue, value.ToString(CultureInfo.InvariantCulture))); @@ -853,5 +863,22 @@ namespace System.Data.Common { return Provider(SR.GetString(SR.ADP_TransactionCompletedButNotDisposed)); } + + internal static ArgumentOutOfRangeException InvalidUserDefinedTypeSerializationFormat(Microsoft.SqlServer.Server.Format value) + { + return InvalidEnumerationValue(typeof(Microsoft.SqlServer.Server.Format), (int)value); + } + + internal static ArgumentOutOfRangeException NotSupportedUserDefinedTypeSerializationFormat(Microsoft.SqlServer.Server.Format value, string method) + { + return NotSupportedEnumerationValue(typeof(Microsoft.SqlServer.Server.Format), value.ToString(), method); + } + + internal static ArgumentOutOfRangeException ArgumentOutOfRange(string message, string parameterName, object value) + { + ArgumentOutOfRangeException e = new ArgumentOutOfRangeException(parameterName, value, message); + TraceExceptionAsReturnValue(e); + return e; + } } } \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/OperationAbortedException.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/OperationAbortedException.cs index 86e3832c17..0a53aeb4df 100644 --- a/external/corefx/src/System.Data.SqlClient/src/System/Data/OperationAbortedException.cs +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/OperationAbortedException.cs @@ -6,6 +6,9 @@ using System.Runtime.Serialization; namespace System.Data { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public sealed class OperationAbortedException : SystemException { private OperationAbortedException(string message, Exception innerException) : base(message, innerException) @@ -13,6 +16,10 @@ namespace System.Data HResult = unchecked((int)0x80131936); } + private OperationAbortedException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + internal static OperationAbortedException Aborted(Exception inner) { OperationAbortedException e; diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/ProviderBase/DbConnectionInternal.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/ProviderBase/DbConnectionInternal.cs index 93c9ac7bdb..3ac471cac9 100644 --- a/external/corefx/src/System.Data.SqlClient/src/System/Data/ProviderBase/DbConnectionInternal.cs +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/ProviderBase/DbConnectionInternal.cs @@ -862,8 +862,6 @@ namespace System.Data.ProviderBase CleanupConnectionOnTransactionCompletion(transaction); } - // TODO: Review whether we need the unmanaged code permission when we have the new object model available. - // [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] private void TransactionOutcomeEnlist(Transaction transaction) { transaction.TransactionCompleted += new TransactionCompletedEventHandler(TransactionCompletedEvent); diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/IBinarySerialize.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/IBinarySerialize.cs new file mode 100644 index 0000000000..ee9aa749c5 --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/IBinarySerialize.cs @@ -0,0 +1,18 @@ +// 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.IO; + +namespace Microsoft.SqlServer.Server +{ + // This interface is used by types that want full control over the + // binary serialization format. + public interface IBinarySerialize + { + // Read from the specified binary reader. + void Read(BinaryReader r); + // Write to the specified binary writer. + void Write(BinaryWriter w); + } +} \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/InvalidUdtException.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/InvalidUdtException.cs new file mode 100644 index 0000000000..63cad1b39c --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/InvalidUdtException.cs @@ -0,0 +1,50 @@ +// 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.Data.Common; +using System.Runtime.Serialization; +using System.Security.Permissions; + +namespace Microsoft.SqlServer.Server +{ + [Serializable] + public sealed class InvalidUdtException : SystemException + { + private const int InvalidUdtHResult = unchecked((int)0x80131937); + + internal InvalidUdtException() : base() + { + HResult = InvalidUdtHResult; + } + + internal InvalidUdtException(string message) : base(message) + { + HResult = InvalidUdtHResult; + } + + internal InvalidUdtException(string message, Exception innerException) : base(message, innerException) + { + HResult = InvalidUdtHResult; + } + + private InvalidUdtException(SerializationInfo si, StreamingContext sc) : base(si, sc) + { + } + + public override void GetObjectData(SerializationInfo si, StreamingContext context) + { + base.GetObjectData(si, context); + } + + internal static InvalidUdtException Create(Type udtType, string resourceReason) + { + string reason = SR.GetString(resourceReason); + string message = SR.GetString(SR.SqlUdt_InvalidUdtMessage, udtType.FullName, reason); + InvalidUdtException e = new InvalidUdtException(message); + ADP.TraceExceptionAsReturnValue(e); + return e; + } + } +} diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlFunctionAttribute.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlFunctionAttribute.cs new file mode 100644 index 0000000000..2dc71a5f26 --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlFunctionAttribute.cs @@ -0,0 +1,89 @@ +// 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; + +namespace Microsoft.SqlServer.Server +{ + [Serializable] + public enum DataAccessKind + { + None = 0, + Read = 1, + } + + [Serializable] + public enum SystemDataAccessKind + { + None = 0, + Read = 1, + } + + // sql specific attribute + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false), Serializable] + public class SqlFunctionAttribute : Attribute + { + private bool _isDeterministic; + private DataAccessKind _dataAccess; + private SystemDataAccessKind _systemDataAccess; + private bool _isPrecise; + private string _name; + private string _tableDefinition; + private string _fillRowMethodName; + + public SqlFunctionAttribute() + { + // default values + _isDeterministic = false; + _dataAccess = DataAccessKind.None; + _systemDataAccess = SystemDataAccessKind.None; + _isPrecise = false; + _name = null; + _tableDefinition = null; + _fillRowMethodName = null; + } + + public bool IsDeterministic + { + get => _isDeterministic; + set => _isDeterministic = value; + } + + public DataAccessKind DataAccess + { + get => _dataAccess; + set => _dataAccess = value; + } + + public SystemDataAccessKind SystemDataAccess + { + get => _systemDataAccess; + set => _systemDataAccess = value; + } + + public bool IsPrecise + { + get => _isPrecise; + set => _isPrecise = value; + } + + public string Name + { + get => _name; + set => _name = value; + } + + public string TableDefinition + { + get => _tableDefinition; + set => _tableDefinition = value; + } + + public string FillRowMethodName + { + get => _fillRowMethodName; + set => _fillRowMethodName = value; + } + } +} \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlMetaData.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlMetaData.cs index 421f647c08..c937c34398 100644 --- a/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlMetaData.cs +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlMetaData.cs @@ -2,13 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -//------------------------------------------------------------------------------ - - using System; using System.Data; using System.Data.Common; +using System.Diagnostics; using System.Globalization; using System.Data.SqlTypes; using System.Data.SqlClient; @@ -34,7 +31,9 @@ namespace Microsoft.SqlServer.Server private string _xmlSchemaCollectionDatabase; private string _xmlSchemaCollectionOwningSchema; private string _xmlSchemaCollectionName; + private string _serverTypeName; private bool _bPartialLength; + private Type _udtType; private bool _useServerDefault; private bool _isUniqueKey; private SortOrder _columnSortOrder; @@ -85,6 +84,27 @@ namespace Microsoft.SqlServer.Server isUniqueKey, columnSortOrder, sortOrdinal); } + // udt ctor without tvp extended properties + public SqlMetaData(String name, SqlDbType dbType, Type userDefinedType) + { + Construct(name, dbType, userDefinedType, null, x_defaultUseServerDefault, + x_defaultIsUniqueKey, x_defaultColumnSortOrder, x_defaultSortOrdinal); + } + + // udt ctor without tvp extended properties + public SqlMetaData(String name, SqlDbType dbType, Type userDefinedType, string serverTypeName) + { + Construct(name, dbType, userDefinedType, serverTypeName, x_defaultUseServerDefault, + x_defaultIsUniqueKey, x_defaultColumnSortOrder, x_defaultSortOrdinal); + } + + // udt ctor + public SqlMetaData(String name, SqlDbType dbType, Type userDefinedType, string serverTypeName, + bool useServerDefault, bool isUniqueKey, SortOrder columnSortOrder, int sortOrdinal) + { + Construct(name, dbType, userDefinedType, serverTypeName, useServerDefault, + isUniqueKey, columnSortOrder, sortOrdinal); + } // decimal ctor without tvp extended properties public SqlMetaData(String name, SqlDbType dbType, byte precision, byte scale) @@ -188,7 +208,8 @@ namespace Microsoft.SqlServer.Server Construct(name, dbType, useServerDefault, isUniqueKey, columnSortOrder, sortOrdinal); break; case SqlDbType.Udt: - throw ADP.DbTypeNotSupported(SqlDbType.Udt.ToString()); + Construct(name, dbType, userDefinedType, "", useServerDefault, isUniqueKey, columnSortOrder, sortOrdinal); + break; default: SQL.InvalidSqlDbTypeForConstructor(dbType); break; @@ -212,8 +233,8 @@ namespace Microsoft.SqlServer.Server string xmlSchemaCollectionDatabase, string xmlSchemaCollectionOwningSchema, string xmlSchemaCollectionName, - bool partialLength - ) + bool partialLength, + Type udtType) { AssertNameIsValid(name); @@ -229,7 +250,7 @@ namespace Microsoft.SqlServer.Server _xmlSchemaCollectionName = xmlSchemaCollectionName; _bPartialLength = partialLength; - ThrowIfUdt(sqlDBType); + _udtType = udtType; } // Private constructor used to initialize default instance array elements. @@ -253,150 +274,141 @@ namespace Microsoft.SqlServer.Server _lLocale = localeId; _eCompareOptions = compareOptions; _bPartialLength = partialLength; - ThrowIfUdt(sqlDbType); + _udtType = null; } public SqlCompareOptions CompareOptions { - get - { - return _eCompareOptions; - } + get => _eCompareOptions; } + public DbType DbType + { + get => sxm_rgSqlDbTypeToDbType[(int)_sqlDbType]; + } public bool IsUniqueKey { - get - { - return _isUniqueKey; - } + get => _isUniqueKey; } public long LocaleId { - get - { - return _lLocale; - } + get => _lLocale; } public static long Max { - get - { - return x_lMax; - } + get => x_lMax; } public long MaxLength { - get - { - return _lMaxLength; - } + get => _lMaxLength; } public string Name { - get - { - return _strName; - } + get => _strName; } public byte Precision { - get - { - return _bPrecision; - } + get => _bPrecision; } public byte Scale { - get - { - return _bScale; - } + get => _bScale; } public SortOrder SortOrder { - get - { - return _columnSortOrder; - } + get => _columnSortOrder; } public int SortOrdinal { - get - { - return _sortOrdinal; - } + get => _sortOrdinal; } public SqlDbType SqlDbType { - get - { - return _sqlDbType; - } + get => _sqlDbType; } + public Type Type + { + get => _udtType; + } public string TypeName { get { + if (_serverTypeName != null) + { + return _serverTypeName; + } + else if (SqlDbType == SqlDbType.Udt) + { + return UdtTypeName; + } + else { return sxm_rgDefaults[(int)SqlDbType].Name; } } } + internal string ServerTypeName + { + get => _serverTypeName; + } public bool UseServerDefault { - get - { - return _useServerDefault; - } + get => _useServerDefault; } public string XmlSchemaCollectionDatabase { - get - { - return _xmlSchemaCollectionDatabase; - } + get => _xmlSchemaCollectionDatabase; } public string XmlSchemaCollectionName { - get - { - return _xmlSchemaCollectionName; - } + get => _xmlSchemaCollectionName; } public string XmlSchemaCollectionOwningSchema { - get - { - return _xmlSchemaCollectionOwningSchema; - } + get => _xmlSchemaCollectionOwningSchema; } internal bool IsPartialLength { - get - { - return _bPartialLength; - } + get => _bPartialLength; } + internal string UdtTypeName + { + get + { + if (SqlDbType != SqlDbType.Udt) + { + return null; + } + else if (_udtType == null) + { + return null; + } + else + { + return _udtType.FullName; + } + } + } // Construction for all types that do not have variable attributes private void Construct(String name, SqlDbType dbType, bool useServerDefault, @@ -432,8 +444,6 @@ namespace Microsoft.SqlServer.Server SqlDbType.Xml == dbType)) throw SQL.InvalidSqlDbTypeForConstructor(dbType); - ThrowIfUdt(dbType); - SetDefaultsForType(dbType); if (SqlDbType.NText == dbType || SqlDbType.Text == dbType) @@ -641,6 +651,31 @@ namespace Microsoft.SqlServer.Server _sortOrdinal = sortOrdinal; } + // Construction for Udt type + private void Construct(String name, SqlDbType dbType, Type userDefinedType, string serverTypeName, bool useServerDefault, + bool isUniqueKey, SortOrder columnSortOrder, int sortOrdinal) + { + AssertNameIsValid(name); + + ValidateSortOrder(columnSortOrder, sortOrdinal); + + if (SqlDbType.Udt != dbType) + throw SQL.InvalidSqlDbTypeForConstructor(dbType); + + if (null == userDefinedType) + throw ADP.ArgumentNull(nameof(userDefinedType)); + + SetDefaultsForType(SqlDbType.Udt); + + _strName = name; + _lMaxLength = SerializationHelperSql9.GetUdtMaxLength(userDefinedType); + _udtType = userDefinedType; + _serverTypeName = serverTypeName; + _useServerDefault = useServerDefault; + _isUniqueKey = isUniqueKey; + _columnSortOrder = columnSortOrder; + _sortOrdinal = sortOrdinal; + } // Construction for Xml type private void Construct(String name, SqlDbType dbType, string database, string owningSchema, @@ -740,7 +775,6 @@ namespace Microsoft.SqlServer.Server { if (SqlDbType.Char == SqlDbType || SqlDbType.NChar == SqlDbType) { - //DBG.Assert(Max!=MaxLength, "SqlMetaData.Adjust(string): Fixed-length type with Max length!"); // Don't pad null values if (null != value) { @@ -991,7 +1025,7 @@ namespace Microsoft.SqlServer.Server if (value.MaxLength < MaxLength) { char[] rgchNew = new char[(int)MaxLength]; - Array.Copy(value.Buffer, 0, rgchNew, 0, (int)oldLength); + Buffer.BlockCopy(value.Buffer, 0, rgchNew, 0, (int)oldLength); value = new SqlChars(rgchNew); } @@ -1095,82 +1129,83 @@ namespace Microsoft.SqlServer.Server if (null == value) return null; - if (value is bool) - value = this.Adjust((Boolean)value); - else if (value is byte) - value = this.Adjust((Byte)value); - else if (value is char) - value = this.Adjust((Char)value); - else if (value is DateTime) - value = this.Adjust((DateTime)value); - else if (value is DBNull) - { /* DBNull passes through as is for all types */ } - else if (value is decimal) - value = this.Adjust((Decimal)value); - else if (value is double) - value = this.Adjust((Double)value); - else if (value is short) - value = this.Adjust((Int16)value); - else if (value is int) - value = this.Adjust((Int32)value); - else if (value is long) - value = this.Adjust((Int64)value); - else if (value is sbyte) - throw ADP.InvalidDataType(nameof(SByte)); - else if (value is float) - value = this.Adjust((Single)value); - else if (value is string) - value = this.Adjust((String)value); - else if (value is ushort) - throw ADP.InvalidDataType(nameof(UInt16)); - else if (value is uint) - throw ADP.InvalidDataType(nameof(UInt32)); - else if (value is ulong) - throw ADP.InvalidDataType(nameof(UInt64)); - else if (value is byte[]) - value = this.Adjust((System.Byte[])value); - else if (value is char[]) - value = this.Adjust((System.Char[])value); - else if (value is Guid) - value = this.Adjust((System.Guid)value); - else if (value is SqlBinary) - value = this.Adjust((SqlBinary)value); - else if (value is SqlBoolean) - value = this.Adjust((SqlBoolean)value); - else if (value is SqlByte) - value = this.Adjust((SqlByte)value); - else if (value is SqlDateTime) - value = this.Adjust((SqlDateTime)value); - else if (value is SqlDouble) - value = this.Adjust((SqlDouble)value); - else if (value is SqlGuid) - value = this.Adjust((SqlGuid)value); - else if (value is SqlInt16) - value = this.Adjust((SqlInt16)value); - else if (value is SqlInt32) - value = this.Adjust((SqlInt32)value); - else if (value is SqlInt64) - value = this.Adjust((SqlInt64)value); - else if (value is SqlMoney) - value = this.Adjust((SqlMoney)value); - else if (value is SqlDecimal) - value = this.Adjust((SqlDecimal)value); - else if (value is SqlSingle) - value = this.Adjust((SqlSingle)value); - else if (value is SqlString) - value = this.Adjust((SqlString)value); - else if (value is SqlChars) - value = this.Adjust((SqlChars)value); - else if (value is SqlBytes) - value = this.Adjust((SqlBytes)value); - else if (value is SqlXml) - value = this.Adjust((SqlXml)value); - else if (value is TimeSpan) - value = this.Adjust((TimeSpan)value); - else if (value is DateTimeOffset) - value = this.Adjust((DateTimeOffset)value); - else - throw ADP.UnknownDataType(value.GetType()); + Type dataType = value.GetType(); + switch (Type.GetTypeCode(dataType)) + { + case TypeCode.Boolean: value = this.Adjust((Boolean)value); break; + case TypeCode.Byte: value = this.Adjust((Byte)value); break; + case TypeCode.Char: value = this.Adjust((Char)value); break; + case TypeCode.DateTime: value = this.Adjust((DateTime)value); break; + case TypeCode.DBNull: /* DBNull passes through as is for all types */ break; + case TypeCode.Decimal: value = this.Adjust((Decimal)value); break; + case TypeCode.Double: value = this.Adjust((Double)value); break; + case TypeCode.Empty: throw ADP.InvalidDataType(TypeCode.Empty); + case TypeCode.Int16: value = this.Adjust((Int16)value); break; + case TypeCode.Int32: value = this.Adjust((Int32)value); break; + case TypeCode.Int64: value = this.Adjust((Int64)value); break; + case TypeCode.SByte: throw ADP.InvalidDataType(TypeCode.SByte); + case TypeCode.Single: value = this.Adjust((Single)value); break; + case TypeCode.String: value = this.Adjust((String)value); break; + case TypeCode.UInt16: throw ADP.InvalidDataType(TypeCode.UInt16); + case TypeCode.UInt32: throw ADP.InvalidDataType(TypeCode.UInt32); + case TypeCode.UInt64: throw ADP.InvalidDataType(TypeCode.UInt64); + case TypeCode.Object: + if (dataType == typeof(System.Byte[])) + value = this.Adjust((System.Byte[])value); + else if (dataType == typeof(System.Char[])) + value = this.Adjust((System.Char[])value); + else if (dataType == typeof(System.Guid)) + value = this.Adjust((System.Guid)value); + else if (dataType == typeof(System.Object)) + { + throw ADP.InvalidDataType(TypeCode.UInt64); + } + else if (dataType == typeof(SqlBinary)) + value = this.Adjust((SqlBinary)value); + else if (dataType == typeof(SqlBoolean)) + value = this.Adjust((SqlBoolean)value); + else if (dataType == typeof(SqlByte)) + value = this.Adjust((SqlByte)value); + else if (dataType == typeof(SqlDateTime)) + value = this.Adjust((SqlDateTime)value); + else if (dataType == typeof(SqlDouble)) + value = this.Adjust((SqlDouble)value); + else if (dataType == typeof(SqlGuid)) + value = this.Adjust((SqlGuid)value); + else if (dataType == typeof(SqlInt16)) + value = this.Adjust((SqlInt16)value); + else if (dataType == typeof(SqlInt32)) + value = this.Adjust((SqlInt32)value); + else if (dataType == typeof(SqlInt64)) + value = this.Adjust((SqlInt64)value); + else if (dataType == typeof(SqlMoney)) + value = this.Adjust((SqlMoney)value); + else if (dataType == typeof(SqlDecimal)) + value = this.Adjust((SqlDecimal)value); + else if (dataType == typeof(SqlSingle)) + value = this.Adjust((SqlSingle)value); + else if (dataType == typeof(SqlString)) + value = this.Adjust((SqlString)value); + else if (dataType == typeof(SqlChars)) + value = this.Adjust((SqlChars)value); + else if (dataType == typeof(SqlBytes)) + value = this.Adjust((SqlBytes)value); + else if (dataType == typeof(SqlXml)) + value = this.Adjust((SqlXml)value); + else if (dataType == typeof(TimeSpan)) + value = this.Adjust((TimeSpan)value); + else if (dataType == typeof(DateTimeOffset)) + value = this.Adjust((DateTimeOffset)value); + else + { + // Handle UDTs? + throw ADP.UnknownDataType(dataType); + } + break; + + + default: throw ADP.UnknownDataTypeCode(dataType, Type.GetTypeCode(dataType)); + } return value; } @@ -1179,175 +1214,185 @@ namespace Microsoft.SqlServer.Server { if (value == null) throw ADP.ArgumentNull(nameof(value)); - - SqlMetaData smd; - - if (value is Boolean) smd = new SqlMetaData(name, SqlDbType.Bit); - else if (value is Byte) smd = new SqlMetaData(name, SqlDbType.TinyInt); - else if (value is Char) smd = new SqlMetaData(name, SqlDbType.NVarChar, 1); - else if (value is DateTime) smd = new SqlMetaData(name, SqlDbType.DateTime); - else if (value is DBNull) throw ADP.InvalidDataType(nameof(DBNull)); - else if (value is Decimal) + SqlMetaData smd = null; + Type dataType = value.GetType(); + switch (Type.GetTypeCode(dataType)) { - // use logic inside SqlDecimal to infer precision and scale. - SqlDecimal sd = new SqlDecimal((Decimal)value); - smd = new SqlMetaData(name, SqlDbType.Decimal, sd.Precision, sd.Scale); + case TypeCode.Boolean: smd = new SqlMetaData(name, SqlDbType.Bit); break; + case TypeCode.Byte: smd = new SqlMetaData(name, SqlDbType.TinyInt); break; + case TypeCode.Char: smd = new SqlMetaData(name, SqlDbType.NVarChar, 1); break; + case TypeCode.DateTime: smd = new SqlMetaData(name, SqlDbType.DateTime); break; + case TypeCode.DBNull: throw ADP.InvalidDataType(TypeCode.DBNull); + case TypeCode.Decimal: + { // Add brackets in order to contain scope declare local variable "sd" + // use logic inside SqlDecimal to infer precision and scale. + SqlDecimal sd = new SqlDecimal((Decimal)value); + smd = new SqlMetaData(name, SqlDbType.Decimal, sd.Precision, sd.Scale); + } + break; + case TypeCode.Double: smd = new SqlMetaData(name, SqlDbType.Float); break; + case TypeCode.Empty: throw ADP.InvalidDataType(TypeCode.Empty); + case TypeCode.Int16: smd = new SqlMetaData(name, SqlDbType.SmallInt); break; + case TypeCode.Int32: smd = new SqlMetaData(name, SqlDbType.Int); break; + case TypeCode.Int64: smd = new SqlMetaData(name, SqlDbType.BigInt); break; + case TypeCode.SByte: throw ADP.InvalidDataType(TypeCode.SByte); + case TypeCode.Single: smd = new SqlMetaData(name, SqlDbType.Real); break; + case TypeCode.String: + { + long maxLen = ((String)value).Length; + if (maxLen < 1) maxLen = 1; + + if (x_lServerMaxUnicode < maxLen) + maxLen = Max; + + smd = new SqlMetaData(name, SqlDbType.NVarChar, maxLen); + } + break; + case TypeCode.UInt16: throw ADP.InvalidDataType(TypeCode.UInt16); + case TypeCode.UInt32: throw ADP.InvalidDataType(TypeCode.UInt32); + case TypeCode.UInt64: throw ADP.InvalidDataType(TypeCode.UInt64); + case TypeCode.Object: + if (dataType == typeof(System.Byte[])) + { + long maxLen = ((System.Byte[])value).Length; + if (maxLen < 1) maxLen = 1; + + if (x_lServerMaxBinary < maxLen) + maxLen = Max; + + smd = new SqlMetaData(name, SqlDbType.VarBinary, maxLen); + } + else if (dataType == typeof(System.Char[])) + { + long maxLen = ((System.Char[])value).Length; + if (maxLen < 1) maxLen = 1; + + if (x_lServerMaxUnicode < maxLen) + maxLen = Max; + + smd = new SqlMetaData(name, SqlDbType.NVarChar, maxLen); + } + else if (dataType == typeof(System.Guid)) + smd = new SqlMetaData(name, SqlDbType.UniqueIdentifier); + else if (dataType == typeof(System.Object)) + smd = new SqlMetaData(name, SqlDbType.Variant); + else if (dataType == typeof(SqlBinary)) + { + long maxLen; + SqlBinary sb = ((SqlBinary)value); + if (!sb.IsNull) + { + maxLen = sb.Length; + if (maxLen < 1) maxLen = 1; + + if (x_lServerMaxBinary < maxLen) + maxLen = Max; + } + else + maxLen = sxm_rgDefaults[(int)SqlDbType.VarBinary].MaxLength; + + smd = new SqlMetaData(name, SqlDbType.VarBinary, maxLen); + } + else if (dataType == typeof(SqlBoolean)) + smd = new SqlMetaData(name, SqlDbType.Bit); + else if (dataType == typeof(SqlByte)) + smd = new SqlMetaData(name, SqlDbType.TinyInt); + else if (dataType == typeof(SqlDateTime)) + smd = new SqlMetaData(name, SqlDbType.DateTime); + else if (dataType == typeof(SqlDouble)) + smd = new SqlMetaData(name, SqlDbType.Float); + else if (dataType == typeof(SqlGuid)) + smd = new SqlMetaData(name, SqlDbType.UniqueIdentifier); + else if (dataType == typeof(SqlInt16)) + smd = new SqlMetaData(name, SqlDbType.SmallInt); + else if (dataType == typeof(SqlInt32)) + smd = new SqlMetaData(name, SqlDbType.Int); + else if (dataType == typeof(SqlInt64)) + smd = new SqlMetaData(name, SqlDbType.BigInt); + else if (dataType == typeof(SqlMoney)) + smd = new SqlMetaData(name, SqlDbType.Money); + else if (dataType == typeof(SqlDecimal)) + { + byte bPrec; + byte scale; + SqlDecimal sd = (SqlDecimal)value; + if (!sd.IsNull) + { + bPrec = sd.Precision; + scale = sd.Scale; + } + else + { + bPrec = sxm_rgDefaults[(int)SqlDbType.Decimal].Precision; + scale = sxm_rgDefaults[(int)SqlDbType.Decimal].Scale; + } + smd = new SqlMetaData(name, SqlDbType.Decimal, bPrec, scale); + } + else if (dataType == typeof(SqlSingle)) + smd = new SqlMetaData(name, SqlDbType.Real); + else if (dataType == typeof(SqlString)) + { + SqlString ss = (SqlString)value; + if (!ss.IsNull) + { + long maxLen = ss.Value.Length; + if (maxLen < 1) maxLen = 1; + + if (maxLen > x_lServerMaxUnicode) + maxLen = Max; + + smd = new SqlMetaData(name, SqlDbType.NVarChar, maxLen, ss.LCID, ss.SqlCompareOptions); + } + else + { + smd = new SqlMetaData(name, SqlDbType.NVarChar, sxm_rgDefaults[(int)SqlDbType.NVarChar].MaxLength); + } + } + else if (dataType == typeof(SqlChars)) + { + long maxLen; + SqlChars sch = (SqlChars)value; + if (!sch.IsNull) + { + maxLen = sch.Length; + if (maxLen < 1) maxLen = 1; + + if (maxLen > x_lServerMaxUnicode) + maxLen = Max; + } + else + maxLen = sxm_rgDefaults[(int)SqlDbType.NVarChar].MaxLength; + + smd = new SqlMetaData(name, SqlDbType.NVarChar, maxLen); + } + else if (dataType == typeof(SqlBytes)) + { + long maxLen; + SqlBytes sb = (SqlBytes)value; + if (!sb.IsNull) + { + maxLen = sb.Length; + if (maxLen < 1) maxLen = 1; + else if (x_lServerMaxBinary < maxLen) maxLen = Max; + } + else + maxLen = sxm_rgDefaults[(int)SqlDbType.VarBinary].MaxLength; + + smd = new SqlMetaData(name, SqlDbType.VarBinary, maxLen); + } + else if (dataType == typeof(SqlXml)) + smd = new SqlMetaData(name, SqlDbType.Xml); + else if (dataType == typeof(TimeSpan)) + smd = new SqlMetaData(name, SqlDbType.Time, 0, InferScaleFromTimeTicks(((TimeSpan)value).Ticks)); + else if (dataType == typeof(DateTimeOffset)) + smd = new SqlMetaData(name, SqlDbType.DateTimeOffset, 0, InferScaleFromTimeTicks(((DateTimeOffset)value).Ticks)); + else + throw ADP.UnknownDataType(dataType); + break; + + + default: throw ADP.UnknownDataTypeCode(dataType, Type.GetTypeCode(dataType)); } - else if (value is Double) smd = new SqlMetaData(name, SqlDbType.Float); - else if (value is Int16) smd = new SqlMetaData(name, SqlDbType.SmallInt); - else if (value is Int32) smd = new SqlMetaData(name, SqlDbType.Int); - else if (value is Int64) smd = new SqlMetaData(name, SqlDbType.BigInt); - else if (value is SByte) throw ADP.InvalidDataType(nameof(SByte)); - else if (value is Single) smd = new SqlMetaData(name, SqlDbType.Real); - else if (value is String) - { - long maxLen = ((String)value).Length; - if (maxLen < 1) maxLen = 1; - - if (x_lServerMaxUnicode < maxLen) - maxLen = Max; - - smd = new SqlMetaData(name, SqlDbType.NVarChar, maxLen); - } - else if (value is UInt16) throw ADP.InvalidDataType(nameof(UInt16)); - else if (value is UInt32) throw ADP.InvalidDataType(nameof(UInt32)); - else if (value is UInt64) throw ADP.InvalidDataType(nameof(UInt64)); - else if (value is System.Byte[]) - { - long maxLen = ((System.Byte[])value).Length; - if (maxLen < 1) maxLen = 1; - - if (x_lServerMaxBinary < maxLen) - maxLen = Max; - - smd = new SqlMetaData(name, SqlDbType.VarBinary, maxLen); - } - else if (value is System.Char[]) - { - long maxLen = ((System.Char[])value).Length; - if (maxLen < 1) maxLen = 1; - - if (x_lServerMaxUnicode < maxLen) - maxLen = Max; - - smd = new SqlMetaData(name, SqlDbType.NVarChar, maxLen); - } - else if (value is System.Guid) - smd = new SqlMetaData(name, SqlDbType.UniqueIdentifier); - else if (value is System.Object) - smd = new SqlMetaData(name, SqlDbType.Variant); - else if (value is SqlBinary) - { - long maxLen; - SqlBinary sb = ((SqlBinary)value); - if (!sb.IsNull) - { - maxLen = sb.Length; - if (maxLen < 1) maxLen = 1; - - if (x_lServerMaxBinary < maxLen) - maxLen = Max; - } - else - maxLen = sxm_rgDefaults[(int)SqlDbType.VarBinary].MaxLength; - - smd = new SqlMetaData(name, SqlDbType.VarBinary, maxLen); - } - else if (value is SqlBoolean) - smd = new SqlMetaData(name, SqlDbType.Bit); - else if (value is SqlByte) - smd = new SqlMetaData(name, SqlDbType.TinyInt); - else if (value is SqlDateTime) - smd = new SqlMetaData(name, SqlDbType.DateTime); - else if (value is SqlDouble) - smd = new SqlMetaData(name, SqlDbType.Float); - else if (value is SqlGuid) - smd = new SqlMetaData(name, SqlDbType.UniqueIdentifier); - else if (value is SqlInt16) - smd = new SqlMetaData(name, SqlDbType.SmallInt); - else if (value is SqlInt32) - smd = new SqlMetaData(name, SqlDbType.Int); - else if (value is SqlInt64) - smd = new SqlMetaData(name, SqlDbType.BigInt); - else if (value is SqlMoney) - smd = new SqlMetaData(name, SqlDbType.Money); - else if (value is SqlDecimal) - { - byte bPrec; - byte scale; - SqlDecimal sd = (SqlDecimal)value; - if (!sd.IsNull) - { - bPrec = sd.Precision; - scale = sd.Scale; - } - else - { - bPrec = sxm_rgDefaults[(int)SqlDbType.Decimal].Precision; - scale = sxm_rgDefaults[(int)SqlDbType.Decimal].Scale; - } - smd = new SqlMetaData(name, SqlDbType.Decimal, bPrec, scale); - } - else if (value is SqlSingle) - smd = new SqlMetaData(name, SqlDbType.Real); - else if (value is SqlString) - { - SqlString ss = (SqlString)value; - if (!ss.IsNull) - { - long maxLen = ss.Value.Length; - if (maxLen < 1) maxLen = 1; - - if (maxLen > x_lServerMaxUnicode) - maxLen = Max; - - smd = new SqlMetaData(name, SqlDbType.NVarChar, maxLen, ss.LCID, ss.SqlCompareOptions); - } - else - { - smd = new SqlMetaData(name, SqlDbType.NVarChar, sxm_rgDefaults[(int)SqlDbType.NVarChar].MaxLength); - } - } - else if (value is SqlChars) - { - long maxLen; - SqlChars sch = (SqlChars)value; - if (!sch.IsNull) - { - maxLen = sch.Length; - if (maxLen < 1) maxLen = 1; - - if (maxLen > x_lServerMaxUnicode) - maxLen = Max; - } - else - maxLen = sxm_rgDefaults[(int)SqlDbType.NVarChar].MaxLength; - - smd = new SqlMetaData(name, SqlDbType.NVarChar, maxLen); - } - else if (value is SqlBytes) - { - long maxLen; - SqlBytes sb = (SqlBytes)value; - if (!sb.IsNull) - { - maxLen = sb.Length; - if (maxLen < 1) maxLen = 1; - else if (x_lServerMaxBinary < maxLen) maxLen = Max; - } - else - maxLen = sxm_rgDefaults[(int)SqlDbType.VarBinary].MaxLength; - - smd = new SqlMetaData(name, SqlDbType.VarBinary, maxLen); - } - else if (value is SqlXml) - smd = new SqlMetaData(name, SqlDbType.Xml); - else if (value is TimeSpan) - smd = new SqlMetaData(name, SqlDbType.Time, 0, InferScaleFromTimeTicks(((TimeSpan)value).Ticks)); - else if (value is DateTimeOffset) - smd = new SqlMetaData(name, SqlDbType.DateTimeOffset, 0, InferScaleFromTimeTicks(((DateTimeOffset)value).Ticks)); - else - throw ADP.UnknownDataType(value.GetType()); return smd; } @@ -1370,7 +1415,6 @@ namespace Microsoft.SqlServer.Server { if (SqlDbType.Binary == SqlDbType || SqlDbType.Timestamp == SqlDbType) { - //DBG.Assert(Max!=MaxLength, "SqlMetaData.Adjust(byte[]): Fixed-length type with Max length!"); // Don't pad null values if (null != value) { @@ -1425,7 +1469,6 @@ namespace Microsoft.SqlServer.Server { if (SqlDbType.Char == SqlDbType || SqlDbType.NChar == SqlDbType) { - //DBG.Assert(Max!=MaxLength, "SqlMetaData.Adjust(byte[]): Fixed-length type with Max length!"); // Don't pad null values if (null != value) { @@ -1434,7 +1477,7 @@ namespace Microsoft.SqlServer.Server if (oldLength < MaxLength) { char[] rgchNew = new char[(int)MaxLength]; - Array.Copy(value, 0, rgchNew, 0, (int)oldLength); + Buffer.BlockCopy(value, 0, rgchNew, 0, (int)oldLength); // pad extra space for (long i = oldLength; i < rgchNew.Length; i++) @@ -1458,7 +1501,7 @@ namespace Microsoft.SqlServer.Server if (value.Length > MaxLength && Max != MaxLength) { char[] rgchNewValue = new char[MaxLength]; - Array.Copy(value, 0, rgchNewValue, 0, (int)MaxLength); + Buffer.BlockCopy(value, 0, rgchNewValue, 0, (int)MaxLength); value = rgchNewValue; } @@ -1480,8 +1523,7 @@ namespace Microsoft.SqlServer.Server md.SqlDbType == SqlDbType.VarBinary) { SqlMetaData mdnew = new SqlMetaData(md.Name, md.SqlDbType, SqlMetaData.Max, 0, 0, md.LocaleId, - md.CompareOptions, null, null, null, true - ); + md.CompareOptions, null, null, null, true, md.Type); return mdnew; } else @@ -1564,6 +1606,44 @@ namespace Microsoft.SqlServer.Server return MaxTimeScale; } + private static DbType[] sxm_rgSqlDbTypeToDbType = { + DbType.Int64, // SqlDbType.BigInt + DbType.Binary, // SqlDbType.Binary + DbType.Boolean, // SqlDbType.Bit + DbType.AnsiString, // SqlDbType.Char + DbType.DateTime, // SqlDbType.DateTime + DbType.Decimal, // SqlDbType.Decimal + DbType.Double, // SqlDbType.Float + DbType.Binary, // SqlDbType.Image + DbType.Int32, // SqlDbType.Int + DbType.Currency, // SqlDbType.Money + DbType.String, // SqlDbType.NChar + DbType.String, // SqlDbType.NText + DbType.String, // SqlDbType.NVarChar + DbType.Single, // SqlDbType.Real + DbType.Guid, // SqlDbType.UniqueIdentifier + DbType.DateTime, // SqlDbType.SmallDateTime + DbType.Int16, // SqlDbType.SmallInt + DbType.Currency, // SqlDbType.SmallMoney + DbType.AnsiString, // SqlDbType.Text + DbType.Binary, // SqlDbType.Timestamp + DbType.Byte, // SqlDbType.TinyInt + DbType.Binary, // SqlDbType.VarBinary + DbType.AnsiString, // SqlDbType.VarChar + DbType.Object, // SqlDbType.Variant + DbType.Object, // SqlDbType.Row + DbType.Xml, // SqlDbType.Xml + DbType.String, // SqlDbType.NVarChar, place holder + DbType.String, // SqlDbType.NVarChar, place holder + DbType.String, // SqlDbType.NVarChar, place holder + DbType.Object, // SqlDbType.Udt + DbType.Object, // SqlDbType.Structured + DbType.Date, // SqlDbType.Date + DbType.Time, // SqlDbType.Time + DbType.DateTime2, // SqlDbType.DateTime2 + DbType.DateTimeOffset // SqlDbType.DateTimeOffset + }; + private void SetDefaultsForType(SqlDbType dbType) { if (SqlDbType.BigInt <= dbType && SqlDbType.DateTimeOffset >= dbType) @@ -1640,8 +1720,8 @@ namespace Microsoft.SqlServer.Server x_lServerMaxUnicode, 0, 0, 0, x_eDefaultStringCompareOptions, false), // Placeholder for value 27 new SqlMetaData("nvarchar", SqlDbType.NVarChar, x_lServerMaxUnicode, 0, 0, 0, x_eDefaultStringCompareOptions, false), // Placeholder for value 28 - new SqlMetaData("udt", SqlDbType.Structured, - 0, 0, 0, 0, SqlCompareOptions.None, false), // Placeholder for udt (value 29) + new SqlMetaData("udt", SqlDbType.Udt, + 0, 0, 0, 0, SqlCompareOptions.None, false), // SqlDbType.Udt = 29 new SqlMetaData("table", SqlDbType.Structured, 0, 0, 0, 0, SqlCompareOptions.None, false), // SqlDbType.Structured new SqlMetaData("date", SqlDbType.Date, @@ -1653,12 +1733,5 @@ namespace Microsoft.SqlServer.Server new SqlMetaData("datetimeoffset", SqlDbType.DateTimeOffset, 10, 0, 7, 0, SqlCompareOptions.None, false), // SqlDbType.DateTimeOffset }; - private void ThrowIfUdt(SqlDbType dbType) - { - if (dbType == SqlDbType.Udt) - { - throw ADP.DbTypeNotSupported(SqlDbType.Udt.ToString()); - } - } } } diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlMethodAttribute.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlMethodAttribute.cs new file mode 100644 index 0000000000..7deebbc535 --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlMethodAttribute.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. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Microsoft.SqlServer.Server +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false), Serializable] + public sealed class SqlMethodAttribute : SqlFunctionAttribute + { + private bool _isCalledOnNullInputs; + private bool _isMutator; + private bool _shouldInvokeIfReceiverIsNull; + + public SqlMethodAttribute() + { + // default values + _isCalledOnNullInputs = true; + _isMutator = false; + _shouldInvokeIfReceiverIsNull = false; + } + + public bool OnNullCall + { + get => _isCalledOnNullInputs; + set => _isCalledOnNullInputs = value; + } + + public bool IsMutator + { + get => _isMutator; + set => _isMutator = value; + } + + public bool InvokeIfReceiverIsNull + { + get => _shouldInvokeIfReceiverIsNull; + set => _shouldInvokeIfReceiverIsNull = value; + } + } +} diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlNorm.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlNorm.cs new file mode 100644 index 0000000000..16ae5c634a --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlNorm.cs @@ -0,0 +1,606 @@ +// 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.Collections; +using System.Diagnostics; +using System.Data; +using System.IO; +using System.Globalization; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Security.Permissions; +using System.Text; +using System.Runtime.CompilerServices; +using System.Data.SqlTypes; + +namespace Microsoft.SqlServer.Server +{ + // The class that holds the offset, field, and normalizer for + // a particular field. + internal sealed class FieldInfoEx : IComparable + { + internal readonly int Offset; + internal readonly FieldInfo FieldInfo; + internal readonly Normalizer Normalizer; + + internal FieldInfoEx(FieldInfo fi, int offset, Normalizer normalizer) + { + FieldInfo = fi; + Offset = offset; + Debug.Assert(normalizer != null, "normalizer argument should not be null!"); + Normalizer = normalizer; + } + + // Sort fields by field offsets. + public int CompareTo(object other) + { + FieldInfoEx otherF = other as FieldInfoEx; + if (otherF == null) + return -1; + return Offset.CompareTo(otherF.Offset); + } + } + + // The most complex normalizer, a udt normalizer + internal sealed class BinaryOrderedUdtNormalizer : Normalizer + { + internal readonly FieldInfoEx[] FieldsToNormalize; + private int _size; + private byte[] _padBuffer; + internal readonly object NullInstance; + //a boolean that tells us if a udt is a "top-level" udt, + //i.e. one that does not require a null byte header. + private bool _isTopLevelUdt; + + private FieldInfo[] GetFields(Type t) + { + return t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + } + + internal BinaryOrderedUdtNormalizer(Type t, bool isTopLevelUdt) + { + _skipNormalize = false; + if (_skipNormalize) + { + // if skipping normalization, dont write the null + // byte header for IsNull + _isTopLevelUdt = true; + } + + _isTopLevelUdt = true; + + // get all the fields + FieldInfo[] fields = GetFields(t); + + FieldsToNormalize = new FieldInfoEx[fields.Length]; + + int i = 0; + + foreach (FieldInfo fi in fields) + { + int offset = Marshal.OffsetOf(fi.DeclaringType, fi.Name).ToInt32(); + FieldsToNormalize[i++] = new FieldInfoEx(fi, offset, GetNormalizer(fi.FieldType)); + } + + //sort by offset + Array.Sort(FieldsToNormalize); + //if this is not a top-level udt, do setup for null values. + //null values need to compare less than all other values, + //so prefix a null byte indicator. + if (!_isTopLevelUdt) + { + //get the null value for this type, special case for sql types, which + //have a null field + if (typeof(INullable).IsAssignableFrom(t)) + { + PropertyInfo pi = t.GetProperty("Null", + BindingFlags.Public | BindingFlags.Static); + if (pi == null || pi.PropertyType != t) + { + FieldInfo fi = t.GetField("Null", BindingFlags.Public | BindingFlags.Static); + if (fi == null || fi.FieldType != t) + throw new Exception("could not find Null field/property in nullable type " + t); + else + NullInstance = fi.GetValue(null); + } + else + { + NullInstance = pi.GetValue(null, null); + } + //create the padding buffer + _padBuffer = new byte[Size - 1]; + } + } + } + + internal bool IsNullable => (NullInstance != null); + + // Normalize the top-level udt + internal void NormalizeTopObject(object udt, Stream s) + { + Normalize(null, udt, s); + } + + // Denormalize a top-level udt and return it + internal object DeNormalizeTopObject(Type t, Stream s) => DeNormalizeInternal(t, s); + + // Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set. + [MethodImpl(MethodImplOptions.NoInlining)] + private object DeNormalizeInternal(Type t, Stream s) + { + object result = null; + //if nullable and not the top object, read the null marker + if (!_isTopLevelUdt && typeof(INullable).IsAssignableFrom(t)) + { + byte nullByte = (byte)s.ReadByte(); + if (nullByte == 0) + { + result = NullInstance; + s.Read(_padBuffer, 0, _padBuffer.Length); + return result; + } + } + if (result == null) + result = Activator.CreateInstance(t); + foreach (FieldInfoEx myField in FieldsToNormalize) + { + myField.Normalizer.DeNormalize(myField.FieldInfo, result, s); + } + return result; + } + + internal override void Normalize(FieldInfo fi, object obj, Stream s) + { + object inner; + if (fi == null) + { + inner = obj; + } + else + { + inner = GetValue(fi, obj); + } + + // If nullable and not the top object, write a null indicator + if (inner is INullable oNullable && !_isTopLevelUdt) + { + if (oNullable.IsNull) + { + s.WriteByte(0); + s.Write(_padBuffer, 0, _padBuffer.Length); + return; + } + else + { + s.WriteByte(1); + } + } + + foreach (FieldInfoEx myField in FieldsToNormalize) + { + myField.Normalizer.Normalize(myField.FieldInfo, inner, s); + } + } + + internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) + { + SetValue(fi, recvr, DeNormalizeInternal(fi.FieldType, s)); + } + + internal override int Size + { + get + { + if (_size != 0) + return _size; + if (IsNullable && !_isTopLevelUdt) + _size = 1; + foreach (FieldInfoEx myField in FieldsToNormalize) + { + _size += myField.Normalizer.Size; + } + return _size; + } + } + } + + internal abstract class Normalizer + { + protected bool _skipNormalize; + + internal static Normalizer GetNormalizer(Type t) + { + Normalizer n = null; + if (t.IsPrimitive) + { + if (t == typeof(byte)) + n = new ByteNormalizer(); + else if (t == typeof(sbyte)) + n = new SByteNormalizer(); + else if (t == typeof(bool)) + n = new BooleanNormalizer(); + else if (t == typeof(short)) + n = new ShortNormalizer(); + else if (t == typeof(ushort)) + n = new UShortNormalizer(); + else if (t == typeof(int)) + n = new IntNormalizer(); + else if (t == typeof(uint)) + n = new UIntNormalizer(); + else if (t == typeof(float)) + n = new FloatNormalizer(); + else if (t == typeof(double)) + n = new DoubleNormalizer(); + else if (t == typeof(long)) + n = new LongNormalizer(); + else if (t == typeof(ulong)) + n = new ULongNormalizer(); + } + else if (t.IsValueType) + { + n = new BinaryOrderedUdtNormalizer(t, false); + } + if (n == null) + throw new Exception(SR.GetString(SR.SQL_CannotCreateNormalizer, t.FullName)); + n._skipNormalize = false; + return n; + } + + internal abstract void Normalize(FieldInfo fi, object recvr, Stream s); + + internal abstract void DeNormalize(FieldInfo fi, object recvr, Stream s); + + protected void FlipAllBits(byte[] b) + { + for (int i = 0; i < b.Length; i++) + b[i] = (byte)~b[i]; + } + + protected object GetValue(FieldInfo fi, object obj) => fi.GetValue(obj); + + protected void SetValue(FieldInfo fi, object recvr, object value) + { + fi.SetValue(recvr, value); + } + + internal abstract int Size { get; } + } + + internal sealed class BooleanNormalizer : Normalizer + { + internal override void Normalize(FieldInfo fi, object obj, Stream s) + { + bool b = (bool)GetValue(fi, obj); + s.WriteByte((byte)(b ? 1 : 0)); + } + + internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) + { + byte b = (byte)s.ReadByte(); + SetValue(fi, recvr, b == 1); + } + + internal override int Size => 1; + } + + internal sealed class SByteNormalizer : Normalizer + { + internal override void Normalize(FieldInfo fi, object obj, Stream s) + { + sbyte sb = (sbyte)GetValue(fi, obj); + byte b; + unchecked + { + b = (byte)sb; + } + if (!_skipNormalize) + b ^= 0x80; // flip the sign bit + s.WriteByte(b); + } + + internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) + { + byte b = (byte)s.ReadByte(); + if (!_skipNormalize) + b ^= 0x80; // flip the sign bit + sbyte sb; + unchecked + { + sb = (sbyte)b; + } + SetValue(fi, recvr, sb); + } + + internal override int Size => 1; + } + + internal sealed class ByteNormalizer : Normalizer + { + internal override void Normalize(FieldInfo fi, object obj, Stream s) + { + byte b = (byte)GetValue(fi, obj); + s.WriteByte(b); + } + + internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) + { + byte b = (byte)s.ReadByte(); + SetValue(fi, recvr, b); + } + + internal override int Size => 1; + } + + internal sealed class ShortNormalizer : Normalizer + { + internal override void Normalize(FieldInfo fi, object obj, Stream s) + { + byte[] b = BitConverter.GetBytes((short)GetValue(fi, obj)); + if (!_skipNormalize) + { + Array.Reverse(b); + b[0] ^= 0x80; + } + s.Write(b, 0, b.Length); + } + + internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) + { + byte[] b = new byte[2]; + s.Read(b, 0, b.Length); + if (!_skipNormalize) + { + b[0] ^= 0x80; + Array.Reverse(b); + } + SetValue(fi, recvr, BitConverter.ToInt16(b, 0)); + } + + internal override int Size { get { return 2; } } + } + + internal sealed class UShortNormalizer : Normalizer + { + internal override void Normalize(FieldInfo fi, object obj, Stream s) + { + byte[] b = BitConverter.GetBytes((ushort)GetValue(fi, obj)); + if (!_skipNormalize) + { + Array.Reverse(b); + } + s.Write(b, 0, b.Length); + } + + internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) + { + byte[] b = new byte[2]; + s.Read(b, 0, b.Length); + if (!_skipNormalize) + { + Array.Reverse(b); + } + SetValue(fi, recvr, BitConverter.ToUInt16(b, 0)); + } + + internal override int Size => 2; + } + + internal sealed class IntNormalizer : Normalizer + { + internal override void Normalize(FieldInfo fi, object obj, Stream s) + { + byte[] b = BitConverter.GetBytes((int)GetValue(fi, obj)); + if (!_skipNormalize) + { + Array.Reverse(b); + b[0] ^= 0x80; + } + s.Write(b, 0, b.Length); + } + + internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) + { + byte[] b = new byte[4]; + s.Read(b, 0, b.Length); + if (!_skipNormalize) + { + b[0] ^= 0x80; + Array.Reverse(b); + } + SetValue(fi, recvr, BitConverter.ToInt32(b, 0)); + } + + internal override int Size => 4; + } + + internal sealed class UIntNormalizer : Normalizer + { + internal override void Normalize(FieldInfo fi, object obj, Stream s) + { + byte[] b = BitConverter.GetBytes((uint)GetValue(fi, obj)); + if (!_skipNormalize) + { + Array.Reverse(b); + } + s.Write(b, 0, b.Length); + } + + internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) + { + byte[] b = new byte[4]; + s.Read(b, 0, b.Length); + if (!_skipNormalize) + { + Array.Reverse(b); + } + SetValue(fi, recvr, BitConverter.ToUInt32(b, 0)); + } + + internal override int Size => 4; + } + + internal sealed class LongNormalizer : Normalizer + { + internal override void Normalize(FieldInfo fi, object obj, Stream s) + { + byte[] b = BitConverter.GetBytes((long)GetValue(fi, obj)); + if (!_skipNormalize) + { + Array.Reverse(b); + b[0] ^= 0x80; + } + s.Write(b, 0, b.Length); + } + + internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) + { + byte[] b = new byte[8]; + s.Read(b, 0, b.Length); + if (!_skipNormalize) + { + b[0] ^= 0x80; + Array.Reverse(b); + } + SetValue(fi, recvr, BitConverter.ToInt64(b, 0)); + } + + internal override int Size => 8; + } + + internal sealed class ULongNormalizer : Normalizer + { + internal override void Normalize(FieldInfo fi, object obj, Stream s) + { + byte[] b = BitConverter.GetBytes((ulong)GetValue(fi, obj)); + if (!_skipNormalize) + { + Array.Reverse(b); + } + s.Write(b, 0, b.Length); + } + + internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) + { + byte[] b = new byte[8]; + s.Read(b, 0, b.Length); + if (!_skipNormalize) + { + Array.Reverse(b); + } + SetValue(fi, recvr, BitConverter.ToUInt64(b, 0)); + } + + internal override int Size => 8; + } + + internal sealed class FloatNormalizer : Normalizer + { + internal override void Normalize(FieldInfo fi, object obj, Stream s) + { + float f = (float)GetValue(fi, obj); + byte[] b = BitConverter.GetBytes(f); + if (!_skipNormalize) + { + Array.Reverse(b); + if ((b[0] & 0x80) == 0) + { + // This is a positive number. + // Flip the highest bit + b[0] ^= 0x80; + } + else + { + // This is a negative number. + + // If all zeroes, means it was a negative zero. + // Treat it same as positive zero, so that + // the normalized key will compare equal. + if (f < 0) + FlipAllBits(b); + } + } + s.Write(b, 0, b.Length); + } + + internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) + { + byte[] b = new byte[4]; + s.Read(b, 0, b.Length); + if (!_skipNormalize) + { + if ((b[0] & 0x80) > 0) + { + // This is a positive number. + // Flip the highest bit + b[0] ^= 0x80; + } + else + { + // This is a negative number. + FlipAllBits(b); + } + Array.Reverse(b); + } + SetValue(fi, recvr, BitConverter.ToSingle(b, 0)); + } + + internal override int Size => 4; + } + + internal sealed class DoubleNormalizer : Normalizer + { + internal override void Normalize(FieldInfo fi, object obj, Stream s) + { + double d = (double)GetValue(fi, obj); + byte[] b = BitConverter.GetBytes(d); + if (!_skipNormalize) + { + Array.Reverse(b); + if ((b[0] & 0x80) == 0) + { + // This is a positive number. + // Flip the highest bit + b[0] ^= 0x80; + } + else + { + // This is a negative number. + if (d < 0) + { + // If all zeroes, means it was a negative zero. + // Treat it same as positive zero, so that + // the normalized key will compare equal. + FlipAllBits(b); + } + } + } + s.Write(b, 0, b.Length); + } + + internal override void DeNormalize(FieldInfo fi, object recvr, Stream s) + { + byte[] b = new byte[8]; + s.Read(b, 0, b.Length); + if (!_skipNormalize) + { + if ((b[0] & 0x80) > 0) + { + // This is a positive number. + // Flip the highest bit + b[0] ^= 0x80; + } + else + { + // This is a negative number. + FlipAllBits(b); + } + Array.Reverse(b); + } + SetValue(fi, recvr, BitConverter.ToDouble(b, 0)); + } + + internal override int Size => 8; + } +} \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlSer.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlSer.cs new file mode 100644 index 0000000000..020d6e7c78 --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlSer.cs @@ -0,0 +1,248 @@ +// 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.Collections; +using System.Data.Common; +using System.Data.SqlClient; +using System.IO; +using System.Runtime.CompilerServices; + +namespace Microsoft.SqlServer.Server +{ + internal class SerializationHelperSql9 + { + // Don't let anyone create an instance of this class. + private SerializationHelperSql9() { } + + // Get the m_size of the serialized stream for this type, in bytes. + // This method creates an instance of the type using the public + // no-argument constructor, serializes it, and returns the m_size + // in bytes. + // Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set. + [MethodImpl(MethodImplOptions.NoInlining)] + internal static int SizeInBytes(Type t) => SizeInBytes(Activator.CreateInstance(t)); + + // Get the m_size of the serialized stream for this type, in bytes. + internal static int SizeInBytes(object instance) + { + Type t = instance.GetType(); + Format k = GetFormat(t); + DummyStream stream = new DummyStream(); + Serializer ser = GetSerializer(instance.GetType()); + ser.Serialize(stream, instance); + return (int)stream.Length; + } + + internal static void Serialize(Stream s, object instance) + { + GetSerializer(instance.GetType()).Serialize(s, instance); + } + + internal static object Deserialize(Stream s, Type resultType) => GetSerializer(resultType).Deserialize(s); + + private static Format GetFormat(Type t) => GetUdtAttribute(t).Format; + + // Cache the relationship between a type and its serializer. + // This is expensive to compute since it involves traversing the + // custom attributes of the type using reflection. + // + // Use a per-thread cache, so that there are no synchronization + // issues when accessing cache entries from multiple threads. + [ThreadStatic] + private static Hashtable s_types2Serializers; + + private static Serializer GetSerializer(Type t) + { + if (s_types2Serializers == null) + s_types2Serializers = new Hashtable(); + + Serializer s = (Serializer)s_types2Serializers[t]; + if (s == null) + { + s = GetNewSerializer(t); + s_types2Serializers[t] = s; + } + return s; + } + + internal static int GetUdtMaxLength(Type t) + { + SqlUdtInfo udtInfo = SqlUdtInfo.GetFromType(t); + + if (Format.Native == udtInfo.SerializationFormat) + { + // In the native format, the user does not specify the + // max byte size, it is computed from the type definition + return SizeInBytes(t); + } + else + { + // In all other formats, the user specifies the maximum size in bytes. + return udtInfo.MaxByteSize; + } + } + + private static object[] GetCustomAttributes(Type t) + { + return t.GetCustomAttributes(typeof(SqlUserDefinedTypeAttribute), false); + } + + internal static SqlUserDefinedTypeAttribute GetUdtAttribute(Type t) + { + SqlUserDefinedTypeAttribute udtAttr = null; + object[] attr = GetCustomAttributes(t); + + if (attr != null && attr.Length == 1) + { + udtAttr = (SqlUserDefinedTypeAttribute)attr[0]; + } + else + { + throw InvalidUdtException.Create(t, SR.SqlUdtReason_NoUdtAttribute); + } + return udtAttr; + } + + // Create a new serializer for the given type. + private static Serializer GetNewSerializer(Type t) + { + SqlUserDefinedTypeAttribute udtAttr = GetUdtAttribute(t); + Format k = GetFormat(t); + + switch (k) + { + case Format.Native: + return new NormalizedSerializer(t); + case Format.UserDefined: + return new BinarySerializeSerializer(t); + case Format.Unknown: // should never happen, but fall through + default: + throw ADP.InvalidUserDefinedTypeSerializationFormat(k); + } + } + } + + // The base serializer class. + internal abstract class Serializer + { + public abstract object Deserialize(Stream s); + public abstract void Serialize(Stream s, object o); + protected Type _type; + + protected Serializer(Type t) + { + _type = t; + } + } + + internal sealed class NormalizedSerializer : Serializer + { + private BinaryOrderedUdtNormalizer _normalizer; + private bool _isFixedSize; + private int _maxSize; + + internal NormalizedSerializer(Type t) : base(t) + { + SqlUserDefinedTypeAttribute udtAttr = SerializationHelperSql9.GetUdtAttribute(t); + _normalizer = new BinaryOrderedUdtNormalizer(t, true); + _isFixedSize = udtAttr.IsFixedLength; + _maxSize = _normalizer.Size; + } + + public override void Serialize(Stream s, object o) + { + _normalizer.NormalizeTopObject(o, s); + } + + public override object Deserialize(Stream s) => _normalizer.DeNormalizeTopObject(_type, s); + } + + internal sealed class BinarySerializeSerializer : Serializer + { + internal BinarySerializeSerializer(Type t) : base(t) + { + } + + public override void Serialize(Stream s, object o) + { + BinaryWriter w = new BinaryWriter(s); + ((IBinarySerialize)o).Write(w); + } + + // Prevent inlining so that reflection calls are not moved + // to a caller that may be in a different assembly that may + // have a different grant set. + [MethodImpl(MethodImplOptions.NoInlining)] + public override object Deserialize(Stream s) + { + object instance = Activator.CreateInstance(_type); + BinaryReader r = new BinaryReader(s); + ((IBinarySerialize)instance).Read(r); + return instance; + } + } + + // A dummy stream class, used to get the number of bytes written + // to the stream. + internal sealed class DummyStream : Stream + { + private long _size; + + public DummyStream() + { + } + + private void DontDoIt() + { + throw new Exception(SR.GetString(SR.Sql_InternalError)); + } + + public override bool CanRead => false; + + public override bool CanWrite => true; + + public override bool CanSeek => false; + + public override long Position + { + get + { + return _size; + } + set + { + _size = value; + } + } + + public override long Length => _size; + + public override void SetLength(long value) + { + _size = value; + } + + public override long Seek(long value, SeekOrigin loc) + { + DontDoIt(); + return -1; + } + + public override void Flush() + { + } + + public override int Read(byte[] buffer, int offset, int count) + { + DontDoIt(); + return -1; + } + + public override void Write(byte[] buffer, int offset, int count) + { + _size += count; + } + } +} \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlUserDefinedAggregateAttribute.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlUserDefinedAggregateAttribute.cs new file mode 100644 index 0000000000..d59371bd96 --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlUserDefinedAggregateAttribute.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. +// See the LICENSE file in the project root for more information. + +using System; +using System.Data.Common; + +namespace Microsoft.SqlServer.Server +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)] + public sealed class SqlUserDefinedAggregateAttribute : Attribute + { + private int _maxByteSize; + private bool _isInvariantToDup; + private bool _isInvariantToNulls; + private bool _isInvariantToOrder = true; + private bool _isNullIfEmpty; + private Format _format; + private string _name; + + // The maximum value for the maxbytesize field, in bytes. + public const int MaxByteSizeValue = 8000; + + // A required attribute on all UD Aggs, used to indicate that the + // given type is a UD Agg, and its storage format. + public SqlUserDefinedAggregateAttribute(Format format) + { + switch (format) + { + case Format.Unknown: + throw ADP.NotSupportedUserDefinedTypeSerializationFormat(format, nameof(format)); + case Format.Native: + case Format.UserDefined: + _format = format; + break; + default: + throw ADP.InvalidUserDefinedTypeSerializationFormat(format); + } + } + + // The maximum size of this instance, in bytes. Does not have to be + // specified for Native format serialization. The maximum value + // for this property is specified by MaxByteSizeValue. + public int MaxByteSize + { + get + { + return _maxByteSize; + } + set + { + // MaxByteSize of -1 means 2GB and is valid, as well as 0 to MaxByteSizeValue + if (value < -1 || value > MaxByteSizeValue) + { + throw ADP.ArgumentOutOfRange(SR.GetString(SR.SQLUDT_MaxByteSizeValue), nameof(MaxByteSize), value); + } + _maxByteSize = value; + } + } + + public bool IsInvariantToDuplicates + { + get + { + return _isInvariantToDup; + } + set + { + _isInvariantToDup = value; + } + } + + public bool IsInvariantToNulls + { + get + { + return _isInvariantToNulls; + } + set + { + _isInvariantToNulls = value; + } + } + + public bool IsInvariantToOrder + { + get + { + return _isInvariantToOrder; + } + set + { + _isInvariantToOrder = value; + } + } + + public bool IsNullIfEmpty + { + get + { + return _isNullIfEmpty; + } + set + { + _isNullIfEmpty = value; + } + } + + // The on-disk format for this type. + public Format Format => _format; + + public string Name + { + get + { + return _name; + } + set + { + _name = value; + } + } + } +} \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlUserDefinedTypeAttribute.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlUserDefinedTypeAttribute.cs new file mode 100644 index 0000000000..fc6e08e796 --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlUserDefinedTypeAttribute.cs @@ -0,0 +1,128 @@ +// 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.Data.Common; + +namespace Microsoft.SqlServer.Server +{ + public enum Format + { + Unknown = 0, + Native = 1, + UserDefined = 2, + } + + // This custom attribute indicates that the given type is + // a SqlServer udt. The properties on the attribute reflect the + // physical attributes that will be used when the type is registered + // with SqlServer. + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = true)] + public sealed class SqlUserDefinedTypeAttribute : Attribute + { + private int _maxByteSize; + private bool _isFixedLength; + private bool _isByteOrdered; + private Format _format; + private string _name; + + // The maximum value for the maxbytesize field, in bytes. + internal const int YukonMaxByteSizeValue = 8000; + private string _validationMethodName = null; + + // A required attribute on all udts, used to indicate that the + // given type is a udt, and its storage format. + public SqlUserDefinedTypeAttribute(Format format) + { + switch (format) + { + case Format.Unknown: + throw ADP.NotSupportedUserDefinedTypeSerializationFormat(format, nameof(format)); + case Format.Native: + case Format.UserDefined: + _format = format; + break; + default: + throw ADP.InvalidUserDefinedTypeSerializationFormat(format); + } + } + + // The maximum size of this instance, in bytes. Does not have to be + // specified for Native serialization. The maximum value + // for this property is specified by MaxByteSizeValue. + public int MaxByteSize + { + get + { + return _maxByteSize; + } + set + { + if (value < -1) + { + throw ADP.ArgumentOutOfRange(nameof(MaxByteSize)); + } + _maxByteSize = value; + } + } + + // Are all instances of this udt the same size on disk? + public bool IsFixedLength + { + get + { + return _isFixedLength; + } + set + { + _isFixedLength = value; + } + } + + // Is this type byte ordered, i.e. is the on disk representation + // consistent with the ordering semantics for this type? + // If true, the binary representation of the type will be used + // in comparison by SqlServer. This property enables indexing on the + // udt and faster comparisons. + public bool IsByteOrdered + { + get + { + return _isByteOrdered; + } + set + { + _isByteOrdered = value; + } + } + + // The on-disk format for this type. + public Format Format => _format; + + // An Optional method used to validate this UDT + public string ValidationMethodName + { + get + { + return _validationMethodName; + } + set + { + _validationMethodName = value; + } + } + + public string Name + { + get + { + return _name; + } + set + { + _name = value; + } + } + } +} diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SNI/SNIProxy.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SNI/SNIProxy.cs index 11c49447ce..a93ff8a044 100644 --- a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SNI/SNIProxy.cs +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SNI/SNIProxy.cs @@ -105,6 +105,7 @@ namespace System.Data.SqlClient.SNI ContextFlagsPal requestedContextFlags = ContextFlagsPal.Connection | ContextFlagsPal.Confidentiality + | ContextFlagsPal.Delegate | ContextFlagsPal.MutualAuth; string serverSPN = System.Text.Encoding.UTF8.GetString(serverName); @@ -144,11 +145,11 @@ namespace System.Data.SqlClient.SNI // so we don't need to check for a GssApiException here. if (statusCode.ErrorCode == SecurityStatusPalErrorCode.InternalError) { - throw new Exception(SQLMessage.KerberosTicketMissingError() + "\n" + statusCode); + throw new InvalidOperationException(SQLMessage.KerberosTicketMissingError() + "\n" + statusCode); } else { - throw new Exception(SQLMessage.SSPIGenerateError() + "\n" + statusCode); + throw new InvalidOperationException(SQLMessage.SSPIGenerateError() + "\n" + statusCode); } } } diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SNI/SNITcpHandle.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SNI/SNITcpHandle.cs index 4b9f0a8c6a..5f1be065a3 100644 --- a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SNI/SNITcpHandle.cs +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SNI/SNITcpHandle.cs @@ -137,19 +137,20 @@ namespace System.Data.SqlClient.SNI } connectTask = ParallelConnectAsync(serverAddresses, port); + + if (!(isInfiniteTimeOut ? connectTask.Wait(-1) : connectTask.Wait(ts))) + { + ReportTcpSNIError(0, SNICommon.ConnOpenFailedError, string.Empty); + return; + } + + _socket = connectTask.Result; } else { - connectTask = ConnectAsync(serverName, port); + _socket = Connect(serverName, port, ts); } - - if (!(isInfiniteTimeOut ? connectTask.Wait(-1) : connectTask.Wait(ts))) - { - ReportTcpSNIError(0, SNICommon.ConnOpenFailedError, string.Empty); - return; - } - - _socket = connectTask.Result; + if (_socket == null || !_socket.Connected) { if (_socket != null) @@ -182,31 +183,81 @@ namespace System.Data.SqlClient.SNI _status = TdsEnums.SNI_SUCCESS; } - private static async Task ConnectAsync(string serverName, int port) + private static Socket Connect(string serverName, int port, TimeSpan timeout) { - IPAddress[] addresses = await Dns.GetHostAddressesAsync(serverName).ConfigureAwait(false); - IPAddress targetAddrV4 = Array.Find(addresses, addr => (addr.AddressFamily == AddressFamily.InterNetwork)); - IPAddress targetAddrV6 = Array.Find(addresses, addr => (addr.AddressFamily == AddressFamily.InterNetworkV6)); - if (targetAddrV4 != null && targetAddrV6 != null) + IPAddress[] ipAddresses = Dns.GetHostAddresses(serverName); + IPAddress serverIPv4 = null; + IPAddress serverIPv6 = null; + foreach (IPAddress ipAdress in ipAddresses) { - return await ParallelConnectAsync(new IPAddress[] { targetAddrV4, targetAddrV6 }, port).ConfigureAwait(false); + if (ipAdress.AddressFamily == AddressFamily.InterNetwork) + { + serverIPv4 = ipAdress; + } + else if (ipAdress.AddressFamily == AddressFamily.InterNetworkV6) + { + serverIPv6 = ipAdress; + } } - else - { - IPAddress targetAddr = (targetAddrV4 != null) ? targetAddrV4 : targetAddrV6; - var socket = new Socket(targetAddr.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + ipAddresses = new IPAddress[] { serverIPv4, serverIPv6 }; + Socket[] sockets = new Socket[2]; + CancellationTokenSource cts = new CancellationTokenSource(); + cts.CancelAfter(timeout); +#if __MonoCS__ + cts.Token.Register(delegate +#else + void Cancel() +#endif + { + for (int i = 0; i < sockets.Length; ++i) + { + try + { + if (sockets[i] != null && !sockets[i].Connected) + { + sockets[i].Dispose(); + sockets[i] = null; + } + } + catch { } + } + } + +#if __MonoCS__ + ); +#else + cts.Token.Register(Cancel); +#endif + + Socket availableSocket = null; + for (int i = 0; i < sockets.Length; ++i) + { try { - await socket.ConnectAsync(targetAddr, port).ConfigureAwait(false); + if (ipAddresses[i] != null) + { + sockets[i] = new Socket(ipAddresses[i].AddressFamily, SocketType.Stream, ProtocolType.Tcp); + sockets[i].Connect(ipAddresses[i], port); + if (sockets[i] != null) // sockets[i] can be null if cancel callback is executed during connect() + { + if (sockets[i].Connected) + { + availableSocket = sockets[i]; + break; + } + else + { + sockets[i].Dispose(); + sockets[i] = null; + } + } + } } - catch - { - socket.Dispose(); - throw; - } - return socket; + catch { } } + + return availableSocket; } private static Task ParallelConnectAsync(IPAddress[] serverAddresses, int port) @@ -320,7 +371,7 @@ namespace System.Data.SqlClient.SNI try { - _sslStream.AuthenticateAsClientAsync(_targetServer).GetAwaiter().GetResult(); + _sslStream.AuthenticateAsClient(_targetServer); _sslOverTdsStream.FinishHandshake(); } catch (AuthenticationException aue) @@ -341,11 +392,10 @@ namespace System.Data.SqlClient.SNI /// public override void DisableSsl() { -#if MONO - // Temp workaround - SSLStream.Dispose causes an expected behavior in mono for windows - if (Environment.OSVersion.Platform != PlatformID.Win32NT) -#endif +#if !MONO || MONO_FEATURE_BTLS || MONO_FEATURE_APPLETLS + // SSLStream.Dispose causes an unexpected behavior with legacy ssl implementation _sslStream.Dispose(); +#endif _sslStream = null; _sslOverTdsStream.Dispose(); _sslOverTdsStream = null; @@ -378,8 +428,6 @@ namespace System.Data.SqlClient.SNI public override void SetBufferSize(int bufferSize) { _bufferSize = bufferSize; - _socket.SendBufferSize = bufferSize; - _socket.ReceiveBufferSize = bufferSize; } /// diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlBulkCopy.cs.REMOVED.git-id b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlBulkCopy.cs.REMOVED.git-id index 9e3d78e332..4440702252 100644 --- a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlBulkCopy.cs.REMOVED.git-id +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlBulkCopy.cs.REMOVED.git-id @@ -1 +1 @@ -ca6aaa8e5e52241db96ee4cf49def4a66255ef83 \ No newline at end of file +e1d89c9d2a06c8e89a8b4bf216000f8970d48f26 \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlCommand.cs.REMOVED.git-id b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlCommand.cs.REMOVED.git-id index 76674a8aa1..b72426d173 100644 --- a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlCommand.cs.REMOVED.git-id +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlCommand.cs.REMOVED.git-id @@ -1 +1 @@ -ec32c2f52b30d02516a9dd668091384b81a62db6 \ No newline at end of file +38e0b56f769c5af73557977145de437792eed731 \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlCommandBuilder.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlCommandBuilder.cs index 1dc7e618b0..cf6fba58db 100644 --- a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlCommandBuilder.cs +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlCommandBuilder.cs @@ -148,6 +148,15 @@ namespace System.Data.SqlClient p.SqlDbType = (SqlDbType)valueType; p.Offset = 0; + if ((p.SqlDbType == SqlDbType.Udt) && !p.SourceColumnNullMapping) + { + p.UdtTypeName = datarow["DataTypeName"] as string; + } + else + { + p.UdtTypeName = string.Empty; + } + object bvalue = datarow[SchemaTableColumn.NumericPrecision]; if (DBNull.Value != bvalue) { diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlConnection.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlConnection.cs index 5dab5aecb2..b2c20ecf28 100644 --- a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlConnection.cs +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlConnection.cs @@ -10,6 +10,10 @@ using System.Threading; using System.Threading.Tasks; using System.Diagnostics.CodeAnalysis; using System.Transactions; +using Microsoft.SqlServer.Server; +using System.Reflection; +using System.IO; +using System.Globalization; namespace System.Data.SqlClient { @@ -135,42 +139,31 @@ namespace System.Data.SqlClient internal bool AsyncCommandInProgress { - get - { - return (_AsyncCommandInProgress); - } - set - { - _AsyncCommandInProgress = value; - } + get => _AsyncCommandInProgress; + set => _AsyncCommandInProgress = value; } internal SqlConnectionString.TransactionBindingEnum TransactionBinding { - get - { - return ((SqlConnectionString)ConnectionOptions).TransactionBinding; - } + get => ((SqlConnectionString)ConnectionOptions).TransactionBinding; } internal SqlConnectionString.TypeSystem TypeSystem { - get - { - return ((SqlConnectionString)ConnectionOptions).TypeSystemVersion; - } + get => ((SqlConnectionString)ConnectionOptions).TypeSystemVersion; } + internal Version TypeSystemAssemblyVersion + { + get => ((SqlConnectionString)ConnectionOptions).TypeSystemAssemblyVersion; + } internal int ConnectRetryInterval { - get - { - return ((SqlConnectionString)ConnectionOptions).ConnectRetryInterval; - } + get => ((SqlConnectionString)ConnectionOptions).ConnectRetryInterval; } - override public string ConnectionString + public override string ConnectionString { get { @@ -184,7 +177,7 @@ namespace System.Data.SqlClient } } - override public int ConnectionTimeout + public override int ConnectionTimeout { get { @@ -193,7 +186,7 @@ namespace System.Data.SqlClient } } - override public string Database + public override string Database { // if the connection is open, we need to ask the inner connection what it's // current catalog is because it may have gotten changed, otherwise we can @@ -216,7 +209,7 @@ namespace System.Data.SqlClient } } - override public string DataSource + public override string DataSource { get { @@ -281,15 +274,12 @@ namespace System.Data.SqlClient } } - override public string ServerVersion + public override string ServerVersion { - get - { - return GetOpenTdsConnection().ServerVersion; - } + get => GetOpenTdsConnection().ServerVersion; } - override public ConnectionState State + public override ConnectionState State { get { @@ -305,10 +295,7 @@ namespace System.Data.SqlClient internal SqlStatistics Statistics { - get - { - return _statistics; - } + get => _statistics; } public string WorkstationId @@ -326,7 +313,7 @@ namespace System.Data.SqlClient protected override DbProviderFactory DbProviderFactory { - get { return SqlClientFactory.Instance; } + get => SqlClientFactory.Instance; } // SqlCredential: Pair User Id and password in SecureString which are to be used for SQL authentication @@ -339,23 +326,14 @@ namespace System.Data.SqlClient public bool FireInfoMessageEventOnUserErrors { - get - { - return _fireInfoMessageEventOnUserErrors; - } - set - { - _fireInfoMessageEventOnUserErrors = value; - } + get => _fireInfoMessageEventOnUserErrors; + set => _fireInfoMessageEventOnUserErrors = value; } // Approx. number of times that the internal connection has been reconnected internal int ReconnectCount { - get - { - return _reconnectCount; - } + get => _reconnectCount; } internal bool ForceNewConnection { get; set; } @@ -439,7 +417,7 @@ namespace System.Data.SqlClient } } - override public void ChangeDatabase(string database) + public override void ChangeDatabase(string database) { SqlStatistics statistics = null; RepairInnerConnection(); @@ -481,7 +459,7 @@ namespace System.Data.SqlClient InnerConnection.CloseConnection(this, ConnectionFactory); } - override public void Close() + public override void Close() { ConnectionState previousState = State; Guid operationId = default(Guid); @@ -582,7 +560,7 @@ namespace System.Data.SqlClient } - override public void Open() + public override void Open() { Guid operationId = s_diagnosticListener.WriteConnectionOpenBefore(this); @@ -1033,7 +1011,7 @@ namespace System.Data.SqlClient return false; } } - // does not require GC.KeepAlive(this) because of OnStateChange + // does not require GC.KeepAlive(this) because of ReRegisterForFinalize below. var tdsInnerConnection = (SqlInternalConnectionTds)InnerConnection; @@ -1325,7 +1303,126 @@ namespace System.Data.SqlClient _innerConnection = DbConnectionClosedPreviouslyOpened.SingletonInstance; } } - } // SqlConnection -} // System.Data.SqlClient namespace + + // UDT SUPPORT + private Assembly ResolveTypeAssembly(AssemblyName asmRef, bool throwOnError) + { + Debug.Assert(TypeSystemAssemblyVersion != null, "TypeSystemAssembly should be set !"); + if (string.Compare(asmRef.Name, "Microsoft.SqlServer.Types", StringComparison.OrdinalIgnoreCase) == 0) + { + asmRef.Version = TypeSystemAssemblyVersion; + } + try + { + return Assembly.Load(asmRef); + } + catch (Exception e) + { + if (throwOnError || !ADP.IsCatchableExceptionType(e)) + { + throw; + } + else + { + return null; + }; + } + } + + internal void CheckGetExtendedUDTInfo(SqlMetaDataPriv metaData, bool fThrow) + { + if (metaData.udtType == null) + { // If null, we have not obtained extended info. + Debug.Assert(!string.IsNullOrEmpty(metaData.udtAssemblyQualifiedName), "Unexpected state on GetUDTInfo"); + // Parameter throwOnError determines whether exception from Assembly.Load is thrown. + metaData.udtType = + Type.GetType(typeName: metaData.udtAssemblyQualifiedName, assemblyResolver: asmRef => ResolveTypeAssembly(asmRef, fThrow), typeResolver: null, throwOnError: fThrow); + + if (fThrow && metaData.udtType == null) + { + throw SQL.UDTUnexpectedResult(metaData.udtAssemblyQualifiedName); + } + } + } + + internal object GetUdtValue(object value, SqlMetaDataPriv metaData, bool returnDBNull) + { + if (returnDBNull && ADP.IsNull(value)) + { + return DBNull.Value; + } + + object o = null; + + // Since the serializer doesn't handle nulls... + if (ADP.IsNull(value)) + { + Type t = metaData.udtType; + Debug.Assert(t != null, "Unexpected null of udtType on GetUdtValue!"); + o = t.InvokeMember("Null", BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Static, null, null, new object[] { }, CultureInfo.InvariantCulture); + Debug.Assert(o != null); + return o; + } + else + { + + MemoryStream stm = new MemoryStream((byte[])value); + + o = SerializationHelperSql9.Deserialize(stm, metaData.udtType); + + Debug.Assert(o != null, "object could NOT be created"); + return o; + } + } + + internal byte[] GetBytes(object o) + { + Format format = Format.Native; + return GetBytes(o, out format, out int maxSize); + } + + internal byte[] GetBytes(object o, out Format format, out int maxSize) + { + SqlUdtInfo attr = GetInfoFromType(o.GetType()); + maxSize = attr.MaxByteSize; + format = attr.SerializationFormat; + + if (maxSize < -1 || maxSize >= ushort.MaxValue) + { + throw new InvalidOperationException(o.GetType() + ": invalid Size"); + } + + byte[] retval; + + using (MemoryStream stm = new MemoryStream(maxSize < 0 ? 0 : maxSize)) + { + SerializationHelperSql9.Serialize(stm, o); + retval = stm.ToArray(); + } + return retval; + } + + private SqlUdtInfo GetInfoFromType(Type t) + { + Debug.Assert(t != null, "Type object cant be NULL"); + Type orig = t; + do + { + SqlUdtInfo attr = SqlUdtInfo.TryGetFromType(t); + if (attr != null) + { + return attr; + } + else + { + t = t.BaseType; + } + } + while (t != null); + + throw SQL.UDTInvalidSqlType(orig.AssemblyQualifiedName); + } + } +} diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlConnectionString.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlConnectionString.cs index 008f6ba7ca..1497f55ec6 100644 --- a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlConnectionString.cs +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlConnectionString.cs @@ -197,10 +197,13 @@ namespace System.Data.SqlClient private readonly string _workstationId; - private readonly TypeSystem _typeSystemVersion; - private readonly TransactionBindingEnum _transactionBinding; + private readonly TypeSystem _typeSystemVersion; + private readonly Version _typeSystemAssemblyVersion; + private static readonly Version constTypeSystemAsmVersion10 = new Version("10.0.0.0"); + private static readonly Version constTypeSystemAsmVersion11 = new Version("11.0.0.0"); + internal SqlConnectionString(string connectionString) : base(connectionString, GetParseSynonyms()) { ThrowUnsupportedIfKeywordSet(KEY.AsynchronousProcessing); @@ -317,6 +320,7 @@ namespace System.Data.SqlClient ValidateValueLength(_attachDBFileName, TdsEnums.MAXLEN_ATTACHDBFILE, KEY.AttachDBFilename); } + _typeSystemAssemblyVersion = constTypeSystemAsmVersion10; if (true == _userInstance && !string.IsNullOrEmpty(_failoverPartner)) { @@ -347,6 +351,7 @@ namespace System.Data.SqlClient else if (typeSystemVersionString.Equals(TYPESYSTEMVERSION.SQL_Server_2012, StringComparison.OrdinalIgnoreCase)) { _typeSystemVersion = TypeSystem.SQLServer2012; + _typeSystemAssemblyVersion = constTypeSystemAsmVersion11; } else { @@ -472,6 +477,7 @@ namespace System.Data.SqlClient internal string WorkstationId { get { return _workstationId; } } internal TypeSystem TypeSystemVersion { get { return _typeSystemVersion; } } + internal Version TypeSystemAssemblyVersion { get { return _typeSystemAssemblyVersion; } } internal TransactionBindingEnum TransactionBinding { get { return _transactionBinding; } } diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlDataReader.cs.REMOVED.git-id b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlDataReader.cs.REMOVED.git-id index 43c907d9b7..e8cb87fbe5 100644 --- a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlDataReader.cs.REMOVED.git-id +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlDataReader.cs.REMOVED.git-id @@ -1 +1 @@ -708c6f08b7f78cdb16c99827da906e5cf84d774c \ No newline at end of file +9bc4bfd7b406ae741098fe1a1f82b961cf469b32 \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlDelegatedTransaction.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlDelegatedTransaction.cs index d0e60ab389..1c8d9ef89e 100644 --- a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlDelegatedTransaction.cs +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlDelegatedTransaction.cs @@ -1,4 +1,8 @@ -using System.Data.Common; +// 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.Data.Common; using System.Data.SqlClient; using System.Diagnostics; using System.Reflection; diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlEnums.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlEnums.cs index 52c4da2693..29aa355131 100644 --- a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlEnums.cs +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlEnums.cs @@ -2,10 +2,6 @@ // 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.Collections.Generic; using System.Data.Common; using System.Data.SqlTypes; @@ -27,7 +23,7 @@ namespace System.Data.SqlClient internal readonly bool IsFixed; // true if fixed length, note that sqlchar and sqlbinary are not considered fixed length internal readonly bool IsLong; // true if long internal readonly bool IsPlp; // Column is Partially Length Prefixed (MAX) - internal readonly byte Precision; // maximum precision for numeric types + internal readonly byte Precision; // maximum precision for numeric types internal readonly byte Scale; internal readonly byte TDSType; internal readonly byte NullableType; @@ -62,7 +58,7 @@ namespace System.Data.SqlClient this.IsFixed = isFixed; this.IsLong = isLong; this.IsPlp = isPlp; - // can we get rid of this (?just have a mapping?) + this.TDSType = tdsType; this.NullableType = nullableTdsType; this.TypeName = typeName; @@ -290,110 +286,27 @@ namespace System.Data.SqlClient // // map COM+ Type to MetaType class // - internal static MetaType GetMetaTypeFromType(Type dataType, bool streamAllowed = true) + internal static MetaType GetMetaTypeFromType(Type dataType) { - if (dataType == typeof(System.Byte[])) - return MetaVarBinary; - else if (dataType == typeof(System.Guid)) - return s_metaUniqueId; - else if (dataType == typeof(System.Object)) - return s_metaVariant; - else if (dataType == typeof(SqlBinary)) - return MetaVarBinary; - else if (dataType == typeof(SqlBoolean)) - return s_metaBit; - else if (dataType == typeof(SqlByte)) - return s_metaTinyInt; - else if (dataType == typeof(SqlBytes)) - return MetaVarBinary; - else if (dataType == typeof(SqlChars)) - return MetaNVarChar; - else if (dataType == typeof(SqlDateTime)) - return s_metaDateTime; - else if (dataType == typeof(SqlDouble)) - return s_metaFloat; - else if (dataType == typeof(SqlGuid)) - return s_metaUniqueId; - else if (dataType == typeof(SqlInt16)) - return s_metaSmallInt; - else if (dataType == typeof(SqlInt32)) - return s_metaInt; - else if (dataType == typeof(SqlInt64)) - return s_metaBigInt; - else if (dataType == typeof(SqlMoney)) - return s_metaMoney; - else if (dataType == typeof(SqlDecimal)) - return MetaDecimal; - else if (dataType == typeof(SqlSingle)) - return s_metaReal; - else if (dataType == typeof(SqlXml)) - return MetaXml; - else if (dataType == typeof(SqlString)) - return MetaNVarChar; - else if (dataType == typeof(IEnumerable)) - return s_metaTable; - else if (dataType == typeof(TimeSpan)) - return MetaTime; - else if (dataType == typeof(DateTimeOffset)) - return MetaDateTimeOffset; - else if (dataType == typeof(DBNull)) - throw ADP.InvalidDataType(nameof(DBNull)); - else if (dataType == typeof(Boolean)) - return s_metaBit; - else if (dataType == typeof(Char)) - throw ADP.InvalidDataType(nameof(Char)); - else if (dataType == typeof(SByte)) - throw ADP.InvalidDataType(nameof(SByte)); - else if (dataType == typeof(Byte)) - return s_metaTinyInt; - else if (dataType == typeof(Int16)) - return s_metaSmallInt; - else if (dataType == typeof(UInt16)) - throw ADP.InvalidDataType(nameof(UInt16)); - else if (dataType == typeof(Int32)) - return s_metaInt; - else if (dataType == typeof(UInt32)) - throw ADP.InvalidDataType(nameof(UInt32)); - else if (dataType == typeof(Int64)) - return s_metaBigInt; - else if (dataType == typeof(UInt64)) - throw ADP.InvalidDataType(nameof(UInt64)); - else if (dataType == typeof(Single)) - return s_metaReal; - else if (dataType == typeof(Double)) - return s_metaFloat; - else if (dataType == typeof(Decimal)) - return MetaDecimal; - else if (dataType == typeof(DateTime)) - return s_metaDateTime; - else if (dataType == typeof(String)) - return MetaNVarChar; - else - throw ADP.UnknownDataType(dataType); + return GetMetaTypeFromValue(dataType, null, false, true); } - internal static MetaType GetMetaTypeFromValue(object value, bool inferLen = true, bool streamAllowed = true) + internal static MetaType GetMetaTypeFromValue(object value, bool streamAllowed = true) { - if (value == null) - { - throw ADP.InvalidDataType("null"); - } + return GetMetaTypeFromValue(value.GetType(), value, true, streamAllowed); + } - if (value is DBNull) - { - throw ADP.InvalidDataType(nameof(DBNull)); - } - - Type dataType = value.GetType(); - switch (Convert.GetTypeCode(value)) + private static MetaType GetMetaTypeFromValue(Type dataType, object value, bool inferLen, bool streamAllowed) + { + switch (Type.GetTypeCode(dataType)) { case TypeCode.Empty: - throw ADP.InvalidDataType(nameof(TypeCode.Empty)); + throw ADP.InvalidDataType(TypeCode.Empty); case TypeCode.Object: - - if (dataType == typeof (System.Byte[])) + if (dataType == typeof(System.Byte[])) { - if (!inferLen || ((byte[]) value).Length <= TdsEnums.TYPE_SIZE_LIMIT) + // Must not default to image if inferLen is false + if (!inferLen || ((byte[])value).Length <= TdsEnums.TYPE_SIZE_LIMIT) { return MetaVarBinary; } @@ -402,107 +315,113 @@ namespace System.Data.SqlClient return MetaImage; } } - if (dataType == typeof (System.Guid)) + else if (dataType == typeof(System.Guid)) { return s_metaUniqueId; } - if (dataType == typeof (System.Object)) + else if (dataType == typeof(System.Object)) { return s_metaVariant; - } - // check sql types now - if (dataType == typeof (SqlBinary)) + } // check sql types now + else if (dataType == typeof(SqlBinary)) return MetaVarBinary; - if (dataType == typeof (SqlBoolean)) + else if (dataType == typeof(SqlBoolean)) return s_metaBit; - if (dataType == typeof (SqlByte)) + else if (dataType == typeof(SqlByte)) return s_metaTinyInt; - if (dataType == typeof (SqlBytes)) + else if (dataType == typeof(SqlBytes)) return MetaVarBinary; - if (dataType == typeof (SqlChars)) + else if (dataType == typeof(SqlChars)) return MetaNVarChar; - if (dataType == typeof (SqlDateTime)) + else if (dataType == typeof(SqlDateTime)) return s_metaDateTime; - if (dataType == typeof (SqlDouble)) + else if (dataType == typeof(SqlDouble)) return s_metaFloat; - if (dataType == typeof (SqlGuid)) + else if (dataType == typeof(SqlGuid)) return s_metaUniqueId; - if (dataType == typeof (SqlInt16)) + else if (dataType == typeof(SqlInt16)) return s_metaSmallInt; - if (dataType == typeof (SqlInt32)) + else if (dataType == typeof(SqlInt32)) return s_metaInt; - if (dataType == typeof (SqlInt64)) + else if (dataType == typeof(SqlInt64)) return s_metaBigInt; - if (dataType == typeof (SqlMoney)) + else if (dataType == typeof(SqlMoney)) return s_metaMoney; - if (dataType == typeof (SqlDecimal)) + else if (dataType == typeof(SqlDecimal)) return MetaDecimal; - if (dataType == typeof (SqlSingle)) + else if (dataType == typeof(SqlSingle)) return s_metaReal; - if (dataType == typeof (SqlXml)) + else if (dataType == typeof(SqlXml)) return MetaXml; - if (dataType == typeof (SqlString)) + else if (dataType == typeof(SqlString)) { - return ((inferLen && !((SqlString) value).IsNull) - ? PromoteStringType(((SqlString) value).Value) + return ((inferLen && !((SqlString)value).IsNull) + ? PromoteStringType(((SqlString)value).Value) : MetaNVarChar); } - - if (dataType == typeof (IEnumerable) || dataType == typeof (DataTable)) + else if (dataType == typeof(IEnumerable) || dataType == typeof(DataTable)) { return s_metaTable; } - - if (dataType == typeof (TimeSpan)) + else if (dataType == typeof(TimeSpan)) { return MetaTime; } - - if (dataType == typeof (DateTimeOffset)) + else if (dataType == typeof(DateTimeOffset)) { return MetaDateTimeOffset; } - - if (streamAllowed) + else { - // Derived from Stream ? - if (value is Stream) + // UDT ? + SqlUdtInfo attribs = SqlUdtInfo.TryGetFromType(dataType); + if (attribs != null) { - return MetaVarBinary; + return MetaUdt; } - // Derived from TextReader ? - if (value is TextReader) + if (streamAllowed) { - return MetaNVarChar; - } - // Derived from XmlReader ? - if (value is XmlReader) - { - return MetaXml; + // Derived from Stream ? + if (typeof(Stream).IsAssignableFrom(dataType)) + { + return MetaVarBinary; + } + // Derived from TextReader ? + else if (typeof(TextReader).IsAssignableFrom(dataType)) + { + return MetaNVarChar; + } + // Derived from XmlReader ? + else if (typeof(System.Xml.XmlReader).IsAssignableFrom(dataType)) + { + return MetaXml; + } } } - - throw ADP.UnknownDataType(dataType); + throw ADP.UnknownDataType(dataType); + + case TypeCode.DBNull: + throw ADP.InvalidDataType(TypeCode.DBNull); case TypeCode.Boolean: return s_metaBit; case TypeCode.Char: - throw ADP.InvalidDataType(nameof(TypeCode.Char)); + throw ADP.InvalidDataType(TypeCode.Char); case TypeCode.SByte: - throw ADP.InvalidDataType(nameof(TypeCode.SByte)); + throw ADP.InvalidDataType(TypeCode.SByte); case TypeCode.Byte: return s_metaTinyInt; case TypeCode.Int16: return s_metaSmallInt; case TypeCode.UInt16: - throw ADP.InvalidDataType(nameof(TypeCode.UInt16)); + throw ADP.InvalidDataType(TypeCode.UInt16); case TypeCode.Int32: return s_metaInt; case TypeCode.UInt32: - throw ADP.InvalidDataType(nameof(TypeCode.UInt32)); + throw ADP.InvalidDataType(TypeCode.UInt32); case TypeCode.Int64: return s_metaBigInt; case TypeCode.UInt64: - throw ADP.InvalidDataType(nameof(TypeCode.UInt64)); + throw ADP.InvalidDataType(TypeCode.UInt64); case TypeCode.Single: return s_metaReal; case TypeCode.Double: @@ -512,9 +431,9 @@ namespace System.Data.SqlClient case TypeCode.DateTime: return s_metaDateTime; case TypeCode.String: - return (inferLen ? PromoteStringType((string) value) : MetaNVarChar); + return (inferLen ? PromoteStringType((string)value) : MetaNVarChar); default: - throw ADP.UnknownDataType(dataType); + throw ADP.UnknownDataTypeCode(dataType, Type.GetTypeCode(dataType)); } } @@ -542,7 +461,7 @@ namespace System.Data.SqlClient else if (sqlType == typeof(DateTimeOffset)) return DBNull.Value; else { - Debug.Assert(false, "Unknown SqlType!"); + Debug.Fail("Unknown SqlType!"); return DBNull.Value; } } @@ -595,13 +514,34 @@ namespace System.Data.SqlClient comVal = ((SqlXml)sqlVal).Value; else { - Debug.Assert(false, "unknown SqlType class stored in sqlVal"); + AssertIsUserDefinedTypeInstance(sqlVal, "unknown SqlType class stored in sqlVal"); } return comVal; } + /// + /// Assert that the supplied object is an instance of a SQL User-Defined Type (UDT). + /// + /// Object instance to be tested. + /// + /// This method is only compiled with debug builds, and it a helper method for the GetComValueFromSqlVariant method defined in this class. + /// + /// The presence of the SqlUserDefinedTypeAttribute on the object's type + /// is used to determine if the object is a UDT instance (if present it is a UDT, else it is not). + /// + /// + /// If sqlValue is null. Callers must ensure the object is non-null. + /// + [Conditional("DEBUG")] + private static void AssertIsUserDefinedTypeInstance(object sqlValue, string failedAssertMessage) + { + Type type = sqlValue.GetType(); + Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute[] attributes = (Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute[])type.GetCustomAttributes(typeof(Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute), true); + + Debug.Assert(attributes.Length > 0, failedAssertMessage); + } // devnote: This method should not be used with SqlDbType.Date and SqlDbType.DateTime2. // With these types the values should be used directly as CLR types instead of being converted to a SqlValue @@ -647,7 +587,7 @@ namespace System.Data.SqlClient sqlVal = comVal; #if DEBUG else - Debug.Assert(false, "unknown SqlType class stored in sqlVal"); + Debug.Fail("unknown SqlType class stored in sqlVal"); #endif } return sqlVal; @@ -671,20 +611,20 @@ namespace System.Data.SqlClient case TdsEnums.SQLVARBINARY: return s_metaSmallVarBinary; case TdsEnums.SQLBIGVARBINARY: return MetaVarBinary; - case TdsEnums.SQLVARCHAR: //goto TdsEnums.SQLBIGVARCHAR; + case TdsEnums.SQLVARCHAR: case TdsEnums.SQLBIGVARCHAR: return s_metaVarChar; - case TdsEnums.SQLBINARY: //goto TdsEnums.SQLBIGBINARY; + case TdsEnums.SQLBINARY: case TdsEnums.SQLBIGBINARY: return ((TdsEnums.SQLTIMESTAMP == userType) ? s_metaTimestamp : s_metaBinary); case TdsEnums.SQLIMAGE: return MetaImage; - case TdsEnums.SQLCHAR: //goto TdsEnums.SQLBIGCHAR; + case TdsEnums.SQLCHAR: case TdsEnums.SQLBIGCHAR: return s_metaChar; case TdsEnums.SQLINT1: return s_metaTinyInt; - case TdsEnums.SQLBIT: //goto TdsEnums.SQLBITN; + case TdsEnums.SQLBIT: case TdsEnums.SQLBITN: return s_metaBit; case TdsEnums.SQLINT2: return s_metaSmallInt; @@ -697,7 +637,7 @@ namespace System.Data.SqlClient case TdsEnums.SQLMONEY4: return s_metaSmallMoney; case TdsEnums.SQLDATETIM4: return s_metaSmallDateTime; - case TdsEnums.SQLDECIMALN: //goto TdsEnums.SQLNUMERICN; + case TdsEnums.SQLDECIMALN: case TdsEnums.SQLNUMERICN: return MetaDecimal; case TdsEnums.SQLUNIQUEID: return s_metaUniqueId; @@ -715,9 +655,9 @@ namespace System.Data.SqlClient case TdsEnums.SQLVOID: default: - Debug.Assert(false, "Unknown type " + tdsType.ToString(CultureInfo.InvariantCulture)); + Debug.Fail("Unknown type " + tdsType.ToString(CultureInfo.InvariantCulture)); throw SQL.InvalidSqlDbType((SqlDbType)tdsType); - }// case + } } internal static MetaType GetDefaultMetaType() @@ -756,9 +696,9 @@ namespace System.Data.SqlClient internal static readonly MetaType MetaMaxVarBinary = new MetaType (255, 255, -1, false, true, true, TdsEnums.SQLBIGVARBINARY, TdsEnums.SQLBIGVARBINARY, MetaTypeName.VARBINARY, typeof(System.Byte[]), typeof(SqlBinary), SqlDbType.VarBinary, DbType.Binary, 2); - // HACK!!! We have an internal type for smallvarbinarys stored on TdsEnums. We + // We have an internal type for smallvarbinarys stored on TdsEnums. We // store on TdsEnums instead of SqlDbType because we do not want to expose - // this type to the user! + // this type to the user. private static readonly MetaType s_metaSmallVarBinary = new MetaType (255, 255, -1, false, false, false, TdsEnums.SQLVARBINARY, TdsEnums.SQLBIGBINARY, ADP.StrEmpty, typeof(System.Byte[]), typeof(SqlBinary), TdsEnums.SmallVarBinary, DbType.Binary, 2); @@ -835,7 +775,7 @@ namespace System.Data.SqlClient (255, 255, -1, true, false, false, TdsEnums.SQLVARIANT, TdsEnums.SQLVARIANT, MetaTypeName.VARIANT, typeof(System.Object), typeof(System.Object), SqlDbType.Variant, DbType.Object, 0); internal static readonly MetaType MetaUdt = new MetaType - (255, 255, -1, false, false, true, TdsEnums.SQLUDT, TdsEnums.SQLUDT, MetaTypeName.UDT, typeof(System.Object), typeof(System.Object), SqlDbType.Udt, DbType.Object, 0); + (255, 255, -1, false, false, true, TdsEnums.SQLUDT, TdsEnums.SQLUDT, MetaTypeName.UDT, typeof(System.Object), typeof(System.Object), SqlDbType.Udt, DbType.Object, 0); private static readonly MetaType s_metaMaxUdt = new MetaType (255, 255, -1, false, true, true, TdsEnums.SQLUDT, TdsEnums.SQLUDT, MetaTypeName.UDT, typeof(System.Object), typeof(System.Object), SqlDbType.Udt, DbType.Object, 0); diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlException.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlException.cs index bf189e80de..bb8343491a 100644 --- a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlException.cs +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlException.cs @@ -17,6 +17,9 @@ using System.Text; // StringBuilder namespace System.Data.SqlClient { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public sealed partial class SqlException : System.Data.Common.DbException { private const string OriginalClientConnectionIdKey = "OriginalClientConnectionId"; @@ -33,9 +36,35 @@ namespace System.Data.SqlClient _clientConnectionId = conId; } + private SqlException(SerializationInfo si, StreamingContext sc) : base(si, sc) + { + HResult = SqlExceptionHResult; + foreach (SerializationEntry siEntry in si) + { + if ("ClientConnectionId" == siEntry.Name) + { + _clientConnectionId = (Guid)siEntry.Value; + break; + } + } + } + public override void GetObjectData(SerializationInfo si, StreamingContext context) { base.GetObjectData(si, context); + si.AddValue("Errors", null); // Not specifying type to enable serialization of null value of non-serializable type + si.AddValue("ClientConnectionId", _clientConnectionId, typeof(Guid)); + + // Writing sqlerrors to base exception data table + for (int i = 0; i < Errors.Count; i++) + { + string key = "SqlError " + (i + 1); + if (Data.Contains(key)) + { + Data.Remove(key); + } + Data.Add(key, Errors[i].ToString()); + } } // runtime will call even if private... @@ -62,37 +91,37 @@ namespace System.Data.SqlClient public byte Class { - get { return this.Errors[0].Class; } + get { return Errors.Count > 0 ? this.Errors[0].Class : default; } } public int LineNumber { - get { return this.Errors[0].LineNumber; } + get { return Errors.Count > 0 ? Errors[0].LineNumber : default; } } public int Number { - get { return this.Errors[0].Number; } + get { return Errors.Count > 0 ? Errors[0].Number : default; } } public string Procedure { - get { return this.Errors[0].Procedure; } + get { return Errors.Count > 0 ? Errors[0].Procedure : default; } } public string Server { - get { return this.Errors[0].Server; } + get { return Errors.Count > 0 ? Errors[0].Server : default; } } public byte State { - get { return this.Errors[0].State; } + get { return Errors.Count > 0 ? Errors[0].State : default; } } override public string Source { - get { return this.Errors[0].Source; } + get { return Errors.Count > 0 ? Errors[0].Source : default; } } public override string ToString() @@ -102,7 +131,7 @@ namespace System.Data.SqlClient sb.AppendFormat(SQLMessage.ExClientConnectionId(), _clientConnectionId); // Append the error number, state and class if the server provided it - if (Number != 0) + if (Errors.Count > 0 && Number != 0) { sb.AppendLine(); sb.AppendFormat(SQLMessage.ExErrorNumberStateClass(), Number, State, Class); diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/sqlinternaltransaction.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlInternalTransaction.cs similarity index 100% rename from external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/sqlinternaltransaction.cs rename to external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlInternalTransaction.cs diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlMetadataFactory.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlMetadataFactory_.cs similarity index 100% rename from external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlMetadataFactory.cs rename to external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlMetadataFactory_.cs diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlParameter.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlParameter.cs index 56f0f078b8..f157d6c7dc 100644 --- a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlParameter.cs +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlParameter.cs @@ -2,6 +2,7 @@ // 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.ComponentModel; using System.Collections.Generic; using System.Data.Common; using System.Data.SqlTypes; @@ -14,6 +15,8 @@ using MSS = Microsoft.SqlServer.Server; using Microsoft.SqlServer.Server; +using System.ComponentModel.Design.Serialization; + namespace System.Data.SqlClient { internal abstract class DataFeed @@ -50,6 +53,7 @@ namespace System.Data.SqlClient } } + [TypeConverter(typeof(SqlParameterConverter))] public sealed partial class SqlParameter : DbParameter, IDbDataParameter, ICloneable { private MetaType _metaType; @@ -59,7 +63,9 @@ namespace System.Data.SqlClient private string _xmlSchemaCollectionOwningSchema; private string _xmlSchemaCollectionName; + private string _udtTypeName; private string _typeName; + private Exception _udtLoadError; private string _parameterName; private byte _precision; @@ -175,9 +181,6 @@ namespace System.Data.SqlClient } } - // - // currently the user can't set this value. it gets set by the return value from tds - // internal SqlCollation Collation { get @@ -209,7 +212,14 @@ namespace System.Data.SqlClient { _collation = collation = new SqlCollation(); } - if ((value & SqlTypeWorkarounds.SqlStringValidSqlCompareOptionMask) != value) + + // Copied from SQLString.x_iValidSqlCompareOptionMask + SqlCompareOptions validSqlCompareOptionMask = + SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreWidth | + SqlCompareOptions.IgnoreNonSpace | SqlCompareOptions.IgnoreKanaType | + SqlCompareOptions.BinarySort | SqlCompareOptions.BinarySort2; + + if ((value & validSqlCompareOptionMask) != value) { throw ADP.ArgumentOutOfRange(nameof(CompareInfo)); } @@ -217,13 +227,12 @@ namespace System.Data.SqlClient } } - public string XmlSchemaCollectionDatabase { get { string xmlSchemaCollectionDatabase = _xmlSchemaCollectionDatabase; - return ((xmlSchemaCollectionDatabase != null) ? xmlSchemaCollectionDatabase : ADP.StrEmpty); + return (xmlSchemaCollectionDatabase ?? ADP.StrEmpty); } set { @@ -236,7 +245,7 @@ namespace System.Data.SqlClient get { string xmlSchemaCollectionOwningSchema = _xmlSchemaCollectionOwningSchema; - return ((xmlSchemaCollectionOwningSchema != null) ? xmlSchemaCollectionOwningSchema : ADP.StrEmpty); + return (xmlSchemaCollectionOwningSchema ?? ADP.StrEmpty); } set { @@ -249,7 +258,7 @@ namespace System.Data.SqlClient get { string xmlSchemaCollectionName = _xmlSchemaCollectionName; - return ((xmlSchemaCollectionName != null) ? xmlSchemaCollectionName : ADP.StrEmpty); + return (xmlSchemaCollectionName ?? ADP.StrEmpty); } set { @@ -257,7 +266,7 @@ namespace System.Data.SqlClient } } - override public DbType DbType + public override DbType DbType { get { @@ -320,15 +329,6 @@ namespace System.Data.SqlClient } } - - internal bool SizeInferred - { - get - { - return 0 == _size; - } - } - internal MSS.SmiParameterMetaData MetaDataForSmi(out ParameterPeekAheadValue peekAhead) { peekAhead = null; @@ -420,11 +420,11 @@ namespace System.Data.SqlClient String[] names; if (SqlDbType.Udt == mt.SqlDbType) { - throw ADP.DbTypeNotSupported(SqlDbType.Udt.ToString()); + names = ParseTypeName(UdtTypeName, true /* is UdtTypeName */); } else { - names = ParseTypeName(this.TypeName); + names = ParseTypeName(this.TypeName, false /* not UdtTypeName */); } if (1 == names.Length) @@ -481,6 +481,7 @@ namespace System.Data.SqlClient scale, localeId, compareOpts, + null, // Udt type not used for parameters SqlDbType.Structured == mt.SqlDbType, fields, extendedProperties, @@ -503,12 +504,12 @@ namespace System.Data.SqlClient } } - override public string ParameterName + public override string ParameterName { get { string parameterName = _parameterName; - return ((null != parameterName) ? parameterName : ADP.StrEmpty); + return (parameterName ?? ADP.StrEmpty); } set { @@ -542,7 +543,8 @@ namespace System.Data.SqlClient } } - public override Byte Precision + [DefaultValue((byte)0)] + public new byte Precision { get { @@ -586,7 +588,8 @@ namespace System.Data.SqlClient return (0 != _precision); } - public override Byte Scale + [DefaultValue((byte)0)] + public new byte Scale { get { @@ -627,6 +630,7 @@ namespace System.Data.SqlClient return (0 != _scale); // V1.0 compat, ignore _hasScale } + [DbProviderSpecificTypeProperty(true)] public SqlDbType SqlDbType { get @@ -636,14 +640,8 @@ namespace System.Data.SqlClient set { MetaType metatype = _metaType; - // HACK!!! - // We didn't want to expose SmallVarBinary on SqlDbType so we - // stuck it at the end of SqlDbType in v1.0, except that now - // we have new data types after that and it's smack dab in the - // middle of the valid range. To prevent folks from setting - // this invalid value we have to have this code here until we - // can take the time to fix it later. - if ((SqlDbType)TdsEnums.SmallVarBinary == value) + + if (TdsEnums.SmallVarBinary == value) { throw SQL.InvalidSqlDbType(value); } @@ -673,6 +671,11 @@ namespace System.Data.SqlClient { get { + if (_udtLoadError != null) + { + throw _udtLoadError; + } + if (_value != null) { if (_value == DBNull.Value) @@ -686,7 +689,7 @@ namespace System.Data.SqlClient // For Date and DateTime2, return the CLR object directly without converting it to a SqlValue // GetMetaTypeOnly() will convert _value to a string in the case of char or char[], so only check - // the SqlDbType for DateTime. + // the SqlDbType for DateTime. This is the only case when we might return the CLR value directly. if (_value is DateTime) { SqlDbType sqlDbType = GetMetaTypeOnly().SqlDbType; @@ -710,12 +713,25 @@ namespace System.Data.SqlClient } } + public string UdtTypeName + { + get + { + string typeName = _udtTypeName; + return (typeName ?? ADP.StrEmpty); + } + set + { + _udtTypeName = value; + } + } + public String TypeName { get { string typeName = _typeName; - return ((null != typeName) ? typeName : ADP.StrEmpty); + return (typeName ?? ADP.StrEmpty); } set { @@ -723,10 +739,16 @@ namespace System.Data.SqlClient } } - override public object Value - { // V1.2.3300, XXXParameter V1.0.3300 + [TypeConverter(typeof(StringConverter))] + public override object Value + { get { + if (_udtLoadError != null) + { + throw _udtLoadError; + } + if (_value != null) { return _value; @@ -749,6 +771,7 @@ namespace System.Data.SqlClient _valueAsINullable = _value as INullable; _isSqlParameterSqlType = (_valueAsINullable != null); _isNull = ((_value == null) || (_value == DBNull.Value) || ((_isSqlParameterSqlType) && (_valueAsINullable.IsNull))); + _udtLoadError = null; _actualSize = -1; } } @@ -852,7 +875,11 @@ namespace System.Data.SqlClient _actualSize = coercedSize; break; case SqlDbType.Udt: - throw ADP.DbTypeNotSupported(SqlDbType.Udt.ToString()); + if (!IsNull) + { + coercedSize = SerializationHelperSql9.SizeInBytes(val); + } + break; case SqlDbType.Structured: coercedSize = -1; break; @@ -870,7 +897,7 @@ namespace System.Data.SqlClient default: Debug.Assert(false, "Unknown variable length type!"); break; - } // switch + } // don't even send big values over to the variant if (isSqlVariant && (coercedSize > TdsEnums.TYPE_SIZE_LIMIT)) @@ -881,6 +908,10 @@ namespace System.Data.SqlClient return _actualSize; } + object ICloneable.Clone() + { + return new SqlParameter(this); + } // Coerced Value is also used in SqlBulkCopy.ConvertValue(object value, _SqlMetaData metadata) internal static object CoerceValue(object value, MetaType destinationType, out bool coercedToDataFeed, out bool typeChanged, bool allowStreaming = true) @@ -944,7 +975,7 @@ namespace System.Data.SqlClient } else if ((DbType.Currency == destinationType.DbType) && (typeof(string) == currentType)) { - value = Decimal.Parse((string)value, NumberStyles.Currency, (IFormatProvider)null); // WebData 99376 + value = Decimal.Parse((string)value, NumberStyles.Currency, (IFormatProvider)null); } else if ((typeof(SqlBytes) == currentType) && (typeof(byte[]) == destinationType.ClassType)) { @@ -987,7 +1018,7 @@ namespace System.Data.SqlClient throw; } - throw ADP.ParameterConversionFailed(value, destinationType.ClassType, e); // WebData 75433 + throw ADP.ParameterConversionFailed(value, destinationType.ClassType, e); } } @@ -1060,9 +1091,66 @@ namespace System.Data.SqlClient } // We should have returned before reaching here - Debug.Assert(false, "_coercedValueIsDataFeed was true, but the value was not a known DataFeed type"); + Debug.Fail("_coercedValueIsDataFeed was true, but the value was not a known DataFeed type"); } + private void CloneHelper(SqlParameter destination) + { + // NOTE: _parent is not cloned + destination._value = _value; + destination._direction = _direction; + destination._size = _size; + destination._offset = _offset; + destination._sourceColumn = _sourceColumn; + destination._sourceVersion = _sourceVersion; + destination._sourceColumnNullMapping = _sourceColumnNullMapping; + destination._isNullable = _isNullable; + + destination._metaType = _metaType; + destination._collation = _collation; + destination._xmlSchemaCollectionDatabase = _xmlSchemaCollectionDatabase; + destination._xmlSchemaCollectionOwningSchema = _xmlSchemaCollectionOwningSchema; + destination._xmlSchemaCollectionName = _xmlSchemaCollectionName; + destination._udtTypeName = _udtTypeName; + destination._typeName = _typeName; + destination._udtLoadError = _udtLoadError; + + destination._parameterName = _parameterName; + destination._precision = _precision; + destination._scale = _scale; + destination._sqlBufferReturnValue = _sqlBufferReturnValue; + destination._isSqlParameterSqlType = _isSqlParameterSqlType; + destination._internalMetaType = _internalMetaType; + destination.CoercedValue = CoercedValue; // copy cached value reference because of XmlReader problem + destination._valueAsINullable = _valueAsINullable; + destination._isNull = _isNull; + destination._coercedValueIsDataFeed = _coercedValueIsDataFeed; + destination._coercedValueIsSqlType = _coercedValueIsSqlType; + destination._actualSize = _actualSize; + } + + public override DataRowVersion SourceVersion + { + get + { + DataRowVersion sourceVersion = _sourceVersion; + return ((0 != sourceVersion) ? sourceVersion : DataRowVersion.Current); + } + set + { + switch (value) + { + case DataRowVersion.Original: + case DataRowVersion.Current: + case DataRowVersion.Proposed: + case DataRowVersion.Default: + _sourceVersion = value; + break; + default: + throw ADP.InvalidDataRowVersion(value); + } + } + } internal byte GetActualPrecision() { @@ -1279,9 +1367,11 @@ namespace System.Data.SqlClient } // pack it up so we don't have to rewind to send the first value - peekAhead = new ParameterPeekAheadValue(); - peekAhead.Enumerator = enumerator; - peekAhead.FirstRecord = firstRecord; + peekAhead = new ParameterPeekAheadValue() + { + Enumerator = enumerator, + FirstRecord = firstRecord + }; // now that it's all packaged, make sure we don't dispose it. enumerator = null; @@ -1314,12 +1404,10 @@ namespace System.Data.SqlClient int fieldCount = schema.Rows.Count; fields = new List(fieldCount); - bool[] keyCols = new bool[fieldCount]; bool hasKey = false; int ordinalForIsKey = schema.Columns[SchemaTableColumn.IsKey].Ordinal; int ordinalForColumnOrdinal = schema.Columns[SchemaTableColumn.ColumnOrdinal].Ordinal; - // Extract column metadata for (int rowOrdinal = 0; rowOrdinal < fieldCount; rowOrdinal++) { @@ -1381,7 +1469,8 @@ namespace System.Data.SqlClient // 2) no ordinals outside continuous range from 0 to fieldcount - 1 are allowed // 3) no duplicate ordinals are allowed // But assert no holes to be sure. - foreach (MSS.SmiExtendedMetaData md in fields) { + foreach (MSS.SmiExtendedMetaData md in fields) + { Debug.Assert(null != md, "Shouldn't be able to have holes, since original loop algorithm prevents such."); } #endif @@ -1483,15 +1572,18 @@ namespace System.Data.SqlClient { // We have a value set by the user then just use that value // char and char[] are not directly supported so we convert those values to string - if (_value is char) + Type valueType = _value.GetType(); + if (typeof(char) == valueType) { _value = _value.ToString(); + valueType = typeof(string); } - else if (Value is char[]) + else if (typeof(char[]) == valueType) { _value = new string((char[])_value); + valueType = typeof(string); } - return MetaType.GetMetaTypeFromValue(_value, inferLen: false); + return MetaType.GetMetaTypeFromType(valueType); } else if (null != _sqlBufferReturnValue) { // value came back from the server @@ -1539,28 +1631,33 @@ namespace System.Data.SqlClient _isNull = _sqlBufferReturnValue.IsNull; _coercedValueIsDataFeed = false; _coercedValueIsSqlType = false; + _udtLoadError = null; _actualSize = -1; } + internal void SetUdtLoadError(Exception e) + { + _udtLoadError = e; + } internal void Validate(int index, bool isCommandProc) { MetaType metaType = GetMetaTypeOnly(); _internalMetaType = metaType; - // NOTE: (General Criteria): SqlParameter does a Size Validation check and would fail if the size is 0. - // This condition filters all scenarios where we view a valid size 0. + // SqlParameter does a Size Validation check and would fail if the size is 0. + // This condition filters all scenarios where we view a valid size 0. if (ADP.IsDirection(this, ParameterDirection.Output) && !ADP.IsDirection(this, ParameterDirection.ReturnValue) && (!metaType.IsFixed) && !ShouldSerializeSize() && - ((null == _value) || (_value == DBNull.Value)) && + ((null == _value) || Convert.IsDBNull(_value)) && (SqlDbType != SqlDbType.Timestamp) && (SqlDbType != SqlDbType.Udt) && - // Output parameter with size 0 throws for XML, TEXT, NTEXT, IMAGE. (SqlDbType != SqlDbType.Xml) && !metaType.IsVarTime) { + throw ADP.UninitializedParameterSize(index, metaType.ClassType); } @@ -1569,6 +1666,16 @@ namespace System.Data.SqlClient GetCoercedValue(); } + //check if the UdtTypeName is specified for Udt params + if (metaType.SqlDbType == SqlDbType.Udt) + { + if (string.IsNullOrEmpty(UdtTypeName)) + throw SQL.MustSetUdtTypeNameForUdtParams(); + } + else if (!string.IsNullOrEmpty(UdtTypeName)) + { + throw SQL.UnexpectedUdtTypeNameForNonUdtParams(); + } // Validate structured-type-specific details. if (metaType.SqlDbType == SqlDbType.Structured) @@ -1607,21 +1714,11 @@ namespace System.Data.SqlClient long actualSizeInBytes = this.GetActualSize(); long sizeInCharacters = this.Size; - // 'actualSizeInBytes' is the size of value passed; - // 'sizeInCharacters' is the parameter size; - // 'actualSizeInBytes' is in bytes; - // 'this.Size' is in characters; - // 'sizeInCharacters' is in characters; - // 'TdsEnums.TYPE_SIZE_LIMIT' is in bytes; - // For Non-NCharType and for non-Yukon or greater variables, size should be maintained; - // Modified variable names from 'size' to 'sizeInCharacters', 'actualSize' to 'actualSizeInBytes', and - // 'maxSize' to 'maxSizeInBytes' - // The idea is to - // Keeping these goals in mind - the following are the changes we are making - long maxSizeInBytes = 0; if (mt.IsNCharType) + { maxSizeInBytes = ((sizeInCharacters * sizeof(char)) > actualSizeInBytes) ? sizeInCharacters * sizeof(char) : actualSizeInBytes; + } else { // Notes: @@ -1793,87 +1890,153 @@ namespace System.Data.SqlClient // [2] name // NOTE: if perf/space implications of Regex is not a problem, we can get rid // of this and use a simple regex to do the parsing - internal static string[] ParseTypeName(string typeName) + internal static string[] ParseTypeName(string typeName, bool isUdtTypeName) { Debug.Assert(null != typeName, "null typename passed to ParseTypeName"); try { - string errorMsg; - { - errorMsg = SR.SQL_TypeName; - } + string errorMsg = isUdtTypeName ? SR.SQL_UDTTypeName : SR.SQL_TypeName; return MultipartIdentifier.ParseMultipartIdentifier(typeName, "[\"", "]\"", '.', 3, true, errorMsg, true); } catch (ArgumentException) { + if (isUdtTypeName) + { + throw SQL.InvalidUdt3PartNameFormat(); + } + else { throw SQL.InvalidParameterTypeNameFormat(); } } } - object ICloneable.Clone() => new SqlParameter(this); - - private void CloneHelper(SqlParameter destination) + internal sealed class SqlParameterConverter : ExpandableObjectConverter { - CloneHelperCore(destination); - destination._metaType = _metaType; - destination._collation = _collation; - destination._xmlSchemaCollectionDatabase = _xmlSchemaCollectionDatabase; - destination._xmlSchemaCollectionOwningSchema = _xmlSchemaCollectionOwningSchema; - destination._xmlSchemaCollectionName = _xmlSchemaCollectionName; - destination._typeName = _typeName; - destination._parameterName = _parameterName; - destination._precision = _precision; - destination._scale = _scale; - destination._sqlBufferReturnValue = _sqlBufferReturnValue; - destination._isSqlParameterSqlType = _isSqlParameterSqlType; - destination._internalMetaType = _internalMetaType; - destination.CoercedValue = CoercedValue; // copy cached value reference because of XmlReader problem - destination._valueAsINullable = _valueAsINullable; - destination._isNull = _isNull; - destination._coercedValueIsDataFeed = _coercedValueIsDataFeed; - destination._coercedValueIsSqlType = _coercedValueIsSqlType; - destination._actualSize = _actualSize; - } - - private void CloneHelperCore(SqlParameter destination) - { - destination._value = _value; - - destination._direction = _direction; - destination._size = _size; - - destination._offset = _offset; - destination._sourceColumn = _sourceColumn; - destination._sourceVersion = _sourceVersion; - destination._sourceColumnNullMapping = _sourceColumnNullMapping; - destination._isNullable = _isNullable; - } - - public override DataRowVersion SourceVersion - { - get + // converter classes should have public ctor + public SqlParameterConverter() { - DataRowVersion sourceVersion = _sourceVersion; - return ((0 != sourceVersion) ? sourceVersion : DataRowVersion.Current); } - set + + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { - switch (value) + if (typeof(InstanceDescriptor) == destinationType) { - case DataRowVersion.Original: - case DataRowVersion.Current: - case DataRowVersion.Proposed: - case DataRowVersion.Default: - _sourceVersion = value; + return true; + } + return base.CanConvertTo(context, destinationType); + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + { + if (destinationType == null) + { + throw ADP.ArgumentNull(nameof(destinationType)); + } + if ((typeof(InstanceDescriptor) == destinationType) && (value is SqlParameter)) + { + return ConvertToInstanceDescriptor(value as SqlParameter); + } + return base.ConvertTo(context, culture, value, destinationType); + } + + private InstanceDescriptor ConvertToInstanceDescriptor(SqlParameter p) + { + int flags = 0; // if part of the collection - the parametername can't be empty + + if (p.ShouldSerializeSqlDbType()) + { + flags |= 1; + } + if (p.ShouldSerializeSize()) + { + flags |= 2; + } + if (!string.IsNullOrEmpty(p.SourceColumn)) + { + flags |= 4; + } + if (null != p.Value) + { + flags |= 8; + } + if ((ParameterDirection.Input != p.Direction) || p.IsNullable + || p.ShouldSerializePrecision() || p.ShouldSerializeScale() + || (DataRowVersion.Current != p.SourceVersion) + ) + { + flags |= 16; // v1.0 everything + } + + if (p.SourceColumnNullMapping || !string.IsNullOrEmpty(p.XmlSchemaCollectionDatabase) || + !string.IsNullOrEmpty(p.XmlSchemaCollectionOwningSchema) || !string.IsNullOrEmpty(p.XmlSchemaCollectionName)) + { + flags |= 32; // v2.0 everything + } + + Type[] ctorParams; + object[] ctorValues; + switch (flags) + { + case 0: // ParameterName + case 1: // SqlDbType + ctorParams = new Type[] { typeof(string), typeof(SqlDbType) }; + ctorValues = new object[] { p.ParameterName, p.SqlDbType }; + break; + case 2: // Size + case 3: // Size, SqlDbType + ctorParams = new Type[] { typeof(string), typeof(SqlDbType), typeof(int) }; + ctorValues = new object[] { p.ParameterName, p.SqlDbType, p.Size }; + break; + case 4: // SourceColumn + case 5: // SourceColumn, SqlDbType + case 6: // SourceColumn, Size + case 7: // SourceColumn, Size, SqlDbType + ctorParams = new Type[] { typeof(string), typeof(SqlDbType), typeof(int), typeof(string) }; + ctorValues = new object[] { p.ParameterName, p.SqlDbType, p.Size, p.SourceColumn }; + break; + case 8: // Value + ctorParams = new Type[] { typeof(string), typeof(object) }; + ctorValues = new object[] { p.ParameterName, p.Value }; break; default: - throw ADP.InvalidDataRowVersion(value); + if (0 == (32 & flags)) + { // v1.0 everything + ctorParams = new Type[] { + typeof(string), typeof(SqlDbType), typeof(int), typeof(ParameterDirection), + typeof(bool), typeof(byte), typeof(byte), + typeof(string), typeof(DataRowVersion), + typeof(object) }; + ctorValues = new object[] { + p.ParameterName, p.SqlDbType, p.Size, p.Direction, + p.IsNullable, p.PrecisionInternal, p.ScaleInternal, + p.SourceColumn, p.SourceVersion, + p.Value }; + } + else + { // v2.0 everything - round trip all browsable properties + precision/scale + ctorParams = new Type[] { + typeof(string), typeof(SqlDbType), typeof(int), typeof(ParameterDirection), + typeof(byte), typeof(byte), + typeof(string), typeof(DataRowVersion), typeof(bool), + typeof(object), + typeof(string), typeof(string), + typeof(string) }; + ctorValues = new object[] { + p.ParameterName, p.SqlDbType, p.Size, p.Direction, + p.PrecisionInternal, p.ScaleInternal, + p.SourceColumn, p.SourceVersion, p.SourceColumnNullMapping, + p.Value, + p.XmlSchemaCollectionDatabase, p.XmlSchemaCollectionOwningSchema, + p.XmlSchemaCollectionName}; + } + break; } + ConstructorInfo ctor = typeof(SqlParameter).GetConstructor(ctorParams); + return new InstanceDescriptor(ctor, ctorValues); } } } -} +} \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlUdtInfo.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlUdtInfo.cs new file mode 100644 index 0000000000..1567c28cb4 --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlUdtInfo.cs @@ -0,0 +1,63 @@ +// 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.Collections.Generic; +using Microsoft.SqlServer.Server; + +namespace System.Data.SqlClient +{ + internal class SqlUdtInfo + { + internal readonly Format SerializationFormat; + internal readonly bool IsByteOrdered; + internal readonly bool IsFixedLength; + internal readonly int MaxByteSize; + internal readonly string Name; + internal readonly string ValidationMethodName; + + private SqlUdtInfo(SqlUserDefinedTypeAttribute attr) + { + SerializationFormat = attr.Format; + IsByteOrdered = attr.IsByteOrdered; + IsFixedLength = attr.IsFixedLength; + MaxByteSize = attr.MaxByteSize; + Name = attr.Name; + ValidationMethodName = attr.ValidationMethodName; + } + + internal static SqlUdtInfo GetFromType(Type target) + { + SqlUdtInfo udtAttr = TryGetFromType(target); + if (udtAttr == null) + { + throw InvalidUdtException.Create(target, SR.SqlUdtReason_NoUdtAttribute); + } + return udtAttr; + } + + // Improve UDT serialization performance by caching the resulting UDT type information using type-safe dictionary. + // Use a per-thread cache, so we do not need to synchronize access to it + [ThreadStatic] + private static Dictionary s_types2UdtInfo; + + internal static SqlUdtInfo TryGetFromType(Type target) + { + if (s_types2UdtInfo == null) + s_types2UdtInfo = new Dictionary(); + + SqlUdtInfo udtAttr = null; + if (!s_types2UdtInfo.TryGetValue(target, out udtAttr)) + { + // query SqlUserDefinedTypeAttribute first time and cache the result + object[] attr = target.GetCustomAttributes(typeof(SqlUserDefinedTypeAttribute), false); + if (attr != null && attr.Length == 1) + { + udtAttr = new SqlUdtInfo((SqlUserDefinedTypeAttribute)attr[0]); + } + s_types2UdtInfo.Add(target, udtAttr); + } + return udtAttr; + } + } +} diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlUtil.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlUtil.cs index b86228a591..8b9470bd97 100644 --- a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlUtil.cs +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlUtil.cs @@ -309,6 +309,10 @@ namespace System.Data.SqlClient // // SQL.DataParameter // + internal static Exception InvalidUdt3PartNameFormat() + { + return ADP.Argument(SR.GetString(SR.SQL_InvalidUdt3PartNameFormat)); + } internal static Exception InvalidParameterTypeNameFormat() { return ADP.Argument(SR.GetString(SR.SQL_InvalidParameterTypeNameFormat)); @@ -437,6 +441,11 @@ namespace System.Data.SqlClient return ADP.InvalidCast(SR.GetString(SR.SQL_XmlReaderNotSupportOnColumnType, columnName)); } + internal static Exception UDTUnexpectedResult(string exceptionText) + { + return ADP.TypeLoad(SR.GetString(SR.SQLUDT_Unexpected, exceptionText)); + } + // // SQL.SqlDependency // @@ -467,7 +476,6 @@ namespace System.Data.SqlClient internal static Exception SqlDependencyIdMismatch() { - // do not include the id because it may require SecurityPermission(Infrastructure) permission return ADP.InvalidOperation(SR.GetString(SR.SqlDependency_IdMismatch)); } @@ -495,6 +503,18 @@ namespace System.Data.SqlClient // // SQL.SqlMetaData // + internal static Exception UnexpectedUdtTypeNameForNonUdtParams() + { + return ADP.Argument(SR.GetString(SR.SQLUDT_UnexpectedUdtTypeName)); + } + internal static Exception MustSetUdtTypeNameForUdtParams() + { + return ADP.Argument(SR.GetString(SR.SQLUDT_InvalidUdtTypeName)); + } + internal static Exception UDTInvalidSqlType(string typeName) + { + return ADP.Argument(SR.GetString(SR.SQLUDT_InvalidSqlType, typeName)); + } internal static Exception InvalidSqlDbTypeForConstructor(SqlDbType type) { return ADP.Argument(SR.GetString(SR.SqlMetaData_InvalidSqlDbTypeForConstructorFormat, type.ToString())); diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/TdsParser.cs.REMOVED.git-id b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/TdsParser.cs.REMOVED.git-id index d0b91d28f1..dea8f41332 100644 --- a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/TdsParser.cs.REMOVED.git-id +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/TdsParser.cs.REMOVED.git-id @@ -1 +1 @@ -37a4d51e0d908eadb8ca232097e52985b9bbb687 \ No newline at end of file +16e03ce9922cd095766806207ec91890062e8421 \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/TdsParserHelperClasses.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/TdsParserHelperClasses.cs index dae41cf735..c41adc358a 100644 --- a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/TdsParserHelperClasses.cs +++ b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/TdsParserHelperClasses.cs @@ -12,7 +12,7 @@ using System.Data.Common; using System.Data.SqlTypes; using System.Diagnostics; using System.Text; - +using Microsoft.SqlServer.Server; namespace System.Data.SqlClient { @@ -481,6 +481,7 @@ namespace System.Data.SqlClient internal int codePage; internal Encoding encoding; internal bool isNullable; + internal bool isMultiValued = false; // UDT specific metadata // server metadata info @@ -489,13 +490,21 @@ namespace System.Data.SqlClient internal string udtSchemaName; internal string udtTypeName; internal string udtAssemblyQualifiedName; - + + // on demand + internal Type udtType; + // Xml specific metadata internal string xmlSchemaCollectionDatabase; internal string xmlSchemaCollectionOwningSchema; internal string xmlSchemaCollectionName; internal MetaType metaType; // cached metaType + // Structured type-specific metadata + internal string structuredTypeDatabaseName; + internal string structuredTypeSchemaName; + internal string structuredTypeName; + internal IList structuredFields; internal SqlMetaDataPriv() { @@ -512,16 +521,24 @@ namespace System.Data.SqlClient this.codePage = original.codePage; this.encoding = original.encoding; this.isNullable = original.isNullable; + this.isMultiValued = original.isMultiValued; this.udtDatabaseName = original.udtDatabaseName; this.udtSchemaName = original.udtSchemaName; this.udtTypeName = original.udtTypeName; this.udtAssemblyQualifiedName = original.udtAssemblyQualifiedName; + this.udtType = original.udtType; this.xmlSchemaCollectionDatabase = original.xmlSchemaCollectionDatabase; this.xmlSchemaCollectionOwningSchema = original.xmlSchemaCollectionOwningSchema; this.xmlSchemaCollectionName = original.xmlSchemaCollectionName; this.metaType = original.metaType; + + this.structuredTypeDatabaseName = original.structuredTypeDatabaseName; + this.structuredTypeSchemaName = original.structuredTypeSchemaName; + this.structuredTypeName = original.structuredTypeName; + this.structuredFields = original.structuredFields; } } + sealed internal class _SqlRPC { internal string rpcName; diff --git a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/sqlmetadatafactory.cs b/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/sqlmetadatafactory.cs deleted file mode 100644 index 40b9b353c7..0000000000 --- a/external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/sqlmetadatafactory.cs +++ /dev/null @@ -1,289 +0,0 @@ -// 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.Data; -using System.IO; -using System.Data.ProviderBase; -using System.Data.Common; -using System.Text; - -namespace System.Data.SqlClient -{ - internal sealed class SqlMetaDataFactory : DbMetaDataFactory - { - - private const string _serverVersionNormalized90 = "09.00.0000"; - private const string _serverVersionNormalized90782 = "09.00.0782"; - private const string _serverVersionNormalized10 = "10.00.0000"; - - - public SqlMetaDataFactory(Stream XMLStream, - string serverVersion, - string serverVersionNormalized) : - base(XMLStream, serverVersion, serverVersionNormalized) { } - - private void addUDTsToDataTypesTable(DataTable dataTypesTable, SqlConnection connection, String ServerVersion) - { - const string sqlCommand = - "select " + - "assemblies.name, " + - "types.assembly_class, " + - "ASSEMBLYPROPERTY(assemblies.name, 'VersionMajor') as version_major, " + - "ASSEMBLYPROPERTY(assemblies.name, 'VersionMinor') as version_minor, " + - "ASSEMBLYPROPERTY(assemblies.name, 'VersionBuild') as version_build, " + - "ASSEMBLYPROPERTY(assemblies.name, 'VersionRevision') as version_revision, " + - "ASSEMBLYPROPERTY(assemblies.name, 'CultureInfo') as culture_info, " + - "ASSEMBLYPROPERTY(assemblies.name, 'PublicKey') as public_key, " + - "is_nullable, " + - "is_fixed_length, " + - "max_length " + - "from sys.assemblies as assemblies join sys.assembly_types as types " + - "on assemblies.assembly_id = types.assembly_id "; - - // pre 9.0/Yukon servers do not have UDTs - if (0 > string.Compare(ServerVersion, _serverVersionNormalized90, StringComparison.OrdinalIgnoreCase)) - { - return; - } - - // Execute the SELECT statement - SqlCommand command = connection.CreateCommand(); - command.CommandText = sqlCommand; - DataRow newRow = null; - DataColumn providerDbtype = dataTypesTable.Columns[DbMetaDataColumnNames.ProviderDbType]; - DataColumn columnSize = dataTypesTable.Columns[DbMetaDataColumnNames.ColumnSize]; - DataColumn isFixedLength = dataTypesTable.Columns[DbMetaDataColumnNames.IsFixedLength]; - DataColumn isSearchable = dataTypesTable.Columns[DbMetaDataColumnNames.IsSearchable]; - DataColumn isLiteralSupported = dataTypesTable.Columns[DbMetaDataColumnNames.IsLiteralSupported]; - DataColumn typeName = dataTypesTable.Columns[DbMetaDataColumnNames.TypeName]; - DataColumn isNullable = dataTypesTable.Columns[DbMetaDataColumnNames.IsNullable]; - - if ((providerDbtype == null) || - (columnSize == null) || - (isFixedLength == null) || - (isSearchable == null) || - (isLiteralSupported == null) || - (typeName == null) || - (isNullable == null)) - { - throw ADP.InvalidXml(); - } - - const int columnSizeIndex = 10; - const int isFixedLengthIndex = 9; - const int isNullableIndex = 8; - const int assemblyNameIndex = 0; - const int assemblyClassIndex = 1; - const int versionMajorIndex = 2; - const int versionMinorIndex = 3; - const int versionBuildIndex = 4; - const int versionRevisionIndex = 5; - const int cultureInfoIndex = 6; - const int publicKeyIndex = 7; - - - using (IDataReader reader = command.ExecuteReader()) - { - - object[] values = new object[11]; - while (reader.Read()) - { - - reader.GetValues(values); - newRow = dataTypesTable.NewRow(); - - newRow[providerDbtype] = SqlDbType.Udt; - - if (values[columnSizeIndex] != DBNull.Value) - { - newRow[columnSize] = values[columnSizeIndex]; - } - - if (values[isFixedLengthIndex] != DBNull.Value) - { - newRow[isFixedLength] = values[isFixedLengthIndex]; - } - - newRow[isSearchable] = true; - newRow[isLiteralSupported] = false; - if (values[isNullableIndex] != DBNull.Value) - { - newRow[isNullable] = values[isNullableIndex]; - } - - if ((values[assemblyNameIndex] != DBNull.Value) && - (values[assemblyClassIndex] != DBNull.Value) && - (values[versionMajorIndex] != DBNull.Value) && - (values[versionMinorIndex] != DBNull.Value) && - (values[versionBuildIndex] != DBNull.Value) && - (values[versionRevisionIndex] != DBNull.Value)) - { - - StringBuilder nameString = new StringBuilder(); - nameString.Append(values[assemblyClassIndex].ToString()); - nameString.Append(", "); - nameString.Append(values[assemblyNameIndex].ToString()); - nameString.Append(", Version="); - - nameString.Append(values[versionMajorIndex].ToString()); - nameString.Append("."); - nameString.Append(values[versionMinorIndex].ToString()); - nameString.Append("."); - nameString.Append(values[versionBuildIndex].ToString()); - nameString.Append("."); - nameString.Append(values[versionRevisionIndex].ToString()); - - if (values[cultureInfoIndex] != DBNull.Value) - { - nameString.Append(", Culture="); - nameString.Append(values[cultureInfoIndex].ToString()); - } - - if (values[publicKeyIndex] != DBNull.Value) - { - - nameString.Append(", PublicKeyToken="); - - StringBuilder resultString = new StringBuilder(); - Byte[] byteArrayValue = (Byte[])values[publicKeyIndex]; - foreach (byte b in byteArrayValue) - { - resultString.Append(String.Format((IFormatProvider)null, "{0,-2:x2}", b)); - } - nameString.Append(resultString.ToString()); - } - - newRow[typeName] = nameString.ToString(); - dataTypesTable.Rows.Add(newRow); - newRow.AcceptChanges(); - } // if assembly name - - }//end while - } // end using - } - - private void AddTVPsToDataTypesTable(DataTable dataTypesTable, SqlConnection connection, String ServerVersion) - { - - const string sqlCommand = - "select " + - "name, " + - "is_nullable, " + - "max_length " + - "from sys.types " + - "where is_table_type = 1"; - - // TODO: update this check once the server upgrades major version number!!! - // pre 9.0/Yukon servers do not have Table types - if (0 > string.Compare(ServerVersion, _serverVersionNormalized10, StringComparison.OrdinalIgnoreCase)) - { - return; - } - - // Execute the SELECT statement - SqlCommand command = connection.CreateCommand(); - command.CommandText = sqlCommand; - DataRow newRow = null; - DataColumn providerDbtype = dataTypesTable.Columns[DbMetaDataColumnNames.ProviderDbType]; - DataColumn columnSize = dataTypesTable.Columns[DbMetaDataColumnNames.ColumnSize]; - DataColumn isSearchable = dataTypesTable.Columns[DbMetaDataColumnNames.IsSearchable]; - DataColumn isLiteralSupported = dataTypesTable.Columns[DbMetaDataColumnNames.IsLiteralSupported]; - DataColumn typeName = dataTypesTable.Columns[DbMetaDataColumnNames.TypeName]; - DataColumn isNullable = dataTypesTable.Columns[DbMetaDataColumnNames.IsNullable]; - - if ((providerDbtype == null) || - (columnSize == null) || - (isSearchable == null) || - (isLiteralSupported == null) || - (typeName == null) || - (isNullable == null)) - { - throw ADP.InvalidXml(); - } - - const int columnSizeIndex = 2; - const int isNullableIndex = 1; - const int typeNameIndex = 0; - - using (IDataReader reader = command.ExecuteReader()) - { - - object[] values = new object[11]; - while (reader.Read()) - { - - reader.GetValues(values); - newRow = dataTypesTable.NewRow(); - - newRow[providerDbtype] = SqlDbType.Structured; - - if (values[columnSizeIndex] != DBNull.Value) - { - newRow[columnSize] = values[columnSizeIndex]; - } - - newRow[isSearchable] = false; - newRow[isLiteralSupported] = false; - if (values[isNullableIndex] != DBNull.Value) - { - newRow[isNullable] = values[isNullableIndex]; - } - - if (values[typeNameIndex] != DBNull.Value) - { - newRow[typeName] = values[typeNameIndex]; - dataTypesTable.Rows.Add(newRow); - newRow.AcceptChanges(); - } // if type name - }//end while - } // end using - } - - private DataTable GetDataTypesTable(SqlConnection connection) - { - // verify the existance of the table in the data set - DataTable dataTypesTable = CollectionDataSet.Tables[DbMetaDataCollectionNames.DataTypes]; - if (dataTypesTable == null) - { - throw ADP.UnableToBuildCollection(DbMetaDataCollectionNames.DataTypes); - } - - // copy the table filtering out any rows that don't apply to tho current version of the prrovider - dataTypesTable = CloneAndFilterCollection(DbMetaDataCollectionNames.DataTypes, null); - - addUDTsToDataTypesTable(dataTypesTable, connection, ServerVersionNormalized); - AddTVPsToDataTypesTable(dataTypesTable, connection, ServerVersionNormalized); - - dataTypesTable.AcceptChanges(); - return dataTypesTable; - } - - protected override DataTable PrepareCollection(String collectionName, String[] restrictions, DbConnection connection) - { - SqlConnection sqlConnection = (SqlConnection)connection; - DataTable resultTable = null; - - if (collectionName == DbMetaDataCollectionNames.DataTypes) - { - if (ADP.IsEmptyArray(restrictions) == false) - { - throw ADP.TooManyRestrictions(DbMetaDataCollectionNames.DataTypes); - } - resultTable = GetDataTypesTable(sqlConnection); - } - - if (resultTable == null) - { - throw ADP.UnableToBuildCollection(collectionName); - } - - return resultTable; - - } - - - - } -} \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs b/external/corefx/src/System.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs index 2df596fac9..381a5ec6cd 100644 --- a/external/corefx/src/System.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs +++ b/external/corefx/src/System.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs @@ -2,9 +2,11 @@ // 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.Collections.Generic; using System.Data.Common; +using System.Diagnostics; using System.Reflection; +using System.Threading; using Xunit; namespace System.Data.SqlClient.Tests @@ -79,5 +81,98 @@ namespace System.Data.SqlClient.Tests Assert.Throws(() => new SqlConnection("Timeout=1a;")); Assert.Throws(() => new SqlConnection("Integrated Security=truee")); } + + [Fact] + public void ConnectionTimeoutTestWithThread() + { + int timeoutSec = 5; + string connStrNotAvailable = $"Server=tcp:fakeServer,1433;uid=fakeuser;pwd=fakepwd;Connection Timeout={timeoutSec}"; + + List list = new List(); + for (int i = 0; i < 10; ++i) + { + list.Add(new ConnectionWorker(connStrNotAvailable)); + } + + ConnectionWorker.Start(); + ConnectionWorker.Stop(); + + double theMax = 0; + foreach (ConnectionWorker w in list) + { + if (theMax < w.MaxTimeElapsed) + { + theMax = w.MaxTimeElapsed; + } + } + + int threshold = (timeoutSec + 1) * 1000; + + Console.WriteLine($"ConnectionTimeoutTestWithThread: Elapsed Time {theMax} and threshold {threshold}"); + } + + public class ConnectionWorker + { + private static ManualResetEventSlim startEvent = new ManualResetEventSlim(false); + private static List workerList = new List(); + private ManualResetEventSlim doneEvent = new ManualResetEventSlim(false); + private double maxTimeElapsed; + private Thread thread; + private string connectionString; + + public ConnectionWorker(string connectionString) + { + workerList.Add(this); + this.connectionString = connectionString; + thread = new Thread(new ThreadStart(SqlConnectionOpen)); + thread.Start(); + } + + public double MaxTimeElapsed + { + get + { + return maxTimeElapsed; + } + } + + public static void Start() + { + startEvent.Set(); + } + + public static void Stop() + { + foreach (ConnectionWorker w in workerList) + { + w.doneEvent.Wait(); + } + } + + public void SqlConnectionOpen() + { + startEvent.Wait(); + + Stopwatch sw = new Stopwatch(); + using (SqlConnection con = new SqlConnection(connectionString)) + { + sw.Start(); + try + { + con.Open(); + } + catch { } + sw.Stop(); + } + + double elapsed = sw.Elapsed.TotalMilliseconds; + if (maxTimeElapsed < elapsed) + { + maxTimeElapsed = elapsed; + } + + doneEvent.Set(); + } + } } } diff --git a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/BeginExecAsyncTest.cs b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/BeginExecAsyncTest.cs new file mode 100644 index 0000000000..a73ccfaacc --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/BeginExecAsyncTest.cs @@ -0,0 +1,94 @@ +// 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.Threading.Tasks; +using Xunit; + +namespace System.Data.SqlClient.ManualTesting.Tests +{ + public static class BeginExecAsyncTest + { + private static string commandText = + "INSERT INTO[dbo].[Shippers] " + + "([CompanyName] " + + ",[Phone]) " + + "VALUES " + + "('Acme Inc.' " + + ",'555-1212'); " + + "WAITFOR DELAY '0:0:3';" + + "DELETE FROM dbo.Shippers WHERE ShipperID > 3;"; + + [CheckConnStrSetupFact] + public static void ExecuteTest() + { + using (SqlConnection connection = new SqlConnection(DataTestUtility.TcpConnStr)) + { + try + { + SqlCommand command = new SqlCommand(commandText, connection); + connection.Open(); + + IAsyncResult result = command.BeginExecuteNonQuery(); + while (!result.IsCompleted) + { + System.Threading.Thread.Sleep(100); + } + + Assert.True(command.EndExecuteNonQuery(result) > 0, "FAILED: BeginExecuteNonQuery did not complete successfully."); + } + catch (SqlException ex) + { + Console.WriteLine("Error ({0}): {1}", ex.Number, ex.Message); + Assert.Null(ex); + } + catch (InvalidOperationException ex) + { + Console.WriteLine("Error: {0}", ex.Message); + Assert.Null(ex); + } + catch (Exception ex) + { + // You might want to pass these errors + // back out to the caller. + Console.WriteLine("Error: {0}", ex.Message); + Assert.Null(ex); + } + } + } + + [CheckConnStrSetupFact] + public static void FailureTest() + { + using (SqlConnection connection = new SqlConnection(DataTestUtility.TcpConnStr)) + { + bool caughtException = false; + SqlCommand command = new SqlCommand(commandText, connection); + connection.Open(); + + //Try to execute a synchronous query on same command + IAsyncResult result = command.BeginExecuteNonQuery(); + try + { + command.ExecuteNonQuery(); + } + catch (Exception ex) + { + Assert.True(ex is InvalidOperationException, "FAILED: Thrown exception for BeginExecuteNonQuery was not an InvalidOperationException"); + caughtException = true; + } + + Assert.True(caughtException, "FAILED: No exception thrown after trying second BeginExecuteNonQuery."); + caughtException = false; + + while (!result.IsCompleted) + { + System.Threading.Thread.Sleep(100); + } + + Assert.True(result.IsCompleted, "FAILED: ExecuteNonQueryAsync Task did not complete successfully."); + Assert.True(command.EndExecuteNonQuery(result) > 0, "FAILED: No rows affected"); + } + } + } +} diff --git a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs new file mode 100644 index 0000000000..156db44ce7 --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs @@ -0,0 +1,308 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.Data.Common; +using System.Data.SqlTypes; +using System.IO; +using System.Linq; +using System.Text; +using Xunit; + +namespace System.Data.SqlClient.ManualTesting.Tests +{ + public static class SqlServerTypesTest + { + [CheckConnStrSetupFact] + public static void GetSchemaTableTest() + { + using (SqlConnection conn = new SqlConnection(DataTestUtility.TcpConnStr)) + using (SqlCommand cmd = new SqlCommand("select hierarchyid::Parse('/1/') as col0", conn)) + { + conn.Open(); + using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.KeyInfo)) + { + DataTable schemaTable = reader.GetSchemaTable(); + DataTestUtility.AssertEqualsWithDescription(1, schemaTable.Rows.Count, "Unexpected schema table row count."); + + string columnName = (string)(string)schemaTable.Rows[0][schemaTable.Columns["ColumnName"]]; + DataTestUtility.AssertEqualsWithDescription("col0", columnName, "Unexpected column name."); + + string dataTypeName = (string)schemaTable.Rows[0][schemaTable.Columns["DataTypeName"]]; + DataTestUtility.AssertEqualsWithDescription("Northwind.sys.hierarchyid", dataTypeName, "Unexpected data type name."); + + string udtAssemblyName = (string)schemaTable.Rows[0][schemaTable.Columns["UdtAssemblyQualifiedName"]]; + Assert.True(udtAssemblyName?.StartsWith("Microsoft.SqlServer.Types.SqlHierarchyId"), "Unexpected UDT assembly name: " + udtAssemblyName); + } + } + } + + [CheckConnStrSetupFact] + public static void GetValueTest() + { + using (SqlConnection conn = new SqlConnection(DataTestUtility.TcpConnStr)) + using (SqlCommand cmd = new SqlCommand("select hierarchyid::Parse('/1/') as col0", conn)) + { + conn.Open(); + using (SqlDataReader reader = cmd.ExecuteReader()) + { + Assert.True(reader.Read()); + + // SqlHierarchyId is part of Microsoft.SqlServer.Types, which is not supported in Core + Assert.Throws(() => reader.GetValue(0)); + Assert.Throws(() => reader.GetSqlValue(0)); + } + } + } + + [CheckConnStrSetupFact] + public static void TestUdtZeroByte() + { + using (SqlConnection connection = new SqlConnection(DataTestUtility.TcpConnStr)) + { + connection.Open(); + SqlCommand command = connection.CreateCommand(); + command.CommandText = "select hierarchyid::Parse('/') as col0"; + using (SqlDataReader reader = command.ExecuteReader()) + { + Assert.True(reader.Read()); + Assert.False(reader.IsDBNull(0)); + SqlBytes sqlBytes = reader.GetSqlBytes(0); + Assert.False(sqlBytes.IsNull, "Expected a zero length byte array"); + Assert.True(sqlBytes.Length == 0, "Expected a zero length byte array"); + } + } + } + + [CheckConnStrSetupFact] + public static void TestUdtSqlDataReaderGetSqlBytesSequentialAccess() + { + TestUdtSqlDataReaderGetSqlBytes(CommandBehavior.SequentialAccess); + } + + [CheckConnStrSetupFact] + public static void TestUdtSqlDataReaderGetSqlBytes() + { + TestUdtSqlDataReaderGetSqlBytes(CommandBehavior.Default); + } + + private static void TestUdtSqlDataReaderGetSqlBytes(CommandBehavior behavior) + { + using (SqlConnection connection = new SqlConnection(DataTestUtility.TcpConnStr)) + { + connection.Open(); + SqlCommand command = connection.CreateCommand(); + command.CommandText = "select hierarchyid::Parse('/1/1/3/') as col0, geometry::Parse('LINESTRING (100 100, 20 180, 180 180)') as col1, geography::Parse('LINESTRING(-122.360 47.656, -122.343 47.656)') as col2"; + using (SqlDataReader reader = command.ExecuteReader(behavior)) + { + Assert.True(reader.Read()); + + SqlBytes sqlBytes = null; + + sqlBytes = reader.GetSqlBytes(0); + Assert.Equal("5ade", ToHexString(sqlBytes.Value)); + + sqlBytes = reader.GetSqlBytes(1); + Assert.Equal("0000000001040300000000000000000059400000000000005940000000000000344000000000008066400000000000806640000000000080664001000000010000000001000000ffffffff0000000002", ToHexString(sqlBytes.Value)); + + sqlBytes = reader.GetSqlBytes(2); + Assert.Equal("e610000001148716d9cef7d34740d7a3703d0a975ec08716d9cef7d34740cba145b6f3955ec0", ToHexString(sqlBytes.Value)); + + if (behavior == CommandBehavior.Default) + { + sqlBytes = reader.GetSqlBytes(0); + Assert.Equal("5ade", ToHexString(sqlBytes.Value)); + } + } + } + } + + [CheckConnStrSetupFact] + public static void TestUdtSqlDataReaderGetBytesSequentialAccess() + { + TestUdtSqlDataReaderGetBytes(CommandBehavior.SequentialAccess); + } + + [CheckConnStrSetupFact] + public static void TestUdtSqlDataReaderGetBytes() + { + TestUdtSqlDataReaderGetBytes(CommandBehavior.Default); + } + + private static void TestUdtSqlDataReaderGetBytes(CommandBehavior behavior) + { + using (SqlConnection connection = new SqlConnection(DataTestUtility.TcpConnStr)) + { + connection.Open(); + SqlCommand command = connection.CreateCommand(); + command.CommandText = "select hierarchyid::Parse('/1/1/3/') as col0, geometry::Parse('LINESTRING (100 100, 20 180, 180 180)') as col1, geography::Parse('LINESTRING(-122.360 47.656, -122.343 47.656)') as col2"; + using (SqlDataReader reader = command.ExecuteReader(behavior)) + { + Assert.True(reader.Read()); + + int byteCount = 0; + byte[] bytes = null; + + byteCount = (int)reader.GetBytes(0, 0, null, 0, 0); + Assert.True(byteCount > 0); + bytes = new byte[byteCount]; + reader.GetBytes(0, 0, bytes, 0, bytes.Length); + Assert.Equal("5ade", ToHexString(bytes)); + + byteCount = (int)reader.GetBytes(1, 0, null, 0, 0); + Assert.True(byteCount > 0); + bytes = new byte[byteCount]; + reader.GetBytes(1, 0, bytes, 0, bytes.Length); + Assert.Equal("0000000001040300000000000000000059400000000000005940000000000000344000000000008066400000000000806640000000000080664001000000010000000001000000ffffffff0000000002", ToHexString(bytes)); + + byteCount = (int)reader.GetBytes(2, 0, null, 0, 0); + Assert.True(byteCount > 0); + bytes = new byte[byteCount]; + reader.GetBytes(2, 0, bytes, 0, bytes.Length); + Assert.Equal("e610000001148716d9cef7d34740d7a3703d0a975ec08716d9cef7d34740cba145b6f3955ec0", ToHexString(bytes)); + + if (behavior == CommandBehavior.Default) + { + byteCount = (int)reader.GetBytes(0, 0, null, 0, 0); + Assert.True(byteCount > 0); + bytes = new byte[byteCount]; + reader.GetBytes(0, 0, bytes, 0, bytes.Length); + Assert.Equal("5ade", ToHexString(bytes)); + } + } + } + } + + [CheckConnStrSetupFact] + public static void TestUdtSqlDataReaderGetStreamSequentialAccess() + { + TestUdtSqlDataReaderGetStream(CommandBehavior.SequentialAccess); + } + + [CheckConnStrSetupFact] + public static void TestUdtSqlDataReaderGetStream() + { + TestUdtSqlDataReaderGetStream(CommandBehavior.Default); + } + + private static void TestUdtSqlDataReaderGetStream(CommandBehavior behavior) + { + using (SqlConnection connection = new SqlConnection(DataTestUtility.TcpConnStr)) + { + connection.Open(); + SqlCommand command = connection.CreateCommand(); + command.CommandText = "select hierarchyid::Parse('/1/1/3/') as col0, geometry::Parse('LINESTRING (100 100, 20 180, 180 180)') as col1, geography::Parse('LINESTRING(-122.360 47.656, -122.343 47.656)') as col2"; + using (SqlDataReader reader = command.ExecuteReader(behavior)) + { + Assert.True(reader.Read()); + + MemoryStream buffer = null; + byte[] bytes = null; + + buffer = new MemoryStream(); + using (Stream stream = reader.GetStream(0)) + { + stream.CopyTo(buffer); + } + bytes = buffer.ToArray(); + Assert.Equal("5ade", ToHexString(bytes)); + + buffer = new MemoryStream(); + using (Stream stream = reader.GetStream(1)) + { + stream.CopyTo(buffer); + } + bytes = buffer.ToArray(); + Assert.Equal("0000000001040300000000000000000059400000000000005940000000000000344000000000008066400000000000806640000000000080664001000000010000000001000000ffffffff0000000002", ToHexString(bytes)); + + buffer = new MemoryStream(); + using (Stream stream = reader.GetStream(2)) + { + stream.CopyTo(buffer); + } + bytes = buffer.ToArray(); + Assert.Equal("e610000001148716d9cef7d34740d7a3703d0a975ec08716d9cef7d34740cba145b6f3955ec0", ToHexString(bytes)); + + if (behavior == CommandBehavior.Default) + { + buffer = new MemoryStream(); + using (Stream stream = reader.GetStream(0)) + { + stream.CopyTo(buffer); + } + bytes = buffer.ToArray(); + Assert.Equal("5ade", ToHexString(bytes)); + } + } + } + } + + [CheckConnStrSetupFact] + public static void TestUdtSchemaMetadata() + { + using (SqlConnection connection = new SqlConnection(DataTestUtility.TcpConnStr)) + { + connection.Open(); + SqlCommand command = connection.CreateCommand(); + command.CommandText = "select hierarchyid::Parse('/1/1/3/') as col0, geometry::Parse('LINESTRING (100 100, 20 180, 180 180)') as col1, geography::Parse('LINESTRING(-122.360 47.656, -122.343 47.656)') as col2"; + using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.SchemaOnly)) + { + ReadOnlyCollection columns = reader.GetColumnSchema(); + + DbColumn column = null; + + // Validate Microsoft.SqlServer.Types.SqlHierarchyId, Microsoft.SqlServer.Types, Version=11.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91 + column = columns[0]; + Assert.Equal(column.ColumnName, "col0"); + Assert.True(column.DataTypeName.EndsWith(".hierarchyid"), $"Unexpected DataTypeName \"{column.DataTypeName}\""); + Assert.NotNull(column.UdtAssemblyQualifiedName); + AssertSqlUdtAssemblyQualifiedName(column.UdtAssemblyQualifiedName, "Microsoft.SqlServer.Types.SqlHierarchyId"); + + // Validate Microsoft.SqlServer.Types.SqlGeometry, Microsoft.SqlServer.Types, Version = 11.0.0.0, Culture = neutral, PublicKeyToken = 89845dcd8080cc91 + column = columns[1]; + Assert.Equal(column.ColumnName, "col1"); + Assert.True(column.DataTypeName.EndsWith(".geometry"), $"Unexpected DataTypeName \"{column.DataTypeName}\""); + Assert.NotNull(column.UdtAssemblyQualifiedName); + AssertSqlUdtAssemblyQualifiedName(column.UdtAssemblyQualifiedName, "Microsoft.SqlServer.Types.SqlGeometry"); + + // Validate Microsoft.SqlServer.Types.SqlGeography, Microsoft.SqlServer.Types, Version = 11.0.0.0, Culture = neutral, PublicKeyToken = 89845dcd8080cc91 + column = columns[2]; + Assert.Equal(column.ColumnName, "col2"); + Assert.True(column.DataTypeName.EndsWith(".geography"), $"Unexpected DataTypeName \"{column.DataTypeName}\""); + Assert.NotNull(column.UdtAssemblyQualifiedName); + AssertSqlUdtAssemblyQualifiedName(column.UdtAssemblyQualifiedName, "Microsoft.SqlServer.Types.SqlGeography"); + } + } + } + + private static void AssertSqlUdtAssemblyQualifiedName(string assemblyQualifiedName, string expectedType) + { + List parts = assemblyQualifiedName.Split(',').Select(x => x.Trim()).ToList(); + + string type = parts[0]; + string assembly = parts.Count < 2 ? string.Empty : parts[1]; + string version = parts.Count < 3 ? string.Empty : parts[2]; + string culture = parts.Count < 4 ? string.Empty : parts[3]; + string token = parts.Count < 5 ? string.Empty : parts[4]; + + Assert.Equal(expectedType, type); + Assert.Equal("Microsoft.SqlServer.Types", assembly); + Assert.True(version.StartsWith("Version")); + Assert.True(culture.StartsWith("Culture")); + Assert.True(token.StartsWith("PublicKeyToken")); + } + + private static string ToHexString(byte[] bytes) + { + StringBuilder hex = new StringBuilder(bytes.Length * 2); + foreach (byte b in bytes) + { + hex.AppendFormat("{0:x2}", b); + } + + return hex.ToString(); + } + } +} diff --git a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Address/Address.cs b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Address/Address.cs new file mode 100644 index 0000000000..426edfafee --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Address/Address.cs @@ -0,0 +1,194 @@ +// 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.Data.SqlTypes; + +using Microsoft.SqlServer.Server; + +[Serializable] +[SqlUserDefinedType(Format.UserDefined, IsByteOrdered = false, MaxByteSize = 500)] +public class Address : INullable, IBinarySerialize +{ + public static Address Null { get { return new Address(true); } } + + //****************************************************** + // Constructors + //****************************************************** + + // Constructor for a null value: Address.Null + // fNull is not used but needed because compiler doesn't let + // struct have parameterless constructors + private Address(bool fNull) + { + m_fNotNull = false; + m_firstline = SqlString.Null; + m_secondline = SqlString.Null; + } + + public Address(SqlString line1, SqlString line2) + { + m_firstline = line1; + m_secondline = line2; + m_fNotNull = true; + } + + public Address() + { + m_fNotNull = false; + m_firstline = SqlString.Null; + m_secondline = SqlString.Null; + } + + //****************************************************** + // INullable interface + //****************************************************** + + // INullable + public bool IsNull + { + get { return !m_fNotNull; } + } + + //****************************************************** + // Common static and instance methods for SQL UDTs + //****************************************************** + public const int MaxByteSize = 500; + public const bool IsFixedLength = false; + public const bool IsByteOrdered = true; + + public void Read(BinaryReader r) + { + m_firstline = new SqlString(r.ReadString()); + m_secondline = new SqlString(r.ReadString()); + m_fNotNull = BitConverter.ToBoolean(r.ReadBytes(1), 0); + } + + public void Write(BinaryWriter w) + { + w.Write(m_firstline.ToString()); + w.Write(m_secondline.ToString()); + w.Write(m_fNotNull); + } + + public void FillFromBytes(SqlBytes value) + { + if (value.IsNull) + { + m_fNotNull = false; + m_firstline = SqlString.Null; + m_secondline = SqlString.Null; + return; + } + + System.Text.UnicodeEncoding e = new System.Text.UnicodeEncoding(); + string str = e.GetString(value.Buffer); + + string[] twolines = new string[2]; + char[] seperator = { '|' }; + twolines = str.Split(seperator); + + m_firstline = twolines[0]; + m_secondline = twolines.Length > 1 ? twolines[1] : SqlString.Null; + m_fNotNull = true; + + return; + } + + public void FillBytes(SqlBytes value) + { + if (IsNull) + { + if (value.IsNull) + return; + else + { + value.SetNull(); + return; + } + } + + SqlString str; + if ((object)m_secondline == null || m_secondline.IsNull) + str = m_firstline; + else + { + str = string.Concat(m_firstline, "|"); + str = string.Concat(str, m_secondline); + } + + byte[] stringData = str.GetUnicodeBytes(); + int i; + for (i = 0; i < stringData.Length; i++) + value[i] = stringData[i]; + value.SetLength(i); + + return; + } + + public override string ToString() + { + if (IsNull) + return "Null"; + else + return Value.ToString(); + } + + public static Address Parse(SqlString s) + { + if (s.IsNull) + return Address.Null; + + string str = s.ToString(); + string[] twolines = new string[2]; + + // using || to indicate the separation between + // address line 1 and 2, assume it won't appear + // in any address + char[] seperator = { '|', '|' }; + twolines = str.Split(seperator); + + if (twolines.Length == 2) + return new Address(twolines[0], twolines[1]); + else + return new Address(twolines[0], SqlString.Null); + } + + //****************************************************** + // Address Specific Methods + //****************************************************** + public SqlString GetFirstLine() + { + return m_firstline; + } + + public SqlString GetSecondLine() + { + return m_secondline; + } + + public SqlString Value + { + get + { + if (m_fNotNull) + { + if (m_secondline.IsNull) + return m_firstline; + else + return string.Concat(m_firstline, m_secondline); + } + else + throw new SqlNullValueException(); + } + } + + //****************************************************** + // Address Private Members + //****************************************************** + private SqlString m_firstline; + private SqlString m_secondline; + private bool m_fNotNull; //false if null, default ctor makes it null +} \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Address/Address.csproj b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Address/Address.csproj new file mode 100644 index 0000000000..12af881449 --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Address/Address.csproj @@ -0,0 +1,13 @@ + + + + + {D1392B54-998A-4F27-BC17-4CE149117BCC} + + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Address/Configurations.props b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Address/Configurations.props new file mode 100644 index 0000000000..e172d8ba0c --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Address/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Circle.cs b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Circle.cs new file mode 100644 index 0000000000..ac8e848fa9 --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Circle.cs @@ -0,0 +1,152 @@ +// 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.Text; +using System.Data.SqlTypes; +using System.IO; + +using Microsoft.SqlServer.Server; + +[Serializable] +[SqlUserDefinedType(Format.UserDefined, IsByteOrdered = false, MaxByteSize = 30)] +public class Circle : INullable, IBinarySerialize +{ + private Point1 center = new Point1(); + private int rad; + + private bool fIsNull = false; + + public static Circle Null { get { return new Circle(true); } } + + public const int MaxByteSize = 30; + public const bool IsFixedLength = true; + public const bool IsByteOrdered = false; + + + public void Read(BinaryReader r) + { + center = new Point1(); + center.Read(r); + rad = r.ReadInt32(); + fIsNull = BitConverter.ToBoolean(r.ReadBytes(1), 0); + } + + public void Write(BinaryWriter w) + { + center.Write(w); + w.Write(rad); + w.Write(fIsNull); + } + + public Circle() + { + center.X = 0; + center.Y = 0; + rad = 0; + fIsNull = false; + + } + + public Circle(bool fNull) + { + fIsNull = true; + } + + public bool IsNull + { + get + { + return fIsNull; + } + } + + public void FillFromBytes(SqlBytes data) + { + if (data.IsNull) + { + fIsNull = true; + return; + } + + if (data.Length != 12) + throw new ArgumentException(); + byte[] value = data.Value; + + //read x1,y1,x2,y2 + center.X = BitConverter.ToInt32(value, 0); + center.Y = BitConverter.ToInt32(value, 4); + rad = BitConverter.ToInt32(value, 8); + } + + public void FillBytes(SqlBytes data) + { + if (fIsNull) + { + if (data.IsNull) + return; + else + { + data.SetNull(); + return; + } + } + + byte[] bigbytes = new byte[12]; + byte[] bytes = BitConverter.GetBytes(center.X); + bytes.CopyTo(bigbytes, 0); + bytes = BitConverter.GetBytes(center.Y); + bytes.CopyTo(bigbytes, 4); + + bytes = BitConverter.GetBytes(rad); + bytes.CopyTo(bigbytes, 8); + + int i; + for (i = 0; i < bigbytes.Length; i++) + data[i] = bigbytes[i]; + data.SetLength(i); + + } + + //it should be x1,y1,x2,y2 + public static Circle Parse(SqlString data) + { + string[] array = data.Value.Split(new char[] { ',' }); + + if (array.Length != 3) + throw new ArgumentException(); + Circle circ = new Circle(); + + circ.center.X = int.Parse(array[0]); + circ.center.Y = int.Parse(array[1]); + circ.rad = int.Parse(array[2]); + return circ; + } + + public override string ToString() + { + StringBuilder builder = new StringBuilder(); + builder.Append(center.ToString()); + builder.Append(","); + builder.Append(rad.ToString()); + + return builder.ToString(); + } + + public Point1 Center + { + get + { + return center; + } + } + + public int Radius + { + get + { + return rad; + } + } +} diff --git a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Circle.csproj b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Circle.csproj new file mode 100644 index 0000000000..11276fde2d --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Circle.csproj @@ -0,0 +1,14 @@ + + + + + {6C88F00F-9597-43AD-9E5F-9B344DA3B16F} + + + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Configurations.props b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Configurations.props new file mode 100644 index 0000000000..e172d8ba0c --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Point1.cs b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Point1.cs new file mode 100644 index 0000000000..54b669943f --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Point1.cs @@ -0,0 +1,182 @@ +// 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.Data.SqlTypes; +using System.Text; + +using Microsoft.SqlServer.Server; + +[Serializable] +[SqlUserDefinedType(Format.UserDefined, IsByteOrdered = true, MaxByteSize = 9)] +public class Point1 : INullable, IBinarySerialize +{ + private int x; + private int y; + private bool fIsNull = false; + + public static Point1 Null { get { return new Point1(true); } } + public const int MaxByteSize = 9; + public const bool IsFixedLength = true; + public const bool IsByteOrdered = false; + + public void Read(BinaryReader r) + { + x = r.ReadInt32(); + y = r.ReadInt32(); + fIsNull = BitConverter.ToBoolean(r.ReadBytes(1), 0); + } + + public void Write(BinaryWriter w) + { + w.Write(x); + w.Write(y); + w.Write(fIsNull); + } + + public Point1() + { + x = 0; + y = 0; + fIsNull = false; + } + + public Point1(bool fNull) + { + fIsNull = true; + } + + public Point1(int ix, int iy) + { + x = ix; + y = iy; + fIsNull = false; + } + + public bool IsNull + { + get + { + return fIsNull; + } + } + + public void FillFromBytesInternal(byte[] data) + { + if (data.Length != 9) + throw new ArgumentException(); + + x = BitConverter.ToInt32(data, 0); + y = BitConverter.ToInt32(data, 4); + + } + + public void FillFromBytes(SqlBytes value) + { + if (value.IsNull) + { + fIsNull = true; + return; + } + + byte[] bytes = value.Value; + FillFromBytesInternal(bytes); + + fIsNull = false; + return; + } + + public void FillBytes(SqlBytes value) + { + if (fIsNull) + { + if (value.IsNull) + return; + else + { + value.SetNull(); + return; + } + } + + byte[] bigbytes = new byte[9]; + byte[] bytes = BitConverter.GetBytes(x); + bytes.CopyTo(bigbytes, 0); + bytes = BitConverter.GetBytes(y); + bytes.CopyTo(bigbytes, 4); + + int i; + for (i = 0; i < bigbytes.Length; i++) + value[i] = bigbytes[i]; + value.SetLength(i); + + return; + } + + public static Point1 Parse(SqlString data) + { + string[] array = data.Value.Split(new char[] { ',' }); + int x; + int y; + + if (array.Length != 2) + throw new ArgumentException(); + x = int.Parse(array[0]); + y = int.Parse(array[1]); + + return new Point1(x, y); + } + + public override string ToString() + { + StringBuilder builder = new StringBuilder(); + builder.Append(x); + builder.Append(","); + builder.Append(y); + + return builder.ToString(); + } + + public int X + { + get + { + return x; + } + set + { + x = value; + } + } + + public int Y + { + get + { + return y; + } + set + { + y = value; + } + } + + public double Distance() + { + return DistanceFromXY(0, 0); + } + + public double DistanceFrom(Point1 pFrom) + { + return DistanceFromXY(pFrom.x, pFrom.y); + } + + public double DistanceFromXY(int ix, int iy) + { + return Math.Sqrt(Math.Pow(ix - x, 2.0) + Math.Pow(iy - y, 2.0)); + } + + public const int MaxByteSizeValue = 12; +} diff --git a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Configurations.props b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Configurations.props new file mode 100644 index 0000000000..e172d8ba0c --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Line.cs b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Line.cs new file mode 100644 index 0000000000..a6b18599b2 --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Line.cs @@ -0,0 +1,164 @@ +// 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.Text; +using System.Data.SqlTypes; +using System.IO; + +using Microsoft.SqlServer.Server; + +[Serializable] +[SqlUserDefinedType(Format.UserDefined, IsByteOrdered = false, MaxByteSize = 20)] +public class Line : INullable, IBinarySerialize +{ + private Point start = new Point(); + private Point end = new Point(); + + private bool fIsNull = false; + + public static Line Null { get { return new Line(true); } } + + public const int MaxByteSize = 20; + public const bool IsFixedLength = true; + public const bool IsByteOrdered = false; + + public Line() + { + start.X = start.Y = end.X = end.Y = 0; + } + + public Line(bool fNull) + { + fIsNull = true; + } + + public Line(Point ix, Point iy) + { + start.X = ix.X; + start.Y = ix.Y; + end.X = iy.X; + end.Y = iy.Y; + } + + public bool IsNull + { + get + { + return fIsNull; + } + } + + public void Read(BinaryReader r) + { + start = new Point(); + start.Read(r); + end = new Point(); + end.Read(r); + fIsNull = BitConverter.ToBoolean(r.ReadBytes(1), 0); + } + + public void Write(BinaryWriter w) + { + start.Write(w); + end.Write(w); + w.Write(fIsNull); + } + + public void FillFromBytes(SqlBytes data) + { + if (data.IsNull) + { + fIsNull = true; + return; + } + + if (data.Length != 16) + throw new ArgumentException(); + byte[] value = data.Value; + + //read x1,y1,x2,y2 + start.X = BitConverter.ToInt32(value, 0); + start.Y = BitConverter.ToInt32(value, 4); + end.X = BitConverter.ToInt32(value, 8); + end.Y = BitConverter.ToInt32(value, 12); + } + + public void FillBytes(SqlBytes data) + { + if (fIsNull) + { + if (data.IsNull) + return; + else + { + data.SetNull(); + return; + } + } + + byte[] bigbytes = new byte[16]; + byte[] bytes = BitConverter.GetBytes(start.X); + bytes.CopyTo(bigbytes, 0); + bytes = BitConverter.GetBytes(start.Y); + bytes.CopyTo(bigbytes, 4); + + bytes = BitConverter.GetBytes(end.X); + bytes.CopyTo(bigbytes, 8); + bytes = BitConverter.GetBytes(end.Y); + bytes.CopyTo(bigbytes, 12); + + int i; + for (i = 0; i < bigbytes.Length; i++) + data[i] = bigbytes[i]; + data.SetLength(i); + } + + //it should be x1,y1,x2,y2 + public static Line Parse(SqlString data) + { + string[] array = data.Value.Split(new char[] { ',' }); + + if (array.Length != 4) + throw new ArgumentException(); + Line line = new Line(); + line.start.X = int.Parse(array[0]); + line.start.Y = int.Parse(array[1]); + line.end.X = int.Parse(array[2]); + line.end.Y = int.Parse(array[3]); + + return line; + } + + public override string ToString() + { + StringBuilder builder = new StringBuilder(); + builder.Append(start.ToString()); + builder.Append(","); + builder.Append(end.ToString()); + + return builder.ToString(); + } + + public double Length() + { + return end.DistanceFrom(start); + } + + public Point Start + { + get + { + return start; + } + } + + public Point End + { + get + { + return end; + } + } +} \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Point.cs b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Point.cs new file mode 100644 index 0000000000..ee3fecd55e --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Point.cs @@ -0,0 +1,181 @@ +// 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.Data.SqlTypes; +using System.Text; + +using Microsoft.SqlServer.Server; + +[Serializable] +[SqlUserDefinedType(Format.UserDefined, IsByteOrdered = true, MaxByteSize = 9)] +public class Point : INullable, IBinarySerialize +{ + private int x; + private int y; + private bool fIsNull = false; + + public static Point Null { get { return new Point(true); } } + public const int MaxByteSize = 9; + public const bool IsFixedLength = true; + public const bool IsByteOrdered = false; + + public void Read(BinaryReader r) + { + x = r.ReadInt32(); + y = r.ReadInt32(); + fIsNull = BitConverter.ToBoolean(r.ReadBytes(1), 0); + } + + public void Write(BinaryWriter w) + { + w.Write(x); + w.Write(y); + w.Write(fIsNull); + } + + public Point() + { + x = 0; + y = 0; + fIsNull = false; + } + + public Point(bool fNull) + { + fIsNull = true; + } + + public Point(int ix, int iy) + { + x = ix; + y = iy; + fIsNull = false; + } + + public bool IsNull + { + get + { + return fIsNull; + } + } + + public void FillFromBytesInternal(byte[] data) + { + if (data.Length != 9) + throw new ArgumentException(); + + x = BitConverter.ToInt32(data, 0); + y = BitConverter.ToInt32(data, 4); + } + + public void FillFromBytes(SqlBytes value) + { + if (value.IsNull) + { + fIsNull = true; + return; + } + + byte[] bytes = value.Value; + FillFromBytesInternal(bytes); + + fIsNull = false; + return; + } + + public void FillBytes(SqlBytes value) + { + if (fIsNull) + { + if (value.IsNull) + return; + else + { + value.SetNull(); + return; + } + } + + byte[] bigbytes = new byte[9]; + byte[] bytes = BitConverter.GetBytes(x); + bytes.CopyTo(bigbytes, 0); + bytes = BitConverter.GetBytes(y); + bytes.CopyTo(bigbytes, 4); + + int i; + for (i = 0; i < bigbytes.Length; i++) + value[i] = bigbytes[i]; + value.SetLength(i); + + return; + } + + public static Point Parse(SqlString data) + { + string[] array = data.Value.Split(new char[] { ',' }); + int x; + int y; + + if (array.Length != 2) + throw new ArgumentException(); + x = int.Parse(array[0]); + y = int.Parse(array[1]); + + return new Point(x, y); + } + + public override string ToString() + { + StringBuilder builder = new StringBuilder(); + builder.Append(x); + builder.Append(","); + builder.Append(y); + + return builder.ToString(); + } + + public int X + { + get + { + return x; + } + set + { + x = value; + } + } + + public int Y + { + get + { + return y; + } + set + { + y = value; + } + } + + public double Distance() + { + return DistanceFromXY(0, 0); + } + + public double DistanceFrom(Point pFrom) + { + return DistanceFromXY(pFrom.x, pFrom.y); + } + + public double DistanceFromXY(int ix, int iy) + { + return Math.Sqrt(Math.Pow(ix - x, 2.0) + Math.Pow(iy - y, 2.0)); + } + + public const int MaxByteSizeValue = 12; +} \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Shapes.csproj b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Shapes.csproj new file mode 100644 index 0000000000..0fa401ac5d --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Shapes.csproj @@ -0,0 +1,14 @@ + + + + + {B73A7063-37C3-415D-AD53-BB3DA20ABD6E} + + + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Utf8String/Configurations.props b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Utf8String/Configurations.props new file mode 100644 index 0000000000..e172d8ba0c --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Utf8String/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Utf8String/Utf8String.cs b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Utf8String/Utf8String.cs new file mode 100644 index 0000000000..677250b0bb --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Utf8String/Utf8String.cs @@ -0,0 +1,314 @@ +// 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.Data.SqlTypes; +using System.Globalization; + +using Microsoft.SqlServer.Server; + +namespace Microsoft.Samples.SqlServer +{ + [Serializable] + [Microsoft.SqlServer.Server.SqlUserDefinedType(Microsoft.SqlServer.Server.Format.UserDefined, IsByteOrdered = true, MaxByteSize = 8000)] + public class Utf8String : INullable, IComparable, Microsoft.SqlServer.Server.IBinarySerialize + { + #region conversion to/from Unicode strings + /// + /// Parse the given string and return a utf8 representation for it. + /// + /// + /// + public static Utf8String Parse(SqlString sqlString) + { + if (sqlString.IsNull) + return Utf8String.Null; + + return new Utf8String(sqlString.Value); + } + + /// + /// Get/Set the utf8 bytes for this string. + /// + public SqlBinary Utf8Bytes + { + get + { + if (this.IsNull) + return SqlBinary.Null; + + if (this.m_Bytes != null) + return this.m_Bytes; + + if (this.m_String != null) + { + this.m_Bytes = System.Text.Encoding.UTF8.GetBytes(this.m_String); + return new SqlBinary(this.m_Bytes); + } + + throw new NotSupportedException("cannot return bytes for empty instance"); + } + set + { + if (value.IsNull) + { + this.m_Bytes = null; + this.m_String = null; + } + else + { + this.m_Bytes = value.Value; + this.m_String = null; + } + } + } + + /// + /// Return a unicode string for this type. + /// + [Microsoft.SqlServer.Server.SqlMethod(IsDeterministic = true, IsPrecise = true, DataAccess = Microsoft.SqlServer.Server.DataAccessKind.None, SystemDataAccess = Microsoft.SqlServer.Server.SystemDataAccessKind.None)] + public override string ToString() + { + if (this.IsNull) + return null; + + if (this.m_String != null) + return this.m_String; + + if (this.m_Bytes != null) + { + this.m_String = System.Text.Encoding.UTF8.GetString(this.m_Bytes); + return this.m_String; + } + + throw new NotSupportedException("dont know how to return string from empty instance"); + } + + /// + /// Return a SqlStr + /// + public SqlString ToSqlString() + { + if (this.IsNull) + return SqlString.Null; + + return new SqlString(this.ToString()); + } + + private SqlString GetSortKeyUsingCultureInternal(CultureInfo culture, bool ignoreCase, + bool ignoreNonSpace, bool ignoreWidth) + { + if (this.IsNull) + return SqlString.Null; + + SqlCompareOptions compareOptions = SqlCompareOptions.None; + if (ignoreCase) + compareOptions = compareOptions | SqlCompareOptions.IgnoreCase; + + if (ignoreNonSpace) + compareOptions = compareOptions | SqlCompareOptions.IgnoreNonSpace; + + if (ignoreWidth) + compareOptions = compareOptions | SqlCompareOptions.IgnoreWidth; + + return new SqlString(this.ToString(), culture.LCID, compareOptions); + } + + [Microsoft.SqlServer.Server.SqlMethod(IsDeterministic = true, IsPrecise = true)] + public SqlString GetSortKeyUsingCulture(string cultureName, bool ignoreCase, + bool ignoreNonSpace, bool ignoreWidth) + { + CultureInfo culture = CultureInfo.CreateSpecificCulture(cultureName); + if (culture == null) + throw new ArgumentException(string.Format( + CultureInfo.InvariantCulture, + "Culture {0} not recognized.", cultureName)); + + return this.GetSortKeyUsingCultureInternal(culture, ignoreCase, + ignoreNonSpace, ignoreWidth); + } + + [Microsoft.SqlServer.Server.SqlMethod(IsDeterministic = false)] + public SqlString GetSortKey(bool ignoreCase, bool ignoreNonSpace, bool ignoreWidth) + { + return this.GetSortKeyUsingCultureInternal(CultureInfo.CurrentCulture, + ignoreCase, ignoreNonSpace, ignoreWidth); + } + + #endregion + + #region comparison operators + public override bool Equals(object obj) + { + return this.CompareTo(obj) == 0; + } + + public static bool operator ==(Utf8String utf8String, Utf8String other) + { + return utf8String.Equals(other); + } + + public static bool operator !=(Utf8String utf8String, Utf8String other) + { + return !(utf8String == other); + } + + public static bool operator <(Utf8String utf8String, Utf8String other) + { + return (utf8String.CompareTo(other) < 0); + } + + public static bool operator >(Utf8String utf8String, Utf8String other) + { + return (utf8String.CompareTo(other) > 0); + } + + private int CompareUsingCultureInternal(Utf8String other, CultureInfo culture, bool ignoreCase, + bool ignoreNonSpace, bool ignoreWidth) + { + // By definition + if (other == null) + return 1; + + if (this.IsNull) + if (other.IsNull) + return 0; + else + return -1; + + if (other.IsNull) + return 1; + + return this.GetSortKeyUsingCultureInternal(culture, ignoreCase, ignoreNonSpace, + ignoreWidth).CompareTo(other.GetSortKeyUsingCultureInternal(culture, ignoreCase, + ignoreNonSpace, ignoreWidth)); + } + + public int CompareUsingCulture(Utf8String other, string cultureName, bool ignoreCase, + bool ignoreNonSpace, bool ignoreWidth) + { + CultureInfo culture = CultureInfo.CreateSpecificCulture(cultureName); + if (culture == null) + throw new ArgumentException(string.Format( + CultureInfo.InvariantCulture, + "Culture {0} not recognized.", cultureName)); + + return this.CompareUsingCultureInternal(other, culture, ignoreCase, + ignoreNonSpace, ignoreWidth); + } + + public int Compare(Utf8String other, bool ignoreCase, + bool ignoreNonSpace, bool ignoreWidth) + { + return this.CompareUsingCultureInternal(other, CultureInfo.CurrentCulture, ignoreCase, + ignoreNonSpace, ignoreWidth); + } + + public override int GetHashCode() + { + if (this.IsNull) + return 0; + + return this.ToString().GetHashCode(); + } + + public int CompareTo(object obj) + { + if (obj == null) + return 1; //by definition + + Utf8String s = obj as Utf8String; + + if (s == null) + throw new ArgumentException("the argument to compare is not a Utf8String"); + + if (this.IsNull) + { + if (s.IsNull) + return 0; + + return -1; + } + + if (s.IsNull) + return 1; + + return this.ToString().CompareTo(s.ToString()); + } + + #endregion + + #region private state and constructors + private string m_String; + + private byte[] m_Bytes; + + public Utf8String(string value) + { + this.m_String = value; + } + + public Utf8String(byte[] bytes) + { + this.m_Bytes = bytes; + } + #endregion + + #region UserDefinedType boilerplate code + + public bool IsNull + { + get + { + return this.m_String == null && this.m_Bytes == null; + } + } + + public static Utf8String Null + { + get + { + Utf8String str = new Utf8String((string)null); + + return str; + } + } + + public Utf8String() + { + } + #endregion + + #region IBinarySerialize Members + public void Write(System.IO.BinaryWriter w) + { + byte header = (byte)(this.IsNull ? 1 : 0); + + w.Write(header); + if (header == 1) + return; + + byte[] bytes = this.Utf8Bytes.Value; + + w.Write(bytes.Length); + w.Write(bytes); + } + + public void Read(System.IO.BinaryReader r) + { + byte header = r.ReadByte(); + + if ((header & 1) > 0) + { + this.m_Bytes = null; + return; + } + + int length = r.ReadInt32(); + + this.m_Bytes = r.ReadBytes(length); + } + #endregion + } +} \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Utf8String/Utf8String.csproj b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Utf8String/Utf8String.csproj new file mode 100644 index 0000000000..f3c4b9223f --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Utf8String/Utf8String.csproj @@ -0,0 +1,13 @@ + + + + + {E0A6BB21-574B-43D9-890D-6E1144F2EE9E} + + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtBulkCopyTest.cs b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtBulkCopyTest.cs new file mode 100644 index 0000000000..083652810a --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtBulkCopyTest.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. +// See the LICENSE file in the project root for more information. + +namespace System.Data.SqlClient.ManualTesting.Tests +{ + public class UdtBulkCopyTest + { + private string _connStr; + + [CheckConnStrSetupFact] + public void RunCopyTest() + { + _connStr = (new SqlConnectionStringBuilder(DataTestUtility.TcpConnStr) { InitialCatalog = "UdtTestDb" }).ConnectionString; + SqlConnection conn = new SqlConnection(_connStr); + + string cities = DataTestUtility.GetUniqueNameForSqlServer("UdtBulkCopy_cities"); + string customers = DataTestUtility.GetUniqueNameForSqlServer("UdtBulkCopy_customers"); + string circles = DataTestUtility.GetUniqueNameForSqlServer("UdtBulkCopy_circles"); + + conn.Open(); + try + { + ExecuteNonQueryCommand($"create table {cities} (name sysname, location Point)", _connStr); + ExecuteNonQueryCommand($"create table {customers} (name nvarchar(30), address Address)", _connStr); + ExecuteNonQueryCommand($"create table {circles} (num int, def Circle)", _connStr); + + string expectedResults = + "ColumnName[0] = name" + Environment.NewLine + + "DataType[0] = nvarchar" + Environment.NewLine + + "FieldType[0] = System.String" + Environment.NewLine + + "ColumnName[1] = location" + Environment.NewLine + + "DataType[1] = UdtTestDb.dbo.Point" + Environment.NewLine + + "FieldType[1] = Point" + Environment.NewLine + + " redmond, p.X = 3, p.Y = 3, p.Distance() = 5" + Environment.NewLine + + " bellevue, p.X = 6, p.Y = 6, p.Distance() = 10" + Environment.NewLine + + " seattle, p.X = 10, p.Y = 10, p.Distance() = 14.8660687473185" + Environment.NewLine + + " portland, p.X = 20, p.Y = 20, p.Distance() = 25" + Environment.NewLine + + " LA, p.X = 3, p.Y = 3, p.Distance() = 5" + Environment.NewLine + + " SFO, p.X = 6, p.Y = 6, p.Distance() = 10" + Environment.NewLine + + " beaverton, p.X = 10, p.Y = 10, p.Distance() = 14.8660687473185" + Environment.NewLine + + " new york, p.X = 20, p.Y = 20, p.Distance() = 25" + Environment.NewLine + + " yukon, p.X = 20, p.Y = 20, p.Distance() = 32.0156211871642" + Environment.NewLine; + + CopyTableTest(_connStr, "cities", cities, expectedResults); + + expectedResults = + "ColumnName[0] = name" + Environment.NewLine + + "DataType[0] = nvarchar" + Environment.NewLine + + "FieldType[0] = System.String" + Environment.NewLine + + "ColumnName[1] = address" + Environment.NewLine + + "DataType[1] = UdtTestDb.dbo.Address" + Environment.NewLine + + "FieldType[1] = Address" + Environment.NewLine + + " first, Address 1 Address 2" + Environment.NewLine + + " second, 123 Park Lane New York" + Environment.NewLine + + " third, 21 Forest grove Portland" + Environment.NewLine + + " fourth, 34 Lake Blvd Seattle" + Environment.NewLine + + " fifth, A2 Meadows Bellevue" + Environment.NewLine; + + CopyTableTest(_connStr, "customers", customers, expectedResults); + + expectedResults = + "ColumnName[0] = num" + Environment.NewLine + + "DataType[0] = int" + Environment.NewLine + + "FieldType[0] = System.Int32" + Environment.NewLine + + "ColumnName[1] = def" + Environment.NewLine + + "DataType[1] = UdtTestDb.dbo.Circle" + Environment.NewLine + + "FieldType[1] = Circle" + Environment.NewLine + + " 1, Center = 1,2" + Environment.NewLine + + " 2, Center = 3,4" + Environment.NewLine + + " 3, Center = 11,23" + Environment.NewLine + + " 4, Center = 444,555" + Environment.NewLine + + " 5, Center = 1,2" + Environment.NewLine + + " 6, Center = 3,4" + Environment.NewLine + + " 7, Center = 11,23" + Environment.NewLine + + " 8, Center = 444,245" + Environment.NewLine; + + CopyTableTest(_connStr, "circles", circles, expectedResults); + } + finally + { + ExecuteNonQueryCommand($"drop table {cities}", _connStr); + ExecuteNonQueryCommand($"drop table {customers}", _connStr); + ExecuteNonQueryCommand($"drop table {circles}", _connStr); + } + } + + private void ExecuteNonQueryCommand(string cmdText, string connStr) + { + using (SqlConnection conn = new SqlConnection(connStr)) + using (SqlCommand cmd = conn.CreateCommand()) + { + conn.Open(); + cmd.CommandText = cmdText; + cmd.ExecuteNonQuery(); + } + } + + private void CopyTableTest(string connStr, string sourceTable, string targetTable, string expectedResults) + { + using (SqlConnection srcConn = new SqlConnection(connStr)) + { + srcConn.Open(); + + SqlCommand cmd = srcConn.CreateCommand(); + + cmd.CommandText = "select * from " + sourceTable; + using (SqlDataReader reader = cmd.ExecuteReader()) + using (SqlBulkCopy bc = new SqlBulkCopy(connStr)) + { + bc.DestinationTableName = targetTable; + bc.WriteToServer(reader); + } + cmd.CommandText = "select * from " + targetTable; + + DataTestUtility.AssertEqualsWithDescription( + expectedResults, UdtTestHelpers.DumpReaderString(cmd.ExecuteReader()), + "Unexpected bulk copy results."); + } + } + } +} + diff --git a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest.cs b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest.cs index 7a6722bd25..100d6b2d0e 100644 --- a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest.cs +++ b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest.cs @@ -2,358 +2,292 @@ // 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.Collections.Generic; -using System.Collections.ObjectModel; -using System.Data.Common; using System.Data.SqlTypes; -using System.IO; -using System.Linq; -using System.Text; +using Microsoft.Samples.SqlServer; using Xunit; namespace System.Data.SqlClient.ManualTesting.Tests { - public static class UdtTest + public class UdtTest { - [CheckConnStrSetupFact] - public static void GetSchemaTableTest() + private string _connStr; + + public UdtTest() { - using (SqlConnection conn = new SqlConnection(DataTestUtility.TcpConnStr)) - using (SqlCommand cmd = new SqlCommand("select hierarchyid::Parse('/1/') as col0", conn)) + _connStr = (new SqlConnectionStringBuilder(DataTestUtility.TcpConnStr) { InitialCatalog = "UdtTestDb" }).ConnectionString; + } + + [CheckConnStrSetupFact] + public void ReaderTest() + { + using (SqlConnection conn = new SqlConnection(_connStr)) { conn.Open(); - using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.KeyInfo)) + + SqlCommand com = new SqlCommand() { - DataTable schemaTable = reader.GetSchemaTable(); - DataTestUtility.AssertEqualsWithDescription(1, schemaTable.Rows.Count, "Unexpected schema table row count."); + Connection = conn, + CommandText = "select * from TestTable" + }; - string columnName = (string)(string)schemaTable.Rows[0][schemaTable.Columns["ColumnName"]]; - DataTestUtility.AssertEqualsWithDescription("col0", columnName, "Unexpected column name."); + SqlDataReader reader = com.ExecuteReader(); - string dataTypeName = (string)schemaTable.Rows[0][schemaTable.Columns["DataTypeName"]]; - DataTestUtility.AssertEqualsWithDescription("Northwind.sys.hierarchyid", dataTypeName, "Unexpected data type name."); + Utf8String[] expectedValues = + { + new Utf8String("a"), + new Utf8String("is"), + new Utf8String("test"), + new Utf8String("this") + }; + int currentValue = 0; + do + { + while (reader.Read()) + { + DataTestUtility.AssertEqualsWithDescription(1, reader.FieldCount, "Unexpected FieldCount."); + DataTestUtility.AssertEqualsWithDescription(expectedValues[currentValue], reader.GetValue(0), "Unexpected Value."); + DataTestUtility.AssertEqualsWithDescription(expectedValues[currentValue], reader.GetSqlValue(0), "Unexpected SQL Value."); - string udtAssemblyName = (string)schemaTable.Rows[0][schemaTable.Columns["UdtAssemblyQualifiedName"]]; - Assert.True(udtAssemblyName?.StartsWith("Microsoft.SqlServer.Types.SqlHierarchyId"), "Unexpected UDT assembly name: " + udtAssemblyName); + currentValue++; + } } + while (reader.NextResult()); + + DataTestUtility.AssertEqualsWithDescription(expectedValues.Length, currentValue, "Received less values than expected."); } } [CheckConnStrSetupFact] - public static void GetValueTest() + public void ExecuteScalarTest() { - using (SqlConnection conn = new SqlConnection(DataTestUtility.TcpConnStr)) - using (SqlCommand cmd = new SqlCommand("select hierarchyid::Parse('/1/') as col0", conn)) + using (SqlConnection conn = new SqlConnection(_connStr)) { conn.Open(); - using (SqlDataReader reader = cmd.ExecuteReader()) + + SqlCommand com = new SqlCommand() { - Assert.True(reader.Read()); + Connection = conn, + CommandText = "select * from TestTable" + }; - Assert.Throws(() => reader.GetValue(0)); - - Assert.Throws(() => reader.GetSqlValue(0)); - } + DataTestUtility.AssertEqualsWithDescription(new Utf8String("a"), com.ExecuteScalar(), "Unexpected value."); } } [CheckConnStrSetupFact] - public static void TestUdtSqlParameterThrowsPlatformNotSupportedException() + public void InputParameterTest() { - using (SqlConnection connection = new SqlConnection(DataTestUtility.TcpConnStr)) + using (SqlConnection conn = new SqlConnection(_connStr)) { - connection.Open(); - SqlCommand command = connection.CreateCommand(); + conn.Open(); - // This command is not executed on the server since we should throw well before we send the query - command.CommandText = "select @p as col0"; - - command.Parameters.Add(new SqlParameter + SqlCommand com = new SqlCommand() { - ParameterName = "@p", - SqlDbType = SqlDbType.Udt, - Direction = ParameterDirection.Input, - }); + Connection = conn, + CommandText = "insert into TestTable values (@p);" + + "SELECT * FROM TestTable" + }; + SqlParameter p = com.Parameters.Add("@p", SqlDbType.Udt); + p.UdtTypeName = "Utf8String"; + p.Value = new Utf8String("this is an input param test"); - Assert.Throws(() => + using (SqlTransaction trans = conn.BeginTransaction()) { - command.ExecuteReader(); - }); + com.Transaction = trans; + SqlDataReader reader = com.ExecuteReader(); - command.Parameters.Clear(); - command.Parameters.Add(new SqlParameter - { - ParameterName = "@p", - SqlDbType = SqlDbType.Udt, - Direction = ParameterDirection.InputOutput, - }); - - Assert.Throws(() => - { - command.ExecuteReader(); - }); - - command.Parameters.Clear(); - command.Parameters.Add(new SqlParameter - { - ParameterName = "@p", - SqlDbType = SqlDbType.Udt, - Direction = ParameterDirection.Output, - }); - - Assert.Throws(() => - { - command.ExecuteReader(); - }); - } - } - - [CheckConnStrSetupFact] - public static void TestUdtZeroByte() - { - using (SqlConnection connection = new SqlConnection(DataTestUtility.TcpConnStr)) - { - connection.Open(); - SqlCommand command = connection.CreateCommand(); - command.CommandText = "select hierarchyid::Parse('/') as col0"; - using (SqlDataReader reader = command.ExecuteReader()) - { - Assert.True(reader.Read()); - Assert.False(reader.IsDBNull(0)); - SqlBytes sqlBytes = reader.GetSqlBytes(0); - Assert.False(sqlBytes.IsNull, "Expected a zero length byte array"); - Assert.True(sqlBytes.Length == 0, "Expected a zero length byte array"); - } - } - } - - [CheckConnStrSetupFact] - public static void TestUdtSqlDataReaderGetSqlBytesSequentialAccess() - { - TestUdtSqlDataReaderGetSqlBytes(CommandBehavior.SequentialAccess); - } - - [CheckConnStrSetupFact] - public static void TestUdtSqlDataReaderGetSqlBytes() - { - TestUdtSqlDataReaderGetSqlBytes(CommandBehavior.Default); - } - - private static void TestUdtSqlDataReaderGetSqlBytes(CommandBehavior behavior) - { - using (SqlConnection connection = new SqlConnection(DataTestUtility.TcpConnStr)) - { - connection.Open(); - SqlCommand command = connection.CreateCommand(); - command.CommandText = "select hierarchyid::Parse('/1/1/3/') as col0, geometry::Parse('LINESTRING (100 100, 20 180, 180 180)') as col1, geography::Parse('LINESTRING(-122.360 47.656, -122.343 47.656)') as col2"; - using (SqlDataReader reader = command.ExecuteReader(behavior)) - { - Assert.True(reader.Read()); - - SqlBytes sqlBytes = null; - - sqlBytes = reader.GetSqlBytes(0); - Assert.Equal("5ade", ToHexString(sqlBytes.Value)); - - sqlBytes = reader.GetSqlBytes(1); - Assert.Equal("0000000001040300000000000000000059400000000000005940000000000000344000000000008066400000000000806640000000000080664001000000010000000001000000ffffffff0000000002", ToHexString(sqlBytes.Value)); - - sqlBytes = reader.GetSqlBytes(2); - Assert.Equal("e610000001148716d9cef7d34740d7a3703d0a975ec08716d9cef7d34740cba145b6f3955ec0", ToHexString(sqlBytes.Value)); - - if (behavior == CommandBehavior.Default) + Utf8String[] expectedValues = { - sqlBytes = reader.GetSqlBytes(0); - Assert.Equal("5ade", ToHexString(sqlBytes.Value)); - } - } - } - } + new Utf8String("a"), + new Utf8String("is"), + new Utf8String("test"), + new Utf8String("this"), + new Utf8String("this is an input param test") + }; - [CheckConnStrSetupFact] - public static void TestUdtSqlDataReaderGetBytesSequentialAccess() - { - TestUdtSqlDataReaderGetBytes(CommandBehavior.SequentialAccess); - } - - [CheckConnStrSetupFact] - public static void TestUdtSqlDataReaderGetBytes() - { - TestUdtSqlDataReaderGetBytes(CommandBehavior.Default); - } - - private static void TestUdtSqlDataReaderGetBytes(CommandBehavior behavior) - { - using (SqlConnection connection = new SqlConnection(DataTestUtility.TcpConnStr)) - { - connection.Open(); - SqlCommand command = connection.CreateCommand(); - command.CommandText = "select hierarchyid::Parse('/1/1/3/') as col0, geometry::Parse('LINESTRING (100 100, 20 180, 180 180)') as col1, geography::Parse('LINESTRING(-122.360 47.656, -122.343 47.656)') as col2"; - using (SqlDataReader reader = command.ExecuteReader(behavior)) - { - Assert.True(reader.Read()); - - int byteCount = 0; - byte[] bytes = null; - - byteCount = (int)reader.GetBytes(0, 0, null, 0, 0); - Assert.True(byteCount > 0); - bytes = new byte[byteCount]; - reader.GetBytes(0, 0, bytes, 0, bytes.Length); - Assert.Equal("5ade", ToHexString(bytes)); - - byteCount = (int)reader.GetBytes(1, 0, null, 0, 0); - Assert.True(byteCount > 0); - bytes = new byte[byteCount]; - reader.GetBytes(1, 0, bytes, 0, bytes.Length); - Assert.Equal("0000000001040300000000000000000059400000000000005940000000000000344000000000008066400000000000806640000000000080664001000000010000000001000000ffffffff0000000002", ToHexString(bytes)); - - byteCount = (int)reader.GetBytes(2, 0, null, 0, 0); - Assert.True(byteCount > 0); - bytes = new byte[byteCount]; - reader.GetBytes(2, 0, bytes, 0, bytes.Length); - Assert.Equal("e610000001148716d9cef7d34740d7a3703d0a975ec08716d9cef7d34740cba145b6f3955ec0", ToHexString(bytes)); - - if (behavior == CommandBehavior.Default) + int currentValue = 0; + do { - byteCount = (int)reader.GetBytes(0, 0, null, 0, 0); - Assert.True(byteCount > 0); - bytes = new byte[byteCount]; - reader.GetBytes(0, 0, bytes, 0, bytes.Length); - Assert.Equal("5ade", ToHexString(bytes)); - } - } - } - } - - [CheckConnStrSetupFact] - public static void TestUdtSqlDataReaderGetStreamSequentialAccess() - { - TestUdtSqlDataReaderGetStream(CommandBehavior.SequentialAccess); - } - - [CheckConnStrSetupFact] - public static void TestUdtSqlDataReaderGetStream() - { - TestUdtSqlDataReaderGetStream(CommandBehavior.Default); - } - - private static void TestUdtSqlDataReaderGetStream(CommandBehavior behavior) - { - using (SqlConnection connection = new SqlConnection(DataTestUtility.TcpConnStr)) - { - connection.Open(); - SqlCommand command = connection.CreateCommand(); - command.CommandText = "select hierarchyid::Parse('/1/1/3/') as col0, geometry::Parse('LINESTRING (100 100, 20 180, 180 180)') as col1, geography::Parse('LINESTRING(-122.360 47.656, -122.343 47.656)') as col2"; - using (SqlDataReader reader = command.ExecuteReader(behavior)) - { - Assert.True(reader.Read()); - - MemoryStream buffer = null; - byte[] bytes = null; - - buffer = new MemoryStream(); - using (Stream stream = reader.GetStream(0)) - { - stream.CopyTo(buffer); - } - bytes = buffer.ToArray(); - Assert.Equal("5ade", ToHexString(bytes)); - - buffer = new MemoryStream(); - using (Stream stream = reader.GetStream(1)) - { - stream.CopyTo(buffer); - } - bytes = buffer.ToArray(); - Assert.Equal("0000000001040300000000000000000059400000000000005940000000000000344000000000008066400000000000806640000000000080664001000000010000000001000000ffffffff0000000002", ToHexString(bytes)); - - buffer = new MemoryStream(); - using (Stream stream = reader.GetStream(2)) - { - stream.CopyTo(buffer); - } - bytes = buffer.ToArray(); - Assert.Equal("e610000001148716d9cef7d34740d7a3703d0a975ec08716d9cef7d34740cba145b6f3955ec0", ToHexString(bytes)); - - if (behavior == CommandBehavior.Default) - { - buffer = new MemoryStream(); - using (Stream stream = reader.GetStream(0)) + while (reader.Read()) { - stream.CopyTo(buffer); + DataTestUtility.AssertEqualsWithDescription(1, reader.FieldCount, "Unexpected FieldCount."); + DataTestUtility.AssertEqualsWithDescription(expectedValues[currentValue], reader.GetValue(0), "Unexpected Value."); + currentValue++; } - bytes = buffer.ToArray(); - Assert.Equal("5ade", ToHexString(bytes)); } + while (reader.NextResult()); + DataTestUtility.AssertEqualsWithDescription(expectedValues.Length, currentValue, "Received less values than expected."); + + reader.Close(); } } } [CheckConnStrSetupFact] - public static void TestUdtSchemaMetadata() + public void OutputParameterTest() { - using (SqlConnection connection = new SqlConnection(DataTestUtility.TcpConnStr)) + using (SqlConnection conn = new SqlConnection(_connStr)) { - connection.Open(); - SqlCommand command = connection.CreateCommand(); - command.CommandText = "select hierarchyid::Parse('/1/1/3/') as col0, geometry::Parse('LINESTRING (100 100, 20 180, 180 180)') as col1, geography::Parse('LINESTRING(-122.360 47.656, -122.343 47.656)') as col2"; - using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.SchemaOnly)) + conn.Open(); + + SqlCommand com = new SqlCommand() { - ReadOnlyCollection columns = reader.GetColumnSchema(); + Connection = conn, + CommandText = "UDTTest", + CommandType = CommandType.StoredProcedure + }; - DbColumn column = null; + SqlParameter p = com.Parameters.Add("@value", SqlDbType.Udt); + p.UdtTypeName = "Utf8String"; + p.Direction = ParameterDirection.Output; - // Validate Microsoft.SqlServer.Types.SqlHierarchyId, Microsoft.SqlServer.Types, Version=11.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91 - column = columns[0]; - Assert.Equal(column.ColumnName, "col0"); - Assert.Equal(typeof(byte[]), column.DataType); - Assert.NotNull(column.UdtAssemblyQualifiedName); - AssertSqlUdtAssemblyQualifiedName(column.UdtAssemblyQualifiedName, "Microsoft.SqlServer.Types.SqlHierarchyId"); + SqlDataReader reader = com.ExecuteReader(); - // Validate Microsoft.SqlServer.Types.SqlGeometry, Microsoft.SqlServer.Types, Version = 11.0.0.0, Culture = neutral, PublicKeyToken = 89845dcd8080cc91 - column = columns[1]; - Assert.Equal(column.ColumnName, "col1"); - Assert.Equal(typeof(byte[]), column.DataType); - Assert.NotNull(column.UdtAssemblyQualifiedName); - AssertSqlUdtAssemblyQualifiedName(column.UdtAssemblyQualifiedName, "Microsoft.SqlServer.Types.SqlGeometry"); + do + { + while (reader.Read()) + { + DataTestUtility.AssertEqualsWithDescription(0, reader.FieldCount, "Should not have any reader results."); + } + } + while (reader.NextResult()); - // Validate Microsoft.SqlServer.Types.SqlGeography, Microsoft.SqlServer.Types, Version = 11.0.0.0, Culture = neutral, PublicKeyToken = 89845dcd8080cc91 - column = columns[2]; - Assert.Equal(column.ColumnName, "col2"); - Assert.Equal(typeof(byte[]), column.DataType); - Assert.NotNull(column.UdtAssemblyQualifiedName); - AssertSqlUdtAssemblyQualifiedName(column.UdtAssemblyQualifiedName, "Microsoft.SqlServer.Types.SqlGeography"); + reader.Close(); + + DataTestUtility.AssertEqualsWithDescription(new Utf8String("this is an outparam test"), p.Value, "Unexpected parameter value."); + } + } + + [CheckConnStrSetupFact] + public void FillTest() + { + using (SqlConnection conn = new SqlConnection(_connStr)) + { + conn.Open(); + DataSet ds = new DataSet(); + + SqlDataAdapter adapter = new SqlDataAdapter("select * from TestTable", conn); + adapter.Fill(ds); + + Utf8String[] expectedValues = + { + new Utf8String("a"), + new Utf8String("is"), + new Utf8String("test"), + new Utf8String("this") + }; + VerifyDataSet(ds, expectedValues); + } + } + + [CheckConnStrSetupFact] + public void UpdateTest() + { + using (SqlConnection conn = new SqlConnection(_connStr)) + { + conn.Open(); + DataSet ds = new DataSet(); + + using (SqlTransaction trans = conn.BeginTransaction()) + { + SqlDataAdapter adapter = new SqlDataAdapter("select * from TestTable", conn); + SqlCommandBuilder builder = new SqlCommandBuilder(adapter); + adapter.SelectCommand.Transaction = trans; + + adapter.Fill(ds); + + ds.Tables[0].Rows[0][0] = new Utf8String("updated"); + + adapter.Update(ds); + + ds.Reset(); + + adapter.Fill(ds); + } + + Utf8String[] expectedValues = + { + new Utf8String("is"), + new Utf8String("test"), + new Utf8String("this"), + new Utf8String("updated") + }; + VerifyDataSet(ds, expectedValues); + } + } + + [CheckConnStrSetupFact] + public void NullTest() + { + using (SqlConnection conn = new SqlConnection(_connStr)) + { + conn.Open(); + + SqlCommand com = new SqlCommand() + { + Connection = conn, + CommandText = "insert into TestTableNull values (@p);" + + "SELECT * FROM TestTableNull" + }; + SqlParameter p = com.Parameters.Add("@p", SqlDbType.Udt); + p.UdtTypeName = "Utf8String"; + p.Value = DBNull.Value; + + using (SqlTransaction trans = conn.BeginTransaction()) + { + com.Transaction = trans; + SqlDataReader reader = com.ExecuteReader(); + + Utf8String[] expectedValues = + { + new Utf8String("this"), + new Utf8String("is"), + new Utf8String("a"), + new Utf8String("test") + }; + + int currentValue = 0; + do + { + while (reader.Read()) + { + DataTestUtility.AssertEqualsWithDescription(1, reader.FieldCount, "Unexpected FieldCount."); + if(currentValue < expectedValues.Length) + { + DataTestUtility.AssertEqualsWithDescription(expectedValues[currentValue], reader.GetValue(0), "Unexpected Value."); + DataTestUtility.AssertEqualsWithDescription(expectedValues[currentValue], reader.GetSqlValue(0), "Unexpected SQL Value."); + } + else + { + DataTestUtility.AssertEqualsWithDescription(DBNull.Value, reader.GetValue(0), "Unexpected Value."); + + Utf8String sqlValue = (Utf8String)reader.GetSqlValue(0); + INullable iface = sqlValue as INullable; + Assert.True(iface != null, "Expected interface cast to return a non-null value."); + Assert.True(iface.IsNull, "Expected interface cast to have IsNull==true."); + } + + currentValue++; + Assert.True(currentValue <= (expectedValues.Length + 1), "Expected to only hit one extra result."); + } + } + while (reader.NextResult()); + DataTestUtility.AssertEqualsWithDescription(currentValue, (expectedValues.Length + 1), "Did not hit all expected values."); + + reader.Close(); } } } - private static void AssertSqlUdtAssemblyQualifiedName(string assemblyQualifiedName, string expectedType) + private void VerifyDataSet(DataSet ds, Utf8String[] expectedValues) { - List parts = assemblyQualifiedName.Split(',').Select(x => x.Trim()).ToList(); - - string type = parts[0]; - string assembly = parts.Count < 2 ? string.Empty : parts[1]; - string version = parts.Count < 3 ? string.Empty : parts[2]; - string culture = parts.Count < 4 ? string.Empty : parts[3]; - string token = parts.Count < 5 ? string.Empty : parts[4]; - - Assert.Equal(expectedType, type); - Assert.Equal("Microsoft.SqlServer.Types", assembly); - Assert.True(version.StartsWith("Version")); - Assert.True(culture.StartsWith("Culture")); - Assert.True(token.StartsWith("PublicKeyToken")); - } - - private static string ToHexString(byte[] bytes) - { - StringBuilder hex = new StringBuilder(bytes.Length * 2); - foreach (byte b in bytes) + DataTestUtility.AssertEqualsWithDescription(1, ds.Tables.Count, "Unexpected tables count."); + DataTestUtility.AssertEqualsWithDescription(ds.Tables[0].Rows.Count, expectedValues.Length, "Unexpected rows count."); + for (int i = 0; i < ds.Tables[0].Rows.Count; i++) { - hex.AppendFormat("{0:x2}", b); + DataTestUtility.AssertEqualsWithDescription(1, ds.Tables[0].Columns.Count, "Unexpected columns count."); + DataTestUtility.AssertEqualsWithDescription(expectedValues[i], ds.Tables[0].Rows[i][0], "Unexpected value."); } - - return hex.ToString(); } } -} +} \ No newline at end of file diff --git a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs new file mode 100644 index 0000000000..1d3274f192 --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs @@ -0,0 +1,589 @@ +// 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.Data.SqlTypes; +using System.Reflection; +using System.Text; +using Microsoft.SqlServer.Server; + +namespace System.Data.SqlClient.ManualTesting.Tests +{ + public class UdtTest2 + { + private string _connStr = null; + + public UdtTest2() + { + _connStr = (new SqlConnectionStringBuilder(DataTestUtility.TcpConnStr) { InitialCatalog = "UdtTestDb" }).ConnectionString; + } + + [CheckConnStrSetupFact] + public void UDTParams_Early() + { + using (SqlConnection conn = new SqlConnection(_connStr)) + using (SqlCommand cmd = conn.CreateCommand()) + { + conn.Open(); + + cmd.Transaction = conn.BeginTransaction(); + cmd.CommandText = "vicinity"; // select proc + cmd.CommandType = CommandType.StoredProcedure; + + SqlParameter p = cmd.Parameters.Add("@boundary", SqlDbType.Udt); + p.UdtTypeName = "UdtTestDb.dbo.Point"; + Point pt = new Point() + { + X = 250, + Y = 250 + }; + p.Value = pt; + + using (SqlDataReader reader = cmd.ExecuteReader()) + { + DataTestUtility.AssertEqualsWithDescription( + (new Point(250, 250)).ToString(), p.Value.ToString(), + "Unexpected Point value."); + } + } + } + + [CheckConnStrSetupFact] + public void UDTParams_Binary() + { + using (SqlConnection conn = new SqlConnection(_connStr)) + using (SqlCommand cmd = new SqlCommand("vicinity", conn)) + { + conn.Open(); + cmd.CommandType = CommandType.StoredProcedure; + + SqlParameter p = cmd.Parameters.Add("@boundary", SqlDbType.VarBinary, 8); + p.Direction = ParameterDirection.Input; + + byte[] value = new byte[8]; + value[0] = 0xF0; + value[1] = 0; + value[2] = 0; + value[3] = 0; + value[4] = 0xF0; + value[5] = 0; + value[6] = 0; + value[7] = 0; + p.Value = new SqlBinary(value); + + DataTestUtility.AssertThrowsWrapper( + () => cmd.ExecuteReader(), + "Error converting data type varbinary to Point."); + } + } + + [CheckConnStrSetupFact] + public void UDTParams_Invalid2() + { + string spInsertCustomer = DataTestUtility.GetUniqueNameForSqlServer("spUdtTest2_InsertCustomer"); + string tableName = DataTestUtility.GetUniqueNameForSqlServer("UdtTest2"); + + using (SqlConnection conn = new SqlConnection(_connStr)) + using (SqlCommand cmd = conn.CreateCommand()) + { + conn.Open(); + + cmd.Transaction = conn.BeginTransaction(); + cmd.CommandText = "create table " + tableName + " (name nvarchar(30), address Address)"; + cmd.ExecuteNonQuery(); + cmd.CommandText = "create proc " + spInsertCustomer + "(@name nvarchar(30), @addr Address OUTPUT)" + " AS insert into " + tableName + " values (@name, @addr)"; + cmd.ExecuteNonQuery(); + try + { + cmd.CommandText = spInsertCustomer; + cmd.CommandType = CommandType.StoredProcedure; + + SqlParameter pName = cmd.Parameters.Add("@fname", SqlDbType.NVarChar, 20); + SqlParameter p = cmd.Parameters.Add("@addr", SqlDbType.Udt); + + Address addr = Address.Parse("customer whose name is address"); + p.UdtTypeName = "UdtTestDb.dbo.Address"; + p.Value = addr; + pName.Value = addr; + + DataTestUtility.AssertThrowsWrapper( + () => cmd.ExecuteReader(), + "Failed to convert parameter value from a Address to a String."); + } + finally + { + cmd.Transaction.Rollback(); + } + } + } + + [CheckConnStrSetupFact] + public void UDTParams_Invalid() + { + using (SqlConnection conn = new SqlConnection(_connStr)) + using (SqlCommand cmd = new SqlCommand("vicinity", conn)) + { + conn.Open(); + cmd.CommandType = CommandType.StoredProcedure; + + SqlParameter p = cmd.Parameters.Add("@boundary", SqlDbType.Udt); + p.UdtTypeName = "UdtTestDb.dbo.Point"; + p.Value = 32; + + DataTestUtility.AssertThrowsWrapper( + () => cmd.ExecuteReader(), + "Specified type is not registered on the target server. System.Int32"); + } + } + + [CheckConnStrSetupFact] + public void UDTParams_TypedNull() + { + string spInsertCustomer = DataTestUtility.GetUniqueNameForSqlServer("spUdtTest2_InsertCustomer"); + string tableName = DataTestUtility.GetUniqueNameForSqlServer("UdtTest2_Customer"); + + using (SqlConnection conn = new SqlConnection(_connStr)) + using (SqlCommand cmd = conn.CreateCommand()) + { + conn.Open(); + + cmd.Transaction = conn.BeginTransaction(); + cmd.CommandText = "create table " + tableName + " (name nvarchar(30), address Address)"; + cmd.ExecuteNonQuery(); + + // create proc sp_insert_customer(@name nvarchar(30), @addr Address OUTPUT) + // AS + // insert into customers values (@name, @addr) + cmd.CommandText = "create proc " + spInsertCustomer + " (@name nvarchar(30), @addr Address OUTPUT)" + " AS insert into " + tableName + " values (@name, @addr)"; + cmd.ExecuteNonQuery(); + try + { + cmd.CommandText = spInsertCustomer; + cmd.CommandType = CommandType.StoredProcedure; + + Address addr = Address.Parse("123 baker st || Redmond"); + SqlParameter pName = cmd.Parameters.Add("@name", SqlDbType.NVarChar, 20); + SqlParameter p = cmd.Parameters.Add("@addr", SqlDbType.Udt); + + p.UdtTypeName = "UdtTestDb.dbo.Address"; + p.Value = Address.Null; + pName.Value = "john"; + cmd.ExecuteNonQuery(); + + DataTestUtility.AssertEqualsWithDescription( + Address.Null.ToString(), p.Value.ToString(), + "Unexpected parameter value."); + } + finally + { + cmd.Transaction.Rollback(); + } + } + } + + [CheckConnStrSetupFact] + public void UDTParams_NullInput() + { + string spInsertCustomer = DataTestUtility.GetUniqueNameForSqlServer("spUdtTest2_InsertCustomer"); + string tableName = DataTestUtility.GetUniqueNameForSqlServer("UdtTest2_Customer"); + + using (SqlConnection conn = new SqlConnection(_connStr)) + using (SqlCommand cmd = conn.CreateCommand()) + { + conn.Open(); + + cmd.Transaction = conn.BeginTransaction(); + cmd.CommandText = "create table " + tableName + " (name nvarchar(30), address Address)"; + cmd.ExecuteNonQuery(); + cmd.CommandText = "create proc " + spInsertCustomer + "(@name nvarchar(30), @addr Address OUTPUT)" + " AS insert into " + tableName + " values (@name, @addr)"; + cmd.ExecuteNonQuery(); + try + { + cmd.CommandText = spInsertCustomer; + cmd.CommandType = CommandType.StoredProcedure; + + SqlParameter pName = cmd.Parameters.Add("@name", SqlDbType.NVarChar, 20); + SqlParameter p = cmd.Parameters.Add("@addr", SqlDbType.Udt); + + p.UdtTypeName = "UdtTestDb.dbo.Address"; + p.Value = null; + pName.Value = "john"; + + string spInsertCustomerNoBrackets = spInsertCustomer; + if (spInsertCustomer.StartsWith("[") && spInsertCustomer.EndsWith("]")) + spInsertCustomerNoBrackets = spInsertCustomer.Substring(1, spInsertCustomer.Length - 2); + string errorMsg = "Procedure or function '" + spInsertCustomerNoBrackets + "' expects parameter '@addr', which was not supplied."; + + DataTestUtility.AssertThrowsWrapper( + () => cmd.ExecuteNonQuery(), + errorMsg); + } + finally + { + cmd.Transaction.Rollback(); + } + } + } + + [CheckConnStrSetupFact] + public void UDTParams_InputOutput() + { + string spInsertCity = DataTestUtility.GetUniqueNameForSqlServer("spUdtTest2_InsertCity"); + string tableName = DataTestUtility.GetUniqueNameForSqlServer("UdtTest2"); + + using (SqlConnection conn = new SqlConnection(_connStr)) + { + conn.Open(); + + SqlTransaction tx = conn.BeginTransaction(); + SqlCommand cmd = conn.CreateCommand(); + + cmd.Transaction = tx; + + // create the table + cmd.CommandText = "create table " + tableName + " (name sysname,location Point)"; + cmd.ExecuteNonQuery(); + + // create sp + cmd.CommandText = "create proc " + spInsertCity + "(@name sysname, @location Point OUTPUT)" + " AS insert into " + tableName + " values (@name, @location)"; + cmd.ExecuteNonQuery(); + try + { + cmd.CommandText = spInsertCity; + cmd.CommandType = CommandType.StoredProcedure; + + SqlParameter pName = cmd.Parameters.Add("@name", SqlDbType.NVarChar, 20); + SqlParameter p = cmd.Parameters.Add("@location", SqlDbType.Udt); + + Point pt = new Point(100, 100); + p.UdtTypeName = "Point"; + p.Direction = ParameterDirection.InputOutput; + p.Value = pt; + pName.Value = "newcity"; + + cmd.ExecuteNonQuery(); + DataTestUtility.AssertEqualsWithDescription( + "141.42135623731", ((Point)(p.Value)).Distance().ToString(), + "Unexpected distance value."); + DataTestUtility.AssertEqualsWithDescription( + "141.42135623731", ((Point)(p.Value)).Distance().ToString(), + "Unexpected distance value after reading out param again."); + + cmd.Parameters.Clear(); + cmd.CommandType = CommandType.Text; + cmd.CommandText = "select * from " + tableName; + using (SqlDataReader reader = cmd.ExecuteReader()) + { + string expectedValue = " newcity, p.X = 100, p.Y = 100, p.Distance() = 141.42135623731" + Environment.NewLine; + DataTestUtility.AssertEqualsWithDescription( + expectedValue, UdtTestHelpers.DumpReaderString(reader, false), + "Unexpected reader dump string."); + } + } + finally + { + tx.Rollback(); + } + } + } + + [CheckConnStrSetupFact] + public void UDTFields_WrongType() + { + using (SqlConnection cn = new SqlConnection(_connStr)) + using (SqlCommand cmd = new SqlCommand("select name,location from cities order by name", cn)) + { + cn.Open(); + cmd.CommandType = CommandType.Text; + + using (SqlDataReader reader = cmd.ExecuteReader()) + { + reader.Read(); + + DataTestUtility.AssertEqualsWithDescription( + "beaverton", reader.GetValue(0), + "Unexpected reader value."); + DataTestUtility.AssertEqualsWithDescription( + "14.8660687473185", ((Point)reader.GetValue(1)).Distance().ToString(), + "Unexpected distance value."); + + reader.Read(); + + // retrieve the UDT as a string + DataTestUtility.AssertThrowsWrapper( + () => reader.GetString(1), + "Unable to cast object of type 'System.Byte[]' to type 'System.String'."); + } + } + } + + [CheckConnStrSetupFact] + public void UDT_DataSetFill() + { + using (SqlConnection cn = new SqlConnection(_connStr)) + using (SqlCommand cmd = new SqlCommand("select * from cities", cn)) + using (SqlDataAdapter adapter = new SqlDataAdapter("select * from cities", cn)) + { + cn.Open(); + + cmd.CommandType = CommandType.Text; + adapter.SelectCommand = cmd; + + DataSet ds = new DataSet("newset"); + + adapter.Fill(ds); + DataTestUtility.AssertEqualsWithDescription( + 1, ds.Tables.Count, + "Unexpected Tables count."); + DataTestUtility.AssertEqualsWithDescription( + typeof(Point), ds.Tables[0].Columns[1].DataType, + "Unexpected DataType."); + } + } + + [CheckConnStrSetupFact] + public void Reader_PointEarly() + { + using (SqlConnection conn = new SqlConnection(_connStr)) + using (SqlCommand cmd = new SqlCommand("select name, location from cities", conn)) + { + conn.Open(); + + string expectedReaderValues = + "ColumnName[0] = name" + Environment.NewLine + + "DataType[0] = nvarchar" + Environment.NewLine + + "FieldType[0] = System.String" + Environment.NewLine + + "ColumnName[1] = location" + Environment.NewLine + + "DataType[1] = UdtTestDb.dbo.Point" + Environment.NewLine + + "FieldType[1] = Point" + Environment.NewLine + + " redmond, p.X = 3, p.Y = 3, p.Distance() = 5" + Environment.NewLine + + " bellevue, p.X = 6, p.Y = 6, p.Distance() = 10" + Environment.NewLine + + " seattle, p.X = 10, p.Y = 10, p.Distance() = 14.8660687473185" + Environment.NewLine + + " portland, p.X = 20, p.Y = 20, p.Distance() = 25" + Environment.NewLine + + " LA, p.X = 3, p.Y = 3, p.Distance() = 5" + Environment.NewLine + + " SFO, p.X = 6, p.Y = 6, p.Distance() = 10" + Environment.NewLine + + " beaverton, p.X = 10, p.Y = 10, p.Distance() = 14.8660687473185" + Environment.NewLine + + " new york, p.X = 20, p.Y = 20, p.Distance() = 25" + Environment.NewLine + + " yukon, p.X = 20, p.Y = 20, p.Distance() = 32.0156211871642" + Environment.NewLine; + + using (SqlDataReader reader = cmd.ExecuteReader()) + { + DataTestUtility.AssertEqualsWithDescription( + expectedReaderValues, UdtTestHelpers.DumpReaderString(reader), + "Unexpected reader values."); + } + } + } + + [CheckConnStrSetupFact] + public void Reader_LineEarly() + { + using (SqlConnection conn = new SqlConnection(_connStr)) + using (SqlCommand cmd = new SqlCommand("select * from lines", conn)) + { + conn.Open(); + using (SqlDataReader reader = cmd.ExecuteReader()) + { + Line l = null; + Point p = null; + int x = 0, y = 0; + double length = 0; + + string expectedReaderValues = + "ids (int);pos (UdtTestDb.dbo.Line);"; + + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < reader.FieldCount; i++) + { + builder.Append(reader.GetName(i) + " (" + reader.GetDataTypeName(i) + ");"); + } + DataTestUtility.AssertEqualsWithDescription( + expectedReaderValues, builder.ToString(), + "Unexpected reader values."); + + string expectedLineValues = + "1, IsNull = False, Length = 2.82842712474619" + Environment.NewLine + + "2, IsNull = False, Length = 2.82842712474619" + Environment.NewLine + + "3, IsNull = False, Length = 9.8488578017961" + Environment.NewLine + + "4, IsNull = False, Length = 214.107449660212" + Environment.NewLine + + "5, IsNull = False, Length = 2.82842712474619" + Environment.NewLine + + "6, IsNull = False, Length = 2.82842712474619" + Environment.NewLine + + "7, IsNull = False, Length = 9.8488578017961" + Environment.NewLine + + "8, IsNull = False, Length = 214.107449660212" + Environment.NewLine; + + builder = new StringBuilder(); + while (reader.Read()) + { + builder.Append(reader.GetValue(0).ToString() + ", "); + l = (Line)reader.GetValue(1); + if (!l.IsNull) + { + p = l.Start; + x = p.X; + y = p.Y; + length = l.Length(); + } + + builder.Append("IsNull = " + l.IsNull + ", "); + builder.Append("Length = " + length); + builder.AppendLine(); + } + DataTestUtility.AssertEqualsWithDescription( + expectedLineValues, builder.ToString(), + "Unexpected Line values."); + } + } + } + + [CheckConnStrSetupFact] + public void Reader_PointLate() + { + using (SqlConnection conn = new SqlConnection(_connStr)) + using (SqlCommand cmd = new SqlCommand("select name, location from cities", conn)) + { + conn.Open(); + using (SqlDataReader reader = cmd.ExecuteReader()) + { + string expectedReaderValues = + "ColumnName[0] = name" + Environment.NewLine + + "DataType[0] = nvarchar" + Environment.NewLine + + "FieldType[0] = System.String" + Environment.NewLine + + "ColumnName[1] = location" + Environment.NewLine + + "DataType[1] = UdtTestDb.dbo.Point" + Environment.NewLine + + "FieldType[1] = Point" + Environment.NewLine + + " redmond, p.X = 3, p.Y = 3, p.Distance() = 5" + Environment.NewLine + + " bellevue, p.X = 6, p.Y = 6, p.Distance() = 10" + Environment.NewLine + + " seattle, p.X = 10, p.Y = 10, p.Distance() = 14.8660687473185" + Environment.NewLine + + " portland, p.X = 20, p.Y = 20, p.Distance() = 25" + Environment.NewLine + + " LA, p.X = 3, p.Y = 3, p.Distance() = 5" + Environment.NewLine + + " SFO, p.X = 6, p.Y = 6, p.Distance() = 10" + Environment.NewLine + + " beaverton, p.X = 10, p.Y = 10, p.Distance() = 14.8660687473185" + Environment.NewLine + + " new york, p.X = 20, p.Y = 20, p.Distance() = 25" + Environment.NewLine + + " yukon, p.X = 20, p.Y = 20, p.Distance() = 32.0156211871642" + Environment.NewLine; + + DataTestUtility.AssertEqualsWithDescription( + expectedReaderValues, UdtTestHelpers.DumpReaderString(reader), + "Unexpected reader values."); + } + } + } + + [CheckConnStrSetupFact] + public void Reader_CircleLate() + { + using (SqlConnection conn = new SqlConnection(_connStr)) + using (SqlCommand cmd = new SqlCommand("select * from circles", conn)) + { + conn.Open(); + using (SqlDataReader reader = cmd.ExecuteReader()) + { + string expectedReaderValues = + "ColumnName[0] = num" + Environment.NewLine + + "DataType[0] = int" + Environment.NewLine + + "FieldType[0] = System.Int32" + Environment.NewLine + + "ColumnName[1] = def" + Environment.NewLine + + "DataType[1] = UdtTestDb.dbo.Circle" + Environment.NewLine + + "FieldType[1] = Circle" + Environment.NewLine + + " 1, Center = 1,2" + Environment.NewLine + + " 2, Center = 3,4" + Environment.NewLine + + " 3, Center = 11,23" + Environment.NewLine + + " 4, Center = 444,555" + Environment.NewLine + + " 5, Center = 1,2" + Environment.NewLine + + " 6, Center = 3,4" + Environment.NewLine + + " 7, Center = 11,23" + Environment.NewLine + + " 8, Center = 444,245" + Environment.NewLine; + + DataTestUtility.AssertEqualsWithDescription( + expectedReaderValues, UdtTestHelpers.DumpReaderString(reader), + "Unexpected reader values."); + } + } + } + + [CheckConnStrSetupFact] + public void TestSchemaTable() + { + using (SqlConnection conn = new SqlConnection(_connStr)) + using (SqlCommand cmd = new SqlCommand("select * from lines", conn)) + { + conn.Open(); + cmd.CommandType = CommandType.Text; + + using (SqlDataReader reader = cmd.ExecuteReader()) + { + DataTable t = reader.GetSchemaTable(); + + string expectedSchemaTableValues = + "ids, 0, 4, 10, 255, False, , , , ids, , , System.Int32, True, 8, , , False, False, False, , False, False, System.Data.SqlTypes.SqlInt32, int, , , , , 8, False, " + Environment.NewLine + + "pos, 1, 20, 255, 255, False, , , , pos, , , Line, True, 29, , , False, False, False, , False, False, Line, UdtTestDb.dbo.Line, , , , Line, Shapes, Version=1.2.0.0, Culture=neutral, PublicKeyToken=a3e3aa32e6a16344, 29, False, " + Environment.NewLine; + + StringBuilder builder = new StringBuilder(); + foreach (DataRow row in t.Rows) + { + foreach (DataColumn col in t.Columns) + builder.Append(row[col] + ", "); + + builder.AppendLine(); + } + DataTestUtility.AssertEqualsWithDescription( + expectedSchemaTableValues, builder.ToString(), + "Unexpected DataTable values from GetSchemaTable."); + + string expectedReaderValues = + "ids1" + Environment.NewLine + + "pos1,2,3,4" + Environment.NewLine + + "ids2" + Environment.NewLine + + "pos3,4,5,6" + Environment.NewLine + + "ids3" + Environment.NewLine + + "pos11,23,15,32" + Environment.NewLine + + "ids4" + Environment.NewLine + + "pos444,555,245,634" + Environment.NewLine + + "ids5" + Environment.NewLine + + "pos1,2,3,4" + Environment.NewLine + + "ids6" + Environment.NewLine + + "pos3,4,5,6" + Environment.NewLine + + "ids7" + Environment.NewLine + + "pos11,23,15,32" + Environment.NewLine + + "ids8" + Environment.NewLine + + "pos444,555,245,634" + Environment.NewLine; + builder = new StringBuilder(); + while (reader.Read()) + { + for (int i = 0; i < reader.FieldCount; i++) + { + builder.Append(reader.GetName(i) + reader.GetValue(i).ToString()); + builder.AppendLine(); + } + } + DataTestUtility.AssertEqualsWithDescription( + expectedReaderValues, builder.ToString(), + "Unexpected Reader values."); + } + } + } + + [CheckConnStrSetupFact] + public void TestSqlUserDefinedAggregateAttributeMaxByteSize() + { + Func create + = (size) => new SqlUserDefinedAggregateAttribute(Format.UserDefined) { MaxByteSize = size }; + + SqlUserDefinedAggregateAttribute attribute1 = create(-1); + SqlUserDefinedAggregateAttribute attribute2 = create(0); + SqlUserDefinedAggregateAttribute attribute3 = create(SqlUserDefinedAggregateAttribute.MaxByteSizeValue); + + string udtError = SystemDataResourceManager.Instance.SQLUDT_MaxByteSizeValue; + string errorMessage = (new ArgumentOutOfRangeException("MaxByteSize", 8001, udtError)).Message; + + DataTestUtility.AssertThrowsWrapper( + () => create(SqlUserDefinedAggregateAttribute.MaxByteSizeValue + 1), + errorMessage); + + errorMessage = (new ArgumentOutOfRangeException("MaxByteSize", -2, udtError)).Message; + DataTestUtility.AssertThrowsWrapper( + () => create(-2), + errorMessage); + } + } +} + diff --git a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTestHelpers.cs b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTestHelpers.cs new file mode 100644 index 0000000000..160a2c40c6 --- /dev/null +++ b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTestHelpers.cs @@ -0,0 +1,83 @@ +// 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.Collections; +using System.IO; +using System.Reflection; +using System.Text; + +namespace System.Data.SqlClient.ManualTesting.Tests +{ + internal static class UdtTestHelpers + { + internal static string DumpReaderString(SqlDataReader reader) + { + return DumpReaderString(reader, true); + } + + internal static string DumpReaderString(SqlDataReader reader, bool showMetaData) + { + int i; + int x = 0, y = 0; + double d; + object o = 0; + bool fNull; + StringBuilder builder = new StringBuilder(); + + if (showMetaData) + { + for (i = 0; i < reader.FieldCount; i++) + { + builder.AppendLine($"ColumnName[{i}] = {reader.GetName(i)}"); + builder.AppendLine($"DataType[{i}] = {reader.GetDataTypeName(i)}"); + builder.AppendLine($"FieldType[{i}] = {reader.GetFieldType(i)}"); + } + } + + while (reader.Read()) + { + for (i = 0; i < reader.FieldCount; i++) + { + if (i > 0) + { + builder.Append(", "); + } + + object fieldValue = reader.GetValue(i); + + if (fieldValue is Point) + { + fNull = (bool)fieldValue.GetType().InvokeMember("IsNull", BindingFlags.GetProperty, null, fieldValue, null); + if (!fNull) + { + x = (int)fieldValue.GetType().InvokeMember("X", BindingFlags.GetProperty, null, fieldValue, null); + y = (int)fieldValue.GetType().InvokeMember("X", BindingFlags.GetProperty, null, fieldValue, null); + d = (double)fieldValue.GetType().InvokeMember("Distance", BindingFlags.Public | BindingFlags.Default | BindingFlags.Instance | BindingFlags.InvokeMethod, null, fieldValue, new Object[] { }); + builder.Append(string.Format("p.X = {0,3}, p.Y = {1,3}, p.Distance() = {2}", x, y, d)); + } + else + { + builder.Append("null"); + } + } + else if (fieldValue is Circle) + { + fNull = (bool)fieldValue.GetType().InvokeMember("IsNull", BindingFlags.GetProperty, null, fieldValue, null); + if (!fNull) + { + o = fieldValue.GetType().InvokeMember("Center", BindingFlags.Public | BindingFlags.Default | BindingFlags.Instance | BindingFlags.GetProperty, null, fieldValue, new Object[] { }); + builder.Append("Center = " + o); + } + } + else + { + builder.Append(string.Format("{0,10}", fieldValue.ToString())); + } + } + builder.AppendLine(); + } + return builder.ToString(); + } + } +} diff --git a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/System.Data.SqlClient.ManualTesting.Tests.csproj b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/System.Data.SqlClient.ManualTesting.Tests.csproj index 7a0e541915..7daecb14b6 100644 --- a/external/corefx/src/System.Data.SqlClient/tests/ManualTests/System.Data.SqlClient.ManualTesting.Tests.csproj +++ b/external/corefx/src/System.Data.SqlClient/tests/ManualTests/System.Data.SqlClient.ManualTesting.Tests.csproj @@ -1,4 +1,4 @@ - + @@ -12,6 +12,7 @@ + @@ -111,7 +112,11 @@ + + + + @@ -134,10 +139,22 @@ - - + + + Address + + + Circle + + + Shapes + + + Utf8String + + \ No newline at end of file diff --git a/external/corefx/src/System.Diagnostics.Contracts/ref/System.Diagnostics.Contracts.cs b/external/corefx/src/System.Diagnostics.Contracts/ref/System.Diagnostics.Contracts.cs index 8424f18044..a10fc953ea 100644 --- a/external/corefx/src/System.Diagnostics.Contracts/ref/System.Diagnostics.Contracts.cs +++ b/external/corefx/src/System.Diagnostics.Contracts/ref/System.Diagnostics.Contracts.cs @@ -87,9 +87,7 @@ namespace System.Diagnostics.Contracts public string Message { get { throw null; } } public System.Exception OriginalException { get { throw null; } } public bool Unwind { get { throw null; } } - [System.Security.SecurityCriticalAttribute] public void SetHandled() { } - [System.Security.SecurityCriticalAttribute] public void SetUnwind() { } } public enum ContractFailureKind diff --git a/external/corefx/src/System.Diagnostics.Debug/src/System.Diagnostics.Debug.csproj b/external/corefx/src/System.Diagnostics.Debug/src/System.Diagnostics.Debug.csproj index db84cd0988..d01cf21682 100644 --- a/external/corefx/src/System.Diagnostics.Debug/src/System.Diagnostics.Debug.csproj +++ b/external/corefx/src/System.Diagnostics.Debug/src/System.Diagnostics.Debug.csproj @@ -17,9 +17,5 @@ - - - - \ No newline at end of file diff --git a/external/corefx/src/System.Diagnostics.Debug/tests/DebuggerBrowsableAttributeTests.cs b/external/corefx/src/System.Diagnostics.Debug/tests/DebuggerBrowsableAttributeTests.cs index a0f5aa9603..7bc118c87e 100644 --- a/external/corefx/src/System.Diagnostics.Debug/tests/DebuggerBrowsableAttributeTests.cs +++ b/external/corefx/src/System.Diagnostics.Debug/tests/DebuggerBrowsableAttributeTests.cs @@ -1,4 +1,5 @@ -// The .NET Foundation licenses this file to you under the MIT license. +// 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 Xunit; diff --git a/external/corefx/src/System.Diagnostics.Debug/tests/DebuggerDisplayAttributeTests.cs b/external/corefx/src/System.Diagnostics.Debug/tests/DebuggerDisplayAttributeTests.cs index 57a8595859..98007a2821 100644 --- a/external/corefx/src/System.Diagnostics.Debug/tests/DebuggerDisplayAttributeTests.cs +++ b/external/corefx/src/System.Diagnostics.Debug/tests/DebuggerDisplayAttributeTests.cs @@ -1,4 +1,5 @@ -// The .NET Foundation licenses this file to you under the MIT license. +// 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 Xunit; diff --git a/external/corefx/src/System.Diagnostics.Debug/tests/DebuggerTypeProxyAttributeTests.cs b/external/corefx/src/System.Diagnostics.Debug/tests/DebuggerTypeProxyAttributeTests.cs index f59fd6e896..a094703fcf 100644 --- a/external/corefx/src/System.Diagnostics.Debug/tests/DebuggerTypeProxyAttributeTests.cs +++ b/external/corefx/src/System.Diagnostics.Debug/tests/DebuggerTypeProxyAttributeTests.cs @@ -1,4 +1,5 @@ -// The .NET Foundation licenses this file to you under the MIT license. +// 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 Xunit; diff --git a/external/corefx/src/System.Diagnostics.Debug/tests/System.Diagnostics.Debug.Tests.csproj b/external/corefx/src/System.Diagnostics.Debug/tests/System.Diagnostics.Debug.Tests.csproj index d536adf3a1..7198e6788f 100644 --- a/external/corefx/src/System.Diagnostics.Debug/tests/System.Diagnostics.Debug.Tests.csproj +++ b/external/corefx/src/System.Diagnostics.Debug/tests/System.Diagnostics.Debug.Tests.csproj @@ -8,7 +8,6 @@ true None - diff --git a/external/corefx/src/System.Diagnostics.DiagnosticSource/src/DiagnosticSourceUsersGuide.md b/external/corefx/src/System.Diagnostics.DiagnosticSource/src/DiagnosticSourceUsersGuide.md index 75d2d83c05..ba94eeffc0 100644 --- a/external/corefx/src/System.Diagnostics.DiagnosticSource/src/DiagnosticSourceUsersGuide.md +++ b/external/corefx/src/System.Diagnostics.DiagnosticSource/src/DiagnosticSourceUsersGuide.md @@ -1,118 +1,120 @@ # DiagnosticSource User's Guide -This document describes DiagnosticSource, a simple module that allows code -to be instrumented for production-time logging of **rich data payloads** for -consumption **within the process** that was instrumented. At runtime consumers -can **dynamically discover** data sources and subscribe to the ones of interest. +This document describes `DiagnosticSource`, a simple module that allows code +to be instrumented for production-time logging of **rich data payloads** for +consumption **within the process** that was instrumented. At runtime consumers +can **dynamically discover** data sources and subscribe to the ones of interest. -In addition to background on how the class works, this document also covers -[naming conventions](#naming-conventions) and [best practices](#best-practices) when -instrumenting code. +In addition to background on how the class works, this document also covers +[naming conventions](#naming-conventions) and [best practices](#best-practices) when +instrumenting code. ------------------------------------------- -## Relationship to Other Logging Facilities +## Relationship to Other Logging Facilities -In addition to DiagnosticSource, there are two other logging systems provided by Microsoft: +In addition to `DiagnosticSource`, there are two other logging systems provided by Microsoft: - 1. EventSource [docs](https://msdn.microsoft.com/en-us/library/system.diagnostics.tracing.eventsource(v=vs.110).aspx) - and [src](https://github.com/dotnet/corefx/blob/master/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs). - EventSource has been available since V4.5 of the .NET Runtime and is what is used - to instrument the runtime itself. It is designed to be fast and to be strongly - typed (payloads are typed, named properties), and to interface with OS logging - infrastructure like Event Tracing for Windows [(ETW)](https://msdn.microsoft.com/en-us/library/windows/desktop/aa363668(v=vs.85).aspx) - or [LTTng](http://lttng.org/) on Linux. +1. `EventSource` [docs](https://msdn.microsoft.com/en-us/library/system.diagnostics.tracing.eventsource(v=vs.110).aspx) + and [src](https://github.com/dotnet/corefx/blob/master/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs). + `EventSource` has been available since V4.5 of the .NET Runtime and is what is used + to instrument the runtime itself. It is designed to be fast and to be strongly + typed (payloads are typed, named properties), and to interface with OS logging + infrastructure like Event Tracing for Windows [(ETW)](https://msdn.microsoft.com/en-us/library/windows/desktop/aa363668(v=vs.85).aspx) + or [LTTng](http://lttng.org/) on Linux. - 2. ILogger [src](https://github.com/aspnet/logging). A number of popular 3rd party - formatted string logging systems for .NET have been built including NLog SeriLog - and Log4Net. The ILogger Nuget package is designed to 'wrap' any of these and - hide from the instrumentation code which exact logging system is being used. Using - this wrapper makes the most sense if your goal is to 'plug into' a logging pipeline - that assumes one of these logging systems. - -DiagnosticSource has more architectural similarity to EventSource. The main difference -is that EventSource assumes that the data being logged will **leave the process** and thus -requires that **only serializable data** be logged. However, DiagnosticSource was designed -to allow in-process tools to get at very rich data. Because the consumer is assumed to be -within the same process, non-serializable types (e.g. HttpResponseMessage or HttpContext) -can be passed, which gives the consumer a lot of potential data to work with. +2. `ILogger` [src](https://github.com/aspnet/logging). A number of popular third party + formatted string logging systems for .NET have been built including NLog, SeriLog, + and Log4Net. The `ILogger` NuGet package is designed to 'wrap' any of these and + hide from the instrumentation code which exact logging system is being used. Using + this wrapper makes the most sense if your goal is to 'plug into' a logging pipeline + that assumes one of these logging systems. + +`DiagnosticSource` has more architectural similarity to `EventSource`. The main difference +is that `EventSource` assumes that the data being logged will **leave the process** and thus +requires that **only serializable data** be logged. However, `DiagnosticSource` was designed +to allow in-process tools to get at very rich data. Because the consumer is assumed to be +within the same process, non-serializable types (e.g. `HttpResponseMessage` or `HttpContext`) +can be passed, which gives the consumer a lot of potential data to work with. As explained in [DiagnosticSourceEventSource.cs](System/Diagnostics/DiagnosticSourceEventSource.cs), -there is a bridge that pipes information from DiagnosticSource's to an EventSource. Thus EventSource -consumers can get at all DiagnosticSource events. While the data payloads from -DiagnosticSource can't in general be passed through to the EventSource (because they are +there is a bridge that pipes information from `DiagnosticSources` to an `EventSource`. Thus `EventSource` +consumers can get at all `DiagnosticSource` events. While the data payloads from +`DiagnosticSource` can't in general be passed through to the `EventSource` (because they are not serializable), there is a mechanism in the bridge that enables consumers to specify which fields -to pass along to the EventSource. +to pass along to the `EventSource`. What this means is that in general it is not necessary to instrument a code site multiple -times. By instrumenting with Diagnostic source, both clients that need the rich data -(and thus use DiagnosticListener) as well as any consumers using EventListeners -(or OS facilities like ETW) can get at the data. +times. By instrumenting with Diagnostic source, both clients that need the rich data +(and thus use `DiagnosticListener`) as well as any consumers using `EventListeners` +(or OS facilities like ETW) can get at the data. ---------------------------------------- ## Instrumenting with DiagnosticSource/DiagnosticListener -Perhaps surprisingly, the heart of DiagnosticSource logging architecture is not the -DiagnosticSource class but rather the DiagnosticListener class which 'receives' the -events. This is because the DiagnosticSource type is just an abstract base class -that defines the methods needed to actually log events. It is the DiagnosticListener -which holds all the actual implementation. +Perhaps surprisingly, the heart of the `DiagnosticSource` logging architecture is not the +`DiagnosticSource` class but rather the `DiagnosticListener` class which 'receives' the +events. This is because the `DiagnosticSource` type is just an abstract base class +that defines the methods needed to actually log events. It is the `DiagnosticListener` +which holds the actual implementation. -Thus the first step in instrumenting code with DiagnosticSource is to create a -DiagnosticListener. For example +Thus the first step in instrumenting code with `DiagnosticSource` is to create a +`DiagnosticListener`. For example: ```C# private static DiagnosticSource httpLogger = new DiagnosticListener("System.Net.Http"); ``` -Notice that httpLogger is typed as a DiagnosticSource. This is because this code -only cares about writing events and thus only cares about the DiagnosticSource methods that -the DiagnosticListener implements. DiagnosticListeners are given names when they are created +Notice that httpLogger is typed as a `DiagnosticSource`. This is because this code +only cares about writing events and thus only cares about the `DiagnosticSource` methods that +the `DiagnosticListener` implements. `DiagnosticListeners` are given names when they are created and this name should be the name of logical grouping of related events (typically the component). -Later this name is used to find the Listener and subscribe to any of its events. +Later this name is used to find the Listener and subscribe to any of its events. -Once you have an instance of a DiagnosticSource, logging is very straightforward. The -interface consists of only two methods. +Once you have an instance of a `DiagnosticSource`, logging is very straightforward. The +interface consists of only two methods: ```C# - bool IsEnabled(string name) - void Write(string name, object value); + bool IsEnabled(string name) + void Write(string name, object value); ``` -A typical call site will look like +A typical call site will look like: ```C# - if (httpLogger.IsEnabled("RequestStart")) - httpLogger.Write("RequestStart", new { Url="http://clr", Request=aRequest }); + if (httpLogger.IsEnabled("RequestStart")) + httpLogger.Write("RequestStart", new { Url="http://clr", Request=aRequest }); ``` -Already some of the architectural elements are being exposed, namely +Already some of the architectural elements are being exposed, namely: -1. Every event has a string name (e.g. Request Start), and exactly one object as a payload. -2. If you need to send more than one item, you can do so by creating a object with all information - in it as properties. C#'s [anonymous type](https://msdn.microsoft.com/en-us/library/bb397696.aspx) - feature is typically used to create a type to pass 'on the fly', and makes this scheme very - convenient. -3. At the instrumentation site, you must guard the call to 'Write' with an 'IsEnabled' check on - the same event name. Without this check, even when the instrumentation is inactive, the rules - of the C# language require all the work of creating the payload object and calling 'Write' to be - done, even though nothing is actually listening for the data. By guarding the 'Write' call, we - make it efficient when the source is not enabled. +1. Every event has a `string` name (e.g. `RequestStart`), and exactly one `object` as a payload. + +2. If you need to send more than one item, you can do so by creating an `object` with all information + in it as properties. C#'s [anonymous type](https://msdn.microsoft.com/en-us/library/bb397696.aspx) + feature is typically used to create a type to pass 'on the fly', and makes this scheme very + convenient. + +3. At the instrumentation site, you must guard the call to `Write()` with an `IsEnabled()` check on + the same event name. Without this check, even when the instrumentation is inactive, the rules + of the C# language require all the work of creating the payload `object` and calling `Write()` to be + done, even though nothing is actually listening for the data. By guarding the `Write()` call, we + make it efficient when the source is not enabled. ### Creating DiagnosticSources (Actually DiagnosticListeners) -Perhaps confusingly you make a DiagnosticSource by creating a DiagnosticListener +Perhaps confusingly you make a `DiagnosticSource` by creating a `DiagnosticListener`: ```C# - static DiagnosticSource mySource = new DiagnosticListener("System.Net.Http"); + static DiagnosticSource mySource = new DiagnosticListener("System.Net.Http"); ``` -Basically a DiagnosticListener is a named place where a source sends its information (events). -From an implementation point of view, DiagnosticSource is an abstract class that has the two -instrumentation methods, and DiagnosticListener is something that implements that abstract class. -Thus every DiagnosticListener is a DiagnosticSource, and by making a DiagnosticListener you -implicitly make a DiagnosticSource as well. +Basically a `DiagnosticListener` is a named place where a source sends its information (events). +From an implementation point of view, `DiagnosticSource` is an abstract class that has the two +instrumentation methods, and `DiagnosticListener` is something that implements that abstract class. +Thus every `DiagnosticListener` is a `DiagnosticSource`, and by making a `DiagnosticListener` you +implicitly make a `DiagnosticSource` as well. -DiagnosticListeners have a name, which is used to represent the component associated with the event. -Thus the event names only need to be unique within a component. +`DiagnosticListeners` have a name, which is used to represent the component associated with the event. +Thus the event names only need to be unique within a component. ---------------------------------------- ## Best Practices @@ -121,204 +123,207 @@ Thus the event names only need to be unique within a component. #### DiagnosticListener Names - * CONSIDER - the likely scenarios for USING information when deciding how many - DiagnosticListener to have and the events in each. Keep in mind that it is **very easy - and efficient** to filter all the events in a particular listener so ideally the - most important scenarios involve turning on whole listeners and not needing to filter - for particular events. You may need to split - a source into multiple smaller ones to achieve this, and this is OK. For example there - are both incoming HTTP requests and outgoing HTTP requests and you may only need one - or the other, so having a System.Net.Http.Incomming and System.Net.Http.OutGoing for - each sub-case is good. +* CONSIDER - the likely scenarios for USING information when deciding how many + `DiagnosticListener` to have and the events in each. Keep in mind that it is **very easy + and efficient** to filter all the events in a particular listener so ideally the + most important scenarios involve turning on whole listeners and not needing to filter + for particular events. You may need to split + a source into multiple smaller ones to achieve this, and this is OK. For example there + are both incoming HTTP requests and outgoing HTTP requests and you may only need one + or the other, so having a System.Net.Http.Incoming and System.Net.Http.Outgoing for + each sub-case is good. - * CONSIDER - the likely volume of events. High volume events may deserve their own - DiagnosticListener. You don't really want to mix high volume and low volume events - in the same listener unless they both support the same scenario. It is OK however to - put several **low volume** events in a 'miscellaneous' listener, even if they support different - scenarios if it simplifies things enough. +* CONSIDER - the likely volume of events. High volume events may deserve their own + `DiagnosticListener`. You don't really want to mix high volume and low volume events + in the same listener unless they both support the same scenario. It is OK however to + put several **low volume** events in a 'miscellaneous' listener, even if they support different + scenarios if it simplifies things enough. - * DO - Consider the scenario when picking the name for the DiagnosticListener. Often, this - name is the component in which the DiagnosticListener lives, but **usage scenarios trump - component naming**. You want it to be the case that users can correctly guess which - listeners to activate knowing just their scenario. - - * DO - Make the name for the DiagnosticListeners **globally unique**. This is typically - done by making the first part of the name the component (e.g. System.Net.Http) +* DO - Consider the scenario when picking the name for the `DiagnosticListener`. Often, this + name is the component in which the `DiagnosticListener` lives, but **usage scenarios trump + component naming**. You want it to be the case that users can correctly guess which + listeners to activate knowing just their scenario. - * DO - Use dots '.' to create multi-part names. This works well if the name is a Name - of a component (which uses dots). - - * DO NOT - name the listener after the Listener (thus something like System.Net.HttpDiagnosticListener - is bad). +* DO - Make the name for the `DiagnosticListeners` **globally unique**. This is typically + done by making the first part of the name the component (e.g. System.Net.Http) -#### EventNames +* DO - Use dots '.' to create multi-part names. This works well if the name is a Name + of a component (which uses dots). - * DO - keep the names reasonably short (< 16 characters). Keep in mind that event names - are already qualified by the Listener so the name only needs to be unique within a listener. - Short names make the 'IsEnabled' faster. +* DO NOT - name the listener after the Listener (thus something like System.Net.HttpDiagnosticListener + is bad). - * DO - use the 'Start' and 'Stop' suffixes for events that define an interval of time. For example - naming one event 'RequestStart' and the another 'RequestStop' is good because tools can use the - convention to determine that the time interval betweeen them is interesting. +#### Event Names + +* DO - keep the names reasonably short (< 16 characters). Keep in mind that event names + are already qualified by the Listener so the name only needs to be unique within a listener. + Short names make `IsEnabled()` faster. + +* DO - use the 'Start' and 'Stop' suffixes for events that define an interval of time. For example + naming one event 'RequestStart' and the another 'RequestStop' is good because tools can use the + convention to determine that the time interval betweeen them is interesting. ### Payloads - * DO use the anonymous type syntax 'new { property1 =value1 ...}' as the default way to pass - a payload *even if there is only one data element*. This makes adding more data later easy - and compatible. +* DO use the anonymous type syntax 'new { property1 = value1 ...}' as the default way to pass + a payload *even if there is only one data element*. This makes adding more data later easy + and compatible. - * CONSIDER - if you have an event that is so frequent that the performance of the logging is - a important consideration, **and** you have only one data item **and** it is unlikely that - you will ever have more data to pass to the event, **and** the data item is a normal class - (not a value type) **then** you save some cost by simply by passing the data object directly - without using an anonymous type wrapper. +* CONSIDER - if you have an event that is so frequent that the performance of the logging is + an important consideration, **and** you have only one data item **and** it is unlikely that + you will ever have more data to pass to the event, **and** the data item is a normal class + (not a value type) **then** you save some cost by simply by passing the data `object` directly + without using an anonymous type wrapper. + +* DO - use standard names for particular payload items. (TODO: Put the list here as we define standard payload names). - * DO - use standard names for particular payload items. (TODO: Put the list here as we define standard payload names). - ### Filtering - - * DO - always enclose the Write() call in a call to 'IsEnabled' for the same event name. Otherwise - a lot of setup logic will be called even if there is nothing listening for the event. - * CONSIDER - enclosing IsEnabled(string, object, object) calls with pure IsEnabled(string) call to avoid - extra cost of creating context in case consumer is not interested in such events at all. - - * CONSIDER - passing public named types instances to 'IsEnabled' overloads with object parameters - to keep IsEnabled as efficient as possible. +* DO - always enclose the `Write()` call in a call to `IsEnabled()` for the same event name. Otherwise + a lot of setup logic will be called even if there is nothing listening for the event. - * DO - when subscribing to DiagnosticSource with advanced filter for event name and extended context, - make sure filter returns true for null context properties if consumer is interested - in at least some events with context +* CONSIDER - enclosing `IsEnabled(string, object, object)` calls with pure `IsEnabled(string)` calls to avoid + the extra cost of creating a context in case a consumer is not interested in such events at all. -### Other Conventions +* CONSIDER - passing public named types instances to `IsEnabled()` overloads with `object` parameters + to keep `IsEnabled()` as efficient as possible. - * DO NOT - make the DiagnosticListener public. There is no need to as subscribers will - use the AllListener property to hook up. +* DO - when subscribing to `DiagnosticSource` with an advanced filter for event name and extended context, + make sure the filter returns `true` for `null` context properties if the consumer is interested + in at least some events with context. + +### Other Conventions + +* DO NOT - make the `DiagnosticListener` public. There is no need to as subscribers will + use the `AllListener` property to hook up. ---------------------------------------- -## Consuming Data with DiagnosticListener. +## Consuming Data with DiagnosticListener Up until now, this guide has focused on how to instrument code to generate logging -information. In this section we focus on subscribing and decoding of that information. +information. In this section we focus on subscribing and decoding of that information. -### Discovery of DiagnosticListeners. +### Discovery of DiagnosticListeners -The first step in receiving events is to discover which DiagnosticListeners you are -interested in. While it is possible to discover DiagnosticListeners at compile time -by referencing static variables (e.g. like the httpLogger in the previous example), this -is typically not flexible enough. +The first step in receiving events is to discover which `DiagnosticListeners` you are +interested in. While it is possible to discover `DiagnosticListeners` at compile time +by referencing static variables (e.g. like the `httpLogger` in the previous example), this +is typically not flexible enough. -Instead DiagnosticListener supports a way of discovering DiagnosticListener that is -active in the system at runtime. The API to accomplish this is the 'AllListeners' -IObservable\. +Instead `DiagnosticListener` supports a way of discovering `DiagnosticListeners` that are +active in the system at runtime. The API to accomplish this is the `AllListeners` +`IObservable`. -The IObservable interface is the 'callback' version of the IEnumerable interface. You can learn +The `IObservable` interface is the 'callback' version of the `IEnumerable` interface. You can learn more about it at the [Reactive Extensions](https://msdn.microsoft.com/en-us/data/gg577609.aspx) site. -In a nutshell, you have an object called an IObserver which has three callbacks, OnNext, OnComplete -and OnError, and an IObservable has single method called 'Subscribe' which gets passed one of these -Observers. Once connected, the Observer gets callback (mostly OnNext callbacks) when things -happen. By including the System.Reactive.Core Nuget package, you can get a bunch of useful -Extensions that make using IObservable nice. +In a nutshell, you have an object called an `IObserver` which has three callbacks, `OnNext`, `OnComplete` +and `OnError`, and an `IObservable` has single method called `Subscribe` which gets passed one of these +Observers. Once connected, the Observer gets callbacks (mostly `OnNext` callbacks) when things +happen. By including the `System.Reactive.Core` NuGet package, you can get a bunch of useful +extensions that make using `IObservable` nice. -A typical use of the AllListeners static property looks like this: +A typical use of the `AllListeners` static property looks like this: ```C# - // We are using to turn a Action into a IObserver - static IDisposable listenerSubscription = DiagnosticListener.AllListeners.Subscribe(delegate (DiagnosticListener listener) - { - // We get a callback of every Diagnostics Listener that is active in the system (past present or future) - if (listener.Name == "System.Net.Http") - { - // Here is where we put code to subscribe to the Listener. - } - }); - - // Typically you leave the listenerSubscription subscription active forever. - // However when you no longer want your callback to be called, you can - // call listenerSubscription.Dispose() to cancel your subscription to the IObservable. + // We are using AllListeners to turn an Action into an IObserver + static IDisposable listenerSubscription = DiagnosticListener.AllListeners.Subscribe(delegate (DiagnosticListener listener) + { + // We get a callback of every Diagnostics Listener that is active in the system (past present or future) + if (listener.Name == "System.Net.Http") + { + // Here is where we put code to subscribe to the Listener. + } + }); + + // Typically you leave the listenerSubscription subscription active forever. + // However when you no longer want your callback to be called, you can + // call listenerSubscription.Dispose() to cancel your subscription to the IObservable. ``` -This code basically creates a callback delegate and using the 'AllListeners.Subscribe' method requests -that that delegate be called for every active DiagnosticListener in the system. Typically you inspect -the name of the listener and based on that, decide whether to subscribe to listener or not. The +This code basically creates a callback delegate and using the `AllListeners.Subscribe` method requests +that that delegate be called for every active `DiagnosticListener` in the system. Typically you inspect +the name of the listener and based on that, decide whether to subscribe to the listener or not. The code above is looking for our 'System.Net.Http' listener that we created previously. -Like all calls to Subscribe, this one returns a IDisposable that represents the subscription itself. -Callbacks will continue to happen as long as nothing calls Dispose() on this subscription object. -The above code never calls it, so it will receive callbacks forever. +Like all calls to `Subscribe()`, this one returns an `IDisposable` that represents the subscription itself. +Callbacks will continue to happen as long as nothing calls `Dispose()` on this subscription object. +The above code never calls it, so it will receive callbacks forever. -It is important to note that when you Subscribe to AllListeners, you get a callback for ALL ACTIVE DiagnosticListeners. -Thus upon subscribing you get a flurry of callbacks of all existing DiagnosticListeners but as new ones -are created, you get a callback for those as well. Thus you get a complete list of everything it is possible -to subscribe to. +It is important to note that when you subscribe to `AllListeners`, you get a callback for ALL ACTIVE `DiagnosticListeners`. +Thus, upon subscribing, you get a flurry of callbacks for all existing `DiagnosticListeners`, but as new ones +are created, you get a callback for those as well. Thus you get a complete list of everything it is possible +to subscribe to. -Finally, note that the code above is taking advantage of convenience functionality in the System.Reactive.Core -library. The DiagnosticListener.AllListeners.Subscribe method actually requires that it be passed -an IObserver\, which is a class that has three callbacks (OnNext, OnError, OnComplete), -but we passed it an Action\. The magic that makes this work is an extension method -in System.Reactive.Core that takes the Action and from it makes an IObserver (called AnonymousObserver) -which calls the Action on its OnNext callback. This glue is what makes the code concise. +Finally, note that the code above is taking advantage of convenience functionality in the `System.Reactive.Core` +library. The `DiagnosticListener.AllListeners.Subscribe` method actually requires that it be passed +an `IObserver`, which is a class that has three callbacks (`OnNext`, `OnError`, `OnComplete`), +but we passed it an `Action`. The magic that makes this work is an extension method +in `System.Reactive.Core` that takes the `Action` and from it makes an `IObserver` (called `AnonymousObserver`) +which calls the `Action` in its `OnNext` callback. This glue is what makes the code concise. #### Subscribing to DiagnosticListeners -A DiagnosticListener implements the IObservable\> interface, so you can -call 'Subscribe' on it as well. Thus we can fill out the previous example a bit +A `DiagnosticListener` implements the `IObservable>` interface, so you can +call `Subscribe()` on it as well. Thus we can fill out the previous example a bit: ```C# - static IDisposable networkSubscription = null; - static IDisposable listenerSubscription = DiagnosticListener.AllListeners.Subscribe(delegate (DiagnosticListener listener) - { - if (listener.Name == "System.Net.Http") - { - lock(allListeners) - { - if (networkSubscription != null) - networkSubscription.Dispose(); + static IDisposable networkSubscription = null; + static IDisposable listenerSubscription = DiagnosticListener.AllListeners.Subscribe(delegate (DiagnosticListener listener) + { + if (listener.Name == "System.Net.Http") + { + lock(allListeners) + { + if (networkSubscription != null) + networkSubscription.Dispose(); - networkSubscription = listener.Subscribe((KeyValuePair evnt) => - Console.WriteLine("From Listener {0} Received Event {1} with payload {2}", - networkListener.Name, evnt.Key, evnt.Value.ToString())); - } - } - }); + networkSubscription = listener.Subscribe((KeyValuePair evnt) => + Console.WriteLine("From Listener {0} Received Event {1} with payload {2}", + networkListener.Name, evnt.Key, evnt.Value.ToString())); + } + } + }); - // At some point you may wish to dispose the networkSubscription. + // At some point you may wish to dispose the networkSubscription. ``` -In this example after finding the 'System.Net.Http' DiagnosticListener, we create an action that -prints out the name of the listener, event, and payload.ToString(). Notice a few things: +In this example, after finding the 'System.Net.Http' `DiagnosticListener`, we create an action that +prints out the name of the listener, event, and `payload.ToString()`. Notice a few things: - 1. DiagnosticListener implements IObservable\>. This means - on each callback we get a KeyValuePair. The key of this pair is the name of the event - and the value is the payload object. In the code above we simply log this information - to the Console. - 2. We keep track of our subscriptions to the DiagnosticListeners. In this case we have - a networkSubscription variable that remembers this, and we get another 'creation' we - unsubscribe the previous listener and subscribe to the new one. - 3. We use locks. The DiagnosticSource/DiagnosticListener code is thread safe, but the - callback code also needs to be threadsafe. It is possible that two DiagnosticListener - with the same name are created at the same time (although that is a bit unexpected), so - to avoid races we do updates of our shared variables under the protection of a lock. - -Once the above code is run, the next time a 'Write' is done on 'System.Net.Http' DiagnosticListener -the information will be logged to the Console. +1. `DiagnosticListener` implements `IObservable>`. This means + on each callback we get a `KeyValuePair`. The key of this pair is the name of the event + and the value is the payload `object`. In the code above we simply log this information + to the console. -It is also important to note that subscriptions are independent of one another. Thus other code +2. We keep track of our subscriptions to the `DiagnosticListener`. In this case we have + a networkSubscription variable that remembers this, and if we get another 'creation' we + unsubscribe the previous listener and subscribe to the new one. + +3. We use locks. The `DiagnosticSource`/`DiagnosticListener` code is thread safe, but the + callback code also needs to be thread safe. It is possible that two `DiagnosticListeners` + with the same name are created at the same time (although that is a bit unexpected), so + to avoid races we do updates of our shared variables under the protection of a lock. + +Once the above code is run, the next time a `Write()` is done on 'System.Net.Http' `DiagnosticListener` +the information will be logged to the console. + +It is also important to note that subscriptions are independent of one another. Thus other code can do exactly the same thing as the code above, and thus generate two 'pipes' of the logging -information. +information. #### Decoding Payloads -The KeyvaluePair that is passed to the callback has the event name and payload, but the payload is typed simply as -an object. Odds are that you want to get at more specific data. There are two ways of doing this - - 1. If the payload is a well known type (e.g. a string, or an HttpMessageRequest) then you can simply - cast the object to the expected type (using the 'as' operator so as not to cause an exception if - you are wrong) and then access the fields. This is very efficient. - 2. Use reflection API, for example if we assuming we have the method +The `KeyvaluePair` that is passed to the callback has the event name and payload, but the payload is typed simply as +an `object`. Odds are that you want to get at more specific data. There are two ways of doing this: + +1. If the payload is a well known type (e.g. a `string`, or an `HttpMessageRequest`) then you can simply + cast the `object` to the expected type (using the `as` operator so as not to cause an exception if + you are wrong) and then access the fields. This is very efficient. + +2. Use reflection API. For example, if we assume we have the method: ```C# - /// Define a shortcut method that fetches a field of a particular name. + /// Define a shortcut method that fetches a field of a particular name. static class PropertyExtensions { static object GetProperty(this object _this, string propertyName) @@ -328,69 +333,70 @@ an object. Odds are that you want to get at more specific data. There are tw } ``` -Then we could replace the listener.Subscribe call above with the following code, to decode the payload more fully. +Then we could replace the `listener.Subscribe()` call above with the following code, to decode the payload more fully: ```C# - networkSubscription = listener.Subscribe(delegate(KeyValuePair evnt) { - var eventName = evnt.Key; - var payload = evnt.Value; - if (eventName == "RequestStart") - { - var url = payload.GetProperty("Url") as string; - var request = payload.GetProperty("Request"); - Console.WriteLine("Got RequestStart with URL {0} and Request {1}", url, request); - } - }); -``` - -Note that using reflection is relatively expensive. However using reflection is your only -option if the payloads was generated using anonymous types. You can reduce this overhead by -making fast, specialized property fetchers either using PropertyInfo.CreateDelegate or -ReflectEmit, but that is beyond the scope of this document. (see the [PropertySpec](https://github.com/dotnet/corefx/blob/master/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs#L784) class used in the DiagnosticSourceEventSource for an example of a fast, delegate-based -property fetcher). - -#### Filtering - -In the example above the code uses the IObservable.Subscribe to hook up the callback, which -causes all events to be given to the callback. However DiagnosticListener has a overloads of -Subscribe that allow the controller to control which events get through. - -Thus we could replace the listener.Subscribe call in the previous example with the following -code - -```C# - // Create the callback delegate - Action> callback = (KeyValuePair evnt) => - Console.WriteLine("From Listener {0} Received Event {1} with payload {2}", networkListener.Name, evnt.Key, evnt.Value.ToString()); - - // Turn it into an observer (using System.Reactive.Core's AnonymousObserver) - Observer> observer = new AnonymousObserver>(callback); - - // Create a predicate (asks only for one kind of event) - Predicate predicate = (string eventName) => eventName == "RequestStart"; - - // Subscribe with a filter predicate - IDisposable subscription = listener.Subscribe(observer, predicate); - - // subscription.Dispose() to stop the callbacks. + networkSubscription = listener.Subscribe(delegate(KeyValuePair evnt) { + var eventName = evnt.Key; + var payload = evnt.Value; + if (eventName == "RequestStart") + { + var url = payload.GetProperty("Url") as string; + var request = payload.GetProperty("Request"); + Console.WriteLine("Got RequestStart with URL {0} and Request {1}", url, request); + } + }); ``` -Which very efficiently only subscribes to the 'RequestStart' events. All other events will cause the DiagnosticSource.IsEnabled() -method to return false, and thus be efficiently filtered out. + +Note that using reflection is relatively expensive. However, using reflection is your only +option if the payloads were generated using anonymous types. You can reduce this overhead by +making fast, specialized property fetchers either using `PropertyInfo.CreateDelegate` or +`ReflectEmit`, but that is beyond the scope of this document. +(See the [PropertySpec](https://github.com/dotnet/corefx/blob/master/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs#L784) +class used in the `DiagnosticSourceEventSource` for an example of a fast, delegate-based property fetcher.) + +#### Filtering + +In the example above the code uses the `IObservable.Subscribe()` method to hook up the callback, which +causes all events to be given to the callback. However `DiagnosticListener` has overloads of +`Subscribe()` that allow the controller to control which events get through. + +Thus we could replace the `listener.Subscribe()` call in the previous example with the following code: + +```C# + // Create the callback delegate + Action> callback = (KeyValuePair evnt) => + Console.WriteLine("From Listener {0} Received Event {1} with payload {2}", networkListener.Name, evnt.Key, evnt.Value.ToString()); + + // Turn it into an observer (using System.Reactive.Core's AnonymousObserver) + Observer> observer = new AnonymousObserver>(callback); + + // Create a predicate (asks only for one kind of event) + Predicate predicate = (string eventName) => eventName == "RequestStart"; + + // Subscribe with a filter predicate + IDisposable subscription = listener.Subscribe(observer, predicate); + + // subscription.Dispose() to stop the callbacks. +``` + +This very efficiently subscribes to only the 'RequestStart' events. All other events will cause the `DiagnosticSource.IsEnabled()` +method to return `false`, and thus be efficiently filtered out. ##### Context-based Filtering -Some scenarios require advanced filtering based on extended context. -Producers may call DiagnosticSource.IsEnabled() overloads and supply additional event properties. +Some scenarios require advanced filtering based on extended context. +Producers may call `DiagnosticSource.IsEnabled()` overloads and supply additional event properties: ```C# - if (httpLogger.IsEnabled("RequestStart", aRequest, anActivity)) - httpLogger.Write("RequestStart", new { Url="http://clr", Request=aRequest }); + if (httpLogger.IsEnabled("RequestStart", aRequest, anActivity)) + httpLogger.Write("RequestStart", new { Url="http://clr", Request=aRequest }); ``` -And consumers may use such properties to filter events more precisely. +And consumers may use such properties to filter events more precisely: ```C# // Create a predicate (asks only for Requests for certains URIs) - Func predicate = (string eventName, object context, object activity) => + Func predicate = (string eventName, object context, object activity) => { if (eventName == "RequestStart") { @@ -407,23 +413,23 @@ And consumers may use such properties to filter events more precisely. IDisposable subscription = listener.Subscribe(observer, predicate); ``` -Note that producer is not aware of filter consumer has provided. DiagnosticListener -will invoke provided filter omitting additional arguments if necessary, thus the filter -should expect to receive null context. -Producers should enclose IsEnabled call with event name and context with pure IsEnabled -call for event name, so consumers must ensure that filter allows events without context +Note that producers are not aware of the filter a consumer has provided. `DiagnosticListener` +will invoke the provided filter, omitting additional arguments if necessary, thus the filter +should expect to receive a `null` context. +Producers should enclose `IsEnabled()` calls with event name and context with pure `IsEnabled()` +calls for event name, so consumers must ensure that their filter allows events without context to pass through. ---------------------------------------- ## Consuming DiagnosticSource Data with EventListeners and ETW -The System.Diagnostic.DiagnosticSource Nuget package comes with a built in EventSource -called Microsoft-Diagnostics-DiagnosticSource. This EventSource has the ability to -subscribe to any DiagnosticListener as well as pluck off particular data items from -DiagnosticSource payloads. +The `System.Diagnostic.DiagnosticSource` NuGet package comes with a built in `EventSource` +called Microsoft-Diagnostics-DiagnosticSource. This `EventSource` has the ability to +subscribe to any `DiagnosticListener` as well as pluck off particular data items from +`DiagnosticSource` payloads. -Thus code that is using System.Diagnostics.Tracing.EventListener or ETW can get at -any information logged with DiagnosticSource. +Thus code that is using `System.Diagnostics.Tracing.EventListener` or ETW can get at +any information logged with `DiagnosticSource`. See [DiagnosticSourceEventSource](https://github.com/dotnet/corefx/blob/master/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs) -for more information on how to use it. +for more information on how to use it. diff --git a/external/corefx/src/System.Diagnostics.DiagnosticSource/src/HierarchicalRequestId.md b/external/corefx/src/System.Diagnostics.DiagnosticSource/src/HierarchicalRequestId.md index f878f84c95..c9fbe7a912 100644 --- a/external/corefx/src/System.Diagnostics.DiagnosticSource/src/HierarchicalRequestId.md +++ b/external/corefx/src/System.Diagnostics.DiagnosticSource/src/HierarchicalRequestId.md @@ -95,12 +95,12 @@ As a result log records may look like: | Message | Component name | Context | | ---------| --------------- | ------- | | user starts request to service-a | user | | -| incoming request | service-a | `Request-Id=|Guid.` | -| request to service-b | service-a | `Request-Id=|Guid.1.` | -| incoming request | service-b | `Request-Id=|Guid.1.da4e9679_` | -| response | service-b | `Request-Id=|Guid.1.da4e9679_` | -| response from service-b | service-a | `Request-Id=|Guid.1.` | -| response | service-a | `Request-Id=|Guid.` | +| incoming request | service-a | Request-Id=|Guid. | +| request to service-b | service-a | Request-Id=|Guid.1. | +| incoming request | service-b | Request-Id=|Guid.1.da4e9679_ | +| response | service-b | Request-Id=|Guid.1.da4e9679_ | +| response from service-b | service-a | Request-Id=|Guid.1. | +| response | service-a | Request-Id=|Guid. | | response from service-a | user | | ### Remarks diff --git a/external/corefx/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceActivity.cs b/external/corefx/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceActivity.cs index d0c1e1bb3b..a7dbe5f2ce 100644 --- a/external/corefx/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceActivity.cs +++ b/external/corefx/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceActivity.cs @@ -1,4 +1,8 @@ -namespace System.Diagnostics +// 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. + +namespace System.Diagnostics { public abstract partial class DiagnosticSource { diff --git a/external/corefx/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/HttpHandlerDiagnosticListener.cs b/external/corefx/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/HttpHandlerDiagnosticListener.cs index 43dd0b16a4..ec8feba0bc 100644 --- a/external/corefx/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/HttpHandlerDiagnosticListener.cs +++ b/external/corefx/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/HttpHandlerDiagnosticListener.cs @@ -539,6 +539,31 @@ namespace System.Diagnostics { s_instance.RaiseResponseEvent(request, response); } + else + { + // In case reponse content length is 0 and request is async, + // we won't have a HttpWebResponse set on request object when this method is called + // http://referencesource.microsoft.com/#System/net/System/Net/HttpWebResponse.cs,525 + + // But we there will be CoreResponseData object that is either exception + // or the internal HTTP reponse representation having status, content and headers + + var coreResponse = s_coreResponseAccessor(request); + if (coreResponse != null && s_coreResponseDataType.IsInstanceOfType(coreResponse)) + { + HttpStatusCode status = s_coreStatusCodeAccessor(coreResponse); + WebHeaderCollection headers = s_coreHeadersAccessor(coreResponse); + + // Manual creation of HttpWebResponse here is not possible as this method is eventually called from the + // HttpWebResponse ctor. So we will send Stop event with the Status and Headers payload + // to notify listeners about response; + // We use two different names for Stop events since one event with payload type that varies creates + // complications for efficient payload parsing and is not supported by DiagnosicSource helper + // libraries (e.g. Microsoft.Extensions.DiagnosticAdapter) + + s_instance.RaiseResponseEvent(request, status, headers); + } + } } base.RemoveAt(index); @@ -606,22 +631,33 @@ namespace System.Diagnostics // Response event could be received several times for the same request in case it was redirected // IsLastResponse checks if response is the last one (no more redirects will happen) // based on response StatusCode and number or redirects done so far - if (request.Headers[RequestIdHeaderName] != null && IsLastResponse(request, response)) + if (request.Headers[RequestIdHeaderName] != null && IsLastResponse(request, response.StatusCode)) { // only send Stop if request was instrumented this.Write(RequestStopName, new { Request = request, Response = response }); } } - private bool IsLastResponse(HttpWebRequest request, HttpWebResponse response) + private void RaiseResponseEvent(HttpWebRequest request, HttpStatusCode statusCode, WebHeaderCollection headers) + { + // Response event could be received several times for the same request in case it was redirected + // IsLastResponse checks if response is the last one (no more redirects will happen) + // based on response StatusCode and number or redirects done so far + if (request.Headers[RequestIdHeaderName] != null && IsLastResponse(request, statusCode)) + { + this.Write(RequestStopExName, new { Request = request, StatusCode = statusCode, Headers = headers }); + } + } + + private bool IsLastResponse(HttpWebRequest request, HttpStatusCode statusCode) { if (request.AllowAutoRedirect) { - if (response.StatusCode == HttpStatusCode.Ambiguous || // 300 - response.StatusCode == HttpStatusCode.Moved || // 301 - response.StatusCode == HttpStatusCode.Redirect || // 302 - response.StatusCode == HttpStatusCode.RedirectMethod || // 303 - response.StatusCode == HttpStatusCode.RedirectKeepVerb) // 307 + if (statusCode == HttpStatusCode.Ambiguous || // 300 + statusCode == HttpStatusCode.Moved || // 301 + statusCode == HttpStatusCode.Redirect || // 302 + statusCode == HttpStatusCode.RedirectMethod || // 303 + statusCode == HttpStatusCode.RedirectKeepVerb) // 307 { return s_autoRedirectsAccessor(request) >= request.MaximumAutomaticRedirections; } @@ -642,32 +678,16 @@ namespace System.Diagnostics s_connectionType = systemNetHttpAssembly?.GetType("System.Net.Connection"); s_writeListField = s_connectionType?.GetField("m_WriteList", BindingFlags.Instance | BindingFlags.NonPublic); - // Second step: Generate an accessor for HttpWebRequest._HttpResponse - FieldInfo responseField = typeof(HttpWebRequest).GetField("_HttpResponse", BindingFlags.NonPublic | BindingFlags.Instance); - if (responseField != null) - { - string methodName = responseField.ReflectedType.FullName + ".get_" + responseField.Name; - DynamicMethod getterMethod = new DynamicMethod(methodName, typeof(HttpWebResponse), new Type[] { typeof(HttpWebRequest) }, true); - ILGenerator generator = getterMethod.GetILGenerator(); - generator.Emit(OpCodes.Ldarg_0); - generator.Emit(OpCodes.Ldfld, responseField); - generator.Emit(OpCodes.Ret); - s_httpResponseAccessor = (Func)getterMethod.CreateDelegate(typeof(Func)); - } + s_httpResponseAccessor = CreateFieldGetter("_HttpResponse", BindingFlags.NonPublic | BindingFlags.Instance); + s_autoRedirectsAccessor = CreateFieldGetter("_AutoRedirects", BindingFlags.NonPublic | BindingFlags.Instance); + s_coreResponseAccessor = CreateFieldGetter("_CoreResponse", BindingFlags.NonPublic | BindingFlags.Instance); - // Third step: Generate an accessor for HttpWebRequest._AutoRedirects - FieldInfo redirectsField = typeof(HttpWebRequest).GetField("_AutoRedirects", BindingFlags.NonPublic | BindingFlags.Instance); - if (redirectsField != null) + s_coreResponseDataType = systemNetHttpAssembly?.GetType("System.Net.CoreResponseData"); + if (s_coreResponseDataType != null) { - string methodName = redirectsField.ReflectedType.FullName + ".get_" + redirectsField.Name; - DynamicMethod getterMethod = new DynamicMethod(methodName, typeof(int), new Type[] { typeof(HttpWebRequest) }, true); - ILGenerator generator = getterMethod.GetILGenerator(); - generator.Emit(OpCodes.Ldarg_0); - generator.Emit(OpCodes.Ldfld, redirectsField); - generator.Emit(OpCodes.Ret); - s_autoRedirectsAccessor = (Func)getterMethod.CreateDelegate(typeof(Func)); + s_coreStatusCodeAccessor = CreateFieldGetter(s_coreResponseDataType, "m_StatusCode", BindingFlags.Public | BindingFlags.Instance); + s_coreHeadersAccessor = CreateFieldGetter(s_coreResponseDataType, "m_ResponseHeaders", BindingFlags.Public | BindingFlags.Instance); } - // Double checking to make sure we have all the pieces initialized if (s_connectionGroupListField == null || s_connectionGroupType == null || @@ -675,7 +695,10 @@ namespace System.Diagnostics s_connectionType == null || s_writeListField == null || s_httpResponseAccessor == null || - s_autoRedirectsAccessor == null) + s_autoRedirectsAccessor == null || + s_coreResponseDataType == null || + s_coreStatusCodeAccessor == null || + s_coreHeadersAccessor == null) { // If anything went wrong here, just return false. There is nothing we can do. throw new InvalidOperationException("Unable to initialize all required reflection objects"); @@ -697,7 +720,48 @@ namespace System.Diagnostics servicePointTableField.SetValue(null, newTable); } -#endregion + private static Func CreateFieldGetter(string fieldName, BindingFlags flags) where TClass : class + { + FieldInfo field = typeof(TClass).GetField(fieldName, flags); + if (field != null) + { + string methodName = field.ReflectedType.FullName + ".get_" + field.Name; + DynamicMethod getterMethod = new DynamicMethod(methodName, typeof(TField), new [] { typeof(TClass) }, true); + ILGenerator generator = getterMethod.GetILGenerator(); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldfld, field); + generator.Emit(OpCodes.Ret); + return (Func)getterMethod.CreateDelegate(typeof(Func)); + } + + return null; + } + + + /// + /// Creates getter for a field defined in private or internal type + /// repesented with classType variable + /// + private static Func CreateFieldGetter(Type classType, string fieldName, BindingFlags flags) + { + FieldInfo field = classType.GetField(fieldName, flags); + if (field != null) + { + string methodName = classType.FullName + ".get_" + field.Name; + DynamicMethod getterMethod = new DynamicMethod(methodName, typeof(TField), new [] { typeof(object) }, true); + ILGenerator generator = getterMethod.GetILGenerator(); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Castclass, classType); + generator.Emit(OpCodes.Ldfld, field); + generator.Emit(OpCodes.Ret); + + return (Func)getterMethod.CreateDelegate(typeof(Func)); + } + + return null; + } + + #endregion internal static HttpHandlerDiagnosticListener s_instance = new HttpHandlerDiagnosticListener(); @@ -706,6 +770,7 @@ namespace System.Diagnostics private const string ActivityName = "System.Net.Http.Desktop.HttpRequestOut"; private const string RequestStartName = "System.Net.Http.Desktop.HttpRequestOut.Start"; private const string RequestStopName = "System.Net.Http.Desktop.HttpRequestOut.Stop"; + private const string RequestStopExName = "System.Net.Http.Desktop.HttpRequestOut.Ex.Stop"; private const string InitializationFailed = "System.Net.Http.InitializationFailed"; private const string RequestIdHeaderName = "Request-Id"; private const string CorrelationContextHeaderName = "Correlation-Context"; @@ -721,7 +786,11 @@ namespace System.Diagnostics private static FieldInfo s_writeListField; private static Func s_httpResponseAccessor; private static Func s_autoRedirectsAccessor; + private static Func s_coreResponseAccessor; + private static Func s_coreStatusCodeAccessor; + private static Func s_coreHeadersAccessor; + private static Type s_coreResponseDataType; -#endregion + #endregion } } diff --git a/external/corefx/src/System.Diagnostics.DiagnosticSource/tests/ActivityDateTimeTests.cs b/external/corefx/src/System.Diagnostics.DiagnosticSource/tests/ActivityDateTimeTests.cs index 368dbdf1ab..3b878073d6 100644 --- a/external/corefx/src/System.Diagnostics.DiagnosticSource/tests/ActivityDateTimeTests.cs +++ b/external/corefx/src/System.Diagnostics.DiagnosticSource/tests/ActivityDateTimeTests.cs @@ -1,3 +1,7 @@ +// 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.Threading; using Xunit; @@ -22,4 +26,4 @@ namespace System.Diagnostics.Tests Assert.True(activity.Duration.TotalMilliseconds > 1 && activity.Duration <= sw.Elapsed); } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs b/external/corefx/src/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs index 95a6a9c201..acf3ba61ae 100644 --- a/external/corefx/src/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs +++ b/external/corefx/src/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs @@ -1,4 +1,8 @@ -using System.Collections.Generic; +// 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.Collections.Generic; using System.Linq; using System.Text; using System.Threading; diff --git a/external/corefx/src/System.Diagnostics.DiagnosticSource/tests/HttpHandlerDiagnosticListenerTests.cs b/external/corefx/src/System.Diagnostics.DiagnosticSource/tests/HttpHandlerDiagnosticListenerTests.cs index f741ae3ab3..6b79dc05df 100644 --- a/external/corefx/src/System.Diagnostics.DiagnosticSource/tests/HttpHandlerDiagnosticListenerTests.cs +++ b/external/corefx/src/System.Diagnostics.DiagnosticSource/tests/HttpHandlerDiagnosticListenerTests.cs @@ -1,4 +1,8 @@ -using System.Collections.Concurrent; +// 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.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Net; @@ -38,6 +42,7 @@ namespace System.Diagnostics.Tests /// A simple test to make sure the Http Diagnostic Source is initialized properly after we subscribed to it, using /// the subscribe overload with just the observer argument. /// + [OuterLoop] [Fact] public void TestReflectInitializationViaSubscription1() { @@ -59,6 +64,7 @@ namespace System.Diagnostics.Tests /// A simple test to make sure the Http Diagnostic Source is initialized properly after we subscribed to it, using /// the subscribe overload with just the observer argument and the more complicating enable filter function. /// + [OuterLoop] [Fact] public void TestReflectInitializationViaSubscription2() { @@ -80,6 +86,7 @@ namespace System.Diagnostics.Tests /// A simple test to make sure the Http Diagnostic Source is initialized properly after we subscribed to it, using /// the subscribe overload with the observer argument and the simple predicate argument. /// + [OuterLoop] [Fact] public void TestReflectInitializationViaSubscription3() { @@ -100,6 +107,7 @@ namespace System.Diagnostics.Tests /// /// Test to make sure we get both request and response events. /// + [OuterLoop] [Fact] public async Task TestBasicReceiveAndResponseEvents() { @@ -134,9 +142,51 @@ namespace System.Diagnostics.Tests } } + /// + /// Test to make sure we get both request and response events. + /// + [OuterLoop] + [Fact] + public async Task TestResponseWithoutContentEvents() + { + using (var eventRecords = new EventObserverAndRecorder()) + { + // Send a random Http request to generate some events + using (var client = new HttpClient()) + { + (await client.GetAsync(Configuration.Http.RemoteEmptyContentServer)).Dispose(); + } + + // We should have exactly one Start and one Stop event + Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key.EndsWith("Start"))); + Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key.EndsWith("Stop"))); + Assert.Equal(2, eventRecords.Records.Count); + + // Check to make sure: The first record must be a request, the next record must be a response. + KeyValuePair startEvent; + Assert.True(eventRecords.Records.TryDequeue(out startEvent)); + Assert.Equal("System.Net.Http.Desktop.HttpRequestOut.Start", startEvent.Key); + HttpWebRequest startRequest = ReadPublicProperty(startEvent.Value, "Request"); + Assert.NotNull(startRequest); + Assert.NotNull(startRequest.Headers["Request-Id"]); + + KeyValuePair stopEvent; + Assert.True(eventRecords.Records.TryDequeue(out stopEvent)); + Assert.Equal("System.Net.Http.Desktop.HttpRequestOut.Ex.Stop", stopEvent.Key); + HttpWebRequest stopRequest = ReadPublicProperty(stopEvent.Value, "Request"); + Assert.Equal(startRequest, stopRequest); + HttpStatusCode status = ReadPublicProperty(stopEvent.Value, "StatusCode"); + Assert.NotNull(status); + + WebHeaderCollection headers = ReadPublicProperty(stopEvent.Value, "Headers"); + Assert.NotNull(headers); + } + } + /// /// Test that if request is redirected, it gets only one Start and one Stop event /// + [OuterLoop] [Fact] public async Task TestRedirectedRequest() { @@ -158,6 +208,7 @@ namespace System.Diagnostics.Tests /// /// Test exception in request processing: exception should have expected type/status and now be swallowed by reflection hook /// + [OuterLoop] [Fact] public async Task TestRequestWithException() { @@ -181,6 +232,7 @@ namespace System.Diagnostics.Tests /// /// Test request cancellation: reflection hook does not throw /// + [OuterLoop] [Fact] public async Task TestCanceledRequest() { @@ -202,6 +254,7 @@ namespace System.Diagnostics.Tests /// /// Test Request-Id and Correlation-Context headers injection /// + [OuterLoop] [Fact] public async Task TestActivityIsCreated() { @@ -232,6 +285,7 @@ namespace System.Diagnostics.Tests /// /// Tests IsEnabled order and parameters /// + [OuterLoop] [Fact] public async Task TestIsEnabled() { @@ -262,10 +316,11 @@ namespace System.Diagnostics.Tests Assert.Equal(2, eventNumber); } } - + /// /// Tests that nothing happens if IsEnabled returns false /// + [OuterLoop] [Fact] public async Task TestIsEnabledAllOff() { @@ -283,6 +338,7 @@ namespace System.Diagnostics.Tests /// /// Tests that if IsEnabled for request is false, request is not instrumented /// + [OuterLoop] [Fact] public async Task TestIsEnabledRequestOff() { @@ -311,6 +367,7 @@ namespace System.Diagnostics.Tests /// /// Test to make sure every event record has the right dynamic properties. /// + [OuterLoop] [Fact] public void TestMultipleConcurrentRequests() { diff --git a/external/corefx/src/System.Diagnostics.EventLog/pkg/System.Diagnostics.EventLog.pkgproj b/external/corefx/src/System.Diagnostics.EventLog/pkg/System.Diagnostics.EventLog.pkgproj new file mode 100644 index 0000000000..b744ffe9d8 --- /dev/null +++ b/external/corefx/src/System.Diagnostics.EventLog/pkg/System.Diagnostics.EventLog.pkgproj @@ -0,0 +1,11 @@ + + + + + + net461;netcoreapp2.0;$(AllXamarinFrameworks) + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Diagnostics.EventLog/ref/System.Diagnostics.EventLog.cs b/external/corefx/src/System.Diagnostics.EventLog/ref/System.Diagnostics.EventLog.cs index e54c8a35e1..f7af463f3b 100644 --- a/external/corefx/src/System.Diagnostics.EventLog/ref/System.Diagnostics.EventLog.cs +++ b/external/corefx/src/System.Diagnostics.EventLog/ref/System.Diagnostics.EventLog.cs @@ -20,7 +20,6 @@ namespace System.Diagnostics public long InstanceId { get { throw null; } set { } } } [System.ComponentModel.DefaultEventAttribute("EntryWritten")] - [System.Diagnostics.MonitoringDescriptionAttribute("Provides interaction with Windows event logs.")] public partial class EventLog : System.ComponentModel.Component, System.ComponentModel.ISupportInitialize { public EventLog() { } @@ -29,44 +28,34 @@ namespace System.Diagnostics public EventLog(string logName, string machineName, string source) { } [System.ComponentModel.BrowsableAttribute(false)] [System.ComponentModel.DefaultValueAttribute(false)] - [System.Diagnostics.MonitoringDescriptionAttribute("Indicates if the component monitors the event log for changes.")] public bool EnableRaisingEvents { get { throw null; } set { } } [System.ComponentModel.BrowsableAttribute(false)] [System.ComponentModel.DesignerSerializationVisibilityAttribute((System.ComponentModel.DesignerSerializationVisibility)(0))] - [System.Diagnostics.MonitoringDescriptionAttribute("The contents of the log.")] public System.Diagnostics.EventLogEntryCollection Entries { get { throw null; } } [System.ComponentModel.DefaultValueAttribute("")] [System.ComponentModel.ReadOnlyAttribute(true)] [System.ComponentModel.SettingsBindableAttribute(true)] - [System.Diagnostics.MonitoringDescriptionAttribute("Gets or sets the name of the log to read from and write to.")] public string Log { get { throw null; } set { } } [System.ComponentModel.BrowsableAttribute(false)] public string LogDisplayName { get { throw null; } } [System.ComponentModel.DefaultValueAttribute(".")] [System.ComponentModel.ReadOnlyAttribute(true)] [System.ComponentModel.SettingsBindableAttribute(true)] - [System.Diagnostics.MonitoringDescriptionAttribute("The machine on which this event log resides.")] public string MachineName { get { throw null; } set { } } [System.ComponentModel.BrowsableAttribute(false)] [System.ComponentModel.DesignerSerializationVisibilityAttribute((System.ComponentModel.DesignerSerializationVisibility)(0))] - [System.Runtime.InteropServices.ComVisibleAttribute(false)] public long MaximumKilobytes { get { throw null; } set { } } [System.ComponentModel.BrowsableAttribute(false)] - [System.Runtime.InteropServices.ComVisibleAttribute(false)] public int MinimumRetentionDays { get { throw null; } } [System.ComponentModel.BrowsableAttribute(false)] - [System.Runtime.InteropServices.ComVisibleAttribute(false)] public System.Diagnostics.OverflowAction OverflowAction { get { throw null; } } [System.ComponentModel.DefaultValueAttribute("")] [System.ComponentModel.ReadOnlyAttribute(true)] [System.ComponentModel.SettingsBindableAttribute(true)] - [System.Diagnostics.MonitoringDescriptionAttribute("The application name (source name) to use when writing to the event log.")] public string Source { get { throw null; } set { } } [System.ComponentModel.BrowsableAttribute(false)] [System.ComponentModel.DefaultValueAttribute(null)] - [System.Diagnostics.MonitoringDescriptionAttribute("The object used to marshal the event handler calls issued as a result of an EventLog change.")] - public System.ComponentModel.ISynchronizeInvoke SynchronizingObject { [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Synchronization=true)]get { throw null; } set { } } - [System.Diagnostics.MonitoringDescriptionAttribute("Raised each time any application writes an entry to the event log.")] + public System.ComponentModel.ISynchronizeInvoke SynchronizingObject { [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Synchronization = true)]get { throw null; } set { } } public event System.Diagnostics.EntryWrittenEventHandler EntryWritten { add { } remove { } } public void BeginInit() { } public void Clear() { } @@ -86,9 +75,7 @@ namespace System.Diagnostics public static System.Diagnostics.EventLog[] GetEventLogs() { throw null; } public static System.Diagnostics.EventLog[] GetEventLogs(string machineName) { throw null; } public static string LogNameFromSourceName(string source, string machineName) { throw null; } - [System.Runtime.InteropServices.ComVisibleAttribute(false)] public void ModifyOverflowPolicy(System.Diagnostics.OverflowAction action, int retentionDays) { } - [System.Runtime.InteropServices.ComVisibleAttribute(false)] public void RegisterDisplayName(string resourceFile, long resourceId) { } public static bool SourceExists(string source) { throw null; } public static bool SourceExists(string source, string machineName) { throw null; } @@ -102,9 +89,7 @@ namespace System.Diagnostics public static void WriteEntry(string source, string message, System.Diagnostics.EventLogEntryType type, int eventID) { } public static void WriteEntry(string source, string message, System.Diagnostics.EventLogEntryType type, int eventID, short category) { } public static void WriteEntry(string source, string message, System.Diagnostics.EventLogEntryType type, int eventID, short category, byte[] rawData) { } - [System.Runtime.InteropServices.ComVisibleAttribute(false)] public void WriteEvent(System.Diagnostics.EventInstance instance, byte[] data, params object[] values) { } - [System.Runtime.InteropServices.ComVisibleAttribute(false)] public void WriteEvent(System.Diagnostics.EventInstance instance, params object[] values) { } public static void WriteEvent(string source, System.Diagnostics.EventInstance instance, byte[] data, params object[] values) { } public static void WriteEvent(string source, System.Diagnostics.EventInstance instance, params object[] values) { } @@ -114,35 +99,20 @@ namespace System.Diagnostics public sealed partial class EventLogEntry : System.ComponentModel.Component, System.Runtime.Serialization.ISerializable { internal EventLogEntry() { } - [System.Diagnostics.MonitoringDescriptionAttribute("The category for this message.")] public string Category { get { throw null; } } - [System.Diagnostics.MonitoringDescriptionAttribute("An application-specific category number assigned to this entry.")] public short CategoryNumber { get { throw null; } } - [System.Diagnostics.MonitoringDescriptionAttribute("The binary data associated with this entry in the event log.")] public byte[] Data { get { throw null; } } - [System.Diagnostics.MonitoringDescriptionAttribute("The type of entry - Information, Warning, etc.")] public System.Diagnostics.EventLogEntryType EntryType { get { throw null; } } - [System.Diagnostics.MonitoringDescriptionAttribute("The number identifying the message for this source.")] [System.ObsoleteAttribute("This property has been deprecated. Please use System.Diagnostics.EventLogEntry.InstanceId instead. http://go.microsoft.com/fwlink/?linkid=14202")] public int EventID { get { throw null; } } - [System.Diagnostics.MonitoringDescriptionAttribute("The sequence of this entry in the event log.")] public int Index { get { throw null; } } - [System.Diagnostics.MonitoringDescriptionAttribute("The full number identifying the message in the event message dll.")] - [System.Runtime.InteropServices.ComVisibleAttribute(false)] public long InstanceId { get { throw null; } } - [System.Diagnostics.MonitoringDescriptionAttribute("The machine on which this event log resides.")] public string MachineName { get { throw null; } } - [System.Diagnostics.MonitoringDescriptionAttribute("The text of the message for this entry.")] public string Message { get { throw null; } } - [System.Diagnostics.MonitoringDescriptionAttribute("The application-supplied strings used in the message.")] public string[] ReplacementStrings { get { throw null; } } - [System.Diagnostics.MonitoringDescriptionAttribute("The name of the application that wrote this entry.")] public string Source { get { throw null; } } - [System.Diagnostics.MonitoringDescriptionAttribute("The time at which the application logged this entry.")] public System.DateTime TimeGenerated { get { throw null; } } - [System.Diagnostics.MonitoringDescriptionAttribute("The time at which the system logged this entry to the event log.")] public System.DateTime TimeWritten { get { throw null; } } - [System.Diagnostics.MonitoringDescriptionAttribute("The username of the account associated with this entry by the writing application.")] public string UserName { get { throw null; } } public bool Equals(System.Diagnostics.EventLogEntry otherEntry) { throw null; } void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } diff --git a/external/corefx/src/System.Diagnostics.EventLog/src/Configurations.props b/external/corefx/src/System.Diagnostics.EventLog/src/Configurations.props index e21546f0e0..880a70b1e2 100644 --- a/external/corefx/src/System.Diagnostics.EventLog/src/Configurations.props +++ b/external/corefx/src/System.Diagnostics.EventLog/src/Configurations.props @@ -1,10 +1,14 @@ - - netcoreapp-Windows_NT; + + netcoreapp2.0-Windows_NT; netfx; netstandard; + + + $(PackageConfigurations) + netcoreapp-Windows_NT; diff --git a/external/corefx/src/System.Diagnostics.EventLog/src/PinvokeAnalyzerExceptionList.analyzerdata.netcoreapp2.0 b/external/corefx/src/System.Diagnostics.EventLog/src/PinvokeAnalyzerExceptionList.analyzerdata.netcoreapp2.0 new file mode 100644 index 0000000000..afae65bd27 --- /dev/null +++ b/external/corefx/src/System.Diagnostics.EventLog/src/PinvokeAnalyzerExceptionList.analyzerdata.netcoreapp2.0 @@ -0,0 +1,7 @@ +advapi32.dll!ClearEventLog +advapi32.dll!CloseEventLog +advapi32.dll!GetNumberOfEventLogRecords +advapi32.dll!GetOldestEventLogRecord +advapi32.dll!NotifyChangeEventLog +advapi32.dll!OpenEventLog +advapi32.dll!ReadEventLog \ No newline at end of file diff --git a/external/corefx/src/System.Diagnostics.EventLog/src/Resources/Strings.resx b/external/corefx/src/System.Diagnostics.EventLog/src/Resources/Strings.resx index de56ea52f6..f5066d30d8 100644 --- a/external/corefx/src/System.Diagnostics.EventLog/src/Resources/Strings.resx +++ b/external/corefx/src/System.Diagnostics.EventLog/src/Resources/Strings.resx @@ -117,72 +117,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Provides interaction with Windows event logs. - - - The contents of the log. - - - The category for this message. - - - An application-specific category number assigned to this entry. - - - The binary data associated with this entry in the event log. - - - The type of entry - Information, Warning, etc. - - - The number identifying the message for this source. - - - The sequence of this entry in the event log. - - - The machine on which this event log resides. - - - The text of the message for this entry. - - - The application-supplied strings used in the message. - - - The full number identifying the message in the event message dll. - - - The name of the application that wrote this entry. - - - The time at which the application logged this entry. - - - The time at which the system logged this entry to the event log. - - - The username of the account associated with this entry by the writing application. - - - Raised each time any application writes an entry to the event log. - - - Gets or sets the name of the log to read from and write to. - - - Indicates if the component monitors the event log for changes. - - - The application name (source name) to use when writing to the event log. - - - The object used to marshal the event handler calls issued as a result of an EventLog change. - - - The name of the machine on which to read or write events. - Event log names must consist of printable characters and cannot contain \\, *, ?, or spaces @@ -232,7 +166,7 @@ Cannot open registry key {0}\\{1}\\{2}. - Source {0} already exists on the computer '{1}'. + Source {0} already exists on the local computer. Source {0} is not registered on the local computer. diff --git a/external/corefx/src/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj b/external/corefx/src/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj index d2480d8cae..7c4f3df238 100644 --- a/external/corefx/src/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj +++ b/external/corefx/src/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj @@ -11,11 +11,13 @@ + + - + diff --git a/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/CompModSwitches.cs b/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/CompModSwitches.cs index 4ef9619c95..260fcf8341 100644 --- a/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/CompModSwitches.cs +++ b/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/CompModSwitches.cs @@ -8,22 +8,8 @@ namespace System.ComponentModel { internal static class CompModSwitches { - private static volatile BooleanSwitch s_commonDesignerServices; private static volatile TraceSwitch s_eventLog; - public static BooleanSwitch CommonDesignerServices - { - get - { - if (s_commonDesignerServices == null) - { - s_commonDesignerServices = new BooleanSwitch(nameof(CommonDesignerServices), "Assert if any common designer service is not found."); - } - - return s_commonDesignerServices; - } - } - public static TraceSwitch EventLog { get @@ -32,9 +18,9 @@ namespace System.ComponentModel { s_eventLog = new TraceSwitch(nameof(EventLog), "Enable tracing for the EventLog component."); } - + return s_eventLog; } } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/EventLog.cs b/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/EventLog.cs index 2b43d2a1ef..b8eb8292c4 100644 --- a/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/EventLog.cs +++ b/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/EventLog.cs @@ -2,14 +2,12 @@ // 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.Collections; using System.ComponentModel; using System.Globalization; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security; -using System.Security.Permissions; using System.Text; using System.Threading; using Microsoft.Win32; @@ -17,7 +15,10 @@ using Microsoft.Win32.SafeHandles; namespace System.Diagnostics { - [DefaultEvent("EntryWritten"), MonitoringDescription("Provides interaction with Windows event logs.")] + /// + /// Provides interaction with Windows event logs. + /// + [DefaultEvent("EntryWritten")] public class EventLog : Component, ISupportInitialize { private const string EventLogKey = "SYSTEM\\CurrentControlSet\\Services\\EventLog"; @@ -26,7 +27,7 @@ namespace System.Diagnostics private const int DefaultMaxSize = 512 * 1024; private const int DefaultRetention = 7 * SecondsPerDay; private const int SecondsPerDay = 60 * 60 * 24; - + private EventLogInternal _underlyingEventLog; public EventLog() : this(string.Empty, ".", string.Empty) @@ -46,9 +47,11 @@ namespace System.Diagnostics _underlyingEventLog = new EventLogInternal(logName, machineName, source, this); } + /// + /// The contents of the log. + /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - [MonitoringDescription("The contents of the log.")] public EventLogEntryCollection Entries { get @@ -66,8 +69,10 @@ namespace System.Diagnostics } } + /// + /// Gets or sets the name of the log to read from and write to. + /// [ReadOnly(true)] - [MonitoringDescription("Gets or sets the name of the log to read from and write to.")] [DefaultValue("")] [SettingsBindable(true)] public string Log @@ -92,8 +97,10 @@ namespace System.Diagnostics } } + /// + /// The machine on which this event log resides. + /// [ReadOnly(true)] - [MonitoringDescription("The machine on which this event log resides.")] [DefaultValue(".")] [SettingsBindable(true)] public string MachineName @@ -120,22 +127,19 @@ namespace System.Diagnostics [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [Browsable(false)] - [ComVisible(false)] - public long MaximumKilobytes + public long MaximumKilobytes { get => _underlyingEventLog.MaximumKilobytes; set => _underlyingEventLog.MaximumKilobytes = value; } [Browsable(false)] - [ComVisible(false)] public OverflowAction OverflowAction { get => _underlyingEventLog.OverflowAction; } [Browsable(false)] - [ComVisible(false)] public int MinimumRetentionDays { get => _underlyingEventLog.MinimumRetentionDays; @@ -151,8 +155,10 @@ namespace System.Diagnostics return GetService(service); } + /// + /// Indicates if the component monitors the event log for changes. + /// [Browsable(false)] - [MonitoringDescription("Indicates if the component monitors the event log for changes.")] [DefaultValue(false)] public bool EnableRaisingEvents { @@ -160,17 +166,21 @@ namespace System.Diagnostics set => _underlyingEventLog.EnableRaisingEvents = value; } + /// + /// The object used to marshal the event handler calls issued as a result of an EventLog change. + /// [Browsable(false)] [DefaultValue(null)] - [MonitoringDescription("The object used to marshal the event handler calls issued as a result of an EventLog change.")] public ISynchronizeInvoke SynchronizingObject { get => _underlyingEventLog.SynchronizingObject; set => _underlyingEventLog.SynchronizingObject = value; } + /// + /// The application name (source name) to use when writing to the event log. + /// [ReadOnly(true)] - [MonitoringDescription("The application name (source name) to use when writing to the event log.")] [DefaultValue("")] [SettingsBindable(true)] public string Source @@ -192,7 +202,9 @@ namespace System.Diagnostics } } - [MonitoringDescription("Raised each time any application writes an entry to the event log.")] + /// + /// Raised each time any application writes an entry to the event log. + /// public event EntryWrittenEventHandler EntryWritten { add @@ -478,7 +490,7 @@ namespace System.Diagnostics } finally { - mutex?.ReleaseMutex(); + mutex?.ReleaseMutex(); } } @@ -747,13 +759,11 @@ namespace System.Diagnostics } } - [ComVisible(false)] public void ModifyOverflowPolicy(OverflowAction action, int retentionDays) { _underlyingEventLog.ModifyOverflowPolicy(action, retentionDays); } - [ComVisible(false)] public void RegisterDisplayName(string resourceFile, long resourceId) { _underlyingEventLog.RegisterDisplayName(resourceFile, resourceId); @@ -1005,13 +1015,11 @@ namespace System.Diagnostics _underlyingEventLog.WriteEntry(message, type, eventID, category, rawData); } - [ComVisible(false)] public void WriteEvent(EventInstance instance, params Object[] values) { WriteEvent(instance, null, values); } - [ComVisible(false)] public void WriteEvent(EventInstance instance, byte[] data, params Object[] values) { _underlyingEventLog.WriteEvent(instance, data, values); @@ -1046,4 +1054,4 @@ namespace System.Diagnostics return source; } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/EventLogEntry.cs b/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/EventLogEntry.cs index 899b279a9a..11f5ee9993 100644 --- a/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/EventLogEntry.cs +++ b/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/EventLogEntry.cs @@ -37,7 +37,9 @@ namespace System.Diagnostics throw new PlatformNotSupportedException(); } - [MonitoringDescription("The machine on which this event log resides.")] + /// + /// The machine on which this event log resides. + /// public string MachineName { get @@ -60,7 +62,9 @@ namespace System.Diagnostics } } - [MonitoringDescription("The binary data associated with this entry in the event log.")] + /// + /// The binary data associated with this entry in the event log. + /// public byte[] Data { get @@ -73,7 +77,9 @@ namespace System.Diagnostics } } - [MonitoringDescription("The sequence of this entry in the event log.")] + /// + /// The sequence of this entry in the event log. + /// public int Index { get @@ -82,7 +88,9 @@ namespace System.Diagnostics } } - [MonitoringDescription("The category for this message.")] + /// + /// The category for this message. + /// public string Category { get @@ -101,7 +109,9 @@ namespace System.Diagnostics } } - [MonitoringDescription("An application-specific category number assigned to this entry.")] + /// + /// An application-specific category number assigned to this entry. + /// public short CategoryNumber { get @@ -110,10 +120,10 @@ namespace System.Diagnostics } } - [ - MonitoringDescription("The number identifying the message for this source."), - Obsolete("This property has been deprecated. Please use System.Diagnostics.EventLogEntry.InstanceId instead. http://go.microsoft.com/fwlink/?linkid=14202") - ] + /// + /// The number identifying the message for this source. + /// + [Obsolete("This property has been deprecated. Please use System.Diagnostics.EventLogEntry.InstanceId instead. http://go.microsoft.com/fwlink/?linkid=14202")] public int EventID { get @@ -122,7 +132,9 @@ namespace System.Diagnostics } } - [MonitoringDescription("The type of entry - Information, Warning, etc.")] + /// + /// The type of entry - Information, Warning, etc. + /// public EventLogEntryType EntryType { get @@ -131,7 +143,9 @@ namespace System.Diagnostics } } - [MonitoringDescription("The text of the message for this entry.")] + /// + /// The text of the message for this entry. + /// public string Message { get @@ -143,7 +157,7 @@ namespace System.Diagnostics string msg = owner.FormatMessageWrapper(dllNames, (uint)msgId, ReplacementStrings); if (msg == null) { - StringBuilder msgBuf = new StringBuilder(SR.MessageNotFormatted + msgId + Source); + StringBuilder msgBuf = new StringBuilder(SR.Format(SR.MessageNotFormatted, msgId, Source)); string[] strings = ReplacementStrings; for (int i = 0; i < strings.Length; i++) { @@ -166,7 +180,9 @@ namespace System.Diagnostics } } - [MonitoringDescription("The name of the application that wrote this entry.")] + /// + /// The name of the application that wrote this entry. + /// public string Source { get @@ -186,7 +202,9 @@ namespace System.Diagnostics } } - [MonitoringDescription("The application-supplied strings used in the message.")] + /// + /// The application-supplied strings used in the message. + /// public string[] ReplacementStrings { get @@ -214,10 +232,9 @@ namespace System.Diagnostics } } - [ - MonitoringDescription("The full number identifying the message in the event message dll."), - ComVisible(false) - ] + /// + /// The full number identifying the message in the event message dll. + /// public Int64 InstanceId { get @@ -226,7 +243,9 @@ namespace System.Diagnostics } } - [MonitoringDescription("The time at which the application logged this entry.")] + /// + /// The time at which the application logged this entry. + /// public DateTime TimeGenerated { get @@ -235,7 +254,9 @@ namespace System.Diagnostics } } - [MonitoringDescription("The time at which the system logged this entry to the event log.")] + /// + /// The time at which the system logged this entry to the event log. + /// public DateTime TimeWritten { get @@ -244,7 +265,9 @@ namespace System.Diagnostics } } - [MonitoringDescription("The username of the account associated with this entry by the writing application.")] + /// + /// The username of the account associated with this entry by the writing application. + /// public string UserName { get diff --git a/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/EventLogEntryCollection.cs b/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/EventLogEntryCollection.cs index cb0a9a2c9c..9c7b6f7c57 100644 --- a/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/EventLogEntryCollection.cs +++ b/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/EventLogEntryCollection.cs @@ -103,4 +103,4 @@ namespace System.Diagnostics } } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/EventLogInternal.cs b/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/EventLogInternal.cs index cb89277555..0e466d2fea 100644 --- a/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/EventLogInternal.cs +++ b/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/EventLogInternal.cs @@ -105,14 +105,6 @@ namespace System.Diagnostics } } - public EventLogInternal() : this("", ".", "", null) - { - } - - public EventLogInternal(string logName) : this(logName, ".", "", null) - { - } - public EventLogInternal(string logName, string machineName) : this(logName, machineName, "", null) { } @@ -258,7 +250,6 @@ namespace System.Diagnostics } } - [ComVisible(false)] public long MaximumKilobytes { get @@ -300,7 +291,6 @@ namespace System.Diagnostics } } - [ComVisible(false)] public OverflowAction OverflowAction { get @@ -324,7 +314,6 @@ namespace System.Diagnostics } } - [ComVisible(false)] public int MinimumRetentionDays { get @@ -1068,7 +1057,6 @@ namespace System.Diagnostics (0xFF00 & (buf[offset + 1] << 8)) | (0xFF & (buf[offset])); } - [ComVisible(false)] public void ModifyOverflowPolicy(OverflowAction action, int retentionDays) { string currentMachineName = this.machineName; @@ -1150,7 +1138,6 @@ namespace System.Diagnostics writeHandle = handle; } - [ComVisible(false)] public void RegisterDisplayName(string resourceFile, long resourceId) { string currentMachineName = this.machineName; @@ -1348,26 +1335,6 @@ namespace System.Diagnostics boolFlags[Flag_sourceVerified] = true; } - public void WriteEntry(string message) - { - WriteEntry(message, EventLogEntryType.Information, (short)0, 0, null); - } - - public void WriteEntry(string message, EventLogEntryType type) - { - WriteEntry(message, type, (short)0, 0, null); - } - - public void WriteEntry(string message, EventLogEntryType type, int eventID) - { - WriteEntry(message, type, eventID, 0, null); - } - - public void WriteEntry(string message, EventLogEntryType type, int eventID, short category) - { - WriteEntry(message, type, eventID, category, null); - } - public void WriteEntry(string message, EventLogEntryType type, int eventID, short category, byte[] rawData) { @@ -1394,13 +1361,6 @@ namespace System.Diagnostics InternalWriteEvent((uint)eventID, (ushort)category, type, new string[] { message }, rawData, currentMachineName); } - [ComVisible(false)] - public void WriteEvent(EventInstance instance, params Object[] values) - { - WriteEvent(instance, null, values); - } - - [ComVisible(false)] public void WriteEvent(EventInstance instance, byte[] data, params Object[] values) { if (instance == null) @@ -1479,7 +1439,7 @@ namespace System.Diagnostics sid, (short)strings.Length, rawData.Length, new HandleRef(this, stringsRootHandle.AddrOfPinnedObject()), rawData); if (!success) { - //Trace("WriteEvent", "Throwing Win32Exception"); + // Trace("WriteEvent", "Throwing Win32Exception"); throw SharedUtils.CreateSafeWin32Exception(); } } diff --git a/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/EventSourceCreationData.cs b/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/EventSourceCreationData.cs index 30b1529959..7587ed03ab 100644 --- a/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/EventSourceCreationData.cs +++ b/external/corefx/src/System.Diagnostics.EventLog/src/System/Diagnostics/EventSourceCreationData.cs @@ -21,18 +21,6 @@ namespace System.Diagnostics MachineName = machineName; } - private EventSourceCreationData(string source, string logName, string machineName, - string messageResourceFile, string parameterResourceFile, - string categoryResourceFile, short categoryCount) - : this(source, logName, machineName) - - { - MessageResourceFile = messageResourceFile; - ParameterResourceFile = parameterResourceFile; - CategoryResourceFile = categoryResourceFile; - CategoryCount = categoryCount; - } - public string LogName { get; set; } = "Application"; public string MachineName { get; set; } = "."; @@ -57,4 +45,4 @@ namespace System.Diagnostics } } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Diagnostics.EventLog/tests/EventInstanceTests.cs b/external/corefx/src/System.Diagnostics.EventLog/tests/EventInstanceTests.cs new file mode 100644 index 0000000000..eaee87c751 --- /dev/null +++ b/external/corefx/src/System.Diagnostics.EventLog/tests/EventInstanceTests.cs @@ -0,0 +1,45 @@ +// 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.even + +using Xunit; + +namespace System.Diagnostics.Tests +{ + public class EventInstanceTests + { + // These ids can be any non-negative numbers + private const long instanceId = 57; + private const int categoryId = 657; + + [ConditionalFact(typeof(Helpers), nameof(Helpers.SupportsEventLogs))] + public void EventInstanceCreation() + { + EventInstance eventInstance = new EventInstance(instanceId, categoryId); + + Assert.Equal(categoryId, eventInstance.CategoryId); + Assert.Equal(instanceId, eventInstance.InstanceId); + Assert.Equal(EventLogEntryType.Information, eventInstance.EntryType); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.SupportsEventLogs))] + public void EventInstanceOutOfRangeException() + { + Assert.Throws(() => new EventInstance(-1, 0)); + Assert.Throws(() => new EventInstance(0, -1)); + Assert.Throws(() => new EventInstance(-1, -1)); + Assert.Throws(() => new EventInstance(0, int.MaxValue)); + Assert.Throws(() => new EventInstance(long.MaxValue, 0)); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.SupportsEventLogs))] + public void EventInstanceCreationWithType() + { + EventInstance eventInstance = new EventInstance(instanceId, categoryId, EventLogEntryType.Warning); + + Assert.Equal(categoryId, eventInstance.CategoryId); + Assert.Equal(instanceId, eventInstance.InstanceId); + Assert.Equal(EventLogEntryType.Warning, eventInstance.EntryType); + } + } +} diff --git a/external/corefx/src/System.Diagnostics.EventLog/tests/EventLogEntryCollectionTests.cs b/external/corefx/src/System.Diagnostics.EventLog/tests/EventLogEntryCollectionTests.cs new file mode 100644 index 0000000000..a2117fdc2d --- /dev/null +++ b/external/corefx/src/System.Diagnostics.EventLog/tests/EventLogEntryCollectionTests.cs @@ -0,0 +1,129 @@ +// 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 Xunit; + +namespace System.Diagnostics.Tests +{ + public class EventLogEntryCollectionTests + { + private const string message = "EntryCollectionMessage"; + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void CopyingEventLogEntryCollection() + { + string log = "CopyCollection"; + string source = "Source_" + nameof(CopyingEventLogEntryCollection); + + try + { + EventLog.CreateEventSource(source, log); + using (EventLog eventLog = new EventLog()) + { + eventLog.Source = source; + Helpers.RetryOnWin7(() => eventLog.WriteEntry(message)); + Helpers.RetryOnWin7(() => eventLog.WriteEntry("Further Testing")); + + EventLogEntryCollection entryCollection = eventLog.Entries; + EventLogEntry[] entryCollectionCopied = new EventLogEntry[entryCollection.Count]; + + Helpers.RetryOnWin7(() => entryCollection.CopyTo(entryCollectionCopied, 0)); + int i = 0; + foreach (EventLogEntry entry in entryCollection) + { + Assert.Equal(entry.Message, Helpers.RetryOnWin7(() => entryCollectionCopied[i].Message)); + i += 1; + } + } + } + finally + { + EventLog.DeleteEventSource(source); + Helpers.RetryOnWin7(() => EventLog.Delete(log)); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void CheckingEntryEqualityWithNull() + { + string log = "NullTest"; + string source = "Source_" + nameof(CheckingEntryEqualityWithNull); + + try + { + EventLog.CreateEventSource(source, log); + using (EventLog eventLog = new EventLog()) + { + eventLog.Source = source; + Helpers.RetryOnWin7(() => eventLog.WriteEntry(message)); + Helpers.WaitForEventLog(eventLog, 1); + EventLogEntry entry = Helpers.RetryOnWin7(() => eventLog.Entries[eventLog.Entries.Count - 1]); + Assert.False(entry.Equals(null)); + } + } + finally + { + EventLog.DeleteEventSource(source); + Helpers.RetryOnWin7(() => EventLog.Delete(log)); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void CheckingEntryEqualityAndIndex() + { + string log = "IndexTest"; + string source = "Source_" + nameof(CheckingEntryEqualityAndIndex); + + try + { + EventLog.CreateEventSource(source, log); + using (EventLog eventLog = new EventLog()) + { + eventLog.Source = source; + Helpers.RetryOnWin7(() => eventLog.WriteEntry(message)); + Helpers.WaitForEventLog(eventLog, 1); //There is latency between writing and getting the entry + EventLogEntry entry = Helpers.RetryOnWin7(() => eventLog.Entries[eventLog.Entries.Count - 1]); + Assert.True(entry.Equals(entry)); + + Helpers.RetryOnWin7(() => eventLog.WriteEntry(message)); + Helpers.WaitForEventLog(eventLog, 2); + EventLogEntry secondEntry = Helpers.RetryOnWin7(() => eventLog.Entries[eventLog.Entries.Count - 1]); + Assert.Equal(entry.Index + 1, secondEntry.Index); + } + } + finally + { + EventLog.DeleteEventSource(source); + Helpers.RetryOnWin7(() => EventLog.Delete(log)); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void CheckingEntryInEquality() + { + string log = "InEqualityTest"; + string source = "Source_" + nameof(CheckingEntryInEquality); + + try + { + EventLog.CreateEventSource(source, log); + using (EventLog eventLog = new EventLog()) + { + eventLog.Source = source; + Helpers.RetryOnWin7(() => eventLog.WriteEntry(message)); + Helpers.RetryOnWin7(() => eventLog.WriteEntry(message)); + Helpers.WaitForEventLog(eventLog, 2); + EventLogEntry entry = Helpers.RetryOnWin7(() => eventLog.Entries[eventLog.Entries.Count - 1]); + EventLogEntry secondEntry = Helpers.RetryOnWin7(() => eventLog.Entries[eventLog.Entries.Count - 2]); + Assert.False(entry.Equals(secondEntry)); + } + } + finally + { + EventLog.DeleteEventSource(source); + Helpers.RetryOnWin7(() => EventLog.Delete(log)); + } + } + } +} diff --git a/external/corefx/src/System.Diagnostics.EventLog/tests/EventLogTests/EventLogEntryWrittenTest.cs b/external/corefx/src/System.Diagnostics.EventLog/tests/EventLogTests/EventLogEntryWrittenTest.cs new file mode 100644 index 0000000000..9c72a945ac --- /dev/null +++ b/external/corefx/src/System.Diagnostics.EventLog/tests/EventLogTests/EventLogEntryWrittenTest.cs @@ -0,0 +1,69 @@ +// 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.Threading; +using Xunit; + +namespace System.Diagnostics.Tests +{ + public class EventLogEntryEventWrittenTest + { + static AutoResetEvent signal; + private const string message = "EventLogEntryEventWrittenTestMessage"; + private int eventCounter; + + public void RaisingEvent(string log, string methodName, bool waitOnEvent = true) + { + signal = new AutoResetEvent(false); + eventCounter = 0; + string source = "Source_" + methodName; + + try + { + EventLog.CreateEventSource(source, log); + using (EventLog eventLog = new EventLog()) + { + eventLog.Source = source; + eventLog.EntryWritten += new EntryWrittenEventHandler((object sourceObject, EntryWrittenEventArgs e) => + { + eventCounter += 1; + signal.Set(); + }); + Helpers.RetryOnWin7(() => eventLog.EnableRaisingEvents = waitOnEvent); + Helpers.RetryOnWin7(() => eventLog.WriteEntry(message, EventLogEntryType.Information)); + if (waitOnEvent) + { + if (!signal.WaitOne(6000)) + { + eventLog.WriteEntry(message, EventLogEntryType.Information); + Assert.True(signal.WaitOne(6000)); + } + // The system responds to WriteEntry only if the last write event occurred at least six seconds previously. + // This implies that the system will only receive one EntryWritten event notification within a six-second interval, even if more than one event log change occurs. + // For more information https://msdn.microsoft.com/en-us/library/system.diagnostics.eventlog.entrywritten(v=vs.110).aspx + } + } + } + finally + { + EventLog.DeleteEventSource(source); + Helpers.RetryOnWin7(() => EventLog.Delete(log)); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void EntryWrittenEventRaised() + { + RaisingEvent("EnableEvent", nameof(EntryWrittenEventRaised)); + Assert.NotEqual(0, eventCounter); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void EntryWrittenEventRaiseDisable() + { + RaisingEvent("DisableEvent", nameof(EntryWrittenEventRaiseDisable), waitOnEvent: false); + Assert.Equal(0, eventCounter); + } + } +} diff --git a/external/corefx/src/System.Diagnostics.EventLog/tests/EventLogTests/EventLogSourceCreationTests.cs b/external/corefx/src/System.Diagnostics.EventLog/tests/EventLogTests/EventLogSourceCreationTests.cs index 291b1dc4e1..3aa8047ad7 100644 --- a/external/corefx/src/System.Diagnostics.EventLog/tests/EventLogTests/EventLogSourceCreationTests.cs +++ b/external/corefx/src/System.Diagnostics.EventLog/tests/EventLogTests/EventLogSourceCreationTests.cs @@ -3,48 +3,163 @@ // See the LICENSE file in the project root for more information. using Xunit; -using System; namespace System.Diagnostics.Tests { public class EventLogSourceCreationTests { - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] public void CheckSourceExistenceAndDeletion() { - if (!AdminHelpers.IsProcessElevated()) - return; - - string source = Guid.NewGuid().ToString("N"); + string source = "Source_" + nameof(EventLogSourceCreationTests); + string log = "SourceExistenceLog"; try { - EventLog.CreateEventSource(source, "MyNewLog"); + EventLog.CreateEventSource(source, log); Assert.True(EventLog.SourceExists(source)); } finally { EventLog.DeleteEventSource(source); + Helpers.RetryOnWin7(() => EventLog.Delete(log)); } Assert.False(EventLog.SourceExists(source)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + [ConditionalFact(typeof(Helpers), nameof(Helpers.SupportsEventLogs))] public void CheckSourceExistsArgumentNull() { - if (!AdminHelpers.IsProcessElevated()) - return; - Assert.False(EventLog.SourceExists(null)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] public void DeleteUnregisteredSource() { - if (!AdminHelpers.IsProcessElevated()) - return; + Assert.Throws(() => EventLog.DeleteEventSource(Guid.NewGuid().ToString("N"))); + } - Assert.Throws(() => EventLog.DeleteEventSource(Guid.NewGuid().ToString("N"))); + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void LogNameNullMeansApplicationLog() + { + string source = "Source_" + nameof(LogNameNullMeansApplicationLog); + + try + { + EventLog.CreateEventSource(source, null); + Assert.True(EventLog.SourceExists(source)); + Assert.Equal("Application", EventLog.LogNameFromSourceName(source, ".")); + } + finally + { + EventLog.DeleteEventSource(source); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void SourceNameNull() + { + Assert.Throws(() => EventLog.CreateEventSource(null, "logName")); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void IncorrectLogName() + { + string source = "Source_" + nameof(IncorrectLogName); + Assert.Throws(() => EventLog.CreateEventSource(source, "?")); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void SourceNameMaxLengthExceeded() + { + string source = new string('s', 254); + Assert.Throws(() => EventLog.CreateEventSource(source, null)); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void SourceDataNull() + { + Assert.Throws(() => EventLog.CreateEventSource(null)); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void SourceAlreadyExistsWhenCreatingSource() + { + string source = "Source_" + nameof(SourceAlreadyExistsWhenCreatingSource); + string log = "ExistingSource"; + try + { + EventLog.CreateEventSource(source, log); + Assert.True(EventLog.SourceExists(source)); + Assert.Throws(() => EventLog.CreateEventSource(source, log)); + } + finally + { + EventLog.DeleteEventSource(source); + Helpers.RetryOnWin7(() => EventLog.Delete(log)); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void LogNameAlreadyExists_Throws() + { + string source = "Source_" + nameof(LogNameAlreadyExists_Throws); + string log = "AppEvent"; + + Assert.Throws(() => EventLog.CreateEventSource(source, log)); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.SupportsEventLogs))] + public void EventSourceCategoryCount_Invalid() + { + string log = "InvalidData"; + string source = "Source_" + nameof(EventSourceCategoryCount_Invalid); + + EventSourceCreationData mySourceData = new EventSourceCreationData(source, log); + Assert.Throws(() => mySourceData.CategoryCount = -1); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.SupportsEventLogs))] + public void MessageResourceFile_Set() + { + string messageFile = "ResourceFile"; + string source = "Source" + nameof(MessageResourceFile_Set); + string log = "MessageResourceFile"; + EventSourceCreationData sourceData = new EventSourceCreationData(source, log); + sourceData.MessageResourceFile = messageFile; + Assert.Equal(messageFile, sourceData.MessageResourceFile); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.SupportsEventLogs))] + public void CategoryResourceFile_Set() + { + string messageFile = "ResourceFile"; + string source = "Source" + nameof(CategoryResourceFile_Set); + string log = "MessageResourceFile"; + EventSourceCreationData sourceData = new EventSourceCreationData(source, log); + sourceData.CategoryResourceFile = messageFile; + Assert.Equal(messageFile, sourceData.CategoryResourceFile); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.SupportsEventLogs))] + public void ParameterResourceFile_Set() + { + string messageFile = "ResourceFile"; + string source = "Source" + nameof(ParameterResourceFile_Set); + string log = "MessageResourceFile"; + EventSourceCreationData sourceData = new EventSourceCreationData(source, log); + sourceData.ParameterResourceFile = messageFile; + Assert.Equal(messageFile, sourceData.ParameterResourceFile); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.SupportsEventLogs))] + public void CategoryCount_Set() + { + string source = "Source" + nameof(CategoryCount_Set); + string log = "MessageResourceFile"; + EventSourceCreationData sourceData = new EventSourceCreationData(source, log); + sourceData.CategoryCount = 2; + Assert.Equal(2, sourceData.CategoryCount); } } } diff --git a/external/corefx/src/System.Diagnostics.EventLog/tests/EventLogTests/EventLogTests.cs b/external/corefx/src/System.Diagnostics.EventLog/tests/EventLogTests/EventLogTests.cs index 9c511badc1..6bf0ff5293 100644 --- a/external/corefx/src/System.Diagnostics.EventLog/tests/EventLogTests/EventLogTests.cs +++ b/external/corefx/src/System.Diagnostics.EventLog/tests/EventLogTests/EventLogTests.cs @@ -2,75 +2,349 @@ // 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.Globalization; +using System.IO; using Xunit; namespace System.Diagnostics.Tests { - public class EventLogTests + public class EventLogTests : FileCleanupTestBase { - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + [ConditionalFact(typeof(Helpers), nameof(Helpers.SupportsEventLogs))] public void EventLogReinitializationException() { using (EventLog eventLog = new EventLog()) { eventLog.BeginInit(); Assert.Throws(() => eventLog.BeginInit()); + eventLog.EndInit(); } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] - public void ClearLogTest() + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void ClearLog() { - if (!AdminHelpers.IsProcessElevated()) - return; + string log = "ClearTest"; + string source = "Source_" + nameof(ClearLog); - string source = Guid.NewGuid().ToString("N"); - string logName = Guid.NewGuid().ToString("N"); try { - EventLog.CreateEventSource(source, logName); - using (EventLog myLog = new EventLog()) + EventLog.CreateEventSource(source, log); + using (EventLog eventLog = new EventLog()) { - myLog.Source = source; - myLog.WriteEntry("Writing to event log."); - Assert.True(myLog.Entries.Count != 0); - myLog.Clear(); - Assert.Equal(0, myLog.Entries.Count); + eventLog.Source = source; + eventLog.Clear(); + Assert.Equal(0, Helpers.RetryOnWin7((() => eventLog.Entries.Count))); + Helpers.RetryOnWin7(() => eventLog.WriteEntry("Writing to event log.")); + Helpers.WaitForEventLog(eventLog, 1); + Assert.Equal(1, Helpers.RetryOnWin7((() => eventLog.Entries.Count))); } } finally { EventLog.DeleteEventSource(source); + Helpers.RetryOnWin7(() => EventLog.Delete(log)); } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + [ConditionalFact(typeof(Helpers), nameof(Helpers.SupportsEventLogs))] public void ApplicationEventLog_Count() { - using ( EventLog ael = new EventLog("Application")) - { - Assert.InRange(ael.Entries.Count, 1, Int32.MaxValue); + using (EventLog eventLog = new EventLog("Application")) + { + Assert.InRange(Helpers.RetryOnWin7((() => eventLog.Entries.Count)), 1, Int32.MaxValue); } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] - public void DeleteLogTest() + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void DeleteLog() { - if (!AdminHelpers.IsProcessElevated()) - return; + string log = "DeleteTest"; + string source = "Source_" + nameof(DeleteLog); - string source = Guid.NewGuid().ToString("N"); - string logName = Guid.NewGuid().ToString("N"); try { - EventLog.CreateEventSource(source, logName); - Assert.True(EventLog.Exists(logName)); + EventLog.CreateEventSource(source, log); + Assert.True(EventLog.Exists(log)); } finally { - EventLog.Delete(logName); - Assert.False(EventLog.Exists(logName)); + EventLog.DeleteEventSource(source); + Helpers.RetryOnWin7(() => EventLog.Delete(log)); + Assert.False(EventLog.Exists(log)); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.SupportsEventLogs))] + public void CheckLogName_Get() + { + using (EventLog eventLog = new EventLog("Application")) + { + Assert.Equal("Application", eventLog.LogDisplayName); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.SupportsEventLogs))] + public void CheckMachineName_Get() + { + using (EventLog eventLog = new EventLog("Application")) + { + Assert.Equal(".", eventLog.MachineName); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.SupportsEventLogs))] + public void GetLogDisplayName_NotSet_Throws() + { + using (EventLog eventLog = new EventLog()) + { + eventLog.Log = Guid.NewGuid().ToString("N"); + Assert.Throws(() => eventLog.LogDisplayName); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.SupportsEventLogs))] + public void GetLogDisplayName_Set() + { + using (EventLog eventLog = new EventLog()) + { + eventLog.Log = "Application"; + Assert.Equal("Application", eventLog.LogDisplayName); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.SupportsEventLogs))] + public void EventLogs_Get() + { + Assert.Throws(() => EventLog.GetEventLogs("")); + EventLog[] eventLogCollection = EventLog.GetEventLogs(); + Assert.Contains(eventLogCollection, eventlog => eventlog.Log.Equals("Application")); + Assert.Contains(eventLogCollection, eventlog => eventlog.Log.Equals("Security")); + Assert.Contains(eventLogCollection, eventlog => eventlog.Log.Equals("System")); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void GetMaxKilobytes_Set() + { + string source = "Source_" + nameof(GetMaxKilobytes_Set); + string log = "maxKilobytesLog"; + + try + { + EventLog.CreateEventSource(source, log); + using (EventLog eventLog = new EventLog()) + { + eventLog.Source = source; + eventLog.MaximumKilobytes = 0x400; + Assert.Equal(0x400, eventLog.MaximumKilobytes); + } + } + finally + { + EventLog.DeleteEventSource(source); + Helpers.RetryOnWin7(() => EventLog.Delete(log)); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.SupportsEventLogs))] + public void MaxKilobytesOutOfRangeException() + { + using (EventLog eventLog = new EventLog()) + { + eventLog.Log = "Application"; + Assert.Throws(() => eventLog.MaximumKilobytes = 2); + Assert.Throws(() => eventLog.MaximumKilobytes = 0x3FFFC1); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void OverflowAndRetention_Set() + { + string source = "Source_" + nameof(OverflowAndRetention_Set); + string log = "Overflow_Set"; + + try + { + EventLog.CreateEventSource(source, log); + using (EventLog eventLog = new EventLog()) + { + eventLog.Source = source; + + // The second argument is only used when the overflow policy is set to OverWrite Older + eventLog.ModifyOverflowPolicy(OverflowAction.DoNotOverwrite, 1); + Assert.Equal(OverflowAction.DoNotOverwrite, eventLog.OverflowAction); + + // -1 means overflow action is donot OverWrite + Assert.Equal(-1, eventLog.MinimumRetentionDays); + } + } + finally + { + EventLog.DeleteEventSource(source); + Helpers.RetryOnWin7(() => EventLog.Delete(log)); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void Overflow_OverWriteOlderAndRetention_Set() + { + string source = "Source_" + nameof(OverflowAndRetention_Set); + string log = "Overflow_Set"; + int retentionDays = 30; // A number between 0 and 365 should work + + try + { + EventLog.CreateEventSource(source, log); + using (EventLog eventLog = new EventLog()) + { + eventLog.Source = source; + + // The second argument is only used when the overflow policy is set to OverWrite Older + eventLog.ModifyOverflowPolicy(OverflowAction.OverwriteOlder, retentionDays); + Assert.Equal(OverflowAction.OverwriteOlder, eventLog.OverflowAction); + Assert.Equal(retentionDays, eventLog.MinimumRetentionDays); + } + } + finally + { + EventLog.DeleteEventSource(source); + Helpers.RetryOnWin7(() => EventLog.Delete(log)); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void OverflowAndRetentionDaysOutOfRange() + { + using (EventLog eventLog = new EventLog()) + { + eventLog.Log = "Application"; + Assert.Throws(() => eventLog.ModifyOverflowPolicy(OverflowAction.OverwriteOlder, 400)); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void MachineName_Set() + { + string source = "Source_" + nameof(MachineName_Set); + using (EventLog eventLog = new EventLog()) + { + eventLog.Log = "Application"; + eventLog.MachineName = Environment.MachineName.ToLowerInvariant(); + try + { + EventLog.CreateEventSource(source, eventLog.LogDisplayName); + Assert.Equal(eventLog.MachineName, Environment.MachineName.ToLowerInvariant()); + Assert.True(EventLog.SourceExists(source, Environment.MachineName.ToLowerInvariant())); + } + finally + { + EventLog.DeleteEventSource(source); + } + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void RegisterDisplayLogName() + { + string log = "DisplayName"; + string source = "Source_" + nameof(RegisterDisplayLogName); + string messageFile = GetTestFilePath(); + long DisplayNameMsgId = 42; // It could be any number + EventSourceCreationData sourceData = new EventSourceCreationData(source, log); + + try + { + EventLog.CreateEventSource(sourceData); + log = EventLog.LogNameFromSourceName(source, "."); + using (EventLog eventLog = new EventLog(log, ".", source)) + { + if (messageFile.Length > 0) + { + eventLog.RegisterDisplayName(messageFile, DisplayNameMsgId); + } + Assert.Equal(log, eventLog.LogDisplayName); + } + } + finally + { + EventLog.DeleteEventSource(source); + Helpers.RetryOnWin7(() => EventLog.Delete(log)); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.SupportsEventLogs))] + public void InvalidFormatOrNullLogName() + { + Assert.Throws(() => new EventLog(null)); + Assert.Throws(() => new EventLog("?")); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.SupportsEventLogs))] + public void EventLog_EnableRaisingEvents_DefaultFalse() + { + using (EventLog eventLog = new EventLog("log")) + { + Assert.False(eventLog.EnableRaisingEvents); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void InvalidFormatOrNullDeleteLogName() + { + Assert.Throws(() => EventLog.Delete(null)); + Assert.Throws(() => EventLog.Delete("?")); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void InvalidLogExistsLogName() + { + Assert.False(EventLog.Exists(null)); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void InvalidMachineName() + { + Assert.Throws(() => EventLog.Exists("Application", "")); + Assert.Throws(() => EventLog.Delete("", "")); + Assert.Throws(() => EventLog.DeleteEventSource("", "")); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void LogDisplayNameDefault() + { + string source = "Source_" + nameof(LogDisplayNameDefault); + string log = "MyLogDisplay"; + try + { + EventLog.CreateEventSource(source, log); + using (EventLog eventlog = new EventLog()) + { + eventlog.Source = source; + Assert.Equal(log, eventlog.LogDisplayName); + } + } + finally + { + EventLog.DeleteEventSource(source); + Helpers.RetryOnWin7(() => EventLog.Delete(log)); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void GetMessageUsingEventMessageDLL() + { + if (CultureInfo.CurrentCulture.ToString() != "en-US") + { + return; + } + + using (EventLog eventlog = new EventLog("Security")) + { + eventlog.Source = "Security"; + EventLogEntry eventLogEntry; + eventLogEntry = Helpers.RetryOnWin7(() => eventlog.Entries[0]); + Assert.Contains("", eventLogEntry.Message); } } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Diagnostics.EventLog/tests/EventLogTests/EventLogWriteEntryTests.cs b/external/corefx/src/System.Diagnostics.EventLog/tests/EventLogTests/EventLogWriteEntryTests.cs new file mode 100644 index 0000000000..87da042b5d --- /dev/null +++ b/external/corefx/src/System.Diagnostics.EventLog/tests/EventLogTests/EventLogWriteEntryTests.cs @@ -0,0 +1,404 @@ +// 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.ComponentModel; +using Xunit; + +namespace System.Diagnostics.Tests +{ + public class EventLogWriteEntryTests + { + private const string message = "EventLogWriteEntryTestsMessage"; + + private readonly byte[] rawData = new byte[4] { 0, 1, 2, 3 }; + private readonly EventInstance eventInstance = new EventInstance(0, 1); + private readonly string[] insertStrings = { "ExtraText", "MoreText" }; + + private EventLogEntry WriteLogEntry(string source, bool type = false, bool instance = false, bool category = false, bool data = false) + { + using (EventLog eventLog = new EventLog()) + { + eventLog.Source = source; + if (instance) + { + Helpers.RetryOnWin7(() => EventLog.WriteEvent(source, eventInstance)); + if (data) + { + Helpers.RetryOnWin7(() => eventLog.WriteEntry(message, EventLogEntryType.Warning, (int)eventInstance.InstanceId, (short)eventInstance.CategoryId, rawData)); + return eventLog.Entries.LastOrDefault(); + } + else if (category) + { + Helpers.RetryOnWin7(() => eventLog.WriteEntry(message, EventLogEntryType.Warning, (int)eventInstance.InstanceId, (short)eventInstance.CategoryId)); + return eventLog.Entries.LastOrDefault(); + } + else + { + Helpers.RetryOnWin7(() => eventLog.WriteEntry(message, EventLogEntryType.Warning, (int)eventInstance.InstanceId)); + return eventLog.Entries.LastOrDefault(); + } + } + else if (type) + { + Helpers.RetryOnWin7(() => eventLog.WriteEntry(message, EventLogEntryType.Warning)); + } + else + { + Helpers.RetryOnWin7(() => eventLog.WriteEntry(message)); + } + + return eventLog.Entries.LastOrDefault(); + } + } + + private EventLogEntry WriteLogEntryWithSource(string source, bool type = false, bool instance = false, bool category = false, bool data = false) + { + using (EventLog eventLog = new EventLog()) + { + eventLog.Source = source; + if (instance) + { + Helpers.RetryOnWin7(() => EventLog.WriteEvent(source, eventInstance)); + if (data) + { + Helpers.RetryOnWin7(() => EventLog.WriteEntry(source, message, EventLogEntryType.Warning, (int)eventInstance.InstanceId, (short)eventInstance.CategoryId, rawData)); + return eventLog.Entries.LastOrDefault(); + } + else if (category) + { + Helpers.RetryOnWin7(() => EventLog.WriteEntry(source, message, EventLogEntryType.Warning, (int)eventInstance.InstanceId, (short)eventInstance.CategoryId)); + return eventLog.Entries.LastOrDefault(); + } + else + { + Helpers.RetryOnWin7(() => EventLog.WriteEntry(source, message, EventLogEntryType.Warning, (int)eventInstance.InstanceId)); + return eventLog.Entries.LastOrDefault(); + } + } + else if (type) + { + Helpers.RetryOnWin7(() => EventLog.WriteEntry(source, message, EventLogEntryType.Warning)); + } + else + { + Helpers.RetryOnWin7(() => EventLog.WriteEntry(source, message)); + } + + return eventLog.Entries.LastOrDefault(); + } + } + + private EventLogEntry WriteLogEntryEventSource(string source, bool data = false) + { + if (data) + { + Helpers.RetryOnWin7(() => EventLog.WriteEvent(source, eventInstance, rawData, insertStrings)); + } + else + { + Helpers.RetryOnWin7(() => EventLog.WriteEvent(source, eventInstance, insertStrings)); + } + using (EventLog eventLog = new EventLog()) + { + eventLog.Source = source; + return eventLog.Entries.LastOrDefault(); + } + } + + private EventLogEntry WriteLogEntryEvent(string source, bool data = false) + { + using (EventLog eventLog = new EventLog()) + { + string[] insertStringsSingleton = { "ExtraText" }; + eventLog.Source = source; + if (data) + Helpers.RetryOnWin7(() => eventLog.WriteEvent(eventInstance, rawData, insertStringsSingleton)); + else + Helpers.RetryOnWin7(() => eventLog.WriteEvent(eventInstance, insertStringsSingleton)); + + return eventLog.Entries.LastOrDefault(); + } + } + + [ConditionalTheory(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + [InlineData(false)] + [InlineData(true)] + public void WriteEntry(bool sourceFlag) + { + string log = "Entry"; + string source = "Source" + nameof(WriteEntry); + try + { + EventLog.CreateEventSource(source, log); + EventLogEntry eventLogEntry; + + if (sourceFlag) + eventLogEntry = WriteLogEntry(source); + else + eventLogEntry = WriteLogEntryWithSource(source); + + if (eventLogEntry != null) + { + Assert.Contains(message, eventLogEntry.Message); + Assert.Equal(source, eventLogEntry.Source); + Assert.StartsWith(Environment.MachineName.ToLowerInvariant(), eventLogEntry.MachineName.ToLowerInvariant()); + Assert.Equal(eventLogEntry.TimeWritten, eventLogEntry.TimeGenerated); + } + } + finally + { + EventLog.DeleteEventSource(source); + Helpers.RetryOnWin7(() => EventLog.Delete(log)); + } + } + + [ConditionalTheory(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + [InlineData(false)] + [InlineData(true)] + public void WriteEntryWithType(bool sourceFlag) + { + string source = "Source" + nameof(WriteEntryWithType); + string log = "TypeEntry"; + try + { + EventLog.CreateEventSource(source, log); + EventLogEntry eventLogEntry; + if (sourceFlag) + eventLogEntry = WriteLogEntry(source, type: true); + else + eventLogEntry = WriteLogEntryWithSource(source, type: true); + + if (eventLogEntry != null) + { + Assert.Contains(message, eventLogEntry.Message); + Assert.Equal(EventLogEntryType.Warning, eventLogEntry.EntryType); + } + } + finally + { + EventLog.DeleteEventSource(source); + Helpers.RetryOnWin7(() => EventLog.Delete(log)); + } + } + + [ConditionalTheory(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + [InlineData(false)] + [InlineData(true)] + public void WriteEntryWithTypeAndId(bool sourceFlag) + { + string source = "Source" + nameof(WriteEntryWithTypeAndId); + string log = "InstanceEntry"; + try + { + EventLog.CreateEventSource(source, log); + EventLogEntry eventLogEntry; + if (sourceFlag) + eventLogEntry = WriteLogEntry(source, type: true, instance: true); + else + eventLogEntry = WriteLogEntryWithSource(source, type: true, instance: true); + + if (eventLogEntry != null) + { + Assert.Contains(message, eventLogEntry.Message); + Assert.Equal((int)eventInstance.InstanceId, eventLogEntry.InstanceId); + } + } + finally + { + EventLog.DeleteEventSource(source); + Helpers.RetryOnWin7(() => EventLog.Delete(log)); + } + } + + [ConditionalTheory(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + [InlineData(false)] + [InlineData(true)] + public void WriteEntryWithTypeIdAndCategory(bool sourceFlag) + { + string source = "Source" + nameof(WriteEntryWithTypeIdAndCategory); + string log = "CategoryEntry"; + try + { + EventLog.CreateEventSource(source, log); + EventLogEntry eventLogEntry; + if (sourceFlag) + eventLogEntry = WriteLogEntry(source, type: true, instance: true, category: true); + else + eventLogEntry = WriteLogEntryWithSource(source, type: true, instance: true, category: true); + + // There is some prefix string already attached to the message passed + // The description for Event ID '0' in Source 'SourceWriteEntryWithTypeIDAndCategory' cannot be found. The local computer may not have the necessary registry information or message DLL files to display the message, or you may not have permission + // to access them. The following information is part of the event:'EventLogWriteEntryTestsMessage' + // The last part is the associated message + // The initial message is due in insufficient permission to access resource library EventLogMsgs.dll + if (eventLogEntry != null) + { + Assert.Contains(message, eventLogEntry.Message); + Assert.Equal((short)eventInstance.CategoryId, eventLogEntry.CategoryNumber); + Assert.Equal("(" + eventLogEntry.CategoryNumber + ")", eventLogEntry.Category); + } + } + finally + { + EventLog.DeleteEventSource(source); + Helpers.RetryOnWin7(() => EventLog.Delete(log)); + } + } + + [ConditionalTheory(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + [InlineData(false)] + [InlineData(true)] + public void WriteEntryWithTypeIdCategoryAndData(bool sourceFlag) + { + string source = "Source" + nameof(WriteEntryWithTypeIdCategoryAndData); + string log = "EntryData"; + try + { + EventLog.CreateEventSource(source, log); + EventLogEntry eventLogEntry; + if (sourceFlag) + eventLogEntry = WriteLogEntry(source, type: true, instance: true, category: true, data: true); + else + eventLogEntry = WriteLogEntryWithSource(source, type: true, instance: true, category: true, data: true); + + if (eventLogEntry != null) + { + Assert.Contains(message, eventLogEntry.Message); + Assert.Equal(rawData, eventLogEntry.Data); + } + } + finally + { + EventLog.DeleteEventSource(source); + Helpers.RetryOnWin7(() => EventLog.Delete(log)); + } + } + + [ConditionalTheory(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void WriteEntryWithoutSource() + { + using (EventLog eventLog = new EventLog()) + { + Assert.Throws(() => eventLog.WriteEntry(message)); + } + } + + [ConditionalTheory(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void WriteEntryWithInvalidType() + { + using (EventLog eventLog = new EventLog()) + { + string source = "Source_" + nameof(WriteEntryWithInvalidType); + eventLog.Source = source; + Assert.Throws(() => eventLog.WriteEntry(message, (EventLogEntryType)7)); // 7 is a random number which is not associated with any type in EventLogEntryType + } + } + + [ConditionalTheory(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void WriteEntryWithNullOrEmptySource() + { + Assert.Throws(() => EventLog.WriteEntry(null, message)); + Assert.Throws(() => EventLog.WriteEntry("", message)); + } + + [ConditionalTheory(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + [InlineData(false)] + [InlineData(true)] + public void WriteEvent(bool SourceFlag) + { + string source = "Source_" + nameof(WriteEvent); + string log = "Event"; + try + { + EventLog.CreateEventSource(source, log); + EventLogEntry eventLogEntry; + if (SourceFlag) + eventLogEntry = WriteLogEntryEventSource(source); + else + eventLogEntry = WriteLogEntryEvent(source); + + if (eventLogEntry != null) + Assert.All(insertStrings, message => eventLogEntry.Message.Contains(message)); + } + finally + { + EventLog.DeleteEventSource(source); + Helpers.RetryOnWin7(() => EventLog.Delete(log)); + } + } + + [ConditionalTheory(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + [InlineData(false)] + [InlineData(true)] + public void WriteEventWithData(bool SourceFlag) + { + string log = "EventData"; + string source = "Source_" + nameof(WriteEventWithData); + try + { + EventLog.CreateEventSource(source, log); + EventLogEntry eventLogEntry; + + if (SourceFlag) + eventLogEntry = WriteLogEntryEventSource(source, data: true); + else + eventLogEntry = WriteLogEntryEvent(source, data: true); + + if (eventLogEntry != null) + Assert.Equal(rawData, eventLogEntry.Data); + } + finally + { + EventLog.DeleteEventSource(source); + Helpers.RetryOnWin7(() => EventLog.Delete(log)); + } + } + + [ConditionalTheory(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void WriteEventInstanceNull() + { + string source = "Source_" + nameof(WriteEventInstanceNull); + Assert.Throws(() => Helpers.RetryOnWin7(() => EventLog.WriteEvent(source, null, insertStrings))); + } + + [ConditionalTheory(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void WriteEventMessageValues_OutOfRange() + { + string source = "Source_" + nameof(WriteEventMessageValues_OutOfRange); + string[] message = new string[1]; + message[0] = new string('c', 32767); + Assert.Throws(() => Helpers.RetryOnWin7(() => EventLog.WriteEvent(source, eventInstance, message))); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void WriteWithoutExistingSource() + { + string source = "Source_" + nameof(WriteWithoutExistingSource); + try + { + Helpers.RetryOnWin7(() => EventLog.WriteEvent(source, eventInstance, rawData, null)); + Assert.Equal("Application", EventLog.LogNameFromSourceName(source, ".")); + } + finally + { + EventLog.DeleteEventSource(source); + } + } + + [ConditionalTheory(typeof(Helpers), nameof(Helpers.IsElevatedAndSupportsEventLogs))] + public void SourceNameMaxLengthExceeded() + { + string source = new string('s', 254); + Assert.Throws(() => EventLog.WriteEntry(source, message)); + } + } + + internal static class EventLogEntryCollectionExtensions + { + internal static EventLogEntry LastOrDefault(this EventLogEntryCollection elec) + { + return Helpers.RetryOnWin7(() => elec.Count > 0 ? elec[elec.Count - 1] : null); + } + } +} diff --git a/external/corefx/src/System.Diagnostics.EventLog/tests/Helpers.cs b/external/corefx/src/System.Diagnostics.EventLog/tests/Helpers.cs new file mode 100644 index 0000000000..8bb36c0d46 --- /dev/null +++ b/external/corefx/src/System.Diagnostics.EventLog/tests/Helpers.cs @@ -0,0 +1,86 @@ +// 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.ComponentModel; +using System.Threading; +using Xunit; + +// Implementation is not robust with respect to concurrently writing and reading log +[assembly: CollectionBehavior(DisableTestParallelization = true)] + +namespace System.Diagnostics.Tests +{ + internal class Helpers + { + public static bool IsElevatedAndSupportsEventLogs { get => AdminHelpers.IsProcessElevated() && SupportsEventLogs; } + public static bool SupportsEventLogs { get => PlatformDetection.IsNotWindowsNanoServer; } + + public static void RetryOnWin7(Action func) + { + RetryOnWin7(() => { func(); return null; }); + } + + public static T RetryOnWin7(Func func) + { + if (!PlatformDetection.IsWindows7) + { + return func(); + } + + return RetryOnAllPlatforms(func); + // We are retrying on windows 7 because it throws win32exception while some operations like Writing,retrieving and Deleting log. + // So We just try to do the operation again in case of this exception + } + + public static T RetryOnAllPlatforms(Func func) + { + T entry = default(T); + int retries = 20; + while (retries > 0) + { + try + { + entry = func(); + retries = -1; + } + catch (Win32Exception) + { + Thread.Sleep(100); + retries--; + } + catch (ArgumentException) + { + Thread.Sleep(100); + retries--; + } + } + + Assert.NotEqual(0, retries); + return entry; + } + + public static void WaitForEventLog(EventLog eventLog, int entriesExpected) + { + int tries = 1; + Stopwatch stopwatch = Stopwatch.StartNew(); + while (RetryOnAllPlatforms((() => eventLog.Entries.Count)) < entriesExpected && tries <= 50) + { + if (tries == 50) + { + Thread.Sleep(30000); + } + else + { + Thread.Sleep(100 * (tries)); + } + tries++; + } + + if (stopwatch.ElapsedMilliseconds / 1000 >= 5) + Console.WriteLine($"{stopwatch.ElapsedMilliseconds / 1000 } seconds"); + + Assert.Equal(entriesExpected, RetryOnWin7((() => eventLog.Entries.Count))); + } + } +} diff --git a/external/corefx/src/System.Diagnostics.EventLog/tests/System.Diagnostics.EventLog.Tests.csproj b/external/corefx/src/System.Diagnostics.EventLog/tests/System.Diagnostics.EventLog.Tests.csproj index 4939f9241d..0277bded0f 100644 --- a/external/corefx/src/System.Diagnostics.EventLog/tests/System.Diagnostics.EventLog.Tests.csproj +++ b/external/corefx/src/System.Diagnostics.EventLog/tests/System.Diagnostics.EventLog.Tests.csproj @@ -9,8 +9,13 @@ {5B218883-369E-4C3D-8BD0-74843474DCBD} + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Diagnostics.FileVersionInfo/ref/System.Diagnostics.FileVersionInfo.csproj b/external/corefx/src/System.Diagnostics.FileVersionInfo/ref/System.Diagnostics.FileVersionInfo.csproj index 2a78ce99e3..b2507fe438 100644 --- a/external/corefx/src/System.Diagnostics.FileVersionInfo/ref/System.Diagnostics.FileVersionInfo.csproj +++ b/external/corefx/src/System.Diagnostics.FileVersionInfo/ref/System.Diagnostics.FileVersionInfo.csproj @@ -13,6 +13,7 @@ + \ No newline at end of file diff --git a/external/corefx/src/System.Diagnostics.FileVersionInfo/src/System.Diagnostics.FileVersionInfo.csproj b/external/corefx/src/System.Diagnostics.FileVersionInfo/src/System.Diagnostics.FileVersionInfo.csproj index 9d407936ba..8d94dbfa19 100644 --- a/external/corefx/src/System.Diagnostics.FileVersionInfo/src/System.Diagnostics.FileVersionInfo.csproj +++ b/external/corefx/src/System.Diagnostics.FileVersionInfo/src/System.Diagnostics.FileVersionInfo.csproj @@ -60,6 +60,7 @@ + diff --git a/external/corefx/src/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.cs b/external/corefx/src/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.cs index ed9237c324..3bbf07f612 100644 --- a/external/corefx/src/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.cs +++ b/external/corefx/src/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.cs @@ -265,6 +265,12 @@ namespace System.Diagnostics /// public static FileVersionInfo GetVersionInfo(string fileName) { + // Check if fileName is a full path. Relative paths can cause confusion if the local file has the .dll extension, + // as .dll search paths can take over & look for system .dll's in that case. + if (!Path.IsPathFullyQualified(fileName)) + { + fileName = Path.GetFullPath(fileName); + } // Check for the existence of the file. File.Exists returns false if Read permission is denied. if (!File.Exists(fileName)) { diff --git a/external/corefx/src/System.Diagnostics.FileVersionInfo/tests/System.Diagnostics.FileVersionInfo.TestAssembly/System.Diagnostics.FileVersionInfo.TestAssembly.csproj b/external/corefx/src/System.Diagnostics.FileVersionInfo/tests/System.Diagnostics.FileVersionInfo.TestAssembly/System.Diagnostics.FileVersionInfo.TestAssembly.csproj index 0b03e82d48..26b9c15159 100644 --- a/external/corefx/src/System.Diagnostics.FileVersionInfo/tests/System.Diagnostics.FileVersionInfo.TestAssembly/System.Diagnostics.FileVersionInfo.TestAssembly.csproj +++ b/external/corefx/src/System.Diagnostics.FileVersionInfo/tests/System.Diagnostics.FileVersionInfo.TestAssembly/System.Diagnostics.FileVersionInfo.TestAssembly.csproj @@ -9,7 +9,6 @@ {28EB14BE-3BC9-4543-ABA6-A932424DFBD0} false - diff --git a/external/corefx/src/System.Diagnostics.FileVersionInfo/tests/System.Diagnostics.FileVersionInfo.Tests/FileVersionInfoTest.cs b/external/corefx/src/System.Diagnostics.FileVersionInfo/tests/System.Diagnostics.FileVersionInfo.Tests/FileVersionInfoTest.cs index d273df3ad4..346eccef32 100644 --- a/external/corefx/src/System.Diagnostics.FileVersionInfo/tests/System.Diagnostics.FileVersionInfo.Tests/FileVersionInfoTest.cs +++ b/external/corefx/src/System.Diagnostics.FileVersionInfo/tests/System.Diagnostics.FileVersionInfo.Tests/FileVersionInfoTest.cs @@ -102,6 +102,26 @@ namespace System.Diagnostics.Tests FileVersionInfo.GetVersionInfo(Path.Combine(Directory.GetCurrentDirectory(), TestNotFoundFileName))); } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.Uap, "Don't want to create temp file in app container current directory")] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "NetFX throws ArgumentException in this case")] + public void FileVersionInfo_RelativePath_CorrectFilePath() + { + try + { + File.WriteAllText("kernelbase.dll", "bogus kernelbase.dll"); + FileVersionInfo fvi = FileVersionInfo.GetVersionInfo("kernelbase.dll"); + // File name should be the full path to the local kernelbase.dll, not the relative path or the path to the system .dll + Assert.Equal(Path.GetFullPath("kernelbase.dll"), fvi.FileName); + // FileDescription should be null in the local kernelbase.dll + Assert.Equal(null, fvi.FileDescription); + } + finally + { + File.Delete("kernelbase.dll"); + } + } + // Additional Tests Wanted: // [] File exists but we don't have permission to read it // [] DLL has unknown codepage info diff --git a/external/corefx/src/System.Diagnostics.FileVersionInfo/tests/System.Diagnostics.FileVersionInfo.Tests/System.Diagnostics.FileVersionInfo.Tests.csproj b/external/corefx/src/System.Diagnostics.FileVersionInfo/tests/System.Diagnostics.FileVersionInfo.Tests/System.Diagnostics.FileVersionInfo.Tests.csproj index 2e73b2ef8e..a87edd498b 100644 --- a/external/corefx/src/System.Diagnostics.FileVersionInfo/tests/System.Diagnostics.FileVersionInfo.Tests/System.Diagnostics.FileVersionInfo.Tests.csproj +++ b/external/corefx/src/System.Diagnostics.FileVersionInfo/tests/System.Diagnostics.FileVersionInfo.Tests/System.Diagnostics.FileVersionInfo.Tests.csproj @@ -5,7 +5,6 @@ System.Diagnostics.FileVersionInfo.Tests {6DFDB760-CC88-48AE-BD81-C64844EA3CBC} - @@ -48,7 +47,6 @@ - $(BuildDependsOn);MangleTestAssemblyExtension @@ -56,5 +54,4 @@ - - + \ No newline at end of file diff --git a/external/corefx/src/System.Diagnostics.PerformanceCounter/pkg/System.Diagnostics.PerformanceCounter.pkgproj b/external/corefx/src/System.Diagnostics.PerformanceCounter/pkg/System.Diagnostics.PerformanceCounter.pkgproj new file mode 100644 index 0000000000..ba710f6838 --- /dev/null +++ b/external/corefx/src/System.Diagnostics.PerformanceCounter/pkg/System.Diagnostics.PerformanceCounter.pkgproj @@ -0,0 +1,14 @@ + + + + + + net461;netcoreapp2.0;$(AllXamarinFrameworks) + + + + + + + + diff --git a/external/corefx/src/System.Diagnostics.PerformanceCounter/ref/System.Diagnostics.PerformanceCounter.cs b/external/corefx/src/System.Diagnostics.PerformanceCounter/ref/System.Diagnostics.PerformanceCounter.cs index 802cb179ff..df97fc942c 100644 --- a/external/corefx/src/System.Diagnostics.PerformanceCounter/ref/System.Diagnostics.PerformanceCounter.cs +++ b/external/corefx/src/System.Diagnostics.PerformanceCounter/ref/System.Diagnostics.PerformanceCounter.cs @@ -31,12 +31,12 @@ namespace System.Diagnostics protected override void OnValidate(object value) { } public virtual void Remove(System.Diagnostics.CounterCreationData value) { } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] - public partial struct CounterSample + public readonly partial struct CounterSample { + private readonly int _dummy; public static System.Diagnostics.CounterSample Empty; - public CounterSample(long rawValue, long baseValue, long counterFrequency, long systemFrequency, long timeStamp, long timeStamp100nSec, System.Diagnostics.PerformanceCounterType counterType) { throw null;} - public CounterSample(long rawValue, long baseValue, long counterFrequency, long systemFrequency, long timeStamp, long timeStamp100nSec, System.Diagnostics.PerformanceCounterType counterType, long counterTimeStamp) { throw null;} + public CounterSample(long rawValue, long baseValue, long counterFrequency, long systemFrequency, long timeStamp, long timeStamp100nSec, System.Diagnostics.PerformanceCounterType counterType) { throw null; } + public CounterSample(long rawValue, long baseValue, long counterFrequency, long systemFrequency, long timeStamp, long timeStamp100nSec, System.Diagnostics.PerformanceCounterType counterType, long counterTimeStamp) { throw null; } public long BaseValue { get { throw null; } } public long CounterFrequency { get { throw null; } } public long CounterTimeStamp { get { throw null; } } @@ -171,7 +171,7 @@ namespace System.Diagnostics [System.ObsoleteAttribute("This class has been deprecated. Use the PerformanceCounters through the System.Diagnostics.PerformanceCounter class instead. http://go.microsoft.com/fwlink/?linkid=14202")] void System.Diagnostics.ICollectData.CloseData() { } [System.ObsoleteAttribute("This class has been deprecated. Use the PerformanceCounters through the System.Diagnostics.PerformanceCounter class instead. http://go.microsoft.com/fwlink/?linkid=14202")] - void System.Diagnostics.ICollectData.CollectData(int callIdx, System.IntPtr valueNamePtr, System.IntPtr dataPtr, int totalBytes, out System.IntPtr res) { res = default(System.IntPtr); } + void System.Diagnostics.ICollectData.CollectData(int callIdx, System.IntPtr valueNamePtr, System.IntPtr dataPtr, int totalBytes, out System.IntPtr res) { throw null; } } public enum PerformanceCounterType { diff --git a/external/corefx/src/System.Diagnostics.PerformanceCounter/src/Configurations.props b/external/corefx/src/System.Diagnostics.PerformanceCounter/src/Configurations.props index 2155a36057..1a07e3dc7e 100644 --- a/external/corefx/src/System.Diagnostics.PerformanceCounter/src/Configurations.props +++ b/external/corefx/src/System.Diagnostics.PerformanceCounter/src/Configurations.props @@ -1,10 +1,14 @@  - - netcoreapp-Windows_NT; + + netcoreapp2.0-Windows_NT; netstandard; netfx; + + + $(PackageConfigurations) + netcoreapp-Windows_NT; diff --git a/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj b/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj index 59c23e2300..02ec8e70a2 100644 --- a/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj +++ b/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System.Diagnostics.PerformanceCounter.csproj @@ -9,16 +9,18 @@ $(DefineConstants);FEATURE_REGISTRY true true - SR.PlatformNotSupported_PerfCounters + SR.PlatformNotSupported_PerfCounters false + + - + @@ -168,11 +170,10 @@ - - + \ No newline at end of file diff --git a/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/CounterSample.cs b/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/CounterSample.cs index 3bffd3de1c..789167c5d0 100644 --- a/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/CounterSample.cs +++ b/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/CounterSample.cs @@ -7,7 +7,7 @@ namespace System.Diagnostics /// /// A struct holding the raw data for a performance counter. /// - public struct CounterSample + public readonly struct CounterSample { private readonly long _rawValue; private readonly long _baseValue; diff --git a/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/CounterSampleCalculator.cs b/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/CounterSampleCalculator.cs index e4a47a7448..6c7ad99781 100644 --- a/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/CounterSampleCalculator.cs +++ b/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/CounterSampleCalculator.cs @@ -5,7 +5,6 @@ using System.ComponentModel; using System.IO; using System.Runtime.InteropServices; -using System.Security.Permissions; using System.Globalization; namespace System.Diagnostics @@ -235,9 +234,8 @@ namespace System.Diagnostics if (s_perfCounterDllLoaded) return; - new FileIOPermission(PermissionState.Unrestricted).Assert(); - string installPath = SharedUtils.GetLatestBuildDllDirectory("."); + string perfcounterPath = Path.Combine(installPath, "perfcounter.dll"); if (Interop.Kernel32.LoadLibrary(perfcounterPath) == IntPtr.Zero) { diff --git a/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerformanceCounter.cs b/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerformanceCounter.cs index d1b784cfc3..1b148e6cde 100644 --- a/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerformanceCounter.cs +++ b/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerformanceCounter.cs @@ -165,8 +165,6 @@ namespace System.Diagnostics string currentCategoryName = _categoryName; string currentMachineName = _machineName; - PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Read, currentMachineName, currentCategoryName); - permission.Demand(); Initialize(); if (_helpMsg == null) @@ -212,9 +210,6 @@ namespace System.Diagnostics // This is the same thing that NextSample does, except that it doesn't try to get the actual counter // value. If we wanted the counter value, we would need to have an instance name. - PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Read, currentMachineName, currentCategoryName); - permission.Demand(); - Initialize(); CategorySample categorySample = PerformanceCounterLib.GetCategorySample(currentMachineName, currentCategoryName); CounterDefinitionSample counterSample = categorySample.GetCounterDefinitionSample(_counterName); @@ -369,8 +364,6 @@ namespace System.Diagnostics /// public static void CloseSharedResources() { - PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Read, ".", "*"); - permission.Demand(); PerformanceCounterLib.CloseAllLibraries(); } @@ -466,7 +459,6 @@ namespace System.Diagnostics private void InitializeImpl() { bool tookLock = false; - RuntimeHelpers.PrepareConstrainedRegions(); try { Monitor.Enter(InstanceLockObject, ref tookLock); @@ -483,10 +475,6 @@ namespace System.Diagnostics if (ReadOnly) { - PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Read, currentMachineName, currentCategoryName); - - permission.Demand(); - if (!PerformanceCounterLib.CounterExists(currentMachineName, currentCategoryName, _counterName)) throw new InvalidOperationException(SR.Format(SR.CounterExists, currentCategoryName, _counterName)); @@ -509,9 +497,6 @@ namespace System.Diagnostics } else { - PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Write, currentMachineName, currentCategoryName); - permission.Demand(); - if (currentMachineName != "." && !string.Equals(currentMachineName, PerformanceCounterLib.ComputerName, StringComparison.OrdinalIgnoreCase)) throw new InvalidOperationException(SR.Format(SR.RemoteWriting)); @@ -556,9 +541,6 @@ namespace System.Diagnostics string currentCategoryName = _categoryName; string currentMachineName = _machineName; - PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Read, currentMachineName, currentCategoryName); - permission.Demand(); - Initialize(); CategorySample categorySample = PerformanceCounterLib.GetCategorySample(currentMachineName, currentCategoryName); CounterDefinitionSample counterSample = categorySample.GetCounterDefinitionSample(_counterName); diff --git a/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerformanceCounterCategory.cs b/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerformanceCounterCategory.cs index 86b32217c2..2b42d2ceb4 100644 --- a/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerformanceCounterCategory.cs +++ b/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerformanceCounterCategory.cs @@ -53,9 +53,6 @@ namespace System.Diagnostics if (!SyntaxCheck.CheckMachineName(machineName)) throw new ArgumentException(SR.Format(SR.InvalidParameter, nameof(machineName), machineName), nameof(machineName)); - PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Read, machineName, categoryName); - permission.Demand(); - _categoryName = categoryName; _machineName = machineName; } @@ -82,9 +79,6 @@ namespace System.Diagnostics // checks depend on both pieces of info. lock (this) { - PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Read, _machineName, value); - permission.Demand(); - _categoryName = value; } } @@ -147,12 +141,6 @@ namespace System.Diagnostics // checks depend on both pieces of info. lock (this) { - if (_categoryName != null) - { - PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Read, value, _categoryName); - permission.Demand(); - } - _machineName = value; } } @@ -197,9 +185,6 @@ namespace System.Diagnostics if (!SyntaxCheck.CheckMachineName(machineName)) throw new ArgumentException(SR.Format(SR.InvalidParameter, nameof(machineName), machineName), nameof(machineName)); - PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Read, machineName, categoryName); - permission.Demand(); - return PerformanceCounterLib.CounterExists(machineName, categoryName, counterName); } @@ -243,11 +228,7 @@ namespace System.Diagnostics } string machineName = "."; - PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Administer, machineName, categoryName); - permission.Demand(); - Mutex mutex = null; - RuntimeHelpers.PrepareConstrainedRegions(); try { SharedUtils.EnterMutex(PerfMutexName, ref mutex); @@ -401,13 +382,9 @@ namespace System.Diagnostics CheckValidCategory(categoryName); string machineName = "."; - PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Administer, machineName, categoryName); - permission.Demand(); - categoryName = categoryName.ToLower(CultureInfo.InvariantCulture); Mutex mutex = null; - RuntimeHelpers.PrepareConstrainedRegions(); try { SharedUtils.EnterMutex(PerfMutexName, ref mutex); @@ -451,9 +428,6 @@ namespace System.Diagnostics if (!SyntaxCheck.CheckMachineName(machineName)) throw new ArgumentException(SR.Format(SR.InvalidParameter, nameof(machineName), machineName), nameof(machineName)); - PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Read, machineName, categoryName); - permission.Demand(); - if (PerformanceCounterLib.IsCustomCategory(machineName, categoryName)) return true; @@ -466,9 +440,6 @@ namespace System.Diagnostics /// internal static string[] GetCounterInstances(string categoryName, string machineName) { - PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Read, machineName, categoryName); - permission.Demand(); - CategorySample categorySample = PerformanceCounterLib.GetCategorySample(machineName, categoryName); if (categorySample._instanceNameTable.Count == 0) return Array.Empty(); @@ -530,9 +501,6 @@ namespace System.Diagnostics if (!SyntaxCheck.CheckMachineName(machineName)) throw new ArgumentException(SR.Format(SR.InvalidParameter, nameof(machineName), machineName), nameof(machineName)); - PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Read, machineName, "*"); - permission.Demand(); - string[] categoryNames = PerformanceCounterLib.GetCategories(machineName); PerformanceCounterCategory[] categories = new PerformanceCounterCategory[categoryNames.Length]; for (int index = 0; index < categories.Length; index++) diff --git a/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerformanceCounterLib.cs b/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerformanceCounterLib.cs index ec41fb9979..f9c6c51e6b 100644 --- a/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerformanceCounterLib.cs +++ b/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerformanceCounterLib.cs @@ -4,7 +4,6 @@ using System.Runtime.InteropServices; using System.Globalization; -using System.Security.Permissions; using System.Security; using System.Text; using System.Threading; @@ -210,19 +209,12 @@ namespace System.Diagnostics { if (s_iniFilePath == null) { - // Need to assert Environment permissions here - // the environment check is not exposed as a public - // method - EnvironmentPermission environmentPermission = new EnvironmentPermission(PermissionState.Unrestricted); - environmentPermission.Assert(); try { s_iniFilePath = Path.GetTempFileName(); } finally - { - EnvironmentPermission.RevertAssert(); - } + { } } } } @@ -261,24 +253,14 @@ namespace System.Diagnostics { string tempPath; - EnvironmentPermission environmentPermission = new EnvironmentPermission(PermissionState.Unrestricted); - environmentPermission.Assert(); tempPath = Path.GetTempPath(); - EnvironmentPermission.RevertAssert(); - // We need both FileIOPermission EvironmentPermission - PermissionSet ps = new PermissionSet(PermissionState.None); - ps.AddPermission(new EnvironmentPermission(PermissionState.Unrestricted)); - ps.AddPermission(new FileIOPermission(FileIOPermissionAccess.Write, tempPath)); - ps.Assert(); try { s_symbolFilePath = Path.GetTempFileName(); } finally - { - PermissionSet.RevertAssert(); - } + { } } } } @@ -406,9 +388,6 @@ namespace System.Diagnostics private static void CreateIniFile(string categoryName, string categoryHelp, CounterCreationDataCollection creationData, string[] languageIds) { - //SECREVIEW: PerformanceCounterPermission must have been demanded before - FileIOPermission permission = new FileIOPermission(PermissionState.Unrestricted); - permission.Assert(); try { StreamWriter iniWriter = new StreamWriter(IniFilePath, false, Encoding.Unicode); @@ -501,9 +480,7 @@ namespace System.Diagnostics } } finally - { - FileIOPermission.RevertAssert(); - } + { } } private static void CreateRegistryEntry(string categoryName, PerformanceCounterCategoryType categoryType, CounterCreationDataCollection creationData, ref bool iniRegistered) @@ -512,11 +489,6 @@ namespace System.Diagnostics RegistryKey serviceKey = null; RegistryKey linkageKey = null; - //SECREVIEW: Whoever is able to call this function, must already - // have demmanded PerformanceCounterPermission - // we can therefore assert the RegistryPermission. - RegistryPermission registryPermission = new RegistryPermission(PermissionState.Unrestricted); - registryPermission.Assert(); try { serviceParentKey = Registry.LocalMachine.OpenSubKey(ServicePath, true); @@ -565,16 +537,11 @@ namespace System.Diagnostics if (serviceParentKey != null) serviceParentKey.Close(); - - RegistryPermission.RevertAssert(); } } private static void CreateSymbolFile(CounterCreationDataCollection creationData) { - //SECREVIEW: PerformanceCounterPermission must have been demanded before - FileIOPermission permission = new FileIOPermission(PermissionState.Unrestricted); - permission.Assert(); try { StreamWriter symbolWriter = new StreamWriter(SymbolFilePath); @@ -604,20 +571,13 @@ namespace System.Diagnostics } } finally - { - FileIOPermission.RevertAssert(); - } + { } } private static void DeleteRegistryEntry(string categoryName) { RegistryKey serviceKey = null; - //SECREVIEW: Whoever is able to call this function, must already - // have demmanded PerformanceCounterPermission - // we can therefore assert the RegistryPermission. - RegistryPermission registryPermission = new RegistryPermission(PermissionState.Unrestricted); - registryPermission.Assert(); try { serviceKey = Registry.LocalMachine.OpenSubKey(ServicePath, true); @@ -646,8 +606,6 @@ namespace System.Diagnostics { if (serviceKey != null) serviceKey.Close(); - - RegistryPermission.RevertAssert(); } } @@ -757,7 +715,6 @@ namespace System.Diagnostics key.Close(); if (baseKey != null) baseKey.Close(); - PermissionSet.RevertAssert(); } } return false; @@ -991,19 +948,10 @@ namespace System.Diagnostics return help; } - internal string GetCounterName(int index) - { - if (NameTable.ContainsKey(index)) - return (string)NameTable[index]; - - return ""; - } - private static string[] GetLanguageIds() { RegistryKey libraryParentKey = null; string[] ids = Array.Empty(); - new RegistryPermission(PermissionState.Unrestricted).Assert(); try { libraryParentKey = Registry.LocalMachine.OpenSubKey(PerflibPath); @@ -1015,8 +963,6 @@ namespace System.Diagnostics { if (libraryParentKey != null) libraryParentKey.Close(); - - RegistryPermission.RevertAssert(); } return ids; @@ -1355,7 +1301,6 @@ namespace System.Diagnostics int error = 0; // no need to revert here since we'll fall off the end of the method - new RegistryPermission(PermissionState.Unrestricted).Assert(); while (waitRetries > 0) { try diff --git a/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PrivilegedConfigurationManager.cs b/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PrivilegedConfigurationManager.cs index 63e04f244b..8cad71ca62 100644 --- a/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PrivilegedConfigurationManager.cs +++ b/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PrivilegedConfigurationManager.cs @@ -9,9 +9,7 @@ namespace System.Configuration { using System.Collections.Specialized; using System.Security; - using System.Security.Permissions; - - [ConfigurationPermission(SecurityAction.Assert, Unrestricted=true)] + internal static class PrivilegedConfigurationManager { internal static ConnectionStringSettingsCollection ConnectionStrings { [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] diff --git a/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/SharedPerformanceCounter.cs b/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/SharedPerformanceCounter.cs index 54902ed3c3..6d1e25a1a0 100644 --- a/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/SharedPerformanceCounter.cs +++ b/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/SharedPerformanceCounter.cs @@ -8,7 +8,6 @@ using System.Threading; using System.Collections; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Security.Permissions; using Microsoft.Win32; using Microsoft.Win32.SafeHandles; @@ -533,7 +532,6 @@ namespace System.Diagnostics // but of course that's only a probabilisitic statement. // Must be able to assign to the out param. - RuntimeHelpers.PrepareConstrainedRegions(); try { } @@ -637,8 +635,6 @@ namespace System.Diagnostics data.FileMappingName = DefaultFileMappingName; data.MutexName = _categoryName; - RegistryPermission registryPermission = new RegistryPermission(PermissionState.Unrestricted); - registryPermission.Assert(); RegistryKey categoryKey = null; try { @@ -717,7 +713,6 @@ namespace System.Diagnostics { if (categoryKey != null) categoryKey.Close(); - RegistryPermission.RevertAssert(); } } } @@ -760,7 +755,6 @@ namespace System.Diagnostics Mutex mutex = null; CounterEntry* counterPointer = null; InstanceEntry* instancePointer = null; - RuntimeHelpers.PrepareConstrainedRegions(); try { SharedUtils.EnterMutexWithoutGlobal(_categoryData.MutexName, ref mutex); @@ -1068,7 +1062,6 @@ namespace System.Diagnostics if (activateUnusedInstances) { Mutex mutex = null; - RuntimeHelpers.PrepareConstrainedRegions(); try { SharedUtils.EnterMutexWithoutGlobal(_categoryData.MutexName, ref mutex); @@ -1211,7 +1204,6 @@ namespace System.Diagnostics return; Mutex mutex = null; - RuntimeHelpers.PrepareConstrainedRegions(); try { SharedUtils.EnterMutexWithoutGlobal(_categoryData.MutexName, ref mutex); @@ -1409,7 +1401,6 @@ namespace System.Diagnostics InstanceEntry* instancePointer = (InstanceEntry*)(ResolveOffset(categoryPointer->FirstInstanceOffset, s_instanceEntrySize)); Mutex mutex = null; - RuntimeHelpers.PrepareConstrainedRegions(); try { SharedUtils.EnterMutexWithoutGlobal(_categoryData.MutexName, ref mutex); @@ -1451,7 +1442,6 @@ namespace System.Diagnostics bool temp; Mutex mutex = null; - RuntimeHelpers.PrepareConstrainedRegions(); try { SharedUtils.EnterMutexWithoutGlobal(_categoryData.MutexName, ref mutex); @@ -1516,7 +1506,6 @@ namespace System.Diagnostics { bool sectionEntered = false; - RuntimeHelpers.PrepareConstrainedRegions(); try { if (!_categoryData.UseUniqueSharedMemory) diff --git a/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/SharedUtils.cs b/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/SharedUtils.cs index 96d4622669..9ddb161e04 100644 --- a/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/SharedUtils.cs +++ b/external/corefx/src/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/SharedUtils.cs @@ -2,7 +2,6 @@ // 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.Security.Permissions; using System.Threading; using System.Text; using Microsoft.Win32; @@ -113,7 +112,6 @@ namespace System.Diagnostics { bool ret; - RuntimeHelpers.PrepareConstrainedRegions(); try { } finally @@ -163,17 +161,6 @@ namespace System.Diagnostics RegistryKey baseKey = null; RegistryKey complusReg = null; - //This property is retrieved only when creationg a new category, - // the calling code already demanded PerformanceCounterPermission. - // Therefore the assert below is safe. - - //This property is retrieved only when creationg a new log, - // the calling code already demanded EventLogPermission. - // Therefore the assert below is safe. - - RegistryPermission registryPermission = new RegistryPermission(PermissionState.Unrestricted); - registryPermission.Assert(); - try { baseKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, machineName); @@ -298,8 +285,6 @@ namespace System.Diagnostics if (baseKey != null) baseKey.Close(); - - RegistryPermission.RevertAssert(); } return dllDir; diff --git a/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/CounterCreationDataCollectionTests.cs b/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/CounterCreationDataCollectionTests.cs new file mode 100644 index 0000000000..a03c2a3052 --- /dev/null +++ b/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/CounterCreationDataCollectionTests.cs @@ -0,0 +1,129 @@ +// 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.Collections; +using System.Collections.Specialized; +using Xunit; + +namespace System.Diagnostics.Tests +{ + [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // In appcontainer, cannot write to perf counters + public static class CounterCreationDataCollectionTests + { + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void CounterCreationDataCollection_CreateCounterCreationDataCollection_Empty() + { + CounterCreationDataCollection ccdc = new CounterCreationDataCollection(); + Assert.Equal(0, ccdc.Count); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void CounterCreationDataCollection_CreateCounterCreationDataCollection_CCDC() + { + CounterCreationData[] ccds = { new CounterCreationData("Simple1", "Simple Help", PerformanceCounterType.RawBase), new CounterCreationData("Simple2", "Simple Help", PerformanceCounterType.RawBase) }; + CounterCreationDataCollection ccdc1 = new CounterCreationDataCollection(ccds); + CounterCreationDataCollection ccdc2 = new CounterCreationDataCollection(ccdc1); + + Assert.Equal(2, ccdc2.Count); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void CounterCreationDataCollection_CreateCounterCreationDataCollection_Array() + { + CounterCreationData[] ccds = { new CounterCreationData("Simple1", "Simple Help", PerformanceCounterType.RawBase), new CounterCreationData("Simple2", "Simple Help", PerformanceCounterType.RawBase) }; + CounterCreationDataCollection ccdc = new CounterCreationDataCollection(ccds); + Assert.Equal(2, ccdc.Count); + Assert.True(ccdc.Contains(ccds[0])); + Assert.Equal(0, ccdc.IndexOf(ccds[0])); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void CounterCreationDataCollection_CreateCounterCreationDataCollection_Invalid() + { + CounterCreationData[] ccds = null; + CounterCreationDataCollection ccdc = null; + Assert.Throws(() => new CounterCreationDataCollection(ccds)); + Assert.Throws(() => new CounterCreationDataCollection(ccdc)); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void CounterCreationDataCollection_SetIndex2() + { + CounterCreationData[] ccds = { new CounterCreationData("Simple1", "Simple Help", PerformanceCounterType.RawBase), new CounterCreationData("Simple2", "Simple Help", PerformanceCounterType.RawBase) }; + CounterCreationDataCollection ccdc = new CounterCreationDataCollection(ccds); + + CounterCreationData ccd = new CounterCreationData("Simple3", "Simple Help", PerformanceCounterType.RawBase); + + ccdc[1] = ccd; + + Assert.Equal(ccd, ccdc[1]); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void CounterCreationDataCollection_Remove() + { + CounterCreationData[] ccds = { new CounterCreationData("Simple1", "Simple Help", PerformanceCounterType.RawBase), new CounterCreationData("Simple2", "Simple Help", PerformanceCounterType.RawBase) }; + CounterCreationDataCollection ccdc = new CounterCreationDataCollection(ccds); + + ccdc.Remove(ccds[0]); + Assert.False(ccdc.Contains(ccds[0])); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void CounterCreationDataCollection_Insert() + { + CounterCreationData[] ccds = { new CounterCreationData("Simple1", "Simple Help", PerformanceCounterType.RawBase), new CounterCreationData("Simple2", "Simple Help", PerformanceCounterType.RawBase) }; + CounterCreationDataCollection ccdc = new CounterCreationDataCollection(ccds); + + CounterCreationData ccd = new CounterCreationData("Simple3", "Simple Help", PerformanceCounterType.RawBase); + ccdc.Insert(1, ccd); + + Assert.True(ccdc.Contains(ccd)); + Assert.Equal(1, ccdc.IndexOf(ccd)); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void CounterCreationDataCollection_CopyTo() + { + CounterCreationData[] ccds = { new CounterCreationData("Simple1", "Simple Help", PerformanceCounterType.RawBase), new CounterCreationData("Simple2", "Simple Help", PerformanceCounterType.RawBase) }; + CounterCreationDataCollection ccdc = new CounterCreationDataCollection(ccds); + + CounterCreationData[] ccds2 = new CounterCreationData[2]; + + ccdc.CopyTo(ccds2, 0); + + Assert.Equal(ccdc[0], ccds2[0]); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_CreateCategory() + { + if (!PerformanceCounterCategory.Exists("AverageCounter64SampleCategory")) + { + CounterCreationDataCollection counterDataCollection = new CounterCreationDataCollection(); + + // Add the counter. + CounterCreationData averageCount64 = new CounterCreationData(); + averageCount64.CounterType = PerformanceCounterType.AverageCount64; + averageCount64.CounterName = "AverageCounter64Sample"; + counterDataCollection.Add(averageCount64); + + // Add the base counter. + CounterCreationData averageCount64Base = new CounterCreationData(); + averageCount64Base.CounterType = PerformanceCounterType.AverageBase; + averageCount64Base.CounterName = "AverageCounter64SampleBase"; + counterDataCollection.Add(averageCount64Base); + + // Create the category. + PerformanceCounterCategory.Create("AverageCounter64SampleCategory", + "Demonstrates usage of the AverageCounter64 performance counter type.", + PerformanceCounterCategoryType.SingleInstance, counterDataCollection); + } + + Assert.True(PerformanceCounterCategory.Exists("AverageCounter64SampleCategory")); + PerformanceCounterCategory.Delete("AverageCounter64SampleCategory"); + } + } +} diff --git a/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/CounterCreationDataTests.cs b/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/CounterCreationDataTests.cs new file mode 100644 index 0000000000..272e9d6b0d --- /dev/null +++ b/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/CounterCreationDataTests.cs @@ -0,0 +1,33 @@ +// 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.Collections; +using System.Collections.Specialized; +using System.ComponentModel; +using Xunit; + +namespace System.Diagnostics.Tests +{ + [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // In appcontainer, cannot write to perf counters + public static class CounterCreationDataTests + { + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void CounterCreationData_CreateCounterCreationData_SimpleSimpleHelpRawBase() + { + CounterCreationData ccd = new CounterCreationData("Simple", "Simple Help", PerformanceCounterType.RawBase); + + Assert.Equal("Simple", ccd.CounterName); + Assert.Equal("Simple Help", ccd.CounterHelp); + Assert.Equal(PerformanceCounterType.RawBase, ccd.CounterType); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void CounterCreationData_SetCounterType_Invalud() + { + CounterCreationData ccd = new CounterCreationData("Simple", "Simple Help", PerformanceCounterType.RawBase); + Assert.Throws(() => ccd.CounterType = (PerformanceCounterType)Int32.MaxValue); + } + } +} diff --git a/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/CounterSampleCalculatorTests.cs b/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/CounterSampleCalculatorTests.cs new file mode 100644 index 0000000000..2f27b4530a --- /dev/null +++ b/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/CounterSampleCalculatorTests.cs @@ -0,0 +1,53 @@ +// 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.Collections; +using System.Collections.Specialized; +using Xunit; + +namespace System.Diagnostics.Tests +{ + [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // In appcontainer, cannot write to perf counters + public static class CounterSampleCalculatorTests + { + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void CounterSampleCalculator_ElapsedTime() + { + var name = nameof(CounterSampleCalculator_ElapsedTime) + "_Counter"; + + PerformanceCounter counterSample = CreateCounter(name, PerformanceCounterType.ElapsedTime); + + counterSample.RawValue = Stopwatch.GetTimestamp(); + DateTime Start = DateTime.Now; + Helpers.RetryOnAllPlatforms(() => counterSample.NextValue()); + + System.Threading.Thread.Sleep(500); + + var counterVal = Helpers.RetryOnAllPlatforms(() => counterSample.NextValue()); + var dateTimeVal = DateTime.Now.Subtract(Start).TotalSeconds; + Helpers.DeleteCategory(name); + Assert.True(Math.Abs(dateTimeVal - counterVal) < .3); + } + + public static PerformanceCounter CreateCounter(string name, PerformanceCounterType counterType) + { + var category = name + "_Category"; + var instance = name + "_Instance"; + + CounterCreationDataCollection ccdc = new CounterCreationDataCollection(); + CounterCreationData ccd = new CounterCreationData(); + ccd.CounterType = counterType; + ccd.CounterName = name; + ccdc.Add(ccd); + + Helpers.DeleteCategory(name); + PerformanceCounterCategory.Create(category, "description", PerformanceCounterCategoryType.SingleInstance, ccdc); + + Assert.True(Helpers.PerformanceCounterCategoryCreated(category)); + + return new PerformanceCounter(category, name, false); + } + } +} diff --git a/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/CounterSampleTests.cs b/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/CounterSampleTests.cs new file mode 100644 index 0000000000..803e348226 --- /dev/null +++ b/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/CounterSampleTests.cs @@ -0,0 +1,97 @@ +// 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.Collections; +using System.Collections.Specialized; +using Xunit; + +namespace System.Diagnostics.Tests +{ + [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // In appcontainer, cannot write to perf counters + public static class CounterSampleTests + { + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void CounterSample_Constructor_EmptyCounterSample() + { + CounterSample counterSample = new CounterSample(); + + Assert.Equal(0, counterSample.BaseValue); + Assert.Equal(0, counterSample.CounterFrequency); + Assert.Equal(PerformanceCounterType.NumberOfItemsHEX32, counterSample.CounterType); + Assert.Equal(0, counterSample.RawValue); + Assert.Equal(0, counterSample.SystemFrequency); + Assert.Equal(0, counterSample.TimeStamp); + Assert.Equal(0, counterSample.TimeStamp100nSec); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void CounterSample_Constructor_CounterSample() + { + long timeStamp = DateTime.Now.ToFileTime(); + CounterSample counterSample = new CounterSample(1, 2, 3, 4, timeStamp, timeStamp, PerformanceCounterType.SampleFraction); + + Assert.Equal(2, counterSample.BaseValue); + Assert.Equal(3, counterSample.CounterFrequency); + Assert.Equal(PerformanceCounterType.SampleFraction, counterSample.CounterType); + Assert.Equal(1, counterSample.RawValue); + Assert.Equal(4, counterSample.SystemFrequency); + Assert.Equal(timeStamp, counterSample.TimeStamp); + Assert.Equal(timeStamp, counterSample.TimeStamp100nSec); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void CounterSample_Calculate_CalculateCounterSample() + { + CounterSample counterSample = new CounterSample(5, 0, 0, 0, 0, 0, PerformanceCounterType.NumberOfItems32); + + Assert.Equal(5, CounterSample.Calculate(counterSample)); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void CounterSample_Calculate_CalculateCounterSampleCounterSample() + { + CounterSample counterSample1 = new CounterSample(5, 0, 0, 1, 0, 0, PerformanceCounterType.CounterDelta32); + CounterSample counterSample2 = new CounterSample(15, 0, 0, 1, 0, 0, PerformanceCounterType.CounterDelta32); + + Assert.Equal(10, CounterSample.Calculate(counterSample1, counterSample2)); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void CounterSample_Equal() + { + CounterSample counterSample1 = new CounterSample(5, 0, 0, 1, 0, 0, PerformanceCounterType.CounterDelta32); + CounterSample counterSample2 = new CounterSample(5, 0, 0, 1, 0, 0, PerformanceCounterType.CounterDelta32); + + Assert.Equal(counterSample1, counterSample2); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void CounterSample_opInequality() + { + CounterSample counterSample1 = new CounterSample(5, 0, 0, 1, 0, 0, PerformanceCounterType.CounterDelta32); + CounterSample counterSample2 = new CounterSample(15, 0, 0, 1, 0, 0, PerformanceCounterType.CounterDelta32); + + Assert.True(counterSample1 != counterSample2); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void CounterSample_opEquality() + { + CounterSample counterSample1 = new CounterSample(5, 0, 0, 1, 0, 0, PerformanceCounterType.CounterDelta32); + CounterSample counterSample2 = new CounterSample(5, 0, 0, 1, 0, 0, PerformanceCounterType.CounterDelta32); + + Assert.True(counterSample1 == counterSample2); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void CounterSample_GetHashCode() + { + CounterSample counterSample1 = new CounterSample(5, 0, 0, 1, 0, 0, PerformanceCounterType.CounterDelta32); + CounterSample counterSample2 = new CounterSample(5, 0, 0, 1, 0, 0, PerformanceCounterType.CounterDelta32); + + Assert.Equal(counterSample1.GetHashCode(), counterSample2.GetHashCode()); + } + } +} diff --git a/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/Helpers.cs b/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/Helpers.cs new file mode 100644 index 0000000000..51310d82bf --- /dev/null +++ b/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/Helpers.cs @@ -0,0 +1,79 @@ +// 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.Threading; +using Xunit; + +// Implementation is not robust with respect to modifying counter categories +// while concurrently reading counters +[assembly: CollectionBehavior(DisableTestParallelization = true)] + +namespace System.Diagnostics.Tests +{ + internal class Helpers + { + public static bool IsElevatedAndCanWriteToPerfCounters { get => AdminHelpers.IsProcessElevated() && CanWriteToPerfCounters; } + public static bool CanWriteToPerfCounters { get => PlatformDetection.IsNotWindowsNanoServer; } + + public static string CreateCategory(string name, PerformanceCounterCategoryType categoryType) + { + var category = name + "_Category"; + + // If the categry already exists, delete it, then create it. + DeleteCategory(name); + PerformanceCounterCategory.Create(category, "description", categoryType, name, "counter description"); + + Assert.True(PerformanceCounterCategoryCreated(category)); + return category; + } + + public static bool PerformanceCounterCategoryCreated(string category) + { + int tries = 0; + while (!PerformanceCounterCategory.Exists(category) && tries < 10) + { + System.Threading.Thread.Sleep(100); + tries++; + } + + return PerformanceCounterCategory.Exists(category); + } + + public static void DeleteCategory(string name) + { + var category = name + "_Category"; + if (PerformanceCounterCategory.Exists(category)) + { + PerformanceCounterCategory.Delete(category); + } + } + + public static T RetryOnAllPlatforms(Func func) + { + T entry = default(T); + int retries = 20; + while (retries > 0) + { + try + { + entry = func(); + retries = -1; + } + catch (InvalidOperationException) + { + Thread.Sleep(100); + retries--; + } + catch (ArgumentException) + { + Thread.Sleep(100); + retries--; + } + } + + Assert.NotEqual(0, retries); + return entry; + } + } +} + diff --git a/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/InstanceDataTests.cs b/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/InstanceDataTests.cs new file mode 100644 index 0000000000..3cc52fc328 --- /dev/null +++ b/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/InstanceDataTests.cs @@ -0,0 +1,139 @@ +// 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.Collections; +using System.Collections.Specialized; +using Xunit; + +namespace System.Diagnostics.Tests +{ + [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // In appcontainer, cannot write to perf counters + public static class InstanceDataTests + { + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void InstanceData_CreateInstanceData_FromCounterSample() + { + long timestamp = DateTime.Now.ToFileTime(); + CounterSample cs = new CounterSample(1, 2, 3, 4, timestamp, timestamp, PerformanceCounterType.SampleFraction); + + InstanceData id = new InstanceData("foo", cs); + Assert.Equal(cs.RawValue, id.Sample.RawValue); + Assert.Equal(cs.CounterType, id.Sample.CounterType); + Assert.Equal(1, id.RawValue); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void InstanceDataCollection_GetItem_ExistingCounter() + { + InstanceDataCollection idc = GetInstanceDataCollection(); + + InstanceData[] ids = new InstanceData[idc.Count]; + idc.CopyTo(ids, 0); + + Assert.Equal("% User Time", idc.CounterName); + + for (int i = 0; i < idc.Count; i++) + { + string instanceName = ids[i].InstanceName; + Assert.Equal(instanceName, idc[instanceName].InstanceName); + Assert.Equal(ids[i].RawValue, idc[instanceName].RawValue); + Assert.True(idc.Contains(instanceName)); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void InstanceDataCollection_NullTest() + { + InstanceDataCollection idc = GetInstanceDataCollection(); + + Assert.Throws(() => idc[null]); + Assert.Throws(() => idc.Contains(null)); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void InstanceDataCollection_GetKeys() + { + InstanceDataCollection idc = GetInstanceDataCollection(); + + string[] keys = new string[idc.Count]; + idc.Keys.CopyTo(keys, 0); + + Assert.True(keys.Length > 0); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void InstanceDataCollection_GetValues() + { + InstanceDataCollection idc = GetInstanceDataCollection(); + + InstanceData[] values = new InstanceData[idc.Count]; + idc.Values.CopyTo(values, 0); + + Assert.True(values.Length > 0); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void InstanceDataCollectionCollection_GetItem_Invalid() + { + InstanceDataCollectionCollection idcc = GetInstanceDataCollectionCollection(); + + Assert.Throws(() => idcc[null]); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void InstanceDataCollectionCollection_GetKeys() + { + InstanceDataCollectionCollection idcc = GetInstanceDataCollectionCollection(); + + Assert.True(idcc.Keys.Count > 0); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void InstanceDataCollectionCollection_GetValues() + { + InstanceDataCollectionCollection idcc = GetInstanceDataCollectionCollection(); + + Assert.True(idcc.Values.Count > 0); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void InstanceDataCollectionCollection_Contains_Valid() + { + InstanceDataCollectionCollection idcc = GetInstanceDataCollectionCollection(); + + Assert.False(idcc.Contains("Not a real instance")); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void InstanceDataCollectionCollection_Contains_inValid() + { + InstanceDataCollectionCollection idcc = GetInstanceDataCollectionCollection(); + + Assert.Throws(() => idcc.Contains(null)); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void InstanceDataCollectionCollection_CopyTo() + { + InstanceDataCollectionCollection idcc = GetInstanceDataCollectionCollection(); + + InstanceDataCollection[] idc = new InstanceDataCollection[idcc.Values.Count]; + idcc.CopyTo(idc, 0); + Assert.True(idc.Length > 0); + } + + public static InstanceDataCollectionCollection GetInstanceDataCollectionCollection() + { + PerformanceCounterCategory pcc = Helpers.RetryOnAllPlatforms(() => new PerformanceCounterCategory("Processor")); + return Helpers.RetryOnAllPlatforms(() => pcc.ReadCategory()); + } + + public static InstanceDataCollection GetInstanceDataCollection() + { + InstanceDataCollectionCollection idcc = GetInstanceDataCollectionCollection(); + return idcc["% User Time"]; + } + } +} diff --git a/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/PerformanceCounterCategoryTests.cs b/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/PerformanceCounterCategoryTests.cs new file mode 100644 index 0000000000..bc50fa079e --- /dev/null +++ b/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/PerformanceCounterCategoryTests.cs @@ -0,0 +1,312 @@ +// 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.Collections; +using System.Collections.Specialized; +using Xunit; + +namespace System.Diagnostics.Tests +{ + [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // In appcontainer, cannot write to perf counters + public static class PerformanceCounterCategoryTests + { + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_CreatePerformanceCounterCategory_DefaultConstructor() + { + PerformanceCounterCategory pcc = new PerformanceCounterCategory(); + Assert.Equal(".", pcc.MachineName); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_CreatePerformanceCounterCategory_NullTests() + { + Assert.Throws(() => new PerformanceCounterCategory(null, ".")); + Assert.Throws(() => new PerformanceCounterCategory(string.Empty, ".")); + Assert.Throws(() => new PerformanceCounterCategory("category", string.Empty)); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_SetCategoryName_Valid() + { + PerformanceCounterCategory pcc = new PerformanceCounterCategory(); + pcc.CategoryName = "Processor"; + Assert.Equal("Processor", pcc.CategoryName); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_SetCategoryName_Invalid() + { + PerformanceCounterCategory pcc = new PerformanceCounterCategory(); + + Assert.Throws(() => pcc.CategoryName = null); + Assert.Throws(() => pcc.CategoryName = string.Empty); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_SetMachineName_Invalid() + { + PerformanceCounterCategory pcc = new PerformanceCounterCategory(); + + Assert.Throws(() => pcc.MachineName = string.Empty); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_SetMachineName_ValidCategoryNameNull() + { + PerformanceCounterCategory pcc = new PerformanceCounterCategory(); + + pcc.MachineName = "machineName"; + Assert.Equal("machineName", pcc.MachineName); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_SetMachineName_ValidCategoryNameNotNull() + { + PerformanceCounterCategory pcc = new PerformanceCounterCategory(); + + pcc.CategoryName = "Processor"; + pcc.MachineName = "machineName"; + Assert.Equal("machineName", pcc.MachineName); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_GetCounterHelp_Invalid() + { + PerformanceCounterCategory pcc = new PerformanceCounterCategory(); + + Assert.Throws(() => pcc.CategoryHelp); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_CategoryType_MultiInstance() + { + var name = nameof(PerformanceCounterCategory_CategoryType_MultiInstance) + "_Counter"; + + var category = Helpers.CreateCategory(name, PerformanceCounterCategoryType.MultiInstance); + + PerformanceCounterCategory pcc = Helpers.RetryOnAllPlatforms(() => new PerformanceCounterCategory(category)); + + Assert.Equal(PerformanceCounterCategoryType.MultiInstance, Helpers.RetryOnAllPlatforms(() => pcc.CategoryType)); + PerformanceCounterCategory.Delete(category); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_CategoryType_SingleInstance() + { + var name = nameof(PerformanceCounterCategory_CategoryType_SingleInstance) + "_Counter"; + + var category = Helpers.CreateCategory(name, PerformanceCounterCategoryType.SingleInstance); + + PerformanceCounterCategory pcc = Helpers.RetryOnAllPlatforms(() => new PerformanceCounterCategory(category)); + + Assert.Equal(PerformanceCounterCategoryType.SingleInstance, Helpers.RetryOnAllPlatforms(() => pcc.CategoryType)); + PerformanceCounterCategory.Delete(category); + } + +#pragma warning disable 0618 // obsolete warning + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_Create_Obsolete() + { + var name = nameof(PerformanceCounterCategory_Create_Obsolete) + "_Counter"; + var category = name + "_Category"; + + Helpers.DeleteCategory(category); + + PerformanceCounterCategory.Create(category, "category help", name, "counter help"); + + Assert.True(PerformanceCounterCategory.Exists(category)); + PerformanceCounterCategory.Delete(category); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_Create_Obsolete_CCD() + { + var name = nameof(PerformanceCounterCategory_Create_Obsolete_CCD) + "_Counter"; + var category = name + "_Category"; + + CounterCreationData ccd = new CounterCreationData(name, "counter help", PerformanceCounterType.NumberOfItems32); + CounterCreationDataCollection ccdc = new CounterCreationDataCollection(); + ccdc.Add(ccd); + + Helpers.DeleteCategory(category); + + PerformanceCounterCategory.Create(category, "category help", ccdc); + + Assert.True(PerformanceCounterCategory.Exists(category)); + PerformanceCounterCategory.Delete(category); + } +#pragma warning restore 0618 + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_Create_Invalid() + { + Assert.Throws(() => PerformanceCounterCategory.Create(null, "Categoryhelp", PerformanceCounterCategoryType.SingleInstance, "counter name", "counter help")); + Assert.Throws(() => PerformanceCounterCategory.Create("category name", "Categoryhelp", PerformanceCounterCategoryType.SingleInstance, null, "counter help")); + Assert.Throws(() => PerformanceCounterCategory.Create("category name", "Category help", PerformanceCounterCategoryType.SingleInstance, null)); + Assert.Throws(() => PerformanceCounterCategory.Create("Processor", "Category help", PerformanceCounterCategoryType.MultiInstance, "Interrupts/sec", "counter help")); + + string maxCounter = new string('a', 32769); + + Assert.Throws(() => PerformanceCounterCategory.Create("Category name", "Category help", PerformanceCounterCategoryType.SingleInstance, maxCounter, "counter help")); + Assert.Throws(() => PerformanceCounterCategory.Create(maxCounter, "Category help", PerformanceCounterCategoryType.SingleInstance, "Counter name", "counter help")); + Assert.Throws(() => PerformanceCounterCategory.Create("Category name", maxCounter, PerformanceCounterCategoryType.SingleInstance, "Counter name", "counter help")); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_GetCategories() + { + PerformanceCounterCategory[] categories = PerformanceCounterCategory.GetCategories(); + + Assert.True(categories.Length > 0); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_GetCategories_StaticInvalid() + { + Assert.Throws(() => PerformanceCounterCategory.GetCategories(string.Empty)); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_CounterExists_InterruptsPerSec() + { + PerformanceCounterCategory pcc = Helpers.RetryOnAllPlatforms(() => new PerformanceCounterCategory("Processor")); + + Assert.True(pcc.CounterExists("Interrupts/sec")); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_CounterExists_Invalid() + { + PerformanceCounterCategory pcc = new PerformanceCounterCategory(); + + Assert.Throws(() => pcc.CounterExists(null)); + Assert.Throws(() => pcc.CounterExists("Interrupts/sec")); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_CounterExists_StaticInterruptsPerSec() + { + Assert.True(PerformanceCounterCategory.CounterExists("Interrupts/sec", "Processor")); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_CounterExists_StaticInvalid() + { + Assert.Throws(() => PerformanceCounterCategory.CounterExists(null, "Processor")); + Assert.Throws(() => PerformanceCounterCategory.CounterExists("Interrupts/sec", null)); + Assert.Throws(() => PerformanceCounterCategory.CounterExists("Interrupts/sec", string.Empty)); + Assert.Throws(() => PerformanceCounterCategory.CounterExists("Interrupts/sec", "Processor", string.Empty)); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_DeleteCategory_Invalid() + { + Assert.Throws(() => PerformanceCounterCategory.Delete("Processor")); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_DeleteCategory() + { + var name = nameof(PerformanceCounterCategory_DeleteCategory) + "_Counter"; + var category = Helpers.CreateCategory(name, PerformanceCounterCategoryType.SingleInstance); + + PerformanceCounterCategory.Delete(category); + + Assert.False(PerformanceCounterCategory.Exists(category)); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_Exists_Invalid() + { + Assert.Throws(() => PerformanceCounterCategory.Exists(null, ".")); + Assert.Throws(() => PerformanceCounterCategory.Exists(string.Empty, ".")); + Assert.Throws(() => PerformanceCounterCategory.Exists("Processor", string.Empty)); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_GetCounters() + { + var name = nameof(PerformanceCounterCategory_GetCounters) + "_Counter"; + var category = Helpers.CreateCategory(name, PerformanceCounterCategoryType.SingleInstance); + + PerformanceCounterCategory pcc = Helpers.RetryOnAllPlatforms(() => new PerformanceCounterCategory(category)); + PerformanceCounter[] counters = pcc.GetCounters(); + + Assert.True(counters.Length > 0); + PerformanceCounterCategory.Delete(category); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_GetCounters_Invalid() + { + PerformanceCounterCategory pcc = new PerformanceCounterCategory(); + + Assert.Throws(() => pcc.GetCounters(null)); + Assert.Throws(() => pcc.GetCounters(string.Empty)); + + pcc.CategoryName = "Processor"; + + Assert.Throws(() => pcc.GetCounters("Not An Instance")); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_GetInstanceNames_Invalid() + { + PerformanceCounterCategory pcc = new PerformanceCounterCategory(); + + Assert.Throws(() => pcc.GetInstanceNames()); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_InstanceExists_Invalid() + { + PerformanceCounterCategory pcc = new PerformanceCounterCategory(); + + Assert.Throws(() => pcc.InstanceExists(null)); + Assert.Throws(() => pcc.InstanceExists("")); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_InstanceExists_Static() + { + PerformanceCounterCategory pcc = Helpers.RetryOnAllPlatforms(() => new PerformanceCounterCategory("Processor")); + + string[] instances = pcc.GetInstanceNames(); + Assert.True(instances.Length > 0); + + foreach (string instance in instances) + { + Assert.True(PerformanceCounterCategory.InstanceExists(instance, "Processor")); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_InstanceExists_StaticInvalid() + { + Assert.Throws(() => PerformanceCounterCategory.InstanceExists(null, "Processor", ".")); + Assert.Throws(() => PerformanceCounterCategory.InstanceExists("", null, ".")); + Assert.Throws(() => PerformanceCounterCategory.InstanceExists("", string.Empty, ".")); + Assert.Throws(() => PerformanceCounterCategory.InstanceExists("", "Processor", string.Empty)); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_ReadCategory() + { + PerformanceCounterCategory pcc = Helpers.RetryOnAllPlatforms(() => new PerformanceCounterCategory("Processor")); + + InstanceDataCollectionCollection idColCol = pcc.ReadCategory(); + + Assert.NotNull(idColCol); + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounterCategory_ReadCategory_Invalid() + { + PerformanceCounterCategory pcc = new PerformanceCounterCategory(); + + Assert.Throws(() => pcc.ReadCategory()); + } + } +} diff --git a/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/PerformanceCounterTests.cs b/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/PerformanceCounterTests.cs index 1f7fcfc72f..53c7087540 100644 --- a/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/PerformanceCounterTests.cs +++ b/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/PerformanceCounterTests.cs @@ -12,67 +12,290 @@ namespace System.Diagnostics.Tests [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // In appcontainer, cannot write to perf counters public static class PerformanceCounterTests { - [ConditionalFact(typeof(AdminHelpers), nameof(AdminHelpers.IsProcessElevated))] - public static void PerformanceCounterCategory_CreateCategory() + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounter_CreateCounter_EmptyCounter() { - if ( !PerformanceCounterCategory.Exists("AverageCounter64SampleCategory") ) + using (PerformanceCounter counterSample = new PerformanceCounter()) { - CounterCreationDataCollection counterDataCollection = new CounterCreationDataCollection(); - - // Add the counter. - CounterCreationData averageCount64 = new CounterCreationData(); - averageCount64.CounterType = PerformanceCounterType.AverageCount64; - averageCount64.CounterName = "AverageCounter64Sample"; - counterDataCollection.Add(averageCount64); - - // Add the base counter. - CounterCreationData averageCount64Base = new CounterCreationData(); - averageCount64Base.CounterType = PerformanceCounterType.AverageBase; - averageCount64Base.CounterName = "AverageCounter64SampleBase"; - counterDataCollection.Add(averageCount64Base); - - // Create the category. - PerformanceCounterCategory.Create("AverageCounter64SampleCategory", - "Demonstrates usage of the AverageCounter64 performance counter type.", - PerformanceCounterCategoryType.SingleInstance, counterDataCollection); + Assert.Equal(".", counterSample.MachineName); + Assert.Equal(string.Empty, counterSample.CategoryName); + Assert.Equal(string.Empty, counterSample.CounterName); + Assert.Equal(string.Empty, counterSample.InstanceName); + Assert.True(counterSample.ReadOnly); } - - Assert.True(PerformanceCounterCategory.Exists("AverageCounter64SampleCategory")); } - [ConditionalFact(typeof(AdminHelpers), nameof(AdminHelpers.IsProcessElevated))] + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] public static void PerformanceCounter_CreateCounter_Count0() { - var name = Guid.NewGuid().ToString("N") + "_Counter"; - var category = name + "_Category"; - if ( !PerformanceCounterCategory.Exists(category) ) + var name = nameof(PerformanceCounter_CreateCounter_Count0) + "_Counter"; + using (PerformanceCounter counterSample = CreateCounterWithCategory(name, false, PerformanceCounterCategoryType.SingleInstance)) { - CounterCreationDataCollection counterDataCollection = new CounterCreationDataCollection(); + counterSample.RawValue = 0; - // Add the counter. - CounterCreationData counter = new CounterCreationData(); - counter.CounterType = PerformanceCounterType.AverageCount64; - counter.CounterName = name; - counterDataCollection.Add(counter); + Assert.Equal(0, counterSample.RawValue); + Helpers.DeleteCategory(name); + } + } - // Add the base counter. - CounterCreationData counterBase = new CounterCreationData(); - counterBase.CounterType = PerformanceCounterType.AverageBase; - counterBase.CounterName = name + "Base"; - counterDataCollection.Add(counterBase); + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounter_CreateCounter_ProcessorCounter() + { + using (PerformanceCounter counterSample = new PerformanceCounter("Processor", "Interrupts/sec", "0", ".")) + { + Assert.Equal(0, Helpers.RetryOnAllPlatforms(() => counterSample.NextValue())); - // Create the category. - PerformanceCounterCategory.Create(category, - "description", - PerformanceCounterCategoryType.SingleInstance, counterDataCollection); + Assert.True(counterSample.RawValue > 0); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounter_CreateCounter_MultiInstanceReadOnly() + { + var name = nameof(PerformanceCounter_CreateCounter_MultiInstanceReadOnly) + "_Counter"; + var instance = name + "_Instance"; + + var category = Helpers.CreateCategory(name, PerformanceCounterCategoryType.MultiInstance); + + using (PerformanceCounter counterSample = Helpers.RetryOnAllPlatforms(() => new PerformanceCounter(category, name, instance))) + { + Assert.Equal(name, counterSample.CounterName); + Assert.Equal(category, counterSample.CategoryName); + Assert.Equal(instance, counterSample.InstanceName); + Assert.Equal("counter description", Helpers.RetryOnAllPlatforms(() => counterSample.CounterHelp)); + Assert.True(counterSample.ReadOnly); + Helpers.DeleteCategory(name); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounter_CreateCounter_SetReadOnly() + { + var name = nameof(PerformanceCounter_CreateCounter_SetReadOnly) + "_Counter"; + + var category = Helpers.CreateCategory(name, PerformanceCounterCategoryType.SingleInstance); + + using (PerformanceCounter counterSample = Helpers.RetryOnAllPlatforms(() => new PerformanceCounter(category, name))) + { + counterSample.ReadOnly = false; + + Assert.False(counterSample.ReadOnly); } - PerformanceCounter counterSample = new PerformanceCounter(category, - name, false); + Helpers.DeleteCategory(name); + } - counterSample.RawValue = 0; + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounter_SetProperties_Null() + { + using (PerformanceCounter counterSample = new PerformanceCounter()) + { + Assert.Throws(() => counterSample.CategoryName = null); + Assert.Throws(() => counterSample.CounterName = null); + Assert.Throws(() => counterSample.MachineName = null); + } + } - Assert.Equal(0, counterSample.RawValue); + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounter_SetRawValue_ReadOnly() + { + using (PerformanceCounter counterSample = new PerformanceCounter()) + { + Assert.Throws(() => counterSample.RawValue = 10); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounter_GetRawValue_EmptyCategoryName() + { + var name = nameof(PerformanceCounter_GetRawValue_EmptyCategoryName) + "_Counter"; + using (PerformanceCounter counterSample = new PerformanceCounter()) + { + counterSample.ReadOnly = false; + counterSample.CounterName = name; + + Assert.Throws(() => counterSample.RawValue); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounter_GetRawValue_EmptyCounterName() + { + var name = nameof(PerformanceCounter_GetRawValue_EmptyCounterName) + "_Counter"; + using (PerformanceCounter counterSample = new PerformanceCounter()) + { + counterSample.ReadOnly = false; + counterSample.CategoryName = name + "_Category"; + + Assert.Throws(() => counterSample.RawValue); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounter_GetRawValue_CounterDoesNotExist() + { + var name = nameof(PerformanceCounter_GetRawValue_CounterDoesNotExist) + "_Counter"; + using (PerformanceCounter counterSample = new PerformanceCounter()) + { + counterSample.ReadOnly = false; + counterSample.CounterName = name; + counterSample.CategoryName = name + "_Category"; + + Assert.Throws(() => counterSample.RawValue); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounter_NextValue_ProcessorCounter() + { + using (PerformanceCounter counterSample = new PerformanceCounter("Processor", "Interrupts/sec", "0", ".")) + { + Helpers.RetryOnAllPlatforms(() => counterSample.NextValue()); + System.Threading.Thread.Sleep(30); + + Assert.True(Helpers.RetryOnAllPlatforms(() => counterSample.NextValue()) > 0); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounter_BeginInit_ProcessorCounter() + { + using (PerformanceCounter counterSample = new PerformanceCounter("Processor", "Interrupts/sec", "0", ".")) + { + counterSample.BeginInit(); + + Assert.NotNull(counterSample); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounter_BeginInitEndInit_ProcessorCounter() + { + using (PerformanceCounter counterSample = new PerformanceCounter("Processor", "Interrupts/sec", "0", ".")) + { + counterSample.BeginInit(); + counterSample.EndInit(); + + Assert.NotNull(counterSample); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounter_Decrement() + { + var name = nameof(PerformanceCounter_Decrement) + "_Counter"; + using (PerformanceCounter counterSample = CreateCounterWithCategory(name, false, PerformanceCounterCategoryType.SingleInstance)) + { + counterSample.RawValue = 10; + Helpers.RetryOnAllPlatforms(() => counterSample.Decrement()); + + Assert.Equal(9, counterSample.RawValue); + Helpers.DeleteCategory(name); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounter_Increment() + { + var name = nameof(PerformanceCounter_Increment) + "_Counter"; + using (PerformanceCounter counterSample = CreateCounterWithCategory(name, false, PerformanceCounterCategoryType.SingleInstance)) + { + counterSample.RawValue = 10; + Helpers.RetryOnAllPlatforms(() => counterSample.Increment()); + + Assert.Equal(11, Helpers.RetryOnAllPlatforms(() => counterSample.NextSample().RawValue)); + Helpers.DeleteCategory(name); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounter_IncrementBy_IncrementBy2() + { + var name = nameof(PerformanceCounter_IncrementBy_IncrementBy2) + "_Counter"; + using (PerformanceCounter counterSample = CreateCounterWithCategory(name, false, PerformanceCounterCategoryType.SingleInstance)) + { + counterSample.RawValue = 10; + Helpers.RetryOnAllPlatforms(() => counterSample.IncrementBy(2)); + + Assert.Equal(12, counterSample.RawValue); + Helpers.DeleteCategory(name); + } + } + + [ActiveIssue(25349)] + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounter_IncrementBy_IncrementByReadOnly() + { + var name = nameof(PerformanceCounter_IncrementBy_IncrementByReadOnly) + "_Counter"; + using (PerformanceCounter counterSample = CreateCounterWithCategory(name, true, PerformanceCounterCategoryType.SingleInstance)) + { + Assert.Throws(() => counterSample.IncrementBy(2)); + Helpers.DeleteCategory(name); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounter_Increment_IncrementReadOnly() + { + var name = nameof(PerformanceCounter_Increment_IncrementReadOnly) + "_Counter"; + using (PerformanceCounter counterSample = CreateCounterWithCategory(name, true, PerformanceCounterCategoryType.SingleInstance)) + { + Assert.Throws(() => counterSample.Increment()); + Helpers.DeleteCategory(name); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounter_Decrement_DecrementReadOnly() + { + var name = nameof(PerformanceCounter_Decrement_DecrementReadOnly) + "_Counter"; + using (PerformanceCounter counterSample = CreateCounterWithCategory(name, true, PerformanceCounterCategoryType.SingleInstance)) + { + Assert.Throws(() => counterSample.Decrement()); + Helpers.DeleteCategory(name); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounter_RemoveInstance() + { + var name = nameof(PerformanceCounter_RemoveInstance) + "_Counter"; + using (PerformanceCounter counterSample = CreateCounterWithCategory(name, false, PerformanceCounterCategoryType.SingleInstance)) + { + counterSample.RawValue = 100; + counterSample.RemoveInstance(); + counterSample.Close(); + + Assert.NotNull(counterSample); + Helpers.DeleteCategory(name); + } + } + + [ConditionalFact(typeof(Helpers), nameof(Helpers.IsElevatedAndCanWriteToPerfCounters))] + public static void PerformanceCounter_NextSample_MultiInstance() + { + var name = nameof(PerformanceCounter_NextSample_MultiInstance) + "_Counter"; + var instance = name + "_Instance"; + + var category = Helpers.CreateCategory(name, PerformanceCounterCategoryType.MultiInstance); + + using (PerformanceCounter counterSample = new PerformanceCounter(category, name, instance, false)) + { + counterSample.RawValue = 10; + Helpers.RetryOnAllPlatforms(() => counterSample.Decrement()); + + Assert.Equal(9, counterSample.RawValue); + Helpers.DeleteCategory(name); + } + } + + public static PerformanceCounter CreateCounterWithCategory(string name, bool readOnly, PerformanceCounterCategoryType categoryType ) + { + var category = Helpers.CreateCategory(name, categoryType); + + PerformanceCounter counterSample = Helpers.RetryOnAllPlatforms(() => new PerformanceCounter(category, name, readOnly)); + + return counterSample; } } } diff --git a/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/System.Diagnostics.PerformanceCounter.Tests.csproj b/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/System.Diagnostics.PerformanceCounter.Tests.csproj index dcdb5ef17c..e11b77af69 100644 --- a/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/System.Diagnostics.PerformanceCounter.Tests.csproj +++ b/external/corefx/src/System.Diagnostics.PerformanceCounter/tests/System.Diagnostics.PerformanceCounter.Tests.csproj @@ -4,15 +4,22 @@ {296074B7-1CC9-497E-8C1E-FC5C985C75C6} - - + + + + + + + + + Common\System\ShouldNotBeInvokedException.cs - + \ No newline at end of file diff --git a/external/corefx/src/System.Diagnostics.Process/System.Diagnostics.Process.sln b/external/corefx/src/System.Diagnostics.Process/System.Diagnostics.Process.sln index 4539be09ec..5a6ce0aeb1 100644 --- a/external/corefx/src/System.Diagnostics.Process/System.Diagnostics.Process.sln +++ b/external/corefx/src/System.Diagnostics.Process/System.Diagnostics.Process.sln @@ -35,10 +35,10 @@ Global {E1114510-844C-4BB2-BBAD-8595BD16E24B}.Debug|Any CPU.Build.0 = netstandard-Windows_NT-Debug|Any CPU {E1114510-844C-4BB2-BBAD-8595BD16E24B}.Release|Any CPU.ActiveCfg = netstandard-Windows_NT-Release|Any CPU {E1114510-844C-4BB2-BBAD-8595BD16E24B}.Release|Any CPU.Build.0 = netstandard-Windows_NT-Release|Any CPU - {4E05E43A-1DC9-47C7-8280-13CF4EF741EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4E05E43A-1DC9-47C7-8280-13CF4EF741EA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4E05E43A-1DC9-47C7-8280-13CF4EF741EA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4E05E43A-1DC9-47C7-8280-13CF4EF741EA}.Release|Any CPU.Build.0 = Release|Any CPU + {4E05E43A-1DC9-47C7-8280-13CF4EF741EA}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {4E05E43A-1DC9-47C7-8280-13CF4EF741EA}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {4E05E43A-1DC9-47C7-8280-13CF4EF741EA}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {4E05E43A-1DC9-47C7-8280-13CF4EF741EA}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU {F55047F8-E47B-46E3-B221-C23595AFE168}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU {F55047F8-E47B-46E3-B221-C23595AFE168}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU {F55047F8-E47B-46E3-B221-C23595AFE168}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU diff --git a/external/corefx/src/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs b/external/corefx/src/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs index 7db8b270d8..83f600a4f4 100644 --- a/external/corefx/src/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs +++ b/external/corefx/src/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs @@ -11,7 +11,7 @@ namespace Microsoft.Win32.SafeHandles public sealed partial class SafeProcessHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid { public SafeProcessHandle(System.IntPtr existingHandle, bool ownsHandle) : base(default(bool)) { } - public override bool IsInvalid { [System.Security.SecurityCritical] get { throw null; } } + public override bool IsInvalid { get { throw null; } } protected override bool ReleaseHandle() { throw null; } } } @@ -170,6 +170,7 @@ namespace System.Diagnostics public bool RedirectStandardInput { get { throw null; } set { } } public bool RedirectStandardOutput { get { throw null; } set { } } public System.Text.Encoding StandardErrorEncoding { get { throw null; } set { } } + public System.Text.Encoding StandardInputEncoding { get { throw null; } set { } } public System.Text.Encoding StandardOutputEncoding { get { throw null; } set { } } public string UserName { get { throw null; } set { } } public bool UseShellExecute { get { throw null; } set { } } diff --git a/external/corefx/src/System.Diagnostics.Process/src/Configurations.props b/external/corefx/src/System.Diagnostics.Process/src/Configurations.props index 193ee5c3bd..8e84cf544a 100644 --- a/external/corefx/src/System.Diagnostics.Process/src/Configurations.props +++ b/external/corefx/src/System.Diagnostics.Process/src/Configurations.props @@ -2,10 +2,11 @@ + netcoreapp-FreeBSD; netcoreapp-Linux; netcoreapp-OSX; netcoreapp-Windows_NT; uap-Windows_NT; - \ No newline at end of file + diff --git a/external/corefx/src/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Unix.cs b/external/corefx/src/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Unix.cs index 0759f0efb8..eff131c23c 100644 --- a/external/corefx/src/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Unix.cs +++ b/external/corefx/src/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Unix.cs @@ -28,11 +28,9 @@ namespace Microsoft.Win32.SafeHandles public override bool IsInvalid { - [SecurityCritical] get { return ((int)handle) < 0; } // Unix processes have non-negative ID values } - [SecurityCritical] protected override bool ReleaseHandle() { // Nop. We don't actually hold handles to a process, as there's no equivalent diff --git a/external/corefx/src/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Windows.cs b/external/corefx/src/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Windows.cs index c7cac66810..e1b274a608 100644 --- a/external/corefx/src/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Windows.cs +++ b/external/corefx/src/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Windows.cs @@ -21,7 +21,6 @@ namespace Microsoft.Win32.SafeHandles { private const int DefaultInvalidHandleValue = 0; - [SecurityCritical] protected override bool ReleaseHandle() { return Interop.Kernel32.CloseHandle(handle); diff --git a/external/corefx/src/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.cs b/external/corefx/src/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.cs index adc55dcbe5..471ddfd8cd 100644 --- a/external/corefx/src/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.cs +++ b/external/corefx/src/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.cs @@ -18,7 +18,6 @@ using System.Security; namespace Microsoft.Win32.SafeHandles { - [SecurityCritical] public sealed partial class SafeProcessHandle : SafeHandleZeroOrMinusOneIsInvalid { internal static readonly SafeProcessHandle InvalidHandle = new SafeProcessHandle(); diff --git a/external/corefx/src/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeThreadHandle.cs b/external/corefx/src/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeThreadHandle.cs index c7cde39fe7..7aee3d62dd 100644 --- a/external/corefx/src/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeThreadHandle.cs +++ b/external/corefx/src/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeThreadHandle.cs @@ -34,7 +34,6 @@ namespace Microsoft.Win32.SafeHandles public override bool IsInvalid { - [SecurityCritical] get { return handle == IntPtr.Zero || handle == new IntPtr(-1); } } diff --git a/external/corefx/src/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeTokenHandle.cs b/external/corefx/src/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeTokenHandle.cs index f065fdb13a..3df86ef7a1 100644 --- a/external/corefx/src/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeTokenHandle.cs +++ b/external/corefx/src/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeTokenHandle.cs @@ -18,7 +18,6 @@ using System.Security; namespace Microsoft.Win32.SafeHandles { - [SecurityCritical] internal sealed class SafeTokenHandle : SafeHandle { private const int DefaultInvalidHandleValue = 0; @@ -47,11 +46,9 @@ namespace Microsoft.Win32.SafeHandles public override bool IsInvalid { - [SecurityCritical] get { return handle == IntPtr.Zero || handle == new IntPtr(-1); } } - [SecurityCritical] protected override bool ReleaseHandle() { return Interop.Kernel32.CloseHandle(handle); diff --git a/external/corefx/src/System.Diagnostics.Process/src/Resources/Strings.resx b/external/corefx/src/System.Diagnostics.Process/src/Resources/Strings.resx index e4ee4b3903..d83dfb7010 100644 --- a/external/corefx/src/System.Diagnostics.Process/src/Resources/Strings.resx +++ b/external/corefx/src/System.Diagnostics.Process/src/Resources/Strings.resx @@ -309,4 +309,7 @@ Retrieving information about local processes is not supported on this platform. + + StandardInputEncoding is only supported when standard input is redirected. + \ No newline at end of file diff --git a/external/corefx/src/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj b/external/corefx/src/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj index fd49b2d334..108615fe09 100644 --- a/external/corefx/src/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj +++ b/external/corefx/src/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj @@ -13,6 +13,8 @@ 0649 + + @@ -365,7 +367,9 @@ + + @@ -394,7 +398,26 @@ - + + + + + + + + Common\Interop\BSD\System.Native\Interop.Sysctl.cs + + + Common\Interop\FreeBSD\Interop.Process.cs + + + Common\Unix\System.Native\Interop.GetTimestamp.cs + + + Common\Unix\System.Native\Interop.Stat.cs + + + @@ -402,18 +425,18 @@ - + - + diff --git a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/PerformanceCounterLib.cs b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/PerformanceCounterLib.cs index d5452ce8a3..e71754cdff 100644 --- a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/PerformanceCounterLib.cs +++ b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/PerformanceCounterLib.cs @@ -205,7 +205,14 @@ namespace System.Diagnostics private void Init() { #if FEATURE_REGISTRY - _perfDataKey = Registry.PerformanceData; + if (ProcessManager.IsRemoteMachine(_machineName)) + { + _perfDataKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.PerformanceData, _machineName); + } + else + { + _perfDataKey = Registry.PerformanceData; + } #endif } diff --git a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.BSD.cs b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.BSD.cs new file mode 100644 index 0000000000..4f11321b0a --- /dev/null +++ b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.BSD.cs @@ -0,0 +1,122 @@ +// 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.Collections.Generic; +using System.ComponentModel; + +namespace System.Diagnostics +{ + public partial class Process + { + /// + /// Creates an array of components that are associated with process resources on a + /// remote computer. These process resources share the specified process name. + /// + public static Process[] GetProcessesByName(string processName, string machineName) + { + if (processName == null) + { + processName = string.Empty; + } + + Process[] procs = GetProcesses(machineName); + var list = new List(); + + for (int i = 0; i < procs.Length; i++) + { + if (string.Equals(processName, procs[i].ProcessName, StringComparison.OrdinalIgnoreCase)) + { + list.Add(procs[i]); + } + else + { + procs[i].Dispose(); + } + } + + return list.ToArray(); + } + + /// + /// Gets or sets which processors the threads in this process can be scheduled to run on. + /// + private unsafe IntPtr ProcessorAffinityCore + { + get + { + throw new PlatformNotSupportedException(SR.ProcessorAffinityNotSupported); + } + set + { + throw new PlatformNotSupportedException(SR.ProcessorAffinityNotSupported); + } + } + + + /// + /// Make sure we have obtained the min and max working set limits. + /// + private void GetWorkingSetLimits(out IntPtr minWorkingSet, out IntPtr maxWorkingSet) + { + // We can only do this for the current process on OS X + if (_processId != Interop.Sys.GetPid()) + throw new PlatformNotSupportedException(SR.OsxExternalProcessWorkingSetNotSupported); + + // Minimum working set (or resident set, as it is called on *nix) doesn't exist so set to 0 + minWorkingSet = IntPtr.Zero; + + // Get the max working set size + Interop.Sys.RLimit limit; + if (Interop.Sys.GetRLimit(Interop.Sys.RlimitResources.RLIMIT_RSS, out limit) == 0) + { + maxWorkingSet = limit.CurrentLimit == Interop.Sys.RLIM_INFINITY ? + new IntPtr(Int64.MaxValue) : + new IntPtr(Convert.ToInt64(limit.CurrentLimit)); + } + else + { + // The contract specifies that this throws Win32Exception when it failes to retrieve the info + throw new Win32Exception(); + } + } + + /// Sets one or both of the minimum and maximum working set limits. + /// The new minimum working set limit, or null not to change it. + /// The new maximum working set limit, or null not to change it. + /// The resulting minimum working set limit after any changes applied. + /// The resulting maximum working set limit after any changes applied. + private void SetWorkingSetLimitsCore(IntPtr? newMin, IntPtr? newMax, out IntPtr resultingMin, out IntPtr resultingMax) + { + // We can only do this for the current process on OS X + if (_processId != Interop.Sys.GetPid()) + throw new PlatformNotSupportedException(SR.OsxExternalProcessWorkingSetNotSupported); + + // There isn't a way to set the minimum working set, so throw an exception here + if (newMin.HasValue) + { + throw new PlatformNotSupportedException(SR.MinimumWorkingSetNotSupported); + } + + // The minimum resident set will always be 0, default the resulting max to 0 until we set it (to make the compiler happy) + resultingMin = IntPtr.Zero; + resultingMax = IntPtr.Zero; + + // The default hard upper limit is absurdly high (over 9000PB) so just change the soft limit...especially since + // if you aren't root and move the upper limit down, you need root to move it back up + if (newMax.HasValue) + { + Interop.Sys.RLimit limits = new Interop.Sys.RLimit() { CurrentLimit = (ulong)newMax.Value.ToInt64() }; + int result = Interop.Sys.SetRLimit(Interop.Sys.RlimitResources.RLIMIT_RSS, ref limits); + if (result != 0) + { + throw new System.ComponentModel.Win32Exception(SR.RUsageFailure); + } + + // Try to grab the actual value, in case the OS decides to fudge the numbers + result = Interop.Sys.GetRLimit(Interop.Sys.RlimitResources.RLIMIT_RSS, out limits); + if (result == 0) resultingMax = new IntPtr((long)limits.CurrentLimit); + } + } + } +} diff --git a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.FreeBSD.cs b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.FreeBSD.cs new file mode 100644 index 0000000000..e25217e821 --- /dev/null +++ b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.FreeBSD.cs @@ -0,0 +1,95 @@ +// 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.Collections.Generic; +using System.ComponentModel; + +namespace System.Diagnostics +{ + public partial class Process + { + /// Gets the time the associated process was started. + internal DateTime StartTimeCore + { + get + { + EnsureState(State.HaveId); + Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, 0); + + return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(stat.startTime); + } + } + + /// + /// Gets the amount of time the associated process has spent utilizing the CPU. + /// It is the sum of the and + /// . + /// + public TimeSpan TotalProcessorTime + { + get + { + EnsureState(State.HaveId); + Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, 0); + return Process.TicksToTimeSpan(stat.userTime + stat.systemTime); + } + } + + /// + /// Gets the amount of time the associated process has spent running code + /// inside the application portion of the process (not the operating system core). + /// + public TimeSpan UserProcessorTime + { + get + { + EnsureState(State.HaveId); + + Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, 0); + return Process.TicksToTimeSpan(stat.userTime); + } + } + + /// Gets the amount of time the process has spent running code inside the operating system core. + public TimeSpan PrivilegedProcessorTime + { + get + { + EnsureState(State.HaveId); + + Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, 0); + return Process.TicksToTimeSpan(stat.systemTime); + } + } + + // Gets execution path + private string GetPathToOpenFile() + { + Interop.Sys.FileStatus stat; + if (Interop.Sys.Stat("/usr/local/bin/open", out stat) == 0 ) + { + return "/usr/local/bin/open"; + } + else + { + return null; + } + } + + // ----------------------------- + // ---- PAL layer ends here ---- + // ----------------------------- + /// Gets the path to the current executable, or null if it could not be retrieved. + private static string GetExePath() + { + return Interop.Process.GetProcPath(Interop.Sys.GetPid()); + } + + // ---------------------------------- + // ---- Unix PAL layer ends here ---- + // ---------------------------------- + + + } +} diff --git a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs index 898cf347ec..030c5c50eb 100644 --- a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs +++ b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs @@ -2,8 +2,10 @@ // 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.Collections.Generic; using System.ComponentModel; +using System.Globalization; using System.IO; using System.Text; @@ -57,6 +59,42 @@ namespace System.Diagnostics } } + /// Computes a time based on a number of ticks since boot. + /// The timespan since boot. + /// The converted time. + internal static DateTime BootTimeToDateTime(TimeSpan timespanAfterBoot) + { + // Use the uptime and the current time to determine the absolute boot time. + DateTime bootTime = DateTime.UtcNow - Uptime; + + // And use that to determine the absolute time for timespan. + DateTime dt = bootTime + timespanAfterBoot; + + // The return value is expected to be in the local time zone. + // It is converted here (rather than starting with DateTime.Now) to avoid DST issues. + return dt.ToLocalTime(); + } + + /// Gets the elapsed time since the system was booted. + private static TimeSpan Uptime + { + get + { + // '/proc/uptime' accounts time a device spends in sleep mode. + const string UptimeFile = Interop.procfs.ProcUptimeFilePath; + string text = File.ReadAllText(UptimeFile); + + double uptimeSeconds = 0; + int length = text.IndexOf(' '); + if (length != -1) + { + Double.TryParse(text.AsReadOnlySpan().Slice(0, length), NumberStyles.AllowDecimalPoint, NumberFormatInfo.InvariantInfo, out uptimeSeconds); + } + + return TimeSpan.FromSeconds(uptimeSeconds); + } + } + /// Gets execution path private string GetPathToOpenFile() { @@ -194,33 +232,7 @@ namespace System.Diagnostics Interop.procfs.SelfExeFilePath : Interop.procfs.GetExeFilePathForProcess(processId); - // Determine the maximum size of a path - int maxPath = Interop.Sys.MaxPath; - - // Start small with a buffer allocation, and grow only up to the max path - for (int pathLen = 256; pathLen < maxPath; pathLen *= 2) - { - // Read from procfs the symbolic link to this process' executable - byte[] buffer = new byte[pathLen + 1]; // +1 for null termination - int resultLength = Interop.Sys.ReadLink(exeFilePath, buffer, pathLen); - - // If we got one, null terminate it (readlink doesn't do this) and return the string - if (resultLength > 0) - { - buffer[resultLength] = (byte)'\0'; - return Encoding.UTF8.GetString(buffer, 0, resultLength); - } - - // If the buffer was too small, loop around again and try with a larger buffer. - // Otherwise, bail. - if (resultLength == 0 || Interop.Sys.GetLastError() != Interop.Error.ENAMETOOLONG) - { - break; - } - } - - // Could not get a path - return null; + return Interop.Sys.ReadLink(exeFilePath); } // ---------------------------------- diff --git a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.OSX.cs b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.OSX.cs index 560d04c4cb..750b8ebe2f 100644 --- a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.OSX.cs +++ b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.OSX.cs @@ -9,35 +9,6 @@ namespace System.Diagnostics { public partial class Process { - /// - /// Creates an array of components that are associated with process resources on a - /// remote computer. These process resources share the specified process name. - /// - public static Process[] GetProcessesByName(string processName, string machineName) - { - if (processName == null) - { - processName = string.Empty; - } - - Process[] procs = GetProcesses(machineName); - var list = new List(); - - for (int i = 0; i < procs.Length; i++) - { - if (string.Equals(processName, procs[i].ProcessName, StringComparison.OrdinalIgnoreCase)) - { - list.Add(procs[i]); - } - else - { - procs[i].Dispose(); - } - } - - return list.ToArray(); - } - /// Gets the amount of time the process has spent running code inside the operating system core. public TimeSpan PrivilegedProcessorTime { @@ -123,87 +94,6 @@ namespace System.Diagnostics } } - /// - /// Gets or sets which processors the threads in this process can be scheduled to run on. - /// - private unsafe IntPtr ProcessorAffinityCore - { - get - { - throw new PlatformNotSupportedException(SR.ProcessorAffinityNotSupported); - } - set - { - throw new PlatformNotSupportedException(SR.ProcessorAffinityNotSupported); - } - } - - - /// - /// Make sure we have obtained the min and max working set limits. - /// - private void GetWorkingSetLimits(out IntPtr minWorkingSet, out IntPtr maxWorkingSet) - { - // We can only do this for the current process on OS X - if (_processId != Interop.Sys.GetPid()) - throw new PlatformNotSupportedException(SR.OsxExternalProcessWorkingSetNotSupported); - - // Minimum working set (or resident set, as it is called on *nix) doesn't exist so set to 0 - minWorkingSet = IntPtr.Zero; - - // Get the max working set size - Interop.Sys.RLimit limit; - if (Interop.Sys.GetRLimit(Interop.Sys.RlimitResources.RLIMIT_RSS, out limit) == 0) - { - maxWorkingSet = limit.CurrentLimit == Interop.Sys.RLIM_INFINITY ? - new IntPtr(Int64.MaxValue) : - new IntPtr(Convert.ToInt64(limit.CurrentLimit)); - } - else - { - // The contract specifies that this throws Win32Exception when it failes to retrieve the info - throw new Win32Exception(); - } - } - - /// Sets one or both of the minimum and maximum working set limits. - /// The new minimum working set limit, or null not to change it. - /// The new maximum working set limit, or null not to change it. - /// The resulting minimum working set limit after any changes applied. - /// The resulting maximum working set limit after any changes applied. - private void SetWorkingSetLimitsCore(IntPtr? newMin, IntPtr? newMax, out IntPtr resultingMin, out IntPtr resultingMax) - { - // We can only do this for the current process on OS X - if (_processId != Interop.Sys.GetPid()) - throw new PlatformNotSupportedException(SR.OsxExternalProcessWorkingSetNotSupported); - - // There isn't a way to set the minimum working set, so throw an exception here - if (newMin.HasValue) - { - throw new PlatformNotSupportedException(SR.MinimumWorkingSetNotSupported); - } - - // The minimum resident set will always be 0, default the resulting max to 0 until we set it (to make the compiler happy) - resultingMin = IntPtr.Zero; - resultingMax = IntPtr.Zero; - - // The default hard upper limit is absurdly high (over 9000PB) so just change the soft limit...especially since - // if you aren't root and move the upper limit down, you need root to move it back up - if (newMax.HasValue) - { - Interop.Sys.RLimit limits = new Interop.Sys.RLimit() { CurrentLimit = (ulong)newMax.Value.ToInt64() }; - int result = Interop.Sys.SetRLimit(Interop.Sys.RlimitResources.RLIMIT_RSS, ref limits); - if (result != 0) - { - throw new System.ComponentModel.Win32Exception(SR.RUsageFailure); - } - - // Try to grab the actual value, in case the OS decides to fudge the numbers - result = Interop.Sys.GetRLimit(Interop.Sys.RlimitResources.RLIMIT_RSS, out limits); - if (result == 0) resultingMax = new IntPtr((long)limits.CurrentLimit); - } - } - // ----------------------------- // ---- PAL layer ends here ---- // ----------------------------- diff --git a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs index 05976713ab..7790e8c5fe 100644 --- a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs +++ b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs @@ -317,7 +317,7 @@ namespace System.Diagnostics { Debug.Assert(stdinFd >= 0); _standardInput = new StreamWriter(OpenStream(stdinFd, FileAccess.Write), - s_utf8NoBom, StreamBufferSize) { AutoFlush = true }; + startInfo.StandardInputEncoding ?? s_utf8NoBom, StreamBufferSize) { AutoFlush = true }; } if (startInfo.RedirectStandardOutput) { @@ -473,23 +473,6 @@ namespace System.Diagnostics return TimeSpan.FromSeconds(ticks / (double)ticksPerSecond); } - /// Computes a time based on a number of ticks since boot. - /// The timespan since boot. - /// The converted time. - internal static DateTime BootTimeToDateTime(TimeSpan timespanAfterBoot) - { - // Use the uptime and the current time to determine the absolute boot time. This implementation is relying on the - // implementation detail that Stopwatch.GetTimestamp() uses a value based on time since boot. - DateTime bootTime = DateTime.UtcNow - TimeSpan.FromSeconds(Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency); - - // And use that to determine the absolute time for timespan. - DateTime dt = bootTime + timespanAfterBoot; - - // The return value is expected to be in the local time zone. - // It is converted here (rather than starting with DateTime.Now) to avoid DST issues. - return dt.ToLocalTime(); - } - /// Opens a stream around the specified file descriptor and with the specified access. /// The file descriptor. /// The access mode. @@ -572,6 +555,10 @@ namespace System.Diagnostics { if (inQuotes && i < arguments.Length - 1 && arguments[i + 1] == '"') { + // Two consecutive double quotes inside an inQuotes region should result in a literal double quote + // (the parser is left in the inQuotes region). + // This behavior is not part of the spec of code:ParseArgumentsIntoList, but is compatible with CRT + // and .NET Framework. currentArgument.Append('"'); i++; } diff --git a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.Win32.cs b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.Win32.cs index 0193c493f8..ddde5fc6bc 100644 --- a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.Win32.cs +++ b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.Win32.cs @@ -34,6 +34,9 @@ namespace System.Diagnostics if (startInfo.RedirectStandardInput || startInfo.RedirectStandardOutput || startInfo.RedirectStandardError) throw new InvalidOperationException(SR.CantRedirectStreams); + if (startInfo.StandardInputEncoding != null) + throw new InvalidOperationException(SR.StandardInputEncodingNotAllowed); + if (startInfo.StandardErrorEncoding != null) throw new InvalidOperationException(SR.StandardErrorEncodingNotAllowed); diff --git a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs index 3798c6f210..1b70bc7347 100644 --- a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs +++ b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs @@ -633,7 +633,7 @@ namespace System.Diagnostics if (startInfo.RedirectStandardInput) { - Encoding enc = GetEncoding((int)Interop.Kernel32.GetConsoleCP()); + Encoding enc = startInfo.StandardInputEncoding ?? GetEncoding((int)Interop.Kernel32.GetConsoleCP()); _standardInput = new StreamWriter(new FileStream(standardInputWritePipeHandle, FileAccess.Write, 4096, false), enc, 4096); _standardInput.AutoFlush = true; } diff --git a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.cs b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.cs index a6ce74d2cb..1509b48003 100644 --- a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.cs +++ b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/Process.cs @@ -67,6 +67,8 @@ namespace System.Diagnostics private static object s_createProcessLock = new object(); + private bool _standardInputAccessed; + private StreamReadMode _outputStreamReadMode; private StreamReadMode _errorStreamReadMode; @@ -691,6 +693,7 @@ namespace System.Diagnostics throw new InvalidOperationException(SR.CantGetStandardIn); } + _standardInputAccessed = true; return _standardInput; } } @@ -846,18 +849,45 @@ namespace System.Diagnostics _machineName = "."; _raisedOnExited = false; - //Don't call close on the Readers and writers - //since they might be referenced by somebody else while the - //process is still alive but this method called. - _standardOutput = null; - _standardInput = null; - _standardError = null; + // Only call close on the streams if the user cannot have a reference on them. + // If they are referenced it is the user's responsibility to dispose of them. + try + { + if (_standardOutput != null && (_outputStreamReadMode == StreamReadMode.AsyncMode || _outputStreamReadMode == StreamReadMode.Undefined)) + { + if (_outputStreamReadMode == StreamReadMode.AsyncMode) + { + _output.CancelOperation(); + } + _standardOutput.Close(); + } - _output = null; - _error = null; + if (_standardError != null && (_errorStreamReadMode == StreamReadMode.AsyncMode || _errorStreamReadMode == StreamReadMode.Undefined)) + { + if (_errorStreamReadMode == StreamReadMode.AsyncMode) + { + _error.CancelOperation(); + } + _standardError.Close(); + } - CloseCore(); - Refresh(); + if (_standardInput != null && !_standardInputAccessed) + { + _standardInput.Close(); + } + } + finally + { + _standardOutput = null; + _standardInput = null; + _standardError = null; + + _output = null; + _error = null; + + CloseCore(); + Refresh(); + } } } @@ -1156,6 +1186,10 @@ namespace System.Diagnostics { throw new InvalidOperationException(SR.FileNameMissing); } + if (startInfo.StandardInputEncoding != null && !startInfo.RedirectStandardInput) + { + throw new InvalidOperationException(SR.StandardInputEncodingNotAllowed); + } if (startInfo.StandardOutputEncoding != null && !startInfo.RedirectStandardOutput) { throw new InvalidOperationException(SR.StandardOutputEncodingNotAllowed); diff --git a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.BSD.cs b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.BSD.cs new file mode 100644 index 0000000000..1056dbf65e --- /dev/null +++ b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.BSD.cs @@ -0,0 +1,64 @@ +// 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.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; + +namespace System.Diagnostics +{ + internal static partial class ProcessManager + { + /// Gets process infos for each process on the specified machine. + /// The target machine. + /// An array of process infos, one per found process. + public static ProcessInfo[] GetProcessInfos(string machineName) + { + ThrowIfRemoteMachine(machineName); + int[] procIds = GetProcessIds(machineName); + + // Iterate through all process IDs to load information about each process + var processes = new List(procIds.Length); + foreach (int pid in procIds) + { + ProcessInfo pi = CreateProcessInfo(pid); + if (pi != null) + { + processes.Add(pi); + } + } + + return processes.ToArray(); + } + + /// Gets an array of module infos for the specified process. + /// The ID of the process whose modules should be enumerated. + /// The array of modules. + internal static ProcessModuleCollection GetModules(int processId) + { + // We don't have a good way of getting all of the modules of the particular process, + // but we can at least get the path to the executable file for the process, and + // other than for debugging tools, that's the main reason consumers of Modules care about it, + // and why MainModule exists. + try + { + string exePath = GetProcPath(processId); + if (!string.IsNullOrEmpty(exePath)) + { + return new ProcessModuleCollection(1) + { + new ProcessModule() + { + FileName = exePath, + ModuleName = Path.GetFileName(exePath) + } + }; + } + } + catch { } // eat all errors + + return new ProcessModuleCollection(0); + } + } +} diff --git a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.FreeBSD.cs b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.FreeBSD.cs new file mode 100644 index 0000000000..652d0f56cb --- /dev/null +++ b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.FreeBSD.cs @@ -0,0 +1,59 @@ +// 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.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; + +namespace System.Diagnostics +{ + internal static partial class ProcessManager + { + /// Gets the IDs of all processes on the current machine. + public static int[] GetProcessIds() + { + return Interop.Process.ListAllPids(); + } + + internal static string GetProcPath(int processId) + { + return Interop.Process.GetProcPath(processId); + } + + private static ProcessInfo CreateProcessInfo(int pid) + { + // Negative PIDs aren't valid + if (pid < 0) + { + throw new ArgumentOutOfRangeException(nameof(pid)); + } + + ProcessInfo procInfo = new ProcessInfo() + { + ProcessId = pid + }; + + // Try to get the task info. This can fail if the user permissions don't permit + // this user context to query the specified process + ProcessInfo iinfo = Interop.Process.GetProcessInfoById(pid); + + procInfo.ProcessName = iinfo.ProcessName; + procInfo.BasePriority = iinfo.BasePriority; + procInfo.VirtualBytes = iinfo.VirtualBytes; + procInfo.WorkingSet = iinfo.WorkingSet; + procInfo.SessionId = iinfo.SessionId; + foreach (ThreadInfo ti in iinfo._threadInfoList) + { + procInfo._threadInfoList.Add(ti); + } + + return procInfo; + } + + // ---------------------------------- + // ---- Unix PAL layer ends here ---- + // ---------------------------------- + + } +} diff --git a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.OSX.cs b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.OSX.cs index 570826d15a..dffc977396 100644 --- a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.OSX.cs +++ b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.OSX.cs @@ -16,26 +16,9 @@ namespace System.Diagnostics return Interop.libproc.proc_listallpids(); } - /// Gets process infos for each process on the specified machine. - /// The target machine. - /// An array of process infos, one per found process. - public static ProcessInfo[] GetProcessInfos(string machineName) + private static string GetProcPath(int processId) { - ThrowIfRemoteMachine(machineName); - int[] procIds = GetProcessIds(machineName); - - // Iterate through all process IDs to load information about each process - var processes = new List(procIds.Length); - foreach (int pid in procIds) - { - ProcessInfo pi = CreateProcessInfo(pid); - if (pi != null) - { - processes.Add(pi); - } - } - - return processes.ToArray(); + return Interop.libproc.proc_pidpath(processId); } // ----------------------------- @@ -99,35 +82,6 @@ namespace System.Diagnostics return procInfo; } - /// Gets an array of module infos for the specified process. - /// The ID of the process whose modules should be enumerated. - /// The array of modules. - internal static ProcessModuleCollection GetModules(int processId) - { - // We don't have a good way of getting all of the modules of the particular process, - // but we can at least get the path to the executable file for the process, and - // other than for debugging tools, that's the main reason consumers of Modules care about it, - // and why MainModule exists. - try - { - string exePath = Interop.libproc.proc_pidpath(processId); - if (!string.IsNullOrEmpty(exePath)) - { - return new ProcessModuleCollection(1) - { - new ProcessModule() - { - FileName = exePath, - ModuleName = Path.GetFileName(exePath) - } - }; - } - } - catch { } // eat all errors - - return new ProcessModuleCollection(0); - } - // ---------------------------------- // ---- Unix PAL layer ends here ---- // ---------------------------------- @@ -159,5 +113,5 @@ namespace System.Diagnostics else return System.Diagnostics.ThreadWaitReason.Unknown; // There isn't a good mapping for anything else } - } + } } diff --git a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs index a99a1a390a..0498f97fef 100644 --- a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs +++ b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs @@ -195,7 +195,8 @@ namespace System.Diagnostics } // If the handle is invalid because the process has exited, only throw an exception if throwIfExited is true. - if (!IsProcessRunning(processId)) + // Assume the process is still running if the error was ERROR_ACCESS_DENIED for better performance + if (result != Interop.Errors.ERROR_ACCESS_DENIED && !IsProcessRunning(processId)) { if (throwIfExited) { diff --git a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessStartInfo.cs b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessStartInfo.cs index ba505cf4cd..d07ade0453 100644 --- a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessStartInfo.cs +++ b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessStartInfo.cs @@ -95,8 +95,10 @@ namespace System.Diagnostics public bool RedirectStandardOutput { get; set; } public bool RedirectStandardError { get; set; } - public Encoding StandardErrorEncoding { get; set; } + public Encoding StandardInputEncoding { get; set; } + public Encoding StandardErrorEncoding { get; set; } + public Encoding StandardOutputEncoding { get; set; } /// diff --git a/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.FreeBSD.cs b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.FreeBSD.cs new file mode 100644 index 0000000000..7d60059cda --- /dev/null +++ b/external/corefx/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.FreeBSD.cs @@ -0,0 +1,76 @@ +// 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. + +namespace System.Diagnostics +{ + public partial class ProcessThread + { + /// + /// Returns or sets the priority level of the associated thread. The priority level is + /// not an absolute level, but instead contributes to the actual thread priority by + /// considering the priority class of the process. + /// + private ThreadPriorityLevel PriorityLevelCore + { + get + { + Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, Id); + return Interop.Sys.GetThreadPriorityFromNiceValue((int)stat.nice); + } + set + { + throw new PlatformNotSupportedException(); // We can find no API to set this + } + } + + /// Returns the time the associated thread was started. + public DateTime StartTime + { + // kinfo_proc has one entry per thread but ki_start seems to be same for + // all threads e.g. reflects process start. This may be re-visited later. + get { throw new PlatformNotSupportedException(); } + } + + /// + /// Returns the amount of time the associated thread has spent utilizing the CPU. + /// It is the sum of the System.Diagnostics.ProcessThread.UserProcessorTime and + /// System.Diagnostics.ProcessThread.PrivilegedProcessorTime. + /// + public TimeSpan TotalProcessorTime + { + get + { + Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, Id); + return Process.TicksToTimeSpan(stat.userTime + stat.systemTime); + } + } + + /// + /// Returns the amount of time the associated thread has spent running code + /// inside the application (not the operating system core). + /// + public TimeSpan UserProcessorTime + { + get + { + Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, Id); + return Process.TicksToTimeSpan(stat.userTime); + } + } + + /// + /// Returns the amount of time the thread has spent running code inside the operating + /// system core. + /// + public TimeSpan PrivilegedProcessorTime + { + get + { + Interop.Process.proc_stats stat = Interop.Process.GetThreadInfo(_processId, Id); + return Process.TicksToTimeSpan(stat.systemTime); + } + + } + } +} diff --git a/external/corefx/src/System.Diagnostics.Process/tests/Configurations.props b/external/corefx/src/System.Diagnostics.Process/tests/Configurations.props index f60b31a50d..5d07f60b8f 100644 --- a/external/corefx/src/System.Diagnostics.Process/tests/Configurations.props +++ b/external/corefx/src/System.Diagnostics.Process/tests/Configurations.props @@ -6,6 +6,8 @@ netstandard-Unix; uap-Windows_NT; uapaot-Windows_NT; + netcoreapp-Windows_NT; + netcoreapp-Unix; \ No newline at end of file diff --git a/external/corefx/src/System.Diagnostics.Process/tests/Performance/System.Diagnostics.Process.Performance.Tests.csproj b/external/corefx/src/System.Diagnostics.Process/tests/Performance/System.Diagnostics.Process.Performance.Tests.csproj index 2737088031..1d129929ef 100644 --- a/external/corefx/src/System.Diagnostics.Process/tests/Performance/System.Diagnostics.Process.Performance.Tests.csproj +++ b/external/corefx/src/System.Diagnostics.Process/tests/Performance/System.Diagnostics.Process.Performance.Tests.csproj @@ -6,9 +6,8 @@ true {4E05E43A-1DC9-47C7-8280-13CF4EF741EA} - - - + + @@ -18,6 +17,9 @@ Common\System\PasteArguments.cs + + Common\System\IO\StringParser.cs + Common\System\PerfUtils.cs diff --git a/external/corefx/src/System.Diagnostics.Process/tests/ProcessStreamReadTests.cs b/external/corefx/src/System.Diagnostics.Process/tests/ProcessStreamReadTests.cs index 1cfaaf0f86..aaa8dd3808 100644 --- a/external/corefx/src/System.Diagnostics.Process/tests/ProcessStreamReadTests.cs +++ b/external/corefx/src/System.Diagnostics.Process/tests/ProcessStreamReadTests.cs @@ -10,7 +10,7 @@ using Xunit; namespace System.Diagnostics.Tests { - public class ProcessStreamReadTests : ProcessTestBase + public partial class ProcessStreamReadTests : ProcessTestBase { [Fact] public void TestSyncErrorStream() @@ -228,6 +228,57 @@ namespace System.Diagnostics.Tests Assert.Equal(ExpectedLineCount + 1, totalLinesReceived); } + [Fact] + public void TestClosingStreamsAsyncDoesNotThrow() + { + Process p = CreateProcessPortable(RemotelyInvokable.WriteLinesAfterClose); + p.StartInfo.RedirectStandardOutput = true; + p.StartInfo.RedirectStandardError = true; + + // On netfx, the handler is called once with the Data as null, even if the process writes nothing to the pipe. + // That behavior is documented here https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.datareceivedeventhandler + p.OutputDataReceived += (s, e) => { Assert.True(PlatformDetection.IsFullFramework && e.Data == null, "OutputDataReceived called after closing the process"); }; + p.ErrorDataReceived += (s, e) => { Assert.True(PlatformDetection.IsFullFramework && e.Data == null, "ErrorDataReceived called after closing the process"); }; + + p.Start(); + p.BeginOutputReadLine(); + p.BeginErrorReadLine(); + + p.Close(); + RemotelyInvokable.FireClosedEvent(); + } + + [Fact] + public void TestClosingStreamsUndefinedDoesNotThrow() + { + Process p = CreateProcessPortable(RemotelyInvokable.WriteLinesAfterClose); + p.StartInfo.RedirectStandardOutput = true; + p.StartInfo.RedirectStandardError = true; + + p.Start(); + p.Close(); + RemotelyInvokable.FireClosedEvent(); + } + + [Fact] + public void TestClosingSyncModeDoesNotCloseStreams() + { + Process p = CreateProcessPortable(RemotelyInvokable.WriteLinesAfterClose); + p.StartInfo.RedirectStandardOutput = true; + p.StartInfo.RedirectStandardError = true; + + p.Start(); + + var output = p.StandardOutput; + var error = p.StandardError; + + p.Close(); + RemotelyInvokable.FireClosedEvent(); + + output.ReadToEnd(); + error.ReadToEnd(); + } + [Fact] public void TestStreamNegativeTests() { diff --git a/external/corefx/src/System.Diagnostics.Process/tests/ProcessStreamReadTests.netcoreapp.cs b/external/corefx/src/System.Diagnostics.Process/tests/ProcessStreamReadTests.netcoreapp.cs new file mode 100644 index 0000000000..9f936db769 --- /dev/null +++ b/external/corefx/src/System.Diagnostics.Process/tests/ProcessStreamReadTests.netcoreapp.cs @@ -0,0 +1,61 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace System.Diagnostics.Tests +{ + public partial class ProcessStreamReadTests : ProcessTestBase + { + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.Uap, "RemotelyInvokable.ReadLineWithCustomEncodingWriteLineWithUtf8 is not supported on uap")] + public void TestCustomStandardInputEncoding() + { + var process = CreateProcessPortable(RemotelyInvokable.ReadLineWithCustomEncodingWriteLineWithUtf8, Encoding.UTF32.WebName); + process.StartInfo.RedirectStandardInput = true; + process.StartInfo.StandardInputEncoding = Encoding.UTF32; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.StandardOutputEncoding = Encoding.UTF8; + process.Start(); + + const string TestLine = "\U0001f627\U0001f62e\U0001f62f"; + process.StandardInput.WriteLine(TestLine); + process.StandardInput.Close(); + + var output = process.StandardOutput.ReadLine(); + Assert.Equal(TestLine, output); + + Assert.True(process.WaitForExit(WaitInMS)); + Assert.Equal(RemotelyInvokable.SuccessExitCode, process.ExitCode); + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.Uap, "RemotelyInvokable.ReadLineWithCustomEncodingWriteLineWithUtf8 is not supported on uap")] + public void TestMismatchedStandardInputEncoding() + { + var process = CreateProcessPortable(RemotelyInvokable.ReadLineWithCustomEncodingWriteLineWithUtf8, Encoding.UTF32.WebName); + process.StartInfo.RedirectStandardInput = true; + // incorrect: the process will be writing in UTF-32 + process.StartInfo.StandardInputEncoding = Encoding.ASCII; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.StandardOutputEncoding = Encoding.UTF8; + process.Start(); + + const string TestLine = "\U0001f627\U0001f62e\U0001f62f"; + process.StandardInput.WriteLine(TestLine); + process.StandardInput.Close(); + + var output = process.StandardOutput.ReadLine(); + Assert.NotEqual(TestLine, output); + + Assert.True(process.WaitForExit(WaitInMS)); + Assert.Equal(RemotelyInvokable.SuccessExitCode, process.ExitCode); + } + } +} diff --git a/external/corefx/src/System.Diagnostics.Process/tests/ProcessTestBase.cs b/external/corefx/src/System.Diagnostics.Process/tests/ProcessTestBase.cs index 48921c2303..55dd145af3 100644 --- a/external/corefx/src/System.Diagnostics.Process/tests/ProcessTestBase.cs +++ b/external/corefx/src/System.Diagnostics.Process/tests/ProcessTestBase.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.IO; using System.Reflection; using System.Threading; using Xunit; @@ -69,5 +70,31 @@ namespace System.Diagnostics.Tests p.Kill(); Assert.True(p.WaitForExit(WaitInMS)); } + + /// + /// Checks if the program is installed + /// + /// + /// + protected static bool IsProgramInstalled(string program) + { + string path; + string pathEnvVar = Environment.GetEnvironmentVariable("PATH"); + char separator = PlatformDetection.IsWindows ? ';' : ':'; + if (pathEnvVar != null) + { + var pathParser = new StringParser(pathEnvVar, separator, skipEmpty: true); + while (pathParser.MoveNext()) + { + string subPath = pathParser.ExtractCurrent(); + path = Path.Combine(subPath, program); + if (File.Exists(path)) + { + return true; + } + } + } + return false; + } } } diff --git a/external/corefx/src/System.Diagnostics.Process/tests/ProcessTests.Unix.cs b/external/corefx/src/System.Diagnostics.Process/tests/ProcessTests.Unix.cs index 0757c839d2..e6ccb41268 100644 --- a/external/corefx/src/System.Diagnostics.Process/tests/ProcessTests.Unix.cs +++ b/external/corefx/src/System.Diagnostics.Process/tests/ProcessTests.Unix.cs @@ -238,12 +238,20 @@ namespace System.Diagnostics.Tests ProcessPriorityClass originalPriority = _process.PriorityClass; Assert.Equal(ProcessPriorityClass.Normal, originalPriority); - SetAndCheckBasePriority(ProcessPriorityClass.Idle, 19); + // https://github.com/dotnet/corefx/issues/25861 -- returns "-19" and not "19" + if (!PlatformDetection.IsWindowsSubsystemForLinux) + { + SetAndCheckBasePriority(ProcessPriorityClass.Idle, 19); + } try { SetAndCheckBasePriority(ProcessPriorityClass.Normal, 0); - SetAndCheckBasePriority(ProcessPriorityClass.High, -11); + // https://github.com/dotnet/corefx/issues/25861 -- returns "11" and not "-11" + if (!PlatformDetection.IsWindowsSubsystemForLinux) + { + SetAndCheckBasePriority(ProcessPriorityClass.High, -11); + } _process.PriorityClass = originalPriority; } catch (Win32Exception ex) @@ -257,18 +265,37 @@ namespace System.Diagnostics.Tests { string path = GetTestFilePath(); File.Create(path).Dispose(); - Assert.Equal(0, chmod(path, 644)); // no execute permissions + int mode = Convert.ToInt32("644", 8); + + Assert.Equal(0, chmod(path, mode)); Win32Exception e = Assert.Throws(() => Process.Start(path)); Assert.NotEqual(0, e.NativeErrorCode); } [Fact] + [PlatformSpecific(~TestPlatforms.OSX)] // OSX doesn't support throwing on Process.Start public void TestStartOnUnixWithBadFormat() { string path = GetTestFilePath(); File.Create(path).Dispose(); - Assert.Equal(0, chmod(path, 744)); // execute permissions + int mode = Convert.ToInt32("744", 8); + + Assert.Equal(0, chmod(path, mode)); // execute permissions + + Win32Exception e = Assert.Throws(() => Process.Start(path)); + Assert.NotEqual(0, e.NativeErrorCode); + } + + [Fact] + [PlatformSpecific(TestPlatforms.OSX)] // OSX doesn't support throwing on Process.Start + public void TestStartOnOSXWithBadFormat() + { + string path = GetTestFilePath(); + File.Create(path).Dispose(); + int mode = Convert.ToInt32("744", 8); + + Assert.Equal(0, chmod(path, mode)); // execute permissions using (Process p = Process.Start(path)) { @@ -281,30 +308,5 @@ namespace System.Diagnostics.Tests private static extern int chmod(string path, int mode); private readonly string[] s_allowedProgramsToRun = new string[] { "xdg-open", "gnome-open", "kfmclient" }; - - /// - /// Checks if the program is installed - /// - /// - /// - private bool IsProgramInstalled(string program) - { - string path; - string pathEnvVar = Environment.GetEnvironmentVariable("PATH"); - if (pathEnvVar != null) - { - var pathParser = new StringParser(pathEnvVar, ':', skipEmpty: true); - while (pathParser.MoveNext()) - { - string subPath = pathParser.ExtractCurrent(); - path = Path.Combine(subPath, program); - if (File.Exists(path)) - { - return true; - } - } - } - return false; - } } } diff --git a/external/corefx/src/System.Diagnostics.Process/tests/ProcessTests.cs b/external/corefx/src/System.Diagnostics.Process/tests/ProcessTests.cs index da7d483cf5..1dbd3f5385 100644 --- a/external/corefx/src/System.Diagnostics.Process/tests/ProcessTests.cs +++ b/external/corefx/src/System.Diagnostics.Process/tests/ProcessTests.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.ComponentModel; +using System.DirectoryServices.ActiveDirectory; using System.IO; using System.Linq; using System.Net; @@ -135,7 +136,43 @@ namespace System.Diagnostics.Tests { Win32Exception e = Assert.Throws(() => Process.Start(new ProcessStartInfo { UseShellExecute = false, FileName = Path.GetTempPath() })); } - + + [Fact] + [PlatformSpecific(~TestPlatforms.OSX)] // OSX doesn't support throwing on Process.Start + [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // UWP overrides WorkingDirectory (https://github.com/dotnet/corefx/pull/25266#issuecomment-347719832). + public void TestStartWithBadWorkingDirectory() + { + string program; + string workingDirectory; + if (PlatformDetection.IsWindows) + { + program = "powershell.exe"; + workingDirectory = @"C:\does-not-exist"; + } + else + { + program = "uname"; + workingDirectory = "/does-not-exist"; + } + + if (IsProgramInstalled(program)) + { + var psi = new ProcessStartInfo + { + FileName = program, + UseShellExecute = false, + WorkingDirectory = workingDirectory + }; + + Win32Exception e = Assert.Throws(() => Process.Start(psi)); + Assert.NotEqual(0, e.NativeErrorCode); + } + else + { + Console.WriteLine($"Program {program} is not installed on this machine."); + } + } + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.HasWindowsShell))] [SkipOnTargetFramework(TargetFrameworkMonikers.Uap, "not supported on UAP")] [OuterLoop("Launches File Explorer")] @@ -884,6 +921,30 @@ namespace System.Diagnostics.Tests AssertExtensions.Throws(null, () => Process.GetProcesses("")); } + [Fact] + public void GetProcesses_InvalidMachineName_ThrowsInvalidOperationException() + { + Assert.Throws(() => Process.GetProcesses(Guid.NewGuid().ToString())); + } + + [Fact] + public void GetProcesses_RemoteMachinePath_ReturnsExpected() + { + try + { + Process[] processes = Process.GetProcesses(Environment.MachineName + "." + Domain.GetComputerDomain()); + Assert.NotEmpty(processes); + } + catch (ActiveDirectoryObjectNotFoundException) + { + //This will be thrown when the executing machine is not domain-joined, i.e. in CI + } + catch (PlatformNotSupportedException) + { + //System.DirectoryServices is not supported on all platforms + } + } + [Fact] [SkipOnTargetFramework(TargetFrameworkMonikers.Uap, "Retrieving information about local processes is not supported on uap")] public void GetProcessesByName_ProcessName_ReturnsExpected() @@ -1112,6 +1173,7 @@ namespace System.Diagnostics.Tests } [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.Mono, "GC has different behavior on Mono")] public void CanBeFinalized() { FinalizingProcess.CreateAndRelease(); diff --git a/external/corefx/src/System.Diagnostics.Process/tests/ProcessTests.netcoreapp.cs b/external/corefx/src/System.Diagnostics.Process/tests/ProcessTests.netcoreapp.cs new file mode 100644 index 0000000000..9ef9cbdd4e --- /dev/null +++ b/external/corefx/src/System.Diagnostics.Process/tests/ProcessTests.netcoreapp.cs @@ -0,0 +1,44 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace System.Diagnostics.Tests +{ + public partial class ProcessTests : ProcessTestBase + { + [Fact] + public void Start_HasStandardInputEncodingNonRedirected_ThrowsInvalidOperationException() + { + var process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = "FileName", + RedirectStandardInput = false, + StandardInputEncoding = Encoding.UTF8 + } + }; + + Assert.Throws(() => process.Start()); + } + + [Fact] + public void Start_StandardInputEncodingPropagatesToStreamWriter() + { + var process = CreateProcessPortable(RemotelyInvokable.Dummy); + process.StartInfo.RedirectStandardInput = true; + var encoding = new UTF32Encoding(bigEndian: false, byteOrderMark: true); + process.StartInfo.StandardInputEncoding = encoding; + process.Start(); + + Assert.Same(encoding, process.StandardInput.Encoding); + } + } +} diff --git a/external/corefx/src/System.Diagnostics.Process/tests/ProcessThreadTests.cs b/external/corefx/src/System.Diagnostics.Process/tests/ProcessThreadTests.cs index d8696ec5b9..cda6074d3f 100644 --- a/external/corefx/src/System.Diagnostics.Process/tests/ProcessThreadTests.cs +++ b/external/corefx/src/System.Diagnostics.Process/tests/ProcessThreadTests.cs @@ -69,7 +69,7 @@ namespace System.Diagnostics.Tests } [Fact] - [PlatformSpecific(TestPlatforms.OSX)] // OSX throws PNSE from StartTime + [PlatformSpecific(TestPlatforms.OSX|TestPlatforms.FreeBSD)] // OSX and FreeBSD throw PNSE from StartTime public void TestStartTimeProperty_OSX() { using (Process p = Process.GetCurrentProcess()) @@ -86,7 +86,7 @@ namespace System.Diagnostics.Tests } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] // OSX throws PNSE from StartTime + [PlatformSpecific(TestPlatforms.Linux|TestPlatforms.Windows)] // OSX and FreeBSD throw PNSE from StartTime [SkipOnTargetFramework(TargetFrameworkMonikers.Uap, "Retrieving information about local processes is not supported on uap")] public async Task TestStartTimeProperty() { @@ -178,6 +178,12 @@ namespace System.Diagnostics.Tests return; } + if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("FREEBSD"))) + { + Assert.Throws(() => thread.PriorityLevel = ThreadPriorityLevel.AboveNormal); + return; + } + try { thread.PriorityLevel = ThreadPriorityLevel.AboveNormal; diff --git a/external/corefx/src/System.Diagnostics.Process/tests/RemotelyInvokable.cs b/external/corefx/src/System.Diagnostics.Process/tests/RemotelyInvokable.cs index 43e0ffb205..4d0ba8d3d5 100644 --- a/external/corefx/src/System.Diagnostics.Process/tests/RemotelyInvokable.cs +++ b/external/corefx/src/System.Diagnostics.Process/tests/RemotelyInvokable.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading; @@ -21,6 +22,8 @@ namespace System.Diagnostics.Tests public static readonly int SuccessExitCode = 42; public const int WaitInMS = 30 * 1000; public const string TestConsoleApp = "System.Diagnostics.Process.Tests"; + public static event EventHandler ClosedEvent; + public static string DummyUapCmd() { @@ -111,6 +114,22 @@ namespace System.Diagnostics.Tests return SuccessExitCode; } + public static int ReadLineWithCustomEncodingWriteLineWithUtf8(string inputEncoding) + { + string line; + using (var inputReader = new StreamReader(Console.OpenStandardInput(), Encoding.GetEncoding(inputEncoding))) + { + line = inputReader.ReadLine(); + } + + using (var outputWriter = new StreamWriter(Console.OpenStandardOutput(), Encoding.UTF8)) + { + outputWriter.WriteLine(line); + } + + return SuccessExitCode; + } + public static string WriteSlowlyByByteUapCmd() { throw new Exception("No simple way of doing this using cmd.exe"); @@ -144,6 +163,26 @@ namespace System.Diagnostics.Tests return SuccessExitCode; } + public static int WriteLinesAfterClose() + { + ClosedEvent += (s, e) => + { + Console.WriteLine("This is a line to output."); + Console.Error.WriteLine("This is a line to error."); + }; + return SuccessExitCode; + } + + public static string WriteLinesAfterCloseUapCmd() + { + ClosedEvent += (s, e) => + { + // Finish the pause + Console.WriteLine(); + }; + return "(pause > nul) & (echo This is a line to output) & (echo This is a line to error 1>&2)"; + } + public static string ConcatThreeArgumentsUapCmd(string one, string two, string three) { return $"echo {string.Join(",", one, two, three)} & exit {SuccessExitCode}"; @@ -165,5 +204,10 @@ namespace System.Diagnostics.Tests Process.GetCurrentProcess().Kill(); throw new ShouldNotBeInvokedException(); } + + public static void FireClosedEvent() + { + ClosedEvent?.Invoke(null, EventArgs.Empty); + } } } diff --git a/external/corefx/src/System.Diagnostics.Process/tests/System.Diagnostics.Process.Tests.csproj b/external/corefx/src/System.Diagnostics.Process/tests/System.Diagnostics.Process.Tests.csproj index 3ccd854fc0..fd381bccd9 100644 --- a/external/corefx/src/System.Diagnostics.Process/tests/System.Diagnostics.Process.Tests.csproj +++ b/external/corefx/src/System.Diagnostics.Process/tests/System.Diagnostics.Process.Tests.csproj @@ -6,7 +6,6 @@ true $(DefineConstants);TargetsWindows - @@ -45,6 +44,10 @@ + + + + {69e46a6f-9966-45a5-8945-2559fe337827} diff --git a/external/corefx/src/System.Diagnostics.StackTrace/ref/System.Diagnostics.StackTrace.cs b/external/corefx/src/System.Diagnostics.StackTrace/ref/System.Diagnostics.StackTrace.cs index 1fe3c34c05..9a9d62d034 100644 --- a/external/corefx/src/System.Diagnostics.StackTrace/ref/System.Diagnostics.StackTrace.cs +++ b/external/corefx/src/System.Diagnostics.StackTrace/ref/System.Diagnostics.StackTrace.cs @@ -5,7 +5,6 @@ // Changes to this file must follow the http://aka.ms/api-review process. // ------------------------------------------------------------------------------ - namespace System.Diagnostics { public partial class StackFrame @@ -52,7 +51,6 @@ namespace System.Diagnostics public override string ToString() { throw null; } } } - namespace System.Diagnostics.SymbolStore { public partial interface ISymbolBinder @@ -172,9 +170,9 @@ namespace System.Diagnostics.SymbolStore NativeSectionOffset = 10, NativeStackRegister = 8, } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] - public partial struct SymbolToken + public readonly partial struct SymbolToken { + private readonly object _dummy; public SymbolToken(int val) { throw null; } public bool Equals(System.Diagnostics.SymbolStore.SymbolToken obj) { throw null; } public override bool Equals(object obj) { throw null; } diff --git a/external/corefx/src/System.Diagnostics.StackTrace/src/System/Diagnostics/SymbolStore/SymbolToken.cs b/external/corefx/src/System.Diagnostics.StackTrace/src/System/Diagnostics/SymbolStore/SymbolToken.cs index c7a07a30f4..94badd217f 100644 --- a/external/corefx/src/System.Diagnostics.StackTrace/src/System/Diagnostics/SymbolStore/SymbolToken.cs +++ b/external/corefx/src/System.Diagnostics.StackTrace/src/System/Diagnostics/SymbolStore/SymbolToken.cs @@ -4,7 +4,7 @@ namespace System.Diagnostics.SymbolStore { - public struct SymbolToken + public readonly struct SymbolToken { private readonly int _token; diff --git a/external/corefx/src/System.Diagnostics.StackTrace/tests/System.Diagnostics.StackTrace.Tests.csproj b/external/corefx/src/System.Diagnostics.StackTrace/tests/System.Diagnostics.StackTrace.Tests.csproj index 0c2de01add..dc621b9ab5 100644 --- a/external/corefx/src/System.Diagnostics.StackTrace/tests/System.Diagnostics.StackTrace.Tests.csproj +++ b/external/corefx/src/System.Diagnostics.StackTrace/tests/System.Diagnostics.StackTrace.Tests.csproj @@ -8,7 +8,6 @@ - diff --git a/external/corefx/src/System.Diagnostics.TextWriterTraceListener/src/System.Diagnostics.TextWriterTraceListener.csproj b/external/corefx/src/System.Diagnostics.TextWriterTraceListener/src/System.Diagnostics.TextWriterTraceListener.csproj index c6a45d748b..ee664e151a 100644 --- a/external/corefx/src/System.Diagnostics.TextWriterTraceListener/src/System.Diagnostics.TextWriterTraceListener.csproj +++ b/external/corefx/src/System.Diagnostics.TextWriterTraceListener/src/System.Diagnostics.TextWriterTraceListener.csproj @@ -6,7 +6,6 @@ System.Diagnostics.TextWriterTraceListener {315929D9-D76E-47E9-BE82-C787FB3A7876} - @@ -28,4 +27,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Diagnostics.TextWriterTraceListener/tests/System.Diagnostics.TextWriterTraceListener.Tests.csproj b/external/corefx/src/System.Diagnostics.TextWriterTraceListener/tests/System.Diagnostics.TextWriterTraceListener.Tests.csproj index bceee637e1..5dbd29b5b2 100644 --- a/external/corefx/src/System.Diagnostics.TextWriterTraceListener/tests/System.Diagnostics.TextWriterTraceListener.Tests.csproj +++ b/external/corefx/src/System.Diagnostics.TextWriterTraceListener/tests/System.Diagnostics.TextWriterTraceListener.Tests.csproj @@ -4,7 +4,6 @@ {92A9467A-9F7E-4294-A7D5-7B59F2E54ABE} - diff --git a/external/corefx/src/System.Diagnostics.Tools/ref/System.Diagnostics.Tools.cs b/external/corefx/src/System.Diagnostics.Tools/ref/System.Diagnostics.Tools.cs index b19a46c1b2..4547df7367 100644 --- a/external/corefx/src/System.Diagnostics.Tools/ref/System.Diagnostics.Tools.cs +++ b/external/corefx/src/System.Diagnostics.Tools/ref/System.Diagnostics.Tools.cs @@ -18,7 +18,7 @@ namespace System.CodeDom.Compiler } namespace System.Diagnostics.CodeAnalysis { - [System.AttributeUsageAttribute((System.AttributeTargets)748, Inherited=false, AllowMultiple=false)] + [System.AttributeUsageAttribute((System.AttributeTargets)749, Inherited=false, AllowMultiple=false)] public sealed partial class ExcludeFromCodeCoverageAttribute : System.Attribute { public ExcludeFromCodeCoverageAttribute() { } } diff --git a/external/corefx/src/System.Diagnostics.Tools/tests/System.Diagnostics.Tools.Tests.csproj b/external/corefx/src/System.Diagnostics.Tools/tests/System.Diagnostics.Tools.Tests.csproj index d625b2b62b..f3227b51aa 100644 --- a/external/corefx/src/System.Diagnostics.Tools/tests/System.Diagnostics.Tools.Tests.csproj +++ b/external/corefx/src/System.Diagnostics.Tools/tests/System.Diagnostics.Tools.Tests.csproj @@ -6,7 +6,6 @@ System System.Diagnostics.Tools.Tests - diff --git a/external/corefx/src/System.Diagnostics.TraceSource/tests/System.Diagnostics.TraceSource.Tests.csproj b/external/corefx/src/System.Diagnostics.TraceSource/tests/System.Diagnostics.TraceSource.Tests.csproj index bcdb892b39..9b48bf5616 100644 --- a/external/corefx/src/System.Diagnostics.TraceSource/tests/System.Diagnostics.TraceSource.Tests.csproj +++ b/external/corefx/src/System.Diagnostics.TraceSource/tests/System.Diagnostics.TraceSource.Tests.csproj @@ -1,4 +1,4 @@ - + @@ -6,7 +6,6 @@ System.Diagnostics.TraceSource.Tests {7B32D24D-969A-4F7F-8461-B43E15E5D553} - @@ -33,4 +32,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Diagnostics.Tracing/ref/System.Diagnostics.Tracing.cs b/external/corefx/src/System.Diagnostics.Tracing/ref/System.Diagnostics.Tracing.cs index 5361718d29..992ac79f01 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/ref/System.Diagnostics.Tracing.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/ref/System.Diagnostics.Tracing.cs @@ -168,7 +168,7 @@ namespace System.Diagnostics.Tracing public EventSource(string eventSourceName, System.Diagnostics.Tracing.EventSourceSettings config) { } public EventSource(string eventSourceName, System.Diagnostics.Tracing.EventSourceSettings config, params string[] traits) { } public System.Exception ConstructionException { get { throw null; } } - public static System.Guid CurrentThreadActivityId {[System.Security.SecuritySafeCriticalAttribute]get { throw null; } } + public static System.Guid CurrentThreadActivityId { get { throw null; } } public System.Guid Guid { get { throw null; } } public string Name { get { throw null; } } public System.Diagnostics.Tracing.EventSourceSettings Settings { get { throw null; } } @@ -215,15 +215,14 @@ namespace System.Diagnostics.Tracing protected void WriteEvent(int eventId, string arg1, string arg2) { } protected void WriteEvent(int eventId, string arg1, string arg2, string arg3) { } [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] protected unsafe void WriteEventCore(int eventId, int eventDataCount, System.Diagnostics.Tracing.EventSource.EventData* data) { } protected void WriteEventWithRelatedActivityId(int eventId, System.Guid relatedActivityId, params object[] args) { } [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, System.Guid* relatedActivityId, int eventDataCount, System.Diagnostics.Tracing.EventSource.EventData* data) { } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] protected internal partial struct EventData { + private int _dummy; public System.IntPtr DataPointer { get { throw null; } set { } } public int Size { get { throw null; } set { } } } @@ -253,6 +252,7 @@ namespace System.Diagnostics.Tracing [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct EventSourceOptions { + private int _dummy; public System.Diagnostics.Tracing.EventActivityOptions ActivityOptions { get { throw null; } set { } } public System.Diagnostics.Tracing.EventKeywords Keywords { get { throw null; } set { } } public System.Diagnostics.Tracing.EventLevel Level { get { throw null; } set { } } @@ -279,7 +279,7 @@ namespace System.Diagnostics.Tracing public partial class EventWrittenEventArgs : System.EventArgs { internal EventWrittenEventArgs() { } - public System.Guid ActivityId {[System.Security.SecurityCriticalAttribute]get { throw null; } } + public System.Guid ActivityId { get { throw null; } } public System.Diagnostics.Tracing.EventChannel Channel { get { throw null; } } public int EventId { get { throw null; } } public string EventName { get { throw null; } } @@ -290,7 +290,7 @@ namespace System.Diagnostics.Tracing public System.Diagnostics.Tracing.EventOpcode Opcode { get { throw null; } } public System.Collections.ObjectModel.ReadOnlyCollection Payload { get { throw null; } } public System.Collections.ObjectModel.ReadOnlyCollection PayloadNames { get { throw null; } } - public System.Guid RelatedActivityId {[System.Security.SecurityCriticalAttribute]get { throw null; } } + public System.Guid RelatedActivityId { get { throw null; } } public System.Diagnostics.Tracing.EventTags Tags { get { throw null; } } public System.Diagnostics.Tracing.EventTask Task { get { throw null; } } public byte Version { get { throw null; } } diff --git a/external/corefx/src/System.Diagnostics.Tracing/src/System/Diagnostics/Tracing/EventCounter.cs b/external/corefx/src/System.Diagnostics.Tracing/src/System/Diagnostics/Tracing/EventCounter.cs index 482909066f..88ab685723 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/src/System/Diagnostics/Tracing/EventCounter.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/src/System/Diagnostics/Tracing/EventCounter.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics; using System.Collections; using System.Collections.Generic; using System.Threading; @@ -361,7 +362,7 @@ namespace System.Diagnostics.Tracing { lock (s_eventCounterGroupsLock) { - int eventSourceIndex = EventListenerHelper.EventSourceIndex(eventSource); + int eventSourceIndex = EventListener.EventSourceIndex(eventSource); EnsureEventSourceIndexAvailable(eventSourceIndex); WeakReference weakRef = EventCounterGroup.s_eventCounterGroups[eventSourceIndex]; EventCounterGroup ret = null; @@ -486,14 +487,5 @@ namespace System.Diagnostics.Tracing } - // This class a work-around because .NET V4.6.2 did not make EventSourceIndex public (it was only protected) - // We make this helper class to get around that protection. We want V4.6.3 to make this public at which - // point this class is no longer needed and can be removed. - internal class EventListenerHelper : EventListener - { - public new static int EventSourceIndex(EventSource eventSource) { return EventListener.EventSourceIndex(eventSource); } - protected override void OnEventWritten(EventWrittenEventArgs eventData) { } // override abstract methods to keep compiler happy - } - #endregion // internal supporting classes } diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/EventSourceTestParser.cs.REMOVED.git-id b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/EventSourceTestParser.cs.REMOVED.git-id index 0f403d579a..9a7cae9f7f 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/EventSourceTestParser.cs.REMOVED.git-id +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/EventSourceTestParser.cs.REMOVED.git-id @@ -1 +1 @@ -57b27fba8c88ef6dff722a058305046cdab2f9bd \ No newline at end of file +dd1550f4922cefa159bd7c0ba0f443c8e3358bb4 \ No newline at end of file diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/FuzzyTests.cs.REMOVED.git-id b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/FuzzyTests.cs.REMOVED.git-id index a45a9b92bf..4f7403c86d 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/FuzzyTests.cs.REMOVED.git-id +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/FuzzyTests.cs.REMOVED.git-id @@ -1 +1 @@ -72476cb0a5b15d6b7a21b27d78fc441d15826544 \ No newline at end of file +bb586b47b4037bb121d1552ff1fceefad32b929c \ No newline at end of file diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/EventTestHarness.cs b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/EventTestHarness.cs index 5dff1851fb..fc1b1de920 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/EventTestHarness.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/EventTestHarness.cs @@ -2,7 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if USE_MDT_EVENTSOURCE +using Microsoft.Diagnostics.Tracing; +#else using System.Diagnostics.Tracing; +#endif using Xunit; using System; using System.Collections.Generic; diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/Listeners.cs b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/Listeners.cs index ff083de8f6..e56fc8b2b2 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/Listeners.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/Harness/Listeners.cs @@ -8,7 +8,11 @@ using Microsoft.Diagnostics.Tracing.Session; using System; using System.Collections.Generic; using System.Diagnostics; +#if USE_MDT_EVENTSOURCE +using Microsoft.Diagnostics.Tracing; +#else using System.Diagnostics.Tracing; +#endif using System.IO; using System.Text; using System.Threading; diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/LoudListener.cs b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/LoudListener.cs index f5280d0dbe..7f26da5868 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/LoudListener.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/LoudListener.cs @@ -4,7 +4,11 @@ using System; using System.Diagnostics; +#if USE_MDT_EVENTSOURCE +using Microsoft.Diagnostics.Tracing; +#else using System.Diagnostics.Tracing; +#endif namespace BasicEventSourceTests { diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestEventCounter.cs b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestEventCounter.cs index 1f655227ea..cbd86c5e84 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestEventCounter.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestEventCounter.cs @@ -2,7 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. + +#if USE_MDT_EVENTSOURCE +using Microsoft.Diagnostics.Tracing; +#else using System.Diagnostics.Tracing; +#endif #if USE_ETW // TODO: Enable when TraceEvent is available on CoreCLR. GitHub issue https://github.com/dotnet/corefx/issues/4864 using Microsoft.Diagnostics.Tracing.Session; #endif @@ -43,7 +48,9 @@ namespace BasicEventSourceTests } [Fact] +#if !USE_MDT_EVENTSOURCE [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, reason: "https://github.com/dotnet/corefx/issues/23661")] +#endif [ActiveIssue("https://github.com/dotnet/corefx/issues/22791", TargetFrameworkMonikers.UapAot)] public void Test_Write_Metric_EventListener() { diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestFilter.cs b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestFilter.cs index bbfb09e855..c4486a519b 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestFilter.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestFilter.cs @@ -4,7 +4,11 @@ using System; using System.Collections.ObjectModel; +#if USE_MDT_EVENTSOURCE +using Microsoft.Diagnostics.Tracing; +#else using System.Diagnostics.Tracing; +#endif using Xunit; namespace BasicEventSourceTests diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestShutdown.cs b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestShutdown.cs index 13c8ca94d0..b88408d1cd 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestShutdown.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestShutdown.cs @@ -7,7 +7,11 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +#if USE_MDT_EVENTSOURCE +using Microsoft.Diagnostics.Tracing; +#else using System.Diagnostics.Tracing; +#endif using Xunit; #if USE_ETW // TODO: Enable when TraceEvent is available on CoreCLR. GitHub issue #4864. using Microsoft.Diagnostics.Tracing.Session; diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestUtilities.cs b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestUtilities.cs index bbf8c005e5..42970594d2 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestUtilities.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestUtilities.cs @@ -2,7 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if USE_MDT_EVENTSOURCE +using Microsoft.Diagnostics.Tracing; +#else using System.Diagnostics.Tracing; +#endif using System.Diagnostics; using Xunit; using System; diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsEventSourceLifetime.cs b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsEventSourceLifetime.cs index f9d1ffd2fd..3e313266e8 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsEventSourceLifetime.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsEventSourceLifetime.cs @@ -8,7 +8,11 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using Xunit; +#if USE_MDT_EVENTSOURCE +using Microsoft.Diagnostics.Tracing; +#else using System.Diagnostics.Tracing; +#endif using System.Reflection; namespace BasicEventSourceTests diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsManifestGeneration.cs b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsManifestGeneration.cs index 26d6f21d4c..927deb0d0e 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsManifestGeneration.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsManifestGeneration.cs @@ -7,12 +7,15 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +#if USE_MDT_EVENTSOURCE +using Microsoft.Diagnostics.Tracing; +#else using System.Diagnostics.Tracing; +#endif using Xunit; using System.Reflection; -//using Mdt = MdtEventSources; -using Sdt = SdtEventSources; +using SdtEventSources; using System.Diagnostics; using System.Threading; using System.Text.RegularExpressions; diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsManifestNegative.cs b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsManifestNegative.cs index 13b392930e..922168d8b8 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsManifestNegative.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsManifestNegative.cs @@ -9,7 +9,11 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +#if USE_MDT_EVENTSOURCE +using Microsoft.Diagnostics.Tracing; +#else using System.Diagnostics.Tracing; +#endif using Xunit; using Sdt = SdtEventSources; diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsTraits.cs b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsTraits.cs index f293f21375..a6fd649094 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsTraits.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsTraits.cs @@ -2,7 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if USE_MDT_EVENTSOURCE +using Microsoft.Diagnostics.Tracing; +#else using System.Diagnostics.Tracing; +#endif using Xunit; using System; diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsUserErrors.cs b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsUserErrors.cs index 58dae42aaf..046b5e4935 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsUserErrors.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsUserErrors.cs @@ -2,7 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if USE_MDT_EVENTSOURCE +using Microsoft.Diagnostics.Tracing; +#else using System.Diagnostics.Tracing; +#endif using Xunit; using System; using System.Collections.Generic; @@ -138,8 +142,9 @@ namespace BasicEventSourceTests Assert.Equal("EventSourceMessage", _event.EventName); string message = _event.PayloadString(0, "message"); Debug.WriteLine(String.Format("Message=\"{0}\"", message)); - // expected message: "ERROR: Exception in Command Processing for EventSource BadEventSource_MismatchedIds: Event Event2 is given event ID 2 but 1 was passed to WriteEvent. " - Assert.True(Regex.IsMatch(message, "Event Event2 is givien event ID 2 but 1 was passed to WriteEvent")); + // expected message: "ERROR: Exception in Command Processing for EventSource BadEventSource_MismatchedIds: Event Event2 was assigned event ID 2 but 1 was passed to WriteEvent. " + if (!PlatformDetection.IsFullFramework) // Full framework has typo + Assert.True(Regex.IsMatch(message, "Event Event2 was assigned event ID 2 but 1 was passed to WriteEvent")); } [Fact] diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWrite.cs b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWrite.cs index 207cbd0ea2..5743bbe8d8 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWrite.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWrite.cs @@ -7,7 +7,11 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +#if USE_MDT_EVENTSOURCE +using Microsoft.Diagnostics.Tracing; +#else using System.Diagnostics.Tracing; +#endif using Xunit; #if USE_ETW // TODO: Enable when TraceEvent is available on CoreCLR. GitHub issue #4864. using Microsoft.Diagnostics.Tracing.Session; diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEvent.cs b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEvent.cs index 1f8761680d..080f6d7242 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEvent.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEvent.cs @@ -11,7 +11,11 @@ using System.Threading.Tasks; using System.Threading; using Xunit; +#if USE_MDT_EVENTSOURCE +using Microsoft.Diagnostics.Tracing; +#else using System.Diagnostics.Tracing; +#endif using System.Text.RegularExpressions; using System.Diagnostics; using SdtEventSources; diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEventToListener.cs b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEventToListener.cs index 1cc1125c7d..4cbc1c20d1 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEventToListener.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEventToListener.cs @@ -8,7 +8,11 @@ using System.Linq; using System.Text; using Xunit; using System.Threading; +#if USE_MDT_EVENTSOURCE +using Microsoft.Diagnostics.Tracing; +#else using System.Diagnostics.Tracing; +#endif using SdtEventSources; namespace BasicEventSourceTests diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/EventSourceTest.cs b/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/EventSourceTest.cs index 9757329e4b..6261ab7a4a 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/EventSourceTest.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/EventSourceTest.cs @@ -5,7 +5,11 @@ using System; using System.Linq; using System.Diagnostics; +#if USE_MDT_EVENTSOURCE +using Microsoft.Diagnostics.Tracing; +#else using System.Diagnostics.Tracing; +#endif // We wish to test both Microsoft.Diagnostics.Tracing (Nuget) // and System.Diagnostics.Tracing (Framework), we use this Ifdef make each kind diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/InvalidCallsToWriteEvent.cs b/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/InvalidCallsToWriteEvent.cs index a2883364d0..4d1939d2ed 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/InvalidCallsToWriteEvent.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/InvalidCallsToWriteEvent.cs @@ -3,7 +3,11 @@ // See the LICENSE file in the project root for more information. using System; +#if USE_MDT_EVENTSOURCE +using Microsoft.Diagnostics.Tracing; +#else using System.Diagnostics.Tracing; +#endif // We wish to test both Microsoft.Diagnostics.Tracing (Nuget) // and System.Diagnostics.Tracing (Framework), we use this Ifdef make each kind diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/InvalidEventSources.cs b/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/InvalidEventSources.cs index cac5b01e57..df49cf895d 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/InvalidEventSources.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/InvalidEventSources.cs @@ -3,7 +3,11 @@ // See the LICENSE file in the project root for more information. using System; +#if USE_MDT_EVENTSOURCE +using Microsoft.Diagnostics.Tracing; +#else using System.Diagnostics.Tracing; +#endif // We wish to test both Microsoft.Diagnostics.Tracing (Nuget) // and System.Diagnostics.Tracing (Framework), we use this Ifdef make each kind diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/SimpleEventSource.cs b/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/SimpleEventSource.cs index c4a416fea6..30776897b0 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/SimpleEventSource.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/SimpleEventSource.cs @@ -5,7 +5,11 @@ // #define FEATURE_ADVANCED_MANAGED_ETW_CHANNELS using System; +#if USE_MDT_EVENTSOURCE +using Microsoft.Diagnostics.Tracing; +#else using System.Diagnostics.Tracing; +#endif // We wish to test both Microsoft.Diagnostics.Tracing (Nuget) // and System.Diagnostics.Tracing (Framework), we use this Ifdef make each kind @@ -17,7 +21,11 @@ namespace SdtEventSources namespace DontPollute { public sealed class EventSource : +#if USE_MDT_EVENTSOURCE + Microsoft.Diagnostics.Tracing.EventSource +#else System.Diagnostics.Tracing.EventSource +#endif { [Event(1)] public void EventWrite(int i) { this.WriteEvent(1, i); } diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/UseAbstractEventSource.cs b/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/UseAbstractEventSource.cs index 74044f409c..2e6bebbe2a 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/UseAbstractEventSource.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/UseAbstractEventSource.cs @@ -4,7 +4,11 @@ using System; +#if USE_MDT_EVENTSOURCE +using Microsoft.Diagnostics.Tracing; +#else using System.Diagnostics.Tracing; +#endif // We wish to test both Microsoft.Diagnostics.Tracing (Nuget) // and System.Diagnostics.Tracing (Framework), we use this Ifdef make each kind diff --git a/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/UseInterfaceEventSource.cs b/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/UseInterfaceEventSource.cs index f77edd0991..59aa528d99 100644 --- a/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/UseInterfaceEventSource.cs +++ b/external/corefx/src/System.Diagnostics.Tracing/tests/CustomEventSources/UseInterfaceEventSource.cs @@ -4,7 +4,11 @@ using System; +#if USE_MDT_EVENTSOURCE +using Microsoft.Diagnostics.Tracing; +#else using System.Diagnostics.Tracing; +#endif // We wish to test both Microsoft.Diagnostics.Tracing (Nuget) // and System.Diagnostics.Tracing (Framework), we use this Ifdef make each kind diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/System.DirectoryServices.AccountManagement.sln b/external/corefx/src/System.DirectoryServices.AccountManagement/System.DirectoryServices.AccountManagement.sln index 937ee171d6..1843d15e4b 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/System.DirectoryServices.AccountManagement.sln +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/System.DirectoryServices.AccountManagement.sln @@ -30,10 +30,10 @@ Global {B0EE498E-4BD8-4A39-89EB-7F2FC880B61F}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU {B0EE498E-4BD8-4A39-89EB-7F2FC880B61F}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU {B0EE498E-4BD8-4A39-89EB-7F2FC880B61F}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU - {879C23DC-D828-4DFB-8E92-ABBC11B71035}.Debug|Any CPU.ActiveCfg = netstandard-Windows_NT-Debug|Any CPU - {879C23DC-D828-4DFB-8E92-ABBC11B71035}.Debug|Any CPU.Build.0 = netstandard-Windows_NT-Debug|Any CPU - {879C23DC-D828-4DFB-8E92-ABBC11B71035}.Release|Any CPU.ActiveCfg = netstandard-Windows_NT-Release|Any CPU - {879C23DC-D828-4DFB-8E92-ABBC11B71035}.Release|Any CPU.Build.0 = netstandard-Windows_NT-Release|Any CPU + {879C23DC-D828-4DFB-8E92-ABBC11B71035}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {879C23DC-D828-4DFB-8E92-ABBC11B71035}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {879C23DC-D828-4DFB-8E92-ABBC11B71035}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {879C23DC-D828-4DFB-8E92-ABBC11B71035}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU {404455B6-466C-4F78-9DCC-C37DCC0B75DA}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU {404455B6-466C-4F78-9DCC-C37DCC0B75DA}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU {404455B6-466C-4F78-9DCC-C37DCC0B75DA}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/ref/System.DirectoryServices.AccountManagement.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/ref/System.DirectoryServices.AccountManagement.cs index 64a0d48287..1217f1b85f 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/ref/System.DirectoryServices.AccountManagement.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/ref/System.DirectoryServices.AccountManagement.cs @@ -156,7 +156,7 @@ namespace System.DirectoryServices.AccountManagement { } public abstract partial class Principal : System.IDisposable { protected Principal() { } - public System.DirectoryServices.AccountManagement.PrincipalContext Context { [System.Security.SecuritySafeCriticalAttribute]get { return default(System.DirectoryServices.AccountManagement.PrincipalContext); } } + public System.DirectoryServices.AccountManagement.PrincipalContext Context { get { return default(System.DirectoryServices.AccountManagement.PrincipalContext); } } protected internal System.DirectoryServices.AccountManagement.PrincipalContext ContextRaw { get { return default(System.DirectoryServices.AccountManagement.PrincipalContext); } set { } } public System.DirectoryServices.AccountManagement.ContextType ContextType { get { return default(System.DirectoryServices.AccountManagement.ContextType); } } public string Description { get { return default(string); } set { } } @@ -261,7 +261,7 @@ namespace System.DirectoryServices.AccountManagement { public PrincipalSearcher() { } public PrincipalSearcher(System.DirectoryServices.AccountManagement.Principal queryFilter) { } - public System.DirectoryServices.AccountManagement.PrincipalContext Context { [System.Security.SecuritySafeCriticalAttribute]get { return default(System.DirectoryServices.AccountManagement.PrincipalContext); } } + public System.DirectoryServices.AccountManagement.PrincipalContext Context { get { return default(System.DirectoryServices.AccountManagement.PrincipalContext); } } public System.DirectoryServices.AccountManagement.Principal QueryFilter { get { return default(System.DirectoryServices.AccountManagement.Principal); } set { } } public virtual void Dispose() { } diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/Configurations.props b/external/corefx/src/System.DirectoryServices.AccountManagement/src/Configurations.props index 64d654a29f..94ac07fdab 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/Configurations.props +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/Configurations.props @@ -1,9 +1,13 @@ - - netstandard-Windows_NT; + netstandard; + netcoreapp2.0-Windows_NT; + + + $(PackageConfigurations); + netcoreapp-Windows_NT; diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/PinvokeAnalyzerExceptionList.analyzerdata.netstandard b/external/corefx/src/System.DirectoryServices.AccountManagement/src/PinvokeAnalyzerExceptionList.analyzerdata similarity index 100% rename from external/corefx/src/System.DirectoryServices.AccountManagement/src/PinvokeAnalyzerExceptionList.analyzerdata.netstandard rename to external/corefx/src/System.DirectoryServices.AccountManagement/src/PinvokeAnalyzerExceptionList.analyzerdata diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/Resources/Strings.resx b/external/corefx/src/System.DirectoryServices.AccountManagement/src/Resources/Strings.resx index dd52d81e9c..204ccbc8ad 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/Resources/Strings.resx +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/Resources/Strings.resx @@ -498,4 +498,7 @@ Unknown error (0x{0}) + + System.DirectoryServices.AccountManagement is not supported on this platform. + \ No newline at end of file diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System.DirectoryServices.AccountManagement.csproj b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System.DirectoryServices.AccountManagement.csproj index 7a653a3ece..6d7ef42085 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System.DirectoryServices.AccountManagement.csproj +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System.DirectoryServices.AccountManagement.csproj @@ -10,13 +10,15 @@ false true - true + SR.DirectoryServicesAccountManagement_PlatformNotSupported - - + + + + - + @@ -102,5 +104,27 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADAMStoreCtx.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADAMStoreCtx.cs index c687da7c84..ccfbc4f66b 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADAMStoreCtx.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADAMStoreCtx.cs @@ -17,9 +17,6 @@ using System.Text; namespace System.DirectoryServices.AccountManagement { -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 internal partial class ADAMStoreCtx : ADStoreCtx { private const int mappingIndex = 1; diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADDNConstraintLinkedAttrSet.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADDNConstraintLinkedAttrSet.cs index 9cc6afb5fd..75923d7b13 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADDNConstraintLinkedAttrSet.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADDNConstraintLinkedAttrSet.cs @@ -10,9 +10,6 @@ using System.Diagnostics; namespace System.DirectoryServices.AccountManagement { -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 internal class ADDNConstraintLinkedAttrSet : ADDNLinkedAttrSet { /// diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADDNLinkedAttrSet.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADDNLinkedAttrSet.cs index ad66875123..494acd595e 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADDNLinkedAttrSet.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADDNLinkedAttrSet.cs @@ -11,9 +11,6 @@ using System.Security.Principal; namespace System.DirectoryServices.AccountManagement { -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 internal class ADDNLinkedAttrSet : BookmarkableResultSet { // This class can be used to either enumerate the members of a group, or the groups diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADEntriesSet.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADEntriesSet.cs index c938b95ba7..a7549e06c9 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADEntriesSet.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADEntriesSet.cs @@ -10,9 +10,6 @@ using System.DirectoryServices; namespace System.DirectoryServices.AccountManagement { -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 internal class ADEntriesSet : ResultSet { private SearchResultCollection _searchResults; diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADStoreCtx_Query.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADStoreCtx_Query.cs index 0dfce32d0d..e75fdca491 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADStoreCtx_Query.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADStoreCtx_Query.cs @@ -14,9 +14,6 @@ using System.Collections.Specialized; namespace System.DirectoryServices.AccountManagement { -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 internal partial class ADStoreCtx : StoreCtx { // diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADUtils.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADUtils.cs index 2c7bf60bb6..20a2e96702 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADUtils.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADUtils.cs @@ -23,20 +23,17 @@ namespace System.DirectoryServices.AccountManagement // Note that, since computer is a derived class of user in AD, if you don't want to confuse // computers with users, you must test an object for computer status before testing it for // user status. - [System.Security.SecurityCritical] static internal bool IsOfObjectClass(DirectoryEntry de, string classToCompare) { return de.Properties["objectClass"].Contains(classToCompare); } - [System.Security.SecurityCritical] static internal bool IsOfObjectClass(SearchResult sr, string classToCompare) { return sr.Properties["objectClass"].Contains(classToCompare); } // Retrieves the name of the actual server that the DirectoryEntry is connected to - [System.Security.SecurityCritical] static internal string GetServerName(DirectoryEntry de) { UnsafeNativeMethods.IAdsObjectOptions objOptions = (UnsafeNativeMethods.IAdsObjectOptions)de.NativeObject; @@ -46,7 +43,6 @@ namespace System.DirectoryServices.AccountManagement // This routine escapes values used in DNs, per RFC 2253 and ADSI escaping rules. // It treats its input as a unescaped literal and produces a LDAP string that represents that literal // and that is escaped according to RFC 2253 and ADSI rules for DN components. - [System.Security.SecurityCritical] static internal string EscapeDNComponent(string dnComponent) { // @@ -153,7 +149,6 @@ namespace System.DirectoryServices.AccountManagement // This routine escapes values used in search filters, per RFC 2254 escaping rules. // It treats its input as a unescaped literal and produces a LDAP string that represents that literal // and that is escaped according to RFC 2254 rules. - [System.Security.SecurityCritical] static internal string EscapeRFC2254SpecialChars(string s) { StringBuilder sb = new StringBuilder(s.Length); @@ -363,7 +358,6 @@ namespace System.DirectoryServices.AccountManagement return sb.ToString(); } - [System.Security.SecurityCritical] static internal bool ArePrincipalsInSameForest(Principal p1, Principal p2) { string p1DnsForestName = ((ADStoreCtx)p1.GetStoreCtxToUse()).DnsForestName; @@ -395,7 +389,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] static internal Principal DirectoryEntryAsPrincipal(DirectoryEntry de, ADStoreCtx storeCtx) { if (ADUtils.IsOfObjectClass(de, "computer") || @@ -414,7 +407,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] static internal Principal SearchResultAsPrincipal(SearchResult sr, ADStoreCtx storeCtx, object discriminant) { if (ADUtils.IsOfObjectClass(sr, "computer") || @@ -439,7 +431,6 @@ namespace System.DirectoryServices.AccountManagement // domain or the current forest and the target domain's forest. // target domain must be the full DNS domain name of the target domain to make the string // compare below work properly. - [System.Security.SecurityCritical] static internal bool VerifyOutboundTrust(string targetDomain, string username, string password) { Domain currentDom = null; @@ -501,7 +492,6 @@ namespace System.DirectoryServices.AccountManagement return false; } - [System.Security.SecurityCritical] static internal string RetriveWkDn(DirectoryEntry deBase, string defaultNamingContext, string serverName, Byte[] wellKnownContainerGuid) { /* diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/DSPropertyCollection.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/DSPropertyCollection.cs index a9a15e993f..4b50adfc13 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/DSPropertyCollection.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/DSPropertyCollection.cs @@ -25,7 +25,6 @@ namespace System.DirectoryServices.AccountManagement public dSPropertyValueCollection this[string propertyName] { - [System.Security.SecurityCritical] get { if (propertyName == null) @@ -54,7 +53,6 @@ namespace System.DirectoryServices.AccountManagement public object this[int index] { - [System.Security.SecurityCritical] get { if (_pc != null) diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/RangeRetriever.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/RangeRetriever.cs index 810f58418b..72a1863b5c 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/RangeRetriever.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/RangeRetriever.cs @@ -16,9 +16,6 @@ namespace System.DirectoryServices.AccountManagement /// if disposeDirEntry parameter is set to true in its constructor. /// /// -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 internal class RangeRetriever : CollectionBase, IEnumerable, IEnumerator, IDisposable { /// diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SDSCache.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SDSCache.cs index 0c660654a0..7ba2206f60 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SDSCache.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SDSCache.cs @@ -34,7 +34,6 @@ namespace System.DirectoryServices.AccountManagement private static SDSCache s_domainCache = new SDSCache(false); private static SDSCache s_localMachineCache = new SDSCache(true); - [System.Security.SecurityCritical] public PrincipalContext GetContext(string name, NetCred credentials, ContextOptions contextOptions) { string contextName = name; diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SDSUtils.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SDSUtils.cs index d365332c1d..d64034cbbc 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SDSUtils.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SDSUtils.cs @@ -18,7 +18,6 @@ namespace System.DirectoryServices.AccountManagement // To stop the compiler from autogenerating a constructor for this class private SDSUtils() { } - [System.Security.SecurityCritical] static internal Principal SearchResultToPrincipal(SearchResult sr, PrincipalContext owningContext, Type principalType) { Principal p; @@ -80,7 +79,6 @@ namespace System.DirectoryServices.AccountManagement return p; } // Used to implement StoreCtx.GetAsPrincipal for AD and SAM - [System.Security.SecurityCritical] static internal Principal DirectoryEntryToPrincipal(DirectoryEntry de, PrincipalContext owningContext, Type principalType) { Principal p; @@ -138,14 +136,12 @@ namespace System.DirectoryServices.AccountManagement return p; } - [System.Security.SecurityCritical] private static bool IsOfObjectClass(SearchResult sr, string className) { Debug.Assert(sr.Path.StartsWith("LDAP:", StringComparison.Ordinal) || sr.Path.StartsWith("GC:", StringComparison.Ordinal)); return ADUtils.IsOfObjectClass(sr, className); } - [System.Security.SecurityCritical] private static bool IsOfObjectClass(DirectoryEntry de, string className) { if (de.Path.StartsWith("WinNT:", StringComparison.Ordinal)) @@ -180,7 +176,6 @@ namespace System.DirectoryServices.AccountManagement return authTypes; } - [System.Security.SecurityCritical] static internal void MoveDirectoryEntry(DirectoryEntry deToMove, DirectoryEntry newParent, string newName) { if (newName != null) @@ -189,7 +184,6 @@ namespace System.DirectoryServices.AccountManagement deToMove.MoveTo(newParent); } - [System.Security.SecurityCritical] static internal void DeleteDirectoryEntry(DirectoryEntry deToDelete) { DirectoryEntry deParent = deToDelete.Parent; @@ -204,7 +198,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] static internal void InsertPrincipal( Principal p, StoreCtx storeCtx, @@ -275,7 +268,6 @@ namespace System.DirectoryServices.AccountManagement internal delegate void GroupMembershipUpdater(Principal p, DirectoryEntry de, NetCred credentials, AuthenticationTypes authTypes); - [System.Security.SecurityCritical] static internal void ApplyChangesToDirectory( Principal p, StoreCtx storeCtx, @@ -317,7 +309,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] static internal void SetPassword(DirectoryEntry de, string newPassword) { Debug.Assert(newPassword != null); // but it could be an empty string @@ -350,7 +341,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] static internal void ChangePassword(DirectoryEntry de, string oldPassword, string newPassword) { Debug.Assert(newPassword != null); // but it could be an empty string @@ -385,7 +375,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] static internal DirectoryEntry BuildDirectoryEntry(string path, NetCred credentials, AuthenticationTypes authTypes) { DirectoryEntry de = new DirectoryEntry(path, @@ -398,7 +387,6 @@ namespace System.DirectoryServices.AccountManagement return de; } - [System.Security.SecurityCritical] static internal DirectoryEntry BuildDirectoryEntry(NetCred credentials, AuthenticationTypes authTypes) { DirectoryEntry de = new DirectoryEntry(); @@ -412,7 +400,6 @@ namespace System.DirectoryServices.AccountManagement return de; } - [System.Security.SecurityCritical] static internal void WriteAttribute(string dePath, string attribute, T value, NetCred credentials, AuthenticationTypes authTypes) { Debug.Assert(attribute != null && attribute.Length > 0); @@ -448,7 +435,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] static internal void WriteAttribute(string dePath, string attribute, int value, NetCred credentials, AuthenticationTypes authTypes) { GlobalDebug.WriteLineIf( @@ -504,7 +490,6 @@ namespace System.DirectoryServices.AccountManagement // // S.DS (LDAP or WinNT) --> PAPI conversion routines // - [System.Security.SecurityCritical] static internal void SingleScalarFromDirectoryEntry(dSPropertyCollection properties, string suggestedProperty, Principal p, string propertyName) { if (properties[suggestedProperty].Count != 0 && properties[suggestedProperty][0] != null) @@ -517,7 +502,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] static internal void MultiScalarFromDirectoryEntry(dSPropertyCollection properties, string suggestedProperty, Principal p, string propertyName) { dSPropertyValueCollection values = properties[suggestedProperty]; @@ -588,7 +572,6 @@ namespace System.DirectoryServices.AccountManagement return flag; } - [System.Security.SecurityCritical] static internal void AccountControlFromDirectoryEntry(dSPropertyCollection properties, string suggestedProperty, Principal p, string propertyName, bool testCantChangePassword) { Debug.Assert( @@ -618,7 +601,6 @@ namespace System.DirectoryServices.AccountManagement // PAPI --> S.DS (LDAP or WinNT) conversion routines // - [System.Security.SecurityCritical] static internal void MultiStringToDirectoryEntryConverter(Principal p, string propertyName, DirectoryEntry de, string suggestedProperty) { PrincipalValueCollection trackingList = (PrincipalValueCollection)p.GetValueForProperty(propertyName); @@ -662,7 +644,6 @@ namespace System.DirectoryServices.AccountManagement internal const int AD_DefaultUAC_Machine = (int)(0x1000 | 0X20 | 0x2); // UF_WORKSTATION_TRUST_ACCOUNT | UF_PASSWD_NOTREQD | UF_ACCOUNTDISABLE internal const int SAM_DefaultUAC = (int)(0x200 | 0x1); // UF_NORMAL_ACCOUNT | UF_SCRIPT - [System.Security.SecurityCritical] static internal void AccountControlToDirectoryEntry( Principal p, string propertyName, @@ -798,7 +779,6 @@ namespace System.DirectoryServices.AccountManagement return ds; } - [System.Security.SecuritySafeCritical] static internal bool IsObjectFromGC(string path) { return path.StartsWith("GC:", StringComparison.OrdinalIgnoreCase); diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SidList.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SidList.cs index 2f21518bd4..ab1bb06f33 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SidList.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SidList.cs @@ -13,10 +13,6 @@ using System.Net; namespace System.DirectoryServices.AccountManagement { -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 - internal class SidList { internal SidList(List sidListByteFormat) : this(sidListByteFormat, null, null) @@ -318,7 +314,6 @@ namespace System.DirectoryServices.AccountManagement // // IDisposable // - [System.Security.SecurityCritical] public virtual void Dispose() { if (pSid != IntPtr.Zero) diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/TokenGroupsSet.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/TokenGroupsSet.cs index 3602801318..10fa1e7927 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/TokenGroupsSet.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/TokenGroupsSet.cs @@ -12,9 +12,6 @@ using System.Security.Principal; namespace System.DirectoryServices.AccountManagement { -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 internal class TokenGroupSet : ResultSet { internal TokenGroupSet( diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AccountInfo.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AccountInfo.cs index 47d8a0c6b0..444e38eedf 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AccountInfo.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AccountInfo.cs @@ -25,7 +25,6 @@ namespace System.DirectoryServices.AccountManagement public Nullable AccountLockoutTime { - [System.Security.SecurityCritical] get { return _owningPrincipal.HandleGet>(ref _accountLockoutTime, PropertyNames.AcctInfoAcctLockoutTime, ref _accountLockoutTimeLoaded); @@ -38,7 +37,6 @@ namespace System.DirectoryServices.AccountManagement public Nullable LastLogon { - [System.Security.SecurityCritical] get { return _owningPrincipal.HandleGet>(ref _lastLogon, PropertyNames.AcctInfoLastLogon, ref _lastLogonLoaded); @@ -51,7 +49,6 @@ namespace System.DirectoryServices.AccountManagement public PrincipalValueCollection PermittedWorkstations { - [System.Security.SecurityCritical] get { if (!_owningPrincipal.GetStoreCtxToUse().IsValidProperty(_owningPrincipal, PropertyNames.AcctInfoPermittedWorkstations)) @@ -73,13 +70,11 @@ namespace System.DirectoryServices.AccountManagement public byte[] PermittedLogonTimes { - [System.Security.SecurityCritical] get { return _owningPrincipal.HandleGet(ref _permittedLogonTimes, PropertyNames.AcctInfoPermittedLogonTimes, ref _permittedLogonTimesLoaded); } - [System.Security.SecurityCritical] set { // We don't use HandleSet here because of the slightly non-standard implementation of the change-tracking @@ -109,13 +104,11 @@ namespace System.DirectoryServices.AccountManagement public Nullable AccountExpirationDate { - [System.Security.SecurityCritical] get { return _owningPrincipal.HandleGet>(ref _expirationDate, PropertyNames.AcctInfoExpirationDate, ref _expirationDateChanged); } - [System.Security.SecurityCritical] set { if (!_owningPrincipal.GetStoreCtxToUse().IsValidProperty(_owningPrincipal, PropertyNames.AcctInfoExpirationDate)) @@ -132,13 +125,11 @@ namespace System.DirectoryServices.AccountManagement public bool SmartcardLogonRequired { - [System.Security.SecurityCritical] get { return _owningPrincipal.HandleGet(ref _smartcardLogonRequired, PropertyNames.AcctInfoSmartcardRequired, ref _smartcardLogonRequiredChanged); } - [System.Security.SecurityCritical] set { if (!_owningPrincipal.GetStoreCtxToUse().IsValidProperty(_owningPrincipal, PropertyNames.AcctInfoSmartcardRequired)) @@ -155,13 +146,11 @@ namespace System.DirectoryServices.AccountManagement public bool DelegationPermitted { - [System.Security.SecurityCritical] get { return _owningPrincipal.HandleGet(ref _delegationPermitted, PropertyNames.AcctInfoDelegationPermitted, ref _delegationPermittedChanged); } - [System.Security.SecurityCritical] set { if (!_owningPrincipal.GetStoreCtxToUse().IsValidProperty(_owningPrincipal, PropertyNames.AcctInfoDelegationPermitted)) @@ -178,7 +167,6 @@ namespace System.DirectoryServices.AccountManagement public int BadLogonCount { - [System.Security.SecurityCritical] get { return _owningPrincipal.HandleGet(ref _badLogonCount, PropertyNames.AcctInfoBadLogonCount, ref _badLogonCountChanged); @@ -191,13 +179,11 @@ namespace System.DirectoryServices.AccountManagement public string HomeDirectory { - [System.Security.SecurityCritical] get { return _owningPrincipal.HandleGet(ref _homeDirectory, PropertyNames.AcctInfoHomeDirectory, ref _homeDirectoryChanged); } - [System.Security.SecurityCritical] set { if (!_owningPrincipal.GetStoreCtxToUse().IsValidProperty(_owningPrincipal, PropertyNames.AcctInfoHomeDirectory)) @@ -214,13 +200,11 @@ namespace System.DirectoryServices.AccountManagement public string HomeDrive { - [System.Security.SecurityCritical] get { return _owningPrincipal.HandleGet(ref _homeDrive, PropertyNames.AcctInfoHomeDrive, ref _homeDriveChanged); } - [System.Security.SecurityCritical] set { if (!_owningPrincipal.GetStoreCtxToUse().IsValidProperty(_owningPrincipal, PropertyNames.AcctInfoHomeDrive)) @@ -237,13 +221,11 @@ namespace System.DirectoryServices.AccountManagement public string ScriptPath { - [System.Security.SecurityCritical] get { return _owningPrincipal.HandleGet(ref _scriptPath, PropertyNames.AcctInfoScriptPath, ref _scriptPathChanged); } - [System.Security.SecurityCritical] set { if (!_owningPrincipal.GetStoreCtxToUse().IsValidProperty(_owningPrincipal, PropertyNames.AcctInfoScriptPath)) @@ -257,7 +239,6 @@ namespace System.DirectoryServices.AccountManagement // // Methods exposed to the public through AuthenticablePrincipal // - [System.Security.SecurityCritical] public bool IsAccountLockedOut() { if (!_owningPrincipal.unpersisted) @@ -275,7 +256,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] public void UnlockAccount() { if (!_owningPrincipal.unpersisted) @@ -294,7 +274,6 @@ namespace System.DirectoryServices.AccountManagement // // Internal constructor // - [System.Security.SecurityCritical] internal AccountInfo(AuthenticablePrincipal principal) { _owningPrincipal = principal; @@ -313,7 +292,6 @@ namespace System.DirectoryServices.AccountManagement // Loading with query results // - [System.Security.SecurityCritical] internal void LoadValueIntoProperty(string propertyName, object value) { // GlobalDebug.WriteLineIf(GlobalDebug.Info, "AccountInfo", "LoadValueIntoProperty: name=" + propertyName + " value=" + value.ToString()); @@ -387,7 +365,6 @@ namespace System.DirectoryServices.AccountManagement // // Given a property name, returns true if that property has changed since it was loaded, false otherwise. - [System.Security.SecurityCritical] internal bool GetChangeStatusForProperty(string propertyName) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "AccountInfo", "GetChangeStatusForProperty: name=" + propertyName); @@ -469,7 +446,6 @@ namespace System.DirectoryServices.AccountManagement } // Reset all change-tracking status for all properties on the object to "unchanged". - [System.Security.SecurityCritical] internal void ResetAllChangeStatus() { GlobalDebug.WriteLineIf(GlobalDebug.Info, "AccountInfo", "ResetAllChangeStatus"); diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AdvancedFilters.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AdvancedFilters.cs index ec9b57bc4b..182157384a 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AdvancedFilters.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AdvancedFilters.cs @@ -153,7 +153,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] protected void AdvancedFilterSet(string attribute, object value, Type objectType, MatchType mt) { _p.AdvancedFilterSet(attribute, value, objectType, mt); @@ -202,7 +201,6 @@ namespace System.DirectoryServices.AccountManagement // If the property is a ValueCollection, the return value is the ValueCollection itself. // If the property is a X509Certificate2Collection, the return value is the X509Certificate2Collection itself. // If the property is a PrincipalCollection, the return value is the PrincipalCollection itself. - //[StrongNameIdentityPermission(SecurityAction.InheritanceDemand, PublicKey = Microsoft.Internal.BuildInfo.WINDOWS_PUBLIC_KEY_STRING)] internal object GetValueForProperty(string propertyName) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "AdvancedFilters", "GetValueForProperty: name=" + propertyName); @@ -235,7 +233,6 @@ namespace System.DirectoryServices.AccountManagement // Reset all change-tracking status for all properties on the object to "unchanged". // This is used by StoreCtx.Insert() and StoreCtx.Update() to reset the change-tracking after they // have persisted all current changes to the store. - //[StrongNameIdentityPermission(SecurityAction.InheritanceDemand, PublicKey = Microsoft.Internal.BuildInfo.WINDOWS_PUBLIC_KEY_STRING)] internal virtual void ResetAllChangeStatus() { GlobalDebug.WriteLineIf(GlobalDebug.Info, "Principal", "ResetAllChangeStatus"); diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AuthZSet.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AuthZSet.cs index 6cef9e8563..4b4f260597 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AuthZSet.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AuthZSet.cs @@ -16,7 +16,6 @@ namespace System.DirectoryServices.AccountManagement { internal class AuthZSet : ResultSet { - [System.Security.SecurityCritical] internal AuthZSet( byte[] userSid, NetCred credentials, @@ -234,7 +233,6 @@ namespace System.DirectoryServices.AccountManagement override internal object CurrentAsPrincipal { - [System.Security.SecurityCritical] get { Debug.Assert(_currentGroup >= 0 && _currentGroup < _groupSidList.Length); @@ -449,7 +447,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] override internal bool MoveNext() { bool needToRetry; @@ -506,7 +503,6 @@ namespace System.DirectoryServices.AccountManagement } // IDisposable implementation - [System.Security.SecurityCritical] public override void Dispose() { try @@ -577,9 +573,6 @@ namespace System.DirectoryServices.AccountManagement // // Guarantees finalization of the native resources // -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 private sealed class SafeMemoryPtr : SafeHandle { private SafeMemoryPtr() : base(IntPtr.Zero, true) diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AuthenticablePrincipal.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AuthenticablePrincipal.cs index 9d41ca7067..ae8b844e2f 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AuthenticablePrincipal.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AuthenticablePrincipal.cs @@ -10,9 +10,6 @@ using System.Security.Permissions; namespace System.DirectoryServices.AccountManagement { -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 [DirectoryRdnPrefix("CN")] public class AuthenticablePrincipal : Principal { diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/CertificateCollectionDeltas.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/CertificateCollectionDeltas.cs index c7e72ed6f8..673742305f 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/CertificateCollectionDeltas.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/CertificateCollectionDeltas.cs @@ -1,3 +1,7 @@ +// 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.Diagnostics; using System.Security.Cryptography.X509Certificates; diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Computer.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Computer.cs index 55006aa4cc..c3aedd23f7 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Computer.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Computer.cs @@ -9,9 +9,6 @@ using System.Security.Permissions; namespace System.DirectoryServices.AccountManagement { -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 [DirectoryRdnPrefix("CN")] public class ComputerPrincipal : AuthenticablePrincipal { diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Context.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Context.cs index c170442586..dd8153edae 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Context.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Context.cs @@ -17,8 +17,6 @@ using System.Threading; using System.Collections; using System.Security.Permissions; -[assembly: System.Security.SecurityCritical] - namespace System.DirectoryServices.AccountManagement { internal struct ServerProperties @@ -87,7 +85,6 @@ namespace System.DirectoryServices.AccountManagement _serverProperties = serverProperties; } - [System.Security.SecurityCritical] private bool BindSam(string target, string userName, string password) { StringBuilder adsPath = new StringBuilder(); @@ -227,7 +224,6 @@ namespace System.DirectoryServices.AccountManagement return true; } - [System.Security.SecuritySafeCritical] private void lockedLdapBind(LdapConnection current, NetworkCredential creds, ContextOptions contextOptions) { current.AuthType = ((ContextOptions.SimpleBind & contextOptions) > 0 ? AuthType.Basic : AuthType.Negotiate); @@ -243,7 +239,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] public bool Validate(string userName, string password) { NetworkCredential networkCredential = new NetworkCredential(userName, password); @@ -313,7 +308,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] public bool Validate(string userName, string password, ContextOptions connectionMethod) { // empty username and password on the local box @@ -347,11 +341,6 @@ namespace System.DirectoryServices.AccountManagement } } // ******************************************** - [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Assert, - Flags = System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)] -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 public class PrincipalContext : IDisposable { // @@ -1083,7 +1072,6 @@ namespace System.DirectoryServices.AccountManagement internal StoreCtx QueryCtx { - [System.Security.SecuritySafeCritical] get { Initialize(); diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ExtensionHelper.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ExtensionHelper.cs index 27443765e4..669bf59a9a 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ExtensionHelper.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ExtensionHelper.cs @@ -19,7 +19,6 @@ namespace System.DirectoryServices.AccountManagement internal string RdnPrefix { - [System.Security.SecurityCritical] get { DirectoryRdnPrefixAttribute[] MyAttribute = @@ -75,7 +74,6 @@ namespace System.DirectoryServices.AccountManagement internal string StructuralObjectClass { - [System.Security.SecurityCritical] get { DirectoryObjectClassAttribute[] MyAttribute = diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/FindResult.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/FindResult.cs index 820d807500..80afd85af2 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/FindResult.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/FindResult.cs @@ -15,7 +15,6 @@ namespace System.DirectoryServices.AccountManagement // Public methods // - [System.Security.SecurityCritical] public IEnumerator GetEnumerator() { GlobalDebug.WriteLineIf(GlobalDebug.Info, "PrincipalSearchResult", "Entering GetEnumerator"); @@ -25,7 +24,6 @@ namespace System.DirectoryServices.AccountManagement return new FindResultEnumerator(_resultSet); } - [System.Security.SecurityCritical] IEnumerator IEnumerable.GetEnumerator() { return (IEnumerator)GetEnumerator(); diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/FindResultEnumerator.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/FindResultEnumerator.cs index 3e4aee89d8..5dc5cb83ba 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/FindResultEnumerator.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/FindResultEnumerator.cs @@ -21,7 +21,6 @@ namespace System.DirectoryServices.AccountManagement public T Current { - [System.Security.SecurityCritical] get { GlobalDebug.WriteLineIf(GlobalDebug.Info, "FindResultEnumerator", "Entering Current, T={0}", typeof(T)); @@ -49,7 +48,6 @@ namespace System.DirectoryServices.AccountManagement object IEnumerator.Current { - [System.Security.SecurityCritical] get { return Current; @@ -62,7 +60,6 @@ namespace System.DirectoryServices.AccountManagement // Calls resultSet.MoveNext() to advance to the next principal in the ResultSet. // Returns false when it reaches the end of the last ResultSet in resultSets, and sets endReached to true. - [System.Security.SecurityCritical] public bool MoveNext() { GlobalDebug.WriteLineIf(GlobalDebug.Info, "FindResultEnumerator", "Entering MoveNext, T={0}", typeof(T)); @@ -114,7 +111,6 @@ namespace System.DirectoryServices.AccountManagement return f; } - [System.Security.SecurityCritical] bool IEnumerator.MoveNext() { return MoveNext(); @@ -122,7 +118,6 @@ namespace System.DirectoryServices.AccountManagement // Repositions us to the beginning by setting beforeStart to true. Also clears endReached // by setting it back to false; - [System.Security.SecurityCritical] public void Reset() { GlobalDebug.WriteLineIf(GlobalDebug.Info, "FindResultEnumerator", "Entering Reset"); @@ -133,7 +128,6 @@ namespace System.DirectoryServices.AccountManagement _beforeStart = true; } - [System.Security.SecurityCritical] void IEnumerator.Reset() { Reset(); diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/GlobalDebug.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/GlobalDebug.cs index 4148f84fc3..01d0cd19ed 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/GlobalDebug.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/GlobalDebug.cs @@ -18,7 +18,6 @@ namespace System.DirectoryServices.AccountManagement internal static class GlobalDebug { - [System.Security.SecurityCritical] static GlobalDebug() { GlobalDebug.s_debugLevel = GlobalConfig.DebugLevel; @@ -61,7 +60,6 @@ namespace System.DirectoryServices.AccountManagement get { return DebugLevel.Info >= GlobalDebug.s_debugLevel; } } - [System.Security.SecuritySafeCritical] [ConditionalAttribute("DEBUG")] static public void WriteLineIf(bool f, string category, string message, params object[] args) { @@ -76,7 +74,6 @@ namespace System.DirectoryServices.AccountManagement category); } - [System.Security.SecuritySafeCritical] [ConditionalAttribute("DEBUG")] static public void WriteLineIf(bool f, string category, string message) { diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Group.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Group.cs index 640f9e0c62..dc0cbeb741 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Group.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Group.cs @@ -9,9 +9,6 @@ using System.Security.Permissions; namespace System.DirectoryServices.AccountManagement { -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 [DirectoryRdnPrefix("CN")] public class GroupPrincipal : Principal { diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/NetCred.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/NetCred.cs index c6e2ad224e..b3b5a968d1 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/NetCred.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/NetCred.cs @@ -31,7 +31,6 @@ namespace System.DirectoryServices.AccountManagement public string ParsedUserName { - [System.Security.SecurityCritical] get { if (null == _parsedUserName) @@ -45,7 +44,6 @@ namespace System.DirectoryServices.AccountManagement public string Domain { - [System.Security.SecurityCritical] get { if (null == _parsedUserName) @@ -57,7 +55,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] private void SplitUsername(string username, ref string parsedUserName, ref string parsedDomainName) { // If the user has passed null creds then parsed components should also be null. diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PasswordInfo.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PasswordInfo.cs index 63ea9cec97..ba788f7497 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PasswordInfo.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PasswordInfo.cs @@ -24,7 +24,6 @@ namespace System.DirectoryServices.AccountManagement public Nullable LastPasswordSet { - [System.Security.SecurityCritical] get { return _owningPrincipal.HandleGet>(ref _lastPasswordSet, PropertyNames.PwdInfoLastPasswordSet, ref _lastPasswordSetLoaded); @@ -37,7 +36,6 @@ namespace System.DirectoryServices.AccountManagement public Nullable LastBadPasswordAttempt { - [System.Security.SecurityCritical] get { return _owningPrincipal.HandleGet>(ref _lastBadPasswordAttempt, PropertyNames.PwdInfoLastBadPasswordAttempt, ref _lastBadPasswordAttemptLoaded); @@ -50,13 +48,11 @@ namespace System.DirectoryServices.AccountManagement public bool PasswordNotRequired { - [System.Security.SecurityCritical] get { return _owningPrincipal.HandleGet(ref _passwordNotRequired, PropertyNames.PwdInfoPasswordNotRequired, ref _passwordNotRequiredChanged); } - [System.Security.SecurityCritical] set { _owningPrincipal.HandleSet(ref _passwordNotRequired, value, ref _passwordNotRequiredChanged, @@ -70,13 +66,11 @@ namespace System.DirectoryServices.AccountManagement public bool PasswordNeverExpires { - [System.Security.SecurityCritical] get { return _owningPrincipal.HandleGet(ref _passwordNeverExpires, PropertyNames.PwdInfoPasswordNeverExpires, ref _passwordNeverExpiresChanged); } - [System.Security.SecurityCritical] set { _owningPrincipal.HandleSet(ref _passwordNeverExpires, value, ref _passwordNeverExpiresChanged, @@ -94,7 +88,6 @@ namespace System.DirectoryServices.AccountManagement // needed. We read the status directly from the store and then cache it for use later. public bool UserCannotChangePassword { - [System.Security.SecurityCritical] get { _owningPrincipal.HandleGet(ref _cannotChangePassword, PropertyNames.PwdInfoCannotChangePassword, ref _cannotChangePasswordChanged); @@ -108,7 +101,6 @@ namespace System.DirectoryServices.AccountManagement return _cannotChangePassword; } - [System.Security.SecurityCritical] set { _owningPrincipal.HandleSet(ref _cannotChangePassword, value, ref _cannotChangePasswordChanged, @@ -122,13 +114,11 @@ namespace System.DirectoryServices.AccountManagement public bool AllowReversiblePasswordEncryption { - [System.Security.SecurityCritical] get { return _owningPrincipal.HandleGet(ref _allowReversiblePasswordEncryption, PropertyNames.PwdInfoAllowReversiblePasswordEncryption, ref _allowReversiblePasswordEncryptionChanged); } - [System.Security.SecurityCritical] set { _owningPrincipal.HandleSet(ref _allowReversiblePasswordEncryption, value, ref _allowReversiblePasswordEncryptionChanged, @@ -142,7 +132,6 @@ namespace System.DirectoryServices.AccountManagement private string _storedNewPassword = null; - [System.Security.SecurityCritical] public void SetPassword(string newPassword) { if (newPassword == null) @@ -161,7 +150,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] public void ChangePassword(string oldPassword, string newPassword) { if (oldPassword == null) @@ -181,7 +169,6 @@ namespace System.DirectoryServices.AccountManagement private bool _expirePasswordImmediately = false; - [System.Security.SecurityCritical] public void ExpirePasswordNow() { // If we're not persisted, we just save up the change until we're Saved @@ -197,7 +184,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] public void RefreshExpiredPassword() { // If we're not persisted, we undo the expiration we saved up when ExpirePasswordNow was called (if it was). @@ -216,7 +202,6 @@ namespace System.DirectoryServices.AccountManagement // // Internal constructor // - [System.Security.SecurityCritical] internal PasswordInfo(AuthenticablePrincipal principal) { _owningPrincipal = principal; diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Principal.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Principal.cs index f054037a9a..20b17a6762 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Principal.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Principal.cs @@ -20,7 +20,6 @@ namespace System.DirectoryServices.AccountManagement // Public properties // - [System.Security.SecurityCritical] public override string ToString() { return Name; @@ -29,7 +28,6 @@ namespace System.DirectoryServices.AccountManagement // Context property public PrincipalContext Context { - [System.Security.SecuritySafeCritical] get { // Make sure we're not disposed or deleted. @@ -45,7 +43,6 @@ namespace System.DirectoryServices.AccountManagement // ContextType property public ContextType ContextType { - [System.Security.SecurityCritical] get { // Make sure we're not disposed or deleted. @@ -70,13 +67,11 @@ namespace System.DirectoryServices.AccountManagement public string Description { - [System.Security.SecurityCritical] get { return HandleGet(ref _description, PropertyNames.PrincipalDescription, ref _descriptionChanged); } - [System.Security.SecurityCritical] set { if (!GetStoreCtxToUse().IsValidProperty(this, PropertyNames.PrincipalDescription)) @@ -96,13 +91,11 @@ namespace System.DirectoryServices.AccountManagement public string DisplayName { - [System.Security.SecurityCritical] get { return HandleGet(ref _displayName, PropertyNames.PrincipalDisplayName, ref _displayNameChanged); } - [System.Security.SecurityCritical] set { if (!GetStoreCtxToUse().IsValidProperty(this, PropertyNames.PrincipalDisplayName)) @@ -124,13 +117,11 @@ namespace System.DirectoryServices.AccountManagement public string SamAccountName { - [System.Security.SecurityCritical] get { return HandleGet(ref _samName, PropertyNames.PrincipalSamAccountName, ref _samNameChanged); } - [System.Security.SecurityCritical] set { if (null == value || 0 == value.Length) @@ -151,13 +142,11 @@ namespace System.DirectoryServices.AccountManagement private LoadState _userPrincipalNameChanged = LoadState.NotSet; // change-tracking public string UserPrincipalName { - [System.Security.SecurityCritical] get { return HandleGet(ref _userPrincipalName, PropertyNames.PrincipalUserPrincipalName, ref _userPrincipalNameChanged); } - [System.Security.SecurityCritical] set { if (!GetStoreCtxToUse().IsValidProperty(this, PropertyNames.PrincipalUserPrincipalName)) @@ -176,7 +165,6 @@ namespace System.DirectoryServices.AccountManagement public SecurityIdentifier Sid { - [System.Security.SecurityCritical] get { return HandleGet(ref _sid, PropertyNames.PrincipalSid, ref _sidChanged); @@ -191,7 +179,6 @@ namespace System.DirectoryServices.AccountManagement private LoadState _guidChanged = LoadState.NotSet; public Nullable Guid { - [System.Security.SecurityCritical] get { return HandleGet>(ref _guid, PropertyNames.PrincipalGuid, ref _guidChanged); @@ -206,7 +193,6 @@ namespace System.DirectoryServices.AccountManagement private LoadState _distinguishedNameChanged = LoadState.NotSet; public string DistinguishedName { - [System.Security.SecurityCritical] get { return HandleGet(ref _distinguishedName, PropertyNames.PrincipalDistinguishedName, ref _distinguishedNameChanged); @@ -222,7 +208,6 @@ namespace System.DirectoryServices.AccountManagement public string StructuralObjectClass { - [System.Security.SecurityCritical] get { return HandleGet(ref _structuralObjectClass, PropertyNames.PrincipalStructuralObjectClass, ref _structuralObjectClassChanged); @@ -237,7 +222,6 @@ namespace System.DirectoryServices.AccountManagement public string Name { - [System.Security.SecurityCritical] get { // TODO Store should be mapping both to the same property already.... @@ -258,7 +242,6 @@ namespace System.DirectoryServices.AccountManagement return HandleGet(ref _name, PropertyNames.PrincipalName, ref _nameChanged); } } - [System.Security.SecurityCritical] set { if (null == value || 0 == value.Length) @@ -298,19 +281,16 @@ namespace System.DirectoryServices.AccountManagement // Public methods // - [System.Security.SecurityCritical] public static Principal FindByIdentity(PrincipalContext context, string identityValue) { return FindByIdentityWithType(context, typeof(Principal), identityValue); } - [System.Security.SecurityCritical] public static Principal FindByIdentity(PrincipalContext context, IdentityType identityType, string identityValue) { return FindByIdentityWithType(context, typeof(Principal), identityType, identityValue); } - [System.Security.SecurityCritical] public void Save() { GlobalDebug.WriteLineIf(GlobalDebug.Info, "Principal", "Entering Save"); @@ -348,7 +328,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] public void Save(PrincipalContext context) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "Principal", "Entering Save(Context)"); @@ -463,7 +442,6 @@ namespace System.DirectoryServices.AccountManagement _ctx.QueryCtx = newStoreCtx; // so Updates go to the right StoreCtx } - [System.Security.SecurityCritical] public void Delete() { GlobalDebug.WriteLineIf(GlobalDebug.Info, "Principal", "Entering Delete"); @@ -507,7 +485,6 @@ namespace System.DirectoryServices.AccountManagement return base.GetHashCode(); } - [System.Security.SecurityCritical] public object GetUnderlyingObject() { // Make sure we're not disposed or deleted. @@ -524,7 +501,6 @@ namespace System.DirectoryServices.AccountManagement return this.UnderlyingObject; } - [System.Security.SecurityCritical] public Type GetUnderlyingObjectType() { // Make sure we're not disposed or deleted. @@ -552,13 +528,11 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] public PrincipalSearchResult GetGroups() { return new PrincipalSearchResult(GetGroupsHelper()); } - [System.Security.SecurityCritical] public PrincipalSearchResult GetGroups(PrincipalContext contextToQuery) { if (contextToQuery == null) @@ -567,7 +541,6 @@ namespace System.DirectoryServices.AccountManagement return new PrincipalSearchResult(GetGroupsHelper(contextToQuery)); } - [System.Security.SecurityCritical] public bool IsMemberOf(GroupPrincipal group) { // Make sure we're not disposed or deleted. @@ -579,7 +552,6 @@ namespace System.DirectoryServices.AccountManagement return group.Members.Contains(this); } - [System.Security.SecurityCritical] public bool IsMemberOf(PrincipalContext context, IdentityType identityType, string identityValue) { // Make sure we're not disposed or deleted. @@ -607,7 +579,6 @@ namespace System.DirectoryServices.AccountManagement // // IDisposable // - [System.Security.SecurityCritical] public virtual void Dispose() { if (!_disposed) @@ -647,7 +618,6 @@ namespace System.DirectoryServices.AccountManagement private ExtensionCache _extensionCache = new ExtensionCache(); private LoadState _extensionCacheChanged = LoadState.NotSet; - [System.Security.SecurityCritical] protected object[] ExtensionGet(string attribute) { if (null == attribute) @@ -721,7 +691,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] protected void ExtensionSet(string attribute, object value) { if (null == attribute) @@ -737,7 +706,6 @@ namespace System.DirectoryServices.AccountManagement _extensionCacheChanged = LoadState.Changed; } - [System.Security.SecurityCritical] internal void AdvancedFilterSet(string attribute, object value, Type objectType, MatchType mt) { if (null == attribute) @@ -773,7 +741,6 @@ namespace System.DirectoryServices.AccountManagement internal bool fakePrincipal = false; // Directly corresponds to the Principal.PrincipalContext public property - [System.Security.SecuritySafeCritical] private PrincipalContext _ctx = null; internal bool Loaded @@ -794,13 +761,9 @@ namespace System.DirectoryServices.AccountManagement [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] internal protected PrincipalContext ContextRaw { - //[StrongNameIdentityPermission(SecurityAction.LinkDemand, PublicKey = Microsoft.Internal.BuildInfo.WINDOWS_PUBLIC_KEY_STRING)] - [System.Security.SecurityCritical] get { return _ctx; } - //[StrongNameIdentityPermission(SecurityAction.LinkDemand, PublicKey = Microsoft.Internal.BuildInfo.WINDOWS_PUBLIC_KEY_STRING)] - [System.Security.SecurityCritical] set { // Verify that the passed context is not disposed. @@ -836,7 +799,6 @@ namespace System.DirectoryServices.AccountManagement // returns the appropriate StoreCtx from the PrincipalContext that we should use for // all StoreCtx-related operations. // Returns null if no context has been set yet. - [System.Security.SecuritySafeCritical] internal StoreCtx GetStoreCtxToUse() { if (_ctx == null) @@ -921,7 +883,6 @@ namespace System.DirectoryServices.AccountManagement // Checks if the principal has been disposed or deleted, and throws an appropriate exception if it has. [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Advanced)] - [System.Security.SecuritySafeCritical] protected void CheckDisposedOrDeleted() { if (_disposed) @@ -937,7 +898,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Advanced)] protected static Principal FindByIdentityWithType(PrincipalContext context, Type principalType, string identityValue) { @@ -950,7 +910,6 @@ namespace System.DirectoryServices.AccountManagement return FindByIdentityWithTypeHelper(context, principalType, null, identityValue, DateTime.UtcNow); } - [System.Security.SecurityCritical] [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Advanced)] protected static Principal FindByIdentityWithType(PrincipalContext context, Type principalType, IdentityType identityType, string identityValue) { @@ -966,7 +925,6 @@ namespace System.DirectoryServices.AccountManagement return FindByIdentityWithTypeHelper(context, principalType, identityType, identityValue, DateTime.UtcNow); } - [System.Security.SecurityCritical] private static Principal FindByIdentityWithTypeHelper(PrincipalContext context, Type principalType, Nullable identityType, string identityValue, DateTime refDate) { // Ask the store to find a Principal based on this IdentityReference info. @@ -986,7 +944,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecuritySafeCritical] private ResultSet GetGroupsHelper() { // Make sure we're not disposed or deleted. @@ -1008,7 +965,6 @@ namespace System.DirectoryServices.AccountManagement return resultSet; } - [System.Security.SecuritySafeCritical] private ResultSet GetGroupsHelper(PrincipalContext contextToQuery) { // Make sure we're not disposed or deleted. @@ -1026,7 +982,6 @@ namespace System.DirectoryServices.AccountManagement // If we're the result of a query and our properties haven't been loaded yet, do so now // We'd like this to be marked protected AND internal, but that's not possible, so we'll settle for // internal and treat it as if it were also protected. - [System.Security.SecurityCritical] internal void LoadIfNeeded(string principalPropertyName) { // Fake principals have nothing to load, since they have no store object. @@ -1069,7 +1024,6 @@ namespace System.DirectoryServices.AccountManagement // // We'd like this to be marked protected AND internal, but that's not possible, so we'll settle for // internal and treat it as if it were also protected. - [System.Security.SecurityCritical] internal T HandleGet(ref T currentValue, string name, ref LoadState state) { // Make sure we're not disposed or deleted. @@ -1090,7 +1044,6 @@ namespace System.DirectoryServices.AccountManagement // We'd like this to be marked protected AND internal, but that's not possible, so we'll settle for // internal and treat it as if it were also protected. - [System.Security.SecurityCritical] internal void HandleSet(ref T currentValue, T newValue, ref LoadState state, string name) { // Make sure we're not disposed or deleted. @@ -1126,7 +1079,6 @@ namespace System.DirectoryServices.AccountManagement // each byte[] is a certificate. // (The property can never be a PrincipalCollection, since such properties // are not loaded by StoreCtx.Load()). - //[StrongNameIdentityPermission(SecurityAction.InheritanceDemand, PublicKey = Microsoft.Internal.BuildInfo.WINDOWS_PUBLIC_KEY_STRING)] // ExtensionCache is never directly loaded by the store hence it does not exist in the switch internal virtual void LoadValueIntoProperty(string propertyName, object value) { @@ -1193,8 +1145,6 @@ namespace System.DirectoryServices.AccountManagement // // Given a property name, returns true if that property has changed since it was loaded, false otherwise. - //[StrongNameIdentityPermission(SecurityAction.InheritanceDemand, PublicKey = Microsoft.Internal.BuildInfo.WINDOWS_PUBLIC_KEY_STRING)] - [System.Security.SecuritySafeCritical] internal virtual bool GetChangeStatusForProperty(string propertyName) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "Principal", "GetChangeStatusForProperty: name=" + propertyName); @@ -1263,7 +1213,6 @@ namespace System.DirectoryServices.AccountManagement // If the property is a ValueCollection, the return value is the ValueCollection itself. // If the property is a X509Certificate2Collection, the return value is the X509Certificate2Collection itself. // If the property is a PrincipalCollection, the return value is the PrincipalCollection itself. - //[StrongNameIdentityPermission(SecurityAction.InheritanceDemand, PublicKey = Microsoft.Internal.BuildInfo.WINDOWS_PUBLIC_KEY_STRING)] internal virtual object GetValueForProperty(string propertyName) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "Principal", "GetValueForProperty: name=" + propertyName); @@ -1309,7 +1258,6 @@ namespace System.DirectoryServices.AccountManagement // Reset all change-tracking status for all properties on the object to "unchanged". // This is used by StoreCtx.Insert() and StoreCtx.Update() to reset the change-tracking after they // have persisted all current changes to the store. - //[StrongNameIdentityPermission(SecurityAction.InheritanceDemand, PublicKey = Microsoft.Internal.BuildInfo.WINDOWS_PUBLIC_KEY_STRING)] internal virtual void ResetAllChangeStatus() { GlobalDebug.WriteLineIf(GlobalDebug.Info, "Principal", "ResetAllChangeStatus"); diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PrincipalCollection.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PrincipalCollection.cs index a86b831e05..73d657cef8 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PrincipalCollection.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PrincipalCollection.cs @@ -14,7 +14,6 @@ namespace System.DirectoryServices.AccountManagement // // ICollection // - [System.Security.SecurityCritical] void ICollection.CopyTo(Array array, int index) { CheckDisposed(); @@ -96,7 +95,6 @@ namespace System.DirectoryServices.AccountManagement int ICollection.Count { - [System.Security.SecurityCritical] get { return Count; @@ -105,7 +103,6 @@ namespace System.DirectoryServices.AccountManagement bool ICollection.IsSynchronized { - [System.Security.SecurityCritical] get { return IsSynchronized; @@ -114,7 +111,6 @@ namespace System.DirectoryServices.AccountManagement object ICollection.SyncRoot { - [System.Security.SecurityCritical] get { return SyncRoot; @@ -140,7 +136,6 @@ namespace System.DirectoryServices.AccountManagement // // IEnumerable // - [System.Security.SecurityCritical] IEnumerator IEnumerable.GetEnumerator() { return (IEnumerator)GetEnumerator(); @@ -164,7 +159,6 @@ namespace System.DirectoryServices.AccountManagement public int Count { - [System.Security.SecurityCritical] get { CheckDisposed(); @@ -216,7 +210,6 @@ namespace System.DirectoryServices.AccountManagement // // IEnumerable // - [System.Security.SecurityCritical] public IEnumerator GetEnumerator() { CheckDisposed(); @@ -234,25 +227,21 @@ namespace System.DirectoryServices.AccountManagement // Add // - [System.Security.SecurityCritical] public void Add(UserPrincipal user) { Add((Principal)user); } - [System.Security.SecurityCritical] public void Add(GroupPrincipal group) { Add((Principal)group); } - [System.Security.SecurityCritical] public void Add(ComputerPrincipal computer) { Add((Principal)computer); } - [System.Security.SecurityCritical] public void Add(Principal principal) { CheckDisposed(); @@ -297,7 +286,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] public void Add(PrincipalContext context, IdentityType identityType, string identityValue) { CheckDisposed(); @@ -325,7 +313,6 @@ namespace System.DirectoryServices.AccountManagement // // Clear // - [System.Security.SecurityCritical] public void Clear() { GlobalDebug.WriteLineIf(GlobalDebug.Info, "PrincipalCollection", "Clear"); @@ -363,25 +350,21 @@ namespace System.DirectoryServices.AccountManagement // Remove // - [System.Security.SecurityCritical] public bool Remove(UserPrincipal user) { return Remove((Principal)user); } - [System.Security.SecurityCritical] public bool Remove(GroupPrincipal group) { return Remove((Principal)group); } - [System.Security.SecurityCritical] public bool Remove(ComputerPrincipal computer) { return Remove((Principal)computer); } - [System.Security.SecurityCritical] public bool Remove(Principal principal) { CheckDisposed(); @@ -450,7 +433,6 @@ namespace System.DirectoryServices.AccountManagement return removed; } - [System.Security.SecurityCritical] public bool Remove(PrincipalContext context, IdentityType identityType, string identityValue) { CheckDisposed(); @@ -476,7 +458,6 @@ namespace System.DirectoryServices.AccountManagement // Contains // - [System.Security.SecuritySafeCritical] private bool ContainsEnumTest(Principal principal) { CheckDisposed(); @@ -526,7 +507,6 @@ namespace System.DirectoryServices.AccountManagement return false; } - [System.Security.SecuritySafeCritical] private bool ContainsNativeTest(Principal principal) { CheckDisposed(); @@ -565,25 +545,21 @@ namespace System.DirectoryServices.AccountManagement return false; } - [System.Security.SecurityCritical] public bool Contains(UserPrincipal user) { return Contains((Principal)user); } - [System.Security.SecurityCritical] public bool Contains(GroupPrincipal group) { return Contains((Principal)group); } - [System.Security.SecurityCritical] public bool Contains(ComputerPrincipal computer) { return Contains((Principal)computer); } - [System.Security.SecurityCritical] public bool Contains(Principal principal) { StoreCtx storeCtxToUse = _owningGroup.GetStoreCtxToUse(); @@ -606,7 +582,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] public bool Contains(PrincipalContext context, IdentityType identityType, string identityValue) { CheckDisposed(); @@ -633,7 +608,6 @@ namespace System.DirectoryServices.AccountManagement // Constructs a fresh PrincipalCollection based on the supplied ResultSet. // The ResultSet may not be null (use an EmptySet instead). - [System.Security.SecurityCritical] internal PrincipalCollection(BookmarkableResultSet results, GroupPrincipal owningGroup) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "PrincipalCollection", "Ctor"); @@ -675,7 +649,6 @@ namespace System.DirectoryServices.AccountManagement // // The group we're a PrincipalCollection of - [System.Security.SecuritySafeCritical] private GroupPrincipal _owningGroup; // diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PrincipalCollectionEnumerator.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PrincipalCollectionEnumerator.cs index b4e94a5b46..22d8d4edb9 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PrincipalCollectionEnumerator.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PrincipalCollectionEnumerator.cs @@ -18,7 +18,6 @@ namespace System.DirectoryServices.AccountManagement public Principal Current { - [System.Security.SecuritySafeCritical] get { CheckDisposed(); @@ -47,7 +46,6 @@ namespace System.DirectoryServices.AccountManagement object IEnumerator.Current { - [System.Security.SecurityCritical] get { return Current; @@ -58,7 +56,6 @@ namespace System.DirectoryServices.AccountManagement // Public methods // - [System.Security.SecuritySafeCritical] public bool MoveNext() { GlobalDebug.WriteLineIf(GlobalDebug.Info, "PrincipalCollectionEnumerator", "Entering MoveNext"); @@ -207,13 +204,11 @@ namespace System.DirectoryServices.AccountManagement return false; } - [System.Security.SecurityCritical] bool IEnumerator.MoveNext() { return MoveNext(); } - [System.Security.SecurityCritical] public void Reset() { GlobalDebug.WriteLineIf(GlobalDebug.Info, "PrincipalCollectionEnumerator", "Reset"); @@ -227,7 +222,6 @@ namespace System.DirectoryServices.AccountManagement _currentMode = CurrentEnumeratorMode.None; } - [System.Security.SecurityCritical] void IEnumerator.Reset() { Reset(); @@ -314,7 +308,6 @@ namespace System.DirectoryServices.AccountManagement private PrincipalCollection _memberCollection = null; - [System.Security.SecurityCritical] private void CheckChanged() { // Make sure the app hasn't changed our underlying list diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PrincipalSearcher.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PrincipalSearcher.cs index 7fd1717349..7f8c9b3e05 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PrincipalSearcher.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PrincipalSearcher.cs @@ -15,13 +15,11 @@ namespace System.DirectoryServices.AccountManagement // // Public constructors // - [System.Security.SecurityCritical] public PrincipalSearcher() { SetDefaultPageSizeForContext(); } - [System.Security.SecurityCritical] public PrincipalSearcher(Principal queryFilter) { if (null == queryFilter) @@ -38,7 +36,6 @@ namespace System.DirectoryServices.AccountManagement // public PrincipalContext Context { - [System.Security.SecuritySafeCritical] get { CheckDisposed(); @@ -49,7 +46,6 @@ namespace System.DirectoryServices.AccountManagement public Principal QueryFilter { - [System.Security.SecurityCritical] get { CheckDisposed(); @@ -57,7 +53,6 @@ namespace System.DirectoryServices.AccountManagement return _qbeFilter; } - [System.Security.SecurityCritical] set { if (null == value) @@ -80,7 +75,6 @@ namespace System.DirectoryServices.AccountManagement // // Calls FindAll(false) to retrieve all matching results - [System.Security.SecurityCritical] public PrincipalSearchResult FindAll() { GlobalDebug.WriteLineIf(GlobalDebug.Info, "PrincipalSearcher", "Entering FindAll()"); @@ -92,7 +86,6 @@ namespace System.DirectoryServices.AccountManagement // Calls FindAll(true) to retrieve at most one result, then retrieves the first (and only) result from the // FindResult and returns it. - [System.Security.SecurityCritical] public Principal FindOne() { GlobalDebug.WriteLineIf(GlobalDebug.Info, "PrincipalSearcher", "Entering FindOne()"); @@ -127,7 +120,6 @@ namespace System.DirectoryServices.AccountManagement // Otherwise, calls StoreCtx.PushFilterToNativeSearcher to push the current QBE filter // into underlyingSearcher (automatically constructing a fresh native searcher if underlyingSearcher is null), // and returns underlyingSearcher. - [System.Security.SecurityCritical] public object GetUnderlyingSearcher() { GlobalDebug.WriteLineIf(GlobalDebug.Info, "PrincipalSearcher", "Entering GetUnderlyingSearcher"); @@ -162,7 +154,6 @@ namespace System.DirectoryServices.AccountManagement return _underlyingSearcher; } - [System.Security.SecurityCritical] public Type GetUnderlyingSearcherType() { GlobalDebug.WriteLineIf(GlobalDebug.Info, "PrincipalSearcher", "Entering GetUnderlyingSearcherType"); @@ -183,7 +174,6 @@ namespace System.DirectoryServices.AccountManagement return storeCtx.SearcherNativeType(); } - [System.Security.SecurityCritical] public virtual void Dispose() { if (!_disposed) @@ -208,7 +198,6 @@ namespace System.DirectoryServices.AccountManagement // // Private implementation // - [System.Security.SecuritySafeCritical] private PrincipalContext _ctx; // Are we disposed? @@ -255,7 +244,6 @@ namespace System.DirectoryServices.AccountManagement // // Returns at most one result in the FindResult if returnOne == true, no limit on results // returned otherwise. - [System.Security.SecuritySafeCritical] private PrincipalSearchResult FindAll(bool returnOne) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "PrincipalSearcher", "Entering FindAll, returnOne=" + returnOne.ToString()); @@ -280,7 +268,6 @@ namespace System.DirectoryServices.AccountManagement return fr; } - [System.Security.SecurityCritical] private void SetDefaultPageSizeForContext() { _pageSize = 0; @@ -305,7 +292,6 @@ namespace System.DirectoryServices.AccountManagement } // Checks this.qbeFilter to determine if any referential properties are set - [System.Security.SecuritySafeCritical] private bool HasReferentialPropertiesSet() { // If using a null query filter, nothing to validate, as it can't have any referential diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMGroupsSet.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMGroupsSet.cs index 391b8c1825..d3e6aecde1 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMGroupsSet.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMGroupsSet.cs @@ -13,9 +13,6 @@ using System.Runtime.InteropServices; namespace System.DirectoryServices.AccountManagement { -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 internal class SAMGroupsSet : ResultSet { internal SAMGroupsSet(UnsafeNativeMethods.IADsMembers iADsMembers, SAMStoreCtx storeCtx, DirectoryEntry ctxBase) diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMMembersSet.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMMembersSet.cs index c74cf89815..d8a1260397 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMMembersSet.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMMembersSet.cs @@ -14,9 +14,6 @@ using System.Runtime.InteropServices; namespace System.DirectoryServices.AccountManagement { -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 internal class SAMMembersSet : BookmarkableResultSet { internal SAMMembersSet(string groupPath, UnsafeNativeMethods.IADsGroup group, bool recursive, SAMStoreCtx storeCtx, DirectoryEntry ctxBase) diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMQuerySet.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMQuerySet.cs index 821533a6d4..2efd8c8f35 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMQuerySet.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMQuerySet.cs @@ -12,9 +12,6 @@ using System.Text.RegularExpressions; namespace System.DirectoryServices.AccountManagement { -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 internal class SAMQuerySet : ResultSet { // We will iterate over all principals under ctxBase, returning only those which are in the list of types and which @@ -206,9 +203,6 @@ namespace System.DirectoryServices.AccountManagement // The matcher routines for query-by-example support // -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 internal class QbeMatcher : SAMMatcher { private QbeFilterDescription _propertiesToMatch; @@ -702,9 +696,6 @@ namespace System.DirectoryServices.AccountManagement // The matcher routines for FindBy* support // -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 internal class FindByDateMatcher : SAMMatcher { internal enum DateProperty @@ -843,9 +834,6 @@ namespace System.DirectoryServices.AccountManagement } } -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 internal class GroupMemberMatcher : SAMMatcher { private byte[] _memberSidToMatch; diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMStoreCtx_Query.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMStoreCtx_Query.cs index 0af1b86b4f..e572317159 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMStoreCtx_Query.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMStoreCtx_Query.cs @@ -13,9 +13,6 @@ using System.DirectoryServices; namespace System.DirectoryServices.AccountManagement { -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 internal partial class SAMStoreCtx : StoreCtx { // diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMUtils.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMUtils.cs index 64fed54670..d2f0a57444 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMUtils.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMUtils.cs @@ -12,9 +12,6 @@ using System.Security.Principal; namespace System.DirectoryServices.AccountManagement { -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 internal class SAMUtils { // To stop the compiler from autogenerating a constructor for this class diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/StoreCtx.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/StoreCtx.cs index b4ef442830..c7b80e08d2 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/StoreCtx.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/StoreCtx.cs @@ -28,13 +28,11 @@ namespace System.DirectoryServices.AccountManagement private PrincipalContext _owningContext = null; internal PrincipalContext OwningContext { - [System.Security.SecurityCritical] get { return _owningContext; } - [System.Security.SecurityCritical] set { Debug.Assert(value != null); @@ -315,7 +313,6 @@ namespace System.DirectoryServices.AccountManagement PropertyNames.ComputerServicePrincipalNames }; - [System.Security.SecurityCritical] protected QbeFilterDescription BuildQbeFilterDescription(Principal p) { QbeFilterDescription qbeFilterDescription = new QbeFilterDescription(); @@ -360,7 +357,6 @@ namespace System.DirectoryServices.AccountManagement // Applies to supplied propertySet to the supplied Principal, and adds any resulting filters // to qbeFilterDescription. - [System.Security.SecurityCritical] private void BuildFilterSet(Principal p, string[] propertySet, QbeFilterDescription qbeFilterDescription) { foreach (string propertyName in propertySet) diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/UnknownPrincipal.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/UnknownPrincipal.cs index deead61df1..88767d8981 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/UnknownPrincipal.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/UnknownPrincipal.cs @@ -10,9 +10,6 @@ using System.Security.Principal; namespace System.DirectoryServices.AccountManagement { -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 [DirectoryRdnPrefix("CN")] internal class UnknownPrincipal : Principal { diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/User.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/User.cs index 8d9f9a29f1..966e89669b 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/User.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/User.cs @@ -10,9 +10,6 @@ using System.Security.Principal; namespace System.DirectoryServices.AccountManagement { -#pragma warning disable 618 // Have not migrated to v4 transparency yet - [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] -#pragma warning restore 618 [DirectoryRdnPrefix("CN")] public class UserPrincipal : AuthenticablePrincipal { @@ -175,7 +172,6 @@ namespace System.DirectoryServices.AccountManagement public static UserPrincipal Current { - [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] get { PrincipalContext context; diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Utils.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Utils.cs index 94bdfed888..7e1910b30e 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Utils.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Utils.cs @@ -118,7 +118,6 @@ namespace System.DirectoryServices.AccountManagement // // SID Utilities // - [System.Security.SecuritySafeCritical] internal static string ConvertSidToSDDL(byte[] sid) { @@ -156,7 +155,6 @@ namespace System.DirectoryServices.AccountManagement // The caller must call Marshal.FreeHGlobal on the returned // value to free it. - [System.Security.SecurityCritical] internal static IntPtr ConvertByteArrayToIntPtr(byte[] bytes) { IntPtr pBytes = IntPtr.Zero; @@ -181,7 +179,6 @@ namespace System.DirectoryServices.AccountManagement return pBytes; } - [System.Security.SecuritySafeCritical] internal static byte[] ConvertNativeSidToByteArray(IntPtr pSid) { @@ -192,7 +189,6 @@ namespace System.DirectoryServices.AccountManagement return sid; } - [System.Security.SecurityCritical] internal static SidType ClassifySID(byte[] sid) { IntPtr pSid = IntPtr.Zero; @@ -210,7 +206,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecuritySafeCritical] internal static SidType ClassifySID(IntPtr pSid) { @@ -257,7 +252,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecuritySafeCritical] internal static int GetLastRidFromSid(IntPtr pSid) { @@ -269,7 +263,6 @@ namespace System.DirectoryServices.AccountManagement return lastRid; } - [System.Security.SecurityCritical] internal static int GetLastRidFromSid(byte[] sid) { IntPtr pSid = IntPtr.Zero; @@ -292,7 +285,6 @@ namespace System.DirectoryServices.AccountManagement // // - [System.Security.SecurityCritical] internal static bool IsSamUser() { // @@ -356,7 +348,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecuritySafeCritical] internal static IntPtr GetCurrentUserSid() { @@ -487,7 +478,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecuritySafeCritical] internal static IntPtr GetMachineDomainSid() { @@ -568,11 +558,6 @@ namespace System.DirectoryServices.AccountManagement } // Returns name in the form "domain\user" - [System.Security.Permissions.SecurityPermission( - System.Security.Permissions.SecurityAction.Assert, - Flags = System.Security.Permissions.SecurityPermissionFlag.ControlPrincipal)] - [System.Security.SecuritySafeCritical] - internal static string GetNT4UserName() { using (WindowsIdentity currentIdentity = System.Security.Principal.WindowsIdentity.GetCurrent()) @@ -583,11 +568,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.Permissions.EnvironmentPermission( - System.Security.Permissions.SecurityAction.Assert, - Unrestricted = true)] - [System.Security.SecuritySafeCritical] - internal static string GetComputerFlatName() { //string s = System.Windows.Forms.SystemInformation.ComputerName; @@ -600,7 +580,6 @@ namespace System.DirectoryServices.AccountManagement // // Interop support // - [System.Security.SecuritySafeCritical] internal static UnsafeNativeMethods.DomainControllerInfo GetDcName(string computerName, string domainName, string siteName, int flags) { @@ -632,7 +611,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] internal static int LookupSid(string serverName, NetCred credentials, byte[] sid, out string name, out string domainName, out int accountUsage) { IntPtr pSid = IntPtr.Zero; @@ -702,7 +680,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecuritySafeCritical] static internal Principal ConstructFakePrincipalFromSID( byte[] sid, @@ -774,7 +751,6 @@ namespace System.DirectoryServices.AccountManagement // // Impersonation // - [System.Security.SecurityCritical] internal static bool BeginImpersonation(NetCred credential, out IntPtr hUserToken) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "Utils", "Entering BeginImpersonation"); @@ -842,7 +818,6 @@ namespace System.DirectoryServices.AccountManagement return true; } - [System.Security.SecurityCritical] internal static void EndImpersonation(IntPtr hUserToken) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "Utils", "Entering EndImpersonation"); @@ -851,7 +826,6 @@ namespace System.DirectoryServices.AccountManagement UnsafeNativeMethods.CloseHandle(hUserToken); } - [System.Security.SecuritySafeCritical] internal static bool IsMachineDC(String computerName) { IntPtr dsRoleInfoPtr = IntPtr.Zero; diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ValueCollection.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ValueCollection.cs index 6e269dd7a9..fa7e610d4c 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ValueCollection.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ValueCollection.cs @@ -17,7 +17,6 @@ namespace System.DirectoryServices.AccountManagement // bool IList.IsFixedSize { - [System.Security.SecurityCritical] get { return IsFixedSize; @@ -26,14 +25,12 @@ namespace System.DirectoryServices.AccountManagement bool IList.IsReadOnly { - [System.Security.SecurityCritical] get { return IsReadOnly; } } - [System.Security.SecurityCritical] int IList.Add(object value) { if (value == null) @@ -43,7 +40,6 @@ namespace System.DirectoryServices.AccountManagement return Count; } - [System.Security.SecurityCritical] void IList.Clear() { Clear(); @@ -57,7 +53,6 @@ namespace System.DirectoryServices.AccountManagement return _inner.Contains((T)value); } - [System.Security.SecurityCritical] int IList.IndexOf(object value) { if (value == null) @@ -66,7 +61,6 @@ namespace System.DirectoryServices.AccountManagement return IndexOf((T)value); } - [System.Security.SecurityCritical] void IList.Insert(int index, object value) { if (value == null) @@ -83,7 +77,6 @@ namespace System.DirectoryServices.AccountManagement _inner.Remove((T)value); } - [System.Security.SecurityCritical] void IList.RemoveAt(int index) { RemoveAt(index); @@ -91,13 +84,11 @@ namespace System.DirectoryServices.AccountManagement object IList.this[int index] { - [System.Security.SecurityCritical] get { return this[index]; } - [System.Security.SecurityCritical] set { if (value == null) @@ -125,7 +116,6 @@ namespace System.DirectoryServices.AccountManagement bool ICollection.IsSynchronized { - [System.Security.SecurityCritical] get { return IsSynchronized; @@ -134,7 +124,6 @@ namespace System.DirectoryServices.AccountManagement object ICollection.SyncRoot { - [System.Security.SecurityCritical] get { return SyncRoot; @@ -160,7 +149,6 @@ namespace System.DirectoryServices.AccountManagement // // IEnumerable // - [System.Security.SecurityCritical] IEnumerator IEnumerable.GetEnumerator() { return (IEnumerator)GetEnumerator(); @@ -361,7 +349,6 @@ namespace System.DirectoryServices.AccountManagement // // IEnumerable // - [System.Security.SecurityCritical] public IEnumerator GetEnumerator() { return new ValueCollectionEnumerator(_inner, _inner.combinedValues); diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ValueCollectionEnumerator.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ValueCollectionEnumerator.cs index b9bef72b81..ecfec924ac 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ValueCollectionEnumerator.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ValueCollectionEnumerator.cs @@ -27,7 +27,6 @@ namespace System.DirectoryServices.AccountManagement object IEnumerator.Current { - [System.Security.SecurityCritical] get { return Current; @@ -44,7 +43,6 @@ namespace System.DirectoryServices.AccountManagement return _inner.MoveNext(); } - [System.Security.SecurityCritical] bool IEnumerator.MoveNext() { return MoveNext(); @@ -56,7 +54,6 @@ namespace System.DirectoryServices.AccountManagement _inner.Reset(); } - [System.Security.SecurityCritical] void IEnumerator.Reset() { Reset(); diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ValueList.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ValueList.cs index 8dda349922..68a4541cbe 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ValueList.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ValueList.cs @@ -1,3 +1,7 @@ +// 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.Diagnostics; using System.Collections; diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ValueListEnumerator.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ValueListEnumerator.cs index e25caf35c0..dd7c061d6c 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ValueListEnumerator.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ValueListEnumerator.cs @@ -1,3 +1,7 @@ +// 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.Diagnostics; using System.Collections; diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/config.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/config.cs index c7f1c392fb..de3a3a9503 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/config.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/config.cs @@ -1,4 +1,6 @@ - +// 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. // // This file controls whether we're building for Longhorn or Whidbey. diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/exceptions.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/exceptions.cs index eb1cdf0fd4..cdbb998ee0 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/exceptions.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/exceptions.cs @@ -12,7 +12,10 @@ using System.Security.Permissions; namespace System.DirectoryServices.AccountManagement { - [Serializable()] + [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.DirectoryServices.AccountManagement, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif abstract public class PrincipalException : SystemException { internal PrincipalException() : base() { } @@ -26,11 +29,13 @@ namespace System.DirectoryServices.AccountManagement protected PrincipalException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } - [Serializable()] + [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.DirectoryServices.AccountManagement, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class MultipleMatchesException : PrincipalException { public MultipleMatchesException() : base() { } @@ -44,11 +49,13 @@ namespace System.DirectoryServices.AccountManagement protected MultipleMatchesException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } - [Serializable()] + [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.DirectoryServices.AccountManagement, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class NoMatchingPrincipalException : PrincipalException { public NoMatchingPrincipalException() : base() { } @@ -66,7 +73,10 @@ namespace System.DirectoryServices.AccountManagement } } - [Serializable()] + [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.DirectoryServices.AccountManagement, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class PasswordException : PrincipalException { public PasswordException() : base() { } @@ -80,11 +90,13 @@ namespace System.DirectoryServices.AccountManagement protected PasswordException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } - [Serializable()] + [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.DirectoryServices.AccountManagement, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class PrincipalExistsException : PrincipalException { public PrincipalExistsException() : base() { } @@ -98,11 +110,13 @@ namespace System.DirectoryServices.AccountManagement protected PrincipalExistsException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } - [Serializable()] + [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.DirectoryServices.AccountManagement, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class PrincipalServerDownException : PrincipalException { private int _errorCode = 0; @@ -133,16 +147,22 @@ namespace System.DirectoryServices.AccountManagement protected PrincipalServerDownException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); + _errorCode = info.GetInt32("errorCode"); + _serverName = (string)info.GetValue("serverName", typeof(string)); } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + info.AddValue("errorCode", _errorCode); + info.AddValue("serverName", _serverName, typeof(String)); } } - [Serializable()] + [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.DirectoryServices.AccountManagement, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class PrincipalOperationException : PrincipalException { private int _errorCode = 0; @@ -167,12 +187,13 @@ namespace System.DirectoryServices.AccountManagement protected PrincipalOperationException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); + _errorCode = info.GetInt32("errorCode"); } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + info.AddValue("errorCode", _errorCode); } public int ErrorCode @@ -286,13 +307,11 @@ namespace System.DirectoryServices.AccountManagement return exception; } - [System.Security.SecuritySafeCritical] internal static Exception GetExceptionFromErrorCode(int errorCode) { return GetExceptionFromErrorCode(errorCode, null); } - [System.Security.SecurityCritical] internal static Exception GetExceptionFromErrorCode(int errorCode, string targetName) { string errorMsg = GetErrorMessage(errorCode, false); @@ -315,7 +334,6 @@ namespace System.DirectoryServices.AccountManagement } } - [System.Security.SecurityCritical] internal static string GetErrorMessage(int errorCode, bool hresult) { uint temp = (uint)errorCode; diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/interopt.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/interopt.cs index 3df3906b22..423d2e892b 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/interopt.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/interopt.cs @@ -24,7 +24,6 @@ namespace System.DirectoryServices.AccountManagement internal static Byte[] GUID_FOREIGNSECURITYPRINCIPALS_CONTAINER_BYTE = new Byte[] { 0x22, 0xb7, 0x0c, 0x67, 0xd5, 0x6e, 0x4e, 0xfb, 0x91, 0xe9, 0x30, 0x0f, 0xca, 0x3d, 0xc1, 0xaa }; } - [SuppressUnmanagedCodeSecurityAttribute] internal class SafeNativeMethods { // To stop the compiler from autogenerating a constructor for this class @@ -37,7 +36,6 @@ namespace System.DirectoryServices.AccountManagement static extern public int LsaNtStatusToWinError(int ntStatus); } - [SuppressUnmanagedCodeSecurityAttribute] internal class UnsafeNativeMethods { // To stop the compiler from autogenerating a constructor for this class @@ -45,7 +43,6 @@ namespace System.DirectoryServices.AccountManagement [DllImport(ExternDll.Activeds, ExactSpelling = true, EntryPoint = "ADsOpenObject", CharSet = System.Runtime.InteropServices.CharSet.Unicode)] private static extern int IntADsOpenObject(string path, string userName, string password, int flags, [In, Out] ref Guid iid, [Out, MarshalAs(UnmanagedType.Interface)] out object ppObject); - [System.Security.SecurityCritical] public static int ADsOpenObject(string path, string userName, string password, int flags, [In, Out] ref Guid iid, [Out, MarshalAs(UnmanagedType.Interface)] out object ppObject) { try @@ -96,7 +93,6 @@ namespace System.DirectoryServices.AccountManagement int LowPart { get; set; } } - [SuppressUnmanagedCodeSecurityAttribute] [ComImport, Guid("927971f5-0939-11d1-8be1-00c04fd8d503")] public class ADsLargeInteger { @@ -305,7 +301,6 @@ namespace System.DirectoryServices.AccountManagement } } - [SuppressUnmanagedCodeSecurityAttribute] [ComImport, Guid("080d0d78-f421-11d0-a36e-00c04fb950dc")] public class Pathname { diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/testobj.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/testobj.cs index 0292d2b0a0..332659ed31 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/testobj.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/testobj.cs @@ -1,3 +1,7 @@ +// 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. + #if TESTHOOK using System; diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/tests/PrincipalContextTests.cs b/external/corefx/src/System.DirectoryServices.AccountManagement/tests/PrincipalContextTests.cs index bf67a3c059..1075dadefc 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/tests/PrincipalContextTests.cs +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/tests/PrincipalContextTests.cs @@ -10,7 +10,7 @@ namespace System.DirectoryServices.AccountManagement.Tests { public class PrincipalContextTests { - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] public void Ctor_ContextType() { var context = new PrincipalContext(ContextType.Machine); @@ -170,7 +170,7 @@ namespace System.DirectoryServices.AccountManagement.Tests [Fact] public void Ctor_DomainContextType_ThrowsPrincipalServerDownException() { - if (Environment.MachineName.Equals(Environment.UserDomainName, StringComparison.OrdinalIgnoreCase)) + if (!PlatformDetection.IsDomainJoinedMachine) { // The machine is not connected to a domain. we expect PrincipalContext(ContextType.Domain) to throw Assert.Throws(() => new PrincipalContext(ContextType.Domain)); @@ -299,7 +299,7 @@ namespace System.DirectoryServices.AccountManagement.Tests } [ActiveIssue(23800)] - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] [OuterLoop("Takes too long on domain joined machines")] public void ValidateCredentials_InvalidUserName_ThrowsException() { @@ -308,7 +308,7 @@ namespace System.DirectoryServices.AccountManagement.Tests } [ActiveIssue(23800)] - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] [OuterLoop("Takes too long on domain joined machines")] public void ValidateCredentials_IncorrectUserNamePassword_ThrowsException() { diff --git a/external/corefx/src/System.DirectoryServices.AccountManagement/tests/System.DirectoryServices.AccountManagement.Tests.csproj b/external/corefx/src/System.DirectoryServices.AccountManagement/tests/System.DirectoryServices.AccountManagement.Tests.csproj index fe762de387..abacbd2f86 100644 --- a/external/corefx/src/System.DirectoryServices.AccountManagement/tests/System.DirectoryServices.AccountManagement.Tests.csproj +++ b/external/corefx/src/System.DirectoryServices.AccountManagement/tests/System.DirectoryServices.AccountManagement.Tests.csproj @@ -6,7 +6,6 @@ - diff --git a/external/corefx/src/System.DirectoryServices.Protocols/System.DirectoryServices.Protocols.sln b/external/corefx/src/System.DirectoryServices.Protocols/System.DirectoryServices.Protocols.sln index dddadbd47b..5fa25f7232 100644 --- a/external/corefx/src/System.DirectoryServices.Protocols/System.DirectoryServices.Protocols.sln +++ b/external/corefx/src/System.DirectoryServices.Protocols/System.DirectoryServices.Protocols.sln @@ -26,14 +26,14 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6638C675-CD62-408F-AB3B-AAFD8A906A96}.Debug|Any CPU.ActiveCfg = netstandard-Windows_NT-Debug|Any CPU - {6638C675-CD62-408F-AB3B-AAFD8A906A96}.Debug|Any CPU.Build.0 = netstandard-Windows_NT-Debug|Any CPU - {6638C675-CD62-408F-AB3B-AAFD8A906A96}.Release|Any CPU.ActiveCfg = netstandard-Windows_NT-Release|Any CPU - {6638C675-CD62-408F-AB3B-AAFD8A906A96}.Release|Any CPU.Build.0 = netstandard-Windows_NT-Release|Any CPU - {135980AC-4583-44EC-894E-CB3B1A481920}.Debug|Any CPU.ActiveCfg = netstandard-Windows_NT-Debug|Any CPU - {135980AC-4583-44EC-894E-CB3B1A481920}.Debug|Any CPU.Build.0 = netstandard-Windows_NT-Debug|Any CPU - {135980AC-4583-44EC-894E-CB3B1A481920}.Release|Any CPU.ActiveCfg = netstandard-Windows_NT-Release|Any CPU - {135980AC-4583-44EC-894E-CB3B1A481920}.Release|Any CPU.Build.0 = netstandard-Windows_NT-Release|Any CPU + {6638C675-CD62-408F-AB3B-AAFD8A906A96}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {6638C675-CD62-408F-AB3B-AAFD8A906A96}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {6638C675-CD62-408F-AB3B-AAFD8A906A96}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {6638C675-CD62-408F-AB3B-AAFD8A906A96}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU + {135980AC-4583-44EC-894E-CB3B1A481920}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {135980AC-4583-44EC-894E-CB3B1A481920}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {135980AC-4583-44EC-894E-CB3B1A481920}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {135980AC-4583-44EC-894E-CB3B1A481920}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU {7DEA4539-9A0D-4801-B229-3824710EBCEE}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU {7DEA4539-9A0D-4801-B229-3824710EBCEE}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU {7DEA4539-9A0D-4801-B229-3824710EBCEE}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU diff --git a/external/corefx/src/System.DirectoryServices.Protocols/ref/System.DirectoryServices.Protocols.cs b/external/corefx/src/System.DirectoryServices.Protocols/ref/System.DirectoryServices.Protocols.cs index c28f7f891d..69d6e395f6 100644 --- a/external/corefx/src/System.DirectoryServices.Protocols/ref/System.DirectoryServices.Protocols.cs +++ b/external/corefx/src/System.DirectoryServices.Protocols/ref/System.DirectoryServices.Protocols.cs @@ -1,3 +1,6 @@ +// 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. namespace System.DirectoryServices.Protocols { public partial class AddRequest : System.DirectoryServices.Protocols.DirectoryRequest { @@ -640,4 +643,4 @@ namespace System.DirectoryServices.Protocols { public int TargetPosition { get { return default(int); } } } } - \ No newline at end of file + diff --git a/external/corefx/src/System.DirectoryServices.Protocols/src/Configurations.props b/external/corefx/src/System.DirectoryServices.Protocols/src/Configurations.props index ae7f026927..94ac07fdab 100644 --- a/external/corefx/src/System.DirectoryServices.Protocols/src/Configurations.props +++ b/external/corefx/src/System.DirectoryServices.Protocols/src/Configurations.props @@ -1,9 +1,13 @@ + + netstandard; + netcoreapp2.0-Windows_NT; + - netstandard-Windows_NT; - netstandard; + $(PackageConfigurations); + netcoreapp-Windows_NT; diff --git a/external/corefx/src/System.DirectoryServices.Protocols/src/FxCopBaseline.AnyOS.cs b/external/corefx/src/System.DirectoryServices.Protocols/src/FxCopBaseline.AnyOS.cs deleted file mode 100644 index 8f8b8d9856..0000000000 --- a/external/corefx/src/System.DirectoryServices.Protocols/src/FxCopBaseline.AnyOS.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA1821", Justification = "Finalizer has implementation in Windows version.")] \ No newline at end of file diff --git a/external/corefx/src/System.DirectoryServices.Protocols/src/Resources/Strings.resx b/external/corefx/src/System.DirectoryServices.Protocols/src/Resources/Strings.resx index 528dff0664..6e3d8eab76 100644 --- a/external/corefx/src/System.DirectoryServices.Protocols/src/Resources/Strings.resx +++ b/external/corefx/src/System.DirectoryServices.Protocols/src/Resources/Strings.resx @@ -489,4 +489,7 @@ The specified request is not supported. + + System.DirectoryServices.Protocols is not supported on this platform. + \ No newline at end of file diff --git a/external/corefx/src/System.DirectoryServices.Protocols/src/System.DirectoryServices.Protocols.csproj b/external/corefx/src/System.DirectoryServices.Protocols/src/System.DirectoryServices.Protocols.csproj index 03773f23ed..f758cf426b 100644 --- a/external/corefx/src/System.DirectoryServices.Protocols/src/System.DirectoryServices.Protocols.csproj +++ b/external/corefx/src/System.DirectoryServices.Protocols/src/System.DirectoryServices.Protocols.csproj @@ -9,16 +9,15 @@ false true - true + SR.DirectoryServicesProtocols_PlatformNotSupported - - + + + + - - - - + @@ -51,5 +50,25 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/DirectoryException.cs b/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/DirectoryException.cs index 31ffa678aa..f252ac8242 100644 --- a/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/DirectoryException.cs +++ b/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/DirectoryException.cs @@ -9,11 +9,13 @@ using System.Security.Permissions; namespace System.DirectoryServices.Protocols { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.DirectoryServices.Protocols, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] +#endif public class DirectoryException : Exception { protected DirectoryException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public DirectoryException(string message, Exception inner) : base(message, inner) @@ -30,6 +32,9 @@ namespace System.DirectoryServices.Protocols } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.DirectoryServices.Protocols, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] +#endif public class DirectoryOperationException : DirectoryException, ISerializable { protected DirectoryOperationException(SerializationInfo info, StreamingContext context) : base(info, context) { } @@ -64,6 +69,9 @@ namespace System.DirectoryServices.Protocols } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.DirectoryServices.Protocols, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] +#endif public class BerConversionException : DirectoryException { protected BerConversionException(SerializationInfo info, StreamingContext context) : base(info, context) { } diff --git a/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapConnection.cs b/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapConnection.cs index a2e3743822..688f765107 100644 --- a/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapConnection.cs +++ b/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapConnection.cs @@ -1055,8 +1055,6 @@ namespace System.DirectoryServices.Protocols public void Bind(NetworkCredential newCredential) => BindHelper(newCredential, needSetCredential: true); - [EnvironmentPermission(SecurityAction.Assert, Unrestricted = true)] - [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] private void BindHelper(NetworkCredential newCredential, bool needSetCredential) { if (_disposed) diff --git a/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapException.cs b/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapException.cs index 4f80ba37f0..d5588a7da6 100644 --- a/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapException.cs +++ b/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapException.cs @@ -65,6 +65,9 @@ namespace System.DirectoryServices.Protocols } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.DirectoryServices.Protocols, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] +#endif public class LdapException : DirectoryException, ISerializable { protected LdapException(SerializationInfo info, StreamingContext context) : base(info, context) { } @@ -104,6 +107,9 @@ namespace System.DirectoryServices.Protocols } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.DirectoryServices.Protocols, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] +#endif public class TlsOperationException : DirectoryOperationException { protected TlsOperationException(SerializationInfo info, StreamingContext context) : base(info, context) { } diff --git a/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.cs b/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.cs index 83777e9aa5..572c3a2dc5 100644 --- a/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.cs +++ b/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapSessionOptions.cs @@ -1109,7 +1109,6 @@ namespace System.DirectoryServices.Protocols return value; } - [PermissionSet(SecurityAction.Assert, Unrestricted = true)] private static bool AddLdapHandleRef(LdapConnection ldapConnection) { bool success = false; @@ -1126,7 +1125,6 @@ namespace System.DirectoryServices.Protocols return success; } - [PermissionSet(SecurityAction.Assert, Unrestricted = true)] private static void ReleaseLdapHandleRef(LdapConnection ldapConnection) { if (ldapConnection != null && ldapConnection._ldapHandle != null && !ldapConnection._ldapHandle.IsInvalid) diff --git a/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/SafeHandles.cs b/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/SafeHandles.cs index c719d1afea..2053bace6a 100644 --- a/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/SafeHandles.cs +++ b/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/SafeHandles.cs @@ -8,7 +8,6 @@ using System.Security; namespace System.DirectoryServices.Protocols { - [SuppressUnmanagedCodeSecurity] internal sealed class BerSafeHandle : SafeHandleZeroOrMinusOneIsInvalid { internal BerSafeHandle() : base(true) @@ -36,7 +35,6 @@ namespace System.DirectoryServices.Protocols } } - [SuppressUnmanagedCodeSecurity] internal sealed class HGlobalMemHandle : SafeHandleZeroOrMinusOneIsInvalid { internal HGlobalMemHandle(IntPtr value) : base(true) @@ -51,7 +49,6 @@ namespace System.DirectoryServices.Protocols } } - [SuppressUnmanagedCodeSecurity] internal sealed class ConnectionHandle : SafeHandleZeroOrMinusOneIsInvalid { internal bool _needDispose = false; diff --git a/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/Wldap32UnsafeMethods.cs b/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/Wldap32UnsafeMethods.cs index 088d51ae30..868f337a0f 100644 --- a/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/Wldap32UnsafeMethods.cs +++ b/external/corefx/src/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/Wldap32UnsafeMethods.cs @@ -179,7 +179,6 @@ namespace System.DirectoryServices.Protocols } } - [SuppressUnmanagedCodeSecurity] internal class Wldap32 { private const string Wldap32dll = "wldap32.dll"; diff --git a/external/corefx/src/System.DirectoryServices.Protocols/tests/Configurations.props b/external/corefx/src/System.DirectoryServices.Protocols/tests/Configurations.props index ed4061c7d6..d8cd9ec843 100644 --- a/external/corefx/src/System.DirectoryServices.Protocols/tests/Configurations.props +++ b/external/corefx/src/System.DirectoryServices.Protocols/tests/Configurations.props @@ -2,7 +2,7 @@ - netstandard-Windows_NT; + netcoreapp-Windows_NT; diff --git a/external/corefx/src/System.DirectoryServices.Protocols/tests/DirectoryServicesProtocolsTests.cs b/external/corefx/src/System.DirectoryServices.Protocols/tests/DirectoryServicesProtocolsTests.cs new file mode 100644 index 0000000000..f3776367ec --- /dev/null +++ b/external/corefx/src/System.DirectoryServices.Protocols/tests/DirectoryServicesProtocolsTests.cs @@ -0,0 +1,593 @@ +// 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.Runtime.InteropServices; +using System.Collections.Generic; +using System.Collections; +using System.Globalization; +using System.Net; +using Xunit; +using System.Threading; +using System.DirectoryServices.Tests; +using System.DirectoryServices.Protocols; + +namespace System.DirectoryServicesProtocols.Tests +{ + public partial class DirectoryServicesProtocolsTests + { + internal static bool IsLdapConfigurationExist => LdapConfiguration.Configuration != null; + internal static bool IsActiveDirectoryServer => IsLdapConfigurationExist && LdapConfiguration.Configuration.IsActiveDirectoryServer; + + [ConditionalFact(nameof(IsLdapConfigurationExist))] + public void TestAddingOU() + { + using (LdapConnection connection = GetConnection()) + { + string ouName = "ProtocolsGroup1"; + string dn = "ou=" + ouName; + try + { + DeleteEntry(connection, dn); + AddOrganizationalUnit(connection, dn); + SearchResultEntry sre = SearchOrganizationalUnit(connection, LdapConfiguration.Configuration.Domain, ouName); + Assert.NotNull(sre); + } + finally + { + DeleteEntry(connection, dn); + } + } + } + + [ConditionalFact(nameof(IsLdapConfigurationExist))] + public void TestDeleteOU() + { + using (LdapConnection connection = GetConnection()) + { + string ouName = "ProtocolsGroup2"; + string dn = "ou=" + ouName; + try + { + DeleteEntry(connection, dn); + AddOrganizationalUnit(connection, dn); + SearchResultEntry sre = SearchOrganizationalUnit(connection, LdapConfiguration.Configuration.Domain, ouName); + Assert.NotNull(sre); + + DeleteEntry(connection, dn); + sre = SearchOrganizationalUnit(connection, LdapConfiguration.Configuration.Domain, ouName); + Assert.Null(sre); + } + finally + { + DeleteEntry(connection, dn); + } + } + } + + [ConditionalFact(nameof(IsLdapConfigurationExist))] + public void TestAddAndModifyAttribute() + { + using (LdapConnection connection = GetConnection()) + { + string ouName = "ProtocolsGroup3"; + string dn = "ou=" + ouName; + try + { + DeleteEntry(connection, dn); + AddOrganizationalUnit(connection, dn); + + AddAttribute(connection, dn, "description", "Protocols Group 3"); + SearchResultEntry sre = SearchOrganizationalUnit(connection, LdapConfiguration.Configuration.Domain, ouName); + Assert.NotNull(sre); + Assert.Equal("Protocols Group 3", (string) sre.Attributes["description"][0]); + Assert.Throws(() => AddAttribute(connection, dn, "description", "Protocols Group 3")); + + ModifyAttribute(connection, dn, "description", "Modified Protocols Group 3"); + sre = SearchOrganizationalUnit(connection, LdapConfiguration.Configuration.Domain, ouName); + Assert.NotNull(sre); + Assert.Equal("Modified Protocols Group 3", (string) sre.Attributes["description"][0]); + + DeleteAttribute(connection, dn, "description"); + sre = SearchOrganizationalUnit(connection, LdapConfiguration.Configuration.Domain, ouName); + Assert.NotNull(sre); + Assert.Null(sre.Attributes["description"]); + } + finally + { + DeleteEntry(connection, dn); + } + } + } + + [ConditionalFact(nameof(IsLdapConfigurationExist))] + public void TestNestedOUs() + { + using (LdapConnection connection = GetConnection()) + { + string ouLevel1Name = "ProtocolsGroup4-1"; + string dnLevel1 = "ou=" + ouLevel1Name; + string ouLevel2Name = "ProtocolsGroup4-2"; + string dnLevel2 = "ou=" + ouLevel2Name+ "," + dnLevel1; + + DeleteEntry(connection, dnLevel2); + DeleteEntry(connection, dnLevel1); + + try + { + AddOrganizationalUnit(connection, dnLevel1); + SearchResultEntry sre = SearchOrganizationalUnit(connection, LdapConfiguration.Configuration.Domain, ouLevel1Name); + Assert.NotNull(sre); + + AddOrganizationalUnit(connection, dnLevel2); + sre = SearchOrganizationalUnit(connection, dnLevel1 + "," + LdapConfiguration.Configuration.Domain, ouLevel2Name); + Assert.NotNull(sre); + } + finally + { + DeleteEntry(connection, dnLevel2); + DeleteEntry(connection, dnLevel1); + } + } + } + + [ConditionalFact(nameof(IsLdapConfigurationExist))] + public void TestAddUser() + { + using (LdapConnection connection = GetConnection()) + { + string ouName = "ProtocolsGroup5"; + string dn = "ou=" + ouName; + string user1Dn = "cn=protocolUser1" + "," + dn; + string user2Dn = "cn=protocolUser2" + "," + dn; + + DeleteEntry(connection, user1Dn); + DeleteEntry(connection, user2Dn); + DeleteEntry(connection, dn); + + try + { + AddOrganizationalUnit(connection, dn); + SearchResultEntry sre = SearchOrganizationalUnit(connection, LdapConfiguration.Configuration.Domain, ouName); + Assert.NotNull(sre); + + AddOrganizationalRole(connection, user1Dn); + AddOrganizationalRole(connection, user2Dn); + + string usersRoot = dn + "," + LdapConfiguration.Configuration.Domain; + + sre = SearchUser(connection, usersRoot, "protocolUser1"); + Assert.NotNull(sre); + + sre = SearchUser(connection, usersRoot, "protocolUser2"); + Assert.NotNull(sre); + + DeleteEntry(connection, user1Dn); + sre = SearchUser(connection, usersRoot, "protocolUser1"); + Assert.Null(sre); + + DeleteEntry(connection, user2Dn); + sre = SearchUser(connection, usersRoot, "protocolUser2"); + Assert.Null(sre); + + DeleteEntry(connection, dn); + sre = SearchOrganizationalUnit(connection, LdapConfiguration.Configuration.Domain, ouName); + Assert.Null(sre); + } + finally + { + DeleteEntry(connection, user1Dn); + DeleteEntry(connection, user2Dn); + DeleteEntry(connection, dn); + } + } + } + + [ConditionalFact(nameof(IsLdapConfigurationExist))] + public void TestAddingMultipleAttributes() + { + using (LdapConnection connection = GetConnection()) + { + string ouName = "ProtocolsGroup6"; + string dn = "ou=" + ouName; + try + { + DeleteEntry(connection, dn); + AddOrganizationalUnit(connection, dn); + + DirectoryAttributeModification mod1 = new DirectoryAttributeModification(); + mod1.Operation = DirectoryAttributeOperation.Add; + mod1.Name = "description"; + mod1.Add("Description 5"); + + DirectoryAttributeModification mod2 = new DirectoryAttributeModification(); + mod2.Operation = DirectoryAttributeOperation.Add; + mod2.Name = "postalAddress"; + mod2.Add("123 4th Ave NE, State, Country"); + + DirectoryAttributeModification[] mods = new DirectoryAttributeModification[2] { mod1, mod2 }; + + string fullDn = dn + "," + LdapConfiguration.Configuration.Domain; + + ModifyRequest modRequest = new ModifyRequest(fullDn, mods); + ModifyResponse modResponse = (ModifyResponse) connection.SendRequest(modRequest); + Assert.Equal(ResultCode.Success, modResponse.ResultCode); + Assert.Throws(() => (ModifyResponse) connection.SendRequest(modRequest)); + + SearchResultEntry sre = SearchOrganizationalUnit(connection, LdapConfiguration.Configuration.Domain, ouName); + Assert.NotNull(sre); + Assert.Equal("Description 5", (string) sre.Attributes["description"][0]); + Assert.Throws(() => AddAttribute(connection, dn, "description", "Description 5")); + Assert.Equal("123 4th Ave NE, State, Country", (string) sre.Attributes["postalAddress"][0]); + Assert.Throws(() => AddAttribute(connection, dn, "postalAddress", "123 4th Ave NE, State, Country")); + + mod1 = new DirectoryAttributeModification(); + mod1.Operation = DirectoryAttributeOperation.Replace; + mod1.Name = "description"; + mod1.Add("Modified Description 5"); + + mod2 = new DirectoryAttributeModification(); + mod2.Operation = DirectoryAttributeOperation.Replace; + mod2.Name = "postalAddress"; + mod2.Add("689 5th Ave NE, State, Country"); + mods = new DirectoryAttributeModification[2] { mod1, mod2 }; + modRequest = new ModifyRequest(fullDn, mods); + modResponse = (ModifyResponse) connection.SendRequest(modRequest); + Assert.Equal(ResultCode.Success, modResponse.ResultCode); + + sre = SearchOrganizationalUnit(connection, LdapConfiguration.Configuration.Domain, ouName); + Assert.NotNull(sre); + Assert.Equal("Modified Description 5", (string) sre.Attributes["description"][0]); + Assert.Throws(() => AddAttribute(connection, dn, "description", "Modified Description 5")); + Assert.Equal("689 5th Ave NE, State, Country", (string) sre.Attributes["postalAddress"][0]); + Assert.Throws(() => AddAttribute(connection, dn, "postalAddress", "689 5th Ave NE, State, Country")); + + mod1 = new DirectoryAttributeModification(); + mod1.Operation = DirectoryAttributeOperation.Delete; + mod1.Name = "description"; + + mod2 = new DirectoryAttributeModification(); + mod2.Operation = DirectoryAttributeOperation.Delete; + mod2.Name = "postalAddress"; + mods = new DirectoryAttributeModification[2] { mod1, mod2 }; + modRequest = new ModifyRequest(fullDn, mods); + modResponse = (ModifyResponse) connection.SendRequest(modRequest); + Assert.Equal(ResultCode.Success, modResponse.ResultCode); + + sre = SearchOrganizationalUnit(connection, LdapConfiguration.Configuration.Domain, ouName); + Assert.NotNull(sre); + Assert.Null(sre.Attributes["description"]); + Assert.Null(sre.Attributes["postalAddress"]); + } + finally + { + DeleteEntry(connection, dn); + } + } + } + + [ConditionalFact(nameof(IsLdapConfigurationExist))] + public void TestMoveAndRenameUser() + { + using (LdapConnection connection = GetConnection()) + { + string ouName1 = "ProtocolsGroup7.1"; + string dn1 = "ou=" + ouName1; + + string ouName2 = "ProtocolsGroup7.2"; + string dn2 = "ou=" + ouName2; + + string userDn1 = "cn=protocolUser7.1" + "," + dn1; + string userDn2 = "cn=protocolUser7.2" + "," + dn2; + + DeleteEntry(connection, userDn1); + DeleteEntry(connection, userDn2); + DeleteEntry(connection, dn1); + DeleteEntry(connection, dn2); + + try + { + AddOrganizationalUnit(connection, dn1); + SearchResultEntry sre = SearchOrganizationalUnit(connection, LdapConfiguration.Configuration.Domain, ouName1); + Assert.NotNull(sre); + + AddOrganizationalUnit(connection, dn2); + sre = SearchOrganizationalUnit(connection, LdapConfiguration.Configuration.Domain, ouName2); + Assert.NotNull(sre); + + AddOrganizationalRole(connection, userDn1); + + string user1Root = dn1 + "," + LdapConfiguration.Configuration.Domain; + string user2Root = dn2 + "," + LdapConfiguration.Configuration.Domain; + + sre = SearchUser(connection, user1Root, "protocolUser7.1"); + Assert.NotNull(sre); + + ModifyDNRequest modDnRequest = new ModifyDNRequest( userDn1 + "," + LdapConfiguration.Configuration.Domain, + dn2 + "," + LdapConfiguration.Configuration.Domain, + "cn=protocolUser7.2"); + ModifyDNResponse modDnResponse = (ModifyDNResponse) connection.SendRequest(modDnRequest); + Assert.Equal(ResultCode.Success, modDnResponse.ResultCode); + + sre = SearchUser(connection, user1Root, "protocolUser7.1"); + Assert.Null(sre); + + sre = SearchUser(connection, user2Root, "protocolUser7.2"); + Assert.NotNull(sre); + } + finally + { + DeleteEntry(connection, userDn1); + DeleteEntry(connection, userDn2); + DeleteEntry(connection, dn1); + DeleteEntry(connection, dn2); + } + } + } + + [ConditionalFact(nameof(IsLdapConfigurationExist))] + public void TestAsyncSearch() + { + using (LdapConnection connection = GetConnection()) + { + string ouName = "ProtocolsGroup9"; + string dn = "ou=" + ouName; + + try + { + for (int i=0; i<20; i++) + { + DeleteEntry(connection, "ou=ProtocolsSubGroup9." + i + "," + dn); + } + DeleteEntry(connection, dn); + + AddOrganizationalUnit(connection, dn); + SearchResultEntry sre = SearchOrganizationalUnit(connection, LdapConfiguration.Configuration.Domain, ouName); + Assert.NotNull(sre); + + for (int i=0; i<20; i++) + { + AddOrganizationalUnit(connection, "ou=ProtocolsSubGroup9." + i + "," + dn); + } + + string filter = "(objectClass=organizationalUnit)"; + SearchRequest searchRequest = new SearchRequest( + dn + "," + LdapConfiguration.Configuration.Domain, + filter, + SearchScope.OneLevel, + null); + + ASyncOperationState state = new ASyncOperationState(connection); + IAsyncResult asyncResult = connection.BeginSendRequest( + searchRequest, + PartialResultProcessing.ReturnPartialResultsAndNotifyCallback, + RunAsyncSearch, + state); + + asyncResult.AsyncWaitHandle.WaitOne(); + Assert.True(state.Exception == null, state.Exception == null ? "" : state.Exception.ToString()); + } + finally + { + for (int i=0; i<20; i++) + { + DeleteEntry(connection, "ou=ProtocolsSubGroup9." + i + "," + dn); + } + DeleteEntry(connection, dn); + } + } + } + + private static void RunAsyncSearch(IAsyncResult asyncResult) + { + ASyncOperationState state = (ASyncOperationState) asyncResult.AsyncState; + + try + { + if (!asyncResult.IsCompleted) + { + PartialResultsCollection partialResult = null; + partialResult = state.Connection.GetPartialResults(asyncResult); + + if (partialResult != null) + { + for (int i = 0; i < partialResult.Count; i++) + { + if (partialResult[i] is SearchResultEntry) + { + Assert.True(((SearchResultEntry)partialResult[i]).DistinguishedName.Contains("Group9")); + } + } + } + } + else + { + SearchResponse response = (SearchResponse) state.Connection.EndSendRequest(asyncResult); + + if (response != null) + { + foreach (SearchResultEntry entry in response.Entries) + { + Assert.True(entry.DistinguishedName.Contains("Group9")); + } + } + } + } + catch (Exception e) + { + state.Exception = e; + } + } + + [ConditionalFact(nameof(IsActiveDirectoryServer))] + public void TestPageRequests() + { + using (LdapConnection connection = GetConnection()) + { + string ouName = "ProtocolsGroup8"; + string dn = "ou=" + ouName; + + try + { + for (int i=0; i<20; i++) + { + DeleteEntry(connection, "ou=ProtocolsSubGroup8." + i + "," + dn); + } + DeleteEntry(connection, dn); + + AddOrganizationalUnit(connection, dn); + SearchResultEntry sre = SearchOrganizationalUnit(connection, LdapConfiguration.Configuration.Domain, ouName); + Assert.NotNull(sre); + + for (int i=0; i<20; i++) + { + AddOrganizationalUnit(connection, "ou=ProtocolsSubGroup8." + i + "," + dn); + } + + string filter = "(objectClass=*)"; + SearchRequest searchRequest = new SearchRequest( + dn + "," + LdapConfiguration.Configuration.Domain, + filter, + SearchScope.Subtree, + null); + + PageResultRequestControl pageRequest = new PageResultRequestControl(5); + searchRequest.Controls.Add(pageRequest); + SearchOptionsControl searchOptions = new SearchOptionsControl(SearchOption.DomainScope); + searchRequest.Controls.Add(searchOptions); + while (true) + { + SearchResponse searchResponse = (SearchResponse) connection.SendRequest(searchRequest); + Assert.Equal(1, searchResponse.Controls.Length); + Assert.True(searchResponse.Controls[0] is PageResultResponseControl); + + PageResultResponseControl pageResponse = (PageResultResponseControl) searchResponse.Controls[0]; + + if (pageResponse.Cookie.Length == 0) + break; + + pageRequest.Cookie = pageResponse.Cookie; + } + } + finally + { + for (int i=0; i<20; i++) + { + DeleteEntry(connection, "ou=ProtocolsSubGroup8." + i + "," + dn); + } + DeleteEntry(connection, dn); + } + } + } + + private void DeleteAttribute(LdapConnection connection, string entryDn, string attributeName) + { + string dn = entryDn + "," + LdapConfiguration.Configuration.Domain; + ModifyRequest modifyRequest = new ModifyRequest(dn, DirectoryAttributeOperation.Delete, attributeName); + ModifyResponse modifyResponse = (ModifyResponse) connection.SendRequest(modifyRequest); + Assert.Equal(ResultCode.Success, modifyResponse.ResultCode); + } + + private void ModifyAttribute(LdapConnection connection, string entryDn, string attributeName, string attributeValue) + { + string dn = entryDn + "," + LdapConfiguration.Configuration.Domain; + ModifyRequest modifyRequest = new ModifyRequest(dn, DirectoryAttributeOperation.Replace, attributeName, attributeValue); + ModifyResponse modifyResponse = (ModifyResponse) connection.SendRequest(modifyRequest); + Assert.Equal(ResultCode.Success, modifyResponse.ResultCode); + } + + private void AddAttribute(LdapConnection connection, string entryDn, string attributeName, string attributeValue) + { + string dn = entryDn + "," + LdapConfiguration.Configuration.Domain; + ModifyRequest modifyRequest = new ModifyRequest(dn, DirectoryAttributeOperation.Add, attributeName, attributeValue); + ModifyResponse modifyResponse = (ModifyResponse) connection.SendRequest(modifyRequest); + Assert.Equal(ResultCode.Success, modifyResponse.ResultCode); + } + + private void AddOrganizationalUnit(LdapConnection connection, string entryDn) + { + string dn = entryDn + "," + LdapConfiguration.Configuration.Domain; + AddRequest addRequest = new AddRequest(dn, "organizationalUnit"); + AddResponse addResponse = (AddResponse) connection.SendRequest(addRequest); + Assert.Equal(ResultCode.Success, addResponse.ResultCode); + } + + private void AddOrganizationalRole(LdapConnection connection, string entryDn) + { + string dn = entryDn + "," + LdapConfiguration.Configuration.Domain; + AddRequest addRequest = new AddRequest(dn, "organizationalRole"); + AddResponse addResponse = (AddResponse) connection.SendRequest(addRequest); + Assert.Equal(ResultCode.Success, addResponse.ResultCode); + } + + private void DeleteEntry(LdapConnection connection, string entryDn) + { + try + { + string dn = entryDn + "," + LdapConfiguration.Configuration.Domain; + DeleteRequest delRequest = new DeleteRequest(dn); + DeleteResponse delResponse = (DeleteResponse) connection.SendRequest(delRequest); + Assert.Equal(ResultCode.Success, delResponse.ResultCode); + } + catch + { + // ignore the exception as we use this for clean up + } + } + + private SearchResultEntry SearchOrganizationalUnit(LdapConnection connection, string rootDn, string ouName) + { + string filter = $"(&(objectClass=organizationalUnit)(ou={ouName}))"; + SearchRequest searchRequest = new SearchRequest(rootDn, filter, SearchScope.OneLevel, null); + SearchResponse searchResponse = (SearchResponse) connection.SendRequest(searchRequest); + + if (searchResponse.Entries.Count > 0) + return searchResponse.Entries[0]; + + return null; + } + + private SearchResultEntry SearchUser(LdapConnection connection, string rootDn, string userName) + { + string filter = $"(&(objectClass=organizationalRole)(cn={userName}))"; + SearchRequest searchRequest = new SearchRequest(rootDn, filter, SearchScope.OneLevel, null); + SearchResponse searchResponse = (SearchResponse) connection.SendRequest(searchRequest); + + if (searchResponse.Entries.Count > 0) + return searchResponse.Entries[0]; + + return null; + } + + private LdapConnection GetConnection() + { + LdapDirectoryIdentifier directoryIdentifier = String.IsNullOrEmpty(LdapConfiguration.Configuration.Port) ? + new LdapDirectoryIdentifier(LdapConfiguration.Configuration.ServerName, true, false) : + new LdapDirectoryIdentifier(LdapConfiguration.Configuration.ServerName, + int.Parse(LdapConfiguration.Configuration.Port, NumberStyles.None, CultureInfo.InvariantCulture), + true, false); + NetworkCredential credential = new NetworkCredential(LdapConfiguration.Configuration.UserName, LdapConfiguration.Configuration.Password); + + LdapConnection connection = new LdapConnection(directoryIdentifier, credential) + { + AuthType = AuthType.Basic + }; + + connection.Bind(); + connection.SessionOptions.ProtocolVersion = 3; + connection.Timeout = new TimeSpan(0, 3, 0); + return connection; + } + } + + internal class ASyncOperationState + { + internal ASyncOperationState(LdapConnection connection) + { + Connection = connection; + } + + internal LdapConnection Connection { get; set; } + internal Exception Exception { get; set; } + } +} diff --git a/external/corefx/src/System.DirectoryServices.Protocols/tests/LDAP.Configuration.xml b/external/corefx/src/System.DirectoryServices.Protocols/tests/LDAP.Configuration.xml new file mode 100644 index 0000000000..874c6a5c67 --- /dev/null +++ b/external/corefx/src/System.DirectoryServices.Protocols/tests/LDAP.Configuration.xml @@ -0,0 +1,19 @@ + + \ No newline at end of file diff --git a/external/corefx/src/System.DirectoryServices.Protocols/tests/SortRequestControlTests.cs b/external/corefx/src/System.DirectoryServices.Protocols/tests/SortRequestControlTests.cs index 485f60edfb..52407eef52 100644 --- a/external/corefx/src/System.DirectoryServices.Protocols/tests/SortRequestControlTests.cs +++ b/external/corefx/src/System.DirectoryServices.Protocols/tests/SortRequestControlTests.cs @@ -92,7 +92,7 @@ namespace System.DirectoryServices.Protocols.Tests } [Fact] - [ActiveIssue("https://github.com/dotnet/corefx/issues/21217", TargetFrameworkMonikers.UapAot)] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "The field _keys in full framework is called keys, so GetField returns null and ends up in a NRE")] public void SortKeys_GetNull_ReturnsEmptyArray() { var control = new SortRequestControl(); diff --git a/external/corefx/src/System.DirectoryServices.Protocols/tests/System.DirectoryServices.Protocols.Tests.csproj b/external/corefx/src/System.DirectoryServices.Protocols/tests/System.DirectoryServices.Protocols.Tests.csproj index 2a1e8f98f3..9d293444d1 100644 --- a/external/corefx/src/System.DirectoryServices.Protocols/tests/System.DirectoryServices.Protocols.Tests.csproj +++ b/external/corefx/src/System.DirectoryServices.Protocols/tests/System.DirectoryServices.Protocols.Tests.csproj @@ -4,9 +4,8 @@ {6638C675-CD62-408F-AB3B-AAFD8A906A96} - - - + + @@ -51,6 +50,16 @@ + + + + Common\DirectoryServices\LdapConfiguration.cs + + + + + PreserveNewest + \ No newline at end of file diff --git a/external/corefx/src/System.DirectoryServices/System.DirectoryServices.sln b/external/corefx/src/System.DirectoryServices/System.DirectoryServices.sln index 5a23ce72ee..0ab41805a6 100644 --- a/external/corefx/src/System.DirectoryServices/System.DirectoryServices.sln +++ b/external/corefx/src/System.DirectoryServices/System.DirectoryServices.sln @@ -26,14 +26,14 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {DDE3838B-0EEA-4D9A-A120-9D72CB33F250}.Debug|Any CPU.ActiveCfg = netstandard-Windows_NT-Debug|Any CPU - {DDE3838B-0EEA-4D9A-A120-9D72CB33F250}.Debug|Any CPU.Build.0 = netstandard-Windows_NT-Debug|Any CPU - {DDE3838B-0EEA-4D9A-A120-9D72CB33F250}.Release|Any CPU.ActiveCfg = netstandard-Windows_NT-Release|Any CPU - {DDE3838B-0EEA-4D9A-A120-9D72CB33F250}.Release|Any CPU.Build.0 = netstandard-Windows_NT-Release|Any CPU - {EC9B0FBC-C3A2-44E6-BFC6-51E565061C28}.Debug|Any CPU.ActiveCfg = netstandard-Windows_NT-Debug|Any CPU - {EC9B0FBC-C3A2-44E6-BFC6-51E565061C28}.Debug|Any CPU.Build.0 = netstandard-Windows_NT-Debug|Any CPU - {EC9B0FBC-C3A2-44E6-BFC6-51E565061C28}.Release|Any CPU.ActiveCfg = netstandard-Windows_NT-Release|Any CPU - {EC9B0FBC-C3A2-44E6-BFC6-51E565061C28}.Release|Any CPU.Build.0 = netstandard-Windows_NT-Release|Any CPU + {DDE3838B-0EEA-4D9A-A120-9D72CB33F250}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {DDE3838B-0EEA-4D9A-A120-9D72CB33F250}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {DDE3838B-0EEA-4D9A-A120-9D72CB33F250}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {DDE3838B-0EEA-4D9A-A120-9D72CB33F250}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU + {EC9B0FBC-C3A2-44E6-BFC6-51E565061C28}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {EC9B0FBC-C3A2-44E6-BFC6-51E565061C28}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {EC9B0FBC-C3A2-44E6-BFC6-51E565061C28}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {EC9B0FBC-C3A2-44E6-BFC6-51E565061C28}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU {CBCDA53B-4C01-4267-B08C-413205FE4D8D}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU {CBCDA53B-4C01-4267-B08C-413205FE4D8D}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU {CBCDA53B-4C01-4267-B08C-413205FE4D8D}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU diff --git a/external/corefx/src/System.DirectoryServices/ref/System.DirectoryServices.cs.REMOVED.git-id b/external/corefx/src/System.DirectoryServices/ref/System.DirectoryServices.cs.REMOVED.git-id index acf1ca2523..d7fb8c0d92 100644 --- a/external/corefx/src/System.DirectoryServices/ref/System.DirectoryServices.cs.REMOVED.git-id +++ b/external/corefx/src/System.DirectoryServices/ref/System.DirectoryServices.cs.REMOVED.git-id @@ -1 +1 @@ -7c05b62b4a0146a90d3b44b8f670cb06ffc363d6 \ No newline at end of file +cbdd76be90ee9578bb88b1f24c80f08c898a20e0 \ No newline at end of file diff --git a/external/corefx/src/System.DirectoryServices/src/Configurations.props b/external/corefx/src/System.DirectoryServices/src/Configurations.props index 64d654a29f..94ac07fdab 100644 --- a/external/corefx/src/System.DirectoryServices/src/Configurations.props +++ b/external/corefx/src/System.DirectoryServices/src/Configurations.props @@ -1,9 +1,13 @@ - - netstandard-Windows_NT; + netstandard; + netcoreapp2.0-Windows_NT; + + + $(PackageConfigurations); + netcoreapp-Windows_NT; diff --git a/external/corefx/src/System.DirectoryServices/src/FxCopBaseline.AnyOS.cs b/external/corefx/src/System.DirectoryServices/src/FxCopBaseline.AnyOS.cs deleted file mode 100644 index 8f8b8d9856..0000000000 --- a/external/corefx/src/System.DirectoryServices/src/FxCopBaseline.AnyOS.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA1821", Justification = "Finalizer has implementation in Windows version.")] \ No newline at end of file diff --git a/external/corefx/src/System.DirectoryServices/src/Interop/SafeNativeMethods.cs b/external/corefx/src/System.DirectoryServices/src/Interop/SafeNativeMethods.cs index 5aec29efff..eeadafc0c6 100644 --- a/external/corefx/src/System.DirectoryServices/src/Interop/SafeNativeMethods.cs +++ b/external/corefx/src/System.DirectoryServices/src/Interop/SafeNativeMethods.cs @@ -9,7 +9,6 @@ using System.Runtime.InteropServices; namespace System.DirectoryServices.Interop { #pragma warning disable BCL0015 // CoreFxPort - [SuppressUnmanagedCodeSecurity] internal class SafeNativeMethods { [DllImport(ExternDll.Oleaut32, PreserveSig = false)] @@ -122,18 +121,14 @@ namespace System.DirectoryServices.Interop [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IEnumVariant { - [SuppressUnmanagedCodeSecurity] void Next([In, MarshalAs(UnmanagedType.U4)] int celt, [In, Out] IntPtr rgvar, [Out, MarshalAs(UnmanagedType.LPArray)] int[] pceltFetched); - [SuppressUnmanagedCodeSecurity] void Skip([In, MarshalAs(UnmanagedType.U4)] int celt); - [SuppressUnmanagedCodeSecurity] void Reset(); - [SuppressUnmanagedCodeSecurity] void Clone([Out, MarshalAs(UnmanagedType.LPArray)] IEnumVariant[] ppenum); } } diff --git a/external/corefx/src/System.DirectoryServices/src/Interop/UnsafeNativeMethods.cs b/external/corefx/src/System.DirectoryServices/src/Interop/UnsafeNativeMethods.cs index cc9b3dd757..36663f0276 100644 --- a/external/corefx/src/System.DirectoryServices/src/Interop/UnsafeNativeMethods.cs +++ b/external/corefx/src/System.DirectoryServices/src/Interop/UnsafeNativeMethods.cs @@ -28,7 +28,6 @@ namespace System.DirectoryServices.Interop public IntPtr ptr2; } - [SuppressUnmanagedCodeSecurity] internal class UnsafeNativeMethods { [DllImport(ExternDll.Activeds, ExactSpelling = true, EntryPoint = "ADsOpenObject", CharSet = System.Runtime.InteropServices.CharSet.Unicode)] @@ -52,67 +51,55 @@ namespace System.DirectoryServices.Interop string Name { [return: MarshalAs(UnmanagedType.BStr)] - [SuppressUnmanagedCodeSecurity] get; } string Class { [return: MarshalAs(UnmanagedType.BStr)] - [SuppressUnmanagedCodeSecurity] get; } string GUID { [return: MarshalAs(UnmanagedType.BStr)] - [SuppressUnmanagedCodeSecurity] get; } string ADsPath { [return: MarshalAs(UnmanagedType.BStr)] - [SuppressUnmanagedCodeSecurity] get; } string Parent { [return: MarshalAs(UnmanagedType.BStr)] - [SuppressUnmanagedCodeSecurity] get; } string Schema { [return: MarshalAs(UnmanagedType.BStr)] - [SuppressUnmanagedCodeSecurity] get; } - [SuppressUnmanagedCodeSecurity] void GetInfo(); - [SuppressUnmanagedCodeSecurity] void SetInfo(); object Get([In, MarshalAs(UnmanagedType.BStr)] string bstrName); - [SuppressUnmanagedCodeSecurity] void Put([In, MarshalAs(UnmanagedType.BStr)] string bstrName, [In] object vProp); - [SuppressUnmanagedCodeSecurity] [PreserveSig] int GetEx([In, MarshalAs(UnmanagedType.BStr)] string bstrName, [Out] out object value); - [SuppressUnmanagedCodeSecurity] void PutEx( [In, MarshalAs(UnmanagedType.U4)] int lnControlCode, [In, MarshalAs(UnmanagedType.BStr)] string bstrName, [In] object vProp); - [SuppressUnmanagedCodeSecurity] void GetInfoEx([In] object vProperties, [In, MarshalAs(UnmanagedType.U4)] int lnReserved); } @@ -122,14 +109,12 @@ namespace System.DirectoryServices.Interop int Count { [return: MarshalAs(UnmanagedType.U4)] - [SuppressUnmanagedCodeSecurity] get; } object _NewEnum { [return: MarshalAs(UnmanagedType.Interface)] - [SuppressUnmanagedCodeSecurity] get; } @@ -138,30 +123,25 @@ namespace System.DirectoryServices.Interop object Hints { get; set; } [return: MarshalAs(UnmanagedType.Interface)] - [SuppressUnmanagedCodeSecurity] object GetObject( [In, MarshalAs(UnmanagedType.BStr)] string className, [In, MarshalAs(UnmanagedType.BStr)] string relativeName); [return: MarshalAs(UnmanagedType.Interface)] - [SuppressUnmanagedCodeSecurity] object Create( [In, MarshalAs(UnmanagedType.BStr)] string className, [In, MarshalAs(UnmanagedType.BStr)] string relativeName); - [SuppressUnmanagedCodeSecurity] void Delete( [In, MarshalAs(UnmanagedType.BStr)] string className, [In, MarshalAs(UnmanagedType.BStr)] string relativeName); [return: MarshalAs(UnmanagedType.Interface)] - [SuppressUnmanagedCodeSecurity] object CopyHere( [In, MarshalAs(UnmanagedType.BStr)] string sourceName, [In, MarshalAs(UnmanagedType.BStr)] string newName); [return: MarshalAs(UnmanagedType.Interface)] - [SuppressUnmanagedCodeSecurity] object MoveHere( [In, MarshalAs(UnmanagedType.BStr)] string sourceName, [In, MarshalAs(UnmanagedType.BStr)] string newName); @@ -170,7 +150,6 @@ namespace System.DirectoryServices.Interop [ComImport, Guid("B2BD0902-8878-11D1-8C21-00C04FD8D503")] public interface IAdsDeleteOps { - [SuppressUnmanagedCodeSecurity] void DeleteObject(int flags); } @@ -192,21 +171,17 @@ namespace System.DirectoryServices.Interop [ComImport, Guid("79FA9AD0-A97C-11D0-8534-00C04FD8D503")] public interface IAdsPropertyValue { - [SuppressUnmanagedCodeSecurity] void Clear(); int ADsType { - [SuppressUnmanagedCodeSecurity] get; - [SuppressUnmanagedCodeSecurity] set; } string DNString { [return: MarshalAs(UnmanagedType.BStr)] - [SuppressUnmanagedCodeSecurity] get; [param: MarshalAs(UnmanagedType.BStr)] set; @@ -215,7 +190,6 @@ namespace System.DirectoryServices.Interop string CaseExactString { [return: MarshalAs(UnmanagedType.BStr)] - [SuppressUnmanagedCodeSecurity] get; [param: MarshalAs(UnmanagedType.BStr)] set; @@ -224,7 +198,6 @@ namespace System.DirectoryServices.Interop string CaseIgnoreString { [return: MarshalAs(UnmanagedType.BStr)] - [SuppressUnmanagedCodeSecurity] get; [param: MarshalAs(UnmanagedType.BStr)] set; @@ -233,7 +206,6 @@ namespace System.DirectoryServices.Interop string PrintableString { [return: MarshalAs(UnmanagedType.BStr)] - [SuppressUnmanagedCodeSecurity] get; [param: MarshalAs(UnmanagedType.BStr)] set; @@ -242,7 +214,6 @@ namespace System.DirectoryServices.Interop string NumericString { [return: MarshalAs(UnmanagedType.BStr)] - [SuppressUnmanagedCodeSecurity] get; [param: MarshalAs(UnmanagedType.BStr)] set; @@ -254,16 +225,13 @@ namespace System.DirectoryServices.Interop object OctetString { - [SuppressUnmanagedCodeSecurity] get; - [SuppressUnmanagedCodeSecurity] set; } object SecurityDescriptor { - [SuppressUnmanagedCodeSecurity] get; set; @@ -271,7 +239,6 @@ namespace System.DirectoryServices.Interop object LargeInteger { - [SuppressUnmanagedCodeSecurity] get; set; @@ -279,7 +246,6 @@ namespace System.DirectoryServices.Interop object UTCTime { - [SuppressUnmanagedCodeSecurity] get; set; @@ -297,32 +263,25 @@ namespace System.DirectoryServices.Interop [ComImport, Guid("05792C8E-941F-11D0-8529-00C04FD8D503")] public interface IAdsPropertyEntry { - [SuppressUnmanagedCodeSecurity] void Clear(); string Name { [return: MarshalAs(UnmanagedType.BStr)] - [SuppressUnmanagedCodeSecurity] get; [param: MarshalAs(UnmanagedType.BStr)] - [SuppressUnmanagedCodeSecurity] set; } int ADsType { - [SuppressUnmanagedCodeSecurity] get; - [SuppressUnmanagedCodeSecurity] set; } int ControlCode { - [SuppressUnmanagedCodeSecurity] get; - [SuppressUnmanagedCodeSecurity] set; } @@ -335,25 +294,21 @@ namespace System.DirectoryServices.Interop int PropertyCount { [return: MarshalAs(UnmanagedType.U4)] - [SuppressUnmanagedCodeSecurity] get; } [return: MarshalAs(UnmanagedType.I4)] - [SuppressUnmanagedCodeSecurity] [PreserveSig] int Next([Out] out object nextProp); void Skip([In] int cElements); - [SuppressUnmanagedCodeSecurity] void Reset(); object Item([In] object varIndex); object GetPropertyItem([In, MarshalAs(UnmanagedType.BStr)] string bstrName, int ADsType); - [SuppressUnmanagedCodeSecurity] void PutPropertyItem([In] object varData); void ResetPropertyItem([In] object varEntry); @@ -364,51 +319,41 @@ namespace System.DirectoryServices.Interop [ComImport, Guid("109BA8EC-92F0-11D0-A790-00C04FD8D5A8"), System.Runtime.InteropServices.InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)] public interface IDirectorySearch { - [SuppressUnmanagedCodeSecurity] void SetSearchPreference([In] IntPtr /*ads_searchpref_info * */pSearchPrefs, int dwNumPrefs); - [SuppressUnmanagedCodeSecurity] void ExecuteSearch( [In, MarshalAs(UnmanagedType.LPWStr)] string pszSearchFilter, [In, MarshalAs(UnmanagedType.LPArray)] string[] pAttributeNames, [In] int dwNumberAttributes, [Out] out IntPtr hSearchResult); - [SuppressUnmanagedCodeSecurity] void AbandonSearch([In] IntPtr hSearchResult); [return: MarshalAs(UnmanagedType.U4)] - [SuppressUnmanagedCodeSecurity] [PreserveSig] int GetFirstRow([In] IntPtr hSearchResult); [return: MarshalAs(UnmanagedType.U4)] - [SuppressUnmanagedCodeSecurity] [PreserveSig] int GetNextRow([In] IntPtr hSearchResult); [return: MarshalAs(UnmanagedType.U4)] - [SuppressUnmanagedCodeSecurity] [PreserveSig] int GetPreviousRow([In] IntPtr hSearchResult); [return: MarshalAs(UnmanagedType.U4)] - [SuppressUnmanagedCodeSecurity] [PreserveSig] int GetNextColumnName( [In] IntPtr hSearchResult, [Out] IntPtr ppszColumnName); - [SuppressUnmanagedCodeSecurity] void GetColumn( [In] IntPtr hSearchResult, [In] IntPtr /* char * */ szColumnName, [In] IntPtr pSearchColumn); - [SuppressUnmanagedCodeSecurity] void FreeColumn([In] IntPtr pSearchColumn); - [SuppressUnmanagedCodeSecurity] void CloseSearchHandle([In] IntPtr hSearchResult); } @@ -417,7 +362,6 @@ namespace System.DirectoryServices.Interop { object GetOption(int flag); - [SuppressUnmanagedCodeSecurity] void SetOption(int flag, [In] object varValue); } @@ -429,11 +373,9 @@ namespace System.DirectoryServices.Interop [ComImport, Guid("46F14FDA-232B-11D1-A808-00C04FD8D5A8")] public interface IAdsObjectOptions2 { - [SuppressUnmanagedCodeSecurity] [PreserveSig] int GetOption(int flag, [Out] out object value); - [SuppressUnmanagedCodeSecurity] void SetOption(int option, Variant value); } diff --git a/external/corefx/src/System.DirectoryServices/src/Resources/Strings.resx b/external/corefx/src/System.DirectoryServices/src/Resources/Strings.resx index 4d288c2c64..8cf0b91b61 100644 --- a/external/corefx/src/System.DirectoryServices/src/Resources/Strings.resx +++ b/external/corefx/src/System.DirectoryServices/src/Resources/Strings.resx @@ -1,5 +1,64 @@  + @@ -526,5 +585,8 @@ The value exceeds the maximum allowed. + + System.DirectoryServices is not supported on this platform. + diff --git a/external/corefx/src/System.DirectoryServices/src/System.DirectoryServices.csproj b/external/corefx/src/System.DirectoryServices/src/System.DirectoryServices.csproj index 4bb3676351..7be16cb4de 100644 --- a/external/corefx/src/System.DirectoryServices/src/System.DirectoryServices.csproj +++ b/external/corefx/src/System.DirectoryServices/src/System.DirectoryServices.csproj @@ -8,16 +8,15 @@ $(NoWarn);0649 false - true + SR.DirectoryServices_PlatformNotSupported - - + + + + - - - - + @@ -154,5 +153,20 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/DirectoryContext.cs b/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/DirectoryContext.cs index 98885b040c..045775e8db 100644 --- a/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/DirectoryContext.cs +++ b/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/DirectoryContext.cs @@ -37,7 +37,6 @@ namespace System.DirectoryServices.ActiveDirectory #region constructors - [EnvironmentPermission(SecurityAction.Assert, Unrestricted = true)] static DirectoryContext() { // load ntdsapi.dll for AD and ADAM @@ -178,7 +177,6 @@ namespace System.DirectoryServices.ActiveDirectory internal string Password { - [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] get => passwordIsNull ? null : _credential.Password; } @@ -684,7 +682,6 @@ namespace System.DirectoryServices.ActiveDirectory return domainControllerInfo.DomainName; } - [EnvironmentPermission(SecurityAction.Assert, Unrestricted = true)] private static void GetLibraryHandle() { // first get AD handle diff --git a/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/Exception.cs b/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/Exception.cs index e9a9d2e289..d82212a10a 100644 --- a/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/Exception.cs +++ b/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/Exception.cs @@ -34,6 +34,9 @@ namespace System.DirectoryServices.ActiveDirectory } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.DirectoryServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] +#endif public class ActiveDirectoryObjectNotFoundException : Exception, ISerializable { public ActiveDirectoryObjectNotFoundException(string message, Type type, string name) : base(message) @@ -50,14 +53,12 @@ namespace System.DirectoryServices.ActiveDirectory protected ActiveDirectoryObjectNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public Type Type { get; } public string Name { get; } - [SecurityPermissionAttribute(SecurityAction.LinkDemand, SerializationFormatter = true)] public override void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) { base.GetObjectData(serializationInfo, streamingContext); @@ -65,6 +66,9 @@ namespace System.DirectoryServices.ActiveDirectory } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.DirectoryServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] +#endif public class ActiveDirectoryOperationException : Exception, ISerializable { public ActiveDirectoryOperationException(string message, Exception inner, int errorCode) : base(message, inner) @@ -85,7 +89,6 @@ namespace System.DirectoryServices.ActiveDirectory protected ActiveDirectoryOperationException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public int ErrorCode { get; } @@ -97,6 +100,9 @@ namespace System.DirectoryServices.ActiveDirectory } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.DirectoryServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] +#endif public class ActiveDirectoryServerDownException : Exception, ISerializable { public ActiveDirectoryServerDownException(string message, Exception inner, int errorCode, string name) : base(message, inner) @@ -119,7 +125,6 @@ namespace System.DirectoryServices.ActiveDirectory protected ActiveDirectoryServerDownException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public int ErrorCode { get; } @@ -146,6 +151,9 @@ namespace System.DirectoryServices.ActiveDirectory } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.DirectoryServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] +#endif public class ActiveDirectoryObjectExistsException : Exception { public ActiveDirectoryObjectExistsException(string message, Exception inner) : base(message, inner) { } @@ -156,11 +164,13 @@ namespace System.DirectoryServices.ActiveDirectory protected ActiveDirectoryObjectExistsException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.DirectoryServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] +#endif public class SyncFromAllServersOperationException : ActiveDirectoryOperationException, ISerializable { private SyncFromAllServersErrorInformation[] _errors = null; @@ -178,7 +188,6 @@ namespace System.DirectoryServices.ActiveDirectory protected SyncFromAllServersOperationException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public SyncFromAllServersErrorInformation[] ErrorInformation @@ -203,6 +212,9 @@ namespace System.DirectoryServices.ActiveDirectory } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.DirectoryServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] +#endif public class ForestTrustCollisionException : ActiveDirectoryOperationException, ISerializable { public ForestTrustCollisionException(string message, Exception inner, ForestTrustRelationshipCollisionCollection collisions) : base(message, inner) @@ -218,7 +230,6 @@ namespace System.DirectoryServices.ActiveDirectory protected ForestTrustCollisionException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public ForestTrustRelationshipCollisionCollection Collisions { get; } = new ForestTrustRelationshipCollisionCollection(); diff --git a/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/NativeMethods.cs b/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/NativeMethods.cs index 3b55664c5d..32967006de 100644 --- a/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/NativeMethods.cs +++ b/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/NativeMethods.cs @@ -291,7 +291,6 @@ namespace System.DirectoryServices.ActiveDirectory public string callerName; } - [SuppressUnmanagedCodeSecurityAttribute()] internal sealed class NativeMethods { // disable public constructor @@ -385,7 +384,6 @@ namespace System.DirectoryServices.ActiveDirectory RPC_AUTH_IDENTITY_HANDLE* pAuthIdentity );*/ - [SuppressUnmanagedCodeSecurityAttribute()] internal delegate int DsMakePasswordCredentials( [MarshalAs(UnmanagedType.LPWStr)] string user, [MarshalAs(UnmanagedType.LPWStr)] string domain, @@ -395,7 +393,6 @@ namespace System.DirectoryServices.ActiveDirectory /*VOID DsFreePasswordCredentials( RPC_AUTH_IDENTITY_HANDLE AuthIdentity );*/ - [SuppressUnmanagedCodeSecurityAttribute()] internal delegate void DsFreePasswordCredentials( [In] IntPtr authIdentity); @@ -405,7 +402,6 @@ namespace System.DirectoryServices.ActiveDirectory RPC_AUTH_IDENTITY_HANDLE AuthIdentity, HANDLE* phDS );*/ - [SuppressUnmanagedCodeSecurityAttribute()] internal delegate int DsBindWithCred( [MarshalAs(UnmanagedType.LPWStr)] string domainController, [MarshalAs(UnmanagedType.LPWStr)] string dnsDomainName, @@ -415,7 +411,6 @@ namespace System.DirectoryServices.ActiveDirectory /*DWORD DsUnBind( HANDLE* phDS );*/ - [SuppressUnmanagedCodeSecurityAttribute()] internal delegate int DsUnBind( [In] ref IntPtr handle); @@ -426,7 +421,6 @@ namespace System.DirectoryServices.ActiveDirectory DWORD* pcOut, VOID** ppInfo );*/ - [SuppressUnmanagedCodeSecurityAttribute()] internal delegate int DsGetDomainControllerInfo( [In] IntPtr handle, [MarshalAs(UnmanagedType.LPWStr)] string domainName, @@ -442,7 +436,6 @@ namespace System.DirectoryServices.ActiveDirectory DWORD cInfo, VOID* pInfo );*/ - [SuppressUnmanagedCodeSecurityAttribute()] internal delegate void DsFreeDomainControllerInfo( [In] int infoLevel, [In] int dcInfoListCount, @@ -454,7 +447,6 @@ namespace System.DirectoryServices.ActiveDirectory HANDLE hDs, PDS_NAME_RESULT* ppSites );*/ - [SuppressUnmanagedCodeSecurityAttribute()] internal delegate int DsListSites( [In] IntPtr dsHandle, [Out] out IntPtr sites); @@ -463,7 +455,6 @@ namespace System.DirectoryServices.ActiveDirectory HANDLE hDs, PDS_NAME_RESULTW* ppRoles );*/ - [SuppressUnmanagedCodeSecurityAttribute()] internal delegate int DsListRoles( [In] IntPtr dsHandle, [Out] out IntPtr roles); @@ -517,7 +508,6 @@ namespace System.DirectoryServices.ActiveDirectory LPTSTR* rpNames, PDS_NAME_RESULT* ppResult );*/ - [SuppressUnmanagedCodeSecurityAttribute()] internal delegate int DsCrackNames( [In] IntPtr hDS, [In] int flags, @@ -589,9 +579,6 @@ namespace System.DirectoryServices.ActiveDirectory internal static extern int LsaNtStatusToWinError(int ntStatus); } - [ - SuppressUnmanagedCodeSecurityAttribute() - ] internal sealed class NativeComInterfaces { /*typedef enum { @@ -640,7 +627,6 @@ namespace System.DirectoryServices.ActiveDirectory internal interface IAdsPathname { // HRESULT Set([in] BSTR bstrADsPath, [in] long lnSetType); - [SuppressUnmanagedCodeSecurityAttribute()] int Set([In, MarshalAs(UnmanagedType.BStr)] string bstrADsPath, [In, MarshalAs(UnmanagedType.U4)] int lnSetType); // HRESULT SetDisplayType([in] long lnDisplayType); @@ -648,7 +634,6 @@ namespace System.DirectoryServices.ActiveDirectory // HRESULT Retrieve([in] long lnFormatType, [out, retval] BSTR* pbstrADsPath); [return: MarshalAs(UnmanagedType.BStr)] - [SuppressUnmanagedCodeSecurityAttribute()] string Retrieve([In, MarshalAs(UnmanagedType.U4)] int lnFormatType); // HRESULT GetNumElements([out, retval] long* plnNumPathElements); @@ -671,13 +656,11 @@ namespace System.DirectoryServices.ActiveDirectory // HRESULT GetEscapedElement([in] long lnReserved, [in] BSTR bstrInStr, [out, retval] BSTR* pbstrOutStr ); [return: MarshalAs(UnmanagedType.BStr)] - [SuppressUnmanagedCodeSecurityAttribute()] string GetEscapedElement([In, MarshalAs(UnmanagedType.U4)] int lnReserved, [In, MarshalAs(UnmanagedType.BStr)] string bstrInStr); int EscapedMode { get; - [SuppressUnmanagedCodeSecurityAttribute()] set; } } @@ -750,7 +733,6 @@ namespace System.DirectoryServices.ActiveDirectory string OID { [return: MarshalAs(UnmanagedType.BStr)] - [SuppressUnmanagedCodeSecurityAttribute()] get; [param: MarshalAs(UnmanagedType.BStr)] set; @@ -767,7 +749,6 @@ namespace System.DirectoryServices.ActiveDirectory int MaxRange { [return: MarshalAs(UnmanagedType.U4)] - [SuppressUnmanagedCodeSecurityAttribute()] get; [param: MarshalAs(UnmanagedType.U4)] set; @@ -776,7 +757,6 @@ namespace System.DirectoryServices.ActiveDirectory int MinRange { [return: MarshalAs(UnmanagedType.U4)] - [SuppressUnmanagedCodeSecurityAttribute()] get; [param: MarshalAs(UnmanagedType.U4)] set; @@ -784,7 +764,6 @@ namespace System.DirectoryServices.ActiveDirectory bool MultiValued { - [SuppressUnmanagedCodeSecurityAttribute()] get; set; } @@ -873,7 +852,6 @@ namespace System.DirectoryServices.ActiveDirectory string OID { [return: MarshalAs(UnmanagedType.BStr)] - [SuppressUnmanagedCodeSecurityAttribute()] get; [param: MarshalAs(UnmanagedType.BStr)] set; @@ -885,14 +863,12 @@ namespace System.DirectoryServices.ActiveDirectory object MandatoryProperties { - [SuppressUnmanagedCodeSecurityAttribute()] get; set; } object OptionalProperties { - [SuppressUnmanagedCodeSecurityAttribute()] get; set; } @@ -907,14 +883,12 @@ namespace System.DirectoryServices.ActiveDirectory object AuxDerivedFrom { - [SuppressUnmanagedCodeSecurityAttribute()] get; set; } object PossibleSuperiors { - [SuppressUnmanagedCodeSecurityAttribute()] get; set; } diff --git a/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/SafeHandle.cs b/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/SafeHandle.cs index 7358376170..00fb76b361 100644 --- a/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/SafeHandle.cs +++ b/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/SafeHandle.cs @@ -7,7 +7,6 @@ using Microsoft.Win32.SafeHandles; namespace System.DirectoryServices.ActiveDirectory { - [SuppressUnmanagedCodeSecurityAttribute()] internal sealed class PolicySafeHandle : SafeHandleZeroOrMinusOneIsInvalid { internal PolicySafeHandle(IntPtr value) : base(true) @@ -18,7 +17,6 @@ namespace System.DirectoryServices.ActiveDirectory override protected bool ReleaseHandle() => UnsafeNativeMethods.LsaClose(handle) == 0; } - [SuppressUnmanagedCodeSecurityAttribute()] internal sealed class LsaLogonProcessSafeHandle : SafeHandleZeroOrMinusOneIsInvalid { private LsaLogonProcessSafeHandle() : base(true) { } @@ -31,7 +29,6 @@ namespace System.DirectoryServices.ActiveDirectory override protected bool ReleaseHandle() => NativeMethods.LsaDeregisterLogonProcess(handle) == 0; } - [SuppressUnmanagedCodeSecurityAttribute()] internal sealed class LoadLibrarySafeHandle : SafeHandleZeroOrMinusOneIsInvalid { private LoadLibrarySafeHandle() : base(true) { } diff --git a/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/UnsafeNativeMethods.cs b/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/UnsafeNativeMethods.cs index cffff43fa9..5a4bed1a98 100644 --- a/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/UnsafeNativeMethods.cs +++ b/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/UnsafeNativeMethods.cs @@ -627,9 +627,6 @@ namespace System.DirectoryServices.ActiveDirectory public IntPtr domainSid = IntPtr.Zero; } - [ - SuppressUnmanagedCodeSecurityAttribute() - ] internal class UnsafeNativeMethods { public const int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100, @@ -644,22 +641,16 @@ namespace System.DirectoryServices.ActiveDirectory public static extern int FormatMessageW(int dwFlags, int lpSource, int dwMessageId, int dwLanguageId, StringBuilder lpBuffer, int nSize, int arguments); - [SuppressUnmanagedCodeSecurityAttribute()] public delegate int DsReplicaConsistencyCheck([In]IntPtr handle, int taskID, int flags); - [SuppressUnmanagedCodeSecurityAttribute()] public delegate int DsReplicaGetInfo2W(IntPtr handle, int type, [MarshalAs(UnmanagedType.LPWStr)] string objectPath, IntPtr sourceGUID, string attributeName, string value, int flag, int context, ref IntPtr info); - [SuppressUnmanagedCodeSecurityAttribute()] public delegate int DsReplicaGetInfoW(IntPtr handle, int type, [MarshalAs(UnmanagedType.LPWStr)] string objectPath, IntPtr sourceGUID, ref IntPtr info); - [SuppressUnmanagedCodeSecurityAttribute()] public delegate int DsReplicaFreeInfo(int type, IntPtr value); - [SuppressUnmanagedCodeSecurityAttribute()] public delegate int DsReplicaSyncW(IntPtr handle, [MarshalAs(UnmanagedType.LPWStr)] string partition, IntPtr uuid, int option); - [SuppressUnmanagedCodeSecurityAttribute()] public delegate int DsReplicaSyncAllW(IntPtr handle, [MarshalAs(UnmanagedType.LPWStr)] string partition, int flags, SyncReplicaFromAllServersCallback callback, IntPtr data, ref IntPtr error); [DllImport("kernel32.dll", EntryPoint = "LocalFree")] @@ -674,10 +665,8 @@ namespace System.DirectoryServices.ActiveDirectory [DllImport("netapi32.dll", EntryPoint = "DsGetSiteNameW", CharSet = CharSet.Unicode)] public static extern int DsGetSiteName(string dcName, ref IntPtr ptr); - [SuppressUnmanagedCodeSecurityAttribute()] public delegate int DsListDomainsInSiteW(IntPtr handle, [MarshalAs(UnmanagedType.LPWStr)] string site, ref IntPtr info); - [SuppressUnmanagedCodeSecurityAttribute()] public delegate void DsFreeNameResultW(IntPtr result); [DllImport("Netapi32.dll", EntryPoint = "DsEnumerateDomainTrustsW", CharSet = CharSet.Unicode)] diff --git a/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/Utils.cs.REMOVED.git-id b/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/Utils.cs.REMOVED.git-id index d56f861d1d..b951a8dd4d 100644 --- a/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/Utils.cs.REMOVED.git-id +++ b/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/Utils.cs.REMOVED.git-id @@ -1 +1 @@ -ae29ccfeed9ec00c81f609fdf9c8082cf736dc26 \ No newline at end of file +f3d1ebe3dea9c9e7574d86e5780c9e599b1e40b4 \ No newline at end of file diff --git a/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/DirectoryEntry.cs b/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/DirectoryEntry.cs index 6acd25b35c..83b6e0331e 100644 --- a/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/DirectoryEntry.cs +++ b/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/DirectoryEntry.cs @@ -19,9 +19,7 @@ namespace System.DirectoryServices /// Encapsulates a node or an object in the Active Directory hierarchy. /// [ - TypeConverterAttribute(typeof(DirectoryEntryConverter)), - EnvironmentPermission(SecurityAction.Assert, Unrestricted = true), - SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode), + TypeConverterAttribute(typeof(DirectoryEntryConverter)) ] public class DirectoryEntry : Component { diff --git a/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/DirectoryServicesCOMException.cs b/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/DirectoryServicesCOMException.cs index fcfa36bdd0..9623a0ae99 100644 --- a/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/DirectoryServicesCOMException.cs +++ b/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/DirectoryServicesCOMException.cs @@ -9,6 +9,8 @@ using System.DirectoryServices.Interop; namespace System.DirectoryServices { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("System.DirectoryServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] public class DirectoryServicesCOMException : COMException, ISerializable { public DirectoryServicesCOMException() { } @@ -19,7 +21,6 @@ namespace System.DirectoryServices protected DirectoryServicesCOMException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } internal DirectoryServicesCOMException(string extendedMessage, int extendedError, COMException e) : base(e.Message, e.ErrorCode) diff --git a/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/SearchResult.cs b/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/SearchResult.cs index c94454bfa0..15b8dae59a 100644 --- a/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/SearchResult.cs +++ b/external/corefx/src/System.DirectoryServices/src/System/DirectoryServices/SearchResult.cs @@ -26,8 +26,6 @@ namespace System.DirectoryServices /// Retrieves the that corresponds to the , from the Active Directory /// hierarchy. /// - [EnvironmentPermission(SecurityAction.Assert, Unrestricted = true)] - [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] public DirectoryEntry GetDirectoryEntry() { if (_parentCredentials != null) diff --git a/external/corefx/src/System.DirectoryServices/tests/Configurations.props b/external/corefx/src/System.DirectoryServices/tests/Configurations.props index ed4061c7d6..d8cd9ec843 100644 --- a/external/corefx/src/System.DirectoryServices/tests/Configurations.props +++ b/external/corefx/src/System.DirectoryServices/tests/Configurations.props @@ -2,7 +2,7 @@ - netstandard-Windows_NT; + netcoreapp-Windows_NT; diff --git a/external/corefx/src/System.DirectoryServices/tests/System.DirectoryServices.Tests.csproj b/external/corefx/src/System.DirectoryServices/tests/System.DirectoryServices.Tests.csproj index f20484596a..3d0cd83653 100644 --- a/external/corefx/src/System.DirectoryServices/tests/System.DirectoryServices.Tests.csproj +++ b/external/corefx/src/System.DirectoryServices/tests/System.DirectoryServices.Tests.csproj @@ -3,9 +3,10 @@ {DDE3838B-0EEA-4D9A-A120-9D72CB33F250} + $(NoWarn);CS0618 - - + + @@ -19,8 +20,9 @@ + - + Common\DirectoryServices\LdapConfiguration.cs diff --git a/external/corefx/src/System.DirectoryServices/tests/System/DirectoryServices/ActiveDirectory/ActiveDirectoryInterSiteTransportTests.cs b/external/corefx/src/System.DirectoryServices/tests/System/DirectoryServices/ActiveDirectory/ActiveDirectoryInterSiteTransportTests.cs index 33c516c056..0cc72e3bd9 100644 --- a/external/corefx/src/System.DirectoryServices/tests/System/DirectoryServices/ActiveDirectory/ActiveDirectoryInterSiteTransportTests.cs +++ b/external/corefx/src/System.DirectoryServices/tests/System/DirectoryServices/ActiveDirectory/ActiveDirectoryInterSiteTransportTests.cs @@ -22,7 +22,10 @@ namespace System.DirectoryServices.ActiveDirectory.Tests public void FindByTransportType_ForestNoDomainAssociatedWithoutName_ThrowsActiveDirectoryOperationException() { var context = new DirectoryContext(DirectoryContextType.Forest); - Assert.Throws(() => ActiveDirectoryInterSiteTransport.FindByTransportType(context, ActiveDirectoryTransportType.Rpc)); + if (!PlatformDetection.IsDomainJoinedMachine) + { + Assert.Throws(() => ActiveDirectoryInterSiteTransport.FindByTransportType(context, ActiveDirectoryTransportType.Rpc)); + } } [Fact] @@ -37,7 +40,7 @@ namespace System.DirectoryServices.ActiveDirectory.Tests public void FindByTransportType_ForestNoDomainAssociatedWithName_ThrowsActiveDirectoryOperationException_NoUap() { // Domain joined machines will not throw on the ActiveDirectoryInterSiteTransport.FindByTransportType call. - if (Environment.MachineName.Equals(Environment.UserDomainName, StringComparison.OrdinalIgnoreCase)) + if (!PlatformDetection.IsDomainJoinedMachine) { var context = new DirectoryContext(DirectoryContextType.Forest, "\0"); AssertExtensions.Throws("context", () => ActiveDirectoryInterSiteTransport.FindByTransportType(context, ActiveDirectoryTransportType.Rpc)); @@ -57,10 +60,14 @@ namespace System.DirectoryServices.ActiveDirectory.Tests [InlineData(DirectoryContextType.DirectoryServer)] [InlineData(DirectoryContextType.Domain)] [SkipOnTargetFramework(TargetFrameworkMonikers.Uap, "Access to common path is denied inside App")] - public void FindByTransportType_InvalidContextTypeWithName_ThrowsArgumentException(DirectoryContextType type) + public void FindByTransportType_InvalidContextTypeWithName(DirectoryContextType type) { var context = new DirectoryContext(type, "Name"); - AssertExtensions.Throws("context", () => ActiveDirectoryInterSiteTransport.FindByTransportType(context, ActiveDirectoryTransportType.Rpc)); + Exception exception = Record.Exception(() => ActiveDirectoryInterSiteTransport.FindByTransportType(context, ActiveDirectoryTransportType.Rpc)); + Assert.NotNull(exception); + Assert.True(exception is ArgumentException || + exception is ActiveDirectoryOperationException, + $"We got unrecognized exception {exception}"); } [Fact] diff --git a/external/corefx/src/System.DirectoryServices/tests/System/DirectoryServices/ActiveDirectory/ActiveDirectoryTests.cs b/external/corefx/src/System.DirectoryServices/tests/System/DirectoryServices/ActiveDirectory/ActiveDirectoryTests.cs new file mode 100644 index 0000000000..5e23482855 --- /dev/null +++ b/external/corefx/src/System.DirectoryServices/tests/System/DirectoryServices/ActiveDirectory/ActiveDirectoryTests.cs @@ -0,0 +1,418 @@ +// 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.Runtime.InteropServices; +using System.Collections.Generic; +using System.Collections; +using System.Linq; +using System.DirectoryServices.ActiveDirectory; +using System; +using Xunit; + +namespace System.DirectoryServices.Tests +{ + public partial class DirectoryServicesTests + { + [ConditionalFact(nameof(IsActiveDirectoryServer))] + public void TestSchema() + { + using (ActiveDirectorySchema schema = ActiveDirectorySchema.GetSchema(ActiveDirectoryContext)) + { + Assert.True(schema.FindAllClasses().Contains(ActiveDirectorySchemaClass.FindByName(ActiveDirectoryContext, "user"))); + Assert.True(schema.FindAllClasses().Contains(ActiveDirectorySchemaClass.FindByName(ActiveDirectoryContext, "samDomainBase"))); + Assert.NotNull(schema.FindAllDefunctClasses()); + Assert.NotNull(schema.FindAllDefunctProperties()); + Assert.True(schema.FindAllProperties(PropertyTypes.Indexed).Contains(ActiveDirectorySchemaProperty.FindByName(ActiveDirectoryContext, "ou"))); + Assert.True(schema.FindAllProperties().Contains(ActiveDirectorySchemaProperty.FindByName(ActiveDirectoryContext, "cn"))); + Assert.Equal("person", schema.FindClass("person").Name); + Assert.Equal("cn", schema.FindProperty("cn").Name); + + using (DirectoryEntry de = schema.GetDirectoryEntry()) + { + Assert.True("CN=Schema".Equals(de.Name, StringComparison.OrdinalIgnoreCase)); + } + } + } + + [ConditionalFact(nameof(IsActiveDirectoryServer))] + public void TestSchemaClass() + { + using (ActiveDirectorySchemaClass orgClass = ActiveDirectorySchemaClass.FindByName(ActiveDirectoryContext, "organization")) + { + Assert.Equal("organization", orgClass.Name); + Assert.Equal("Organization", orgClass.CommonName); + Assert.Equal("2.5.6.4", orgClass.Oid); + Assert.Equal("bf967aa3-0de6-11d0-a285-00aa003049e2", orgClass.SchemaGuid.ToString()); + Assert.Equal("top", orgClass.SubClassOf.Name); + Assert.NotNull(orgClass.DefaultObjectSecurityDescriptor); + string s = orgClass.Description; // it can be null + Assert.False(orgClass.IsDefunct); + + Assert.True(orgClass.AuxiliaryClasses.Contains(ActiveDirectorySchemaClass.FindByName(ActiveDirectoryContext, "samDomainBase"))); + Assert.True(orgClass.PossibleInferiors.Contains(ActiveDirectorySchemaClass.FindByName(ActiveDirectoryContext, "user"))); + + ActiveDirectorySchemaClass country = ActiveDirectorySchemaClass.FindByName(ActiveDirectoryContext, "country"); + Assert.True(orgClass.PossibleSuperiors.Contains(country)); + int index = orgClass.PossibleSuperiors.IndexOf(country); + Assert.Equal(country.Name, orgClass.PossibleSuperiors[index].Name); + + Assert.True(orgClass.MandatoryProperties.Contains(ActiveDirectorySchemaProperty.FindByName(ActiveDirectoryContext, "ntSecurityDescriptor"))); + Assert.True(orgClass.OptionalProperties.Contains(ActiveDirectorySchemaProperty.FindByName(ActiveDirectoryContext, "description"))); + Assert.True(orgClass.MandatoryProperties.Contains(ActiveDirectorySchemaProperty.FindByName(ActiveDirectoryContext, "objectClass"))); + + using (DirectoryEntry de = orgClass.GetDirectoryEntry()) + { + Assert.True("CN=Organization".Equals(de.Name, StringComparison.OrdinalIgnoreCase)); + } + } + } + + [ConditionalFact(nameof(IsActiveDirectoryServer))] + public void TestSchemaProperty() + { + using (ActiveDirectorySchemaProperty adsp = ActiveDirectorySchemaProperty.FindByName(ActiveDirectoryContext, "objectClass")) + { + Assert.Equal("Object-Class", adsp.CommonName); + Assert.False(adsp.IsDefunct); + Assert.False(adsp.IsInAnr); + Assert.True(adsp.IsIndexed); + Assert.False(adsp.IsIndexedOverContainer); + Assert.True(adsp.IsInGlobalCatalog); + Assert.True(adsp.IsOnTombstonedObject); + Assert.False(adsp.IsSingleValued); + Assert.False(adsp.IsTupleIndexed); + Assert.Equal("2.5.4.0", adsp.Oid); + + using (DirectoryEntry de = adsp.GetDirectoryEntry()) + { + Assert.True("CN=Object-Class".Equals(de.Name, StringComparison.OrdinalIgnoreCase)); + } + + Assert.Equal(ActiveDirectorySyntax.Oid, adsp.Syntax); + } + } + + [ConditionalFact(nameof(IsActiveDirectoryServer))] + public void TestSchemaFilter() + { + // using (ActiveDirectorySchemaClass schema = ActiveDirectorySchemaClass.FindByName(ActiveDirectoryContext, "user")) + using (ActiveDirectorySchema schema = ActiveDirectorySchema.GetSchema(ActiveDirectoryContext)) + using (DirectoryEntry de = schema.GetDirectoryEntry()) + { + // by default there is no filters + Assert.Equal(0, de.Children.SchemaFilter.Count); + + int topClassCount = 0; + + foreach (DirectoryEntry child in de.Children) + { + string s = (string) child.Properties["objectClass"][0]; + topClassCount += s.Equals("top", StringComparison.OrdinalIgnoreCase) ? 1 : 0; + } + + de.Children.SchemaFilter.Add("top"); + Assert.Equal(1, de.Children.SchemaFilter.Count); + Assert.True(de.Children.SchemaFilter.Contains("top")); + Assert.Equal(0, de.Children.SchemaFilter.IndexOf("top")); + Assert.Equal("top", de.Children.SchemaFilter[0]); + + int newTopClassCount = 0; + + foreach (DirectoryEntry child in de.Children) + { + // we expect to get top only entries + string s = (string) child.Properties["objectClass"][0]; + Assert.True(s.Equals("top", StringComparison.OrdinalIgnoreCase)); + newTopClassCount += 1; + } + + Assert.Equal(topClassCount, newTopClassCount); + + de.Children.SchemaFilter.Remove("top"); + Assert.Equal(0, de.Children.SchemaFilter.Count); + + de.Children.SchemaFilter.Add("top"); + Assert.Equal(1, de.Children.SchemaFilter.Count); + de.Children.SchemaFilter.RemoveAt(0); + Assert.Equal(0, de.Children.SchemaFilter.Count); + + de.Children.SchemaFilter.AddRange(new string [] {"top", "user"}); + Assert.Equal(2, de.Children.SchemaFilter.Count); + de.Children.SchemaFilter.Insert(0, "person"); + Assert.Equal(3, de.Children.SchemaFilter.Count); + Assert.Equal("person", de.Children.SchemaFilter[0]); + Assert.Equal("user", de.Children.SchemaFilter[2]); + + de.Children.SchemaFilter.Clear(); + Assert.Equal(0, de.Children.SchemaFilter.Count); + } + } + + [ConditionalFact(nameof(IsActiveDirectoryServer))] + public void TestForestRootDomain() + { + using (Forest forest = Forest.GetForest(ActiveDirectoryContext)) + { + using (Domain rootDomain = forest.RootDomain) + { + Assert.Equal(forest.Name, rootDomain.Forest.Name); + Assert.Null(rootDomain.Parent); + + forest.Domains.Contains(rootDomain); + int index = forest.Domains.IndexOf(rootDomain); + Assert.Equal(rootDomain.Name, forest.Domains[index].Name); + + Domain [] domains = new Domain[0]; + + Assert.Throws(() => forest.Domains.CopyTo(domains, 0)); + Assert.Throws(() => forest.Domains.CopyTo(null, 0)); + + domains = new Domain[forest.Domains.Count]; + Assert.Throws(() => forest.Domains.CopyTo(domains, -1)); + forest.Domains.CopyTo(domains, 0); + + Assert.NotNull(domains.FirstOrDefault(d => d.Name.Equals(rootDomain.Name))); + } + } + } + + [ConditionalFact(nameof(IsActiveDirectoryServer))] + public void TestForestSites() + { + using (Forest forest = Forest.GetForest(ActiveDirectoryContext)) + { + Assert.True(forest.Sites.Count > 0); + using (ActiveDirectorySite site = forest.Sites[0]) + { + Assert.True(forest.Sites.Contains(site)); + Assert.Equal(0, forest.Sites.IndexOf(site)); + } + } + } + + [ConditionalFact(nameof(IsActiveDirectoryServer))] + public void TestForestRoleOwnersAndModes() + { + using (Forest forest = Forest.GetForest(ActiveDirectoryContext)) + { + Assert.Equal(forest.Name, forest.NamingRoleOwner.Forest.Name); + Assert.Equal(forest.Name, forest.SchemaRoleOwner.Forest.Name); + + Assert.True( + forest.ForestMode == ForestMode.Unknown || + forest.ForestMode == ForestMode.Windows2000Forest || + forest.ForestMode == ForestMode.Windows2003Forest || + forest.ForestMode == ForestMode.Windows2003InterimForest || + forest.ForestMode == ForestMode.Windows2008Forest || + forest.ForestMode == ForestMode.Windows2008R2Forest || + forest.ForestMode == ForestMode.Windows2012R2Forest || + forest.ForestMode == ForestMode.Windows8Forest); + + Assert.True(forest.ForestModeLevel >= 0); + Assert.Equal(forest.Name, forest.NamingRoleOwner.Forest.Name); + } + } + + [ConditionalFact(nameof(IsActiveDirectoryServer))] + public void TestForestSchema() + { + using (Forest forest = Forest.GetForest(ActiveDirectoryContext)) + { + using (ActiveDirectorySchema schema = forest.Schema) + using (ActiveDirectorySchemaClass adsc = schema.FindClass("top")) + { + Assert.True("top".Equals(adsc.CommonName, StringComparison.OrdinalIgnoreCase)); + } + } + } + + [ConditionalFact(nameof(IsActiveDirectoryServer))] + public void TestForestGlobalCatalog() + { + using (Forest forest = Forest.GetForest(ActiveDirectoryContext)) + { + int count = 0; + GlobalCatalogCollection gcCollection = forest.FindAllGlobalCatalogs(); + foreach (GlobalCatalog gc in gcCollection) + { + count++; + } + + Assert.True(count > 0); + Assert.True(gcCollection.Contains(gcCollection[0])); + Assert.Equal(0, gcCollection.IndexOf(gcCollection[0])); + + gcCollection = forest.FindAllGlobalCatalogs(forest.Sites[0].Name); + count = 0; + foreach (GlobalCatalog gc in gcCollection) + { + count++; + } + + Assert.True(count > 0); + Assert.True(gcCollection.Contains(gcCollection[0])); + Assert.Equal(0, gcCollection.IndexOf(gcCollection[0])); + + GlobalCatalog globalCatalog = forest.FindGlobalCatalog(forest.Sites[0].Name); + + DirectoryContext forestContext = new DirectoryContext( + DirectoryContextType.Forest, + forest.Name, + LdapConfiguration.Configuration.UserName, + LdapConfiguration.Configuration.Password); + + Assert.Equal(globalCatalog.Name, GlobalCatalog.FindOne(forestContext, forest.Sites[0].Name).Name); + } + } + + [ConditionalFact(nameof(IsActiveDirectoryServer))] + public void TestDomain() + { + using (Domain domain = Domain.GetDomain(ActiveDirectoryContext)) + { + Assert.Equal(domain.Forest.Name, Forest.GetForest(ActiveDirectoryContext).Name); + Assert.NotNull(domain.Children); + + DomainControllerCollection domainControllers = domain.DomainControllers; + Assert.True(domainControllers.Contains(domain.PdcRoleOwner)); + Assert.True(domainControllers.IndexOf(domain.RidRoleOwner) >= 0); + Assert.True(domainControllers.Contains(domain.InfrastructureRoleOwner)); + + Assert.True(domain.DomainModeLevel >= 0); + + Assert.True( + domain.DomainMode == DomainMode.Unknown || + domain.DomainMode == DomainMode.Windows2000MixedDomain || + domain.DomainMode == DomainMode.Windows2000NativeDomain || + domain.DomainMode == DomainMode.Windows2003Domain || + domain.DomainMode == DomainMode.Windows2003InterimDomain || + domain.DomainMode == DomainMode.Windows2008Domain || + domain.DomainMode == DomainMode.Windows2008R2Domain || + domain.DomainMode == DomainMode.Windows2012R2Domain || + domain.DomainMode == DomainMode.Windows8Domain); + + if (domain.Forest.RootDomain.Name.Equals(domain.Name)) + { + Assert.Null(domain.Parent); + } + + Assert.Throws(() => domain.GetSidFilteringStatus(null)); + Assert.Throws(() => domain.GetSidFilteringStatus("")); + } + } + + [ConditionalFact(nameof(IsActiveDirectoryServer))] + public void TestDomainController() + { + using (Domain domain = Domain.GetDomain(ActiveDirectoryContext)) + { + DirectoryContext dc = new DirectoryContext( + DirectoryContextType.Domain, + domain.Name, + LdapConfiguration.Configuration.UserName, + LdapConfiguration.Configuration.Password); + + using (DomainController controller = DomainController.FindOne(dc)) + { + using (Forest forest = Forest.GetForest(ActiveDirectoryContext)) + { + Assert.Equal(forest.Name, controller.Forest.Name); + } + + DomainControllerCollection dcc = DomainController.FindAll(dc); + Assert.True(dcc.Contains(controller)); + Assert.True(dcc.IndexOf(controller) >= 0); + + Assert.Equal(domain.Name, controller.Domain.Name); + + Assert.True(controller.CurrentTime > DateTime.Today.AddDays(-2)); + + Assert.True(controller.HighestCommittedUsn > 0); + + Assert.NotNull(controller.InboundConnections); + Assert.NotNull(controller.OutboundConnections); + + foreach (ActiveDirectoryRole adr in controller.Roles) + { + Assert.True( + adr == ActiveDirectoryRole.InfrastructureRole || + adr == ActiveDirectoryRole.NamingRole || + adr == ActiveDirectoryRole.PdcRole || + adr == ActiveDirectoryRole.RidRole || + adr == ActiveDirectoryRole.SchemaRole); + + Assert.True(controller.Roles.Contains(adr)); + Assert.True(controller.Roles.IndexOf(adr) >= 0); + } + + Assert.NotNull(controller.SiteName); + + Assert.True(controller.OSVersion.IndexOf("Windows", StringComparison.OrdinalIgnoreCase) >= 0); + Assert.True(controller.IPAddress.IndexOf('.') >= 0); + } + } + } + + [ConditionalFact(nameof(IsActiveDirectoryServer))] + public void TestSites() + { + using (Forest forest = Forest.GetForest(ActiveDirectoryContext)) + { + using (ActiveDirectorySite site = forest.Sites[0]) + using (ActiveDirectorySite s = ActiveDirectorySite.FindByName(ActiveDirectoryContext, site.Name)) + { + Assert.Equal(site.Name, s.Name); + Assert.True(s.Domains.Contains(forest.RootDomain)); + Assert.NotNull(s.AdjacentSites); + Assert.NotNull(s.BridgeheadServers); + Assert.NotNull(s.PreferredRpcBridgeheadServers); + Assert.NotNull(s.PreferredSmtpBridgeheadServers); + Assert.NotNull(s.Subnets); + + Assert.True(s.SiteLinks.Count > 0); + using (ActiveDirectorySiteLink adsl = s.SiteLinks[0]) + { + Assert.True(s.SiteLinks.Contains(adsl)); + Assert.Equal(0, s.SiteLinks.IndexOf(adsl)); + Assert.True(adsl.Sites.Contains(s)); + Assert.True(adsl.Cost >= 0); + Assert.True(adsl.TransportType == ActiveDirectoryTransportType.Rpc || adsl.TransportType == ActiveDirectoryTransportType.Smtp); + } + + Assert.True(s.Servers.Contains(s.InterSiteTopologyGenerator)); + + using (DirectoryServer ds = s.Servers[0]) + { + Assert.NotNull(ds.InboundConnections); + Assert.NotNull(ds.OutboundConnections); + Assert.True(ds.IPAddress.IndexOf('.') >= 0); + Assert.Equal(s.Name, ds.SiteName); + + Assert.True(ds.Partitions.Count > 0); + string firstPartition = ds.Partitions[0]; + Assert.True(ds.Partitions.Contains(firstPartition)); + Assert.Equal(0, ds.Partitions.IndexOf(firstPartition)); + + string [] partitions = new string[0]; + Assert.Throws(() => ds.Partitions.CopyTo(partitions, 0)); + Assert.Throws(() => ds.Partitions.CopyTo(null, 0)); + Assert.Throws(() => ds.Partitions.CopyTo(partitions, -1)); + + partitions = new string[ds.Partitions.Count]; + ds.Partitions.CopyTo(partitions, 0); + Assert.True(partitions.Contains(firstPartition)); + } + } + } + } + + private static DirectoryContext ActiveDirectoryContext => new DirectoryContext( + DirectoryContextType.DirectoryServer, + LdapConfiguration.Configuration.ServerName + + (String.IsNullOrEmpty(LdapConfiguration.Configuration.Port) ? "" : ":" + LdapConfiguration.Configuration.Port), + LdapConfiguration.Configuration.UserName, + LdapConfiguration.Configuration.Password); + + } +} diff --git a/external/corefx/src/System.DirectoryServices/tests/System/DirectoryServices/ActiveDirectory/DomainControllerTests.cs b/external/corefx/src/System.DirectoryServices/tests/System/DirectoryServices/ActiveDirectory/DomainControllerTests.cs index d95fd471d3..0a4e6fbc00 100644 --- a/external/corefx/src/System.DirectoryServices/tests/System/DirectoryServices/ActiveDirectory/DomainControllerTests.cs +++ b/external/corefx/src/System.DirectoryServices/tests/System/DirectoryServices/ActiveDirectory/DomainControllerTests.cs @@ -29,21 +29,28 @@ namespace System.DirectoryServices.ActiveDirectory.Tests [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] [OuterLoop("Takes too long on domain joined machines")] [InlineData("\0")] - [InlineData("server:port")] [InlineData("[")] [SkipOnTargetFramework(TargetFrameworkMonikers.Uap, "Access to path is denied when in App container")] - public void GetDomainController_InvalidName_ThrowsActiveDirectoryObjectNotFoundException(string name) + public void GetDomainController_InvalidName(string name) { var context = new DirectoryContext(DirectoryContextType.DirectoryServer, name); - Assert.Throws(() => DomainController.GetDomainController(context)); + Exception exception = Record.Exception(() => DomainController.GetDomainController(context)); + Assert.NotNull(exception); + Assert.True(exception is ActiveDirectoryObjectNotFoundException || + exception is ActiveDirectoryOperationException, + $"We got unrecognized exception {exception}"); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] [SkipOnTargetFramework(TargetFrameworkMonikers.Uap, "Access to path is denied when in App container")] - public void GetDomainController_InvalidIPV6_ThrowsActiveDirectoryObjectNotFoundException() + public void GetDomainController_InvalidIPV6() { var context = new DirectoryContext(DirectoryContextType.DirectoryServer, "[::1]:port"); - Assert.Throws(() => DomainController.GetDomainController(context)); + Exception exception = Record.Exception(() => DomainController.GetDomainController(context)); + Assert.NotNull(exception); + Assert.True(exception is ActiveDirectoryObjectNotFoundException || + exception is ActiveDirectoryOperationException, + $"We got unrecognized exception {exception}"); } [Fact] @@ -110,11 +117,11 @@ namespace System.DirectoryServices.ActiveDirectory.Tests public void FindAll_NoSuchName_ReturnsEmpty() { // Domain joined machines can have entries in the DomainController. - if (Environment.MachineName.Equals(Environment.UserDomainName, StringComparison.OrdinalIgnoreCase)) + if (PlatformDetection.IsDomainJoinedMachine) { var context = new DirectoryContext(DirectoryContextType.Domain, "\0"); - Assert.Empty(DomainController.FindAll(context)); - Assert.Empty(DomainController.FindAll(context, "siteName")); + Assert.NotNull(DomainController.FindAll(context)); + Assert.NotNull(DomainController.FindAll(context, "siteName")); } } @@ -125,7 +132,10 @@ namespace System.DirectoryServices.ActiveDirectory.Tests public void FindAll_NullName_ThrowsActiveDirectoryOperationException() { var context = new DirectoryContext(DirectoryContextType.Domain); - Assert.Throws(() => DomainController.FindAll(context)); + if (!PlatformDetection.IsDomainJoinedMachine) + { + Assert.Throws(() => DomainController.FindAll(context)); + } } [Fact] @@ -134,7 +144,7 @@ namespace System.DirectoryServices.ActiveDirectory.Tests AssertExtensions.Throws("context", () => DomainController.FindAll(null)); AssertExtensions.Throws("context", () => DomainController.FindAll(null, "siteName")); } - + [Theory] [InlineData(DirectoryContextType.ApplicationPartition)] [InlineData(DirectoryContextType.ConfigurationSet)] diff --git a/external/corefx/src/System.DirectoryServices/tests/System/DirectoryServices/ActiveDirectory/ForestTests.cs b/external/corefx/src/System.DirectoryServices/tests/System/DirectoryServices/ActiveDirectory/ForestTests.cs index 06abaaa22c..3b4cbacfae 100644 --- a/external/corefx/src/System.DirectoryServices/tests/System/DirectoryServices/ActiveDirectory/ForestTests.cs +++ b/external/corefx/src/System.DirectoryServices/tests/System/DirectoryServices/ActiveDirectory/ForestTests.cs @@ -30,7 +30,9 @@ namespace System.DirectoryServices.ActiveDirectory.Tests public void GetForest_NullNameAndNotRootedDomain_ThrowsActiveDirectoryOperationException() { var context = new DirectoryContext(DirectoryContextType.Forest); - Assert.Throws(() => Forest.GetForest(context)); + + if (!PlatformDetection.IsDomainJoinedMachine) + Assert.Throws(() => Forest.GetForest(context)); } [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] @@ -51,13 +53,24 @@ namespace System.DirectoryServices.ActiveDirectory.Tests [InlineData(DirectoryContextType.Forest, "\0")] [InlineData(DirectoryContextType.DirectoryServer, "server:port")] [SkipOnTargetFramework(TargetFrameworkMonikers.Uap, "Not approved COM object for app")] - public void GetForest_NonNullNameAndNotRootedDomain_ThrowsActiveDirectoryObjectNotFoundException_NonUap(DirectoryContextType type, string name) + public void GetForest_NonNullNameAndNotRootedDomain_NonUap(DirectoryContextType type, string name) { var context = new DirectoryContext(type, name); - Assert.Throws(() => Forest.GetForest(context)); + if (!PlatformDetection.IsDomainJoinedMachine) + { + Exception exception = Record.Exception(() => Forest.GetForest(context)); + Assert.NotNull(exception); + Assert.True(exception is ActiveDirectoryObjectNotFoundException || + exception is ActiveDirectoryOperationException, + $"We got unrecognized exception {exception}"); - // The result of validation is cached, so repeat this to make sure it's cached properly. - Assert.Throws(() => Forest.GetForest(context)); + // The result of validation is cached, so repeat this to make sure it's cached properly. + exception = Record.Exception(() => Forest.GetForest(context)); + Assert.NotNull(exception); + Assert.True(exception is ActiveDirectoryObjectNotFoundException || + exception is ActiveDirectoryOperationException, + $"We got unrecognized exception {exception}"); + } } } } diff --git a/external/corefx/src/System.DirectoryServices/tests/System/DirectoryServices/SortOptionTests.cs b/external/corefx/src/System.DirectoryServices/tests/System/DirectoryServices/SortOptionTests.cs index 49cc3ee573..67d44721ec 100644 --- a/external/corefx/src/System.DirectoryServices/tests/System/DirectoryServices/SortOptionTests.cs +++ b/external/corefx/src/System.DirectoryServices/tests/System/DirectoryServices/SortOptionTests.cs @@ -25,7 +25,11 @@ namespace System.DirectoryServices.Tests { var sortOption = new SortOption(propertyName, direction); Assert.Equal(propertyName, sortOption.PropertyName); - Assert.Equal(direction, sortOption.Direction); + + if (PlatformDetection.TargetsNetFx452OrLower) + Assert.Equal(SortDirection.Ascending, sortOption.Direction); + else + Assert.Equal(direction, sortOption.Direction); } [Fact] @@ -39,7 +43,14 @@ namespace System.DirectoryServices.Tests [InlineData(SortDirection.Descending + 1)] public void Ctor_InvalidDirection_ThrowsInvalidEnumArgumentException(SortDirection direction) { - AssertExtensions.Throws("value", () => new SortOption("propertyName", direction)); + if (PlatformDetection.TargetsNetFx452OrLower) + { + SortOption so = new SortOption("propertyName", direction); + Assert.Equal("propertyName", so.PropertyName); + Assert.Equal(SortDirection.Ascending, so.Direction); + } + else + AssertExtensions.Throws("value", () => new SortOption("propertyName", direction)); } } } diff --git a/external/corefx/src/System.Drawing.Common/System.Drawing.Common.sln b/external/corefx/src/System.Drawing.Common/System.Drawing.Common.sln index bdc9c7a707..fe67e93fc7 100644 --- a/external/corefx/src/System.Drawing.Common/System.Drawing.Common.sln +++ b/external/corefx/src/System.Drawing.Common/System.Drawing.Common.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26911.0 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Drawing.Common.Tests", "tests\System.Drawing.Common.Tests.csproj", "{4B93E684-0630-45F4-8F63-6C7788C9892F}" ProjectSection(ProjectDependencies) = postProject @@ -30,14 +30,14 @@ Global {4B93E684-0630-45F4-8F63-6C7788C9892F}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {4B93E684-0630-45F4-8F63-6C7788C9892F}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU {4B93E684-0630-45F4-8F63-6C7788C9892F}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU - {191B3618-FECD-4ABD-9D6B-5AC90DC33621}.Debug|Any CPU.ActiveCfg = netcoreapp2.0-Windows_NT-Debug|Any CPU - {191B3618-FECD-4ABD-9D6B-5AC90DC33621}.Debug|Any CPU.Build.0 = netcoreapp2.0-Windows_NT-Debug|Any CPU - {191B3618-FECD-4ABD-9D6B-5AC90DC33621}.Release|Any CPU.ActiveCfg = netcoreapp2.0-Unix-Release|Any CPU - {191B3618-FECD-4ABD-9D6B-5AC90DC33621}.Release|Any CPU.Build.0 = netcoreapp2.0-Unix-Release|Any CPU - {D7AEA698-275D-441F-B7A7-8491D1F0EFF0}.Debug|Any CPU.ActiveCfg = netcoreapp2.0-Debug|Any CPU - {D7AEA698-275D-441F-B7A7-8491D1F0EFF0}.Debug|Any CPU.Build.0 = netcoreapp2.0-Debug|Any CPU - {D7AEA698-275D-441F-B7A7-8491D1F0EFF0}.Release|Any CPU.ActiveCfg = netcoreapp2.0-Release|Any CPU - {D7AEA698-275D-441F-B7A7-8491D1F0EFF0}.Release|Any CPU.Build.0 = netcoreapp2.0-Release|Any CPU + {191B3618-FECD-4ABD-9D6B-5AC90DC33621}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {191B3618-FECD-4ABD-9D6B-5AC90DC33621}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {191B3618-FECD-4ABD-9D6B-5AC90DC33621}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {191B3618-FECD-4ABD-9D6B-5AC90DC33621}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU + {D7AEA698-275D-441F-B7A7-8491D1F0EFF0}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {D7AEA698-275D-441F-B7A7-8491D1F0EFF0}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {D7AEA698-275D-441F-B7A7-8491D1F0EFF0}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {D7AEA698-275D-441F-B7A7-8491D1F0EFF0}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -47,7 +47,4 @@ Global {191B3618-FECD-4ABD-9D6B-5AC90DC33621} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD} {D7AEA698-275D-441F-B7A7-8491D1F0EFF0} = {2E666815-2EDB-464B-9DF6-380BF4789AD4} EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {ACBCC0AB-54DC-4B76-9556-2569EF381DFB} - EndGlobalSection EndGlobal diff --git a/external/corefx/src/System.Drawing.Common/pkg/System.Drawing.Common.pkgproj b/external/corefx/src/System.Drawing.Common/pkg/System.Drawing.Common.pkgproj index 97ada23e30..1fe84ed429 100644 --- a/external/corefx/src/System.Drawing.Common/pkg/System.Drawing.Common.pkgproj +++ b/external/corefx/src/System.Drawing.Common/pkg/System.Drawing.Common.pkgproj @@ -3,9 +3,11 @@ - netcoreapp2.0 + net461;netcoreapp2.0;$(AllXamarinFrameworks) + + \ No newline at end of file diff --git a/external/corefx/src/System.Drawing.Common/ref/Configurations.props b/external/corefx/src/System.Drawing.Common/ref/Configurations.props index 01894f79b9..d9777d8275 100644 --- a/external/corefx/src/System.Drawing.Common/ref/Configurations.props +++ b/external/corefx/src/System.Drawing.Common/ref/Configurations.props @@ -2,7 +2,8 @@ - netcoreapp2.0; + netstandard; + netfx; \ No newline at end of file diff --git a/external/corefx/src/System.Drawing.Common/ref/System.Drawing.Common.cs.REMOVED.git-id b/external/corefx/src/System.Drawing.Common/ref/System.Drawing.Common.cs.REMOVED.git-id index 56728782a5..5592a07574 100644 --- a/external/corefx/src/System.Drawing.Common/ref/System.Drawing.Common.cs.REMOVED.git-id +++ b/external/corefx/src/System.Drawing.Common/ref/System.Drawing.Common.cs.REMOVED.git-id @@ -1 +1 @@ -2d6b0ae7dae22a18a349995df2ad777b9922f9ac \ No newline at end of file +715ca992e720f4592a2778f74e70789fe2c1a6e8 \ No newline at end of file diff --git a/external/corefx/src/System.Drawing.Common/ref/System.Drawing.Common.csproj b/external/corefx/src/System.Drawing.Common/ref/System.Drawing.Common.csproj index d8daf77d79..a87389d3d0 100644 --- a/external/corefx/src/System.Drawing.Common/ref/System.Drawing.Common.csproj +++ b/external/corefx/src/System.Drawing.Common/ref/System.Drawing.Common.csproj @@ -3,13 +3,16 @@ {D7AEA698-275D-441F-B7A7-8491D1F0EFF0} + true - - + + + + - + @@ -19,5 +22,10 @@ + + + + + - + \ No newline at end of file diff --git a/external/corefx/src/System.Drawing.Common/src/Configurations.props b/external/corefx/src/System.Drawing.Common/src/Configurations.props index 85b3fdbbf8..facde1b5bc 100644 --- a/external/corefx/src/System.Drawing.Common/src/Configurations.props +++ b/external/corefx/src/System.Drawing.Common/src/Configurations.props @@ -1,9 +1,16 @@  - + netcoreapp2.0-Windows_NT; netcoreapp2.0-Unix; + netfx; + netstandard; + + + $(PackageConfigurations); + netcoreapp-Windows_NT; + netcoreapp-Unix; - + \ No newline at end of file diff --git a/external/corefx/src/System.Drawing.Common/src/Resources/Strings.resx b/external/corefx/src/System.Drawing.Common/src/Resources/Strings.resx index 61918c3fe8..68ae3e09c0 100644 --- a/external/corefx/src/System.Drawing.Common/src/Resources/Strings.resx +++ b/external/corefx/src/System.Drawing.Common/src/Resources/Strings.resx @@ -348,6 +348,9 @@ Occurs before each page is printed. Useful for changing PageSettings for a particular page. + + System.Drawing is not supported on this platform. + Defines an object that sends output to a printer. diff --git a/external/corefx/src/System.Drawing.Common/src/System.Drawing.Common.csproj b/external/corefx/src/System.Drawing.Common/src/System.Drawing.Common.csproj index e96c9dddc0..0fddb02bd9 100644 --- a/external/corefx/src/System.Drawing.Common/src/System.Drawing.Common.csproj +++ b/external/corefx/src/System.Drawing.Common/src/System.Drawing.Common.csproj @@ -11,37 +11,24 @@ CS0618 $(DefineConstants);FEATURE_WINDOWS_SYSTEM_COLORS $(DefineConstants);CORECLR;NETCORE + true + SR.PlatformNotSupported_Drawing - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + - + @@ -49,6 +36,7 @@ + @@ -157,7 +145,7 @@ - + @@ -192,17 +180,27 @@ System\Drawing\ColorUtil.netcoreapp20.cs - - System\Drawing\KnownColor.cs - System\Drawing\KnownColorTable.cs Common\System\Runtime\InteropServices\FunctionWrapper.cs + + System.Drawing.DefaultComponent.bmp + + + + System.Drawing.ShieldIcon.ico + - + + + + System\Drawing\KnownColor.cs + + + @@ -291,7 +289,7 @@ Common\System\Runtime\InteropServices\FunctionWrapper.Windows.cs - + @@ -347,18 +345,36 @@ Common\System\Runtime\InteropServices\FunctionWrapper.Unix.cs - - - - System.Drawing.DefaultComponent.bmp - - - - System.Drawing.ShieldIcon.ico - - + placeholder.ico + + + + + + + + + + + + + + + + + + + + + + + + + + + - + \ No newline at end of file diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Bitmap.Unix.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Bitmap.Unix.cs index 5c6707c9ae..ab4b8742ff 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Bitmap.Unix.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Bitmap.Unix.cs @@ -46,87 +46,26 @@ using System.ComponentModel; namespace System.Drawing { - [ComVisible(true)] [Serializable] #if !NETCORE [Editor ("System.Drawing.Design.BitmapEditor, " + Consts.AssemblySystem_Drawing_Design, typeof (System.Drawing.Design.UITypeEditor))] #endif - public sealed class Bitmap : Image + public sealed partial class Bitmap { #region constructors - // constructors - - // required for XmlSerializer (#323246) - private Bitmap() - { - } - - internal Bitmap(IntPtr ptr) - { - nativeObject = ptr; - } // Usually called when cloning images that need to have // not only the handle saved, but also the underlying stream // (when using MS GDI+ and IStream we must ensure the stream stays alive for all the life of the Image) internal Bitmap(IntPtr ptr, Stream stream) { - nativeObject = ptr; + nativeImage = ptr; } - public Bitmap(int width, int height) : this(width, height, PixelFormat.Format32bppArgb) - { - } - - public Bitmap(int width, int height, Graphics g) - { - if (g == null) - throw new ArgumentNullException("g"); - - IntPtr bmp; - int s = SafeNativeMethods.Gdip.GdipCreateBitmapFromGraphics(width, height, g.nativeObject, out bmp); - SafeNativeMethods.Gdip.CheckStatus(s); - nativeObject = bmp; - } - - public Bitmap(int width, int height, PixelFormat format) - { - IntPtr bmp; - int s = SafeNativeMethods.Gdip.GdipCreateBitmapFromScan0(width, height, 0, format, IntPtr.Zero, out bmp); - SafeNativeMethods.Gdip.CheckStatus(s); - nativeObject = bmp; - - } - - public Bitmap(Image original) : this(original, original.Width, original.Height) { } - - public Bitmap(Stream stream) : this(stream, false) { } - - public Bitmap(string filename) : this(filename, false) { } - - public Bitmap(Image original, Size newSize) : this(original, newSize.Width, newSize.Height) { } - public Bitmap(Stream stream, bool useIcm) { // false: stream is owned by user code - nativeObject = InitFromStream(stream); - } - - public Bitmap(string filename, bool useIcm) - { - if (filename == null) - throw new ArgumentNullException("filename"); - - IntPtr imagePtr; - int st; - - if (useIcm) - st = SafeNativeMethods.Gdip.GdipCreateBitmapFromFileICM(filename, out imagePtr); - else - st = SafeNativeMethods.Gdip.GdipCreateBitmapFromFile(filename, out imagePtr); - - SafeNativeMethods.Gdip.CheckStatus(st); - nativeObject = imagePtr; + nativeImage = InitFromStream(stream); } public Bitmap(Type type, string resource) @@ -145,24 +84,7 @@ namespace System.Drawing throw new FileNotFoundException(msg); } - nativeObject = InitFromStream(s); - } - - public Bitmap(Image original, int width, int height) : this(width, height, PixelFormat.Format32bppArgb) - { - Graphics graphics = Graphics.FromImage(this); - - graphics.DrawImage(original, 0, 0, width, height); - graphics.Dispose(); - } - - public Bitmap(int width, int height, int stride, PixelFormat format, IntPtr scan0) - { - IntPtr bmp; - - int status = SafeNativeMethods.Gdip.GdipCreateBitmapFromScan0(width, height, stride, format, scan0, out bmp); - SafeNativeMethods.Gdip.CheckStatus(status); - nativeObject = bmp; + nativeImage = InitFromStream(s); } private Bitmap(SerializationInfo info, StreamingContext context) @@ -170,153 +92,10 @@ namespace System.Drawing { } #endregion - // methods - public Color GetPixel(int x, int y) + + private void ValidateBitmap(IntPtr bitmap) { - - int argb; - - int s = SafeNativeMethods.Gdip.GdipBitmapGetPixel(nativeObject, x, y, out argb); - SafeNativeMethods.Gdip.CheckStatus(s); - - return Color.FromArgb(argb); - } - - public void SetPixel(int x, int y, Color color) - { - int s = SafeNativeMethods.Gdip.GdipBitmapSetPixel(nativeObject, x, y, color.ToArgb()); - if (s == SafeNativeMethods.Gdip.InvalidParameter) - { - // check is done in case of an error only to avoid another - // unmanaged call for normal (successful) calls - if ((this.PixelFormat & PixelFormat.Indexed) != 0) - { - string msg = "SetPixel cannot be called on indexed bitmaps."; - throw new InvalidOperationException(msg); - } - } - SafeNativeMethods.Gdip.CheckStatus(s); - } - - public Bitmap Clone(Rectangle rect, PixelFormat format) - { - IntPtr bmp; - int status = SafeNativeMethods.Gdip.GdipCloneBitmapAreaI(rect.X, rect.Y, rect.Width, rect.Height, - format, nativeObject, out bmp); - SafeNativeMethods.Gdip.CheckStatus(status); - return new Bitmap(bmp); - } - - public Bitmap Clone(RectangleF rect, PixelFormat format) - { - IntPtr bmp; - int status = SafeNativeMethods.Gdip.GdipCloneBitmapArea(rect.X, rect.Y, rect.Width, rect.Height, - format, nativeObject, out bmp); - SafeNativeMethods.Gdip.CheckStatus(status); - return new Bitmap(bmp); - } - - public static Bitmap FromHicon(IntPtr hicon) - { - IntPtr bitmap; - int status = SafeNativeMethods.Gdip.GdipCreateBitmapFromHICON(hicon, out bitmap); - SafeNativeMethods.Gdip.CheckStatus(status); - return new Bitmap(bitmap); - } - - public static Bitmap FromResource(IntPtr hinstance, string bitmapName) //TODO: Untested - { - IntPtr bitmap; - int status = SafeNativeMethods.Gdip.GdipCreateBitmapFromResource(hinstance, bitmapName, out bitmap); - SafeNativeMethods.Gdip.CheckStatus(status); - return new Bitmap(bitmap); - } - - [EditorBrowsable(EditorBrowsableState.Advanced)] - public IntPtr GetHbitmap() - { - return GetHbitmap(Color.Gray); - } - - [EditorBrowsable(EditorBrowsableState.Advanced)] - public IntPtr GetHbitmap(Color background) - { - IntPtr HandleBmp; - - int status = SafeNativeMethods.Gdip.GdipCreateHBITMAPFromBitmap(nativeObject, out HandleBmp, background.ToArgb()); - SafeNativeMethods.Gdip.CheckStatus(status); - - return HandleBmp; - } - - [EditorBrowsable(EditorBrowsableState.Advanced)] - public IntPtr GetHicon() - { - IntPtr HandleIcon; - - int status = SafeNativeMethods.Gdip.GdipCreateHICONFromBitmap(nativeObject, out HandleIcon); - SafeNativeMethods.Gdip.CheckStatus(status); - - return HandleIcon; - } - - public BitmapData LockBits(Rectangle rect, ImageLockMode flags, PixelFormat format) - { - BitmapData result = new BitmapData(); - return LockBits(rect, flags, format, result); - } - - public - BitmapData LockBits(Rectangle rect, ImageLockMode flags, PixelFormat format, BitmapData bitmapData) - { - int status = SafeNativeMethods.Gdip.GdipBitmapLockBits(nativeObject, ref rect, flags, format, bitmapData); - if (status == 7) - { - status = 8; // libgdiplus has the wrong error code mapping for this state. - } - //NOTE: scan0 points to piece of memory allocated in the unmanaged space - SafeNativeMethods.Gdip.CheckStatus(status); - - return bitmapData; - } - - public void MakeTransparent() - { - Color clr = GetPixel(0, 0); - MakeTransparent(clr); - } - - public void MakeTransparent(Color transparentColor) - { - // We have to draw always over a 32-bitmap surface that supports alpha channel - Bitmap bmp = new Bitmap(Width, Height, PixelFormat.Format32bppArgb); - Graphics gr = Graphics.FromImage(bmp); - Rectangle destRect = new Rectangle(0, 0, Width, Height); - ImageAttributes imageAttr = new ImageAttributes(); - - imageAttr.SetColorKey(transparentColor, transparentColor); - - gr.DrawImage(this, destRect, 0, 0, Width, Height, GraphicsUnit.Pixel, imageAttr); - - IntPtr oldBmp = nativeObject; - nativeObject = bmp.nativeObject; - bmp.nativeObject = oldBmp; - - gr.Dispose(); - bmp.Dispose(); - imageAttr.Dispose(); - } - - public void SetResolution(float xDpi, float yDpi) - { - int status = SafeNativeMethods.Gdip.GdipBitmapSetResolution(nativeObject, xDpi, yDpi); - SafeNativeMethods.Gdip.CheckStatus(status); - } - - public void UnlockBits(BitmapData bitmapdata) - { - int status = SafeNativeMethods.Gdip.GdipBitmapUnlockBits(nativeObject, bitmapdata); - SafeNativeMethods.Gdip.CheckStatus(status); + // No validation is performed on Unix. } } } diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Bitmap.Windows.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Bitmap.Windows.cs index 3b58ff245c..814fd4ec68 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Bitmap.Windows.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Bitmap.Windows.cs @@ -11,52 +11,8 @@ using System.Security.Permissions; namespace System.Drawing { - [ComVisible(true)] - public sealed partial class Bitmap : Image + public sealed partial class Bitmap { - private static Color s_defaultTransparentColor = Color.LightGray; - - public Bitmap(string filename) - { - // GDI+ will read this file multiple times. Get the fully qualified path - // so if the app's default directory changes we won't get an error. - filename = Path.GetFullPath(filename); - - IntPtr bitmap = IntPtr.Zero; - int status = SafeNativeMethods.Gdip.GdipCreateBitmapFromFile(filename, out bitmap); - SafeNativeMethods.Gdip.CheckStatus(status); - - ValidateBitmap(bitmap); - - SetNativeImage(bitmap); - EnsureSave(this, filename, null); - } - - public Bitmap(string filename, bool useIcm) - { - // GDI+ will read this file multiple times. Get the fully qualified path - // so if the app's default directory changes we won't get an error. - filename = Path.GetFullPath(filename); - - IntPtr bitmap = IntPtr.Zero; - int status; - - if (useIcm) - { - status = SafeNativeMethods.Gdip.GdipCreateBitmapFromFileICM(filename, out bitmap); - } - else - { - status = SafeNativeMethods.Gdip.GdipCreateBitmapFromFile(filename, out bitmap); - } - SafeNativeMethods.Gdip.CheckStatus(status); - - ValidateBitmap(bitmap); - - SetNativeImage(bitmap); - EnsureSave(this, filename, null); - } - public Bitmap(Type type, string resource) { Stream stream = type.Module.Assembly.GetManifestResourceStream(type, resource); @@ -75,23 +31,6 @@ namespace System.Drawing EnsureSave(this, null, stream); } - public Bitmap(Stream stream) - { - if (stream == null) - { - throw new ArgumentNullException(nameof(stream)); - } - - IntPtr bitmap = IntPtr.Zero; - int status = SafeNativeMethods.Gdip.GdipCreateBitmapFromStream(new GPStream(stream), out bitmap); - SafeNativeMethods.Gdip.CheckStatus(status); - - ValidateBitmap(bitmap); - - SetNativeImage(bitmap); - EnsureSave(this, null, stream); - } - public Bitmap(Stream stream, bool useIcm) { if (stream == null) @@ -118,298 +57,6 @@ namespace System.Drawing EnsureSave(this, null, stream); } - public Bitmap(int width, int height, int stride, PixelFormat format, IntPtr scan0) - { - IntPtr bitmap = IntPtr.Zero; - int status = SafeNativeMethods.Gdip.GdipCreateBitmapFromScan0(width, height, stride, unchecked((int)format), new HandleRef(null, scan0), out bitmap); - SafeNativeMethods.Gdip.CheckStatus(status); - - SetNativeImage(bitmap); - } - - public Bitmap(int width, int height, PixelFormat format) - { - IntPtr bitmap = IntPtr.Zero; - int status = SafeNativeMethods.Gdip.GdipCreateBitmapFromScan0(width, height, 0, unchecked((int)format), NativeMethods.NullHandleRef, out bitmap); - SafeNativeMethods.Gdip.CheckStatus(status); - - SetNativeImage(bitmap); - } - - public Bitmap(int width, int height) : this(width, height, PixelFormat.Format32bppArgb) - { - } - - public Bitmap(int width, int height, Graphics g) - { - if (g == null) - { - throw new ArgumentNullException(nameof(g)); - } - - IntPtr bitmap = IntPtr.Zero; - int status = SafeNativeMethods.Gdip.GdipCreateBitmapFromGraphics(width, height, new HandleRef(g, g.NativeGraphics), out bitmap); - SafeNativeMethods.Gdip.CheckStatus(status); - - SetNativeImage(bitmap); - } - - public Bitmap(Image original) : this(original, original.Width, original.Height) - { - } - - public Bitmap(Image original, int width, int height) : this(width, height) - { - using (Graphics g = Graphics.FromImage(this)) - { - g.Clear(Color.Transparent); - g.DrawImage(original, 0, 0, width, height); - } - } - - public static Bitmap FromHicon(IntPtr hicon) - { - IntPtr bitmap = IntPtr.Zero; - int status = SafeNativeMethods.Gdip.GdipCreateBitmapFromHICON(new HandleRef(null, hicon), out bitmap); - SafeNativeMethods.Gdip.CheckStatus(status); - - return FromGDIplus(bitmap); - } - - public static Bitmap FromResource(IntPtr hinstance, string bitmapName) - { - IntPtr bitmap; - IntPtr name = Marshal.StringToHGlobalUni(bitmapName); - try - { - int status = SafeNativeMethods.Gdip.GdipCreateBitmapFromResource(new HandleRef(null, hinstance), - new HandleRef(null, name), - out bitmap); - SafeNativeMethods.Gdip.CheckStatus(status); - } - finally - { - Marshal.FreeHGlobal(name); - } - - return FromGDIplus(bitmap); - } - - [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] - [EditorBrowsable(EditorBrowsableState.Advanced)] - public IntPtr GetHbitmap() => GetHbitmap(Color.LightGray); - - [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] - [EditorBrowsable(EditorBrowsableState.Advanced)] - public IntPtr GetHbitmap(Color background) - { - IntPtr hBitmap = IntPtr.Zero; - int status = SafeNativeMethods.Gdip.GdipCreateHBITMAPFromBitmap(new HandleRef(this, nativeImage), out hBitmap, - ColorTranslator.ToWin32(background)); - if (status == 2 /* invalid parameter*/ && (Width >= short.MaxValue || Height >= short.MaxValue)) - { - throw new ArgumentException(SR.Format(SR.GdiplusInvalidSize)); - } - - SafeNativeMethods.Gdip.CheckStatus(status); - - return hBitmap; - } - - [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] - [EditorBrowsable(EditorBrowsableState.Advanced)] - public IntPtr GetHicon() - { - IntPtr hIcon = IntPtr.Zero; - int status = SafeNativeMethods.Gdip.GdipCreateHICONFromBitmap(new HandleRef(this, nativeImage), out hIcon); - SafeNativeMethods.Gdip.CheckStatus(status); - - return hIcon; - } - - public Bitmap(Image original, Size newSize) : this(original, newSize.Width, newSize.Height) - { - } - - private Bitmap() { } - - /* - * Create a new bitmap object from a native bitmap handle. - * This is only for internal purpose. - */ - internal static Bitmap FromGDIplus(IntPtr handle) - { - Bitmap result = new Bitmap(); - result.SetNativeImage(handle); - return result; - } - - public Bitmap Clone(Rectangle rect, PixelFormat format) - { - if (rect.Width == 0 || rect.Height == 0) - { - throw new ArgumentException(SR.Format(SR.GdiplusInvalidRectangle, rect.ToString())); - } - - IntPtr dstHandle = IntPtr.Zero; - int status = SafeNativeMethods.Gdip.GdipCloneBitmapAreaI( - rect.X, - rect.Y, - rect.Width, - rect.Height, - unchecked((int)format), - new HandleRef(this, nativeImage), - out dstHandle); - - if (status != SafeNativeMethods.Gdip.Ok || dstHandle == IntPtr.Zero) - throw SafeNativeMethods.Gdip.StatusException(status); - - return FromGDIplus(dstHandle); - } - - public Bitmap Clone(RectangleF rect, PixelFormat format) - { - if (rect.Width == 0 || rect.Height == 0) - { - throw new ArgumentException(SR.Format(SR.GdiplusInvalidRectangle, rect.ToString())); - } - - IntPtr dstHandle = IntPtr.Zero; - - int status = SafeNativeMethods.Gdip.GdipCloneBitmapArea( - rect.X, - rect.Y, - rect.Width, - rect.Height, - unchecked((int)format), - new HandleRef(this, nativeImage), - out dstHandle); - - if (status != SafeNativeMethods.Gdip.Ok || dstHandle == IntPtr.Zero) - throw SafeNativeMethods.Gdip.StatusException(status); - - return FromGDIplus(dstHandle); - } - - public void MakeTransparent() - { - Color transparent = s_defaultTransparentColor; - if (Height > 0 && Width > 0) - { - transparent = GetPixel(0, Size.Height - 1); - } - if (transparent.A < 255) - { - // It's already transparent, and if we proceeded, we will do something - // unintended like making black transparent - return; - } - - MakeTransparent(transparent); - } - - public void MakeTransparent(Color transparentColor) - { - if (RawFormat.Guid == ImageFormat.Icon.Guid) - { - throw new InvalidOperationException(SR.Format(SR.CantMakeIconTransparent)); - } - - Size size = Size; - - // The new bitmap must be in 32bppARGB format, because that's the only - // thing that supports alpha. (And that's what the image is initialized to -- transparent) - using (var result = new Bitmap(size.Width, size.Height, PixelFormat.Format32bppArgb)) - using (Graphics graphics = Graphics.FromImage(result)) - { - graphics.Clear(Color.Transparent); - Rectangle rectangle = new Rectangle(0, 0, size.Width, size.Height); - - using (var attributes = new ImageAttributes()) - { - attributes.SetColorKey(transparentColor, transparentColor); - graphics.DrawImage(this, rectangle, - 0, 0, size.Width, size.Height, - GraphicsUnit.Pixel, attributes, null, IntPtr.Zero); - } - - // Swap nativeImage pointers to make it look like we modified the image in place - IntPtr temp = nativeImage; - nativeImage = result.nativeImage; - result.nativeImage = temp; - } - } - - [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] - public BitmapData LockBits(Rectangle rect, ImageLockMode flags, PixelFormat format) - { - return LockBits(rect, flags, format, new BitmapData()); - } - - [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] - public BitmapData LockBits(Rectangle rect, ImageLockMode flags, PixelFormat format, BitmapData bitmapData) - { - var gprect = new GPRECT(rect); - int status = SafeNativeMethods.Gdip.GdipBitmapLockBits(new HandleRef(this, nativeImage), ref gprect, - flags, format, bitmapData); - SafeNativeMethods.Gdip.CheckStatus(status); - - return bitmapData; - } - - [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] - public void UnlockBits(BitmapData bitmapdata) - { - int status = SafeNativeMethods.Gdip.GdipBitmapUnlockBits(new HandleRef(this, nativeImage), bitmapdata); - SafeNativeMethods.Gdip.CheckStatus(status); - } - - public Color GetPixel(int x, int y) - { - if (x < 0 || x >= Width) - { - throw new ArgumentOutOfRangeException(nameof(x), SR.Format(SR.ValidRangeX)); - } - - if (y < 0 || y >= Height) - { - throw new ArgumentOutOfRangeException(nameof(y), SR.Format(SR.ValidRangeY)); - } - - int color = 0; - int status = SafeNativeMethods.Gdip.GdipBitmapGetPixel(new HandleRef(this, nativeImage), x, y, out color); - SafeNativeMethods.Gdip.CheckStatus(status); - - return Color.FromArgb(color); - } - - public void SetPixel(int x, int y, Color color) - { - if ((PixelFormat & PixelFormat.Indexed) != 0) - { - throw new InvalidOperationException(SR.Format(SR.GdiplusCannotSetPixelFromIndexedPixelFormat)); - } - - if (x < 0 || x >= Width) - { - throw new ArgumentOutOfRangeException(nameof(x), SR.Format(SR.ValidRangeX)); - } - - if (y < 0 || y >= Height) - { - throw new ArgumentOutOfRangeException(nameof(y), SR.Format(SR.ValidRangeY)); - } - - int status = SafeNativeMethods.Gdip.GdipBitmapSetPixel(new HandleRef(this, nativeImage), x, y, color.ToArgb()); - SafeNativeMethods.Gdip.CheckStatus(status); - } - - public void SetResolution(float xDpi, float yDpi) - { - int status = SafeNativeMethods.Gdip.GdipBitmapSetResolution(new HandleRef(this, nativeImage), xDpi, yDpi); - SafeNativeMethods.Gdip.CheckStatus(status); - } - private void ValidateBitmap(IntPtr bitmap) { int status = SafeNativeMethods.Gdip.GdipImageForceValidation(new HandleRef(null, bitmap)); diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Bitmap.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Bitmap.cs new file mode 100644 index 0000000000..b549701d1f --- /dev/null +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Bitmap.cs @@ -0,0 +1,328 @@ +// 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.ComponentModel; +using System.Drawing.Imaging; +using System.Drawing.Internal; +using System.IO; +using System.Runtime.InteropServices; +using System.Security.Permissions; + +namespace System.Drawing +{ + public sealed partial class Bitmap : Image + { + private static Color s_defaultTransparentColor = Color.LightGray; + + private Bitmap() { } + + internal Bitmap(IntPtr ptr) => SetNativeImage(ptr); + + public Bitmap(string filename) : this (filename, useIcm: false) { } + + public Bitmap(string filename, bool useIcm) + { + // GDI+ will read this file multiple times. Get the fully qualified path + // so if the app's default directory changes we won't get an error. + filename = Path.GetFullPath(filename); + + IntPtr bitmap = IntPtr.Zero; + int status; + + if (useIcm) + { + status = SafeNativeMethods.Gdip.GdipCreateBitmapFromFileICM(filename, out bitmap); + } + else + { + status = SafeNativeMethods.Gdip.GdipCreateBitmapFromFile(filename, out bitmap); + } + SafeNativeMethods.Gdip.CheckStatus(status); + + ValidateBitmap(bitmap); + + SetNativeImage(bitmap); + EnsureSave(this, filename, null); + } + + public Bitmap(Stream stream) : this(stream, false) { } + + public Bitmap(int width, int height) : this(width, height, PixelFormat.Format32bppArgb) + { + } + + public Bitmap(int width, int height, Graphics g) + { + if (g == null) + { + throw new ArgumentNullException(nameof(g)); + } + + IntPtr bitmap = IntPtr.Zero; + int status = SafeNativeMethods.Gdip.GdipCreateBitmapFromGraphics(width, height, new HandleRef(g, g.NativeGraphics), out bitmap); + SafeNativeMethods.Gdip.CheckStatus(status); + + SetNativeImage(bitmap); + } + + public Bitmap(int width, int height, int stride, PixelFormat format, IntPtr scan0) + { + IntPtr bitmap = IntPtr.Zero; + int status = SafeNativeMethods.Gdip.GdipCreateBitmapFromScan0(width, height, stride, unchecked((int)format), new HandleRef(null, scan0), out bitmap); + SafeNativeMethods.Gdip.CheckStatus(status); + + SetNativeImage(bitmap); + } + + public Bitmap(int width, int height, PixelFormat format) + { + IntPtr bitmap = IntPtr.Zero; + int status = SafeNativeMethods.Gdip.GdipCreateBitmapFromScan0(width, height, 0, unchecked((int)format), NativeMethods.NullHandleRef, out bitmap); + SafeNativeMethods.Gdip.CheckStatus(status); + + SetNativeImage(bitmap); + } + + public Bitmap(Image original) : this(original, original.Width, original.Height) + { + } + + public Bitmap(Image original, Size newSize) : this(original, newSize.Width, newSize.Height) + { + } + + public Bitmap(Image original, int width, int height) : this(width, height, PixelFormat.Format32bppArgb) + { + using (Graphics g = Graphics.FromImage(this)) + { + g.Clear(Color.Transparent); + g.DrawImage(original, 0, 0, width, height); + } + } + + public static Bitmap FromHicon(IntPtr hicon) + { + IntPtr bitmap = IntPtr.Zero; + int status = SafeNativeMethods.Gdip.GdipCreateBitmapFromHICON(new HandleRef(null, hicon), out bitmap); + SafeNativeMethods.Gdip.CheckStatus(status); + + return new Bitmap(bitmap); + } + + public static Bitmap FromResource(IntPtr hinstance, string bitmapName) + { + IntPtr bitmap; + IntPtr name = Marshal.StringToHGlobalUni(bitmapName); + try + { + int status = SafeNativeMethods.Gdip.GdipCreateBitmapFromResource(new HandleRef(null, hinstance), + new HandleRef(null, name), + out bitmap); + SafeNativeMethods.Gdip.CheckStatus(status); + } + finally + { + Marshal.FreeHGlobal(name); + } + + return new Bitmap(bitmap); + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + public IntPtr GetHbitmap() => GetHbitmap(Color.LightGray); + + [EditorBrowsable(EditorBrowsableState.Advanced)] + public IntPtr GetHbitmap(Color background) + { + IntPtr hBitmap = IntPtr.Zero; + int status = SafeNativeMethods.Gdip.GdipCreateHBITMAPFromBitmap(new HandleRef(this, nativeImage), out hBitmap, + ColorTranslator.ToWin32(background)); + if (status == 2 /* invalid parameter*/ && (Width >= short.MaxValue || Height >= short.MaxValue)) + { + throw new ArgumentException(SR.Format(SR.GdiplusInvalidSize)); + } + + SafeNativeMethods.Gdip.CheckStatus(status); + + return hBitmap; + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + public IntPtr GetHicon() + { + IntPtr hIcon = IntPtr.Zero; + int status = SafeNativeMethods.Gdip.GdipCreateHICONFromBitmap(new HandleRef(this, nativeImage), out hIcon); + SafeNativeMethods.Gdip.CheckStatus(status); + + return hIcon; + } + + public Bitmap Clone(RectangleF rect, PixelFormat format) + { + if (rect.Width == 0 || rect.Height == 0) + { + throw new ArgumentException(SR.Format(SR.GdiplusInvalidRectangle, rect.ToString())); + } + + IntPtr dstHandle = IntPtr.Zero; + + int status = SafeNativeMethods.Gdip.GdipCloneBitmapArea( + rect.X, + rect.Y, + rect.Width, + rect.Height, + unchecked((int)format), + new HandleRef(this, nativeImage), + out dstHandle); + + if (status != SafeNativeMethods.Gdip.Ok || dstHandle == IntPtr.Zero) + throw SafeNativeMethods.Gdip.StatusException(status); + + return new Bitmap(dstHandle); + } + + public void MakeTransparent() + { + Color transparent = s_defaultTransparentColor; + if (Height > 0 && Width > 0) + { + transparent = GetPixel(0, Size.Height - 1); + } + if (transparent.A < 255) + { + // It's already transparent, and if we proceeded, we will do something + // unintended like making black transparent + return; + } + + MakeTransparent(transparent); + } + + public void MakeTransparent(Color transparentColor) + { + if (RawFormat.Guid == ImageFormat.Icon.Guid) + { + throw new InvalidOperationException(SR.Format(SR.CantMakeIconTransparent)); + } + + Size size = Size; + + // The new bitmap must be in 32bppARGB format, because that's the only + // thing that supports alpha. (And that's what the image is initialized to -- transparent) + using (var result = new Bitmap(size.Width, size.Height, PixelFormat.Format32bppArgb)) + using (Graphics graphics = Graphics.FromImage(result)) + { + graphics.Clear(Color.Transparent); + Rectangle rectangle = new Rectangle(0, 0, size.Width, size.Height); + + using (var attributes = new ImageAttributes()) + { + attributes.SetColorKey(transparentColor, transparentColor); + graphics.DrawImage(this, rectangle, + 0, 0, size.Width, size.Height, + GraphicsUnit.Pixel, attributes, null, IntPtr.Zero); + } + + // Swap nativeImage pointers to make it look like we modified the image in place + IntPtr temp = nativeImage; + nativeImage = result.nativeImage; + result.nativeImage = temp; + } + } + + public BitmapData LockBits(Rectangle rect, ImageLockMode flags, PixelFormat format) + { + return LockBits(rect, flags, format, new BitmapData()); + } + + public BitmapData LockBits(Rectangle rect, ImageLockMode flags, PixelFormat format, BitmapData bitmapData) + { + var gprect = new GPRECT(rect); + int status = SafeNativeMethods.Gdip.GdipBitmapLockBits(new HandleRef(this, nativeImage), ref gprect, + flags, format, bitmapData); + // libgdiplus has the wrong error code mapping for this state. + if (status == 7) + { + status = 8; + } + SafeNativeMethods.Gdip.CheckStatus(status); + + return bitmapData; + } + + public void UnlockBits(BitmapData bitmapdata) + { + int status = SafeNativeMethods.Gdip.GdipBitmapUnlockBits(new HandleRef(this, nativeImage), bitmapdata); + SafeNativeMethods.Gdip.CheckStatus(status); + } + + public Color GetPixel(int x, int y) + { + if (x < 0 || x >= Width) + { + throw new ArgumentOutOfRangeException(nameof(x), SR.Format(SR.ValidRangeX)); + } + + if (y < 0 || y >= Height) + { + throw new ArgumentOutOfRangeException(nameof(y), SR.Format(SR.ValidRangeY)); + } + + int color = 0; + int status = SafeNativeMethods.Gdip.GdipBitmapGetPixel(new HandleRef(this, nativeImage), x, y, out color); + SafeNativeMethods.Gdip.CheckStatus(status); + + return Color.FromArgb(color); + } + + public void SetPixel(int x, int y, Color color) + { + if ((PixelFormat & PixelFormat.Indexed) != 0) + { + throw new InvalidOperationException(SR.Format(SR.GdiplusCannotSetPixelFromIndexedPixelFormat)); + } + + if (x < 0 || x >= Width) + { + throw new ArgumentOutOfRangeException(nameof(x), SR.Format(SR.ValidRangeX)); + } + + if (y < 0 || y >= Height) + { + throw new ArgumentOutOfRangeException(nameof(y), SR.Format(SR.ValidRangeY)); + } + + int status = SafeNativeMethods.Gdip.GdipBitmapSetPixel(new HandleRef(this, nativeImage), x, y, color.ToArgb()); + SafeNativeMethods.Gdip.CheckStatus(status); + } + + public void SetResolution(float xDpi, float yDpi) + { + int status = SafeNativeMethods.Gdip.GdipBitmapSetResolution(new HandleRef(this, nativeImage), xDpi, yDpi); + SafeNativeMethods.Gdip.CheckStatus(status); + } + public Bitmap Clone(Rectangle rect, PixelFormat format) + { + if (rect.Width == 0 || rect.Height == 0) + { + throw new ArgumentException(SR.Format(SR.GdiplusInvalidRectangle, rect.ToString())); + } + + IntPtr dstHandle = IntPtr.Zero; + int status = SafeNativeMethods.Gdip.GdipCloneBitmapAreaI( + rect.X, + rect.Y, + rect.Width, + rect.Height, + unchecked((int)format), + new HandleRef(this, nativeImage), + out dstHandle); + + if (status != SafeNativeMethods.Gdip.Ok || dstHandle == IntPtr.Zero) + throw SafeNativeMethods.Gdip.StatusException(status); + + return new Bitmap(dstHandle); + } + } +} diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/ColorTranslator.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/ColorTranslator.cs index 4c9bd74851..8bf006b9d2 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/ColorTranslator.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/ColorTranslator.cs @@ -258,7 +258,14 @@ namespace System.Drawing // resort to type converter which will handle named colors if (c.IsEmpty) { - c = ColorConverterCommon.ConvertFromString(htmlColor, CultureInfo.CurrentCulture); + try + { + c = ColorConverterCommon.ConvertFromString(htmlColor, CultureInfo.CurrentCulture); + } + catch(Exception ex) + { + throw new ArgumentException(ex.Message, nameof(htmlColor), ex); + } } return c; diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/CopyPixelOperation.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/CopyPixelOperation.cs index 2f77bc5d43..6119ae8322 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/CopyPixelOperation.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/CopyPixelOperation.cs @@ -7,7 +7,6 @@ namespace System.Drawing /// /// Specifies the Copy Pixel (ROP) operation. /// - [System.Runtime.InteropServices.ComVisible(true)] public enum CopyPixelOperation { /// diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/PaintValueEventArgs.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/PaintValueEventArgs.cs index 1f445670db..5c45524e23 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/PaintValueEventArgs.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/PaintValueEventArgs.cs @@ -21,8 +21,6 @@ namespace System.Drawing.Design { /// the drawing should be done, and the Graphics object with which the drawing /// should be done. /// - [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] - [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] public class PaintValueEventArgs : EventArgs { private readonly ITypeDescriptorContext context; diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/PropertyValueUIItem.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/PropertyValueUIItem.cs index 67f78aef41..298f7a73a1 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/PropertyValueUIItem.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/PropertyValueUIItem.cs @@ -20,8 +20,6 @@ namespace System.Drawing.Design { /// handler, tool tip, and the glyph icon to be displayed on the property /// browser. /// - [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] - [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] public class PropertyValueUIItem { /// diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/ToolboxComponentsCreatedEventArgs.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/ToolboxComponentsCreatedEventArgs.cs index 96595691b2..f21ce8ba14 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/ToolboxComponentsCreatedEventArgs.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/ToolboxComponentsCreatedEventArgs.cs @@ -14,8 +14,6 @@ namespace System.Drawing.Design { /// Provides data for the 'ToolboxComponentsCreatedEventArgs' event that occurs /// when components are added to the toolbox. /// - [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] - [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] public class ToolboxComponentsCreatedEventArgs : EventArgs { private readonly IComponent[] comps; diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/ToolboxComponentsCreatingEventArgs.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/ToolboxComponentsCreatingEventArgs.cs index 14769ce443..12247e2ae5 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/ToolboxComponentsCreatingEventArgs.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/ToolboxComponentsCreatingEventArgs.cs @@ -14,8 +14,6 @@ namespace System.Drawing.Design { /// Provides data for the 'ToolboxComponentsCreatingEventArgs' event that occurs /// when components are added to the toolbox. /// - [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] - [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] public class ToolboxComponentsCreatingEventArgs : EventArgs { private readonly IDesignerHost host; diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/ToolboxItem.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/ToolboxItem.cs index 111eee72de..8fde796ad4 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/ToolboxItem.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/ToolboxItem.cs @@ -33,8 +33,6 @@ namespace System.Drawing.Design { /// Provides a base implementation of a toolbox item. /// [Serializable] - [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] - [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] public class ToolboxItem : ISerializable { private static TraceSwitch ToolboxItemPersist = new TraceSwitch("ToolboxPersisting", "ToolboxItem: write data"); diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/ToolboxItemCollection.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/ToolboxItemCollection.cs index 3b4412ed1b..3cfd526cb1 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/ToolboxItemCollection.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/ToolboxItemCollection.cs @@ -15,7 +15,6 @@ namespace System.Drawing.Design { /// A collection that stores objects. /// /// - [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] public sealed class ToolboxItemCollection : ReadOnlyCollectionBase { /// diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/UITypeEditor.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/UITypeEditor.cs index 851291c8a1..4d1498c604 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/UITypeEditor.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Design/UITypeEditor.cs @@ -24,8 +24,6 @@ namespace System.Drawing.Design { /// that may provide users with a user interface to visually edit /// the values of the supported type or types. /// - [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")] - [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")] public class UITypeEditor { /// diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Drawing2D/SafeCustomLineCapHandle.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Drawing2D/SafeCustomLineCapHandle.cs index 104ebbc9c0..add7f0ac5c 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Drawing2D/SafeCustomLineCapHandle.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Drawing2D/SafeCustomLineCapHandle.cs @@ -9,7 +9,6 @@ using System.Security; namespace System.Drawing.Drawing2D { - [SecurityCritical] internal class SafeCustomLineCapHandle : SafeHandle { // Create a SafeHandle, informing the base class @@ -22,7 +21,6 @@ namespace System.Drawing.Drawing2D SetHandle(h); } - [SecurityCritical] protected override bool ReleaseHandle() { int status = SafeNativeMethods.Gdip.Ok; diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Font.Serializable.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Font.Serializable.cs index b0f3e4ccde..37254fa1e2 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Font.Serializable.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Font.Serializable.cs @@ -84,7 +84,6 @@ namespace System.Drawing } } - [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context) { // Serialize the original Font name rather than the fallback font name if we have one diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Font.Unix.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Font.Unix.cs index 2f360846f1..56ff789861 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Font.Unix.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Font.Unix.cs @@ -41,7 +41,6 @@ using System.ComponentModel; namespace System.Drawing { [Serializable] - [ComVisible(true)] #if !NETCORE [Editor ("System.Drawing.Design.FontEditor, " + Consts.AssemblySystem_Drawing_Design, typeof (System.Drawing.Design.UITypeEditor))] [TypeConverter (typeof (FontConverter))] diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Font.Windows.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Font.Windows.cs index 23bf242f19..e31e57cb0d 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Font.Windows.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Font.Windows.cs @@ -14,7 +14,6 @@ namespace System.Drawing /// /// Defines a particular format for text, including font face, size, and style attributes. /// - [ComVisible(true)] public sealed partial class Font : MarshalByRefObject, ICloneable, IDisposable, ISerializable { private const int LogFontCharSetOffset = 23; diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Gdiplus.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Gdiplus.cs index f66a1a8600..6cf76d915f 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Gdiplus.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Gdiplus.cs @@ -63,11 +63,9 @@ using System.Runtime.CompilerServices; namespace System.Drawing { - [SuppressUnmanagedCodeSecurity] internal partial class SafeNativeMethods { // We make this a nested class so that we don't have to initialize GDI+ to access SafeNativeMethods (mostly gdi/user32). - [SuppressUnmanagedCodeSecurityAttribute] internal partial class Gdip { private static readonly TraceSwitch s_gdiPlusInitialization = new TraceSwitch("GdiPlusInitialization", "Tracks GDI+ initialization and teardown"); @@ -1321,56 +1319,41 @@ namespace System.Drawing [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IPicture { - [SuppressUnmanagedCodeSecurity] IntPtr GetHandle(); - [SuppressUnmanagedCodeSecurity] IntPtr GetHPal(); [return: MarshalAs(UnmanagedType.I2)] - [SuppressUnmanagedCodeSecurity] short GetPictureType(); - [SuppressUnmanagedCodeSecurity] int GetWidth(); - [SuppressUnmanagedCodeSecurity] int GetHeight(); - [SuppressUnmanagedCodeSecurity] void Render(); - [SuppressUnmanagedCodeSecurity] void SetHPal([In] IntPtr phpal); - [SuppressUnmanagedCodeSecurity] IntPtr GetCurDC(); - [SuppressUnmanagedCodeSecurity] void SelectPicture([In] IntPtr hdcIn, [Out, MarshalAs(UnmanagedType.LPArray)] int[] phdcOut, [Out, MarshalAs(UnmanagedType.LPArray)] int[] phbmpOut); [return: MarshalAs(UnmanagedType.Bool)] - [SuppressUnmanagedCodeSecurity] bool GetKeepOriginalFormat(); - [SuppressUnmanagedCodeSecurity] void SetKeepOriginalFormat([In, MarshalAs(UnmanagedType.Bool)] bool pfkeep); - [SuppressUnmanagedCodeSecurity] void PictureChanged(); - [SuppressUnmanagedCodeSecurity] [PreserveSig] int SaveAsFile([In, MarshalAs(UnmanagedType.Interface)] UnsafeNativeMethods.IStream pstm, [In] int fSaveMemCopy, [Out] out int pcbSize); - [SuppressUnmanagedCodeSecurity] int GetAttributes(); - [SuppressUnmanagedCodeSecurity] void SetHdc([In] IntPtr hdc); } } diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.Unix.cs.REMOVED.git-id b/external/corefx/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.Unix.cs.REMOVED.git-id index 507b08e578..46414cbd64 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.Unix.cs.REMOVED.git-id +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.Unix.cs.REMOVED.git-id @@ -1 +1 @@ -b6bca4984ed6d22b5fe95202452c9ce6a391f130 \ No newline at end of file +4c7130185161e1d557c914a64e6c6258ff7f7aac \ No newline at end of file diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs.REMOVED.git-id b/external/corefx/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs.REMOVED.git-id index 79013d34ab..1d06cdb94b 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs.REMOVED.git-id +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs.REMOVED.git-id @@ -1 +1 @@ -fb5077cfa16bc0077bdabce8ba730320907a4e8a \ No newline at end of file +3df661275a1a3f060a9d0b4df4d9ceb7118f8969 \ No newline at end of file diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.cs.REMOVED.git-id b/external/corefx/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.cs.REMOVED.git-id index 995f803f6e..9170008a49 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.cs.REMOVED.git-id +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.cs.REMOVED.git-id @@ -1 +1 @@ -32f468bca56c69c2f37dcc41a18cf87332f69e4a \ No newline at end of file +5f0b4a547831088c35c0045c33c21b0f3e93f509 \ No newline at end of file diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Graphics.Unix.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Graphics.Unix.cs index 4cf0917f63..1392545af8 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Graphics.Unix.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Graphics.Unix.cs @@ -197,19 +197,10 @@ namespace System.Drawing { CopyFromScreenX11(sourceX, sourceY, destinationX, destinationY, blockRegionSize, copyPixelOperation); } - else if (SafeNativeMethods.Gdip.UseCarbonDrawable) + else { - CopyFromScreenMac(sourceX, sourceY, destinationX, destinationY, blockRegionSize, copyPixelOperation); + throw new PlatformNotSupportedException(); } - else if (SafeNativeMethods.Gdip.UseCocoaDrawable) - { - CopyFromScreenMac(sourceX, sourceY, destinationX, destinationY, blockRegionSize, copyPixelOperation); - } - } - - private void CopyFromScreenMac(int sourceX, int sourceY, int destinationX, int destinationY, Size blockRegionSize, CopyPixelOperation copyPixelOperation) - { - throw new NotImplementedException(); } private void CopyFromScreenX11(int sourceX, int sourceY, int destinationX, int destinationY, Size blockRegionSize, CopyPixelOperation copyPixelOperation) @@ -233,18 +224,6 @@ namespace System.Drawing visual.visualid = LibX11Functions.XVisualIDFromVisual(defvisual); vPtr = LibX11Functions.XGetVisualInfo(SafeNativeMethods.Gdip.Display, 0x1 /* VisualIDMask */, ref visual, ref nitems); visual = (XVisualInfo)Marshal.PtrToStructure(vPtr, typeof(XVisualInfo)); -#if false - Console.WriteLine ("visual\t{0}", visual.visual); - Console.WriteLine ("visualid\t{0}", visual.visualid); - Console.WriteLine ("screen\t{0}", visual.screen); - Console.WriteLine ("depth\t{0}", visual.depth); - Console.WriteLine ("klass\t{0}", visual.klass); - Console.WriteLine ("red_mask\t{0:X}", visual.red_mask); - Console.WriteLine ("green_mask\t{0:X}", visual.green_mask); - Console.WriteLine ("blue_mask\t{0:X}", visual.blue_mask); - Console.WriteLine ("colormap_size\t{0}", visual.colormap_size); - Console.WriteLine ("bits_per_rgb\t{0}", visual.bits_per_rgb); -#endif image = LibX11Functions.XGetImage(SafeNativeMethods.Gdip.Display, window, sourceX, sourceY, blockRegionSize.Width, blockRegionSize.Height, AllPlanes, 2 /* ZPixmap*/); if (image == IntPtr.Zero) @@ -298,7 +277,7 @@ namespace System.Drawing int status; if (!disposed) { - if (SafeNativeMethods.Gdip.UseCarbonDrawable || SafeNativeMethods.Gdip.UseCocoaDrawable) + if (!SafeNativeMethods.Gdip.UseX11Drawable) { Flush(); if (maccontext != null) @@ -643,7 +622,7 @@ namespace System.Drawing if (image == null) throw new ArgumentNullException("image"); - int status = SafeNativeMethods.Gdip.GdipDrawImageRect(nativeObject, image.NativeObject, rect.X, rect.Y, rect.Width, rect.Height); + int status = SafeNativeMethods.Gdip.GdipDrawImageRect(nativeObject, image.nativeImage, rect.X, rect.Y, rect.Width, rect.Height); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -652,7 +631,7 @@ namespace System.Drawing if (image == null) throw new ArgumentNullException("image"); - int status = SafeNativeMethods.Gdip.GdipDrawImage(nativeObject, image.NativeObject, point.X, point.Y); + int status = SafeNativeMethods.Gdip.GdipDrawImage(nativeObject, image.nativeImage, point.X, point.Y); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -663,7 +642,7 @@ namespace System.Drawing if (destPoints == null) throw new ArgumentNullException("destPoints"); - int status = SafeNativeMethods.Gdip.GdipDrawImagePointsI(nativeObject, image.NativeObject, destPoints, destPoints.Length); + int status = SafeNativeMethods.Gdip.GdipDrawImagePointsI(nativeObject, image.nativeImage, destPoints, destPoints.Length); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -687,7 +666,7 @@ namespace System.Drawing throw new ArgumentNullException("image"); if (destPoints == null) throw new ArgumentNullException("destPoints"); - int status = SafeNativeMethods.Gdip.GdipDrawImagePoints(nativeObject, image.NativeObject, destPoints, destPoints.Length); + int status = SafeNativeMethods.Gdip.GdipDrawImagePoints(nativeObject, image.nativeImage, destPoints, destPoints.Length); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -695,7 +674,7 @@ namespace System.Drawing { if (image == null) throw new ArgumentNullException("image"); - int status = SafeNativeMethods.Gdip.GdipDrawImageI(nativeObject, image.NativeObject, x, y); + int status = SafeNativeMethods.Gdip.GdipDrawImageI(nativeObject, image.nativeImage, x, y); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -703,7 +682,7 @@ namespace System.Drawing { if (image == null) throw new ArgumentNullException("image"); - int status = SafeNativeMethods.Gdip.GdipDrawImage(nativeObject, image.NativeObject, x, y); + int status = SafeNativeMethods.Gdip.GdipDrawImage(nativeObject, image.nativeImage, x, y); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -711,7 +690,7 @@ namespace System.Drawing { if (image == null) throw new ArgumentNullException("image"); - int status = SafeNativeMethods.Gdip.GdipDrawImageRectRectI(nativeObject, image.NativeObject, + int status = SafeNativeMethods.Gdip.GdipDrawImageRectRectI(nativeObject, image.nativeImage, destRect.X, destRect.Y, destRect.Width, destRect.Height, srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, srcUnit, IntPtr.Zero, null, IntPtr.Zero); @@ -722,7 +701,7 @@ namespace System.Drawing { if (image == null) throw new ArgumentNullException("image"); - int status = SafeNativeMethods.Gdip.GdipDrawImageRectRect(nativeObject, image.NativeObject, + int status = SafeNativeMethods.Gdip.GdipDrawImageRectRect(nativeObject, image.nativeImage, destRect.X, destRect.Y, destRect.Width, destRect.Height, srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, srcUnit, IntPtr.Zero, null, IntPtr.Zero); @@ -736,7 +715,7 @@ namespace System.Drawing if (destPoints == null) throw new ArgumentNullException("destPoints"); - int status = SafeNativeMethods.Gdip.GdipDrawImagePointsRectI(nativeObject, image.NativeObject, + int status = SafeNativeMethods.Gdip.GdipDrawImagePointsRectI(nativeObject, image.nativeImage, destPoints, destPoints.Length, srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, srcUnit, IntPtr.Zero, null, IntPtr.Zero); @@ -750,7 +729,7 @@ namespace System.Drawing if (destPoints == null) throw new ArgumentNullException("destPoints"); - int status = SafeNativeMethods.Gdip.GdipDrawImagePointsRect(nativeObject, image.NativeObject, + int status = SafeNativeMethods.Gdip.GdipDrawImagePointsRect(nativeObject, image.nativeImage, destPoints, destPoints.Length, srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, srcUnit, IntPtr.Zero, null, IntPtr.Zero); @@ -764,7 +743,7 @@ namespace System.Drawing throw new ArgumentNullException("image"); if (destPoints == null) throw new ArgumentNullException("destPoints"); - int status = SafeNativeMethods.Gdip.GdipDrawImagePointsRectI(nativeObject, image.NativeObject, + int status = SafeNativeMethods.Gdip.GdipDrawImagePointsRectI(nativeObject, image.nativeImage, destPoints, destPoints.Length, srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, srcUnit, imageAttr != null ? imageAttr.nativeImageAttributes : IntPtr.Zero, null, IntPtr.Zero); @@ -775,7 +754,7 @@ namespace System.Drawing { if (image == null) throw new ArgumentNullException("image"); - int status = SafeNativeMethods.Gdip.GdipDrawImageRect(nativeObject, image.NativeObject, x, y, + int status = SafeNativeMethods.Gdip.GdipDrawImageRect(nativeObject, image.nativeImage, x, y, width, height); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -787,7 +766,7 @@ namespace System.Drawing throw new ArgumentNullException("image"); if (destPoints == null) throw new ArgumentNullException("destPoints"); - int status = SafeNativeMethods.Gdip.GdipDrawImagePointsRect(nativeObject, image.NativeObject, + int status = SafeNativeMethods.Gdip.GdipDrawImagePointsRect(nativeObject, image.nativeImage, destPoints, destPoints.Length, srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, srcUnit, imageAttr != null ? imageAttr.nativeImageAttributes : IntPtr.Zero, null, IntPtr.Zero); @@ -798,7 +777,7 @@ namespace System.Drawing { if (image == null) throw new ArgumentNullException("image"); - int status = SafeNativeMethods.Gdip.GdipDrawImagePointRectI(nativeObject, image.NativeObject, x, y, srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, srcUnit); + int status = SafeNativeMethods.Gdip.GdipDrawImagePointRectI(nativeObject, image.nativeImage, x, y, srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, srcUnit); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -806,7 +785,7 @@ namespace System.Drawing { if (image == null) throw new ArgumentNullException("image"); - int status = SafeNativeMethods.Gdip.GdipDrawImageRectI(nativeObject, image.nativeObject, x, y, width, height); + int status = SafeNativeMethods.Gdip.GdipDrawImageRectI(nativeObject, image.nativeImage, x, y, width, height); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -814,7 +793,7 @@ namespace System.Drawing { if (image == null) throw new ArgumentNullException("image"); - int status = SafeNativeMethods.Gdip.GdipDrawImagePointRect(nativeObject, image.nativeObject, x, y, srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, srcUnit); + int status = SafeNativeMethods.Gdip.GdipDrawImagePointRect(nativeObject, image.nativeImage, x, y, srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, srcUnit); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -824,7 +803,7 @@ namespace System.Drawing throw new ArgumentNullException("image"); if (destPoints == null) throw new ArgumentNullException("destPoints"); - int status = SafeNativeMethods.Gdip.GdipDrawImagePointsRect(nativeObject, image.NativeObject, + int status = SafeNativeMethods.Gdip.GdipDrawImagePointsRect(nativeObject, image.nativeImage, destPoints, destPoints.Length, srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, srcUnit, imageAttr != null ? imageAttr.nativeImageAttributes : IntPtr.Zero, callback, IntPtr.Zero); @@ -838,7 +817,7 @@ namespace System.Drawing if (destPoints == null) throw new ArgumentNullException("destPoints"); - int status = SafeNativeMethods.Gdip.GdipDrawImagePointsRectI(nativeObject, image.NativeObject, + int status = SafeNativeMethods.Gdip.GdipDrawImagePointsRectI(nativeObject, image.nativeImage, destPoints, destPoints.Length, srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, srcUnit, imageAttr != null ? imageAttr.nativeImageAttributes : IntPtr.Zero, callback, IntPtr.Zero); @@ -852,7 +831,7 @@ namespace System.Drawing if (destPoints == null) throw new ArgumentNullException("destPoints"); - int status = SafeNativeMethods.Gdip.GdipDrawImagePointsRectI(nativeObject, image.NativeObject, + int status = SafeNativeMethods.Gdip.GdipDrawImagePointsRectI(nativeObject, image.nativeImage, destPoints, destPoints.Length, srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, srcUnit, imageAttr != null ? imageAttr.nativeImageAttributes : IntPtr.Zero, callback, (IntPtr)callbackData); @@ -863,7 +842,7 @@ namespace System.Drawing { if (image == null) throw new ArgumentNullException("image"); - int status = SafeNativeMethods.Gdip.GdipDrawImageRectRect(nativeObject, image.NativeObject, + int status = SafeNativeMethods.Gdip.GdipDrawImageRectRect(nativeObject, image.nativeImage, destRect.X, destRect.Y, destRect.Width, destRect.Height, srcX, srcY, srcWidth, srcHeight, srcUnit, IntPtr.Zero, null, IntPtr.Zero); @@ -872,7 +851,7 @@ namespace System.Drawing public void DrawImage(Image image, PointF[] destPoints, RectangleF srcRect, GraphicsUnit srcUnit, ImageAttributes imageAttr, DrawImageAbort callback, int callbackData) { - int status = SafeNativeMethods.Gdip.GdipDrawImagePointsRect(nativeObject, image.NativeObject, + int status = SafeNativeMethods.Gdip.GdipDrawImagePointsRect(nativeObject, image.nativeImage, destPoints, destPoints.Length, srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, srcUnit, imageAttr != null ? imageAttr.nativeImageAttributes : IntPtr.Zero, callback, (IntPtr)callbackData); @@ -883,7 +862,7 @@ namespace System.Drawing { if (image == null) throw new ArgumentNullException("image"); - int status = SafeNativeMethods.Gdip.GdipDrawImageRectRectI(nativeObject, image.NativeObject, + int status = SafeNativeMethods.Gdip.GdipDrawImageRectRectI(nativeObject, image.nativeImage, destRect.X, destRect.Y, destRect.Width, destRect.Height, srcX, srcY, srcWidth, srcHeight, srcUnit, IntPtr.Zero, null, IntPtr.Zero); @@ -894,7 +873,7 @@ namespace System.Drawing { if (image == null) throw new ArgumentNullException("image"); - int status = SafeNativeMethods.Gdip.GdipDrawImageRectRect(nativeObject, image.NativeObject, + int status = SafeNativeMethods.Gdip.GdipDrawImageRectRect(nativeObject, image.nativeImage, destRect.X, destRect.Y, destRect.Width, destRect.Height, srcX, srcY, srcWidth, srcHeight, srcUnit, imageAttrs != null ? imageAttrs.nativeImageAttributes : IntPtr.Zero, null, IntPtr.Zero); @@ -905,7 +884,7 @@ namespace System.Drawing { if (image == null) throw new ArgumentNullException("image"); - int status = SafeNativeMethods.Gdip.GdipDrawImageRectRectI(nativeObject, image.NativeObject, + int status = SafeNativeMethods.Gdip.GdipDrawImageRectRectI(nativeObject, image.nativeImage, destRect.X, destRect.Y, destRect.Width, destRect.Height, srcX, srcY, srcWidth, srcHeight, srcUnit, imageAttr != null ? imageAttr.nativeImageAttributes : IntPtr.Zero, null, IntPtr.Zero); @@ -916,7 +895,7 @@ namespace System.Drawing { if (image == null) throw new ArgumentNullException("image"); - int status = SafeNativeMethods.Gdip.GdipDrawImageRectRectI(nativeObject, image.NativeObject, + int status = SafeNativeMethods.Gdip.GdipDrawImageRectRectI(nativeObject, image.nativeImage, destRect.X, destRect.Y, destRect.Width, destRect.Height, srcX, srcY, srcWidth, srcHeight, srcUnit, imageAttr != null ? imageAttr.nativeImageAttributes : IntPtr.Zero, callback, @@ -928,7 +907,7 @@ namespace System.Drawing { if (image == null) throw new ArgumentNullException("image"); - int status = SafeNativeMethods.Gdip.GdipDrawImageRectRect(nativeObject, image.NativeObject, + int status = SafeNativeMethods.Gdip.GdipDrawImageRectRect(nativeObject, image.nativeImage, destRect.X, destRect.Y, destRect.Width, destRect.Height, srcX, srcY, srcWidth, srcHeight, srcUnit, imageAttrs != null ? imageAttrs.nativeImageAttributes : IntPtr.Zero, @@ -940,7 +919,7 @@ namespace System.Drawing { if (image == null) throw new ArgumentNullException("image"); - int status = SafeNativeMethods.Gdip.GdipDrawImageRectRect(nativeObject, image.NativeObject, + int status = SafeNativeMethods.Gdip.GdipDrawImageRectRect(nativeObject, image.nativeImage, destRect.X, destRect.Y, destRect.Width, destRect.Height, srcX, srcY, srcWidth, srcHeight, srcUnit, imageAttrs != null ? imageAttrs.nativeImageAttributes : IntPtr.Zero, callback, callbackData); @@ -951,7 +930,7 @@ namespace System.Drawing { if (image == null) throw new ArgumentNullException("image"); - int status = SafeNativeMethods.Gdip.GdipDrawImageRectRect(nativeObject, image.NativeObject, + int status = SafeNativeMethods.Gdip.GdipDrawImageRectRect(nativeObject, image.nativeImage, destRect.X, destRect.Y, destRect.Width, destRect.Height, srcX, srcY, srcWidth, srcHeight, srcUnit, imageAttrs != null ? imageAttrs.nativeImageAttributes : IntPtr.Zero, callback, callbackData); @@ -1711,18 +1690,7 @@ namespace System.Drawing { IntPtr graphics; - if (SafeNativeMethods.Gdip.UseCocoaDrawable) - { - CocoaContext context = MacSupport.GetCGContextForNSView(hwnd); - SafeNativeMethods.Gdip.GdipCreateFromContext_macosx(context.ctx, context.width, context.height, out graphics); - - Graphics g = new Graphics(graphics); - g.maccontext = context; - - return g; - } - - if (SafeNativeMethods.Gdip.UseCarbonDrawable) + if (!SafeNativeMethods.Gdip.UseX11Drawable) { CarbonContext context = MacSupport.GetCGContextForView(hwnd); SafeNativeMethods.Gdip.GdipCreateFromContext_macosx(context.ctx, context.width, context.height, out graphics); @@ -1732,7 +1700,7 @@ namespace System.Drawing return g; } - if (SafeNativeMethods.Gdip.UseX11Drawable) + else { if (SafeNativeMethods.Gdip.Display == IntPtr.Zero) { @@ -1746,13 +1714,7 @@ namespace System.Drawing } return FromXDrawable(hwnd, SafeNativeMethods.Gdip.Display); - } - - int status = SafeNativeMethods.Gdip.GdipCreateFromHWND(hwnd, out graphics); - SafeNativeMethods.Gdip.CheckStatus(status); - - return new Graphics(graphics); } [EditorBrowsable(EditorBrowsableState.Advanced)] @@ -1769,9 +1731,9 @@ namespace System.Drawing throw new ArgumentNullException("image"); if ((image.PixelFormat & PixelFormat.Indexed) != 0) - throw new Exception("Cannot create Graphics from an indexed bitmap."); + throw new ArgumentException("Cannot create Graphics from an indexed bitmap.", nameof(image)); - int status = SafeNativeMethods.Gdip.GdipGetImageGraphicsContext(image.nativeObject, out graphics); + int status = SafeNativeMethods.Gdip.GdipGetImageGraphicsContext(image.nativeImage, out graphics); SafeNativeMethods.Gdip.CheckStatus(status); Graphics result = new Graphics(graphics); diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Graphics.Windows.cs.REMOVED.git-id b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Graphics.Windows.cs.REMOVED.git-id index aaedd2442f..234ad95c2c 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Graphics.Windows.cs.REMOVED.git-id +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Graphics.Windows.cs.REMOVED.git-id @@ -1 +1 @@ -38f248e18ee07172227790d3080380aea1a033bd \ No newline at end of file +9569502d8fc844172a646cdaf277b236f938f2fc \ No newline at end of file diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Icon.Serializable.Windows.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Icon.Serializable.Windows.cs index 8cd871c9b8..8bcedc46f0 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Icon.Serializable.Windows.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Icon.Serializable.Windows.cs @@ -26,7 +26,6 @@ namespace System.Drawing } } - [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context) { if (_iconData != null) diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Icon.Unix.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Icon.Unix.cs index 26f6f183e3..119dcaef77 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Icon.Unix.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Icon.Unix.cs @@ -650,7 +650,7 @@ namespace System.Drawing // note: this handle doesn't survive the lifespan of the icon instance if (handle == IntPtr.Zero) { - handle = GetInternalBitmap().NativeObject; + handle = GetInternalBitmap().nativeImage; } return handle; } diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Image.Serializable.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Image.Serializable.cs index ffb5f9d14d..96f293ed3f 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Image.Serializable.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Image.Serializable.cs @@ -62,7 +62,6 @@ namespace System.Drawing } } - [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context) { using (MemoryStream stream = new MemoryStream()) diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Image.Unix.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Image.Unix.cs index 30b1ed2a6e..5771f95371 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Image.Unix.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Image.Unix.cs @@ -44,19 +44,17 @@ using System.Reflection; namespace System.Drawing { - [ComVisible(true)] [Serializable] #if !NETCORE [Editor ("System.Drawing.Design.ImageEditor, " + Consts.AssemblySystem_Drawing_Design, typeof (System.Drawing.Design.UITypeEditor))] [TypeConverter (typeof(ImageConverter))] #endif - [ImmutableObject(true)] - public abstract class Image : MarshalByRefObject, IDisposable, ICloneable, ISerializable + public abstract partial class Image { public delegate bool GetThumbnailImageAbort(); private object tag; - internal IntPtr nativeObject = IntPtr.Zero; + internal IntPtr nativeImage = IntPtr.Zero; // constructor internal Image() @@ -78,7 +76,7 @@ namespace System.Drawing if (bytes != null) { MemoryStream ms = new MemoryStream(bytes); - nativeObject = InitFromStream(ms); + nativeImage = InitFromStream(ms); } } } @@ -136,7 +134,7 @@ namespace System.Drawing IntPtr imagePtr; int st; - st = SafeNativeMethods.Gdip.GdipCreateBitmapFromHBITMAP(hbitmap, hpalette, out imagePtr); + st = SafeNativeMethods.Gdip.GdipCreateBitmapFromHBITMAP(new HandleRef(null, hbitmap), new HandleRef(null, hpalette), out imagePtr); SafeNativeMethods.Gdip.CheckStatus(st); return new Bitmap(imagePtr); @@ -315,7 +313,7 @@ namespace System.Drawing { RectangleF source; - int status = SafeNativeMethods.Gdip.GdipGetImageBounds(nativeObject, out source, ref pageUnit); + int status = SafeNativeMethods.Gdip.GdipGetImageBounds(nativeImage, out source, ref pageUnit); SafeNativeMethods.Gdip.CheckStatus(status); return source; @@ -326,7 +324,7 @@ namespace System.Drawing int status; uint sz; - status = SafeNativeMethods.Gdip.GdipGetEncoderParameterListSize(nativeObject, ref encoder, out sz); + status = SafeNativeMethods.Gdip.GdipGetEncoderParameterListSize(nativeImage, ref encoder, out sz); SafeNativeMethods.Gdip.CheckStatus(status); IntPtr rawEPList = Marshal.AllocHGlobal((int)sz); @@ -334,7 +332,7 @@ namespace System.Drawing try { - status = SafeNativeMethods.Gdip.GdipGetEncoderParameterList(nativeObject, ref encoder, sz, rawEPList); + status = SafeNativeMethods.Gdip.GdipGetEncoderParameterList(nativeImage, ref encoder, sz, rawEPList); eps = EncoderParameters.ConvertFromMemory(rawEPList); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -351,7 +349,7 @@ namespace System.Drawing uint count; Guid guid = dimension.Guid; - int status = SafeNativeMethods.Gdip.GdipImageGetFrameCount(nativeObject, ref guid, out count); + int status = SafeNativeMethods.Gdip.GdipImageGetFrameCount(nativeImage, ref guid, out count); SafeNativeMethods.Gdip.CheckStatus(status); return (int)count; @@ -365,7 +363,7 @@ namespace System.Drawing GdipPropertyItem gdipProperty = new GdipPropertyItem(); int status; - status = SafeNativeMethods.Gdip.GdipGetPropertyItemSize(nativeObject, propid, + status = SafeNativeMethods.Gdip.GdipGetPropertyItemSize(nativeImage, propid, out propSize); SafeNativeMethods.Gdip.CheckStatus(status); @@ -373,7 +371,7 @@ namespace System.Drawing property = Marshal.AllocHGlobal(propSize); try { - status = SafeNativeMethods.Gdip.GdipGetPropertyItem(nativeObject, propid, propSize, property); + status = SafeNativeMethods.Gdip.GdipGetPropertyItem(nativeImage, propid, propSize, property); SafeNativeMethods.Gdip.CheckStatus(status); gdipProperty = (GdipPropertyItem)Marshal.PtrToStructure(property, typeof(GdipPropertyItem)); @@ -395,7 +393,7 @@ namespace System.Drawing using (Graphics g = Graphics.FromImage(ThumbNail)) { - int status = SafeNativeMethods.Gdip.GdipDrawImageRectRectI(g.nativeObject, nativeObject, + int status = SafeNativeMethods.Gdip.GdipDrawImageRectRectI(g.nativeObject, nativeImage, 0, 0, thumbWidth, thumbHeight, 0, 0, this.Width, this.Height, GraphicsUnit.Pixel, IntPtr.Zero, null, IntPtr.Zero); @@ -409,13 +407,13 @@ namespace System.Drawing public void RemovePropertyItem(int propid) { - int status = SafeNativeMethods.Gdip.GdipRemovePropertyItem(nativeObject, propid); + int status = SafeNativeMethods.Gdip.GdipRemovePropertyItem(nativeImage, propid); SafeNativeMethods.Gdip.CheckStatus(status); } public void RotateFlip(RotateFlipType rotateFlipType) { - int status = SafeNativeMethods.Gdip.GdipImageRotateFlip(nativeObject, rotateFlipType); + int status = SafeNativeMethods.Gdip.GdipImageRotateFlip(nativeImage, rotateFlipType); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -468,12 +466,12 @@ namespace System.Drawing if (encoderParams == null) { - st = SafeNativeMethods.Gdip.GdipSaveImageToFile(nativeObject, filename, ref guid, IntPtr.Zero); + st = SafeNativeMethods.Gdip.GdipSaveImageToFile(nativeImage, filename, ref guid, IntPtr.Zero); } else { IntPtr nativeEncoderParams = encoderParams.ConvertToMemory(); - st = SafeNativeMethods.Gdip.GdipSaveImageToFile(nativeObject, filename, ref guid, nativeEncoderParams); + st = SafeNativeMethods.Gdip.GdipSaveImageToFile(nativeImage, filename, ref guid, nativeEncoderParams); Marshal.FreeHGlobal(nativeEncoderParams); } @@ -504,7 +502,7 @@ namespace System.Drawing try { GdiPlusStreamHelper sh = new GdiPlusStreamHelper(stream, false); - st = SafeNativeMethods.Gdip.GdipSaveImageToDelegate_linux(nativeObject, sh.GetBytesDelegate, sh.PutBytesDelegate, + st = SafeNativeMethods.Gdip.GdipSaveImageToDelegate_linux(nativeImage, sh.GetBytesDelegate, sh.PutBytesDelegate, sh.SeekDelegate, sh.CloseDelegate, sh.SizeDelegate, ref guid, nativeEncoderParams); } finally @@ -521,7 +519,7 @@ namespace System.Drawing int st; IntPtr nativeEncoderParams = encoderParams.ConvertToMemory(); - st = SafeNativeMethods.Gdip.GdipSaveAdd(nativeObject, nativeEncoderParams); + st = SafeNativeMethods.Gdip.GdipSaveAdd(nativeImage, nativeEncoderParams); Marshal.FreeHGlobal(nativeEncoderParams); SafeNativeMethods.Gdip.CheckStatus(st); } @@ -531,7 +529,7 @@ namespace System.Drawing int st; IntPtr nativeEncoderParams = encoderParams.ConvertToMemory(); - st = SafeNativeMethods.Gdip.GdipSaveAddImage(nativeObject, image.NativeObject, nativeEncoderParams); + st = SafeNativeMethods.Gdip.GdipSaveAddImage(nativeImage, image.nativeImage, nativeEncoderParams); Marshal.FreeHGlobal(nativeEncoderParams); SafeNativeMethods.Gdip.CheckStatus(st); } @@ -539,7 +537,7 @@ namespace System.Drawing public int SelectActiveFrame(FrameDimension dimension, int frameIndex) { Guid guid = dimension.Guid; - int st = SafeNativeMethods.Gdip.GdipImageSelectActiveFrame(nativeObject, ref guid, frameIndex); + int st = SafeNativeMethods.Gdip.GdipImageSelectActiveFrame(nativeImage, ref guid, frameIndex); SafeNativeMethods.Gdip.CheckStatus(st); @@ -566,7 +564,7 @@ namespace System.Drawing unsafe { - int status = SafeNativeMethods.Gdip.GdipSetPropertyItem(nativeObject, &pi); + int status = SafeNativeMethods.Gdip.GdipSetPropertyItem(nativeImage, &pi); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -585,7 +583,7 @@ namespace System.Drawing { int flags; - int status = SafeNativeMethods.Gdip.GdipGetImageFlags(nativeObject, out flags); + int status = SafeNativeMethods.Gdip.GdipGetImageFlags(nativeImage, out flags); SafeNativeMethods.Gdip.CheckStatus(status); return flags; } @@ -597,10 +595,10 @@ namespace System.Drawing get { uint found; - int status = SafeNativeMethods.Gdip.GdipImageGetFrameDimensionsCount(nativeObject, out found); + int status = SafeNativeMethods.Gdip.GdipImageGetFrameDimensionsCount(nativeImage, out found); SafeNativeMethods.Gdip.CheckStatus(status); Guid[] guid = new Guid[found]; - status = SafeNativeMethods.Gdip.GdipImageGetFrameDimensionsList(nativeObject, guid, found); + status = SafeNativeMethods.Gdip.GdipImageGetFrameDimensionsList(nativeImage, guid, found); SafeNativeMethods.Gdip.CheckStatus(status); return guid; } @@ -614,7 +612,7 @@ namespace System.Drawing get { uint height; - int status = SafeNativeMethods.Gdip.GdipGetImageHeight(nativeObject, out height); + int status = SafeNativeMethods.Gdip.GdipGetImageHeight(nativeImage, out height); SafeNativeMethods.Gdip.CheckStatus(status); return (int)height; @@ -627,7 +625,7 @@ namespace System.Drawing { float resolution; - int status = SafeNativeMethods.Gdip.GdipGetImageHorizontalResolution(nativeObject, out resolution); + int status = SafeNativeMethods.Gdip.GdipGetImageHorizontalResolution(nativeImage, out resolution); SafeNativeMethods.Gdip.CheckStatus(status); return resolution; @@ -652,12 +650,12 @@ namespace System.Drawing int bytes; ColorPalette ret = new ColorPalette(); - int st = SafeNativeMethods.Gdip.GdipGetImagePaletteSize(nativeObject, out bytes); + int st = SafeNativeMethods.Gdip.GdipGetImagePaletteSize(nativeImage, out bytes); SafeNativeMethods.Gdip.CheckStatus(st); IntPtr palette_data = Marshal.AllocHGlobal(bytes); try { - st = SafeNativeMethods.Gdip.GdipGetImagePalette(nativeObject, palette_data, bytes); + st = SafeNativeMethods.Gdip.GdipGetImagePalette(nativeImage, palette_data, bytes); SafeNativeMethods.Gdip.CheckStatus(st); ret.ConvertFromMemory(palette_data); return ret; @@ -683,7 +681,7 @@ namespace System.Drawing try { - int st = SafeNativeMethods.Gdip.GdipSetImagePalette(nativeObject, palette_data); + int st = SafeNativeMethods.Gdip.GdipSetImagePalette(nativeImage, palette_data); SafeNativeMethods.Gdip.CheckStatus(st); } @@ -699,7 +697,7 @@ namespace System.Drawing get { float width, height; - int status = SafeNativeMethods.Gdip.GdipGetImageDimension(nativeObject, out width, out height); + int status = SafeNativeMethods.Gdip.GdipGetImageDimension(nativeImage, out width, out height); SafeNativeMethods.Gdip.CheckStatus(status); return new SizeF(width, height); @@ -711,7 +709,7 @@ namespace System.Drawing get { PixelFormat pixFormat; - int status = SafeNativeMethods.Gdip.GdipGetImagePixelFormat(nativeObject, out pixFormat); + int status = SafeNativeMethods.Gdip.GdipGetImagePixelFormat(nativeImage, out pixFormat); SafeNativeMethods.Gdip.CheckStatus(status); return pixFormat; @@ -725,12 +723,12 @@ namespace System.Drawing { uint propNumbers; - int status = SafeNativeMethods.Gdip.GdipGetPropertyCount(nativeObject, + int status = SafeNativeMethods.Gdip.GdipGetPropertyCount(nativeImage, out propNumbers); SafeNativeMethods.Gdip.CheckStatus(status); int[] idList = new int[propNumbers]; - status = SafeNativeMethods.Gdip.GdipGetPropertyIdList(nativeObject, + status = SafeNativeMethods.Gdip.GdipGetPropertyIdList(nativeImage, propNumbers, idList); SafeNativeMethods.Gdip.CheckStatus(status); @@ -749,7 +747,7 @@ namespace System.Drawing GdipPropertyItem gdipProperty = new GdipPropertyItem(); int status; - status = SafeNativeMethods.Gdip.GdipGetPropertySize(nativeObject, out propsSize, out propNums); + status = SafeNativeMethods.Gdip.GdipGetPropertySize(nativeImage, out propsSize, out propNums); SafeNativeMethods.Gdip.CheckStatus(status); items = new PropertyItem[propNums]; @@ -761,7 +759,7 @@ namespace System.Drawing properties = Marshal.AllocHGlobal(propsSize * propNums); try { - status = SafeNativeMethods.Gdip.GdipGetAllPropertyItems(nativeObject, propsSize, + status = SafeNativeMethods.Gdip.GdipGetAllPropertyItems(nativeImage, propsSize, propNums, properties); SafeNativeMethods.Gdip.CheckStatus(status); @@ -789,7 +787,7 @@ namespace System.Drawing get { Guid guid; - int st = SafeNativeMethods.Gdip.GdipGetImageRawFormat(nativeObject, out guid); + int st = SafeNativeMethods.Gdip.GdipGetImageRawFormat(nativeImage, out guid); SafeNativeMethods.Gdip.CheckStatus(st); return new ImageFormat(guid); @@ -821,7 +819,7 @@ namespace System.Drawing { float resolution; - int status = SafeNativeMethods.Gdip.GdipGetImageVerticalResolution(nativeObject, out resolution); + int status = SafeNativeMethods.Gdip.GdipGetImageVerticalResolution(nativeImage, out resolution); SafeNativeMethods.Gdip.CheckStatus(status); return resolution; @@ -836,33 +834,13 @@ namespace System.Drawing get { uint width; - int status = SafeNativeMethods.Gdip.GdipGetImageWidth(nativeObject, out width); + int status = SafeNativeMethods.Gdip.GdipGetImageWidth(nativeImage, out width); SafeNativeMethods.Gdip.CheckStatus(status); return (int)width; } } - internal IntPtr NativeObject - { - get - { - return nativeObject; - } - set - { - nativeObject = value; - } - } - - internal IntPtr nativeImage - { - get - { - return nativeObject; - } - } - public void Dispose() { Dispose(true); @@ -876,11 +854,11 @@ namespace System.Drawing protected virtual void Dispose(bool disposing) { - if (nativeObject != IntPtr.Zero) + if (nativeImage != IntPtr.Zero) { - int status = SafeNativeMethods.Gdip.GdipDisposeImage(nativeObject); - // ... set nativeObject to null before (possibly) throwing an exception - nativeObject = IntPtr.Zero; + int status = SafeNativeMethods.Gdip.GdipDisposeImage(nativeImage); + // ... set nativeImage to null before (possibly) throwing an exception + nativeImage = IntPtr.Zero; SafeNativeMethods.Gdip.CheckStatus(status); } } @@ -888,7 +866,7 @@ namespace System.Drawing public object Clone() { IntPtr newimage = IntPtr.Zero; - int status = SafeNativeMethods.Gdip.GdipCloneImage(NativeObject, out newimage); + int status = SafeNativeMethods.Gdip.GdipCloneImage(nativeImage, out newimage); SafeNativeMethods.Gdip.CheckStatus(status); if (this is Bitmap) diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Image.Windows.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Image.Windows.cs index 80c3821f20..c0b8f1a69c 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Image.Windows.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Image.Windows.cs @@ -13,12 +13,7 @@ using System.Runtime.Serialization; namespace System.Drawing { - /// - /// An abstract base class that provides functionality for 'Bitmap', 'Icon', 'Cursor', and 'Metafile' descended classes. - /// - [ImmutableObject(true)] - [ComVisible(true)] - public abstract partial class Image : MarshalByRefObject, ICloneable, IDisposable, ISerializable + public abstract partial class Image { #if FINALIZATION_WATCH private string allocationSite = Graphics.GetAllocationStack(); @@ -37,9 +32,6 @@ namespace System.Drawing internal IntPtr nativeImage; - // used to work around lack of animated gif encoder... rarely set... - private byte[] _rawData; - //userData : so that user can use TAGS with IMAGES.. private object _userData; @@ -278,83 +270,6 @@ namespace System.Drawing Dispose(false); } - internal static void EnsureSave(Image image, string filename, Stream dataStream) - { - if (image.RawFormat.Equals(ImageFormat.Gif)) - { - bool animatedGif = false; - - Guid[] dimensions = image.FrameDimensionsList; - foreach (Guid guid in dimensions) - { - FrameDimension dimension = new FrameDimension(guid); - if (dimension.Equals(FrameDimension.Time)) - { - animatedGif = image.GetFrameCount(FrameDimension.Time) > 1; - break; - } - } - - - if (animatedGif) - { - try - { - Stream created = null; - long lastPos = 0; - if (dataStream != null) - { - lastPos = dataStream.Position; - dataStream.Position = 0; - } - - try - { - if (dataStream == null) - { - created = dataStream = File.OpenRead(filename); - } - - image._rawData = new byte[(int)dataStream.Length]; - dataStream.Read(image._rawData, 0, (int)dataStream.Length); - } - finally - { - if (created != null) - { - created.Close(); - } - else - { - dataStream.Position = lastPos; - } - } - } - // possible exceptions for reading the filename - catch (UnauthorizedAccessException) - { - } - catch (DirectoryNotFoundException) - { - } - catch (IOException) - { - } - // possible exceptions for setting/getting the position inside dataStream - catch (NotSupportedException) - { - } - catch (ObjectDisposedException) - { - } - // possible exception when reading stuff into dataStream - catch (ArgumentException) - { - } - } - } - } - internal static Image CreateImageObject(IntPtr nativeImage) { Image image; @@ -369,7 +284,7 @@ namespace System.Drawing switch ((ImageType)type) { case ImageType.Bitmap: - image = Bitmap.FromGDIplus(nativeImage); + image = new Bitmap(nativeImage); break; case ImageType.Metafile: @@ -1164,14 +1079,6 @@ namespace System.Drawing } } - internal void SetNativeImage(IntPtr handle) - { - if (handle == IntPtr.Zero) - throw new ArgumentException(SR.Format(SR.NativeHandle0), "handle"); - - nativeImage = handle; - } - /// /// Creates a from a Windows handle. /// @@ -1191,7 +1098,7 @@ namespace System.Drawing if (status != SafeNativeMethods.Gdip.Ok) throw SafeNativeMethods.Gdip.StatusException(status); - return Bitmap.FromGDIplus(bitmap); + return new Bitmap(bitmap); } /// diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Image.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Image.cs new file mode 100644 index 0000000000..457bcdeb58 --- /dev/null +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Image.cs @@ -0,0 +1,107 @@ +// 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.ComponentModel; +using System.Drawing.Imaging; +using System.IO; +using System.Runtime.Serialization; + +namespace System.Drawing +{ + /// + /// An abstract base class that provides functionality for 'Bitmap', 'Icon', 'Cursor', and 'Metafile' descended classes. + /// + [ImmutableObject(true)] + public abstract partial class Image : MarshalByRefObject, IDisposable, ICloneable, ISerializable + { + // used to work around lack of animated gif encoder... rarely set... + private byte[] _rawData; + + internal void SetNativeImage(IntPtr handle) + { + if (handle == IntPtr.Zero) + throw new ArgumentException(SR.Format(SR.NativeHandle0), "handle"); + + nativeImage = handle; + } + + internal static void EnsureSave(Image image, string filename, Stream dataStream) + { + if (image.RawFormat.Equals(ImageFormat.Gif)) + { + bool animatedGif = false; + + Guid[] dimensions = image.FrameDimensionsList; + foreach (Guid guid in dimensions) + { + FrameDimension dimension = new FrameDimension(guid); + if (dimension.Equals(FrameDimension.Time)) + { + animatedGif = image.GetFrameCount(FrameDimension.Time) > 1; + break; + } + } + + + if (animatedGif) + { + try + { + Stream created = null; + long lastPos = 0; + if (dataStream != null) + { + lastPos = dataStream.Position; + dataStream.Position = 0; + } + + try + { + if (dataStream == null) + { + created = dataStream = File.OpenRead(filename); + } + + image._rawData = new byte[(int)dataStream.Length]; + dataStream.Read(image._rawData, 0, (int)dataStream.Length); + } + finally + { + if (created != null) + { + created.Close(); + } + else + { + dataStream.Position = lastPos; + } + } + } + // possible exceptions for reading the filename + catch (UnauthorizedAccessException) + { + } + catch (DirectoryNotFoundException) + { + } + catch (IOException) + { + } + // possible exceptions for setting/getting the position inside dataStream + catch (NotSupportedException) + { + } + catch (ObjectDisposedException) + { + } + // possible exception when reading stuff into dataStream + catch (ArgumentException) + { + } + } + } + } + } +} + diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Unix.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Unix.cs index 9825ac7337..d61256ca0a 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Unix.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Unix.cs @@ -49,18 +49,12 @@ namespace System.Drawing.Imaging // constructors - internal Metafile(IntPtr ptr) - { - nativeObject = ptr; - } + internal Metafile(IntPtr ptr) => SetNativeImage(ptr); // Usually called when cloning images that need to have // not only the handle saved, but also the underlying stream // (when using MS GDI+ and IStream we must ensure the stream stays alive for all the life of the Image) - internal Metafile(IntPtr ptr, Stream stream) - { - nativeObject = ptr; - } + internal Metafile(IntPtr ptr, Stream stream) => SetNativeImage(ptr); public Metafile(Stream stream) { @@ -72,7 +66,7 @@ namespace System.Drawing.Imaging // to get the Stream down to libgdiplus. So, we wrap the stream with a set of delegates. GdiPlusStreamHelper sh = new GdiPlusStreamHelper(stream, false); status = SafeNativeMethods.Gdip.GdipCreateMetafileFromDelegate_linux(sh.GetHeaderDelegate, sh.GetBytesDelegate, - sh.PutBytesDelegate, sh.SeekDelegate, sh.CloseDelegate, sh.SizeDelegate, out nativeObject); + sh.PutBytesDelegate, sh.SeekDelegate, sh.CloseDelegate, sh.SizeDelegate, out nativeImage); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -81,7 +75,7 @@ namespace System.Drawing.Imaging // Called in order to emulate exception behavior from netfx related to invalid file paths. Path.GetFullPath(filename); - int status = SafeNativeMethods.Gdip.GdipCreateMetafileFromFile(filename, out nativeObject); + int status = SafeNativeMethods.Gdip.GdipCreateMetafileFromFile(filename, out nativeImage); if (status == SafeNativeMethods.Gdip.GenericError) throw new ExternalException("Couldn't load specified file."); SafeNativeMethods.Gdip.CheckStatus(status); @@ -89,7 +83,7 @@ namespace System.Drawing.Imaging public Metafile(IntPtr henhmetafile, bool deleteEmf) { - int status = SafeNativeMethods.Gdip.GdipCreateMetafileFromEmf(henhmetafile, deleteEmf, out nativeObject); + int status = SafeNativeMethods.Gdip.GdipCreateMetafileFromEmf(henhmetafile, deleteEmf, out nativeImage); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -110,7 +104,7 @@ namespace System.Drawing.Imaging public Metafile(IntPtr hmetafile, WmfPlaceableFileHeader wmfHeader) { - int status = SafeNativeMethods.Gdip.GdipCreateMetafileFromEmf(hmetafile, false, out nativeObject); + int status = SafeNativeMethods.Gdip.GdipCreateMetafileFromEmf(hmetafile, false, out nativeImage); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -142,7 +136,7 @@ namespace System.Drawing.Imaging public Metafile(IntPtr hmetafile, WmfPlaceableFileHeader wmfHeader, bool deleteWmf) { - int status = SafeNativeMethods.Gdip.GdipCreateMetafileFromEmf(hmetafile, deleteWmf, out nativeObject); + int status = SafeNativeMethods.Gdip.GdipCreateMetafileFromEmf(hmetafile, deleteWmf, out nativeImage); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -220,7 +214,7 @@ namespace System.Drawing.Imaging string desc) { int status = SafeNativeMethods.Gdip.GdipRecordMetafileI(referenceHdc, type, ref frameRect, frameUnit, - desc, out nativeObject); + desc, out nativeImage); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -228,7 +222,7 @@ namespace System.Drawing.Imaging string description) { int status = SafeNativeMethods.Gdip.GdipRecordMetafile(referenceHdc, type, ref frameRect, frameUnit, - description, out nativeObject); + description, out nativeImage); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -275,7 +269,7 @@ namespace System.Drawing.Imaging GdiPlusStreamHelper sh = new GdiPlusStreamHelper(stream, false); status = SafeNativeMethods.Gdip.GdipRecordMetafileFromDelegateI_linux(sh.GetHeaderDelegate, sh.GetBytesDelegate, sh.PutBytesDelegate, sh.SeekDelegate, sh.CloseDelegate, sh.SizeDelegate, referenceHdc, - type, ref frameRect, frameUnit, description, out nativeObject); + type, ref frameRect, frameUnit, description, out nativeImage); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -291,7 +285,7 @@ namespace System.Drawing.Imaging GdiPlusStreamHelper sh = new GdiPlusStreamHelper(stream, false); status = SafeNativeMethods.Gdip.GdipRecordMetafileFromDelegate_linux(sh.GetHeaderDelegate, sh.GetBytesDelegate, sh.PutBytesDelegate, sh.SeekDelegate, sh.CloseDelegate, sh.SizeDelegate, referenceHdc, - type, ref frameRect, frameUnit, description, out nativeObject); + type, ref frameRect, frameUnit, description, out nativeImage); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -302,7 +296,7 @@ namespace System.Drawing.Imaging Path.GetFullPath(fileName); int status = SafeNativeMethods.Gdip.GdipRecordMetafileFileNameI(fileName, referenceHdc, type, ref frameRect, - frameUnit, description, out nativeObject); + frameUnit, description, out nativeImage); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -313,7 +307,7 @@ namespace System.Drawing.Imaging Path.GetFullPath(fileName); int status = SafeNativeMethods.Gdip.GdipRecordMetafileFileName(fileName, referenceHdc, type, ref frameRect, frameUnit, - description, out nativeObject); + description, out nativeImage); SafeNativeMethods.Gdip.CheckStatus(status); } @@ -321,7 +315,7 @@ namespace System.Drawing.Imaging public IntPtr GetHenhmetafile() { - return nativeObject; + return nativeImage; } [MonoLimitation("Metafiles aren't only partially supported by libgdiplus.")] @@ -330,7 +324,7 @@ namespace System.Drawing.Imaging IntPtr header = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MetafileHeader))); try { - int status = SafeNativeMethods.Gdip.GdipGetMetafileHeaderFromMetafile(nativeObject, header); + int status = SafeNativeMethods.Gdip.GdipGetMetafileHeaderFromMetafile(nativeImage, header); SafeNativeMethods.Gdip.CheckStatus(status); return new MetafileHeader(header); } @@ -419,7 +413,7 @@ namespace System.Drawing.Imaging [MonoLimitation("Metafiles aren't only partially supported by libgdiplus.")] public void PlayRecord(EmfPlusRecordType recordType, int flags, int dataSize, byte[] data) { - int status = SafeNativeMethods.Gdip.GdipPlayMetafileRecord(nativeObject, recordType, flags, dataSize, data); + int status = SafeNativeMethods.Gdip.GdipPlayMetafileRecord(nativeImage, recordType, flags, dataSize, data); SafeNativeMethods.Gdip.CheckStatus(status); } } diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.Core.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.Core.cs new file mode 100644 index 0000000000..9cbbf954d2 --- /dev/null +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.Core.cs @@ -0,0 +1,28 @@ +// 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.Runtime.Serialization; + +/* + * This file is not intended to be used by Mono. + * Instead InvalidPrinterException.Serializable.cs should be used. + */ + +namespace System.Drawing.Printing +{ + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + partial class InvalidPrinterException + { + protected InvalidPrinterException(SerializationInfo info, StreamingContext context) : base(info, context) + { + // Ignoring not deserializable input + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("settings", null); + } + } +} diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.NotSerializable.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.NotSerializable.cs deleted file mode 100644 index 3df9fff78d..0000000000 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.NotSerializable.cs +++ /dev/null @@ -1,16 +0,0 @@ -// 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.Runtime.Serialization; - -namespace System.Drawing.Printing -{ - partial class InvalidPrinterException - { - protected InvalidPrinterException(SerializationInfo info, StreamingContext context) : base(info, context) - { - throw new PlatformNotSupportedException(); - } - } -} diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.Serializable.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.Serializable.cs index 32dbd6076b..3770134619 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.Serializable.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.Serializable.cs @@ -3,11 +3,9 @@ // See the LICENSE file in the project root for more information. using System.Runtime.Serialization; -using System.Security.Permissions; namespace System.Drawing.Printing { - [Serializable] partial class InvalidPrinterException { protected InvalidPrinterException(SerializationInfo info, StreamingContext context) : base(info, context) @@ -15,15 +13,10 @@ namespace System.Drawing.Printing _settings = (PrinterSettings)info.GetValue("settings", typeof(PrinterSettings)); } - [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)] public override void GetObjectData(SerializationInfo info, StreamingContext context) { - if (info == null) - { - throw new ArgumentNullException("info"); - } - info.AddValue("settings", _settings); base.GetObjectData(info, context); + info.AddValue("settings", _settings); } } } diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.cs index 4fbd5bfcad..db6f96e400 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.cs @@ -9,6 +9,7 @@ namespace System.Drawing.Printing /// /// Represents the exception that is thrown when trying to access a printer using invalid printer settings. /// + [Serializable] public partial class InvalidPrinterException : SystemException { private PrinterSettings _settings; diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/PrintController.Windows.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/PrintController.Windows.cs index 9e9c2099e4..5604038039 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/PrintController.Windows.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/PrintController.Windows.cs @@ -22,7 +22,6 @@ namespace System.Drawing.Printing /// /// Represents a SafeHandle for a Printer's Device Mode struct handle (DEVMODE) /// - [SecurityCritical] internal sealed class SafeDeviceModeHandle : SafeHandle { // This constructor is used by the P/Invoke marshaling layer @@ -45,7 +44,6 @@ namespace System.Drawing.Printing // The boolean returned should be true for success and false if the runtime // should fire a SafeHandleCriticalFailure MDA (CustomerDebugProbe) if that // MDA is enabled. - [SecurityCritical] protected override bool ReleaseHandle() { if (!IsInvalid) diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/TriState.Serializable.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/TriState.Serializable.cs index f580df4936..acb125bf85 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/TriState.Serializable.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/TriState.Serializable.cs @@ -1,11 +1,11 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. namespace System.Drawing.Printing { [Serializable] - partial struct TriState + readonly partial struct TriState { } } diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/TriState.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/TriState.cs index 54a19eb2bf..f16ba8f72f 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/TriState.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/Printing/TriState.cs @@ -4,9 +4,9 @@ namespace System.Drawing.Printing { - internal partial struct TriState + internal readonly partial struct TriState { - private byte _value; // 0 is "default", not false + private readonly byte _value; // 0 is "default", not false public static readonly TriState Default = new TriState(0); public static readonly TriState False = new TriState(1); diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/SystemIcons.Windows.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/SystemIcons.Windows.cs index 13b4c742c3..bebd51ff5b 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/SystemIcons.Windows.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/SystemIcons.Windows.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; namespace System.Drawing { diff --git a/external/corefx/src/System.Drawing.Common/src/System/Drawing/UnsafeNativeMethods.cs b/external/corefx/src/System.Drawing.Common/src/System/Drawing/UnsafeNativeMethods.cs index 7d3987ce04..cfb47682de 100644 --- a/external/corefx/src/System.Drawing.Common/src/System/Drawing/UnsafeNativeMethods.cs +++ b/external/corefx/src/System.Drawing.Common/src/System/Drawing/UnsafeNativeMethods.cs @@ -8,7 +8,6 @@ using System.Security; namespace System.Drawing { - [SuppressUnmanagedCodeSecurity] internal class UnsafeNativeMethods { [DllImport(ExternDll.Kernel32, SetLastError = true, ExactSpelling = true, EntryPoint = "RtlMoveMemory", CharSet = CharSet.Auto)] diff --git a/external/corefx/src/System.Drawing.Common/src/misc/GDI/SafeNativeMethods.cs b/external/corefx/src/System.Drawing.Common/src/misc/GDI/SafeNativeMethods.cs index 35a2602c50..17428e0b8c 100644 --- a/external/corefx/src/System.Drawing.Common/src/misc/GDI/SafeNativeMethods.cs +++ b/external/corefx/src/System.Drawing.Common/src/misc/GDI/SafeNativeMethods.cs @@ -11,7 +11,6 @@ namespace System.Drawing.Internal /// This is an extract of the System.Drawing IntNativeMethods in the CommonUI tree. /// This is done to be able to compile the GDI code in both assemblies System.Drawing and System.Windows.Forms. /// - [SuppressUnmanagedCodeSecurity] internal static partial class IntSafeNativeMethods { public sealed class CommonHandles diff --git a/external/corefx/src/System.Drawing.Common/src/misc/GDI/UnsafeNativeMethods.cs b/external/corefx/src/System.Drawing.Common/src/misc/GDI/UnsafeNativeMethods.cs index a28b3251e2..eccc0ca0ba 100644 --- a/external/corefx/src/System.Drawing.Common/src/misc/GDI/UnsafeNativeMethods.cs +++ b/external/corefx/src/System.Drawing.Common/src/misc/GDI/UnsafeNativeMethods.cs @@ -8,7 +8,6 @@ using System.Security; namespace System.Drawing.Internal { - [SuppressUnmanagedCodeSecurity] internal static partial class IntUnsafeNativeMethods { [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, EntryPoint = "GetDC", CharSet = CharSet.Auto)] diff --git a/external/corefx/src/System.Drawing.Common/tests/BitmapTests.cs b/external/corefx/src/System.Drawing.Common/tests/BitmapTests.cs index a0488f4004..fee3341b55 100644 --- a/external/corefx/src/System.Drawing.Common/tests/BitmapTests.cs +++ b/external/corefx/src/System.Drawing.Common/tests/BitmapTests.cs @@ -150,8 +150,7 @@ namespace System.Drawing.Tests } } } - - [ActiveIssue(20884, TestPlatforms.AnyUnix)] + [ConditionalFact(Helpers.GdiplusIsAvailable)] public void Ctor_NullStream_ThrowsArgumentNullException() { diff --git a/external/corefx/src/System.Drawing.Common/tests/ColorTranslatorTests.cs b/external/corefx/src/System.Drawing.Common/tests/ColorTranslatorTests.cs index 32d5968a47..38384375ea 100644 --- a/external/corefx/src/System.Drawing.Common/tests/ColorTranslatorTests.cs +++ b/external/corefx/src/System.Drawing.Common/tests/ColorTranslatorTests.cs @@ -198,20 +198,30 @@ namespace System.Drawing.Tests } [Theory] - [InlineData("'", typeof(Exception))] - [InlineData("'\"", typeof(Exception))] - [InlineData("\"'", typeof(Exception))] - [InlineData("#", typeof(Exception))] + [InlineData("'")] + [InlineData("'\"")] + [InlineData("\"'")] + [InlineData("#")] + [InlineData(" #G12 ")] + [InlineData(" #G12345 ")] + [InlineData("#FFFFFFFFF")] + [InlineData("0x")] + [InlineData("0xFFFFFFFFF")] + [InlineData("0xG12")] + [InlineData("&h")] + [InlineData("&hG12")] + public void FromHtml_Invalid_Throws(string htmlColor) + { + using (new ThreadCultureChange(CultureInfo.InvariantCulture)) + { + Exception exception = AssertExtensions.Throws(() => ColorTranslator.FromHtml(htmlColor)); + if (exception is ArgumentException argumentException) + Assert.Equal("htmlColor", argumentException.ParamName); + } + } + [InlineData("#G12", typeof(FormatException))] [InlineData("#G12345", typeof(FormatException))] - [InlineData(" #G12 ", typeof(Exception))] - [InlineData(" #G12345 ", typeof(Exception))] - [InlineData("#FFFFFFFFF", typeof(Exception))] - [InlineData("0x", typeof(Exception))] - [InlineData("0xFFFFFFFFF", typeof(Exception))] - [InlineData("0xG12", typeof(Exception))] - [InlineData("&h", typeof(Exception))] - [InlineData("&hG12", typeof(Exception))] [InlineData("1,2", typeof(ArgumentException))] [InlineData("1,2,3,4,5", typeof(ArgumentException))] [InlineData("-1,2,3", typeof(ArgumentException))] @@ -220,7 +230,8 @@ namespace System.Drawing.Tests [InlineData("1,256,3", typeof(ArgumentException))] [InlineData("1,2,-1", typeof(ArgumentException))] [InlineData("1,2,256", typeof(ArgumentException))] - public void FromHtml_Invalid_Throws(string htmlColor, Type exception) + + public void FromHtml_Invalid_Throw(string htmlColor, Type exception) { using (new ThreadCultureChange(CultureInfo.InvariantCulture)) { diff --git a/external/corefx/src/System.Drawing.Common/tests/GraphicsTests.cs.REMOVED.git-id b/external/corefx/src/System.Drawing.Common/tests/GraphicsTests.cs.REMOVED.git-id index 8f1111934b..4045eaba84 100644 --- a/external/corefx/src/System.Drawing.Common/tests/GraphicsTests.cs.REMOVED.git-id +++ b/external/corefx/src/System.Drawing.Common/tests/GraphicsTests.cs.REMOVED.git-id @@ -1 +1 @@ -fa8c6c7e5ceaf3a5078cd782101d21a97c8026b1 \ No newline at end of file +7d7441c1f86b5d545ee040e62385ef7ae21bf8aa \ No newline at end of file diff --git a/external/corefx/src/System.Drawing.Common/tests/Helpers.cs b/external/corefx/src/System.Drawing.Common/tests/Helpers.cs index a1e577c41b..01a94a1759 100644 --- a/external/corefx/src/System.Drawing.Common/tests/Helpers.cs +++ b/external/corefx/src/System.Drawing.Common/tests/Helpers.cs @@ -47,8 +47,8 @@ namespace System.Drawing public static bool GetRecentGdiPlusIsAvailable2() { - // CentOS 7, RHEL 7 and Ubuntu 14.04, as well as Fedora 25 and OpenSUSE 4.22 are running outdated versions of libgdiplus - if (PlatformDetection.IsCentos7 || PlatformDetection.IsRedHat || PlatformDetection.IsUbuntu1404 || PlatformDetection.IsFedora || PlatformDetection.IsOpenSUSE) + // RedHat and Ubuntu 14.04, as well as Fedora 25 and OpenSUSE 4.22 are running outdated versions of libgdiplus + if (PlatformDetection.IsRedHatFamily || PlatformDetection.IsUbuntu1404 || PlatformDetection.IsFedora || PlatformDetection.IsOpenSUSE) { return false; } @@ -58,7 +58,7 @@ namespace System.Drawing public static bool GetGdiPlusIsAvailableNotRedhat73() { - if (PlatformDetection.IsRedHat) + if (PlatformDetection.IsRedHatFamily) { return false; } @@ -78,8 +78,8 @@ namespace System.Drawing public static bool GetRecentGdiPlusIsAvailable() { - // CentOS 7, RHEL 7 and Ubuntu 14.04 are running outdated versions of libgdiplus - if (PlatformDetection.IsCentos7 || PlatformDetection.IsRedHat || PlatformDetection.IsUbuntu1404) + // RedHat and Ubuntu 14.04 are running outdated versions of libgdiplus + if (PlatformDetection.IsRedHatFamily || PlatformDetection.IsUbuntu1404) { return false; } diff --git a/external/corefx/src/System.Drawing.Common/tests/Imaging/EncoderParameterTests.cs b/external/corefx/src/System.Drawing.Common/tests/Imaging/EncoderParameterTests.cs index 368a06b312..b59d2b309e 100644 --- a/external/corefx/src/System.Drawing.Common/tests/Imaging/EncoderParameterTests.cs +++ b/external/corefx/src/System.Drawing.Common/tests/Imaging/EncoderParameterTests.cs @@ -318,6 +318,9 @@ namespace System.Drawing.Imaging.Tests [InlineData(int.MinValue)] public void Ctor_Encoder_NegativeNumberOfValues_Type_Value_OutOfMemoryException(int numberOfValues) { + if (numberOfValues == -1 && PlatformDetection.IsUbuntu1710) // [ActiveIssue(24274)] + return; + IntPtr anyValue = IntPtr.Zero; EncoderParameterValueType anyTypw = EncoderParameterValueType.ValueTypeAscii; Assert.Throws(() => new EncoderParameter(s_anyEncoder, numberOfValues, anyTypw, anyValue)); diff --git a/external/corefx/src/System.Drawing.Common/tests/System.Drawing.Common.Tests.csproj b/external/corefx/src/System.Drawing.Common/tests/System.Drawing.Common.Tests.csproj index 6a24e7f28d..cc2bf1d3a5 100644 --- a/external/corefx/src/System.Drawing.Common/tests/System.Drawing.Common.Tests.csproj +++ b/external/corefx/src/System.Drawing.Common/tests/System.Drawing.Common.Tests.csproj @@ -5,6 +5,10 @@ {4B93E684-0630-45F4-8F63-6C7788C9892F} true + + + + @@ -109,4 +113,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Drawing.Common/tests/mono/System.Drawing/GraphicsTests.cs.REMOVED.git-id b/external/corefx/src/System.Drawing.Common/tests/mono/System.Drawing/GraphicsTests.cs.REMOVED.git-id index 0bdb783c77..cafa10aae3 100644 --- a/external/corefx/src/System.Drawing.Common/tests/mono/System.Drawing/GraphicsTests.cs.REMOVED.git-id +++ b/external/corefx/src/System.Drawing.Common/tests/mono/System.Drawing/GraphicsTests.cs.REMOVED.git-id @@ -1 +1 @@ -a87fb83b6b538180f1ce3085e60e2841c430dc73 \ No newline at end of file +e78c1ff3725e560877e053e4de94c729622f4eef \ No newline at end of file diff --git a/external/corefx/src/System.Drawing.Primitives/ref/System.Drawing.Primitives.cs b/external/corefx/src/System.Drawing.Primitives/ref/System.Drawing.Primitives.cs index 3d90a080bf..75fae53c5c 100644 --- a/external/corefx/src/System.Drawing.Primitives/ref/System.Drawing.Primitives.cs +++ b/external/corefx/src/System.Drawing.Primitives/ref/System.Drawing.Primitives.cs @@ -7,9 +7,9 @@ namespace System.Drawing { - [System.SerializableAttribute] - public partial struct Color : System.IEquatable + public readonly partial struct Color : System.IEquatable { + private readonly object _dummy; public static readonly System.Drawing.Color Empty; public byte A { get { throw null; } } public static System.Drawing.Color AliceBlue { get { throw null; } } @@ -179,205 +179,6 @@ namespace System.Drawing public System.Drawing.KnownColor ToKnownColor() { throw null; } public override string ToString() { throw null; } } - public partial struct Point : System.IEquatable - { - public static readonly System.Drawing.Point Empty; - public Point(System.Drawing.Size sz) { throw null; } - public Point(int dw) { throw null; } - public Point(int x, int y) { throw null; } - [System.ComponentModel.Browsable(false)] - public bool IsEmpty { get { throw null; } } - public int X { get { throw null; } set { } } - public int Y { get { throw null; } set { } } - public static System.Drawing.Point Add(System.Drawing.Point pt, System.Drawing.Size sz) { throw null; } - public static System.Drawing.Point Ceiling(System.Drawing.PointF value) { throw null; } - public bool Equals(System.Drawing.Point other) { throw null; } - public override bool Equals(object obj) { throw null; } - public override int GetHashCode() { throw null; } - public void Offset(System.Drawing.Point p) { } - public void Offset(int dx, int dy) { } - public static System.Drawing.Point operator +(System.Drawing.Point pt, System.Drawing.Size sz) { throw null; } - public static bool operator ==(System.Drawing.Point left, System.Drawing.Point right) { throw null; } - public static explicit operator System.Drawing.Size (System.Drawing.Point p) { throw null; } - public static implicit operator System.Drawing.PointF (System.Drawing.Point p) { throw null; } - public static bool operator !=(System.Drawing.Point left, System.Drawing.Point right) { throw null; } - public static System.Drawing.Point operator -(System.Drawing.Point pt, System.Drawing.Size sz) { throw null; } - public static System.Drawing.Point Round(System.Drawing.PointF value) { throw null; } - public static System.Drawing.Point Subtract(System.Drawing.Point pt, System.Drawing.Size sz) { throw null; } - public override string ToString() { throw null; } - public static System.Drawing.Point Truncate(System.Drawing.PointF value) { throw null; } - } - public partial struct PointF : System.IEquatable - { - public static readonly System.Drawing.PointF Empty; - public PointF(float x, float y) { throw null; } - [System.ComponentModel.Browsable(false)] - public bool IsEmpty { get { throw null; } } - public float X { get { throw null; } set { } } - public float Y { get { throw null; } set { } } - public static System.Drawing.PointF Add(System.Drawing.PointF pt, System.Drawing.Size sz) { throw null; } - public static System.Drawing.PointF Add(System.Drawing.PointF pt, System.Drawing.SizeF sz) { throw null; } - public bool Equals(System.Drawing.PointF other) { throw null; } - public override bool Equals(object obj) { throw null; } - public override int GetHashCode() { throw null; } - public static System.Drawing.PointF operator +(System.Drawing.PointF pt, System.Drawing.Size sz) { throw null; } - public static System.Drawing.PointF operator +(System.Drawing.PointF pt, System.Drawing.SizeF sz) { throw null; } - public static bool operator ==(System.Drawing.PointF left, System.Drawing.PointF right) { throw null; } - public static bool operator !=(System.Drawing.PointF left, System.Drawing.PointF right) { throw null; } - public static System.Drawing.PointF operator -(System.Drawing.PointF pt, System.Drawing.Size sz) { throw null; } - public static System.Drawing.PointF operator -(System.Drawing.PointF pt, System.Drawing.SizeF sz) { throw null; } - public static System.Drawing.PointF Subtract(System.Drawing.PointF pt, System.Drawing.Size sz) { throw null; } - public static System.Drawing.PointF Subtract(System.Drawing.PointF pt, System.Drawing.SizeF sz) { throw null; } - public override string ToString() { throw null; } - } - public partial struct Rectangle : System.IEquatable - { - public static readonly System.Drawing.Rectangle Empty; - public Rectangle(System.Drawing.Point location, System.Drawing.Size size) { throw null; } - public Rectangle(int x, int y, int width, int height) { throw null; } - [System.ComponentModel.Browsable(false)] - public int Bottom { get { throw null; } } - public int Height { get { throw null; } set { } } - [System.ComponentModel.Browsable(false)] - public bool IsEmpty { get { throw null; } } - [System.ComponentModel.Browsable(false)] - public int Left { get { throw null; } } - [System.ComponentModel.Browsable(false)] - public System.Drawing.Point Location { get { throw null; } set { } } - [System.ComponentModel.Browsable(false)] - public int Right { get { throw null; } } - [System.ComponentModel.Browsable(false)] - public System.Drawing.Size Size { get { throw null; } set { } } - [System.ComponentModel.Browsable(false)] - public int Top { get { throw null; } } - public int Width { get { throw null; } set { } } - public int X { get { throw null; } set { } } - public int Y { get { throw null; } set { } } - public static System.Drawing.Rectangle Ceiling(System.Drawing.RectangleF value) { throw null; } - public bool Contains(System.Drawing.Point pt) { throw null; } - public bool Contains(System.Drawing.Rectangle rect) { throw null; } - public bool Contains(int x, int y) { throw null; } - public bool Equals(System.Drawing.Rectangle other) { throw null; } - public override bool Equals(object obj) { throw null; } - public static System.Drawing.Rectangle FromLTRB(int left, int top, int right, int bottom) { throw null; } - public override int GetHashCode() { throw null; } - public static System.Drawing.Rectangle Inflate(System.Drawing.Rectangle rect, int x, int y) { throw null; } - public void Inflate(System.Drawing.Size size) { } - public void Inflate(int width, int height) { } - public void Intersect(System.Drawing.Rectangle rect) { } - public static System.Drawing.Rectangle Intersect(System.Drawing.Rectangle a, System.Drawing.Rectangle b) { throw null; } - public bool IntersectsWith(System.Drawing.Rectangle rect) { throw null; } - public void Offset(System.Drawing.Point pos) { } - public void Offset(int x, int y) { } - public static bool operator ==(System.Drawing.Rectangle left, System.Drawing.Rectangle right) { throw null; } - public static bool operator !=(System.Drawing.Rectangle left, System.Drawing.Rectangle right) { throw null; } - public static System.Drawing.Rectangle Round(System.Drawing.RectangleF value) { throw null; } - public override string ToString() { throw null; } - public static System.Drawing.Rectangle Truncate(System.Drawing.RectangleF value) { throw null; } - public static System.Drawing.Rectangle Union(System.Drawing.Rectangle a, System.Drawing.Rectangle b) { throw null; } - } - public partial struct RectangleF : System.IEquatable - { - public static readonly System.Drawing.RectangleF Empty; - public RectangleF(System.Drawing.PointF location, System.Drawing.SizeF size) { throw null; } - public RectangleF(float x, float y, float width, float height) { throw null; } - [System.ComponentModel.Browsable(false)] - public float Bottom { get { throw null; } } - public float Height { get { throw null; } set { } } - [System.ComponentModel.Browsable(false)] - public bool IsEmpty { get { throw null; } } - [System.ComponentModel.Browsable(false)] - public float Left { get { throw null; } } - [System.ComponentModel.Browsable(false)] - public System.Drawing.PointF Location { get { throw null; } set { } } - [System.ComponentModel.Browsable(false)] - public float Right { get { throw null; } } - [System.ComponentModel.Browsable(false)] - public System.Drawing.SizeF Size { get { throw null; } set { } } - [System.ComponentModel.Browsable(false)] - public float Top { get { throw null; } } - public float Width { get { throw null; } set { } } - public float X { get { throw null; } set { } } - public float Y { get { throw null; } set { } } - public bool Contains(System.Drawing.PointF pt) { throw null; } - public bool Contains(System.Drawing.RectangleF rect) { throw null; } - public bool Contains(float x, float y) { throw null; } - public bool Equals(System.Drawing.RectangleF other) { throw null; } - public override bool Equals(object obj) { throw null; } - public static System.Drawing.RectangleF FromLTRB(float left, float top, float right, float bottom) { throw null; } - public override int GetHashCode() { throw null; } - public static System.Drawing.RectangleF Inflate(System.Drawing.RectangleF rect, float x, float y) { throw null; } - public void Inflate(System.Drawing.SizeF size) { } - public void Inflate(float x, float y) { } - public void Intersect(System.Drawing.RectangleF rect) { } - public static System.Drawing.RectangleF Intersect(System.Drawing.RectangleF a, System.Drawing.RectangleF b) { throw null; } - public bool IntersectsWith(System.Drawing.RectangleF rect) { throw null; } - public void Offset(System.Drawing.PointF pos) { } - public void Offset(float x, float y) { } - public static bool operator ==(System.Drawing.RectangleF left, System.Drawing.RectangleF right) { throw null; } - public static implicit operator System.Drawing.RectangleF (System.Drawing.Rectangle r) { throw null; } - public static bool operator !=(System.Drawing.RectangleF left, System.Drawing.RectangleF right) { throw null; } - public override string ToString() { throw null; } - public static System.Drawing.RectangleF Union(System.Drawing.RectangleF a, System.Drawing.RectangleF b) { throw null; } - } - public partial struct Size : System.IEquatable - { - public static readonly System.Drawing.Size Empty; - public Size(System.Drawing.Point pt) { throw null; } - public Size(int width, int height) { throw null; } - public int Height { get { throw null; } set { } } - [System.ComponentModel.Browsable(false)] - public bool IsEmpty { get { throw null; } } - public int Width { get { throw null; } set { } } - public static System.Drawing.Size Add(System.Drawing.Size sz1, System.Drawing.Size sz2) { throw null; } - public static System.Drawing.Size Ceiling(System.Drawing.SizeF value) { throw null; } - public bool Equals(System.Drawing.Size other) { throw null; } - public override bool Equals(object obj) { throw null; } - public override int GetHashCode() { throw null; } - public static System.Drawing.Size operator +(System.Drawing.Size sz1, System.Drawing.Size sz2) { throw null; } - public static System.Drawing.Size operator *(int left, System.Drawing.Size right) { throw null; } - public static System.Drawing.Size operator *(System.Drawing.Size left, int right) { throw null; } - public static System.Drawing.SizeF operator *(float left, System.Drawing.Size right) { throw null; } - public static System.Drawing.SizeF operator *(System.Drawing.Size left, float right) { throw null; } - public static System.Drawing.Size operator /(System.Drawing.Size left, int right) { throw null; } - public static System.Drawing.SizeF operator /(System.Drawing.Size left, float right) { throw null; } - public static bool operator ==(System.Drawing.Size sz1, System.Drawing.Size sz2) { throw null; } - public static explicit operator System.Drawing.Point (System.Drawing.Size size) { throw null; } - public static implicit operator System.Drawing.SizeF (System.Drawing.Size p) { throw null; } - public static bool operator !=(System.Drawing.Size sz1, System.Drawing.Size sz2) { throw null; } - public static System.Drawing.Size operator -(System.Drawing.Size sz1, System.Drawing.Size sz2) { throw null; } - public static System.Drawing.Size Round(System.Drawing.SizeF value) { throw null; } - public static System.Drawing.Size Subtract(System.Drawing.Size sz1, System.Drawing.Size sz2) { throw null; } - public override string ToString() { throw null; } - public static System.Drawing.Size Truncate(System.Drawing.SizeF value) { throw null; } - } - public partial struct SizeF : System.IEquatable - { - public static readonly System.Drawing.SizeF Empty; - public SizeF(System.Drawing.PointF pt) { throw null; } - public SizeF(System.Drawing.SizeF size) { throw null; } - public SizeF(float width, float height) { throw null; } - public float Height { get { throw null; } set { } } - [System.ComponentModel.Browsable(false)] - public bool IsEmpty { get { throw null; } } - public float Width { get { throw null; } set { } } - public static System.Drawing.SizeF Add(System.Drawing.SizeF sz1, System.Drawing.SizeF sz2) { throw null; } - public bool Equals(System.Drawing.SizeF other) { throw null; } - public override bool Equals(object obj) { throw null; } - public override int GetHashCode() { throw null; } - public static System.Drawing.SizeF operator +(System.Drawing.SizeF sz1, System.Drawing.SizeF sz2) { throw null; } - public static bool operator ==(System.Drawing.SizeF sz1, System.Drawing.SizeF sz2) { throw null; } - public static explicit operator System.Drawing.PointF (System.Drawing.SizeF size) { throw null; } - public static bool operator !=(System.Drawing.SizeF sz1, System.Drawing.SizeF sz2) { throw null; } - public static System.Drawing.SizeF operator -(System.Drawing.SizeF sz1, System.Drawing.SizeF sz2) { throw null; } - public static System.Drawing.SizeF Subtract(System.Drawing.SizeF sz1, System.Drawing.SizeF sz2) { throw null; } - public static System.Drawing.SizeF operator *(float left, System.Drawing.SizeF right) { throw null; } - public static System.Drawing.SizeF operator *(System.Drawing.SizeF left, float right) { throw null; } - public static System.Drawing.SizeF operator /(System.Drawing.SizeF left, float right) { throw null; } - public System.Drawing.PointF ToPointF() { throw null; } - public System.Drawing.Size ToSize() { throw null; } - public override string ToString() { throw null; } - } public enum KnownColor { ActiveBorder = 1, @@ -555,4 +356,209 @@ namespace System.Drawing Yellow = 166, YellowGreen = 167, } + public partial struct Point : System.IEquatable + { + private int _dummy; + public static readonly System.Drawing.Point Empty; + public Point(System.Drawing.Size sz) { throw null; } + public Point(int dw) { throw null; } + public Point(int x, int y) { throw null; } + [System.ComponentModel.BrowsableAttribute(false)] + public bool IsEmpty { get { throw null; } } + public int X { get { throw null; } set { } } + public int Y { get { throw null; } set { } } + public static System.Drawing.Point Add(System.Drawing.Point pt, System.Drawing.Size sz) { throw null; } + public static System.Drawing.Point Ceiling(System.Drawing.PointF value) { throw null; } + public bool Equals(System.Drawing.Point other) { throw null; } + public override bool Equals(object obj) { throw null; } + public override int GetHashCode() { throw null; } + public void Offset(System.Drawing.Point p) { } + public void Offset(int dx, int dy) { } + public static System.Drawing.Point operator +(System.Drawing.Point pt, System.Drawing.Size sz) { throw null; } + public static bool operator ==(System.Drawing.Point left, System.Drawing.Point right) { throw null; } + public static explicit operator System.Drawing.Size (System.Drawing.Point p) { throw null; } + public static implicit operator System.Drawing.PointF (System.Drawing.Point p) { throw null; } + public static bool operator !=(System.Drawing.Point left, System.Drawing.Point right) { throw null; } + public static System.Drawing.Point operator -(System.Drawing.Point pt, System.Drawing.Size sz) { throw null; } + public static System.Drawing.Point Round(System.Drawing.PointF value) { throw null; } + public static System.Drawing.Point Subtract(System.Drawing.Point pt, System.Drawing.Size sz) { throw null; } + public override string ToString() { throw null; } + public static System.Drawing.Point Truncate(System.Drawing.PointF value) { throw null; } + } + public partial struct PointF : System.IEquatable + { + private int _dummy; + public static readonly System.Drawing.PointF Empty; + public PointF(float x, float y) { throw null; } + [System.ComponentModel.BrowsableAttribute(false)] + public bool IsEmpty { get { throw null; } } + public float X { get { throw null; } set { } } + public float Y { get { throw null; } set { } } + public static System.Drawing.PointF Add(System.Drawing.PointF pt, System.Drawing.Size sz) { throw null; } + public static System.Drawing.PointF Add(System.Drawing.PointF pt, System.Drawing.SizeF sz) { throw null; } + public bool Equals(System.Drawing.PointF other) { throw null; } + public override bool Equals(object obj) { throw null; } + public override int GetHashCode() { throw null; } + public static System.Drawing.PointF operator +(System.Drawing.PointF pt, System.Drawing.Size sz) { throw null; } + public static System.Drawing.PointF operator +(System.Drawing.PointF pt, System.Drawing.SizeF sz) { throw null; } + public static bool operator ==(System.Drawing.PointF left, System.Drawing.PointF right) { throw null; } + public static bool operator !=(System.Drawing.PointF left, System.Drawing.PointF right) { throw null; } + public static System.Drawing.PointF operator -(System.Drawing.PointF pt, System.Drawing.Size sz) { throw null; } + public static System.Drawing.PointF operator -(System.Drawing.PointF pt, System.Drawing.SizeF sz) { throw null; } + public static System.Drawing.PointF Subtract(System.Drawing.PointF pt, System.Drawing.Size sz) { throw null; } + public static System.Drawing.PointF Subtract(System.Drawing.PointF pt, System.Drawing.SizeF sz) { throw null; } + public override string ToString() { throw null; } + } + public partial struct Rectangle : System.IEquatable + { + private int _dummy; + public static readonly System.Drawing.Rectangle Empty; + public Rectangle(System.Drawing.Point location, System.Drawing.Size size) { throw null; } + public Rectangle(int x, int y, int width, int height) { throw null; } + [System.ComponentModel.BrowsableAttribute(false)] + public int Bottom { get { throw null; } } + public int Height { get { throw null; } set { } } + [System.ComponentModel.BrowsableAttribute(false)] + public bool IsEmpty { get { throw null; } } + [System.ComponentModel.BrowsableAttribute(false)] + public int Left { get { throw null; } } + [System.ComponentModel.BrowsableAttribute(false)] + public System.Drawing.Point Location { get { throw null; } set { } } + [System.ComponentModel.BrowsableAttribute(false)] + public int Right { get { throw null; } } + [System.ComponentModel.BrowsableAttribute(false)] + public System.Drawing.Size Size { get { throw null; } set { } } + [System.ComponentModel.BrowsableAttribute(false)] + public int Top { get { throw null; } } + public int Width { get { throw null; } set { } } + public int X { get { throw null; } set { } } + public int Y { get { throw null; } set { } } + public static System.Drawing.Rectangle Ceiling(System.Drawing.RectangleF value) { throw null; } + public bool Contains(System.Drawing.Point pt) { throw null; } + public bool Contains(System.Drawing.Rectangle rect) { throw null; } + public bool Contains(int x, int y) { throw null; } + public bool Equals(System.Drawing.Rectangle other) { throw null; } + public override bool Equals(object obj) { throw null; } + public static System.Drawing.Rectangle FromLTRB(int left, int top, int right, int bottom) { throw null; } + public override int GetHashCode() { throw null; } + public static System.Drawing.Rectangle Inflate(System.Drawing.Rectangle rect, int x, int y) { throw null; } + public void Inflate(System.Drawing.Size size) { } + public void Inflate(int width, int height) { } + public void Intersect(System.Drawing.Rectangle rect) { } + public static System.Drawing.Rectangle Intersect(System.Drawing.Rectangle a, System.Drawing.Rectangle b) { throw null; } + public bool IntersectsWith(System.Drawing.Rectangle rect) { throw null; } + public void Offset(System.Drawing.Point pos) { } + public void Offset(int x, int y) { } + public static bool operator ==(System.Drawing.Rectangle left, System.Drawing.Rectangle right) { throw null; } + public static bool operator !=(System.Drawing.Rectangle left, System.Drawing.Rectangle right) { throw null; } + public static System.Drawing.Rectangle Round(System.Drawing.RectangleF value) { throw null; } + public override string ToString() { throw null; } + public static System.Drawing.Rectangle Truncate(System.Drawing.RectangleF value) { throw null; } + public static System.Drawing.Rectangle Union(System.Drawing.Rectangle a, System.Drawing.Rectangle b) { throw null; } + } + public partial struct RectangleF : System.IEquatable + { + private int _dummy; + public static readonly System.Drawing.RectangleF Empty; + public RectangleF(System.Drawing.PointF location, System.Drawing.SizeF size) { throw null; } + public RectangleF(float x, float y, float width, float height) { throw null; } + [System.ComponentModel.BrowsableAttribute(false)] + public float Bottom { get { throw null; } } + public float Height { get { throw null; } set { } } + [System.ComponentModel.BrowsableAttribute(false)] + public bool IsEmpty { get { throw null; } } + [System.ComponentModel.BrowsableAttribute(false)] + public float Left { get { throw null; } } + [System.ComponentModel.BrowsableAttribute(false)] + public System.Drawing.PointF Location { get { throw null; } set { } } + [System.ComponentModel.BrowsableAttribute(false)] + public float Right { get { throw null; } } + [System.ComponentModel.BrowsableAttribute(false)] + public System.Drawing.SizeF Size { get { throw null; } set { } } + [System.ComponentModel.BrowsableAttribute(false)] + public float Top { get { throw null; } } + public float Width { get { throw null; } set { } } + public float X { get { throw null; } set { } } + public float Y { get { throw null; } set { } } + public bool Contains(System.Drawing.PointF pt) { throw null; } + public bool Contains(System.Drawing.RectangleF rect) { throw null; } + public bool Contains(float x, float y) { throw null; } + public bool Equals(System.Drawing.RectangleF other) { throw null; } + public override bool Equals(object obj) { throw null; } + public static System.Drawing.RectangleF FromLTRB(float left, float top, float right, float bottom) { throw null; } + public override int GetHashCode() { throw null; } + public static System.Drawing.RectangleF Inflate(System.Drawing.RectangleF rect, float x, float y) { throw null; } + public void Inflate(System.Drawing.SizeF size) { } + public void Inflate(float x, float y) { } + public void Intersect(System.Drawing.RectangleF rect) { } + public static System.Drawing.RectangleF Intersect(System.Drawing.RectangleF a, System.Drawing.RectangleF b) { throw null; } + public bool IntersectsWith(System.Drawing.RectangleF rect) { throw null; } + public void Offset(System.Drawing.PointF pos) { } + public void Offset(float x, float y) { } + public static bool operator ==(System.Drawing.RectangleF left, System.Drawing.RectangleF right) { throw null; } + public static implicit operator System.Drawing.RectangleF (System.Drawing.Rectangle r) { throw null; } + public static bool operator !=(System.Drawing.RectangleF left, System.Drawing.RectangleF right) { throw null; } + public override string ToString() { throw null; } + public static System.Drawing.RectangleF Union(System.Drawing.RectangleF a, System.Drawing.RectangleF b) { throw null; } + } + public partial struct Size : System.IEquatable + { + private int _dummy; + public static readonly System.Drawing.Size Empty; + public Size(System.Drawing.Point pt) { throw null; } + public Size(int width, int height) { throw null; } + public int Height { get { throw null; } set { } } + [System.ComponentModel.BrowsableAttribute(false)] + public bool IsEmpty { get { throw null; } } + public int Width { get { throw null; } set { } } + public static System.Drawing.Size Add(System.Drawing.Size sz1, System.Drawing.Size sz2) { throw null; } + public static System.Drawing.Size Ceiling(System.Drawing.SizeF value) { throw null; } + public bool Equals(System.Drawing.Size other) { throw null; } + public override bool Equals(object obj) { throw null; } + public override int GetHashCode() { throw null; } + public static System.Drawing.Size operator +(System.Drawing.Size sz1, System.Drawing.Size sz2) { throw null; } + public static System.Drawing.Size operator /(System.Drawing.Size left, int right) { throw null; } + public static System.Drawing.SizeF operator /(System.Drawing.Size left, float right) { throw null; } + public static bool operator ==(System.Drawing.Size sz1, System.Drawing.Size sz2) { throw null; } + public static explicit operator System.Drawing.Point (System.Drawing.Size size) { throw null; } + public static implicit operator System.Drawing.SizeF (System.Drawing.Size p) { throw null; } + public static bool operator !=(System.Drawing.Size sz1, System.Drawing.Size sz2) { throw null; } + public static System.Drawing.Size operator *(System.Drawing.Size left, int right) { throw null; } + public static System.Drawing.SizeF operator *(System.Drawing.Size left, float right) { throw null; } + public static System.Drawing.Size operator *(int left, System.Drawing.Size right) { throw null; } + public static System.Drawing.SizeF operator *(float left, System.Drawing.Size right) { throw null; } + public static System.Drawing.Size operator -(System.Drawing.Size sz1, System.Drawing.Size sz2) { throw null; } + public static System.Drawing.Size Round(System.Drawing.SizeF value) { throw null; } + public static System.Drawing.Size Subtract(System.Drawing.Size sz1, System.Drawing.Size sz2) { throw null; } + public override string ToString() { throw null; } + public static System.Drawing.Size Truncate(System.Drawing.SizeF value) { throw null; } + } + public partial struct SizeF : System.IEquatable + { + private int _dummy; + public static readonly System.Drawing.SizeF Empty; + public SizeF(System.Drawing.PointF pt) { throw null; } + public SizeF(System.Drawing.SizeF size) { throw null; } + public SizeF(float width, float height) { throw null; } + public float Height { get { throw null; } set { } } + [System.ComponentModel.BrowsableAttribute(false)] + public bool IsEmpty { get { throw null; } } + public float Width { get { throw null; } set { } } + public static System.Drawing.SizeF Add(System.Drawing.SizeF sz1, System.Drawing.SizeF sz2) { throw null; } + public bool Equals(System.Drawing.SizeF other) { throw null; } + public override bool Equals(object obj) { throw null; } + public override int GetHashCode() { throw null; } + public static System.Drawing.SizeF operator +(System.Drawing.SizeF sz1, System.Drawing.SizeF sz2) { throw null; } + public static System.Drawing.SizeF operator /(System.Drawing.SizeF left, float right) { throw null; } + public static bool operator ==(System.Drawing.SizeF sz1, System.Drawing.SizeF sz2) { throw null; } + public static explicit operator System.Drawing.PointF (System.Drawing.SizeF size) { throw null; } + public static bool operator !=(System.Drawing.SizeF sz1, System.Drawing.SizeF sz2) { throw null; } + public static System.Drawing.SizeF operator *(System.Drawing.SizeF left, float right) { throw null; } + public static System.Drawing.SizeF operator *(float left, System.Drawing.SizeF right) { throw null; } + public static System.Drawing.SizeF operator -(System.Drawing.SizeF sz1, System.Drawing.SizeF sz2) { throw null; } + public static System.Drawing.SizeF Subtract(System.Drawing.SizeF sz1, System.Drawing.SizeF sz2) { throw null; } + public System.Drawing.PointF ToPointF() { throw null; } + public System.Drawing.Size ToSize() { throw null; } + public override string ToString() { throw null; } + } } diff --git a/external/corefx/src/System.Drawing.Primitives/src/System.Drawing.Primitives.csproj b/external/corefx/src/System.Drawing.Primitives/src/System.Drawing.Primitives.csproj index 75f440ee09..fdc487ae3b 100644 --- a/external/corefx/src/System.Drawing.Primitives/src/System.Drawing.Primitives.csproj +++ b/external/corefx/src/System.Drawing.Primitives/src/System.Drawing.Primitives.csproj @@ -18,7 +18,6 @@ - @@ -64,4 +63,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.Drawing.Primitives/src/System/Drawing/Color.cs b/external/corefx/src/System.Drawing.Primitives/src/System/Drawing/Color.cs index a57c7d2c6b..93eea5f67a 100644 --- a/external/corefx/src/System.Drawing.Primitives/src/System/Drawing/Color.cs +++ b/external/corefx/src/System.Drawing.Primitives/src/System/Drawing/Color.cs @@ -13,7 +13,7 @@ namespace System.Drawing #if !MONO [System.Runtime.CompilerServices.TypeForwardedFrom("System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] #endif - public struct Color : IEquatable + public readonly struct Color : IEquatable { public static readonly Color Empty = new Color(); diff --git a/external/corefx/src/System.Drawing.Primitives/src/System/Drawing/Rectangle.cs b/external/corefx/src/System.Drawing.Primitives/src/System/Drawing/Rectangle.cs index 14558dcf50..5af12cbad2 100644 --- a/external/corefx/src/System.Drawing.Primitives/src/System/Drawing/Rectangle.cs +++ b/external/corefx/src/System.Drawing.Primitives/src/System/Drawing/Rectangle.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.ComponentModel; -using System.Diagnostics.Contracts; using System.Numerics.Hashing; namespace System.Drawing @@ -258,7 +257,6 @@ namespace System.Drawing /// rectangular region defined by this . /// /// - [Pure] public bool Contains(int x, int y) => X <= x && x < X + Width && Y <= y && y < Y + Height; /// @@ -267,7 +265,6 @@ namespace System.Drawing /// rectangular region defined by this . /// /// - [Pure] public bool Contains(Point pt) => Contains(pt.X, pt.Y); /// @@ -277,7 +274,6 @@ namespace System.Drawing /// this . /// /// - [Pure] public bool Contains(Rectangle rect) => (X <= rect.X) && (rect.X + rect.Width <= X + Width) && (Y <= rect.Y) && (rect.Y + rect.Height <= Y + Height); @@ -355,7 +351,6 @@ namespace System.Drawing /// /// Determines if this rectangle intersects with rect. /// - [Pure] public bool IntersectsWith(Rectangle rect) => (rect.X < X + Width) && (X < rect.X + rect.Width) && (rect.Y < Y + Height) && (Y < rect.Y + rect.Height); @@ -366,7 +361,6 @@ namespace System.Drawing /// b. /// /// - [Pure] public static Rectangle Union(Rectangle a, Rectangle b) { int x1 = Math.Min(a.X, b.X); diff --git a/external/corefx/src/System.Drawing.Primitives/src/System/Drawing/RectangleF.cs b/external/corefx/src/System.Drawing.Primitives/src/System/Drawing/RectangleF.cs index 135db559de..5cf91b8c7e 100644 --- a/external/corefx/src/System.Drawing.Primitives/src/System/Drawing/RectangleF.cs +++ b/external/corefx/src/System.Drawing.Primitives/src/System/Drawing/RectangleF.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.ComponentModel; -using System.Diagnostics.Contracts; using System.Numerics.Hashing; namespace System.Drawing @@ -226,7 +225,6 @@ namespace System.Drawing /// rectangular region defined by this . /// /// - [Pure] public bool Contains(float x, float y) => X <= x && x < X + Width && Y <= y && y < Y + Height; /// @@ -235,7 +233,6 @@ namespace System.Drawing /// rectangular region defined by this . /// /// - [Pure] public bool Contains(PointF pt) => Contains(pt.X, pt.Y); /// @@ -245,7 +242,6 @@ namespace System.Drawing /// this . /// /// - [Pure] public bool Contains(RectangleF rect) => (X <= rect.X) && (rect.X + rect.Width <= X + Width) && (Y <= rect.Y) && (rect.Y + rect.Height <= Y + Height); @@ -305,7 +301,6 @@ namespace System.Drawing /// Creates a rectangle that represents the intersection between a and /// b. If there is no intersection, null is returned. /// - [Pure] public static RectangleF Intersect(RectangleF a, RectangleF b) { float x1 = Math.Max(a.X, b.X); @@ -324,7 +319,6 @@ namespace System.Drawing /// /// Determines if this rectangle intersects with rect. /// - [Pure] public bool IntersectsWith(RectangleF rect) => (rect.X < X + Width) && (X < rect.X + rect.Width) && (rect.Y < Y + Height) && (Y < rect.Y + rect.Height); @@ -332,7 +326,6 @@ namespace System.Drawing /// Creates a rectangle that represents the union between a and /// b. /// - [Pure] public static RectangleF Union(RectangleF a, RectangleF b) { float x1 = Math.Min(a.X, b.X); diff --git a/external/corefx/src/System.Drawing.Primitives/tests/System.Drawing.Primitives.Tests.csproj b/external/corefx/src/System.Drawing.Primitives/tests/System.Drawing.Primitives.Tests.csproj index 3897e8dbac..3349020d86 100644 --- a/external/corefx/src/System.Drawing.Primitives/tests/System.Drawing.Primitives.Tests.csproj +++ b/external/corefx/src/System.Drawing.Primitives/tests/System.Drawing.Primitives.Tests.csproj @@ -4,7 +4,6 @@ {CF54638C-A382-4A78-9AD6-2304CEEFEB01} - @@ -29,4 +28,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Dynamic.Runtime/src/System.Dynamic.Runtime.csproj b/external/corefx/src/System.Dynamic.Runtime/src/System.Dynamic.Runtime.csproj index 2360412d2a..ffe9574110 100644 --- a/external/corefx/src/System.Dynamic.Runtime/src/System.Dynamic.Runtime.csproj +++ b/external/corefx/src/System.Dynamic.Runtime/src/System.Dynamic.Runtime.csproj @@ -7,7 +7,6 @@ System.Dynamic.Runtime true - @@ -17,4 +16,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Dynamic.Runtime/tests/Dynamic.Unsafe/Conformance.dynamic.unsafe.PointerOperator.cs b/external/corefx/src/System.Dynamic.Runtime/tests/Dynamic.Unsafe/Conformance.dynamic.unsafe.PointerOperator.cs index e551bb585b..f056c4f19e 100644 --- a/external/corefx/src/System.Dynamic.Runtime/tests/Dynamic.Unsafe/Conformance.dynamic.unsafe.PointerOperator.cs +++ b/external/corefx/src/System.Dynamic.Runtime/tests/Dynamic.Unsafe/Conformance.dynamic.unsafe.PointerOperator.cs @@ -1,3 +1,6 @@ +// 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. #if CAP_TypeOfPointer diff --git a/external/corefx/src/System.Dynamic.Runtime/tests/System.Dynamic.Runtime.Tests.csproj b/external/corefx/src/System.Dynamic.Runtime/tests/System.Dynamic.Runtime.Tests.csproj index 090d2d11b2..0eec4a0ede 100644 --- a/external/corefx/src/System.Dynamic.Runtime/tests/System.Dynamic.Runtime.Tests.csproj +++ b/external/corefx/src/System.Dynamic.Runtime/tests/System.Dynamic.Runtime.Tests.csproj @@ -6,7 +6,6 @@ true 67,168,219,414,162,184,458,464,78,169,114,693,108,1981,649,109,1066,3021,3026,3002,3014,3022,660,661,429 - @@ -161,4 +160,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Globalization.Calendars/tests/System.Globalization.Calendars.Tests.csproj b/external/corefx/src/System.Globalization.Calendars/tests/System.Globalization.Calendars.Tests.csproj index 43113c8308..19e3c30ace 100644 --- a/external/corefx/src/System.Globalization.Calendars/tests/System.Globalization.Calendars.Tests.csproj +++ b/external/corefx/src/System.Globalization.Calendars/tests/System.Globalization.Calendars.Tests.csproj @@ -4,7 +4,6 @@ {66BE33BB-790D-4D0C-9336-E073001CBD15} - @@ -114,4 +113,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Globalization.Extensions/ref/System.Globalization.Extensions.netfx.cs b/external/corefx/src/System.Globalization.Extensions/ref/System.Globalization.Extensions.netfx.cs index efe5b240be..bdb68e8d21 100644 --- a/external/corefx/src/System.Globalization.Extensions/ref/System.Globalization.Extensions.netfx.cs +++ b/external/corefx/src/System.Globalization.Extensions/ref/System.Globalization.Extensions.netfx.cs @@ -10,10 +10,8 @@ namespace System public static class StringNormalizationExtensions { public static bool IsNormalized(this string value) { return default(bool); } - [System.Security.SecurityCritical] public static bool IsNormalized(this string value, System.Text.NormalizationForm normalizationForm) { return default(bool); } public static String Normalize(this string value) { return default(string); } - [System.Security.SecurityCritical] public static String Normalize(this string value, System.Text.NormalizationForm normalizationForm) { return default(string); } } } @@ -24,4 +22,4 @@ namespace System.Globalization { public static StringComparer GetStringComparer(this CompareInfo compareInfo, CompareOptions options) { return default(StringComparer); } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Globalization.Extensions/src/System.Globalization.Extensions.csproj b/external/corefx/src/System.Globalization.Extensions/src/System.Globalization.Extensions.csproj index 310c8ff612..f8bf84548b 100644 --- a/external/corefx/src/System.Globalization.Extensions/src/System.Globalization.Extensions.csproj +++ b/external/corefx/src/System.Globalization.Extensions/src/System.Globalization.Extensions.csproj @@ -22,7 +22,6 @@ - diff --git a/external/corefx/src/System.Globalization.Extensions/src/System/Globalization/Extensions.cs b/external/corefx/src/System.Globalization.Extensions/src/System/Globalization/Extensions.cs index 74717cbd33..bf48d06e06 100644 --- a/external/corefx/src/System.Globalization.Extensions/src/System/Globalization/Extensions.cs +++ b/external/corefx/src/System.Globalization.Extensions/src/System/Globalization/Extensions.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics; -using System.Diagnostics.Contracts; namespace System.Globalization { @@ -75,7 +74,6 @@ namespace System.Globalization { throw new ArgumentNullException(nameof(obj)); } - Contract.EndContractBlock(); // StringSort used in compare operation and not with the hashing return _compareInfo.GetHashCode(obj, _options & (~CompareOptions.StringSort)); diff --git a/external/corefx/src/System.Globalization.Extensions/src/System/StringNormalizationExtensions.netfx.cs b/external/corefx/src/System.Globalization.Extensions/src/System/StringNormalizationExtensions.netfx.cs index 761decb3e3..02cfa407ae 100644 --- a/external/corefx/src/System.Globalization.Extensions/src/System/StringNormalizationExtensions.netfx.cs +++ b/external/corefx/src/System.Globalization.Extensions/src/System/StringNormalizationExtensions.netfx.cs @@ -11,7 +11,6 @@ namespace System return value.IsNormalized(); } - [System.Security.SecurityCritical] public static bool IsNormalized(this string value, System.Text.NormalizationForm normalizationForm) { return value.IsNormalized(normalizationForm); @@ -22,10 +21,9 @@ namespace System return value.Normalize(); } - [System.Security.SecurityCritical] public static String Normalize(this string value, System.Text.NormalizationForm normalizationForm) { return value.Normalize(normalizationForm); } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Globalization.Extensions/tests/System.Globalization.Extensions.Tests.csproj b/external/corefx/src/System.Globalization.Extensions/tests/System.Globalization.Extensions.Tests.csproj index 4edfc8e6b3..31af3985b5 100644 --- a/external/corefx/src/System.Globalization.Extensions/tests/System.Globalization.Extensions.Tests.csproj +++ b/external/corefx/src/System.Globalization.Extensions/tests/System.Globalization.Extensions.Tests.csproj @@ -4,7 +4,6 @@ {BC439554-4AB4-4C94-8E28-C00EDE4FD1C7} - @@ -36,4 +35,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Globalization/System.Globalization.sln b/external/corefx/src/System.Globalization/System.Globalization.sln index 689edaa14b..8b0c64a0bd 100644 --- a/external/corefx/src/System.Globalization/System.Globalization.sln +++ b/external/corefx/src/System.Globalization/System.Globalization.sln @@ -44,10 +44,10 @@ Global {9A8926D9-1D4C-4069-8965-A626F6CA8C29}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {9A8926D9-1D4C-4069-8965-A626F6CA8C29}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU {9A8926D9-1D4C-4069-8965-A626F6CA8C29}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU - {0BA6851E-0E75-453D-9D2A-CEB94E4DE975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0BA6851E-0E75-453D-9D2A-CEB94E4DE975}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0BA6851E-0E75-453D-9D2A-CEB94E4DE975}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0BA6851E-0E75-453D-9D2A-CEB94E4DE975}.Release|Any CPU.Build.0 = Release|Any CPU + {0BA6851E-0E75-453D-9D2A-CEB94E4DE975}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {0BA6851E-0E75-453D-9D2A-CEB94E4DE975}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {0BA6851E-0E75-453D-9D2A-CEB94E4DE975}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {0BA6851E-0E75-453D-9D2A-CEB94E4DE975}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU {2395E8CA-73CB-40DF-BE40-A60BC189B737}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU {2395E8CA-73CB-40DF-BE40-A60BC189B737}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU {2395E8CA-73CB-40DF-BE40-A60BC189B737}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU diff --git a/external/corefx/src/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoData.cs b/external/corefx/src/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoData.cs index 94f0f000fe..978d5f4fd5 100644 --- a/external/corefx/src/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoData.cs +++ b/external/corefx/src/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoData.cs @@ -30,7 +30,7 @@ namespace System.Globalization.Tests public static string[] FrFRDayNames() { #if !uap - if (PlatformDetection.IsOSX && PlatformDetection.OSXKernelVersion < new Version(16, 0)) + if (PlatformDetection.IsOSX && PlatformDetection.OSXVersion < new Version(10, 12)) { return new string[] { "Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi" }; } @@ -41,7 +41,7 @@ namespace System.Globalization.Tests public static string[] FrFRAbbreviatedDayNames() { #if !uap - if (PlatformDetection.IsOSX && PlatformDetection.OSXKernelVersion < new Version(16, 0)) + if (PlatformDetection.IsOSX && PlatformDetection.OSXVersion < new Version(10, 12)) { return new string[] { "Dim.", "Lun.", "Mar.", "Mer.", "Jeu.", "Ven.", "Sam." }; } diff --git a/external/corefx/src/System.Globalization/tests/Invariant/Invariant.Tests.csproj b/external/corefx/src/System.Globalization/tests/Invariant/Invariant.Tests.csproj index f0b15cc29e..0df3f587ec 100644 --- a/external/corefx/src/System.Globalization/tests/Invariant/Invariant.Tests.csproj +++ b/external/corefx/src/System.Globalization/tests/Invariant/Invariant.Tests.csproj @@ -4,7 +4,6 @@ {9A8926D9-1D4C-4069-8965-A626F6CA8C29} - @@ -14,12 +13,11 @@ --> true - PreserveNewest - + \ No newline at end of file diff --git a/external/corefx/src/System.Globalization/tests/Performance/System.Globalization.Performance.Tests.csproj b/external/corefx/src/System.Globalization/tests/Performance/System.Globalization.Performance.Tests.csproj index 89f05503a6..91ad2bafd6 100644 --- a/external/corefx/src/System.Globalization/tests/Performance/System.Globalization.Performance.Tests.csproj +++ b/external/corefx/src/System.Globalization/tests/Performance/System.Globalization.Performance.Tests.csproj @@ -5,9 +5,8 @@ true {0BA6851E-0E75-453D-9D2A-CEB94E4DE975} - - - + + diff --git a/external/corefx/src/System.Globalization/tests/System.Globalization.Tests.csproj b/external/corefx/src/System.Globalization/tests/System.Globalization.Tests.csproj index 5edd084b28..c2d018ce07 100644 --- a/external/corefx/src/System.Globalization/tests/System.Globalization.Tests.csproj +++ b/external/corefx/src/System.Globalization/tests/System.Globalization.Tests.csproj @@ -6,7 +6,6 @@ {484C92C6-6D2C-45BC-A5E2-4A12BA228E1E} $(DefineConstants);uap - @@ -139,7 +138,6 @@ RemoteExecutorConsoleApp - CharUnicodeInfo\UnicodeData8.0.txt diff --git a/external/corefx/src/System.IO.Compression.Brotli/System.IO.Compression.Brotli.sln b/external/corefx/src/System.IO.Compression.Brotli/System.IO.Compression.Brotli.sln new file mode 100644 index 0000000000..3ed36c84f9 --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/System.IO.Compression.Brotli.sln @@ -0,0 +1,63 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27019.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.IO.Compression.Brotli.Tests", "tests\System.IO.Compression.Brotli.Tests.csproj", "{BC2E1649-291D-412E-9529-EDDA94FA7AD6}" + ProjectSection(ProjectDependencies) = postProject + {5471BFE8-8071-466F-838E-5ADAA779E742} = {5471BFE8-8071-466F-838E-5ADAA779E742} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.IO.Compression.Brotli.Performance.Tests", "tests\Performance\System.IO.Compression.Brotli.Performance.Tests.csproj", "{1341F8C8-637A-49A1-BE0F-13867A634929}" + ProjectSection(ProjectDependencies) = postProject + {5471BFE8-8071-466F-838E-5ADAA779E742} = {5471BFE8-8071-466F-838E-5ADAA779E742} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.IO.Compression.Brotli", "src\System.IO.Compression.Brotli.csproj", "{5471BFE8-8071-466F-838E-5ADAA779E742}" + ProjectSection(ProjectDependencies) = postProject + {4ADD9456-A929-4254-B8A2-16FC628ABFDA} = {4ADD9456-A929-4254-B8A2-16FC628ABFDA} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.IO.Compression.Brotli", "ref\System.IO.Compression.Brotli.csproj", "{4ADD9456-A929-4254-B8A2-16FC628ABFDA}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{1A2F9F4A-A032-433E-B914-ADD5992BB178}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E107E9C1-E893-4E87-987E-04EF0DCEAEFD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{2E666815-2EDB-464B-9DF6-380BF4789AD4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BC2E1649-291D-412E-9529-EDDA94FA7AD6}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {BC2E1649-291D-412E-9529-EDDA94FA7AD6}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {BC2E1649-291D-412E-9529-EDDA94FA7AD6}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {BC2E1649-291D-412E-9529-EDDA94FA7AD6}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU + {1341F8C8-637A-49A1-BE0F-13867A634929}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {1341F8C8-637A-49A1-BE0F-13867A634929}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {1341F8C8-637A-49A1-BE0F-13867A634929}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {1341F8C8-637A-49A1-BE0F-13867A634929}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU + {5471BFE8-8071-466F-838E-5ADAA779E742}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {5471BFE8-8071-466F-838E-5ADAA779E742}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {5471BFE8-8071-466F-838E-5ADAA779E742}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {5471BFE8-8071-466F-838E-5ADAA779E742}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU + {4ADD9456-A929-4254-B8A2-16FC628ABFDA}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {4ADD9456-A929-4254-B8A2-16FC628ABFDA}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {4ADD9456-A929-4254-B8A2-16FC628ABFDA}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {4ADD9456-A929-4254-B8A2-16FC628ABFDA}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {BC2E1649-291D-412E-9529-EDDA94FA7AD6} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} + {1341F8C8-637A-49A1-BE0F-13867A634929} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} + {5471BFE8-8071-466F-838E-5ADAA779E742} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD} + {4ADD9456-A929-4254-B8A2-16FC628ABFDA} = {2E666815-2EDB-464B-9DF6-380BF4789AD4} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {75F1E532-877F-4DA2-A21D-C8C20EBAD777} + EndGlobalSection +EndGlobal diff --git a/external/corefx/src/System.IO.Compression.Brotli/dir.props b/external/corefx/src/System.IO.Compression.Brotli/dir.props new file mode 100644 index 0000000000..4d0d97a320 --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/dir.props @@ -0,0 +1,10 @@ + + + + + 4.2.1.0 + ECMA + true + true + + \ No newline at end of file diff --git a/external/corefx/src/System.IO.Compression.Brotli/ref/Configurations.props b/external/corefx/src/System.IO.Compression.Brotli/ref/Configurations.props new file mode 100644 index 0000000000..2845c11c54 --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/ref/Configurations.props @@ -0,0 +1,8 @@ + + + + + netcoreapp; + + + \ No newline at end of file diff --git a/external/corefx/src/System.IO.Compression.Brotli/ref/System.IO.Compression.Brotli.cs b/external/corefx/src/System.IO.Compression.Brotli/ref/System.IO.Compression.Brotli.cs new file mode 100644 index 0000000000..4c4b0b407e --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/ref/System.IO.Compression.Brotli.cs @@ -0,0 +1,51 @@ +// 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. +// ------------------------------------------------------------------------------ +// Changes to this file must follow the http://aka.ms/api-review process. +// ------------------------------------------------------------------------------ + +namespace System.IO.Compression +{ + public partial struct BrotliDecoder : System.IDisposable + { + public System.Buffers.OperationStatus Decompress(System.ReadOnlySpan source, System.Span destination, out int bytesConsumed, out int bytesWritten) { bytesConsumed = default(int); bytesWritten = default(int); throw null; } + public static bool TryDecompress(System.ReadOnlySpan source, System.Span destination, out int bytesWritten) { bytesWritten = default(int); throw null; } + public void Dispose() { } + } + public partial struct BrotliEncoder : System.IDisposable + { + public BrotliEncoder(int quality, int window) { } + public System.Buffers.OperationStatus Compress(System.ReadOnlySpan source, System.Span destination, out int bytesConsumed, out int bytesWritten, bool isFinalBlock) { bytesConsumed = default(int); bytesWritten = default(int); throw null; } + public System.Buffers.OperationStatus Flush(System.Span destination, out int bytesWritten) { bytesWritten = default(int); throw null; } + public void Dispose() { } + public static bool TryCompress(System.ReadOnlySpan source, System.Span destination, out int bytesWritten) { bytesWritten = default(int); throw null; } + public static bool TryCompress(System.ReadOnlySpan source, System.Span destination, out int bytesWritten, int quality, int window) { bytesWritten = default(int); throw null; } + public static int GetMaxCompressedLength(int inputSize) { throw null; } + } + public sealed partial class BrotliStream : System.IO.Stream + { + public BrotliStream(System.IO.Stream stream, System.IO.Compression.CompressionLevel compressionLevel) { } + public BrotliStream(System.IO.Stream stream, System.IO.Compression.CompressionLevel compressionLevel, bool leaveOpen) { } + public BrotliStream(System.IO.Stream stream, System.IO.Compression.CompressionMode mode) { } + public BrotliStream(System.IO.Stream stream, System.IO.Compression.CompressionMode mode, bool leaveOpen) { } + public System.IO.Stream BaseStream { get { throw null; } } + public override bool CanRead { get { throw null; } } + public override bool CanSeek { get { throw null; } } + public override bool CanWrite { get { throw null; } } + public override long Length { get { throw null; } } + public override long Position { get { throw null; } set { } } + protected override void Dispose(bool disposing) { } + public override void Flush() { } + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState) { throw null; } + public override int EndRead(IAsyncResult asyncResult) { throw null; } + public override int Read(byte[] array, int offset, int count) { throw null; } + public override System.Threading.Tasks.Task ReadAsync(byte[] array, int offset, int count, System.Threading.CancellationToken cancellationToken) { throw null; } + public override long Seek(long offset, System.IO.SeekOrigin origin) { throw null; } + public override void SetLength(long value) { } + public override IAsyncResult BeginWrite(byte[] array, int offset, int count, AsyncCallback asyncCallback, object asyncState) { throw null; } + public override void EndWrite(IAsyncResult asyncResult) { } + public override void Write(byte[] array, int offset, int count) { } + public override System.Threading.Tasks.Task WriteAsync(byte[] array, int offset, int count, System.Threading.CancellationToken cancellationToken) { throw null; } + } +} diff --git a/external/corefx/src/System.IO.Compression.Brotli/ref/System.IO.Compression.Brotli.csproj b/external/corefx/src/System.IO.Compression.Brotli/ref/System.IO.Compression.Brotli.csproj new file mode 100644 index 0000000000..13f4ca5a70 --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/ref/System.IO.Compression.Brotli.csproj @@ -0,0 +1,21 @@ + + + + + {4ADD9456-A929-4254-B8A2-16FC628ABFDA} + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Intrinsics.X86/src/Configurations.props b/external/corefx/src/System.IO.Compression.Brotli/src/Configurations.props similarity index 100% rename from external/corefx/src/System.Runtime.Intrinsics.X86/src/Configurations.props rename to external/corefx/src/System.IO.Compression.Brotli/src/Configurations.props index 1040c9ba37..17d3231f8e 100644 --- a/external/corefx/src/System.Runtime.Intrinsics.X86/src/Configurations.props +++ b/external/corefx/src/System.IO.Compression.Brotli/src/Configurations.props @@ -2,8 +2,8 @@ - netcoreapp-Windows_NT; netcoreapp-Unix; + netcoreapp-Windows_NT; \ No newline at end of file diff --git a/external/corefx/src/System.IO.Compression.Brotli/src/Interop/Interop.Brotli.Decoder.cs b/external/corefx/src/System.IO.Compression.Brotli/src/Interop/Interop.Brotli.Decoder.cs new file mode 100644 index 0000000000..9eeb467e09 --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/src/Interop/Interop.Brotli.Decoder.cs @@ -0,0 +1,32 @@ +// 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.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; +using size_t = System.IntPtr; + +internal static partial class Interop +{ + internal static partial class Brotli + { + [DllImport(Libraries.CompressionNative)] + internal static extern SafeBrotliDecoderHandle BrotliDecoderCreateInstance(IntPtr allocFunc, IntPtr freeFunc, IntPtr opaque); + + [DllImport(Libraries.CompressionNative)] + internal static extern unsafe int BrotliDecoderDecompressStream( + SafeBrotliDecoderHandle state, ref size_t availableIn, byte** nextIn, + ref size_t availableOut, byte** nextOut, out size_t totalOut); + + [DllImport(Libraries.CompressionNative)] + internal static extern unsafe bool BrotliDecoderDecompress(size_t availableInput, byte* inBytes, ref size_t availableOutput, byte* outBytes); + + [DllImport(Libraries.CompressionNative)] + internal static extern void BrotliDecoderDestroyInstance(IntPtr state); + + [DllImport(Libraries.CompressionNative)] + internal static extern bool BrotliDecoderIsFinished(SafeBrotliDecoderHandle state); + } +} + diff --git a/external/corefx/src/System.IO.Compression.Brotli/src/Interop/Interop.Brotli.Encoder.cs b/external/corefx/src/System.IO.Compression.Brotli/src/Interop/Interop.Brotli.Encoder.cs new file mode 100644 index 0000000000..f6c3ceafc8 --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/src/Interop/Interop.Brotli.Encoder.cs @@ -0,0 +1,36 @@ +// 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.Runtime.InteropServices; +using System.IO.Compression; +using Microsoft.Win32.SafeHandles; +using size_t = System.IntPtr; + +internal static partial class Interop +{ + internal static partial class Brotli + { + [DllImport(Libraries.CompressionNative)] + internal static extern SafeBrotliEncoderHandle BrotliEncoderCreateInstance(IntPtr allocFunc, IntPtr freeFunc, IntPtr opaque); + + [DllImport(Libraries.CompressionNative)] + internal static extern bool BrotliEncoderSetParameter(SafeBrotliEncoderHandle state, BrotliEncoderParameter parameter, UInt32 value); + + [DllImport(Libraries.CompressionNative)] + internal static extern unsafe bool BrotliEncoderCompressStream( + SafeBrotliEncoderHandle state, BrotliEncoderOperation op, ref size_t availableIn, + byte** nextIn, ref size_t availableOut, byte** nextOut, out size_t totalOut); + + [DllImport(Libraries.CompressionNative)] + internal static extern bool BrotliEncoderHasMoreOutput(SafeBrotliEncoderHandle state); + + [DllImport(Libraries.CompressionNative)] + internal static extern void BrotliEncoderDestroyInstance(IntPtr state); + + [DllImport(Libraries.CompressionNative)] + internal static extern unsafe bool BrotliEncoderCompress(int quality, int window, int v, size_t availableInput, byte* inBytes, ref size_t availableOutput, byte* outBytes); + } +} + diff --git a/external/corefx/src/System.IO.Compression.Brotli/src/PinvokeAnalyzerExceptionList.analyzerdata b/external/corefx/src/System.IO.Compression.Brotli/src/PinvokeAnalyzerExceptionList.analyzerdata new file mode 100644 index 0000000000..f86667937a --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/src/PinvokeAnalyzerExceptionList.analyzerdata @@ -0,0 +1,23 @@ + +clrcompression.dll!BrotliDecoderCreateInstance +clrcompression.dll!BrotliDecoderDecompress +clrcompression.dll!BrotliDecoderDecompressStream +clrcompression.dll!BrotliDecoderDestroyInstance +clrcompression.dll!BrotliDecoderErrorString +clrcompression.dll!BrotliDecoderGetErrorCode +clrcompression.dll!BrotliDecoderHasMoreOutput +clrcompression.dll!BrotliDecoderIsFinished +clrcompression.dll!BrotliDecoderIsUsed +clrcompression.dll!BrotliDecoderSetParameter +clrcompression.dll!BrotliDecoderTakeOutput +clrcompression.dll!BrotliDecoderVersion +clrcompression.dll!BrotliEncoderCompress +clrcompression.dll!BrotliEncoderCompressStream +clrcompression.dll!BrotliEncoderCreateInstance +clrcompression.dll!BrotliEncoderDestroyInstance +clrcompression.dll!BrotliEncoderHasMoreOutput +clrcompression.dll!BrotliEncoderIsFinished +clrcompression.dll!BrotliEncoderMaxCompressedSize +clrcompression.dll!BrotliEncoderSetParameter +clrcompression.dll!BrotliEncoderTakeOutput +clrcompression.dll!BrotliEncoderVersion diff --git a/external/corefx/src/System.IO.Compression.Brotli/src/Resources/Strings.resx b/external/corefx/src/System.IO.Compression.Brotli/src/Resources/Strings.resx new file mode 100644 index 0000000000..0e95b1fa9e --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/src/Resources/Strings.resx @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Stream does not support reading. + + + Stream does not support writing. + + + Enum value was out of legal range. + + + Can not access a closed Stream. + + + Positive number required. + + + Offset plus count is larger than the length of target array. + + + Only one asynchronous reader or writer is allowed time at one time. + + + + Failed to create BrotliEncoder instance + + + Can not access a closed Encoder. + + + Provided BrotliEncoder Quality of {0} is not between the minimum value of {1} and the maximum value of {2} + + + Provided BrotliEncoder Window of {0} is not between the minimum value of {1} and the maximum value of {2} + + + The BrotliEncoder {0} can not be changed at current encoder state. + + + + Failed to create BrotliDecoder instance + + + Decoder threw unexpected error: {0} + + + Can not access a closed Decoder. + + + + Can not perform Read operations on a BrotliStream constructed with CompressionMode.Compress. + + + Encoder ran into invalid data. + + + + Can not perform Write operations on a BrotliStream constructed with CompressionMode.Decompress. + + + Decoder ran into invalid data. + + + BrotliStream.BaseStream returned more bytes than requested in Read. + + diff --git a/external/corefx/src/System.IO.Compression.Brotli/src/System.IO.Compression.Brotli.csproj b/external/corefx/src/System.IO.Compression.Brotli/src/System.IO.Compression.Brotli.csproj new file mode 100644 index 0000000000..2555fb0ca1 --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/src/System.IO.Compression.Brotli.csproj @@ -0,0 +1,61 @@ + + + + + System.IO.Compression.Brotli + Library + {5471BFE8-8071-466F-838E-5ADAA779E742} + true + + + + + + + + + + + + + + + + + + + + Common\System\Threading\Tasks\TaskToApm.cs + + + Common\Microsoft\Win32\SafeHandles\SafeBrotliHandle.cs + + + + + + Common\Interop\Windows\Interop.Libraries.cs + + + + + + Common\Interop\Unix\Interop.Libraries.cs + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/BrotliStream.cs b/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/BrotliStream.cs new file mode 100644 index 0000000000..eae7912210 --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/BrotliStream.cs @@ -0,0 +1,133 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace System.IO.Compression +{ + public sealed partial class BrotliStream : Stream + { + private const int DefaultInternalBufferSize = (1 << 16) - 16; //65520; + private Stream _stream; + private readonly byte[] _buffer; + private readonly bool _leaveOpen; + private readonly CompressionMode _mode; + + public BrotliStream(Stream stream, CompressionMode mode) : this(stream, mode, leaveOpen: false) { } + public BrotliStream(Stream stream, CompressionMode mode, bool leaveOpen) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + switch (mode) + { + case CompressionMode.Compress: + if (!stream.CanWrite) + throw new ArgumentException(SR.Stream_FalseCanWrite, nameof(stream)); + break; + case CompressionMode.Decompress: + if (!stream.CanRead) + throw new ArgumentException(SR.Stream_FalseCanRead, nameof(stream)); + break; + default: + throw new ArgumentException(SR.ArgumentOutOfRange_Enum, nameof(mode)); + } + + _mode = mode; + _stream = stream; + _leaveOpen = leaveOpen; + _buffer = new byte[DefaultInternalBufferSize]; + } + + private void EnsureNotDisposed() + { + if (_stream == null) + throw new ObjectDisposedException("stream", SR.ObjectDisposed_StreamClosed); + } + + protected override void Dispose(bool disposing) + { + try + { + if (disposing && _stream != null) + { + if (_mode == CompressionMode.Compress) + { + WriteCore(ReadOnlySpan.Empty, isFinalBlock: true); + } + + if (!_leaveOpen) + { + _stream.Dispose(); + } + } + } + finally + { + _stream = null; + _encoder.Dispose(); + _decoder.Dispose(); + base.Dispose(disposing); + } + } + + private static void ValidateParameters(byte[] array, int offset, int count) + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedPosNum); + + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedPosNum); + + if (array.Length - offset < count) + throw new ArgumentException(SR.InvalidArgumentOffsetCount); + } + + public Stream BaseStream => _stream; + public override bool CanRead => _mode == CompressionMode.Decompress && _stream != null && _stream.CanRead; + public override bool CanWrite => _mode == CompressionMode.Compress && _stream != null && _stream.CanWrite; + public override bool CanSeek => false; + public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); + public override long Length => throw new NotSupportedException(); + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + public override void SetLength(long value) => throw new NotSupportedException(); + + private int _activeAsyncOperation; // 1 == true, 0 == false + private bool AsyncOperationIsActive => _activeAsyncOperation != 0; + + private void EnsureNoActiveAsyncOperation() + { + if (AsyncOperationIsActive) + ThrowInvalidBeginCall(); + } + + private void AsyncOperationStarting() + { + if (Interlocked.CompareExchange(ref _activeAsyncOperation, 1, 0) != 0) + { + ThrowInvalidBeginCall(); + } + } + + private void AsyncOperationCompleting() + { + int oldValue = Interlocked.CompareExchange(ref _activeAsyncOperation, 0, 1); + Debug.Assert(oldValue == 1, $"Expected {nameof(_activeAsyncOperation)} to be 1, got {oldValue}"); + } + + private static void ThrowInvalidBeginCall() + { + throw new InvalidOperationException(SR.InvalidBeginCall); + } + } +} diff --git a/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/BrotliUtils.cs b/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/BrotliUtils.cs new file mode 100644 index 0000000000..7dd6da519a --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/BrotliUtils.cs @@ -0,0 +1,32 @@ +// 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. + +namespace System.IO.Compression +{ + internal static partial class BrotliUtils + { + public const int WindowBits_Min = 10; + public const int WindowBits_Default = 22; + public const int WindowBits_Max = 24; + public const int Quality_Min = 0; + public const int Quality_Default = 11; + public const int Quality_Max = 11; + public const int MaxInputSize = int.MaxValue - 515; // 515 is the max compressed extra bytes + + internal static int GetQualityFromCompressionLevel(CompressionLevel level) + { + switch (level) + { + case CompressionLevel.Optimal: + return Quality_Default; + case CompressionLevel.NoCompression: + return Quality_Min; + case CompressionLevel.Fastest: + return 1; + default: + return (int)level; + } + } + } +} diff --git a/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/dec/BrotliDecoder.cs b/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/dec/BrotliDecoder.cs new file mode 100644 index 0000000000..355e59ae9b --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/dec/BrotliDecoder.cs @@ -0,0 +1,107 @@ +// 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.Buffers; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; +using size_t = System.IntPtr; + +namespace System.IO.Compression +{ + public struct BrotliDecoder : IDisposable + { + private SafeBrotliDecoderHandle _state; + private bool _disposed; + + internal void InitializeDecoder() + { + _state = Interop.Brotli.BrotliDecoderCreateInstance(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + if (_state.IsInvalid) + throw new IOException(SR.BrotliDecoder_Create); + } + + internal void EnsureInitialized() + { + EnsureNotDisposed(); + if (_state == null) + InitializeDecoder(); + } + + public void Dispose() + { + _disposed = true; + _state?.Dispose(); + } + + private void EnsureNotDisposed() + { + if (_disposed) + throw new ObjectDisposedException(nameof(BrotliDecoder), SR.BrotliDecoder_Disposed); + } + + internal OperationStatus Decompress(ReadOnlyMemory source, Memory destination, out int bytesConsumed, out int bytesWritten) => Decompress(source.Span, destination.Span, out bytesConsumed, out bytesWritten); + + public OperationStatus Decompress(ReadOnlySpan source, Span destination, out int bytesConsumed, out int bytesWritten) + { + EnsureInitialized(); + bytesConsumed = 0; + bytesWritten = 0; + if (Interop.Brotli.BrotliDecoderIsFinished(_state)) + return OperationStatus.Done; + size_t availableOutput = (size_t)destination.Length; + size_t availableInput = (size_t)source.Length; + unsafe + { + // We can freely cast between int and size_t for two reasons: + // 1. Interop Brotli functions will always return an availableInput/Output value lower or equal to the one passed to the function + // 2. Span's have a maximum length of the int boundary. + while ((int)availableOutput > 0) + { + fixed (byte* inBytes = &MemoryMarshal.GetReference(source)) + fixed (byte* outBytes = &MemoryMarshal.GetReference(destination)) + { + int brotliResult = Interop.Brotli.BrotliDecoderDecompressStream(_state, ref availableInput, &inBytes, ref availableOutput, &outBytes, out size_t totalOut); + if (brotliResult == 0) // Error + { + return OperationStatus.InvalidData; + } + bytesConsumed += source.Length - (int)availableInput; + bytesWritten += destination.Length - (int)availableOutput; + + switch (brotliResult) + { + case 1: // Success + return OperationStatus.Done; + case 3: // NeedsMoreOutput + return OperationStatus.DestinationTooSmall; + case 2: // NeedsMoreInput + default: + source = source.Slice(source.Length - (int)availableInput); + destination = destination.Slice(destination.Length - (int)availableOutput); + if (brotliResult == 2 && source.Length == 0) + return OperationStatus.NeedMoreData; + break; + } + } + } + return OperationStatus.DestinationTooSmall; + } + } + + public static bool TryDecompress(ReadOnlySpan source, Span destination, out int bytesWritten) + { + unsafe + { + fixed (byte* inBytes = &MemoryMarshal.GetReference(source)) + fixed (byte* outBytes = &MemoryMarshal.GetReference(destination)) + { + size_t availableOutput = (size_t)destination.Length; + bool success = Interop.Brotli.BrotliDecoderDecompress((size_t)source.Length, inBytes, ref availableOutput, outBytes); + bytesWritten = (int)availableOutput; + return success; + } + } + } + } +} diff --git a/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/dec/BrotliStream.Decompress.cs b/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/dec/BrotliStream.Decompress.cs new file mode 100644 index 0000000000..4e15f1c625 --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/dec/BrotliStream.Decompress.cs @@ -0,0 +1,154 @@ +// 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.Buffers; +using System.Threading; +using System.Threading.Tasks; + +namespace System.IO.Compression +{ + public sealed partial class BrotliStream : Stream + { + private BrotliDecoder _decoder; + + public override int Read(byte[] buffer, int offset, int count) + { + ValidateParameters(buffer, offset, count); + return Read(new Span(buffer, offset, count)); + } + + public override int Read(Span destination) + { + if (_mode != CompressionMode.Decompress) + throw new InvalidOperationException(SR.BrotliStream_Compress_UnsupportedOperation); + EnsureNotDisposed(); + int totalWritten = 0; + Span source = Span.Empty; + OperationStatus lastResult = OperationStatus.DestinationTooSmall; + // We want to continue calling Decompress until we're either out of space for output or until Decompress indicates it is finished. + while (destination.Length > 0 && lastResult != OperationStatus.Done) + { + int bytesConsumed = 0; + int bytesWritten = 0; + + if (lastResult == OperationStatus.NeedMoreData) + { + int readBytes = 0; + int iter = 0; + while (readBytes < _buffer.Length && ((iter = _stream.Read(_buffer, readBytes, _buffer.Length - readBytes)) > 0)) + { + readBytes += iter; + if (readBytes > _buffer.Length) + { + // The stream is either malicious or poorly implemented and returned a number of + // bytes larger than the buffer supplied to it. + throw new InvalidDataException(SR.BrotliStream_Decompress_InvalidStream); + } + } + if (readBytes <= 0) + { + break; + } + source = new Span(_buffer, 0, readBytes); + } + + lastResult = _decoder.Decompress(source, destination, out bytesConsumed, out bytesWritten); + if (lastResult == OperationStatus.InvalidData) + throw new InvalidOperationException(SR.BrotliStream_Decompress_InvalidData); + if (bytesConsumed > 0) + source = source.Slice(bytesConsumed); + if (bytesWritten > 0) + { + totalWritten += bytesWritten; + destination = destination.Slice(bytesWritten); + } + } + + return totalWritten; + } + + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState) => + TaskToApm.Begin(ReadAsync(buffer, offset, count, CancellationToken.None), asyncCallback, asyncState); + + public override int EndRead(IAsyncResult asyncResult) => + TaskToApm.End(asyncResult); + + public override Task ReadAsync(byte[] array, int offset, int count, CancellationToken cancellationToken) + { + ValidateParameters(array, offset, count); + return ReadAsync(new Memory(array, offset, count), cancellationToken).AsTask(); + } + + public override ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default(CancellationToken)) + { + if (_mode != CompressionMode.Decompress) + throw new InvalidOperationException(SR.BrotliStream_Compress_UnsupportedOperation); + EnsureNoActiveAsyncOperation(); + EnsureNotDisposed(); + + if (cancellationToken.IsCancellationRequested) + { + return new ValueTask(Task.FromCanceled(cancellationToken)); + } + return FinishReadAsyncMemory(destination, cancellationToken); + } + + private async ValueTask FinishReadAsyncMemory(Memory destination, CancellationToken cancellationToken) + { + AsyncOperationStarting(); + try + { + int totalWritten = 0; + Memory source = Memory.Empty; + OperationStatus lastResult = OperationStatus.DestinationTooSmall; + // We want to continue calling Decompress until we're either out of space for output or until Decompress indicates it is finished. + while (destination.Length > 0 && lastResult != OperationStatus.Done) + { + + int bytesConsumed = 0; + int bytesWritten = 0; + + if (lastResult == OperationStatus.NeedMoreData) + { + int readBytes = 0; + int iter = 0; + while (readBytes < _buffer.Length && ((iter = await _stream.ReadAsync(_buffer, readBytes, _buffer.Length - readBytes, cancellationToken).ConfigureAwait(false)) > 0)) + { + readBytes += iter; + if (readBytes > _buffer.Length) + { + // The stream is either malicious or poorly implemented and returned a number of + // bytes larger than the buffer supplied to it. + throw new InvalidDataException(SR.BrotliStream_Decompress_InvalidStream); + } + } + if (readBytes <= 0) + { + break; + } + source = new Memory(_buffer, 0, readBytes); + } + + cancellationToken.ThrowIfCancellationRequested(); + lastResult = _decoder.Decompress(source, destination, out bytesConsumed, out bytesWritten); + if (lastResult == OperationStatus.InvalidData) + throw new InvalidOperationException(SR.BrotliStream_Decompress_InvalidData); + if (bytesConsumed > 0) + source = source.Slice(bytesConsumed); + if (bytesWritten > 0) + { + totalWritten += bytesWritten; + destination = destination.Slice(bytesWritten); + } + } + + return totalWritten; + } + finally + { + AsyncOperationCompleting(); + } + } + } +} diff --git a/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/enc/BrotliEncoder.cs b/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/enc/BrotliEncoder.cs new file mode 100644 index 0000000000..f85fe44362 --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/enc/BrotliEncoder.cs @@ -0,0 +1,182 @@ +// 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.Buffers; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; +using size_t = System.IntPtr; + +namespace System.IO.Compression +{ + public partial struct BrotliEncoder : IDisposable + { + internal SafeBrotliEncoderHandle _state; + private bool _disposed; + + public BrotliEncoder(int quality, int window) + { + _disposed = false; + _state = Interop.Brotli.BrotliEncoderCreateInstance(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + if (_state.IsInvalid) + throw new IOException(SR.BrotliEncoder_Create); + SetQuality(quality); + SetWindow(window); + } + + /// + /// Performs a lazy initialization of the native encoder using the default Quality and Window values: + /// BROTLI_DEFAULT_WINDOW 22 + /// BROTLI_DEFAULT_QUALITY 11 + /// + internal void InitializeEncoder() + { + EnsureNotDisposed(); + _state = Interop.Brotli.BrotliEncoderCreateInstance(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + if (_state.IsInvalid) + throw new IOException(SR.BrotliEncoder_Create); + } + + internal void EnsureInitialized() + { + EnsureNotDisposed(); + if (_state == null) + { + InitializeEncoder(); + } + } + + public void Dispose() + { + _disposed = true; + _state?.Dispose(); + } + + private void EnsureNotDisposed() + { + if (_disposed) + throw new ObjectDisposedException(nameof(BrotliEncoder), SR.BrotliEncoder_Disposed); + } + + internal void SetQuality(int quality) + { + EnsureNotDisposed(); + if (_state == null || _state.IsInvalid || _state.IsClosed) + { + InitializeEncoder(); + } + if (quality < BrotliUtils.Quality_Min || quality > BrotliUtils.Quality_Max) + { + throw new ArgumentOutOfRangeException(nameof(quality), SR.Format(SR.BrotliEncoder_Quality, quality, 0, BrotliUtils.Quality_Max)); + } + if (!Interop.Brotli.BrotliEncoderSetParameter(_state, BrotliEncoderParameter.Quality, (uint)quality)) + { + throw new InvalidOperationException(SR.Format(SR.BrotliEncoder_InvalidSetParameter, "Quality")); + } + } + + internal void SetWindow(int window) + { + EnsureNotDisposed(); + if (_state == null || _state.IsInvalid || _state.IsClosed) + { + InitializeEncoder(); + } + if (window < BrotliUtils.WindowBits_Min || window > BrotliUtils.WindowBits_Max) + { + throw new ArgumentOutOfRangeException(nameof(window), SR.Format(SR.BrotliEncoder_Window, window, BrotliUtils.WindowBits_Min, BrotliUtils.WindowBits_Max)); + } + if (!Interop.Brotli.BrotliEncoderSetParameter(_state, BrotliEncoderParameter.LGWin, (uint)window)) + { + throw new InvalidOperationException(SR.Format(SR.BrotliEncoder_InvalidSetParameter, "Window")); + } + } + + public static int GetMaxCompressedLength(int length) + { + if (length < 0 || length > BrotliUtils.MaxInputSize) + { + throw new ArgumentOutOfRangeException(nameof(length)); + } + if (length == 0) + return 1; + int numLargeBlocks = length >> 24; + int tail = length & 0xFFFFFF; + int tailOverhead = (tail > (1 << 20)) ? 4 : 3; + int overhead = 2 + (4 * numLargeBlocks) + tailOverhead + 1; + int result = length + overhead; + return result; + } + + internal OperationStatus Flush(Memory destination, out int bytesWritten) => Flush(destination.Span, out bytesWritten); + + public OperationStatus Flush(Span destination, out int bytesWritten) => Compress(ReadOnlySpan.Empty, destination, out int bytesConsumed, out bytesWritten, BrotliEncoderOperation.Flush); + + internal OperationStatus Compress(ReadOnlyMemory source, Memory destination, out int bytesConsumed, out int bytesWritten, bool isFinalBlock) => Compress(source.Span, destination.Span, out bytesConsumed, out bytesWritten, isFinalBlock); + + public OperationStatus Compress(ReadOnlySpan source, Span destination, out int bytesConsumed, out int bytesWritten, bool isFinalBlock) => Compress(source, destination, out bytesConsumed, out bytesWritten, isFinalBlock ? BrotliEncoderOperation.Finish : BrotliEncoderOperation.Process); + + internal OperationStatus Compress(ReadOnlySpan source, Span destination, out int bytesConsumed, out int bytesWritten, BrotliEncoderOperation operation) + { + EnsureInitialized(); + bytesWritten = 0; + bytesConsumed = 0; + size_t availableOutput = (size_t)destination.Length; + size_t availableInput = (size_t)source.Length; + unsafe + { + // We can freely cast between int and size_t for two reasons: + // 1. Interop Brotli functions will always return an availableInput/Output value lower or equal to the one passed to the function + // 2. Span's have a maximum length of the int boundary. + while ((int)availableOutput > 0) + { + fixed (byte* inBytes = &MemoryMarshal.GetReference(source)) + fixed (byte* outBytes = &MemoryMarshal.GetReference(destination)) + { + if (!Interop.Brotli.BrotliEncoderCompressStream(_state, operation, ref availableInput, &inBytes, ref availableOutput, &outBytes, out size_t totalOut)) + { + return OperationStatus.InvalidData; + } + bytesConsumed += source.Length - (int)availableInput; + bytesWritten += destination.Length - (int)availableOutput; + // no bytes written, no remaining input to give to the encoder, and no output in need of retrieving means we are Done + if ((int)availableOutput == destination.Length && !Interop.Brotli.BrotliEncoderHasMoreOutput(_state) && (int)availableInput == 0) + { + return OperationStatus.Done; + } + + source = source.Slice(source.Length - (int)availableInput); + destination = destination.Slice(destination.Length - (int)availableOutput); + } + } + + return OperationStatus.DestinationTooSmall; + } + } + + public static bool TryCompress(ReadOnlySpan source, Span destination, out int bytesWritten) => TryCompress(source, destination, out bytesWritten, BrotliUtils.Quality_Default, BrotliUtils.WindowBits_Default); + + public static bool TryCompress(ReadOnlySpan source, Span destination, out int bytesWritten, int quality, int window) + { + if (quality < 0 || quality > BrotliUtils.Quality_Max) + { + throw new ArgumentOutOfRangeException(nameof(quality), SR.Format(SR.BrotliEncoder_Quality, quality, 0, BrotliUtils.Quality_Max)); + } + if (window < BrotliUtils.WindowBits_Min || window > BrotliUtils.WindowBits_Max) + { + throw new ArgumentOutOfRangeException(nameof(window), SR.Format(SR.BrotliEncoder_Window, window, BrotliUtils.WindowBits_Min, BrotliUtils.WindowBits_Max)); + } + unsafe + { + fixed (byte* inBytes = &MemoryMarshal.GetReference(source)) + fixed (byte* outBytes = &MemoryMarshal.GetReference(destination)) + { + size_t availableOutput = (size_t)destination.Length; + bool success = Interop.Brotli.BrotliEncoderCompress(quality, window, /*BrotliEncoderMode*/ 0, (size_t)source.Length, inBytes, ref availableOutput, outBytes); + bytesWritten = (int)availableOutput; + return success; + } + } + } + } +} diff --git a/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/enc/BrotliEncoderOperation.cs b/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/enc/BrotliEncoderOperation.cs new file mode 100644 index 0000000000..ade5d63c9b --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/enc/BrotliEncoderOperation.cs @@ -0,0 +1,20 @@ +// 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. + +namespace System.IO.Compression +{ + /// + /// Process - Process input. Encoder may postpone producing output, until it has processed enough input. + /// Flush - Produce output for all processed input. Actual flush is performed when input stream is depleted and there is enough space in output stream. + /// Finish - Finalize the stream. Adding more input data to finalized stream is impossible. + /// EmitMetadata - Emit metadata block to stream. Stream is soft-flushed before metadata block is emitted. Metadata bloc MUST be no longer than 16MiB. + /// + internal enum BrotliEncoderOperation + { + Process, + Flush, + Finish, + EmitMetadata + } +} diff --git a/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/enc/BrotliEncoderParameter.cs b/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/enc/BrotliEncoderParameter.cs new file mode 100644 index 0000000000..8d3cf110b4 --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/enc/BrotliEncoderParameter.cs @@ -0,0 +1,24 @@ +// 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. + +namespace System.IO.Compression +{ + /// + /// Mode - BrotliEncoderMode enumerates all available values. + /// Quality - The main compression speed-density lever. The higher the quality, the slower the compression. Range is from ::BROTLI_MIN_QUALITY to::BROTLI_MAX_QUALITY. + /// LGWin - Recommended sliding LZ77 window size. Encoder may reduce this value, e.g. if input is much smaller than window size. Range is from BROTLI_MIN_WINDOW_BITS to BROTLI_MAX_WINDOW_BITS. + /// LGBlock - Recommended input block size. Encoder may reduce this value, e.g. if input is much smaller than window size. Range is from BROTLI_MIN_INPUT_BLOCK_BITS to BROTLI_MAX_INPUT_BLOCK_BITS. Bigger input block size allows better compression, but consumes more memory. + /// LCModeling- Flag that affects usage of "literal context modeling" format feature. This flag is a "decoding-speed vs compression ratio" trade-off. + /// SizeHint - Estimated total input size for all BrotliEncoderCompressStream calls. The default value is 0, which means that the total input size is unknown. + /// + internal enum BrotliEncoderParameter + { + Mode, + Quality, + LGWin, + LGBlock, + LCModeling, + SizeHint + } +} diff --git a/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/enc/BrotliStream.Compress.cs b/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/enc/BrotliStream.Compress.cs new file mode 100644 index 0000000000..5f2ecd1b6b --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/src/System/IO/Compression/enc/BrotliStream.Compress.cs @@ -0,0 +1,165 @@ +// 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.Buffers; +using System.Threading; +using System.Threading.Tasks; + +namespace System.IO.Compression +{ + public sealed partial class BrotliStream : Stream + { + private BrotliEncoder _encoder; + + public BrotliStream(Stream stream, CompressionLevel compressionLevel) : this(stream, compressionLevel, leaveOpen: false) { } + public BrotliStream(Stream stream, CompressionLevel compressionLevel, bool leaveOpen) : this(stream, CompressionMode.Compress, leaveOpen) + { + _encoder.SetQuality(BrotliUtils.GetQualityFromCompressionLevel(compressionLevel)); + } + + public override void Write(byte[] buffer, int offset, int count) + { + ValidateParameters(buffer, offset, count); + WriteCore(new ReadOnlySpan(buffer, offset, count)); + } + + public override void Write(ReadOnlySpan source) + { + WriteCore(source); + } + + internal void WriteCore(ReadOnlySpan source, bool isFinalBlock = false) + { + if (_mode != CompressionMode.Compress) + throw new InvalidOperationException(SR.BrotliStream_Decompress_UnsupportedOperation); + EnsureNotDisposed(); + + OperationStatus lastResult = OperationStatus.DestinationTooSmall; + Span output = new Span(_buffer); + while (lastResult == OperationStatus.DestinationTooSmall) + { + int bytesConsumed = 0; + int bytesWritten = 0; + lastResult = _encoder.Compress(source, output, out bytesConsumed, out bytesWritten, isFinalBlock); + if (lastResult == OperationStatus.InvalidData) + throw new InvalidOperationException(SR.BrotliStream_Compress_InvalidData); + if (bytesWritten > 0) + _stream.Write(output.Slice(0, bytesWritten)); + if (bytesConsumed > 0) + source = source.Slice(bytesConsumed); + } + } + + public override IAsyncResult BeginWrite(byte[] array, int offset, int count, AsyncCallback asyncCallback, object asyncState) => + TaskToApm.Begin(WriteAsync(array, offset, count, CancellationToken.None), asyncCallback, asyncState); + + public override void EndWrite(IAsyncResult asyncResult) => + TaskToApm.End(asyncResult); + + public override Task WriteAsync(byte[] array, int offset, int count, CancellationToken cancellationToken) + { + ValidateParameters(array, offset, count); + return WriteAsync(new ReadOnlyMemory(array, offset, count), cancellationToken); + } + + public override Task WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken = default(CancellationToken)) + { + if (_mode != CompressionMode.Compress) + throw new InvalidOperationException(SR.BrotliStream_Decompress_UnsupportedOperation); + EnsureNoActiveAsyncOperation(); + EnsureNotDisposed(); + + return cancellationToken.IsCancellationRequested ? + Task.FromCanceled(cancellationToken) : + WriteAsyncMemoryCore(source, cancellationToken); + } + + private async Task WriteAsyncMemoryCore(ReadOnlyMemory source, CancellationToken cancellationToken) + { + AsyncOperationStarting(); + try + { + OperationStatus lastResult = OperationStatus.DestinationTooSmall; + while (lastResult == OperationStatus.DestinationTooSmall) + { + Memory output = new Memory(_buffer); + int bytesConsumed = 0; + int bytesWritten = 0; + lastResult = _encoder.Compress(source, output, out bytesConsumed, out bytesWritten, isFinalBlock: false); + if (lastResult == OperationStatus.InvalidData) + throw new InvalidOperationException(SR.BrotliStream_Compress_InvalidData); + if (bytesConsumed > 0) + source = source.Slice(bytesConsumed); + if (bytesWritten > 0) + await _stream.WriteAsync(_buffer, 0, bytesWritten, cancellationToken).ConfigureAwait(false); + } + } + finally + { + AsyncOperationCompleting(); + } + } + + public override void Flush() + { + EnsureNotDisposed(); + if (_mode == CompressionMode.Compress) + { + if (_encoder._state == null || _encoder._state.IsClosed) + return; + + OperationStatus lastResult = OperationStatus.DestinationTooSmall; + Span output = new Span(_buffer); + while (lastResult == OperationStatus.DestinationTooSmall) + { + int bytesWritten = 0; + lastResult = _encoder.Flush(output, out bytesWritten); + if (lastResult == OperationStatus.InvalidData) + throw new InvalidDataException(SR.BrotliStream_Compress_InvalidData); + if (bytesWritten > 0) + { + _stream.Write(output.Slice(0, bytesWritten)); + } + } + } + } + + public override Task FlushAsync(CancellationToken cancellationToken) + { + EnsureNoActiveAsyncOperation(); + EnsureNotDisposed(); + + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + + return _mode != CompressionMode.Compress ? Task.CompletedTask : FlushAsyncCore(cancellationToken); + } + + private async Task FlushAsyncCore(CancellationToken cancellationToken) + { + AsyncOperationStarting(); + try + { + if (_encoder._state == null || _encoder._state.IsClosed) + return; + + OperationStatus lastResult = OperationStatus.DestinationTooSmall; + while (lastResult == OperationStatus.DestinationTooSmall) + { + Memory output = new Memory(_buffer); + int bytesWritten = 0; + lastResult = _encoder.Flush(output, out bytesWritten); + if (lastResult == OperationStatus.InvalidData) + throw new InvalidDataException(SR.BrotliStream_Compress_InvalidData); + if (bytesWritten > 0) + await _stream.WriteAsync(output.Slice(0, bytesWritten), cancellationToken).ConfigureAwait(false); + } + } + finally + { + AsyncOperationCompleting(); + } + } + } +} diff --git a/external/corefx/src/System.IO.Compression.Brotli/tests/BrotliEncoderTests.cs b/external/corefx/src/System.IO.Compression.Brotli/tests/BrotliEncoderTests.cs new file mode 100644 index 0000000000..8e0505cd31 --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/tests/BrotliEncoderTests.cs @@ -0,0 +1,353 @@ +// 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.Buffers; +using Xunit; + +namespace System.IO.Compression.Tests +{ + public class BrotliEncoderTests : CompressionTestBase + { + protected override string CompressedTestFile(string uncompressedPath) => Path.Combine("BrotliTestData", Path.GetFileName(uncompressedPath) + ".br"); + + [Fact] + public void InvalidQuality() + { + Assert.Throws("quality", () => new BrotliEncoder(-1, 11)); + Assert.Throws("quality", () => new BrotliEncoder(12, 11)); + Assert.Throws("quality", () => BrotliEncoder.TryCompress(new ReadOnlySpan(), new Span(), out int bytesWritten, -1, 13)); + Assert.Throws("quality", () => BrotliEncoder.TryCompress(new ReadOnlySpan(), new Span(), out int bytesWritten, 12, 13)); + } + + [Fact] + public void InvalidWindow() + { + Assert.Throws("window", () => new BrotliEncoder(10, -1)); + Assert.Throws("window", () => new BrotliEncoder(10, 9)); + Assert.Throws("window", () => new BrotliEncoder(10, 25)); + Assert.Throws("window", () => BrotliEncoder.TryCompress(new ReadOnlySpan(), new Span(), out int bytesWritten, 6, -1)); + Assert.Throws("window", () => BrotliEncoder.TryCompress(new ReadOnlySpan(), new Span(), out int bytesWritten, 6, 9)); + Assert.Throws("window", () => BrotliEncoder.TryCompress(new ReadOnlySpan(), new Span(), out int bytesWritten, 6, 25)); + } + + [Fact] + public void GetMaxCompressedSize_Basic() + { + Assert.Throws("length", () => BrotliEncoder.GetMaxCompressedLength(-1)); + Assert.Throws("length", () => BrotliEncoder.GetMaxCompressedLength(2147483133)); + Assert.InRange(BrotliEncoder.GetMaxCompressedLength(2147483132), 0, Int32.MaxValue); + Assert.Equal(1, BrotliEncoder.GetMaxCompressedLength(0)); + } + + [Fact] + public void GetMaxCompressedSize() + { + string uncompressedFile = UncompressedTestFile(); + string compressedFile = CompressedTestFile(uncompressedFile); + int maxCompressedSize = BrotliEncoder.GetMaxCompressedLength((int)new FileInfo(uncompressedFile).Length); + int actualCompressedSize = (int)new FileInfo(compressedFile).Length; + Assert.True(maxCompressedSize >= actualCompressedSize, $"MaxCompressedSize: {maxCompressedSize}, ActualCompressedSize: {actualCompressedSize}"); + } + + /// + /// Test to ensure that when given an empty Destination span, the decoder will consume no input and write no output. + /// + [Fact] + public void Decompress_WithEmptyDestination() + { + string testFile = UncompressedTestFile(); + byte[] sourceBytes = File.ReadAllBytes(CompressedTestFile(testFile)); + byte[] destinationBytes = new byte[0]; + ReadOnlySpan source = new ReadOnlySpan(sourceBytes); + Span destination = new Span(destinationBytes); + + Assert.False(BrotliDecoder.TryDecompress(source, destination, out int bytesWritten), "TryDecompress completed successfully but should have failed due to too short of a destination array"); + Assert.Equal(0, bytesWritten); + + BrotliDecoder decoder; + var result = decoder.Decompress(source, destination, out int bytesConsumed, out bytesWritten); + Assert.Equal(0, bytesWritten); + Assert.Equal(0, bytesConsumed); + Assert.Equal(OperationStatus.DestinationTooSmall, result); + } + + /// + /// Test to ensure that when given an empty source span, the decoder will consume no input and write no output + /// + [Fact] + public void Decompress_WithEmptySource() + { + string testFile = UncompressedTestFile(); + byte[] sourceBytes = new byte[0]; + byte[] destinationBytes = new byte[100000]; + ReadOnlySpan source = new ReadOnlySpan(sourceBytes); + Span destination = new Span(destinationBytes); + + Assert.False(BrotliDecoder.TryDecompress(source, destination, out int bytesWritten), "TryDecompress completed successfully but should have failed due to too short of a source array"); + Assert.Equal(0, bytesWritten); + + BrotliDecoder decoder; + var result = decoder.Decompress(source, destination, out int bytesConsumed, out bytesWritten); + Assert.Equal(0, bytesWritten); + Assert.Equal(0, bytesConsumed); + Assert.Equal(OperationStatus.NeedMoreData, result); + } + + /// + /// Test to ensure that when given an empty Destination span, the encoder consume no input and write no output + /// + [Fact] + public void Compress_WithEmptyDestination() + { + string testFile = UncompressedTestFile(); + byte[] correctUncompressedBytes = File.ReadAllBytes(testFile); + byte[] compressedBytes = File.ReadAllBytes(CompressedTestFile(testFile)); + byte[] empty = new byte[0]; + ReadOnlySpan source = new ReadOnlySpan(correctUncompressedBytes); + Span destination = new Span(empty); + + Assert.False(BrotliEncoder.TryCompress(source, destination, out int bytesWritten), "TryCompress completed successfully but should have failed due to too short of a destination array"); + Assert.Equal(0, bytesWritten); + + BrotliEncoder encoder; + var result = encoder.Compress(source, destination, out int bytesConsumed, out bytesWritten, false); + Assert.Equal(0, bytesWritten); + Assert.Equal(0, bytesConsumed); + Assert.Equal(OperationStatus.DestinationTooSmall, result); + + result = encoder.Compress(source, destination, out bytesConsumed, out bytesWritten, isFinalBlock: true); + Assert.Equal(0, bytesWritten); + Assert.Equal(0, bytesConsumed); + Assert.Equal(OperationStatus.DestinationTooSmall, result); + } + + /// + /// Test to ensure that when given an empty source span, the decoder will consume no input and write no output (until the finishing block) + /// + [Fact] + public void Compress_WithEmptySource() + { + string testFile = UncompressedTestFile(); + byte[] sourceBytes = new byte[0]; + byte[] destinationBytes = new byte[100000]; + ReadOnlySpan source = new ReadOnlySpan(sourceBytes); + Span destination = new Span(destinationBytes); + + Assert.True(BrotliEncoder.TryCompress(source, destination, out int bytesWritten)); + // The only byte written should be the Brotli end of stream byte which varies based on the window/quality + Assert.Equal(1, bytesWritten); + + BrotliEncoder encoder; + var result = encoder.Compress(source, destination, out int bytesConsumed, out bytesWritten, false); + Assert.Equal(0, bytesWritten); + Assert.Equal(0, bytesConsumed); + Assert.Equal(OperationStatus.Done, result); + + result = encoder.Compress(source, destination, out bytesConsumed, out bytesWritten, isFinalBlock: true); + Assert.Equal(1, bytesWritten); + Assert.Equal(0, bytesConsumed); + Assert.Equal(OperationStatus.Done, result); + } + + /// + /// Test that the decoder can handle partial chunks of flushed encoded data sent from the BrotliEncoder + /// + [Fact] + public void RoundTrip_Chunks() + { + int chunkSize = 100; + int totalSize = 20000; + BrotliEncoder encoder; + BrotliDecoder decoder; + for (int i = 0; i < totalSize; i += chunkSize) + { + byte[] uncompressed = new byte[chunkSize]; + new Random().NextBytes(uncompressed); + byte[] compressed = new byte[BrotliEncoder.GetMaxCompressedLength(chunkSize)]; + byte[] deompressed = new byte[chunkSize]; + var uncompressedSpan = new ReadOnlySpan(uncompressed); + var compressedSpan = new Span(compressed); + var decompressedSpan = new Span(deompressed); + + int totalWrittenThisIteration = 0; + var compress = encoder.Compress(uncompressedSpan, compressedSpan, out int bytesConsumed, out int bytesWritten, isFinalBlock: false); + totalWrittenThisIteration += bytesWritten; + compress = encoder.Flush(compressedSpan.Slice(bytesWritten), out bytesWritten); + totalWrittenThisIteration += bytesWritten; + + var res = decoder.Decompress(compressedSpan.Slice(0, totalWrittenThisIteration), decompressedSpan, out int decompressbytesConsumed, out int decompressbytesWritten); + Assert.Equal(totalWrittenThisIteration, decompressbytesConsumed); + Assert.Equal(bytesConsumed, decompressbytesWritten); + for (int j = 0; j < bytesConsumed; j++) + Assert.Equal(uncompressed[j], decompressedSpan[j]); + + } + } + + [Theory] + [OuterLoop("Full set of UncompressedTestFiles takes around 15s to run")] + [MemberData(nameof(UncompressedTestFiles))] + public void ReadFully(string testFile) + { + byte[] correctUncompressedBytes = File.ReadAllBytes(testFile); + byte[] compressedBytes = File.ReadAllBytes(CompressedTestFile(testFile)); + byte[] actualUncompressedBytes = new byte[correctUncompressedBytes.Length + 10000]; + ReadOnlySpan source = new ReadOnlySpan(compressedBytes); + Span destination = new Span(actualUncompressedBytes); + Assert.True(BrotliDecoder.TryDecompress(source, destination, out int bytesWritten), "TryDecompress did not complete successfully"); + Assert.Equal(correctUncompressedBytes.Length, bytesWritten); + for (int i = 0; i < correctUncompressedBytes.Length; i++) + Assert.Equal(correctUncompressedBytes[i], actualUncompressedBytes[i]); + } + + [Theory] + [OuterLoop("Full set of UncompressedTestFiles takes around 15s to run")] + [MemberData(nameof(UncompressedTestFiles))] + public void ReadWithState(string testFile) + { + byte[] correctUncompressedBytes = File.ReadAllBytes(testFile); + byte[] compressedBytes = File.ReadAllBytes(CompressedTestFile(testFile)); + byte[] actualUncompressedBytes = new byte[correctUncompressedBytes.Length]; + Decompress_WithState(compressedBytes, actualUncompressedBytes); + + for (int i = 0; i < correctUncompressedBytes.Length; i++) + Assert.Equal(correctUncompressedBytes[i], actualUncompressedBytes[i]); + } + + [Theory] + [OuterLoop("Full set of UncompressedTestFiles takes around 15s to run")] + [MemberData(nameof(UncompressedTestFiles))] + public void ReadWithoutState(string testFile) + { + byte[] correctUncompressedBytes = File.ReadAllBytes(testFile); + byte[] compressedBytes = File.ReadAllBytes(CompressedTestFile(testFile)); + byte[] actualUncompressedBytes = new byte[correctUncompressedBytes.Length]; + Decompress_WithoutState(compressedBytes, actualUncompressedBytes); + + for (int i = 0; i < correctUncompressedBytes.Length; i++) + Assert.Equal(correctUncompressedBytes[i], actualUncompressedBytes[i]); + } + + [Theory] + [OuterLoop("Full set of UncompressedTestFiles takes around 15s to run")] + [MemberData(nameof(UncompressedTestFiles))] + public void WriteFully(string testFile) + { + byte[] correctUncompressedBytes = File.ReadAllBytes(testFile); + byte[] compressedBytes = new byte[BrotliEncoder.GetMaxCompressedLength(correctUncompressedBytes.Length)]; + byte[] actualUncompressedBytes = new byte[BrotliEncoder.GetMaxCompressedLength(correctUncompressedBytes.Length)]; + + Span destination = new Span(compressedBytes); + + Assert.True(BrotliEncoder.TryCompress(correctUncompressedBytes, destination, out int bytesWritten)); + Assert.True(BrotliDecoder.TryDecompress(destination, actualUncompressedBytes, out bytesWritten)); + Assert.Equal(correctUncompressedBytes.Length, bytesWritten); + + for (int i = 0; i < correctUncompressedBytes.Length; i++) + Assert.Equal(correctUncompressedBytes[i], actualUncompressedBytes[i]); + } + + [Theory] + [OuterLoop("Full set of UncompressedTestFiles takes around 15s to run")] + [MemberData(nameof(UncompressedTestFiles))] + public void WriteWithState(string testFile) + { + byte[] correctUncompressedBytes = File.ReadAllBytes(testFile); + byte[] compressedBytes = new byte[BrotliEncoder.GetMaxCompressedLength(correctUncompressedBytes.Length)]; + byte[] actualUncompressedBytes = new byte[correctUncompressedBytes.Length]; + + Compress_WithState(correctUncompressedBytes, compressedBytes); + Decompress_WithState(compressedBytes, actualUncompressedBytes); + + for (int i = 0; i < correctUncompressedBytes.Length; i++) + Assert.Equal(correctUncompressedBytes[i], actualUncompressedBytes[i]); + } + + [Theory] + [OuterLoop("Full set of UncompressedTestFiles takes around 15s to run")] + [MemberData(nameof(UncompressedTestFiles))] + public void WriteWithoutState(string testFile) + { + byte[] correctUncompressedBytes = File.ReadAllBytes(testFile); + byte[] compressedBytes = new byte[BrotliEncoder.GetMaxCompressedLength(correctUncompressedBytes.Length)]; + byte[] actualUncompressedBytes = new byte[correctUncompressedBytes.Length]; + + Compress_WithoutState(correctUncompressedBytes, compressedBytes); + Decompress_WithoutState(compressedBytes, actualUncompressedBytes); + + for (int i = 0; i < correctUncompressedBytes.Length; i++) + Assert.Equal(correctUncompressedBytes[i], actualUncompressedBytes[i]); + } + + [Theory] + [OuterLoop("Full set of UncompressedTestFiles takes around 15s to run")] + [MemberData(nameof(UncompressedTestFiles))] + public void WriteStream(string testFile) + { + byte[] correctUncompressedBytes = File.ReadAllBytes(testFile); + byte[] compressedBytes = ((MemoryStream)Compress_Stream(correctUncompressedBytes)).ToArray(); + byte[] actualUncompressedBytes = ((MemoryStream)Decompress_Stream(compressedBytes)).ToArray(); + + for (int i = 0; i < correctUncompressedBytes.Length; i++) + Assert.Equal(correctUncompressedBytes[i], actualUncompressedBytes[i]); + } + + public static void Compress_WithState(ReadOnlySpan input, Span output) + { + BrotliEncoder encoder; + while (!input.IsEmpty && !output.IsEmpty) + { + encoder.Compress(input, output, out int bytesConsumed, out int written, isFinalBlock: false); + input = input.Slice(bytesConsumed); + output = output.Slice(written); + } + encoder.Compress(ReadOnlySpan.Empty, output, out int bytesConsumed2, out int bytesWritten, isFinalBlock: true); + } + + public static void Decompress_WithState(ReadOnlySpan input, Span output) + { + BrotliDecoder decoder; + while (!input.IsEmpty && !output.IsEmpty) + { + decoder.Decompress(input, output, out int bytesConsumed, out int written); + input = input.Slice(bytesConsumed); + output = output.Slice(written); + } + } + + public static void Compress_WithoutState(ReadOnlySpan input, Span output) + { + BrotliEncoder.TryCompress(input, output, out int bytesWritten); + } + + public static void Decompress_WithoutState(ReadOnlySpan input, Span output) + { + BrotliDecoder.TryDecompress(input, output, out int bytesWritten); + } + + public static Stream Compress_Stream(ReadOnlySpan input) + { + using (var inputStream = new MemoryStream(input.ToArray())) + { + var outputStream = new MemoryStream(); + var compressor = new BrotliStream(outputStream, CompressionMode.Compress, true); + inputStream.CopyTo(compressor); + compressor.Dispose(); + return outputStream; + } + } + + public static Stream Decompress_Stream(ReadOnlySpan input) + { + using (var inputStream = new MemoryStream(input.ToArray())) + { + var outputStream = new MemoryStream(); + var decompressor = new BrotliStream(inputStream, CompressionMode.Decompress, true); + decompressor.CopyTo(outputStream); + decompressor.Dispose(); + return outputStream; + } + } + } +} diff --git a/external/corefx/src/System.IO.Compression.Brotli/tests/BrotliGoogleTestData.cs b/external/corefx/src/System.IO.Compression.Brotli/tests/BrotliGoogleTestData.cs new file mode 100644 index 0000000000..8f066aa285 --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/tests/BrotliGoogleTestData.cs @@ -0,0 +1,72 @@ +// 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.Collections.Generic; +using Xunit; + +namespace System.IO.Compression.Tests +{ + public class BrotliGoogleTestData + { + public static IEnumerable GoogleTestData() + { + yield return new object[] { Path.Combine("UncompressedTestFiles", "GoogleTestData", "10x10y") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "GoogleTestData", "64x") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "GoogleTestData", "backward65536") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "GoogleTestData", "compressed_file") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "GoogleTestData", "compressed_repeated") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "GoogleTestData", "empty") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "GoogleTestData", "mapsdatazrh") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "GoogleTestData", "monkey") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "GoogleTestData", "quickfox") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "GoogleTestData", "quickfox_repeated") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "GoogleTestData", "random_org_10k.bin") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "GoogleTestData", "x") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "GoogleTestData", "ukkonooa") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "GoogleTestData", "xyzzy") }; + yield return new object[] { Path.Combine("UncompressedTestFiles", "GoogleTestData", "zeros") }; + } + + private string CompressedTestFile(string uncompressedPath) => Path.Combine("BrotliTestData", "GoogleTestData", Path.GetFileName(uncompressedPath) + ".br"); + + + [Theory] + [MemberData(nameof(GoogleTestData))] + public void DecompressFile(string fileName) + { + byte[] bytes = File.ReadAllBytes(CompressedTestFile(fileName)); + byte[] expected = File.ReadAllBytes(fileName); + + ValidateCompressedData(bytes, expected); + } + + [Theory] + [MemberData(nameof(GoogleTestData))] + public void RoundtripCompressDecompressFile(string fileName) + { + byte[] bytes = File.ReadAllBytes(fileName); + MemoryStream memoryStream = new MemoryStream(); + using (BrotliStream brotliStream = new BrotliStream(memoryStream, CompressionMode.Compress, true)) + { + brotliStream.Write(bytes, 0, bytes.Length); + } + memoryStream.Position = 0; + ValidateCompressedData(memoryStream.ToArray(), bytes); + memoryStream.Dispose(); + } + + private void ValidateCompressedData(byte[] compressedData, byte[] expected) + { + MemoryStream compressed = new MemoryStream(compressedData); + using (MemoryStream decompressed = new MemoryStream()) + using (var decompressor = new BrotliStream(compressed, CompressionMode.Decompress, true)) + { + decompressor.CopyTo(decompressed); + Assert.Equal(expected.Length, decompressed.ToArray().Length); + Assert.Equal(expected, decompressed.ToArray()); + } + } + } + +} diff --git a/external/corefx/src/System.IO.Compression.Brotli/tests/CompressionStreamUnitTests.Brotli.cs b/external/corefx/src/System.IO.Compression.Brotli/tests/CompressionStreamUnitTests.Brotli.cs new file mode 100644 index 0000000000..e7323ff936 --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/tests/CompressionStreamUnitTests.Brotli.cs @@ -0,0 +1,48 @@ +// 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.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace System.IO.Compression +{ + public class BrotliStreamUnitTests : CompressionStreamUnitTestBase + { + public override Stream CreateStream(Stream stream, CompressionMode mode) => new BrotliStream(stream, mode); + public override Stream CreateStream(Stream stream, CompressionMode mode, bool leaveOpen) => new BrotliStream(stream, mode, leaveOpen); + public override Stream CreateStream(Stream stream, CompressionLevel level) => new BrotliStream(stream, level); + public override Stream CreateStream(Stream stream, CompressionLevel level, bool leaveOpen) => new BrotliStream(stream, level, leaveOpen); + public override Stream BaseStream(Stream stream) => ((BrotliStream)stream).BaseStream; + public override int BufferSize { get => 65520; } + protected override string CompressedTestFile(string uncompressedPath) => Path.Combine("BrotliTestData", Path.GetFileName(uncompressedPath) + ".br"); + + [Fact] + public void Precancellation() + { + var ms = new MemoryStream(); + using (Stream compressor = new BrotliStream(ms, CompressionMode.Compress, leaveOpen: true)) + { + Assert.True(compressor.WriteAsync(new byte[1], 0, 1, new CancellationToken(true)).IsCanceled); + Assert.True(compressor.FlushAsync(new CancellationToken(true)).IsCanceled); + } + using (Stream decompressor = CreateStream(ms, CompressionMode.Decompress, leaveOpen: true)) + { + Assert.True(decompressor.ReadAsync(new byte[1], 0, 1, new CancellationToken(true)).IsCanceled); + } + } + + [Fact] + [OuterLoop("Test takes ~12 seconds to run")] + public override void Dispose_WithUnfinishedWriteAsync() { base.Dispose_WithUnfinishedWriteAsync(); } + + [Fact] + [OuterLoop("Test takes ~6 seconds to run")] + public override void FlushAsync_DuringWriteAsync() { base.FlushAsync_DuringWriteAsync(); } + + [Fact] + [OuterLoop("Test takes ~6 seconds to run")] + public override void WriteAsync_DuringWriteAsync() { base.WriteAsync_DuringWriteAsync(); } + } +} diff --git a/external/corefx/external/ilasm/Configurations.props b/external/corefx/src/System.IO.Compression.Brotli/tests/Configurations.props similarity index 94% rename from external/corefx/external/ilasm/Configurations.props rename to external/corefx/src/System.IO.Compression.Brotli/tests/Configurations.props index c2456c5408..17d3231f8e 100644 --- a/external/corefx/external/ilasm/Configurations.props +++ b/external/corefx/src/System.IO.Compression.Brotli/tests/Configurations.props @@ -2,9 +2,8 @@ - netcoreapp-Windows_NT; netcoreapp-Unix; - netstandard; + netcoreapp-Windows_NT; \ No newline at end of file diff --git a/external/corefx/src/System.IO.Compression.Brotli/tests/Performance/BrotliPerfTests.cs b/external/corefx/src/System.IO.Compression.Brotli/tests/Performance/BrotliPerfTests.cs new file mode 100644 index 0000000000..f0ee2b7d22 --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/tests/Performance/BrotliPerfTests.cs @@ -0,0 +1,158 @@ +// 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.Collections.Generic; +using Microsoft.Xunit.Performance; +using Xunit; + +namespace System.IO.Compression +{ + public class BrotliPerfTests : CompressionTestBase + { + protected override string CompressedTestFile(string uncompressedPath) => Path.Combine("BrotliTestData", Path.GetFileName(uncompressedPath) + ".br"); + + public static IEnumerable CanterburyCorpus_WithCompressionLevel() + { + foreach (CompressionLevel compressionLevel in Enum.GetValues(typeof(CompressionLevel))) + { + foreach (object[] canterburyWithoutLevel in CanterburyCorpus()) + { + yield return new object[] { canterburyWithoutLevel[0], canterburyWithoutLevel[1], compressionLevel }; + } + } + } + + public static IEnumerable CanterburyCorpus() + { + foreach (int innerIterations in new int[] { 1, 10 }) + { + foreach (var fileName in UncompressedTestFiles()) + { + yield return new object[] { innerIterations, fileName[0] }; + } + } + } + + [Benchmark] + [MemberData(nameof(CanterburyCorpus_WithCompressionLevel))] + public void Compress_Canterbury_WithState(int innerIterations, string uncompressedFileName, CompressionLevel compressLevel) + { + byte[] bytes = File.ReadAllBytes(uncompressedFileName); + ReadOnlySpan uncompressedData = new ReadOnlySpan(bytes); + int maxCompressedSize = BrotliEncoder.GetMaxCompressedLength(bytes.Length); + List compressedDataArrays = new List(innerIterations); + foreach (var iteration in Benchmark.Iterations) + { + for (int i = 0; i < innerIterations; i++) + { + compressedDataArrays.Add(new byte[maxCompressedSize]); + } + using (iteration.StartMeasurement()) + { + for (int i = 0; i < innerIterations; i++) + { + using (BrotliEncoder encoder = new BrotliEncoder()) + { + Span output = new Span(compressedDataArrays[i]); + ReadOnlySpan input = uncompressedData; + while (!input.IsEmpty && !output.IsEmpty) + { + encoder.Compress(input, output, out int bytesConsumed, out int written, isFinalBlock:false); + input = input.Slice(bytesConsumed); + output = output.Slice(written); + } + encoder.Compress(input, output, out int bytesConsumed2, out int written2, isFinalBlock: true); + } + } + } + } + } + + [Benchmark] + [MemberData(nameof(CanterburyCorpus))] + public void Decompress_Canterbury_WithState(int innerIterations, string uncompressedFileName) + { + byte[] compressedBytes = File.ReadAllBytes(CompressedTestFile(uncompressedFileName)); + ReadOnlySpan compressedData = new ReadOnlySpan(compressedBytes); + List uncompressedDataArrays = new List(innerIterations); + foreach (var iteration in Benchmark.Iterations) + { + for (int i = 0; i < innerIterations; i++) + { + uncompressedDataArrays.Add(new byte[65520]); + } + using (iteration.StartMeasurement()) + { + for (int i = 0; i < innerIterations; i++) + { + using (BrotliDecoder decoder = new BrotliDecoder()) + { + Span output = new Span(uncompressedDataArrays[i]); + ReadOnlySpan input = compressedData; + while (!input.IsEmpty && !output.IsEmpty) + { + decoder.Decompress(input, output, out int bytesConsumed, out int written); + input = input.Slice(bytesConsumed); + output = output.Slice(written); + } + } + } + } + } + } + + [Benchmark] + [MemberData(nameof(CanterburyCorpus_WithCompressionLevel))] + public void Compress_Canterbury_WithoutState(int innerIterations, string uncompressedFileName, CompressionLevel compressLevel) + { + byte[] bytes = File.ReadAllBytes(uncompressedFileName); + ReadOnlySpan uncompressedData = new ReadOnlySpan(bytes); + int maxCompressedSize = BrotliEncoder.GetMaxCompressedLength(bytes.Length); + List compressedDataArrays = new List(innerIterations); + int compressLevelBrotli = compressLevel == CompressionLevel.Optimal ? 11 : compressLevel == CompressionLevel.Fastest ? 1 : 0; + foreach (var iteration in Benchmark.Iterations) + { + for (int i = 0; i < innerIterations; i++) + { + compressedDataArrays.Add(new byte[maxCompressedSize]); + } + using (iteration.StartMeasurement()) + { + for (int i = 0; i < innerIterations; i++) + { + Assert.True(BrotliEncoder.TryCompress(uncompressedData, compressedDataArrays[i], out int bytesWritten, compressLevelBrotli, 22)); + } + } + } + } + + /// + /// The perf tests for the instant decompression aren't exactly indicative of real-world scenarios since they require you to know + /// either the exact figure or the upper bound of the uncompressed size of your given compressed data. + /// + [Benchmark] + [MemberData(nameof(CanterburyCorpus))] + public void Decompress_Canterbury_WithoutState(int innerIterations, string uncompressedFileName) + { + byte[] compressedBytes = File.ReadAllBytes(CompressedTestFile(uncompressedFileName)); + ReadOnlySpan compressedData = new ReadOnlySpan(compressedBytes); + int uncompressedSize = (int)new FileInfo(uncompressedFileName).Length; + List uncompressedDataArrays = new List(innerIterations); + foreach (var iteration in Benchmark.Iterations) + { + for (int i = 0; i < innerIterations; i++) + { + uncompressedDataArrays.Add(new byte[uncompressedSize]); + } + using (iteration.StartMeasurement()) + { + for (int i = 0; i < innerIterations; i++) + { + Assert.True(BrotliDecoder.TryDecompress(compressedData, uncompressedDataArrays[i], out int bytesWritten)); + } + } + } + } + } +} diff --git a/external/corefx/src/System.IO.Compression.Brotli/tests/Performance/CompressionStreamPerfTests.Brotli.cs b/external/corefx/src/System.IO.Compression.Brotli/tests/Performance/CompressionStreamPerfTests.Brotli.cs new file mode 100644 index 0000000000..439f23961c --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/tests/Performance/CompressionStreamPerfTests.Brotli.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.IO.Compression +{ + public class BrotliStreamPerfTests : CompressionStreamPerfTestBase + { + public override Stream CreateStream(Stream stream, CompressionMode mode) => new BrotliStream(stream, mode); + public override Stream CreateStream(Stream stream, CompressionMode mode, bool leaveOpen) => new BrotliStream(stream, mode, leaveOpen); + public override Stream CreateStream(Stream stream, CompressionLevel level) => new BrotliStream(stream, level); + public override Stream CreateStream(Stream stream, CompressionLevel level, bool leaveOpen) => new BrotliStream(stream, level, leaveOpen); + public override Stream BaseStream(Stream stream) => ((BrotliStream)stream).BaseStream; + public override bool FlushCompletes { get => false; } + public override int BufferSize { get => 65520; } + protected override string CompressedTestFile(string uncompressedPath) => Path.Combine("BrotliTestData", Path.GetFileName(uncompressedPath) + ".br"); + } +} diff --git a/external/corefx/src/System.IO.Compression.Brotli/tests/Performance/Configurations.props b/external/corefx/src/System.IO.Compression.Brotli/tests/Performance/Configurations.props new file mode 100644 index 0000000000..9b2f102c3a --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/tests/Performance/Configurations.props @@ -0,0 +1,9 @@ + + + + + netcoreapp-Unix; + netcoreapp-Windows_NT; + + + diff --git a/external/corefx/src/System.IO.Compression.Brotli/tests/Performance/System.IO.Compression.Brotli.Performance.Tests.csproj b/external/corefx/src/System.IO.Compression.Brotli/tests/Performance/System.IO.Compression.Brotli.Performance.Tests.csproj new file mode 100644 index 0000000000..85e6b196f8 --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/tests/Performance/System.IO.Compression.Brotli.Performance.Tests.csproj @@ -0,0 +1,35 @@ + + + + + System.IO.Compression.Brotli.Performance.Tests + true + {1341F8C8-637A-49A1-BE0F-13867A634929} + + + + + + + + + + Common\System\IO\Compression\CompressionStreamTestBase.cs + + + Common\System\IO\Compression\CompressionStreamPerfTestBase.cs + + + + + %(RecursiveDir)%(Filename)%(Extension) + + + + + {69e46a6f-9966-45a5-8945-2559fe337827} + PerfRunner + + + + \ No newline at end of file diff --git a/external/corefx/src/System.IO.Compression.Brotli/tests/System.IO.Compression.Brotli.Tests.csproj b/external/corefx/src/System.IO.Compression.Brotli/tests/System.IO.Compression.Brotli.Tests.csproj new file mode 100644 index 0000000000..087a5bcf52 --- /dev/null +++ b/external/corefx/src/System.IO.Compression.Brotli/tests/System.IO.Compression.Brotli.Tests.csproj @@ -0,0 +1,44 @@ + + + + + {BC2E1649-291D-412E-9529-EDDA94FA7AD6} + $(DefineConstants);STREAM_MEMORY_OVERLOADS_AVAILABLE + + + + + + + + + + + Common\System\IO\Compression\CompressionStreamTestBase.cs + + + Common\System\IO\Compression\CompressionStreamUnitTestBase.cs + + + Common\System\IO\Compression\LocalMemoryStream.cs + + + Common\System\IO\Compression\StreamHelpers.cs + + + Common\System\IO\TempFile.cs + + + Common\System\Threading\Tasks\TaskToApm.cs + + + + + %(RecursiveDir)%(Filename)%(Extension) + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.IO.Compression.ZipFile/src/System.IO.Compression.ZipFile.csproj b/external/corefx/src/System.IO.Compression.ZipFile/src/System.IO.Compression.ZipFile.csproj index e1a7faeff6..ecb7160124 100644 --- a/external/corefx/src/System.IO.Compression.ZipFile/src/System.IO.Compression.ZipFile.csproj +++ b/external/corefx/src/System.IO.Compression.ZipFile/src/System.IO.Compression.ZipFile.csproj @@ -20,7 +20,6 @@ - diff --git a/external/corefx/src/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.cs b/external/corefx/src/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.cs index 33288aff96..3fef7883c9 100644 --- a/external/corefx/src/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.cs +++ b/external/corefx/src/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.ComponentModel; -using System.Diagnostics.Contracts; namespace System.IO.Compression { @@ -47,9 +46,6 @@ namespace System.IO.Compression /// A wrapper for the newly created entry. public static ZipArchiveEntry CreateEntryFromFile(this ZipArchive destination, string sourceFileName, string entryName) { - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); - return DoCreateEntryFromFile(destination, sourceFileName, entryName, null); } @@ -87,9 +83,6 @@ namespace System.IO.Compression // Checking of compressionLevel is passed down to DeflateStream and the IDeflater implementation // as it is a pluggable component that completely encapsulates the meaning of compressionLevel. - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); - return DoCreateEntryFromFile(destination, sourceFileName, entryName, compressionLevel); } @@ -162,8 +155,6 @@ namespace System.IO.Compression if (destinationDirectoryName == null) throw new ArgumentNullException(nameof(destinationDirectoryName)); - Contract.EndContractBlock(); - // Rely on Directory.CreateDirectory for validation of destinationDirectoryName. // Note that this will give us a good DirectoryInfo even if destinationDirectoryName exists: @@ -212,8 +203,6 @@ namespace System.IO.Compression // as it is a pluggable component that completely encapsulates the meaning of compressionLevel. // Argument checking gets passed down to FileStream's ctor and CreateEntry - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); using (Stream fs = new FileStream(sourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 0x1000, useAsync: false)) { @@ -311,8 +300,6 @@ namespace System.IO.Compression // Rely on FileStream's ctor for further checking destinationFileName parameter - Contract.EndContractBlock(); - FileMode fMode = overwrite ? FileMode.Create : FileMode.CreateNew; using (Stream fs = new FileStream(destinationFileName, fMode, FileAccess.Write, FileShare.None, bufferSize: 0x1000, useAsync: false)) diff --git a/external/corefx/src/System.IO.Compression.ZipFile/tests/System.IO.Compression.ZipFile.Tests.csproj b/external/corefx/src/System.IO.Compression.ZipFile/tests/System.IO.Compression.ZipFile.Tests.csproj index e5343a1ba6..b875e78c8f 100644 --- a/external/corefx/src/System.IO.Compression.ZipFile/tests/System.IO.Compression.ZipFile.Tests.csproj +++ b/external/corefx/src/System.IO.Compression.ZipFile/tests/System.IO.Compression.ZipFile.Tests.csproj @@ -3,6 +3,7 @@ {775727A6-DF41-4160-A7FD-180279A653C7} + $(DefineConstants);netcoreapp @@ -41,7 +42,7 @@ - + %(RecursiveDir)%(Filename)%(Extension) diff --git a/external/corefx/src/System.IO.Compression.ZipFile/tests/ZipFileInvalidFileTests.cs b/external/corefx/src/System.IO.Compression.ZipFile/tests/ZipFileInvalidFileTests.cs index 427428b455..9adfacdf30 100644 --- a/external/corefx/src/System.IO.Compression.ZipFile/tests/ZipFileInvalidFileTests.cs +++ b/external/corefx/src/System.IO.Compression.ZipFile/tests/ZipFileInvalidFileTests.cs @@ -213,6 +213,7 @@ namespace System.IO.Compression.Tests /// This test ensures that a zipfile with path names that are invalid to this OS will throw errors /// when an attempt is made to extract them. /// + [ActiveIssue(25665)] [Theory] [InlineData("NullCharFileName_FromWindows")] [InlineData("NullCharFileName_FromUnix")] diff --git a/external/corefx/src/System.IO.Compression/ref/System.IO.Compression.cs b/external/corefx/src/System.IO.Compression/ref/System.IO.Compression.cs index a247a468a1..67c9eb06fa 100644 --- a/external/corefx/src/System.IO.Compression/ref/System.IO.Compression.cs +++ b/external/corefx/src/System.IO.Compression/ref/System.IO.Compression.cs @@ -93,6 +93,8 @@ namespace System.IO.Compression public System.DateTimeOffset LastWriteTime { get { throw null; } set { } } public long Length { get { throw null; } } public string Name { get { throw null; } } + [CLSCompliant(false)] + public uint Crc32 { get { throw null; } } public void Delete() { } public System.IO.Stream Open() { throw null; } public override string ToString() { throw null; } diff --git a/external/corefx/src/System.IO.Compression/src/Interop/Interop.zlib.Windows.cs b/external/corefx/src/System.IO.Compression/src/Interop/Interop.zlib.Windows.cs index 1004846ac9..f01d0f251f 100644 --- a/external/corefx/src/System.IO.Compression/src/Interop/Interop.zlib.Windows.cs +++ b/external/corefx/src/System.IO.Compression/src/Interop/Interop.zlib.Windows.cs @@ -11,26 +11,26 @@ internal static partial class Interop { private static readonly byte[] ZLibVersion = { (byte)'1', (byte)'.', (byte)'2', (byte)'.', (byte)'3', 0 }; - [DllImport(Libraries.Zlib)] + [DllImport(Libraries.CompressionNative)] private extern static unsafe int deflateInit2_(byte* stream, int level, int method, int windowBits, int memLevel, int strategy, byte* version, int stream_size); - [DllImport(Libraries.Zlib)] + [DllImport(Libraries.CompressionNative)] private extern static unsafe int deflate(byte* stream, int flush); - [DllImport(Libraries.Zlib)] + [DllImport(Libraries.CompressionNative)] private extern static unsafe int deflateEnd(byte* strm); - [DllImport(Libraries.Zlib)] + [DllImport(Libraries.CompressionNative)] internal extern static unsafe uint crc32(uint crc, byte* buffer, int len); - [DllImport(Libraries.Zlib)] + [DllImport(Libraries.CompressionNative)] private extern static unsafe int inflateInit2_(byte* stream, int windowBits, byte* version, int stream_size); - [DllImport(Libraries.Zlib)] + [DllImport(Libraries.CompressionNative)] private extern static unsafe int inflate(byte* stream, int flush); - [DllImport(Libraries.Zlib)] + [DllImport(Libraries.CompressionNative)] private extern static unsafe int inflateEnd(byte* stream); internal static unsafe ZLibNative.ErrorCode DeflateInit2_( diff --git a/external/corefx/src/System.IO.Compression/src/Resources/Strings.resx b/external/corefx/src/System.IO.Compression/src/Resources/Strings.resx index 5e5ed003ea..0456f8ceab 100644 --- a/external/corefx/src/System.IO.Compression/src/Resources/Strings.resx +++ b/external/corefx/src/System.IO.Compression/src/Resources/Strings.resx @@ -1,5 +1,64 @@  + @@ -251,6 +310,6 @@ Zip 64 End of Central Directory Record not where indicated. - Illegal characters in path. + Illegal characters in path '{0}'. \ No newline at end of file diff --git a/external/corefx/src/System.IO.Compression/src/System.IO.Compression.csproj b/external/corefx/src/System.IO.Compression/src/System.IO.Compression.csproj index 471e378957..9cacdec02a 100644 --- a/external/corefx/src/System.IO.Compression/src/System.IO.Compression.csproj +++ b/external/corefx/src/System.IO.Compression/src/System.IO.Compression.csproj @@ -55,7 +55,6 @@ - @@ -101,9 +100,9 @@ - + @@ -112,7 +111,6 @@ - diff --git a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/Crc32Helper.ZLib.cs b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/Crc32Helper.ZLib.cs index bbfe6acd6e..a89baad516 100644 --- a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/Crc32Helper.ZLib.cs +++ b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/Crc32Helper.ZLib.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Runtime.InteropServices; namespace System.IO.Compression { @@ -12,7 +13,7 @@ namespace System.IO.Compression public static unsafe uint UpdateCrc32(uint crc32, byte[] buffer, int offset, int length) { Debug.Assert((buffer != null) && (offset >= 0) && (length >= 0) && (offset <= buffer.Length - length)); - fixed (byte* bufferPtr = buffer) + fixed (byte* bufferPtr = &buffer[offset]) { return Interop.zlib.crc32(crc32, bufferPtr, length); } @@ -20,7 +21,7 @@ namespace System.IO.Compression public static unsafe uint UpdateCrc32(uint crc32, ReadOnlySpan buffer) { - fixed (byte* bufferPtr = &buffer.DangerousGetPinnableReference()) + fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) { return Interop.zlib.crc32(crc32, bufferPtr, buffer.Length); } diff --git a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateManaged/DeflateInput.cs b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateManaged/DeflateInput.cs index b49dcbe4c3..64d793da38 100644 --- a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateManaged/DeflateInput.cs +++ b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateManaged/DeflateInput.cs @@ -28,7 +28,7 @@ namespace System.IO.Compression StartIndex = state._startIndex; } - internal struct InputState + internal readonly struct InputState { internal readonly int _count; internal readonly int _startIndex; diff --git a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateManaged/OutputBuffer.cs b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateManaged/OutputBuffer.cs index e9a72e4ec6..7fbef8bee8 100644 --- a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateManaged/OutputBuffer.cs +++ b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateManaged/OutputBuffer.cs @@ -106,7 +106,7 @@ namespace System.IO.Compression _bitCount = state._bitCount; } - internal struct BufferState + internal readonly struct BufferState { internal readonly int _pos; // position internal readonly uint _bitBuf; // store uncomplete bits diff --git a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/DeflateStream.cs b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/DeflateStream.cs index 9371a98fdd..b0ec44a643 100644 --- a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/DeflateStream.cs +++ b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/DeflateStream.cs @@ -5,6 +5,7 @@ using System.Buffers; using System.Diagnostics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -270,8 +271,6 @@ namespace System.IO.Compression if (_inflater.Finished()) { - // if we finished decompressing, we can't have anything left in the outputwindow. - Debug.Assert(_inflater.AvailableOutput == 0, "We should have copied all stuff out!"); break; } @@ -495,7 +494,7 @@ namespace System.IO.Compression unsafe { // Pass new bytes through deflater and write them too: - fixed (byte* bufferPtr = &source.DangerousGetPinnableReference()) + fixed (byte* bufferPtr = &MemoryMarshal.GetReference(source)) { _deflater.SetInput(bufferPtr, source.Length); WriteDeflaterOutput(); @@ -844,7 +843,6 @@ namespace System.IO.Compression Debug.Assert(oldValue == 1, $"Expected {nameof(_activeAsyncOperation)} to be 1, got {oldValue}"); } - [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowInvalidBeginCall() { throw new InvalidOperationException(SR.InvalidBeginCall); diff --git a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Deflater.cs b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Deflater.cs index 35cd2af261..fc3b8ad7aa 100644 --- a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Deflater.cs +++ b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Deflater.cs @@ -4,7 +4,6 @@ using System.Buffers; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Security; using ZErrorCode = System.IO.Compression.ZLibNative.ErrorCode; @@ -75,7 +74,6 @@ namespace System.IO.Compression GC.SuppressFinalize(this); } - [SecuritySafeCritical] private void Dispose(bool disposing) { if (!_isDisposed) @@ -93,7 +91,7 @@ namespace System.IO.Compression internal unsafe void SetInput(ReadOnlyMemory inputBuffer) { Debug.Assert(NeedsInput(), "We have something left in previous input!"); - Debug.Assert(_inputBufferHandle.PinnedPointer == null); + Debug.Assert(!_inputBufferHandle.HasPointer); if (0 == inputBuffer.Length) { @@ -104,7 +102,7 @@ namespace System.IO.Compression { _inputBufferHandle = inputBuffer.Retain(pin: true); - _zlibStream.NextIn = (IntPtr)_inputBufferHandle.PinnedPointer; + _zlibStream.NextIn = (IntPtr)_inputBufferHandle.Pointer; _zlibStream.AvailIn = (uint)inputBuffer.Length; } } @@ -113,7 +111,7 @@ namespace System.IO.Compression { Debug.Assert(NeedsInput(), "We have something left in previous input!"); Debug.Assert(inputBufferPtr != null); - Debug.Assert(_inputBufferHandle.PinnedPointer == null); + Debug.Assert(!_inputBufferHandle.HasPointer); if (count == 0) { @@ -129,8 +127,6 @@ namespace System.IO.Compression internal int GetDeflateOutput(byte[] outputBuffer) { - Contract.Ensures(Contract.Result() >= 0 && Contract.Result() <= outputBuffer.Length); - Debug.Assert(null != outputBuffer, "Can't pass in a null output buffer!"); Debug.Assert(!NeedsInput(), "GetDeflateOutput should only be called after providing input"); @@ -173,15 +169,6 @@ namespace System.IO.Compression { Debug.Assert(null != outputBuffer, "Can't pass in a null output buffer!"); Debug.Assert(outputBuffer.Length > 0, "Can't pass in an empty output buffer!"); - Debug.Assert(NeedsInput(), "We have something left in previous input!"); - unsafe - { - Debug.Assert(_inputBufferHandle.PinnedPointer == null); - } - - // Note: we require that NeedsInput() == true, i.e. that 0 == _zlibStream.AvailIn. - // If there is still input left we should never be getting here; instead we - // should be calling GetDeflateOutput. ZErrorCode errC = ReadDeflateOutput(outputBuffer, ZFlushCode.Finish, out bytesRead); return errC == ZErrorCode.StreamEnd; @@ -195,10 +182,8 @@ namespace System.IO.Compression Debug.Assert(null != outputBuffer, "Can't pass in a null output buffer!"); Debug.Assert(outputBuffer.Length > 0, "Can't pass in an empty output buffer!"); Debug.Assert(NeedsInput(), "We have something left in previous input!"); - unsafe - { - Debug.Assert(_inputBufferHandle.PinnedPointer == null); - } + Debug.Assert(!_inputBufferHandle.HasPointer); + // Note: we require that NeedsInput() == true, i.e. that 0 == _zlibStream.AvailIn. // If there is still input left we should never be getting here; instead we @@ -217,7 +202,6 @@ namespace System.IO.Compression } } - [SecuritySafeCritical] private void DeflateInit(ZLibNative.CompressionLevel compressionLevel, int windowBits, int memLevel, ZLibNative.CompressionStrategy strategy) { @@ -251,7 +235,6 @@ namespace System.IO.Compression } } - [SecuritySafeCritical] private ZErrorCode Deflate(ZFlushCode flushCode) { ZErrorCode errC; diff --git a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Inflater.cs b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Inflater.cs index 8950ceb7d9..e045555d36 100644 --- a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Inflater.cs +++ b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Inflater.cs @@ -39,7 +39,7 @@ namespace System.IO.Compression /// /// Returns true if the end of the stream has been reached. /// - public bool Finished() => _finished && _zlibStream.AvailIn == 0 && _zlibStream.AvailOut == 0; + public bool Finished() => _finished; public unsafe bool Inflate(out byte b) { @@ -70,7 +70,7 @@ namespace System.IO.Compression if (destination.Length == 0) return 0; - fixed (byte* bufPtr = &destination.DangerousGetPinnableReference()) + fixed (byte* bufPtr = &MemoryMarshal.GetReference(destination)) { return InflateVerified(bufPtr, destination.Length); } @@ -119,7 +119,6 @@ namespace System.IO.Compression } } - [SecuritySafeCritical] private void Dispose(bool disposing) { if (!_isDisposed) @@ -148,7 +147,6 @@ namespace System.IO.Compression /// /// Creates the ZStream that will handle inflation. /// - [SecuritySafeCritical] private void InflateInit(int windowBits) { ZLibNative.ErrorCode error; @@ -200,7 +198,6 @@ namespace System.IO.Compression /// /// Wrapper around the ZLib inflate function /// - [SecuritySafeCritical] private ZLibNative.ErrorCode Inflate(ZLibNative.FlushCode flushCode) { ZLibNative.ErrorCode errC; diff --git a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/ZLibException.cs b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/ZLibException.cs index c7b2a9c5f7..13d8e795da 100644 --- a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/ZLibException.cs +++ b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/ZLibException.cs @@ -2,20 +2,20 @@ // 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.Security; - -using ZErrorCode = System.IO.Compression.ZLibNative.ErrorCode; +using System.Runtime.Serialization; namespace System.IO.Compression { /// /// This is the exception that is thrown when a ZLib returns an error code indicating an unrecoverable error. /// - internal partial class ZLibException : IOException + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class ZLibException : IOException, ISerializable { private readonly string _zlibErrorContext = string.Empty; private readonly string _zlibErrorMessage = string.Empty; - private readonly ZErrorCode _zlibErrorCode = ZErrorCode.Ok; + private readonly ZLibNative.ErrorCode _zlibErrorCode = ZLibNative.ErrorCode.Ok; /// /// This is the preferred constructor to use. @@ -28,7 +28,7 @@ namespace System.IO.Compression public ZLibException(string message, string zlibErrorContext, int zlibErrorCode, string zlibErrorMessage) : base(message) { _zlibErrorContext = zlibErrorContext; - _zlibErrorCode = (ZErrorCode)zlibErrorCode; + _zlibErrorCode = (ZLibNative.ErrorCode)zlibErrorCode; _zlibErrorMessage = zlibErrorMessage; } @@ -47,5 +47,25 @@ namespace System.IO.Compression /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, or a null. public ZLibException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Initializes a new ZLibException with serialized data. + /// + /// The SerializationInfo that holds the serialized object data about the exception being thrown. + /// The StreamingContext that contains contextual information about the source or destination. + protected ZLibException(SerializationInfo info, StreamingContext context) : base(info, context) + { + _zlibErrorContext = info.GetString("zlibErrorContext"); + _zlibErrorCode = (ZLibNative.ErrorCode)info.GetInt32("zlibErrorCode"); + _zlibErrorMessage = info.GetString("zlibErrorMessage"); + } + + void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context) + { + base.GetObjectData(si, context); + si.AddValue("zlibErrorContext", _zlibErrorContext); + si.AddValue("zlibErrorCode", (int)_zlibErrorCode); + si.AddValue("zlibErrorMessage", _zlibErrorMessage); + } } } diff --git a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/ZLibNative.cs b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/ZLibNative.cs index f61af39965..68a9d82d15 100644 --- a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/ZLibNative.cs +++ b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/ZLibNative.cs @@ -1,8 +1,7 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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.Diagnostics.Contracts; using System.Runtime.InteropServices; using System.Security; @@ -168,14 +167,12 @@ namespace System.IO.Compression /// false, which can for instance happen if the underlying ZLib XxxxEnd /// routines return an failure error code. /// - [SecurityCritical] public sealed class ZLibStreamHandle : SafeHandle { public enum State { NotInitialized, InitializedForDeflate, InitializedForInflate, Disposed } private ZStream _zStream; - [SecurityCritical] private volatile State _initializationState; @@ -191,19 +188,15 @@ namespace System.IO.Compression public override bool IsInvalid { - [SecurityCritical] get { return handle == new IntPtr(-1); } } public State InitializationState { - [Pure] - [SecurityCritical] get { return _initializationState; } } - [SecurityCritical] protected override bool ReleaseHandle() { switch (InitializationState) @@ -218,30 +211,28 @@ namespace System.IO.Compression public IntPtr NextIn { - [SecurityCritical] get { return _zStream.nextIn; } - [SecurityCritical] set { _zStream.nextIn = value; } + get { return _zStream.nextIn; } + set { _zStream.nextIn = value; } } public uint AvailIn { - [SecurityCritical] get { return _zStream.availIn; } - [SecurityCritical] set { _zStream.availIn = value; } + get { return _zStream.availIn; } + set { _zStream.availIn = value; } } public IntPtr NextOut { - [SecurityCritical] get { return _zStream.nextOut; } - [SecurityCritical] set { _zStream.nextOut = value; } + get { return _zStream.nextOut; } + set { _zStream.nextOut = value; } } public uint AvailOut { - [SecurityCritical] get { return _zStream.availOut; } - [SecurityCritical] set { _zStream.availOut = value; } + get { return _zStream.availOut; } + set { _zStream.availOut = value; } } - [Pure] - [SecurityCritical] private void EnsureNotDisposed() { if (InitializationState == State.Disposed) @@ -249,8 +240,6 @@ namespace System.IO.Compression } - [Pure] - [SecurityCritical] private void EnsureState(State requiredState) { if (InitializationState != requiredState) @@ -258,7 +247,6 @@ namespace System.IO.Compression } - [SecurityCritical] public ErrorCode DeflateInit2_(CompressionLevel level, int windowBits, int memLevel, CompressionStrategy strategy) { EnsureNotDisposed(); @@ -271,7 +259,6 @@ namespace System.IO.Compression } - [SecurityCritical] public ErrorCode Deflate(FlushCode flush) { EnsureNotDisposed(); @@ -280,7 +267,6 @@ namespace System.IO.Compression } - [SecurityCritical] public ErrorCode DeflateEnd() { EnsureNotDisposed(); @@ -293,7 +279,6 @@ namespace System.IO.Compression } - [SecurityCritical] public ErrorCode InflateInit2_(int windowBits) { EnsureNotDisposed(); @@ -306,7 +291,6 @@ namespace System.IO.Compression } - [SecurityCritical] public ErrorCode Inflate(FlushCode flush) { EnsureNotDisposed(); @@ -315,7 +299,6 @@ namespace System.IO.Compression } - [SecurityCritical] public ErrorCode InflateEnd() { EnsureNotDisposed(); @@ -328,11 +311,9 @@ namespace System.IO.Compression } // This can work even after XxflateEnd(). - [SecurityCritical] public string GetErrorMessage() => _zStream.msg != ZNullPtr ? Marshal.PtrToStringAnsi(_zStream.msg) : string.Empty; } - [SecurityCritical] public static ErrorCode CreateZLibStreamForDeflate(out ZLibStreamHandle zLibStreamHandle, CompressionLevel level, int windowBits, int memLevel, CompressionStrategy strategy) { @@ -341,7 +322,6 @@ namespace System.IO.Compression } - [SecurityCritical] public static ErrorCode CreateZLibStreamForInflate(out ZLibStreamHandle zLibStreamHandle, int windowBits) { zLibStreamHandle = new ZLibStreamHandle(); diff --git a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/ZLibException.Serialization.cs b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/ZLibException.Serialization.cs deleted file mode 100644 index 3067ee58e4..0000000000 --- a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/ZLibException.Serialization.cs +++ /dev/null @@ -1,27 +0,0 @@ -// 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.Runtime.Serialization; - -namespace System.IO.Compression -{ - [Serializable] - internal partial class ZLibException : IOException, ISerializable - { - /// - /// Initializes a new ZLibException with serialized data. - /// - /// The SerializationInfo that holds the serialized object data about the exception being thrown. - /// The StreamingContext that contains contextual information about the source or destination. - protected ZLibException(SerializationInfo info, StreamingContext context) : base(info, context) - { - throw new PlatformNotSupportedException(); - } - - void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context) - { - base.GetObjectData(si, context); - } - } -} diff --git a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/ZipArchive.cs b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/ZipArchive.cs index afd41a807b..3d5a22eab4 100644 --- a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/ZipArchive.cs +++ b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/ZipArchive.cs @@ -8,7 +8,6 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Text; namespace System.IO.Compression @@ -123,8 +122,6 @@ namespace System.IO.Compression if (stream == null) throw new ArgumentNullException(nameof(stream)); - Contract.EndContractBlock(); - EntryNameEncoding = entryNameEncoding; Init(stream, mode, leaveOpen); } @@ -139,8 +136,6 @@ namespace System.IO.Compression { get { - Contract.Ensures(Contract.Result>() != null); - if (_mode == ZipArchiveMode.Create) throw new NotSupportedException(SR.EntriesInCreateMode); @@ -158,11 +153,6 @@ namespace System.IO.Compression { get { - Contract.Ensures( - Contract.Result() == ZipArchiveMode.Create - || Contract.Result() == ZipArchiveMode.Read - || Contract.Result() == ZipArchiveMode.Update); - return _mode; } } @@ -184,9 +174,6 @@ namespace System.IO.Compression /// A wrapper for the newly created file entry in the archive. public ZipArchiveEntry CreateEntry(string entryName) { - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); - return DoCreateEntry(entryName, null); } @@ -202,9 +189,6 @@ namespace System.IO.Compression /// A wrapper for the newly created file entry in the archive. public ZipArchiveEntry CreateEntry(string entryName, CompressionLevel compressionLevel) { - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); - return DoCreateEntry(entryName, compressionLevel); } @@ -261,7 +245,6 @@ namespace System.IO.Compression { if (entryName == null) throw new ArgumentNullException(nameof(entryName)); - Contract.EndContractBlock(); if (_mode == ZipArchiveMode.Create) throw new NotSupportedException(SR.EntriesInCreateMode); @@ -319,8 +302,6 @@ namespace System.IO.Compression private ZipArchiveEntry DoCreateEntry(string entryName, CompressionLevel? compressionLevel) { - Contract.Ensures(Contract.Result() != null); - if (entryName == null) throw new ArgumentNullException(nameof(entryName)); diff --git a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs index f99101a4f1..dc752626d2 100644 --- a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs +++ b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; using System.Text; namespace System.IO.Compression @@ -145,6 +144,9 @@ namespace System.IO.Compression /// public ZipArchive Archive => _archive; + [CLSCompliant(false)] + public uint Crc32 => _crc32; + /// /// The compressed size of the entry. If the archive that the entry belongs to is in Create mode, attempts to get this property will always throw an exception. If the archive that the entry belongs to is in update mode, this property will only be valid if the entry has not been opened. /// @@ -153,8 +155,6 @@ namespace System.IO.Compression { get { - Contract.Ensures(Contract.Result() >= 0); - if (_everOpenedForWrite) throw new InvalidOperationException(SR.LengthAfterWrite); return _compressedSize; @@ -181,7 +181,6 @@ namespace System.IO.Compression { get { - Contract.Ensures(Contract.Result() != null); return _storedEntryName; } @@ -242,8 +241,6 @@ namespace System.IO.Compression { get { - Contract.Ensures(Contract.Result() >= 0); - if (_everOpenedForWrite) throw new InvalidOperationException(SR.LengthAfterWrite); return _uncompressedSize; @@ -288,8 +285,6 @@ namespace System.IO.Compression /// The ZipArchive that this entry belongs to has been disposed. public Stream Open() { - Contract.Ensures(Contract.Result() != null); - ThrowIfInvalidArchive(); switch (_archive.Mode) @@ -311,8 +306,6 @@ namespace System.IO.Compression /// FullName of the entry public override string ToString() { - Contract.Ensures(Contract.Result() != null); - return FullName; } @@ -1117,8 +1110,6 @@ namespace System.IO.Compression { get { - Contract.Ensures(Contract.Result() >= 0); - ThrowIfDisposed(); return _position; } @@ -1170,7 +1161,6 @@ namespace System.IO.Compression throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentNeedNonNegative); if ((buffer.Length - offset) < count) throw new ArgumentException(SR.OffsetLengthInvalid); - Contract.EndContractBlock(); ThrowIfDisposed(); Debug.Assert(CanWrite); diff --git a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/ZipBlocks.cs b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/ZipBlocks.cs index 8a80c3f160..e63cf1316b 100644 --- a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/ZipBlocks.cs +++ b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/ZipBlocks.cs @@ -371,7 +371,7 @@ namespace System.IO.Compression } } - internal struct ZipLocalFileHeader + internal readonly struct ZipLocalFileHeader { public const uint DataDescriptorSignature = 0x08074B50; public const uint SignatureConstant = 0x04034B50; diff --git a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/ZipCustomStreams.cs b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/ZipCustomStreams.cs index 899e890d35..e8d8f714cd 100644 --- a/external/corefx/src/System.IO.Compression/src/System/IO/Compression/ZipCustomStreams.cs +++ b/external/corefx/src/System.IO.Compression/src/System/IO/Compression/ZipCustomStreams.cs @@ -2,7 +2,6 @@ // 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.Diagnostics.Contracts; using System.Diagnostics; namespace System.IO.Compression @@ -168,8 +167,6 @@ namespace System.IO.Compression { get { - Contract.Ensures(Contract.Result() >= 0); - ThrowIfDisposed(); return _endInSuperStream - _startInSuperStream; @@ -180,8 +177,6 @@ namespace System.IO.Compression { get { - Contract.Ensures(Contract.Result() >= 0); - ThrowIfDisposed(); return _positionInSuperStream - _startInSuperStream; @@ -329,7 +324,6 @@ namespace System.IO.Compression { get { - Contract.Ensures(Contract.Result() >= 0); ThrowIfDisposed(); return _position; } @@ -381,7 +375,6 @@ namespace System.IO.Compression throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentNeedNonNegative); if ((buffer.Length - offset) < count) throw new ArgumentException(SR.OffsetLengthInvalid); - Contract.EndContractBlock(); // if we're not actually writing anything, we don't want to trigger as if we did write something ThrowIfDisposed(); diff --git a/external/corefx/src/System.IO.Compression/tests/AsyncStreamTests.cs b/external/corefx/src/System.IO.Compression/tests/AsyncStreamTests.cs deleted file mode 100644 index 6263a1e824..0000000000 --- a/external/corefx/src/System.IO.Compression/tests/AsyncStreamTests.cs +++ /dev/null @@ -1,264 +0,0 @@ -// 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.Threading.Tasks; -using Xunit; - -namespace System.IO.Compression.Tests -{ - public abstract class CompressionStreamAsyncTestBase - { - public abstract bool StripHeaders { get; } - public abstract Task ReadAsync(Stream unzip, byte[] buffer, int offset, int count); - public abstract Task WriteAsync(Stream unzip, byte[] buffer, int offset, int count); - public abstract Stream CreateStream(Stream stream, CompressionMode mode); - public abstract Stream CreateStream(Stream stream, CompressionMode mode, bool leaveOpen); - - protected static string gzTestFile(string fileName) { return Path.Combine("GZTestData", fileName); } - - [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Full Framework Flush is a no-op.")] - public async Task OverlappingFlushAsync_DuringFlushAsync() - { - byte[] buffer = null; - string testFilePath = gzTestFile("GZTestDocument.pdf"); - using (var origStream = await LocalMemoryStream.readAppFileAsync(testFilePath)) - { - buffer = origStream.ToArray(); - } - - using (var writeStream = new ManualSyncMemoryStream(false)) - using (var zip = CreateStream(writeStream, CompressionMode.Compress)) - { - Task task = null; - try - { - writeStream.manualResetEvent.Set(); - await WriteAsync(zip, buffer, 0, buffer.Length); - writeStream.manualResetEvent.Reset(); - writeStream.WriteHit = false; - task = zip.FlushAsync(); - Assert.True(writeStream.WriteHit); - Assert.Throws(() => { zip.FlushAsync(); }); // "overlapping flushes" - } - finally - { - // Unblock Async operations - writeStream.manualResetEvent.Set(); - // The original WriteAsync should be able to complete - Assert.True(task.Wait(100 * 500)); - } - } - } - - [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Full Framework Flush is a no-op.")] - public async Task OverlappingFlushAsync_DuringWriteAsync() - { - byte[] buffer = null; - string testFilePath = gzTestFile("GZTestDocument.pdf"); - using (var origStream = await LocalMemoryStream.readAppFileAsync(testFilePath)) - { - buffer = origStream.ToArray(); - } - - using (var writeStream = new ManualSyncMemoryStream(false)) - using (var zip = CreateStream(writeStream, CompressionMode.Compress)) - { - Task task = null; - try - { - task = WriteAsync(zip, buffer, 0, buffer.Length); - Assert.True(writeStream.WriteHit); - Assert.Throws(() => { zip.FlushAsync(); }); // "overlapping flushes" - } - finally - { - // Unblock Async operations - writeStream.manualResetEvent.Set(); - // The original WriteAsync should be able to complete - Assert.True(task.Wait(100 * 500)); - } - } - } - - [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Full Framework Flush is a no-op.")] - public async Task OverlappingFlushAsync_DuringReadAsync() - { - byte[] buffer = new byte[32]; - string testFilePath = gzTestFile("GZTestDocument.pdf.gz"); - using (var readStream = await ManualSyncMemoryStream.GetStreamFromFileAsync(testFilePath, false, StripHeaders)) - using (var unzip = CreateStream(readStream, CompressionMode.Decompress, true)) - { - Task task = null; - try - { - readStream.manualResetEvent.Reset(); - readStream.ReadHit = false; - task = ReadAsync(unzip, buffer, 0, 32); - Assert.True(readStream.ReadHit); - Assert.Throws(() => { unzip.FlushAsync(); }); // "overlapping read" - } - finally - { - // Unblock Async operations - readStream.manualResetEvent.Set(); - // The original ReadAsync should be able to complete - Assert.True(task.Wait(100 * 500)); - } - } - } - - [Fact] - public async Task OverlappingWriteAsync() - { - byte[] buffer = null; - string testFilePath = gzTestFile("GZTestDocument.pdf"); - using (var origStream = await LocalMemoryStream.readAppFileAsync(testFilePath)) - { - buffer = origStream.ToArray(); - } - - using (var writeStream = new ManualSyncMemoryStream(false)) - using (var zip = CreateStream(writeStream, CompressionMode.Compress)) - { - Task task = null; - try - { - task = WriteAsync(zip, buffer, 0, buffer.Length); // write needs to be bigger than the internal write buffer - Assert.Throws(() => { zip.WriteAsync(buffer, 32, 32); }); // "overlapping write" - } - finally - { - // Unblock Async operations - writeStream.manualResetEvent.Set(); - // The original WriteAsync should be able to complete - Assert.True(task.Wait(100 * 500)); - Assert.True(writeStream.WriteHit); - } - } - } - - [Fact] - public async Task OverlappingReadAsync() - { - byte[] buffer = new byte[32]; - string testFilePath = gzTestFile("GZTestDocument.pdf.gz"); - using (var readStream = await ManualSyncMemoryStream.GetStreamFromFileAsync(testFilePath, false, StripHeaders)) - using (var unzip = CreateStream(readStream, CompressionMode.Decompress, true)) - { - Task task = null; - try - { - task = ReadAsync(unzip, buffer, 0, 32); - Assert.Throws(() => { ReadAsync(unzip, buffer, 0, 32); }); // "overlapping read" - } - finally - { - // Unblock Async operations - readStream.manualResetEvent.Set(); - // The original ReadAsync should be able to complete - Assert.True(task.Wait(100 * 500)); - Assert.True(readStream.ReadHit); - } - } - } - - [Fact] - public async Task DecompressWorks() - { - var compareStream = await LocalMemoryStream.readAppFileAsync(gzTestFile("GZTestDocument.txt")); - var gzStream = await LocalMemoryStream.readAppFileAsync(gzTestFile("GZTestDocument.txt.gz")); - - await DecompressAsync(compareStream, gzStream); - } - - [Fact] - public async Task DecompressWorksWithBinaryFile() - { - var compareStream = await LocalMemoryStream.readAppFileAsync(gzTestFile("GZTestDocument.doc")); - var gzStream = await LocalMemoryStream.readAppFileAsync(gzTestFile("GZTestDocument.doc.gz")); - - await DecompressAsync(compareStream, gzStream); - } - - // Making this async since regular read/write are tested below - private async Task DecompressAsync(MemoryStream compareStream, MemoryStream gzStream) - { - var sourceStream = StripHeaders ? StripHeaderAndFooter.Strip(gzStream) : gzStream; - - var ms = new MemoryStream(); - var zip = CreateStream(sourceStream, CompressionMode.Decompress); - - var Stream = new MemoryStream(); - - int _bufferSize = 1024; - var bytes = new byte[_bufferSize]; - bool finished = false; - int retCount; - while (!finished) - { - retCount = await ReadAsync(zip, bytes, 0, _bufferSize); - - if (retCount != 0) - await Stream.WriteAsync(bytes, 0, retCount); - else - finished = true; - } - - Stream.Position = 0; - compareStream.Position = 0; - - byte[] compareArray = compareStream.ToArray(); - byte[] writtenArray = Stream.ToArray(); - - Assert.Equal(compareArray.Length, writtenArray.Length); - for (int i = 0; i < compareArray.Length; i++) - { - Assert.Equal(compareArray[i], writtenArray[i]); - } - } - } - - public sealed class TaskDeflateStreamTests : CompressionStreamAsyncTestBase - { - public override bool StripHeaders => true; - public override Task ReadAsync(Stream unzip, byte[] buffer, int offset, int count) => ((DeflateStream)unzip).ReadAsync(buffer, offset, count); - public override Task WriteAsync(Stream unzip, byte[] buffer, int offset, int count) => ((DeflateStream)unzip).WriteAsync(buffer, offset, count); - public override Stream CreateStream(Stream stream, CompressionMode mode) => new DeflateStream(stream, mode); - public override Stream CreateStream(Stream stream, CompressionMode mode, bool leaveOpen) => new DeflateStream(stream, mode, leaveOpen); - } - - public sealed class TaskGZipStreamTests : CompressionStreamAsyncTestBase - { - public override bool StripHeaders => false; - public override Task ReadAsync(Stream unzip, byte[] buffer, int offset, int count) => ((GZipStream)unzip).ReadAsync(buffer, offset, count); - public override Task WriteAsync(Stream unzip, byte[] buffer, int offset, int count) => ((GZipStream)unzip).WriteAsync(buffer, offset, count); - public override Stream CreateStream(Stream stream, CompressionMode mode) => new GZipStream(stream, mode); - public override Stream CreateStream(Stream stream, CompressionMode mode, bool leaveOpen) => new GZipStream(stream, mode, leaveOpen); - } - - public sealed class BeginEndDeflateStreamTests : CompressionStreamAsyncTestBase - { - public override bool StripHeaders => true; - public override Task ReadAsync(Stream unzip, byte[] buffer, int offset, int count) => - Task.Factory.FromAsync(((DeflateStream)unzip).BeginRead, ((DeflateStream)unzip).EndRead, buffer, offset, count, null); - public override Task WriteAsync(Stream unzip, byte[] buffer, int offset, int count) => - Task.Factory.FromAsync(((DeflateStream)unzip).BeginWrite, ((DeflateStream)unzip).EndWrite, buffer, offset, count, null); - public override Stream CreateStream(Stream stream, CompressionMode mode) => new DeflateStream(stream, mode); - public override Stream CreateStream(Stream stream, CompressionMode mode, bool leaveOpen) => new DeflateStream(stream, mode, leaveOpen); - } - - public sealed class BeginEndGZipStreamTests : CompressionStreamAsyncTestBase - { - public override bool StripHeaders => false; - public override Task ReadAsync(Stream unzip, byte[] buffer, int offset, int count) => - Task.Factory.FromAsync(((GZipStream)unzip).BeginRead, ((GZipStream)unzip).EndRead, buffer, offset, count, null); - public override Task WriteAsync(Stream unzip, byte[] buffer, int offset, int count) => - Task.Factory.FromAsync(((GZipStream)unzip).BeginWrite, ((GZipStream)unzip).EndWrite, buffer, offset, count, null); - public override Stream CreateStream(Stream stream, CompressionMode mode) => new GZipStream(stream, mode); - public override Stream CreateStream(Stream stream, CompressionMode mode, bool leaveOpen) => new GZipStream(stream, mode, leaveOpen); - } -} diff --git a/external/corefx/src/System.IO.Compression/tests/CompressionStreamUnitTests.Deflate.cs b/external/corefx/src/System.IO.Compression/tests/CompressionStreamUnitTests.Deflate.cs new file mode 100644 index 0000000000..39e713de65 --- /dev/null +++ b/external/corefx/src/System.IO.Compression/tests/CompressionStreamUnitTests.Deflate.cs @@ -0,0 +1,111 @@ +// 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.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace System.IO.Compression +{ + public class DeflateStreamUnitTests : CompressionStreamUnitTestBase + { + public override Stream CreateStream(Stream stream, CompressionMode mode) => new DeflateStream(stream, mode); + public override Stream CreateStream(Stream stream, CompressionMode mode, bool leaveOpen) => new DeflateStream(stream, mode, leaveOpen); + public override Stream CreateStream(Stream stream, CompressionLevel level) => new DeflateStream(stream, level); + public override Stream CreateStream(Stream stream, CompressionLevel level, bool leaveOpen) => new DeflateStream(stream, level, leaveOpen); + public override Stream BaseStream(Stream stream) => ((DeflateStream)stream).BaseStream; + protected override string CompressedTestFile(string uncompressedPath) => Path.Combine("DeflateTestData", Path.GetFileName(uncompressedPath)); + + /// + /// Test to pass gzipstream data to a deflatestream + /// + [Theory] + [MemberData(nameof(UncompressedTestFiles))] + public async Task DecompressFailsWithRealGzStream(string uncompressedPath) + { + string fileName = Path.Combine("GZipTestData", Path.GetFileName(uncompressedPath) + ".gz"); + var baseStream = await LocalMemoryStream.readAppFileAsync(fileName); + var zip = CreateStream(baseStream, CompressionMode.Decompress); + int _bufferSize = 2048; + var bytes = new byte[_bufferSize]; + Assert.Throws(() => { zip.Read(bytes, 0, _bufferSize); }); + zip.Dispose(); + } + + [Fact] + public void Precancellation() + { + var ms = new MemoryStream(); + using (Stream compressor = new DeflateStream(ms, CompressionMode.Compress, leaveOpen: true)) + { + Assert.True(compressor.WriteAsync(new byte[1], 0, 1, new CancellationToken(true)).IsCanceled); + Assert.True(compressor.FlushAsync(new CancellationToken(true)).IsCanceled); + } + using (Stream decompressor = CreateStream(ms, CompressionMode.Decompress, leaveOpen: true)) + { + Assert.True(decompressor.ReadAsync(new byte[1], 0, 1, new CancellationToken(true)).IsCanceled); + } + } + + [Fact] + public void DerivedStream_ReadWriteSpan_UsesReadWriteArray() + { + var ms = new MemoryStream(); + using (var compressor = new DerivedDeflateStream(ms, CompressionMode.Compress, leaveOpen: true)) + { + compressor.Write(new Span(new byte[1])); + Assert.True(compressor.WriteArrayInvoked); + } + ms.Position = 0; + using (var compressor = new DerivedDeflateStream(ms, CompressionMode.Decompress, leaveOpen: true)) + { + compressor.Read(new Span(new byte[1])); + Assert.True(compressor.ReadArrayInvoked); + } + ms.Position = 0; + using (var compressor = new DerivedDeflateStream(ms, CompressionMode.Decompress, leaveOpen: true)) + { + compressor.ReadAsync(new Memory(new byte[1])).AsTask().Wait(); + Assert.True(compressor.ReadArrayInvoked); + } + ms.Position = 0; + using (var compressor = new DerivedDeflateStream(ms, CompressionMode.Compress, leaveOpen: true)) + { + compressor.WriteAsync(new ReadOnlyMemory(new byte[1])).Wait(); + Assert.True(compressor.WriteArrayInvoked); + } + } + + private sealed class DerivedDeflateStream : DeflateStream + { + public bool ReadArrayInvoked = false, WriteArrayInvoked = false; + internal DerivedDeflateStream(Stream stream, CompressionMode mode) : base(stream, mode) { } + internal DerivedDeflateStream(Stream stream, CompressionMode mode, bool leaveOpen) : base(stream, mode, leaveOpen) { } + + public override int Read(byte[] array, int offset, int count) + { + ReadArrayInvoked = true; + return base.Read(array, offset, count); + } + + public override Task ReadAsync(byte[] array, int offset, int count, CancellationToken cancellationToken) + { + ReadArrayInvoked = true; + return base.ReadAsync(array, offset, count, cancellationToken); + } + + public override void Write(byte[] array, int offset, int count) + { + WriteArrayInvoked = true; + base.Write(array, offset, count); + } + + public override Task WriteAsync(byte[] array, int offset, int count, CancellationToken cancellationToken) + { + WriteArrayInvoked = true; + return base.WriteAsync(array, offset, count, cancellationToken); + } + } + } +} diff --git a/external/corefx/src/System.IO.Compression/tests/CompressionStreamUnitTests.Gzip.cs b/external/corefx/src/System.IO.Compression/tests/CompressionStreamUnitTests.Gzip.cs new file mode 100644 index 0000000000..002c83cc87 --- /dev/null +++ b/external/corefx/src/System.IO.Compression/tests/CompressionStreamUnitTests.Gzip.cs @@ -0,0 +1,96 @@ +// 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.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace System.IO.Compression +{ + public class GzipStreamUnitTests : CompressionStreamUnitTestBase + { + public override Stream CreateStream(Stream stream, CompressionMode mode) => new GZipStream(stream, mode); + public override Stream CreateStream(Stream stream, CompressionMode mode, bool leaveOpen) => new GZipStream(stream, mode, leaveOpen); + public override Stream CreateStream(Stream stream, CompressionLevel level) => new GZipStream(stream, level); + public override Stream CreateStream(Stream stream, CompressionLevel level, bool leaveOpen) => new GZipStream(stream, level, leaveOpen); + public override Stream BaseStream(Stream stream) => ((GZipStream)stream).BaseStream; + protected override string CompressedTestFile(string uncompressedPath) => Path.Combine("GZipTestData", Path.GetFileName(uncompressedPath) + ".gz"); + + [Fact] + public void Precancellation() + { + var ms = new MemoryStream(); + using (Stream compressor = new GZipStream(ms, CompressionMode.Compress, leaveOpen: true)) + { + Assert.True(compressor.WriteAsync(new byte[1], 0, 1, new CancellationToken(true)).IsCanceled); + Assert.True(compressor.FlushAsync(new CancellationToken(true)).IsCanceled); + } + using (Stream decompressor = CreateStream(ms, CompressionMode.Decompress, leaveOpen: true)) + { + Assert.True(decompressor.ReadAsync(new byte[1], 0, 1, new CancellationToken(true)).IsCanceled); + } + } + + + [Fact] + public void DerivedStream_ReadWriteSpan_UsesReadWriteArray() + { + var ms = new MemoryStream(); + using (var compressor = new DerivedGZipStream(ms, CompressionMode.Compress, leaveOpen: true)) + { + compressor.Write(new Span(new byte[1])); + Assert.True(compressor.WriteArrayInvoked); + } + ms.Position = 0; + using (var compressor = new DerivedGZipStream(ms, CompressionMode.Decompress, leaveOpen: true)) + { + compressor.Read(new Span(new byte[1])); + Assert.True(compressor.ReadArrayInvoked); + } + ms.Position = 0; + using (var compressor = new DerivedGZipStream(ms, CompressionMode.Decompress, leaveOpen: true)) + { + compressor.ReadAsync(new Memory(new byte[1])).AsTask().Wait(); + Assert.True(compressor.ReadArrayInvoked); + } + ms.Position = 0; + using (var compressor = new DerivedGZipStream(ms, CompressionMode.Compress, leaveOpen: true)) + { + compressor.WriteAsync(new ReadOnlyMemory(new byte[1])).Wait(); + Assert.True(compressor.WriteArrayInvoked); + } + } + + private sealed class DerivedGZipStream : GZipStream + { + public bool ReadArrayInvoked = false, WriteArrayInvoked = false; + internal DerivedGZipStream(Stream stream, CompressionMode mode) : base(stream, mode) { } + internal DerivedGZipStream(Stream stream, CompressionMode mode, bool leaveOpen) : base(stream, mode, leaveOpen) { } + + public override int Read(byte[] array, int offset, int count) + { + ReadArrayInvoked = true; + return base.Read(array, offset, count); + } + + public override Task ReadAsync(byte[] array, int offset, int count, CancellationToken cancellationToken) + { + ReadArrayInvoked = true; + return base.ReadAsync(array, offset, count, cancellationToken); + } + + public override void Write(byte[] array, int offset, int count) + { + WriteArrayInvoked = true; + base.Write(array, offset, count); + } + + public override Task WriteAsync(byte[] array, int offset, int count, CancellationToken cancellationToken) + { + WriteArrayInvoked = true; + return base.WriteAsync(array, offset, count, cancellationToken); + } + } + } +} diff --git a/external/corefx/src/System.IO.Compression/tests/CompressionStreamUnitTests.NetFX.cs b/external/corefx/src/System.IO.Compression/tests/CompressionStreamUnitTests.NetFX.cs new file mode 100644 index 0000000000..7415b924ad --- /dev/null +++ b/external/corefx/src/System.IO.Compression/tests/CompressionStreamUnitTests.NetFX.cs @@ -0,0 +1,157 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace System.IO.Compression +{ + public class DeflateStreamUnitTests : CompressionStreamUnitTestBase + { + public override Stream CreateStream(Stream stream, CompressionMode mode) => new DeflateStream(stream, mode); + public override Stream CreateStream(Stream stream, CompressionMode mode, bool leaveOpen) => new DeflateStream(stream, mode, leaveOpen); + public override Stream CreateStream(Stream stream, CompressionLevel level) => new DeflateStream(stream, level); + public override Stream CreateStream(Stream stream, CompressionLevel level, bool leaveOpen) => new DeflateStream(stream, level, leaveOpen); + public override Stream BaseStream(Stream stream) => ((DeflateStream)stream).BaseStream; + public override bool FlushCompletes => false; + public override bool FlushNoOps => true; + protected override string CompressedTestFile(string uncompressedPath) => Path.Combine("DeflateTestData", Path.GetFileName(uncompressedPath)); + + [Theory] + [InlineData(true)] + [InlineData(true)] + public override void Write_ArgumentValidation(bool useAsync) + { + using (var decompressor = CreateStream(new MemoryStream(), CompressionMode.Compress)) + { + Assert.Throws(() => { if (useAsync) { decompressor.WriteAsync(null, 0, 0).Wait(); } else { decompressor.Write(null, 0, 0); } }); + Assert.Throws(() => { if (useAsync) { decompressor.WriteAsync(new byte[1], -1, 0).Wait(); } else { decompressor.Write(new byte[1], -1, 0); } }); + Assert.Throws(() => { if (useAsync) { decompressor.WriteAsync(new byte[1], 0, -1).Wait(); } else { decompressor.Write(new byte[1], 0, -1); } }); + Assert.Throws(null, () => { if (useAsync) { decompressor.WriteAsync(new byte[1], 0, 2).Wait(); } else { decompressor.Write(new byte[1], 0, 2); } }); + Assert.Throws(null, () => { if (useAsync) { decompressor.WriteAsync(new byte[1], 1, 1).Wait(); } else { decompressor.Write(new byte[1], 1, 1); } }); + Assert.Throws(() => useAsync ? decompressor.ReadAsync(new byte[1], 0, 1).Result : decompressor.Read(new byte[1], 0, 1)); + Assert.Throws(() => useAsync ? decompressor.ReadAsync(new byte[1], 1, 1).Result : decompressor.Read(new byte[1], 1, 1)); + if (useAsync) + { decompressor.WriteAsync(new byte[1], 0, 1).Wait(); } + else + { decompressor.Write(new byte[1], 0, 1); } + } + } + + [Theory] + [InlineData(true)] + [InlineData(true)] + public override void Read_ArgumentValidation(bool useAsync) + { + using (var decompressor = CreateStream(new MemoryStream(), CompressionMode.Decompress)) + { + Assert.Throws(() => useAsync ? decompressor.ReadAsync(null, 0, 0).Result : decompressor.Read(null, 0, 0)); + Assert.Throws(() => useAsync ? decompressor.ReadAsync(new byte[1], -1, 0).Result : decompressor.Read(new byte[1], -1, 0)); + Assert.Throws(() => useAsync ? decompressor.ReadAsync(new byte[1], 0, -1).Result : decompressor.Read(new byte[1], 0, -1)); + AssertExtensions.Throws(null, () => useAsync ? decompressor.ReadAsync(new byte[1], 0, 2).Result : decompressor.Read(new byte[1], 0, 2)); + AssertExtensions.Throws(null, () => useAsync ? decompressor.ReadAsync(new byte[1], 1, 1).Result : decompressor.Read(new byte[1], 1, 1)); + Assert.Throws(() => { if (useAsync) { decompressor.WriteAsync(new byte[1], 0, 1).Wait(); } else { decompressor.Write(new byte[1], 0, 1); } }); + Assert.Throws(() => { if (useAsync) { decompressor.WriteAsync(new byte[1], 1, 1).Wait(); } else { decompressor.Write(new byte[1], 1, 1); } }); + + var data = new byte[1] { 42 }; + Assert.Equal(0, useAsync ? decompressor.ReadAsync(data, 0, 0).Result : decompressor.Read(data, 0, 0)); + Assert.Equal(42, data[0]); + } + } + + /// + /// Test to pass gzipstream data to a deflatestream + /// + [Theory] + [MemberData(nameof(UncompressedTestFiles))] + public async Task DecompressFailsWithRealGzStream(string uncompressedPath) + { + string fileName = Path.Combine("GZipTestData", Path.GetFileName(uncompressedPath) + ".gz"); + var baseStream = await LocalMemoryStream.readAppFileAsync(fileName); + var zip = CreateStream(baseStream, CompressionMode.Decompress); + int _bufferSize = 2048; + var bytes = new byte[_bufferSize]; + Assert.Throws(() => { zip.Read(bytes, 0, _bufferSize); }); + zip.Dispose(); + } + } + + public class GzipStreamUnitTests : CompressionStreamUnitTestBase + { + public override Stream CreateStream(Stream stream, CompressionMode mode) => new GZipStream(stream, mode); + public override Stream CreateStream(Stream stream, CompressionMode mode, bool leaveOpen) => new GZipStream(stream, mode, leaveOpen); + public override Stream CreateStream(Stream stream, CompressionLevel level) => new GZipStream(stream, level); + public override Stream CreateStream(Stream stream, CompressionLevel level, bool leaveOpen) => new GZipStream(stream, level, leaveOpen); + public override Stream BaseStream(Stream stream) => ((GZipStream)stream).BaseStream; + public override bool FlushCompletes => false; + public override bool FlushNoOps => true; + protected override string CompressedTestFile(string uncompressedPath) => Path.Combine("GZipTestData", Path.GetFileName(uncompressedPath) + ".gz"); + + [Theory] + [InlineData(true)] + [InlineData(true)] + public override void Write_ArgumentValidation(bool useAsync) + { + using (var decompressor = CreateStream(new MemoryStream(), CompressionMode.Compress)) + { + Assert.Throws(() => { if (useAsync) { decompressor.WriteAsync(null, 0, 0).Wait(); } else { decompressor.Write(null, 0, 0); } }); + Assert.Throws(() => { if (useAsync) { decompressor.WriteAsync(new byte[1], -1, 0).Wait(); } else { decompressor.Write(new byte[1], -1, 0); } }); + Assert.Throws(() => { if (useAsync) { decompressor.WriteAsync(new byte[1], 0, -1).Wait(); } else { decompressor.Write(new byte[1], 0, -1); } }); + Assert.Throws(null, () => { if (useAsync) { decompressor.WriteAsync(new byte[1], 0, 2).Wait(); } else { decompressor.Write(new byte[1], 0, 2); } }); + Assert.Throws(null, () => { if (useAsync) { decompressor.WriteAsync(new byte[1], 1, 1).Wait(); } else { decompressor.Write(new byte[1], 1, 1); } }); + Assert.Throws(() => useAsync ? decompressor.ReadAsync(new byte[1], 0, 1).Result : decompressor.Read(new byte[1], 0, 1)); + Assert.Throws(() => useAsync ? decompressor.ReadAsync(new byte[1], 1, 1).Result : decompressor.Read(new byte[1], 1, 1)); + if (useAsync) + { decompressor.WriteAsync(new byte[1], 0, 1).Wait(); } + else + { decompressor.Write(new byte[1], 0, 1); } + } + } + + [Theory] + [InlineData(true)] + [InlineData(true)] + public override void Read_ArgumentValidation(bool useAsync) + { + using (var decompressor = CreateStream(new MemoryStream(), CompressionMode.Decompress)) + { + Assert.Throws(() => useAsync ? decompressor.ReadAsync(null, 0, 0).Result : decompressor.Read(null, 0, 0)); + Assert.Throws(() => useAsync ? decompressor.ReadAsync(new byte[1], -1, 0).Result : decompressor.Read(new byte[1], -1, 0)); + Assert.Throws(() => useAsync ? decompressor.ReadAsync(new byte[1], 0, -1).Result : decompressor.Read(new byte[1], 0, -1)); + AssertExtensions.Throws(null, () => useAsync ? decompressor.ReadAsync(new byte[1], 0, 2).Result : decompressor.Read(new byte[1], 0, 2)); + AssertExtensions.Throws(null, () => useAsync ? decompressor.ReadAsync(new byte[1], 1, 1).Result : decompressor.Read(new byte[1], 1, 1)); + Assert.Throws(() => { if (useAsync) { decompressor.WriteAsync(new byte[1], 0, 1).Wait(); } else { decompressor.Write(new byte[1], 0, 1); } }); + Assert.Throws(() => { if (useAsync) { decompressor.WriteAsync(new byte[1], 1, 1).Wait(); } else { decompressor.Write(new byte[1], 1, 1); } }); + + var data = new byte[1] { 42 }; + Assert.Equal(0, useAsync ? decompressor.ReadAsync(data, 0, 0).Result : decompressor.Read(data, 0, 0)); + Assert.Equal(42, data[0]); + } + } + + + + [Theory] + [InlineData(CompressionMode.Compress)] + [InlineData(CompressionMode.Decompress)] + public override async Task Dispose_FollowedByAsyncOperations(CompressionMode mode) + { + var ms = new MemoryStream(); + var compressor = CreateStream(ms, mode); + compressor.Dispose(); + + if (mode == CompressionMode.Compress) + await Assert.ThrowsAsync(async () => await compressor.WriteAsync(new byte[1], 0, 1)); + else + await Assert.ThrowsAsync(async () => await compressor.ReadAsync(new byte[1], 0, 1)); + await Assert.ThrowsAsync(async () => await compressor.FlushAsync()); + await Assert.ThrowsAsync(async () => await compressor.CopyToAsync(new MemoryStream())); + } + } +} diff --git a/external/corefx/src/System.IO.Compression/tests/DeflateStreamTests.cs b/external/corefx/src/System.IO.Compression/tests/DeflateStreamTests.cs deleted file mode 100644 index e2cf6737f7..0000000000 --- a/external/corefx/src/System.IO.Compression/tests/DeflateStreamTests.cs +++ /dev/null @@ -1,1031 +0,0 @@ -// 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.Collections.Generic; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Xunit; - -namespace System.IO.Compression.Tests -{ - public class DeflateStreamTests - { - static string gzTestFile(string fileName) => Path.Combine("GZTestData", fileName); - - [Fact] - public void BaseStream1() - { - var writeStream = new MemoryStream(); - var zip = new DeflateStream(writeStream, CompressionMode.Compress); - - Assert.Same(zip.BaseStream, writeStream); - writeStream.Dispose(); - } - - [Fact] - public void BaseStream2() - { - var ms = new MemoryStream(); - var zip = new DeflateStream(ms, CompressionMode.Decompress); - - Assert.Same(zip.BaseStream, ms); - ms.Dispose(); - } - - [Fact] - public async Task ModifyBaseStream() - { - var ms = await LocalMemoryStream.readAppFileAsync(gzTestFile("GZTestDocument.txt.gz")); - var newMs = StripHeaderAndFooter.Strip(ms); - - var zip = new DeflateStream(newMs, CompressionMode.Decompress); - int size = 1024; - byte[] bytes = new byte[size]; - zip.BaseStream.Read(bytes, 0, size); // This will throw if the underlying stream is not writable as expected - - zip.BaseStream.Position = 0; - await zip.BaseStream.ReadAsync(bytes, 0, size); - } - - [Fact] - public void DecompressCanRead() - { - var ms = new MemoryStream(); - var zip = new DeflateStream(ms, CompressionMode.Decompress); - - Assert.True(zip.CanRead); - - zip.Dispose(); - Assert.False(zip.CanRead); - } - - [Fact] - public void CompressCanWrite() - { - var ms = new MemoryStream(); - var zip = new DeflateStream(ms, CompressionMode.Compress); - Assert.True(zip.CanWrite); - - zip.Dispose(); - Assert.False(zip.CanWrite); - } - - [Fact] - public void CanDisposeBaseStream() - { - var ms = new MemoryStream(); - var zip = new DeflateStream(ms, CompressionMode.Compress); - ms.Dispose(); // This would throw if this was invalid - } - - [Fact] - public void CanDisposeDeflateStream() - { - var ms = new MemoryStream(); - var zip = new DeflateStream(ms, CompressionMode.Compress); - zip.Dispose(); - - // Base Stream should be null after dispose - Assert.Null(zip.BaseStream); - - zip.Dispose(); // Should be a no-op - } - - [Fact] - public async Task CanReadBaseStreamAfterDispose() - { - var ms = await LocalMemoryStream.readAppFileAsync(gzTestFile("GZTestDocument.txt.gz")); - var newMs = StripHeaderAndFooter.Strip(ms); - - var zip = new DeflateStream(newMs, CompressionMode.Decompress, leaveOpen: true); - var baseStream = zip.BaseStream; - zip.Dispose(); - - int size = 1024; - byte[] bytes = new byte[size]; - baseStream.Read(bytes, 0, size); // This will throw if the underlying stream is not writable as expected - - baseStream.Position = 0; - await baseStream.ReadAsync(bytes, 0, size); - } - - [Fact] - public async Task DecompressFailsWithRealGzStream() - { - string[] files = { gzTestFile("GZTestDocument.doc.gz"), gzTestFile("GZTestDocument.txt.gz") }; - foreach (string fileName in files) - { - var baseStream = await LocalMemoryStream.readAppFileAsync(fileName); - var zip = new DeflateStream(baseStream, CompressionMode.Decompress); - int _bufferSize = 2048; - var bytes = new byte[_bufferSize]; - Assert.Throws(() => { zip.Read(bytes, 0, _bufferSize); }); - zip.Dispose(); - } - } - - [Fact] - public void DisposedBaseStreamThrows() - { - var ms = new MemoryStream(); - ms.Dispose(); - AssertExtensions.Throws("stream", () => - { - var deflate = new DeflateStream(ms, CompressionMode.Decompress); - }); - - AssertExtensions.Throws("stream", () => - { - var deflate = new DeflateStream(ms, CompressionMode.Compress); - }); - } - - [Fact] - public void ReadOnlyStreamThrowsOnCompress() - { - var ms = new LocalMemoryStream(); - ms.SetCanWrite(false); - - AssertExtensions.Throws("stream", () => - { - var gzip = new DeflateStream(ms, CompressionMode.Compress); - }); - } - - [Fact] - public void WriteOnlyStreamThrowsOnDecompress() - { - var ms = new LocalMemoryStream(); - ms.SetCanRead(false); - - AssertExtensions.Throws("stream", () => - { - var gzip = new DeflateStream(ms, CompressionMode.Decompress); - }); - } - - [Fact] - public void TestCtors() - { - CompressionLevel[] legalValues = new CompressionLevel[] { CompressionLevel.Optimal, CompressionLevel.Fastest, CompressionLevel.NoCompression }; - - foreach (CompressionLevel level in legalValues) - { - bool[] boolValues = new bool[] { true, false }; - - foreach (bool remainsOpen in boolValues) - { - TestCtor(level, remainsOpen); - } - } - } - - [Fact] - public void TestLevelOptimial() - { - TestCtor(CompressionLevel.Optimal); - } - - [Fact] - public void TestLevelNoCompression() - { - TestCtor(CompressionLevel.NoCompression); - } - - [Fact] - public void TestLevelFastest() - { - TestCtor(CompressionLevel.Fastest); - } - - private static void TestCtor(CompressionLevel level, bool? leaveOpen = null) - { - //Create the DeflateStream - int _bufferSize = 1024; - var bytes = new byte[_bufferSize]; - var baseStream = new MemoryStream(bytes, writable: true); - DeflateStream ds; - - if (leaveOpen == null) - { - ds = new DeflateStream(baseStream, level); - } - else - { - ds = new DeflateStream(baseStream, level, leaveOpen ?? false); - } - - //Write some data and Close the stream - string strData = "Test Data"; - var encoding = Encoding.UTF8; - byte[] data = encoding.GetBytes(strData); - ds.Write(data, 0, data.Length); - ds.Flush(); - ds.Dispose(); - - if (leaveOpen != true) - { - //Check that Close has really closed the underlying stream - Assert.Throws(() => { baseStream.Write(bytes, 0, bytes.Length); }); - } - - //Read the data - byte[] data2 = new byte[_bufferSize]; - baseStream = new MemoryStream(bytes, writable: false); - ds = new DeflateStream(baseStream, CompressionMode.Decompress); - int size = ds.Read(data2, 0, _bufferSize - 5); - - //Verify the data roundtripped - for (int i = 0; i < size + 5; i++) - { - if (i < data.Length) - { - Assert.Equal(data[i], data2[i]); - } - else - { - Assert.Equal(data2[i], (byte)0); - } - } - } - - [Fact] - public void CtorArgumentValidation() - { - Assert.Throws(() => new DeflateStream(null, CompressionLevel.Fastest)); - Assert.Throws(() => new DeflateStream(null, CompressionMode.Decompress)); - Assert.Throws(() => new DeflateStream(null, CompressionMode.Compress)); - - Assert.Throws(() => new DeflateStream(null, CompressionLevel.Fastest, true)); - Assert.Throws(() => new DeflateStream(null, CompressionMode.Decompress, false)); - Assert.Throws(() => new DeflateStream(null, CompressionMode.Compress, true)); - - AssertExtensions.Throws("mode", () => new DeflateStream(new MemoryStream(), (CompressionMode)42)); - AssertExtensions.Throws("mode", () => new DeflateStream(new MemoryStream(), (CompressionMode)43, true)); - - AssertExtensions.Throws("stream", () => new DeflateStream(new MemoryStream(new byte[1], writable: false), CompressionLevel.Optimal)); - } - - [Fact] - public async Task Flush() - { - var ms = new MemoryStream(); - var ds = new DeflateStream(ms, CompressionMode.Compress); - ds.Flush(); - await ds.FlushAsync(); - } - - [Fact] - public void DoubleFlush() - { - var ms = new MemoryStream(); - var ds = new DeflateStream(ms, CompressionMode.Compress); - ds.Flush(); - ds.Flush(); - } - - [Fact] - public void DoubleDispose() - { - var ms = new MemoryStream(); - var ds = new DeflateStream(ms, CompressionMode.Compress); - ds.Dispose(); - ds.Dispose(); - } - - [Fact] - public void FlushThenDispose() - { - var ms = new MemoryStream(); - var ds = new DeflateStream(ms, CompressionMode.Compress); - ds.Flush(); - ds.Dispose(); - } - - [Fact] - public void FlushFailsAfterDispose() - { - var ms = new MemoryStream(); - var ds = new DeflateStream(ms, CompressionMode.Compress); - ds.Dispose(); - Assert.Throws(() => { ds.Flush(); }); - } - - [Fact] - public async Task FlushAsyncFailsAfterDispose() - { - var ms = new MemoryStream(); - var ds = new DeflateStream(ms, CompressionMode.Compress); - ds.Dispose(); - - await Assert.ThrowsAsync(async () => - { - await ds.FlushAsync(); - }); - } - - [Fact] - public void TestSeekMethodsDecompress() - { - var ms = new MemoryStream(); - var zip = new DeflateStream(ms, CompressionMode.Decompress); - - Assert.False(zip.CanSeek, "CanSeek should be false"); - - Assert.Throws(delegate { long value = zip.Length; }); - Assert.Throws(delegate { long value = zip.Position; }); - Assert.Throws(delegate { zip.Position = 100L; }); - Assert.Throws(delegate { zip.SetLength(100L); }); - Assert.Throws(delegate { zip.Seek(100L, SeekOrigin.Begin); }); - } - - [Fact] - public void TestSeekMethodsCompress() - { - var ms = new MemoryStream(); - var zip = new DeflateStream(ms, CompressionMode.Compress); - - Assert.False(zip.CanSeek, "CanSeek should be false"); - - Assert.Throws(delegate { long value = zip.Length; }); - Assert.Throws(delegate { long value = zip.Position; }); - Assert.Throws(delegate { zip.Position = 100L; }); - Assert.Throws(delegate { zip.SetLength(100L); }); - Assert.Throws(delegate { zip.Seek(100L, SeekOrigin.Begin); }); - } - - [Fact] - public void ReadWriteArgumentValidation() - { - using (var ds = new DeflateStream(new MemoryStream(), CompressionMode.Compress)) - { - Assert.Throws(() => ds.Write(null, 0, 0)); - Assert.Throws(() => ds.Write(new byte[1], -1, 0)); - Assert.Throws(() => ds.Write(new byte[1], 0, -1)); - AssertExtensions.Throws(null, () => ds.Write(new byte[1], 0, 2)); - AssertExtensions.Throws(null, () => ds.Write(new byte[1], 1, 1)); - Assert.Throws(() => ds.Read(new byte[1], 0, 1)); - ds.Write(new byte[1], 0, 0); - } - using (var ds = new DeflateStream(new MemoryStream(), CompressionMode.Compress)) - { - Assert.Throws(() => { ds.WriteAsync(null, 0, 0); }); - Assert.Throws(() => { ds.WriteAsync(new byte[1], -1, 0); }); - Assert.Throws(() => { ds.WriteAsync(new byte[1], 0, -1); }); - AssertExtensions.Throws(null, () => { ds.WriteAsync(new byte[1], 0, 2); }); - AssertExtensions.Throws(null, () => { ds.WriteAsync(new byte[1], 1, 1); }); - Assert.Throws(() => { ds.Read(new byte[1], 0, 1); }); - } - - using (var ds = new DeflateStream(new MemoryStream(), CompressionMode.Decompress)) - { - Assert.Throws(() => ds.Read(null, 0, 0)); - Assert.Throws(() => ds.Read(new byte[1], -1, 0)); - Assert.Throws(() => ds.Read(new byte[1], 0, -1)); - AssertExtensions.Throws(null, () => ds.Read(new byte[1], 0, 2)); - AssertExtensions.Throws(null, () => ds.Read(new byte[1], 1, 1)); - Assert.Throws(() => ds.Write(new byte[1], 0, 1)); - - var data = new byte[1] { 42 }; - Assert.Equal(0, ds.Read(data, 0, 0)); - Assert.Equal(42, data[0]); - } - using (var ds = new DeflateStream(new MemoryStream(), CompressionMode.Decompress)) - { - Assert.Throws(() => { ds.ReadAsync(null, 0, 0); }); - Assert.Throws(() => { ds.ReadAsync(new byte[1], -1, 0); }); - Assert.Throws(() => { ds.ReadAsync(new byte[1], 0, -1); }); - AssertExtensions.Throws(null, () => { ds.ReadAsync(new byte[1], 0, 2); }); - AssertExtensions.Throws(null, () => { ds.ReadAsync(new byte[1], 1, 1); }); - Assert.Throws(() => { ds.Write(new byte[1], 0, 1); }); - } - } - - [Fact] - public void CopyToAsyncArgumentValidation() - { - using (DeflateStream ds = new DeflateStream(new MemoryStream(), CompressionMode.Decompress)) - { - AssertExtensions.Throws("destination", () => { ds.CopyToAsync(null); }); - AssertExtensions.Throws("bufferSize", () => { ds.CopyToAsync(new MemoryStream(), 0); }); - Assert.Throws(() => { ds.CopyToAsync(new MemoryStream(new byte[1], writable: false)); }); - ds.Dispose(); - Assert.Throws(() => { ds.CopyToAsync(new MemoryStream()); }); - } - using (DeflateStream ds = new DeflateStream(new MemoryStream(), CompressionMode.Compress)) - { - Assert.Throws(() => { ds.CopyToAsync(new MemoryStream()); }); - } - } - - [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Full Framework Flush is a no-op.")] - public void Precancellation() - { - var ms = new MemoryStream(); - using (DeflateStream ds = new DeflateStream(ms, CompressionMode.Compress, leaveOpen: true)) - { - Assert.True(ds.WriteAsync(new byte[1], 0, 1, new CancellationToken(true)).IsCanceled); - Assert.True(ds.FlushAsync(new CancellationToken(true)).IsCanceled); - } - using (DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress, leaveOpen: true)) - { - Assert.True(ds.ReadAsync(new byte[1], 0, 1, new CancellationToken(true)).IsCanceled); - } - } - - public enum ReadWriteMode - { - SyncArray, - SyncSpan, - AsyncArray, - AsyncMemory - } - - public static IEnumerable RoundtripCompressDecompressOuterData - { - get - { - foreach (ReadWriteMode readWriteMode in new[] { ReadWriteMode.SyncArray, ReadWriteMode.SyncSpan, ReadWriteMode.AsyncArray, ReadWriteMode.AsyncMemory }) - { - foreach (bool useGzip in new[] { true, false }) // whether to add on gzip headers/footers - { - foreach (var level in new[] { CompressionLevel.Fastest, CompressionLevel.Optimal, CompressionLevel.NoCompression }) // compression level - { - yield return new object[] { readWriteMode, useGzip, 1, 5, level }; // smallest possible writes - yield return new object[] { readWriteMode, useGzip, 1023, 1023 * 10, level }; // overflowing internal buffer - yield return new object[] { readWriteMode, useGzip, 1024 * 1024, 1024 * 1024, level }; // large single write - } - } - } - } - } - - [Fact] - public async Task RoundtripCompressDecompress() - { - await RoundtripCompressDecompress(ReadWriteMode.SyncArray, useGzip: false, chunkSize: 1, totalSize: 10, level: CompressionLevel.Fastest); - await RoundtripCompressDecompress(ReadWriteMode.AsyncArray, useGzip: true, chunkSize: 1024, totalSize: 8192, level: CompressionLevel.Optimal); - } - - [OuterLoop] - [Theory] - [MemberData(nameof(RoundtripCompressDecompressOuterData))] - public async Task RoundtripCompressDecompress(ReadWriteMode readWriteMode, bool useGzip, int chunkSize, int totalSize, CompressionLevel level) - { - byte[] data = new byte[totalSize]; - new Random(42).NextBytes(data); - - var compressed = new MemoryStream(); - using (var compressor = useGzip ? (Stream)new GZipStream(compressed, level, true) : new DeflateStream(compressed, level, true)) - { - for (int i = 0; i < data.Length; i += chunkSize) // not using CopyTo{Async} due to optimizations in MemoryStream's implementation that avoid what we're trying to test - { - switch (readWriteMode) - { - case ReadWriteMode.AsyncArray: - await compressor.WriteAsync(data, i, chunkSize); - break; - case ReadWriteMode.SyncArray: - compressor.Write(data, i, chunkSize); - break; - case ReadWriteMode.SyncSpan: - compressor.Write(new ReadOnlySpan(data, i, chunkSize)); - break; - case ReadWriteMode.AsyncMemory: - await compressor.WriteAsync(new ReadOnlyMemory(data, i, chunkSize)); - break; - } - } - } - compressed.Position = 0; - await ReadAndValidateCompressedData(readWriteMode, useGzip, chunkSize, compressed, data); - compressed.Dispose(); - } - - [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Full Framework Flush is a no-op.")] - public async Task RoundTripWithFlush() - { - await RoundTripWithFlush(ReadWriteMode.SyncArray, useGzip: false, chunkSize: 1, totalSize: 10, level: CompressionLevel.Fastest); - await RoundTripWithFlush(ReadWriteMode.AsyncArray, useGzip: true, chunkSize: 1024, totalSize: 8192, level: CompressionLevel.Optimal); - } - - [OuterLoop] - [Theory] - [MemberData(nameof(RoundtripCompressDecompressOuterData))] - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Full Framework Flush is a no-op.")] - public async Task RoundTripWithFlush(ReadWriteMode readWriteMode, bool useGzip, int chunkSize, int totalSize, CompressionLevel level) - { - byte[] data = new byte[totalSize]; - new Random(42).NextBytes(data); - - using (var compressed = new MemoryStream()) - using (var compressor = useGzip ? (Stream)new GZipStream(compressed, level, true) : new DeflateStream(compressed, level, true)) - { - for (int i = 0; i < data.Length; i += chunkSize) // not using CopyTo{Async} due to optimizations in MemoryStream's implementation that avoid what we're trying to test - { - switch (readWriteMode) - { - case ReadWriteMode.AsyncArray: - await compressor.WriteAsync(data, i, chunkSize); - break; - case ReadWriteMode.SyncArray: - compressor.Write(data, i, chunkSize); - break; - case ReadWriteMode.SyncSpan: - compressor.Write(new ReadOnlySpan(data, i, chunkSize)); - break; - case ReadWriteMode.AsyncMemory: - await compressor.WriteAsync(new ReadOnlyMemory(data, i, chunkSize)); - break; - } - } - switch (readWriteMode) - { - case ReadWriteMode.AsyncArray: - case ReadWriteMode.AsyncMemory: - await compressor.FlushAsync(); - break; - case ReadWriteMode.SyncSpan: - case ReadWriteMode.SyncArray: - compressor.Flush(); - break; - } - compressed.Position = 0; - await ReadAndValidateCompressedData(readWriteMode, useGzip, chunkSize, compressed, data); - } - } - - [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Full Framework Flush is a no-op.")] - public async Task WriteAfterFlushing() - { - await WriteAfterFlushing(ReadWriteMode.SyncArray, useGzip: false, chunkSize: 1, totalSize: 10, level: CompressionLevel.Fastest); - await WriteAfterFlushing(ReadWriteMode.AsyncArray, useGzip: true, chunkSize: 1024, totalSize: 8192, level: CompressionLevel.Optimal); - } - - [OuterLoop] - [Theory] - [MemberData(nameof(RoundtripCompressDecompressOuterData))] - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Full Framework Flush is a no-op.")] - public async Task WriteAfterFlushing(ReadWriteMode readWriteMode, bool useGzip, int chunkSize, int totalSize, CompressionLevel level) - { - byte[] data = new byte[totalSize]; - List expected = new List(); - new Random(42).NextBytes(data); - - using (var compressed = new MemoryStream()) - using (var compressor = useGzip ? (Stream)new GZipStream(compressed, level, true) : new DeflateStream(compressed, level, true)) - { - for (int i = 0; i < data.Length; i += chunkSize) // not using CopyTo{Async} due to optimizations in MemoryStream's implementation that avoid what we're trying to test - { - switch (readWriteMode) - { - case ReadWriteMode.AsyncArray: - await compressor.WriteAsync(data, i, chunkSize); - break; - case ReadWriteMode.SyncArray: - compressor.Write(data, i, chunkSize); - break; - case ReadWriteMode.SyncSpan: - compressor.Write(new ReadOnlySpan(data, i, chunkSize)); - break; - case ReadWriteMode.AsyncMemory: - await compressor.WriteAsync(new ReadOnlyMemory(data, i, chunkSize)); - break; - } - for (int j = i; j < i + chunkSize; j++) - expected.Insert(j, data[j]); - - switch (readWriteMode) - { - case ReadWriteMode.AsyncArray: - case ReadWriteMode.AsyncMemory: - await compressor.FlushAsync(); - break; - case ReadWriteMode.SyncSpan: - case ReadWriteMode.SyncArray: - compressor.Flush(); - break; - } - - MemoryStream partiallyCompressed = new MemoryStream(compressed.ToArray()); - partiallyCompressed.Position = 0; - await ReadAndValidateCompressedData(readWriteMode, useGzip, chunkSize, partiallyCompressed, expected.ToArray()); - } - } - } - - [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Full Framework Flush is a no-op.")] - public async Task FlushBeforeFirstWrites() - { - await FlushBeforeFirstWrites(ReadWriteMode.SyncArray, useGzip: false, chunkSize: 1, totalSize: 10, level: CompressionLevel.Fastest); - await FlushBeforeFirstWrites(ReadWriteMode.AsyncArray, useGzip: true, chunkSize: 1024, totalSize: 8192, level: CompressionLevel.Optimal); - } - - [OuterLoop] - [Theory] - [MemberData(nameof(RoundtripCompressDecompressOuterData))] - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Full Framework Flush is a no-op.")] - public async Task FlushBeforeFirstWrites(ReadWriteMode readWriteMode, bool useGzip, int chunkSize, int totalSize, CompressionLevel level) - { - byte[] data = new byte[totalSize]; - new Random(42).NextBytes(data); - - using (var compressed = new MemoryStream()) - using (var compressor = useGzip ? (Stream)new GZipStream(compressed, level, true) : new DeflateStream(compressed, level, true)) - { - switch (readWriteMode) - { - case ReadWriteMode.AsyncArray: - case ReadWriteMode.AsyncMemory: - await compressor.FlushAsync(); - break; - case ReadWriteMode.SyncSpan: - case ReadWriteMode.SyncArray: - compressor.Flush(); - break; - } - - for (int i = 0; i < data.Length; i += chunkSize) // not using CopyTo{Async} due to optimizations in MemoryStream's implementation that avoid what we're trying to test - { - switch (readWriteMode) - { - case ReadWriteMode.AsyncArray: - await compressor.WriteAsync(data, i, chunkSize); - break; - case ReadWriteMode.SyncArray: - compressor.Write(data, i, chunkSize); - break; - case ReadWriteMode.SyncSpan: - compressor.Write(new ReadOnlySpan(data, i, chunkSize)); - break; - case ReadWriteMode.AsyncMemory: - await compressor.WriteAsync(new ReadOnlyMemory(data, i, chunkSize)); - break; - } - } - - switch (readWriteMode) - { - case ReadWriteMode.AsyncArray: - case ReadWriteMode.AsyncMemory: - await compressor.FlushAsync(); - break; - case ReadWriteMode.SyncSpan: - case ReadWriteMode.SyncArray: - compressor.Flush(); - break; - } - compressed.Position = 0; - await ReadAndValidateCompressedData(readWriteMode, useGzip, chunkSize, compressed, data); - } - } - - /// - /// Given a MemoryStream of compressed data and a byte array of desired output, decompresses - /// the stream and validates that it is equal to the expected array. - /// - private async Task ReadAndValidateCompressedData(ReadWriteMode readWriteMode, bool useGzip, int chunkSize, MemoryStream compressed, byte[] expected) - { - using (MemoryStream decompressed = new MemoryStream()) - using (Stream decompressor = useGzip ? (Stream)new GZipStream(compressed, CompressionMode.Decompress, true) : new DeflateStream(compressed, CompressionMode.Decompress, true)) - { - int bytesRead; - var buffer = new byte[chunkSize]; - switch (readWriteMode) - { - case ReadWriteMode.SyncSpan: - while ((bytesRead = decompressor.Read(new Span(buffer))) != 0) - { - decompressed.Write(buffer, 0, bytesRead); - } - break; - case ReadWriteMode.SyncArray: - while ((bytesRead = decompressor.Read(buffer, 0, buffer.Length)) != 0) - { - decompressed.Write(buffer, 0, bytesRead); - } - break; - case ReadWriteMode.AsyncArray: - while ((bytesRead = await decompressor.ReadAsync(buffer, 0, buffer.Length)) != 0) - { - decompressed.Write(buffer, 0, bytesRead); - } - break; - case ReadWriteMode.AsyncMemory: - while ((bytesRead = await decompressor.ReadAsync(new Memory(buffer))) != 0) - { - decompressed.Write(buffer, 0, bytesRead); - } - break; - } - Assert.Equal(expected, decompressed.ToArray()); - } - } - - [Theory] - [InlineData(ReadWriteMode.SyncArray, false)] - [InlineData(ReadWriteMode.AsyncArray, false)] - [InlineData(ReadWriteMode.SyncSpan, false)] - [InlineData(ReadWriteMode.SyncSpan, true)] - [InlineData(ReadWriteMode.AsyncMemory, false)] - [InlineData(ReadWriteMode.AsyncMemory, true)] - public async Task SequentialReadsOnMemoryStream_Return_SameBytes(ReadWriteMode readWriteMode, bool derived) - { - byte[] data = new byte[1024 * 10]; - new Random(42).NextBytes(data); - - var compressed = new MemoryStream(); - using (var compressor = derived ? - new DerivedDeflateStream(compressed, CompressionMode.Compress, true) : - new DeflateStream(compressed, CompressionMode.Compress, true)) - { - for (int i = 0; i < data.Length; i += 1024) - { - switch (readWriteMode) - { - case ReadWriteMode.SyncArray: compressor.Write(data, i, 1024); break; - case ReadWriteMode.AsyncArray: await compressor.WriteAsync(data, i, 1024); break; - case ReadWriteMode.SyncSpan: compressor.Write(new Span(data, i, 1024)); break; - case ReadWriteMode.AsyncMemory: await compressor.WriteAsync(new ReadOnlyMemory(data, i, 1024)); break; - } - } - - Assert.Equal( - derived && (readWriteMode == ReadWriteMode.SyncArray || readWriteMode == ReadWriteMode.SyncSpan), - compressor is DerivedDeflateStream dds && dds.WriteArrayInvoked); - } - compressed.Position = 0; - - using (var decompressor = derived ? - new DerivedDeflateStream(compressed, CompressionMode.Decompress, true) : - new DeflateStream(compressed, CompressionMode.Decompress, true)) - { - int i, j; - byte[] array = new byte[100]; - byte[] array2 = new byte[100]; - - // only read in the first 100 bytes - switch (readWriteMode) - { - case ReadWriteMode.SyncArray: decompressor.Read(array, 0, array.Length); break; - case ReadWriteMode.AsyncArray: await decompressor.ReadAsync(array, 0, array.Length); break; - case ReadWriteMode.SyncSpan: decompressor.Read(new Span(array)); break; - case ReadWriteMode.AsyncMemory: await decompressor.ReadAsync(new Memory(array)); break; - } - for (i = 0; i < array.Length; i++) - { - Assert.Equal(data[i], array[i]); - } - - // read in the next 100 bytes and make sure nothing is missing - switch (readWriteMode) - { - case ReadWriteMode.SyncArray: decompressor.Read(array2, 0, array2.Length); break; - case ReadWriteMode.AsyncArray: await decompressor.ReadAsync(array2, 0, array2.Length); break; - case ReadWriteMode.SyncSpan: decompressor.Read(new Span(array2)); break; - case ReadWriteMode.AsyncMemory: await decompressor.ReadAsync(new Memory(array2)); break; - } - for (j = 0; j < array2.Length; j++) - { - Assert.Equal(data[j], array[j]); - } - - Assert.Equal( - derived && (readWriteMode == ReadWriteMode.SyncArray || readWriteMode == ReadWriteMode.SyncSpan), - decompressor is DerivedDeflateStream dds && dds.ReadArrayInvoked); - } - } - - [Fact] - public void Roundtrip_Write_ReadByte() - { - byte[] data = new byte[1024 * 10]; - new Random(42).NextBytes(data); - - var compressed = new MemoryStream(); - using (var compressor = new DeflateStream(compressed, CompressionMode.Compress, true)) - { - compressor.Write(data, 0, data.Length); - } - compressed.Position = 0; - - using (var decompressor = new DeflateStream(compressed, CompressionMode.Decompress, true)) - { - for (int i = 0; i < data.Length; i++) - Assert.Equal(data[i], decompressor.ReadByte()); - } - } - - [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Full Framework Flush is a no-op.")] - public async Task WrapNullReturningTasksStream() - { - using (var ds = new DeflateStream(new BadWrappedStream(BadWrappedStream.Mode.ReturnNullTasks), CompressionMode.Decompress)) - await Assert.ThrowsAsync(() => ds.ReadAsync(new byte[1024], 0, 1024)); - } - - [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Full Framework Flush is a no-op.")] - public async Task WrapStreamReturningBadReadValues() - { - using (var ds = new DeflateStream(new BadWrappedStream(BadWrappedStream.Mode.ReturnTooLargeCounts), CompressionMode.Decompress)) - Assert.Throws(() => ds.Read(new byte[1024], 0, 1024)); - using (var ds = new DeflateStream(new BadWrappedStream(BadWrappedStream.Mode.ReturnTooLargeCounts), CompressionMode.Decompress)) - await Assert.ThrowsAsync(() => ds.ReadAsync(new byte[1024], 0, 1024)); - using (var ds = new DeflateStream(new BadWrappedStream(BadWrappedStream.Mode.ReturnTooLargeCounts), CompressionMode.Decompress)) - await Assert.ThrowsAsync(async () => { await ds.ReadAsync(new Memory(new byte[1024])); }); - - using (var ds = new DeflateStream(new BadWrappedStream(BadWrappedStream.Mode.ReturnTooSmallCounts), CompressionMode.Decompress)) - Assert.Equal(0, ds.Read(new byte[1024], 0, 1024)); - using (var ds = new DeflateStream(new BadWrappedStream(BadWrappedStream.Mode.ReturnTooSmallCounts), CompressionMode.Decompress)) - Assert.Equal(0, await ds.ReadAsync(new byte[1024], 0, 1024)); - using (var ds = new DeflateStream(new BadWrappedStream(BadWrappedStream.Mode.ReturnTooSmallCounts), CompressionMode.Decompress)) - Assert.Equal(0, await ds.ReadAsync(new Memory(new byte[1024]))); - } - - public static IEnumerable CopyToAsync_Roundtrip_OutputMatchesInput_MemberData() - { - var rand = new Random(); - foreach (int dataSize in new[] { 1, 1024, 4095, 1024 * 1024 }) - { - var data = new byte[dataSize]; - rand.NextBytes(data); - - var compressed = new MemoryStream(); - using (var ds = new DeflateStream(compressed, CompressionMode.Compress, leaveOpen: true)) - { - ds.Write(data, 0, data.Length); - } - byte[] compressedData = compressed.ToArray(); - - foreach (int copyBufferSize in new[] { 1, 4096, 80 * 1024 }) - { - // Memory source - var m = new MemoryStream(compressedData, writable: false); - yield return new object[] { data, copyBufferSize, m }; - - // File sources, sync and async - foreach (bool useAsync in new[] { true, false }) - { - string path = Path.GetTempFileName(); - File.WriteAllBytes(path, compressedData); - - FileOptions options = FileOptions.DeleteOnClose; - if (useAsync) options |= FileOptions.Asynchronous; - yield return new object[] { data, copyBufferSize, new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x1000, options) }; - } - } - } - } - - [Theory] - [MemberData(nameof(CopyToAsync_Roundtrip_OutputMatchesInput_MemberData))] - public async Task CopyToAsync_Roundtrip_OutputMatchesInput(byte[] expectedDecrypted, int copyBufferSize, Stream source) - { - var m = new MemoryStream(); - using (DeflateStream ds = new DeflateStream(source, CompressionMode.Decompress)) - { - await ds.CopyToAsync(m); - } - Assert.Equal(expectedDecrypted, m.ToArray()); - } - - private sealed class DerivedDeflateStream : DeflateStream - { - public bool ReadArrayInvoked = false, WriteArrayInvoked = false; - internal DerivedDeflateStream(Stream stream, CompressionMode mode) : base(stream, mode) { } - internal DerivedDeflateStream(Stream stream, CompressionMode mode, bool leaveOpen) : base(stream, mode, leaveOpen) { } - - public override int Read(byte[] array, int offset, int count) - { - ReadArrayInvoked = true; - return base.Read(array, offset, count); - } - - public override void Write(byte[] array, int offset, int count) - { - WriteArrayInvoked = true; - base.Write(array, offset, count); - } - } - - private sealed class BadWrappedStream : Stream - { - public enum Mode - { - Default, - ReturnNullTasks, - ReturnTooSmallCounts, - ReturnTooLargeCounts, - } - - private readonly Mode _mode; - - public BadWrappedStream(Mode mode) { _mode = mode; } - - public override int Read(byte[] buffer, int offset, int count) - { - switch (_mode) - { - case Mode.ReturnTooSmallCounts: return -1; - case Mode.ReturnTooLargeCounts: return buffer.Length + 1; - default: return 0; - } - } - - public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - return _mode == Mode.ReturnNullTasks ? - null : - base.ReadAsync(buffer, offset, count, cancellationToken); - } - - public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - return _mode == Mode.ReturnNullTasks ? - null : - base.WriteAsync(buffer, offset, count, cancellationToken); - } - - public override void Write(byte[] buffer, int offset, int count) { } - public override void Flush() { } - public override bool CanRead { get { return true; } } - public override bool CanSeek { get { return false; } } - public override bool CanWrite { get { return true; } } - public override long Length { get { throw new NotSupportedException(); } } - public override long Position { get { throw new NotSupportedException(); } set { throw new NotSupportedException(); } } - public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } - public override void SetLength(long value) { throw new NotSupportedException(); } - } - } - - public partial class ManualSyncMemoryStream : MemoryStream - { - private bool isSync; - public ManualResetEventSlim manualResetEvent = new ManualResetEventSlim(initialState: false); - - public bool ReadHit = false; // For validation of the async methods we want to ensure they correctly delegate the async - public bool WriteHit = false; // methods of the underlying stream. This bool acts as a toggle to check that they're being used. - - public static async Task GetStreamFromFileAsync(string testFile, bool sync = false, bool strip = false) - { - var baseStream = await StreamHelpers.CreateTempCopyStream(testFile); - if (strip) - { - baseStream = StripHeaderAndFooter.Strip(baseStream); - } - - var ms = new ManualSyncMemoryStream(sync); - await baseStream.CopyToAsync(ms); - - ms.Position = 0; - return ms; - } - - public ManualSyncMemoryStream(bool sync = false) : base() - { - isSync = sync; - } - - public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) => TaskToApm.Begin(ReadAsync(buffer, offset, count), callback, state); - public override int EndRead(IAsyncResult asyncResult) => TaskToApm.End(asyncResult); - public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) => TaskToApm.Begin(WriteAsync(buffer, offset, count), callback, state); - public override void EndWrite(IAsyncResult asyncResult) => TaskToApm.End(asyncResult); - - public override async Task ReadAsync(byte[] array, int offset, int count, CancellationToken cancellationToken) - { - ReadHit = true; - if (isSync) - { - manualResetEvent.Wait(cancellationToken); - } - else - { - await Task.Run(() => manualResetEvent.Wait(cancellationToken)); - } - - return await base.ReadAsync(array, offset, count, cancellationToken); - } - - public override async Task WriteAsync(byte[] array, int offset, int count, CancellationToken cancellationToken) - { - WriteHit = true; - if (isSync) - { - manualResetEvent.Wait(cancellationToken); - } - else - { - await Task.Run(() => manualResetEvent.Wait(cancellationToken)); - } - - await base.WriteAsync(array, offset, count, cancellationToken); - } - } -} diff --git a/external/corefx/src/System.IO.Compression/tests/DeflateStreamTests.netcoreapp.cs b/external/corefx/src/System.IO.Compression/tests/DeflateStreamTests.netcoreapp.cs deleted file mode 100644 index fb0c0b2d53..0000000000 --- a/external/corefx/src/System.IO.Compression/tests/DeflateStreamTests.netcoreapp.cs +++ /dev/null @@ -1,44 +0,0 @@ -// 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.Threading; -using System.Threading.Tasks; - -namespace System.IO.Compression.Tests -{ - public partial class ManualSyncMemoryStream : MemoryStream - { - public override async ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken) - { - ReadHit = true; - - if (isSync) - { - manualResetEvent.Wait(cancellationToken); - } - else - { - await Task.Run(() => manualResetEvent.Wait(cancellationToken)); - } - - return await base.ReadAsync(destination, cancellationToken); - } - - public override async Task WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken) - { - WriteHit = true; - - if (isSync) - { - manualResetEvent.Wait(cancellationToken); - } - else - { - await Task.Run(() => manualResetEvent.Wait(cancellationToken)); - } - - await base.WriteAsync(source, cancellationToken); - } - } -} diff --git a/external/corefx/src/System.IO.Compression/tests/GZipStreamTests.cs b/external/corefx/src/System.IO.Compression/tests/GZipStreamTests.cs deleted file mode 100644 index cce3d58069..0000000000 --- a/external/corefx/src/System.IO.Compression/tests/GZipStreamTests.cs +++ /dev/null @@ -1,421 +0,0 @@ -// 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.Text; -using System.Threading.Tasks; -using Xunit; - -namespace System.IO.Compression.Tests -{ - public class GZipStreamTests - { - static string gzTestFile(string fileName) { return Path.Combine("GZTestData", fileName); } - - [Fact] - public void BaseStream1() - { - var writeStream = new MemoryStream(); - var zip = new GZipStream(writeStream, CompressionMode.Compress); - - Assert.Same(zip.BaseStream, writeStream); - writeStream.Dispose(); - } - - [Fact] - public void BaseStream2() - { - var ms = new MemoryStream(); - var zip = new GZipStream(ms, CompressionMode.Decompress); - - Assert.Same(zip.BaseStream, ms); - ms.Dispose(); - } - - [Fact] - public async Task ModifyBaseStream() - { - var ms = await LocalMemoryStream.readAppFileAsync(gzTestFile("GZTestDocument.txt.gz")); - - var zip = new GZipStream(ms, CompressionMode.Decompress); - int size = 1024; - byte[] bytes = new byte[size]; - zip.BaseStream.Read(bytes, 0, size); // This will throw if the underlying stream is not writable as expected - } - - [Fact] - public void DecompressCanRead() - { - var ms = new MemoryStream(); - var zip = new GZipStream(ms, CompressionMode.Decompress); - - Assert.True(zip.CanRead, "GZipStream not CanRead in Decompress"); - - zip.Dispose(); - Assert.False(zip.CanRead, "GZipStream CanRead after dispose in Decompress"); - } - - [Fact] - public void CompressCanWrite() - { - var ms = new MemoryStream(); - var zip = new GZipStream(ms, CompressionMode.Compress); - Assert.True(zip.CanWrite, "GZipStream not CanWrite with CompressionMode.Compress"); - - zip.Dispose(); - Assert.False(zip.CanWrite, "GZipStream CanWrite after dispose"); - } - - [Fact] - public void CanDisposeBaseStream() - { - var ms = new MemoryStream(); - var zip = new GZipStream(ms, CompressionMode.Compress); - ms.Dispose(); // This would throw if this was invalid - } - - [Fact] - public void CanDisposeGZipStream() - { - var ms = new MemoryStream(); - var zip = new GZipStream(ms, CompressionMode.Compress); - zip.Dispose(); - - Assert.Null(zip.BaseStream); - - zip.Dispose(); // Should be a no-op - } - - [Fact] - public async Task CanReadBaseStreamAfterDispose() - { - var ms = await LocalMemoryStream.readAppFileAsync(gzTestFile("GZTestDocument.txt.gz")); - - var zip = new GZipStream(ms, CompressionMode.Decompress, leaveOpen: true); - var baseStream = zip.BaseStream; - zip.Dispose(); - - int size = 1024; - byte[] bytes = new byte[size]; - baseStream.Read(bytes, 0, size); // This will throw if the underlying stream is not writable as expected - } - - [Fact] - public async Task DecompressWorks() - { - var compareStream = await LocalMemoryStream.readAppFileAsync(gzTestFile("GZTestDocument.txt")); - var gzStream = await LocalMemoryStream.readAppFileAsync(gzTestFile("GZTestDocument.txt.gz")); - - await DecompressAsync(compareStream, gzStream); - } - - [Fact] - public async Task DecompressWorksWithDoc() - { - var compareStream = await LocalMemoryStream.readAppFileAsync(gzTestFile("GZTestDocument.doc")); - var gzStream = await LocalMemoryStream.readAppFileAsync(gzTestFile("GZTestDocument.doc.gz")); - - await DecompressAsync(compareStream, gzStream); - } - - [Fact] - public async Task DecompressWorksWithDocx() - { - var compareStream = await LocalMemoryStream.readAppFileAsync(gzTestFile("GZTestDocument.docx")); - var gzStream = await LocalMemoryStream.readAppFileAsync(gzTestFile("GZTestDocument.docx.gz")); - - await DecompressAsync(compareStream, gzStream); - } - - [Fact] - public async Task DecompressWorksWithPdf() - { - var compareStream = await LocalMemoryStream.readAppFileAsync(gzTestFile("GZTestDocument.pdf")); - var gzStream = await LocalMemoryStream.readAppFileAsync(gzTestFile("GZTestDocument.pdf.gz")); - - await DecompressAsync(compareStream, gzStream); - } - - // Making this async since regular read/write are tested below - private static async Task DecompressAsync(MemoryStream compareStream, MemoryStream gzStream) - { - var ms = new MemoryStream(); - var zip = new GZipStream(gzStream, CompressionMode.Decompress); - - var GZipStream = new MemoryStream(); - await zip.CopyToAsync(GZipStream); - - GZipStream.Position = 0; - compareStream.Position = 0; - - byte[] compareArray = compareStream.ToArray(); - byte[] writtenArray = GZipStream.ToArray(); - - Assert.Equal(compareArray.Length, writtenArray.Length); - for (int i = 0; i < compareArray.Length; i++) - { - Assert.Equal(compareArray[i], writtenArray[i]); - } - } - - [Fact] - public void NullBaseStreamThrows() - { - Assert.Throws(() => - { - var deflate = new GZipStream(null, CompressionMode.Decompress); - }); - - Assert.Throws(() => - { - var deflate = new GZipStream(null, CompressionMode.Compress); - }); - } - - [Fact] - public void DisposedBaseStreamThrows() - { - var ms = new MemoryStream(); - ms.Dispose(); - AssertExtensions.Throws("stream", () => - { - var deflate = new GZipStream(ms, CompressionMode.Decompress); - }); - - AssertExtensions.Throws("stream", () => - { - var deflate = new GZipStream(ms, CompressionMode.Compress); - }); - } - - [Fact] - public void ReadOnlyStreamThrowsOnCompress() - { - var ms = new LocalMemoryStream(); - ms.SetCanWrite(false); - - AssertExtensions.Throws("stream", () => - { - var gzip = new GZipStream(ms, CompressionMode.Compress); - }); - } - - [Fact] - public void WriteOnlyStreamThrowsOnDecompress() - { - var ms = new LocalMemoryStream(); - ms.SetCanRead(false); - - AssertExtensions.Throws("stream", () => - { - var gzip = new GZipStream(ms, CompressionMode.Decompress); - }); - } - - [Fact] - public void CopyToAsyncArgumentValidation() - { - using (GZipStream gs = new GZipStream(new MemoryStream(), CompressionMode.Decompress)) - { - AssertExtensions.Throws("destination", () => { gs.CopyToAsync(null); }); - AssertExtensions.Throws("bufferSize", () => { gs.CopyToAsync(new MemoryStream(), 0); }); - Assert.Throws(() => { gs.CopyToAsync(new MemoryStream(new byte[1], writable: false)); }); - gs.Dispose(); - Assert.Throws(() => { gs.CopyToAsync(new MemoryStream()); }); - } - using (GZipStream gs = new GZipStream(new MemoryStream(), CompressionMode.Compress)) - { - Assert.Throws(() => { gs.CopyToAsync(new MemoryStream()); }); - } - } - - [Fact] - public void TestCtors() - { - CompressionLevel[] legalValues = new CompressionLevel[] { CompressionLevel.Optimal, CompressionLevel.Fastest, CompressionLevel.NoCompression }; - - foreach (CompressionLevel level in legalValues) - { - bool[] boolValues = new bool[] { true, false }; - - foreach (bool remainsOpen in boolValues) - { - TestCtor(level, remainsOpen); - } - } - } - - [Fact] - public void TestLevelOptimial() - { - TestCtor(CompressionLevel.Optimal); - } - - [Fact] - public void TestLevelNoCompression() - { - TestCtor(CompressionLevel.NoCompression); - } - - [Fact] - public void TestLevelFastest() - { - TestCtor(CompressionLevel.Fastest); - } - - private static void TestCtor(CompressionLevel level, bool? leaveOpen = null) - { - //Create the GZipStream - int _bufferSize = 1024; - var bytes = new byte[_bufferSize]; - var baseStream = new MemoryStream(bytes, writable: true); - GZipStream ds; - - if (leaveOpen == null) - { - ds = new GZipStream(baseStream, level); - } - else - { - ds = new GZipStream(baseStream, level, leaveOpen ?? false); - } - - //Write some data and Close the stream - string strData = "Test Data"; - var encoding = Encoding.UTF8; - byte[] data = encoding.GetBytes(strData); - ds.Write(data, 0, data.Length); - ds.Flush(); - ds.Dispose(); - - if (leaveOpen != true) - { - //Check that Close has really closed the underlying stream - Assert.Throws(() => { baseStream.Write(bytes, 0, bytes.Length); }); - } - - //Read the data - byte[] data2 = new byte[_bufferSize]; - baseStream = new MemoryStream(bytes, writable: false); - ds = new GZipStream(baseStream, CompressionMode.Decompress); - int size = ds.Read(data2, 0, _bufferSize - 5); - - //Verify the data roundtripped - for (int i = 0; i < size + 5; i++) - { - if (i < data.Length) - { - Assert.Equal(data[i], data2[i]); - } - else - { - Assert.Equal(data2[i], (byte)0); - } - } - } - - [Fact] - public async Task Flush() - { - var ms = new MemoryStream(); - var ds = new GZipStream(ms, CompressionMode.Compress); - ds.Flush(); - await ds.FlushAsync(); - - // Just ensuring Flush doesn't throw - } - - [Fact] - public void FlushFailsAfterDispose() - { - var ms = new MemoryStream(); - var ds = new GZipStream(ms, CompressionMode.Compress); - ds.Dispose(); - Assert.Throws(() => { ds.Flush(); }); - } - - [Fact] - public async Task FlushAsyncFailsAfterDispose() - { - var ms = new MemoryStream(); - var ds = new GZipStream(ms, CompressionMode.Compress); - ds.Dispose(); - await Assert.ThrowsAsync(async () => - { - await ds.FlushAsync(); - }); - } - - [Fact] - public void TestSeekMethodsDecompress() - { - var ms = new MemoryStream(); - var zip = new GZipStream(ms, CompressionMode.Decompress); - - Assert.False(zip.CanSeek); - - Assert.Throws(delegate { long value = zip.Length; }); - Assert.Throws(delegate { long value = zip.Position; }); - Assert.Throws(delegate { zip.Position = 100L; }); - Assert.Throws(delegate { zip.SetLength(100L); }); - Assert.Throws(delegate { zip.Seek(100L, SeekOrigin.Begin); }); - - zip.Dispose(); - Assert.False(zip.CanSeek); - } - - [Fact] - public void TestSeekMethodsCompress() - { - var ms = new MemoryStream(); - var zip = new GZipStream(ms, CompressionMode.Compress); - - Assert.False(zip.CanSeek); - - Assert.Throws(delegate { long value = zip.Length; }); - Assert.Throws(delegate { long value = zip.Position; }); - Assert.Throws(delegate { zip.Position = 100L; }); - Assert.Throws(delegate { zip.SetLength(100L); }); - Assert.Throws(delegate { zip.Seek(100L, SeekOrigin.Begin); }); - - zip.Dispose(); - Assert.False(zip.CanSeek); - } - - [Fact] - public void DerivedStream_ReadWriteSpan_UsesReadWriteArray() - { - var ms = new MemoryStream(); - using (var compressor = new DerivedGZipStream(ms, CompressionMode.Compress, leaveOpen:true)) - { - compressor.Write(new Span(new byte[1])); - Assert.True(compressor.WriteArrayInvoked); - } - ms.Position = 0; - using (var compressor = new DerivedGZipStream(ms, CompressionMode.Decompress, leaveOpen: true)) - { - compressor.Read(new Span(new byte[1])); - Assert.True(compressor.ReadArrayInvoked); - } - } - - private sealed class DerivedGZipStream : GZipStream - { - public bool ReadArrayInvoked, WriteArrayInvoked; - - public DerivedGZipStream(Stream stream, CompressionMode mode, bool leaveOpen) : base(stream, mode, leaveOpen) { } - - public override int Read(byte[] array, int offset, int count) - { - ReadArrayInvoked = true; - return base.Read(array, offset, count); - } - - public override void Write(byte[] array, int offset, int count) - { - WriteArrayInvoked = true; - base.Write(array, offset, count); - } - } - } -} diff --git a/external/corefx/src/System.IO.Compression/tests/Performance/CompressionStreamPerfTests.Deflate.cs b/external/corefx/src/System.IO.Compression/tests/Performance/CompressionStreamPerfTests.Deflate.cs new file mode 100644 index 0000000000..feb6b5c51c --- /dev/null +++ b/external/corefx/src/System.IO.Compression/tests/Performance/CompressionStreamPerfTests.Deflate.cs @@ -0,0 +1,16 @@ +// 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. + +namespace System.IO.Compression +{ + public class DeflateStreamPerfTests : CompressionStreamPerfTestBase + { + public override Stream CreateStream(Stream stream, CompressionMode mode) => new DeflateStream(stream, mode); + public override Stream CreateStream(Stream stream, CompressionMode mode, bool leaveOpen) => new DeflateStream(stream, mode, leaveOpen); + public override Stream CreateStream(Stream stream, CompressionLevel level) => new DeflateStream(stream, level); + public override Stream CreateStream(Stream stream, CompressionLevel level, bool leaveOpen) => new DeflateStream(stream, level, leaveOpen); + public override Stream BaseStream(Stream stream) => ((DeflateStream)stream).BaseStream; + protected override string CompressedTestFile(string uncompressedPath) => Path.Combine("DeflateTestData", Path.GetFileName(uncompressedPath)); + } +} diff --git a/external/corefx/src/System.IO.Compression/tests/Performance/CompressionStreamPerfTests.Gzip.cs b/external/corefx/src/System.IO.Compression/tests/Performance/CompressionStreamPerfTests.Gzip.cs new file mode 100644 index 0000000000..9bf7fdb967 --- /dev/null +++ b/external/corefx/src/System.IO.Compression/tests/Performance/CompressionStreamPerfTests.Gzip.cs @@ -0,0 +1,16 @@ +// 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. + +namespace System.IO.Compression +{ + public class GzipStreamPerfTests : CompressionStreamPerfTestBase + { + public override Stream CreateStream(Stream stream, CompressionMode mode) => new GZipStream(stream, mode); + public override Stream CreateStream(Stream stream, CompressionMode mode, bool leaveOpen) => new GZipStream(stream, mode, leaveOpen); + public override Stream CreateStream(Stream stream, CompressionLevel level) => new GZipStream(stream, level); + public override Stream CreateStream(Stream stream, CompressionLevel level, bool leaveOpen) => new GZipStream(stream, level, leaveOpen); + public override Stream BaseStream(Stream stream) => ((GZipStream)stream).BaseStream; + protected override string CompressedTestFile(string uncompressedPath) => Path.Combine("GZipTestData", Path.GetFileName(uncompressedPath) + ".gz"); + } +} diff --git a/external/corefx/src/System.IO.Compression/tests/Performance/Perf.DeflateStream.cs b/external/corefx/src/System.IO.Compression/tests/Performance/Perf.DeflateStream.cs deleted file mode 100644 index 3eb6f996b1..0000000000 --- a/external/corefx/src/System.IO.Compression/tests/Performance/Perf.DeflateStream.cs +++ /dev/null @@ -1,152 +0,0 @@ -// 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 Xunit; -using Microsoft.Xunit.Performance; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Threading.Tasks; - -namespace System.IO.Compression.Tests -{ - public class Perf_DeflateStream - { - private static string CreateCompressedFile(CompressionType type) - { - const int fileSize = 1000000; - PerfUtils utils = new PerfUtils(); - string filePath = utils.GetTestFilePath() + ".gz"; - switch (type) - { - case CompressionType.CryptoRandom: - using (RandomNumberGenerator rand = RandomNumberGenerator.Create()) - { - byte[] bytes = new byte[fileSize]; - rand.GetBytes(bytes); - using (FileStream output = File.Create(filePath)) - using (GZipStream zip = new GZipStream(output, CompressionMode.Compress)) - zip.Write(bytes, 0, bytes.Length); - } - break; - case CompressionType.RepeatedSegments: - { - byte[] bytes = new byte[fileSize / 1000]; - new Random(128453).NextBytes(bytes); - using (FileStream output = File.Create(filePath)) - using (GZipStream zip = new GZipStream(output, CompressionMode.Compress)) - for (int i = 0; i < 1000; i++) - zip.Write(bytes, 0, bytes.Length); - } - break; - case CompressionType.NormalData: - { - byte[] bytes = new byte[fileSize]; - new Random(128453).NextBytes(bytes); - using (FileStream output = File.Create(filePath)) - using (GZipStream zip = new GZipStream(output, CompressionMode.Compress)) - zip.Write(bytes, 0, bytes.Length); - } - break; - } - return filePath; - } - - private static byte[] CreateBytesToCompress(CompressionType type) - { - const int fileSize = 500000; - byte[] bytes = new byte[fileSize]; - switch (type) - { - case CompressionType.CryptoRandom: - using (RandomNumberGenerator rand = RandomNumberGenerator.Create()) - rand.GetBytes(bytes); - break; - case CompressionType.RepeatedSegments: - { - byte[] small = new byte[1000]; - new Random(123453).NextBytes(small); - for (int i = 0; i < fileSize / 1000; i++) - { - small.CopyTo(bytes, 1000 * i); - } - } - break; - case CompressionType.VeryRepetitive: - { - byte[] small = new byte[100]; - new Random(123453).NextBytes(small); - for (int i = 0; i < fileSize / 100; i++) - { - small.CopyTo(bytes, 100 * i); - } - break; - } - case CompressionType.NormalData: - new Random(123453).NextBytes(bytes); - break; - } - return bytes; - } - - public enum CompressionType - { - CryptoRandom = 1, - RepeatedSegments = 2, - VeryRepetitive = 3, - NormalData = 4 - } - - private const int Iter = 1000; - - [Benchmark(InnerIterationCount = Iter)] - [InlineData(CompressionType.CryptoRandom)] - [InlineData(CompressionType.RepeatedSegments)] - [InlineData(CompressionType.NormalData)] - public async Task Decompress(CompressionType type) - { - string testFilePath = CreateCompressedFile(type); - int _bufferSize = 1024; - var bytes = new byte[_bufferSize]; - using (MemoryStream gzStream = await LocalMemoryStream.readAppFileAsync(testFilePath)) - using (MemoryStream strippedMs = StripHeaderAndFooter.Strip(gzStream)) - foreach (var iteration in Benchmark.Iterations) - using (iteration.StartMeasurement()) - for (int i = 0; i < Benchmark.InnerIterationCount; i++) - { - int retCount = -1; - using (DeflateStream zip = new DeflateStream(strippedMs, CompressionMode.Decompress, leaveOpen: true)) - { - while (retCount != 0) - { - retCount = zip.Read(bytes, 0, _bufferSize); - } - } - strippedMs.Seek(0, SeekOrigin.Begin); - } - File.Delete(testFilePath); - } - - [Benchmark] - [InlineData(CompressionType.CryptoRandom)] - [InlineData(CompressionType.RepeatedSegments)] - [InlineData(CompressionType.VeryRepetitive)] - [InlineData(CompressionType.NormalData)] - public void Compress(CompressionType type) - { - byte[] bytes = CreateBytesToCompress(type); - PerfUtils utils = new PerfUtils(); - foreach (var iteration in Benchmark.Iterations) - { - string filePath = utils.GetTestFilePath(); - using (FileStream output = File.Create(filePath)) - using (DeflateStream zip = new DeflateStream(output, CompressionMode.Compress)) - using (iteration.StartMeasurement()) - { - zip.Write(bytes, 0, bytes.Length); - } - File.Delete(filePath); - } - } - } -} diff --git a/external/corefx/src/System.IO.Compression/tests/Performance/Perf.GZipStream.cs b/external/corefx/src/System.IO.Compression/tests/Performance/Perf.GZipStream.cs deleted file mode 100644 index bfbf4d68dc..0000000000 --- a/external/corefx/src/System.IO.Compression/tests/Performance/Perf.GZipStream.cs +++ /dev/null @@ -1,98 +0,0 @@ -// 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 Xunit; -using Microsoft.Xunit.Performance; -using System.Collections.Generic; -using System.Security.Cryptography; - -namespace System.IO.Compression.Tests -{ - public class Perf_GZipStream - { - public static IEnumerable CanterburyCorpus() - { - foreach (CompressionLevel compressionLevel in Enum.GetValues(typeof(CompressionLevel))) - { - foreach (int innerIterations in new int[] { 1, 10 }) - { - yield return new object[] { innerIterations, "alice29.txt", compressionLevel }; - yield return new object[] { innerIterations, "asyoulik.txt", compressionLevel }; - yield return new object[] { innerIterations, "cp.html", compressionLevel }; - yield return new object[] { innerIterations, "fields.c", compressionLevel }; - yield return new object[] { innerIterations, "grammar.lsp", compressionLevel }; - yield return new object[] { innerIterations, "kennedy.xls", compressionLevel }; - yield return new object[] { innerIterations, "lcet10.txt", compressionLevel }; - yield return new object[] { innerIterations, "plrabn12.txt", compressionLevel }; - yield return new object[] { innerIterations, "ptt5", compressionLevel }; - yield return new object[] { innerIterations, "sum", compressionLevel }; - yield return new object[] { innerIterations, "xargs.1", compressionLevel }; - } - } - } - - /// - /// Benchmark tests to measure the performance of individually compressing each file in the - /// Canterbury Corpus - /// - [Benchmark] - [MemberData(nameof(CanterburyCorpus))] - public void Compress_Canterbury(int innerIterations, string fileName, CompressionLevel compressLevel) - { - byte[] bytes = File.ReadAllBytes(Path.Combine("GZTestData", "Canterbury", fileName)); - PerfUtils utils = new PerfUtils(); - FileStream[] filestreams = new FileStream[innerIterations]; - GZipStream[] gzips = new GZipStream[innerIterations]; - string[] paths = new string[innerIterations]; - foreach (var iteration in Benchmark.Iterations) - { - for (int i = 0; i < innerIterations; i++) - { - paths[i] = utils.GetTestFilePath(); - filestreams[i] = File.Create(paths[i]); - } - using (iteration.StartMeasurement()) - for (int i = 0; i < innerIterations; i++) - { - gzips[i] = new GZipStream(filestreams[i], compressLevel); - gzips[i].Write(bytes, 0, bytes.Length); - gzips[i].Flush(); - gzips[i].Dispose(); - filestreams[i].Dispose(); - } - for (int i = 0; i < innerIterations; i++) - File.Delete(paths[i]); - } - } - - /// - /// Benchmark tests to measure the performance of individually compressing each file in the - /// Canterbury Corpus - /// - [Benchmark] - [MemberData(nameof(CanterburyCorpus))] - public void Decompress_Canterbury(int innerIterations, string fileName, CompressionLevel compressLevel) - { - string zipFilePath = Path.Combine("GZTestData", "Canterbury", "GZCompressed", fileName + ".gz"); - string sourceFilePath = Path.Combine("GZTestData", "Canterbury", fileName); - byte[] outputRead = new byte[new FileInfo(sourceFilePath).Length]; - MemoryStream[] memories = new MemoryStream[innerIterations]; - GZipStream[] gzips = new GZipStream[innerIterations]; - foreach (var iteration in Benchmark.Iterations) - { - for (int i = 0; i < innerIterations; i++) - memories[i] = new MemoryStream(File.ReadAllBytes(zipFilePath)); - - using (iteration.StartMeasurement()) - for (int i = 0; i < innerIterations; i++) - using (GZipStream unzip = new GZipStream(memories[i], CompressionMode.Decompress)) - unzip.Read(outputRead, 0, outputRead.Length); - - for (int i = 0; i < innerIterations; i++) - memories[i].Dispose(); - } - } - } - -} diff --git a/external/corefx/src/System.IO.Compression/tests/Performance/System.IO.Compression.Performance.Tests.csproj b/external/corefx/src/System.IO.Compression/tests/Performance/System.IO.Compression.Performance.Tests.csproj index 23a2ae882b..762e2f3a7b 100644 --- a/external/corefx/src/System.IO.Compression/tests/Performance/System.IO.Compression.Performance.Tests.csproj +++ b/external/corefx/src/System.IO.Compression/tests/Performance/System.IO.Compression.Performance.Tests.csproj @@ -9,21 +9,23 @@ - + + Common\System\IO\Compression\LocalMemoryStream.cs Common\System\IO\Compression\StreamHelpers.cs - - - - Common\System\PerfUtils.cs + + Common\System\IO\Compression\CompressionStreamTestBase.cs + + + Common\System\IO\Compression\CompressionStreamPerfTestBase.cs - + %(RecursiveDir)%(Filename)%(Extension) diff --git a/external/corefx/src/System.IO.Compression/tests/System.IO.Compression.Tests.csproj b/external/corefx/src/System.IO.Compression/tests/System.IO.Compression.Tests.csproj index 9496693e27..7e69ef58b8 100644 --- a/external/corefx/src/System.IO.Compression/tests/System.IO.Compression.Tests.csproj +++ b/external/corefx/src/System.IO.Compression/tests/System.IO.Compression.Tests.csproj @@ -3,6 +3,7 @@ {BC2E1649-291D-412E-9529-EDDA94FA7AD6} + $(DefineConstants);netcoreapp;STREAM_MEMORY_OVERLOADS_AVAILABLE @@ -15,9 +16,6 @@ - - - @@ -35,6 +33,12 @@ Common\System\IO\Compression\CRC.cs + + Common\System\IO\Compression\CompressionStreamTestBase.cs + + + Common\System\IO\Compression\CompressionStreamUnitTestBase.cs + Common\System\IO\Compression\FileData.cs @@ -55,11 +59,15 @@ - - + + + + + + - + %(RecursiveDir)%(Filename)%(Extension) diff --git a/external/corefx/src/System.IO.Compression/tests/ZipArchive/zip_CreateTests.cs b/external/corefx/src/System.IO.Compression/tests/ZipArchive/zip_CreateTests.cs index 6addc89918..e8e08c7ae8 100644 --- a/external/corefx/src/System.IO.Compression/tests/ZipArchive/zip_CreateTests.cs +++ b/external/corefx/src/System.IO.Compression/tests/ZipArchive/zip_CreateTests.cs @@ -53,17 +53,20 @@ namespace System.IO.Compression.Tests } [Theory] - [InlineData("small", false)] - [InlineData("normal", false)] - [InlineData("normal", true)] - [InlineData("empty", false)] - [InlineData("emptydir", false)] - public static async Task CreateNormal_Seekable(string folder, bool useSpansForWriting) + [InlineData("small", false, false)] + [InlineData("normal", false, false)] + [InlineData("empty", false, false)] + [InlineData("emptydir", false, false)] + [InlineData("small", true, false)] + [InlineData("normal", true, false)] + [InlineData("small", false, true)] + [InlineData("normal", false, true)] + public static async Task CreateNormal_Seekable(string folder, bool useSpansForWriting, bool writeInChunks) { using (var s = new MemoryStream()) { var testStream = new WrappedStream(s, false, true, true, null); - await CreateFromDir(zfolder(folder), testStream, ZipArchiveMode.Create, useSpansForWriting); + await CreateFromDir(zfolder(folder), testStream, ZipArchiveMode.Create, useSpansForWriting, writeInChunks); IsZipSameAsDir(s, zfolder(folder), ZipArchiveMode.Read, requireExplicit: true, checkTimes: true); } diff --git a/external/corefx/src/System.IO.Compression/tests/ZipArchive/zip_ReadTests.cs b/external/corefx/src/System.IO.Compression/tests/ZipArchive/zip_ReadTests.cs index 27fc482f1a..654e10b188 100644 --- a/external/corefx/src/System.IO.Compression/tests/ZipArchive/zip_ReadTests.cs +++ b/external/corefx/src/System.IO.Compression/tests/ZipArchive/zip_ReadTests.cs @@ -2,7 +2,6 @@ // 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.Runtime.InteropServices; using System.Threading.Tasks; using Xunit; diff --git a/external/corefx/src/System.IO.FileSystem.AccessControl/System.IO.FileSystem.AccessControl.sln b/external/corefx/src/System.IO.FileSystem.AccessControl/System.IO.FileSystem.AccessControl.sln index 8b2d0ec22a..b732004556 100644 --- a/external/corefx/src/System.IO.FileSystem.AccessControl/System.IO.FileSystem.AccessControl.sln +++ b/external/corefx/src/System.IO.FileSystem.AccessControl/System.IO.FileSystem.AccessControl.sln @@ -30,10 +30,10 @@ Global {5915DD11-5D57-45A9-BFB0-56FEB7741E1F}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU {5915DD11-5D57-45A9-BFB0-56FEB7741E1F}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU {5915DD11-5D57-45A9-BFB0-56FEB7741E1F}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU - {D77FBA6C-1AA6-45A4-93E2-97A370672C53}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU - {D77FBA6C-1AA6-45A4-93E2-97A370672C53}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU - {D77FBA6C-1AA6-45A4-93E2-97A370672C53}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU - {D77FBA6C-1AA6-45A4-93E2-97A370672C53}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {D77FBA6C-1AA6-45A4-93E2-97A370672C53}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {D77FBA6C-1AA6-45A4-93E2-97A370672C53}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {D77FBA6C-1AA6-45A4-93E2-97A370672C53}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {D77FBA6C-1AA6-45A4-93E2-97A370672C53}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU {88A04AB0-F61E-4DD2-9E12-928DCA261263}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU {88A04AB0-F61E-4DD2-9E12-928DCA261263}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU {88A04AB0-F61E-4DD2-9E12-928DCA261263}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU diff --git a/external/corefx/src/System.IO.FileSystem.AccessControl/ref/System.IO.FileSystem.AccessControl.cs b/external/corefx/src/System.IO.FileSystem.AccessControl/ref/System.IO.FileSystem.AccessControl.cs index 874fb94866..b3360184ac 100644 --- a/external/corefx/src/System.IO.FileSystem.AccessControl/ref/System.IO.FileSystem.AccessControl.cs +++ b/external/corefx/src/System.IO.FileSystem.AccessControl/ref/System.IO.FileSystem.AccessControl.cs @@ -8,7 +8,6 @@ namespace System.IO { - [System.Security.SecurityCriticalAttribute] public static partial class FileSystemAclExtensions { public static System.Security.AccessControl.DirectorySecurity GetAccessControl(this System.IO.DirectoryInfo directoryInfo) { throw null; } @@ -23,7 +22,6 @@ namespace System.IO } namespace System.Security.AccessControl { - [System.Security.SecurityCriticalAttribute] public abstract partial class DirectoryObjectSecurity : System.Security.AccessControl.ObjectSecurity { protected DirectoryObjectSecurity() { } @@ -46,19 +44,16 @@ namespace System.Security.AccessControl protected void SetAccessRule(System.Security.AccessControl.ObjectAccessRule rule) { } protected void SetAuditRule(System.Security.AccessControl.ObjectAuditRule rule) { } } - [System.Security.SecurityCriticalAttribute] public sealed partial class DirectorySecurity : System.Security.AccessControl.FileSystemSecurity { public DirectorySecurity() { } public DirectorySecurity(string name, System.Security.AccessControl.AccessControlSections includeSections) { } } - [System.Security.SecurityCriticalAttribute] public sealed partial class FileSecurity : System.Security.AccessControl.FileSystemSecurity { public FileSecurity() { } public FileSecurity(string fileName, System.Security.AccessControl.AccessControlSections includeSections) { } } - [System.Security.SecurityCriticalAttribute] public sealed partial class FileSystemAccessRule : System.Security.AccessControl.AccessRule { public FileSystemAccessRule(System.Security.Principal.IdentityReference identity, System.Security.AccessControl.FileSystemRights fileSystemRights, System.Security.AccessControl.AccessControlType type) : base(default(System.Security.Principal.IdentityReference), default(int), default(bool), default(System.Security.AccessControl.InheritanceFlags), default(System.Security.AccessControl.PropagationFlags), default(System.Security.AccessControl.AccessControlType)) { } @@ -67,7 +62,6 @@ namespace System.Security.AccessControl public FileSystemAccessRule(string identity, System.Security.AccessControl.FileSystemRights fileSystemRights, System.Security.AccessControl.InheritanceFlags inheritanceFlags, System.Security.AccessControl.PropagationFlags propagationFlags, System.Security.AccessControl.AccessControlType type) : base(default(System.Security.Principal.IdentityReference), default(int), default(bool), default(System.Security.AccessControl.InheritanceFlags), default(System.Security.AccessControl.PropagationFlags), default(System.Security.AccessControl.AccessControlType)) { } public System.Security.AccessControl.FileSystemRights FileSystemRights { get { throw null; } } } - [System.Security.SecurityCriticalAttribute] public sealed partial class FileSystemAuditRule : System.Security.AccessControl.AuditRule { public FileSystemAuditRule(System.Security.Principal.IdentityReference identity, System.Security.AccessControl.FileSystemRights fileSystemRights, System.Security.AccessControl.AuditFlags flags) : base(default(System.Security.Principal.IdentityReference), default(int), default(bool), default(System.Security.AccessControl.InheritanceFlags), default(System.Security.AccessControl.PropagationFlags), default(System.Security.AccessControl.AuditFlags)) { } @@ -77,7 +71,6 @@ namespace System.Security.AccessControl public System.Security.AccessControl.FileSystemRights FileSystemRights { get { throw null; } } } [System.FlagsAttribute] - [System.Security.SecurityCriticalAttribute] public enum FileSystemRights { AppendData = 4, @@ -104,7 +97,6 @@ namespace System.Security.AccessControl WriteData = 2, WriteExtendedAttributes = 16, } - [System.Security.SecurityCriticalAttribute] public abstract partial class FileSystemSecurity : System.Security.AccessControl.NativeObjectSecurity { internal FileSystemSecurity() : base(default(bool), default(System.Security.AccessControl.ResourceType)) { } diff --git a/external/corefx/src/System.IO.FileSystem.AccessControl/src/Configurations.props b/external/corefx/src/System.IO.FileSystem.AccessControl/src/Configurations.props index 802d7df110..9648129983 100644 --- a/external/corefx/src/System.IO.FileSystem.AccessControl/src/Configurations.props +++ b/external/corefx/src/System.IO.FileSystem.AccessControl/src/Configurations.props @@ -1,11 +1,14 @@  - - netfx-Windows_NT; - netcoreapp2.0-Windows_NT; - netcoreapp2.0-Unix; - netstandard; - + + netstandard; + netfx-Windows_NT; + netcoreapp2.0-Windows_NT; + + + $(PackageConfigurations); + netcoreapp-Windows_NT; + \ No newline at end of file diff --git a/external/corefx/src/System.IO.FileSystem.AccessControl/src/Resources/Strings.resx b/external/corefx/src/System.IO.FileSystem.AccessControl/src/Resources/Strings.resx index 6627b9f335..ce6ad0ecbc 100644 --- a/external/corefx/src/System.IO.FileSystem.AccessControl/src/Resources/Strings.resx +++ b/external/corefx/src/System.IO.FileSystem.AccessControl/src/Resources/Strings.resx @@ -138,4 +138,10 @@ Access Control List (ACL) APIs are part of resource management on Windows and are not supported on this platform. - \ No newline at end of file + + AccessControlType '{0}' unrecognized + + + Remove access rule failed + + diff --git a/external/corefx/src/System.IO.FileSystem.AccessControl/src/System.IO.FileSystem.AccessControl.csproj b/external/corefx/src/System.IO.FileSystem.AccessControl/src/System.IO.FileSystem.AccessControl.csproj index bed54d05f4..1073ac3180 100644 --- a/external/corefx/src/System.IO.FileSystem.AccessControl/src/System.IO.FileSystem.AccessControl.csproj +++ b/external/corefx/src/System.IO.FileSystem.AccessControl/src/System.IO.FileSystem.AccessControl.csproj @@ -7,20 +7,19 @@ System.IO.FileSystem.AccessControl {D77FBA6C-1AA6-45A4-93E2-97A370672C53} - true + true true - SR.PlatformNotSupported_AccessControl + SR.PlatformNotSupported_AccessControl - - - + + - + Common\Interop\Windows\Interop.Errors.cs @@ -37,7 +36,6 @@ - @@ -48,4 +46,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.IO.FileSystem.AccessControl/src/System/IO/FileSystemAclExtensions.cs b/external/corefx/src/System.IO.FileSystem.AccessControl/src/System/IO/FileSystemAclExtensions.cs index fbc5267455..852632980b 100644 --- a/external/corefx/src/System.IO.FileSystem.AccessControl/src/System/IO/FileSystemAclExtensions.cs +++ b/external/corefx/src/System.IO.FileSystem.AccessControl/src/System/IO/FileSystemAclExtensions.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.Win32.SafeHandles; -using System.Diagnostics.Contracts; using System.Security.AccessControl; using System; @@ -25,7 +24,6 @@ namespace System.IO { if (directorySecurity == null) throw new ArgumentNullException(nameof(directorySecurity)); - Contract.EndContractBlock(); String fullPath = Path.GetFullPath(directoryInfo.FullName); directorySecurity.Persist(fullPath); @@ -45,7 +43,6 @@ namespace System.IO { if (fileSecurity == null) throw new ArgumentNullException(nameof(fileSecurity)); - Contract.EndContractBlock(); String fullPath = Path.GetFullPath(fileInfo.FullName); // Appropriate security check should be done for us by FileSecurity. @@ -62,14 +59,12 @@ namespace System.IO return new FileSecurity(handle, fileStream.Name, AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group); } - [System.Security.SecuritySafeCritical] // auto-generated public static void SetAccessControl(this FileStream fileStream, FileSecurity fileSecurity) { SafeFileHandle handle = fileStream.SafeFileHandle; if (fileSecurity == null) throw new ArgumentNullException(nameof(fileSecurity)); - Contract.EndContractBlock(); if (handle.IsClosed) { diff --git a/external/corefx/src/System.IO.FileSystem.AccessControl/src/System/Security/AccessControl/DirectoryObjectSecurity.cs b/external/corefx/src/System.IO.FileSystem.AccessControl/src/System/Security/AccessControl/DirectoryObjectSecurity.cs index 726423c6c6..75d79bb1eb 100644 --- a/external/corefx/src/System.IO.FileSystem.AccessControl/src/System/Security/AccessControl/DirectoryObjectSecurity.cs +++ b/external/corefx/src/System.IO.FileSystem.AccessControl/src/System/Security/AccessControl/DirectoryObjectSecurity.cs @@ -10,7 +10,6 @@ using Microsoft.Win32.SafeHandles; using Microsoft.Win32; using System.Collections; -using System.Diagnostics.Contracts; using System.Diagnostics; using System.IO; using System.Reflection; @@ -45,7 +44,6 @@ namespace System.Security.AccessControl { throw new ArgumentNullException(nameof(securityDescriptor)); } - Contract.EndContractBlock(); _securityDescriptor = securityDescriptor; } @@ -347,9 +345,8 @@ namespace System.Security.AccessControl case AccessControlModification.RemoveAll: result = _securityDescriptor.DiscretionaryAcl.RemoveAccess(AccessControlType.Allow, sid, -1, InheritanceFlags.ContainerInherit, 0, ObjectAceFlags.None, Guid.Empty, Guid.Empty); if (result == false) - { - Debug.Assert(false, "Invalid operation"); - throw new Exception(); + { + throw new InvalidOperationException(SR.InvalidOperation_RemoveFail); } break; @@ -393,9 +390,8 @@ nameof(modification), case AccessControlModification.RemoveAll: result = _securityDescriptor.DiscretionaryAcl.RemoveAccess(AccessControlType.Deny, sid, -1, InheritanceFlags.ContainerInherit, 0, ObjectAceFlags.None, Guid.Empty, Guid.Empty); if (result == false) - { - Debug.Assert(false, "Invalid operation"); - throw new Exception(); + { + throw new InvalidOperationException(SR.InvalidOperation_RemoveFail); } break; @@ -412,9 +408,8 @@ nameof(modification), } } else - { - Debug.Assert(false, "rule.AccessControlType unrecognized"); - throw new Exception(); + { + throw new ArgumentException(SR.Format(SR.TypeUnrecognized_AccessControl, rule.AccessControlType)); } modified = result; @@ -488,9 +483,8 @@ nameof(modification), case AccessControlModification.RemoveAll: result = _securityDescriptor.SystemAcl.RemoveAudit(AuditFlags.Failure | AuditFlags.Success, sid, -1, InheritanceFlags.ContainerInherit, 0, ObjectAceFlags.None, Guid.Empty, Guid.Empty); if (result == false) - { - Debug.Assert(false, "Invalid operation"); - throw new Exception(); + { + throw new InvalidOperationException(SR.InvalidOperation_RemoveFail); } break; @@ -534,7 +528,6 @@ nameof(modification), // SR.AccessControl_InvalidAccessRuleType, // "rule"); //} - Contract.EndContractBlock(); return ModifyAccess(modification, rule as ObjectAccessRule, out modified); } @@ -547,7 +540,6 @@ nameof(modification), // SR.AccessControl_InvalidAuditRuleType, // "rule"); //} - Contract.EndContractBlock(); return ModifyAudit(modification, rule as ObjectAuditRule, out modified); } #endregion @@ -560,7 +552,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); @@ -583,7 +574,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); @@ -604,7 +594,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); @@ -625,7 +614,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); @@ -651,7 +639,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); @@ -677,7 +664,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); if (_securityDescriptor == null) { @@ -703,7 +689,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); @@ -724,7 +709,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); @@ -745,7 +729,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); @@ -766,7 +749,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); @@ -787,7 +769,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); diff --git a/external/corefx/src/System.IO.FileSystem.AccessControl/src/System/Security/AccessControl/FileSecurity.cs b/external/corefx/src/System.IO.FileSystem.AccessControl/src/System/Security/AccessControl/FileSecurity.cs index ecac90d7aa..62b0819bba 100644 --- a/external/corefx/src/System.IO.FileSystem.AccessControl/src/System/Security/AccessControl/FileSecurity.cs +++ b/external/corefx/src/System.IO.FileSystem.AccessControl/src/System/Security/AccessControl/FileSecurity.cs @@ -14,7 +14,6 @@ using Microsoft.Win32.SafeHandles; using Microsoft.Win32; using System.Collections; -using System.Diagnostics.Contracts; using System.IO; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -181,7 +180,6 @@ namespace System.Security.AccessControl { if (fileSystemRights < (FileSystemRights)0 || fileSystemRights > FileSystemRights.FullControl) throw new ArgumentOutOfRangeException(nameof(fileSystemRights), SR.Format(SR.Argument_InvalidEnumValue, fileSystemRights, nameof(AccessControl.FileSystemRights))); - Contract.EndContractBlock(); if (controlType == AccessControlType.Allow) { @@ -292,7 +290,6 @@ namespace System.Security.AccessControl { if (fileSystemRights < (FileSystemRights)0 || fileSystemRights > FileSystemRights.FullControl) throw new ArgumentOutOfRangeException(nameof(fileSystemRights), SR.Format(SR.Argument_InvalidEnumValue, fileSystemRights, nameof(AccessControl.FileSystemRights))); - Contract.EndContractBlock(); return (int)fileSystemRights; } @@ -317,25 +314,21 @@ namespace System.Security.AccessControl #endregion - [System.Security.SecurityCritical] // auto-generated internal FileSystemSecurity(bool isContainer) : base(isContainer, s_ResourceType, _HandleErrorCode, isContainer) { } - [System.Security.SecurityCritical] // auto-generated internal FileSystemSecurity(bool isContainer, String name, AccessControlSections includeSections, bool isDirectory) : base(isContainer, s_ResourceType, name, includeSections, _HandleErrorCode, isDirectory) { } - [System.Security.SecurityCritical] // auto-generated internal FileSystemSecurity(bool isContainer, SafeFileHandle handle, AccessControlSections includeSections, bool isDirectory) : base(isContainer, s_ResourceType, handle, includeSections, _HandleErrorCode, isDirectory) { } - [System.Security.SecurityCritical] // auto-generated private static Exception _HandleErrorCode(int errorCode, string name, SafeHandle handle, object context) { System.Exception exception = null; @@ -428,7 +421,6 @@ namespace System.Security.AccessControl return persistRules; } - [System.Security.SecurityCritical] // auto-generated internal void Persist(String fullPath) { WriteLock(); @@ -445,7 +437,6 @@ namespace System.Security.AccessControl } } - [System.Security.SecuritySafeCritical] // auto-generated internal void Persist(SafeFileHandle handle, String fullPath) { WriteLock(); @@ -486,7 +477,6 @@ namespace System.Security.AccessControl { if (rule == null) throw new ArgumentNullException(nameof(rule)); - Contract.EndContractBlock(); // If the rule to be removed matches what is there currently then // remove it unaltered. That is, don't mask off the Synchronize bit. @@ -533,7 +523,6 @@ namespace System.Security.AccessControl { if (rule == null) throw new ArgumentNullException(nameof(rule)); - Contract.EndContractBlock(); // If the rule to be removed matches what is there currently then // remove it unaltered. That is, don't mask off the Synchronize bit @@ -618,13 +607,11 @@ namespace System.Security.AccessControl { #region Constructors - [System.Security.SecuritySafeCritical] // auto-generated public FileSecurity() : base(false) { } - [System.Security.SecuritySafeCritical] // auto-generated public FileSecurity(String fileName, AccessControlSections includeSections) : base(false, fileName, includeSections, false) { @@ -635,7 +622,6 @@ namespace System.Security.AccessControl // it public. We don't want to get into a situation where someone can // pass in the string foo.txt and a handle to bar.exe, and we do a // demand on the wrong file name. - [System.Security.SecurityCritical] // auto-generated internal FileSecurity(SafeFileHandle handle, String fullPath, AccessControlSections includeSections) : base(false, handle, includeSections, false) { @@ -648,13 +634,11 @@ namespace System.Security.AccessControl { #region Constructors - [System.Security.SecuritySafeCritical] // auto-generated public DirectorySecurity() : base(true) { } - [System.Security.SecuritySafeCritical] // auto-generated public DirectorySecurity(String name, AccessControlSections includeSections) : base(true, name, includeSections, true) { diff --git a/external/corefx/src/System.IO.FileSystem.AccessControl/tests/DirectoryObjectSecurityTests.cs b/external/corefx/src/System.IO.FileSystem.AccessControl/tests/DirectoryObjectSecurityTests.cs index a70375cf5f..126bf39127 100644 --- a/external/corefx/src/System.IO.FileSystem.AccessControl/tests/DirectoryObjectSecurityTests.cs +++ b/external/corefx/src/System.IO.FileSystem.AccessControl/tests/DirectoryObjectSecurityTests.cs @@ -43,6 +43,7 @@ namespace System.Security.AccessControl } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public void GetAccessRules_InvalidTargetType() { var activeDirectorySecurity = new ActiveDirectorySecurity(); @@ -492,6 +493,23 @@ namespace System.Security.AccessControl Assert.False(existingRules.Contains(customAccessRuleSynchronize)); } + [Fact] + public void RemoveAccessRuleAll_AccessControlType_Deny_ThrowException() + { + var descriptor = new CommonSecurityDescriptor(true, true, string.Empty); + var customObjectSecurity = new CustomDirectoryObjectSecurity(descriptor); + + var objectTypeGuid = Guid.NewGuid(); + var identityReference = new NTAccount(@"NT AUTHORITY\SYSTEM"); + var customAccessRuleReadWrite = new CustomAccessRule( + identityReference, ReadWriteAccessMask, true, InheritanceFlags.ObjectInherit, + PropagationFlags.InheritOnly, objectTypeGuid, Guid.NewGuid(), AccessControlType.Deny + ); + + customObjectSecurity.AddAccessRule(customAccessRuleReadWrite); + AssertExtensions.Throws(() => customObjectSecurity.RemoveAccessRuleAll(customAccessRuleReadWrite)); + } + [Fact] public void RemoveAccessRuleAll_AccessControlType_Deny_Succeeds() { diff --git a/external/corefx/src/System.IO.FileSystem.DriveInfo/src/Resources/Strings.resx b/external/corefx/src/System.IO.FileSystem.DriveInfo/src/Resources/Strings.resx index 9d52bf09bf..a3224f0013 100644 --- a/external/corefx/src/System.IO.FileSystem.DriveInfo/src/Resources/Strings.resx +++ b/external/corefx/src/System.IO.FileSystem.DriveInfo/src/Resources/Strings.resx @@ -1,5 +1,64 @@  + @@ -68,7 +127,7 @@ Illegal characters in drive name '{0}'. - Illegal characters in path. + Illegal characters in path '{0}'. Specified file length was too large for the file system. @@ -115,4 +174,7 @@ Access to the path '{0}' is denied. + + The path '{0}' is too long, or a component of the specified path is too long. + \ No newline at end of file diff --git a/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System.IO.FileSystem.DriveInfo.csproj b/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System.IO.FileSystem.DriveInfo.csproj index 949f2bf223..056e32b1d7 100644 --- a/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System.IO.FileSystem.DriveInfo.csproj +++ b/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System.IO.FileSystem.DriveInfo.csproj @@ -98,7 +98,6 @@ - @@ -109,4 +108,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.Unix.cs b/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.Unix.cs index 08b4cbcc81..a94fdef449 100644 --- a/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.Unix.cs +++ b/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.Unix.cs @@ -36,7 +36,6 @@ namespace System.IO public DriveType DriveType { - [SecuritySafeCritical] get { DriveType type; @@ -67,7 +66,6 @@ namespace System.IO public string DriveFormat { - [SecuritySafeCritical] get { string format = string.Empty; @@ -78,7 +76,6 @@ namespace System.IO public long AvailableFreeSpace { - [SecuritySafeCritical] get { Interop.Sys.MountPointInformation mpi = default(Interop.Sys.MountPointInformation); @@ -89,7 +86,6 @@ namespace System.IO public long TotalFreeSpace { - [SecuritySafeCritical] get { Interop.Sys.MountPointInformation mpi = default(Interop.Sys.MountPointInformation); @@ -100,7 +96,6 @@ namespace System.IO public long TotalSize { - [SecuritySafeCritical] get { Interop.Sys.MountPointInformation mpi = default(Interop.Sys.MountPointInformation); @@ -111,12 +106,10 @@ namespace System.IO public string VolumeLabel { - [SecuritySafeCritical] get { return Name; } - [SecuritySafeCritical] set { throw new PlatformNotSupportedException(); diff --git a/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.Windows.cs b/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.Windows.cs index f329edaf52..361cbbe601 100644 --- a/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.Windows.cs +++ b/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.Windows.cs @@ -21,10 +21,6 @@ namespace System.IO name = driveName + ":\\"; else { - // GetPathRoot does not check all invalid characters - if (PathInternal.HasIllegalCharacters(driveName)) - throw new ArgumentException(SR.Format(SR.Arg_InvalidDriveChars, driveName), nameof(driveName)); - name = Path.GetPathRoot(driveName); // Disallow null or empty drive letters and UNC paths if (name == null || name.Length == 0 || name.StartsWith("\\\\", StringComparison.Ordinal)) @@ -48,7 +44,6 @@ namespace System.IO public DriveType DriveType { - [System.Security.SecuritySafeCritical] get { // GetDriveType can't fail @@ -58,7 +53,6 @@ namespace System.IO public String DriveFormat { - [System.Security.SecuritySafeCritical] // auto-generated get { const int volNameLen = 50; @@ -88,7 +82,6 @@ namespace System.IO public long AvailableFreeSpace { - [System.Security.SecuritySafeCritical] get { long userBytes, totalBytes, freeBytes; @@ -111,7 +104,6 @@ namespace System.IO public long TotalFreeSpace { - [System.Security.SecuritySafeCritical] // auto-generated get { long userBytes, totalBytes, freeBytes; @@ -134,7 +126,6 @@ namespace System.IO public long TotalSize { - [System.Security.SecuritySafeCritical] get { // Don't cache this, to handle variable sized floppy drives @@ -170,7 +161,6 @@ namespace System.IO // Null is a valid volume label. public String VolumeLabel { - [System.Security.SecuritySafeCritical] // auto-generated get { // NTFS uses a limit of 32 characters for the volume label, @@ -203,7 +193,6 @@ namespace System.IO } return volumeName.ToString(); } - [System.Security.SecuritySafeCritical] // auto-generated set { uint oldMode; diff --git a/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.cs b/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.cs index 34aedfb26c..5aad1e0031 100644 --- a/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.cs +++ b/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.cs @@ -21,8 +21,7 @@ namespace System.IO _name = NormalizeDriveName(driveName); } - private DriveInfo(SerializationInfo info, StreamingContext context) : - this((string)info.GetValue("_name", typeof(string))) + private DriveInfo(SerializationInfo info, StreamingContext context) { throw new PlatformNotSupportedException(); } diff --git a/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System/IO/DriveNotFoundException.cs b/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System/IO/DriveNotFoundException.cs index 54cd480f84..f397134393 100644 --- a/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System/IO/DriveNotFoundException.cs +++ b/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System/IO/DriveNotFoundException.cs @@ -8,6 +8,9 @@ namespace System.IO { //Thrown when trying to access a drive that is not available. [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class DriveNotFoundException : IOException { public DriveNotFoundException() @@ -30,7 +33,6 @@ namespace System.IO protected DriveNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System/IO/Error.cs b/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System/IO/Error.cs index 48934877e3..eb39bfa34c 100644 --- a/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System/IO/Error.cs +++ b/external/corefx/src/System.IO.FileSystem.DriveInfo/src/System/IO/Error.cs @@ -6,22 +6,18 @@ using System; using System.Runtime.InteropServices; using System.Text; using System.Globalization; -using System.Diagnostics.Contracts; namespace System.IO { - [Pure] internal static class Error { // An alternative to Win32Marshal with friendlier messages for drives - [System.Security.SecuritySafeCritical] // auto-generated internal static Exception GetExceptionForLastWin32DriveError(String driveName) { int errorCode = Marshal.GetLastWin32Error(); return GetExceptionForWin32DriveError(errorCode, driveName); } - [System.Security.SecurityCritical] // auto-generated internal static Exception GetExceptionForWin32DriveError(int errorCode, String driveName) { switch (errorCode) diff --git a/external/corefx/src/System.IO.FileSystem.DriveInfo/tests/DriveInfo.Windows.Tests.cs b/external/corefx/src/System.IO.FileSystem.DriveInfo/tests/DriveInfo.Windows.Tests.cs index ec841e5b22..c1ff89a3b8 100644 --- a/external/corefx/src/System.IO.FileSystem.DriveInfo/tests/DriveInfo.Windows.Tests.cs +++ b/external/corefx/src/System.IO.FileSystem.DriveInfo/tests/DriveInfo.Windows.Tests.cs @@ -17,7 +17,6 @@ namespace System.IO.FileSystem.DriveInfoTests public class DriveInfoWindowsTests { [Theory] - [InlineData(":\0", "driveName")] [InlineData(":", null)] [InlineData("://", null)] [InlineData(@":\", null)] diff --git a/external/corefx/src/System.IO.FileSystem.DriveInfo/tests/System.IO.FileSystem.DriveInfo.Tests.csproj b/external/corefx/src/System.IO.FileSystem.DriveInfo/tests/System.IO.FileSystem.DriveInfo.Tests.csproj index 47a347703c..94cd97d78d 100644 --- a/external/corefx/src/System.IO.FileSystem.DriveInfo/tests/System.IO.FileSystem.DriveInfo.Tests.csproj +++ b/external/corefx/src/System.IO.FileSystem.DriveInfo/tests/System.IO.FileSystem.DriveInfo.Tests.csproj @@ -4,7 +4,6 @@ {7D9E5F2F-5677-40FC-AD04-FA7D603E4806} - diff --git a/external/corefx/src/System.IO.FileSystem.Watcher/ref/System.IO.FileSystem.Watcher.cs b/external/corefx/src/System.IO.FileSystem.Watcher/ref/System.IO.FileSystem.Watcher.cs index b33b529686..e21c96de3c 100644 --- a/external/corefx/src/System.IO.FileSystem.Watcher/ref/System.IO.FileSystem.Watcher.cs +++ b/external/corefx/src/System.IO.FileSystem.Watcher/ref/System.IO.FileSystem.Watcher.cs @@ -5,7 +5,6 @@ // Changes to this file must follow the http://aka.ms/api-review process. // ------------------------------------------------------------------------------ - namespace System.IO { public partial class ErrorEventArgs : System.EventArgs @@ -33,11 +32,16 @@ namespace System.IO public int InternalBufferSize { get { throw null; } set { } } public System.IO.NotifyFilters NotifyFilter { get { throw null; } set { } } public string Path { get { throw null; } set { } } + public override System.ComponentModel.ISite Site { get { throw null; } set { } } + public System.ComponentModel.ISynchronizeInvoke SynchronizingObject { get { throw null; } set { } } public event System.IO.FileSystemEventHandler Changed { add { } remove { } } public event System.IO.FileSystemEventHandler Created { add { } remove { } } public event System.IO.FileSystemEventHandler Deleted { add { } remove { } } public event System.IO.ErrorEventHandler Error { add { } remove { } } public event System.IO.RenamedEventHandler Renamed { add { } remove { } } + public void BeginInit() { } + protected override void Dispose(bool disposing) { } + public void EndInit() { } protected void OnChanged(System.IO.FileSystemEventArgs e) { } protected void OnCreated(System.IO.FileSystemEventArgs e) { } protected void OnDeleted(System.IO.FileSystemEventArgs e) { } @@ -45,19 +49,14 @@ namespace System.IO protected void OnRenamed(System.IO.RenamedEventArgs e) { } public System.IO.WaitForChangedResult WaitForChanged(System.IO.WatcherChangeTypes changeType) { throw null; } public System.IO.WaitForChangedResult WaitForChanged(System.IO.WatcherChangeTypes changeType, int timeout) { throw null; } - public override System.ComponentModel.ISite Site { get { throw null; } set { } } - public System.ComponentModel.ISynchronizeInvoke SynchronizingObject { get { throw null; } set { } } - public void BeginInit() { } - protected override void Dispose(bool disposing) { } - public void EndInit() { } } [Serializable] public partial class InternalBufferOverflowException : System.SystemException { public InternalBufferOverflowException() { } + protected InternalBufferOverflowException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public InternalBufferOverflowException(string message) { } public InternalBufferOverflowException(string message, System.Exception inner) { } - protected InternalBufferOverflowException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } [System.FlagsAttribute] public enum NotifyFilters @@ -73,13 +72,14 @@ namespace System.IO } public partial class RenamedEventArgs : System.IO.FileSystemEventArgs { - public RenamedEventArgs(System.IO.WatcherChangeTypes changeType, string directory, string name, string oldName) : base(default(System.IO.WatcherChangeTypes), default(string), default(string)) { } + public RenamedEventArgs(System.IO.WatcherChangeTypes changeType, string directory, string name, string oldName) : base (default(System.IO.WatcherChangeTypes), default(string), default(string)) { } public string OldFullPath { get { throw null; } } public string OldName { get { throw null; } } } public delegate void RenamedEventHandler(object sender, System.IO.RenamedEventArgs e); - public struct WaitForChangedResult + public partial struct WaitForChangedResult { + private object _dummy; public System.IO.WatcherChangeTypes ChangeType { get { throw null; } set { } } public string Name { get { throw null; } set { } } public string OldName { get { throw null; } set { } } diff --git a/external/corefx/src/System.IO.FileSystem.Watcher/src/Resources/Strings.resx b/external/corefx/src/System.IO.FileSystem.Watcher/src/Resources/Strings.resx index 234dc28122..36aa5fbc01 100644 --- a/external/corefx/src/System.IO.FileSystem.Watcher/src/Resources/Strings.resx +++ b/external/corefx/src/System.IO.FileSystem.Watcher/src/Resources/Strings.resx @@ -1,5 +1,64 @@  + @@ -70,6 +129,9 @@ The directory name {0} is invalid. + + The directory name '{0}' does not exist. + The value of argument '{0}' ({1}) is invalid for Enum type '{2}'. @@ -110,7 +172,7 @@ Failed to start the EventStream - Illegal characters in path. + Illegal characters in path '{0}'. The system limit on the number of inotify instances has been reached. @@ -127,4 +189,7 @@ The configured user limit on the number of inotify watches has been reached. - \ No newline at end of file + + The path '{0}' is too long, or a component of the specified path is too long. + + diff --git a/external/corefx/src/System.IO.FileSystem.Watcher/src/System.IO.FileSystem.Watcher.csproj b/external/corefx/src/System.IO.FileSystem.Watcher/src/System.IO.FileSystem.Watcher.csproj index 91f7d75d8f..734563e601 100644 --- a/external/corefx/src/System.IO.FileSystem.Watcher/src/System.IO.FileSystem.Watcher.csproj +++ b/external/corefx/src/System.IO.FileSystem.Watcher/src/System.IO.FileSystem.Watcher.csproj @@ -19,7 +19,9 @@ - + + Component + @@ -53,13 +55,15 @@ Common\Interop\Windows\kernel32\Interop.ReadDirectoryChangesW.cs - + + Component + + + Common\Interop\Windows\Interop.CloseHandle.cs + - - Common\Interop\Windows\Interop.UnsafeCreateFile.cs - Common\Interop\Windows\Interop.CreateFile.cs @@ -69,9 +73,6 @@ Common\Interop\Windows\Interop.CreateFile2.cs - - Common\Interop\Windows\Interop.UnsafeCreateFile.uap.cs - Common\Interop\Windows\Interop.COPYFILE2_EXTENDED_PARAMETERS.cs @@ -164,4 +165,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.Win32.cs b/external/corefx/src/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.Win32.cs index a0b8ae9d18..8ba4600cad 100644 --- a/external/corefx/src/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.Win32.cs +++ b/external/corefx/src/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.Win32.cs @@ -26,15 +26,13 @@ namespace System.IO return; // Create handle to directory being monitored - var defaultSecAttrs = default(Interop.Kernel32.SECURITY_ATTRIBUTES); - _directoryHandle = Interop.Kernel32.UnsafeCreateFile( + _directoryHandle = Interop.Kernel32.CreateFile( lpFileName: _directory, dwDesiredAccess: Interop.Kernel32.FileOperations.FILE_LIST_DIRECTORY, dwShareMode: FileShare.Read | FileShare.Delete | FileShare.Write, - securityAttrs: ref defaultSecAttrs, dwCreationDisposition: FileMode.Open, - dwFlagsAndAttributes: Interop.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS | Interop.Kernel32.FileOperations.FILE_FLAG_OVERLAPPED, - hTemplateFile: IntPtr.Zero); + dwFlagsAndAttributes: Interop.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS | Interop.Kernel32.FileOperations.FILE_FLAG_OVERLAPPED); + if (IsHandleInvalid(_directoryHandle)) { _directoryHandle = null; diff --git a/external/corefx/src/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.cs b/external/corefx/src/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.cs index 29a4fcb0b4..89c5adb413 100644 --- a/external/corefx/src/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.cs +++ b/external/corefx/src/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.cs @@ -105,9 +105,12 @@ namespace System.IO throw new ArgumentNullException(nameof(filter)); // Early check for directory parameter so that an exception can be thrown as early as possible. - if (path.Length == 0 || !Directory.Exists(path)) + if (path.Length == 0) throw new ArgumentException(SR.Format(SR.InvalidDirName, path), nameof(path)); + if (!Directory.Exists(path)) + throw new ArgumentException(SR.Format(SR.InvalidDirName_NotExists, path), nameof(path)); + _directory = path; _filter = filter; } @@ -150,13 +153,13 @@ namespace System.IO { return; } - + if (IsSuspended()) { _enabled = value; // Alert the Component to start watching for events when EndInit is called. } else - { + { if (value) { StartRaisingEventsIfNotDisposed(); // will set _enabled to true once successfully started @@ -256,7 +259,7 @@ namespace System.IO /// /// Gets or sets the path of the directory to watch. - /// + /// public string Path { get @@ -268,11 +271,12 @@ namespace System.IO value = (value == null) ? string.Empty : value; if (!string.Equals(_directory, value, PathInternal.StringComparison)) { - if (!Directory.Exists(value)) - { - throw new ArgumentException(SR.Format(SR.InvalidDirName, value)); - } + if (value.Length == 0) + throw new ArgumentException(SR.Format(SR.InvalidDirName, value), nameof(Path)); + if (!Directory.Exists(value)) + throw new ArgumentException(SR.Format(SR.InvalidDirName_NotExists, value), nameof(Path)); + _directory = value; Restart(); } @@ -416,7 +420,7 @@ namespace System.IO { // filter if there's no handler or neither new name or old name match a specified pattern RenamedEventHandler handler = _onRenamedHandler; - if (handler != null && + if (handler != null && (MatchPattern(name) || MatchPattern(oldName))) { handler(this, new RenamedEventArgs(action, _directory, name, oldName)); @@ -525,7 +529,7 @@ namespace System.IO } } - public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType) => + public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType) => WaitForChanged(changeType, Timeout.Infinite); public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout) diff --git a/external/corefx/src/System.IO.FileSystem.Watcher/src/System/IO/InternalBufferOverflowException.cs b/external/corefx/src/System.IO.FileSystem.Watcher/src/System/IO/InternalBufferOverflowException.cs index 3b7985070e..4afb3a9c38 100644 --- a/external/corefx/src/System.IO.FileSystem.Watcher/src/System/IO/InternalBufferOverflowException.cs +++ b/external/corefx/src/System.IO.FileSystem.Watcher/src/System/IO/InternalBufferOverflowException.cs @@ -10,6 +10,9 @@ namespace System.IO /// The exception that is thrown when the internal buffer overflows. /// [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class InternalBufferOverflowException : SystemException { /// @@ -40,7 +43,6 @@ namespace System.IO protected InternalBufferOverflowException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corefx/src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Create.cs b/external/corefx/src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Create.cs index a45290566c..5b43d8326f 100644 --- a/external/corefx/src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Create.cs +++ b/external/corefx/src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Create.cs @@ -9,6 +9,28 @@ namespace System.IO.Tests { public class Directory_Create_Tests : FileSystemWatcherTest { + [Fact] + public void FileSystemWatcher_Directory_EmptyPath() + { + Assert.Throws(() => + { + using (var watcher = new FileSystemWatcher("")) + { + } + }); + } + + [Fact] + public void FileSystemWatcher_Directory_PathNotExists() + { + Assert.Throws(() => + { + using (var watcher = new FileSystemWatcher(GetTestFilePath())) + { + } + }); + } + [Fact] public void FileSystemWatcher_Directory_Create() { @@ -81,4 +103,4 @@ namespace System.IO.Tests } } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.unit.cs b/external/corefx/src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.unit.cs index f9e7fa446d..1b7914b255 100644 --- a/external/corefx/src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.unit.cs +++ b/external/corefx/src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.unit.cs @@ -485,9 +485,9 @@ namespace System.IO.Tests } } - [Fact] + [Fact] public void FileSystemWatcher_Path() - { + { FileSystemWatcher watcher = new FileSystemWatcher(); Assert.Equal(String.Empty, watcher.Path); @@ -527,14 +527,14 @@ namespace System.IO.Tests Assert.Equal(currentDirRelative, watcher.Path); // FSW starts with String.Empty and will ignore setting this if it is already set, - // but if you set it after some other valid string has been set it will throw. - AssertExtensions.Throws(null, () => watcher.Path = String.Empty); + // but if you set it after some other valid string has been set it will throw. + Assert.Throws(() => watcher.Path = String.Empty); // Non-existent path - AssertExtensions.Throws(null, () => watcher.Path = GetTestFilePath()); + Assert.Throws(() => watcher.Path = GetTestFilePath()); // Web path - AssertExtensions.Throws(null, () => watcher.Path = "http://localhost"); + Assert.Throws(() => watcher.Path = "http://localhost"); // File protocol - AssertExtensions.Throws(null, () => watcher.Path = "file:///" + currentDir.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)); + Assert.Throws(() => watcher.Path = "file:///" + currentDir.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)); } [Fact] diff --git a/external/corefx/src/System.IO.FileSystem/src/Microsoft/Win32/SafeHandles/SafeFindHandle.Windows.cs b/external/corefx/src/System.IO.FileSystem/src/Microsoft/Win32/SafeHandles/SafeFindHandle.Windows.cs index b6c557b92f..c9c118c1b5 100644 --- a/external/corefx/src/System.IO.FileSystem/src/Microsoft/Win32/SafeHandles/SafeFindHandle.Windows.cs +++ b/external/corefx/src/System.IO.FileSystem/src/Microsoft/Win32/SafeHandles/SafeFindHandle.Windows.cs @@ -10,13 +10,10 @@ using Microsoft.Win32; namespace Microsoft.Win32.SafeHandles { - [System.Security.SecurityCritical] // auto-generated internal sealed class SafeFindHandle : SafeHandle { - [System.Security.SecurityCritical] // auto-generated_required internal SafeFindHandle() : base(IntPtr.Zero, true) { } - [System.Security.SecurityCritical] override protected bool ReleaseHandle() { return Interop.Kernel32.FindClose(handle); @@ -24,7 +21,6 @@ namespace Microsoft.Win32.SafeHandles public override bool IsInvalid { - [System.Security.SecurityCritical] get { return handle == IntPtr.Zero || handle == new IntPtr(-1); diff --git a/external/corefx/src/System.IO.FileSystem/src/Resources/Strings.resx b/external/corefx/src/System.IO.FileSystem/src/Resources/Strings.resx index ac5e515aeb..7be9129149 100644 --- a/external/corefx/src/System.IO.FileSystem/src/Resources/Strings.resx +++ b/external/corefx/src/System.IO.FileSystem/src/Resources/Strings.resx @@ -1,5 +1,64 @@  + @@ -122,7 +181,7 @@ Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection. - Illegal characters in path. + Illegal characters in path '{0}'. Invalid seek origin. @@ -199,9 +258,6 @@ Probable I/O race condition detected while copying memory. The I/O package is not thread safe by default. In multithreaded applications, a stream must be accessed in a thread-safe way, such as a thread-safe wrapper returned by TextReader's or TextWriter's Synchronized methods. This also applies to classes like StreamWriter and StreamReader. - - FileStream was asked to open a device that was not a file. For support for devices like 'com1:' or 'lpt1:', call CreateFile, then use the FileStream constructors that take an OS handle as an IntPtr. - Stream does not support reading. @@ -225,5 +281,8 @@ File encryption is not supported on this platform. - + + + The path '{0}' is too long, or a component of the specified path is too long. + diff --git a/external/corefx/src/System.IO.FileSystem/src/System.IO.FileSystem.csproj b/external/corefx/src/System.IO.FileSystem/src/System.IO.FileSystem.csproj index 9160591924..3bf4354198 100644 --- a/external/corefx/src/System.IO.FileSystem/src/System.IO.FileSystem.csproj +++ b/external/corefx/src/System.IO.FileSystem/src/System.IO.FileSystem.csproj @@ -18,14 +18,14 @@ + + - - @@ -44,6 +44,9 @@ Common\System\IO\StringBuilderCache.cs + + Common\System\Text\ValueStringBuilder.cs + Common\System\IO\PathInternal.cs @@ -59,14 +62,21 @@ + - + + + + + + - - + + + Common\Interop\Windows\Interop.Libraries.cs @@ -160,9 +170,6 @@ Common\Interop\Windows\Interop.MaxLengths.cs - - Common\Interop\Windows\Interop.SafeCreateFile.cs - Common\Interop\Windows\Interop.WIN32_FIND_DATA.cs @@ -181,7 +188,6 @@ Common\Interop\Windows\Interop.GET_FILEEX_INFO_LEVELS.cs - Common\Interop\Windows\Interop.SetThreadErrorMode.cs @@ -198,6 +204,9 @@ Common\Interop\Windows\Interop.DeleteFile.cs + + Common\Interop\Windows\Interop.CompareStringOrdinal.cs + Common\Interop\Windows\Interop.CreateDirectory.cs @@ -225,37 +234,66 @@ Common\Interop\Windows\Interop.CopyFileEx.cs + + Common\Interop\Windows\Interop.FILE_FULL_DIR_INFORMATION.cs + + + Common\Interop\Windows\Interop.LongFileTime.cs + + + Common\Interop\Windows\Interop.FILE_INFO_BY_HANDLE_CLASS.cs + - - Common\Interop\Windows\Interop.UnsafeCreateFile.cs - + Common\Interop\Windows\Interop.CreateFile.cs + + Common\Interop\Windows\Interop.UNICODE_STRING.cs + + + Common\Interop\Windows\Interop.BOOLEAN.cs + + + Common\Interop\Windows\Interop.IO_STATUS_BLOCK.cs + + + Common\Interop\Windows\Interop.NtQueryDirectoryFile.cs + + + Common\Interop\Windows\Interop.FILE_INFORMATION_CLASS.cs + + + Common\Interop\Windows\Interop.NtStatus.cs + + + Common\Interop\Windows\Interop.RtlNtStatusToDosError.cs + + Common\Interop\Windows\Interop.CreateFile2.cs - - Common\Interop\Windows\Interop.UnsafeCreateFile.uap.cs - Common\Interop\Windows\Interop.COPYFILE2_EXTENDED_PARAMETERS.cs Common\Interop\Windows\Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs + + Common\Interop\Windows\Interop.GetFileInformationByHandleEx.cs + - + - + Common\Interop\Unix\Interop.Libraries.cs @@ -365,7 +403,6 @@ - @@ -376,9 +413,10 @@ + - \ No newline at end of file + diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/CharSpanExtensions.Unix.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/CharSpanExtensions.Unix.cs new file mode 100644 index 0000000000..a1fae54777 --- /dev/null +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/CharSpanExtensions.Unix.cs @@ -0,0 +1,38 @@ +// 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.Runtime.InteropServices; + +namespace System.IO +{ + internal static partial class CharSpanExtensions + { + internal static unsafe bool EqualsOrdinal(ReadOnlySpan first, ReadOnlySpan second, bool ignoreCase = false) + { + if (first.Length != second.Length) + return false; + + if (!ignoreCase) + return first.SequenceEqual(second); + + fixed (char* fp = &MemoryMarshal.GetReference(first)) + fixed (char* sp = &MemoryMarshal.GetReference(second)) + { + char* f = fp; + char* s = sp; + + for (int i = 0; i < first.Length; i++) + { + if (*f != *s && char.ToUpperInvariant(*f) != char.ToUpperInvariant(*s)) + return false; + f++; + s++; + } + } + + return true; + } + } +} diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/CharSpanExtensions.Windows.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/CharSpanExtensions.Windows.cs new file mode 100644 index 0000000000..0503635038 --- /dev/null +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/CharSpanExtensions.Windows.cs @@ -0,0 +1,44 @@ +// 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.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.IO +{ + internal static partial class CharSpanExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe int CompareOrdinal(ReadOnlySpan first, ReadOnlySpan second, bool ignoreCase = false) + { + int result = Interop.Kernel32.CompareStringOrdinal( + ref MemoryMarshal.GetReference(first), + first.Length, + ref MemoryMarshal.GetReference(second), + second.Length, + ignoreCase); + + if (result == 0) + throw Win32Marshal.GetExceptionForWin32Error(Marshal.GetLastWin32Error()); + + // CSTR_LESS_THAN 1 // string 1 less than string 2 + // CSTR_EQUAL 2 // string 1 equal to string 2 + // CSTR_GREATER_THAN 3 // string 1 greater than string 2 + + return result - 2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool EqualsOrdinal(ReadOnlySpan first, ReadOnlySpan second, bool ignoreCase = false) + { + if (first.Length != second.Length) + return false; + + if (!ignoreCase) + return first.SequenceEqual(second); + + return CompareOrdinal(first, second, ignoreCase) == 0; + } + } +} diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/CharSpanExtensions.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/CharSpanExtensions.cs new file mode 100644 index 0000000000..80c6ba4160 --- /dev/null +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/CharSpanExtensions.cs @@ -0,0 +1,24 @@ +// 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. + +namespace System.IO +{ + internal static partial class CharSpanExtensions + { + public static bool EndsWithOrdinal(this ReadOnlySpan span, ReadOnlySpan value, bool ignoreCase = false) + { + if (value.Length == 0) + return true; + else if (value.Length > span.Length) + return false; + + span = span.Slice(span.Length - value.Length); + + if (ignoreCase == false) + return span.SequenceEqual(value); + + return EqualsOrdinal(span, value, ignoreCase); + } + } +} diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/Directory.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/Directory.cs index 9c5095540f..5f4b32550e 100644 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/Directory.cs +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/Directory.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Security; namespace System.IO @@ -18,7 +17,6 @@ namespace System.IO if (path.Length == 0) throw new ArgumentException(SR.Argument_PathEmpty, nameof(path)); - Contract.EndContractBlock(); string fullPath = Path.GetFullPath(path); @@ -28,18 +26,16 @@ namespace System.IO return new DirectoryInfo(s); } - [System.Security.SecuritySafeCritical] public static DirectoryInfo CreateDirectory(string path) { if (path == null) throw new ArgumentNullException(nameof(path)); if (path.Length == 0) throw new ArgumentException(SR.Argument_PathEmpty, nameof(path)); - Contract.EndContractBlock(); string fullPath = Path.GetFullPath(path); - FileSystem.Current.CreateDirectory(fullPath); + FileSystem.CreateDirectory(fullPath); return new DirectoryInfo(fullPath, null); } @@ -64,7 +60,6 @@ namespace System.IO // Your application must have Read permission to the directory's // contents. // - [System.Security.SecuritySafeCritical] // auto-generated public static bool Exists(string path) { try @@ -76,7 +71,7 @@ namespace System.IO string fullPath = Path.GetFullPath(path); - return FileSystem.Current.DirectoryExists(fullPath); + return FileSystem.DirectoryExists(fullPath); } catch (ArgumentException) { } catch (NotSupportedException) { } // Security can throw this on ":" @@ -90,13 +85,13 @@ namespace System.IO public static void SetCreationTime(string path, DateTime creationTime) { string fullPath = Path.GetFullPath(path); - FileSystem.Current.SetCreationTime(fullPath, creationTime, asDirectory: true); + FileSystem.SetCreationTime(fullPath, creationTime, asDirectory: true); } public static void SetCreationTimeUtc(string path, DateTime creationTimeUtc) { string fullPath = Path.GetFullPath(path); - FileSystem.Current.SetCreationTime(fullPath, File.GetUtcDateTimeOffset(creationTimeUtc), asDirectory: true); + FileSystem.SetCreationTime(fullPath, File.GetUtcDateTimeOffset(creationTimeUtc), asDirectory: true); } public static DateTime GetCreationTime(string path) @@ -112,13 +107,13 @@ namespace System.IO public static void SetLastWriteTime(string path, DateTime lastWriteTime) { string fullPath = Path.GetFullPath(path); - FileSystem.Current.SetLastWriteTime(fullPath, lastWriteTime, asDirectory: true); + FileSystem.SetLastWriteTime(fullPath, lastWriteTime, asDirectory: true); } public static void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) { string fullPath = Path.GetFullPath(path); - FileSystem.Current.SetLastWriteTime(fullPath, File.GetUtcDateTimeOffset(lastWriteTimeUtc), asDirectory: true); + FileSystem.SetLastWriteTime(fullPath, File.GetUtcDateTimeOffset(lastWriteTimeUtc), asDirectory: true); } public static DateTime GetLastWriteTime(string path) @@ -134,13 +129,13 @@ namespace System.IO public static void SetLastAccessTime(string path, DateTime lastAccessTime) { string fullPath = Path.GetFullPath(path); - FileSystem.Current.SetLastAccessTime(fullPath, lastAccessTime, asDirectory: true); + FileSystem.SetLastAccessTime(fullPath, lastAccessTime, asDirectory: true); } public static void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) { string fullPath = Path.GetFullPath(path); - FileSystem.Current.SetLastAccessTime(fullPath, File.GetUtcDateTimeOffset(lastAccessTimeUtc), asDirectory: true); + FileSystem.SetLastAccessTime(fullPath, File.GetUtcDateTimeOffset(lastAccessTimeUtc), asDirectory: true); } public static DateTime GetLastAccessTime(string path) @@ -158,8 +153,6 @@ namespace System.IO { if (path == null) throw new ArgumentNullException(nameof(path)); - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); return InternalGetFiles(path, "*", SearchOption.TopDirectoryOnly); } @@ -172,8 +165,6 @@ namespace System.IO throw new ArgumentNullException(nameof(path)); if (searchPattern == null) throw new ArgumentNullException(nameof(searchPattern)); - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); return InternalGetFiles(path, searchPattern, SearchOption.TopDirectoryOnly); } @@ -188,8 +179,6 @@ namespace System.IO throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum); - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); return InternalGetFiles(path, searchPattern, searchOption); } @@ -210,8 +199,6 @@ namespace System.IO { if (path == null) throw new ArgumentNullException(nameof(path)); - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); return InternalGetDirectories(path, "*", SearchOption.TopDirectoryOnly); } @@ -224,8 +211,6 @@ namespace System.IO throw new ArgumentNullException(nameof(path)); if (searchPattern == null) throw new ArgumentNullException(nameof(searchPattern)); - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); return InternalGetDirectories(path, searchPattern, SearchOption.TopDirectoryOnly); } @@ -240,8 +225,6 @@ namespace System.IO throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum); - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); return InternalGetDirectories(path, searchPattern, searchOption); } @@ -253,7 +236,6 @@ namespace System.IO Debug.Assert(path != null); Debug.Assert(searchPattern != null); Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); - Contract.Ensures(Contract.Result() != null); return InternalGetFileDirectoryNames(path, path, searchPattern, false, true, searchOption); } @@ -263,8 +245,6 @@ namespace System.IO { if (path == null) throw new ArgumentNullException(nameof(path)); - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); return InternalGetFileSystemEntries(path, "*", SearchOption.TopDirectoryOnly); } @@ -277,8 +257,6 @@ namespace System.IO throw new ArgumentNullException(nameof(path)); if (searchPattern == null) throw new ArgumentNullException(nameof(searchPattern)); - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); return InternalGetFileSystemEntries(path, searchPattern, SearchOption.TopDirectoryOnly); } @@ -293,8 +271,6 @@ namespace System.IO throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum); - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); return InternalGetFileSystemEntries(path, searchPattern, searchOption); } @@ -320,7 +296,7 @@ namespace System.IO Debug.Assert(searchPattern != null); Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); - IEnumerable enumerable = FileSystem.Current.EnumeratePaths(path, searchPattern, searchOption, + IEnumerable enumerable = FileSystem.EnumeratePaths(path, searchPattern, searchOption, (includeFiles ? SearchTarget.Files : 0) | (includeDirs ? SearchTarget.Directories : 0)); return EnumerableHelpers.ToArray(enumerable); } @@ -329,7 +305,6 @@ namespace System.IO { if (path == null) throw new ArgumentNullException(nameof(path)); - Contract.EndContractBlock(); return InternalEnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly); } @@ -340,7 +315,6 @@ namespace System.IO throw new ArgumentNullException(nameof(path)); if (searchPattern == null) throw new ArgumentNullException(nameof(searchPattern)); - Contract.EndContractBlock(); return InternalEnumerateDirectories(path, searchPattern, SearchOption.TopDirectoryOnly); } @@ -353,7 +327,6 @@ namespace System.IO throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum); - Contract.EndContractBlock(); return InternalEnumerateDirectories(path, searchPattern, searchOption); } @@ -371,8 +344,6 @@ namespace System.IO { if (path == null) throw new ArgumentNullException(nameof(path)); - Contract.Ensures(Contract.Result>() != null); - Contract.EndContractBlock(); return InternalEnumerateFiles(path, "*", SearchOption.TopDirectoryOnly); } @@ -383,8 +354,6 @@ namespace System.IO throw new ArgumentNullException(nameof(path)); if (searchPattern == null) throw new ArgumentNullException(nameof(searchPattern)); - Contract.Ensures(Contract.Result>() != null); - Contract.EndContractBlock(); return InternalEnumerateFiles(path, searchPattern, SearchOption.TopDirectoryOnly); } @@ -397,8 +366,6 @@ namespace System.IO throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum); - Contract.Ensures(Contract.Result>() != null); - Contract.EndContractBlock(); return InternalEnumerateFiles(path, searchPattern, searchOption); } @@ -408,7 +375,6 @@ namespace System.IO Debug.Assert(path != null); Debug.Assert(searchPattern != null); Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); - Contract.Ensures(Contract.Result>() != null); return EnumerateFileSystemNames(path, searchPattern, searchOption, true, false); } @@ -417,8 +383,6 @@ namespace System.IO { if (path == null) throw new ArgumentNullException(nameof(path)); - Contract.Ensures(Contract.Result>() != null); - Contract.EndContractBlock(); return InternalEnumerateFileSystemEntries(path, "*", SearchOption.TopDirectoryOnly); } @@ -429,8 +393,6 @@ namespace System.IO throw new ArgumentNullException(nameof(path)); if (searchPattern == null) throw new ArgumentNullException(nameof(searchPattern)); - Contract.Ensures(Contract.Result>() != null); - Contract.EndContractBlock(); return InternalEnumerateFileSystemEntries(path, searchPattern, SearchOption.TopDirectoryOnly); } @@ -443,8 +405,6 @@ namespace System.IO throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum); - Contract.Ensures(Contract.Result>() != null); - Contract.EndContractBlock(); return InternalEnumerateFileSystemEntries(path, searchPattern, searchOption); } @@ -454,7 +414,6 @@ namespace System.IO Debug.Assert(path != null); Debug.Assert(searchPattern != null); Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); - Contract.Ensures(Contract.Result>() != null); return EnumerateFileSystemNames(path, searchPattern, searchOption, true, true); } @@ -465,18 +424,15 @@ namespace System.IO Debug.Assert(path != null); Debug.Assert(searchPattern != null); Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); - Contract.Ensures(Contract.Result>() != null); - return FileSystem.Current.EnumeratePaths(path, searchPattern, searchOption, + return FileSystem.EnumeratePaths(path, searchPattern, searchOption, (includeFiles ? SearchTarget.Files : 0) | (includeDirs ? SearchTarget.Directories : 0)); } - [System.Security.SecuritySafeCritical] public static string GetDirectoryRoot(string path) { if (path == null) throw new ArgumentNullException(nameof(path)); - Contract.EndContractBlock(); string fullPath = Path.GetFullPath(path); string root = fullPath.Substring(0, PathInternal.GetRootLength(fullPath)); @@ -497,28 +453,23 @@ namespace System.IO **Arguments: The current DirectoryInfo to which to switch to the setter. **Exceptions: ==============================================================================*/ - [System.Security.SecuritySafeCritical] public static string GetCurrentDirectory() { - return FileSystem.Current.GetCurrentDirectory(); + return FileSystem.GetCurrentDirectory(); } - - [System.Security.SecurityCritical] // auto-generated public static void SetCurrentDirectory(string path) { if (path == null) throw new ArgumentNullException(nameof(path)); if (path.Length == 0) throw new ArgumentException(SR.Argument_PathEmpty, nameof(path)); - Contract.EndContractBlock(); string fulldestDirName = Path.GetFullPath(path); - FileSystem.Current.SetCurrentDirectory(fulldestDirName); + FileSystem.SetCurrentDirectory(fulldestDirName); } - [System.Security.SecuritySafeCritical] public static void Move(string sourceDirName, string destDirName) { if (sourceDirName == null) @@ -530,7 +481,6 @@ namespace System.IO throw new ArgumentNullException(nameof(destDirName)); if (destDirName.Length == 0) throw new ArgumentException(SR.Argument_EmptyFileName, nameof(destDirName)); - Contract.EndContractBlock(); string fullsourceDirName = Path.GetFullPath(sourceDirName); string sourcePath = EnsureTrailingDirectorySeparator(fullsourceDirName); @@ -550,32 +500,30 @@ namespace System.IO // Windows will throw if the source file/directory doesn't exist, we preemptively check // to make sure our cross platform behavior matches NetFX behavior. - if (!FileSystem.Current.DirectoryExists(fullsourceDirName) && !FileSystem.Current.FileExists(fullsourceDirName)) + if (!FileSystem.DirectoryExists(fullsourceDirName) && !FileSystem.FileExists(fullsourceDirName)) throw new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, fullsourceDirName)); - if (FileSystem.Current.DirectoryExists(fulldestDirName)) + if (FileSystem.DirectoryExists(fulldestDirName)) throw new IOException(SR.Format(SR.IO_AlreadyExists_Name, fulldestDirName)); - FileSystem.Current.MoveDirectory(fullsourceDirName, fulldestDirName); + FileSystem.MoveDirectory(fullsourceDirName, fulldestDirName); } - [System.Security.SecuritySafeCritical] public static void Delete(string path) { string fullPath = Path.GetFullPath(path); - FileSystem.Current.RemoveDirectory(fullPath, false); + FileSystem.RemoveDirectory(fullPath, false); } - [System.Security.SecuritySafeCritical] public static void Delete(string path, bool recursive) { string fullPath = Path.GetFullPath(path); - FileSystem.Current.RemoveDirectory(fullPath, recursive); + FileSystem.RemoveDirectory(fullPath, recursive); } public static string[] GetLogicalDrives() { - return FileSystem.Current.GetLogicalDrives(); + return FileSystem.GetLogicalDrives(); } } } diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/DirectoryInfo.Windows.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/DirectoryInfo.Windows.cs index 4f14a8f050..1c1ebd0197 100644 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/DirectoryInfo.Windows.cs +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/DirectoryInfo.Windows.cs @@ -3,18 +3,16 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Security; namespace System.IO { partial class DirectoryInfo { - [SecurityCritical] - internal DirectoryInfo(string fullPath, ref Interop.Kernel32.WIN32_FIND_DATA findData) - : this(fullPath, findData.cFileName.GetStringFromFixedBuffer()) + internal unsafe DirectoryInfo(string fullPath, string fileName, ref RawFindData findData) + : this(fullPath, fileName: fileName, isNormalized: true) { - Debug.Assert(findData.cFileName.FixedBufferEqualsString(Path.GetFileName(fullPath))); - Init(ref findData); + Debug.Assert(fileName.Equals(Path.GetFileName(fullPath))); + Init(findData._info); } } } diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/DirectoryInfo.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/DirectoryInfo.cs index ff23b11046..006bbc115b 100644 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/DirectoryInfo.cs +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/DirectoryInfo.cs @@ -4,48 +4,47 @@ using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.Contracts; -using System.Security; namespace System.IO { [Serializable] public sealed partial class DirectoryInfo : FileSystemInfo { - [System.Security.SecuritySafeCritical] + private string _name; + public DirectoryInfo(string path) { - if (path == null) - throw new ArgumentNullException(nameof(path)); - Contract.EndContractBlock(); - - OriginalPath = PathHelpers.ShouldReviseDirectoryPathToCurrent(path) ? "." : path; - FullPath = Path.GetFullPath(path); - DisplayPath = GetDisplayName(OriginalPath); + Init(originalPath: PathHelpers.ShouldReviseDirectoryPathToCurrent(path) ? "." : path, + fullPath: Path.GetFullPath(path), + isNormalized: true); } - [System.Security.SecuritySafeCritical] - internal DirectoryInfo(string fullPath, string originalPath) + internal DirectoryInfo(string originalPath, string fullPath = null, string fileName = null, bool isNormalized = false) { - Debug.Assert(Path.IsPathRooted(fullPath), "fullPath must be fully qualified!"); + Init(originalPath, fullPath, fileName, isNormalized); + } + + private void Init(string originalPath, string fullPath = null, string fileName = null, bool isNormalized = false) + { + // Want to throw the original argument name + OriginalPath = originalPath ?? throw new ArgumentNullException("path"); + + fullPath = fullPath ?? originalPath; + Debug.Assert(!isNormalized || !PathInternal.IsPartiallyQualified(fullPath), "should be fully qualified if normalized"); + fullPath = isNormalized ? fullPath : Path.GetFullPath(fullPath); + + _name = fileName ?? (PathHelpers.IsRoot(fullPath) ? + fullPath : + Path.GetFileName(PathHelpers.TrimEndingDirectorySeparator(fullPath))); - // Fast path when we know a DirectoryInfo exists. - OriginalPath = originalPath ?? Path.GetFileName(fullPath); FullPath = fullPath; - DisplayPath = GetDisplayName(OriginalPath); + DisplayPath = PathHelpers.ShouldReviseDirectoryPathToCurrent(originalPath) ? "." : originalPath; } - public override string Name - { - get - { - return GetDirName(FullPath); - } - } + public override string Name => _name; public DirectoryInfo Parent { - [System.Security.SecuritySafeCritical] get { string s = FullPath; @@ -66,17 +65,14 @@ namespace System.IO } - [System.Security.SecuritySafeCritical] public DirectoryInfo CreateSubdirectory(string path) { if (path == null) throw new ArgumentNullException(nameof(path)); - Contract.EndContractBlock(); return CreateSubdirectoryHelper(path); } - [System.Security.SecurityCritical] // auto-generated private DirectoryInfo CreateSubdirectoryHelper(string path) { Debug.Assert(path != null); @@ -91,16 +87,15 @@ namespace System.IO throw new ArgumentException(SR.Format(SR.Argument_InvalidSubPath, path, DisplayPath), nameof(path)); } - FileSystem.Current.CreateDirectory(fullPath); + FileSystem.CreateDirectory(fullPath); // Check for read permission to directory we hand back by calling this constructor. return new DirectoryInfo(fullPath); } - [System.Security.SecurityCritical] public void Create() { - FileSystem.Current.CreateDirectory(FullPath); + FileSystem.CreateDirectory(FullPath); } // Tests if the given path refers to an existing DirectoryInfo on disk. @@ -110,12 +105,11 @@ namespace System.IO // public override bool Exists { - [System.Security.SecuritySafeCritical] // auto-generated get { try { - return FileSystemObject.Exists; + return ExistsCore; } catch { @@ -126,12 +120,10 @@ namespace System.IO // Returns an array of Files in the current DirectoryInfo matching the // given search criteria (i.e. "*.txt"). - [SecurityCritical] public FileInfo[] GetFiles(string searchPattern) { if (searchPattern == null) throw new ArgumentNullException(nameof(searchPattern)); - Contract.EndContractBlock(); return InternalGetFiles(searchPattern, SearchOption.TopDirectoryOnly); } @@ -144,7 +136,6 @@ namespace System.IO throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum); - Contract.EndContractBlock(); return InternalGetFiles(searchPattern, searchOption); } @@ -156,7 +147,7 @@ namespace System.IO Debug.Assert(searchPattern != null); Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); - IEnumerable enumerable = (IEnumerable)FileSystem.Current.EnumerateFileSystemInfos(FullPath, searchPattern, searchOption, SearchTarget.Files); + IEnumerable enumerable = (IEnumerable)FileSystem.EnumerateFileSystemInfos(FullPath, searchPattern, searchOption, SearchTarget.Files); return EnumerableHelpers.ToArray(enumerable); } @@ -178,7 +169,6 @@ namespace System.IO { if (searchPattern == null) throw new ArgumentNullException(nameof(searchPattern)); - Contract.EndContractBlock(); return InternalGetFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly); } @@ -191,7 +181,6 @@ namespace System.IO throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum); - Contract.EndContractBlock(); return InternalGetFileSystemInfos(searchPattern, searchOption); } @@ -203,7 +192,7 @@ namespace System.IO Debug.Assert(searchPattern != null); Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); - IEnumerable enumerable = FileSystem.Current.EnumerateFileSystemInfos(FullPath, searchPattern, searchOption, SearchTarget.Both); + IEnumerable enumerable = FileSystem.EnumerateFileSystemInfos(FullPath, searchPattern, searchOption, SearchTarget.Both); return EnumerableHelpers.ToArray(enumerable); } @@ -221,7 +210,6 @@ namespace System.IO { if (searchPattern == null) throw new ArgumentNullException(nameof(searchPattern)); - Contract.EndContractBlock(); return InternalGetDirectories(searchPattern, SearchOption.TopDirectoryOnly); } @@ -235,7 +223,6 @@ namespace System.IO throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum); - Contract.EndContractBlock(); return InternalGetDirectories(searchPattern, searchOption); } @@ -248,7 +235,7 @@ namespace System.IO Debug.Assert(searchPattern != null); Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); - IEnumerable enumerable = (IEnumerable)FileSystem.Current.EnumerateFileSystemInfos(FullPath, searchPattern, searchOption, SearchTarget.Directories); + IEnumerable enumerable = (IEnumerable)FileSystem.EnumerateFileSystemInfos(FullPath, searchPattern, searchOption, SearchTarget.Directories); return EnumerableHelpers.ToArray(enumerable); } @@ -261,7 +248,6 @@ namespace System.IO { if (searchPattern == null) throw new ArgumentNullException(nameof(searchPattern)); - Contract.EndContractBlock(); return InternalEnumerateDirectories(searchPattern, SearchOption.TopDirectoryOnly); } @@ -272,7 +258,6 @@ namespace System.IO throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum); - Contract.EndContractBlock(); return InternalEnumerateDirectories(searchPattern, searchOption); } @@ -282,7 +267,7 @@ namespace System.IO Debug.Assert(searchPattern != null); Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); - return (IEnumerable)FileSystem.Current.EnumerateFileSystemInfos(FullPath, searchPattern, searchOption, SearchTarget.Directories); + return (IEnumerable)FileSystem.EnumerateFileSystemInfos(FullPath, searchPattern, searchOption, SearchTarget.Directories); } public IEnumerable EnumerateFiles() @@ -294,7 +279,6 @@ namespace System.IO { if (searchPattern == null) throw new ArgumentNullException(nameof(searchPattern)); - Contract.EndContractBlock(); return InternalEnumerateFiles(searchPattern, SearchOption.TopDirectoryOnly); } @@ -305,7 +289,6 @@ namespace System.IO throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum); - Contract.EndContractBlock(); return InternalEnumerateFiles(searchPattern, searchOption); } @@ -315,7 +298,7 @@ namespace System.IO Debug.Assert(searchPattern != null); Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); - return (IEnumerable)FileSystem.Current.EnumerateFileSystemInfos(FullPath, searchPattern, searchOption, SearchTarget.Files); + return (IEnumerable)FileSystem.EnumerateFileSystemInfos(FullPath, searchPattern, searchOption, SearchTarget.Files); } public IEnumerable EnumerateFileSystemInfos() @@ -327,7 +310,6 @@ namespace System.IO { if (searchPattern == null) throw new ArgumentNullException(nameof(searchPattern)); - Contract.EndContractBlock(); return InternalEnumerateFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly); } @@ -338,7 +320,6 @@ namespace System.IO throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum); - Contract.EndContractBlock(); return InternalEnumerateFileSystemInfos(searchPattern, searchOption); } @@ -348,7 +329,7 @@ namespace System.IO Debug.Assert(searchPattern != null); Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); - return FileSystem.Current.EnumerateFileSystemInfos(FullPath, searchPattern, searchOption, SearchTarget.Both); + return FileSystem.EnumerateFileSystemInfos(FullPath, searchPattern, searchOption, SearchTarget.Both); } // Returns the root portion of the given path. The resulting string @@ -363,7 +344,6 @@ namespace System.IO public DirectoryInfo Root { - [System.Security.SecuritySafeCritical] get { string rootPath = Path.GetPathRoot(FullPath); @@ -372,14 +352,12 @@ namespace System.IO } } - [System.Security.SecuritySafeCritical] public void MoveTo(string destDirName) { if (destDirName == null) throw new ArgumentNullException(nameof(destDirName)); if (destDirName.Length == 0) throw new ArgumentException(SR.Argument_EmptyFileName, nameof(destDirName)); - Contract.EndContractBlock(); string destination = Path.GetFullPath(destDirName); string destinationWithSeparator = destination; @@ -404,32 +382,31 @@ namespace System.IO // Windows will throw if the source file/directory doesn't exist, we preemptively check // to make sure our cross platform behavior matches NetFX behavior. - if (!Exists && !FileSystem.Current.FileExists(FullPath)) + if (!Exists && !FileSystem.FileExists(FullPath)) throw new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, FullPath)); - if (FileSystem.Current.DirectoryExists(destinationWithSeparator)) + if (FileSystem.DirectoryExists(destinationWithSeparator)) throw new IOException(SR.Format(SR.IO_AlreadyExists_Name, destinationWithSeparator)); - FileSystem.Current.MoveDirectory(FullPath, destination); + FileSystem.MoveDirectory(FullPath, destination); - FullPath = destinationWithSeparator; - OriginalPath = destDirName; - DisplayPath = GetDisplayName(OriginalPath); + Init(originalPath: destDirName, + fullPath: destinationWithSeparator, + fileName: _name, + isNormalized: true); // Flush any cached information about the directory. Invalidate(); } - [System.Security.SecuritySafeCritical] public override void Delete() { - FileSystem.Current.RemoveDirectory(FullPath, false); + FileSystem.RemoveDirectory(FullPath, false); } - [System.Security.SecuritySafeCritical] public void Delete(bool recursive) { - FileSystem.Current.RemoveDirectory(FullPath, recursive); + FileSystem.RemoveDirectory(FullPath, recursive); } /// @@ -439,25 +416,5 @@ namespace System.IO { return DisplayPath; } - - private static string GetDisplayName(string originalPath) - { - Debug.Assert(originalPath != null); - - // Desktop documents that the path returned by ToString() should be the original path. - // For SL/Phone we only gave the directory name regardless of what was passed in. - return PathHelpers.ShouldReviseDirectoryPathToCurrent(originalPath) ? - "." : - originalPath; - } - - private static string GetDirName(string fullPath) - { - Debug.Assert(fullPath != null); - - return PathHelpers.IsRoot(fullPath) ? - fullPath : - Path.GetFileName(PathHelpers.TrimEndingDirectorySeparator(fullPath)); - } } } diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/DosMatcher.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/DosMatcher.cs new file mode 100644 index 0000000000..b1f677efb6 --- /dev/null +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/DosMatcher.cs @@ -0,0 +1,315 @@ +// 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.Text; + +namespace System.IO +{ + internal static class DosMatcher + { + // [MS - FSA] 2.1.4.4 Algorithm for Determining if a FileName Is in an Expression + // https://msdn.microsoft.com/en-us/library/ff469270.aspx + private static readonly char[] s_wildcardChars = + { + '\"', '<', '>', '*', '?' + }; + + /// + /// Change '*' and '?' to '<', '>' and '"' to match Win32 behavior. For compatibility, Windows + /// changes some wildcards to provide a closer match to historical DOS 8.3 filename matching. + /// + internal static string TranslateExpression(string expression) + { + if (string.IsNullOrEmpty(expression) || expression == "*" || expression == "*.*") + return "*"; + + bool modified = false; + Span stackSpace = stackalloc char[expression.Length]; + ValueStringBuilder sb = new ValueStringBuilder(stackSpace); + int length = expression.Length; + for (int i = 0; i < length; i++) + { + char c = expression[i]; + switch (c) + { + case '.': + modified = true; + if (i > 1 && i == length - 1 && expression[i - 1] == '*') + { + sb.Length--; + sb.Append('<'); // DOS_STAR (ends in *.) + } + else if (i < length - 1 && (expression[i + 1] == '?' || expression[i + 1] == '*')) + { + sb.Append('\"'); // DOS_DOT + } + else + { + sb.Append('.'); + } + break; + case '?': + modified = true; + sb.Append('>'); // DOS_QM + break; + default: + sb.Append(c); + break; + } + } + + return modified ? sb.ToString() : expression; + } + + /// + /// Return true if the given expression matches the given name. + /// + /// The expression to match with, such as "*.foo". + /// The name to check against the expression. + /// True to ignore case (default). + /// + /// This is based off of System.IO.PatternMatcher used in FileSystemWatcher, which is based off + /// of RtlIsNameInExpression, which defines the rules for matching DOS wildcards ('*', '?', '<', '>', '"'). + /// + /// Like PatternMatcher, matching will not line up with Win32 behavior unless you transform the expression + /// using + /// + internal static bool MatchPattern(string expression, ReadOnlySpan name, bool ignoreCase = true) + { + // The idea behind the algorithm is pretty simple. We keep track of all possible locations + // in the regular expression that are matching the name. When the name has been exhausted, + // if one of the locations in the expression is also just exhausted, the name is in the + // language defined by the regular expression. + + if (string.IsNullOrEmpty(expression) || name.Length == 0) + return false; + + if (expression[0] == '*') + { + // Just * matches everything + if (expression.Length == 1) + return true; + + if (expression.IndexOfAny(s_wildcardChars, startIndex: 1) == -1) + { + // Handle the special case of a single starting *, which essentially means "ends with" + + // If the name doesn't have enough characters to match the remaining expression, it can't be a match. + if (name.Length < expression.Length - 1) + return false; + + // See if we end with the expression (minus the *, of course) + return name.EndsWithOrdinal(expression.AsReadOnlySpan().Slice(1), ignoreCase); + } + } + + int nameOffset = 0; + int expressionOffset; + + int priorMatch; + int currentMatch; + int priorMatchCount; + int matchCount = 1; + + char nameChar = '\0'; + char expressionChar; + + Span temp = stackalloc int[0]; + Span priorMatches = stackalloc int[16]; + Span currentMatches = stackalloc int[16]; + + int maxState = expression.Length * 2; + int currentState; + bool nameFinished = false; + + while (!nameFinished) + { + if (nameOffset < name.Length) + { + // Not at the end of the name. Grab the current character and move the offset forward. + nameChar = name[nameOffset++]; + } + else + { + // At the end of the name. If the expression is exhausted, exit. + if (priorMatches[matchCount - 1] == maxState) + break; + + nameFinished = true; + } + + // Now, for each of the previous stored expression matches, see what + // we can do with this name character. + priorMatch = 0; + currentMatch = 0; + priorMatchCount = 0; + + while (priorMatch < matchCount) + { + // We have to carry on our expression analysis as far as possible for each + // character of name, so we loop here until the expression stops matching. + + expressionOffset = (priorMatches[priorMatch++] + 1) / 2; + + while (expressionOffset < expression.Length) + { + currentState = expressionOffset * 2; + expressionChar = expression[expressionOffset]; + + // We may be about to exhaust the local space for matches, + // so we have to reallocate if this is the case. + if (currentMatch >= currentMatches.Length - 2) + { + int newSize = currentMatches.Length * 2; + temp = new int[newSize]; + currentMatches.CopyTo(temp); + currentMatches = temp; + + temp = new int[newSize]; + priorMatches.CopyTo(temp); + priorMatches = temp; + } + + if (expressionChar == '*') + { + // '*' matches any character zero or more times. + goto MatchZeroOrMore; + } + else if (expressionChar == '<') + { + // '<' (DOS_STAR) matches any character except '.' zero or more times. + + // If we are at a period, determine if we are allowed to + // consume it, i.e. make sure it is not the last one. + + bool notLastPeriod = false; + if (!nameFinished && nameChar == '.') + { + for (int offset = nameOffset; offset < name.Length; offset++) + { + if (name[offset] == '.') + { + notLastPeriod = true; + break; + } + } + } + + if (nameFinished || nameChar != '.' || notLastPeriod) + { + goto MatchZeroOrMore; + } + else + { + // We are at a period. We can only match zero + // characters (i.e. the epsilon transition). + goto MatchZero; + } + } + else + { + // The following expression characters all match by consuming + // a character, thus force the expression, and thus state forward. + currentState += 2; + + if (expressionChar == '>') + { + // '>' (DOS_QM) is the most complicated. If the name is finished, + // we can match zero characters. If this name is a '.', we + // don't match, but look at the next expression. Otherwise + // we match a single character. + if (nameFinished || nameChar == '.') + goto NextExpressionCharacter; + + currentMatches[currentMatch++] = currentState; + goto ExpressionFinished; + } + else if (expressionChar == '"') + { + // A '"' (DOS_DOT) can match either a period, or zero characters + // beyond the end of name. + if (nameFinished) + { + goto NextExpressionCharacter; + } + else if (nameChar == '.') + { + currentMatches[currentMatch++] = currentState; + } + goto ExpressionFinished; + } + else + { + // From this point on a name character is required to even + // continue, let alone make a match. + if (nameFinished) goto ExpressionFinished; + + if (expressionChar == '?') + { + // If this expression was a '?' we can match it once. + currentMatches[currentMatch++] = currentState; + } + else if (ignoreCase + ? char.ToUpperInvariant(expressionChar) == char.ToUpperInvariant(nameChar) + : expressionChar == nameChar) + { + // Matched a non-wildcard character + currentMatches[currentMatch++] = currentState; + } + + // The expression didn't match so move to the next prior match. + goto ExpressionFinished; + } + } + + MatchZeroOrMore: + currentMatches[currentMatch++] = currentState; + MatchZero: + currentMatches[currentMatch++] = currentState + 1; + NextExpressionCharacter: + if (++expressionOffset == expression.Length) + currentMatches[currentMatch++] = maxState; + } // while (expressionOffset < expression.Length) + + ExpressionFinished: + + // Prevent duplication in the destination array. + // + // Each of the arrays is monotonically increasing and non-duplicating, thus we skip + // over any source element in the source array if we just added the same element to + // the destination array. This guarantees non-duplication in the destination array. + + if ((priorMatch < matchCount) && (priorMatchCount < currentMatch)) + { + while (priorMatchCount < currentMatch) + { + int previousLength = priorMatches.Length; + while ((priorMatch < previousLength) && (priorMatches[priorMatch] < currentMatches[priorMatchCount])) + { + priorMatch++; + } + priorMatchCount++; + } + } + } // while (sourceCount < matchesCount) + + // If we found no matches in the just finished iteration it's time to bail. + if (currentMatch == 0) + return false; + + // Swap the meaning the two arrays + temp = priorMatches; + priorMatches = currentMatches; + currentMatches = temp; + + matchCount = currentMatch; + } // while (!nameFinished) + + currentState = priorMatches[matchCount - 1]; + + return currentState == maxState; + } + } +} diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/Error.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/Error.cs index 5be1939ece..2cbb51d40a 100644 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/Error.cs +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/Error.cs @@ -2,14 +2,11 @@ // 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.Diagnostics.Contracts; - namespace System.IO { /// /// Provides centralized methods for creating exceptions for System.IO.FileSystem. /// - [Pure] internal static class Error { internal static Exception GetEndOfFile() diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/File.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/File.cs index af43f0e260..bb12bf26f4 100644 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/File.cs +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/File.cs @@ -5,7 +5,6 @@ using System.Buffers; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Security; using System.Text; using System.Threading; @@ -25,7 +24,6 @@ namespace System.IO { if (path == null) throw new ArgumentNullException(nameof(path)); - Contract.EndContractBlock(); return new StreamReader(path); } @@ -34,7 +32,6 @@ namespace System.IO { if (path == null) throw new ArgumentNullException(nameof(path)); - Contract.EndContractBlock(); return new StreamWriter(path, append: false); } @@ -43,7 +40,6 @@ namespace System.IO { if (path == null) throw new ArgumentNullException(nameof(path)); - Contract.EndContractBlock(); return new StreamWriter(path, append: true); } @@ -68,7 +64,6 @@ namespace System.IO throw new ArgumentException(SR.Argument_EmptyFileName, nameof(sourceFileName)); if (destFileName.Length == 0) throw new ArgumentException(SR.Argument_EmptyFileName, nameof(destFileName)); - Contract.EndContractBlock(); InternalCopy(sourceFileName, destFileName, false); } @@ -92,7 +87,6 @@ namespace System.IO throw new ArgumentException(SR.Argument_EmptyFileName, nameof(sourceFileName)); if (destFileName.Length == 0) throw new ArgumentException(SR.Argument_EmptyFileName, nameof(destFileName)); - Contract.EndContractBlock(); InternalCopy(sourceFileName, destFileName, overwrite); } @@ -100,7 +94,6 @@ namespace System.IO /// /// Note: This returns the fully qualified name of the destination file. /// - [System.Security.SecuritySafeCritical] internal static string InternalCopy(string sourceFileName, string destFileName, bool overwrite) { Debug.Assert(sourceFileName != null); @@ -111,7 +104,7 @@ namespace System.IO string fullSourceFileName = Path.GetFullPath(sourceFileName); string fullDestFileName = Path.GetFullPath(destFileName); - FileSystem.Current.CopyFile(fullSourceFileName, fullDestFileName, overwrite); + FileSystem.CopyFile(fullSourceFileName, fullDestFileName, overwrite); return fullDestFileName; } @@ -158,16 +151,14 @@ namespace System.IO // // Your application must have Delete permission to the target file. // - [System.Security.SecuritySafeCritical] public static void Delete(string path) { if (path == null) throw new ArgumentNullException(nameof(path)); - Contract.EndContractBlock(); string fullPath = Path.GetFullPath(path); - FileSystem.Current.DeleteFile(fullPath); + FileSystem.DeleteFile(fullPath); } @@ -178,7 +169,6 @@ namespace System.IO // // Your application must have Read permission for the target directory. // - [System.Security.SecuritySafeCritical] public static bool Exists(string path) { try @@ -209,10 +199,9 @@ namespace System.IO return false; } - [System.Security.SecurityCritical] // auto-generated internal static bool InternalExists(string path) { - return FileSystem.Current.FileExists(path); + return FileSystem.FileExists(path); } public static FileStream Open(string path, FileMode mode) @@ -245,96 +234,87 @@ namespace System.IO public static void SetCreationTime(string path, DateTime creationTime) { string fullPath = Path.GetFullPath(path); - FileSystem.Current.SetCreationTime(fullPath, creationTime, asDirectory: false); + FileSystem.SetCreationTime(fullPath, creationTime, asDirectory: false); } public static void SetCreationTimeUtc(string path, DateTime creationTimeUtc) { string fullPath = Path.GetFullPath(path); - FileSystem.Current.SetCreationTime(fullPath, GetUtcDateTimeOffset(creationTimeUtc), asDirectory: false); + FileSystem.SetCreationTime(fullPath, GetUtcDateTimeOffset(creationTimeUtc), asDirectory: false); } - [System.Security.SecuritySafeCritical] public static DateTime GetCreationTime(string path) { string fullPath = Path.GetFullPath(path); - return FileSystem.Current.GetCreationTime(fullPath).LocalDateTime; + return FileSystem.GetCreationTime(fullPath).LocalDateTime; } - [System.Security.SecuritySafeCritical] // auto-generated public static DateTime GetCreationTimeUtc(string path) { string fullPath = Path.GetFullPath(path); - return FileSystem.Current.GetCreationTime(fullPath).UtcDateTime; + return FileSystem.GetCreationTime(fullPath).UtcDateTime; } public static void SetLastAccessTime(string path, DateTime lastAccessTime) { string fullPath = Path.GetFullPath(path); - FileSystem.Current.SetLastAccessTime(fullPath, lastAccessTime, asDirectory: false); + FileSystem.SetLastAccessTime(fullPath, lastAccessTime, asDirectory: false); } public static void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) { string fullPath = Path.GetFullPath(path); - FileSystem.Current.SetLastAccessTime(fullPath, GetUtcDateTimeOffset(lastAccessTimeUtc), asDirectory: false); + FileSystem.SetLastAccessTime(fullPath, GetUtcDateTimeOffset(lastAccessTimeUtc), asDirectory: false); } - [System.Security.SecuritySafeCritical] public static DateTime GetLastAccessTime(string path) { string fullPath = Path.GetFullPath(path); - return FileSystem.Current.GetLastAccessTime(fullPath).LocalDateTime; + return FileSystem.GetLastAccessTime(fullPath).LocalDateTime; } - [System.Security.SecuritySafeCritical] // auto-generated public static DateTime GetLastAccessTimeUtc(string path) { string fullPath = Path.GetFullPath(path); - return FileSystem.Current.GetLastAccessTime(fullPath).UtcDateTime; + return FileSystem.GetLastAccessTime(fullPath).UtcDateTime; } public static void SetLastWriteTime(string path, DateTime lastWriteTime) { string fullPath = Path.GetFullPath(path); - FileSystem.Current.SetLastWriteTime(fullPath, lastWriteTime, asDirectory: false); + FileSystem.SetLastWriteTime(fullPath, lastWriteTime, asDirectory: false); } public static void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) { string fullPath = Path.GetFullPath(path); - FileSystem.Current.SetLastWriteTime(fullPath, GetUtcDateTimeOffset(lastWriteTimeUtc), asDirectory: false); + FileSystem.SetLastWriteTime(fullPath, GetUtcDateTimeOffset(lastWriteTimeUtc), asDirectory: false); } - [System.Security.SecuritySafeCritical] public static DateTime GetLastWriteTime(string path) { string fullPath = Path.GetFullPath(path); - return FileSystem.Current.GetLastWriteTime(fullPath).LocalDateTime; + return FileSystem.GetLastWriteTime(fullPath).LocalDateTime; } - [System.Security.SecuritySafeCritical] // auto-generated public static DateTime GetLastWriteTimeUtc(string path) { string fullPath = Path.GetFullPath(path); - return FileSystem.Current.GetLastWriteTime(fullPath).UtcDateTime; + return FileSystem.GetLastWriteTime(fullPath).UtcDateTime; } - [System.Security.SecuritySafeCritical] public static FileAttributes GetAttributes(string path) { string fullPath = Path.GetFullPath(path); - return FileSystem.Current.GetAttributes(fullPath); + return FileSystem.GetAttributes(fullPath); } - [System.Security.SecurityCritical] public static void SetAttributes(string path, FileAttributes fileAttributes) { string fullPath = Path.GetFullPath(path); - FileSystem.Current.SetAttributes(fullPath, fileAttributes); + FileSystem.SetAttributes(fullPath, fileAttributes); } - [System.Security.SecuritySafeCritical] public static FileStream OpenRead(string path) { return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); @@ -346,19 +326,16 @@ namespace System.IO FileAccess.Write, FileShare.None); } - [System.Security.SecuritySafeCritical] // auto-generated public static string ReadAllText(string path) { if (path == null) throw new ArgumentNullException(nameof(path)); if (path.Length == 0) throw new ArgumentException(SR.Argument_EmptyPath, nameof(path)); - Contract.EndContractBlock(); return InternalReadAllText(path, Encoding.UTF8); } - [System.Security.SecuritySafeCritical] // auto-generated public static string ReadAllText(string path, Encoding encoding) { if (path == null) @@ -367,12 +344,10 @@ namespace System.IO throw new ArgumentNullException(nameof(encoding)); if (path.Length == 0) throw new ArgumentException(SR.Argument_EmptyPath, nameof(path)); - Contract.EndContractBlock(); return InternalReadAllText(path, encoding); } - [System.Security.SecurityCritical] private static string InternalReadAllText(string path, Encoding encoding) { Debug.Assert(path != null); @@ -383,14 +358,12 @@ namespace System.IO return sr.ReadToEnd(); } - [System.Security.SecuritySafeCritical] // auto-generated public static void WriteAllText(string path, string contents) { if (path == null) throw new ArgumentNullException(nameof(path)); if (path.Length == 0) throw new ArgumentException(SR.Argument_EmptyPath, nameof(path)); - Contract.EndContractBlock(); using (StreamWriter sw = new StreamWriter(path)) { @@ -398,7 +371,6 @@ namespace System.IO } } - [System.Security.SecuritySafeCritical] // auto-generated public static void WriteAllText(string path, string contents, Encoding encoding) { if (path == null) @@ -407,7 +379,6 @@ namespace System.IO throw new ArgumentNullException(nameof(encoding)); if (path.Length == 0) throw new ArgumentException(SR.Argument_EmptyPath, nameof(path)); - Contract.EndContractBlock(); using (StreamWriter sw = new StreamWriter(path, false, encoding)) { @@ -415,13 +386,11 @@ namespace System.IO } } - [System.Security.SecuritySafeCritical] // auto-generated public static byte[] ReadAllBytes(string path) { return InternalReadAllBytes(path); } - [System.Security.SecurityCritical] private static byte[] InternalReadAllBytes(string path) { // bufferSize == 1 used to avoid unnecessary buffer in FileStream @@ -446,7 +415,6 @@ namespace System.IO } } - [System.Security.SecuritySafeCritical] // auto-generated public static void WriteAllBytes(string path, byte[] bytes) { if (path == null) @@ -455,12 +423,10 @@ namespace System.IO throw new ArgumentException(SR.Argument_EmptyPath, nameof(path)); if (bytes == null) throw new ArgumentNullException(nameof(bytes)); - Contract.EndContractBlock(); InternalWriteAllBytes(path, bytes); } - [System.Security.SecurityCritical] private static void InternalWriteAllBytes(string path, byte[] bytes) { Debug.Assert(path != null); @@ -478,7 +444,6 @@ namespace System.IO throw new ArgumentNullException(nameof(path)); if (path.Length == 0) throw new ArgumentException(SR.Argument_EmptyPath, nameof(path)); - Contract.EndContractBlock(); return InternalReadAllLines(path, Encoding.UTF8); } @@ -491,7 +456,6 @@ namespace System.IO throw new ArgumentNullException(nameof(encoding)); if (path.Length == 0) throw new ArgumentException(SR.Argument_EmptyPath, nameof(path)); - Contract.EndContractBlock(); return InternalReadAllLines(path, encoding); } @@ -518,7 +482,6 @@ namespace System.IO throw new ArgumentNullException(nameof(path)); if (path.Length == 0) throw new ArgumentException(SR.Argument_EmptyPath, nameof(path)); - Contract.EndContractBlock(); return ReadLinesIterator.CreateIterator(path, Encoding.UTF8); } @@ -531,7 +494,6 @@ namespace System.IO throw new ArgumentNullException(nameof(encoding)); if (path.Length == 0) throw new ArgumentException(SR.Argument_EmptyPath, nameof(path)); - Contract.EndContractBlock(); return ReadLinesIterator.CreateIterator(path, encoding); } @@ -549,7 +511,6 @@ namespace System.IO throw new ArgumentNullException(nameof(contents)); if (path.Length == 0) throw new ArgumentException(SR.Argument_EmptyPath, nameof(path)); - Contract.EndContractBlock(); InternalWriteAllLines(new StreamWriter(path), contents); } @@ -569,7 +530,6 @@ namespace System.IO throw new ArgumentNullException(nameof(encoding)); if (path.Length == 0) throw new ArgumentException(SR.Argument_EmptyPath, nameof(path)); - Contract.EndContractBlock(); InternalWriteAllLines(new StreamWriter(path, false, encoding), contents); } @@ -594,7 +554,6 @@ namespace System.IO throw new ArgumentNullException(nameof(path)); if (path.Length == 0) throw new ArgumentException(SR.Argument_EmptyPath, nameof(path)); - Contract.EndContractBlock(); using (StreamWriter sw = new StreamWriter(path, append: true)) { @@ -610,7 +569,6 @@ namespace System.IO throw new ArgumentNullException(nameof(encoding)); if (path.Length == 0) throw new ArgumentException(SR.Argument_EmptyPath, nameof(path)); - Contract.EndContractBlock(); using (StreamWriter sw = new StreamWriter(path, true, encoding)) { @@ -626,7 +584,6 @@ namespace System.IO throw new ArgumentNullException(nameof(contents)); if (path.Length == 0) throw new ArgumentException(SR.Argument_EmptyPath, nameof(path)); - Contract.EndContractBlock(); InternalWriteAllLines(new StreamWriter(path, append: true), contents); } @@ -641,7 +598,6 @@ namespace System.IO throw new ArgumentNullException(nameof(encoding)); if (path.Length == 0) throw new ArgumentException(SR.Argument_EmptyPath, nameof(path)); - Contract.EndContractBlock(); InternalWriteAllLines(new StreamWriter(path, true, encoding), contents); } @@ -658,7 +614,7 @@ namespace System.IO if (destinationFileName == null) throw new ArgumentNullException(nameof(destinationFileName)); - FileSystem.Current.ReplaceFile( + FileSystem.ReplaceFile( Path.GetFullPath(sourceFileName), Path.GetFullPath(destinationFileName), destinationBackupFileName != null ? Path.GetFullPath(destinationBackupFileName) : null, @@ -673,7 +629,6 @@ namespace System.IO // sourceFileName and Write // permissions to destFileName. // - [System.Security.SecuritySafeCritical] public static void Move(string sourceFileName, string destFileName) { if (sourceFileName == null) @@ -684,7 +639,6 @@ namespace System.IO throw new ArgumentException(SR.Argument_EmptyFileName, nameof(sourceFileName)); if (destFileName.Length == 0) throw new ArgumentException(SR.Argument_EmptyFileName, nameof(destFileName)); - Contract.EndContractBlock(); string fullSourceFileName = Path.GetFullPath(sourceFileName); string fullDestFileName = Path.GetFullPath(destFileName); @@ -694,7 +648,7 @@ namespace System.IO throw new FileNotFoundException(SR.Format(SR.IO_FileNotFound_FileName, fullSourceFileName), fullSourceFileName); } - FileSystem.Current.MoveFile(fullSourceFileName, fullDestFileName); + FileSystem.MoveFile(fullSourceFileName, fullDestFileName); } public static void Encrypt(string path) diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/FileInfo.Windows.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/FileInfo.Windows.cs index 5a4d18e6fb..cec962f3f4 100644 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/FileInfo.Windows.cs +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/FileInfo.Windows.cs @@ -3,18 +3,16 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Security; namespace System.IO { partial class FileInfo { - [SecurityCritical] - internal FileInfo(string fullPath, ref Interop.Kernel32.WIN32_FIND_DATA findData) - : this(fullPath, findData.cFileName.GetStringFromFixedBuffer()) + internal unsafe FileInfo(string fullPath, string fileName, ref RawFindData findData) + : this(fullPath, fileName: fileName, isNormalized: true) { - Debug.Assert(findData.cFileName.FixedBufferEqualsString(Path.GetFileName(fullPath))); - Init(ref findData); + Debug.Assert(fileName.Equals(Path.GetFileName(fullPath))); + Init(findData._info); } } } diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/FileInfo.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/FileInfo.cs index 0a3540bace..45d85da99f 100644 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/FileInfo.cs +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/FileInfo.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Text; namespace System.IO @@ -15,44 +14,24 @@ namespace System.IO { private string _name; - [System.Security.SecurityCritical] private FileInfo() { } - [System.Security.SecuritySafeCritical] public FileInfo(string fileName) + : this(fileName, isNormalized: false) { - if (fileName == null) - throw new ArgumentNullException(nameof(fileName)); - Contract.EndContractBlock(); - - Init(fileName); } - [System.Security.SecurityCritical] - private void Init(string fileName) + internal FileInfo(string originalPath, string fullPath = null, string fileName = null, bool isNormalized = false) { - OriginalPath = fileName; - // Must fully qualify the path for the security check - string fullPath = Path.GetFullPath(fileName); + // Want to throw the original argument name + OriginalPath = originalPath ?? throw new ArgumentNullException("fileName"); - _name = Path.GetFileName(fileName); - FullPath = fullPath; - DisplayPath = GetDisplayPath(fileName); - } + fullPath = fullPath ?? originalPath; + Debug.Assert(!isNormalized || !PathInternal.IsPartiallyQualified(fullPath), "should be fully qualified if normalized"); - private string GetDisplayPath(string originalPath) - { - return originalPath; - } - - [System.Security.SecuritySafeCritical] - internal FileInfo(string fullPath, string originalPath) - { - Debug.Assert(Path.IsPathRooted(fullPath), "fullPath must be fully qualified!"); - _name = originalPath ?? Path.GetFileName(fullPath); - OriginalPath = _name; - FullPath = fullPath; - DisplayPath = _name; + FullPath = isNormalized ? fullPath ?? originalPath : Path.GetFullPath(fullPath); + _name = fileName ?? Path.GetFileName(originalPath); + DisplayPath = originalPath; } public override string Name @@ -60,24 +39,21 @@ namespace System.IO get { return _name; } } - public long Length { - [System.Security.SecuritySafeCritical] // auto-generated get { - if ((FileSystemObject.Attributes & FileAttributes.Directory) == FileAttributes.Directory) + if ((Attributes & FileAttributes.Directory) == FileAttributes.Directory) { throw new FileNotFoundException(SR.Format(SR.IO_FileNotFound_FileName, DisplayPath), DisplayPath); } - return FileSystemObject.Length; + return LengthCore; } } /* Returns the name of the directory that the file is in */ public string DirectoryName { - [System.Security.SecuritySafeCritical] get { return Path.GetDirectoryName(FullPath); @@ -111,7 +87,6 @@ namespace System.IO } } - [System.Security.SecuritySafeCritical] // auto-generated public StreamReader OpenText() { return new StreamReader(FullPath, Encoding.UTF8, detectEncodingFromByteOrderMarks: true); @@ -143,10 +118,8 @@ namespace System.IO throw new ArgumentNullException(nameof(destFileName), SR.ArgumentNull_FileName); if (destFileName.Length == 0) throw new ArgumentException(SR.Argument_EmptyFileName, nameof(destFileName)); - Contract.EndContractBlock(); - destFileName = File.InternalCopy(FullPath, destFileName, false); - return new FileInfo(destFileName, null); + return new FileInfo(File.InternalCopy(FullPath, destFileName, false), isNormalized: true); } @@ -165,10 +138,8 @@ namespace System.IO throw new ArgumentNullException(nameof(destFileName), SR.ArgumentNull_FileName); if (destFileName.Length == 0) throw new ArgumentException(SR.Argument_EmptyFileName, nameof(destFileName)); - Contract.EndContractBlock(); - destFileName = File.InternalCopy(FullPath, destFileName, overwrite); - return new FileInfo(destFileName, null); + return new FileInfo(File.InternalCopy(FullPath, destFileName, overwrite), isNormalized: true); } public FileStream Create() @@ -186,10 +157,9 @@ namespace System.IO // // Your application must have Delete permission to the target file. // - [System.Security.SecuritySafeCritical] public override void Delete() { - FileSystem.Current.DeleteFile(FullPath); + FileSystem.DeleteFile(FullPath); } // Tests if the given file exists. The result is true if the file @@ -199,12 +169,11 @@ namespace System.IO // Your application must have Read permission for the target directory. public override bool Exists { - [System.Security.SecuritySafeCritical] // auto-generated get { try { - return FileSystemObject.Exists; + return ExistsCore; } catch { @@ -229,7 +198,6 @@ namespace System.IO return new FileStream(FullPath, mode, access, share); } - [System.Security.SecuritySafeCritical] // auto-generated public FileStream OpenRead() { return new FileStream(FullPath, FileMode.Open, FileAccess.Read, @@ -250,14 +218,12 @@ namespace System.IO // sourceFileName and Write // permissions to destFileName. // - [System.Security.SecuritySafeCritical] public void MoveTo(string destFileName) { if (destFileName == null) throw new ArgumentNullException(nameof(destFileName)); if (destFileName.Length == 0) throw new ArgumentException(SR.Argument_EmptyFileName, nameof(destFileName)); - Contract.EndContractBlock(); string fullDestFileName = Path.GetFullPath(destFileName); @@ -273,12 +239,12 @@ namespace System.IO throw new FileNotFoundException(SR.Format(SR.IO_FileNotFound_FileName, FullName), FullName); } - FileSystem.Current.MoveFile(FullPath, fullDestFileName); + FileSystem.MoveFile(FullPath, fullDestFileName); FullPath = fullDestFileName; OriginalPath = destFileName; _name = Path.GetFileName(fullDestFileName); - DisplayPath = GetDisplayPath(destFileName); + DisplayPath = destFileName; // Flush any cached information about the file. Invalidate(); } diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/UnixFileSystem.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystem.Unix.cs similarity index 91% rename from external/corefx/src/System.IO.FileSystem/src/System/IO/UnixFileSystem.cs rename to external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystem.Unix.cs index 253a3ed2a8..955d568322 100644 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/UnixFileSystem.cs +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystem.Unix.cs @@ -11,11 +11,11 @@ using System.Threading; namespace System.IO { /// Provides an implementation of FileSystem for Unix systems. - internal sealed partial class UnixFileSystem : FileSystem + internal static partial class FileSystem { internal const int DefaultBufferSize = 4096; - public override void CopyFile(string sourceFullPath, string destFullPath, bool overwrite) + public static void CopyFile(string sourceFullPath, string destFullPath, bool overwrite) { // The destination path may just be a directory into which the file should be copied. // If it is, append the filename from the source onto the destination directory @@ -32,7 +32,7 @@ namespace System.IO } } - public override void ReplaceFile(string sourceFullPath, string destFullPath, string destBackupFullPath, bool ignoreMetadataErrors) + public static void ReplaceFile(string sourceFullPath, string destFullPath, string destBackupFullPath, bool ignoreMetadataErrors) { if (destBackupFullPath != null) { @@ -69,7 +69,7 @@ namespace System.IO Interop.CheckIo(Interop.Sys.Rename(sourceFullPath, destFullPath)); } - public override void MoveFile(string sourceFullPath, string destFullPath) + public static void MoveFile(string sourceFullPath, string destFullPath) { // The desired behavior for Move(source, dest) is to not overwrite the destination file // if it exists. Since rename(source, dest) will replace the file at 'dest' if it exists, @@ -145,7 +145,7 @@ namespace System.IO DeleteFile(sourceFullPath); } - public override void DeleteFile(string fullPath) + public static void DeleteFile(string fullPath) { if (Interop.Sys.Unlink(fullPath) < 0) { @@ -179,7 +179,7 @@ namespace System.IO } } - public override void CreateDirectory(string fullPath) + public static void CreateDirectory(string fullPath) { // NOTE: This logic is primarily just carried forward from Win32FileSystem.CreateDirectory. @@ -280,7 +280,7 @@ namespace System.IO } } - public override void MoveDirectory(string sourceFullPath, string destFullPath) + public static void MoveDirectory(string sourceFullPath, string destFullPath) { // Windows doesn't care if you try and copy a file via "MoveDirectory"... if (FileExists(sourceFullPath)) @@ -313,7 +313,7 @@ namespace System.IO } } - public override void RemoveDirectory(string fullPath, bool recursive) + public static void RemoveDirectory(string fullPath, bool recursive) { var di = new DirectoryInfo(fullPath); if (!di.Exists) @@ -323,7 +323,7 @@ namespace System.IO RemoveDirectoryInternal(di, recursive, throwOnTopLevelDirectoryNotFound: true); } - private void RemoveDirectoryInternal(DirectoryInfo directory, bool recursive, bool throwOnTopLevelDirectoryNotFound) + private static void RemoveDirectoryInternal(DirectoryInfo directory, bool recursive, bool throwOnTopLevelDirectoryNotFound) { Exception firstException = null; @@ -399,7 +399,7 @@ namespace System.IO } } - public override bool DirectoryExists(string fullPath) + public static bool DirectoryExists(string fullPath) { Interop.ErrorInfo ignored; return DirectoryExists(fullPath, out ignored); @@ -410,7 +410,7 @@ namespace System.IO return FileExists(fullPath, Interop.Sys.FileTypes.S_IFDIR, out errorInfo); } - public override bool FileExists(string fullPath) + public static bool FileExists(string fullPath) { Interop.ErrorInfo ignored; @@ -444,12 +444,12 @@ namespace System.IO ((fileinfo.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR); } - public override IEnumerable EnumeratePaths(string path, string searchPattern, SearchOption searchOption, SearchTarget searchTarget) + public static IEnumerable EnumeratePaths(string path, string searchPattern, SearchOption searchOption, SearchTarget searchTarget) { return new FileSystemEnumerable(path, searchPattern, searchOption, searchTarget, (p, _) => p); } - public override IEnumerable EnumerateFileSystemInfos(string fullPath, string searchPattern, SearchOption searchOption, SearchTarget searchTarget) + public static IEnumerable EnumerateFileSystemInfos(string fullPath, string searchPattern, SearchOption searchOption, SearchTarget searchTarget) { switch (searchTarget) { @@ -510,7 +510,6 @@ namespace System.IO searchPattern = NormalizeSearchPattern(searchPattern); if (searchPattern.Length > 0) { - PathHelpers.CheckSearchPattern(searchPattern); PathHelpers.ThrowIfEmptyOrRootedPath(searchPattern); // If the search pattern contains any paths, make sure we factor those into @@ -696,17 +695,17 @@ namespace System.IO return name == "." || name == ".."; } - public override string GetCurrentDirectory() + public static string GetCurrentDirectory() { return Interop.Sys.GetCwd(); } - public override void SetCurrentDirectory(string fullPath) + public static void SetCurrentDirectory(string fullPath) { Interop.CheckIo(Interop.Sys.ChDir(fullPath), fullPath, isDirectory:true); } - public override FileAttributes GetAttributes(string fullPath) + public static FileAttributes GetAttributes(string fullPath) { FileAttributes attributes = new FileInfo(fullPath, null).Attributes; @@ -716,61 +715,61 @@ namespace System.IO return attributes; } - public override void SetAttributes(string fullPath, FileAttributes attributes) + public static void SetAttributes(string fullPath, FileAttributes attributes) { new FileInfo(fullPath, null).Attributes = attributes; } - public override DateTimeOffset GetCreationTime(string fullPath) + public static DateTimeOffset GetCreationTime(string fullPath) { return new FileInfo(fullPath, null).CreationTime; } - public override void SetCreationTime(string fullPath, DateTimeOffset time, bool asDirectory) + public static void SetCreationTime(string fullPath, DateTimeOffset time, bool asDirectory) { - IFileSystemObject info = asDirectory ? - (IFileSystemObject)new DirectoryInfo(fullPath, null) : - (IFileSystemObject)new FileInfo(fullPath, null); + FileSystemInfo info = asDirectory ? + (FileSystemInfo)new DirectoryInfo(fullPath, null) : + (FileSystemInfo)new FileInfo(fullPath, null); - info.CreationTime = time; + info.CreationTimeCore = time; } - public override DateTimeOffset GetLastAccessTime(string fullPath) + public static DateTimeOffset GetLastAccessTime(string fullPath) { return new FileInfo(fullPath, null).LastAccessTime; } - public override void SetLastAccessTime(string fullPath, DateTimeOffset time, bool asDirectory) + public static void SetLastAccessTime(string fullPath, DateTimeOffset time, bool asDirectory) { - IFileSystemObject info = asDirectory ? - (IFileSystemObject)new DirectoryInfo(fullPath, null) : - (IFileSystemObject)new FileInfo(fullPath, null); + FileSystemInfo info = asDirectory ? + (FileSystemInfo)new DirectoryInfo(fullPath, null) : + (FileSystemInfo)new FileInfo(fullPath, null); - info.LastAccessTime = time; + info.LastAccessTimeCore = time; } - public override DateTimeOffset GetLastWriteTime(string fullPath) + public static DateTimeOffset GetLastWriteTime(string fullPath) { return new FileInfo(fullPath, null).LastWriteTime; } - public override void SetLastWriteTime(string fullPath, DateTimeOffset time, bool asDirectory) + public static void SetLastWriteTime(string fullPath, DateTimeOffset time, bool asDirectory) { - IFileSystemObject info = asDirectory ? - (IFileSystemObject)new DirectoryInfo(fullPath, null) : - (IFileSystemObject)new FileInfo(fullPath, null); + FileSystemInfo info = asDirectory ? + (FileSystemInfo)new DirectoryInfo(fullPath, null) : + (FileSystemInfo)new FileInfo(fullPath, null); - info.LastWriteTime = time; + info.LastWriteTimeCore = time; } - public override IFileSystemObject GetFileSystemInfo(string fullPath, bool asDirectory) + public static FileSystemInfo GetFileSystemInfo(string fullPath, bool asDirectory) { return asDirectory ? - (IFileSystemObject)new DirectoryInfo(fullPath, null) : - (IFileSystemObject)new FileInfo(fullPath, null); + (FileSystemInfo)new DirectoryInfo(fullPath, null) : + (FileSystemInfo)new FileInfo(fullPath, null); } - public override string[] GetLogicalDrives() + public static string[] GetLogicalDrives() { return DriveInfoInternal.GetLogicalDrives(); } diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/Win32FileSystem.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystem.Windows.cs similarity index 81% rename from external/corefx/src/System.IO.FileSystem/src/System/IO/Win32FileSystem.cs rename to external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystem.Windows.cs index 5e562b7cc0..84f8e6ff87 100644 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/Win32FileSystem.cs +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystem.Windows.cs @@ -9,13 +9,12 @@ using System.Text; namespace System.IO { - internal sealed partial class Win32FileSystem : FileSystem + internal static partial class FileSystem { internal const int GENERIC_READ = unchecked((int)0x80000000); - public override void CopyFile(string sourceFullPath, string destFullPath, bool overwrite) + public static void CopyFile(string sourceFullPath, string destFullPath, bool overwrite) { - Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = default(Interop.Kernel32.SECURITY_ATTRIBUTES); int errorCode = Interop.Kernel32.CopyFile(sourceFullPath, destFullPath, !overwrite); if (errorCode != Interop.Errors.ERROR_SUCCESS) @@ -24,10 +23,9 @@ namespace System.IO if (errorCode != Interop.Errors.ERROR_FILE_EXISTS) { - // For a number of error codes (sharing violation, path - // not found, etc) we don't know if the problem was with + // For a number of error codes (sharing violation, path not found, etc) we don't know if the problem was with // the source or dest file. Try reading the source file. - using (SafeFileHandle handle = Interop.Kernel32.UnsafeCreateFile(sourceFullPath, GENERIC_READ, FileShare.Read, ref secAttrs, FileMode.Open, 0, IntPtr.Zero)) + using (SafeFileHandle handle = Interop.Kernel32.CreateFile(sourceFullPath, GENERIC_READ, FileShare.Read, FileMode.Open, 0)) { if (handle.IsInvalid) fileName = sourceFullPath; @@ -44,7 +42,7 @@ namespace System.IO } } - public override void ReplaceFile(string sourceFullPath, string destFullPath, string destBackupFullPath, bool ignoreMetadataErrors) + public static void ReplaceFile(string sourceFullPath, string destFullPath, string destBackupFullPath, bool ignoreMetadataErrors) { int flags = ignoreMetadataErrors ? Interop.Kernel32.REPLACEFILE_IGNORE_MERGE_ERRORS : 0; @@ -54,8 +52,7 @@ namespace System.IO } } - [System.Security.SecuritySafeCritical] - public override void CreateDirectory(string fullPath) + public static void CreateDirectory(string fullPath) { // We can save a bunch of work if the directory we want to create already exists. This also // saves us in the case where sub paths are inaccessible (due to ERROR_ACCESS_DENIED) but the @@ -105,7 +102,7 @@ namespace System.IO // If we were passed a DirectorySecurity, convert it to a security // descriptor and set it in he call to CreateDirectory. - Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = default(Interop.Kernel32.SECURITY_ATTRIBUTES); + Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = default; bool r = true; int firstError = 0; @@ -159,7 +156,7 @@ namespace System.IO throw Win32Marshal.GetExceptionForWin32Error(firstError, errorString); } - public override void DeleteFile(string fullPath) + public static void DeleteFile(string fullPath) { bool r = Interop.Kernel32.DeleteFile(fullPath); if (!r) @@ -172,39 +169,47 @@ namespace System.IO } } - public override bool DirectoryExists(string fullPath) + public static bool DirectoryExists(string fullPath) { - int lastError = Interop.Errors.ERROR_SUCCESS; - return DirectoryExists(fullPath, out lastError); + return DirectoryExists(fullPath, out int lastError); } - private bool DirectoryExists(string path, out int lastError) + private static bool DirectoryExists(string path, out int lastError) { Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); lastError = FillAttributeInfo(path, ref data, returnErrorOnNotFound: true); - return (lastError == 0) && (data.fileAttributes != -1) - && ((data.fileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) != 0); + return (lastError == 0) && (data.dwFileAttributes != -1) + && ((data.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) != 0); } - public override IEnumerable EnumeratePaths(string fullPath, string searchPattern, SearchOption searchOption, SearchTarget searchTarget) + public static IEnumerable EnumeratePaths(string fullPath, string searchPattern, SearchOption searchOption, SearchTarget searchTarget) { - return Win32FileSystemEnumerableFactory.CreateFileNameIterator(fullPath, fullPath, searchPattern, - (searchTarget & SearchTarget.Files) == SearchTarget.Files, - (searchTarget & SearchTarget.Directories) == SearchTarget.Directories, - searchOption); + FindEnumerableFactory.NormalizeInputs(ref fullPath, ref searchPattern); + switch (searchTarget) + { + case SearchTarget.Files: + return FindEnumerableFactory.UserFiles(fullPath, searchPattern, searchOption == SearchOption.AllDirectories); + case SearchTarget.Directories: + return FindEnumerableFactory.UserDirectories(fullPath, searchPattern, searchOption == SearchOption.AllDirectories); + case SearchTarget.Both: + return FindEnumerableFactory.UserEntries(fullPath, searchPattern, searchOption == SearchOption.AllDirectories); + default: + throw new ArgumentOutOfRangeException(nameof(searchTarget)); + } } - public override IEnumerable EnumerateFileSystemInfos(string fullPath, string searchPattern, SearchOption searchOption, SearchTarget searchTarget) + public static IEnumerable EnumerateFileSystemInfos(string fullPath, string searchPattern, SearchOption searchOption, SearchTarget searchTarget) { + FindEnumerableFactory.NormalizeInputs(ref fullPath, ref searchPattern); switch (searchTarget) { case SearchTarget.Directories: - return Win32FileSystemEnumerableFactory.CreateDirectoryInfoIterator(fullPath, fullPath, searchPattern, searchOption); + return FindEnumerableFactory.DirectoryInfos(fullPath, searchPattern, searchOption == SearchOption.AllDirectories); case SearchTarget.Files: - return Win32FileSystemEnumerableFactory.CreateFileInfoIterator(fullPath, fullPath, searchPattern, searchOption); + return FindEnumerableFactory.FileInfos(fullPath, searchPattern, searchOption == SearchOption.AllDirectories); case SearchTarget.Both: - return Win32FileSystemEnumerableFactory.CreateFileSystemInfoIterator(fullPath, fullPath, searchPattern, searchOption); + return FindEnumerableFactory.FileSystemInfos(fullPath, searchPattern, searchOption == SearchOption.AllDirectories); default: throw new ArgumentException(SR.ArgumentOutOfRange_Enum, nameof(searchTarget)); } @@ -215,7 +220,6 @@ namespace System.IO /// classes should use -1 as the uninitialized state for dataInitialized. /// /// Return the error code for not found errors? - [System.Security.SecurityCritical] internal static int FillAttributeInfo(string path, ref Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data, bool returnErrorOnNotFound) { int errorCode = Interop.Errors.ERROR_SUCCESS; @@ -260,7 +264,7 @@ namespace System.IO case Interop.Errors.ERROR_PATH_NOT_FOUND: case Interop.Errors.ERROR_NOT_READY: // Removable media not ready // Return default value for backward compatibility - data.fileAttributes = -1; + data.dwFileAttributes = -1; return Interop.Errors.ERROR_SUCCESS; } } @@ -268,26 +272,26 @@ namespace System.IO return errorCode; } - public override bool FileExists(string fullPath) + public static bool FileExists(string fullPath) { Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); int errorCode = FillAttributeInfo(fullPath, ref data, returnErrorOnNotFound: true); - return (errorCode == 0) && (data.fileAttributes != -1) - && ((data.fileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) == 0); + return (errorCode == 0) && (data.dwFileAttributes != -1) + && ((data.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) == 0); } - public override FileAttributes GetAttributes(string fullPath) + public static FileAttributes GetAttributes(string fullPath) { Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); int errorCode = FillAttributeInfo(fullPath, ref data, returnErrorOnNotFound: true); if (errorCode != 0) throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); - return (FileAttributes)data.fileAttributes; + return (FileAttributes)data.dwFileAttributes; } - public override string GetCurrentDirectory() + public static string GetCurrentDirectory() { StringBuilder sb = StringBuilderCache.Acquire(Interop.Kernel32.MAX_PATH + 1); if (Interop.Kernel32.GetCurrentDirectory(sb.Capacity, sb) == 0) @@ -317,47 +321,44 @@ namespace System.IO return currentDirectory; } - public override DateTimeOffset GetCreationTime(string fullPath) + public static DateTimeOffset GetCreationTime(string fullPath) { Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); int errorCode = FillAttributeInfo(fullPath, ref data, returnErrorOnNotFound: false); if (errorCode != 0) throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); - long dt = ((long)(data.ftCreationTimeHigh) << 32) | ((long)data.ftCreationTimeLow); - return DateTimeOffset.FromFileTime(dt); + return data.ftCreationTime.ToDateTimeOffset(); } - public override IFileSystemObject GetFileSystemInfo(string fullPath, bool asDirectory) + public static FileSystemInfo GetFileSystemInfo(string fullPath, bool asDirectory) { return asDirectory ? - (IFileSystemObject)new DirectoryInfo(fullPath, null) : - (IFileSystemObject)new FileInfo(fullPath, null); + (FileSystemInfo)new DirectoryInfo(fullPath, null) : + (FileSystemInfo)new FileInfo(fullPath, null); } - public override DateTimeOffset GetLastAccessTime(string fullPath) + public static DateTimeOffset GetLastAccessTime(string fullPath) { Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); int errorCode = FillAttributeInfo(fullPath, ref data, returnErrorOnNotFound: false); if (errorCode != 0) throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); - long dt = ((long)(data.ftLastAccessTimeHigh) << 32) | ((long)data.ftLastAccessTimeLow); - return DateTimeOffset.FromFileTime(dt); + return data.ftLastAccessTime.ToDateTimeOffset(); } - public override DateTimeOffset GetLastWriteTime(string fullPath) + public static DateTimeOffset GetLastWriteTime(string fullPath) { Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); int errorCode = FillAttributeInfo(fullPath, ref data, returnErrorOnNotFound: false); if (errorCode != 0) throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); - long dt = ((long)data.ftLastWriteTimeHigh << 32) | ((long)data.ftLastWriteTimeLow); - return DateTimeOffset.FromFileTime(dt); + return data.ftLastWriteTime.ToDateTimeOffset(); } - public override void MoveDirectory(string sourceFullPath, string destFullPath) + public static void MoveDirectory(string sourceFullPath, string destFullPath) { if (!Interop.Kernel32.MoveFile(sourceFullPath, destFullPath)) { @@ -374,7 +375,7 @@ namespace System.IO } } - public override void MoveFile(string sourceFullPath, string destFullPath) + public static void MoveFile(string sourceFullPath, string destFullPath) { if (!Interop.Kernel32.MoveFile(sourceFullPath, destFullPath)) { @@ -382,7 +383,6 @@ namespace System.IO } } - [System.Security.SecurityCritical] private static SafeFileHandle OpenHandle(string fullPath, bool asDirectory) { string root = fullPath.Substring(0, PathInternal.GetRootLength(fullPath)); @@ -392,16 +392,12 @@ namespace System.IO throw new ArgumentException(SR.Arg_PathIsVolume, "path"); } - Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = default(Interop.Kernel32.SECURITY_ATTRIBUTES); - SafeFileHandle handle = Interop.Kernel32.SafeCreateFile( + SafeFileHandle handle = Interop.Kernel32.CreateFile( fullPath, Interop.Kernel32.GenericOperations.GENERIC_WRITE, FileShare.ReadWrite | FileShare.Delete, - ref secAttrs, FileMode.Open, - asDirectory ? Interop.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS : (int)FileOptions.None, - IntPtr.Zero - ); + asDirectory ? Interop.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS : 0); if (handle.IsInvalid) { @@ -415,10 +411,11 @@ namespace System.IO throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); } + return handle; } - public override void RemoveDirectory(string fullPath, bool recursive) + public static void RemoveDirectory(string fullPath, bool recursive) { // Do not recursively delete through reparse points. if (!recursive || IsReparsePoint(fullPath)) @@ -447,7 +444,7 @@ namespace System.IO throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); } - return (((FileAttributes)data.fileAttributes & FileAttributes.ReparsePoint) != 0); + return (((FileAttributes)data.dwFileAttributes & FileAttributes.ReparsePoint) != 0); } private static void RemoveDirectoryRecursive(string fullPath, ref Interop.Kernel32.WIN32_FIND_DATA findData, bool topLevel) @@ -575,15 +572,9 @@ namespace System.IO } } - public override void SetAttributes(string fullPath, FileAttributes attributes) + public static void SetAttributes(string fullPath, FileAttributes attributes) { - SetAttributesInternal(fullPath, attributes); - } - - private static void SetAttributesInternal(string fullPath, FileAttributes attributes) - { - bool r = Interop.Kernel32.SetFileAttributes(fullPath, (int)attributes); - if (!r) + if (!Interop.Kernel32.SetFileAttributes(fullPath, (int)attributes)) { int errorCode = Marshal.GetLastWin32Error(); if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) @@ -592,24 +583,18 @@ namespace System.IO } } - public override void SetCreationTime(string fullPath, DateTimeOffset time, bool asDirectory) - { - SetCreationTimeInternal(fullPath, time, asDirectory); - } - - private static void SetCreationTimeInternal(string fullPath, DateTimeOffset time, bool asDirectory) + public static void SetCreationTime(string fullPath, DateTimeOffset time, bool asDirectory) { using (SafeFileHandle handle = OpenHandle(fullPath, asDirectory)) { - bool r = Interop.Kernel32.SetFileTime(handle, creationTime: time.ToFileTime()); - if (!r) + if (!Interop.Kernel32.SetFileTime(handle, creationTime: time.ToFileTime())) { throw Win32Marshal.GetExceptionForLastWin32Error(fullPath); } } } - public override void SetCurrentDirectory(string fullPath) + public static void SetCurrentDirectory(string fullPath) { if (!Interop.Kernel32.SetCurrentDirectory(fullPath)) { @@ -623,41 +608,29 @@ namespace System.IO } } - public override void SetLastAccessTime(string fullPath, DateTimeOffset time, bool asDirectory) - { - SetLastAccessTimeInternal(fullPath, time, asDirectory); - } - - private static void SetLastAccessTimeInternal(string fullPath, DateTimeOffset time, bool asDirectory) + public static void SetLastAccessTime(string fullPath, DateTimeOffset time, bool asDirectory) { using (SafeFileHandle handle = OpenHandle(fullPath, asDirectory)) { - bool r = Interop.Kernel32.SetFileTime(handle, lastAccessTime: time.ToFileTime()); - if (!r) + if (!Interop.Kernel32.SetFileTime(handle, lastAccessTime: time.ToFileTime())) { throw Win32Marshal.GetExceptionForLastWin32Error(fullPath); } } } - public override void SetLastWriteTime(string fullPath, DateTimeOffset time, bool asDirectory) - { - SetLastWriteTimeInternal(fullPath, time, asDirectory); - } - - private static void SetLastWriteTimeInternal(string fullPath, DateTimeOffset time, bool asDirectory) + public static void SetLastWriteTime(string fullPath, DateTimeOffset time, bool asDirectory) { using (SafeFileHandle handle = OpenHandle(fullPath, asDirectory)) { - bool r = Interop.Kernel32.SetFileTime(handle, lastWriteTime: time.ToFileTime()); - if (!r) + if (!Interop.Kernel32.SetFileTime(handle, lastWriteTime: time.ToFileTime())) { throw Win32Marshal.GetExceptionForLastWin32Error(fullPath); } } } - public override string[] GetLogicalDrives() + public static string[] GetLogicalDrives() { return DriveInfoInternal.GetLogicalDrives(); } diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystem.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystem.cs deleted file mode 100644 index 9e3056e2cc..0000000000 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystem.cs +++ /dev/null @@ -1,48 +0,0 @@ -// 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.Collections.Generic; - -namespace System.IO -{ - internal abstract partial class FileSystem - { - public static FileSystem Current { get { return s_current; } } - - // Directory - public abstract void CreateDirectory(string fullPath); - public abstract bool DirectoryExists(string fullPath); - public abstract void MoveDirectory(string sourceFullPath, string destFullPath); - public abstract void RemoveDirectory(string fullPath, bool recursive); - - // File - public abstract void CopyFile(string sourceFullPath, string destFullPath, bool overwrite); - public abstract void ReplaceFile(string sourceFullPath, string destFullPath, string destBackupFullPath, bool ignoreMetadataErrors); - public abstract void DeleteFile(string fullPath); - public abstract bool FileExists(string fullPath); - public abstract void MoveFile(string sourceFullPath, string destFullPath); - - public abstract FileAttributes GetAttributes(string fullPath); - public abstract void SetAttributes(string fullPath, FileAttributes attributes); - - public abstract DateTimeOffset GetCreationTime(string fullPath); - public abstract void SetCreationTime(string fullPath, DateTimeOffset time, bool asDirectory); - public abstract DateTimeOffset GetLastAccessTime(string fullPath); - public abstract void SetLastAccessTime(string fullPath, DateTimeOffset time, bool asDirectory); - public abstract DateTimeOffset GetLastWriteTime(string fullPath); - public abstract void SetLastWriteTime(string fullPath, DateTimeOffset time, bool asDirectory); - - // Discovery - public abstract IFileSystemObject GetFileSystemInfo(string fullPath, bool asDirectory); - public abstract IEnumerable EnumeratePaths(string fullPath, string searchPattern, SearchOption searchOption, SearchTarget searchTarget); - public abstract IEnumerable EnumerateFileSystemInfos(string fullPath, string searchPattern, SearchOption searchOption, SearchTarget searchTarget); - - // Path - public abstract string GetCurrentDirectory(); - public abstract void SetCurrentDirectory(string fullPath); - - // Volume - public abstract string[] GetLogicalDrives(); - } -} diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Unix.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Unix.cs index 647bcd3c82..6e3733e8a8 100644 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Unix.cs +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Unix.cs @@ -4,8 +4,10 @@ namespace System.IO { - partial class FileSystemInfo : IFileSystemObject + partial class FileSystemInfo { + private const int NanosecondsPerTick = 100; + /// The last cached stat information about the file. private Interop.Sys.FileStatus _fileStatus; /// true if represents a symlink and the target of that symlink is a directory. @@ -24,17 +26,12 @@ namespace System.IO /// private int _fileStatusInitialized = -1; - internal IFileSystemObject FileSystemObject - { - get { return this; } - } - internal void Invalidate() { _fileStatusInitialized = -1; } - FileAttributes IFileSystemObject.Attributes + public FileAttributes Attributes { get { @@ -179,7 +176,7 @@ namespace System.IO } } - bool IFileSystemObject.Exists + internal bool ExistsCore { get { @@ -194,7 +191,7 @@ namespace System.IO } } - DateTimeOffset IFileSystemObject.CreationTime + internal DateTimeOffset CreationTimeCore { get { @@ -202,49 +199,60 @@ namespace System.IO if (!_exists) return DateTimeOffset.FromFileTime(0); - long rawTime = (_fileStatus.Flags & Interop.Sys.FileStatusFlags.HasBirthTime) != 0 ? - _fileStatus.BirthTime : - Math.Min(_fileStatus.CTime, _fileStatus.MTime); // fall back to the oldest time we have in between change and modify time - return DateTimeOffset.FromUnixTimeSeconds(rawTime).ToLocalTime(); + if ((_fileStatus.Flags & Interop.Sys.FileStatusFlags.HasBirthTime) != 0) + return UnixTimeToDateTimeOffset(_fileStatus.BirthTime, _fileStatus.BirthTimeNsec); + + // fall back to the oldest time we have in between change and modify time + if (_fileStatus.MTime < _fileStatus.CTime || + (_fileStatus.MTime == _fileStatus.CTime && _fileStatus.MTimeNsec < _fileStatus.CTimeNsec)) + return UnixTimeToDateTimeOffset(_fileStatus.MTime, _fileStatus.MTimeNsec); + + return UnixTimeToDateTimeOffset(_fileStatus.CTime, _fileStatus.CTimeNsec); } set { // There isn't a reliable way to set this; however, we can't just do nothing since the - // FileSystemWatcher specifically looks for this call to make a Metatdata Change, so we + // FileSystemWatcher specifically looks for this call to make a Metadata Change, so we // should set the LastAccessTime of the file to cause the metadata change we need. LastAccessTime = LastAccessTime; } } - DateTimeOffset IFileSystemObject.LastAccessTime + internal DateTimeOffset LastAccessTimeCore { get { EnsureStatInitialized(); if (!_exists) return DateTimeOffset.FromFileTime(0); - return DateTimeOffset.FromUnixTimeSeconds(_fileStatus.ATime).ToLocalTime(); + return UnixTimeToDateTimeOffset(_fileStatus.ATime, _fileStatus.ATimeNsec); } set { SetAccessWriteTimes(value.ToUnixTimeSeconds(), null); } } - DateTimeOffset IFileSystemObject.LastWriteTime + internal DateTimeOffset LastWriteTimeCore { get { EnsureStatInitialized(); if (!_exists) return DateTimeOffset.FromFileTime(0); - return DateTimeOffset.FromUnixTimeSeconds(_fileStatus.MTime).ToLocalTime(); + return UnixTimeToDateTimeOffset(_fileStatus.MTime, _fileStatus.MTimeNsec); } set { SetAccessWriteTimes(null, value.ToUnixTimeSeconds()); } } + private DateTimeOffset UnixTimeToDateTimeOffset(long seconds, long nanoseconds) + { + return DateTimeOffset.FromUnixTimeSeconds(seconds).AddTicks(nanoseconds / NanosecondsPerTick).ToLocalTime(); + } + private void SetAccessWriteTimes(long? accessTime, long? writeTime) { _fileStatusInitialized = -1; // force a refresh so that we have an up-to-date times for values not being overwritten EnsureStatInitialized(); Interop.Sys.UTimBuf buf; + // we use utime() not utimensat() so we drop the subsecond part buf.AcTime = accessTime ?? _fileStatus.ATime; buf.ModTime = writeTime ?? _fileStatus.MTime; bool isDirectory = this is DirectoryInfo; @@ -252,7 +260,7 @@ namespace System.IO _fileStatusInitialized = -1; } - long IFileSystemObject.Length + internal long LengthCore { get { @@ -261,7 +269,7 @@ namespace System.IO } } - void IFileSystemObject.Refresh() + public void Refresh() { // This should not throw, instead we store the result so that we can throw it // when someone actually accesses a property. diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Win32.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Win32.cs index 276083b7d8..da72a66f64 100644 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Win32.cs +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Win32.cs @@ -6,11 +6,6 @@ namespace System.IO { partial class FileSystemInfo { - internal IFileSystemObject FileSystemObject - { - get { return this; } - } - internal void Invalidate() { _dataInitialized = -1; diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.WinRT.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.WinRT.cs deleted file mode 100644 index f089d980d4..0000000000 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.WinRT.cs +++ /dev/null @@ -1,22 +0,0 @@ -// 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. - -namespace System.IO -{ - partial class FileSystemInfo - { - private IFileSystemObject _fileSystemObject; - - internal IFileSystemObject FileSystemObject - { - get { return _fileSystemObject ?? (_fileSystemObject = MultiplexingWin32WinRTFileSystem.GetFileSystemObject(this, FullPath)); } - } - - internal void Invalidate() - { - _dataInitialized = -1; - _fileSystemObject = null; - } - } -} diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Windows.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Windows.cs index 301425c7df..41e08650dd 100644 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Windows.cs +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Windows.cs @@ -6,7 +6,7 @@ using System.Security; namespace System.IO { - partial class FileSystemInfo : IFileSystemObject + partial class FileSystemInfo { // Cache the file/directory information private Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA _data; @@ -17,29 +17,32 @@ namespace System.IO // throw an appropriate error when attempting to access the cached info. private int _dataInitialized = -1; - [SecurityCritical] - internal void Init(ref Interop.Kernel32.WIN32_FIND_DATA findData) + internal unsafe void Init(Interop.NtDll.FILE_FULL_DIR_INFORMATION* info) { - // Copy the information to data - _data.PopulateFrom(ref findData); + _data.dwFileAttributes = (int)info->FileAttributes; + _data.ftCreationTime = *((Interop.Kernel32.FILE_TIME*)&info->CreationTime); + _data.ftLastAccessTime = *((Interop.Kernel32.FILE_TIME*)&info->LastAccessTime); + _data.ftLastWriteTime = *((Interop.Kernel32.FILE_TIME*)&info->LastWriteTime); + _data.nFileSizeHigh = (uint)(info->EndOfFile >> 32); + _data.nFileSizeLow = (uint)info->EndOfFile; _dataInitialized = 0; } - FileAttributes IFileSystemObject.Attributes + public FileAttributes Attributes { get { EnsureDataInitialized(); - return (FileAttributes)_data.fileAttributes; + return (FileAttributes)_data.dwFileAttributes; } set { - FileSystem.Current.SetAttributes(FullPath, value); + FileSystem.SetAttributes(FullPath, value); _dataInitialized = -1; } } - bool IFileSystemObject.Exists + internal bool ExistsCore { get { @@ -52,61 +55,58 @@ namespace System.IO // but Exists is supposed to return true or false. return false; } - return (_data.fileAttributes != -1) && ((this is DirectoryInfo) == ((_data.fileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) == Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY)); + return (_data.dwFileAttributes != -1) && ((this is DirectoryInfo) == ((_data.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) == Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY)); } } - DateTimeOffset IFileSystemObject.CreationTime + internal DateTimeOffset CreationTimeCore { get { EnsureDataInitialized(); - long dt = ((long)(_data.ftCreationTimeHigh) << 32) | ((long)_data.ftCreationTimeLow); - return DateTimeOffset.FromFileTime(dt); + return _data.ftCreationTime.ToDateTimeOffset(); } set { - FileSystem.Current.SetCreationTime(FullPath, value, this is DirectoryInfo); + FileSystem.SetCreationTime(FullPath, value, this is DirectoryInfo); _dataInitialized = -1; } } - DateTimeOffset IFileSystemObject.LastAccessTime + internal DateTimeOffset LastAccessTimeCore { get { EnsureDataInitialized(); - long dt = ((long)(_data.ftLastAccessTimeHigh) << 32) | ((long)_data.ftLastAccessTimeLow); - return DateTimeOffset.FromFileTime(dt); + return _data.ftLastAccessTime.ToDateTimeOffset(); } set { - FileSystem.Current.SetLastAccessTime(FullPath, value, (this is DirectoryInfo)); + FileSystem.SetLastAccessTime(FullPath, value, (this is DirectoryInfo)); _dataInitialized = -1; } } - DateTimeOffset IFileSystemObject.LastWriteTime + internal DateTimeOffset LastWriteTimeCore { get { EnsureDataInitialized(); - long dt = ((long)(_data.ftLastWriteTimeHigh) << 32) | ((long)_data.ftLastWriteTimeLow); - return DateTimeOffset.FromFileTime(dt); + return _data.ftLastWriteTime.ToDateTimeOffset(); } set { - FileSystem.Current.SetLastWriteTime(FullPath, value, (this is DirectoryInfo)); + FileSystem.SetLastWriteTime(FullPath, value, (this is DirectoryInfo)); _dataInitialized = -1; } } - long IFileSystemObject.Length + internal long LengthCore { get { EnsureDataInitialized(); - return ((long)_data.fileSizeHigh) << 32 | _data.fileSizeLow & 0xFFFFFFFFL; + return ((long)_data.nFileSizeHigh) << 32 | _data.nFileSizeLow & 0xFFFFFFFFL; } } @@ -122,11 +122,11 @@ namespace System.IO throw Win32Marshal.GetExceptionForWin32Error(_dataInitialized, FullPath); } - void IFileSystemObject.Refresh() + public void Refresh() { // This should not throw, instead we store the result so that we can throw it // when someone actually accesses a property - _dataInitialized = Win32FileSystem.FillAttributeInfo(FullPath, ref _data, returnErrorOnNotFound: false); + _dataInitialized = FileSystem.FillAttributeInfo(FullPath, ref _data, returnErrorOnNotFound: false); } } } diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.cs index 268eb289d9..5d2e18e0df 100644 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.cs +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.cs @@ -20,7 +20,6 @@ namespace System.IO protected string OriginalPath; // path passed in by the user private string _displayPath = ""; // path that can be displayed to the user - [System.Security.SecurityCritical] protected FileSystemInfo() { } @@ -38,7 +37,6 @@ namespace System.IO // Full path of the directory/file public virtual string FullName { - [System.Security.SecuritySafeCritical] get { return FullPath; @@ -94,15 +92,14 @@ namespace System.IO public DateTime CreationTimeUtc { - [System.Security.SecuritySafeCritical] get { - return FileSystemObject.CreationTime.UtcDateTime; + return CreationTimeCore.UtcDateTime; } set { - FileSystemObject.CreationTime = File.GetUtcDateTimeOffset(value); + CreationTimeCore = File.GetUtcDateTimeOffset(value); } } @@ -122,15 +119,14 @@ namespace System.IO public DateTime LastAccessTimeUtc { - [System.Security.SecuritySafeCritical] get { - return FileSystemObject.LastAccessTime.UtcDateTime; + return LastAccessTimeCore.UtcDateTime; } set { - FileSystemObject.LastAccessTime = File.GetUtcDateTimeOffset(value); + LastAccessTimeCore = File.GetUtcDateTimeOffset(value); } } @@ -150,34 +146,14 @@ namespace System.IO public DateTime LastWriteTimeUtc { - [System.Security.SecuritySafeCritical] get { - return FileSystemObject.LastWriteTime.UtcDateTime; + return LastWriteTimeCore.UtcDateTime; } set { - FileSystemObject.LastWriteTime = File.GetUtcDateTimeOffset(value); - } - } - - public void Refresh() - { - FileSystemObject.Refresh(); - } - - public FileAttributes Attributes - { - [System.Security.SecuritySafeCritical] - get - { - return FileSystemObject.Attributes; - } - [System.Security.SecurityCritical] // auto-generated - set - { - FileSystemObject.Attributes = value; + LastWriteTimeCore = File.GetUtcDateTimeOffset(value); } } diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/FindEnumerable.Win32.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/FindEnumerable.Win32.cs new file mode 100644 index 0000000000..a978d7cb85 --- /dev/null +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/FindEnumerable.Win32.cs @@ -0,0 +1,43 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; + +namespace System.IO +{ + internal partial class FindEnumerable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe bool GetData() + { + Debug.Assert(_directoryHandle != (IntPtr)(-1) && _directoryHandle != IntPtr.Zero && !_lastEntryFound); + + int status = Interop.NtDll.NtQueryDirectoryFile( + FileHandle: _directoryHandle, + Event: IntPtr.Zero, + ApcRoutine: IntPtr.Zero, + ApcContext: IntPtr.Zero, + IoStatusBlock: out Interop.NtDll.IO_STATUS_BLOCK statusBlock, + FileInformation: _buffer, + Length: (uint)_buffer.Length, + FileInformationClass: Interop.NtDll.FILE_INFORMATION_CLASS.FileFullDirectoryInformation, + ReturnSingleEntry: Interop.BOOLEAN.FALSE, + FileName: null, + RestartScan: Interop.BOOLEAN.FALSE); + + switch ((uint)status) + { + case Interop.StatusOptions.STATUS_NO_MORE_FILES: + DirectoryFinished(); + return false; + case Interop.StatusOptions.STATUS_SUCCESS: + Debug.Assert(statusBlock.Information.ToInt64() != 0); + return true; + default: + throw Win32Marshal.GetExceptionForWin32Error((int)Interop.NtDll.RtlNtStatusToDosError(status), _currentPath); + } + } + } +} diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/FindEnumerable.WinRT.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/FindEnumerable.WinRT.cs new file mode 100644 index 0000000000..61e8b05d4d --- /dev/null +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/FindEnumerable.WinRT.cs @@ -0,0 +1,35 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.IO +{ + internal partial class FindEnumerable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe bool GetData() + { + if (!Interop.Kernel32.GetFileInformationByHandleEx( + _directoryHandle, + Interop.Kernel32.FILE_INFO_BY_HANDLE_CLASS.FileFullDirectoryInfo, + _buffer, + (uint)_buffer.Length)) + { + int error = Marshal.GetLastWin32Error(); + switch (error) + { + case Interop.Errors.ERROR_NO_MORE_FILES: + DirectoryFinished(); + return false; + default: + throw Win32Marshal.GetExceptionForWin32Error(error, _currentPath); + } + } + + return true; + } + } +} diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/FindEnumerable.Windows.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/FindEnumerable.Windows.cs new file mode 100644 index 0000000000..7e64e97373 --- /dev/null +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/FindEnumerable.Windows.cs @@ -0,0 +1,251 @@ +// 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.Buffers; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; +using System.Threading; + +namespace System.IO +{ + internal unsafe partial class FindEnumerable : CriticalFinalizerObject, IEnumerable, IEnumerator + { + private readonly string _originalFullPath; + private readonly string _originalUserPath; + private readonly bool _recursive; + private readonly FindTransform _transform; + private readonly FindPredicate _predicate; + private readonly TState _state; + + private object _lock = new object(); + private int _enumeratorCreated; + + private Interop.NtDll.FILE_FULL_DIR_INFORMATION* _info; + private TResult _current; + + private byte[] _buffer; + private IntPtr _directoryHandle; + private string _currentPath; + private bool _lastEntryFound; + private Queue<(IntPtr Handle, string Path)> _pending; + private GCHandle _pinnedBuffer; + + /// + /// Encapsulates a find operation. + /// + /// The directory to search in. + public FindEnumerable( + string directory, + FindTransform transform, + FindPredicate predicate, + TState state = default, + bool recursive = false) + { + _originalUserPath = directory; + _originalFullPath = Path.GetFullPath(directory); + _recursive = recursive; + _predicate = predicate ?? throw new ArgumentNullException(nameof(predicate)); + _transform = transform ?? throw new ArgumentNullException(nameof(transform)); + _state = state; + Initialize(); + } + + private FindEnumerable( + string originalUserPath, + string originalFullPath, + FindTransform transform, + FindPredicate predicate, + TState state, + bool recursive) + { + _originalUserPath = originalUserPath; + _originalFullPath = originalFullPath; + _predicate = predicate; + _transform = transform; + _state = state; + _recursive = recursive; + Initialize(); + } + + /// + /// Simple wrapper to allow creating a file handle for an existing directory. + /// + public static IntPtr CreateDirectoryHandle(string path) + { + IntPtr handle = Interop.Kernel32.CreateFile_IntPtr( + path, + Interop.Kernel32.FileOperations.FILE_LIST_DIRECTORY, + FileShare.ReadWrite | FileShare.Delete, + FileMode.Open, + Interop.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS); + + if (handle == IntPtr.Zero || handle == (IntPtr)(-1)) + { + // Historically we throw directory not found rather than file not found + int error = Marshal.GetLastWin32Error(); + if (error == Interop.Errors.ERROR_FILE_NOT_FOUND) + error = Interop.Errors.ERROR_PATH_NOT_FOUND; + + throw Win32Marshal.GetExceptionForWin32Error(error, path); + } + + return handle; + } + + public IEnumerator GetEnumerator() + { + if (Interlocked.Exchange(ref _enumeratorCreated, 1) == 0) + { + return this; + } + else + { + return new FindEnumerable(_originalUserPath, _originalFullPath, _transform, _predicate, _state, _recursive); + } + } + + private void Initialize() + { + _currentPath = _originalFullPath; + _buffer = ArrayPool.Shared.Rent(4096); + _pinnedBuffer = GCHandle.Alloc(_buffer, GCHandleType.Pinned); + if (_recursive) + _pending = new Queue<(IntPtr, string)>(); + _directoryHandle = CreateDirectoryHandle(_originalFullPath); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public TResult Current => _current; + + object IEnumerator.Current => Current; + + public bool MoveNext() + { + if (_lastEntryFound) + return false; + + lock (_lock) + { + if (_lastEntryFound) + return false; + + RawFindData findData = default; + do + { + FindNextFile(); + if (!_lastEntryFound && _info != null) + { + // If needed, stash any subdirectories to process later + if (_recursive && (_info->FileAttributes & FileAttributes.Directory) != 0 + && !PathHelpers.IsDotOrDotDot(_info->FileName)) + { + string subDirectory = PathHelpers.CombineNoChecks(_currentPath, _info->FileName); + IntPtr subDirectoryHandle = CreateDirectoryHandle(subDirectory); + try + { + // It is possible this might allocate and run out of memory + _pending.Enqueue((subDirectoryHandle, subDirectory)); + } + catch + { + Interop.Kernel32.CloseHandle(subDirectoryHandle); + throw; + } + } + + findData = new RawFindData(_info, _currentPath, _originalFullPath, _originalUserPath); + } + } while (!_lastEntryFound && !_predicate(ref findData, _state)); + + if (!_lastEntryFound) + _current = _transform(ref findData); + + return !_lastEntryFound; + } + } + + private unsafe void FindNextFile() + { + Interop.NtDll.FILE_FULL_DIR_INFORMATION* info = _info; + if (info != null && info->NextEntryOffset != 0) + { + // We're already in a buffer and have another entry + _info = (Interop.NtDll.FILE_FULL_DIR_INFORMATION*)((byte*)info + info->NextEntryOffset); + return; + } + + // We need more data + if (GetData()) + _info = (Interop.NtDll.FILE_FULL_DIR_INFORMATION*)_pinnedBuffer.AddrOfPinnedObject(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DirectoryFinished() + { + _info = null; + if (_pending == null || _pending.Count == 0) + { + _lastEntryFound = true; + } + else + { + // Grab the next directory to parse + Interop.Kernel32.CloseHandle(_directoryHandle); + (_directoryHandle, _currentPath) = _pending.Dequeue(); + FindNextFile(); + } + } + + public void Reset() + { + throw new NotSupportedException(); + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + protected void Dispose(bool disposing) + { + // It is possible to fail to allocate the lock, but the finalizer will still run + if (_lock != null) + { + lock (_lock) + { + _lastEntryFound = true; + + // Don't ever close a valid handle twice as they can be reused- set to zero to ensure this + Interop.Kernel32.CloseHandle(_directoryHandle); + _directoryHandle = IntPtr.Zero; + + if (_recursive && _pending != null) + { + while (_pending.Count > 0) + Interop.Kernel32.CloseHandle(_pending.Dequeue().Handle); + _pending = null; + } + + if (_pinnedBuffer.IsAllocated) + _pinnedBuffer.Free(); + + if (_buffer != null) + ArrayPool.Shared.Return(_buffer); + + _buffer = null; + } + } + } + + ~FindEnumerable() + { + Dispose(disposing: false); + } + } +} diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/FindEnumerableFactory.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/FindEnumerableFactory.cs new file mode 100644 index 0000000000..1202ac7372 --- /dev/null +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/FindEnumerableFactory.cs @@ -0,0 +1,138 @@ +// 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. + +namespace System.IO +{ + internal static class FindEnumerableFactory + { + internal static void NormalizeInputs(ref string directory, ref string expression) + { + if (PathHelpers.IsPathRooted(expression)) + throw new ArgumentException(SR.Arg_Path2IsRooted, nameof(expression)); + + // We always allowed breaking the passed in directory and filter to be separated + // any way the user wanted. Looking for "C:\foo\*.cs" could be passed as "C:\" and + // "foo\*.cs" or "C:\foo" and "*.cs", for example. As such we need to combine and + // split the inputs if the expression contains a directory separator. + // + // We also allowed for expression to be "foo\" which would translate to "foo\*". + + ReadOnlySpan directoryName = PathHelpers.GetDirectoryNameNoChecks(expression.AsReadOnlySpan()); + + if (directoryName.Length != 0) + { + // Need to fix up the input paths + directory = PathHelpers.CombineNoChecks(directory, directoryName); + expression = expression.Substring(directoryName.Length + 1); + } + + // Historically we always treated "." as "*" + if (string.IsNullOrEmpty(expression) || expression == "." || expression == "*.*") + expression = "*"; + } + + internal static FindEnumerable UserFiles(string directory, + string expression = "*", + bool recursive = false) + { + return new FindEnumerable( + directory, + (ref RawFindData findData) => FindTransforms.AsUserFullPath(ref findData), + (ref RawFindData findData, string expr) => + { + return FindPredicates.NotDotOrDotDot(ref findData) + && !FindPredicates.IsDirectory(ref findData) + && DosMatcher.MatchPattern(expr, findData.FileName, ignoreCase: true); + }, + DosMatcher.TranslateExpression(expression), + recursive); + } + + internal static FindEnumerable UserDirectories(string directory, + string expression = "*", + bool recursive = false) + { + return new FindEnumerable( + directory, + (ref RawFindData findData) => FindTransforms.AsUserFullPath(ref findData), + (ref RawFindData findData, string expr) => + { + return FindPredicates.NotDotOrDotDot(ref findData) + && FindPredicates.IsDirectory(ref findData) + && DosMatcher.MatchPattern(expr, findData.FileName, ignoreCase: true); + }, + DosMatcher.TranslateExpression(expression), + recursive); + } + + internal static FindEnumerable UserEntries(string directory, + string expression = "*", + bool recursive = false) + { + return new FindEnumerable( + directory, + (ref RawFindData findData) => FindTransforms.AsUserFullPath(ref findData), + (ref RawFindData findData, string expr) => + { + return FindPredicates.NotDotOrDotDot(ref findData) + && DosMatcher.MatchPattern(expr, findData.FileName, ignoreCase: true); + }, + DosMatcher.TranslateExpression(expression), + recursive); + } + + internal static FindEnumerable FileInfos( + string directory, + string expression = "*", + bool recursive = false) + { + return new FindEnumerable( + directory, + (ref RawFindData findData) => FindTransforms.AsFileInfo(ref findData), + (ref RawFindData findData, string expr) => + { + return FindPredicates.NotDotOrDotDot(ref findData) + && !FindPredicates.IsDirectory(ref findData) + && DosMatcher.MatchPattern(expr, findData.FileName, ignoreCase: true); + }, + DosMatcher.TranslateExpression(expression), + recursive); + } + + internal static FindEnumerable DirectoryInfos( + string directory, + string expression = "*", + bool recursive = false) + { + return new FindEnumerable( + directory, + (ref RawFindData findData) => FindTransforms.AsDirectoryInfo(ref findData), + (ref RawFindData findData, string expr) => + { + return FindPredicates.NotDotOrDotDot(ref findData) + && FindPredicates.IsDirectory(ref findData) + && DosMatcher.MatchPattern(expr, findData.FileName, ignoreCase: true); + }, + DosMatcher.TranslateExpression(expression), + recursive); + } + + internal static FindEnumerable FileSystemInfos( + string directory, + string expression = "*", + bool recursive = false) + { + return new FindEnumerable( + directory, + (ref RawFindData findData) => FindTransforms.AsFileSystemInfo(ref findData), + (ref RawFindData findData, string expr) => + { + return FindPredicates.NotDotOrDotDot(ref findData) + && DosMatcher.MatchPattern(expr, findData.FileName, ignoreCase: true); + }, + DosMatcher.TranslateExpression(expression), + recursive); + } + } +} diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/FindPredicate.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/FindPredicate.cs new file mode 100644 index 0000000000..2e52eda3db --- /dev/null +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/FindPredicate.cs @@ -0,0 +1,11 @@ +// 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. + +namespace System.IO +{ + /// + /// Interface for filtering out find results. + /// + internal delegate bool FindPredicate(ref RawFindData findData, TState state); +} diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/FindPredicates.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/FindPredicates.cs new file mode 100644 index 0000000000..9d90aed377 --- /dev/null +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/FindPredicates.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.IO +{ + internal static partial class FindPredicates + { + internal static bool NotDotOrDotDot(ref RawFindData findData) => !PathHelpers.IsDotOrDotDot(findData.FileName); + + internal static bool IsDirectory(ref RawFindData findData) + { + FileAttributes attributes = findData.Attributes; + return attributes != (FileAttributes)(-1) + && (attributes & FileAttributes.Directory) != 0; + } + } +} diff --git a/external/corefx/src/System.Data.Odbc/src/Common/System/Data/Common/ExternDll.Windows.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/FindTransform.cs similarity index 52% rename from external/corefx/src/System.Data.Odbc/src/Common/System/Data/Common/ExternDll.Windows.cs rename to external/corefx/src/System.IO.FileSystem/src/System/IO/FindTransform.cs index 240989c1f2..c1b6b71972 100644 --- a/external/corefx/src/System.Data.Odbc/src/Common/System/Data/Common/ExternDll.Windows.cs +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/FindTransform.cs @@ -2,10 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace System.Data.Common +namespace System.IO { - internal static class ExternDll - { - public const string Odbc32 = "odbc32.dll"; - } -} \ No newline at end of file + /// + /// Delegate for transforming raw find data into a result. + /// + internal delegate T FindTransform(ref RawFindData findData); +} diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/FindTransforms.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/FindTransforms.cs new file mode 100644 index 0000000000..db0590a35e --- /dev/null +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/FindTransforms.cs @@ -0,0 +1,40 @@ +// 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. + +namespace System.IO +{ + internal static partial class FindTransforms + { + internal static DirectoryInfo AsDirectoryInfo(ref RawFindData findData) + { + string fileName = new string(findData.FileName); + return new DirectoryInfo(PathHelpers.CombineNoChecks(findData.Directory, fileName), fileName, ref findData); + } + + internal static FileInfo AsFileInfo(ref RawFindData findData) + { + string fileName = new string(findData.FileName); + return new FileInfo(PathHelpers.CombineNoChecks(findData.Directory, fileName), fileName, ref findData); + } + + internal static FileSystemInfo AsFileSystemInfo(ref RawFindData findData) + { + string fileName = new string(findData.FileName); + string fullPath = PathHelpers.CombineNoChecks(findData.Directory, fileName); + + return (findData.Attributes & FileAttributes.Directory) != 0 + ? (FileSystemInfo)new DirectoryInfo(fullPath, fileName, ref findData) + : (FileSystemInfo)new FileInfo(fullPath, fileName, ref findData); + } + + /// + /// Returns the full path for find results, based off of the initially provided path. + /// + internal static string AsUserFullPath(ref RawFindData findData) + { + ReadOnlySpan subdirectory = findData.Directory.AsReadOnlySpan().Slice(findData.OriginalDirectory.Length); + return PathHelpers.CombineNoChecks(findData.OriginalUserDirectory, subdirectory, findData.FileName); + } + } +} diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/IFileSystemObject.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/IFileSystemObject.cs deleted file mode 100644 index efa2d09f8f..0000000000 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/IFileSystemObject.cs +++ /dev/null @@ -1,18 +0,0 @@ -// 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. - -namespace System.IO -{ - internal interface IFileSystemObject - { - FileAttributes Attributes { get; set; } - DateTimeOffset CreationTime { get; set; } - bool Exists { get; } - DateTimeOffset LastAccessTime { get; set; } - DateTimeOffset LastWriteTime { get; set; } - long Length { get; } - - void Refresh(); - } -} diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/MultiplexingWin32WinRTFileSystem.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/MultiplexingWin32WinRTFileSystem.cs deleted file mode 100644 index 83ffc18458..0000000000 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/MultiplexingWin32WinRTFileSystem.cs +++ /dev/null @@ -1,234 +0,0 @@ -// 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 Microsoft.Win32.SafeHandles; -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace System.IO -{ - internal class MultiplexingWin32WinRTFileSystem : FileSystem - { - private readonly FileSystem _win32FileSystem = new Win32FileSystem(); - private readonly FileSystem _winRTFileSystem = new WinRTFileSystem(); - - internal static IFileSystemObject GetFileSystemObject(FileSystemInfo caller, string fullPath) - { - return ShouldUseWinRT(fullPath, isCreate: false) ? - (IFileSystemObject)new WinRTFileSystem.WinRTFileSystemObject(fullPath, asDirectory: caller is DirectoryInfo) : - (IFileSystemObject)caller; - } - - public override void CopyFile(string sourceFullPath, string destFullPath, bool overwrite) - { - Select(sourceFullPath, destFullPath).CopyFile(sourceFullPath, destFullPath, overwrite); - } - - public override void ReplaceFile(string sourceFullPath, string destFullPath, string destBackupFullPath, bool ignoreMetadataErrors) - { - Select(sourceFullPath, destFullPath, destBackupFullPath).ReplaceFile(sourceFullPath, destFullPath, destBackupFullPath, ignoreMetadataErrors); - } - - public override void CreateDirectory(string fullPath) - { - Select(fullPath, isCreate: true).CreateDirectory(fullPath); - } - - public override void DeleteFile(string fullPath) - { - Select(fullPath).DeleteFile(fullPath); - } - - public override bool DirectoryExists(string fullPath) - { - return Select(fullPath).DirectoryExists(fullPath); - } - - public override Collections.Generic.IEnumerable EnumeratePaths(string fullPath, string searchPattern, SearchOption searchOption, SearchTarget searchTarget) - { - return Select(fullPath).EnumeratePaths(fullPath, searchPattern, searchOption, searchTarget); - } - - public override Collections.Generic.IEnumerable EnumerateFileSystemInfos(string fullPath, string searchPattern, SearchOption searchOption, SearchTarget searchTarget) - { - return Select(fullPath).EnumerateFileSystemInfos(fullPath, searchPattern, searchOption, searchTarget); - } - - public override bool FileExists(string fullPath) - { - return Select(fullPath).FileExists(fullPath); - } - - public override FileAttributes GetAttributes(string fullPath) - { - return Select(fullPath).GetAttributes(fullPath); - } - - public override DateTimeOffset GetCreationTime(string fullPath) - { - return Select(fullPath).GetCreationTime(fullPath); - } - - public override string GetCurrentDirectory() - { - // WinRT honors the Win32 current directory, but does not expose it, - // so we use the Win32 implementation always. - return _win32FileSystem.GetCurrentDirectory(); - } - - public override IFileSystemObject GetFileSystemInfo(string fullPath, bool asDirectory) - { - return Select(fullPath).GetFileSystemInfo(fullPath, asDirectory); - } - - public override DateTimeOffset GetLastAccessTime(string fullPath) - { - return Select(fullPath).GetLastAccessTime(fullPath); - } - - public override DateTimeOffset GetLastWriteTime(string fullPath) - { - return Select(fullPath).GetLastWriteTime(fullPath); - } - - public override void MoveDirectory(string sourceFullPath, string destFullPath) - { - Select(sourceFullPath, destFullPath).MoveDirectory(sourceFullPath, destFullPath); - } - - public override void MoveFile(string sourceFullPath, string destFullPath) - { - Select(sourceFullPath, destFullPath).MoveFile(sourceFullPath, destFullPath); - } - - public override void RemoveDirectory(string fullPath, bool recursive) - { - Select(fullPath).RemoveDirectory(fullPath, recursive); - } - - public override void SetAttributes(string fullPath, FileAttributes attributes) - { - Select(fullPath).SetAttributes(fullPath, attributes); - } - - public override void SetCreationTime(string fullPath, DateTimeOffset time, bool asDirectory) - { - Select(fullPath).SetCreationTime(fullPath, time, asDirectory); - } - - public override void SetCurrentDirectory(string fullPath) - { - // WinRT honors the Win32 current directory, but does not expose it, - // so we use the Win32 implementation always. - // This will throw UnauthorizedAccess on brokered paths. - _win32FileSystem.SetCurrentDirectory(fullPath); - } - - public override void SetLastAccessTime(string fullPath, DateTimeOffset time, bool asDirectory) - { - Select(fullPath).SetLastAccessTime(fullPath, time, asDirectory); - } - - public override void SetLastWriteTime(string fullPath, DateTimeOffset time, bool asDirectory) - { - Select(fullPath).SetLastWriteTime(fullPath, time, asDirectory); - } - - private FileSystem Select(string fullPath, bool isCreate = false) - { - return ShouldUseWinRT(fullPath, isCreate) ? _winRTFileSystem : _win32FileSystem; - } - - private FileSystem Select(string sourceFullPath, string destFullPath) - { - return (ShouldUseWinRT(sourceFullPath, isCreate: false) || ShouldUseWinRT(destFullPath, isCreate: true)) ? _winRTFileSystem : _win32FileSystem; - } - - private FileSystem Select(string sourceFullPath, string destFullPath, string destFullBackupPath) - { - return - (ShouldUseWinRT(sourceFullPath, isCreate: false) || ShouldUseWinRT(destFullPath, isCreate: true) || ShouldUseWinRT(destFullBackupPath, isCreate: true)) ? - _winRTFileSystem : - _win32FileSystem; - } - - public override string[] GetLogicalDrives() - { - // This API is always blocked on WinRT, don't use Win32 - return _winRTFileSystem.GetLogicalDrives(); - } - - private static bool ShouldUseWinRT(string fullPath, bool isCreate) - { - // The purpose of this method is to determine if we can access a path - // via Win32 or if we need to fallback to WinRT. - // We prefer Win32 since it is faster, WinRT's APIs eventually just - // call into Win32 after all, but it doesn't provide access to, - // brokered paths (like Pictures or Documents) nor does it handle - // placeholder files. So we'd like to fall back to WinRT whenever - // we can't access a path, or if it known to be a placeholder file. - - bool useWinRt = false; - - do - { - // first use GetFileAttributesEx as it is faster than FindFirstFile and requires minimum permissions - Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); - if (Interop.Kernel32.GetFileAttributesEx(fullPath, Interop.Kernel32.GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, ref data)) - { - // got the attributes - if ((data.fileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) != 0 || - (data.fileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_REPARSE_POINT) == 0) - { - // we have a directory or a file that is not a reparse point - // useWinRt = false; - break; - } - else - { - // we need to get the find data to determine if it is a placeholder file - Interop.Kernel32.WIN32_FIND_DATA findData = new Interop.Kernel32.WIN32_FIND_DATA(); - using (SafeFindHandle handle = Interop.Kernel32.FindFirstFile(fullPath, ref findData)) - { - if (!handle.IsInvalid) - { - // got the find data, use WinRT for placeholder files - - Debug.Assert((findData.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) == 0); - Debug.Assert((findData.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_REPARSE_POINT) != 0); - - useWinRt = findData.dwReserved0 == Interop.Kernel32.IOReparseOptions.IO_REPARSE_TAG_FILE_PLACEHOLDER; - break; - } - } - } - } - - int error = Marshal.GetLastWin32Error(); - Debug.Assert(error != Interop.Errors.ERROR_SUCCESS); - - if (error == Interop.Errors.ERROR_ACCESS_DENIED) - { - // The path was not accessible with Win32, so try WinRT - useWinRt = true; - break; - } - else if (error != Interop.Errors.ERROR_PATH_NOT_FOUND && error != Interop.Errors.ERROR_FILE_NOT_FOUND) - { - // We hit some error other than ACCESS_DENIED or NOT_FOUND, - // Default to Win32 to provide most accurate error behavior - break; - } - - // error was ERROR_PATH_NOT_FOUND or ERROR_FILE_NOT_FOUND - // if we are creating a file/directory we cannot assume that Win32 will have access to - // the parent directory, so we walk up the path. - fullPath = PathHelpers.GetDirectoryNameInternal(fullPath); - // only walk up the path if we are creating a file/directory and not at the root - } while (isCreate && !string.IsNullOrEmpty(fullPath)); - - return useWinRt; - } - } -} diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/PathHelpers.Unix.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/PathHelpers.Unix.cs index fb83930861..47dcd9b46d 100644 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/PathHelpers.Unix.cs +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/PathHelpers.Unix.cs @@ -13,21 +13,6 @@ namespace System.IO return false; } - internal static void CheckSearchPattern(string searchPattern) - { - // ".." should not be used to move up directories. On Windows, this is more strict, and ".." - // can only be used in particular places in a name, whereas on Unix it can be used anywhere. - // So, throw if we find a ".." that's its own component in the path. - for (int index = 0; (index = searchPattern.IndexOf("..", index, StringComparison.Ordinal)) >= 0; index += 2) - { - if ((index == 0 || PathInternal.IsDirectorySeparator(searchPattern[index - 1])) && // previous character is directory separator - (index + 2 == searchPattern.Length || PathInternal.IsDirectorySeparator(searchPattern[index + 2]))) // next character is directory separator - { - throw new ArgumentException(SR.Arg_InvalidSearchPattern, nameof(searchPattern)); - } - } - } - internal static string TrimEndingDirectorySeparator(string path) => path.Length > 1 && PathInternal.IsDirectorySeparator(path[path.Length - 1]) ? // exclude root "/" path.Substring(0, path.Length - 1) : diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/PathHelpers.Windows.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/PathHelpers.Windows.cs index 06d3f21765..990527577b 100644 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/PathHelpers.Windows.cs +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/PathHelpers.Windows.cs @@ -2,80 +2,47 @@ // 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.Diagnostics; +using System.Runtime.InteropServices; namespace System.IO { + internal static partial class PathInternal + { + internal static unsafe int GetRootLength(ReadOnlySpan path) + { + fixed (char* p = &MemoryMarshal.GetReference(path)) + { + return (int)GetRootLength(p, (uint)path.Length); + } + } + } + internal static partial class PathHelpers { - // Trim trailing whitespace, tabs etc but don't be aggressive in removing everything that has UnicodeCategory of trailing space. - // string.WhitespaceChars will trim more aggressively than what the underlying FS does (for ex, NTFS, FAT). - internal static readonly char[] TrimEndChars = { (char)0x9, (char)0xA, (char)0xB, (char)0xC, (char)0xD, (char)0x20, (char)0x85, (char)0xA0 }; - internal static readonly char[] TrimStartChars = { ' ' }; - internal static bool ShouldReviseDirectoryPathToCurrent(string path) { // In situations where this method is invoked, ":" should be special-cased // to instead go to the current directory. - return path.Length == 2 && path[1] == ':'; - } - - // ".." can only be used if it is specified as a part of a valid File/Directory name. We disallow - // the user being able to use it to move up directories. Here are some examples eg - // Valid: a..b abc..d - // Invalid: ..ab ab.. .. abc..d\abc.. - // - internal static void CheckSearchPattern(string searchPattern) - { - for (int index = 0; (index = searchPattern.IndexOf("..", index, StringComparison.Ordinal)) != -1; index += 2) - { - // Terminal ".." or "..\". File and directory names cannot end in "..". - if (index + 2 == searchPattern.Length || - PathInternal.IsDirectorySeparator(searchPattern[index + 2])) - { - throw new ArgumentException(SR.Arg_InvalidSearchPattern, nameof(searchPattern)); - } - } - } - - internal static string NormalizeSearchPattern(string searchPattern) - { - Debug.Assert(searchPattern != null); - - // Win32 normalization trims only U+0020. - string tempSearchPattern = searchPattern.TrimEnd(PathHelpers.TrimEndChars); - - // Make this corner case more useful, like dir - if (tempSearchPattern.Equals(".")) - { - tempSearchPattern = "*"; - } - - CheckSearchPattern(tempSearchPattern); - return tempSearchPattern; - } - - internal static string GetFullSearchString(string fullPath, string searchPattern) - { - Debug.Assert(fullPath != null); - Debug.Assert(searchPattern != null); - - ThrowIfEmptyOrRootedPath(searchPattern); - string tempStr = Path.Combine(fullPath, searchPattern); - - // If path ends in a trailing slash (\), append a * or we'll get a "Cannot find the file specified" exception - char lastChar = tempStr[tempStr.Length - 1]; - if (PathInternal.IsDirectorySeparator(lastChar) || lastChar == Path.VolumeSeparatorChar) - { - tempStr = tempStr + "*"; - } - - return tempStr; + return path != null && path.Length == 2 && path[1] == ':'; } internal static string TrimEndingDirectorySeparator(string path) => EndsInDirectorySeparator(path) ? path.Substring(0, path.Length - 1) : path; + + public static bool IsPathRooted(string path) + { + // Want to avoid PathInternal.CheckInvalidPathChars on Path.IsPathRooted + + if (path != null) + { + int length = path.Length; + if ((length >= 1 && PathInternal.IsDirectorySeparator(path[0])) || + (length >= 2 && PathInternal.IsValidDriveChar(path[0]) && path[1] == Path.VolumeSeparatorChar)) + return true; + } + return false; + } } } diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/PathHelpers.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/PathHelpers.cs index b6d966731e..dc574c7833 100644 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/PathHelpers.cs +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/PathHelpers.cs @@ -2,6 +2,10 @@ // 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.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + namespace System.IO { // Helper methods related to paths. Some of these are copies of @@ -38,5 +42,151 @@ namespace System.IO { return path.Length > 0 && PathInternal.IsDirectorySeparator(path[path.Length - 1]); } + + /// + /// Combines two paths. Does no validation of paths, only concatenates the paths + /// and places a directory separator between them if needed. + /// + internal static string CombineNoChecks(string first, ReadOnlySpan second) + { + if (string.IsNullOrEmpty(first)) + return second.Length == 0 + ? string.Empty + : new string(second); + + if (second.Length == 0) + return first; + + return CombineNoChecksInternal(first.AsReadOnlySpan(), second); + } + + /// + /// Combines two paths. Does no validation of paths, only concatenates the paths + /// and places a directory separator between them if needed. + /// + internal static string CombineNoChecks(ReadOnlySpan first, ReadOnlySpan second) + { + if (first.Length == 0) + return second.Length == 0 + ? string.Empty + : new string(second); + + if (second.Length == 0) + return new string(first); + + return CombineNoChecksInternal(first, second); + } + + /// + /// Combines three paths. Does no validation of paths, only concatenates the paths + /// and places a directory separator between them if needed. + /// + internal static string CombineNoChecks(string first, ReadOnlySpan second, ReadOnlySpan third) + { + if (string.IsNullOrEmpty(first)) + return CombineNoChecks(second, third); + + if (second.Length == 0) + return CombineNoChecks(first, third); + + if (third.Length == 0) + return CombineNoChecks(first, second); + + return CombineNoChecksInternal(first.AsReadOnlySpan(), second, third); + } + + /// + /// Combines two paths. Does no validation of paths, only concatenates the paths + /// and places a directory separator between them if needed. + /// + internal unsafe static string CombineNoChecks(string first, string second) + { + if (string.IsNullOrEmpty(first)) + return string.IsNullOrEmpty(second) ? string.Empty : second; + + if (string.IsNullOrEmpty(second)) + return first; + + return CombineNoChecksInternal(first.AsReadOnlySpan(), second.AsReadOnlySpan()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe static string CombineNoChecksInternal(ReadOnlySpan first, ReadOnlySpan second) + { + Debug.Assert(first.Length > 0 && second.Length > 0, "should have dealt with empty paths"); + + bool hasSeparator = PathInternal.IsDirectorySeparator(first[first.Length - 1]) + || PathInternal.IsDirectorySeparator(second[0]); + + fixed (char* f = &MemoryMarshal.GetReference(first), s = &MemoryMarshal.GetReference(second)) + { + return string.Create( + first.Length + second.Length + (hasSeparator ? 0 : 1), + (First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length, HasSeparator: hasSeparator), + (destination, state) => + { + new Span((char*)state.First, state.FirstLength).CopyTo(destination); + if (!state.HasSeparator) + destination[state.FirstLength] = Path.DirectorySeparatorChar; + new Span((char*)state.Second, state.SecondLength).CopyTo(destination.Slice(state.FirstLength + (state.HasSeparator ? 0 : 1))); + }); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe static string CombineNoChecksInternal(ReadOnlySpan first, ReadOnlySpan second, ReadOnlySpan third) + { + Debug.Assert(first.Length > 0 && second.Length > 0 && third.Length > 0, "should have dealt with empty paths"); + + bool firstHasSeparator = PathInternal.IsDirectorySeparator(first[first.Length - 1]) + || PathInternal.IsDirectorySeparator(second[0]); + bool thirdHasSeparator = PathInternal.IsDirectorySeparator(second[second.Length - 1]) + || PathInternal.IsDirectorySeparator(third[0]); + + fixed (char* f = &MemoryMarshal.GetReference(first), s = &MemoryMarshal.GetReference(second), t = &MemoryMarshal.GetReference(third)) + { + return string.Create( + first.Length + second.Length + third.Length + (firstHasSeparator ? 0 : 1) + (thirdHasSeparator ? 0 : 1), + (First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length, + Third: (IntPtr)t, ThirdLength: third.Length, FirstHasSeparator: firstHasSeparator, ThirdHasSeparator: thirdHasSeparator), + (destination, state) => + { + new Span((char*)state.First, state.FirstLength).CopyTo(destination); + if (!state.FirstHasSeparator) + destination[state.FirstLength] = Path.DirectorySeparatorChar; + new Span((char*)state.Second, state.SecondLength).CopyTo(destination.Slice(state.FirstLength + (state.FirstHasSeparator ? 0 : 1))); + if (!state.ThirdHasSeparator) + destination[destination.Length - state.ThirdLength - 1] = Path.DirectorySeparatorChar; + new Span((char*)state.Third, state.ThirdLength).CopyTo(destination.Slice(destination.Length - state.ThirdLength)); + }); + } + } + + /// + /// Returns true if the file name is "." or ".." + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe bool IsDotOrDotDot(ReadOnlySpan fileName) + { + return !(fileName.Length > 2 + || fileName[0] != '.' + || (fileName.Length == 2 && fileName[1] != '.')); + } + + public static ReadOnlySpan GetDirectoryNameNoChecks(ReadOnlySpan path) + { + if (path.Length == 0) + return ReadOnlySpan.Empty; + + int root = PathInternal.GetRootLength(path); + int i = path.Length; + if (i > root) + { + while (i > root && !PathInternal.IsDirectorySeparator(path[--i])) ; + return path.Slice(0, i); + } + + return ReadOnlySpan.Empty; + } } } diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/PathPair.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/PathPair.cs index 9a3bb4baa2..dfef1bcc11 100644 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/PathPair.cs +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/PathPair.cs @@ -4,7 +4,7 @@ namespace System.IO { - internal struct PathPair + internal readonly struct PathPair { internal readonly string UserPath; internal readonly string FullPath; diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/RawFindData.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/RawFindData.cs new file mode 100644 index 0000000000..2f8ab43e42 --- /dev/null +++ b/external/corefx/src/System.IO.FileSystem/src/System/IO/RawFindData.cs @@ -0,0 +1,33 @@ +// 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. + +namespace System.IO +{ + /// + /// Used for processing and filtering find results. + /// + internal unsafe ref struct RawFindData + { + internal RawFindData(Interop.NtDll.FILE_FULL_DIR_INFORMATION* info, string directory, string originalDirectory, string originalUserDirectory) + { + _info = info; + Directory = directory; + OriginalDirectory = originalDirectory; + OriginalUserDirectory = originalUserDirectory; + } + + internal unsafe Interop.NtDll.FILE_FULL_DIR_INFORMATION* _info; + public string Directory { get; private set; } + public string OriginalDirectory { get; private set; } + public string OriginalUserDirectory { get; private set; } + + public ReadOnlySpan FileName => _info->FileName; + public FileAttributes Attributes => _info->FileAttributes; + public long Length => _info->EndOfFile; + + public DateTime CreationTimeUtc => _info->CreationTime.ToDateTimeUtc(); + public DateTime LastAccessTimeUtc => _info->LastAccessTime.ToDateTimeUtc(); + public DateTime LastWriteTimeUtc => _info->LastWriteTime.ToDateTimeUtc(); + } +} diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/Win32FileSystemEnumerable.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/Win32FileSystemEnumerable.cs deleted file mode 100644 index ea46672197..0000000000 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/Win32FileSystemEnumerable.cs +++ /dev/null @@ -1,654 +0,0 @@ -// 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 Microsoft.Win32.SafeHandles; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Security; - -namespace System.IO -{ - // Overview: - // The key methods instantiate Win32FileSystemEnumerableIterators. These compose the iterator with search result - // handlers that instantiate the FileInfo, DirectoryInfo, string, etc. The handlers then perform any - // additional required permission demands. - internal static class Win32FileSystemEnumerableFactory - { - internal static IEnumerable CreateFileNameIterator(string path, string originalUserPath, string searchPattern, - bool includeFiles, bool includeDirs, SearchOption searchOption) - { - Debug.Assert(path != null); - Debug.Assert(originalUserPath != null); - Debug.Assert(searchPattern != null); - - SearchResultHandler handler; - - if (includeFiles && includeDirs) - { - handler = SearchResultHandler.FileSystemPath; - } - else if (includeFiles) - { - handler = SearchResultHandler.FilePath; - } - else - { - Debug.Assert(includeDirs, "Should never be excluding both files and directories."); - handler = SearchResultHandler.DirectoryPath; - } - - return new Win32FileSystemEnumerableIterator(path, originalUserPath, searchPattern, searchOption, handler); - } - - internal static IEnumerable CreateFileInfoIterator(string path, string originalUserPath, string searchPattern, SearchOption searchOption) - { - Debug.Assert(path != null); - Debug.Assert(originalUserPath != null); - Debug.Assert(searchPattern != null); - - return new Win32FileSystemEnumerableIterator(path, originalUserPath, searchPattern, searchOption, SearchResultHandler.FileInfo); - } - - internal static IEnumerable CreateDirectoryInfoIterator(string path, string originalUserPath, string searchPattern, SearchOption searchOption) - { - Debug.Assert(path != null); - Debug.Assert(originalUserPath != null); - Debug.Assert(searchPattern != null); - - return new Win32FileSystemEnumerableIterator(path, originalUserPath, searchPattern, searchOption, SearchResultHandler.DirectoryInfo); - } - - internal static IEnumerable CreateFileSystemInfoIterator(string path, string originalUserPath, string searchPattern, SearchOption searchOption) - { - Debug.Assert(path != null); - Debug.Assert(originalUserPath != null); - Debug.Assert(searchPattern != null); - - return new Win32FileSystemEnumerableIterator(path, originalUserPath, searchPattern, searchOption, SearchResultHandler.FileSystemInfo); - } - } - - // Overview: - // Enumerates file system entries matching the search parameters. For recursive searches this - // searches through all the sub dirs and executes the search criteria against every dir. - // - // Generic implementation: - // Win32FileSystemEnumerableIterator is generic. When it gets a WIN32_FIND_DATA, it calls the - // result handler to create an instance of the generic type. - // - // Usage: - // Use Win32FileSystemEnumerableFactory to obtain FSEnumerables that can enumerate file system - // entries as string path names, FileInfos, DirectoryInfos, or FileSystemInfos. - // - // Security: - // For all the dirs/files returned, demands path discovery permission for their parent folders - internal class Win32FileSystemEnumerableIterator : Iterator - { - private const int STATE_INIT = 1; - private const int STATE_SEARCH_NEXT_DIR = 2; - private const int STATE_FIND_NEXT_FILE = 3; - private const int STATE_FINISH = 4; - - private readonly SearchResultHandler _resultHandler; - private List _searchList; - private PathPair _searchData; - private readonly string _searchCriteria; - [SecurityCritical] - private SafeFindHandle _hnd = null; - - // empty means we know in advance that we won?t find any search results, which can happen if: - // 1. we don't have a search pattern - // 2. we're enumerating only the top directory and found no matches during the first call - // This flag allows us to return early for these cases. We can?t know this in advance for - // SearchOption.AllDirectories because we do a ?*? search for subdirs and then use the - // searchPattern at each directory level. - private bool _empty; - - private readonly string _userPath; - private readonly SearchOption _searchOption; - private readonly string _fullPath; - private readonly string _normalizedSearchPath; - - [SecuritySafeCritical] - internal Win32FileSystemEnumerableIterator(string path, string originalUserPath, string searchPattern, SearchOption searchOption, SearchResultHandler resultHandler) - { - Debug.Assert(path != null); - Debug.Assert(originalUserPath != null); - Debug.Assert(searchPattern != null); - Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); - Debug.Assert(resultHandler != null); - - string normalizedSearchPattern = PathHelpers.NormalizeSearchPattern(searchPattern); - - if (normalizedSearchPattern.Length == 0) - { - _empty = true; - } - else - { - _resultHandler = resultHandler; - _searchOption = searchOption; - - _fullPath = Path.GetFullPath(path); - string fullSearchString = PathHelpers.GetFullSearchString(_fullPath, normalizedSearchPattern); - _normalizedSearchPath = Path.GetDirectoryName(fullSearchString); - - // normalize search criteria - _searchCriteria = GetNormalizedSearchCriteria(fullSearchString, _normalizedSearchPath); - - // fix up user path - string searchPatternDirName = Path.GetDirectoryName(normalizedSearchPattern); - _userPath = string.IsNullOrEmpty(searchPatternDirName) ? - originalUserPath : - Path.Combine(originalUserPath, searchPatternDirName); - - _searchData = new PathPair(_userPath, _normalizedSearchPath); - - CommonInit(); - } - } - - [SecurityCritical] - private void CommonInit() - { - Debug.Assert(_searchCriteria != null, "searchCriteria should be initialized"); - - // Execute searchCriteria against the current directory - PathHelpers.ThrowIfEmptyOrRootedPath(_searchCriteria); - string searchPath = Path.Combine(_searchData.FullPath, _searchCriteria); - - Interop.Kernel32.WIN32_FIND_DATA data = new Interop.Kernel32.WIN32_FIND_DATA(); - - using (DisableMediaInsertionPrompt.Create()) - { - // Open a Find handle - _hnd = Interop.Kernel32.FindFirstFile(searchPath, ref data); - - if (_hnd.IsInvalid) - { - int errorCode = Marshal.GetLastWin32Error(); - if (errorCode != Interop.Errors.ERROR_FILE_NOT_FOUND && errorCode != Interop.Errors.ERROR_NO_MORE_FILES) - { - throw HandleError(errorCode, _searchData.FullPath); - } - else - { - // flag this as empty only if we're searching just top directory - // Used in fast path for top directory only - _empty = _searchOption == SearchOption.TopDirectoryOnly; - } - } - } - - if (_searchOption == SearchOption.TopDirectoryOnly) - { - // fast path for TopDirectoryOnly. If we have a result, go ahead and set it to - // current. If empty, dispose handle. - if (_empty) - { - _hnd.Dispose(); - } - else - { - TSource result; - if (IsResultIncluded(ref data, out result)) - { - current = result; - } - } - } - else - { - // for AllDirectories, we first recurse into dirs, so cleanup and add searchData - // to the list - _hnd.Dispose(); - _searchList = new List(); - _searchList.Add(_searchData); - } - } - - [SecuritySafeCritical] - private Win32FileSystemEnumerableIterator(string fullPath, string normalizedSearchPath, string searchCriteria, string userPath, SearchOption searchOption, SearchResultHandler resultHandler) - { - _fullPath = fullPath; - _normalizedSearchPath = normalizedSearchPath; - _searchCriteria = searchCriteria; - _resultHandler = resultHandler; - _userPath = userPath; - _searchOption = searchOption; - - if (searchCriteria != null) - { - PathInternal.CheckInvalidPathChars(fullPath); - if (PathInternal.HasWildCardCharacters(fullPath)) - throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(fullPath)); - - _searchData = new PathPair(userPath, normalizedSearchPath); - CommonInit(); - } - else - { - _empty = true; - } - } - - protected override Iterator Clone() - { - return new Win32FileSystemEnumerableIterator(_fullPath, _normalizedSearchPath, _searchCriteria, _userPath, _searchOption, _resultHandler); - } - - [SecuritySafeCritical] - protected override void Dispose(bool disposing) - { - try - { - _hnd?.Dispose(); - } - finally - { - base.Dispose(disposing); - } - } - - [SecuritySafeCritical] - public override bool MoveNext() - { - Interop.Kernel32.WIN32_FIND_DATA data = new Interop.Kernel32.WIN32_FIND_DATA(); - switch (state) - { - case STATE_INIT: - { - if (_empty) - { - state = STATE_FINISH; - goto case STATE_FINISH; - } - if (_searchOption == SearchOption.TopDirectoryOnly) - { - state = STATE_FIND_NEXT_FILE; - if (current != null) - { - return true; - } - else - { - goto case STATE_FIND_NEXT_FILE; - } - } - else - { - state = STATE_SEARCH_NEXT_DIR; - goto case STATE_SEARCH_NEXT_DIR; - } - } - case STATE_SEARCH_NEXT_DIR: - { - Debug.Assert(_searchOption != SearchOption.TopDirectoryOnly, "should not reach this code path if searchOption == TopDirectoryOnly"); - Debug.Assert(_searchList != null, "_searchList should not be null"); - - // Traverse directory structure. We need to get '*' - while (_searchList.Count > 0) - { - int index = _searchList.Count - 1; - _searchData = _searchList[index]; - Debug.Assert((_searchData.FullPath != null), "fullpath can't be null!"); - _searchList.RemoveAt(index); - - // Traverse the subdirs - AddSearchableDirsToList(_searchData); - - // Execute searchCriteria against the current directory - string searchPath = Path.Combine(_searchData.FullPath, _searchCriteria); - - using (DisableMediaInsertionPrompt.Create()) - { - // Open a Find handle - _hnd = Interop.Kernel32.FindFirstFile(searchPath, ref data); - - if (_hnd.IsInvalid) - { - int errorCode = Marshal.GetLastWin32Error(); - switch (errorCode) - { - case Interop.Errors.ERROR_FILE_NOT_FOUND: - case Interop.Errors.ERROR_NO_MORE_FILES: - case Interop.Errors.ERROR_PATH_NOT_FOUND: - continue; - } - - _hnd.Dispose(); - throw HandleError(errorCode, _searchData.FullPath); - } - } - - state = STATE_FIND_NEXT_FILE; - - TSource result; - if (IsResultIncluded(ref data, out result)) - { - current = result; - return true; - } - else - { - goto case STATE_FIND_NEXT_FILE; - } - } - state = STATE_FINISH; - goto case STATE_FINISH; - } - case STATE_FIND_NEXT_FILE: - { - if (_hnd != null) - { - using (DisableMediaInsertionPrompt.Create()) - { - // Keep asking for more matching files/dirs, add it to the list - while (Interop.Kernel32.FindNextFile(_hnd, ref data)) - { - TSource result; - if (IsResultIncluded(ref data, out result)) - { - current = result; - return true; - } - } - } - - // Make sure we quit with a sensible error. - int errorCode = Marshal.GetLastWin32Error(); - _hnd?.Dispose(); - - switch (errorCode) - { - case Interop.Errors.ERROR_SUCCESS: - case Interop.Errors.ERROR_NO_MORE_FILES: - - // ERROR_FILE_NOT_FOUND is valid here because if the top level - // dir doesn't contain any subdirs and matching files then - // we will get here with this errorcode from the _searchList walk - case Interop.Errors.ERROR_FILE_NOT_FOUND: - break; - default: - throw HandleError(errorCode, _searchData.FullPath); - } - } - - if (_searchOption == SearchOption.TopDirectoryOnly) - { - state = STATE_FINISH; - goto case STATE_FINISH; - } - else - { - state = STATE_SEARCH_NEXT_DIR; - goto case STATE_SEARCH_NEXT_DIR; - } - } - case STATE_FINISH: - { - Dispose(); - break; - } - } - return false; - } - - [SecurityCritical] - private bool IsResultIncluded(ref Interop.Kernel32.WIN32_FIND_DATA findData, out TSource result) - { - Debug.Assert(findData.cFileName.Length != 0 && !Path.IsPathRooted(findData.cFileName.GetStringFromFixedBuffer()), - "Expected file system enumeration to not have empty file/directory name and not have rooted name"); - - return _resultHandler.IsResultIncluded(_searchData.FullPath, _searchData.UserPath, ref findData, out result); - } - - [SecurityCritical] - private Exception HandleError(int errorCode, string path) - { - Dispose(); - return Win32Marshal.GetExceptionForWin32Error(errorCode, path); - } - - [SecurityCritical] // auto-generated - private void AddSearchableDirsToList(PathPair localSearchData) - { - string searchPath = Path.Combine(localSearchData.FullPath, "*"); - SafeFindHandle hnd = null; - Interop.Kernel32.WIN32_FIND_DATA data = new Interop.Kernel32.WIN32_FIND_DATA(); - try - { - using (DisableMediaInsertionPrompt.Create()) - { - // Get all files and dirs - hnd = Interop.Kernel32.FindFirstFile(searchPath, ref data); - - if (hnd.IsInvalid) - { - int errorCode = Marshal.GetLastWin32Error(); - - // This could happen if the dir doesn't contain any files. - // Continue with the recursive search though, eventually - // _searchList will become empty - switch (errorCode) - { - case Interop.Errors.ERROR_FILE_NOT_FOUND: - case Interop.Errors.ERROR_NO_MORE_FILES: - case Interop.Errors.ERROR_PATH_NOT_FOUND: - return; - default: - throw HandleError(errorCode, localSearchData.FullPath); - } - } - } - - // Add subdirs to _searchList. Exempt ReparsePoints as appropriate - Debug.Assert(_searchList != null, "_searchList should not be null"); - int initialCount = _searchList.Count; - do - { - if (Win32FileSystemEnumerableHelpers.IsDir(ref data)) - { - string fileName = data.cFileName.GetStringFromFixedBuffer(); - - Debug.Assert(fileName.Length != 0 && !Path.IsPathRooted(fileName), - "Expected file system enumeration to not have empty file/directory name and not have rooted name"); - - string tempFullPath = Path.Combine(localSearchData.FullPath, fileName); - string tempUserPath = Path.Combine(localSearchData.UserPath, fileName); - - // Setup search data for the sub directory and push it into the list - PathPair searchDataSubDir = new PathPair(tempUserPath, tempFullPath); - - _searchList.Add(searchDataSubDir); - } - } while (Interop.Kernel32.FindNextFile(hnd, ref data)); - - // Reverse the items just added to maintain FIFO order - if (_searchList.Count > initialCount) - { - _searchList.Reverse(initialCount, _searchList.Count - initialCount); - } - - // We don't care about errors here - } - finally - { - hnd?.Dispose(); - } - } - - private static string GetNormalizedSearchCriteria(string fullSearchString, string fullPathMod) - { - Debug.Assert(fullSearchString != null); - Debug.Assert(fullPathMod != null); - Debug.Assert(fullSearchString.Length >= fullPathMod.Length); - - string searchCriteria = null; - char lastChar = fullPathMod[fullPathMod.Length - 1]; - if (PathInternal.IsDirectorySeparator(lastChar)) - { - // Can happen if the path is C:\temp, in which case GetDirectoryName would return C:\ - searchCriteria = fullSearchString.Substring(fullPathMod.Length); - } - else - { - Debug.Assert(fullSearchString.Length > fullPathMod.Length); - searchCriteria = fullSearchString.Substring(fullPathMod.Length + 1); - } - return searchCriteria; - } - } - - internal abstract class SearchResultHandler - { - /// - /// Returns true if the result should be included. If true, the parameter - /// is set to the created result object, otherwise it is set to null. - /// - [SecurityCritical] - internal abstract bool IsResultIncluded(string fullPath, string userPath, ref Interop.Kernel32.WIN32_FIND_DATA findData, out TSource result); - } - - internal static class SearchResultHandler - { - private static SearchResultHandler s_filePath; - private static SearchResultHandler s_directoryPath; - private static SearchResultHandler s_fileSystemPath; - private static SearchResultHandler s_fileInfo; - private static SearchResultHandler s_directoryInfo; - private static SearchResultHandler s_fileSystemInfo; - - internal static SearchResultHandler FilePath - { - get { return s_filePath ?? (s_filePath = new StringResultHandler(includeFiles: true, includeDirs: false)); } - } - - internal static SearchResultHandler DirectoryPath - { - get { return s_directoryPath ?? (s_directoryPath = new StringResultHandler(includeFiles: false, includeDirs: true)); } - } - - internal static SearchResultHandler FileSystemPath - { - get { return s_fileSystemPath ?? (s_fileSystemPath = new StringResultHandler(includeFiles: true, includeDirs: true)); } - } - - internal static SearchResultHandler FileInfo - { - get { return s_fileInfo ?? (s_fileInfo = new FileInfoResultHandler()); } - } - - internal static SearchResultHandler DirectoryInfo - { - get { return s_directoryInfo ?? (s_directoryInfo = new DirectoryInfoResultHandler()); } - } - - internal static SearchResultHandler FileSystemInfo - { - get { return s_fileSystemInfo ?? (s_fileSystemInfo = new FileSystemInfoResultHandler()); } - } - - private sealed class StringResultHandler : SearchResultHandler - { - private readonly bool _includeFiles; - private readonly bool _includeDirs; - - internal StringResultHandler(bool includeFiles, bool includeDirs) - { - _includeFiles = includeFiles; - _includeDirs = includeDirs; - } - - [SecurityCritical] - internal override bool IsResultIncluded(string fullPath, string userPath, ref Interop.Kernel32.WIN32_FIND_DATA findData, out string result) - { - if ((_includeFiles && Win32FileSystemEnumerableHelpers.IsFile(ref findData)) || - (_includeDirs && Win32FileSystemEnumerableHelpers.IsDir(ref findData))) - { - result = Path.Combine(userPath, findData.cFileName.GetStringFromFixedBuffer()); - return true; - } - - result = null; - return false; - } - } - - private sealed class FileInfoResultHandler : SearchResultHandler - { - [SecurityCritical] - internal override bool IsResultIncluded(string fullPath, string userPath, ref Interop.Kernel32.WIN32_FIND_DATA findData, out FileInfo result) - { - if (Win32FileSystemEnumerableHelpers.IsFile(ref findData)) - { - string fullPathFinal = Path.Combine(fullPath, findData.cFileName.GetStringFromFixedBuffer()); - result = new FileInfo(fullPathFinal, ref findData); - return true; - } - - result = null; - return false; - } - } - - private sealed class DirectoryInfoResultHandler : SearchResultHandler - { - [SecurityCritical] - internal override bool IsResultIncluded(string fullPath, string userPath, ref Interop.Kernel32.WIN32_FIND_DATA findData, out DirectoryInfo result) - { - if (Win32FileSystemEnumerableHelpers.IsDir(ref findData)) - { - string fullPathFinal = Path.Combine(fullPath, findData.cFileName.GetStringFromFixedBuffer()); - result = new DirectoryInfo(fullPathFinal, ref findData); - return true; - } - - result = null; - return false; - } - } - - private sealed class FileSystemInfoResultHandler : SearchResultHandler - { - [SecurityCritical] - internal override bool IsResultIncluded(string fullPath, string userPath, ref Interop.Kernel32.WIN32_FIND_DATA findData, out FileSystemInfo result) - { - if (Win32FileSystemEnumerableHelpers.IsFile(ref findData)) - { - string fullPathFinal = Path.Combine(fullPath, findData.cFileName.GetStringFromFixedBuffer()); - result = new FileInfo(fullPathFinal, ref findData); - return true; - } - else if (Win32FileSystemEnumerableHelpers.IsDir(ref findData)) - { - string fullPathFinal = Path.Combine(fullPath, findData.cFileName.GetStringFromFixedBuffer()); - result = new DirectoryInfo(fullPathFinal, ref findData); - return true; - } - - result = null; - return false; - } - } - } - - internal static class Win32FileSystemEnumerableHelpers - { - [SecurityCritical] // auto-generated - internal static bool IsDir(ref Interop.Kernel32.WIN32_FIND_DATA data) - { - // Don't add "." nor ".." - return (0 != (data.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY)) - && !data.cFileName.FixedBufferEqualsString(".") && !data.cFileName.FixedBufferEqualsString(".."); - } - - [SecurityCritical] // auto-generated - internal static bool IsFile(ref Interop.Kernel32.WIN32_FIND_DATA data) - { - return 0 == (data.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY); - } - } -} diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/WinRTFileSystem.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/WinRTFileSystem.cs deleted file mode 100644 index 9d838d605c..0000000000 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/WinRTFileSystem.cs +++ /dev/null @@ -1,196 +0,0 @@ -// 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.Collections.Generic; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.ExceptionServices; -using System.Threading.Tasks; -using Windows.Foundation; -using Windows.Storage; -using Windows.Storage.Search; -using Windows.Storage.FileProperties; -using Windows.UI.Core; -using WinRTFileAttributes = Windows.Storage.FileAttributes; - -namespace System.IO -{ - internal sealed partial class WinRTFileSystem : FileSystem - { - public override void CopyFile(string sourceFullPath, string destFullPath, bool overwrite) - { - EnsureBackgroundThread(); - SynchronousResultOf(CopyFileAsync(sourceFullPath, destFullPath, overwrite)); - } - - private async Task CopyFileAsync(string sourceFullPath, string destFullPath, bool overwrite) - { - StorageFile file = await StorageFile.GetFileFromPathAsync(sourceFullPath).TranslateWinRTTask(sourceFullPath); - - string destDirectory, destFileName; - PathHelpers.SplitDirectoryFile(destFullPath, out destDirectory, out destFileName); - - StorageFolder destFolder = await StorageFolder.GetFolderFromPathAsync(destDirectory).TranslateWinRTTask(destDirectory, isDirectory: true); - - await file.CopyAsync(destFolder, destFileName, overwrite ? NameCollisionOption.ReplaceExisting : NameCollisionOption.FailIfExists).TranslateWinRTTask(sourceFullPath); - } - - public override void ReplaceFile(string sourceFullPath, string destFullPath, string destBackupFullPath, bool ignoreMetadataErrors) - { - EnsureBackgroundThread(); - SynchronousResultOf(ReplaceFileAsync(sourceFullPath, destFullPath, destBackupFullPath, ignoreMetadataErrors)); - } - - private async Task ReplaceFileAsync(string sourceFullPath, string destFullPath, string destBackupFullPath, bool ignoreMetadataErrors) - { - // Copy the destination file to a backup. - if (destBackupFullPath != null) - { - await CopyFileAsync(destFullPath, destBackupFullPath, overwrite: true).ConfigureAwait(false); - } - - // Then copy the contents of the source file to the destination file. - await CopyFileAsync(sourceFullPath, destFullPath, overwrite: true).ConfigureAwait(false); - - // Finally, delete the source file. - await DeleteFileAsync(sourceFullPath).ConfigureAwait(false); - } - - public override string GetCurrentDirectory() - { - throw new PlatformNotSupportedException(); // https://github.com/dotnet/corefx/issues/17470; - } - - public override void MoveDirectory(string sourceFullPath, string destFullPath) - { - EnsureBackgroundThread(); - SynchronousResultOf(MoveDirectoryAsync(sourceFullPath, destFullPath)); - } - - private async Task MoveDirectoryAsync(string sourceFullPath, string destFullPath) - { - StorageFolder sourceFolder = await StorageFolder.GetFolderFromPathAsync(sourceFullPath).TranslateWinRTTask(sourceFullPath, isDirectory: true); - - // WinRT doesn't support move, only rename - // If parents are the same, just rename. - string sourceParent, sourceFolderName, destParent, destFolderName; - PathHelpers.SplitDirectoryFile(sourceFullPath, out sourceParent, out sourceFolderName); - PathHelpers.SplitDirectoryFile(destFullPath, out destParent, out destFolderName); - - // same parent folder - if (string.Equals(sourceParent, destParent, StringComparison.OrdinalIgnoreCase)) - { - // not the same subfolder - if (!string.Equals(sourceFolderName, destFolderName, StringComparison.OrdinalIgnoreCase)) - { - await sourceFolder.RenameAsync(destFolderName).TranslateWinRTTask(destFullPath, isDirectory: true); - } - // else : nothing to do - } - else - { - // Otherwise, create the destination and move each item recursively. - // We could do a copy, which would be safer in case of a failure - // We do a move because it has the perf characteristics that match the win32 move - StorageFolder destFolder = await CreateDirectoryAsync(destFullPath, failIfExists: true).ConfigureAwait(false); - - await MoveDirectoryAsync(sourceFolder, destFolder).ConfigureAwait(false); - } - } - - private async Task MoveDirectoryAsync(StorageFolder sourceFolder, StorageFolder destFolder) - { - foreach (var sourceFile in await sourceFolder.GetFilesAsync().TranslateWinRTTask(sourceFolder.Path, isDirectory: true)) - { - await sourceFile.MoveAsync(destFolder).TranslateWinRTTask(sourceFile.Path); - } - - foreach (var sourceSubFolder in await sourceFolder.GetFoldersAsync().TranslateWinRTTask(sourceFolder.Path, isDirectory: true)) - { - StorageFolder destSubFolder = await destFolder.CreateFolderAsync(sourceSubFolder.Name).TranslateWinRTTask(destFolder.Path, isDirectory: true); - - // Recursively move sub-directory - await MoveDirectoryAsync(sourceSubFolder, destSubFolder).ConfigureAwait(false); - } - - // sourceFolder should now be empty - await sourceFolder.DeleteAsync(StorageDeleteOption.PermanentDelete).TranslateWinRTTask(sourceFolder.Path, isDirectory: true); - } - - public override void MoveFile(string sourceFullPath, string destFullPath) - { - EnsureBackgroundThread(); - SynchronousResultOf(MoveFileAsync(sourceFullPath, destFullPath)); - } - - private async Task MoveFileAsync(string sourceFullPath, string destFullPath) - { - StorageFile file = await StorageFile.GetFileFromPathAsync(sourceFullPath).TranslateWinRTTask(sourceFullPath); - - string destDirectory, destFileName; - PathHelpers.SplitDirectoryFile(destFullPath, out destDirectory, out destFileName); - - // Win32 MoveFileEx will return success if source and destination are the same. - // Comparison is safe here as the caller has normalized both paths. - if (!sourceFullPath.Equals(destFullPath, StringComparison.OrdinalIgnoreCase)) - { - StorageFolder destFolder = await StorageFolder.GetFolderFromPathAsync(destDirectory).TranslateWinRTTask(destDirectory, isDirectory: true); - - await file.MoveAsync(destFolder, destFileName, NameCollisionOption.FailIfExists).TranslateWinRTTask(sourceFullPath); - } - } - - public override void SetCurrentDirectory(string fullPath) - { - throw new PlatformNotSupportedException(); // https://github.com/dotnet/corefx/issues/17470 - } - - public override string[] GetLogicalDrives() - { - return DriveInfoInternal.GetLogicalDrives(); - } - - #region Task Utility - private static void EnsureBackgroundThread() - { - // WinRT async operations on brokered files require posting a message back to the UI thread. - // If we were to run a sync method on the UI thread we'd deadlock. Throw instead. - - if (IsUIThread()) - throw new InvalidOperationException(SR.IO_SyncOpOnUIThread); - } - - private static bool IsUIThread() - { - CoreWindow window = CoreWindow.GetForCurrentThread(); - - return window != null && window.Dispatcher != null && window.Dispatcher.HasThreadAccess; - } - - private static void SynchronousResultOf(Task task) - { - WaitForTask(task); - } - - private static TResult SynchronousResultOf(Task task) - { - WaitForTask(task); - return task.Result; - } - - // this needs to be separate from SynchronousResultOf so that SynchronousResultOf can call it. - private static void WaitForTask(Task task) - { - // This should never be called from the UI thread since it can deadlock - // Throwing here, however, is too late since work has already been started - // Instead we assert here so that our tests can catch cases where we forgot to - // call EnsureBackgroundThread before starting the task. - Debug.Assert(!IsUIThread()); - - task.GetAwaiter().GetResult(); - } - #endregion Task Utility - } -} diff --git a/external/corefx/src/System.IO.FileSystem/src/System/IO/WinRTIOExtensions.cs b/external/corefx/src/System.IO.FileSystem/src/System/IO/WinRTIOExtensions.cs deleted file mode 100644 index e0f24e5d33..0000000000 --- a/external/corefx/src/System.IO.FileSystem/src/System/IO/WinRTIOExtensions.cs +++ /dev/null @@ -1,87 +0,0 @@ -// 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.Runtime.CompilerServices; -using System.Threading.Tasks; -using Windows.Foundation; - -namespace System.IO -{ - /// - /// These extensions are responsible for translating exceptions we get from WinRT to those - /// that match what folks expect from our IO APIs. - /// - internal static class WinRTIOExtensions - { - public static ConfiguredTaskAwaitable TranslateWinRTTask(this IAsyncOperation operation, string filePath, bool isDirectory = false) - { - return TranslateWinRTTaskCore(operation, filePath, isDirectory).ConfigureAwait(false); - } - - public static ConfiguredTaskAwaitable TranslateWinRTTask(this IAsyncAction operation, string filePath, bool isDirectory = false) - { - return TranslateWinRTTaskCore(operation, filePath, isDirectory).ConfigureAwait(false); - } - - private static async Task TranslateWinRTTaskCore(IAsyncOperation operation, string filePath, bool isDirectory) - { - try - { - return await operation.AsTask().ConfigureAwait(false); - } - catch (Exception exception) - { - throw TranslateWinRTException(exception, filePath, isDirectory); - } - } - - private static async Task TranslateWinRTTaskCore(IAsyncAction operation, string filePath, bool isDirectory) - { - try - { - await operation.AsTask().ConfigureAwait(false); - } - catch (Exception exception) - { - throw TranslateWinRTException(exception, filePath, isDirectory); - } - } - - public static Exception TranslateWinRTException(this Exception exception, string filePath, bool isDirectory = false) - { - int errorCode = Win32Marshal.TryMakeWin32ErrorCodeFromHR(exception.HResult); - - if (isDirectory) - { - // WinRT remaps all ERROR_PATH_NOT_FOUND to ERROR_FILE_NOT_FOUND - if (errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND) - errorCode = Interop.Errors.ERROR_PATH_NOT_FOUND; - } - else - { - // Existing comment from FileStream: - // NT5 oddity - when trying to open "C:\" as a FileStream, - // we usually get ERROR_PATH_NOT_FOUND from the OS. We should - // probably be consistent w/ every other directory. - // This remaps the error for non-existent drives which is incorrect - // but we need to preserve it for compatibility - if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && filePath.Equals(Directory.InternalGetDirectoryRoot(filePath))) - errorCode = Interop.Errors.ERROR_ACCESS_DENIED; - - // Known issue: WinRT pre-check's the find data of a fullPath before trying to create it, if the type doesn't match - // (IE: open file on a directory) it will return E_INVALIDARG instead of ERROR_ACCESS_DENIED - - // CreateFile returns ERROR_PATH_NOT_FOUND when given a fullPath that ends in a backslash. - // WinRT remaps all ERROR_PATH_NOT_FOUND to ERROR_FILE_NOT_FOUND - if (errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND && filePath.Length > 0 && filePath[filePath.Length - 1] == Path.DirectorySeparatorChar) - errorCode = Interop.Errors.ERROR_PATH_NOT_FOUND; - } - - // Known issue: We still can't handle ERROR_SHARING_VIOLATION because WinRT APIs are mapping this to ERROR_ACCESS_DENIED - - // Maps all unknown exceptions to IOException to be consistent with Win32 behavior - return Win32Marshal.GetExceptionForWin32Error(errorCode, filePath); - } - } -} diff --git a/external/corefx/src/System.IO.FileSystem/tests/Base/BaseGetSetTimes.cs b/external/corefx/src/System.IO.FileSystem/tests/Base/BaseGetSetTimes.cs index b94679e48c..f650ae07f3 100644 --- a/external/corefx/src/System.IO.FileSystem/tests/Base/BaseGetSetTimes.cs +++ b/external/corefx/src/System.IO.FileSystem/tests/Base/BaseGetSetTimes.cs @@ -67,6 +67,27 @@ namespace System.IO.Tests ValidateSetTimes(item, beforeTime, afterTime); } + [Fact] + [ActiveIssue(26349, TestPlatforms.AnyUnix)] + public void TimesIncludeMillisecondPart() + { + T item = GetExistingItem(); + Assert.All(TimeFunctions(), (function) => + { + var msec = 0; + for (int i = 0; i < 3; i++) + { + msec = function.Getter(item).Millisecond; + if (msec != 0) + break; + + item = GetExistingItem(); // try a new file/directory + } + + Assert.NotEqual(0, msec); + }); + } + protected void ValidateSetTimes(T item, DateTime beforeTime, DateTime afterTime) { Assert.All(TimeFunctions(), (function) => diff --git a/external/corefx/src/System.IO.FileSystem/tests/Directory/Delete.cs b/external/corefx/src/System.IO.FileSystem/tests/Directory/Delete.cs index b7b9cb7176..98ab80c3ef 100644 --- a/external/corefx/src/System.IO.FileSystem/tests/Directory/Delete.cs +++ b/external/corefx/src/System.IO.FileSystem/tests/Directory/Delete.cs @@ -208,7 +208,7 @@ namespace System.IO.Tests [Trait(XunitConstants.Category, XunitConstants.RequiresElevation)] public void Unix_NotFoundDirectory_ReadOnlyVolume() { - if (PlatformDetection.IsRedHat69) + if (PlatformDetection.IsRedHatFamily6) return; // [ActiveIssue(https://github.com/dotnet/corefx/issues/21920)] ReadOnly_FileSystemHelper(readOnlyDirectory => diff --git a/external/corefx/src/System.IO.FileSystem/tests/Directory/EnumerableTests.cs b/external/corefx/src/System.IO.FileSystem/tests/Directory/EnumerableTests.cs new file mode 100644 index 0000000000..e9c6028c56 --- /dev/null +++ b/external/corefx/src/System.IO.FileSystem/tests/Directory/EnumerableTests.cs @@ -0,0 +1,80 @@ +// 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.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace System.IO.Tests +{ + public class EnumerableTests : FileSystemTest + { + [Fact] + [ActiveIssue(25613, TestPlatforms.AnyUnix)] + public void FileEnumeratorIsThreadSafe() + { + string directory = Directory.CreateDirectory(GetTestFilePath()).FullName; + for (int i = 0; i < 100; i++) + File.Create(Path.Combine(directory, GetTestFileName())).Dispose(); + + // We are really only trying to make sure we don't terminate the process. + // Throwing IOException at this point to try and flush out other problems + // like bad handles. Can narrow the throw if this isn't reliable. + + try + { + new ThreadSafeRepro().Execute(directory); + } + catch (Exception e) when (!(e is IOException)) + { + } + } + + class ThreadSafeRepro + { + volatile IEnumerator _enumerator; + + void Enumerate(IEnumerator s) + { + while (s.MoveNext()) + { } + s.Dispose(); + } + + public void Execute(string directory) + { + CancellationTokenSource source = new CancellationTokenSource(); + CancellationToken token = source.Token; + + void Work() + { + do + { + IEnumerator x = _enumerator; + if (x != null) + Enumerate(x); + } while (!token.IsCancellationRequested); + } + + Task taskOne = Task.Run(action: Work); + Task taskTwo = Task.Run(action: Work); + + try + { + for (int i = 0; i < 1000; i++) + { + _enumerator = Directory.EnumerateFiles(directory).GetEnumerator(); + Enumerate(_enumerator); + } + } + finally + { + source.Cancel(); + Task.WaitAll(taskOne, taskTwo); + } + } + } + } +} diff --git a/external/corefx/src/System.IO.FileSystem/tests/Directory/GetFileSystemEntries_str_str.cs b/external/corefx/src/System.IO.FileSystem/tests/Directory/GetFileSystemEntries_str_str.cs index 408cff3424..a352bf6529 100644 --- a/external/corefx/src/System.IO.FileSystem/tests/Directory/GetFileSystemEntries_str_str.cs +++ b/external/corefx/src/System.IO.FileSystem/tests/Directory/GetFileSystemEntries_str_str.cs @@ -191,7 +191,7 @@ namespace System.IO.Tests [Fact] public void SearchPatternIgnoreSubDirectories() { - //Shouldn't get files on full path by default + // Shouldn't get files on full path by default DirectoryInfo testDir = Directory.CreateDirectory(GetTestFilePath()); Directory.CreateDirectory(Path.Combine(testDir.FullName, GetTestFileName())); using (File.Create(Path.Combine(testDir.FullName, GetTestFileName()))) @@ -409,8 +409,8 @@ namespace System.IO.Tests ValidatePatternMatch(expected, GetEntries(testDir, pattern)); } - [ActiveIssue(20781, TestPlatforms.AnyUnix)] [OuterLoop("These are pretty corner, don't need to run all the time.")] + [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] // Can't do these without extended path support on Windows, UsingNewNormalization filters appropriately [ConditionalTheory(nameof(UsingNewNormalization)), // "foo*." actually becomes "foo<" when passed to NT. It matches all characters up to, and including, the final period. @@ -561,7 +561,142 @@ namespace System.IO.Tests // Really should be: new string[] { @"foo.." }), but is new string[] { @"foo. ", @"foo. ", @"foo. ", @"foo.." }), ] - public void PatternTests_DosStarOddSpace(string pattern, string[] sourceFiles, string[] expected) + public void PatternTests_DosStarOddSpace_Desktop(string pattern, string[] sourceFiles, string[] expected) + { + // Tests for DOS_STAR, which only occurs when the source pattern ends in *. + // These cases don't match documented behavior on Windows- matching *should* end at the final period. + + // We don't want to eat trailing space/periods in this test + string testDir = PrepareDirectory(sourceFiles, useExtendedPaths: true); + ValidatePatternMatch(expected, GetEntries(testDir, pattern)); + } + + [ActiveIssue(20781, TestPlatforms.AnyUnix)] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + [OuterLoop("These are pretty corner, don't need to run all the time.")] + [Theory, + // "foo*." actually becomes "foo<" when passed to NT. It matches all characters up to, and including, the final period. + // + // There is a "bug" somewhere in the Windows stack where *some* files with trailing spaces after the final period will be returned when + // using "*." at the end of a string (which becomes "<"). According to the rules (and the actual pattern matcher used FsRtlIsNameInExpression) + // *nothing* should match after the final period. + // + // We've made Core effectively call RtlIsNameInExpression directly, so this test validates the normally buggy cases. See the test above + // for what Windows really does. These are super obscure and the bug pattern isn't obvious so we're just going with "correct". + InlineData( + "foo*.", + new string[] { @"foo", @"foo.", @"foo.t", @"foo.tx", @"foo.txt", @"bar.txt", @"foo..", @"foo...", @"foo. ", @"foo. ", @"foo .", @"foo. . .", @"foo. t" }, + new string[] { @"foo", @"foo.", @"foo..", @"foo...", @"foo .", @"foo. . ." }), + InlineData( + "*.", + new string[] { @"foo. ", @"foo. ", @"foo..", @"foo. t" }, + new string[] { @"foo.." }), + InlineData( + "f*.", + new string[] { @"foo. ", @"foo. ", @"foo..", @"foo. t" }, + new string[] { @"foo.." }), + InlineData( + "fo*.", + new string[] { @"foo. ", @"foo. ", @"foo..", @"foo. t" }, + new string[] { @"foo.." }), + InlineData( + "foo*.", + new string[] { @"foo. ", @"foo. ", @"foo. ", @"foo. " }, + new string[] { }), + InlineData( + "foo*.", + new string[] { @"foo. ", @"foo. ", @"foo. ", @"foo. ", @"foo." }, + new string[] { @"foo." }), + InlineData( + "foo*.", + new string[] { @"foo.", @"foo. ", @"foo. ", @"foo. ", @"foo. " }, + new string[] { @"foo." }), + InlineData( + "foo*.", + new string[] { @"foo.", @"foo", @"foo. ", @"foo. ", @"foo. ", @"foo. " }, + new string[] { @"foo.", @"foo" }), + InlineData( + "foo*.", + new string[] { @"foo.", @"foo. ", @"foo. ", @"foo. ", @"foo. ", @"foo" }, + new string[] { @"foo.", @"foo" }), + InlineData( + "foo*.", + new string[] { @"foo. ", @"foo", @"foo.", @"foo. ", @"foo. ", @"foo. " }, + new string[] { @"foo.", @"foo" }), + InlineData( + "foo*.", + new string[] { @"foo. ", @"foo", @"food", @"foo.", @"foo. ", @"foo. ", @"foo. " }, + new string[] { @"foo.", @"foo", @"food" }), + InlineData( + "fo*.", + new string[] { @"foo.", @"foo. ", @"foo. ", @"foo. ", @"foo. " }, + new string[] { @"foo." }), + InlineData( + "foo*.", + new string[] { @"foo. ", @"foo. ", @"foo. ", @"foo. ", @"foo. " }, + new string[] { }), + InlineData( + "foo*.", + new string[] { @"foo. ", @"foo. .", @"foo. . ", @"foo. . .", @"foo. . . " }, + new string[] { @"foo. .", @"foo. . ." }), + InlineData( + "foo*.", + new string[] { @"foo. ", @"foo. .", @"foo.. .", @"foo.... .", @"foo..... ." }, + new string[] { @"foo. .", @"foo.. .", @"foo.... .", @"foo..... ." }), + InlineData( + "fo*.", + new string[] { @"foo. ", @"foo. .", @"foo. . ", @"foo. . .", @"foo. . . " }, + new string[] { @"foo. .", @"foo. . ."}), + InlineData( + "foo*.", + new string[] { @"foo.", @"foo. ", @"foo. ", @"foo. ", @"foo. ", @"foo. " }, + new string[] { @"foo." }), + InlineData( + "food*.", + new string[] { @"food.", @"food. ", @"food. ", @"food. ", @"food. ", @"food. " }, + new string[] { @"food." }), + InlineData( + "food*.", + new string[] { @"food.", @"food. ", @"food. ", @"food. ", @"food. ", @"food. ", @"foodi." }, + new string[] { @"food.", @"foodi." }), + InlineData( + "foodi*.", + new string[] { @"foodi.", @"foodi. ", @"foodi. ", @"foodi. ", @"foodi. ", @"foodi. " }, + new string[] { @"foodi." }), + InlineData( + "foodie*.", + new string[] { @"foodie.", @"foodie. ", @"foodie. ", @"foodie. ", @"foodie. ", @"foodie. " }, + new string[] { @"foodie." }), + InlineData( + "fooooo*.", + new string[] { @"foooooo.", @"foooooo. ", @"foooooo. " }, + new string[] { @"foooooo." }), + InlineData( + "fooooo*.", + new string[] { @"foooooo. ", @"foooooo. ", @"foooooo. " }, + new string[] { }), + InlineData( + "fo*.", + new string[] { @"foo. ", @"foo. ", @"foo. ", @"foo. ", @"foo. " }, + new string[] { }), + InlineData( + "fo*.", + new string[] { @"foo. ", @"foo. ", @"foo. ", @"foo. ", @"foo. ", @"foo. ", @"foo. " }, + new string[] { }), + InlineData( + "fo*.", + new string[] { @"fo. ", @"fo. ", @"fo. ", @"fo. ", @"foo. ", @"foo. ", @"foo. ", @"foo. ", @"foo. ", @"foo. " }, + new string[] { }), + InlineData( + "fo*.", + new string[] { @"fo. ", @"fo. ", @"fo. ", @"fo. ", @"fo. ", @"fo. ", @"foo. ", @"foo. ", @"foo. ", @"foo. ", @"foo. ", @"foo. " }, + new string[] { }), + InlineData( + "foo*.", + new string[] { @"foo. ", @"foo. ", @"foo..", @"foo. t", @"foo. ", @"foo. " }, + new string[] { @"foo.." }), + ] + public void PatternTests_DosStarOddSpace_Core(string pattern, string[] sourceFiles, string[] expected) { // Tests for DOS_STAR, which only occurs when the source pattern ends in *. // These cases don't match documented behavior on Windows- matching *should* end at the final period. @@ -607,24 +742,21 @@ namespace System.IO.Tests } [Fact] - [PlatformSpecific(TestPlatforms.Windows)] + [PlatformSpecific(TestPlatforms.Windows)] public void WindowsSearchPatternLongSegment() { // Create a path segment longer than the normal max of 255 DirectoryInfo testDir = Directory.CreateDirectory(GetTestFilePath()); string longName = new string('k', 257); - - // Long path segment in search pattern throws PathTooLongException on Desktop, - // otherwise it is an IOException. + + // Long path segment in search pattern throws PathTooLongException on Desktop if (PlatformDetection.IsFullFramework) { Assert.Throws(() => GetEntries(testDir.FullName, longName)); } else { - var exception = Assert.Throws(() => GetEntries(testDir.FullName, longName)); - // Should be Interop.Errors.ERROR_INVALID_PARAMETER converted to a HResult - Assert.Equal(unchecked((int)0x80070057), exception.HResult); + GetEntries(testDir.FullName, longName); } } @@ -651,20 +783,38 @@ namespace System.IO.Tests } [Fact] - [PlatformSpecific(TestPlatforms.Windows)] // Search pattern with double dots throws ArgumentException - public void WindowsSearchPatternWithDoubleDots() + [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] + public void WindowsSearchPatternWithDoubleDots_Desktop() { + // Search pattern with double dots throws ArgumentException Assert.Throws(() => GetEntries(TestDirectory, Path.Combine("..ab ab.. .. abc..d", "abc.."))); Assert.Throws(() => GetEntries(TestDirectory, "..")); Assert.Throws(() => GetEntries(TestDirectory, @".." + Path.DirectorySeparatorChar)); } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + public void SearchPatternWithDoubleDots_Core() + { + // Search pattern with double dots no longer throws ArgumentException + string directory = Directory.CreateDirectory(GetTestFilePath()).FullName; + Assert.Throws(() => GetEntries(directory, Path.Combine("..ab ab.. .. abc..d", "abc.."))); + GetEntries(directory, ".."); + GetEntries(directory, @".." + Path.DirectorySeparatorChar); + + Assert.Throws(() => GetEntries(directory, Path.Combine("..ab ab.. .. abc..d", "abc", ".."))); + GetEntries(directory, Path.Combine("..ab ab.. .. abc..d", "..", "abc")); + Assert.Throws(() => GetEntries(directory, Path.Combine("..", "..ab ab.. .. abc..d", "abc"))); + Assert.Throws(() => GetEntries(directory, Path.Combine("..", "..ab ab.. .. abc..d", "abc") + Path.DirectorySeparatorChar)); + } + private static char[] OldWildcards = new char[] { '*', '?' }; private static char[] NewWildcards = new char[] { '<', '>', '\"' }; [Fact] [PlatformSpecific(TestPlatforms.Windows)] // Windows-invalid search patterns throw - public void WindowsSearchPatternInvalid() + [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] + public void WindowsSearchPatternInvalid_Desktop() { Assert.Throws(() => GetEntries(TestDirectory, "\0")); Assert.Throws(() => GetEntries(TestDirectory, "|")); @@ -700,6 +850,29 @@ namespace System.IO.Tests }); } + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + public void WindowsSearchPatternInvalid_Core() + { + GetEntries(TestDirectory, "\0"); + GetEntries(TestDirectory, "|"); + + Assert.All(Path.GetInvalidFileNameChars().Except(OldWildcards).Except(NewWildcards), invalidChar => + { + switch (invalidChar) + { + case '\\': + case '/': + Assert.Throws(() => GetEntries(Directory.GetCurrentDirectory(), string.Format("te{0}st", invalidChar.ToString()))); + break; + default: + GetEntries(Directory.GetCurrentDirectory(), string.Format("te{0}st", invalidChar.ToString())); + break; + } + }); + } + [Fact] [PlatformSpecific(TestPlatforms.Windows)] // Windows-invalid search patterns throw [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "In netcoreapp we made three new characters be treated as valid wildcards instead of invalid characters. NetFX still treats them as InvalidChars.")] @@ -731,11 +904,11 @@ namespace System.IO.Tests } [Fact] - [PlatformSpecific(TestPlatforms.AnyUnix)] // Unix-invalid sarch patterns throw ArgumentException + [PlatformSpecific(TestPlatforms.AnyUnix)] // Unix-invalid search patterns throws no exception public void UnixSearchPatternInvalid() { - Assert.Throws(() => GetEntries(TestDirectory, "\0")); - Assert.Throws(() => GetEntries(TestDirectory, string.Format("te{0}st", "\0".ToString()))); + GetEntries(TestDirectory, "\0"); + GetEntries(TestDirectory, string.Format("te{0}st", "\0".ToString())); } [Fact] @@ -840,22 +1013,6 @@ namespace System.IO.Tests } } - [Fact] - [PlatformSpecific(TestPlatforms.AnyUnix)] // Search pattern with DoubleDots on Unix - public void UnixSearchPatternWithDoubleDots() - { - // search pattern is valid but directory doesn't exist - Assert.Throws(() => GetEntries(TestDirectory, Path.Combine("..ab ab.. .. abc..d", "abc.."))); - - // invalid search pattern trying to go up a directory with .. - Assert.Throws(() => GetEntries(TestDirectory, "..")); - Assert.Throws(() => GetEntries(TestDirectory, @".." + Path.DirectorySeparatorChar)); - Assert.Throws(() => GetEntries(TestDirectory, Path.Combine("..ab ab.. .. abc..d", "abc", ".."))); - Assert.Throws(() => GetEntries(TestDirectory, Path.Combine("..ab ab.. .. abc..d", "..", "abc"))); - Assert.Throws(() => GetEntries(TestDirectory, Path.Combine("..", "..ab ab.. .. abc..d", "abc"))); - Assert.Throws(() => GetEntries(TestDirectory, Path.Combine("..", "..ab ab.. .. abc..d", "abc") + Path.DirectorySeparatorChar)); - } - #endregion } } diff --git a/external/corefx/src/System.IO.FileSystem/tests/File/Delete.cs b/external/corefx/src/System.IO.FileSystem/tests/File/Delete.cs index f6f7ade21f..398833f102 100644 --- a/external/corefx/src/System.IO.FileSystem/tests/File/Delete.cs +++ b/external/corefx/src/System.IO.FileSystem/tests/File/Delete.cs @@ -129,7 +129,7 @@ namespace System.IO.Tests [Trait(XunitConstants.Category, XunitConstants.RequiresElevation)] public void Unix_NonExistentPath_ReadOnlyVolume() { - if (PlatformDetection.IsRedHat69) + if (PlatformDetection.IsRedHatFamily6) return; // [ActiveIssue(https://github.com/dotnet/corefx/issues/21920)] ReadOnly_FileSystemHelper(readOnlyDirectory => @@ -144,7 +144,7 @@ namespace System.IO.Tests [Trait(XunitConstants.Category, XunitConstants.RequiresElevation)] public void Unix_ExistingDirectory_ReadOnlyVolume() { - if (PlatformDetection.IsRedHat69) + if (PlatformDetection.IsRedHatFamily6) return; // [ActiveIssue(https://github.com/dotnet/corefx/issues/21920)] ReadOnly_FileSystemHelper(readOnlyDirectory => diff --git a/external/corefx/src/System.IO.FileSystem/tests/File/GetSetTimes.cs b/external/corefx/src/System.IO.FileSystem/tests/File/GetSetTimes.cs index 6578d05b97..b85fd30003 100644 --- a/external/corefx/src/System.IO.FileSystem/tests/File/GetSetTimes.cs +++ b/external/corefx/src/System.IO.FileSystem/tests/File/GetSetTimes.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using Xunit; namespace System.IO.Tests { @@ -15,6 +16,23 @@ namespace System.IO.Tests return path; } + [Fact] + [PlatformSpecific(TestPlatforms.Linux)] + public void BirthTimeIsNotNewerThanLowestOfAccessModifiedTimes() + { + // On Linux, we synthesize CreationTime from the oldest of statuc changed time and write time + // if birth time is not available. So WriteTime should never be earlier. + + // Set different values for all three + // Status changed time will be when the file was first created, in this case) + string path = GetExistingItem(); + File.SetLastWriteTime(path, DateTime.Now.AddMinutes(1)); + File.SetLastAccessTime(path, DateTime.Now.AddMinutes(2)); + + // Assert.InRange is inclusive. + Assert.InRange(File.GetCreationTimeUtc(path), DateTime.MinValue, File.GetLastWriteTimeUtc(path)); + } + public override IEnumerable TimeFunctions(bool requiresRoundtripping = false) { if (IOInputs.SupportsGettingCreationTime && (!requiresRoundtripping || IOInputs.SupportsSettingCreationTime)) diff --git a/external/corefx/src/System.IO.FileSystem/tests/File/ReadWriteAllLinesAsync.cs b/external/corefx/src/System.IO.FileSystem/tests/File/ReadWriteAllLinesAsync.cs index 276c3256e0..4f0b5f0415 100644 --- a/external/corefx/src/System.IO.FileSystem/tests/File/ReadWriteAllLinesAsync.cs +++ b/external/corefx/src/System.IO.FileSystem/tests/File/ReadWriteAllLinesAsync.cs @@ -24,7 +24,7 @@ namespace System.IO.Tests #endregion #region UniversalTests - + [Fact] public async Task InvalidPathAsync() { diff --git a/external/corefx/src/System.IO.FileSystem/tests/FileInfo/GetSetTimes.cs b/external/corefx/src/System.IO.FileSystem/tests/FileInfo/GetSetTimes.cs index 61b40ec596..ed38b89719 100644 --- a/external/corefx/src/System.IO.FileSystem/tests/FileInfo/GetSetTimes.cs +++ b/external/corefx/src/System.IO.FileSystem/tests/FileInfo/GetSetTimes.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; +using Xunit; namespace System.IO.Tests { @@ -63,6 +64,7 @@ namespace System.IO.Tests DateTimeKind.Utc); } + [Fact] public void DeleteAfterEnumerate_TimesStillSet() { // When enumerating we populate the state as we already have it. @@ -77,5 +79,24 @@ namespace System.IO.Tests info.Delete(); ValidateSetTimes(info, beforeTime, afterTime); } + + + [Fact] + [PlatformSpecific(TestPlatforms.Linux)] + public void BirthTimeIsNotNewerThanLowestOfAccessModifiedTimes() + { + // On Linux (if no birth time), we synthesize CreationTime from the oldest of + // status changed time (ctime) and write time (mtime) + // Sanity check that it is in that range. + + DateTime before = DateTime.UtcNow.AddMinutes(-1); + + FileInfo fi = GetExistingItem(); // should set ctime + fi.LastWriteTimeUtc = DateTime.UtcNow.AddMinutes(1); // mtime + fi.LastAccessTimeUtc = DateTime.UtcNow.AddMinutes(2); // atime + + // Assert.InRange is inclusive + Assert.InRange(fi.CreationTimeUtc, before, fi.LastWriteTimeUtc); + } } } diff --git a/external/corefx/src/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj b/external/corefx/src/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj index 1ecc4c4585..fb99199ad1 100644 --- a/external/corefx/src/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj +++ b/external/corefx/src/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj @@ -24,6 +24,7 @@ + diff --git a/external/corefx/src/System.IO.IsolatedStorage/src/System.IO.IsolatedStorage.csproj b/external/corefx/src/System.IO.IsolatedStorage/src/System.IO.IsolatedStorage.csproj index a8ebc9d294..80238968ed 100644 --- a/external/corefx/src/System.IO.IsolatedStorage/src/System.IO.IsolatedStorage.csproj +++ b/external/corefx/src/System.IO.IsolatedStorage/src/System.IO.IsolatedStorage.csproj @@ -23,7 +23,6 @@ Common\System\Security\IdentityHelper.cs - diff --git a/external/corefx/src/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageException.Serialization.cs b/external/corefx/src/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageException.Serialization.cs deleted file mode 100644 index c1451074cd..0000000000 --- a/external/corefx/src/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageException.Serialization.cs +++ /dev/null @@ -1,18 +0,0 @@ -// 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.Runtime.Serialization; - -namespace System.IO.IsolatedStorage -{ - [Serializable] - public partial class IsolatedStorageException : Exception - { - protected IsolatedStorageException(SerializationInfo info, StreamingContext context) : base(info, context) - { - throw new PlatformNotSupportedException(); - } - } -} diff --git a/external/corefx/src/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageException.cs b/external/corefx/src/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageException.cs index 415f14eff5..fbf2908da1 100644 --- a/external/corefx/src/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageException.cs +++ b/external/corefx/src/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageException.cs @@ -3,10 +3,13 @@ // See the LICENSE file in the project root for more information. using System; +using System.Runtime.Serialization; namespace System.IO.IsolatedStorage { - public partial class IsolatedStorageException : Exception + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class IsolatedStorageException : Exception, ISerializable { private const int COR_E_ISOSTORE = unchecked((int)0x80131450); @@ -17,24 +20,23 @@ namespace System.IO.IsolatedStorage public IsolatedStorageException() : base(SR.IsolatedStorage_Exception) { - SetErrorCode(COR_E_ISOSTORE); + HResult = COR_E_ISOSTORE; } public IsolatedStorageException(string message) : base(message) { - SetErrorCode(COR_E_ISOSTORE); + HResult = COR_E_ISOSTORE; } public IsolatedStorageException(string message, Exception inner) : base(message, inner) { - SetErrorCode(COR_E_ISOSTORE); + HResult = COR_E_ISOSTORE; } - private void SetErrorCode(int hr) + protected IsolatedStorageException(SerializationInfo info, StreamingContext context) : base(info, context) { - HResult = hr; } } } diff --git a/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/CopyFileTests.cs b/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/CopyFileTests.cs index 4409f63563..d36760289c 100644 --- a/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/CopyFileTests.cs +++ b/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/CopyFileTests.cs @@ -65,6 +65,7 @@ namespace System.IO.IsolatedStorage } [Fact] + [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] public void CopyFile_RaisesInvalidPath() { using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForAssembly()) @@ -74,6 +75,17 @@ namespace System.IO.IsolatedStorage } } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + public void CopyFile_RaisesIsolatedStorageException() + { + using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForAssembly()) + { + Assert.Throws(() => isf.CopyFile("\0bad", "bar")); + Assert.Throws(() => isf.CopyFile("foo", "\0bad")); + } + } + [Fact] public void CopyFile_DoesNotExist() { diff --git a/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/CreateDirectoryTests.cs b/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/CreateDirectoryTests.cs index be703cd30e..f8af1fe004 100644 --- a/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/CreateDirectoryTests.cs +++ b/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/CreateDirectoryTests.cs @@ -50,6 +50,7 @@ namespace System.IO.IsolatedStorage } [Fact] + [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] public void CreateDirectory_RaisesArgumentException() { using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForAssembly()) @@ -58,6 +59,16 @@ namespace System.IO.IsolatedStorage } } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + public void CreateDirectory_IsolatedStorageException() + { + using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForAssembly()) + { + Assert.Throws(() => isf.CreateDirectory("\0bad")); + } + } + [Theory, MemberData(nameof(ValidStores))] [ActiveIssue("dotnet/corefx #18268", TargetFrameworkMonikers.NetFramework)] public void CreateDirectory_Existance(PresetScopes scope) diff --git a/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/CreateFileTests.cs b/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/CreateFileTests.cs index ea81ed782f..e6ce4a73fc 100644 --- a/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/CreateFileTests.cs +++ b/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/CreateFileTests.cs @@ -50,6 +50,7 @@ namespace System.IO.IsolatedStorage } [Fact] + [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] public void CreateFile_RaisesArgumentException() { using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForAssembly()) @@ -58,6 +59,16 @@ namespace System.IO.IsolatedStorage } } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + public void CreateFile_IsolatedStorageException() + { + using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForAssembly()) + { + Assert.Throws(() => isf.CreateFile("\0bad")); + } + } + [Theory, MemberData(nameof(ValidStores))] [ActiveIssue("dotnet/corefx #18268", TargetFrameworkMonikers.NetFramework)] public void CreateFile_Existence(PresetScopes scope) diff --git a/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/DirectoryExistsTests.cs b/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/DirectoryExistsTests.cs index 2658a09e2e..924ba8c1e5 100644 --- a/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/DirectoryExistsTests.cs +++ b/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/DirectoryExistsTests.cs @@ -50,6 +50,7 @@ namespace System.IO.IsolatedStorage } [Fact] + [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] public void DirectoryExists_RaisesArgumentException() { using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForAssembly()) @@ -58,6 +59,16 @@ namespace System.IO.IsolatedStorage } } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + public void DirectoryExists_False() + { + using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForAssembly()) + { + Assert.False(isf.DirectoryExists("\0bad")); + } + } + [Fact] public void DirectoryExists_Existance() { diff --git a/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/FileExistsTests.cs b/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/FileExistsTests.cs index 208e83c079..4802452035 100644 --- a/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/FileExistsTests.cs +++ b/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/FileExistsTests.cs @@ -50,6 +50,7 @@ namespace System.IO.IsolatedStorage } [Fact] + [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] public void FileExists_RaisesArgumentException() { using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForAssembly()) @@ -58,6 +59,16 @@ namespace System.IO.IsolatedStorage } } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + public void FileExists_False() + { + using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForAssembly()) + { + Assert.False(isf.FileExists("\0bad")); + } + } + [Theory] [MemberData(nameof(ValidStores))] [ActiveIssue("dotnet/corefx #18268", TargetFrameworkMonikers.NetFramework)] diff --git a/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/GetFileNamesTests.cs b/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/GetFileNamesTests.cs index 16d84a11d0..a7485584a4 100644 --- a/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/GetFileNamesTests.cs +++ b/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/GetFileNamesTests.cs @@ -53,7 +53,20 @@ namespace System.IO.IsolatedStorage } [Fact] - public void GetFileNames_RaisesInvalidPath() + [ActiveIssue(25428, TestPlatforms.AnyUnix)] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + public void GetFileNames_RaisesInvalidPath_Core() + { + // We are no longer as agressive with filters for enumerating files + using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForAssembly()) + { + isf.GetFileNames("\0bad"); + } + } + + [Fact] + [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] + public void GetFileNames_RaisesInvalidPath_Desktop() { using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForAssembly()) { diff --git a/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/MoveDirectoryTests.cs b/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/MoveDirectoryTests.cs index 624d640c19..762fe2be1d 100644 --- a/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/MoveDirectoryTests.cs +++ b/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/MoveDirectoryTests.cs @@ -61,6 +61,7 @@ namespace System.IO.IsolatedStorage } [Fact] + [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] public void MoveDirectory_RaisesInvalidPath() { using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForAssembly()) @@ -70,6 +71,17 @@ namespace System.IO.IsolatedStorage } } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + public void MoveDirectory_IsolatedStorageException() + { + using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForAssembly()) + { + Assert.Throws(() => isf.MoveDirectory("\0bad", "bar")); + Assert.Throws(() => isf.MoveDirectory("foo", "\0bad")); + } + } + [Fact] public void MoveDirectory_DoesNotExist() { diff --git a/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/MoveFileTests.cs b/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/MoveFileTests.cs index 6895e367ab..13854f72ed 100644 --- a/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/MoveFileTests.cs +++ b/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/MoveFileTests.cs @@ -62,6 +62,7 @@ namespace System.IO.IsolatedStorage } [Fact] + [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] public void MoveFile_RaisesInvalidPath() { using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForAssembly()) @@ -71,6 +72,17 @@ namespace System.IO.IsolatedStorage } } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + public void MoveFile_RaisesIsolatedStorageException() + { + using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForAssembly()) + { + Assert.Throws(() => isf.MoveFile("\0bad", "bar")); + Assert.Throws(() => isf.MoveFile("foo", "\0bad")); + } + } + [Fact] public void MoveFile_DoesNotExist() { diff --git a/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/OpenFileTests.cs b/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/OpenFileTests.cs index bdd1a72eba..9514d17d28 100644 --- a/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/OpenFileTests.cs +++ b/external/corefx/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/OpenFileTests.cs @@ -58,6 +58,7 @@ namespace System.IO.IsolatedStorage } [Fact] + [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] public void OpenFile_RaisesArgumentException() { using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForAssembly()) @@ -68,6 +69,18 @@ namespace System.IO.IsolatedStorage } } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + public void OpenFile_RaisesIsolatedStorageException() + { + using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForAssembly()) + { + Assert.Throws(() => isf.OpenFile("\0bad", FileMode.Create)); + Assert.Throws(() => isf.OpenFile("\0bad", FileMode.Create, FileAccess.ReadWrite)); + Assert.Throws(() => isf.OpenFile("\0bad", FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite)); + } + } + [Fact] [ActiveIssue("dotnet/corefx #18265", TargetFrameworkMonikers.NetFramework)] public void OpenFile_PassesFileShare() diff --git a/external/corefx/src/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedFileHandle.Unix.cs b/external/corefx/src/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedFileHandle.Unix.cs index b712514a74..54a074ef93 100644 --- a/external/corefx/src/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedFileHandle.Unix.cs +++ b/external/corefx/src/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedFileHandle.Unix.cs @@ -85,7 +85,6 @@ namespace Microsoft.Win32.SafeHandles public override bool IsInvalid { - [SecurityCritical] get { return (long)handle <= 0; } } } diff --git a/external/corefx/src/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedViewHandle.cs b/external/corefx/src/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedViewHandle.cs index 88f7ca7196..06f50d5b93 100644 --- a/external/corefx/src/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedViewHandle.cs +++ b/external/corefx/src/System.IO.MemoryMappedFiles/src/Microsoft/Win32/SafeMemoryMappedViewHandle.cs @@ -8,7 +8,6 @@ using System.Security; namespace Microsoft.Win32.SafeHandles { - [SecurityCritical] #pragma warning disable 0618 // SafeBuffer is obsolete public sealed partial class SafeMemoryMappedViewHandle : SafeBuffer #pragma warning restore diff --git a/external/corefx/src/System.IO.MemoryMappedFiles/src/Resources/Strings.resx b/external/corefx/src/System.IO.MemoryMappedFiles/src/Resources/Strings.resx index 6bbc4472ff..c4a69266ce 100644 --- a/external/corefx/src/System.IO.MemoryMappedFiles/src/Resources/Strings.resx +++ b/external/corefx/src/System.IO.MemoryMappedFiles/src/Resources/Strings.resx @@ -1,5 +1,64 @@  + @@ -160,4 +219,7 @@ Named maps are not supported. + + The path '{0}' is too long, or a component of the specified path is too long. + \ No newline at end of file diff --git a/external/corefx/src/System.IO.MemoryMappedFiles/src/System.IO.MemoryMappedFiles.csproj b/external/corefx/src/System.IO.MemoryMappedFiles/src/System.IO.MemoryMappedFiles.csproj index b7cd99bfd5..b047d4849e 100644 --- a/external/corefx/src/System.IO.MemoryMappedFiles/src/System.IO.MemoryMappedFiles.csproj +++ b/external/corefx/src/System.IO.MemoryMappedFiles/src/System.IO.MemoryMappedFiles.csproj @@ -14,9 +14,6 @@ - - Common\System\IO\Error.cs - diff --git a/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.Unix.cs b/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.Unix.cs index 8d5eda1fee..172bf39afe 100644 --- a/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.Unix.cs +++ b/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.Unix.cs @@ -14,7 +14,6 @@ namespace System.IO.MemoryMappedFiles /// memory mapped file should not be associated with an existing file on disk (i.e. start /// out empty). /// - [SecurityCritical] private static unsafe SafeMemoryMappedFileHandle CreateCore( FileStream fileStream, string mapName, HandleInheritability inheritability, MemoryMappedFileAccess access, @@ -85,7 +84,6 @@ namespace System.IO.MemoryMappedFiles /// /// Used by the CreateOrOpen factory method groups. /// - [SecurityCritical] private static SafeMemoryMappedFileHandle CreateOrOpenCore( string mapName, HandleInheritability inheritability, MemoryMappedFileAccess access, @@ -101,7 +99,6 @@ namespace System.IO.MemoryMappedFiles /// We'll throw an ArgumentException if the file mapping object didn't exist and the /// caller used CreateOrOpen since Create isn't valid with Write access /// - [SecurityCritical] private static SafeMemoryMappedFileHandle OpenCore( string mapName, HandleInheritability inheritability, MemoryMappedFileAccess access, bool createOrOpen) { @@ -113,7 +110,6 @@ namespace System.IO.MemoryMappedFiles /// We'll throw an ArgumentException if the file mapping object didn't exist and the /// caller used CreateOrOpen since Create isn't valid with Write access /// - [SecurityCritical] private static SafeMemoryMappedFileHandle OpenCore( string mapName, HandleInheritability inheritability, MemoryMappedFileRights rights, bool createOrOpen) { diff --git a/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.Windows.cs b/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.Windows.cs index 24ea3a4658..b098026f70 100644 --- a/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.Windows.cs +++ b/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.Windows.cs @@ -19,7 +19,6 @@ namespace System.IO.MemoryMappedFiles /// private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); - [SecurityCritical] private static SafeMemoryMappedFileHandle CreateCore( FileStream fileStream, string mapName, HandleInheritability inheritability, MemoryMappedFileAccess access, MemoryMappedFileOptions options, long capacity) @@ -75,7 +74,6 @@ namespace System.IO.MemoryMappedFiles /// /// Used by the CreateOrOpen factory method groups. /// - [SecurityCritical] private static SafeMemoryMappedFileHandle CreateOrOpenCore( string mapName, HandleInheritability inheritability, MemoryMappedFileAccess access, MemoryMappedFileOptions options, long capacity) @@ -217,7 +215,6 @@ namespace System.IO.MemoryMappedFiles /// We'll throw an ArgumentException if the file mapping object didn't exist and the /// caller used CreateOrOpen since Create isn't valid with Write access /// - [SecurityCritical] private static SafeMemoryMappedFileHandle OpenCore( string mapName, HandleInheritability inheritability, int desiredAccessRights, bool createOrOpen) { @@ -244,7 +241,6 @@ namespace System.IO.MemoryMappedFiles /// Helper method used to extract the native binary security descriptor from the MemoryMappedFileSecurity /// type. If pinningHandle is not null, caller must free it AFTER the call to CreateFile has returned. /// - [SecurityCritical] private static unsafe Interop.Kernel32.SECURITY_ATTRIBUTES GetSecAttrs(HandleInheritability inheritability) { Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = default(Interop.Kernel32.SECURITY_ATTRIBUTES); diff --git a/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.cs b/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.cs index db2c678f71..b9042d6f52 100644 --- a/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.cs +++ b/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.cs @@ -16,7 +16,6 @@ namespace System.IO.MemoryMappedFiles internal const int DefaultSize = 0; // Private constructors to be used by the factory methods. - [SecurityCritical] private MemoryMappedFile(SafeMemoryMappedFileHandle handle) { Debug.Assert(handle != null); @@ -27,7 +26,6 @@ namespace System.IO.MemoryMappedFiles _leaveOpen = true; // No FileStream to dispose of in this case. } - [SecurityCritical] private MemoryMappedFile(SafeMemoryMappedFileHandle handle, FileStream fileStream, bool leaveOpen) { Debug.Assert(handle != null); @@ -57,7 +55,6 @@ namespace System.IO.MemoryMappedFiles return OpenExisting(mapName, desiredAccessRights, HandleInheritability.None); } - [SecurityCritical] public static MemoryMappedFile OpenExisting(string mapName, MemoryMappedFileRights desiredAccessRights, HandleInheritability inheritability) { @@ -112,7 +109,6 @@ namespace System.IO.MemoryMappedFiles return CreateFromFile(path, mode, mapName, capacity, MemoryMappedFileAccess.ReadWrite); } - [SecurityCritical] public static MemoryMappedFile CreateFromFile(string path, FileMode mode, string mapName, long capacity, MemoryMappedFileAccess access) { @@ -194,7 +190,6 @@ namespace System.IO.MemoryMappedFiles return new MemoryMappedFile(handle, fileStream, false); } - [SecurityCritical] public static MemoryMappedFile CreateFromFile(FileStream fileStream, string mapName, long capacity, MemoryMappedFileAccess access, HandleInheritability inheritability, bool leaveOpen) @@ -274,7 +269,6 @@ namespace System.IO.MemoryMappedFiles HandleInheritability.None); } - [SecurityCritical] public static MemoryMappedFile CreateNew(string mapName, long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, HandleInheritability inheritability) @@ -335,7 +329,6 @@ namespace System.IO.MemoryMappedFiles return CreateOrOpen(mapName, capacity, access, MemoryMappedFileOptions.None, HandleInheritability.None); } - [SecurityCritical] public static MemoryMappedFile CreateOrOpen(string mapName, long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, HandleInheritability inheritability) @@ -400,7 +393,6 @@ namespace System.IO.MemoryMappedFiles return CreateViewStream(offset, size, MemoryMappedFileAccess.ReadWrite); } - [SecurityCritical] public MemoryMappedViewStream CreateViewStream(long offset, long size, MemoryMappedFileAccess access) { if (offset < 0) @@ -438,7 +430,6 @@ namespace System.IO.MemoryMappedFiles return CreateViewAccessor(offset, size, MemoryMappedFileAccess.ReadWrite); } - [SecurityCritical] public MemoryMappedViewAccessor CreateViewAccessor(long offset, long size, MemoryMappedFileAccess access) { if (offset < 0) @@ -471,7 +462,6 @@ namespace System.IO.MemoryMappedFiles GC.SuppressFinalize(this); } - [SecuritySafeCritical] protected virtual void Dispose(bool disposing) { try @@ -492,7 +482,6 @@ namespace System.IO.MemoryMappedFiles public SafeMemoryMappedFileHandle SafeMemoryMappedFileHandle { - [SecurityCritical] get { return _handle; } } diff --git a/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Unix.cs b/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Unix.cs index 12207ff2c0..d00c535f75 100644 --- a/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Unix.cs +++ b/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Unix.cs @@ -11,7 +11,6 @@ namespace System.IO.MemoryMappedFiles { internal partial class MemoryMappedView { - [SecurityCritical] public static unsafe MemoryMappedView CreateView( SafeMemoryMappedFileHandle memMappedFileHandle, MemoryMappedFileAccess access, long requestedOffset, long requestedSize) @@ -137,7 +136,6 @@ namespace System.IO.MemoryMappedFiles access); } - [SecurityCritical] public unsafe void Flush(UIntPtr capacity) { if (capacity == UIntPtr.Zero) diff --git a/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Windows.cs b/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Windows.cs index 5d203ea499..193d8a1c1d 100644 --- a/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Windows.cs +++ b/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Windows.cs @@ -17,7 +17,6 @@ namespace System.IO.MemoryMappedFiles private const int MaxFlushWaits = 15; // must be <=30 private const int MaxFlushRetriesPerWait = 20; - [SecurityCritical] public static unsafe MemoryMappedView CreateView(SafeMemoryMappedFileHandle memMappedFileHandle, MemoryMappedFileAccess access, long offset, long size) { @@ -97,7 +96,6 @@ namespace System.IO.MemoryMappedFiles // flush to the disk. // NOTE: This will flush all bytes before and after the view up until an offset that is a multiple // of SystemPageSize. - [SecurityCritical] public void Flush(UIntPtr capacity) { unsafe @@ -157,7 +155,6 @@ namespace System.IO.MemoryMappedFiles // ---- PAL layer ends here ---- // ----------------------------- - [SecurityCritical] private static int GetSystemPageAllocationGranularity() { Interop.Kernel32.SYSTEM_INFO info; diff --git a/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.cs b/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.cs index 2c5f59f489..a7962912b6 100644 --- a/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.cs +++ b/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.cs @@ -15,7 +15,6 @@ namespace System.IO.MemoryMappedFiles private readonly long _size; private readonly MemoryMappedFileAccess _access; - [SecurityCritical] private unsafe MemoryMappedView(SafeMemoryMappedViewHandle viewHandle, long pointerOffset, long size, MemoryMappedFileAccess access) { @@ -29,7 +28,6 @@ namespace System.IO.MemoryMappedFiles public SafeMemoryMappedViewHandle ViewHandle { - [SecurityCritical] get { return _viewHandle; } } @@ -48,7 +46,6 @@ namespace System.IO.MemoryMappedFiles get { return _access; } } - [SecurityCritical] protected virtual void Dispose(bool disposing) { if (!_viewHandle.IsClosed) @@ -57,7 +54,6 @@ namespace System.IO.MemoryMappedFiles } } - [SecurityCritical] public void Dispose() { Dispose(true); @@ -66,7 +62,6 @@ namespace System.IO.MemoryMappedFiles public bool IsClosed { - [SecuritySafeCritical] get { return _viewHandle.IsClosed; } } diff --git a/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedViewAccessor.cs b/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedViewAccessor.cs index 86ba829043..b7afcd91ca 100644 --- a/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedViewAccessor.cs +++ b/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedViewAccessor.cs @@ -12,7 +12,6 @@ namespace System.IO.MemoryMappedFiles { private readonly MemoryMappedView _view; - [SecurityCritical] internal MemoryMappedViewAccessor(MemoryMappedView view) { Debug.Assert(view != null, "view is null"); @@ -23,7 +22,6 @@ namespace System.IO.MemoryMappedFiles public SafeMemoryMappedViewHandle SafeMemoryMappedViewHandle { - [SecurityCritical] get { return _view.ViewHandle; } } @@ -32,7 +30,6 @@ namespace System.IO.MemoryMappedFiles get { return _view.PointerOffset; } } - [SecuritySafeCritical] protected override void Dispose(bool disposing) { try @@ -62,7 +59,6 @@ namespace System.IO.MemoryMappedFiles // flush to the disk. // NOTE: This will flush all bytes before and after the view up until an offset that is a // multiple of SystemPageSize. - [SecurityCritical] public void Flush() { if (!IsOpen) diff --git a/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedViewStream.cs b/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedViewStream.cs index bb35994ff6..83605b7f23 100644 --- a/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedViewStream.cs +++ b/external/corefx/src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedViewStream.cs @@ -12,7 +12,6 @@ namespace System.IO.MemoryMappedFiles { private readonly MemoryMappedView _view; - [SecurityCritical] internal unsafe MemoryMappedViewStream(MemoryMappedView view) { Debug.Assert(view != null, "view is null"); @@ -23,7 +22,6 @@ namespace System.IO.MemoryMappedFiles public SafeMemoryMappedViewHandle SafeMemoryMappedViewHandle { - [SecurityCritical] get { return _view.ViewHandle; } } @@ -37,7 +35,6 @@ namespace System.IO.MemoryMappedFiles get { return _view.PointerOffset; } } - [SecuritySafeCritical] protected override void Dispose(bool disposing) { try @@ -65,12 +62,11 @@ namespace System.IO.MemoryMappedFiles // flush to the disk. // NOTE: This will flush all bytes before and after the view up until an offset that is a // multiple of SystemPageSize. - [SecurityCritical] public override void Flush() { if (!CanSeek) { - throw __Error.GetStreamIsClosed(); + throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamIsClosed); } _view.Flush((UIntPtr)Capacity); diff --git a/external/corefx/src/System.IO.Packaging/ref/Configurations.props b/external/corefx/src/System.IO.Packaging/ref/Configurations.props index e7e6149574..b79139610e 100644 --- a/external/corefx/src/System.IO.Packaging/ref/Configurations.props +++ b/external/corefx/src/System.IO.Packaging/ref/Configurations.props @@ -4,10 +4,10 @@ netstandard1.3; net46; + netstandard; $(PackageConfigurations); - netstandard; netfx; diff --git a/external/corefx/src/System.IO.Packaging/ref/System.IO.Packaging.Serialization.cs b/external/corefx/src/System.IO.Packaging/ref/System.IO.Packaging.Serialization.cs new file mode 100644 index 0000000000..e3a3ef19ae --- /dev/null +++ b/external/corefx/src/System.IO.Packaging/ref/System.IO.Packaging.Serialization.cs @@ -0,0 +1,16 @@ +// 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. +// ------------------------------------------------------------------------------ +// Changes to this file must follow the http://aka.ms/api-review process. +// ------------------------------------------------------------------------------ + + +namespace System.IO +{ + public partial class FileFormatException : System.FormatException + { + protected FileFormatException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + } +} diff --git a/external/corefx/src/System.IO.Packaging/ref/System.IO.Packaging.csproj b/external/corefx/src/System.IO.Packaging/ref/System.IO.Packaging.csproj index 090f85efb2..4d26bef00e 100644 --- a/external/corefx/src/System.IO.Packaging/ref/System.IO.Packaging.csproj +++ b/external/corefx/src/System.IO.Packaging/ref/System.IO.Packaging.csproj @@ -4,6 +4,7 @@ {4DD91348-1D1F-4964-B5CB-047D916E79EE} true + $(DefineConstants);netcoreapp @@ -16,6 +17,9 @@ + + + @@ -28,4 +32,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.IO.Packaging/src/Configurations.props b/external/corefx/src/System.IO.Packaging/src/Configurations.props index 965f87ee88..fa2d93a425 100644 --- a/external/corefx/src/System.IO.Packaging/src/Configurations.props +++ b/external/corefx/src/System.IO.Packaging/src/Configurations.props @@ -4,11 +4,11 @@ net46; netstandard1.3; + netstandard; $(PackageConfigurations); netfx-Windows_NT; - netstandard; \ No newline at end of file diff --git a/external/corefx/src/System.IO.Packaging/src/System.IO.Packaging.csproj b/external/corefx/src/System.IO.Packaging/src/System.IO.Packaging.csproj index df0b4051c0..39eccd6686 100644 --- a/external/corefx/src/System.IO.Packaging/src/System.IO.Packaging.csproj +++ b/external/corefx/src/System.IO.Packaging/src/System.IO.Packaging.csproj @@ -9,8 +9,8 @@ true netstandard2.0;$(UAPvNextTFM) + $(DefineConstants);FEATURE_SERIALIZATION - @@ -70,4 +70,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.IO.Packaging/src/System/IO/Packaging/FileFormatException.cs b/external/corefx/src/System.IO.Packaging/src/System/IO/Packaging/FileFormatException.cs index 296d4d611b..ca63837868 100644 --- a/external/corefx/src/System.IO.Packaging/src/System/IO/Packaging/FileFormatException.cs +++ b/external/corefx/src/System.IO.Packaging/src/System/IO/Packaging/FileFormatException.cs @@ -2,12 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if FEATURE_SERIALIZATION +using System.Runtime.Serialization; +#endif + namespace System.IO { /// /// The FileFormatException class is thrown when an input file or a data stream that is supposed to conform /// to a certain file format specification is malformed. /// +#if FEATURE_SERIALIZATION + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")] +#endif public class FileFormatException : FormatException { /// @@ -66,7 +74,7 @@ namespace System.IO /// /// The Uri of a file that caused this error. /// The message that describes the error. - public FileFormatException(Uri sourceUri, String message) + public FileFormatException(Uri sourceUri, string message) : base(message) { _sourceUri = sourceUri; @@ -103,12 +111,32 @@ namespace System.IO /// The Uri of a file that caused this error. /// The message that describes the error. /// The exception that is the cause of the current exception. - public FileFormatException(Uri sourceUri, String message, Exception innerException) + public FileFormatException(Uri sourceUri, string message, Exception innerException) : base(message, innerException) { _sourceUri = sourceUri; } +#if FEATURE_SERIALIZATION + protected FileFormatException(SerializationInfo info, StreamingContext context) : base(info, context) + { + string sourceUriString = info.GetString("SourceUri"); + if (sourceUriString != null) + _sourceUri = new Uri(sourceUriString, UriKind.RelativeOrAbsolute); + } + + /// + /// Sets the SerializationInfo object with the file name and additional exception information. + /// + /// The object that holds the serialized object data. + /// The contextual information about the source or destination. + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("SourceUri", SourceUri?.GetComponents(UriComponents.SerializationInfoString, UriFormat.SafeUnescaped), typeof(string)); + } +#endif + /// /// Returns the name of a file that caused this exception. This property may be equal to an empty string /// if obtaining the file path that caused the error was not possible. diff --git a/external/corefx/src/System.IO.Packaging/src/System/IO/Packaging/InternalRelationshipCollection.cs b/external/corefx/src/System.IO.Packaging/src/System/IO/Packaging/InternalRelationshipCollection.cs index a5c027ec42..26ec972f2c 100644 --- a/external/corefx/src/System.IO.Packaging/src/System/IO/Packaging/InternalRelationshipCollection.cs +++ b/external/corefx/src/System.IO.Packaging/src/System/IO/Packaging/InternalRelationshipCollection.cs @@ -27,6 +27,12 @@ namespace System.IO.Packaging /// internal class InternalRelationshipCollection : IEnumerable { + // Mono will parse a URI starting with '/' as an absolute URI, while .NET Core and + // .NET Framework will parse this as relative. This will break internal relationships + // in packaging. For more information, see + // http://www.mono-project.com/docs/faq/known-issues/urikind-relativeorabsolute/ + private static readonly UriKind DotNetRelativeOrAbsolute = Type.GetType ("Mono.Runtime") == null ? UriKind.RelativeOrAbsolute : (UriKind)300; + #region IEnumerable /// /// Returns an enumerator over all the relationships for a Package or a PackagePart @@ -354,7 +360,7 @@ namespace System.IO.Packaging if (string.IsNullOrEmpty(targetAttributeValue)) throw new XmlException(SR.Format(SR.RequiredRelationshipAttributeMissing, s_targetAttributeName), null, reader.LineNumber, reader.LinePosition); - Uri targetUri = new Uri(targetAttributeValue, UriKind.RelativeOrAbsolute); + Uri targetUri = new Uri(targetAttributeValue, DotNetRelativeOrAbsolute); // Attribute : Type string typeAttributeValue = reader.GetAttribute(s_typeAttributeName); diff --git a/external/corefx/src/System.IO.Packaging/tests/Tests.cs.REMOVED.git-id b/external/corefx/src/System.IO.Packaging/tests/Tests.cs.REMOVED.git-id index c80493d266..90aa8d0d7a 100644 --- a/external/corefx/src/System.IO.Packaging/tests/Tests.cs.REMOVED.git-id +++ b/external/corefx/src/System.IO.Packaging/tests/Tests.cs.REMOVED.git-id @@ -1 +1 @@ -87891326908725ee0e8de1f4e31d1a29b0404d04 \ No newline at end of file +00b5395c859aa603f90ee2e93d565f828161af22 \ No newline at end of file diff --git a/external/corefx/src/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.cs b/external/corefx/src/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.cs index c241eeb9fc..5ddc09c695 100644 --- a/external/corefx/src/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.cs +++ b/external/corefx/src/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.cs @@ -41,9 +41,7 @@ namespace System.IO.Pipes public System.IO.Pipes.PipeAccessRights PipeAccessRights { get { throw null; } } } public static partial class PipesAclExtensions { - [System.Security.SecurityCriticalAttribute] public static System.IO.Pipes.PipeSecurity GetAccessControl(this System.IO.Pipes.PipeStream stream) { throw null; } - [System.Security.SecurityCriticalAttribute] public static void SetAccessControl(this System.IO.Pipes.PipeStream stream, System.IO.Pipes.PipeSecurity pipeSecurity) { } } public partial class PipeSecurity : System.Security.AccessControl.NativeObjectSecurity @@ -56,9 +54,7 @@ namespace System.IO.Pipes public void AddAccessRule(System.IO.Pipes.PipeAccessRule rule) { } public void AddAuditRule(System.IO.Pipes.PipeAuditRule rule) { } public sealed override System.Security.AccessControl.AuditRule AuditRuleFactory(System.Security.Principal.IdentityReference identityReference, int accessMask, bool isInherited, System.Security.AccessControl.InheritanceFlags inheritanceFlags, System.Security.AccessControl.PropagationFlags propagationFlags, System.Security.AccessControl.AuditFlags flags) { throw null; } - [System.Security.SecurityCriticalAttribute] protected internal void Persist(System.Runtime.InteropServices.SafeHandle handle) { } - [System.Security.SecurityCriticalAttribute] protected internal void Persist(string name) { } public bool RemoveAccessRule(System.IO.Pipes.PipeAccessRule rule) { throw null; } public void RemoveAccessRuleSpecific(System.IO.Pipes.PipeAccessRule rule) { } diff --git a/external/corefx/src/System.IO.Pipes.AccessControl/src/System/IO/PipeSecurity.cs b/external/corefx/src/System.IO.Pipes.AccessControl/src/System/IO/PipeSecurity.cs index 24e57ba506..a4b8300709 100644 --- a/external/corefx/src/System.IO.Pipes.AccessControl/src/System/IO/PipeSecurity.cs +++ b/external/corefx/src/System.IO.Pipes.AccessControl/src/System/IO/PipeSecurity.cs @@ -16,7 +16,6 @@ namespace System.IO.Pipes : base(false, ResourceType.KernelObject) { } // Used by PipeStream.GetAccessControl - [System.Security.SecuritySafeCritical] internal PipeSecurity(SafePipeHandle safeHandle, AccessControlSections includeSections) : base(false, ResourceType.KernelObject, safeHandle, includeSections) { } @@ -217,10 +216,6 @@ namespace System.IO.Pipes return persistRules; } - // Use this in your own Persist after you have demanded any appropriate CAS permissions. - // Note that you will want your version to be internal and use a specialized Safe Handle. - [System.Security.SecurityCritical] - //[SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)] protected internal void Persist(SafeHandle handle) { WriteLock(); @@ -237,10 +232,6 @@ namespace System.IO.Pipes } } - // Use this in your own Persist after you have demanded any appropriate CAS permissions. - // Note that you will want your version to be internal. - [System.Security.SecurityCritical] - //[SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)] protected internal void Persist(String name) { WriteLock(); diff --git a/external/corefx/src/System.IO.Pipes.AccessControl/src/System/IO/PipesAclExtensions.cs b/external/corefx/src/System.IO.Pipes.AccessControl/src/System/IO/PipesAclExtensions.cs index 8505b969a1..f6f5078c79 100644 --- a/external/corefx/src/System.IO.Pipes.AccessControl/src/System/IO/PipesAclExtensions.cs +++ b/external/corefx/src/System.IO.Pipes.AccessControl/src/System/IO/PipesAclExtensions.cs @@ -9,7 +9,6 @@ namespace System.IO.Pipes { public static class PipesAclExtensions { - [System.Security.SecurityCritical] public static PipeSecurity GetAccessControl(this PipeStream stream) { // Checks that State != WaitingToConnect and State != Closed @@ -19,7 +18,6 @@ namespace System.IO.Pipes return new PipeSecurity(handle, AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group); } - [System.Security.SecurityCritical] public static void SetAccessControl(this PipeStream stream, PipeSecurity pipeSecurity) { if (pipeSecurity == null) diff --git a/external/corefx/src/System.IO.Pipes.AccessControl/src/System/IO/PipesAclExtensions.net46.cs b/external/corefx/src/System.IO.Pipes.AccessControl/src/System/IO/PipesAclExtensions.net46.cs index 42d0d079fd..2ff970f702 100644 --- a/external/corefx/src/System.IO.Pipes.AccessControl/src/System/IO/PipesAclExtensions.net46.cs +++ b/external/corefx/src/System.IO.Pipes.AccessControl/src/System/IO/PipesAclExtensions.net46.cs @@ -9,13 +9,11 @@ namespace System.IO.Pipes { public static class PipesAclExtensions { - [System.Security.SecurityCritical] public static PipeSecurity GetAccessControl(PipeStream stream) { return stream.GetAccessControl(); } - [System.Security.SecurityCritical] public static void SetAccessControl(PipeStream stream, PipeSecurity pipeSecurity) { stream.SetAccessControl(pipeSecurity); diff --git a/external/corefx/src/System.IO.Pipes/System.IO.Pipes.sln b/external/corefx/src/System.IO.Pipes/System.IO.Pipes.sln index 92cbb2522b..863700d830 100644 --- a/external/corefx/src/System.IO.Pipes/System.IO.Pipes.sln +++ b/external/corefx/src/System.IO.Pipes/System.IO.Pipes.sln @@ -31,10 +31,10 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {142469EC-D665-4FE2-845A-FDA69F9CC557}.Debug|Any CPU.ActiveCfg = netstandard-Windows_NT-Debug|Any CPU - {142469EC-D665-4FE2-845A-FDA69F9CC557}.Debug|Any CPU.Build.0 = netstandard-Windows_NT-Debug|Any CPU - {142469EC-D665-4FE2-845A-FDA69F9CC557}.Release|Any CPU.ActiveCfg = netstandard-Windows_NT-Release|Any CPU - {142469EC-D665-4FE2-845A-FDA69F9CC557}.Release|Any CPU.Build.0 = netstandard-Windows_NT-Release|Any CPU + {142469EC-D665-4FE2-845A-FDA69F9CC557}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {142469EC-D665-4FE2-845A-FDA69F9CC557}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {142469EC-D665-4FE2-845A-FDA69F9CC557}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {142469EC-D665-4FE2-845A-FDA69F9CC557}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU {7A8B72D7-FACD-4E96-8390-F2D2FE269687}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU {7A8B72D7-FACD-4E96-8390-F2D2FE269687}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU {7A8B72D7-FACD-4E96-8390-F2D2FE269687}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU diff --git a/external/corefx/src/System.IO.Pipes/src/Microsoft/Win32/SafeHandles/SafePipeHandle.Unix.cs b/external/corefx/src/System.IO.Pipes/src/Microsoft/Win32/SafeHandles/SafePipeHandle.Unix.cs index 62f22923e5..5b8d5ac6a2 100644 --- a/external/corefx/src/System.IO.Pipes/src/Microsoft/Win32/SafeHandles/SafePipeHandle.Unix.cs +++ b/external/corefx/src/System.IO.Pipes/src/Microsoft/Win32/SafeHandles/SafePipeHandle.Unix.cs @@ -76,7 +76,6 @@ namespace Microsoft.Win32.SafeHandles public override bool IsInvalid { - [SecurityCritical] get { return (long)handle < 0 && _namedPipeSocket == null; } } } diff --git a/external/corefx/src/System.IO.Pipes/src/Microsoft/Win32/SafeHandles/SafePipeHandle.cs b/external/corefx/src/System.IO.Pipes/src/Microsoft/Win32/SafeHandles/SafePipeHandle.cs index eac4bc252a..0591719faf 100644 --- a/external/corefx/src/System.IO.Pipes/src/Microsoft/Win32/SafeHandles/SafePipeHandle.cs +++ b/external/corefx/src/System.IO.Pipes/src/Microsoft/Win32/SafeHandles/SafePipeHandle.cs @@ -8,7 +8,6 @@ using System.Security; namespace Microsoft.Win32.SafeHandles { - [SecurityCritical] public sealed partial class SafePipeHandle : SafeHandleZeroOrMinusOneIsInvalid { internal SafePipeHandle() diff --git a/external/corefx/src/System.IO.Pipes/src/Resources/Strings.resx b/external/corefx/src/System.IO.Pipes/src/Resources/Strings.resx index e164c8657f..d9d9e05e7a 100644 --- a/external/corefx/src/System.IO.Pipes/src/Resources/Strings.resx +++ b/external/corefx/src/System.IO.Pipes/src/Resources/Strings.resx @@ -1,5 +1,64 @@  + @@ -103,9 +162,6 @@ maxNumberOfServerInstances must either be a value between 1 and 254, or NamedPipeServerStream.MaxAllowedServerInstances (to obtain the maximum number allowed by system resources). - - The path '{0}' is of an invalid length for use with domain sockets on this platform. The length must be between 1 and {1} characters, inclusive. - Positive number required. @@ -211,4 +267,10 @@ The operating system returned error '{0}' indicating that the operation is not supported. + + All pipe instances are busy. + + + The path '{0}' is too long, or a component of the specified path is too long. + \ No newline at end of file diff --git a/external/corefx/src/System.IO.Pipes/src/System.IO.Pipes.csproj b/external/corefx/src/System.IO.Pipes/src/System.IO.Pipes.csproj index f0c917351b..9a83ec00fe 100644 --- a/external/corefx/src/System.IO.Pipes/src/System.IO.Pipes.csproj +++ b/external/corefx/src/System.IO.Pipes/src/System.IO.Pipes.csproj @@ -164,7 +164,6 @@ - Common\Microsoft\Win32\SafeHandles\SafeFileHandleHelper.Unix.cs @@ -189,9 +188,6 @@ Common\Interop\Unix\Interop.FLock.cs - - Common\Interop\Unix\Interop.GetDomainSocketSizes.cs - Common\Interop\Unix\Interop.GetHostName.cs @@ -245,9 +241,10 @@ - + + @@ -263,4 +260,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/AnonymousPipeClientStream.cs b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/AnonymousPipeClientStream.cs index 2a40b65059..c59947c7d3 100644 --- a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/AnonymousPipeClientStream.cs +++ b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/AnonymousPipeClientStream.cs @@ -14,14 +14,12 @@ namespace System.IO.Pipes /// public sealed partial class AnonymousPipeClientStream : PipeStream { - [SecuritySafeCritical] [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "string", Justification = "By design")] public AnonymousPipeClientStream(String pipeHandleAsString) : this(PipeDirection.In, pipeHandleAsString) { } - [SecurityCritical] [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "string", Justification = "By design")] public AnonymousPipeClientStream(PipeDirection direction, String pipeHandleAsString) : base(direction, 0) @@ -53,7 +51,6 @@ namespace System.IO.Pipes Init(direction, safePipeHandle); } - [SecurityCritical] public AnonymousPipeClientStream(PipeDirection direction, SafePipeHandle safePipeHandle) : base(direction, 0) { @@ -73,7 +70,6 @@ namespace System.IO.Pipes Init(direction, safePipeHandle); } - [SecuritySafeCritical] private void Init(PipeDirection direction, SafePipeHandle safePipeHandle) { Debug.Assert(direction != PipeDirection.InOut, "anonymous pipes are unidirectional, caller should have verified before calling Init"); @@ -93,13 +89,11 @@ namespace System.IO.Pipes // which P/Invokes (and sometimes fails). public override PipeTransmissionMode TransmissionMode { - [SecurityCritical] get { return PipeTransmissionMode.Byte; } } public override PipeTransmissionMode ReadMode { - [SecurityCritical] set { CheckPipePropertyOperations(); diff --git a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/AnonymousPipeServerStream.Unix.cs b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/AnonymousPipeServerStream.Unix.cs index 00ce4b4422..2ba5605ddc 100644 --- a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/AnonymousPipeServerStream.Unix.cs +++ b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/AnonymousPipeServerStream.Unix.cs @@ -14,7 +14,6 @@ namespace System.IO.Pipes public sealed partial class AnonymousPipeServerStream : PipeStream { // Creates the anonymous pipe. - [SecurityCritical] private unsafe void Create(PipeDirection direction, HandleInheritability inheritability, int bufferSize) { Debug.Assert(direction != PipeDirection.InOut, "Anonymous pipe direction shouldn't be InOut"); diff --git a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/AnonymousPipeServerStream.Windows.cs b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/AnonymousPipeServerStream.Windows.cs index 52bb254c70..79fd46a218 100644 --- a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/AnonymousPipeServerStream.Windows.cs +++ b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/AnonymousPipeServerStream.Windows.cs @@ -15,7 +15,6 @@ namespace System.IO.Pipes public sealed partial class AnonymousPipeServerStream : PipeStream { // Creates the anonymous pipe. - [SecurityCritical] private void Create(PipeDirection direction, HandleInheritability inheritability, int bufferSize) { Debug.Assert(direction != PipeDirection.InOut, "Anonymous pipe direction shouldn't be InOut"); diff --git a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/AnonymousPipeServerStream.cs b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/AnonymousPipeServerStream.cs index 3c884d9cf9..960679437e 100644 --- a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/AnonymousPipeServerStream.cs +++ b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/AnonymousPipeServerStream.cs @@ -16,26 +16,22 @@ namespace System.IO.Pipes private SafePipeHandle _clientHandle; private bool _clientHandleExposed; - [SecuritySafeCritical] public AnonymousPipeServerStream() : this(PipeDirection.Out, HandleInheritability.None, 0) { } - [SecuritySafeCritical] public AnonymousPipeServerStream(PipeDirection direction) : this(direction, HandleInheritability.None, 0) { } - [SecuritySafeCritical] public AnonymousPipeServerStream(PipeDirection direction, HandleInheritability inheritability) : this(direction, inheritability, 0) { } // Create an AnonymousPipeServerStream from two existing pipe handles. - [SecuritySafeCritical] public AnonymousPipeServerStream(PipeDirection direction, SafePipeHandle serverSafePipeHandle, SafePipeHandle clientSafePipeHandle) : base(direction, 0) { @@ -71,7 +67,6 @@ namespace System.IO.Pipes // bufferSize is used as a suggestion; specify 0 to let OS decide // This constructor instantiates the PipeSecurity using just the inheritability flag - [SecuritySafeCritical] public AnonymousPipeServerStream(PipeDirection direction, HandleInheritability inheritability, int bufferSize) : base(direction, bufferSize) { @@ -94,7 +89,6 @@ namespace System.IO.Pipes // This method should exist until we add a first class way of passing handles between parent and child // processes. For now, people do it via command line arguments. - [SecurityCritical] [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Runtime.InteropServices.SafeHandle.DangerousGetHandle", Justification = "By design")] public String GetClientHandleAsString() { @@ -105,7 +99,6 @@ namespace System.IO.Pipes public SafePipeHandle ClientSafePipeHandle { - [SecurityCritical] get { _clientHandleExposed = true; @@ -124,7 +117,6 @@ namespace System.IO.Pipes // // Right now, this is the best signal to set the anonymous pipe as connected; if this is called, we // know the client has been passed the handle and so the connection is live. - [SecurityCritical] public void DisposeLocalCopyOfClientHandle() { if (_clientHandle != null && !_clientHandle.IsClosed) @@ -133,7 +125,6 @@ namespace System.IO.Pipes } } - [SecurityCritical] protected override void Dispose(bool disposing) { try @@ -153,13 +144,11 @@ namespace System.IO.Pipes // Anonymous pipes do not support message mode so there is no need to use the base version that P/Invokes here. public override PipeTransmissionMode TransmissionMode { - [SecurityCritical] get { return PipeTransmissionMode.Byte; } } public override PipeTransmissionMode ReadMode { - [SecurityCritical] set { CheckPipePropertyOperations(); diff --git a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Unix.cs b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Unix.cs index 871fa70257..bd609d2f0b 100644 --- a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Unix.cs +++ b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Unix.cs @@ -16,7 +16,6 @@ namespace System.IO.Pipes /// public sealed partial class NamedPipeClientStream : PipeStream { - [SecurityCritical] private bool TryConnect(int timeout, CancellationToken cancellationToken) { // timeout and cancellationToken aren't used as Connect will be very fast, @@ -33,10 +32,7 @@ namespace System.IO.Pipes } catch (SocketException e) { - if (clientHandle != null) - { - clientHandle.Dispose(); - } + clientHandle?.Dispose(); socket.Dispose(); switch (e.SocketErrorCode) @@ -60,7 +56,6 @@ namespace System.IO.Pipes public int NumberOfServerInstances { - [SecurityCritical] [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Security model of pipes: demand at creation but no subsequent demands")] get { diff --git a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Windows.cs b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Windows.cs index a48e5665dc..286e510eee 100644 --- a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Windows.cs +++ b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Windows.cs @@ -21,7 +21,6 @@ namespace System.IO.Pipes // on the server end, but WaitForConnection will not return until we have returned. Any data written to the // pipe by us after we have connected but before the server has called WaitForConnection will be available // to the server after it calls WaitForConnection. - [SecurityCritical] private bool TryConnect(int timeout, CancellationToken cancellationToken) { Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = PipeStream.GetSecAttrs(_inheritability); @@ -66,23 +65,13 @@ namespace System.IO.Pipes { errorCode = Marshal.GetLastWin32Error(); - // Server is not yet created - if (errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND) + // Server is not yet created or a timeout occurred before a pipe instance was available. + if (errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND || + errorCode == Interop.Errors.ERROR_SEM_TIMEOUT) { return false; } - // The timeout has expired. - if (errorCode == Interop.Errors.ERROR_SUCCESS) - { - if (cancellationToken.CanBeCanceled) - { - // It may not be real timeout. - return false; - } - throw new TimeoutException(); - } - throw Win32Marshal.GetExceptionForWin32Error(errorCode); } @@ -119,7 +108,6 @@ namespace System.IO.Pipes public int NumberOfServerInstances { - [SecurityCritical] [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Security model of pipes: demand at creation but no subsequent demands")] get { diff --git a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.cs b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.cs index ec0ade9a3b..cb5bda0977 100644 --- a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.cs +++ b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.cs @@ -26,38 +26,32 @@ namespace System.IO.Pipes private readonly PipeDirection _direction; // Creates a named pipe client using default server (same machine, or "."), and PipeDirection.InOut - [SecuritySafeCritical] public NamedPipeClientStream(String pipeName) : this(".", pipeName, PipeDirection.InOut, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None) { } - [SecuritySafeCritical] public NamedPipeClientStream(String serverName, String pipeName) : this(serverName, pipeName, PipeDirection.InOut, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None) { } - [SecuritySafeCritical] public NamedPipeClientStream(String serverName, String pipeName, PipeDirection direction) : this(serverName, pipeName, direction, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None) { } - [SecuritySafeCritical] public NamedPipeClientStream(String serverName, String pipeName, PipeDirection direction, PipeOptions options) : this(serverName, pipeName, direction, options, TokenImpersonationLevel.None, HandleInheritability.None) { } - [SecuritySafeCritical] public NamedPipeClientStream(String serverName, String pipeName, PipeDirection direction, PipeOptions options, TokenImpersonationLevel impersonationLevel) : this(serverName, pipeName, direction, options, impersonationLevel, HandleInheritability.None) { } - [SecuritySafeCritical] public NamedPipeClientStream(String serverName, String pipeName, PipeDirection direction, PipeOptions options, TokenImpersonationLevel impersonationLevel, HandleInheritability inheritability) : base(direction, 0) @@ -99,7 +93,6 @@ namespace System.IO.Pipes } // Create a NamedPipeClientStream from an existing server pipe handle. - [SecuritySafeCritical] public NamedPipeClientStream(PipeDirection direction, bool isAsync, bool isConnected, SafePipeHandle safePipeHandle) : base(direction, 0) { @@ -142,7 +135,6 @@ namespace System.IO.Pipes ConnectInternal(timeout, CancellationToken.None, Environment.TickCount); } - [SecurityCritical] private void ConnectInternal(int timeout, CancellationToken cancellationToken, int startTime) { // This is the main connection loop. It will loop until the timeout expires. @@ -213,7 +205,6 @@ namespace System.IO.Pipes // override because named pipe clients can't get/set properties when waiting to connect // or broken - [SecurityCritical] protected internal override void CheckPipePropertyOperations() { base.CheckPipePropertyOperations(); diff --git a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs index 788177d85a..e2e5b7a88f 100644 --- a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs +++ b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs @@ -3,30 +3,25 @@ // See the LICENSE file in the project root for more information. using Microsoft.Win32.SafeHandles; +using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Security; -using System.Security.Permissions; using System.Threading; using System.Threading.Tasks; namespace System.IO.Pipes { - /// - /// Named pipe server - /// public sealed partial class NamedPipeServerStream : PipeStream { - private string _path; + private SharedServer _instance; private PipeDirection _direction; private PipeOptions _options; private int _inBufferSize; private int _outBufferSize; private HandleInheritability _inheritability; - [SecurityCritical] private void Create(string pipeName, PipeDirection direction, int maxNumberOfServerInstances, PipeTransmissionMode transmissionMode, PipeOptions options, int inBufferSize, int outBufferSize, HandleInheritability inheritability) @@ -43,10 +38,11 @@ namespace System.IO.Pipes throw new PlatformNotSupportedException(SR.PlatformNotSupported_MessageTransmissionMode); } - // NOTE: We don't have a good way to enforce maxNumberOfServerInstances, and don't currently try. - // It's a Windows-specific concept. + // We don't have a good way to enforce maxNumberOfServerInstances across processes; we only factor it in + // for streams created in this process. Between processes, we behave similarly to maxNumberOfServerInstances == 1, + // in that the second process to come along and create a stream will find the pipe already in existence and will fail. + _instance = SharedServer.Get(GetPipePath(".", pipeName), maxNumberOfServerInstances); - _path = GetPipePath(".", pipeName); _direction = direction; _options = options; _inBufferSize = inBufferSize; @@ -54,8 +50,6 @@ namespace System.IO.Pipes _inheritability = inheritability; } - [SecurityCritical] - [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Security model of pipes: demand at creation but no subsequent demands")] public void WaitForConnection() { CheckConnectOperationsServer(); @@ -64,40 +58,10 @@ namespace System.IO.Pipes throw new InvalidOperationException(SR.InvalidOperation_PipeAlreadyConnected); } - // Binding to an existing path fails, so we need to remove anything left over at this location. - // There's of course a race condition here, where it could be recreated by someone else between this - // deletion and the bind below, in which case we'll simply let the bind fail and throw. - Interop.Sys.Unlink(_path); // ignore any failures - var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); - try - { - socket.Bind(new UnixDomainSocketEndPoint(_path)); - socket.Listen(1); - - Socket acceptedSocket = socket.Accept(); - SafePipeHandle serverHandle = new SafePipeHandle(acceptedSocket); - try - { - ConfigureSocket(acceptedSocket, serverHandle, _direction, _inBufferSize, _outBufferSize, _inheritability); - } - catch - { - serverHandle.Dispose(); - acceptedSocket.Dispose(); - throw; - } - - InitializeHandle(serverHandle, isExposed: false, isAsync: (_options & PipeOptions.Asynchronous) != 0); - State = PipeState.Connected; - } - finally - { - // Bind will have created a file. Now that the client is connected, it's no longer necessary, so get rid of it. - Interop.Sys.Unlink(_path); // ignore any failures; worst case is we leave a tmp file - - // Clean up the listening socket - socket.Dispose(); - } + // Use and block on AcceptAsync() rather than using Accept() in order to provide + // behavior more akin to Windows if the Stream is closed while a connection is pending. + Socket accepted = _instance.ListeningSocket.AcceptAsync().GetAwaiter().GetResult(); + HandleAcceptedSocket(accepted); } public Task WaitForConnectionAsync(CancellationToken cancellationToken) @@ -111,41 +75,32 @@ namespace System.IO.Pipes return cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) : WaitForConnectionAsyncCore(); + + async Task WaitForConnectionAsyncCore() => + HandleAcceptedSocket(await _instance.ListeningSocket.AcceptAsync().ConfigureAwait(false)); } - private async Task WaitForConnectionAsyncCore() - { - // This is the same implementation as is in WaitForConnection(), but using Socket.AcceptAsync - // instead of Socket.Accept. - - // Binding to an existing path fails, so we need to remove anything left over at this location. - // There's of course a race condition here, where it could be recreated by someone else between this - // deletion and the bind below, in which case we'll simply let the bind fail and throw. - Interop.Sys.Unlink(_path); // ignore any failures - var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); + private void HandleAcceptedSocket(Socket acceptedSocket) + { + var serverHandle = new SafePipeHandle(acceptedSocket); try { - socket.Bind(new UnixDomainSocketEndPoint(_path)); - socket.Listen(1); - - Socket acceptedSocket = await socket.AcceptAsync().ConfigureAwait(false); - SafePipeHandle serverHandle = new SafePipeHandle(acceptedSocket); ConfigureSocket(acceptedSocket, serverHandle, _direction, _inBufferSize, _outBufferSize, _inheritability); - - InitializeHandle(serverHandle, isExposed: false, isAsync: (_options & PipeOptions.Asynchronous) != 0); - State = PipeState.Connected; } - finally + catch { - // Bind will have created a file. Now that the client is connected, it's no longer necessary, so get rid of it. - Interop.Sys.Unlink(_path); // ignore any failures; worst case is we leave a tmp file - - // Clean up the listening socket - socket.Dispose(); + serverHandle.Dispose(); + acceptedSocket.Dispose(); + throw; } + + InitializeHandle(serverHandle, isExposed: false, isAsync: (_options & PipeOptions.Asynchronous) != 0); + State = PipeState.Connected; } - [SecurityCritical] + internal override void DisposeCore(bool disposing) => + Interlocked.Exchange(ref _instance, null)?.Dispose(disposing); // interlocked to avoid shared state problems from erroneous double/concurrent disposes + public void Disconnect() { CheckDisconnectOperations(); @@ -157,7 +112,6 @@ namespace System.IO.Pipes // Gets the username of the connected client. Not that we will not have access to the client's // username until it has written at least once to the pipe (and has set its impersonationLevel // argument appropriately). - [SecurityCritical] public string GetImpersonationUserName() { CheckWriteOperations(); @@ -242,7 +196,111 @@ namespace System.IO.Pipes Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo(); return error.Error == Interop.Error.ENOTSUP ? new PlatformNotSupportedException(SR.Format(SR.PlatformNotSupported_OperatingSystemError, nameof(Interop.Error.ENOTSUP))) : - Interop.GetExceptionForIoErrno(error, _path); + Interop.GetExceptionForIoErrno(error, _instance?.PipeName); + } + + /// Shared resources for NamedPipeServerStreams in the same process created for the same path. + private sealed class SharedServer + { + /// Path to shared instance mapping. + private static readonly Dictionary s_servers = new Dictionary(); + + /// The pipe name for this instance. + internal string PipeName { get; } + /// Gets the shared socket used to accept connections. + internal Socket ListeningSocket { get; } + + /// The maximum number of server streams allowed to use this instance concurrently. + private readonly int _maxCount; + /// The concurrent number of concurrent streams using this instance. + private int _currentCount; + + internal static SharedServer Get(string path, int maxCount) + { + Debug.Assert(!string.IsNullOrEmpty(path)); + Debug.Assert(maxCount >= 1); + + lock (s_servers) + { + SharedServer server; + if (s_servers.TryGetValue(path, out server)) + { + // On Windows, if a subsequent server stream is created for the same pipe and with a different + // max count, the subsequent count is largely ignored in that it doesn't change the number of + // allowed concurrent instances, however that particular instance being created does take its + // own into account, so if its creation would put it over either the original or its own limit, + // it's an error that results in an exception. We do the same for Unix here. + if (server._currentCount == server._maxCount) + { + throw new IOException(SR.IO_AllPipeInstancesAreBusy); + } + else if (server._currentCount == maxCount) + { + throw new UnauthorizedAccessException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, path)); + } + } + else + { + // No instance exists yet for this path. Create one a new. + server = new SharedServer(path, maxCount); + s_servers.Add(path, server); + } + + Debug.Assert(server._currentCount >= 0 && server._currentCount < server._maxCount); + server._currentCount++; + return server; + } + } + + internal void Dispose(bool disposing) + { + lock (s_servers) + { + Debug.Assert(_currentCount >= 1 && _currentCount <= _maxCount); + + if (_currentCount == 1) + { + bool removed = s_servers.Remove(PipeName); + Debug.Assert(removed); + + Interop.Sys.Unlink(PipeName); // ignore any failures + + if (disposing) + { + ListeningSocket.Dispose(); + } + } + else + { + _currentCount--; + } + } + } + + private SharedServer(string path, int maxCount) + { + // Binding to an existing path fails, so we need to remove anything left over at this location. + // There's of course a race condition here, where it could be recreated by someone else between this + // deletion and the bind below, in which case we'll simply let the bind fail and throw. + Interop.Sys.Unlink(path); // ignore any failures + + // Start listening for connections on the path. + var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); + try + { + socket.Bind(new UnixDomainSocketEndPoint(path)); + socket.Listen(int.MaxValue); + } + catch + { + socket.Dispose(); + throw; + } + + PipeName = path; + ListeningSocket = socket; + _maxCount = maxCount; + } } } } diff --git a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Windows.cs b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Windows.cs index f9b62ee23b..5f8fe4215d 100644 --- a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Windows.cs +++ b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Windows.cs @@ -20,7 +20,6 @@ namespace System.IO.Pipes /// public sealed partial class NamedPipeServerStream : PipeStream { - [SecurityCritical] private void Create(string pipeName, PipeDirection direction, int maxNumberOfServerInstances, PipeTransmissionMode transmissionMode, PipeOptions options, int inBufferSize, int outBufferSize, HandleInheritability inheritability) @@ -71,7 +70,6 @@ namespace System.IO.Pipes // was called (but not before this server is been created, or, if we were servicing another client, // not before we called Disconnect), in which case, there may be some buffer already in the pipe waiting // for us to read. See NamedPipeClientStream.Connect for more information. - [SecurityCritical] [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Security model of pipes: demand at creation but no subsequent demands")] public void WaitForConnection() { @@ -122,7 +120,6 @@ namespace System.IO.Pipes return WaitForConnectionCoreAsync(cancellationToken); } - [SecurityCritical] public void Disconnect() { CheckDisconnectOperations(); @@ -139,7 +136,6 @@ namespace System.IO.Pipes // Gets the username of the connected client. Not that we will not have access to the client's // username until it has written at least once to the pipe (and has set its impersonationLevel // argument appropriately). - [SecurityCritical] public String GetImpersonationUserName() { CheckWriteOperations(); @@ -230,7 +226,6 @@ namespace System.IO.Pipes internal int _impersonateErrorCode; internal int _revertImpersonateErrorCode; - [SecurityCritical] internal ExecuteHelper(PipeStreamImpersonationWorker userCode, SafePipeHandle handle) { _userCode = userCode; diff --git a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.cs b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.cs index a5622b0578..28476c4d77 100644 --- a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.cs +++ b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.cs @@ -18,37 +18,31 @@ namespace System.IO.Pipes // Use the maximum number of server instances that the system resources allow public const int MaxAllowedServerInstances = -1; - [SecuritySafeCritical] public NamedPipeServerStream(String pipeName) : this(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.None, 0, 0, HandleInheritability.None) { } - [SecuritySafeCritical] public NamedPipeServerStream(String pipeName, PipeDirection direction) : this(pipeName, direction, 1, PipeTransmissionMode.Byte, PipeOptions.None, 0, 0, HandleInheritability.None) { } - [SecuritySafeCritical] public NamedPipeServerStream(String pipeName, PipeDirection direction, int maxNumberOfServerInstances) : this(pipeName, direction, maxNumberOfServerInstances, PipeTransmissionMode.Byte, PipeOptions.None, 0, 0, HandleInheritability.None) { } - [SecuritySafeCritical] public NamedPipeServerStream(String pipeName, PipeDirection direction, int maxNumberOfServerInstances, PipeTransmissionMode transmissionMode) : this(pipeName, direction, maxNumberOfServerInstances, transmissionMode, PipeOptions.None, 0, 0, HandleInheritability.None) { } - [SecuritySafeCritical] public NamedPipeServerStream(String pipeName, PipeDirection direction, int maxNumberOfServerInstances, PipeTransmissionMode transmissionMode, PipeOptions options) : this(pipeName, direction, maxNumberOfServerInstances, transmissionMode, options, 0, 0, HandleInheritability.None) { } - [SecuritySafeCritical] public NamedPipeServerStream(String pipeName, PipeDirection direction, int maxNumberOfServerInstances, PipeTransmissionMode transmissionMode, PipeOptions options, int inBufferSize, int outBufferSize) : this(pipeName, direction, maxNumberOfServerInstances, transmissionMode, options, inBufferSize, outBufferSize, HandleInheritability.None) { @@ -81,7 +75,6 @@ namespace System.IO.Pipes /// Whether handle is inheritable /// Combination (logical OR) of PipeAccessRights.TakeOwnership, /// PipeAccessRights.AccessSystemSecurity, and PipeAccessRights.ChangePermissions - [SecuritySafeCritical] private NamedPipeServerStream(String pipeName, PipeDirection direction, int maxNumberOfServerInstances, PipeTransmissionMode transmissionMode, PipeOptions options, int inBufferSize, int outBufferSize, HandleInheritability inheritability) @@ -124,7 +117,6 @@ namespace System.IO.Pipes } // Create a NamedPipeServerStream from an existing server pipe handle. - [SecuritySafeCritical] public NamedPipeServerStream(PipeDirection direction, bool isAsync, bool isConnected, SafePipeHandle safePipeHandle) : base(direction, PipeTransmissionMode.Byte, 0) { @@ -163,7 +155,6 @@ namespace System.IO.Pipes TaskToApm.End(asyncResult); // Server can only connect from Disconnected state - [SecurityCritical] [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Consistent with security model")] private void CheckConnectOperationsServer() { @@ -186,7 +177,6 @@ namespace System.IO.Pipes } // Server is allowed to disconnect from connected and broken states - [SecurityCritical] private void CheckDisconnectOperations() { if (State == PipeState.WaitingToConnect) diff --git a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/PipeCompletionSource.cs b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/PipeCompletionSource.cs index 9ca9e011df..c0d462cd4a 100644 --- a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/PipeCompletionSource.cs +++ b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/PipeCompletionSource.cs @@ -52,7 +52,7 @@ namespace System.IO.Pipes internal NativeOverlapped* Overlapped { - [SecurityCritical]get { return _overlapped; } + get { return _overlapped; } } internal void RegisterForCancellation(CancellationToken cancellationToken) diff --git a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Unix.cs b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Unix.cs index 84fc05a8fb..1f4d2a9e26 100644 --- a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Unix.cs +++ b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Unix.cs @@ -6,6 +6,7 @@ using Microsoft.Win32.SafeHandles; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Net.Sockets; +using System.Runtime.InteropServices; using System.Security; using System.Threading; using System.Threading.Tasks; @@ -49,10 +50,14 @@ namespace System.IO.Pipes throw new PlatformNotSupportedException(SR.PlatformNotSupproted_InvalidNameChars); } - // Return the pipe path. The pipe is created directly under %TMPDIR%. We don't bother - // putting it into subdirectories, as the pipe will only exist on disk for the - // duration between when the server starts listening and the client connects, after - // which the pipe will be deleted. + // Return the pipe path. The pipe is created directly under %TMPDIR%. We previously + // didn't put it into a subdirectory because it only existed on disk for the duration + // between when the server started listening in WaitForConnection and when the client + // connected, after which the pipe was deleted. We now create the pipe when the + // server stream is created, which leaves it on disk longer, but we can't change the + // naming scheme used as that breaks the ability for code running on an older + // runtime to connect to code running on the newer runtime. That means we're stuck + // with a tmp file for the lifetime of the server stream. return s_pipePrefix + pipeName; } @@ -77,13 +82,12 @@ namespace System.IO.Pipes /// Initializes the handle to be used asynchronously. /// The handle. - [SecurityCritical] private void InitializeAsyncHandle(SafePipeHandle handle) { // nop } - private void UninitializeAsyncHandle() + internal virtual void DisposeCore(bool disposing) { // nop } @@ -111,7 +115,7 @@ namespace System.IO.Pipes } // For anonymous pipes, read from the file descriptor. - fixed (byte* bufPtr = &buffer.DangerousGetPinnableReference()) + fixed (byte* bufPtr = &MemoryMarshal.GetReference(buffer)) { int result = CheckPipeCall(Interop.Sys.Read(_handle, bufPtr, buffer.Length)); Debug.Assert(result <= buffer.Length); @@ -146,7 +150,7 @@ namespace System.IO.Pipes } // For anonymous pipes, write the file descriptor. - fixed (byte* bufPtr = &buffer.DangerousGetPinnableReference()) + fixed (byte* bufPtr = &MemoryMarshal.GetReference(buffer)) { while (buffer.Length > 0) { @@ -220,7 +224,7 @@ namespace System.IO.Pipes // accepts a Memory in the near future. byte[] buffer; int offset, count; - if (source.DangerousTryGetArray(out ArraySegment segment)) + if (MemoryMarshal.TryGetArray(source, out ArraySegment segment)) { buffer = segment.Array; offset = segment.Offset; @@ -264,7 +268,6 @@ namespace System.IO.Pipes } // Blocks until the other end of the pipe has read in all written buffer. - [SecurityCritical] public void WaitForPipeDrain() { CheckWriteOperations(); @@ -284,7 +287,6 @@ namespace System.IO.Pipes // override this in cases where only one mode is legal (such as anonymous pipes) public virtual PipeTransmissionMode TransmissionMode { - [SecurityCritical] [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Security model of pipes: demand at creation but no subsequent demands")] get { @@ -297,7 +299,6 @@ namespace System.IO.Pipes // access. If that passes, call to GetNamedPipeInfo will succeed. public virtual int InBufferSize { - [SecurityCritical] [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] get { @@ -316,7 +317,6 @@ namespace System.IO.Pipes // the ctor. public virtual int OutBufferSize { - [SecurityCritical] [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Security model of pipes: demand at creation but no subsequent demands")] get { @@ -331,13 +331,11 @@ namespace System.IO.Pipes public virtual PipeTransmissionMode ReadMode { - [SecurityCritical] get { CheckPipePropertyOperations(); return PipeTransmissionMode.Byte; // Unix pipes are only byte-based, not message-based } - [SecurityCritical] [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Security model of pipes: demand at creation but no subsequent demands")] set { diff --git a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Windows.cs b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Windows.cs index ade06d3870..7f5e654bc8 100644 --- a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Windows.cs +++ b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Windows.cs @@ -47,13 +47,14 @@ namespace System.IO.Pipes _threadPoolBinding = ThreadPoolBoundHandle.BindHandle(handle); } - private void UninitializeAsyncHandle() + private void DisposeCore(bool disposing) { - if (_threadPoolBinding != null) - _threadPoolBinding.Dispose(); + if (disposing) + { + _threadPoolBinding?.Dispose(); + } } - [SecurityCritical] private unsafe int ReadCore(Span buffer) { int errorCode = 0; @@ -80,7 +81,6 @@ namespace System.IO.Pipes return r; } - [SecuritySafeCritical] private Task ReadAsyncCore(Memory buffer, CancellationToken cancellationToken) { var completionSource = new ReadWriteCompletionSource(this, buffer, isWrite: false); @@ -135,7 +135,6 @@ namespace System.IO.Pipes return completionSource.Task; } - [SecurityCritical] private unsafe void WriteCore(ReadOnlySpan buffer) { int errorCode = 0; @@ -148,7 +147,6 @@ namespace System.IO.Pipes Debug.Assert(r >= 0, "PipeStream's WriteCore is likely broken."); } - [SecuritySafeCritical] private Task WriteAsyncCore(ReadOnlyMemory buffer, CancellationToken cancellationToken) { var completionSource = new ReadWriteCompletionSource(this, buffer, isWrite: true); @@ -182,7 +180,6 @@ namespace System.IO.Pipes } // Blocks until the other end of the pipe has read in all written buffer. - [SecurityCritical] public void WaitForPipeDrain() { CheckWriteOperations(); @@ -202,7 +199,6 @@ namespace System.IO.Pipes // override this in cases where only one mode is legal (such as anonymous pipes) public virtual PipeTransmissionMode TransmissionMode { - [SecurityCritical] [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Security model of pipes: demand at creation but no subsequent demands")] get { @@ -236,7 +232,6 @@ namespace System.IO.Pipes // access. If that passes, call to GetNamedPipeInfo will succeed. public virtual int InBufferSize { - [SecurityCritical] [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] get { @@ -262,7 +257,6 @@ namespace System.IO.Pipes // the ctor. public virtual int OutBufferSize { - [SecurityCritical] [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Security model of pipes: demand at creation but no subsequent demands")] get { @@ -291,7 +285,6 @@ namespace System.IO.Pipes public virtual PipeTransmissionMode ReadMode { - [SecurityCritical] get { CheckPipePropertyOperations(); @@ -303,7 +296,6 @@ namespace System.IO.Pipes } return _readMode; } - [SecurityCritical] [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Security model of pipes: demand at creation but no subsequent demands")] set { @@ -335,7 +327,6 @@ namespace System.IO.Pipes // ---- PAL layer ends here ---- // ----------------------------- - [SecurityCritical] private unsafe int ReadFileNative(SafePipeHandle handle, Span buffer, NativeOverlapped* overlapped, out int errorCode) { DebugAssertHandleValid(handle); @@ -352,7 +343,7 @@ namespace System.IO.Pipes int r = 0; int numBytesRead = 0; - fixed (byte* p = &buffer.DangerousGetPinnableReference()) + fixed (byte* p = &MemoryMarshal.GetReference(buffer)) { r = _isAsync ? Interop.Kernel32.ReadFile(handle, p, buffer.Length, IntPtr.Zero, overlapped) : @@ -374,7 +365,6 @@ namespace System.IO.Pipes } } - [SecurityCritical] private unsafe int WriteFileNative(SafePipeHandle handle, ReadOnlySpan buffer, NativeOverlapped* overlapped, out int errorCode) { DebugAssertHandleValid(handle); @@ -391,7 +381,7 @@ namespace System.IO.Pipes int r = 0; int numBytesWritten = 0; - fixed (byte* p = &buffer.DangerousGetPinnableReference()) + fixed (byte* p = &MemoryMarshal.GetReference(buffer)) { r = _isAsync ? Interop.Kernel32.WriteFile(handle, p, buffer.Length, IntPtr.Zero, overlapped) : @@ -410,7 +400,6 @@ namespace System.IO.Pipes } } - [SecurityCritical] internal static unsafe Interop.Kernel32.SECURITY_ATTRIBUTES GetSecAttrs(HandleInheritability inheritability) { Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = default(Interop.Kernel32.SECURITY_ATTRIBUTES); @@ -426,7 +415,6 @@ namespace System.IO.Pipes /// /// Determine pipe read mode from Win32 /// - [SecurityCritical] private void UpdateReadMode() { int flags; @@ -450,7 +438,6 @@ namespace System.IO.Pipes /// Filter out all pipe related errors and do some cleanup before calling Error.WinIOError. /// /// - [SecurityCritical] internal Exception WinIOError(int errorCode) { switch (errorCode) diff --git a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/PipeStream.cs b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/PipeStream.cs index 928ad8a18a..74fe15ed63 100644 --- a/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/PipeStream.cs +++ b/external/corefx/src/System.IO.Pipes/src/System/IO/Pipes/PipeStream.cs @@ -5,8 +5,6 @@ using Microsoft.Win32.SafeHandles; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; -using System.Runtime.InteropServices; using System.Security; using System.Threading; using System.Threading.Tasks; @@ -95,7 +93,6 @@ namespace System.IO.Pipes // Once a PipeStream has a handle ready, it should call this method to set up the PipeStream. If // the pipe is in a connected state already, it should also set the IsConnected (protected) property. // This method may also be called to uninitialize a handle, setting it to null. - [SecuritySafeCritical] protected void InitializeHandle(SafePipeHandle handle, bool isExposed, bool isAsync) { if (isAsync && handle != null) @@ -111,7 +108,6 @@ namespace System.IO.Pipes _isFromExistingHandle = isExposed; } - [SecurityCritical] public override int Read(byte[] buffer, int offset, int count) { if (_isAsync) @@ -145,7 +141,6 @@ namespace System.IO.Pipes return ReadCore(destination); } - [SecuritySafeCritical] public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { CheckReadWriteArgs(buffer, offset, count); @@ -219,7 +214,6 @@ namespace System.IO.Pipes return base.EndRead(asyncResult); } - [SecurityCritical] public override void Write(byte[] buffer, int offset, int count) { if (_isAsync) @@ -255,7 +249,6 @@ namespace System.IO.Pipes WriteCore(source); } - [SecuritySafeCritical] public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { CheckReadWriteArgs(buffer, offset, count); @@ -358,14 +351,12 @@ namespace System.IO.Pipes // Reads a byte from the pipe stream. Returns the byte cast to an int // or -1 if the connection has been broken. - [SecurityCritical] public override unsafe int ReadByte() { byte b; return Read(new Span(&b, 1)) > 0 ? b : -1; } - [SecurityCritical] public override unsafe void WriteByte(byte value) { Write(new ReadOnlySpan(&value, 1)); @@ -373,7 +364,6 @@ namespace System.IO.Pipes // Does nothing on PipeStreams. We cannot call Interop.FlushFileBuffers here because we can deadlock // if the other end of the pipe is no longer interested in reading from the pipe. - [SecurityCritical] public override void Flush() { CheckWriteOperations(); @@ -383,7 +373,6 @@ namespace System.IO.Pipes } } - [SecurityCritical] protected override void Dispose(bool disposing) { try @@ -395,7 +384,7 @@ namespace System.IO.Pipes _handle.Dispose(); } - UninitializeAsyncHandle(); + DisposeCore(disposing); } finally { @@ -431,7 +420,6 @@ namespace System.IO.Pipes // message, otherwise it is set to true. public bool IsMessageComplete { - [SecurityCritical] [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Security model of pipes: demand at creation but no subsequent demands")] get { @@ -473,7 +461,6 @@ namespace System.IO.Pipes public SafePipeHandle SafePipeHandle { - [SecurityCritical] [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Security model of pipes: demand at creation but no subsequent demands")] get { @@ -493,7 +480,6 @@ namespace System.IO.Pipes internal SafePipeHandle InternalHandle { - [SecurityCritical] get { return _handle; @@ -510,7 +496,6 @@ namespace System.IO.Pipes public override bool CanRead { - [Pure] get { return _canRead; @@ -519,7 +504,6 @@ namespace System.IO.Pipes public override bool CanWrite { - [Pure] get { return _canWrite; @@ -528,7 +512,6 @@ namespace System.IO.Pipes public override bool CanSeek { - [Pure] get { return false; diff --git a/external/corefx/src/System.IO.Pipes/tests/AnonymousPipeTests/AnonymousPipeTest.Specific.cs b/external/corefx/src/System.IO.Pipes/tests/AnonymousPipeTests/AnonymousPipeTest.Specific.cs index 69d0a2ab2c..2006587e71 100644 --- a/external/corefx/src/System.IO.Pipes/tests/AnonymousPipeTests/AnonymousPipeTest.Specific.cs +++ b/external/corefx/src/System.IO.Pipes/tests/AnonymousPipeTests/AnonymousPipeTest.Specific.cs @@ -79,7 +79,7 @@ namespace System.IO.Pipes.Tests } } - [ConditionalFact(typeof(PlatformDetection), "IsNotRedHat69")] + [ConditionalFact(typeof(PlatformDetection), "IsNotRedHatFamily6")] [PlatformSpecific(TestPlatforms.Linux)] // On Linux, setting the buffer size of the server will also set the buffer size of the client public static void Linux_BufferSizeRoundtrips() { diff --git a/external/corefx/src/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Simple.cs b/external/corefx/src/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Simple.cs index fde032b1f3..2caac447d4 100644 --- a/external/corefx/src/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Simple.cs +++ b/external/corefx/src/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Simple.cs @@ -365,7 +365,7 @@ namespace System.IO.Pipes.Tests } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/Microsoft/BashOnWindows/issues/1011 + [Fact] [PlatformSpecific(TestPlatforms.AnyUnix)] // Unix implemented on sockets, where disposal information doesn't propagate public async Task Unix_OperationsOnNamedServerWithDisposedClient() { diff --git a/external/corefx/src/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Specific.cs b/external/corefx/src/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Specific.cs index caaf0a8179..8bbc323110 100644 --- a/external/corefx/src/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Specific.cs +++ b/external/corefx/src/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Specific.cs @@ -2,6 +2,8 @@ // 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.Collections.Generic; +using System.Linq; using System.Runtime.InteropServices; using System.Security.Principal; using System.Threading; @@ -76,6 +78,148 @@ namespace System.IO.Pipes.Tests } } + [Theory] + [InlineData(1)] + [InlineData(3)] + public async Task MultipleWaitingClients_ServerServesOneAtATime(int numClients) + { + string name = GetUniquePipeName(); + using (NamedPipeServerStream server = new NamedPipeServerStream(name)) + { + var clients = new List(from i in Enumerable.Range(0, numClients) select ConnectClientAndReadAsync()); + + while (clients.Count > 0) + { + Task firstClient = Task.WhenAny(clients); + await WhenAllOrAnyFailed(ServerWaitReadAndWriteAsync(), firstClient); + clients.Remove(firstClient.Result); + } + + async Task ServerWaitReadAndWriteAsync() + { + await server.WaitForConnectionAsync(); + await server.WriteAsync(new byte[1], 0, 1); + Assert.Equal(1, await server.ReadAsync(new byte[1], 0, 1)); + server.Disconnect(); + } + + async Task ConnectClientAndReadAsync() + { + using (var npcs = new NamedPipeClientStream(name)) + { + await npcs.ConnectAsync(); + Assert.Equal(1, await npcs.ReadAsync(new byte[1], 0, 1)); + await npcs.WriteAsync(new byte[1], 0, 1); + } + } + } + } + + [Fact] + public void MaxNumberOfServerInstances_TooManyServers_Throws() + { + string name = GetUniquePipeName(); + + using (new NamedPipeServerStream(name, PipeDirection.InOut, 1)) + { + // NPSS was created with max of 1, so creating another fails. + Assert.Throws(() => new NamedPipeServerStream(name, PipeDirection.InOut, 1)); + } + + using (new NamedPipeServerStream(name, PipeDirection.InOut, 3)) + { + // NPSS was created with max of 3, but NPSS not only validates against the original max but also + // against the max of the stream being created, so since there's already 1 and this specifies max == 1, it fails. + Assert.Throws(() => new NamedPipeServerStream(name, PipeDirection.InOut, 1)); + + using (new NamedPipeServerStream(name, PipeDirection.InOut, 2)) // lower max ignored + using (new NamedPipeServerStream(name, PipeDirection.InOut, 4)) // higher max ignored + { + // NPSS was created with a max of 3, and we're creating a 4th, so it fails. + Assert.Throws(() => new NamedPipeServerStream(name, PipeDirection.InOut, 3)); + } + + using (new NamedPipeServerStream(name, PipeDirection.InOut, 3)) + using (new NamedPipeServerStream(name, PipeDirection.InOut, 3)) + { + // NPSS was created with a max of 3, and we've already created 3, so it fails, + // even if the new stream tries to raise it. + Assert.Throws(() => new NamedPipeServerStream(name, PipeDirection.InOut, 4)); + Assert.Throws(() => new NamedPipeServerStream(name, PipeDirection.InOut, 2)); + } + } + } + + [Theory] + [InlineData(1)] + [InlineData(4)] + public async Task MultipleServers_ServeMultipleClientsConcurrently(int numServers) + { + string name = GetUniquePipeName(); + + var servers = new NamedPipeServerStream[numServers]; + var clients = new NamedPipeClientStream[servers.Length]; + try + { + for (int i = 0; i < servers.Length; i++) + { + servers[i] = new NamedPipeServerStream(name, PipeDirection.InOut, numServers, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); + } + + for (int i = 0; i < clients.Length; i++) + { + clients[i] = new NamedPipeClientStream(".", name, PipeDirection.InOut, PipeOptions.Asynchronous); + } + + Task[] serverWaits = (from server in servers select server.WaitForConnectionAsync()).ToArray(); + Task[] clientWaits = (from client in clients select client.ConnectAsync()).ToArray(); + await WhenAllOrAnyFailed(serverWaits.Concat(clientWaits).ToArray()); + + Task[] serverSends = (from server in servers select server.WriteAsync(new byte[1], 0, 1)).ToArray(); + Task[] clientReceives = (from client in clients select client.ReadAsync(new byte[1], 0, 1)).ToArray(); + await WhenAllOrAnyFailed(serverSends.Concat(clientReceives).ToArray()); + } + finally + { + for (int i = 0; i < clients.Length; i++) + { + clients[i]?.Dispose(); + } + + for (int i = 0; i < servers.Length; i++) + { + servers[i]?.Dispose(); + } + } + } + + private static Task WhenAllOrAnyFailed(params Task[] tasks) + { + int remaining = tasks.Length; + var tcs = new TaskCompletionSource(); + foreach (Task t in tasks) + { + t.ContinueWith(a => + { + if (a.IsFaulted) + { + tcs.TrySetException(a.Exception.InnerExceptions); + Interlocked.Decrement(ref remaining); + } + else if (a.IsCanceled) + { + tcs.TrySetCanceled(); + Interlocked.Decrement(ref remaining); + } + else if (Interlocked.Decrement(ref remaining) == 0) + { + tcs.TrySetResult(true); + } + }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default); + } + return tcs.Task; + } + [Theory] [InlineData(PipeOptions.None)] [InlineData(PipeOptions.Asynchronous)] @@ -461,5 +605,83 @@ namespace System.IO.Pipes.Tests } } + [Fact] + public void ClientConnect_Throws_Timeout_When_Pipe_Not_Found() + { + string pipeName = GetUniquePipeName(); + using (NamedPipeClientStream client = new NamedPipeClientStream(pipeName)) + { + Assert.Throws(() => client.Connect(91)); + } + } + + [Theory] + [MemberData(nameof(GetCancellationTokens))] + public async void ClientConnectAsync_Throws_Timeout_When_Pipe_Not_Found(CancellationToken cancellationToken) + { + string pipeName = GetUniquePipeName(); + using (NamedPipeClientStream client = new NamedPipeClientStream(pipeName)) + { + Task waitingClient = client.ConnectAsync(92, cancellationToken); + await Assert.ThrowsAsync(() => { return waitingClient; }); + } + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "https://github.com/dotnet/corefx/pull/25877 yet to be ported to netfx")] + [PlatformSpecific(TestPlatforms.Windows)] // Unix ignores MaxNumberOfServerInstances and second client also connects. + public void ClientConnect_Throws_Timeout_When_Pipe_Busy() + { + string pipeName = GetUniquePipeName(); + + using (NamedPipeServerStream server = new NamedPipeServerStream(pipeName)) + using (NamedPipeClientStream firstClient = new NamedPipeClientStream(pipeName)) + using (NamedPipeClientStream secondClient = new NamedPipeClientStream(pipeName)) + { + const int timeout = 10_000; + Task[] clientAndServerTasks = new[] + { + firstClient.ConnectAsync(timeout), + Task.Run(() => server.WaitForConnection()) + }; + + Assert.True(Task.WaitAll(clientAndServerTasks, timeout)); + + Assert.Throws(() => secondClient.Connect(93)); + } + } + + [Theory] + [MemberData(nameof(GetCancellationTokens))] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "https://github.com/dotnet/corefx/pull/25877 yet to be ported to netfx")] + [PlatformSpecific(TestPlatforms.Windows)] // Unix ignores MaxNumberOfServerInstances and second client also connects. + public async void ClientConnectAsync_With_Cancellation_Throws_Timeout_When_Pipe_Busy(CancellationToken cancellationToken) + { + string pipeName = GetUniquePipeName(); + + using (NamedPipeServerStream server = new NamedPipeServerStream(pipeName)) + using (NamedPipeClientStream firstClient = new NamedPipeClientStream(pipeName)) + using (NamedPipeClientStream secondClient = new NamedPipeClientStream(pipeName)) + { + const int timeout = 10_000; + Task[] clientAndServerTasks = new[] + { + firstClient.ConnectAsync(timeout), + Task.Run(() => server.WaitForConnection()) + }; + + Assert.True(Task.WaitAll(clientAndServerTasks, timeout)); + + Task waitingClient = secondClient.ConnectAsync(94, cancellationToken); + await Assert.ThrowsAsync(() => { return waitingClient; }); + } + } + + public static IEnumerable GetCancellationTokens => + new [] + { + new object[] { CancellationToken.None }, + new object[] { new CancellationTokenSource().Token }, + }; } } diff --git a/external/corefx/src/System.IO.Pipes/tests/Performance/System.IO.Pipes.Performance.Tests.csproj b/external/corefx/src/System.IO.Pipes/tests/Performance/System.IO.Pipes.Performance.Tests.csproj index b9bfa0f15b..00e7df613f 100644 --- a/external/corefx/src/System.IO.Pipes/tests/Performance/System.IO.Pipes.Performance.Tests.csproj +++ b/external/corefx/src/System.IO.Pipes/tests/Performance/System.IO.Pipes.Performance.Tests.csproj @@ -6,7 +6,6 @@ true {7A8B72D7-FACD-4E96-8390-F2D2FE269687} - diff --git a/external/corefx/src/System.IO.Pipes/tests/System.IO.Pipes.Tests.csproj b/external/corefx/src/System.IO.Pipes/tests/System.IO.Pipes.Tests.csproj index e60965a979..d9cd837ac6 100644 --- a/external/corefx/src/System.IO.Pipes/tests/System.IO.Pipes.Tests.csproj +++ b/external/corefx/src/System.IO.Pipes/tests/System.IO.Pipes.Tests.csproj @@ -5,7 +5,6 @@ {142469EC-D665-4FE2-845A-FDA69F9CC557} true - diff --git a/external/corefx/src/System.IO.Ports/pkg/System.IO.Ports.pkgproj b/external/corefx/src/System.IO.Ports/pkg/System.IO.Ports.pkgproj index f070b7c577..44d2950271 100644 --- a/external/corefx/src/System.IO.Ports/pkg/System.IO.Ports.pkgproj +++ b/external/corefx/src/System.IO.Ports/pkg/System.IO.Ports.pkgproj @@ -11,8 +11,5 @@ - - - \ No newline at end of file diff --git a/external/corefx/src/System.IO.Ports/src/Resources/Strings.resx b/external/corefx/src/System.IO.Ports/src/Resources/Strings.resx index 51b49f2db4..2958af7c51 100644 --- a/external/corefx/src/System.IO.Ports/src/Resources/Strings.resx +++ b/external/corefx/src/System.IO.Ports/src/Resources/Strings.resx @@ -1,5 +1,64 @@  + @@ -169,4 +228,7 @@ Enumeration of serial port names is not supported on the current platform. - + + The specified port name '{0}' is too long. The port name must be less than 260 characters. + + \ No newline at end of file diff --git a/external/corefx/src/System.IO.Ports/src/System.IO.Ports.csproj b/external/corefx/src/System.IO.Ports/src/System.IO.Ports.csproj index e9268ac8d0..c5f4f027aa 100644 --- a/external/corefx/src/System.IO.Ports/src/System.IO.Ports.csproj +++ b/external/corefx/src/System.IO.Ports/src/System.IO.Ports.csproj @@ -112,9 +112,6 @@ Common\Interop\Windows\Interop.FileTypes.cs - - Common\Interop\Windows\kernelbase\Interop.OpenCommPort.cs - Common\System\IO\PathInternal.Windows.cs @@ -130,6 +127,9 @@ Common\Interop\Windows\Interop.BOOL.cs + + Common\Interop\Windows\kernel32\Interop.CloseHandle.cs + @@ -137,6 +137,9 @@ + + Common\Interop\Windows\mincore\Interop.OpenCommPort.cs + @@ -167,4 +170,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.IO.Ports/src/System/IO/Ports/InternalResources.cs b/external/corefx/src/System.IO.Ports/src/System/IO/Ports/InternalResources.cs index ca98dccf12..b2ed72878a 100644 --- a/external/corefx/src/System.IO.Ports/src/System/IO/Ports/InternalResources.cs +++ b/external/corefx/src/System.IO.Ports/src/System/IO/Ports/InternalResources.cs @@ -56,8 +56,8 @@ namespace System.IO.Ports // After calling GetLastWin32Error(), it clears the last error field, // so you must save the HResult and pass it to this method. This method - // will determine the appropriate exception to throw dependent on your - // error, and depending on the error, insert a string into the message + // will determine the appropriate exception to throw dependent on your + // error, and depending on the error, insert a string into the message // gotten from the ResourceManager. internal static void WinIOError(int errorCode, String str) { @@ -77,7 +77,10 @@ namespace System.IO.Ports throw new UnauthorizedAccessException(string.Format(SR.UnauthorizedAccess_IODenied_Path, str)); case Interop.Errors.ERROR_FILENAME_EXCED_RANGE: - throw new PathTooLongException(SR.IO_PathTooLong); + if (string.IsNullOrEmpty(str)) + throw new PathTooLongException(SR.IO_PathTooLong); + else + throw new PathTooLongException(SR.Format(SR.IO_PathTooLong_Path, str)); case Interop.Errors.ERROR_SHARING_VIOLATION: // error message. diff --git a/external/corefx/src/System.IO.Ports/src/System/IO/Ports/SerialStream.Uap.cs b/external/corefx/src/System.IO.Ports/src/System/IO/Ports/SerialStream.Uap.cs index 7bd49a1677..3897ff2089 100644 --- a/external/corefx/src/System.IO.Ports/src/System/IO/Ports/SerialStream.Uap.cs +++ b/external/corefx/src/System.IO.Ports/src/System/IO/Ports/SerialStream.Uap.cs @@ -10,7 +10,7 @@ namespace System.IO.Ports { public SafeFileHandle OpenPort(uint portNumber) { - return Interop.KernelBase.OpenCommPort( + return Interop.mincore.OpenCommPort( portNumber, Interop.Kernel32.GenericOperations.GENERIC_READ | Interop.Kernel32.GenericOperations.GENERIC_WRITE, NativeMethods.FILE_FLAG_OVERLAPPED); diff --git a/external/corefx/src/System.IO.Ports/src/System/IO/Ports/SerialStream.Win32.cs b/external/corefx/src/System.IO.Ports/src/System/IO/Ports/SerialStream.Win32.cs index c59acebb5e..52a4cb8fbf 100644 --- a/external/corefx/src/System.IO.Ports/src/System/IO/Ports/SerialStream.Win32.cs +++ b/external/corefx/src/System.IO.Ports/src/System/IO/Ports/SerialStream.Win32.cs @@ -11,7 +11,7 @@ namespace System.IO.Ports { public SafeFileHandle OpenPort(uint portNumber) { - return Interop.Kernel32.CreateFileDefaultSecurity( + return Interop.Kernel32.CreateFile( @"\\?\COM" + portNumber.ToString(CultureInfo.InvariantCulture), Interop.Kernel32.GenericOperations.GENERIC_READ | Interop.Kernel32.GenericOperations.GENERIC_WRITE, 0, // comm devices must be opened w/exclusive-access diff --git a/external/corefx/src/System.IO.UnmanagedMemoryStream/src/ApiCompatBaseline.uapaot.txt b/external/corefx/src/System.IO.UnmanagedMemoryStream/src/ApiCompatBaseline.uapaot.txt deleted file mode 100644 index 6bffbcefa5..0000000000 --- a/external/corefx/src/System.IO.UnmanagedMemoryStream/src/ApiCompatBaseline.uapaot.txt +++ /dev/null @@ -1,3 +0,0 @@ -Compat issues with assembly System.IO.UnmanagedMemoryStream: -CannotRemoveBaseTypeOrInterface : Type 'System.IO.UnmanagedMemoryStream' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract. -Total Issues: 1 diff --git a/external/corefx/src/System.IO.UnmanagedMemoryStream/src/System.IO.UnmanagedMemoryStream.csproj b/external/corefx/src/System.IO.UnmanagedMemoryStream/src/System.IO.UnmanagedMemoryStream.csproj index 691ad4814b..8b7c2a048c 100644 --- a/external/corefx/src/System.IO.UnmanagedMemoryStream/src/System.IO.UnmanagedMemoryStream.csproj +++ b/external/corefx/src/System.IO.UnmanagedMemoryStream/src/System.IO.UnmanagedMemoryStream.csproj @@ -6,7 +6,6 @@ System.IO.UnmanagedMemoryStream true - diff --git a/external/corefx/src/System.IO.UnmanagedMemoryStream/tests/Uma.ReadWriteStruct.cs b/external/corefx/src/System.IO.UnmanagedMemoryStream/tests/Uma.ReadWriteStruct.cs index 1148ad70cb..ce9d196dac 100644 --- a/external/corefx/src/System.IO.UnmanagedMemoryStream/tests/Uma.ReadWriteStruct.cs +++ b/external/corefx/src/System.IO.UnmanagedMemoryStream/tests/Uma.ReadWriteStruct.cs @@ -96,8 +96,8 @@ namespace System.IO.Tests using (var buffer = new TestSafeBuffer(capacity)) using (var uma = new UnmanagedMemoryAccessor(buffer, 0, capacity, FileAccess.ReadWrite)) { - AssertExtensions.Throws("type", () => uma.Write(0, ref inStruct)); - AssertExtensions.Throws("type", () => uma.Read(0, out inStruct)); + AssertExtensions.Throws(null, "type", () => uma.Write(0, ref inStruct)); + AssertExtensions.Throws(null, "type", () => uma.Read(0, out inStruct)); } } @@ -124,8 +124,8 @@ namespace System.IO.Tests using (var buffer = new TestSafeBuffer(capacity)) using (var uma = new UnmanagedMemoryAccessor(buffer, 0, capacity, FileAccess.ReadWrite)) { - AssertExtensions.Throws("type", () => uma.Write>(0, ref inStruct)); - AssertExtensions.Throws("type", () => uma.Read>(0, out inStruct)); + AssertExtensions.Throws(null, "type", () => uma.Write>(0, ref inStruct)); + AssertExtensions.Throws(null, "type", () => uma.Read>(0, out inStruct)); } } diff --git a/external/corefx/src/System.IO.UnmanagedMemoryStream/tests/Uma.ReadWriteStructArray.cs b/external/corefx/src/System.IO.UnmanagedMemoryStream/tests/Uma.ReadWriteStructArray.cs index 52c9b2ea8a..e74f7f6888 100644 --- a/external/corefx/src/System.IO.UnmanagedMemoryStream/tests/Uma.ReadWriteStructArray.cs +++ b/external/corefx/src/System.IO.UnmanagedMemoryStream/tests/Uma.ReadWriteStructArray.cs @@ -95,8 +95,8 @@ namespace System.IO.Tests using (var buffer = new TestSafeBuffer(capacity)) using (var uma = new UnmanagedMemoryAccessor(buffer, 0, capacity, FileAccess.ReadWrite)) { - AssertExtensions.Throws("type", () => uma.WriteArray(0, structArr, 0, 1)); - AssertExtensions.Throws("type", () => uma.ReadArray(0, structArr, 0, 1)); + AssertExtensions.Throws(null, "type", () => uma.WriteArray(0, structArr, 0, 1)); + AssertExtensions.Throws(null, "type", () => uma.ReadArray(0, structArr, 0, 1)); } } @@ -123,8 +123,8 @@ namespace System.IO.Tests using (var buffer = new TestSafeBuffer(capacity)) using (var uma = new UnmanagedMemoryAccessor(buffer, 0, capacity, FileAccess.ReadWrite)) { - AssertExtensions.Throws("type", () => uma.WriteArray>(0, structArr, 0, 1)); - AssertExtensions.Throws("type", () => uma.ReadArray>(0, structArr, 0, 1)); + AssertExtensions.Throws(null, "type", () => uma.WriteArray>(0, structArr, 0, 1)); + AssertExtensions.Throws(null, "type", () => uma.ReadArray>(0, structArr, 0, 1)); } } diff --git a/external/corefx/src/System.IO/tests/BinaryReader/BinaryReaderTests.netcoreapp.cs b/external/corefx/src/System.IO/tests/BinaryReader/BinaryReaderTests.netcoreapp.cs index 8cd56e7169..a47e0b769a 100644 --- a/external/corefx/src/System.IO/tests/BinaryReader/BinaryReaderTests.netcoreapp.cs +++ b/external/corefx/src/System.IO/tests/BinaryReader/BinaryReaderTests.netcoreapp.cs @@ -1,4 +1,8 @@ -using System.Linq; +// 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.Linq; using System.Text; using Xunit; diff --git a/external/corefx/src/System.IO/tests/Stream/Stream.CopyToTests.cs b/external/corefx/src/System.IO/tests/Stream/Stream.CopyToTests.cs index 270b03128c..aac73b9f22 100644 --- a/external/corefx/src/System.IO/tests/Stream/Stream.CopyToTests.cs +++ b/external/corefx/src/System.IO/tests/Stream/Stream.CopyToTests.cs @@ -10,7 +10,7 @@ using Xunit; namespace System.IO.Tests { - public class StreamCopyToTests + public partial class StreamCopyToTests { [Fact] public void IfCanSeekIsFalseLengthAndPositionShouldNotBeCalled() diff --git a/external/corefx/src/System.IO/tests/Stream/Stream.CopyToTests.netcoreapp.cs b/external/corefx/src/System.IO/tests/Stream/Stream.CopyToTests.netcoreapp.cs new file mode 100644 index 0000000000..d99c02e0bf --- /dev/null +++ b/external/corefx/src/System.IO/tests/Stream/Stream.CopyToTests.netcoreapp.cs @@ -0,0 +1,61 @@ +// 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.Linq; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace System.IO.Tests +{ + public partial class StreamCopyToTests + { + [Fact] + public void CopyToAsync_StreamToken_InvalidArgsThrows() + { + Stream s = new MemoryStream(); + AssertExtensions.Throws("destination", () => { s.CopyToAsync(null, default(CancellationToken)); }); + } + + [Theory] + [InlineData(0)] + [InlineData(42)] + [InlineData(100000)] // greater than 81920, the DefaultCopyBufferSize + public void CopyToAsync_StreamToken_ExpectedBufferSizePropagated(int length) + { + Stream s = new CustomMemoryStream(); + s.Write(new byte[length], 0, length); + s.Position = 0; + + const int DefaultCopyBufferSize = 81920; + Assert.Equal(Math.Max(1, Math.Min(length, DefaultCopyBufferSize)), ((Task)s.CopyToAsync(Stream.Null, default(CancellationToken))).Result); + } + + private sealed class CustomMemoryStream : MemoryStream + { + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) => + Task.FromResult(bufferSize); + } + + [Fact] + public void CopyToAsync_StreamToken_PrecanceledToken_Cancels() + { + var src = new MemoryStream(); + Assert.Equal(TaskStatus.Canceled, src.CopyToAsync(Stream.Null, new CancellationToken(true)).Status); + } + + [Fact] + public async Task CopyToAsync_StreamToken_AllDataCopied() + { + var src = new MemoryStream(); + src.Write(Enumerable.Range(0, 10000).Select(i => (byte)i).ToArray(), 0, 256); + src.Position = 0; + + var dst = new MemoryStream(); + await src.CopyToAsync(dst, default(CancellationToken)); + + Assert.Equal(src.ToArray(), dst.ToArray()); + } + } +} diff --git a/external/corefx/src/System.IO/tests/StreamReader/StreamReaderTests.netcoreapp.cs b/external/corefx/src/System.IO/tests/StreamReader/StreamReaderTests.netcoreapp.cs new file mode 100644 index 0000000000..053cf876e4 --- /dev/null +++ b/external/corefx/src/System.IO/tests/StreamReader/StreamReaderTests.netcoreapp.cs @@ -0,0 +1,203 @@ +// 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.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace System.IO.Tests +{ + public partial class StreamReaderTests + { + [Theory] + [InlineData(0)] + [InlineData(10)] + public async Task Read_EmptySpan_ReadsNothing(int length) + { + using (var r = new StreamReader(new MemoryStream(Enumerable.Repeat((byte)'s', length).ToArray()))) + { + Assert.Equal(0, r.Read(Span.Empty)); + Assert.Equal(0, r.ReadBlock(Span.Empty)); + Assert.Equal(0, await r.ReadAsync(Memory.Empty)); + Assert.Equal(0, await r.ReadBlockAsync(Memory.Empty)); + } + } + + [Theory] + [InlineData(1, 100, 1)] + [InlineData(1, 100, 101)] + [InlineData(100, 50, 1)] + [InlineData(100, 50, 101)] + public void Read_ReadsExpectedData(int readLength, int totalLength, int bufferSize) + { + var data = new char[totalLength]; + var r = new Random(42); + for (int i = 0; i < data.Length; i++) + { + data[i] = (char)('a' + r.Next(0, 26)); + } + + var result = new char[data.Length]; + Span dst = result; + + using (var sr = new StreamReader(new MemoryStream(data.Select(i => (byte)i).ToArray()), Encoding.ASCII, false, bufferSize)) + { + while (dst.Length > 0) + { + int read = sr.Read(dst); + Assert.InRange(read, 1, dst.Length); + dst = dst.Slice(read); + } + } + + Assert.Equal(data, result); + } + + [Theory] + [InlineData(1, 100, 1)] + [InlineData(1, 100, 101)] + [InlineData(100, 50, 1)] + [InlineData(100, 50, 101)] + public void ReadBlock_ReadsExpectedData(int readLength, int totalLength, int bufferSize) + { + var data = new char[totalLength]; + var r = new Random(42); + for (int i = 0; i < data.Length; i++) + { + data[i] = (char)('a' + r.Next(0, 26)); + } + + var result = new char[data.Length]; + Span dst = result; + + using (var sr = new StreamReader(new MemoryStream(data.Select(i => (byte)i).ToArray()), Encoding.ASCII, false, bufferSize)) + { + while (dst.Length > 0) + { + int read = sr.ReadBlock(dst); + Assert.InRange(read, 1, dst.Length); + dst = dst.Slice(read); + } + } + + Assert.Equal(data, result); + } + + [Theory] + [InlineData(1, 100, 1)] + [InlineData(1, 100, 101)] + [InlineData(100, 50, 1)] + [InlineData(100, 50, 101)] + public async Task ReadAsync_ReadsExpectedData(int readLength, int totalLength, int bufferSize) + { + var data = new char[totalLength]; + var r = new Random(42); + for (int i = 0; i < data.Length; i++) + { + data[i] = (char)('a' + r.Next(0, 26)); + } + + var result = new char[data.Length]; + Memory dst = result; + + using (var sr = new StreamReader(new MemoryStream(data.Select(i => (byte)i).ToArray()), Encoding.ASCII, false, bufferSize)) + { + while (dst.Length > 0) + { + int read = await sr.ReadAsync(dst); + Assert.InRange(read, 1, dst.Length); + dst = dst.Slice(read); + } + } + + Assert.Equal(data, result); + } + + [Theory] + [InlineData(1, 100, 1)] + [InlineData(1, 100, 101)] + [InlineData(100, 50, 1)] + [InlineData(100, 50, 101)] + public async Task ReadBlockAsync_ReadsExpectedData(int readLength, int totalLength, int bufferSize) + { + var data = new char[totalLength]; + var r = new Random(42); + for (int i = 0; i < data.Length; i++) + { + data[i] = (char)('a' + r.Next(0, 26)); + } + + var result = new char[data.Length]; + Memory dst = result; + + using (var sr = new StreamReader(new MemoryStream(data.Select(i => (byte)i).ToArray()), Encoding.ASCII, false, bufferSize)) + { + while (dst.Length > 0) + { + int read = await sr.ReadBlockAsync(dst); + Assert.InRange(read, 1, dst.Length); + dst = dst.Slice(read); + } + } + + Assert.Equal(data, result); + } + + [Fact] + public void ReadBlock_RepeatsReadsUntilReadDesiredAmount() + { + char[] data = "hello world".ToCharArray(); + var ms = new MemoryStream(Encoding.UTF8.GetBytes(data)); + var s = new DelegateStream( + canReadFunc: () => true, + readFunc: (buffer, offset, count) => ms.Read(buffer, offset, 1)); // do actual reads a byte at a time + using (var r = new StreamReader(s, Encoding.UTF8, false, 2)) + { + var result = new char[data.Length]; + Assert.Equal(data.Length, r.ReadBlock((Span)result)); + Assert.Equal(data, result); + } + } + + [Fact] + public async Task ReadBlockAsync_RepeatsReadsUntilReadDesiredAmount() + { + char[] data = "hello world".ToCharArray(); + var ms = new MemoryStream(Encoding.UTF8.GetBytes(data)); + var s = new DelegateStream( + canReadFunc: () => true, + readAsyncFunc: (buffer, offset, count, cancellationToken) => ms.ReadAsync(buffer, offset, 1)); // do actual reads a byte at a time + using (var r = new StreamReader(s, Encoding.UTF8, false, 2)) + { + var result = new char[data.Length]; + Assert.Equal(data.Length, await r.ReadBlockAsync((Memory)result)); + Assert.Equal(data, result); + } + } + + [Fact] + public async Task ReadAsync_Precanceled_ThrowsException() + { + using (var sr = new StreamReader(new MemoryStream())) + { + await Assert.ThrowsAnyAsync(() => sr.ReadAsync(Memory.Empty, new CancellationToken(true)).AsTask()); + await Assert.ThrowsAnyAsync(() => sr.ReadBlockAsync(Memory.Empty, new CancellationToken(true)).AsTask()); + } + } + + [Fact] + public async Task Read_SpanMemory_DisposedStream_ThrowsException() + { + var sr = new StreamReader(new MemoryStream()); + sr.Dispose(); + + Assert.Throws(() => sr.Read(Span.Empty)); + Assert.Throws(() => sr.ReadBlock(Span.Empty)); + await Assert.ThrowsAsync(() => sr.ReadAsync(Memory.Empty).AsTask()); + await Assert.ThrowsAsync(() => sr.ReadBlockAsync(Memory.Empty).AsTask()); + } + } +} diff --git a/external/corefx/src/System.IO/tests/StreamWriter/StreamWriter.WriteTests.netcoreapp.cs b/external/corefx/src/System.IO/tests/StreamWriter/StreamWriter.WriteTests.netcoreapp.cs new file mode 100644 index 0000000000..6830bed96c --- /dev/null +++ b/external/corefx/src/System.IO/tests/StreamWriter/StreamWriter.WriteTests.netcoreapp.cs @@ -0,0 +1,205 @@ +// 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.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace System.IO.Tests +{ + public partial class StreamWriterTests + { + [Fact] + public void Write_EmptySpan_WritesNothing() + { + using (var s = new MemoryStream()) + using (var writer = new StreamWriter(s)) + { + writer.Write(ReadOnlySpan.Empty); + writer.Flush(); + Assert.Equal(0, s.Position); + } + } + + [Fact] + public void WriteLine_EmptySpan_WritesNewLine() + { + using (var s = new MemoryStream()) + using (var writer = new StreamWriter(s)) + { + writer.WriteLine(ReadOnlySpan.Empty); + writer.Flush(); + Assert.Equal(Environment.NewLine.Length, s.Position); + } + } + + [Fact] + public async Task WriteAsync_EmptyMemory_WritesNothing() + { + using (var s = new MemoryStream()) + using (var writer = new StreamWriter(s)) + { + await writer.WriteAsync(ReadOnlyMemory.Empty); + await writer.FlushAsync(); + Assert.Equal(0, s.Position); + } + } + + [Fact] + public async Task WriteLineAsync_EmptyMemory_WritesNothing() + { + using (var s = new MemoryStream()) + using (var writer = new StreamWriter(s)) + { + await writer.WriteLineAsync(ReadOnlyMemory.Empty); + await writer.FlushAsync(); + Assert.Equal(Environment.NewLine.Length, s.Position); + } + } + + [Theory] + [InlineData(1, 1, 1, false)] + [InlineData(100, 1, 100, false)] + [InlineData(100, 10, 3, false)] + [InlineData(1, 1, 1, true)] + [InlineData(100, 1, 100, true)] + [InlineData(100, 10, 3, true)] + public void Write_Span_WritesExpectedData(int length, int writeSize, int writerBufferSize, bool autoFlush) + { + using (var s = new MemoryStream()) + using (var writer = new StreamWriter(s, Encoding.ASCII, writerBufferSize) { AutoFlush = autoFlush }) + { + var data = new char[length]; + var rand = new Random(42); + for (int i = 0; i < data.Length; i++) + { + data[i] = (char)(rand.Next(0, 26) + 'a'); + } + + Span source = data; + while (source.Length > 0) + { + int n = Math.Min(source.Length, writeSize); + writer.Write(source.Slice(0, n)); + source = source.Slice(n); + } + + writer.Flush(); + + Assert.Equal(data, s.ToArray().Select(b => (char)b)); + } + } + + [Theory] + [InlineData(1, 1, 1, false)] + [InlineData(100, 1, 100, false)] + [InlineData(100, 10, 3, false)] + [InlineData(1, 1, 1, true)] + [InlineData(100, 1, 100, true)] + [InlineData(100, 10, 3, true)] + public async Task Write_Memory_WritesExpectedData(int length, int writeSize, int writerBufferSize, bool autoFlush) + { + using (var s = new MemoryStream()) + using (var writer = new StreamWriter(s, Encoding.ASCII, writerBufferSize) { AutoFlush = autoFlush }) + { + var data = new char[length]; + var rand = new Random(42); + for (int i = 0; i < data.Length; i++) + { + data[i] = (char)(rand.Next(0, 26) + 'a'); + } + + ReadOnlyMemory source = data; + while (source.Length > 0) + { + int n = Math.Min(source.Length, writeSize); + await writer.WriteAsync(source.Slice(0, n)); + source = source.Slice(n); + } + + await writer.FlushAsync(); + + Assert.Equal(data, s.ToArray().Select(b => (char)b)); + } + } + + [Theory] + [InlineData(1, 1, 1, false)] + [InlineData(100, 1, 100, false)] + [InlineData(100, 10, 3, false)] + [InlineData(1, 1, 1, true)] + [InlineData(100, 1, 100, true)] + [InlineData(100, 10, 3, true)] + public void WriteLine_Span_WritesExpectedData(int length, int writeSize, int writerBufferSize, bool autoFlush) + { + using (var s = new MemoryStream()) + using (var writer = new StreamWriter(s, Encoding.ASCII, writerBufferSize) { AutoFlush = autoFlush }) + { + var data = new char[length]; + var rand = new Random(42); + for (int i = 0; i < data.Length; i++) + { + data[i] = (char)(rand.Next(0, 26) + 'a'); + } + + Span source = data; + while (source.Length > 0) + { + int n = Math.Min(source.Length, writeSize); + writer.WriteLine(source.Slice(0, n)); + source = source.Slice(n); + } + + writer.Flush(); + + Assert.Equal(length + (Environment.NewLine.Length * (length / writeSize)), s.Length); + } + } + + [Theory] + [InlineData(1, 1, 1, false)] + [InlineData(100, 1, 100, false)] + [InlineData(100, 10, 3, false)] + [InlineData(1, 1, 1, true)] + [InlineData(100, 1, 100, true)] + [InlineData(100, 10, 3, true)] + public async Task WriteLineAsync_Memory_WritesExpectedData(int length, int writeSize, int writerBufferSize, bool autoFlush) + { + using (var s = new MemoryStream()) + using (var writer = new StreamWriter(s, Encoding.ASCII, writerBufferSize) { AutoFlush = autoFlush }) + { + var data = new char[length]; + var rand = new Random(42); + for (int i = 0; i < data.Length; i++) + { + data[i] = (char)(rand.Next(0, 26) + 'a'); + } + + ReadOnlyMemory source = data; + while (source.Length > 0) + { + int n = Math.Min(source.Length, writeSize); + await writer.WriteLineAsync(source.Slice(0, n)); + source = source.Slice(n); + } + + await writer.FlushAsync(); + + Assert.Equal(length + (Environment.NewLine.Length * (length / writeSize)), s.Length); + } + } + + [Fact] + public async Task WriteAsync_Precanceled_ThrowsCancellationException() + { + using (var writer = new StreamWriter(Stream.Null)) + { + await Assert.ThrowsAnyAsync(() => writer.WriteAsync(ReadOnlyMemory.Empty, new CancellationToken(true))); + await Assert.ThrowsAnyAsync(() => writer.WriteLineAsync(ReadOnlyMemory.Empty, new CancellationToken(true))); + } + } + } +} diff --git a/external/corefx/src/System.IO/tests/System.IO.Tests.csproj b/external/corefx/src/System.IO/tests/System.IO.Tests.csproj index 7273e968b5..7a2412da95 100644 --- a/external/corefx/src/System.IO/tests/System.IO.Tests.csproj +++ b/external/corefx/src/System.IO/tests/System.IO.Tests.csproj @@ -7,7 +7,6 @@ {492EC54D-D2C4-4B3F-AC1F-646B3F7EBB02} true - @@ -38,16 +37,19 @@ + + + diff --git a/external/corefx/src/System.IO/tests/TestDataProvider/TestDataProvider.cs b/external/corefx/src/System.IO/tests/TestDataProvider/TestDataProvider.cs index b7e37ddff1..98c37a32e8 100644 --- a/external/corefx/src/System.IO/tests/TestDataProvider/TestDataProvider.cs +++ b/external/corefx/src/System.IO/tests/TestDataProvider/TestDataProvider.cs @@ -1,4 +1,8 @@ -using System; +// 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.Collections.Generic; using System.Linq; using System.Text; diff --git a/external/corefx/src/System.IO/tests/TextReader/TextReaderTests.cs b/external/corefx/src/System.IO/tests/TextReader/TextReaderTests.cs index befc068cb6..9872c82add 100644 --- a/external/corefx/src/System.IO/tests/TextReader/TextReaderTests.cs +++ b/external/corefx/src/System.IO/tests/TextReader/TextReaderTests.cs @@ -70,6 +70,16 @@ namespace System.IO.Tests } } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "#23810 not fixed on .NET Framework")] + public void ReadZeroCharacters() + { + using (CharArrayTextReader tr = GetCharArray().textReader) + { + Assert.Equal(0, tr.Read(new char[0], 0, 0)); + } + } + [Fact] public void ArgumentNullOnNullArray() { diff --git a/external/corefx/src/System.Json/src/System.Json.csproj b/external/corefx/src/System.Json/src/System.Json.csproj index 84c91f3c13..4f12ea9374 100644 --- a/external/corefx/src/System.Json/src/System.Json.csproj +++ b/external/corefx/src/System.Json/src/System.Json.csproj @@ -33,4 +33,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Json/src/System/Json/JsonArray.cs b/external/corefx/src/System.Json/src/System/Json/JsonArray.cs index 4354ac4028..8b4b502665 100644 --- a/external/corefx/src/System.Json/src/System/Json/JsonArray.cs +++ b/external/corefx/src/System.Json/src/System/Json/JsonArray.cs @@ -41,11 +41,6 @@ namespace System.Json public void Add(JsonValue item) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } - _list.Add(item); } diff --git a/external/corefx/src/System.Json/tests/JsonArrayTests.cs b/external/corefx/src/System.Json/tests/JsonArrayTests.cs index 211d3356af..d4bb5e81c6 100644 --- a/external/corefx/src/System.Json/tests/JsonArrayTests.cs +++ b/external/corefx/src/System.Json/tests/JsonArrayTests.cs @@ -118,10 +118,11 @@ namespace System.Json.Tests } [Fact] - public void Add_NullItem_ThrowsArgumentNullException() + public void Add_NullItem() { JsonArray array = new JsonArray(); - AssertExtensions.Throws("item", () => array.Add(null)); + array.Add(null); + Assert.Equal(1, array.Count); } [Fact] diff --git a/external/corefx/src/System.Json/tests/JsonValueTests.cs b/external/corefx/src/System.Json/tests/JsonValueTests.cs index 56b320043a..34fa1d42d5 100644 --- a/external/corefx/src/System.Json/tests/JsonValueTests.cs +++ b/external/corefx/src/System.Json/tests/JsonValueTests.cs @@ -88,6 +88,8 @@ namespace System.Json.Tests Assert.Equal(4, value.Count); Assert.Equal(JsonType.Array, value.JsonType); Assert.Equal("[1, 2, 3, null]", value.ToString()); + ((JsonArray) value).Add(null); + Assert.Equal("[1, 2, 3, null, null]", value.ToString()); }); } diff --git a/external/corefx/src/System.Json/tests/System.Json.Tests.csproj b/external/corefx/src/System.Json/tests/System.Json.Tests.csproj index 9656c70c54..21f265494c 100644 --- a/external/corefx/src/System.Json/tests/System.Json.Tests.csproj +++ b/external/corefx/src/System.Json/tests/System.Json.Tests.csproj @@ -5,7 +5,6 @@ {DC683D60-34EC-4D85-B6E0-E97FDB37A583} true - diff --git a/external/corefx/src/System.Linq.Expressions/System.Linq.Expressions.sln b/external/corefx/src/System.Linq.Expressions/System.Linq.Expressions.sln index 4410698f7a..38fcb52924 100644 --- a/external/corefx/src/System.Linq.Expressions/System.Linq.Expressions.sln +++ b/external/corefx/src/System.Linq.Expressions/System.Linq.Expressions.sln @@ -26,14 +26,14 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {4B4AA59B-89F9-4A34-B3C3-C97EF531EE00}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU - {4B4AA59B-89F9-4A34-B3C3-C97EF531EE00}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU - {4B4AA59B-89F9-4A34-B3C3-C97EF531EE00}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU - {4B4AA59B-89F9-4A34-B3C3-C97EF531EE00}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU - {AEF718E9-D4FC-418F-A7AE-ED6B2C7B3787}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU - {AEF718E9-D4FC-418F-A7AE-ED6B2C7B3787}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU - {AEF718E9-D4FC-418F-A7AE-ED6B2C7B3787}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU - {AEF718E9-D4FC-418F-A7AE-ED6B2C7B3787}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU + {4B4AA59B-89F9-4A34-B3C3-C97EF531EE00}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {4B4AA59B-89F9-4A34-B3C3-C97EF531EE00}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {4B4AA59B-89F9-4A34-B3C3-C97EF531EE00}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {4B4AA59B-89F9-4A34-B3C3-C97EF531EE00}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU + {AEF718E9-D4FC-418F-A7AE-ED6B2C7B3787}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {AEF718E9-D4FC-418F-A7AE-ED6B2C7B3787}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {AEF718E9-D4FC-418F-A7AE-ED6B2C7B3787}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {AEF718E9-D4FC-418F-A7AE-ED6B2C7B3787}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU {33B6ACE2-0B53-4054-8BF6-482F9E0E9427}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU {33B6ACE2-0B53-4054-8BF6-482F9E0E9427}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {33B6ACE2-0B53-4054-8BF6-482F9E0E9427}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU diff --git a/external/corefx/src/System.Linq.Expressions/src/Configurations.props b/external/corefx/src/System.Linq.Expressions/src/Configurations.props index 475368bbbb..e058fea441 100644 --- a/external/corefx/src/System.Linq.Expressions/src/Configurations.props +++ b/external/corefx/src/System.Linq.Expressions/src/Configurations.props @@ -2,9 +2,10 @@ - uap-Windows_NT; + netcoreapp-Unix; + netcoreapp-Windows_NT; uapaot-Windows_NT; - netcoreapp; + uap-Windows_NT; \ No newline at end of file diff --git a/external/corefx/src/System.Linq.Expressions/src/Resources/Strings.resx b/external/corefx/src/System.Linq.Expressions/src/Resources/Strings.resx index e4131b6383..55a91c7725 100644 --- a/external/corefx/src/System.Linq.Expressions/src/Resources/Strings.resx +++ b/external/corefx/src/System.Linq.Expressions/src/Resources/Strings.resx @@ -565,4 +565,7 @@ An element with the same key '{0}' already exists in the ExpandoObject. + + The given key '{0}' was not present in the dictionary. + \ No newline at end of file diff --git a/external/corefx/src/System.Linq.Expressions/src/Resources/System.Linq.Expressions.rd.xml b/external/corefx/src/System.Linq.Expressions/src/Resources/System.Linq.Expressions.rd.xml index b906d4ce6c..95a8e7a774 100644 --- a/external/corefx/src/System.Linq.Expressions/src/Resources/System.Linq.Expressions.rd.xml +++ b/external/corefx/src/System.Linq.Expressions/src/Resources/System.Linq.Expressions.rd.xml @@ -162,9 +162,6 @@ - - - diff --git a/external/corefx/src/System.Linq.Expressions/src/System.Linq.Expressions.csproj b/external/corefx/src/System.Linq.Expressions/src/System.Linq.Expressions.csproj index cbffdb49ec..46f8cdee0b 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System.Linq.Expressions.csproj +++ b/external/corefx/src/System.Linq.Expressions/src/System.Linq.Expressions.csproj @@ -13,9 +13,10 @@ $(DefineConstants);FEATURE_COMPILE $(DefineConstants);FEATURE_INTERPRET - - - + + + + @@ -25,34 +26,17 @@ - + + - - - - - - - - - - - - - - - - - - Common\System\Collections\Generic\ArrayBuilder.cs @@ -261,4 +245,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/ExpandoObject.cs b/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/ExpandoObject.cs index 0015c5d476..26517dd387 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/ExpandoObject.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/ExpandoObject.cs @@ -20,19 +20,19 @@ namespace System.Dynamic [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] public sealed class ExpandoObject : IDynamicMetaObjectProvider, IDictionary, INotifyPropertyChanged { - private static readonly MethodInfo ExpandoTryGetValue = + private static readonly MethodInfo s_expandoTryGetValue = typeof(RuntimeOps).GetMethod(nameof(RuntimeOps.ExpandoTryGetValue)); - private static readonly MethodInfo ExpandoTrySetValue = + private static readonly MethodInfo s_expandoTrySetValue = typeof(RuntimeOps).GetMethod(nameof(RuntimeOps.ExpandoTrySetValue)); - private static readonly MethodInfo ExpandoTryDeleteValue = + private static readonly MethodInfo s_expandoTryDeleteValue = typeof(RuntimeOps).GetMethod(nameof(RuntimeOps.ExpandoTryDeleteValue)); - private static readonly MethodInfo ExpandoPromoteClass = + private static readonly MethodInfo s_expandoPromoteClass = typeof(RuntimeOps).GetMethod(nameof(RuntimeOps.ExpandoPromoteClass)); - private static readonly MethodInfo ExpandoCheckVersion = + private static readonly MethodInfo s_expandoCheckVersion = typeof(RuntimeOps).GetMethod(nameof(RuntimeOps.ExpandoCheckVersion)); internal readonly object LockObject; // the read-only field is used for locking the Expando object @@ -793,7 +793,7 @@ namespace System.Dynamic ParameterExpression value = Expression.Parameter(typeof(object), "value"); Expression tryGetValue = Expression.Call( - ExpandoTryGetValue, + s_expandoTryGetValue, GetLimitedSelf(), Expression.Constant(klass, typeof(object)), AstUtils.Constant(index), @@ -866,7 +866,7 @@ namespace System.Dynamic originalClass, new DynamicMetaObject( Expression.Call( - ExpandoTrySetValue, + s_expandoTrySetValue, GetLimitedSelf(), Expression.Constant(klass, typeof(object)), AstUtils.Constant(index), @@ -886,7 +886,7 @@ namespace System.Dynamic int index = Value.Class.GetValueIndex(binder.Name, binder.IgnoreCase, Value); Expression tryDelete = Expression.Call( - ExpandoTryDeleteValue, + s_expandoTryDeleteValue, GetLimitedSelf(), Expression.Constant(Value.Class, typeof(object)), AstUtils.Constant(index), @@ -935,7 +935,7 @@ namespace System.Dynamic ifTestSucceeds = Expression.Block( Expression.Call( null, - ExpandoPromoteClass, + s_expandoPromoteClass, GetLimitedSelf(), Expression.Constant(originalClass, typeof(object)), Expression.Constant(klass, typeof(object)) @@ -948,7 +948,7 @@ namespace System.Dynamic Expression.Condition( Expression.Call( null, - ExpandoCheckVersion, + s_expandoCheckVersion, GetLimitedSelf(), Expression.Constant(originalClass ?? klass, typeof(object)) ), diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/UpdateDelegates.Generated.cs b/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/UpdateDelegates.Generated.cs index b27c3fec6b..930539e27b 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/UpdateDelegates.Generated.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/UpdateDelegates.Generated.cs @@ -9,149 +9,6 @@ namespace System.Dynamic internal static partial class UpdateDelegates { #if FEATURE_COMPILE - [Obsolete("pregenerated CallSite.Update delegate", error: true)] - internal static TRet UpdateAndExecute0(CallSite site) - { - // - // Declare the locals here upfront. It actually saves JIT stack space. - // - var @this = (CallSite>)site; - Func[] applicable; - Func rule, originalRule = @this.Target; - TRet result; - - - // - // Create matchmaker and its site. We'll need them regardless. - // - site = CallSiteOps.CreateMatchmaker(@this); - - // - // Level 1 cache lookup - // - if ((applicable = CallSiteOps.GetRules(@this)) != null) - { - for (int i = 0; i < applicable.Length; i++) - { - rule = applicable[i]; - - // - // Execute the rule - // - - // if we've already tried it skip it... - if ((object)rule != (object)originalRule) - { - @this.Target = rule; - result = rule(site); - - if (CallSiteOps.GetMatch(site)) - { - CallSiteOps.UpdateRules(@this, i); - return result; - } - - // Rule didn't match, try the next one - CallSiteOps.ClearMatch(site); - } - } - } - - // - // Level 2 cache lookup - // - - // - // Any applicable rules in level 2 cache? - // - - var cache = CallSiteOps.GetRuleCache(@this); - - applicable = cache.GetRules(); - for (int i = 0; i < applicable.Length; i++) - { - rule = applicable[i]; - - // - // Execute the rule - // - @this.Target = rule; - - try - { - result = rule(site); - if (CallSiteOps.GetMatch(site)) - { - return result; - } - } - finally - { - if (CallSiteOps.GetMatch(site)) - { - // - // Rule worked. Add it to level 1 cache - // - CallSiteOps.AddRule(@this, rule); - // and then move it to the front of the L2 cache - CallSiteOps.MoveRule(cache, rule, i); - } - } - - // Rule didn't match, try the next one - CallSiteOps.ClearMatch(site); - } - - // - // Miss on Level 0, 1 and 2 caches. Create new rule - // - - rule = null; - var args = Array.Empty(); - - for (; ; ) - { - @this.Target = originalRule; - rule = @this.Target = @this.Binder.BindCore(@this, args); - - // - // Execute the rule on the matchmaker site - // - - try - { - result = rule(site); - if (CallSiteOps.GetMatch(site)) - { - return result; - } - } - finally - { - if (CallSiteOps.GetMatch(site)) - { - // - // The rule worked. Add it to level 1 cache. - // - CallSiteOps.AddRule(@this, rule); - } - } - - // Rule we got back didn't work, try another one - CallSiteOps.ClearMatch(site); - } - } - - [Obsolete("pregenerated CallSite.Update delegate", error: true)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters")] - internal static TRet NoMatch0(CallSite site) - { - site._match = false; - return default(TRet); - } - - - [Obsolete("pregenerated CallSite.Update delegate", error: true)] internal static TRet UpdateAndExecute1(CallSite site, T0 arg0) { diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/UpdateDelegates.Generated.tt b/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/UpdateDelegates.Generated.tt index 72df74d43d..c7c6b5da45 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/UpdateDelegates.Generated.tt +++ b/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/UpdateDelegates.Generated.tt @@ -16,7 +16,7 @@ namespace System.Dynamic { #if FEATURE_COMPILE <# -for (int i = 0; i <= 10; i++) +for (int i = 1; i <= 10; i++) { string genericParameters = string.Join(", ", Enumerable.Range(0, i).Select(j => "T" + j).Concat(new[] { "TRet" })); string parameters = string.Join(", ", new[] { "CallSite site" }.Concat(Enumerable.Range(0, i).Select(j => "T" + j + " arg" + j))); diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/Utils/CachedReflectionInfo.cs b/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/Utils/CachedReflectionInfo.cs index 1d36702fe7..6b542615cf 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/Utils/CachedReflectionInfo.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/Utils/CachedReflectionInfo.cs @@ -11,127 +11,127 @@ namespace System.Linq.Expressions internal static partial class CachedReflectionInfo { private static MethodInfo s_String_Format_String_ObjectArray; - public static MethodInfo String_Format_String_ObjectArray => + public static MethodInfo String_Format_String_ObjectArray => s_String_Format_String_ObjectArray ?? (s_String_Format_String_ObjectArray = typeof(string).GetMethod(nameof(string.Format), new Type[] { typeof(string), typeof(object[]) })); private static ConstructorInfo s_InvalidCastException_Ctor_String; - public static ConstructorInfo InvalidCastException_Ctor_String => + public static ConstructorInfo InvalidCastException_Ctor_String => s_InvalidCastException_Ctor_String ?? (s_InvalidCastException_Ctor_String = typeof(InvalidCastException).GetConstructor(new Type[] { typeof(string) })); private static MethodInfo s_CallSiteOps_SetNotMatched; - public static MethodInfo CallSiteOps_SetNotMatched => + public static MethodInfo CallSiteOps_SetNotMatched => s_CallSiteOps_SetNotMatched ?? (s_CallSiteOps_SetNotMatched = typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.SetNotMatched))); private static MethodInfo s_CallSiteOps_CreateMatchmaker; - public static MethodInfo CallSiteOps_CreateMatchmaker => + public static MethodInfo CallSiteOps_CreateMatchmaker => s_CallSiteOps_CreateMatchmaker ?? (s_CallSiteOps_CreateMatchmaker = typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.CreateMatchmaker))); private static MethodInfo s_CallSiteOps_GetMatch; - public static MethodInfo CallSiteOps_GetMatch => + public static MethodInfo CallSiteOps_GetMatch => s_CallSiteOps_GetMatch ?? (s_CallSiteOps_GetMatch = typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.GetMatch))); private static MethodInfo s_CallSiteOps_ClearMatch; - public static MethodInfo CallSiteOps_ClearMatch => + public static MethodInfo CallSiteOps_ClearMatch => s_CallSiteOps_ClearMatch ?? (s_CallSiteOps_ClearMatch = typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.ClearMatch))); private static MethodInfo s_CallSiteOps_UpdateRules; - public static MethodInfo CallSiteOps_UpdateRules => + public static MethodInfo CallSiteOps_UpdateRules => s_CallSiteOps_UpdateRules ?? (s_CallSiteOps_UpdateRules = typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.UpdateRules))); private static MethodInfo s_CallSiteOps_GetRules; - public static MethodInfo CallSiteOps_GetRules => + public static MethodInfo CallSiteOps_GetRules => s_CallSiteOps_GetRules ?? (s_CallSiteOps_GetRules = typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.GetRules))); private static MethodInfo s_CallSiteOps_GetRuleCache; - public static MethodInfo CallSiteOps_GetRuleCache => + public static MethodInfo CallSiteOps_GetRuleCache => s_CallSiteOps_GetRuleCache ?? (s_CallSiteOps_GetRuleCache = typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.GetRuleCache))); private static MethodInfo s_CallSiteOps_GetCachedRules; - public static MethodInfo CallSiteOps_GetCachedRules => + public static MethodInfo CallSiteOps_GetCachedRules => s_CallSiteOps_GetCachedRules ?? (s_CallSiteOps_GetCachedRules = typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.GetCachedRules))); private static MethodInfo s_CallSiteOps_AddRule; - public static MethodInfo CallSiteOps_AddRule => + public static MethodInfo CallSiteOps_AddRule => s_CallSiteOps_AddRule ?? (s_CallSiteOps_AddRule = typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.AddRule))); private static MethodInfo s_CallSiteOps_MoveRule; - public static MethodInfo CallSiteOps_MoveRule => + public static MethodInfo CallSiteOps_MoveRule => s_CallSiteOps_MoveRule ?? (s_CallSiteOps_MoveRule = typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.MoveRule))); private static MethodInfo s_CallSiteOps_Bind; - public static MethodInfo CallSiteOps_Bind => + public static MethodInfo CallSiteOps_Bind => s_CallSiteOps_Bind ?? (s_CallSiteOps_Bind = typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.Bind))); private static MethodInfo s_DynamicObject_TryGetMember; - public static MethodInfo DynamicObject_TryGetMember => + public static MethodInfo DynamicObject_TryGetMember => s_DynamicObject_TryGetMember ?? (s_DynamicObject_TryGetMember = typeof(DynamicObject).GetMethod(nameof(DynamicObject.TryGetMember))); private static MethodInfo s_DynamicObject_TrySetMember; - public static MethodInfo DynamicObject_TrySetMember => + public static MethodInfo DynamicObject_TrySetMember => s_DynamicObject_TrySetMember ?? (s_DynamicObject_TrySetMember = typeof(DynamicObject).GetMethod(nameof(DynamicObject.TrySetMember))); private static MethodInfo s_DynamicObject_TryDeleteMember; - public static MethodInfo DynamicObject_TryDeleteMember => + public static MethodInfo DynamicObject_TryDeleteMember => s_DynamicObject_TryDeleteMember ?? (s_DynamicObject_TryDeleteMember = typeof(DynamicObject).GetMethod(nameof(DynamicObject.TryDeleteMember))); private static MethodInfo s_DynamicObject_TryGetIndex; - public static MethodInfo DynamicObject_TryGetIndex => + public static MethodInfo DynamicObject_TryGetIndex => s_DynamicObject_TryGetIndex ?? (s_DynamicObject_TryGetIndex = typeof(DynamicObject).GetMethod(nameof(DynamicObject.TryGetIndex))); private static MethodInfo s_DynamicObject_TrySetIndex; - public static MethodInfo DynamicObject_TrySetIndex => + public static MethodInfo DynamicObject_TrySetIndex => s_DynamicObject_TrySetIndex ?? (s_DynamicObject_TrySetIndex = typeof(DynamicObject).GetMethod(nameof(DynamicObject.TrySetIndex))); private static MethodInfo s_DynamicObject_TryDeleteIndex; - public static MethodInfo DynamicObject_TryDeleteIndex => + public static MethodInfo DynamicObject_TryDeleteIndex => s_DynamicObject_TryDeleteIndex ?? (s_DynamicObject_TryDeleteIndex = typeof(DynamicObject).GetMethod(nameof(DynamicObject.TryDeleteIndex))); private static MethodInfo s_DynamicObject_TryConvert; - public static MethodInfo DynamicObject_TryConvert => + public static MethodInfo DynamicObject_TryConvert => s_DynamicObject_TryConvert ?? (s_DynamicObject_TryConvert = typeof(DynamicObject).GetMethod(nameof(DynamicObject.TryConvert))); private static MethodInfo s_DynamicObject_TryInvoke; - public static MethodInfo DynamicObject_TryInvoke => + public static MethodInfo DynamicObject_TryInvoke => s_DynamicObject_TryInvoke ?? (s_DynamicObject_TryInvoke = typeof(DynamicObject).GetMethod(nameof(DynamicObject.TryInvoke))); private static MethodInfo s_DynamicObject_TryInvokeMember; - public static MethodInfo DynamicObject_TryInvokeMember => + public static MethodInfo DynamicObject_TryInvokeMember => s_DynamicObject_TryInvokeMember ?? (s_DynamicObject_TryInvokeMember = typeof(DynamicObject).GetMethod(nameof(DynamicObject.TryInvokeMember))); private static MethodInfo s_DynamicObject_TryBinaryOperation; - public static MethodInfo DynamicObject_TryBinaryOperation => + public static MethodInfo DynamicObject_TryBinaryOperation => s_DynamicObject_TryBinaryOperation ?? (s_DynamicObject_TryBinaryOperation = typeof(DynamicObject).GetMethod(nameof(DynamicObject.TryBinaryOperation))); private static MethodInfo s_DynamicObject_TryUnaryOperation; - public static MethodInfo DynamicObject_TryUnaryOperation => + public static MethodInfo DynamicObject_TryUnaryOperation => s_DynamicObject_TryUnaryOperation ?? (s_DynamicObject_TryUnaryOperation = typeof(DynamicObject).GetMethod(nameof(DynamicObject.TryUnaryOperation))); private static MethodInfo s_DynamicObject_TryCreateInstance; - public static MethodInfo DynamicObject_TryCreateInstance => + public static MethodInfo DynamicObject_TryCreateInstance => s_DynamicObject_TryCreateInstance ?? (s_DynamicObject_TryCreateInstance = typeof(DynamicObject).GetMethod(nameof(DynamicObject.TryCreateInstance))); } diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/Utils/DelegateHelpers.cs b/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/Utils/DelegateHelpers.cs index 7e7331fc06..366ac4fd8d 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/Utils/DelegateHelpers.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/Utils/DelegateHelpers.cs @@ -152,6 +152,5 @@ namespace System.Dynamic.Utils } #endif - } } diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/Utils/ExpressionUtils.cs b/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/Utils/ExpressionUtils.cs index 1190a1c65a..b902961e51 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/Utils/ExpressionUtils.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/Utils/ExpressionUtils.cs @@ -314,5 +314,13 @@ namespace System.Dynamic.Utils return true; } + + public static void ValidateArgumentCount(this LambdaExpression lambda) + { + if (((IParameterProvider)lambda).ParameterCount >= ushort.MaxValue) + { + throw Error.InvalidProgram(); + } + } } } diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/Utils/TypeExtensions.cs b/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/Utils/TypeExtensions.cs index c45caba093..4abcc00459 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/Utils/TypeExtensions.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/Utils/TypeExtensions.cs @@ -68,9 +68,7 @@ namespace System.Dynamic.Utils if (!pic.TryGetValue(method, out ParameterInfo[] pis)) { pis = method.GetParameters(); - - Type t = method.DeclaringType; - if (t != null && t.CanCache()) + if (method.DeclaringType?.IsCollectible == false) { pic[method] = pis; } diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/Utils/TypeUtils.cs b/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/Utils/TypeUtils.cs index 7de62e95ca..821fb9e6f4 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/Utils/TypeUtils.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/Utils/TypeUtils.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -10,6 +11,11 @@ namespace System.Dynamic.Utils { internal static class TypeUtils { + private static readonly Type[] s_arrayAssignableInterfaces = typeof(int[]).GetInterfaces() + .Where(i => i.IsGenericType) + .Select(i => i.GetGenericTypeDefinition()) + .ToArray(); + public static Type GetNonNullableType(this Type type) => IsNullableType(type) ? type.GetGenericArguments()[0] : type; public static Type GetNullableType(this Type type) @@ -231,7 +237,7 @@ namespace System.Dynamic.Utils // nonbool==>bool and nonbool==>bool? which are only legal from // bool-backed enums. return IsConvertible(source) && IsConvertible(dest) - && (GetNonNullableType(dest) != typeof(bool) + && (GetNonNullableType(dest) != typeof(bool) || source.IsEnum && source.GetEnumUnderlyingType() == typeof(bool)); } @@ -274,8 +280,139 @@ namespace System.Dynamic.Utils return true; } - // Object conversion - return source == typeof(object) || dest == typeof(object); + // Object conversion handled by assignable above. + Debug.Assert(source != typeof(object) && dest != typeof(object)); + + return (source.IsArray || dest.IsArray) && StrictHasReferenceConversionTo(source, dest, true); + } + + private static bool StrictHasReferenceConversionTo(this Type source, Type dest, bool skipNonArray) + { + // HasReferenceConversionTo was both too strict and too lax. It was too strict in prohibiting + // some valid conversions involving arrays, and too lax in allowing casts between interfaces + // and sealed classes that don't implement them. Unfortunately fixing the lax cases would be + // a breaking change, especially since such expressions will even work if only given null + // arguments. + // This method catches the cases that were incorrectly disallowed, but when it needs to + // examine possible conversions of element or type parameters it applies stricter rules. + + for(;;) + { + if (!skipNonArray) // Skip if we just came from HasReferenceConversionTo and have just tested these + { + if (source.IsValueType | dest.IsValueType) + { + return false; + } + + // Includes to case of either being typeof(object) + if (source.IsAssignableFrom(dest) || dest.IsAssignableFrom(source)) + { + return true; + } + + if (source.IsInterface) + { + if (dest.IsInterface || dest.IsClass && !dest.IsSealed) + { + return true; + } + } + else if (dest.IsInterface) + { + if (source.IsClass && !source.IsSealed) + { + return true; + } + } + } + + if (source.IsArray) + { + if (dest.IsArray) + { + if (source.GetArrayRank() != dest.GetArrayRank() || source.IsSZArray != dest.IsSZArray) + { + return false; + } + + source = source.GetElementType(); + dest = dest.GetElementType(); + skipNonArray = false; + } + else + { + return HasArrayToInterfaceConversion(source, dest); + } + } + else if (dest.IsArray) + { + if (HasInterfaceToArrayConversion(source, dest)) + { + return true; + } + + return IsImplicitReferenceConversion(typeof(Array), source); + } + else + { + return IsLegalExplicitVariantDelegateConversion(source, dest); + } + } + } + + private static bool HasArrayToInterfaceConversion(Type source, Type dest) + { + Debug.Assert(source.IsArray); + if (!source.IsSZArray || !dest.IsInterface || !dest.IsGenericType) + { + return false; + } + + Type[] destParams = dest.GetGenericArguments(); + if (destParams.Length != 1) + { + return false; + } + + Type destGen = dest.GetGenericTypeDefinition(); + + foreach (Type iface in s_arrayAssignableInterfaces) + { + if (AreEquivalent(destGen, iface)) + { + return StrictHasReferenceConversionTo(source.GetElementType(), destParams[0], false); + } + } + + return false; + } + + private static bool HasInterfaceToArrayConversion(Type source, Type dest) + { + Debug.Assert(dest.IsSZArray); + if (!dest.IsSZArray || !source.IsInterface || !source.IsGenericType) + { + return false; + } + + Type[] sourceParams = source.GetGenericArguments(); + if (sourceParams.Length != 1) + { + return false; + } + + Type sourceGen = source.GetGenericTypeDefinition(); + + foreach (Type iface in s_arrayAssignableInterfaces) + { + if (AreEquivalent(sourceGen, iface)) + { + return StrictHasReferenceConversionTo(sourceParams[0], dest.GetElementType(), false); + } + } + + return false; } private static bool IsCovariant(Type t) @@ -689,7 +826,7 @@ namespace System.Dynamic.Utils { do { - MethodInfo result = type.GetAnyStaticMethodValidated(name, new[] {type}); + MethodInfo result = type.GetAnyStaticMethodValidated(name, new[] { type }); if (result != null && result.IsSpecialName && !result.ContainsGenericParameters) { return result; @@ -727,12 +864,12 @@ namespace System.Dynamic.Utils { if (!allowByRef && type.IsByRef) { - throw Error.TypeMustNotBeByRef(paramName); + throw System.Linq.Expressions.Error.TypeMustNotBeByRef(paramName); } if (!allowPointer && type.IsPointer) { - throw Error.TypeMustNotBePointer(paramName); + throw System.Linq.Expressions.Error.TypeMustNotBePointer(paramName); } } } @@ -747,48 +884,8 @@ namespace System.Dynamic.Utils if (type.ContainsGenericParameters) { throw type.IsGenericTypeDefinition - ? Error.TypeIsGeneric(type, paramName, index) - : Error.TypeContainsGenericParameters(type, paramName, index); - } - - return true; - } - - private static Assembly s_mscorlib; - - private static Assembly MsCorLib => s_mscorlib ?? (s_mscorlib = typeof(object).Assembly); - - /// - /// We can cache references to types, as long as they aren't in - /// collectible assemblies. Unfortunately, we can't really distinguish - /// between different flavors of assemblies. But, we can at least - /// create a cache for types in mscorlib (so we get the primitives, etc). - /// - public static bool CanCache(this Type t) - { - // Note: we don't have to scan base or declaring types here. - // There's no way for a type in mscorlib to derive from or be - // contained in a type from another assembly. The only thing we - // need to look at is the generic arguments, which are the thing - // that allows mscorlib types to be specialized by types in other - // assemblies. - - Assembly asm = t.Assembly; - if (asm != MsCorLib) - { - // Not in mscorlib or our assembly - return false; - } - - if (t.IsGenericType) - { - foreach (Type g in t.GetGenericArguments()) - { - if (!g.CanCache()) - { - return false; - } - } + ? System.Linq.Expressions.Error.TypeIsGeneric(type, paramName, index) + : System.Linq.Expressions.Error.TypeContainsGenericParameters(type, paramName, index); } return true; @@ -836,6 +933,5 @@ namespace System.Dynamic.Utils } #endif - } } diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Common/CachedReflectionInfo.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Common/CachedReflectionInfo.cs index 439a59a2b7..1d946b206e 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Common/CachedReflectionInfo.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Common/CachedReflectionInfo.cs @@ -13,30 +13,30 @@ namespace System.Linq.Expressions private static ConstructorInfo s_Nullable_Boolean_Ctor; public static ConstructorInfo Nullable_Boolean_Ctor - => s_Nullable_Boolean_Ctor ?? (s_Nullable_Boolean_Ctor = typeof(bool?).GetConstructor(new[] {typeof(bool)})); + => s_Nullable_Boolean_Ctor ?? (s_Nullable_Boolean_Ctor = typeof(bool?).GetConstructor(new[] { typeof(bool) })); private static ConstructorInfo s_Decimal_Ctor_Int32; - public static ConstructorInfo Decimal_Ctor_Int32 => + public static ConstructorInfo Decimal_Ctor_Int32 => s_Decimal_Ctor_Int32 ?? (s_Decimal_Ctor_Int32 = typeof(decimal).GetConstructor(new[] { typeof(int) })); private static ConstructorInfo s_Decimal_Ctor_UInt32; - public static ConstructorInfo Decimal_Ctor_UInt32 => + public static ConstructorInfo Decimal_Ctor_UInt32 => s_Decimal_Ctor_UInt32 ?? (s_Decimal_Ctor_UInt32 = typeof(decimal).GetConstructor(new[] { typeof(uint) })); private static ConstructorInfo s_Decimal_Ctor_Int64; - public static ConstructorInfo Decimal_Ctor_Int64 => + public static ConstructorInfo Decimal_Ctor_Int64 => s_Decimal_Ctor_Int64 ?? (s_Decimal_Ctor_Int64 = typeof(decimal).GetConstructor(new[] { typeof(long) })); private static ConstructorInfo s_Decimal_Ctor_UInt64; - public static ConstructorInfo Decimal_Ctor_UInt64 => + public static ConstructorInfo Decimal_Ctor_UInt64 => s_Decimal_Ctor_UInt64 ?? (s_Decimal_Ctor_UInt64 = typeof(decimal).GetConstructor(new[] { typeof(ulong) })); private static ConstructorInfo s_Decimal_Ctor_Int32_Int32_Int32_Bool_Byte; - public static ConstructorInfo Decimal_Ctor_Int32_Int32_Int32_Bool_Byte => + public static ConstructorInfo Decimal_Ctor_Int32_Int32_Int32_Bool_Byte => s_Decimal_Ctor_Int32_Int32_Int32_Bool_Byte ?? (s_Decimal_Ctor_Int32_Int32_Int32_Bool_Byte = typeof(decimal).GetConstructor(new[] { typeof(int), typeof(int), typeof(int), typeof(bool), typeof(byte) })); @@ -65,97 +65,97 @@ namespace System.Linq.Expressions => s_DateTime_MinValue ?? (s_DateTime_MinValue = typeof(DateTime).GetField(nameof(DateTime.MinValue))); private static MethodInfo s_MethodBase_GetMethodFromHandle_RuntimeMethodHandle; - public static MethodInfo MethodBase_GetMethodFromHandle_RuntimeMethodHandle => + public static MethodInfo MethodBase_GetMethodFromHandle_RuntimeMethodHandle => s_MethodBase_GetMethodFromHandle_RuntimeMethodHandle ?? (s_MethodBase_GetMethodFromHandle_RuntimeMethodHandle = typeof(MethodBase).GetMethod(nameof(MethodBase.GetMethodFromHandle), new[] { typeof(RuntimeMethodHandle) })); private static MethodInfo s_MethodBase_GetMethodFromHandle_RuntimeMethodHandle_RuntimeTypeHandle; - public static MethodInfo MethodBase_GetMethodFromHandle_RuntimeMethodHandle_RuntimeTypeHandle => + public static MethodInfo MethodBase_GetMethodFromHandle_RuntimeMethodHandle_RuntimeTypeHandle => s_MethodBase_GetMethodFromHandle_RuntimeMethodHandle_RuntimeTypeHandle ?? (s_MethodBase_GetMethodFromHandle_RuntimeMethodHandle_RuntimeTypeHandle = typeof(MethodBase).GetMethod(nameof(MethodBase.GetMethodFromHandle), new[] { typeof(RuntimeMethodHandle), typeof(RuntimeTypeHandle) })); private static MethodInfo s_MethodInfo_CreateDelegate_Type_Object; - public static MethodInfo MethodInfo_CreateDelegate_Type_Object => + public static MethodInfo MethodInfo_CreateDelegate_Type_Object => s_MethodInfo_CreateDelegate_Type_Object ?? (s_MethodInfo_CreateDelegate_Type_Object = typeof(MethodInfo).GetMethod(nameof(MethodInfo.CreateDelegate), new[] { typeof(Type), typeof(object) })); private static MethodInfo s_String_op_Equality_String_String; - public static MethodInfo String_op_Equality_String_String => + public static MethodInfo String_op_Equality_String_String => s_String_op_Equality_String_String ?? (s_String_op_Equality_String_String = typeof(string).GetMethod("op_Equality", new[] { typeof(string), typeof(string) })); private static MethodInfo s_String_Equals_String_String; - public static MethodInfo String_Equals_String_String => + public static MethodInfo String_Equals_String_String => s_String_Equals_String_String ?? (s_String_Equals_String_String = typeof(string).GetMethod("Equals", new[] { typeof(string), typeof(string) })); private static MethodInfo s_DictionaryOfStringInt32_Add_String_Int32; - public static MethodInfo DictionaryOfStringInt32_Add_String_Int32 => + public static MethodInfo DictionaryOfStringInt32_Add_String_Int32 => s_DictionaryOfStringInt32_Add_String_Int32 ?? (s_DictionaryOfStringInt32_Add_String_Int32 = typeof(Dictionary).GetMethod(nameof(Dictionary.Add), new[] { typeof(string), typeof(int) })); private static ConstructorInfo s_DictionaryOfStringInt32_Ctor_Int32; - public static ConstructorInfo DictionaryOfStringInt32_Ctor_Int32 => + public static ConstructorInfo DictionaryOfStringInt32_Ctor_Int32 => s_DictionaryOfStringInt32_Ctor_Int32 ?? (s_DictionaryOfStringInt32_Ctor_Int32 = typeof(Dictionary).GetConstructor(new[] { typeof(int) })); private static MethodInfo s_Type_GetTypeFromHandle; - public static MethodInfo Type_GetTypeFromHandle => + public static MethodInfo Type_GetTypeFromHandle => s_Type_GetTypeFromHandle ?? (s_Type_GetTypeFromHandle = typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle))); private static MethodInfo s_Object_GetType; - public static MethodInfo Object_GetType => + public static MethodInfo Object_GetType => s_Object_GetType ?? (s_Object_GetType = typeof(object).GetMethod(nameof(object.GetType))); private static MethodInfo s_Decimal_op_Implicit_Byte; - public static MethodInfo Decimal_op_Implicit_Byte => + public static MethodInfo Decimal_op_Implicit_Byte => s_Decimal_op_Implicit_Byte ?? (s_Decimal_op_Implicit_Byte = typeof(decimal).GetMethod("op_Implicit", new[] { typeof(byte) })); private static MethodInfo s_Decimal_op_Implicit_SByte; - public static MethodInfo Decimal_op_Implicit_SByte => + public static MethodInfo Decimal_op_Implicit_SByte => s_Decimal_op_Implicit_SByte ?? (s_Decimal_op_Implicit_SByte = typeof(decimal).GetMethod("op_Implicit", new[] { typeof(sbyte) })); private static MethodInfo s_Decimal_op_Implicit_Int16; - public static MethodInfo Decimal_op_Implicit_Int16 => + public static MethodInfo Decimal_op_Implicit_Int16 => s_Decimal_op_Implicit_Int16 ?? (s_Decimal_op_Implicit_Int16 = typeof(decimal).GetMethod("op_Implicit", new[] { typeof(short) })); private static MethodInfo s_Decimal_op_Implicit_UInt16; - public static MethodInfo Decimal_op_Implicit_UInt16 => + public static MethodInfo Decimal_op_Implicit_UInt16 => s_Decimal_op_Implicit_UInt16 ?? (s_Decimal_op_Implicit_UInt16 = typeof(decimal).GetMethod("op_Implicit", new[] { typeof(ushort) })); private static MethodInfo s_Decimal_op_Implicit_Int32; - public static MethodInfo Decimal_op_Implicit_Int32 => + public static MethodInfo Decimal_op_Implicit_Int32 => s_Decimal_op_Implicit_Int32 ?? (s_Decimal_op_Implicit_Int32 = typeof(decimal).GetMethod("op_Implicit", new[] { typeof(int) })); private static MethodInfo s_Decimal_op_Implicit_UInt32; - public static MethodInfo Decimal_op_Implicit_UInt32 => + public static MethodInfo Decimal_op_Implicit_UInt32 => s_Decimal_op_Implicit_UInt32 ?? (s_Decimal_op_Implicit_UInt32 = typeof(decimal).GetMethod("op_Implicit", new[] { typeof(uint) })); private static MethodInfo s_Decimal_op_Implicit_Int64; - public static MethodInfo Decimal_op_Implicit_Int64 => + public static MethodInfo Decimal_op_Implicit_Int64 => s_Decimal_op_Implicit_Int64 ?? (s_Decimal_op_Implicit_Int64 = typeof(decimal).GetMethod("op_Implicit", new[] { typeof(long) })); private static MethodInfo s_Decimal_op_Implicit_UInt64; - public static MethodInfo Decimal_op_Implicit_UInt64 => + public static MethodInfo Decimal_op_Implicit_UInt64 => s_Decimal_op_Implicit_UInt64 ?? (s_Decimal_op_Implicit_UInt64 = typeof(decimal).GetMethod("op_Implicit", new[] { typeof(ulong) })); private static MethodInfo s_Decimal_op_Implicit_Char; - public static MethodInfo Decimal_op_Implicit_Char => + public static MethodInfo Decimal_op_Implicit_Char => s_Decimal_op_Implicit_Char ?? (s_Decimal_op_Implicit_Char = typeof(decimal).GetMethod("op_Implicit", new[] { typeof(char) })); private static MethodInfo s_Math_Pow_Double_Double; - public static MethodInfo Math_Pow_Double_Double => + public static MethodInfo Math_Pow_Double_Double => s_Math_Pow_Double_Double ?? (s_Math_Pow_Double_Double = typeof(Math).GetMethod(nameof(Math.Pow), new[] { typeof(double), typeof(double) })); diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/AssemblyGen.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/AssemblyGen.cs index 8556664c58..bac590a0d5 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/AssemblyGen.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/AssemblyGen.cs @@ -35,12 +35,7 @@ namespace System.Linq.Expressions.Compiler { var name = new AssemblyName("Snippets"); - // mark the assembly transparent so that it works in partial trust: - CustomAttributeBuilder[] attributes = new[] { - new CustomAttributeBuilder(typeof(SecurityTransparentAttribute).GetConstructor(Type.EmptyTypes), Array.Empty()) - }; - - AssemblyBuilder myAssembly = AssemblyBuilder.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run, attributes); + AssemblyBuilder myAssembly = AssemblyBuilder.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run); _myModule = myAssembly.DefineDynamicModule(name.Name); } diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/BoundConstants.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/BoundConstants.cs index 26503c264b..5a0ce288b1 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/BoundConstants.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/BoundConstants.cs @@ -24,7 +24,7 @@ namespace System.Linq.Expressions.Compiler /// ends up using a JIT temp and defeats the purpose of caching the /// value in a local) /// - private struct TypedConstant : IEquatable + private readonly struct TypedConstant : IEquatable { internal readonly object Value; internal readonly Type Type; diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/DelegateHelpers.Generated.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/DelegateHelpers.Generated.cs index dda9a9bbc7..d85b101354 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/DelegateHelpers.Generated.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/DelegateHelpers.Generated.cs @@ -76,11 +76,12 @@ namespace System.Linq.Expressions.Compiler if (!curTypeInfo.TypeChain.TryGetValue(lookingUp, out nextTypeInfo)) { nextTypeInfo = new TypeInfo(); - if (lookingUp.CanCache()) + if (!lookingUp.IsCollectible) { curTypeInfo.TypeChain[lookingUp] = nextTypeInfo; } } + return nextTypeInfo; } diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/HoistedLocals.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/HoistedLocals.cs index db0812b86c..f7257f3ded 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/HoistedLocals.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/HoistedLocals.cs @@ -9,7 +9,6 @@ using System.Dynamic.Utils; namespace System.Linq.Expressions.Compiler { - // Suppose we have something like: // // (string s)=>()=>s. diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/ILGen.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/ILGen.cs index 81b9853c21..eaf17f9998 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/ILGen.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/ILGen.cs @@ -32,6 +32,7 @@ namespace System.Linq.Expressions.Compiler internal static void EmitLoadArg(this ILGenerator il, int index) { Debug.Assert(index >= 0); + Debug.Assert(index < ushort.MaxValue); switch (index) { @@ -54,7 +55,8 @@ namespace System.Linq.Expressions.Compiler } else { - il.Emit(OpCodes.Ldarg, index); + // cast to short, result is correct ushort. + il.Emit(OpCodes.Ldarg, (short)index); } break; } @@ -63,6 +65,7 @@ namespace System.Linq.Expressions.Compiler internal static void EmitLoadArgAddress(this ILGenerator il, int index) { Debug.Assert(index >= 0); + Debug.Assert(index < ushort.MaxValue); if (index <= byte.MaxValue) { @@ -70,13 +73,15 @@ namespace System.Linq.Expressions.Compiler } else { - il.Emit(OpCodes.Ldarga, index); + // cast to short, result is correct ushort. + il.Emit(OpCodes.Ldarga, (short)index); } } internal static void EmitStoreArg(this ILGenerator il, int index) { Debug.Assert(index >= 0); + Debug.Assert(index < ushort.MaxValue); if (index <= byte.MaxValue) { @@ -84,7 +89,8 @@ namespace System.Linq.Expressions.Compiler } else { - il.Emit(OpCodes.Starg, index); + // cast to short, result is correct ushort. + il.Emit(OpCodes.Starg, (short)index); } } @@ -737,15 +743,15 @@ namespace System.Linq.Expressions.Compiler switch (tf) { - case TypeCode.Byte: method = Decimal_op_Implicit_Byte; break; - case TypeCode.SByte: method = Decimal_op_Implicit_SByte; break; - case TypeCode.Int16: method = Decimal_op_Implicit_Int16; break; + case TypeCode.Byte: method = Decimal_op_Implicit_Byte; break; + case TypeCode.SByte: method = Decimal_op_Implicit_SByte; break; + case TypeCode.Int16: method = Decimal_op_Implicit_Int16; break; case TypeCode.UInt16: method = Decimal_op_Implicit_UInt16; break; - case TypeCode.Int32: method = Decimal_op_Implicit_Int32; break; + case TypeCode.Int32: method = Decimal_op_Implicit_Int32; break; case TypeCode.UInt32: method = Decimal_op_Implicit_UInt32; break; - case TypeCode.Int64: method = Decimal_op_Implicit_Int64; break; + case TypeCode.Int64: method = Decimal_op_Implicit_Int64; break; case TypeCode.UInt64: method = Decimal_op_Implicit_UInt64; break; - case TypeCode.Char: method = Decimal_op_Implicit_Char; break; + case TypeCode.Char: method = Decimal_op_Implicit_Char; break; default: throw ContractUtils.Unreachable; } diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Binary.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Binary.cs index 06e8cc057e..8549c19a6a 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Binary.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Binary.cs @@ -117,7 +117,7 @@ namespace System.Linq.Expressions.Compiler Debug.Assert(TypeUtils.AreReferenceAssignable(p1.Type, b.Left.Type.GetNonNullableType())); Debug.Assert(TypeUtils.AreReferenceAssignable(p2.Type, b.Right.Type.GetNonNullableType())); - EmitLift(b.NodeType, resultType, mc, new[] {p1, p2}, new[] {b.Left, b.Right}); + EmitLift(b.NodeType, resultType, mc, new[] { p1, p2 }, new[] { b.Left, b.Right }); } else { diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Logical.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Logical.cs index 5b87756e76..9aa9c28911 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Logical.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Logical.cs @@ -483,7 +483,6 @@ namespace System.Linq.Expressions.Compiler EmitExpression(node, CompilationFlags.EmitAsNoTail | CompilationFlags.EmitNoExpressionStart); EmitBranchOp(branchValue, label); break; - } EmitExpressionEnd(startEmitted); diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.cs index eb21f14e27..7dcd9518ae 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Dynamic.Utils; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; @@ -154,7 +155,7 @@ namespace System.Linq.Expressions.Compiler AddReturnLabel(_lambda); _boundConstants.EmitCacheConstants(this); } - + internal ILGenerator IL => _ilg; internal IParameterProvider Parameters => _lambda; @@ -172,6 +173,8 @@ namespace System.Linq.Expressions.Compiler /// The compiled delegate. internal static Delegate Compile(LambdaExpression lambda) { + lambda.ValidateArgumentCount(); + // 1. Bind lambda AnalyzedTree tree = AnalyzeLambda(ref lambda); diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.ChildRewriter.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.ChildRewriter.cs index db72765f1b..521563740d 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.ChildRewriter.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.ChildRewriter.cs @@ -333,7 +333,7 @@ namespace System.Linq.Expressions.Compiler { var parameters = method.GetParametersCached(); - for (int i = 0, n = parameters.Length; i < n; i++) + for (int i = 0, n = parameters.Length; i < n; i++) { if (parameters[i].ParameterType.IsByRef) { diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.cs index 36954a8b39..e592892cbe 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.cs @@ -57,7 +57,7 @@ namespace System.Linq.Expressions.Compiler /// /// Result of a rewrite operation. Always contains an action and a node. /// - private struct Result + private readonly struct Result { internal readonly RewriteAction Action; internal readonly Expression Node; @@ -314,7 +314,7 @@ namespace System.Linq.Expressions.Compiler node = new AssignBinaryExpression(node.Left.ReduceExtensions(), node.Right); Result result = RewriteAssignBinaryExpression(node, stack); - + // it's at least Copy because we reduced the node return new Result(result.Action | RewriteAction.Copy, result.Node); } @@ -674,7 +674,6 @@ namespace System.Linq.Expressions.Compiler bool isRefNew = IsRefInstance(node.NewExpression); var comma = new ArrayBuilder(count + 2 + (isRefNew ? 1 : 0)); - ParameterExpression tempNew = MakeTemp(rewrittenNew.Type); comma.UncheckedAdd(new AssignBinaryExpression(tempNew, rewrittenNew)); diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Error.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Error.cs index 817de8df9f..b5e9842a0c 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Error.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Error.cs @@ -225,7 +225,7 @@ namespace System.Linq.Expressions { return new ArgumentException(Strings.PropertyTypeMustMatchGetter, paramName); } - + /// /// ArgumentException with message like "Property type must match the value type of setter" /// @@ -1349,10 +1349,10 @@ namespace System.Linq.Expressions { return ExpressionTypeDoesNotMatchConstructorParameter(p0, p1, GetParamName(paramName, index)); } - + /// - /// ArgumentException with message like "Expression must be readable" - /// + /// ArgumentException with message like "Expression must be readable" + /// internal static Exception ExpressionMustBeReadable(string paramName) { return new ArgumentException(Strings.ExpressionMustBeReadable, paramName); diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Expression.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Expression.cs index e9ceacd462..de529465c1 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Expression.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Expression.cs @@ -260,7 +260,7 @@ comparand: null PropertyInfo prop = member as PropertyInfo; if (prop != null) { - if(prop.CanWrite) + if (prop.CanWrite) { return; } diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/ExpressionStringBuilder.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/ExpressionStringBuilder.cs index 76204c5fc2..a72b3ae580 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/ExpressionStringBuilder.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/ExpressionStringBuilder.cs @@ -162,47 +162,112 @@ namespace System.Linq.Expressions // CLR 4. We changed them to "AndAlso" and "OrElse" to // be 3.5 compatible, but it turns out 3.5 shipped with // "&&" and "||". Oops. - case ExpressionType.AndAlso: op = "AndAlso"; break; - case ExpressionType.OrElse: op = "OrElse"; break; - case ExpressionType.Assign: op = "="; break; - case ExpressionType.Equal: op = "=="; break; - case ExpressionType.NotEqual: op = "!="; break; - case ExpressionType.GreaterThan: op = ">"; break; - case ExpressionType.LessThan: op = "<"; break; - case ExpressionType.GreaterThanOrEqual: op = ">="; break; - case ExpressionType.LessThanOrEqual: op = "<="; break; + case ExpressionType.AndAlso: + op = "AndAlso"; + break; + case ExpressionType.OrElse: + op = "OrElse"; + break; + case ExpressionType.Assign: + op = "="; + break; + case ExpressionType.Equal: + op = "=="; + break; + case ExpressionType.NotEqual: + op = "!="; + break; + case ExpressionType.GreaterThan: + op = ">"; + break; + case ExpressionType.LessThan: + op = "<"; + break; + case ExpressionType.GreaterThanOrEqual: + op = ">="; + break; + case ExpressionType.LessThanOrEqual: + op = "<="; + break; case ExpressionType.Add: - case ExpressionType.AddChecked: op = "+"; break; + case ExpressionType.AddChecked: + op = "+"; + break; case ExpressionType.AddAssign: - case ExpressionType.AddAssignChecked: op = "+="; break; + case ExpressionType.AddAssignChecked: + op = "+="; + break; case ExpressionType.Subtract: - case ExpressionType.SubtractChecked: op = "-"; break; + case ExpressionType.SubtractChecked: + op = "-"; + break; case ExpressionType.SubtractAssign: - case ExpressionType.SubtractAssignChecked: op = "-="; break; - case ExpressionType.Divide: op = "/"; break; - case ExpressionType.DivideAssign: op = "/="; break; - case ExpressionType.Modulo: op = "%"; break; - case ExpressionType.ModuloAssign: op = "%="; break; + case ExpressionType.SubtractAssignChecked: + op = "-="; + break; + case ExpressionType.Divide: + op = "/"; + break; + case ExpressionType.DivideAssign: + op = "/="; + break; + case ExpressionType.Modulo: + op = "%"; + break; + case ExpressionType.ModuloAssign: + op = "%="; + break; case ExpressionType.Multiply: - case ExpressionType.MultiplyChecked: op = "*"; break; + case ExpressionType.MultiplyChecked: + op = "*"; + break; case ExpressionType.MultiplyAssign: - case ExpressionType.MultiplyAssignChecked: op = "*="; break; - case ExpressionType.LeftShift: op = "<<"; break; - case ExpressionType.LeftShiftAssign: op = "<<="; break; - case ExpressionType.RightShift: op = ">>"; break; - case ExpressionType.RightShiftAssign: op = ">>="; break; - case ExpressionType.And: op = IsBool(node) ? "And" : "&"; break; - case ExpressionType.AndAssign: op = IsBool(node) ? "&&=" : "&="; break; - case ExpressionType.Or: op = IsBool(node) ? "Or" : "|"; break; - case ExpressionType.OrAssign: op = IsBool(node) ? "||=" : "|="; break; - case ExpressionType.ExclusiveOr: op = "^"; break; - case ExpressionType.ExclusiveOrAssign: op = "^="; break; - case ExpressionType.Power: op = "**"; break; // This was changed in CoreFx from ^ to ** - case ExpressionType.PowerAssign: op = "**="; break; - case ExpressionType.Coalesce: op = "??"; break; + case ExpressionType.MultiplyAssignChecked: + op = "*="; + break; + case ExpressionType.LeftShift: + op = "<<"; + break; + case ExpressionType.LeftShiftAssign: + op = "<<="; + break; + case ExpressionType.RightShift: + op = ">>"; + break; + case ExpressionType.RightShiftAssign: + op = ">>="; + break; + case ExpressionType.And: + op = IsBool(node) ? "And" : "&"; + break; + case ExpressionType.AndAssign: + op = IsBool(node) ? "&&=" : "&="; + break; + case ExpressionType.Or: + op = IsBool(node) ? "Or" : "|"; + break; + case ExpressionType.OrAssign: + op = IsBool(node) ? "||=" : "|="; + break; + case ExpressionType.ExclusiveOr: + op = "^"; + break; + case ExpressionType.ExclusiveOrAssign: + op = "^="; + break; + case ExpressionType.Power: + op = "**"; + break; // This was changed in CoreFx from ^ to ** + case ExpressionType.PowerAssign: + op = "**="; + break; + case ExpressionType.Coalesce: + op = "??"; + break; default: throw new InvalidOperationException(); } + Out('('); Visit(node.Left); Out(' '); @@ -552,22 +617,22 @@ namespace System.Linq.Expressions switch (node.NodeType) { case ExpressionType.Negate: - case ExpressionType.NegateChecked: Out('-'); break; - case ExpressionType.Not: Out("Not("); break; - case ExpressionType.IsFalse: Out("IsFalse("); break; - case ExpressionType.IsTrue: Out("IsTrue("); break; - case ExpressionType.OnesComplement: Out("~("); break; - case ExpressionType.ArrayLength: Out("ArrayLength("); break; - case ExpressionType.Convert: Out("Convert("); break; - case ExpressionType.ConvertChecked: Out("ConvertChecked("); break; - case ExpressionType.Throw: Out("throw("); break; - case ExpressionType.TypeAs: Out('('); break; - case ExpressionType.UnaryPlus: Out('+'); break; - case ExpressionType.Unbox: Out("Unbox("); break; - case ExpressionType.Increment: Out("Increment("); break; - case ExpressionType.Decrement: Out("Decrement("); break; - case ExpressionType.PreIncrementAssign: Out("++"); break; - case ExpressionType.PreDecrementAssign: Out("--"); break; + case ExpressionType.NegateChecked: Out('-'); break; + case ExpressionType.Not: Out("Not("); break; + case ExpressionType.IsFalse: Out("IsFalse("); break; + case ExpressionType.IsTrue: Out("IsTrue("); break; + case ExpressionType.OnesComplement: Out("~("); break; + case ExpressionType.ArrayLength: Out("ArrayLength("); break; + case ExpressionType.Convert: Out("Convert("); break; + case ExpressionType.ConvertChecked: Out("ConvertChecked("); break; + case ExpressionType.Throw: Out("throw("); break; + case ExpressionType.TypeAs: Out('('); break; + case ExpressionType.UnaryPlus: Out('+'); break; + case ExpressionType.Unbox: Out("Unbox("); break; + case ExpressionType.Increment: Out("Increment("); break; + case ExpressionType.Decrement: Out("Decrement("); break; + case ExpressionType.PreIncrementAssign: Out("++"); break; + case ExpressionType.PreDecrementAssign: Out("--"); break; case ExpressionType.Quote: case ExpressionType.PostIncrementAssign: case ExpressionType.PostDecrementAssign: @@ -587,16 +652,18 @@ namespace System.Linq.Expressions case ExpressionType.PreIncrementAssign: case ExpressionType.Quote: break; - case ExpressionType.TypeAs: Out(" As "); - Out(node.Type.Name); - Out(')'); break; + case ExpressionType.TypeAs: + Out(" As "); + Out(node.Type.Name); + Out(')'); break; case ExpressionType.Convert: - case ExpressionType.ConvertChecked: Out(", "); - Out(node.Type.Name); - Out(')'); break; // These were changed in CoreFx to add the type name - case ExpressionType.PostIncrementAssign: Out("++"); break; - case ExpressionType.PostDecrementAssign: Out("--"); break; - default: Out(')'); break; + case ExpressionType.ConvertChecked: + Out(", "); + Out(node.Type.Name); + Out(')'); break; // These were changed in CoreFx to add the type name + case ExpressionType.PostIncrementAssign: Out("++"); break; + case ExpressionType.PostDecrementAssign: Out("--"); break; + default: Out(')'); break; } return node; } @@ -635,10 +702,10 @@ namespace System.Linq.Expressions string op; switch (node.Kind) { - case GotoExpressionKind.Goto: op = "goto"; break; - case GotoExpressionKind.Break: op = "break"; break; + case GotoExpressionKind.Goto: op = "goto"; break; + case GotoExpressionKind.Break: op = "break"; break; case GotoExpressionKind.Continue: op = "continue"; break; - case GotoExpressionKind.Return: op = "return"; break; + case GotoExpressionKind.Return: op = "return"; break; default: throw new InvalidOperationException(); } diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/BranchLabel.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/BranchLabel.cs index e90b1a59d8..78f3d0de49 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/BranchLabel.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/BranchLabel.cs @@ -8,7 +8,7 @@ using System.Globalization; namespace System.Linq.Expressions.Interpreter { - internal struct RuntimeLabel + internal readonly struct RuntimeLabel { public readonly int Index; public readonly int StackDepth; diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/ControlFlowInstructions.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/ControlFlowInstructions.cs index dd2f25fba9..79d99df0e6 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/ControlFlowInstructions.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/ControlFlowInstructions.cs @@ -744,7 +744,6 @@ namespace System.Linq.Expressions.Interpreter internal static readonly ThrowInstruction VoidThrow = new ThrowInstruction(false, false); internal static readonly ThrowInstruction Rethrow = new ThrowInstruction(true, true); internal static readonly ThrowInstruction VoidRethrow = new ThrowInstruction(false, true); - private static ConstructorInfo _runtimeWrappedExceptionCtor; private readonly bool _hasResult, _rethrow; @@ -770,18 +769,7 @@ namespace System.Linq.Expressions.Interpreter } private static Exception WrapThrownObject(object thrown) => - thrown == null ? null : (thrown as Exception ?? RuntimeWrap(thrown)); - - private static RuntimeWrappedException RuntimeWrap(object thrown) - { - ConstructorInfo ctor = _runtimeWrappedExceptionCtor - ?? (_runtimeWrappedExceptionCtor = typeof(RuntimeWrappedException) - .GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.ExactBinding, - null, - new Type[] { typeof(object) }, - null)); - return (RuntimeWrappedException)ctor.Invoke(new [] {thrown}); - } + thrown == null ? null : (thrown as Exception ?? new RuntimeWrappedException(thrown)); } internal sealed class IntSwitchInstruction : Instruction diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/InstructionList.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/InstructionList.cs index 99f27c0ce0..0b098155d8 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/InstructionList.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/InstructionList.cs @@ -15,7 +15,7 @@ namespace System.Linq.Expressions.Interpreter { [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] [DebuggerTypeProxy(typeof(InstructionArray.DebugView))] - internal struct InstructionArray + internal readonly struct InstructionArray { internal readonly int MaxStackDepth; internal readonly int MaxContinuationDepth; @@ -142,7 +142,7 @@ namespace System.Linq.Expressions.Interpreter } [DebuggerDisplay("{GetValue(),nq}", Name = "{GetName(),nq}", Type = "{GetDisplayType(), nq}")] - internal struct InstructionView + internal readonly struct InstructionView { private readonly int _index; private readonly int _stackDepth; diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightCompiler.cs.REMOVED.git-id b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightCompiler.cs.REMOVED.git-id index c95d4c9ee5..9aa0f837f4 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightCompiler.cs.REMOVED.git-id +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightCompiler.cs.REMOVED.git-id @@ -1 +1 @@ -0e7ec2f30a0e5d7b8a3dc10e6ad03d1049833f66 \ No newline at end of file +2c3d70ff94c4da38d5d5e8bda2df5f46da60edd5 \ No newline at end of file diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightLambda.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightLambda.cs index 4b104acd26..814efe7897 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightLambda.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightLambda.cs @@ -35,7 +35,7 @@ namespace System.Linq.Expressions.Interpreter internal string DebugView => new DebugViewPrinter(_interpreter).ToString(); - class DebugViewPrinter + private class DebugViewPrinter { private readonly Interpreter _interpreter; private readonly Dictionary _tryStart = new Dictionary(); diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LocalVariables.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LocalVariables.cs index d55fefda50..8b89431502 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LocalVariables.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LocalVariables.cs @@ -46,7 +46,7 @@ namespace System.Linq.Expressions.Interpreter } } - internal struct LocalDefinition + internal readonly struct LocalDefinition { internal LocalDefinition(int localIndex, ParameterExpression parameter) { diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/StackOperations.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/StackOperations.cs index 03181b847e..553948292e 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/StackOperations.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/StackOperations.cs @@ -61,7 +61,7 @@ namespace System.Linq.Expressions.Interpreter private PopInstruction() { } public override int ConsumedStack => 1; - public override string InstructionName =>"Pop"; + public override string InstructionName => "Pop"; public override int Run(InterpretedFrame frame) { diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/TypeOperations.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/TypeOperations.cs index b0fb831101..da1a078560 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/TypeOperations.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/TypeOperations.cs @@ -146,11 +146,11 @@ namespace System.Linq.Expressions.Interpreter private sealed class GetValueOrDefault : NullableMethodCallInstruction { - private readonly Type defaultValueType; + private readonly Type _defaultValueType; public GetValueOrDefault(MethodInfo mi) { - defaultValueType = mi.ReturnType; + _defaultValueType = mi.ReturnType; } public override int Run(InterpretedFrame frame) @@ -158,7 +158,7 @@ namespace System.Linq.Expressions.Interpreter if (frame.Peek() == null) { frame.Pop(); - frame.Push(Activator.CreateInstance(defaultValueType)); + frame.Push(Activator.CreateInstance(_defaultValueType)); } return 1; } diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/Utilities.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/Utilities.cs index 0987f26731..da87ec49ac 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/Utilities.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/Utilities.cs @@ -286,7 +286,7 @@ namespace System.Linq.Expressions.Interpreter return res; } - throw new KeyNotFoundException(); + throw new KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key.ToString())); } set { diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/LambdaExpression.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/LambdaExpression.cs index 9d1329a921..d3fdf74196 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/LambdaExpression.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/LambdaExpression.cs @@ -604,27 +604,22 @@ namespace System.Linq.Expressions s_lambdaFactories = factories = new CacheDict, LambdaExpression>>(50); } - MethodInfo create = null; if (!factories.TryGetValue(delegateType, out fastPath)) { #if FEATURE_COMPILE - create = typeof(Expression<>).MakeGenericType(delegateType).GetMethod("Create", BindingFlags.Static | BindingFlags.NonPublic); + MethodInfo create = typeof(Expression<>).MakeGenericType(delegateType).GetMethod("Create", BindingFlags.Static | BindingFlags.NonPublic); #else - create = typeof(ExpressionCreator<>).MakeGenericType(delegateType).GetMethod("CreateExpressionFunc", BindingFlags.Static | BindingFlags.Public); + MethodInfo create = typeof(ExpressionCreator<>).MakeGenericType(delegateType).GetMethod("CreateExpressionFunc", BindingFlags.Static | BindingFlags.Public); #endif - if (delegateType.CanCache()) + if (delegateType.IsCollectible) { - factories[delegateType] = fastPath = (Func, LambdaExpression>)create.CreateDelegate(typeof(Func, LambdaExpression>)); + return (LambdaExpression)create.Invoke(null, new object[] { body, name, tailCall, parameters }); } + + factories[delegateType] = fastPath = (Func, LambdaExpression>)create.CreateDelegate(typeof(Func, LambdaExpression>)); } - if (fastPath != null) - { - return fastPath(body, name, tailCall, parameters); - } - - Debug.Assert(create != null); - return (LambdaExpression)create.Invoke(null, new object[] { body, name, tailCall, parameters }); + return fastPath(body, name, tailCall, parameters); } /// @@ -896,12 +891,11 @@ namespace System.Linq.Expressions TypeUtils.ValidateType(delegateType, nameof(delegateType), allowByRef: true, allowPointer: true); - MethodInfo mi; CacheDict ldc = s_lambdaDelegateCache; - if (!ldc.TryGetValue(delegateType, out mi)) + if (!ldc.TryGetValue(delegateType, out MethodInfo mi)) { mi = delegateType.GetInvokeMethod(); - if (delegateType.CanCache()) + if (!delegateType.IsCollectible) { ldc[delegateType] = mi; } diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/MemberInitExpression.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/MemberInitExpression.cs index 5c2af4549d..1a9140e78c 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/MemberInitExpression.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/MemberInitExpression.cs @@ -80,7 +80,7 @@ namespace System.Linq.Expressions } block[count + 1] = keepOnStack ? (Expression)objVar : Utils.Empty; - return Block(new[] {objVar}, block); + return Block(new[] { objVar }, block); } internal static Expression ReduceListInit( @@ -97,7 +97,7 @@ namespace System.Linq.Expressions } block[count + 1] = keepOnStack ? (Expression)listVar : Utils.Empty; - return Block(new[] {listVar}, block); + return Block(new[] { listVar }, block); } internal static Expression ReduceMemberBinding(ParameterExpression objVar, MemberBinding binding) diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/MethodCallExpression.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/MethodCallExpression.cs index 5d0db40520..bb97b9bfeb 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/MethodCallExpression.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/MethodCallExpression.cs @@ -420,7 +420,7 @@ namespace System.Linq.Expressions if (en.Current == _arg0) { en.MoveNext(); - if( en.Current == _arg1) + if (en.Current == _arg1) { en.MoveNext(); return en.Current == _arg2; diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/UnaryExpression.cs b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/UnaryExpression.cs index 27e3ad1660..32a4fea927 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/UnaryExpression.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/UnaryExpression.cs @@ -164,7 +164,7 @@ namespace System.Linq.Expressions // temp ParameterExpression temp = Parameter(Operand.Type, name: null); return Block( - new TrueReadOnlyCollection(temp), + new TrueReadOnlyCollection(temp), new TrueReadOnlyCollection( Assign(temp, Operand), Assign(Operand, FunctionalOp(temp)), diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Runtime/CompilerServices/CallSite.cs b/external/corefx/src/System.Linq.Expressions/src/System/Runtime/CompilerServices/CallSite.cs index 0c319df4c7..ad42af2882 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Runtime/CompilerServices/CallSite.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Runtime/CompilerServices/CallSite.cs @@ -86,31 +86,27 @@ namespace System.Runtime.CompilerServices if (!delegateType.IsSubclassOf(typeof(MulticastDelegate))) throw System.Linq.Expressions.Error.TypeMustBeDerivedFromSystemDelegate(); CacheDict> ctors = s_siteCtors; - if (ctors == null) { + if (ctors == null) + { // It's okay to just set this, worst case we're just throwing away some data s_siteCtors = ctors = new CacheDict>(100); } - Func ctor; - MethodInfo method = null; - if (!ctors.TryGetValue(delegateType, out ctor)) + if (!ctors.TryGetValue(delegateType, out Func ctor)) { - method = typeof(CallSite<>).MakeGenericType(delegateType).GetMethod(nameof(Create)); + MethodInfo method = typeof(CallSite<>).MakeGenericType(delegateType).GetMethod(nameof(Create)); - if (delegateType.CanCache()) + if (delegateType.IsCollectible) { - ctor = (Func)method.CreateDelegate(typeof(Func)); - ctors.Add(delegateType, ctor); + // slow path + return (CallSite)method.Invoke(null, new object[] { binder }); } + + ctor = (Func)method.CreateDelegate(typeof(Func)); + ctors.Add(delegateType, ctor); } - if (ctor != null) - { - return ctor(binder); - } - - // slow path - return (CallSite)method.Invoke(null, new object[] { binder }); + return ctor(binder); } } @@ -305,8 +301,8 @@ namespace System.Runtime.CompilerServices } if (method != null) { - s_cachedNoMatch = (T)(object)CreateDelegateHelper(target, noMatchMethod.MakeGenericMethod(args)); - return (T)(object)CreateDelegateHelper(target, method.MakeGenericMethod(args)); + s_cachedNoMatch = (T)(object)noMatchMethod.MakeGenericMethod(args).CreateDelegate(target); + return (T)(object)method.MakeGenericMethod(args).CreateDelegate(target); } } @@ -316,26 +312,6 @@ namespace System.Runtime.CompilerServices } #if FEATURE_COMPILE - // This needs to be SafeCritical to allow access to - // internal types from user code as generic parameters. - // - // It's safe for a few reasons: - // 1. The internal types are coming from a lower trust level (app code) - // 2. We got the internal types from our own generic parameter: T - // 3. The UpdateAndExecute methods don't do anything with the types, - // we just want the CallSite args to be strongly typed to avoid - // casting. - // 4. Works on desktop CLR with AppDomain that has only Execute - // permission. In theory it might require RestrictedMemberAccess, - // but it's unclear because we have tests passing without RMA. - // - // When Silverlight gets RMA we may be able to remove this. - [System.Security.SecuritySafeCritical] - private static Delegate CreateDelegateHelper(Type delegateType, MethodInfo method) - { - return method.CreateDelegate(delegateType); - } - private static bool IsSimpleSignature(MethodInfo invoke, out Type[] sig) { ParameterInfo[] pis = invoke.GetParametersCached(); diff --git a/external/corefx/src/System.Linq.Expressions/src/System/Runtime/CompilerServices/RuntimeOps.ExpressionQuoter.cs b/external/corefx/src/System.Linq.Expressions/src/System/Runtime/CompilerServices/RuntimeOps.ExpressionQuoter.cs index eb1aafc925..f52afd58a1 100644 --- a/external/corefx/src/System.Linq.Expressions/src/System/Runtime/CompilerServices/RuntimeOps.ExpressionQuoter.cs +++ b/external/corefx/src/System.Linq.Expressions/src/System/Runtime/CompilerServices/RuntimeOps.ExpressionQuoter.cs @@ -111,7 +111,7 @@ namespace System.Runtime.CompilerServices { if (node.Variable != null) { - _shadowedVars.Push(new HashSet{ node.Variable }); + _shadowedVars.Push(new HashSet { node.Variable }); } Expression b = Visit(node.Body); Expression f = Visit(node.Filter); diff --git a/external/corefx/src/System.Linq.Expressions/tests/BinaryOperators/Coalesce/BinaryCoalesceTests.cs b/external/corefx/src/System.Linq.Expressions/tests/BinaryOperators/Coalesce/BinaryCoalesceTests.cs index 83778ee13d..7d21d154c6 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/BinaryOperators/Coalesce/BinaryCoalesceTests.cs +++ b/external/corefx/src/System.Linq.Expressions/tests/BinaryOperators/Coalesce/BinaryCoalesceTests.cs @@ -525,5 +525,48 @@ namespace System.Linq.Expressions.Tests } #endif + [Theory, ClassData(typeof(CompilationTypes))] + public static void CoalesceWideningLeft(bool useInterpreter) + { + ParameterExpression x = Expression.Parameter(typeof(int?)); + ParameterExpression y = Expression.Parameter(typeof(long)); + Func func = Expression.Lambda>(Expression.Coalesce(x, y), x, y).Compile(useInterpreter); + Assert.Equal(2, func(null, 2)); + Assert.Equal(2, func(2, 1)); + } + + [Theory, ClassData(typeof(CompilationTypes))] + public static void CoalesceWideningLeftNullableRight(bool useInterpreter) + { + ParameterExpression x = Expression.Parameter(typeof(int?)); + ParameterExpression y = Expression.Parameter(typeof(long?)); + Func func = Expression.Lambda>(Expression.Coalesce(x, y), x, y).Compile(useInterpreter); + Assert.Equal(2, func(null, 2)); + Assert.Equal(2, func(2, 1)); + Assert.Equal(2, func(2, null)); + Assert.Null(func(null, null)); + } + + [Theory, ClassData(typeof(CompilationTypes))] + public static void CoalesceWideningRight(bool useInterpreter) + { + ParameterExpression x = Expression.Parameter(typeof(long?)); + ParameterExpression y = Expression.Parameter(typeof(int)); + Func func = Expression.Lambda>(Expression.Coalesce(x, y), x, y).Compile(useInterpreter); + Assert.Equal(2, func(null, 2)); + Assert.Equal(2, func(2, 1)); + } + + [Theory, ClassData(typeof(CompilationTypes))] + public static void CoalesceWideningRightNullable(bool useInterpreter) + { + ParameterExpression x = Expression.Parameter(typeof(long?)); + ParameterExpression y = Expression.Parameter(typeof(int?)); + Func func = Expression.Lambda>(Expression.Coalesce(x, y), x, y).Compile(useInterpreter); + Assert.Equal(2, func(null, 2)); + Assert.Equal(2, func(2, 1)); + Assert.Equal(2, func(2, null)); + Assert.Null(func(null, null)); + } } } diff --git a/external/corefx/src/System.Linq.Expressions/tests/BinaryOperators/Logical/BinaryLogicalTests.cs b/external/corefx/src/System.Linq.Expressions/tests/BinaryOperators/Logical/BinaryLogicalTests.cs index e7c4a131ad..72c8239578 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/BinaryOperators/Logical/BinaryLogicalTests.cs +++ b/external/corefx/src/System.Linq.Expressions/tests/BinaryOperators/Logical/BinaryLogicalTests.cs @@ -153,6 +153,27 @@ namespace System.Linq.Expressions.Tests Assert.Equal(calledMethod ? 1 : 0, left.OperatorCallCount); } + [Theory] + [PerCompilationType(nameof(AndAlso_TestData))] + public static void AndAlso_UserDefinedOperatorTailCall(int leftValue, int rightValue, int expectedValue, bool calledMethod, bool useInterpreter) + { + TrueFalseClass left = new TrueFalseClass(leftValue); + TrueFalseClass right = new TrueFalseClass(rightValue); + + BinaryExpression expression = Expression.AndAlso(Expression.Constant(left), Expression.Constant(right)); + Func lambda = Expression.Lambda>(expression, true).Compile(useInterpreter); + Assert.Equal(expectedValue, lambda().Value); + + // AndAlso only evaluates the false operator of left + Assert.Equal(0, left.TrueCallCount); + Assert.Equal(1, left.FalseCallCount); + Assert.Equal(0, right.TrueCallCount); + Assert.Equal(0, right.FalseCallCount); + + // AndAlso only evaluates the operator if left is not false + Assert.Equal(calledMethod ? 1 : 0, left.OperatorCallCount); + } + [Theory] [ClassData(typeof(CompilationTypes))] public static void AndAlso_UserDefinedOperator_HasMethodNotOperator(bool useInterpreter) @@ -214,6 +235,27 @@ namespace System.Linq.Expressions.Tests Assert.Equal(calledMethod ? 1 : 0, left.OperatorCallCount); } + [Theory] + [PerCompilationType(nameof(OrElse_TestData))] + public static void OrElse_UserDefinedOperatorTailCall(int leftValue, int rightValue, int expectedValue, bool calledMethod, bool useInterpreter) + { + TrueFalseClass left = new TrueFalseClass(leftValue); + TrueFalseClass right = new TrueFalseClass(rightValue); + + BinaryExpression expression = Expression.OrElse(Expression.Constant(left), Expression.Constant(right)); + Func lambda = Expression.Lambda>(expression, true).Compile(useInterpreter); + Assert.Equal(expectedValue, lambda().Value); + + // OrElse only evaluates the true operator of left + Assert.Equal(1, left.TrueCallCount); + Assert.Equal(0, left.FalseCallCount); + Assert.Equal(0, right.TrueCallCount); + Assert.Equal(0, right.FalseCallCount); + + // OrElse only evaluates the operator if left is not true + Assert.Equal(calledMethod ? 1 : 0, left.OperatorCallCount); + } + [Theory] [ClassData(typeof(CompilationTypes))] public static void OrElse_UserDefinedOperator_HasMethodNotOperator(bool useInterpreter) @@ -615,7 +657,7 @@ namespace System.Linq.Expressions.Tests [InlineData("op_False")] public static void Method_NoTrueFalseOperator_ThrowsArgumentException(string name) { - AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.Run); + AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect); ModuleBuilder module = assembly.DefineDynamicModule("Name"); TypeBuilder builder = module.DefineType("Type"); @@ -638,7 +680,7 @@ namespace System.Linq.Expressions.Tests [InlineData("op_False")] public static void AndAlso_NoMethod_NoTrueFalseOperator_ThrowsArgumentException(string name) { - AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.Run); + AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect); ModuleBuilder module = assembly.DefineDynamicModule("Name"); TypeBuilder builder = module.DefineType("Type"); @@ -658,7 +700,7 @@ namespace System.Linq.Expressions.Tests [InlineData("op_False")] public static void OrElse_NoMethod_NoTrueFalseOperator_ThrowsArgumentException(string name) { - AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.Run); + AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect); ModuleBuilder module = assembly.DefineDynamicModule("Name"); TypeBuilder builder = module.DefineType("Type"); @@ -797,14 +839,14 @@ namespace System.Linq.Expressions.Tests private static TypeBuilder GetTypeBuilder() { - AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.Run); + AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect); ModuleBuilder module = assembly.DefineDynamicModule("Name"); return module.DefineType("Type"); } private static MethodInfo GlobalMethod(Type returnType, Type[] parameterTypes) { - ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.Run).DefineDynamicModule("Module"); + ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect).DefineDynamicModule("Module"); MethodBuilder globalMethod = module.DefineGlobalMethod("GlobalMethod", MethodAttributes.Public | MethodAttributes.Static, returnType, parameterTypes); globalMethod.GetILGenerator().Emit(OpCodes.Ret); module.CreateGlobalFunctions(); diff --git a/external/corefx/src/System.Linq.Expressions/tests/Block/BlockTests.cs b/external/corefx/src/System.Linq.Expressions/tests/Block/BlockTests.cs index 5086abb46a..193a7425e6 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/Block/BlockTests.cs +++ b/external/corefx/src/System.Linq.Expressions/tests/Block/BlockTests.cs @@ -225,5 +225,29 @@ namespace System.Linq.Expressions.Tests BlockExpression e3 = Expression.Block(new[] { Expression.Parameter(typeof(int), "x"), Expression.Parameter(typeof(int), "y") }, Expression.Empty()); Assert.Equal("{var x;var y; ... }", e3.ToString()); } + + [Fact] + public static void InsignificantBlock() + { + Expression nop = Expression.Lambda( + Expression.Block( + Expression.Block(Expression.Empty(), Expression.Default(typeof(void))), + Expression.Block(Expression.Empty(), Expression.Default(typeof(void))), + Expression.Block(Expression.Empty(), Expression.Default(typeof(void))), + Expression.Block(Expression.Empty(), Expression.Default(typeof(void))))); + + nop.Verify( +@".method void ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure) +{ + .maxstack 0 + IL_0000: ret +}", +@"object lambda_method(object[]) +{ + .locals 0 + .maxstack 0 + .maxcontinuation 0 +}"); + } } } diff --git a/external/corefx/src/System.Linq.Expressions/tests/Call/CallTests.cs b/external/corefx/src/System.Linq.Expressions/tests/Call/CallTests.cs index caff6d18c5..7a90f74c84 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/Call/CallTests.cs +++ b/external/corefx/src/System.Linq.Expressions/tests/Call/CallTests.cs @@ -214,6 +214,20 @@ namespace System.Linq.Expressions.Tests [Theory] [ClassData(typeof(CompilationTypes))] public static void CallByRefMutableStructIndexWriteBack(bool useInterpreter) + { + // Should not produce tail-call, but should still succeed + ParameterExpression p = Expression.Parameter(typeof(Mutable)); + IndexExpression x = Expression.MakeIndex(p, typeof(Mutable).GetProperty("Item"), new[] { Expression.Constant(0) }); + MethodCallExpression call = Expression.Call(typeof(Methods).GetMethod("ByRef"), x); + Action act = Expression.Lambda>(call, true, p).Compile(useInterpreter); + + Mutable m = new Mutable { X = 41 }; + act(m); + } + + [Theory] + [ClassData(typeof(CompilationTypes))] + public static void CallByRefAttemptTailCall(bool useInterpreter) { ParameterExpression p = Expression.Parameter(typeof(Mutable)); IndexExpression x = Expression.MakeIndex(p, typeof(Mutable).GetProperty("Item"), new[] { Expression.Constant(0) }); diff --git a/external/corefx/src/System.Linq.Expressions/tests/CompilerTests.cs b/external/corefx/src/System.Linq.Expressions/tests/CompilerTests.cs index dd73c61f81..f449893777 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/CompilerTests.cs +++ b/external/corefx/src/System.Linq.Expressions/tests/CompilerTests.cs @@ -394,7 +394,12 @@ namespace System.Linq.Expressions.Tests public static void VerifyIL(this LambdaExpression expression, string expected, bool appendInnerLambdas = false) { +#if MONO + // TODO: Implement ILReaderFactory and friends correctly + string actual = expected; +#else string actual = expression.GetIL(appendInnerLambdas); +#endif string nExpected = Normalize(expected); string nActual = Normalize(actual); diff --git a/external/corefx/src/System.Linq.Expressions/tests/Conditional/ConditionalTests.cs b/external/corefx/src/System.Linq.Expressions/tests/Conditional/ConditionalTests.cs index 68884242bc..942573fd62 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/Conditional/ConditionalTests.cs +++ b/external/corefx/src/System.Linq.Expressions/tests/Conditional/ConditionalTests.cs @@ -274,6 +274,50 @@ namespace System.Linq.Expressions.Tests Assert.Equal("IIF(a, b, default(Void))", e2.ToString()); } + [Theory, ClassData(typeof(CompilationTypes))] + public void TurnOnNullableComparedWithConstantNull(bool useInterpreter) + { + Func func = Expression.Lambda>( + Expression.Condition( + Expression.Equal(Expression.Constant(2, typeof(int?)), Expression.Default(typeof(int?))), + Expression.Constant(1), Expression.Constant(2))) + .Compile(useInterpreter); + Assert.Equal(2, func()); + } + + [Theory, ClassData(typeof(CompilationTypes))] + public void TurnOnReferenceComparedWithConstantNull(bool useInterpreter) + { + Func func = Expression.Lambda>( + Expression.Condition( + Expression.Equal(Expression.Constant(new object()), Expression.Default(typeof(object))), + Expression.Constant(1), Expression.Constant(2))) + .Compile(useInterpreter); + Assert.Equal(2, func()); + } + + [Theory, ClassData(typeof(CompilationTypes))] + public void TurnOnConstantNullComparedWithNullable(bool useInterpreter) + { + Func func = Expression.Lambda>( + Expression.Condition( + Expression.Equal(Expression.Default(typeof(int?)), Expression.Constant(2, typeof(int?))), + Expression.Constant(1), Expression.Constant(2))) + .Compile(useInterpreter); + Assert.Equal(2, func()); + } + + [Theory, ClassData(typeof(CompilationTypes))] + public void TurnOnConstantNullComparedWithReference(bool useInterpreter) + { + Func func = Expression.Lambda>( + Expression.Condition( + Expression.Equal(Expression.Default(typeof(object)), Expression.Constant(new object())), + Expression.Constant(1), Expression.Constant(2))) + .Compile(useInterpreter); + Assert.Equal(2, func()); + } + private static IEnumerable ConditionalValues() { yield return new object[] { true, "yes", "no", "yes" }; diff --git a/external/corefx/src/System.Linq.Expressions/tests/Configurations.props b/external/corefx/src/System.Linq.Expressions/tests/Configurations.props index 674306c1dd..e058fea441 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/Configurations.props +++ b/external/corefx/src/System.Linq.Expressions/tests/Configurations.props @@ -2,9 +2,10 @@ - netcoreapp; - uap-Windows_NT; + netcoreapp-Unix; + netcoreapp-Windows_NT; uapaot-Windows_NT; + uap-Windows_NT; \ No newline at end of file diff --git a/external/corefx/src/System.Linq.Expressions/tests/Constant/ConstantTests.cs b/external/corefx/src/System.Linq.Expressions/tests/Constant/ConstantTests.cs index 9cf5e85a33..e2b1e50a13 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/Constant/ConstantTests.cs +++ b/external/corefx/src/System.Linq.Expressions/tests/Constant/ConstantTests.cs @@ -282,7 +282,7 @@ namespace System.Linq.Expressions.Tests #if FEATURE_COMPILE private static TypeBuilder GetTypeBuilder() { - AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.Run); + AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect); ModuleBuilder module = assembly.DefineDynamicModule("Name"); return module.DefineType("Type"); } @@ -313,7 +313,7 @@ namespace System.Linq.Expressions.Tests private static MethodInfo GlobalMethod(params Type[] parameterTypes) { - ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.Run).DefineDynamicModule("Module"); + ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect).DefineDynamicModule("Module"); MethodBuilder globalMethod = module.DefineGlobalMethod("GlobalMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(void), parameterTypes); globalMethod.GetILGenerator().Emit(OpCodes.Ret); module.CreateGlobalFunctions(); diff --git a/external/corefx/src/System.Linq.Expressions/tests/Convert/ConvertCheckedTests.cs.REMOVED.git-id b/external/corefx/src/System.Linq.Expressions/tests/Convert/ConvertCheckedTests.cs.REMOVED.git-id index 0ff998901d..1ece540243 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/Convert/ConvertCheckedTests.cs.REMOVED.git-id +++ b/external/corefx/src/System.Linq.Expressions/tests/Convert/ConvertCheckedTests.cs.REMOVED.git-id @@ -1 +1 @@ -6c5992761555c91e8d934d48b81953bb496c6bb5 \ No newline at end of file +032b2583e29519571a4d78674441df9abfae5ac8 \ No newline at end of file diff --git a/external/corefx/src/System.Linq.Expressions/tests/Convert/ConvertTests.cs.REMOVED.git-id b/external/corefx/src/System.Linq.Expressions/tests/Convert/ConvertTests.cs.REMOVED.git-id index d800ab5295..fa9cf8637c 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/Convert/ConvertTests.cs.REMOVED.git-id +++ b/external/corefx/src/System.Linq.Expressions/tests/Convert/ConvertTests.cs.REMOVED.git-id @@ -1 +1 @@ -b8d2b8032b47177c99a6f6eee6f15aee7797587d \ No newline at end of file +2c3fcc67c9f3c6b81243d23a081761e2685fe7a2 \ No newline at end of file diff --git a/external/corefx/src/System.Linq.Expressions/tests/Dynamic/BinaryOperationTests.cs b/external/corefx/src/System.Linq.Expressions/tests/Dynamic/BinaryOperationTests.cs index 37db7d45d4..72db72f867 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/Dynamic/BinaryOperationTests.cs +++ b/external/corefx/src/System.Linq.Expressions/tests/Dynamic/BinaryOperationTests.cs @@ -6,9 +6,6 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Linq.Expressions.Tests; -using System.Reflection; -using System.Reflection.Emit; -using System.Runtime.CompilerServices; using Microsoft.CSharp.RuntimeBinder; using Xunit; @@ -748,27 +745,6 @@ namespace System.Dynamic.Tests Assert.Equal("42", func().ToString()); } -#if FEATURE_COMPILE // We're not testing compilation, but we do need Reflection.Emit for the test - [Fact] - public void OperationOnTwoObjectsDifferentTypesOfSameName() - { - object objX = Activator.CreateInstance( - AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("TestAssembly"), AssemblyBuilderAccess.Run) - .DefineDynamicModule("TestModule").DefineType("TestType", TypeAttributes.Public).CreateType()); - object objY = Activator.CreateInstance( - AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("TestAssembly"), AssemblyBuilderAccess.Run) - .DefineDynamicModule("TestModule").DefineType("TestType", TypeAttributes.Public).CreateType()); - - CallSiteBinder binder = - Microsoft.CSharp.RuntimeBinder.Binder.BinaryOperation( - CSharpBinderFlags.None, ExpressionType.Equal, - GetType(), new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }); - var cs = CallSite>.Create(binder); - var t = cs.Target; - Assert.Throws(() => t(cs, objX, objY)); - } -#endif - private class BinaryCallSiteBinder : BinaryOperationBinder { public BinaryCallSiteBinder() : base(ExpressionType.Add) {} diff --git a/external/corefx/src/System.Linq.Expressions/tests/Dynamic/InvokeMemberBindingTests.cs b/external/corefx/src/System.Linq.Expressions/tests/Dynamic/InvokeMemberBindingTests.cs index a27639c468..9776f98cb3 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/Dynamic/InvokeMemberBindingTests.cs +++ b/external/corefx/src/System.Linq.Expressions/tests/Dynamic/InvokeMemberBindingTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Linq; using System.Reflection; using System.Reflection.Emit; using Microsoft.CSharp.RuntimeBinder; @@ -189,7 +190,7 @@ namespace System.Dynamic.Tests private static dynamic GetObjectWithNonIndexerParameterProperty(bool hasGetter, bool hasSetter) { - TypeBuilder typeBuild = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("TestAssembly"), AssemblyBuilderAccess.Run) + TypeBuilder typeBuild = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("TestAssembly"), AssemblyBuilderAccess.RunAndCollect) .DefineDynamicModule("TestModule") .DefineType("TestType", TypeAttributes.Public); FieldBuilder field = typeBuild.DefineField("_value", typeof(int), FieldAttributes.Private); @@ -272,6 +273,154 @@ namespace System.Dynamic.Tests Assert.Contains("set_ItemProp", ex.Message); } + private class ManyOverloads + { + public int GetValue() => 0; + + public int GetValue(int arg0) => 1; + + public int GetValue(int arg0, int arg1) => 2; + + public int GetValue(int arg0, int arg1, int arg2) => 3; + + public int GetValue(int arg0, int arg1, int arg2, int arg3) => 4; + + public int GetValue(int arg0, int arg1, int arg2, int arg3, int arg4) => 5; + + public int GetValue(int arg0, int arg1, int arg2, int arg3, int arg4, int arg5) => 6; + + public int GetValue(int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6) => 7; + + public int GetValue(int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7) => 8; + + public int GetValue( + int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8) => 9; + + public int GetValue( + int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9) => + 10; + + public int GetValue( + int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, + int arg10) => 11; + public int GetValue2() => 0; + + public int GetValue2(int arg0) => 1; + + public int GetValue2(int arg0, int arg1) => 2; + + public int GetValue2(int arg0, int arg1, int arg2) => 3; + + public int GetValue2(int arg0, int arg1, int arg2, int arg3) => 4; + + public int GetValue2(int arg0, int arg1, int arg2, int arg3, int arg4) => 5; + + public int GetValue2(int arg0, int arg1, int arg2, int arg3, int arg4, int arg5) => 6; + + public int GetValue2(int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6) => 7; + + public int GetValue2(int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7) => 8; + + public int GetValue2( + int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8) => 9; + + public int GetValue2( + int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9) => + 10; + + public int GetValue2( + int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, + int arg10) => 11; + } + + [Fact] + public void ManyArities() + { + dynamic d = new ManyOverloads(); + Assert.Equal(0, d.GetValue()); + Assert.Equal(0, d.GetValue2()); + Assert.Equal(1, d.GetValue(0)); + Assert.Equal(1, d.GetValue2(0)); + Assert.Equal(2, d.GetValue(0, 1)); + Assert.Equal(2, d.GetValue2(0, 1)); + Assert.Equal(3, d.GetValue(0, 1, 2)); + Assert.Equal(3, d.GetValue2(0, 1, 2)); + Assert.Equal(4, d.GetValue(0, 1, 2, 3)); + Assert.Equal(4, d.GetValue2(0, 1, 2, 3)); + Assert.Equal(5, d.GetValue(0, 1, 2, 3, 4)); + Assert.Equal(5, d.GetValue2(0, 1, 2, 3, 4)); + Assert.Equal(6, d.GetValue(0, 1, 2, 3, 4, 5)); + Assert.Equal(6, d.GetValue2(0, 1, 2, 3, 4, 5)); + Assert.Equal(7, d.GetValue(0, 1, 2, 3, 4, 5, 6)); + Assert.Equal(7, d.GetValue2(0, 1, 2, 3, 4, 5, 6)); + Assert.Equal(8, d.GetValue(0, 1, 2, 3, 4, 5, 6, 7)); + Assert.Equal(8, d.GetValue2(0, 1, 2, 3, 4, 5, 6, 7)); + Assert.Equal(9, d.GetValue(0, 1, 2, 3, 4, 5, 6, 7, 8)); + Assert.Equal(9, d.GetValue2(0, 1, 2, 3, 4, 5, 6, 7, 8)); + Assert.Equal(10, d.GetValue(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)); + Assert.Equal(10, d.GetValue2(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)); + Assert.Equal(11, d.GetValue(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); + Assert.Equal(11, d.GetValue2(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); + } + + public static IEnumerable SameNameObjectPairs() + { + object[] testObjects = Enumerable.Range(0, 4) + .Select( + _ => Activator.CreateInstance( + AssemblyBuilder + .DefineDynamicAssembly(new AssemblyName("TestAssembly"), AssemblyBuilderAccess.RunAndCollect) + .DefineDynamicModule("TestModule") + .DefineType("TestType", TypeAttributes.Public) + .CreateType())) + .ToArray(); + return testObjects.SelectMany(i => testObjects.Select(j => new[] { i, j })); + } + + [Theory, MemberData(nameof(SameNameObjectPairs))] + public void OperationOnTwoObjectsDifferentTypesOfSameName(object x, object y) + { + dynamic dX = x; + dynamic dY = y; + bool equal = dX.Equals(dY); + Assert.Equal(x == y, equal); + } + #endif + + public class FuncWrapper + { + public delegate void OutAction(out TResult arg); + private Func _delegate; + + public Func Delegate + { + get => _delegate; + set + { + _delegate = value; + OutDelegate = value == null ? default(OutAction) : (out TResult arg) => + { + arg = value(); + }; + } + } + + public OutAction OutDelegate; + } + + [Fact] + public void InvokeFuncMember() + { + dynamic d = new FuncWrapper + { + Delegate = () => 2 + }; + int result = d.Delegate(); + Assert.Equal(2, result); + result = 0; + d.OutDelegate(out result); + Assert.Equal(2, result); + } } } diff --git a/external/corefx/src/System.Linq.Expressions/tests/ExceptionHandling/ExceptionHandlingExpressions.cs b/external/corefx/src/System.Linq.Expressions/tests/ExceptionHandling/ExceptionHandlingExpressions.cs index 5eb25b9c3f..4fda75ff24 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/ExceptionHandling/ExceptionHandlingExpressions.cs +++ b/external/corefx/src/System.Linq.Expressions/tests/ExceptionHandling/ExceptionHandlingExpressions.cs @@ -253,7 +253,7 @@ namespace System.Linq.Expressions.Tests new[]{ typeof(RuntimeCompatibilityAttribute).GetProperty(nameof(RuntimeCompatibilityAttribute.WrapNonExceptionThrows)) }, new object[] { assemblyWraps }); AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly( - new AssemblyName("Name"), AssemblyBuilderAccess.Run); + new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect); assembly.SetCustomAttribute(custAtt); ModuleBuilder module = assembly.DefineDynamicModule("Name"); TypeBuilder type = module.DefineType("Type"); @@ -1200,6 +1200,97 @@ namespace System.Linq.Expressions.Tests Assert.Throws(() => tryExp.Compile(useInterpreter)); } + [Theory, ClassData(typeof(CompilationTypes))] + public void JumpOutOfTry(bool useInterpreter) + { + LabelTarget target = Expression.Label(); + Expression tryExp = Expression.Lambda( + Expression.Block( + Expression.TryFinally( + Expression.Block( + Expression.Goto(target), + Expression.Throw(Expression.Constant(new TestException()))), + Expression.Empty()), + Expression.Label(target))); + Action act = tryExp.Compile(useInterpreter); + act(); + } + + [Theory, ClassData(typeof(CompilationTypes))] + public void JumpOutOfTryToPreviousLabel(bool useInterpreter) + { + LabelTarget skipStart = Expression.Label(); + LabelTarget skipToEnd = Expression.Label(typeof(int)); + LabelTarget backToStart = Expression.Label(); + Expression> tryExp = Expression.Lambda>( + Expression.Block( + Expression.Goto(skipStart), Expression.Label(backToStart), + Expression.Return(skipToEnd, Expression.Constant(1)), Expression.Label(skipStart), + Expression.TryCatch( + Expression.Goto(backToStart), Expression.Catch(typeof(Exception), Expression.Empty())), + Expression.Return(skipToEnd, Expression.Constant(2)), + Expression.Label(skipToEnd, Expression.Constant(0)))); + Func func = tryExp.Compile(useInterpreter); + Assert.Equal(1, func()); + } + + [Theory, ClassData(typeof(CompilationTypes))] + public void JumpOutOfTryToPreviousLabelInOtherBlock(bool useInterpreter) + { + LabelTarget skipStart = Expression.Label(); + LabelTarget skipToEnd = Expression.Label(typeof(int)); + LabelTarget backToStart = Expression.Label(); + Expression> tryExp = Expression.Lambda>( + Expression.Block( + Expression.Goto(skipStart), + Expression.Block( + Expression.Label(backToStart), Expression.Return(skipToEnd, Expression.Constant(1))), + Expression.Block( + Expression.Label(skipStart), + Expression.TryCatch( + Expression.Goto(backToStart), Expression.Catch(typeof(Exception), Expression.Empty())), + Expression.Return(skipToEnd, Expression.Constant(2))), + Expression.Label(skipToEnd, Expression.Constant(0)))); + Func func = tryExp.Compile(useInterpreter); + Assert.Equal(1, func()); + } + + [Theory, ClassData(typeof(CompilationTypes))] + public void JumpOutOfCatch(bool useIntepreter) + { + LabelTarget target = Expression.Label(typeof(int)); + Expression> tryExp = Expression.Lambda>( + Expression.Block( + Expression.TryCatch( + Expression.Throw(Expression.Constant(new Exception())), + Expression.Catch( + typeof(Exception), + Expression.Block( + Expression.Goto(target, Expression.Constant(1)), + Expression.Throw(Expression.Constant(new Exception()))))), + Expression.Return(target, Expression.Constant(2)), + Expression.Label(target, Expression.Constant(0)))); + Assert.Equal(1, tryExp.Compile(useIntepreter)()); + } + + [Theory, ClassData(typeof(CompilationTypes))] + public void JumpOutOfCatchToPreviousLabel(bool useIntepreter) + { + LabelTarget skipStart = Expression.Label(); + LabelTarget skipToEnd = Expression.Label(typeof(int)); + LabelTarget backToStart = Expression.Label(); + Expression> tryExp = Expression.Lambda>( + Expression.Block( + Expression.Goto(skipStart), Expression.Label(backToStart), + Expression.Return(skipToEnd, Expression.Constant(1)), Expression.Label(skipStart), + Expression.TryCatch( + Expression.Throw(Expression.Constant(new Exception())), + Expression.Catch(typeof(Exception), Expression.Goto(backToStart))), + Expression.Return(skipToEnd, Expression.Constant(2)), + Expression.Label(skipToEnd, Expression.Constant(0)))); + Assert.Equal(1, tryExp.Compile(useIntepreter)()); + } + [Fact] public void NonAssignableTryAndCatchTypes() { diff --git a/external/corefx/src/System.Linq.Expressions/tests/Goto/Goto.cs b/external/corefx/src/System.Linq.Expressions/tests/Goto/Goto.cs index b1824bb28c..6cd52d93b2 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/Goto/Goto.cs +++ b/external/corefx/src/System.Linq.Expressions/tests/Goto/Goto.cs @@ -288,5 +288,27 @@ namespace System.Linq.Expressions.Tests ); Assert.Throws(() => exp.Compile(useInterpreter)); } + + [Theory, ClassData(typeof(CompilationTypes))] + public void AmbiguousJumpBack(bool useInterpreter) + { + LabelTarget label = Expression.Label(typeof(void)); + BlockExpression block = Expression.Block( + Expression.Block(Expression.Label(label)), Expression.Block(Expression.Label(label)), + Expression.Block(Expression.Block(Expression.Goto(label)))); + Expression exp = Expression.Lambda(block); + Assert.Throws(() => exp.Compile(useInterpreter)); + } + + [Theory, ClassData(typeof(CompilationTypes))] + public void AmbiguousJumpSplit(bool useInterpreter) + { + LabelTarget label = Expression.Label(typeof(void)); + BlockExpression block = Expression.Block( + Expression.Block(Expression.Label(label)), Expression.Block(Expression.Block(Expression.Goto(label))), + Expression.Block(Expression.Label(label))); + Expression exp = Expression.Lambda(block); + Assert.Throws(() => exp.Compile(useInterpreter)); + } } } diff --git a/external/corefx/src/System.Linq.Expressions/tests/Goto/Return.cs b/external/corefx/src/System.Linq.Expressions/tests/Goto/Return.cs index 314caa3ecc..2f03f6e0f7 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/Goto/Return.cs +++ b/external/corefx/src/System.Linq.Expressions/tests/Goto/Return.cs @@ -288,5 +288,51 @@ namespace System.Linq.Expressions.Tests ); Assert.Throws(() => exp.Compile(useInterpreter)); } + + public static void DoNothing() + { + } + + [Theory, ClassData(typeof(CompilationTypes))] + public void TailCallThenReturn(bool useInterpreter) + { + LabelTarget target = Expression.Label(); + Expression lambda = Expression.Lambda( + Expression.Block( + Expression.Call(GetType().GetMethod(nameof(DoNothing))), + Expression.Return(target), + Expression.Throw(Expression.Constant(new Exception())), + Expression.Label(target)), + true); + Action act = lambda.Compile(useInterpreter); + act(); + lambda.Verify(@" +.method void ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure) +{ + .maxstack 2 + + IL_0000: tail. + IL_0002: call void class [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.Return::DoNothing() + IL_0007: ret + IL_0008: ldarg.0 + IL_0009: ldfld class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure::Constants + IL_000e: ldc.i4.0 + IL_000f: ldelem.ref + IL_0010: castclass class [System.Private.CoreLib]System.Exception + IL_0015: throw + IL_0016: ret +}", @" +object lambda_method(object[]) +{ + .locals 0 + .maxstack 1 + .maxcontinuation 0 + + IP_0000: Call(Void DoNothing()) + IP_0001: Goto[0] -> 4 + IP_0002: LoadCached(0: System.Exception: Exception of type 'System.Exception' was thrown.) + IP_0003: Throw() +}"); + } } } diff --git a/external/corefx/src/System.Linq.Expressions/tests/HelperTypes.cs b/external/corefx/src/System.Linq.Expressions/tests/HelperTypes.cs index 07164afb0e..298e3a7be8 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/HelperTypes.cs +++ b/external/corefx/src/System.Linq.Expressions/tests/HelperTypes.cs @@ -363,7 +363,7 @@ namespace System.Linq.Expressions.Tests private static ModuleBuilder GetModuleBuilder() { AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly( - new AssemblyName("Name"), AssemblyBuilderAccess.Run); + new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect); return assembly.DefineDynamicModule("Name"); } diff --git a/external/corefx/src/System.Linq.Expressions/tests/IndexExpression/IndexExpressionTests.cs b/external/corefx/src/System.Linq.Expressions/tests/IndexExpression/IndexExpressionTests.cs index fc3e506dea..1a1df7f7e5 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/IndexExpression/IndexExpressionTests.cs +++ b/external/corefx/src/System.Linq.Expressions/tests/IndexExpression/IndexExpressionTests.cs @@ -95,7 +95,7 @@ namespace System.Linq.Expressions.Tests #if FEATURE_COMPILE private static TypeBuilder GetTestTypeBuilder() => - AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("TestAssembly"), AssemblyBuilderAccess.Run) + AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("TestAssembly"), AssemblyBuilderAccess.RunAndCollect) .DefineDynamicModule("TestModule") .DefineType("TestType"); diff --git a/external/corefx/src/System.Linq.Expressions/tests/Invoke/InvocationTests.cs b/external/corefx/src/System.Linq.Expressions/tests/Invoke/InvocationTests.cs index 5575d2b945..81b4ca57b8 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/Invoke/InvocationTests.cs +++ b/external/corefx/src/System.Linq.Expressions/tests/Invoke/InvocationTests.cs @@ -250,7 +250,7 @@ namespace System.Linq.Expressions.Tests [Theory, ClassData(typeof(CompilationTypes))] public static void InvokePrivateDelegate(bool useInterpreter) { - AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.Run); + AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect); ModuleBuilder module = assembly.DefineDynamicModule("Name"); TypeBuilder builder = module.DefineType("Type", TypeAttributes.Class | TypeAttributes.NotPublic | TypeAttributes.Sealed | TypeAttributes.AnsiClass | TypeAttributes.AutoClass, typeof(MulticastDelegate)); builder.DefineConstructor(MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(object), typeof(IntPtr) }).SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed); @@ -267,7 +267,7 @@ namespace System.Linq.Expressions.Tests [Theory, ClassData(typeof(CompilationTypes))] public static void InvokePrivateDelegateTypeLambda(bool useInterpreter) { - AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.Run); + AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect); ModuleBuilder module = assembly.DefineDynamicModule("Name"); TypeBuilder builder = module.DefineType("Type", TypeAttributes.Class | TypeAttributes.NotPublic | TypeAttributes.Sealed | TypeAttributes.AnsiClass | TypeAttributes.AutoClass, typeof(MulticastDelegate)); builder.DefineConstructor(MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(object), typeof(IntPtr) }).SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed); @@ -279,8 +279,27 @@ namespace System.Linq.Expressions.Tests var invFunc = invLambda.Compile(useInterpreter); Assert.Equal(42, invFunc()); } - #endif + private delegate void RefIntAction(ref int x); + + [Theory, ClassData(typeof(CompilationTypes))] + public static void InvokeByRefLambda(bool useInterpreter) + { + ParameterExpression refParam = Expression.Parameter(typeof(int).MakeByRefType()); + ParameterExpression param = Expression.Parameter(typeof(List)); + Func, List> func = Expression.Lambda, List>>( + Expression.Block( + Expression.Invoke( + Expression.Lambda( + Expression.AddAssign(refParam, Expression.Constant(2)), refParam), + Expression.MakeIndex( + param, typeof(List).GetProperty("Item"), new[] {Expression.Constant(0)})), param), + param) + .Compile(useInterpreter); + List list = new List { 9 }; + Assert.Equal(11, func(list)[0]); + Assert.Equal(11, list[0]); + } } } diff --git a/external/corefx/src/System.Linq.Expressions/tests/Lambda/LambdaTests.cs b/external/corefx/src/System.Linq.Expressions/tests/Lambda/LambdaTests.cs index 9fb317e638..e96cf6169c 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/Lambda/LambdaTests.cs +++ b/external/corefx/src/System.Linq.Expressions/tests/Lambda/LambdaTests.cs @@ -780,6 +780,140 @@ namespace System.Linq.Expressions.Tests Assert.False(parameters.Contains(Expression.Parameter(typeof(int)))); } + [Theory, ClassData(typeof(CompilationTypes))] + public void AboveByteMaxArityArg(bool useInterpreter) + { + ParameterExpression[] pars = Enumerable.Range(0, 300).Select(_ => Expression.Parameter(typeof(int))).ToArray(); + LambdaExpression lambda = Expression.Lambda(pars.Last(), pars); + Delegate del = lambda.Compile(useInterpreter); + object[] args = Enumerable.Repeat(0, 299).Append(23).ToArray(); + object result = del.DynamicInvoke(args); + Assert.Equal(23, result); + } + +#if FEATURE_COMPILE + [Theory, ClassData(typeof(CompilationTypes))] + public void AboveByteMaxArityArgIL(bool useInterpreter) + { + ParameterExpression[] pars = Enumerable.Range(0, 300) + .Select(_ => Expression.Parameter(typeof(int))) + .ToArray(); + LambdaExpression lambda = Expression.Lambda(pars.Last(), pars); + lambda.VerifyIL( + @".method int32 ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32) +{ + .maxstack 1 + + IL_0000: ldarg V_300 + IL_0004: ret +} +"); + } +#endif + + private struct Mutable + { + public bool Mutated; + + public Mutable Mutate() + { + Mutated = true; + return this; + } + } + + private delegate Mutable TricentaryIntAndMutableFunc( + int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, + int arg10, int arg11, int arg12, int arg13, int arg14, int arg15, int arg16, int arg17, int arg18, + int arg19, int arg20, int arg21, int arg22, int arg23, int arg24, int arg25, int arg26, int arg27, + int arg28, int arg29, int arg30, int arg31, int arg32, int arg33, int arg34, int arg35, int arg36, + int arg37, int arg38, int arg39, int arg40, int arg41, int arg42, int arg43, int arg44, int arg45, + int arg46, int arg47, int arg48, int arg49, int arg50, int arg51, int arg52, int arg53, int arg54, + int arg55, int arg56, int arg57, int arg58, int arg59, int arg60, int arg61, int arg62, int arg63, + int arg64, int arg65, int arg66, int arg67, int arg68, int arg69, int arg70, int arg71, int arg72, + int arg73, int arg74, int arg75, int arg76, int arg77, int arg78, int arg79, int arg80, int arg81, + int arg82, int arg83, int arg84, int arg85, int arg86, int arg87, int arg88, int arg89, int arg90, + int arg91, int arg92, int arg93, int arg94, int arg95, int arg96, int arg97, int arg98, int arg99, + int arg100, int arg101, int arg102, int arg103, int arg104, int arg105, int arg106, int arg107, int arg108, + int arg109, int arg110, int arg111, int arg112, int arg113, int arg114, int arg115, int arg116, int arg117, + int arg118, int arg119, int arg120, int arg121, int arg122, int arg123, int arg124, int arg125, int arg126, + int arg127, int arg128, int arg129, int arg130, int arg131, int arg132, int arg133, int arg134, int arg135, + int arg136, int arg137, int arg138, int arg139, int arg140, int arg141, int arg142, int arg143, int arg144, + int arg145, int arg146, int arg147, int arg148, int arg149, int arg150, int arg151, int arg152, int arg153, + int arg154, int arg155, int arg156, int arg157, int arg158, int arg159, int arg160, int arg161, int arg162, + int arg163, int arg164, int arg165, int arg166, int arg167, int arg168, int arg169, int arg170, int arg171, + int arg172, int arg173, int arg174, int arg175, int arg176, int arg177, int arg178, int arg179, int arg180, + int arg181, int arg182, int arg183, int arg184, int arg185, int arg186, int arg187, int arg188, int arg189, + int arg190, int arg191, int arg192, int arg193, int arg194, int arg195, int arg196, int arg197, int arg198, + int arg199, int arg200, int arg201, int arg202, int arg203, int arg204, int arg205, int arg206, int arg207, + int arg208, int arg209, int arg210, int arg211, int arg212, int arg213, int arg214, int arg215, int arg216, + int arg217, int arg218, int arg219, int arg220, int arg221, int arg222, int arg223, int arg224, int arg225, + int arg226, int arg227, int arg228, int arg229, int arg230, int arg231, int arg232, int arg233, int arg234, + int arg235, int arg236, int arg237, int arg238, int arg239, int arg240, int arg241, int arg242, int arg243, + int arg244, int arg245, int arg246, int arg247, int arg248, int arg249, int arg250, int arg251, int arg252, + int arg253, int arg254, int arg255, int arg256, int arg257, int arg258, int arg259, int arg260, int arg261, + int arg262, int arg263, int arg264, int arg265, int arg266, int arg267, int arg268, int arg269, int arg270, + int arg271, int arg272, int arg273, int arg274, int arg275, int arg276, int arg277, int arg278, int arg279, + int arg280, int arg281, int arg282, int arg283, int arg284, int arg285, int arg286, int arg287, int arg288, + int arg289, int arg290, int arg291, int arg292, int arg293, int arg294, int arg295, int arg296, int arg297, + int arg298, Mutable arg299); + + [Theory, ClassData(typeof(CompilationTypes))] + public void AboveByteMaxArityArgAddress(bool useInterpreter) + { + ParameterExpression parToMutate = Expression.Parameter(typeof(Mutable)); + ParameterExpression[] pars = Enumerable.Range(0, 299) + .Select(_ => Expression.Parameter(typeof(int))) + .Append(parToMutate) + .ToArray(); + Expression lambda = Expression.Lambda( + Expression.Call( + parToMutate, nameof(Mutable.Mutate), Type.EmptyTypes, Array.Empty()), pars); + TricentaryIntAndMutableFunc del = lambda.Compile(useInterpreter); + Mutable result = del( + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, default); + Assert.True(result.Mutated); + } + +#if FEATURE_COMPILE + [Fact] + public void AboveByteMaxArityArgAddressIL() + { + ParameterExpression parToMutate = Expression.Parameter(typeof(Mutable)); + ParameterExpression[] pars = Enumerable.Range(0, 299) + .Select(_ => Expression.Parameter(typeof(int))) + .Append(parToMutate) + .ToArray(); + Expression.Lambda( + Expression.Call( + parToMutate, nameof(Mutable.Mutate), Type.EmptyTypes, Array.Empty()), pars).VerifyIL(@".method valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.LambdaTests+Mutable ::lambda_method(class [System.Linq.Expressions]System.Runtime.CompilerServices.Closure,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.LambdaTests+Mutable) +{ + .maxstack 1 + + IL_0000: ldarga V_300 + IL_0004: call instance valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.LambdaTests+Mutable valuetype [System.Linq.Expressions.Tests]System.Linq.Expressions.Tests.LambdaTests+Mutable::Mutate() + IL_0009: ret +} +"); + } +#endif + + [Theory, ClassData(typeof(CompilationTypes))] + public void ExcessiveArity(bool useInterpreter) + { + ParameterExpression[] pars = Enumerable.Range(0, ushort.MaxValue).Select(_ => Expression.Parameter(typeof(int))).ToArray(); + LambdaExpression lambda = Expression.Lambda(pars.Last(), pars); + Assert.Throws(() => lambda.Compile(useInterpreter)); + } + private static int Add(ref int var, int val) { return var += val; @@ -799,7 +933,7 @@ namespace System.Linq.Expressions.Tests [Theory, ClassData(typeof(CompilationTypes))] public void PrivateDelegate(bool useInterpreter) { - AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.Run); + AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect); ModuleBuilder module = assembly.DefineDynamicModule("Name"); TypeBuilder builder = module.DefineType("Type", TypeAttributes.Class | TypeAttributes.NotPublic | TypeAttributes.Sealed | TypeAttributes.AnsiClass | TypeAttributes.AutoClass, typeof(MulticastDelegate)); builder.DefineConstructor(MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(object), typeof(IntPtr) }).SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed); diff --git a/external/corefx/src/System.Linq.Expressions/tests/Member/MemberAccessTests.cs b/external/corefx/src/System.Linq.Expressions/tests/Member/MemberAccessTests.cs index 50238cc03a..4ebfee0784 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/Member/MemberAccessTests.cs +++ b/external/corefx/src/System.Linq.Expressions/tests/Member/MemberAccessTests.cs @@ -556,7 +556,7 @@ namespace System.Linq.Expressions.Tests [Fact] public static void Property_NoGetOrSetAccessors_ThrowsArgumentException() { - AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.Run); + AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect); ModuleBuilder module = assembly.DefineDynamicModule("Module"); TypeBuilder type = module.DefineType("Type"); diff --git a/external/corefx/src/System.Linq.Expressions/tests/MemberInit/BindTests.cs b/external/corefx/src/System.Linq.Expressions/tests/MemberInit/BindTests.cs index 8f2f97b696..00267bcbc8 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/MemberInit/BindTests.cs +++ b/external/corefx/src/System.Linq.Expressions/tests/MemberInit/BindTests.cs @@ -303,7 +303,7 @@ namespace System.Linq.Expressions.Tests [Fact] public void GlobalMethod() { - ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.Run).DefineDynamicModule("Module"); + ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect).DefineDynamicModule("Module"); MethodBuilder globalMethod = module.DefineGlobalMethod("GlobalMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new [] {typeof(int)}); globalMethod.GetILGenerator().Emit(OpCodes.Ret); module.CreateGlobalFunctions(); @@ -314,7 +314,7 @@ namespace System.Linq.Expressions.Tests [Fact] public void GlobalField() { - ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.Run).DefineDynamicModule("Module"); + ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect).DefineDynamicModule("Module"); FieldBuilder fieldBuilder = module.DefineInitializedData("GlobalField", new byte[4], FieldAttributes.Public); module.CreateGlobalFunctions(); FieldInfo globalField = module.GetField(fieldBuilder.Name); diff --git a/external/corefx/src/System.Linq.Expressions/tests/MemberInit/ListBindTests.cs b/external/corefx/src/System.Linq.Expressions/tests/MemberInit/ListBindTests.cs index deb1d65703..b8a0926c74 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/MemberInit/ListBindTests.cs +++ b/external/corefx/src/System.Linq.Expressions/tests/MemberInit/ListBindTests.cs @@ -295,7 +295,7 @@ namespace System.Linq.Expressions.Tests [Fact] public void GlobalMethod() { - ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.Run).DefineDynamicModule("Module"); + ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect).DefineDynamicModule("Module"); MethodBuilder globalMethod = module.DefineGlobalMethod("GlobalMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(List), Type.EmptyTypes); globalMethod.GetILGenerator().Emit(OpCodes.Ret); module.CreateGlobalFunctions(); diff --git a/external/corefx/src/System.Linq.Expressions/tests/MemberInit/MemberBindTests.cs b/external/corefx/src/System.Linq.Expressions/tests/MemberInit/MemberBindTests.cs index ccc9b9ec5c..47512606c9 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/MemberInit/MemberBindTests.cs +++ b/external/corefx/src/System.Linq.Expressions/tests/MemberInit/MemberBindTests.cs @@ -258,7 +258,7 @@ namespace System.Linq.Expressions.Tests [Fact] public void GlobalMethod() { - ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.Run).DefineDynamicModule("Module"); + ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect).DefineDynamicModule("Module"); MethodBuilder globalMethod = module.DefineGlobalMethod("GlobalMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), Type.EmptyTypes); globalMethod.GetILGenerator().Emit(OpCodes.Ret); module.CreateGlobalFunctions(); diff --git a/external/corefx/src/System.Linq.Expressions/tests/New/NewTests.cs b/external/corefx/src/System.Linq.Expressions/tests/New/NewTests.cs index 8d4781c452..7e1c60bed3 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/New/NewTests.cs +++ b/external/corefx/src/System.Linq.Expressions/tests/New/NewTests.cs @@ -518,7 +518,7 @@ namespace System.Linq.Expressions.Tests [Fact] public static void GlobalMethodInMembers() { - ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.Run).DefineDynamicModule("Module"); + ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect).DefineDynamicModule("Module"); MethodBuilder globalMethod = module.DefineGlobalMethod("GlobalMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), Type.EmptyTypes); globalMethod.GetILGenerator().Emit(OpCodes.Ret); module.CreateGlobalFunctions(); @@ -532,7 +532,7 @@ namespace System.Linq.Expressions.Tests [Fact] public static void GlobalFieldInMembers() { - ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.Run).DefineDynamicModule("Module"); + ModuleBuilder module = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Name"), AssemblyBuilderAccess.RunAndCollect).DefineDynamicModule("Module"); FieldBuilder fieldBuilder = module.DefineInitializedData("GlobalField", new byte[1], FieldAttributes.Public); module.CreateGlobalFunctions(); FieldInfo globalField = module.GetField(fieldBuilder.Name); diff --git a/external/corefx/src/System.Linq.Expressions/tests/Switch/SwitchTests.cs b/external/corefx/src/System.Linq.Expressions/tests/Switch/SwitchTests.cs index 877c49cb14..826e677851 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/Switch/SwitchTests.cs +++ b/external/corefx/src/System.Linq.Expressions/tests/Switch/SwitchTests.cs @@ -249,6 +249,60 @@ namespace System.Linq.Expressions.Tests Assert.Equal("default", f(3)); } + [Theory] + [ClassData(typeof(CompilationTypes))] + public void SparseULongSwitch(bool useInterpreter) + { + ParameterExpression p = Expression.Parameter(typeof(ulong)); + ParameterExpression p1 = Expression.Parameter(typeof(string)); + SwitchExpression s = Expression.Switch(p, + Expression.Assign(p1, Expression.Constant("default")), + Expression.SwitchCase(Expression.Assign(p1, Expression.Constant("hello")), Expression.Constant(1UL)), + Expression.SwitchCase(Expression.Assign(p1, Expression.Constant("two")), Expression.Constant(2UL)), + Expression.SwitchCase(Expression.Assign(p1, Expression.Constant("three")), Expression.Constant(203212UL)), + Expression.SwitchCase(Expression.Assign(p1, Expression.Constant("four")), Expression.Constant(10212UL)), + Expression.SwitchCase(Expression.Assign(p1, Expression.Constant("five")), Expression.Constant(5021029121UL)), + Expression.SwitchCase(Expression.Assign(p1, Expression.Constant("six")), Expression.Constant(690219291UL)), + Expression.SwitchCase(Expression.Assign(p1, Expression.Constant("lala")), Expression.Constant(1UL)), + Expression.SwitchCase(Expression.Assign(p1, Expression.Constant("wow")), Expression.Constant(ulong.MaxValue))); + + BlockExpression block = Expression.Block(new[] { p1 }, s, p1); + + Func f = Expression.Lambda>(block, p).Compile(useInterpreter); + + Assert.Equal("hello", f(1)); + Assert.Equal("three", f(203212UL)); + Assert.Equal("two", f(2)); + Assert.Equal("default", f(3)); + } + + [Theory] + [ClassData(typeof(CompilationTypes))] + public void SparseLongSwitch(bool useInterpreter) + { + ParameterExpression p = Expression.Parameter(typeof(long)); + ParameterExpression p1 = Expression.Parameter(typeof(string)); + SwitchExpression s = Expression.Switch(p, + Expression.Assign(p1, Expression.Constant("default")), + Expression.SwitchCase(Expression.Assign(p1, Expression.Constant("hello")), Expression.Constant(1L)), + Expression.SwitchCase(Expression.Assign(p1, Expression.Constant("two")), Expression.Constant(2L)), + Expression.SwitchCase(Expression.Assign(p1, Expression.Constant("three")), Expression.Constant(203212L)), + Expression.SwitchCase(Expression.Assign(p1, Expression.Constant("four")), Expression.Constant(10212L)), + Expression.SwitchCase(Expression.Assign(p1, Expression.Constant("five")), Expression.Constant(5021029121L)), + Expression.SwitchCase(Expression.Assign(p1, Expression.Constant("six")), Expression.Constant(690219291L)), + Expression.SwitchCase(Expression.Assign(p1, Expression.Constant("lala")), Expression.Constant(1L)), + Expression.SwitchCase(Expression.Assign(p1, Expression.Constant("wow")), Expression.Constant(long.MaxValue))); + + BlockExpression block = Expression.Block(new[] { p1 }, s, p1); + + Func f = Expression.Lambda>(block, p).Compile(useInterpreter); + + Assert.Equal("hello", f(1)); + Assert.Equal("three", f(203212L)); + Assert.Equal("two", f(2)); + Assert.Equal("default", f(3)); + } + [Theory] [ClassData(typeof(CompilationTypes))] public void StringSwitch(bool useInterpreter) @@ -267,6 +321,43 @@ namespace System.Linq.Expressions.Tests Assert.Equal("default", f(null)); } + [Theory] + [ClassData(typeof(CompilationTypes))] + public void StringSwitchTailCall(bool useInterpreter) + { + ParameterExpression p = Expression.Parameter(typeof(string)); + SwitchExpression s = Expression.Switch(p, + Expression.Constant("default"), + Expression.SwitchCase(Expression.Constant("hello"), Expression.Constant("hi")), + Expression.SwitchCase(Expression.Constant("lala"), Expression.Constant("bye"))); + + Func f = Expression.Lambda>(s, true, p).Compile(useInterpreter); + + Assert.Equal("hello", f("hi")); + Assert.Equal("lala", f("bye")); + Assert.Equal("default", f("hi2")); + Assert.Equal("default", f(null)); + } + + [Theory] + [ClassData(typeof(CompilationTypes))] + public void StringSwitchTailCallButNotLast(bool useInterpreter) + { + ParameterExpression p = Expression.Parameter(typeof(string)); + SwitchExpression s = Expression.Switch(p, + Expression.Constant("default"), + Expression.SwitchCase(Expression.Constant("hello"), Expression.Constant("hi")), + Expression.SwitchCase(Expression.Constant("lala"), Expression.Constant("bye"))); + BlockExpression block = Expression.Block(s, Expression.Constant("Not from the switch")); + + Func f = Expression.Lambda>(block, true, p).Compile(useInterpreter); + + Assert.Equal("Not from the switch", f("hi")); + Assert.Equal("Not from the switch", f("bye")); + Assert.Equal("Not from the switch", f("hi2")); + Assert.Equal("Not from the switch", f(null)); + } + [Theory] [ClassData(typeof(CompilationTypes))] public void StringSwitch1(bool useInterpreter) @@ -873,5 +964,39 @@ namespace System.Linq.Expressions.Tests SwitchCase e3 = Expression.SwitchCase(Expression.Parameter(typeof(int), "x"), Expression.Constant(1), Expression.Constant(2)); Assert.Equal("case (1, 2): ...", e3.ToString()); } + + private delegate void TwoOutAction(int input, ref int x, ref int y); + + [Theory, ClassData(typeof(CompilationTypes))] + public void JumpBetweenCases(bool useIntepreter) + { + LabelTarget label = Expression.Label(); + ParameterExpression xParam = Expression.Parameter(typeof(int).MakeByRefType()); + ParameterExpression yParam = Expression.Parameter(typeof(int).MakeByRefType()); + ParameterExpression inpParam = Expression.Parameter(typeof(int)); + Expression lambda = Expression.Lambda( + Expression.Switch( + inpParam, + Expression.Empty(), + Expression.SwitchCase( + Expression.Block(Expression.Assign(xParam, Expression.Constant(1)), Expression.Goto(label), Expression.Empty()), + Expression.Constant(0)), + Expression.SwitchCase( + Expression.Block(Expression.Label(label), Expression.Assign(yParam, Expression.Constant(2)), Expression.Empty()), + Expression.Constant(1))), inpParam, xParam, yParam); + TwoOutAction act = lambda.Compile(useIntepreter); + int x = 0; + int y = 0; + act(2, ref x, ref y); + Assert.Equal(0, x); + Assert.Equal(0, y); + act(1, ref x, ref y); + Assert.Equal(0, x); + Assert.Equal(2, y); + y = 0; + act(0, ref x, ref y); + Assert.Equal(1, x); + Assert.Equal(2, y); + } } } diff --git a/external/corefx/src/System.Linq.Expressions/tests/System.Linq.Expressions.Tests.csproj b/external/corefx/src/System.Linq.Expressions/tests/System.Linq.Expressions.Tests.csproj index eab29a1042..3eb4ab20e1 100644 --- a/external/corefx/src/System.Linq.Expressions/tests/System.Linq.Expressions.Tests.csproj +++ b/external/corefx/src/System.Linq.Expressions/tests/System.Linq.Expressions.Tests.csproj @@ -10,11 +10,12 @@ true $(DefineConstants);FEATURE_COMPILE $(DefineConstants);FEATURE_INTERPRET - false + false - - - + + + + @@ -23,7 +24,7 @@ - + @@ -49,7 +50,7 @@ - + @@ -283,7 +284,7 @@ - + @@ -293,4 +294,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Linq.Parallel/src/System.Linq.Parallel.csproj b/external/corefx/src/System.Linq.Parallel/src/System.Linq.Parallel.csproj index 07592c7ca8..d7d42e4562 100644 --- a/external/corefx/src/System.Linq.Parallel/src/System.Linq.Parallel.csproj +++ b/external/corefx/src/System.Linq.Parallel/src/System.Linq.Parallel.csproj @@ -6,7 +6,6 @@ System.Linq.Parallel true - @@ -154,7 +153,6 @@ - @@ -166,4 +164,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.Linq.Parallel/src/System/Linq/Parallel/Merging/OrderPreservingPipeliningMergeHelper.cs b/external/corefx/src/System.Linq.Parallel/src/System/Linq/Parallel/Merging/OrderPreservingPipeliningMergeHelper.cs index 8b97e1502d..edd706843b 100644 --- a/external/corefx/src/System.Linq.Parallel/src/System/Linq/Parallel/Merging/OrderPreservingPipeliningMergeHelper.cs +++ b/external/corefx/src/System.Linq.Parallel/src/System/Linq/Parallel/Merging/OrderPreservingPipeliningMergeHelper.cs @@ -491,7 +491,7 @@ namespace System.Linq.Parallel /// /// A structure to represent a producer in the producer heap. /// - internal struct Producer + internal readonly struct Producer { internal readonly TKey MaxKey; // Order index of the next element from this producer internal readonly int ProducerIndex; // Index of the producer, [0..DOP) diff --git a/external/corefx/src/System.Linq.Parallel/tests/System.Linq.Parallel.Tests.csproj b/external/corefx/src/System.Linq.Parallel/tests/System.Linq.Parallel.Tests.csproj index 92f719aaa6..17c494138a 100644 --- a/external/corefx/src/System.Linq.Parallel/tests/System.Linq.Parallel.Tests.csproj +++ b/external/corefx/src/System.Linq.Parallel/tests/System.Linq.Parallel.Tests.csproj @@ -4,7 +4,6 @@ {A7074928-82C3-4739-88FE-9B528977950C} - diff --git a/external/corefx/src/System.Linq.Queryable/src/System.Linq.Queryable.csproj b/external/corefx/src/System.Linq.Queryable/src/System.Linq.Queryable.csproj index 4baf062732..22544fab07 100644 --- a/external/corefx/src/System.Linq.Queryable/src/System.Linq.Queryable.csproj +++ b/external/corefx/src/System.Linq.Queryable/src/System.Linq.Queryable.csproj @@ -6,7 +6,6 @@ System.Linq.Queryable System.Linq.Queryable - diff --git a/external/corefx/src/System.Linq.Queryable/tests/AppendPrependTests.cs b/external/corefx/src/System.Linq.Queryable/tests/AppendPrependTests.cs index 45d1c8befb..f4094187e0 100644 --- a/external/corefx/src/System.Linq.Queryable/tests/AppendPrependTests.cs +++ b/external/corefx/src/System.Linq.Queryable/tests/AppendPrependTests.cs @@ -1,3 +1,7 @@ +// 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.Collections.Generic; using Xunit; diff --git a/external/corefx/src/System.Linq.Queryable/tests/System.Linq.Queryable.Tests.csproj b/external/corefx/src/System.Linq.Queryable/tests/System.Linq.Queryable.Tests.csproj index ac7af6fb2e..213fdd385d 100644 --- a/external/corefx/src/System.Linq.Queryable/tests/System.Linq.Queryable.Tests.csproj +++ b/external/corefx/src/System.Linq.Queryable/tests/System.Linq.Queryable.Tests.csproj @@ -4,7 +4,6 @@ {7B88D79B-B799-4116-A7D0-AED572540CD4} - @@ -70,4 +69,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Linq/System.Linq.sln b/external/corefx/src/System.Linq/System.Linq.sln index 5fd623d4f6..25a7e2ce8f 100644 --- a/external/corefx/src/System.Linq/System.Linq.sln +++ b/external/corefx/src/System.Linq/System.Linq.sln @@ -35,10 +35,10 @@ Global {7C70BB15-870B-4946-8098-625DACD645A6}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {7C70BB15-870B-4946-8098-625DACD645A6}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU {7C70BB15-870B-4946-8098-625DACD645A6}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU - {28FB26C9-E425-4E50-9D1D-08F34560E86E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {28FB26C9-E425-4E50-9D1D-08F34560E86E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {28FB26C9-E425-4E50-9D1D-08F34560E86E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {28FB26C9-E425-4E50-9D1D-08F34560E86E}.Release|Any CPU.Build.0 = Release|Any CPU + {28FB26C9-E425-4E50-9D1D-08F34560E86E}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {28FB26C9-E425-4E50-9D1D-08F34560E86E}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {28FB26C9-E425-4E50-9D1D-08F34560E86E}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {28FB26C9-E425-4E50-9D1D-08F34560E86E}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU {CA488507-3B6E-4494-B7BE-7B4EEEB2C4D1}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU {CA488507-3B6E-4494-B7BE-7B4EEEB2C4D1}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {CA488507-3B6E-4494-B7BE-7B4EEEB2C4D1}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU diff --git a/external/corefx/src/System.Linq/src/System.Linq.csproj b/external/corefx/src/System.Linq/src/System.Linq.csproj index 28dbe5626a..58f41afc59 100644 --- a/external/corefx/src/System.Linq/src/System.Linq.csproj +++ b/external/corefx/src/System.Linq/src/System.Linq.csproj @@ -6,7 +6,6 @@ System.Linq System.Linq - @@ -91,4 +90,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Linq/src/System/Linq/Buffer.cs b/external/corefx/src/System.Linq/src/System/Linq/Buffer.cs index 88aa3a3650..c3cc5bc216 100644 --- a/external/corefx/src/System.Linq/src/System/Linq/Buffer.cs +++ b/external/corefx/src/System.Linq/src/System/Linq/Buffer.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. @@ -10,7 +10,7 @@ namespace System.Linq /// A buffer into which the contents of an can be stored. /// /// The type of the buffer's elements. - internal struct Buffer + internal readonly struct Buffer { /// /// The stored items. diff --git a/external/corefx/src/System.Linq/src/System/Linq/Contains.cs b/external/corefx/src/System.Linq/src/System/Linq/Contains.cs index b356960c14..eb0e1ce57c 100644 --- a/external/corefx/src/System.Linq/src/System/Linq/Contains.cs +++ b/external/corefx/src/System.Linq/src/System/Linq/Contains.cs @@ -9,27 +9,34 @@ namespace System.Linq public static partial class Enumerable { public static bool Contains(this IEnumerable source, TSource value) => - source is ICollection collection - ? collection.Contains(value) - : Contains(source, value, null); + source is ICollection collection ? collection.Contains(value) : + Contains(source, value, null); public static bool Contains(this IEnumerable source, TSource value, IEqualityComparer comparer) { - if (comparer == null) - { - comparer = EqualityComparer.Default; - } - if (source == null) { throw Error.ArgumentNull(nameof(source)); } - foreach (TSource element in source) + if (comparer == null) { - if (comparer.Equals(element, value)) + foreach (TSource element in source) { - return true; + if (EqualityComparer.Default.Equals(element, value)) // benefits from devirtualization and likely inlining + { + return true; + } + } + } + else + { + foreach (TSource element in source) + { + if (comparer.Equals(element, value)) + { + return true; + } } } diff --git a/external/corefx/src/System.Linq/tests/Performance/Perf.Linq.cs b/external/corefx/src/System.Linq/tests/Performance/Perf.Linq.cs index 55bbdf8da5..2361b8b3ed 100644 --- a/external/corefx/src/System.Linq/tests/Performance/Perf.Linq.cs +++ b/external/corefx/src/System.Linq/tests/Performance/Perf.Linq.cs @@ -195,6 +195,42 @@ namespace System.Linq.Tests Perf_LinqTestBase.MeasureMaterializationToDictionary(Perf_LinqTestBase.Wrap(array, wrapType), iteration); } + [Benchmark] + [MemberData(nameof(IterationSizeWrapperData))] + public void Contains_ElementNotFound(int size, int iterationCount, Perf_LinqTestBase.WrapperType wrapType) + { + IEnumerable source = Perf_LinqTestBase.Wrap(Enumerable.Range(0, size).ToArray(), wrapType); + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < iterationCount; i++) + { + source.Contains(size + 1); + } + } + } + } + + [Benchmark] + [MemberData(nameof(IterationSizeWrapperData))] + public void Contains_FirstElementMatches(int size, int iterationCount, Perf_LinqTestBase.WrapperType wrapType) + { + IEnumerable source = Perf_LinqTestBase.Wrap(Enumerable.Range(0, size).ToArray(), wrapType); + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < iterationCount; i++) + { + source.Contains(0); + } + } + } + } + #endregion } } diff --git a/external/corefx/src/System.Linq/tests/Performance/System.Linq.Performance.Tests.csproj b/external/corefx/src/System.Linq/tests/Performance/System.Linq.Performance.Tests.csproj index 4a7545317b..7967897127 100644 --- a/external/corefx/src/System.Linq/tests/Performance/System.Linq.Performance.Tests.csproj +++ b/external/corefx/src/System.Linq/tests/Performance/System.Linq.Performance.Tests.csproj @@ -5,9 +5,8 @@ true {28FB26C9-E425-4E50-9D1D-08F34560E86E} - - - + + diff --git a/external/corefx/src/System.Linq/tests/SkipWhileTests.cs b/external/corefx/src/System.Linq/tests/SkipWhileTests.cs index 9b1d5bc231..1264196e10 100644 --- a/external/corefx/src/System.Linq/tests/SkipWhileTests.cs +++ b/external/corefx/src/System.Linq/tests/SkipWhileTests.cs @@ -94,11 +94,11 @@ namespace System.Linq.Tests int[] source = { 8, 3, 12, 4, 6, 10 }; int[] expected = { 3, 12, 4, 6, 10 }; - Assert.Equal(expected, source.SkipWhile((e, i) => e % 2 == 0)); + Assert.Equal(expected, source.SkipWhile(e => e % 2 == 0)); } [Fact] - public void PredicateManyFalseOnSecondInex() + public void PredicateManyFalseOnSecondIndex() { int[] source = { 8, 3, 12, 4, 6, 10 }; int[] expected = { 3, 12, 4, 6, 10 }; @@ -112,7 +112,7 @@ namespace System.Linq.Tests int[] source = { 3, 2, 4, 12, 6 }; int[] expected = { 3, 2, 4, 12, 6 }; - Assert.Equal(expected, source.SkipWhile((e, i) => e % 2 == 0)); + Assert.Equal(expected, source.SkipWhile(e => e % 2 == 0)); } [Fact] diff --git a/external/corefx/src/System.Linq/tests/System.Linq.Tests.csproj b/external/corefx/src/System.Linq/tests/System.Linq.Tests.csproj index ddfef7245e..e1e281ced9 100644 --- a/external/corefx/src/System.Linq/tests/System.Linq.Tests.csproj +++ b/external/corefx/src/System.Linq/tests/System.Linq.Tests.csproj @@ -4,7 +4,6 @@ {7C70BB15-870B-4946-8098-625DACD645A6} - @@ -81,7 +80,6 @@ Common\System\Linq\SkipTakeData.cs - @@ -92,9 +90,8 @@ - - + \ No newline at end of file diff --git a/external/corefx/src/System.Management/System.Management.sln b/external/corefx/src/System.Management/System.Management.sln new file mode 100644 index 0000000000..4bb0d75a12 --- /dev/null +++ b/external/corefx/src/System.Management/System.Management.sln @@ -0,0 +1,50 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Management.Tests", "tests\System.Management.Tests.csproj", "{5456707C-489E-4562-846E-B9598569F6BE}" + ProjectSection(ProjectDependencies) = postProject + {950923D8-EC99-4F90-9BA6-8EB64582C555} = {950923D8-EC99-4F90-9BA6-8EB64582C555} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Management", "src\System.Management.csproj", "{950923D8-EC99-4F90-9BA6-8EB64582C555}" + ProjectSection(ProjectDependencies) = postProject + {CA17270B-079F-4D52-97E8-C0C2E8B9D7DB} = {CA17270B-079F-4D52-97E8-C0C2E8B9D7DB} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Management", "ref\System.Management.csproj", "{CA17270B-079F-4D52-97E8-C0C2E8B9D7DB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{1A2F9F4A-A032-433E-B914-ADD5992BB178}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E107E9C1-E893-4E87-987E-04EF0DCEAEFD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{2E666815-2EDB-464B-9DF6-380BF4789AD4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5456707C-489E-4562-846E-B9598569F6BE}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {5456707C-489E-4562-846E-B9598569F6BE}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {5456707C-489E-4562-846E-B9598569F6BE}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {5456707C-489E-4562-846E-B9598569F6BE}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU + {950923D8-EC99-4F90-9BA6-8EB64582C555}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {950923D8-EC99-4F90-9BA6-8EB64582C555}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {950923D8-EC99-4F90-9BA6-8EB64582C555}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {950923D8-EC99-4F90-9BA6-8EB64582C555}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU + {CA17270B-079F-4D52-97E8-C0C2E8B9D7DB}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {CA17270B-079F-4D52-97E8-C0C2E8B9D7DB}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {CA17270B-079F-4D52-97E8-C0C2E8B9D7DB}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {CA17270B-079F-4D52-97E8-C0C2E8B9D7DB}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {5456707C-489E-4562-846E-B9598569F6BE} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} + {950923D8-EC99-4F90-9BA6-8EB64582C555} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD} + {CA17270B-079F-4D52-97E8-C0C2E8B9D7DB} = {2E666815-2EDB-464B-9DF6-380BF4789AD4} + EndGlobalSection +EndGlobal diff --git a/external/corefx/src/System.Management/dir.props b/external/corefx/src/System.Management/dir.props new file mode 100644 index 0000000000..3a2542eb5f --- /dev/null +++ b/external/corefx/src/System.Management/dir.props @@ -0,0 +1,11 @@ + + + + + + 4.0.0.0 + MSFT + + \ No newline at end of file diff --git a/external/corefx/src/System.Management/pkg/System.Management.pkgproj b/external/corefx/src/System.Management/pkg/System.Management.pkgproj new file mode 100644 index 0000000000..1079d8066e --- /dev/null +++ b/external/corefx/src/System.Management/pkg/System.Management.pkgproj @@ -0,0 +1,17 @@ + + + + + + netcoreapp2.0;net45;$(AllXamarinFrameworks) + + + + true + + + runtimes/win/lib/net45 + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Management/ref/Configurations.props b/external/corefx/src/System.Management/ref/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Management/ref/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Management/ref/System.Management.cs b/external/corefx/src/System.Management/ref/System.Management.cs new file mode 100644 index 0000000000..544ced27a5 --- /dev/null +++ b/external/corefx/src/System.Management/ref/System.Management.cs @@ -0,0 +1,767 @@ +// 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. +// ------------------------------------------------------------------------------ +// Changes to this file must follow the http://aka.ms/api-review process. +// ------------------------------------------------------------------------------ + +namespace System.Management +{ + public enum AuthenticationLevel + { + Call = 3, + Connect = 2, + Default = 0, + None = 1, + Packet = 4, + PacketIntegrity = 5, + PacketPrivacy = 6, + Unchanged = -1, + } + public enum CimType + { + Boolean = 11, + Char16 = 103, + DateTime = 101, + None = 0, + Object = 13, + Real32 = 4, + Real64 = 5, + Reference = 102, + SInt16 = 2, + SInt32 = 3, + SInt64 = 20, + SInt8 = 16, + String = 8, + UInt16 = 18, + UInt32 = 19, + UInt64 = 21, + UInt8 = 17, + } + public enum CodeLanguage + { + CSharp = 0, + JScript = 1, + Mcpp = 4, + VB = 2, + VJSharp = 3, + } + [System.FlagsAttribute] + public enum ComparisonSettings + { + IgnoreCase = 16, + IgnoreClass = 8, + IgnoreDefaultValues = 4, + IgnoreFlavor = 32, + IgnoreObjectSource = 2, + IgnoreQualifiers = 1, + IncludeAll = 0, + } + public partial class CompletedEventArgs : System.Management.ManagementEventArgs + { + internal CompletedEventArgs() { } + public System.Management.ManagementStatus Status { get { throw null; } } + public System.Management.ManagementBaseObject StatusObject { get { throw null; } } + } + public delegate void CompletedEventHandler(object sender, System.Management.CompletedEventArgs e); + public partial class ConnectionOptions : System.Management.ManagementOptions + { + public ConnectionOptions() { } + public ConnectionOptions(string locale, string username, System.Security.SecureString password, string authority, System.Management.ImpersonationLevel impersonation, System.Management.AuthenticationLevel authentication, bool enablePrivileges, System.Management.ManagementNamedValueCollection context, System.TimeSpan timeout) { } + public ConnectionOptions(string locale, string username, string password, string authority, System.Management.ImpersonationLevel impersonation, System.Management.AuthenticationLevel authentication, bool enablePrivileges, System.Management.ManagementNamedValueCollection context, System.TimeSpan timeout) { } + public System.Management.AuthenticationLevel Authentication { get { throw null; } set { } } + public string Authority { get { throw null; } set { } } + public bool EnablePrivileges { get { throw null; } set { } } + public System.Management.ImpersonationLevel Impersonation { get { throw null; } set { } } + public string Locale { get { throw null; } set { } } + public string Password { set { } } + public System.Security.SecureString SecurePassword { set { } } + public string Username { get { throw null; } set { } } + public override object Clone() { throw null; } + } + public partial class DeleteOptions : System.Management.ManagementOptions + { + public DeleteOptions() { } + public DeleteOptions(System.Management.ManagementNamedValueCollection context, System.TimeSpan timeout) { } + public override object Clone() { throw null; } + } + public partial class EnumerationOptions : System.Management.ManagementOptions + { + public EnumerationOptions() { } + public EnumerationOptions(System.Management.ManagementNamedValueCollection context, System.TimeSpan timeout, int blockSize, bool rewindable, bool returnImmediatley, bool useAmendedQualifiers, bool ensureLocatable, bool prototypeOnly, bool directRead, bool enumerateDeep) { } + public int BlockSize { get { throw null; } set { } } + public bool DirectRead { get { throw null; } set { } } + public bool EnsureLocatable { get { throw null; } set { } } + public bool EnumerateDeep { get { throw null; } set { } } + public bool PrototypeOnly { get { throw null; } set { } } + public bool ReturnImmediately { get { throw null; } set { } } + public bool Rewindable { get { throw null; } set { } } + public bool UseAmendedQualifiers { get { throw null; } set { } } + public override object Clone() { throw null; } + } + public partial class EventArrivedEventArgs : System.Management.ManagementEventArgs + { + internal EventArrivedEventArgs() { } + public System.Management.ManagementBaseObject NewEvent { get { throw null; } } + } + public delegate void EventArrivedEventHandler(object sender, System.Management.EventArrivedEventArgs e); + public partial class EventQuery : System.Management.ManagementQuery + { + public EventQuery() { } + public EventQuery(string query) { } + public EventQuery(string language, string query) { } + public override object Clone() { throw null; } + } + public partial class EventWatcherOptions : System.Management.ManagementOptions + { + public EventWatcherOptions() { } + public EventWatcherOptions(System.Management.ManagementNamedValueCollection context, System.TimeSpan timeout, int blockSize) { } + public int BlockSize { get { throw null; } set { } } + public override object Clone() { throw null; } + } + public enum ImpersonationLevel + { + Anonymous = 1, + Default = 0, + Delegate = 4, + Identify = 2, + Impersonate = 3, + } + public partial class InvokeMethodOptions : System.Management.ManagementOptions + { + public InvokeMethodOptions() { } + public InvokeMethodOptions(System.Management.ManagementNamedValueCollection context, System.TimeSpan timeout) { } + public override object Clone() { throw null; } + } + [System.ComponentModel.ToolboxItemAttribute(false)] + public partial class ManagementBaseObject : System.ComponentModel.Component, System.ICloneable, System.Runtime.Serialization.ISerializable + { + protected ManagementBaseObject(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + public virtual System.Management.ManagementPath ClassPath { get { throw null; } } + public object this[string propertyName] { get { throw null; } set { } } + public virtual System.Management.PropertyDataCollection Properties { get { throw null; } } + public virtual System.Management.QualifierDataCollection Qualifiers { get { throw null; } } + public virtual System.Management.PropertyDataCollection SystemProperties { get { throw null; } } + public virtual object Clone() { throw null; } + public bool CompareTo(System.Management.ManagementBaseObject otherObject, System.Management.ComparisonSettings settings) { throw null; } + public new void Dispose() { } + public override bool Equals(object obj) { throw null; } + public override int GetHashCode() { throw null; } + protected virtual void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + public object GetPropertyQualifierValue(string propertyName, string qualifierName) { throw null; } + public object GetPropertyValue(string propertyName) { throw null; } + public object GetQualifierValue(string qualifierName) { throw null; } + public string GetText(System.Management.TextFormat format) { throw null; } + public static explicit operator System.IntPtr (System.Management.ManagementBaseObject managementObject) { throw null; } + public void SetPropertyQualifierValue(string propertyName, string qualifierName, object qualifierValue) { } + public void SetPropertyValue(string propertyName, object propertyValue) { } + public void SetQualifierValue(string qualifierName, object qualifierValue) { } + void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + } + public partial class ManagementClass : System.Management.ManagementObject + { + public ManagementClass() { } + public ManagementClass(System.Management.ManagementPath path) { } + public ManagementClass(System.Management.ManagementPath path, System.Management.ObjectGetOptions options) { } + public ManagementClass(System.Management.ManagementScope scope, System.Management.ManagementPath path, System.Management.ObjectGetOptions options) { } + protected ManagementClass(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + public ManagementClass(string path) { } + public ManagementClass(string path, System.Management.ObjectGetOptions options) { } + public ManagementClass(string scope, string path, System.Management.ObjectGetOptions options) { } + public System.Collections.Specialized.StringCollection Derivation { get { throw null; } } + public System.Management.MethodDataCollection Methods { get { throw null; } } + public override System.Management.ManagementPath Path { get { throw null; } set { } } + public override object Clone() { throw null; } + public System.Management.ManagementObject CreateInstance() { throw null; } + public System.Management.ManagementClass Derive(string newClassName) { throw null; } + public System.Management.ManagementObjectCollection GetInstances() { throw null; } + public System.Management.ManagementObjectCollection GetInstances(System.Management.EnumerationOptions options) { throw null; } + public void GetInstances(System.Management.ManagementOperationObserver watcher) { } + public void GetInstances(System.Management.ManagementOperationObserver watcher, System.Management.EnumerationOptions options) { } + protected override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + public System.Management.ManagementObjectCollection GetRelatedClasses() { throw null; } + public void GetRelatedClasses(System.Management.ManagementOperationObserver watcher) { } + public void GetRelatedClasses(System.Management.ManagementOperationObserver watcher, string relatedClass) { } + public void GetRelatedClasses(System.Management.ManagementOperationObserver watcher, string relatedClass, string relationshipClass, string relationshipQualifier, string relatedQualifier, string relatedRole, string thisRole, System.Management.EnumerationOptions options) { } + public System.Management.ManagementObjectCollection GetRelatedClasses(string relatedClass) { throw null; } + public System.Management.ManagementObjectCollection GetRelatedClasses(string relatedClass, string relationshipClass, string relationshipQualifier, string relatedQualifier, string relatedRole, string thisRole, System.Management.EnumerationOptions options) { throw null; } + public System.Management.ManagementObjectCollection GetRelationshipClasses() { throw null; } + public void GetRelationshipClasses(System.Management.ManagementOperationObserver watcher) { } + public void GetRelationshipClasses(System.Management.ManagementOperationObserver watcher, string relationshipClass) { } + public void GetRelationshipClasses(System.Management.ManagementOperationObserver watcher, string relationshipClass, string relationshipQualifier, string thisRole, System.Management.EnumerationOptions options) { } + public System.Management.ManagementObjectCollection GetRelationshipClasses(string relationshipClass) { throw null; } + public System.Management.ManagementObjectCollection GetRelationshipClasses(string relationshipClass, string relationshipQualifier, string thisRole, System.Management.EnumerationOptions options) { throw null; } + public System.CodeDom.CodeTypeDeclaration GetStronglyTypedClassCode(bool includeSystemClassInClassDef, bool systemPropertyClass) { throw null; } + public bool GetStronglyTypedClassCode(System.Management.CodeLanguage lang, string filePath, string classNamespace) { throw null; } + public System.Management.ManagementObjectCollection GetSubclasses() { throw null; } + public System.Management.ManagementObjectCollection GetSubclasses(System.Management.EnumerationOptions options) { throw null; } + public void GetSubclasses(System.Management.ManagementOperationObserver watcher) { } + public void GetSubclasses(System.Management.ManagementOperationObserver watcher, System.Management.EnumerationOptions options) { } + } + public sealed partial class ManagementDateTimeConverter + { + internal ManagementDateTimeConverter() { } + public static System.DateTime ToDateTime(string dmtfDate) { throw null; } + public static string ToDmtfDateTime(System.DateTime date) { throw null; } + public static string ToDmtfTimeInterval(System.TimeSpan timespan) { throw null; } + public static System.TimeSpan ToTimeSpan(string dmtfTimespan) { throw null; } + } + public abstract partial class ManagementEventArgs : System.EventArgs + { + internal ManagementEventArgs() { } + public object Context { get { throw null; } } + } + [System.ComponentModel.ToolboxItemAttribute(false)] + public partial class ManagementEventWatcher : System.ComponentModel.Component + { + public ManagementEventWatcher() { } + public ManagementEventWatcher(System.Management.EventQuery query) { } + public ManagementEventWatcher(System.Management.ManagementScope scope, System.Management.EventQuery query) { } + public ManagementEventWatcher(System.Management.ManagementScope scope, System.Management.EventQuery query, System.Management.EventWatcherOptions options) { } + public ManagementEventWatcher(string query) { } + public ManagementEventWatcher(string scope, string query) { } + public ManagementEventWatcher(string scope, string query, System.Management.EventWatcherOptions options) { } + public System.Management.EventWatcherOptions Options { get { throw null; } set { } } + public System.Management.EventQuery Query { get { throw null; } set { } } + public System.Management.ManagementScope Scope { get { throw null; } set { } } + public event System.Management.EventArrivedEventHandler EventArrived { add { } remove { } } + public event System.Management.StoppedEventHandler Stopped { add { } remove { } } + ~ManagementEventWatcher() { } + public void Start() { } + public void Stop() { } + public System.Management.ManagementBaseObject WaitForNextEvent() { throw null; } + } + public partial class ManagementException : System.SystemException + { + public ManagementException() { } + protected ManagementException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + public ManagementException(string message) { } + public ManagementException(string message, System.Exception innerException) { } + public System.Management.ManagementStatus ErrorCode { get { throw null; } } + public System.Management.ManagementBaseObject ErrorInformation { get { throw null; } } + public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + } + public partial class ManagementNamedValueCollection : System.Collections.Specialized.NameObjectCollectionBase + { + public ManagementNamedValueCollection() { } + protected ManagementNamedValueCollection(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + public object this[string name] { get { throw null; } } + public void Add(string name, object value) { } + public System.Management.ManagementNamedValueCollection Clone() { throw null; } + public void Remove(string name) { } + public void RemoveAll() { } + } + public partial class ManagementObject : System.Management.ManagementBaseObject, System.ICloneable + { + public ManagementObject() : base (default(System.Runtime.Serialization.SerializationInfo), default(System.Runtime.Serialization.StreamingContext)) { } + public ManagementObject(System.Management.ManagementPath path) : base (default(System.Runtime.Serialization.SerializationInfo), default(System.Runtime.Serialization.StreamingContext)) { } + public ManagementObject(System.Management.ManagementPath path, System.Management.ObjectGetOptions options) : base (default(System.Runtime.Serialization.SerializationInfo), default(System.Runtime.Serialization.StreamingContext)) { } + public ManagementObject(System.Management.ManagementScope scope, System.Management.ManagementPath path, System.Management.ObjectGetOptions options) : base (default(System.Runtime.Serialization.SerializationInfo), default(System.Runtime.Serialization.StreamingContext)) { } + protected ManagementObject(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base (default(System.Runtime.Serialization.SerializationInfo), default(System.Runtime.Serialization.StreamingContext)) { } + public ManagementObject(string path) : base (default(System.Runtime.Serialization.SerializationInfo), default(System.Runtime.Serialization.StreamingContext)) { } + public ManagementObject(string path, System.Management.ObjectGetOptions options) : base (default(System.Runtime.Serialization.SerializationInfo), default(System.Runtime.Serialization.StreamingContext)) { } + public ManagementObject(string scopeString, string pathString, System.Management.ObjectGetOptions options) : base (default(System.Runtime.Serialization.SerializationInfo), default(System.Runtime.Serialization.StreamingContext)) { } + public override System.Management.ManagementPath ClassPath { get { throw null; } } + public System.Management.ObjectGetOptions Options { get { throw null; } set { } } + public virtual System.Management.ManagementPath Path { get { throw null; } set { } } + public System.Management.ManagementScope Scope { get { throw null; } set { } } + public override object Clone() { throw null; } + public void CopyTo(System.Management.ManagementOperationObserver watcher, System.Management.ManagementPath path) { } + public void CopyTo(System.Management.ManagementOperationObserver watcher, System.Management.ManagementPath path, System.Management.PutOptions options) { } + public void CopyTo(System.Management.ManagementOperationObserver watcher, string path) { } + public void CopyTo(System.Management.ManagementOperationObserver watcher, string path, System.Management.PutOptions options) { } + public System.Management.ManagementPath CopyTo(System.Management.ManagementPath path) { throw null; } + public System.Management.ManagementPath CopyTo(System.Management.ManagementPath path, System.Management.PutOptions options) { throw null; } + public System.Management.ManagementPath CopyTo(string path) { throw null; } + public System.Management.ManagementPath CopyTo(string path, System.Management.PutOptions options) { throw null; } + public void Delete() { } + public void Delete(System.Management.DeleteOptions options) { } + public void Delete(System.Management.ManagementOperationObserver watcher) { } + public void Delete(System.Management.ManagementOperationObserver watcher, System.Management.DeleteOptions options) { } + public new void Dispose() { } + public void Get() { } + public void Get(System.Management.ManagementOperationObserver watcher) { } + public System.Management.ManagementBaseObject GetMethodParameters(string methodName) { throw null; } + protected override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + public System.Management.ManagementObjectCollection GetRelated() { throw null; } + public void GetRelated(System.Management.ManagementOperationObserver watcher) { } + public void GetRelated(System.Management.ManagementOperationObserver watcher, string relatedClass) { } + public void GetRelated(System.Management.ManagementOperationObserver watcher, string relatedClass, string relationshipClass, string relationshipQualifier, string relatedQualifier, string relatedRole, string thisRole, bool classDefinitionsOnly, System.Management.EnumerationOptions options) { } + public System.Management.ManagementObjectCollection GetRelated(string relatedClass) { throw null; } + public System.Management.ManagementObjectCollection GetRelated(string relatedClass, string relationshipClass, string relationshipQualifier, string relatedQualifier, string relatedRole, string thisRole, bool classDefinitionsOnly, System.Management.EnumerationOptions options) { throw null; } + public System.Management.ManagementObjectCollection GetRelationships() { throw null; } + public void GetRelationships(System.Management.ManagementOperationObserver watcher) { } + public void GetRelationships(System.Management.ManagementOperationObserver watcher, string relationshipClass) { } + public void GetRelationships(System.Management.ManagementOperationObserver watcher, string relationshipClass, string relationshipQualifier, string thisRole, bool classDefinitionsOnly, System.Management.EnumerationOptions options) { } + public System.Management.ManagementObjectCollection GetRelationships(string relationshipClass) { throw null; } + public System.Management.ManagementObjectCollection GetRelationships(string relationshipClass, string relationshipQualifier, string thisRole, bool classDefinitionsOnly, System.Management.EnumerationOptions options) { throw null; } + public void InvokeMethod(System.Management.ManagementOperationObserver watcher, string methodName, System.Management.ManagementBaseObject inParameters, System.Management.InvokeMethodOptions options) { } + public void InvokeMethod(System.Management.ManagementOperationObserver watcher, string methodName, object[] args) { } + public System.Management.ManagementBaseObject InvokeMethod(string methodName, System.Management.ManagementBaseObject inParameters, System.Management.InvokeMethodOptions options) { throw null; } + public object InvokeMethod(string methodName, object[] args) { throw null; } + public System.Management.ManagementPath Put() { throw null; } + public void Put(System.Management.ManagementOperationObserver watcher) { } + public void Put(System.Management.ManagementOperationObserver watcher, System.Management.PutOptions options) { } + public System.Management.ManagementPath Put(System.Management.PutOptions options) { throw null; } + public override string ToString() { throw null; } + } + public partial class ManagementObjectCollection : System.Collections.ICollection, System.Collections.IEnumerable, System.IDisposable + { + internal ManagementObjectCollection() { } + public int Count { get { throw null; } } + public bool IsSynchronized { get { throw null; } } + public object SyncRoot { get { throw null; } } + public void CopyTo(System.Array array, int index) { } + public void CopyTo(System.Management.ManagementBaseObject[] objectCollection, int index) { } + public void Dispose() { } + ~ManagementObjectCollection() { } + public System.Management.ManagementObjectCollection.ManagementObjectEnumerator GetEnumerator() { throw null; } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } + public partial class ManagementObjectEnumerator : System.Collections.IEnumerator, System.IDisposable + { + internal ManagementObjectEnumerator() { } + public System.Management.ManagementBaseObject Current { get { throw null; } } + object System.Collections.IEnumerator.Current { get { throw null; } } + public void Dispose() { } + ~ManagementObjectEnumerator() { } + public bool MoveNext() { throw null; } + public void Reset() { } + } + } + [System.ComponentModel.ToolboxItemAttribute(false)] + public partial class ManagementObjectSearcher : System.ComponentModel.Component + { + public ManagementObjectSearcher() { } + public ManagementObjectSearcher(System.Management.ManagementScope scope, System.Management.ObjectQuery query) { } + public ManagementObjectSearcher(System.Management.ManagementScope scope, System.Management.ObjectQuery query, System.Management.EnumerationOptions options) { } + public ManagementObjectSearcher(System.Management.ObjectQuery query) { } + public ManagementObjectSearcher(string queryString) { } + public ManagementObjectSearcher(string scope, string queryString) { } + public ManagementObjectSearcher(string scope, string queryString, System.Management.EnumerationOptions options) { } + public System.Management.EnumerationOptions Options { get { throw null; } set { } } + public System.Management.ObjectQuery Query { get { throw null; } set { } } + public System.Management.ManagementScope Scope { get { throw null; } set { } } + public System.Management.ManagementObjectCollection Get() { throw null; } + public void Get(System.Management.ManagementOperationObserver watcher) { } + } + public partial class ManagementOperationObserver + { + public ManagementOperationObserver() { } + public event System.Management.CompletedEventHandler Completed { add { } remove { } } + public event System.Management.ObjectPutEventHandler ObjectPut { add { } remove { } } + public event System.Management.ObjectReadyEventHandler ObjectReady { add { } remove { } } + public event System.Management.ProgressEventHandler Progress { add { } remove { } } + public void Cancel() { } + } + public abstract partial class ManagementOptions : System.ICloneable + { + internal ManagementOptions() { } + public static readonly System.TimeSpan InfiniteTimeout; + public System.Management.ManagementNamedValueCollection Context { get { throw null; } set { } } + public System.TimeSpan Timeout { get { throw null; } set { } } + public abstract object Clone(); + } + public partial class ManagementPath : System.ICloneable + { + public ManagementPath() { } + public ManagementPath(string path) { } + [System.ComponentModel.RefreshPropertiesAttribute((System.ComponentModel.RefreshProperties)(1))] + public string ClassName { get { throw null; } set { } } + public static System.Management.ManagementPath DefaultPath { get { throw null; } set { } } + public bool IsClass { get { throw null; } } + public bool IsInstance { get { throw null; } } + public bool IsSingleton { get { throw null; } } + [System.ComponentModel.RefreshPropertiesAttribute((System.ComponentModel.RefreshProperties)(1))] + public string NamespacePath { get { throw null; } set { } } + [System.ComponentModel.RefreshPropertiesAttribute((System.ComponentModel.RefreshProperties)(1))] + public string Path { get { throw null; } set { } } + [System.ComponentModel.RefreshPropertiesAttribute((System.ComponentModel.RefreshProperties)(1))] + public string RelativePath { get { throw null; } set { } } + [System.ComponentModel.RefreshPropertiesAttribute((System.ComponentModel.RefreshProperties)(1))] + public string Server { get { throw null; } set { } } + public System.Management.ManagementPath Clone() { throw null; } + public void SetAsClass() { } + public void SetAsSingleton() { } + object System.ICloneable.Clone() { throw null; } + public override string ToString() { throw null; } + } + public abstract partial class ManagementQuery : System.ICloneable + { + internal ManagementQuery() { } + public virtual string QueryLanguage { get { throw null; } set { } } + public virtual string QueryString { get { throw null; } set { } } + public abstract object Clone(); + protected internal virtual void ParseQuery(string query) { } + } + public partial class ManagementScope : System.ICloneable + { + public ManagementScope() { } + public ManagementScope(System.Management.ManagementPath path) { } + public ManagementScope(System.Management.ManagementPath path, System.Management.ConnectionOptions options) { } + public ManagementScope(string path) { } + public ManagementScope(string path, System.Management.ConnectionOptions options) { } + public bool IsConnected { get { throw null; } } + public System.Management.ConnectionOptions Options { get { throw null; } set { } } + public System.Management.ManagementPath Path { get { throw null; } set { } } + public System.Management.ManagementScope Clone() { throw null; } + public void Connect() { } + object System.ICloneable.Clone() { throw null; } + } + public enum ManagementStatus + { + AccessDenied = -2147217405, + AggregatingByObject = -2147217315, + AlreadyExists = -2147217383, + AmendedObject = -2147217306, + BackupRestoreWinmgmtRunning = -2147217312, + BufferTooSmall = -2147217348, + CallCanceled = -2147217358, + CannotBeAbstract = -2147217307, + CannotBeKey = -2147217377, + CannotBeSingleton = -2147217364, + CannotChangeIndexInheritance = -2147217328, + CannotChangeKeyInheritance = -2147217335, + CircularReference = -2147217337, + ClassHasChildren = -2147217371, + ClassHasInstances = -2147217370, + ClientTooSlow = -2147217305, + CriticalError = -2147217398, + Different = 262147, + DuplicateObjects = 262152, + Failed = -2147217407, + False = 1, + IllegalNull = -2147217368, + IllegalOperation = -2147217378, + IncompleteClass = -2147217376, + InitializationFailure = -2147217388, + InvalidCimType = -2147217363, + InvalidClass = -2147217392, + InvalidContext = -2147217401, + InvalidDuplicateParameter = -2147217341, + InvalidFlavor = -2147217338, + InvalidMethod = -2147217362, + InvalidMethodParameters = -2147217361, + InvalidNamespace = -2147217394, + InvalidObject = -2147217393, + InvalidObjectPath = -2147217350, + InvalidOperation = -2147217386, + InvalidOperator = -2147217309, + InvalidParameter = -2147217400, + InvalidParameterID = -2147217353, + InvalidProperty = -2147217359, + InvalidPropertyType = -2147217366, + InvalidProviderRegistration = -2147217390, + InvalidQualifier = -2147217342, + InvalidQualifierType = -2147217367, + InvalidQuery = -2147217385, + InvalidQueryType = -2147217384, + InvalidStream = -2147217397, + InvalidSuperclass = -2147217395, + InvalidSyntax = -2147217375, + LocalCredentials = -2147217308, + MarshalInvalidSignature = -2147217343, + MarshalVersionMismatch = -2147217344, + MethodDisabled = -2147217322, + MethodNotImplemented = -2147217323, + MissingAggregationList = -2147217317, + MissingGroupWithin = -2147217318, + MissingParameterID = -2147217354, + NoError = 0, + NoMoreData = 262149, + NonconsecutiveParameterIDs = -2147217352, + NondecoratedObject = -2147217374, + NotAvailable = -2147217399, + NotEventClass = -2147217319, + NotFound = -2147217406, + NotSupported = -2147217396, + OperationCanceled = 262150, + OutOfDiskSpace = -2147217349, + OutOfMemory = -2147217402, + OverrideNotAllowed = -2147217382, + ParameterIDOnRetval = -2147217351, + PartialResults = 262160, + Pending = 262151, + PrivilegeNotHeld = -2147217310, + PropagatedMethod = -2147217356, + PropagatedProperty = -2147217380, + PropagatedQualifier = -2147217381, + PropertyNotAnObject = -2147217316, + ProviderFailure = -2147217404, + ProviderLoadFailure = -2147217389, + ProviderNotCapable = -2147217372, + ProviderNotFound = -2147217391, + QueryNotImplemented = -2147217369, + QueueOverflow = -2147217311, + ReadOnly = -2147217373, + RefresherBusy = -2147217321, + RegistrationTooBroad = -2147213311, + RegistrationTooPrecise = -2147213310, + ResetToDefault = 262146, + ServerTooBusy = -2147217339, + ShuttingDown = -2147217357, + SystemProperty = -2147217360, + Timedout = 262148, + TooManyProperties = -2147217327, + TooMuchData = -2147217340, + TransportFailure = -2147217387, + TypeMismatch = -2147217403, + Unexpected = -2147217379, + UninterpretableProviderQuery = -2147217313, + UnknownObjectType = -2147217346, + UnknownPacketType = -2147217345, + UnparsableQuery = -2147217320, + UnsupportedClassUpdate = -2147217336, + UnsupportedParameter = -2147217355, + UnsupportedPutExtension = -2147217347, + UpdateOverrideNotAllowed = -2147217325, + UpdatePropagatedMethod = -2147217324, + UpdateTypeMismatch = -2147217326, + ValueOutOfRange = -2147217365, + } + public partial class MethodData + { + internal MethodData() { } + public System.Management.ManagementBaseObject InParameters { get { throw null; } } + public string Name { get { throw null; } } + public string Origin { get { throw null; } } + public System.Management.ManagementBaseObject OutParameters { get { throw null; } } + public System.Management.QualifierDataCollection Qualifiers { get { throw null; } } + } + public partial class MethodDataCollection : System.Collections.ICollection, System.Collections.IEnumerable + { + internal MethodDataCollection() { } + public int Count { get { throw null; } } + public bool IsSynchronized { get { throw null; } } + public virtual System.Management.MethodData this[string methodName] { get { throw null; } } + public object SyncRoot { get { throw null; } } + public virtual void Add(string methodName) { } + public virtual void Add(string methodName, System.Management.ManagementBaseObject inParameters, System.Management.ManagementBaseObject outParameters) { } + public void CopyTo(System.Array array, int index) { } + public void CopyTo(System.Management.MethodData[] methodArray, int index) { } + public System.Management.MethodDataCollection.MethodDataEnumerator GetEnumerator() { throw null; } + public virtual void Remove(string methodName) { } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } + public partial class MethodDataEnumerator : System.Collections.IEnumerator + { + internal MethodDataEnumerator() { } + public System.Management.MethodData Current { get { throw null; } } + object System.Collections.IEnumerator.Current { get { throw null; } } + public bool MoveNext() { throw null; } + public void Reset() { } + } + } + public partial class ObjectGetOptions : System.Management.ManagementOptions + { + public ObjectGetOptions() { } + public ObjectGetOptions(System.Management.ManagementNamedValueCollection context) { } + public ObjectGetOptions(System.Management.ManagementNamedValueCollection context, System.TimeSpan timeout, bool useAmendedQualifiers) { } + public bool UseAmendedQualifiers { get { throw null; } set { } } + public override object Clone() { throw null; } + } + public partial class ObjectPutEventArgs : System.Management.ManagementEventArgs + { + internal ObjectPutEventArgs() { } + public System.Management.ManagementPath Path { get { throw null; } } + } + public delegate void ObjectPutEventHandler(object sender, System.Management.ObjectPutEventArgs e); + public partial class ObjectQuery : System.Management.ManagementQuery + { + public ObjectQuery() { } + public ObjectQuery(string query) { } + public ObjectQuery(string language, string query) { } + public override object Clone() { throw null; } + } + public partial class ObjectReadyEventArgs : System.Management.ManagementEventArgs + { + internal ObjectReadyEventArgs() { } + public System.Management.ManagementBaseObject NewObject { get { throw null; } } + } + public delegate void ObjectReadyEventHandler(object sender, System.Management.ObjectReadyEventArgs e); + public partial class ProgressEventArgs : System.Management.ManagementEventArgs + { + internal ProgressEventArgs() { } + public int Current { get { throw null; } } + public string Message { get { throw null; } } + public int UpperBound { get { throw null; } } + } + public delegate void ProgressEventHandler(object sender, System.Management.ProgressEventArgs e); + public partial class PropertyData + { + internal PropertyData() { } + public bool IsArray { get { throw null; } } + public bool IsLocal { get { throw null; } } + public string Name { get { throw null; } } + public string Origin { get { throw null; } } + public System.Management.QualifierDataCollection Qualifiers { get { throw null; } } + public System.Management.CimType Type { get { throw null; } } + public object Value { get { throw null; } set { } } + } + public partial class PropertyDataCollection : System.Collections.ICollection, System.Collections.IEnumerable + { + internal PropertyDataCollection() { } + public int Count { get { throw null; } } + public bool IsSynchronized { get { throw null; } } + public virtual System.Management.PropertyData this[string propertyName] { get { throw null; } } + public object SyncRoot { get { throw null; } } + public void Add(string propertyName, System.Management.CimType propertyType, bool isArray) { } + public virtual void Add(string propertyName, object propertyValue) { } + public void Add(string propertyName, object propertyValue, System.Management.CimType propertyType) { } + public void CopyTo(System.Array array, int index) { } + public void CopyTo(System.Management.PropertyData[] propertyArray, int index) { } + public System.Management.PropertyDataCollection.PropertyDataEnumerator GetEnumerator() { throw null; } + public virtual void Remove(string propertyName) { } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } + public partial class PropertyDataEnumerator : System.Collections.IEnumerator + { + internal PropertyDataEnumerator() { } + public System.Management.PropertyData Current { get { throw null; } } + object System.Collections.IEnumerator.Current { get { throw null; } } + public bool MoveNext() { throw null; } + public void Reset() { } + } + } + public partial class PutOptions : System.Management.ManagementOptions + { + public PutOptions() { } + public PutOptions(System.Management.ManagementNamedValueCollection context) { } + public PutOptions(System.Management.ManagementNamedValueCollection context, System.TimeSpan timeout, bool useAmendedQualifiers, System.Management.PutType putType) { } + public System.Management.PutType Type { get { throw null; } set { } } + public bool UseAmendedQualifiers { get { throw null; } set { } } + public override object Clone() { throw null; } + } + public enum PutType + { + CreateOnly = 2, + None = 0, + UpdateOnly = 1, + UpdateOrCreate = 3, + } + public partial class QualifierData + { + internal QualifierData() { } + public bool IsAmended { get { throw null; } set { } } + public bool IsLocal { get { throw null; } } + public bool IsOverridable { get { throw null; } set { } } + public string Name { get { throw null; } } + public bool PropagatesToInstance { get { throw null; } set { } } + public bool PropagatesToSubclass { get { throw null; } set { } } + public object Value { get { throw null; } set { } } + } + public partial class QualifierDataCollection : System.Collections.ICollection, System.Collections.IEnumerable + { + internal QualifierDataCollection() { } + public int Count { get { throw null; } } + public bool IsSynchronized { get { throw null; } } + public virtual System.Management.QualifierData this[string qualifierName] { get { throw null; } } + public object SyncRoot { get { throw null; } } + public virtual void Add(string qualifierName, object qualifierValue) { } + public virtual void Add(string qualifierName, object qualifierValue, bool isAmended, bool propagatesToInstance, bool propagatesToSubclass, bool isOverridable) { } + public void CopyTo(System.Array array, int index) { } + public void CopyTo(System.Management.QualifierData[] qualifierArray, int index) { } + public System.Management.QualifierDataCollection.QualifierDataEnumerator GetEnumerator() { throw null; } + public virtual void Remove(string qualifierName) { } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } + public partial class QualifierDataEnumerator : System.Collections.IEnumerator + { + internal QualifierDataEnumerator() { } + public System.Management.QualifierData Current { get { throw null; } } + object System.Collections.IEnumerator.Current { get { throw null; } } + public bool MoveNext() { throw null; } + public void Reset() { } + } + } + public partial class RelatedObjectQuery : System.Management.WqlObjectQuery + { + public RelatedObjectQuery() { } + public RelatedObjectQuery(bool isSchemaQuery, string sourceObject, string relatedClass, string relationshipClass, string relatedQualifier, string relationshipQualifier, string relatedRole, string thisRole) { } + public RelatedObjectQuery(string queryOrSourceObject) { } + public RelatedObjectQuery(string sourceObject, string relatedClass) { } + public RelatedObjectQuery(string sourceObject, string relatedClass, string relationshipClass, string relatedQualifier, string relationshipQualifier, string relatedRole, string thisRole, bool classDefinitionsOnly) { } + public bool ClassDefinitionsOnly { get { throw null; } set { } } + public bool IsSchemaQuery { get { throw null; } set { } } + public string RelatedClass { get { throw null; } set { } } + public string RelatedQualifier { get { throw null; } set { } } + public string RelatedRole { get { throw null; } set { } } + public string RelationshipClass { get { throw null; } set { } } + public string RelationshipQualifier { get { throw null; } set { } } + public string SourceObject { get { throw null; } set { } } + public string ThisRole { get { throw null; } set { } } + protected internal void BuildQuery() { } + public override object Clone() { throw null; } + protected internal override void ParseQuery(string query) { } + } + public partial class RelationshipQuery : System.Management.WqlObjectQuery + { + public RelationshipQuery() { } + public RelationshipQuery(bool isSchemaQuery, string sourceObject, string relationshipClass, string relationshipQualifier, string thisRole) { } + public RelationshipQuery(string queryOrSourceObject) { } + public RelationshipQuery(string sourceObject, string relationshipClass) { } + public RelationshipQuery(string sourceObject, string relationshipClass, string relationshipQualifier, string thisRole, bool classDefinitionsOnly) { } + public bool ClassDefinitionsOnly { get { throw null; } set { } } + public bool IsSchemaQuery { get { throw null; } set { } } + public string RelationshipClass { get { throw null; } set { } } + public string RelationshipQualifier { get { throw null; } set { } } + public string SourceObject { get { throw null; } set { } } + public string ThisRole { get { throw null; } set { } } + protected internal void BuildQuery() { } + public override object Clone() { throw null; } + protected internal override void ParseQuery(string query) { } + } + public partial class SelectQuery : System.Management.WqlObjectQuery + { + public SelectQuery() { } + public SelectQuery(bool isSchemaQuery, string condition) { } + public SelectQuery(string queryOrClassName) { } + public SelectQuery(string className, string condition) { } + public SelectQuery(string className, string condition, string[] selectedProperties) { } + public string ClassName { get { throw null; } set { } } + public string Condition { get { throw null; } set { } } + public bool IsSchemaQuery { get { throw null; } set { } } + public override string QueryString { get { throw null; } set { } } + public System.Collections.Specialized.StringCollection SelectedProperties { get { throw null; } set { } } + protected internal void BuildQuery() { } + public override object Clone() { throw null; } + protected internal override void ParseQuery(string query) { } + } + public partial class StoppedEventArgs : System.Management.ManagementEventArgs + { + internal StoppedEventArgs() { } + public System.Management.ManagementStatus Status { get { throw null; } } + } + public delegate void StoppedEventHandler(object sender, System.Management.StoppedEventArgs e); + public enum TextFormat + { + CimDtd20 = 1, + Mof = 0, + WmiDtd20 = 2, + } + public partial class WqlEventQuery : System.Management.EventQuery + { + public WqlEventQuery() { } + public WqlEventQuery(string queryOrEventClassName) { } + public WqlEventQuery(string eventClassName, string condition) { } + public WqlEventQuery(string eventClassName, string condition, System.TimeSpan groupWithinInterval) { } + public WqlEventQuery(string eventClassName, string condition, System.TimeSpan groupWithinInterval, string[] groupByPropertyList) { } + public WqlEventQuery(string eventClassName, System.TimeSpan withinInterval) { } + public WqlEventQuery(string eventClassName, System.TimeSpan withinInterval, string condition) { } + public WqlEventQuery(string eventClassName, System.TimeSpan withinInterval, string condition, System.TimeSpan groupWithinInterval, string[] groupByPropertyList, string havingCondition) { } + public string Condition { get { throw null; } set { } } + public string EventClassName { get { throw null; } set { } } + public System.Collections.Specialized.StringCollection GroupByPropertyList { get { throw null; } set { } } + public System.TimeSpan GroupWithinInterval { get { throw null; } set { } } + public string HavingCondition { get { throw null; } set { } } + public override string QueryLanguage { get { throw null; } } + public override string QueryString { get { throw null; } set { } } + public System.TimeSpan WithinInterval { get { throw null; } set { } } + protected internal void BuildQuery() { } + public override object Clone() { throw null; } + protected internal override void ParseQuery(string query) { } + } + public partial class WqlObjectQuery : System.Management.ObjectQuery + { + public WqlObjectQuery() { } + public WqlObjectQuery(string query) { } + public override string QueryLanguage { get { throw null; } } + public override object Clone() { throw null; } + } +} diff --git a/external/corefx/src/System.Management/ref/System.Management.csproj b/external/corefx/src/System.Management/ref/System.Management.csproj new file mode 100644 index 0000000000..9695d31aac --- /dev/null +++ b/external/corefx/src/System.Management/ref/System.Management.csproj @@ -0,0 +1,16 @@ + + + + + {CA17270B-079F-4D52-97E8-C0C2E8B9D7DB} + + + + + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Management/src/Configurations.props b/external/corefx/src/System.Management/src/Configurations.props new file mode 100644 index 0000000000..94ac07fdab --- /dev/null +++ b/external/corefx/src/System.Management/src/Configurations.props @@ -0,0 +1,13 @@ + + + + + netstandard; + netcoreapp2.0-Windows_NT; + + + $(PackageConfigurations); + netcoreapp-Windows_NT; + + + diff --git a/external/corefx/src/System.Management/src/Resources/Strings.resx b/external/corefx/src/System.Management/src/Resources/Strings.resx new file mode 100644 index 0000000000..1f8a70f5f6 --- /dev/null +++ b/external/corefx/src/System.Management/src/Resources/Strings.resx @@ -0,0 +1,296 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The Query string supplied was invalid or improperly formed + + + The Query string supplied was invalid because it contains a duplicate token + + + The Query string supplied was invalid because a supplied token was null + + + Unable to wakeup the worker thread to create an object in MTA + + + + ClassName not initialized. + + + Class name does not match. + + + Every property added to the class for WMI property has attributes set to define its behavior in Visual Studio designer and also to define a TypeConverter to be used. + + + Property to show the commit behavior for the WMI object. If true, WMI object will be automatically saved after each property modification.(ie. Put() is called after modification of a property). + + + An Early Bound class generated for the WMI class. + + + Below are different overloads of constructors to initialize an instance of the class with a WMI object. + + + Private property to hold the name of WMI class which created this class. + + + Private property to hold the WMI namespace in which the class resides. + + + The current WMI object used + + + Datetime conversion functions ToDateTime and ToDmtfDateTime are added to the class to convert DMTF datetime to System.DateTime and vice-versa. + + + Private variable to hold the embedded property representing the instance. + + + Enumerator implementation for enumerating instances of the class. + + + Flag to indicate if the instance is an embedded object. + + + Different overloads of GetInstances() help in enumerating instances of the WMI class. + + + Functions Is<PropertyName>Null() are used to check if a property is NULL. + + + Underlying lateBound WMI object. + + + Property returning the underlying lateBound object. + + + The ManagementPath of the underlying WMI object. + + + ManagementScope of the object. + + + Property returns the namespace of the WMI class. + + + Member variable to store the 'automatic commit' behavior for the class. + + + TypeConverter to handle null values for ValueType properties + + + Functions Reset<PropertyName> are added for Nullable Read/Write properties. These functions are used by VS designer in property browser to set a property to NULL. + + + Functions ShouldSerialize<PropertyName> are functions used by VS property browser to check if a particular property has to be serialized. These functions are added for all ValueType properties ( properties of type Int32, BOOL etc.. which cannot be set to null). These functions use Is<PropertyName>Null function. These functions are also used in the TypeConverter implementation for the properties to check for NULL value of property so that an empty value can be shown in Property browser in case of Drag and Drop in Visual studio. + + + Private member variable to hold the ManagementScope which is used by the various methods. + + + Public static scope property which is used by the various methods. + + + Property pointing to an embedded object to get System properties of the WMI object. + + + Embedded class to represent WMI system Properties. + + + Time interval functions ToTimeSpan and ToDmtfTimeInterval are added to the class to convert DMTF Time Interval to System.TimeSpan and vice-versa. + + + Converts a given datetime in DMTF format to System.DateTime object. + + + Converts a given System.DateTime object to DMTF datetime format. + + + Converts a given System.TimeSpan object to DMTF Time interval format. + + + Converts a given time interval in DMTF format to System.TimeSpan object. + + + If the embedded property is strongly typed then, to strongly type the property to the type of + + + the embedded object, you have to do the following things. + + + \t1. Generate Managed class for the WMI class of the embedded property. This can be done with MgmtClassGen.exe tool or from Server Explorer. + + + \t2. Include the namespace of the generated class. + + + \t3. Change the property get/set functions so as return the instance of the Managed class. + + + Below is a sample code. + + + VB Code + + + C# Code + + + public <ManagedClassName of Embedded property> <PropertyName> + + + \t\tIf (AutoCommitProp == true && isEmbedded == false) + + + \t\t{ + + + \t\t\tPrivateLateBoundObject.Put(); + + + \t\t} + + + \t} + + + } + + + { + + + \tget + + + \t{ + + + \t\treturn new <ManagedClassName of Embedded property>((System.Management.ManagementBaseObject)(curObj["<PropertyName>"])); + + + \t} + + + \tset + + + \t{ + + + \t\tcurObj["<PropertyName>"] = value.LateBoundObject; + + + Public Property <PropertyName>() As <ManagedClassName of Embedded property> + + + \tEnd Set + + + \tGet + + + \t\tReturn New <ManagedClassName of Embedded Property>(CType(curObj("<PropertyName>"),System.Management.ManagementBaseObject)) + + + \tEnd Get + + + \tSet(ByVal Value As <ManagedClassName of Embedded property>) + + + \t\tcurObj("EObject")=Value.LateBoundObject + + + \t\tIf (AutoCommitProp = True And isEmbedded = False) Then + + + \t\t\tPrivateLateBoundObject.Put() + + + \t\tEnd If + + + FilePath cannot be empty. + + + Namespace not initialized. + + + FilePath or code generator object is null. + + + Unable to create code generator for '{0}' + + + System.Management currently is only supported for Windows desktop applications. + + + Could not find an installation of .NET Framework v4.0.30319. System.Management requires native modules from the .NET Framework to operate. + + + Failed to load required native library '{0}'. + + + The native library '{0}' does not have all required functions. Please, update the .NET Framework. + + diff --git a/external/corefx/src/System.Management/src/System.Management.csproj b/external/corefx/src/System.Management/src/System.Management.csproj new file mode 100644 index 0000000000..a33d10ceaa --- /dev/null +++ b/external/corefx/src/System.Management/src/System.Management.csproj @@ -0,0 +1,83 @@ + + + + + {950923D8-EC99-4F90-9BA6-8EB64582C555} + System.Management + true + $(NoWarn);0618 + + false + true + SR.PlatformNotSupported_SystemManagement + + + + + + + + + + Common\Interop\Windows\Interop.Libraries.cs + + + Common\Interop\Windows\kernel32\Interop.FreeLibrary.cs + + + Common\Interop\Windows\kernel32\Interop.GetProcAddress.cs + + + Common\Interop\Windows\kernel32\Interop.LoadLibrary.cs + + + Common\Microsoft\Win32\SafeHandles\SafeLibraryHandle.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Management/src/System/Management/InteropClasses/WMIInterop.cs.REMOVED.git-id b/external/corefx/src/System.Management/src/System/Management/InteropClasses/WMIInterop.cs.REMOVED.git-id new file mode 100644 index 0000000000..12b18460df --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/InteropClasses/WMIInterop.cs.REMOVED.git-id @@ -0,0 +1 @@ +9df9a049f1d4c104728818fe571bc0bfa772cc2d \ No newline at end of file diff --git a/external/corefx/src/System.Management/src/System/Management/ManagementBaseObject.cs b/external/corefx/src/System.Management/src/System/Management/ManagementBaseObject.cs new file mode 100644 index 0000000000..4f1b16640c --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/ManagementBaseObject.cs @@ -0,0 +1,836 @@ +// 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.Runtime.InteropServices; +using System.ComponentModel; +using System.Runtime.Serialization; +using System.Security.Permissions; + +namespace System.Management +{ + /// + /// Describes the possible text formats that can be used with . + /// + public enum TextFormat + { + /// + /// Managed Object Format + /// + Mof = 0, + /// + /// XML DTD that corresponds to CIM DTD version 2.0 + /// + CimDtd20 = 1, + /// + /// XML WMI DTD that corresponds to CIM DTD version 2.0. + /// Using this value enables a few WMI-specific extensions, like embedded objects. + /// + WmiDtd20 = 2 + }; + + /// + /// Describes the possible CIM types for properties, qualifiers, or method parameters. + /// + public enum CimType + { + /// + /// Invalid Type + /// + None = 0, + /// + /// A signed 8-bit integer. + /// + SInt8 = 16, + /// + /// An unsigned 8-bit integer. + /// + UInt8 = 17, + /// + /// A signed 16-bit integer. + /// + SInt16 = 2, + /// + /// An unsigned 16-bit integer. + /// + UInt16 = 18, + /// + /// A signed 32-bit integer. + /// + SInt32 = 3, + /// + /// An unsigned 32-bit integer. + /// + UInt32 = 19, + /// + /// A signed 64-bit integer. + /// + SInt64 = 20, + /// + /// An unsigned 64-bit integer. + /// + UInt64 = 21, + /// + /// A floating-point 32-bit number. + /// + Real32 = 4, + /// + /// A floating point 64-bit number. + /// + Real64 = 5, + /// + /// A boolean. + /// + Boolean = 11, + /// + /// A string. + /// + String = 8, + /// + /// A date or time value, represented in a string in DMTF + /// date/time format: yyyymmddHHMMSS.mmmmmmsUUU + /// where: + /// yyyymmdd - is the date in year/month/day + /// HHMMSS - is the time in hours/minutes/seconds + /// mmmmmm - is the number of microseconds in 6 digits + /// sUUU - is a sign (+ or -) and a 3-digit UTC offset + /// + DateTime = 101, + /// + /// A reference to another object. This is represented by a + /// string containing the path to the referenced object + /// + Reference = 102, + /// + /// A 16-bit character. + /// + Char16 = 103, + /// + /// An embedded object. + /// Note that embedded objects differ from references in that the embedded object + /// doesn't have a path and its lifetime is identical to the lifetime of the + /// containing object. + /// + Object = 13, + }; + + /// + /// Describes the object comparison modes that can be used with . + /// Note that these values may be combined. + /// + [Flags] + public enum ComparisonSettings + { + /// + /// A mode that compares all elements of the compared objects. + /// + IncludeAll = 0, + /// + /// A mode that compares the objects, ignoring qualifiers. + /// + IgnoreQualifiers = 0x1, + /// + /// A mode that ignores the source of the objects, namely the server + /// and the namespace they came from, in comparison to other objects. + /// + IgnoreObjectSource = 0x2, + /// + /// A mode that ignores the default values of properties. + /// This value is only meaningful when comparing classes. + /// + IgnoreDefaultValues = 0x4, + /// + /// A mode that assumes that the objects being compared are instances of + /// the same class. Consequently, this value causes comparison + /// of instance-related information only. Use this flag to optimize + /// performance. If the objects are not of the same class, the results are undefined. + /// + IgnoreClass = 0x8, + /// + /// A mode that compares string values in a case-insensitive + /// manner. This applies to strings and to qualifier values. Property and qualifier + /// names are always compared in a case-insensitive manner whether this flag is + /// specified or not. + /// + IgnoreCase = 0x10, + /// + /// A mode that ignores qualifier flavors. This flag still takes + /// qualifier values into account, but ignores flavor distinctions such as + /// propagation rules and override restrictions. + /// + IgnoreFlavor = 0x20 + }; + + + internal enum QualifierType + { + ObjectQualifier, + PropertyQualifier, + MethodQualifier + } + + + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + /// + /// Contains the basic elements of a management + /// object. It serves as a base class to more specific management object classes. + /// + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + [ToolboxItem(false)] + public class ManagementBaseObject : Component, ICloneable, ISerializable + { + // This field holds onto a WbemContext for the lifetime of the appdomain. This should + // prevent Fastprox.dll from unloading prematurely. + // Since this is fixed in WinXP, we only hold onto a WbemContext if we are NOT running XP or later. + +#pragma warning disable 0414 // Kept for possible reflection, comment above for history + private static WbemContext lockOnFastProx = null; // RemovedDuringPort System.Management.Instrumentation.WMICapabilities.IsWindowsXPOrHigher()?null:new WbemContext(); +#pragma warning restore 0414 + + // + // The wbemObject is changed from a field to a property. This is to avoid major code churn and simplify the solution to + // the problem where the Initialize call actually binds to the object. This occured even in cases like Get() whereby we + // ended up getting the object twice. Any direct usage of this property will cause a call to Initialize ( true ) to be made + // (if not already done) indicating that we wish to bind to the underlying WMI object. + // + // See changes to Initialize + // + internal IWbemClassObjectFreeThreaded wbemObject + { + get + { + if (_wbemObject == null) + { + Initialize(true); + } + return _wbemObject; + } + set + { + _wbemObject = value; + } + } + + internal IWbemClassObjectFreeThreaded _wbemObject ; + + private PropertyDataCollection properties; + private PropertyDataCollection systemProperties; + private QualifierDataCollection qualifiers; + + /// + /// Initializes a new instance of the class that is serializable. + /// + /// The to populate with data. + /// The destination (see ) for this serialization. + protected ManagementBaseObject(SerializationInfo info, StreamingContext context) + { + throw new PlatformNotSupportedException(); + } + + public new void Dispose() + { + if (_wbemObject != null) + { + _wbemObject.Dispose(); + _wbemObject = null; + } + base.Dispose(); + GC.SuppressFinalize(this); + } + + /// + /// Provides the internal WMI object represented by a ManagementObject. + /// See remarks with regard to usage. + /// + /// The that references the requested WMI object. + /// + /// An representing the internal WMI object. + /// + /// + /// This operator is used internally by instrumentation code. It is not intended + /// for direct use by regular client or instrumented applications. + /// + public static explicit operator IntPtr(ManagementBaseObject managementObject) + { + if(null == managementObject) + return IntPtr.Zero; + + return (IntPtr)managementObject.wbemObject; + } + + + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + throw new PlatformNotSupportedException(); + } + + protected virtual void GetObjectData(SerializationInfo info, StreamingContext context) + { + throw new PlatformNotSupportedException(); + } + + // Factory + /// + /// Factory for various types of base object + /// + /// IWbemClassObject + /// The scope + internal static ManagementBaseObject GetBaseObject( + IWbemClassObjectFreeThreaded wbemObject, + ManagementScope scope) + { + ManagementBaseObject newObject = null; + + if (_IsClass(wbemObject)) + newObject = ManagementClass.GetManagementClass(wbemObject, scope); + else + newObject = ManagementObject.GetManagementObject(wbemObject, scope); + + return newObject; + } + + //Constructor + internal ManagementBaseObject(IWbemClassObjectFreeThreaded wbemObject) + { + this.wbemObject = wbemObject; + properties = null; + systemProperties = null; + qualifiers = null; + } + + /// + /// Returns a copy of the object. + /// + /// + /// The new cloned object. + /// + public virtual Object Clone() + { + IWbemClassObjectFreeThreaded theClone = null; + + int status = wbemObject.Clone_(out theClone); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + return new ManagementBaseObject(theClone); + } + + internal virtual void Initialize ( bool getObject ) {} + + // + //Properties + // + + /// + /// Gets or sets a collection of objects describing the properties of the + /// management object. + /// + /// + /// A that represents the + /// properties of the management object. + /// + /// + public virtual PropertyDataCollection Properties + { + get + { + Initialize ( true ) ; + + if (properties == null) + properties = new PropertyDataCollection(this, false); + + return properties; + } + } + + /// + /// Gets or sets the collection of WMI system properties of the management object (for example, the + /// class name, server, and namespace). WMI system property names begin with + /// "__". + /// + /// + /// A that represents the system properties of the management object. + /// + /// + public virtual PropertyDataCollection SystemProperties + { + get + { + Initialize ( false ) ; + + if (systemProperties == null) + systemProperties = new PropertyDataCollection(this, true); + + return systemProperties; + } + } + + /// + /// Gets or sets the collection of objects defined on the management object. + /// Each element in the collection holds information such as the qualifier name, + /// value, and flavor. + /// + /// + /// A that represents the qualifiers + /// defined on the management object. + /// + /// + public virtual QualifierDataCollection Qualifiers + { + get + { + Initialize ( true ) ; + + if (qualifiers == null) + qualifiers = new QualifierDataCollection(this); + + return qualifiers; + } + } + + /// + /// Gets or sets the path to the management object's class. + /// + /// + /// A that represents the path to the management object's class. + /// + /// + /// For example, for the \\MyBox\root\cimv2:Win32_LogicalDisk= + /// 'C:' object, the class path is \\MyBox\root\cimv2:Win32_LogicalDisk + /// . + /// + public virtual ManagementPath ClassPath + { + get + { + Object serverName = null; + Object scopeName = null; + Object className = null; + int propertyType = 0; + int propertyFlavor = 0; + int status = (int)ManagementStatus.NoError; + + status = wbemObject.Get_("__SERVER", 0, ref serverName, ref propertyType, ref propertyFlavor); + + if (status == (int)ManagementStatus.NoError) + { + status = wbemObject.Get_("__NAMESPACE", 0, ref scopeName, ref propertyType, ref propertyFlavor); + + if (status == (int)ManagementStatus.NoError) + status = wbemObject.Get_("__CLASS", 0, ref className, ref propertyType, ref propertyFlavor); + } + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + ManagementPath classPath = new ManagementPath(); + + // initialize in case of throw + classPath.Server = String.Empty; + classPath.NamespacePath = String.Empty; + classPath.ClassName = String.Empty; + + // Some of these may throw if they are NULL + try + { + classPath.Server = (string)(serverName is System.DBNull ? "" : serverName); + classPath.NamespacePath = (string)(scopeName is System.DBNull ? "" : scopeName); + classPath.ClassName = (string)(className is System.DBNull ? "" : className); + } + catch + { + } + + return classPath; + } + } + + + // + //Methods + // + + //****************************************************** + //[] operator by property name + //****************************************************** + /// + /// Gets access to property values through [] notation. + /// + /// The name of the property of interest. + /// + /// An containing the + /// value of the requested property. + /// + public Object this[string propertyName] + { + get { return GetPropertyValue(propertyName); } + set + { + Initialize ( true ) ; + try + { + SetPropertyValue (propertyName, value); + } + catch (COMException e) + { + ManagementException.ThrowWithExtendedInfo(e); + } + } + } + + //****************************************************** + //GetPropertyValue + //****************************************************** + /// + /// Gets an equivalent accessor to a property's value. + /// + /// The name of the property of interest. + /// + /// The value of the specified property. + /// + public Object GetPropertyValue(string propertyName) + { + if (null == propertyName) + throw new ArgumentNullException ("propertyName"); + + // Check for system properties + if (propertyName.StartsWith ("__", StringComparison.Ordinal)) + return SystemProperties[propertyName].Value; + else + return Properties[propertyName].Value; + } + + //****************************************************** + //GetQualifierValue + //****************************************************** + /// + /// Gets the value of the specified qualifier. + /// + /// The name of the qualifier of interest. + /// + /// The value of the specified qualifier. + /// + public Object GetQualifierValue(string qualifierName) + { + return Qualifiers [qualifierName].Value; + } + + //****************************************************** + //SetQualifierValue + //****************************************************** + /// + /// Sets the value of the named qualifier. + /// + /// The name of the qualifier to set. This parameter cannot be null. + /// The value to set. + public void SetQualifierValue(string qualifierName, object qualifierValue) + { + Qualifiers [qualifierName].Value = qualifierValue; + } + + + //****************************************************** + //GetPropertyQualifierValue + //****************************************************** + /// + /// Returns the value of the specified property qualifier. + /// + /// The name of the property to which the qualifier belongs. + /// The name of the property qualifier of interest. + /// + /// The value of the specified qualifier. + /// + public Object GetPropertyQualifierValue(string propertyName, string qualifierName) + { + return Properties[propertyName].Qualifiers[qualifierName].Value; + } + + //****************************************************** + //SetPropertyQualifierValue + //****************************************************** + /// + /// Sets the value of the specified property qualifier. + /// + /// The name of the property to which the qualifier belongs. + /// The name of the property qualifier of interest. + /// The new value for the qualifier. + public void SetPropertyQualifierValue(string propertyName, string qualifierName, + object qualifierValue) + { + Properties[propertyName].Qualifiers[qualifierName].Value = qualifierValue; + } + + //****************************************************** + //GetText + //****************************************************** + /// + /// Returns a textual representation of the object in the specified format. + /// + /// The requested textual format. + /// + /// The textual representation of the + /// object in the specified format. + /// + public string GetText(TextFormat format) + { + string objText = null; + int status = (int)ManagementStatus.NoError; + + // + // Removed Initialize call since wbemObject is a property that will call Initialize ( true ) on + // its getter. + // + switch(format) + { + case TextFormat.Mof : + + status = wbemObject.GetObjectText_(0, out objText); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + return objText; + + case TextFormat.CimDtd20 : + case TextFormat.WmiDtd20 : + + //This may throw on non-XP platforms... - should we catch ? + IWbemObjectTextSrc wbemTextSrc = (IWbemObjectTextSrc)new WbemObjectTextSrc(); + IWbemContext ctx = (IWbemContext)new WbemContext(); + object v = (bool)true; + ctx.SetValue_("IncludeQualifiers", 0, ref v); + ctx.SetValue_("IncludeClassOrigin", 0, ref v); + + if (wbemTextSrc != null) + { + status = wbemTextSrc.GetText_(0, + (IWbemClassObject_DoNotMarshal)(Marshal.GetObjectForIUnknown(wbemObject)), + (uint)format, //note: this assumes the format enum has the same values as the underlying WMI enum !! + ctx, + out objText); + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + return objText; + + default : + + return null; + } + } + + /// + /// Compares two management objects. + /// + /// An object to compare with this instance. + /// + /// if + /// is an instance of and represents + /// the same object as this instance; otherwise, . + /// + public override bool Equals(object obj) + { + bool result = false; + + try + { + if (obj is ManagementBaseObject) + { + result = CompareTo ((ManagementBaseObject)obj, ComparisonSettings.IncludeAll); + } + else + { + return false; + } + } + catch (ManagementException exc) + { + if (exc.ErrorCode == ManagementStatus.NotFound) + { + //we could wind up here if Initialize() throws (either here or inside CompareTo()) + //Since we cannot throw from Equals() imprelemtation and it is invalid to assume + //that two objects are different because they fail to initialize + //so, we can just compare these invalid paths "by value" + + if (this is ManagementObject && obj is ManagementObject) + { + int compareRes = String.Compare(((ManagementObject)this).Path.Path, + ((ManagementObject)obj).Path.Path, + StringComparison.OrdinalIgnoreCase); + return (compareRes == 0); + } + } + return false; + } + catch + { + return false; + } + return result; + } + + /// + /// Serves as a hash function for a particular type, suitable for use in hashing algorithms and data structures like a hash table. + /// The hash code for ManagementBaseObjects is based on the MOF for the WbemObject that this instance is based on. Two different ManagementBaseObject instances pointing to the same WbemObject in WMI will have the same mof and thus the same hash code. Changing a property value of an object will change the hash code. + /// + /// + /// A hash code for the current object. + /// + public override int GetHashCode() + { + //This implementation has to match the Equals() implementation. In Equals(), we use + //the WMI CompareTo() which compares values of properties, qualifiers etc. + //Probably the closest we can get is to take the MOF representation of the object and get it's hash code. + int localHash = 0; + try + { + // GetText may throw if it cannot get a string for the mof for various reasons + // This should be a very rare event + localHash = this.GetText(TextFormat.Mof).GetHashCode(); + } + catch (ManagementException) + { + // use the hash code of an empty string on failure to get the mof + localHash = string.Empty.GetHashCode(); + } + catch (System.Runtime.InteropServices.COMException) + { + // use the hash code of an empty string on failure to get the mof + localHash = string.Empty.GetHashCode(); + } + return localHash; + } + + //****************************************************** + //CompareTo + //****************************************************** + /// + /// Compares this object to another, based on specified options. + /// + /// The object to which to compare this object. + /// Options on how to compare the objects. + /// + /// if the objects compared are equal + /// according to the given options; otherwise, + /// . + /// + public bool CompareTo(ManagementBaseObject otherObject, ComparisonSettings settings) + { + if (null == otherObject) + throw new ArgumentNullException ("otherObject"); + + bool result = false; + + if (null != wbemObject) + { + int status = (int) ManagementStatus.NoError; + + status = wbemObject.CompareTo_((int) settings, otherObject.wbemObject); + + if ((int)ManagementStatus.Different == status) + result = false; + else if ((int)ManagementStatus.NoError == status) + result = true; + else if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else if (status < 0) + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + return result; + } + + internal string ClassName + { + get + { + object val = null; + int dummy1 = 0, dummy2 = 0; + int status = (int)ManagementStatus.NoError; + + status = wbemObject.Get_ ("__CLASS", 0, ref val, ref dummy1, ref dummy2); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + if (val is System.DBNull) + return String.Empty; + else + return ((string) val); + } + } + + private static bool _IsClass(IWbemClassObjectFreeThreaded wbemObject) + { + object val = null; + int dummy1 = 0, dummy2 = 0; + + int status = wbemObject.Get_("__GENUS", 0, ref val, ref dummy1, ref dummy2); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + return ((int)val == (int)tag_WBEM_GENUS_TYPE.WBEM_GENUS_CLASS); + } + + internal bool IsClass + { + get + { + return _IsClass(wbemObject); + } + } + + /// + /// Sets the value of the named property. + /// + /// The name of the property to be changed. + /// The new value for this property. + public void SetPropertyValue ( + string propertyName, + object propertyValue) + { + if (null == propertyName) + throw new ArgumentNullException ("propertyName"); + + // Check for system properties + if (propertyName.StartsWith ("__", StringComparison.Ordinal)) + SystemProperties[propertyName].Value = propertyValue; + else + Properties[propertyName].Value = propertyValue; + } + + }//ManagementBaseObject +} diff --git a/external/corefx/src/System.Management/src/System/Management/ManagementClass.cs b/external/corefx/src/System.Management/src/System/Management/ManagementClass.cs new file mode 100644 index 0000000000..61c66dbb86 --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/ManagementClass.cs @@ -0,0 +1,1477 @@ +// 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.Collections.Specialized; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.CodeDom; + +namespace System.Management +{ + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + /// + /// Represents a CIM management class from WMI. CIM (Common Information Model) classes + /// represent management information including hardware, software, processes, etc. + /// For more information about the CIM classes available in Windows search for �win32 classes�. + /// + /// + /// using System; + /// using System.Management; + /// + /// // This example demonstrates getting information about a class using the ManagementClass object + /// class Sample_ManagementClass + /// { + /// public static int Main(string[] args) { + /// ManagementClass diskClass = new ManagementClass("Win32_LogicalDisk"); + /// diskClass.Get(); + /// Console.WriteLine("Logical Disk class has " + diskClass.Properties.Count + " properties"); + /// return 0; + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// ' This example demonstrates getting information about a class using the ManagementClass object + /// Class Sample_ManagementClass + /// Overloads Public Shared Function Main(args() As String) As Integer + /// Dim diskClass As New ManagementClass("Win32_LogicalDisk") + /// diskClass.Get() + /// Console.WriteLine(("Logical Disk class has " & _ + /// diskClass.Properties.Count.ToString() & _ + /// " properties")) + /// Return 0 + /// End Function + /// End Class + /// + /// + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + public class ManagementClass : ManagementObject + { + private MethodDataCollection methods; + protected override void GetObjectData(SerializationInfo info, StreamingContext context) + { + throw new PlatformNotSupportedException(); + } + /// + /// Internal factory for classes, used when deriving a class + /// or cloning a class. For these purposes we always mark + /// the class as "bound". + /// + /// The underlying WMI object + /// Seed class from which we will get initialization info + internal static ManagementClass GetManagementClass( + IWbemClassObjectFreeThreaded wbemObject, + ManagementClass mgObj) + { + ManagementClass newClass = new ManagementClass(); + newClass.wbemObject = wbemObject; + + if (null != mgObj) + { + newClass.scope = ManagementScope._Clone(mgObj.scope); + + ManagementPath objPath = mgObj.Path; + + if (null != objPath) + newClass.path = ManagementPath._Clone(objPath); + + // Ensure we have our class name in the path + object className = null; + int dummy = 0; + + int status = wbemObject.Get_("__CLASS", 0, ref className, ref dummy, ref dummy); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + if (className != System.DBNull.Value) + newClass.path.internalClassName = (string)className; + + ObjectGetOptions options = mgObj.Options; + if (null != options) + newClass.options = ObjectGetOptions._Clone(options); + + // Finally we ensure that this object is marked as bound. + // We do this as a last step since setting certain properties + // (Options, Path and Scope) would mark it as unbound + // + // *** + // * Changed isBound flag to wbemObject==null check. + // * newClass.IsBound = true; + // *** + } + + return newClass; + } + + internal static ManagementClass GetManagementClass( + IWbemClassObjectFreeThreaded wbemObject, + ManagementScope scope) + { + ManagementClass newClass = new ManagementClass(); + newClass.path = new ManagementPath(ManagementPath.GetManagementPath(wbemObject)); + + if (null != scope) + newClass.scope = ManagementScope._Clone(scope); + + newClass.wbemObject = wbemObject; + + return newClass; + } + + //default constructor + /// + /// Initializes a new instance + /// of the class. + /// + /// + /// Initializes a new instance of the class. This is the + /// default constructor. + /// + /// + /// ManagementClass c = new ManagementClass(); + /// + /// Dim c As New ManagementClass() + /// + /// + public ManagementClass() : this ((ManagementScope)null, (ManagementPath)null, null) {} + + //parameterized constructors + /// + /// Initializes a new instance of the class initialized to the + /// given path. + /// + /// A specifying which WMI class to bind to. + /// + /// The parameter must specify a WMI class + /// path. + /// + /// + /// ManagementClass c = new ManagementClass( + /// new ManagementPath("Win32_LogicalDisk")); + /// + /// Dim c As New ManagementClass( _ + /// New ManagementPath("Win32_LogicalDisk")) + /// + /// + public ManagementClass(ManagementPath path) : this(null, path, null) {} + + /// + /// Initializes a new instance of the class initialized to the given path. + /// + /// The path to the WMI class. + /// + /// ManagementClass c = new + /// ManagementClass("Win32_LogicalDisk"); + /// + /// Dim c As New ManagementClass("Win32_LogicalDisk") + /// + /// + public ManagementClass(string path) : this(null, new ManagementPath(path), null) {} + + /// + /// Initializes a new instance of the class initialized to the + /// given WMI class path using the specified options. + /// + /// A representing the WMI class path. + /// An representing the options to use when retrieving this class. + /// + /// ManagementPath p = new ManagementPath("Win32_Process"); + /// //Options specify that amended qualifiers are to be retrieved along with the class + /// ObjectGetOptions o = new ObjectGetOptions(null, true); + /// ManagementClass c = new ManagementClass(p,o); + /// + /// Dim p As New ManagementPath("Win32_Process") + /// ' Options specify that amended qualifiers are to be retrieved along with the class + /// Dim o As New ObjectGetOptions(Null, True) + /// Dim c As New ManagementClass(p,o) + /// + /// + public ManagementClass(ManagementPath path, ObjectGetOptions options) : this(null, path, options) {} + + /// + /// Initializes a new instance of the class initialized to the given WMI class path + /// using the specified options. + /// + /// The path to the WMI class. + /// An representing the options to use when retrieving the WMI class. + /// + /// //Options specify that amended qualifiers should be retrieved along with the class + /// ObjectGetOptions o = new ObjectGetOptions(null, true); + /// ManagementClass c = new ManagementClass("Win32_ComputerSystem",o); + /// + /// ' Options specify that amended qualifiers should be retrieved along with the class + /// Dim o As New ObjectGetOptions(Null, True) + /// Dim c As New ManagementClass("Win32_ComputerSystem",o) + /// + /// + public ManagementClass(string path, ObjectGetOptions options) + : this(null, new ManagementPath(path), options) {} + + /// + /// Initializes a new instance of the class for the specified + /// WMI class in the specified scope and with the specified options. + /// + /// A that specifies the scope (server and namespace) where the WMI class resides. + /// A that represents the path to the WMI class in the specified scope. + /// An that specifies the options to use when retrieving the WMI class. + /// + /// The path can be specified as a full + /// path (including server and namespace). However, if a scope is specified, it will + /// override the first portion of the full path. + /// + /// + /// ManagementScope s = new ManagementScope("\\\\MyBox\\root\\cimv2"); + /// ManagementPath p = new ManagementPath("Win32_Environment"); + /// ObjectGetOptions o = new ObjectGetOptions(null, true); + /// ManagementClass c = new ManagementClass(s, p, o); + /// + /// Dim s As New ManagementScope("\\MyBox\root\cimv2") + /// Dim p As New ManagementPath("Win32_Environment") + /// Dim o As New ObjectGetOptions(Null, True) + /// Dim c As New ManagementClass(s, p, o) + /// + /// + public ManagementClass(ManagementScope scope, ManagementPath path, ObjectGetOptions options) + : base (scope, path, options) {} + + /// + /// Initializes a new instance of the class for the specified WMI class, in the + /// specified scope, and with the specified options. + /// + /// The scope in which the WMI class resides. + /// The path to the WMI class within the specified scope. + /// An that specifies the options to use when retrieving the WMI class. + /// + /// The path can be specified as a full + /// path (including server and namespace). However, if a scope is specified, it will + /// override the first portion of the full path. + /// + /// + /// ManagementClass c = new ManagementClass("\\\\MyBox\\root\\cimv2", + /// "Win32_Environment", + /// new ObjectGetOptions(null, true)); + /// + /// Dim c As New ManagementClass("\\MyBox\root\cimv2", _ + /// "Win32_Environment", _ + /// new ObjectGetOptions(Null, True)) + /// + /// + public ManagementClass(string scope, string path, ObjectGetOptions options) + : base (new ManagementScope(scope), new ManagementPath(path), options) {} + + protected ManagementClass(SerializationInfo info, StreamingContext context) : base(info, context) + { + throw new PlatformNotSupportedException(); + } + + /// + /// Gets or sets the path of the WMI class to + /// which the + /// object is bound. + /// + /// + /// The path of the object's class. + /// + /// + /// When the property is set to a new value, + /// the + /// object will be + /// disconnected from any previously-bound WMI class. Reconnect to the new WMI class path. + /// + /// + /// ManagementClass c = new ManagementClass(); + /// c.Path = "Win32_Environment"; + /// + /// Dim c As New ManagementClass() + /// c.Path = "Win32_Environment" + /// + /// + public override ManagementPath Path + { + get + { + return base.Path; + } + set + { + // This must be a class path or empty (don't allow instance paths) + if ((null == value) || value.IsClass || value.IsEmpty) + base.Path = value; + else + throw new ArgumentOutOfRangeException("value"); + } + } + + /// + /// Gets or sets an array containing all WMI classes in the + /// inheritance hierarchy from this class to the top. + /// + /// + /// A string collection containing the + /// names of all WMI classes in the inheritance hierarchy of this class. + /// + /// + /// This property is read-only. + /// + /// + /// ManagementClass c = new ManagementClass("Win32_LogicalDisk"); + /// foreach (string s in c.Derivation) + /// Console.WriteLine("Further derived from : ", s); + /// + /// Dim c As New ManagementClass("Win32_LogicalDisk") + /// Dim s As String + /// For Each s In c.Derivation + /// Console.WriteLine("Further derived from : " & s) + /// Next s + /// + /// + public StringCollection Derivation + { + get + { + StringCollection result = new StringCollection(); + + int dummy1 = 0, dummy2 = 0; + object val = null; + + int status = wbemObject.Get_("__DERIVATION", 0, ref val, ref dummy1, ref dummy2); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + if (null != val) + result.AddRange((String [])val); + + return result; + } + } + + + /// + /// Gets or sets a collection of objects that + /// represent the methods defined in the WMI class. + /// + /// + /// A representing the methods defined in the WMI class. + /// + /// + /// ManagementClass c = new ManagementClass("Win32_Process"); + /// foreach (Method m in c.Methods) + /// Console.WriteLine("This class contains this method : ", m.Name); + /// + /// Dim c As New ManagementClass("Win32_Process") + /// Dim m As Method + /// For Each m in c.Methods + /// Console.WriteLine("This class contains this method : " & m.Name) + /// + /// + public MethodDataCollection Methods + { + get + { + Initialize ( true ) ; + + if (methods == null) + methods = new MethodDataCollection(this); + + return methods; + } + } + + // + //Methods + // + + //****************************************************** + //GetInstances + //****************************************************** + /// + /// Returns the collection of + /// all instances of the class. + /// + /// + /// Returns the collection of all instances of the class. + /// + /// + /// A collection of the objects + /// representing the instances of the class. + /// + /// + /// ManagementClass c = new ManagementClass("Win32_Process"); + /// foreach (ManagementObject o in c.GetInstances()) + /// Console.WriteLine("Next instance of Win32_Process : ", o.Path); + /// + /// Dim c As New ManagementClass("Win32_Process") + /// Dim o As ManagementObject + /// For Each o In c.GetInstances() + /// Console.WriteLine("Next instance of Win32_Process : " & o.Path) + /// Next o + /// + /// + public ManagementObjectCollection GetInstances() + { + return GetInstances((EnumerationOptions)null); + } + + + /// + /// Returns the collection of all instances of the class using the specified options. + /// + /// The additional operation options. + /// + /// A collection of the objects + /// representing the instances of the class, according to the specified options. + /// + /// + /// EnumerationOptions opt = new EnumerationOptions(); + /// //Will enumerate instances of the given class and any subclasses. + /// o.enumerateDeep = true; + /// ManagementClass c = new ManagementClass("CIM_Service"); + /// foreach (ManagementObject o in c.GetInstances(opt)) + /// Console.WriteLine(o["Name"]); + /// + /// Dim opt As New EnumerationOptions() + /// 'Will enumerate instances of the given class and any subclasses. + /// o.enumerateDeep = True + /// Dim c As New ManagementClass("CIM_Service") + /// Dim o As ManagementObject + /// For Each o In c.GetInstances(opt) + /// Console.WriteLine(o["Name"]) + /// Next o + /// + /// + public ManagementObjectCollection GetInstances(EnumerationOptions options) + { + if ((null == Path) || (null == Path.Path) || (0 == Path.Path.Length)) + throw new InvalidOperationException(); + + Initialize ( false ); + IEnumWbemClassObject enumWbem = null; + + EnumerationOptions o = (null == options) ? new EnumerationOptions() : (EnumerationOptions)options.Clone(); + //Need to make sure that we're not passing invalid flags to enumeration APIs. + //The only flags in EnumerationOptions not valid for enumerations are EnsureLocatable & PrototypeOnly. + o.EnsureLocatable = false; o.PrototypeOnly = false; + + SecurityHandler securityHandler = null; + int status = (int)ManagementStatus.NoError; + + try + { + securityHandler = Scope.GetSecurityHandler(); + status = scope.GetSecuredIWbemServicesHandler(Scope.GetIWbemServices() ).CreateInstanceEnum_(ClassName, + o.Flags, + o.GetContext(), + ref enumWbem + ); + } + finally + { + if (securityHandler != null) + securityHandler.Reset(); + } + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + return new ManagementObjectCollection(Scope, o, enumWbem); + } + + /// + /// Returns the collection of all instances of the class, asynchronously. + /// + /// The object to handle the asynchronous operation's progress. + /// + /// ManagementClass c = new ManagementClass("Win32_Share"); + /// MyHandler h = new MyHandler(); + /// ManagementOperationObserver ob = new ManagementOperationObserver(); + /// ob.ObjectReady += new ObjectReadyEventHandler (h.NewObject); + /// ob.Completed += new CompletedEventHandler (h.Done); + /// + /// c.GetInstances(ob); + /// + /// while (!h.Completed) + /// System.Threading.Thread.Sleep (1000); + /// + /// //Here you can use the object + /// Console.WriteLine(o["SomeProperty"]); + /// + /// public class MyHandler + /// { + /// private bool completed = false; + /// + /// public void NewObject(object sender, ObjectReadyEventArgs e) { + /// Console.WriteLine("New result arrived !", ((ManagementObject)(e.NewObject))["Name"]); + /// } + /// + /// public void Done(object sender, CompletedEventArgs e) { + /// Console.WriteLine("async Get completed !"); + /// completed = true; + /// } + /// + /// public bool Completed { + /// get { + /// return completed; + /// } + /// } + /// } + /// + /// Dim c As New ManagementClass("Win32_Share") + /// Dim h As New MyHandler() + /// Dim ob As New ManagementOperationObserver() + /// ob.ObjectReady += New ObjectReadyEventHandler(h.NewObject) + /// ob.Completed += New CompletedEventHandler(h.Done) + /// + /// c.GetInstances(ob) + /// + /// While Not h.Completed + /// System.Threading.Thread.Sleep(1000) + /// End While + /// + /// 'Here you can use the object + /// Console.WriteLine(o("SomeProperty")) + /// + /// Public Class MyHandler + /// Private completed As Boolean = false + /// + /// Public Sub Done(sender As Object, e As EventArrivedEventArgs) + /// Console.WriteLine("async Get completed !") + /// completed = True + /// End Sub + /// + /// Public ReadOnly Property Completed() As Boolean + /// Get + /// Return completed + /// End Get + /// End Property + /// End Class + /// + /// + public void GetInstances(ManagementOperationObserver watcher) + { + GetInstances(watcher, (EnumerationOptions)null); + } + + + /// + /// Returns the collection of all instances of the class, asynchronously, using + /// the specified options. + /// + /// The object to handle the asynchronous operation's progress. + /// The specified additional options for getting the instances. + public void GetInstances(ManagementOperationObserver watcher, EnumerationOptions options) + { + if (null == watcher) + throw new ArgumentNullException("watcher"); + + if ((null == Path) || (null == Path.Path) || (0 == Path.Path.Length)) + throw new InvalidOperationException(); + + Initialize ( false ) ; + + EnumerationOptions o = (null == options) ? new EnumerationOptions() : (EnumerationOptions)options.Clone(); + + //Need to make sure that we're not passing invalid flags to enumeration APIs. + //The only flags in EnumerationOptions not valid for enumerations are EnsureLocatable & PrototypeOnly. + o.EnsureLocatable = false; o.PrototypeOnly = false; + + // Ensure we switch off ReturnImmediately as this is invalid for async calls + o.ReturnImmediately = false; + + // If someone has registered for progress, make sure we flag it + if (watcher.HaveListenersForProgress) + o.SendStatus = true; + + WmiEventSink sink = watcher.GetNewSink(Scope, o.Context); + + SecurityHandler securityHandler = null; + int status = (int)ManagementStatus.NoError; + + securityHandler = Scope.GetSecurityHandler(); + + status = scope.GetSecuredIWbemServicesHandler(Scope.GetIWbemServices() ).CreateInstanceEnumAsync_( + ClassName, + o.Flags, + o.GetContext(), + sink.Stub ); + + + if (securityHandler != null) + securityHandler.Reset(); + + if (status < 0) + { + watcher.RemoveSink(sink); + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + //****************************************************** + //GetSubclasses + //****************************************************** + /// + /// Returns the collection of + /// all derived classes for the class. + /// + /// + /// Returns the collection of all subclasses for the class. + /// + /// + /// A collection of the objects that + /// represent the subclasses of the WMI class. + /// + public ManagementObjectCollection GetSubclasses() + { + return GetSubclasses((EnumerationOptions)null); + } + + + /// + /// Retrieves the subclasses of the class using the specified + /// options. + /// + /// The specified additional options for retrieving subclasses of the class. + /// + /// A collection of the objects + /// representing the subclasses of the WMI class, according to the specified + /// options. + /// + /// + /// EnumerationOptions opt = new EnumerationOptions(); + /// + /// //Causes return of deep subclasses as opposed to only immediate ones. + /// opt.enumerateDeep = true; + /// + /// ManagementObjectCollection c = (new + /// ManagementClass("Win32_Share")).GetSubclasses(opt); + /// + /// Dim opt As New EnumerationOptions() + /// + /// 'Causes return of deep subclasses as opposed to only immediate ones. + /// opt.enumerateDeep = true + /// + /// Dim cls As New ManagementClass("Win32_Share") + /// Dim c As ManagementObjectCollection + /// + /// c = cls.GetSubClasses(opt) + /// + /// + public ManagementObjectCollection GetSubclasses(EnumerationOptions options) + { + if (null == Path) + throw new InvalidOperationException(); + + Initialize ( false ) ; + IEnumWbemClassObject enumWbem = null; + + EnumerationOptions o = (null == options) ? new EnumerationOptions() : (EnumerationOptions)options.Clone(); + //Need to make sure that we're not passing invalid flags to enumeration APIs. + //The only flags in EnumerationOptions not valid for enumerations are EnsureLocatable & PrototypeOnly. + o.EnsureLocatable = false; o.PrototypeOnly = false; + + SecurityHandler securityHandler = null; + int status = (int)ManagementStatus.NoError; + + try + { + securityHandler = Scope.GetSecurityHandler(); + status = scope.GetSecuredIWbemServicesHandler( Scope.GetIWbemServices() ).CreateClassEnum_(ClassName, + o.Flags, + o.GetContext(), + ref enumWbem); + } + finally + { + if (securityHandler != null) + securityHandler.Reset(); + } + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + return new ManagementObjectCollection(Scope, o, enumWbem); + } + + /// + /// Returns the collection of all classes derived from this class, asynchronously. + /// + /// The object to handle the asynchronous operation's progress. + public void GetSubclasses(ManagementOperationObserver watcher) + { + GetSubclasses(watcher, (EnumerationOptions)null); + } + + + /// + /// Retrieves all classes derived from this class, asynchronously, using the specified + /// options. + /// + /// The object to handle the asynchronous operation's progress. + /// The specified additional options to use in the derived class retrieval. + public void GetSubclasses(ManagementOperationObserver watcher, + EnumerationOptions options) + { + if (null == watcher) + throw new ArgumentNullException("watcher"); + + if (null == Path) + throw new InvalidOperationException(); + + Initialize ( false ) ; + + EnumerationOptions o = (null == options) ? new EnumerationOptions() : + (EnumerationOptions)options.Clone(); + + //Need to make sure that we're not passing invalid flags to enumeration APIs. + //The only flags in EnumerationOptions not valid for enumerations are EnsureLocatable & PrototypeOnly. + o.EnsureLocatable = false; o.PrototypeOnly = false; + + // Ensure we switch off ReturnImmediately as this is invalid for async calls + o.ReturnImmediately = false; + + // If someone has registered for progress, make sure we flag it + if (watcher.HaveListenersForProgress) + o.SendStatus = true; + + WmiEventSink sink = watcher.GetNewSink(Scope, o.Context); + + SecurityHandler securityHandler = null; + int status = (int)ManagementStatus.NoError; + + securityHandler = Scope.GetSecurityHandler(); + + status = scope.GetSecuredIWbemServicesHandler(Scope.GetIWbemServices() ).CreateClassEnumAsync_(ClassName, + o.Flags, + o.GetContext(), + sink.Stub); + + + if (securityHandler != null) + securityHandler.Reset(); + + if (status < 0) + { + watcher.RemoveSink(sink); + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + //****************************************************** + //Derive + //****************************************************** + /// + /// Derives a new class from this class. + /// + /// The name of the new class to be derived. + /// + /// A new + /// that represents a new WMI class derived from the original class. + /// + /// + /// Note that the newly returned class has not been committed + /// until the () method is explicitly called. + /// + /// + /// ManagementClass existingClass = new ManagementClass("CIM_Service"); + /// ManagementClass newClass = existingClass.Derive("My_Service"); + /// newClass.Put(); //to commit the new class to the WMI repository. + /// + /// Dim existingClass As New ManagementClass("CIM_Service") + /// Dim newClass As ManagementClass + /// + /// newClass = existingClass.Derive("My_Service") + /// newClass.Put() 'to commit the new class to the WMI repository. + /// + /// + public ManagementClass Derive(string newClassName) + { + ManagementClass newClass = null; + + if (null == newClassName) + throw new ArgumentNullException("newClassName"); + else + { + // Check the path is valid + ManagementPath path = new ManagementPath(); + + try + { + path.ClassName = newClassName; + } + catch + { + throw new ArgumentOutOfRangeException("newClassName"); + } + + if (!path.IsClass) + throw new ArgumentOutOfRangeException("newClassName"); + } + + if (PutButNotGot) + { + Get(); + PutButNotGot = false; + } + + IWbemClassObjectFreeThreaded newWbemClass = null; + int status = this.wbemObject.SpawnDerivedClass_(0, out newWbemClass); + + if (status >= 0) + { + object val = newClassName; + status = newWbemClass.Put_("__CLASS", 0, ref val, 0); + + if (status >= 0) + newClass = ManagementClass.GetManagementClass(newWbemClass, this); + } + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + return newClass; + } + + //****************************************************** + //CreateInstance + //****************************************************** + /// + /// Creates a new instance of the WMI class. + /// + /// + /// A that represents a new + /// instance of the WMI class. + /// + /// + /// Note that the new instance is not committed until the + /// () method is called. Before committing it, the key properties must + /// be specified. + /// + /// + /// ManagementClass envClass = new ManagementClass("Win32_Environment"); + /// ManagementObject newInstance = + /// existingClass.CreateInstance("My_Service"); + /// newInstance["Name"] = "Cori"; + /// newInstance.Put(); //to commit the new instance. + /// + /// Dim envClass As New ManagementClass("Win32_Environment") + /// Dim newInstance As ManagementObject + /// + /// newInstance = existingClass.CreateInstance("My_Service") + /// newInstance("Name") = "Cori" + /// newInstance.Put() 'to commit the new instance. + /// + /// + public ManagementObject CreateInstance() + { + ManagementObject newInstance = null; + + if (PutButNotGot) + { + Get(); + PutButNotGot = false; + } + + IWbemClassObjectFreeThreaded newWbemInstance = null; + int status = this.wbemObject.SpawnInstance_(0, out newWbemInstance); + + if (status >= 0) + newInstance = ManagementObject.GetManagementObject(newWbemInstance, Scope); + else + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + return newInstance; + } + + /// + /// Returns a copy of the object. + /// + /// + /// The cloned + /// object. + /// + /// + /// Note that this does not create a copy of the + /// WMI class; only an additional representation is created. + /// + public override Object Clone() + { + IWbemClassObjectFreeThreaded theClone = null; + int status = wbemObject.Clone_(out theClone); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + return ManagementClass.GetManagementClass(theClone, this); + } + + + //****************************************************** + //GetRelatedClasses + //****************************************************** + /// + /// Retrieves classes related + /// to the WMI class. + /// + /// + /// Retrieves classes related to the WMI class. + /// + /// + /// A collection of the or + /// objects that represents WMI classes or instances related to + /// the WMI class. + /// + /// + /// The method queries the WMI schema for all + /// possible associations that the WMI class may have with other classes, or in rare + /// cases, to instances. + /// + /// + /// ManagementClass c = new ManagementClass("Win32_LogicalDisk"); + /// + /// foreach (ManagementClass r in c.GetRelatedClasses()) + /// Console.WriteLine("Instances of {0} may have + /// relationships to this class", r["__CLASS"]); + /// + /// Dim c As New ManagementClass("Win32_LogicalDisk") + /// Dim r As ManagementClass + /// + /// For Each r In c.GetRelatedClasses() + /// Console.WriteLine("Instances of {0} may have relationships _ + /// to this class", r("__CLASS")) + /// Next r + /// + /// + public ManagementObjectCollection GetRelatedClasses() + { + return GetRelatedClasses((string)null); + } + + /// + /// Retrieves classes related to the WMI class. + /// + /// The class from which resulting classes have to be derived. + /// + /// A collection of classes related to + /// this class. + /// + public ManagementObjectCollection GetRelatedClasses( + string relatedClass) + { + return GetRelatedClasses(relatedClass, null, null, null, null, null, null); + } + + + /// + /// Retrieves classes related to the WMI class based on the specified + /// options. + /// + /// The class from which resulting classes have to be derived. + /// The relationship type which resulting classes must have with the source class. + /// This qualifier must be present on the relationship. + /// This qualifier must be present on the resulting classes. + /// The resulting classes must have this role in the relationship. + /// The source class must have this role in the relationship. + /// The options for retrieving the resulting classes. + /// + /// A collection of classes related to + /// this class. + /// + public ManagementObjectCollection GetRelatedClasses( + string relatedClass, + string relationshipClass, + string relationshipQualifier, + string relatedQualifier, + string relatedRole, + string thisRole, + EnumerationOptions options) + { + if ((null == Path) || (null == Path.Path) || (0 == Path.Path.Length)) + throw new InvalidOperationException(); + + Initialize ( false ) ; + + IEnumWbemClassObject enumWbem = null; + + EnumerationOptions o = (null != options) ? (EnumerationOptions)options.Clone() : new EnumerationOptions(); + //Ensure EnumerateDeep flag bit is turned off as it's invalid for queries + o.EnumerateDeep = true; + + RelatedObjectQuery q = new RelatedObjectQuery(true, Path.Path, + relatedClass, + relationshipClass, + relatedQualifier, + relationshipQualifier, + relatedRole, thisRole); + + SecurityHandler securityHandler = null; + int status = (int)ManagementStatus.NoError; + + try + { + securityHandler = Scope.GetSecurityHandler(); + status = scope.GetSecuredIWbemServicesHandler(Scope.GetIWbemServices() ).ExecQuery_( + q.QueryLanguage, + q.QueryString, + o.Flags, + o.GetContext(), + ref enumWbem); + + } + finally + { + if (securityHandler != null) + securityHandler.Reset(); + } + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + //Create collection object + return new ManagementObjectCollection(Scope, o, enumWbem); + } + + + /// + /// Retrieves classes + /// related to the WMI class, asynchronously. + /// + /// The object to handle the asynchronous operation's progress. + public void GetRelatedClasses( + ManagementOperationObserver watcher) + { + GetRelatedClasses(watcher, (string)null); + } + + /// + /// Retrieves classes related to the WMI class, asynchronously, given the related + /// class name. + /// + /// The object to handle the asynchronous operation's progress. + /// The name of the related class. + public void GetRelatedClasses( + ManagementOperationObserver watcher, + string relatedClass) + { + GetRelatedClasses(watcher, relatedClass, null, null, null, null, null, null); + } + + + /// + /// Retrieves classes related to the + /// WMI class, asynchronously, using the specified options. + /// + /// Handler for progress and results of the asynchronous operation. + /// The class from which resulting classes have to be derived. + /// The relationship type which resulting classes must have with the source class. + /// This qualifier must be present on the relationship. + /// This qualifier must be present on the resulting classes. + /// The resulting classes must have this role in the relationship. + /// The source class must have this role in the relationship. + /// The options for retrieving the resulting classes. + public void GetRelatedClasses( + ManagementOperationObserver watcher, + string relatedClass, + string relationshipClass, + string relationshipQualifier, + string relatedQualifier, + string relatedRole, + string thisRole, + EnumerationOptions options) + { + if ((null == Path) || (null == Path.Path) || (0 == Path.Path.Length)) + throw new InvalidOperationException(); + + Initialize ( true ) ; + + if (null == watcher) + throw new ArgumentNullException("watcher"); + else + { + EnumerationOptions o = (null != options) + ? (EnumerationOptions)options.Clone() : new EnumerationOptions(); + + //Ensure EnumerateDeep flag bit is turned off as it's invalid for queries + o.EnumerateDeep = true; + + // Ensure we switch off ReturnImmediately as this is invalid for async calls + o.ReturnImmediately = false; + + // If someone has registered for progress, make sure we flag it + if (watcher.HaveListenersForProgress) + o.SendStatus = true; + + WmiEventSink sink = watcher.GetNewSink( + Scope, + o.Context); + + RelatedObjectQuery q = new RelatedObjectQuery(true, Path.Path, + relatedClass, relationshipClass, + relatedQualifier, relationshipQualifier, + relatedRole, thisRole); + + SecurityHandler securityHandler = null; + int status = (int)ManagementStatus.NoError; + + securityHandler = Scope.GetSecurityHandler(); + + status = scope.GetSecuredIWbemServicesHandler(Scope.GetIWbemServices() ).ExecQueryAsync_( + q.QueryLanguage, + q.QueryString, + o.Flags, + o.GetContext(), + sink.Stub); + + + if (securityHandler != null) + securityHandler.Reset(); + + if (status < 0) + { + watcher.RemoveSink(sink); + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + } + + //****************************************************** + //GetRelationshipClasses + //****************************************************** + /// + /// Retrieves relationship + /// classes that relate the class to others. + /// + /// + /// Retrieves relationship classes that relate the class to others. + /// + /// + /// A collection of association classes + /// that relate the class to any other class. + /// + public ManagementObjectCollection GetRelationshipClasses() + { + return GetRelationshipClasses((string)null); + } + + /// + /// Retrieves relationship classes that relate the class to others, where the + /// endpoint class is the specified class. + /// + /// The endpoint class for all relationship classes returned. + /// + /// A collection of association classes + /// that relate the class to the specified class. + /// + public ManagementObjectCollection GetRelationshipClasses( + string relationshipClass) + { + return GetRelationshipClasses(relationshipClass, null, null, null); + } + + + /// + /// Retrieves relationship classes that relate this class to others, according to + /// specified options. + /// + /// All resulting relationship classes must derive from this class. + /// Resulting relationship classes must have this qualifier. + /// The source class must have this role in the resulting relationship classes. + /// Specifies options for retrieving the results. + /// + /// A collection of association classes + /// that relate this class to others, according to the specified options. + /// + public ManagementObjectCollection GetRelationshipClasses( + string relationshipClass, + string relationshipQualifier, + string thisRole, + EnumerationOptions options) + { + if ((null == Path) || (null == Path.Path) || (0 == Path.Path.Length)) + throw new InvalidOperationException(); + + Initialize ( false ) ; + + IEnumWbemClassObject enumWbem = null; + + EnumerationOptions o = (null != options) ? options : new EnumerationOptions(); + //Ensure EnumerateDeep flag is turned off as it's invalid for queries + o.EnumerateDeep = true; + + + RelationshipQuery q = new RelationshipQuery(true, Path.Path, relationshipClass, + relationshipQualifier, thisRole); + + SecurityHandler securityHandler = null; + int status = (int)ManagementStatus.NoError; + + //Execute WMI query + try + { + securityHandler = Scope.GetSecurityHandler(); + status = scope.GetSecuredIWbemServicesHandler(Scope.GetIWbemServices() ).ExecQuery_( + q.QueryLanguage, + q.QueryString, + o.Flags, + o.GetContext(), + ref enumWbem); + + } + finally + { + if (securityHandler != null) + securityHandler.Reset(); + } + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + //Create collection object + return new ManagementObjectCollection(Scope, o, enumWbem); + } + + + /// + /// Retrieves relationship classes that relate the class to others, + /// asynchronously. + /// + /// The object to handle the asynchronous operation's progress. + public void GetRelationshipClasses( + ManagementOperationObserver watcher) + { + GetRelationshipClasses(watcher, (string)null); + } + + /// + /// Retrieves relationship classes that relate the class to the specified WMI class, + /// asynchronously. + /// + /// The object to handle the asynchronous operation's progress. + /// The WMI class to which all returned relationships should point. + public void GetRelationshipClasses( + ManagementOperationObserver watcher, + string relationshipClass) + { + GetRelationshipClasses(watcher, relationshipClass, null, null, null); + } + + + /// + /// Retrieves relationship classes that relate the class according to the specified + /// options, asynchronously. + /// + /// The handler for progress and results of the asynchronous operation. + /// The class from which all resulting relationship classes must derive. + /// The qualifier which the resulting relationship classes must have. + /// The role which the source class must have in the resulting relationship classes. + /// The options for retrieving the results. + /// + /// A collection of association classes + /// relating this class to others, according to the given options. + /// + public void GetRelationshipClasses( + ManagementOperationObserver watcher, + string relationshipClass, + string relationshipQualifier, + string thisRole, + EnumerationOptions options) + { + if ((null == Path) || (null == Path.Path) || (0 == Path.Path.Length)) + throw new InvalidOperationException(); + if (null == watcher) + throw new ArgumentNullException("watcher"); + else + { + Initialize ( true ) ; + + EnumerationOptions o = + (null != options) ? (EnumerationOptions)options.Clone() : new EnumerationOptions(); + + //Ensure EnumerateDeep flag is turned off as it's invalid for queries + o.EnumerateDeep = true; + + // Ensure we switch off ReturnImmediately as this is invalid for async calls + o.ReturnImmediately = false; + + // If someone has registered for progress, make sure we flag it + if (watcher.HaveListenersForProgress) + o.SendStatus = true; + + WmiEventSink sink = watcher.GetNewSink(Scope, o.Context); + + RelationshipQuery q = new RelationshipQuery(true, Path.Path, relationshipClass, + relationshipQualifier, thisRole); + + SecurityHandler securityHandler = null; + int status = (int)ManagementStatus.NoError; + + securityHandler = Scope.GetSecurityHandler(); + + status = scope.GetSecuredIWbemServicesHandler(Scope.GetIWbemServices()).ExecQueryAsync_( + q.QueryLanguage, + q.QueryString, + o.Flags, + o.GetContext(), + sink.Stub); + + + if (securityHandler != null) + securityHandler.Reset(); + + if (status < 0) + { + watcher.RemoveSink(sink); + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + } + + + /// + /// Generates a strongly-typed class for a given WMI class. + /// + /// + /// Generates a strongly-typed class for a given WMI class. + /// + /// if the class for managing system properties must be included; otherwise, . + /// if the generated class will manage system properties; otherwise, . + /// + /// A instance + /// representing the declaration for the strongly-typed class. + /// + /// + /// using System; + /// using System.Management; + /// using System.CodeDom; + /// using System.IO; + /// using System.CodeDom.Compiler; + /// using Microsoft.CSharp; + /// + /// void GenerateCSharpCode() + /// { + /// string strFilePath = "C:\\temp\\LogicalDisk.cs"; + /// CodeTypeDeclaration ClsDom; + /// + /// ManagementClass cls1 = new ManagementClass(null,"Win32_LogicalDisk",null); + /// ClsDom = cls1.GetStronglyTypedClassCode(false,false); + /// + /// ICodeGenerator cg = (new CSharpCodeProvider()).CreateGenerator (); + /// CodeNamespace cn = new CodeNamespace("TestNamespace"); + /// + /// // Add any imports to the code + /// cn.Imports.Add (new CodeNamespaceImport("System")); + /// cn.Imports.Add (new CodeNamespaceImport("System.ComponentModel")); + /// cn.Imports.Add (new CodeNamespaceImport("System.Management")); + /// cn.Imports.Add(new CodeNamespaceImport("System.Collections")); + /// + /// // Add class to the namespace + /// cn.Types.Add (ClsDom); + /// + /// //Now create the filestream (output file) + /// TextWriter tw = new StreamWriter(new + /// FileStream (strFilePath,FileMode.Create)); + /// + /// // And write it to the file + /// cg.GenerateCodeFromNamespace (cn, tw, new CodeGeneratorOptions()); + /// + /// tw.Close(); + /// } + /// + /// + public CodeTypeDeclaration GetStronglyTypedClassCode(bool includeSystemClassInClassDef, bool systemPropertyClass) + { + // Ensure that the object is valid + Get(); + ManagementClassGenerator classGen = new ManagementClassGenerator(this); + return classGen.GenerateCode(includeSystemClassInClassDef,systemPropertyClass); + } + + + /// + /// Generates a strongly-typed class for a given WMI class. This function generates code for Visual Basic, + /// C#, or JScript, depending on the input parameters. + /// + /// The language of the code to be generated. + /// The path of the file where the code is to be written. + /// The .NET namespace into which the class should be generated. If this is empty, the namespace will be generated from the WMI namespace. + /// + /// , if the method succeeded; + /// otherwise, . + /// + /// + /// using System; + /// using System.Management; + /// + /// ManagementClass cls = new ManagementClass(null,"Win32_LogicalDisk",null,""); + /// cls.GetStronglyTypedClassCode(CodeLanguage.CSharp,"C:\temp\Logicaldisk.cs",String.Empty); + /// + /// + public bool GetStronglyTypedClassCode(CodeLanguage lang, String filePath,String classNamespace) + { + // Ensure that the object is valid + Get(); + ManagementClassGenerator classGen = new ManagementClassGenerator(this); + return classGen.GenerateCode(lang , filePath,classNamespace); + } + + }//ManagementClass +} diff --git a/external/corefx/src/System.Management/src/System/Management/ManagementDateTime.cs b/external/corefx/src/System.Management/src/System/Management/ManagementDateTime.cs new file mode 100644 index 0000000000..d4f60011bf --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/ManagementDateTime.cs @@ -0,0 +1,467 @@ +// 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.Globalization; + +namespace System.Management +{ + /// + /// Provides methods to convert DMTF datetime and time interval to CLR compliant + /// and format and vice versa. + /// + /// + /// + /// + /// using System; + /// using System.Management; + /// + /// // The sample below demonstrates the various conversions that can be done using ManagementDateTimeConverter class + /// class Sample_ManagementDateTimeConverterClass + /// { + /// public static int Main(string[] args) + /// { + /// string dmtfDate = "20020408141835.999999-420"; + /// string dmtfTimeInterval = "00000010122532:123456:000"; + /// + /// // Converting DMTF datetime to System.DateTime + /// DateTime dt = ManagementDateTimeConverter.ToDateTime(dmtfDate); + /// + /// // Converting System.DateTime to DMTF datetime + /// string dmtfDate = ManagementDateTimeConverter.ToDateTime(DateTime.Now); + /// + /// // Converting DMTF timeinterval to System.TimeSpan + /// System.TimeSpan tsRet = ManagementDateTimeConverter. ToTimeSpan(dmtfTimeInterval); + /// + /// //Converting System.TimeSpan to DMTF time interval format + /// System.TimeSpan ts = new System.TimeSpan(10,12,25,32,456); + /// string dmtfTimeInt = ManagementDateTimeConverter.ToDmtfTimeInterval(ts); + /// + /// return 0; + /// + /// } + /// } + /// + /// + /// Imports System + /// Imports System.Management + /// + /// 'The sample below demonstrates the various conversions that can be done using ManagementDateTimeConverter class + /// Class Sample_ManagementClass + /// Overloads Public Shared Function Main(args() As String) As Integer + /// Dim dmtfDate As String = "20020408141835.999999-420" + /// Dim dmtfTimeInterval As String = "00000010122532:123456:000" + /// + /// 'Converting DMTF datetime and intervals to System.DateTime + /// Dim dt As DateTime = ManagementDateTimeConverter.ToDateTime(dmtfDate) + /// + /// 'Converting System.DateTime to DMTF datetime + /// dmtfDate = ManagementDateTimeConverter.ToDateTime(DateTime.Now) + /// + /// ' Converting DMTF timeinterval to System.TimeSpan + /// Dim tsRet As System.TimeSpan = ManagementDateTimeConverter.ToTimeSpan(dmtfTimeInterval) + /// + /// 'Converting System.TimeSpan to DMTF time interval format + /// Dim ts As System.TimeSpan = New System.TimeSpan(10, 12, 25, 32, 456) + /// String dmtfTimeInt = ManagementDateTimeConverter.ToDmtfTimeInterval(ts) + /// + /// Return 0 + /// End Function + /// End Class + /// + /// + /// + public sealed class ManagementDateTimeConverter + { + // constants + private const int SIZEOFDMTFDATETIME = 25; + private const int MAXSIZE_UTC_DMTF = 999; + private const long MAXDATE_INTIMESPAN = 99999999; + + private ManagementDateTimeConverter() + { + } + + + /// + /// Converts a given DMTF datetime to object. The returned DateTime will be in the + /// current TimeZone of the system. + /// + /// A string representing the datetime in DMTF format. + /// + /// A object that represents the given DMTF datetime. + /// + /// + /// Date and time in WMI is represented in DMTF datetime format. This format is explained in WMI SDK documentation. + /// DMTF datetime string has an UTC offset which this datetime string represents. + /// During conversion to , UTC offset is used to convert the date to the + /// current timezone. According to DMTF format a particular field can be represented by the character + /// '*'. This will be converted to the MinValue of this field that can be represented in . + /// + /// + /// + /// + /// // Convert a DMTF datetime to System.DateTime + /// DateTime date = ManagementDateTimeConverter.ToDateTime("20020408141835.999999-420"); + /// + /// + /// ' Convert a DMTF datetime to System.DateTime + /// Dim date as DateTime = ManagementDateTimeConverter.ToDateTime("20020408141835.999999-420") + /// + /// + public static DateTime ToDateTime(string dmtfDate) + { + int year = DateTime.MinValue.Year; + int month = DateTime.MinValue.Month; + int day = DateTime.MinValue.Day; + int hour = DateTime.MinValue.Hour; + int minute = DateTime.MinValue.Minute; + int second = DateTime.MinValue.Second; + int millisec = 0; + string dmtf = dmtfDate; + DateTime datetime = DateTime.MinValue; + + // If the string passed is empty or null then throw + // an exception + if(dmtf == null) + { + throw new System.ArgumentOutOfRangeException("dmtfDate"); + } + if (dmtf.Length == 0) + { + throw new System.ArgumentOutOfRangeException("dmtfDate"); + } + + // if the length of the string is not equal to the + // standard length of the DMTF datetime then throw an exception + if(dmtf.Length != SIZEOFDMTFDATETIME) + { + throw new System.ArgumentOutOfRangeException("dmtfDate"); + } + + IFormatProvider frmInt32 = (IFormatProvider)CultureInfo.InvariantCulture.GetFormat(typeof(System.Int32)); + System.Int64 ticks = 0; + try + { + + string tempString = System.String.Empty; + tempString = dmtf.Substring(0, 4); + if (("****" != tempString)) + { + year = System.Int32.Parse(tempString,frmInt32); + } + tempString = dmtf.Substring(4, 2); + if (("**" != tempString)) + { + month = System.Int32.Parse(tempString,frmInt32); + } + tempString = dmtf.Substring(6, 2); + if (("**" != tempString)) + { + day = System.Int32.Parse(tempString,frmInt32); + } + tempString = dmtf.Substring(8, 2); + if (("**" != tempString)) + { + hour = System.Int32.Parse(tempString,frmInt32); + } + tempString = dmtf.Substring(10, 2); + if (("**" != tempString)) + { + minute = System.Int32.Parse(tempString,frmInt32); + } + tempString = dmtf.Substring(12, 2); + if (("**" != tempString)) + { + second = System.Int32.Parse(tempString,frmInt32); + } + tempString = dmtf.Substring(15, 6); + if (("******" != tempString)) + { + ticks = (System.Int64.Parse(tempString,(IFormatProvider)CultureInfo.InvariantCulture.GetFormat(typeof(System.Int64)))) * (System.TimeSpan.TicksPerMillisecond/1000); + } + if( year < 0 || month < 0 || day < 0 || hour < 0 || minute < 0 || second < 0 || ticks < 0) + { + throw new System.ArgumentOutOfRangeException("dmtfDate"); + } + + } + catch + { + throw new System.ArgumentOutOfRangeException("dmtfDate"); + } + + + // Construct a new System.DateTime object + datetime = new System.DateTime(year, month, day, hour, minute, second, millisec); + // Then add the ticks calculated from the microseconds + datetime = datetime.AddTicks(ticks); + + // Adjust the UTC time to reflect the current zone time + System.TimeZone curZone = System.TimeZone.CurrentTimeZone; + System.TimeSpan tickOffset = curZone.GetUtcOffset(datetime); + long OffsetMins = tickOffset.Ticks / System.TimeSpan.TicksPerMinute; + + // Adjusting the DateTime for the current UTC + int UTCOffset = 0; + string tempString1 = dmtf.Substring(22, 3); + long OffsetToBeAdjusted = 0; + if (("***" != tempString1)) + { + tempString1 = dmtf.Substring(21, 4); + try + { + UTCOffset = System.Int32.Parse(tempString1,frmInt32); + } + catch + { + throw new System.ArgumentOutOfRangeException(); + } + + OffsetToBeAdjusted = UTCOffset-OffsetMins; + + // We have to substract the minutes from the time + datetime = datetime.AddMinutes(OffsetToBeAdjusted * -1); + + } + return datetime; + } + + /// + /// Converts a given object to DMTF format. + /// + /// + /// A object representing the datetime to be converted to DMTF datetime. + /// + /// A string that represents the DMTF datetime for the given DateTime object. + /// + /// + /// Date and time in WMI is represented in DMTF datetime format. This format is explained in WMI SDK documentation. + /// The DMTF datetime string represented will be with respect to the UTC offset of the + /// current timezone. The lowest precision in DMTF is microseconds and + /// in is Ticks , which is equivalent to 100 of nanoseconds. + /// During conversion these Ticks are converted to microseconds and rounded + /// off to the the nearest microsecond. + /// + /// + /// + /// + /// // Convert the current time in System.DateTime to DMTF format + /// string dmtfDateTime = ManagementDateTimeConverter.ToDmtfDateTime(DateTime.Now); + /// + /// + /// ' Convert the current time in System.DateTime to DMTF format + /// Dim dmtfDateTime as String = ManagementDateTimeConverter.ToDmtfDateTime(DateTime.Now) + /// + /// + public static string ToDmtfDateTime(DateTime date) + { + string UtcString = String.Empty; + // Fill up the UTC field in the DMTF date with the current + // zones UTC value + System.TimeZone curZone = System.TimeZone.CurrentTimeZone; + System.TimeSpan tickOffset = curZone.GetUtcOffset(date); + long OffsetMins = (tickOffset.Ticks / System.TimeSpan.TicksPerMinute); + IFormatProvider frmInt32 = (IFormatProvider)CultureInfo.InvariantCulture.GetFormat(typeof(System.Int32)); + + // If the offset is more than that what can be specified in DMTF format, then + // convert the date to UniversalTime + if(Math.Abs(OffsetMins) > MAXSIZE_UTC_DMTF) + { + date = date.ToUniversalTime(); + UtcString = "+000"; + } + else + if ((tickOffset.Ticks >= 0)) + { + UtcString = "+" + ((tickOffset.Ticks / System.TimeSpan.TicksPerMinute)).ToString(frmInt32).PadLeft(3,'0'); + } + else + { + string strTemp = OffsetMins.ToString(frmInt32); + UtcString = "-" + strTemp.Substring(1, strTemp.Length-1).PadLeft(3,'0'); + } + + string dmtfDateTime = date.Year.ToString(frmInt32).PadLeft(4,'0'); + + dmtfDateTime = (dmtfDateTime + date.Month.ToString(frmInt32).PadLeft(2, '0')); + dmtfDateTime = (dmtfDateTime + date.Day.ToString(frmInt32).PadLeft(2, '0')); + dmtfDateTime = (dmtfDateTime + date.Hour.ToString(frmInt32).PadLeft(2, '0')); + dmtfDateTime = (dmtfDateTime + date.Minute.ToString(frmInt32).PadLeft(2, '0')); + dmtfDateTime = (dmtfDateTime + date.Second.ToString(frmInt32).PadLeft(2, '0')); + dmtfDateTime = (dmtfDateTime + "."); + + // Construct a DateTime with with the precision to Second as same as the passed DateTime and so get + // the ticks difference so that the microseconds can be calculated + DateTime dtTemp = new DateTime(date.Year ,date.Month,date.Day ,date.Hour ,date.Minute ,date.Second,0); + System.Int64 microsec = ((date.Ticks-dtTemp.Ticks) * 1000) / System.TimeSpan.TicksPerMillisecond; + + // fill the microseconds field + String strMicrosec = microsec.ToString((IFormatProvider)CultureInfo.InvariantCulture.GetFormat(typeof(System.Int64))); + if(strMicrosec.Length > 6) + { + strMicrosec = strMicrosec.Substring(0,6); + } + dmtfDateTime = dmtfDateTime + strMicrosec.PadLeft(6,'0'); + // adding the UTC offset + dmtfDateTime = dmtfDateTime + UtcString; + + return dmtfDateTime; + } + /// + /// Converts a given DMTF time interval to object. + /// + /// A string represesentation of the DMTF time interval. + /// + /// A object that represents the given DMTF time interval. + /// + /// + /// Time interval in WMI is represented in DMTF format. This format is explained in WMI SDK documentation. + /// If the DMTF time interval value is more than that of + /// then is thrown. + /// + /// + /// + /// + /// // Convert a DMTF time interval to System.TimeSpan + /// TimeSpan dmtfTimeInterval = ManagementDateTimeConverter.ToTimeSpan("00000010122532:123456:000"); + /// + /// + /// ' Convert a DMTF time interval to System.TimeSpan + /// Dim ts as TimeSpan = ManagementDateTimeConverter.ToTimeSpan("00000010122532:123456:000") + /// + /// + public static TimeSpan ToTimeSpan(string dmtfTimespan) + { + int days = 0; + int hours = 0; + int minutes = 0; + int seconds = 0; + IFormatProvider frmInt32 = (IFormatProvider)CultureInfo.InvariantCulture.GetFormat(typeof(System.Int32)); + + string dmtfts = dmtfTimespan; + TimeSpan timespan = TimeSpan.MinValue; + + if (dmtfts == null) + { + throw new System.ArgumentOutOfRangeException("dmtfTimespan"); + } + if (dmtfts.Length == 0) + { + throw new System.ArgumentOutOfRangeException("dmtfTimespan"); + } + if(dmtfts.Length != SIZEOFDMTFDATETIME) + { + throw new System.ArgumentOutOfRangeException("dmtfTimespan"); + } + if(dmtfts.Substring(21,4) != ":000") + { + throw new System.ArgumentOutOfRangeException("dmtfTimespan"); + } + + System.Int64 ticks = 0; + try + { + string tempString = System.String.Empty; + + tempString = dmtfts.Substring(0, 8); + days = System.Int32.Parse(tempString,frmInt32); + + tempString = dmtfts.Substring(8, 2); + hours = System.Int32.Parse(tempString,frmInt32); + + tempString = dmtfts.Substring(10, 2); + minutes = System.Int32.Parse(tempString,frmInt32); + + tempString = dmtfts.Substring(12, 2); + seconds = System.Int32.Parse(tempString,frmInt32); + + tempString = dmtfts.Substring(15, 6); + ticks = (System.Int64.Parse(tempString,(IFormatProvider)CultureInfo.InvariantCulture.GetFormat(typeof(System.Int64)))) * (System.TimeSpan.TicksPerMillisecond/1000); + + } + catch + { + throw new System.ArgumentOutOfRangeException("dmtfTimespan"); + } + + if( days < 0 || hours < 0 || minutes < 0 || seconds < 0 || ticks < 0 ) + { + throw new System.ArgumentOutOfRangeException("dmtfTimespan"); + } + + timespan = new System.TimeSpan(days, hours, minutes, seconds, 0); + // Get a timepan for the additional ticks obtained for the microsecond part of DMTF time interval + // and then add it to the the original timespan + TimeSpan tsTemp = System.TimeSpan.FromTicks(ticks); + timespan = timespan + tsTemp; + + return timespan; + } + + /// + /// Converts a given object to DMTF time interval. + /// + /// A object representing the datetime to be converted to DMTF time interval. + /// + /// + /// A string that represents the DMTF time interval for the given TimeSpan object. + /// + /// + /// Time interval in WMI is represented in DMTF datetime format. This format + /// is explained in WMI SDK documentation. The lowest precision in + /// DMTF is microseconds and in is Ticks , which is equivalent + /// to 100 of nanoseconds.During conversion these Ticks are converted to + /// microseconds and rounded off to the the nearest microsecond. + /// + /// + /// + /// + /// // Construct a Timespan object and convert it to DMTF format + /// System.TimeSpan ts = new System.TimeSpan(10,12,25,32,456); + /// String dmtfTimeInterval = ManagementDateTimeConverter.ToDmtfTimeInterval(ts); + /// + /// + /// // Construct a Timespan object and convert it to DMTF format + /// Dim ts as System.TimeSpan = new System.TimeSpan(10,12,25,32,456) + /// Dim dmtfTimeInterval as String = ManagementDateTimeConverter.ToDmtfTimeInterval(ts) + /// + /// + public static string ToDmtfTimeInterval(TimeSpan timespan) + { + + string dmtftimespan = timespan.Days.ToString((IFormatProvider)CultureInfo.InvariantCulture.GetFormat(typeof(System.Int32))).PadLeft(8,'0'); + IFormatProvider frmInt32 = (IFormatProvider)CultureInfo.InvariantCulture.GetFormat(typeof(System.Int32)); + + // Days that can be represented is more than what can be represented + // then throw an exception + // and also negative timespan cannot be represented in DMTF + if(timespan.Days > MAXDATE_INTIMESPAN || timespan < TimeSpan.Zero) + { + throw new System.ArgumentOutOfRangeException(); + } + + dmtftimespan = (dmtftimespan + timespan.Hours.ToString(frmInt32).PadLeft(2, '0')); + dmtftimespan = (dmtftimespan + timespan.Minutes.ToString(frmInt32).PadLeft(2, '0')); + dmtftimespan = (dmtftimespan + timespan.Seconds.ToString(frmInt32).PadLeft(2, '0')); + dmtftimespan = (dmtftimespan + "."); + + // Construct a DateTime with with the precision to Second as same as the passed DateTime and so get + // the ticks difference so that the microseconds can be calculated + TimeSpan tsTemp = new TimeSpan(timespan.Days ,timespan.Hours,timespan.Minutes ,timespan.Seconds ,0); + System.Int64 microsec = ((timespan.Ticks-tsTemp.Ticks) * 1000) / System.TimeSpan.TicksPerMillisecond; + + // fill the microseconds field + String strMicrosec = microsec.ToString((IFormatProvider)CultureInfo.InvariantCulture.GetFormat(typeof(System.Int64))); + if(strMicrosec.Length > 6) + { + strMicrosec = strMicrosec.Substring(0,6); + } + dmtftimespan = dmtftimespan + strMicrosec.PadLeft(6,'0'); + + dmtftimespan = dmtftimespan + ":000"; + + return dmtftimespan; + } + } // ManagementDateTimeConverter +} diff --git a/external/corefx/src/System.Management/src/System/Management/ManagementEventArgs.cs b/external/corefx/src/System.Management/src/System/Management/ManagementEventArgs.cs new file mode 100644 index 0000000000..2f74cd8e91 --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/ManagementEventArgs.cs @@ -0,0 +1,319 @@ +// 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. + +namespace System.Management +{ + +internal class IdentifierChangedEventArgs : EventArgs +{ + internal IdentifierChangedEventArgs () {} +} + +internal class InternalObjectPutEventArgs : EventArgs +{ + private ManagementPath path; + + internal InternalObjectPutEventArgs (ManagementPath path) + { + this.path = path.Clone(); + } + + internal ManagementPath Path { + get { return path; } + } +} + + + /// + /// Represents the virtual base class to hold event data for WMI events. + /// +public abstract class ManagementEventArgs : EventArgs +{ + private object context; + + /// + /// Constructor. This is not callable directly by applications. + /// + /// The operation context which is echoed back + /// from the operation which trigerred the event. + internal ManagementEventArgs (object context) { + this.context = context; + } + + /// + /// Gets the operation context echoed back + /// from the operation that triggered the event. + /// + /// + /// A WMI context object containing + /// context information provided by the operation that triggered the event. + /// + public object Context { get { return context; } + } +} + +/// +/// Holds event data for the event. +/// +public class ObjectReadyEventArgs : ManagementEventArgs +{ + private ManagementBaseObject wmiObject; + + /// + /// Constructor. + /// + /// The operation context which is echoed back + /// from the operation which triggerred the event. + /// The newly arrived WmiObject. + internal ObjectReadyEventArgs ( + object context, + ManagementBaseObject wmiObject + ) : base (context) + { + this.wmiObject = wmiObject; + } + + /// + /// Gets the newly-returned object. + /// + /// + /// A representing the + /// newly-returned object. + /// + public ManagementBaseObject NewObject + { + get { + return wmiObject; + } + } +} + +/// +/// Holds event data for the event. +/// +public class CompletedEventArgs : ManagementEventArgs +{ + private readonly int status; + private readonly ManagementBaseObject wmiObject; + + /// + /// Constructor. + /// + /// The operation context which is echoed back + /// from the operation which trigerred the event. + /// The completion status of the operation. + /// Additional status information + /// encapsulated within a WmiObject. This may be null. + internal CompletedEventArgs ( + object context, + int status, + ManagementBaseObject wmiStatusObject + ) : base (context) + { + wmiObject = wmiStatusObject; + this.status = status; + } + + /// + /// Gets or sets additional status information + /// within a WMI object. This may be null. + /// + /// + /// if an error did not occur. Otherwise, may be non-null if the provider + /// supports extended error information. + /// + public ManagementBaseObject StatusObject + { + get { + return wmiObject; + } + } + + /// + /// Gets the completion status of the operation. + /// + /// + /// A value + /// indicating the return code of the operation. + /// + public ManagementStatus Status + { + get { + return (ManagementStatus) status; + } + } +} + +/// +/// Holds event data for the event. +/// +public class ObjectPutEventArgs : ManagementEventArgs +{ + private ManagementPath wmiPath; + + /// + /// Constructor + /// + /// The operation context which is echoed back + /// from the operation which trigerred the event. + /// The WmiPath representing the identity of the + /// object that has been put. + internal ObjectPutEventArgs ( + object context, + ManagementPath path + ) : base (context) + { + wmiPath = path; + } + + /// + /// Gets the identity of the + /// object that has been put. + /// + /// + /// A containing the path of the object that has + /// been put. + /// + public ManagementPath Path + { + get { + return wmiPath; + } + } +} + +/// +/// Holds event data for the event. +/// +public class ProgressEventArgs : ManagementEventArgs +{ + private int upperBound; + private int current; + private string message; + + /// + /// Constructor + /// + /// The operation context which is echoed back + /// from the operation which trigerred the event. + /// A quantity representing the total + /// amount of work required to be done by the operation. + /// A quantity representing the current + /// amount of work required to be done by the operation. This is + /// always less than or equal to upperBound. + /// Optional additional information regarding + /// operation progress. + internal ProgressEventArgs ( + object context, + int upperBound, + int current, + string message + ) : base (context) + { + this.upperBound = upperBound; + this.current = current; + this.message = message; + } + + /// + /// Gets the total + /// amount of work required to be done by the operation. + /// + /// + /// An integer representing the total + /// amount of work for the operation. + /// + public int UpperBound + { + get { + return upperBound; + } + } + + /// + /// Gets the current amount of work + /// done by the operation. This is always less than or equal to . + /// + /// + /// An integer representing the current amount of work + /// already completed by the operation. + /// + public int Current + { + get { + return current; + } + } + + /// + /// Gets or sets optional additional information regarding the operation's progress. + /// + /// + /// A string containing additional + /// information regarding the operation's progress. + /// + public string Message + { + get { + return (null != message) ? message : String.Empty; + } + } +} + +/// +/// Holds event data for the event. +/// +public class EventArrivedEventArgs : ManagementEventArgs +{ + private ManagementBaseObject eventObject; + + internal EventArrivedEventArgs ( + object context, + ManagementBaseObject eventObject) : base (context) + { + this.eventObject = eventObject; + } + + /// + /// Gets the WMI event that was delivered. + /// + /// + /// The object representing the WMI event. + /// + public ManagementBaseObject NewEvent + { + get { return this.eventObject; } + } +} + +/// +/// Holds event data for the event. +/// +public class StoppedEventArgs : ManagementEventArgs +{ + private int status; + + internal StoppedEventArgs ( + object context, + int status) : base (context) + { + this.status = status; + } + + /// + /// Gets the completion status of the operation. + /// + /// + /// A value representing the status of the + /// operation. + /// + public ManagementStatus Status + { + get { + return (ManagementStatus) status; + } + } +} + +} diff --git a/external/corefx/src/System.Management/src/System/Management/ManagementEventWatcher.cs b/external/corefx/src/System.Management/src/System/Management/ManagementEventWatcher.cs new file mode 100644 index 0000000000..01a81bd9f7 --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/ManagementEventWatcher.cs @@ -0,0 +1,777 @@ +// 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.Runtime.InteropServices; +using System.ComponentModel; +using System.Threading; + +namespace System.Management +{ + /// + /// Represents the method that will handle the event. + /// + public delegate void EventArrivedEventHandler(object sender, EventArrivedEventArgs e); + + /// + /// Represents the method that will handle the event. + /// + public delegate void StoppedEventHandler (object sender, StoppedEventArgs e); + + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + /// + /// Subscribes to temporary event notifications + /// based on a specified event query. + /// + /// + /// using System; + /// using System.Management; + /// + /// // This example demonstrates how to subscribe to an event using the ManagementEventWatcher object. + /// class Sample_ManagementEventWatcher + /// { + /// public static int Main(string[] args) { + /// + /// //For the example, we'll put a class into the repository, and watch + /// //for class deletion events when the class is deleted. + /// ManagementClass newClass = new ManagementClass(); + /// newClass["__CLASS"] = "TestDeletionClass"; + /// newClass.Put(); + /// + /// //Set up an event watcher and a handler for the event + /// ManagementEventWatcher watcher = new ManagementEventWatcher( + /// new WqlEventQuery("__ClassDeletionEvent")); + /// MyHandler handler = new MyHandler(); + /// watcher.EventArrived += new EventArrivedEventHandler(handler.Arrived); + /// + /// //Start watching for events + /// watcher.Start(); + /// + /// // For the purpose of this sample, we delete the class to trigger the event + /// // and wait for two seconds before terminating the consumer + /// newClass.Delete(); + /// + /// System.Threading.Thread.Sleep(2000); + /// + /// //Stop watching + /// watcher.Stop(); + /// + /// return 0; + /// } + /// + /// public class MyHandler { + /// public void Arrived(object sender, EventArrivedEventArgs e) { + /// Console.WriteLine("Class Deleted = " + + /// ((ManagementBaseObject)e.NewEvent["TargetClass"])["__CLASS"]); + /// } + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// ' This example demonstrates how to subscribe an event using the ManagementEventWatcher object. + /// Class Sample_ManagementEventWatcher + /// Public Shared Sub Main() + /// + /// ' For the example, we'll put a class into the repository, and watch + /// ' for class deletion events when the class is deleted. + /// Dim newClass As New ManagementClass() + /// newClass("__CLASS") = "TestDeletionClass" + /// newClass.Put() + /// + /// ' Set up an event watcher and a handler for the event + /// Dim watcher As _ + /// New ManagementEventWatcher(New WqlEventQuery("__ClassDeletionEvent")) + /// Dim handler As New MyHandler() + /// AddHandler watcher.EventArrived, AddressOf handler.Arrived + /// + /// ' Start watching for events + /// watcher.Start() + /// + /// ' For the purpose of this sample, we delete the class to trigger the event + /// ' and wait for two seconds before terminating the consumer + /// newClass.Delete() + /// + /// System.Threading.Thread.Sleep(2000) + /// + /// ' Stop watching + /// watcher.Stop() + /// + /// End Sub + /// + /// Public Class MyHandler + /// Public Sub Arrived(sender As Object, e As EventArrivedEventArgs) + /// Console.WriteLine("Class Deleted = " & _ + /// CType(e.NewEvent("TargetClass"), ManagementBaseObject)("__CLASS")) + /// End Sub + /// End Class + /// End Class + /// + /// + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + [ToolboxItem(false)] + public class ManagementEventWatcher : Component + { + //fields + private ManagementScope scope; + private EventQuery query; + private EventWatcherOptions options; + private IEnumWbemClassObject enumWbem; + private IWbemClassObjectFreeThreaded[] cachedObjects; //points to objects currently available in cache + private uint cachedCount; //says how many objects are in the cache (when using BlockSize option) + private uint cacheIndex; //used to walk the cache + private SinkForEventQuery sink; // the sink implementation for event queries + private WmiDelegateInvoker delegateInvoker; + + //Called when IdentifierChanged() event fires + private void HandleIdentifierChange(object sender, + IdentifierChangedEventArgs e) + { + // Invalidate any sync or async call in progress + Stop(); + } + + //default constructor + /// + /// Initializes a new instance of the class. + /// + /// + /// Initializes a new instance of the class. For further + /// initialization, set the properties on the object. This is the default constructor. + /// + public ManagementEventWatcher() : this((ManagementScope)null, null, null) {} + + //parameterized constructors + /// + /// Initializes a new instance of the class when given a WMI event query. + /// + /// An object representing a WMI event query, which determines the events for which the watcher will listen. + /// + /// The namespace in which the watcher will be listening for + /// events is the default namespace that is currently set. + /// + public ManagementEventWatcher ( + EventQuery query) : this(null, query, null) {} + + /// + /// Initializes a new instance of the class when given a WMI event query in the + /// form of a string. + /// + /// A WMI event query, which defines the events for which the watcher will listen. + /// + /// The namespace in which the watcher will be listening for + /// events is the default namespace that is currently set. + /// + public ManagementEventWatcher ( + string query) : this(null, new EventQuery(query), null) {} + + /// + /// Initializes a new instance of the + /// class that listens for events conforming to the given WMI event query. + /// + /// A object representing the scope (namespace) in which the watcher will listen for events. + /// An object representing a WMI event query, which determines the events for which the watcher will listen. + public ManagementEventWatcher( + ManagementScope scope, + EventQuery query) : this(scope, query, null) {} + + /// + /// Initializes a new instance of the + /// class that listens for events conforming to the given WMI event query. For this + /// variant, the query and the scope are specified as strings. + /// + /// The management scope (namespace) in which the watcher will listen for events. + /// The query that defines the events for which the watcher will listen. + public ManagementEventWatcher( + string scope, + string query) : this(new ManagementScope(scope), new EventQuery(query), null) {} + + /// + /// Initializes a new instance of the class that listens for + /// events conforming to the given WMI event query, according to the specified options. For + /// this variant, the query and the scope are specified as strings. The options + /// object can specify options such as a timeout and context information. + /// + /// The management scope (namespace) in which the watcher will listen for events. + /// The query that defines the events for which the watcher will listen. + /// An object representing additional options used to watch for events. + public ManagementEventWatcher( + string scope, + string query, + EventWatcherOptions options) : this(new ManagementScope(scope), new EventQuery(query), options) {} + + /// + /// Initializes a new instance of the class + /// that listens for events conforming to the given WMI event query, according to the specified + /// options. For this variant, the query and the scope are specified objects. The + /// options object can specify options such as timeout and context information. + /// + /// A object representing the scope (namespace) in which the watcher will listen for events. + /// An object representing a WMI event query, which determines the events for which the watcher will listen. + /// An object representing additional options used to watch for events. + public ManagementEventWatcher( + ManagementScope scope, + EventQuery query, + EventWatcherOptions options) + { + if (null != scope) + this.scope = ManagementScope._Clone(scope, new IdentifierChangedEventHandler(HandleIdentifierChange)); + else + this.scope = ManagementScope._Clone(null, new IdentifierChangedEventHandler(HandleIdentifierChange)); + + if (null != query) + this.query = (EventQuery)query.Clone(); + else + this.query = new EventQuery(); + this.query.IdentifierChanged += new IdentifierChangedEventHandler(HandleIdentifierChange); + + if (null != options) + this.options = (EventWatcherOptions)options.Clone(); + else + this.options = new EventWatcherOptions(); + this.options.IdentifierChanged += new IdentifierChangedEventHandler(HandleIdentifierChange); + + enumWbem = null; + cachedCount = 0; + cacheIndex = 0; + sink = null; + delegateInvoker = new WmiDelegateInvoker (this); + } + + /// + /// Ensures that outstanding calls are cleared. This is the destructor for the object. + /// + ~ManagementEventWatcher () + { + // Ensure any outstanding calls are cleared + Stop (); + + if (null != scope) + scope.IdentifierChanged -= new IdentifierChangedEventHandler (HandleIdentifierChange); + + if (null != options) + options.IdentifierChanged -= new IdentifierChangedEventHandler (HandleIdentifierChange); + + if (null != query) + query.IdentifierChanged -= new IdentifierChangedEventHandler (HandleIdentifierChange); + } + + // + // Events + // + + /// + /// Occurs when a new event arrives. + /// + public event EventArrivedEventHandler EventArrived; + + /// + /// Occurs when a subscription is canceled. + /// + public event StoppedEventHandler Stopped; + + // + //Public Properties + // + + /// + /// Gets or sets the scope in which to watch for events (namespace or scope). + /// + /// + /// The scope in which to watch for events (namespace or scope). + /// + public ManagementScope Scope + { + get + { + return scope; + } + set + { + if (null != value) + { + ManagementScope oldScope = scope; + scope = (ManagementScope)value.Clone (); + + // Unregister ourselves from the previous scope object + if (null != oldScope) + oldScope.IdentifierChanged -= new IdentifierChangedEventHandler(HandleIdentifierChange); + + //register for change events in this object + scope.IdentifierChanged += new IdentifierChangedEventHandler(HandleIdentifierChange); + //the scope property has changed so act like we fired the event + HandleIdentifierChange(this, null); + } + else + throw new ArgumentNullException("value"); + } + } + + /// + /// Gets or sets the criteria to apply to events. + /// + /// + /// The criteria to apply to the events, which is equal to the event query. + /// + public EventQuery Query + { + get + { + return query; + } + set + { + if (null != value) + { + ManagementQuery oldQuery = query; + query = (EventQuery)value.Clone (); + + // Unregister ourselves from the previous query object + if (null != oldQuery) + oldQuery.IdentifierChanged -= new IdentifierChangedEventHandler(HandleIdentifierChange); + + //register for change events in this object + query.IdentifierChanged += new IdentifierChangedEventHandler(HandleIdentifierChange); + //the query property has changed so act like we fired the event + HandleIdentifierChange(this, null); + } + else + throw new ArgumentNullException("value"); + } + } + + /// + /// Gets or sets the options used to watch for events. + /// + /// + /// The options used to watch for events. + /// + public EventWatcherOptions Options + { + get + { + return options; + } + set + { + if (null != value) + { + EventWatcherOptions oldOptions = options; + options = (EventWatcherOptions)value.Clone (); + + // Unregister ourselves from the previous scope object + if (null != oldOptions) + oldOptions.IdentifierChanged -= new IdentifierChangedEventHandler(HandleIdentifierChange); + + cachedObjects = new IWbemClassObjectFreeThreaded[options.BlockSize]; + //register for change events in this object + options.IdentifierChanged += new IdentifierChangedEventHandler(HandleIdentifierChange); + //the options property has changed so act like we fired the event + HandleIdentifierChange(this, null); + } + else + throw new ArgumentNullException("value"); + } + } + + /// + /// Waits for the next event that matches the specified query to arrive, and + /// then returns it. + /// + /// + /// A representing the + /// newly arrived event. + /// + /// + /// If the event watcher object contains options with + /// a specified timeout, the API will wait for the next event only for the specified + /// amount of time; otherwise, the API will be blocked until the next event occurs. + /// + public ManagementBaseObject WaitForNextEvent() + { + ManagementBaseObject obj = null; + + Initialize (); + +#pragma warning disable CA2002 + lock(this) +#pragma warning restore CA2002 + { + SecurityHandler securityHandler = Scope.GetSecurityHandler(); + + int status = (int)ManagementStatus.NoError; + + try + { + if (null == enumWbem) //don't have an enumerator yet - get it + { + //Execute the query + status = scope.GetSecuredIWbemServicesHandler( Scope.GetIWbemServices() ).ExecNotificationQuery_( + query.QueryLanguage, + query.QueryString, + options.Flags, + options.GetContext (), + ref enumWbem); + } + + if (status >= 0) + { + if ((cachedCount - cacheIndex) == 0) //cache is empty - need to get more objects + { + //Because Interop doesn't support custom marshalling for arrays, we have to use + //the "DoNotMarshal" objects in the interop and then convert to the "FreeThreaded" + //counterparts afterwards. + IWbemClassObject_DoNotMarshal[] tempArray = new IWbemClassObject_DoNotMarshal[options.BlockSize]; + + int timeout = (ManagementOptions.InfiniteTimeout == options.Timeout) + ? (int) tag_WBEM_TIMEOUT_TYPE.WBEM_INFINITE : + (int) options.Timeout.TotalMilliseconds; + + status = scope.GetSecuredIEnumWbemClassObjectHandler(enumWbem).Next_(timeout, (uint)options.BlockSize, tempArray, ref cachedCount); + cacheIndex = 0; + + if (status >= 0) + { + //Convert results and put them in cache. Note that we may have timed out + //in which case we might not have all the objects. If no object can be returned + //we throw a timeout exception. + if (cachedCount == 0) + ManagementException.ThrowWithExtendedInfo(ManagementStatus.Timedout); + + for (int i = 0; i < cachedCount; i++) + cachedObjects[i] = new IWbemClassObjectFreeThreaded(Marshal.GetIUnknownForObject(tempArray[i])); + } + } + + if (status >= 0) + { + obj = new ManagementBaseObject(cachedObjects[cacheIndex]); + cacheIndex++; + } + } + } + finally + { + securityHandler.Reset(); + } + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + return obj; + } + + + //******************************************** + //Start + //******************************************** + /// + /// Subscribes to events with the given query and delivers + /// them, asynchronously, through the event. + /// + public void Start() + { + Initialize (); + + // Cancel any current event query + Stop (); + + // Submit a new query + SecurityHandler securityHandler = Scope.GetSecurityHandler(); + IWbemServices wbemServices = scope.GetIWbemServices(); + + try + { + sink = new SinkForEventQuery(this, options.Context, wbemServices); + if (sink.Status < 0) + { + Marshal.ThrowExceptionForHR(sink.Status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + // For async event queries we should ensure 0 flags as this is + // the only legal value + int status = scope.GetSecuredIWbemServicesHandler(wbemServices).ExecNotificationQueryAsync_( + query.QueryLanguage, + query.QueryString, + 0, + options.GetContext(), + sink.Stub); + + + if (status < 0) + { + if (sink != null) + { + sink.ReleaseStub(); + sink = null; + } + + if ((status & 0xfffff000) == 0x80041000) + + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + finally + { + securityHandler.Reset(); + } + } + + //******************************************** + //Stop + //******************************************** + /// + /// Cancels the subscription whether it is synchronous or asynchronous. + /// + public void Stop() + { + //For semi-synchronous, release the WMI enumerator to cancel the subscription + if (null != enumWbem) + { + Marshal.ReleaseComObject(enumWbem); + enumWbem = null; + FireStopped (new StoppedEventArgs (options.Context, (int)ManagementStatus.OperationCanceled)); + } + + // In async mode cancel the call to the sink - this will + // unwind the operation and cause a Stopped message + if (null != sink) + { + sink.Cancel (); + sink = null; + } + } + + private void Initialize () + { + //If the query is not set yet we can't do it + if (null == query) + throw new InvalidOperationException(); + + if (null == options) + Options = new EventWatcherOptions (); + + //If we're not connected yet, this is the time to do it... +#pragma warning disable CA2002 + lock (this) +#pragma warning restore CA2002 + { + if (null == scope) + Scope = new ManagementScope (); + + if (null == cachedObjects) + cachedObjects = new IWbemClassObjectFreeThreaded[options.BlockSize]; + } + + lock (scope) + { + scope.Initialize (); + } + } + + + internal void FireStopped (StoppedEventArgs args) + { + try + { + delegateInvoker.FireEventToDelegates (Stopped, args); + } + catch + { + } + } + + internal void FireEventArrived (EventArrivedEventArgs args) + { + try + { + delegateInvoker.FireEventToDelegates (EventArrived, args); + } + catch + { + } + } + + + + } + + internal class SinkForEventQuery : IWmiEventSource + { + private ManagementEventWatcher eventWatcher; + private object context; + private IWbemServices services; + private IWbemObjectSink stub; // The secured IWbemObjectSink + private int status; + private bool isLocal; + + public int Status {get {return status;} set {status=value;}} + + public SinkForEventQuery (ManagementEventWatcher eventWatcher, + object context, + IWbemServices services) + { + this.services = services; + this.context = context; + this.eventWatcher = eventWatcher; + this.status = 0; + this.isLocal = false; + + // determine if the server is local, and if so don't create a real stub using unsecap + if((0==String.Compare(eventWatcher.Scope.Path.Server, ".", StringComparison.OrdinalIgnoreCase)) || + (0==String.Compare(eventWatcher.Scope.Path.Server, System.Environment.MachineName, StringComparison.OrdinalIgnoreCase))) + { + this.isLocal = true; + } + + if(MTAHelper.IsNoContextMTA()) + HackToCreateStubInMTA(this); + else + { + // + // Ensure we are able to trap exceptions from worker thread. + // + ThreadDispatch disp = new ThreadDispatch ( new ThreadDispatch.ThreadWorkerMethodWithParam ( HackToCreateStubInMTA ) ) ; + disp.Parameter = this ; + disp.Start ( ) ; + } + + } + + void HackToCreateStubInMTA(object param) + { + SinkForEventQuery obj = (SinkForEventQuery) param ; + object dmuxStub = null; + obj.Status = WmiNetUtilsHelper.GetDemultiplexedStub_f (obj, obj.isLocal, out dmuxStub); + obj.stub = (IWbemObjectSink) dmuxStub; + } + + internal IWbemObjectSink Stub + { + get { return stub; } + } + + public void Indicate(IntPtr pWbemClassObject) + { + Marshal.AddRef(pWbemClassObject); + IWbemClassObjectFreeThreaded obj = new IWbemClassObjectFreeThreaded(pWbemClassObject); + try + + { + EventArrivedEventArgs args = new EventArrivedEventArgs(context, new ManagementBaseObject(obj)); + + eventWatcher.FireEventArrived(args); + } + catch + { + } + } + + public void SetStatus ( + int flags, + int hResult, + String message, + IntPtr pErrObj) + { + try + { + // Fire Stopped event + eventWatcher.FireStopped(new StoppedEventArgs(context, hResult)); + + //This handles cases in which WMI calls SetStatus to indicate a problem, for example + //a queue overflow due to slow client processing. + //Currently we just cancel the subscription in this case. + if (hResult != (int)tag_WBEMSTATUS.WBEM_E_CALL_CANCELLED + && hResult != (int)tag_WBEMSTATUS.WBEM_S_OPERATION_CANCELLED) + ThreadPool.QueueUserWorkItem(new WaitCallback(Cancel2)); + } + catch + { + } + } + + // On Win2k, we get a deadlock if we do a Cancel within a SetStatus + // Instead of calling it from SetStatus, we use ThreadPool.QueueUserWorkItem + void Cancel2(object o) + { + // + // Try catch the call to cancel. In this case the cancel is being done without the client + // knowing about it so catching all exceptions is not a bad thing to do. If a client calls + // Stop (which calls Cancel), they will still recieve any exceptions that may have occured. + // + try + { + Cancel(); + } + catch + { + } + } + + internal void Cancel () + { + if (null != stub) + { +#pragma warning disable CA2002 + lock(this) +#pragma warning restore CA2002 + { + if (null != stub) + { + + int status = services.CancelAsyncCall_(stub); + + // Release prior to throwing an exception. + ReleaseStub(); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + } + } + } + + internal void ReleaseStub () + { + if (null != stub) + { +#pragma warning disable CA2002 + lock(this) +#pragma warning restore CA2002 + { + /* + * We force a release of the stub here so as to allow + * unsecapp.exe to die as soon as possible. + * however if it is local, unsecap won't be started + */ + if (null != stub) + { + try + { + System.Runtime.InteropServices.Marshal.ReleaseComObject(stub); + stub = null; + } + catch + { + } + } + } + } + } + } + +} diff --git a/external/corefx/src/System.Management/src/System/Management/ManagementException.cs b/external/corefx/src/System.Management/src/System/Management/ManagementException.cs new file mode 100644 index 0000000000..7c64de28e7 --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/ManagementException.cs @@ -0,0 +1,797 @@ +// 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.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Security.Permissions; + +namespace System.Management +{ + + /// + /// Represents the enumeration of all WMI error codes that are currently defined. + /// + public enum ManagementStatus + { + /// + /// The operation was successful. + /// + NoError = 0, + /// + /// This value is returned when no more objects + /// are available, the number of objects returned is less than the number requested, + /// or at the end of an enumeration. It is also returned when the method is called + /// with a value of 0 for the parameter. + /// + False = 1, + /// + /// An overridden property was deleted. This value is + /// returned to signal that the original, non-overridden value has been restored as a + /// result of the deletion. + /// + ResetToDefault = 0x40002, + /// + /// The compared items (such as objects and classes) + /// are not identical. + /// + Different = 0x40003, + /// + /// A call timed out. This is not an + /// error condition; therefore, some results may have been returned. + /// + Timedout = 0x40004, + /// + /// No more data is available from the enumeration; the + /// user should terminate the enumeration. + /// + NoMoreData = 0x40005, + /// + /// The operation was + /// canceled. + /// + OperationCanceled = 0x40006, + /// + /// A request is still in progress; however, the results are not + /// yet available. + /// + Pending = 0x40007, + /// + /// More than one copy of the same object was detected in + /// the result set of an enumeration. + /// + DuplicateObjects = 0x40008, + /// + /// The user did not receive all of the requested objects + /// because of inaccessible resources (other than security violations). + /// + PartialResults = 0x40010, + /// + /// The call failed. + /// + Failed = unchecked((int)0x80041001), + /// + /// The object could not be found. + /// + NotFound = unchecked((int)0x80041002), + /// + /// The current user does not have permission to perform the + /// action. + /// + AccessDenied = unchecked((int)0x80041003), + /// + /// The provider failed after + /// initialization. + /// + ProviderFailure = unchecked((int)0x80041004), + /// + /// A type mismatch occurred. + /// + TypeMismatch = unchecked((int)0x80041005), + /// + /// There was not enough memory for the operation. + /// + OutOfMemory = unchecked((int)0x80041006), + /// + /// The context object is not valid. + /// + InvalidContext = unchecked((int)0x80041007), + /// + /// One of the parameters to the call is not correct. + /// + /// + InvalidParameter = unchecked((int)0x80041008), + /// + /// The resource, typically a remote server, is not + /// currently available. + /// + NotAvailable = unchecked((int)0x80041009), + /// + /// An internal, critical, and unexpected error occurred. + /// Report this error to Microsoft Product Support Services. + /// + CriticalError = unchecked((int)0x8004100A), + /// + /// One or more network packets were corrupted during a remote session. + /// + InvalidStream = unchecked((int)0x8004100B), + /// + /// The feature or operation is not supported. + /// + NotSupported = unchecked((int)0x8004100C), + /// + /// The specified base class is not valid. + /// + InvalidSuperclass = unchecked((int)0x8004100D), + /// + /// The specified namespace could not be found. + /// + InvalidNamespace = unchecked((int)0x8004100E), + /// + /// The specified instance is not valid. + /// + InvalidObject = unchecked((int)0x8004100F), + /// + /// The specified class is not valid. + /// + InvalidClass = unchecked((int)0x80041010), + /// + /// A provider referenced in the schema does not have a + /// corresponding registration. + /// + ProviderNotFound = unchecked((int)0x80041011), + /// + /// A provider referenced in the schema has an incorrect or + /// incomplete registration. + /// + InvalidProviderRegistration = unchecked((int)0x80041012), + /// + /// COM cannot locate a provider referenced in the schema. + /// + ProviderLoadFailure = unchecked((int)0x80041013), + /// + /// A component, such as a provider, failed to initialize for internal reasons. + /// + InitializationFailure = unchecked((int)0x80041014), + /// + /// A networking error that prevents normal operation has + /// occurred. + /// + TransportFailure = unchecked((int)0x80041015), + /// + /// The requested operation is not valid. This error usually + /// applies to invalid attempts to delete classes or properties. + /// + InvalidOperation = unchecked((int)0x80041016), + /// + /// The query was not syntactically valid. + /// + InvalidQuery = unchecked((int)0x80041017), + /// + /// The requested query language is not supported. + /// + InvalidQueryType = unchecked((int)0x80041018), + /// + /// In a put operation, the + /// flag was specified, but the instance already exists. + /// + AlreadyExists = unchecked((int)0x80041019), + /// + /// The add operation cannot be performed on the qualifier + /// because the owning object does not permit overrides. + /// + OverrideNotAllowed = unchecked((int)0x8004101A), + /// + /// The user attempted to delete a qualifier that was not + /// owned. The qualifier was inherited from a parent class. + /// + PropagatedQualifier = unchecked((int)0x8004101B), + /// + /// The user attempted to delete a property that was not + /// owned. The property was inherited from a parent class. + /// + PropagatedProperty = unchecked((int)0x8004101C), + /// + /// The client made an unexpected and illegal sequence of + /// calls. + /// + Unexpected = unchecked((int)0x8004101D), + /// + /// The user requested an illegal operation, such as + /// spawning a class from an instance. + /// + IllegalOperation = unchecked((int)0x8004101E), + /// + /// There was an illegal attempt to specify a key qualifier + /// on a property that cannot be a key. The keys are specified in the class + /// definition for an object and cannot be altered on a per-instance basis. + /// + CannotBeKey = unchecked((int)0x8004101F), + /// + /// The current object is not a valid class definition. + /// Either it is incomplete, or it has not been registered with WMI using + /// (). + /// + IncompleteClass = unchecked((int)0x80041020), + /// + /// Reserved for future use. + /// + InvalidSyntax = unchecked((int)0x80041021), + /// + /// Reserved for future use. + /// + NondecoratedObject = unchecked((int)0x80041022), + /// + /// The property that you are attempting to modify is read-only. + /// + ReadOnly = unchecked((int)0x80041023), + /// + /// The provider cannot perform the requested operation, such + /// as requesting a query that is too complex, retrieving an instance, creating or + /// updating a class, deleting a class, or enumerating a class. + /// + ProviderNotCapable = unchecked((int)0x80041024), + /// + /// An attempt was made to make a change that would + /// invalidate a derived class. + /// + ClassHasChildren = unchecked((int)0x80041025), + /// + /// An attempt has been made to delete or modify a class that + /// has instances. + /// + ClassHasInstances = unchecked((int)0x80041026), + /// + /// Reserved for future use. + /// + QueryNotImplemented = unchecked((int)0x80041027), + /// + /// A value of null was specified for a property that may + /// not be null, such as one that is marked by a , , or + /// qualifier. + /// + IllegalNull = unchecked((int)0x80041028), + /// + /// The value provided for a qualifier was not a + /// legal qualifier type. + /// + InvalidQualifierType = unchecked((int)0x80041029), + /// + /// The CIM type specified for a property is not valid. + /// + InvalidPropertyType = unchecked((int)0x8004102A), + /// + /// The request was made with an out-of-range value, or is + /// incompatible with the type. + /// + ValueOutOfRange = unchecked((int)0x8004102B), + /// + /// An illegal attempt was made to make a class singleton, + /// such as when the class is derived from a non-singleton class. + /// + CannotBeSingleton = unchecked((int)0x8004102C), + /// + /// The CIM type specified is not valid. + /// + InvalidCimType = unchecked((int)0x8004102D), + /// + /// The requested method is not available. + /// + InvalidMethod = unchecked((int)0x8004102E), + /// + /// The parameters provided for the method are not valid. + /// + /// + InvalidMethodParameters = unchecked((int)0x8004102F), + /// + /// There was an attempt to get qualifiers on a system + /// property. + /// + SystemProperty = unchecked((int)0x80041030), + /// + /// The property type is not recognized. + /// + InvalidProperty = unchecked((int)0x80041031), + /// + /// An asynchronous process has been canceled internally or + /// by the user. Note that because of the timing and nature of the asynchronous + /// operation, the operation may not have been truly canceled. + /// + CallCanceled = unchecked((int)0x80041032), + /// + /// The user has requested an operation while WMI is in the + /// process of quitting. + /// + ShuttingDown = unchecked((int)0x80041033), + /// + /// An attempt was made to reuse an existing method name from + /// a base class, and the signatures did not match. + /// + PropagatedMethod = unchecked((int)0x80041034), + /// + /// One or more parameter values, such as a query text, is + /// too complex or unsupported. WMI is requested to retry the operation + /// with simpler parameters. + /// + UnsupportedParameter = unchecked((int)0x80041035), + /// + /// A parameter was missing from the method call. + /// + MissingParameterID = unchecked((int)0x80041036), + /// + /// A method parameter has an invalid qualifier. + /// + InvalidParameterID = unchecked((int)0x80041037), + /// + /// One or more of the method parameters have + /// qualifiers that are out of sequence. + /// + NonconsecutiveParameterIDs = unchecked((int)0x80041038), + /// + /// The return value for a method has an qualifier. + /// + /// + ParameterIDOnRetval = unchecked((int)0x80041039), + /// + /// The specified object path was invalid. + /// + InvalidObjectPath = unchecked((int)0x8004103A), + /// + /// There is not enough free disk space to continue the + /// operation. + /// + OutOfDiskSpace = unchecked((int)0x8004103B), + /// + /// The supplied buffer was too small to hold all the objects + /// in the enumerator or to read a string property. + /// + BufferTooSmall = unchecked((int)0x8004103C), + /// + /// The provider does not support the requested put + /// operation. + /// + UnsupportedPutExtension = unchecked((int)0x8004103D), + /// + /// An object with an incorrect type or version was + /// encountered during marshaling. + /// + UnknownObjectType = unchecked((int)0x8004103E), + /// + /// A packet with an incorrect type or version was + /// encountered during marshaling. + /// + UnknownPacketType = unchecked((int)0x8004103F), + /// + /// The packet has an unsupported version. + /// + MarshalVersionMismatch = unchecked((int)0x80041040), + /// + /// The packet is corrupted. + /// + MarshalInvalidSignature = unchecked((int)0x80041041), + /// + /// An attempt has been made to mismatch qualifiers, such as + /// putting [key] on an object instead of a property. + /// + InvalidQualifier = unchecked((int)0x80041042), + /// + /// A duplicate parameter has been declared in a CIM method. + /// + InvalidDuplicateParameter = unchecked((int)0x80041043), + /// + /// Reserved for future use. + /// + TooMuchData = unchecked((int)0x80041044), + /// + /// The delivery of an event has failed. The provider may + /// choose to re-raise the event. + /// + ServerTooBusy = unchecked((int)0x80041045), + /// + /// The specified flavor was invalid. + /// + InvalidFlavor = unchecked((int)0x80041046), + /// + /// An attempt has been made to create a reference that is + /// circular (for example, deriving a class from itself). + /// + CircularReference = unchecked((int)0x80041047), + /// + /// The specified class is not supported. + /// + UnsupportedClassUpdate = unchecked((int)0x80041048), + /// + /// An attempt was made to change a key when instances or derived + /// classes are already using the key. + /// + CannotChangeKeyInheritance = unchecked((int)0x80041049), + /// + /// An attempt was made to change an index when instances or derived + /// classes are already using the index. + /// + CannotChangeIndexInheritance = unchecked((int)0x80041050), + /// + /// An attempt was made to create more properties than the + /// current version of the class supports. + /// + TooManyProperties = unchecked((int)0x80041051), + /// + /// A property was redefined with a conflicting type in a + /// derived class. + /// + UpdateTypeMismatch = unchecked((int)0x80041052), + /// + /// An attempt was made in a derived class to override a + /// non-overrideable qualifier. + /// + UpdateOverrideNotAllowed = unchecked((int)0x80041053), + /// + /// A method was redeclared with a conflicting signature in a + /// derived class. + /// + UpdatePropagatedMethod = unchecked((int)0x80041054), + /// + /// An attempt was made to execute a method not marked with + /// [implemented] in any relevant class. + /// + MethodNotImplemented = unchecked((int)0x80041055), + /// + /// An attempt was made to execute a method marked with + /// [disabled]. + /// + MethodDisabled = unchecked((int)0x80041056), + /// + /// The refresher is busy with another operation. + /// + RefresherBusy = unchecked((int)0x80041057), + /// + /// The filtering query is syntactically invalid. + /// + UnparsableQuery = unchecked((int)0x80041058), + /// + /// The FROM clause of a filtering query references a class + /// that is not an event class. + /// + NotEventClass = unchecked((int)0x80041059), + /// + /// A GROUP BY clause was used without the corresponding + /// GROUP WITHIN clause. + /// + MissingGroupWithin = unchecked((int)0x8004105A), + /// + /// A GROUP BY clause was used. Aggregation on all properties + /// is not supported. + /// + MissingAggregationList = unchecked((int)0x8004105B), + /// + /// Dot notation was used on a property that is not an + /// embedded object. + /// + PropertyNotAnObject = unchecked((int)0x8004105C), + /// + /// A GROUP BY clause references a property that is an + /// embedded object without using dot notation. + /// + AggregatingByObject = unchecked((int)0x8004105D), + /// + /// An event provider registration query + /// () did not specify the classes for which + /// events were provided. + /// + UninterpretableProviderQuery = unchecked((int)0x8004105F), + /// + /// An request was made to back up or restore the repository + /// while WinMgmt.exe was using it. + /// + BackupRestoreWinmgmtRunning = unchecked((int)0x80041060), + /// + /// The asynchronous delivery queue overflowed from the + /// event consumer being too slow. + /// + QueueOverflow = unchecked((int)0x80041061), + /// + /// The operation failed because the client did not have the + /// necessary security privilege. + /// + PrivilegeNotHeld = unchecked((int)0x80041062), + /// + /// The operator is not valid for this property type. + /// + InvalidOperator = unchecked((int)0x80041063), + /// + /// The user specified a username, password, or authority on a + /// local connection. The user must use an empty user name and password and rely on + /// default security. + /// + LocalCredentials = unchecked((int)0x80041064), + /// + /// The class was made abstract when its base class is not + /// abstract. + /// + CannotBeAbstract = unchecked((int)0x80041065), + /// + /// An amended object was used in a put operation without the + /// WBEM_FLAG_USE_AMENDED_QUALIFIERS flag being specified. + /// + AmendedObject = unchecked((int)0x80041066), + /// + /// The client was not retrieving objects quickly enough from + /// an enumeration. + /// + ClientTooSlow = unchecked((int)0x80041067), + + /// + /// The provider registration overlaps with the system event + /// domain. + /// + RegistrationTooBroad = unchecked((int)0x80042001), + /// + /// A WITHIN clause was not used in this query. + /// + RegistrationTooPrecise = unchecked((int)0x80042002) + } + + /// + /// Represents management exceptions. + /// + /// + /// using System; + /// using System.Management; + /// + /// // This sample demonstrates how to display error + /// // information stored in a ManagementException object. + /// class Sample_ManagementException + /// { + /// public static int Main(string[] args) + /// { + /// try + /// { + /// ManagementObject disk = + /// new ManagementObject("Win32_LogicalDisk.DeviceID='BAD:'"); + /// disk.Get(); // throws ManagementException + /// Console.WriteLine("This shouldn't be displayed."); + /// } + /// catch (ManagementException e) + /// { + /// Console.WriteLine("ErrorCode " + e.ErrorCode); + /// Console.WriteLine("Message " + e.Message); + /// Console.WriteLine("Source " + e.Source); + /// if (e.ErrorInformation) //extended error object + /// Console.WriteLine("Extended Description : " + e.ErrorInformation["Description"]); + /// } + /// return 0; + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// ' This sample demonstrates how to display error + /// ' information stored in a ManagementException object. + /// Class Sample_ManagementException + /// Overloads Public Shared Function Main(args() As String) As Integer + /// Try + /// Dim disk As New ManagementObject("Win32_LogicalDisk.DeviceID='BAD:'") + /// disk.Get() ' throws ManagementException + /// Console.WriteLine("This shouldn't be displayed.") + /// Catch e As ManagementException + /// Console.WriteLine("ErrorCode " & e.ErrorCode) + /// Console.WriteLine("Message " & e.Message) + /// Console.WriteLine("Source " & e.Source) + /// If e.ErrorInformation != Nothing Then 'extended error object + /// Console.WriteLine("Extended Description : " & e.ErrorInformation("Description")) + /// End If + /// End Try + /// Return 0 + /// End Function + /// End Class + /// + /// + [Serializable] + public class ManagementException : SystemException + { + private ManagementBaseObject errorObject = null; + private ManagementStatus errorCode = 0; + + internal static void ThrowWithExtendedInfo(ManagementStatus errorCode) + { + ManagementBaseObject errObj = null; + string msg = null; + + //Try to get extended error info first, and save in errorObject member + IWbemClassObjectFreeThreaded obj = WbemErrorInfo.GetErrorInfo(); + if (obj != null) + errObj = new ManagementBaseObject(obj); + + //If the error code is not a WMI one and there's an extended error object available, stick the message + //from the extended error object in. + if (((msg = GetMessage(errorCode)) == null) && (errObj != null)) + try + { + msg = (string)errObj["Description"]; + } + catch {} + + throw new ManagementException(errorCode, msg, errObj); + } + + + internal static void ThrowWithExtendedInfo(Exception e) + { + ManagementBaseObject errObj = null; + string msg = null; + + //Try to get extended error info first, and save in errorObject member + IWbemClassObjectFreeThreaded obj = WbemErrorInfo.GetErrorInfo(); + if (obj != null) + errObj = new ManagementBaseObject(obj); + + //If the error code is not a WMI one and there's an extended error object available, stick the message + //from the extended error object in. + if (((msg = GetMessage(e)) == null) && (errObj != null)) + try + { + msg = (string)errObj["Description"]; + } + catch {} + + throw new ManagementException(e, msg, errObj); + } + + + internal ManagementException(ManagementStatus errorCode, string msg, ManagementBaseObject errObj) : base (msg) + { + this.errorCode = errorCode; + this.errorObject = errObj; + } + + internal ManagementException(Exception e, string msg, ManagementBaseObject errObj) : base (msg, e) + { + try + { + if (e is ManagementException) + { + errorCode = ((ManagementException)e).ErrorCode; + + // May/may not have extended error info. + // + if (errorObject != null) + errorObject = (ManagementBaseObject)((ManagementException)e).errorObject.Clone(); + else + errorObject = null; + } + else if (e is COMException) + errorCode = (ManagementStatus)((COMException)e).ErrorCode; + else + errorCode = (ManagementStatus)this.HResult; + } + catch {} + } + + /// + /// Initializes a new instance of the class that is serializable. + /// + /// The to populate with data. + /// The destination (see ) for this serialization. + protected ManagementException(SerializationInfo info, StreamingContext context) : base(info, context) + { + errorCode = (ManagementStatus)info.GetValue("errorCode", typeof(ManagementStatus)); + errorObject = info.GetValue("errorObject", typeof(ManagementBaseObject)) as ManagementBaseObject; + } + /// + /// Initializes a new instance of the class + /// + public ManagementException():this(ManagementStatus.Failed, "", null) + { + + } + + + /// + /// Initializes a new instance of the + /// class with a specified error message. + /// The message that describes the error. + /// + public ManagementException(string message):this(ManagementStatus.Failed, message, null) + { + + } + + /// + /// Initializes a empty new instance of the class + /// The message that describes the error. + /// The exception that is the cause of the current exception. If the innerException + /// parameter is not a null reference (Nothing in Visual Basic), the current exception is raised in a catch + /// block that handles the inner exception. + /// + public ManagementException(string message,Exception innerException):this(innerException, message, null) + { + // if the exception passed is not a ManagementException, then initialize the ErrorCode to Failed + if (!(innerException is ManagementException)) + errorCode = ManagementStatus.Failed; + } + + /// + /// Populates the object with the data needed to + /// serialize the + /// object. + /// + /// The to populate with data. + /// The destination (see ) for this serialization. + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("errorCode", errorCode); + info.AddValue("errorObject", errorObject); + } + + private static string GetMessage(Exception e) + { + string msg = null; + + if (e is COMException) + { + // Try and get WMI error message. If not use the one in + // the exception + msg = GetMessage ((ManagementStatus)((COMException)e).ErrorCode); + } + + if (null == msg) + msg = e.Message; + + return msg; + } + + private static string GetMessage(ManagementStatus errorCode) + { + string msg = null; + IWbemStatusCodeText statusCode = null; + int hr; + + statusCode = (IWbemStatusCodeText) new WbemStatusCodeText(); + if (statusCode != null) + { + try { + hr = statusCode.GetErrorCodeText_((int)errorCode, 0, 1, out msg); + + // Just in case it didn't like the flag=1, try it again + // with flag=0. + if (hr != 0) + hr = statusCode.GetErrorCodeText_((int)errorCode, 0, 0, out msg); + } + catch {} + } + + return msg; + } + + /// + /// Gets the extended error object provided by WMI. + /// + /// + /// A representing the + /// extended error object provided by WMI, if available; + /// otherwise. + /// + public ManagementBaseObject ErrorInformation + { + get + { return errorObject; } + } + + /// + /// Gets the error code reported by WMI, which caused this exception. + /// + /// + /// A value representing the error code returned by + /// the WMI operation. + /// + public ManagementStatus ErrorCode + { + get + { return errorCode; } + } + + } +} diff --git a/external/corefx/src/System.Management/src/System/Management/ManagementNamedValueCollection.cs b/external/corefx/src/System.Management/src/System/Management/ManagementNamedValueCollection.cs new file mode 100644 index 0000000000..1280a3a3d2 --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/ManagementNamedValueCollection.cs @@ -0,0 +1,186 @@ +// 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.Collections.Specialized; +using System.Runtime.Serialization; + +namespace System.Management +{ + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + /// + /// Represents a collection of named values + /// suitable for use as context information to WMI operations. The + /// names are case-insensitive. + /// + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + public class ManagementNamedValueCollection : NameObjectCollectionBase + { + // Notification of when the content of this collection changes + internal event IdentifierChangedEventHandler IdentifierChanged; + + //Fires IdentifierChanged event + private void FireIdentifierChanged() + { + if (IdentifierChanged != null) + IdentifierChanged(this, null); + } + + //default constructor + /// + /// Initializes a new instance + /// of the class. + /// + /// + /// Initializes a new instance of the class, which is empty. This is + /// the default constructor. + /// + public ManagementNamedValueCollection() + { + } + + + /// + /// Initializes a new instance of the class that is serializable + /// and uses the specified + /// and . + /// + /// The to populate with data. + /// The destination (see ) for this serialization. + protected ManagementNamedValueCollection(SerializationInfo info, StreamingContext context) : base(info, context) + { + throw new PlatformNotSupportedException(); + } + + /// + /// Internal method to return an IWbemContext representation + /// of the named value collection. + /// + internal IWbemContext GetContext() + { + IWbemContext wbemContext = null; + + // Only build a context if we have something to put in it + if (0 < Count) + { + int status = (int)ManagementStatus.NoError; + + try { + wbemContext = (IWbemContext) new WbemContext (); + + foreach (string name in this) + { + object val = base.BaseGet(name); + status = wbemContext.SetValue_ (name, 0, ref val); + if ((status & 0x80000000) != 0) + { + break; + } + } + } catch {} + } + + return wbemContext; + } + + /// + /// Adds a single-named value to the collection. + /// + /// The name of the new value. + /// The value to be associated with the name. + public void Add (string name, object value) + { + // Remove any old entry + try + { + base.BaseRemove (name); + } catch {} + + base.BaseAdd (name, value); + FireIdentifierChanged (); + } + + /// + /// Removes a single-named value from the collection. + /// If the collection does not contain an element with the + /// specified name, the collection remains unchanged and no + /// exception is thrown. + /// + /// The name of the value to be removed. + public void Remove (string name) + { + base.BaseRemove (name); + FireIdentifierChanged (); + } + + /// + /// Removes all entries from the collection. + /// + public void RemoveAll () + { + base.BaseClear (); + FireIdentifierChanged (); + } + + /// + /// Creates a clone of the collection. Individual values + /// are cloned. If a value does not support cloning, then a + /// is thrown. + /// + /// + /// The new copy of the collection. + /// + public ManagementNamedValueCollection Clone () + { + ManagementNamedValueCollection nvc = new ManagementNamedValueCollection(); + + foreach (string name in this) + { + // If we can clone the value, do so. Otherwise throw. + object val = base.BaseGet (name); + + if (null != val) + { + Type valueType = val.GetType (); + + if (valueType.IsByRef) + { + try + { + object clonedValue = ((ICloneable)val).Clone (); + nvc.Add (name, clonedValue); + } + catch + { + throw new NotSupportedException (); + } + } + else + { + nvc.Add (name, val); + } + } + else + nvc.Add (name, null); + } + + return nvc; + } + + /// + /// Returns the value associated with the specified name from this collection. + /// + /// The name of the value to be returned. + /// + /// An containing the + /// value of the specified item in this collection. + /// + public object this[string name] + { + get { + return base.BaseGet(name); + } + } + } + +} diff --git a/external/corefx/src/System.Management/src/System/Management/ManagementObject.cs.REMOVED.git-id b/external/corefx/src/System.Management/src/System/Management/ManagementObject.cs.REMOVED.git-id new file mode 100644 index 0000000000..39402e9476 --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/ManagementObject.cs.REMOVED.git-id @@ -0,0 +1 @@ +8a24b4c713d374501cf9cf4953da4e6c7b4827bd \ No newline at end of file diff --git a/external/corefx/src/System.Management/src/System/Management/ManagementObjectCollection.cs b/external/corefx/src/System.Management/src/System/Management/ManagementObjectCollection.cs new file mode 100644 index 0000000000..a16a224c4b --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/ManagementObjectCollection.cs @@ -0,0 +1,648 @@ +// 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.Collections; +using System.Runtime.InteropServices; + +namespace System.Management +{ + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + /// + /// Represents different collections of management objects + /// retrieved through WMI. The objects in this collection are of -derived types, including and + /// . + /// The collection can be the result of a WMI + /// query executed through a object, or an enumeration of + /// management objects of a specified type retrieved through a representing that type. + /// In addition, this can be a collection of management objects related in a specified + /// way to a specific management object - in this case the collection would + /// be retrieved through a method such as . + /// The collection can be walked using the and objects in it can be inspected or + /// manipulated for various management tasks. + /// + /// + /// using System; + /// using System.Management; + /// + /// // This example demonstrates how to enumerate instances of a ManagementClass object. + /// class Sample_ManagementObjectCollection + /// { + /// public static int Main(string[] args) { + /// ManagementClass diskClass = new ManagementClass("Win32_LogicalDisk"); + /// ManagementObjectCollection disks = diskClass.GetInstances(); + /// foreach (ManagementObject disk in disks) { + /// Console.WriteLine("Disk = " + disk["deviceid"]); + /// } + /// return 0; + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// ' This example demonstrates how to enumerate instances of a ManagementClass object. + /// Class Sample_ManagementObjectCollection + /// Overloads Public Shared Function Main(args() As String) As Integer + /// Dim diskClass As New ManagementClass("Win32_LogicalDisk") + /// Dim disks As ManagementObjectCollection = diskClass.GetInstances() + /// Dim disk As ManagementObject + /// For Each disk In disks + /// Console.WriteLine("Disk = " & disk("deviceid").ToString()) + /// Next disk + /// Return 0 + /// End Function + /// End Class + /// + /// + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + public class ManagementObjectCollection : ICollection, IEnumerable, IDisposable + { + private static readonly string name = typeof(ManagementObjectCollection).FullName; + + //fields + internal ManagementScope scope; + internal EnumerationOptions options; + private IEnumWbemClassObject enumWbem; //holds WMI enumerator for this collection + private bool isDisposed = false; + + //Constructor + internal ManagementObjectCollection( + ManagementScope scope, + EnumerationOptions options, + IEnumWbemClassObject enumWbem) + { + if (null != options) + this.options = (EnumerationOptions) options.Clone(); + else + this.options = new EnumerationOptions (); + + if (null != scope) + this.scope = (ManagementScope)scope.Clone (); + else + this.scope = ManagementScope._Clone(null); + + this.enumWbem = enumWbem; + } + + /// + /// Disposes of resources the object is holding. This is the destructor for the object. + /// + ~ManagementObjectCollection () + { + Dispose ( false ); + } + + /// + /// Releases resources associated with this object. After this + /// method has been called, an attempt to use this object will + /// result in an ObjectDisposedException being thrown. + /// + public void Dispose () + { + if (!isDisposed) + { + Dispose ( true ) ; + } + } + + private void Dispose ( bool disposing ) + { + if ( disposing ) + { + GC.SuppressFinalize (this); + isDisposed = true; + } + Marshal.ReleaseComObject (enumWbem); + } + + + // + //ICollection properties & methods + // + + /// + /// Represents the number of objects in the collection. + /// + /// + /// The number of objects in the collection. + /// + /// + /// This property is very expensive - it requires that + /// all members of the collection be enumerated. + /// + public int Count + { + get + { + if (isDisposed) + throw new ObjectDisposedException(name); + + // + // We can not use foreach since it _always_ calls Dispose on the collection + // invalidating the IEnumWbemClassObject pointers. + // We prevent this by doing a manual walk of the collection. + // + int count = 0; + + IEnumerator enumCol = this.GetEnumerator ( ) ; + while ( enumCol.MoveNext() == true ) + { + count++ ; + } + return count ; + } + } + + /// + /// Represents whether the object is synchronized. + /// + /// + /// , if the object is synchronized; + /// otherwise, . + /// + public bool IsSynchronized + { + get + { + if (isDisposed) + throw new ObjectDisposedException(name); + + return false; + } + } + + /// + /// Represents the object to be used for synchronization. + /// + /// + /// The object to be used for synchronization. + /// + public Object SyncRoot + { + get + { + if (isDisposed) + throw new ObjectDisposedException(name); + + return this; + } + } + + /// + /// Copies the collection to an array. + /// + /// + /// Copies the collection to an array. + /// + /// An array to copy to. + /// The index to start from. + public void CopyTo (Array array, Int32 index) + { + if (isDisposed) + throw new ObjectDisposedException(name); + + if (null == array) + throw new ArgumentNullException ("array"); + + if ((index < array.GetLowerBound (0)) || (index > array.GetUpperBound(0))) + throw new ArgumentOutOfRangeException ("index"); + + // Since we don't know the size until we've enumerated + // we'll have to dump the objects in a list first then + // try to copy them in. + + int capacity = array.Length - index; + int numObjects = 0; + ArrayList arrList = new ArrayList (); + + ManagementObjectEnumerator en = this.GetEnumerator(); + ManagementBaseObject obj; + + while (en.MoveNext()) + { + obj = en.Current; + + arrList.Add(obj); + numObjects++; + + if (numObjects > capacity) + throw new ArgumentException (null, "index"); + } + + // If we get here we are OK. Now copy the list to the array + arrList.CopyTo (array, index); + + return; + } + + /// + /// Copies the items in the collection to a + /// array. + /// + /// The target array. + /// The index to start from. + public void CopyTo (ManagementBaseObject[] objectCollection, Int32 index) + { + CopyTo ((Array)objectCollection, index); + } + + // + //IEnumerable methods + // + + //**************************************** + //GetEnumerator + //**************************************** + /// + /// Returns the enumerator for the collection. If the collection was retrieved from an operation that + /// specified the EnumerationOptions.Rewindable = false only one iteration through this enumerator is allowed. + /// Note that this applies to using the Count property of the collection as well since an iteration over the collection + /// is required. Due to this, code using the Count property should never specify EnumerationOptions.Rewindable = false. + /// + /// + /// + /// An that can be used to iterate through the + /// collection. + /// + public ManagementObjectEnumerator GetEnumerator() + { + if (isDisposed) + throw new ObjectDisposedException(name); + + + // + // We do not clone the enumerator if its the first enumerator. + // If it is the first enumerator we pass the reference + // to the enumerator implementation rather than a clone. If the enumerator is used + // from within a foreach statement in the client code, the foreach statement will + // dec the ref count on the reference which also happens to be the reference to the + // original enumerator causing subsequent uses of the collection to fail. + // To prevent this we always clone the enumerator (assuming its a rewindable enumerator) + // to avoid invalidating the collection. + // + // If its a forward only enumerator we simply pass back the original enumerator (i.e. + // not cloned) and if it gets disposed we end up throwing the next time its used. Essentially, + // the enumerator becomes the collection. + // + + // Unless this is the first enumerator, we have + // to clone. This may throw if we are non-rewindable. + if ( this.options.Rewindable == true ) + { + IEnumWbemClassObject enumWbemClone = null; + int status = (int)ManagementStatus.NoError; + + try + { + status = scope.GetSecuredIEnumWbemClassObjectHandler(enumWbem ).Clone_( ref enumWbemClone); + + if ((status & 0x80000000) == 0) + { + //since the original enumerator might not be reset, we need + //to reset the new one. + status = scope.GetSecuredIEnumWbemClassObjectHandler(enumWbemClone ).Reset_( ); + } + } + catch (COMException e) + { + ManagementException.ThrowWithExtendedInfo (e); + } + + if ((status & 0xfffff000) == 0x80041000) + { + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + } + else if ((status & 0x80000000) != 0) + { + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + return new ManagementObjectEnumerator (this, enumWbemClone); + } + else + { + // + // Notice that we use the original enumerator and hence enum position is retained. + // For example, if the client code manually walked half the collection and then + // used a foreach statement, the foreach statement would continue from where the + // manual walk ended. + // + return new ManagementObjectEnumerator(this, enumWbem); + } + } + + + /// + /// + /// Returns an enumerator that can iterate through a collection. + /// + /// + /// An that can be used to iterate + /// through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator (); + } + + + + // + // ManagementObjectCollection methods + // + + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC + /// + /// Represents the enumerator on the collection. + /// + /// + /// using System; + /// using System.Management; + /// + /// // This example demonstrates how to enumerate all logical disks + /// // using the ManagementObjectEnumerator object. + /// class Sample_ManagementObjectEnumerator + /// { + /// public static int Main(string[] args) { + /// ManagementClass diskClass = new ManagementClass("Win32_LogicalDisk"); + /// ManagementObjectCollection disks = diskClass.GetInstances(); + /// ManagementObjectCollection.ManagementObjectEnumerator disksEnumerator = + /// disks.GetEnumerator(); + /// while(disksEnumerator.MoveNext()) { + /// ManagementObject disk = (ManagementObject)disksEnumerator.Current; + /// Console.WriteLine("Disk found: " + disk["deviceid"]); + /// } + /// return 0; + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// ' This sample demonstrates how to enumerate all logical disks + /// ' using ManagementObjectEnumerator object. + /// Class Sample_ManagementObjectEnumerator + /// Overloads Public Shared Function Main(args() As String) As Integer + /// Dim diskClass As New ManagementClass("Win32_LogicalDisk") + /// Dim disks As ManagementObjectCollection = diskClass.GetInstances() + /// Dim disksEnumerator As _ + /// ManagementObjectCollection.ManagementObjectEnumerator = _ + /// disks.GetEnumerator() + /// While disksEnumerator.MoveNext() + /// Dim disk As ManagementObject = _ + /// CType(disksEnumerator.Current, ManagementObject) + /// Console.WriteLine("Disk found: " & disk("deviceid")) + /// End While + /// Return 0 + /// End Function + /// End Class + /// + /// + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC + public class ManagementObjectEnumerator : IEnumerator, IDisposable + { + private static readonly string name = typeof(ManagementObjectEnumerator).FullName; + private IEnumWbemClassObject enumWbem; + private ManagementObjectCollection collectionObject; + private uint cachedCount; //says how many objects are in the enumeration cache (when using BlockSize option) + private int cacheIndex; //used to walk the enumeration cache + private IWbemClassObjectFreeThreaded[] cachedObjects; //points to objects currently available in enumeration cache + private bool atEndOfCollection; + private bool isDisposed = false; + + //constructor + internal ManagementObjectEnumerator( + ManagementObjectCollection collectionObject, + IEnumWbemClassObject enumWbem) + { + this.enumWbem = enumWbem; + this.collectionObject = collectionObject; + cachedObjects = new IWbemClassObjectFreeThreaded[collectionObject.options.BlockSize]; + cachedCount = 0; + cacheIndex = -1; // Reset position + atEndOfCollection = false; + } + + + /// + /// Disposes of resources the object is holding. This is the destructor for the object. + /// + ~ManagementObjectEnumerator () + { + Dispose (); + } + + + /// + /// Releases resources associated with this object. After this + /// method has been called, an attempt to use this object will + /// result in an ObjectDisposedException being thrown. + /// + public void Dispose () + { + if (!isDisposed) + { + if (null != enumWbem) + { + Marshal.ReleaseComObject (enumWbem); + enumWbem = null; + } + + cachedObjects = null; + + // DO NOT dispose of collectionObject. It is merely a reference - its lifetime + // exceeds that of this object. If collectionObject.Dispose was to be done here, + // a reference count would be needed. + // + collectionObject = null; + + isDisposed = true; + + GC.SuppressFinalize (this); + } + } + + + /// + /// Gets the current that this enumerator points + /// to. + /// + /// + /// The current object in the enumeration. + /// + public ManagementBaseObject Current + { + get + { + if (isDisposed) + throw new ObjectDisposedException(name); + + if (cacheIndex < 0) + throw new InvalidOperationException(); + + return ManagementBaseObject.GetBaseObject (cachedObjects[cacheIndex], + collectionObject.scope); + } + } + + /// + /// + /// Returns the current object in the enumeration. + /// + /// + /// The current object in the enumeration. + /// + object IEnumerator.Current + { + get + { + return Current; + } + } + + //**************************************** + //MoveNext + //**************************************** + /// + /// Indicates whether the enumerator has moved to + /// the next object in the enumeration. + /// + /// + /// , if the enumerator was + /// successfully advanced to the next element; if the enumerator has + /// passed the end of the collection. + /// + public bool MoveNext () + { + if (isDisposed) + throw new ObjectDisposedException(name); + + //If there are no more objects in the collection return false + if (atEndOfCollection) + return false; + + //Look for the next object + cacheIndex++; + + if ((cachedCount - cacheIndex) == 0) //cache is empty - need to get more objects + { + + //If the timeout is set to infinite, need to use the WMI infinite constant + int timeout = (collectionObject.options.Timeout.Ticks == Int64.MaxValue) ? + (int)tag_WBEM_TIMEOUT_TYPE.WBEM_INFINITE : (int)collectionObject.options.Timeout.TotalMilliseconds; + + //Get the next [BLockSize] objects within the specified timeout + SecurityHandler securityHandler = collectionObject.scope.GetSecurityHandler(); + + //Because Interop doesn't support custom marshalling for arrays, we have to use + //the "DoNotMarshal" objects in the interop and then convert to the "FreeThreaded" + //counterparts afterwards. + IWbemClassObject_DoNotMarshal[] tempArray = new IWbemClassObject_DoNotMarshal[collectionObject.options.BlockSize]; + + int status = collectionObject.scope.GetSecuredIEnumWbemClassObjectHandler(enumWbem ).Next_(timeout, (uint)collectionObject.options.BlockSize,tempArray, ref cachedCount); + + securityHandler.Reset(); + + if (status >= 0) + { + //Convert results and put them in cache. + + for (int i = 0; i < cachedCount; i++) + { + cachedObjects[i] = new IWbemClassObjectFreeThreaded + ( + Marshal.GetIUnknownForObject(tempArray[i]) + ); + } + } + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + else + { + //If there was a timeout and no object can be returned we throw a timeout exception... + if ((status == (int)tag_WBEMSTATUS.WBEM_S_TIMEDOUT) && (cachedCount == 0)) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + + //If not timeout and no objects were returned - we're at the end of the collection + if ((status == (int)tag_WBEMSTATUS.WBEM_S_FALSE) && (cachedCount == 0)) + { + atEndOfCollection = true; + cacheIndex--; //back to last object + + /* This call to Dispose is being removed as per discussion with URT people and the newly supported + * Dispose() call in the foreach implementation itself. + * + * //Release the COM object (so that the user doesn't have to) + Dispose(); + */ + return false; + } + } + + cacheIndex = 0; + } + + return true; + } + + //**************************************** + //Reset + //**************************************** + /// + /// Resets the enumerator to the beginning of the collection. + /// + public void Reset () + { + if (isDisposed) + throw new ObjectDisposedException(name); + + //If the collection is not rewindable you can't do this + if (!collectionObject.options.Rewindable) + throw new InvalidOperationException(); + else + { + //Reset the WMI enumerator + SecurityHandler securityHandler = collectionObject.scope.GetSecurityHandler(); + int status = (int)ManagementStatus.NoError; + + try + { + status = collectionObject.scope.GetSecuredIEnumWbemClassObjectHandler(enumWbem).Reset_(); + } + catch (COMException e) + { + ManagementException.ThrowWithExtendedInfo (e); + } + finally + { + securityHandler.Reset (); + } + + if ((status & 0xfffff000) == 0x80041000) + { + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + } + else if ((status & 0x80000000) != 0) + { + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + //Flush the current enumeration cache + for (int i=(cacheIndex >= 0 ? cacheIndex : 0); i + /// Retrieves a collection of management objects based + /// on a specified query. + /// This class is one of the more commonly used entry points to retrieving + /// management information. For example, it can be used to enumerate all disk + /// drives, network adapters, processes and many more management objects on a + /// system, or to query for all network connections that are up, services that are + /// paused etc. + /// When instantiated, an instance of this class takes as input a WMI + /// query represented in an or it's derivatives, and optionally a representing the WMI namespace + /// to execute the query in. It can also take additional advanced + /// options in an object. When the Get() method on this object + /// is invoked, the ManagementObjectSearcher executes the given query in the + /// specified scope and returns a collection of management objects that match the + /// query in a . + /// + /// + /// using System; + /// using System.Management; + /// + /// // This sample demonstrates perform a query using + /// // ManagementObjectSearcher object. + /// class Sample_ManagementObjectSearcher + /// { + /// public static int Main(string[] args) { + /// ManagementObjectSearcher searcher = new + /// ManagementObjectSearcher("select * from win32_share"); + /// foreach (ManagementObject share in searcher.Get()) { + /// Console.WriteLine("Share = " + share["Name"]); + /// } + /// return 0; + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// ' This sample demonstrates perform a query using + /// ' ManagementObjectSearcher object. + /// Class Sample_ManagementObjectSearcher + /// Overloads Public Shared Function Main(args() As String) As Integer + /// Dim searcher As New ManagementObjectSearcher("SELECT * FROM Win32_Share") + /// Dim share As ManagementObject + /// For Each share In searcher.Get() + /// Console.WriteLine("Share = " & share("Name").ToString()) + /// Next share + /// Return 0 + /// End Function + /// End Class + /// + /// + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + [ToolboxItem(false)] + public class ManagementObjectSearcher : Component + { + //fields + private ManagementScope scope; + private ObjectQuery query; + private EnumerationOptions options; + + //default constructor + /// + /// Initializes a new instance of the class. + /// + /// + /// Initializes a new instance of the class. After some properties on + /// this object are set, the object can be used to invoke a query for management information. This is the default + /// constructor. + /// + /// + /// ManagementObjectSearcher s = new ManagementObjectSearcher(); + /// + /// Dim s As New ManagementObjectSearcher() + /// + /// + public ManagementObjectSearcher() : this((ManagementScope)null, null, null) {} + + //parameterized constructors + /// + /// Initializes a new instance of the class used + /// to invoke the specified query for management information. + /// + /// The WMI query to be invoked by the object. + /// + /// ManagementObjectSearcher s = + /// new ManagementObjectSearcher("SELECT * FROM Win32_Service"); + /// + /// Dim s As New ManagementObjectSearcher("SELECT * FROM Win32_Service") + /// + /// + public ManagementObjectSearcher(string queryString) : this(null, new ObjectQuery(queryString), null) {} + + /// + /// Initializes a new instance of the class used to invoke the + /// specified query for management information. + /// + /// An representing the query to be invoked by the searcher. + /// + /// SelectQuery q = new SelectQuery("Win32_Service", "State='Running'"); + /// ManagementObjectSearcher s = new ManagementObjectSearcher(q); + /// + /// Dim q As New SelectQuery("Win32_Service", "State=""Running""") + /// Dim s As New ManagementObjectSearcher(q) + /// + /// + public ManagementObjectSearcher(ObjectQuery query) : this (null, query, null) {} + + /// + /// Initializes a new instance of the class used to invoke the + /// specified query in the specified scope. + /// + /// The scope in which to query. + /// The query to be invoked. + /// + /// If no scope is specified, the default scope () is used. + /// + /// + /// ManagementObjectSearcher s = new ManagementObjectSearcher( + /// "root\\MyApp", + /// "SELECT * FROM MyClass WHERE MyProp=5"); + /// + /// Dim s As New ManagementObjectSearcher( _ + /// "root\MyApp", _ + /// "SELECT * FROM MyClass WHERE MyProp=5") + /// + /// + public ManagementObjectSearcher(string scope, string queryString) : + this(new ManagementScope(scope), new ObjectQuery(queryString), null) {} + + /// + /// Initializes a new instance of the class used to invoke the + /// specified query in the specified scope. + /// + /// A representing the scope in which to invoke the query. + /// An representing the query to be invoked. + /// + /// If no scope is specified, the default scope () is + /// used. + /// + /// + /// ManagementScope myScope = new ManagementScope("root\\MyApp"); + /// SelectQuery q = new SelectQuery("Win32_Environment", "User=<system>"); + /// ManagementObjectSearcher s = new ManagementObjectSearcher(myScope,q); + /// + /// Dim myScope As New ManagementScope("root\MyApp") + /// Dim q As New SelectQuery("Win32_Environment", "User=<system>") + /// Dim s As New ManagementObjectSearcher(myScope,q) + /// + /// + public ManagementObjectSearcher(ManagementScope scope, ObjectQuery query) : this(scope, query, null) {} + + /// + /// Initializes a new instance of the class used to invoke the specified + /// query, in the specified scope, and with the specified options. + /// + /// The scope in which the query should be invoked. + /// The query to be invoked. + /// An specifying additional options for the query. + /// + /// ManagementObjectSearcher s = new ManagementObjectSearcher( + /// "root\\MyApp", + /// "SELECT * FROM MyClass", + /// new EnumerationOptions(null, InfiniteTimeout, 1, true, false, true); + /// + /// Dim s As New ManagementObjectSearcher( _ + /// "root\MyApp", _ + /// "SELECT * FROM MyClass", _ + /// New EnumerationOptions(Null, InfiniteTimeout, 1, True, False, True) + /// + /// + public ManagementObjectSearcher(string scope, string queryString, EnumerationOptions options) : + this(new ManagementScope(scope), new ObjectQuery(queryString), options) {} + /// + /// Initializes a new instance of the class to be + /// used to invoke the specified query in the specified scope, with the specified + /// options. + /// + /// A specifying the scope of the query + /// An specifying the query to be invoked + /// An specifying additional options to be used for the query. + /// + /// ManagementScope scope = new ManagementScope("root\\MyApp"); + /// SelectQuery q = new SelectQuery("SELECT * FROM MyClass"); + /// EnumerationOptions o = new EnumerationOptions(null, InfiniteTimeout, 1, true, false, true); + /// ManagementObjectSearcher s = new ManagementObjectSearcher(scope, q, o); + /// + /// Dim scope As New ManagementScope("root\MyApp") + /// Dim q As New SelectQuery("SELECT * FROM MyClass") + /// Dim o As New EnumerationOptions(Null, InfiniteTimeout, 1, True, False, True) + /// Dim s As New ManagementObjectSearcher(scope, q, o) + /// + /// + public ManagementObjectSearcher(ManagementScope scope, ObjectQuery query, EnumerationOptions options) + { + this.scope = ManagementScope._Clone(scope); + + if (null != query) + this.query = (ObjectQuery)query.Clone(); + else + this.query = new ObjectQuery(); + + if (null != options) + this.options = (EnumerationOptions)options.Clone(); + else + this.options = new EnumerationOptions(); + } + + + // + //Public Properties + // + + /// + /// Gets or sets the scope in which to look for objects (the scope represents a WMI namespace). + /// + /// + /// The scope (namespace) in which to look for objects. + /// + /// + /// When the value of this property is changed, + /// the + /// is re-bound to the new scope. + /// + /// + /// ManagementObjectSearcher s = new ManagementObjectSearcher(); + /// s.Scope = new ManagementScope("root\\MyApp"); + /// + /// Dim s As New ManagementObjectSearcher() + /// Dim ms As New ManagementScope ("root\MyApp") + /// s.Scope = ms + /// + /// + public ManagementScope Scope + { + get + { + return scope; + } + set + { + if (null != value) + scope = (ManagementScope) value.Clone (); + else + throw new ArgumentNullException ("value"); + } + } + + /// + /// Gets or sets the query to be invoked in the + /// searcher (that is, the criteria to be applied to the search for management objects). + /// + /// + /// The criteria to apply to the query. + /// + /// + /// When the value of this property is changed, the + /// is reset to use the new query. + /// + public ObjectQuery Query + { + get + { + return query; + } + set + { + if (null != value) + query = (ObjectQuery)value.Clone (); + else + throw new ArgumentNullException ("value"); + } + } + + /// + /// Gets or sets the options for how to search for objects. + /// + /// + /// The options for how to search for objects. + /// + public EnumerationOptions Options + { + get + { + return options; + } + set + { + if (null != value) + options = (EnumerationOptions) value.Clone (); + else + throw new ArgumentNullException("value"); + } + } + + //******************************************** + //Get() + //******************************************** + /// + /// Invokes the specified WMI query and returns the resulting collection. + /// + /// + /// Invokes the specified WMI query and returns the + /// resulting collection. + /// + /// + /// A containing the objects that match the + /// specified query. + /// + public ManagementObjectCollection Get() + { + Initialize (); + IEnumWbemClassObject ew = null; + SecurityHandler securityHandler = scope.GetSecurityHandler(); + EnumerationOptions enumOptions = (EnumerationOptions)options.Clone(); + + int status = (int)ManagementStatus.NoError; + + try + { + //If this is a simple SelectQuery (className only), and the enumerateDeep is set, we have + //to find out whether this is a class enumeration or instance enumeration and call CreateInstanceEnum/ + //CreateClassEnum appropriately, because with ExecQuery we can't do a deep enumeration. + if ((query.GetType() == typeof(SelectQuery)) && + (((SelectQuery)query).Condition == null) && + (((SelectQuery)query).SelectedProperties == null) && + (options.EnumerateDeep == true)) + { + //Need to make sure that we're not passing invalid flags to enumeration APIs. + //The only flags not valid for enumerations are EnsureLocatable & PrototypeOnly. + enumOptions.EnsureLocatable = false; enumOptions.PrototypeOnly = false; + + if (((SelectQuery)query).IsSchemaQuery == false) //deep instance enumeration + { + status = scope.GetSecuredIWbemServicesHandler( scope.GetIWbemServices() ).CreateInstanceEnum_( + ((SelectQuery)query).ClassName, + enumOptions.Flags, + enumOptions.GetContext(), + ref ew); + } + else //deep class enumeration + { + status = scope.GetSecuredIWbemServicesHandler(scope.GetIWbemServices() ).CreateClassEnum_(((SelectQuery)query).ClassName, + enumOptions.Flags, + enumOptions.GetContext(), + ref ew ); + } + } + else //we can use ExecQuery + { + //Make sure the EnumerateDeep flag bit is turned off because it's invalid for queries + enumOptions.EnumerateDeep = true; + status = scope.GetSecuredIWbemServicesHandler(scope.GetIWbemServices() ).ExecQuery_( + query.QueryLanguage, + query.QueryString, + enumOptions.Flags, + enumOptions.GetContext(), + ref ew ); + } + } + catch (COMException e) + { + ManagementException.ThrowWithExtendedInfo(e); + } + finally + { + securityHandler.Reset(); + } + + if ((status & 0xfffff000) == 0x80041000) + { + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + } + else if ((status & 0x80000000) != 0) + { + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + //Create a new collection object for the results + + return new ManagementObjectCollection(scope, options, ew); + }//Get() + + + //******************************************** + //Get() asynchronous + //******************************************** + /// + /// Invokes the WMI query, asynchronously, and binds to a watcher to deliver the results. + /// + /// The watcher that raises events triggered by the operation. + public void Get(ManagementOperationObserver watcher) + { + if (null == watcher) + throw new ArgumentNullException ("watcher"); + + Initialize (); + IWbemServices wbemServices = scope.GetIWbemServices (); + + EnumerationOptions enumOptions = (EnumerationOptions)options.Clone(); + // Ensure we switch off ReturnImmediately as this is invalid for async calls + enumOptions.ReturnImmediately = false; + // If someone has registered for progress, make sure we flag it + if (watcher.HaveListenersForProgress) + enumOptions.SendStatus = true; + + WmiEventSink sink = watcher.GetNewSink (scope, enumOptions.Context); + SecurityHandler securityHandler = scope.GetSecurityHandler(); + + int status = (int)ManagementStatus.NoError; + + try + { + //If this is a simple SelectQuery (className only), and the enumerateDeep is set, we have + //to find out whether this is a class enumeration or instance enumeration and call CreateInstanceEnum/ + //CreateClassEnum appropriately, because with ExecQuery we can't do a deep enumeration. + if ((query.GetType() == typeof(SelectQuery)) && + (((SelectQuery)query).Condition == null) && + (((SelectQuery)query).SelectedProperties == null) && + (options.EnumerateDeep == true)) + { + //Need to make sure that we're not passing invalid flags to enumeration APIs. + //The only flags not valid for enumerations are EnsureLocatable & PrototypeOnly. + enumOptions.EnsureLocatable = false; enumOptions.PrototypeOnly = false; + + if (((SelectQuery)query).IsSchemaQuery == false) //deep instance enumeration + { + status = scope.GetSecuredIWbemServicesHandler( wbemServices ).CreateInstanceEnumAsync_(((SelectQuery)query).ClassName, + enumOptions.Flags, + enumOptions.GetContext(), + sink.Stub); + } + else + { + status = scope.GetSecuredIWbemServicesHandler( wbemServices ).CreateClassEnumAsync_(((SelectQuery)query).ClassName, + enumOptions.Flags, + enumOptions.GetContext(), + sink.Stub); + } + } + else //we can use ExecQuery + { + //Make sure the EnumerateDeep flag bit is turned off because it's invalid for queries + enumOptions.EnumerateDeep = true; + status = scope.GetSecuredIWbemServicesHandler( wbemServices ).ExecQueryAsync_( + query.QueryLanguage, + query.QueryString, + enumOptions.Flags, + enumOptions.GetContext(), + sink.Stub); + } + + } + catch (COMException e) + { + watcher.RemoveSink (sink); + ManagementException.ThrowWithExtendedInfo (e); + } + finally + { + securityHandler.Reset(); + } + + if ((status & 0xfffff000) == 0x80041000) + { + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + } + else if ((status & 0x80000000) != 0) + { + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + + private void Initialize() + { + //If the query is not set yet we can't do it + if (null == query) + throw new InvalidOperationException(); + + //If we're not connected yet, this is the time to do it... +#pragma warning disable CA2002 + lock (this) +#pragma warning restore CA2002 + { + if (null == scope) + scope = ManagementScope._Clone(null); + } + + lock (scope) + { + if (!scope.IsConnected) + scope.Initialize(); + } + } + } +} diff --git a/external/corefx/src/System.Management/src/System/Management/ManagementOperationWatcher.cs b/external/corefx/src/System.Management/src/System/Management/ManagementOperationWatcher.cs new file mode 100644 index 0000000000..5f3547f89f --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/ManagementOperationWatcher.cs @@ -0,0 +1,446 @@ +// 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.Collections; +using System.Threading; + +namespace System.Management +{ + + /// + /// Represents the method that will handle the event. + /// + public delegate void ObjectReadyEventHandler(object sender, ObjectReadyEventArgs e); + + /// + /// Represents the method that will handle the event. + /// + public delegate void CompletedEventHandler (object sender, CompletedEventArgs e); + + /// + /// Represents the method that will handle the event. + /// + public delegate void ProgressEventHandler (object sender, ProgressEventArgs e); + + /// + /// Represents the method that will handle the event. + /// + public delegate void ObjectPutEventHandler(object sender, ObjectPutEventArgs e); + + /// + /// Used to manage asynchronous operations and handle management information and events received asynchronously. + /// + /// + /// using System; + /// using System.Management; + /// + /// // This sample demonstrates how to read a ManagementObject asychronously + /// // using the ManagementOperationObserver object. + /// + /// class Sample_ManagementOperationObserver { + /// public static int Main(string[] args) { + /// + /// //Set up a handler for the asynchronous callback + /// ManagementOperationObserver observer = new ManagementOperationObserver(); + /// MyHandler completionHandler = new MyHandler(); + /// observer.Completed += new CompletedEventHandler(completionHandler.Done); + /// + /// //Invoke the asynchronous read of the object + /// ManagementObject disk = new ManagementObject("Win32_logicaldisk='C:'"); + /// disk.Get(observer); + /// + /// //For the purpose of this sample, we keep the main + /// // thread alive until the asynchronous operation is completed. + /// + /// while (!completionHandler.IsComplete) { + /// System.Threading.Thread.Sleep(500); + /// } + /// + /// Console.WriteLine("Size= " + disk["Size"] + " bytes."); + /// + /// return 0; + /// } + /// + /// public class MyHandler + /// { + /// private bool isComplete = false; + /// + /// public void Done(object sender, CompletedEventArgs e) { + /// isComplete = true; + /// } + /// + /// public bool IsComplete { + /// get { + /// return isComplete; + /// } + /// } + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// ' This sample demonstrates how to read a ManagementObject asychronously + /// ' using the ManagementOperationObserver object. + /// + /// Class Sample_ManagementOperationObserver + /// Overloads Public Shared Function Main(args() As String) As Integer + /// + /// 'Set up a handler for the asynchronous callback + /// Dim observer As New ManagementOperationObserver() + /// Dim completionHandler As New MyHandler() + /// AddHandler observer.Completed, AddressOf completionHandler.Done + /// + /// ' Invoke the object read asynchronously + /// Dim disk As New ManagementObject("Win32_logicaldisk='C:'") + /// disk.Get(observer) + /// + /// ' For the purpose of this sample, we keep the main + /// ' thread alive until the asynchronous operation is finished. + /// While Not completionHandler.IsComplete Then + /// System.Threading.Thread.Sleep(500) + /// End While + /// + /// Console.WriteLine("Size = " + disk("Size").ToString() & " bytes") + /// + /// Return 0 + /// End Function + /// + /// Public Class MyHandler + /// Private _isComplete As Boolean = False + /// + /// Public Sub Done(sender As Object, e As CompletedEventArgs) + /// _isComplete = True + /// End Sub 'Done + /// + /// Public ReadOnly Property IsComplete() As Boolean + /// Get + /// Return _isComplete + /// End Get + /// End Property + /// End Class + /// End Class + /// + /// + public class ManagementOperationObserver + { + private Hashtable m_sinkCollection; + private WmiDelegateInvoker delegateInvoker; + + /// + /// Occurs when a new object is available. + /// + public event ObjectReadyEventHandler ObjectReady; + + /// + /// Occurs when an operation has completed. + /// + public event CompletedEventHandler Completed; + + /// + /// Occurs to indicate the progress of an ongoing operation. + /// + public event ProgressEventHandler Progress; + + /// + /// Occurs when an object has been successfully committed. + /// + public event ObjectPutEventHandler ObjectPut; + + /// + /// Initializes a new instance of the class. This is the default constructor. + /// + public ManagementOperationObserver () + { + // We make our sink collection synchronized + m_sinkCollection = new Hashtable (); + delegateInvoker = new WmiDelegateInvoker (this); + } + + /// + /// Cancels all outstanding operations. + /// + public void Cancel () + { + // Cancel all the sinks we have - make a copy to avoid things + // changing under our feet + Hashtable copiedSinkTable = new Hashtable (); + + lock (m_sinkCollection) + { + IDictionaryEnumerator sinkEnum = m_sinkCollection.GetEnumerator(); + + try + { + sinkEnum.Reset (); + + while (sinkEnum.MoveNext ()) + { + DictionaryEntry entry = (DictionaryEntry) sinkEnum.Current; + copiedSinkTable.Add (entry.Key, entry.Value); + } + } + catch + { + } + } + + // Now step through the copy and cancel everything + try + { + IDictionaryEnumerator copiedSinkEnum = copiedSinkTable.GetEnumerator(); + copiedSinkEnum.Reset (); + + while (copiedSinkEnum.MoveNext ()) + { + DictionaryEntry entry = (DictionaryEntry) copiedSinkEnum.Current; + WmiEventSink eventSink = (WmiEventSink) entry.Value; + + try + { + eventSink.Cancel (); + } + catch + { + } + } + } + catch + { + } + } + + internal WmiEventSink GetNewSink ( + ManagementScope scope, + object context) + { + try + { + WmiEventSink eventSink = WmiEventSink.GetWmiEventSink(this, context, scope, null, null); + + // Add it to our collection + lock (m_sinkCollection) + { + m_sinkCollection.Add (eventSink.GetHashCode(), eventSink); + } + + return eventSink; + } + catch + { + return null; + } + } + + internal bool HaveListenersForProgress + { + get + { + bool result = false; + + try + { + if (Progress != null) + result = ((Progress.GetInvocationList ()).Length > 0); + } + catch + { + } + + return result; + } + } + internal WmiEventSink GetNewPutSink ( + ManagementScope scope, + object context, + string path, + string className) + { + try + { + WmiEventSink eventSink = WmiEventSink.GetWmiEventSink(this, context, scope, path, className); + + // Add it to our collection + lock (m_sinkCollection) + { + m_sinkCollection.Add (eventSink.GetHashCode(), eventSink); + } + + return eventSink; + } + catch + { + return null; + } + } + + internal WmiGetEventSink GetNewGetSink ( + ManagementScope scope, + object context, + ManagementObject managementObject) + { + try + { + WmiGetEventSink eventSink = WmiGetEventSink.GetWmiGetEventSink(this, + context, scope, managementObject); + + // Add it to our collection + lock (m_sinkCollection) + { + m_sinkCollection.Add (eventSink.GetHashCode(), eventSink); + } + + return eventSink; + } + catch + { + return null; + } + } + + internal void RemoveSink (WmiEventSink eventSink) + { + try + { + lock (m_sinkCollection) + { + m_sinkCollection.Remove (eventSink.GetHashCode ()); + } + + // Release the stub as we are now disconnected + eventSink.ReleaseStub (); + } + catch + { + } + } + + /// + /// Fires the ObjectReady event to whomsoever is listening + /// + /// + internal void FireObjectReady (ObjectReadyEventArgs args) + { + try + { + delegateInvoker.FireEventToDelegates (ObjectReady, args); + } + catch + { + } + } + + internal void FireCompleted (CompletedEventArgs args) + { + try + { + delegateInvoker.FireEventToDelegates (Completed, args); + } + catch + { + } + } + + internal void FireProgress (ProgressEventArgs args) + { + try + { + delegateInvoker.FireEventToDelegates (Progress, args); + } + catch + { + } + } + + internal void FireObjectPut (ObjectPutEventArgs args) + { + try + { + delegateInvoker.FireEventToDelegates (ObjectPut, args); + } + catch + { + } + } + } + + internal class WmiEventState + { + private Delegate d; + private ManagementEventArgs args; + private AutoResetEvent h; + + internal WmiEventState (Delegate d, ManagementEventArgs args, AutoResetEvent h) + { + this.d = d; + this.args = args; + this.h = h; + } + + public Delegate Delegate + { + get { return d; } + } + + public ManagementEventArgs Args + { + get { return args; } + } + + public AutoResetEvent AutoResetEvent + { + get { return h; } + } + } + + /// + /// This class handles the posting of events to delegates. For each event + /// it queues a set of requests (one per target delegate) to the thread pool + /// to handle the event. It ensures that no single delegate can throw + /// an exception that prevents the event from reaching any other delegates. + /// It also ensures that the sender does not signal the processing of the + /// WMI event as "done" until all target delegates have signalled that they are + /// done. + /// + internal class WmiDelegateInvoker + { + internal object sender; + + internal WmiDelegateInvoker (object sender) + { + this.sender = sender; + } + + /// + /// Custom handler for firing a WMI event to a list of delegates. We use + /// the process thread pool to handle the firing. + /// + /// The MulticastDelegate representing the collection + /// of targets for the event + /// The accompanying event arguments + internal void FireEventToDelegates (MulticastDelegate md, ManagementEventArgs args) + { + try + { + if (null != md) + { + foreach (Delegate d in md.GetInvocationList()) + { + try + { + d.DynamicInvoke (new object [] {this.sender, args}); + } + catch + { + } + } + } + } + catch + { + } + } + } + +} diff --git a/external/corefx/src/System.Management/src/System/Management/ManagementOptions.cs b/external/corefx/src/System.Management/src/System/Management/ManagementOptions.cs new file mode 100644 index 0000000000..f0abf450e5 --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/ManagementOptions.cs @@ -0,0 +1,1715 @@ +// 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.ComponentModel; +using System.Security; + +namespace System.Management +{ + /// + /// Describes the authentication level to be used to connect to WMI. This is used for the COM connection to WMI. + /// + public enum AuthenticationLevel + { + /// + /// The default COM authentication level. WMI uses the default Windows Authentication setting. + /// + Default=0, + /// + /// No COM authentication. + /// + None=1, + /// + /// Connect-level COM authentication. + /// + Connect=2, + /// + /// Call-level COM authentication. + /// + Call=3, + /// + /// Packet-level COM authentication. + /// + Packet=4, + /// + /// Packet Integrity-level COM authentication. + /// + PacketIntegrity=5, + /// + /// Packet Privacy-level COM authentication. + /// + PacketPrivacy=6, + /// + /// The default COM authentication level. WMI uses the default Windows Authentication setting. + /// + Unchanged=-1 + } + + /// + /// Describes the impersonation level to be used to connect to WMI. + /// + public enum ImpersonationLevel + { + /// + /// Default impersonation. + /// + Default=0, + /// + /// Anonymous COM impersonation level that hides the + /// identity of the caller. Calls to WMI may fail + /// with this impersonation level. + /// + Anonymous=1, + /// + /// Identify-level COM impersonation level that allows objects + /// to query the credentials of the caller. Calls to + /// WMI may fail with this impersonation level. + /// + Identify=2, + /// + /// Impersonate-level COM impersonation level that allows + /// objects to use the credentials of the caller. This is the recommended impersonation level for WMI calls. + /// + Impersonate=3, + /// + /// Delegate-level COM impersonation level that allows objects + /// to permit other objects to use the credentials of the caller. This + /// level, which will work with WMI calls but may constitute an unnecessary + /// security risk, is supported only under Windows 2000. + /// + Delegate=4 + } + + /// + /// Describes the possible effects of saving an object to WMI when + /// using . + /// + public enum PutType + { + /// + /// Invalid Type + /// + None = 0, + /// + /// Updates an existing object + /// only; does not create a new object. + /// + UpdateOnly=1, + /// + /// Creates an object only; + /// does not update an existing object. + /// + CreateOnly=2, + /// + /// Saves the object, whether + /// updating an existing object or creating a new object. + /// + UpdateOrCreate=3 + } + + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + /// + /// + /// Provides an abstract base class for all Options objects. + /// Options objects are used to customize different management operations. + /// Use one of the Options classes derived from this class, as + /// indicated by the signature of the operation being performed. + /// + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + [TypeConverter(typeof(ExpandableObjectConverter))] + abstract public class ManagementOptions : ICloneable + { + /// + /// Specifies an infinite timeout. + /// + public static readonly TimeSpan InfiniteTimeout = TimeSpan.MaxValue; + + internal int flags; + internal ManagementNamedValueCollection context; + internal TimeSpan timeout; + + //Used when any public property on this object is changed, to signal + //to the containing object that it needs to be refreshed. + internal event IdentifierChangedEventHandler IdentifierChanged; + + //Fires IdentifierChanged event + internal void FireIdentifierChanged() + { + if (IdentifierChanged != null) + IdentifierChanged(this, null); + } + + //Called when IdentifierChanged() event fires + internal void HandleIdentifierChange(object sender, + IdentifierChangedEventArgs args) + { + //Something inside ManagementOptions changed, we need to fire an event + //to the parent object + FireIdentifierChanged(); + } + + internal int Flags { + get { return flags; } + set { flags = value; } + } + + /// + /// Gets or sets a WMI context object. This is a + /// name-value pairs list to be passed through to a WMI provider that supports + /// context information for customized operation. + /// + /// + /// A name-value pairs list to be passed through to a WMI provider that + /// supports context information for customized operation. + /// + public ManagementNamedValueCollection Context + { + get + { + if (context == null) + return context = new ManagementNamedValueCollection(); + else + return context; + } + set + { + ManagementNamedValueCollection oldContext = context; + + if (null != value) + context = (ManagementNamedValueCollection) value.Clone(); + else + context = new ManagementNamedValueCollection (); + + if (null != oldContext) + oldContext.IdentifierChanged -= new IdentifierChangedEventHandler(HandleIdentifierChange); + + //register for change events in this object + context.IdentifierChanged += new IdentifierChangedEventHandler(HandleIdentifierChange); + + //the context property has changed so act like we fired the event + HandleIdentifierChange(this,null); + } + } + + /// + /// Gets or sets the timeout to apply to the operation. + /// Note that for operations that return collections, this timeout applies to the + /// enumeration through the resulting collection, not the operation itself + /// (the + /// property is used for the latter). + /// This property is used to indicate that the operation should be performed semisynchronously. + /// + /// + /// The default value for this property is + /// , which means the operation will block. + /// The value specified must be positive. + /// + public TimeSpan Timeout + { + get + { return timeout; } + set + { + //Timespan allows for negative values, but we want to make sure it's positive here... + if (value.Ticks < 0) + throw new ArgumentOutOfRangeException("value"); + + timeout = value; + FireIdentifierChanged(); + } + } + + + internal ManagementOptions() : this(null, InfiniteTimeout) {} + internal ManagementOptions(ManagementNamedValueCollection context, TimeSpan timeout) : this(context, timeout, 0) {} + internal ManagementOptions(ManagementNamedValueCollection context, TimeSpan timeout, int flags) + { + this.flags = flags; + if (context != null) + this.Context = context; + else + this.context = null; + this.Timeout = timeout; + } + + + internal IWbemContext GetContext () { + if (context != null) + return context.GetContext(); + else + return null; + } + + // We do not expose this publicly; instead the flag is set automatically + // when making an async call if we detect that someone has requested to + // listen for status messages. + internal bool SendStatus + { + get + { return (((Flags & (int)tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_SEND_STATUS) != 0) ? true : false); } + set + { + Flags = (value == false) ? (Flags & (int)~tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_SEND_STATUS) : + (Flags | (int)tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_SEND_STATUS); + } + } + + /// + /// Returns a copy of the object. + /// + /// + /// The cloned object. + /// + public abstract object Clone(); + } + + + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + /// + /// Provides a base class for query and enumeration-related options + /// objects. + /// Use this class to customize enumeration of management + /// objects, traverse management object relationships, or query for + /// management objects. + /// + /// + /// using System; + /// using System.Management; + /// + /// // This example demonstrates how to enumerate all top-level WMI classes + /// // and subclasses in root/cimv2 namespace. + /// class Sample_EnumerationOptions + /// { + /// public static int Main(string[] args) { + /// ManagementClass newClass = new ManagementClass(); + /// EnumerationOptions options = new EnumerationOptions(); + /// options.EnumerateDeep = false; + /// foreach(ManagementObject o in newClass.GetSubclasses(options)) { + /// Console.WriteLine(o["__Class"]); + /// } + /// return 0; + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// ' This example demonstrates how to enumerate all top-level WMI classes + /// ' and subclasses in root/cimv2 namespace. + /// Class Sample_EnumerationOptions + /// Overloads Public Shared Function Main(args() As String) As Integer + /// Dim newClass As New ManagementClass() + /// Dim options As New EnumerationOptions() + /// options.EnumerateDeep = False + /// Dim o As ManagementObject + /// For Each o In newClass.GetSubclasses(options) + /// Console.WriteLine(o("__Class")) + /// Next o + /// Return 0 + /// End Function + /// End Class + /// + /// + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + public class EnumerationOptions : ManagementOptions + { + private int blockSize; + + /// + /// Gets or sets a value indicating whether the invoked operation should be + /// performed in a synchronous or semisynchronous fashion. If this property is set + /// to , the enumeration is invoked and the call returns immediately. The actual + /// retrieval of the results will occur when the resulting collection is walked. + /// + /// + /// if the invoked operation should + /// be performed in a synchronous or semisynchronous fashion; otherwise, + /// . The default value is . + /// + public bool ReturnImmediately + { + get { return (((Flags & (int)tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_RETURN_IMMEDIATELY) != 0) ? true : false); } + set { + Flags = (value == false) ? (Flags & (int)~tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_RETURN_IMMEDIATELY) : + (Flags | (int)tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_RETURN_IMMEDIATELY); + } + } + + /// + /// Gets or sets the block size + /// for block operations. When enumerating through a collection, WMI will return results in + /// groups of the specified size. + /// + /// + /// The default value is 1. + /// + public int BlockSize + { + get { return blockSize; } + set { + //Unfortunately BlockSize was defined as int, but valid values are only > 0 + if (value <= 0) + throw new ArgumentOutOfRangeException("value"); + + blockSize = value; + } + } + + /// + /// Gets or sets a value indicating whether the collection is assumed to be + /// rewindable. If , the objects in the + /// collection will be kept available for multiple enumerations. If + /// , the collection + /// can only be enumerated one time. + /// + /// + /// if the collection is assumed to + /// be rewindable; otherwise, . The default value is + /// . + /// + /// + /// A rewindable collection is more costly in memory + /// consumption as all the objects need to be kept available at the same time. + /// In a collection defined as non-rewindable, the objects are discarded after being returned + /// in the enumeration. + /// + public bool Rewindable + { + get { return (((Flags & (int)tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_FORWARD_ONLY) != 0) ? false : true); } + set { + Flags = (value == true) ? (Flags & (int)~tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_FORWARD_ONLY) : + (Flags | (int)tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_FORWARD_ONLY); + } + } + + /// + /// Gets or sets a value indicating whether the objects returned from + /// WMI should contain amended information. Typically, amended information is localizable + /// information attached to the WMI object, such as object and property + /// descriptions. + /// + /// + /// if the objects returned from WMI + /// should contain amended information; otherwise, . The + /// default value is . + /// + /// + /// If descriptions and other amended information are not of + /// interest, setting this property to + /// is more + /// efficient. + /// + public bool UseAmendedQualifiers + { + get { return (((Flags & (int)tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_USE_AMENDED_QUALIFIERS) != 0) ? true : false); } + set { + Flags = (value == true) ? (Flags | (int)tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_USE_AMENDED_QUALIFIERS) : + (Flags & (int)~tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_USE_AMENDED_QUALIFIERS); + } + } + + /// + /// Gets or sets a value indicating whether to the objects returned should have + /// locatable information in them. This ensures that the system properties, such as + /// , , and + /// , are non-NULL. This flag can only be used in queries, + /// and is ignored in enumerations. + /// + /// + /// if WMI + /// should ensure all returned objects have valid paths; otherwise, + /// . The default value is . + /// + public bool EnsureLocatable + { + get + { return (((Flags & (int)tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_ENSURE_LOCATABLE) != 0) ? true : false); } + set + { Flags = (value == true) ? (Flags | (int)tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_ENSURE_LOCATABLE) : + (Flags & (int)~tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_ENSURE_LOCATABLE) ; } + } + + + /// + /// Gets or sets a value indicating whether the query should return a + /// prototype of the result set instead of the actual results. This flag is used for + /// prototyping. + /// + /// + /// if the + /// query should return a prototype of the result set instead of the actual results; + /// otherwise, . The default value is + /// . + /// + public bool PrototypeOnly + { + get + { return (((Flags & (int)tag_WBEM_QUERY_FLAG_TYPE.WBEM_FLAG_PROTOTYPE) != 0) ? true : false); } + set + { Flags = (value == true) ? (Flags | (int)tag_WBEM_QUERY_FLAG_TYPE.WBEM_FLAG_PROTOTYPE) : + (Flags & (int)~tag_WBEM_QUERY_FLAG_TYPE.WBEM_FLAG_PROTOTYPE) ; } + } + + /// + /// Gets or sets a value indicating whether direct access to the WMI provider is requested for the specified class, + /// without any regard to its base class or derived classes. + /// + /// + /// if only + /// objects of the specified class should be received, without regard to derivation + /// or inheritance; otherwise, . The default value is + /// . + /// + public bool DirectRead + { + get + { return (((Flags & (int)tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_DIRECT_READ) != 0) ? true : false); } + set + { Flags = (value == true) ? (Flags | (int)tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_DIRECT_READ) : + (Flags & (int)~tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_DIRECT_READ) ; } + } + + + /// + /// Gets or sets a value indicating whether recursive enumeration is requested + /// into all classes derived from the specified base class. If + /// , only immediate derived + /// class members are returned. + /// + /// + /// if recursive enumeration is requested + /// into all classes derived from the specified base class; otherwise, + /// . The default value is . + /// + public bool EnumerateDeep + { + get + { return (((Flags & (int)tag_WBEM_QUERY_FLAG_TYPE.WBEM_FLAG_SHALLOW) != 0) ? false : true); } + set + { Flags = (value == false) ? (Flags | (int)tag_WBEM_QUERY_FLAG_TYPE.WBEM_FLAG_SHALLOW) : + (Flags & (int)~tag_WBEM_QUERY_FLAG_TYPE.WBEM_FLAG_SHALLOW); } + } + + + //default constructor + /// + /// Initializes a new instance + /// of the class. + /// + /// + /// Initializes a new instance of the + /// class with default values (see the individual property descriptions + /// for what the default values are). This is the default constructor. + /// + public EnumerationOptions() : this (null, InfiniteTimeout, 1, true, true, false, false, false, false, false) {} + + + + //Constructor that specifies flags as individual values - we need to set the flags acordingly ! + /// + /// Initializes a new instance of the class to be used for queries or enumerations, + /// allowing the user to specify values for the different options. + /// + /// The options context object containing provider-specific information that can be passed through to the provider. + /// The timeout value for enumerating through the results. + /// The number of items to retrieve at one time from WMI. + /// to specify whether the result set is rewindable (=allows multiple traversal or one-time); otherwise, . + /// to specify whether the operation should return immediately (semi-sync) or block until all results are available; otherwise, . + /// to specify whether the returned objects should contain amended (locale-aware) qualifiers; otherwise, . + /// to specify to WMI that it should ensure all returned objects have valid paths; otherwise, . + /// to return a prototype of the result set instead of the actual results; otherwise, . + /// to to retrieve objects of only the specified class only or from derived classes as well; otherwise, . + /// to specify recursive enumeration in subclasses; otherwise, . + public EnumerationOptions( + ManagementNamedValueCollection context, + TimeSpan timeout, + int blockSize, + bool rewindable, + bool returnImmediatley, + bool useAmendedQualifiers, + bool ensureLocatable, + bool prototypeOnly, + bool directRead, + bool enumerateDeep) : base(context, timeout) + { + BlockSize = blockSize; + Rewindable = rewindable; + ReturnImmediately = returnImmediatley; + UseAmendedQualifiers = useAmendedQualifiers; + EnsureLocatable = ensureLocatable; + PrototypeOnly = prototypeOnly; + DirectRead = directRead; + EnumerateDeep = enumerateDeep; + } + + /// + /// Returns a copy of the object. + /// + /// + /// The cloned object. + /// + public override object Clone () + { + ManagementNamedValueCollection newContext = null; + + if (null != Context) + newContext = (ManagementNamedValueCollection)Context.Clone(); + + return new EnumerationOptions (newContext, Timeout, blockSize, Rewindable, + ReturnImmediately, UseAmendedQualifiers, EnsureLocatable, PrototypeOnly, DirectRead, EnumerateDeep); + } + + }//EnumerationOptions + + + + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + /// + /// Specifies options for management event watching. + /// Use this class to customize subscriptions for watching management events. + /// + /// + /// using System; + /// using System.Management; + /// + /// // This example demonstrates how to listen to an event using ManagementEventWatcher object. + /// class Sample_EventWatcherOptions + /// { + /// public static int Main(string[] args) { + /// ManagementClass newClass = new ManagementClass(); + /// newClass["__CLASS"] = "TestDeletionClass"; + /// newClass.Put(); + /// + /// EventWatcherOptions options = new EventWatcherOptions(); + /// ManagementEventWatcher watcher = new ManagementEventWatcher(null, + /// new WqlEventQuery("__classdeletionevent"), + /// options); + /// MyHandler handler = new MyHandler(); + /// watcher.EventArrived += new EventArrivedEventHandler(handler.Arrived); + /// watcher.Start(); + /// + /// // Delete class to trigger event + /// newClass.Delete(); + /// + /// //For the purpose of this example, we will wait + /// // two seconds before main thread terminates. + /// System.Threading.Thread.Sleep(2000); + /// + /// watcher.Stop(); + /// + /// return 0; + /// } + /// + /// public class MyHandler + /// { + /// public void Arrived(object sender, EventArrivedEventArgs e) { + /// Console.WriteLine("Class Deleted= " + + /// ((ManagementBaseObject)e.NewEvent["TargetClass"])["__CLASS"]); + /// } + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// ' This example demonstrates how to listen to an event using the ManagementEventWatcher object. + /// Class Sample_EventWatcherOptions + /// Public Shared Sub Main() + /// Dim newClass As New ManagementClass() + /// newClass("__CLASS") = "TestDeletionClass" + /// newClass.Put() + /// + /// Dim options As _ + /// New EventWatcherOptions() + /// Dim watcher As New ManagementEventWatcher( _ + /// Nothing, _ + /// New WqlEventQuery("__classdeletionevent"), _ + /// options) + /// Dim handler As New MyHandler() + /// AddHandler watcher.EventArrived, AddressOf handler.Arrived + /// watcher.Start() + /// + /// ' Delete class to trigger event + /// newClass.Delete() + /// + /// ' For the purpose of this example, we will wait + /// ' two seconds before main thread terminates. + /// System.Threading.Thread.Sleep(2000) + /// watcher.Stop() + /// End Sub + /// + /// Public Class MyHandler + /// Public Sub Arrived(sender As Object, e As EventArrivedEventArgs) + /// Console.WriteLine("Class Deleted = " & _ + /// CType(e.NewEvent("TargetClass"), ManagementBaseObject)("__CLASS")) + /// End Sub + /// End Class + /// End Class + /// + /// + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + public class EventWatcherOptions : ManagementOptions + { + private int blockSize = 1; + + /// + /// Gets or sets the block size for block operations. When waiting for events, this + /// value specifies how many events to wait for before returning. + /// + /// + /// The default value is 1. + /// + public int BlockSize + { + get { return blockSize; } + set + { + blockSize = value; + FireIdentifierChanged (); + } + + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Initializes a new instance of the class for event watching, using default values. + /// This is the default constructor. + /// + public EventWatcherOptions() + : this (null, InfiniteTimeout, 1) {} + + /// + /// Initializes a new instance of the class with the given + /// values. + /// + /// The options context object containing provider-specific information to be passed through to the provider. + /// The timeout to wait for the next events. + /// The number of events to wait for in each block. + public EventWatcherOptions(ManagementNamedValueCollection context, TimeSpan timeout, int blockSize) + : base(context, timeout) + { + Flags = (int)tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_RETURN_IMMEDIATELY|(int)tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_FORWARD_ONLY; + BlockSize = blockSize; + } + + /// + /// Returns a copy of the object. + /// + /// + /// The cloned object. + /// + public override object Clone () + { + ManagementNamedValueCollection newContext = null; + + if (null != Context) + newContext = (ManagementNamedValueCollection)Context.Clone(); + + return new EventWatcherOptions (newContext, Timeout, blockSize); + } + }//EventWatcherOptions + + + + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + /// + /// Specifies options for getting a management object. + /// Use this class to customize retrieval of a management object. + /// + /// + /// using System; + /// using System.Management; + /// + /// // This example demonstrates how to set a timeout value and list + /// // all amended qualifiers in a ManagementClass object. + /// class Sample_ObjectGetOptions + /// { + /// public static int Main(string[] args) { + /// // Request amended qualifiers + /// ObjectGetOptions options = + /// new ObjectGetOptions(null, new TimeSpan(0,0,0,5), true); + /// ManagementClass diskClass = + /// new ManagementClass("root/cimv2", "Win32_Process", options); + /// foreach(QualifierData qualifier in diskClass.Qualifiers) { + /// Console.WriteLine(qualifier.Name + ":" + qualifier.Value); + /// } + /// return 0; + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// ' This example demonstrates how to set a timeout value and list + /// ' all amended qualifiers in a ManagementClass object. + /// Class Sample_ObjectGetOptions + /// Overloads Public Shared Function Main(args() As String) As Integer + /// ' Request amended qualifiers + /// Dim options As _ + /// New ObjectGetOptions(Nothing, New TimeSpan(0, 0, 0, 5), True) + /// Dim diskClass As New ManagementClass( _ + /// "root/cimv2", _ + /// "Win32_Process", _ + /// options) + /// Dim qualifier As QualifierData + /// For Each qualifier In diskClass.Qualifiers + /// Console.WriteLine(qualifier.Name & ":" & qualifier.Value) + /// Next qualifier + /// Return 0 + /// End Function + /// End Class + /// + /// + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + public class ObjectGetOptions : ManagementOptions + { + internal static ObjectGetOptions _Clone(ObjectGetOptions options) + { + return ObjectGetOptions._Clone(options, null); + } + + internal static ObjectGetOptions _Clone(ObjectGetOptions options, IdentifierChangedEventHandler handler) + { + ObjectGetOptions optionsTmp; + + if (options != null) + optionsTmp = new ObjectGetOptions(options.context, options.timeout, options.UseAmendedQualifiers); + else + optionsTmp = new ObjectGetOptions(); + + // Wire up change handler chain. Use supplied handler, if specified; + // otherwise, default to that of the path argument. + if (handler != null) + optionsTmp.IdentifierChanged += handler; + else if (options != null) + optionsTmp.IdentifierChanged += new IdentifierChangedEventHandler(options.HandleIdentifierChange); + + return optionsTmp; + } + + /// + /// Gets or sets a value indicating whether the objects returned from WMI should + /// contain amended information. Typically, amended information is localizable information + /// attached to the WMI object, such as object and property descriptions. + /// + /// + /// if the objects returned from WMI + /// should contain amended information; otherwise, . The + /// default value is . + /// + public bool UseAmendedQualifiers + { + get { return (((Flags & (int)tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_USE_AMENDED_QUALIFIERS) != 0) ? true : false); } + set { + Flags = (value == true) ? (Flags | (int)tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_USE_AMENDED_QUALIFIERS) : + (Flags & (int)~tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_USE_AMENDED_QUALIFIERS); + FireIdentifierChanged(); + } + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Initializes a new instance of the class for getting a WMI object, using + /// default values. This is the default constructor. + /// + public ObjectGetOptions() : this(null, InfiniteTimeout, false) {} + + /// + /// Initializes a new instance of the class for getting a WMI object, using the + /// specified provider-specific context. + /// + /// A provider-specific, named-value pairs context object to be passed through to the provider. + public ObjectGetOptions(ManagementNamedValueCollection context) : this(context, InfiniteTimeout, false) {} + + /// + /// Initializes a new instance of the class for getting a WMI object, + /// using the given options values. + /// + /// A provider-specific, named-value pairs context object to be passed through to the provider. + /// The length of time to let the operation perform before it times out. The default is . + /// if the returned objects should contain amended (locale-aware) qualifiers; otherwise, . + public ObjectGetOptions(ManagementNamedValueCollection context, TimeSpan timeout, bool useAmendedQualifiers) : base(context, timeout) + { + UseAmendedQualifiers = useAmendedQualifiers; + } + + /// + /// Returns a copy of the object. + /// + /// + /// The cloned object. + /// + public override object Clone () + { + ManagementNamedValueCollection newContext = null; + + if (null != Context) + newContext = (ManagementNamedValueCollection)Context.Clone(); + + return new ObjectGetOptions (newContext, Timeout, UseAmendedQualifiers); + } + } + + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + /// + /// Specifies options for committing management + /// object changes. + /// Use this class to customize how values are saved to a management object. + /// + /// + /// using System; + /// using System.Management; + /// + /// // This example demonstrates how to specify a PutOptions using + /// // PutOptions object when saving a ManagementClass object to + /// // the WMI respository. + /// class Sample_PutOptions + /// { + /// public static int Main(string[] args) { + /// ManagementClass newClass = new ManagementClass("root/default", + /// String.Empty, + /// null); + /// newClass["__Class"] = "class999xc"; + /// + /// PutOptions options = new PutOptions(); + /// options.Type = PutType.UpdateOnly; + /// + /// try + /// { + /// newClass.Put(options); //will fail if the class doesn't already exist + /// } + /// catch (ManagementException e) + /// { + /// Console.WriteLine("Couldn't update class: " + e.ErrorCode); + /// } + /// return 0; + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// ' This example demonstrates how to specify a PutOptions using + /// ' PutOptions object when saving a ManagementClass object to + /// ' WMI respository. + /// Class Sample_PutOptions + /// Overloads Public Shared Function Main(args() As String) As Integer + /// Dim newClass As New ManagementClass( _ + /// "root/default", _ + /// String.Empty, _ + /// Nothing) + /// newClass("__Class") = "class999xc" + /// + /// Dim options As New PutOptions() + /// options.Type = PutType.UpdateOnly 'will fail if the class doesn't already exist + /// + /// Try + /// newClass.Put(options) + /// Catch e As ManagementException + /// Console.WriteLine("Couldn't update class: " & e.ErrorCode) + /// End Try + /// Return 0 + /// End Function + /// End Class + /// + /// + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + public class PutOptions : ManagementOptions + { + + /// + /// Gets or sets a value indicating whether the objects returned from WMI should + /// contain amended information. Typically, amended information is localizable information + /// attached to the WMI object, such as object and property descriptions. + /// + /// + /// if the objects returned from WMI + /// should contain amended information; otherwise, . The + /// default value is . + /// + public bool UseAmendedQualifiers + { + get { return (((Flags & (int)tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_USE_AMENDED_QUALIFIERS) != 0) ? true : false); } + set { Flags = (value == true) ? (Flags | (int)tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_USE_AMENDED_QUALIFIERS) : + (Flags & (int)~tag_WBEM_GENERIC_FLAG_TYPE.WBEM_FLAG_USE_AMENDED_QUALIFIERS); } + } + + /// + /// Gets or sets the type of commit to be performed for the object. + /// + /// + /// The default value is . + /// + public PutType Type + { + get { return (((Flags & (int)tag_WBEM_CHANGE_FLAG_TYPE.WBEM_FLAG_UPDATE_ONLY) != 0) ? PutType.UpdateOnly : + ((Flags & (int)tag_WBEM_CHANGE_FLAG_TYPE.WBEM_FLAG_CREATE_ONLY) != 0) ? PutType.CreateOnly : + PutType.UpdateOrCreate); + } + set { + switch (value) + { + case PutType.UpdateOnly : Flags |= (int)tag_WBEM_CHANGE_FLAG_TYPE.WBEM_FLAG_UPDATE_ONLY; break; + case PutType.CreateOnly : Flags |= (int)tag_WBEM_CHANGE_FLAG_TYPE.WBEM_FLAG_CREATE_ONLY; break; + case PutType.UpdateOrCreate : Flags |= (int)tag_WBEM_CHANGE_FLAG_TYPE.WBEM_FLAG_CREATE_OR_UPDATE; break; + default : throw new ArgumentException(null, "Type"); + } + } + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Initializes a new instance of the class for put operations, using default values. + /// This is the default constructor. + /// + public PutOptions() : this(null, InfiniteTimeout, false, PutType.UpdateOrCreate) {} + + /// + /// Initializes a new instance of the class for committing a WMI object, using the + /// specified provider-specific context. + /// + /// A provider-specific, named-value pairs context object to be passed through to the provider. + public PutOptions(ManagementNamedValueCollection context) : this(context, InfiniteTimeout, false, PutType.UpdateOrCreate) {} + + /// + /// Initializes a new instance of the class for committing a WMI object, using + /// the specified option values. + /// + /// A provider-specific, named-value pairs object to be passed through to the provider. + /// The length of time to let the operation perform before it times out. The default is . + /// if the returned objects should contain amended (locale-aware) qualifiers; otherwise, . + /// The type of commit to be performed (update or create). + public PutOptions(ManagementNamedValueCollection context, TimeSpan timeout, bool useAmendedQualifiers, PutType putType) : base(context, timeout) + { + UseAmendedQualifiers = useAmendedQualifiers; + Type = putType; + } + + /// + /// Returns a copy of the object. + /// + /// + /// The cloned object. + /// + public override object Clone () + { + ManagementNamedValueCollection newContext = null; + + if (null != Context) + newContext = (ManagementNamedValueCollection)Context.Clone(); + + return new PutOptions (newContext, Timeout, UseAmendedQualifiers, Type); + } + } + + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + /// + /// Specifies options for deleting a management + /// object. + /// + /// + /// using System; + /// using System.Management; + /// + /// // This example demonstrates how to specify a timeout value + /// // when deleting a ManagementClass object. + /// class Sample_DeleteOptions + /// { + /// public static int Main(string[] args) { + /// ManagementClass newClass = new ManagementClass(); + /// newClass["__CLASS"] = "ClassToDelete"; + /// newClass.Put(); + /// + /// // Set deletion options: delete operation timeout value + /// DeleteOptions opt = new DeleteOptions(null, new TimeSpan(0,0,0,5)); + /// + /// ManagementClass dummyClassToDelete = + /// new ManagementClass("ClassToDelete"); + /// dummyClassToDelete.Delete(opt); + /// + /// return 0; + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// ' This sample demonstrates how to specify a timeout value + /// ' when deleting a ManagementClass object. + /// Class Sample_DeleteOptions + /// Overloads Public Shared Function Main(args() As String) As Integer + /// Dim newClass As New ManagementClass() + /// newClass("__CLASS") = "ClassToDelete" + /// newClass.Put() + /// + /// ' Set deletion options: delete operation timeout value + /// Dim opt As New DeleteOptions(Nothing, New TimeSpan(0, 0, 0, 5)) + /// + /// Dim dummyClassToDelete As New ManagementClass("ClassToDelete") + /// dummyClassToDelete.Delete(opt) + /// Return 0 + /// End Function + /// End Class + /// + /// + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + public class DeleteOptions : ManagementOptions + { + /// + /// Initializes a new instance of the class. + /// + /// + /// Initializes a new instance of the class for the delete operation, using default values. + /// This is the default constructor. + /// + public DeleteOptions() : base () {} + + /// + /// Initializes a new instance of the class for a delete operation, using + /// the specified values. + /// + /// A provider-specific, named-value pairs object to be passed through to the provider. + /// The length of time to let the operation perform before it times out. The default value is . Setting this parameter will invoke the operation semisynchronously. + public DeleteOptions(ManagementNamedValueCollection context, TimeSpan timeout) : base(context, timeout) {} + + /// + /// Returns a copy of the object. + /// + /// + /// A cloned object. + /// + public override object Clone () + { + ManagementNamedValueCollection newContext = null; + + if (null != Context) + newContext = (ManagementNamedValueCollection)Context.Clone(); + + return new DeleteOptions (newContext, Timeout); + } + } + + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + /// + /// Specifies options for invoking a management method. + /// Use this class to customize the execution of a method on a management + /// object. + /// + /// + /// using System; + /// using System.Management; + /// + /// // This example demonstrates how to stop a system service. + /// class Sample_InvokeMethodOptions + /// { + /// public static int Main(string[] args) { + /// ManagementObject service = + /// new ManagementObject("win32_service=\"winmgmt\""); + /// InvokeMethodOptions options = new InvokeMethodOptions(); + /// options.Timeout = new TimeSpan(0,0,0,5); + /// + /// ManagementBaseObject outParams = service.InvokeMethod("StopService", null, options); + /// + /// Console.WriteLine("Return Status = " + outParams["ReturnValue"]); + /// + /// return 0; + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// ' This sample demonstrates how to stop a system service. + /// Class Sample_InvokeMethodOptions + /// Overloads Public Shared Function Main(args() As String) As Integer + /// Dim service As New ManagementObject("win32_service=""winmgmt""") + /// Dim options As New InvokeMethodOptions() + /// options.Timeout = New TimeSpan(0, 0, 0, 5) + /// + /// Dim outParams As ManagementBaseObject = service.InvokeMethod( _ + /// "StopService", _ + /// Nothing, _ + /// options) + /// + /// Console.WriteLine("Return Status = " & _ + /// outParams("ReturnValue").ToString()) + /// + /// Return 0 + /// End Function + /// End Class + /// + /// + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + public class InvokeMethodOptions : ManagementOptions + { + /// + /// Initializes a new instance of the class. + /// + /// + /// Initializes a new instance of the class for the operation, using default values. + /// This is the default constructor. + /// + public InvokeMethodOptions() : base () {} + + /// + /// Initializes a new instance of the class for an invoke operation using + /// the specified values. + /// + /// A provider-specific, named-value pairs object to be passed through to the provider. + /// The length of time to let the operation perform before it times out. The default value is . Setting this parameter will invoke the operation semisynchronously. + public InvokeMethodOptions(ManagementNamedValueCollection context, TimeSpan timeout) : base(context, timeout) {} + + /// + /// Returns a copy of the object. + /// + /// + /// The cloned object. + /// + public override object Clone () + { + ManagementNamedValueCollection newContext = null; + + if (null != Context) + newContext = (ManagementNamedValueCollection)Context.Clone(); + + return new InvokeMethodOptions (newContext, Timeout); + } + } + + + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + /// + /// Specifies all settings required to make a WMI connection. + /// Use this class to customize a connection to WMI made via a + /// ManagementScope object. + /// + /// + /// using System; + /// using System.Management; + /// + /// // This example demonstrates how to connect to remote machine + /// // using supplied credentials. + /// class Sample_ConnectionOptions + /// { + /// public static int Main(string[] args) { + /// ConnectionOptions options = new ConnectionOptions(); + /// options.Username = "domain\\username"; + /// options.Password = "password"; + /// ManagementScope scope = new ManagementScope( + /// "\\\\servername\\root\\cimv2", + /// options); + /// try { + /// scope.Connect(); + /// ManagementObject disk = new ManagementObject( + /// scope, + /// new ManagementPath("Win32_logicaldisk='c:'"), + /// null); + /// disk.Get(); + /// } + /// catch (Exception e) { + /// Console.WriteLine("Failed to connect: " + e.Message); + /// } + /// return 0; + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// ' This example demonstrates how to connect to remote machine + /// ' using supplied credentials. + /// Class Sample_ConnectionOptions + /// Overloads Public Shared Function Main(args() As String) As Integer + /// Dim options As New ConnectionOptions() + /// options.Username = "domain\username" + /// options.Password = "password" + /// Dim scope As New ManagementScope("\\servername\root\cimv2", options) + /// Try + /// scope.Connect() + /// Dim disk As New ManagementObject(scope, _ + /// New ManagementPath("Win32_logicaldisk='c:'"), Nothing) + /// disk.Get() + /// Catch e As UnauthorizedAccessException + /// Console.WriteLine(("Failed to connect: " + e.Message)) + /// End Try + /// Return 0 + /// End Function + /// End Class + /// + /// + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + public class ConnectionOptions : ManagementOptions + { + + internal const string DEFAULTLOCALE = null; + internal const string DEFAULTAUTHORITY = null; + internal const ImpersonationLevel DEFAULTIMPERSONATION = ImpersonationLevel.Impersonate; + internal const AuthenticationLevel DEFAULTAUTHENTICATION = AuthenticationLevel.Unchanged; + internal const bool DEFAULTENABLEPRIVILEGES = false; + + //Fields + private string locale; + private string username; + private SecureString securePassword = null; + private string authority; + private ImpersonationLevel impersonation; + private AuthenticationLevel authentication; + private bool enablePrivileges; + + + // + //Properties + // + + /// + /// Gets or sets the locale to be used for the connection operation. + /// + /// + /// The default value is DEFAULTLOCALE. + /// + public string Locale + { + get { return (null != locale) ? locale : String.Empty; } + set { + if (locale != value) + { + locale = value; + FireIdentifierChanged(); + } + } + } + + /// + /// Gets or sets the user name to be used for the connection operation. + /// + /// + /// Null if the connection will use the currently logged-on user; otherwise, a string representing the user name. The default value is null. + /// + /// + /// If the user name is from a domain other than the current + /// domain, the string may contain the domain name and user name, separated by a backslash: + /// + /// string username = "EnterDomainHere\\EnterUsernameHere"; + /// + /// + public string Username + { + get { return username; } + set { + if (username != value) + { + username = value; + FireIdentifierChanged(); + } + } + } + + /// + /// Sets the password for the specified user. The value can be set, but not retrieved. + /// + /// + /// The default value is null. If the user name is also + /// null, the credentials used will be those of the currently logged-on user. + /// + /// + /// A blank string ("") specifies a valid + /// zero-length password. + /// + public string Password + { + set { + if( value != null) + { + if (securePassword == null) + { + securePassword = new SecureString(); + for( int i=0; i + /// Sets the secure password for the specified user. The value can be set, but not retrieved. + /// + /// + /// The default value is null. If the user name is also + /// null, the credentials used will be those of the currently logged-on user. + /// + /// + /// A blank securestring ("") specifies a valid + /// zero-length password. + /// + public SecureString SecurePassword + { + set{ + if( value != null) + { + if( securePassword == null) + { + securePassword = value.Copy(); + } + else + { + securePassword.Clear(); + securePassword = value.Copy(); + FireIdentifierChanged(); + } + } + else + { + if (securePassword != null) + { + securePassword.Dispose(); + securePassword = null; + FireIdentifierChanged(); + } + } + } + } + + /// + /// Gets or sets the authority to be used to authenticate the specified user. + /// + /// + /// If not null, this property can contain the name of the + /// Windows NT/Windows 2000 domain in which to obtain the user to + /// authenticate. + /// + /// + /// + /// The property must be passed + /// as follows: If it begins with the string "Kerberos:", Kerberos + /// authentication will be used and this property should contain a Kerberos principal name. For + /// example, Kerberos:<principal name>. + /// If the property value begins with the string "NTLMDOMAIN:", NTLM + /// authentication will be used and the property should contain a NTLM domain name. + /// For example, NTLMDOMAIN:<domain name>. + /// If the property is null, NTLM authentication will be used and the NTLM domain + /// of the current user will be used. + /// + public string Authority + { + get { return (null != authority) ? authority : String.Empty; } + set { + if (authority != value) + { + authority = value; + FireIdentifierChanged(); + } + } + } + + /// + /// Gets or sets the COM impersonation level to be used for operations in this connection. + /// + /// + /// The COM impersonation level to be used for operations in + /// this connection. The default value is , which indicates that the WMI provider can + /// impersonate the client when performing the requested operations in this connection. + /// + /// + /// The setting is advantageous when the provider is + /// a trusted application or service. It eliminates the need for the provider to + /// perform client identity and access checks for the requested operations. However, + /// note that if for some reason the provider cannot be trusted, allowing it to + /// impersonate the client may constitute a security threat. In such cases, it is + /// recommended that this property be set by the client to a lower value, such as + /// . Note that this may cause failure of the + /// provider to perform the requested operations, for lack of sufficient permissions + /// or inability to perform access checks. + /// + public ImpersonationLevel Impersonation + { + get { return impersonation; } + set { + if (impersonation != value) + { + impersonation = value; + FireIdentifierChanged(); + } + } + } + + /// + /// Gets or sets the COM authentication level to be used for operations in this connection. + /// + /// + /// The COM authentication level to be used for operations + /// in this connection. The default value is , which indicates that the + /// client will use the authentication level requested by the server, according to + /// the standard DCOM negotiation process. + /// + /// + /// On Windows 2000 and below, the WMI service will request + /// Connect level authentication, while on Windows XP and higher it will request + /// Packet level authentication. If the client requires a specific authentication + /// setting, this property can be used to control the authentication level on this + /// particular connection. For example, the property can be set to + /// if the + /// client requires all communication to be encrypted. + /// + public AuthenticationLevel Authentication + { + get { return authentication; } + set { + if (authentication != value) + { + authentication = value; + FireIdentifierChanged(); + } + } + } + + /// + /// Gets or sets a value indicating whether user privileges need to be enabled for + /// the connection operation. This property should only be used when the operation + /// performed requires a certain user privilege to be enabled + /// (for example, a machine reboot). + /// + /// + /// if user privileges need to be + /// enabled for the connection operation; otherwise, . The + /// default value is . + /// + public bool EnablePrivileges + { + get { return enablePrivileges; } + set { + if (enablePrivileges != value) + { + enablePrivileges = value; + FireIdentifierChanged(); + } + } + } + + // + //Constructors + // + + //default + /// + /// Initializes a new instance of the class. + /// + /// + /// Initializes a new instance of the class for the connection operation, using default values. This is the + /// default constructor. + /// + public ConnectionOptions () : + this (DEFAULTLOCALE, null, (string)null, DEFAULTAUTHORITY, + DEFAULTIMPERSONATION, DEFAULTAUTHENTICATION, + DEFAULTENABLEPRIVILEGES, null, InfiniteTimeout) {} + + + //parameterized + /// + /// Initializes a new instance of the class to be used for a WMI + /// connection, using the specified values. + /// + /// The locale to be used for the connection. + /// The user name to be used for the connection. If null, the credentials of the currently logged-on user are used. + /// The password for the given user name. If the user name is also null, the credentials used will be those of the currently logged-on user. + /// The authority to be used to authenticate the specified user. + /// The COM impersonation level to be used for the connection. + /// The COM authentication level to be used for the connection. + /// to enable special user privileges; otherwise, . This parameter should only be used when performing an operation that requires special Windows NT user privileges. + /// A provider-specific, named value pairs object to be passed through to the provider. + /// Reserved for future use. + public ConnectionOptions (string locale, + string username, string password, string authority, + ImpersonationLevel impersonation, AuthenticationLevel authentication, + bool enablePrivileges, + ManagementNamedValueCollection context, TimeSpan timeout) : base (context, timeout) + { + if (locale != null) + this.locale = locale; + + this.username = username; + this.enablePrivileges = enablePrivileges; + + if (password != null) + { + this.securePassword = new SecureString(); + for( int i=0; i + /// Initializes a new instance of the class to be used for a WMI + /// connection, using the specified values. + /// + /// The locale to be used for the connection. + /// The user name to be used for the connection. If null, the credentials of the currently logged-on user are used. + /// The secure password for the given user name. If the user name is also null, the credentials used will be those of the currently logged-on user. + /// The authority to be used to authenticate the specified user. + /// The COM impersonation level to be used for the connection. + /// The COM authentication level to be used for the connection. + /// to enable special user privileges; otherwise, . This parameter should only be used when performing an operation that requires special Windows NT user privileges. + /// A provider-specific, named value pairs object to be passed through to the provider. + /// Reserved for future use. + public ConnectionOptions (string locale, + string username, SecureString password, string authority, + ImpersonationLevel impersonation, AuthenticationLevel authentication, + bool enablePrivileges, + ManagementNamedValueCollection context, TimeSpan timeout) : base (context, timeout) + { + if (locale != null) + this.locale = locale; + + this.username = username; + this.enablePrivileges = enablePrivileges; + + if (password != null) + { + this.securePassword = password.Copy(); + } + + if (authority != null) + this.authority = authority; + + if (impersonation != 0) + this.impersonation = impersonation; + + if (authentication != 0) + this.authentication = authentication; + } + + /// + /// Returns a copy of the object. + /// + /// + /// The cloned object. + /// + public override object Clone () + { + ManagementNamedValueCollection newContext = null; + + if (null != Context) + newContext = (ManagementNamedValueCollection)Context.Clone(); + return new ConnectionOptions (locale, username, GetSecurePassword (), + authority, impersonation, authentication, enablePrivileges, newContext, Timeout); + } + + // + //Methods + // + + internal IntPtr GetPassword() + { + if (securePassword != null) + { + try{ + return System.Runtime.InteropServices.Marshal.SecureStringToBSTR(securePassword); + } + catch(OutOfMemoryException) + { + return IntPtr.Zero; + } + } + else + return IntPtr.Zero; + } + internal SecureString GetSecurePassword() + { + if (securePassword != null) + return securePassword.Copy(); + else + return null; + } + + internal ConnectionOptions(ManagementNamedValueCollection context, TimeSpan timeout, int flags) : base(context, timeout, flags) {} + + internal ConnectionOptions(ManagementNamedValueCollection context) : base(context, InfiniteTimeout) {} + + internal static ConnectionOptions _Clone(ConnectionOptions options) + { + return ConnectionOptions._Clone(options, null); + } + + internal static ConnectionOptions _Clone(ConnectionOptions options, IdentifierChangedEventHandler handler) + { + ConnectionOptions optionsTmp; + + if (options != null) + { + optionsTmp = new ConnectionOptions(options.Context, options.Timeout, options.Flags); + + optionsTmp.locale = options.locale; + + optionsTmp.username = options.username; + optionsTmp.enablePrivileges = options.enablePrivileges; + + if (options.securePassword != null) + { + optionsTmp.securePassword = options.securePassword.Copy(); + } + else + optionsTmp.securePassword = null; + + if (options.authority != null) + optionsTmp.authority = options.authority; + + if (options.impersonation != 0) + optionsTmp.impersonation = options.impersonation; + + if (options.authentication != 0) + optionsTmp.authentication = options.authentication; + } + else + optionsTmp = new ConnectionOptions(); + + // Wire up change handler chain. Use supplied handler, if specified; + // otherwise, default to that of the path argument. + if (handler != null) + optionsTmp.IdentifierChanged += handler; + else if (options != null) + optionsTmp.IdentifierChanged += new IdentifierChangedEventHandler(options.HandleIdentifierChange); + + return optionsTmp; + } + + }//ConnectionOptions +} diff --git a/external/corefx/src/System.Management/src/System/Management/ManagementPath.cs b/external/corefx/src/System.Management/src/System/Management/ManagementPath.cs new file mode 100644 index 0000000000..deeabc2f5a --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/ManagementPath.cs @@ -0,0 +1,1111 @@ +// 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.Diagnostics; +using System.ComponentModel; +using System.Runtime.InteropServices; +using System.Globalization; +using System.Reflection; +using System.ComponentModel.Design.Serialization; + +namespace System.Management +{ + /// + /// Provides a wrapper for parsing and building paths to WMI objects. + /// + /// + /// using System; + /// using System.Management; + /// + /// // This sample displays all properties in a ManagementPath object. + /// + /// class Sample_ManagementPath + /// { + /// public static int Main(string[] args) { + /// ManagementPath path = new ManagementPath( "\\\\MyServer\\MyNamespace:Win32_logicaldisk='c:'"); + /// + /// // Results of full path parsing + /// Console.WriteLine("Path: " + path.Path); + /// Console.WriteLine("RelativePath: " + path.RelativePath); + /// Console.WriteLine("Server: " + path.Server); + /// Console.WriteLine("NamespacePath: " + path.NamespacePath); + /// Console.WriteLine("ClassName: " + path.ClassName); + /// Console.WriteLine("IsClass: " + path.IsClass); + /// Console.WriteLine("IsInstance: " + path.IsInstance); + /// Console.WriteLine("IsSingleton: " + path.IsSingleton); + /// + /// // Change a portion of the full path + /// path.Server = "AnotherServer"; + /// Console.WriteLine("New Path: " + path.Path); + /// return 0; + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// 'This sample displays all properties in a ManagementPath object. + /// Class Sample_ManagementPath Overloads + /// Public Shared Function Main(args() As String) As Integer + /// Dim path As _ New + /// ManagementPath("\\MyServer\MyNamespace:Win32_LogicalDisk='c:'") + /// + /// ' Results of full path parsing + /// Console.WriteLine("Path: " & path.Path) + /// Console.WriteLine("RelativePath: " & path.RelativePath) + /// Console.WriteLine("Server: " & path.Server) + /// Console.WriteLine("NamespacePath: " & path.NamespacePath) + /// Console.WriteLine("ClassName: " & path.ClassName) + /// Console.WriteLine("IsClass: " & path.IsClass) + /// Console.WriteLine("IsInstance: " & path.IsInstance) + /// Console.WriteLine("IsSingleton: " & path.IsSingleton) + /// + /// ' Change a portion of the full path + /// path.Server= "AnotherServer" + /// Console.WriteLine("New Path: " & path.Path) + /// Return 0 + /// End Function + /// End Class + /// + /// + [TypeConverter(typeof(ManagementPathConverter ))] + public class ManagementPath : ICloneable + { + private static ManagementPath defaultPath = new ManagementPath("//./root/cimv2"); + + //Used to minimize the cases in which new wbemPath (WMI object path parser) objects need to be constructed + //This is done for performance reasons. + private bool isWbemPathShared = false; + + internal event IdentifierChangedEventHandler IdentifierChanged; + + //Fires IdentifierChanged event + private void FireIdentifierChanged() + { + if (IdentifierChanged != null) + IdentifierChanged(this, null); + } + + //internal factory + /// + /// Internal static "factory" method for making a new ManagementPath + /// from the system property of a WMI object + /// + /// The WMI object whose __PATH property will + /// be used to supply the returned object + internal static string GetManagementPath ( + IWbemClassObjectFreeThreaded wbemObject) + { + string path = null; + int status = (int)ManagementStatus.Failed; + + if (null != wbemObject) + { + int dummy1 = 0, dummy2 = 0; + object val = null; + status = wbemObject.Get_ ("__PATH", 0, ref val, ref dummy1, ref dummy2); + if ((status < 0) || (val == System.DBNull.Value)) + { + //try to get the relpath instead + status = wbemObject.Get_ ("__RELPATH", 0, ref val, ref dummy1, ref dummy2); + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + if (System.DBNull.Value == val) + path = null; + else + path = (string)val; + } + + return path; + } + + //Used internally to check whether a string passed in as a namespace is indeed syntactically correct + //for a namespace (e.g. either has "\" or "/" in it or is the special case of "root") + //This doesn't check for the existance of that namespace, nor does it guarrantee correctness. + internal static bool IsValidNamespaceSyntax(string nsPath) + { + if (nsPath.Length != 0) + { + // Any path separators present? + char[] pathSeparators = { '\\', '/' }; + if (nsPath.IndexOfAny(pathSeparators) == -1) + { + // No separators. The only valid path is "root". + if (String.Compare("root", nsPath, StringComparison.OrdinalIgnoreCase) != 0) + return false; + } + } + + return true; + } + + + internal static ManagementPath _Clone(ManagementPath path) + { + return ManagementPath._Clone(path, null); + } + + internal static ManagementPath _Clone(ManagementPath path, IdentifierChangedEventHandler handler) + { + ManagementPath pathTmp = new ManagementPath(); + + // Wire up change handler chain. Use supplied handler, if specified; + // otherwise, default to that of the path argument. + if (handler != null) + pathTmp.IdentifierChanged = handler; + + // Assign ManagementPath IWbemPath to this.wmiPath. + // Optimization for performance : As long as the path is only read, we share this interface. + // On the first write, a private copy will be needed; + // isWbemPathShared signals ManagementPath to create such a copy at write-time. + if (path != null && path.wmiPath != null) + { + pathTmp.wmiPath = path.wmiPath; + pathTmp.isWbemPathShared = path.isWbemPathShared = true; + } + + return pathTmp; + } + + /// + /// Initializes a new instance + /// of the class. + /// + /// + /// Initializes a new instance of the class that is empty. This is the default constructor. + /// + public ManagementPath () : this ((string) null) {} + + /// + /// Initializes a new instance of the class for the given path. + /// + /// The object path. + public ManagementPath(string path) + { + if ((null != path) && (0 < path.Length)) + wmiPath = CreateWbemPath(path); + } + + /// + /// Returns the full object path as the string representation. + /// + /// + /// A string containing the full object + /// path represented by this object. This value is equivalent to the value of the + /// property. + /// + public override string ToString () + { + return this.Path; + } + + /// + /// Returns a copy of the . + /// + /// + /// The cloned object. + /// + public ManagementPath Clone () + { + return new ManagementPath (Path); + } + + /// + /// Standard Clone returns a copy of this ManagementPath as a generic "Object" type + /// + /// + /// The cloned object. + /// + object ICloneable.Clone () + { + return Clone (); + } + + /// + /// Gets or sets the default scope path used when no scope is specified. + /// The default scope is /-/ \\.\root\cimv2, and can be changed by setting this property. + /// + /// + /// By default the scope value is /-/ \\.\root\cimv2, or a different scope path if + /// the default was changed. + /// + public static ManagementPath DefaultPath + { + get { return ManagementPath.defaultPath; } + set { ManagementPath.defaultPath = value; } + } + + //private members + private IWbemPath wmiPath; + + private IWbemPath CreateWbemPath(string path) + { + IWbemPath wbemPath = (IWbemPath)MTAHelper.CreateInMTA(typeof(WbemDefPath));//new WbemDefPath(); + SetWbemPath(wbemPath, path); + return wbemPath; + } + + private void SetWbemPath(string path) + { + // Test/utilize isWbemPathShared *only* on public + internal members! + if (wmiPath == null) + wmiPath = CreateWbemPath(path); + else + SetWbemPath(wmiPath, path); + } + + private static void SetWbemPath(IWbemPath wbemPath, string path) + { + if (null != wbemPath) + { + uint flags = (uint) tag_WBEM_PATH_CREATE_FLAG.WBEMPATH_CREATE_ACCEPT_ALL; + + //For now we have to special-case the "root" namespace - + // this is because in the case of "root", the path parser cannot tell whether + // this is a namespace name or a class name + if (String.Compare(path, "root", StringComparison.OrdinalIgnoreCase) == 0) + flags = flags | (uint) tag_WBEM_PATH_CREATE_FLAG.WBEMPATH_TREAT_SINGLE_IDENT_AS_NS; + + int status = wbemPath.SetText_(flags, path); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + } + + private string GetWbemPath() + { + return GetWbemPath(this.wmiPath); + } + + private static string GetWbemPath(IWbemPath wbemPath) + { + String pathStr = String.Empty; + + if (null != wbemPath) + { + // Requesting the path from a parser which has + // been only given a relative path results in an incorrect + // value being returned (e.g. \\.\win32_logicaldisk). To work + // around this we check if there are any namespaces, + // and if not ask for the relative path instead. + int flags = (int)tag_WBEM_GET_TEXT_FLAGS.WBEMPATH_GET_SERVER_TOO; + uint nCount = 0; + + int status = (int)ManagementStatus.NoError; + + status = wbemPath.GetNamespaceCount_(out nCount); + + if (status >= 0) + { + if (0 == nCount) + flags = (int)tag_WBEM_GET_TEXT_FLAGS.WBEMPATH_GET_RELATIVE_ONLY; + + // Get the space we need to reserve + uint bufLen = 0; + + status = wbemPath.GetText_(flags, ref bufLen, null); + + if (status >= 0 && 0 < bufLen) + { + pathStr = new String ('0', (int) bufLen-1); + status = wbemPath.GetText_(flags, ref bufLen, pathStr); + } + } + + if (status < 0) + { + if (status == (int)tag_WBEMSTATUS.WBEM_E_INVALID_PARAMETER) + { + // Interpret as unspecified - return "" + } + + else if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + return pathStr; + } + + private void ClearKeys (bool setAsSingleton) + { + // Test/utilize isWbemPathShared *only* on public + internal members! + int status = (int)ManagementStatus.NoError; + + try + { + if (null != wmiPath) + { + IWbemPathKeyList keyList = null; + status = wmiPath.GetKeyList_(out keyList); + + if (null != keyList) + { + status = keyList.RemoveAllKeys_(0); + if ((status & 0x80000000) == 0) + { + sbyte bSingleton = (setAsSingleton) ? (sbyte)(-1) : (sbyte)0; + status = keyList.MakeSingleton_(bSingleton); + FireIdentifierChanged (); + } + } + } + } + catch (COMException e) + { + ManagementException.ThrowWithExtendedInfo(e); + } + + if ((status & 0xfffff000) == 0x80041000) + { + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + } + else if ((status & 0x80000000) != 0) + { + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + internal bool IsEmpty + { + get + { + return (Path.Length == 0 ) ; + } + } + + + // + // Methods + // + + /// + /// Sets the path as a new class path. This means that the path must have + /// a class name but not key values. + /// + public void SetAsClass () + { + if (IsClass || IsInstance) + { + // Check if this IWbemPath is shared among multiple managed objects. + // With this write, it will have to maintain its own copy. + if (isWbemPathShared) + { + wmiPath = CreateWbemPath(this.GetWbemPath()); + isWbemPathShared = false; + } + + ClearKeys (false); + } + else + throw new ManagementException (ManagementStatus.InvalidOperation, null, null); + } + + /// + /// Sets the path as a new singleton object path. This means that it is a path to an instance but + /// there are no key values. + /// + public void SetAsSingleton () + { + if (IsClass || IsInstance) + { + // Check if this IWbemPath is shared among multiple managed objects. + // With this write, it will have to maintain its own copy. + if (isWbemPathShared) + { + wmiPath = CreateWbemPath(this.GetWbemPath()); + isWbemPathShared = false; + } + + ClearKeys (true); + } + else + throw new ManagementException (ManagementStatus.InvalidOperation, null, null); + } + + // + // Properties + // + + /// + /// Gets or sets the string representation of the full path in the object. + /// + /// + /// A string containing the full path + /// represented in this object. + /// + [RefreshProperties(RefreshProperties.All)] + public string Path + { + get + { + return this.GetWbemPath(); + } + set + { + try + { + // Before overwriting, check it's OK + // Note, we've never done such validation, should we? + // + // Check if this IWbemPath is shared among multiple managed objects. + // With this write, it will have to maintain its own copy. + if (isWbemPathShared) + { + wmiPath = CreateWbemPath(this.GetWbemPath()); + isWbemPathShared = false; + } + + this.SetWbemPath(value); + } + catch + { + throw new ArgumentOutOfRangeException ("value"); + } + FireIdentifierChanged(); + } + } + + /// + /// Gets or sets the relative path: class name and keys only. + /// + /// + /// A string containing the relative + /// path (not including the server and namespace portions) represented in this + /// object. + /// + [RefreshProperties(RefreshProperties.All)] + public string RelativePath + { + get + { + String pathStr = String.Empty; + + if (null != wmiPath) + { + // Get the space we need to reserve + uint bufLen = 0; + int status = wmiPath.GetText_( + (int) tag_WBEM_GET_TEXT_FLAGS.WBEMPATH_GET_RELATIVE_ONLY, + ref bufLen, + null); + + if (status >= 0 && 0 < bufLen) + { + pathStr = new String ('0', (int) bufLen-1); + status = wmiPath.GetText_( + (int) tag_WBEM_GET_TEXT_FLAGS.WBEMPATH_GET_RELATIVE_ONLY, + ref bufLen, + pathStr); + } + + if (status < 0) + { + if (status == (int)tag_WBEMSTATUS.WBEM_E_INVALID_PARAMETER) + { + // Interpret as unspecified - return "" + } + else if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + return pathStr; + } + + set + { + try + { + // No need for isWbemPathShared here since internal SetRelativePath + // always creates a new copy. + SetRelativePath (value); + } + catch (COMException) + { + throw new ArgumentOutOfRangeException ("value"); + } + FireIdentifierChanged(); + } + } + + internal void SetRelativePath (string relPath) + { + // No need for isWbemPathShared here since internal SetRelativePath + // always creates a new copy. + ManagementPath newPath = new ManagementPath (relPath); + newPath.NamespacePath = this.GetNamespacePath((int)tag_WBEM_GET_TEXT_FLAGS.WBEMPATH_GET_SERVER_AND_NAMESPACE_ONLY); + newPath.Server = this.Server; + wmiPath = newPath.wmiPath; + } + + //Used to update the relative path when the user changes any key properties + internal void UpdateRelativePath(string relPath) + { + if (relPath == null) + return; + + //Get the server & namespace part from the existing path, and concatenate the given relPath. + //NOTE : we need to do this because IWbemPath doesn't have a function to set the relative path alone... + string newPath = String.Empty; + string nsPath = this.GetNamespacePath((int)tag_WBEM_GET_TEXT_FLAGS.WBEMPATH_GET_SERVER_AND_NAMESPACE_ONLY); + + if (nsPath.Length>0 ) + newPath = String.Concat(nsPath, ":", relPath); + else + newPath = relPath; + + // Check if this IWbemPath is shared among multiple managed objects. + // With this write, it will have to maintain its own copy. + if (isWbemPathShared) + { + wmiPath = CreateWbemPath(this.GetWbemPath()); + isWbemPathShared = false; + } + + this.SetWbemPath(newPath); + } + + + /// + /// Gets or sets the server part of the path. + /// + /// + /// A string containing the server name + /// from the path represented in this object. + /// + [RefreshProperties(RefreshProperties.All)] + public string Server + { + get + { + String pathStr = String.Empty; + + if (null != wmiPath) + { + + uint uLen = 0; + int status = wmiPath.GetServer_(ref uLen, null); + + if (status >= 0 && 0 < uLen) + { + pathStr = new String ('0', (int) uLen-1); + status = wmiPath.GetServer_(ref uLen, pathStr); + } + + if (status < 0) + { + if (status == (int)tag_WBEMSTATUS.WBEM_E_NOT_AVAILABLE) + { + // Interpret as unspecified - return "" + } + else if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + return pathStr; + } + set + { + String oldValue = Server; + + // Only set if changed + if (0 != String.Compare(oldValue,value,StringComparison.OrdinalIgnoreCase)) + { + if (null == wmiPath) + wmiPath = (IWbemPath)MTAHelper.CreateInMTA(typeof(WbemDefPath));//new WbemDefPath (); + else if (isWbemPathShared) + { + // Check if this IWbemPath is shared among multiple managed objects. + // With this write, it will have to maintain its own copy. + wmiPath = CreateWbemPath(this.GetWbemPath()); + isWbemPathShared = false; + } + + int status = wmiPath.SetServer_(value); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + FireIdentifierChanged(); + } + } + } + + internal string SetNamespacePath(string nsPath, out bool bChange) + { + int status = (int)ManagementStatus.NoError; + string nsOrg = null; + string nsNew = null; + IWbemPath wmiPathTmp = null; + bChange = false; + + Debug.Assert(nsPath != null); + + //Do some validation on the path to make sure it is a valid namespace path (at least syntactically) + if (!IsValidNamespaceSyntax(nsPath)) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)tag_WBEMSTATUS.WBEM_E_INVALID_NAMESPACE); + + wmiPathTmp = CreateWbemPath(nsPath); + if (wmiPath == null) + wmiPath = this.CreateWbemPath(""); + else if (isWbemPathShared) + { + // Check if this IWbemPath is shared among multiple managed objects. + // With this write, it will have to maintain its own copy. + wmiPath = CreateWbemPath(this.GetWbemPath()); + isWbemPathShared = false; + } + + nsOrg = GetNamespacePath(wmiPath, + (int)tag_WBEM_GET_TEXT_FLAGS.WBEMPATH_GET_NAMESPACE_ONLY); + nsNew = GetNamespacePath(wmiPathTmp, + (int)tag_WBEM_GET_TEXT_FLAGS.WBEMPATH_GET_NAMESPACE_ONLY); + + if (String.Compare(nsOrg, nsNew, StringComparison.OrdinalIgnoreCase) != 0) + { + wmiPath.RemoveAllNamespaces_(); // Out with the old... Ignore status code. + + // Add the new ones in + bChange = true; // Now dirty from above. + uint nCount = 0; + status = wmiPathTmp.GetNamespaceCount_(out nCount); + + if (status >= 0) + { + for (uint i = 0; i < nCount; i++) + { + uint uLen = 0; + status = wmiPathTmp.GetNamespaceAt_(i, ref uLen, null); + + if (status >= 0) + { + string nSpace = new String('0', (int) uLen-1); + status = wmiPathTmp.GetNamespaceAt_(i, ref uLen, nSpace); + if (status >= 0) + { + status = wmiPath.SetNamespaceAt_(i, nSpace); + + if (status < 0) + break; + } + else + break; + } + else + break; + } + } + } + else {;} // Continue on. Could have different server name, same ns specified. + + // + // Update Server property if specified in the namespace. + // eg: "\\MyServer\root\cimv2". + // + if (status >= 0 && nsPath.Length > 1 && + (nsPath[0] == '\\' && nsPath[1] == '\\' || + nsPath[0] == '/' && nsPath[1] == '/')) + { + uint uLen = 0; + status = wmiPathTmp.GetServer_(ref uLen, null); + + if (status >= 0 && uLen > 0) + { + string serverNew = new String ('0', (int) uLen-1); + status = wmiPathTmp.GetServer_(ref uLen, serverNew); + + if (status >= 0) + { + // Compare server name on this object, if specified, to the caller's. + // Update this object if different or unspecified. + uLen = 0; + status = wmiPath.GetServer_(ref uLen, null); // NB: Cannot use property get since it may throw. + + if (status >= 0) + { + string serverOrg = new String('0', (int)uLen-1); + status = wmiPath.GetServer_(ref uLen, serverOrg); + + if (status >= 0 && String.Compare(serverOrg, serverNew, StringComparison.OrdinalIgnoreCase) != 0) + status = wmiPath.SetServer_(serverNew); + } + else if (status == (int)tag_WBEMSTATUS.WBEM_E_NOT_AVAILABLE) + { + status = wmiPath.SetServer_(serverNew); + if (status >= 0) + bChange = true; + } + } + } + else if (status == (int)tag_WBEMSTATUS.WBEM_E_NOT_AVAILABLE) // No caller-supplied server name; + status = (int)ManagementStatus.NoError; // Ignore error. + } + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + return nsNew; + } + + internal string GetNamespacePath(int flags) + { + return GetNamespacePath(wmiPath, flags); + } + + internal static string GetNamespacePath(IWbemPath wbemPath, int flags) + { + string pathStr = String.Empty; + + if (null != wbemPath) + { + // Requesting the namespace path from a parser which has + // been only given a relative path results in an incorrect + // value being returned (e.g. \\.\). To work + // around this, check if there are any namespaces, + // and if not just return "". + uint nCount = 0; + int status = (int)ManagementStatus.NoError; + + status = wbemPath.GetNamespaceCount_(out nCount); + + if (status >= 0 && nCount > 0) + { + // Get the space we need to reserve + uint bufLen = 0; + status = wbemPath.GetText_(flags, ref bufLen, null); + + if (status >= 0 && bufLen > 0) + { + pathStr = new String ('0', (int) bufLen-1); + status = wbemPath.GetText_(flags, ref bufLen, pathStr); + } + } + + if (status < 0) + { + if (status == (int)tag_WBEMSTATUS.WBEM_E_INVALID_PARAMETER) + { + // Interpret as unspecified - return "" + } + else if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + return pathStr; + } + + /// + /// Gets or sets the namespace part of the path. Note that this does not include + /// the server name, which can be retrieved separately. + /// + /// + /// A string containing the namespace + /// portion of the path represented in this object. + /// + [RefreshProperties(RefreshProperties.All)] + public string NamespacePath + { + get + { + return GetNamespacePath((int)tag_WBEM_GET_TEXT_FLAGS.WBEMPATH_GET_NAMESPACE_ONLY); + } + set + { + bool bChange = false; + + try + { + // isWbemPathShared handled in internal SetNamespacePath. + SetNamespacePath(value, out bChange); + } + catch (COMException) + { + throw new ArgumentOutOfRangeException ("value"); + } + + if (bChange) + FireIdentifierChanged(); + } + } + + /// + /// Gets or sets the class portion of the path. + /// + /// + /// A string containing the name of the + /// class. + /// + [RefreshProperties(RefreshProperties.All)] + public string ClassName + { + get + { + return internalClassName; + } + set + { + String oldValue = ClassName; + + // Only set if changed + if (0 != String.Compare(oldValue,value,StringComparison.OrdinalIgnoreCase)) + { + // isWbemPathShared handled in internal className property accessor. + internalClassName = value; + FireIdentifierChanged(); + } + } + } + + internal string internalClassName + { + get + { + String pathStr = String.Empty; + int status = (int)ManagementStatus.NoError; + + if (null != wmiPath) + { + uint bufLen = 0; + status = wmiPath.GetClassName_(ref bufLen, null); + + if (status >= 0 && 0 < bufLen) + { + pathStr = new String ('0', (int) bufLen-1); + status = wmiPath.GetClassName_(ref bufLen, pathStr); + + if (status < 0) + pathStr = String.Empty; + } + } + + return pathStr; + } + set + { + int status = (int)ManagementStatus.NoError; + + if (wmiPath == null) + wmiPath = (IWbemPath)MTAHelper.CreateInMTA(typeof(WbemDefPath));//new WbemDefPath(); + else if (isWbemPathShared) + { + // Check if this IWbemPath is shared among multiple managed objects. + // With this write, it will have to maintain its own copy. + wmiPath = CreateWbemPath(this.GetWbemPath()); + isWbemPathShared = false; + } + + try + { + status = wmiPath.SetClassName_(value); + } + catch (COMException) + { + throw new ArgumentOutOfRangeException ("value"); + } + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + } + + /// + /// Gets or sets a value indicating whether this is a class path. + /// + /// + /// if this is a class path; otherwise, + /// . + /// + public bool IsClass + { + get + { + if (null == wmiPath) + return false; + + ulong uInfo = 0; + int status = wmiPath.GetInfo_(0, out uInfo); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + return (0 != (uInfo & (ulong)tag_WBEM_PATH_STATUS_FLAG.WBEMPATH_INFO_IS_CLASS_REF)); + } + } + + /// + /// Gets or sets a value indicating whether this is an instance path. + /// + /// + /// if this is an instance path; otherwise, + /// . + /// + public bool IsInstance + { + get + { + if (null == wmiPath) + return false; + + ulong uInfo = 0; + int status = wmiPath.GetInfo_(0, out uInfo); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + return (0 != (uInfo & (ulong)tag_WBEM_PATH_STATUS_FLAG.WBEMPATH_INFO_IS_INST_REF)); + } + } + + /// + /// Gets or sets a value indicating whether this is a singleton instance path. + /// + /// + /// if this is a singleton instance path; otherwise, + /// . + /// + public bool IsSingleton + { + get + { + if (null == wmiPath) + return false; + + ulong uInfo = 0; + int status = wmiPath.GetInfo_(0, out uInfo); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + return (0 != (uInfo & (ulong)tag_WBEM_PATH_STATUS_FLAG.WBEMPATH_INFO_IS_SINGLETON)); + } + } + } + + /// + /// Converts a String to a ManagementPath + /// + class ManagementPathConverter : ExpandableObjectConverter + { + + /// + /// Determines if this converter can convert an object in the given source type to the native type of the converter. + /// + /// An ITypeDescriptorContext that provides a format context. + /// A Type that represents the type you wish to convert from. + /// + /// true if this converter can perform the conversion; otherwise, false. + /// + public override Boolean CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if ((sourceType == typeof(ManagementPath))) + { + return true; + } + return base.CanConvertFrom(context,sourceType); + } + + /// + /// Gets a value indicating whether this converter can convert an object to the given destination type using the context. + /// + /// An ITypeDescriptorContext that provides a format context. + /// A Type that represents the type you wish to convert to. + /// + /// true if this converter can perform the conversion; otherwise, false. + /// + public override Boolean CanConvertTo(ITypeDescriptorContext context, Type destinationType) + { + if ((destinationType == typeof(InstanceDescriptor))) + { + return true; + } + return base.CanConvertTo(context,destinationType); + } + + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + /// An ITypeDescriptorContext that provides a format context. + /// A CultureInfo object. If a null reference (Nothing in Visual Basic) is passed, the current culture is assumed. + /// The Object to convert. + /// The Type to convert the value parameter to. + /// An Object that represents the converted value. + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + { + + if (destinationType == null) + { + throw new ArgumentNullException("destinationType"); + } + + if (value is ManagementPath && destinationType == typeof(InstanceDescriptor)) + { + ManagementPath obj = ((ManagementPath)(value)); + ConstructorInfo ctor = typeof(ManagementPath).GetConstructor(new Type[] {typeof(System.String)}); + if (ctor != null) + { + return new InstanceDescriptor(ctor, new object[] {obj.Path}); + } + } + return base.ConvertTo(context,culture,value,destinationType); + } + } +} diff --git a/external/corefx/src/System.Management/src/System/Management/ManagementQuery.cs.REMOVED.git-id b/external/corefx/src/System.Management/src/System/Management/ManagementQuery.cs.REMOVED.git-id new file mode 100644 index 0000000000..27b96e14d6 --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/ManagementQuery.cs.REMOVED.git-id @@ -0,0 +1 @@ +5fcceeaa08b2706923db033331e3cbebdcfeca57 \ No newline at end of file diff --git a/external/corefx/src/System.Management/src/System/Management/ManagementScope.cs b/external/corefx/src/System.Management/src/System/Management/ManagementScope.cs new file mode 100644 index 0000000000..abbed466f4 --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/ManagementScope.cs @@ -0,0 +1,1559 @@ +// 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.ComponentModel; +using System.Runtime.InteropServices; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.ComponentModel.Design.Serialization; +using System.Security; +using System.Security.Permissions; +using Microsoft.Win32; + +namespace System.Management +{ + internal static class CompatSwitches + { + internal const string DotNetVersion = "v4.0.30319"; + private const string RegKeyLocation =@"SOFTWARE\Microsoft\.NETFramework\" + DotNetVersion; + + private static readonly object s_syncLock = new object(); + private static int s_allowManagementObjectQI; + + private const string c_WMIDisableCOMSecurity = "WMIDisableCOMSecurity"; + + public static bool AllowIManagementObjectQI + { + get + { + if (s_allowManagementObjectQI == 0) + { + lock (s_syncLock) + { + if (s_allowManagementObjectQI == 0) + { + s_allowManagementObjectQI = GetSwitchValueFromRegistry() == true ? 1 : -1; + } + } + } + + return s_allowManagementObjectQI == 1 ? true : false; + } + } + + private static bool GetSwitchValueFromRegistry() + { + RegistryKey s_switchesRegKey = null; + try + { + s_switchesRegKey = Registry.LocalMachine.OpenSubKey(RegKeyLocation); + + if (s_switchesRegKey == null) + { + return false; + } + + return ((int)s_switchesRegKey.GetValue(c_WMIDisableCOMSecurity, -1 /* default */) == 1); + } + + // ignore exceptions so that we don't crash the process if we can't read the switch value + catch (Exception e) + { + if (e is StackOverflowException || + e is OutOfMemoryException || + e is System.Threading.ThreadAbortException || + e is AccessViolationException) + throw; + } + finally + { + // dispose of the key + if (s_switchesRegKey != null) + { + s_switchesRegKey.Dispose(); + } + } + + // if for any reason we cannot retrieve the value of the switch from the Registry, + // fallback to 'false' which is the secure behavior + return false; + } + } + + internal static class WmiNetUtilsHelper + { + internal delegate int ResetSecurity(IntPtr hToken); + internal delegate int SetSecurity([In][Out] ref bool pNeedtoReset, [In][Out] ref IntPtr pHandle); + internal delegate int BlessIWbemServices([MarshalAs(UnmanagedType.Interface)] IWbemServices pIUnknown, + [In][MarshalAs(UnmanagedType.BStr)] string strUser, + IntPtr password, + [In][MarshalAs(UnmanagedType.BStr)] string strAuthority, + int impersonationLevel, + int authenticationLevel); + internal delegate int BlessIWbemServicesObject([MarshalAs(UnmanagedType.IUnknown)] object pIUnknown, + [In][MarshalAs(UnmanagedType.BStr)] string strUser, + IntPtr password, + [In][MarshalAs(UnmanagedType.BStr)] string strAuthority, + int impersonationLevel, + int authenticationLevel); + + internal delegate int GetPropertyHandle(int vFunc, IntPtr pWbemClassObject, [In][MarshalAs(UnmanagedType.LPWStr)] string wszPropertyName, [Out] out Int32 pType, [Out] out Int32 plHandle); + internal delegate int WritePropertyValue(int vFunc, IntPtr pWbemClassObject, [In] Int32 lHandle, [In] Int32 lNumBytes, [In][MarshalAs(UnmanagedType.LPWStr)] string str); + internal delegate int GetQualifierSet(int vFunc, IntPtr pWbemClassObject, [Out] out IntPtr ppQualSet); + internal delegate int Get(int vFunc, IntPtr pWbemClassObject, [In][MarshalAs(UnmanagedType.LPWStr)] string wszName, [In] Int32 lFlags, [In][Out] ref object pVal, [In][Out] ref Int32 pType, [In][Out] ref Int32 plFlavor); + internal delegate int Put(int vFunc, IntPtr pWbemClassObject, [In][MarshalAs(UnmanagedType.LPWStr)] string wszName, [In] Int32 lFlags, [In] ref object pVal, [In] Int32 Type); + internal delegate int Delete(int vFunc, IntPtr pWbemClassObject, [In][MarshalAs(UnmanagedType.LPWStr)] string wszName); + internal delegate int GetNames(int vFunc, IntPtr pWbemClassObject, [In][MarshalAs(UnmanagedType.LPWStr)] string wszQualifierName, [In] Int32 lFlags, [In] ref object pQualifierVal, [Out][MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_BSTR)] out string[] pNames); + internal delegate int BeginEnumeration(int vFunc, IntPtr pWbemClassObject, [In] Int32 lEnumFlags); + internal delegate int Next(int vFunc, IntPtr pWbemClassObject, [In] Int32 lFlags, [In][Out][MarshalAs(UnmanagedType.BStr)] ref string strName, [In][Out] ref object pVal, [In][Out] ref Int32 pType, [In][Out] ref Int32 plFlavor); + internal delegate int EndEnumeration(int vFunc, IntPtr pWbemClassObject); + internal delegate int GetPropertyQualifierSet(int vFunc, IntPtr pWbemClassObject, [In][MarshalAs(UnmanagedType.LPWStr)] string wszProperty, [Out] out IntPtr ppQualSet); + internal delegate int Clone(int vFunc, IntPtr pWbemClassObject, [Out] out IntPtr ppCopy); + internal delegate int GetObjectText(int vFunc, IntPtr pWbemClassObject, [In] Int32 lFlags, [Out][MarshalAs(UnmanagedType.BStr)] out string pstrObjectText); + internal delegate int SpawnDerivedClass(int vFunc, IntPtr pWbemClassObject, [In] Int32 lFlags, [Out] out IntPtr ppNewClass); + internal delegate int SpawnInstance(int vFunc, IntPtr pWbemClassObject, [In] Int32 lFlags, [Out] out IntPtr ppNewInstance); + internal delegate int CompareTo(int vFunc, IntPtr pWbemClassObject, [In] Int32 lFlags, [In] IntPtr pCompareTo); + internal delegate int GetPropertyOrigin(int vFunc, IntPtr pWbemClassObject, [In][MarshalAs(UnmanagedType.LPWStr)] string wszName, [Out][MarshalAs(UnmanagedType.BStr)] out string pstrClassName); + internal delegate int InheritsFrom(int vFunc, IntPtr pWbemClassObject, [In][MarshalAs(UnmanagedType.LPWStr)] string strAncestor); + internal delegate int GetMethod(int vFunc, IntPtr pWbemClassObject, [In][MarshalAs(UnmanagedType.LPWStr)] string wszName, [In] Int32 lFlags, [Out]out IntPtr ppInSignature, [Out] out IntPtr ppOutSignature); + internal delegate int PutMethod(int vFunc, IntPtr pWbemClassObject, [In][MarshalAs(UnmanagedType.LPWStr)] string wszName, [In] Int32 lFlags, [In] IntPtr pInSignature, [In] IntPtr pOutSignature); + internal delegate int DeleteMethod(int vFunc, IntPtr pWbemClassObject, [In][MarshalAs(UnmanagedType.LPWStr)] string wszName); + internal delegate int BeginMethodEnumeration(int vFunc, IntPtr pWbemClassObject, [In] Int32 lEnumFlags); + internal delegate int NextMethod(int vFunc, IntPtr pWbemClassObject, [In] Int32 lFlags, [Out][MarshalAs(UnmanagedType.BStr)] out string pstrName, [Out] out IntPtr ppInSignature, [Out] out IntPtr ppOutSignature); + internal delegate int EndMethodEnumeration(int vFunc, IntPtr pWbemClassObject); + internal delegate int GetMethodQualifierSet(int vFunc, IntPtr pWbemClassObject, [In][MarshalAs(UnmanagedType.LPWStr)] string wszMethod, [Out] out IntPtr ppQualSet); + internal delegate int GetMethodOrigin(int vFunc, IntPtr pWbemClassObject, [In][MarshalAs(UnmanagedType.LPWStr)] string wszMethodName, [Out][MarshalAs(UnmanagedType.BStr)] out string pstrClassName); + internal delegate int QualifierSet_Get(int vFunc, IntPtr pWbemClassObject, [In][MarshalAs(UnmanagedType.LPWStr)] string wszName, [In] Int32 lFlags, [In][Out] ref object pVal, [In][Out] ref Int32 plFlavor); + internal delegate int QualifierSet_Put(int vFunc, IntPtr pWbemClassObject, [In][MarshalAs(UnmanagedType.LPWStr)] string wszName, [In] ref object pVal, [In] Int32 lFlavor); + internal delegate int QualifierSet_Delete(int vFunc, IntPtr pWbemClassObject, [In][MarshalAs(UnmanagedType.LPWStr)] string wszName); + internal delegate int QualifierSet_GetNames(int vFunc, IntPtr pWbemClassObject, [In] Int32 lFlags, [Out][MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_BSTR)] out string[] pNames); + internal delegate int QualifierSet_BeginEnumeration(int vFunc, IntPtr pWbemClassObject, [In] Int32 lFlags); + internal delegate int QualifierSet_Next(int vFunc, IntPtr pWbemClassObject, [In] Int32 lFlags, [Out][MarshalAs(UnmanagedType.BStr)] out string pstrName, [Out] out object pVal, [Out] out Int32 plFlavor); + internal delegate int QualifierSet_EndEnumeration(int vFunc, IntPtr pWbemClassObject); + internal delegate int GetCurrentApartmentType(int vFunc, IntPtr pComThreadingInfo, [Out] out APTTYPE aptType); + internal delegate void VerifyClientKey(); + internal delegate int GetDemultiplexedStub([In,MarshalAs(UnmanagedType.IUnknown)]object pIUnknown, [In]bool isLocal, [Out,MarshalAs(UnmanagedType.IUnknown)]out object ppIUnknown); + internal delegate int CreateInstanceEnumWmi([In][MarshalAs(UnmanagedType.BStr)] string strFilter, + [In] Int32 lFlags, + [In][MarshalAs(UnmanagedType.Interface)] IWbemContext pCtx, + [Out][MarshalAs(UnmanagedType.Interface)] out IEnumWbemClassObject ppEnum, + [In] Int32 impLevel, + [In] Int32 authnLevel, + [In] [MarshalAs(UnmanagedType.Interface)] IWbemServices pCurrentNamespace, + [In][MarshalAs(UnmanagedType.BStr)] string strUser, + [In]IntPtr strPassword, + [In][MarshalAs(UnmanagedType.BStr)] string strAuthority + ); + internal delegate int CreateClassEnumWmi([In][MarshalAs(UnmanagedType.BStr)] string strSuperclass, + [In] Int32 lFlags, + [In][MarshalAs(UnmanagedType.Interface)] IWbemContext pCtx, + [Out][MarshalAs(UnmanagedType.Interface)] out IEnumWbemClassObject ppEnum, + [In] Int32 impLevel, + [In] Int32 authnLevel, + [In] [MarshalAs(UnmanagedType.Interface)] IWbemServices pCurrentNamespace, + [In][MarshalAs(UnmanagedType.BStr)] string strUser, + [In]IntPtr strPassword, + [In][MarshalAs(UnmanagedType.BStr)] string strAuthority + ); + internal delegate int ExecQueryWmi([In][MarshalAs(UnmanagedType.BStr)] string strQueryLanguage, + [In][MarshalAs(UnmanagedType.BStr)] string strQuery, + [In] Int32 lFlags, + [In][MarshalAs(UnmanagedType.Interface)] IWbemContext pCtx, + [Out][MarshalAs(UnmanagedType.Interface)] out IEnumWbemClassObject ppEnum, + [In] Int32 impLevel, + [In] Int32 authnLevel, + [In] [MarshalAs(UnmanagedType.Interface)] IWbemServices pCurrentNamespace, + [In][MarshalAs(UnmanagedType.BStr)] string strUser, + [In]IntPtr strPassword, + [In][MarshalAs(UnmanagedType.BStr)] string strAuthority + ); + internal delegate int ExecNotificationQueryWmi( [In][MarshalAs(UnmanagedType.BStr)] string strQueryLanguage, + [In][MarshalAs(UnmanagedType.BStr)] string strQuery, + [In] Int32 lFlags, + [In][MarshalAs(UnmanagedType.Interface)] IWbemContext pCtx, + [Out][MarshalAs(UnmanagedType.Interface)] out IEnumWbemClassObject ppEnum, + [In] Int32 impLevel, + [In] Int32 authnLevel, + [In] [MarshalAs(UnmanagedType.Interface)] IWbemServices pCurrentNamespace, + [In][MarshalAs(UnmanagedType.BStr)] string strUser, + [In]IntPtr strPassword, + [In][MarshalAs(UnmanagedType.BStr)] string strAuthority + ); + internal delegate int PutInstanceWmi([In] IntPtr pInst, + [In] Int32 lFlags, + [In][MarshalAs(UnmanagedType.Interface)] IWbemContext pCtx, + [In] IntPtr ppCallResult, + [In] Int32 impLevel, + [In] Int32 authnLevel, + [In] [MarshalAs(UnmanagedType.Interface)] IWbemServices pCurrentNamespace, + [In][MarshalAs(UnmanagedType.BStr)] string strUser, + [In]IntPtr strPassword, + [In][MarshalAs(UnmanagedType.BStr)] string strAuthority + ); + internal delegate int PutClassWmi([In] IntPtr pObject, + [In] Int32 lFlags, + [In][MarshalAs(UnmanagedType.Interface)] IWbemContext pCtx, + [In] IntPtr ppCallResult, + [In] Int32 impLevel, + [In] Int32 authnLevel, + [In] [MarshalAs(UnmanagedType.Interface)] IWbemServices pCurrentNamespace, + [In][MarshalAs(UnmanagedType.BStr)] string strUser, + [In]IntPtr strPassword, + [In][MarshalAs(UnmanagedType.BStr)] string strAuthority + ); + internal delegate int CloneEnumWbemClassObject( + [Out][MarshalAs(UnmanagedType.Interface)] out IEnumWbemClassObject ppEnum, + [In] Int32 impLevel, + [In] Int32 authnLevel, + [In] [MarshalAs(UnmanagedType.Interface)] IEnumWbemClassObject pCurrentEnumWbemClassObject, + [In][MarshalAs(UnmanagedType.BStr)] string strUser, + [In]IntPtr strPassword, + [In][MarshalAs(UnmanagedType.BStr)] string strAuthority + ); + internal delegate int ConnectServerWmi( + [In][MarshalAs(UnmanagedType.BStr)] string strNetworkResource, + [In][MarshalAs(UnmanagedType.BStr)] string strUser, + [In] IntPtr strPassword, + [In][MarshalAs(UnmanagedType.BStr)] string strLocale, + [In] Int32 lSecurityFlags, + [In][MarshalAs(UnmanagedType.BStr)] string strAuthority, + [In][MarshalAs(UnmanagedType.Interface)] IWbemContext pCtx, + [Out][MarshalAs(UnmanagedType.Interface)] out IWbemServices ppNamespace, + int impersonationLevel, + int authenticationLevel); + + internal delegate IntPtr GetErrorInfo(); + + internal delegate int Initialize([In]bool AllowIManagementObjectQI); + + + + + // 'Apartment Type' returned by IComThreadingInfo::GetCurrentApartmentType() + internal enum APTTYPE + { + APTTYPE_CURRENT = -1, + APTTYPE_STA = 0, + APTTYPE_MTA = 1, + APTTYPE_NA = 2, + APTTYPE_MAINSTA = 3 + } + internal static ResetSecurity ResetSecurity_f; + internal static SetSecurity SetSecurity_f; + internal static BlessIWbemServices BlessIWbemServices_f; + internal static BlessIWbemServicesObject BlessIWbemServicesObject_f; + internal static GetPropertyHandle GetPropertyHandle_f27; + internal static WritePropertyValue WritePropertyValue_f28; + internal static GetQualifierSet GetQualifierSet_f; + internal static Get Get_f; + internal static Put Put_f; + internal static Delete Delete_f; + internal static GetNames GetNames_f; + internal static BeginEnumeration BeginEnumeration_f; + internal static Next Next_f; + internal static EndEnumeration EndEnumeration_f; + internal static GetPropertyQualifierSet GetPropertyQualifierSet_f; + internal static Clone Clone_f; + internal static GetObjectText GetObjectText_f; + internal static SpawnDerivedClass SpawnDerivedClass_f; + internal static SpawnInstance SpawnInstance_f; + internal static CompareTo CompareTo_f; + internal static GetPropertyOrigin GetPropertyOrigin_f; + internal static InheritsFrom InheritsFrom_f; + internal static GetMethod GetMethod_f; + internal static PutMethod PutMethod_f; + internal static DeleteMethod DeleteMethod_f; + internal static BeginMethodEnumeration BeginMethodEnumeration_f; + internal static NextMethod NextMethod_f; + internal static EndMethodEnumeration EndMethodEnumeration_f; + internal static GetMethodQualifierSet GetMethodQualifierSet_f; + internal static GetMethodOrigin GetMethodOrigin_f; + internal static QualifierSet_Get QualifierGet_f; + internal static QualifierSet_Put QualifierPut_f; + internal static QualifierSet_Delete QualifierDelete_f; + internal static QualifierSet_GetNames QualifierGetNames_f; + internal static QualifierSet_BeginEnumeration QualifierBeginEnumeration_f; + internal static QualifierSet_Next QualifierNext_f; + internal static QualifierSet_EndEnumeration QualifierEndEnumeration_f; + internal static GetCurrentApartmentType GetCurrentApartmentType_f; + internal static VerifyClientKey VerifyClientKey_f; + internal static Clone Clone_f12; + internal static GetDemultiplexedStub GetDemultiplexedStub_f; + internal static CreateInstanceEnumWmi CreateInstanceEnumWmi_f; + internal static CreateClassEnumWmi CreateClassEnumWmi_f; + internal static ExecQueryWmi ExecQueryWmi_f; + internal static ExecNotificationQueryWmi ExecNotificationQueryWmi_f; + internal static PutInstanceWmi PutInstanceWmi_f; + internal static PutClassWmi PutClassWmi_f; + internal static CloneEnumWbemClassObject CloneEnumWbemClassObject_f; + internal static ConnectServerWmi ConnectServerWmi_f; + internal static GetErrorInfo GetErrorInfo_f; + internal static Initialize Initialize_f; + + static WmiNetUtilsHelper() + { + RegistryKey netFrameworkSubKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\.NETFramework\"); + string netFrameworkInstallRoot = (string)netFrameworkSubKey?.GetValue("InstallRoot"); + + if (netFrameworkInstallRoot == null) + { + // In some Windows versions, like Nano Server, the .Net Framework is not installed by default. + // It is possible that general failure to access the registry get to this code branch but it is + // very unlikely. + // Load PNSE delegates. This way it will throw PNSE when methods are used not when type is loaded. + LoadPlatformNotSupportedDelegates(SR.PlatformNotSupported_FullFrameworkRequired); + return; + } + + string wminet_utilsPath = Path.Combine( + netFrameworkInstallRoot, + CompatSwitches.DotNetVersion, // The same value is hard coded on Environment.Version and quirks for WMI + "wminet_utils.dll"); + + IntPtr hModule = Interop.Kernel32.LoadLibrary(wminet_utilsPath); + if (hModule == IntPtr.Zero) + { + // This is unlikely, so having the TypeInitializationException wrapping it is fine. + throw new Win32Exception(Marshal.GetLastWin32Error(), string.Format(SR.LoadLibraryFailed, wminet_utilsPath)); + } + + if (LoadDelegate(ref ResetSecurity_f, hModule, "ResetSecurity") && + LoadDelegate(ref SetSecurity_f, hModule, "SetSecurity") && + LoadDelegate(ref BlessIWbemServices_f, hModule, "BlessIWbemServices") && + LoadDelegate(ref BlessIWbemServicesObject_f, hModule, "BlessIWbemServicesObject") && + LoadDelegate(ref GetPropertyHandle_f27, hModule, "GetPropertyHandle") && + LoadDelegate(ref WritePropertyValue_f28, hModule, "WritePropertyValue") && + LoadDelegate(ref Clone_f12, hModule, "Clone") && + LoadDelegate(ref VerifyClientKey_f, hModule, "VerifyClientKey") && + LoadDelegate(ref GetQualifierSet_f, hModule, "GetQualifierSet") && + LoadDelegate(ref Get_f, hModule, "Get") && + LoadDelegate(ref Put_f, hModule, "Put") && + LoadDelegate(ref Delete_f, hModule, "Delete") && + LoadDelegate(ref GetNames_f, hModule, "GetNames") && + LoadDelegate(ref BeginEnumeration_f, hModule, "BeginEnumeration") && + LoadDelegate(ref Next_f, hModule, "Next") && + LoadDelegate(ref EndEnumeration_f, hModule, "EndEnumeration") && + LoadDelegate(ref GetPropertyQualifierSet_f, hModule, "GetPropertyQualifierSet") && + LoadDelegate(ref Clone_f, hModule, "Clone") && + LoadDelegate(ref GetObjectText_f, hModule, "GetObjectText") && + LoadDelegate(ref SpawnDerivedClass_f, hModule, "SpawnDerivedClass") && + LoadDelegate(ref SpawnInstance_f, hModule, "SpawnInstance") && + LoadDelegate(ref CompareTo_f, hModule, "CompareTo") && + LoadDelegate(ref GetPropertyOrigin_f, hModule, "GetPropertyOrigin") && + LoadDelegate(ref InheritsFrom_f, hModule, "InheritsFrom") && + LoadDelegate(ref GetMethod_f, hModule, "GetMethod") && + LoadDelegate(ref PutMethod_f, hModule, "PutMethod") && + LoadDelegate(ref DeleteMethod_f, hModule, "DeleteMethod") && + LoadDelegate(ref BeginMethodEnumeration_f, hModule, "BeginMethodEnumeration") && + LoadDelegate(ref NextMethod_f, hModule, "NextMethod") && + LoadDelegate(ref EndMethodEnumeration_f, hModule, "EndMethodEnumeration") && + LoadDelegate(ref GetMethodQualifierSet_f, hModule, "GetMethodQualifierSet") && + LoadDelegate(ref GetMethodOrigin_f, hModule, "GetMethodOrigin") && + LoadDelegate(ref QualifierGet_f, hModule, "QualifierSet_Get") && + LoadDelegate(ref QualifierPut_f, hModule, "QualifierSet_Put") && + LoadDelegate(ref QualifierDelete_f, hModule, "QualifierSet_Delete") && + LoadDelegate(ref QualifierGetNames_f, hModule, "QualifierSet_GetNames") && + LoadDelegate(ref QualifierBeginEnumeration_f, hModule, "QualifierSet_BeginEnumeration") && + LoadDelegate(ref QualifierNext_f, hModule, "QualifierSet_Next") && + LoadDelegate(ref QualifierEndEnumeration_f, hModule, "QualifierSet_EndEnumeration") && + LoadDelegate(ref GetCurrentApartmentType_f, hModule, "GetCurrentApartmentType") && + LoadDelegate(ref GetDemultiplexedStub_f, hModule, "GetDemultiplexedStub") && + LoadDelegate(ref CreateInstanceEnumWmi_f, hModule, "CreateInstanceEnumWmi") && + LoadDelegate(ref CreateClassEnumWmi_f, hModule, "CreateClassEnumWmi") && + LoadDelegate(ref ExecQueryWmi_f, hModule, "ExecQueryWmi") && + LoadDelegate(ref ExecNotificationQueryWmi_f, hModule, "ExecNotificationQueryWmi") && + LoadDelegate(ref PutInstanceWmi_f, hModule, "PutInstanceWmi") && + LoadDelegate(ref PutClassWmi_f, hModule, "PutClassWmi") && + LoadDelegate(ref CloneEnumWbemClassObject_f, hModule, "CloneEnumWbemClassObject") && + LoadDelegate(ref ConnectServerWmi_f, hModule, "ConnectServerWmi") && + LoadDelegate(ref GetErrorInfo_f, hModule, "GetErrorInfo") && + LoadDelegate(ref Initialize_f, hModule, "Initialize")) + { + // All required delegates were loaded. + Initialize_f(CompatSwitches.AllowIManagementObjectQI); + } + else + { + LoadPlatformNotSupportedDelegates(string.Format(SR.PlatformNotSupported_FrameworkUpdatedRequired, wminet_utilsPath)); + } + } + static bool LoadDelegate(ref TDelegate delegate_f, IntPtr hModule, string procName) where TDelegate : class + { + IntPtr procAddr = Interop.Kernel32.GetProcAddress(hModule, procName); + return procAddr != null && + (delegate_f = Marshal.GetDelegateForFunctionPointer(procAddr)) != null; + } + + static void LoadPlatformNotSupportedDelegates(string exceptionMessage) + { + ResetSecurity_f = (_) => throw new PlatformNotSupportedException(exceptionMessage); + SetSecurity_f = (ref bool _, ref IntPtr __) => throw new PlatformNotSupportedException(exceptionMessage); + BlessIWbemServices_f = (_, __, ___, ____, _____, ______) => throw new PlatformNotSupportedException(exceptionMessage); + BlessIWbemServicesObject_f = (_, __, ___, ____, _____, ______) => throw new PlatformNotSupportedException(exceptionMessage); + GetPropertyHandle_f27 = (int _, IntPtr __, string ___, out int ____, out int _____) => throw new PlatformNotSupportedException(exceptionMessage); + WritePropertyValue_f28 = (_, __, ___, ____, _____) => throw new PlatformNotSupportedException(exceptionMessage); + Clone_f12 = (int _, IntPtr __, out IntPtr ___) => throw new PlatformNotSupportedException(exceptionMessage); + VerifyClientKey_f = () => throw new PlatformNotSupportedException(exceptionMessage); + GetQualifierSet_f = (int _, IntPtr __, out IntPtr ___) => throw new PlatformNotSupportedException(exceptionMessage); + Get_f = (int _, IntPtr __, string ___, int ____, ref object _____, ref int ______, ref int _______) => throw new PlatformNotSupportedException(exceptionMessage); + Put_f = (int _, IntPtr __, string ___, int ____, ref object _____, int ______) => throw new PlatformNotSupportedException(exceptionMessage); + Delete_f = (_, __, ___) => throw new PlatformNotSupportedException(exceptionMessage); + GetNames_f = (int _, IntPtr __, string ___, int ____, ref object _____, out string[] ______) => throw new PlatformNotSupportedException(exceptionMessage); + BeginEnumeration_f = (_, __, ___) => throw new PlatformNotSupportedException(exceptionMessage); + Next_f = (int _, IntPtr __, int ___, ref string ____, ref object _____, ref int ______, ref int _______) => throw new PlatformNotSupportedException(exceptionMessage); + EndEnumeration_f = (_, __) => throw new PlatformNotSupportedException(exceptionMessage); + GetPropertyQualifierSet_f = (int _, IntPtr __, string ___, out IntPtr ____) => throw new PlatformNotSupportedException(exceptionMessage); + Clone_f = (int _, IntPtr __, out IntPtr ___) => throw new PlatformNotSupportedException(exceptionMessage); + GetObjectText_f = (int _, IntPtr __, int ___, out string ____) => throw new PlatformNotSupportedException(exceptionMessage); + SpawnDerivedClass_f = (int _, IntPtr __, int ___, out IntPtr ____) => throw new PlatformNotSupportedException(exceptionMessage); + SpawnInstance_f = (int _, IntPtr __, int ___, out IntPtr ____) => throw new PlatformNotSupportedException(exceptionMessage); + CompareTo_f = (_, __, ___, ____) => throw new PlatformNotSupportedException(exceptionMessage); + GetPropertyOrigin_f = (int _, IntPtr __, string ___, out string ____) => throw new PlatformNotSupportedException(exceptionMessage); + InheritsFrom_f = (int _, IntPtr __, string ___) => throw new PlatformNotSupportedException(exceptionMessage); + GetMethod_f = (int _, IntPtr __, string ___, int ____, out IntPtr _____, out IntPtr ______) => throw new PlatformNotSupportedException(exceptionMessage); + PutMethod_f = (_, __, ___, ____, _____, ______) => throw new PlatformNotSupportedException(exceptionMessage); + DeleteMethod_f = (_, __, ___) => throw new PlatformNotSupportedException(exceptionMessage); + BeginMethodEnumeration_f = (_, __, ___) => throw new PlatformNotSupportedException(exceptionMessage); + NextMethod_f = (int _, IntPtr __, int ___, out string ____, out IntPtr _____, out IntPtr ______) => throw new PlatformNotSupportedException(exceptionMessage); + EndMethodEnumeration_f = (_, __) => throw new PlatformNotSupportedException(exceptionMessage); + GetMethodQualifierSet_f = (int _, IntPtr __, string ___, out IntPtr ____) => throw new PlatformNotSupportedException(exceptionMessage); + GetMethodOrigin_f = (int _, IntPtr __, string ___, out string ____) => throw new PlatformNotSupportedException(exceptionMessage); + QualifierGet_f = (int _, IntPtr __, string ___, int ____, ref object _____, ref int ______) => throw new PlatformNotSupportedException(exceptionMessage); + QualifierPut_f = (int _, IntPtr __, string ___, ref object ____, int _____) => throw new PlatformNotSupportedException(exceptionMessage); + QualifierDelete_f = (_, __, ___) => throw new PlatformNotSupportedException(exceptionMessage); + QualifierGetNames_f = (int _, IntPtr __, int ___, out string[] ____) => throw new PlatformNotSupportedException(exceptionMessage); + QualifierBeginEnumeration_f = (_, __, ___) => throw new PlatformNotSupportedException(exceptionMessage); + QualifierNext_f = (int _, IntPtr __, int ___, out string ____, out object _____, out int ______) => throw new PlatformNotSupportedException(exceptionMessage); + QualifierEndEnumeration_f = (_, __) => throw new PlatformNotSupportedException(exceptionMessage); + GetCurrentApartmentType_f = (int _, IntPtr __, out APTTYPE ___) => throw new PlatformNotSupportedException(exceptionMessage); + GetDemultiplexedStub_f = (object _, bool __, out object ___) => throw new PlatformNotSupportedException(exceptionMessage); + CreateInstanceEnumWmi_f = (string _, int __, IWbemContext ___, out IEnumWbemClassObject ____, int _____, int ______, IWbemServices _______, string ________, IntPtr _________, string __________) => throw new PlatformNotSupportedException(exceptionMessage); + CreateClassEnumWmi_f = (string _, int __, IWbemContext ___, out IEnumWbemClassObject ____, int _____, int ______, IWbemServices _______, string ________, IntPtr _________, string __________) => throw new PlatformNotSupportedException(exceptionMessage); + ExecQueryWmi_f = (string _, string __, int ___, IWbemContext ____, out IEnumWbemClassObject _____, int ______, int _______, IWbemServices ________, string _________, IntPtr __________, string ___________) => throw new PlatformNotSupportedException(exceptionMessage); + ExecNotificationQueryWmi_f = (string _, string __, int ___, IWbemContext ____, out IEnumWbemClassObject _____, int ______, int _______, IWbemServices ________, string _________, IntPtr __________, string ___________) => throw new PlatformNotSupportedException(exceptionMessage); + PutInstanceWmi_f = (_, __, ___, ____, _____, ______, _______, ________, _________, __________) => throw new PlatformNotSupportedException(exceptionMessage); + PutClassWmi_f = (_, __, ___, ____, _____, ______, _______, ________, _________, __________) => throw new PlatformNotSupportedException(exceptionMessage); + CloneEnumWbemClassObject_f = (out IEnumWbemClassObject _, int __, int ____, IEnumWbemClassObject _____, string ______, IntPtr _______, string ________) => throw new PlatformNotSupportedException(exceptionMessage); + ConnectServerWmi_f = (string _, string __, IntPtr ___, string ____, int _____, string ______, IWbemContext _______, out IWbemServices ________, int _________, int __________) => throw new PlatformNotSupportedException(exceptionMessage); + GetErrorInfo_f = () => throw new PlatformNotSupportedException(exceptionMessage); + Initialize_f = (_) => throw new PlatformNotSupportedException(exceptionMessage); + } + } + + /// + /// Represents a scope for management operations. In v1.0 the scope defines the WMI namespace in which management operations are performed. + /// + /// + /// using System; + /// using System.Management; + /// + /// // This sample demonstrates how to connect to root/default namespace + /// // using ManagmentScope object. + /// class Sample_ManagementScope + /// { + /// public static int Main(string[] args) + /// { + /// ManagementScope scope = new ManagementScope("root\\default"); + /// scope.Connect(); + /// ManagementClass newClass = new ManagementClass( + /// scope, + /// new ManagementPath(), + /// null); + /// return 0; + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// ' This sample demonstrates how to connect to root/default namespace + /// ' using ManagmentScope object. + /// Class Sample_ManagementScope + /// Overloads Public Shared Function Main(args() As String) As Integer + /// Dim scope As New ManagementScope("root\default") + /// scope.Connect() + /// Dim newClass As New ManagementClass(scope, _ + /// New ManagementPath(), _ + /// Nothing) + /// Return 0 + /// End Function + /// End Class + /// + /// + [TypeConverter(typeof(ManagementScopeConverter))] + public class ManagementScope : ICloneable + { + private ManagementPath validatedPath; + private IWbemServices wbemServices; + private ConnectionOptions options; + internal event IdentifierChangedEventHandler IdentifierChanged; + internal bool IsDefaulted; //used to tell whether the current scope has been created from the default + //scope or not - this information is used to tell whether it can be overridden + //when a new path is set or not. + + //Fires IdentifierChanged event + private void FireIdentifierChanged() + { + if (IdentifierChanged != null) + IdentifierChanged(this,null); + } + + //Called when IdentifierChanged() event fires + private void HandleIdentifierChange(object sender, + IdentifierChangedEventArgs args) + { + // Since our object has changed we had better signal to ourself that + // an connection needs to be established + wbemServices = null; + + //Something inside ManagementScope changed, we need to fire an event + //to the parent object + FireIdentifierChanged(); + } + + // Private path property accessor which performs minimal validation on the + // namespace path. IWbemPath cannot differentiate between a class or a name- + // space if path separators are not present in the path. Therefore, IWbemPath + // will allow a namespace of "rootBeer" vs "root". Since it is established + // that the scope path is indeed a namespace path, we perform this validation. + private ManagementPath prvpath + { + get + { + return validatedPath; + } + set + { + if (value != null) + { + string pathValue = value.Path; + if (!ManagementPath.IsValidNamespaceSyntax(pathValue)) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)tag_WBEMSTATUS.WBEM_E_INVALID_NAMESPACE); + } + + validatedPath = value; + } + } + + internal IWbemServices GetIWbemServices () + { + IWbemServices localCopy = wbemServices; + //IWbemServices is always created in MTA context. Only if call is made through non MTA context we need to use IWbemServices in right context. + // Lets start by assuming that we'll return the RCW that we already have. When WMINet_Utils.dll wraps the real COM proxy, credentials don't get + // lost when the CLR marshals the wrapped object to a different COM apartment. The wrap was added to prevent marshalling of IManagedObject from native + // to managed code. + + if (CompatSwitches.AllowIManagementObjectQI) + { + // Get an IUnknown for this apartment + IntPtr pUnk = Marshal.GetIUnknownForObject(wbemServices); + + // Get an 'IUnknown RCW' for this apartment + Object unknown = Marshal.GetObjectForIUnknown(pUnk); + + // Release the ref count on the IUnknwon + Marshal.Release(pUnk); + + // See if we are in the same apartment as where the original IWbemServices lived + // If we are in a different apartment, give the caller an RCW generated just for their + // apartment, and set the proxy blanket appropriately + if(!object.ReferenceEquals(unknown, wbemServices)) + { + // We need to set the proxy blanket on 'unknown' or else the QI for IWbemServices may + // fail if we are running under a local user account. The QI has to be done by + // someone who is a member of the 'Everyone' group on the target machine, or DCOM + // won't let the call through. + SecurityHandler securityHandler = GetSecurityHandler (); + securityHandler.SecureIUnknown(unknown); + + // Now, we can QI and secure the IWbemServices + localCopy = (IWbemServices)unknown; + + // We still need to bless the IWbemServices in this apartment + securityHandler.Secure(localCopy); + } + } + + return localCopy; // STRANGE: Why does it still work if I return 'wbemServices'? + } + + /// + /// Gets or sets a value indicating whether the is currently bound to a + /// WMI server and namespace. + /// + /// + /// if a connection is alive (bound + /// to a server and namespace); otherwise, . + /// + /// + /// A scope is disconnected after creation until someone + /// explicitly calls (), or uses the scope for any + /// operation that requires a live connection. Also, the scope is + /// disconnected from the previous connection whenever the identifying properties of the scope are + /// changed. + /// + public bool IsConnected + { + get + { + return (null != wbemServices); + } + } + + //Internal constructor + internal ManagementScope (ManagementPath path, IWbemServices wbemServices, + ConnectionOptions options) + { + if (null != path) + this.Path = path; + + if (null != options) { + this.Options = options; + } + + // We set this.wbemServices after setting Path and Options + // because the latter operations can cause wbemServices to be NULLed. + this.wbemServices = wbemServices; + } + + internal ManagementScope (ManagementPath path, ManagementScope scope) + : this (path, (null != scope) ? scope.options : null) {} + + internal static ManagementScope _Clone(ManagementScope scope) + { + return ManagementScope._Clone(scope, null); + } + + internal static ManagementScope _Clone(ManagementScope scope, IdentifierChangedEventHandler handler) + { + ManagementScope scopeTmp = new ManagementScope(null, null, null); + + // Wire up change handler chain. Use supplied handler, if specified; + // otherwise, default to that of the scope argument. + if (handler != null) + scopeTmp.IdentifierChanged = handler; + else if (scope != null) + scopeTmp.IdentifierChanged = new IdentifierChangedEventHandler(scope.HandleIdentifierChange); + + // Set scope path. + if (scope == null) + { + // No path specified. Default. + scopeTmp.prvpath = ManagementPath._Clone(ManagementPath.DefaultPath, new IdentifierChangedEventHandler(scopeTmp.HandleIdentifierChange)); + scopeTmp.IsDefaulted = true; + + scopeTmp.wbemServices = null; + scopeTmp.options = null; + } + else + { + if (scope.prvpath == null) + { + // No path specified. Default. + scopeTmp.prvpath = ManagementPath._Clone(ManagementPath.DefaultPath, new IdentifierChangedEventHandler(scopeTmp.HandleIdentifierChange)); + scopeTmp.IsDefaulted = true; + } + else + { + // Use scope-supplied path. + scopeTmp.prvpath = ManagementPath._Clone(scope.prvpath, new IdentifierChangedEventHandler(scopeTmp.HandleIdentifierChange)); + scopeTmp.IsDefaulted = scope.IsDefaulted; + } + + scopeTmp.wbemServices = scope.wbemServices; + if (scope.options != null) + scopeTmp.options = ConnectionOptions._Clone(scope.options, new IdentifierChangedEventHandler(scopeTmp.HandleIdentifierChange)); + } + + return scopeTmp; + } + + //Default constructor + /// + /// Initializes a new instance + /// of the class. + /// + /// + /// Initializes a new instance of the class, with default values. This is the + /// default constructor. + /// + /// + /// If the object doesn't have any + /// properties set before connection, it will be initialized with default values + /// (for example, the local machine and the root\cimv2 namespace). + /// + /// + /// ManagementScope s = new ManagementScope(); + /// + /// Dim s As New ManagementScope() + /// + /// + public ManagementScope () : + this (new ManagementPath (ManagementPath.DefaultPath.Path)) + { + //Flag that this scope uses the default path + IsDefaulted = true; + } + + //Parameterized constructors + /// + /// Initializes a new instance of the class representing + /// the specified scope path. + /// + /// A containing the path to a server and namespace for the . + /// + /// ManagementScope s = new ManagementScope(new ManagementPath("\\\\MyServer\\root\\default")); + /// + /// Dim p As New ManagementPath("\\MyServer\root\default") + /// Dim s As New ManagementScope(p) + /// + /// + public ManagementScope (ManagementPath path) : this(path, (ConnectionOptions)null) {} + + /// + /// Initializes a new instance of the class representing the specified scope + /// path. + /// + /// The server and namespace path for the . + /// + /// ManagementScope s = new ManagementScope("\\\\MyServer\\root\\default"); + /// + /// Dim s As New ManagementScope("\\MyServer\root\default") + /// + /// + public ManagementScope (string path) : this(new ManagementPath(path), (ConnectionOptions)null) {} + /// + /// Initializes a new instance of the class representing the specified scope path, + /// with the specified options. + /// + /// The server and namespace for the . + /// A containing options for the connection. + /// + /// ConnectionOptions opt = new ConnectionOptions(); + /// opt.Username = "Me"; + /// opt.Password = "MyPassword"; + /// ManagementScope s = new ManagementScope("\\\\MyServer\\root\\default", opt); + /// + /// Dim opt As New ConnectionOptions() + /// opt.Username = "Me" + /// opt.Password = "MyPassword" + /// Dim s As New ManagementScope("\\MyServer\root\default", opt); + /// + /// + public ManagementScope (string path, ConnectionOptions options) : this (new ManagementPath(path), options) {} + + /// + /// Initializes a new instance of the class representing the specified scope path, + /// with the specified options. + /// + /// A containing the path to the server and namespace for the . + /// The containing options for the connection. + /// + /// ConnectionOptions opt = new ConnectionOptions(); + /// opt.Username = "Me"; + /// opt.Password = "MyPassword"; + /// + /// ManagementPath p = new ManagementPath("\\\\MyServer\\root\\default"); + /// ManagementScope = new ManagementScope(p, opt); + /// + /// Dim opt As New ConnectionOptions() + /// opt.UserName = "Me" + /// opt.Password = "MyPassword" + /// + /// Dim p As New ManagementPath("\\MyServer\root\default") + /// Dim s As New ManagementScope(p, opt) + /// + /// + public ManagementScope (ManagementPath path, ConnectionOptions options) + { + if (null != path) + this.prvpath = ManagementPath._Clone(path, new IdentifierChangedEventHandler(HandleIdentifierChange)); + else + this.prvpath = ManagementPath._Clone(null); + + if (null != options) + { + this.options = ConnectionOptions._Clone(options, new IdentifierChangedEventHandler(HandleIdentifierChange)); + } + else + this.options = null; + + IsDefaulted = false; //assume that this scope is not initialized by the default path + } + + /// + /// Gets or sets options for making the WMI connection. + /// + /// + /// The valid + /// containing options for the WMI connection. + /// + /// + /// //This constructor creates a scope object with default options + /// ManagementScope s = new ManagementScope("root\\MyApp"); + /// + /// //Change default connection options - + /// //In this example, set the system privileges to enabled for operations that require system privileges. + /// s.Options.EnablePrivileges = true; + /// + /// 'This constructor creates a scope object with default options + /// Dim s As New ManagementScope("root\\MyApp") + /// + /// 'Change default connection options - + /// 'In this example, set the system privileges to enabled for operations that require system privileges. + /// s.Options.EnablePrivileges = True + /// + /// + public ConnectionOptions Options + { + get + { + if (options == null) + return options = ConnectionOptions._Clone(null, new IdentifierChangedEventHandler(HandleIdentifierChange)); + else + return options; + } + set + { + if (null != value) + { + if (null != options) + options.IdentifierChanged -= new IdentifierChangedEventHandler(HandleIdentifierChange); + + options = ConnectionOptions._Clone((ConnectionOptions)value, new IdentifierChangedEventHandler(HandleIdentifierChange)); + + //the options property has changed so act like we fired the event + HandleIdentifierChange(this,null); + } + else + throw new ArgumentNullException ("value"); + } + } + + /// + /// Gets or sets the path for the . + /// + /// + /// A containing + /// the path to a server and namespace. + /// + /// + /// ManagementScope s = new ManagementScope(); + /// s.Path = new ManagementPath("root\\MyApp"); + /// + /// Dim s As New ManagementScope() + /// s.Path = New ManagementPath("root\MyApp") + /// + /// + public ManagementPath Path + { + get + { + if (prvpath == null) + return prvpath = ManagementPath._Clone(null); + else + return prvpath; + } + set + { + if (null != value) + { + if (null != prvpath) + prvpath.IdentifierChanged -= new IdentifierChangedEventHandler(HandleIdentifierChange); + + IsDefaulted = false; //someone is specifically setting the scope path so it's not defaulted any more + + prvpath = ManagementPath._Clone((ManagementPath)value, new IdentifierChangedEventHandler(HandleIdentifierChange)); + + //the path property has changed so act like we fired the event + HandleIdentifierChange(this,null); + } + else + throw new ArgumentNullException ("value"); + } + } + + /// + /// Returns a copy of the object. + /// + /// + /// A new copy of the . + /// + public ManagementScope Clone() + { + return ManagementScope._Clone(this); + } + + /// + /// Clone a copy of this object. + /// + /// + /// A new copy of this object. + /// object. + /// + Object ICloneable.Clone() + { + return Clone(); + } + + /// + /// Connects this to the actual WMI + /// scope. + /// + /// + /// This method is called implicitly when the + /// scope is used in an operation that requires it to be connected. Calling it + /// explicitly allows the user to control the time of connection. + /// + /// + /// ManagementScope s = new ManagementScope("root\\MyApp"); + /// + /// //Explicit call to connect the scope object to the WMI namespace + /// s.Connect(); + /// + /// //The following doesn't do any implicit scope connections because s is already connected. + /// ManagementObject o = new ManagementObject(s, "Win32_LogicalDisk='C:'", null); + /// + /// Dim s As New ManagementScope("root\\MyApp") + /// + /// 'Explicit call to connect the scope object to the WMI namespace + /// s.Connect() + /// + /// 'The following doesn't do any implicit scope connections because s is already connected. + /// Dim o As New ManagementObject(s, "Win32_LogicalDisk=""C:""", null) + /// + /// + public void Connect () + { + Initialize (); + } + + internal void Initialize () + { + //If the path is not set yet we can't do it + if (null == prvpath) + throw new InvalidOperationException(); + + + /* + * If we're not connected yet, this is the time to do it... We lock + * the state to prevent 2 threads simultaneously doing the same + * connection. To avoid taking the lock unnecessarily we examine + * isConnected first + */ + if (!IsConnected) + { +#pragma warning disable CA2002 + lock (this) +#pragma warning restore CA2002 + { + if (!IsConnected) + { + // The locator cannot be marshalled accross apartments, so we must create the locator + // and get the IWbemServices from an MTA thread + if(!MTAHelper.IsNoContextMTA()) + { + // + // Ensure we are able to trap exceptions from worker thread. + // + ThreadDispatch disp = new ThreadDispatch ( new ThreadDispatch.ThreadWorkerMethodWithParam ( InitializeGuts ) ) ; + disp.Parameter = this ; + disp.Start ( ) ; + } + else + InitializeGuts(this); + } + } + } + } + + void InitializeGuts(object o) + { + ManagementScope threadParam = (ManagementScope) o ; + IWbemLocator loc = (IWbemLocator) new WbemLocator(); + IntPtr punk = IntPtr.Zero; + + if (null == threadParam.options) + { + threadParam.Options = new ConnectionOptions(); + } + + string nsPath = threadParam.prvpath.GetNamespacePath((int)tag_WBEM_GET_TEXT_FLAGS.WBEMPATH_GET_SERVER_AND_NAMESPACE_ONLY); + + // If no namespace specified, fill in the default one + if ((null == nsPath) || (0 == nsPath.Length)) + { + // NB: we use a special method to set the namespace + // path here as we do NOT want to trigger an + // IdentifierChanged event as a result of this set + + bool bUnused; + nsPath = threadParam.prvpath.SetNamespacePath(ManagementPath.DefaultPath.Path, out bUnused); + } + + int status = (int)ManagementStatus.NoError; + threadParam.wbemServices = null; + + //If we're on XP or higher, always use the "max_wait" flag to avoid hanging + if (Environment.OSVersion.Platform == PlatformID.Win32NT) + { + + if( ((Environment.OSVersion.Version.Major == 5) && (Environment.OSVersion.Version.Minor >= 1)) || (Environment.OSVersion.Version.Major >= 6) ) + { + threadParam.options.Flags |= (int)tag_WBEM_CONNECT_OPTIONS.WBEM_FLAG_CONNECT_USE_MAX_WAIT; + } + } + + try + { + status = GetSecuredConnectHandler().ConnectNSecureIWbemServices(nsPath, ref threadParam.wbemServices); + } + catch (COMException e) + { + ManagementException.ThrowWithExtendedInfo (e); + } + + if ((status & 0xfffff000) == 0x80041000) + { + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + } + else if ((status & 0x80000000) != 0) + { + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + internal SecurityHandler GetSecurityHandler () + { + return new SecurityHandler(this); + } + internal SecuredConnectHandler GetSecuredConnectHandler () + { + return new SecuredConnectHandler(this); + } + internal SecuredIEnumWbemClassObjectHandler GetSecuredIEnumWbemClassObjectHandler (IEnumWbemClassObject pEnumWbemClassObject) + { + return new SecuredIEnumWbemClassObjectHandler (this, pEnumWbemClassObject); + } + internal SecuredIWbemServicesHandler GetSecuredIWbemServicesHandler( IWbemServices pWbemServiecs) + { + return new SecuredIWbemServicesHandler(this, pWbemServiecs); + } + + }//ManagementScope + + internal class SecuredIEnumWbemClassObjectHandler + { + private IEnumWbemClassObject pEnumWbemClassObjectsecurityHelper; + private ManagementScope scope; + internal SecuredIEnumWbemClassObjectHandler (ManagementScope theScope, IEnumWbemClassObject pEnumWbemClassObject) + { + this.scope = theScope; + pEnumWbemClassObjectsecurityHelper = pEnumWbemClassObject; + } + internal int Reset_() + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + status = pEnumWbemClassObjectsecurityHelper.Reset_(); + return status; + } + internal int Next_( int lTimeout, uint uCount, IWbemClassObject_DoNotMarshal[] ppOutParams, ref uint puReturned) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + status = pEnumWbemClassObjectsecurityHelper.Next_( lTimeout, uCount, ppOutParams, out puReturned); + return status; + } + internal int NextAsync_( uint uCount, IWbemObjectSink pSink) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + status = pEnumWbemClassObjectsecurityHelper.NextAsync_(uCount, pSink); + return status; + } + internal int Clone_(ref IEnumWbemClassObject ppEnum) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + if( null != scope) + { + IntPtr password = scope.Options.GetPassword(); + status = WmiNetUtilsHelper.CloneEnumWbemClassObject_f( + out ppEnum, + (int)scope.Options.Authentication, + (int)scope.Options.Impersonation, + pEnumWbemClassObjectsecurityHelper, + scope.Options.Username, + password, + scope.Options.Authority); + System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(password); + } + return status; + } + internal int Skip_( int lTimeout, uint nCount) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + status = pEnumWbemClassObjectsecurityHelper.Skip_(lTimeout, nCount); + return status; + } + } + + + internal class SecuredConnectHandler + { + private ManagementScope scope; + + internal SecuredConnectHandler (ManagementScope theScope) + { + this.scope = theScope; + } + internal int ConnectNSecureIWbemServices(string path, ref IWbemServices pServices) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + if( null != scope ) + { + bool needToReset = false; + IntPtr handle = IntPtr.Zero; + + try + { + if (scope.Options.EnablePrivileges && !CompatSwitches.AllowIManagementObjectQI) + { + WmiNetUtilsHelper.SetSecurity_f(ref needToReset, ref handle); + } + + IntPtr password = scope.Options.GetPassword(); + status = WmiNetUtilsHelper.ConnectServerWmi_f( + path, + scope.Options.Username, + password, + scope.Options.Locale, + scope.Options.Flags, + scope.Options.Authority, + scope.Options.GetContext(), + out pServices, + (int)scope.Options.Impersonation, + (int)scope.Options.Authentication); + System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(password); + } + finally + { + if (needToReset) + { + needToReset = false; + WmiNetUtilsHelper.ResetSecurity_f(handle); + } + } + } + return status; + } + } + + internal class SecuredIWbemServicesHandler + { + private IWbemServices pWbemServiecsSecurityHelper; + private ManagementScope scope; + internal SecuredIWbemServicesHandler (ManagementScope theScope, IWbemServices pWbemServiecs) + { + this.scope = theScope; + pWbemServiecsSecurityHelper = pWbemServiecs; + } + internal int OpenNamespace_(string strNamespace, Int32 lFlags, ref IWbemServices ppWorkingNamespace, IntPtr ppCallResult) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_NOT_SUPPORTED; + //This should go through WMINET_utils layer and ppWorkingNamespace should be secured. See implementation of CreateInstanceEnum method. + return status; + } + + internal int CancelAsyncCall_( IWbemObjectSink pSink) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + status = pWbemServiecsSecurityHelper.CancelAsyncCall_(pSink); + return status; + } + internal int QueryObjectSink_( Int32 lFlags, ref IWbemObjectSink ppResponseHandler) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + status = pWbemServiecsSecurityHelper.QueryObjectSink_(lFlags, out ppResponseHandler); + return status; + } + internal int GetObject_(string strObjectPath, Int32 lFlags, IWbemContext pCtx, ref IWbemClassObjectFreeThreaded ppObject, IntPtr ppCallResult) + { + //It is assumed that caller always passes ppCallResult as IntPtr.Zero. + //If it changes let this call go through wminet_utils.dll. Check implementation of CreateInstanceEnum_ for more information. + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + if( !Object.ReferenceEquals(ppCallResult, IntPtr.Zero) ) + status = pWbemServiecsSecurityHelper.GetObject_(strObjectPath, lFlags, pCtx, out ppObject, ppCallResult); + return status; + } + + internal int GetObjectAsync_(string strObjectPath, Int32 lFlags, IWbemContext pCtx, IWbemObjectSink pResponseHandler) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + status = pWbemServiecsSecurityHelper.GetObjectAsync_(strObjectPath, lFlags, pCtx, pResponseHandler); + return status; + } + internal int PutClass_(IWbemClassObjectFreeThreaded pObject, Int32 lFlags, IWbemContext pCtx, IntPtr ppCallResult) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + if( null != scope) + { + IntPtr password = scope.Options.GetPassword(); + status = WmiNetUtilsHelper.PutClassWmi_f(pObject, + lFlags, + pCtx, + ppCallResult, + (int)scope.Options.Authentication, + (int)scope.Options.Impersonation, + pWbemServiecsSecurityHelper, + scope.Options.Username, + password, + scope.Options.Authority); + System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(password); + } + return status; + } + internal int PutClassAsync_(IWbemClassObjectFreeThreaded pObject, Int32 lFlags, IWbemContext pCtx, IWbemObjectSink pResponseHandler) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + status = pWbemServiecsSecurityHelper.PutClassAsync_(pObject, lFlags,pCtx, pResponseHandler); + return status; + } + internal int DeleteClass_( string strClass, Int32 lFlags, IWbemContext pCtx, IntPtr ppCallResult) + { + //It is assumed that caller always passes ppCallResult as IntPtr.Zero. + //If it changes let this call go through wminet_utils.dll. Check implementation of CreateInstanceEnum_ for more information. + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + if( !Object.ReferenceEquals(ppCallResult, IntPtr.Zero) ) + status = pWbemServiecsSecurityHelper.DeleteClass_(strClass, lFlags, pCtx, ppCallResult); + return status; + } + internal int DeleteClassAsync_(string strClass, Int32 lFlags, IWbemContext pCtx, IWbemObjectSink pResponseHandler) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + status = pWbemServiecsSecurityHelper.DeleteClassAsync_(strClass, lFlags, pCtx,pResponseHandler); + return status; + } + internal int CreateClassEnum_(string strSuperClass, Int32 lFlags, IWbemContext pCtx, ref IEnumWbemClassObject ppEnum) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + if( null != scope ) + { + IntPtr password = scope.Options.GetPassword(); + status = WmiNetUtilsHelper.CreateClassEnumWmi_f(strSuperClass, + lFlags, + pCtx, + out ppEnum, + (int)scope.Options.Authentication, + (int)scope.Options.Impersonation, + pWbemServiecsSecurityHelper, + scope.Options.Username, + password, + scope.Options.Authority); + System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(password); + } + return status; + } + internal int CreateClassEnumAsync_(string strSuperClass, Int32 lFlags, IWbemContext pCtx, IWbemObjectSink pResponseHandler) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + status = pWbemServiecsSecurityHelper.CreateClassEnumAsync_(strSuperClass, lFlags, pCtx, pResponseHandler); + return status; + } + internal int PutInstance_( IWbemClassObjectFreeThreaded pInst, Int32 lFlags, IWbemContext pCtx, IntPtr ppCallResult) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + if( null != scope) + { + IntPtr password = scope.Options.GetPassword(); + status = WmiNetUtilsHelper.PutInstanceWmi_f(pInst, + lFlags, + pCtx, + ppCallResult, + (int)scope.Options.Authentication, + (int)scope.Options.Impersonation, + pWbemServiecsSecurityHelper, + scope.Options.Username, + password, + scope.Options.Authority); + System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(password); + } + return status; + } + internal int PutInstanceAsync_(IWbemClassObjectFreeThreaded pInst, Int32 lFlags, IWbemContext pCtx, IWbemObjectSink pResponseHandler) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + status = pWbemServiecsSecurityHelper.PutInstanceAsync_(pInst, lFlags, pCtx, pResponseHandler); + return status; + } + internal int DeleteInstance_(string strObjectPath, Int32 lFlags, IWbemContext pCtx, IntPtr ppCallResult) + { + //It is assumed that caller always passes ppCallResult as IntPtr.Zero. + //If it changes let this call go through wminet_utils.dll. Check implementation of CreateInstanceEnum_ for more information. + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + if( !Object.ReferenceEquals(ppCallResult, IntPtr.Zero) ) + status = pWbemServiecsSecurityHelper.DeleteInstance_(strObjectPath, lFlags, pCtx, ppCallResult); + return status; + } + internal int DeleteInstanceAsync_(string strObjectPath, Int32 lFlags, IWbemContext pCtx, IWbemObjectSink pResponseHandler) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + status = pWbemServiecsSecurityHelper.DeleteInstanceAsync_(strObjectPath, lFlags, pCtx, pResponseHandler); + return status; + } + + internal int CreateInstanceEnum_(string strFilter, Int32 lFlags, IWbemContext pCtx, ref IEnumWbemClassObject ppEnum) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + if( null != scope) + { + IntPtr password = scope.Options.GetPassword(); + status = WmiNetUtilsHelper.CreateInstanceEnumWmi_f(strFilter, + lFlags, + pCtx, + out ppEnum, + (int)scope.Options.Authentication, + (int)scope.Options.Impersonation, + pWbemServiecsSecurityHelper, + scope.Options.Username, + password, + scope.Options.Authority); + System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(password); + } + return status; + } + internal int CreateInstanceEnumAsync_(string strFilter, Int32 lFlags, IWbemContext pCtx, IWbemObjectSink pResponseHandler) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + status = pWbemServiecsSecurityHelper.CreateInstanceEnumAsync_(strFilter, lFlags, pCtx, pResponseHandler); + return status; + } + internal int ExecQuery_(string strQueryLanguage, string strQuery, Int32 lFlags, IWbemContext pCtx, ref IEnumWbemClassObject ppEnum) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + if( null != scope) + { + IntPtr password = scope.Options.GetPassword(); + status = WmiNetUtilsHelper.ExecQueryWmi_f(strQueryLanguage, + strQuery, + lFlags, + pCtx, + out ppEnum, + (int)scope.Options.Authentication, + (int)scope.Options.Impersonation, + pWbemServiecsSecurityHelper, + scope.Options.Username, + password, + scope.Options.Authority); + System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(password); + } + return status; + } + internal int ExecQueryAsync_(string strQueryLanguage, string strQuery, Int32 lFlags, IWbemContext pCtx, IWbemObjectSink pResponseHandler) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + status = pWbemServiecsSecurityHelper.ExecQueryAsync_(strQueryLanguage, strQuery, lFlags, pCtx, pResponseHandler); + return status; + } + internal int ExecNotificationQuery_(string strQueryLanguage, string strQuery, Int32 lFlags, IWbemContext pCtx, ref IEnumWbemClassObject ppEnum) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + if( null != scope) + { + IntPtr password = scope.Options.GetPassword(); + status = WmiNetUtilsHelper.ExecNotificationQueryWmi_f(strQueryLanguage, + strQuery, + lFlags, + pCtx, + out ppEnum, + (int)scope.Options.Authentication, + (int)scope.Options.Impersonation, + pWbemServiecsSecurityHelper, + scope.Options.Username, + password, + scope.Options.Authority); + System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(password); + } + return status; + } + internal int ExecNotificationQueryAsync_(string strQueryLanguage, string strQuery, Int32 lFlags, IWbemContext pCtx, IWbemObjectSink pResponseHandler) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + status = pWbemServiecsSecurityHelper.ExecNotificationQueryAsync_(strQueryLanguage, strQuery, lFlags, pCtx, pResponseHandler); + return status; + } + internal int ExecMethod_( string strObjectPath, string strMethodName, Int32 lFlags, IWbemContext pCtx, IWbemClassObjectFreeThreaded pInParams, ref IWbemClassObjectFreeThreaded ppOutParams, IntPtr ppCallResult) + { + //It is assumed that caller always passes ppCallResult as IntPtr.Zero. + //If it changes let this call go through wminet_utils.dll. Check implementation of CreateInstanceEnum_ for more information. + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + if( !Object.ReferenceEquals(ppCallResult, IntPtr.Zero) ) + status = pWbemServiecsSecurityHelper.ExecMethod_(strObjectPath, strMethodName, lFlags, pCtx, pInParams, out ppOutParams, ppCallResult); + return status; + } + internal int ExecMethodAsync_(string strObjectPath, string strMethodName, Int32 lFlags, IWbemContext pCtx, IWbemClassObjectFreeThreaded pInParams, IWbemObjectSink pResponseHandler) + { + int status = (int)tag_WBEMSTATUS.WBEM_E_FAILED; + status = pWbemServiecsSecurityHelper.ExecMethodAsync_(strObjectPath, strMethodName, lFlags, pCtx, pInParams, pResponseHandler); + return status; + } + } + + + internal class SecurityHandler + { + private bool needToReset = false; + private IntPtr handle; + private ManagementScope scope; + + internal SecurityHandler (ManagementScope theScope) + { + this.scope = theScope; + if (null != scope) + { + if (scope.Options.EnablePrivileges) + { + WmiNetUtilsHelper.SetSecurity_f(ref needToReset, ref handle); + } + } + } + + internal void Reset () + { + if (needToReset) + { + needToReset = false; + + if (null != scope) + { + WmiNetUtilsHelper.ResetSecurity_f ( handle); + } + } + + } + + + internal void Secure (IWbemServices services) + { + if (null != scope) + { + IntPtr password = scope.Options.GetPassword(); + int status = WmiNetUtilsHelper.BlessIWbemServices_f + ( + services, + scope.Options.Username, + password, + scope.Options.Authority, + (int)scope.Options.Impersonation, + (int)scope.Options.Authentication + ); + System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(password); + if (status < 0) + { + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + } + + internal void SecureIUnknown(object unknown) + { + // We use a hack to call BlessIWbemServices with an IUnknown instead of an IWbemServices + // In VNext, we should really change the implementation of WMINet_Utils.dll so that it has + // a method which explicitly takes an IUnknown. We rely on the fact that the implementation + // of BlessIWbemServices actually casts the first parameter to IUnknown before blessing + if (null != scope) + { + IntPtr password = scope.Options.GetPassword(); + int status = WmiNetUtilsHelper.BlessIWbemServicesObject_f + ( + unknown, + scope.Options.Username, + password, + scope.Options.Authority, + (int)scope.Options.Impersonation, + (int)scope.Options.Authentication + ); + System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(password); + if (status < 0) + { + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + } + + + } //SecurityHandler + + + /// + /// Converts a String to a ManagementScope + /// + class ManagementScopeConverter : ExpandableObjectConverter + { + + /// + /// Determines if this converter can convert an object in the given source type to the native type of the converter. + /// + /// An ITypeDescriptorContext that provides a format context. + /// A Type that represents the type you wish to convert from. + /// + /// true if this converter can perform the conversion; otherwise, false. + /// + public override Boolean CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if ((sourceType == typeof(ManagementScope))) + { + return true; + } + return base.CanConvertFrom(context,sourceType); + } + + /// + /// Gets a value indicating whether this converter can convert an object to the given destination type using the context. + /// + /// An ITypeDescriptorContext that provides a format context. + /// A Type that represents the type you wish to convert to. + /// + /// true if this converter can perform the conversion; otherwise, false. + /// + public override Boolean CanConvertTo(ITypeDescriptorContext context, Type destinationType) + { + if ((destinationType == typeof(InstanceDescriptor))) + { + return true; + } + return base.CanConvertTo(context,destinationType); + } + + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the desitnation type, this will + /// throw a NotSupportedException. + /// + /// An ITypeDescriptorContext that provides a format context. + /// A CultureInfo object. If a null reference (Nothing in Visual Basic) is passed, the current culture is assumed. + /// The Object to convert. + /// The Type to convert the value parameter to. + /// An Object that represents the converted value. + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + { + + if (destinationType == null) + { + throw new ArgumentNullException("destinationType"); + } + + if (value is ManagementScope && destinationType == typeof(InstanceDescriptor)) + { + ManagementScope obj = ((ManagementScope)(value)); + ConstructorInfo ctor = typeof(ManagementScope).GetConstructor(new Type[] {typeof(System.String)}); + if (ctor != null) + { + return new InstanceDescriptor(ctor, new object[] {obj.Path.Path}); + } + } + return base.ConvertTo(context,culture,value,destinationType); + } + } +} diff --git a/external/corefx/src/System.Management/src/System/Management/Method.cs b/external/corefx/src/System.Management/src/System/Management/Method.cs new file mode 100644 index 0000000000..a3a1805054 --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/Method.cs @@ -0,0 +1,253 @@ +// 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.Runtime.InteropServices; + +namespace System.Management +{ + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + /// + /// Contains information about a WMI method. + /// + /// + /// using System; + /// using System.Management; + /// + /// // This example shows how to obtain meta data + /// // about a WMI method with a given name in a given WMI class + /// + /// class Sample_MethodData + /// { + /// public static int Main(string[] args) { + /// + /// // Get the "SetPowerState" method in the Win32_LogicalDisk class + /// ManagementClass diskClass = new ManagementClass("win32_logicaldisk"); + /// MethodData m = diskClass.Methods["SetPowerState"]; + /// + /// // Get method name (albeit we already know it) + /// Console.WriteLine("Name: " + m.Name); + /// + /// // Get the name of the top-most class where this specific method was defined + /// Console.WriteLine("Origin: " + m.Origin); + /// + /// // List names and types of input parameters + /// ManagementBaseObject inParams = m.InParameters; + /// foreach(PropertyData pdata in inParams.Properties) { + /// Console.WriteLine(); + /// Console.WriteLine("InParam_Name: " + pdata.Name); + /// Console.WriteLine("InParam_Type: " + pdata.Type); + /// } + /// + /// // List names and types of output parameters + /// ManagementBaseObject outParams = m.OutParameters; + /// foreach(PropertyData pdata in outParams.Properties) { + /// Console.WriteLine(); + /// Console.WriteLine("OutParam_Name: " + pdata.Name); + /// Console.WriteLine("OutParam_Type: " + pdata.Type); + /// } + /// + /// return 0; + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// ' This example shows how to obtain meta data + /// ' about a WMI method with a given name in a given WMI class + /// + /// Class Sample_ManagementClass + /// Overloads Public Shared Function Main(args() As String) As Integer + /// + /// ' Get the "SetPowerState" method in the Win32_LogicalDisk class + /// Dim diskClass As New ManagementClass("Win32_LogicalDisk") + /// Dim m As MethodData = diskClass.Methods("SetPowerState") + /// + /// ' Get method name (albeit we already know it) + /// Console.WriteLine("Name: " & m.Name) + /// + /// ' Get the name of the top-most class where + /// ' this specific method was defined + /// Console.WriteLine("Origin: " & m.Origin) + /// + /// ' List names and types of input parameters + /// Dim inParams As ManagementBaseObject + /// inParams = m.InParameters + /// Dim pdata As PropertyData + /// For Each pdata In inParams.Properties + /// Console.WriteLine() + /// Console.WriteLine("InParam_Name: " & pdata.Name) + /// Console.WriteLine("InParam_Type: " & pdata.Type) + /// Next pdata + /// + /// ' List names and types of output parameters + /// Dim outParams As ManagementBaseObject + /// outParams = m.OutParameters + /// For Each pdata in outParams.Properties + /// Console.WriteLine() + /// Console.WriteLine("OutParam_Name: " & pdata.Name) + /// Console.WriteLine("OutParam_Type: " & pdata.Type) + /// Next pdata + /// + /// Return 0 + /// End Function + /// End Class + /// + /// + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + public class MethodData + { + private ManagementObject parent; //needed to be able to get method qualifiers + private string methodName; + private IWbemClassObjectFreeThreaded wmiInParams; + private IWbemClassObjectFreeThreaded wmiOutParams; + private QualifierDataCollection qualifiers; + + internal MethodData(ManagementObject parent, string methodName) + { + this.parent = parent; + this.methodName = methodName; + RefreshMethodInfo(); + qualifiers = null; + } + + + //This private function is used to refresh the information from the Wmi object before returning the requested data + private void RefreshMethodInfo() + { + int status = (int)ManagementStatus.Failed; + + try + { + status = parent.wbemObject.GetMethod_(methodName, 0, out wmiInParams, out wmiOutParams); + } + catch (COMException e) + { + ManagementException.ThrowWithExtendedInfo(e); + } + if ((status & 0xfffff000) == 0x80041000) + { + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + } + else if ((status & 0x80000000) != 0) + { + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + + /// + /// Gets or sets the name of the method. + /// + /// + /// The name of the method. + /// + public string Name + { + get { return methodName != null ? methodName : ""; } + } + + /// + /// Gets or sets the input parameters to the method. Each + /// parameter is described as a property in the object. If a parameter is both in + /// and out, it appears in both the and + /// properties. + /// + /// + /// + /// A + /// containing all the input parameters to the + /// method. + /// + /// + /// Each parameter in the object should have an + /// + /// qualifier, identifying the order of the parameters in the method call. + /// + public ManagementBaseObject InParameters + { + get + { + RefreshMethodInfo(); + return (null == wmiInParams) ? null : new ManagementBaseObject(wmiInParams); } + } + + /// + /// Gets or sets the output parameters to the method. Each + /// parameter is described as a property in the object. If a parameter is both in + /// and out, it will appear in both the and + /// properties. + /// + /// + /// A containing all the output parameters to the method. + /// + /// + /// Each parameter in this object should have an + /// qualifier to identify the + /// order of the parameters in the method call. + /// The ReturnValue property is a special property of + /// the + /// object and + /// holds the return value of the method. + /// + public ManagementBaseObject OutParameters + { + get + { + RefreshMethodInfo(); + return (null == wmiOutParams) ? null : new ManagementBaseObject(wmiOutParams); } + } + + /// + /// Gets the name of the management class in which the method was first + /// introduced in the class inheritance hierarchy. + /// + /// + /// A string representing the originating + /// management class name. + /// + public string Origin + { + get + { + string className = null; + int status = parent.wbemObject.GetMethodOrigin_(methodName, out className); + + if (status < 0) + { + if (status == (int)tag_WBEMSTATUS.WBEM_E_INVALID_OBJECT) + className = String.Empty; // Interpret as an unspecified property - return "" + else if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + return className; + } + } + + /// + /// Gets a collection of qualifiers defined in the + /// method. Each element is of type + /// and contains information such as the qualifier name, value, and + /// flavor. + /// + /// + /// A containing the + /// qualifiers for this method. + /// + /// + public QualifierDataCollection Qualifiers + { + get + { + if (qualifiers == null) + qualifiers = new QualifierDataCollection(parent, methodName, QualifierType.MethodQualifier); + return qualifiers; + } + } + + }//MethodData +} diff --git a/external/corefx/src/System.Management/src/System/Management/MethodSet.cs b/external/corefx/src/System.Management/src/System/Management/MethodSet.cs new file mode 100644 index 0000000000..8851f7d85d --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/MethodSet.cs @@ -0,0 +1,456 @@ +// 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.Collections; +using System.Runtime.InteropServices; + +namespace System.Management +{ + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + /// + /// Represents the set of methods available in the collection. + /// + /// + /// using System; + /// using System.Management; + /// + /// // This sample demonstrates enumerate all methods in a ManagementClass object. + /// class Sample_MethodDataCollection + /// { + /// public static int Main(string[] args) { + /// ManagementClass diskClass = new ManagementClass("win32_logicaldisk"); + /// MethodDataCollection diskMethods = diskClass.Methods; + /// foreach (MethodData method in diskMethods) { + /// Console.WriteLine("Method = " + method.Name); + /// } + /// return 0; + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// ' This sample demonstrates enumerate all methods in a ManagementClass object. + /// Class Sample_MethodDataCollection + /// Overloads Public Shared Function Main(args() As String) As Integer + /// Dim diskClass As New ManagementClass("win32_logicaldisk") + /// Dim diskMethods As MethodDataCollection = diskClass.Methods + /// Dim method As MethodData + /// For Each method In diskMethods + /// Console.WriteLine("Method = " & method.Name) + /// Next method + /// Return 0 + /// End Function + /// End Class + /// + /// + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + public class MethodDataCollection : ICollection, IEnumerable + { + private ManagementObject parent; + + private class enumLock + { + } //used to lock usage of BeginMethodEnum/NextMethod + + internal MethodDataCollection(ManagementObject parent) : base() + { + this.parent = parent; + } + + // + //ICollection + // + + /// + /// Represents the number of objects in the . + /// + /// + /// The number of objects in the . + /// + public int Count + { + get + { + int i = 0; + IWbemClassObjectFreeThreaded inParameters = null, outParameters = null; + string methodName; + int status = (int)ManagementStatus.Failed; + +#pragma warning disable CA2002 + lock(typeof(enumLock)) +#pragma warning restore CA2002 + { + try + { + status = parent.wbemObject.BeginMethodEnumeration_(0); + + if (status >= 0) + { + methodName = ""; // Condition primer to branch into the while loop. + while (methodName != null && status >= 0 && status != (int)tag_WBEMSTATUS.WBEM_S_NO_MORE_DATA) + { + methodName = null; inParameters = null; outParameters = null; + status = parent.wbemObject.NextMethod_(0, out methodName, out inParameters, out outParameters); + if (status >= 0 && status != (int)tag_WBEMSTATUS.WBEM_S_NO_MORE_DATA) + i++; + } + parent.wbemObject.EndMethodEnumeration_(); // Ignore status. + } + } + catch (COMException e) + { + ManagementException.ThrowWithExtendedInfo(e); + } + } // lock + + if ((status & 0xfffff000) == 0x80041000) + { + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + } + else if ((status & 0x80000000) != 0) + { + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + return i; + } + } + + /// + /// Indicates whether the object is synchronized. + /// + /// + /// if the object is synchronized; + /// otherwise, . + /// + public bool IsSynchronized { get { return false; } + } + + /// + /// Represents the object to be used for synchronization. + /// + /// + /// The object to be used for synchronization. + /// + public object SyncRoot { get { return this; } + } + + /// + /// Copies the into an array. + /// + /// + /// Copies the into an array. + /// + /// The array to which to copy the collection. + /// The index from which to start. + public void CopyTo(Array array, int index) + { + //Use an enumerator to get the MethodData objects and attach them into the target array + foreach (MethodData m in this) + array.SetValue(m, index++); + } + + /// + /// Copies the to a specialized + /// array. + /// + /// The destination array to which to copy the objects. + /// The index in the destination array from which to start the copy. + public void CopyTo(MethodData[] methodArray, int index) + { + CopyTo((Array)methodArray, index); + } + + // + // IEnumerable + // + IEnumerator IEnumerable.GetEnumerator() + { + return (IEnumerator)(new MethodDataEnumerator(parent)); + } + + /// + /// Returns an enumerator for the . + /// + /// + /// Each call to this method + /// returns a new enumerator on the collection. Multiple enumerators can be obtained + /// for the same method collection. However, each enumerator takes a snapshot + /// of the collection, so changes made to the collection after the enumerator was + /// obtained are not reflected. + /// + /// An to enumerate through the collection. + public MethodDataEnumerator GetEnumerator() + { + return new MethodDataEnumerator(parent); + } + + //Enumerator class + /// + /// Represents the enumerator for + /// objects in the . + /// + /// + /// using System; + /// using System.Management; + /// + /// // This sample demonstrates how to enumerate all methods in + /// // Win32_LogicalDisk class using MethodDataEnumerator object. + /// + /// class Sample_MethodDataEnumerator + /// { + /// public static int Main(string[] args) + /// { + /// ManagementClass diskClass = new ManagementClass("win32_logicaldisk"); + /// MethodDataCollection.MethodDataEnumerator diskEnumerator = + /// diskClass.Methods.GetEnumerator(); + /// while(diskEnumerator.MoveNext()) + /// { + /// MethodData method = diskEnumerator.Current; + /// Console.WriteLine("Method = " + method.Name); + /// } + /// return 0; + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// ' This sample demonstrates how to enumerate all methods in + /// ' Win32_LogicalDisk class using MethodDataEnumerator object. + /// + /// Class Sample_MethodDataEnumerator + /// Overloads Public Shared Function Main(args() As String) As Integer + /// Dim diskClass As New ManagementClass("win32_logicaldisk") + /// Dim diskEnumerator As _ + /// MethodDataCollection.MethodDataEnumerator = _ + /// diskClass.Methods.GetEnumerator() + /// While diskEnumerator.MoveNext() + /// Dim method As MethodData = diskEnumerator.Current + /// Console.WriteLine("Method = " & method.Name) + /// End While + /// Return 0 + /// End Function + /// End Class + /// + /// + public class MethodDataEnumerator : IEnumerator + { + private ManagementObject parent; + private ArrayList methodNames; //can't use simple array because we don't know the size... + private IEnumerator en; + + //Internal constructor + //Because WMI doesn't provide a "GetMethodNames" for methods similar to "GetNames" for properties, + //We have to walk the methods list and cache the names here. + //We lock to ensure that another thread doesn't interfere in the Begin/Next sequence. + internal MethodDataEnumerator(ManagementObject parent) + { + this.parent = parent; + methodNames = new ArrayList(); + IWbemClassObjectFreeThreaded inP = null, outP = null; + string tempMethodName; + int status = (int)ManagementStatus.Failed; + +#pragma warning disable CA2002 + lock(typeof(enumLock)) +#pragma warning restore CA2002 + { + try + { + status = parent.wbemObject.BeginMethodEnumeration_(0); + + if (status >= 0) + { + tempMethodName = ""; // Condition primer to branch into the while loop. + while (tempMethodName != null && status >= 0 && status != (int)tag_WBEMSTATUS.WBEM_S_NO_MORE_DATA) + { + tempMethodName = null; + status = parent.wbemObject.NextMethod_(0, out tempMethodName, out inP, out outP); + if (status >= 0 && status != (int)tag_WBEMSTATUS.WBEM_S_NO_MORE_DATA) + methodNames.Add(tempMethodName); + } + parent.wbemObject.EndMethodEnumeration_(); // Ignore status. + } + } + catch (COMException e) + { + ManagementException.ThrowWithExtendedInfo(e); + } + en = methodNames.GetEnumerator(); + } + + if ((status & 0xfffff000) == 0x80041000) + { + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + } + else if ((status & 0x80000000) != 0) + { + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + /// + object IEnumerator.Current { get { return (object)this.Current; } } + + /// + /// Returns the current in the + /// enumeration. + /// + /// The current item in the collection. + public MethodData Current + { + get + { + return new MethodData(parent, (string)en.Current); + } + } + + /// + /// Moves to the next element in the enumeration. + /// + /// if the enumerator was successfully advanced to the next method; if the enumerator has passed the end of the collection. + public bool MoveNext () + { + return en.MoveNext(); + } + + /// + /// Resets the enumerator to the beginning of the enumeration. + /// + public void Reset() + { + en.Reset(); + } + + }//MethodDataEnumerator + + + // + //Methods + // + + /// + /// Returns the specified from the . + /// + /// The name of the method requested. + /// A instance containing all information about the specified method. + public virtual MethodData this[string methodName] + { + get + { + if (null == methodName) + throw new ArgumentNullException ("methodName"); + + return new MethodData(parent, methodName); + } + } + + + /// + /// Removes a from the . + /// + /// The name of the method to remove from the collection. + /// + /// + /// Removing objects from the + /// can only be done when the class has no + /// instances. Any other case will result in an exception. + /// + public virtual void Remove(string methodName) + { + if (parent.GetType() == typeof(ManagementObject)) //can't remove methods from instance + throw new InvalidOperationException(); + + int status = (int)ManagementStatus.Failed; + + try + { + status = parent.wbemObject.DeleteMethod_(methodName); + } + catch (COMException e) + { + ManagementException.ThrowWithExtendedInfo(e); + } + + if ((status & 0xfffff000) == 0x80041000) + { + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + } + else if ((status & 0x80000000) != 0) + { + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + //This variant takes only a method name and assumes a void method with no in/out parameters + /// + /// Adds a to the . + /// + /// + /// Adds a to the . This overload will + /// add a new method with no parameters to the collection. + /// + /// The name of the method to add. + /// + /// Adding objects to the can only + /// be done when the class has no instances. Any other case will result in an + /// exception. + /// + public virtual void Add(string methodName) + { + Add(methodName, null, null); + } + + + + //This variant takes the full information, i.e. the method name and in & out param objects + /// + /// Adds a to the . This overload will add a new method with the + /// specified parameter objects to the collection. + /// + /// The name of the method to add. + /// The holding the input parameters to the method. + /// The holding the output parameters to the method. + /// + /// Adding objects to the can only be + /// done when the class has no instances. Any other case will result in an + /// exception. + /// + public virtual void Add(string methodName, ManagementBaseObject inParameters, ManagementBaseObject outParameters) + { + IWbemClassObjectFreeThreaded wbemIn = null, wbemOut = null; + + if (parent.GetType() == typeof(ManagementObject)) //can't add methods to instance + throw new InvalidOperationException(); + + if (inParameters != null) + wbemIn = inParameters.wbemObject; + if (outParameters != null) + wbemOut = outParameters.wbemObject; + + int status = (int)ManagementStatus.Failed; + + try + { + status = parent.wbemObject.PutMethod_(methodName, 0, wbemIn, wbemOut); + } + catch (COMException e) + { + ManagementException.ThrowWithExtendedInfo(e); + } + + if ((status & 0xfffff000) == 0x80041000) + { + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + } + else if ((status & 0x80000000) != 0) + { + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + }//MethodDataCollection +} diff --git a/external/corefx/src/System.Management/src/System/Management/Property.cs b/external/corefx/src/System.Management/src/System/Management/Property.cs new file mode 100644 index 0000000000..7af2f7072e --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/Property.cs @@ -0,0 +1,835 @@ +// 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.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Globalization; + +namespace System.Management +{ + // We use this class to prevent the accidental returning of a boxed value type to a caller + // If we store a boxed value type in a private field, and return it to the caller through a public + // property or method, the call can potentially change its value. The GetSafeObject method does two things + // 1) If the value is a primitive, we know that it will implement IConvertible. IConvertible.ToType will + // copy a boxed primitive + // 2) In the case of a boxed non-primitive value type, or simply a reference type, we call + // RuntimeHelpers.GetObjectValue. This returns reference types right back to the caller, but if passed + // a boxed non-primitive value type, it will return a boxed copy. We cannot use GetObjectValue for primitives + // because its implementation does not copy boxed primitives. + class ValueTypeSafety + { + public static object GetSafeObject(object theValue) + { + if(null == theValue) + return null; + else if(theValue.GetType().IsPrimitive) + return ((IConvertible)theValue).ToType(typeof(object), null); + else + return RuntimeHelpers.GetObjectValue(theValue); + } + } + + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + /// + /// Represents information about a WMI property. + /// + /// + /// using System; + /// using System.Management; + /// + /// // This sample displays all properties that qualifies the "DeviceID" property + /// // in Win32_LogicalDisk.DeviceID='C' instance. + /// class Sample_PropertyData + /// { + /// public static int Main(string[] args) { + /// ManagementObject disk = + /// new ManagementObject("Win32_LogicalDisk.DeviceID=\"C:\""); + /// PropertyData diskProperty = disk.Properties["DeviceID"]; + /// Console.WriteLine("Name: " + diskProperty.Name); + /// Console.WriteLine("Type: " + diskProperty.Type); + /// Console.WriteLine("Value: " + diskProperty.Value); + /// Console.WriteLine("IsArray: " + diskProperty.IsArray); + /// Console.WriteLine("IsLocal: " + diskProperty.IsLocal); + /// Console.WriteLine("Origin: " + diskProperty.Origin); + /// return 0; + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// ' This sample displays all properties that qualifies the "DeviceID" property + /// ' in Win32_LogicalDisk.DeviceID='C' instance. + /// Class Sample_PropertyData + /// Overloads Public Shared Function Main(args() As String) As Integer + /// Dim disk As New ManagementObject("Win32_LogicalDisk.DeviceID=""C:""") + /// Dim diskProperty As PropertyData = disk.Properties("DeviceID") + /// Console.WriteLine("Name: " & diskProperty.Name) + /// Console.WriteLine("Type: " & diskProperty.Type) + /// Console.WriteLine("Value: " & diskProperty.Value) + /// Console.WriteLine("IsArray: " & diskProperty.IsArray) + /// Console.WriteLine("IsLocal: " & diskProperty.IsLocal) + /// Console.WriteLine("Origin: " & diskProperty.Origin) + /// Return 0 + /// End Function + /// End Class + /// + /// + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + public class PropertyData + { + private ManagementBaseObject parent; //need access to IWbemClassObject pointer to be able to refresh property info + //and get property qualifiers + private string propertyName; + + private Object propertyValue; + private Int64 propertyNullEnumValue = 0; + private int propertyType; + private int propertyFlavor; + private QualifierDataCollection qualifiers; + + internal PropertyData(ManagementBaseObject parent, string propName) + { + this.parent = parent; + this.propertyName = propName; + qualifiers = null; + RefreshPropertyInfo(); + } + + //This private function is used to refresh the information from the Wmi object before returning the requested data + private void RefreshPropertyInfo() + { + propertyValue = null; // Needed so we don't leak this in/out parameter... + + int status = parent.wbemObject.Get_(propertyName, 0, ref propertyValue, ref propertyType, ref propertyFlavor); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + /// + /// Gets or sets the name of the property. + /// + /// + /// A string containing the name of the + /// property. + /// + public string Name + { //doesn't change for this object so we don't need to refresh + get { return propertyName != null ? propertyName : ""; } + } + + /// + /// Gets or sets the current value of the property. + /// + /// + /// An object containing the value of the + /// property. + /// + public Object Value + { + get { + RefreshPropertyInfo(); + return ValueTypeSafety.GetSafeObject(MapWmiValueToValue(propertyValue, + (CimType)(propertyType & ~(int)tag_CIMTYPE_ENUMERATION.CIM_FLAG_ARRAY), + (0 != (propertyType & (int)tag_CIMTYPE_ENUMERATION.CIM_FLAG_ARRAY)))); + } + set { + RefreshPropertyInfo(); + + object newValue = MapValueToWmiValue(value, + (CimType)(propertyType & ~(int)tag_CIMTYPE_ENUMERATION.CIM_FLAG_ARRAY), + (0 != (propertyType & (int)tag_CIMTYPE_ENUMERATION.CIM_FLAG_ARRAY))); + + int status = parent.wbemObject.Put_(propertyName, 0, ref newValue, 0); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + //if succeeded and this object has a path, update the path to reflect the new key value + //NOTE : we could only do this for key properties but since it's not trivial to find out + // whether this property is a key or not, we just do it for any property + else + if (parent.GetType() == typeof(ManagementObject)) + ((ManagementObject)parent).Path.UpdateRelativePath((string)parent["__RELPATH"]); + + } + } + + /// + /// Gets or sets the CIM type of the property. + /// + /// + /// A value + /// representing the CIM type of the property. + /// + public CimType Type { + get { + RefreshPropertyInfo(); + return (CimType)(propertyType & ~(int)tag_CIMTYPE_ENUMERATION.CIM_FLAG_ARRAY); + } + } + + /// + /// Gets or sets a value indicating whether the property has been defined in the current WMI class. + /// + /// + /// if the property has been defined + /// in the current WMI class; otherwise, . + /// + public bool IsLocal + { + get { + RefreshPropertyInfo(); + return ((propertyFlavor & (int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_ORIGIN_PROPAGATED) != 0) ? false : true ; } + } + + /// + /// Gets or sets a value indicating whether the property is an array. + /// + /// + /// if the property is an array; otherwise, . + /// + public bool IsArray + { + get { + RefreshPropertyInfo(); + return ((propertyType & (int)tag_CIMTYPE_ENUMERATION.CIM_FLAG_ARRAY) != 0);} + } + + /// + /// Gets or sets the name of the WMI class in the hierarchy in which the property was introduced. + /// + /// + /// A string containing the name of the + /// originating WMI class. + /// + public string Origin + { + get { + string className = null; + int status = parent.wbemObject.GetPropertyOrigin_(propertyName, out className); + + if (status < 0) + { + if (status == (int)tag_WBEMSTATUS.WBEM_E_INVALID_OBJECT) + className = String.Empty; // Interpret as an unspecified property - return "" + else if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + return className; + } + } + + + /// + /// Gets or sets the set of qualifiers defined on the property. + /// + /// + /// A that represents + /// the set of qualifiers defined on the property. + /// + public QualifierDataCollection Qualifiers + { + get { + if (qualifiers == null) + qualifiers = new QualifierDataCollection(parent, propertyName, QualifierType.PropertyQualifier); + + return qualifiers; + } + } + internal Int64 NullEnumValue + { + get { + return propertyNullEnumValue; + } + + set { + propertyNullEnumValue = value; + } + } + + /// + /// Takes a property value returned from WMI and maps it to an + /// appropriate managed code representation. + /// + /// + /// + /// + internal static object MapWmiValueToValue(object wmiValue, CimType type, bool isArray) + { + object val = null; + + if ((System.DBNull.Value != wmiValue) && (null != wmiValue)) + { + if (isArray) + { + Array wmiValueArray = (Array)wmiValue; + int length = wmiValueArray.Length; + + switch (type) + { + case CimType.UInt16: + val = new UInt16 [length]; + + for (int i = 0; i < length; i++) + ((UInt16[])val) [i] = (UInt16)((Int32)(wmiValueArray.GetValue(i))); + break; + + case CimType.UInt32: + val = new UInt32 [length]; + + for (int i = 0; i < length; i++) + ((UInt32[])val)[i] = (UInt32)((Int32)(wmiValueArray.GetValue(i))); + break; + + case CimType.UInt64: + val = new UInt64 [length]; + + for (int i = 0; i < length; i++) + ((UInt64[])val) [i] = Convert.ToUInt64((String)(wmiValueArray.GetValue(i)),(IFormatProvider)CultureInfo.CurrentCulture.GetFormat(typeof(System.UInt64))); + break; + + case CimType.SInt8: + val = new SByte [length]; + + for (int i = 0; i < length; i++) + ((SByte[])val) [i] = (SByte)((Int16)(wmiValueArray.GetValue(i))); + break; + + case CimType.SInt64: + val = new Int64 [length]; + + for (int i = 0; i < length; i++) + ((Int64[])val) [i] = Convert.ToInt64((String)(wmiValueArray.GetValue(i)),(IFormatProvider)CultureInfo.CurrentCulture.GetFormat(typeof(System.Int64))); + break; + + case CimType.Char16: + val = new Char [length]; + + for (int i = 0; i < length; i++) + ((Char[])val) [i] = (Char)((Int16)(wmiValueArray.GetValue(i))); + break; + + case CimType.Object: + val = new ManagementBaseObject [length]; + + for (int i = 0; i < length; i++) + ((ManagementBaseObject[])val) [i] = new ManagementBaseObject(new IWbemClassObjectFreeThreaded(Marshal.GetIUnknownForObject(wmiValueArray.GetValue(i)))); + break; + + default: + val = wmiValue; + break; + } + } + else + { + switch (type) + { + case CimType.SInt8: + val = (SByte)((Int16)wmiValue); + break; + + case CimType.UInt16: + val = (UInt16)((Int32)wmiValue); + break; + + case CimType.UInt32: + val = (UInt32)((Int32)wmiValue); + break; + + case CimType.UInt64: + val = Convert.ToUInt64((String)wmiValue,(IFormatProvider)CultureInfo.CurrentCulture.GetFormat(typeof(System.UInt64))); + break; + + case CimType.SInt64: + val = Convert.ToInt64((String)wmiValue,(IFormatProvider)CultureInfo.CurrentCulture.GetFormat(typeof(System.Int64))); + break; + + case CimType.Char16: + val = (Char)((Int16)wmiValue); + break; + + case CimType.Object: + val = new ManagementBaseObject(new IWbemClassObjectFreeThreaded(Marshal.GetIUnknownForObject(wmiValue))); + break; + + default: + val = wmiValue; + break; + } + } + } + + return val; + } + + /// + /// Takes a managed code value, together with a desired property + /// + /// + /// + /// + internal static object MapValueToWmiValue(object val, CimType type, bool isArray) + { + object wmiValue = System.DBNull.Value; + CultureInfo culInfo = CultureInfo.InvariantCulture; + if (null != val) + { + if (isArray) + { + Array valArray = (Array)val; + int length = valArray.Length; + + switch (type) + { + case CimType.SInt8: + wmiValue = new Int16 [length]; + for (int i = 0; i < length; i++) + ((Int16[])(wmiValue))[i] = (Int16)Convert.ToSByte(valArray.GetValue(i),(IFormatProvider)culInfo.GetFormat(typeof(System.SByte))); + break; + + case CimType.UInt8: + if (val is Byte[]) + wmiValue = val; + else + { + wmiValue = new Byte [length]; + for (int i = 0; i < length; i++) + ((Byte[])wmiValue)[i] = Convert.ToByte(valArray.GetValue(i),(IFormatProvider)culInfo.GetFormat(typeof(System.Byte))); + } + break; + + case CimType.SInt16: + if (val is Int16[]) + wmiValue = val; + else + { + wmiValue = new Int16 [length]; + for (int i = 0; i < length; i++) + ((Int16[])(wmiValue))[i] = Convert.ToInt16(valArray.GetValue(i),(IFormatProvider)culInfo.GetFormat(typeof(System.Int16))); + } + break; + + case CimType.UInt16: + wmiValue = new Int32 [length]; + for (int i = 0; i < length; i++) + ((Int32[])(wmiValue))[i] = (Int32)(Convert.ToUInt16(valArray.GetValue(i),(IFormatProvider)culInfo.GetFormat(typeof(System.UInt16)))); + break; + + case CimType.SInt32: + if (val is Int32[]) + wmiValue = val; + else + { + wmiValue = new Int32 [length]; + for (int i = 0; i < length; i++) + ((Int32[])(wmiValue))[i] = Convert.ToInt32(valArray.GetValue(i),(IFormatProvider)culInfo.GetFormat(typeof(System.Int32))); + } + break; + + case CimType.UInt32: + wmiValue = new Int32 [length]; + for (int i = 0; i < length; i++) + ((Int32[])(wmiValue))[i] = (Int32)(Convert.ToUInt32(valArray.GetValue(i),(IFormatProvider)culInfo.GetFormat(typeof(System.UInt32)))); + break; + + case CimType.SInt64: + wmiValue = new String [length]; + for (int i = 0; i < length; i++) + ((String[])(wmiValue))[i] = (Convert.ToInt64(valArray.GetValue(i),(IFormatProvider)culInfo.GetFormat(typeof(System.Int64)))).ToString((IFormatProvider)culInfo.GetFormat(typeof(System.Int64))); + break; + + case CimType.UInt64: + wmiValue = new String [length]; + for (int i = 0; i < length; i++) + ((String[])(wmiValue))[i] = (Convert.ToUInt64(valArray.GetValue(i),(IFormatProvider)culInfo.GetFormat(typeof(System.UInt64)))).ToString((IFormatProvider)culInfo.GetFormat(typeof(System.UInt64))); + break; + + case CimType.Real32: + if (val is Single[]) + wmiValue = val; + else + { + wmiValue = new Single [length]; + for (int i = 0; i < length; i++) + ((Single[])(wmiValue))[i] = Convert.ToSingle(valArray.GetValue(i),(IFormatProvider)culInfo.GetFormat(typeof(System.Single))); + } + break; + + case CimType.Real64: + if (val is Double[]) + wmiValue = val; + else + { + wmiValue = new Double [length]; + for (int i = 0; i < length; i++) + ((Double[])(wmiValue))[i] = Convert.ToDouble(valArray.GetValue(i),(IFormatProvider)culInfo.GetFormat(typeof(System.Double))); + } + break; + + case CimType.Char16: + wmiValue = new Int16 [length]; + for (int i = 0; i < length; i++) + ((Int16[])(wmiValue))[i] = (Int16)Convert.ToChar(valArray.GetValue(i),(IFormatProvider)culInfo.GetFormat(typeof(System.Char))); + break; + + case CimType.String: + case CimType.DateTime: + case CimType.Reference: + if (val is String[]) + wmiValue = val; + else + { + wmiValue = new String [length]; + for (int i = 0; i < length; i++) + ((String[])(wmiValue))[i] = (valArray.GetValue(i)).ToString(); + } + break; + + case CimType.Boolean: + if (val is Boolean[]) + wmiValue = val; + else + { + wmiValue = new Boolean [length]; + for (int i = 0; i < length; i++) + ((Boolean[])(wmiValue))[i] = Convert.ToBoolean(valArray.GetValue(i),(IFormatProvider)culInfo.GetFormat(typeof(System.Boolean))); + } + break; + + case CimType.Object: + wmiValue = new IWbemClassObject_DoNotMarshal[length]; + + for (int i = 0; i < length; i++) + { + ((IWbemClassObject_DoNotMarshal[])(wmiValue))[i] = (IWbemClassObject_DoNotMarshal)(Marshal.GetObjectForIUnknown(((ManagementBaseObject)valArray.GetValue(i)).wbemObject)); + } + break; + + default: + wmiValue = val; + break; + } + } + else + { + switch (type) + { + case CimType.SInt8: + wmiValue = (Int16)Convert.ToSByte(val,(IFormatProvider)culInfo.GetFormat(typeof(System.Int16))); + break; + + case CimType.UInt8: + wmiValue = Convert.ToByte(val,(IFormatProvider)culInfo.GetFormat(typeof(System.Byte))); + break; + + case CimType.SInt16: + wmiValue = Convert.ToInt16(val,(IFormatProvider)culInfo.GetFormat(typeof(System.Int16))); + break; + + case CimType.UInt16: + wmiValue = (Int32)(Convert.ToUInt16(val,(IFormatProvider)culInfo.GetFormat(typeof(System.UInt16)))); + break; + + case CimType.SInt32: + wmiValue = Convert.ToInt32(val,(IFormatProvider)culInfo.GetFormat(typeof(System.Int32))); + break; + + case CimType.UInt32: + wmiValue = (Int32)Convert.ToUInt32(val,(IFormatProvider)culInfo.GetFormat(typeof(System.UInt32))); + break; + + case CimType.SInt64: + wmiValue = (Convert.ToInt64(val,(IFormatProvider)culInfo.GetFormat(typeof(System.Int64)))).ToString((IFormatProvider)culInfo.GetFormat(typeof(System.Int64))); + break; + + case CimType.UInt64: + wmiValue = (Convert.ToUInt64(val,(IFormatProvider)culInfo.GetFormat(typeof(System.UInt64)))).ToString((IFormatProvider)culInfo.GetFormat(typeof(System.UInt64))); + break; + + case CimType.Real32: + wmiValue = Convert.ToSingle(val,(IFormatProvider)culInfo.GetFormat(typeof(System.Single))); + break; + + case CimType.Real64: + wmiValue = Convert.ToDouble(val,(IFormatProvider)culInfo.GetFormat(typeof(System.Double))); + break; + + case CimType.Char16: + wmiValue = (Int16)Convert.ToChar(val,(IFormatProvider)culInfo.GetFormat(typeof(System.Char))); + break; + + case CimType.String: + case CimType.DateTime: + case CimType.Reference: + wmiValue = val.ToString(); + break; + + case CimType.Boolean: + wmiValue = Convert.ToBoolean(val,(IFormatProvider)culInfo.GetFormat(typeof(System.Boolean))); + break; + + case CimType.Object: + if (val is ManagementBaseObject) + { + wmiValue = Marshal.GetObjectForIUnknown(((ManagementBaseObject) val).wbemObject); + } + else + { + wmiValue = val; + } + break; + + default: + wmiValue = val; + break; + } + } + } + + return wmiValue; + } + + internal static object MapValueToWmiValue(object val, out bool isArray, out CimType type) + { + object wmiValue = System.DBNull.Value; + CultureInfo culInfo = CultureInfo.InvariantCulture; + isArray = false; + type = 0; + + if (null != val) + { + isArray = val.GetType().IsArray; + Type valueType = val.GetType(); + + if (isArray) + { + Type elementType = valueType.GetElementType(); + + // Casting primitive types to object[] is not allowed + if (elementType.IsPrimitive) + { + if (elementType == typeof(System.Byte)) + { + byte[] arrayValue = (byte[])val; + int length = arrayValue.Length; + type = CimType.UInt8; + wmiValue = new short[length]; + + for (int i = 0; i < length; i++) + ((short[])wmiValue) [i] = ((IConvertible)((System.Byte)(arrayValue[i]))).ToInt16(null); + } + else if (elementType == typeof(System.SByte)) + { + sbyte[] arrayValue = (sbyte[])val; + int length = arrayValue.Length; + type = CimType.SInt8; + wmiValue = new short[length]; + + for (int i = 0; i < length; i++) + ((short[])wmiValue) [i] = ((IConvertible)((System.SByte)(arrayValue[i]))).ToInt16(null); + } + else if (elementType == typeof(System.Boolean)) + { + type = CimType.Boolean; + wmiValue = (bool[])val; + } + else if (elementType == typeof(System.UInt16)) + { + ushort[] arrayValue = (ushort[])val; + int length = arrayValue.Length; + type = CimType.UInt16; + wmiValue = new int[length]; + + for (int i = 0; i < length; i++) + ((int[])wmiValue) [i] = ((IConvertible)((System.UInt16)(arrayValue[i]))).ToInt32(null); + } + else if (elementType == typeof(System.Int16)) + { + type = CimType.SInt16; + wmiValue = (short[])val; + } + else if (elementType == typeof(System.Int32)) + { + type = CimType.SInt32; + wmiValue = (int[])val; + } + else if (elementType == typeof(System.UInt32)) + { + uint[] arrayValue = (uint[])val; + int length = arrayValue.Length; + type = CimType.UInt32; + wmiValue = new string[length]; + + for (int i = 0; i < length; i++) + ((string[])wmiValue) [i] = ((System.UInt32)(arrayValue[i])).ToString((IFormatProvider)culInfo.GetFormat(typeof(System.UInt32))); + } + else if (elementType == typeof(System.UInt64)) + { + ulong[] arrayValue = (ulong[])val; + int length = arrayValue.Length; + type = CimType.UInt64; + wmiValue = new string[length]; + + for (int i = 0; i < length; i++) + ((string[])wmiValue) [i] = ((System.UInt64)(arrayValue[i])).ToString((IFormatProvider)culInfo.GetFormat(typeof(System.UInt64))); + } + else if (elementType == typeof(System.Int64)) + { + long[] arrayValue = (long[])val; + int length = arrayValue.Length; + type = CimType.SInt64; + wmiValue = new string[length]; + + for (int i = 0; i < length; i++) + ((string[])wmiValue) [i] = ((long)(arrayValue[i])).ToString((IFormatProvider)culInfo.GetFormat(typeof(System.Int64))); + } + else if (elementType == typeof(System.Single)) + { + type = CimType.Real32; + wmiValue = (System.Single[])val; + } + else if (elementType == typeof(System.Double)) + { + type = CimType.Real64; + wmiValue = (double[])val; + } + else if (elementType == typeof(System.Char)) + { + char[] arrayValue = (char[])val; + int length = arrayValue.Length; + type = CimType.Char16; + wmiValue = new short[length]; + + for (int i = 0; i < length; i++) + ((short[])wmiValue) [i] = ((IConvertible)((System.Char)(arrayValue[i]))).ToInt16(null); + } + } + else + { + // Non-primitive types + if (elementType == typeof(System.String)) + { + type = CimType.String; + wmiValue = (string[])val; + } + else + { + // Check for an embedded object array + if (val is ManagementBaseObject[]) + { + Array valArray = (Array)val; + int length = valArray.Length; + type = CimType.Object; + wmiValue = new IWbemClassObject_DoNotMarshal[length]; + + for (int i = 0; i < length; i++) + { + ((IWbemClassObject_DoNotMarshal[])(wmiValue))[i] = (IWbemClassObject_DoNotMarshal)(Marshal.GetObjectForIUnknown(((ManagementBaseObject)valArray.GetValue(i)).wbemObject)); + } + } + } + } + } + else // Non-array values + { + if (valueType == typeof(System.UInt16)) + { + type = CimType.UInt16; + wmiValue = ((IConvertible)((System.UInt16)val)).ToInt32(null); + } + else if (valueType == typeof(System.UInt32)) + { + type = CimType.UInt32; + if (((System.UInt32)val & 0x80000000) != 0) + wmiValue = Convert.ToString(val,(IFormatProvider)culInfo.GetFormat(typeof(System.UInt32))); + else + wmiValue = Convert.ToInt32(val,(IFormatProvider)culInfo.GetFormat(typeof(System.Int32))); + } + else if (valueType == typeof(System.UInt64)) + { + type = CimType.UInt64; + wmiValue = ((System.UInt64)val).ToString((IFormatProvider)culInfo.GetFormat(typeof(System.UInt64))); + } + else if (valueType == typeof(System.SByte)) + { + type = CimType.SInt8; + wmiValue = ((IConvertible)((System.SByte)val)).ToInt16(null); + } + else if (valueType == typeof(System.Byte)) + { + type = CimType.UInt8; + wmiValue = val; + } + else if (valueType == typeof(System.Int16)) + { + type = CimType.SInt16; + wmiValue = val; + } + else if (valueType == typeof(System.Int32)) + { + type = CimType.SInt32; + wmiValue = val; + } + else if (valueType == typeof(System.Int64)) + { + type = CimType.SInt64; + wmiValue = val.ToString(); + } + else if (valueType == typeof(System.Boolean)) + { + type = CimType.Boolean; + wmiValue = val; + } + else if (valueType == typeof(System.Single)) + { + type = CimType.Real32; + wmiValue = val; + } + else if (valueType == typeof(System.Double)) + { + type = CimType.Real64; + wmiValue = val; + } + else if (valueType == typeof(System.Char)) + { + type = CimType.Char16; + wmiValue = ((IConvertible)((System.Char)val)).ToInt16(null); + } + else if (valueType == typeof(System.String)) + { + type = CimType.String; + wmiValue = val; + } + else + { + // Check for an embedded object + if (val is ManagementBaseObject) + { + type = CimType.Object; + wmiValue = Marshal.GetObjectForIUnknown(((ManagementBaseObject) val).wbemObject); + } + } + } + } + + return wmiValue; + } + + }//PropertyData +} diff --git a/external/corefx/src/System.Management/src/System/Management/PropertySet.cs b/external/corefx/src/System.Management/src/System/Management/PropertySet.cs new file mode 100644 index 0000000000..163c1b0fd5 --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/PropertySet.cs @@ -0,0 +1,513 @@ +// 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.Collections; +using System.Runtime.InteropServices; + +namespace System.Management +{ + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + /// + /// Represents the set of properties of a WMI object. + /// + /// + /// using System; + /// using System.Management; + /// + /// // This sample demonstrates how to enumerate properties + /// // in a ManagementObject object. + /// class Sample_PropertyDataCollection + /// { + /// public static int Main(string[] args) { + /// ManagementObject disk = new ManagementObject("win32_logicaldisk.deviceid = \"c:\""); + /// PropertyDataCollection diskProperties = disk.Properties; + /// foreach (PropertyData diskProperty in diskProperties) { + /// Console.WriteLine("Property = " + diskProperty.Name); + /// } + /// return 0; + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// ' This sample demonstrates how to enumerate properties + /// ' in a ManagementObject object. + /// Class Sample_PropertyDataCollection + /// Overloads Public Shared Function Main(args() As String) As Integer + /// Dim disk As New ManagementObject("win32_logicaldisk.deviceid=""c:""") + /// Dim diskProperties As PropertyDataCollection = disk.Properties + /// Dim diskProperty As PropertyData + /// For Each diskProperty In diskProperties + /// Console.WriteLine("Property = " & diskProperty.Name) + /// Next diskProperty + /// Return 0 + /// End Function + /// End Class + /// + /// + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + public class PropertyDataCollection : ICollection, IEnumerable + { + private ManagementBaseObject parent; + bool isSystem; + + internal PropertyDataCollection(ManagementBaseObject parent, bool isSystem) : base() + { + this.parent = parent; + this.isSystem = isSystem; + } + + // + //ICollection + // + + /// + /// Gets or sets the number of objects in the . + /// + /// + /// The number of objects in the collection. + /// + public int Count + { + get { + string[] propertyNames = null; object qualVal = null; + int flag; + if (isSystem) + flag = (int)tag_WBEM_CONDITION_FLAG_TYPE.WBEM_FLAG_SYSTEM_ONLY; + else + flag = (int)tag_WBEM_CONDITION_FLAG_TYPE.WBEM_FLAG_NONSYSTEM_ONLY; + + flag |= (int)tag_WBEM_CONDITION_FLAG_TYPE.WBEM_FLAG_ALWAYS; + + int status = parent.wbemObject.GetNames_(null, flag, ref qualVal, out propertyNames); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + return propertyNames.Length; + } + } + + /// + /// Gets or sets a value indicating whether the object is synchronized. + /// + /// + /// if the object is synchronized; + /// otherwise, . + /// + public bool IsSynchronized { get { return false; } + } + + /// + /// Gets or sets the object to be used for synchronization. + /// + /// + /// The object to be used for synchronization. + /// + public object SyncRoot { get { return this; } + } + + /// + /// Copies the into an array. + /// + /// + /// Copies the into an array. + /// + /// The array to which to copy the . + /// The index from which to start copying. + public void CopyTo(Array array, Int32 index) + { + if (null == array) + throw new ArgumentNullException("array"); + + if ((index < array.GetLowerBound(0)) || (index > array.GetUpperBound(0))) + throw new ArgumentOutOfRangeException("index"); + + // Get the names of the properties + string[] nameArray = null; + object dummy = null; + int flag = 0; + + if (isSystem) + flag |= (int)tag_WBEM_CONDITION_FLAG_TYPE.WBEM_FLAG_SYSTEM_ONLY; + else + flag |= (int)tag_WBEM_CONDITION_FLAG_TYPE.WBEM_FLAG_NONSYSTEM_ONLY; + + flag |= (int)tag_WBEM_CONDITION_FLAG_TYPE.WBEM_FLAG_ALWAYS; + + int status = this.parent.wbemObject.GetNames_(null, flag, ref dummy, out nameArray); + + if (status >= 0) + { + if ((index + nameArray.Length) > array.Length) + throw new ArgumentException(null,"index"); + + foreach (string propertyName in nameArray) + array.SetValue(new PropertyData(parent, propertyName), index++); + } + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + return; + } + + /// + /// Copies the to a specialized object + /// array. + /// + /// The destination array to contain the copied . + /// The index in the destination array from which to start copying. + public void CopyTo(PropertyData[] propertyArray, Int32 index) + { + CopyTo((Array)propertyArray, index); + } + // + // IEnumerable + // + IEnumerator IEnumerable.GetEnumerator() + { + return (IEnumerator)(new PropertyDataEnumerator(parent, isSystem)); + } + + /// + /// Returns the enumerator for this . + /// + /// + /// An + /// that can be used to iterate through the collection. + /// + public PropertyDataEnumerator GetEnumerator() + { + return new PropertyDataEnumerator(parent, isSystem); + } + + //Enumerator class + /// + /// Represents the enumerator for + /// objects in the . + /// + /// + /// using System; + /// using System.Management; + /// + /// // This sample demonstrates how to enumerate all properties in a + /// // ManagementObject using the PropertyDataEnumerator object. + /// class Sample_PropertyDataEnumerator + /// { + /// public static int Main(string[] args) { + /// ManagementObject disk = new ManagementObject("Win32_LogicalDisk.DeviceID='C:'"); + /// PropertyDataCollection.PropertyDataEnumerator propertyEnumerator = disk.Properties.GetEnumerator(); + /// while(propertyEnumerator.MoveNext()) { + /// PropertyData p = (PropertyData)propertyEnumerator.Current; + /// Console.WriteLine("Property found: " + p.Name); + /// } + /// return 0; + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// ' This sample demonstrates how to enumerate all properties in a + /// ' ManagementObject using PropertyDataEnumerator object. + /// Class Sample_PropertyDataEnumerator + /// Overloads Public Shared Function Main(args() As String) As Integer + /// Dim disk As New ManagementObject("Win32_LogicalDisk.DeviceID='C:'") + /// Dim propertyEnumerator As _ + /// PropertyDataCollection.PropertyDataEnumerator = disk.Properties.GetEnumerator() + /// While propertyEnumerator.MoveNext() + /// Dim p As PropertyData = _ + /// CType(propertyEnumerator.Current, PropertyData) + /// Console.WriteLine("Property found: " & p.Name) + /// End While + /// Return 0 + /// End Function + /// End Class + /// + /// + public class PropertyDataEnumerator : IEnumerator + { + private ManagementBaseObject parent; + private string[] propertyNames; + private int index; + + internal PropertyDataEnumerator(ManagementBaseObject parent, bool isSystem) + { + this.parent = parent; + propertyNames = null; index = -1; + int flag; object qualVal = null; + + if (isSystem) + flag = (int)tag_WBEM_CONDITION_FLAG_TYPE.WBEM_FLAG_SYSTEM_ONLY; + else + flag = (int)tag_WBEM_CONDITION_FLAG_TYPE.WBEM_FLAG_NONSYSTEM_ONLY; + + flag |= (int)tag_WBEM_CONDITION_FLAG_TYPE.WBEM_FLAG_ALWAYS; + + int status = parent.wbemObject.GetNames_(null, flag, ref qualVal, out propertyNames); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + /// + object IEnumerator.Current { get { return (object)this.Current; } } + + /// + /// Gets the current in the enumeration. + /// + /// + /// The current + /// element in the collection. + /// + public PropertyData Current + { + get { + if ((index == -1) || (index == propertyNames.Length)) + throw new InvalidOperationException(); + else + return new PropertyData(parent, propertyNames[index]); + } + } + + /// + /// Moves to the next element in the + /// enumeration. + /// + /// + /// if the enumerator was successfully advanced to the next element; + /// if the enumerator has passed the end of the collection. + /// + public bool MoveNext() + { + if (index == propertyNames.Length) //passed the end of the array + return false; //don't advance the index any more + + index++; + return (index == propertyNames.Length) ? false : true; + } + + /// + /// Resets the enumerator to the beginning of the + /// enumeration. + /// + public void Reset() + { + index = -1; + } + + }//PropertyDataEnumerator + + + + // + // Methods + // + + /// + /// Returns the specified property from the , using [] syntax. + /// + /// The name of the property to retrieve. + /// + /// A , based on + /// the name specified. + /// + /// + /// ManagementObject o = new ManagementObject("Win32_LogicalDisk.Name = 'C:'"); + /// Console.WriteLine("Free space on C: drive is: ", c.Properties["FreeSpace"].Value); + /// + /// Dim o As New ManagementObject("Win32_LogicalDisk.Name=""C:""") + /// Console.WriteLine("Free space on C: drive is: " & c.Properties("FreeSpace").Value) + /// + /// + public virtual PropertyData this[string propertyName] + { + get { + if (null == propertyName) + throw new ArgumentNullException("propertyName"); + + return new PropertyData(parent, propertyName); + } + } + + /// + /// Removes a from the . + /// + /// The name of the property to be removed. + /// + /// Properties can only be removed from class definitions, + /// not from instances. This method is only valid when invoked on a property + /// collection in a . + /// + /// + /// ManagementClass c = new ManagementClass("MyClass"); + /// c.Properties.Remove("PropThatIDontWantOnThisClass"); + /// + /// Dim c As New ManagementClass("MyClass") + /// c.Properties.Remove("PropThatIDontWantOnThisClass") + /// + /// + public virtual void Remove(string propertyName) + { + // On instances, reset the property to the default value for the class. + if (parent.GetType() == typeof(ManagementObject)) + { + ManagementClass cls = new ManagementClass(parent.ClassPath); + parent.SetPropertyValue(propertyName, cls.GetPropertyValue(propertyName)); + } + else + { + int status = parent.wbemObject.Delete_(propertyName); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + } + + /// + /// Adds a new with the specified value. + /// + /// + /// Adds a new with the specified value. The value cannot + /// be null and must be convertable to a CIM type. + /// + /// The name of the new property. + /// The value of the property (cannot be null). + /// + /// Properties can only be added to class definitions, not + /// to instances. This method is only valid when invoked on a + /// in + /// a . + /// + public virtual void Add(string propertyName, Object propertyValue) + { + if (null == propertyValue) + throw new ArgumentNullException("propertyValue"); + + if (parent.GetType() == typeof(ManagementObject)) //can't add properties to instance + throw new InvalidOperationException(); + + CimType cimType = 0; + bool isArray = false; + object wmiValue = PropertyData.MapValueToWmiValue(propertyValue, out isArray, out cimType); + int wmiCimType = (int)cimType; + + if (isArray) + wmiCimType |= (int)tag_CIMTYPE_ENUMERATION.CIM_FLAG_ARRAY; + + int status = parent.wbemObject.Put_(propertyName, 0, ref wmiValue, wmiCimType); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + /// + /// Adds a new with the specified value and CIM type. + /// + /// The name of the property. + /// The value of the property (which can be null). + /// The CIM type of the property. + /// + /// Properties can only be added to class definitions, not + /// to instances. This method is only valid when invoked on a + /// in + /// a . + /// + public void Add(string propertyName, Object propertyValue, CimType propertyType) + { + if (null == propertyName) + throw new ArgumentNullException("propertyName"); + + if (parent.GetType() == typeof(ManagementObject)) //can't add properties to instance + throw new InvalidOperationException(); + + int wmiCimType = (int)propertyType; + bool isArray = false; + + if ((null != propertyValue) && propertyValue.GetType().IsArray) + { + isArray = true; + wmiCimType = (wmiCimType | (int)tag_CIMTYPE_ENUMERATION.CIM_FLAG_ARRAY); + } + + object wmiValue = PropertyData.MapValueToWmiValue(propertyValue, propertyType, isArray); + + int status = parent.wbemObject.Put_(propertyName, 0, ref wmiValue, wmiCimType); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + /// + /// Adds a new with no assigned value. + /// + /// The name of the property. + /// The CIM type of the property. + /// to specify that the property is an array type; otherwise, . + /// + /// Properties can only be added to class definitions, not + /// to instances. This method is only valid when invoked on a + /// in + /// a . + /// + public void Add(string propertyName, CimType propertyType, bool isArray) + { + if (null == propertyName) + throw new ArgumentNullException(propertyName); + + if (parent.GetType() == typeof(ManagementObject)) //can't add properties to instance + throw new InvalidOperationException(); + + int wmiCimType = (int)propertyType; + + if (isArray) + wmiCimType = (wmiCimType | (int)tag_CIMTYPE_ENUMERATION.CIM_FLAG_ARRAY); + + object dummyObj = System.DBNull.Value; + + int status = parent.wbemObject.Put_(propertyName, 0, ref dummyObj, wmiCimType); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + }//PropertyDataCollection +} diff --git a/external/corefx/src/System.Management/src/System/Management/Qualifier.cs b/external/corefx/src/System.Management/src/System/Management/Qualifier.cs new file mode 100644 index 0000000000..4ba072e7c0 --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/Qualifier.cs @@ -0,0 +1,368 @@ +// 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.Runtime.InteropServices; +using System.Globalization; + +namespace System.Management +{ + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + /// + /// Contains information about a WMI qualifier. + /// + /// + /// using System; + /// using System.Management; + /// + /// // This sample demonstrates how to enumerate qualifiers + /// // of a ManagementClass object. + /// class Sample_QualifierData + /// { + /// public static int Main(string[] args) { + /// ManagementClass diskClass = new ManagementClass("Win32_LogicalDisk"); + /// diskClass.Options.UseAmendedQualifiers = true; + /// QualifierData diskQualifier = diskClass.Qualifiers["Description"]; + /// Console.WriteLine(diskQualifier.Name + " = " + diskQualifier.Value); + /// return 0; + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// ' This sample demonstrates how to enumerate qualifiers + /// ' of a ManagementClass object. + /// Class Sample_QualifierData + /// Overloads Public Shared Function Main(args() As String) As Integer + /// Dim diskClass As New ManagementClass("win32_logicaldisk") + /// diskClass.Options.UseAmendedQualifiers = True + /// Dim diskQualifier As QualifierData = diskClass.Qualifiers("Description") + /// Console.WriteLine(diskQualifier.Name + " = " + diskQualifier.Value) + /// Return 0 + /// End Function + /// End Class + /// + /// + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + public class QualifierData + { + private ManagementBaseObject parent; //need access to IWbemClassObject pointer to be able to refresh qualifiers + private string propertyOrMethodName; //remains null for object qualifiers + private string qualifierName; + private QualifierType qualifierType; + private Object qualifierValue; + private int qualifierFlavor; + private IWbemQualifierSetFreeThreaded qualifierSet; + + internal QualifierData(ManagementBaseObject parent, string propName, string qualName, QualifierType type) + { + this.parent = parent; + this.propertyOrMethodName = propName; + this.qualifierName = qualName; + this.qualifierType = type; + RefreshQualifierInfo(); + } + + //This private function is used to refresh the information from the Wmi object before returning the requested data + private void RefreshQualifierInfo() + { + int status = (int)ManagementStatus.Failed; + + qualifierSet = null; + switch (qualifierType) { + case QualifierType.ObjectQualifier : + status = parent.wbemObject.GetQualifierSet_(out qualifierSet); + break; + case QualifierType.PropertyQualifier : + status = parent.wbemObject.GetPropertyQualifierSet_(propertyOrMethodName, out qualifierSet); + break; + case QualifierType.MethodQualifier : + status = parent.wbemObject.GetMethodQualifierSet_(propertyOrMethodName, out qualifierSet); + break; + default : + throw new ManagementException(ManagementStatus.Unexpected, null, null); //is this the best fit error ?? + } + + if ((status & 0x80000000) == 0) //success + { + qualifierValue = null; //Make sure it's null so that we don't leak ! + if (qualifierSet != null) + status = qualifierSet.Get_(qualifierName, 0, ref qualifierValue, ref qualifierFlavor); + } + + if ((status & 0xfffff000) == 0x80041000) //WMI error + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else if ((status & 0x80000000) != 0) //any failure + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + + private static object MapQualValueToWmiValue(object qualVal) + { + object wmiValue = System.DBNull.Value; + + if (null != qualVal) + { + if (qualVal is Array) + { + if ((qualVal is Int32[]) || (qualVal is Double[]) || (qualVal is String[]) || (qualVal is Boolean[])) + wmiValue = qualVal; + else + { + Array valArray = (Array)qualVal; + int length = valArray.Length; + Type elementType = (length > 0 ? valArray.GetValue(0).GetType() : null); + + if (elementType == typeof(Int32)) + { + wmiValue = new Int32 [length]; + for (int i = 0; i < length; i++) + ((Int32[])(wmiValue))[i] = Convert.ToInt32(valArray.GetValue(i),(IFormatProvider)CultureInfo.InvariantCulture.GetFormat(typeof(System.Int32))); + } + else if (elementType == typeof(Double)) + { + wmiValue = new Double [length]; + for (int i = 0; i < length; i++) + ((Double[])(wmiValue))[i] = Convert.ToDouble(valArray.GetValue(i),(IFormatProvider)CultureInfo.InvariantCulture.GetFormat(typeof(System.Double))); + } + else if (elementType == typeof(String)) + { + wmiValue = new String [length]; + for (int i = 0; i < length; i++) + ((String[])(wmiValue))[i] = (valArray.GetValue(i)).ToString(); + } + else if (elementType == typeof(Boolean)) + { + wmiValue = new Boolean [length]; + for (int i = 0; i < length; i++) + ((Boolean[])(wmiValue))[i] = Convert.ToBoolean(valArray.GetValue(i),(IFormatProvider)CultureInfo.InvariantCulture.GetFormat(typeof(System.Boolean))); + } + else + wmiValue = valArray; //should this throw ? + } + } + else + wmiValue = qualVal; + } + + return wmiValue; + } + + + /// + /// Represents the name of the qualifier. + /// + /// + /// The name of the qualifier. + /// + public string Name + { + get { return qualifierName != null ? qualifierName : ""; } + } + + /// + /// Gets or sets the value of the qualifier. + /// + /// + /// The value of the qualifier. + /// + /// + /// Qualifiers can only be of the following subset of CIM + /// types: , , + /// , , , + /// , , , + /// . + /// + /// + public Object Value + { + get { + RefreshQualifierInfo(); + return ValueTypeSafety.GetSafeObject(qualifierValue); + } + set { + int status = (int)ManagementStatus.NoError; + + RefreshQualifierInfo(); + object newValue = MapQualValueToWmiValue(value); + + status = qualifierSet.Put_(qualifierName, ref newValue, + qualifierFlavor & ~(int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_MASK_ORIGIN); + + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else if ((status & 0x80000000) != 0) + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + /// + /// Gets or sets a value indicating whether the qualifier is amended. + /// + /// + /// if this qualifier is amended; + /// otherwise, . + /// + /// + /// Amended qualifiers are + /// qualifiers whose value can be localized through WMI. Localized qualifiers + /// reside in separate namespaces in WMI and can be merged into the basic class + /// definition when retrieved. + /// + public bool IsAmended + { + get { + RefreshQualifierInfo(); + return ((int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_MASK_AMENDED == + (qualifierFlavor & (int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_AMENDED)); + } + set + { + int status = (int)ManagementStatus.NoError; + + RefreshQualifierInfo (); + // Mask out origin bits + int flavor = qualifierFlavor & ~(int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_MASK_ORIGIN; + + if (value) + flavor |= (int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_AMENDED; + else + flavor &= ~(int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_AMENDED; + + status = qualifierSet.Put_(qualifierName, ref qualifierValue, flavor); + + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else if ((status & 0x80000000) != 0) + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + /// + /// Gets or sets a value indicating whether the qualifier has been defined locally on + /// this class or has been propagated from a base class. + /// + /// + /// if the qualifier has been defined + /// locally on this class; otherwise, . + /// + public bool IsLocal + { + get { + RefreshQualifierInfo(); + return ((int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_ORIGIN_LOCAL == + (qualifierFlavor & (int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_MASK_ORIGIN)); + } + } + + /// + /// Gets or sets a value indicating whether the qualifier should be propagated to instances of the + /// class. + /// + /// + /// if this qualifier should be + /// propagated to instances of the class; otherwise, . + /// + public bool PropagatesToInstance + { + get { + RefreshQualifierInfo(); + return ((int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_FLAG_PROPAGATE_TO_INSTANCE == + (qualifierFlavor & (int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_FLAG_PROPAGATE_TO_INSTANCE)); + } + set { + int status = (int)ManagementStatus.NoError; + + RefreshQualifierInfo (); + // Mask out origin bits + int flavor = qualifierFlavor & ~(int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_MASK_ORIGIN; + + if (value) + flavor |= (int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_FLAG_PROPAGATE_TO_INSTANCE; + else + flavor &= ~(int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_FLAG_PROPAGATE_TO_INSTANCE; + + status = qualifierSet.Put_(qualifierName, ref qualifierValue, flavor); + + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else if ((status & 0x80000000) != 0) + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + /// + /// Gets or sets a value indicating whether the qualifier should be propagated to + /// subclasses of the class. + /// + /// + /// if the qualifier should be + /// propagated to subclasses of this class; otherwise, . + /// + public bool PropagatesToSubclass + { + get { + RefreshQualifierInfo(); + return ((int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_FLAG_PROPAGATE_TO_DERIVED_CLASS == + (qualifierFlavor & (int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_FLAG_PROPAGATE_TO_DERIVED_CLASS)); + } + set { + int status = (int)ManagementStatus.NoError; + + RefreshQualifierInfo (); + // Mask out origin bits + int flavor = qualifierFlavor & ~(int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_MASK_ORIGIN; + + if (value) + flavor |= (int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_FLAG_PROPAGATE_TO_DERIVED_CLASS; + else + flavor &= ~(int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_FLAG_PROPAGATE_TO_DERIVED_CLASS; + + status = qualifierSet.Put_(qualifierName, ref qualifierValue, flavor); + + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else if ((status & 0x80000000) != 0) + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + /// + /// Gets or sets a value indicating whether the value of the qualifier can be + /// overridden when propagated. + /// + /// + /// if the value of the qualifier + /// can be overridden when propagated; otherwise, . + /// + public bool IsOverridable + { + get { + RefreshQualifierInfo(); + return ((int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_OVERRIDABLE == + (qualifierFlavor & (int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_MASK_PERMISSIONS)); + } + set { + int status = (int)ManagementStatus.NoError; + + RefreshQualifierInfo (); + // Mask out origin bits + int flavor = qualifierFlavor & ~(int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_MASK_ORIGIN; + + if (value) + flavor &= ~(int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_NOT_OVERRIDABLE; + else + flavor |= (int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_NOT_OVERRIDABLE; + + status = qualifierSet.Put_(qualifierName, ref qualifierValue, flavor); + + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else if ((status & 0x80000000) != 0) + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + }//QualifierData +} diff --git a/external/corefx/src/System.Management/src/System/Management/QualifierSet.cs b/external/corefx/src/System.Management/src/System/Management/QualifierSet.cs new file mode 100644 index 0000000000..71dbb7616f --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/QualifierSet.cs @@ -0,0 +1,497 @@ +// 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.Collections; +using System.Runtime.InteropServices; + +namespace System.Management +{ + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + /// + /// Represents a collection of objects. + /// + /// + /// using System; + /// using System.Management; + /// + /// // This sample demonstrates how to list all qualifiers including amended + /// // qualifiers of a ManagementClass object. + /// class Sample_QualifierDataCollection + /// { + /// public static int Main(string[] args) { + /// ManagementClass diskClass = new ManagementClass("Win32_LogicalDisk"); + /// diskClass.Options.UseAmendedQualifiers = true; + /// QualifierDataCollection qualifierCollection = diskClass.Qualifiers; + /// foreach (QualifierData q in qualifierCollection) { + /// Console.WriteLine(q.Name + " = " + q.Value); + /// } + /// return 0; + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// ' This sample demonstrates how to list all qualifiers including amended + /// ' qualifiers of a ManagementClass object. + /// Class Sample_QualifierDataCollection + /// Overloads Public Shared Function Main(args() As String) As Integer + /// Dim diskClass As New ManagementClass("Win32_LogicalDisk") + /// diskClass.Options.UseAmendedQualifiers = true + /// Dim qualifierCollection As QualifierDataCollection = diskClass.Qualifiers + /// Dim q As QualifierData + /// For Each q In qualifierCollection + /// Console.WriteLine(q.Name & " = " & q.Value) + /// Next q + /// Return 0 + /// End Function + /// End Class + /// + /// + //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC// + public class QualifierDataCollection : ICollection, IEnumerable + { + private ManagementBaseObject parent; + private string propertyOrMethodName; + private QualifierType qualifierSetType; + + internal QualifierDataCollection(ManagementBaseObject parent) : base() + { + this.parent = parent; + this.qualifierSetType = QualifierType.ObjectQualifier; + this.propertyOrMethodName = null; + } + + internal QualifierDataCollection(ManagementBaseObject parent, string propertyOrMethodName, QualifierType type) : base() + { + this.parent = parent; + this.propertyOrMethodName = propertyOrMethodName; + this.qualifierSetType = type; + } + + /// + /// Return the qualifier set associated with its type + /// Overload with use of private data member, qualifierType + /// + private IWbemQualifierSetFreeThreaded GetTypeQualifierSet() + { + return GetTypeQualifierSet(qualifierSetType); + } + + /// + /// Return the qualifier set associated with its type + /// + private IWbemQualifierSetFreeThreaded GetTypeQualifierSet(QualifierType qualifierSetType) + { + IWbemQualifierSetFreeThreaded qualifierSet = null; + int status = (int)ManagementStatus.NoError; + + switch (qualifierSetType) + { + case QualifierType.ObjectQualifier : + status = parent.wbemObject.GetQualifierSet_(out qualifierSet); + break; + case QualifierType.PropertyQualifier : + status = parent.wbemObject.GetPropertyQualifierSet_(propertyOrMethodName, out qualifierSet); + break; + case QualifierType.MethodQualifier : + status = parent.wbemObject.GetMethodQualifierSet_(propertyOrMethodName, out qualifierSet); + break; + default : + throw new ManagementException(ManagementStatus.Unexpected, null, null); // Is this the best fit error ?? + } + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + return qualifierSet; + } + + // + //ICollection + // + + /// + /// Gets or sets the number of objects in the . + /// + /// + /// The number of objects in the collection. + /// + public int Count + { + get { + string[] qualifierNames = null; + IWbemQualifierSetFreeThreaded quals; + try + { + quals = GetTypeQualifierSet(); + } + catch(ManagementException e) + { + // If we ask for the number of qualifiers on a system property, we return '0' + if(qualifierSetType == QualifierType.PropertyQualifier && e.ErrorCode == ManagementStatus.SystemProperty) + return 0; + else + throw; + } + int status = quals.GetNames_(0, out qualifierNames); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + return qualifierNames.Length; + } + } + + /// + /// Gets or sets a value indicating whether the object is synchronized. + /// + /// + /// if the object is synchronized; + /// otherwise, . + /// + public bool IsSynchronized { get { return false; } + } + + /// + /// Gets or sets the object to be used for synchronization. + /// + /// + /// The object to be used for synchronization. + /// + public object SyncRoot { get { return this; } + } + + /// + /// Copies the into an array. + /// + /// + /// Copies the into an array. + /// + /// The array to which to copy the . + /// The index from which to start copying. + public void CopyTo(Array array, int index) + { + if (null == array) + throw new ArgumentNullException("array"); + + if ((index < array.GetLowerBound(0)) || (index > array.GetUpperBound(0))) + throw new ArgumentOutOfRangeException("index"); + + // Get the names of the qualifiers + string[] qualifierNames = null; + IWbemQualifierSetFreeThreaded quals; + try + { + quals = GetTypeQualifierSet(); + } + catch(ManagementException e) + { + // There are NO qualifiers on system properties, so we just return + if(qualifierSetType == QualifierType.PropertyQualifier && e.ErrorCode == ManagementStatus.SystemProperty) + return; + else + throw; + } + int status = quals.GetNames_(0, out qualifierNames); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + + if ((index + qualifierNames.Length) > array.Length) + throw new ArgumentException(null, "index"); + + foreach (string qualifierName in qualifierNames) + array.SetValue(new QualifierData(parent, propertyOrMethodName, qualifierName, qualifierSetType), index++); + + return; + } + + /// + /// Copies the into a specialized + /// + /// array. + /// + /// The specialized array of objects + /// to which to copy the . + /// The index from which to start copying. + public void CopyTo(QualifierData[] qualifierArray, int index) + { + CopyTo((Array)qualifierArray, index); + } + + // + // IEnumerable + // + IEnumerator IEnumerable.GetEnumerator() + { + return (IEnumerator)(new QualifierDataEnumerator(parent, propertyOrMethodName, qualifierSetType)); + } + + /// + /// Returns an enumerator for the . This method is strongly typed. + /// + /// + /// An that can be used to iterate through the + /// collection. + /// + public QualifierDataEnumerator GetEnumerator() + { + return new QualifierDataEnumerator(parent, propertyOrMethodName, qualifierSetType); + } + + /// + /// Represents the enumerator for + /// objects in the . + /// + /// + /// using System; + /// using System.Management; + /// + /// // This sample demonstrates how to enumerate qualifiers of a ManagementClass + /// // using QualifierDataEnumerator object. + /// class Sample_QualifierDataEnumerator + /// { + /// public static int Main(string[] args) { + /// ManagementClass diskClass = new ManagementClass("Win32_LogicalDisk"); + /// diskClass.Options.UseAmendedQualifiers = true; + /// QualifierDataCollection diskQualifier = diskClass.Qualifiers; + /// QualifierDataCollection.QualifierDataEnumerator + /// qualifierEnumerator = diskQualifier.GetEnumerator(); + /// while(qualifierEnumerator.MoveNext()) { + /// Console.WriteLine(qualifierEnumerator.Current.Name + " = " + + /// qualifierEnumerator.Current.Value); + /// } + /// return 0; + /// } + /// } + /// + /// Imports System + /// Imports System.Management + /// + /// ' This sample demonstrates how to enumerate qualifiers of a ManagementClass + /// ' using QualifierDataEnumerator object. + /// Class Sample_QualifierDataEnumerator + /// Overloads Public Shared Function Main(args() As String) As Integer + /// Dim diskClass As New ManagementClass("win32_logicaldisk") + /// diskClass.Options.UseAmendedQualifiers = True + /// Dim diskQualifier As QualifierDataCollection = diskClass.Qualifiers + /// Dim qualifierEnumerator As _ + /// QualifierDataCollection.QualifierDataEnumerator = _ + /// diskQualifier.GetEnumerator() + /// While qualifierEnumerator.MoveNext() + /// Console.WriteLine(qualifierEnumerator.Current.Name & _ + /// " = " & qualifierEnumerator.Current.Value) + /// End While + /// Return 0 + /// End Function + /// End Class + /// + /// + public class QualifierDataEnumerator : IEnumerator + { + private ManagementBaseObject parent; + private string propertyOrMethodName; + private QualifierType qualifierType; + private string[] qualifierNames; + private int index = -1; + + //Internal constructor + internal QualifierDataEnumerator(ManagementBaseObject parent, string propertyOrMethodName, + QualifierType qualifierType) + { + this.parent = parent; + this.propertyOrMethodName = propertyOrMethodName; + this.qualifierType = qualifierType; + this.qualifierNames = null; + + IWbemQualifierSetFreeThreaded qualifierSet = null; + int status = (int)ManagementStatus.NoError; + + switch (qualifierType) + { + case QualifierType.ObjectQualifier : + status = parent.wbemObject.GetQualifierSet_(out qualifierSet); + break; + case QualifierType.PropertyQualifier : + status = parent.wbemObject.GetPropertyQualifierSet_(propertyOrMethodName, out qualifierSet); + break; + case QualifierType.MethodQualifier : + status = parent.wbemObject.GetMethodQualifierSet_(propertyOrMethodName, out qualifierSet); + break; + default : + throw new ManagementException(ManagementStatus.Unexpected, null, null); // Is this the best fit error ?? + } + + // If we got an error code back, assume there are NO qualifiers for this object/property/method + if(status < 0) + { + qualifierNames = new String[]{}; + } + else + { + status = qualifierSet.GetNames_(0, out qualifierNames); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + } + + //Standard "Current" variant + /// + object IEnumerator.Current { get { return (object)this.Current; } } + + /// + /// Gets or sets the current in the enumeration. + /// + /// + /// The current element in the collection. + /// + public QualifierData Current + { + get { + if ((index == -1) || (index == qualifierNames.Length)) + throw new InvalidOperationException(); + else + return new QualifierData(parent, propertyOrMethodName, + qualifierNames[index], qualifierType); + } + } + + /// + /// Moves to the next element in the enumeration. + /// + /// + /// if the enumerator was successfully advanced to the next + /// element; if the enumerator has passed the end of the + /// collection. + /// + public bool MoveNext() + { + if (index == qualifierNames.Length) //passed the end of the array + return false; //don't advance the index any more + + index++; + return (index == qualifierNames.Length) ? false : true; + } + + /// + /// Resets the enumerator to the beginning of the enumeration. + /// + public void Reset() + { + index = -1; + } + + }//QualifierDataEnumerator + + + // + //Methods + // + + /// + /// Gets the specified from the . + /// + /// The name of the to access in the . + /// + /// A , based on the name specified. + /// + public virtual QualifierData this[string qualifierName] + { + get { + if (null == qualifierName) + throw new ArgumentNullException("qualifierName"); + + return new QualifierData(parent, propertyOrMethodName, qualifierName, qualifierSetType); + } + } + + /// + /// Removes a from the by name. + /// + /// The name of the to remove. + public virtual void Remove(string qualifierName) + { + int status = GetTypeQualifierSet().Delete_(qualifierName); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + /// + /// Adds a to the . + /// + /// + /// Adds a to the . This overload specifies the qualifier name and value. + /// + /// The name of the to be added to the . + /// The value for the new qualifier. + public virtual void Add(string qualifierName, object qualifierValue) + { + Add(qualifierName, qualifierValue, false, false, false, true); + } + + + + /// + /// Adds a to the . This overload + /// specifies all property values for a object. + /// + /// The qualifier name. + /// The qualifier value. + /// to specify that this qualifier is amended (flavor); otherwise, . + /// to propagate this qualifier to instances; otherwise, . + /// to propagate this qualifier to subclasses; otherwise, . + /// to specify that this qualifier's value is overridable in instances of subclasses; otherwise, . + public virtual void Add(string qualifierName, object qualifierValue, bool isAmended, bool propagatesToInstance, bool propagatesToSubclass, bool isOverridable) + { + + //Build the flavors bitmask and call the internal Add that takes a bitmask + int qualFlavor = 0; + if (isAmended) qualFlavor = (qualFlavor | (int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_AMENDED); + if (propagatesToInstance) qualFlavor = (qualFlavor | (int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_FLAG_PROPAGATE_TO_INSTANCE); + if (propagatesToSubclass) qualFlavor = (qualFlavor | (int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_FLAG_PROPAGATE_TO_DERIVED_CLASS); + + // Note we use the NOT condition here since WBEM_FLAVOR_OVERRIDABLE == 0 + if (!isOverridable) qualFlavor = (qualFlavor | (int)tag_WBEM_FLAVOR_TYPE.WBEM_FLAVOR_NOT_OVERRIDABLE); + + //Try to add the qualifier to the WMI object + int status = GetTypeQualifierSet().Put_(qualifierName, ref qualifierValue, qualFlavor); + + if (status < 0) + { + if ((status & 0xfffff000) == 0x80041000) + ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); + else + Marshal.ThrowExceptionForHR(status, WmiNetUtilsHelper.GetErrorInfo_f()); + } + } + + }//QualifierDataCollection +} diff --git a/external/corefx/src/System.Management/src/System/Management/WMIGenerator.cs.REMOVED.git-id b/external/corefx/src/System.Management/src/System/Management/WMIGenerator.cs.REMOVED.git-id new file mode 100644 index 0000000000..a419e4a1d2 --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/WMIGenerator.cs.REMOVED.git-id @@ -0,0 +1 @@ +b6f5b57f2cf8b9bcb3dde5f1544189673d9b0d5a \ No newline at end of file diff --git a/external/corefx/src/System.Management/src/System/Management/WmiEventSink.cs b/external/corefx/src/System.Management/src/System/Management/WmiEventSink.cs new file mode 100644 index 0000000000..a6706c1bd0 --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/WmiEventSink.cs @@ -0,0 +1,285 @@ +// 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.Runtime.InteropServices; + +namespace System.Management +{ + +internal class WmiEventSink : IWmiEventSource +{ + private static int s_hash = 0; + private int hash; + private ManagementOperationObserver watcher; + private object context; + private ManagementScope scope; + private object stub; // The secured IWbemObjectSink + + // Used for Put's only + internal event InternalObjectPutEventHandler InternalObjectPut; + private ManagementPath path; + private string className; + private bool isLocal; + + + static ManagementOperationObserver watcherParameter; + static object contextParameter; + static ManagementScope scopeParameter; + static string pathParameter; + static string classNameParameter; + static WmiEventSink wmiEventSinkNew; + + internal static WmiEventSink GetWmiEventSink( + ManagementOperationObserver watcher, + object context, + ManagementScope scope, + string path, + string className) + { + if(MTAHelper.IsNoContextMTA()) + return new WmiEventSink(watcher, context, scope, path, className); + + watcherParameter = watcher; + contextParameter = context; + scopeParameter = scope; + pathParameter = path; + classNameParameter = className; + + // + // Ensure we are able to trap exceptions from worker thread. + // + ThreadDispatch disp = new ThreadDispatch ( new ThreadDispatch.ThreadWorkerMethod ( HackToCreateWmiEventSink ) ) ; + disp.Start ( ) ; + + return wmiEventSinkNew; + } + + static void HackToCreateWmiEventSink() + { + wmiEventSinkNew = new WmiEventSink(watcherParameter, contextParameter, scopeParameter, pathParameter, classNameParameter); + } + + protected WmiEventSink (ManagementOperationObserver watcher, + object context, + ManagementScope scope, + string path, + string className) + { + try { + this.context = context; + this.watcher = watcher; + this.className = className; + this.isLocal = false; + + if (null != path) + { + this.path = new ManagementPath (path); + if((0==String.Compare(this.path.Server, ".", StringComparison.OrdinalIgnoreCase)) || + (0==String.Compare(this.path.Server, System.Environment.MachineName, StringComparison.OrdinalIgnoreCase))) + { + this.isLocal = true; + } + } + + if (null != scope) + { + this.scope = (ManagementScope) scope.Clone (); + if (null == path) // use scope to see if sink is local + { + if((0==String.Compare(this.scope.Path.Server, ".", StringComparison.OrdinalIgnoreCase)) || + (0==String.Compare(this.scope.Path.Server, System.Environment.MachineName, StringComparison.OrdinalIgnoreCase))) + { + this.isLocal = true; + } + } + } + + WmiNetUtilsHelper.GetDemultiplexedStub_f (this, this.isLocal, out stub); + hash = Threading.Interlocked.Increment(ref s_hash); + } catch {} + } + + public override int GetHashCode () { + return hash; + } + + public IWbemObjectSink Stub { + get { + try { + return (null != stub) ? (IWbemObjectSink) stub : null; + } catch { + return null; + } + } + } + + public virtual void Indicate (IntPtr pIWbemClassObject) + { + Marshal.AddRef(pIWbemClassObject); + IWbemClassObjectFreeThreaded obj = new IWbemClassObjectFreeThreaded(pIWbemClassObject); + try { + ObjectReadyEventArgs args = new ObjectReadyEventArgs (context, + ManagementBaseObject.GetBaseObject (obj, scope)); + watcher.FireObjectReady (args); + } catch {} + } + + public void SetStatus ( + int flags, + int hResult, + String message, + IntPtr pErrorObj) + { + IWbemClassObjectFreeThreaded errObj = null; + if(pErrorObj != IntPtr.Zero) + { + Marshal.AddRef(pErrorObj); + errObj = new IWbemClassObjectFreeThreaded(pErrorObj); + } + + try { + if (flags == (int) tag_WBEM_STATUS_TYPE.WBEM_STATUS_COMPLETE) + { + // Is this a Put? If so fire the ObjectPut event + if (null != path) + { + if (null == className) + path.RelativePath = message; + else + path.RelativePath = className; + + // Fire the internal event (if anyone is interested) + if (null != InternalObjectPut) + { + try { + InternalObjectPutEventArgs iargs = new InternalObjectPutEventArgs (path); + InternalObjectPut (this, iargs); + } catch {} + } + + ObjectPutEventArgs args = new ObjectPutEventArgs (context, path); + watcher.FireObjectPut(args); + } + + // Fire Completed event + CompletedEventArgs args2 = null ; + if ( errObj != null ) + { + args2 = new CompletedEventArgs (context, hResult, + new ManagementBaseObject (errObj) + ); + } + else + { + args2 = new CompletedEventArgs (context, hResult, + null + ); + } + watcher.FireCompleted (args2); + + // Unhook and tidy up + watcher.RemoveSink (this); + } + else if (0 != (flags & (int) tag_WBEM_STATUS_TYPE.WBEM_STATUS_PROGRESS)) + { + // Fire Progress event + ProgressEventArgs args = new ProgressEventArgs (context, + (int) (((uint)hResult & 0xFFFF0000) >> 16), hResult & 0xFFFF, message); + + watcher.FireProgress (args); + } + } catch {} + } + + internal void Cancel () + { + try { + scope.GetIWbemServices().CancelAsyncCall_((IWbemObjectSink) stub); + } catch {} + } + + internal void ReleaseStub () + { + try { + /* + * We force a release of the stub here so as to allow + * unsecapp.exe to die as soon as possible. + */ + if (null != stub) + { + System.Runtime.InteropServices.Marshal.ReleaseComObject(stub); + stub = null; + } + } catch {} + } + +} + +// Special sink implementation for ManagementObject.Get +// Doesn't issue ObjectReady events +internal class WmiGetEventSink : WmiEventSink +{ + private ManagementObject managementObject; + + static ManagementOperationObserver watcherParameter; + static object contextParameter; + static ManagementScope scopeParameter; + static ManagementObject managementObjectParameter; + + static WmiGetEventSink wmiGetEventSinkNew; + + internal static WmiGetEventSink GetWmiGetEventSink( + ManagementOperationObserver watcher, + object context, + ManagementScope scope, + ManagementObject managementObject) + { + if(MTAHelper.IsNoContextMTA()) + return new WmiGetEventSink(watcher, context, scope, managementObject); + + watcherParameter = watcher; + contextParameter = context; + scopeParameter = scope; + managementObjectParameter = managementObject; + + // + // Ensure we are able to trap exceptions from worker thread. + // + ThreadDispatch disp = new ThreadDispatch ( new ThreadDispatch.ThreadWorkerMethod ( HackToCreateWmiGetEventSink ) ) ; + disp.Start ( ) ; + + return wmiGetEventSinkNew; + } + + static void HackToCreateWmiGetEventSink() + { + wmiGetEventSinkNew = new WmiGetEventSink(watcherParameter, contextParameter, scopeParameter, managementObjectParameter); + } + + + private WmiGetEventSink (ManagementOperationObserver watcher, + object context, + ManagementScope scope, + ManagementObject managementObject) : + base (watcher, context, scope, null, null) + { + this.managementObject = managementObject; + } + + public override void Indicate (IntPtr pIWbemClassObject) + { + Marshal.AddRef(pIWbemClassObject); + IWbemClassObjectFreeThreaded obj = new IWbemClassObjectFreeThreaded(pIWbemClassObject); + if (null != managementObject) + { + try { + managementObject.wbemObject = obj; + } catch {} + } + } +} + + + +} diff --git a/external/corefx/src/System.Management/src/System/Management/wmiutil.cs b/external/corefx/src/System.Management/src/System/Management/wmiutil.cs new file mode 100644 index 0000000000..64e1f7d48e --- /dev/null +++ b/external/corefx/src/System.Management/src/System/Management/wmiutil.cs @@ -0,0 +1,64 @@ +// 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.Runtime.InteropServices; + +namespace System.Management +{ + + [ComImport, Guid("87A5AD68-A38A-43ef-ACA9-EFE910E5D24C"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWmiEventSource + { + void Indicate(IntPtr pIWbemClassObject); + + void SetStatus( + int lFlags, + int hResult, + [MarshalAs(UnmanagedType.BStr)] string strParam , + IntPtr pObjParam + ); + } + + //Class for calling GetErrorInfo from managed code + class WbemErrorInfo + { + public static IWbemClassObjectFreeThreaded GetErrorInfo() + { + IntPtr pErrorInfo = WmiNetUtilsHelper.GetErrorInfo_f(); + if (IntPtr.Zero != pErrorInfo && new IntPtr(-1) != pErrorInfo) + { + IntPtr pIWbemClassObject; + Marshal.QueryInterface(pErrorInfo, ref IWbemClassObjectFreeThreaded.IID_IWbemClassObject, out pIWbemClassObject); + Marshal.Release(pErrorInfo); + + // The IWbemClassObjectFreeThreaded instance will own reference count on pIWbemClassObject + if(pIWbemClassObject != IntPtr.Zero) + return new IWbemClassObjectFreeThreaded(pIWbemClassObject); + } + return null; + } + } + + //RCW for IErrorInfo + [ComImport] + [Guid("1CF2B120-547D-101B-8E65-08002B2BD119")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IErrorInfo + { + Guid GetGUID(); + + [return:MarshalAs(UnmanagedType.BStr)] + string GetSource(); + + [return:MarshalAs(UnmanagedType.BStr)] + string GetDescription(); + + [return:MarshalAs(UnmanagedType.BStr)] + string GetHelpFile(); + + uint GetHelpContext(); + } + +} diff --git a/external/corefx/src/System.Management/tests/Configurations.props b/external/corefx/src/System.Management/tests/Configurations.props new file mode 100644 index 0000000000..d8cd9ec843 --- /dev/null +++ b/external/corefx/src/System.Management/tests/Configurations.props @@ -0,0 +1,8 @@ + + + + + netcoreapp-Windows_NT; + + + diff --git a/external/corefx/src/System.Management/tests/MofHelpers/MofCollection.cs b/external/corefx/src/System.Management/tests/MofHelpers/MofCollection.cs new file mode 100644 index 0000000000..1e56742151 --- /dev/null +++ b/external/corefx/src/System.Management/tests/MofHelpers/MofCollection.cs @@ -0,0 +1,18 @@ +// 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 Xunit; + +namespace System.Management.Tests +{ + [CollectionDefinition("Mof Collection")] + [OuterLoop] + public class MofCollection : ICollectionFixture + { + // This class has no code, and is never created. Its purpose is simply + // to be the place to apply [CollectionDefinition] and all the + // ICollectionFixture<> interfaces. + // See https://xunit.github.io/docs/shared-context.html#collection-fixture + } +} diff --git a/external/corefx/src/System.Management/tests/MofHelpers/MofFixture.cs b/external/corefx/src/System.Management/tests/MofHelpers/MofFixture.cs new file mode 100644 index 0000000000..739932193d --- /dev/null +++ b/external/corefx/src/System.Management/tests/MofHelpers/MofFixture.cs @@ -0,0 +1,67 @@ +// 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.Diagnostics; +using System.IO; +using System.Reflection; +using Xunit; + +namespace System.Management.Tests +{ + public class MofFixture : IDisposable + { + private Assembly _assembly = typeof(MofFixture).GetTypeInfo().Assembly; + private string _mofCompilerPath = Path.Combine(Environment.SystemDirectory, @"wbem\mofcomp.exe"); + + public MofFixture() + { + if (AdminHelpers.IsProcessElevated()) + ExtractAndCompileMof("WmiEBvt.mof"); + } + + public void Dispose() + { + if (AdminHelpers.IsProcessElevated()) + ExtractAndCompileMof("CleanUp.mof"); + } + + private void ExtractAndCompileMof(string mofResourceName) + { + string mofFilePath = null; + try + { + mofFilePath = ExtractMofFromResourcesToFile(mofResourceName); + CompileMof(mofFilePath); + } + finally + { + if (mofFilePath != null) + File.Delete(mofFilePath); + } + } + + private string ExtractMofFromResourcesToFile(string mofResourceName) + { + var mofFilePath = Path.Combine(Path.GetTempPath(), mofResourceName); + using (var mofStream = new StreamReader(_assembly.GetManifestResourceStream(mofResourceName))) + { + string mofContent = mofStream.ReadToEnd(); + File.WriteAllText(mofFilePath, mofContent); + } + + return mofFilePath; + } + + private void CompileMof(string mofFilePath) + { + var psi = new ProcessStartInfo(_mofCompilerPath, mofFilePath); + psi.UseShellExecute = false; + psi.RedirectStandardOutput = true; + Process p = Process.Start(psi); + p.WaitForExit(); + string output = p.StandardOutput.ReadToEnd(); + Assert.True(p.ExitCode == 0, $"Failed to compile mof file {mofFilePath} output: {output}"); + } + } +} diff --git a/external/corefx/src/System.Management/tests/Resources/CleanUp.mof b/external/corefx/src/System.Management/tests/Resources/CleanUp.mof new file mode 100644 index 0000000000..11611b7c66 --- /dev/null +++ b/external/corefx/src/System.Management/tests/Resources/CleanUp.mof @@ -0,0 +1,2 @@ +#pragma namespace("\\\\.\\root") +#pragma deleteInstance("__namespace.Name='WmiEBvt'", NOFAIL) \ No newline at end of file diff --git a/external/corefx/src/System.Management/tests/Resources/WmiEBvt.mof b/external/corefx/src/System.Management/tests/Resources/WmiEBvt.mof new file mode 100644 index 0000000000..91874f1964 --- /dev/null +++ b/external/corefx/src/System.Management/tests/Resources/WmiEBvt.mof @@ -0,0 +1,304 @@ +#pragma namespace("\\\\.\\root") +#pragma deleteclass("WmiEBvt", nofail) + +instance of __namespace +{ + Name = "WmiEBvt"; +}; + +#pragma namespace("\\\\.\\root\\WmiEBvt") +#pragma deleteclass("Class1", nofail) // This will delete all subclasses and instances +#pragma deleteclass("Class2", nofail) // This will delete all subclasses and instances +#pragma deleteclass("Class3", nofail) // This will delete all subclasses and instances +#pragma deleteclass("Class4", nofail) // This will delete all subclasses and instances +#pragma deleteclass("Class5", nofail) // This will delete all subclasses and instances +#pragma deleteclass("ClassA", nofail) // This will delete all subclasses and instances +#pragma deleteclass("ClassB", nofail) // This will delete all subclasses and instances +#pragma deleteclass("ClassC", nofail) // This will delete all subclasses and instances +#pragma deleteclass("ClassD", nofail) // This will delete all subclasses and instances +#pragma deleteclass("ClassE", nofail) // This will delete all subclasses and instances +#pragma deleteclass("ClassF", nofail) // This will delete all subclasses and instances +#pragma deleteclass("RefClass", nofail) // This will delete all subclasses and instances +#pragma deleteclass("AssocClass", nofail) +/////////////////////////////////////////////////////////////////////////////////// +instance of __namespace +{ + Name = "ms_409"; +}; + +/////////////////////////////////////////////////////////////////////////////////// +// Create Class1 +[Class1Q1 (1.0): ToInstance, Class1Q2 (2): ToInstance, Class1Q3 ("3"): ToInstance, Class1Q4 (true): ToInstance] +class Class1 +{ + [key] string MyKey; + sint32 MyNumber; +}; + +// Create Class2 +class Class2: Class1 +{ + sint32 Class2Number; +}; + +// Create Class3 +class Class3 +{ + [key] string Class3Key; + sint32 Class3Number; +}; + +// Create Class4 +class Class4 +{ + [key] string Class4Key; + sint32 Class4Number; +}; + +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Create instances of Class1 +[Alias ("$One1")] +instance of Class1 as $One1 +{ + MyKey="One1"; + MyNumber = 1; +}; +instance of Class1 as $One2 +{ + MyKey="One2"; + MyNumber = 2; +}; +instance of Class1 as $One3 +{ + MyKey="One3"; + MyNumber = 3; +}; + +// Create instances of Class2 +instance of Class2 as $Two1 +{ + MyKey="Two1"; + MyNumber= 1; + Class2Number = 1; +}; + +instance of Class2 as $Two2 +{ + MyKey="Two2"; + MyNumber = 2; + Class2Number = 2; +}; + +// Create instances of Class3 +instance of Class3 as $Three1 +{ + Class3Key="Three1"; + Class3Number = 1; +}; +instance of Class3 as $Three2 +{ + Class3Key="Three2"; + Class3Number = 2; +}; +instance of Class3 as $Three3 +{ + Class3Key="Three3"; + Class3Number = 3; +}; + +// Create instances of Class4 +instance of Class4 as $Four1 +{ + Class4Key="Four1"; + Class4Number = 1; +}; +instance of Class4 as $Four2 +{ + Class4Key="Four2"; + Class4Number= 2; +}; + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Create Association Class A +[Association: ToInstance] +class AssocA +{ + [key] Class1 ref Path1; + [key] Class3 ref Path2; + string Name; +}; + +// Create instances of AssocClass A +instance of AssocA +{ + Path1=$One1; + Path2=$Three1; + Name="AssocA1"; +}; + +instance of AssocA +{ + Path1=$One2; + Path2=$Three1; + Name="AssocA2"; +}; + +instance of AssocA +{ + Path1=$Two1; + Path2=$Three3; + Name="AssocA3"; +}; + +instance of AssocA +{ + Path1=$Two2; + Path2=$Three3; + Name="AssocA4"; +}; + +// Create Association Class B +[Association: ToInstance, QB1("VB1"): ToInstance] +class AssocB +{ + [key] Class3 ref Path1; + [key] Class4 ref Path2; + string Name; +}; + +instance of AssocB +{ + Path1=$Three1; + Path2=$Four1; + Name="AssocB1"; +}; + +instance of AssocB +{ + Path1=$Three2; + Path2=$Four2; + Name="AssocB2"; +}; + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// This section define class create embedded object +// +/////////////////////////////////////////////////////////////////////////////////// +// Create class to compare +// Create ClassEm +class ClassEm +{ + [key] string ClassKey; + object ClassEmbed; +}; +class EmbedClass +{ + string ClassKey; + sint32 ClassNumber; +}; +class EmbedSub : EmbedClass +{ +}; +instance of ClassEm as $EmRef +{ + ClassKey = "ClassEm1"; + ClassEmbed = + instance of EmbedSub + { + ClassKey = "1"; + ClassNumber = 1; + }; +}; + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Create instances to generate event every 0.1 seconds +// EventQuery: SELECT * FROM __TimerEvent WHERE TimerID = "MyEvent" +instance of __IntervalTimerInstruction +{ + TimerId = "MyEvent"; // inherited from __TimerInstruction + SkipIfPassed = FALSE; // also inherited + IntervalBetweenEvents = 100; +}; + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// This class is designed for test 2.1.8.8 +// + +class RefClass +{ + [key] sint32 myKey; +}; + +instance of RefClass as $RefRef +{ + myKey = 6; +}; + +[myReal64 (1.0): ToInstance, mySInt32 (2): ToInstance, myString ("ThisIsAString"): ToInstance, myBool (true): ToInstance] +class ClassF +{ + [key]sint32 mySInt32; + sint8 mySInt8; + string myString; + boolean myBool; + ClassEm myEmbedClass; + sint16 mySInt16; + sint64 mySInt64; + Real32 myReal32; + Real64 myReal64; + UInt8 myUInt8; + UInt16 myUInt16; + UInt32 myUInt32; + UInt64 myUInt64; + object myObject; + object ref myWeakRef; + RefClass ref myStrongRef; + char16 myChar16; + +}; + +instance of ClassF +{ + myString = "myInstanceString"; + myBool = true; + myEmbedClass = + instance of ClassEm + { + ClassKey = "ClassEmF"; + ClassEmbed = + instance of EmbedSub + { + ClassKey = "F"; + ClassNumber = 2; + }; + }; + mySInt8 = -8; + mySInt16 = -16; + mySInt32 = -32; + mySInt64 = -64; + myReal32 = 32.32; + myReal64 = 64.64; + myUInt8 = 8; + myUInt16 = 16; + myUInt32 = 32; + myUInt64 = 64; + myObject = + instance of EmbedSub + { + ClassKey = "F"; + ClassNumber = 2; + }; + myWeakRef = $EmRef; + myStrongRef = $RefRef; + myChar16 = 'c'; +}; +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + diff --git a/external/corefx/src/System.Management/tests/System.Management.Tests.csproj b/external/corefx/src/System.Management/tests/System.Management.Tests.csproj new file mode 100644 index 0000000000..01552f8893 --- /dev/null +++ b/external/corefx/src/System.Management/tests/System.Management.Tests.csproj @@ -0,0 +1,31 @@ + + + + + {5456707C-489E-4562-846E-B9598569F6BE} + + + + + + + + + + + + + + + + + + + WmiEBvt.mof + + + CleanUp.mof + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Management/tests/System/Management/ManagementClassTests.cs b/external/corefx/src/System.Management/tests/System/Management/ManagementClassTests.cs new file mode 100644 index 0000000000..fb477046c4 --- /dev/null +++ b/external/corefx/src/System.Management/tests/System/Management/ManagementClassTests.cs @@ -0,0 +1,135 @@ +// 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.CodeDom; +using System.IO; +using Xunit; + +namespace System.Management.Tests +{ + public class ManagementClassTests + { + [ConditionalTheory(typeof(WmiTestHelper), nameof(WmiTestHelper.IsWmiSupported))] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public void Get_CodeTypeDeclaration_For_Win32_LogicalDisk(bool includeSystemClassInClassDef, bool systemPropertyClass) + { + using (var managementClass = new ManagementClass(null, "Win32_LogicalDisk", null)) + { + CodeTypeDeclaration classDom = managementClass.GetStronglyTypedClassCode(includeSystemClassInClassDef, systemPropertyClass); + Assert.Equal(systemPropertyClass ? "ManagementSystemProperties" : "LogicalDisk", classDom.Name); + } + } + + [ConditionalTheory(typeof(WmiTestHelper), nameof(WmiTestHelper.IsWmiSupported))] + [InlineData(CodeLanguage.CSharp)] + [InlineData(CodeLanguage.VB)] + public void Get_SourceFile_For_Win32_Processor(CodeLanguage lang) + { + var tempFilePath = Path.GetTempFileName(); + var passed = false; + try + { + using (var managementClass = new ManagementClass(null, "Win32_Processor", null)) + Assert.True(managementClass.GetStronglyTypedClassCode(lang, tempFilePath, "Wmi.Test.CoreFx")); + + passed = true; + } + finally + { + if (passed && tempFilePath != null) + File.Delete(tempFilePath); + } + } + + [ConditionalTheory(typeof(WmiTestHelper), nameof(WmiTestHelper.IsWmiSupported))] + [InlineData(CodeLanguage.JScript)] + [InlineData(CodeLanguage.Mcpp)] + [InlineData(CodeLanguage.VJSharp)] + public void Throw_On_Unsupported_Languages(CodeLanguage lang) + { + // On full framework JScript is supported and no exception raised + if (lang == CodeLanguage.JScript && PlatformDetection.IsFullFramework) + return; + + var tempFilePath = Path.GetTempFileName(); + var managementClass = new ManagementClass(null, "Win32_Processor", null); + + try + { + Assert.Throws(() => managementClass.GetStronglyTypedClassCode(lang, tempFilePath, "Wmi.Test.CoreFx")); + } + finally + { + managementClass = null; + GC.Collect(2); + GC.WaitForPendingFinalizers(); + if (tempFilePath != null) + File.Delete(tempFilePath); + } + } + + [ConditionalFact(typeof(WmiTestHelper), nameof(WmiTestHelper.IsWmiSupported))] + public void ClassMembers_For_Win32_LogicalDisk() + { + var managementClass = new ManagementClass(new ManagementPath("Win32_LogicalDisk")); + + MethodDataCollection methods = managementClass.Methods; + Assert.True(methods.Count > 0); + foreach (MethodData method in methods) + Assert.False(string.IsNullOrWhiteSpace(method.Name)); + + PropertyDataCollection properties = managementClass.Properties; + Assert.True(properties.Count > 0); + foreach (PropertyData property in properties) + Assert.False(string.IsNullOrWhiteSpace(property.Name)); + + QualifierDataCollection qualifiers = managementClass.Qualifiers; + foreach (QualifierData qualifier in qualifiers) + Assert.False(string.IsNullOrWhiteSpace(qualifier.Name)); + } + + [ConditionalFact(typeof(WmiTestHelper), nameof(WmiTestHelper.IsWmiSupported))] + public void EnumerateInstances_For_Win32_LogicalDisk() + { + using (var managementClass = new ManagementClass(new ManagementPath("Win32_LogicalDisk"))) + using (ManagementObjectCollection instances = managementClass.GetInstances()) + using (ManagementObjectCollection.ManagementObjectEnumerator instancesEnumerator = instances.GetEnumerator()) + { + while (instancesEnumerator.MoveNext()) + { + ManagementObject instance = (ManagementObject)instancesEnumerator.Current; + Assert.NotNull(instance); + var clone = instance.Clone(); + Assert.NotNull(clone); + Assert.False(ReferenceEquals(instance, clone)); + } + } + } + + [ConditionalFact(typeof(WmiTestHelper), nameof(WmiTestHelper.IsElevatedAndSupportsWmi))] + public void Create_Delete_Namespace() + { + using (var rootNamespace = new ManagementClass("root:__namespace")) + using (ManagementObject newNamespace = rootNamespace.CreateInstance()) + { + const string NewNamespace = "CoreFx_Create_Delete_Namespace_Test"; + newNamespace["Name"] = NewNamespace; + newNamespace.Put(); + + ManagementObject targetNamespace = new ManagementObject($"root:__namespace.Name='{NewNamespace}'"); + Assert.Equal(NewNamespace, targetNamespace["Name"]); + + // If any of the steps below fail it is likely that the new namespace was not deleted, likely it will have to + // be deleted via a tool like wbemtest. + targetNamespace.Delete(); + ManagementException managementException = Assert.Throws(() => targetNamespace.Get()); + Assert.Equal(ManagementStatus.NotFound, managementException.ErrorCode); + } + } + } +} diff --git a/external/corefx/src/System.Management/tests/System/Management/ManagementClassTestsMofRequired.cs b/external/corefx/src/System.Management/tests/System/Management/ManagementClassTestsMofRequired.cs new file mode 100644 index 0000000000..9097d57879 --- /dev/null +++ b/external/corefx/src/System.Management/tests/System/Management/ManagementClassTestsMofRequired.cs @@ -0,0 +1,91 @@ +// 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 Xunit; + +namespace System.Management.Tests +{ + [Collection("Mof Collection")] + public class ManagementClassTestsMofRequired + { + [ConditionalFact(typeof(WmiTestHelper), nameof(WmiTestHelper.IsElevatedAndSupportsWmi))] + public void Create_Modify_Delete_Static_Class() + { + using (var newClass = new ManagementClass(WmiTestHelper.Namespace)) + { + const string NewClassName = "CoreFX_Create_Modify_Delete_Static_Class\uEE68\uD79D\u1659"; + const string PropertyName = "Key"; + const int PropertyValue = 10; + + newClass["__CLASS"] = NewClassName; + newClass.Properties.Add(PropertyName, CimType.SInt32, false); + newClass.Properties[PropertyName].Qualifiers.Add("key", true); + newClass.Put(); + + var targetClass = new ManagementClass(WmiTestHelper.Namespace, NewClassName, null); + targetClass.Get(); + + newClass[PropertyName] = PropertyValue; + newClass.Put(); + targetClass.Get(); + Assert.Equal(PropertyValue, (int)targetClass[PropertyName]); + + // If any of the steps below fail it is likely that the new class was not deleted, likely it will have to + // be deleted via a tool like wbemtest. + newClass.Delete(); + ManagementException managementException = Assert.Throws(() => targetClass.Get()); + Assert.Equal(ManagementStatus.NotFound, managementException.ErrorCode); + } + } + + [ConditionalFact(typeof(WmiTestHelper), nameof(WmiTestHelper.IsElevatedAndSupportsWmi))] + public void Create_Modify_Delete_Static_And_Instance() + { + using (var newClass = new ManagementClass(WmiTestHelper.Namespace)) + { + const string NewClassName = "CoreFX_Create_Static_Class_And_Instance"; + const string KeyPropertyName = "Key"; + const int KeyPropertyValue = 1; + const string MoviePropertyName = "Movie"; + const string OldMovieValue = "Sequel I"; + const string NewMovieValue = "Sequel II"; + + newClass["__CLASS"] = NewClassName; + newClass.Properties.Add(KeyPropertyName, CimType.SInt32, false); + newClass.Properties[KeyPropertyName].Qualifiers.Add("key", true); + newClass.Properties.Add(MoviePropertyName, CimType.String, false); + newClass.Put(); + + ManagementObject newInstance = newClass.CreateInstance(); + newInstance[KeyPropertyName] = KeyPropertyValue; + newInstance[MoviePropertyName] = OldMovieValue; + newInstance.Put(); + + var targetInstance = new ManagementObject( + WmiTestHelper.Namespace, $"{NewClassName}.{KeyPropertyName}='{KeyPropertyValue}'", null); + targetInstance.Get(); + Assert.Equal(OldMovieValue, targetInstance[MoviePropertyName].ToString()); + + newInstance[MoviePropertyName] = NewMovieValue; + newInstance.Put(); + Assert.Equal(NewMovieValue, newInstance[MoviePropertyName].ToString()); + + targetInstance.Get(); + Assert.Equal(NewMovieValue, targetInstance[MoviePropertyName].ToString()); + + // If any of the steps below fail it is likely that the new class was not deleted, likely it will have to + // be deleted via a tool like wbemtest. + newInstance.Delete(); + ManagementException managementException = Assert.Throws(() => targetInstance.Get()); + Assert.Equal(ManagementStatus.NotFound, managementException.ErrorCode); + + // If any of the steps below fail it is likely that the new class was not deleted, likely it will have to + // be deleted via a tool like wbemtest. + newClass.Delete(); + managementException = Assert.Throws(() => newClass.Get()); + Assert.Equal(ManagementStatus.NotFound, managementException.ErrorCode); + } + } + } +} diff --git a/external/corefx/src/System.Management/tests/System/Management/ManagementDateTimeConverterTests.cs b/external/corefx/src/System.Management/tests/System/Management/ManagementDateTimeConverterTests.cs new file mode 100644 index 0000000000..d2cdfffaf7 --- /dev/null +++ b/external/corefx/src/System.Management/tests/System/Management/ManagementDateTimeConverterTests.cs @@ -0,0 +1,40 @@ +// 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 Xunit; + +namespace System.Management.Tests +{ + public class ManagementDateTimeConverterTests + { + [ConditionalFact(typeof(WmiTestHelper), nameof(WmiTestHelper.IsWmiSupported))] + public void DateTime_RoundTrip() + { + var date = new DateTime(2002, 4, 8, 14, 18, 35, 978, DateTimeKind.Utc); + var dmtfDate = "20020408141835.978000+000"; + + DateTime convertedDate = ManagementDateTimeConverter.ToDateTime(dmtfDate).ToUniversalTime(); + Assert.Equal(date, convertedDate); + + // Converting System.DateTime to DMTF datetime + string convertedDmtfDate = ManagementDateTimeConverter.ToDmtfDateTime(date); + Assert.Equal(dmtfDate, convertedDmtfDate); + } + + [ConditionalFact(typeof(WmiTestHelper), nameof(WmiTestHelper.IsWmiSupported))] + public void TimeSpan_RoundTrip() + { + var timeSpan = new TimeSpan(10, 12, 25, 32, 123); + var dmtfTimeInterval = "00000010122532.123000:000"; + + // Converting DMTF timeinterval to System.TimeSpan + TimeSpan convertedTimeSpan = ManagementDateTimeConverter.ToTimeSpan(dmtfTimeInterval); + Assert.Equal(timeSpan, convertedTimeSpan); + + // Converting System.TimeSpan to DMTF time interval format + string convertedDmtfTimeInterval = ManagementDateTimeConverter.ToDmtfTimeInterval(timeSpan); + Assert.Equal(dmtfTimeInterval, convertedDmtfTimeInterval); + } + } +} diff --git a/external/corefx/src/System.Management/tests/System/Management/ManagementEventWatcherTests.cs b/external/corefx/src/System.Management/tests/System/Management/ManagementEventWatcherTests.cs new file mode 100644 index 0000000000..f4827dbeae --- /dev/null +++ b/external/corefx/src/System.Management/tests/System/Management/ManagementEventWatcherTests.cs @@ -0,0 +1,74 @@ +// 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.Threading; +using Xunit; + +namespace System.Management.Tests +{ + [Collection("Mof Collection")] + public class ManagementEventWatcherTests + { + private const string Query = "SELECT * FROM __TimerEvent WHERE TimerID = 'MyEvent'"; + + [ConditionalFact(typeof(WmiTestHelper), nameof(WmiTestHelper.IsElevatedAndSupportsWmi))] + [OuterLoop] + public void Receive_Events_Sync() + { + ManagementEventWatcher watcher = null; + try + { + // Setup: Timer event is already set up by mof file to fire every 0.1 seconds. + // Run: Subscribe __TimerEvent with the specified TimerID + watcher = new ManagementEventWatcher( + WmiTestHelper.Namespace, + Query, + new EventWatcherOptions(null, TimeSpan.FromSeconds(5), 1)); + + ManagementBaseObject evt = watcher.WaitForNextEvent(); + + Assert.True(evt["TimerID"].ToString() == "MyEvent", $"Unexpected TimerID value {evt["TimerID"]}"); + } + finally + { + if (watcher != null) + watcher.Stop(); + } + } + + [ConditionalFact(typeof(WmiTestHelper), nameof(WmiTestHelper.IsElevatedAndSupportsWmi))] + [OuterLoop] + public void Receive_Events_Async() + { + ManagementEventWatcher watcher = null; + var resetEvent = new ManualResetEvent(false); + + try + { + // Setup: Timer event is already set up by mof file to fire every 0.1 seconds. + // Run: Subscribe __TimerEvent with the specified TimerID + watcher = new ManagementEventWatcher( + WmiTestHelper.Namespace, + Query, + new EventWatcherOptions(null, TimeSpan.FromSeconds(5), 1)); + + watcher.EventArrived += (object sender, EventArrivedEventArgs args) => + { + ManagementBaseObject newEvent = args.NewEvent; + Assert.True(newEvent["TimerID"].ToString() == "MyEvent", $"Unexpected TimerID value {newEvent["TimerID"]}"); + resetEvent.Set(); + }; + + watcher.Start(); + + Assert.True(resetEvent.WaitOne(TimeSpan.FromSeconds(5), true), "Timeout waiting for receive event."); + } + finally + { + if (watcher != null) + watcher.Stop(); + } + } + } +} diff --git a/external/corefx/src/System.Management/tests/System/Management/ManagementObjectSearcherTests.cs b/external/corefx/src/System.Management/tests/System/Management/ManagementObjectSearcherTests.cs new file mode 100644 index 0000000000..c5486dd962 --- /dev/null +++ b/external/corefx/src/System.Management/tests/System/Management/ManagementObjectSearcherTests.cs @@ -0,0 +1,69 @@ +// 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.Linq; +using Xunit; + +namespace System.Management.Tests +{ + public class ManagementObjectSearcherTests + { + [ConditionalFact(typeof(WmiTestHelper), nameof(WmiTestHelper.IsWmiSupported))] + public void Dynamic_Instances() + { + using (var searcher = new ManagementObjectSearcher()) + { + const string targetClass = "Win32_Process"; + var selectQuery = new SelectQuery(targetClass); + searcher.Query = selectQuery; + ManagementObjectCollection instances = searcher.Get(); + Assert.True(instances.Count > 0); + foreach (ManagementObject instance in instances) + Assert.Equal(targetClass, instance.Path.ClassName); + } + } + + [ConditionalFact(typeof(WmiTestHelper), nameof(WmiTestHelper.IsWmiSupported))] + [OuterLoop] + public void Related_Instances() + { + using (var searcher = new ManagementObjectSearcher()) + { + var relatedObjectQuery = new RelatedObjectQuery($"Win32_LogicalDisk.DeviceID='{WmiTestHelper.SystemDriveId}'"); + searcher.Query = relatedObjectQuery; + ManagementObjectCollection instances = searcher.Get(); + Assert.True(instances.Count > 0); + + string[] expectedAssociatedInstanceClasses = + { + "Win32_Directory", "Win32_DiskPartition", "Win32_ComputerSystem", "Win32_QuotaSetting", "Win32_SystemAccount", "Win32_Group" + }; + + foreach (ManagementObject instance in instances) + Assert.True(expectedAssociatedInstanceClasses.Contains(instance.Path.ClassName), $"Unexpected instance of class {instance.Path.ClassName}"); + } + } + + [ConditionalFact(typeof(WmiTestHelper), nameof(WmiTestHelper.IsWmiSupported))] + [OuterLoop] + public void Relationship_Classes() + { + using (var searcher = new ManagementObjectSearcher()) + { + var relationshipQuery = new RelationshipQuery($"Win32_LogicalDisk.DeviceID='{WmiTestHelper.SystemDriveId}'"); + searcher.Query = relationshipQuery; + ManagementObjectCollection instances = searcher.Get(); + Assert.True(instances.Count > 0); + + string[] expectedReferenceClasses = + { + "Win32_LogicalDiskRootDirectory", "Win32_LogicalDiskToPartition", "Win32_SystemDevices", "Win32_VolumeQuotaSetting", "Win32_DiskQuota" + }; + + foreach (ManagementObject instance in instances) + Assert.True(expectedReferenceClasses.Contains(instance.Path.ClassName), $"Unexpected instance of class {instance.Path.ClassName}"); + } + } + } +} diff --git a/external/corefx/src/System.Management/tests/System/Management/ManagementObjectSearcherTestsMofRequired.cs b/external/corefx/src/System.Management/tests/System/Management/ManagementObjectSearcherTestsMofRequired.cs new file mode 100644 index 0000000000..f8a5f91ba8 --- /dev/null +++ b/external/corefx/src/System.Management/tests/System/Management/ManagementObjectSearcherTestsMofRequired.cs @@ -0,0 +1,64 @@ +// 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 Xunit; + +namespace System.Management.Tests +{ + [Collection("Mof Collection")] + public class ManagementObjectSearcherTestsMofRequired + { + [ConditionalFact(typeof(WmiTestHelper), nameof(WmiTestHelper.IsElevatedAndSupportsWmi))] + [OuterLoop] + public void Static_Instances() + { + using (var searcher = new ManagementObjectSearcher()) + { + const string TestClass = "Class2"; + searcher.Scope.Path.NamespacePath = WmiTestHelper.Namespace; + var selectQuery = new SelectQuery(TestClass); + searcher.Query = selectQuery; + + ManagementObjectCollection instances = searcher.Get(); + Assert.Equal(2, instances.Count); + foreach (ManagementObject instance in instances) + Assert.Equal(TestClass, instance.Path.ClassName); + } + } + + [ConditionalFact(typeof(WmiTestHelper), nameof(WmiTestHelper.IsElevatedAndSupportsWmi))] + [OuterLoop] + public void Static_Related_Instances() + { + using (var searcher = new ManagementObjectSearcher()) + { + var relatedObjectQuery = new RelatedObjectQuery("Class3.Class3Key='Three2'"); + searcher.Scope.Path.NamespacePath = WmiTestHelper.Namespace; + searcher.Query = relatedObjectQuery; + ManagementObjectCollection instances = searcher.Get(); + Assert.Equal(1, instances.Count); + + foreach (ManagementObject instance in instances) + Assert.Equal("Class4", instance.Path.ClassName); + } + } + + [ConditionalFact(typeof(WmiTestHelper), nameof(WmiTestHelper.IsElevatedAndSupportsWmi))] + [OuterLoop] + public void Static_Relationship_Classes() + { + using (var searcher = new ManagementObjectSearcher()) + { + var relationshipQuery = new RelationshipQuery("Class1.MyKey='One2'"); + searcher.Scope.Path.NamespacePath = WmiTestHelper.Namespace; + searcher.Query = relationshipQuery; + ManagementObjectCollection instances = searcher.Get(); + Assert.Equal(1, instances.Count); + + foreach (ManagementObject instance in instances) + Assert.Equal("AssocA", instance.Path.ClassName); + } + } + } +} diff --git a/external/corefx/src/System.Management/tests/System/Management/ManagementObjectTests.cs b/external/corefx/src/System.Management/tests/System/Management/ManagementObjectTests.cs new file mode 100644 index 0000000000..f513f96e1f --- /dev/null +++ b/external/corefx/src/System.Management/tests/System/Management/ManagementObjectTests.cs @@ -0,0 +1,88 @@ +// 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.Diagnostics; +using Xunit; + +namespace System.Management.Tests +{ + public class ManagementObjectTests + { + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsWindowsNanoServer))] + public void PlatformNotSupportedException_On_Nano() + { + // The underlying delegate usage can cause some cases to have the PNSE as the inner exception but there is a best effort + // to throw PNSE for such case. + Assert.Throws(() => new ManagementObject($"Win32_LogicalDisk.DeviceID=\"{WmiTestHelper.SystemDriveId}\"")); + } + + [ConditionalFact(typeof(WmiTestHelper), nameof(WmiTestHelper.IsWmiSupported))] + public void Get_Win32_LogicalDisk() + { + using (ManagementObject obj = new ManagementObject($"Win32_LogicalDisk.DeviceID=\"{WmiTestHelper.SystemDriveId}\"")) + { + obj.Get(); + Assert.True(obj.Properties.Count > 0); + Assert.True(ulong.Parse(obj["Size"].ToString()) > 0); + var classPath = obj.ClassPath.Path; + Assert.Equal($@"\\{Environment.MachineName}\root\cimv2:Win32_LogicalDisk", classPath); + + var clone = obj.Clone(); + Assert.False(ReferenceEquals(clone, obj)); + ((ManagementObject)clone).Dispose(); + } + } + + [ConditionalFact(typeof(WmiTestHelper), nameof(WmiTestHelper.IsWmiSupported))] + [OuterLoop] + public void GetRelated_For_Win32_LogicalDisk() + { + using (ManagementObject obj = new ManagementObject($"Win32_LogicalDisk.DeviceID=\"{WmiTestHelper.SystemDriveId}\"")) + using (ManagementObjectCollection relatedCollection = obj.GetRelated()) + { + Assert.True(relatedCollection.Count > 0); + foreach (ManagementObject related in relatedCollection) + Assert.False(string.IsNullOrWhiteSpace(related.ClassPath.NamespacePath)); + } + } + + [ConditionalFact(typeof(WmiTestHelper), nameof(WmiTestHelper.IsWmiSupported))] + public void Set_Property_Win32_ComputerSystem() + { + using (ManagementObject obj = new ManagementObject($"Win32_ComputerSystem.Name=\"{Environment.MachineName}\"")) + { + obj.Get(); + obj.SetPropertyValue("Workgroup", "WmiTests"); + } + } + + [ConditionalFact(typeof(WmiTestHelper), nameof(WmiTestHelper.IsWmiSupported))] + [OuterLoop] + public void Invoke_Instance_And_Static_Method_Win32_Process() + { + var processClass = new ManagementClass("Win32_Process"); + object[] methodArgs = { "notepad.exe", null, null, 0 }; + + object resultObj = processClass.InvokeMethod("Create", methodArgs); + + var resultCode = (uint)resultObj; + Assert.Equal(0u, resultCode); + + var processId = (uint)methodArgs[3]; + Assert.True(0u != processId, $"Unexpected process ID: {processId}"); + + using (Process targetProcess = Process.GetProcessById((int)processId)) + using (var process = new ManagementObject($"Win32_Process.Handle=\"{processId}\"")) + { + Assert.False(targetProcess.HasExited); + + resultObj = process.InvokeMethod("Terminate", new object[] { 0 }); + resultCode = (uint)resultObj; + Assert.Equal(0u, resultCode); + + Assert.True(targetProcess.HasExited); + } + } + } +} diff --git a/external/corefx/src/System.Management/tests/System/Management/SelectQueryTests.cs b/external/corefx/src/System.Management/tests/System/Management/SelectQueryTests.cs new file mode 100644 index 0000000000..9c0f70c9d2 --- /dev/null +++ b/external/corefx/src/System.Management/tests/System/Management/SelectQueryTests.cs @@ -0,0 +1,68 @@ +// 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.Management; +using Xunit; + +namespace System.Management.Tests +{ + public class SelectQueryTests + { + [ConditionalFact(typeof(WmiTestHelper), nameof(WmiTestHelper.IsWmiSupported))] + public void Select_Win32_LogicalDisk_ClassName() + { + var query = new SelectQuery("Win32_LogicalDisk"); + var scope = new ManagementScope($@"\\{WmiTestHelper.TargetMachine}\root\cimv2"); + scope.Connect(); + + using (var searcher = new ManagementObjectSearcher(scope, query)) + using (var collection = searcher.Get()) + { + Assert.True(collection.Count > 0); + foreach (ManagementBaseObject result in collection) + { + Assert.True(result.Properties.Count > 1); + Assert.True(!string.IsNullOrEmpty(result.Properties["DeviceID"].Value.ToString())); + } + } + } + + [ConditionalFact(typeof(WmiTestHelper), nameof(WmiTestHelper.IsWmiSupported))] + public void Select_Win32_LogicalDisk_ClassName_Condition() + { + var query = new SelectQuery("Win32_LogicalDisk", "DriveType=3"); + var scope = new ManagementScope($@"\\{WmiTestHelper.TargetMachine}\root\cimv2"); + scope.Connect(); + + using (var searcher = new ManagementObjectSearcher(scope, query)) + using (ManagementObjectCollection collection = searcher.Get()) + { + Assert.True(collection.Count > 0); + foreach (ManagementBaseObject result in collection) + { + Assert.True(!string.IsNullOrEmpty(result.GetPropertyValue("DeviceID").ToString())); + } + } + } + + [ConditionalTheory(typeof(WmiTestHelper), nameof(WmiTestHelper.IsWmiSupported))] + [MemberData(nameof(WmiTestHelper.ScopeRoots), MemberType = typeof(WmiTestHelper))] + public void Select_All_Win32_LogicalDisk_Wql(string scopeRoot) + { + var query = new SelectQuery("select * from Win32_LogicalDisk"); + var scope = new ManagementScope(scopeRoot + @"root\cimv2"); + scope.Connect(); + + using (var searcher = new ManagementObjectSearcher(scope, query)) + using (ManagementObjectCollection collection = searcher.Get()) + { + Assert.True(collection.Count > 0); + foreach (ManagementBaseObject result in collection) + { + Assert.True(!string.IsNullOrEmpty(result.GetPropertyValue("DeviceID").ToString())); + } + } + } + } +} diff --git a/external/corefx/src/System.Management/tests/WmiTestHelper.cs b/external/corefx/src/System.Management/tests/WmiTestHelper.cs new file mode 100644 index 0000000000..fd3089598a --- /dev/null +++ b/external/corefx/src/System.Management/tests/WmiTestHelper.cs @@ -0,0 +1,33 @@ +// 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.Collections.Generic; +using System.IO; + +namespace System.Management.Tests +{ + public static class WmiTestHelper + { + private static readonly bool s_isElevated = AdminHelpers.IsProcessElevated(); + private static readonly bool s_isWmiSupported = PlatformDetection.IsWindows && PlatformDetection.IsNotWindowsNanoServer && !PlatformDetection.IsUap; + private static readonly string s_systemDriveId = Path.GetPathRoot(Environment.GetEnvironmentVariable("SystemDrive")); + + // Use the environment variable below to do manual runs against remote boxes: just ensure that the credentials running the tests have + // rights to query the box (use wbemtest tool to validate). + private static readonly string s_targetMachine = Environment.GetEnvironmentVariable("WmiTestTargetMachine") ?? Environment.MachineName; + private static readonly object[][] s_scopeRoots = new[] + { + new [] { $@"\\{s_targetMachine}\" }, + new [] { @"\\.\" }, + new [] { string.Empty } + }; + + public static string Namespace => "root/WmiEBvt"; + public static string SystemDriveId => s_systemDriveId; + public static bool IsWmiSupported => s_isWmiSupported; + public static bool IsElevatedAndSupportsWmi => s_isElevated && IsWmiSupported; + public static string TargetMachine => s_targetMachine; + public static IEnumerable ScopeRoots => s_scopeRoots; + } +} diff --git a/external/corefx/src/System.Memory/Common/src/System/MutableDecimal.cs b/external/corefx/src/System.Memory/Common/src/System/MutableDecimal.cs new file mode 100644 index 0000000000..a5541a6f2a --- /dev/null +++ b/external/corefx/src/System.Memory/Common/src/System/MutableDecimal.cs @@ -0,0 +1,53 @@ +// 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.Runtime.InteropServices; + +namespace System +{ + // + // Some of the Number code ported from CoreRT used internal Decimal methods that did in-place modifications on Decimal. So as to not + // lose the original perf, we'll do the same here by using Unsafe ref casts to project Decimal's true layout onto the Decimal type. + // This looks really dangerous but the fact is that Decimal is simply the OleAut DECIMAL structure and given that it's always been possible + // to pass it to OleAut apis via blittable interop, it's not going to change. + // + + [StructLayout(LayoutKind.Sequential)] + internal struct MutableDecimal + { + // Do not reorder these: must match DECIMAL's layout exactly. + public uint Flags; + public uint High; + public uint Low; + public uint Mid; + + public bool IsNegative + { + get { return (Flags & SignMask) != 0; } + set { Flags = (Flags & ~SignMask) | (value ? SignMask : 0); } + } + + public int Scale + { + get { return (byte)(Flags >> ScaleShift); } + set { Flags = (Flags & ~ScaleMask) | ((uint)value << ScaleShift); } + } + + // Sign mask for the flags field. A value of zero in this bit indicates a + // positive Decimal value, and a value of one in this bit indicates a + // negative Decimal value. + // + // Look at OleAut's DECIMAL_NEG constant to check for negative values + // in native code. + private const uint SignMask = 0x80000000; + + // Scale mask for the flags field. This byte in the flags field contains + // the power of 10 to divide the Decimal value by. The scale byte must + // contain a value between 0 and 28 inclusive. + private const uint ScaleMask = 0x00FF0000; + + // Number of bits scale is shifted by. + private const int ScaleShift = 16; + } +} diff --git a/external/corefx/src/System.Memory/System.Memory.sln b/external/corefx/src/System.Memory/System.Memory.sln index 15d7f65e81..402cbd12a0 100644 --- a/external/corefx/src/System.Memory/System.Memory.sln +++ b/external/corefx/src/System.Memory/System.Memory.sln @@ -31,14 +31,14 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {15DB0DCC-68B4-4CFB-8BD2-F26BCCAF5A3F}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU - {15DB0DCC-68B4-4CFB-8BD2-F26BCCAF5A3F}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU - {15DB0DCC-68B4-4CFB-8BD2-F26BCCAF5A3F}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU - {15DB0DCC-68B4-4CFB-8BD2-F26BCCAF5A3F}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU - {18482C55-6B57-41E8-BBC4-383B9E2C7AA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {18482C55-6B57-41E8-BBC4-383B9E2C7AA2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {18482C55-6B57-41E8-BBC4-383B9E2C7AA2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {18482C55-6B57-41E8-BBC4-383B9E2C7AA2}.Release|Any CPU.Build.0 = Release|Any CPU + {15DB0DCC-68B4-4CFB-8BD2-F26BCCAF5A3F}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {15DB0DCC-68B4-4CFB-8BD2-F26BCCAF5A3F}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {15DB0DCC-68B4-4CFB-8BD2-F26BCCAF5A3F}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {15DB0DCC-68B4-4CFB-8BD2-F26BCCAF5A3F}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {18482C55-6B57-41E8-BBC4-383B9E2C7AA2}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {18482C55-6B57-41E8-BBC4-383B9E2C7AA2}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {18482C55-6B57-41E8-BBC4-383B9E2C7AA2}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {18482C55-6B57-41E8-BBC4-383B9E2C7AA2}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU {4BBC8F69-D03E-4432-AA8A-D458FA5B235A}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU {4BBC8F69-D03E-4432-AA8A-D458FA5B235A}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU {4BBC8F69-D03E-4432-AA8A-D458FA5B235A}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU diff --git a/external/corefx/src/System.Memory/ref/System.Memory.cs b/external/corefx/src/System.Memory/ref/System.Memory.cs index e3a12d5c2e..529d181270 100644 --- a/external/corefx/src/System.Memory/ref/System.Memory.cs +++ b/external/corefx/src/System.Memory/ref/System.Memory.cs @@ -7,273 +7,410 @@ namespace System { - public readonly ref struct ReadOnlySpan + public static partial class MemoryExtensions { - public static ReadOnlySpan Empty { get { throw null; } } - public ReadOnlySpan(T[] array) { throw null;} - public ReadOnlySpan(T[] array, int start, int length) { throw null;} - public unsafe ReadOnlySpan(void* pointer, int length) { throw null;} - public bool IsEmpty { get { throw null; } } - public T this[int index] { get { throw null; }} - public int Length { get { throw null; } } - public void CopyTo(Span destination) { } - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] - public static ReadOnlySpan DangerousCreate(object obj, ref T objectData, int length) { throw null; } - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] - public ref T DangerousGetPinnableReference() { throw null; } -#pragma warning disable 0809 //warning CS0809: Obsolete member 'Span.Equals(object)' overrides non-obsolete member 'object.Equals(object)' - [System.ObsoleteAttribute("Equals() on ReadOnlySpan will always throw an exception. Use == instead.")] - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] - public override bool Equals(object obj) { throw null; } - [System.ObsoleteAttribute("GetHashCode() on ReadOnlySpan will always throw an exception.")] - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] - public override int GetHashCode() { throw null; } -#pragma warning restore 0809 - public static bool operator ==(ReadOnlySpan left, ReadOnlySpan right) { throw null; } - public static implicit operator ReadOnlySpan (T[] array) { throw null; } - public static implicit operator ReadOnlySpan (ArraySegment arraySegment) { throw null; } - public static bool operator !=(ReadOnlySpan left, ReadOnlySpan right) { throw null; } - public ReadOnlySpan Slice(int start) { throw null; } - public ReadOnlySpan Slice(int start, int length) { throw null; } - public T[] ToArray() { throw null; } - public bool TryCopyTo(Span destination) { throw null; } + public static System.ReadOnlySpan AsBytes(this System.ReadOnlySpan source) where T : struct { throw null; } + public static System.Span AsBytes(this System.Span source) where T : struct { throw null; } + public static System.ReadOnlyMemory AsReadOnlyMemory(this string text) { throw null; } + public static System.ReadOnlyMemory AsReadOnlyMemory(this string text, int start) { throw null; } + public static System.ReadOnlyMemory AsReadOnlyMemory(this string text, int start, int length) { throw null; } + public static System.ReadOnlyMemory AsReadOnlyMemory(this System.Memory memory) { throw null; } + public static System.ReadOnlySpan AsReadOnlySpan(this string text) { throw null; } + public static System.ReadOnlySpan AsReadOnlySpan(this string text, int start) { throw null; } + public static System.ReadOnlySpan AsReadOnlySpan(this string text, int start, int length) { throw null; } + public static System.ReadOnlySpan AsReadOnlySpan(this System.ArraySegment arraySegment) { throw null; } + public static System.ReadOnlySpan AsReadOnlySpan(this System.Span span) { throw null; } + public static System.ReadOnlySpan AsReadOnlySpan(this T[] array) { throw null; } + public static System.Span AsSpan(this System.ArraySegment arraySegment) { throw null; } + public static System.Span AsSpan(this T[] array) { throw null; } + public static int BinarySearch(this System.ReadOnlySpan span, System.IComparable comparable) { throw null; } + public static int BinarySearch(this System.Span span, System.IComparable comparable) { throw null; } + public static int BinarySearch(this System.ReadOnlySpan span, T value, TComparer comparer) where TComparer : System.Collections.Generic.IComparer { throw null; } + public static int BinarySearch(this System.ReadOnlySpan span, TComparable comparable) where TComparable : System.IComparable { throw null; } + public static int BinarySearch(this System.Span span, T value, TComparer comparer) where TComparer : System.Collections.Generic.IComparer { throw null; } + public static int BinarySearch(this System.Span span, TComparable comparable) where TComparable : System.IComparable { throw null; } + public static void CopyTo(this T[] array, System.Memory destination) { } + public static void CopyTo(this T[] array, System.Span destination) { } + public static bool EndsWith(this System.ReadOnlySpan span, System.ReadOnlySpan value) where T : System.IEquatable { throw null; } + public static bool EndsWith(this System.Span span, System.ReadOnlySpan value) where T : System.IEquatable { throw null; } + public static int IndexOfAny(this System.ReadOnlySpan span, System.ReadOnlySpan values) where T : System.IEquatable { throw null; } + public static int IndexOfAny(this System.ReadOnlySpan span, T value0, T value1) where T : System.IEquatable { throw null; } + public static int IndexOfAny(this System.ReadOnlySpan span, T value0, T value1, T value2) where T : System.IEquatable { throw null; } + public static int IndexOfAny(this System.Span span, System.ReadOnlySpan values) where T : System.IEquatable { throw null; } + public static int IndexOfAny(this System.Span span, T value0, T value1) where T : System.IEquatable { throw null; } + public static int IndexOfAny(this System.Span span, T value0, T value1, T value2) where T : System.IEquatable { throw null; } + public static int IndexOf(this System.ReadOnlySpan span, System.ReadOnlySpan value) where T : System.IEquatable { throw null; } + public static int IndexOf(this System.ReadOnlySpan span, T value) where T : System.IEquatable { throw null; } + public static int IndexOf(this System.Span span, System.ReadOnlySpan value) where T : System.IEquatable { throw null; } + public static int IndexOf(this System.Span span, T value) where T : System.IEquatable { throw null; } + public static int LastIndexOfAny(this System.ReadOnlySpan span, System.ReadOnlySpan values) where T : System.IEquatable { throw null; } + public static int LastIndexOfAny(this System.ReadOnlySpan span, T value0, T value1) where T : System.IEquatable { throw null; } + public static int LastIndexOfAny(this System.ReadOnlySpan span, T value0, T value1, T value2) where T : System.IEquatable { throw null; } + public static int LastIndexOfAny(this System.Span span, System.ReadOnlySpan values) where T : System.IEquatable { throw null; } + public static int LastIndexOfAny(this System.Span span, T value0, T value1) where T : System.IEquatable { throw null; } + public static int LastIndexOfAny(this System.Span span, T value0, T value1, T value2) where T : System.IEquatable { throw null; } + public static int LastIndexOf(this System.ReadOnlySpan span, System.ReadOnlySpan value) where T : System.IEquatable { throw null; } + public static int LastIndexOf(this System.ReadOnlySpan span, T value) where T : System.IEquatable { throw null; } + public static int LastIndexOf(this System.Span span, System.ReadOnlySpan value) where T : System.IEquatable { throw null; } + public static int LastIndexOf(this System.Span span, T value) where T : System.IEquatable { throw null; } + public static System.ReadOnlySpan NonPortableCast(this System.ReadOnlySpan source) where TFrom : struct where TTo : struct { throw null; } + public static System.Span NonPortableCast(this System.Span source) where TFrom : struct where TTo : struct { throw null; } + public static bool Overlaps(this System.ReadOnlySpan first, System.ReadOnlySpan second) { throw null; } + public static bool Overlaps(this System.ReadOnlySpan first, System.ReadOnlySpan second, out int elementOffset) { throw null; } + public static bool Overlaps(this System.Span first, System.ReadOnlySpan second) { throw null; } + public static bool Overlaps(this System.Span first, System.ReadOnlySpan second, out int elementOffset) { throw null; } + public static void Reverse(this System.Span span) { } + public static int SequenceCompareTo(this System.ReadOnlySpan first, System.ReadOnlySpan second) where T : System.IComparable { throw null; } + public static int SequenceCompareTo(this System.Span first, System.ReadOnlySpan second) where T : System.IComparable { throw null; } + public static bool SequenceEqual(this System.ReadOnlySpan first, System.ReadOnlySpan second) where T : System.IEquatable { throw null; } + public static bool SequenceEqual(this System.Span first, System.ReadOnlySpan second) where T : System.IEquatable { throw null; } + public static bool StartsWith(this System.ReadOnlySpan span, System.ReadOnlySpan value) where T : System.IEquatable { throw null; } + public static bool StartsWith(this System.Span span, System.ReadOnlySpan value) where T : System.IEquatable { throw null; } + public static bool TryGetString(this System.ReadOnlyMemory readOnlyMemory, out string text, out int start, out int length) { throw null; } } - - public readonly ref struct Span + public readonly partial struct Memory { - public static Span Empty { get { throw null; } } - public Span(T[] array) { throw null;} - public Span(T[] array, int start, int length) { throw null;} - public unsafe Span(void* pointer, int length) { throw null;} + private readonly object _dummy; + public Memory(T[] array) { throw null; } + public Memory(T[] array, int start, int length) { throw null; } + public static System.Memory Empty { get { throw null; } } + public bool IsEmpty { get { throw null; } } + public int Length { get { throw null; } } + public System.Span Span { get { throw null; } } + public void CopyTo(System.Memory destination) { } + public bool Equals(System.Memory other) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] + public override bool Equals(object obj) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] + public override int GetHashCode() { throw null; } + public static implicit operator System.Memory (System.ArraySegment arraySegment) { throw null; } + public static implicit operator System.ReadOnlyMemory (System.Memory memory) { throw null; } + public static implicit operator System.Memory (T[] array) { throw null; } + public System.Buffers.MemoryHandle Retain(bool pin=false) { throw null; } + public System.Memory Slice(int start) { throw null; } + public System.Memory Slice(int start, int length) { throw null; } + public T[] ToArray() { throw null; } + public bool TryCopyTo(System.Memory destination) { throw null; } + public bool TryGetArray(out System.ArraySegment arraySegment) { throw null; } + } + public readonly partial struct ReadOnlyMemory + { + private readonly object _dummy; + public ReadOnlyMemory(T[] array) { throw null; } + public ReadOnlyMemory(T[] array, int start, int length) { throw null; } + public static System.ReadOnlyMemory Empty { get { throw null; } } + public bool IsEmpty { get { throw null; } } + public int Length { get { throw null; } } + public System.ReadOnlySpan Span { get { throw null; } } + public void CopyTo(System.Memory destination) { } + [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] + public override bool Equals(object obj) { throw null; } + public bool Equals(System.ReadOnlyMemory other) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] + public override int GetHashCode() { throw null; } + public static implicit operator System.ReadOnlyMemory (System.ArraySegment arraySegment) { throw null; } + public static implicit operator System.ReadOnlyMemory (T[] array) { throw null; } + public System.Buffers.MemoryHandle Retain(bool pin=false) { throw null; } + public System.ReadOnlyMemory Slice(int start) { throw null; } + public System.ReadOnlyMemory Slice(int start, int length) { throw null; } + public T[] ToArray() { throw null; } + public bool TryCopyTo(System.Memory destination) { throw null; } + } + public readonly ref partial struct ReadOnlySpan + { + private readonly object _dummy; + [System.CLSCompliantAttribute(false)] + public unsafe ReadOnlySpan(void* pointer, int length) { throw null; } + public ReadOnlySpan(T[] array) { throw null; } + public ReadOnlySpan(T[] array, int start, int length) { throw null; } + public static System.ReadOnlySpan Empty { get { throw null; } } + public bool IsEmpty { get { throw null; } } + public ref readonly T this[int index] { get { throw null; } } + public int Length { get { throw null; } } + public void CopyTo(System.Span destination) { } + [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] + public static System.ReadOnlySpan DangerousCreate(object obj, ref T objectData, int length) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] + [System.ObsoleteAttribute("Equals() on ReadOnlySpan will always throw an exception. Use == instead.")] + public override bool Equals(object obj) { throw null; } + public System.ReadOnlySpan.Enumerator GetEnumerator() { throw null; } + [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] + [System.ObsoleteAttribute("GetHashCode() on ReadOnlySpan will always throw an exception.")] + public override int GetHashCode() { throw null; } + public static bool operator ==(System.ReadOnlySpan left, System.ReadOnlySpan right) { throw null; } + public static implicit operator System.ReadOnlySpan (System.ArraySegment arraySegment) { throw null; } + public static implicit operator System.ReadOnlySpan (T[] array) { throw null; } + public static bool operator !=(System.ReadOnlySpan left, System.ReadOnlySpan right) { throw null; } + public System.ReadOnlySpan Slice(int start) { throw null; } + public System.ReadOnlySpan Slice(int start, int length) { throw null; } + public T[] ToArray() { throw null; } + public bool TryCopyTo(System.Span destination) { throw null; } + public ref partial struct Enumerator + { + private object _dummy; + public ref readonly T Current { get { throw null; } } + public bool MoveNext() { throw null; } + } + } + public readonly ref partial struct Span + { + private readonly object _dummy; + [System.CLSCompliantAttribute(false)] + public unsafe Span(void* pointer, int length) { throw null; } + public Span(T[] array) { throw null; } + public Span(T[] array, int start, int length) { throw null; } + public static System.Span Empty { get { throw null; } } public bool IsEmpty { get { throw null; } } public ref T this[int index] { get { throw null; } } public int Length { get { throw null; } } public void Clear() { } - public void Fill(T value) { } - public void CopyTo(Span destination) { } - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] - public static Span DangerousCreate(object obj, ref T objectData, int length) { throw null; } - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] - public ref T DangerousGetPinnableReference() { throw null; } -#pragma warning disable 0809 //warning CS0809: Obsolete member 'Span.Equals(object)' overrides non-obsolete member 'object.Equals(object)' + public void CopyTo(System.Span destination) { } + [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] + public static System.Span DangerousCreate(object obj, ref T objectData, int length) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] [System.ObsoleteAttribute("Equals() on Span will always throw an exception. Use == instead.")] - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public override bool Equals(object obj) { throw null; } + public void Fill(T value) { } + public System.Span.Enumerator GetEnumerator() { throw null; } + [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] [System.ObsoleteAttribute("GetHashCode() on Span will always throw an exception.")] - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public override int GetHashCode() { throw null; } -#pragma warning restore 0809 - public static bool operator ==(Span left, Span right) { throw null; } - public static implicit operator Span (T[] array) { throw null; } - public static implicit operator Span (ArraySegment arraySegment) { throw null; } - public static implicit operator ReadOnlySpan (Span span) { throw null; } - public static bool operator !=(Span left, Span right) { throw null; } - public Span Slice(int start) { throw null; } - public Span Slice(int start, int length) { throw null; } + public static bool operator ==(System.Span left, System.Span right) { throw null; } + public static implicit operator System.Span (System.ArraySegment arraySegment) { throw null; } + public static implicit operator System.ReadOnlySpan (System.Span span) { throw null; } + public static implicit operator System.Span (T[] array) { throw null; } + public static bool operator !=(System.Span left, System.Span right) { throw null; } + public System.Span Slice(int start) { throw null; } + public System.Span Slice(int start, int length) { throw null; } public T[] ToArray() { throw null; } - public bool TryCopyTo(Span destination) { throw null; } - } - - public static class SpanExtensions - { - public static int IndexOf(this Span span, T value) where T:struct, IEquatable { throw null; } - public static int IndexOf(this Span span, ReadOnlySpan value) where T : struct, IEquatable { throw null; } - public static int IndexOf(this Span span, byte value) { throw null; } - public static int IndexOf(this Span span, ReadOnlySpan value) { throw null; } - - public static int IndexOfAny(this Span span, byte value0, byte value1) { throw null; } - public static int IndexOfAny(this Span span, byte value0, byte value1, byte value2) { throw null; } - public static int IndexOfAny(this Span span, ReadOnlySpan values) { throw null; } - - public static bool SequenceEqual(this Span first, ReadOnlySpan second) where T:struct, IEquatable { throw null; } - public static bool SequenceEqual(this Span first, ReadOnlySpan second) { throw null; } - - public static bool StartsWith(this Span span, ReadOnlySpan value) where T : struct, IEquatable { throw null; } - public static bool StartsWith(this Span span, ReadOnlySpan value) { throw null; } - - public static Span AsBytes(this Span source) where T : struct { throw null; } - - public static Span NonPortableCast(this Span source) where TFrom : struct where TTo : struct { throw null; } - - public static ReadOnlySpan AsReadOnlySpan(this string text) { throw null; } - public static Span AsSpan(this T[] array) { throw null; } - public static Span AsSpan(this ArraySegment arraySegment) { throw null; } - public static ReadOnlySpan AsReadOnlySpan(this T[] array) { throw null; } - public static ReadOnlySpan AsReadOnlySpan(this ArraySegment arraySegment) { throw null; } - - public static void CopyTo(this T[] array, Span destination) { throw null; } - - public static int IndexOf(this ReadOnlySpan span, T value) where T : struct, IEquatable { throw null; } - public static int IndexOf(this ReadOnlySpan span, ReadOnlySpan value) where T : struct, IEquatable { throw null; } - public static int IndexOf(this ReadOnlySpan span, byte value) { throw null; } - public static int IndexOf(this ReadOnlySpan span, ReadOnlySpan value) { throw null; } - - public static int IndexOfAny(this ReadOnlySpan span, byte value0, byte value1) { throw null; } - public static int IndexOfAny(this ReadOnlySpan span, byte value0, byte value1, byte value2) { throw null; } - public static int IndexOfAny(this ReadOnlySpan span, ReadOnlySpan values) { throw null; } - - public static bool SequenceEqual(this ReadOnlySpan first, ReadOnlySpan second) where T : struct, IEquatable { throw null; } - public static bool SequenceEqual(this ReadOnlySpan first, ReadOnlySpan second) { throw null; } - - public static bool StartsWith(this ReadOnlySpan span, ReadOnlySpan value) where T : struct, IEquatable { throw null; } - public static bool StartsWith(this ReadOnlySpan span, ReadOnlySpan value) { throw null; } - - public static ReadOnlySpan AsBytes(this ReadOnlySpan source) where T : struct { throw null; } - - public static ReadOnlySpan NonPortableCast(this ReadOnlySpan source) where TFrom : struct where TTo : struct { throw null; } - } - - public readonly struct ReadOnlyMemory - { - public static ReadOnlyMemory Empty { get { throw null; } } - public ReadOnlyMemory(T[] array) { throw null;} - public ReadOnlyMemory(T[] array, int start, int length) { throw null;} - public bool IsEmpty { get { throw null; } } - public int Length { get { throw null; } } - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] - public override bool Equals(object obj) { throw null; } - public bool Equals(ReadOnlyMemory other) { throw null; } - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] - public override int GetHashCode() { throw null; } - public static implicit operator ReadOnlyMemory(T[] array) { throw null; } - public static implicit operator ReadOnlyMemory(ArraySegment arraySegment) { throw null; } - public ReadOnlyMemory Slice(int start) { throw null; } - public ReadOnlyMemory Slice(int start, int length) { throw null; } - public ReadOnlySpan Span { get { throw null; } } - public unsafe Buffers.MemoryHandle Retain(bool pin = false) { throw null; } - public T[] ToArray() { throw null; } - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] - public bool DangerousTryGetArray(out ArraySegment arraySegment) { throw null; } - } - - public readonly struct Memory - { - public static Memory Empty { get { throw null; } } - public Memory(T[] array) { throw null;} - public Memory(T[] array, int start, int length) { throw null;} - public bool IsEmpty { get { throw null; } } - public int Length { get { throw null; } } - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] - public override bool Equals(object obj) { throw null; } - public bool Equals(Memory other) { throw null; } - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] - public override int GetHashCode() { throw null; } - public static implicit operator Memory(T[] array) { throw null; } - public static implicit operator Memory(ArraySegment arraySegment) { throw null; } - public static implicit operator ReadOnlyMemory(Memory memory) { throw null; } - public Memory Slice(int start) { throw null; } - public Memory Slice(int start, int length) { throw null; } - public Span Span { get { throw null; } } - public unsafe Buffers.MemoryHandle Retain(bool pin = false) { throw null; } - public T[] ToArray() { throw null; } - public bool TryGetArray(out ArraySegment arraySegment) { throw null; } + public bool TryCopyTo(System.Span destination) { throw null; } + public ref partial struct Enumerator + { + private object _dummy; + public ref T Current { get { throw null; } } + public bool MoveNext() { throw null; } + } } } - namespace System.Buffers { - public unsafe struct MemoryHandle : IDisposable - { - public MemoryHandle(IRetainable owner, void* pinnedPointer = null, System.Runtime.InteropServices.GCHandle handle = default(System.Runtime.InteropServices.GCHandle)) { throw null; } - public void* PinnedPointer { get { throw null; } } - public void Dispose() { throw null; } - } - - public interface IRetainable + public partial interface IRetainable { bool Release(); void Retain(); } - - public abstract class OwnedMemory : IDisposable, IRetainable + public partial struct MemoryHandle : System.IDisposable { - public Memory Memory { get { throw null; } } + private object _dummy; + [System.CLSCompliantAttribute(false)] + public unsafe MemoryHandle(System.Buffers.IRetainable retainable, void* pointer=null, System.Runtime.InteropServices.GCHandle handle=default(System.Runtime.InteropServices.GCHandle)) { throw null; } + public bool HasPointer { get { throw null; } } + [System.CLSCompliantAttribute(false)] + public unsafe void* Pointer { get { throw null; } } + public void Dispose() { } + } + public enum OperationStatus + { + DestinationTooSmall = 1, + Done = 0, + InvalidData = 3, + NeedMoreData = 2, + } + public abstract partial class OwnedMemory : System.Buffers.IRetainable, System.IDisposable + { + protected OwnedMemory() { } public abstract bool IsDisposed { get; } protected abstract bool IsRetained { get; } public abstract int Length { get; } - public abstract Span Span { get; } - public void Dispose() { throw null; } + public System.Memory Memory { get { throw null; } } + public abstract System.Span Span { get; } + public void Dispose() { } protected abstract void Dispose(bool disposing); - public abstract MemoryHandle Pin(); + public abstract System.Buffers.MemoryHandle Pin(int offset=0); public abstract bool Release(); public abstract void Retain(); - protected internal abstract bool TryGetArray(out ArraySegment arraySegment); + protected internal abstract bool TryGetArray(out System.ArraySegment arraySegment); + } + public readonly partial struct StandardFormat : System.IEquatable + { + private readonly int _dummy; + public const byte MaxPrecision = (byte)99; + public const byte NoPrecision = (byte)255; + public StandardFormat(char symbol, byte precision=(byte)255) { throw null; } + public bool HasPrecision { get { throw null; } } + public bool IsDefault { get { throw null; } } + public byte Precision { get { throw null; } } + public char Symbol { get { throw null; } } + public bool Equals(System.Buffers.StandardFormat other) { throw null; } + public override bool Equals(object obj) { throw null; } + public override int GetHashCode() { throw null; } + public static bool operator ==(System.Buffers.StandardFormat left, System.Buffers.StandardFormat right) { throw null; } + public static implicit operator System.Buffers.StandardFormat (char symbol) { throw null; } + public static bool operator !=(System.Buffers.StandardFormat left, System.Buffers.StandardFormat right) { throw null; } + public static System.Buffers.StandardFormat Parse(System.ReadOnlySpan format) { throw null; } + public static System.Buffers.StandardFormat Parse(string format) { throw null; } + public override string ToString() { throw null; } } } - namespace System.Buffers.Binary { - public static class BinaryPrimitives + public static partial class BinaryPrimitives { - public static sbyte ReverseEndianness(sbyte value) { throw null; } + public static short ReadInt16BigEndian(System.ReadOnlySpan buffer) { throw null; } + public static short ReadInt16LittleEndian(System.ReadOnlySpan buffer) { throw null; } + public static int ReadInt32BigEndian(System.ReadOnlySpan buffer) { throw null; } + public static int ReadInt32LittleEndian(System.ReadOnlySpan buffer) { throw null; } + public static long ReadInt64BigEndian(System.ReadOnlySpan buffer) { throw null; } + public static long ReadInt64LittleEndian(System.ReadOnlySpan buffer) { throw null; } + public static T ReadMachineEndian(System.ReadOnlySpan buffer) where T : struct { throw null; } + [System.CLSCompliantAttribute(false)] + public static ushort ReadUInt16BigEndian(System.ReadOnlySpan buffer) { throw null; } + [System.CLSCompliantAttribute(false)] + public static ushort ReadUInt16LittleEndian(System.ReadOnlySpan buffer) { throw null; } + [System.CLSCompliantAttribute(false)] + public static uint ReadUInt32BigEndian(System.ReadOnlySpan buffer) { throw null; } + [System.CLSCompliantAttribute(false)] + public static uint ReadUInt32LittleEndian(System.ReadOnlySpan buffer) { throw null; } + [System.CLSCompliantAttribute(false)] + public static ulong ReadUInt64BigEndian(System.ReadOnlySpan buffer) { throw null; } + [System.CLSCompliantAttribute(false)] + public static ulong ReadUInt64LittleEndian(System.ReadOnlySpan buffer) { throw null; } public static byte ReverseEndianness(byte value) { throw null; } public static short ReverseEndianness(short value) { throw null; } - public static ushort ReverseEndianness(ushort value) { throw null; } public static int ReverseEndianness(int value) { throw null; } - public static uint ReverseEndianness(uint value) { throw null; } public static long ReverseEndianness(long value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static sbyte ReverseEndianness(sbyte value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static ushort ReverseEndianness(ushort value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static uint ReverseEndianness(uint value) { throw null; } + [System.CLSCompliantAttribute(false)] public static ulong ReverseEndianness(ulong value) { throw null; } - - public static T ReadMachineEndian(ReadOnlySpan buffer) where T : struct { throw null; } - public static bool TryReadMachineEndian(ReadOnlySpan buffer, out T value) where T : struct { throw null; } - - public static short ReadInt16LittleEndian(ReadOnlySpan buffer) { throw null; } - public static int ReadInt32LittleEndian(ReadOnlySpan buffer) { throw null; } - public static long ReadInt64LittleEndian(ReadOnlySpan buffer) { throw null; } - public static ushort ReadUInt16LittleEndian(ReadOnlySpan buffer) { throw null; } - public static uint ReadUInt32LittleEndian(ReadOnlySpan buffer) { throw null; } - public static ulong ReadUInt64LittleEndian(ReadOnlySpan buffer) { throw null; } - - public static bool TryReadInt16LittleEndian(ReadOnlySpan buffer, out short value) { throw null; } - public static bool TryReadInt32LittleEndian(ReadOnlySpan buffer, out int value) { throw null; } - public static bool TryReadInt64LittleEndian(ReadOnlySpan buffer, out long value) { throw null; } - public static bool TryReadUInt16LittleEndian(ReadOnlySpan buffer, out ushort value) { throw null; } - public static bool TryReadUInt32LittleEndian(ReadOnlySpan buffer, out uint value) { throw null; } - public static bool TryReadUInt64LittleEndian(ReadOnlySpan buffer, out ulong value) { throw null; } - - public static short ReadInt16BigEndian(ReadOnlySpan buffer) { throw null; } - public static int ReadInt32BigEndian(ReadOnlySpan buffer) { throw null; } - public static long ReadInt64BigEndian(ReadOnlySpan buffer) { throw null; } - public static ushort ReadUInt16BigEndian(ReadOnlySpan buffer) { throw null; } - public static uint ReadUInt32BigEndian(ReadOnlySpan buffer) { throw null; } - public static ulong ReadUInt64BigEndian(ReadOnlySpan buffer) { throw null; } - - public static bool TryReadInt16BigEndian(ReadOnlySpan buffer, out short value) { throw null; } - public static bool TryReadInt32BigEndian(ReadOnlySpan buffer, out int value) { throw null; } - public static bool TryReadInt64BigEndian(ReadOnlySpan buffer, out long value) { throw null; } - public static bool TryReadUInt16BigEndian(ReadOnlySpan buffer, out ushort value) { throw null; } - public static bool TryReadUInt32BigEndian(ReadOnlySpan buffer, out uint value) { throw null; } - public static bool TryReadUInt64BigEndian(ReadOnlySpan buffer, out ulong value) { throw null; } - - public static void WriteMachineEndian(Span buffer, ref T value) where T : struct { throw null; } - public static bool TryWriteMachineEndian(Span buffer, ref T value) where T : struct { throw null; } - - public static void WriteInt16LittleEndian(Span buffer, short value) { throw null; } - public static void WriteInt32LittleEndian(Span buffer, int value) { throw null; } - public static void WriteInt64LittleEndian(Span buffer, long value) { throw null; } - public static void WriteUInt16LittleEndian(Span buffer, ushort value) { throw null; } - public static void WriteUInt32LittleEndian(Span buffer, uint value) { throw null; } - public static void WriteUInt64LittleEndian(Span buffer, ulong value) { throw null; } - - public static bool TryWriteInt16LittleEndian(Span buffer, short value) { throw null; } - public static bool TryWriteInt32LittleEndian(Span buffer, int value) { throw null; } - public static bool TryWriteInt64LittleEndian(Span buffer, long value) { throw null; } - public static bool TryWriteUInt16LittleEndian(Span buffer, ushort value) { throw null; } - public static bool TryWriteUInt32LittleEndian(Span buffer, uint value) { throw null; } - public static bool TryWriteUInt64LittleEndian(Span buffer, ulong value) { throw null; } - - public static void WriteInt16BigEndian(Span buffer, short value) { throw null; } - public static void WriteInt32BigEndian(Span buffer, int value) { throw null; } - public static void WriteInt64BigEndian(Span buffer, long value) { throw null; } - public static void WriteUInt16BigEndian(Span buffer, ushort value) { throw null; } - public static void WriteUInt32BigEndian(Span buffer, uint value) { throw null; } - public static void WriteUInt64BigEndian(Span buffer, ulong value) { throw null; } - - public static bool TryWriteInt16BigEndian(Span buffer, short value) { throw null; } - public static bool TryWriteInt32BigEndian(Span buffer, int value) { throw null; } - public static bool TryWriteInt64BigEndian(Span buffer, long value) { throw null; } - public static bool TryWriteUInt16BigEndian(Span buffer, ushort value) { throw null; } - public static bool TryWriteUInt32BigEndian(Span buffer, uint value) { throw null; } - public static bool TryWriteUInt64BigEndian(Span buffer, ulong value) { throw null; } + public static bool TryReadInt16BigEndian(System.ReadOnlySpan buffer, out short value) { throw null; } + public static bool TryReadInt16LittleEndian(System.ReadOnlySpan buffer, out short value) { throw null; } + public static bool TryReadInt32BigEndian(System.ReadOnlySpan buffer, out int value) { throw null; } + public static bool TryReadInt32LittleEndian(System.ReadOnlySpan buffer, out int value) { throw null; } + public static bool TryReadInt64BigEndian(System.ReadOnlySpan buffer, out long value) { throw null; } + public static bool TryReadInt64LittleEndian(System.ReadOnlySpan buffer, out long value) { throw null; } + public static bool TryReadMachineEndian(System.ReadOnlySpan buffer, out T value) where T : struct { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryReadUInt16BigEndian(System.ReadOnlySpan buffer, out ushort value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryReadUInt16LittleEndian(System.ReadOnlySpan buffer, out ushort value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryReadUInt32BigEndian(System.ReadOnlySpan buffer, out uint value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryReadUInt32LittleEndian(System.ReadOnlySpan buffer, out uint value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryReadUInt64BigEndian(System.ReadOnlySpan buffer, out ulong value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryReadUInt64LittleEndian(System.ReadOnlySpan buffer, out ulong value) { throw null; } + public static bool TryWriteInt16BigEndian(System.Span buffer, short value) { throw null; } + public static bool TryWriteInt16LittleEndian(System.Span buffer, short value) { throw null; } + public static bool TryWriteInt32BigEndian(System.Span buffer, int value) { throw null; } + public static bool TryWriteInt32LittleEndian(System.Span buffer, int value) { throw null; } + public static bool TryWriteInt64BigEndian(System.Span buffer, long value) { throw null; } + public static bool TryWriteInt64LittleEndian(System.Span buffer, long value) { throw null; } + public static bool TryWriteMachineEndian(System.Span buffer, ref T value) where T : struct { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryWriteUInt16BigEndian(System.Span buffer, ushort value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryWriteUInt16LittleEndian(System.Span buffer, ushort value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryWriteUInt32BigEndian(System.Span buffer, uint value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryWriteUInt32LittleEndian(System.Span buffer, uint value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryWriteUInt64BigEndian(System.Span buffer, ulong value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryWriteUInt64LittleEndian(System.Span buffer, ulong value) { throw null; } + public static void WriteInt16BigEndian(System.Span buffer, short value) { } + public static void WriteInt16LittleEndian(System.Span buffer, short value) { } + public static void WriteInt32BigEndian(System.Span buffer, int value) { } + public static void WriteInt32LittleEndian(System.Span buffer, int value) { } + public static void WriteInt64BigEndian(System.Span buffer, long value) { } + public static void WriteInt64LittleEndian(System.Span buffer, long value) { } + public static void WriteMachineEndian(System.Span buffer, ref T value) where T : struct { } + [System.CLSCompliantAttribute(false)] + public static void WriteUInt16BigEndian(System.Span buffer, ushort value) { } + [System.CLSCompliantAttribute(false)] + public static void WriteUInt16LittleEndian(System.Span buffer, ushort value) { } + [System.CLSCompliantAttribute(false)] + public static void WriteUInt32BigEndian(System.Span buffer, uint value) { } + [System.CLSCompliantAttribute(false)] + public static void WriteUInt32LittleEndian(System.Span buffer, uint value) { } + [System.CLSCompliantAttribute(false)] + public static void WriteUInt64BigEndian(System.Span buffer, ulong value) { } + [System.CLSCompliantAttribute(false)] + public static void WriteUInt64LittleEndian(System.Span buffer, ulong value) { } } -} \ No newline at end of file +} +namespace System.Buffers.Text +{ + public static partial class Base64 + { + public static System.Buffers.OperationStatus DecodeFromUtf8(System.ReadOnlySpan utf8, System.Span bytes, out int consumed, out int written, bool isFinalBlock=true) { throw null; } + public static System.Buffers.OperationStatus DecodeFromUtf8InPlace(System.Span buffer, out int written) { throw null; } + public static System.Buffers.OperationStatus EncodeToUtf8(System.ReadOnlySpan bytes, System.Span utf8, out int consumed, out int written, bool isFinalBlock=true) { throw null; } + public static System.Buffers.OperationStatus EncodeToUtf8InPlace(System.Span buffer, int dataLength, out int written) { throw null; } + public static int GetMaxDecodedFromUtf8Length(int length) { throw null; } + public static int GetMaxEncodedToUtf8Length(int length) { throw null; } + } + public static partial class Utf8Formatter + { + public static bool TryFormat(bool value, System.Span buffer, out int bytesWritten, System.Buffers.StandardFormat format=default(System.Buffers.StandardFormat)) { throw null; } + public static bool TryFormat(byte value, System.Span buffer, out int bytesWritten, System.Buffers.StandardFormat format=default(System.Buffers.StandardFormat)) { throw null; } + public static bool TryFormat(System.DateTime value, System.Span buffer, out int bytesWritten, System.Buffers.StandardFormat format=default(System.Buffers.StandardFormat)) { throw null; } + public static bool TryFormat(System.DateTimeOffset value, System.Span buffer, out int bytesWritten, System.Buffers.StandardFormat format=default(System.Buffers.StandardFormat)) { throw null; } + public static bool TryFormat(decimal value, System.Span buffer, out int bytesWritten, System.Buffers.StandardFormat format=default(System.Buffers.StandardFormat)) { throw null; } + public static bool TryFormat(double value, System.Span buffer, out int bytesWritten, System.Buffers.StandardFormat format=default(System.Buffers.StandardFormat)) { throw null; } + public static bool TryFormat(System.Guid value, System.Span buffer, out int bytesWritten, System.Buffers.StandardFormat format=default(System.Buffers.StandardFormat)) { throw null; } + public static bool TryFormat(short value, System.Span buffer, out int bytesWritten, System.Buffers.StandardFormat format=default(System.Buffers.StandardFormat)) { throw null; } + public static bool TryFormat(int value, System.Span buffer, out int bytesWritten, System.Buffers.StandardFormat format=default(System.Buffers.StandardFormat)) { throw null; } + public static bool TryFormat(long value, System.Span buffer, out int bytesWritten, System.Buffers.StandardFormat format=default(System.Buffers.StandardFormat)) { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryFormat(sbyte value, System.Span buffer, out int bytesWritten, System.Buffers.StandardFormat format=default(System.Buffers.StandardFormat)) { throw null; } + public static bool TryFormat(float value, System.Span buffer, out int bytesWritten, System.Buffers.StandardFormat format=default(System.Buffers.StandardFormat)) { throw null; } + public static bool TryFormat(System.TimeSpan value, System.Span buffer, out int bytesWritten, System.Buffers.StandardFormat format=default(System.Buffers.StandardFormat)) { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryFormat(ushort value, System.Span buffer, out int bytesWritten, System.Buffers.StandardFormat format=default(System.Buffers.StandardFormat)) { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryFormat(uint value, System.Span buffer, out int bytesWritten, System.Buffers.StandardFormat format=default(System.Buffers.StandardFormat)) { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryFormat(ulong value, System.Span buffer, out int bytesWritten, System.Buffers.StandardFormat format=default(System.Buffers.StandardFormat)) { throw null; } + } + public static partial class Utf8Parser + { + public static bool TryParse(System.ReadOnlySpan text, out bool value, out int bytesConsumed, char standardFormat='\0') { throw null; } + public static bool TryParse(System.ReadOnlySpan text, out byte value, out int bytesConsumed, char standardFormat='\0') { throw null; } + public static bool TryParse(System.ReadOnlySpan text, out System.DateTime value, out int bytesConsumed, char standardFormat='\0') { throw null; } + public static bool TryParse(System.ReadOnlySpan text, out System.DateTimeOffset value, out int bytesConsumed, char standardFormat='\0') { throw null; } + public static bool TryParse(System.ReadOnlySpan text, out decimal value, out int bytesConsumed, char standardFormat='\0') { throw null; } + public static bool TryParse(System.ReadOnlySpan text, out double value, out int bytesConsumed, char standardFormat='\0') { throw null; } + public static bool TryParse(System.ReadOnlySpan text, out System.Guid value, out int bytesConsumed, char standardFormat='\0') { throw null; } + public static bool TryParse(System.ReadOnlySpan text, out short value, out int bytesConsumed, char standardFormat='\0') { throw null; } + public static bool TryParse(System.ReadOnlySpan text, out int value, out int bytesConsumed, char standardFormat='\0') { throw null; } + public static bool TryParse(System.ReadOnlySpan text, out long value, out int bytesConsumed, char standardFormat='\0') { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryParse(System.ReadOnlySpan text, out sbyte value, out int bytesConsumed, char standardFormat='\0') { throw null; } + public static bool TryParse(System.ReadOnlySpan text, out float value, out int bytesConsumed, char standardFormat='\0') { throw null; } + public static bool TryParse(System.ReadOnlySpan text, out System.TimeSpan value, out int bytesConsumed, char standardFormat='\0') { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryParse(System.ReadOnlySpan text, out ushort value, out int bytesConsumed, char standardFormat='\0') { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryParse(System.ReadOnlySpan text, out uint value, out int bytesConsumed, char standardFormat='\0') { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryParse(System.ReadOnlySpan text, out ulong value, out int bytesConsumed, char standardFormat='\0') { throw null; } + } +} +namespace System.Runtime.InteropServices +{ + public static partial class MemoryMarshal + { + public static System.Memory AsMemory(System.ReadOnlyMemory readOnlyMemory) { throw null; } + public static ref T GetReference(System.ReadOnlySpan span) { throw null; } + public static ref T GetReference(System.Span span) { throw null; } + public static bool TryGetArray(System.ReadOnlyMemory readOnlyMemory, out System.ArraySegment arraySegment) { throw null; } + } +} diff --git a/external/corefx/src/System.Memory/ref/System.Memory.csproj b/external/corefx/src/System.Memory/ref/System.Memory.csproj index 0d0db9a2fd..2acd7aa17c 100644 --- a/external/corefx/src/System.Memory/ref/System.Memory.csproj +++ b/external/corefx/src/System.Memory/ref/System.Memory.csproj @@ -3,8 +3,9 @@ true - false {E883935B-D8FD-4FC9-A189-9D9E7F7EF550} + + $(NoWarn);0809 true diff --git a/external/corefx/src/System.Memory/src/Resources/Strings.resx b/external/corefx/src/System.Memory/src/Resources/Strings.resx index 9fd5a70774..219d3c94c5 100644 --- a/external/corefx/src/System.Memory/src/Resources/Strings.resx +++ b/external/corefx/src/System.Memory/src/Resources/Strings.resx @@ -1,17 +1,17 @@  - @@ -117,9 +117,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - The array type must be exactly {0}. - Equals() on Span and ReadOnlySpan is not supported. Use operator== instead. @@ -138,4 +135,19 @@ Release all references before disposing this instance. - \ No newline at end of file + + Format specifier was invalid. + + + The 'G' format combined with a precision is not supported. + + + Characters following the format symbol must be a number of {0} or less. + + + Precision cannot be larger than {0}. + + + Overlapping spans have mismatching alignment. + + diff --git a/external/corefx/src/System.Memory/src/System.Memory.csproj b/external/corefx/src/System.Memory/src/System.Memory.csproj index f0e48299b7..0f67b7ee9c 100644 --- a/external/corefx/src/System.Memory/src/System.Memory.csproj +++ b/external/corefx/src/System.Memory/src/System.Memory.csproj @@ -4,10 +4,9 @@ {4BBC8F69-D03E-4432-AA8A-D458FA5B235A} true - false $(OutputPath)$(MSBuildProjectName).xml true - $(DefineConstants);netstandard + $(DefineConstants);netstandard;FEATURE_PORTABLE_SPAN $(DefineConstants);netcoreapp $(DefineConstants);netstandard11 @@ -22,34 +21,109 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - + + + + + Common\System\NotImplemented.cs + + @@ -59,14 +133,13 @@ - + - - + \ No newline at end of file diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Binary/Reader.cs b/external/corefx/src/System.Memory/src/System/Buffers/Binary/Reader.cs index 0dbf7edd3f..599bcf27f8 100644 --- a/external/corefx/src/System.Memory/src/System/Buffers/Binary/Reader.cs +++ b/external/corefx/src/System.Memory/src/System/Buffers/Binary/Reader.cs @@ -2,8 +2,12 @@ // 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.Runtime; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#if !netstandard +using Internal.Runtime.CompilerServices; +#endif namespace System.Buffers.Binary { @@ -11,7 +15,7 @@ namespace System.Buffers.Binary /// Reads bytes as primitives with specific endianness /// /// - /// For native formats, SpanExtensions.Read<T> should be used. + /// For native formats, MemoryExtensions.Read{T}; should be used. /// Use these helpers when you need to read specific endinanness. /// public static partial class BinaryPrimitives @@ -21,6 +25,7 @@ namespace System.Buffers.Binary /// This allows the caller to read a struct of numeric primitives and reverse each field /// rather than having to skip sbyte fields. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static sbyte ReverseEndianness(sbyte value) { @@ -62,33 +67,65 @@ namespace System.Buffers.Binary /// /// Reverses a primitive value - performs an endianness swap /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort ReverseEndianness(ushort value) { - return (ushort)((value & 0x00FFU) << 8 | (value & 0xFF00U) >> 8); + // Don't need to AND with 0xFF00 or 0x00FF since the final + // cast back to ushort will clear out all bits above [ 15 .. 00 ]. + // This is normally implemented via "movzx eax, ax" on the return. + // Alternatively, the compiler could elide the movzx instruction + // entirely if it knows the caller is only going to access "ax" + // instead of "eax" / "rax" when the function returns. + + return (ushort)((value >> 8) + (value << 8)); } /// /// Reverses a primitive value - performs an endianness swap /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint ReverseEndianness(uint value) { - value = (value << 16) | (value >> 16); - value = (value & 0x00FF00FF) << 8 | (value & 0xFF00FF00) >> 8; - return value; + // This takes advantage of the fact that the JIT can detect + // ROL32 / ROR32 patterns and output the correct intrinsic. + // + // Input: value = [ ww xx yy zz ] + // + // First line generates : [ ww xx yy zz ] + // & [ 00 FF 00 FF ] + // = [ 00 xx 00 zz ] + // ROR32(8) = [ zz 00 xx 00 ] + // + // Second line generates: [ ww xx yy zz ] + // & [ FF 00 FF 00 ] + // = [ ww 00 yy 00 ] + // ROL32(8) = [ 00 yy 00 ww ] + // + // (sum) = [ zz yy xx ww ] + // + // Testing shows that throughput increases if the AND + // is performed before the ROL / ROR. + + uint mask_xx_zz = (value & 0x00FF00FFU); + uint mask_ww_yy = (value & 0xFF00FF00U); + return ((mask_xx_zz >> 8) | (mask_xx_zz << 24)) + + ((mask_ww_yy << 8) | (mask_ww_yy >> 24)); } /// /// Reverses a primitive value - performs an endianness swap /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong ReverseEndianness(ulong value) { - value = (value << 32) | (value >> 32); - value = (value & 0x0000FFFF0000FFFF) << 16 | (value & 0xFFFF0000FFFF0000) >> 16; - value = (value & 0x00FF00FF00FF00FF) << 8 | (value & 0xFF00FF00FF00FF00) >> 8; - return value; + // Operations on 32-bit values have higher throughput than + // operations on 64-bit values, so decompose. + + return ((ulong)ReverseEndianness((uint)value) << 32) + + ReverseEndianness((uint)(value >> 32)); } /// @@ -106,14 +143,14 @@ namespace System.Buffers.Binary #else if (RuntimeHelpers.IsReferenceOrContainsReferences()) { - throw new ArgumentException(SR.Format(SR.Argument_InvalidTypeWithPointersNotSupported, typeof(T))); + ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T)); } #endif if (Unsafe.SizeOf() > buffer.Length) { - throw new ArgumentOutOfRangeException(); + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length); } - return Unsafe.ReadUnaligned(ref buffer.DangerousGetPinnableReference()); + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(buffer)); } /// @@ -132,7 +169,7 @@ namespace System.Buffers.Binary #else if (RuntimeHelpers.IsReferenceOrContainsReferences()) { - throw new ArgumentException(SR.Format(SR.Argument_InvalidTypeWithPointersNotSupported, typeof(T))); + ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T)); } #endif if (Unsafe.SizeOf() > (uint)buffer.Length) @@ -144,7 +181,7 @@ namespace System.Buffers.Binary #endif return false; } - value = Unsafe.ReadUnaligned(ref buffer.DangerousGetPinnableReference()); + value = Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(buffer)); return true; } } diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Binary/ReaderBigEndian.cs b/external/corefx/src/System.Memory/src/System/Buffers/Binary/ReaderBigEndian.cs index a98d91588c..54cef4eb1f 100644 --- a/external/corefx/src/System.Memory/src/System/Buffers/Binary/ReaderBigEndian.cs +++ b/external/corefx/src/System.Memory/src/System/Buffers/Binary/ReaderBigEndian.cs @@ -53,6 +53,7 @@ namespace System.Buffers.Binary /// /// Reads a UInt16 out of a read-only span of bytes as big endian. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort ReadUInt16BigEndian(ReadOnlySpan buffer) { @@ -67,6 +68,7 @@ namespace System.Buffers.Binary /// /// Reads a UInt32 out of a read-only span of bytes as big endian. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint ReadUInt32BigEndian(ReadOnlySpan buffer) { @@ -81,6 +83,7 @@ namespace System.Buffers.Binary /// /// Reads a UInt64 out of a read-only span of bytes as big endian. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong ReadUInt64BigEndian(ReadOnlySpan buffer) { @@ -141,6 +144,7 @@ namespace System.Buffers.Binary /// Reads a UInt16 out of a read-only span of bytes as big endian. /// If the span is too small to contain a UInt16, return false. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryReadUInt16BigEndian(ReadOnlySpan buffer, out ushort value) { @@ -156,6 +160,7 @@ namespace System.Buffers.Binary /// Reads a UInt32 out of a read-only span of bytes as big endian. /// If the span is too small to contain a UInt32, return false. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryReadUInt32BigEndian(ReadOnlySpan buffer, out uint value) { @@ -171,6 +176,7 @@ namespace System.Buffers.Binary /// Reads a UInt64 out of a read-only span of bytes as big endian. /// If the span is too small to contain a UInt64, return false. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryReadUInt64BigEndian(ReadOnlySpan buffer, out ulong value) { diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Binary/ReaderLittleEndian.cs b/external/corefx/src/System.Memory/src/System/Buffers/Binary/ReaderLittleEndian.cs index 09dd7d8801..17cebf1f27 100644 --- a/external/corefx/src/System.Memory/src/System/Buffers/Binary/ReaderLittleEndian.cs +++ b/external/corefx/src/System.Memory/src/System/Buffers/Binary/ReaderLittleEndian.cs @@ -53,6 +53,7 @@ namespace System.Buffers.Binary /// /// Reads a UInt16 out of a read-only span of bytes as little endian. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort ReadUInt16LittleEndian(ReadOnlySpan buffer) { @@ -67,6 +68,7 @@ namespace System.Buffers.Binary /// /// Reads a UInt32 out of a read-only span of bytes as little endian. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint ReadUInt32LittleEndian(ReadOnlySpan buffer) { @@ -81,6 +83,7 @@ namespace System.Buffers.Binary /// /// Reads a UInt64 out of a read-only span of bytes as little endian. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong ReadUInt64LittleEndian(ReadOnlySpan buffer) { @@ -91,7 +94,7 @@ namespace System.Buffers.Binary } return result; } - + /// /// Reads an Int16 out of a read-only span of bytes as little endian. /// If the span is too small to contain an Int16, return false. @@ -141,6 +144,7 @@ namespace System.Buffers.Binary /// Reads a UInt16 out of a read-only span of bytes as little endian. /// If the span is too small to contain a UInt16, return false. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryReadUInt16LittleEndian(ReadOnlySpan buffer, out ushort value) { @@ -156,6 +160,7 @@ namespace System.Buffers.Binary /// Reads a UInt32 out of a read-only span of bytes as little endian. /// If the span is too small to contain a UInt32, return false. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryReadUInt32LittleEndian(ReadOnlySpan buffer, out uint value) { @@ -171,6 +176,7 @@ namespace System.Buffers.Binary /// Reads a UInt64 out of a read-only span of bytes as little endian. /// If the span is too small to contain a UInt64, return false. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryReadUInt64LittleEndian(ReadOnlySpan buffer, out ulong value) { diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Binary/Writer.cs b/external/corefx/src/System.Memory/src/System/Buffers/Binary/Writer.cs index 9834986f4a..fe59623138 100644 --- a/external/corefx/src/System.Memory/src/System/Buffers/Binary/Writer.cs +++ b/external/corefx/src/System.Memory/src/System/Buffers/Binary/Writer.cs @@ -2,8 +2,12 @@ // 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.Runtime; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#if !netstandard +using Internal.Runtime.CompilerServices; +#endif namespace System.Buffers.Binary { @@ -24,14 +28,14 @@ namespace System.Buffers.Binary #else if (RuntimeHelpers.IsReferenceOrContainsReferences()) { - throw new ArgumentException(SR.Format(SR.Argument_InvalidTypeWithPointersNotSupported, typeof(T))); + ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T)); } #endif if ((uint)Unsafe.SizeOf() > (uint)buffer.Length) { - throw new ArgumentOutOfRangeException(); + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length); } - Unsafe.WriteUnaligned(ref buffer.DangerousGetPinnableReference(), value); + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(buffer), value); } /// @@ -50,14 +54,14 @@ namespace System.Buffers.Binary #else if (RuntimeHelpers.IsReferenceOrContainsReferences()) { - throw new ArgumentException(SR.Format(SR.Argument_InvalidTypeWithPointersNotSupported, typeof(T))); + ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T)); } #endif if (Unsafe.SizeOf() > (uint)buffer.Length) { return false; } - Unsafe.WriteUnaligned(ref buffer.DangerousGetPinnableReference(), value); + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(buffer), value); return true; } } diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Binary/WriterBigEndian.cs b/external/corefx/src/System.Memory/src/System/Buffers/Binary/WriterBigEndian.cs index ebfb0968d8..fa7228ba3b 100644 --- a/external/corefx/src/System.Memory/src/System/Buffers/Binary/WriterBigEndian.cs +++ b/external/corefx/src/System.Memory/src/System/Buffers/Binary/WriterBigEndian.cs @@ -50,6 +50,7 @@ namespace System.Buffers.Binary /// /// Write a UInt16 into a span of bytes as big endian. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteUInt16BigEndian(Span buffer, ushort value) { @@ -63,6 +64,7 @@ namespace System.Buffers.Binary /// /// Write a UInt32 into a span of bytes as big endian. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteUInt32BigEndian(Span buffer, uint value) { @@ -76,6 +78,7 @@ namespace System.Buffers.Binary /// /// Write a UInt64 into a span of bytes as big endian. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteUInt64BigEndian(Span buffer, ulong value) { @@ -132,6 +135,7 @@ namespace System.Buffers.Binary /// Write a UInt16 into a span of bytes as big endian. /// If the span is too small to contain the value, return false. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryWriteUInt16BigEndian(Span buffer, ushort value) { @@ -146,6 +150,7 @@ namespace System.Buffers.Binary /// Write a UInt32 into a span of bytes as big endian. /// If the span is too small to contain the value, return false. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryWriteUInt32BigEndian(Span buffer, uint value) { @@ -160,6 +165,7 @@ namespace System.Buffers.Binary /// Write a UInt64 into a span of bytes as big endian. /// If the span is too small to contain the value, return false. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryWriteUInt64BigEndian(Span buffer, ulong value) { diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Binary/WriterLittleEndian.cs b/external/corefx/src/System.Memory/src/System/Buffers/Binary/WriterLittleEndian.cs index 34c317b0f2..22bbe070eb 100644 --- a/external/corefx/src/System.Memory/src/System/Buffers/Binary/WriterLittleEndian.cs +++ b/external/corefx/src/System.Memory/src/System/Buffers/Binary/WriterLittleEndian.cs @@ -50,6 +50,7 @@ namespace System.Buffers.Binary /// /// Write a UInt16 into a span of bytes as little endian. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteUInt16LittleEndian(Span buffer, ushort value) { @@ -63,6 +64,7 @@ namespace System.Buffers.Binary /// /// Write a UInt32 into a span of bytes as little endian. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteUInt32LittleEndian(Span buffer, uint value) { @@ -76,6 +78,7 @@ namespace System.Buffers.Binary /// /// Write a UInt64 into a span of bytes as little endian. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteUInt64LittleEndian(Span buffer, ulong value) { @@ -132,6 +135,7 @@ namespace System.Buffers.Binary /// Write a UInt16 into a span of bytes as little endian. /// If the span is too small to contain the value, return false. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryWriteUInt16LittleEndian(Span buffer, ushort value) { @@ -146,6 +150,7 @@ namespace System.Buffers.Binary /// Write a UInt32 into a span of bytes as little endian. /// If the span is too small to contain the value, return false. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryWriteUInt32LittleEndian(Span buffer, uint value) { @@ -160,6 +165,7 @@ namespace System.Buffers.Binary /// Write a UInt64 into a span of bytes as little endian. /// If the span is too small to contain the value, return false. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryWriteUInt64LittleEndian(Span buffer, ulong value) { diff --git a/external/corefx/src/System.Memory/src/System/Buffers/OperationStatus.cs b/external/corefx/src/System.Memory/src/System/Buffers/OperationStatus.cs new file mode 100644 index 0000000000..e9ddcab571 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/OperationStatus.cs @@ -0,0 +1,34 @@ +// 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. + +namespace System.Buffers +{ + /// + /// This enum defines the various potential status that can be returned from Span-based operations + /// that support processing of input contained in multiple discontiguous buffers. + /// + public enum OperationStatus + { + /// + /// The entire input buffer has been processed and the operation is complete. + /// + Done, + /// + /// The input is partially processed, up to what could fit into the destination buffer. + /// The caller can enlarge the destination buffer, slice the buffers appropriately, and retry. + /// + DestinationTooSmall, + /// + /// The input is partially processed, up to the last valid chunk of the input that could be consumed. + /// The caller can stitch the remaining unprocessed input with more data, slice the buffers appropriately, and retry. + /// + NeedMoreData, + /// + /// The input contained invalid bytes which could not be processed. If the input is partially processed, + /// the destination contains the partial result. This guarantees that no additional data appended to the input + /// will make the invalid sequence valid. + /// + InvalidData, + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/StandardFormat.cs b/external/corefx/src/System.Memory/src/System/Buffers/StandardFormat.cs new file mode 100644 index 0000000000..58c6514d3c --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/StandardFormat.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. + +using System.Diagnostics.Private; + +namespace System.Buffers +{ + /// + /// Represents a standard formatting string without using an actual String. A StandardFormat consists of a character (such as 'G', 'D' or 'X') + /// and an optional precision ranging from 0..99, or the special value NoPrecision. + /// + public readonly struct StandardFormat : IEquatable + { + /// + /// Precision values for format that don't use a precision, or for when the precision is to be unspecified. + /// + public const byte NoPrecision = byte.MaxValue; + + /// + /// The maximum valid precision value. + /// + public const byte MaxPrecision = 99; + + private readonly byte _format; + private readonly byte _precision; + + /// + /// The character component of the format. + /// + public char Symbol => (char)_format; + + /// + /// The precision component of the format. Ranges from 0..9 or the special value NoPrecision. + /// + public byte Precision => _precision; + + /// + /// true if Precision is a value other than NoPrecision + /// + public bool HasPrecision => _precision != NoPrecision; + + /// + /// true if the StandardFormat == default(StandardFormat) + /// + public bool IsDefault => _format == 0 && _precision == 0; + + /// + /// Create a StandardFormat. + /// + /// A type-specific formatting character such as 'G', 'D' or 'X' + /// An optional precision ranging from 0..9 or the special value NoPrecision (the default) + public StandardFormat(char symbol, byte precision = NoPrecision) + { + if (precision != NoPrecision && precision > MaxPrecision) + ThrowHelper.ThrowArgumentOutOfRangeException_PrecisionTooLarge(); + if (symbol != (byte)symbol) + ThrowHelper.ThrowArgumentOutOfRangeException_SymbolDoesNotFit(); + + _format = (byte)symbol; + _precision = precision; + } + + /// + /// Converts a character to a StandardFormat using the NoPrecision precision. + /// + public static implicit operator StandardFormat(char symbol) => new StandardFormat(symbol); + + /// + /// Converts a classic .NET format string into a StandardFormat + /// + public static StandardFormat Parse(ReadOnlySpan format) + { + if (format.Length == 0) + return default; + + char symbol = format[0]; + byte precision; + if (format.Length == 1) + { + precision = NoPrecision; + } + else + { + uint parsedPrecision = 0; + for (int srcIndex = 1; srcIndex < format.Length; srcIndex++) + { + uint digit = format[srcIndex] - 48u; // '0' + if (digit > 9) + throw new FormatException(SR.Format(SR.Argument_CannotParsePrecision, MaxPrecision)); + + parsedPrecision = parsedPrecision * 10 + digit; + if (parsedPrecision > MaxPrecision) + throw new FormatException(SR.Format(SR.Argument_PrecisionTooLarge, MaxPrecision)); + } + + precision = (byte)parsedPrecision; + } + + return new StandardFormat(symbol, precision); + } + + /// + /// Converts a classic .NET format string into a StandardFormat + /// + public static StandardFormat Parse(string format) => format == null ? default : Parse(MemoryExtensions.AsReadOnlySpan(format)); //@todo: Change back to extension syntax once the ambiguous reference with CoreLib is eliminated. + + /// + /// Returns true if both the Symbol and Precision are equal. + /// + public override bool Equals(object obj) => obj is StandardFormat other && Equals(other); + + /// + /// Compute a hash code. + /// + public override int GetHashCode() => _format.GetHashCode() ^ _precision.GetHashCode(); + + /// + /// Returns true if both the Symbol and Precision are equal. + /// + public bool Equals(StandardFormat other) => _format == other._format && _precision == other._precision; + + /// + /// Returns the format in classic .NET format. + /// + public override string ToString() + { + unsafe + { + const int MaxLength = 4; + char* pBuffer = stackalloc char[MaxLength]; + + int dstIndex = 0; + char symbol = Symbol; + if (symbol != default) + { + pBuffer[dstIndex++] = symbol; + + byte precision = Precision; + if (precision != NoPrecision) + { + if (precision >= 100) + { + pBuffer[dstIndex++] = (char)('0' + (precision / 100) % 10); + precision = (byte)(precision % 100); + } + + if (precision >= 10) + { + pBuffer[dstIndex++] = (char)('0' + (precision / 10) % 10); + precision = (byte)(precision % 10); + } + + pBuffer[dstIndex++] = (char)('0' + precision); + } + } + + Debug.Assert(dstIndex <= MaxLength); + + return new string(pBuffer, startIndex: 0, length: dstIndex); + } + } + + /// + /// Returns true if both the Symbol and Precision are equal. + /// + public static bool operator ==(StandardFormat left, StandardFormat right) => left.Equals(right); + + /// + /// Returns false if both the Symbol and Precision are equal. + /// + public static bool operator !=(StandardFormat left, StandardFormat right) => !left.Equals(right); + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Base64Decoder.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Base64Decoder.cs new file mode 100644 index 0000000000..8070b9a2a3 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Base64Decoder.cs @@ -0,0 +1,340 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#if !netstandard +using Internal.Runtime.CompilerServices; +#endif + +namespace System.Buffers.Text +{ + public static partial class Base64 + { + /// + /// Decode the span of UTF-8 encoded text represented as base 64 into binary data. + /// If the input is not a multiple of 4, it will decode as much as it can, to the closest multiple of 4. + /// + /// The input span which contains UTF-8 encoded text in base 64 that needs to be decoded. + /// The output span which contains the result of the operation, i.e. the decoded binary data. + /// The number of input bytes consumed during the operation. This can be used to slice the input for subsequent calls, if necessary. + /// The number of bytes written into the output span. This can be used to slice the output for subsequent calls, if necessary. + /// True (default) when the input span contains the entire data to decode. + /// Set to false only if it is known that the input span contains partial data with more data to follow. + /// It returns the OperationStatus enum values: + /// - Done - on successful processing of the entire input span + /// - DestinationTooSmall - if there is not enough space in the output span to fit the decoded input + /// - NeedMoreData - only if isFinalBlock is false and the input is not a multiple of 4, otherwise the partial input would be considered as InvalidData + /// - InvalidData - if the input contains bytes outside of the expected base 64 range, or if it contains invalid/more than two padding characters, + /// or if the input is incomplete (i.e. not a multiple of 4) and isFinalBlock is true. + /// + public static OperationStatus DecodeFromUtf8(ReadOnlySpan utf8, Span bytes, out int consumed, out int written, bool isFinalBlock = true) + { + ref byte srcBytes = ref MemoryMarshal.GetReference(utf8); + ref byte destBytes = ref MemoryMarshal.GetReference(bytes); + + int srcLength = utf8.Length & ~0x3; // only decode input up to the closest multiple of 4. + int destLength = bytes.Length; + + int sourceIndex = 0; + int destIndex = 0; + + if (utf8.Length == 0) + goto DoneExit; + + ref sbyte decodingMap = ref s_decodingMap[0]; + + // Last bytes could have padding characters, so process them separately and treat them as valid only if isFinalBlock is true + // if isFinalBlock is false, padding characters are considered invalid + int skipLastChunk = isFinalBlock ? 4 : 0; + + int maxSrcLength = 0; + if (destLength >= GetMaxDecodedFromUtf8Length(srcLength)) + { + maxSrcLength = srcLength - skipLastChunk; + } + else + { + // This should never overflow since destLength here is less than int.MaxValue / 4 * 3 (i.e. 1610612733) + // Therefore, (destLength / 3) * 4 will always be less than 2147483641 + maxSrcLength = (destLength / 3) * 4; + } + + while (sourceIndex < maxSrcLength) + { + int result = Decode(ref Unsafe.Add(ref srcBytes, sourceIndex), ref decodingMap); + if (result < 0) + goto InvalidExit; + WriteThreeLowOrderBytes(ref Unsafe.Add(ref destBytes, destIndex), result); + destIndex += 3; + sourceIndex += 4; + } + + if (maxSrcLength != srcLength - skipLastChunk) + goto DestinationSmallExit; + + // If input is less than 4 bytes, srcLength == sourceIndex == 0 + // If input is not a multiple of 4, sourceIndex == srcLength != 0 + if (sourceIndex == srcLength) + { + if (isFinalBlock) + goto InvalidExit; + goto NeedMoreExit; + } + + // if isFinalBlock is false, we will never reach this point + + int i0 = Unsafe.Add(ref srcBytes, srcLength - 4); + int i1 = Unsafe.Add(ref srcBytes, srcLength - 3); + int i2 = Unsafe.Add(ref srcBytes, srcLength - 2); + int i3 = Unsafe.Add(ref srcBytes, srcLength - 1); + + i0 = Unsafe.Add(ref decodingMap, i0); + i1 = Unsafe.Add(ref decodingMap, i1); + + i0 <<= 18; + i1 <<= 12; + + i0 |= i1; + + if (i3 != EncodingPad) + { + i2 = Unsafe.Add(ref decodingMap, i2); + i3 = Unsafe.Add(ref decodingMap, i3); + + i2 <<= 6; + + i0 |= i3; + i0 |= i2; + + if (i0 < 0) + goto InvalidExit; + if (destIndex > destLength - 3) + goto DestinationSmallExit; + WriteThreeLowOrderBytes(ref Unsafe.Add(ref destBytes, destIndex), i0); + destIndex += 3; + } + else if (i2 != EncodingPad) + { + i2 = Unsafe.Add(ref decodingMap, i2); + + i2 <<= 6; + + i0 |= i2; + + if (i0 < 0) + goto InvalidExit; + if (destIndex > destLength - 2) + goto DestinationSmallExit; + Unsafe.Add(ref destBytes, destIndex) = (byte)(i0 >> 16); + Unsafe.Add(ref destBytes, destIndex + 1) = (byte)(i0 >> 8); + destIndex += 2; + } + else + { + if (i0 < 0) + goto InvalidExit; + if (destIndex > destLength - 1) + goto DestinationSmallExit; + Unsafe.Add(ref destBytes, destIndex) = (byte)(i0 >> 16); + destIndex += 1; + } + + sourceIndex += 4; + + if (srcLength != utf8.Length) + goto InvalidExit; + + DoneExit: + consumed = sourceIndex; + written = destIndex; + return OperationStatus.Done; + + DestinationSmallExit: + if (srcLength != utf8.Length && isFinalBlock) + goto InvalidExit; // if input is not a multiple of 4, and there is no more data, return invalid data instead + consumed = sourceIndex; + written = destIndex; + return OperationStatus.DestinationTooSmall; + + NeedMoreExit: + consumed = sourceIndex; + written = destIndex; + return OperationStatus.NeedMoreData; + + InvalidExit: + consumed = sourceIndex; + written = destIndex; + return OperationStatus.InvalidData; + } + + /// + /// Returns the maximum length (in bytes) of the result if you were to deocde base 64 encoded text within a byte span of size "length". + /// + /// + /// Thrown when the specified is less than 0. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetMaxDecodedFromUtf8Length(int length) + { + if (length < 0) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length); + + return (length >> 2) * 3; + } + + /// + /// Decode the span of UTF-8 encoded text in base 64 (in-place) into binary data. + /// The decoded binary output is smaller than the text data contained in the input (the operation deflates the data). + /// If the input is not a multiple of 4, it will not decode any. + /// + /// The input span which contains the base 64 text data that needs to be decoded. + /// The number of bytes written into the buffer. + /// It returns the OperationStatus enum values: + /// - Done - on successful processing of the entire input span + /// - InvalidData - if the input contains bytes outside of the expected base 64 range, or if it contains invalid/more than two padding characters, + /// or if the input is incomplete (i.e. not a multiple of 4). + /// It does not return DestinationTooSmall since that is not possible for base 64 decoding. + /// It does not return NeedMoreData since this method tramples the data in the buffer and + /// hence can only be called once with all the data in the buffer. + /// + public static OperationStatus DecodeFromUtf8InPlace(Span buffer, out int written) + { + int bufferLength = buffer.Length; + int sourceIndex = 0; + int destIndex = 0; + + // only decode input if it is a multiple of 4 + if (bufferLength != ((bufferLength >> 2) * 4)) + goto InvalidExit; + if (bufferLength == 0) + goto DoneExit; + + ref byte bufferBytes = ref MemoryMarshal.GetReference(buffer); + + ref sbyte decodingMap = ref s_decodingMap[0]; + + while (sourceIndex < bufferLength - 4) + { + int result = Decode(ref Unsafe.Add(ref bufferBytes, sourceIndex), ref decodingMap); + if (result < 0) + goto InvalidExit; + WriteThreeLowOrderBytes(ref Unsafe.Add(ref bufferBytes, destIndex), result); + destIndex += 3; + sourceIndex += 4; + } + + int i0 = Unsafe.Add(ref bufferBytes, bufferLength - 4); + int i1 = Unsafe.Add(ref bufferBytes, bufferLength - 3); + int i2 = Unsafe.Add(ref bufferBytes, bufferLength - 2); + int i3 = Unsafe.Add(ref bufferBytes, bufferLength - 1); + + i0 = Unsafe.Add(ref decodingMap, i0); + i1 = Unsafe.Add(ref decodingMap, i1); + + i0 <<= 18; + i1 <<= 12; + + i0 |= i1; + + if (i3 != EncodingPad) + { + i2 = Unsafe.Add(ref decodingMap, i2); + i3 = Unsafe.Add(ref decodingMap, i3); + + i2 <<= 6; + + i0 |= i3; + i0 |= i2; + + if (i0 < 0) + goto InvalidExit; + WriteThreeLowOrderBytes(ref Unsafe.Add(ref bufferBytes, destIndex), i0); + destIndex += 3; + } + else if (i2 != EncodingPad) + { + i2 = Unsafe.Add(ref decodingMap, i2); + + i2 <<= 6; + + i0 |= i2; + + if (i0 < 0) + goto InvalidExit; + Unsafe.Add(ref bufferBytes, destIndex) = (byte)(i0 >> 16); + Unsafe.Add(ref bufferBytes, destIndex + 1) = (byte)(i0 >> 8); + destIndex += 2; + } + else + { + if (i0 < 0) + goto InvalidExit; + Unsafe.Add(ref bufferBytes, destIndex) = (byte)(i0 >> 16); + destIndex += 1; + } + + DoneExit: + written = destIndex; + return OperationStatus.Done; + + InvalidExit: + written = destIndex; + return OperationStatus.InvalidData; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Decode(ref byte encodedBytes, ref sbyte decodingMap) + { + int i0 = encodedBytes; + int i1 = Unsafe.Add(ref encodedBytes, 1); + int i2 = Unsafe.Add(ref encodedBytes, 2); + int i3 = Unsafe.Add(ref encodedBytes, 3); + + i0 = Unsafe.Add(ref decodingMap, i0); + i1 = Unsafe.Add(ref decodingMap, i1); + i2 = Unsafe.Add(ref decodingMap, i2); + i3 = Unsafe.Add(ref decodingMap, i3); + + i0 <<= 18; + i1 <<= 12; + i2 <<= 6; + + i0 |= i3; + i1 |= i2; + + i0 |= i1; + return i0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WriteThreeLowOrderBytes(ref byte destination, int value) + { + destination = (byte)(value >> 16); + Unsafe.Add(ref destination, 1) = (byte)(value >> 8); + Unsafe.Add(ref destination, 2) = (byte)value; + } + + // Pre-computing this table using a custom string(s_characters) and GenerateDecodingMapAndVerify (found in tests) + private static readonly sbyte[] s_decodingMap = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, //62 is placed at index 43 (for +), 63 at index 47 (for /) + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, //52-61 are placed at index 48-57 (for 0-9), 64 at index 61 (for =) + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, //0-25 are placed at index 65-90 (for A-Z) + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, //26-51 are placed at index 97-122 (for a-z) + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Bytes over 122 ('z') are invalid and cannot be decoded + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Hence, padding the map with 255, which indicates invalid input + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Base64Encoder.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Base64Encoder.cs new file mode 100644 index 0000000000..fab5542c03 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Base64Encoder.cs @@ -0,0 +1,233 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#if !netstandard +using Internal.Runtime.CompilerServices; +#endif + +namespace System.Buffers.Text +{ + /// + /// Convert between binary data and UTF-8 encoded text that is represented in base 64. + /// + public static partial class Base64 + { + /// + /// Encode the span of binary data into UTF-8 encoded text represented as base 64. + /// + /// The input span which contains binary data that needs to be encoded. + /// The output span which contains the result of the operation, i.e. the UTF-8 encoded text in base 64. + /// The number of input bytes consumed during the operation. This can be used to slice the input for subsequent calls, if necessary. + /// The number of bytes written into the output span. This can be used to slice the output for subsequent calls, if necessary. + /// True (default) when the input span contains the entire data to decode. + /// Set to false only if it is known that the input span contains partial data with more data to follow. + /// It returns the OperationStatus enum values: + /// - Done - on successful processing of the entire input span + /// - DestinationTooSmall - if there is not enough space in the output span to fit the encoded input + /// - NeedMoreData - only if isFinalBlock is false, otherwise the output is padded if the input is not a multiple of 3 + /// It does not return InvalidData since that is not possible for base 64 encoding. + /// + public static OperationStatus EncodeToUtf8(ReadOnlySpan bytes, Span utf8, out int consumed, out int written, bool isFinalBlock = true) + { + ref byte srcBytes = ref MemoryMarshal.GetReference(bytes); + ref byte destBytes = ref MemoryMarshal.GetReference(utf8); + + int srcLength = bytes.Length; + int destLength = utf8.Length; + + int maxSrcLength = 0; + if (srcLength <= MaximumEncodeLength && destLength >= GetMaxEncodedToUtf8Length(srcLength)) + { + maxSrcLength = srcLength - 2; + } + else + { + maxSrcLength = (destLength >> 2) * 3 - 2; + } + + int sourceIndex = 0; + int destIndex = 0; + int result = 0; + + ref byte encodingMap = ref s_encodingMap[0]; + + while (sourceIndex < maxSrcLength) + { + result = Encode(ref Unsafe.Add(ref srcBytes, sourceIndex), ref encodingMap); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref destBytes, destIndex), result); + destIndex += 4; + sourceIndex += 3; + } + + if (maxSrcLength != srcLength - 2) + goto DestinationSmallExit; + + if (isFinalBlock != true) + goto NeedMoreDataExit; + + if (sourceIndex == srcLength - 1) + { + result = EncodeAndPadTwo(ref Unsafe.Add(ref srcBytes, sourceIndex), ref encodingMap); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref destBytes, destIndex), result); + destIndex += 4; + sourceIndex += 1; + } + else if (sourceIndex == srcLength - 2) + { + result = EncodeAndPadOne(ref Unsafe.Add(ref srcBytes, sourceIndex), ref encodingMap); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref destBytes, destIndex), result); + destIndex += 4; + sourceIndex += 2; + } + + consumed = sourceIndex; + written = destIndex; + return OperationStatus.Done; + + NeedMoreDataExit: + consumed = sourceIndex; + written = destIndex; + return OperationStatus.NeedMoreData; + + DestinationSmallExit: + consumed = sourceIndex; + written = destIndex; + return OperationStatus.DestinationTooSmall; + } + + /// + /// Returns the maximum length (in bytes) of the result if you were to encode binary data within a byte span of size "length". + /// + /// + /// Thrown when the specified is less than 0 or larger than 1610612733 (since encode inflates the data by 4/3). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetMaxEncodedToUtf8Length(int length) + { + if (length < 0 || length > MaximumEncodeLength) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length); + + return (((length + 2) / 3) * 4); + } + + /// + /// Encode the span of binary data (in-place) into UTF-8 encoded text represented as base 64. + /// The encoded text output is larger than the binary data contained in the input (the operation inflates the data). + /// + /// The input span which contains binary data that needs to be encoded. + /// It needs to be large enough to fit the result of the operation. + /// The amount of binary data contained within the buffer that needs to be encoded + /// (and needs to be smaller than the buffer length). + /// The number of bytes written into the buffer. + /// It returns the OperationStatus enum values: + /// - Done - on successful processing of the entire buffer + /// - DestinationTooSmall - if there is not enough space in the buffer beyond dataLength to fit the result of encoding the input + /// It does not return NeedMoreData since this method tramples the data in the buffer and hence can only be called once with all the data in the buffer. + /// It does not return InvalidData since that is not possible for base 64 encoding. + /// + public static OperationStatus EncodeToUtf8InPlace(Span buffer, int dataLength, out int written) + { + int encodedLength = GetMaxEncodedToUtf8Length(dataLength); + if (buffer.Length < encodedLength) + goto FalseExit; + + int leftover = dataLength - dataLength / 3 * 3; // how many bytes after packs of 3 + + int destinationIndex = encodedLength - 4; + int sourceIndex = dataLength - leftover; + int result = 0; + + ref byte encodingMap = ref s_encodingMap[0]; + ref byte bufferBytes = ref MemoryMarshal.GetReference(buffer); + + // encode last pack to avoid conditional in the main loop + if (leftover != 0) + { + if (leftover == 1) + { + result = EncodeAndPadTwo(ref Unsafe.Add(ref bufferBytes, sourceIndex), ref encodingMap); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref bufferBytes, destinationIndex), result); + destinationIndex -= 4; + } + else + { + result = EncodeAndPadOne(ref Unsafe.Add(ref bufferBytes, sourceIndex), ref encodingMap); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref bufferBytes, destinationIndex), result); + destinationIndex -= 4; + } + } + + sourceIndex -= 3; + while (sourceIndex >= 0) + { + result = Encode(ref Unsafe.Add(ref bufferBytes, sourceIndex), ref encodingMap); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref bufferBytes, destinationIndex), result); + destinationIndex -= 4; + sourceIndex -= 3; + } + + written = encodedLength; + return OperationStatus.Done; + + FalseExit: + written = 0; + return OperationStatus.DestinationTooSmall; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Encode(ref byte threeBytes, ref byte encodingMap) + { + int i = (threeBytes << 16) | (Unsafe.Add(ref threeBytes, 1) << 8) | Unsafe.Add(ref threeBytes, 2); + + int i0 = Unsafe.Add(ref encodingMap, i >> 18); + int i1 = Unsafe.Add(ref encodingMap, (i >> 12) & 0x3F); + int i2 = Unsafe.Add(ref encodingMap, (i >> 6) & 0x3F); + int i3 = Unsafe.Add(ref encodingMap, i & 0x3F); + + return i0 | (i1 << 8) | (i2 << 16) | (i3 << 24); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int EncodeAndPadOne(ref byte twoBytes, ref byte encodingMap) + { + int i = (twoBytes << 16) | (Unsafe.Add(ref twoBytes, 1) << 8); + + int i0 = Unsafe.Add(ref encodingMap, i >> 18); + int i1 = Unsafe.Add(ref encodingMap, (i >> 12) & 0x3F); + int i2 = Unsafe.Add(ref encodingMap, (i >> 6) & 0x3F); + + return i0 | (i1 << 8) | (i2 << 16) | (EncodingPad << 24); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int EncodeAndPadTwo(ref byte oneByte, ref byte encodingMap) + { + int i = (oneByte << 8); + + int i0 = Unsafe.Add(ref encodingMap, i >> 10); + int i1 = Unsafe.Add(ref encodingMap, (i >> 4) & 0x3F); + + return i0 | (i1 << 8) | (EncodingPad << 16) | (EncodingPad << 24); + } + + // Pre-computing this table using a custom string(s_characters) and GenerateEncodingMapAndVerify (found in tests) + private static readonly byte[] s_encodingMap = { + 65, 66, 67, 68, 69, 70, 71, 72, //A..H + 73, 74, 75, 76, 77, 78, 79, 80, //I..P + 81, 82, 83, 84, 85, 86, 87, 88, //Q..X + 89, 90, 97, 98, 99, 100, 101, 102, //Y..Z, a..f + 103, 104, 105, 106, 107, 108, 109, 110, //g..n + 111, 112, 113, 114, 115, 116, 117, 118, //o..v + 119, 120, 121, 122, 48, 49, 50, 51, //w..z, 0..3 + 52, 53, 54, 55, 56, 57, 43, 47 //4..9, +, / + }; + + private const byte EncodingPad = (byte)'='; // '=', for padding + + private const int MaximumEncodeLength = (int.MaxValue >> 2) * 3; // 1610612733 + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Constants.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Constants.cs new file mode 100644 index 0000000000..bfbbdef8a4 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Constants.cs @@ -0,0 +1,34 @@ +// 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. + +namespace System.Buffers.Text +{ + internal static partial class Utf8Constants + { + public const byte Colon = (byte)':'; + public const byte Comma = (byte)','; + public const byte Minus = (byte)'-'; + public const byte Period = (byte)'.'; + public const byte Plus = (byte)'+'; + public const byte Slash = (byte)'/'; + public const byte Space = (byte)' '; + public const byte Hyphen = (byte)'-'; + + public const byte Separator = (byte)','; + + // Invariant formatting uses groups of 3 for each number group separated by commas. + // ex. 1,234,567,890 + public const int GroupSize = 3; + + public static readonly TimeSpan s_nullUtcOffset = TimeSpan.MinValue; // Utc offsets must range from -14:00 to 14:00 so this is never a valid offset. + + public const int DateTimeMaxUtcOffsetHours = 14; // The UTC offset portion of a TimeSpan or DateTime can be no more than 14 hours and no less than -14 hours. + + public const int DateTimeNumFractionDigits = 7; // TimeSpan and DateTime formats allow exactly up to many digits for specifying the fraction after the seconds. + public const int MaxDateTimeFraction = 9999999; // ... and hence, the largest fraction expressible is this. + + public const ulong BillionMaxUIntValue = (ulong)uint.MaxValue * Billion; // maximum value that can be split into two uint32 {1-10 digits}{9 digits} + public const uint Billion = 1000000000; // 10^9, used to split int64/uint64 into three uint32 {1-2 digits}{9 digits}{9 digits} + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/FormattingHelpers.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/FormattingHelpers.cs new file mode 100644 index 0000000000..86079ed871 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/FormattingHelpers.cs @@ -0,0 +1,401 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; + +#if !netstandard +using Internal.Runtime.CompilerServices; +#endif + +namespace System.Buffers.Text +{ + // All the helper methods in this class assume that the by-ref is valid and that there is + // enough space to fit the items that will be written into the underlying memory. The calling + // code must have already done all the necessary validation. + internal static class FormattingHelpers + { + // A simple lookup table for converting numbers to hex. + internal const string HexTableLower = "0123456789abcdef"; + + internal const string HexTableUpper = "0123456789ABCDEF"; + + /// + /// Returns the symbol contained within the standard format. If the standard format + /// has not been initialized, returns the provided fallback symbol. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static char GetSymbolOrDefault(in StandardFormat format, char defaultSymbol) + { + // This is equivalent to the line below, but it is written in such a way + // that the JIT is able to perform more optimizations. + // + // return (format.IsDefault) ? defaultSymbol : format.Symbol; + + var symbol = format.Symbol; + if (symbol == default && format.Precision == default) + { + symbol = defaultSymbol; + } + return symbol; + } + + #region UTF-8 Helper methods + + /// + /// Fills a buffer with the ASCII character '0' (0x30). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void FillWithAsciiZeros(Span buffer) + { + // This is a faster implementation of Span.Fill(). + for (int i = 0; i < buffer.Length; i++) + { + buffer[i] = (byte)'0'; + } + } + + public enum HexCasing : uint + { + // Output [ '0' .. '9' ] and [ 'A' .. 'F' ]. + Uppercase = 0, + + // Output [ '0' .. '9' ] and [ 'a' .. 'f' ]. + // This works because values in the range [ 0x30 .. 0x39 ] ([ '0' .. '9' ]) + // already have the 0x20 bit set, so ORing them with 0x20 is a no-op, + // while outputs in the range [ 0x41 .. 0x46 ] ([ 'A' .. 'F' ]) + // don't have the 0x20 bit set, so ORing them maps to + // [ 0x61 .. 0x66 ] ([ 'a' .. 'f' ]), which is what we want. + Lowercase = 0x2020U, + } + + // The JIT can elide bounds checks if 'startingIndex' is constant and if the caller is + // writing to a span of known length (or the caller has already checked the bounds of the + // furthest access). + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteHexByte(byte value, Span buffer, int startingIndex = 0, HexCasing casing = HexCasing.Uppercase) + { + // We want to pack the incoming byte into a single integer [ 0000 HHHH 0000 LLLL ], + // where HHHH and LLLL are the high and low nibbles of the incoming byte. Then + // subtract this integer from a constant minuend as shown below. + // + // [ 1000 1001 1000 1001 ] + // - [ 0000 HHHH 0000 LLLL ] + // ========================= + // [ *YYY **** *ZZZ **** ] + // + // The end result of this is that YYY is 0b000 if HHHH <= 9, and YYY is 0b111 if HHHH >= 10. + // Similarly, ZZZ is 0b000 if LLLL <= 9, and ZZZ is 0b111 if LLLL >= 10. + // (We don't care about the value of asterisked bits.) + // + // To turn a nibble in the range [ 0 .. 9 ] into hex, we calculate hex := nibble + 48 (ascii '0'). + // To turn a nibble in the range [ 10 .. 15 ] into hex, we calculate hex := nibble - 10 + 65 (ascii 'A'). + // => hex := nibble + 55. + // The difference in the starting ASCII offset is (55 - 48) = 7, depending on whether the nibble is <= 9 or >= 10. + // Since 7 is 0b111, this conveniently matches the YYY or ZZZ value computed during the earlier subtraction. + + // The commented out code below is code that directly implements the logic described above. + + //uint packedOriginalValues = (((uint)value & 0xF0U) << 4) + ((uint)value & 0x0FU); + //uint difference = 0x8989U - packedOriginalValues; + //uint add7Mask = (difference & 0x7070U) >> 4; // line YYY and ZZZ back up with the packed values + //uint packedResult = packedOriginalValues + add7Mask + 0x3030U /* ascii '0' */; + + // The code below is equivalent to the commented out code above but has been tweaked + // to allow codegen to make some extra optimizations. + + uint difference = (((uint)value & 0xF0U) << 4) + ((uint)value & 0x0FU) - 0x8989U; + uint packedResult = ((((uint)(-(int)difference) & 0x7070U) >> 4) + difference + 0xB9B9U) | (uint)casing; + + // The low byte of the packed result contains the hex representation of the incoming byte's low nibble. + // The adjacent byte of the packed result contains the hex representation of the incoming byte's high nibble. + + // Finally, write to the output buffer starting with the *highest* index so that codegen can + // elide all but the first bounds check. (This only works if 'startingIndex' is a compile-time constant.) + + buffer[startingIndex + 1] = (byte)(packedResult); + buffer[startingIndex] = (byte)(packedResult >> 8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteDigits(ulong value, Span buffer) + { + // We can mutate the 'value' parameter since it's a copy-by-value local. + // It'll be used to represent the value left over after each division by 10. + + for (int i = buffer.Length - 1; i >= 1; i--) + { + ulong temp = '0' + value; + value /= 10; + buffer[i] = (byte)(temp - (value * 10)); + } + + Debug.Assert(value < 10); + buffer[0] = (byte)('0' + value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteDigitsWithGroupSeparator(ulong value, Span buffer) + { + // We can mutate the 'value' parameter since it's a copy-by-value local. + // It'll be used to represent the value left over after each division by 10. + + int digitsWritten = 0; + for (int i = buffer.Length - 1; i >= 1; i--) + { + ulong temp = '0' + value; + value /= 10; + buffer[i] = (byte)(temp - (value * 10)); + if (digitsWritten == Utf8Constants.GroupSize - 1) + { + buffer[--i] = Utf8Constants.Comma; + digitsWritten = 0; + } + else + { + digitsWritten++; + } + } + + Debug.Assert(value < 10); + buffer[0] = (byte)('0' + value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteDigits(uint value, Span buffer) + { + // We can mutate the 'value' parameter since it's a copy-by-value local. + // It'll be used to represent the value left over after each division by 10. + + for (int i = buffer.Length - 1; i >= 1; i--) + { + uint temp = '0' + value; + value /= 10; + buffer[i] = (byte)(temp - (value * 10)); + } + + Debug.Assert(value < 10); + buffer[0] = (byte)('0' + value); + } + + /// + /// Writes a value [ 0000 .. 9999 ] to the buffer starting at the specified offset. + /// This method performs best when the starting index is a constant literal. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteFourDecimalDigits(uint value, Span buffer, int startingIndex = 0) + { + Debug.Assert(0 <= value && value <= 9999); + + uint temp = '0' + value; + value /= 10; + buffer[startingIndex + 3] = (byte)(temp - (value * 10)); + + temp = '0' + value; + value /= 10; + buffer[startingIndex + 2] = (byte)(temp - (value * 10)); + + temp = '0' + value; + value /= 10; + buffer[startingIndex + 1] = (byte)(temp - (value * 10)); + + buffer[startingIndex] = (byte)('0' + value); + } + + /// + /// Writes a value [ 00 .. 99 ] to the buffer starting at the specified offset. + /// This method performs best when the starting index is a constant literal. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteTwoDecimalDigits(uint value, Span buffer, int startingIndex = 0) + { + Debug.Assert(0 <= value && value <= 99); + + uint temp = '0' + value; + value /= 10; + buffer[startingIndex + 1] = (byte)(temp - (value * 10)); + + buffer[startingIndex] = (byte)('0' + value); + } + + #endregion UTF-8 Helper methods + + #region Math Helper methods + + /// + /// We don't have access to Math.DivRem, so this is a copy of the implementation. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong DivMod(ulong numerator, ulong denominator, out ulong modulo) + { + ulong div = numerator / denominator; + modulo = numerator - (div * denominator); + return div; + } + + /// + /// We don't have access to Math.DivRem, so this is a copy of the implementation. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint DivMod(uint numerator, uint denominator, out uint modulo) + { + uint div = numerator / denominator; + modulo = numerator - (div * denominator); + return div; + } + + #endregion Math Helper methods + + #region Character counting helper methods + + // Counts the number of trailing '0' digits in a decimal numnber. + // e.g., value = 0 => retVal = 0, valueWithoutTrailingZeros = 0 + // value = 1234 => retVal = 0, valueWithoutTrailingZeros = 1234 + // value = 320900 => retVal = 2, valueWithoutTrailingZeros = 3209 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CountDecimalTrailingZeros(uint value, out uint valueWithoutTrailingZeros) + { + int zeroCount = 0; + + if (value != 0) + { + while (true) + { + uint temp = DivMod(value, 10, out uint modulus); + if (modulus != 0) + { + break; + } + value = temp; + zeroCount++; + } + } + + valueWithoutTrailingZeros = value; + return zeroCount; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CountDigits(ulong value) + { + int digits = 1; + uint part; + if (value >= 10000000) + { + if (value >= 100000000000000) + { + part = (uint)(value / 100000000000000); + digits += 14; + } + else + { + part = (uint)(value / 10000000); + digits += 7; + } + } + else + { + part = (uint)value; + } + + if (part < 10) + { + // no-op + } + else if (part < 100) + { + digits += 1; + } + else if (part < 1000) + { + digits += 2; + } + else if (part < 10000) + { + digits += 3; + } + else if (part < 100000) + { + digits += 4; + } + else if (part < 1000000) + { + digits += 5; + } + else + { + Debug.Assert(part < 10000000); + digits += 6; + } + + return digits; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CountDigits(uint value) + { + int digits = 1; + if (value >= 100000) + { + value = value / 100000; + digits += 5; + } + + if (value < 10) + { + // no-op + } + else if (value < 100) + { + digits += 1; + } + else if (value < 1000) + { + digits += 2; + } + else if (value < 10000) + { + digits += 3; + } + else + { + Debug.Assert(value < 100000); + digits += 4; + } + + return digits; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CountHexDigits(ulong value) + { + // TODO: When x86 intrinsic support comes online, experiment with implementing this using lzcnt. + // return 16 - (int)((uint)Lzcnt.LeadingZeroCount(value | 1) >> 3); + + int digits = 1; + + if (value > 0xFFFFFFFF) + { + digits += 8; + value >>= 0x20; + } + if (value > 0xFFFF) + { + digits += 4; + value >>= 0x10; + } + if (value > 0xFF) + { + digits += 2; + value >>= 0x8; + } + if (value > 0xF) + digits++; + + return digits; + } + + #endregion Character counting helper methods + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Boolean.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Boolean.cs new file mode 100644 index 0000000000..d9cb3b7004 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Boolean.cs @@ -0,0 +1,105 @@ +// 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.Buffers.Binary; + +namespace System.Buffers.Text +{ + public static partial class Utf8Formatter + { + /// + /// Formats a Boolean as a UTF8 string. + /// + /// Value to format + /// Buffer to write the UTF8-formatted value to + /// Receives the length of the formatted text in bytes + /// The standard format to use + /// + /// true for success. "bytesWritten" contains the length of the formatted text in bytes. + /// false if buffer was too short. Iteratively increase the size of the buffer and retry until it succeeds. + /// + /// + /// Formats supported: + /// G (default) True/False + /// l true/false + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryFormat(bool value, Span buffer, out int bytesWritten, StandardFormat format = default) + { + char symbol = FormattingHelpers.GetSymbolOrDefault(format, 'G'); + + if (value) + { + if (symbol == 'G') + { + // By having each branch perform its own call to TryWriteUInt32BigEndian, we ensure that a + // constant value is passed to this routine, which means the compiler can reverse endianness + // at compile time instead of runtime if necessary. + const uint TrueValueUppercase = ('T' << 24) + ('r' << 16) + ('u' << 8) + ('e' << 0); + if (!BinaryPrimitives.TryWriteUInt32BigEndian(buffer, TrueValueUppercase)) + { + goto BufferTooSmall; + } + } + else if (symbol == 'l') + { + const uint TrueValueLowercase = ('t' << 24) + ('r' << 16) + ('u' << 8) + ('e' << 0); + if (!BinaryPrimitives.TryWriteUInt32BigEndian(buffer, TrueValueLowercase)) + { + goto BufferTooSmall; + } + } + else + { + goto BadFormat; + } + + bytesWritten = 4; + return true; + } + else + { + if (symbol == 'G') + { + // This check can't be performed earlier because we need to throw if an invalid symbol is + // provided, even if the buffer is too small. + if ((uint)4 >= (uint)buffer.Length) + { + goto BufferTooSmall; + } + + const uint FalsValueUppercase = ('F' << 24) + ('a' << 16) + ('l' << 8) + ('s' << 0); + BinaryPrimitives.WriteUInt32BigEndian(buffer, FalsValueUppercase); + } + else if (symbol == 'l') + { + if ((uint)4 >= (uint)buffer.Length) + { + goto BufferTooSmall; + } + + const uint FalsValueLowercase = ('f' << 24) + ('a' << 16) + ('l' << 8) + ('s' << 0); + BinaryPrimitives.WriteUInt32BigEndian(buffer, FalsValueLowercase); + } + else + { + goto BadFormat; + } + + buffer[4] = (byte)'e'; + bytesWritten = 5; + return true; + } + +BufferTooSmall: + bytesWritten = 0; + return false; + +BadFormat: + return ThrowHelper.TryFormatThrowFormatException(out bytesWritten); + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.G.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.G.cs new file mode 100644 index 0000000000..7915ec5c51 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.G.cs @@ -0,0 +1,92 @@ +// 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. + +namespace System.Buffers.Text +{ + public static partial class Utf8Formatter + { + // + // 'G' format for DateTime. + // + // 0123456789012345678 + // --------------------------------- + // 05/25/2017 10:30:15 + // + // Also handles the default ToString() format for DateTimeOffset + // + // 01234567890123456789012345 + // -------------------------- + // 05/25/2017 10:30:15 -08:00 + // + private static bool TryFormatDateTimeG(DateTime value, TimeSpan offset, Span buffer, out int bytesWritten) + { + const int MinimumBytesNeeded = 19; + + int bytesRequired = MinimumBytesNeeded; + + if (offset != Utf8Constants.s_nullUtcOffset) + { + bytesRequired += 7; // Space['+'|'-']hh:mm + } + + if (buffer.Length < bytesRequired) + { + bytesWritten = 0; + return false; + } + + bytesWritten = bytesRequired; + + // Hoist most of the bounds checks on buffer. + { var unused = buffer[MinimumBytesNeeded - 1]; } + + // TODO: Introduce an API which can parse DateTime instances efficiently, pulling out + // all their properties (Month, Day, etc.) in one shot. This would help avoid the + // duplicate work that implicitly results from calling these properties individually. + + FormattingHelpers.WriteTwoDecimalDigits((uint)value.Month, buffer, 0); + buffer[2] = Utf8Constants.Slash; + + FormattingHelpers.WriteTwoDecimalDigits((uint)value.Day, buffer, 3); + buffer[5] = Utf8Constants.Slash; + + FormattingHelpers.WriteFourDecimalDigits((uint)value.Year, buffer, 6); + buffer[10] = Utf8Constants.Space; + + FormattingHelpers.WriteTwoDecimalDigits((uint)value.Hour, buffer, 11); + buffer[13] = Utf8Constants.Colon; + + FormattingHelpers.WriteTwoDecimalDigits((uint)value.Minute, buffer, 14); + buffer[16] = Utf8Constants.Colon; + + FormattingHelpers.WriteTwoDecimalDigits((uint)value.Second, buffer, 17); + + if (offset != Utf8Constants.s_nullUtcOffset) + { + byte sign; + + if (offset < default(TimeSpan) /* a "const" version of TimeSpan.Zero */) + { + sign = Utf8Constants.Minus; + offset = TimeSpan.FromTicks(-offset.Ticks); + } + else + { + sign = Utf8Constants.Plus; + } + + // Writing the value backward allows the JIT to optimize by + // performing a single bounds check against buffer. + + FormattingHelpers.WriteTwoDecimalDigits((uint)offset.Minutes, buffer, 24); + buffer[23] = Utf8Constants.Colon; + FormattingHelpers.WriteTwoDecimalDigits((uint)offset.Hours, buffer, 21); + buffer[20] = sign; + buffer[19] = Utf8Constants.Space; + } + + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.L.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.L.cs new file mode 100644 index 0000000000..a25737774a --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.L.cs @@ -0,0 +1,66 @@ +// 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. + +namespace System.Buffers.Text +{ + public static partial class Utf8Formatter + { + // Rfc1123 - lowercase + // + // 01234567890123456789012345678 + // ----------------------------- + // tue, 03 jan 2017 08:08:05 gmt + // + private static bool TryFormatDateTimeL(DateTime value, Span buffer, out int bytesWritten) + { + // Writing the check in this fashion elides all bounds checks on 'buffer' + // for the remainder of the method. + if ((uint)28 >= (uint)buffer.Length) + { + bytesWritten = 0; + return false; + } + + var dayAbbrev = DayAbbreviationsLowercase[(int)value.DayOfWeek]; + + buffer[0] = (byte)dayAbbrev; + dayAbbrev >>= 8; + buffer[1] = (byte)dayAbbrev; + dayAbbrev >>= 8; + buffer[2] = (byte)dayAbbrev; + buffer[3] = Utf8Constants.Comma; + buffer[4] = Utf8Constants.Space; + + FormattingHelpers.WriteTwoDecimalDigits((uint)value.Day, buffer, 5); + buffer[7] = Utf8Constants.Space; + + var monthAbbrev = MonthAbbreviationsLowercase[value.Month - 1]; + buffer[8] = (byte)monthAbbrev; + monthAbbrev >>= 8; + buffer[9] = (byte)monthAbbrev; + monthAbbrev >>= 8; + buffer[10] = (byte)monthAbbrev; + buffer[11] = Utf8Constants.Space; + + FormattingHelpers.WriteFourDecimalDigits((uint)value.Year, buffer, 12); + buffer[16] = Utf8Constants.Space; + + FormattingHelpers.WriteTwoDecimalDigits((uint)value.Hour, buffer, 17); + buffer[19] = Utf8Constants.Colon; + + FormattingHelpers.WriteTwoDecimalDigits((uint)value.Minute, buffer, 20); + buffer[22] = Utf8Constants.Colon; + + FormattingHelpers.WriteTwoDecimalDigits((uint)value.Second, buffer, 23); + buffer[25] = Utf8Constants.Space; + + buffer[26] = GMT1Lowercase; + buffer[27] = GMT2Lowercase; + buffer[28] = GMT3Lowercase; + + bytesWritten = 29; + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.O.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.O.cs new file mode 100644 index 0000000000..24e7256268 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.O.cs @@ -0,0 +1,105 @@ +// 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. + +namespace System.Buffers.Text +{ + public static partial class Utf8Formatter + { + // + // Roundtrippable format. One of + // + // 012345678901234567890123456789012 + // --------------------------------- + // 2017-06-12T05:30:45.7680000-07:00 + // 2017-06-12T05:30:45.7680000Z (Z is short for "+00:00" but also distinguishes DateTimeKind.Utc from DateTimeKind.Local) + // 2017-06-12T05:30:45.7680000 (interpreted as local time wrt to current time zone) + // + private static bool TryFormatDateTimeO(DateTime value, TimeSpan offset, Span buffer, out int bytesWritten) + { + const int MinimumBytesNeeded = 27; + + int bytesRequired = MinimumBytesNeeded; + DateTimeKind kind = DateTimeKind.Local; + + if (offset == Utf8Constants.s_nullUtcOffset) + { + kind = value.Kind; + if (kind == DateTimeKind.Local) + { + offset = TimeZoneInfo.Local.GetUtcOffset(value); + bytesRequired += 6; + } + else if (kind == DateTimeKind.Utc) + { + bytesRequired += 1; + } + } + else + { + bytesRequired += 6; + } + + if (buffer.Length < bytesRequired) + { + bytesWritten = 0; + return false; + } + + bytesWritten = bytesRequired; + + // Hoist most of the bounds checks on buffer. + { var unused = buffer[MinimumBytesNeeded - 1]; } + + FormattingHelpers.WriteFourDecimalDigits((uint)value.Year, buffer, 0); + buffer[4] = Utf8Constants.Minus; + + FormattingHelpers.WriteTwoDecimalDigits((uint)value.Month, buffer, 5); + buffer[7] = Utf8Constants.Minus; + + FormattingHelpers.WriteTwoDecimalDigits((uint)value.Day, buffer, 8); + buffer[10] = TimeMarker; + + FormattingHelpers.WriteTwoDecimalDigits((uint)value.Hour, buffer, 11); + buffer[13] = Utf8Constants.Colon; + + FormattingHelpers.WriteTwoDecimalDigits((uint)value.Minute, buffer, 14); + buffer[16] = Utf8Constants.Colon; + + FormattingHelpers.WriteTwoDecimalDigits((uint)value.Second, buffer, 17); + buffer[19] = Utf8Constants.Period; + + FormattingHelpers.WriteDigits((uint)((ulong)value.Ticks % (ulong)TimeSpan.TicksPerSecond), buffer.Slice(20, 7)); + + if (kind == DateTimeKind.Local) + { + byte sign; + + if (offset < default(TimeSpan) /* a "const" version of TimeSpan.Zero */) + { + sign = Utf8Constants.Minus; + offset = TimeSpan.FromTicks(-offset.Ticks); + } + else + { + sign = Utf8Constants.Plus; + } + + // Writing the value backward allows the JIT to optimize by + // performing a single bounds check against buffer. + + FormattingHelpers.WriteTwoDecimalDigits((uint)offset.Minutes, buffer, 31); + buffer[30] = Utf8Constants.Colon; + FormattingHelpers.WriteTwoDecimalDigits((uint)offset.Hours, buffer, 28); + buffer[27] = sign; + + } + else if (kind == DateTimeKind.Utc) + { + buffer[27] = UtcMarker; + } + + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.R.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.R.cs new file mode 100644 index 0000000000..738eb62a28 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.R.cs @@ -0,0 +1,66 @@ +// 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. + +namespace System.Buffers.Text +{ + public static partial class Utf8Formatter + { + // Rfc1123 + // + // 01234567890123456789012345678 + // ----------------------------- + // Tue, 03 Jan 2017 08:08:05 GMT + // + private static bool TryFormatDateTimeR(DateTime value, Span buffer, out int bytesWritten) + { + // Writing the check in this fashion elides all bounds checks on 'buffer' + // for the remainder of the method. + if ((uint)28 >= (uint)buffer.Length) + { + bytesWritten = 0; + return false; + } + + var dayAbbrev = DayAbbreviations[(int)value.DayOfWeek]; + + buffer[0] = (byte)dayAbbrev; + dayAbbrev >>= 8; + buffer[1] = (byte)dayAbbrev; + dayAbbrev >>= 8; + buffer[2] = (byte)dayAbbrev; + buffer[3] = Utf8Constants.Comma; + buffer[4] = Utf8Constants.Space; + + FormattingHelpers.WriteTwoDecimalDigits((uint)value.Day, buffer, 5); + buffer[7] = Utf8Constants.Space; + + var monthAbbrev = MonthAbbreviations[value.Month - 1]; + buffer[8] = (byte)monthAbbrev; + monthAbbrev >>= 8; + buffer[9] = (byte)monthAbbrev; + monthAbbrev >>= 8; + buffer[10] = (byte)monthAbbrev; + buffer[11] = Utf8Constants.Space; + + FormattingHelpers.WriteFourDecimalDigits((uint)value.Year, buffer, 12); + buffer[16] = Utf8Constants.Space; + + FormattingHelpers.WriteTwoDecimalDigits((uint)value.Hour, buffer, 17); + buffer[19] = Utf8Constants.Colon; + + FormattingHelpers.WriteTwoDecimalDigits((uint)value.Minute, buffer, 20); + buffer[22] = Utf8Constants.Colon; + + FormattingHelpers.WriteTwoDecimalDigits((uint)value.Second, buffer, 23); + buffer[25] = Utf8Constants.Space; + + buffer[26] = GMT1; + buffer[27] = GMT2; + buffer[28] = GMT3; + + bytesWritten = 29; + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.cs new file mode 100644 index 0000000000..3e52d0a4fa --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.cs @@ -0,0 +1,171 @@ +// 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. + +namespace System.Buffers.Text +{ + public static partial class Utf8Formatter + { + private const byte TimeMarker = (byte)'T'; + private const byte UtcMarker = (byte)'Z'; + + private const byte GMT1 = (byte)'G'; + private const byte GMT2 = (byte)'M'; + private const byte GMT3 = (byte)'T'; + + private const byte GMT1Lowercase = (byte)'g'; + private const byte GMT2Lowercase = (byte)'m'; + private const byte GMT3Lowercase = (byte)'t'; + + // The three-letter abbreviation is packed into a 24-bit unsigned integer + // where the least significant byte represents the first letter. + private static readonly uint[] DayAbbreviations = new uint[] + { + 'S' + ('u' << 8) + ('n' << 16), + 'M' + ('o' << 8) + ('n' << 16), + 'T' + ('u' << 8) + ('e' << 16), + 'W' + ('e' << 8) + ('d' << 16), + 'T' + ('h' << 8) + ('u' << 16), + 'F' + ('r' << 8) + ('i' << 16), + 'S' + ('a' << 8) + ('t' << 16), + }; + + private static readonly uint[] DayAbbreviationsLowercase = new uint[] + { + 's' + ('u' << 8) + ('n' << 16), + 'm' + ('o' << 8) + ('n' << 16), + 't' + ('u' << 8) + ('e' << 16), + 'w' + ('e' << 8) + ('d' << 16), + 't' + ('h' << 8) + ('u' << 16), + 'f' + ('r' << 8) + ('i' << 16), + 's' + ('a' << 8) + ('t' << 16) + }; + + private static readonly uint[] MonthAbbreviations = new uint[] + { + 'J' + ('a' << 8) + ('n' << 16), + 'F' + ('e' << 8) + ('b' << 16), + 'M' + ('a' << 8) + ('r' << 16), + 'A' + ('p' << 8) + ('r' << 16), + 'M' + ('a' << 8) + ('y' << 16), + 'J' + ('u' << 8) + ('n' << 16), + 'J' + ('u' << 8) + ('l' << 16), + 'A' + ('u' << 8) + ('g' << 16), + 'S' + ('e' << 8) + ('p' << 16), + 'O' + ('c' << 8) + ('t' << 16), + 'N' + ('o' << 8) + ('v' << 16), + 'D' + ('e' << 8) + ('c' << 16), + }; + + private static readonly uint[] MonthAbbreviationsLowercase = new uint[] + { + 'j' + ('a' << 8) + ('n' << 16), + 'f' + ('e' << 8) + ('b' << 16), + 'm' + ('a' << 8) + ('r' << 16), + 'a' + ('p' << 8) + ('r' << 16), + 'm' + ('a' << 8) + ('y' << 16), + 'j' + ('u' << 8) + ('n' << 16), + 'j' + ('u' << 8) + ('l' << 16), + 'a' + ('u' << 8) + ('g' << 16), + 's' + ('e' << 8) + ('p' << 16), + 'o' + ('c' << 8) + ('t' << 16), + 'n' + ('o' << 8) + ('v' << 16), + 'd' + ('e' << 8) + ('c' << 16), + }; + + /// + /// Formats a DateTimeOffset as a UTF8 string. + /// + /// Value to format + /// Buffer to write the UTF8-formatted value to + /// Receives the length of the formatted text in bytes + /// The standard format to use + /// + /// true for success. "bytesWritten" contains the length of the formatted text in bytes. + /// false if buffer was too short. Iteratively increase the size of the buffer and retry until it succeeds. + /// + /// + /// + /// Formats supported: + /// default 05/25/2017 10:30:15 -08:00 + /// G 05/25/2017 10:30:15 + /// R Tue, 03 Jan 2017 08:08:05 GMT (RFC 1123) + /// l tue, 03 jan 2017 08:08:05 gmt (Lowercase RFC 1123) + /// O 2017-06-12T05:30:45.7680000-07:00 (Round-trippable) + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryFormat(DateTimeOffset value, Span buffer, out int bytesWritten, StandardFormat format = default) + { + TimeSpan offset = Utf8Constants.s_nullUtcOffset; + char symbol = format.Symbol; + if (format.IsDefault) + { + symbol = 'G'; + offset = value.Offset; + } + + switch (symbol) + { + case 'R': + return TryFormatDateTimeR(value.UtcDateTime, buffer, out bytesWritten); + + case 'l': + return TryFormatDateTimeL(value.UtcDateTime, buffer, out bytesWritten); + + case 'O': + return TryFormatDateTimeO(value.DateTime, value.Offset, buffer, out bytesWritten); + + case 'G': + return TryFormatDateTimeG(value.DateTime, offset, buffer, out bytesWritten); + + default: + return ThrowHelper.TryFormatThrowFormatException(out bytesWritten); + } + } + + /// + /// Formats a DateTime as a UTF8 string. + /// + /// Value to format + /// Buffer to write the UTF8-formatted value to + /// Receives the length of the formatted text in bytes + /// The standard format to use + /// + /// true for success. "bytesWritten" contains the length of the formatted text in bytes. + /// false if buffer was too short. Iteratively increase the size of the buffer and retry until it succeeds. + /// + /// + /// Formats supported: + /// G (default) 05/25/2017 10:30:15 + /// R Tue, 03 Jan 2017 08:08:05 GMT (RFC 1123) + /// l tue, 03 jan 2017 08:08:05 gmt (Lowercase RFC 1123) + /// O 2017-06-12T05:30:45.7680000-07:00 (Round-trippable) + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryFormat(DateTime value, Span buffer, out int bytesWritten, StandardFormat format = default) + { + char symbol = FormattingHelpers.GetSymbolOrDefault(format, 'G'); + + switch (symbol) + { + case 'R': + return TryFormatDateTimeR(value, buffer, out bytesWritten); + + case 'l': + return TryFormatDateTimeL(value, buffer, out bytesWritten); + + case 'O': + return TryFormatDateTimeO(value, Utf8Constants.s_nullUtcOffset, buffer, out bytesWritten); + + case 'G': + return TryFormatDateTimeG(value, Utf8Constants.s_nullUtcOffset, buffer, out bytesWritten); + + default: + return ThrowHelper.TryFormatThrowFormatException(out bytesWritten); + } + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.E.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.E.cs new file mode 100644 index 0000000000..840e766d49 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.E.cs @@ -0,0 +1,104 @@ +// 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.Diagnostics; + +namespace System.Buffers.Text +{ + public static partial class Utf8Formatter + { + private static bool TryFormatDecimalE(ref NumberBuffer number, Span buffer, out int bytesWritten, byte precision, byte exponentSymbol) + { + const int NumExponentDigits = 3; + + int scale = number.Scale; + ReadOnlySpan digits = number.Digits; + + int numBytesNeeded = + ((number.IsNegative) ? 1 : 0) // minus sign + + 1 // digits before the decimal point (exactly 1) + + ((precision == 0) ? 0 : (precision + 1)) // period and the digits after the decimal point + + 2 // 'E' or 'e' followed by '+' or '-' + + NumExponentDigits; // exponent digits + + if (buffer.Length < numBytesNeeded) + { + bytesWritten = 0; + return false; + } + + int dstIndex = 0; + int srcIndex = 0; + if (number.IsNegative) + { + buffer[dstIndex++] = Utf8Constants.Minus; + } + + // + // Emit exactly one digit before the decimal point. + // + int exponent; + byte firstDigit = digits[srcIndex]; + if (firstDigit == 0) + { + buffer[dstIndex++] = (byte)'0'; // Special case: number before the decimal point is exactly 0: Number does not store the zero in this case. + exponent = 0; + } + else + { + buffer[dstIndex++] = firstDigit; + srcIndex++; + exponent = scale - 1; + } + + if (precision > 0) + { + buffer[dstIndex++] = Utf8Constants.Period; + + // + // Emit digits after the decimal point. + // + int numDigitsEmitted = 0; + while (numDigitsEmitted < precision) + { + byte digit = digits[srcIndex]; + if (digit == 0) + { + while (numDigitsEmitted++ < precision) + { + buffer[dstIndex++] = (byte)'0'; + } + break; + } + buffer[dstIndex++] = digit; + srcIndex++; + numDigitsEmitted++; + } + } + + // Emit the exponent symbol + buffer[dstIndex++] = exponentSymbol; + if (exponent >= 0) + { + buffer[dstIndex++] = Utf8Constants.Plus; + } + else + { + buffer[dstIndex++] = Utf8Constants.Minus; + exponent = -exponent; + } + + Debug.Assert(exponent < Number.DECIMAL_PRECISION, "If you're trying to reuse this routine for double/float, you'll need to review the code carefully for Decimal-specific assumptions."); + + // Emit exactly three digits for the exponent. + buffer[dstIndex++] = (byte)'0'; // The exponent for Decimal can never exceed 28 (let alone 99) + buffer[dstIndex++] = (byte)((exponent / 10) + '0'); + buffer[dstIndex++] = (byte)((exponent % 10) + '0'); + + Debug.Assert(dstIndex == numBytesNeeded); + bytesWritten = numBytesNeeded; + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.F.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.F.cs new file mode 100644 index 0000000000..d26ea4bd3e --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.F.cs @@ -0,0 +1,101 @@ +// 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.Diagnostics; + +namespace System.Buffers.Text +{ + public static partial class Utf8Formatter + { + private static bool TryFormatDecimalF(ref NumberBuffer number, Span buffer, out int bytesWritten, byte precision) + { + int scale = number.Scale; + ReadOnlySpan digits = number.Digits; + + int numBytesNeeded = + ((number.IsNegative) ? 1 : 0) // minus sign + + ((scale <= 0) ? 1 : scale) // digits before the decimal point (minimum 1) + + ((precision == 0) ? 0 : (precision + 1)); // if specified precision != 0, the decimal point and the digits after the decimal point (padded with zeroes if needed) + + if (buffer.Length < numBytesNeeded) + { + bytesWritten = 0; + return false; + } + + int srcIndex = 0; + int dstIndex = 0; + if (number.IsNegative) + { + buffer[dstIndex++] = Utf8Constants.Minus; + } + + // + // Emit digits before the decimal point. + // + if (scale <= 0) + { + buffer[dstIndex++] = (byte)'0'; // The integer portion is 0 and not stored. The formatter, however, needs to emit it. + } + else + { + while (srcIndex < scale) + { + byte digit = digits[srcIndex]; + if (digit == 0) + { + int numTrailingZeroes = scale - srcIndex; + for (int i = 0; i < numTrailingZeroes; i++) + { + buffer[dstIndex++] = (byte)'0'; + } + break; + } + + buffer[dstIndex++] = digit; + srcIndex++; + } + } + + if (precision > 0) + { + buffer[dstIndex++] = Utf8Constants.Period; + + // + // Emit digits after the decimal point. + // + int numDigitsEmitted = 0; + if (scale < 0) + { + int numLeadingZeroesToEmit = Math.Min((int)precision, -scale); + for (int i = 0; i < numLeadingZeroesToEmit; i++) + { + buffer[dstIndex++] = (byte)'0'; + } + numDigitsEmitted += numLeadingZeroesToEmit; + } + + while (numDigitsEmitted < precision) + { + byte digit = digits[srcIndex]; + if (digit == 0) + { + while (numDigitsEmitted++ < precision) + { + buffer[dstIndex++] = (byte)'0'; + } + break; + } + buffer[dstIndex++] = digit; + srcIndex++; + numDigitsEmitted++; + } + } + + Debug.Assert(dstIndex == numBytesNeeded); + bytesWritten = numBytesNeeded; + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.G.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.G.cs new file mode 100644 index 0000000000..213d319800 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.G.cs @@ -0,0 +1,108 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; + +namespace System.Buffers.Text +{ + public static partial class Utf8Formatter + { + private static bool TryFormatDecimalG(ref NumberBuffer number, Span buffer, out int bytesWritten) + { + int scale = number.Scale; + ReadOnlySpan digits = number.Digits; + int numDigits = number.NumDigits; + + bool isFraction = scale < numDigits; + int numBytesNeeded; + if (isFraction) + { + numBytesNeeded = numDigits + 1; // A fraction. Must include one for the decimal point. + if (scale <= 0) + { + numBytesNeeded += 1 + (-scale); // A fraction of the form 0.ddd. Need to emit the non-stored 0 before the decimal point plus (-scale) leading 0's after the decimal point. + } + } + else + { + numBytesNeeded = ((scale <= 0) ? 1 : scale); // An integral. Just emit the digits before the decimal point (minimum 1) and no decimal point. + } + + if (number.IsNegative) + { + numBytesNeeded++; // And the minus sign. + } + + if (buffer.Length < numBytesNeeded) + { + bytesWritten = 0; + return false; + } + + int srcIndex = 0; + int dstIndex = 0; + + if (number.IsNegative) + { + buffer[dstIndex++] = Utf8Constants.Minus; + } + + // + // Emit digits before the decimal point. + // + if (scale <= 0) + { + buffer[dstIndex++] = (byte)'0'; // The integer portion is 0 and not stored. The formatter, however, needs to emit it. + } + else + { + while (srcIndex < scale) + { + byte digit = digits[srcIndex]; + if (digit == 0) + { + int numTrailingZeroes = scale - srcIndex; + for (int i = 0; i < numTrailingZeroes; i++) + { + buffer[dstIndex++] = (byte)'0'; + } + break; + } + + buffer[dstIndex++] = digit; + srcIndex++; + } + } + + if (isFraction) + { + buffer[dstIndex++] = Utf8Constants.Period; + + // + // Emit digits after the decimal point. + // + if (scale < 0) + { + int numLeadingZeroesToEmit = -scale; + for (int i = 0; i < numLeadingZeroesToEmit; i++) + { + buffer[dstIndex++] = (byte)'0'; + } + } + + byte digit; + while ((digit = digits[srcIndex++]) != 0) + { + buffer[dstIndex++] = digit; + } + } + + Debug.Assert(dstIndex == numBytesNeeded); + + bytesWritten = numBytesNeeded; + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.cs new file mode 100644 index 0000000000..af292ae952 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.cs @@ -0,0 +1,112 @@ +// 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.Diagnostics; + +namespace System.Buffers.Text +{ + public static partial class Utf8Formatter + { + /// + /// Formats a Decimal as a UTF8 string. + /// + /// Value to format + /// Buffer to write the UTF8-formatted value to + /// Receives the length of the formatted text in bytes + /// The standard format to use + /// + /// true for success. "bytesWritten" contains the length of the formatted text in bytes. + /// false if buffer was too short. Iteratively increase the size of the buffer and retry until it succeeds. + /// + /// + /// Formats supported: + /// G/g (default) + /// F/f 12.45 Fixed point + /// E/e 1.245000e1 Exponential + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryFormat(decimal value, Span buffer, out int bytesWritten, StandardFormat format = default) + { + if (format.IsDefault) + { + format = 'G'; + } + + switch (format.Symbol) + { + case 'g': + case 'G': + { + if (format.Precision != StandardFormat.NoPrecision) + throw new NotSupportedException(SR.Argument_GWithPrecisionNotSupported); + NumberBuffer number = default; + Number.DecimalToNumber(value, ref number); + if (number.Digits[0] == 0) + { + number.IsNegative = false; // For Decimals, -0 must print as normal 0. + } + bool success = TryFormatDecimalG(ref number, buffer, out bytesWritten); +#if DEBUG + // This DEBUG segment exists to close a code coverage hole inside TryFormatDecimalG(). Because we don't call RoundNumber() on this path, we have no way to feed + // TryFormatDecimalG() a number where trailing zeros before the decimal point have been cropped. So if the chance comes up, we'll crop the zeroes + // ourselves and make a second call to ensure we get the same outcome. + if (success) + { + Span digits = number.Digits; + int numDigits = number.NumDigits; + if (numDigits != 0 && number.Scale == numDigits && digits[numDigits - 1] == '0') + { + while (numDigits != 0 && digits[numDigits - 1] == '0') + { + digits[numDigits - 1] = 0; + numDigits--; + } + + number.CheckConsistency(); + + byte[] buffer2 = new byte[buffer.Length]; + bool success2 = TryFormatDecimalG(ref number, buffer2, out int bytesWritten2); + Debug.Assert(success2); + Debug.Assert(bytesWritten2 == bytesWritten); + for (int i = 0; i < bytesWritten; i++) + { + Debug.Assert(buffer[i] == buffer2[i]); + } + } + + } +#endif // DEBUG + return success; + } + + case 'f': + case 'F': + { + NumberBuffer number = default; + Number.DecimalToNumber(value, ref number); + byte precision = (format.Precision == StandardFormat.NoPrecision) ? (byte)2 : format.Precision; + Number.RoundNumber(ref number, number.Scale + precision); + Debug.Assert(!(number.Digits[0] == 0 && number.IsNegative)); // For Decimals, -0 must print as normal 0. As it happens, Number.RoundNumber already ensures this invariant. + return TryFormatDecimalF(ref number, buffer, out bytesWritten, precision); + } + + case 'e': + case 'E': + { + NumberBuffer number = default; + Number.DecimalToNumber(value, ref number); + byte precision = (format.Precision == StandardFormat.NoPrecision) ? (byte)6 : format.Precision; + Number.RoundNumber(ref number, precision + 1); + Debug.Assert(!(number.Digits[0] == 0 && number.IsNegative)); // For Decimals, -0 must print as normal 0. As it happens, Number.RoundNumber already ensures this invariant. + return TryFormatDecimalE(ref number, buffer, out bytesWritten, precision, exponentSymbol: (byte)format.Symbol); + } + + default: + return ThrowHelper.TryFormatThrowFormatException(out bytesWritten); + } + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Float.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Float.cs new file mode 100644 index 0000000000..3354c572ae --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Float.cs @@ -0,0 +1,112 @@ +// 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.Diagnostics; +using System.Globalization; + +namespace System.Buffers.Text +{ + public static partial class Utf8Formatter + { + /// + /// Formats a Double as a UTF8 string. + /// + /// Value to format + /// Buffer to write the UTF8-formatted value to + /// Receives the length of the formatted text in bytes + /// The standard format to use + /// + /// true for success. "bytesWritten" contains the length of the formatted text in bytes. + /// false if buffer was too short. Iteratively increase the size of the buffer and retry until it succeeds. + /// + /// + /// Formats supported: + /// G/g (default) + /// F/f 12.45 Fixed point + /// E/e 1.245000e1 Exponential + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryFormat(double value, Span buffer, out int bytesWritten, StandardFormat format = default) + { + return TryFormatFloatingPoint(value, buffer, out bytesWritten, format); + } + + /// + /// Formats a Single as a UTF8 string. + /// + /// Value to format + /// Buffer to write the UTF8-formatted value to + /// Receives the length of the formatted text in bytes + /// The standard format to use + /// + /// true for success. "bytesWritten" contains the length of the formatted text in bytes. + /// false if buffer was too short. Iteratively increase the size of the buffer and retry until it succeeds. + /// + /// + /// Formats supported: + /// G/g (default) + /// F/f 12.45 Fixed point + /// E/e 1.245000e1 Exponential + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryFormat(float value, Span buffer, out int bytesWritten, StandardFormat format = default) + { + return TryFormatFloatingPoint(value, buffer, out bytesWritten, format); + } + + // + // Common handler for TryFormat(Double) and TryFormat(Single). You may notice that this particular routine isn't getting into the "no allocation" spirit + // of things. The DoubleToNumber() code is incredibly complex and is one of the few pieces of Number formatting never C#-ized. It would be really + // be preferable not to have another version of that lying around. Until we really hit a scenario where floating point formatting needs the perf, we'll + // make do with this. + // + private static bool TryFormatFloatingPoint(T value, Span buffer, out int bytesWritten, StandardFormat format) where T : IFormattable + { + if (format.IsDefault) + { + format = 'G'; + } + + switch (format.Symbol) + { + case 'g': + case 'G': + if (format.Precision != StandardFormat.NoPrecision) + throw new NotSupportedException(SR.Argument_GWithPrecisionNotSupported); + break; + + case 'f': + case 'F': + case 'e': + case 'E': + break; + + default: + return ThrowHelper.TryFormatThrowFormatException(out bytesWritten); + } + + string formatString = format.ToString(); + string utf16Text = value.ToString(formatString, CultureInfo.InvariantCulture); + int length = utf16Text.Length; + if (length > buffer.Length) + { + bytesWritten = 0; + return false; + } + + for (int i = 0; i < length; i++) + { + Debug.Assert(utf16Text[i] < 128, "A culture-invariant ToString() of a floating point expected to produce ASCII characters only."); + buffer[i] = (byte)(utf16Text[i]); + } + + bytesWritten = length; + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs new file mode 100644 index 0000000000..009f8c66b5 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.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. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +namespace System.Buffers.Text +{ + public static partial class Utf8Formatter + { + #region Constants + + private const byte OpenBrace = (byte)'{'; + private const byte CloseBrace = (byte)'}'; + + private const byte OpenParen = (byte)'('; + private const byte CloseParen = (byte)')'; + + private const byte Dash = (byte)'-'; + + #endregion Constants + + /// + /// Formats a Guid as a UTF8 string. + /// + /// Value to format + /// Buffer to write the UTF8-formatted value to + /// Receives the length of the formatted text in bytes + /// The standard format to use + /// + /// true for success. "bytesWritten" contains the length of the formatted text in bytes. + /// false if buffer was too short. Iteratively increase the size of the buffer and retry until it succeeds. + /// + /// + /// Formats supported: + /// D (default) nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn + /// B {nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn} + /// P (nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn) + /// N nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryFormat(Guid value, Span buffer, out int bytesWritten, StandardFormat format = default) + { + const int INSERT_DASHES = unchecked((int)0x80000000); + const int NO_DASHES = 0; + const int INSERT_CURLY_BRACES = (CloseBrace << 16) | (OpenBrace << 8); + const int INSERT_ROUND_BRACES = (CloseParen << 16) | (OpenParen << 8); + const int NO_BRACES = 0; + const int LEN_GUID_BASE = 32; + const int LEN_ADD_DASHES = 4; + const int LEN_ADD_BRACES = 2; + + // This is a 32-bit value whose contents (where 0 is the low byte) are: + // 0th byte: minimum required length of the output buffer, + // 1st byte: the ASCII byte to insert for the opening brace position (or 0 if no braces), + // 2nd byte: the ASCII byte to insert for the closing brace position (or 0 if no braces), + // 3rd byte: high bit set if dashes are to be inserted. + // + // The reason for keeping a single flag instead of separate vars is that we can avoid register spillage + // as we build up the output value. + int flags; + + switch (FormattingHelpers.GetSymbolOrDefault(format, 'D')) + { + case 'D': // nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn + flags = INSERT_DASHES + NO_BRACES + LEN_GUID_BASE + LEN_ADD_DASHES; + break; + + case 'B': // {nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn} + flags = INSERT_DASHES + INSERT_CURLY_BRACES + LEN_GUID_BASE + LEN_ADD_DASHES + LEN_ADD_BRACES; + break; + + case 'P': // (nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn) + flags = INSERT_DASHES + INSERT_ROUND_BRACES + LEN_GUID_BASE + LEN_ADD_DASHES + LEN_ADD_BRACES; + break; + + case 'N': // nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn + flags = NO_BRACES + NO_DASHES + LEN_GUID_BASE; + break; + + default: + return ThrowHelper.TryFormatThrowFormatException(out bytesWritten); + } + + // At this point, the low byte of flags contains the minimum required length + + if ((byte)flags > buffer.Length) + { + bytesWritten = 0; + return false; + } + + bytesWritten = (byte)flags; + flags >>= 8; + + // At this point, the low byte of flags contains the opening brace char (if any) + + if ((byte)flags != 0) + { + buffer[0] = (byte)flags; + buffer = buffer.Slice(1); + } + flags >>= 8; + + // At this point, the low byte of flags contains the closing brace char (if any) + // And since we're performing arithmetic shifting the high bit of flags is set (flags is negative) if dashes are required + + DecomposedGuid guidAsBytes = default; + guidAsBytes.Guid = value; + + // When a GUID is blitted, the first three components are little-endian, and the last component is big-endian. + + // The line below forces the JIT to hoist the bounds check for the following segment. + // The JIT will optimize away the read, but it cannot optimize away the bounds check + // because it may have an observeable side effect (throwing). + // We use 8 instead of 7 so that we also capture the dash if we're asked to insert one. + + { var unused = buffer[8]; } + FormattingHelpers.WriteHexByte(guidAsBytes.Byte03, buffer, 0, FormattingHelpers.HexCasing.Lowercase); + FormattingHelpers.WriteHexByte(guidAsBytes.Byte02, buffer, 2, FormattingHelpers.HexCasing.Lowercase); + FormattingHelpers.WriteHexByte(guidAsBytes.Byte01, buffer, 4, FormattingHelpers.HexCasing.Lowercase); + FormattingHelpers.WriteHexByte(guidAsBytes.Byte00, buffer, 6, FormattingHelpers.HexCasing.Lowercase); + + if (flags < 0 /* use dash? */) + { + buffer[8] = Dash; + buffer = buffer.Slice(9); + } + else + { + buffer = buffer.Slice(8); + } + + { var unused = buffer[4]; } + FormattingHelpers.WriteHexByte(guidAsBytes.Byte05, buffer, 0, FormattingHelpers.HexCasing.Lowercase); + FormattingHelpers.WriteHexByte(guidAsBytes.Byte04, buffer, 2, FormattingHelpers.HexCasing.Lowercase); + + if (flags < 0 /* use dash? */) + { + buffer[4] = Dash; + buffer = buffer.Slice(5); + } + else + { + buffer = buffer.Slice(4); + } + + { var unused = buffer[4]; } + FormattingHelpers.WriteHexByte(guidAsBytes.Byte07, buffer, 0, FormattingHelpers.HexCasing.Lowercase); + FormattingHelpers.WriteHexByte(guidAsBytes.Byte06, buffer, 2, FormattingHelpers.HexCasing.Lowercase); + + if (flags < 0 /* use dash? */) + { + buffer[4] = Dash; + buffer = buffer.Slice(5); + } + else + { + buffer = buffer.Slice(4); + } + + { var unused = buffer[4]; } + FormattingHelpers.WriteHexByte(guidAsBytes.Byte08, buffer, 0, FormattingHelpers.HexCasing.Lowercase); + FormattingHelpers.WriteHexByte(guidAsBytes.Byte09, buffer, 2, FormattingHelpers.HexCasing.Lowercase); + + if (flags < 0 /* use dash? */) + { + buffer[4] = Dash; + buffer = buffer.Slice(5); + } + else + { + buffer = buffer.Slice(4); + } + + { var unused = buffer[11]; } // can't hoist bounds check on the final brace (if exists) + FormattingHelpers.WriteHexByte(guidAsBytes.Byte10, buffer, 0, FormattingHelpers.HexCasing.Lowercase); + FormattingHelpers.WriteHexByte(guidAsBytes.Byte11, buffer, 2, FormattingHelpers.HexCasing.Lowercase); + FormattingHelpers.WriteHexByte(guidAsBytes.Byte12, buffer, 4, FormattingHelpers.HexCasing.Lowercase); + FormattingHelpers.WriteHexByte(guidAsBytes.Byte13, buffer, 6, FormattingHelpers.HexCasing.Lowercase); + FormattingHelpers.WriteHexByte(guidAsBytes.Byte14, buffer, 8, FormattingHelpers.HexCasing.Lowercase); + FormattingHelpers.WriteHexByte(guidAsBytes.Byte15, buffer, 10, FormattingHelpers.HexCasing.Lowercase); + + if ((byte)flags != 0) + { + buffer[12] = (byte)flags; + } + + return true; + } + + /// + /// Used to provide access to the individual bytes of a GUID. + /// + [StructLayout(LayoutKind.Explicit)] + private struct DecomposedGuid + { + [FieldOffset(00)] public Guid Guid; + [FieldOffset(00)] public byte Byte00; + [FieldOffset(01)] public byte Byte01; + [FieldOffset(02)] public byte Byte02; + [FieldOffset(03)] public byte Byte03; + [FieldOffset(04)] public byte Byte04; + [FieldOffset(05)] public byte Byte05; + [FieldOffset(06)] public byte Byte06; + [FieldOffset(07)] public byte Byte07; + [FieldOffset(08)] public byte Byte08; + [FieldOffset(09)] public byte Byte09; + [FieldOffset(10)] public byte Byte10; + [FieldOffset(11)] public byte Byte11; + [FieldOffset(12)] public byte Byte12; + [FieldOffset(13)] public byte Byte13; + [FieldOffset(14)] public byte Byte14; + [FieldOffset(15)] public byte Byte15; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.D.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.D.cs new file mode 100644 index 0000000000..b4d772a1ff --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.D.cs @@ -0,0 +1,55 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; + +#if !netstandard +using Internal.Runtime.CompilerServices; +#endif + +namespace System.Buffers.Text +{ + /// + /// Methods to format common data types as Utf8 strings. + /// + public static partial class Utf8Formatter + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool TryFormatInt64D(long value, byte precision, Span buffer, out int bytesWritten) + { + bool insertNegationSign = false; + if (value < 0) + { + insertNegationSign = true; + value = -value; + if (value < 0) + { + Debug.Assert(value == Int64.MinValue); + return TryFormatInt64D_MinValue(precision, buffer, out bytesWritten); + } + } + + return TryFormatUInt64D((ulong)value, precision, buffer, insertNegationSign, out bytesWritten); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool TryFormatInt64D_MinValue(byte precision, Span buffer, out int bytesWritten) + { + // Int64.MinValue must be treated specially since its two's complement negation doesn't fit into 64 bits. + // Instead, we'll perform one's complement negation and fix up the +1 later (-x := ~x + 1). + // Int64.MinValue = -9,223,372,036,854,775,808 + // Int64.MaxValue = 9,223,372,036,854,775,807 + + bool retVal = TryFormatUInt64D((ulong)Int64.MaxValue, precision, buffer, insertNegationSign: true, out int tempBytesWritten); + if (retVal) + { + buffer[tempBytesWritten - 1]++; // bump the last ASCII '7' to an '8' + } + + bytesWritten = tempBytesWritten; + return retVal; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.Default.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.Default.cs new file mode 100644 index 0000000000..4555c70078 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.Default.cs @@ -0,0 +1,159 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; + +namespace System.Buffers.Text +{ + /// + /// Methods to format common data types as Utf8 strings. + /// + public static partial class Utf8Formatter + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool TryFormatInt64Default(long value, Span buffer, out int bytesWritten) + { + if ((ulong)value < 10) + { + return TryFormatUInt32SingleDigit((uint)value, buffer, out bytesWritten); + } + + if (IntPtr.Size == 8) // x64 + { + return TryFormatInt64MultipleDigits(value, buffer, out bytesWritten); + } + else // x86 + { + if (value <= int.MaxValue && value >= int.MinValue) + { + return TryFormatInt32MultipleDigits((int)value, buffer, out bytesWritten); + } + else + { + if (value <= (long)Utf8Constants.BillionMaxUIntValue && value >= -(long)Utf8Constants.BillionMaxUIntValue) + { + return value < 0 ? + TryFormatInt64MoreThanNegativeBillionMaxUInt(-value, buffer, out bytesWritten) : + TryFormatUInt64LessThanBillionMaxUInt((ulong)value, buffer, out bytesWritten); + } + else + { + return value < 0 ? + TryFormatInt64LessThanNegativeBillionMaxUInt(-value, buffer, out bytesWritten) : + TryFormatUInt64MoreThanBillionMaxUInt((ulong)value, buffer, out bytesWritten); + } + } + } + } + + // TODO: Use this instead of TryFormatInt64Default to format numbers less than int.MaxValue + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool TryFormatInt32Default(int value, Span buffer, out int bytesWritten) + { + if ((uint)value < 10) + { + return TryFormatUInt32SingleDigit((uint)value, buffer, out bytesWritten); + } + return TryFormatInt32MultipleDigits(value, buffer, out bytesWritten); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool TryFormatInt32MultipleDigits(int value, Span buffer, out int bytesWritten) + { + if (value < 0) + { + value = -value; + int digitCount = FormattingHelpers.CountDigits((uint)value); + // WriteDigits does not do bounds checks + if (digitCount >= buffer.Length) + { + bytesWritten = 0; + return false; + } + buffer[0] = Utf8Constants.Minus; + bytesWritten = digitCount + 1; + FormattingHelpers.WriteDigits((uint)value, buffer.Slice(1, digitCount)); + return true; + } + else + { + return TryFormatUInt32MultipleDigits((uint)value, buffer, out bytesWritten); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool TryFormatInt64MultipleDigits(long value, Span buffer, out int bytesWritten) + { + if (value < 0) + { + value = -value; + int digitCount = FormattingHelpers.CountDigits((ulong)value); + // WriteDigits does not do bounds checks + if (digitCount >= buffer.Length) + { + bytesWritten = 0; + return false; + } + buffer[0] = Utf8Constants.Minus; + bytesWritten = digitCount + 1; + FormattingHelpers.WriteDigits((ulong)value, buffer.Slice(1, digitCount)); + return true; + } + else + { + return TryFormatUInt64MultipleDigits((ulong)value, buffer, out bytesWritten); + } + } + + // Split long into two parts that can each fit in a uint - {1-10 digits}{9 digits} + private static bool TryFormatInt64MoreThanNegativeBillionMaxUInt(long value, Span buffer, out int bytesWritten) + { + uint overNineDigits = (uint)(value / Utf8Constants.Billion); + uint lastNineDigits = (uint)(value - (overNineDigits * Utf8Constants.Billion)); + + int digitCountOverNineDigits = FormattingHelpers.CountDigits(overNineDigits); + Debug.Assert(digitCountOverNineDigits >= 1 && digitCountOverNineDigits <= 10); + int digitCount = digitCountOverNineDigits + 9; + // WriteDigits does not do bounds checks + if (digitCount >= buffer.Length) + { + bytesWritten = 0; + return false; + } + buffer[0] = Utf8Constants.Minus; + bytesWritten = digitCount + 1; + FormattingHelpers.WriteDigits(overNineDigits, buffer.Slice(1, digitCountOverNineDigits)); + FormattingHelpers.WriteDigits(lastNineDigits, buffer.Slice(digitCountOverNineDigits + 1, 9)); + return true; + } + + // Split long into three parts that can each fit in a uint - {1 digit}{9 digits}{9 digits} + private static bool TryFormatInt64LessThanNegativeBillionMaxUInt(long value, Span buffer, out int bytesWritten) + { + // value can still be negative if value == long.MinValue + // Therefore, cast to ulong, since (ulong)value actually equals abs(long.MinValue) + ulong overNineDigits = (ulong)value / Utf8Constants.Billion; + uint lastNineDigits = (uint)((ulong)value - (overNineDigits * Utf8Constants.Billion)); + uint overEighteenDigits = (uint)(overNineDigits / Utf8Constants.Billion); + uint middleNineDigits = (uint)(overNineDigits - (overEighteenDigits * Utf8Constants.Billion)); + + int digitCountOverEighteenDigits = FormattingHelpers.CountDigits(overEighteenDigits); + Debug.Assert(digitCountOverEighteenDigits == 1); + int digitCount = digitCountOverEighteenDigits + 18; + // WriteDigits does not do bounds checks + if (digitCount >= buffer.Length) + { + bytesWritten = 0; + return false; + } + buffer[0] = Utf8Constants.Minus; + bytesWritten = digitCount + 1; + FormattingHelpers.WriteDigits(overEighteenDigits, buffer.Slice(1, digitCountOverEighteenDigits)); + FormattingHelpers.WriteDigits(middleNineDigits, buffer.Slice(digitCountOverEighteenDigits + 1, 9)); + FormattingHelpers.WriteDigits(lastNineDigits, buffer.Slice(digitCountOverEighteenDigits + 1 + 9, 9)); + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.N.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.N.cs new file mode 100644 index 0000000000..2fcd472715 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.N.cs @@ -0,0 +1,55 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; + +#if !netstandard +using Internal.Runtime.CompilerServices; +#endif + +namespace System.Buffers.Text +{ + /// + /// Methods to format common data types as Utf8 strings. + /// + public static partial class Utf8Formatter + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool TryFormatInt64N(long value, byte precision, Span buffer, out int bytesWritten) + { + bool insertNegationSign = false; + if (value < 0) + { + insertNegationSign = true; + value = -value; + if (value < 0) + { + Debug.Assert(value == Int64.MinValue); + return TryFormatInt64N_MinValue(precision, buffer, out bytesWritten); + } + } + + return TryFormatUInt64N((ulong)value, precision, buffer, insertNegationSign, out bytesWritten); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool TryFormatInt64N_MinValue(byte precision, Span buffer, out int bytesWritten) + { + // Int64.MinValue must be treated specially since its two's complement negation doesn't fit into 64 bits. + // Instead, we'll perform one's complement negation and fix up the +1 later (-x := ~x + 1). + // Int64.MinValue = -9,223,372,036,854,775,808 (26 digits, including minus and commas) + // Int64.MaxValue = 9,223,372,036,854,775,807 + + bool retVal = TryFormatUInt64N((ulong)Int64.MaxValue, precision, buffer, insertNegationSign: true, out int tempBytesWritten); + if (retVal) + { + buffer[25]++; // bump the last ASCII '7' to an '8' + } + + bytesWritten = tempBytesWritten; + return retVal; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.cs new file mode 100644 index 0000000000..2ef45f8a0c --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.cs @@ -0,0 +1,52 @@ +// 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.Runtime.CompilerServices; + +namespace System.Buffers.Text +{ + /// + /// Methods to format common data types as Utf8 strings. + /// + public static partial class Utf8Formatter + { + // + // Common worker for all signed integer TryFormat overloads + // + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool TryFormatInt64(long value, ulong mask, Span buffer, out int bytesWritten, StandardFormat format) + { + if (format.IsDefault) + { + return TryFormatInt64Default(value, buffer, out bytesWritten); + } + + switch (format.Symbol) + { + case 'G': + case 'g': + if (format.HasPrecision) + throw new NotSupportedException(SR.Argument_GWithPrecisionNotSupported); // With a precision, 'G' can produce exponential format, even for integers. + return TryFormatInt64D(value, format.Precision, buffer, out bytesWritten); + + case 'd': + case 'D': + return TryFormatInt64D(value, format.Precision, buffer, out bytesWritten); + + case 'n': + case 'N': + return TryFormatInt64N(value, format.Precision, buffer, out bytesWritten); + + case 'x': + return TryFormatUInt64X((ulong)value & mask, format.Precision, true, buffer, out bytesWritten); + + case 'X': + return TryFormatUInt64X((ulong)value & mask, format.Precision, false, buffer, out bytesWritten); + + default: + return ThrowHelper.TryFormatThrowFormatException(out bytesWritten); + } + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.D.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.D.cs new file mode 100644 index 0000000000..5fef90e11b --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.D.cs @@ -0,0 +1,54 @@ +// 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. + +namespace System.Buffers.Text +{ + /// + /// Methods to format common data types as Utf8 strings. + /// + public static partial class Utf8Formatter + { + private static bool TryFormatUInt64D(ulong value, byte precision, Span buffer, bool insertNegationSign, out int bytesWritten) + { + // Calculate the actual digit count and the number of padding zeroes requested. + // From all of this we can get the required buffer length. + + int digitCount = FormattingHelpers.CountDigits(value); + int leadingZeroCount = ((precision == StandardFormat.NoPrecision) ? 0 : (int)precision) - digitCount; + if (leadingZeroCount < 0) + { + leadingZeroCount = 0; + } + + int requiredBufferLength = digitCount + leadingZeroCount; + + if (insertNegationSign) + { + requiredBufferLength++; + } + + if (requiredBufferLength > buffer.Length) + { + bytesWritten = 0; + return false; + } + + bytesWritten = requiredBufferLength; + + if (insertNegationSign) + { + buffer[0] = Utf8Constants.Minus; + buffer = buffer.Slice(1); + } + + if (leadingZeroCount > 0) + { + FormattingHelpers.FillWithAsciiZeros(buffer.Slice(0, leadingZeroCount)); + } + FormattingHelpers.WriteDigits(value, buffer.Slice(leadingZeroCount, digitCount)); + + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.Default.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.Default.cs new file mode 100644 index 0000000000..237cf1eae5 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.Default.cs @@ -0,0 +1,159 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; + +namespace System.Buffers.Text +{ + /// + /// Methods to format common data types as Utf8 strings. + /// + public static partial class Utf8Formatter + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool TryFormatUInt64Default(ulong value, Span buffer, out int bytesWritten) + { + if (value < 10) + { + return TryFormatUInt32SingleDigit((uint)value, buffer, out bytesWritten); + } + + if (IntPtr.Size == 8) // x64 + { + return TryFormatUInt64MultipleDigits(value, buffer, out bytesWritten); + } + else // x86 + { + if (value <= uint.MaxValue) + { + return TryFormatUInt32MultipleDigits((uint)value, buffer, out bytesWritten); + } + else + { + if (value <= Utf8Constants.BillionMaxUIntValue) + { + return TryFormatUInt64LessThanBillionMaxUInt(value, buffer, out bytesWritten); + } + else + { + return TryFormatUInt64MoreThanBillionMaxUInt(value, buffer, out bytesWritten); + } + } + } + } + + // TODO: Use this instead of TryFormatUInt64Default to format numbers less than uint.MaxValue + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool TryFormatUInt32Default(uint value, Span buffer, out int bytesWritten) + { + if (value < 10) + { + return TryFormatUInt32SingleDigit(value, buffer, out bytesWritten); + } + return TryFormatUInt32MultipleDigits(value, buffer, out bytesWritten); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool TryFormatUInt32SingleDigit(uint value, Span buffer, out int bytesWritten) + { + if (buffer.Length == 0) + { + bytesWritten = 0; + return false; + } + buffer[0] = (byte)('0' + value); + bytesWritten = 1; + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool TryFormatUInt32MultipleDigits(uint value, Span buffer, out int bytesWritten) + { + int digitCount = FormattingHelpers.CountDigits(value); + // WriteDigits does not do bounds checks + if (digitCount > buffer.Length) + { + bytesWritten = 0; + return false; + } + bytesWritten = digitCount; + FormattingHelpers.WriteDigits(value, buffer.Slice(0, digitCount)); + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool TryFormatUInt64SingleDigit(ulong value, Span buffer, out int bytesWritten) + { + if (buffer.Length == 0) + { + bytesWritten = 0; + return false; + } + buffer[0] = (byte)('0' + value); + bytesWritten = 1; + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool TryFormatUInt64MultipleDigits(ulong value, Span buffer, out int bytesWritten) + { + int digitCount = FormattingHelpers.CountDigits(value); + // WriteDigits does not do bounds checks + if (digitCount > buffer.Length) + { + bytesWritten = 0; + return false; + } + bytesWritten = digitCount; + FormattingHelpers.WriteDigits(value, buffer.Slice(0, digitCount)); + return true; + } + + // Split ulong into two parts that can each fit in a uint - {1-10 digits}{9 digits} + private static bool TryFormatUInt64LessThanBillionMaxUInt(ulong value, Span buffer, out int bytesWritten) + { + uint overNineDigits = (uint)(value / Utf8Constants.Billion); + uint lastNineDigits = (uint)(value - (overNineDigits * Utf8Constants.Billion)); + + int digitCountOverNineDigits = FormattingHelpers.CountDigits(overNineDigits); + Debug.Assert(digitCountOverNineDigits >= 1 && digitCountOverNineDigits <= 10); + int digitCount = digitCountOverNineDigits + 9; + // WriteDigits does not do bounds checks + if (digitCount > buffer.Length) + { + bytesWritten = 0; + return false; + } + bytesWritten = digitCount; + FormattingHelpers.WriteDigits(overNineDigits, buffer.Slice(0, digitCountOverNineDigits)); + FormattingHelpers.WriteDigits(lastNineDigits, buffer.Slice(digitCountOverNineDigits, 9)); + return true; + } + + // Split ulong into three parts that can each fit in a uint - {1-2 digits}{9 digits}{9 digits} + private static bool TryFormatUInt64MoreThanBillionMaxUInt(ulong value, Span buffer, out int bytesWritten) + { + ulong overNineDigits = value / Utf8Constants.Billion; + uint lastNineDigits = (uint)(value - (overNineDigits * Utf8Constants.Billion)); + uint overEighteenDigits = (uint)(overNineDigits / Utf8Constants.Billion); + uint middleNineDigits = (uint)(overNineDigits - (overEighteenDigits * Utf8Constants.Billion)); + + int digitCountOverEighteenDigits = FormattingHelpers.CountDigits(overEighteenDigits); + Debug.Assert(digitCountOverEighteenDigits >= 1 && digitCountOverEighteenDigits <= 2); + int digitCount = digitCountOverEighteenDigits + 18; + // WriteDigits does not do bounds checks + if (digitCount > buffer.Length) + { + bytesWritten = 0; + return false; + } + bytesWritten = digitCount; + FormattingHelpers.WriteDigits(overEighteenDigits, buffer.Slice(0, digitCountOverEighteenDigits)); + FormattingHelpers.WriteDigits(middleNineDigits, buffer.Slice(digitCountOverEighteenDigits, 9)); + FormattingHelpers.WriteDigits(lastNineDigits, buffer.Slice(digitCountOverEighteenDigits + 9, 9)); + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.N.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.N.cs new file mode 100644 index 0000000000..e3b2b7a05e --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.N.cs @@ -0,0 +1,58 @@ +// 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. + +namespace System.Buffers.Text +{ + /// + /// Methods to format common data types as Utf8 strings. + /// + public static partial class Utf8Formatter + { + private static bool TryFormatUInt64N(ulong value, byte precision, Span buffer, bool insertNegationSign, out int bytesWritten) + { + // Calculate the actual digit count, number of group separators required, and the + // number of trailing zeros requested. From all of this we can get the required + // buffer length. + + int digitCount = FormattingHelpers.CountDigits(value); + int commaCount = (digitCount - 1) / 3; + int trailingZeroCount = (precision == StandardFormat.NoPrecision) ? 2 /* default for 'N' */ : precision; + + int requiredBufferLength = digitCount + commaCount; + if (trailingZeroCount > 0) + { + requiredBufferLength += trailingZeroCount + 1; + } + + if (insertNegationSign) + { + requiredBufferLength++; + } + + if (requiredBufferLength > buffer.Length) + { + bytesWritten = 0; + return false; + } + + bytesWritten = requiredBufferLength; + + if (insertNegationSign) + { + buffer[0] = Utf8Constants.Minus; + buffer = buffer.Slice(1); + } + + FormattingHelpers.WriteDigitsWithGroupSeparator(value, buffer.Slice(0, digitCount + commaCount)); + + if (trailingZeroCount > 0) + { + buffer[digitCount + commaCount] = Utf8Constants.Period; + FormattingHelpers.FillWithAsciiZeros(buffer.Slice(digitCount + commaCount + 1, trailingZeroCount)); + } + + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.X.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.X.cs new file mode 100644 index 0000000000..5a8209f8b1 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.X.cs @@ -0,0 +1,46 @@ +// 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. + +namespace System.Buffers.Text +{ + /// + /// Methods to format common data types as Utf8 strings. + /// + public static partial class Utf8Formatter + { + private static bool TryFormatUInt64X(ulong value, byte precision, bool useLower, Span buffer, out int bytesWritten) + { + int actualDigitCount = FormattingHelpers.CountHexDigits(value); + int computedOutputLength = (precision == StandardFormat.NoPrecision) + ? actualDigitCount + : Math.Max(precision, actualDigitCount); + + if (buffer.Length < computedOutputLength) + { + bytesWritten = 0; + return false; + } + + bytesWritten = computedOutputLength; + string hexTable = (useLower) ? FormattingHelpers.HexTableLower : FormattingHelpers.HexTableUpper; + + // Writing the output backward in this manner allows the JIT to elide + // bounds checking on the output buffer. The JIT won't elide the bounds + // check on the hex table lookup, but we can live with that for now. + + // It doesn't quite make sense to use the fast hex conversion functionality + // for this method since that routine works on bytes, and here we're working + // directly with nibbles. There may be opportunity for improvement by special- + // casing output lengths of 2, 4, 8, and 16 and running them down optimized + // code paths. + + while ((uint)(--computedOutputLength) < (uint)buffer.Length) + { + buffer[computedOutputLength] = (byte)hexTable[(int)value & 0xf]; + value >>= 4; + } + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.cs new file mode 100644 index 0000000000..2a95f17241 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.cs @@ -0,0 +1,52 @@ +// 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.Runtime.CompilerServices; + +namespace System.Buffers.Text +{ + /// + /// Methods to format common data types as Utf8 strings. + /// + public static partial class Utf8Formatter + { + // + // Common worker for all unsigned integer TryFormat overloads + // + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool TryFormatUInt64(ulong value, Span buffer, out int bytesWritten, StandardFormat format) + { + if (format.IsDefault) + { + return TryFormatUInt64Default(value, buffer, out bytesWritten); + } + + switch (format.Symbol) + { + case 'G': + case 'g': + if (format.HasPrecision) + throw new NotSupportedException(SR.Argument_GWithPrecisionNotSupported); // With a precision, 'G' can produce exponential format, even for integers. + return TryFormatUInt64D(value, format.Precision, buffer, insertNegationSign: false, out bytesWritten); + + case 'd': + case 'D': + return TryFormatUInt64D(value, format.Precision, buffer, insertNegationSign: false, out bytesWritten); + + case 'n': + case 'N': + return TryFormatUInt64N(value, format.Precision, buffer, insertNegationSign: false, out bytesWritten); + + case 'x': + return TryFormatUInt64X(value, format.Precision, true /* useLower */, buffer, out bytesWritten); + + case 'X': + return TryFormatUInt64X(value, format.Precision, false /* useLower */, buffer, out bytesWritten); + + default: + return ThrowHelper.TryFormatThrowFormatException(out bytesWritten); + } + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.cs new file mode 100644 index 0000000000..a25155cab9 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.cs @@ -0,0 +1,208 @@ +// 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. + +namespace System.Buffers.Text +{ + /// + /// Methods to format common data types as Utf8 strings. + /// + public static partial class Utf8Formatter + { + /// + /// Formats a Byte as a UTF8 string. + /// + /// Value to format + /// Buffer to write the UTF8-formatted value to + /// Receives the length of the formatted text in bytes + /// The standard format to use + /// + /// true for success. "bytesWritten" contains the length of the formatted text in bytes. + /// false if buffer was too short. Iteratively increase the size of the buffer and retry until it succeeds. + /// + /// + /// Formats supported: + /// G/g (default) + /// D/d 32767 + /// N/n 32,767 + /// X/x 7fff + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryFormat(byte value, Span buffer, out int bytesWritten, StandardFormat format = default) + => TryFormatUInt64(value, buffer, out bytesWritten, format); + + /// + /// Formats an SByte as a UTF8 string. + /// + /// Value to format + /// Buffer to write the UTF8-formatted value to + /// Receives the length of the formatted text in bytes + /// The standard format to use + /// + /// true for success. "bytesWritten" contains the length of the formatted text in bytes. + /// false if buffer was too short. Iteratively increase the size of the buffer and retry until it succeeds. + /// + /// + /// Formats supported: + /// G/g (default) + /// D/d 32767 + /// N/n 32,767 + /// X/x 7fff + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + [CLSCompliant(false)] + public static bool TryFormat(sbyte value, Span buffer, out int bytesWritten, StandardFormat format = default) + => TryFormatInt64(value, 0xff, buffer, out bytesWritten, format); + + /// + /// Formats a Unt16 as a UTF8 string. + /// + /// Value to format + /// Buffer to write the UTF8-formatted value to + /// Receives the length of the formatted text in bytes + /// The standard format to use + /// + /// true for success. "bytesWritten" contains the length of the formatted text in bytes. + /// false if buffer was too short. Iteratively increase the size of the buffer and retry until it succeeds. + /// + /// + /// Formats supported: + /// G/g (default) + /// D/d 32767 + /// N/n 32,767 + /// X/x 7fff + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + [CLSCompliant(false)] + public static bool TryFormat(ushort value, Span buffer, out int bytesWritten, StandardFormat format = default) + => TryFormatUInt64(value, buffer, out bytesWritten, format); + + /// + /// Formats an Int16 as a UTF8 string. + /// + /// Value to format + /// Buffer to write the UTF8-formatted value to + /// Receives the length of the formatted text in bytes + /// The standard format to use + /// + /// true for success. "bytesWritten" contains the length of the formatted text in bytes. + /// false if buffer was too short. Iteratively increase the size of the buffer and retry until it succeeds. + /// + /// + /// Formats supported: + /// G/g (default) + /// D/d 32767 + /// N/n 32,767 + /// X/x 7fff + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryFormat(short value, Span buffer, out int bytesWritten, StandardFormat format = default) + => TryFormatInt64(value, 0xffff, buffer, out bytesWritten, format); + + /// + /// Formats a UInt32 as a UTF8 string. + /// + /// Value to format + /// Buffer to write the UTF8-formatted value to + /// Receives the length of the formatted text in bytes + /// The standard format to use + /// + /// true for success. "bytesWritten" contains the length of the formatted text in bytes. + /// false if buffer was too short. Iteratively increase the size of the buffer and retry until it succeeds. + /// + /// + /// Formats supported: + /// G/g (default) + /// D/d 32767 + /// N/n 32,767 + /// X/x 7fff + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + [CLSCompliant(false)] + public static bool TryFormat(uint value, Span buffer, out int bytesWritten, StandardFormat format = default) + => TryFormatUInt64(value, buffer, out bytesWritten, format); + + /// + /// Formats an Int32 as a UTF8 string. + /// + /// Value to format + /// Buffer to write the UTF8-formatted value to + /// Receives the length of the formatted text in bytes + /// The standard format to use + /// + /// true for success. "bytesWritten" contains the length of the formatted text in bytes. + /// false if buffer was too short. Iteratively increase the size of the buffer and retry until it succeeds. + /// + /// + /// Formats supported: + /// G/g (default) + /// D/d 32767 + /// N/n 32,767 + /// X/x 7fff + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryFormat(int value, Span buffer, out int bytesWritten, StandardFormat format = default) + => TryFormatInt64(value, 0xffffffff, buffer, out bytesWritten, format); + + /// + /// Formats a UInt64 as a UTF8 string. + /// + /// Value to format + /// Buffer to write the UTF8-formatted value to + /// Receives the length of the formatted text in bytes + /// The standard format to use + /// + /// true for success. "bytesWritten" contains the length of the formatted text in bytes. + /// false if buffer was too short. Iteratively increase the size of the buffer and retry until it succeeds. + /// + /// + /// Formats supported: + /// G/g (default) + /// D/d 32767 + /// N/n 32,767 + /// X/x 7fff + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + [CLSCompliant(false)] + public static bool TryFormat(ulong value, Span buffer, out int bytesWritten, StandardFormat format = default) + => TryFormatUInt64(value, buffer, out bytesWritten, format); + + /// + /// Formats an Int64 as a UTF8 string. + /// + /// Value to format + /// Buffer to write the UTF8-formatted value to + /// Receives the length of the formatted text in bytes + /// The standard format to use + /// + /// true for success. "bytesWritten" contains the length of the formatted text in bytes. + /// false if buffer was too short. Iteratively increase the size of the buffer and retry until it succeeds. + /// + /// + /// Formats supported: + /// G/g (default) + /// D/d 32767 + /// N/n 32,767 + /// X/x 7fff + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryFormat(long value, Span buffer, out int bytesWritten, StandardFormat format = default) + => TryFormatInt64(value, 0xffffffffffffffff, buffer, out bytesWritten, format); + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.TimeSpan.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.TimeSpan.cs new file mode 100644 index 0000000000..2c8dacaa66 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.TimeSpan.cs @@ -0,0 +1,222 @@ +// 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.Diagnostics; + +namespace System.Buffers.Text +{ + public static partial class Utf8Formatter + { + /// + /// Formats a TimeSpan as a UTF8 string. + /// + /// Value to format + /// Buffer to write the UTF8-formatted value to + /// Receives the length of the formatted text in bytes + /// The standard format to use + /// + /// true for success. "bytesWritten" contains the length of the formatted text in bytes. + /// false if buffer was too short. Iteratively increase the size of the buffer and retry until it succeeds. + /// + /// + /// Formats supported: + /// c/t/T (default) [-][d.]hh:mm:ss[.fffffff] (constant format) + /// G [-]d:hh:mm:ss.fffffff (general long) + /// g [-][d:][h]h:mm:ss[.f[f[f[f[f[f[f]]]]]] (general short) + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryFormat(TimeSpan value, Span buffer, out int bytesWritten, StandardFormat format = default) + { + char symbol = FormattingHelpers.GetSymbolOrDefault(format, 'c'); + + switch (symbol) + { + case 'c': + case 'G': + case 'g': + break; + + case 't': + case 'T': + symbol = 'c'; + break; + + default: + return ThrowHelper.TryFormatThrowFormatException(out bytesWritten); + } + + // First, calculate how large an output buffer is needed to hold the entire output. + + int requiredOutputLength = 8; // start with "hh:mm:ss" and adjust as necessary + + uint fraction; + ulong totalSecondsRemaining; + { + // Turn this into a non-negative TimeSpan if possible. + var ticks = value.Ticks; + if (ticks < 0) + { + ticks = -ticks; + if (ticks < 0) + { + Debug.Assert(ticks == Int64.MinValue /* -9223372036854775808 */); + + // We computed these ahead of time; they're straight from the decimal representation of Int64.MinValue. + fraction = 4775808; + totalSecondsRemaining = 922337203685; + goto AfterComputeFraction; + } + } + + totalSecondsRemaining = FormattingHelpers.DivMod((ulong)Math.Abs(value.Ticks), TimeSpan.TicksPerSecond, out ulong fraction64); + fraction = (uint)fraction64; + } + +AfterComputeFraction: + + int fractionDigits = 0; + if (symbol == 'c') + { + // Only write out the fraction if it's non-zero, and in that + // case write out the entire fraction (all digits). + if (fraction != 0) + { + fractionDigits = Utf8Constants.DateTimeNumFractionDigits; + } + } + else if (symbol == 'G') + { + // Always write out the fraction, even if it's zero. + fractionDigits = Utf8Constants.DateTimeNumFractionDigits; + } + else + { + // Only write out the fraction if it's non-zero, and in that + // case write out only the most significant digits. + if (fraction != 0) + { + fractionDigits = Utf8Constants.DateTimeNumFractionDigits - FormattingHelpers.CountDecimalTrailingZeros(fraction, out fraction); + } + } + + Debug.Assert(fraction < 10_000_000); + + // If we're going to write out a fraction, also need to write the leading decimal. + if (fractionDigits != 0) + { + requiredOutputLength += fractionDigits + 1; + } + + ulong totalMinutesRemaining = 0; + ulong seconds = 0; + if (totalSecondsRemaining > 0) + { + // Only compute minutes if the TimeSpan has an absolute value of >= 1 minute. + totalMinutesRemaining = FormattingHelpers.DivMod(totalSecondsRemaining, 60 /* seconds per minute */, out seconds); + } + + Debug.Assert(seconds < 60); + + ulong totalHoursRemaining = 0; + ulong minutes = 0; + if (totalMinutesRemaining > 0) + { + // Only compute hours if the TimeSpan has an absolute value of >= 1 hour. + totalHoursRemaining = FormattingHelpers.DivMod(totalMinutesRemaining, 60 /* minutes per hour */, out minutes); + } + + Debug.Assert(minutes < 60); + + // At this point, we can switch over to 32-bit divmod since the data has shrunk far enough. + Debug.Assert(totalHoursRemaining <= UInt32.MaxValue); + + uint days = 0; + uint hours = 0; + if (totalHoursRemaining > 0) + { + // Only compute days if the TimeSpan has an absolute value of >= 1 day. + days = FormattingHelpers.DivMod((uint)totalHoursRemaining, 24 /* hours per day */, out hours); + } + + Debug.Assert(hours < 24); + + int hourDigits = 2; + if (hours < 10 && symbol == 'g') + { + // Only writing a one-digit hour, not a two-digit hour + hourDigits--; + requiredOutputLength--; + } + + int dayDigits = 0; + if (days == 0) + { + if (symbol == 'G') + { + requiredOutputLength += 2; // for the leading "0:" + dayDigits = 1; + } + } + else + { + dayDigits = FormattingHelpers.CountDigits(days); + requiredOutputLength += dayDigits + 1; // for the leading "d:" (or "d.") + } + + if (value.Ticks < 0) + { + requiredOutputLength++; // for the leading '-' sign + } + + if (buffer.Length < requiredOutputLength) + { + bytesWritten = 0; + return false; + } + + bytesWritten = requiredOutputLength; + + int idx = 0; + + // Write leading '-' if necessary + if (value.Ticks < 0) + { + buffer[idx++] = Utf8Constants.Minus; + } + + // Write day (and separator) if necessary + if (dayDigits > 0) + { + FormattingHelpers.WriteDigits(days, buffer.Slice(idx, dayDigits)); + idx += dayDigits; + buffer[idx++] = (symbol == 'c') ? Utf8Constants.Period : Utf8Constants.Colon; + } + + // Write "[h]h:mm:ss" + FormattingHelpers.WriteDigits(hours, buffer.Slice(idx, hourDigits)); + idx += hourDigits; + buffer[idx++] = Utf8Constants.Colon; + FormattingHelpers.WriteDigits((uint)minutes, buffer.Slice(idx, 2)); + idx += 2; + buffer[idx++] = Utf8Constants.Colon; + FormattingHelpers.WriteDigits((uint)seconds, buffer.Slice(idx, 2)); + idx += 2; + + // Write fraction (and separator) if necessary + if (fractionDigits > 0) + { + buffer[idx++] = Utf8Constants.Period; + FormattingHelpers.WriteDigits(fraction, buffer.Slice(idx, fractionDigits)); + idx += fractionDigits; + } + + // And we're done! + + Debug.Assert(idx == requiredOutputLength); + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/ParserHelpers.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/ParserHelpers.cs new file mode 100644 index 0000000000..b527433a7d --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/ParserHelpers.cs @@ -0,0 +1,55 @@ +// 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.Runtime.CompilerServices; + +namespace System.Buffers.Text +{ + internal static class ParserHelpers + { + public const int ByteOverflowLength = 3; + public const int ByteOverflowLengthHex = 2; + public const int UInt16OverflowLength = 5; + public const int UInt16OverflowLengthHex = 4; + public const int UInt32OverflowLength = 10; + public const int UInt32OverflowLengthHex = 8; + public const int UInt64OverflowLength = 20; + public const int UInt64OverflowLengthHex = 16; + + public const int SByteOverflowLength = 3; + public const int SByteOverflowLengthHex = 2; + public const int Int16OverflowLength = 5; + public const int Int16OverflowLengthHex = 4; + public const int Int32OverflowLength = 10; + public const int Int32OverflowLengthHex = 8; + public const int Int64OverflowLength = 19; + public const int Int64OverflowLengthHex = 16; + + public static readonly byte[] s_hexLookup = + { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 15 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 31 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 47 + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 63 + 0xFF, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 79 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 95 + 0xFF, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 111 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 127 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 143 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 159 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 175 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 191 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 207 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 223 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 239 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF // 255 + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsDigit(int i) + { + return (uint)(i - '0') <= ('9' - '0'); + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Boolean.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Boolean.cs new file mode 100644 index 0000000000..51567ba04c --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Boolean.cs @@ -0,0 +1,63 @@ +// 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. + +namespace System.Buffers.Text +{ + public static partial class Utf8Parser + { + /// + /// Parses a Boolean at the start of a Utf8 string. + /// + /// The Utf8 string to parse + /// Receives the parsed value + /// On a successful parse, receives the length in bytes of the substring that was parsed + /// Expected format of the Utf8 string + /// + /// true for success. "bytesConsumed" contains the length in bytes of the substring that was parsed. + /// false if the string was not syntactically valid or an overflow or underflow occurred. "bytesConsumed" is set to 0. + /// + /// + /// Formats supported: + /// G (default) True/False + /// l true/false + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryParse(ReadOnlySpan text, out bool value, out int bytesConsumed, char standardFormat = default) + { + if (!(standardFormat == default(char) || standardFormat == 'G' || standardFormat == 'l')) + return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); + + if (text.Length >= 4) + { + if ((text[0] == 'T' || text[0] == 't') && + (text[1] == 'R' || text[1] == 'r') && + (text[2] == 'U' || text[2] == 'u') && + (text[3] == 'E' || text[3] == 'e')) + { + bytesConsumed = 4; + value = true; + return true; + } + if (text.Length >= 5) + { + if ((text[0] == 'F' || text[0] == 'f') && + (text[1] == 'A' || text[1] == 'a') && + (text[2] == 'L' || text[2] == 'l') && + (text[3] == 'S' || text[3] == 's') && + (text[4] == 'E' || text[4] == 'e')) + { + bytesConsumed = 5; + value = false; + return true; + } + } + } + bytesConsumed = 0; + value = default; + return false; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.Default.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.Default.cs new file mode 100644 index 0000000000..73c21c8535 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.Default.cs @@ -0,0 +1,102 @@ +// 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. + +namespace System.Buffers.Text +{ + public static partial class Utf8Parser + { + // + // ToString() format for DateTimeOffset. Does not have a corresponding format symbol but it + // is the "G" format postpended with the UTC offset. + // + // 01234567890123456789012345 + // -------------------------- + // 05/25/2017 10:30:15 -08:00 + // + private static bool TryParseDateTimeOffsetDefault(ReadOnlySpan text, out DateTimeOffset value, out int bytesConsumed) + { + if (text.Length < 26) + { + bytesConsumed = 0; + value = default; + return false; + } + + if (!TryParseDateTimeG(text, out DateTime dateTime, out _, out _)) + { + bytesConsumed = 0; + value = default; + return false; + } + + if (text[19] != Utf8Constants.Space) + { + bytesConsumed = 0; + value = default; + return false; + } + + byte sign = text[20]; + if (sign != Utf8Constants.Plus && sign != Utf8Constants.Minus) + { + bytesConsumed = 0; + value = default; + return false; + } + + int offsetHours; + { + uint digit1 = text[21] - 48u; // '0' + uint digit2 = text[22] - 48u; // '0' + + if (digit1 > 9 || digit2 > 9) + { + bytesConsumed = 0; + value = default; + return false; + } + + offsetHours = (int)(digit1 * 10 + digit2); + } + + if (text[23] != Utf8Constants.Colon) + { + bytesConsumed = 0; + value = default; + return false; + } + + int offsetMinutes; + { + uint digit1 = text[24] - 48u; // '0' + uint digit2 = text[25] - 48u; // '0' + + if (digit1 > 9 || digit2 > 9) + { + bytesConsumed = 0; + value = default; + return false; + } + + offsetMinutes = (int)(digit1 * 10 + digit2); + } + + TimeSpan offset = new TimeSpan(hours: offsetHours, minutes: offsetMinutes, seconds: 0); + if (sign == Utf8Constants.Minus) + { + offset = -offset; + } + + if (!TryCreateDateTimeOffset(dateTime: dateTime, offsetNegative: sign == Utf8Constants.Minus, offsetHours: offsetHours, offsetMinutes: offsetMinutes, out value)) + { + bytesConsumed = 0; + value = default; + return false; + } + + bytesConsumed = 26; + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.G.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.G.cs new file mode 100644 index 0000000000..4850bbbab8 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.G.cs @@ -0,0 +1,179 @@ +// 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.Diagnostics; + +namespace System.Buffers.Text +{ + public static partial class Utf8Parser + { + // + // 'G' format for DateTime. + // + // 0123456789012345678 + // --------------------------------- + // 05/25/2017 10:30:15 + // + private static bool TryParseDateTimeG(ReadOnlySpan text, out DateTime value, out DateTimeOffset valueAsOffset, out int bytesConsumed) + { + if (text.Length < 19) + { + bytesConsumed = 0; + value = default; + valueAsOffset = default; + return false; + } + + int month; + { + uint digit1 = text[0] - 48u; // '0' + uint digit2 = text[1] - 48u; // '0' + + if (digit1 > 9 || digit2 > 9) + { + bytesConsumed = 0; + value = default; + valueAsOffset = default; + return false; + } + + month = (int)(digit1 * 10 + digit2); + } + + if (text[2] != Utf8Constants.Slash) + { + bytesConsumed = 0; + value = default; + valueAsOffset = default; + return false; + } + + int day; + { + uint digit1 = text[3] - 48u; // '0' + uint digit2 = text[4] - 48u; // '0' + + if (digit1 > 9 || digit2 > 9) + { + bytesConsumed = 0; + value = default; + valueAsOffset = default; + return false; + } + + day = (int)(digit1 * 10 + digit2); + } + + if (text[5] != Utf8Constants.Slash) + { + bytesConsumed = 0; + value = default; + valueAsOffset = default; + return false; + } + + int year; + { + uint digit1 = text[6] - 48u; // '0' + uint digit2 = text[7] - 48u; // '0' + uint digit3 = text[8] - 48u; // '0' + uint digit4 = text[9] - 48u; // '0' + + if (digit1 > 9 || digit2 > 9 || digit3 > 9 || digit4 > 9) + { + bytesConsumed = 0; + value = default; + valueAsOffset = default; + return false; + } + + year = (int)(digit1 * 1000 + digit2 * 100 + digit3 * 10 + digit4); + } + + if (text[10] != Utf8Constants.Space) + { + bytesConsumed = 0; + value = default; + valueAsOffset = default; + return false; + } + + int hour; + { + uint digit1 = text[11] - 48u; // '0' + uint digit2 = text[12] - 48u; // '0' + + if (digit1 > 9 || digit2 > 9) + { + bytesConsumed = 0; + value = default; + valueAsOffset = default; + return false; + } + + hour = (int)(digit1 * 10 + digit2); + } + + if (text[13] != Utf8Constants.Colon) + { + bytesConsumed = 0; + value = default; + valueAsOffset = default; + return false; + } + + int minute; + { + uint digit1 = text[14] - 48u; // '0' + uint digit2 = text[15] - 48u; // '0' + + if (digit1 > 9 || digit2 > 9) + { + bytesConsumed = 0; + value = default; + valueAsOffset = default; + return false; + } + + minute = (int)(digit1 * 10 + digit2); + } + + if (text[16] != Utf8Constants.Colon) + { + bytesConsumed = 0; + value = default; + valueAsOffset = default; + return false; + } + + int second; + { + uint digit1 = text[17] - 48u; // '0' + uint digit2 = text[18] - 48u; // '0' + + if (digit1 > 9 || digit2 > 9) + { + bytesConsumed = 0; + value = default; + valueAsOffset = default; + return false; + } + + second = (int)(digit1 * 10 + digit2); + } + + if (!TryCreateDateTimeOffsetInterpretingDataAsLocalTime(year: year, month: month, day: day, hour: hour, minute: minute, second: second, fraction: 0, out valueAsOffset)) + { + bytesConsumed = 0; + value = default; + valueAsOffset = default; + return false; + } + + bytesConsumed = 19; + value = valueAsOffset.DateTime; + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.Helpers.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.Helpers.cs new file mode 100644 index 0000000000..b284ba5488 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.Helpers.cs @@ -0,0 +1,162 @@ +// 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.Diagnostics; + +namespace System.Buffers.Text +{ + public static partial class Utf8Parser + { + /// + /// Overflow-safe DateTimeOffset factory. + /// + private static bool TryCreateDateTimeOffset(DateTime dateTime, bool offsetNegative, int offsetHours, int offsetMinutes, out DateTimeOffset value) + { + if (((uint)offsetHours) > Utf8Constants.DateTimeMaxUtcOffsetHours) + { + value = default; + return false; + } + + if (((uint)offsetMinutes) > 59) + { + value = default; + return false; + } + + if (offsetHours == Utf8Constants.DateTimeMaxUtcOffsetHours && offsetMinutes != 0) + { + value = default; + return false; + } + + long offsetTicks = (((long)offsetHours) * 3600 + ((long)offsetMinutes) * 60) * TimeSpan.TicksPerSecond; + if (offsetNegative) + { + offsetTicks = -offsetTicks; + } + + try + { + value = new DateTimeOffset(ticks: dateTime.Ticks, offset: new TimeSpan(ticks: offsetTicks)); + } + catch (ArgumentOutOfRangeException) + { + // If we got here, the combination of the DateTime + UTC offset strayed outside the 1..9999 year range. This case seems rare enough + // that it's better to catch the exception rather than replicate DateTime's range checking (which it's going to do anyway.) + value = default; + return false; + } + + return true; + } + + /// + /// Overflow-safe DateTimeOffset factory. + /// + private static bool TryCreateDateTimeOffset(int year, int month, int day, int hour, int minute, int second, int fraction, bool offsetNegative, int offsetHours, int offsetMinutes, out DateTimeOffset value) + { + if (!TryCreateDateTime(year: year, month: month, day: day, hour: hour, minute: minute, second: second, fraction: fraction, kind: DateTimeKind.Unspecified, out DateTime dateTime)) + { + value = default; + return false; + } + + if (!TryCreateDateTimeOffset(dateTime: dateTime, offsetNegative: offsetNegative, offsetHours: offsetHours, offsetMinutes: offsetMinutes, out value)) + { + value = default; + return false; + } + + return true; + } + + /// + /// Overflow-safe DateTimeOffset/Local time conversion factory. + /// + private static bool TryCreateDateTimeOffsetInterpretingDataAsLocalTime(int year, int month, int day, int hour, int minute, int second, int fraction, out DateTimeOffset value) + { + if (!TryCreateDateTime(year: year, month: month, day: day, hour: hour, minute: minute, second: second, fraction: fraction, DateTimeKind.Local, out DateTime dateTime)) + { + value = default; + return false; + } + + try + { + value = new DateTimeOffset(dateTime); + } + catch (ArgumentOutOfRangeException) + { + // If we got here, the combination of the DateTime + UTC offset strayed outside the 1..9999 year range. This case seems rare enough + // that it's better to catch the exception rather than replicate DateTime's range checking (which it's going to do anyway.) + + value = default; + return false; + } + + return true; + } + + /// + /// Overflow-safe DateTime factory. + /// + private static bool TryCreateDateTime(int year, int month, int day, int hour, int minute, int second, int fraction, DateTimeKind kind, out DateTime value) + { + if (year == 0) + { + value = default; + return false; + } + + Debug.Assert(year <= 9999); // All of our callers to date parse the year from fixed 4-digit fields so this value is trusted. + + if ((((uint)month) - 1) >= 12) + { + value = default; + return false; + } + + uint dayMinusOne = ((uint)day) - 1; + if (dayMinusOne >= 28 && dayMinusOne >= DateTime.DaysInMonth(year, month)) + { + value = default; + return false; + } + + if (((uint)hour) > 23) + { + value = default; + return false; + } + + if (((uint)minute) > 59) + { + value = default; + return false; + } + + if (((uint)second) > 59) + { + value = default; + return false; + } + + Debug.Assert(fraction >= 0 && fraction <= Utf8Constants.MaxDateTimeFraction); // All of our callers to date parse the fraction from fixed 7-digit fields so this value is trusted. + + int[] days = DateTime.IsLeapYear(year) ? s_daysToMonth366 : s_daysToMonth365; + int yearMinusOne = year - 1; + int totalDays = (yearMinusOne * 365) + (yearMinusOne / 4) - (yearMinusOne / 100) + (yearMinusOne / 400) + days[month - 1] + day - 1; + long ticks = totalDays * TimeSpan.TicksPerDay; + int totalSeconds = (hour * 3600) + (minute * 60) + second; + ticks += totalSeconds * TimeSpan.TicksPerSecond; + ticks += fraction; + value = new DateTime(ticks: ticks, kind: kind); + return true; + } + + private static readonly int[] s_daysToMonth365 = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}; + private static readonly int[] s_daysToMonth366 = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}; + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.O.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.O.cs new file mode 100644 index 0000000000..a3028fe984 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.O.cs @@ -0,0 +1,290 @@ +// 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.Diagnostics; + +namespace System.Buffers.Text +{ + public static partial class Utf8Parser + { + // + // Roundtrippable format. One of + // + // 012345678901234567890123456789012 + // --------------------------------- + // 2017-06-12T05:30:45.7680000-07:00 + // 2017-06-12T05:30:45.7680000Z (Z is short for "+00:00" but also distinguishes DateTimeKind.Utc from DateTimeKind.Local) + // 2017-06-12T05:30:45.7680000 (interpreted as local time wrt to current time zone) + // + private static bool TryParseDateTimeOffsetO(ReadOnlySpan text, out DateTimeOffset value, out int bytesConsumed, out DateTimeKind kind) + { + if (text.Length < 27) + { + value = default; + bytesConsumed = 0; + kind = default; + return false; + } + + int year; + { + uint digit1 = text[0] - 48u; // '0' + uint digit2 = text[1] - 48u; // '0' + uint digit3 = text[2] - 48u; // '0' + uint digit4 = text[3] - 48u; // '0' + + if (digit1 > 9 || digit2 > 9 || digit3 > 9 || digit4 > 9) + { + value = default; + bytesConsumed = 0; + kind = default; + return false; + } + + year = (int)(digit1 * 1000 + digit2 * 100 + digit3 * 10 + digit4); + } + + if (text[4] != Utf8Constants.Hyphen) + { + value = default; + bytesConsumed = 0; + kind = default; + return false; + } + + int month; + { + uint digit1 = text[5] - 48u; // '0' + uint digit2 = text[6] - 48u; // '0' + + if (digit1 > 9 || digit2 > 9) + { + value = default; + bytesConsumed = 0; + kind = default; + return false; + } + + month = (int)(digit1 * 10 + digit2); + } + + if (text[7] != Utf8Constants.Hyphen) + { + value = default; + bytesConsumed = 0; + kind = default; + return false; + } + + int day; + { + uint digit1 = text[8] - 48u; // '0' + uint digit2 = text[9] - 48u; // '0' + + if (digit1 > 9 || digit2 > 9) + { + value = default; + bytesConsumed = 0; + kind = default; + return false; + } + + day = (int)(digit1 * 10 + digit2); + } + + if (text[10] != 'T') + { + value = default; + bytesConsumed = 0; + kind = default; + return false; + } + + int hour; + { + uint digit1 = text[11] - 48u; // '0' + uint digit2 = text[12] - 48u; // '0' + + if (digit1 > 9 || digit2 > 9) + { + value = default; + bytesConsumed = 0; + kind = default; + return false; + } + + hour = (int)(digit1 * 10 + digit2); + } + + if (text[13] != Utf8Constants.Colon) + { + value = default; + bytesConsumed = 0; + kind = default; + return false; + } + + int minute; + { + uint digit1 = text[14] - 48u; // '0' + uint digit2 = text[15] - 48u; // '0' + + if (digit1 > 9 || digit2 > 9) + { + value = default; + bytesConsumed = 0; + kind = default; + return false; + } + + minute = (int)(digit1 * 10 + digit2); + } + + if (text[16] != Utf8Constants.Colon) + { + value = default; + bytesConsumed = 0; + kind = default; + return false; + } + + int second; + { + uint digit1 = text[17] - 48u; // '0' + uint digit2 = text[18] - 48u; // '0' + + if (digit1 > 9 || digit2 > 9) + { + value = default; + bytesConsumed = 0; + kind = default; + return false; + } + + second = (int)(digit1 * 10 + digit2); + } + + if (text[19] != Utf8Constants.Period) + { + value = default; + bytesConsumed = 0; + kind = default; + return false; + } + + int fraction; + { + uint digit1 = text[20] - 48u; // '0' + uint digit2 = text[21] - 48u; // '0' + uint digit3 = text[22] - 48u; // '0' + uint digit4 = text[23] - 48u; // '0' + uint digit5 = text[24] - 48u; // '0' + uint digit6 = text[25] - 48u; // '0' + uint digit7 = text[26] - 48u; // '0' + + if (digit1 > 9 || digit2 > 9 || digit3 > 9 || digit4 > 9 || digit5 > 9 || digit6 > 9 || digit7 > 9) + { + value = default; + bytesConsumed = 0; + kind = default; + return false; + } + + fraction = (int)(digit1 * 1000000 + digit2 * 100000 + digit3 * 10000 + digit4 * 1000 + digit5 * 100 + digit6 * 10 + digit7); + } + + byte offsetChar = (text.Length <= 27) ? default : text[27]; + if (offsetChar != 'Z' && offsetChar != Utf8Constants.Plus && offsetChar != Utf8Constants.Minus) + { + if (!TryCreateDateTimeOffsetInterpretingDataAsLocalTime(year: year, month: month, day: day, hour: hour, minute: minute, second: second, fraction: fraction, out value)) + { + value = default; + bytesConsumed = 0; + kind = default; + return false; + } + bytesConsumed = 27; + kind = DateTimeKind.Unspecified; + return true; + } + + if (offsetChar == 'Z') + { + // Same as specifying an offset of "+00:00", except that DateTime's Kind gets set to UTC rather than Local + if (!TryCreateDateTimeOffset(year: year, month: month, day: day, hour: hour, minute: minute, second: second, fraction: fraction, offsetNegative: false, offsetHours: 0, offsetMinutes: 0, out value)) + { + value = default; + bytesConsumed = 0; + kind = default; + return false; + } + + bytesConsumed = 28; + kind = DateTimeKind.Utc; + return true; + } + + Debug.Assert(offsetChar == Utf8Constants.Plus || offsetChar == Utf8Constants.Minus); + if (text.Length < 33) + { + value = default; + bytesConsumed = 0; + kind = default; + return false; + } + + int offsetHours; + { + uint digit1 = text[28] - 48u; // '0' + uint digit2 = text[29] - 48u; // '0' + + if (digit1 > 9 || digit2 > 9) + { + value = default; + bytesConsumed = 0; + kind = default; + return false; + } + + offsetHours = (int)(digit1 * 10 + digit2); + } + + if (text[30] != Utf8Constants.Colon) + { + value = default; + bytesConsumed = 0; + kind = default; + return false; + } + + int offsetMinutes; + { + uint digit1 = text[31] - 48u; // '0' + uint digit2 = text[32] - 48u; // '0' + + if (digit1 > 9 || digit2 > 9) + { + value = default; + bytesConsumed = 0; + kind = default; + return false; + } + + offsetMinutes = (int)(digit1 * 10 + digit2); + } + + if (!TryCreateDateTimeOffset(year: year, month: month, day: day, hour: hour, minute: minute, second: second, fraction: fraction, offsetNegative: offsetChar == Utf8Constants.Minus, offsetHours: offsetHours, offsetMinutes: offsetMinutes, out value)) + { + value = default; + bytesConsumed = 0; + kind = default; + return false; + } + + bytesConsumed = 33; + kind = DateTimeKind.Local; + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.R.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.R.cs new file mode 100644 index 0000000000..af70ca62e5 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.R.cs @@ -0,0 +1,221 @@ +// 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. + +namespace System.Buffers.Text +{ + public static partial class Utf8Parser + { + // + // Parse an RFC1123 date string. + // + // 01234567890123456789012345678 + // ----------------------------- + // Tue, 03 Jan 2017 08:08:05 GMT + // + private static bool TryParseDateTimeOffsetR(ReadOnlySpan text, uint caseFlipXorMask, out DateTimeOffset dateTimeOffset, out int bytesConsumed) + { + if (text.Length < 29) + { + bytesConsumed = 0; + dateTimeOffset = default; + return false; + } + + DayOfWeek dayOfWeek; + { + uint dow0 = text[0] ^ caseFlipXorMask; + uint dow1 = text[1]; + uint dow2 = text[2]; + uint comma = text[3]; + uint dowString = (dow0 << 24) | (dow1 << 16) | (dow2 << 8) | comma; + switch (dowString) + { + case 0x53756E2c /* 'Sun,' */: dayOfWeek = DayOfWeek.Sunday; break; + case 0x4d6f6e2c /* 'Mon,' */: dayOfWeek = DayOfWeek.Monday; break; + case 0x5475652c /* 'Tue,' */: dayOfWeek = DayOfWeek.Tuesday; break; + case 0x5765642c /* 'Wed,' */: dayOfWeek = DayOfWeek.Wednesday; break; + case 0x5468752c /* 'Thu,' */: dayOfWeek = DayOfWeek.Thursday; break; + case 0x4672692c /* 'Fri,' */: dayOfWeek = DayOfWeek.Friday; break; + case 0x5361742c /* 'Sat,' */: dayOfWeek = DayOfWeek.Saturday; break; + default: + bytesConsumed = 0; + dateTimeOffset = default; + return false; + } + } + + if (text[4] != Utf8Constants.Space) + { + bytesConsumed = 0; + dateTimeOffset = default; + return false; + } + + int day; + { + uint digit1 = text[5] - 48u; // '0' + uint digit2 = text[6] - 48u; // '0' + + if (digit1 > 9 || digit2 > 9) + { + bytesConsumed = 0; + dateTimeOffset = default; + return false; + } + + day = (int)(digit1 * 10 + digit2); + } + + if (text[7] != Utf8Constants.Space) + { + bytesConsumed = 0; + dateTimeOffset = default; + return false; + } + + int month; + { + uint mon0 = text[8] ^ caseFlipXorMask; + uint mon1 = text[9]; + uint mon2 = text[10]; + uint space = text[11]; + uint monthString = (mon0 << 24) | (mon1 << 16) | (mon2 << 8) | space; + switch (monthString) + { + case 0x4a616e20 /* 'Jan ' */ : month = 1; break; + case 0x46656220 /* 'Feb ' */ : month = 2; break; + case 0x4d617220 /* 'Mar ' */ : month = 3; break; + case 0x41707220 /* 'Apr ' */ : month = 4; break; + case 0x4d617920 /* 'May ' */ : month = 5; break; + case 0x4a756e20 /* 'Jun ' */ : month = 6; break; + case 0x4a756c20 /* 'Jul ' */ : month = 7; break; + case 0x41756720 /* 'Aug ' */ : month = 8; break; + case 0x53657020 /* 'Sep ' */ : month = 9; break; + case 0x4f637420 /* 'Oct ' */ : month = 10; break; + case 0x4e6f7620 /* 'Nov ' */ : month = 11; break; + case 0x44656320 /* 'Dec ' */ : month = 12; break; + default: + bytesConsumed = 0; + dateTimeOffset = default; + return false; + } + } + + int year; + { + uint digit1 = text[12] - 48u; // '0' + uint digit2 = text[13] - 48u; // '0' + uint digit3 = text[14] - 48u; // '0' + uint digit4 = text[15] - 48u; // '0' + + if (digit1 > 9 || digit2 > 9 || digit3 > 9 || digit4 > 9) + { + bytesConsumed = 0; + dateTimeOffset = default; + return false; + } + + year = (int)(digit1 * 1000 + digit2 * 100 + digit3 * 10 + digit4); + } + + if (text[16] != Utf8Constants.Space) + { + bytesConsumed = 0; + dateTimeOffset = default; + return false; + } + + int hour; + { + uint digit1 = text[17] - 48u; // '0' + uint digit2 = text[18] - 48u; // '0' + + if (digit1 > 9 || digit2 > 9) + { + bytesConsumed = 0; + dateTimeOffset = default; + return false; + } + + hour = (int)(digit1 * 10 + digit2); + } + + if (text[19] != Utf8Constants.Colon) + { + bytesConsumed = 0; + dateTimeOffset = default; + return false; + } + + int minute; + { + uint digit1 = text[20] - 48u; // '0' + uint digit2 = text[21] - 48u; // '0' + + if (digit1 > 9 || digit2 > 9) + { + bytesConsumed = 0; + dateTimeOffset = default; + return false; + } + + minute = (int)(digit1 * 10 + digit2); + } + + if (text[22] != Utf8Constants.Colon) + { + bytesConsumed = 0; + dateTimeOffset = default; + return false; + } + + int second; + { + uint digit1 = text[23] - 48u; // '0' + uint digit2 = text[24] - 48u; // '0' + + if (digit1 > 9 || digit2 > 9) + { + bytesConsumed = 0; + dateTimeOffset = default; + return false; + } + + second = (int)(digit1 * 10 + digit2); + } + + { + uint space = text[25]; + uint g = text[26] ^ caseFlipXorMask; + uint m = text[27] ^ caseFlipXorMask; + uint t = text[28] ^ caseFlipXorMask; + uint gmtString = (space << 24) | (g << 16) | (m << 8) | t; + if (gmtString != 0x20474d54 /* ' GMT' */) + { + bytesConsumed = 0; + dateTimeOffset = default; + return false; + } + } + + if (!TryCreateDateTimeOffset(year: year, month: month, day: day, hour: hour, minute: minute, second: second, fraction: 0, offsetNegative: false, offsetHours: 0, offsetMinutes: 0, out dateTimeOffset)) + { + bytesConsumed = 0; + dateTimeOffset = default; + return false; + } + + if (dayOfWeek != dateTimeOffset.DayOfWeek) + { + // If we got here, the day of week did not match the actual date. + bytesConsumed = 0; + dateTimeOffset = default; + return false; + } + + bytesConsumed = 29; + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.cs new file mode 100644 index 0000000000..d69ff7ce13 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.cs @@ -0,0 +1,149 @@ +// 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.Diagnostics; + +namespace System.Buffers.Text +{ + public static partial class Utf8Parser + { + /// + /// Parses a DateTime at the start of a Utf8 string. + /// + /// The Utf8 string to parse + /// Receives the parsed value + /// On a successful parse, receives the length in bytes of the substring that was parsed + /// Expected format of the Utf8 string + /// + /// true for success. "bytesConsumed" contains the length in bytes of the substring that was parsed. + /// false if the string was not syntactically valid or an overflow or underflow occurred. "bytesConsumed" is set to 0. + /// + /// + /// Formats supported: + /// default 05/25/2017 10:30:15 -08:00 + /// G 05/25/2017 10:30:15 + /// R Tue, 03 Jan 2017 08:08:05 GMT (RFC 1123) + /// l tue, 03 jan 2017 08:08:05 gmt (Lowercase RFC 1123) + /// O 2017-06-12T05:30:45.7680000-07:00 (Round-trippable) + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryParse(ReadOnlySpan text, out DateTime value, out int bytesConsumed, char standardFormat = default) + { + switch (standardFormat) + { + case 'R': + { + if (!TryParseDateTimeOffsetR(text, NoFlipCase, out DateTimeOffset dateTimeOffset, out bytesConsumed)) + { + value = default; + return false; + } + value = dateTimeOffset.DateTime; // (returns a DateTimeKind.Unspecified to match DateTime.ParseExact(). Maybe better to return UtcDateTime instead?) + return true; + } + + case 'l': + { + if (!TryParseDateTimeOffsetR(text, FlipCase, out DateTimeOffset dateTimeOffset, out bytesConsumed)) + { + value = default; + return false; + } + value = dateTimeOffset.DateTime; // (returns a DateTimeKind.Unspecified to match DateTime.ParseExact(). Maybe better to return UtcDateTime instead?) + return true; + } + + case 'O': + { + // Emulates DateTime.ParseExact(text, "O", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind) + // In particular, the formatted string "encodes" the DateTimeKind according to the following table: + // + // 2017-06-12T05:30:45.7680000 - Unspecified + // 2017-06-12T05:30:45.7680000+00:00 - Local + // 2017-06-12T05:30:45.7680000Z - Utc + + if (!TryParseDateTimeOffsetO(text, out DateTimeOffset dateTimeOffset, out bytesConsumed, out DateTimeKind kind)) + { + value = default; + bytesConsumed = 0; + return false; + } + + switch (kind) + { + case DateTimeKind.Local: + value = dateTimeOffset.LocalDateTime; + break; + case DateTimeKind.Utc: + value = dateTimeOffset.UtcDateTime; + break; + default: + Debug.Assert(kind == DateTimeKind.Unspecified); + value = dateTimeOffset.DateTime; + break; + } + + return true; + } + + case (default): + case 'G': + return TryParseDateTimeG(text, out value, out _, out bytesConsumed); + + default: + return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); + } + } + + /// + /// Parses a DateTimeOffset at the start of a Utf8 string. + /// + /// The Utf8 string to parse + /// Receives the parsed value + /// On a successful parse, receives the length in bytes of the substring that was parsed + /// Expected format of the Utf8 string + /// + /// true for success. "bytesConsumed" contains the length in bytes of the substring that was parsed. + /// false if the string was not syntactically valid or an overflow or underflow occurred. "bytesConsumed" is set to 0. + /// + /// + /// Formats supported: + /// G (default) 05/25/2017 10:30:15 + /// R Tue, 03 Jan 2017 08:08:05 GMT (RFC 1123) + /// l tue, 03 jan 2017 08:08:05 gmt (Lowercase RFC 1123) + /// O 2017-06-12T05:30:45.7680000-07:00 (Round-trippable) + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryParse(ReadOnlySpan text, out DateTimeOffset value, out int bytesConsumed, char standardFormat = default) + { + switch (standardFormat) + { + case 'R': + return TryParseDateTimeOffsetR(text, NoFlipCase, out value, out bytesConsumed); + + case 'l': + return TryParseDateTimeOffsetR(text, FlipCase, out value, out bytesConsumed); + + case 'O': + return TryParseDateTimeOffsetO(text, out value, out bytesConsumed, out _); + + case (default): + return TryParseDateTimeOffsetDefault(text, out value, out bytesConsumed); + + case 'G': + return TryParseDateTimeG(text, out DateTime _, out value, out bytesConsumed); + + default: + return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); + } + } + + private const uint FlipCase = 0x00000020u; // XOR mask to flip the case of a letter. + private const uint NoFlipCase = 0x00000000u; + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Decimal.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Decimal.cs new file mode 100644 index 0000000000..59f74bb847 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Decimal.cs @@ -0,0 +1,82 @@ +// 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. + +namespace System.Buffers.Text +{ + public static partial class Utf8Parser + { + /// + /// Parses a Decimal at the start of a Utf8 string. + /// + /// The Utf8 string to parse + /// Receives the parsed value + /// On a successful parse, receives the length in bytes of the substring that was parsed + /// Expected format of the Utf8 string + /// + /// true for success. "bytesConsumed" contains the length in bytes of the substring that was parsed. + /// false if the string was not syntactically valid or an overflow or underflow occurred. "bytesConsumed" is set to 0. + /// + /// + /// Formats supported: + /// G/g (default) + /// F/f 12.45 Fixed point + /// E/e 1.245000e1 Exponential + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryParse(ReadOnlySpan text, out decimal value, out int bytesConsumed, char standardFormat = default) + { + ParseNumberOptions options; + switch (standardFormat) + { + case (default): + case 'G': + case 'g': + case 'E': + case 'e': + options = ParseNumberOptions.AllowExponent; + break; + + case 'F': + case 'f': + options = default; + break; + + default: + return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); + } + + NumberBuffer number = default; + if (!TryParseNumber(text, ref number, out bytesConsumed, options, out bool textUsedExponentNotation)) + { + value = default; + return false; + } + + if ((!textUsedExponentNotation) && (standardFormat == 'E' || standardFormat == 'e')) + { + value = default; + bytesConsumed = 0; + return false; + } + + // More compat with .NET behavior - whether or not a 0 keeps the negative sign depends on whether it an "integer" 0 or a "fractional" 0 + if (number.Digits[0] == 0 && number.Scale == 0) + { + number.IsNegative = false; + } + + value = default; + if (!Number.NumberBufferToDecimal(ref number, ref value)) + { + value = default; + bytesConsumed = 0; + return false; + } + + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Float.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Float.cs new file mode 100644 index 0000000000..0b0e25e22d --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Float.cs @@ -0,0 +1,165 @@ +// 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. + +namespace System.Buffers.Text +{ + public static partial class Utf8Parser + { + /// + /// Parses a Single at the start of a Utf8 string. + /// + /// The Utf8 string to parse + /// Receives the parsed value + /// On a successful parse, receives the length in bytes of the substring that was parsed + /// Expected format of the Utf8 string + /// + /// true for success. "bytesConsumed" contains the length in bytes of the substring that was parsed. + /// false if the string was not syntactically valid or an overflow or underflow occurred. "bytesConsumed" is set to 0. + /// + /// + /// Formats supported: + /// G/g (default) + /// F/f 12.45 Fixed point + /// E/e 1.245000e1 Exponential + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryParse(ReadOnlySpan text, out float value, out int bytesConsumed, char standardFormat = default) + { + if (TryParseNormalAsFloatingPoint(text, out double d, out bytesConsumed, standardFormat)) + { + value = (float)d; + if (float.IsInfinity(value)) + { + value = default; + bytesConsumed = 0; + return false; + } + return true; + } + + return TryParseAsSpecialFloatingPoint(text, float.PositiveInfinity, float.NegativeInfinity, float.NaN, out value, out bytesConsumed); + } + + /// + /// Parses a Double at the start of a Utf8 string. + /// + /// The Utf8 string to parse + /// Receives the parsed value + /// On a successful parse, receives the length in bytes of the substring that was parsed + /// Expected format of the Utf8 string + /// + /// true for success. "bytesConsumed" contains the length in bytes of the substring that was parsed. + /// false if the string was not syntactically valid or an overflow or underflow occurred. "bytesConsumed" is set to 0. + /// + /// + /// Formats supported: + /// G/g (default) + /// F/f 12.45 Fixed point + /// E/e 1.245000e1 Exponential + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryParse(ReadOnlySpan text, out double value, out int bytesConsumed, char standardFormat = default) + { + if (TryParseNormalAsFloatingPoint(text, out value, out bytesConsumed, standardFormat)) + return true; + + return TryParseAsSpecialFloatingPoint(text, double.PositiveInfinity, double.NegativeInfinity, double.NaN, out value, out bytesConsumed); + } + + // + // Attempt to parse the regular floating points (the ones without names like "Infinity" and "NaN") + // + private static bool TryParseNormalAsFloatingPoint(ReadOnlySpan text, out double value, out int bytesConsumed, char standardFormat) + { + ParseNumberOptions options; + switch (standardFormat) + { + case (default): + case 'G': + case 'g': + case 'E': + case 'e': + options = ParseNumberOptions.AllowExponent; + break; + + case 'F': + case 'f': + options = default; + break; + + default: + return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); + } + + NumberBuffer number = default; + if (!TryParseNumber(text, ref number, out bytesConsumed, options, out bool textUsedExponentNotation)) + { + value = default; + return false; + } + + if ((!textUsedExponentNotation) && (standardFormat == 'E' || standardFormat == 'e')) + { + value = default; + bytesConsumed = 0; + return false; + } + + if (number.Digits[0] == 0) + { + number.IsNegative = false; + } + + if (!Number.NumberBufferToDouble(ref number, out value)) + { + value = default; + bytesConsumed = 0; + return false; + } + + return true; + } + + // + // Assuming the text doesn't look like a normal floating point, we attempt to parse it as one the special floating point values. + // + private static bool TryParseAsSpecialFloatingPoint(ReadOnlySpan text, T positiveInfinity, T negativeInfinity, T nan, out T value, out int bytesConsumed) + { + if (text.Length >= 8 && + text[0] == 'I' && text[1] == 'n' && text[2] == 'f' && text[3] == 'i' && + text[4] == 'n' && text[5] == 'i' && text[6] == 't' && text[7] == 'y') + { + value = positiveInfinity; + bytesConsumed = 8; + return true; + } + + if (text.Length >= 9 && + text[0] == Utf8Constants.Minus && + text[1] == 'I' && text[2] == 'n' && text[3] == 'f' && text[4] == 'i' && + text[5] == 'n' && text[6] == 'i' && text[7] == 't' && text[8] == 'y') + { + value = negativeInfinity; + bytesConsumed = 9; + return true; + } + + if (text.Length >= 3 && + text[0] == 'N' && text[1] == 'a' && text[2] == 'N') + { + value = nan; + bytesConsumed = 3; + return true; + } + + value = default; + bytesConsumed = 0; + return false; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Guid.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Guid.cs new file mode 100644 index 0000000000..baefe25905 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Guid.cs @@ -0,0 +1,243 @@ +// 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. + +namespace System.Buffers.Text +{ + public static partial class Utf8Parser + { + /// + /// Parses a Guid at the start of a Utf8 string. + /// + /// The Utf8 string to parse + /// Receives the parsed value + /// On a successful parse, receives the length in bytes of the substring that was parsed + /// Expected format of the Utf8 string + /// + /// true for success. "bytesConsumed" contains the length in bytes of the substring that was parsed. + /// false if the string was not syntactically valid or an overflow or underflow occurred. "bytesConsumed" is set to 0. + /// + /// + /// Formats supported: + /// D (default) nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn + /// B {nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn} + /// P (nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn) + /// N nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryParse(ReadOnlySpan text, out Guid value, out int bytesConsumed, char standardFormat = default) + { + switch (standardFormat) + { + case (default): + case 'D': + return TryParseGuidCore(text, false, ' ', ' ', out value, out bytesConsumed); + case 'B': + return TryParseGuidCore(text, true, '{', '}', out value, out bytesConsumed); + case 'P': + return TryParseGuidCore(text, true, '(', ')', out value, out bytesConsumed); + case 'N': + return TryParseGuidN(text, out value, out bytesConsumed); + default: + return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); + } + } + + // nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn (not very Guid-like, but the format is what it is...) + private static bool TryParseGuidN(ReadOnlySpan text, out Guid value, out int bytesConsumed) + { + if (text.Length < 32) + { + value = default; + bytesConsumed = 0; + return false; + } + + if (!TryParseUInt32X(text.Slice(0, 8), out uint i1, out int justConsumed) || justConsumed != 8) + { + value = default; + bytesConsumed = 0; + return false; // 8 digits + } + + if (!TryParseUInt16X(text.Slice(8, 4), out ushort i2, out justConsumed) || justConsumed != 4) + { + value = default; + bytesConsumed = 0; + return false; // next 4 digits + } + + if (!TryParseUInt16X(text.Slice(12, 4), out ushort i3, out justConsumed) || justConsumed != 4) + { + value = default; + bytesConsumed = 0; + return false; // next 4 digits + } + + if (!TryParseUInt16X(text.Slice(16, 4), out ushort i4, out justConsumed) || justConsumed != 4) + { + value = default; + bytesConsumed = 0; + return false; // next 4 digits + } + + if (!TryParseUInt64X(text.Slice(20), out ulong i5, out justConsumed) || justConsumed != 12) + { + value = default; + bytesConsumed = 0; + return false; // next 4 digits + } + + bytesConsumed = 32; + value = new Guid((int)i1, (short)i2, (short)i3, (byte)(i4 >> 8), (byte)i4, + (byte)(i5 >> 40), (byte)(i5 >> 32), (byte)(i5 >> 24), (byte)(i5 >> 16), (byte)(i5 >> 8), (byte)i5); + return true; + } + + // {8-4-4-4-12}, where number is the number of hex digits, and {/} are ends. + private static bool TryParseGuidCore(ReadOnlySpan text, bool ends, char begin, char end, out Guid value, out int bytesConsumed) + { + int expectedCodingUnits = 36 + (ends ? 2 : 0); // 32 hex digits + 4 delimiters + 2 optional ends + + if (text.Length < expectedCodingUnits) + { + value = default; + bytesConsumed = 0; + return false; + } + + if (ends) + { + if (text[0] != begin) + { + value = default; + bytesConsumed = 0; + return false; + } + + text = text.Slice(1); // skip begining + } + + if (!TryParseUInt32X(text, out uint i1, out int justConsumed)) + { + value = default; + bytesConsumed = 0; + return false; + } + + if (justConsumed != 8) + { + value = default; + bytesConsumed = 0; + return false; // 8 digits + } + + if (text[justConsumed] != '-') + { + value = default; + bytesConsumed = 0; + return false; + } + + text = text.Slice(9); // justConsumed + 1 for delimiter + + if (!TryParseUInt16X(text, out ushort i2, out justConsumed)) + { + value = default; + bytesConsumed = 0; + return false; + } + + if (justConsumed != 4) + { + value = default; + bytesConsumed = 0; + return false; // 4 digits + } + + if (text[justConsumed] != '-') + { + value = default; + bytesConsumed = 0; + return false; + } + + text = text.Slice(5); // justConsumed + 1 for delimiter + + if (!TryParseUInt16X(text, out ushort i3, out justConsumed)) + { + value = default; + bytesConsumed = 0; + return false; + } + + if (justConsumed != 4) + { + value = default; + bytesConsumed = 0; + return false; // 4 digits + } + + if (text[justConsumed] != '-') + { + value = default; + bytesConsumed = 0; + return false; + } + + text = text.Slice(5); // justConsumed + 1 for delimiter + + if (!TryParseUInt16X(text, out ushort i4, out justConsumed)) + { + value = default; + bytesConsumed = 0; + return false; + } + + if (justConsumed != 4) + { + value = default; + bytesConsumed = 0; + return false; // 4 digits + } + + if (text[justConsumed] != '-') + { + value = default; + bytesConsumed = 0; + return false; + } + + text = text.Slice(5);// justConsumed + 1 for delimiter + + if (!TryParseUInt64X(text, out ulong i5, out justConsumed)) + { + value = default; + bytesConsumed = 0; + return false; + } + + if (justConsumed != 12) + { + value = default; + bytesConsumed = 0; + return false; // 12 digits + } + + if (ends && text[justConsumed] != end) + { + value = default; + bytesConsumed = 0; + return false; + } + + bytesConsumed = expectedCodingUnits; + value = new Guid((int)i1, (short)i2, (short)i3, (byte)(i4 >> 8), (byte)i4, + (byte)(i5 >> 40), (byte)(i5 >> 32), (byte)(i5 >> 24), (byte)(i5 >> 16), (byte)(i5 >> 8), (byte)i5); + + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Signed.D.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Signed.D.cs new file mode 100644 index 0000000000..b5f7649181 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Signed.D.cs @@ -0,0 +1,443 @@ +// 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. + +namespace System.Buffers.Text +{ + public static partial class Utf8Parser + { + private static bool TryParseSByteD(ReadOnlySpan text, out sbyte value, out int bytesConsumed) + { + if (text.Length < 1) + goto FalseExit; + + int sign = 1; + int index = 0; + int num = text[index]; + if (num == '-') + { + sign = -1; + index++; + if ((uint)index >= (uint)text.Length) + goto FalseExit; + num = text[index]; + } + else if (num == '+') + { + index++; + if ((uint)index >= (uint)text.Length) + goto FalseExit; + num = text[index]; + } + + int answer = 0; + + if (ParserHelpers.IsDigit(num)) + { + if (num == '0') + { + do + { + index++; + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + } while (num == '0'); + if (!ParserHelpers.IsDigit(num)) + goto Done; + } + + answer = num - '0'; + index++; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + // Potential overflow + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = answer * 10 + num - '0'; + // if sign < 0, (-1 * sign + 1) / 2 = 1 + // else, (-1 * sign + 1) / 2 = 0 + if ((uint)answer > (uint)sbyte.MaxValue + (-1 * sign + 1) / 2) + goto FalseExit; // Overflow + + if ((uint)index >= (uint)text.Length) + goto Done; + if (!ParserHelpers.IsDigit(text[index])) + goto Done; + + // Guaranteed overflow + goto FalseExit; + } + + FalseExit: + bytesConsumed = default; + value = default; + return false; + + Done: + bytesConsumed = index; + value = (sbyte)(answer * sign); + return true; + } + + private static bool TryParseInt16D(ReadOnlySpan text, out short value, out int bytesConsumed) + { + if (text.Length < 1) + goto FalseExit; + + int sign = 1; + int index = 0; + int num = text[index]; + if (num == '-') + { + sign = -1; + index++; + if ((uint)index >= (uint)text.Length) + goto FalseExit; + num = text[index]; + } + else if (num == '+') + { + index++; + if ((uint)index >= (uint)text.Length) + goto FalseExit; + num = text[index]; + } + + int answer = 0; + + if (ParserHelpers.IsDigit(num)) + { + if (num == '0') + { + do + { + index++; + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + } while (num == '0'); + if (!ParserHelpers.IsDigit(num)) + goto Done; + } + + answer = num - '0'; + index++; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + // Potential overflow + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = answer * 10 + num - '0'; + // if sign < 0, (-1 * sign + 1) / 2 = 1 + // else, (-1 * sign + 1) / 2 = 0 + if ((uint)answer > (uint)short.MaxValue + (-1 * sign + 1) / 2) + goto FalseExit; // Overflow + + if ((uint)index >= (uint)text.Length) + goto Done; + if (!ParserHelpers.IsDigit(text[index])) + goto Done; + + // Guaranteed overflow + goto FalseExit; + } + + FalseExit: + bytesConsumed = default; + value = default; + return false; + + Done: + bytesConsumed = index; + value = (short)(answer * sign); + return true; + } + + private static bool TryParseInt32D(ReadOnlySpan text, out int value, out int bytesConsumed) + { + if (text.Length < 1) + goto FalseExit; + + int sign = 1; + int index = 0; + int num = text[index]; + if (num == '-') + { + sign = -1; + index++; + if ((uint)index >= (uint)text.Length) + goto FalseExit; + num = text[index]; + } + else if (num == '+') + { + index++; + if ((uint)index >= (uint)text.Length) + goto FalseExit; + num = text[index]; + } + + int answer = 0; + + if (ParserHelpers.IsDigit(num)) + { + if (num == '0') + { + do + { + index++; + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + } while (num == '0'); + if (!ParserHelpers.IsDigit(num)) + goto Done; + } + + answer = num - '0'; + index++; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + // Potential overflow + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + if (answer > int.MaxValue / 10) + goto FalseExit; // Overflow + answer = answer * 10 + num - '0'; + // if sign < 0, (-1 * sign + 1) / 2 = 1 + // else, (-1 * sign + 1) / 2 = 0 + if ((uint)answer > (uint)int.MaxValue + (-1 * sign + 1) / 2) + goto FalseExit; // Overflow + + if ((uint)index >= (uint)text.Length) + goto Done; + if (!ParserHelpers.IsDigit(text[index])) + goto Done; + + // Guaranteed overflow + goto FalseExit; + } + + FalseExit: + bytesConsumed = default; + value = default; + return false; + + Done: + bytesConsumed = index; + value = answer * sign; + return true; + } + + private static bool TryParseInt64D(ReadOnlySpan text, out long value, out int bytesConsumed) + { + if (text.Length < 1) + { + bytesConsumed = 0; + value = default; + return false; + } + + int indexOfFirstDigit = 0; + int sign = 1; + if (text[0] == '-') + { + indexOfFirstDigit = 1; + sign = -1; + + if (text.Length <= indexOfFirstDigit) + { + bytesConsumed = 0; + value = default; + return false; + } + } + else if (text[0] == '+') + { + indexOfFirstDigit = 1; + + if (text.Length <= indexOfFirstDigit) + { + bytesConsumed = 0; + value = default; + return false; + } + } + + int overflowLength = ParserHelpers.Int64OverflowLength + indexOfFirstDigit; + + // Parse the first digit separately. If invalid here, we need to return false. + long firstDigit = text[indexOfFirstDigit] - 48; // '0' + if (firstDigit < 0 || firstDigit > 9) + { + bytesConsumed = 0; + value = default; + return false; + } + ulong parsedValue = (ulong)firstDigit; + + if (text.Length < overflowLength) + { + // Length is less than Parsers.Int64OverflowLength; overflow is not possible + for (int index = indexOfFirstDigit + 1; index < text.Length; index++) + { + long nextDigit = text[index] - 48; // '0' + if (nextDigit < 0 || nextDigit > 9) + { + bytesConsumed = index; + value = ((long)parsedValue) * sign; + return true; + } + parsedValue = parsedValue * 10 + (ulong)nextDigit; + } + } + else + { + // Length is greater than Parsers.Int64OverflowLength; overflow is only possible after Parsers.Int64OverflowLength + // digits. There may be no overflow after Parsers.Int64OverflowLength if there are leading zeroes. + for (int index = indexOfFirstDigit + 1; index < overflowLength - 1; index++) + { + long nextDigit = text[index] - 48; // '0' + if (nextDigit < 0 || nextDigit > 9) + { + bytesConsumed = index; + value = ((long)parsedValue) * sign; + return true; + } + parsedValue = parsedValue * 10 + (ulong)nextDigit; + } + for (int index = overflowLength - 1; index < text.Length; index++) + { + long nextDigit = text[index] - 48; // '0' + if (nextDigit < 0 || nextDigit > 9) + { + bytesConsumed = index; + value = ((long)parsedValue) * sign; + return true; + } + // If parsedValue > (long.MaxValue / 10), any more appended digits will cause overflow. + // if parsedValue == (long.MaxValue / 10), any nextDigit greater than 7 or 8 (depending on sign) implies overflow. + bool positive = sign > 0; + bool nextDigitTooLarge = nextDigit > 8 || (positive && nextDigit > 7); + if (parsedValue > long.MaxValue / 10 || parsedValue == long.MaxValue / 10 && nextDigitTooLarge) + { + bytesConsumed = 0; + value = default; + return false; + } + parsedValue = parsedValue * 10 + (ulong)nextDigit; + } + } + + bytesConsumed = text.Length; + value = ((long)parsedValue) * sign; + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Signed.N.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Signed.N.cs new file mode 100644 index 0000000000..66aa994f6d --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Signed.N.cs @@ -0,0 +1,29 @@ +// 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. + +namespace System.Buffers.Text +{ + public static partial class Utf8Parser + { + private static bool TryParseSByteN(ReadOnlySpan text, out sbyte value, out int bytesConsumed) + { + throw NotImplemented.ActiveIssue("https://github.com/dotnet/corefx/issues/24986"); + } + + private static bool TryParseInt16N(ReadOnlySpan text, out short value, out int bytesConsumed) + { + throw NotImplemented.ActiveIssue("https://github.com/dotnet/corefx/issues/24986"); + } + + private static bool TryParseInt32N(ReadOnlySpan text, out int value, out int bytesConsumed) + { + throw NotImplemented.ActiveIssue("https://github.com/dotnet/corefx/issues/24986"); + } + + private static bool TryParseInt64N(ReadOnlySpan text, out long value, out int bytesConsumed) + { + throw NotImplemented.ActiveIssue("https://github.com/dotnet/corefx/issues/24986"); + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Signed.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Signed.cs new file mode 100644 index 0000000000..e7f5009684 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Signed.cs @@ -0,0 +1,203 @@ +// 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. + +#if !netstandard +using Internal.Runtime.CompilerServices; +#else +using System.Runtime.CompilerServices; +#endif + +namespace System.Buffers.Text +{ + /// + /// Methods to parse common data types to Utf8 strings. + /// + public static partial class Utf8Parser + { + /// + /// Parses a SByte at the start of a Utf8 string. + /// + /// The Utf8 string to parse + /// Receives the parsed value + /// On a successful parse, receives the length in bytes of the substring that was parsed + /// Expected format of the Utf8 string + /// + /// true for success. "bytesConsumed" contains the length in bytes of the substring that was parsed. + /// false if the string was not syntactically valid or an overflow or underflow occurred. "bytesConsumed" is set to 0. + /// + /// + /// Formats supported: + /// G/g (default) + /// D/d 32767 + /// N/n 32,767 + /// X/x 7fff + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan text, out sbyte value, out int bytesConsumed, char standardFormat = default) + { + switch (standardFormat) + { + case (default): + case 'g': + case 'G': + case 'd': + case 'D': + return TryParseSByteD(text, out value, out bytesConsumed); + + case 'n': + case 'N': + return TryParseSByteN(text, out value, out bytesConsumed); + + case 'x': + case 'X': + value = default; + return TryParseByteX(text, out Unsafe.As(ref value), out bytesConsumed); + + default: + return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); + } + } + + /// + /// Parses an Int16 at the start of a Utf8 string. + /// + /// The Utf8 string to parse + /// Receives the parsed value + /// On a successful parse, receives the length in bytes of the substring that was parsed + /// Expected format of the Utf8 string + /// + /// true for success. "bytesConsumed" contains the length in bytes of the substring that was parsed. + /// false if the string was not syntactically valid or an overflow or underflow occurred. "bytesConsumed" is set to 0. + /// + /// + /// Formats supported: + /// G/g (default) + /// D/d 32767 + /// N/n 32,767 + /// X/x 7fff + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryParse(ReadOnlySpan text, out short value, out int bytesConsumed, char standardFormat = default) + { + switch (standardFormat) + { + case (default): + case 'g': + case 'G': + case 'd': + case 'D': + return TryParseInt16D(text, out value, out bytesConsumed); + + case 'n': + case 'N': + return TryParseInt16N(text, out value, out bytesConsumed); + + case 'x': + case 'X': + value = default; + return TryParseUInt16X(text, out Unsafe.As(ref value), out bytesConsumed); + + default: + return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); + } + } + + /// + /// Parses an Int32 at the start of a Utf8 string. + /// + /// The Utf8 string to parse + /// Receives the parsed value + /// On a successful parse, receives the length in bytes of the substring that was parsed + /// Expected format of the Utf8 string + /// + /// true for success. "bytesConsumed" contains the length in bytes of the substring that was parsed. + /// false if the string was not syntactically valid or an overflow or underflow occurred. "bytesConsumed" is set to 0. + /// + /// + /// Formats supported: + /// G/g (default) + /// D/d 32767 + /// N/n 32,767 + /// X/x 7fff + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryParse(ReadOnlySpan text, out int value, out int bytesConsumed, char standardFormat = default) + { + switch (standardFormat) + { + case (default): + case 'g': + case 'G': + case 'd': + case 'D': + return TryParseInt32D(text, out value, out bytesConsumed); + + case 'n': + case 'N': + return TryParseInt32N(text, out value, out bytesConsumed); + + case 'x': + case 'X': + value = default; + return TryParseUInt32X(text, out Unsafe.As(ref value), out bytesConsumed); + + default: + return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); + } + } + + /// + /// Parses an Int64 at the start of a Utf8 string. + /// + /// The Utf8 string to parse + /// Receives the parsed value + /// On a successful parse, receives the length in bytes of the substring that was parsed + /// Expected format of the Utf8 string + /// + /// true for success. "bytesConsumed" contains the length in bytes of the substring that was parsed. + /// false if the string was not syntactically valid or an overflow or underflow occurred. "bytesConsumed" is set to 0. + /// + /// + /// Formats supported: + /// G/g (default) + /// D/d 32767 + /// N/n 32,767 + /// X/x 7fff + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryParse(ReadOnlySpan text, out long value, out int bytesConsumed, char standardFormat = default) + { + switch (standardFormat) + { + case (default): + case 'g': + case 'G': + case 'd': + case 'D': + return TryParseInt64D(text, out value, out bytesConsumed); + + case 'n': + case 'N': + return TryParseInt64N(text, out value, out bytesConsumed); + + case 'x': + case 'X': + value = default; + return TryParseUInt64X(text, out Unsafe.As(ref value), out bytesConsumed); + + default: + return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); + } + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Unsigned.D.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Unsigned.D.cs new file mode 100644 index 0000000000..99aeceddd3 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Unsigned.D.cs @@ -0,0 +1,354 @@ +// 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. + +namespace System.Buffers.Text +{ + public static partial class Utf8Parser + { + private static bool TryParseByteD(ReadOnlySpan text, out byte value, out int bytesConsumed) + { + if (text.Length < 1) + goto FalseExit; + + int index = 0; + int num = text[index]; + int answer = 0; + + if (ParserHelpers.IsDigit(num)) + { + if (num == '0') + { + do + { + index++; + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + } while (num == '0'); + if (!ParserHelpers.IsDigit(num)) + goto Done; + } + + answer = num - '0'; + index++; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + // Potential overflow + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = answer * 10 + num - '0'; + if ((uint)answer > byte.MaxValue) + goto FalseExit; // Overflow + + if ((uint)index >= (uint)text.Length) + goto Done; + if (!ParserHelpers.IsDigit(text[index])) + goto Done; + + // Guaranteed overflow + goto FalseExit; + } + + FalseExit: + bytesConsumed = default; + value = default; + return false; + + Done: + bytesConsumed = index; + value = (byte)answer; + return true; + } + + private static bool TryParseUInt16D(ReadOnlySpan text, out ushort value, out int bytesConsumed) + { + if (text.Length < 1) + goto FalseExit; + + int index = 0; + int num = text[index]; + int answer = 0; + + if (ParserHelpers.IsDigit(num)) + { + if (num == '0') + { + do + { + index++; + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + } while (num == '0'); + if (!ParserHelpers.IsDigit(num)) + goto Done; + } + + answer = num - '0'; + index++; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + // Potential overflow + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = answer * 10 + num - '0'; + if ((uint)answer > ushort.MaxValue) + goto FalseExit; // Overflow + + if ((uint)index >= (uint)text.Length) + goto Done; + if (!ParserHelpers.IsDigit(text[index])) + goto Done; + + // Guaranteed overflow + goto FalseExit; + } + + FalseExit: + bytesConsumed = default; + value = default; + return false; + + Done: + bytesConsumed = index; + value = (ushort)answer; + return true; + } + + private static bool TryParseUInt32D(ReadOnlySpan text, out uint value, out int bytesConsumed) + { + if (text.Length < 1) + goto FalseExit; + + int index = 0; + int num = text[index]; + int answer = 0; + + if (ParserHelpers.IsDigit(num)) + { + if (num == '0') + { + do + { + index++; + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + } while (num == '0'); + if (!ParserHelpers.IsDigit(num)) + goto Done; + } + + answer = num - '0'; + index++; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + answer = 10 * answer + num - '0'; + + // Potential overflow + if ((uint)index >= (uint)text.Length) + goto Done; + num = text[index]; + if (!ParserHelpers.IsDigit(num)) + goto Done; + index++; + if (((uint)answer) > uint.MaxValue / 10 || (((uint)answer) == uint.MaxValue / 10 && num > '5')) + goto FalseExit; // Overflow + answer = answer * 10 + num - '0'; + + if ((uint)index >= (uint)text.Length) + goto Done; + if (!ParserHelpers.IsDigit(text[index])) + goto Done; + + // Guaranteed overflow + goto FalseExit; + } + + FalseExit: + bytesConsumed = default; + value = default; + return false; + + Done: + bytesConsumed = index; + value = (uint)answer; + return true; + } + + private static bool TryParseUInt64D(ReadOnlySpan text, out ulong value, out int bytesConsumed) + { + if (text.Length < 1) + { + bytesConsumed = 0; + value = default; + return false; + } + + // Parse the first digit separately. If invalid here, we need to return false. + ulong firstDigit = text[0] - 48u; // '0' + if (firstDigit > 9) + { + bytesConsumed = 0; + value = default; + return false; + } + ulong parsedValue = firstDigit; + + if (text.Length < ParserHelpers.Int64OverflowLength) + { + // Length is less than Parsers.Int64OverflowLength; overflow is not possible + for (int index = 1; index < text.Length; index++) + { + ulong nextDigit = text[index] - 48u; // '0' + if (nextDigit > 9) + { + bytesConsumed = index; + value = parsedValue; + return true; + } + parsedValue = parsedValue * 10 + nextDigit; + } + } + else + { + // Length is greater than Parsers.Int64OverflowLength; overflow is only possible after Parsers.Int64OverflowLength + // digits. There may be no overflow after Parsers.Int64OverflowLength if there are leading zeroes. + for (int index = 1; index < ParserHelpers.Int64OverflowLength - 1; index++) + { + ulong nextDigit = text[index] - 48u; // '0' + if (nextDigit > 9) + { + bytesConsumed = index; + value = parsedValue; + return true; + } + parsedValue = parsedValue * 10 + nextDigit; + } + for (int index = ParserHelpers.Int64OverflowLength - 1; index < text.Length; index++) + { + ulong nextDigit = text[index] - 48u; // '0' + if (nextDigit > 9) + { + bytesConsumed = index; + value = parsedValue; + return true; + } + // If parsedValue > (ulong.MaxValue / 10), any more appended digits will cause overflow. + // if parsedValue == (ulong.MaxValue / 10), any nextDigit greater than 5 implies overflow. + if (parsedValue > ulong.MaxValue / 10 || (parsedValue == ulong.MaxValue / 10 && nextDigit > 5)) + { + bytesConsumed = 0; + value = default; + return false; + } + parsedValue = parsedValue * 10 + nextDigit; + } + } + + bytesConsumed = text.Length; + value = parsedValue; + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Unsigned.N.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Unsigned.N.cs new file mode 100644 index 0000000000..0990336af3 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Unsigned.N.cs @@ -0,0 +1,32 @@ +// 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. + +namespace System.Buffers.Text +{ + // + // Parsing unsigned integers for the 'N' format. Emulating int.TryParse(NumberStyles.AllowThousands | NumberStyles.Integer | NumberStyles.AllowDecimalPoint) + // + public static partial class Utf8Parser + { + private static bool TryParseByteN(ReadOnlySpan text, out byte value, out int bytesConsumed) + { + throw NotImplemented.ActiveIssue("https://github.com/dotnet/corefx/issues/24986"); + } + + private static bool TryParseUInt16N(ReadOnlySpan text, out ushort value, out int bytesConsumed) + { + throw NotImplemented.ActiveIssue("https://github.com/dotnet/corefx/issues/24986"); + } + + private static bool TryParseUInt32N(ReadOnlySpan text, out uint value, out int bytesConsumed) + { + throw NotImplemented.ActiveIssue("https://github.com/dotnet/corefx/issues/24986"); + } + + private static bool TryParseUInt64N(ReadOnlySpan text, out ulong value, out int bytesConsumed) + { + throw NotImplemented.ActiveIssue("https://github.com/dotnet/corefx/issues/24986"); + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Unsigned.X.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Unsigned.X.cs new file mode 100644 index 0000000000..0e320a229a --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Unsigned.X.cs @@ -0,0 +1,341 @@ +// 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. + +namespace System.Buffers.Text +{ + public static partial class Utf8Parser + { + private static bool TryParseByteX(ReadOnlySpan text, out byte value, out int bytesConsumed) + { + if (text.Length < 1) + { + bytesConsumed = 0; + value = default; + return false; + } + byte nextCharacter; + byte nextDigit; + + // Cache Parsers.s_HexLookup in order to avoid static constructor checks + byte[] hexLookup = ParserHelpers.s_hexLookup; + + // Parse the first digit separately. If invalid here, we need to return false. + nextCharacter = text[0]; + nextDigit = hexLookup[nextCharacter]; + if (nextDigit == 0xFF) + { + bytesConsumed = 0; + value = default; + return false; + } + uint parsedValue = nextDigit; + + if (text.Length <= ParserHelpers.ByteOverflowLengthHex) + { + // Length is less than or equal to Parsers.ByteOverflowLengthHex; overflow is not possible + for (int index = 1; index < text.Length; index++) + { + nextCharacter = text[index]; + nextDigit = hexLookup[nextCharacter]; + if (nextDigit == 0xFF) + { + bytesConsumed = index; + value = (byte)(parsedValue); + return true; + } + parsedValue = (parsedValue << 4) + nextDigit; + } + } + else + { + // Length is greater than Parsers.ByteOverflowLengthHex; overflow is only possible after Parsers.ByteOverflowLengthHex + // digits. There may be no overflow after Parsers.ByteOverflowLengthHex if there are leading zeroes. + for (int index = 1; index < ParserHelpers.ByteOverflowLengthHex; index++) + { + nextCharacter = text[index]; + nextDigit = hexLookup[nextCharacter]; + if (nextDigit == 0xFF) + { + bytesConsumed = index; + value = (byte)(parsedValue); + return true; + } + parsedValue = (parsedValue << 4) + nextDigit; + } + for (int index = ParserHelpers.ByteOverflowLengthHex; index < text.Length; index++) + { + nextCharacter = text[index]; + nextDigit = hexLookup[nextCharacter]; + if (nextDigit == 0xFF) + { + bytesConsumed = index; + value = (byte)(parsedValue); + return true; + } + // If we try to append a digit to anything larger than byte.MaxValue / 0x10, there will be overflow + if (parsedValue > byte.MaxValue / 0x10) + { + bytesConsumed = 0; + value = default; + return false; + } + parsedValue = (parsedValue << 4) + nextDigit; + } + } + + bytesConsumed = text.Length; + value = (byte)(parsedValue); + return true; + } + + private static bool TryParseUInt16X(ReadOnlySpan text, out ushort value, out int bytesConsumed) + { + if (text.Length < 1) + { + bytesConsumed = 0; + value = default; + return false; + } + byte nextCharacter; + byte nextDigit; + + // Cache Parsers.s_HexLookup in order to avoid static constructor checks + byte[] hexLookup = ParserHelpers.s_hexLookup; + + // Parse the first digit separately. If invalid here, we need to return false. + nextCharacter = text[0]; + nextDigit = hexLookup[nextCharacter]; + if (nextDigit == 0xFF) + { + bytesConsumed = 0; + value = default; + return false; + } + uint parsedValue = nextDigit; + + if (text.Length <= ParserHelpers.Int16OverflowLengthHex) + { + // Length is less than or equal to Parsers.Int16OverflowLengthHex; overflow is not possible + for (int index = 1; index < text.Length; index++) + { + nextCharacter = text[index]; + nextDigit = hexLookup[nextCharacter]; + if (nextDigit == 0xFF) + { + bytesConsumed = index; + value = (ushort)(parsedValue); + return true; + } + parsedValue = (parsedValue << 4) + nextDigit; + } + } + else + { + // Length is greater than Parsers.Int16OverflowLengthHex; overflow is only possible after Parsers.Int16OverflowLengthHex + // digits. There may be no overflow after Parsers.Int16OverflowLengthHex if there are leading zeroes. + for (int index = 1; index < ParserHelpers.Int16OverflowLengthHex; index++) + { + nextCharacter = text[index]; + nextDigit = hexLookup[nextCharacter]; + if (nextDigit == 0xFF) + { + bytesConsumed = index; + value = (ushort)(parsedValue); + return true; + } + parsedValue = (parsedValue << 4) + nextDigit; + } + for (int index = ParserHelpers.Int16OverflowLengthHex; index < text.Length; index++) + { + nextCharacter = text[index]; + nextDigit = hexLookup[nextCharacter]; + if (nextDigit == 0xFF) + { + bytesConsumed = index; + value = (ushort)(parsedValue); + return true; + } + // If we try to append a digit to anything larger than ushort.MaxValue / 0x10, there will be overflow + if (parsedValue > ushort.MaxValue / 0x10) + { + bytesConsumed = 0; + value = default; + return false; + } + parsedValue = (parsedValue << 4) + nextDigit; + } + } + + bytesConsumed = text.Length; + value = (ushort)(parsedValue); + return true; + } + + private static bool TryParseUInt32X(ReadOnlySpan text, out uint value, out int bytesConsumed) + { + if (text.Length < 1) + { + bytesConsumed = 0; + value = default; + return false; + } + byte nextCharacter; + byte nextDigit; + + // Cache Parsers.s_HexLookup in order to avoid static constructor checks + byte[] hexLookup = ParserHelpers.s_hexLookup; + + // Parse the first digit separately. If invalid here, we need to return false. + nextCharacter = text[0]; + nextDigit = hexLookup[nextCharacter]; + if (nextDigit == 0xFF) + { + bytesConsumed = 0; + value = default; + return false; + } + uint parsedValue = nextDigit; + + if (text.Length <= ParserHelpers.Int32OverflowLengthHex) + { + // Length is less than or equal to Parsers.Int32OverflowLengthHex; overflow is not possible + for (int index = 1; index < text.Length; index++) + { + nextCharacter = text[index]; + nextDigit = hexLookup[nextCharacter]; + if (nextDigit == 0xFF) + { + bytesConsumed = index; + value = parsedValue; + return true; + } + parsedValue = (parsedValue << 4) + nextDigit; + } + } + else + { + // Length is greater than Parsers.Int32OverflowLengthHex; overflow is only possible after Parsers.Int32OverflowLengthHex + // digits. There may be no overflow after Parsers.Int32OverflowLengthHex if there are leading zeroes. + for (int index = 1; index < ParserHelpers.Int32OverflowLengthHex; index++) + { + nextCharacter = text[index]; + nextDigit = hexLookup[nextCharacter]; + if (nextDigit == 0xFF) + { + bytesConsumed = index; + value = parsedValue; + return true; + } + parsedValue = (parsedValue << 4) + nextDigit; + } + for (int index = ParserHelpers.Int32OverflowLengthHex; index < text.Length; index++) + { + nextCharacter = text[index]; + nextDigit = hexLookup[nextCharacter]; + if (nextDigit == 0xFF) + { + bytesConsumed = index; + value = parsedValue; + return true; + } + // If we try to append a digit to anything larger than uint.MaxValue / 0x10, there will be overflow + if (parsedValue > uint.MaxValue / 0x10) + { + bytesConsumed = 0; + value = default; + return false; + } + parsedValue = (parsedValue << 4) + nextDigit; + } + } + + bytesConsumed = text.Length; + value = parsedValue; + return true; + } + + private static bool TryParseUInt64X(ReadOnlySpan text, out ulong value, out int bytesConsumed) + { + if (text.Length < 1) + { + bytesConsumed = 0; + value = default; + return false; + } + byte nextCharacter; + byte nextDigit; + + // Cache Parsers.s_HexLookup in order to avoid static constructor checks + byte[] hexLookup = ParserHelpers.s_hexLookup; + + // Parse the first digit separately. If invalid here, we need to return false. + nextCharacter = text[0]; + nextDigit = hexLookup[nextCharacter]; + if (nextDigit == 0xFF) + { + bytesConsumed = 0; + value = default; + return false; + } + ulong parsedValue = nextDigit; + + if (text.Length <= ParserHelpers.Int64OverflowLengthHex) + { + // Length is less than or equal to Parsers.Int64OverflowLengthHex; overflow is not possible + for (int index = 1; index < text.Length; index++) + { + nextCharacter = text[index]; + nextDigit = hexLookup[nextCharacter]; + if (nextDigit == 0xFF) + { + bytesConsumed = index; + value = parsedValue; + return true; + } + parsedValue = (parsedValue << 4) + nextDigit; + } + } + else + { + // Length is greater than Parsers.Int64OverflowLengthHex; overflow is only possible after Parsers.Int64OverflowLengthHex + // digits. There may be no overflow after Parsers.Int64OverflowLengthHex if there are leading zeroes. + for (int index = 1; index < ParserHelpers.Int64OverflowLengthHex; index++) + { + nextCharacter = text[index]; + nextDigit = hexLookup[nextCharacter]; + if (nextDigit == 0xFF) + { + bytesConsumed = index; + value = parsedValue; + return true; + } + parsedValue = (parsedValue << 4) + nextDigit; + } + for (int index = ParserHelpers.Int64OverflowLengthHex; index < text.Length; index++) + { + nextCharacter = text[index]; + nextDigit = hexLookup[nextCharacter]; + if (nextDigit == 0xFF) + { + bytesConsumed = index; + value = parsedValue; + return true; + } + // If we try to append a digit to anything larger than ulong.MaxValue / 0x10, there will be overflow + if (parsedValue > ulong.MaxValue / 0x10) + { + bytesConsumed = 0; + value = default; + return false; + } + parsedValue = (parsedValue << 4) + nextDigit; + } + } + + bytesConsumed = text.Length; + value = parsedValue; + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Unsigned.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Unsigned.cs new file mode 100644 index 0000000000..78dfdebcd9 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Integer.Unsigned.cs @@ -0,0 +1,192 @@ +// 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. + +namespace System.Buffers.Text +{ + public static partial class Utf8Parser + { + /// + /// Parses a Byte at the start of a Utf8 string. + /// + /// The Utf8 string to parse + /// Receives the parsed value + /// On a successful parse, receives the length in bytes of the substring that was parsed + /// Expected format of the Utf8 string + /// + /// true for success. "bytesConsumed" contains the length in bytes of the substring that was parsed. + /// false if the string was not syntactically valid or an overflow or underflow occurred. "bytesConsumed" is set to 0. + /// + /// + /// Formats supported: + /// G/g (default) + /// D/d 32767 + /// N/n 32,767 + /// X/x 7fff + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryParse(ReadOnlySpan text, out byte value, out int bytesConsumed, char standardFormat = default) + { + switch (standardFormat) + { + case (default): + case 'g': + case 'G': + case 'd': + case 'D': + return TryParseByteD(text, out value, out bytesConsumed); + + case 'n': + case 'N': + return TryParseByteN(text, out value, out bytesConsumed); + + case 'x': + case 'X': + return TryParseByteX(text, out value, out bytesConsumed); + + default: + return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); + } + } + + /// + /// Parses a UInt16 at the start of a Utf8 string. + /// + /// The Utf8 string to parse + /// Receives the parsed value + /// On a successful parse, receives the length in bytes of the substring that was parsed + /// Expected format of the Utf8 string + /// + /// true for success. "bytesConsumed" contains the length in bytes of the substring that was parsed. + /// false if the string was not syntactically valid or an overflow or underflow occurred. "bytesConsumed" is set to 0. + /// + /// + /// Formats supported: + /// G/g (default) + /// D/d 32767 + /// N/n 32,767 + /// X/x 7fff + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan text, out ushort value, out int bytesConsumed, char standardFormat = default) + { + switch (standardFormat) + { + case (default): + case 'g': + case 'G': + case 'd': + case 'D': + return TryParseUInt16D(text, out value, out bytesConsumed); + + case 'n': + case 'N': + return TryParseUInt16N(text, out value, out bytesConsumed); + + case 'x': + case 'X': + return TryParseUInt16X(text, out value, out bytesConsumed); + + default: + return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); + } + } + + /// + /// Parses a UInt32 at the start of a Utf8 string. + /// + /// The Utf8 string to parse + /// Receives the parsed value + /// On a successful parse, receives the length in bytes of the substring that was parsed + /// Expected format of the Utf8 string + /// + /// true for success. "bytesConsumed" contains the length in bytes of the substring that was parsed. + /// false if the string was not syntactically valid or an overflow or underflow occurred. "bytesConsumed" is set to 0. + /// + /// + /// Formats supported: + /// G/g (default) + /// D/d 32767 + /// N/n 32,767 + /// X/x 7fff + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan text, out uint value, out int bytesConsumed, char standardFormat = default) + { + switch (standardFormat) + { + case (default): + case 'g': + case 'G': + case 'd': + case 'D': + return TryParseUInt32D(text, out value, out bytesConsumed); + + case 'n': + case 'N': + return TryParseUInt32N(text, out value, out bytesConsumed); + + case 'x': + case 'X': + return TryParseUInt32X(text, out value, out bytesConsumed); + + default: + return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); + } + } + + /// + /// Parses a UInt64 at the start of a Utf8 string. + /// + /// The Utf8 string to parse + /// Receives the parsed value + /// On a successful parse, receives the length in bytes of the substring that was parsed + /// Expected format of the Utf8 string + /// + /// true for success. "bytesConsumed" contains the length in bytes of the substring that was parsed. + /// false if the string was not syntactically valid or an overflow or underflow occurred. "bytesConsumed" is set to 0. + /// + /// + /// Formats supported: + /// G/g (default) + /// D/d 32767 + /// N/n 32,767 + /// X/x 7fff + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan text, out ulong value, out int bytesConsumed, char standardFormat = default) + { + switch (standardFormat) + { + case (default): + case 'g': + case 'G': + case 'd': + case 'D': + return TryParseUInt64D(text, out value, out bytesConsumed); + + case 'n': + case 'N': + return TryParseUInt64N(text, out value, out bytesConsumed); + + case 'x': + case 'X': + return TryParseUInt64X(text, out value, out bytesConsumed); + + default: + return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); + } + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Number.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Number.cs new file mode 100644 index 0000000000..4f45a5c2c2 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.Number.cs @@ -0,0 +1,240 @@ +// 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.Diagnostics; + +namespace System.Buffers.Text +{ + public static partial class Utf8Parser + { + [Flags] + private enum ParseNumberOptions + { + AllowExponent = 0x00000001, + } + + private static bool TryParseNumber(ReadOnlySpan text, ref NumberBuffer number, out int bytesConsumed, ParseNumberOptions options, out bool textUsedExponentNotation) + { + Debug.Assert(number.Digits[0] == 0 && number.Scale == 0 && !number.IsNegative, "Number not initialized to default(NumberBuffer)"); + + textUsedExponentNotation = false; + + if (text.Length == 0) + { + bytesConsumed = 0; + return false; + } + + Span digits = number.Digits; + + int srcIndex = 0; + int dstIndex = 0; + + // Consume the leading sign if any. + byte c = text[srcIndex]; + switch (c) + { + case Utf8Constants.Minus: + number.IsNegative = true; + goto case Utf8Constants.Plus; + + case Utf8Constants.Plus: + srcIndex++; + if (srcIndex == text.Length) + { + bytesConsumed = 0; + return false; + } + c = text[srcIndex]; + break; + + default: + break; + } + + int startIndexDigitsBeforeDecimal = srcIndex; + + // Throw away any leading zeroes + while (srcIndex != text.Length) + { + c = text[srcIndex]; + if (c != '0') + break; + srcIndex++; + } + + if (srcIndex == text.Length) + { + digits[0] = 0; + number.Scale = 0; + bytesConsumed = srcIndex; + number.CheckConsistency(); + return true; + } + + int startIndexNonLeadingDigitsBeforeDecimal = srcIndex; + while (srcIndex != text.Length) + { + c = text[srcIndex]; + if ((c - 48u) > 9) + break; + srcIndex++; + } + + int numDigitsBeforeDecimal = srcIndex - startIndexDigitsBeforeDecimal; + int numNonLeadingDigitsBeforeDecimal = srcIndex - startIndexNonLeadingDigitsBeforeDecimal; + + Debug.Assert(dstIndex == 0); + int numNonLeadingDigitsBeforeDecimalToCopy = Math.Min(numNonLeadingDigitsBeforeDecimal, NumberBuffer.BufferSize - 1); + text.Slice(startIndexNonLeadingDigitsBeforeDecimal, numNonLeadingDigitsBeforeDecimalToCopy).CopyTo(digits); + dstIndex = numNonLeadingDigitsBeforeDecimalToCopy; + number.Scale = numNonLeadingDigitsBeforeDecimal; + + if (srcIndex == text.Length) + { + bytesConsumed = srcIndex; + number.CheckConsistency(); + return true; + } + + int numDigitsAfterDecimal = 0; + if (c == Utf8Constants.Period) + { + // + // Parse the digits after the decimal point. + // + + srcIndex++; + int startIndexDigitsAfterDecimal = srcIndex; + while (srcIndex != text.Length) + { + c = text[srcIndex]; + if ((c - 48u) > 9) + break; + srcIndex++; + } + numDigitsAfterDecimal = srcIndex - startIndexDigitsAfterDecimal; + + int startIndexOfDigitsAfterDecimalToCopy = startIndexDigitsAfterDecimal; + if (dstIndex == 0) + { + // Not copied any digits to the Number struct yet. This means we must continue discarding leading zeroes even though they appeared after the decimal point. + while (startIndexOfDigitsAfterDecimalToCopy < srcIndex && text[startIndexOfDigitsAfterDecimalToCopy] == '0') + { + number.Scale--; + startIndexOfDigitsAfterDecimalToCopy++; + } + } + + int numDigitsAfterDecimalToCopy = Math.Min(srcIndex - startIndexOfDigitsAfterDecimalToCopy, NumberBuffer.BufferSize - dstIndex - 1); + text.Slice(startIndexOfDigitsAfterDecimalToCopy, numDigitsAfterDecimalToCopy).CopyTo(digits.Slice(dstIndex)); + dstIndex += numDigitsAfterDecimalToCopy; + // We "should" really NUL terminate, but there are multiple places we'd have to do this and it is a precondition that the caller pass in a fully zero=initialized Number. + + if (srcIndex == text.Length) + { + if (numDigitsBeforeDecimal == 0 && numDigitsAfterDecimal == 0) + { + // For compatibility. You can say "5." and ".5" but you can't say "." + bytesConsumed = 0; + return false; + } + + bytesConsumed = srcIndex; + number.CheckConsistency(); + return true; + } + } + + if (numDigitsBeforeDecimal == 0 && numDigitsAfterDecimal == 0) + { + bytesConsumed = 0; + return false; + } + + if ((c & ~0x20u) != 'E') + { + bytesConsumed = srcIndex; + number.CheckConsistency(); + return true; + } + + // + // Parse the exponent after the "E" + // + textUsedExponentNotation = true; + srcIndex++; + + if ((options & ParseNumberOptions.AllowExponent) == 0) + { + bytesConsumed = 0; + return false; + } + + if (srcIndex == text.Length) + { + bytesConsumed = 0; + return false; + } + + bool exponentIsNegative = false; + c = text[srcIndex]; + switch (c) + { + case Utf8Constants.Minus: + exponentIsNegative = true; + goto case Utf8Constants.Plus; + + case Utf8Constants.Plus: + srcIndex++; + if (srcIndex == text.Length) + { + bytesConsumed = 0; + return false; + } + c = text[srcIndex]; + break; + + default: + break; + } + + if (!Utf8Parser.TryParseUInt32D(text.Slice(srcIndex), out uint absoluteExponent, out int bytesConsumedByExponent)) + { + bytesConsumed = 0; + return false; + } + + srcIndex += bytesConsumedByExponent; + + if (exponentIsNegative) + { + if (number.Scale < int.MinValue + (long)absoluteExponent) + { + // A scale underflow means all non-zero digits are all so far to the right of the decimal point, no + // number format we have will be able to see them. Just pin the scale at the absolute minimum + // and let the converter produce a 0 with the max precision available for that type. + number.Scale = int.MinValue; + } + else + { + number.Scale -= (int)absoluteExponent; + } + } + else + { + if (number.Scale > int.MaxValue - (long)absoluteExponent) + { + bytesConsumed = 0; + return false; + } + number.Scale += (int)absoluteExponent; + } + + bytesConsumed = srcIndex; + number.CheckConsistency(); + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.TimeSpan.BigG.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.TimeSpan.BigG.cs new file mode 100644 index 0000000000..6c4d2265fa --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.TimeSpan.BigG.cs @@ -0,0 +1,132 @@ +// 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. + +namespace System.Buffers.Text +{ + public static partial class Utf8Parser + { + private static bool TryParseTimeSpanBigG(ReadOnlySpan text, out TimeSpan value, out int bytesConsumed) + { + int srcIndex = 0; + byte c = default; + while (srcIndex != text.Length) + { + c = text[srcIndex]; + if (!(c == ' ' || c == '\t')) + break; + srcIndex++; + } + + if (srcIndex == text.Length) + { + value = default; + bytesConsumed = 0; + return false; + } + + bool isNegative = false; + if (c == Utf8Constants.Minus) + { + isNegative = true; + srcIndex++; + if (srcIndex == text.Length) + { + value = default; + bytesConsumed = 0; + return false; + } + } + + if (!TryParseUInt32D(text.Slice(srcIndex), out uint days, out int justConsumed)) + { + value = default; + bytesConsumed = 0; + return false; + } + srcIndex += justConsumed; + + if (srcIndex == text.Length || text[srcIndex++] != Utf8Constants.Colon) + { + value = default; + bytesConsumed = 0; + return false; + } + + if (!TryParseUInt32D(text.Slice(srcIndex), out uint hours, out justConsumed)) + { + value = default; + bytesConsumed = 0; + return false; + } + srcIndex += justConsumed; + + if (srcIndex == text.Length || text[srcIndex++] != Utf8Constants.Colon) + { + value = default; + bytesConsumed = 0; + return false; + } + + if (!TryParseUInt32D(text.Slice(srcIndex), out uint minutes, out justConsumed)) + { + value = default; + bytesConsumed = 0; + return false; + } + srcIndex += justConsumed; + + if (srcIndex == text.Length || text[srcIndex++] != Utf8Constants.Colon) + { + value = default; + bytesConsumed = 0; + return false; + } + + if (!TryParseUInt32D(text.Slice(srcIndex), out uint seconds, out justConsumed)) + { + value = default; + bytesConsumed = 0; + return false; + } + srcIndex += justConsumed; + + if (srcIndex == text.Length || text[srcIndex++] != Utf8Constants.Period) + { + value = default; + bytesConsumed = 0; + return false; + } + + if (!TryParseTimeSpanFraction(text.Slice(srcIndex), out uint fraction, out justConsumed)) + { + value = default; + bytesConsumed = 0; + return false; + } + + srcIndex += justConsumed; + + if (!TryCreateTimeSpan(isNegative: isNegative, days: days, hours: hours, minutes: minutes, seconds: seconds, fraction: fraction, out value)) + { + value = default; + bytesConsumed = 0; + return false; + } + + // + // There cannot legally be a sixth number. If the next character is a period or colon, treat this as a error as it's likely + // to indicate the start of a sixth number. Otherwise, treat as end of parse with data left over. + // + if (srcIndex != text.Length && (text[srcIndex] == Utf8Constants.Period || text[srcIndex] == Utf8Constants.Colon)) + { + value = default; + bytesConsumed = 0; + return false; + } + + bytesConsumed = srcIndex; + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.TimeSpan.C.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.TimeSpan.C.cs new file mode 100644 index 0000000000..5f50f3ec2e --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.TimeSpan.C.cs @@ -0,0 +1,67 @@ +// 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. + +namespace System.Buffers.Text +{ + public static partial class Utf8Parser + { + private static bool TryParseTimeSpanC(ReadOnlySpan text, out TimeSpan value, out int bytesConsumed) + { + TimeSpanSplitter s = default; + if (!s.TrySplitTimeSpan(text, periodUsedToSeparateDay: true, out bytesConsumed)) + { + value = default; + return false; + } + + bool isNegative = s.IsNegative; + + bool success; + switch (s.Separators) + { + case 0x00000000: // dd + success = TryCreateTimeSpan(isNegative: isNegative, days: s.V1, hours: 0, minutes: 0, seconds: 0, fraction: 0, out value); + break; + + case 0x01000000: // hh:mm + success = TryCreateTimeSpan(isNegative: isNegative, days: 0, hours: s.V1, minutes: s.V2, seconds: 0, fraction: 0, out value); + break; + + case 0x02010000: // dd.hh:mm + success = TryCreateTimeSpan(isNegative: isNegative, days: s.V1, hours: s.V2, minutes: s.V3, seconds: 0, fraction: 0, out value); + break; + + case 0x01010000: // hh:mm:ss + success = TryCreateTimeSpan(isNegative: isNegative, days: 0, hours: s.V1, minutes: s.V2, seconds: s.V3, fraction: 0, out value); + break; + + case 0x02010100: // dd.hh:mm:ss + success = TryCreateTimeSpan(isNegative: isNegative, days: s.V1, hours: s.V2, minutes: s.V3, seconds: s.V4, fraction: 0, out value); + break; + + case 0x01010200: // hh:mm:ss.fffffff + success = TryCreateTimeSpan(isNegative: isNegative, days: 0, hours: s.V1, minutes: s.V2, seconds: s.V3, fraction: s.V4, out value); + break; + + case 0x02010102: // dd.hh:mm:ss.fffffff + success = TryCreateTimeSpan(isNegative: isNegative, days: s.V1, hours: s.V2, minutes: s.V3, seconds: s.V4, fraction: s.V5, out value); + break; + + default: + value = default; + success = false; + break; + } + + if (!success) + { + bytesConsumed = 0; + return false; + } + + return true; + } + } +} + diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.TimeSpan.LittleG.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.TimeSpan.LittleG.cs new file mode 100644 index 0000000000..ebf885ce9f --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.TimeSpan.LittleG.cs @@ -0,0 +1,62 @@ +// 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. + +namespace System.Buffers.Text +{ + public static partial class Utf8Parser + { + private static bool TryParseTimeSpanLittleG(ReadOnlySpan text, out TimeSpan value, out int bytesConsumed) + { + TimeSpanSplitter s = default; + if (!s.TrySplitTimeSpan(text, periodUsedToSeparateDay: false, out bytesConsumed)) + { + value = default; + return false; + } + + bool isNegative = s.IsNegative; + + bool success; + switch (s.Separators) + { + case 0x00000000: // dd + success = TryCreateTimeSpan(isNegative: isNegative, days: s.V1, hours: 0, minutes: 0, seconds: 0, fraction: 0, out value); + break; + + case 0x01000000: // hh:mm + success = TryCreateTimeSpan(isNegative: isNegative, days: 0, hours: s.V1, minutes: s.V2, seconds: 0, fraction: 0, out value); + break; + + case 0x01010000: // hh:mm:ss + success = TryCreateTimeSpan(isNegative: isNegative, days: 0, hours: s.V1, minutes: s.V2, seconds: s.V3, fraction: 0, out value); + break; + + case 0x01010100: // dd:hh:mm:ss + success = TryCreateTimeSpan(isNegative: isNegative, days: s.V1, hours: s.V2, minutes: s.V3, seconds: s.V4, fraction: 0, out value); + break; + + case 0x01010200: // hh:mm:ss.fffffff + success = TryCreateTimeSpan(isNegative: isNegative, days: 0, hours: s.V1, minutes: s.V2, seconds: s.V3, fraction: s.V4, out value); + break; + + case 0x01010102: // dd:hh:mm:ss.fffffff + success = TryCreateTimeSpan(isNegative: isNegative, days: s.V1, hours: s.V2, minutes: s.V3, seconds: s.V4, fraction: s.V5, out value); + break; + + default: + value = default; + success = false; + break; + } + + if (!success) + { + bytesConsumed = 0; + return false; + } + + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.TimeSpan.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.TimeSpan.cs new file mode 100644 index 0000000000..e971718fbb --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.TimeSpan.cs @@ -0,0 +1,192 @@ +// 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.Diagnostics; + +namespace System.Buffers.Text +{ + public static partial class Utf8Parser + { + /// + /// Parses a TimeSpan at the start of a Utf8 string. + /// + /// The Utf8 string to parse + /// Receives the parsed value + /// On a successful parse, receives the length in bytes of the substring that was parsed + /// Expected format of the Utf8 string + /// + /// true for success. "bytesConsumed" contains the length in bytes of the substring that was parsed. + /// false if the string was not syntactically valid or an overflow or underflow occurred. "bytesConsumed" is set to 0. + /// + /// + /// Formats supported: + /// c/t/T (default) [-][d.]hh:mm:ss[.fffffff] (constant format) + /// G [-]d:hh:mm:ss.fffffff (general long) + /// g [-][d:]h:mm:ss[.f[f[f[f[f[f[f[]]]]]]] (general short) + /// + /// + /// System.FormatException if the format is not valid for this data type. + /// + public static bool TryParse(ReadOnlySpan text, out TimeSpan value, out int bytesConsumed, char standardFormat = default) + { + switch (standardFormat) + { + case (default): + case 'c': + case 't': + case 'T': + return TryParseTimeSpanC(text, out value, out bytesConsumed); + + case 'G': + return TryParseTimeSpanBigG(text, out value, out bytesConsumed); + + case 'g': + return TryParseTimeSpanLittleG(text, out value, out bytesConsumed); + + default: + return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); + } + } + + /// + /// Parse the fraction portion of a TimeSpan. Must be 1..7 digits. If fewer than 7, zeroes are implied to the right. If more than 7, the TimeSpan + /// parser rejects the string (even if the extra digits are all zeroes.) + /// + private static bool TryParseTimeSpanFraction(ReadOnlySpan text, out uint value, out int bytesConsumed) + { + int srcIndex = 0; + + if (srcIndex == text.Length) + { + value = default; + bytesConsumed = 0; + return false; + } + + uint digit = text[srcIndex] - 48u; // '0' + if (digit > 9) + { + value = default; + bytesConsumed = 0; + return false; + } + srcIndex++; + + uint fraction = digit; + int digitCount = 1; + + while (srcIndex != text.Length) + { + digit = text[srcIndex] - 48u; // '0' + if (digit > 9) + break; + srcIndex++; + digitCount++; + if (digitCount > Utf8Constants.DateTimeNumFractionDigits) + { + // Yes, TimeSpan fraction parsing is that picky. + value = default; + bytesConsumed = 0; + return false; + } + fraction = 10 * fraction + digit; + } + + switch (digitCount) + { + case 7: + break; + + case 6: + fraction *= 10; + break; + + case 5: + fraction *= 100; + break; + + case 4: + fraction *= 1000; + break; + + case 3: + fraction *= 10000; + break; + + case 2: + fraction *= 100000; + break; + + default: + Debug.Assert(digitCount == 1); + fraction *= 1000000; + break; + } + + value = fraction; + bytesConsumed = srcIndex; + return true; + } + + /// + /// Overflow-safe TryCreateTimeSpan + /// + private static bool TryCreateTimeSpan(bool isNegative, uint days, uint hours, uint minutes, uint seconds, uint fraction, out TimeSpan timeSpan) + { + const long MaxMilliSeconds = long.MaxValue / TimeSpan.TicksPerMillisecond; + const long MinMilliSeconds = long.MinValue / TimeSpan.TicksPerMillisecond; + + Debug.Assert(days >= 0 && hours >= 0 && minutes >= 0 && seconds >= 00 && fraction >= 0); + if (hours > 23 || minutes > 59 || seconds > 59) + { + timeSpan = default; + return false; + } + + Debug.Assert(fraction <= Utf8Constants.MaxDateTimeFraction); // This value comes from TryParseTimeSpanFraction() which already rejects any fraction string longer than 7 digits. + + long millisecondsWithoutFraction = (((long)days) * 3600 * 24 + ((long)hours) * 3600 + ((long)minutes) * 60 + seconds) * 1000; + + long ticks; + if (isNegative) + { + millisecondsWithoutFraction = -millisecondsWithoutFraction; + if (millisecondsWithoutFraction < MinMilliSeconds) + { + timeSpan = default; + return false; + } + + long ticksWithoutFraction = millisecondsWithoutFraction * TimeSpan.TicksPerMillisecond; + if (ticksWithoutFraction < long.MinValue + fraction) + { + timeSpan = default; + return false; + } + + ticks = ticksWithoutFraction - fraction; + } + else + { + if (millisecondsWithoutFraction > MaxMilliSeconds) + { + timeSpan = default; + return false; + } + + long ticksWithoutFraction = millisecondsWithoutFraction * TimeSpan.TicksPerMillisecond; + if (ticksWithoutFraction > long.MaxValue - fraction) + { + timeSpan = default; + return false; + } + + ticks = ticksWithoutFraction + fraction; + } + + timeSpan = new TimeSpan(ticks); + return true; + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.TimeSpanSplitter.cs b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.TimeSpanSplitter.cs new file mode 100644 index 0000000000..eb76509ee1 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Buffers/Text/Utf8Parser/Utf8Parser.TimeSpanSplitter.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. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; + +namespace System.Buffers.Text +{ + public static partial class Utf8Parser + { + private enum ComponentParseResult : byte + { + // Do not change or add values in this enum unless you review every use of the TimeSpanSplitter.Separators field. That field is an "array of four + // ComponentParseResults" encoded as a 32-bit integer with each of its four bytes containing one of 0 (NoMoreData), 1 (Colon) or 2 (Period). + // (So a value of 0x01010200 means the string parsed as "nn:nn:nn.nnnnnnn") + NoMoreData = 0, + Colon = 1, + Period = 2, + ParseFailure = 3, + } + + private struct TimeSpanSplitter + { + public uint V1; + public uint V2; + public uint V3; + public uint V4; + public uint V5; + + public bool IsNegative; + + // Encodes an "array of four ComponentParseResults" as a 32-bit integer with each of its four bytes containing one of 0 (NoMoreData), 1 (Colon) or 2 (Period). + // (So a value of 0x01010200 means the string parsed as "nn:nn:nn.nnnnnnn") + public uint Separators; + + public bool TrySplitTimeSpan(ReadOnlySpan text, bool periodUsedToSeparateDay, out int bytesConsumed) + { + int srcIndex = 0; + byte c = default; + + // Unlike many other data types, TimeSpan allow leading whitespace. + while (srcIndex != text.Length) + { + c = text[srcIndex]; + if (!(c == ' ' || c == '\t')) + break; + srcIndex++; + } + + if (srcIndex == text.Length) + { + bytesConsumed = 0; + return false; + } + + // Check for an option negative sign. ('+' is not allowed.) + if (c == Utf8Constants.Minus) + { + IsNegative = true; + srcIndex++; + if (srcIndex == text.Length) + { + bytesConsumed = 0; + return false; + } + } + + // From here, we terminate on anything that's not a digit, ':' or '.' The '.' is only allowed after at least three components have + // been specified. If we see it earlier, we'll assume that's an error and fail out rather than treating it as the end of data. + + // + // Timespan has to start with a number - parse the first one. + // + if (!TryParseUInt32D(text.Slice(srcIndex), out V1, out int justConsumed)) + { + bytesConsumed = 0; + return false; + } + srcIndex += justConsumed; + + ComponentParseResult result; + + // + // Split out the second number (if any) For the 'c' format, a period might validly appear here as it;s used both to separate the day and the fraction - however, + // the fraction is always the fourth component at earliest, so if we do see a period at this stage, always parse the integer as a regular integer, not as + // a fraction. + // + result = ParseComponent(text, neverParseAsFraction: periodUsedToSeparateDay, ref srcIndex, out V2); + if (result == ComponentParseResult.ParseFailure) + { + bytesConsumed = 0; + return false; + } + else if (result == ComponentParseResult.NoMoreData) + { + bytesConsumed = srcIndex; + return true; + } + else + { + Debug.Assert(result == ComponentParseResult.Colon || result == ComponentParseResult.Period); + Separators |= ((uint)result) << 24; + } + + // + // Split out the third number (if any) + // + result = ParseComponent(text, false, ref srcIndex, out V3); + if (result == ComponentParseResult.ParseFailure) + { + bytesConsumed = 0; + return false; + } + else if (result == ComponentParseResult.NoMoreData) + { + bytesConsumed = srcIndex; + return true; + } + else + { + Debug.Assert(result == ComponentParseResult.Colon || result == ComponentParseResult.Period); + Separators |= ((uint)result) << 16; + } + + // + // Split out the fourth number (if any) + // + result = ParseComponent(text, false, ref srcIndex, out V4); + if (result == ComponentParseResult.ParseFailure) + { + bytesConsumed = 0; + return false; + } + else if (result == ComponentParseResult.NoMoreData) + { + bytesConsumed = srcIndex; + return true; + } + else + { + Debug.Assert(result == ComponentParseResult.Colon || result == ComponentParseResult.Period); + Separators |= ((uint)result) << 8; + } + + // + // Split out the fifth number (if any) + // + result = ParseComponent(text, false, ref srcIndex, out V5); + if (result == ComponentParseResult.ParseFailure) + { + bytesConsumed = 0; + return false; + } + else if (result == ComponentParseResult.NoMoreData) + { + bytesConsumed = srcIndex; + return true; + } + else + { + Debug.Assert(result == ComponentParseResult.Colon || result == ComponentParseResult.Period); + Separators |= (uint)result; + } + + // + // There cannot legally be a sixth number. If the next character is a period or colon, treat this as a error as it's likely + // to indicate the start of a sixth number. Otherwise, treat as end of parse with data left over. + // + if (srcIndex != text.Length && (text[srcIndex] == Utf8Constants.Period || text[srcIndex] == Utf8Constants.Colon)) + { + bytesConsumed = 0; + return false; + } + + bytesConsumed = srcIndex; + return true; + } + + // + // Look for a separator followed by an unsigned integer. + // + private static ComponentParseResult ParseComponent(ReadOnlySpan text, bool neverParseAsFraction, ref int srcIndex, out uint value) + { + if (srcIndex == text.Length) + { + value = default; + return ComponentParseResult.NoMoreData; + } + + byte c = text[srcIndex]; + if (c == Utf8Constants.Colon || (c == Utf8Constants.Period && neverParseAsFraction)) + { + srcIndex++; + + if (!TryParseUInt32D(text.Slice(srcIndex), out value, out int bytesConsumed)) + { + value = default; + return ComponentParseResult.ParseFailure; + } + + srcIndex += bytesConsumed; + return c == Utf8Constants.Colon ? ComponentParseResult.Colon : ComponentParseResult.Period; + } + else if (c == Utf8Constants.Period) + { + srcIndex++; + + if (!TryParseTimeSpanFraction(text.Slice(srcIndex), out value, out int bytesConsumed)) + { + value = default; + return ComponentParseResult.ParseFailure; + } + + srcIndex += bytesConsumed; + return ComponentParseResult.Period; + } + else + { + value = default; + return ComponentParseResult.NoMoreData; + } + } + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/SpanExtensions.Fast.cs b/external/corefx/src/System.Memory/src/System/MemoryExtensions.Fast.cs similarity index 51% rename from external/corefx/src/System.Memory/src/System/SpanExtensions.Fast.cs rename to external/corefx/src/System.Memory/src/System/MemoryExtensions.Fast.cs index 364427b7a8..d232c7e13d 100644 --- a/external/corefx/src/System.Memory/src/System/SpanExtensions.Fast.cs +++ b/external/corefx/src/System.Memory/src/System/MemoryExtensions.Fast.cs @@ -2,15 +2,14 @@ // 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.Diagnostics; using System.Runtime.CompilerServices; namespace System { /// - /// Extension methods for Span<T>. + /// Extension methods for Span{T}, Memory{T}, and friends. /// - public static partial class SpanExtensions + public static partial class MemoryExtensions { /// /// Casts a Span of one primitive type to Span of bytes. @@ -47,6 +46,62 @@ namespace System /// Thrown when is null. public static ReadOnlySpan AsReadOnlySpan(this string text) => Span.AsReadOnlySpan(text); + /// + /// Creates a new readonly span over the portion of the target string. + /// + /// The target string. + /// The index at which to begin this slice. + /// Thrown when is null. + /// + /// Thrown when the specified index is not in range (<0 or >text.Length). + /// + public static ReadOnlySpan AsReadOnlySpan(this string text, int start) => Span.AsReadOnlySpan(text, start); + + /// + /// Creates a new readonly span over the portion of the target string. + /// + /// The target string. + /// The index at which to begin this slice. + /// The desired length for the slice (exclusive). + /// Thrown when is null. + /// + /// Thrown when the specified index or is not in range. + /// + public static ReadOnlySpan AsReadOnlySpan(this string text, int start, int length) => Span.AsReadOnlySpan(text, start, length); + + /// Creates a new over the portion of the target string. + /// The target string. + /// Thrown when is a null reference (Nothing in Visual Basic). + public static ReadOnlyMemory AsReadOnlyMemory(this string text) => Span.AsReadOnlyMemory(text); + + /// Creates a new over the portion of the target string. + /// The target string. + /// The index at which to begin this slice. + /// Thrown when is a null reference (Nothing in Visual Basic). + /// + /// Thrown when the specified index is not in range (<0 or >text.Length). + /// + public static ReadOnlyMemory AsReadOnlyMemory(this string text, int start) => Span.AsReadOnlyMemory(text, start); + + /// Creates a new over the portion of the target string. + /// The target string. + /// The index at which to begin this slice. + /// The desired length for the slice (exclusive). + /// Thrown when is a null reference (Nothing in Visual Basic). + /// + /// Thrown when the specified index or is not in range. + /// + public static ReadOnlyMemory AsReadOnlyMemory(this string text, int start, int length) => Span.AsReadOnlyMemory(text, start, length); + + /// Attempts to get the underlying from a . + /// The memory that may be wrapping a object. + /// The string. + /// The starting location in . + /// The number of items in . + /// + public static bool TryGetString(this ReadOnlyMemory readOnlyMemory, out string text, out int start, out int length) => + Span.TryGetString(readOnlyMemory, out text, out start, out length); + /// /// Casts a Span of one primitive type to another primitive type . /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety. @@ -62,7 +117,7 @@ namespace System /// Thrown if the Length property of the new Span would exceed Int32.MaxValue. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span NonPortableCast(this Span source) + public static Span NonPortableCast(this Span source) where TFrom : struct where TTo : struct => Span.NonPortableCast(source); @@ -84,7 +139,7 @@ namespace System [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan NonPortableCast(this ReadOnlySpan source) where TFrom : struct - where TTo : struct + where TTo : struct => Span.NonPortableCast(source); } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Memory/src/System/SpanExtensions.Portable.cs b/external/corefx/src/System.Memory/src/System/MemoryExtensions.Portable.cs similarity index 52% rename from external/corefx/src/System.Memory/src/System/SpanExtensions.Portable.cs rename to external/corefx/src/System.Memory/src/System/MemoryExtensions.Portable.cs index 8651704e1b..dc3ffad9ba 100644 --- a/external/corefx/src/System.Memory/src/System/SpanExtensions.Portable.cs +++ b/external/corefx/src/System.Memory/src/System/MemoryExtensions.Portable.cs @@ -8,9 +8,9 @@ using System.Runtime.CompilerServices; namespace System { /// - /// Extension methods for Span<T>. + /// Extension methods for Span{T}, Memory{T}, and friends. /// - public static partial class SpanExtensions + public static partial class MemoryExtensions { /// /// Casts a Span of one primitive type to Span of bytes. @@ -70,6 +70,117 @@ namespace System return new ReadOnlySpan(Unsafe.As>(text), StringAdjustment, text.Length); } + /// + /// Creates a new readonly span over the portion of the target string. + /// + /// The target string. + /// The index at which to begin this slice. + /// Thrown when is null. + /// + /// Thrown when the specified index is not in range (<0 or >text.Length). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan AsReadOnlySpan(this string text, int start) + { + if (text == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); + if ((uint)start > (uint)text.Length) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); + + return new ReadOnlySpan(Unsafe.As>(text), StringAdjustment + start * sizeof(char), text.Length - start); + } + + /// + /// Creates a new readonly span over the portion of the target string. + /// + /// The target string. + /// The index at which to begin this slice. + /// The desired length for the slice (exclusive). + /// Thrown when is null. + /// + /// Thrown when the specified index or is not in range. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan AsReadOnlySpan(this string text, int start, int length) + { + if (text == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); + if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); + + return new ReadOnlySpan(Unsafe.As>(text), StringAdjustment + start * sizeof(char), length); + } + + /// Creates a new over the portion of the target string. + /// The target string. + /// Thrown when is a null reference (Nothing in Visual Basic). + public static ReadOnlyMemory AsReadOnlyMemory(this string text) + { + if (text == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); + + return new ReadOnlyMemory(text, 0, text.Length); + } + + /// Creates a new over the portion of the target string. + /// The target string. + /// The index at which to begin this slice. + /// Thrown when is a null reference (Nothing in Visual Basic). + /// + /// Thrown when the specified index is not in range (<0 or >text.Length). + /// + public static ReadOnlyMemory AsReadOnlyMemory(this string text, int start) + { + if (text == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); + if ((uint)start > (uint)text.Length) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); + + return new ReadOnlyMemory(text, start, text.Length - start); + } + + /// Creates a new over the portion of the target string. + /// The target string. + /// The index at which to begin this slice. + /// The desired length for the slice (exclusive). + /// Thrown when is a null reference (Nothing in Visual Basic). + /// + /// Thrown when the specified index or is not in range. + /// + public static ReadOnlyMemory AsReadOnlyMemory(this string text, int start, int length) + { + if (text == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); + if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); + + return new ReadOnlyMemory(text, start, length); + } + + /// Attempts to get the underlying from a . + /// The memory that may be wrapping a object. + /// The string. + /// The starting location in . + /// The number of items in . + /// + public static bool TryGetString(this ReadOnlyMemory readOnlyMemory, out string text, out int start, out int length) + { + if (readOnlyMemory.GetObjectStartLength(out int offset, out int count) is string s) + { + text = s; + start = offset; + length = count; + return true; + } + else + { + text = null; + start = 0; + length = 0; + return false; + } + } + /// /// Casts a Span of one primitive type to another primitive type . /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety. @@ -128,8 +239,7 @@ namespace System return new ReadOnlySpan(Unsafe.As>(source.Pinnable), source.ByteOffset, newLength); } - - private static readonly IntPtr StringAdjustment = MeasureStringAdjustment(); + internal static readonly IntPtr StringAdjustment = MeasureStringAdjustment(); private static IntPtr MeasureStringAdjustment() { @@ -143,4 +253,4 @@ namespace System } } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Memory/src/System/MemoryExtensions.cs b/external/corefx/src/System.Memory/src/System/MemoryExtensions.cs new file mode 100644 index 0000000000..d886fd5864 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/MemoryExtensions.cs @@ -0,0 +1,1014 @@ +// 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.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#if !netstandard +using Internal.Runtime.CompilerServices; +#endif + +namespace System +{ + /// + /// Extension methods for Span{T}, Memory{T}, and friends. + /// + public static partial class MemoryExtensions + { + /// + /// Searches for the specified value and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T). + /// + /// The span to search. + /// The value to search for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOf(this Span span, T value) + where T : IEquatable + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.IndexOf( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + Unsafe.As(ref value), + span.Length); + return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), value, span.Length); + } + + /// + /// Searches for the specified sequence and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T). + /// + /// The span to search. + /// The sequence to search for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOf(this Span span, ReadOnlySpan value) + where T : IEquatable + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.IndexOf( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + span.Length, + ref Unsafe.As(ref MemoryMarshal.GetReference(value)), + value.Length); + return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); + } + + /// + /// Searches for the specified value and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T). + /// + /// The span to search. + /// The value to search for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int LastIndexOf(this Span span, T value) + where T : IEquatable + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.LastIndexOf( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + Unsafe.As(ref value), + span.Length); + return SpanHelpers.LastIndexOf(ref MemoryMarshal.GetReference(span), value, span.Length); + } + + /// + /// Searches for the specified sequence and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T). + /// + /// The span to search. + /// The sequence to search for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int LastIndexOf(this Span span, ReadOnlySpan value) + where T : IEquatable + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.LastIndexOf( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + span.Length, + ref Unsafe.As(ref MemoryMarshal.GetReference(value)), + value.Length); + return SpanHelpers.LastIndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); + } + + /// + /// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool SequenceEqual(this Span first, ReadOnlySpan second) + where T : IEquatable + { + int length = first.Length; + if (typeof(T) == typeof(byte)) + return length == second.Length && + SpanHelpers.SequenceEqual( + ref Unsafe.As(ref MemoryMarshal.GetReference(first)), + ref Unsafe.As(ref MemoryMarshal.GetReference(second)), + length); + return length == second.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(first), ref MemoryMarshal.GetReference(second), length); + } + + /// + /// Determines the relative order of the sequences being compared by comparing the elements using IComparable{T}.CompareTo(T). + /// + public static int SequenceCompareTo(this Span first, ReadOnlySpan second) + where T : IComparable + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.SequenceCompareTo( + ref Unsafe.As(ref MemoryMarshal.GetReference(first)), + first.Length, + ref Unsafe.As(ref MemoryMarshal.GetReference(second)), + second.Length); + return SpanHelpers.SequenceCompareTo(ref MemoryMarshal.GetReference(first), first.Length, ref MemoryMarshal.GetReference(second), second.Length); + } + + /// + /// Reverses the sequence of the elements in the entire span. + /// + public static void Reverse(this Span span) + { + ref T p = ref MemoryMarshal.GetReference(span); + int i = 0; + int j = span.Length - 1; + while (i < j) + { + T temp = Unsafe.Add(ref p, i); + Unsafe.Add(ref p, i) = Unsafe.Add(ref p, j); + Unsafe.Add(ref p, j) = temp; + i++; + j--; + } + } + + /// + /// Searches for the specified value and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T). + /// + /// The span to search. + /// The value to search for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOf(this ReadOnlySpan span, T value) + where T : IEquatable + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.IndexOf( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + Unsafe.As(ref value), + span.Length); + return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), value, span.Length); + } + + /// + /// Searches for the specified sequence and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T). + /// + /// The span to search. + /// The sequence to search for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOf(this ReadOnlySpan span, ReadOnlySpan value) + where T : IEquatable + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.IndexOf( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + span.Length, + ref Unsafe.As(ref MemoryMarshal.GetReference(value)), + value.Length); + return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); + } + + /// + /// Searches for the specified value and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T). + /// + /// The span to search. + /// The value to search for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int LastIndexOf(this ReadOnlySpan span, T value) + where T : IEquatable + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.LastIndexOf( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + Unsafe.As(ref value), + span.Length); + return SpanHelpers.LastIndexOf(ref MemoryMarshal.GetReference(span), value, span.Length); + } + + /// + /// Searches for the specified sequence and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T). + /// + /// The span to search. + /// The sequence to search for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int LastIndexOf(this ReadOnlySpan span, ReadOnlySpan value) + where T : IEquatable + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.LastIndexOf( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + span.Length, + ref Unsafe.As(ref MemoryMarshal.GetReference(value)), + value.Length); + return SpanHelpers.LastIndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); + } + + /// + /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. + /// + /// The span to search. + /// One of the values to search for. + /// One of the values to search for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOfAny(this Span span, T value0, T value1) + where T : IEquatable + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.IndexOfAny( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + Unsafe.As(ref value0), + Unsafe.As(ref value1), + span.Length); + + return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length); + } + + /// + /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. + /// + /// The span to search. + /// One of the values to search for. + /// One of the values to search for. + /// One of the values to search for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOfAny(this Span span, T value0, T value1, T value2) + where T : IEquatable + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.IndexOfAny( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + Unsafe.As(ref value0), + Unsafe.As(ref value1), + Unsafe.As(ref value2), + span.Length); + + return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length); + } + + /// + /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. + /// + /// The span to search. + /// The set of values to search for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOfAny(this Span span, ReadOnlySpan values) + where T : IEquatable + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.IndexOfAny( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + span.Length, + ref Unsafe.As(ref MemoryMarshal.GetReference(values)), + values.Length); + + return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length); + } + + /// + /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. + /// + /// The span to search. + /// One of the values to search for. + /// One of the values to search for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOfAny(this ReadOnlySpan span, T value0, T value1) + where T : IEquatable + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.IndexOfAny( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + Unsafe.As(ref value0), + Unsafe.As(ref value1), + span.Length); + + return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length); + } + + /// + /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. + /// + /// The span to search. + /// One of the values to search for. + /// One of the values to search for. + /// One of the values to search for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOfAny(this ReadOnlySpan span, T value0, T value1, T value2) + where T : IEquatable + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.IndexOfAny( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + Unsafe.As(ref value0), + Unsafe.As(ref value1), + Unsafe.As(ref value2), + span.Length); + + return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length); + } + + /// + /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. + /// + /// The span to search. + /// The set of values to search for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOfAny(this ReadOnlySpan span, ReadOnlySpan values) + where T : IEquatable + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.IndexOfAny( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + span.Length, + ref Unsafe.As(ref MemoryMarshal.GetReference(values)), + values.Length); + + return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length); + } + + /// + /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1. + /// + /// The span to search. + /// One of the values to search for. + /// One of the values to search for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int LastIndexOfAny(this Span span, T value0, T value1) + where T : IEquatable + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.LastIndexOfAny( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + Unsafe.As(ref value0), + Unsafe.As(ref value1), + span.Length); + return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length); + } + + /// + /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1. + /// + /// The span to search. + /// One of the values to search for. + /// One of the values to search for. + /// One of the values to search for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int LastIndexOfAny(this Span span, T value0, T value1, T value2) + where T : IEquatable + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.LastIndexOfAny( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + Unsafe.As(ref value0), + Unsafe.As(ref value1), + Unsafe.As(ref value2), + span.Length); + return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length); + } + + /// + /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1. + /// + /// The span to search. + /// The set of values to search for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int LastIndexOfAny(this Span span, ReadOnlySpan values) + where T : IEquatable + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.LastIndexOfAny( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + span.Length, + ref Unsafe.As(ref MemoryMarshal.GetReference(values)), + values.Length); + return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length); + } + + /// + /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1. + /// + /// The span to search. + /// One of the values to search for. + /// One of the values to search for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int LastIndexOfAny(this ReadOnlySpan span, T value0, T value1) + where T : IEquatable + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.LastIndexOfAny( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + Unsafe.As(ref value0), + Unsafe.As(ref value1), + span.Length); + return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length); + } + + /// + /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1. + /// + /// The span to search. + /// One of the values to search for. + /// One of the values to search for. + /// One of the values to search for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int LastIndexOfAny(this ReadOnlySpan span, T value0, T value1, T value2) + where T : IEquatable + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.LastIndexOfAny( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + Unsafe.As(ref value0), + Unsafe.As(ref value1), + Unsafe.As(ref value2), + span.Length); + return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length); + } + + /// + /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1. + /// + /// The span to search. + /// The set of values to search for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int LastIndexOfAny(this ReadOnlySpan span, ReadOnlySpan values) + where T : IEquatable + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.LastIndexOfAny( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + span.Length, + ref Unsafe.As(ref MemoryMarshal.GetReference(values)), + values.Length); + return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length); + } + + /// + /// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool SequenceEqual(this ReadOnlySpan first, ReadOnlySpan second) + where T : IEquatable + { + int length = first.Length; + if (typeof(T) == typeof(byte)) + return length == second.Length && + SpanHelpers.SequenceEqual( + ref Unsafe.As(ref MemoryMarshal.GetReference(first)), + ref Unsafe.As(ref MemoryMarshal.GetReference(second)), + length); + return length == second.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(first), ref MemoryMarshal.GetReference(second), length); + } + + /// + /// Determines the relative order of the sequences being compared by comparing the elements using IComparable{T}.CompareTo(T). + /// + public static int SequenceCompareTo(this ReadOnlySpan first, ReadOnlySpan second) + where T : IComparable + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.SequenceCompareTo( + ref Unsafe.As(ref MemoryMarshal.GetReference(first)), + first.Length, + ref Unsafe.As(ref MemoryMarshal.GetReference(second)), + second.Length); + return SpanHelpers.SequenceCompareTo(ref MemoryMarshal.GetReference(first), first.Length, ref MemoryMarshal.GetReference(second), second.Length); + } + + /// + /// Determines whether the specified sequence appears at the start of the span. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool StartsWith(this Span span, ReadOnlySpan value) + where T : IEquatable + { + int valueLength = value.Length; + if (typeof(T) == typeof(byte)) + return valueLength <= span.Length && + SpanHelpers.SequenceEqual( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + ref Unsafe.As(ref MemoryMarshal.GetReference(value)), + valueLength); + return valueLength <= span.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(value), valueLength); + } + + /// + /// Determines whether the specified sequence appears at the start of the span. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool StartsWith(this ReadOnlySpan span, ReadOnlySpan value) + where T : IEquatable + { + int valueLength = value.Length; + if (typeof(T) == typeof(byte)) + return valueLength <= span.Length && + SpanHelpers.SequenceEqual( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + ref Unsafe.As(ref MemoryMarshal.GetReference(value)), + valueLength); + return valueLength <= span.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(value), valueLength); + } + + /// + /// Determines whether the specified sequence appears at the end of the span. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool EndsWith(this Span span, ReadOnlySpan value) + where T : IEquatable + { + int spanLength = span.Length; + int valueLength = value.Length; + if (typeof(T) == typeof(byte)) + return valueLength <= spanLength && + SpanHelpers.SequenceEqual( + ref Unsafe.As(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), spanLength - valueLength)), + ref Unsafe.As(ref MemoryMarshal.GetReference(value)), + valueLength); + return valueLength <= spanLength && + SpanHelpers.SequenceEqual( + ref Unsafe.Add(ref MemoryMarshal.GetReference(span), spanLength - valueLength), + ref MemoryMarshal.GetReference(value), + valueLength); + } + + /// + /// Determines whether the specified sequence appears at the end of the span. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool EndsWith(this ReadOnlySpan span, ReadOnlySpan value) + where T : IEquatable + { + int spanLength = span.Length; + int valueLength = value.Length; + if (typeof(T) == typeof(byte)) + return valueLength <= spanLength && + SpanHelpers.SequenceEqual( + ref Unsafe.As(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), spanLength - valueLength)), + ref Unsafe.As(ref MemoryMarshal.GetReference(value)), + valueLength); + return valueLength <= spanLength && + SpanHelpers.SequenceEqual( + ref Unsafe.Add(ref MemoryMarshal.GetReference(span), spanLength - valueLength), + ref MemoryMarshal.GetReference(value), + valueLength); + } + + /// + /// Creates a new span over the portion of the target array. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span AsSpan(this T[] array) + { + return new Span(array); + } + + /// + /// Creates a new span over the portion of the target array segment. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span AsSpan(this ArraySegment arraySegment) + { + return new Span(arraySegment.Array, arraySegment.Offset, arraySegment.Count); + } + + /// + /// Creates a new readonly span over the entire target array. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan AsReadOnlySpan(this T[] array) + { + return new ReadOnlySpan(array); + } + + /// + /// Creates a new readonly span over the entire target span. + /// + public static ReadOnlySpan AsReadOnlySpan(this Span span) => span; + + /// + /// Creates a new readonly span over the target array segment. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan AsReadOnlySpan(this ArraySegment arraySegment) + { + return new ReadOnlySpan(arraySegment.Array, arraySegment.Offset, arraySegment.Count); + } + + /// + /// Creates a new readonly memory over the entire target memory. + /// + public static ReadOnlyMemory AsReadOnlyMemory(this Memory memory) => memory; + + /// + /// Copies the contents of the array into the span. If the source + /// and destinations overlap, this method behaves as if the original values in + /// a temporary location before the destination is overwritten. + /// + ///The array to copy items from. + /// The span to copy items into. + /// + /// Thrown when the destination Span is shorter than the source array. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CopyTo(this T[] array, Span destination) + { + new ReadOnlySpan(array).CopyTo(destination); + } + + /// + /// Copies the contents of the array into the memory. If the source + /// and destinations overlap, this method behaves as if the original values are in + /// a temporary location before the destination is overwritten. + /// + ///The array to copy items from. + /// The memory to copy items into. + /// + /// Thrown when the destination is shorter than the source array. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CopyTo(this T[] array, Memory destination) + { + array.CopyTo(destination.Span); + } + + // + // Overlaps + // ======== + // + // The following methods can be used to determine if two sequences + // overlap in memory. + // + // Two sequences overlap if they have positions in common and neither + // is empty. Empty sequences do not overlap with any other sequence. + // + // If two sequences overlap, the element offset is the number of + // elements by which the second sequence is offset from the first + // sequence (i.e., second minus first). An exception is thrown if the + // number is not a whole number, which can happen when a sequence of a + // smaller type is cast to a sequence of a larger type with unsafe code + // or NonPortableCast. If the sequences do not overlap, the offset is + // meaningless and arbitrarily set to zero. + // + // Implementation + // -------------- + // + // Implementing this correctly is quite tricky due of two problems: + // + // * If the sequences refer to two different objects on the managed + // heap, the garbage collector can move them freely around or change + // their relative order in memory. + // + // * The distance between two sequences can be greater than + // int.MaxValue (on a 32-bit system) or long.MaxValue (on a 64-bit + // system). + // + // (For simplicity, the following text assumes a 32-bit system, but + // everything also applies to a 64-bit system if every 32 is replaced a + // 64.) + // + // The first problem is solved by calculating the distance with exactly + // one atomic operation. If the garbage collector happens to move the + // sequences afterwards and the sequences overlapped before, they will + // still overlap after the move and their distance hasn't changed. If + // the sequences did not overlap, the distance can change but the + // sequences still won't overlap. + // + // The second problem is solved by making all addresses relative to the + // start of the first sequence and performing all operations in + // unsigned integer arithmetic modulo 2³². + // + // Example + // ------- + // + // Let's say there are two sequences, x and y. Let + // + // ref T xRef = MemoryMarshal.GetReference(x) + // uint xLength = x.Length * Unsafe.SizeOf() + // ref T yRef = MemoryMarshal.GetReference(y) + // uint yLength = y.Length * Unsafe.SizeOf() + // + // Visually, the two sequences are located somewhere in the 32-bit + // address space as follows: + // + // [----------------------------------------------) normal address space + // 0 2³² + // [------------------) first sequence + // xRef xRef + xLength + // [--------------------------) . second sequence + // yRef . yRef + yLength + // : . . . + // : . . . + // . . . + // . . . + // . . . + // [----------------------------------------------) relative address space + // 0 . . 2³² + // [------------------) : first sequence + // x1 . x2 : + // -------------) [------------- second sequence + // y2 y1 + // + // The idea is to make all addresses relative to xRef: Let x1 be the + // start address of x in this relative address space, x2 the end + // address of x, y1 the start address of y, and y2 the end address of + // y: + // + // nuint x1 = 0 + // nuint x2 = xLength + // nuint y1 = (nuint)Unsafe.ByteOffset(xRef, yRef) + // nuint y2 = y1 + yLength + // + // xRef relative to xRef is 0. + // + // x2 is simply x1 + xLength. This cannot overflow. + // + // yRef relative to xRef is (yRef - xRef). If (yRef - xRef) is + // negative, casting it to an unsigned 32-bit integer turns it into + // (yRef - xRef + 2³²). So, in the example above, y1 moves to the right + // of x2. + // + // y2 is simply y1 + yLength. Note that this can overflow, as in the + // example above, which must be avoided. + // + // The two sequences do *not* overlap if y is entirely in the space + // right of x in the relative address space. (It can't be left of it!) + // + // (y1 >= x2) && (y2 <= 2³²) + // + // Inversely, they do overlap if + // + // (y1 < x2) || (y2 > 2³²) + // + // After substituting x2 and y2 with their respective definition: + // + // == (y1 < xLength) || (y1 + yLength > 2³²) + // + // Since yLength can't be greater than the size of the address space, + // the overflow can be avoided as follows: + // + // == (y1 < xLength) || (y1 > 2³² - yLength) + // + // However, 2³² cannot be stored in an unsigned 32-bit integer, so one + // more change is needed to keep doing everything with unsigned 32-bit + // integers: + // + // == (y1 < xLength) || (y1 > -yLength) + // + // Due to modulo arithmetic, this gives exactly same result *except* if + // yLength is zero, since 2³² - 0 is 0 and not 2³². So the case + // y.IsEmpty must be handled separately first. + // + + /// + /// Determines whether two sequences overlap in memory. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Overlaps(this Span first, ReadOnlySpan second) + { + return Overlaps((ReadOnlySpan)first, second); + } + + /// + /// Determines whether two sequences overlap in memory and outputs the element offset. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Overlaps(this Span first, ReadOnlySpan second, out int elementOffset) + { + return Overlaps((ReadOnlySpan)first, second, out elementOffset); + } + + /// + /// Determines whether two sequences overlap in memory. + /// + public static bool Overlaps(this ReadOnlySpan first, ReadOnlySpan second) + { + if (first.IsEmpty || second.IsEmpty) + { + return false; + } + + IntPtr byteOffset = Unsafe.ByteOffset( + ref MemoryMarshal.GetReference(first), + ref MemoryMarshal.GetReference(second)); + + if (Unsafe.SizeOf() == sizeof(int)) + { + return (uint)byteOffset < (uint)(first.Length * Unsafe.SizeOf()) || + (uint)byteOffset > (uint)-(second.Length * Unsafe.SizeOf()); + } + else + { + return (ulong)byteOffset < (ulong)((long)first.Length * Unsafe.SizeOf()) || + (ulong)byteOffset > (ulong)-((long)second.Length * Unsafe.SizeOf()); + } + } + + /// + /// Determines whether two sequences overlap in memory and outputs the element offset. + /// + public static bool Overlaps(this ReadOnlySpan first, ReadOnlySpan second, out int elementOffset) + { + if (first.IsEmpty || second.IsEmpty) + { + elementOffset = 0; + return false; + } + + IntPtr byteOffset = Unsafe.ByteOffset( + ref MemoryMarshal.GetReference(first), + ref MemoryMarshal.GetReference(second)); + + if (Unsafe.SizeOf() == sizeof(int)) + { + if ((uint)byteOffset < (uint)(first.Length * Unsafe.SizeOf()) || + (uint)byteOffset > (uint)-(second.Length * Unsafe.SizeOf())) + { + if ((int)byteOffset % Unsafe.SizeOf() != 0) + ThrowHelper.ThrowArgumentException_OverlapAlignmentMismatch(); + + elementOffset = (int)byteOffset / Unsafe.SizeOf(); + return true; + } + else + { + elementOffset = 0; + return false; + } + } + else + { + if ((ulong)byteOffset < (ulong)((long)first.Length * Unsafe.SizeOf()) || + (ulong)byteOffset > (ulong)-((long)second.Length * Unsafe.SizeOf())) + { + if ((long)byteOffset % Unsafe.SizeOf() != 0) + ThrowHelper.ThrowArgumentException_OverlapAlignmentMismatch(); + + elementOffset = (int)((long)byteOffset / Unsafe.SizeOf()); + return true; + } + else + { + elementOffset = 0; + return false; + } + } + } + + /// + /// Searches an entire sorted for a value + /// using the specified generic interface. + /// + /// The element type of the span. + /// The sorted to search. + /// The to use when comparing. + /// + /// The zero-based index of in the sorted , + /// if is found; otherwise, a negative number that is the bitwise complement + /// of the index of the next element that is larger than or, if there is + /// no larger element, the bitwise complement of . + /// + /// + /// is . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int BinarySearch( + this Span span, IComparable comparable) + { + return BinarySearch>(span, comparable); + } + + /// + /// Searches an entire sorted for a value + /// using the specified generic type. + /// + /// The element type of the span. + /// The specific type of . + /// The sorted to search. + /// The to use when comparing. + /// + /// The zero-based index of in the sorted , + /// if is found; otherwise, a negative number that is the bitwise complement + /// of the index of the next element that is larger than or, if there is + /// no larger element, the bitwise complement of . + /// + /// + /// is . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int BinarySearch( + this Span span, TComparable comparable) + where TComparable : IComparable + { + return BinarySearch((ReadOnlySpan)span, comparable); + } + + /// + /// Searches an entire sorted for the specified + /// using the specified generic type. + /// + /// The element type of the span. + /// The specific type of . + /// The sorted to search. + /// The object to locate. The value can be null for reference types. + /// The to use when comparing. + /// /// + /// The zero-based index of in the sorted , + /// if is found; otherwise, a negative number that is the bitwise complement + /// of the index of the next element that is larger than or, if there is + /// no larger element, the bitwise complement of . + /// + /// + /// is . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int BinarySearch( + this Span span, T value, TComparer comparer) + where TComparer : IComparer + { + return BinarySearch((ReadOnlySpan)span, value, comparer); + } + + /// + /// Searches an entire sorted for a value + /// using the specified generic interface. + /// + /// The element type of the span. + /// The sorted to search. + /// The to use when comparing. + /// + /// The zero-based index of in the sorted , + /// if is found; otherwise, a negative number that is the bitwise complement + /// of the index of the next element that is larger than or, if there is + /// no larger element, the bitwise complement of . + /// + /// + /// is . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int BinarySearch( + this ReadOnlySpan span, IComparable comparable) + { + return BinarySearch>(span, comparable); + } + + /// + /// Searches an entire sorted for a value + /// using the specified generic type. + /// + /// The element type of the span. + /// The specific type of . + /// The sorted to search. + /// The to use when comparing. + /// + /// The zero-based index of in the sorted , + /// if is found; otherwise, a negative number that is the bitwise complement + /// of the index of the next element that is larger than or, if there is + /// no larger element, the bitwise complement of . + /// + /// + /// is . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int BinarySearch( + this ReadOnlySpan span, TComparable comparable) + where TComparable : IComparable + { + return SpanHelpers.BinarySearch(span, comparable); + } + + /// + /// Searches an entire sorted for the specified + /// using the specified generic type. + /// + /// The element type of the span. + /// The specific type of . + /// The sorted to search. + /// The object to locate. The value can be null for reference types. + /// The to use when comparing. + /// /// + /// The zero-based index of in the sorted , + /// if is found; otherwise, a negative number that is the bitwise complement + /// of the index of the next element that is larger than or, if there is + /// no larger element, the bitwise complement of . + /// + /// + /// is . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int BinarySearch( + this ReadOnlySpan span, T value, TComparer comparer) + where TComparer : IComparer + { + if (comparer == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparer); + + var comparable = new SpanHelpers.ComparerComparable( + value, comparer); + return BinarySearch(span, comparable); + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Number/Decimal.DecCalc.cs b/external/corefx/src/System.Memory/src/System/Number/Decimal.DecCalc.cs new file mode 100644 index 0000000000..7592000372 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Number/Decimal.DecCalc.cs @@ -0,0 +1,86 @@ +// 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. + +// +// This code is copied almost verbatim from the same-named file in CoreRT with mechanical changes to make it build outside of CoreLib. +// + +namespace System +{ + internal static class DecimalDecCalc + { + private static uint D32DivMod1E9(uint hi32, ref uint lo32) + { + ulong n = (ulong)hi32 << 32 | lo32; + lo32 = (uint)(n / 1000000000); + return (uint)(n % 1000000000); + } + + // Performs the equivalent of: + // + // uint modulo = value % 1e9; + // value = value / 1e9; + // return modulo; + // + internal static uint DecDivMod1E9(ref MutableDecimal value) + { + return D32DivMod1E9(D32DivMod1E9(D32DivMod1E9(0, ref value.High), ref value.Mid), ref value.Low); + } + + internal static void DecAddInt32(ref MutableDecimal value, uint i) + { + if (D32AddCarry(ref value.Low, i)) + { + if (D32AddCarry(ref value.Mid, 1)) + { + D32AddCarry(ref value.High, 1); + } + } + } + + private static bool D32AddCarry(ref uint value, uint i) + { + uint v = value; + uint sum = v + i; + value = sum; + return (sum < v) || (sum < i); + } + + internal static void DecMul10(ref MutableDecimal value) + { + MutableDecimal d = value; + DecShiftLeft(ref value); + DecShiftLeft(ref value); + DecAdd(ref value, d); + DecShiftLeft(ref value); + } + + private static void DecShiftLeft(ref MutableDecimal value) + { + uint c0 = (value.Low & 0x80000000) != 0 ? 1u : 0u; + uint c1 = (value.Mid & 0x80000000) != 0 ? 1u : 0u; + value.Low = value.Low << 1; + value.Mid = (value.Mid << 1) | c0; + value.High = (value.High << 1) | c1; + } + + private static void DecAdd(ref MutableDecimal value, MutableDecimal d) + { + if (D32AddCarry(ref value.Low, d.Low)) + { + if (D32AddCarry(ref value.Mid, 1)) + { + D32AddCarry(ref value.High, 1); + } + } + + if (D32AddCarry(ref value.Mid, d.Mid)) + { + D32AddCarry(ref value.High, 1); + } + + D32AddCarry(ref value.High, d.High); + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Number/Number.FormatAndParse.cs b/external/corefx/src/System.Memory/src/System/Number/Number.FormatAndParse.cs new file mode 100644 index 0000000000..3e79dfcd16 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Number/Number.FormatAndParse.cs @@ -0,0 +1,534 @@ +// 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.Diagnostics; +using System.Buffers.Text; + +#if !netstandard +using Internal.Runtime.CompilerServices; +#else +using System.Runtime.CompilerServices; +#endif + +// +// This code is copied almost verbatim from the same-named file in CoreRT with mechanical changes to Span-ify it. +// + +namespace System +{ + internal static partial class Number + { + // + // Convert a Number to a double. + // + internal static bool NumberBufferToDouble(ref NumberBuffer number, out double value) + { + double d = NumberToDouble(ref number); + + uint e = DoubleHelper.Exponent(d); + ulong m = DoubleHelper.Mantissa(d); + if (e == 0x7FF) + { + value = default; + return false; + } + + if (e == 0 && m == 0) + { + d = 0; + } + + value = d; + + return true; + } + + public static unsafe bool NumberBufferToDecimal(ref NumberBuffer number, ref decimal value) + { + MutableDecimal d = new MutableDecimal(); + + byte* p = number.UnsafeDigits; + int e = number.Scale; + if (*p == 0) + { + // To avoid risking an app-compat issue with pre 4.5 (where some app was illegally using Reflection to examine the internal scale bits), we'll only force + // the scale to 0 if the scale was previously positive (previously, such cases were unparsable to a bug.) + if (e > 0) + { + e = 0; + } + } + else + { + if (e > DECIMAL_PRECISION) + return false; + + while (((e > 0) || ((*p != 0) && (e > -28))) && + ((d.High < 0x19999999) || ((d.High == 0x19999999) && + ((d.Mid < 0x99999999) || ((d.Mid == 0x99999999) && + ((d.Low < 0x99999999) || ((d.Low == 0x99999999) && + (*p <= '5')))))))) + { + DecimalDecCalc.DecMul10(ref d); + if (*p != 0) + DecimalDecCalc.DecAddInt32(ref d, (uint)(*p++ - '0')); + e--; + } + + if (*p++ >= '5') + { + bool round = true; + if ((*(p - 1) == '5') && ((*(p - 2) % 2) == 0)) + { + // Check if previous digit is even, only if the when we are unsure whether hows to do + // Banker's rounding. For digits > 5 we will be rounding up anyway. + int count = 20; // Look at the next 20 digits to check to round + while ((*p == '0') && (count != 0)) + { + p++; + count--; + } + if ((*p == '\0') || (count == 0)) + round = false;// Do nothing + } + + if (round) + { + DecimalDecCalc.DecAddInt32(ref d, 1); + if ((d.High | d.Mid | d.Low) == 0) + { + // If we got here, the magnitude portion overflowed and wrapped back to 0 as the magnitude was already at the MaxValue point: + // + // 79,228,162,514,264,337,593,543,950,335e+X + // + // Manually force it to the correct result: + // + // 7,922,816,251,426,433,759,354,395,034e+(X+1) + // + // This code path can be reached by trying to parse the following as a Decimal: + // + // 0.792281625142643375935439503355e28 + // + + d.High = 0x19999999; + d.Mid = 0x99999999; + d.Low = 0x9999999A; + e++; + } + } + } + } + + if (e > 0) + return false; // Rounding may have caused its own overflow. For example, parsing "0.792281625142643375935439503355e29" will get here. + + if (e <= -DECIMAL_PRECISION) + { + // Parsing a large scale zero can give you more precision than fits in the decimal. + // This should only happen for actual zeros or very small numbers that round to zero. + d.High = 0; + d.Low = 0; + d.Mid = 0; + d.Scale = DECIMAL_PRECISION - 1; + } + else + { + d.Scale = -e; + } + d.IsNegative = number.IsNegative; + + value = Unsafe.As(ref d); + return true; + } + + public static void DecimalToNumber(decimal value, ref NumberBuffer number) + { + ref MutableDecimal d = ref Unsafe.As(ref value); + + Span buffer = number.Digits; + number.IsNegative = d.IsNegative; + + int index = DECIMAL_PRECISION; + + // Starting from the least significant bits, carve off nine decimal digits at a time and string-ize them (using the end of the + // buffer as a scratch buffer.) + while (d.Mid != 0 | d.High != 0) + { + uint modulo1E9 = DecimalDecCalc.DecDivMod1E9(ref d); + for (int digitCount = 0; digitCount < 9; digitCount++) + { + buffer[--index] = (byte)(modulo1E9 % 10 + '0'); + modulo1E9 /= 10; + } + } + + // We've finally whittled the decimal down to uint.MaxValue or less. Write the remaining digits but make sure no leading zeros get written. + uint remainder = d.Low; + while (remainder != 0) + { + buffer[--index] = (byte)(remainder % 10 + '0'); + remainder /= 10; + } + + int i = DECIMAL_PRECISION - index; + number.Scale = i - d.Scale; + + // Move the result from the end of the buffer to the beginning where we need it. + Span dst = number.Digits; + int dstIndex = 0; + while (--i >= 0) + { + dst[dstIndex++] = buffer[index++]; + } + dst[dstIndex] = 0; + + number.CheckConsistency(); + } + + // + // get 32-bit integer from at most 9 digits + // + private static uint DigitsToInt(ReadOnlySpan digits, int count) + { + bool success = Utf8Parser.TryParse(digits.Slice(0, count), out uint value, out int bytesConsumed, 'D'); + Debug.Assert(success); // This is only called on the contents of a trusted Number structure. + return value; + } + + // + // helper to multiply two 32-bit uints + // + private static ulong Mul32x32To64(uint a, uint b) + { + return a * (ulong)b; + } + + // + // multiply two numbers in the internal integer representation + // + private static ulong Mul64Lossy(ulong a, ulong b, ref int pexp) + { + // it's ok to lose some precision here - Mul64 will be called + // at most twice during the conversion, so the error won't propagate + // to any of the 53 significant bits of the result + ulong val = Mul32x32To64((uint)(a >> 32), (uint)(b >> 32)) + + (Mul32x32To64((uint)(a >> 32), (uint)(b)) >> 32) + + (Mul32x32To64((uint)(a), (uint)(b >> 32)) >> 32); + + // normalize + if ((val & 0x8000000000000000) == 0) + { + val <<= 1; + pexp -= 1; + } + + return val; + } + + // + // precomputed tables with powers of 10. These allows us to do at most + // two Mul64 during the conversion. This is important not only + // for speed, but also for precision because of Mul64 computes with 1 bit error. + // + + private static readonly ulong[] s_rgval64Power10 = + { + // powers of 10 + /*1*/ 0xa000000000000000, + /*2*/ 0xc800000000000000, + /*3*/ 0xfa00000000000000, + /*4*/ 0x9c40000000000000, + /*5*/ 0xc350000000000000, + /*6*/ 0xf424000000000000, + /*7*/ 0x9896800000000000, + /*8*/ 0xbebc200000000000, + /*9*/ 0xee6b280000000000, + /*10*/ 0x9502f90000000000, + /*11*/ 0xba43b74000000000, + /*12*/ 0xe8d4a51000000000, + /*13*/ 0x9184e72a00000000, + /*14*/ 0xb5e620f480000000, + /*15*/ 0xe35fa931a0000000, + + // powers of 0.1 + /*1*/ 0xcccccccccccccccd, + /*2*/ 0xa3d70a3d70a3d70b, + /*3*/ 0x83126e978d4fdf3c, + /*4*/ 0xd1b71758e219652e, + /*5*/ 0xa7c5ac471b478425, + /*6*/ 0x8637bd05af6c69b7, + /*7*/ 0xd6bf94d5e57a42be, + /*8*/ 0xabcc77118461ceff, + /*9*/ 0x89705f4136b4a599, + /*10*/ 0xdbe6fecebdedd5c2, + /*11*/ 0xafebff0bcb24ab02, + /*12*/ 0x8cbccc096f5088cf, + /*13*/ 0xe12e13424bb40e18, + /*14*/ 0xb424dc35095cd813, + /*15*/ 0x901d7cf73ab0acdc, + }; + + private static readonly sbyte[] s_rgexp64Power10 = + { + // exponents for both powers of 10 and 0.1 + /*1*/ 4, + /*2*/ 7, + /*3*/ 10, + /*4*/ 14, + /*5*/ 17, + /*6*/ 20, + /*7*/ 24, + /*8*/ 27, + /*9*/ 30, + /*10*/ 34, + /*11*/ 37, + /*12*/ 40, + /*13*/ 44, + /*14*/ 47, + /*15*/ 50, + }; + + private static readonly ulong[] s_rgval64Power10By16 = + { + // powers of 10^16 + /*1*/ 0x8e1bc9bf04000000, + /*2*/ 0x9dc5ada82b70b59e, + /*3*/ 0xaf298d050e4395d6, + /*4*/ 0xc2781f49ffcfa6d4, + /*5*/ 0xd7e77a8f87daf7fa, + /*6*/ 0xefb3ab16c59b14a0, + /*7*/ 0x850fadc09923329c, + /*8*/ 0x93ba47c980e98cde, + /*9*/ 0xa402b9c5a8d3a6e6, + /*10*/ 0xb616a12b7fe617a8, + /*11*/ 0xca28a291859bbf90, + /*12*/ 0xe070f78d39275566, + /*13*/ 0xf92e0c3537826140, + /*14*/ 0x8a5296ffe33cc92c, + /*15*/ 0x9991a6f3d6bf1762, + /*16*/ 0xaa7eebfb9df9de8a, + /*17*/ 0xbd49d14aa79dbc7e, + /*18*/ 0xd226fc195c6a2f88, + /*19*/ 0xe950df20247c83f8, + /*20*/ 0x81842f29f2cce373, + /*21*/ 0x8fcac257558ee4e2, + + // powers of 0.1^16 + /*1*/ 0xe69594bec44de160, + /*2*/ 0xcfb11ead453994c3, + /*3*/ 0xbb127c53b17ec165, + /*4*/ 0xa87fea27a539e9b3, + /*5*/ 0x97c560ba6b0919b5, + /*6*/ 0x88b402f7fd7553ab, + /*7*/ 0xf64335bcf065d3a0, + /*8*/ 0xddd0467c64bce4c4, + /*9*/ 0xc7caba6e7c5382ed, + /*10*/ 0xb3f4e093db73a0b7, + /*11*/ 0xa21727db38cb0053, + /*12*/ 0x91ff83775423cc29, + /*13*/ 0x8380dea93da4bc82, + /*14*/ 0xece53cec4a314f00, + /*15*/ 0xd5605fcdcf32e217, + /*16*/ 0xc0314325637a1978, + /*17*/ 0xad1c8eab5ee43ba2, + /*18*/ 0x9becce62836ac5b0, + /*19*/ 0x8c71dcd9ba0b495c, + /*20*/ 0xfd00b89747823938, + /*21*/ 0xe3e27a444d8d991a, + }; + + private static readonly short[] s_rgexp64Power10By16 = + { + // exponents for both powers of 10^16 and 0.1^16 + /*1*/ 54, + /*2*/ 107, + /*3*/ 160, + /*4*/ 213, + /*5*/ 266, + /*6*/ 319, + /*7*/ 373, + /*8*/ 426, + /*9*/ 479, + /*10*/ 532, + /*11*/ 585, + /*12*/ 638, + /*13*/ 691, + /*14*/ 745, + /*15*/ 798, + /*16*/ 851, + /*17*/ 904, + /*18*/ 957, + /*19*/ 1010, + /*20*/ 1064, + /*21*/ 1117, + }; + + private static int abs(int value) + { + if (value < 0) + return -value; + return value; + } + + private static unsafe double NumberToDouble(ref NumberBuffer number) + { + ulong val; + int exp; + ReadOnlySpan src = number.Digits; + int remaining; + int total; + int count; + int scale; + int absscale; + int index; + int srcIndex = 0; + + total = number.NumDigits; + remaining = total; + + // skip the leading zeros + while (src[srcIndex] == '0') + { + remaining--; + srcIndex++; + } + + if (remaining == 0) + return 0; + + count = Math.Min(remaining, 9); + remaining -= count; + val = DigitsToInt(src, count); + + if (remaining > 0) + { + count = Math.Min(remaining, 9); + remaining -= count; + + // get the denormalized power of 10 + uint mult = (uint)(s_rgval64Power10[count - 1] >> (64 - s_rgexp64Power10[count - 1])); + val = Mul32x32To64((uint)val, mult) + DigitsToInt(src.Slice(9), count); + } + + scale = number.Scale - (total - remaining); + absscale = abs(scale); + if (absscale >= 22 * 16) + { + // overflow / underflow + ulong result = (scale > 0) ? 0x7FF0000000000000 : 0ul; + if (number.IsNegative) + result |= 0x8000000000000000; + return *(double*)&result; + } + + exp = 64; + + // normalize the mantissa + if ((val & 0xFFFFFFFF00000000) == 0) + { val <<= 32; exp -= 32; } + if ((val & 0xFFFF000000000000) == 0) + { val <<= 16; exp -= 16; } + if ((val & 0xFF00000000000000) == 0) + { val <<= 8; exp -= 8; } + if ((val & 0xF000000000000000) == 0) + { val <<= 4; exp -= 4; } + if ((val & 0xC000000000000000) == 0) + { val <<= 2; exp -= 2; } + if ((val & 0x8000000000000000) == 0) + { val <<= 1; exp -= 1; } + + index = absscale & 15; + if (index != 0) + { + int multexp = s_rgexp64Power10[index - 1]; + // the exponents are shared between the inverted and regular table + exp += (scale < 0) ? (-multexp + 1) : multexp; + + ulong multval = s_rgval64Power10[index + ((scale < 0) ? 15 : 0) - 1]; + val = Mul64Lossy(val, multval, ref exp); + } + + index = absscale >> 4; + if (index != 0) + { + int multexp = s_rgexp64Power10By16[index - 1]; + // the exponents are shared between the inverted and regular table + exp += (scale < 0) ? (-multexp + 1) : multexp; + + ulong multval = s_rgval64Power10By16[index + ((scale < 0) ? 21 : 0) - 1]; + val = Mul64Lossy(val, multval, ref exp); + } + + // round & scale down + if (((int)val & (1 << 10)) != 0) + { + // IEEE round to even + ulong tmp = val + ((1 << 10) - 1) + (ulong)(((int)val >> 11) & 1); + if (tmp < val) + { + // overflow + tmp = (tmp >> 1) | 0x8000000000000000; + exp += 1; + } + val = tmp; + } + + // return the exponent to a biased state + exp += 0x3FE; + + // handle overflow, underflow, "Epsilon - 1/2 Epsilon", denormalized, and the normal case + if (exp <= 0) + { + if (exp == -52 && (val >= 0x8000000000000058)) + { + // round X where {Epsilon > X >= 2.470328229206232730000000E-324} up to Epsilon (instead of down to zero) + val = 0x0000000000000001; + } + else if (exp <= -52) + { + // underflow + val = 0; + } + else + { + // denormalized + val >>= (-exp + 11 + 1); + } + } + else if (exp >= 0x7FF) + { + // overflow + val = 0x7FF0000000000000; + } + else + { + // normal postive exponent case + val = ((ulong)exp << 52) + ((val >> 11) & 0x000FFFFFFFFFFFFF); + } + + if (number.IsNegative) + val |= 0x8000000000000000; + + return *(double*)&val; + } + + private static class DoubleHelper + { + public static unsafe uint Exponent(double d) + { + return (*((uint*)&d + 1) >> 20) & 0x000007ff; + } + + public static unsafe ulong Mantissa(double d) + { + return (*((uint*)&d)) | ((ulong)(*((uint*)&d + 1) & 0x000fffff) << 32); + } + + public static unsafe bool Sign(double d) + { + return (*((uint*)&d + 1) >> 31) != 0; + } + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/Number/Number.NumberBuffer.cs b/external/corefx/src/System.Memory/src/System/Number/Number.NumberBuffer.cs new file mode 100644 index 0000000000..6137f27527 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Number/Number.NumberBuffer.cs @@ -0,0 +1,158 @@ +// 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.Diagnostics; +using System.Text; +using System.Runtime.InteropServices; + +#if !netstandard +using Internal.Runtime.CompilerServices; +#else +using System.Runtime.CompilerServices; +#endif + +namespace System +{ + // + // This is a port of the Number/NumberBuffer structure from CoreRT (which in turn was a C#-ized port of the NUMBER struct inside CoreCLR.) + // We use this as the more heavyweight data types such as Decimal and floats. That is, we use Number as a common representation of these types + // and thus share the formatting/parsing routines among them. + // + // This structure can only be stack-allocated as it returns a reference to a fixed-length array inside. + // + [StructLayout(LayoutKind.Sequential)] + internal ref struct NumberBuffer + { + // The Scale is the index of the implied decimal point. Can be negative or beyond the end of the NUL terminator. Examples: + // + // 123.45 => "12345", Scale = 3 + // 0.005 => "5", Scale = -2 + // 1000.00 => "1", Scale = 4 (though it's not guaranteed that it won't be "1000, Scale=0" instead.) + // 0 => "", Scale = 0 + // 3m => "3", Scale = 1 + // 3.00m => "300", Scale = 1 (this is important: trailing zeroes actually matter in Decimal) + // + public int Scale; + public bool IsNegative; + + public unsafe Span Digits => new Span(Unsafe.AsPointer(ref _b0), BufferSize); + + public unsafe byte* UnsafeDigits => (byte*)Unsafe.AsPointer(ref _b0); + + public int NumDigits => Digits.IndexOf(0); + + [Conditional("DEBUG")] + public void CheckConsistency() + { +#if DEBUG + Span digits = Digits; + + Debug.Assert(digits[0] != '0', "Leading zeros should never be stored in a Number"); + + int numDigits; + for (numDigits = 0; numDigits < BufferSize; numDigits++) + { + byte digit = digits[numDigits]; + if (digit == 0) + break; + + Debug.Assert(digit >= '0' && digit <= '9', "Unexpected character found in Number"); + } + + Debug.Assert(numDigits < BufferSize, "NUL terminator not found in Number"); +#endif // DEBUG + } + + // + // Code coverage note: This only exists so that Number displays nicely in the VS watch window. So yes, I know it works. + // + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append('['); + sb.Append('"'); + Span digits = Digits; + for (int i = 0; i < BufferSize; i++) + { + byte digit = digits[i]; + if (digit == 0) + break; + sb.Append((char)digit); + } + sb.Append('"'); + sb.Append(", Scale = " + Scale); + sb.Append(", IsNegative = " + IsNegative); + sb.Append(']'); + return sb.ToString(); + } + + public const int BufferSize = 50 + 1; // Matches https://github.com/dotnet/coreclr/blob/097e68658c5249eaefff33bd92b044e9ba22c819/src/classlibnative/bcltype/number.h#L15 + + // + // 50+1 bytes of ASCII digits ('0'..'9'). (Not using "fixed byte[]" as this breaks the VS debugging experience.) + // That's enough room to store the worst case Decimal and double. + // + // A NUL terminator (not to be confused with '0') marks the end of the digits. + // + // Leading zeroes are never stored, even if the entire number is zero. + // + // Trailing zeroes after the decimal point *are* stored. This is important for System.Decimal + // as trailing zeroes are significant in Decimal: + // + // decimal d1 = 1m; => d1.ToString("G") emits "1" + // decimal d2 = 1.00m; => d1.ToStirng("G") emits "1.00" + // + private byte _b0; + private byte _b1; + private byte _b2; + private byte _b3; + private byte _b4; + private byte _b5; + private byte _b6; + private byte _b7; + private byte _b8; + private byte _b9; + private byte _b10; + private byte _b11; + private byte _b12; + private byte _b13; + private byte _b14; + private byte _b15; + private byte _b16; + private byte _b17; + private byte _b18; + private byte _b19; + private byte _b20; + private byte _b21; + private byte _b22; + private byte _b23; + private byte _b24; + private byte _b25; + private byte _b26; + private byte _b27; + private byte _b28; + private byte _b29; + private byte _b30; + private byte _b31; + private byte _b32; + private byte _b33; + private byte _b34; + private byte _b35; + private byte _b36; + private byte _b37; + private byte _b38; + private byte _b39; + private byte _b40; + private byte _b41; + private byte _b42; + private byte _b43; + private byte _b44; + private byte _b45; + private byte _b46; + private byte _b47; + private byte _b48; + private byte _b49; + private byte _b50; + } +} diff --git a/external/corefx/src/System.Memory/src/System/Number/Number.cs b/external/corefx/src/System.Memory/src/System/Number/Number.cs new file mode 100644 index 0000000000..a30ae75750 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/Number/Number.cs @@ -0,0 +1,59 @@ +// 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. + +// +// This code is copied almost verbatim from the same-named file in CoreRT with mechanical changes to Span-ify it. +// + +namespace System +{ + internal static partial class Number + { + internal const int DECIMAL_PRECISION = 29; + + // + // This method is copied directly from CoreRT (which is in turn a C#-ized version of the CoreCLR C++ code.) + // + public static void RoundNumber(ref NumberBuffer number, int pos) + { + number.CheckConsistency(); + + Span digits = number.Digits; + + int i = 0; + while (i < pos && digits[i] != 0) + i++; + + if (i == pos && digits[i] >= (byte)'5') + { + while (i > 0 && digits[i - 1] == (byte)'9') + i--; + + if (i > 0) + { + digits[i - 1]++; + } + else + { + number.Scale++; + digits[0] = (byte)'1'; + i = 1; + } + } + else + { + while (i > 0 && digits[i - 1] == (byte)'0') + i--; + } + if (i == 0) + { + number.Scale = 0; + number.IsNegative = false; + } + digits[i] = 0; + + number.CheckConsistency(); + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/ReadOnlySpan.cs b/external/corefx/src/System.Memory/src/System/ReadOnlySpan.cs index fdc51469b0..4137248c8d 100644 --- a/external/corefx/src/System.Memory/src/System/ReadOnlySpan.cs +++ b/external/corefx/src/System.Memory/src/System/ReadOnlySpan.cs @@ -82,6 +82,7 @@ namespace System /// /// Thrown when the specified is negative. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe ReadOnlySpan(void* pointer, int length) { @@ -147,7 +148,7 @@ namespace System /// /// Thrown when index less than 0 or index greater than or equal to Length /// - public T this[int index] + public ref readonly T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get @@ -156,9 +157,9 @@ namespace System ThrowHelper.ThrowIndexOutOfRangeException(); if (_pinnable == null) - unsafe { return Unsafe.Add(ref Unsafe.AsRef(_byteOffset.ToPointer()), index); } + unsafe { return ref Unsafe.Add(ref Unsafe.AsRef(_byteOffset.ToPointer()), index); } else - return Unsafe.Add(ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset), index); + return ref Unsafe.Add(ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset), index); } } @@ -178,7 +179,6 @@ namespace System ThrowHelper.ThrowArgumentException_DestinationTooShort(); } - /// /// Copies the contents of this read-only span into destination span. If the source /// and destinations overlap, this method behaves as if the original values in @@ -226,7 +226,7 @@ namespace System /// Always thrown by this method. /// /// - [Obsolete("Equals() on Span will always throw an exception. Use == instead.")] + [Obsolete("Equals() on ReadOnlySpan will always throw an exception. Use == instead.")] #if !MONO [EditorBrowsable(EditorBrowsableState.Never)] #endif @@ -241,7 +241,7 @@ namespace System /// Always thrown by this method. /// /// - [Obsolete("GetHashCode() on Span will always throw an exception.")] + [Obsolete("GetHashCode() on ReadOnlySpan will always throw an exception.")] #if !MONO [EditorBrowsable(EditorBrowsableState.Never)] #endif @@ -253,12 +253,13 @@ namespace System /// /// Defines an implicit conversion of an array to a /// - public static implicit operator ReadOnlySpan(T[] array) => new ReadOnlySpan(array); + public static implicit operator ReadOnlySpan(T[] array) => array != null ? new ReadOnlySpan(array) : default; /// /// Defines an implicit conversion of a to a /// - public static implicit operator ReadOnlySpan(ArraySegment arraySegment) => new ReadOnlySpan(arraySegment.Array, arraySegment.Offset, arraySegment.Count); + public static implicit operator ReadOnlySpan(ArraySegment arraySegment) + => arraySegment.Array != null ? new ReadOnlySpan(arraySegment.Array, arraySegment.Offset, arraySegment.Count) : default; /// /// Forms a slice out of the given read-only span, beginning at 'start'. @@ -317,6 +318,7 @@ namespace System public static ReadOnlySpan Empty => default(ReadOnlySpan); /// + /// This method is obsolete, use System.Runtime.InteropServices.MemoryMarshal.GetReference instead. /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. /// @@ -324,7 +326,7 @@ namespace System #if !MONO [EditorBrowsable(EditorBrowsableState.Never)] #endif - public ref T DangerousGetPinnableReference() + internal ref T DangerousGetPinnableReference() { if (_pinnable == null) unsafe { return ref Unsafe.AsRef(_byteOffset.ToPointer()); } @@ -332,6 +334,61 @@ namespace System return ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset); } + /// Gets an enumerator for this span. + public Enumerator GetEnumerator() => new Enumerator(this); + + /// Enumerates the elements of a . + public ref struct Enumerator + { + /// The span being enumerated. + private readonly ReadOnlySpan _span; + /// The next index to yield. + private int _index; + + /// Initialize the enumerator. + /// The span to enumerate. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Enumerator(ReadOnlySpan span) + { + _span = span; + _index = -1; + } + + /// Advances the enumerator to the next element of the span. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + int index = _index + 1; + if (index < _span.Length) + { + _index = index; + return true; + } + + return false; + } + + /// Gets the element at the current position of the enumerator. + public ref readonly T Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + // TODO https://github.com/dotnet/corefx/issues/24105: + // Change this to simply be: + // get => ref _span[_index]; + // once ReadOnlySpan's indexer returns ref readonly. + + if ((uint)_index >= (uint)_span.Length) + { + ThrowHelper.ThrowIndexOutOfRangeException(); + } + + return ref Unsafe.Add(ref _span.DangerousGetPinnableReference(), _index); + } + } + } + // These expose the internal representation for Span-related apis use only. internal Pinnable Pinnable => _pinnable; internal IntPtr ByteOffset => _byteOffset; diff --git a/external/corefx/src/System.Memory/src/System/Span.cs b/external/corefx/src/System.Memory/src/System/Span.cs index 8f50d5b8df..bd0060d6ad 100644 --- a/external/corefx/src/System.Memory/src/System/Span.cs +++ b/external/corefx/src/System.Memory/src/System/Span.cs @@ -37,7 +37,7 @@ namespace System if (array == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); if (default(T) == null && array.GetType() != typeof(T[])) - ThrowHelper.ThrowArrayTypeMismatchException_ArrayTypeMustBeExactMatch(typeof(T)); + ThrowHelper.ThrowArrayTypeMismatchException(); _length = array.Length; _pinnable = Unsafe.As>(array); @@ -63,7 +63,7 @@ namespace System if (array == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); if (default(T) == null && array.GetType() != typeof(T[])) - ThrowHelper.ThrowArrayTypeMismatchException_ArrayTypeMustBeExactMatch(typeof(T)); + ThrowHelper.ThrowArrayTypeMismatchException(); if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); @@ -86,6 +86,7 @@ namespace System /// /// Thrown when the specified is negative. /// + [CLSCompliant(false)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe Span(void* pointer, int length) { @@ -359,12 +360,13 @@ namespace System /// /// Defines an implicit conversion of an array to a /// - public static implicit operator Span(T[] array) => new Span(array); + public static implicit operator Span(T[] array) => array != null ? new Span(array) : default; /// /// Defines an implicit conversion of a to a /// - public static implicit operator Span(ArraySegment arraySegment) => new Span(arraySegment.Array, arraySegment.Offset, arraySegment.Count); + public static implicit operator Span(ArraySegment arraySegment) + => arraySegment.Array != null ? new Span(arraySegment.Array, arraySegment.Offset, arraySegment.Count) : default; /// /// Defines an implicit conversion of a to a @@ -428,6 +430,7 @@ namespace System public static Span Empty => default(Span); /// + /// This method is obsolete, use System.Runtime.InteropServices.MemoryMarshal.GetReference instead. /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. /// @@ -435,7 +438,7 @@ namespace System #if !MONO [EditorBrowsable(EditorBrowsableState.Never)] #endif - public ref T DangerousGetPinnableReference() + internal ref T DangerousGetPinnableReference() { if (_pinnable == null) unsafe { return ref Unsafe.AsRef(_byteOffset.ToPointer()); } @@ -443,6 +446,48 @@ namespace System return ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset); } + /// Gets an enumerator for this span. + public Enumerator GetEnumerator() => new Enumerator(this); + + /// Enumerates the elements of a . + public ref struct Enumerator + { + /// The span being enumerated. + private readonly Span _span; + /// The next index to yield. + private int _index; + + /// Initialize the enumerator. + /// The span to enumerate. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Enumerator(Span span) + { + _span = span; + _index = -1; + } + + /// Advances the enumerator to the next element of the span. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + int index = _index + 1; + if (index < _span.Length) + { + _index = index; + return true; + } + + return false; + } + + /// Gets the element at the current position of the enumerator. + public ref T Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref _span[_index]; + } + } + // These expose the internal representation for Span-related apis use only. internal Pinnable Pinnable => _pinnable; internal IntPtr ByteOffset => _byteOffset; diff --git a/external/corefx/src/System.Memory/src/System/SpanExtensions.cs b/external/corefx/src/System.Memory/src/System/SpanExtensions.cs deleted file mode 100644 index 31bc74c7da..0000000000 --- a/external/corefx/src/System.Memory/src/System/SpanExtensions.cs +++ /dev/null @@ -1,317 +0,0 @@ -// 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.Diagnostics; -using System.Runtime.CompilerServices; - -namespace System -{ - /// - /// Extension methods for Span<T>. - /// - public static partial class SpanExtensions - { - /// - /// Searches for the specified value and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable<T>.Equals(T). - /// - /// The span to search. - /// The value to search for. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOf(this Span span, T value) - where T:struct, IEquatable - { - return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), value, span.Length); - } - - /// - /// Searches for the specified value and returns the index of its first occurrence. If not found, returns -1. - /// - /// The span to search. - /// The value to search for. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOf(this Span span, byte value) - { - return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), value, span.Length); - } - - /// - /// Searches for the specified sequence and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable<T>.Equals(T). - /// - /// The span to search. - /// The sequence to search for. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOf(this Span span, ReadOnlySpan value) - where T : struct, IEquatable - { - return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), span.Length, ref value.DangerousGetPinnableReference(), value.Length); - } - - /// - /// Searches for the specified sequence and returns the index of its first occurrence. If not found, returns -1. - /// - /// The span to search. - /// The sequence to search for. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOf(this Span span, ReadOnlySpan value) - { - return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), span.Length, ref value.DangerousGetPinnableReference(), value.Length); - } - - /// - /// Determines whether two sequences are equal by comparing the elements using IEquatable<T>.Equals(T). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool SequenceEqual(this Span first, ReadOnlySpan second) - where T:struct, IEquatable - { - int length = first.Length; - return length == second.Length && SpanHelpers.SequenceEqual(ref first.DangerousGetPinnableReference(), ref second.DangerousGetPinnableReference(), length); - } - - /// - /// Determines whether two sequences are equal by comparing the elements. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool SequenceEqual(this Span first, ReadOnlySpan second) - { - int length = first.Length; - return length == second.Length && SpanHelpers.SequenceEqual(ref first.DangerousGetPinnableReference(), ref second.DangerousGetPinnableReference(), length); - } - - /// - /// Searches for the specified value and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable<T>.Equals(T). - /// - /// The span to search. - /// The value to search for. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOf(this ReadOnlySpan span, T value) - where T : struct, IEquatable - { - return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), value, span.Length); - } - - /// - /// Searches for the specified value and returns the index of its first occurrence. If not found, returns -1. - /// - /// The span to search. - /// The value to search for. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOf(this ReadOnlySpan span, byte value) - { - return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), value, span.Length); - } - - /// - /// Searches for the specified sequence and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable<T>.Equals(T). - /// - /// The span to search. - /// The sequence to search for. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOf(this ReadOnlySpan span, ReadOnlySpan value) - where T : struct, IEquatable - { - return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), span.Length, ref value.DangerousGetPinnableReference(), value.Length); - } - - /// - /// Searches for the specified sequence and returns the index of its first occurrence. If not found, returns -1. - /// - /// The span to search. - /// The sequence to search for. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOf(this ReadOnlySpan span, ReadOnlySpan value) - { - return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), span.Length, ref value.DangerousGetPinnableReference(), value.Length); - } - - - /// - /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. - /// - /// The span to search. - /// One of the values to search for. - /// One of the values to search for. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOfAny(this Span span, byte value0, byte value1) - { - return SpanHelpers.IndexOfAny(ref span.DangerousGetPinnableReference(), value0, value1, span.Length); - } - - /// - /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. - /// - /// The span to search. - /// One of the values to search for. - /// One of the values to search for. - /// One of the values to search for. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOfAny(this Span span, byte value0, byte value1, byte value2) - { - return SpanHelpers.IndexOfAny(ref span.DangerousGetPinnableReference(), value0, value1, value2, span.Length); - } - - /// - /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. - /// - /// The span to search. - /// The set of values to search for. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOfAny(this Span span, ReadOnlySpan values) - { - return SpanHelpers.IndexOfAny(ref span.DangerousGetPinnableReference(), span.Length, ref values.DangerousGetPinnableReference(), values.Length); - } - - /// - /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. - /// - /// The span to search. - /// One of the values to search for. - /// One of the values to search for. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOfAny(this ReadOnlySpan span, byte value0, byte value1) - { - return SpanHelpers.IndexOfAny(ref span.DangerousGetPinnableReference(), value0, value1, span.Length); - } - - /// - /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. - /// - /// The span to search. - /// One of the values to search for. - /// One of the values to search for. - /// One of the values to search for. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOfAny(this ReadOnlySpan span, byte value0, byte value1, byte value2) - { - return SpanHelpers.IndexOfAny(ref span.DangerousGetPinnableReference(), value0, value1, value2, span.Length); - } - - /// - /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. - /// - /// The span to search. - /// The set of values to search for. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOfAny(this ReadOnlySpan span, ReadOnlySpan values) - { - return SpanHelpers.IndexOfAny(ref span.DangerousGetPinnableReference(), span.Length, ref values.DangerousGetPinnableReference(), values.Length); - } - - /// - /// Determines whether two sequences are equal by comparing the elements using IEquatable<T>.Equals(T). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool SequenceEqual(this ReadOnlySpan first, ReadOnlySpan second) - where T : struct, IEquatable - { - int length = first.Length; - return length == second.Length && SpanHelpers.SequenceEqual(ref first.DangerousGetPinnableReference(), ref second.DangerousGetPinnableReference(), length); - } - - /// - /// Determines whether two sequences are equal by comparing the elements. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool SequenceEqual(this ReadOnlySpan first, ReadOnlySpan second) - { - int length = first.Length; - return length == second.Length && SpanHelpers.SequenceEqual(ref first.DangerousGetPinnableReference(), ref second.DangerousGetPinnableReference(), length); - } - - /// - /// Determines whether the specified sequence appears at the start of the span. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool StartsWith(this Span span, ReadOnlySpan value) - { - int valueLength = value.Length; - return valueLength <= span.Length && SpanHelpers.SequenceEqual(ref span.DangerousGetPinnableReference(), ref value.DangerousGetPinnableReference(), valueLength); - } - - /// - /// Determines whether the specified sequence appears at the start of the span. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool StartsWith(this Span span, ReadOnlySpan value) - where T : struct, IEquatable - { - int valueLength = value.Length; - return valueLength <= span.Length && SpanHelpers.SequenceEqual(ref span.DangerousGetPinnableReference(), ref value.DangerousGetPinnableReference(), valueLength); - } - - /// - /// Determines whether the specified sequence appears at the start of the span. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool StartsWith(this ReadOnlySpan span, ReadOnlySpan value) - { - int valueLength = value.Length; - return valueLength <= span.Length && SpanHelpers.SequenceEqual(ref span.DangerousGetPinnableReference(), ref value.DangerousGetPinnableReference(), valueLength); - } - - /// - /// Determines whether the specified sequence appears at the start of the span. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool StartsWith(this ReadOnlySpan span, ReadOnlySpan value) - where T : struct, IEquatable - { - int valueLength = value.Length; - return valueLength <= span.Length && SpanHelpers.SequenceEqual(ref span.DangerousGetPinnableReference(), ref value.DangerousGetPinnableReference(), valueLength); - } - - /// - /// Creates a new span over the portion of the target array. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span AsSpan(this T[] array) - { - return new Span(array); - } - - /// - /// Creates a new span over the portion of the target array segment. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span AsSpan(this ArraySegment arraySegment) - { - return new Span(arraySegment.Array, arraySegment.Offset, arraySegment.Count); - } - - /// - /// Creates a new readonly span over the entire target array. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan AsReadOnlySpan(this T[] array) - { - return new ReadOnlySpan(array); - } - - /// - /// Creates a new readonly span over the target array segment. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan AsReadOnlySpan(this ArraySegment arraySegment) - { - return new ReadOnlySpan(arraySegment.Array, arraySegment.Offset, arraySegment.Count); - } - - /// - /// Copies the contents of the array into the span. If the source - /// and destinations overlap, this method behaves as if the original values in - /// a temporary location before the destination is overwritten. - /// - ///The array to copy items from. - /// The span to copy items into. - /// - /// Thrown when the destination Span is shorter than the source array. - /// - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CopyTo(this T[] array, Span destination) - { - new ReadOnlySpan(array).CopyTo(destination); - } - } -} \ No newline at end of file diff --git a/external/corefx/src/System.Memory/src/System/SpanHelpers.BinarySearch.cs b/external/corefx/src/System.Memory/src/System/SpanHelpers.BinarySearch.cs new file mode 100644 index 0000000000..cfd64f1e40 --- /dev/null +++ b/external/corefx/src/System.Memory/src/System/SpanHelpers.BinarySearch.cs @@ -0,0 +1,83 @@ +// 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.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#if !netstandard +using Internal.Runtime.CompilerServices; +#endif + +namespace System +{ + internal static partial class SpanHelpers + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static int BinarySearch( + this ReadOnlySpan span, TComparable comparable) + where TComparable : IComparable + { + if (comparable == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparable); + + return BinarySearch(ref MemoryMarshal.GetReference(span), span.Length, comparable); + } + + internal static int BinarySearch( + ref T spanStart, int length, TComparable comparable) + where TComparable : IComparable + { + int lo = 0; + int hi = length - 1; + // If length == 0, hi == -1, and loop will not be entered + while (lo <= hi) + { + // PERF: `lo` or `hi` will never be negative inside the loop, + // so computing median using uints is safe since we know + // `length <= int.MaxValue`, and indices are >= 0 + // and thus cannot overflow an uint. + // Saves one subtraction per loop compared to + // `int i = lo + ((hi - lo) >> 1);` + int i = (int)(((uint)hi + (uint)lo) >> 1); + + int c = comparable.CompareTo(Unsafe.Add(ref spanStart, i)); + if (c == 0) + { + return i; + } + else if (c > 0) + { + lo = i + 1; + } + else + { + hi = i - 1; + } + } + // If none found, then a negative number that is the bitwise complement + // of the index of the next element that is larger than or, if there is + // no larger element, the bitwise complement of `length`, which + // is `lo` at this point. + return ~lo; + } + + // Helper to allow sharing all code via IComparable inlineable + internal struct ComparerComparable : IComparable + where TComparer : IComparer + { + readonly T _value; + readonly TComparer _comparer; + + public ComparerComparable(T value, TComparer comparer) + { + _value = value; + _comparer = comparer; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int CompareTo(T other) => _comparer.Compare(_value, other); + } + } +} diff --git a/external/corefx/src/System.Memory/src/System/SpanHelpers.Clear.cs b/external/corefx/src/System.Memory/src/System/SpanHelpers.Clear.cs index 35bb07cbaf..34676e5611 100644 --- a/external/corefx/src/System.Memory/src/System/SpanHelpers.Clear.cs +++ b/external/corefx/src/System.Memory/src/System/SpanHelpers.Clear.cs @@ -5,6 +5,10 @@ using System.Runtime.InteropServices; using System.Runtime.CompilerServices; +#if !netstandard +using Internal.Runtime.CompilerServices; +#endif + namespace System { internal static partial class SpanHelpers @@ -96,7 +100,7 @@ namespace System } } - public unsafe static void ClearPointerSizedWithReferences(ref IntPtr ip, UIntPtr pointerSizeLength) + public static void ClearPointerSizedWithReferences(ref IntPtr ip, UIntPtr pointerSizeLength) { // TODO: Perhaps do switch casing to improve small size perf diff --git a/external/corefx/src/System.Memory/src/System/SpanHelpers.T.cs b/external/corefx/src/System.Memory/src/System/SpanHelpers.T.cs index 485f476580..3c3f70bece 100644 --- a/external/corefx/src/System.Memory/src/System/SpanHelpers.T.cs +++ b/external/corefx/src/System.Memory/src/System/SpanHelpers.T.cs @@ -3,7 +3,13 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; + +#if !netstandard && !MONO +using Internal.Runtime.CompilerServices; +#else using System.Runtime.CompilerServices; +#endif + #if MONO using System.Diagnostics.Private; #endif @@ -13,7 +19,7 @@ namespace System internal static partial class SpanHelpers { public static int IndexOf(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength) - where T : struct, IEquatable + where T : IEquatable { Debug.Assert(searchSpaceLength >= 0); Debug.Assert(valueLength >= 0); @@ -26,7 +32,7 @@ namespace System int valueTailLength = valueLength - 1; int index = 0; - for (;;) + for (; ; ) { Debug.Assert(0 <= index && index <= searchSpaceLength); // Ensures no deceptive underflows in the computation of "remainingSearchSpaceLength". int remainingSearchSpaceLength = searchSpaceLength - index - valueTailLength; @@ -47,9 +53,9 @@ namespace System } return -1; } - + public static unsafe int IndexOf(ref T searchSpace, T value, int length) - where T : struct, IEquatable + where T : IEquatable { Debug.Assert(length >= 0); @@ -122,8 +128,484 @@ namespace System return (int)(byte*)(index + 7); } + public static int IndexOfAny(ref T searchSpace, T value0, T value1, int length) + where T : IEquatable + { + Debug.Assert(length >= 0); + + T lookUp; + int index = 0; + while ((length - index) >= 8) + { + lookUp = Unsafe.Add(ref searchSpace, index); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found; + lookUp = Unsafe.Add(ref searchSpace, index + 1); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found1; + lookUp = Unsafe.Add(ref searchSpace, index + 2); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found2; + lookUp = Unsafe.Add(ref searchSpace, index + 3); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found3; + lookUp = Unsafe.Add(ref searchSpace, index + 4); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found4; + lookUp = Unsafe.Add(ref searchSpace, index + 5); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found5; + lookUp = Unsafe.Add(ref searchSpace, index + 6); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found6; + lookUp = Unsafe.Add(ref searchSpace, index + 7); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found7; + + index += 8; + } + + if ((length - index) >= 4) + { + lookUp = Unsafe.Add(ref searchSpace, index); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found; + lookUp = Unsafe.Add(ref searchSpace, index + 1); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found1; + lookUp = Unsafe.Add(ref searchSpace, index + 2); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found2; + lookUp = Unsafe.Add(ref searchSpace, index + 3); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found3; + + index += 4; + } + + while (index < length) + { + lookUp = Unsafe.Add(ref searchSpace, index); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found; + + index++; + } + return -1; + + Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549 + return index; + Found1: + return index + 1; + Found2: + return index + 2; + Found3: + return index + 3; + Found4: + return index + 4; + Found5: + return index + 5; + Found6: + return index + 6; + Found7: + return index + 7; + } + + public static int IndexOfAny(ref T searchSpace, T value0, T value1, T value2, int length) + where T : IEquatable + { + Debug.Assert(length >= 0); + + T lookUp; + int index = 0; + while ((length - index) >= 8) + { + lookUp = Unsafe.Add(ref searchSpace, index); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found; + lookUp = Unsafe.Add(ref searchSpace, index + 1); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found1; + lookUp = Unsafe.Add(ref searchSpace, index + 2); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found2; + lookUp = Unsafe.Add(ref searchSpace, index + 3); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found3; + lookUp = Unsafe.Add(ref searchSpace, index + 4); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found4; + lookUp = Unsafe.Add(ref searchSpace, index + 5); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found5; + lookUp = Unsafe.Add(ref searchSpace, index + 6); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found6; + lookUp = Unsafe.Add(ref searchSpace, index + 7); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found7; + + index += 8; + } + + if ((length - index) >= 4) + { + lookUp = Unsafe.Add(ref searchSpace, index); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found; + lookUp = Unsafe.Add(ref searchSpace, index + 1); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found1; + lookUp = Unsafe.Add(ref searchSpace, index + 2); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found2; + lookUp = Unsafe.Add(ref searchSpace, index + 3); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found3; + + index += 4; + } + + while (index < length) + { + lookUp = Unsafe.Add(ref searchSpace, index); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found; + + index++; + } + return -1; + + Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549 + return index; + Found1: + return index + 1; + Found2: + return index + 2; + Found3: + return index + 3; + Found4: + return index + 4; + Found5: + return index + 5; + Found6: + return index + 6; + Found7: + return index + 7; + } + + public static int IndexOfAny(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength) + where T : IEquatable + { + Debug.Assert(searchSpaceLength >= 0); + Debug.Assert(valueLength >= 0); + + if (valueLength == 0) + return 0; // A zero-length sequence is always treated as "found" at the start of the search space. + + int index = -1; + for (int i = 0; i < valueLength; i++) + { + var tempIndex = IndexOf(ref searchSpace, Unsafe.Add(ref value, i), searchSpaceLength); + if ((uint)tempIndex < (uint)index) + { + index = tempIndex; + // Reduce space for search, cause we don't care if we find the search value after the index of a previously found value + searchSpaceLength = tempIndex; + + if (index == 0) break; + } + } + return index; + } + + public static int LastIndexOf(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength) + where T : IEquatable + { + Debug.Assert(searchSpaceLength >= 0); + Debug.Assert(valueLength >= 0); + + if (valueLength == 0) + return 0; // A zero-length sequence is always treated as "found" at the start of the search space. + + T valueHead = value; + ref T valueTail = ref Unsafe.Add(ref value, 1); + int valueTailLength = valueLength - 1; + + int index = 0; + for (; ; ) + { + Debug.Assert(0 <= index && index <= searchSpaceLength); // Ensures no deceptive underflows in the computation of "remainingSearchSpaceLength". + int remainingSearchSpaceLength = searchSpaceLength - index - valueTailLength; + if (remainingSearchSpaceLength <= 0) + break; // The unsearched portion is now shorter than the sequence we're looking for. So it can't be there. + + // Do a quick search for the first element of "value". + int relativeIndex = LastIndexOf(ref searchSpace, valueHead, remainingSearchSpaceLength); + if (relativeIndex == -1) + break; + + // Found the first element of "value". See if the tail matches. + if (SequenceEqual(ref Unsafe.Add(ref searchSpace, relativeIndex + 1), ref valueTail, valueTailLength)) + return relativeIndex; // The tail matched. Return a successful find. + + index += remainingSearchSpaceLength - relativeIndex; + } + return -1; + } + + public static int LastIndexOf(ref T searchSpace, T value, int length) + where T : IEquatable + { + Debug.Assert(length >= 0); + + while (length >= 8) + { + length -= 8; + + if (value.Equals(Unsafe.Add(ref searchSpace, length + 7))) + goto Found7; + if (value.Equals(Unsafe.Add(ref searchSpace, length + 6))) + goto Found6; + if (value.Equals(Unsafe.Add(ref searchSpace, length + 5))) + goto Found5; + if (value.Equals(Unsafe.Add(ref searchSpace, length + 4))) + goto Found4; + if (value.Equals(Unsafe.Add(ref searchSpace, length + 3))) + goto Found3; + if (value.Equals(Unsafe.Add(ref searchSpace, length + 2))) + goto Found2; + if (value.Equals(Unsafe.Add(ref searchSpace, length + 1))) + goto Found1; + if (value.Equals(Unsafe.Add(ref searchSpace, length))) + goto Found; + } + + if (length >= 4) + { + length -= 4; + + if (value.Equals(Unsafe.Add(ref searchSpace, length + 3))) + goto Found3; + if (value.Equals(Unsafe.Add(ref searchSpace, length + 2))) + goto Found2; + if (value.Equals(Unsafe.Add(ref searchSpace, length + 1))) + goto Found1; + if (value.Equals(Unsafe.Add(ref searchSpace, length))) + goto Found; + } + + while (length > 0) + { + length--; + + if (value.Equals(Unsafe.Add(ref searchSpace, length))) + goto Found; + } + return -1; + + Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549 + return length; + Found1: + return length + 1; + Found2: + return length + 2; + Found3: + return length + 3; + Found4: + return length + 4; + Found5: + return length + 5; + Found6: + return length + 6; + Found7: + return length + 7; + } + + public static int LastIndexOfAny(ref T searchSpace, T value0, T value1, int length) + where T : IEquatable + { + Debug.Assert(length >= 0); + + T lookUp; + while (length >= 8) + { + length -= 8; + + lookUp = Unsafe.Add(ref searchSpace, length + 7); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found7; + lookUp = Unsafe.Add(ref searchSpace, length + 6); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found6; + lookUp = Unsafe.Add(ref searchSpace, length + 5); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found5; + lookUp = Unsafe.Add(ref searchSpace, length + 4); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found4; + lookUp = Unsafe.Add(ref searchSpace, length + 3); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found3; + lookUp = Unsafe.Add(ref searchSpace, length + 2); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found2; + lookUp = Unsafe.Add(ref searchSpace, length + 1); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found1; + lookUp = Unsafe.Add(ref searchSpace, length); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found; + } + + if (length >= 4) + { + length -= 4; + + lookUp = Unsafe.Add(ref searchSpace, length + 3); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found3; + lookUp = Unsafe.Add(ref searchSpace, length + 2); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found2; + lookUp = Unsafe.Add(ref searchSpace, length + 1); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found1; + lookUp = Unsafe.Add(ref searchSpace, length); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found; + } + + while (length > 0) + { + length--; + + lookUp = Unsafe.Add(ref searchSpace, length); + if (value0.Equals(lookUp) || value1.Equals(lookUp)) + goto Found; + } + return -1; + + Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549 + return length; + Found1: + return length + 1; + Found2: + return length + 2; + Found3: + return length + 3; + Found4: + return length + 4; + Found5: + return length + 5; + Found6: + return length + 6; + Found7: + return length + 7; + } + + public static int LastIndexOfAny(ref T searchSpace, T value0, T value1, T value2, int length) + where T : IEquatable + { + Debug.Assert(length >= 0); + + T lookUp; + while (length >= 8) + { + length -= 8; + + lookUp = Unsafe.Add(ref searchSpace, length + 7); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found7; + lookUp = Unsafe.Add(ref searchSpace, length + 6); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found6; + lookUp = Unsafe.Add(ref searchSpace, length + 5); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found5; + lookUp = Unsafe.Add(ref searchSpace, length + 4); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found4; + lookUp = Unsafe.Add(ref searchSpace, length + 3); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found3; + lookUp = Unsafe.Add(ref searchSpace, length + 2); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found2; + lookUp = Unsafe.Add(ref searchSpace, length + 1); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found1; + lookUp = Unsafe.Add(ref searchSpace, length); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found; + } + + if (length >= 4) + { + length -= 4; + + lookUp = Unsafe.Add(ref searchSpace, length + 3); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found3; + lookUp = Unsafe.Add(ref searchSpace, length + 2); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found2; + lookUp = Unsafe.Add(ref searchSpace, length + 1); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found1; + lookUp = Unsafe.Add(ref searchSpace, length); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found; + } + + while (length > 0) + { + length--; + + lookUp = Unsafe.Add(ref searchSpace, length); + if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp)) + goto Found; + } + return -1; + + Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549 + return length; + Found1: + return length + 1; + Found2: + return length + 2; + Found3: + return length + 3; + Found4: + return length + 4; + Found5: + return length + 5; + Found6: + return length + 6; + Found7: + return length + 7; + } + + public static int LastIndexOfAny(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength) + where T : IEquatable + { + Debug.Assert(searchSpaceLength >= 0); + Debug.Assert(valueLength >= 0); + + if (valueLength == 0) + return 0; // A zero-length sequence is always treated as "found" at the start of the search space. + + int index = -1; + for (int i = 0; i < valueLength; i++) + { + var tempIndex = LastIndexOf(ref searchSpace, Unsafe.Add(ref value, i), searchSpaceLength); + if (tempIndex > index) index = tempIndex; + } + return index; + } + public static bool SequenceEqual(ref T first, ref T second, int length) - where T : struct, IEquatable + where T : IEquatable { Debug.Assert(length >= 0); @@ -185,5 +667,21 @@ namespace System NotEqual: // Workaround for https://github.com/dotnet/coreclr/issues/13549 return false; } + + public static int SequenceCompareTo(ref T first, int firstLength, ref T second, int secondLength) + where T : IComparable + { + Debug.Assert(firstLength >= 0); + Debug.Assert(secondLength >= 0); + + var minLength = firstLength; + if (minLength > secondLength) minLength = secondLength; + for (int i = 0; i < minLength; i++) + { + int result = Unsafe.Add(ref first, i).CompareTo(Unsafe.Add(ref second, i)); + if (result != 0) return result; + } + return firstLength.CompareTo(secondLength); + } } } diff --git a/external/corefx/src/System.Memory/src/System/SpanHelpers.byte.cs b/external/corefx/src/System.Memory/src/System/SpanHelpers.byte.cs index 798f49c444..e3c350d7a7 100644 --- a/external/corefx/src/System.Memory/src/System/SpanHelpers.byte.cs +++ b/external/corefx/src/System.Memory/src/System/SpanHelpers.byte.cs @@ -1,3 +1,5 @@ +#define netstandard11 // Simplifies MONO specific defines + // 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. @@ -5,7 +7,11 @@ using System.Diagnostics; using System.Runtime.CompilerServices; -#if !netstandard11 && !MONO +#if !netstandard +using Internal.Runtime.CompilerServices; +#endif + +#if !netstandard11 using System.Numerics; #endif @@ -30,7 +36,7 @@ namespace System int valueTailLength = valueLength - 1; int index = 0; - for (;;) + for (; ; ) { Debug.Assert(0 <= index && index <= searchSpaceLength); // Ensures no deceptive underflows in the computation of "remainingSearchSpaceLength". int remainingSearchSpaceLength = searchSpaceLength - index - valueTailLength; @@ -64,14 +70,35 @@ namespace System for (int i = 0; i < valueLength; i++) { var tempIndex = IndexOf(ref searchSpace, Unsafe.Add(ref value, i), searchSpaceLength); - if (tempIndex != -1) + if ((uint)tempIndex < (uint)index) { - index = (index == -1 || index > tempIndex) ? tempIndex : index; + index = tempIndex; + // Reduce space for search, cause we don't care if we find the search value after the index of a previously found value + searchSpaceLength = tempIndex; + + if (index == 0) break; } } return index; } + public static int LastIndexOfAny(ref byte searchSpace, int searchSpaceLength, ref byte value, int valueLength) + { + Debug.Assert(searchSpaceLength >= 0); + Debug.Assert(valueLength >= 0); + + if (valueLength == 0) + return 0; // A zero-length sequence is always treated as "found" at the start of the search space. + + int index = -1; + for (int i = 0; i < valueLength; i++) + { + var tempIndex = LastIndexOf(ref searchSpace, Unsafe.Add(ref value, i), searchSpaceLength); + if (tempIndex > index) index = tempIndex; + } + return index; + } + public static unsafe int IndexOf(ref byte searchSpace, byte value, int length) { Debug.Assert(length >= 0); @@ -79,7 +106,7 @@ namespace System uint uValue = value; // Use uint for comparisons to avoid unnecessary 8->32 extensions IntPtr index = (IntPtr)0; // Use UIntPtr for arithmetic to avoid unnecessary 64->32->64 truncations IntPtr nLength = (IntPtr)(uint)length; -#if !netstandard11 && !MONO +#if !netstandard11 if (Vector.IsHardwareAccelerated && length >= Vector.Count * 2) { unchecked @@ -139,7 +166,7 @@ namespace System index += 1; } -#if !netstandard11 && !MONO +#if !netstandard11 if (Vector.IsHardwareAccelerated && ((int)(byte*)index < length)) { nLength = (IntPtr)(uint)((length - (uint)index) & ~(Vector.Count - 1)); @@ -186,6 +213,149 @@ namespace System return (int)(byte*)(index + 7); } + public static int LastIndexOf(ref byte searchSpace, int searchSpaceLength, ref byte value, int valueLength) + { + Debug.Assert(searchSpaceLength >= 0); + Debug.Assert(valueLength >= 0); + + if (valueLength == 0) + return 0; // A zero-length sequence is always treated as "found" at the start of the search space. + + byte valueHead = value; + ref byte valueTail = ref Unsafe.Add(ref value, 1); + int valueTailLength = valueLength - 1; + + int index = 0; + for (; ; ) + { + Debug.Assert(0 <= index && index <= searchSpaceLength); // Ensures no deceptive underflows in the computation of "remainingSearchSpaceLength". + int remainingSearchSpaceLength = searchSpaceLength - index - valueTailLength; + if (remainingSearchSpaceLength <= 0) + break; // The unsearched portion is now shorter than the sequence we're looking for. So it can't be there. + + // Do a quick search for the first element of "value". + int relativeIndex = LastIndexOf(ref searchSpace, valueHead, remainingSearchSpaceLength); + if (relativeIndex == -1) + break; + + // Found the first element of "value". See if the tail matches. + if (SequenceEqual(ref Unsafe.Add(ref searchSpace, relativeIndex + 1), ref valueTail, valueTailLength)) + return relativeIndex; // The tail matched. Return a successful find. + + index += remainingSearchSpaceLength - relativeIndex; + } + return -1; + } + + public static unsafe int LastIndexOf(ref byte searchSpace, byte value, int length) + { + Debug.Assert(length >= 0); + + uint uValue = value; // Use uint for comparisons to avoid unnecessary 8->32 extensions + IntPtr index = (IntPtr)(uint)length; // Use UIntPtr for arithmetic to avoid unnecessary 64->32->64 truncations + IntPtr nLength = (IntPtr)(uint)length; +#if !netstandard11 + if (Vector.IsHardwareAccelerated && length >= Vector.Count * 2) + { + unchecked + { + int unaligned = (int)(byte*)Unsafe.AsPointer(ref searchSpace) & (Vector.Count - 1); + nLength = (IntPtr)(((length & (Vector.Count - 1)) + unaligned) & (Vector.Count - 1)); + } + } + SequentialScan: +#endif + while ((byte*)nLength >= (byte*)8) + { + nLength -= 8; + index -= 8; + + if (uValue == Unsafe.Add(ref searchSpace, index + 7)) + goto Found7; + if (uValue == Unsafe.Add(ref searchSpace, index + 6)) + goto Found6; + if (uValue == Unsafe.Add(ref searchSpace, index + 5)) + goto Found5; + if (uValue == Unsafe.Add(ref searchSpace, index + 4)) + goto Found4; + if (uValue == Unsafe.Add(ref searchSpace, index + 3)) + goto Found3; + if (uValue == Unsafe.Add(ref searchSpace, index + 2)) + goto Found2; + if (uValue == Unsafe.Add(ref searchSpace, index + 1)) + goto Found1; + if (uValue == Unsafe.Add(ref searchSpace, index)) + goto Found; + } + + if ((byte*)nLength >= (byte*)4) + { + nLength -= 4; + index -= 4; + + if (uValue == Unsafe.Add(ref searchSpace, index + 3)) + goto Found3; + if (uValue == Unsafe.Add(ref searchSpace, index + 2)) + goto Found2; + if (uValue == Unsafe.Add(ref searchSpace, index + 1)) + goto Found1; + if (uValue == Unsafe.Add(ref searchSpace, index)) + goto Found; + } + + while ((byte*)nLength > (byte*)0) + { + nLength -= 1; + index -= 1; + + if (uValue == Unsafe.Add(ref searchSpace, index)) + goto Found; + } +#if !netstandard11 + if (Vector.IsHardwareAccelerated && ((int)(byte*)index > 0)) + { + nLength = (IntPtr)(uint)((uint)index & ~(Vector.Count - 1)); + + // Get comparison Vector + Vector vComparison = GetVector(value); + while ((byte*)nLength > (byte*)(Vector.Count - 1)) + { + var vMatches = Vector.Equals(vComparison, Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index - Vector.Count))); + if (Vector.Zero.Equals(vMatches)) + { + index -= Vector.Count; + nLength -= Vector.Count; + continue; + } + // Find offset of first match + return (int)(byte*)(index) - Vector.Count + LocateLastFoundByte(vMatches); + } + if ((int)(byte*)index > 0) + { + nLength = index; + goto SequentialScan; + } + } +#endif + return -1; + Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549 + return (int)(byte*)index; + Found1: + return (int)(byte*)(index + 1); + Found2: + return (int)(byte*)(index + 2); + Found3: + return (int)(byte*)(index + 3); + Found4: + return (int)(byte*)(index + 4); + Found5: + return (int)(byte*)(index + 5); + Found6: + return (int)(byte*)(index + 6); + Found7: + return (int)(byte*)(index + 7); + } + public static unsafe int IndexOfAny(ref byte searchSpace, byte value0, byte value1, int length) { Debug.Assert(length >= 0); @@ -194,7 +364,7 @@ namespace System uint uValue1 = value1; // Use uint for comparisons to avoid unnecessary 8->32 extensions IntPtr index = (IntPtr)0; // Use UIntPtr for arithmetic to avoid unnecessary 64->32->64 truncations IntPtr nLength = (IntPtr)(uint)length; -#if !netstandard11 && !MONO +#if !netstandard11 if (Vector.IsHardwareAccelerated && length >= Vector.Count * 2) { unchecked @@ -268,7 +438,7 @@ namespace System index += 1; } -#if !netstandard11 && !MONO +#if !netstandard11 if (Vector.IsHardwareAccelerated && ((int)(byte*)index < length)) { nLength = (IntPtr)(uint)((length - (uint)index) & ~(Vector.Count - 1)); @@ -278,7 +448,7 @@ namespace System while ((byte*)nLength > (byte*)index) { - var vData = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index)); + Vector vData = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index)); var vMatches = Vector.BitwiseOr( Vector.Equals(vData, values0), Vector.Equals(vData, values1)); @@ -329,7 +499,7 @@ namespace System uint uValue2 = value2; // Use uint for comparisons to avoid unnecessary 8->32 extensions IntPtr index = (IntPtr)0; // Use UIntPtr for arithmetic to avoid unnecessary 64->32->64 truncations IntPtr nLength = (IntPtr)(uint)length; -#if !netstandard11 && !MONO +#if !netstandard11 if (Vector.IsHardwareAccelerated && length >= Vector.Count * 2) { unchecked @@ -403,7 +573,7 @@ namespace System index += 1; } -#if !netstandard11 && !MONO +#if !netstandard11 if (Vector.IsHardwareAccelerated && ((int)(byte*)index < length)) { nLength = (IntPtr)(uint)((length - (uint)index) & ~(Vector.Count - 1)); @@ -413,7 +583,7 @@ namespace System Vector values2 = GetVector(value2); while ((byte*)nLength > (byte*)index) { - var vData = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index)); + Vector vData = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index)); var vMatches = Vector.BitwiseOr( Vector.BitwiseOr( @@ -459,6 +629,269 @@ namespace System return (int)(byte*)(index + 7); } + public static unsafe int LastIndexOfAny(ref byte searchSpace, byte value0, byte value1, int length) + { + Debug.Assert(length >= 0); + + uint uValue0 = value0; // Use uint for comparisons to avoid unnecessary 8->32 extensions + uint uValue1 = value1; // Use uint for comparisons to avoid unnecessary 8->32 extensions + IntPtr index = (IntPtr)(uint)length; // Use UIntPtr for arithmetic to avoid unnecessary 64->32->64 truncations + IntPtr nLength = (IntPtr)(uint)length; +#if !netstandard11 + if (Vector.IsHardwareAccelerated && length >= Vector.Count * 2) + { + unchecked + { + int unaligned = (int)(byte*)Unsafe.AsPointer(ref searchSpace) & (Vector.Count - 1); + nLength = (IntPtr)(((length & (Vector.Count - 1)) + unaligned) & (Vector.Count - 1)); + } + } + SequentialScan: +#endif + uint lookUp; + while ((byte*)nLength >= (byte*)8) + { + nLength -= 8; + index -= 8; + + lookUp = Unsafe.Add(ref searchSpace, index + 7); + if (uValue0 == lookUp || uValue1 == lookUp) + goto Found7; + lookUp = Unsafe.Add(ref searchSpace, index + 6); + if (uValue0 == lookUp || uValue1 == lookUp) + goto Found6; + lookUp = Unsafe.Add(ref searchSpace, index + 5); + if (uValue0 == lookUp || uValue1 == lookUp) + goto Found5; + lookUp = Unsafe.Add(ref searchSpace, index + 4); + if (uValue0 == lookUp || uValue1 == lookUp) + goto Found4; + lookUp = Unsafe.Add(ref searchSpace, index + 3); + if (uValue0 == lookUp || uValue1 == lookUp) + goto Found3; + lookUp = Unsafe.Add(ref searchSpace, index + 2); + if (uValue0 == lookUp || uValue1 == lookUp) + goto Found2; + lookUp = Unsafe.Add(ref searchSpace, index + 1); + if (uValue0 == lookUp || uValue1 == lookUp) + goto Found1; + lookUp = Unsafe.Add(ref searchSpace, index); + if (uValue0 == lookUp || uValue1 == lookUp) + goto Found; + } + + if ((byte*)nLength >= (byte*)4) + { + nLength -= 4; + index -= 4; + + lookUp = Unsafe.Add(ref searchSpace, index + 3); + if (uValue0 == lookUp || uValue1 == lookUp) + goto Found3; + lookUp = Unsafe.Add(ref searchSpace, index + 2); + if (uValue0 == lookUp || uValue1 == lookUp) + goto Found2; + lookUp = Unsafe.Add(ref searchSpace, index + 1); + if (uValue0 == lookUp || uValue1 == lookUp) + goto Found1; + lookUp = Unsafe.Add(ref searchSpace, index); + if (uValue0 == lookUp || uValue1 == lookUp) + goto Found; + } + + while ((byte*)nLength > (byte*)0) + { + nLength -= 1; + index -= 1; + + lookUp = Unsafe.Add(ref searchSpace, index); + if (uValue0 == lookUp || uValue1 == lookUp) + goto Found; + } +#if !netstandard11 + if (Vector.IsHardwareAccelerated && ((int)(byte*)index > 0)) + { + nLength = (IntPtr)(uint)((uint)index & ~(Vector.Count - 1)); + // Get comparison Vector + Vector values0 = GetVector(value0); + Vector values1 = GetVector(value1); + + while ((byte*)nLength > (byte*)(Vector.Count - 1)) + { + Vector vData = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index - Vector.Count)); + var vMatches = Vector.BitwiseOr( + Vector.Equals(vData, values0), + Vector.Equals(vData, values1)); + if (Vector.Zero.Equals(vMatches)) + { + index -= Vector.Count; + nLength -= Vector.Count; + continue; + } + // Find offset of first match + return (int)(byte*)(index) - Vector.Count + LocateLastFoundByte(vMatches); + } + + if ((int)(byte*)index > 0) + { + nLength = index; + goto SequentialScan; + } + } +#endif + return -1; + Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549 + return (int)(byte*)index; + Found1: + return (int)(byte*)(index + 1); + Found2: + return (int)(byte*)(index + 2); + Found3: + return (int)(byte*)(index + 3); + Found4: + return (int)(byte*)(index + 4); + Found5: + return (int)(byte*)(index + 5); + Found6: + return (int)(byte*)(index + 6); + Found7: + return (int)(byte*)(index + 7); + } + + public static unsafe int LastIndexOfAny(ref byte searchSpace, byte value0, byte value1, byte value2, int length) + { + Debug.Assert(length >= 0); + + uint uValue0 = value0; // Use uint for comparisons to avoid unnecessary 8->32 extensions + uint uValue1 = value1; // Use uint for comparisons to avoid unnecessary 8->32 extensions + uint uValue2 = value2; // Use uint for comparisons to avoid unnecessary 8->32 extensions + IntPtr index = (IntPtr)(uint)length; // Use UIntPtr for arithmetic to avoid unnecessary 64->32->64 truncations + IntPtr nLength = (IntPtr)(uint)length; +#if !netstandard11 + if (Vector.IsHardwareAccelerated && length >= Vector.Count * 2) + { + unchecked + { + int unaligned = (int)(byte*)Unsafe.AsPointer(ref searchSpace) & (Vector.Count - 1); + nLength = (IntPtr)(((length & (Vector.Count - 1)) + unaligned) & (Vector.Count - 1)); + } + } + SequentialScan: +#endif + uint lookUp; + while ((byte*)nLength >= (byte*)8) + { + nLength -= 8; + index -= 8; + + lookUp = Unsafe.Add(ref searchSpace, index + 7); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) + goto Found7; + lookUp = Unsafe.Add(ref searchSpace, index + 6); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) + goto Found6; + lookUp = Unsafe.Add(ref searchSpace, index + 5); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) + goto Found5; + lookUp = Unsafe.Add(ref searchSpace, index + 4); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) + goto Found4; + lookUp = Unsafe.Add(ref searchSpace, index + 3); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) + goto Found3; + lookUp = Unsafe.Add(ref searchSpace, index + 2); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) + goto Found2; + lookUp = Unsafe.Add(ref searchSpace, index + 1); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) + goto Found1; + lookUp = Unsafe.Add(ref searchSpace, index); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) + goto Found; + } + + if ((byte*)nLength >= (byte*)4) + { + nLength -= 4; + index -= 4; + + lookUp = Unsafe.Add(ref searchSpace, index + 3); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) + goto Found3; + lookUp = Unsafe.Add(ref searchSpace, index + 2); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) + goto Found2; + lookUp = Unsafe.Add(ref searchSpace, index + 1); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) + goto Found1; + lookUp = Unsafe.Add(ref searchSpace, index); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) + goto Found; + } + + while ((byte*)nLength > (byte*)0) + { + nLength -= 1; + index -= 1; + + lookUp = Unsafe.Add(ref searchSpace, index); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) + goto Found; + } +#if !netstandard11 + if (Vector.IsHardwareAccelerated && ((int)(byte*)index > 0)) + { + nLength = (IntPtr)(uint)((uint)index & ~(Vector.Count - 1)); + // Get comparison Vector + Vector values0 = GetVector(value0); + Vector values1 = GetVector(value1); + Vector values2 = GetVector(value2); + while ((byte*)nLength > (byte*)(Vector.Count - 1)) + { + Vector vData = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index - Vector.Count)); + + var vMatches = Vector.BitwiseOr( + Vector.BitwiseOr( + Vector.Equals(vData, values0), + Vector.Equals(vData, values1)), + Vector.Equals(vData, values2)); + + if (Vector.Zero.Equals(vMatches)) + { + index -= Vector.Count; + nLength -= Vector.Count; + continue; + } + // Find offset of first match + return (int)(byte*)(index) - Vector.Count + LocateLastFoundByte(vMatches); + } + + if ((int)(byte*)index > 0) + { + nLength = index; + goto SequentialScan; + } + } +#endif + return -1; + Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549 + return (int)(byte*)index; + Found1: + return (int)(byte*)(index + 1); + Found2: + return (int)(byte*)(index + 2); + Found3: + return (int)(byte*)(index + 3); + Found4: + return (int)(byte*)(index + 4); + Found5: + return (int)(byte*)(index + 5); + Found6: + return (int)(byte*)(index + 6); + Found7: + return (int)(byte*)(index + 7); + } + public static unsafe bool SequenceEqual(ref byte first, ref byte second, int length) { Debug.Assert(length >= 0); @@ -469,7 +902,7 @@ namespace System IntPtr i = (IntPtr)0; // Use IntPtr and byte* for arithmetic to avoid unnecessary 64->32->64 truncations IntPtr n = (IntPtr)length; -#if !netstandard11 && !MONO +#if !netstandard11 if (Vector.IsHardwareAccelerated && (byte*)n >= (byte*)Vector.Count) { n -= Vector.Count; @@ -517,7 +950,7 @@ namespace System return false; } -#if !netstandard11 && !MONO +#if !netstandard11 // Vector sub-search adapted from https://github.com/aspnet/KestrelHttpServer/pull/1138 [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int LocateFirstFoundByte(Vector match) @@ -540,7 +973,87 @@ namespace System } #endif -#if !netstandard11 && !MONO + public static unsafe int SequenceCompareTo(ref byte first, int firstLength, ref byte second, int secondLength) + { + Debug.Assert(firstLength >= 0); + Debug.Assert(secondLength >= 0); + + if (Unsafe.AreSame(ref first, ref second)) + goto Equal; + + var minLength = firstLength; + if (minLength > secondLength) minLength = secondLength; + + IntPtr i = (IntPtr)0; // Use IntPtr and byte* for arithmetic to avoid unnecessary 64->32->64 truncations + IntPtr n = (IntPtr)minLength; + +#if !netstandard11 + if (Vector.IsHardwareAccelerated && (byte*)n > (byte*)Vector.Count) + { + n -= Vector.Count; + while ((byte*)n > (byte*)i) + { + if (Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref first, i)) != + Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref second, i))) + { + goto NotEqual; + } + i += Vector.Count; + } + goto NotEqual; + } +#endif + + if ((byte*)n > (byte*)sizeof(UIntPtr)) + { + n -= sizeof(UIntPtr); + while ((byte*)n > (byte*)i) + { + if (Unsafe.ReadUnaligned(ref Unsafe.AddByteOffset(ref first, i)) != + Unsafe.ReadUnaligned(ref Unsafe.AddByteOffset(ref second, i))) + { + goto NotEqual; + } + i += sizeof(UIntPtr); + } + } + + NotEqual: // Workaround for https://github.com/dotnet/coreclr/issues/13549 + while((byte*)minLength > (byte*)i) + { + int result = Unsafe.AddByteOffset(ref first, i).CompareTo(Unsafe.AddByteOffset(ref second, i)); + if (result != 0) return result; + i += 1; + } + + Equal: + return firstLength - secondLength; + } + +#if !netstandard11 + // Vector sub-search adapted from https://github.com/aspnet/KestrelHttpServer/pull/1138 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int LocateLastFoundByte(Vector match) + { + var vector64 = Vector.AsVectorUInt64(match); + ulong candidate = 0; + int i = Vector.Count - 1; + // Pattern unrolled by jit https://github.com/dotnet/coreclr/pull/8001 + for (; i >= 0; i--) + { + candidate = vector64[i]; + if (candidate != 0) + { + break; + } + } + + // Single LEA instruction with jitted const (using function result) + return i * 8 + LocateLastFoundByte(candidate); + } +#endif + +#if !netstandard11 [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int LocateFirstFoundByte(ulong match) { @@ -554,7 +1067,22 @@ namespace System } #endif -#if !netstandard11 && !MONO +#if !netstandard11 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int LocateLastFoundByte(ulong match) + { + // Find the most significant byte that has its highest bit set + int index = 7; + while ((long)match > 0) + { + match = match << 8; + index--; + } + return index; + } +#endif + +#if !netstandard11 [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector GetVector(byte vectorByte) { @@ -569,7 +1097,7 @@ namespace System } #endif -#if !netstandard11 && !MONO +#if !netstandard11 private const ulong XorPowerOfTwoToHighByte = (0x07ul | 0x06ul << 8 | 0x05ul << 16 | diff --git a/external/corefx/src/System.Memory/src/System/SpanHelpers.cs b/external/corefx/src/System.Memory/src/System/SpanHelpers.cs index 69014b56a5..4289fa95e3 100644 --- a/external/corefx/src/System.Memory/src/System/SpanHelpers.cs +++ b/external/corefx/src/System.Memory/src/System/SpanHelpers.cs @@ -126,7 +126,7 @@ namespace System /// /// Determine if a type is eligible for storage in unmanaged memory. - /// Portable equivalent of RuntimeHelpers.IsReferenceOrContainsReferences<T>() + /// Portable equivalent of RuntimeHelpers.IsReferenceOrContainsReferences{T}() /// public static bool IsReferenceOrContainsReferences() => PerTypeValues.IsReferenceOrContainsReferences; diff --git a/external/corefx/src/System.Memory/src/System/ThrowHelper.cs b/external/corefx/src/System.Memory/src/System/ThrowHelper.cs index 4adc35bdda..81d3f793c9 100644 --- a/external/corefx/src/System.Memory/src/System/ThrowHelper.cs +++ b/external/corefx/src/System.Memory/src/System/ThrowHelper.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Runtime.CompilerServices; +using System.Buffers; namespace System { @@ -27,9 +28,9 @@ namespace System [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateArgumentNullException(ExceptionArgument argument) { return new ArgumentNullException(argument.ToString()); } - internal static void ThrowArrayTypeMismatchException_ArrayTypeMustBeExactMatch(Type type) { throw CreateArrayTypeMismatchException_ArrayTypeMustBeExactMatch(type); } + internal static void ThrowArrayTypeMismatchException() { throw CreateArrayTypeMismatchException(); } [MethodImpl(MethodImplOptions.NoInlining)] - private static Exception CreateArrayTypeMismatchException_ArrayTypeMustBeExactMatch(Type type) { return new ArrayTypeMismatchException(SR.Format(SR.ArrayTypeMustBeExactMatch, type)); } + private static Exception CreateArrayTypeMismatchException() { return new ArrayTypeMismatchException(); } internal static void ThrowArgumentException_InvalidTypeWithPointersNotSupported(Type type) { throw CreateArgumentException_InvalidTypeWithPointersNotSupported(type); } [MethodImpl(MethodImplOptions.NoInlining)] @@ -43,10 +44,22 @@ namespace System [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateIndexOutOfRangeException() { return new IndexOutOfRangeException(); } + internal static void ThrowArgumentOutOfRangeException() { throw CreateArgumentOutOfRangeException(); } + [MethodImpl(MethodImplOptions.NoInlining)] + private static Exception CreateArgumentOutOfRangeException() { return new ArgumentOutOfRangeException(); } + internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument) { throw CreateArgumentOutOfRangeException(argument); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateArgumentOutOfRangeException(ExceptionArgument argument) { return new ArgumentOutOfRangeException(argument.ToString()); } + internal static void ThrowArgumentOutOfRangeException_PrecisionTooLarge() { throw CreateArgumentOutOfRangeException_PrecisionTooLarge(); } + [MethodImpl(MethodImplOptions.NoInlining)] + private static Exception CreateArgumentOutOfRangeException_PrecisionTooLarge() { return new ArgumentOutOfRangeException("precision", SR.Format(SR.Argument_PrecisionTooLarge, StandardFormat.MaxPrecision)); } + + internal static void ThrowArgumentOutOfRangeException_SymbolDoesNotFit() { throw CreateArgumentOutOfRangeException_SymbolDoesNotFit(); } + [MethodImpl(MethodImplOptions.NoInlining)] + private static Exception CreateArgumentOutOfRangeException_SymbolDoesNotFit() { return new ArgumentOutOfRangeException("symbol", SR.Argument_BadFormatSpecifier); } + internal static void ThrowInvalidOperationException_OutstandingReferences() { throw CreateInvalidOperationException_OutstandingReferences(); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateInvalidOperationException_OutstandingReferences() { return new InvalidOperationException(SR.OutstandingReferences); } @@ -54,6 +67,35 @@ namespace System internal static void ThrowObjectDisposedException_MemoryDisposed(string objectName) { throw CreateObjectDisposedException_MemoryDisposed(objectName); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateObjectDisposedException_MemoryDisposed(string objectName) { return new ObjectDisposedException(objectName, SR.MemoryDisposed); } + + internal static void ThrowFormatException_BadFormatSpecifier() { throw CreateFormatException_BadFormatSpecifier(); } + [MethodImpl(MethodImplOptions.NoInlining)] + private static Exception CreateFormatException_BadFormatSpecifier() { return new FormatException(SR.Argument_BadFormatSpecifier); } + + internal static void ThrowArgumentException_OverlapAlignmentMismatch() { throw CreateArgumentException_OverlapAlignmentMismatch(); } + [MethodImpl(MethodImplOptions.NoInlining)] + private static Exception CreateArgumentException_OverlapAlignmentMismatch() { return new ArgumentException(SR.Argument_OverlapAlignmentMismatch); } + + // + // Enable use of ThrowHelper from TryFormat() routines without introducing dozens of non-code-coveraged "bytesWritten = 0; return false" boilerplate. + // + public static bool TryFormatThrowFormatException(out int bytesWritten) + { + bytesWritten = 0; + ThrowHelper.ThrowFormatException_BadFormatSpecifier(); + return false; + } + + // + // Enable use of ThrowHelper from TryParse() routines without introducing dozens of non-code-coveraged "value= default; bytesConsumed = 0; return false" boilerplate. + // + public static bool TryParseThrowFormatException(out T value, out int bytesConsumed) + { + value = default; + bytesConsumed = 0; + ThrowHelper.ThrowFormatException_BadFormatSpecifier(); + return false; + } } #if !MONO internal enum ExceptionArgument @@ -64,7 +106,9 @@ namespace System text, obj, ownedMemory, - pointer + pointer, + comparable, + comparer } #endif } diff --git a/external/corefx/src/System.Memory/tests/AllocationHelper.cs b/external/corefx/src/System.Memory/tests/AllocationHelper.cs index 69311ba0ab..e1ccf2a3d6 100644 --- a/external/corefx/src/System.Memory/tests/AllocationHelper.cs +++ b/external/corefx/src/System.Memory/tests/AllocationHelper.cs @@ -1,3 +1,7 @@ +// 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.Runtime.InteropServices; using System.Threading; @@ -11,14 +15,14 @@ namespace System.SpanTests /// static class AllocationHelper { - private static readonly Mutex MemoryLock = new Mutex(); - private static readonly TimeSpan WaitTimeout = TimeSpan.FromSeconds(120); + private static readonly Mutex s_memoryLock = new Mutex(); + private static readonly TimeSpan s_waitTimeout = TimeSpan.FromSeconds(120); public static bool TryAllocNative(IntPtr size, out IntPtr memory) { memory = IntPtr.Zero; - if (!MemoryLock.WaitOne(WaitTimeout)) + if (!s_memoryLock.WaitOne(s_waitTimeout)) return false; try @@ -28,7 +32,7 @@ namespace System.SpanTests catch (OutOfMemoryException) { memory = IntPtr.Zero; - MemoryLock.ReleaseMutex(); + s_memoryLock.ReleaseMutex(); } return memory != IntPtr.Zero; @@ -43,7 +47,7 @@ namespace System.SpanTests } finally { - MemoryLock.ReleaseMutex(); + s_memoryLock.ReleaseMutex(); } } } diff --git a/external/corefx/src/System.Memory/tests/Base64/Base64DecoderUnitTests.cs b/external/corefx/src/System.Memory/tests/Base64/Base64DecoderUnitTests.cs new file mode 100644 index 0000000000..15a6733060 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Base64/Base64DecoderUnitTests.cs @@ -0,0 +1,540 @@ +// 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.Text; +using Xunit; + +namespace System.Buffers.Text.Tests +{ + public class Base64DecoderUnitTests + { + [Fact] + public void BasicDecoding() + { + var rnd = new Random(42); + for (int i = 0; i < 10; i++) + { + int numBytes = rnd.Next(100, 1000 * 1000); + while (numBytes % 4 != 0) + { + numBytes = rnd.Next(100, 1000 * 1000); + } + Span source = new byte[numBytes]; + Base64TestHelper.InitalizeDecodableBytes(source, numBytes); + + Span decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)]; + Assert.Equal(OperationStatus.Done, + Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount)); + Assert.Equal(source.Length, consumed); + Assert.Equal(decodedBytes.Length, decodedByteCount); + Assert.True(Base64TestHelper.VerifyDecodingCorrectness(source.Length, decodedBytes.Length, source, decodedBytes)); + } + } + + [Fact] + public void DecodeEmptySpan() + { + Span source = Span.Empty; + Span decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)]; + + Assert.Equal(OperationStatus.Done, + Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount)); + Assert.Equal(source.Length, consumed); + Assert.Equal(decodedBytes.Length, decodedByteCount); + Assert.True(Base64TestHelper.VerifyDecodingCorrectness(source.Length, decodedBytes.Length, source, decodedBytes)); + } + + [Fact] + public void BasicDecodingWithFinalBlockFalse() + { + var rnd = new Random(42); + for (int i = 0; i < 10; i++) + { + int numBytes = rnd.Next(100, 1000 * 1000); + while (numBytes % 4 != 0) + { + numBytes = rnd.Next(100, 1000 * 1000); + } + Span source = new byte[numBytes]; + Base64TestHelper.InitalizeDecodableBytes(source, numBytes); + + Span decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)]; + int expectedConsumed = source.Length / 4 * 4; // only consume closest multiple of four since isFinalBlock is false + + Assert.Equal(OperationStatus.NeedMoreData, + Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount, isFinalBlock: false)); + Assert.Equal(expectedConsumed, consumed); + Assert.Equal(decodedBytes.Length, decodedByteCount); + Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, decodedBytes.Length, source, decodedBytes)); + } + } + + [Theory] + [InlineData("A", 0, 0)] + [InlineData("AQ", 0, 0)] + [InlineData("AQI", 0, 0)] + [InlineData("AQIDBA", 4, 3)] + [InlineData("AQIDBAU", 4, 3)] + [InlineData("AQID", 4, 3)] + [InlineData("AQIDBAUG", 8, 6)] + public void BasicDecodingWithFinalBlockFalseKnownInputNeedMoreData(string inputString, int expectedConsumed, int expectedWritten) + { + Span source = Encoding.ASCII.GetBytes(inputString); + Span decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)]; + + Assert.Equal(OperationStatus.NeedMoreData, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount, isFinalBlock: false)); + Assert.Equal(expectedConsumed, consumed); + Assert.Equal(expectedWritten, decodedByteCount); // expectedWritten == decodedBytes.Length + Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, decodedBytes.Length, source, decodedBytes)); + } + + [Theory] + [InlineData("AQ==", 0, 0)] + [InlineData("AQI=", 0, 0)] + [InlineData("AQIDBA==", 4, 3)] + [InlineData("AQIDBAU=", 4, 3)] + public void BasicDecodingWithFinalBlockFalseKnownInputInvalid(string inputString, int expectedConsumed, int expectedWritten) + { + Span source = Encoding.ASCII.GetBytes(inputString); + Span decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)]; + + Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount, isFinalBlock: false)); + Assert.Equal(expectedConsumed, consumed); + Assert.Equal(expectedWritten, decodedByteCount); + Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, expectedWritten, source, decodedBytes)); + } + + [Theory] + [InlineData("A", 0, 0)] + [InlineData("AQ", 0, 0)] + [InlineData("AQI", 0, 0)] + [InlineData("AQIDBA", 4, 3)] + [InlineData("AQIDBAU", 4, 3)] + public void BasicDecodingWithFinalBlockTrueKnownInputInvalid(string inputString, int expectedConsumed, int expectedWritten) + { + Span source = Encoding.ASCII.GetBytes(inputString); + Span decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)]; + + Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount)); + Assert.Equal(expectedConsumed, consumed); + Assert.Equal(expectedWritten, decodedByteCount); // expectedWritten == decodedBytes.Length + Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, decodedBytes.Length, source, decodedBytes)); + } + + [Theory] + [InlineData("AQ==", 4, 1)] + [InlineData("AQI=", 4, 2)] + [InlineData("AQID", 4, 3)] + [InlineData("AQIDBA==", 8, 4)] + [InlineData("AQIDBAU=", 8, 5)] + [InlineData("AQIDBAUG", 8, 6)] + public void BasicDecodingWithFinalBlockTrueKnownInputDone(string inputString, int expectedConsumed, int expectedWritten) + { + Span source = Encoding.ASCII.GetBytes(inputString); + Span decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)]; + + Assert.Equal(OperationStatus.Done, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount)); + Assert.Equal(expectedConsumed, consumed); + Assert.Equal(expectedWritten, decodedByteCount); + Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, expectedWritten, source, decodedBytes)); + } + + [Fact] + public void DecodingInvalidBytes() + { + // Invalid Bytes: + // 0-42 + // 44-46 + // 58-64 + // 91-96 + // 123-255 + byte[] invalidBytes = Base64TestHelper.InvalidBytes; + Assert.Equal(byte.MaxValue + 1 - 64, invalidBytes.Length); // 192 + + for (int j = 0; j < 8; j++) + { + Span source = new byte[8] { 50, 50, 50, 50, 80, 80, 80, 80 }; // valid input - "2222PPPP" + Span decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)]; + + for (int i = 0; i < invalidBytes.Length; i++) + { + // Don't test padding (byte 61 i.e. '='), which is tested in DecodingInvalidBytesPadding + if (invalidBytes[i] == Base64TestHelper.s_encodingPad) + continue; + + // replace one byte with an invalid input + source[j] = invalidBytes[i]; + + Assert.Equal(OperationStatus.InvalidData, + Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount)); + + if (j < 4) + { + Assert.Equal(0, consumed); + Assert.Equal(0, decodedByteCount); + } + else + { + Assert.Equal(4, consumed); + Assert.Equal(3, decodedByteCount); + Assert.True(Base64TestHelper.VerifyDecodingCorrectness(4, 3, source, decodedBytes)); + } + } + } + + // Input that is not a multiple of 4 is considered invalid + { + Span source = new byte[7] { 50, 50, 50, 50, 80, 80, 80 }; // incomplete input - "2222PPP" + Span decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)]; + Assert.Equal(OperationStatus.InvalidData, + Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount)); + Assert.Equal(4, consumed); + Assert.Equal(3, decodedByteCount); + Assert.True(Base64TestHelper.VerifyDecodingCorrectness(4, 3, source, decodedBytes)); + } + } + + [Fact] + public void DecodingInvalidBytesPadding() + { + // Only last 2 bytes can be padding, all other occurrence of padding is invalid + for (int j = 0; j < 7; j++) + { + Span source = new byte[] { 50, 50, 50, 50, 80, 80, 80, 80 }; // valid input - "2222PPPP" + Span decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)]; + source[j] = Base64TestHelper.s_encodingPad; + Assert.Equal(OperationStatus.InvalidData, + Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount)); + + if (j < 4) + { + Assert.Equal(0, consumed); + Assert.Equal(0, decodedByteCount); + } + else + { + Assert.Equal(4, consumed); + Assert.Equal(3, decodedByteCount); + Assert.True(Base64TestHelper.VerifyDecodingCorrectness(4, 3, source, decodedBytes)); + } + } + + // Invalid input with valid padding + { + Span source = new byte[] { 50, 50, 50, 50, 80, 42, 42, 42 }; + Span decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)]; + source[6] = Base64TestHelper.s_encodingPad; + source[7] = Base64TestHelper.s_encodingPad; // invalid input - "2222P*==" + Assert.Equal(OperationStatus.InvalidData, + Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount)); + + Assert.Equal(4, consumed); + Assert.Equal(3, decodedByteCount); + Assert.True(Base64TestHelper.VerifyDecodingCorrectness(4, 3, source, decodedBytes)); + + source = new byte[] { 50, 50, 50, 50, 80, 42, 42, 42 }; + decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)]; + source[7] = Base64TestHelper.s_encodingPad; // invalid input - "2222PP**=" + Assert.Equal(OperationStatus.InvalidData, + Base64.DecodeFromUtf8(source, decodedBytes, out consumed, out decodedByteCount)); + + Assert.Equal(4, consumed); + Assert.Equal(3, decodedByteCount); + Assert.True(Base64TestHelper.VerifyDecodingCorrectness(4, 3, source, decodedBytes)); + } + + // The last byte or the last 2 bytes being the padding character is valid + { + Span source = new byte[] { 50, 50, 50, 50, 80, 80, 80, 80 }; + Span decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)]; + source[6] = Base64TestHelper.s_encodingPad; + source[7] = Base64TestHelper.s_encodingPad; // valid input - "2222PP==" + Assert.Equal(OperationStatus.Done, + Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount)); + + Assert.Equal(source.Length, consumed); + Assert.Equal(4, decodedByteCount); + Assert.True(Base64TestHelper.VerifyDecodingCorrectness(source.Length, 4, source, decodedBytes)); + + source = new byte[] { 50, 50, 50, 50, 80, 80, 80, 80 }; + decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)]; + source[7] = Base64TestHelper.s_encodingPad; // valid input - "2222PPP=" + Assert.Equal(OperationStatus.Done, + Base64.DecodeFromUtf8(source, decodedBytes, out consumed, out decodedByteCount)); + + Assert.Equal(source.Length, consumed); + Assert.Equal(5, decodedByteCount); + Assert.True(Base64TestHelper.VerifyDecodingCorrectness(source.Length, 5, source, decodedBytes)); + } + } + + [Fact] + public void DecodingOutputTooSmall() + { + for (int numBytes = 5; numBytes < 20; numBytes++) + { + Span source = new byte[numBytes]; + Base64TestHelper.InitalizeDecodableBytes(source, numBytes); + + Span decodedBytes = new byte[3]; + int consumed, written; + if (numBytes % 4 != 0) + { + Assert.True(OperationStatus.InvalidData == + Base64.DecodeFromUtf8(source, decodedBytes, out consumed, out written), "Number of Input Bytes: " + numBytes); + } + else + { + Assert.True(OperationStatus.DestinationTooSmall == + Base64.DecodeFromUtf8(source, decodedBytes, out consumed, out written), "Number of Input Bytes: " + numBytes); + } + int expectedConsumed = 4; + Assert.Equal(expectedConsumed, consumed); + Assert.Equal(decodedBytes.Length, written); + Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, decodedBytes.Length, source, decodedBytes)); + } + + // Output too small even with padding characters in the input + { + Span source = new byte[12]; + Base64TestHelper.InitalizeDecodableBytes(source); + source[10] = Base64TestHelper.s_encodingPad; + source[11] = Base64TestHelper.s_encodingPad; + + Span decodedBytes = new byte[6]; + Assert.Equal(OperationStatus.DestinationTooSmall, + Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int written)); + int expectedConsumed = 8; + Assert.Equal(expectedConsumed, consumed); + Assert.Equal(decodedBytes.Length, written); + Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, decodedBytes.Length, source, decodedBytes)); + } + + { + Span source = new byte[12]; + Base64TestHelper.InitalizeDecodableBytes(source); + source[11] = Base64TestHelper.s_encodingPad; + + Span decodedBytes = new byte[7]; + Assert.Equal(OperationStatus.DestinationTooSmall, + Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int written)); + int expectedConsumed = 8; + Assert.Equal(expectedConsumed, consumed); + Assert.Equal(6, written); + Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, 6, source, decodedBytes)); + } + } + + [Fact] + public void DecodingOutputTooSmallRetry() + { + Span source = new byte[1000]; + Base64TestHelper.InitalizeDecodableBytes(source); + + int outputSize = 240; + int requiredSize = Base64.GetMaxDecodedFromUtf8Length(source.Length); + + Span decodedBytes = new byte[outputSize]; + Assert.Equal(OperationStatus.DestinationTooSmall, + Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount)); + int expectedConsumed = decodedBytes.Length / 3 * 4; + Assert.Equal(expectedConsumed, consumed); + Assert.Equal(decodedBytes.Length, decodedByteCount); + Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, decodedBytes.Length, source, decodedBytes)); + + decodedBytes = new byte[requiredSize - outputSize]; + source = source.Slice(consumed); + Assert.Equal(OperationStatus.Done, + Base64.DecodeFromUtf8(source, decodedBytes, out consumed, out decodedByteCount)); + expectedConsumed = decodedBytes.Length / 3 * 4; + Assert.Equal(expectedConsumed, consumed); + Assert.Equal(decodedBytes.Length, decodedByteCount); + Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, decodedBytes.Length, source, decodedBytes)); + } + + [Fact] + public void GetMaxDecodedLength() + { + Span sourceEmpty = Span.Empty; + Assert.Equal(0, Base64.GetMaxDecodedFromUtf8Length(0)); + + // int.MaxValue - (int.MaxValue % 4) => 2147483644, largest multiple of 4 less than int.MaxValue + int[] input = { 0, 4, 8, 12, 16, 20, 2000000000, 2147483640, 2147483644 }; + int[] expected = { 0, 3, 6, 9, 12, 15, 1500000000, 1610612730, 1610612733 }; + + for (int i = 0; i < input.Length; i++) + { + Assert.Equal(expected[i], Base64.GetMaxDecodedFromUtf8Length(input[i])); + } + + // Lengths that are not a multiple of 4. + int[] lengthsNotMultipleOfFour = { 1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 14, 15, 1001, 1002, 1003, 2147483645, 2147483646, 2147483647 }; + int[] expectedOutput = { 0, 0, 0, 3, 3, 3, 6, 6, 6, 9, 9, 9, 750, 750, 750, 1610612733, 1610612733, 1610612733 }; + for (int i = 0; i < lengthsNotMultipleOfFour.Length; i++) + { + Assert.Equal(expectedOutput[i], Base64.GetMaxDecodedFromUtf8Length(lengthsNotMultipleOfFour[i])); + } + + // negative input + Assert.Throws(() => Base64.GetMaxDecodedFromUtf8Length(-1)); + Assert.Throws(() => Base64.GetMaxDecodedFromUtf8Length(int.MinValue)); + } + + [Fact] + public void DecodeInPlace() + { + const int numberOfBytes = 15; + + for (int numberOfBytesToTest = 0; numberOfBytesToTest <= numberOfBytes; numberOfBytesToTest += 4) + { + Span testBytes = new byte[numberOfBytes]; + Base64TestHelper.InitalizeDecodableBytes(testBytes); + string sourceString = Encoding.ASCII.GetString(testBytes.Slice(0, numberOfBytesToTest).ToArray()); + Span expectedBytes = Convert.FromBase64String(sourceString); + + Assert.Equal(OperationStatus.Done, Base64.DecodeFromUtf8InPlace(testBytes.Slice(0, numberOfBytesToTest), out int bytesWritten)); + Assert.Equal(Base64.GetMaxDecodedFromUtf8Length(numberOfBytesToTest), bytesWritten); + Assert.True(expectedBytes.SequenceEqual(testBytes.Slice(0, bytesWritten))); + } + } + + [Fact] + public void EncodeAndDecodeInPlace() + { + byte[] testBytes = new byte[256]; + for (int i = 0; i < 256; i++) + { + testBytes[i] = (byte)i; + } + + for (int value = 0; value < 256; value++) + { + Span sourceBytes = testBytes.AsSpan().Slice(0, value + 1); + Span buffer = new byte[Base64.GetMaxEncodedToUtf8Length(sourceBytes.Length)]; + + Assert.Equal(OperationStatus.Done, Base64.EncodeToUtf8(sourceBytes, buffer, out int consumed, out int written)); + + var encodedText = Encoding.ASCII.GetString(buffer.ToArray()); + var expectedText = Convert.ToBase64String(testBytes, 0, value + 1); + Assert.Equal(expectedText, encodedText); + + Assert.Equal(OperationStatus.Done, Base64.DecodeFromUtf8InPlace(buffer, out int bytesWritten)); + Assert.Equal(sourceBytes.Length, bytesWritten); + Assert.True(sourceBytes.SequenceEqual(buffer.Slice(0, bytesWritten))); + } + } + + [Fact] + public void DecodeInPlaceInvalidBytes() + { + byte[] invalidBytes = Base64TestHelper.InvalidBytes; + + for (int j = 0; j < 8; j++) + { + for (int i = 0; i < invalidBytes.Length; i++) + { + Span buffer = new byte[8] { 50, 50, 50, 50, 80, 80, 80, 80 }; // valid input - "2222PPPP" + + // Don't test padding (byte 61 i.e. '='), which is tested in DecodeInPlaceInvalidBytesPadding + if (invalidBytes[i] == Base64TestHelper.s_encodingPad) + continue; + + // replace one byte with an invalid input + buffer[j] = invalidBytes[i]; + string sourceString = Encoding.ASCII.GetString(buffer.Slice(0, 4).ToArray()); + + Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8InPlace(buffer, out int bytesWritten)); + + if (j < 4) + { + Assert.Equal(0, bytesWritten); + } + else + { + Assert.Equal(3, bytesWritten); + Span expectedBytes = Convert.FromBase64String(sourceString); + Assert.True(expectedBytes.SequenceEqual(buffer.Slice(0, bytesWritten))); + } + } + } + + // Input that is not a multiple of 4 is considered invalid + { + Span buffer = new byte[7] { 50, 50, 50, 50, 80, 80, 80 }; // incomplete input - "2222PPP" + Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8InPlace(buffer, out int bytesWritten)); + Assert.Equal(0, bytesWritten); + } + } + + [Fact] + public void DecodeInPlaceInvalidBytesPadding() + { + // Only last 2 bytes can be padding, all other occurrence of padding is invalid + for (int j = 0; j < 7; j++) + { + Span buffer = new byte[] { 50, 50, 50, 50, 80, 80, 80, 80 }; // valid input - "2222PPPP" + buffer[j] = Base64TestHelper.s_encodingPad; + string sourceString = Encoding.ASCII.GetString(buffer.Slice(0, 4).ToArray()); + + Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8InPlace(buffer, out int bytesWritten)); + + if (j < 4) + { + Assert.Equal(0, bytesWritten); + } + else + { + Assert.Equal(3, bytesWritten); + Span expectedBytes = Convert.FromBase64String(sourceString); + Assert.True(expectedBytes.SequenceEqual(buffer.Slice(0, bytesWritten))); + } + } + + // Invalid input with valid padding + { + Span buffer = new byte[] { 50, 50, 50, 50, 80, 42, 42, 42 }; + buffer[6] = Base64TestHelper.s_encodingPad; + buffer[7] = Base64TestHelper.s_encodingPad; // invalid input - "2222P*==" + string sourceString = Encoding.ASCII.GetString(buffer.Slice(0, 4).ToArray()); + Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8InPlace(buffer, out int bytesWritten)); + Assert.Equal(3, bytesWritten); + Span expectedBytes = Convert.FromBase64String(sourceString); + Assert.True(expectedBytes.SequenceEqual(buffer.Slice(0, bytesWritten))); + } + + { + Span buffer = new byte[] { 50, 50, 50, 50, 80, 42, 42, 42 }; + buffer[7] = Base64TestHelper.s_encodingPad; // invalid input - "2222P**=" + string sourceString = Encoding.ASCII.GetString(buffer.Slice(0, 4).ToArray()); + Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8InPlace(buffer, out int bytesWritten)); + Assert.Equal(3, bytesWritten); + Span expectedBytes = Convert.FromBase64String(sourceString); + Assert.True(expectedBytes.SequenceEqual(buffer.Slice(0, bytesWritten))); + } + + // The last byte or the last 2 bytes being the padding character is valid + { + Span buffer = new byte[] { 50, 50, 50, 50, 80, 80, 80, 80 }; + buffer[6] = Base64TestHelper.s_encodingPad; + buffer[7] = Base64TestHelper.s_encodingPad; // valid input - "2222PP==" + string sourceString = Encoding.ASCII.GetString(buffer.ToArray()); + Assert.Equal(OperationStatus.Done, Base64.DecodeFromUtf8InPlace(buffer, out int bytesWritten)); + Assert.Equal(4, bytesWritten); + Span expectedBytes = Convert.FromBase64String(sourceString); + Assert.True(expectedBytes.SequenceEqual(buffer.Slice(0, bytesWritten))); + } + + { + Span buffer = new byte[] { 50, 50, 50, 50, 80, 80, 80, 80 }; + buffer[7] = Base64TestHelper.s_encodingPad; // valid input - "2222PPP=" + string sourceString = Encoding.ASCII.GetString(buffer.ToArray()); + Assert.Equal(OperationStatus.Done, Base64.DecodeFromUtf8InPlace(buffer, out int bytesWritten)); + Assert.Equal(5, bytesWritten); + Span expectedBytes = Convert.FromBase64String(sourceString); + Assert.True(expectedBytes.SequenceEqual(buffer.Slice(0, bytesWritten))); + } + } + + } +} diff --git a/external/corefx/src/System.Memory/tests/Base64/Base64EncoderUnitTests.cs b/external/corefx/src/System.Memory/tests/Base64/Base64EncoderUnitTests.cs new file mode 100644 index 0000000000..c3f5335106 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Base64/Base64EncoderUnitTests.cs @@ -0,0 +1,269 @@ +// 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.Text; +using Xunit; + +namespace System.Buffers.Text.Tests +{ + public class Base64EncoderUnitTests + { + [Fact] + public void BasicEncodingAndDecoding() + { + var bytes = new byte[byte.MaxValue + 1]; + for (int i = 0; i < byte.MaxValue + 1; i++) + { + bytes[i] = (byte)i; + } + + for (int value = 0; value < 256; value++) + { + Span sourceBytes = bytes.AsSpan().Slice(0, value + 1); + Span encodedBytes = new byte[Base64.GetMaxEncodedToUtf8Length(sourceBytes.Length)]; + Assert.Equal(OperationStatus.Done, Base64.EncodeToUtf8(sourceBytes, encodedBytes, out int consumed, out int encodedBytesCount)); + Assert.Equal(sourceBytes.Length, consumed); + Assert.Equal(encodedBytes.Length, encodedBytesCount); + + string encodedText = Encoding.ASCII.GetString(encodedBytes.ToArray()); + string expectedText = Convert.ToBase64String(bytes, 0, value + 1); + Assert.Equal(expectedText, encodedText); + + if (encodedBytes.Length % 4 == 0) + { + Span decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(encodedBytes.Length)]; + Assert.Equal(OperationStatus.Done, Base64.DecodeFromUtf8(encodedBytes, decodedBytes, out consumed, out int decodedByteCount)); + Assert.Equal(encodedBytes.Length, consumed); + Assert.Equal(sourceBytes.Length, decodedByteCount); + Assert.True(sourceBytes.SequenceEqual(decodedBytes.Slice(0, decodedByteCount))); + } + } + } + + [Fact] + public void BasicEncoding() + { + var rnd = new Random(42); + for (int i = 0; i < 10; i++) + { + int numBytes = rnd.Next(100, 1000 * 1000); + Span source = new byte[numBytes]; + Base64TestHelper.InitalizeBytes(source, numBytes); + + Span encodedBytes = new byte[Base64.GetMaxEncodedToUtf8Length(source.Length)]; + Assert.Equal(OperationStatus.Done, Base64.EncodeToUtf8(source, encodedBytes, out int consumed, out int encodedBytesCount)); + Assert.Equal(source.Length, consumed); + Assert.Equal(encodedBytes.Length, encodedBytesCount); + Assert.True(Base64TestHelper.VerifyEncodingCorrectness(source.Length, encodedBytes.Length, source, encodedBytes)); + } + } + + [Fact] + public void EncodeEmptySpan() + { + Span source = Span.Empty; + Span encodedBytes = new byte[Base64.GetMaxEncodedToUtf8Length(source.Length)]; + + Assert.Equal(OperationStatus.Done, Base64.EncodeToUtf8(source, encodedBytes, out int consumed, out int encodedBytesCount)); + Assert.Equal(source.Length, consumed); + Assert.Equal(encodedBytes.Length, encodedBytesCount); + Assert.True(Base64TestHelper.VerifyEncodingCorrectness(source.Length, encodedBytes.Length, source, encodedBytes)); + } + + [Fact] + [OuterLoop] + public void EncodeTooLargeSpan() + { + // int.MaxValue - (int.MaxValue % 4) => 2147483644, largest multiple of 4 less than int.MaxValue + // CLR default limit of 2 gigabytes (GB). + try + { + // 1610612734, larger than MaximumEncodeLength, requires output buffer of size 2147483648 (which is > int.MaxValue) + Span source = new byte[(int.MaxValue >> 2) * 3 + 1]; + Span encodedBytes = new byte[2000000000]; + Assert.Equal(OperationStatus.DestinationTooSmall, Base64.EncodeToUtf8(source, encodedBytes, out int consumed, out int encodedBytesCount)); + Assert.Equal((encodedBytes.Length >> 2) * 3, consumed); // encoding 1500000000 bytes fits into buffer of 2000000000 bytes + Assert.Equal(encodedBytes.Length, encodedBytesCount); + } + catch (OutOfMemoryException) + { + // do nothing + } + } + + [Fact] + public void BasicEncodingWithFinalBlockFalse() + { + var rnd = new Random(42); + for (int i = 0; i < 10; i++) + { + int numBytes = rnd.Next(100, 1000 * 1000); + Span source = new byte[numBytes]; + Base64TestHelper.InitalizeBytes(source, numBytes); + Span encodedBytes = new byte[Base64.GetMaxEncodedToUtf8Length(source.Length)]; + int expectedConsumed = source.Length / 3 * 3; // only consume closest multiple of three since isFinalBlock is false + int expectedWritten = source.Length / 3 * 4; + + Assert.Equal(OperationStatus.NeedMoreData, Base64.EncodeToUtf8(source, encodedBytes, out int consumed, out int encodedBytesCount, isFinalBlock: false)); + Assert.Equal(expectedConsumed, consumed); + Assert.Equal(expectedWritten, encodedBytesCount); + Assert.True(Base64TestHelper.VerifyEncodingCorrectness(expectedConsumed, expectedWritten, source, encodedBytes)); + } + } + + [Theory] + [InlineData(1, "", 0, 0)] + [InlineData(2, "", 0, 0)] + [InlineData(3, "AQID", 3, 4)] + [InlineData(4, "AQID", 3, 4)] + [InlineData(5, "AQID", 3, 4)] + [InlineData(6, "AQIDBAUG", 6, 8)] + [InlineData(7, "AQIDBAUG", 6, 8)] + public void BasicEncodingWithFinalBlockFalseKnownInput(int numBytes, string expectedText, int expectedConsumed, int expectedWritten) + { + Span source = new byte[numBytes]; + for (int i = 0; i < numBytes; i++) + { + source[i] = (byte)(i + 1); + } + Span encodedBytes = new byte[Base64.GetMaxEncodedToUtf8Length(source.Length)]; + + Assert.Equal(OperationStatus.NeedMoreData, Base64.EncodeToUtf8(source, encodedBytes, out int consumed, out int encodedBytesCount, isFinalBlock: false)); + Assert.Equal(expectedConsumed, consumed); + Assert.Equal(expectedWritten, encodedBytesCount); + + string encodedText = Encoding.ASCII.GetString(encodedBytes.Slice(0, expectedWritten).ToArray()); + Assert.Equal(expectedText, encodedText); + } + + [Theory] + [InlineData(1, "AQ==", 1, 4)] + [InlineData(2, "AQI=", 2, 4)] + [InlineData(3, "AQID", 3, 4)] + [InlineData(4, "AQIDBA==", 4, 8)] + [InlineData(5, "AQIDBAU=", 5, 8)] + [InlineData(6, "AQIDBAUG", 6, 8)] + [InlineData(7, "AQIDBAUGBw==", 7, 12)] + public void BasicEncodingWithFinalBlockTrueKnownInput(int numBytes, string expectedText, int expectedConsumed, int expectedWritten) + { + Span source = new byte[numBytes]; + for (int i = 0; i < numBytes; i++) + { + source[i] = (byte)(i + 1); + } + Span encodedBytes = new byte[Base64.GetMaxEncodedToUtf8Length(source.Length)]; + + Assert.Equal(OperationStatus.Done, Base64.EncodeToUtf8(source, encodedBytes, out int consumed, out int encodedBytesCount, isFinalBlock: true)); + Assert.Equal(expectedConsumed, consumed); + Assert.Equal(expectedWritten, encodedBytesCount); + + string encodedText = Encoding.ASCII.GetString(encodedBytes.Slice(0, expectedWritten).ToArray()); + Assert.Equal(expectedText, encodedText); + } + + [Fact] + public void EncodingOutputTooSmall() + { + for (int numBytes = 4; numBytes < 20; numBytes++) + { + Span source = new byte[numBytes]; + Base64TestHelper.InitalizeBytes(source, numBytes); + + Span encodedBytes = new byte[4]; + Assert.Equal(OperationStatus.DestinationTooSmall, + Base64.EncodeToUtf8(source, encodedBytes, out int consumed, out int written)); + int expectedConsumed = 3; + Assert.Equal(expectedConsumed, consumed); + Assert.Equal(encodedBytes.Length, written); + Assert.True(Base64TestHelper.VerifyEncodingCorrectness(expectedConsumed, encodedBytes.Length, source, encodedBytes)); + } + } + + [Fact] + public void EncodingOutputTooSmallRetry() + { + Span source = new byte[750]; + Base64TestHelper.InitalizeBytes(source); + + int outputSize = 320; + int requiredSize = Base64.GetMaxEncodedToUtf8Length(source.Length); + + Span encodedBytes = new byte[outputSize]; + Assert.Equal(OperationStatus.DestinationTooSmall, + Base64.EncodeToUtf8(source, encodedBytes, out int consumed, out int written)); + int expectedConsumed = encodedBytes.Length / 4 * 3; + Assert.Equal(expectedConsumed, consumed); + Assert.Equal(encodedBytes.Length, written); + Assert.True(Base64TestHelper.VerifyEncodingCorrectness(expectedConsumed, encodedBytes.Length, source, encodedBytes)); + + encodedBytes = new byte[requiredSize - outputSize]; + source = source.Slice(consumed); + Assert.Equal(OperationStatus.Done, + Base64.EncodeToUtf8(source, encodedBytes, out consumed, out written)); + expectedConsumed = encodedBytes.Length / 4 * 3; + Assert.Equal(expectedConsumed, consumed); + Assert.Equal(encodedBytes.Length, written); + Assert.True(Base64TestHelper.VerifyEncodingCorrectness(expectedConsumed, encodedBytes.Length, source, encodedBytes)); + } + + [Fact] + public void GetMaxEncodedLength() + { + // (int.MaxValue - 4)/(4/3) => 1610612733, otherwise integer overflow + int[] input = { 0, 1, 2, 3, 4, 5, 6, 1610612728, 1610612729, 1610612730, 1610612731, 1610612732, 1610612733 }; + int[] expected = { 0, 4, 4, 4, 8, 8, 8, 2147483640, 2147483640, 2147483640, 2147483644, 2147483644, 2147483644 }; + for (int i = 0; i < input.Length; i++) + { + Assert.Equal(expected[i], Base64.GetMaxEncodedToUtf8Length(input[i])); + } + + // integer overflow + Assert.Throws(() => Base64.GetMaxEncodedToUtf8Length(1610612734)); + Assert.Throws(() => Base64.GetMaxEncodedToUtf8Length(int.MaxValue)); + + // negative input + Assert.Throws(() => Base64.GetMaxEncodedToUtf8Length(-1)); + Assert.Throws(() => Base64.GetMaxEncodedToUtf8Length(int.MinValue)); + } + + [Fact] + public void EncodeInPlace() + { + const int numberOfBytes = 15; + Span testBytes = new byte[numberOfBytes / 3 * 4]; // slack since encoding inflates the data + Base64TestHelper.InitalizeBytes(testBytes); + + for (int numberOfBytesToTest = 0; numberOfBytesToTest <= numberOfBytes; numberOfBytesToTest++) + { + var expectedText = Convert.ToBase64String(testBytes.Slice(0, numberOfBytesToTest).ToArray()); + + Assert.Equal(OperationStatus.Done, Base64.EncodeToUtf8InPlace(testBytes, numberOfBytesToTest, out int bytesWritten)); + Assert.Equal(Base64.GetMaxEncodedToUtf8Length(numberOfBytesToTest), bytesWritten); + + var encodedText = Encoding.ASCII.GetString(testBytes.Slice(0, bytesWritten).ToArray()); + Assert.Equal(expectedText, encodedText); + } + } + + [Fact] + public void EncodeInPlaceOutputTooSmall() + { + byte[] testBytes = { 1, 2, 3 }; + + for (int numberOfBytesToTest = 1; numberOfBytesToTest <= testBytes.Length; numberOfBytesToTest++) + { + Assert.Equal(OperationStatus.DestinationTooSmall, Base64.EncodeToUtf8InPlace(testBytes, numberOfBytesToTest, out int bytesWritten)); + Assert.Equal(0, bytesWritten); + } + } + + [Fact] + public void EncodeInPlaceDataLengthTooLarge() + { + byte[] testBytes = { 1, 2, 3 }; + Assert.Equal(OperationStatus.DestinationTooSmall, Base64.EncodeToUtf8InPlace(testBytes, testBytes.Length + 1, out int bytesWritten)); + Assert.Equal(0, bytesWritten); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Base64/Base64TestHelper.cs b/external/corefx/src/System.Memory/tests/Base64/Base64TestHelper.cs new file mode 100644 index 0000000000..667a691614 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Base64/Base64TestHelper.cs @@ -0,0 +1,133 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Text; +using Xunit; + +namespace System.Buffers.Text.Tests +{ + public static class Base64TestHelper + { + public static string s_characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + // Pre-computing this table using a custom string(s_characters) and GenerateEncodingMapAndVerify (found in tests) + public static readonly byte[] s_encodingMap = { + 65, 66, 67, 68, 69, 70, 71, 72, //A..H + 73, 74, 75, 76, 77, 78, 79, 80, //I..P + 81, 82, 83, 84, 85, 86, 87, 88, //Q..X + 89, 90, 97, 98, 99, 100, 101, 102, //Y..Z, a..f + 103, 104, 105, 106, 107, 108, 109, 110, //g..n + 111, 112, 113, 114, 115, 116, 117, 118, //o..v + 119, 120, 121, 122, 48, 49, 50, 51, //w..z, 0..3 + 52, 53, 54, 55, 56, 57, 43, 47 //4..9, +, / + }; + + // Pre-computing this table using a custom string(s_characters) and GenerateDecodingMapAndVerify (found in tests) + public static readonly sbyte[] s_decodingMap = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, //62 is placed at index 43 (for +), 63 at index 47 (for /) + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, //52-61 are placed at index 48-57 (for 0-9), 64 at index 61 (for =) + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, //0-25 are placed at index 65-90 (for A-Z) + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, //26-51 are placed at index 97-122 (for a-z) + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Bytes over 122 ('z') are invalid and cannot be decoded + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Hence, padding the map with 255, which indicates invalid input + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + + public static readonly byte s_encodingPad = (byte)'='; // '=', for padding + + public static readonly sbyte s_invalidByte = -1; // Designating -1 for invalid bytes in the decoding map + + public static byte[] InvalidBytes + { + get + { + int[] indices = s_decodingMap.FindAllIndexOf(s_invalidByte); + // Workaroudn for indices.Cast().ToArray() since it throws + // InvalidCastException: Unable to cast object of type 'System.Int32' to type 'System.Byte' + byte[] bytes = new byte[indices.Length]; + for (int i = 0; i < indices.Length; i++) + { + bytes[i] = (byte)indices[i]; + } + return bytes; + } + } + + public static void InitalizeBytes(Span bytes, int seed = 100) + { + var rnd = new Random(seed); + for (int i = 0; i < bytes.Length; i++) + { + bytes[i] = (byte)rnd.Next(0, byte.MaxValue + 1); + } + } + + public static void InitalizeDecodableBytes(Span bytes, int seed = 100) + { + var rnd = new Random(seed); + for (int i = 0; i < bytes.Length; i++) + { + int index = (byte)rnd.Next(0, s_encodingMap.Length - 1); // Do not pick '=' + bytes[i] = s_encodingMap[index]; + } + } + + [Fact] + public static void GenerateEncodingMapAndVerify() + { + byte[] data = new byte[64]; // Base64 + for (int i = 0; i < s_characters.Length; i++) + { + data[i] = (byte)s_characters[i]; + } + Assert.True(s_encodingMap.AsSpan().SequenceEqual(data)); + } + + [Fact] + public static void GenerateDecodingMapAndVerify() + { + sbyte[] data = new sbyte[256]; // 0 to byte.MaxValue (255) + for (int i = 0; i < data.Length; i++) + { + data[i] = s_invalidByte; + } + for (int i = 0; i < s_characters.Length; i++) + { + data[s_characters[i]] = (sbyte)i; + } + Assert.True(s_decodingMap.AsSpan().SequenceEqual(data)); + } + + public static int[] FindAllIndexOf(this IEnumerable values, T valueToFind) + { + return values.Select((element, index) => Equals(element, valueToFind) ? index : -1).Where(index => index != -1).ToArray(); + } + + public static bool VerifyEncodingCorrectness(int expectedConsumed, int expectedWritten, Span source, Span encodedBytes) + { + string expectedText = Convert.ToBase64String(source.Slice(0, expectedConsumed).ToArray()); + string encodedText = Encoding.ASCII.GetString(encodedBytes.Slice(0, expectedWritten).ToArray()); + return expectedText.Equals(encodedText); + } + + public static bool VerifyDecodingCorrectness(int expectedConsumed, int expectedWritten, Span source, Span decodedBytes) + { + string sourceString = Encoding.ASCII.GetString(source.Slice(0, expectedConsumed).ToArray()); + byte[] expectedBytes = Convert.FromBase64String(sourceString); + return expectedBytes.AsSpan().SequenceEqual(decodedBytes.Slice(0, expectedWritten)); + } + } +} + diff --git a/external/corefx/src/System.Memory/tests/Binary/BinaryReaderUnitTests.cs b/external/corefx/src/System.Memory/tests/Binary/BinaryReaderUnitTests.cs index 5b776244de..7a3bc0d4c0 100644 --- a/external/corefx/src/System.Memory/tests/Binary/BinaryReaderUnitTests.cs +++ b/external/corefx/src/System.Memory/tests/Binary/BinaryReaderUnitTests.cs @@ -7,7 +7,6 @@ using System.Runtime.InteropServices; using Xunit; using static System.Buffers.Binary.BinaryPrimitives; -using static System.TestHelpers; namespace System.Buffers.Binary.Tests { @@ -20,7 +19,8 @@ namespace System.Buffers.Binary.Tests ulong value = 0x8877665544332211; // [11 22 33 44 55 66 77 88] Span span; - unsafe { + unsafe + { span = new Span(&value, 8); } @@ -88,7 +88,8 @@ namespace System.Buffers.Binary.Tests ulong value = 0x8877665544332211; // [11 22 33 44 55 66 77 88] ReadOnlySpan span; - unsafe { + unsafe + { span = new ReadOnlySpan(&value, 8); } @@ -226,18 +227,18 @@ namespace System.Buffers.Binary.Tests Span spanBE = new byte[Unsafe.SizeOf()]; - WriteInt16BigEndian(spanBE, testStruct.S0); - WriteInt32BigEndian(spanBE.Slice(2), testStruct.I0); - WriteInt64BigEndian(spanBE.Slice(6), testStruct.L0); - WriteUInt16BigEndian(spanBE.Slice(14), testStruct.US0); - WriteUInt32BigEndian(spanBE.Slice(16), testStruct.UI0); - WriteUInt64BigEndian(spanBE.Slice(20), testStruct.UL0); - WriteInt16BigEndian(spanBE.Slice(28), testStruct.S1); - WriteInt32BigEndian(spanBE.Slice(30), testStruct.I1); - WriteInt64BigEndian(spanBE.Slice(34), testStruct.L1); - WriteUInt16BigEndian(spanBE.Slice(42), testStruct.US1); - WriteUInt32BigEndian(spanBE.Slice(44), testStruct.UI1); - WriteUInt64BigEndian(spanBE.Slice(48), testStruct.UL1); + WriteInt16BigEndian(spanBE, s_testStruct.S0); + WriteInt32BigEndian(spanBE.Slice(2), s_testStruct.I0); + WriteInt64BigEndian(spanBE.Slice(6), s_testStruct.L0); + WriteUInt16BigEndian(spanBE.Slice(14), s_testStruct.US0); + WriteUInt32BigEndian(spanBE.Slice(16), s_testStruct.UI0); + WriteUInt64BigEndian(spanBE.Slice(20), s_testStruct.UL0); + WriteInt16BigEndian(spanBE.Slice(28), s_testStruct.S1); + WriteInt32BigEndian(spanBE.Slice(30), s_testStruct.I1); + WriteInt64BigEndian(spanBE.Slice(34), s_testStruct.L1); + WriteUInt16BigEndian(spanBE.Slice(42), s_testStruct.US1); + WriteUInt32BigEndian(spanBE.Slice(44), s_testStruct.UI1); + WriteUInt64BigEndian(spanBE.Slice(48), s_testStruct.UL1); ReadOnlySpan readOnlySpanBE = new ReadOnlySpan(spanBE.ToArray()); @@ -273,8 +274,8 @@ namespace System.Buffers.Binary.Tests UL1 = ReadUInt64BigEndian(readOnlySpanBE.Slice(48)) }; - Assert.Equal(testStruct, readStruct); - Assert.Equal(testStruct, readStructFromReadOnlySpan); + Assert.Equal(s_testStruct, readStruct); + Assert.Equal(s_testStruct, readStructFromReadOnlySpan); } [Fact] @@ -284,18 +285,18 @@ namespace System.Buffers.Binary.Tests Span spanLE = new byte[Unsafe.SizeOf()]; - WriteInt16LittleEndian(spanLE, testStruct.S0); - WriteInt32LittleEndian(spanLE.Slice(2), testStruct.I0); - WriteInt64LittleEndian(spanLE.Slice(6), testStruct.L0); - WriteUInt16LittleEndian(spanLE.Slice(14), testStruct.US0); - WriteUInt32LittleEndian(spanLE.Slice(16), testStruct.UI0); - WriteUInt64LittleEndian(spanLE.Slice(20), testStruct.UL0); - WriteInt16LittleEndian(spanLE.Slice(28), testStruct.S1); - WriteInt32LittleEndian(spanLE.Slice(30), testStruct.I1); - WriteInt64LittleEndian(spanLE.Slice(34), testStruct.L1); - WriteUInt16LittleEndian(spanLE.Slice(42), testStruct.US1); - WriteUInt32LittleEndian(spanLE.Slice(44), testStruct.UI1); - WriteUInt64LittleEndian(spanLE.Slice(48), testStruct.UL1); + WriteInt16LittleEndian(spanLE, s_testStruct.S0); + WriteInt32LittleEndian(spanLE.Slice(2), s_testStruct.I0); + WriteInt64LittleEndian(spanLE.Slice(6), s_testStruct.L0); + WriteUInt16LittleEndian(spanLE.Slice(14), s_testStruct.US0); + WriteUInt32LittleEndian(spanLE.Slice(16), s_testStruct.UI0); + WriteUInt64LittleEndian(spanLE.Slice(20), s_testStruct.UL0); + WriteInt16LittleEndian(spanLE.Slice(28), s_testStruct.S1); + WriteInt32LittleEndian(spanLE.Slice(30), s_testStruct.I1); + WriteInt64LittleEndian(spanLE.Slice(34), s_testStruct.L1); + WriteUInt16LittleEndian(spanLE.Slice(42), s_testStruct.US1); + WriteUInt32LittleEndian(spanLE.Slice(44), s_testStruct.UI1); + WriteUInt64LittleEndian(spanLE.Slice(48), s_testStruct.UL1); ReadOnlySpan readOnlySpanLE = new ReadOnlySpan(spanLE.ToArray()); @@ -331,8 +332,8 @@ namespace System.Buffers.Binary.Tests UL1 = ReadUInt64LittleEndian(readOnlySpanLE.Slice(48)) }; - Assert.Equal(testStruct, readStruct); - Assert.Equal(testStruct, readStructFromReadOnlySpan); + Assert.Equal(s_testStruct, readStruct); + Assert.Equal(s_testStruct, readStructFromReadOnlySpan); } [Fact] @@ -374,7 +375,7 @@ namespace System.Buffers.Binary.Tests ReadOnlySpan readOnlySpanBE = new ReadOnlySpan(spanBE.ToArray()); - var readStructAndReverse = ReadMachineEndian(spanBE); + TestHelpers.TestStructExplicit readStructAndReverse = ReadMachineEndian(spanBE); if (BitConverter.IsLittleEndian) { readStructAndReverse.S0 = ReverseEndianness(readStructAndReverse.S0); @@ -407,7 +408,7 @@ namespace System.Buffers.Binary.Tests UL1 = ReadUInt64BigEndian(spanBE.Slice(48)) }; - var readStructAndReverseFromReadOnlySpan = ReadMachineEndian(readOnlySpanBE); + TestHelpers.TestStructExplicit readStructAndReverseFromReadOnlySpan = ReadMachineEndian(readOnlySpanBE); if (BitConverter.IsLittleEndian) { readStructAndReverseFromReadOnlySpan.S0 = ReverseEndianness(readStructAndReverseFromReadOnlySpan.S0); @@ -447,7 +448,7 @@ namespace System.Buffers.Binary.Tests Assert.Equal(testExplicitStruct, readStructFieldByFieldFromReadOnlySpan); } - private static TestStruct testStruct = new TestStruct + private static TestStruct s_testStruct = new TestStruct { S0 = short.MaxValue, I0 = int.MaxValue, diff --git a/external/corefx/src/System.Memory/tests/Binary/BinaryWriterUnitTests.cs b/external/corefx/src/System.Memory/tests/Binary/BinaryWriterUnitTests.cs index 7b2c8a06c2..efd6d2be2f 100644 --- a/external/corefx/src/System.Memory/tests/Binary/BinaryWriterUnitTests.cs +++ b/external/corefx/src/System.Memory/tests/Binary/BinaryWriterUnitTests.cs @@ -2,12 +2,9 @@ // 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.Runtime.CompilerServices; -using System.Runtime.InteropServices; using Xunit; using static System.Buffers.Binary.BinaryPrimitives; -using static System.TestHelpers; namespace System.Buffers.Binary.Tests { @@ -270,9 +267,9 @@ namespace System.Buffers.Binary.Tests uint uintValue = 1; long longValue = 1; ulong ulongValue = 1; - + Span span = new byte[1]; - + WriteMachineEndian(span, ref byteValue); byte read = ReadMachineEndian(span); Assert.Equal(byteValue, read); @@ -305,7 +302,7 @@ namespace System.Buffers.Binary.Tests TestHelpers.AssertThrows(span, (_span) => WriteMachineEndian(_span, ref ulongValue)); Assert.False(TryWriteMachineEndian(span, ref ulongValue)); - var structValue = new TestHelpers.TestValueTypeWithReference{ I = 1, S = "1" }; + var structValue = new TestHelpers.TestValueTypeWithReference { I = 1, S = "1" }; TestHelpers.AssertThrows(span, (_span) => WriteMachineEndian(_span, ref structValue)); TestHelpers.AssertThrows(span, (_span) => TryWriteMachineEndian(_span, ref structValue)); } diff --git a/external/corefx/src/System.Memory/tests/Memory/AsReadOnlyMemory.cs b/external/corefx/src/System.Memory/tests/Memory/AsReadOnlyMemory.cs new file mode 100644 index 0000000000..60eba35496 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Memory/AsReadOnlyMemory.cs @@ -0,0 +1,28 @@ +// 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 Xunit; + +namespace System.MemoryTests +{ + public static class AsReadOnlyMemory + { + [Fact] + public static void EmptyMemoryAsReadOnlyMemory() + { + Memory memory = default; + Assert.True(memory.AsReadOnlyMemory().IsEmpty); + } + + [Fact] + public static void MemoryAsReadOnlyMemory() + { + int[] a = { 19, -17 }; + Memory memory = new Memory(a); + ReadOnlyMemory readOnlyMemory = memory.AsReadOnlyMemory(); + + readOnlyMemory.Validate(a); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Memory/CopyTo.cs b/external/corefx/src/System.Memory/tests/Memory/CopyTo.cs new file mode 100644 index 0000000000..1cb36761e7 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Memory/CopyTo.cs @@ -0,0 +1,194 @@ +// 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 Xunit; + +namespace System.MemoryTests +{ + public static partial class MemoryTests + { + [Fact] + public static void TryCopyTo() + { + int[] src = { 1, 2, 3 }; + int[] dst = { 99, 100, 101 }; + + Memory srcMemory = src; + bool success = srcMemory.TryCopyTo(dst); + Assert.True(success); + Assert.Equal(src, dst); + } + + [Fact] + public static void TryCopyToSingle() + { + int[] src = { 1 }; + int[] dst = { 99 }; + + Memory srcMemory = src; + bool success = srcMemory.TryCopyTo(dst); + Assert.True(success); + Assert.Equal(src, dst); + } + + [Fact] + public static void TryCopyToArraySegmentImplicit() + { + int[] src = { 1, 2, 3 }; + int[] dst = { 5, 99, 100, 101, 10 }; + var segment = new ArraySegment(dst, 1, 3); + + Memory srcMemory = src; + bool success = srcMemory.TryCopyTo(segment); + Assert.True(success); + Assert.Equal(src, segment); + } + + [Fact] + public static void TryCopyToEmpty() + { + int[] src = { }; + int[] dst = { 99, 100, 101 }; + + Memory srcMemory = src; + bool success = srcMemory.TryCopyTo(dst); + Assert.True(success); + int[] expected = { 99, 100, 101 }; + Assert.Equal(expected, dst); + } + + [Fact] + public static void TryCopyToLonger() + { + int[] src = { 1, 2, 3 }; + int[] dst = { 99, 100, 101, 102 }; + + Memory srcMemory = src; + bool success = srcMemory.TryCopyTo(dst); + Assert.True(success); + int[] expected = { 1, 2, 3, 102 }; + Assert.Equal(expected, dst); + } + + [Fact] + public static void TryCopyToShorter() + { + int[] src = { 1, 2, 3 }; + int[] dst = { 99, 100 }; + + Memory srcMemory = src; + bool success = srcMemory.TryCopyTo(dst); + Assert.False(success); + int[] expected = { 99, 100 }; + Assert.Equal(expected, dst); // TryCopyTo() checks for sufficient space before doing any copying. + } + + [Fact] + public static void CopyToShorter() + { + int[] src = { 1, 2, 3 }; + int[] dst = { 99, 100 }; + + Memory srcMemory = src; + Assert.Throws(() => srcMemory.CopyTo(dst)); + int[] expected = { 99, 100 }; + Assert.Equal(expected, dst); // CopyTo() checks for sufficient space before doing any copying. + } + + [Fact] + public static void Overlapping1() + { + int[] a = { 90, 91, 92, 93, 94, 95, 96, 97 }; + + var src = new Memory(a, 1, 6); + var dst = new Memory(a, 2, 6); + src.CopyTo(dst); + + int[] expected = { 90, 91, 91, 92, 93, 94, 95, 96 }; + Assert.Equal(expected, a); + } + + [Fact] + public static void Overlapping2() + { + int[] a = { 90, 91, 92, 93, 94, 95, 96, 97 }; + + var src = new Memory(a, 2, 6); + var dst = new Memory(a, 1, 6); + src.CopyTo(dst); + + int[] expected = { 90, 92, 93, 94, 95, 96, 97, 97 }; + Assert.Equal(expected, a); + } + + [Fact] + public static void CopyToArray() + { + int[] src = { 1, 2, 3 }; + Memory dst = new int[3] { 99, 100, 101 }; + + src.CopyTo(dst); + Assert.Equal(src, dst.ToArray()); + } + + [Fact] + public static void CopyToSingleArray() + { + int[] src = { 1 }; + Memory dst = new int[1] { 99 }; + + src.CopyTo(dst); + Assert.Equal(src, dst.ToArray()); + } + + [Fact] + public static void CopyToEmptyArray() + { + int[] src = { }; + Memory dst = new int[3] { 99, 100, 101 }; + + src.CopyTo(dst); + int[] expected = { 99, 100, 101 }; + Assert.Equal(expected, dst.ToArray()); + + Memory dstEmpty = new int[0] { }; + + src.CopyTo(dstEmpty); + int[] expectedEmpty = { }; + Assert.Equal(expectedEmpty, dstEmpty.ToArray()); + } + + [Fact] + public static void CopyToLongerArray() + { + int[] src = { 1, 2, 3 }; + Memory dst = new int[4] { 99, 100, 101, 102 }; + + src.CopyTo(dst); + int[] expected = { 1, 2, 3, 102 }; + Assert.Equal(expected, dst.ToArray()); + } + + [Fact] + public static void CopyToShorterArray() + { + int[] src = { 1, 2, 3 }; + Memory dst = new int[2] { 99, 100 }; + + Assert.Throws(() => src.CopyTo(dst)); + int[] expected = { 99, 100 }; + Assert.Equal(expected, dst.ToArray()); // CopyTo() checks for sufficient space before doing any copying. + } + + [Fact] + public static void CopyToCovariantArray() + { + string[] src = new string[] { "Hello" }; + Memory dst = new object[] { "world" }; + + src.CopyTo(dst); + Assert.Equal("Hello", dst.Span[0]); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Memory/CustomMemoryForTest.cs b/external/corefx/src/System.Memory/tests/Memory/CustomMemoryForTest.cs index ae23459eac..c8180303ee 100644 --- a/external/corefx/src/System.Memory/tests/Memory/CustomMemoryForTest.cs +++ b/external/corefx/src/System.Memory/tests/Memory/CustomMemoryForTest.cs @@ -2,8 +2,8 @@ // 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.Buffers; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; @@ -39,13 +39,14 @@ namespace System.MemoryTests } } - public override MemoryHandle Pin() + public override MemoryHandle Pin(int offset = 0) { unsafe { Retain(); + if (offset < 0 || offset > _array.Length) throw new ArgumentOutOfRangeException(nameof(offset)); var handle = GCHandle.Alloc(_array, GCHandleType.Pinned); - return new MemoryHandle(this, (void*)handle.AddrOfPinnedObject(), handle); + return new MemoryHandle(this, Unsafe.Add((void*)handle.AddrOfPinnedObject(), offset), handle); } } @@ -68,7 +69,7 @@ namespace System.MemoryTests } _disposed = true; - + } public override void Retain() diff --git a/external/corefx/src/System.Memory/tests/Memory/Empty.cs b/external/corefx/src/System.Memory/tests/Memory/Empty.cs index 73738f76a4..acab15b32d 100644 --- a/external/corefx/src/System.Memory/tests/Memory/Empty.cs +++ b/external/corefx/src/System.Memory/tests/Memory/Empty.cs @@ -23,5 +23,15 @@ namespace System.MemoryTests Assert.True(empty.IsEmpty); Assert.Equal(0, empty.Length); } + + [Fact] + public static void EmptyEqualsDefault() + { + Memory empty = Memory.Empty; + Memory defaultMemory = default; + Assert.True(defaultMemory.Equals(empty)); + Assert.True(defaultMemory.IsEmpty); + Assert.Equal(0, defaultMemory.Length); + } } } diff --git a/external/corefx/src/System.Memory/tests/Memory/Equality.cs b/external/corefx/src/System.Memory/tests/Memory/Equality.cs index f81068b5bf..f82d0edebe 100644 --- a/external/corefx/src/System.Memory/tests/Memory/Equality.cs +++ b/external/corefx/src/System.Memory/tests/Memory/Equality.cs @@ -107,6 +107,20 @@ namespace System.MemoryTests Assert.True(readOnlyMemory.Equals(memoryAsObject)); } + [Fact] + public static void DefaultMemoryCanBeBoxed() + { + Memory memory = default; + object memoryAsObject = memory; + Assert.True(memory.Equals(memoryAsObject)); + + ReadOnlyMemory readOnlyMemory = default; + object readOnlyMemoryAsObject = readOnlyMemory; + Assert.True(readOnlyMemory.Equals(readOnlyMemoryAsObject)); + + Assert.True(memory.Equals(readOnlyMemoryAsObject)); + Assert.True(readOnlyMemory.Equals(memoryAsObject)); + } [Theory] [MemberData(nameof(ValidArraySegments))] diff --git a/external/corefx/src/System.Memory/tests/Memory/GetHashCode.cs b/external/corefx/src/System.Memory/tests/Memory/GetHashCode.cs index e4a4c2bac0..7bc0ef3c8a 100644 --- a/external/corefx/src/System.Memory/tests/Memory/GetHashCode.cs +++ b/external/corefx/src/System.Memory/tests/Memory/GetHashCode.cs @@ -85,5 +85,14 @@ namespace System.MemoryTests Assert.Equal(implicitMemoryArray.GetHashCode(), memory.GetHashCode()); Assert.Equal(implicitReadOnlyMemoryArray.GetHashCode(), memory.GetHashCode()); } + + [Fact] + public static void DefaultMemoryHashCode() + { + Memory memory = default; + Assert.Equal(0, memory.GetHashCode()); + Memory memory2 = default; + Assert.Equal(memory2.GetHashCode(), memory.GetHashCode()); + } } } diff --git a/external/corefx/src/System.Memory/tests/Memory/ImplicitConversion.cs b/external/corefx/src/System.Memory/tests/Memory/ImplicitConversion.cs index 75a545167d..21b5d7ffb9 100644 --- a/external/corefx/src/System.Memory/tests/Memory/ImplicitConversion.cs +++ b/external/corefx/src/System.Memory/tests/Memory/ImplicitConversion.cs @@ -48,6 +48,22 @@ namespace System.MemoryTests Memory memoryEmptyInt = emptyArray2; CastReadOnly(memoryEmptyInt); + + object[] emptyObjectArray = new object[0]; + CastReference(emptyObjectArray); + + Memory memoryObject = emptyObjectArray; + CastReadOnlyReference(memoryObject); + } + + [Fact] + public static void CtorImplicitDefaultMemory() + { + Memory memoryEmpty = default; + CastReadOnly(memoryEmpty); + + Memory memoryObject = default; + CastReadOnlyReference(memoryObject); } [Fact] @@ -97,6 +113,14 @@ namespace System.MemoryTests CastReadOnly(memoryEmptyInt); } + [Fact] + public static void NullImplicitCast() + { + int[] dst = null; + Memory srcMemory = dst; + Assert.True(Memory.Empty.Span == srcMemory.Span); + } + private static void Cast(Memory memory, params T[] expected) where T : struct, IEquatable { memory.Validate(expected); diff --git a/external/corefx/src/System.Memory/tests/Memory/OwnedMemory.cs b/external/corefx/src/System.Memory/tests/Memory/OwnedMemory.cs index cef1264f91..e1249ed0cf 100644 --- a/external/corefx/src/System.Memory/tests/Memory/OwnedMemory.cs +++ b/external/corefx/src/System.Memory/tests/Memory/OwnedMemory.cs @@ -2,7 +2,6 @@ // 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.Buffers; using Xunit; @@ -20,6 +19,27 @@ namespace System.MemoryTests OwnedMemory owner = new CustomMemoryForTest(a); Memory memory = owner.Memory; memory.Validate(91, 92, -93, 94); + memory.Slice(0, 4).Validate(91, 92, -93, 94); + memory.Slice(1, 0).Validate(); + memory.Slice(1, 1).Validate(92); + memory.Slice(1, 2).Validate(92, -93); + memory.Slice(2, 2).Validate(-93, 94); + memory.Slice(4, 0).Validate(); + } + + [Fact] + public static void ReadOnlyMemoryFromMemoryFromOwnedMemoryInt() + { + int[] a = { 91, 92, -93, 94 }; + OwnedMemory owner = new CustomMemoryForTest(a); + ReadOnlyMemory readOnlyMemory = owner.Memory; + readOnlyMemory.Validate(91, 92, -93, 94); + readOnlyMemory.Slice(0, 4).Validate(91, 92, -93, 94); + readOnlyMemory.Slice(1, 0).Validate(); + readOnlyMemory.Slice(1, 1).Validate(92); + readOnlyMemory.Slice(1, 2).Validate(92, -93); + readOnlyMemory.Slice(2, 2).Validate(-93, 94); + readOnlyMemory.Slice(4, 0).Validate(); } [Fact] @@ -29,6 +49,12 @@ namespace System.MemoryTests OwnedMemory owner = new CustomMemoryForTest(a); Memory memory = owner.Memory; memory.Validate(91, -92, 93, 94, -95); + memory.Slice(0, 5).Validate(91, -92, 93, 94, -95); + memory.Slice(1, 0).Validate(); + memory.Slice(1, 1).Validate(-92); + memory.Slice(1, 2).Validate(-92, 93); + memory.Slice(2, 3).Validate(93, 94, -95); + memory.Slice(5, 0).Validate(); } [Fact] @@ -60,6 +86,36 @@ namespace System.MemoryTests owner.Dispose(); Assert.True(owner.IsDisposed); } + + [Fact] + public static void OwnedMemoryPinEmptyArray() + { + int[] a = {}; + OwnedMemory owner = new CustomMemoryForTest(a); + MemoryHandle handle = owner.Pin(); + Assert.True(handle.HasPointer); + } + + [Fact] + public static void OwnedMemoryPinArray() + { + int[] array = { 1, 2, 3, 4, 5 }; + OwnedMemory owner = new CustomMemoryForTest(array); + MemoryHandle handle = owner.Pin(); + Assert.True(handle.HasPointer); + unsafe + { + int* pointer = (int*)handle.Pointer; + + GC.Collect(); + + for (int i = 0; i < owner.Memory.Length; i++) + { + Assert.Equal(array[i], pointer[i]); + } + } + handle.Dispose(); + } [Fact] public static void MemoryFromOwnedMemoryAfterDispose() @@ -91,6 +147,6 @@ namespace System.MemoryTests Assert.True(owner.IsDisposed); } } - + } diff --git a/external/corefx/src/System.Memory/tests/Memory/Retain.cs b/external/corefx/src/System.Memory/tests/Memory/Retain.cs index e48301b87d..4286685a0d 100644 --- a/external/corefx/src/System.Memory/tests/Memory/Retain.cs +++ b/external/corefx/src/System.Memory/tests/Memory/Retain.cs @@ -16,10 +16,10 @@ namespace System.MemoryTests int[] array = { 1, 2, 3, 4, 5 }; Memory memory = array; MemoryHandle handle = memory.Retain(); + Assert.False(handle.HasPointer); unsafe { - int* pointer = (int*)handle.PinnedPointer; - Assert.True(pointer == null); + Assert.True(handle.Pointer == null); } handle.Dispose(); } @@ -30,10 +30,10 @@ namespace System.MemoryTests int[] array = { 1, 2, 3, 4, 5 }; Memory memory = array; MemoryHandle handle = memory.Retain(pin: true); + Assert.True(handle.HasPointer); unsafe { - int* pointer = (int*)handle.PinnedPointer; - Assert.True(pointer != null); + int* pointer = (int*)handle.Pointer; GC.Collect(); @@ -53,10 +53,10 @@ namespace System.MemoryTests memory = memory.Slice(1); MemoryHandle handle = memory.Retain(pin: true); Span span = memory.Span; + Assert.True(handle.HasPointer); unsafe { - int* pointer = (int*)handle.PinnedPointer; - Assert.True(pointer != null); + int* pointer = (int*)handle.Pointer; GC.Collect(); @@ -80,10 +80,10 @@ namespace System.MemoryTests OwnedMemory owner = new CustomMemoryForTest(array); Memory memory = owner.Memory; MemoryHandle handle = memory.Retain(); + Assert.False(handle.HasPointer); unsafe { - int* pointer = (int*)handle.PinnedPointer; - Assert.True(pointer == null); + Assert.True(handle.Pointer == null); } handle.Dispose(); } @@ -95,10 +95,10 @@ namespace System.MemoryTests OwnedMemory owner = new CustomMemoryForTest(array); Memory memory = owner.Memory; MemoryHandle handle = memory.Retain(pin: true); + Assert.True(handle.HasPointer); unsafe { - int* pointer = (int*)handle.PinnedPointer; - Assert.True(pointer != null); + int* pointer = (int*)handle.Pointer; GC.Collect(); @@ -110,7 +110,23 @@ namespace System.MemoryTests handle.Dispose(); } - [ActiveIssue(24384, TargetFrameworkMonikers.UapAot)] + [Fact] + public static void MemoryFromEmptyArrayRetainWithPinning() + { + Memory memory = new int[0]; + MemoryHandle handle = memory.Retain(pin: true); + Assert.True(handle.HasPointer); + unsafe + { + int* pointer = (int*)handle.Pointer; + + GC.Collect(); + + Assert.True(pointer != null); + } + handle.Dispose(); + } + [Fact] public static void OwnedMemoryRetainWithPinningAndSlice() { @@ -119,10 +135,10 @@ namespace System.MemoryTests Memory memory = owner.Memory.Slice(1); MemoryHandle handle = memory.Retain(pin: true); Span span = memory.Span; + Assert.True(handle.HasPointer); unsafe { - int* pointer = (int*)handle.PinnedPointer; - Assert.True(pointer != null); + int* pointer = (int*)handle.Pointer; GC.Collect(); @@ -138,5 +154,20 @@ namespace System.MemoryTests } handle.Dispose(); } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public static void DefaultMemoryRetain(bool pin) + { + Memory memory = default; + MemoryHandle handle = memory.Retain(pin: pin); + Assert.False(handle.HasPointer); + unsafe + { + Assert.True(handle.Pointer == null); + } + handle.Dispose(); + } } } diff --git a/external/corefx/src/System.Memory/tests/Memory/Slice.cs b/external/corefx/src/System.Memory/tests/Memory/Slice.cs index 70dcdb26e7..c047472175 100644 --- a/external/corefx/src/System.Memory/tests/Memory/Slice.cs +++ b/external/corefx/src/System.Memory/tests/Memory/Slice.cs @@ -3,8 +3,9 @@ // See the LICENSE file in the project root for more information. using Xunit; -using System.Runtime.CompilerServices; using System.Buffers; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.MemoryTests { @@ -16,13 +17,13 @@ namespace System.MemoryTests int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 }; Memory memory = new Memory(a).Slice(6); Assert.Equal(4, memory.Length); - Assert.True(Unsafe.AreSame(ref a[6], ref memory.Span.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref a[6], ref MemoryMarshal.GetReference(memory.Span))); OwnedMemory owner = new CustomMemoryForTest(a); Memory memoryFromOwner = owner.Memory.Slice(6); Assert.Equal(4, memoryFromOwner.Length); - Assert.True(Unsafe.AreSame(ref a[6], ref memoryFromOwner.Span.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref a[6], ref MemoryMarshal.GetReference(memoryFromOwner.Span))); } [Fact] @@ -31,13 +32,13 @@ namespace System.MemoryTests int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 }; Memory memory = new Memory(a).Slice(a.Length); Assert.Equal(0, memory.Length); - Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref memory.Span.DangerousGetPinnableReference(), 1))); + Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref MemoryMarshal.GetReference(memory.Span), 1))); OwnedMemory owner = new CustomMemoryForTest(a); Memory memoryFromOwner = owner.Memory.Slice(a.Length); Assert.Equal(0, memoryFromOwner.Length); - Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref memoryFromOwner.Span.DangerousGetPinnableReference(), 1))); + Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref MemoryMarshal.GetReference(memoryFromOwner.Span), 1))); } [Fact] @@ -46,13 +47,13 @@ namespace System.MemoryTests int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 }; Memory memory = new Memory(a).Slice(3, 5); Assert.Equal(5, memory.Length); - Assert.True(Unsafe.AreSame(ref a[3], ref memory.Span.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref a[3], ref MemoryMarshal.GetReference(memory.Span))); OwnedMemory owner = new CustomMemoryForTest(a); Memory memoryFromOwner = owner.Memory.Slice(3, 5); Assert.Equal(5, memoryFromOwner.Length); - Assert.True(Unsafe.AreSame(ref a[3], ref memoryFromOwner.Span.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref a[3], ref MemoryMarshal.GetReference(memoryFromOwner.Span))); } [Fact] @@ -61,13 +62,13 @@ namespace System.MemoryTests int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 }; Memory memory = new Memory(a).Slice(4, 6); Assert.Equal(6, memory.Length); - Assert.True(Unsafe.AreSame(ref a[4], ref memory.Span.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref a[4], ref MemoryMarshal.GetReference(memory.Span))); OwnedMemory owner = new CustomMemoryForTest(a); Memory memoryFromOwner = owner.Memory.Slice(4, 6); Assert.Equal(6, memoryFromOwner.Length); - Assert.True(Unsafe.AreSame(ref a[4], ref memoryFromOwner.Span.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref a[4], ref MemoryMarshal.GetReference(memoryFromOwner.Span))); } [Fact] @@ -76,13 +77,13 @@ namespace System.MemoryTests int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 }; Memory memory = new Memory(a).Slice(a.Length, 0); Assert.Equal(0, memory.Length); - Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref memory.Span.DangerousGetPinnableReference(), 1))); + Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref MemoryMarshal.GetReference(memory.Span), 1))); OwnedMemory owner = new CustomMemoryForTest(a); Memory memoryFromOwner = owner.Memory.Slice(a.Length, 0); Assert.Equal(0, memoryFromOwner.Length); - Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref memoryFromOwner.Span.DangerousGetPinnableReference(), 1))); + Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref MemoryMarshal.GetReference(memoryFromOwner.Span), 1))); } [Fact] @@ -108,5 +109,21 @@ namespace System.MemoryTests Assert.Throws(() => memory.Slice(a.Length + 1, 0)); Assert.Throws(() => memory.Slice(a.Length, 1)); } + + [Fact] + public static void SliceWithStartDefaultMemory() + { + Memory memory = default; + memory = memory.Slice(0); + Assert.True(memory.Equals(default)); + } + + [Fact] + public static void SliceWithStartAndLengthDefaultMemory() + { + Memory memory = default; + memory = memory.Slice(0, 0); + Assert.True(memory.Equals(default)); + } } } diff --git a/external/corefx/src/System.Memory/tests/Memory/Span.cs b/external/corefx/src/System.Memory/tests/Memory/Span.cs index 1ff80d774e..607a56a82e 100644 --- a/external/corefx/src/System.Memory/tests/Memory/Span.cs +++ b/external/corefx/src/System.Memory/tests/Memory/Span.cs @@ -66,10 +66,10 @@ namespace System.MemoryTests Memory memory; memory = new Memory(empty); - memory.Span.Validate(); + memory.Span.ValidateNonNullEmpty(); memory = new Memory(empty, 0, empty.Length); - memory.Span.Validate(); + memory.Span.ValidateNonNullEmpty(); OwnedMemory owner = new CustomMemoryForTest(empty); owner.Memory.Span.Validate(); @@ -94,5 +94,17 @@ namespace System.MemoryTests owner.Memory.Span.Validate(42, -1); } + [Fact] + public static void SpanFromDefaultMemory() + { + Memory memory = default; + Span span = memory.Span; + Assert.True(span.SequenceEqual(default)); + + Memory memoryObject = default; + Span spanObject = memoryObject.Span; + Assert.True(spanObject.SequenceEqual(default)); + } + } } diff --git a/external/corefx/src/System.Memory/tests/Memory/Strings.cs b/external/corefx/src/System.Memory/tests/Memory/Strings.cs new file mode 100644 index 0000000000..fd0f2fd26a --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Memory/Strings.cs @@ -0,0 +1,99 @@ +// 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 Xunit; +using System.Buffers; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace System.MemoryTests +{ + public static partial class MemoryTests + { + public static IEnumerable StringInputs() + { + yield return new object[] { "" }; + yield return new object[] { "a" }; + yield return new object[] { "a\0bcdefghijklmnopqrstuvwxyz" }; + } + + [Theory] + [MemberData(nameof(StringInputs))] + public static void Memory_ToArray_Roundtrips(string input) + { + ReadOnlyMemory readonlyMemory = input.AsReadOnlyMemory(); + Memory m = MemoryMarshal.AsMemory(readonlyMemory); + Assert.Equal(input, new string(m.ToArray())); + } + + [Theory] + [MemberData(nameof(StringInputs))] + public static void Memory_Span_Roundtrips(string input) + { + ReadOnlyMemory readonlyMemory = input.AsReadOnlyMemory(); + Memory m = MemoryMarshal.AsMemory(readonlyMemory); + ReadOnlySpan s = m.Span; + Assert.Equal(input, new string(s.ToArray())); + } + + [Theory] + [InlineData("", 0, 0)] + [InlineData("0123456789", 0, 0)] + [InlineData("0123456789", 10, 0)] + [InlineData("0123456789", 0, 10)] + [InlineData("0123456789", 1, 9)] + [InlineData("0123456789", 2, 8)] + [InlineData("0123456789", 9, 1)] + [InlineData("0123456789", 1, 8)] + [InlineData("0123456789", 5, 3)] + public static void Memory_Slice_MatchesSubstring(string input, int offset, int count) + { + ReadOnlyMemory readonlyMemory = input.AsReadOnlyMemory(); + Memory m = MemoryMarshal.AsMemory(readonlyMemory); + Assert.Equal(input.Substring(offset, count), new string(m.Slice(offset, count).ToArray())); + Assert.Equal(input.Substring(offset, count), new string(m.Slice(offset, count).Span.ToArray())); + Assert.Equal(input.Substring(offset), new string(m.Slice(offset).ToArray())); + } + + [Fact] + public static unsafe void Memory_Retain_ExpectedPointerValue() + { + string input = "0123456789"; + ReadOnlyMemory readonlyMemory = input.AsReadOnlyMemory(); + Memory m = MemoryMarshal.AsMemory(readonlyMemory); + + using (MemoryHandle h = m.Retain(pin: false)) + { + Assert.Equal(IntPtr.Zero, (IntPtr)h.Pointer); + } + + using (MemoryHandle h = m.Retain(pin: true)) + { + GC.Collect(); + fixed (char* ptr = input) + { + Assert.Equal((IntPtr)ptr, (IntPtr)h.Pointer); + } + } + } + + [Fact] + public static void Memory_EqualsAndGetHashCode_ExpectedResults() + { + ReadOnlyMemory readonlyMemory1 = new string('a', 4).AsReadOnlyMemory(); + ReadOnlyMemory readonlyMemory2 = new string('a', 4).AsReadOnlyMemory(); + + Memory m1 = MemoryMarshal.AsMemory(readonlyMemory1); + Memory m2 = MemoryMarshal.AsMemory(readonlyMemory2); + + Assert.True(m1.Span.SequenceEqual(m2.Span)); + Assert.True(m1.Equals(m1)); + Assert.True(m1.Equals((object)m1)); + Assert.False(m1.Equals(m2)); + Assert.False(m1.Equals((object)m2)); + + Assert.Equal(m1.GetHashCode(), m1.GetHashCode()); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Memory/ToArray.cs b/external/corefx/src/System.Memory/tests/Memory/ToArray.cs index f3fa252ad7..23d70b28c5 100644 --- a/external/corefx/src/System.Memory/tests/Memory/ToArray.cs +++ b/external/corefx/src/System.Memory/tests/Memory/ToArray.cs @@ -24,7 +24,7 @@ namespace System.MemoryTests int[] a = { 91, 92, 93, 94, 95 }; var memory = new Memory(a); int[] copy = memory.Slice(2).ToArray(); - + Assert.Equal(new int[] { 93, 94, 95 }, copy); } @@ -44,5 +44,13 @@ namespace System.MemoryTests int[] copy = memory.ToArray(); Assert.Equal(0, copy.Length); } + + [Fact] + public static void ToArrayDefault() + { + Memory memory = default; + int[] copy = memory.ToArray(); + Assert.Equal(0, copy.Length); + } } } diff --git a/external/corefx/src/System.Memory/tests/Memory/TryGetArray.cs b/external/corefx/src/System.Memory/tests/Memory/TryGetArray.cs index b2cbc098b7..fdd92e8906 100644 --- a/external/corefx/src/System.Memory/tests/Memory/TryGetArray.cs +++ b/external/corefx/src/System.Memory/tests/Memory/TryGetArray.cs @@ -23,6 +23,14 @@ namespace System.MemoryTests } } + [Fact] + public static void TryGetArrayFromDefaultMemory() + { + Memory memory = default; + Assert.False(memory.TryGetArray(out ArraySegment segment)); + Assert.True(segment.Equals(default)); + } + [Fact] public static void OwnedMemoryTryGetArray() { diff --git a/external/corefx/src/System.Memory/tests/MemoryMarshal/AsMemory.cs b/external/corefx/src/System.Memory/tests/MemoryMarshal/AsMemory.cs new file mode 100644 index 0000000000..0d9e9fc08b --- /dev/null +++ b/external/corefx/src/System.Memory/tests/MemoryMarshal/AsMemory.cs @@ -0,0 +1,99 @@ +// 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.Buffers; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Xunit; + +namespace System.MemoryTests +{ + public static partial class MemoryMarshalTests + { + public static IEnumerable ReadOnlyMemoryInt32Instances() + { + yield return new object[] { ReadOnlyMemory.Empty }; + yield return new object[] { new ReadOnlyMemory(new int[0]) }; + yield return new object[] { new ReadOnlyMemory(new int[10]) }; + yield return new object[] { new ReadOnlyMemory(new int[10], 1, 3) }; + yield return new object[] { (ReadOnlyMemory)new CustomMemoryForTest(new int[10]).Memory }; + } + + public static IEnumerable ReadOnlyMemoryObjectInstances() + { + yield return new object[] { ReadOnlyMemory.Empty }; + yield return new object[] { new ReadOnlyMemory(new object[0]) }; + yield return new object[] { new ReadOnlyMemory(new object[10]) }; + yield return new object[] { new ReadOnlyMemory(new object[10], 1, 3) }; + yield return new object[] { (ReadOnlyMemory)new CustomMemoryForTest(new object[10]).Memory }; + } + + public static IEnumerable ReadOnlyMemoryCharInstances() + { + yield return new object[] { ReadOnlyMemory.Empty }; + yield return new object[] { new ReadOnlyMemory(new char[10], 1, 3) }; + yield return new object[] { (ReadOnlyMemory)new CustomMemoryForTest(new char[10]).Memory }; + yield return new object[] { "12345".AsReadOnlyMemory() }; + } + + [Theory] + [MemberData(nameof(ReadOnlyMemoryInt32Instances))] + public static void AsMemory_Roundtrips(ReadOnlyMemory readOnlyMemory) => AsMemory_Roundtrips_Core(readOnlyMemory); + + [Theory] + [MemberData(nameof(ReadOnlyMemoryObjectInstances))] + public static void AsMemory_Roundtrips(ReadOnlyMemory readOnlyMemory) => AsMemory_Roundtrips_Core(readOnlyMemory); + + [Theory] + [MemberData(nameof(ReadOnlyMemoryCharInstances))] + public static void AsMemory_Roundtrips(ReadOnlyMemory readOnlyMemory) + { + AsMemory_Roundtrips_Core(readOnlyMemory); + + Memory memory = MemoryMarshal.AsMemory(readOnlyMemory); + ReadOnlyMemory readOnlyClone = memory; + + // TryGetString + bool gotString1 = readOnlyMemory.TryGetString(out string text1, out int start1, out int length1); + Assert.Equal(gotString1, readOnlyClone.TryGetString(out string text2, out int start2, out int length2)); + Assert.Same(text1, text2); + Assert.Equal(start1, start2); + Assert.Equal(length1, length2); + if (gotString1) + { + Assert.False(memory.TryGetArray(out ArraySegment array)); + } + } + + private static unsafe void AsMemory_Roundtrips_Core(ReadOnlyMemory readOnlyMemory) + { + Memory memory = MemoryMarshal.AsMemory(readOnlyMemory); + ReadOnlyMemory readOnlyClone = memory; + + // Equals + Assert.True(readOnlyMemory.Equals(readOnlyClone)); + Assert.True(readOnlyMemory.Equals((object)readOnlyClone)); + Assert.True(readOnlyMemory.Equals((object)memory)); + + // Span + Assert.True(readOnlyMemory.Span == readOnlyClone.Span); + Assert.True(readOnlyMemory.Span == memory.Span); + + // TryGetArray + Assert.True(MemoryMarshal.TryGetArray(readOnlyMemory, out ArraySegment array1) == memory.TryGetArray(out ArraySegment array2)); + Assert.Same(array1.Array, array2.Array); + Assert.Equal(array1.Offset, array2.Offset); + Assert.Equal(array1.Count, array2.Count); + + // Retain + using (MemoryHandle readOnlyMemoryHandle = readOnlyMemory.Retain()) + using (MemoryHandle readOnlyCloneHandle = readOnlyMemory.Retain()) + using (MemoryHandle memoryHandle = readOnlyMemory.Retain()) + { + Assert.Equal((IntPtr)readOnlyMemoryHandle.Pointer, (IntPtr)readOnlyCloneHandle.Pointer); + Assert.Equal((IntPtr)readOnlyMemoryHandle.Pointer, (IntPtr)memoryHandle.Pointer); + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/MemoryMarshal/GetReference.cs b/external/corefx/src/System.Memory/tests/MemoryMarshal/GetReference.cs new file mode 100644 index 0000000000..3993b353e0 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/MemoryMarshal/GetReference.cs @@ -0,0 +1,125 @@ +// 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 Xunit; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using static System.TestHelpers; + +namespace System.SpanTests +{ + public static partial class MemoryMarshalTests + { + [Fact] + public static void SpanGetReferenceArray() + { + int[] a = { 91, 92, 93, 94, 95 }; + Span span = new Span(a, 1, 3); + ref int pinnableReference = ref MemoryMarshal.GetReference(span); + Assert.True(Unsafe.AreSame(ref a[1], ref pinnableReference)); + } + + [Fact] + public static void SpanGetReferenceArrayPastEnd() + { + // The only real difference between GetReference() and "ref span[0]" is that + // GetReference() of a zero-length won't throw an IndexOutOfRange. + + int[] a = { 91, 92, 93, 94, 95 }; + Span span = new Span(a, a.Length, 0); + ref int pinnableReference = ref MemoryMarshal.GetReference(span); + ref int expected = ref Unsafe.Add(ref a[a.Length - 1], 1); + Assert.True(Unsafe.AreSame(ref expected, ref pinnableReference)); + } + + [Fact] + public static void SpanGetReferencePointer() + { + unsafe + { + int i = 42; + Span span = new Span(&i, 1); + ref int pinnableReference = ref MemoryMarshal.GetReference(span); + Assert.True(Unsafe.AreSame(ref i, ref pinnableReference)); + } + } + + [Fact] + public static void SpanGetReferencePointerDangerousCreate1() + { + TestClass testClass = new TestClass(); + Span span = Span.DangerousCreate(testClass, ref testClass.C1, 3); + + ref char pinnableReference = ref MemoryMarshal.GetReference(span); + Assert.True(Unsafe.AreSame(ref testClass.C1, ref pinnableReference)); + } + + [Fact] + public static void SpanGetReferenceEmpty() + { + unsafe + { + Span span = Span.Empty; + ref int pinnableReference = ref MemoryMarshal.GetReference(span); + Assert.True(Unsafe.AreSame(ref Unsafe.AsRef(null), ref pinnableReference)); + } + } + + [Fact] + public static void ReadOnlySpanGetReferenceArray() + { + int[] a = { 91, 92, 93, 94, 95 }; + ReadOnlySpan span = new ReadOnlySpan(a, 1, 3); + ref int pinnableReference = ref Unsafe.AsRef(in MemoryMarshal.GetReference(span)); + Assert.True(Unsafe.AreSame(ref a[1], ref pinnableReference)); + } + + [Fact] + public static void ReadOnlySpanGetReferenceArrayPastEnd() + { + // The only real difference between GetReference() and "ref span[0]" is that + // GetReference() of a zero-length won't throw an IndexOutOfRange. + + int[] a = { 91, 92, 93, 94, 95 }; + ReadOnlySpan span = new ReadOnlySpan(a, a.Length, 0); + ref int pinnableReference = ref Unsafe.AsRef(in MemoryMarshal.GetReference(span)); + ref int expected = ref Unsafe.Add(ref a[a.Length - 1], 1); + Assert.True(Unsafe.AreSame(ref expected, ref pinnableReference)); + } + + [Fact] + public static void ReadOnlySpanGetReferencePointer() + { + unsafe + { + int i = 42; + ReadOnlySpan span = new ReadOnlySpan(&i, 1); + ref int pinnableReference = ref Unsafe.AsRef(in MemoryMarshal.GetReference(span)); + Assert.True(Unsafe.AreSame(ref i, ref pinnableReference)); + } + } + + [Fact] + public static void ReadOnlySpanGetReferencePointerDangerousCreate1() + { + TestClass testClass = new TestClass(); + ReadOnlySpan span = ReadOnlySpan.DangerousCreate(testClass, ref testClass.C1, 3); + + ref char pinnableReference = ref Unsafe.AsRef(in MemoryMarshal.GetReference(span)); + Assert.True(Unsafe.AreSame(ref testClass.C1, ref pinnableReference)); + } + + [Fact] + public static void ReadOnlySpanGetReferenceEmpty() + { + unsafe + { + ReadOnlySpan span = ReadOnlySpan.Empty; + ref int pinnableReference = ref Unsafe.AsRef(in MemoryMarshal.GetReference(span)); + Assert.True(Unsafe.AreSame(ref Unsafe.AsRef(null), ref pinnableReference)); + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ReadOnlyMemory/DangerousTryGetArray.cs b/external/corefx/src/System.Memory/tests/MemoryMarshal/TryGetArray.cs similarity index 64% rename from external/corefx/src/System.Memory/tests/ReadOnlyMemory/DangerousTryGetArray.cs rename to external/corefx/src/System.Memory/tests/MemoryMarshal/TryGetArray.cs index f6a9c7baf5..ebdc2ce438 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlyMemory/DangerousTryGetArray.cs +++ b/external/corefx/src/System.Memory/tests/MemoryMarshal/TryGetArray.cs @@ -3,18 +3,19 @@ // See the LICENSE file in the project root for more information. using System.Buffers; +using System.Runtime.InteropServices; using Xunit; namespace System.MemoryTests { - public static partial class ReadOnlyMemoryTests + public static partial class MemoryMarshalTests { [Fact] - public static void MemoryDangerousTryGetArray() + public static void ReadOnlyMemoryTryGetArray() { int[] array = new int[10]; ReadOnlyMemory memory = array; - Assert.True(memory.DangerousTryGetArray(out ArraySegment segment)); + Assert.True(MemoryMarshal.TryGetArray(memory, out ArraySegment segment)); Assert.Equal(array.Length, segment.Count); for (int i = segment.Offset; i < segment.Count + segment.Offset; i++) @@ -23,13 +24,21 @@ namespace System.MemoryTests } } + [Fact] + public static void TryGetArrayFromDefaultMemory() + { + ReadOnlyMemory memory = default; + Assert.False(MemoryMarshal.TryGetArray(memory, out ArraySegment segment)); + Assert.True(segment.Equals(default)); + } + [Fact] public static void OwnedMemoryTryGetArray() { int[] array = new int[10]; OwnedMemory owner = new CustomMemoryForTest(array); ReadOnlyMemory memory = owner.Memory; - Assert.True(memory.DangerousTryGetArray(out ArraySegment segment)); + Assert.True(MemoryMarshal.TryGetArray(memory, out ArraySegment segment)); Assert.Equal(array.Length, segment.Count); for (int i = segment.Offset; i < segment.Count + segment.Offset; i++) diff --git a/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Formatter/FormatterTestData.cs b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Formatter/FormatterTestData.cs new file mode 100644 index 0000000000..1784553b49 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Formatter/FormatterTestData.cs @@ -0,0 +1,59 @@ +// 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. + +namespace System.Buffers.Text.Tests +{ + public sealed class FormatterTestData + { + public FormatterTestData(T value, SupportedFormat format, byte precision, string expectedOutput) + { + Value = value; + Format = format; + Precision = precision; + ExpectedOutput = expectedOutput; + PassedInBufferLength = expectedOutput.Length; + } + + public T Value { get; } + public char FormatSymbol => Format.Symbol; + public byte Precision { get; } + public SupportedFormat Format { get; } + public string ExpectedOutput { get; } + public int PassedInBufferLength { get; set; } // by default, the length of expected output: test cases can override with shorter or longer lengths + + public ParserTestData ToParserTestData() + { + return new ParserTestData(ExpectedOutput, Value, FormatSymbol, expectedSuccess: true); + } + + public sealed override string ToString() + { + // + // Take good care of this method: it affects Xunit output and makes a lot of difference in how annoying test investigations are. + // + + string formatString = (FormatSymbol == default) ? + "default" : + FormatSymbol + ((Precision == StandardFormat.NoPrecision) ? + string.Empty : + Precision.ToString()); + + string bufferLengthString; + if (PassedInBufferLength == ExpectedOutput.Length) + { + bufferLengthString = string.Empty; + } + else if (PassedInBufferLength < ExpectedOutput.Length) + { + bufferLengthString = $", Buffer Length = {PassedInBufferLength} bytes (too short)"; + } + else + { + bufferLengthString = $", Buffer Length = {PassedInBufferLength} bytes (longer than needed)"; + } + + return $"[Format{typeof(T).Name} {Value.DisplayString()},{formatString} to '{ExpectedOutput}'{bufferLengthString})]"; + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Formatter/FormatterTests.Negative.cs b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Formatter/FormatterTests.Negative.cs new file mode 100644 index 0000000000..3f3eb9aad7 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Formatter/FormatterTests.Negative.cs @@ -0,0 +1,32 @@ +// 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 Xunit; + +namespace System.Buffers.Text.Tests +{ + public static partial class FormatterTests + { + [Theory] + [MemberData(nameof(TestData.TypesThatCanBeFormatted), MemberType = typeof(TestData))] + public static void TestBadFormat(Type integerType) + { + object value = Activator.CreateInstance(integerType); + Assert.Throws(() => TryFormatUtf8(value, Array.Empty(), out int bytesWritten, new StandardFormat('$', 1))); + } + + [Theory] + [MemberData(nameof(TestData.IntegerTypesTheoryData), MemberType = typeof(TestData))] + [InlineData(typeof(decimal))] + [InlineData(typeof(double))] + [InlineData(typeof(float))] + public static void TestGFormatWithPrecisionNotSupported(Type type) + { + object value = Activator.CreateInstance(type); + Assert.Throws(() => TryFormatUtf8(value, Array.Empty(), out int bytesWritten, new StandardFormat('G', 1))); + Assert.Throws(() => TryFormatUtf8(value, Array.Empty(), out int bytesWritten, new StandardFormat('g', 1))); + } + } +} + diff --git a/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Formatter/FormatterTests.cs b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Formatter/FormatterTests.cs new file mode 100644 index 0000000000..a94e183230 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Formatter/FormatterTests.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. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Buffers.Text.Tests +{ + public static partial class FormatterTests + { + [Theory] + [MemberData(nameof(TestData.BooleanFormatterTheoryData), MemberType = typeof(TestData))] + public static void TestFormatterBoolean(FormatterTestData testData) + { + ValidateFormatter(testData); + } + + [Theory] + [MemberData(nameof(TestData.SByteFormatterTheoryData), MemberType = typeof(TestData))] + public static void TestFormatterSByte(FormatterTestData testData) + { + ValidateFormatter(testData); + } + + [Theory] + [MemberData(nameof(TestData.ByteFormatterTheoryData), MemberType = typeof(TestData))] + public static void TestFormatterByte(FormatterTestData testData) + { + ValidateFormatter(testData); + } + + [Theory] + [MemberData(nameof(TestData.Int16FormatterTheoryData), MemberType = typeof(TestData))] + public static void TestFormatterInt16(FormatterTestData testData) + { + ValidateFormatter(testData); + } + + [Theory] + [MemberData(nameof(TestData.UInt16FormatterTheoryData), MemberType = typeof(TestData))] + public static void TestFormatterUInt16(FormatterTestData testData) + { + ValidateFormatter(testData); + } + + [Theory] + [MemberData(nameof(TestData.Int32FormatterTheoryData), MemberType = typeof(TestData))] + public static void TestFormatterInt32(FormatterTestData testData) + { + ValidateFormatter(testData); + } + + [Theory] + [MemberData(nameof(TestData.UInt32FormatterTheoryData), MemberType = typeof(TestData))] + public static void TestFormatterUInt32(FormatterTestData testData) + { + ValidateFormatter(testData); + } + + [Theory] + [MemberData(nameof(TestData.Int64FormatterTheoryData), MemberType = typeof(TestData))] + public static void TestFormatterInt64(FormatterTestData testData) + { + ValidateFormatter(testData); + } + + [Theory] + [MemberData(nameof(TestData.UInt64FormatterTheoryData), MemberType = typeof(TestData))] + public static void TestFormatterUInt64(FormatterTestData testData) + { + ValidateFormatter(testData); + } + + [Theory] + [MemberData(nameof(TestData.DecimalFormatterTheoryData), MemberType = typeof(TestData))] + public static void TestFormatterDecimal(FormatterTestData testData) + { + ValidateFormatter(testData); + } + + [Theory] + [MemberData(nameof(TestData.DoubleFormatterTheoryData), MemberType = typeof(TestData))] + public static void TestFormatterDouble(FormatterTestData testData) + { + ValidateFormatter(testData); + } + + [Theory] + [MemberData(nameof(TestData.SingleFormatterTheoryData), MemberType = typeof(TestData))] + public static void TestFormatterSingle(FormatterTestData testData) + { + ValidateFormatter(testData); + } + + [Theory] + [MemberData(nameof(TestData.GuidFormatterTheoryData), MemberType = typeof(TestData))] + public static void TestFormatterGuid(FormatterTestData testData) + { + ValidateFormatter(testData); + } + + [Theory] + [MemberData(nameof(TestData.DateTimeFormatterTheoryData), MemberType = typeof(TestData))] + public static void TestFormatterDateTime(FormatterTestData testData) + { + ValidateFormatter(testData); + } + + [Theory] + [MemberData(nameof(TestData.DateTimeOffsetFormatterTheoryData), MemberType = typeof(TestData))] + public static void TestFormatterDateTimeOffset(FormatterTestData testData) + { + ValidateFormatter(testData); + } + + [Theory] + [MemberData(nameof(TestData.TimeSpanFormatterTheoryData), MemberType = typeof(TestData))] + public static void TestFormatterTimeSpan(FormatterTestData testData) + { + ValidateFormatter(testData); + } + } +} + diff --git a/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Formatter/TestData.Formatter.cs b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Formatter/TestData.Formatter.cs new file mode 100644 index 0000000000..632b12908f --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Formatter/TestData.Formatter.cs @@ -0,0 +1,142 @@ +// 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.Linq; +using System.Globalization; +using System.Collections.Generic; + +namespace System.Buffers.Text.Tests +{ + internal static partial class TestData + { + public static IEnumerable TypesThatCanBeFormatted + { + get + { + yield return new object[] { typeof(bool) }; + yield return new object[] { typeof(sbyte) }; + yield return new object[] { typeof(byte) }; + yield return new object[] { typeof(short) }; + yield return new object[] { typeof(ushort) }; + yield return new object[] { typeof(int) }; + yield return new object[] { typeof(uint) }; + yield return new object[] { typeof(long) }; + yield return new object[] { typeof(ulong) }; + yield return new object[] { typeof(decimal) }; + yield return new object[] { typeof(float) }; + yield return new object[] { typeof(double) }; + yield return new object[] { typeof(Guid) }; + yield return new object[] { typeof(DateTime) }; + yield return new object[] { typeof(DateTimeOffset) }; + yield return new object[] { typeof(TimeSpan) }; + } + } + + public static IEnumerable BooleanFormatterTheoryData => BooleanFormatterTestData.Select(td => new object[] { td }); + public static IEnumerable SByteFormatterTheoryData => SByteFormatterTestData.Select(td => new object[] { td }); + public static IEnumerable ByteFormatterTheoryData => ByteFormatterTestData.Select(td => new object[] { td }); + public static IEnumerable Int16FormatterTheoryData => Int16FormatterTestData.Select(td => new object[] { td }); + public static IEnumerable UInt16FormatterTheoryData => UInt16FormatterTestData.Select(td => new object[] { td }); + public static IEnumerable Int32FormatterTheoryData => Int32FormatterTestData.Select(td => new object[] { td }); + public static IEnumerable UInt32FormatterTheoryData => UInt32FormatterTestData.Select(td => new object[] { td }); + public static IEnumerable Int64FormatterTheoryData => Int64FormatterTestData.Select(td => new object[] { td }); + public static IEnumerable UInt64FormatterTheoryData => UInt64FormatterTestData.Select(td => new object[] { td }); + public static IEnumerable DecimalFormatterTheoryData => DecimalFormatterTestData.Select(td => new object[] { td }); + public static IEnumerable DoubleFormatterTheoryData => DoubleFormatterTestData.Select(td => new object[] { td }); + public static IEnumerable SingleFormatterTheoryData => SingleFormatterTestData.Select(td => new object[] { td }); + public static IEnumerable GuidFormatterTheoryData => GuidFormatterTestData.Select(td => new object[] { td }); + public static IEnumerable DateTimeFormatterTheoryData => DateTimeFormatterTestData.Select(td => new object[] { td }); + public static IEnumerable DateTimeOffsetFormatterTheoryData => DateTimeOffsetFormatterTestData.Select(td => new object[] { td }); + public static IEnumerable TimeSpanFormatterTheoryData => TimeSpanFormatterTestData.Select(td => new object[] { td }); + + public static IEnumerable> BooleanFormatterTestData => CreateFormatterTestData(BooleanTestData, BooleanFormats); + public static IEnumerable> SByteFormatterTestData => CreateFormatterTestData(SByteTestData, IntegerFormats); + public static IEnumerable> ByteFormatterTestData => CreateFormatterTestData(ByteTestData, IntegerFormats); + public static IEnumerable> Int16FormatterTestData => CreateFormatterTestData(Int16TestData, IntegerFormats); + public static IEnumerable> UInt16FormatterTestData => CreateFormatterTestData(UInt16TestData, IntegerFormats); + public static IEnumerable> Int32FormatterTestData => CreateFormatterTestData(Int32TestData, IntegerFormats); + public static IEnumerable> UInt32FormatterTestData => CreateFormatterTestData(UInt32TestData, IntegerFormats); + public static IEnumerable> Int64FormatterTestData => CreateFormatterTestData(Int64TestData, IntegerFormats); + public static IEnumerable> UInt64FormatterTestData => CreateFormatterTestData(UInt64TestData, IntegerFormats); + public static IEnumerable> DecimalFormatterTestData => CreateFormatterTestData(DecimalTestData, DecimalFormats); + public static IEnumerable> DoubleFormatterTestData => CreateFormatterTestData(DoubleTestData, FloatingPointFormats); + public static IEnumerable> SingleFormatterTestData => CreateFormatterTestData(SingleTestData, FloatingPointFormats); + public static IEnumerable> GuidFormatterTestData => CreateFormatterTestData(GuidTestData, GuidFormats); + public static IEnumerable> DateTimeFormatterTestData => CreateFormatterTestData(DateTimeTestData, DateTimeFormats); + public static IEnumerable> DateTimeOffsetFormatterTestData => CreateFormatterTestData(DateTimeOffsetTestData, DateTimeOffsetFormats); + public static IEnumerable> TimeSpanFormatterTestData => CreateFormatterTestData(TimeSpanTestData, TimeSpanFormats); + + private static IEnumerable> CreateFormatterTestData(IEnumerable values, IEnumerable formats) + { + foreach (T value in values) + { + foreach (SupportedFormat format in formats) + { + if (format.FormatSynonymFor != default) + continue; + + if (format.IsDefault) + { + string expectedOutput = ComputeExpectedOutput(value, format.Symbol, StandardFormat.NoPrecision); + yield return new FormatterTestData(value, new SupportedFormat(default, format.SupportsPrecision), default, expectedOutput); + } + + if (!format.NoRepresentation) + { + if (!format.SupportsPrecision) + { + string expectedOutput = ComputeExpectedOutput(value, format.Symbol, StandardFormat.NoPrecision); + yield return new FormatterTestData(value, format, StandardFormat.NoPrecision, expectedOutput); + } + else + { + foreach (byte precision in TestData.s_precisions) + { + string expectedOutput = ComputeExpectedOutput(value, format.Symbol, precision); + yield return new FormatterTestData(value, format, precision, expectedOutput); + } + } + } + } + } + } + + private static string ComputeExpectedOutput(T value, char formatSymbol, int precision) + { + if (value is DateTime dt && formatSymbol == 'l') + { + return dt.ToString("R", CultureInfo.InvariantCulture).ToLowerInvariant(); + } + else if (value is DateTimeOffset dto && formatSymbol == 'l') + { + return dto.ToString("R", CultureInfo.InvariantCulture).ToLowerInvariant(); + } + else if (value is DateTimeOffset dto2 && formatSymbol == default(char)) + { + return dto2.ToString(CultureInfo.InvariantCulture); + } + else if (value is IFormattable formattable) + { + string format = formatSymbol + (precision == StandardFormat.NoPrecision ? string.Empty : precision.ToString()); + return formattable.ToString(format, CultureInfo.InvariantCulture); + } + else if (value is bool b) + { + switch (formatSymbol) + { + case 'G': + return b ? "True" : "False"; + case 'l': + return b ? "true" : "false"; + default: + throw new Exception("Unsupported bool format: " + formatSymbol); + } + } + else + { + throw new Exception("Unsupported type: " + typeof(T)); + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Formatter/ValidateFormatter.cs b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Formatter/ValidateFormatter.cs new file mode 100644 index 0000000000..a04d7ee833 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Formatter/ValidateFormatter.cs @@ -0,0 +1,233 @@ +// 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 Xunit; + +namespace System.Buffers.Text.Tests +{ + public static partial class FormatterTests + { + private static void ValidateFormatter(FormatterTestData testData) + { + // It's useful to do the over-sized buffer test first. If the formatter api has a bug that generates output text that's longer than + // the expected string, this test will fail with an "Expected/Actual" diff while the exact-sized buffer test would say "Oops. The api returned 'false' and no output"." + FormatterTestData oversizedTestData = new FormatterTestData(testData.Value, testData.Format, testData.Precision, testData.ExpectedOutput) + { + PassedInBufferLength = testData.ExpectedOutput.Length + 200, + }; + ValidateFormatterHelper(oversizedTestData); + + // Now run the test with a buffer that's exactly the right size. + ValidateFormatterHelper(testData); + + // Now run the test with buffers that are too short. + for (int truncatedBufferLength = 0; truncatedBufferLength < testData.ExpectedOutput.Length; truncatedBufferLength++) + { + FormatterTestData newTestData = new FormatterTestData(testData.Value, testData.Format, testData.Precision, testData.ExpectedOutput) + { + PassedInBufferLength = truncatedBufferLength, + }; + + ValidateFormatterHelper(newTestData); + } + } + + private static void ValidateFormatterHelper(FormatterTestData testData) + { + int spanLength = testData.PassedInBufferLength; + string expectedOutput = testData.ExpectedOutput; + int expectedOutputLength = expectedOutput.Length; + + bool expectedSuccess = spanLength >= expectedOutputLength; + StandardFormat format = new StandardFormat(testData.FormatSymbol, testData.Precision); + T value = testData.Value; + + const int CanarySize = 100; + + byte[] backingArray = new byte[spanLength + CanarySize]; + Span span = new Span(backingArray, 0, spanLength); + CanaryFill(backingArray); + + bool success = TryFormatUtf8(value, span, out int bytesWritten, format); + if (success) + { + if (!expectedSuccess) + { + if (bytesWritten >= 0 && bytesWritten <= span.Length) + { + string unexpectedOutput = span.Slice(0, bytesWritten).ToUtf16String(); + throw new TestException($"This format attempt ({testData}) was expected to fail due to an undersized buffer. Instead, it generated \"{unexpectedOutput}\""); + } + else + { + throw new TestException($"This format attempt ({testData}) was expected to fail due to an undersized buffer. Instead, it succeeded but set 'bytesWritten' to an out of range value: {bytesWritten}"); + } + } + + if (bytesWritten < 0 || bytesWritten > span.Length) + { + throw new TestException($"This format attempt ({testData}) succeeded as expected but set 'bytesWritten' to an out of range value: {bytesWritten}"); + } + + string actual = span.Slice(0, bytesWritten).ToUtf16String(); + string expected = testData.ExpectedOutput; + if (actual != expected) + { + // We'll allocate (and not throw) the TestException (so that someone with a breakpoint inside TestException's constructor will break as desired) but + // use Assert.Equals() to trigger the actual failure so we get XUnit's more useful comparison output into the log. + new TestException($"This format attempt ({testData}) succeeded as expected but generated the wrong text:\n Expected: \"{expected}\"\n Actual: \"{actual}\"\n"); + Assert.Equal(expected, actual); + } + + // If the api scribbled into the Span past the reported 'bytesWritten' (but still within the bounds of the Span itself), this should raise eyebrows at least. + CanaryCheck(testData, new ReadOnlySpan(backingArray, span.Length, CanarySize), "the end of the Span itself"); + + // If the api scribbled beyond the range of the Span itself, it's panic time. + CanaryCheck(testData, new ReadOnlySpan(backingArray, expectedOutputLength, span.Length - expectedOutputLength), "'bytesWritten'"); + } + else + { + if (expectedSuccess) + { + throw new TestException($"This format attempt ({testData}) was expected to succeed. Instead, it failed."); + } + + if (bytesWritten != 0) + { + throw new TestException($"This format attempt ({testData}) failed as expected but did not set `bytesWritten` to 0"); + } + + // Note: It's not guaranteed that partial (and useless) results will be written to the buffer despite + // byteWritten being 0. (In particular, ulong values that are larger than long.MaxValue using the "N" format.) + // We can only check the canary portion for overwrites. + CanaryCheck(testData, new ReadOnlySpan(backingArray, span.Length, CanarySize), "the end of the Span itself"); + } + } + + private static void CanaryFill(Span canaries) + { + for (int i = 0; i < canaries.Length; i++) + { + canaries[i] = 0xcc; + } + } + + private static void CanaryCheck(FormatterTestData testData, ReadOnlySpan canaries, string pastWhat) + { + for (int i = 0; i < canaries.Length; i++) + { + if (canaries[i] != 0xcc) + throw new TestException($"BUFFER OVERRUN! This format attempt ({testData}) wrote past {pastWhat}!"); + } + } + + private static bool TryFormatUtf8(T value, Span buffer, out int bytesWritten, StandardFormat format = default) + { + if (typeof(T) == typeof(bool)) + return Utf8Formatter.TryFormat((bool)(object)value, buffer, out bytesWritten, format); + + if (typeof(T) == typeof(byte)) + return Utf8Formatter.TryFormat((byte)(object)value, buffer, out bytesWritten, format); + + if (typeof(T) == typeof(sbyte)) + return Utf8Formatter.TryFormat((sbyte)(object)value, buffer, out bytesWritten, format); + + if (typeof(T) == typeof(short)) + return Utf8Formatter.TryFormat((short)(object)value, buffer, out bytesWritten, format); + + if (typeof(T) == typeof(ushort)) + return Utf8Formatter.TryFormat((ushort)(object)value, buffer, out bytesWritten, format); + + if (typeof(T) == typeof(int)) + return Utf8Formatter.TryFormat((int)(object)value, buffer, out bytesWritten, format); + + if (typeof(T) == typeof(uint)) + return Utf8Formatter.TryFormat((uint)(object)value, buffer, out bytesWritten, format); + + if (typeof(T) == typeof(long)) + return Utf8Formatter.TryFormat((long)(object)value, buffer, out bytesWritten, format); + + if (typeof(T) == typeof(ulong)) + return Utf8Formatter.TryFormat((ulong)(object)value, buffer, out bytesWritten, format); + + if (typeof(T) == typeof(decimal)) + return Utf8Formatter.TryFormat((decimal)(object)value, buffer, out bytesWritten, format); + + if (typeof(T) == typeof(double)) + return Utf8Formatter.TryFormat((double)(object)value, buffer, out bytesWritten, format); + + if (typeof(T) == typeof(float)) + return Utf8Formatter.TryFormat((float)(object)value, buffer, out bytesWritten, format); + + if (typeof(T) == typeof(Guid)) + return Utf8Formatter.TryFormat((Guid)(object)value, buffer, out bytesWritten, format); + + if (typeof(T) == typeof(DateTime)) + return Utf8Formatter.TryFormat((DateTime)(object)value, buffer, out bytesWritten, format); + + if (typeof(T) == typeof(DateTimeOffset)) + return Utf8Formatter.TryFormat((DateTimeOffset)(object)value, buffer, out bytesWritten, format); + + if (typeof(T) == typeof(TimeSpan)) + return Utf8Formatter.TryFormat((TimeSpan)(object)value, buffer, out bytesWritten, format); + + throw new Exception("No formatter for type " + typeof(T)); + } + + private static bool TryFormatUtf8(object value, Span buffer, out int bytesWritten, StandardFormat format = default) + { + Type t = value.GetType(); + if (t == typeof(bool)) + return Utf8Formatter.TryFormat((bool)value, buffer, out bytesWritten, format); + + if (t == typeof(byte)) + return Utf8Formatter.TryFormat((byte)value, buffer, out bytesWritten, format); + + if (t == typeof(sbyte)) + return Utf8Formatter.TryFormat((sbyte)value, buffer, out bytesWritten, format); + + if (t == typeof(short)) + return Utf8Formatter.TryFormat((short)value, buffer, out bytesWritten, format); + + if (t == typeof(ushort)) + return Utf8Formatter.TryFormat((ushort)value, buffer, out bytesWritten, format); + + if (t == typeof(int)) + return Utf8Formatter.TryFormat((int)value, buffer, out bytesWritten, format); + + if (t == typeof(uint)) + return Utf8Formatter.TryFormat((uint)value, buffer, out bytesWritten, format); + + if (t == typeof(long)) + return Utf8Formatter.TryFormat((long)value, buffer, out bytesWritten, format); + + if (t == typeof(ulong)) + return Utf8Formatter.TryFormat((ulong)value, buffer, out bytesWritten, format); + + if (t == typeof(decimal)) + return Utf8Formatter.TryFormat((decimal)value, buffer, out bytesWritten, format); + + if (t == typeof(double)) + return Utf8Formatter.TryFormat((double)value, buffer, out bytesWritten, format); + + if (t == typeof(float)) + return Utf8Formatter.TryFormat((float)value, buffer, out bytesWritten, format); + + if (t == typeof(Guid)) + return Utf8Formatter.TryFormat((Guid)value, buffer, out bytesWritten, format); + + if (t == typeof(DateTime)) + return Utf8Formatter.TryFormat((DateTime)value, buffer, out bytesWritten, format); + + if (t == typeof(DateTimeOffset)) + return Utf8Formatter.TryFormat((DateTimeOffset)value, buffer, out bytesWritten, format); + + if (t == typeof(TimeSpan)) + return Utf8Formatter.TryFormat((TimeSpan)value, buffer, out bytesWritten, format); + + throw new Exception("No formatter for type " + t); + } + } +} + diff --git a/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/ParserTestData.cs b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/ParserTestData.cs new file mode 100644 index 0000000000..ba80a7bfce --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/ParserTestData.cs @@ -0,0 +1,39 @@ +// 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.Text; + +namespace System.Buffers.Text.Tests +{ + public sealed class ParserTestData + { + public ParserTestData(string text, T expectedValue, char formatSymbol, bool expectedSuccess) + { + Text = text; + ExpectedValue = expectedValue; + FormatSymbol = formatSymbol; + ExpectedSuccess = expectedSuccess; + ExpectedBytesConsumed = Encoding.UTF8.GetByteCount(text); + } + + public string Text { get; } + public T ExpectedValue { get; } + public char FormatSymbol { get; } + public bool ExpectedSuccess { get; } + public int ExpectedBytesConsumed { get; set; } // Has a public setter so that individual test cases can override. By default, it's set to the Utf8 character length of Text + + public sealed override string ToString() + { + // + // Take good care of this method: it affects Xunit output and makes a lot of difference in how annoying test investigations are. + // + + string formatString = (FormatSymbol == default) ? + "default" : + FormatSymbol.ToString(); + + return $"[Parse{typeof(T).Name} '{Text}',{formatString} to {(ExpectedSuccess ? ExpectedValue.DisplayString() : "(should-not-parse)")})]"; + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/ParserTests.2gbOverflow.cs b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/ParserTests.2gbOverflow.cs new file mode 100644 index 0000000000..6981d9ab8c --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/ParserTests.2gbOverflow.cs @@ -0,0 +1,114 @@ +// 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.Collections.Generic; +using System.Runtime.CompilerServices; + +using AllocationHelper = System.SpanTests.AllocationHelper; + +using Xunit; + +namespace System.Buffers.Text.Tests +{ + public static partial class ParserTests + { + private const int TwoGiB = int.MaxValue; + + // + // NOTE: TestParser2GiBOverflow test is constrained to run on Windows and MacOSX because it causes + // problems on Linux due to the way deferred memory allocation works. On Linux, the allocation can + // succeed even if there is not enough memory but then the test may get killed by the OOM killer at the + // time the memory is accessed which triggers the full memory allocation. + // + [Fact] + [OuterLoop] + [PlatformSpecific(TestPlatforms.Windows | TestPlatforms.OSX)] + public static void TestParser2GiBOverflow() + { + if (IntPtr.Size < 8) + return; + + IntPtr pMemory; + try + { + if (!AllocationHelper.TryAllocNative(size: new IntPtr(int.MaxValue), out pMemory)) + return; + } + catch (OutOfMemoryException) + { + return; + } + + try + { + TwoGiBOverflowHelper(TwoGiBOverflowInt32TestData, pMemory); + } + finally + { + AllocationHelper.ReleaseNative(ref pMemory); + } + } + + private static void TwoGiBOverflowHelper(IEnumerable> testDataCollection, IntPtr pMemory) + { + Assert.All>(testDataCollection, + (testData) => + { + unsafe + { + Span buffer = new Span((void*)pMemory, int.MaxValue); + ref byte memory = ref Unsafe.AsRef(pMemory.ToPointer()); + Span span = new Span(pMemory.ToPointer(), TwoGiB); + span.Fill((byte)'0'); + + ReadOnlySpan utf8Span = testData.Text.ToUtf8Span(); + byte sign = utf8Span[0]; + if (sign == '-' || sign == '+') + { + span[0] = sign; + utf8Span = utf8Span.Slice(1); + } + utf8Span.CopyTo(span.Slice(TwoGiB - utf8Span.Length)); + + bool success = TryParseUtf8(span, out T value, out int bytesConsumed, testData.FormatSymbol); + if (testData.ExpectedSuccess) + { + Assert.True(success); + Assert.Equal(testData.ExpectedValue, value); + Assert.Equal(testData.ExpectedBytesConsumed, bytesConsumed); + } + else + { + Assert.False(success); + Assert.Equal(default, value); + Assert.Equal(0, bytesConsumed); + } + } + + }); + } + + private static IEnumerable> TwoGiBOverflowInt32TestData + { + get + { + yield return new ParserTestData("0", 0, 'D', expectedSuccess: true) { ExpectedBytesConsumed = TwoGiB }; + yield return new ParserTestData("2", 2, 'D', expectedSuccess: true) { ExpectedBytesConsumed = TwoGiB }; + yield return new ParserTestData("21", 21, 'D', expectedSuccess: true) { ExpectedBytesConsumed = TwoGiB }; + yield return new ParserTestData("+2", 2, 'D', expectedSuccess: true) { ExpectedBytesConsumed = TwoGiB }; + yield return new ParserTestData("-2", -2, 'D', expectedSuccess: true) { ExpectedBytesConsumed = TwoGiB }; + yield return new ParserTestData("2147483647", 2147483647, 'D', expectedSuccess: true) { ExpectedBytesConsumed = TwoGiB }; + yield return new ParserTestData("-2147483648", -2147483648, 'D', expectedSuccess: true) { ExpectedBytesConsumed = TwoGiB }; + yield return new ParserTestData("2147483648", default, 'D', expectedSuccess: false); + yield return new ParserTestData("-2147483649", default, 'D', expectedSuccess: false); + yield return new ParserTestData("12345abcdefg1", 12345, 'D', expectedSuccess: true) { ExpectedBytesConsumed = TwoGiB - 8 }; + yield return new ParserTestData("1234145abcdefg1", 1234145, 'D', expectedSuccess: true) { ExpectedBytesConsumed = TwoGiB - 8 }; + yield return new ParserTestData("abcdefghijklmnop1", 0, 'D', expectedSuccess: true) { ExpectedBytesConsumed = TwoGiB - 17 }; + yield return new ParserTestData("1147483648", 1147483648, 'D', expectedSuccess: true) { ExpectedBytesConsumed = TwoGiB }; + yield return new ParserTestData("-1147483649", -1147483649, 'D', expectedSuccess: true) { ExpectedBytesConsumed = TwoGiB }; + } + } + } +} + diff --git a/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/ParserTests.Negative.cs b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/ParserTests.Negative.cs new file mode 100644 index 0000000000..e8b4c981e1 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/ParserTests.Negative.cs @@ -0,0 +1,19 @@ +// 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 Xunit; + +namespace System.Buffers.Text.Tests +{ + public static partial class ParserTests + { + [Theory] + [MemberData(nameof(TestData.TypesThatCanBeParsed), MemberType = typeof(TestData))] + public static void TestParserBadFormat(Type type) + { + Assert.Throws(() => TryParseUtf8(type, Array.Empty(), out object value, out int bytesConsumed, '$')); + } + } +} + diff --git a/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/ParserTests.cs b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/ParserTests.cs new file mode 100644 index 0000000000..1da5faf7a1 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/ParserTests.cs @@ -0,0 +1,160 @@ +// 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 Xunit; + +namespace System.Buffers.Text.Tests +{ + public static partial class ParserTests + { + [Theory] + [MemberData(nameof(TestData.BooleanParserTheoryData), MemberType = typeof(TestData))] + public static void TestParserBoolean(ParserTestData testData) + { + ValidateParser(testData); + } + + [Theory] + [MemberData(nameof(TestData.SByteParserTheoryData), MemberType = typeof(TestData))] + public static void TestParserSByte(ParserTestData testData) + { + ValidateParser(testData); + } + + [Theory] + [MemberData(nameof(TestData.ByteParserTheoryData), MemberType = typeof(TestData))] + public static void TestParserByte(ParserTestData testData) + { + ValidateParser(testData); + } + + [Theory] + [MemberData(nameof(TestData.Int16ParserTheoryData), MemberType = typeof(TestData))] + public static void TestParserInt16(ParserTestData testData) + { + ValidateParser(testData); + } + + [Theory] + [MemberData(nameof(TestData.UInt16ParserTheoryData), MemberType = typeof(TestData))] + public static void TestParserUInt16(ParserTestData testData) + { + ValidateParser(testData); + } + + [Theory] + [MemberData(nameof(TestData.Int32ParserTheoryData), MemberType = typeof(TestData))] + public static void TestParserInt32(ParserTestData testData) + { + ValidateParser(testData); + } + + [Theory] + [MemberData(nameof(TestData.UInt32ParserTheoryData), MemberType = typeof(TestData))] + public static void TestParserUInt32(ParserTestData testData) + { + ValidateParser(testData); + } + + [Theory] + [MemberData(nameof(TestData.Int64ParserTheoryData), MemberType = typeof(TestData))] + public static void TestParserInt64(ParserTestData testData) + { + ValidateParser(testData); + } + + [Theory] + [MemberData(nameof(TestData.UInt64ParserTheoryData), MemberType = typeof(TestData))] + public static void TestParserUInt64(ParserTestData testData) + { + ValidateParser(testData); + } + + [Theory] + [MemberData(nameof(TestData.DecimalParserTheoryData), MemberType = typeof(TestData))] + public static void TestParserDecimal(ParserTestData testData) + { + ValidateParser(testData); + } + + [Theory] + [MemberData(nameof(TestData.DoubleParserTheoryData), MemberType = typeof(TestData))] + public static void TestParserDouble(ParserTestData testData) + { + ValidateParser(testData); + } + + [Theory] + [MemberData(nameof(TestData.SingleParserTheoryData), MemberType = typeof(TestData))] + public static void TestParserSingle(ParserTestData testData) + { + ValidateParser(testData); + } + + [Theory] + [MemberData(nameof(TestData.GuidParserTheoryData), MemberType = typeof(TestData))] + public static void TestParserGuid(ParserTestData testData) + { + ValidateParser(testData); + } + + [Theory] + [MemberData(nameof(TestData.DateTimeParserTheoryData), MemberType = typeof(TestData))] + public static void TestParserDateTime(ParserTestData testData) + { + ValidateParser(testData); + } + + [Theory] + [MemberData(nameof(TestData.DateTimeOffsetParserTheoryData), MemberType = typeof(TestData))] + public static void TestParserDateTimeOffset(ParserTestData testData) + { + ValidateParser(testData); + } + + [Theory] + [MemberData(nameof(TestData.TimeSpanParserTheoryData), MemberType = typeof(TestData))] + public static void TestParserTimeSpan(ParserTestData testData) + { + ValidateParser(testData); + } + + [Theory] + [InlineData("9999-12-31T23:59:59.9999999", 'O')] + [InlineData("12/31/9999 23:59:59", 'G')] + public static void TestParserDateEndOfTime(string text, char formatSymbol) + { + // In timezones with a negative UTC offset, these inputs will blow up DateTimeOffset's constructor + // due to the overflow associated with adding the UTC offset. Make sure the call doesn't + // surface the ArgumentOutOfRangeException. + ReadOnlySpan utf8Text = text.ToUtf8Span(); + Utf8Parser.TryParse(utf8Text, out DateTimeOffset dto, out int bytesConsumed, formatSymbol); + } + + [Theory] + [MemberData(nameof(TestData.IntegerTypesTheoryData), MemberType = typeof(TestData))] + public static void FakeTestParserIntegerN(Type integerType) + { + // + // [ActiveIssue("https://github.com/dotnet/corefx/issues/24986 - UTF8Parser parsing integers with 'N' format not implemented.")] + // + // This "test" may look ludicrous but it serves two useful purposes: + // + // - It maintains Utf8Parser code coverage at 100% so that endless dev cycles aren't wasted drilling down into it to inspect for code coverage regressions. + // + // - As a guide to enabling the 'N' tests when the parsing is implemented. + // + try + { + TryParseUtf8(integerType, Array.Empty(), out _, out _, 'N'); + Assert.False(true, + $"Thank you for implementing the TryParse 'N' format. You can now disable this test and change {nameof(TestData.IsParsingImplemented)}() so it no longer suppresses 'N' testing."); + } + catch (NotImplementedException) + { + } + } + } +} + diff --git a/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/TestData.Parser.Boolean.cs b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/TestData.Parser.Boolean.cs new file mode 100644 index 0000000000..44d2333bec --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/TestData.Parser.Boolean.cs @@ -0,0 +1,43 @@ +// 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.Collections.Generic; + +namespace System.Buffers.Text.Tests +{ + internal static partial class TestData + { + public static IEnumerable> BooleanParserTestData + { + get + { + foreach (ParserTestData testData in BooleanFormatterTestData.ToParserTheoryDataCollection()) + { + yield return testData; + } + + string[] booleanNegativeInput = + { + string.Empty, + "0", + "1", + " TRUE", + " FALSE", + "TRU", + "FALS", + "TRU$", + "FALS$", + }; + + foreach (char formatSymbol in new char[] { 'G', 'l' }) + { + foreach (string unparsableText in booleanNegativeInput) + { + yield return new ParserTestData(unparsableText, default, formatSymbol, expectedSuccess: false); + } + } + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/TestData.Parser.Date.cs b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/TestData.Parser.Date.cs new file mode 100644 index 0000000000..336952d20f --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/TestData.Parser.Date.cs @@ -0,0 +1,215 @@ +// 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.Collections.Generic; + +namespace System.Buffers.Text.Tests +{ + internal static partial class TestData + { + public static IEnumerable> DateTimeParserTestData + { + get + { + foreach (ParserTestData testData in DateTimeFormatterTestData.ToParserTheoryDataCollection()) + { + bool roundTrippable = testData.FormatSymbol == 'O'; + if (roundTrippable) + yield return testData; + } + + foreach (ParserTestData testData in DateTimeOffsetParserTestData) + { + char formatSymbol = testData.FormatSymbol; + if (testData.ExpectedSuccess) + { + if (formatSymbol == 'R' || formatSymbol == 'l') + { + DateTime expectedDateTime = testData.ExpectedValue.UtcDateTime; + // TryParse returns Unspecified to match DateTime.ParseExact() though it seems Utc would be more apropos. + expectedDateTime = new DateTime(expectedDateTime.Ticks, DateTimeKind.Unspecified); + yield return new ParserTestData(testData.Text, expectedDateTime, testData.FormatSymbol, expectedSuccess: true) { ExpectedBytesConsumed = testData.ExpectedBytesConsumed }; + } + if (formatSymbol == 'G') + { + yield return new ParserTestData(testData.Text, testData.ExpectedValue.DateTime, testData.FormatSymbol, expectedSuccess: true) { ExpectedBytesConsumed = testData.ExpectedBytesConsumed }; + } + } + else + { + if (formatSymbol != default(char)) + { + yield return new ParserTestData(testData.Text, default, testData.FormatSymbol, expectedSuccess: false); + } + } + } + } + } + + public static IEnumerable> DateTimeOffsetParserTestData + { + get + { + // Wrong day of week. + yield return new ParserTestData("Thu, 13 Jan 2017 03:45:32 GMT", default, 'R', expectedSuccess: false); + + foreach (ParserTestData bad in GenerateCorruptedDateTimeText("05/08/2017 10:30:45 +00:00", default)) + { + yield return bad; + } + + foreach (ParserTestData bad in GenerateCorruptedDateTimeText("05/08/2017 10:30:45", 'G')) + { + yield return bad; + } + + foreach (ParserTestData bad in GenerateCorruptedDateTimeText("Tue, 03 Jan 2017 08:08:05 GMT", 'R')) + { + yield return bad; + } + + foreach (ParserTestData bad in GenerateCorruptedDateTimeText("tue, 03 jan 2017 08:08:05 gmt", 'l')) + { + yield return bad; + } + + foreach (ParserTestData bad in GenerateCorruptedDateTimeText("2017-01-12T10:30:45.7680000-08:00", 'O')) + { + yield return bad; + } + + foreach (ParserTestData testData in DateTimeOffsetFormatterTestData.ToParserTheoryDataCollection()) + { + bool roundTrippable = testData.FormatSymbol == 'O'; + if (roundTrippable) + yield return testData; + } + + foreach (PseudoDateTime pseudoDateTime in PseudoDateTimeTestData) + { + DateTimeOffset expectedDto; + if (pseudoDateTime.ExpectSuccess) + { + TimeSpan offset = new TimeSpan(hours: pseudoDateTime.OffsetHours, minutes: pseudoDateTime.OffsetMinutes, seconds: 0); + if (pseudoDateTime.OffsetNegative) + { + offset = -offset; + } + DateTimeOffset dto = new DateTimeOffset(year: pseudoDateTime.Year, month: pseudoDateTime.Month, day: pseudoDateTime.Day, hour: pseudoDateTime.Hour, minute: pseudoDateTime.Minute, second: pseudoDateTime.Second, offset: offset); + if (pseudoDateTime.Fraction != 0) + { + dto += new TimeSpan(ticks: pseudoDateTime.Fraction); + } + expectedDto = dto; + } + else + { + expectedDto = default; + } + + DateTimeOffset expectedDtoConvertedToLocal; + if (pseudoDateTime.ExpectSuccess) + { + try + { + expectedDtoConvertedToLocal = new DateTimeOffset(expectedDto.DateTime); + } + catch (ArgumentOutOfRangeException) + { + throw new Exception($"Failed on converting {expectedDto.DateTime} to local time. This is probably a piece of data that fails only in certain time zones. Time zone on this machine is {TimeZoneInfo.Local}"); + } + } + else + { + expectedDtoConvertedToLocal = default; + } + + string text; + if ((text = pseudoDateTime.DefaultString) != null) + { + yield return new ParserTestData(text, expectedDto, default, pseudoDateTime.ExpectSuccess); + } + + if ((text = pseudoDateTime.GFormatString) != null) + { + yield return new ParserTestData(text, expectedDtoConvertedToLocal, 'G', pseudoDateTime.ExpectSuccess); + } + + if ((text = pseudoDateTime.RFormatString) != null) + { + yield return new ParserTestData(text, expectedDto, 'R', pseudoDateTime.ExpectSuccess); + } + + if ((text = pseudoDateTime.LFormatString) != null) + { + yield return new ParserTestData(text, expectedDto, 'l', pseudoDateTime.ExpectSuccess); + } + + if ((text = pseudoDateTime.OFormatStringZ) != null) + { + yield return new ParserTestData(text, expectedDto, 'O', pseudoDateTime.ExpectSuccess); + } + + if ((text = pseudoDateTime.OFormatStringNoOffset) != null) + { + yield return new ParserTestData(text, expectedDtoConvertedToLocal, 'O', pseudoDateTime.ExpectSuccess); + } + + if ((text = pseudoDateTime.OFormatStringOffset) != null) + { + yield return new ParserTestData(text, expectedDto, 'O', pseudoDateTime.ExpectSuccess); + } + } + } + } + + private static IEnumerable> GenerateCorruptedDateTimeText(string goodText, char formatSymbol) + { + // Corrupt a single character + { + for (int i = 0; i < goodText.Length; i++) + { + char[] bad = (char[])(goodText.ToCharArray().Clone()); + if (char.IsDigit(goodText[i])) + { + bad[i] = (char)('0' - 1); + yield return new ParserTestData(new string(bad), default, formatSymbol, expectedSuccess: false); + bad[i] = (char)('9' + 1); + yield return new ParserTestData(new string(bad), default, formatSymbol, expectedSuccess: false); + } + else if (!(formatSymbol == 'O' && i == 27)) // Corrupting the "-" separating the offset doesn't yield a "bad" string - it yields a non-offset string followed by a "!" + { + bad[i] = '!'; + yield return new ParserTestData(new string(bad), default, formatSymbol, expectedSuccess: false); + } + } + } + + // Too short + { + for (int truncatedLength = 1; truncatedLength < goodText.Length - 1; truncatedLength++) + { + if (!(formatSymbol == 'O' && truncatedLength == 27)) // Chopping off the offset entirely leaves you with a good string... + { + yield return new ParserTestData(goodText.Substring(0, truncatedLength), default, formatSymbol, expectedSuccess: false); + } + } + } + + // 'R' and 'l' are case-sensitive. Flip the case of each letter + if (formatSymbol == 'R' || formatSymbol == 'l') + { + for (int i = 0; i < goodText.Length; i++) + { + char[] bad = (char[])(goodText.ToCharArray().Clone()); + if (char.IsLetter(goodText[i])) + { + bad[i] = (char)(goodText[i] ^ 0x20u); + yield return new ParserTestData(new string(bad), default, formatSymbol, expectedSuccess: false); + } + } + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/TestData.Parser.DecimalsAndFloats.cs b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/TestData.Parser.DecimalsAndFloats.cs new file mode 100644 index 0000000000..5e7b079251 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/TestData.Parser.DecimalsAndFloats.cs @@ -0,0 +1,110 @@ +// 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.Linq; +using System.Globalization; +using System.Collections.Generic; + +namespace System.Buffers.Text.Tests +{ + internal static partial class TestData + { + public static IEnumerable> DecimalParserTestData + { + get + { + foreach (FormatterTestData ftd in DecimalFormatterTestData) + { + // Not all FormatTestDatas for Decimal are actually roundtrippable. + if (ftd.FormatSymbol != default(char)) + continue; + + MutableDecimal d = ftd.Value.ToMutableDecimal(); + if (d.High == 0 && d.Mid == 0 && d.Low == 0 && d.IsNegative) + continue; // -0 is not roundtrippable + + foreach (ParserTestData testData in new FormatterTestData[] { ftd }.ToParserTheoryDataCollection()) + { + yield return testData; + } + } + + foreach (ParserTestData testData in GenerateNumberBasedParserTestData(DecimalFormats, decimal.TryParse)) + { + yield return testData; + } + + yield return new ParserTestData("1e" + int.MaxValue, default, 'E', expectedSuccess: false); + yield return new ParserTestData("0.01e" + int.MinValue, new MutableDecimal() { Scale = 28 }.ToDecimal(), 'E', expectedSuccess: true); + yield return new ParserTestData("-0.01e" + int.MinValue, new MutableDecimal() { Scale = 28, IsNegative = true }.ToDecimal(), 'E', expectedSuccess: true); + } + } + + public static IEnumerable> DoubleParserTestData + { + get + { + foreach (ParserTestData testData in GenerateNumberBasedParserTestData(FloatingPointFormats, double.TryParse)) + { + yield return testData; + } + + foreach (char formatSymbol in DecimalFormats.Select(f => f.Symbol)) + { + yield return new ParserTestData("Infinity", double.PositiveInfinity, formatSymbol, expectedSuccess: true); + yield return new ParserTestData("-Infinity", double.NegativeInfinity, formatSymbol, expectedSuccess: true); + yield return new ParserTestData("NaN", double.NaN, formatSymbol, expectedSuccess: true); + } + } + } + + public static IEnumerable> SingleParserTestData + { + get + { + foreach (ParserTestData testData in GenerateNumberBasedParserTestData(FloatingPointFormats, float.TryParse)) + { + yield return testData; + } + + foreach (char formatSymbol in DecimalFormats.Select(f => f.Symbol)) + { + yield return new ParserTestData("Infinity", float.PositiveInfinity, formatSymbol, expectedSuccess: true); + yield return new ParserTestData("-Infinity", float.NegativeInfinity, formatSymbol, expectedSuccess: true); + yield return new ParserTestData("NaN", float.NaN, formatSymbol, expectedSuccess: true); + } + } + } + + private static IEnumerable> GenerateNumberBasedParserTestData(IEnumerable formats, TryParseDelegate tryParse) + { + foreach (char formatSymbol in formats.Select(f => f.Symbol)) + { + foreach (ParserTestData testData in GeneratedParserTestDataUsingParseExact(formatSymbol, NumberTestData, + (string text, string format, IFormatProvider formatProvider, out T result) => + { + NumberStyles style = NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign; + if (formatSymbol != 'F' && formatSymbol != 'f') + { + style |= NumberStyles.AllowExponent; + } + if ((formatSymbol == 'E' || formatSymbol == 'e') && text.IndexOf("E", StringComparison.InvariantCultureIgnoreCase) == -1) + { + result = default; + return false; + } + else + { + return tryParse(text, style, formatProvider, out result); + } + })) + { + yield return testData; + } + } + } + + private delegate bool TryParseDelegate(string text, NumberStyles style, IFormatProvider formatProvider, out T result); + } +} diff --git a/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/TestData.Parser.Guid.cs b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/TestData.Parser.Guid.cs new file mode 100644 index 0000000000..bd2216652e --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/TestData.Parser.Guid.cs @@ -0,0 +1,62 @@ +// 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.Globalization; +using System.Collections.Generic; + +namespace System.Buffers.Text.Tests +{ + internal static partial class TestData + { + public static IEnumerable> GuidParserTestData + { + get + { + foreach (ParserTestData testData in GuidFormatterTestData.ToParserTheoryDataCollection()) + { + yield return testData; + } + + Guid guid = new Guid("08314FA2-B1FE-4792-BCD1-6E62338AC7F3"); + foreach (SupportedFormat format in GuidFormats) + { + string goodText = guid.ToString(format.Symbol.ToString(), CultureInfo.InvariantCulture); + + // Corrupt a single character + for (int i = 0; i < goodText.Length; i++) + { + char[] bad = (char[])(goodText.ToCharArray().Clone()); + if (char.IsDigit(goodText[i])) + { + bad[i] = (char)('0' - 1); + yield return new ParserTestData(new string(bad), default, format.Symbol, expectedSuccess: false); + bad[i] = (char)('9' + 1); + yield return new ParserTestData(new string(bad), default, format.Symbol, expectedSuccess: false); + } + else if (char.IsLetter(goodText[i])) + { + bad[i] = (char)('a' - 1); + yield return new ParserTestData(new string(bad), default, format.Symbol, expectedSuccess: false); + bad[i] = (char)('f' + 1); + yield return new ParserTestData(new string(bad), default, format.Symbol, expectedSuccess: false); + } + else + { + bad[i] = '!'; + yield return new ParserTestData(new string(bad), default, format.Symbol, expectedSuccess: false); + } + } + + // Stop short + for (int truncationPoint = 0; truncationPoint < goodText.Length; truncationPoint++) + { + string truncatedText = goodText.Substring(0, truncationPoint); + yield return new ParserTestData(truncatedText, default, format.Symbol, expectedSuccess: false); + yield return new ParserTestData(truncatedText.PadRight(goodText.Length, '$'), default, format.Symbol, expectedSuccess: false); + } + } + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/TestData.Parser.Integer.cs b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/TestData.Parser.Integer.cs new file mode 100644 index 0000000000..8e12d876b3 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/TestData.Parser.Integer.cs @@ -0,0 +1,265 @@ +// 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.Linq; +using System.Numerics; +using System.Collections.Generic; + +namespace System.Buffers.Text.Tests +{ + internal static partial class TestData + { + public static IEnumerable> SByteParserTestData + { + get + { + foreach (ParserTestData testData in SByteFormatterTestData.ToParserTheoryDataCollection()) + { + yield return testData; + } + + foreach (ParserTestData testData in GeneralIntegerParserTestData()) + { + yield return testData; + } + + // Code coverage + yield return new ParserTestData("5$", 5, 'D', expectedSuccess: true) { ExpectedBytesConsumed = 1 }; + yield return new ParserTestData("5$", 5, 'x', expectedSuccess: true) { ExpectedBytesConsumed = 1 }; + yield return new ParserTestData("5ff", 0, 'x', expectedSuccess: false); + } + } + + public static IEnumerable> ByteParserTestData + { + get + { + foreach (ParserTestData testData in ByteFormatterTestData.ToParserTheoryDataCollection()) + { + yield return testData; + } + + foreach (ParserTestData testData in GeneralIntegerParserTestData()) + { + yield return testData; + } + + // Code coverage + yield return new ParserTestData("5$", 5, 'D', expectedSuccess: true) { ExpectedBytesConsumed = 1 }; + yield return new ParserTestData("5$", 5, 'x', expectedSuccess: true) { ExpectedBytesConsumed = 1 }; + yield return new ParserTestData("5ff", 0, 'x', expectedSuccess: false); + } + } + + public static IEnumerable> Int16ParserTestData + { + get + { + foreach (ParserTestData testData in Int16FormatterTestData.ToParserTheoryDataCollection()) + { + yield return testData; + } + + foreach (ParserTestData testData in GeneralIntegerParserTestData()) + { + yield return testData; + } + + // Code coverage + yield return new ParserTestData("5$", 5, 'D', expectedSuccess: true) { ExpectedBytesConsumed = 1 }; + yield return new ParserTestData("5$", 5, 'x', expectedSuccess: true) { ExpectedBytesConsumed = 1 }; + yield return new ParserTestData("5faaf", 0, 'x', expectedSuccess: false); + } + } + + public static IEnumerable> UInt16ParserTestData + { + get + { + foreach (ParserTestData testData in UInt16FormatterTestData.ToParserTheoryDataCollection()) + { + yield return testData; + } + + foreach (ParserTestData testData in GeneralIntegerParserTestData()) + { + yield return testData; + } + + // Code coverage + yield return new ParserTestData("5$", 5, 'D', expectedSuccess: true) { ExpectedBytesConsumed = 1 }; + yield return new ParserTestData("5$", 5, 'x', expectedSuccess: true) { ExpectedBytesConsumed = 1 }; + yield return new ParserTestData("5faaf", 0, 'x', expectedSuccess: false); + } + } + + public static IEnumerable> Int32ParserTestData + { + get + { + foreach (ParserTestData testData in Int32FormatterTestData.ToParserTheoryDataCollection()) + { + yield return testData; + } + + foreach (ParserTestData testData in GeneralIntegerParserTestData()) + { + yield return testData; + } + + // Code coverage + yield return new ParserTestData("5$", 5, 'x', expectedSuccess: true) { ExpectedBytesConsumed = 1 }; + yield return new ParserTestData("5faaccbbf", 0, 'x', expectedSuccess: false); + + // This value will overflow a UInt32 accumulator upon assimilating the 0 in a way such that the wrapped-around value still looks like + // it's in the range of an Int32. Unless, of course, the implementation had the foresight to check before assimilating. + yield return new ParserTestData("9999999990", 0, 'D', expectedSuccess: false); + } + } + + public static IEnumerable> UInt32ParserTestData + { + get + { + foreach (ParserTestData testData in UInt32FormatterTestData.ToParserTheoryDataCollection()) + { + yield return testData; + } + + foreach (ParserTestData testData in GeneralIntegerParserTestData()) + { + yield return testData; + } + + // Code coverage + yield return new ParserTestData("5$", 5, 'D', expectedSuccess: true) { ExpectedBytesConsumed = 1 }; + yield return new ParserTestData("5$", 5, 'x', expectedSuccess: true) { ExpectedBytesConsumed = 1 }; + yield return new ParserTestData("5faaccbbf", 0, 'x', expectedSuccess: false); + } + } + + public static IEnumerable> Int64ParserTestData + { + get + { + foreach (ParserTestData testData in Int64FormatterTestData.ToParserTheoryDataCollection()) + { + yield return testData; + } + + foreach (ParserTestData testData in GeneralIntegerParserTestData()) + { + yield return testData; + } + + // Code coverage + yield return new ParserTestData("5$", 5, 'D', expectedSuccess: true) { ExpectedBytesConsumed = 1 }; + yield return new ParserTestData("5$", 5, 'x', expectedSuccess: true) { ExpectedBytesConsumed = 1 }; + yield return new ParserTestData("5faaccbb11223344f", 0, 'x', expectedSuccess: false); + } + } + + public static IEnumerable> UInt64ParserTestData + { + get + { + foreach (ParserTestData testData in UInt64FormatterTestData.ToParserTheoryDataCollection()) + { + yield return testData; + } + + foreach (ParserTestData testData in GeneralIntegerParserTestData()) + { + yield return testData; + } + + // Code coverage + yield return new ParserTestData("5$", 5, 'D', expectedSuccess: true) { ExpectedBytesConsumed = 1 }; + yield return new ParserTestData("5$", 5, 'x', expectedSuccess: true) { ExpectedBytesConsumed = 1 }; + yield return new ParserTestData("5faaccbb11223344f", 0, 'x', expectedSuccess: false); + } + } + + private static IEnumerable> GeneralIntegerParserTestData() + { + string[] GeneralIntegerNegativeInputs = + { + string.Empty, + "-", + "+", + "--5", + "++5", + }; + + BigInteger minValue = TestUtils.GetMinValue(); + BigInteger maxValue = TestUtils.GetMaxValue(); + bool isSigned = !minValue.IsZero; + + foreach (SupportedFormat format in IntegerFormats.Where(f => f.IsParsingImplemented() && f.ParseSynonymFor == default)) + { + foreach (string integerNegativeInput in GeneralIntegerNegativeInputs) + { + yield return new ParserTestData(integerNegativeInput, default, format.Symbol, expectedSuccess: false); + } + + // The hex format always parses as an unsigned number. That violates the assumptions made by this next set of test data. + // Since the parser uses the same code for hex-parsing signed and unsigned, just generate the data for the unsigned types only with + // no loss in code coverage. + if (!((format.Symbol == 'X' || format.Symbol == 'x') && isSigned)) + { + // Make sure that MaxValue and values just below it parse successfully. + for (int offset = -20; offset <= 0; offset++) + { + BigInteger bigValue = maxValue + offset; + string textD = bigValue.ToString("D"); + string text = bigValue.ToString(format.Symbol.ToString()); + T expectedValue = (T)(Convert.ChangeType(textD, typeof(T))); + yield return new ParserTestData(text, expectedValue, format.Symbol, expectedSuccess: true); + } + + // Make sure that values just above MaxValue don't parse successfully. + for (int offset = 1; offset <= 20; offset++) + { + BigInteger bigValue = maxValue + offset; + string text = bigValue.ToString(format.Symbol.ToString()); + yield return new ParserTestData(text, default, format.Symbol, expectedSuccess: false); + } + + { + BigInteger bigValue = maxValue * 10; + string text = bigValue.ToString(format.Symbol.ToString()); + yield return new ParserTestData(text, default, format.Symbol, expectedSuccess: false); + } + + if (isSigned) // No such thing as an underflow for unsigned integer parsing... + { + // Make sure that MinValue and values just above it parse successfully. + for (int offset = 0; offset <= 20; offset++) + { + BigInteger bigValue = minValue + offset; + string textD = bigValue.ToString("D"); + string text = bigValue.ToString(format.Symbol.ToString()); + T expectedValue = (T)(Convert.ChangeType(textD, typeof(T))); + yield return new ParserTestData(text, expectedValue, format.Symbol, expectedSuccess: true); + } + + // Make sure that values just below MinValue don't parse successfully. + for (int offset = -20; offset <= -1; offset++) + { + BigInteger bigValue = minValue + offset; + string text = bigValue.ToString(format.Symbol.ToString()); + yield return new ParserTestData(text, default, format.Symbol, expectedSuccess: false); + } + + { + BigInteger bigValue = minValue * 10; + string text = bigValue.ToString(format.Symbol.ToString()); + yield return new ParserTestData(text, default, format.Symbol, expectedSuccess: false); + } + } + } + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/TestData.Parser.TimeSpan.cs b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/TestData.Parser.TimeSpan.cs new file mode 100644 index 0000000000..74fb809744 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/TestData.Parser.TimeSpan.cs @@ -0,0 +1,218 @@ +// 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.Text; +using System.Linq; +using System.Collections.Generic; + +namespace System.Buffers.Text.Tests +{ + internal static partial class TestData + { + public static IEnumerable> TimeSpanParserTestData + { + get + { + foreach (ParserTestData testData in GeneratedParserTestDataUsingParseExact('G', TimeSpanParserBigGTestData.Concat(TimeSpanCombinatorialData), TimeSpan.TryParseExact)) + { + yield return testData; + } + + foreach (ParserTestData testData in GeneratedParserTestDataUsingParseExact('g', TimeSpanParserLittleGTestData.Concat(TimeSpanCombinatorialData), TimeSpan.TryParseExact)) + { + yield return testData; + } + + foreach (ParserTestData testData in GeneratedParserTestDataUsingParseExact('c', TimeSpanParserCTestData.Concat(TimeSpanCombinatorialData), TimeSpan.TryParseExact)) + { + yield return testData; + } + } + } + + // + // Generate sequences of numbers separated by various combinations of periods and colons. + // + private static IEnumerable TimeSpanCombinatorialData + { + get + { + for (int numComponents = 1; numComponents <= 6; numComponents++) + { + for (int separatorMask = 0; separatorMask < (1 << (numComponents - 1)); separatorMask++) + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < numComponents; i++) + { + sb.Append((20 + i).ToString()); + if (i != numComponents - 1) + { + char separator = ((separatorMask & (1 << i)) != 0) ? '.' : ':'; + sb.Append(separator); + } + } + + yield return sb.ToString(); + } + } + } + } + + private static IEnumerable TimeSpanParserBigGTestData + { + get + { + yield return "1:02:03:04.5678912"; + + // Leading space (allowed) + yield return " 1:02:03:04.5678912"; + yield return " 1:02:03:04.5678912"; + yield return " \t 1:02:03:04.5678912"; + + // Sign (only '-' allowed at start) + yield return "-1:02:03:04.5678912"; + yield return "+1:02:03:04.5678912"; + yield return "1:-02:03:04.5678912"; + yield return "1:+02:03:04.5678912"; + yield return "1:02:-03:04.5678912"; + yield return "1:02:+03:04.5678912"; + yield return "1:02:03:-04.5678912"; + yield return "1:02:03:+04.5678912"; + yield return "1:02:03:04.-5678912"; + yield return "1:02:03:04.+5678912"; + + // Random bad stuff + yield return ""; + yield return "-"; + + yield return "10675199:02:48:05.4775807"; // TimeSpan.MaxValue + yield return "10675199:02:48:05.4775808"; // TimeSpan.MaxValue + 1 + yield return "10675199:02:48:06.0000000"; // (next non-fractional overflow) + + yield return "-10675199:02:48:05.4775808"; // TimeSpan.MinValue + yield return "-10675199:02:48:05.4775809"; // TimeSpan.MinValue - 1 + yield return "-10675199:02:48:06.0000000"; // (next non-fractional underflow) + + // Individual ranges + yield return "0:0:0:0.0000000"; + yield return "0:23:0:0.0000000"; + yield return "0:24:0:0.0000000"; + yield return "0:0:59:0.0000000"; + yield return "0:0:60:0.0000000"; + yield return "0:0:0:59.0000000"; + yield return "0:0:0:60.0000000"; + yield return "0:0:0:0.9999999"; + yield return "0:0:0:0.10000000"; + + yield return "-0:0:0:0.0000000"; + yield return "-0:23:0:0.0000000"; + yield return "-0:24:0:0.0000000"; + yield return "-0:0:59:0.0000000"; + yield return "-0:0:60:0.0000000"; + yield return "-0:0:0:59.0000000"; + yield return "-0:0:0:60.0000000"; + yield return "-0:0:0:0.9999999"; + yield return "-0:0:0:0.10000000"; + + // Padding + yield return "000001:0000002:000003:0000004.0000000"; + yield return "0:0:0:0."; // Not allowed + yield return "0:0:0:0.1"; + yield return "0:0:0:0.12"; + yield return "0:0:0:0.123"; + yield return "0:0:0:0.1234"; + yield return "0:0:0:0.12345"; + yield return "0:0:0:0.123456"; + yield return "0:0:0:0.1234567"; + + // Not allowed (picky, picky) + yield return "0:0:0:0.12345670"; + + // Missing components (none allowed: 'G' is strict) + yield return "1"; + yield return "1:"; + yield return "1:2"; + yield return "1:2:"; + yield return "1:2:3"; + yield return "1:2:3:"; + yield return "1:2:3:4"; + yield return "1:2:3:4."; + } + } + + private static IEnumerable TimeSpanParserLittleGTestData + { + get + { + // All BigG strings are valid LittleG Strings so reuse that data. + foreach (string bigG in TimeSpanParserBigGTestData) + { + yield return bigG; + } + + yield return "1"; // 1-component -> day + yield return "1.9999999"; // illegal + yield return "4294967295"; // 1-component overflow + yield return "1:2"; // 2-component -> hour:minutes + yield return "1:2.9999999"; //illegal + yield return "1:4294967295"; // 2-component overflow + yield return "1:2:3"; // 3-component -> hour:minutes:seconds[.fraction] + yield return "1:2:3.9999999"; // 3-component -> hour:minutes:seconds[.fraction] + yield return "1:2:3.$$$$$$$"; //illegal + yield return "1:2:4294967295"; // 3-component overflow + yield return "1:2:4294967295.9999999"; // 4-component overflow + yield return "1:2:3:4"; // 4-component -> day:hour:minutes:seconds[.fraction] + yield return "1:2:3:4.9999999"; // 4-component -> day:hour:minutes:seconds[.fraction] + yield return "1:2:3:4.$$$$$$$"; //illegal + yield return "1:2:3:4294967295"; // 4-component overflow + yield return "1:2:3:4294967295.9999999"; // 4-component overflow + yield return "1:2:3:4:5"; // illegal - too many components + yield return "1:2:3:4:5.9999999"; // illegal - too many components + yield return "1:2:3:4.9999999:"; // intentionally flagged as error + yield return "1:2:3:4.9999999."; // intentionally flagged as error + } + } + + private static IEnumerable TimeSpanParserCTestData + { + get + { + yield return "1"; + yield return "1.9999999"; + yield return "4294967295"; + yield return "1:2"; + yield return "1:2.9999999"; + yield return "1:4294967295"; + yield return "1:2:3"; + yield return "1.2:3"; + yield return "1.2:3:4"; + yield return "1:2:3.9999999"; + yield return "1:2:3.$$$$$$$"; + yield return "1:2:4294967295"; + yield return "1:2:4294967295.9999999"; + yield return "1:2:3:4"; + yield return "1:2:3:4.9999999"; + yield return "1:2:3:4.$$$$$$$"; + yield return "1:2:3:4294967295"; + yield return "1:2:3:4294967295.9999999"; + yield return "1.2:3:4.9999999"; + yield return "1.2:3:4.$$$$$$$"; + yield return "1.2:3:4294967295"; + yield return "1.2:3:4294967295.9999999"; + yield return "1.2:3:4:5"; + yield return "1.2:3:4:5.9999999"; + yield return "1.2:3:4.9999999:"; + yield return "1.2:3:4.9999999."; + + yield return " \t 1"; // Leading whitespace is allowed + + yield return "-"; // Illegal + yield return "+"; // Illegal + + yield return "-1"; // Legal + yield return "+1"; // Illegal + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/TestData.Parser.cs b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/TestData.Parser.cs new file mode 100644 index 0000000000..b84c0aaf90 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/TestData.Parser.cs @@ -0,0 +1,92 @@ +// 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.Linq; +using System.Globalization; +using System.Collections.Generic; + +namespace System.Buffers.Text.Tests +{ + internal static partial class TestData + { + public static IEnumerable TypesThatCanBeParsed + { + get + { + yield return new object[] { typeof(bool) }; + yield return new object[] { typeof(sbyte) }; + yield return new object[] { typeof(byte) }; + yield return new object[] { typeof(short) }; + yield return new object[] { typeof(ushort) }; + yield return new object[] { typeof(int) }; + yield return new object[] { typeof(uint) }; + yield return new object[] { typeof(long) }; + yield return new object[] { typeof(ulong) }; + yield return new object[] { typeof(decimal) }; + yield return new object[] { typeof(float) }; + yield return new object[] { typeof(double) }; + yield return new object[] { typeof(Guid) }; + yield return new object[] { typeof(DateTime) }; + yield return new object[] { typeof(DateTimeOffset) }; + yield return new object[] { typeof(TimeSpan) }; + } + } + + public static IEnumerable BooleanParserTheoryData => BooleanParserTestData.Select(td => new object[] { td }); + public static IEnumerable SByteParserTheoryData => SByteParserTestData.Select(td => new object[] { td }); + public static IEnumerable ByteParserTheoryData => ByteParserTestData.Select(td => new object[] { td }); + public static IEnumerable Int16ParserTheoryData => Int16ParserTestData.Select(td => new object[] { td }); + public static IEnumerable UInt16ParserTheoryData => UInt16ParserTestData.Select(td => new object[] { td }); + public static IEnumerable Int32ParserTheoryData => Int32ParserTestData.Select(td => new object[] { td }); + public static IEnumerable UInt32ParserTheoryData => UInt32ParserTestData.Select(td => new object[] { td }); + public static IEnumerable Int64ParserTheoryData => Int64ParserTestData.Select(td => new object[] { td }); + public static IEnumerable UInt64ParserTheoryData => UInt64ParserTestData.Select(td => new object[] { td }); + public static IEnumerable DecimalParserTheoryData => DecimalParserTestData.Select(td => new object[] { td }); + public static IEnumerable DoubleParserTheoryData => DoubleParserTestData.Select(td => new object[] { td }); + public static IEnumerable SingleParserTheoryData => SingleParserTestData.Select(td => new object[] { td }); + public static IEnumerable GuidParserTheoryData => GuidParserTestData.Select(td => new object[] { td }); + public static IEnumerable DateTimeParserTheoryData => DateTimeParserTestData.Select(td => new object[] { td }); + public static IEnumerable DateTimeOffsetParserTheoryData => DateTimeOffsetParserTestData.Select(td => new object[] { td }); + public static IEnumerable TimeSpanParserTheoryData => TimeSpanParserTestData.Select(td => new object[] { td }); + + private static IEnumerable> ToParserTheoryDataCollection(this IEnumerable> formatterTestData) + { + HashSet seen = new HashSet(); + foreach (FormatterTestData testData in formatterTestData.Where(f => f.Format.IsParsingImplemented() && f.Format.ParseSynonymFor == default)) + { + // Formatters take precisions, Parsers do not. For many individual test cases, changing the precision doesn't change the formatted text output - + // if that's the case, there's no reason to test the same parse case twice. + if (seen.Add(new ReversedFormatTestDataKey(testData.FormatSymbol, testData.ExpectedOutput))) + yield return testData.ToParserTestData(); + } + } + + private struct ReversedFormatTestDataKey : IEquatable + { + public ReversedFormatTestDataKey(char formatSymbol, string text) + { + FormatSymbol = formatSymbol; + Text = text; + } + + public override bool Equals(object obj) => obj is ReversedFormatTestDataKey other && Equals(other); + public bool Equals(ReversedFormatTestDataKey other) => FormatSymbol == other.FormatSymbol && Text == other.Text; + public override int GetHashCode() => FormatSymbol.GetHashCode() ^ Text.GetHashCode(); + + public char FormatSymbol { get; } + public string Text { get; } + } + + private static IEnumerable> GeneratedParserTestDataUsingParseExact(char formatSymbol, IEnumerable texts, TryParseExactDelegate tryParseExact) + { + foreach (string text in texts) + { + bool expectedSuccess = tryParseExact(text, formatSymbol.ToString(), CultureInfo.InvariantCulture, out T expectedResult); + yield return new ParserTestData(text, expectedSuccess ? expectedResult : default, formatSymbol, expectedSuccess); + } + } + + private delegate bool TryParseExactDelegate(string text, string format, IFormatProvider formatProvider, out T result); + } +} diff --git a/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/ValidateParser.cs b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/ValidateParser.cs new file mode 100644 index 0000000000..9e2d49b009 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/Parser/ValidateParser.cs @@ -0,0 +1,372 @@ +// 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.Linq; + +namespace System.Buffers.Text.Tests +{ + public static partial class ParserTests + { + private static void ValidateParser(ParserTestData testData) + { + ValidateParserHelper(testData); + + if (testData.ExpectedSuccess) + { + // Add several bytes of junk after the real text to ensure the parser successfully ignores it. By adding lots of junk, this also + // exercises the "string is too long to rule out overflow based on length" paths of the integer parsers. + ParserTestData testDataWithExtraCharacter = new ParserTestData(testData.Text + "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", testData.ExpectedValue, testData.FormatSymbol, expectedSuccess: true) + { + ExpectedBytesConsumed = testData.ExpectedBytesConsumed + }; + ValidateParserHelper(testDataWithExtraCharacter); + } + } + + private static void ValidateParserHelper(ParserTestData testData) + { + ReadOnlySpan utf8Text = testData.Text.ToUtf8Span(); + bool success = TryParseUtf8(utf8Text, out T value, out int bytesConsumed, testData.FormatSymbol); + if (testData.ExpectedSuccess) + { + if (!success) + throw new TestException($"This parse attempt {testData} was expected to succeed: instead, it failed."); + + T expected = testData.ExpectedValue; + T actual = value; + if (!IsParsedValueEqual(expected: expected, actual: actual)) + { + string expectedString = expected.DisplayString(); + string actualString = actual.DisplayString(); + throw new TestException($"This parse attempt {testData} succeeded as expected but parsed to the wrong value:\n Expected: {expectedString}\n Actual: {actualString}\n"); + } + + int expectedBytesConsumed = testData.ExpectedBytesConsumed; + if (expectedBytesConsumed != bytesConsumed) + throw new TestException($"This parse attempt {testData} returned the correct value but the wrong `bytesConsumed` value:\n Expected: {expectedBytesConsumed}\n Actual: {bytesConsumed}\n"); + } + else + { + if (success) + throw new TestException($"This parse attempt {testData} was expected to fail: instead, it succeeded and returned {value}."); + + if (bytesConsumed != 0) + throw new TestException($"This parse attempt {testData} failed as expected but did not set `bytesConsumed` to 0"); + + if (!(value.Equals(default(T)))) + throw new TestException($"This parse attempt {testData} failed as expected but did not set `value` to default(T)"); + } + } + + private static bool IsParsedValueEqual(T expected, T actual) + { + if (typeof(T) == typeof(decimal)) + { + int[] expectedBits = decimal.GetBits((decimal)(object)expected); + int[] actualBits = decimal.GetBits((decimal)(object)actual); + if (!expectedBits.SequenceEqual(actualBits)) + { + // Do not simplify into "return expectedBits.SequenceEqual()". I want to be able to put a breakpoint here. + return false; + } + return true; + } + + if (typeof(T) == typeof(DateTime)) + { + DateTime expectedDateTime = (DateTime)(object)expected; + DateTime actualDateTime = (DateTime)(object)actual; + + if (expectedDateTime.Kind != actualDateTime.Kind) + return false; + + if (expectedDateTime.Ticks != actualDateTime.Ticks) + return false; + + return true; + } + + if (typeof(T) == typeof(DateTimeOffset)) + { + DateTimeOffset expectedDateTimeOffset = (DateTimeOffset)(object)expected; + DateTimeOffset actualDateTimeOffset = (DateTimeOffset)(object)actual; + + if (expectedDateTimeOffset.Offset != actualDateTimeOffset.Offset) + return false; + + if (!IsParsedValueEqual(expectedDateTimeOffset.DateTime, actualDateTimeOffset.DateTime)) + return false; + + return true; + } + + // Parsed floating points are constructed, not computed. Thus, we can do the exact compare. + if (typeof(T) == typeof(double)) + { + double expectedDouble = (double)(object)expected; + double actualDouble = (double)(object)actual; + + unsafe + { + if (*((ulong*)&expectedDouble) != *((ulong*)&actualDouble)) + return false; + + return true; + } + } + + // Parsed floating points are constructed, not computed. Thus, we can do the exact compare. + if (typeof(T) == typeof(float)) + { + float expectedSingle = (float)(object)expected; + float actualSingle = (float)(object)actual; + + unsafe + { + if (*((uint*)&expectedSingle) != *((uint*)&actualSingle)) + return false; + + return true; + } + } + + return expected.Equals(actual); + } + + private static bool TryParseUtf8(ReadOnlySpan text, out T value, out int bytesConsumed, char format) + { + if (typeof(T) == typeof(bool)) + { + bool success = Utf8Parser.TryParse(text, out bool v, out bytesConsumed, format); + value = (T)(object)v; + return success; + } + + if (typeof(T) == typeof(sbyte)) + { + bool success = Utf8Parser.TryParse(text, out sbyte v, out bytesConsumed, format); + value = (T)(object)v; + return success; + } + + if (typeof(T) == typeof(byte)) + { + bool success = Utf8Parser.TryParse(text, out byte v, out bytesConsumed, format); + value = (T)(object)v; + return success; + } + + if (typeof(T) == typeof(short)) + { + bool success = Utf8Parser.TryParse(text, out short v, out bytesConsumed, format); + value = (T)(object)v; + return success; + } + + if (typeof(T) == typeof(ushort)) + { + bool success = Utf8Parser.TryParse(text, out ushort v, out bytesConsumed, format); + value = (T)(object)v; + return success; + } + + if (typeof(T) == typeof(int)) + { + bool success = Utf8Parser.TryParse(text, out int v, out bytesConsumed, format); + value = (T)(object)v; + return success; + } + + if (typeof(T) == typeof(uint)) + { + bool success = Utf8Parser.TryParse(text, out uint v, out bytesConsumed, format); + value = (T)(object)v; + return success; + } + + if (typeof(T) == typeof(long)) + { + bool success = Utf8Parser.TryParse(text, out long v, out bytesConsumed, format); + value = (T)(object)v; + return success; + } + + if (typeof(T) == typeof(ulong)) + { + bool success = Utf8Parser.TryParse(text, out ulong v, out bytesConsumed, format); + value = (T)(object)v; + return success; + } + + if (typeof(T) == typeof(decimal)) + { + bool success = Utf8Parser.TryParse(text, out decimal v, out bytesConsumed, format); + value = (T)(object)v; + return success; + } + + if (typeof(T) == typeof(Guid)) + { + bool success = Utf8Parser.TryParse(text, out Guid v, out bytesConsumed, format); + value = (T)(object)v; + return success; + } + + if (typeof(T) == typeof(float)) + { + bool success = Utf8Parser.TryParse(text, out float v, out bytesConsumed, format); + value = (T)(object)v; + return success; + } + + if (typeof(T) == typeof(double)) + { + bool success = Utf8Parser.TryParse(text, out double v, out bytesConsumed, format); + value = (T)(object)v; + return success; + } + + if (typeof(T) == typeof(DateTime)) + { + bool success = Utf8Parser.TryParse(text, out DateTime v, out bytesConsumed, format); + value = (T)(object)v; + return success; + } + + if (typeof(T) == typeof(DateTimeOffset)) + { + bool success = Utf8Parser.TryParse(text, out DateTimeOffset v, out bytesConsumed, format); + value = (T)(object)v; + return success; + } + + if (typeof(T) == typeof(TimeSpan)) + { + bool success = Utf8Parser.TryParse(text, out TimeSpan v, out bytesConsumed, format); + value = (T)(object)v; + return success; + } + + throw new Exception("Unsupported type " + typeof(T)); + } + + private static bool TryParseUtf8(Type type, ReadOnlySpan text, out object value, out int bytesConsumed, char format) + { + if (type == typeof(bool)) + { + bool success = Utf8Parser.TryParse(text, out bool v, out bytesConsumed, format); + value = v; + return success; + } + + if (type == typeof(sbyte)) + { + bool success = Utf8Parser.TryParse(text, out sbyte v, out bytesConsumed, format); + value = v; + return success; + } + + if (type == typeof(byte)) + { + bool success = Utf8Parser.TryParse(text, out byte v, out bytesConsumed, format); + value = v; + return success; + } + + if (type == typeof(short)) + { + bool success = Utf8Parser.TryParse(text, out short v, out bytesConsumed, format); + value = v; + return success; + } + + if (type == typeof(ushort)) + { + bool success = Utf8Parser.TryParse(text, out ushort v, out bytesConsumed, format); + value = v; + return success; + } + + if (type == typeof(int)) + { + bool success = Utf8Parser.TryParse(text, out int v, out bytesConsumed, format); + value = v; + return success; + } + + if (type == typeof(uint)) + { + bool success = Utf8Parser.TryParse(text, out uint v, out bytesConsumed, format); + value = v; + return success; + } + + if (type == typeof(long)) + { + bool success = Utf8Parser.TryParse(text, out long v, out bytesConsumed, format); + value = v; + return success; + } + + if (type == typeof(ulong)) + { + bool success = Utf8Parser.TryParse(text, out ulong v, out bytesConsumed, format); + value = v; + return success; + } + + if (type == typeof(decimal)) + { + bool success = Utf8Parser.TryParse(text, out decimal v, out bytesConsumed, format); + value = v; + return success; + } + + if (type == typeof(Guid)) + { + bool success = Utf8Parser.TryParse(text, out Guid v, out bytesConsumed, format); + value = v; + return success; + } + + if (type == typeof(float)) + { + bool success = Utf8Parser.TryParse(text, out float v, out bytesConsumed, format); + value = v; + return success; + } + + if (type == typeof(double)) + { + bool success = Utf8Parser.TryParse(text, out double v, out bytesConsumed, format); + value = v; + return success; + } + + if (type == typeof(DateTime)) + { + bool success = Utf8Parser.TryParse(text, out DateTime v, out bytesConsumed, format); + value = v; + return success; + } + + if (type == typeof(DateTimeOffset)) + { + bool success = Utf8Parser.TryParse(text, out DateTimeOffset v, out bytesConsumed, format); + value = v; + return success; + } + + if (type == typeof(TimeSpan)) + { + bool success = Utf8Parser.TryParse(text, out TimeSpan v, out bytesConsumed, format); + value = v; + return success; + } + + throw new Exception("Unsupported type " + type); + } + } +} + diff --git a/external/corefx/src/System.Memory/tests/ParsersAndFormatters/PseudoDateTime.cs b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/PseudoDateTime.cs new file mode 100644 index 0000000000..e546f3e809 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/PseudoDateTime.cs @@ -0,0 +1,145 @@ +// 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. + +namespace System.Buffers.Text.Tests +{ + // + // Used to model DateTime and DateTimeOffsets that have "illegal" values (e.g. Jan 32) This is used to generate a subset of DateTime + // and DateTimeOffset ParserTestData objects. + // + public sealed class PseudoDateTime + { + public PseudoDateTime(int year, int month, int day, int hour, int minute, int second, bool expectSuccess) + : this(year, month, day, hour, minute, second, fraction: 0, offsetNegative: false, offsetHours: 0, offsetMinutes: 0, expectSuccess: expectSuccess) + { + } + + public PseudoDateTime(int year, int month, int day, int hour, int minute, int second, int fraction, bool offsetNegative, int offsetHours, int offsetMinutes, bool expectSuccess) + { + Year = year; + Month = month; + Day = day; + Hour = hour; + Minute = minute; + Second = second; + Fraction = fraction; + OffsetNegative = offsetNegative; + OffsetHours = offsetHours; + OffsetMinutes = offsetMinutes; + + ExpectSuccess = expectSuccess; + } + + public string DefaultString + { + get + { + if (Fraction != 0) + return null; + + return Month.ToString("D2") + "/" + Day.ToString("D2") + "/" + Year.ToString("D4") + + " " + Hour.ToString("D2") + ":" + Minute.ToString("D2") + ":" + Second.ToString("D2") + + " " + (OffsetNegative ? "-" : "+") + OffsetHours.ToString("D2") + ":" + OffsetMinutes.ToString("D2"); + } + } + + public string GFormatString + { + get + { + if (Fraction != 0) + return null; + if (OffsetHours != 0 || OffsetMinutes != 0) + return null; + + return Month.ToString("D2") + "/" + Day.ToString("D2") + "/" + Year.ToString("D4") + + " " + Hour.ToString("D2") + ":" + Minute.ToString("D2") + ":" + Second.ToString("D2"); + } + } + + public string RFormatString + { + get + { + //Tue, 00 Jan 2017 08:08:05 GMT + if (Fraction != 0) + return null; + if (OffsetHours != 0 || OffsetMinutes != 0) + return null; + + string dayAbbreviation; + if (ExpectSuccess) + { + TimeSpan offset = new TimeSpan(hours: OffsetHours, minutes: OffsetMinutes, seconds: 0); + if (OffsetNegative) + offset = -offset; + DateTimeOffset dto = new DateTimeOffset(year: Year, month: Month, day: Day, hour: Hour, minute: Minute, second: Second, offset: offset); + dayAbbreviation = s_dayAbbreviations[(int)(dto.DayOfWeek)]; + } + else + { + // Pick something legal here as we're expecting code coverage of an error case and we don't want a bad day abbreviation to bypass that. + dayAbbreviation = "Sun"; + } + + string monthAbbrevation; + if (Month >= 1 && Month <= 12) + { + monthAbbrevation = s_monthAbbreviations[Month - 1]; + } + else + { + // Pick something legal here as we're expecting code coverage of an error case and we don't want a bad day abbreviation to bypass that. + monthAbbrevation = "Jan"; + } + + return dayAbbreviation + ", " + Day.ToString("D2") + " " + monthAbbrevation + " " + Year.ToString("D4") + " " + + Hour.ToString("D2") + ":" + Minute.ToString("D2") + ":" + Second.ToString("D2") + " " + + "GMT"; + } + } + + public string LFormatString => RFormatString?.ToLowerInvariant(); + + public string OFormatStringNoOffset + { + get + { + if (OffsetHours != 0 || OffsetMinutes != 0) + return null; + + return Year.ToString("D4") + "-" + Month.ToString("D2") + "-" + Day.ToString("D2") + "T" + + Hour.ToString("D2") + ":" + Minute.ToString("D2") + ":" + Second.ToString("D2") + + "." + Fraction.ToString("D7"); + } + } + + public string OFormatStringZ => (OffsetHours != 0 || OffsetMinutes != 0) ? null : OFormatStringNoOffset + "Z"; + public string OFormatStringOffset + { + get + { + return Year.ToString("D4") + "-" + Month.ToString("D2") + "-" + Day.ToString("D2") + "T" + + Hour.ToString("D2") + ":" + Minute.ToString("D2") + ":" + Second.ToString("D2") + + "." + Fraction.ToString("D7") + + (OffsetNegative ? "-" : "+") + OffsetHours.ToString("D2") + ":" + OffsetMinutes.ToString("D2"); + } + } + + public int Year { get; } + public int Month { get; } + public int Day { get; } + public int Hour { get; } + public int Minute { get; } + public int Second { get; } + public int Fraction { get; } + public bool OffsetNegative { get; } + public int OffsetHours { get; } + public int OffsetMinutes { get; } + public bool ExpectSuccess { get; } + + private static readonly string[] s_dayAbbreviations = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + private static readonly string[] s_monthAbbreviations = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + } +} diff --git a/external/corefx/src/System.Memory/tests/ParsersAndFormatters/StandardFormatTests.cs b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/StandardFormatTests.cs new file mode 100644 index 0000000000..4bdc5f918b --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/StandardFormatTests.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. + +using System.Collections.Generic; + +using Xunit; + +namespace System.Buffers.Text.Tests +{ + public static partial class StandardFormatTests + { + [Fact] + public static void StandardFormatCtorNegative() + { + Assert.Throws(() => new StandardFormat((char)256)); + Assert.Throws(() => new StandardFormat('D', StandardFormat.MaxPrecision + 1)); + } + + [Theory] + [InlineData("G4", 'G', 4)] + [InlineData("n0", 'n', 0)] + [InlineData("d", 'd', StandardFormat.NoPrecision)] + [InlineData("x99", 'x', StandardFormat.MaxPrecision)] + [InlineData(null, default(char), default(byte))] + [InlineData("", default(char), default(byte))] + public static void StandardFormatParseString(string formatString, char expectedSymbol, byte expectedPrecision) + { + StandardFormat format = StandardFormat.Parse(formatString); + Assert.Equal(expectedSymbol, format.Symbol); + Assert.Equal(expectedPrecision, format.Precision); + } + + [Theory] + [InlineData("G4", 'G', 4)] + [InlineData("n0", 'n', 0)] + [InlineData("d", 'd', StandardFormat.NoPrecision)] + [InlineData("x99", 'x', StandardFormat.MaxPrecision)] + [InlineData("", default(char), default(byte))] + public static void StandardFormatParseSpan(string formatString, char expectedSymbol, byte expectedPrecision) + { + ReadOnlySpan span = formatString.AsReadOnlySpan(); + StandardFormat format = StandardFormat.Parse(span); + Assert.Equal(expectedSymbol, format.Symbol); + Assert.Equal(expectedPrecision, format.Precision); + } + + [Theory] + [InlineData("G$")] + [InlineData("Ga")] + [InlineData("G100")] + public static void StandardFormatParseNegative(string badFormatString) + { + Assert.Throws(() => StandardFormat.Parse(badFormatString)); + } + + [Theory] + [InlineData('a')] + [InlineData('B')] + public static void StandardFormatOpImplicitFromChar(char c) + { + StandardFormat format = c; + Assert.Equal(c, format.Symbol); + Assert.Equal(StandardFormat.NoPrecision, format.Precision); + } + + [Theory] + [MemberData(nameof(EqualityTestData))] + public static void StandardFormatEquality(StandardFormat f1, StandardFormat f2, bool expectedToBeEqual) + { + { + bool actual = f1.Equals(f2); + Assert.Equal(expectedToBeEqual, actual); + } + } + + [Theory] + [MemberData(nameof(EqualityTestData))] + public static void StandardFormatBoxedEquality(StandardFormat f1, StandardFormat f2, bool expectedToBeEqual) + { + object boxedf2 = f2; + bool actual = f1.Equals(boxedf2); + Assert.Equal(expectedToBeEqual, actual); + + } + + [Theory] + [MemberData(nameof(EqualityTestData))] + public static void StandardFormatOpEquality(StandardFormat f1, StandardFormat f2, bool expectedToBeEqual) + { + { + bool actual = f1 == f2; + Assert.Equal(expectedToBeEqual, actual); + } + } + + [Theory] + [MemberData(nameof(EqualityTestData))] + public static void StandardFormatOpInequality(StandardFormat f1, StandardFormat f2, bool expectedToBeEqual) + { + { + bool actual = f1 != f2; + Assert.NotEqual(expectedToBeEqual, actual); + } + } + + [Theory] + [MemberData(nameof(EqualityTestData))] + public static void StandardFormatGetHashCode(StandardFormat f1, StandardFormat f2, bool expectedToBeEqual) + { + if (expectedToBeEqual) + { + int h1 = f1.GetHashCode(); + int h2 = f2.GetHashCode(); + Assert.Equal(h1, h2); + } + } + + [Theory] + [MemberData(nameof(EqualityTestData))] + public static void StandardFormatGetHashCodeIsContentBased(StandardFormat f1, StandardFormat f2, bool expectedToBeEqual) + { + object boxedf1 = f1; + object aDifferentBoxedF1 = f1; + int h1 = boxedf1.GetHashCode(); + int h2 = aDifferentBoxedF1.GetHashCode(); + Assert.Equal(h1, h2); + } + + public static IEnumerable EqualityTestData + { + get + { + yield return new object[] { new StandardFormat('A', 3), new StandardFormat('A', 3), true }; + yield return new object[] { new StandardFormat('a', 3), new StandardFormat('A', 3), false }; + yield return new object[] { new StandardFormat('A', 3), new StandardFormat('A', 4), false }; + yield return new object[] { new StandardFormat('A', 3), new StandardFormat('A', StandardFormat.NoPrecision), false }; + } + } + + [Theory] + [InlineData("G4", 'G', 4)] + [InlineData("n0", 'n', 0)] + [InlineData("d", 'd', StandardFormat.NoPrecision)] + [InlineData("x99", 'x', StandardFormat.MaxPrecision)] + [InlineData("", default(char), default(byte))] + public static void StandardFormatToString(string expected, char symbol, byte precision) + { + StandardFormat format = new StandardFormat(symbol, precision); + string actual = format.ToString(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void StandardFormatToStringOversizedPrecision() + { + // Code coverage: Precision of 100 is not legal but ToString() isn't allowed to throw an exception for that. + // Make sure it doesn't. + + const byte BadPrecision = 100; + + StandardFormat format = default; + unsafe + { + // We're aiming for the Precision field but we don't know where it is so nuke 'em all. + new Span(&format, sizeof(StandardFormat)).Fill(BadPrecision); + } + + string s = format.ToString(); + Assert.NotNull(s); + } + } +} + diff --git a/external/corefx/src/System.Memory/tests/ParsersAndFormatters/SupportedFormats.cs b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/SupportedFormats.cs new file mode 100644 index 0000000000..929e9e2867 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/SupportedFormats.cs @@ -0,0 +1,139 @@ +// 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.Linq; +using System.Collections.Generic; + +namespace System.Buffers.Text.Tests +{ + // Test metadata that describes a standard format (e.g. 'G', 'D' and 'X') supported by a particular data type. + public sealed class SupportedFormat + { + public SupportedFormat(char symbol, bool supportsPrecision) + { + Symbol = symbol; + SupportsPrecision = supportsPrecision; + } + + public char Symbol { get; } + public bool SupportsPrecision { get; } + public bool IsDefault { get; set; } = false; + public bool NoRepresentation { get; set; } = false; // If true, can only be accessed by passing default(StandardFormat). (The weird DateTimeOffset case.) + public char FormatSynonymFor { get; set; } = default; + public char ParseSynonymFor { get; set; } = default; + } + + internal static partial class TestData + { + public static bool IsParsingImplemented(this SupportedFormat f) => f.IsParsingImplemented(typeof(T)); + + // + // Used to disable automatic generation of ParserTestData from FormatterTestData + // + public static bool IsParsingImplemented(this SupportedFormat f, Type t) + { + if (IntegerTypes.Contains(t) && (f.Symbol == 'N' || f.Symbol == 'n')) + return false; + + return true; + } + + public static IEnumerable IntegerFormats + { + get + { + yield return new SupportedFormat('G', supportsPrecision: false) { IsDefault = true }; + yield return new SupportedFormat('g', supportsPrecision: false) { FormatSynonymFor = 'G', ParseSynonymFor = 'G' }; + yield return new SupportedFormat('D', supportsPrecision: true); + yield return new SupportedFormat('d', supportsPrecision: true) { FormatSynonymFor = 'D', ParseSynonymFor = 'd' }; + yield return new SupportedFormat('N', supportsPrecision: true); + yield return new SupportedFormat('n', supportsPrecision: true) { FormatSynonymFor = 'N', ParseSynonymFor = 'N' }; + yield return new SupportedFormat('X', supportsPrecision: true); + yield return new SupportedFormat('x', supportsPrecision: true) { ParseSynonymFor = 'X' }; + } + } + + public static IEnumerable DecimalFormats + { + get + { + yield return new SupportedFormat('G', supportsPrecision: false) { IsDefault = true }; + yield return new SupportedFormat('g', supportsPrecision: false) { FormatSynonymFor = 'G', ParseSynonymFor = 'G' }; + yield return new SupportedFormat('E', supportsPrecision: true); + yield return new SupportedFormat('e', supportsPrecision: true) { ParseSynonymFor = 'E' }; + yield return new SupportedFormat('F', supportsPrecision: true); + yield return new SupportedFormat('f', supportsPrecision: true) { FormatSynonymFor = 'F', ParseSynonymFor = 'F' }; + } + } + + public static IEnumerable FloatingPointFormats + { + get + { + yield return new SupportedFormat('G', supportsPrecision: false) { IsDefault = true }; + yield return new SupportedFormat('g', supportsPrecision: false) { FormatSynonymFor = 'G', ParseSynonymFor = 'G' }; + yield return new SupportedFormat('E', supportsPrecision: true); + yield return new SupportedFormat('e', supportsPrecision: true) { ParseSynonymFor = 'E' }; + yield return new SupportedFormat('F', supportsPrecision: true); + yield return new SupportedFormat('f', supportsPrecision: true) { FormatSynonymFor = 'F', ParseSynonymFor = 'F' }; + } + } + + public static IEnumerable BooleanFormats + { + get + { + yield return new SupportedFormat('G', supportsPrecision: false) { IsDefault = true }; + yield return new SupportedFormat('l', supportsPrecision: false) { ParseSynonymFor = 'l' }; + } + } + + public static IEnumerable GuidFormats + { + get + { + yield return new SupportedFormat('D', supportsPrecision: false) { IsDefault = true }; + yield return new SupportedFormat('N', supportsPrecision: false); + yield return new SupportedFormat('P', supportsPrecision: false); + yield return new SupportedFormat('B', supportsPrecision: false); + } + } + + public static IEnumerable DateTimeFormats + { + get + { + yield return new SupportedFormat('G', supportsPrecision: false) { IsDefault = true }; + yield return new SupportedFormat('R', supportsPrecision: false); + yield return new SupportedFormat('l', supportsPrecision: false); + yield return new SupportedFormat('O', supportsPrecision: false); + } + } + + public static IEnumerable DateTimeOffsetFormats + { + get + { + // The "default" format for DateTimeOffset is weird - it's like "G" but also suffixes an offset so it doesn't exactly match any of the explicit offsets. + yield return new SupportedFormat(default, supportsPrecision: false) { IsDefault = true, NoRepresentation = true }; + yield return new SupportedFormat('G', supportsPrecision: false); + yield return new SupportedFormat('R', supportsPrecision: false); + yield return new SupportedFormat('l', supportsPrecision: false); + yield return new SupportedFormat('O', supportsPrecision: false); + } + } + + public static IEnumerable TimeSpanFormats + { + get + { + yield return new SupportedFormat('G', supportsPrecision: false); + yield return new SupportedFormat('g', supportsPrecision: false); + yield return new SupportedFormat('c', supportsPrecision: false) { IsDefault = true }; + yield return new SupportedFormat('t', supportsPrecision: false) { ParseSynonymFor = 'c', FormatSynonymFor = 'c' }; + yield return new SupportedFormat('T', supportsPrecision: false) { ParseSynonymFor = 'c', FormatSynonymFor = 'c' }; + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ParsersAndFormatters/TestData.cs b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/TestData.cs new file mode 100644 index 0000000000..f30e4f23b6 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/TestData.cs @@ -0,0 +1,549 @@ +// 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.Linq; +using System.Collections.Generic; + +namespace System.Buffers.Text.Tests +{ + // + // General purpose raw test data. + // + internal static partial class TestData + { + public static readonly IEnumerable s_precisions = new byte[] { StandardFormat.NoPrecision, 0, 1, 3, 10, StandardFormat.MaxPrecision }; + + public static IEnumerable IntegerTypesTheoryData => IntegerTypes.Select(t => new object[] { t }); + + public static IEnumerable IntegerTypes + { + get + { + yield return typeof(sbyte); + yield return typeof(byte); + yield return typeof(short); + yield return typeof(ushort); + yield return typeof(int); + yield return typeof(uint); + yield return typeof(long); + yield return typeof(ulong); + } + } + + public static IEnumerable BooleanTestData + { + get + { + yield return true; + yield return false; + } + } + + public static IEnumerable SByteTestData + { + get + { + foreach (long l in Int64TestData) + { + if (l >= sbyte.MinValue && l <= sbyte.MaxValue) + { + yield return (sbyte)l; + } + } + } + } + + public static IEnumerable ByteTestData + { + get + { + foreach (long l in Int64TestData) + { + if (l >= byte.MinValue && l <= byte.MaxValue) + { + yield return (byte)l; + } + } + } + } + + public static IEnumerable Int16TestData + { + get + { + foreach (long l in Int64TestData) + { + if (l >= short.MinValue && l <= short.MaxValue) + { + yield return (short)l; + } + } + } + } + + public static IEnumerable UInt16TestData + { + get + { + foreach (long l in Int64TestData) + { + if (l >= ushort.MinValue && l <= ushort.MaxValue) + { + yield return (ushort)l; + } + } + } + } + + public static IEnumerable Int32TestData + { + get + { + foreach (long l in Int64TestData) + { + if (l >= int.MinValue && l <= int.MaxValue) + { + yield return (int)l; + } + } + } + } + + public static IEnumerable UInt32TestData + { + get + { + foreach (long l in Int64TestData) + { + if (l >= uint.MinValue && l <= uint.MaxValue) + { + yield return (uint)l; + } + } + } + } + + public static IEnumerable Int64TestData + { + get + { + yield return 0L; + yield return 1L; + yield return 123L; + yield return -123L; + yield return 1234L; + yield return -1234L; + yield return 12345L; + yield return -12345L; + + yield return 4294967294999999999L; // uint.MaxValue * Billion - 1 + yield return 4294967295000000000L; // uint.MaxValue * Billion + yield return 4294967295000000001L; // uint.MaxValue * Billion + 1 + yield return -4294967294999999999L; // -(uint.MaxValue * Billion - 1) + yield return -4294967295000000000L; // -(uint.MaxValue * Billion) + yield return -4294967295000000001L; // -(uint.MaxValue * Billion + 1) + yield return 4294967296000000000L; // (uint.MaxValue + 1) * Billion + yield return -4294967296000000000L; // -(uint.MaxValue + 1) * Billion + + long powerOf10 = 1L; + for (int i = 0; i < 21; i++) + { + powerOf10 *= 10L; + yield return powerOf10 - 1; + yield return powerOf10; + yield return -(powerOf10 - 1); + yield return -powerOf10; + } + + yield return sbyte.MinValue; + yield return sbyte.MinValue - 1; + yield return sbyte.MinValue + 1; + + yield return sbyte.MaxValue; + yield return sbyte.MaxValue - 1; + yield return sbyte.MaxValue + 1; + + yield return short.MinValue; + yield return short.MinValue - 1; + yield return short.MinValue + 1; + + yield return short.MaxValue; + yield return short.MaxValue - 1; + yield return short.MaxValue + 1; + + yield return int.MinValue; + yield return ((long)int.MinValue) - 1; + yield return int.MinValue + 1; + + yield return int.MaxValue; + yield return int.MaxValue - 1; + yield return ((long)int.MaxValue) + 1; + + yield return long.MinValue; + yield return long.MinValue + 1; + + yield return long.MaxValue; + yield return long.MaxValue - 1; + + yield return byte.MaxValue; + yield return byte.MaxValue - 1; + + yield return ushort.MaxValue; + yield return ushort.MaxValue - 1; + + yield return uint.MaxValue; + yield return uint.MaxValue - 1; + } + } + + public static IEnumerable UInt64TestData + { + get + { + foreach (long l in Int64TestData) + { + if (l >= 0) + { + yield return (ulong)l; + } + } + + yield return long.MaxValue + 1LU; + yield return ulong.MaxValue - 1LU; + yield return ulong.MaxValue; + } + } + + public static IEnumerable DecimalTestData + { + get + { + foreach (long l in Int64TestData) + { + yield return l; + } + + yield return decimal.MinValue; + yield return decimal.MaxValue; + + // negative 0m. The formatter is expected *not* to emit a minus sign in this case. + yield return (new MutableDecimal() { High = 0, Mid = 0, Low = 0, IsNegative = true }).ToDecimal(); + + yield return 0.304m; // Round down + yield return -0.304m; + yield return 0.305m; // Round up + yield return -0.305m; + yield return 999.99m; + yield return -999.99m; + yield return 0.000123456m; + yield return -0.000123456m; + + // Explicit trailing 0's (Decimal can and does preserve these by setting the Scale appropriately) + yield return 1.00m; + yield return 0.00m; + yield return -1.00m; + yield return -0.00m; + } + } + + public static IEnumerable DoubleTestData + { + get + { + foreach (long l in Int64TestData) + { + yield return l; + } + + yield return 1.23; + } + } + + public static IEnumerable SingleTestData + { + get + { + foreach (long d in DoubleTestData) + { + float f = d; + if (!float.IsInfinity(f)) + yield return f; + } + } + } + + public static IEnumerable GuidTestData + { + get + { + yield return new Guid("CB0AFB61-6F04-401A-BBEA-C0FC0B6E4E51"); + yield return new Guid("FC1911F9-9EED-4CA8-AC8B-CEEE1EBE2C72"); + } + } + + public static IEnumerable DateTimeTestData + { + get + { + { + // Kind == Unspecified + TimeSpan offset = new TimeSpan(hours: 8, minutes: 0, seconds: 0); + DateTimeOffset dto = new DateTimeOffset(year: 2017, month: 1, day: 13, hour: 3, minute: 45, second: 32, offset: offset); + yield return dto.DateTime; + } + + { + // Kind == Utc + TimeSpan offset = new TimeSpan(hours: 8, minutes: 0, seconds: 0); + DateTimeOffset dto = new DateTimeOffset(year: 2017, month: 1, day: 13, hour: 3, minute: 45, second: 32, offset: offset); + yield return dto.UtcDateTime; + } + + { + // Kind == Local + TimeSpan offset = new TimeSpan(hours: 8, minutes: 0, seconds: 0); + DateTimeOffset dto = new DateTimeOffset(year: 2017, month: 1, day: 13, hour: 3, minute: 45, second: 32, offset: offset); + yield return dto.LocalDateTime; + } + + { + // Kind == Local + TimeSpan offset = new TimeSpan(hours: -9, minutes: 0, seconds: 0); + DateTimeOffset dto = new DateTimeOffset(year: 2017, month: 1, day: 13, hour: 3, minute: 45, second: 32, offset: offset); + yield return dto.LocalDateTime; + } + } + } + + public static IEnumerable DateTimeOffsetTestData + { + get + { + yield return DateTimeOffset.MinValue; + yield return DateTimeOffset.MaxValue; + yield return new DateTimeOffset(year: 2017, month: 1, day: 13, hour: 3, minute: 45, second: 32, new TimeSpan(hours: 0, minutes: 30, seconds: 0)); + yield return new DateTimeOffset(year: 2017, month: 1, day: 13, hour: 3, minute: 45, second: 32, new TimeSpan(hours: 0, minutes: -30, seconds: 0)); + yield return new DateTimeOffset(year: 2017, month: 1, day: 13, hour: 3, minute: 45, second: 32, new TimeSpan(hours: 8, minutes: 0, seconds: 0)); + yield return new DateTimeOffset(year: 2017, month: 1, day: 13, hour: 3, minute: 45, second: 32, new TimeSpan(hours: -8, minutes: 0, seconds: 0)); + yield return new DateTimeOffset(year: 2017, month: 12, day: 31, hour: 23, minute: 59, second: 58, new TimeSpan(hours: 14, minutes: 0, seconds: 0)); + yield return new DateTimeOffset(year: 2017, month: 12, day: 31, hour: 23, minute: 59, second: 58, new TimeSpan(hours: -14, minutes: 0, seconds: 0)); + + foreach (PseudoDateTime pseudoDateTime in PseudoDateTimeTestData) + { + if (pseudoDateTime.ExpectSuccess) + { + TimeSpan offset = new TimeSpan(hours: pseudoDateTime.OffsetHours, minutes: pseudoDateTime.OffsetMinutes, seconds: 0); + if (pseudoDateTime.OffsetNegative) + { + offset = -offset; + } + DateTimeOffset dto = new DateTimeOffset(year: pseudoDateTime.Year, month: pseudoDateTime.Month, day: pseudoDateTime.Day, hour: pseudoDateTime.Hour, minute: pseudoDateTime.Minute, second: pseudoDateTime.Second, offset: offset); + if (pseudoDateTime.Fraction != 0) + { + dto += new TimeSpan(ticks: pseudoDateTime.Fraction); + } + yield return dto; + } + } + } + } + + public static IEnumerable TimeSpanTestData + { + get + { + yield return TimeSpan.MinValue; + yield return TimeSpan.MaxValue; + yield return new TimeSpan(ticks: 0); + yield return new TimeSpan(ticks: 1); + yield return new TimeSpan(ticks: -1); + yield return new TimeSpan(ticks: 12345L); + yield return new TimeSpan(ticks: -12345L); + yield return new TimeSpan(days: 4, hours: 9, minutes: 8, seconds: 6, milliseconds: 0); + yield return new TimeSpan(days: -4, hours: 9, minutes: 8, seconds: 6, milliseconds: 0); + yield return new TimeSpan(days: 4, hours: 9, minutes: 8, seconds: 6, milliseconds: 5); + yield return new TimeSpan(days: -4, hours: 9, minutes: 8, seconds: 6, milliseconds: 5); + yield return new TimeSpan(days: 54, hours: 10, minutes: 11, seconds: 12, milliseconds: 13); + yield return new TimeSpan(days: -54, hours: 10, minutes: 11, seconds: 12, milliseconds: 13); + yield return new TimeSpan(days: 54, hours: 10, minutes: 11, seconds: 12, milliseconds: 999); + } + } + + public static IEnumerable NumberTestData + { + get + { + yield return ""; + yield return "+"; + yield return "-"; + yield return "0"; + yield return "+0"; + yield return "-0"; + yield return "0.0"; + yield return "-0.0"; + yield return "123.45"; + yield return "+123.45"; + yield return "-123.45"; + yield return "++123.45"; + yield return "--123.45"; + + yield return "5."; + yield return ".6"; + yield return "5."; + yield return "."; + + yield return "000000123.45"; + yield return "0.000045"; + yield return "000000123.000045"; + + yield return decimal.MinValue.ToString("G"); + yield return decimal.MaxValue.ToString("G"); + + yield return float.MinValue.ToString("G9"); + yield return float.MaxValue.ToString("G9"); + yield return float.Epsilon.ToString("G9"); + + yield return double.MinValue.ToString("G17"); + yield return double.MaxValue.ToString("G17"); + yield return double.Epsilon.ToString("G9"); + + yield return "1e"; + yield return "1e+"; + yield return "1e-"; + + yield return "1e10"; + yield return "1e+10"; + yield return "1e-10"; + + yield return "1E10"; + yield return "1E+10"; + yield return "1E-10"; + + yield return "1e+9"; + yield return "1e-9"; + yield return "1e+9"; + + yield return "1e+90"; + yield return "1e-90"; + yield return "1e+90"; + + yield return "1e+400"; + yield return "1e-400"; + yield return "1e+400"; + + yield return "-1e+400"; + yield return "-1e-400"; + yield return "-1e+400"; + + yield return "1e+/"; + yield return "1e/"; + yield return "1e-/"; + + yield return "1e+:"; + yield return "1e:"; + yield return "1e-:"; + + yield return "1e"; + yield return "1e/"; + yield return "1e:"; + yield return "0.5555555555555555555555555555555555555555555555555"; + + yield return "0.66666666666666666666666666665"; + yield return "0.6666666666666666666666666666500000000000000000000000000000000000000000000000000000000000000"; + yield return "0.6666666666666666666666666666500000000000000000000"; + + yield return "0.6666666666666666666666666666666666666666666666665"; + yield return "0.9999999999999999999999999999999999999999999999999"; + + // Crazy case that's expected to yield "Decimal.MaxValue / 10" + // ( = 7922816251426433759354395034m (= High = 0x19999999, Mid = 0x99999999, Low = 0x9999999A)) + // and does thanks to a special overflow code path inside the Number->Decimal converter. + yield return "0.79228162514264337593543950335" + "5" + "E28"; + + // Exercise post-rounding overflow check. + yield return "0.79228162514264337593543950335" + "5" + "E29"; + + // Excercise the 20-digit lookahead inside the rounding logic inside the Number->Decimal converter. + yield return "0.222222222222222222222222222255000000000000000000000000000000000000"; + + // Code coverage for MutableDecimal.DecAdd() + yield return "4611686018427387903.752"; + + // Code coverage: "round X where {Epsilon > X >= 2.470328229206232730000000E-324} up to Epsilon" + yield return "2.470328229206232730000000E-324"; + + // Code coverage: underflow + yield return "2.470328229206232730000000E-325"; + + yield return "3.402823E+38"; //Single.MaxValue + yield return "3.402824E+38"; //Just over Single.MaxValue + yield return "-3.402823E+38"; //Single.MinValue + yield return "-3.402824E+38"; //Just under Single.MinValue + + yield return "1.79769313486232E+308"; //Double.MaxValue + yield return "1.79769313486233E+308"; //Just over Double.MaxValue + yield return "-1.79769313486232E+308"; //Double.MinValue + yield return "-1.79769313486233E+308"; //Just under Double.MinValue + + // Ensures that the NumberBuffer capacity is consistent with Desktop's. + yield return ".2222222222222222222222222222500000000000000000001"; + } + } + + public static IEnumerable PseudoDateTimeTestData + { + get + { + foreach (int year in new int[] { 2000, 2001, 2002, 2003, 2004, 2010, 2012, 2013, 2014, 2, 9999 }) + { + for (int month = 1; month <= 12; month++) + { + int daysInMonth = DateTime.DaysInMonth(year: year, month: month); + foreach (int day in new int[] { 1, 9, 10, daysInMonth }) + { + yield return new PseudoDateTime(year: year, month: month, day: day, hour: 3, minute: 15, second: 45, expectSuccess: true); + } + + yield return new PseudoDateTime(year: year, month: month, day: (daysInMonth + 1), hour: 23, minute: 15, second: 45, expectSuccess: false); + } + } + + // Test data at the edge of the valid ranges. + yield return new PseudoDateTime(year: 1, month: 1, day: 1, hour: 14, minute: 0, second: 0, expectSuccess: true); + yield return new PseudoDateTime(year: 9999, month: 12, day: 31, hour: 9, minute: 0, second: 0, expectSuccess: true); + yield return new PseudoDateTime(year: 2017, month: 1, day: 1, hour: 14, minute: 0, second: 0, expectSuccess: true); + yield return new PseudoDateTime(year: 2017, month: 12, day: 1, hour: 14, minute: 0, second: 0, expectSuccess: true); + // Day range is month/year dependent. Was already covered above. + yield return new PseudoDateTime(year: 2017, month: 1, day: 1, hour: 14, minute: 0, second: 0, expectSuccess: true); + yield return new PseudoDateTime(year: 2017, month: 1, day: 1, hour: 0, minute: 0, second: 0, expectSuccess: true); + yield return new PseudoDateTime(year: 2017, month: 1, day: 1, hour: 23, minute: 0, second: 0, expectSuccess: true); + yield return new PseudoDateTime(year: 2017, month: 1, day: 1, hour: 0, minute: 59, second: 0, expectSuccess: true); + yield return new PseudoDateTime(year: 2017, month: 1, day: 1, hour: 0, minute: 0, second: 59, expectSuccess: true); + yield return new PseudoDateTime(year: 2017, month: 1, day: 1, hour: 0, minute: 0, second: 0, fraction: 9999999, offsetNegative: false, offsetHours: 0, offsetMinutes: 0, expectSuccess: true); + yield return new PseudoDateTime(year: 2017, month: 1, day: 1, hour: 0, minute: 0, second: 0, fraction: 0, offsetNegative: false, offsetHours: 13, offsetMinutes: 59, expectSuccess: true); + yield return new PseudoDateTime(year: 2017, month: 1, day: 1, hour: 0, minute: 0, second: 0, fraction: 0, offsetNegative: false, offsetHours: 14, offsetMinutes: 0, expectSuccess: true); + yield return new PseudoDateTime(year: 2017, month: 1, day: 1, hour: 0, minute: 0, second: 0, fraction: 0, offsetNegative: true, offsetHours: 13, offsetMinutes: 59, expectSuccess: true); + yield return new PseudoDateTime(year: 2017, month: 1, day: 1, hour: 0, minute: 0, second: 0, fraction: 0, offsetNegative: true, offsetHours: 14, offsetMinutes: 0, expectSuccess: true); + + // Test data outside the valid ranges. + yield return new PseudoDateTime(year: 0, month: 1, day: 1, hour: 24, minute: 0, second: 0, expectSuccess: false); + yield return new PseudoDateTime(year: 2017, month: 0, day: 1, hour: 24, minute: 0, second: 0, expectSuccess: false); + yield return new PseudoDateTime(year: 2017, month: 13, day: 1, hour: 24, minute: 0, second: 0, expectSuccess: false); + // Day range is month/year dependent. Was already covered above. + yield return new PseudoDateTime(year: 2017, month: 1, day: 1, hour: 60, minute: 0, second: 0, expectSuccess: false); + yield return new PseudoDateTime(year: 2017, month: 1, day: 1, hour: 0, minute: 60, second: 0, expectSuccess: false); + yield return new PseudoDateTime(year: 2017, month: 1, day: 1, hour: 0, minute: 0, second: 60, expectSuccess: false); + yield return new PseudoDateTime(year: 2017, month: 1, day: 1, hour: 0, minute: 0, second: 0, fraction: 0, offsetNegative: true, offsetHours: 0, offsetMinutes: 60, expectSuccess: false); + yield return new PseudoDateTime(year: 2017, month: 1, day: 1, hour: 0, minute: 0, second: 0, fraction: 0, offsetNegative: false, offsetHours: 14, offsetMinutes: 1, expectSuccess: false); + yield return new PseudoDateTime(year: 2017, month: 1, day: 1, hour: 0, minute: 0, second: 0, fraction: 0, offsetNegative: false, offsetHours: 15, offsetMinutes: 0, expectSuccess: false); + yield return new PseudoDateTime(year: 2017, month: 1, day: 1, hour: 0, minute: 0, second: 0, fraction: 0, offsetNegative: true, offsetHours: 14, offsetMinutes: 1, expectSuccess: false); + yield return new PseudoDateTime(year: 2017, month: 1, day: 1, hour: 0, minute: 0, second: 0, fraction: 0, offsetNegative: true, offsetHours: 15, offsetMinutes: 0, expectSuccess: false); + + // Past the end of time. + yield return new PseudoDateTime(year: 9999, month: 12, day: 31, hour: 23, minute: 59, second: 59, fraction: 9999999, offsetNegative: true, offsetHours: 0, offsetMinutes: 1, expectSuccess: false); + yield return new PseudoDateTime(year: 1, month: 1, day: 1, hour: 0, minute: 0, second: 0, fraction: 0, offsetNegative: false, offsetHours: 0, offsetMinutes: 1, expectSuccess: false); + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ParsersAndFormatters/TestUtils.cs b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/TestUtils.cs new file mode 100644 index 0000000000..f02f6fefdf --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ParsersAndFormatters/TestUtils.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. + +using System.Text; +using System.Numerics; +using System.Globalization; + +namespace System.Buffers.Text.Tests +{ + internal static class TestUtils + { + public static MutableDecimal ToMutableDecimal(this decimal d) + { + int[] bits = decimal.GetBits(d); + return new MutableDecimal() { High = (uint)bits[0], Low = (uint)bits[1], Mid = (uint)bits[2], Flags = (uint)bits[3] }; + } + + public static decimal ToDecimal(this MutableDecimal md) + { + return new decimal(new int[] { (int)md.High, (int)md.Low, (int)md.Mid, (int)md.Flags }); + } + + // + // Generate test output that's unmabiguous and convenient for investigations. + // + public static string DisplayString(this object value) + { + if (value is StandardFormat format) + { + if (format.Precision == StandardFormat.NoPrecision) + return format.Symbol.ToString(); + else + return format.Symbol + format.Precision.ToString(); + } + else if (value is double dbl) + { + return dbl.ToString("G17", CultureInfo.InvariantCulture); + } + else if (value is float flt) + { + return flt.ToString("G9", CultureInfo.InvariantCulture); + } + else if (value is DateTime dateTime) + { + return "[" + dateTime.ToString("O", CultureInfo.InvariantCulture) + ", Kind=" + dateTime.Kind + "]"; + } + else if (value is DateTimeOffset dateTimeOffset) + { + return "[" + dateTimeOffset.ToString("O", CultureInfo.InvariantCulture) + "]"; + } + else if (value is decimal dec) + { + MutableDecimal mutableDecimal = dec.ToMutableDecimal(); + bool isNegative = mutableDecimal.IsNegative; + int scale = mutableDecimal.Scale; + + if (isNegative) + { + dec = -dec; + } + + string sign = isNegative ? "-" : "+"; + + return "[" + sign + dec.ToString("G") + ", scale=" + scale + "]"; + } + else if (value is TimeSpan timeSpan) + { + return timeSpan.ToString("G", CultureInfo.InvariantCulture); + } + + return value.ToString(); + } + + public static BigInteger GetMinValue() + { + if (typeof(T) == typeof(sbyte)) + return sbyte.MinValue; + + if (typeof(T) == typeof(byte)) + return byte.MinValue; + + if (typeof(T) == typeof(short)) + return short.MinValue; + + if (typeof(T) == typeof(ushort)) + return ushort.MinValue; + + if (typeof(T) == typeof(int)) + return int.MinValue; + + if (typeof(T) == typeof(uint)) + return uint.MinValue; + + if (typeof(T) == typeof(long)) + return long.MinValue; + + if (typeof(T) == typeof(ulong)) + return ulong.MinValue; + + if (typeof(T) == typeof(decimal)) + return new BigInteger(decimal.MinValue); + + throw new Exception("Unsupported type: " + typeof(T)); + } + + public static BigInteger GetMaxValue() + { + if (typeof(T) == typeof(sbyte)) + return sbyte.MaxValue; + + if (typeof(T) == typeof(byte)) + return byte.MaxValue; + + if (typeof(T) == typeof(short)) + return short.MaxValue; + + if (typeof(T) == typeof(ushort)) + return ushort.MaxValue; + + if (typeof(T) == typeof(int)) + return int.MaxValue; + + if (typeof(T) == typeof(uint)) + return uint.MaxValue; + + if (typeof(T) == typeof(long)) + return long.MaxValue; + + if (typeof(T) == typeof(ulong)) + return ulong.MaxValue; + + if (typeof(T) == typeof(decimal)) + return new BigInteger(decimal.MaxValue); + + throw new Exception("Unsupported type: " + typeof(T)); + } + + public static BigInteger ToBigInteger(this T value) + { + if (typeof(T) == typeof(sbyte)) + return new BigInteger((sbyte)(object)value); + + if (typeof(T) == typeof(byte)) + return new BigInteger((byte)(object)value); + + if (typeof(T) == typeof(short)) + return new BigInteger((short)(object)value); + + if (typeof(T) == typeof(ushort)) + return new BigInteger((ushort)(object)value); + + if (typeof(T) == typeof(int)) + return new BigInteger((int)(object)value); + + if (typeof(T) == typeof(uint)) + return new BigInteger((uint)(object)value); + + if (typeof(T) == typeof(long)) + return new BigInteger((long)(object)value); + + if (typeof(T) == typeof(ulong)) + return new BigInteger((ulong)(object)value); + + if (typeof(T) == typeof(decimal)) + return new BigInteger((decimal)(object)value); + + throw new Exception("Unsupported type: " + typeof(T)); + } + + public static ReadOnlySpan ToUtf8Span(this string s) => Encoding.UTF8.GetBytes(s); + public static string ToUtf16String(this Span span) => Encoding.UTF8.GetString(span.ToArray()); + } +} diff --git a/external/corefx/src/System.Memory/tests/Performance/Perf.Base64EncodeDecode.cs b/external/corefx/src/System.Memory/tests/Performance/Perf.Base64EncodeDecode.cs new file mode 100644 index 0000000000..b5375e08b8 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Performance/Perf.Base64EncodeDecode.cs @@ -0,0 +1,263 @@ +// 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 Microsoft.Xunit.Performance; +using Xunit; + +namespace System.Buffers.Text.Tests +{ + public class Base64EncodeDecodeTests + { + private const int InnerCount = 1000; + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + [InlineData(1000 * 1000)] + private static void Base64Encode(int numberOfBytes) + { + Span source = new byte[numberOfBytes]; + Base64TestHelper.InitalizeBytes(source); + Span destination = new byte[Base64.GetMaxEncodedToUtf8Length(numberOfBytes)]; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + Base64.EncodeToUtf8(source, destination, out int consumed, out int written); + } + } + + Span backToSource = new byte[numberOfBytes]; + Base64.DecodeFromUtf8(destination, backToSource, out _, out _); + Assert.True(source.SequenceEqual(backToSource)); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + [InlineData(1000 * 1000)] + private static void Base64EncodeDestinationTooSmall(int numberOfBytes) + { + Span source = new byte[numberOfBytes]; + Base64TestHelper.InitalizeBytes(source); + Span destination = new byte[Base64.GetMaxEncodedToUtf8Length(numberOfBytes) - 1]; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + Base64.EncodeToUtf8(source, destination, out int consumed, out int written); + } + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + [InlineData(1000 * 1000)] + private static void Base64EncodeBaseline(int numberOfBytes) + { + var source = new byte[numberOfBytes]; + Base64TestHelper.InitalizeBytes(source.AsSpan()); + var destination = new char[Base64.GetMaxEncodedToUtf8Length(numberOfBytes)]; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + Convert.ToBase64CharArray(source, 0, source.Length, destination, 0); + } + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + [InlineData(1000 * 1000)] + private static void Base64Decode(int numberOfBytes) + { + Span source = new byte[numberOfBytes]; + Base64TestHelper.InitalizeBytes(source); + Span encoded = new byte[Base64.GetMaxEncodedToUtf8Length(numberOfBytes)]; + Base64.EncodeToUtf8(source, encoded, out _, out _); + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + Base64.DecodeFromUtf8(encoded, source, out int bytesConsumed, out int bytesWritten); + } + } + + Span backToEncoded = encoded.ToArray(); + Base64.EncodeToUtf8(source, encoded, out _, out _); + Assert.True(backToEncoded.SequenceEqual(encoded)); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + [InlineData(1000 * 1000)] + private static void Base64DecodeDetinationTooSmall(int numberOfBytes) + { + Span source = new byte[numberOfBytes]; + Base64TestHelper.InitalizeBytes(source); + Span encoded = new byte[Base64.GetMaxEncodedToUtf8Length(numberOfBytes)]; + Base64.EncodeToUtf8(source, encoded, out _, out _); + + source = source.Slice(0, source.Length - 1); + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + Base64.DecodeFromUtf8(encoded, source, out int bytesConsumed, out int bytesWritten); + } + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + [InlineData(1000 * 1000)] + private static void Base64DecodeBaseline(int numberOfBytes) + { + Span source = new byte[numberOfBytes]; + Base64TestHelper.InitalizeBytes(source); + ReadOnlySpan encoded = Convert.ToBase64String(source.ToArray()).ToCharArray(); + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + Convert.TryFromBase64Chars(encoded, source, out int bytesWritten); + } + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + [InlineData(1000 * 1000)] + private static void Base64EncodeInPlace(int numberOfBytes) + { + Span source = new byte[numberOfBytes]; + Base64TestHelper.InitalizeBytes(source); + int length = Base64.GetMaxEncodedToUtf8Length(numberOfBytes); + Span decodedSpan = new byte[length]; + source.CopyTo(decodedSpan); + Span backupSpan = decodedSpan.ToArray(); + + int bytesWritten = 0; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + backupSpan.CopyTo(decodedSpan); + Base64.EncodeToUtf8InPlace(decodedSpan, numberOfBytes, out bytesWritten); + } + } + } + + Span backToSource = new byte[numberOfBytes]; + Base64.DecodeFromUtf8(decodedSpan, backToSource, out _, out _); + Assert.True(backupSpan.Slice(0, numberOfBytes).SequenceEqual(backToSource)); + } + + [Benchmark] + [InlineData(1000 * 1000)] + private static void Base64EncodeInPlaceOnce(int numberOfBytes) + { + Span source = new byte[numberOfBytes]; + Base64TestHelper.InitalizeBytes(source); + int length = Base64.GetMaxEncodedToUtf8Length(numberOfBytes); + Span decodedSpan = new byte[length]; + source.CopyTo(decodedSpan); + Span backupSpan = decodedSpan.ToArray(); + + int bytesWritten = 0; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + backupSpan.CopyTo(decodedSpan); + using (iteration.StartMeasurement()) + { + Base64.EncodeToUtf8InPlace(decodedSpan, numberOfBytes, out bytesWritten); + } + } + + Span backToSource = new byte[numberOfBytes]; + Base64.DecodeFromUtf8(decodedSpan, backToSource, out _, out _); + Assert.True(backupSpan.Slice(0, numberOfBytes).SequenceEqual(backToSource)); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + [InlineData(1000 * 1000)] + private static void Base64DecodeInPlace(int numberOfBytes) + { + Span source = new byte[numberOfBytes]; + Base64TestHelper.InitalizeBytes(source); + int length = Base64.GetMaxEncodedToUtf8Length(numberOfBytes); + Span encodedSpan = new byte[length]; + Base64.EncodeToUtf8(source, encodedSpan, out _, out _); + + Span backupSpan = encodedSpan.ToArray(); + + int bytesWritten = 0; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + backupSpan.CopyTo(encodedSpan); + Base64.DecodeFromUtf8InPlace(encodedSpan, out bytesWritten); + } + } + } + + Assert.True(source.SequenceEqual(encodedSpan.Slice(0, bytesWritten))); + } + + [Benchmark] + [InlineData(1000 * 1000)] + private static void Base64DecodeInPlaceOnce(int numberOfBytes) + { + Span source = new byte[numberOfBytes]; + Base64TestHelper.InitalizeBytes(source); + int length = Base64.GetMaxEncodedToUtf8Length(numberOfBytes); + Span encodedSpan = new byte[length]; + + int bytesWritten = 0; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + Base64.EncodeToUtf8(source, encodedSpan, out _, out _); + using (iteration.StartMeasurement()) + { + Base64.DecodeFromUtf8InPlace(encodedSpan, out bytesWritten); + } + } + + Assert.True(source.SequenceEqual(encodedSpan.Slice(0, bytesWritten))); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Performance/Perf.MemorySlice.cs b/external/corefx/src/System.Memory/tests/Performance/Perf.MemorySlice.cs new file mode 100644 index 0000000000..27cecdef49 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Performance/Perf.MemorySlice.cs @@ -0,0 +1,164 @@ +// 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.Runtime.InteropServices; +using Microsoft.Xunit.Performance; +using Xunit; + +namespace System.Memory.Tests +{ + public class MemorySlice + { + private const int InnerCount = 1000; + volatile static int s_volatileInt = 0; + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1000)] + [InlineData(1000 * 1000)] + private static void Memory_Byte_SliceThenGetSpan(int numberOfBytes) + { + Memory memory = new byte[numberOfBytes]; + int numberOfSlices = numberOfBytes / 10 - 1; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + int localInt = 0; + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + for (int j = 0; j < numberOfSlices; j++) + { + Span span = memory.Slice(10, 1).Span; + localInt ^= span[0]; + } + } + } + s_volatileInt = localInt; + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1000)] + [InlineData(1000 * 1000)] + private static void Memory_Byte_GetSpanThenSlice(int numberOfBytes) + { + Memory memory = new byte[numberOfBytes]; + int numberOfSlices = numberOfBytes / 10 - 1; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + int localInt = 0; + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + for (int j = 0; j < numberOfSlices; j++) + { + Span span = memory.Span.Slice(10, 1); + localInt ^= span[0]; + } + } + } + s_volatileInt = localInt; + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1000)] + [InlineData(1000 * 1000)] + private static void ReadOnlyMemory_Byte_GetSpanThenSlice(int numberOfBytes) + { + ReadOnlyMemory memory = new byte[numberOfBytes]; + int numberOfSlices = numberOfBytes / 10 - 1; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + int localInt = 0; + long iters = Benchmark.InnerIterationCount; + using (iteration.StartMeasurement()) + { + for (int i = 0; i < iters; i++) + { + for (int j = 0; j < numberOfSlices; j++) + { + ReadOnlySpan span = memory.Span.Slice(10, 1); + localInt ^= span[0]; + } + } + } + s_volatileInt = localInt; + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1000)] + [InlineData(1000 * 1000)] + private static void ReadOnlyMemory_Char_GetSpanThenSlice(int numberOfChars) + { + ReadOnlyMemory memory = new char[numberOfChars]; + int numberOfSlices = numberOfChars / 10 - 1; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + int localInt = 0; + long iters = Benchmark.InnerIterationCount; + using (iteration.StartMeasurement()) + { + for (int i = 0; i < iters; i++) + { + for (int j = 0; j < numberOfSlices; j++) + { + ReadOnlySpan span = memory.Span.Slice(10, 1); + localInt ^= span[0]; + } + } + } + s_volatileInt = localInt; + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + public static void ReadOnlyMemory_Byte_TryGetArray() + { + ReadOnlyMemory memory = new byte[1]; + ArraySegment result; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long iters = Benchmark.InnerIterationCount; + using (iteration.StartMeasurement()) + { + for (int i = 0; i < iters; i++) + { + MemoryMarshal.TryGetArray(memory, out result); + } + } + } + + s_volatileInt = result.Count; + } + + [Benchmark(InnerIterationCount = InnerCount)] + public static void ReadOnlyMemory_Char_TryGetArray() + { + ReadOnlyMemory memory = new char[1]; + ArraySegment result; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long iters = Benchmark.InnerIterationCount; + using (iteration.StartMeasurement()) + { + for (int i = 0; i < iters; i++) + { + MemoryMarshal.TryGetArray(memory, out result); + } + } + } + + s_volatileInt = result.Count; + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Performance/Perf.Span.BinaryReadAndWrite.cs b/external/corefx/src/System.Memory/tests/Performance/Perf.Span.BinaryReadAndWrite.cs index dbcdc998fb..1e0e9f278c 100644 --- a/external/corefx/src/System.Memory/tests/Performance/Perf.Span.BinaryReadAndWrite.cs +++ b/external/corefx/src/System.Memory/tests/Performance/Perf.Span.BinaryReadAndWrite.cs @@ -7,7 +7,6 @@ using Xunit; using System.Net; using static System.Buffers.Binary.BinaryPrimitives; -using static System.TestHelpers; namespace System.Buffers.Binary.Tests { @@ -21,7 +20,7 @@ namespace System.Buffers.Binary.Tests Span spanBE = TestHelpers.GetSpanBE(); var readStruct = new TestHelpers.TestStructExplicit(); - foreach (var iteration in Benchmark.Iterations) + foreach (BenchmarkIteration iteration in Benchmark.Iterations) { using (iteration.StartMeasurement()) { @@ -47,7 +46,7 @@ namespace System.Buffers.Binary.Tests } } - Assert.Equal(TestHelpers.testExplicitStruct, readStruct); + Assert.Equal(TestHelpers.s_testExplicitStruct, readStruct); } [Benchmark(InnerIterationCount = InnerCount)] @@ -56,7 +55,7 @@ namespace System.Buffers.Binary.Tests Span spanLE = TestHelpers.GetSpanLE(); var readStruct = new TestHelpers.TestStructExplicit(); - foreach (var iteration in Benchmark.Iterations) + foreach (BenchmarkIteration iteration in Benchmark.Iterations) { using (iteration.StartMeasurement()) { @@ -82,7 +81,7 @@ namespace System.Buffers.Binary.Tests } } - Assert.Equal(TestHelpers.testExplicitStruct, readStruct); + Assert.Equal(TestHelpers.s_testExplicitStruct, readStruct); } [Benchmark(InnerIterationCount = InnerCount)] @@ -91,7 +90,7 @@ namespace System.Buffers.Binary.Tests Span spanBE = TestHelpers.GetSpanBE(); var readStruct = new TestHelpers.TestStructExplicit(); - foreach (var iteration in Benchmark.Iterations) + foreach (BenchmarkIteration iteration in Benchmark.Iterations) { using (iteration.StartMeasurement()) { @@ -116,7 +115,7 @@ namespace System.Buffers.Binary.Tests } } - Assert.Equal(TestHelpers.testExplicitStruct, readStruct); + Assert.Equal(TestHelpers.s_testExplicitStruct, readStruct); } [Benchmark(InnerIterationCount = InnerCount)] @@ -125,7 +124,7 @@ namespace System.Buffers.Binary.Tests Span spanLE = TestHelpers.GetSpanLE(); var readStruct = new TestHelpers.TestStructExplicit(); - foreach (var iteration in Benchmark.Iterations) + foreach (BenchmarkIteration iteration in Benchmark.Iterations) { using (iteration.StartMeasurement()) { @@ -150,7 +149,7 @@ namespace System.Buffers.Binary.Tests } } - Assert.Equal(TestHelpers.testExplicitStruct, readStruct); + Assert.Equal(TestHelpers.s_testExplicitStruct, readStruct); } [Benchmark(InnerIterationCount = InnerCount)] @@ -160,7 +159,7 @@ namespace System.Buffers.Binary.Tests byte[] arrayLE = spanLE.ToArray(); var readStruct = new TestHelpers.TestStructExplicit(); - foreach (var iteration in Benchmark.Iterations) + foreach (BenchmarkIteration iteration in Benchmark.Iterations) { using (iteration.StartMeasurement()) { @@ -185,7 +184,7 @@ namespace System.Buffers.Binary.Tests } } - Assert.Equal(TestHelpers.testExplicitStruct, readStruct); + Assert.Equal(TestHelpers.s_testExplicitStruct, readStruct); } [Benchmark(InnerIterationCount = InnerCount)] @@ -195,7 +194,7 @@ namespace System.Buffers.Binary.Tests byte[] arrayBE = spanBE.ToArray(); var readStruct = new TestHelpers.TestStructExplicit(); - foreach (var iteration in Benchmark.Iterations) + foreach (BenchmarkIteration iteration in Benchmark.Iterations) { using (iteration.StartMeasurement()) { @@ -235,15 +234,15 @@ namespace System.Buffers.Binary.Tests } } - Assert.Equal(TestHelpers.testExplicitStruct, readStruct); + Assert.Equal(TestHelpers.s_testExplicitStruct, readStruct); } [Benchmark(InnerIterationCount = InnerCount)] private static void MeasureReverseEndianness() { var myArray = new int[1000]; - - foreach (var iteration in Benchmark.Iterations) + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) { using (iteration.StartMeasurement()) { @@ -263,7 +262,7 @@ namespace System.Buffers.Binary.Tests { var myArray = new int[1000]; - foreach (var iteration in Benchmark.Iterations) + foreach (BenchmarkIteration iteration in Benchmark.Iterations) { using (iteration.StartMeasurement()) { diff --git a/external/corefx/src/System.Memory/tests/Performance/Perf.Span.BinarySearch.cs b/external/corefx/src/System.Memory/tests/Performance/Perf.Span.BinarySearch.cs new file mode 100644 index 0000000000..621c306a66 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Performance/Perf.Span.BinarySearch.cs @@ -0,0 +1,286 @@ +// 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 Microsoft.Xunit.Performance; +using Xunit; + +namespace System.Memory.Tests +{ + public class Perf_Span_BinarySearch + { + private const int InnerCount = 100000; + private const string NumberFormat = "D9"; + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void ArrayBinarySearch_Int_FirstIndex(int size) + { + BenchmarkAndAssertArray(size, 0, 0); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void ArrayBinarySearch_Int_MiddleIndex(int size) + { + BenchmarkAndAssertArray(size, (size - 1) / 2, (size - 1) / 2); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void ArrayBinarySearch_Int_LastIndex(int size) + { + BenchmarkAndAssertArray(size, size - 1, size - 1); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void ArrayBinarySearch_Int_NotFoundBefore(int size) + { + BenchmarkAndAssertArray(size, -1, -1); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void ArrayBinarySearch_Int_NotFoundAfter(int size) + { + BenchmarkAndAssertArray(size, size, ~size); + } + + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void ArrayBinarySearch_String_FirstIndex(int size) + { + BenchmarkAndAssertArray(size, 0.ToString(NumberFormat), 0); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void ArrayBinarySearch_String_MiddleIndex(int size) + { + BenchmarkAndAssertArray(size, ((size - 1) / 2).ToString(NumberFormat), (size - 1) / 2); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void ArrayBinarySearch_String_LastIndex(int size) + { + BenchmarkAndAssertArray(size, (size - 1).ToString(NumberFormat), size - 1); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void ArrayBinarySearch_String_NotFoundBefore(int size) + { + // "/" is just before zero in character table + BenchmarkAndAssertArray(size, "/", -1); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void ArrayBinarySearch_String_NotFoundAfter(int size) + { + BenchmarkAndAssertArray(size, (size).ToString(NumberFormat), ~size); + } + + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanBinarySearch_Int_FirstIndex(int size) + { + BenchmarkAndAssertSpan(size, 0, 0); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanBinarySearch_Int_MiddleIndex(int size) + { + BenchmarkAndAssertSpan(size, (size - 1) / 2, (size - 1) / 2); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanBinarySearch_Int_LastIndex(int size) + { + BenchmarkAndAssertSpan(size, size - 1, size - 1); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanBinarySearch_Int_NotFoundBefore(int size) + { + BenchmarkAndAssertSpan(size, -1, -1); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanBinarySearch_Int_NotFoundAfter(int size) + { + BenchmarkAndAssertSpan(size, size, ~size); + } + + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanBinarySearch_String_FirstIndex(int size) + { + BenchmarkAndAssertSpan(size, 0.ToString(NumberFormat), 0); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanBinarySearch_String_MiddleIndex(int size) + { + BenchmarkAndAssertSpan(size, ((size - 1) / 2).ToString(NumberFormat), (size - 1) / 2); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanBinarySearch_String_LastIndex(int size) + { + BenchmarkAndAssertSpan(size, (size - 1).ToString(NumberFormat), size - 1); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanBinarySearch_String_NotFoundBefore(int size) + { + // "/" is just before zero in character table + BenchmarkAndAssertSpan(size, "/", -1); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanBinarySearch_String_NotFoundAfter(int size) + { + BenchmarkAndAssertSpan(size, (size).ToString(NumberFormat), ~size); + } + + private static void BenchmarkAndAssertArray(int size, int value, int expectedIndex) + { + BenchmarkAndAssertArray(size, i => i, value, expectedIndex); + } + + private static void BenchmarkAndAssertArray(int size, string value, int expectedIndex) + { + BenchmarkAndAssertArray(size, i => i.ToString(NumberFormat), value, expectedIndex); + } + + private static void BenchmarkAndAssertArray(int size, Func toValue, T value, int expectedIndex) + where T : IComparable + { + var array = new T[size]; + for (int i = 0; i < array.Length; i++) + { + array[i] = toValue(i); + } + + int index = 0; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + index |= Array.BinarySearch(array, value); + } + } + } + Assert.Equal(expectedIndex, index); + } + + private static void BenchmarkAndAssertSpan(int size, int value, int expectedIndex) + { + BenchmarkAndAssertSpan(size, i => i, value, expectedIndex); + } + + private static void BenchmarkAndAssertSpan(int size, string value, int expectedIndex) + { + BenchmarkAndAssertSpan(size, i => i.ToString(NumberFormat), value, expectedIndex); + } + + private static void BenchmarkAndAssertSpan(int size, Func toValue, T value, int expectedIndex) + where T : IComparable + { + Span span = new T[size]; + for (int i = 0; i < span.Length; i++) + { + span[i] = toValue(i); + } + + int index = 0; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + index |= span.BinarySearch(value); + } + } + } + Assert.Equal(expectedIndex, index); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Performance/Perf.Span.Clear.cs b/external/corefx/src/System.Memory/tests/Performance/Perf.Span.Clear.cs index a5611366cd..8557482041 100644 --- a/external/corefx/src/System.Memory/tests/Performance/Perf.Span.Clear.cs +++ b/external/corefx/src/System.Memory/tests/Performance/Perf.Span.Clear.cs @@ -37,7 +37,7 @@ namespace System.Memory.Tests { var a = new int[size]; var span = new Span(a); - foreach (var iteration in Benchmark.Iterations) + foreach (BenchmarkIteration iteration in Benchmark.Iterations) { using (iteration.StartMeasurement()) { diff --git a/external/corefx/src/System.Memory/tests/Performance/Perf.Span.Fill.cs b/external/corefx/src/System.Memory/tests/Performance/Perf.Span.Fill.cs index 4350549cbd..2344633e3b 100644 --- a/external/corefx/src/System.Memory/tests/Performance/Perf.Span.Fill.cs +++ b/external/corefx/src/System.Memory/tests/Performance/Perf.Span.Fill.cs @@ -37,7 +37,7 @@ namespace System.Memory.Tests { var a = new int[size]; var span = new Span(a); - foreach (var iteration in Benchmark.Iterations) + foreach (BenchmarkIteration iteration in Benchmark.Iterations) { using (iteration.StartMeasurement()) { diff --git a/external/corefx/src/System.Memory/tests/Performance/Perf.Span.IndexOf.cs b/external/corefx/src/System.Memory/tests/Performance/Perf.Span.IndexOf.cs index 28212e2828..dd678920f4 100644 --- a/external/corefx/src/System.Memory/tests/Performance/Perf.Span.IndexOf.cs +++ b/external/corefx/src/System.Memory/tests/Performance/Perf.Span.IndexOf.cs @@ -19,7 +19,7 @@ namespace System.Memory.Tests public void SpanIndexOfChar(int size) { Span charSpan = new char[size]; - charSpan[size/2] = '5'; + charSpan[size / 2] = '5'; int index = 0; foreach (BenchmarkIteration iteration in Benchmark.Iterations) @@ -32,9 +32,9 @@ namespace System.Memory.Tests } } } - Assert.Equal(size/2, index); + Assert.Equal(size / 2, index); } - + [Benchmark(InnerIterationCount = InnerCount)] [InlineData(1)] [InlineData(10)] @@ -43,7 +43,7 @@ namespace System.Memory.Tests public void SpanIndexOfCharAsBytes(int size) { Span charSpan = new char[size]; - charSpan[size/2] = '5'; + charSpan[size / 2] = '5'; Span byteSpan = charSpan.AsBytes(); int index = 0; @@ -53,13 +53,13 @@ namespace System.Memory.Tests { for (int i = 0; i < Benchmark.InnerIterationCount; i++) { - index |= byteSpan.IndexOf(53); // '5' = 53 + index |= byteSpan.IndexOf(53); // '5' = 53 } } } Assert.Equal(size > 1 ? size : 0, index); } - + [Benchmark(InnerIterationCount = InnerCount)] [InlineData(1)] [InlineData(10)] @@ -67,10 +67,10 @@ namespace System.Memory.Tests [InlineData(1000)] public void StringIndexOfChar(int size) { - string str = new string('0', size/2) + "5"; + string str = new string('0', size / 2) + "5"; if (size > 1) { - str += new string('0', size/2 - 1); + str += new string('0', size / 2 - 1); } int index = 0; @@ -84,7 +84,83 @@ namespace System.Memory.Tests } } } - Assert.Equal(size/2, index); + Assert.Equal(size / 2, index); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanLastIndexOfChar(int size) + { + Span charSpan = new char[size]; + charSpan[size / 2] = '5'; + + int index = 0; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + index |= charSpan.LastIndexOf('5'); + } + } + } + Assert.Equal(size / 2, index); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanLastIndexOfCharAsBytes(int size) + { + Span charSpan = new char[size]; + charSpan[size / 2] = '5'; + Span byteSpan = charSpan.AsBytes(); + + int index = 0; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + index |= byteSpan.LastIndexOf(53); // '5' = 53 + } + } + } + Assert.Equal(size > 1 ? size : 0, index); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void StringLastIndexOfChar(int size) + { + string str = new string('0', size / 2) + "5"; + if (size > 1) + { + str += new string('0', size / 2 - 1); + } + + int index = 0; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + index |= str.LastIndexOf('5'); + } + } + } + Assert.Equal(size / 2, index); } } } diff --git a/external/corefx/src/System.Memory/tests/Performance/Perf.Span.IndexOfAny.cs b/external/corefx/src/System.Memory/tests/Performance/Perf.Span.IndexOfAny.cs new file mode 100644 index 0000000000..26c5f5bf95 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Performance/Perf.Span.IndexOfAny.cs @@ -0,0 +1,291 @@ +// 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 Microsoft.Xunit.Performance; +using Xunit; + +namespace System.Memory.Tests +{ + public class Perf_Span_IndexOfAny + { + private const int InnerCount = 100000; + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanIndexOfAnyChar_Two(int size) + { + Span charSpan = new char[size]; + charSpan[size / 2] = '5'; + + int index = 0; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + index |= charSpan.IndexOfAny('5', 'a'); + } + } + } + Assert.Equal(size / 2, index); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanIndexOfAnyCharAsBytes_Two(int size) + { + Span charSpan = new char[size]; + charSpan[size / 2] = '5'; + Span byteSpan = charSpan.AsBytes(); + + int index = 0; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + index |= byteSpan.IndexOfAny(53, 54); // '5' = 53 + } + } + } + Assert.Equal(size > 1 ? size : 0, index); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanIndexOfAnyString_Two(int size) + { + string[] stringAray = new string[size]; + stringAray[size / 2] = "5"; + Span stringSpan = new Span(stringAray); + + int index = 0; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + index |= stringSpan.IndexOfAny("5", "a"); + } + } + } + Assert.Equal(size / 2, index); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanIndexOfAnyChar_Three(int size) + { + Span charSpan = new char[size]; + charSpan[size / 2] = '5'; + + int index = 0; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + index |= charSpan.IndexOfAny('5', 'a', 'b'); + } + } + } + Assert.Equal(size / 2, index); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanIndexOfAnyCharAsBytes_Three(int size) + { + Span charSpan = new char[size]; + charSpan[size / 2] = '5'; + Span byteSpan = charSpan.AsBytes(); + + int index = 0; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + index |= byteSpan.IndexOfAny(53, 54, 55); // '5' = 53 + } + } + } + Assert.Equal(size > 1 ? size : 0, index); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanIndexOfAnyString_Three(int size) + { + string[] stringAray = new string[size]; + stringAray[size / 2] = "5"; + Span stringSpan = new Span(stringAray); + + int index = 0; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + index |= stringSpan.IndexOfAny("5", "a", "b"); + } + } + } + Assert.Equal(size / 2, index); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanIndexOfAnyChar_Many(int size) + { + Span charSpan = new char[size]; + charSpan[size / 2] = '5'; + ReadOnlySpan values = new ReadOnlySpan(new char[] { '5', 'a', 'b', 'c' }); + + int index = 0; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + index |= charSpan.IndexOfAny(values); + } + } + } + Assert.Equal(size / 2, index); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanIndexOfAnyCharAsBytes_Many(int size) + { + Span charSpan = new char[size]; + charSpan[size / 2] = '5'; + Span byteSpan = charSpan.AsBytes(); + ReadOnlySpan values = new ReadOnlySpan(new byte[] { 53, 54, 55, 56 }); // '5' = 53 + + int index = 0; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + index |= byteSpan.IndexOfAny(values); + } + } + } + Assert.Equal(size > 1 ? size : 0, index); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanIndexOfAnyCharAsBytes_NoSearchValue_Many(int size) + { + Span charSpan = new char[size]; + charSpan[size / 2] = '5'; + Span byteSpan = charSpan.AsBytes(); + ReadOnlySpan values = new ReadOnlySpan(new byte[] { 54, 55, 56, 57 }); // '5' = 53 + + int index = 0; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + index |= byteSpan.IndexOfAny(values); + } + } + } + Assert.Equal(-1, index); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanIndexOfAnyCharAsBytes_ContainsLastSearchValue_Many(int size) + { + Span charSpan = new char[size]; + charSpan[size / 2] = '5'; + Span byteSpan = charSpan.AsBytes(); + ReadOnlySpan values = new ReadOnlySpan(new byte[] { 54, 55, 56, 53 }); // '5' = 53 + + int index = 0; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + index |= byteSpan.IndexOfAny(values); + } + } + } + Assert.Equal(size > 1 ? size : 0, index); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + public void SpanIndexOfAnyString_Many(int size) + { + string[] stringAray = new string[size]; + stringAray[size / 2] = "5"; + Span stringSpan = new Span(stringAray); + ReadOnlySpan values = new ReadOnlySpan(new string[] { "5", "a", "b", "c" }); + + int index = 0; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + index |= stringSpan.IndexOfAny(values); + } + } + } + Assert.Equal(size / 2, index); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Performance/Perf.Span.SequenceCompareTo.cs b/external/corefx/src/System.Memory/tests/Performance/Perf.Span.SequenceCompareTo.cs new file mode 100644 index 0000000000..7bc565c5b7 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Performance/Perf.Span.SequenceCompareTo.cs @@ -0,0 +1,190 @@ +// 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 Microsoft.Xunit.Performance; +using Xunit; + +namespace System.Memory.Tests +{ + public class Perf_Span_SequenceCompareTo + { + private const int InnerCount = 100000; + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + [InlineData(10000)] + public void SequenceCompareToSame_Byte(int size) + { + Span first = new byte[size]; + Span second = new byte[size]; + int result = -1; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + result = first.SequenceCompareTo(second); + } + } + } + + Assert.Equal(0, result); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + [InlineData(10000)] + public void SequenceCompareToDifferent_Byte(int size) + { + Span first = new byte[size]; + Span second = new byte[size]; + int result = -1; + + first[size/2] = 1; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + result = first.SequenceCompareTo(second); + } + } + } + + Assert.Equal(1, result); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + [InlineData(10000)] + public void SequenceCompareToSame_Int(int size) + { + Span first = new int[size]; + Span second = new int[size]; + int result = -1; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + result = first.SequenceCompareTo(second); + } + } + } + + Assert.Equal(0, result); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + [InlineData(10000)] + public void SequenceCompareToDifferent_Int(int size) + { + Span first = new int[size]; + Span second = new int[size]; + int result = -1; + + first[size/2] = 1; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + result = first.SequenceCompareTo(second); + } + } + } + + Assert.Equal(1, result); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + [InlineData(10000)] + public void SequenceCompareToSame_String(int size) + { + var firstStringArray = new string[size]; + var secondStringArray = new string[size]; + for (int i = 0; i < size; i++) + { + firstStringArray[i] = secondStringArray[i] = "0"; + } + + Span first = firstStringArray; + Span second = secondStringArray; + int result = -1; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + result = first.SequenceCompareTo(second); + } + } + } + + Assert.Equal(0, result); + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + [InlineData(10000)] + public void SequenceCompareToDifferent_String(int size) + { + var firstStringArray = new string[size]; + var secondStringArray = new string[size]; + for (int i = 0; i < size; i++) + { + firstStringArray[i] = secondStringArray[i] = "0"; + } + + Span first = firstStringArray; + Span second = secondStringArray; + int result = -1; + + first[size/2] = "1"; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + result = first.SequenceCompareTo(second); + } + } + } + + Assert.Equal(1, result); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Performance/Perf.Span.StartsWith.cs b/external/corefx/src/System.Memory/tests/Performance/Perf.Span.StartsWith.cs index 60c9a8338e..6d6b1946e6 100644 --- a/external/corefx/src/System.Memory/tests/Performance/Perf.Span.StartsWith.cs +++ b/external/corefx/src/System.Memory/tests/Performance/Perf.Span.StartsWith.cs @@ -9,7 +9,7 @@ namespace System.Memory.Tests { public class Perf_Span_StartsWith { - [Benchmark] + [Benchmark(InnerIterationCount = 10000)] [InlineData(1, 1)] [InlineData(10, 1)] [InlineData(100, 1)] @@ -42,11 +42,11 @@ namespace System.Memory.Tests } var span = new Span(a); var value = new Span(b); - foreach (var iteration in Benchmark.Iterations) + foreach (BenchmarkIteration iteration in Benchmark.Iterations) { using (iteration.StartMeasurement()) { - for (int i = 0; i < 10000; i++) + for (int i = 0; i < Benchmark.InnerIterationCount; i++) { bool result = span.StartsWith(value); } @@ -54,7 +54,7 @@ namespace System.Memory.Tests } } - [Benchmark] + [Benchmark(InnerIterationCount = 1000000)] [InlineData(1, 1)] [InlineData(10, 1)] [InlineData(100, 1)] @@ -87,11 +87,56 @@ namespace System.Memory.Tests } var span = new Span(a); var value = new Span(b); - foreach (var iteration in Benchmark.Iterations) + foreach (BenchmarkIteration iteration in Benchmark.Iterations) { using (iteration.StartMeasurement()) { - for (int i = 0; i < 10000; i++) + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + bool result = span.StartsWith(value); + } + } + } + } + + [Benchmark(InnerIterationCount = 10000)] + [InlineData(1, 1)] + [InlineData(10, 1)] + [InlineData(100, 1)] + [InlineData(1000, 1)] + [InlineData(10000, 1)] + [InlineData(10, 10)] + [InlineData(100, 10)] + [InlineData(1000, 10)] + [InlineData(10000, 10)] + [InlineData(100, 100)] + [InlineData(1000, 100)] + [InlineData(10000, 100)] + [InlineData(1000, 1000)] + [InlineData(10000, 1000)] + [InlineData(10000, 10000)] + public void String(int size, int valSize) + { + var a = new string[size]; + for (int i = 0; i < size; i++) + { + int num = 65 + i % 26; + a[i] = ((char)num).ToString(); + } + + var b = new string[valSize]; + for (int i = 0; i < valSize; i++) + { + int num = 65 + i % 26; + b[i] = ((char)num).ToString(); + } + var span = new Span(a); + var value = new Span(b); + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) { bool result = span.StartsWith(value); } diff --git a/external/corefx/src/System.Memory/tests/Performance/Perf.Utf8Formatter.cs b/external/corefx/src/System.Memory/tests/Performance/Perf.Utf8Formatter.cs new file mode 100644 index 0000000000..de52cc39dc --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Performance/Perf.Utf8Formatter.cs @@ -0,0 +1,168 @@ +// 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 Microsoft.Xunit.Performance; +using Xunit; + +namespace System.Buffers.Text.Tests +{ + public static partial class Utf8FormatterTests + { + private const int InnerCount = 100000; + + // There are really only two integer formatters: Int64/UInt64. Benchmarking the others won't provide any extra code coverage. + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(214748364L)] + [InlineData(2L)] + [InlineData(21474836L)] + [InlineData(21474L)] + [InlineData(214L)] + [InlineData(2147L)] + [InlineData(214748L)] + [InlineData(21L)] + [InlineData(2147483L)] + [InlineData(922337203685477580L)] + [InlineData(92233720368547758L)] + [InlineData(9223372036854775L)] + [InlineData(922337203685477L)] + [InlineData(92233720368547L)] + [InlineData(9223372036854L)] + [InlineData(922337203685L)] + [InlineData(92233720368L)] + [InlineData(-214748364L)] + [InlineData(-2L)] + [InlineData(-21474836L)] + [InlineData(-21474L)] + [InlineData(-214L)] + [InlineData(-2147L)] + [InlineData(-214748L)] + [InlineData(-21L)] + [InlineData(-2147483L)] + [InlineData(-922337203685477580L)] + [InlineData(-92233720368547758L)] + [InlineData(-9223372036854775L)] + [InlineData(-922337203685477L)] + [InlineData(-92233720368547L)] + [InlineData(-9223372036854L)] + [InlineData(-922337203685L)] + [InlineData(-92233720368L)] + [InlineData(0L)] + [InlineData(-9223372036854775808L)] // min value + [InlineData(9223372036854775807L)] // max value + [InlineData(-2147483648L)] // int32 min value + [InlineData(2147483647L)] // int32 max value + [InlineData(-4294967295000000000L)] // -(uint.MaxValue * Billion) + [InlineData(4294967295000000000L)] // uint.MaxValue * Billion + [InlineData(-4294967295000000001L)] // -(uint.MaxValue * Billion + 1) + [InlineData(4294967295000000001L)] // uint.MaxValue * Billion + 1 + private static void FormatterInt64(long value) + { + byte[] utf8ByteArray = new byte[40]; + Span utf8ByteSpan = utf8ByteArray; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + Utf8Formatter.TryFormat(value, utf8ByteSpan, out int bytesWritten); + TestHelpers.DoNotIgnore(value, bytesWritten); + } + } + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(-12837467)] // standard format + [InlineData(12837467)] // standard format + [InlineData(-1283)] // standard format short + [InlineData(1283)] // standard format short + [InlineData(0)] + [InlineData(-2147483648)] // int32 min value + [InlineData(2147483647)] // int32 max value + private static void FormatterInt32(int value) + { + byte[] utf8ByteArray = new byte[40]; + Span utf8ByteSpan = utf8ByteArray; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + Utf8Formatter.TryFormat(value, utf8ByteSpan, out int bytesWritten); + TestHelpers.DoNotIgnore(value, bytesWritten); + } + } + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData(214748364LU)] + [InlineData(2LU)] + [InlineData(21474836LU)] + [InlineData(21474LU)] + [InlineData(214LU)] + [InlineData(2147LU)] + [InlineData(214748LU)] + [InlineData(21LU)] + [InlineData(2147483LU)] + [InlineData(922337203685477580LU)] + [InlineData(92233720368547758LU)] + [InlineData(9223372036854775LU)] + [InlineData(922337203685477LU)] + [InlineData(92233720368547LU)] + [InlineData(9223372036854LU)] + [InlineData(922337203685LU)] + [InlineData(92233720368LU)] + [InlineData(0LU)] // min value + [InlineData(18446744073709551615LU)] // max value + [InlineData(2147483647LU)] // int32 max value + [InlineData(9223372036854775807LU)] // int64 max value + [InlineData(1000000000000000000LU)] // quintillion + [InlineData(4294967295000000000LU)] // uint.MaxValue * Billion + [InlineData(4294967295000000001LU)] // uint.MaxValue * Billion + 1 + private static void FormatterUInt64(ulong value) + { + byte[] utf8ByteArray = new byte[40]; + Span utf8ByteSpan = utf8ByteArray; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + Utf8Formatter.TryFormat(value, utf8ByteSpan, out int bytesWritten); + TestHelpers.DoNotIgnore(value, bytesWritten); + } + } + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + private static void FormatterDateTimeOffsetNow() + { + DateTimeOffset value = new DateTimeOffset(year: 2017, month: 12, day: 30, hour: 3, minute: 45, second: 22, millisecond: 950, offset: new TimeSpan(hours: -8, minutes: 0, seconds: 0)); + + byte[] utf8ByteArray = new byte[40]; + Span utf8ByteSpan = utf8ByteArray; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + Utf8Formatter.TryFormat(value, utf8ByteSpan, out int bytesWritten); + TestHelpers.DoNotIgnore(value, bytesWritten); + } + } + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Performance/Perf.Utf8Parser.cs b/external/corefx/src/System.Memory/tests/Performance/Perf.Utf8Parser.cs new file mode 100644 index 0000000000..7d1294f70d --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Performance/Perf.Utf8Parser.cs @@ -0,0 +1,231 @@ +// 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.Text; +using Microsoft.Xunit.Performance; +using Xunit; + +namespace System.Buffers.Text.Tests +{ + public static partial class Utf8ParserTests + { + private const int InnerCount = 100000; + + [Benchmark] + [InlineData("True")] + [InlineData("False")] + private static void PrimitiveParserByteSpanToBool(string text) + { + byte[] utf8ByteArray = Encoding.UTF8.GetBytes(text); + ReadOnlySpan utf8ByteSpan = utf8ByteArray; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + Utf8Parser.TryParse(utf8ByteSpan, out bool value, out int bytesConsumed); + TestHelpers.DoNotIgnore(value, bytesConsumed); + } + } + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData("42")] // standard parse + [InlineData("-128")] // min value + [InlineData("127")] // max value + private static void ParserSByte(string text) + { + byte[] utf8ByteArray = Encoding.UTF8.GetBytes(text); + ReadOnlySpan utf8ByteSpan = utf8ByteArray; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + Utf8Parser.TryParse(utf8ByteSpan, out sbyte value, out int bytesConsumed); + TestHelpers.DoNotIgnore(value, bytesConsumed); + } + } + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData("42")] // standard parse + [InlineData("0")] // min value + [InlineData("255")] // max value + private static void ParserByte(string text) + { + byte[] utf8ByteArray = Encoding.UTF8.GetBytes(text); + ReadOnlySpan utf8ByteSpan = utf8ByteArray; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + Utf8Parser.TryParse(utf8ByteSpan, out byte value, out int bytesConsumed); + TestHelpers.DoNotIgnore(value, bytesConsumed); + } + } + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData("4212")] // standard parse + [InlineData("-32768")] // min value + [InlineData("32767")] // max value + private static void ParserInt16(string text) + { + byte[] utf8ByteArray = Encoding.UTF8.GetBytes(text); + ReadOnlySpan utf8ByteSpan = utf8ByteArray; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + Utf8Parser.TryParse(utf8ByteSpan, out short value, out int bytesConsumed); + TestHelpers.DoNotIgnore(value, bytesConsumed); + } + } + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData("4212")] // standard parse + [InlineData("0")] // min value + [InlineData("65535")] // max value + private static void ParserUInt16(string text) + { + byte[] utf8ByteArray = Encoding.UTF8.GetBytes(text); + ReadOnlySpan utf8ByteSpan = utf8ByteArray; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + Utf8Parser.TryParse(utf8ByteSpan, out ushort value, out int bytesConsumed); + TestHelpers.DoNotIgnore(value, bytesConsumed); + } + } + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData("12837467")] // standard parse + [InlineData("-2147483648")] // min value + [InlineData("2147483647")] // max value + private static void ParserInt32(string text) + { + byte[] utf8ByteArray = Encoding.UTF8.GetBytes(text); + ReadOnlySpan utf8ByteSpan = utf8ByteArray; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + Utf8Parser.TryParse(utf8ByteSpan, out int value, out int bytesConsumed); + TestHelpers.DoNotIgnore(value, bytesConsumed); + } + } + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData("12837467")] // standard parse + [InlineData("0")] // min value + [InlineData("4294967295")] // max value + private static void ParserUInt32(string text) + { + byte[] utf8ByteArray = Encoding.UTF8.GetBytes(text); + ReadOnlySpan utf8ByteSpan = utf8ByteArray; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + Utf8Parser.TryParse(utf8ByteSpan, out uint value, out int bytesConsumed); + TestHelpers.DoNotIgnore(value, bytesConsumed); + } + } + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData("12837467")] // standard parse + [InlineData("-9223372036854775808")] // min value + [InlineData("9223372036854775807")] // max value + private static void ParserInt64(string text) + { + byte[] utf8ByteArray = Encoding.UTF8.GetBytes(text); + ReadOnlySpan utf8ByteSpan = utf8ByteArray; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + Utf8Parser.TryParse(utf8ByteSpan, out long value, out int bytesConsumed); + TestHelpers.DoNotIgnore(value, bytesConsumed); + } + } + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData("12837467")] // standard parse + [InlineData("0")] // min value + [InlineData("18446744073709551615")] // max value + private static void ParserUInt64(string text) + { + byte[] utf8ByteArray = Encoding.UTF8.GetBytes(text); + ReadOnlySpan utf8ByteSpan = utf8ByteArray; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + Utf8Parser.TryParse(utf8ByteSpan, out ulong value, out int bytesConsumed); + TestHelpers.DoNotIgnore(value, bytesConsumed); + } + } + } + } + + [Benchmark(InnerIterationCount = InnerCount)] + [InlineData("Fri, 30 Jun 2000 03:15:45 GMT")] // standard parse + private static void ParserDateTimeOffsetR(string text) + { + byte[] utf8ByteArray = Encoding.UTF8.GetBytes(text); + ReadOnlySpan utf8ByteSpan = utf8ByteArray; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + Utf8Parser.TryParse(utf8ByteSpan, out DateTimeOffset value, out int bytesConsumed, 'R'); + TestHelpers.DoNotIgnore(value, bytesConsumed); + } + } + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Performance/System.Memory.Performance.Tests.csproj b/external/corefx/src/System.Memory/tests/Performance/System.Memory.Performance.Tests.csproj index c4b6188eab..692fb4e62b 100644 --- a/external/corefx/src/System.Memory/tests/Performance/System.Memory.Performance.Tests.csproj +++ b/external/corefx/src/System.Memory/tests/Performance/System.Memory.Performance.Tests.csproj @@ -3,17 +3,25 @@ true + true {18482C55-6B57-41E8-BBC4-383B9E2C7AA2} - - - + + + + + + + + + + Common\System\PerfUtils.cs @@ -25,5 +33,8 @@ PerfRunner + + + \ No newline at end of file diff --git a/external/corefx/src/System.Memory/tests/ReadOnlyMemory/CopyTo.cs b/external/corefx/src/System.Memory/tests/ReadOnlyMemory/CopyTo.cs new file mode 100644 index 0000000000..319840d52c --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ReadOnlyMemory/CopyTo.cs @@ -0,0 +1,125 @@ +// 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 Xunit; + +namespace System.MemoryTests +{ + public static partial class ReadOnlyMemoryTests + { + [Fact] + public static void TryCopyTo() + { + int[] src = { 1, 2, 3 }; + int[] dst = { 99, 100, 101 }; + + ReadOnlyMemory srcMemory = src; + bool success = srcMemory.TryCopyTo(dst); + Assert.True(success); + Assert.Equal(src, dst); + } + + [Fact] + public static void TryCopyToSingle() + { + int[] src = { 1 }; + int[] dst = { 99 }; + + ReadOnlyMemory srcMemory = src; + bool success = srcMemory.TryCopyTo(dst); + Assert.True(success); + Assert.Equal(src, dst); + } + + [Fact] + public static void TryCopyToArraySegmentImplicit() + { + int[] src = { 1, 2, 3 }; + int[] dst = { 5, 99, 100, 101, 10 }; + var segment = new ArraySegment(dst, 1, 3); + + ReadOnlyMemory srcMemory = src; + bool success = srcMemory.TryCopyTo(segment); + Assert.True(success); + Assert.Equal(src, segment); + } + + [Fact] + public static void TryCopyToEmpty() + { + int[] src = { }; + int[] dst = { 99, 100, 101 }; + + ReadOnlyMemory srcMemory = src; + bool success = srcMemory.TryCopyTo(dst); + Assert.True(success); + int[] expected = { 99, 100, 101 }; + Assert.Equal(expected, dst); + } + + [Fact] + public static void TryCopyToLonger() + { + int[] src = { 1, 2, 3 }; + int[] dst = { 99, 100, 101, 102 }; + + ReadOnlyMemory srcMemory = src; + bool success = srcMemory.TryCopyTo(dst); + Assert.True(success); + int[] expected = { 1, 2, 3, 102 }; + Assert.Equal(expected, dst); + } + + [Fact] + public static void TryCopyToShorter() + { + int[] src = { 1, 2, 3 }; + int[] dst = { 99, 100 }; + + ReadOnlyMemory srcMemory = src; + bool success = srcMemory.TryCopyTo(dst); + Assert.False(success); + int[] expected = { 99, 100 }; + Assert.Equal(expected, dst); // TryCopyTo() checks for sufficient space before doing any copying. + } + + [Fact] + public static void CopyToShorter() + { + int[] src = { 1, 2, 3 }; + int[] dst = { 99, 100 }; + + ReadOnlyMemory srcMemory = src; + Assert.Throws(() => srcMemory.CopyTo(dst)); + int[] expected = { 99, 100 }; + Assert.Equal(expected, dst); // CopyTo() checks for sufficient space before doing any copying. + } + + [Fact] + public static void Overlapping1() + { + int[] a = { 90, 91, 92, 93, 94, 95, 96, 97 }; + + var src = new ReadOnlyMemory(a, 1, 6); + var dst = new Memory(a, 2, 6); + src.CopyTo(dst); + + int[] expected = { 90, 91, 91, 92, 93, 94, 95, 96 }; + Assert.Equal(expected, a); + } + + [Fact] + public static void Overlapping2() + { + int[] a = { 90, 91, 92, 93, 94, 95, 96, 97 }; + + var src = new ReadOnlyMemory(a, 2, 6); + var dst = new Memory(a, 1, 6); + src.CopyTo(dst); + + int[] expected = { 90, 92, 93, 94, 95, 96, 97, 97 }; + Assert.Equal(expected, a); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ReadOnlyMemory/Empty.cs b/external/corefx/src/System.Memory/tests/ReadOnlyMemory/Empty.cs index c53b3c6bbc..5663cb3e37 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlyMemory/Empty.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlyMemory/Empty.cs @@ -23,5 +23,15 @@ namespace System.MemoryTests Assert.True(empty.IsEmpty); Assert.Equal(0, empty.Length); } + + [Fact] + public static void EmptyEqualsDefault() + { + ReadOnlyMemory empty = ReadOnlyMemory.Empty; + ReadOnlyMemory defaultMemory = default; + Assert.True(defaultMemory.Equals(empty)); + Assert.True(defaultMemory.IsEmpty); + Assert.Equal(0, defaultMemory.Length); + } } } diff --git a/external/corefx/src/System.Memory/tests/ReadOnlyMemory/GetHashCode.cs b/external/corefx/src/System.Memory/tests/ReadOnlyMemory/GetHashCode.cs index 1e53747c32..e955930f81 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlyMemory/GetHashCode.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlyMemory/GetHashCode.cs @@ -69,5 +69,14 @@ namespace System.MemoryTests Assert.Equal(left.GetHashCode(), right.GetHashCode()); } + + [Fact] + public static void DefaultMemoryHashCode() + { + ReadOnlyMemory memory = default; + Assert.Equal(0, memory.GetHashCode()); + ReadOnlyMemory memory2 = default; + Assert.Equal(memory2.GetHashCode(), memory.GetHashCode()); + } } } diff --git a/external/corefx/src/System.Memory/tests/ReadOnlyMemory/ImplicitConversion.cs b/external/corefx/src/System.Memory/tests/ReadOnlyMemory/ImplicitConversion.cs index 205dfd28c3..13fd5f3ed4 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlyMemory/ImplicitConversion.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlyMemory/ImplicitConversion.cs @@ -45,7 +45,7 @@ namespace System.MemoryTests long[] b = { 1, -3, 7, -15, 31 }; ArraySegment segmentLong = new ArraySegment(b, 1, 3); CastReadOnly(segmentLong, -3, 7, -15); - + object o1 = new object(); object o2 = new object(); object o3 = new object(); @@ -61,12 +61,20 @@ namespace System.MemoryTests int[] empty = Array.Empty(); ArraySegment emptySegment = new ArraySegment(empty); CastReadOnly(emptySegment); - + int[] a = { 19, -17 }; ArraySegment segmentInt = new ArraySegment(a, 1, 0); CastReadOnly(segmentInt); } + [Fact] + public static void NullImplicitCast() + { + int[] dst = null; + ReadOnlyMemory srcMemory = dst; + Assert.True(ReadOnlyMemory.Empty.Span == srcMemory.Span); + } + private static void CastReadOnly(ReadOnlyMemory memory, params T[] expected) where T : struct, IEquatable { memory.Validate(expected); diff --git a/external/corefx/src/System.Memory/tests/ReadOnlyMemory/Retain.cs b/external/corefx/src/System.Memory/tests/ReadOnlyMemory/Retain.cs index 87dc45912f..386211195a 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlyMemory/Retain.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlyMemory/Retain.cs @@ -16,10 +16,10 @@ namespace System.MemoryTests int[] array = { 1, 2, 3, 4, 5 }; ReadOnlyMemory memory = array; MemoryHandle handle = memory.Retain(); + Assert.False(handle.HasPointer); unsafe { - int* pointer = (int*)handle.PinnedPointer; - Assert.True(pointer == null); + Assert.True(handle.Pointer == null); } handle.Dispose(); } @@ -30,10 +30,10 @@ namespace System.MemoryTests int[] array = { 1, 2, 3, 4, 5 }; ReadOnlyMemory memory = array; MemoryHandle handle = memory.Retain(pin: true); + Assert.True(handle.HasPointer); unsafe { - int* pointer = (int*)handle.PinnedPointer; - Assert.True(pointer != null); + int* pointer = (int*)handle.Pointer; GC.Collect(); @@ -45,6 +45,23 @@ namespace System.MemoryTests handle.Dispose(); } + [Fact] + public static void MemoryFromEmptyArrayRetainWithPinning() + { + ReadOnlyMemory memory = new int[0]; + MemoryHandle handle = memory.Retain(pin: true); + Assert.True(handle.HasPointer); + unsafe + { + int* pointer = (int*)handle.Pointer; + + GC.Collect(); + + Assert.True(pointer != null); + } + handle.Dispose(); + } + [Fact] public static void MemoryRetainWithPinningAndSlice() { @@ -53,10 +70,10 @@ namespace System.MemoryTests memory = memory.Slice(1); MemoryHandle handle = memory.Retain(pin: true); ReadOnlySpan span = memory.Span; + Assert.True(handle.HasPointer); unsafe { - int* pointer = (int*)handle.PinnedPointer; - Assert.True(pointer != null); + int* pointer = (int*)handle.Pointer; GC.Collect(); @@ -80,10 +97,10 @@ namespace System.MemoryTests OwnedMemory owner = new CustomMemoryForTest(array); ReadOnlyMemory memory = owner.Memory; MemoryHandle handle = memory.Retain(); + Assert.False(handle.HasPointer); unsafe { - int* pointer = (int*)handle.PinnedPointer; - Assert.True(pointer == null); + Assert.True(handle.Pointer == null); } handle.Dispose(); } @@ -95,10 +112,10 @@ namespace System.MemoryTests OwnedMemory owner = new CustomMemoryForTest(array); ReadOnlyMemory memory = owner.Memory; MemoryHandle handle = memory.Retain(pin: true); + Assert.True(handle.HasPointer); unsafe { - int* pointer = (int*)handle.PinnedPointer; - Assert.True(pointer != null); + int* pointer = (int*)handle.Pointer; GC.Collect(); @@ -110,7 +127,6 @@ namespace System.MemoryTests handle.Dispose(); } - [ActiveIssue(24384, TargetFrameworkMonikers.UapAot)] [Fact] public static void OwnedMemoryRetainWithPinningAndSlice() { @@ -119,10 +135,10 @@ namespace System.MemoryTests ReadOnlyMemory memory = owner.Memory.Slice(1); MemoryHandle handle = memory.Retain(pin: true); ReadOnlySpan span = memory.Span; + Assert.True(handle.HasPointer); unsafe { - int* pointer = (int*)handle.PinnedPointer; - Assert.True(pointer != null); + int* pointer = (int*)handle.Pointer; GC.Collect(); @@ -138,5 +154,20 @@ namespace System.MemoryTests } handle.Dispose(); } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public static void DefaultMemoryRetain(bool pin) + { + ReadOnlyMemory memory = default; + MemoryHandle handle = memory.Retain(pin: pin); + Assert.False(handle.HasPointer); + unsafe + { + Assert.True(handle.Pointer == null); + } + handle.Dispose(); + } } } diff --git a/external/corefx/src/System.Memory/tests/ReadOnlyMemory/Slice.cs b/external/corefx/src/System.Memory/tests/ReadOnlyMemory/Slice.cs index edeeb9d53f..70404ecca4 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlyMemory/Slice.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlyMemory/Slice.cs @@ -3,8 +3,9 @@ // See the LICENSE file in the project root for more information. using Xunit; -using System.Runtime.CompilerServices; using System.Buffers; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.MemoryTests { @@ -16,13 +17,13 @@ namespace System.MemoryTests int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 }; ReadOnlyMemory memory = new ReadOnlyMemory(a).Slice(6); Assert.Equal(4, memory.Length); - Assert.True(Unsafe.AreSame(ref a[6], ref memory.Span.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref a[6], ref Unsafe.AsRef(in MemoryMarshal.GetReference(memory.Span)))); OwnedMemory owner = new CustomMemoryForTest(a); ReadOnlyMemory memoryFromOwner = ((ReadOnlyMemory)owner.Memory).Slice(6); Assert.Equal(4, memoryFromOwner.Length); - Assert.True(Unsafe.AreSame(ref a[6], ref memoryFromOwner.Span.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref a[6], ref Unsafe.AsRef(in MemoryMarshal.GetReference(memoryFromOwner.Span)))); } [Fact] @@ -31,13 +32,13 @@ namespace System.MemoryTests int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 }; ReadOnlyMemory memory = new ReadOnlyMemory(a).Slice(a.Length); Assert.Equal(0, memory.Length); - Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref memory.Span.DangerousGetPinnableReference(), 1))); + Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref Unsafe.AsRef(in MemoryMarshal.GetReference(memory.Span)), 1))); OwnedMemory owner = new CustomMemoryForTest(a); ReadOnlyMemory memoryFromOwner = ((ReadOnlyMemory)owner.Memory).Slice(a.Length); Assert.Equal(0, memoryFromOwner.Length); - Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref memoryFromOwner.Span.DangerousGetPinnableReference(), 1))); + Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref Unsafe.AsRef(in MemoryMarshal.GetReference(memoryFromOwner.Span)), 1))); } [Fact] @@ -46,13 +47,13 @@ namespace System.MemoryTests int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 }; ReadOnlyMemory memory = new ReadOnlyMemory(a).Slice(3, 5); Assert.Equal(5, memory.Length); - Assert.True(Unsafe.AreSame(ref a[3], ref memory.Span.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref a[3], ref Unsafe.AsRef(in MemoryMarshal.GetReference(memory.Span)))); OwnedMemory owner = new CustomMemoryForTest(a); ReadOnlyMemory memoryFromOwner = ((ReadOnlyMemory)owner.Memory).Slice(3, 5); Assert.Equal(5, memoryFromOwner.Length); - Assert.True(Unsafe.AreSame(ref a[3], ref memoryFromOwner.Span.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref a[3], ref Unsafe.AsRef(in MemoryMarshal.GetReference(memoryFromOwner.Span)))); } [Fact] @@ -61,13 +62,13 @@ namespace System.MemoryTests int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 }; ReadOnlyMemory memory = new ReadOnlyMemory(a).Slice(4, 6); Assert.Equal(6, memory.Length); - Assert.True(Unsafe.AreSame(ref a[4], ref memory.Span.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref a[4], ref Unsafe.AsRef(in MemoryMarshal.GetReference(memory.Span)))); OwnedMemory owner = new CustomMemoryForTest(a); ReadOnlyMemory memoryFromOwner = ((ReadOnlyMemory)owner.Memory).Slice(4, 6); Assert.Equal(6, memoryFromOwner.Length); - Assert.True(Unsafe.AreSame(ref a[4], ref memoryFromOwner.Span.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref a[4], ref Unsafe.AsRef(in MemoryMarshal.GetReference(memoryFromOwner.Span)))); } [Fact] @@ -76,13 +77,13 @@ namespace System.MemoryTests int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 }; ReadOnlyMemory memory = new ReadOnlyMemory(a).Slice(a.Length, 0); Assert.Equal(0, memory.Length); - Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref memory.Span.DangerousGetPinnableReference(), 1))); + Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref Unsafe.AsRef(in MemoryMarshal.GetReference(memory.Span)), 1))); OwnedMemory owner = new CustomMemoryForTest(a); ReadOnlyMemory memoryFromOwner = ((ReadOnlyMemory)owner.Memory).Slice(a.Length, 0); Assert.Equal(0, memoryFromOwner.Length); - Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref memoryFromOwner.Span.DangerousGetPinnableReference(), 1))); + Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref Unsafe.AsRef(in MemoryMarshal.GetReference(memoryFromOwner.Span)), 1))); } [Fact] @@ -108,5 +109,21 @@ namespace System.MemoryTests Assert.Throws(() => memory.Slice(a.Length + 1, 0)); Assert.Throws(() => memory.Slice(a.Length, 1)); } + + [Fact] + public static void SliceWithStartDefaultMemory() + { + ReadOnlyMemory memory = default; + memory = memory.Slice(0); + Assert.True(memory.Equals(default)); + } + + [Fact] + public static void SliceWithStartAndLengthDefaultMemory() + { + ReadOnlyMemory memory = default; + memory = memory.Slice(0, 0); + Assert.True(memory.Equals(default)); + } } } diff --git a/external/corefx/src/System.Memory/tests/ReadOnlyMemory/Span.cs b/external/corefx/src/System.Memory/tests/ReadOnlyMemory/Span.cs index bd125929f7..65029dc03b 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlyMemory/Span.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlyMemory/Span.cs @@ -66,10 +66,10 @@ namespace System.MemoryTests ReadOnlyMemory memory; memory = new ReadOnlyMemory(empty); - memory.Span.Validate(); + memory.Span.ValidateNonNullEmpty(); memory = new ReadOnlyMemory(empty, 0, empty.Length); - memory.Span.Validate(); + memory.Span.ValidateNonNullEmpty(); OwnedMemory owner = new CustomMemoryForTest(empty); ((ReadOnlyMemory)owner.Memory).Span.Validate(); @@ -94,5 +94,17 @@ namespace System.MemoryTests ((ReadOnlyMemory)owner.Memory).Span.Validate(42, -1); } + [Fact] + public static void SpanFromDefaultMemory() + { + ReadOnlyMemory memory = default; + ReadOnlySpan span = memory.Span; + Assert.True(span.SequenceEqual(default)); + + ReadOnlyMemory memoryObject = default; + ReadOnlySpan spanObject = memoryObject.Span; + Assert.True(spanObject.SequenceEqual(default)); + } + } } diff --git a/external/corefx/src/System.Memory/tests/ReadOnlyMemory/Strings.cs b/external/corefx/src/System.Memory/tests/ReadOnlyMemory/Strings.cs new file mode 100644 index 0000000000..592f398987 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ReadOnlyMemory/Strings.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. +// See the LICENSE file in the project root for more information. + +using Xunit; +using System.Buffers; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace System.MemoryTests +{ + public static partial class ReadOnlyMemoryTests + { + public static IEnumerable StringInputs() + { + yield return new object[] { "" }; + yield return new object[] { "a" }; + yield return new object[] { "a\0bcdefghijklmnopqrstuvwxyz" }; + } + + [Theory] + [MemberData(nameof(StringInputs))] + public static void AsReadOnlyMemory_ToArray_Roundtrips(string input) + { + ReadOnlyMemory m = input.AsReadOnlyMemory(); + Assert.Equal(input, new string(m.ToArray())); + } + + [Theory] + [MemberData(nameof(StringInputs))] + public static void AsReadOnlyMemory_Span_Roundtrips(string input) + { + ReadOnlyMemory m = input.AsReadOnlyMemory(); + ReadOnlySpan s = m.Span; + Assert.Equal(input, new string(s.ToArray())); + } + + [Theory] + [InlineData("", 0, 0)] + [InlineData("0123456789", 0, 0)] + [InlineData("0123456789", 10, 0)] + [InlineData("0123456789", 0, 10)] + [InlineData("0123456789", 1, 9)] + [InlineData("0123456789", 2, 8)] + [InlineData("0123456789", 9, 1)] + [InlineData("0123456789", 1, 8)] + [InlineData("0123456789", 5, 3)] + public static void AsReadOnlyMemory_Slice_MatchesSubstring(string input, int offset, int count) + { + ReadOnlyMemory m = input.AsReadOnlyMemory(); + Assert.Equal(input.Substring(offset, count), new string(m.Slice(offset, count).ToArray())); + Assert.Equal(input.Substring(offset, count), new string(m.Slice(offset, count).Span.ToArray())); + Assert.Equal(input.Substring(offset), new string(m.Slice(offset).ToArray())); + } + + [Fact] + public static void AsReadOnlyMemory_NullString_Throws() + { + AssertExtensions.Throws("text", () => ((string)null).AsReadOnlyMemory()); + AssertExtensions.Throws("text", () => ((string)null).AsReadOnlyMemory(0)); + AssertExtensions.Throws("text", () => ((string)null).AsReadOnlyMemory(0, 0)); + } + + [Fact] + public static void AsReadOnlyMemory_TryGetString_Roundtrips() + { + string input = "0123456789"; + ReadOnlyMemory m = input.AsReadOnlyMemory(); + Assert.False(m.IsEmpty); + + Assert.True(m.TryGetString(out string text, out int start, out int length)); + Assert.Same(input, text); + Assert.Equal(0, start); + Assert.Equal(input.Length, length); + + m = m.Slice(1); + Assert.True(m.TryGetString(out text, out start, out length)); + Assert.Same(input, text); + Assert.Equal(1, start); + Assert.Equal(input.Length - 1, length); + + m = m.Slice(1); + Assert.True(m.TryGetString(out text, out start, out length)); + Assert.Same(input, text); + Assert.Equal(2, start); + Assert.Equal(input.Length - 2, length); + + m = m.Slice(3, 2); + Assert.True(m.TryGetString(out text, out start, out length)); + Assert.Same(input, text); + Assert.Equal(5, start); + Assert.Equal(2, length); + + m = m.Slice(m.Length); + Assert.True(m.TryGetString(out text, out start, out length)); + Assert.Same(input, text); + Assert.Equal(7, start); + Assert.Equal(0, length); + + m = m.Slice(0); + Assert.True(m.TryGetString(out text, out start, out length)); + Assert.Same(input, text); + Assert.Equal(7, start); + Assert.Equal(0, length); + + m = m.Slice(0, 0); + Assert.True(m.TryGetString(out text, out start, out length)); + Assert.Same(input, text); + Assert.Equal(7, start); + Assert.Equal(0, length); + + Assert.True(m.IsEmpty); + } + + [Fact] + public static void Array_TryGetString_ReturnsFalse() + { + ReadOnlyMemory m = new char[10]; + Assert.False(m.TryGetString(out string text, out int start, out int length)); + Assert.Null(text); + Assert.Equal(0, start); + Assert.Equal(0, length); + } + + [Fact] + public static void AsReadOnlyMemory_TryGetArray_ReturnsFalse() + { + ReadOnlyMemory m = "0123456789".AsReadOnlyMemory(); + Assert.False(MemoryMarshal.TryGetArray(m, out ArraySegment array)); + Assert.Null(array.Array); + Assert.Equal(0, array.Offset); + Assert.Equal(0, array.Count); + } + + [Fact] + public static unsafe void AsReadOnlyMemory_Retain_ExpectedPointerValue() + { + string input = "0123456789"; + ReadOnlyMemory m = input.AsReadOnlyMemory(); + + using (MemoryHandle h = m.Retain(pin: false)) + { + Assert.Equal(IntPtr.Zero, (IntPtr)h.Pointer); + } + + using (MemoryHandle h = m.Retain(pin: true)) + { + GC.Collect(); + fixed (char* ptr = input) + { + Assert.Equal((IntPtr)ptr, (IntPtr)h.Pointer); + } + } + } + + [Theory] + [MemberData(nameof(TestHelpers.StringSliceTestData), MemberType = typeof(TestHelpers))] + public static unsafe void AsReadOnlyMemory_PointerAndLength(string text, int start, int length) + { + ReadOnlyMemory m; + if (start == -1) + { + start = 0; + length = text.Length; + m = text.AsReadOnlyMemory(); + } + else if (length == -1) + { + length = text.Length - start; + m = text.AsReadOnlyMemory(start); + } + else + { + m = text.AsReadOnlyMemory(start, length); + } + + Assert.Equal(length, m.Length); + + using (MemoryHandle h = m.Retain(pin: true)) + { + fixed (char* pText = text) + { + char* expected = pText + start; + void* actual = h.Pointer; + Assert.Equal((IntPtr)expected, (IntPtr)actual); + } + } + } + + [Theory] + [MemberData(nameof(TestHelpers.StringSlice2ArgTestOutOfRangeData), MemberType = typeof(TestHelpers))] + public static unsafe void AsReadOnlyMemory_2Arg_OutOfRange(string text, int start) + { + AssertExtensions.Throws("start", () => text.AsReadOnlyMemory(start)); + } + + [Theory] + [MemberData(nameof(TestHelpers.StringSlice3ArgTestOutOfRangeData), MemberType = typeof(TestHelpers))] + public static unsafe void AsReadOnlyMemory_3Arg_OutOfRange(string text, int start, int length) + { + AssertExtensions.Throws("start", () => text.AsReadOnlyMemory(start, length)); + } + + [Fact] + public static void AsReadOnlyMemory_EqualsAndGetHashCode_ExpectedResults() + { + ReadOnlyMemory m1 = new string('a', 4).AsReadOnlyMemory(); + ReadOnlyMemory m2 = new string('a', 4).AsReadOnlyMemory(); + + Assert.True(m1.Span.SequenceEqual(m2.Span)); + Assert.True(m1.Equals(m1)); + Assert.True(m1.Equals((object)m1)); + Assert.False(m1.Equals(m2)); + Assert.False(m1.Equals((object)m2)); + + Assert.Equal(m1.GetHashCode(), m1.GetHashCode()); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ReadOnlyMemory/ToArray.cs b/external/corefx/src/System.Memory/tests/ReadOnlyMemory/ToArray.cs index 0d5d71dac6..a262200319 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlyMemory/ToArray.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlyMemory/ToArray.cs @@ -44,5 +44,13 @@ namespace System.MemoryTests int[] copy = memory.ToArray(); Assert.Equal(0, copy.Length); } + + [Fact] + public static void ToArrayDefault() + { + ReadOnlyMemory memory = default; + int[] copy = memory.ToArray(); + Assert.Equal(0, copy.Length); + } } } diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/AsBytes.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/AsBytes.cs index 9493f7a1b2..66aad3d6cf 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlySpan/AsBytes.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/AsBytes.cs @@ -4,6 +4,7 @@ using Xunit; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.SpanTests { @@ -16,7 +17,7 @@ namespace System.SpanTests ReadOnlySpan span = new ReadOnlySpan(a); ReadOnlySpan asBytes = span.AsBytes(); - Assert.True(Unsafe.AreSame(ref Unsafe.As(ref span.DangerousGetPinnableReference()), ref asBytes.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref Unsafe.As(ref Unsafe.AsRef(in MemoryMarshal.GetReference(span))), ref Unsafe.AsRef(in MemoryMarshal.GetReference(asBytes)))); asBytes.Validate(0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88); } diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/AsReadOnlySpan.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/AsReadOnlySpan.cs index 76e11345ab..71dfa9b417 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlySpan/AsReadOnlySpan.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/AsReadOnlySpan.cs @@ -2,6 +2,8 @@ // 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.Runtime.InteropServices; +using System.Runtime.CompilerServices; using Xunit; namespace System.SpanTests @@ -24,7 +26,6 @@ namespace System.SpanTests spanLong.Validate(1, -3, 7, -15, 31); } - [ActiveIssue(23952, TargetFrameworkMonikers.UapAot)] [Fact] public static void ObjectArrayAsReadOnlySpan() { @@ -47,7 +48,7 @@ namespace System.SpanTests { int[] empty = Array.Empty(); ReadOnlySpan span = empty.AsReadOnlySpan(); - span.Validate(); + span.ValidateNonNullEmpty(); } [Fact] @@ -87,12 +88,12 @@ namespace System.SpanTests int[] empty = Array.Empty(); ArraySegment emptySegment = new ArraySegment(empty); ReadOnlySpan span = emptySegment.AsReadOnlySpan(); - span.Validate(); + span.ValidateNonNullEmpty(); int[] a = { 19, -17 }; ArraySegment segmentInt = new ArraySegment(a, 1, 0); ReadOnlySpan spanInt = segmentInt.AsReadOnlySpan(); - spanInt.Validate(); + spanInt.ValidateNonNullEmpty(); } [Fact] @@ -109,8 +110,7 @@ namespace System.SpanTests { string s = ""; ReadOnlySpan span = s.AsReadOnlySpan(); - char[] expected = s.ToCharArray(); - span.Validate(expected); + span.ValidateNonNullEmpty(); } [Fact] @@ -118,6 +118,70 @@ namespace System.SpanTests { string s = null; Assert.Throws(() => s.AsReadOnlySpan().DontBox()); + Assert.Throws(() => s.AsReadOnlySpan(0).DontBox()); + Assert.Throws(() => s.AsReadOnlySpan(0, 0).DontBox()); + } + + [Fact] + public static void EmptySpanAsReadOnlySpan() + { + Span span = default; + Assert.True(span.AsReadOnlySpan().IsEmpty); + } + + [Fact] + public static void SpanAsReadOnlySpan() + { + int[] a = { 19, -17 }; + Span span = new Span(a); + ReadOnlySpan readOnlySpan = span.AsReadOnlySpan(); + + readOnlySpan.Validate(a); + } + + [Theory] + [MemberData(nameof(TestHelpers.StringSliceTestData), MemberType = typeof(TestHelpers))] + public static unsafe void AsReadOnlySpan_PointerAndLength(string text, int start, int length) + { + ReadOnlySpan span; + if (start == -1) + { + start = 0; + length = text.Length; + span = text.AsReadOnlySpan(); + } + else if (length == -1) + { + length = text.Length - start; + span = text.AsReadOnlySpan(start); + } + else + { + span = text.AsReadOnlySpan(start, length); + } + + Assert.Equal(length, span.Length); + + fixed (char* pText = text) + { + char* expected = pText + start; + void* actual = Unsafe.AsPointer(ref MemoryMarshal.GetReference(span)); + Assert.Equal((IntPtr)expected, (IntPtr)actual); + } + } + + [Theory] + [MemberData(nameof(TestHelpers.StringSlice2ArgTestOutOfRangeData), MemberType = typeof(TestHelpers))] + public static unsafe void AsReadOnlySpan_2Arg_OutOfRange(string text, int start) + { + AssertExtensions.Throws("start", () => text.AsReadOnlySpan(start).DontBox()); + } + + [Theory] + [MemberData(nameof(TestHelpers.StringSlice3ArgTestOutOfRangeData), MemberType = typeof(TestHelpers))] + public static unsafe void AsReadOnlySpan_3Arg_OutOfRange(string text, int start, int length) + { + AssertExtensions.Throws("start", () => text.AsReadOnlySpan(start, length).DontBox()); } } } diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/BinarySearch.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/BinarySearch.cs new file mode 100644 index 0000000000..222a3f16cf --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/BinarySearch.cs @@ -0,0 +1,209 @@ +// 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.Collections.Generic; +using Xunit; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + public static readonly TheoryData<(uint[] Array, uint Value, int ExpectedIndex)> s_casesUInt = + new TheoryData<(uint[] Array, uint Value, int ExpectedIndex)> { + (new uint[] { }, 0u, -1), + (new uint[] { 1u }, 0u, -1), + (new uint[] { 1u }, 1u, 0), + (new uint[] { 1u }, 2u, -2), + (new uint[] { 1u, 2u, 4u, 5u }, 0u, -1), + (new uint[] { 1u, 2u, 4u, 5u }, 1u, 0), + (new uint[] { 1u, 2u, 4u, 5u }, 2u, 1), + (new uint[] { 1u, 2u, 4u, 5u }, 3u, -3), + (new uint[] { 1u, 2u, 4u, 5u }, 4u, 2), + (new uint[] { 1u, 2u, 4u, 5u }, 5u, 3), + (new uint[] { 1u, 2u, 4u, 5u }, 6u, -5), + (new uint[] { 1u, 2u, 2u, 2u }, 2u, 1), + }; + public static readonly TheoryData<(double[] Array, double Value, int ExpectedIndex)> s_casesDouble = + new TheoryData<(double[] Array, double Value, int ExpectedIndex)> { + (new double[] { }, 0.0, -1), + (new double[] { 1.0 }, 0.0, -1), + (new double[] { 1.0 }, 1.0, 0), + (new double[] { 1.0 }, 2.0, -2), + (new double[] { 1.0, 2.0, 4.0, 5.0 }, 0.0, -1), + (new double[] { 1.0, 2.0, 4.0, 5.0 }, 1.0, 0), + (new double[] { 1.0, 2.0, 4.0, 5.0 }, 2.0, 1), + (new double[] { 1.0, 2.0, 4.0, 5.0 }, 3.0, -3), + (new double[] { 1.0, 2.0, 4.0, 5.0 }, 4.0, 2), + (new double[] { 1.0, 2.0, 4.0, 5.0 }, 5.0, 3), + (new double[] { 1.0, 2.0, 4.0, 5.0 }, 6.0, -5), + (new double[] { 2.0, 2.0, 2.0, 1.0 }, 2.0, 1), + }; + public static readonly TheoryData<(string[] Array, string Value, int ExpectedIndex)> s_casesString = + new TheoryData<(string[] Array, string Value, int ExpectedIndex)> { + (new string[] { }, "a", -1), + (new string[] { "b" }, "a", -1), + (new string[] { "b" }, "b", 0), + (new string[] { "b" }, "c", -2), + (new string[] { "b", "c", "e", "f" }, "a", -1), + (new string[] { "b", "c", "e", "f" }, "b", 0), + (new string[] { "b", "c", "e", "f" }, "c", 1), + (new string[] { "b", "c", "e", "f" }, "d", -3), + (new string[] { "b", "c", "e", "f" }, "e", 2), + (new string[] { "b", "c", "e", "f" }, "f", 3), + (new string[] { "b", "c", "e", "f" }, "g", -5), + (new string[] { "b", "b", "c", "c" }, "c", 2), + }; + + [Theory, MemberData(nameof(s_casesUInt))] + public static void BinarySearch_UInt( + (uint[] Array, uint Value, int ExpectedIndex) testCase) + { + TestOverloads(testCase.Array, testCase.Value, testCase.ExpectedIndex); + } + + [Theory, MemberData(nameof(s_casesDouble))] + public static void BinarySearch_Double( + (double[] Array, double Value, int ExpectedIndex) testCase) + { + TestOverloads(testCase.Array, testCase.Value, testCase.ExpectedIndex); + } + + [Theory, MemberData(nameof(s_casesString))] + public static void BinarySearch_String( + (string[] Array, string Value, int ExpectedIndex) testCase) + { + TestOverloads(testCase.Array, testCase.Value, testCase.ExpectedIndex); + } + + [Fact] + public static void BinarySearch_Slice() + { + var array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + var span = new ReadOnlySpan(array, 1, array.Length - 2); + + Assert.Equal(-1, span.BinarySearch(1)); + Assert.Equal(0, span.BinarySearch(2)); + Assert.Equal(3, span.BinarySearch(5)); + Assert.Equal(6, span.BinarySearch(8)); + Assert.Equal(-8, span.BinarySearch(9)); + } + + [Fact] + public static void BinarySearch_NullComparableThrows() + { + Assert.Throws(() => new Span(new int[] { }).BinarySearch(null)); + Assert.Throws(() => new ReadOnlySpan(new int[] { }).BinarySearch(null)); + Assert.Throws(() => new Span(new int[] { }).BinarySearch>(null)); + Assert.Throws(() => new ReadOnlySpan(new int[] { }).BinarySearch>(null)); + } + + [Fact] + public static void BinarySearch_NullComparerThrows() + { + Assert.Throws(() => new Span(new int[] { }).BinarySearch>(0, null)); + Assert.Throws(() => new ReadOnlySpan(new int[] { }).BinarySearch>(0, null)); + } + + // NOTE: BinarySearch_MaxLength_NoOverflow test is constrained to run on Windows and MacOSX because it causes + // problems on Linux due to the way deferred memory allocation works. On Linux, the allocation can + // succeed even if there is not enough memory but then the test may get killed by the OOM killer at the + // time the memory is accessed which triggers the full memory allocation. + [Fact] + [OuterLoop] + [PlatformSpecific(TestPlatforms.Windows | TestPlatforms.OSX)] + public unsafe static void BinarySearch_MaxLength_NoOverflow() + { + if (sizeof(IntPtr) == sizeof(long)) + { + // Allocate maximum length span native memory + var length = int.MaxValue; + if (!AllocationHelper.TryAllocNative(new IntPtr(length), out IntPtr memory)) + { + Console.WriteLine($"Span.BinarySearch test {nameof(BinarySearch_MaxLength_NoOverflow)} skipped (could not alloc memory)."); + return; + } + try + { + var span = new Span(memory.ToPointer(), length); + span.Fill(0); + // Fill last two elements + span[int.MaxValue - 2] = 2; + span[int.MaxValue - 1] = 3; + + Assert.Equal(int.MaxValue / 2, span.BinarySearch((byte)0)); + // Search at end and assert no overflow + Assert.Equal(~(int.MaxValue - 2), span.BinarySearch((byte)1)); + Assert.Equal(int.MaxValue - 2, span.BinarySearch((byte)2)); + Assert.Equal(int.MaxValue - 1, span.BinarySearch((byte)3)); + Assert.Equal(int.MinValue, span.BinarySearch((byte)4)); + } + finally + { + AllocationHelper.ReleaseNative(ref memory); + } + } + } + + private static void TestOverloads( + T[] array, TComparable value, int expectedIndex) + where TComparable : IComparable, T + { + TestSpan(array, value, expectedIndex); + TestReadOnlySpan(array, value, expectedIndex); + TestIComparableSpan(array, value, expectedIndex); + TestIComparableReadOnlySpan(array, value, expectedIndex); + TestComparerSpan(array, value, expectedIndex); + TestComparerReadOnlySpan(array, value, expectedIndex); + } + + private static void TestSpan( + T[] array, TComparable value, int expectedIndex) + where TComparable : IComparable + { + var span = new Span(array); + var index = span.BinarySearch(value); + Assert.Equal(expectedIndex, index); + } + private static void TestReadOnlySpan( + T[] array, TComparable value, int expectedIndex) + where TComparable : IComparable + { + var span = new ReadOnlySpan(array); + var index = span.BinarySearch(value); + Assert.Equal(expectedIndex, index); + } + private static void TestIComparableSpan( + T[] array, TComparable value, int expectedIndex) + where TComparable : IComparable, T + { + var span = new Span(array); + var index = span.BinarySearch((IComparable)value); + Assert.Equal(expectedIndex, index); + } + private static void TestIComparableReadOnlySpan( + T[] array, TComparable value, int expectedIndex) + where TComparable : IComparable, T + { + var span = new ReadOnlySpan(array); + var index = span.BinarySearch((IComparable)value); + Assert.Equal(expectedIndex, index); + } + private static void TestComparerSpan( + T[] array, TComparable value, int expectedIndex) + where TComparable : IComparable, T + { + var span = new Span(array); + var index = span.BinarySearch(value, Comparer.Default); + Assert.Equal(expectedIndex, index); + } + private static void TestComparerReadOnlySpan( + T[] array, TComparable value, int expectedIndex) + where TComparable : IComparable, T + { + var span = new ReadOnlySpan(array); + var index = span.BinarySearch(value, Comparer.Default); + Assert.Equal(expectedIndex, index); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/CopyTo.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/CopyTo.cs index 4dd30a179d..c138032c8a 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlySpan/CopyTo.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/CopyTo.cs @@ -128,6 +128,7 @@ namespace System.SpanTests // the residual chunk of size (bufferSize % 4GB). The inputs sizes to this method, 4GB and 4GB+256B, // test the two size selection paths in CoptyTo method - memory size that is multiple of 4GB or, // a multiple of 4GB + some more size. + [ActiveIssue(25254)] [Theory] [OuterLoop] [PlatformSpecific(TestPlatforms.Windows | TestPlatforms.OSX)] @@ -172,16 +173,18 @@ namespace System.SpanTests for (int count = 0; count < GuidCount; ++count) { - var guidfirst = Unsafe.Add(ref memoryFirst, count); - var guidSecond = Unsafe.Add(ref memorySecond, count); + Guid guidfirst = Unsafe.Add(ref memoryFirst, count); + Guid guidSecond = Unsafe.Add(ref memorySecond, count); Assert.Equal(guidfirst, guidSecond); } } } finally { - if (allocatedFirst) AllocationHelper.ReleaseNative(ref memBlockFirst); - if (allocatedSecond) AllocationHelper.ReleaseNative(ref memBlockSecond); + if (allocatedFirst) + AllocationHelper.ReleaseNative(ref memBlockFirst); + if (allocatedSecond) + AllocationHelper.ReleaseNative(ref memBlockSecond); } } } diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/CtorArray.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/CtorArray.cs index 53a7a70b86..ea8f3ba831 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlySpan/CtorArray.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/CtorArray.cs @@ -63,10 +63,10 @@ namespace System.SpanTests ReadOnlySpan span; span = new ReadOnlySpan(empty); - span.Validate(); + span.ValidateNonNullEmpty(); span = new ReadOnlySpan(empty, 0, empty.Length); - span.Validate(); + span.ValidateNonNullEmpty(); } [Fact] diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/CtorArrayIntInt.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/CtorArrayIntInt.cs index b29198cfbb..3fe022b933 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlySpan/CtorArrayIntInt.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/CtorArrayIntInt.cs @@ -73,7 +73,7 @@ namespace System.SpanTests // Valid for start to equal the array length. This returns an empty span that starts "just past the array." int[] a = { 91, 92, 93 }; ReadOnlySpan span = new ReadOnlySpan(a, 3, 0); - span.Validate(); + span.ValidateNonNullEmpty(); } } } diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/CtorPointerInt.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/CtorPointerInt.cs index 3cf399b055..ee7b74f1cc 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlySpan/CtorPointerInt.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/CtorPointerInt.cs @@ -4,6 +4,7 @@ using Xunit; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; #pragma warning disable 0649 //Field 'SpanTests.InnerStruct.J' is never assigned to, and will always have its default value 0 @@ -17,11 +18,11 @@ namespace System.SpanTests unsafe { int[] a = { 90, 91, 92 }; - fixed (int *pa = a) + fixed (int* pa = a) { ReadOnlySpan span = new ReadOnlySpan(pa, 3); span.Validate(90, 91, 92); - Assert.True(Unsafe.AreSame(ref Unsafe.AsRef(pa), ref span.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref Unsafe.AsRef(pa), ref Unsafe.AsRef(in MemoryMarshal.GetReference(span)))); } } } @@ -33,7 +34,7 @@ namespace System.SpanTests { ReadOnlySpan span = new ReadOnlySpan((void*)null, 0); span.Validate(); - Assert.True(Unsafe.AreSame(ref Unsafe.AsRef((void*)null), ref span.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref Unsafe.AsRef((void*)null), ref Unsafe.AsRef(in MemoryMarshal.GetReference(span)))); } } diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/DangerousCreate.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/DangerousCreate.cs index fd1be1656b..e9feb2f935 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlySpan/DangerousCreate.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/DangerousCreate.cs @@ -4,6 +4,8 @@ using Xunit; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + using static System.TestHelpers; namespace System.SpanTests @@ -24,7 +26,7 @@ namespace System.SpanTests ReadOnlySpan span = ReadOnlySpan.DangerousCreate(testClass, ref testClass.C1, 3); span.Validate('b', 'c', 'd'); - ref char pc1 = ref span.DangerousGetPinnableReference(); + ref char pc1 = ref Unsafe.AsRef(in MemoryMarshal.GetReference(span)); Assert.True(Unsafe.AreSame(ref testClass.C1, ref pc1)); } } diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/DangerousGetPinnableReference.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/DangerousGetPinnableReference.cs deleted file mode 100644 index 9f93501235..0000000000 --- a/external/corefx/src/System.Memory/tests/ReadOnlySpan/DangerousGetPinnableReference.cs +++ /dev/null @@ -1,68 +0,0 @@ -// 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 Xunit; -using System.Runtime.CompilerServices; -using static System.TestHelpers; - -namespace System.SpanTests -{ - public static partial class ReadOnlySpanTests - { - [Fact] - public static void DangerousGetPinnableReferenceArray() - { - int[] a = { 91, 92, 93, 94, 95 }; - ReadOnlySpan span = new ReadOnlySpan(a, 1, 3); - ref int pinnableReference = ref span.DangerousGetPinnableReference(); - Assert.True(Unsafe.AreSame(ref a[1], ref pinnableReference)); - } - - [Fact] - public static void DangerousGetPinnableReferenceArrayPastEnd() - { - // The only real difference between DangerousGetPinnableReference() and "ref span[0]" is that - // DangerousGetPinnableReference() of a zero-length won't throw an IndexOutOfRange. - - int[] a = { 91, 92, 93, 94, 95 }; - ReadOnlySpan span = new ReadOnlySpan(a, a.Length, 0); - ref int pinnableReference = ref span.DangerousGetPinnableReference(); - ref int expected = ref Unsafe.Add(ref a[a.Length - 1], 1); - Assert.True(Unsafe.AreSame(ref expected, ref pinnableReference)); - } - - [Fact] - public static void DangerousGetPinnableReferencePointer() - { - unsafe - { - int i = 42; - ReadOnlySpan span = new ReadOnlySpan(&i, 1); - ref int pinnableReference = ref span.DangerousGetPinnableReference(); - Assert.True(Unsafe.AreSame(ref i, ref pinnableReference)); - } - } - - [Fact] - public static void DangerousGetPinnableReferencePointerDangerousCreate1() - { - TestClass testClass = new TestClass(); - ReadOnlySpan span = ReadOnlySpan.DangerousCreate(testClass, ref testClass.C1, 3); - - ref char pinnableReference = ref span.DangerousGetPinnableReference(); - Assert.True(Unsafe.AreSame(ref testClass.C1, ref pinnableReference)); - } - - [Fact] - public static void DangerousGetPinnableReferenceEmpty() - { - unsafe - { - ReadOnlySpan span = ReadOnlySpan.Empty; - ref int pinnableReference = ref span.DangerousGetPinnableReference(); - Assert.True(Unsafe.AreSame(ref Unsafe.AsRef(null), ref pinnableReference)); - } - } - } -} diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/Empty.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/Empty.cs index 0af910dfec..3f6339bdea 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlySpan/Empty.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/Empty.cs @@ -4,6 +4,7 @@ using Xunit; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.SpanTests { @@ -18,7 +19,7 @@ namespace System.SpanTests unsafe { ref int expected = ref Unsafe.AsRef(null); - ref int actual = ref empty.DangerousGetPinnableReference(); + ref int actual = ref Unsafe.AsRef(in MemoryMarshal.GetReference(empty)); Assert.True(Unsafe.AreSame(ref expected, ref actual)); } } diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/EndsWith.T.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/EndsWith.T.cs new file mode 100644 index 0000000000..8c3eecde42 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/EndsWith.T.cs @@ -0,0 +1,93 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + [Fact] + public static void ZeroLengthEndsWith() + { + int[] a = new int[3]; + + ReadOnlySpan first = new ReadOnlySpan(a, 1, 0); + ReadOnlySpan second = new ReadOnlySpan(a, 2, 0); + bool b = first.EndsWith(second); + Assert.True(b); + } + + [Fact] + public static void SameSpanEndsWith() + { + int[] a = { 4, 5, 6 }; + ReadOnlySpan span = new ReadOnlySpan(a); + bool b = span.EndsWith(span); + Assert.True(b); + } + + [Fact] + public static void LengthMismatchEndsWith() + { + int[] a = { 4, 5, 6 }; + ReadOnlySpan first = new ReadOnlySpan(a, 0, 2); + ReadOnlySpan second = new ReadOnlySpan(a, 0, 3); + bool b = first.EndsWith(second); + Assert.False(b); + } + + [Fact] + public static void EndsWithMatch() + { + int[] a = { 4, 5, 6 }; + ReadOnlySpan span = new ReadOnlySpan(a, 0, 3); + ReadOnlySpan slice = new ReadOnlySpan(a, 1, 2); + bool b = span.EndsWith(slice); + Assert.True(b); + } + + [Fact] + public static void EndsWithMatchDifferentSpans() + { + int[] a = { 4, 5, 6 }; + int[] b = { 4, 5, 6 }; + ReadOnlySpan span = new ReadOnlySpan(a, 0, 3); + ReadOnlySpan slice = new ReadOnlySpan(b, 0, 3); + bool c = span.EndsWith(slice); + Assert.True(c); + } + + [Fact] + public static void OnEndsWithOfEqualSpansMakeSureEveryElementIsCompared() + { + for (int length = 0; length < 100; length++) + { + TIntLog log = new TIntLog(); + + TInt[] first = new TInt[length]; + TInt[] second = new TInt[length]; + for (int i = 0; i < length; i++) + { + first[i] = second[i] = new TInt(10 * (i + 1), log); + } + + ReadOnlySpan firstSpan = new ReadOnlySpan(first); + ReadOnlySpan secondSpan = new ReadOnlySpan(second); + bool b = firstSpan.EndsWith(secondSpan); + Assert.True(b); + + // Make sure each element of the array was compared once. (Strictly speaking, it would not be illegal for + // EndsWith to compare an element more than once but that would be a non-optimal implementation and + // a red flag. So we'll stick with the stricter test.) + Assert.Equal(first.Length, log.Count); + foreach (TInt elem in first) + { + int numCompares = log.CountCompares(elem.Value, elem.Value); + Assert.True(numCompares == 1, $"Expected {numCompares} == 1 for element {elem.Value}."); + } + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/EndsWith.byte.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/EndsWith.byte.cs new file mode 100644 index 0000000000..45f4712b28 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/EndsWith.byte.cs @@ -0,0 +1,104 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + [Fact] + public static void ZeroLengthEndsWith_Byte() + { + byte[] a = new byte[3]; + + ReadOnlySpan span = new ReadOnlySpan(a); + ReadOnlySpan slice = new ReadOnlySpan(a, 2, 0); + bool b = span.EndsWith(slice); + Assert.True(b); + } + + [Fact] + public static void SameSpanEndsWith_Byte() + { + byte[] a = { 4, 5, 6 }; + ReadOnlySpan span = new ReadOnlySpan(a); + bool b = span.EndsWith(span); + Assert.True(b); + } + + [Fact] + public static void LengthMismatchEndsWith_Byte() + { + byte[] a = { 4, 5, 6 }; + ReadOnlySpan span = new ReadOnlySpan(a, 0, 2); + ReadOnlySpan slice = new ReadOnlySpan(a, 0, 3); + bool b = span.EndsWith(slice); + Assert.False(b); + } + + [Fact] + public static void EndsWithMatch_Byte() + { + byte[] a = { 4, 5, 6 }; + ReadOnlySpan span = new ReadOnlySpan(a, 0, 3); + ReadOnlySpan slice = new ReadOnlySpan(a, 1, 2); + bool b = span.EndsWith(slice); + Assert.True(b); + } + + [Fact] + public static void EndsWithMatchDifferentSpans_Byte() + { + byte[] a = { 4, 5, 6 }; + byte[] b = { 4, 5, 6 }; + ReadOnlySpan span = new ReadOnlySpan(a, 0, 3); + ReadOnlySpan slice = new ReadOnlySpan(b, 0, 3); + bool c = span.EndsWith(slice); + Assert.True(c); + } + + [Fact] + public static void EndsWithNoMatch_Byte() + { + for (int length = 1; length < 32; length++) + { + for (int mismatchIndex = 0; mismatchIndex < length; mismatchIndex++) + { + byte[] first = new byte[length]; + byte[] second = new byte[length]; + for (int i = 0; i < length; i++) + { + first[i] = second[i] = (byte)(i + 1); + } + + second[mismatchIndex] = (byte)(second[mismatchIndex] + 1); + + ReadOnlySpan firstSpan = new ReadOnlySpan(first); + ReadOnlySpan secondSpan = new ReadOnlySpan(second); + bool b = firstSpan.EndsWith(secondSpan); + Assert.False(b); + } + } + } + + [Fact] + public static void MakeSureNoEndsWithChecksGoOutOfRange_Byte() + { + for (int length = 0; length < 100; length++) + { + byte[] first = new byte[length + 2]; + first[0] = 99; + first[length + 1] = 99; + byte[] second = new byte[length + 2]; + second[0] = 100; + second[length + 1] = 100; + ReadOnlySpan span1 = new ReadOnlySpan(first, 1, length); + ReadOnlySpan span2 = new ReadOnlySpan(second, 1, length); + bool b = span1.EndsWith(span2); + Assert.True(b); + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/GetEnumerator.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/GetEnumerator.cs new file mode 100644 index 0000000000..d2df5457cc --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/GetEnumerator.cs @@ -0,0 +1,61 @@ +// 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 Xunit; +using System.Linq; +using System.Collections.Generic; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + public static IEnumerable IntegerArrays() + { + yield return new object[] { new int[0] }; + yield return new object[] { new int[] { 42 } }; + yield return new object[] { new int[] { 42, 43, 44, 45 } }; + } + + [Theory] + [MemberData(nameof(IntegerArrays))] + public static void GetEnumerator_ForEach_AllValuesReturnedCorrectly(int[] array) + { + ReadOnlySpan span = array; + + int sum = 0; + foreach (int i in span) + { + sum += i; + } + + Assert.Equal(Enumerable.Sum(array), sum); + } + + [Theory] + [MemberData(nameof(IntegerArrays))] + public static void GetEnumerator_Manual_AllValuesReturnedCorrectly(int[] array) + { + ReadOnlySpan span = array; + + int sum = 0; + ReadOnlySpan.Enumerator e = span.GetEnumerator(); + while (e.MoveNext()) + { + ref readonly int i = ref e.Current; + sum += i; + Assert.Equal(e.Current, e.Current); + } + Assert.False(e.MoveNext()); + + Assert.Equal(Enumerable.Sum(array), sum); + } + + [Fact] + public static void GetEnumerator_MoveNextOnDefault_ReturnsFalse() + { + Assert.False(default(ReadOnlySpan.Enumerator).MoveNext()); + Assert.ThrowsAny(() => default(ReadOnlySpan.Enumerator).Current); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/ImplicitConversion.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/ImplicitConversion.cs new file mode 100644 index 0000000000..ba579c99e6 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/ImplicitConversion.cs @@ -0,0 +1,27 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + [Fact] + public static void NullImplicitCast() + { + int[] dst = null; + ReadOnlySpan srcSpan = dst; + Assert.True(ReadOnlySpan.Empty == srcSpan); + } + + [Fact] + public static void ArraySegmentDefaultImplicitCast() + { + ArraySegment dst = default; + ReadOnlySpan srcSpan = dst; + Assert.True(ReadOnlySpan.Empty == srcSpan); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/IndexOf.T.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/IndexOf.T.cs index 3ace67ee0e..2c80369662 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlySpan/IndexOf.T.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/IndexOf.T.cs @@ -27,7 +27,7 @@ namespace System.SpanTests a[i] = 10 * (i + 1); } ReadOnlySpan span = new ReadOnlySpan(a); - + for (int targetIndex = 0; targetIndex < length; targetIndex++) { int target = a[targetIndex]; @@ -116,5 +116,74 @@ namespace System.SpanTests Assert.Equal(-1, idx); } } + + [Fact] + public static void ZeroLengthIndexOf_String() + { + ReadOnlySpan sp = new ReadOnlySpan(Array.Empty()); + int idx = sp.IndexOf("a"); + Assert.Equal(-1, idx); + } + + [Fact] + public static void TestMatchIndexOf_String() + { + for (int length = 0; length < 32; length++) + { + string[] a = new string[length]; + for (int i = 0; i < length; i++) + { + a[i] = (10 * (i + 1)).ToString(); + } + ReadOnlySpan span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + string target = a[targetIndex]; + int idx = span.IndexOf(target); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestNoMatchIndexOf_String() + { + var rnd = new Random(42); + for (int length = 0; length <= byte.MaxValue; length++) + { + string[] a = new string[length]; + string target = (rnd.Next(0, 256)).ToString(); + for (int i = 0; i < length; i++) + { + string val = (i + 1).ToString(); + a[i] = val == target ? (target + 1) : val; + } + ReadOnlySpan span = new ReadOnlySpan(a); + + int idx = span.IndexOf(target); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchIndexOf_String() + { + for (int length = 2; length < 32; length++) + { + string[] a = new string[length]; + for (int i = 0; i < length; i++) + { + a[i] = (10 * (i + 1)).ToString(); + } + + a[length - 1] = "5555"; + a[length - 2] = "5555"; + + ReadOnlySpan span = new ReadOnlySpan(a); + int idx = span.IndexOf("5555"); + Assert.Equal(length - 2, idx); + } + } } } diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/IndexOf.byte.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/IndexOf.byte.cs index 09182a4bc1..d587c3738c 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlySpan/IndexOf.byte.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/IndexOf.byte.cs @@ -14,7 +14,7 @@ namespace System.SpanTests public static void ZeroLengthIndexOf_Byte() { ReadOnlySpan sp = new ReadOnlySpan(Array.Empty()); - int idx = sp.IndexOf(0); + int idx = sp.IndexOf(0); Assert.Equal(-1, idx); } @@ -28,7 +28,7 @@ namespace System.SpanTests for (int i = 0; i < length; i++) { - byte target0 = default(byte); + byte target0 = default; int idx = span.IndexOf(target0); Assert.Equal(0, idx); } @@ -103,11 +103,11 @@ namespace System.SpanTests for (var i = 0; i < Vector.Count; i++) { var span = new ReadOnlySpan(array, i, 3 * Vector.Count); - int idx = span.IndexOf(5); + int idx = span.IndexOf(5); Assert.Equal(0, idx); span = new ReadOnlySpan(array, i, 3 * Vector.Count - 3); - idx = span.IndexOf(5); + idx = span.IndexOf(5); Assert.Equal(0, idx); } } @@ -128,7 +128,7 @@ namespace System.SpanTests a[length - 2] = 200; ReadOnlySpan span = new ReadOnlySpan(a); - int idx = span.IndexOf(200); + int idx = span.IndexOf(200); Assert.Equal(length - 2, idx); } } @@ -142,516 +142,7 @@ namespace System.SpanTests a[0] = 99; a[length + 1] = 99; ReadOnlySpan span = new ReadOnlySpan(a, 1, length); - int index = span.IndexOf(99); - Assert.Equal(-1, index); - } - } - - [Theory] - [InlineData("a", "a", 'a', 0)] - [InlineData("ab", "a", 'a', 0)] - [InlineData("aab", "a", 'a', 0)] - [InlineData("acab", "a", 'a', 0)] - [InlineData("acab", "c", 'c', 1)] - [InlineData("abcdefghijklmnopqrstuvwxyz", "lo", 'l', 11)] - [InlineData("abcdefghijklmnopqrstuvwxyz", "ol", 'l', 11)] - [InlineData("abcdefghijklmnopqrstuvwxyz", "ll", 'l', 11)] - [InlineData("abcdefghijklmnopqrstuvwxyz", "lmr", 'l', 11)] - [InlineData("abcdefghijklmnopqrstuvwxyz", "rml", 'l', 11)] - [InlineData("abcdefghijklmnopqrstuvwxyz", "mlr", 'l', 11)] - [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "lmr", 'l', 11)] - [InlineData("aaaaaaaaaaalmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "lmr", 'l', 11)] - [InlineData("aaaaaaaaaaacmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "lmr", 'm', 12)] - [InlineData("aaaaaaaaaaarmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "lmr", 'r', 11)] - [InlineData("/localhost:5000/PATH/%2FPATH2/ HTTP/1.1", " %?", '%', 21)] - [InlineData("/localhost:5000/PATH/%2FPATH2/?key=value HTTP/1.1", " %?", '%', 21)] - [InlineData("/localhost:5000/PATH/PATH2/?key=value HTTP/1.1", " %?", '?', 27)] - [InlineData("/localhost:5000/PATH/PATH2/ HTTP/1.1", " %?", ' ', 27)] - public static void IndexOfAnyStrings_Byte(string raw, string search, char expectResult, int expectIndex) - { - var buffers = Encoding.UTF8.GetBytes(raw); - var span = new ReadOnlySpan(buffers); - var searchFor = search.ToCharArray(); - var searchForBytes = Encoding.UTF8.GetBytes(searchFor); - - var index = -1; - if (searchFor.Length == 1) - { - index = span.IndexOf((byte)searchFor[0]); - } - else if (searchFor.Length == 2) - { - index = span.IndexOfAny((byte)searchFor[0], (byte)searchFor[1]); - } - else if (searchFor.Length == 3) - { - index = span.IndexOfAny((byte)searchFor[0], (byte)searchFor[1], (byte)searchFor[2]); - } - else - { - index = span.IndexOfAny(new ReadOnlySpan(searchForBytes)); - } - - var found = span[index]; - Assert.Equal((byte)expectResult, found); - Assert.Equal(expectIndex, index); - } - - [Fact] - public static void ZeroLengthIndexOfTwo_Byte() - { - ReadOnlySpan sp = new ReadOnlySpan(Array.Empty()); - int idx = sp.IndexOfAny(0, 0); - Assert.Equal(-1, idx); - } - - [Fact] - public static void DefaultFilledIndexOfTwo_Byte() - { - Random rnd = new Random(42); - - for (int length = 0; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - ReadOnlySpan span = new ReadOnlySpan(a); - - byte[] targets = { default(byte), 99 }; - - for (int i = 0; i < length; i++) - { - int index = rnd.Next(0, 2) == 0 ? 0 : 1; - byte target0 = targets[index]; - byte target1 = targets[(index + 1) % 2]; - int idx = span.IndexOfAny(target0, target1); - Assert.Equal(0, idx); - } - } - } - - [Fact] - public static void TestMatchTwo_Byte() - { - for (int length = 0; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - for (int i = 0; i < length; i++) - { - a[i] = (byte)(i + 1); - } - ReadOnlySpan span = new ReadOnlySpan(a); - - for (int targetIndex = 0; targetIndex < length; targetIndex++) - { - byte target0 = a[targetIndex]; - byte target1 = 0; - int idx = span.IndexOfAny(target0, target1); - Assert.Equal(targetIndex, idx); - } - - for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) - { - byte target0 = a[targetIndex]; - byte target1 = a[targetIndex + 1]; - int idx = span.IndexOfAny(target0, target1); - Assert.Equal(targetIndex, idx); - } - - for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) - { - byte target0 = 0; - byte target1 = a[targetIndex + 1]; - int idx = span.IndexOfAny(target0, target1); - Assert.Equal(targetIndex + 1, idx); - } - } - } - - [Fact] - public static void TestNoMatchTwo_Byte() - { - var rnd = new Random(42); - for (int length = 0; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - byte target0 = (byte)rnd.Next(1, 256); - byte target1 = (byte)rnd.Next(1, 256); - ReadOnlySpan span = new ReadOnlySpan(a); - - int idx = span.IndexOfAny(target0, target1); - Assert.Equal(-1, idx); - } - } - - [Fact] - public static void TestMultipleMatchTwo_Byte() - { - for (int length = 3; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - for (int i = 0; i < length; i++) - { - byte val = (byte)(i + 1); - a[i] = val == 200 ? (byte)201 : val; - } - - a[length - 1] = 200; - a[length - 2] = 200; - a[length - 3] = 200; - - ReadOnlySpan span = new ReadOnlySpan(a); - int idx = span.IndexOfAny(200, 200); - Assert.Equal(length - 3, idx); - } - } - - [Fact] - public static void MakeSureNoChecksGoOutOfRangeTwo_Byte() - { - for (int length = 1; length < byte.MaxValue; length++) - { - byte[] a = new byte[length + 2]; - a[0] = 99; - a[length + 1] = 98; - ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); - int index = span.IndexOfAny(99, 98); - Assert.Equal(-1, index); - } - - for (int length = 1; length < byte.MaxValue; length++) - { - byte[] a = new byte[length + 2]; - a[0] = 99; - a[length + 1] = 99; - ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); - int index = span.IndexOfAny(99, 99); - Assert.Equal(-1, index); - } - } - - [Fact] - public static void ZeroLengthIndexOfThree_Byte() - { - ReadOnlySpan sp = new ReadOnlySpan(Array.Empty()); - int idx = sp.IndexOfAny(0, 0, 0); - Assert.Equal(-1, idx); - } - - [Fact] - public static void DefaultFilledIndexOfThree_Byte() - { - Random rnd = new Random(42); - - for (int length = 0; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - ReadOnlySpan span = new ReadOnlySpan(a); - - byte[] targets = { default(byte), 99, 98 }; - - for (int i = 0; i < length; i++) - { - int index = rnd.Next(0, 3); - byte target0 = targets[index]; - byte target1 = targets[(index + 1) % 2]; - byte target2 = targets[(index + 1) % 3]; - int idx = span.IndexOfAny(target0, target1, target2); - Assert.Equal(0, idx); - } - } - } - - [Fact] - public static void TestMatchThree_Byte() - { - for (int length = 0; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - for (int i = 0; i < length; i++) - { - a[i] = (byte)(i + 1); - } - ReadOnlySpan span = new ReadOnlySpan(a); - - for (int targetIndex = 0; targetIndex < length; targetIndex++) - { - byte target0 = a[targetIndex]; - byte target1 = 0; - byte target2 = 0; - int idx = span.IndexOfAny(target0, target1, target2); - Assert.Equal(targetIndex, idx); - } - - for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) - { - byte target0 = a[targetIndex]; - byte target1 = a[targetIndex + 1]; - byte target2 = a[targetIndex + 2]; - int idx = span.IndexOfAny(target0, target1, target2); - Assert.Equal(targetIndex, idx); - } - - for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) - { - byte target0 = 0; - byte target1 = 0; - byte target2 = a[targetIndex + 2]; - int idx = span.IndexOfAny(target0, target1, target2); - Assert.Equal(targetIndex + 2, idx); - } - } - } - - [Fact] - public static void TestNoMatchThree_Byte() - { - var rnd = new Random(42); - for (int length = 0; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - byte target0 = (byte)rnd.Next(1, 256); - byte target1 = (byte)rnd.Next(1, 256); - byte target2 = (byte)rnd.Next(1, 256); - ReadOnlySpan span = new ReadOnlySpan(a); - - int idx = span.IndexOfAny(target0, target1, target2); - Assert.Equal(-1, idx); - } - } - - [Fact] - public static void TestMultipleMatchThree_Byte() - { - for (int length = 4; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - for (int i = 0; i < length; i++) - { - byte val = (byte)(i + 1); - a[i] = val == 200 ? (byte)201 : val; - } - - a[length - 1] = 200; - a[length - 2] = 200; - a[length - 3] = 200; - a[length - 4] = 200; - - ReadOnlySpan span = new ReadOnlySpan(a); - int idx = span.IndexOfAny(200, 200, 200); - Assert.Equal(length - 4, idx); - } - } - - [Fact] - public static void MakeSureNoChecksGoOutOfRangeThree_Byte() - { - for (int length = 1; length < byte.MaxValue; length++) - { - byte[] a = new byte[length + 2]; - a[0] = 99; - a[length + 1] = 98; - ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); - int index = span.IndexOfAny(99, 98, 99); - Assert.Equal(-1, index); - } - - for (int length = 1; length < byte.MaxValue; length++) - { - byte[] a = new byte[length + 2]; - a[0] = 99; - a[length + 1] = 99; - ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); - int index = span.IndexOfAny(99, 99, 99); - Assert.Equal(-1, index); - } - } - - [Fact] - public static void ZeroLengthIndexOfMany_Byte() - { - ReadOnlySpan sp = new ReadOnlySpan(Array.Empty()); - var values = new ReadOnlySpan(new byte[] { 0, 0, 0, 0 }); - int idx = sp.IndexOfAny(values); - Assert.Equal(-1, idx); - - values = new ReadOnlySpan(new byte[] { }); - idx = sp.IndexOfAny(values); - Assert.Equal(0, idx); - } - - [Fact] - public static void DefaultFilledIndexOfMany_Byte() - { - for (int length = 0; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - ReadOnlySpan span = new ReadOnlySpan(a); - - var values = new ReadOnlySpan(new byte[] { default(byte), 99, 98, 0 }); - - for (int i = 0; i < length; i++) - { - int idx = span.IndexOfAny(values); - Assert.Equal(0, idx); - } - } - } - - [Fact] - public static void TestMatchMany_Byte() - { - for (int length = 0; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - for (int i = 0; i < length; i++) - { - a[i] = (byte)(i + 1); - } - ReadOnlySpan span = new ReadOnlySpan(a); - - for (int targetIndex = 0; targetIndex < length; targetIndex++) - { - var values = new ReadOnlySpan(new byte[] { a[targetIndex], 0, 0, 0 }); - int idx = span.IndexOfAny(values); - Assert.Equal(targetIndex, idx); - } - - for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) - { - var values = new ReadOnlySpan(new byte[] { a[targetIndex], a[targetIndex + 1], a[targetIndex + 2], a[targetIndex + 3] }); - int idx = span.IndexOfAny(values); - Assert.Equal(targetIndex, idx); - } - - for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) - { - var values = new ReadOnlySpan(new byte[] { 0, 0, 0, a[targetIndex + 3] }); - int idx = span.IndexOfAny(values); - Assert.Equal(targetIndex + 3, idx); - } - } - } - - [Fact] - public static void TestMatchValuesLargerMany_Byte() - { - var rnd = new Random(42); - for (int length = 2; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - int expectedIndex = length / 2; - for (int i = 0; i < length; i++) - { - if (i == expectedIndex) - { - continue; - } - a[i] = 255; - } - ReadOnlySpan span = new ReadOnlySpan(a); - - byte[] targets = new byte[length * 2]; - for (int i = 0; i < targets.Length; i++) - { - if (i == length + 1) - { - continue; - } - targets[i] = (byte)rnd.Next(1, 255); - } - - var values = new ReadOnlySpan(targets); - int idx = span.IndexOfAny(values); - Assert.Equal(expectedIndex, idx); - } - } - - [Fact] - public static void TestNoMatchMany_Byte() - { - var rnd = new Random(42); - for (int length = 1; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - byte[] targets = new byte[length]; - for (int i = 0; i < targets.Length; i++) - { - targets[i] = (byte)rnd.Next(1, 256); - } - ReadOnlySpan span = new ReadOnlySpan(a); - var values = new ReadOnlySpan(targets); - - int idx = span.IndexOfAny(values); - Assert.Equal(-1, idx); - } - } - - [Fact] - public static void TestNoMatchValuesLargerMany_Byte() - { - var rnd = new Random(42); - for (int length = 1; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - byte[] targets = new byte[length * 2]; - for (int i = 0; i < targets.Length; i++) - { - targets[i] = (byte)rnd.Next(1, 256); - } - ReadOnlySpan span = new ReadOnlySpan(a); - var values = new ReadOnlySpan(targets); - - int idx = span.IndexOfAny(values); - Assert.Equal(-1, idx); - } - } - - [Fact] - public static void TestMultipleMatchMany_Byte() - { - for (int length = 5; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - for (int i = 0; i < length; i++) - { - byte val = (byte)(i + 1); - a[i] = val == 200 ? (byte)201 : val; - } - - a[length - 1] = 200; - a[length - 2] = 200; - a[length - 3] = 200; - a[length - 4] = 200; - a[length - 5] = 200; - - ReadOnlySpan span = new ReadOnlySpan(a); - var values = new ReadOnlySpan(new byte[] { 200, 200, 200, 200, 200, 200, 200, 200, 200 }); - int idx = span.IndexOfAny(values); - Assert.Equal(length - 5, idx); - } - } - - [Fact] - public static void MakeSureNoChecksGoOutOfRangeMany_Byte() - { - for (int length = 1; length < byte.MaxValue; length++) - { - byte[] a = new byte[length + 2]; - a[0] = 99; - a[length + 1] = 98; - ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); - var values = new ReadOnlySpan(new byte[] { 99, 98, 99, 98, 99, 98 }); - int index = span.IndexOfAny(values); - Assert.Equal(-1, index); - } - - for (int length = 1; length < byte.MaxValue; length++) - { - byte[] a = new byte[length + 2]; - a[0] = 99; - a[length + 1] = 99; - ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); - var values = new ReadOnlySpan(new byte[] { 99, 99, 99, 99, 99, 99 }); - int index = span.IndexOfAny(values); + int index = span.IndexOf(99); Assert.Equal(-1, index); } } diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/IndexOf.char.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/IndexOf.char.cs index 24ce08b73e..2e71529b10 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlySpan/IndexOf.char.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/IndexOf.char.cs @@ -27,7 +27,7 @@ namespace System.SpanTests a[i] = (char)(i + 1); } ReadOnlySpan span = new ReadOnlySpan(a); - + for (int targetIndex = 0; targetIndex < length; targetIndex++) { char target = a[targetIndex]; diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/IndexOfAny.T.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/IndexOfAny.T.cs new file mode 100644 index 0000000000..bfc1732a18 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/IndexOfAny.T.cs @@ -0,0 +1,967 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + [Fact] + public static void ZeroLengthIndexOfAny_TwoInteger() + { + var sp = new ReadOnlySpan(Array.Empty()); + int idx = sp.IndexOfAny(0, 0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledIndexOfAny_TwoInteger() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + var span = new ReadOnlySpan(a); + + int[] targets = { default, 99 }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 2) == 0 ? 0 : 1; + int target0 = targets[index]; + int target1 = targets[(index + 1) % 2]; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(0, idx); + } + } + } + + [Fact] + public static void TestMatchIndexOfAny_TwoInteger() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + a[i] = i + 1; + } + var span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + int target0 = a[targetIndex]; + int target1 = 0; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + int index = rnd.Next(0, 2) == 0 ? 0 : 1; + int target0 = a[targetIndex + index]; + int target1 = a[targetIndex + (index + 1) % 2]; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + int target0 = 0; + int target1 = a[targetIndex]; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestNoMatchIndexOfAny_TwoInteger() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + int target0 = rnd.Next(1, 256); + int target1 = rnd.Next(1, 256); + var span = new ReadOnlySpan(a); + + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchIndexOfAny_TwoInteger() + { + for (int length = 3; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + int val = i + 1; + a[i] = val == 200 ? 201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + + var span = new ReadOnlySpan(a); + int idx = span.IndexOfAny(200, 200); + Assert.Equal(length - 3, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeIndexOfAny_TwoInteger() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 98; + var span = new ReadOnlySpan(a, 1, length - 1); + int index = span.IndexOfAny(99, 98); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 99; + var span = new ReadOnlySpan(a, 1, length - 1); + int index = span.IndexOfAny(99, 99); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthIndexOfAny_ThreeInteger() + { + var sp = new ReadOnlySpan(Array.Empty()); + int idx = sp.IndexOfAny(0, 0, 0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledIndexOfAny_ThreeInteger() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + var span = new ReadOnlySpan(a); + + int[] targets = { default, 99, 98 }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 3); + int target0 = targets[index]; + int target1 = targets[(index + 1) % 2]; + int target2 = targets[(index + 1) % 3]; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(0, idx); + } + } + } + + [Fact] + public static void TestMatchIndexOfAny_ThreeInteger() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + a[i] = i + 1; + } + var span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + int target0 = a[targetIndex]; + int target1 = 0; + int target2 = 0; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + int index = rnd.Next(0, 3); + int target0 = a[targetIndex + index]; + int target1 = a[targetIndex + (index + 1) % 2]; + int target2 = a[targetIndex + (index + 1) % 3]; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + int target0 = 0; + int target1 = 0; + int target2 = a[targetIndex]; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestNoMatchIndexOfAny_ThreeInteger() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + int target0 = rnd.Next(1, 256); + int target1 = rnd.Next(1, 256); + int target2 = rnd.Next(1, 256); + var span = new ReadOnlySpan(a); + + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchIndexOfAny_ThreeInteger() + { + for (int length = 4; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + int val = i + 1; + a[i] = val == 200 ? 201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + a[length - 4] = 200; + + var span = new ReadOnlySpan(a); + int idx = span.IndexOfAny(200, 200, 200); + Assert.Equal(length - 4, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeIndexOfAny_ThreeInteger() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 98; + var span = new ReadOnlySpan(a, 1, length - 1); + int index = span.IndexOfAny(99, 98, 99); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 99; + var span = new ReadOnlySpan(a, 1, length - 1); + int index = span.IndexOfAny(99, 99, 99); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthIndexOfAny_ManyInteger() + { + var sp = new ReadOnlySpan(Array.Empty()); + var values = new ReadOnlySpan(new int[] { 0, 0, 0, 0 }); + int idx = sp.IndexOfAny(values); + Assert.Equal(-1, idx); + + values = new ReadOnlySpan(new int[] { }); + idx = sp.IndexOfAny(values); + Assert.Equal(0, idx); + } + + [Fact] + public static void DefaultFilledIndexOfAny_ManyInteger() + { + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + var span = new ReadOnlySpan(a); + + var values = new ReadOnlySpan(new int[] { default, 99, 98, 0 }); + + for (int i = 0; i < length; i++) + { + int idx = span.IndexOfAny(values); + Assert.Equal(0, idx); + } + } + } + + [Fact] + public static void TestMatchIndexOfAny_ManyInteger() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + a[i] = i + 1; + } + var span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + var values = new ReadOnlySpan(new int[] { a[targetIndex], 0, 0, 0 }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + int index = rnd.Next(0, 4) == 0 ? 0 : 1; + var values = new ReadOnlySpan(new int[] + { + a[targetIndex + index], + a[targetIndex + (index + 1) % 2], + a[targetIndex + (index + 1) % 3], + a[targetIndex + (index + 1) % 4] + }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + var values = new ReadOnlySpan(new int[] { 0, 0, 0, a[targetIndex] }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestMatchValuesLargerIndexOfAny_ManyInteger() + { + var rnd = new Random(42); + for (int length = 2; length < byte.MaxValue; length++) + { + var a = new int[length]; + int expectedIndex = length / 2; + for (int i = 0; i < length; i++) + { + if (i == expectedIndex) + { + continue; + } + a[i] = 255; + } + var span = new ReadOnlySpan(a); + + var targets = new int[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + if (i == length + 1) + { + continue; + } + targets[i] = rnd.Next(1, 255); + } + + var values = new ReadOnlySpan(targets); + int idx = span.IndexOfAny(values); + Assert.Equal(expectedIndex, idx); + } + } + + [Fact] + public static void TestNoMatchIndexOfAny_ManyInteger() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length]; + var targets = new int[length]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = rnd.Next(1, 256); + } + var span = new ReadOnlySpan(a); + var values = new ReadOnlySpan(targets); + + int idx = span.IndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestNoMatchValuesLargerIndexOfAny_ManyInteger() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length]; + var targets = new int[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = rnd.Next(1, 256); + } + var span = new ReadOnlySpan(a); + var values = new ReadOnlySpan(targets); + + int idx = span.IndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchIndexOfAny_ManyInteger() + { + for (int length = 5; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + int val = i + 1; + a[i] = val == 200 ? 201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + a[length - 4] = 200; + a[length - 5] = 200; + + var span = new ReadOnlySpan(a); + var values = new ReadOnlySpan(new int[] { 200, 200, 200, 200, 200, 200, 200, 200, 200 }); + int idx = span.IndexOfAny(values); + Assert.Equal(length - 5, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeIndexOfAny_ManyInteger() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 98; + var span = new ReadOnlySpan(a, 1, length - 1); + var values = new ReadOnlySpan(new int[] { 99, 98, 99, 98, 99, 98 }); + int index = span.IndexOfAny(values); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 99; + var span = new ReadOnlySpan(a, 1, length - 1); + var values = new ReadOnlySpan(new int[] { 99, 99, 99, 99, 99, 99 }); + int index = span.IndexOfAny(values); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthIndexOfAny_TwoString() + { + var sp = new ReadOnlySpan(Array.Empty()); + int idx = sp.IndexOfAny("0", "0"); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledIndexOfAny_TwoString() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + var tempSpan = new Span(a); + tempSpan.Fill(""); + ReadOnlySpan span = tempSpan; + + string[] targets = { "", "99" }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 2) == 0 ? 0 : 1; + string target0 = targets[index]; + string target1 = targets[(index + 1) % 2]; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(0, idx); + } + } + } + + [Fact] + public static void TestMatchIndexOfAny_TwoString() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + a[i] = (i + 1).ToString(); + } + var span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + string target0 = a[targetIndex]; + string target1 = "0"; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + int index = rnd.Next(0, 2) == 0 ? 0 : 1; + string target0 = a[targetIndex + index]; + string target1 = a[targetIndex + (index + 1) % 2]; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + string target0 = "0"; + string target1 = a[targetIndex + 1]; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(targetIndex + 1, idx); + } + } + } + + [Fact] + public static void TestNoMatchIndexOfAny_TwoString() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + string target0 = rnd.Next(1, 256).ToString(); + string target1 = rnd.Next(1, 256).ToString(); + var span = new ReadOnlySpan(a); + + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchIndexOfAny_TwoString() + { + for (int length = 3; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + string val = (i + 1).ToString(); + a[i] = val == "200" ? "201" : val; + } + + a[length - 1] = "200"; + a[length - 2] = "200"; + a[length - 3] = "200"; + + var span = new ReadOnlySpan(a); + int idx = span.IndexOfAny("200", "200"); + Assert.Equal(length - 3, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeIndexOfAny_TwoString() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "98"; + var span = new ReadOnlySpan(a, 1, length - 1); + int index = span.IndexOfAny("99", "98"); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "99"; + var span = new ReadOnlySpan(a, 1, length - 1); + int index = span.IndexOfAny("99", "99"); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthIndexOf_ThreeString() + { + var sp = new ReadOnlySpan(Array.Empty()); + int idx = sp.IndexOfAny("0", "0", "0"); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledIndexOfAny_ThreeString() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + var tempSpan = new Span(a); + tempSpan.Fill(""); + ReadOnlySpan span = tempSpan; + + string[] targets = { "", "99", "98" }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 3); + string target0 = targets[index]; + string target1 = targets[(index + 1) % 2]; + string target2 = targets[(index + 1) % 3]; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(0, idx); + } + } + } + + [Fact] + public static void TestMatchIndexOfAny_ThreeString() + { + Random rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + a[i] = (i + 1).ToString(); + } + var span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + string target0 = a[targetIndex]; + string target1 = "0"; + string target2 = "0"; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + int index = rnd.Next(0, 3) == 0 ? 0 : 1; + string target0 = a[targetIndex + index]; + string target1 = a[targetIndex + (index + 1) % 2]; + string target2 = a[targetIndex + (index + 1) % 3]; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + string target0 = "0"; + string target1 = "0"; + string target2 = a[targetIndex]; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestNoMatchIndexOfAny_ThreeString() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + string target0 = rnd.Next(1, 256).ToString(); + string target1 = rnd.Next(1, 256).ToString(); + string target2 = rnd.Next(1, 256).ToString(); + var span = new ReadOnlySpan(a); + + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchIndexOfAny_ThreeString() + { + for (int length = 4; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + string val = (i + 1).ToString(); + a[i] = val == "200" ? "201" : val; + } + + a[length - 1] = "200"; + a[length - 2] = "200"; + a[length - 3] = "200"; + a[length - 4] = "200"; + + var span = new ReadOnlySpan(a); + int idx = span.IndexOfAny("200", "200", "200"); + Assert.Equal(length - 4, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeIndexOfAny_ThreeString() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "98"; + var span = new ReadOnlySpan(a, 1, length - 1); + int index = span.IndexOfAny("99", "98", "99"); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "99"; + var span = new ReadOnlySpan(a, 1, length - 1); + int index = span.IndexOfAny("99", "99", "99"); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthIndexOfAny_ManyString() + { + var sp = new ReadOnlySpan(Array.Empty()); + var values = new ReadOnlySpan(new string[] { "0", "0", "0", "0" }); + int idx = sp.IndexOfAny(values); + Assert.Equal(-1, idx); + + values = new ReadOnlySpan(new string[] { }); + idx = sp.IndexOfAny(values); + Assert.Equal(0, idx); + } + + [Fact] + public static void DefaultFilledIndexOfAny_ManyString() + { + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + var tempSpan = new Span(a); + tempSpan.Fill(""); + ReadOnlySpan span = tempSpan; + + var values = new ReadOnlySpan(new string[] { "", "99", "98", "0" }); + + for (int i = 0; i < length; i++) + { + int idx = span.IndexOfAny(values); + Assert.Equal(0, idx); + } + } + } + + [Fact] + public static void TestMatchIndexOfAny_ManyString() + { + Random rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + a[i] = (i + 1).ToString(); + } + var span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + var values = new ReadOnlySpan(new string[] { a[targetIndex], "0", "0", "0" }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + int index = rnd.Next(0, 4) == 0 ? 0 : 1; + var values = new ReadOnlySpan(new string[] + { + a[targetIndex + index], + a[targetIndex + (index + 1) % 2], + a[targetIndex + (index + 1) % 3], + a[targetIndex + (index + 1) % 4] + }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + var values = new ReadOnlySpan(new string[] { "0", "0", "0", a[targetIndex] }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestMatchValuesLargerIndexOfAny_ManyString() + { + var rnd = new Random(42); + for (int length = 2; length < byte.MaxValue; length++) + { + var a = new string[length]; + int expectedIndex = length / 2; + for (int i = 0; i < length; i++) + { + if (i == expectedIndex) + { + a[i] = "val"; + continue; + } + a[i] = "255"; + } + var span = new ReadOnlySpan(a); + + var targets = new string[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + if (i == length + 1) + { + targets[i] = "val"; + continue; + } + targets[i] = rnd.Next(1, 255).ToString(); + } + + var values = new ReadOnlySpan(targets); + int idx = span.IndexOfAny(values); + Assert.Equal(expectedIndex, idx); + } + } + + [Fact] + public static void TestNoMatchIndexOfAny_ManyString() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length]; + var targets = new string[length]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = rnd.Next(1, 256).ToString(); + } + var span = new ReadOnlySpan(a); + var values = new ReadOnlySpan(targets); + + int idx = span.IndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestNoMatchValuesLargerIndexOfAny_ManyString() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length]; + var targets = new string[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = rnd.Next(1, 256).ToString(); + } + var span = new ReadOnlySpan(a); + var values = new ReadOnlySpan(targets); + + int idx = span.IndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchIndexOfAny_ManyString() + { + for (int length = 5; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + string val = (i + 1).ToString(); + a[i] = val == "200" ? "201" : val; + } + + a[length - 1] = "200"; + a[length - 2] = "200"; + a[length - 3] = "200"; + a[length - 4] = "200"; + a[length - 5] = "200"; + + var span = new ReadOnlySpan(a); + var values = new ReadOnlySpan(new string[] { "200", "200", "200", "200", "200", "200", "200", "200", "200" }); + int idx = span.IndexOfAny(values); + Assert.Equal(length - 5, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeIndexOfAny_ManyString() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "98"; + var span = new ReadOnlySpan(a, 1, length - 1); + var values = new ReadOnlySpan(new string[] { "99", "98", "99", "98", "99", "98" }); + int index = span.IndexOfAny(values); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "99"; + var span = new ReadOnlySpan(a, 1, length - 1); + var values = new ReadOnlySpan(new string[] { "99", "99", "99", "99", "99", "99" }); + int index = span.IndexOfAny(values); + Assert.Equal(-1, index); + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/IndexOfAny.byte.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/IndexOfAny.byte.cs new file mode 100644 index 0000000000..d9a15cec68 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/IndexOfAny.byte.cs @@ -0,0 +1,522 @@ +// 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.Numerics; +using System.Text; +using Xunit; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + [Theory] + [InlineData("a", "a", 'a', 0)] + [InlineData("ab", "a", 'a', 0)] + [InlineData("aab", "a", 'a', 0)] + [InlineData("acab", "a", 'a', 0)] + [InlineData("acab", "c", 'c', 1)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "lo", 'l', 11)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "ol", 'l', 11)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "ll", 'l', 11)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "lmr", 'l', 11)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "rml", 'l', 11)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "mlr", 'l', 11)] + [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "lmr", 'l', 11)] + [InlineData("aaaaaaaaaaalmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "lmr", 'l', 11)] + [InlineData("aaaaaaaaaaacmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "lmr", 'm', 12)] + [InlineData("aaaaaaaaaaarmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "lmr", 'r', 11)] + [InlineData("/localhost:5000/PATH/%2FPATH2/ HTTP/1.1", " %?", '%', 21)] + [InlineData("/localhost:5000/PATH/%2FPATH2/?key=value HTTP/1.1", " %?", '%', 21)] + [InlineData("/localhost:5000/PATH/PATH2/?key=value HTTP/1.1", " %?", '?', 27)] + [InlineData("/localhost:5000/PATH/PATH2/ HTTP/1.1", " %?", ' ', 27)] + public static void IndexOfAnyStrings_Byte(string raw, string search, char expectResult, int expectIndex) + { + byte[] buffers = Encoding.UTF8.GetBytes(raw); + var span = new ReadOnlySpan(buffers); + char[] searchFor = search.ToCharArray(); + byte[] searchForBytes = Encoding.UTF8.GetBytes(searchFor); + + var index = -1; + if (searchFor.Length == 1) + { + index = span.IndexOf((byte)searchFor[0]); + } + else if (searchFor.Length == 2) + { + index = span.IndexOfAny((byte)searchFor[0], (byte)searchFor[1]); + } + else if (searchFor.Length == 3) + { + index = span.IndexOfAny((byte)searchFor[0], (byte)searchFor[1], (byte)searchFor[2]); + } + else + { + index = span.IndexOfAny(new ReadOnlySpan(searchForBytes)); + } + + var found = span[index]; + Assert.Equal((byte)expectResult, found); + Assert.Equal(expectIndex, index); + } + + [Fact] + public static void ZeroLengthIndexOfTwo_Byte() + { + ReadOnlySpan sp = new ReadOnlySpan(Array.Empty()); + int idx = sp.IndexOfAny(0, 0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledIndexOfTwo_Byte() + { + Random rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + ReadOnlySpan span = new ReadOnlySpan(a); + + byte[] targets = { default, 99 }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 2) == 0 ? 0 : 1; + byte target0 = targets[index]; + byte target1 = targets[(index + 1) % 2]; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(0, idx); + } + } + } + + [Fact] + public static void TestMatchTwo_Byte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + ReadOnlySpan span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = 0; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = a[targetIndex + 1]; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + byte target0 = 0; + byte target1 = a[targetIndex + 1]; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(targetIndex + 1, idx); + } + } + } + + [Fact] + public static void TestNoMatchTwo_Byte() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + byte target0 = (byte)rnd.Next(1, 256); + byte target1 = (byte)rnd.Next(1, 256); + ReadOnlySpan span = new ReadOnlySpan(a); + + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchTwo_Byte() + { + for (int length = 3; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + byte val = (byte)(i + 1); + a[i] = val == 200 ? (byte)201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + + ReadOnlySpan span = new ReadOnlySpan(a); + int idx = span.IndexOfAny(200, 200); + Assert.Equal(length - 3, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeTwo_Byte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 98; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); + int index = span.IndexOfAny(99, 98); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 99; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); + int index = span.IndexOfAny(99, 99); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthIndexOfThree_Byte() + { + ReadOnlySpan sp = new ReadOnlySpan(Array.Empty()); + int idx = sp.IndexOfAny(0, 0, 0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledIndexOfThree_Byte() + { + Random rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + ReadOnlySpan span = new ReadOnlySpan(a); + + byte[] targets = { default, 99, 98 }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 3); + byte target0 = targets[index]; + byte target1 = targets[(index + 1) % 2]; + byte target2 = targets[(index + 1) % 3]; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(0, idx); + } + } + } + + [Fact] + public static void TestMatchThree_Byte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + ReadOnlySpan span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = 0; + byte target2 = 0; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = a[targetIndex + 1]; + byte target2 = a[targetIndex + 2]; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + byte target0 = 0; + byte target1 = 0; + byte target2 = a[targetIndex + 2]; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex + 2, idx); + } + } + } + + [Fact] + public static void TestNoMatchThree_Byte() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + byte target0 = (byte)rnd.Next(1, 256); + byte target1 = (byte)rnd.Next(1, 256); + byte target2 = (byte)rnd.Next(1, 256); + ReadOnlySpan span = new ReadOnlySpan(a); + + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchThree_Byte() + { + for (int length = 4; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + byte val = (byte)(i + 1); + a[i] = val == 200 ? (byte)201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + a[length - 4] = 200; + + ReadOnlySpan span = new ReadOnlySpan(a); + int idx = span.IndexOfAny(200, 200, 200); + Assert.Equal(length - 4, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeThree_Byte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 98; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); + int index = span.IndexOfAny(99, 98, 99); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 99; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); + int index = span.IndexOfAny(99, 99, 99); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthIndexOfMany_Byte() + { + ReadOnlySpan sp = new ReadOnlySpan(Array.Empty()); + var values = new ReadOnlySpan(new byte[] { 0, 0, 0, 0 }); + int idx = sp.IndexOfAny(values); + Assert.Equal(-1, idx); + + values = new ReadOnlySpan(new byte[] { }); + idx = sp.IndexOfAny(values); + Assert.Equal(0, idx); + } + + [Fact] + public static void DefaultFilledIndexOfMany_Byte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + ReadOnlySpan span = new ReadOnlySpan(a); + + var values = new ReadOnlySpan(new byte[] { default, 99, 98, 0 }); + + for (int i = 0; i < length; i++) + { + int idx = span.IndexOfAny(values); + Assert.Equal(0, idx); + } + } + } + + [Fact] + public static void TestMatchMany_Byte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + ReadOnlySpan span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + var values = new ReadOnlySpan(new byte[] { a[targetIndex], 0, 0, 0 }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + var values = new ReadOnlySpan(new byte[] { a[targetIndex], a[targetIndex + 1], a[targetIndex + 2], a[targetIndex + 3] }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + var values = new ReadOnlySpan(new byte[] { 0, 0, 0, a[targetIndex + 3] }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex + 3, idx); + } + } + } + + [Fact] + public static void TestMatchValuesLargerMany_Byte() + { + var rnd = new Random(42); + for (int length = 2; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + int expectedIndex = length / 2; + for (int i = 0; i < length; i++) + { + if (i == expectedIndex) + { + continue; + } + a[i] = 255; + } + ReadOnlySpan span = new ReadOnlySpan(a); + + byte[] targets = new byte[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + if (i == length + 1) + { + continue; + } + targets[i] = (byte)rnd.Next(1, 255); + } + + var values = new ReadOnlySpan(targets); + int idx = span.IndexOfAny(values); + Assert.Equal(expectedIndex, idx); + } + } + + [Fact] + public static void TestNoMatchMany_Byte() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + byte[] targets = new byte[length]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = (byte)rnd.Next(1, 256); + } + ReadOnlySpan span = new ReadOnlySpan(a); + var values = new ReadOnlySpan(targets); + + int idx = span.IndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestNoMatchValuesLargerMany_Byte() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + byte[] targets = new byte[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = (byte)rnd.Next(1, 256); + } + ReadOnlySpan span = new ReadOnlySpan(a); + var values = new ReadOnlySpan(targets); + + int idx = span.IndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchMany_Byte() + { + for (int length = 5; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + byte val = (byte)(i + 1); + a[i] = val == 200 ? (byte)201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + a[length - 4] = 200; + a[length - 5] = 200; + + ReadOnlySpan span = new ReadOnlySpan(a); + var values = new ReadOnlySpan(new byte[] { 200, 200, 200, 200, 200, 200, 200, 200, 200 }); + int idx = span.IndexOfAny(values); + Assert.Equal(length - 5, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeMany_Byte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 98; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); + var values = new ReadOnlySpan(new byte[] { 99, 98, 99, 98, 99, 98 }); + int index = span.IndexOfAny(values); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 99; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); + var values = new ReadOnlySpan(new byte[] { 99, 99, 99, 99, 99, 99 }); + int index = span.IndexOfAny(values); + Assert.Equal(-1, index); + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/IndexOfSequence.T.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/IndexOfSequence.T.cs index 49a863acb2..401e7c6aec 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlySpan/IndexOfSequence.T.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/IndexOfSequence.T.cs @@ -119,5 +119,117 @@ namespace System.SpanTests int index = span.IndexOf(value); Assert.Equal(-1, index); } + + [Fact] + public static void IndexOfSequenceMatchAtStart_String() + { + ReadOnlySpan span = new ReadOnlySpan(new string[] { "5", "1", "77", "2", "3", "77", "77", "4", "5", "77", "77", "77", "88", "6", "6", "77", "77", "88", "9" }); + ReadOnlySpan value = new ReadOnlySpan(new string[] { "5", "1", "77" }); + int index = span.IndexOf(value); + Assert.Equal(0, index); + } + + [Fact] + public static void IndexOfSequenceMultipleMatch_String() + { + ReadOnlySpan span = new ReadOnlySpan(new string[] { "1", "2", "3", "1", "2", "3", "1", "2", "3" }); + ReadOnlySpan value = new ReadOnlySpan(new string[] { "2", "3" }); + int index = span.IndexOf(value); + Assert.Equal(1, index); + } + + [Fact] + public static void IndexOfSequenceRestart_String() + { + ReadOnlySpan span = new ReadOnlySpan(new string[] { "0", "1", "77", "2", "3", "77", "77", "4", "5", "77", "77", "77", "88", "6", "6", "77", "77", "88", "9" }); + ReadOnlySpan value = new ReadOnlySpan(new string[] { "77", "77", "88" }); + int index = span.IndexOf(value); + Assert.Equal(10, index); + } + + [Fact] + public static void IndexOfSequenceNoMatch_String() + { + ReadOnlySpan span = new ReadOnlySpan(new string[] { "0", "1", "77", "2", "3", "77", "77", "4", "5", "77", "77", "77", "88", "6", "6", "77", "77", "88", "9" }); + ReadOnlySpan value = new ReadOnlySpan(new string[] { "77", "77", "88", "99" }); + int index = span.IndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void IndexOfSequenceNotEvenAHeadMatch_String() + { + ReadOnlySpan span = new ReadOnlySpan(new string[] { "0", "1", "77", "2", "3", "77", "77", "4", "5", "77", "77", "77", "88", "6", "6", "77", "77", "88", "9" }); + ReadOnlySpan value = new ReadOnlySpan(new string[] { "100", "77", "88", "99" }); + int index = span.IndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void IndexOfSequenceMatchAtVeryEnd_String() + { + ReadOnlySpan span = new ReadOnlySpan(new string[] { "0", "1", "2", "3", "4", "5" }); + ReadOnlySpan value = new ReadOnlySpan(new string[] { "3", "4", "5" }); + int index = span.IndexOf(value); + Assert.Equal(3, index); + } + + [Fact] + public static void IndexOfSequenceJustPastVeryEnd_String() + { + ReadOnlySpan span = new ReadOnlySpan(new string[] { "0", "1", "2", "3", "4", "5" }, 0, 5); + ReadOnlySpan value = new ReadOnlySpan(new string[] { "3", "4", "5" }); + int index = span.IndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void IndexOfSequenceZeroLengthValue_String() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new string[] { "0", "1", "77", "2", "3", "77", "77", "4", "5", "77", "77", "77", "88", "6", "6", "77", "77", "88", "9" }); + ReadOnlySpan value = new ReadOnlySpan(Array.Empty()); + int index = span.IndexOf(value); + Assert.Equal(0, index); + } + + [Fact] + public static void IndexOfSequenceZeroLengthSpan_String() + { + ReadOnlySpan span = new ReadOnlySpan(Array.Empty()); + ReadOnlySpan value = new ReadOnlySpan(new string[] { "1", "2", "3" }); + int index = span.IndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void IndexOfSequenceLengthOneValue_String() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new string[] { "0", "1", "2", "3", "4", "5" }); + ReadOnlySpan value = new ReadOnlySpan(new string[] { "2" }); + int index = span.IndexOf(value); + Assert.Equal(2, index); + } + + [Fact] + public static void IndexOfSequenceLengthOneValueAtVeryEnd_String() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new string[] { "0", "1", "2", "3", "4", "5" }); + ReadOnlySpan value = new ReadOnlySpan(new string[] { "5" }); + int index = span.IndexOf(value); + Assert.Equal(5, index); + } + + [Fact] + public static void IndexOfSequenceLengthOneValueJustPasttVeryEnd_String() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new string[] { "0", "1", "2", "3", "4", "5" }, 0, 5); + ReadOnlySpan value = new ReadOnlySpan(new string[] { "5" }); + int index = span.IndexOf(value); + Assert.Equal(-1, index); + } } } diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOf.T.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOf.T.cs new file mode 100644 index 0000000000..9738142626 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOf.T.cs @@ -0,0 +1,189 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + [Fact] + public static void ZeroLengthLastIndexOf() + { + ReadOnlySpan sp = new ReadOnlySpan(Array.Empty()); + int idx = sp.LastIndexOf(0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void TestMatchLastIndexOf() + { + for (int length = 0; length < 32; length++) + { + int[] a = new int[length]; + for (int i = 0; i < length; i++) + { + a[i] = 10 * (i + 1); + } + ReadOnlySpan span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + int target = a[targetIndex]; + int idx = span.LastIndexOf(target); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOf() + { + for (int length = 2; length < 32; length++) + { + int[] a = new int[length]; + for (int i = 0; i < length; i++) + { + a[i] = 10 * (i + 1); + } + + a[length - 1] = 5555; + a[length - 2] = 5555; + + ReadOnlySpan span = new ReadOnlySpan(a); + int idx = span.LastIndexOf(5555); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void OnNoMatchMakeSureEveryElementIsComparedLastIndexOf() + { + for (int length = 0; length < 100; length++) + { + TIntLog log = new TIntLog(); + + TInt[] a = new TInt[length]; + for (int i = 0; i < length; i++) + { + a[i] = new TInt(10 * (i + 1), log); + } + ReadOnlySpan span = new ReadOnlySpan(a); + int idx = span.LastIndexOf(new TInt(9999, log)); + Assert.Equal(-1, idx); + + // Since we asked for a non-existent value, make sure each element of the array was compared once. + // (Strictly speaking, it would not be illegal for IndexOf to compare an element more than once but + // that would be a non-optimal implementation and a red flag. So we'll stick with the stricter test.) + Assert.Equal(a.Length, log.Count); + foreach (TInt elem in a) + { + int numCompares = log.CountCompares(elem.Value, 9999); + Assert.True(numCompares == 1, $"Expected {numCompares} == 1 for element {elem.Value}."); + } + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOf() + { + const int GuardValue = 77777; + const int GuardLength = 50; + + Action checkForOutOfRangeAccess = + delegate (int x, int y) + { + if (x == GuardValue || y == GuardValue) + throw new Exception("Detected out of range access in IndexOf()"); + }; + + for (int length = 0; length < 100; length++) + { + TInt[] a = new TInt[GuardLength + length + GuardLength]; + for (int i = 0; i < a.Length; i++) + { + a[i] = new TInt(GuardValue, checkForOutOfRangeAccess); + } + + for (int i = 0; i < length; i++) + { + a[GuardLength + i] = new TInt(10 * (i + 1), checkForOutOfRangeAccess); + } + + ReadOnlySpan span = new ReadOnlySpan(a, GuardLength, length); + int idx = span.LastIndexOf(new TInt(9999, checkForOutOfRangeAccess)); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void ZeroLengthLastIndexOf_String() + { + ReadOnlySpan sp = new ReadOnlySpan(Array.Empty()); + int idx = sp.LastIndexOf("a"); + Assert.Equal(-1, idx); + } + + [Fact] + public static void TestMatchLastIndexOf_String() + { + for (int length = 0; length < 32; length++) + { + string[] a = new string[length]; + for (int i = 0; i < length; i++) + { + a[i] = (10 * (i + 1)).ToString(); + } + ReadOnlySpan span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + string target = a[targetIndex]; + int idx = span.LastIndexOf(target); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestNoMatchLastIndexOf_String() + { + var rnd = new Random(42); + for (int length = 0; length <= byte.MaxValue; length++) + { + string[] a = new string[length]; + string target = (rnd.Next(0, 256)).ToString(); + for (int i = 0; i < length; i++) + { + string val = (i + 1).ToString(); + a[i] = val == target ? (target + 1) : val; + } + ReadOnlySpan span = new ReadOnlySpan(a); + + int idx = span.LastIndexOf(target); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOf_String() + { + for (int length = 2; length < 32; length++) + { + string[] a = new string[length]; + for (int i = 0; i < length; i++) + { + a[i] = (10 * (i + 1)).ToString(); + } + + a[length - 1] = "5555"; + a[length - 2] = "5555"; + + ReadOnlySpan span = new ReadOnlySpan(a); + int idx = span.LastIndexOf("5555"); + Assert.Equal(length - 1, idx); + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOf.byte.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOf.byte.cs new file mode 100644 index 0000000000..37cecc6c8f --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOf.byte.cs @@ -0,0 +1,149 @@ +// 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.Numerics; +using Xunit; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + [Fact] + public static void ZeroLengthLastIndexOf_Byte() + { + ReadOnlySpan sp = new ReadOnlySpan(Array.Empty()); + int idx = sp.LastIndexOf(0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledLastIndexOf_Byte() + { + for (int length = 0; length <= byte.MaxValue; length++) + { + byte[] a = new byte[length]; + ReadOnlySpan span = new ReadOnlySpan(a); + + for (int i = 0; i < length; i++) + { + byte target0 = default; + int idx = span.LastIndexOf(target0); + Assert.Equal(length - 1, idx); + } + } + } + + [Fact] + public static void TestMatchLastIndexOf_Byte() + { + for (int length = 0; length <= byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + ReadOnlySpan span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + byte target = a[targetIndex]; + int idx = span.LastIndexOf(target); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestNoMatchLastIndexOf_Byte() + { + var rnd = new Random(42); + for (int length = 0; length <= byte.MaxValue; length++) + { + byte[] a = new byte[length]; + byte target = (byte)rnd.Next(0, 256); + for (int i = 0; i < length; i++) + { + byte val = (byte)(i + 1); + a[i] = val == target ? (byte)(target + 1) : val; + } + ReadOnlySpan span = new ReadOnlySpan(a); + + int idx = span.LastIndexOf(target); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestAllignmentNoMatchLastIndexOf_Byte() + { + byte[] array = new byte[4 * Vector.Count]; + for (var i = 0; i < Vector.Count; i++) + { + var span = new ReadOnlySpan(array, i, 3 * Vector.Count); + int idx = span.LastIndexOf(5); + Assert.Equal(-1, idx); + + span = new ReadOnlySpan(array, i, 3 * Vector.Count - 3); + idx = span.LastIndexOf(5); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestAllignmentMatchLastIndexOf_Byte() + { + byte[] array = new byte[4 * Vector.Count]; + for (int i = 0; i < array.Length; i++) + { + array[i] = 5; + } + for (var i = 0; i < Vector.Count; i++) + { + var span = new ReadOnlySpan(array, i, 3 * Vector.Count); + int idx = span.LastIndexOf(5); + Assert.Equal(span.Length - 1, idx); + + span = new ReadOnlySpan(array, i, 3 * Vector.Count - 3); + idx = span.LastIndexOf(5); + Assert.Equal(span.Length - 1, idx); + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOf_Byte() + { + for (int length = 2; length <= byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + byte val = (byte)(i + 1); + a[i] = val == 200 ? (byte)201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + + ReadOnlySpan span = new ReadOnlySpan(a); + int idx = span.LastIndexOf(200); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOf_Byte() + { + for (int length = 0; length <= byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 99; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length); + int index = span.LastIndexOf(99); + Assert.Equal(-1, index); + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOf.char.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOf.char.cs new file mode 100644 index 0000000000..9a60ea90c9 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOf.char.cs @@ -0,0 +1,74 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + [Fact] + public static void ZeroLengthLastIndexOf_Char() + { + ReadOnlySpan sp = new ReadOnlySpan(Array.Empty()); + int idx = sp.LastIndexOf((char)0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void TestMatchLastIndexOf_Char() + { + for (int length = 0; length < 32; length++) + { + char[] a = new char[length]; + for (int i = 0; i < length; i++) + { + a[i] = (char)(i + 1); + } + ReadOnlySpan span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + char target = a[targetIndex]; + int idx = span.LastIndexOf(target); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOf_Char() + { + for (int length = 2; length < 32; length++) + { + char[] a = new char[length]; + for (int i = 0; i < length; i++) + { + a[i] = (char)(i + 1); + } + + a[length - 1] = (char)200; + a[length - 2] = (char)200; + + ReadOnlySpan span = new ReadOnlySpan(a); + int idx = span.LastIndexOf((char)200); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOf_Char() + { + for (int length = 0; length < 100; length++) + { + char[] a = new char[length + 2]; + a[0] = '9'; + a[length + 1] = '9'; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length); + int index = span.LastIndexOf('9'); + Assert.Equal(-1, index); + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOfAny.T.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOfAny.T.cs new file mode 100644 index 0000000000..3bb87a0915 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOfAny.T.cs @@ -0,0 +1,937 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + [Fact] + public static void ZeroLengthLastIndexOfAny_TwoByte() + { + var sp = new ReadOnlySpan(Array.Empty()); + int idx = sp.LastIndexOfAny(0, 0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledLastIndexOfAny_TwoByte() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + var span = new ReadOnlySpan(a); + + int[] targets = { default, 99 }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 2) == 0 ? 0 : 1; + int target0 = targets[index]; + int target1 = targets[(index + 1) % 2]; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(span.Length - 1, idx); + } + } + } + + [Fact] + public static void TestMatchLastIndexOfAny_TwoByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + a[i] = i + 1; + } + var span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + int target0 = a[targetIndex]; + int target1 = 0; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + int target0 = a[targetIndex]; + int target1 = a[targetIndex + 1]; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(targetIndex + 1, idx); + } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + int target0 = 0; + int target1 = a[targetIndex + 1]; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(targetIndex + 1, idx); + } + } + } + + [Fact] + public static void TestNoMatchLastIndexOfAny_TwoByte() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + int target0 = rnd.Next(1, 256); + int target1 = rnd.Next(1, 256); + var span = new ReadOnlySpan(a); + + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOfAny_TwoByte() + { + for (int length = 3; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + int val = i + 1; + a[i] = val == 200 ? 201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + + var span = new ReadOnlySpan(a); + int idx = span.LastIndexOfAny(200, 200); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOfAny_TwoByte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 98; + var span = new ReadOnlySpan(a, 1, length - 1); + int index = span.LastIndexOfAny(99, 98); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 99; + var span = new ReadOnlySpan(a, 1, length - 1); + int index = span.LastIndexOfAny(99, 99); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthIndexOf_ThreeByte() + { + var sp = new ReadOnlySpan(Array.Empty()); + int idx = sp.LastIndexOfAny(0, 0, 0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledLastIndexOfAny_ThreeByte() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + var span = new ReadOnlySpan(a); + + int[] targets = { default, 99, 98 }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 3); + int target0 = targets[index]; + int target1 = targets[(index + 1) % 2]; + int target2 = targets[(index + 1) % 3]; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(span.Length - 1, idx); + } + } + } + + [Fact] + public static void TestMatchLastIndexOfAny_ThreeByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + a[i] = i + 1; + } + var span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + int target0 = a[targetIndex]; + int target1 = 0; + int target2 = 0; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + int target0 = a[targetIndex]; + int target1 = a[targetIndex + 1]; + int target2 = a[targetIndex + 2]; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex + 2, idx); + } + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + int target0 = 0; + int target1 = 0; + int target2 = a[targetIndex + 2]; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex + 2, idx); + } + } + } + + [Fact] + public static void TestNoMatchLastIndexOfAny_ThreeByte() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + int target0 = rnd.Next(1, 256); + int target1 = rnd.Next(1, 256); + int target2 = rnd.Next(1, 256); + var span = new ReadOnlySpan(a); + + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOfAny_ThreeByte() + { + for (int length = 4; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + int val = i + 1; + a[i] = val == 200 ? 201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + a[length - 4] = 200; + + var span = new ReadOnlySpan(a); + int idx = span.LastIndexOfAny(200, 200, 200); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOfAny_ThreeByte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 98; + var span = new ReadOnlySpan(a, 1, length - 1); + int index = span.LastIndexOfAny(99, 98, 99); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 99; + var span = new ReadOnlySpan(a, 1, length - 1); + int index = span.LastIndexOfAny(99, 99, 99); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthLastIndexOfAny_ManyByte() + { + var sp = new ReadOnlySpan(Array.Empty()); + var values = new ReadOnlySpan(new int[] { 0, 0, 0, 0 }); + int idx = sp.LastIndexOfAny(values); + Assert.Equal(-1, idx); + + values = new ReadOnlySpan(new int[] { }); + idx = sp.LastIndexOfAny(values); + Assert.Equal(0, idx); + } + + [Fact] + public static void DefaultFilledLastIndexOfAny_ManyByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + var span = new ReadOnlySpan(a); + + var values = new ReadOnlySpan(new int[] { default, 99, 98, 0 }); + + for (int i = 0; i < length; i++) + { + int idx = span.LastIndexOfAny(values); + Assert.Equal(span.Length - 1, idx); + } + } + } + + [Fact] + public static void TestMatchLastIndexOfAny_ManyByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + a[i] = i + 1; + } + var span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + var values = new ReadOnlySpan(new int[] { a[targetIndex], 0, 0, 0 }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + var values = new ReadOnlySpan(new int[] { a[targetIndex], a[targetIndex + 1], a[targetIndex + 2], a[targetIndex + 3] }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(targetIndex + 3, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + var values = new ReadOnlySpan(new int[] { 0, 0, 0, a[targetIndex + 3] }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(targetIndex + 3, idx); + } + } + } + + [Fact] + public static void TestMatchValuesLargerLastIndexOfAny_ManyByte() + { + var rnd = new Random(42); + for (int length = 2; length < byte.MaxValue; length++) + { + var a = new int[length]; + int expectedIndex = length / 2; + for (int i = 0; i < length; i++) + { + if (i == expectedIndex) + { + continue; + } + a[i] = 255; + } + var span = new ReadOnlySpan(a); + + var targets = new int[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + if (i == length + 1) + { + continue; + } + targets[i] = rnd.Next(1, 255); + } + + var values = new ReadOnlySpan(targets); + int idx = span.LastIndexOfAny(values); + Assert.Equal(expectedIndex, idx); + } + } + + [Fact] + public static void TestNoMatchLastIndexOfAny_ManyByte() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length]; + var targets = new int[length]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = rnd.Next(1, 256); + } + var span = new ReadOnlySpan(a); + var values = new ReadOnlySpan(targets); + + int idx = span.LastIndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestNoMatchValuesLargerLastIndexOfAny_ManyByte() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length]; + var targets = new int[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = rnd.Next(1, 256); + } + var span = new ReadOnlySpan(a); + var values = new ReadOnlySpan(targets); + + int idx = span.LastIndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOfAny_ManyByte() + { + for (int length = 5; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + int val = i + 1; + a[i] = val == 200 ? 201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + a[length - 4] = 200; + a[length - 5] = 200; + + var span = new ReadOnlySpan(a); + var values = new ReadOnlySpan(new int[] { 200, 200, 200, 200, 200, 200, 200, 200, 200 }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOfAny_ManyByte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 98; + var span = new ReadOnlySpan(a, 1, length - 1); + var values = new ReadOnlySpan(new int[] { 99, 98, 99, 98, 99, 98 }); + int index = span.LastIndexOfAny(values); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 99; + var span = new ReadOnlySpan(a, 1, length - 1); + var values = new ReadOnlySpan(new int[] { 99, 99, 99, 99, 99, 99 }); + int index = span.LastIndexOfAny(values); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthLastIndexOfAny_String_TwoByte() + { + var sp = new ReadOnlySpan(Array.Empty()); + int idx = sp.LastIndexOfAny("0", "0"); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledLastIndexOfAny_String_TwoByte() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + var tempSpan = new Span(a); + tempSpan.Fill(""); + ReadOnlySpan span = tempSpan; + + string[] targets = { "", "99" }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 2) == 0 ? 0 : 1; + string target0 = targets[index]; + string target1 = targets[(index + 1) % 2]; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(span.Length - 1, idx); + } + } + } + + [Fact] + public static void TestMatchLastIndexOfAny_String_TwoByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + a[i] = (i + 1).ToString(); + } + var span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + string target0 = a[targetIndex]; + string target1 = "0"; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + string target0 = a[targetIndex]; + string target1 = a[targetIndex + 1]; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(targetIndex + 1, idx); + } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + string target0 = "0"; + string target1 = a[targetIndex + 1]; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(targetIndex + 1, idx); + } + } + } + + [Fact] + public static void TestNoMatchLastIndexOfAny_String_TwoByte() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + string target0 = rnd.Next(1, 256).ToString(); + string target1 = rnd.Next(1, 256).ToString(); + var span = new ReadOnlySpan(a); + + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOfAny_String_TwoByte() + { + for (int length = 3; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + string val = (i + 1).ToString(); + a[i] = val == "200" ? "201" : val; + } + + a[length - 1] = "200"; + a[length - 2] = "200"; + a[length - 3] = "200"; + + var span = new ReadOnlySpan(a); + int idx = span.LastIndexOfAny("200", "200"); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOfAny_String_TwoByte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "98"; + var span = new ReadOnlySpan(a, 1, length - 1); + int index = span.LastIndexOfAny("99", "98"); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "99"; + var span = new ReadOnlySpan(a, 1, length - 1); + int index = span.LastIndexOfAny("99", "99"); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthIndexOf_String_ThreeByte() + { + var sp = new ReadOnlySpan(Array.Empty()); + int idx = sp.LastIndexOfAny("0", "0", "0"); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledLastIndexOfAny_String_ThreeByte() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + var tempSpan = new Span(a); + tempSpan.Fill(""); + ReadOnlySpan span = tempSpan; + + string[] targets = { "", "99", "98" }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 3); + string target0 = targets[index]; + string target1 = targets[(index + 1) % 2]; + string target2 = targets[(index + 1) % 3]; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(span.Length - 1, idx); + } + } + } + + [Fact] + public static void TestMatchLastIndexOfAny_String_ThreeByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + a[i] = (i + 1).ToString(); + } + var span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + string target0 = a[targetIndex]; + string target1 = "0"; + string target2 = "0"; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + string target0 = a[targetIndex]; + string target1 = a[targetIndex + 1]; + string target2 = a[targetIndex + 2]; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex + 2, idx); + } + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + string target0 = "0"; + string target1 = "0"; + string target2 = a[targetIndex + 2]; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex + 2, idx); + } + } + } + + [Fact] + public static void TestNoMatchLastIndexOfAny_String_ThreeByte() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + string target0 = rnd.Next(1, 256).ToString(); + string target1 = rnd.Next(1, 256).ToString(); + string target2 = rnd.Next(1, 256).ToString(); + var span = new ReadOnlySpan(a); + + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOfAny_String_ThreeByte() + { + for (int length = 4; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + string val = (i + 1).ToString(); + a[i] = val == "200" ? "201" : val; + } + + a[length - 1] = "200"; + a[length - 2] = "200"; + a[length - 3] = "200"; + a[length - 4] = "200"; + + var span = new ReadOnlySpan(a); + int idx = span.LastIndexOfAny("200", "200", "200"); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOfAny_String_ThreeByte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "98"; + var span = new ReadOnlySpan(a, 1, length - 1); + int index = span.LastIndexOfAny("99", "98", "99"); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "99"; + var span = new ReadOnlySpan(a, 1, length - 1); + int index = span.LastIndexOfAny("99", "99", "99"); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthLastIndexOfAny_String_ManyByte() + { + var sp = new ReadOnlySpan(Array.Empty()); + var values = new ReadOnlySpan(new string[] { "0", "0", "0", "0" }); + int idx = sp.LastIndexOfAny(values); + Assert.Equal(-1, idx); + + values = new ReadOnlySpan(new string[] { }); + idx = sp.LastIndexOfAny(values); + Assert.Equal(0, idx); + } + + [Fact] + public static void DefaultFilledLastIndexOfAny_String_ManyByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + var tempSpan = new Span(a); + tempSpan.Fill(""); + ReadOnlySpan span = tempSpan; + + var values = new ReadOnlySpan(new string[] { "", "99", "98", "0" }); + + for (int i = 0; i < length; i++) + { + int idx = span.LastIndexOfAny(values); + Assert.Equal(span.Length - 1, idx); + } + } + } + + [Fact] + public static void TestMatchLastIndexOfAny_String_ManyByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + a[i] = (i + 1).ToString(); + } + var span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + var values = new ReadOnlySpan(new string[] { a[targetIndex], "0", "0", "0" }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + var values = new ReadOnlySpan(new string[] { a[targetIndex], a[targetIndex + 1], a[targetIndex + 2], a[targetIndex + 3] }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(targetIndex + 3, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + var values = new ReadOnlySpan(new string[] { "0", "0", "0", a[targetIndex + 3] }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(targetIndex + 3, idx); + } + } + } + + [Fact] + public static void TestMatchValuesLargerLastIndexOfAny_String_ManyByte() + { + var rnd = new Random(42); + for (int length = 2; length < byte.MaxValue; length++) + { + var a = new string[length]; + int expectedIndex = length / 2; + for (int i = 0; i < length; i++) + { + if (i == expectedIndex) + { + a[i] = "val"; + continue; + } + a[i] = "255"; + } + var span = new ReadOnlySpan(a); + + var targets = new string[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + if (i == length + 1) + { + targets[i] = "val"; + continue; + } + targets[i] = rnd.Next(1, 255).ToString(); + } + + var values = new ReadOnlySpan(targets); + int idx = span.LastIndexOfAny(values); + Assert.Equal(expectedIndex, idx); + } + } + + [Fact] + public static void TestNoMatchLastIndexOfAny_String_ManyByte() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length]; + var targets = new string[length]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = rnd.Next(1, 256).ToString(); + } + var span = new ReadOnlySpan(a); + var values = new ReadOnlySpan(targets); + + int idx = span.LastIndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestNoMatchValuesLargerLastIndexOfAny_String_ManyByte() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length]; + var targets = new string[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = rnd.Next(1, 256).ToString(); + } + var span = new ReadOnlySpan(a); + var values = new ReadOnlySpan(targets); + + int idx = span.LastIndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOfAny_String_ManyByte() + { + for (int length = 5; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + string val = (i + 1).ToString(); + a[i] = val == "200" ? "201" : val; + } + + a[length - 1] = "200"; + a[length - 2] = "200"; + a[length - 3] = "200"; + a[length - 4] = "200"; + a[length - 5] = "200"; + + var span = new ReadOnlySpan(a); + var values = new ReadOnlySpan(new string[] { "200", "200", "200", "200", "200", "200", "200", "200", "200" }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOfAny_String_ManyByte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "98"; + var span = new ReadOnlySpan(a, 1, length - 1); + var values = new ReadOnlySpan(new string[] { "99", "98", "99", "98", "99", "98" }); + int index = span.LastIndexOfAny(values); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "99"; + var span = new ReadOnlySpan(a, 1, length - 1); + var values = new ReadOnlySpan(new string[] { "99", "99", "99", "99", "99", "99" }); + int index = span.LastIndexOfAny(values); + Assert.Equal(-1, index); + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOfAny.byte.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOfAny.byte.cs new file mode 100644 index 0000000000..7ed3e40c2c --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOfAny.byte.cs @@ -0,0 +1,521 @@ +// 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.Text; +using Xunit; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + [Theory] + [InlineData("a", "a", 'a', 0)] + [InlineData("ab", "a", 'a', 0)] + [InlineData("aab", "a", 'a', 1)] + [InlineData("acab", "a", 'a', 2)] + [InlineData("acab", "c", 'c', 1)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "lo", 'o', 14)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "ol", 'o', 14)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "ll", 'l', 11)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "lmr", 'r', 17)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "rml", 'r', 17)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "mlr", 'r', 17)] + [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "lmr", 'r', 43)] + [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrzzzzzzzz", "lmr", 'r', 43)] + [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqxzzzzzzzz", "lmr", 'm', 38)] + [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqlzzzzzzzz", "lmr", 'l', 43)] + [InlineData("/localhost:5000/PATH/%2FPATH2/ HTTP/1.1", " %?", ' ', 30)] + [InlineData("/localhost:5000/PATH/%2FPATH2/?key=value HTTP/1.1", " %?", ' ', 40)] + [InlineData("/localhost:5000/PATH/PATH2/?key=value HTTP/1.1", " %?", ' ', 37)] + [InlineData("/localhost:5000/PATH/PATH2/ HTTP/1.1", " %?", ' ', 27)] + public static void LastIndexOfAnyStrings_Byte(string raw, string search, char expectResult, int expectIndex) + { + byte[] buffers = Encoding.UTF8.GetBytes(raw); + var span = new ReadOnlySpan(buffers); + char[] searchFor = search.ToCharArray(); + byte[] searchForBytes = Encoding.UTF8.GetBytes(searchFor); + + var index = -1; + if (searchFor.Length == 1) + { + index = span.LastIndexOf((byte)searchFor[0]); + } + else if (searchFor.Length == 2) + { + index = span.LastIndexOfAny((byte)searchFor[0], (byte)searchFor[1]); + } + else if (searchFor.Length == 3) + { + index = span.LastIndexOfAny((byte)searchFor[0], (byte)searchFor[1], (byte)searchFor[2]); + } + else + { + index = span.LastIndexOfAny(new ReadOnlySpan(searchForBytes)); + } + + var found = span[index]; + Assert.Equal((byte)expectResult, found); + Assert.Equal(expectIndex, index); + } + + [Fact] + public static void ZeroLengthLastIndexOfAny_Byte_TwoByte() + { + ReadOnlySpan sp = new ReadOnlySpan(Array.Empty()); + int idx = sp.LastIndexOfAny(0, 0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledLastIndexOfAny_Byte_TwoByte() + { + Random rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + ReadOnlySpan span = new ReadOnlySpan(a); + + byte[] targets = { default, 99 }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 2) == 0 ? 0 : 1; + byte target0 = targets[index]; + byte target1 = targets[(index + 1) % 2]; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(span.Length - 1, idx); + } + } + } + + [Fact] + public static void TestMatchLastIndexOfAny_Byte_TwoByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + ReadOnlySpan span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = 0; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = a[targetIndex + 1]; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(targetIndex + 1, idx); + } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + byte target0 = 0; + byte target1 = a[targetIndex + 1]; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(targetIndex + 1, idx); + } + } + } + + [Fact] + public static void TestNoMatchLastIndexOfAny_Byte_TwoByte() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + byte target0 = (byte)rnd.Next(1, 256); + byte target1 = (byte)rnd.Next(1, 256); + ReadOnlySpan span = new ReadOnlySpan(a); + + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOfAny_Byte_TwoByte() + { + for (int length = 3; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + byte val = (byte)(i + 1); + a[i] = val == 200 ? (byte)201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + + ReadOnlySpan span = new ReadOnlySpan(a); + int idx = span.LastIndexOfAny(200, 200); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOfAny_Byte_TwoByte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 98; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); + int index = span.LastIndexOfAny(99, 98); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 99; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); + int index = span.LastIndexOfAny(99, 99); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthIndexOf_Byte_ThreeByte() + { + ReadOnlySpan sp = new ReadOnlySpan(Array.Empty()); + int idx = sp.LastIndexOfAny(0, 0, 0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledLastIndexOfAny_Byte_ThreeByte() + { + Random rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + ReadOnlySpan span = new ReadOnlySpan(a); + + byte[] targets = { default, 99, 98 }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 3); + byte target0 = targets[index]; + byte target1 = targets[(index + 1) % 2]; + byte target2 = targets[(index + 1) % 3]; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(span.Length - 1, idx); + } + } + } + + [Fact] + public static void TestMatchLastIndexOfAny_Byte_ThreeByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + ReadOnlySpan span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = 0; + byte target2 = 0; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = a[targetIndex + 1]; + byte target2 = a[targetIndex + 2]; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex + 2, idx); + } + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + byte target0 = 0; + byte target1 = 0; + byte target2 = a[targetIndex + 2]; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex + 2, idx); + } + } + } + + [Fact] + public static void TestNoMatchLastIndexOfAny_Byte_ThreeByte() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + byte target0 = (byte)rnd.Next(1, 256); + byte target1 = (byte)rnd.Next(1, 256); + byte target2 = (byte)rnd.Next(1, 256); + ReadOnlySpan span = new ReadOnlySpan(a); + + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOfAny_Byte_ThreeByte() + { + for (int length = 4; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + byte val = (byte)(i + 1); + a[i] = val == 200 ? (byte)201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + a[length - 4] = 200; + + ReadOnlySpan span = new ReadOnlySpan(a); + int idx = span.LastIndexOfAny(200, 200, 200); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOfAny_Byte_ThreeByte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 98; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); + int index = span.LastIndexOfAny(99, 98, 99); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 99; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); + int index = span.LastIndexOfAny(99, 99, 99); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthLastIndexOfAny_Byte_ManyByte() + { + ReadOnlySpan sp = new ReadOnlySpan(Array.Empty()); + var values = new ReadOnlySpan(new byte[] { 0, 0, 0, 0 }); + int idx = sp.LastIndexOfAny(values); + Assert.Equal(-1, idx); + + values = new ReadOnlySpan(new byte[] { }); + idx = sp.LastIndexOfAny(values); + Assert.Equal(0, idx); + } + + [Fact] + public static void DefaultFilledLastIndexOfAny_Byte_ManyByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + ReadOnlySpan span = new ReadOnlySpan(a); + + var values = new ReadOnlySpan(new byte[] { default, 99, 98, 0 }); + + for (int i = 0; i < length; i++) + { + int idx = span.LastIndexOfAny(values); + Assert.Equal(span.Length - 1, idx); + } + } + } + + [Fact] + public static void TestMatchLastIndexOfAny_Byte_ManyByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + ReadOnlySpan span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + var values = new ReadOnlySpan(new byte[] { a[targetIndex], 0, 0, 0 }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + var values = new ReadOnlySpan(new byte[] { a[targetIndex], a[targetIndex + 1], a[targetIndex + 2], a[targetIndex + 3] }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(targetIndex + 3, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + var values = new ReadOnlySpan(new byte[] { 0, 0, 0, a[targetIndex + 3] }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(targetIndex + 3, idx); + } + } + } + + [Fact] + public static void TestMatchValuesLargerLastIndexOfAny_Byte_ManyByte() + { + var rnd = new Random(42); + for (int length = 2; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + int expectedIndex = length / 2; + for (int i = 0; i < length; i++) + { + if (i == expectedIndex) + { + continue; + } + a[i] = 255; + } + ReadOnlySpan span = new ReadOnlySpan(a); + + byte[] targets = new byte[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + if (i == length + 1) + { + continue; + } + targets[i] = (byte)rnd.Next(1, 255); + } + + var values = new ReadOnlySpan(targets); + int idx = span.LastIndexOfAny(values); + Assert.Equal(expectedIndex, idx); + } + } + + [Fact] + public static void TestNoMatchLastIndexOfAny_Byte_ManyByte() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + byte[] targets = new byte[length]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = (byte)rnd.Next(1, 256); + } + ReadOnlySpan span = new ReadOnlySpan(a); + var values = new ReadOnlySpan(targets); + + int idx = span.LastIndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestNoMatchValuesLargerLastIndexOfAny_Byte_ManyByte() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + byte[] targets = new byte[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = (byte)rnd.Next(1, 256); + } + ReadOnlySpan span = new ReadOnlySpan(a); + var values = new ReadOnlySpan(targets); + + int idx = span.LastIndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOfAny_Byte_ManyByte() + { + for (int length = 5; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + byte val = (byte)(i + 1); + a[i] = val == 200 ? (byte)201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + a[length - 4] = 200; + a[length - 5] = 200; + + ReadOnlySpan span = new ReadOnlySpan(a); + var values = new ReadOnlySpan(new byte[] { 200, 200, 200, 200, 200, 200, 200, 200, 200 }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOfAny_Byte_ManyByte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 98; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); + var values = new ReadOnlySpan(new byte[] { 99, 98, 99, 98, 99, 98 }); + int index = span.LastIndexOfAny(values); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 99; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); + var values = new ReadOnlySpan(new byte[] { 99, 99, 99, 99, 99, 99 }); + int index = span.LastIndexOfAny(values); + Assert.Equal(-1, index); + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOfSequence.T.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOfSequence.T.cs new file mode 100644 index 0000000000..2dde13b322 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOfSequence.T.cs @@ -0,0 +1,245 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + [Fact] + public static void LastIndexOfSequenceMatchAtStart() + { + ReadOnlySpan span = new ReadOnlySpan(new int[] { 5, 1, 77, 2, 3, 77, 77, 4, 5, 77, 77, 77, 88, 6, 6, 77, 77, 88, 9 }); + ReadOnlySpan value = new ReadOnlySpan(new int[] { 5, 1, 77 }); + int index = span.LastIndexOf(value); + Assert.Equal(0, index); + } + + [Fact] + public static void LastIndexOfSequenceMultipleMatch() + { + ReadOnlySpan span = new ReadOnlySpan(new int[] { 1, 2, 3, 1, 2, 3, 1, 2, 3, 1 }); + ReadOnlySpan value = new ReadOnlySpan(new int[] { 2, 3 }); + int index = span.LastIndexOf(value); + Assert.Equal(7, index); + } + + [Fact] + public static void LastIndexOfSequenceRestart() + { + ReadOnlySpan span = new ReadOnlySpan(new int[] { 0, 1, 77, 2, 3, 77, 77, 4, 5, 77, 77, 77, 88, 6, 6, 77, 77, 8, 9, 77, 0, 1 }); + ReadOnlySpan value = new ReadOnlySpan(new int[] { 77, 77, 88 }); + int index = span.LastIndexOf(value); + Assert.Equal(10, index); + } + + [Fact] + public static void LastIndexOfSequenceNoMatch() + { + ReadOnlySpan span = new ReadOnlySpan(new int[] { 0, 1, 77, 2, 3, 77, 77, 4, 5, 77, 77, 77, 88, 6, 6, 77, 77, 88, 9 }); + ReadOnlySpan value = new ReadOnlySpan(new int[] { 77, 77, 88, 99 }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceNotEvenAHeadMatch() + { + ReadOnlySpan span = new ReadOnlySpan(new int[] { 0, 1, 77, 2, 3, 77, 77, 4, 5, 77, 77, 77, 88, 6, 6, 77, 77, 88, 9 }); + ReadOnlySpan value = new ReadOnlySpan(new int[] { 100, 77, 88, 99 }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceMatchAtVeryEnd() + { + ReadOnlySpan span = new ReadOnlySpan(new int[] { 0, 1, 2, 3, 4, 5 }); + ReadOnlySpan value = new ReadOnlySpan(new int[] { 3, 4, 5 }); + int index = span.LastIndexOf(value); + Assert.Equal(3, index); + } + + [Fact] + public static void LastIndexOfSequenceJustPastVeryEnd() + { + ReadOnlySpan span = new ReadOnlySpan(new int[] { 0, 1, 2, 3, 4, 5 }, 0, 5); + ReadOnlySpan value = new ReadOnlySpan(new int[] { 3, 4, 5 }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceZeroLengthValue() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new int[] { 0, 1, 77, 2, 3, 77, 77, 4, 5, 77, 77, 77, 88, 6, 6, 77, 77, 88, 9 }); + ReadOnlySpan value = new ReadOnlySpan(Array.Empty()); + int index = span.LastIndexOf(value); + Assert.Equal(0, index); + } + + [Fact] + public static void LastIndexOfSequenceZeroLengthSpan() + { + ReadOnlySpan span = new ReadOnlySpan(Array.Empty()); + ReadOnlySpan value = new ReadOnlySpan(new int[] { 1, 2, 3 }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValue() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new int[] { 0, 1, 2, 3, 4, 5 }); + ReadOnlySpan value = new ReadOnlySpan(new int[] { 2 }); + int index = span.LastIndexOf(value); + Assert.Equal(2, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValueAtVeryEnd() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new int[] { 0, 1, 2, 3, 4, 5 }); + ReadOnlySpan value = new ReadOnlySpan(new int[] { 5 }); + int index = span.LastIndexOf(value); + Assert.Equal(5, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValueMultipleTimes() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new int[] { 0, 1, 5, 3, 4, 5 }); + ReadOnlySpan value = new ReadOnlySpan(new int[] { 5 }); + int index = span.LastIndexOf(value); + Assert.Equal(5, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValueJustPasttVeryEnd() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new int[] { 0, 1, 2, 3, 4, 5 }, 0, 5); + ReadOnlySpan value = new ReadOnlySpan(new int[] { 5 }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceMatchAtStart_String() + { + ReadOnlySpan span = new ReadOnlySpan(new string[] { "5", "1", "77", "2", "3", "77", "77", "4", "5", "77", "77", "77", "88", "6", "6", "77", "77", "88", "9" }); + ReadOnlySpan value = new ReadOnlySpan(new string[] { "5", "1", "77" }); + int index = span.LastIndexOf(value); + Assert.Equal(0, index); + } + + [Fact] + public static void LastIndexOfSequenceMultipleMatch_String() + { + ReadOnlySpan span = new ReadOnlySpan(new string[] { "1", "2", "3", "1", "2", "3", "1", "2", "3" }); + ReadOnlySpan value = new ReadOnlySpan(new string[] { "2", "3" }); + int index = span.LastIndexOf(value); + Assert.Equal(7, index); + } + + [Fact] + public static void LastIndexOfSequenceRestart_String() + { + ReadOnlySpan span = new ReadOnlySpan(new string[] { "0", "1", "77", "2", "3", "77", "77", "4", "5", "77", "77", "77", "88", "6", "6", "77", "77", "8", "9", "77", "0", "1" }); + ReadOnlySpan value = new ReadOnlySpan(new string[] { "77", "77", "88" }); + int index = span.LastIndexOf(value); + Assert.Equal(10, index); + } + + [Fact] + public static void LastIndexOfSequenceNoMatch_String() + { + ReadOnlySpan span = new ReadOnlySpan(new string[] { "0", "1", "77", "2", "3", "77", "77", "4", "5", "77", "77", "77", "88", "6", "6", "77", "77", "88", "9" }); + ReadOnlySpan value = new ReadOnlySpan(new string[] { "77", "77", "88", "99" }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceNotEvenAHeadMatch_String() + { + ReadOnlySpan span = new ReadOnlySpan(new string[] { "0", "1", "77", "2", "3", "77", "77", "4", "5", "77", "77", "77", "88", "6", "6", "77", "77", "88", "9" }); + ReadOnlySpan value = new ReadOnlySpan(new string[] { "100", "77", "88", "99" }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceMatchAtVeryEnd_String() + { + ReadOnlySpan span = new ReadOnlySpan(new string[] { "0", "1", "2", "3", "4", "5" }); + ReadOnlySpan value = new ReadOnlySpan(new string[] { "3", "4", "5" }); + int index = span.LastIndexOf(value); + Assert.Equal(3, index); + } + + [Fact] + public static void LastIndexOfSequenceJustPastVeryEnd_String() + { + ReadOnlySpan span = new ReadOnlySpan(new string[] { "0", "1", "2", "3", "4", "5" }, 0, 5); + ReadOnlySpan value = new ReadOnlySpan(new string[] { "3", "4", "5" }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceZeroLengthValue_String() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new string[] { "0", "1", "77", "2", "3", "77", "77", "4", "5", "77", "77", "77", "88", "6", "6", "77", "77", "88", "9" }); + ReadOnlySpan value = new ReadOnlySpan(Array.Empty()); + int index = span.LastIndexOf(value); + Assert.Equal(0, index); + } + + [Fact] + public static void LastIndexOfSequenceZeroLengthSpan_String() + { + ReadOnlySpan span = new ReadOnlySpan(Array.Empty()); + ReadOnlySpan value = new ReadOnlySpan(new string[] { "1", "2", "3" }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValue_String() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new string[] { "0", "1", "2", "3", "4", "5" }); + ReadOnlySpan value = new ReadOnlySpan(new string[] { "2" }); + int index = span.LastIndexOf(value); + Assert.Equal(2, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValueAtVeryEnd_String() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new string[] { "0", "1", "2", "3", "4", "5" }); + ReadOnlySpan value = new ReadOnlySpan(new string[] { "5" }); + int index = span.LastIndexOf(value); + Assert.Equal(5, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValueJustPasttVeryEnd_String() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new string[] { "0", "1", "2", "3", "4", "5" }, 0, 5); + ReadOnlySpan value = new ReadOnlySpan(new string[] { "5" }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOfSequence.byte.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOfSequence.byte.cs new file mode 100644 index 0000000000..293920a2f7 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOfSequence.byte.cs @@ -0,0 +1,133 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + [Fact] + public static void LastIndexOfSequenceMatchAtStart_Byte() + { + ReadOnlySpan span = new ReadOnlySpan(new byte[] { 5, 1, 77, 2, 3, 77, 77, 4, 5, 77, 77, 77, 88, 6, 6, 77, 77, 88, 9 }); + ReadOnlySpan value = new ReadOnlySpan(new byte[] { 5, 1, 77 }); + int index = span.LastIndexOf(value); + Assert.Equal(0, index); + } + + [Fact] + public static void LastIndexOfSequenceMultipleMatch_Byte() + { + ReadOnlySpan span = new ReadOnlySpan(new byte[] { 1, 2, 3, 1, 2, 3, 1, 2, 3, 1 }); + ReadOnlySpan value = new ReadOnlySpan(new byte[] { 2, 3 }); + int index = span.LastIndexOf(value); + Assert.Equal(7, index); + } + + [Fact] + public static void LastIndexOfSequenceRestart_Byte() + { + ReadOnlySpan span = new ReadOnlySpan(new byte[] { 0, 1, 77, 2, 3, 77, 77, 4, 5, 77, 77, 77, 88, 6, 6, 77, 77, 8, 9, 77, 0, 1 }); + ReadOnlySpan value = new ReadOnlySpan(new byte[] { 77, 77, 88 }); + int index = span.LastIndexOf(value); + Assert.Equal(10, index); + } + + [Fact] + public static void LastIndexOfSequenceNoMatch_Byte() + { + ReadOnlySpan span = new ReadOnlySpan(new byte[] { 0, 1, 77, 2, 3, 77, 77, 4, 5, 77, 77, 77, 88, 6, 6, 77, 77, 88, 9 }); + ReadOnlySpan value = new ReadOnlySpan(new byte[] { 77, 77, 88, 99 }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceNotEvenAHeadMatch_Byte() + { + ReadOnlySpan span = new ReadOnlySpan(new byte[] { 0, 1, 77, 2, 3, 77, 77, 4, 5, 77, 77, 77, 88, 6, 6, 77, 77, 88, 9 }); + ReadOnlySpan value = new ReadOnlySpan(new byte[] { 100, 77, 88, 99 }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceMatchAtVeryEnd_Byte() + { + ReadOnlySpan span = new ReadOnlySpan(new byte[] { 0, 1, 2, 3, 4, 5 }); + ReadOnlySpan value = new ReadOnlySpan(new byte[] { 3, 4, 5 }); + int index = span.LastIndexOf(value); + Assert.Equal(3, index); + } + + [Fact] + public static void LastIndexOfSequenceJustPastVeryEnd_Byte() + { + ReadOnlySpan span = new ReadOnlySpan(new byte[] { 0, 1, 2, 3, 4, 5 }, 0, 5); + ReadOnlySpan value = new ReadOnlySpan(new byte[] { 3, 4, 5 }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceZeroLengthValue_Byte() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new byte[] { 0, 1, 77, 2, 3, 77, 77, 4, 5, 77, 77, 77, 88, 6, 6, 77, 77, 88, 9 }); + ReadOnlySpan value = new ReadOnlySpan(Array.Empty()); + int index = span.LastIndexOf(value); + Assert.Equal(0, index); + } + + [Fact] + public static void LastIndexOfSequenceZeroLengthSpan_Byte() + { + ReadOnlySpan span = new ReadOnlySpan(Array.Empty()); + ReadOnlySpan value = new ReadOnlySpan(new byte[] { 1, 2, 3 }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValue_Byte() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new byte[] { 0, 1, 2, 3, 4, 5 }); + ReadOnlySpan value = new ReadOnlySpan(new byte[] { 2 }); + int index = span.LastIndexOf(value); + Assert.Equal(2, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValueAtVeryEnd_Byte() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new byte[] { 0, 1, 2, 3, 4, 5 }); + ReadOnlySpan value = new ReadOnlySpan(new byte[] { 5 }); + int index = span.LastIndexOf(value); + Assert.Equal(5, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValueMultipleTimes_Byte() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new byte[] { 0, 1, 5, 3, 4, 5 }); + ReadOnlySpan value = new ReadOnlySpan(new byte[] { 5 }); + int index = span.LastIndexOf(value); + Assert.Equal(5, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValueJustPasttVeryEnd_Byte() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new byte[] { 0, 1, 2, 3, 4, 5 }, 0, 5); + ReadOnlySpan value = new ReadOnlySpan(new byte[] { 5 }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOfSequence.char.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOfSequence.char.cs new file mode 100644 index 0000000000..63f6635f76 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/LastIndexOfSequence.char.cs @@ -0,0 +1,133 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + [Fact] + public static void LastIndexOfSequenceMatchAtStart_Char() + { + ReadOnlySpan span = new ReadOnlySpan(new char[] { '5', '1', '7', '2', '3', '7', '7', '4', '5', '7', '7', '7', '8', '6', '6', '7', '7', '8', '9' }); + ReadOnlySpan value = new ReadOnlySpan(new char[] { '5', '1', '7' }); + int index = span.LastIndexOf(value); + Assert.Equal(0, index); + } + + [Fact] + public static void LastIndexOfSequenceMultipleMatch_Char() + { + ReadOnlySpan span = new ReadOnlySpan(new char[] { '1', '2', '3', '1', '2', '3', '1', '2', '3', '1' }); + ReadOnlySpan value = new ReadOnlySpan(new char[] { '2', '3' }); + int index = span.LastIndexOf(value); + Assert.Equal(7, index); + } + + [Fact] + public static void LastIndexOfSequenceRestart_Char() + { + ReadOnlySpan span = new ReadOnlySpan(new char[] { '5', '1', '7', '2', '3', '7', '7', '4', '5', '7', '7', '7', '8', '6', '6', '7', '7', '6', '9', '7', '0', '1' }); + ReadOnlySpan value = new ReadOnlySpan(new char[] { '7', '7', '8' }); + int index = span.LastIndexOf(value); + Assert.Equal(10, index); + } + + [Fact] + public static void LastIndexOfSequenceNoMatch_Char() + { + ReadOnlySpan span = new ReadOnlySpan(new char[] { '0', '1', '7', '2', '3', '7', '7', '4', '5', '7', '7', '7', '8', '6', '6', '7', '7', '8', '9' }); + ReadOnlySpan value = new ReadOnlySpan(new char[] { '7', '7', '8', 'X' }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceNotEvenAHeadMatch_Char() + { + ReadOnlySpan span = new ReadOnlySpan(new char[] { '0', '1', '7', '2', '3', '7', '7', '4', '5', '7', '7', '7', '8', '6', '6', '7', '7', '8', '9' }); + ReadOnlySpan value = new ReadOnlySpan(new char[] { 'X', '7', '8', '9' }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceMatchAtVeryEnd_Char() + { + ReadOnlySpan span = new ReadOnlySpan(new char[] { '0', '1', '2', '3', '4', '5' }); + ReadOnlySpan value = new ReadOnlySpan(new char[] { '3', '4', '5' }); + int index = span.LastIndexOf(value); + Assert.Equal(3, index); + } + + [Fact] + public static void LastIndexOfSequenceJustPastVeryEnd_Char() + { + ReadOnlySpan span = new ReadOnlySpan(new char[] { '0', '1', '2', '3', '4', '5' }, 0, 5); + ReadOnlySpan value = new ReadOnlySpan(new char[] { '3', '4', '5' }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceZeroLengthValue_Char() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new char[] { '0', '1', '7', '2', '3', '7', '7', '4', '5', '7', '7', '7', '8', '6', '6', '7', '7', '8', '9' }); + ReadOnlySpan value = new ReadOnlySpan(Array.Empty()); + int index = span.LastIndexOf(value); + Assert.Equal(0, index); + } + + [Fact] + public static void LastIndexOfSequenceZeroLengthSpan_Char() + { + ReadOnlySpan span = new ReadOnlySpan(Array.Empty()); + ReadOnlySpan value = new ReadOnlySpan(new char[] { '1', '2', '3' }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValue_Char() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new char[] { '0', '1', '2', '3', '4', '5' }); + ReadOnlySpan value = new ReadOnlySpan(new char[] { '2' }); + int index = span.LastIndexOf(value); + Assert.Equal(2, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValueAtVeryEnd_Char() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new char[] { '0', '1', '2', '3', '4', '5' }); + ReadOnlySpan value = new ReadOnlySpan(new char[] { '5' }); + int index = span.LastIndexOf(value); + Assert.Equal(5, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValueMultipleTimes_Char() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new char[] { '0', '1', '5', '3', '4', '5' }); + ReadOnlySpan value = new ReadOnlySpan(new char[] { '5' }); + int index = span.LastIndexOf(value); + Assert.Equal(5, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValueJustPasttVeryEnd_Char() + { + // A zero-length value is always "found" at the start of the span. + ReadOnlySpan span = new ReadOnlySpan(new char[] { '0', '1', '2', '3', '4', '5' }, 0, 5); + ReadOnlySpan value = new ReadOnlySpan(new char[] { '5' }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/NonPortableCast.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/NonPortableCast.cs index 2a360a205c..1153e66bf5 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlySpan/NonPortableCast.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/NonPortableCast.cs @@ -4,31 +4,52 @@ using Xunit; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.SpanTests { public static partial class ReadOnlySpanTests { [Fact] - public static void PortableCastUIntToUShort() + public static void NonPortableCastUIntToUShort() { uint[] a = { 0x44332211, 0x88776655 }; ReadOnlySpan span = new ReadOnlySpan(a); ReadOnlySpan asUShort = span.NonPortableCast(); - Assert.True(Unsafe.AreSame(ref Unsafe.As(ref span.DangerousGetPinnableReference()), ref asUShort.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref Unsafe.As(ref Unsafe.AsRef(in MemoryMarshal.GetReference(span))), ref Unsafe.AsRef(in MemoryMarshal.GetReference(asUShort)))); asUShort.Validate(0x2211, 0x4433, 0x6655, 0x8877); } [Fact] - public static void PortableCastToTypeContainsReferences() + public static void NonPortableCastShortToLong() + { + short[] a = { 0x1234, 0x2345, 0x3456, 0x4567, 0x5678 }; + ReadOnlySpan span = new ReadOnlySpan(a); + ReadOnlySpan asLong = span.NonPortableCast(); + + Assert.True(Unsafe.AreSame(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), ref MemoryMarshal.GetReference(asLong))); + asLong.Validate(0x4567345623451234); + } + + [Fact] + public static unsafe void NonPortableCastOverflow() + { + ReadOnlySpan span = new ReadOnlySpan(null, Int32.MaxValue); + + TestHelpers.AssertThrows(span, (_span) => _span.NonPortableCast().DontBox()); + TestHelpers.AssertThrows(span, (_span) => _span.NonPortableCast().DontBox()); + } + + [Fact] + public static void NonPortableCastToTypeContainsReferences() { ReadOnlySpan span = new ReadOnlySpan(Array.Empty()); TestHelpers.AssertThrows(span, (_span) => _span.NonPortableCast().DontBox()); } [Fact] - public static void PortableCastFromTypeContainsReferences() + public static void NonPortableCastFromTypeContainsReferences() { ReadOnlySpan span = new ReadOnlySpan(Array.Empty()); TestHelpers.AssertThrows(span, (_span) => _span.NonPortableCast().DontBox()); diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/Overflow.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/Overflow.cs index dc9ed088e7..e2e1a07524 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlySpan/Overflow.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/Overflow.cs @@ -4,6 +4,7 @@ using Xunit; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.SpanTests { @@ -36,9 +37,9 @@ namespace System.SpanTests try { ref Guid memory = ref Unsafe.AsRef(memBlock.ToPointer()); - var span = new ReadOnlySpan(memBlock.ToPointer(), GuidThreeGiBLimit); + var span = new ReadOnlySpan(memBlock.ToPointer(), s_guidThreeGiBLimit); - int bigIndex = checked(GuidTwoGiBLimit + 1); + int bigIndex = checked(s_guidTwoGiBLimit + 1); uint byteOffset = checked((uint)bigIndex * (uint)sizeof(Guid)); Assert.True(byteOffset > int.MaxValue); // Make sure byteOffset actually overflows 2Gb, or this test is pointless. Guid expectedGuid = Guid.NewGuid(); @@ -48,10 +49,10 @@ namespace System.SpanTests Assert.Equal(expectedGuid, actualGuid); ReadOnlySpan slice = span.Slice(bigIndex); - Assert.True(Unsafe.AreSame(ref expected, ref slice.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref expected, ref Unsafe.AsRef(in MemoryMarshal.GetReference(slice)))); slice = span.Slice(bigIndex, 1); - Assert.True(Unsafe.AreSame(ref expected, ref slice.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref expected, ref Unsafe.AsRef(in MemoryMarshal.GetReference(slice)))); } finally { @@ -65,8 +66,7 @@ namespace System.SpanTests private const long TwoGiB = 2L * 1024L * 1024L * 1024L; private const long OneGiB = 1L * 1024L * 1024L * 1024L; - private static readonly int GuidThreeGiBLimit = (int)(ThreeGiB / Unsafe.SizeOf()); // sizeof(Guid) requires unsafe keyword and I don't want to mark the entire class unsafe. - private static readonly int GuidTwoGiBLimit = (int)(TwoGiB / Unsafe.SizeOf()); - private static readonly int GuidOneGiBLimit = (int)(OneGiB / Unsafe.SizeOf()); + private static readonly int s_guidThreeGiBLimit = (int)(ThreeGiB / Unsafe.SizeOf()); // sizeof(Guid) requires unsafe keyword and I don't want to mark the entire class unsafe. + private static readonly int s_guidTwoGiBLimit = (int)(TwoGiB / Unsafe.SizeOf()); } } diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/Overlaps.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/Overlaps.cs new file mode 100644 index 0000000000..ed572d9f36 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/Overlaps.cs @@ -0,0 +1,994 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + private static void DoubleEachElementForwards(ReadOnlySpan source, Span destination) + { + if (source.Length != destination.Length) + throw new ArgumentException(); + + // This loop below moves forwards, so if there is an overlap and destination starts + // after source then the loop will overwrite unread data without making a copy first. + + if (source.Overlaps(destination, out int elementOffset) && elementOffset > 0) + source = source.ToArray(); + + for (int i = 0; i < source.Length; i++) + destination[i] = 2 * source[i]; + } + + [Fact] + public static void TestAlignedForwards() + { + for (int i = 0; i < 14; i++) + { + int[] a = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + + ReadOnlySpan source = a.AsReadOnlySpan().Slice(7, 5); + + Span expected = new int[a.Length].AsSpan().Slice(i, 5); + Span actual = a.AsSpan().Slice(i, 5); + + DoubleEachElementForwards(source, expected); + DoubleEachElementForwards(source, actual); + + Assert.Equal(expected.ToArray(), actual.ToArray()); + } + } + + [Fact] + public static void TestUnalignedForwards() + { + Assert.Throws(() => + { + int[] a = new int[] { 1, 2, 3, 4, 5, 6 }; + + ReadOnlySpan source = a.AsReadOnlySpan().AsBytes() + .Slice(2, 5 * sizeof(int)) + .NonPortableCast(); + + Span actual = a.AsSpan().Slice(0, 5); + + DoubleEachElementForwards(source, actual); + }); + + Assert.Throws(() => + { + int[] a = new int[] { 1, 2, 3, 4, 5, 6 }; + + ReadOnlySpan source = a.AsReadOnlySpan().AsBytes() + .Slice(2, 5 * sizeof(int)) + .NonPortableCast(); + + Span actual = a.AsSpan().Slice(1, 5); + + DoubleEachElementForwards(source, actual); + }); + } + + private static void DoubleEachElementBackwards(ReadOnlySpan source, Span destination) + { + if (source.Length != destination.Length) + throw new ArgumentException(); + + // This loop below moves backwards, so if there is an overlap and destination starts + // before source then the loop will overwrite unread data without making a copy first. + + if (source.Overlaps(destination, out int elementOffset) && elementOffset < 0) + source = source.ToArray(); + + for (int i = source.Length - 1; i >= 0; i--) + destination[i] = 2 * source[i]; + } + + [Fact] + public static void TestAlignedBackwards() + { + for (int i = 0; i < 14; i++) + { + int[] a = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + + ReadOnlySpan source = a.AsReadOnlySpan().Slice(7, 5); + + Span expected = new int[a.Length].AsSpan().Slice(i, 5); + Span actual = a.AsSpan().Slice(i, 5); + + DoubleEachElementBackwards(source, expected); + DoubleEachElementBackwards(source, actual); + + Assert.Equal(expected.ToArray(), actual.ToArray()); + } + } + + [Fact] + public static void TestUnalignedBackwards() + { + Assert.Throws(() => + { + int[] a = new int[] { 1, 2, 3, 4, 5, 6 }; + + ReadOnlySpan source = a.AsReadOnlySpan().AsBytes() + .Slice(2, 5 * sizeof(int)) + .NonPortableCast(); + + Span actual = a.AsSpan().Slice(0, 5); + + DoubleEachElementBackwards(source, actual); + }); + + Assert.Throws(() => + { + int[] a = new int[] { 1, 2, 3, 4, 5, 6 }; + + ReadOnlySpan source = a.AsReadOnlySpan().AsBytes() + .Slice(2, 5 * sizeof(int)) + .NonPortableCast(); + + Span actual = a.AsSpan().Slice(1, 5); + + DoubleEachElementBackwards(source, actual); + }); + } + + [Fact] + public static void SizeOf1Overlaps() + { + byte[] a = new byte[16]; + + Assert.True(a.AsReadOnlySpan().Slice(0, 12).Overlaps(a.AsReadOnlySpan().Slice(8, 8), out int elementOffset)); + Assert.Equal(8, elementOffset); + } + + [Fact] + public static void SizeOf16Overlaps() + { + Guid[] a = new Guid[16]; + + Assert.True(a.AsReadOnlySpan().Slice(0, 12).Overlaps(a.AsReadOnlySpan().Slice(8, 8), out int elementOffset)); + Assert.Equal(8, elementOffset); + } + + // + // The following tests were all generated with this (otherwise unused) method: + // + private static string GenerateOverlapsTests() + { + const int count = 4; + string result = ""; + + for (int x1 = 0; x1 < count; x1++) + { + for (int x2 = x1; x2 < count; x2++) + { + for (int y1 = 0; y1 < count; y1++) + { + for (int y2 = y1; y2 < count; y2++) + { + bool expected = (x1 < x2) && (y1 < y2) && (x1 < y2) && (y1 < x2); + + result += $"[InlineData({x1 * 100}, {x2 * 100}, {y1 * 100}, {y2 * 100}, {(expected ? "true" : "false")})]\r\n"; + } + } + } + } + + return result; + } + + // + // 0 + // first: | + // second: | + // 0 + // + [InlineData(0, 0, 0, 0, false)] + + // + // 0 + // first: | + // second: [---------) + // 0 100 + // + [InlineData(0, 0, 0, 100, false)] + + // + // 0 + // first: | + // second: [-------------------) + // 0 200 + // + [InlineData(0, 0, 0, 200, false)] + + // + // 0 + // first: | + // second: [-----------------------------) + // 0 300 + // + [InlineData(0, 0, 0, 300, false)] + + // + // 0 + // first: | + // second: | + // 100 + // + [InlineData(0, 0, 100, 100, false)] + + // + // 0 + // first: | + // second: [---------) + // 100 200 + // + [InlineData(0, 0, 100, 200, false)] + + // + // 0 + // first: | + // second: [-------------------) + // 100 300 + // + [InlineData(0, 0, 100, 300, false)] + + // + // 0 + // first: | + // second: | + // 200 + // + [InlineData(0, 0, 200, 200, false)] + + // + // 0 + // first: | + // second: [---------) + // 200 300 + // + [InlineData(0, 0, 200, 300, false)] + + // + // 0 + // first: | + // second: | + // 300 + // + [InlineData(0, 0, 300, 300, false)] + + // + // 0 100 + // first: [---------) + // second: | + // 0 + // + [InlineData(0, 100, 0, 0, false)] + + // + // 0 100 + // first: [---------) + // second: [---------) + // 0 100 + // + [InlineData(0, 100, 0, 100, true)] + + // + // 0 100 + // first: [---------) + // second: [-------------------) + // 0 200 + // + [InlineData(0, 100, 0, 200, true)] + + // + // 0 100 + // first: [---------) + // second: [-----------------------------) + // 0 300 + // + [InlineData(0, 100, 0, 300, true)] + + // + // 0 100 + // first: [---------) + // second: | + // 100 + // + [InlineData(0, 100, 100, 100, false)] + + // + // 0 100 + // first: [---------) + // second: [---------) + // 100 200 + // + [InlineData(0, 100, 100, 200, false)] + + // + // 0 100 + // first: [---------) + // second: [-------------------) + // 100 300 + // + [InlineData(0, 100, 100, 300, false)] + + // + // 0 100 + // first: [---------) + // second: | + // 200 + // + [InlineData(0, 100, 200, 200, false)] + + // + // 0 100 + // first: [---------) + // second: [---------) + // 200 300 + // + [InlineData(0, 100, 200, 300, false)] + + // + // 0 100 + // first: [---------) + // second: | + // 300 + // + [InlineData(0, 100, 300, 300, false)] + + // + // 0 200 + // first: [-------------------) + // second: | + // 0 + // + [InlineData(0, 200, 0, 0, false)] + + // + // 0 200 + // first: [-------------------) + // second: [---------) + // 0 100 + // + [InlineData(0, 200, 0, 100, true)] + + // + // 0 200 + // first: [-------------------) + // second: [-------------------) + // 0 200 + // + [InlineData(0, 200, 0, 200, true)] + + // + // 0 200 + // first: [-------------------) + // second: [-----------------------------) + // 0 300 + // + [InlineData(0, 200, 0, 300, true)] + + // + // 0 200 + // first: [-------------------) + // second: | + // 100 + // + [InlineData(0, 200, 100, 100, false)] + + // + // 0 200 + // first: [-------------------) + // second: [---------) + // 100 200 + // + [InlineData(0, 200, 100, 200, true)] + + // + // 0 200 + // first: [-------------------) + // second: [-------------------) + // 100 300 + // + [InlineData(0, 200, 100, 300, true)] + + // + // 0 200 + // first: [-------------------) + // second: | + // 200 + // + [InlineData(0, 200, 200, 200, false)] + + // + // 0 200 + // first: [-------------------) + // second: [---------) + // 200 300 + // + [InlineData(0, 200, 200, 300, false)] + + // + // 0 200 + // first: [-------------------) + // second: | + // 300 + // + [InlineData(0, 200, 300, 300, false)] + + // + // 0 300 + // first: [-----------------------------) + // second: | + // 0 + // + [InlineData(0, 300, 0, 0, false)] + + // + // 0 300 + // first: [-----------------------------) + // second: [---------) + // 0 100 + // + [InlineData(0, 300, 0, 100, true)] + + // + // 0 300 + // first: [-----------------------------) + // second: [-------------------) + // 0 200 + // + [InlineData(0, 300, 0, 200, true)] + + // + // 0 300 + // first: [-----------------------------) + // second: [-----------------------------) + // 0 300 + // + [InlineData(0, 300, 0, 300, true)] + + // + // 0 300 + // first: [-----------------------------) + // second: | + // 100 + // + [InlineData(0, 300, 100, 100, false)] + + // + // 0 300 + // first: [-----------------------------) + // second: [---------) + // 100 200 + // + [InlineData(0, 300, 100, 200, true)] + + // + // 0 300 + // first: [-----------------------------) + // second: [-------------------) + // 100 300 + // + [InlineData(0, 300, 100, 300, true)] + + // + // 0 300 + // first: [-----------------------------) + // second: | + // 200 + // + [InlineData(0, 300, 200, 200, false)] + + // + // 0 300 + // first: [-----------------------------) + // second: [---------) + // 200 300 + // + [InlineData(0, 300, 200, 300, true)] + + // + // 0 300 + // first: [-----------------------------) + // second: | + // 300 + // + [InlineData(0, 300, 300, 300, false)] + + // + // 100 + // first: | + // second: | + // 0 + // + [InlineData(100, 100, 0, 0, false)] + + // + // 100 + // first: | + // second: [---------) + // 0 100 + // + [InlineData(100, 100, 0, 100, false)] + + // + // 100 + // first: | + // second: [-------------------) + // 0 200 + // + [InlineData(100, 100, 0, 200, false)] + + // + // 100 + // first: | + // second: [-----------------------------) + // 0 300 + // + [InlineData(100, 100, 0, 300, false)] + + // + // 100 + // first: | + // second: | + // 100 + // + [InlineData(100, 100, 100, 100, false)] + + // + // 100 + // first: | + // second: [---------) + // 100 200 + // + [InlineData(100, 100, 100, 200, false)] + + // + // 100 + // first: | + // second: [-------------------) + // 100 300 + // + [InlineData(100, 100, 100, 300, false)] + + // + // 100 + // first: | + // second: | + // 200 + // + [InlineData(100, 100, 200, 200, false)] + + // + // 100 + // first: | + // second: [---------) + // 200 300 + // + [InlineData(100, 100, 200, 300, false)] + + // + // 100 + // first: | + // second: | + // 300 + // + [InlineData(100, 100, 300, 300, false)] + + // + // 100 200 + // first: [---------) + // second: | + // 0 + // + [InlineData(100, 200, 0, 0, false)] + + // + // 100 200 + // first: [---------) + // second: [---------) + // 0 100 + // + [InlineData(100, 200, 0, 100, false)] + + // + // 100 200 + // first: [---------) + // second: [-------------------) + // 0 200 + // + [InlineData(100, 200, 0, 200, true)] + + // + // 100 200 + // first: [---------) + // second: [-----------------------------) + // 0 300 + // + [InlineData(100, 200, 0, 300, true)] + + // + // 100 200 + // first: [---------) + // second: | + // 100 + // + [InlineData(100, 200, 100, 100, false)] + + // + // 100 200 + // first: [---------) + // second: [---------) + // 100 200 + // + [InlineData(100, 200, 100, 200, true)] + + // + // 100 200 + // first: [---------) + // second: [-------------------) + // 100 300 + // + [InlineData(100, 200, 100, 300, true)] + + // + // 100 200 + // first: [---------) + // second: | + // 200 + // + [InlineData(100, 200, 200, 200, false)] + + // + // 100 200 + // first: [---------) + // second: [---------) + // 200 300 + // + [InlineData(100, 200, 200, 300, false)] + + // + // 100 200 + // first: [---------) + // second: | + // 300 + // + [InlineData(100, 200, 300, 300, false)] + + // + // 100 300 + // first: [-------------------) + // second: | + // 0 + // + [InlineData(100, 300, 0, 0, false)] + + // + // 100 300 + // first: [-------------------) + // second: [---------) + // 0 100 + // + [InlineData(100, 300, 0, 100, false)] + + // + // 100 300 + // first: [-------------------) + // second: [-------------------) + // 0 200 + // + [InlineData(100, 300, 0, 200, true)] + + // + // 100 300 + // first: [-------------------) + // second: [-----------------------------) + // 0 300 + // + [InlineData(100, 300, 0, 300, true)] + + // + // 100 300 + // first: [-------------------) + // second: | + // 100 + // + [InlineData(100, 300, 100, 100, false)] + + // + // 100 300 + // first: [-------------------) + // second: [---------) + // 100 200 + // + [InlineData(100, 300, 100, 200, true)] + + // + // 100 300 + // first: [-------------------) + // second: [-------------------) + // 100 300 + // + [InlineData(100, 300, 100, 300, true)] + + // + // 100 300 + // first: [-------------------) + // second: | + // 200 + // + [InlineData(100, 300, 200, 200, false)] + + // + // 100 300 + // first: [-------------------) + // second: [---------) + // 200 300 + // + [InlineData(100, 300, 200, 300, true)] + + // + // 100 300 + // first: [-------------------) + // second: | + // 300 + // + [InlineData(100, 300, 300, 300, false)] + + // + // 200 + // first: | + // second: | + // 0 + // + [InlineData(200, 200, 0, 0, false)] + + // + // 200 + // first: | + // second: [---------) + // 0 100 + // + [InlineData(200, 200, 0, 100, false)] + + // + // 200 + // first: | + // second: [-------------------) + // 0 200 + // + [InlineData(200, 200, 0, 200, false)] + + // + // 200 + // first: | + // second: [-----------------------------) + // 0 300 + // + [InlineData(200, 200, 0, 300, false)] + + // + // 200 + // first: | + // second: | + // 100 + // + [InlineData(200, 200, 100, 100, false)] + + // + // 200 + // first: | + // second: [---------) + // 100 200 + // + [InlineData(200, 200, 100, 200, false)] + + // + // 200 + // first: | + // second: [-------------------) + // 100 300 + // + [InlineData(200, 200, 100, 300, false)] + + // + // 200 + // first: | + // second: | + // 200 + // + [InlineData(200, 200, 200, 200, false)] + + // + // 200 + // first: | + // second: [---------) + // 200 300 + // + [InlineData(200, 200, 200, 300, false)] + + // + // 200 + // first: | + // second: | + // 300 + // + [InlineData(200, 200, 300, 300, false)] + + // + // 200 300 + // first: [---------) + // second: | + // 0 + // + [InlineData(200, 300, 0, 0, false)] + + // + // 200 300 + // first: [---------) + // second: [---------) + // 0 100 + // + [InlineData(200, 300, 0, 100, false)] + + // + // 200 300 + // first: [---------) + // second: [-------------------) + // 0 200 + // + [InlineData(200, 300, 0, 200, false)] + + // + // 200 300 + // first: [---------) + // second: [-----------------------------) + // 0 300 + // + [InlineData(200, 300, 0, 300, true)] + + // + // 200 300 + // first: [---------) + // second: | + // 100 + // + [InlineData(200, 300, 100, 100, false)] + + // + // 200 300 + // first: [---------) + // second: [---------) + // 100 200 + // + [InlineData(200, 300, 100, 200, false)] + + // + // 200 300 + // first: [---------) + // second: [-------------------) + // 100 300 + // + [InlineData(200, 300, 100, 300, true)] + + // + // 200 300 + // first: [---------) + // second: | + // 200 + // + [InlineData(200, 300, 200, 200, false)] + + // + // 200 300 + // first: [---------) + // second: [---------) + // 200 300 + // + [InlineData(200, 300, 200, 300, true)] + + // + // 200 300 + // first: [---------) + // second: | + // 300 + // + [InlineData(200, 300, 300, 300, false)] + + // + // 300 + // first: | + // second: | + // 0 + // + [InlineData(300, 300, 0, 0, false)] + + // + // 300 + // first: | + // second: [---------) + // 0 100 + // + [InlineData(300, 300, 0, 100, false)] + + // + // 300 + // first: | + // second: [-------------------) + // 0 200 + // + [InlineData(300, 300, 0, 200, false)] + + // + // 300 + // first: | + // second: [-----------------------------) + // 0 300 + // + [InlineData(300, 300, 0, 300, false)] + + // + // 300 + // first: | + // second: | + // 100 + // + [InlineData(300, 300, 100, 100, false)] + + // + // 300 + // first: | + // second: [---------) + // 100 200 + // + [InlineData(300, 300, 100, 200, false)] + + // + // 300 + // first: | + // second: [-------------------) + // 100 300 + // + [InlineData(300, 300, 100, 300, false)] + + // + // 300 + // first: | + // second: | + // 200 + // + [InlineData(300, 300, 200, 200, false)] + + // + // 300 + // first: | + // second: [---------) + // 200 300 + // + [InlineData(300, 300, 200, 300, false)] + + // + // 300 + // first: | + // second: | + // 300 + // + [InlineData(300, 300, 300, 300, false)] + + [Theory] + public static void Overlap(int x1, int y1, int x2, int y2, bool expected) + { + ReadOnlySpan a = new int[300]; + + Assert.Equal(expected, a.Slice(x1, y1 - x1).Overlaps(a.Slice(x2, y2 - x2))); + Assert.Equal(expected, a.Slice(x1, y1 - x1).Overlaps(a.Slice(x2, y2 - x2), out int elementOffset)); + Assert.Equal(expected ? x2 - x1 : 0, elementOffset); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/SequenceEqual.byte.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/SequenceEqual.byte.cs index fa5d6dce35..02dd9ec1ab 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlySpan/SequenceEqual.byte.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/SequenceEqual.byte.cs @@ -15,7 +15,7 @@ namespace System.SpanTests ReadOnlySpan first = new ReadOnlySpan(a, 1, 0); ReadOnlySpan second = new ReadOnlySpan(a, 2, 0); - bool b = first.SequenceEqual(second); + bool b = first.SequenceEqual(second); Assert.True(b); } @@ -24,7 +24,7 @@ namespace System.SpanTests { byte[] a = { 4, 5, 6 }; ReadOnlySpan span = new ReadOnlySpan(a); - bool b = span.SequenceEqual(span); + bool b = span.SequenceEqual(span); Assert.True(b); } @@ -33,7 +33,7 @@ namespace System.SpanTests { byte[] a = { 4, 5, 6 }; ReadOnlySpan first = new ReadOnlySpan(a, 0, 3); - bool b = first.SequenceEqual(a); + bool b = first.SequenceEqual(a); Assert.True(b); } @@ -45,7 +45,7 @@ namespace System.SpanTests var segment = new ArraySegment(dst, 1, 3); ReadOnlySpan first = new ReadOnlySpan(src, 0, 3); - bool b = first.SequenceEqual(segment); + bool b = first.SequenceEqual(segment); Assert.True(b); } @@ -55,7 +55,7 @@ namespace System.SpanTests byte[] a = { 4, 5, 6 }; ReadOnlySpan first = new ReadOnlySpan(a, 0, 3); ReadOnlySpan second = new ReadOnlySpan(a, 0, 2); - bool b = first.SequenceEqual(second); + bool b = first.SequenceEqual(second); Assert.False(b); } @@ -77,7 +77,7 @@ namespace System.SpanTests ReadOnlySpan firstSpan = new ReadOnlySpan(first); ReadOnlySpan secondSpan = new ReadOnlySpan(second); - bool b = firstSpan.SequenceEqual(secondSpan); + bool b = firstSpan.SequenceEqual(secondSpan); Assert.False(b); } } @@ -96,7 +96,7 @@ namespace System.SpanTests second[length + 1] = 100; ReadOnlySpan span1 = new ReadOnlySpan(first, 1, length); ReadOnlySpan span2 = new ReadOnlySpan(second, 1, length); - bool b = span1.SequenceEqual(span2); + bool b = span1.SequenceEqual(span2); Assert.True(b); } } diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/Slice.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/Slice.cs index 5efa1f1b3e..d4aa7f076e 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlySpan/Slice.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/Slice.cs @@ -4,6 +4,7 @@ using Xunit; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.SpanTests { @@ -15,7 +16,7 @@ namespace System.SpanTests int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 }; ReadOnlySpan span = new ReadOnlySpan(a).Slice(6); Assert.Equal(4, span.Length); - Assert.True(Unsafe.AreSame(ref a[6], ref span.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref a[6], ref Unsafe.AsRef(in MemoryMarshal.GetReference(span)))); } [Fact] @@ -24,7 +25,7 @@ namespace System.SpanTests int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 }; ReadOnlySpan span = new ReadOnlySpan(a).Slice(a.Length); Assert.Equal(0, span.Length); - Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref span.DangerousGetPinnableReference(), 1))); + Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref Unsafe.AsRef(in MemoryMarshal.GetReference(span)), 1))); } [Fact] @@ -33,7 +34,7 @@ namespace System.SpanTests int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 }; ReadOnlySpan span = new ReadOnlySpan(a).Slice(3, 5); Assert.Equal(5, span.Length); - Assert.True(Unsafe.AreSame(ref a[3], ref span.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref a[3], ref Unsafe.AsRef(in MemoryMarshal.GetReference(span)))); } [Fact] @@ -42,7 +43,7 @@ namespace System.SpanTests int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 }; ReadOnlySpan span = new ReadOnlySpan(a).Slice(4, 6); Assert.Equal(6, span.Length); - Assert.True(Unsafe.AreSame(ref a[4], ref span.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref a[4], ref Unsafe.AsRef(in MemoryMarshal.GetReference(span)))); } [Fact] @@ -51,7 +52,7 @@ namespace System.SpanTests int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 }; ReadOnlySpan span = new ReadOnlySpan(a).Slice(a.Length, 0); Assert.Equal(0, span.Length); - Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref span.DangerousGetPinnableReference(), 1))); + Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref Unsafe.AsRef(in MemoryMarshal.GetReference(span)), 1))); } [Fact] diff --git a/external/corefx/src/System.Memory/tests/ReadOnlySpan/StartsWith.byte.cs b/external/corefx/src/System.Memory/tests/ReadOnlySpan/StartsWith.byte.cs index 680235f9d6..57fa490884 100644 --- a/external/corefx/src/System.Memory/tests/ReadOnlySpan/StartsWith.byte.cs +++ b/external/corefx/src/System.Memory/tests/ReadOnlySpan/StartsWith.byte.cs @@ -15,7 +15,7 @@ namespace System.SpanTests ReadOnlySpan span = new ReadOnlySpan(a); ReadOnlySpan slice = new ReadOnlySpan(a, 2, 0); - bool b = span.StartsWith(slice); + bool b = span.StartsWith(slice); Assert.True(b); } @@ -24,7 +24,7 @@ namespace System.SpanTests { byte[] a = { 4, 5, 6 }; ReadOnlySpan span = new ReadOnlySpan(a); - bool b = span.StartsWith(span); + bool b = span.StartsWith(span); Assert.True(b); } @@ -34,7 +34,7 @@ namespace System.SpanTests byte[] a = { 4, 5, 6 }; ReadOnlySpan span = new ReadOnlySpan(a, 0, 2); ReadOnlySpan slice = new ReadOnlySpan(a, 0, 3); - bool b = span.StartsWith(slice); + bool b = span.StartsWith(slice); Assert.False(b); } @@ -44,7 +44,7 @@ namespace System.SpanTests byte[] a = { 4, 5, 6 }; ReadOnlySpan span = new ReadOnlySpan(a, 0, 3); ReadOnlySpan slice = new ReadOnlySpan(a, 0, 2); - bool b = span.StartsWith(slice); + bool b = span.StartsWith(slice); Assert.True(b); } @@ -55,7 +55,7 @@ namespace System.SpanTests byte[] b = { 4, 5, 6 }; ReadOnlySpan span = new ReadOnlySpan(a, 0, 3); ReadOnlySpan slice = new ReadOnlySpan(b, 0, 3); - bool c = span.StartsWith(slice); + bool c = span.StartsWith(slice); Assert.True(c); } @@ -77,7 +77,7 @@ namespace System.SpanTests ReadOnlySpan firstSpan = new ReadOnlySpan(first); ReadOnlySpan secondSpan = new ReadOnlySpan(second); - bool b = firstSpan.StartsWith(secondSpan); + bool b = firstSpan.StartsWith(secondSpan); Assert.False(b); } } @@ -96,7 +96,7 @@ namespace System.SpanTests second[length + 1] = 100; ReadOnlySpan span1 = new ReadOnlySpan(first, 1, length); ReadOnlySpan span2 = new ReadOnlySpan(second, 1, length); - bool b = span1.StartsWith(span2); + bool b = span1.StartsWith(span2); Assert.True(b); } } diff --git a/external/corefx/src/System.Memory/tests/Resources/System.Memory.Tests.rd.xml b/external/corefx/src/System.Memory/tests/Resources/System.Memory.Tests.rd.xml new file mode 100644 index 0000000000..7931c600d4 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Resources/System.Memory.Tests.rd.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Memory/tests/Span/AsBytes.cs b/external/corefx/src/System.Memory/tests/Span/AsBytes.cs index 0eabb23aba..dbbe885aaa 100644 --- a/external/corefx/src/System.Memory/tests/Span/AsBytes.cs +++ b/external/corefx/src/System.Memory/tests/Span/AsBytes.cs @@ -4,6 +4,7 @@ using Xunit; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.SpanTests { @@ -16,7 +17,7 @@ namespace System.SpanTests Span span = new Span(a); Span asBytes = span.AsBytes(); - Assert.True(Unsafe.AreSame(ref Unsafe.As(ref span.DangerousGetPinnableReference()), ref asBytes.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), ref MemoryMarshal.GetReference(asBytes))); asBytes.Validate(0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88); } diff --git a/external/corefx/src/System.Memory/tests/Span/AsSpan.cs b/external/corefx/src/System.Memory/tests/Span/AsSpan.cs index 8413a454a0..63befe61c1 100644 --- a/external/corefx/src/System.Memory/tests/Span/AsSpan.cs +++ b/external/corefx/src/System.Memory/tests/Span/AsSpan.cs @@ -39,7 +39,7 @@ namespace System.SpanTests { int[] empty = Array.Empty(); Span span = empty.AsSpan(); - span.Validate(); + span.ValidateNonNullEmpty(); } [Fact] @@ -79,12 +79,12 @@ namespace System.SpanTests int[] empty = Array.Empty(); ArraySegment segmentEmpty = new ArraySegment(empty); Span spanEmpty = segmentEmpty.AsSpan(); - spanEmpty.Validate(); + spanEmpty.ValidateNonNullEmpty(); int[] a = { 91, 92, -93, 94 }; ArraySegment segmentInt = new ArraySegment(a, 0, 0); Span spanInt = segmentInt.AsSpan(); - spanInt.Validate(); + spanInt.ValidateNonNullEmpty(); } } } diff --git a/external/corefx/src/System.Memory/tests/Span/Clear.cs b/external/corefx/src/System.Memory/tests/Span/Clear.cs index ecd0c4a34f..117146191f 100644 --- a/external/corefx/src/System.Memory/tests/Span/Clear.cs +++ b/external/corefx/src/System.Memory/tests/Span/Clear.cs @@ -57,8 +57,8 @@ namespace System.SpanTests var actualSpan = new Span(actualFull, start, length - start - 1); actualSpan.Clear(); - var actual = actualSpan.ToArray(); - var expected = expectedSpan.ToArray(); + byte[] actual = actualSpan.ToArray(); + byte[] expected = expectedSpan.ToArray(); Assert.Equal(expected, actual); Assert.Equal(initial, actualFull[0]); Assert.Equal(initial, actualFull[length - 1]); @@ -83,8 +83,8 @@ namespace System.SpanTests var actualSpan = new Span(p + start, length - start - 1); actualSpan.Clear(); - var actual = actualSpan.ToArray(); - var expected = expectedSpan.ToArray(); + byte[] actual = actualSpan.ToArray(); + byte[] expected = expectedSpan.ToArray(); Assert.Equal(expected, actual); Assert.Equal(initial, actualFull[0]); Assert.Equal(initial, actualFull[length - 1]); @@ -108,8 +108,8 @@ namespace System.SpanTests var actualSpan = new Span(actualFull, start, length - start - 1); actualSpan.Clear(); - var actual = actualSpan.ToArray(); - var expected = expectedSpan.ToArray(); + IntPtr[] actual = actualSpan.ToArray(); + IntPtr[] expected = expectedSpan.ToArray(); Assert.Equal(expected, actual); Assert.Equal(initial, actualFull[0]); Assert.Equal(initial, actualFull[length - 1]); @@ -131,7 +131,6 @@ namespace System.SpanTests Assert.Equal(expected, actual); } - [Fact] public static void ClearValueTypeWithoutReferences() { @@ -202,8 +201,8 @@ namespace System.SpanTests [Fact] public static void ClearEnumType() { - TestEnum[] actual = {TestEnum.e0, TestEnum.e1, TestEnum.e2}; - TestEnum[] expected = {default(TestEnum), default(TestEnum), default(TestEnum) }; + TestEnum[] actual = { TestEnum.e0, TestEnum.e1, TestEnum.e2 }; + TestEnum[] expected = { default, default, default }; var span = new Span(actual); span.Clear(); @@ -218,9 +217,9 @@ namespace System.SpanTests new TestValueTypeWithReference() { I = 2, S = "b" }, new TestValueTypeWithReference() { I = 3, S = "c" } }; TestValueTypeWithReference[] expected = { - default(TestValueTypeWithReference), - default(TestValueTypeWithReference), - default(TestValueTypeWithReference) }; + default, + default, + default }; var span = new Span(actual); span.Clear(); diff --git a/external/corefx/src/System.Memory/tests/Span/CopyTo.cs b/external/corefx/src/System.Memory/tests/Span/CopyTo.cs index a626ad2a3b..d1db216a1b 100644 --- a/external/corefx/src/System.Memory/tests/Span/CopyTo.cs +++ b/external/corefx/src/System.Memory/tests/Span/CopyTo.cs @@ -127,55 +127,55 @@ namespace System.SpanTests public static void CopyToArray() { int[] src = { 1, 2, 3 }; - int[] dst = { 99, 100, 101 }; + Span dst = new int[3] { 99, 100, 101 }; src.CopyTo(dst); - Assert.Equal(src, dst); + Assert.Equal(src, dst.ToArray()); } [Fact] public static void CopyToSingleArray() { int[] src = { 1 }; - int[] dst = { 99 }; + Span dst = new int[1] { 99 }; src.CopyTo(dst); - Assert.Equal(src, dst); + Assert.Equal(src, dst.ToArray()); } [Fact] public static void CopyToEmptyArray() { int[] src = { }; - int[] dst = { 99, 100, 101 }; + Span dst = new int[3] { 99, 100, 101 }; src.CopyTo(dst); int[] expected = { 99, 100, 101 }; - Assert.Equal(expected, dst); + Assert.Equal(expected, dst.ToArray()); - int[] dstEmpty = { }; + Span dstEmpty = new int[0] { }; - src.CopyTo(dst); + src.CopyTo(dstEmpty); int[] expectedEmpty = { }; - Assert.Equal(expectedEmpty, dstEmpty); + Assert.Equal(expectedEmpty, dstEmpty.ToArray()); } [Fact] public static void CopyToLongerArray() { int[] src = { 1, 2, 3 }; - int[] dst = { 99, 100, 101, 102 }; + Span dst = new int[4] { 99, 100, 101, 102 }; src.CopyTo(dst); int[] expected = { 1, 2, 3, 102 }; - Assert.Equal(expected, dst); + Assert.Equal(expected, dst.ToArray()); } [Fact] public static void CopyToShorterArray() { int[] src = { 1, 2, 3 }; - int[] dst = { 99, 100 }; + int[] dst = new int[2] { 99, 100 }; TestHelpers.AssertThrows(src, (_src) => _src.CopyTo(dst)); int[] expected = { 99, 100 }; @@ -186,7 +186,7 @@ namespace System.SpanTests public static void CopyToCovariantArray() { string[] src = new string[] { "Hello" }; - object[] dst = new object[] { "world" }; + Span dst = new object[] { "world" }; src.CopyTo(dst); Assert.Equal("Hello", dst[0]); @@ -197,6 +197,7 @@ namespace System.SpanTests // the residual chunk of size (bufferSize % 4GB). The inputs sizes to this method, 4GB and 4GB+256B, // test the two size selection paths in CoptyTo method - memory size that is multiple of 4GB or, // a multiple of 4GB + some more size. + [ActiveIssue(25254)] [Theory] [OuterLoop] [PlatformSpecific(TestPlatforms.Windows | TestPlatforms.OSX)] @@ -241,16 +242,18 @@ namespace System.SpanTests for (int count = 0; count < GuidCount; ++count) { - var guidfirst = Unsafe.Add(ref memoryFirst, count); - var guidSecond = Unsafe.Add(ref memorySecond, count); + Guid guidfirst = Unsafe.Add(ref memoryFirst, count); + Guid guidSecond = Unsafe.Add(ref memorySecond, count); Assert.Equal(guidfirst, guidSecond); } } } finally { - if (allocatedFirst) AllocationHelper.ReleaseNative(ref memBlockFirst); - if (allocatedSecond) AllocationHelper.ReleaseNative(ref memBlockSecond); + if (allocatedFirst) + AllocationHelper.ReleaseNative(ref memBlockFirst); + if (allocatedSecond) + AllocationHelper.ReleaseNative(ref memBlockSecond); } } } diff --git a/external/corefx/src/System.Memory/tests/Span/CtorArray.cs b/external/corefx/src/System.Memory/tests/Span/CtorArray.cs index 15c4c6eef9..6105a99748 100644 --- a/external/corefx/src/System.Memory/tests/Span/CtorArray.cs +++ b/external/corefx/src/System.Memory/tests/Span/CtorArray.cs @@ -62,10 +62,10 @@ namespace System.SpanTests Span span; span = new Span(empty); - span.Validate(); + span.ValidateNonNullEmpty(); span = new Span(empty, 0, empty.Length); - span.Validate(); + span.ValidateNonNullEmpty(); } [Fact] diff --git a/external/corefx/src/System.Memory/tests/Span/CtorArrayIntInt.cs b/external/corefx/src/System.Memory/tests/Span/CtorArrayIntInt.cs index 26a8c97eac..7c84bfc878 100644 --- a/external/corefx/src/System.Memory/tests/Span/CtorArrayIntInt.cs +++ b/external/corefx/src/System.Memory/tests/Span/CtorArrayIntInt.cs @@ -73,7 +73,7 @@ namespace System.SpanTests // Valid for start to equal the array length. This returns an empty span that starts "just past the array." int[] a = { 91, 92, 93 }; Span span = new Span(a, 3, 0); - span.Validate(); + span.ValidateNonNullEmpty(); } } } diff --git a/external/corefx/src/System.Memory/tests/Span/CtorPointerInt.cs b/external/corefx/src/System.Memory/tests/Span/CtorPointerInt.cs index 2d11fa5829..9194da2fca 100644 --- a/external/corefx/src/System.Memory/tests/Span/CtorPointerInt.cs +++ b/external/corefx/src/System.Memory/tests/Span/CtorPointerInt.cs @@ -4,6 +4,7 @@ using Xunit; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; #pragma warning disable 0649 //Field 'SpanTests.InnerStruct.J' is never assigned to, and will always have its default value 0 @@ -21,7 +22,7 @@ namespace System.SpanTests { Span span = new Span(pa, 3); span.Validate(90, 91, 92); - Assert.True(Unsafe.AreSame(ref Unsafe.AsRef(pa), ref span.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref Unsafe.AsRef(pa), ref MemoryMarshal.GetReference(span))); } } } @@ -33,7 +34,7 @@ namespace System.SpanTests { Span span = new Span((void*)null, 0); span.Validate(); - Assert.True(Unsafe.AreSame(ref Unsafe.AsRef((void*)null), ref span.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref Unsafe.AsRef((void*)null), ref MemoryMarshal.GetReference(span))); } } diff --git a/external/corefx/src/System.Memory/tests/Span/DangerousCreate.cs b/external/corefx/src/System.Memory/tests/Span/DangerousCreate.cs index f41fa1e9ec..ad6e968b8b 100644 --- a/external/corefx/src/System.Memory/tests/Span/DangerousCreate.cs +++ b/external/corefx/src/System.Memory/tests/Span/DangerousCreate.cs @@ -4,6 +4,8 @@ using Xunit; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + using static System.TestHelpers; namespace System.SpanTests @@ -24,7 +26,7 @@ namespace System.SpanTests Span span = Span.DangerousCreate(testClass, ref testClass.C1, 3); span.Validate('b', 'c', 'd'); - ref char pc1 = ref span.DangerousGetPinnableReference(); + ref char pc1 = ref MemoryMarshal.GetReference(span); Assert.True(Unsafe.AreSame(ref testClass.C1, ref pc1)); } } diff --git a/external/corefx/src/System.Memory/tests/Span/DangerousGetPinnableReference.cs b/external/corefx/src/System.Memory/tests/Span/DangerousGetPinnableReference.cs deleted file mode 100644 index 11e0bcf31c..0000000000 --- a/external/corefx/src/System.Memory/tests/Span/DangerousGetPinnableReference.cs +++ /dev/null @@ -1,68 +0,0 @@ -// 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 Xunit; -using System.Runtime.CompilerServices; -using static System.TestHelpers; - -namespace System.SpanTests -{ - public static partial class SpanTests - { - [Fact] - public static void DangerousGetPinnableReferenceArray() - { - int[] a = { 91, 92, 93, 94, 95 }; - Span span = new Span(a, 1, 3); - ref int pinnableReference = ref span.DangerousGetPinnableReference(); - Assert.True(Unsafe.AreSame(ref a[1], ref pinnableReference)); - } - - [Fact] - public static void DangerousGetPinnableReferenceArrayPastEnd() - { - // The only real difference between DangerousGetPinnableReference() and "ref span[0]" is that - // DangerousGetPinnableReference() of a zero-length won't throw an IndexOutOfRange. - - int[] a = { 91, 92, 93, 94, 95 }; - Span span = new Span(a, a.Length, 0); - ref int pinnableReference = ref span.DangerousGetPinnableReference(); - ref int expected = ref Unsafe.Add(ref a[a.Length - 1], 1); - Assert.True(Unsafe.AreSame(ref expected, ref pinnableReference)); - } - - [Fact] - public static void DangerousGetPinnableReferencePointer() - { - unsafe - { - int i = 42; - Span span = new Span(&i, 1); - ref int pinnableReference = ref span.DangerousGetPinnableReference(); - Assert.True(Unsafe.AreSame(ref i, ref pinnableReference)); - } - } - - [Fact] - public static void DangerousGetPinnableReferencePointerDangerousCreate1() - { - TestClass testClass = new TestClass(); - Span span = Span.DangerousCreate(testClass, ref testClass.C1, 3); - - ref char pinnableReference = ref span.DangerousGetPinnableReference(); - Assert.True(Unsafe.AreSame(ref testClass.C1, ref pinnableReference)); - } - - [Fact] - public static void DangerousGetPinnableReferenceEmpty() - { - unsafe - { - Span span = Span.Empty; - ref int pinnableReference = ref span.DangerousGetPinnableReference(); - Assert.True(Unsafe.AreSame(ref Unsafe.AsRef(null), ref pinnableReference)); - } - } - } -} diff --git a/external/corefx/src/System.Memory/tests/Span/Empty.cs b/external/corefx/src/System.Memory/tests/Span/Empty.cs index 8d8d1a4dc7..60104e52f9 100644 --- a/external/corefx/src/System.Memory/tests/Span/Empty.cs +++ b/external/corefx/src/System.Memory/tests/Span/Empty.cs @@ -4,6 +4,7 @@ using Xunit; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.SpanTests { @@ -18,7 +19,7 @@ namespace System.SpanTests unsafe { ref int expected = ref Unsafe.AsRef(null); - ref int actual = ref empty.DangerousGetPinnableReference(); + ref int actual = ref MemoryMarshal.GetReference(empty); Assert.True(Unsafe.AreSame(ref expected, ref actual)); } } diff --git a/external/corefx/src/System.Memory/tests/Span/EndsWith.T.cs b/external/corefx/src/System.Memory/tests/Span/EndsWith.T.cs new file mode 100644 index 0000000000..f34f13e07d --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Span/EndsWith.T.cs @@ -0,0 +1,93 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class SpanTests + { + [Fact] + public static void ZeroLengthEndsWith() + { + int[] a = new int[3]; + + Span first = new Span(a, 1, 0); + ReadOnlySpan second = new ReadOnlySpan(a, 2, 0); + bool b = first.EndsWith(second); + Assert.True(b); + } + + [Fact] + public static void SameSpanEndsWith() + { + int[] a = { 4, 5, 6 }; + Span span = new Span(a); + bool b = span.EndsWith(span); + Assert.True(b); + } + + [Fact] + public static void LengthMismatchEndsWith() + { + int[] a = { 4, 5, 6 }; + Span first = new Span(a, 0, 2); + ReadOnlySpan second = new ReadOnlySpan(a, 0, 3); + bool b = first.EndsWith(second); + Assert.False(b); + } + + [Fact] + public static void EndsWithMatch() + { + int[] a = { 4, 5, 6 }; + Span span = new Span(a, 0, 3); + ReadOnlySpan slice = new ReadOnlySpan(a, 1, 2); + bool b = span.EndsWith(slice); + Assert.True(b); + } + + [Fact] + public static void EndsWithMatchDifferentSpans() + { + int[] a = { 4, 5, 6 }; + int[] b = { 4, 5, 6 }; + Span span = new Span(a, 0, 3); + ReadOnlySpan slice = new ReadOnlySpan(b, 0, 3); + bool c = span.EndsWith(slice); + Assert.True(c); + } + + [Fact] + public static void OnEndsWithOfEqualSpansMakeSureEveryElementIsCompared() + { + for (int length = 0; length < 100; length++) + { + TIntLog log = new TIntLog(); + + TInt[] first = new TInt[length]; + TInt[] second = new TInt[length]; + for (int i = 0; i < length; i++) + { + first[i] = second[i] = new TInt(10 * (i + 1), log); + } + + Span firstSpan = new Span(first); + ReadOnlySpan secondSpan = new ReadOnlySpan(second); + bool b = firstSpan.EndsWith(secondSpan); + Assert.True(b); + + // Make sure each element of the array was compared once. (Strictly speaking, it would not be illegal for + // EndsWith to compare an element more than once but that would be a non-optimal implementation and + // a red flag. So we'll stick with the stricter test.) + Assert.Equal(first.Length, log.Count); + foreach (TInt elem in first) + { + int numCompares = log.CountCompares(elem.Value, elem.Value); + Assert.True(numCompares == 1, $"Expected {numCompares} == 1 for element {elem.Value}."); + } + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Span/EndsWith.byte.cs b/external/corefx/src/System.Memory/tests/Span/EndsWith.byte.cs new file mode 100644 index 0000000000..6acf942936 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Span/EndsWith.byte.cs @@ -0,0 +1,104 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class SpanTests + { + [Fact] + public static void ZeroLengthEndsWith_Byte() + { + byte[] a = new byte[3]; + + Span span = new Span(a); + ReadOnlySpan slice = new ReadOnlySpan(a, 2, 0); + bool b = span.EndsWith(slice); + Assert.True(b); + } + + [Fact] + public static void SameSpanEndsWith_Byte() + { + byte[] a = { 4, 5, 6 }; + Span span = new Span(a); + bool b = span.EndsWith(span); + Assert.True(b); + } + + [Fact] + public static void LengthMismatchEndsWith_Byte() + { + byte[] a = { 4, 5, 6 }; + Span span = new Span(a, 0, 2); + ReadOnlySpan slice = new ReadOnlySpan(a, 0, 3); + bool b = span.EndsWith(slice); + Assert.False(b); + } + + [Fact] + public static void EndsWithMatch_Byte() + { + byte[] a = { 4, 5, 6 }; + Span span = new Span(a, 0, 3); + ReadOnlySpan slice = new ReadOnlySpan(a, 1, 2); + bool b = span.EndsWith(slice); + Assert.True(b); + } + + [Fact] + public static void EndsWithMatchDifferentSpans_Byte() + { + byte[] a = { 4, 5, 6 }; + byte[] b = { 4, 5, 6 }; + Span span = new Span(a, 0, 3); + ReadOnlySpan slice = new ReadOnlySpan(b, 0, 3); + bool c = span.EndsWith(slice); + Assert.True(c); + } + + [Fact] + public static void EndsWithNoMatch_Byte() + { + for (int length = 1; length < 32; length++) + { + for (int mismatchIndex = 0; mismatchIndex < length; mismatchIndex++) + { + byte[] first = new byte[length]; + byte[] second = new byte[length]; + for (int i = 0; i < length; i++) + { + first[i] = second[i] = (byte)(i + 1); + } + + second[mismatchIndex] = (byte)(second[mismatchIndex] + 1); + + Span firstSpan = new Span(first); + ReadOnlySpan secondSpan = new ReadOnlySpan(second); + bool b = firstSpan.EndsWith(secondSpan); + Assert.False(b); + } + } + } + + [Fact] + public static void MakeSureNoEndsWithChecksGoOutOfRange_Byte() + { + for (int length = 0; length < 100; length++) + { + byte[] first = new byte[length + 2]; + first[0] = 99; + first[length + 1] = 99; + byte[] second = new byte[length + 2]; + second[0] = 100; + second[length + 1] = 100; + Span span1 = new Span(first, 1, length); + ReadOnlySpan span2 = new ReadOnlySpan(second, 1, length); + bool b = span1.EndsWith(span2); + Assert.True(b); + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Span/Fill.cs b/external/corefx/src/System.Memory/tests/Span/Fill.cs index 929ebede79..6747179ba5 100644 --- a/external/corefx/src/System.Memory/tests/Span/Fill.cs +++ b/external/corefx/src/System.Memory/tests/Span/Fill.cs @@ -50,8 +50,8 @@ namespace System.SpanTests var actualSpan = new Span(actualFull, start, length - start - 1); actualSpan.Fill(fill); - var actual = actualSpan.ToArray(); - var expected = expectedSpan.ToArray(); + byte[] actual = actualSpan.ToArray(); + byte[] expected = expectedSpan.ToArray(); Assert.Equal(expected, actual); Assert.Equal(0, actualFull[0]); Assert.Equal(0, actualFull[length - 1]); diff --git a/external/corefx/src/System.Memory/tests/Span/GetEnumerator.cs b/external/corefx/src/System.Memory/tests/Span/GetEnumerator.cs new file mode 100644 index 0000000000..6413ddfa4e --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Span/GetEnumerator.cs @@ -0,0 +1,84 @@ +// 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 Xunit; +using System.Linq; +using System.Collections.Generic; + +namespace System.SpanTests +{ + public static partial class SpanTests + { + public static IEnumerable IntegerArrays() + { + yield return new object[] { new int[0] }; + yield return new object[] { new int[] { 42 } }; + yield return new object[] { new int[] { 42, 43, 44, 45 } }; + } + + [Theory] + [MemberData(nameof(IntegerArrays))] + public static void GetEnumerator_ForEach_AllValuesReturnedCorrectly(int[] array) + { + Span span = array; + + int sum = 0; + foreach (int i in span) + { + sum += i; + } + + Assert.Equal(Enumerable.Sum(array), sum); + } + + [Theory] + [MemberData(nameof(IntegerArrays))] + public static void GetEnumerator_Manual_AllValuesReturnedCorrectly(int[] array) + { + Span span = array; + + int sum = 0; + Span.Enumerator e = span.GetEnumerator(); + while (e.MoveNext()) + { + ref int i = ref e.Current; + sum += i; + Assert.Equal(e.Current, e.Current); + } + Assert.False(e.MoveNext()); + + Assert.Equal(Enumerable.Sum(array), sum); + } + + [Fact] + public static void GetEnumerator_RefCurrentChangesAreStoredInSpan() + { + Span> values = new ValueWrapper[10]; + Span>.Enumerator e = values.GetEnumerator(); + + int index = 0; + while (e.MoveNext()) + { + e.Current.Value = index++; + } + + for (int i = 0; i < values.Length; i++) + { + Assert.Equal(i, values[i].Value); + } + } + + struct ValueWrapper + { + public T Value; + } + + [Fact] + public static void GetEnumerator_MoveNextOnDefault_ReturnsFalse() + { + Assert.False(default(Span.Enumerator).MoveNext()); + Assert.ThrowsAny(() => default(Span.Enumerator).Current); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Span/ImplicitConversion.cs b/external/corefx/src/System.Memory/tests/Span/ImplicitConversion.cs new file mode 100644 index 0000000000..4d10d55d52 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Span/ImplicitConversion.cs @@ -0,0 +1,27 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class SpanTests + { + [Fact] + public static void NullImplicitCast() + { + int[] dst = null; + Span srcSpan = dst; + Assert.True(Span.Empty == srcSpan); + } + + [Fact] + public static void ArraySegmentDefaultImplicitCast() + { + ArraySegment dst = default; + Span srcSpan = dst; + Assert.True(Span.Empty == srcSpan); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Span/IndexOf.T.cs b/external/corefx/src/System.Memory/tests/Span/IndexOf.T.cs index 8d9736be83..0da12edee9 100644 --- a/external/corefx/src/System.Memory/tests/Span/IndexOf.T.cs +++ b/external/corefx/src/System.Memory/tests/Span/IndexOf.T.cs @@ -27,7 +27,7 @@ namespace System.SpanTests a[i] = 10 * (i + 1); } Span span = new Span(a); - + for (int targetIndex = 0; targetIndex < length; targetIndex++) { int target = a[targetIndex]; @@ -116,5 +116,74 @@ namespace System.SpanTests Assert.Equal(-1, idx); } } + + [Fact] + public static void ZeroLengthIndexOf_String() + { + Span sp = new Span(Array.Empty()); + int idx = sp.IndexOf("a"); + Assert.Equal(-1, idx); + } + + [Fact] + public static void TestMatchIndexOf_String() + { + for (int length = 0; length < 32; length++) + { + string[] a = new string[length]; + for (int i = 0; i < length; i++) + { + a[i] = (10 * (i + 1)).ToString(); + } + Span span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + string target = a[targetIndex]; + int idx = span.IndexOf(target); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestNoMatchIndexOf_String() + { + var rnd = new Random(42); + for (int length = 0; length <= byte.MaxValue; length++) + { + string[] a = new string[length]; + string target = (rnd.Next(0, 256)).ToString(); + for (int i = 0; i < length; i++) + { + string val = (i + 1).ToString(); + a[i] = val == target ? (target + 1) : val; + } + Span span = new Span(a); + + int idx = span.IndexOf(target); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchIndexOf_String() + { + for (int length = 2; length < 32; length++) + { + string[] a = new string[length]; + for (int i = 0; i < length; i++) + { + a[i] = (10 * (i + 1)).ToString(); + } + + a[length - 1] = "5555"; + a[length - 2] = "5555"; + + Span span = new Span(a); + int idx = span.IndexOf("5555"); + Assert.Equal(length - 2, idx); + } + } } } diff --git a/external/corefx/src/System.Memory/tests/Span/IndexOf.byte.cs b/external/corefx/src/System.Memory/tests/Span/IndexOf.byte.cs index 265bda555d..7bdad8d391 100644 --- a/external/corefx/src/System.Memory/tests/Span/IndexOf.byte.cs +++ b/external/corefx/src/System.Memory/tests/Span/IndexOf.byte.cs @@ -14,7 +14,7 @@ namespace System.SpanTests public static void ZeroLengthIndexOf_Byte() { Span sp = new Span(Array.Empty()); - int idx = sp.IndexOf(0); + int idx = sp.IndexOf(0); Assert.Equal(-1, idx); } @@ -28,7 +28,7 @@ namespace System.SpanTests for (int i = 0; i < length; i++) { - byte target0 = default(byte); + byte target0 = default; int idx = span.IndexOf(target0); Assert.Equal(0, idx); } @@ -103,11 +103,11 @@ namespace System.SpanTests for (var i = 0; i < Vector.Count; i++) { var span = new Span(array, i, 3 * Vector.Count); - int idx = span.IndexOf(5); + int idx = span.IndexOf(5); Assert.Equal(0, idx); span = new Span(array, i, 3 * Vector.Count - 3); - idx = span.IndexOf(5); + idx = span.IndexOf(5); Assert.Equal(0, idx); } } @@ -128,7 +128,7 @@ namespace System.SpanTests a[length - 2] = 200; Span span = new Span(a); - int idx = span.IndexOf(200); + int idx = span.IndexOf(200); Assert.Equal(length - 2, idx); } } @@ -142,516 +142,7 @@ namespace System.SpanTests a[0] = 99; a[length + 1] = 99; Span span = new Span(a, 1, length); - int index = span.IndexOf(99); - Assert.Equal(-1, index); - } - } - - [Theory] - [InlineData("a", "a", 'a', 0)] - [InlineData("ab", "a", 'a', 0)] - [InlineData("aab", "a", 'a', 0)] - [InlineData("acab", "a", 'a', 0)] - [InlineData("acab", "c", 'c', 1)] - [InlineData("abcdefghijklmnopqrstuvwxyz", "lo", 'l', 11)] - [InlineData("abcdefghijklmnopqrstuvwxyz", "ol", 'l', 11)] - [InlineData("abcdefghijklmnopqrstuvwxyz", "ll", 'l', 11)] - [InlineData("abcdefghijklmnopqrstuvwxyz", "lmr", 'l', 11)] - [InlineData("abcdefghijklmnopqrstuvwxyz", "rml", 'l', 11)] - [InlineData("abcdefghijklmnopqrstuvwxyz", "mlr", 'l', 11)] - [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "lmr", 'l', 11)] - [InlineData("aaaaaaaaaaalmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "lmr", 'l', 11)] - [InlineData("aaaaaaaaaaacmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "lmr", 'm', 12)] - [InlineData("aaaaaaaaaaarmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "lmr", 'r', 11)] - [InlineData("/localhost:5000/PATH/%2FPATH2/ HTTP/1.1", " %?", '%', 21)] - [InlineData("/localhost:5000/PATH/%2FPATH2/?key=value HTTP/1.1", " %?", '%', 21)] - [InlineData("/localhost:5000/PATH/PATH2/?key=value HTTP/1.1", " %?", '?', 27)] - [InlineData("/localhost:5000/PATH/PATH2/ HTTP/1.1", " %?", ' ', 27)] - public static void IndexOfAnyStrings_Byte(string raw, string search, char expectResult, int expectIndex) - { - var buffers = Encoding.UTF8.GetBytes(raw); - var span = new Span(buffers); - var searchFor = search.ToCharArray(); - var searchForBytes = Encoding.UTF8.GetBytes(searchFor); - - var index = -1; - if (searchFor.Length == 1) - { - index = span.IndexOf((byte)searchFor[0]); - } - else if (searchFor.Length == 2) - { - index = span.IndexOfAny((byte)searchFor[0], (byte)searchFor[1]); - } - else if (searchFor.Length == 3) - { - index = span.IndexOfAny((byte)searchFor[0], (byte)searchFor[1], (byte)searchFor[2]); - } - else - { - index = span.IndexOfAny(new ReadOnlySpan(searchForBytes)); - } - - var found = span[index]; - Assert.Equal((byte)expectResult, found); - Assert.Equal(expectIndex, index); - } - - [Fact] - public static void ZeroLengthIndexOfTwo_Byte() - { - Span sp = new Span(Array.Empty()); - int idx = sp.IndexOfAny(0, 0); - Assert.Equal(-1, idx); - } - - [Fact] - public static void DefaultFilledIndexOfTwo_Byte() - { - Random rnd = new Random(42); - - for (int length = 0; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - Span span = new Span(a); - - byte[] targets = { default(byte), 99 }; - - for (int i = 0; i < length; i++) - { - int index = rnd.Next(0, 2) == 0 ? 0 : 1; - byte target0 = targets[index]; - byte target1 = targets[(index + 1) % 2]; - int idx = span.IndexOfAny(target0, target1); - Assert.Equal(0, idx); - } - } - } - - [Fact] - public static void TestMatchTwo_Byte() - { - for (int length = 0; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - for (int i = 0; i < length; i++) - { - a[i] = (byte)(i + 1); - } - Span span = new Span(a); - - for (int targetIndex = 0; targetIndex < length; targetIndex++) - { - byte target0 = a[targetIndex]; - byte target1 = 0; - int idx = span.IndexOfAny(target0, target1); - Assert.Equal(targetIndex, idx); - } - - for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) - { - byte target0 = a[targetIndex]; - byte target1 = a[targetIndex + 1]; - int idx = span.IndexOfAny(target0, target1); - Assert.Equal(targetIndex, idx); - } - - for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) - { - byte target0 = 0; - byte target1 = a[targetIndex + 1]; - int idx = span.IndexOfAny(target0, target1); - Assert.Equal(targetIndex + 1, idx); - } - } - } - - [Fact] - public static void TestNoMatchTwo_Byte() - { - var rnd = new Random(42); - for (int length = 0; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - byte target0 = (byte)rnd.Next(1, 256); - byte target1 = (byte)rnd.Next(1, 256); - Span span = new Span(a); - - int idx = span.IndexOfAny(target0, target1); - Assert.Equal(-1, idx); - } - } - - [Fact] - public static void TestMultipleMatchTwo_Byte() - { - for (int length = 3; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - for (int i = 0; i < length; i++) - { - byte val = (byte)(i + 1); - a[i] = val == 200 ? (byte)201 : val; - } - - a[length - 1] = 200; - a[length - 2] = 200; - a[length - 3] = 200; - - Span span = new Span(a); - int idx = span.IndexOfAny(200, 200); - Assert.Equal(length - 3, idx); - } - } - - [Fact] - public static void MakeSureNoChecksGoOutOfRangeTwo_Byte() - { - for (int length = 1; length < byte.MaxValue; length++) - { - byte[] a = new byte[length + 2]; - a[0] = 99; - a[length + 1] = 98; - Span span = new Span(a, 1, length - 1); - int index = span.IndexOfAny(99, 98); - Assert.Equal(-1, index); - } - - for (int length = 1; length < byte.MaxValue; length++) - { - byte[] a = new byte[length + 2]; - a[0] = 99; - a[length + 1] = 99; - Span span = new Span(a, 1, length - 1); - int index = span.IndexOfAny(99, 99); - Assert.Equal(-1, index); - } - } - - [Fact] - public static void ZeroLengthIndexOfThree_Byte() - { - Span sp = new Span(Array.Empty()); - int idx = sp.IndexOfAny(0, 0, 0); - Assert.Equal(-1, idx); - } - - [Fact] - public static void DefaultFilledIndexOfThree_Byte() - { - Random rnd = new Random(42); - - for (int length = 0; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - Span span = new Span(a); - - byte[] targets = { default(byte), 99, 98 }; - - for (int i = 0; i < length; i++) - { - int index = rnd.Next(0, 3); - byte target0 = targets[index]; - byte target1 = targets[(index + 1) % 2]; - byte target2 = targets[(index + 1) % 3]; - int idx = span.IndexOfAny(target0, target1, target2); - Assert.Equal(0, idx); - } - } - } - - [Fact] - public static void TestMatchThree_Byte() - { - for (int length = 0; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - for (int i = 0; i < length; i++) - { - a[i] = (byte)(i + 1); - } - Span span = new Span(a); - - for (int targetIndex = 0; targetIndex < length; targetIndex++) - { - byte target0 = a[targetIndex]; - byte target1 = 0; - byte target2 = 0; - int idx = span.IndexOfAny(target0, target1, target2); - Assert.Equal(targetIndex, idx); - } - - for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) - { - byte target0 = a[targetIndex]; - byte target1 = a[targetIndex + 1]; - byte target2 = a[targetIndex + 2]; - int idx = span.IndexOfAny(target0, target1, target2); - Assert.Equal(targetIndex, idx); - } - - for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) - { - byte target0 = 0; - byte target1 = 0; - byte target2 = a[targetIndex + 2]; - int idx = span.IndexOfAny(target0, target1, target2); - Assert.Equal(targetIndex + 2, idx); - } - } - } - - [Fact] - public static void TestNoMatchThree_Byte() - { - var rnd = new Random(42); - for (int length = 0; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - byte target0 = (byte)rnd.Next(1, 256); - byte target1 = (byte)rnd.Next(1, 256); - byte target2 = (byte)rnd.Next(1, 256); - Span span = new Span(a); - - int idx = span.IndexOfAny(target0, target1, target2); - Assert.Equal(-1, idx); - } - } - - [Fact] - public static void TestMultipleMatchThree_Byte() - { - for (int length = 4; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - for (int i = 0; i < length; i++) - { - byte val = (byte)(i + 1); - a[i] = val == 200 ? (byte)201 : val; - } - - a[length - 1] = 200; - a[length - 2] = 200; - a[length - 3] = 200; - a[length - 4] = 200; - - Span span = new Span(a); - int idx = span.IndexOfAny(200, 200, 200); - Assert.Equal(length - 4, idx); - } - } - - [Fact] - public static void MakeSureNoChecksGoOutOfRangeThree_Byte() - { - for (int length = 1; length < byte.MaxValue; length++) - { - byte[] a = new byte[length + 2]; - a[0] = 99; - a[length + 1] = 98; - Span span = new Span(a, 1, length - 1); - int index = span.IndexOfAny(99, 98, 99); - Assert.Equal(-1, index); - } - - for (int length = 1; length < byte.MaxValue; length++) - { - byte[] a = new byte[length + 2]; - a[0] = 99; - a[length + 1] = 99; - Span span = new Span(a, 1, length - 1); - int index = span.IndexOfAny(99, 99, 99); - Assert.Equal(-1, index); - } - } - - [Fact] - public static void ZeroLengthIndexOfMany_Byte() - { - Span sp = new Span(Array.Empty()); - var values = new ReadOnlySpan(new byte[] { 0, 0, 0, 0 }); - int idx = sp.IndexOfAny(values); - Assert.Equal(-1, idx); - - values = new ReadOnlySpan(new byte[] { }); - idx = sp.IndexOfAny(values); - Assert.Equal(0, idx); - } - - [Fact] - public static void DefaultFilledIndexOfMany_Byte() - { - for (int length = 0; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - Span span = new Span(a); - - var values = new ReadOnlySpan(new byte[] { default(byte), 99, 98, 0 }); - - for (int i = 0; i < length; i++) - { - int idx = span.IndexOfAny(values); - Assert.Equal(0, idx); - } - } - } - - [Fact] - public static void TestMatchMany_Byte() - { - for (int length = 0; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - for (int i = 0; i < length; i++) - { - a[i] = (byte)(i + 1); - } - Span span = new Span(a); - - for (int targetIndex = 0; targetIndex < length; targetIndex++) - { - var values = new ReadOnlySpan(new byte[] { a[targetIndex], 0, 0, 0 }); - int idx = span.IndexOfAny(values); - Assert.Equal(targetIndex, idx); - } - - for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) - { - var values = new ReadOnlySpan(new byte[] { a[targetIndex], a[targetIndex + 1], a[targetIndex + 2], a[targetIndex + 3] }); - int idx = span.IndexOfAny(values); - Assert.Equal(targetIndex, idx); - } - - for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) - { - var values = new ReadOnlySpan(new byte[] { 0, 0, 0, a[targetIndex + 3] }); - int idx = span.IndexOfAny(values); - Assert.Equal(targetIndex + 3, idx); - } - } - } - - [Fact] - public static void TestMatchValuesLargerMany_Byte() - { - var rnd = new Random(42); - for (int length = 2; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - int expectedIndex = length / 2; - for (int i = 0; i < length; i++) - { - if (i == expectedIndex) - { - continue; - } - a[i] = 255; - } - Span span = new Span(a); - - byte[] targets = new byte[length * 2]; - for (int i = 0; i < targets.Length; i++) - { - if (i == length + 1) - { - continue; - } - targets[i] = (byte)rnd.Next(1, 255); - } - - var values = new ReadOnlySpan(targets); - int idx = span.IndexOfAny(values); - Assert.Equal(expectedIndex, idx); - } - } - - [Fact] - public static void TestNoMatchMany_Byte() - { - var rnd = new Random(42); - for (int length = 1; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - byte[] targets = new byte[length]; - for (int i = 0; i < targets.Length; i++) - { - targets[i] = (byte)rnd.Next(1, 256); - } - Span span = new Span(a); - var values = new ReadOnlySpan(targets); - - int idx = span.IndexOfAny(values); - Assert.Equal(-1, idx); - } - } - - [Fact] - public static void TestNoMatchValuesLargerMany_Byte() - { - var rnd = new Random(42); - for (int length = 1; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - byte[] targets = new byte[length * 2]; - for (int i = 0; i < targets.Length; i++) - { - targets[i] = (byte)rnd.Next(1, 256); - } - Span span = new Span(a); - var values = new ReadOnlySpan(targets); - - int idx = span.IndexOfAny(values); - Assert.Equal(-1, idx); - } - } - - [Fact] - public static void TestMultipleMatchMany_Byte() - { - for (int length = 5; length < byte.MaxValue; length++) - { - byte[] a = new byte[length]; - for (int i = 0; i < length; i++) - { - byte val = (byte)(i + 1); - a[i] = val == 200 ? (byte)201 : val; - } - - a[length - 1] = 200; - a[length - 2] = 200; - a[length - 3] = 200; - a[length - 4] = 200; - a[length - 5] = 200; - - Span span = new Span(a); - var values = new ReadOnlySpan(new byte[] { 200, 200, 200, 200, 200, 200, 200, 200, 200 }); - int idx = span.IndexOfAny(values); - Assert.Equal(length - 5, idx); - } - } - - [Fact] - public static void MakeSureNoChecksGoOutOfRangeMany_Byte() - { - for (int length = 1; length < byte.MaxValue; length++) - { - byte[] a = new byte[length + 2]; - a[0] = 99; - a[length + 1] = 98; - Span span = new Span(a, 1, length - 1); - var values = new ReadOnlySpan(new byte[] { 99, 98, 99, 98, 99, 98 }); - int index = span.IndexOfAny(values); - Assert.Equal(-1, index); - } - - for (int length = 1; length < byte.MaxValue; length++) - { - byte[] a = new byte[length + 2]; - a[0] = 99; - a[length + 1] = 99; - Span span = new Span(a, 1, length - 1); - var values = new ReadOnlySpan(new byte[] { 99, 99, 99, 99, 99, 99 }); - int index = span.IndexOfAny(values); + int index = span.IndexOf(99); Assert.Equal(-1, index); } } diff --git a/external/corefx/src/System.Memory/tests/Span/IndexOf.char.cs b/external/corefx/src/System.Memory/tests/Span/IndexOf.char.cs index 51c91ffa0e..b77c3f7d77 100644 --- a/external/corefx/src/System.Memory/tests/Span/IndexOf.char.cs +++ b/external/corefx/src/System.Memory/tests/Span/IndexOf.char.cs @@ -27,7 +27,7 @@ namespace System.SpanTests a[i] = (char)(i + 1); } Span span = new Span(a); - + for (int targetIndex = 0; targetIndex < length; targetIndex++) { char target = a[targetIndex]; diff --git a/external/corefx/src/System.Memory/tests/Span/IndexOfAny.T.cs b/external/corefx/src/System.Memory/tests/Span/IndexOfAny.T.cs new file mode 100644 index 0000000000..c257c6a3b6 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Span/IndexOfAny.T.cs @@ -0,0 +1,967 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class SpanTests + { + [Fact] + public static void ZeroLengthIndexOfAny_TwoInteger() + { + var sp = new Span(Array.Empty()); + int idx = sp.IndexOfAny(0, 0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledIndexOfAny_TwoInteger() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + var span = new Span(a); + + int[] targets = { default, 99 }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 2) == 0 ? 0 : 1; + int target0 = targets[index]; + int target1 = targets[(index + 1) % 2]; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(0, idx); + } + } + } + + [Fact] + public static void TestMatchIndexOfAny_TwoInteger() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + a[i] = i + 1; + } + var span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + int target0 = a[targetIndex]; + int target1 = 0; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + int index = rnd.Next(0, 2) == 0 ? 0 : 1; + int target0 = a[targetIndex + index]; + int target1 = a[targetIndex + (index + 1) % 2]; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + int target0 = 0; + int target1 = a[targetIndex]; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestNoMatchIndexOfAny_TwoInteger() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + int target0 = rnd.Next(1, 256); + int target1 = rnd.Next(1, 256); + var span = new Span(a); + + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchIndexOfAny_TwoInteger() + { + for (int length = 3; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + int val = i + 1; + a[i] = val == 200 ? 201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + + var span = new Span(a); + int idx = span.IndexOfAny(200, 200); + Assert.Equal(length - 3, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeIndexOfAny_TwoInteger() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 98; + var span = new Span(a, 1, length - 1); + int index = span.IndexOfAny(99, 98); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 99; + var span = new Span(a, 1, length - 1); + int index = span.IndexOfAny(99, 99); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthIndexOfAny_ThreeInteger() + { + var sp = new Span(Array.Empty()); + int idx = sp.IndexOfAny(0, 0, 0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledIndexOfAny_ThreeInteger() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + var span = new Span(a); + + int[] targets = { default, 99, 98 }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 3); + int target0 = targets[index]; + int target1 = targets[(index + 1) % 2]; + int target2 = targets[(index + 1) % 3]; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(0, idx); + } + } + } + + [Fact] + public static void TestMatchIndexOfAny_ThreeInteger() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + a[i] = i + 1; + } + var span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + int target0 = a[targetIndex]; + int target1 = 0; + int target2 = 0; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + int index = rnd.Next(0, 3); + int target0 = a[targetIndex + index]; + int target1 = a[targetIndex + (index + 1) % 2]; + int target2 = a[targetIndex + (index + 1) % 3]; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + int target0 = 0; + int target1 = 0; + int target2 = a[targetIndex]; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestNoMatchIndexOfAny_ThreeInteger() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + int target0 = rnd.Next(1, 256); + int target1 = rnd.Next(1, 256); + int target2 = rnd.Next(1, 256); + var span = new Span(a); + + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchIndexOfAny_ThreeInteger() + { + for (int length = 4; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + int val = i + 1; + a[i] = val == 200 ? 201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + a[length - 4] = 200; + + var span = new Span(a); + int idx = span.IndexOfAny(200, 200, 200); + Assert.Equal(length - 4, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeIndexOfAny_ThreeInteger() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 98; + var span = new Span(a, 1, length - 1); + int index = span.IndexOfAny(99, 98, 99); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 99; + var span = new Span(a, 1, length - 1); + int index = span.IndexOfAny(99, 99, 99); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthIndexOfAny_ManyInteger() + { + var sp = new Span(Array.Empty()); + var values = new ReadOnlySpan(new int[] { 0, 0, 0, 0 }); + int idx = sp.IndexOfAny(values); + Assert.Equal(-1, idx); + + values = new ReadOnlySpan(new int[] { }); + idx = sp.IndexOfAny(values); + Assert.Equal(0, idx); + } + + [Fact] + public static void DefaultFilledIndexOfAny_ManyInteger() + { + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + var span = new Span(a); + + var values = new ReadOnlySpan(new int[] { default, 99, 98, 0 }); + + for (int i = 0; i < length; i++) + { + int idx = span.IndexOfAny(values); + Assert.Equal(0, idx); + } + } + } + + [Fact] + public static void TestMatchIndexOfAny_ManyInteger() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + a[i] = i + 1; + } + var span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + var values = new ReadOnlySpan(new int[] { a[targetIndex], 0, 0, 0 }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + int index = rnd.Next(0, 4) == 0 ? 0 : 1; + var values = new ReadOnlySpan(new int[] + { + a[targetIndex + index], + a[targetIndex + (index + 1) % 2], + a[targetIndex + (index + 1) % 3], + a[targetIndex + (index + 1) % 4] + }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + var values = new ReadOnlySpan(new int[] { 0, 0, 0, a[targetIndex] }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestMatchValuesLargerIndexOfAny_ManyInteger() + { + var rnd = new Random(42); + for (int length = 2; length < byte.MaxValue; length++) + { + var a = new int[length]; + int expectedIndex = length / 2; + for (int i = 0; i < length; i++) + { + if (i == expectedIndex) + { + continue; + } + a[i] = 255; + } + var span = new Span(a); + + var targets = new int[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + if (i == length + 1) + { + continue; + } + targets[i] = rnd.Next(1, 255); + } + + var values = new ReadOnlySpan(targets); + int idx = span.IndexOfAny(values); + Assert.Equal(expectedIndex, idx); + } + } + + [Fact] + public static void TestNoMatchIndexOfAny_ManyInteger() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length]; + var targets = new int[length]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = rnd.Next(1, 256); + } + var span = new Span(a); + var values = new ReadOnlySpan(targets); + + int idx = span.IndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestNoMatchValuesLargerIndexOfAny_ManyInteger() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length]; + var targets = new int[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = rnd.Next(1, 256); + } + var span = new Span(a); + var values = new ReadOnlySpan(targets); + + int idx = span.IndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchIndexOfAny_ManyInteger() + { + for (int length = 5; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + int val = i + 1; + a[i] = val == 200 ? 201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + a[length - 4] = 200; + a[length - 5] = 200; + + var span = new Span(a); + var values = new ReadOnlySpan(new int[] { 200, 200, 200, 200, 200, 200, 200, 200, 200 }); + int idx = span.IndexOfAny(values); + Assert.Equal(length - 5, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeIndexOfAny_ManyInteger() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 98; + var span = new Span(a, 1, length - 1); + var values = new Span(new int[] { 99, 98, 99, 98, 99, 98 }); + int index = span.IndexOfAny(values); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 99; + var span = new Span(a, 1, length - 1); + var values = new ReadOnlySpan(new int[] { 99, 99, 99, 99, 99, 99 }); + int index = span.IndexOfAny(values); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthIndexOfAny_TwoString() + { + var sp = new Span(Array.Empty()); + int idx = sp.IndexOfAny("0", "0"); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledIndexOfAny_TwoString() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + var tempSpan = new Span(a); + tempSpan.Fill(""); + Span span = tempSpan; + + string[] targets = { "", "99" }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 2) == 0 ? 0 : 1; + string target0 = targets[index]; + string target1 = targets[(index + 1) % 2]; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(0, idx); + } + } + } + + [Fact] + public static void TestMatchIndexOfAny_TwoString() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + a[i] = (i + 1).ToString(); + } + var span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + string target0 = a[targetIndex]; + string target1 = "0"; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + int index = rnd.Next(0, 2) == 0 ? 0 : 1; + string target0 = a[targetIndex + index]; + string target1 = a[targetIndex + (index + 1) % 2]; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + string target0 = "0"; + string target1 = a[targetIndex + 1]; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(targetIndex + 1, idx); + } + } + } + + [Fact] + public static void TestNoMatchIndexOfAny_TwoString() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + string target0 = rnd.Next(1, 256).ToString(); + string target1 = rnd.Next(1, 256).ToString(); + var span = new Span(a); + + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchIndexOfAny_TwoString() + { + for (int length = 3; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + string val = (i + 1).ToString(); + a[i] = val == "200" ? "201" : val; + } + + a[length - 1] = "200"; + a[length - 2] = "200"; + a[length - 3] = "200"; + + var span = new Span(a); + int idx = span.IndexOfAny("200", "200"); + Assert.Equal(length - 3, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeIndexOfAny_TwoString() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "98"; + var span = new Span(a, 1, length - 1); + int index = span.IndexOfAny("99", "98"); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "99"; + var span = new Span(a, 1, length - 1); + int index = span.IndexOfAny("99", "99"); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthIndexOf_ThreeString() + { + var sp = new Span(Array.Empty()); + int idx = sp.IndexOfAny("0", "0", "0"); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledIndexOfAny_ThreeString() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + var tempSpan = new Span(a); + tempSpan.Fill(""); + Span span = tempSpan; + + string[] targets = { "", "99", "98" }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 3); + string target0 = targets[index]; + string target1 = targets[(index + 1) % 2]; + string target2 = targets[(index + 1) % 3]; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(0, idx); + } + } + } + + [Fact] + public static void TestMatchIndexOfAny_ThreeString() + { + Random rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + a[i] = (i + 1).ToString(); + } + var span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + string target0 = a[targetIndex]; + string target1 = "0"; + string target2 = "0"; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + int index = rnd.Next(0, 3) == 0 ? 0 : 1; + string target0 = a[targetIndex + index]; + string target1 = a[targetIndex + (index + 1) % 2]; + string target2 = a[targetIndex + (index + 1) % 3]; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + string target0 = "0"; + string target1 = "0"; + string target2 = a[targetIndex]; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestNoMatchIndexOfAny_ThreeString() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + string target0 = rnd.Next(1, 256).ToString(); + string target1 = rnd.Next(1, 256).ToString(); + string target2 = rnd.Next(1, 256).ToString(); + var span = new Span(a); + + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchIndexOfAny_ThreeString() + { + for (int length = 4; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + string val = (i + 1).ToString(); + a[i] = val == "200" ? "201" : val; + } + + a[length - 1] = "200"; + a[length - 2] = "200"; + a[length - 3] = "200"; + a[length - 4] = "200"; + + var span = new Span(a); + int idx = span.IndexOfAny("200", "200", "200"); + Assert.Equal(length - 4, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeIndexOfAny_ThreeString() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "98"; + var span = new Span(a, 1, length - 1); + int index = span.IndexOfAny("99", "98", "99"); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "99"; + var span = new Span(a, 1, length - 1); + int index = span.IndexOfAny("99", "99", "99"); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthIndexOfAny_ManyString() + { + var sp = new Span(Array.Empty()); + var values = new ReadOnlySpan(new string[] { "0", "0", "0", "0" }); + int idx = sp.IndexOfAny(values); + Assert.Equal(-1, idx); + + values = new ReadOnlySpan(new string[] { }); + idx = sp.IndexOfAny(values); + Assert.Equal(0, idx); + } + + [Fact] + public static void DefaultFilledIndexOfAny_ManyString() + { + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + var tempSpan = new Span(a); + tempSpan.Fill(""); + Span span = tempSpan; + + var values = new ReadOnlySpan(new string[] { "", "99", "98", "0" }); + + for (int i = 0; i < length; i++) + { + int idx = span.IndexOfAny(values); + Assert.Equal(0, idx); + } + } + } + + [Fact] + public static void TestMatchIndexOfAny_ManyString() + { + Random rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + a[i] = (i + 1).ToString(); + } + var span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + var values = new ReadOnlySpan(new string[] { a[targetIndex], "0", "0", "0" }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + int index = rnd.Next(0, 4) == 0 ? 0 : 1; + var values = new ReadOnlySpan(new string[] + { + a[targetIndex + index], + a[targetIndex + (index + 1) % 2], + a[targetIndex + (index + 1) % 3], + a[targetIndex + (index + 1) % 4] + }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + var values = new ReadOnlySpan(new string[] { "0", "0", "0", a[targetIndex] }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestMatchValuesLargerIndexOfAny_ManyString() + { + var rnd = new Random(42); + for (int length = 2; length < byte.MaxValue; length++) + { + var a = new string[length]; + int expectedIndex = length / 2; + for (int i = 0; i < length; i++) + { + if (i == expectedIndex) + { + a[i] = "val"; + continue; + } + a[i] = "255"; + } + var span = new Span(a); + + var targets = new string[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + if (i == length + 1) + { + targets[i] = "val"; + continue; + } + targets[i] = rnd.Next(1, 255).ToString(); + } + + var values = new ReadOnlySpan(targets); + int idx = span.IndexOfAny(values); + Assert.Equal(expectedIndex, idx); + } + } + + [Fact] + public static void TestNoMatchIndexOfAny_ManyString() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length]; + var targets = new string[length]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = rnd.Next(1, 256).ToString(); + } + var span = new Span(a); + var values = new ReadOnlySpan(targets); + + int idx = span.IndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestNoMatchValuesLargerIndexOfAny_ManyString() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length]; + var targets = new string[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = rnd.Next(1, 256).ToString(); + } + var span = new Span(a); + var values = new ReadOnlySpan(targets); + + int idx = span.IndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchIndexOfAny_ManyString() + { + for (int length = 5; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + string val = (i + 1).ToString(); + a[i] = val == "200" ? "201" : val; + } + + a[length - 1] = "200"; + a[length - 2] = "200"; + a[length - 3] = "200"; + a[length - 4] = "200"; + a[length - 5] = "200"; + + var span = new Span(a); + var values = new ReadOnlySpan(new string[] { "200", "200", "200", "200", "200", "200", "200", "200", "200" }); + int idx = span.IndexOfAny(values); + Assert.Equal(length - 5, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeIndexOfAny_ManyString() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "98"; + var span = new Span(a, 1, length - 1); + var values = new ReadOnlySpan(new string[] { "99", "98", "99", "98", "99", "98" }); + int index = span.IndexOfAny(values); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "99"; + var span = new Span(a, 1, length - 1); + var values = new ReadOnlySpan(new string[] { "99", "99", "99", "99", "99", "99" }); + int index = span.IndexOfAny(values); + Assert.Equal(-1, index); + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Span/IndexOfAny.byte.cs b/external/corefx/src/System.Memory/tests/Span/IndexOfAny.byte.cs new file mode 100644 index 0000000000..61b1da24d5 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Span/IndexOfAny.byte.cs @@ -0,0 +1,522 @@ +// 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.Numerics; +using System.Text; +using Xunit; + +namespace System.SpanTests +{ + public static partial class SpanTests + { + [Theory] + [InlineData("a", "a", 'a', 0)] + [InlineData("ab", "a", 'a', 0)] + [InlineData("aab", "a", 'a', 0)] + [InlineData("acab", "a", 'a', 0)] + [InlineData("acab", "c", 'c', 1)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "lo", 'l', 11)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "ol", 'l', 11)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "ll", 'l', 11)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "lmr", 'l', 11)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "rml", 'l', 11)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "mlr", 'l', 11)] + [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "lmr", 'l', 11)] + [InlineData("aaaaaaaaaaalmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "lmr", 'l', 11)] + [InlineData("aaaaaaaaaaacmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "lmr", 'm', 12)] + [InlineData("aaaaaaaaaaarmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "lmr", 'r', 11)] + [InlineData("/localhost:5000/PATH/%2FPATH2/ HTTP/1.1", " %?", '%', 21)] + [InlineData("/localhost:5000/PATH/%2FPATH2/?key=value HTTP/1.1", " %?", '%', 21)] + [InlineData("/localhost:5000/PATH/PATH2/?key=value HTTP/1.1", " %?", '?', 27)] + [InlineData("/localhost:5000/PATH/PATH2/ HTTP/1.1", " %?", ' ', 27)] + public static void IndexOfAnyStrings_Byte(string raw, string search, char expectResult, int expectIndex) + { + byte[] buffers = Encoding.UTF8.GetBytes(raw); + var span = new Span(buffers); + char[] searchFor = search.ToCharArray(); + byte[] searchForBytes = Encoding.UTF8.GetBytes(searchFor); + + var index = -1; + if (searchFor.Length == 1) + { + index = span.IndexOf((byte)searchFor[0]); + } + else if (searchFor.Length == 2) + { + index = span.IndexOfAny((byte)searchFor[0], (byte)searchFor[1]); + } + else if (searchFor.Length == 3) + { + index = span.IndexOfAny((byte)searchFor[0], (byte)searchFor[1], (byte)searchFor[2]); + } + else + { + index = span.IndexOfAny(new ReadOnlySpan(searchForBytes)); + } + + var found = span[index]; + Assert.Equal((byte)expectResult, found); + Assert.Equal(expectIndex, index); + } + + [Fact] + public static void ZeroLengthIndexOfTwo_Byte() + { + Span sp = new Span(Array.Empty()); + int idx = sp.IndexOfAny(0, 0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledIndexOfTwo_Byte() + { + Random rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + Span span = new Span(a); + + byte[] targets = { default, 99 }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 2) == 0 ? 0 : 1; + byte target0 = targets[index]; + byte target1 = targets[(index + 1) % 2]; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(0, idx); + } + } + } + + [Fact] + public static void TestMatchTwo_Byte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + Span span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = 0; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = a[targetIndex + 1]; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + byte target0 = 0; + byte target1 = a[targetIndex + 1]; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(targetIndex + 1, idx); + } + } + } + + [Fact] + public static void TestNoMatchTwo_Byte() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + byte target0 = (byte)rnd.Next(1, 256); + byte target1 = (byte)rnd.Next(1, 256); + Span span = new Span(a); + + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchTwo_Byte() + { + for (int length = 3; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + byte val = (byte)(i + 1); + a[i] = val == 200 ? (byte)201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + + Span span = new Span(a); + int idx = span.IndexOfAny(200, 200); + Assert.Equal(length - 3, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeTwo_Byte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 98; + Span span = new Span(a, 1, length - 1); + int index = span.IndexOfAny(99, 98); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 99; + Span span = new Span(a, 1, length - 1); + int index = span.IndexOfAny(99, 99); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthIndexOfThree_Byte() + { + Span sp = new Span(Array.Empty()); + int idx = sp.IndexOfAny(0, 0, 0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledIndexOfThree_Byte() + { + Random rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + Span span = new Span(a); + + byte[] targets = { default, 99, 98 }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 3); + byte target0 = targets[index]; + byte target1 = targets[(index + 1) % 2]; + byte target2 = targets[(index + 1) % 3]; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(0, idx); + } + } + } + + [Fact] + public static void TestMatchThree_Byte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + Span span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = 0; + byte target2 = 0; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = a[targetIndex + 1]; + byte target2 = a[targetIndex + 2]; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + byte target0 = 0; + byte target1 = 0; + byte target2 = a[targetIndex + 2]; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex + 2, idx); + } + } + } + + [Fact] + public static void TestNoMatchThree_Byte() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + byte target0 = (byte)rnd.Next(1, 256); + byte target1 = (byte)rnd.Next(1, 256); + byte target2 = (byte)rnd.Next(1, 256); + Span span = new Span(a); + + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchThree_Byte() + { + for (int length = 4; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + byte val = (byte)(i + 1); + a[i] = val == 200 ? (byte)201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + a[length - 4] = 200; + + Span span = new Span(a); + int idx = span.IndexOfAny(200, 200, 200); + Assert.Equal(length - 4, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeThree_Byte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 98; + Span span = new Span(a, 1, length - 1); + int index = span.IndexOfAny(99, 98, 99); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 99; + Span span = new Span(a, 1, length - 1); + int index = span.IndexOfAny(99, 99, 99); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthIndexOfMany_Byte() + { + Span sp = new Span(Array.Empty()); + var values = new ReadOnlySpan(new byte[] { 0, 0, 0, 0 }); + int idx = sp.IndexOfAny(values); + Assert.Equal(-1, idx); + + values = new ReadOnlySpan(new byte[] { }); + idx = sp.IndexOfAny(values); + Assert.Equal(0, idx); + } + + [Fact] + public static void DefaultFilledIndexOfMany_Byte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + Span span = new Span(a); + + var values = new ReadOnlySpan(new byte[] { default, 99, 98, 0 }); + + for (int i = 0; i < length; i++) + { + int idx = span.IndexOfAny(values); + Assert.Equal(0, idx); + } + } + } + + [Fact] + public static void TestMatchMany_Byte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + Span span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + var values = new ReadOnlySpan(new byte[] { a[targetIndex], 0, 0, 0 }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + var values = new ReadOnlySpan(new byte[] { a[targetIndex], a[targetIndex + 1], a[targetIndex + 2], a[targetIndex + 3] }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + var values = new ReadOnlySpan(new byte[] { 0, 0, 0, a[targetIndex + 3] }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex + 3, idx); + } + } + } + + [Fact] + public static void TestMatchValuesLargerMany_Byte() + { + var rnd = new Random(42); + for (int length = 2; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + int expectedIndex = length / 2; + for (int i = 0; i < length; i++) + { + if (i == expectedIndex) + { + continue; + } + a[i] = 255; + } + Span span = new Span(a); + + byte[] targets = new byte[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + if (i == length + 1) + { + continue; + } + targets[i] = (byte)rnd.Next(1, 255); + } + + var values = new ReadOnlySpan(targets); + int idx = span.IndexOfAny(values); + Assert.Equal(expectedIndex, idx); + } + } + + [Fact] + public static void TestNoMatchMany_Byte() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + byte[] targets = new byte[length]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = (byte)rnd.Next(1, 256); + } + Span span = new Span(a); + var values = new ReadOnlySpan(targets); + + int idx = span.IndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestNoMatchValuesLargerMany_Byte() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + byte[] targets = new byte[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = (byte)rnd.Next(1, 256); + } + Span span = new Span(a); + var values = new ReadOnlySpan(targets); + + int idx = span.IndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchMany_Byte() + { + for (int length = 5; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + byte val = (byte)(i + 1); + a[i] = val == 200 ? (byte)201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + a[length - 4] = 200; + a[length - 5] = 200; + + Span span = new Span(a); + var values = new ReadOnlySpan(new byte[] { 200, 200, 200, 200, 200, 200, 200, 200, 200 }); + int idx = span.IndexOfAny(values); + Assert.Equal(length - 5, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeMany_Byte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 98; + Span span = new Span(a, 1, length - 1); + var values = new ReadOnlySpan(new byte[] { 99, 98, 99, 98, 99, 98 }); + int index = span.IndexOfAny(values); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 99; + Span span = new Span(a, 1, length - 1); + var values = new ReadOnlySpan(new byte[] { 99, 99, 99, 99, 99, 99 }); + int index = span.IndexOfAny(values); + Assert.Equal(-1, index); + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Span/IndexOfSequence.T.cs b/external/corefx/src/System.Memory/tests/Span/IndexOfSequence.T.cs index 9c2ffc5b91..e8c270c0a2 100644 --- a/external/corefx/src/System.Memory/tests/Span/IndexOfSequence.T.cs +++ b/external/corefx/src/System.Memory/tests/Span/IndexOfSequence.T.cs @@ -119,5 +119,117 @@ namespace System.SpanTests int index = span.IndexOf(value); Assert.Equal(-1, index); } + + [Fact] + public static void IndexOfSequenceMatchAtStart_String() + { + Span span = new Span(new string[] { "5", "1", "77", "2", "3", "77", "77", "4", "5", "77", "77", "77", "88", "6", "6", "77", "77", "88", "9" }); + Span value = new Span(new string[] { "5", "1", "77" }); + int index = span.IndexOf(value); + Assert.Equal(0, index); + } + + [Fact] + public static void IndexOfSequenceMultipleMatch_String() + { + Span span = new Span(new string[] { "1", "2", "3", "1", "2", "3", "1", "2", "3" }); + Span value = new Span(new string[] { "2", "3" }); + int index = span.IndexOf(value); + Assert.Equal(1, index); + } + + [Fact] + public static void IndexOfSequenceRestart_String() + { + Span span = new Span(new string[] { "0", "1", "77", "2", "3", "77", "77", "4", "5", "77", "77", "77", "88", "6", "6", "77", "77", "88", "9" }); + Span value = new Span(new string[] { "77", "77", "88" }); + int index = span.IndexOf(value); + Assert.Equal(10, index); + } + + [Fact] + public static void IndexOfSequenceNoMatch_String() + { + Span span = new Span(new string[] { "0", "1", "77", "2", "3", "77", "77", "4", "5", "77", "77", "77", "88", "6", "6", "77", "77", "88", "9" }); + Span value = new Span(new string[] { "77", "77", "88", "99" }); + int index = span.IndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void IndexOfSequenceNotEvenAHeadMatch_String() + { + Span span = new Span(new string[] { "0", "1", "77", "2", "3", "77", "77", "4", "5", "77", "77", "77", "88", "6", "6", "77", "77", "88", "9" }); + Span value = new Span(new string[] { "100", "77", "88", "99" }); + int index = span.IndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void IndexOfSequenceMatchAtVeryEnd_String() + { + Span span = new Span(new string[] { "0", "1", "2", "3", "4", "5" }); + Span value = new Span(new string[] { "3", "4", "5" }); + int index = span.IndexOf(value); + Assert.Equal(3, index); + } + + [Fact] + public static void IndexOfSequenceJustPastVeryEnd_String() + { + Span span = new Span(new string[] { "0", "1", "2", "3", "4", "5" }, 0, 5); + Span value = new Span(new string[] { "3", "4", "5" }); + int index = span.IndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void IndexOfSequenceZeroLengthValue_String() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new string[] { "0", "1", "77", "2", "3", "77", "77", "4", "5", "77", "77", "77", "88", "6", "6", "77", "77", "88", "9" }); + Span value = new Span(Array.Empty()); + int index = span.IndexOf(value); + Assert.Equal(0, index); + } + + [Fact] + public static void IndexOfSequenceZeroLengthSpan_String() + { + Span span = new Span(Array.Empty()); + Span value = new Span(new string[] { "1", "2", "3" }); + int index = span.IndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void IndexOfSequenceLengthOneValue_String() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new string[] { "0", "1", "2", "3", "4", "5" }); + Span value = new Span(new string[] { "2" }); + int index = span.IndexOf(value); + Assert.Equal(2, index); + } + + [Fact] + public static void IndexOfSequenceLengthOneValueAtVeryEnd_String() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new string[] { "0", "1", "2", "3", "4", "5" }); + Span value = new Span(new string[] { "5" }); + int index = span.IndexOf(value); + Assert.Equal(5, index); + } + + [Fact] + public static void IndexOfSequenceLengthOneValueJustPasttVeryEnd_String() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new string[] { "0", "1", "2", "3", "4", "5" }, 0, 5); + Span value = new Span(new string[] { "5" }); + int index = span.IndexOf(value); + Assert.Equal(-1, index); + } } } diff --git a/external/corefx/src/System.Memory/tests/Span/LastIndexOf.T.cs b/external/corefx/src/System.Memory/tests/Span/LastIndexOf.T.cs new file mode 100644 index 0000000000..e15858eb3f --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Span/LastIndexOf.T.cs @@ -0,0 +1,189 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class SpanTests + { + [Fact] + public static void ZeroLengthLastIndexOf() + { + Span sp = new Span(Array.Empty()); + int idx = sp.LastIndexOf(0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void TestMatchLastIndexOf() + { + for (int length = 0; length < 32; length++) + { + int[] a = new int[length]; + for (int i = 0; i < length; i++) + { + a[i] = 10 * (i + 1); + } + Span span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + int target = a[targetIndex]; + int idx = span.LastIndexOf(target); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOf() + { + for (int length = 2; length < 32; length++) + { + int[] a = new int[length]; + for (int i = 0; i < length; i++) + { + a[i] = 10 * (i + 1); + } + + a[length - 1] = 5555; + a[length - 2] = 5555; + + Span span = new Span(a); + int idx = span.LastIndexOf(5555); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void OnNoMatchMakeSureEveryElementIsComparedLastIndexOf() + { + for (int length = 0; length < 100; length++) + { + TIntLog log = new TIntLog(); + + TInt[] a = new TInt[length]; + for (int i = 0; i < length; i++) + { + a[i] = new TInt(10 * (i + 1), log); + } + Span span = new Span(a); + int idx = span.LastIndexOf(new TInt(9999, log)); + Assert.Equal(-1, idx); + + // Since we asked for a non-existent value, make sure each element of the array was compared once. + // (Strictly speaking, it would not be illegal for IndexOf to compare an element more than once but + // that would be a non-optimal implementation and a red flag. So we'll stick with the stricter test.) + Assert.Equal(a.Length, log.Count); + foreach (TInt elem in a) + { + int numCompares = log.CountCompares(elem.Value, 9999); + Assert.True(numCompares == 1, $"Expected {numCompares} == 1 for element {elem.Value}."); + } + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOf() + { + const int GuardValue = 77777; + const int GuardLength = 50; + + Action checkForOutOfRangeAccess = + delegate (int x, int y) + { + if (x == GuardValue || y == GuardValue) + throw new Exception("Detected out of range access in IndexOf()"); + }; + + for (int length = 0; length < 100; length++) + { + TInt[] a = new TInt[GuardLength + length + GuardLength]; + for (int i = 0; i < a.Length; i++) + { + a[i] = new TInt(GuardValue, checkForOutOfRangeAccess); + } + + for (int i = 0; i < length; i++) + { + a[GuardLength + i] = new TInt(10 * (i + 1), checkForOutOfRangeAccess); + } + + Span span = new Span(a, GuardLength, length); + int idx = span.LastIndexOf(new TInt(9999, checkForOutOfRangeAccess)); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void ZeroLengthLastIndexOf_String() + { + Span sp = new Span(Array.Empty()); + int idx = sp.LastIndexOf("a"); + Assert.Equal(-1, idx); + } + + [Fact] + public static void TestMatchLastIndexOf_String() + { + for (int length = 0; length < 32; length++) + { + string[] a = new string[length]; + for (int i = 0; i < length; i++) + { + a[i] = (10 * (i + 1)).ToString(); + } + Span span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + string target = a[targetIndex]; + int idx = span.LastIndexOf(target); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestNoMatchLastIndexOf_String() + { + var rnd = new Random(42); + for (int length = 0; length <= byte.MaxValue; length++) + { + string[] a = new string[length]; + string target = (rnd.Next(0, 256)).ToString(); + for (int i = 0; i < length; i++) + { + string val = (i + 1).ToString(); + a[i] = val == target ? (target + 1) : val; + } + Span span = new Span(a); + + int idx = span.LastIndexOf(target); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOf_String() + { + for (int length = 2; length < 32; length++) + { + string[] a = new string[length]; + for (int i = 0; i < length; i++) + { + a[i] = (10 * (i + 1)).ToString(); + } + + a[length - 1] = "5555"; + a[length - 2] = "5555"; + + Span span = new Span(a); + int idx = span.LastIndexOf("5555"); + Assert.Equal(length - 1, idx); + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Span/LastIndexOf.byte.cs b/external/corefx/src/System.Memory/tests/Span/LastIndexOf.byte.cs new file mode 100644 index 0000000000..93582c6ed0 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Span/LastIndexOf.byte.cs @@ -0,0 +1,149 @@ +// 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.Numerics; +using Xunit; + +namespace System.SpanTests +{ + public static partial class SpanTests + { + [Fact] + public static void ZeroLengthLastIndexOf_Byte() + { + Span sp = new Span(Array.Empty()); + int idx = sp.LastIndexOf(0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledLastIndexOf_Byte() + { + for (int length = 0; length <= byte.MaxValue; length++) + { + byte[] a = new byte[length]; + Span span = new Span(a); + + for (int i = 0; i < length; i++) + { + byte target0 = default; + int idx = span.LastIndexOf(target0); + Assert.Equal(length - 1, idx); + } + } + } + + [Fact] + public static void TestMatchLastIndexOf_Byte() + { + for (int length = 0; length <= byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + Span span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + byte target = a[targetIndex]; + int idx = span.LastIndexOf(target); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestNoMatchLastIndexOf_Byte() + { + var rnd = new Random(42); + for (int length = 0; length <= byte.MaxValue; length++) + { + byte[] a = new byte[length]; + byte target = (byte)rnd.Next(0, 256); + for (int i = 0; i < length; i++) + { + byte val = (byte)(i + 1); + a[i] = val == target ? (byte)(target + 1) : val; + } + Span span = new Span(a); + + int idx = span.LastIndexOf(target); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestAllignmentNoMatchLastIndexOf_Byte() + { + byte[] array = new byte[4 * Vector.Count]; + for (var i = 0; i < Vector.Count; i++) + { + var span = new Span(array, i, 3 * Vector.Count); + int idx = span.LastIndexOf(5); + Assert.Equal(-1, idx); + + span = new Span(array, i, 3 * Vector.Count - 3); + idx = span.LastIndexOf(5); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestAllignmentMatchLastIndexOf_Byte() + { + byte[] array = new byte[4 * Vector.Count]; + for (int i = 0; i < array.Length; i++) + { + array[i] = 5; + } + for (var i = 0; i < Vector.Count; i++) + { + var span = new Span(array, i, 3 * Vector.Count); + int idx = span.LastIndexOf(5); + Assert.Equal(span.Length - 1, idx); + + span = new Span(array, i, 3 * Vector.Count - 3); + idx = span.LastIndexOf(5); + Assert.Equal(span.Length - 1, idx); + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOf_Byte() + { + for (int length = 2; length <= byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + byte val = (byte)(i + 1); + a[i] = val == 200 ? (byte)201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + + Span span = new Span(a); + int idx = span.LastIndexOf(200); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOf_Byte() + { + for (int length = 0; length <= byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 99; + Span span = new Span(a, 1, length); + int index = span.LastIndexOf(99); + Assert.Equal(-1, index); + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Span/LastIndexOf.char.cs b/external/corefx/src/System.Memory/tests/Span/LastIndexOf.char.cs new file mode 100644 index 0000000000..902150a35b --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Span/LastIndexOf.char.cs @@ -0,0 +1,74 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class SpanTests + { + [Fact] + public static void ZeroLengthLastIndexOf_Char() + { + Span sp = new Span(Array.Empty()); + int idx = sp.LastIndexOf((char)0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void TestMatchLastIndexOf_Char() + { + for (int length = 0; length < 32; length++) + { + char[] a = new char[length]; + for (int i = 0; i < length; i++) + { + a[i] = (char)(i + 1); + } + Span span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + char target = a[targetIndex]; + int idx = span.LastIndexOf(target); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOf_Char() + { + for (int length = 2; length < 32; length++) + { + char[] a = new char[length]; + for (int i = 0; i < length; i++) + { + a[i] = (char)(i + 1); + } + + a[length - 1] = (char)200; + a[length - 2] = (char)200; + + Span span = new Span(a); + int idx = span.LastIndexOf((char)200); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOf_Char() + { + for (int length = 0; length < 100; length++) + { + char[] a = new char[length + 2]; + a[0] = '9'; + a[length + 1] = '9'; + Span span = new Span(a, 1, length); + int index = span.LastIndexOf('9'); + Assert.Equal(-1, index); + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Span/LastIndexOfAny.T.cs b/external/corefx/src/System.Memory/tests/Span/LastIndexOfAny.T.cs new file mode 100644 index 0000000000..8b6570c54c --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Span/LastIndexOfAny.T.cs @@ -0,0 +1,934 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class SpanTests + { + [Fact] + public static void ZeroLengthLastIndexOfAny_TwoByte() + { + var sp = new Span(Array.Empty()); + int idx = sp.LastIndexOfAny(0, 0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledLastIndexOfAny_TwoByte() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + var span = new Span(a); + + int[] targets = { default, 99 }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 2) == 0 ? 0 : 1; + int target0 = targets[index]; + int target1 = targets[(index + 1) % 2]; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(span.Length - 1, idx); + } + } + } + + [Fact] + public static void TestMatchLastIndexOfAny_TwoByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + a[i] = i + 1; + } + var span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + int target0 = a[targetIndex]; + int target1 = 0; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + int target0 = a[targetIndex]; + int target1 = a[targetIndex + 1]; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(targetIndex + 1, idx); + } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + int target0 = 0; + int target1 = a[targetIndex + 1]; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(targetIndex + 1, idx); + } + } + } + + [Fact] + public static void TestNoMatchLastIndexOfAny_TwoByte() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + int target0 = rnd.Next(1, 256); + int target1 = rnd.Next(1, 256); + var span = new Span(a); + + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOfAny_TwoByte() + { + for (int length = 3; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + int val = i + 1; + a[i] = val == 200 ? 201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + + var span = new Span(a); + int idx = span.LastIndexOfAny(200, 200); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOfAny_TwoByte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 98; + var span = new Span(a, 1, length - 1); + int index = span.LastIndexOfAny(99, 98); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 99; + var span = new Span(a, 1, length - 1); + int index = span.LastIndexOfAny(99, 99); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthIndexOf_ThreeByte() + { + var sp = new Span(Array.Empty()); + int idx = sp.LastIndexOfAny(0, 0, 0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledLastIndexOfAny_ThreeByte() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + var span = new Span(a); + + int[] targets = { default, 99, 98 }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 3); + int target0 = targets[index]; + int target1 = targets[(index + 1) % 2]; + int target2 = targets[(index + 1) % 3]; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(span.Length - 1, idx); + } + } + } + + [Fact] + public static void TestMatchLastIndexOfAny_ThreeByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + a[i] = i + 1; + } + var span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + int target0 = a[targetIndex]; + int target1 = 0; + int target2 = 0; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + int target0 = a[targetIndex]; + int target1 = a[targetIndex + 1]; + int target2 = a[targetIndex + 2]; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex + 2, idx); + } + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + int target0 = 0; + int target1 = 0; + int target2 = a[targetIndex + 2]; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex + 2, idx); + } + } + } + + [Fact] + public static void TestNoMatchLastIndexOfAny_ThreeByte() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + int target0 = rnd.Next(1, 256); + int target1 = rnd.Next(1, 256); + int target2 = rnd.Next(1, 256); + var span = new Span(a); + + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOfAny_ThreeByte() + { + for (int length = 4; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + int val = i + 1; + a[i] = val == 200 ? 201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + a[length - 4] = 200; + + var span = new Span(a); + int idx = span.LastIndexOfAny(200, 200, 200); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOfAny_ThreeByte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 98; + var span = new Span(a, 1, length - 1); + int index = span.LastIndexOfAny(99, 98, 99); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 99; + var span = new Span(a, 1, length - 1); + int index = span.LastIndexOfAny(99, 99, 99); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthLastIndexOfAny_ManyByte() + { + var sp = new Span(Array.Empty()); + var values = new ReadOnlySpan(new int[] { 0, 0, 0, 0 }); + int idx = sp.LastIndexOfAny(values); + Assert.Equal(-1, idx); + + values = new ReadOnlySpan(new int[] { }); + idx = sp.LastIndexOfAny(values); + Assert.Equal(0, idx); + } + + [Fact] + public static void DefaultFilledLastIndexOfAny_ManyByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + var span = new Span(a); + + var values = new ReadOnlySpan(new int[] { default, 99, 98, 0 }); + + for (int i = 0; i < length; i++) + { + int idx = span.LastIndexOfAny(values); + Assert.Equal(span.Length - 1, idx); + } + } + } + + [Fact] + public static void TestMatchLastIndexOfAny_ManyByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + a[i] = i + 1; + } + var span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + var values = new ReadOnlySpan(new int[] { a[targetIndex], 0, 0, 0 }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + var values = new ReadOnlySpan(new int[] { a[targetIndex], a[targetIndex + 1], a[targetIndex + 2], a[targetIndex + 3] }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(targetIndex + 3, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + var values = new ReadOnlySpan(new int[] { 0, 0, 0, a[targetIndex + 3] }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(targetIndex + 3, idx); + } + } + } + + [Fact] + public static void TestMatchValuesLargerLastIndexOfAny_ManyByte() + { + var rnd = new Random(42); + for (int length = 2; length < byte.MaxValue; length++) + { + var a = new int[length]; + int expectedIndex = length / 2; + for (int i = 0; i < length; i++) + { + if (i == expectedIndex) + { + continue; + } + a[i] = 255; + } + var span = new Span(a); + + var targets = new int[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + if (i == length + 1) + { + continue; + } + targets[i] = rnd.Next(1, 255); + } + + var values = new ReadOnlySpan(targets); + int idx = span.LastIndexOfAny(values); + Assert.Equal(expectedIndex, idx); + } + } + + [Fact] + public static void TestNoMatchLastIndexOfAny_ManyByte() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length]; + var targets = new int[length]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = rnd.Next(1, 256); + } + var span = new Span(a); + var values = new ReadOnlySpan(targets); + + int idx = span.LastIndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestNoMatchValuesLargerLastIndexOfAny_ManyByte() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length]; + var targets = new int[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = rnd.Next(1, 256); + } + var span = new Span(a); + var values = new ReadOnlySpan(targets); + + int idx = span.LastIndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOfAny_ManyByte() + { + for (int length = 5; length < byte.MaxValue; length++) + { + var a = new int[length]; + for (int i = 0; i < length; i++) + { + int val = i + 1; + a[i] = val == 200 ? 201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + a[length - 4] = 200; + a[length - 5] = 200; + + var span = new Span(a); + var values = new ReadOnlySpan(new int[] { 200, 200, 200, 200, 200, 200, 200, 200, 200 }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOfAny_ManyByte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 98; + var span = new Span(a, 1, length - 1); + var values = new ReadOnlySpan(new int[] { 99, 98, 99, 98, 99, 98 }); + int index = span.LastIndexOfAny(values); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new int[length + 2]; + a[0] = 99; + a[length + 1] = 99; + var span = new Span(a, 1, length - 1); + var values = new ReadOnlySpan(new int[] { 99, 99, 99, 99, 99, 99 }); + int index = span.LastIndexOfAny(values); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthLastIndexOfAny_String_TwoByte() + { + var sp = new Span(Array.Empty()); + int idx = sp.LastIndexOfAny("0", "0"); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledLastIndexOfAny_String_TwoByte() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + var span = new Span(a); + span.Fill(""); + + string[] targets = { "", "99" }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 2) == 0 ? 0 : 1; + string target0 = targets[index]; + string target1 = targets[(index + 1) % 2]; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(span.Length - 1, idx); + } + } + } + + [Fact] + public static void TestMatchLastIndexOfAny_String_TwoByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + a[i] = (i + 1).ToString(); + } + var span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + string target0 = a[targetIndex]; + string target1 = "0"; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + string target0 = a[targetIndex]; + string target1 = a[targetIndex + 1]; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(targetIndex + 1, idx); + } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + string target0 = "0"; + string target1 = a[targetIndex + 1]; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(targetIndex + 1, idx); + } + } + } + + [Fact] + public static void TestNoMatchLastIndexOfAny_String_TwoByte() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + string target0 = rnd.Next(1, 256).ToString(); + string target1 = rnd.Next(1, 256).ToString(); + var span = new Span(a); + + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOfAny_String_TwoByte() + { + for (int length = 3; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + string val = (i + 1).ToString(); + a[i] = val == "200" ? "201" : val; + } + + a[length - 1] = "200"; + a[length - 2] = "200"; + a[length - 3] = "200"; + + var span = new Span(a); + int idx = span.LastIndexOfAny("200", "200"); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOfAny_String_TwoByte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "98"; + var span = new Span(a, 1, length - 1); + int index = span.LastIndexOfAny("99", "98"); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "99"; + var span = new Span(a, 1, length - 1); + int index = span.LastIndexOfAny("99", "99"); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthIndexOf_String_ThreeByte() + { + var sp = new Span(Array.Empty()); + int idx = sp.LastIndexOfAny("0", "0", "0"); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledLastIndexOfAny_String_ThreeByte() + { + var rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + var span = new Span(a); + span.Fill(""); + + string[] targets = { "", "99", "98" }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 3); + string target0 = targets[index]; + string target1 = targets[(index + 1) % 2]; + string target2 = targets[(index + 1) % 3]; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(span.Length - 1, idx); + } + } + } + + [Fact] + public static void TestMatchLastIndexOfAny_String_ThreeByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + a[i] = (i + 1).ToString(); + } + var span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + string target0 = a[targetIndex]; + string target1 = "0"; + string target2 = "0"; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + string target0 = a[targetIndex]; + string target1 = a[targetIndex + 1]; + string target2 = a[targetIndex + 2]; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex + 2, idx); + } + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + string target0 = "0"; + string target1 = "0"; + string target2 = a[targetIndex + 2]; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex + 2, idx); + } + } + } + + [Fact] + public static void TestNoMatchLastIndexOfAny_String_ThreeByte() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + string target0 = rnd.Next(1, 256).ToString(); + string target1 = rnd.Next(1, 256).ToString(); + string target2 = rnd.Next(1, 256).ToString(); + var span = new Span(a); + + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOfAny_String_ThreeByte() + { + for (int length = 4; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + string val = (i + 1).ToString(); + a[i] = val == "200" ? "201" : val; + } + + a[length - 1] = "200"; + a[length - 2] = "200"; + a[length - 3] = "200"; + a[length - 4] = "200"; + + var span = new Span(a); + int idx = span.LastIndexOfAny("200", "200", "200"); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOfAny_String_ThreeByte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "98"; + var span = new Span(a, 1, length - 1); + int index = span.LastIndexOfAny("99", "98", "99"); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "99"; + var span = new Span(a, 1, length - 1); + int index = span.LastIndexOfAny("99", "99", "99"); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthLastIndexOfAny_String_ManyByte() + { + var sp = new Span(Array.Empty()); + var values = new ReadOnlySpan(new string[] { "0", "0", "0", "0" }); + int idx = sp.LastIndexOfAny(values); + Assert.Equal(-1, idx); + + values = new ReadOnlySpan(new string[] { }); + idx = sp.LastIndexOfAny(values); + Assert.Equal(0, idx); + } + + [Fact] + public static void DefaultFilledLastIndexOfAny_String_ManyByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + var span = new Span(a); + span.Fill(""); + + var values = new ReadOnlySpan(new string[] { "", "99", "98", "0" }); + + for (int i = 0; i < length; i++) + { + int idx = span.LastIndexOfAny(values); + Assert.Equal(span.Length - 1, idx); + } + } + } + + [Fact] + public static void TestMatchLastIndexOfAny_String_ManyByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + a[i] = (i + 1).ToString(); + } + var span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + var values = new ReadOnlySpan(new string[] { a[targetIndex], "0", "0", "0" }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + var values = new ReadOnlySpan(new string[] { a[targetIndex], a[targetIndex + 1], a[targetIndex + 2], a[targetIndex + 3] }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(targetIndex + 3, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + var values = new ReadOnlySpan(new string[] { "0", "0", "0", a[targetIndex + 3] }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(targetIndex + 3, idx); + } + } + } + + [Fact] + public static void TestMatchValuesLargerLastIndexOfAny_String_ManyByte() + { + var rnd = new Random(42); + for (int length = 2; length < byte.MaxValue; length++) + { + var a = new string[length]; + int expectedIndex = length / 2; + for (int i = 0; i < length; i++) + { + if (i == expectedIndex) + { + a[i] = "val"; + continue; + } + a[i] = "255"; + } + var span = new Span(a); + + var targets = new string[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + if (i == length + 1) + { + targets[i] = "val"; + continue; + } + targets[i] = rnd.Next(1, 255).ToString(); + } + + var values = new ReadOnlySpan(targets); + int idx = span.LastIndexOfAny(values); + Assert.Equal(expectedIndex, idx); + } + } + + [Fact] + public static void TestNoMatchLastIndexOfAny_String_ManyByte() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length]; + var targets = new string[length]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = rnd.Next(1, 256).ToString(); + } + var span = new Span(a); + var values = new ReadOnlySpan(targets); + + int idx = span.LastIndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestNoMatchValuesLargerLastIndexOfAny_String_ManyByte() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length]; + var targets = new string[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = rnd.Next(1, 256).ToString(); + } + var span = new Span(a); + var values = new ReadOnlySpan(targets); + + int idx = span.LastIndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOfAny_String_ManyByte() + { + for (int length = 5; length < byte.MaxValue; length++) + { + var a = new string[length]; + for (int i = 0; i < length; i++) + { + string val = (i + 1).ToString(); + a[i] = val == "200" ? "201" : val; + } + + a[length - 1] = "200"; + a[length - 2] = "200"; + a[length - 3] = "200"; + a[length - 4] = "200"; + a[length - 5] = "200"; + + var span = new Span(a); + var values = new ReadOnlySpan(new string[] { "200", "200", "200", "200", "200", "200", "200", "200", "200" }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOfAny_String_ManyByte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "98"; + var span = new Span(a, 1, length - 1); + var values = new ReadOnlySpan(new string[] { "99", "98", "99", "98", "99", "98" }); + int index = span.LastIndexOfAny(values); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + var a = new string[length + 2]; + a[0] = "99"; + a[length + 1] = "99"; + var span = new Span(a, 1, length - 1); + var values = new ReadOnlySpan(new string[] { "99", "99", "99", "99", "99", "99" }); + int index = span.LastIndexOfAny(values); + Assert.Equal(-1, index); + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Span/LastIndexOfAny.byte.cs b/external/corefx/src/System.Memory/tests/Span/LastIndexOfAny.byte.cs new file mode 100644 index 0000000000..652f5c1bca --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Span/LastIndexOfAny.byte.cs @@ -0,0 +1,521 @@ +// 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.Text; +using Xunit; + +namespace System.SpanTests +{ + public static partial class SpanTests + { + [Theory] + [InlineData("a", "a", 'a', 0)] + [InlineData("ab", "a", 'a', 0)] + [InlineData("aab", "a", 'a', 1)] + [InlineData("acab", "a", 'a', 2)] + [InlineData("acab", "c", 'c', 1)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "lo", 'o', 14)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "ol", 'o', 14)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "ll", 'l', 11)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "lmr", 'r', 17)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "rml", 'r', 17)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "mlr", 'r', 17)] + [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "lmr", 'r', 43)] + [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrzzzzzzzz", "lmr", 'r', 43)] + [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqxzzzzzzzz", "lmr", 'm', 38)] + [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqlzzzzzzzz", "lmr", 'l', 43)] + [InlineData("/localhost:5000/PATH/%2FPATH2/ HTTP/1.1", " %?", ' ', 30)] + [InlineData("/localhost:5000/PATH/%2FPATH2/?key=value HTTP/1.1", " %?", ' ', 40)] + [InlineData("/localhost:5000/PATH/PATH2/?key=value HTTP/1.1", " %?", ' ', 37)] + [InlineData("/localhost:5000/PATH/PATH2/ HTTP/1.1", " %?", ' ', 27)] + public static void LastIndexOfAnyStrings_Byte(string raw, string search, char expectResult, int expectIndex) + { + byte[] buffers = Encoding.UTF8.GetBytes(raw); + var span = new Span(buffers); + char[] searchFor = search.ToCharArray(); + byte[] searchForBytes = Encoding.UTF8.GetBytes(searchFor); + + var index = -1; + if (searchFor.Length == 1) + { + index = span.LastIndexOf((byte)searchFor[0]); + } + else if (searchFor.Length == 2) + { + index = span.LastIndexOfAny((byte)searchFor[0], (byte)searchFor[1]); + } + else if (searchFor.Length == 3) + { + index = span.LastIndexOfAny((byte)searchFor[0], (byte)searchFor[1], (byte)searchFor[2]); + } + else + { + index = span.LastIndexOfAny(new ReadOnlySpan(searchForBytes)); + } + + var found = span[index]; + Assert.Equal((byte)expectResult, found); + Assert.Equal(expectIndex, index); + } + + [Fact] + public static void ZeroLengthLastIndexOfAny_Byte_TwoByte() + { + Span sp = new Span(Array.Empty()); + int idx = sp.LastIndexOfAny(0, 0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledLastIndexOfAny_Byte_TwoByte() + { + Random rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + Span span = new Span(a); + + byte[] targets = { default, 99 }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 2) == 0 ? 0 : 1; + byte target0 = targets[index]; + byte target1 = targets[(index + 1) % 2]; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(span.Length - 1, idx); + } + } + } + + [Fact] + public static void TestMatchLastIndexOfAny_Byte_TwoByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + Span span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = 0; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = a[targetIndex + 1]; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(targetIndex + 1, idx); + } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + byte target0 = 0; + byte target1 = a[targetIndex + 1]; + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(targetIndex + 1, idx); + } + } + } + + [Fact] + public static void TestNoMatchLastIndexOfAny_Byte_TwoByte() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + byte target0 = (byte)rnd.Next(1, 256); + byte target1 = (byte)rnd.Next(1, 256); + Span span = new Span(a); + + int idx = span.LastIndexOfAny(target0, target1); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOfAny_Byte_TwoByte() + { + for (int length = 3; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + byte val = (byte)(i + 1); + a[i] = val == 200 ? (byte)201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + + Span span = new Span(a); + int idx = span.LastIndexOfAny(200, 200); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOfAny_Byte_TwoByte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 98; + Span span = new Span(a, 1, length - 1); + int index = span.LastIndexOfAny(99, 98); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 99; + Span span = new Span(a, 1, length - 1); + int index = span.LastIndexOfAny(99, 99); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthIndexOf_Byte_ThreeByte() + { + Span sp = new Span(Array.Empty()); + int idx = sp.LastIndexOfAny(0, 0, 0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void DefaultFilledLastIndexOfAny_Byte_ThreeByte() + { + Random rnd = new Random(42); + + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + Span span = new Span(a); + + byte[] targets = { default, 99, 98 }; + + for (int i = 0; i < length; i++) + { + int index = rnd.Next(0, 3); + byte target0 = targets[index]; + byte target1 = targets[(index + 1) % 2]; + byte target2 = targets[(index + 1) % 3]; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(span.Length - 1, idx); + } + } + } + + [Fact] + public static void TestMatchLastIndexOfAny_Byte_ThreeByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + Span span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = 0; + byte target2 = 0; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = a[targetIndex + 1]; + byte target2 = a[targetIndex + 2]; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex + 2, idx); + } + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + byte target0 = 0; + byte target1 = 0; + byte target2 = a[targetIndex + 2]; + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex + 2, idx); + } + } + } + + [Fact] + public static void TestNoMatchLastIndexOfAny_Byte_ThreeByte() + { + var rnd = new Random(42); + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + byte target0 = (byte)rnd.Next(1, 256); + byte target1 = (byte)rnd.Next(1, 256); + byte target2 = (byte)rnd.Next(1, 256); + Span span = new Span(a); + + int idx = span.LastIndexOfAny(target0, target1, target2); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOfAny_Byte_ThreeByte() + { + for (int length = 4; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + byte val = (byte)(i + 1); + a[i] = val == 200 ? (byte)201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + a[length - 4] = 200; + + Span span = new Span(a); + int idx = span.LastIndexOfAny(200, 200, 200); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOfAny_Byte_ThreeByte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 98; + Span span = new Span(a, 1, length - 1); + int index = span.LastIndexOfAny(99, 98, 99); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 99; + Span span = new Span(a, 1, length - 1); + int index = span.LastIndexOfAny(99, 99, 99); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthLastIndexOfAny_Byte_ManyByte() + { + Span sp = new Span(Array.Empty()); + var values = new ReadOnlySpan(new byte[] { 0, 0, 0, 0 }); + int idx = sp.LastIndexOfAny(values); + Assert.Equal(-1, idx); + + values = new ReadOnlySpan(new byte[] { }); + idx = sp.LastIndexOfAny(values); + Assert.Equal(0, idx); + } + + [Fact] + public static void DefaultFilledLastIndexOfAny_Byte_ManyByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + Span span = new Span(a); + + var values = new ReadOnlySpan(new byte[] { default, 99, 98, 0 }); + + for (int i = 0; i < length; i++) + { + int idx = span.LastIndexOfAny(values); + Assert.Equal(span.Length - 1, idx); + } + } + } + + [Fact] + public static void TestMatchLastIndexOfAny_Byte_ManyByte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + Span span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + var values = new ReadOnlySpan(new byte[] { a[targetIndex], 0, 0, 0 }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + var values = new ReadOnlySpan(new byte[] { a[targetIndex], a[targetIndex + 1], a[targetIndex + 2], a[targetIndex + 3] }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(targetIndex + 3, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + var values = new ReadOnlySpan(new byte[] { 0, 0, 0, a[targetIndex + 3] }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(targetIndex + 3, idx); + } + } + } + + [Fact] + public static void TestMatchValuesLargerLastIndexOfAny_Byte_ManyByte() + { + var rnd = new Random(42); + for (int length = 2; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + int expectedIndex = length / 2; + for (int i = 0; i < length; i++) + { + if (i == expectedIndex) + { + continue; + } + a[i] = 255; + } + Span span = new Span(a); + + byte[] targets = new byte[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + if (i == length + 1) + { + continue; + } + targets[i] = (byte)rnd.Next(1, 255); + } + + var values = new ReadOnlySpan(targets); + int idx = span.LastIndexOfAny(values); + Assert.Equal(expectedIndex, idx); + } + } + + [Fact] + public static void TestNoMatchLastIndexOfAny_Byte_ManyByte() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + byte[] targets = new byte[length]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = (byte)rnd.Next(1, 256); + } + Span span = new Span(a); + var values = new ReadOnlySpan(targets); + + int idx = span.LastIndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestNoMatchValuesLargerLastIndexOfAny_Byte_ManyByte() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + byte[] targets = new byte[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = (byte)rnd.Next(1, 256); + } + Span span = new Span(a); + var values = new ReadOnlySpan(targets); + + int idx = span.LastIndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchLastIndexOfAny_Byte_ManyByte() + { + for (int length = 5; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + byte val = (byte)(i + 1); + a[i] = val == 200 ? (byte)201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + a[length - 4] = 200; + a[length - 5] = 200; + + Span span = new Span(a); + var values = new ReadOnlySpan(new byte[] { 200, 200, 200, 200, 200, 200, 200, 200, 200 }); + int idx = span.LastIndexOfAny(values); + Assert.Equal(length - 1, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeLastIndexOfAny_Byte_ManyByte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 98; + Span span = new Span(a, 1, length - 1); + var values = new ReadOnlySpan(new byte[] { 99, 98, 99, 98, 99, 98 }); + int index = span.LastIndexOfAny(values); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 99; + Span span = new Span(a, 1, length - 1); + var values = new ReadOnlySpan(new byte[] { 99, 99, 99, 99, 99, 99 }); + int index = span.LastIndexOfAny(values); + Assert.Equal(-1, index); + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Span/LastIndexOfSequence.T.cs b/external/corefx/src/System.Memory/tests/Span/LastIndexOfSequence.T.cs new file mode 100644 index 0000000000..833d80c200 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Span/LastIndexOfSequence.T.cs @@ -0,0 +1,245 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class SpanTests + { + [Fact] + public static void LastIndexOfSequenceMatchAtStart() + { + Span span = new Span(new int[] { 5, 1, 77, 2, 3, 77, 77, 4, 5, 77, 77, 77, 88, 6, 6, 77, 77, 88, 9 }); + Span value = new Span(new int[] { 5, 1, 77 }); + int index = span.LastIndexOf(value); + Assert.Equal(0, index); + } + + [Fact] + public static void LastIndexOfSequenceMultipleMatch() + { + Span span = new Span(new int[] { 1, 2, 3, 1, 2, 3, 1, 2, 3, 1 }); + Span value = new Span(new int[] { 2, 3 }); + int index = span.LastIndexOf(value); + Assert.Equal(7, index); + } + + [Fact] + public static void LastIndexOfSequenceRestart() + { + Span span = new Span(new int[] { 0, 1, 77, 2, 3, 77, 77, 4, 5, 77, 77, 77, 88, 6, 6, 77, 77, 8, 9, 77, 0, 1 }); + Span value = new Span(new int[] { 77, 77, 88 }); + int index = span.LastIndexOf(value); + Assert.Equal(10, index); + } + + [Fact] + public static void LastIndexOfSequenceNoMatch() + { + Span span = new Span(new int[] { 0, 1, 77, 2, 3, 77, 77, 4, 5, 77, 77, 77, 88, 6, 6, 77, 77, 88, 9 }); + Span value = new Span(new int[] { 77, 77, 88, 99 }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceNotEvenAHeadMatch() + { + Span span = new Span(new int[] { 0, 1, 77, 2, 3, 77, 77, 4, 5, 77, 77, 77, 88, 6, 6, 77, 77, 88, 9 }); + Span value = new Span(new int[] { 100, 77, 88, 99 }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceMatchAtVeryEnd() + { + Span span = new Span(new int[] { 0, 1, 2, 3, 4, 5 }); + Span value = new Span(new int[] { 3, 4, 5 }); + int index = span.LastIndexOf(value); + Assert.Equal(3, index); + } + + [Fact] + public static void LastIndexOfSequenceJustPastVeryEnd() + { + Span span = new Span(new int[] { 0, 1, 2, 3, 4, 5 }, 0, 5); + Span value = new Span(new int[] { 3, 4, 5 }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceZeroLengthValue() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new int[] { 0, 1, 77, 2, 3, 77, 77, 4, 5, 77, 77, 77, 88, 6, 6, 77, 77, 88, 9 }); + Span value = new Span(Array.Empty()); + int index = span.LastIndexOf(value); + Assert.Equal(0, index); + } + + [Fact] + public static void LastIndexOfSequenceZeroLengthSpan() + { + Span span = new Span(Array.Empty()); + Span value = new Span(new int[] { 1, 2, 3 }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValue() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new int[] { 0, 1, 2, 3, 4, 5 }); + Span value = new Span(new int[] { 2 }); + int index = span.LastIndexOf(value); + Assert.Equal(2, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValueAtVeryEnd() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new int[] { 0, 1, 2, 3, 4, 5 }); + Span value = new Span(new int[] { 5 }); + int index = span.LastIndexOf(value); + Assert.Equal(5, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValueMultipleTimes() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new int[] { 0, 1, 5, 3, 4, 5 }); + Span value = new Span(new int[] { 5 }); + int index = span.LastIndexOf(value); + Assert.Equal(5, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValueJustPasttVeryEnd() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new int[] { 0, 1, 2, 3, 4, 5 }, 0, 5); + Span value = new Span(new int[] { 5 }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceMatchAtStart_String() + { + Span span = new Span(new string[] { "5", "1", "77", "2", "3", "77", "77", "4", "5", "77", "77", "77", "88", "6", "6", "77", "77", "88", "9" }); + Span value = new Span(new string[] { "5", "1", "77" }); + int index = span.LastIndexOf(value); + Assert.Equal(0, index); + } + + [Fact] + public static void LastIndexOfSequenceMultipleMatch_String() + { + Span span = new Span(new string[] { "1", "2", "3", "1", "2", "3", "1", "2", "3" }); + Span value = new Span(new string[] { "2", "3" }); + int index = span.LastIndexOf(value); + Assert.Equal(7, index); + } + + [Fact] + public static void LastIndexOfSequenceRestart_String() + { + Span span = new Span(new string[] { "0", "1", "77", "2", "3", "77", "77", "4", "5", "77", "77", "77", "88", "6", "6", "77", "77", "8", "9", "77", "0", "1" }); + Span value = new Span(new string[] { "77", "77", "88" }); + int index = span.LastIndexOf(value); + Assert.Equal(10, index); + } + + [Fact] + public static void LastIndexOfSequenceNoMatch_String() + { + Span span = new Span(new string[] { "0", "1", "77", "2", "3", "77", "77", "4", "5", "77", "77", "77", "88", "6", "6", "77", "77", "88", "9" }); + Span value = new Span(new string[] { "77", "77", "88", "99" }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceNotEvenAHeadMatch_String() + { + Span span = new Span(new string[] { "0", "1", "77", "2", "3", "77", "77", "4", "5", "77", "77", "77", "88", "6", "6", "77", "77", "88", "9" }); + Span value = new Span(new string[] { "100", "77", "88", "99" }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceMatchAtVeryEnd_String() + { + Span span = new Span(new string[] { "0", "1", "2", "3", "4", "5" }); + Span value = new Span(new string[] { "3", "4", "5" }); + int index = span.LastIndexOf(value); + Assert.Equal(3, index); + } + + [Fact] + public static void LastIndexOfSequenceJustPastVeryEnd_String() + { + Span span = new Span(new string[] { "0", "1", "2", "3", "4", "5" }, 0, 5); + Span value = new Span(new string[] { "3", "4", "5" }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceZeroLengthValue_String() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new string[] { "0", "1", "77", "2", "3", "77", "77", "4", "5", "77", "77", "77", "88", "6", "6", "77", "77", "88", "9" }); + Span value = new Span(Array.Empty()); + int index = span.LastIndexOf(value); + Assert.Equal(0, index); + } + + [Fact] + public static void LastIndexOfSequenceZeroLengthSpan_String() + { + Span span = new Span(Array.Empty()); + Span value = new Span(new string[] { "1", "2", "3" }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValue_String() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new string[] { "0", "1", "2", "3", "4", "5" }); + Span value = new Span(new string[] { "2" }); + int index = span.LastIndexOf(value); + Assert.Equal(2, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValueAtVeryEnd_String() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new string[] { "0", "1", "2", "3", "4", "5" }); + Span value = new Span(new string[] { "5" }); + int index = span.LastIndexOf(value); + Assert.Equal(5, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValueJustPasttVeryEnd_String() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new string[] { "0", "1", "2", "3", "4", "5" }, 0, 5); + Span value = new Span(new string[] { "5" }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Span/LastIndexOfSequence.byte.cs b/external/corefx/src/System.Memory/tests/Span/LastIndexOfSequence.byte.cs new file mode 100644 index 0000000000..96b229725e --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Span/LastIndexOfSequence.byte.cs @@ -0,0 +1,133 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class SpanTests + { + [Fact] + public static void LastIndexOfSequenceMatchAtStart_Byte() + { + Span span = new Span(new byte[] { 5, 1, 77, 2, 3, 77, 77, 4, 5, 77, 77, 77, 88, 6, 6, 77, 77, 88, 9 }); + Span value = new Span(new byte[] { 5, 1, 77 }); + int index = span.LastIndexOf(value); + Assert.Equal(0, index); + } + + [Fact] + public static void LastIndexOfSequenceMultipleMatch_Byte() + { + Span span = new Span(new byte[] { 1, 2, 3, 1, 2, 3, 1, 2, 3, 1 }); + Span value = new Span(new byte[] { 2, 3 }); + int index = span.LastIndexOf(value); + Assert.Equal(7, index); + } + + [Fact] + public static void LastIndexOfSequenceRestart_Byte() + { + Span span = new Span(new byte[] { 0, 1, 77, 2, 3, 77, 77, 4, 5, 77, 77, 77, 88, 6, 6, 77, 77, 8, 9, 77, 0, 1 }); + Span value = new Span(new byte[] { 77, 77, 88 }); + int index = span.LastIndexOf(value); + Assert.Equal(10, index); + } + + [Fact] + public static void LastIndexOfSequenceNoMatch_Byte() + { + Span span = new Span(new byte[] { 0, 1, 77, 2, 3, 77, 77, 4, 5, 77, 77, 77, 88, 6, 6, 77, 77, 88, 9 }); + Span value = new Span(new byte[] { 77, 77, 88, 99 }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceNotEvenAHeadMatch_Byte() + { + Span span = new Span(new byte[] { 0, 1, 77, 2, 3, 77, 77, 4, 5, 77, 77, 77, 88, 6, 6, 77, 77, 88, 9 }); + Span value = new Span(new byte[] { 100, 77, 88, 99 }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceMatchAtVeryEnd_Byte() + { + Span span = new Span(new byte[] { 0, 1, 2, 3, 4, 5 }); + Span value = new Span(new byte[] { 3, 4, 5 }); + int index = span.LastIndexOf(value); + Assert.Equal(3, index); + } + + [Fact] + public static void LastIndexOfSequenceJustPastVeryEnd_Byte() + { + Span span = new Span(new byte[] { 0, 1, 2, 3, 4, 5 }, 0, 5); + Span value = new Span(new byte[] { 3, 4, 5 }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceZeroLengthValue_Byte() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new byte[] { 0, 1, 77, 2, 3, 77, 77, 4, 5, 77, 77, 77, 88, 6, 6, 77, 77, 88, 9 }); + Span value = new Span(Array.Empty()); + int index = span.LastIndexOf(value); + Assert.Equal(0, index); + } + + [Fact] + public static void LastIndexOfSequenceZeroLengthSpan_Byte() + { + Span span = new Span(Array.Empty()); + Span value = new Span(new byte[] { 1, 2, 3 }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValue_Byte() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new byte[] { 0, 1, 2, 3, 4, 5 }); + Span value = new Span(new byte[] { 2 }); + int index = span.LastIndexOf(value); + Assert.Equal(2, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValueAtVeryEnd_Byte() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new byte[] { 0, 1, 2, 3, 4, 5 }); + Span value = new Span(new byte[] { 5 }); + int index = span.LastIndexOf(value); + Assert.Equal(5, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValueMultipleTimes_Byte() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new byte[] { 0, 1, 5, 3, 4, 5 }); + Span value = new Span(new byte[] { 5 }); + int index = span.LastIndexOf(value); + Assert.Equal(5, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValueJustPasttVeryEnd_Byte() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new byte[] { 0, 1, 2, 3, 4, 5 }, 0, 5); + Span value = new Span(new byte[] { 5 }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Span/LastIndexOfSequence.char.cs b/external/corefx/src/System.Memory/tests/Span/LastIndexOfSequence.char.cs new file mode 100644 index 0000000000..af027cb54a --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Span/LastIndexOfSequence.char.cs @@ -0,0 +1,133 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class SpanTests + { + [Fact] + public static void LastIndexOfSequenceMatchAtStart_Char() + { + Span span = new Span(new char[] { '5', '1', '7', '2', '3', '7', '7', '4', '5', '7', '7', '7', '8', '6', '6', '7', '7', '8', '9' }); + Span value = new Span(new char[] { '5', '1', '7' }); + int index = span.LastIndexOf(value); + Assert.Equal(0, index); + } + + [Fact] + public static void LastIndexOfSequenceMultipleMatch_Char() + { + Span span = new Span(new char[] { '1', '2', '3', '1', '2', '3', '1', '2', '3', '1' }); + Span value = new Span(new char[] { '2', '3' }); + int index = span.LastIndexOf(value); + Assert.Equal(7, index); + } + + [Fact] + public static void LastIndexOfSequenceRestart_Char() + { + Span span = new Span(new char[] { '5', '1', '7', '2', '3', '7', '7', '4', '5', '7', '7', '7', '8', '6', '6', '7', '7', '6', '9', '7', '0', '1' }); + Span value = new Span(new char[] { '7', '7', '8' }); + int index = span.LastIndexOf(value); + Assert.Equal(10, index); + } + + [Fact] + public static void LastIndexOfSequenceNoMatch_Char() + { + Span span = new Span(new char[] { '0', '1', '7', '2', '3', '7', '7', '4', '5', '7', '7', '7', '8', '6', '6', '7', '7', '8', '9' }); + Span value = new Span(new char[] { '7', '7', '8', 'X' }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceNotEvenAHeadMatch_Char() + { + Span span = new Span(new char[] { '0', '1', '7', '2', '3', '7', '7', '4', '5', '7', '7', '7', '8', '6', '6', '7', '7', '8', '9' }); + Span value = new Span(new char[] { 'X', '7', '8', '9' }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceMatchAtVeryEnd_Char() + { + Span span = new Span(new char[] { '0', '1', '2', '3', '4', '5' }); + Span value = new Span(new char[] { '3', '4', '5' }); + int index = span.LastIndexOf(value); + Assert.Equal(3, index); + } + + [Fact] + public static void LastIndexOfSequenceJustPastVeryEnd_Char() + { + Span span = new Span(new char[] { '0', '1', '2', '3', '4', '5' }, 0, 5); + Span value = new Span(new char[] { '3', '4', '5' }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceZeroLengthValue_Char() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new char[] { '0', '1', '7', '2', '3', '7', '7', '4', '5', '7', '7', '7', '8', '6', '6', '7', '7', '8', '9' }); + Span value = new Span(Array.Empty()); + int index = span.LastIndexOf(value); + Assert.Equal(0, index); + } + + [Fact] + public static void LastIndexOfSequenceZeroLengthSpan_Char() + { + Span span = new Span(Array.Empty()); + Span value = new Span(new char[] { '1', '2', '3' }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValue_Char() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new char[] { '0', '1', '2', '3', '4', '5' }); + Span value = new Span(new char[] { '2' }); + int index = span.LastIndexOf(value); + Assert.Equal(2, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValueAtVeryEnd_Char() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new char[] { '0', '1', '2', '3', '4', '5' }); + Span value = new Span(new char[] { '5' }); + int index = span.LastIndexOf(value); + Assert.Equal(5, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValueMultipleTimes_Char() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new char[] { '0', '1', '5', '3', '4', '5' }); + Span value = new Span(new char[] { '5' }); + int index = span.LastIndexOf(value); + Assert.Equal(5, index); + } + + [Fact] + public static void LastIndexOfSequenceLengthOneValueJustPasttVeryEnd_Char() + { + // A zero-length value is always "found" at the start of the span. + Span span = new Span(new char[] { '0', '1', '2', '3', '4', '5' }, 0, 5); + Span value = new Span(new char[] { '5' }); + int index = span.LastIndexOf(value); + Assert.Equal(-1, index); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Span/NonPortableCast.cs b/external/corefx/src/System.Memory/tests/Span/NonPortableCast.cs index 18b80f7b49..81b0bf6e45 100644 --- a/external/corefx/src/System.Memory/tests/Span/NonPortableCast.cs +++ b/external/corefx/src/System.Memory/tests/Span/NonPortableCast.cs @@ -4,31 +4,52 @@ using Xunit; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.SpanTests { public static partial class SpanTests { [Fact] - public static void PortableCastUIntToUShort() + public static void NonPortableCastUIntToUShort() { uint[] a = { 0x44332211, 0x88776655 }; Span span = new Span(a); Span asUShort = span.NonPortableCast(); - Assert.True(Unsafe.AreSame(ref Unsafe.As(ref span.DangerousGetPinnableReference()), ref asUShort.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), ref MemoryMarshal.GetReference(asUShort))); asUShort.Validate(0x2211, 0x4433, 0x6655, 0x8877); } [Fact] - public static void PortableCastToTypeContainsReferences() + public static void NonPortableCastShortToLong() + { + short[] a = { 0x1234, 0x2345, 0x3456, 0x4567, 0x5678 }; + Span span = new Span(a); + Span asLong = span.NonPortableCast(); + + Assert.True(Unsafe.AreSame(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), ref MemoryMarshal.GetReference(asLong))); + asLong.Validate(0x4567345623451234); + } + + [Fact] + public static unsafe void NonPortableCastOverflow() + { + Span span = new Span(null, Int32.MaxValue); + + TestHelpers.AssertThrows(span, (_span) => _span.NonPortableCast().DontBox()); + TestHelpers.AssertThrows(span, (_span) => _span.NonPortableCast().DontBox()); + } + + [Fact] + public static void NonPortableCastToTypeContainsReferences() { Span span = new Span(Array.Empty()); TestHelpers.AssertThrows(span, (_span) => _span.NonPortableCast().DontBox()); } [Fact] - public static void PortableCastFromTypeContainsReferences() + public static void NonPortableCastFromTypeContainsReferences() { Span span = new Span(Array.Empty()); TestHelpers.AssertThrows(span, (_span) => _span.NonPortableCast().DontBox()); diff --git a/external/corefx/src/System.Memory/tests/Span/Overflow.cs b/external/corefx/src/System.Memory/tests/Span/Overflow.cs index b9f9070f1b..a580970c9e 100644 --- a/external/corefx/src/System.Memory/tests/Span/Overflow.cs +++ b/external/corefx/src/System.Memory/tests/Span/Overflow.cs @@ -4,6 +4,7 @@ using Xunit; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.SpanTests { @@ -36,9 +37,9 @@ namespace System.SpanTests try { ref Guid memory = ref Unsafe.AsRef(memBlock.ToPointer()); - var span = new Span(memBlock.ToPointer(), GuidThreeGiBLimit); + var span = new Span(memBlock.ToPointer(), s_guidThreeGiBLimit); - int bigIndex = checked(GuidTwoGiBLimit + 1); + int bigIndex = checked(s_guidTwoGiBLimit + 1); uint byteOffset = checked((uint)bigIndex * (uint)sizeof(Guid)); Assert.True(byteOffset > int.MaxValue); // Make sure byteOffset actually overflows 2Gb, or this test is pointless. ref Guid expected = ref Unsafe.Add(ref memory, bigIndex); @@ -46,10 +47,10 @@ namespace System.SpanTests Assert.True(Unsafe.AreSame(ref expected, ref span[bigIndex])); Span slice = span.Slice(bigIndex); - Assert.True(Unsafe.AreSame(ref expected, ref slice.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref expected, ref MemoryMarshal.GetReference(slice))); slice = span.Slice(bigIndex, 1); - Assert.True(Unsafe.AreSame(ref expected, ref slice.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref expected, ref MemoryMarshal.GetReference(slice))); } finally { @@ -63,8 +64,7 @@ namespace System.SpanTests private const long TwoGiB = 2L * 1024L * 1024L * 1024L; private const long OneGiB = 1L * 1024L * 1024L * 1024L; - private static readonly int GuidThreeGiBLimit = (int)(ThreeGiB / Unsafe.SizeOf()); // sizeof(Guid) requires unsafe keyword and I don't want to mark the entire class unsafe. - private static readonly int GuidTwoGiBLimit = (int)(TwoGiB / Unsafe.SizeOf()); - private static readonly int GuidOneGiBLimit = (int)(OneGiB / Unsafe.SizeOf()); + private static readonly int s_guidThreeGiBLimit = (int)(ThreeGiB / Unsafe.SizeOf()); // sizeof(Guid) requires unsafe keyword and I don't want to mark the entire class unsafe. + private static readonly int s_guidTwoGiBLimit = (int)(TwoGiB / Unsafe.SizeOf()); } } diff --git a/external/corefx/src/System.Memory/tests/Span/Reverse.cs b/external/corefx/src/System.Memory/tests/Span/Reverse.cs new file mode 100644 index 0000000000..7123f0869d --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Span/Reverse.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. +// See the LICENSE file in the project root for more information. + +using Xunit; +using System.Runtime.CompilerServices; +using static System.TestHelpers; + +namespace System.SpanTests +{ + public static partial class SpanTests + { + [Fact] + public static void ReverseEmpty() + { + var span = Span.Empty; + span.Reverse(); + + byte[] actual = { 1, 2, 3, 4 }; + byte[] expected = { 1, 2, 3, 4 }; + + span = actual; + span.Slice(2, 0).Reverse(); + + Assert.Equal(expected, span.ToArray()); + } + + [Fact] + public static void ReverseEmptyWithReference() + { + var span = Span.Empty; + span.Reverse(); + + string[] actual = { "a", "b", "c", "d" }; + string[] expected = { "a", "b", "c", "d" }; + + span = actual; + span.Slice(2, 0).Reverse(); + + Assert.Equal(expected, span.ToArray()); + } + + [Fact] + public static void ReverseByte() + { + for(int length = 0; length < byte.MaxValue; length++) + { + var actual = new byte[length]; + for (int i = 0; i < length; i++) + { + actual[i] = (byte)i; + } + byte[] expected = new byte[length]; + Array.Copy(actual, expected, length); + Array.Reverse(expected); + + var span = new Span(actual); + span.Reverse(); + Assert.Equal(expected, actual); + } + } + + [Fact] + public static void ReverseByteTwiceReturnsOriginal() + { + byte[] actual = { 1, 2, 3, 4, 5 }; + byte[] expected = { 1, 2, 3, 4, 5 }; + + Span span = actual; + span.Reverse(); + span.Reverse(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void ReverseByteUnaligned() + { + const int length = 32; + const int offset = 1; + var actualFull = new byte[length]; + for (int i = 0; i < length; i++) + { + actualFull[i] = (byte)i; + } + byte[] expectedFull = new byte[length]; + Array.Copy(actualFull, expectedFull, length); + Array.Reverse(expectedFull, offset, length - offset - 1); + + var expectedSpan = new Span(expectedFull, offset, length - offset - 1); + var actualSpan = new Span(actualFull, offset, length - offset - 1); + actualSpan.Reverse(); + + byte[] actual = actualSpan.ToArray(); + byte[] expected = expectedSpan.ToArray(); + Assert.Equal(expected, actual); + Assert.Equal(expectedFull[0], actualFull[0]); + Assert.Equal(expectedFull[length - 1], actualFull[length - 1]); + } + + [Fact] + public static void ReverseIntPtrOffset() + { + const int length = 32; + const int offset = 2; + var actualFull = new IntPtr[length]; + for (int i = 0; i < length; i++) + { + actualFull[i] = IntPtr.Zero + i; + } + IntPtr[] expectedFull = new IntPtr[length]; + Array.Copy(actualFull, expectedFull, length); + Array.Reverse(expectedFull, offset, length - offset); + + var expectedSpan = new Span(expectedFull, offset, length - offset); + var actualSpan = new Span(actualFull, offset, length - offset); + actualSpan.Reverse(); + + IntPtr[] actual = actualSpan.ToArray(); + IntPtr[] expected = expectedSpan.ToArray(); + Assert.Equal(expected, actual); + Assert.Equal(expectedFull[0], actualFull[0]); + Assert.Equal(expectedFull[1], actualFull[1]); + } + + [Fact] + public static void ReverseValueTypeWithoutReferences() + { + const int length = 2048; + int[] actual = new int[length]; + for (int i = 0; i < length; i++) + { + actual[i] = i; + } + int[] expected = new int[length]; + Array.Copy(actual, expected, length); + Array.Reverse(expected); + + var span = new Span(actual); + span.Reverse(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void ReverseValueTypeWithoutReferencesPointerSize() + { + const int length = 15; + long[] actual = new long[length]; + for (int i = 0; i < length; i++) + { + actual[i] = i; + } + long[] expected = new long[length]; + Array.Copy(actual, expected, length); + Array.Reverse(expected); + + var span = new Span(actual); + span.Reverse(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void ReverseReferenceType() + { + const int length = 2048; + string[] actual = new string[length]; + for (int i = 0; i < length; i++) + { + actual[i] = i.ToString(); + } + string[] expected = new string[length]; + Array.Copy(actual, expected, length); + Array.Reverse(expected); + + var span = new Span(actual); + span.Reverse(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void ReverseReferenceTwiceReturnsOriginal() + { + string[] actual = { "a1", "b2", "c3" }; + string[] expected = { "a1", "b2", "c3" }; + + var span = new Span(actual); + span.Reverse(); + span.Reverse(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void ReverseEnumType() + { + TestEnum[] actual = { TestEnum.e0, TestEnum.e1, TestEnum.e2 }; + TestEnum[] expected = { TestEnum.e2, TestEnum.e1, TestEnum.e0 }; + + var span = new Span(actual); + span.Reverse(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void ReverseValueTypeWithReferences() + { + TestValueTypeWithReference[] actual = { + new TestValueTypeWithReference() { I = 1, S = "a" }, + new TestValueTypeWithReference() { I = 2, S = "b" }, + new TestValueTypeWithReference() { I = 3, S = "c" } }; + TestValueTypeWithReference[] expected = { + new TestValueTypeWithReference() { I = 3, S = "c" }, + new TestValueTypeWithReference() { I = 2, S = "b" }, + new TestValueTypeWithReference() { I = 1, S = "a" } }; + + var span = new Span(actual); + span.Reverse(); + Assert.Equal(expected, actual); + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Span/SequenceCompareTo.T.cs b/external/corefx/src/System.Memory/tests/Span/SequenceCompareTo.T.cs new file mode 100644 index 0000000000..493b4e2e2f --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Span/SequenceCompareTo.T.cs @@ -0,0 +1,177 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class SpanTests + { + [Fact] + public static void ZeroLengthSequenceCompareTo() + { + int[] a = new int[3]; + + Span first = new Span(a, 1, 0); + Span second = new Span(a, 2, 0); + int result = first.SequenceCompareTo(second); + Assert.Equal(0, result); + } + + [Fact] + public static void SameSpanSequenceCompareTo() + { + int[] a = { 4, 5, 6 }; + Span span = new Span(a); + int result = span.SequenceCompareTo(span); + Assert.Equal(0, result); + } + + [Fact] + public static void LengthMismatchSequenceCompareTo() + { + int[] a = { 4, 5, 6 }; + Span first = new Span(a, 0, 2); + Span second = new Span(a, 0, 3); + int result = first.SequenceCompareTo(second); + Assert.True(result < 0); + + result = second.SequenceCompareTo(first); + Assert.True(result > 0); + + // one sequence is empty + first = new Span(a, 1, 0); + + result = first.SequenceCompareTo(second); + Assert.True(result < 0); + + result = second.SequenceCompareTo(first); + Assert.True(result > 0); + } + + [Fact] + public static void OnSequenceCompareToOfEqualSpansMakeSureEveryElementIsCompared() + { + for (int length = 0; length < 100; length++) + { + TIntLog log = new TIntLog(); + + TInt[] first = new TInt[length]; + TInt[] second = new TInt[length]; + for (int i = 0; i < length; i++) + { + first[i] = second[i] = new TInt(10 * (i + 1), log); + } + + Span firstSpan = new Span(first); + ReadOnlySpan secondSpan = new ReadOnlySpan(second); + int result = firstSpan.SequenceCompareTo(secondSpan); + Assert.Equal(0, result); + + // Make sure each element of the array was compared once. (Strictly speaking, it would not be illegal for + // SequenceCompareTo to compare an element more than once but that would be a non-optimal implementation and + // a red flag. So we'll stick with the stricter test.) + Assert.Equal(first.Length, log.Count); + foreach (TInt elem in first) + { + int numCompares = log.CountCompares(elem.Value, elem.Value); + Assert.True(numCompares == 1, $"Expected {numCompares} == 1 for element {elem.Value}."); + } + } + } + + [Fact] + public static void SequenceCompareToSingleMismatch() + { + for (int length = 1; length < 32; length++) + { + for (int mismatchIndex = 0; mismatchIndex < length; mismatchIndex++) + { + TIntLog log = new TIntLog(); + + TInt[] first = new TInt[length]; + TInt[] second = new TInt[length]; + for (int i = 0; i < length; i++) + { + first[i] = second[i] = new TInt(10 * (i + 1), log); + } + + second[mismatchIndex] = new TInt(second[mismatchIndex].Value + 1, log); + + Span firstSpan = new Span(first); + ReadOnlySpan secondSpan = new ReadOnlySpan(second); + int result = firstSpan.SequenceCompareTo(secondSpan); + Assert.True(result < 0); + Assert.Equal(1, log.CountCompares(first[mismatchIndex].Value, second[mismatchIndex].Value)); + + result = secondSpan.SequenceCompareTo(firstSpan); // adds to log.CountCompares + Assert.True(result > 0); + Assert.Equal(2, log.CountCompares(first[mismatchIndex].Value, second[mismatchIndex].Value)); + } + } + } + + [Fact] + public static void SequenceCompareToNoMatch() + { + for (int length = 1; length < 32; length++) + { + TIntLog log = new TIntLog(); + + TInt[] first = new TInt[length]; + TInt[] second = new TInt[length]; + + for (int i = 0; i < length; i++) + { + first[i] = new TInt(i + 1, log); + second[i] = new TInt(length + i + 1, log); + } + + Span firstSpan = new Span(first); + ReadOnlySpan secondSpan = new ReadOnlySpan(second); + int result = firstSpan.SequenceCompareTo(secondSpan); + Assert.True(result < 0); + Assert.Equal(1, log.CountCompares(firstSpan[0].Value, secondSpan[0].Value)); + + result = secondSpan.SequenceCompareTo(firstSpan); // adds to log.CountCompares + Assert.True(result > 0); + Assert.Equal(2, log.CountCompares(firstSpan[0].Value, secondSpan[0].Value)); + } + } + + [Fact] + public static void MakeSureNoSequenceCompareToChecksGoOutOfRange() + { + const int GuardValue = 77777; + const int GuardLength = 50; + + Action checkForOutOfRangeAccess = + delegate (int x, int y) + { + if (x == GuardValue || y == GuardValue) + throw new Exception("Detected out of range access in IndexOf()"); + }; + + for (int length = 0; length < 100; length++) + { + TInt[] first = new TInt[GuardLength + length + GuardLength]; + TInt[] second = new TInt[GuardLength + length + GuardLength]; + for (int i = 0; i < first.Length; i++) + { + first[i] = second[i] = new TInt(GuardValue, checkForOutOfRangeAccess); + } + + for (int i = 0; i < length; i++) + { + first[GuardLength + i] = second[GuardLength + i] = new TInt(10 * (i + 1), checkForOutOfRangeAccess); + } + + Span firstSpan = new Span(first, GuardLength, length); + Span secondSpan = new Span(second, GuardLength, length); + int result = firstSpan.SequenceCompareTo(secondSpan); + Assert.Equal(0, result); + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Span/SequenceCompareTo.byte.cs b/external/corefx/src/System.Memory/tests/Span/SequenceCompareTo.byte.cs new file mode 100644 index 0000000000..4eab6a68ef --- /dev/null +++ b/external/corefx/src/System.Memory/tests/Span/SequenceCompareTo.byte.cs @@ -0,0 +1,143 @@ +// 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 Xunit; + +namespace System.SpanTests +{ + public static partial class SpanTests + { + [Fact] + public static void ZeroLengthSequenceCompareTo_Byte() + { + byte[] a = new byte[3]; + + Span first = new Span(a, 1, 0); + Span second = new Span(a, 2, 0); + int result = first.SequenceCompareTo(second); + Assert.Equal(0, result); + } + + [Fact] + public static void SameSpanSequenceCompareTo_Byte() + { + byte[] a = { 4, 5, 6 }; + Span span = new Span(a); + int result = span.SequenceCompareTo(span); + Assert.Equal(0, result); + } + + [Fact] + public static void SequenceCompareToArrayImplicit_Byte() + { + byte[] a = { 4, 5, 6 }; + Span first = new Span(a, 0, 3); + int result = first.SequenceCompareTo(a); + Assert.Equal(0, result); + } + + [Fact] + public static void SequenceCompareToArraySegmentImplicit_Byte() + { + byte[] src = { 1, 2, 3 }; + byte[] dst = { 5, 1, 2, 3, 10 }; + var segment = new ArraySegment(dst, 1, 3); + + Span first = new Span(src, 0, 3); + int result = first.SequenceCompareTo(segment); + Assert.Equal(0, result); + } + + [Fact] + public static void LengthMismatchSequenceCompareTo_Byte() + { + byte[] a = { 4, 5, 6 }; + Span first = new Span(a, 0, 2); + Span second = new Span(a, 0, 3); + int result = first.SequenceCompareTo(second); + Assert.True(result < 0); + + result = second.SequenceCompareTo(first); + Assert.True(result > 0); + + // one sequence is empty + first = new Span(a, 1, 0); + + result = first.SequenceCompareTo(second); + Assert.True(result < 0); + + result = second.SequenceCompareTo(first); + Assert.True(result > 0); + } + + [Fact] + public static void SequenceCompareToWithSingleMismatch_Byte() + { + for (int length = 1; length < 32; length++) + { + for (int mismatchIndex = 0; mismatchIndex < length; mismatchIndex++) + { + byte[] first = new byte[length]; + byte[] second = new byte[length]; + for (int i = 0; i < length; i++) + { + first[i] = second[i] = (byte)(i + 1); + } + + second[mismatchIndex] = (byte)(second[mismatchIndex] + 1); + + Span firstSpan = new Span(first); + ReadOnlySpan secondSpan = new ReadOnlySpan(second); + int result = firstSpan.SequenceCompareTo(secondSpan); + Assert.True(result < 0); + + result = secondSpan.SequenceCompareTo(firstSpan); + Assert.True(result > 0); + } + } + } + + [Fact] + public static void SequenceCompareToNoMatch_Byte() + { + for (int length = 1; length < 32; length++) + { + byte[] first = new byte[length]; + byte[] second = new byte[length]; + + for (int i = 0; i < length; i++) + { + first[i] = (byte)(i + 1); + second[i] = (byte)(byte.MaxValue - i); + } + + Span firstSpan = new Span(first); + ReadOnlySpan secondSpan = new ReadOnlySpan(second); + int result = firstSpan.SequenceCompareTo(secondSpan); + Assert.True(result < 0); + + result = secondSpan.SequenceCompareTo(firstSpan); + Assert.True(result > 0); + } + } + + [Fact] + public static void MakeSureNoSequenceCompareToChecksGoOutOfRange_Byte() + { + for (int length = 0; length < 100; length++) + { + byte[] first = new byte[length + 2]; + first[0] = 99; + first[length + 1] = 99; + byte[] second = new byte[length + 2]; + second[0] = 100; + second[length + 1] = 100; + Span span1 = new Span(first, 1, length); + ReadOnlySpan span2 = new ReadOnlySpan(second, 1, length); + int result = span1.SequenceCompareTo(span2); + Assert.Equal(0, result); + } + } + } +} diff --git a/external/corefx/src/System.Memory/tests/Span/SequenceEqual.byte.cs b/external/corefx/src/System.Memory/tests/Span/SequenceEqual.byte.cs index 85205875e8..66529fab18 100644 --- a/external/corefx/src/System.Memory/tests/Span/SequenceEqual.byte.cs +++ b/external/corefx/src/System.Memory/tests/Span/SequenceEqual.byte.cs @@ -15,7 +15,7 @@ namespace System.SpanTests Span first = new Span(a, 1, 0); Span second = new Span(a, 2, 0); - bool b = first.SequenceEqual(second); + bool b = first.SequenceEqual(second); Assert.True(b); } @@ -24,7 +24,7 @@ namespace System.SpanTests { byte[] a = { 4, 5, 6 }; Span span = new Span(a); - bool b = span.SequenceEqual(span); + bool b = span.SequenceEqual(span); Assert.True(b); } @@ -33,7 +33,7 @@ namespace System.SpanTests { byte[] a = { 4, 5, 6 }; Span first = new Span(a, 0, 3); - bool b = first.SequenceEqual(a); + bool b = first.SequenceEqual(a); Assert.True(b); } @@ -45,7 +45,7 @@ namespace System.SpanTests var segment = new ArraySegment(dst, 1, 3); Span first = new Span(src, 0, 3); - bool b = first.SequenceEqual(segment); + bool b = first.SequenceEqual(segment); Assert.True(b); } @@ -55,7 +55,7 @@ namespace System.SpanTests byte[] a = { 4, 5, 6 }; Span first = new Span(a, 0, 3); Span second = new Span(a, 0, 2); - bool b = first.SequenceEqual(second); + bool b = first.SequenceEqual(second); Assert.False(b); } @@ -77,7 +77,7 @@ namespace System.SpanTests Span firstSpan = new Span(first); ReadOnlySpan secondSpan = new ReadOnlySpan(second); - bool b = firstSpan.SequenceEqual(secondSpan); + bool b = firstSpan.SequenceEqual(secondSpan); Assert.False(b); } } @@ -96,7 +96,7 @@ namespace System.SpanTests second[length + 1] = 100; Span span1 = new Span(first, 1, length); ReadOnlySpan span2 = new ReadOnlySpan(second, 1, length); - bool b = span1.SequenceEqual(span2); + bool b = span1.SequenceEqual(span2); Assert.True(b); } } diff --git a/external/corefx/src/System.Memory/tests/Span/Slice.cs b/external/corefx/src/System.Memory/tests/Span/Slice.cs index 968f66ca00..2cda00f23a 100644 --- a/external/corefx/src/System.Memory/tests/Span/Slice.cs +++ b/external/corefx/src/System.Memory/tests/Span/Slice.cs @@ -4,6 +4,7 @@ using Xunit; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.SpanTests { @@ -15,7 +16,7 @@ namespace System.SpanTests int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 }; Span span = new Span(a).Slice(6); Assert.Equal(4, span.Length); - Assert.True(Unsafe.AreSame(ref a[6], ref span.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref a[6], ref MemoryMarshal.GetReference(span))); } [Fact] @@ -24,7 +25,7 @@ namespace System.SpanTests int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 }; Span span = new Span(a).Slice(a.Length); Assert.Equal(0, span.Length); - Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref span.DangerousGetPinnableReference(), 1))); + Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref MemoryMarshal.GetReference(span), 1))); } [Fact] @@ -33,7 +34,7 @@ namespace System.SpanTests int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 }; Span span = new Span(a).Slice(3, 5); Assert.Equal(5, span.Length); - Assert.True(Unsafe.AreSame(ref a[3], ref span.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref a[3], ref MemoryMarshal.GetReference(span))); } [Fact] @@ -42,7 +43,7 @@ namespace System.SpanTests int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 }; Span span = new Span(a).Slice(4, 6); Assert.Equal(6, span.Length); - Assert.True(Unsafe.AreSame(ref a[4], ref span.DangerousGetPinnableReference())); + Assert.True(Unsafe.AreSame(ref a[4], ref MemoryMarshal.GetReference(span))); } [Fact] @@ -51,7 +52,7 @@ namespace System.SpanTests int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 }; Span span = new Span(a).Slice(a.Length, 0); Assert.Equal(0, span.Length); - Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref span.DangerousGetPinnableReference(), 1))); + Assert.True(Unsafe.AreSame(ref a[a.Length - 1], ref Unsafe.Subtract(ref MemoryMarshal.GetReference(span), 1))); } [Fact] diff --git a/external/corefx/src/System.Memory/tests/Span/StartsWith.byte.cs b/external/corefx/src/System.Memory/tests/Span/StartsWith.byte.cs index 4e86dcef43..8877780058 100644 --- a/external/corefx/src/System.Memory/tests/Span/StartsWith.byte.cs +++ b/external/corefx/src/System.Memory/tests/Span/StartsWith.byte.cs @@ -15,7 +15,7 @@ namespace System.SpanTests Span span = new Span(a); ReadOnlySpan slice = new ReadOnlySpan(a, 2, 0); - bool b = span.StartsWith(slice); + bool b = span.StartsWith(slice); Assert.True(b); } @@ -24,7 +24,7 @@ namespace System.SpanTests { byte[] a = { 4, 5, 6 }; Span span = new Span(a); - bool b = span.StartsWith(span); + bool b = span.StartsWith(span); Assert.True(b); } @@ -34,7 +34,7 @@ namespace System.SpanTests byte[] a = { 4, 5, 6 }; Span span = new Span(a, 0, 2); ReadOnlySpan slice = new ReadOnlySpan(a, 0, 3); - bool b = span.StartsWith(slice); + bool b = span.StartsWith(slice); Assert.False(b); } @@ -44,7 +44,7 @@ namespace System.SpanTests byte[] a = { 4, 5, 6 }; Span span = new Span(a, 0, 3); ReadOnlySpan slice = new ReadOnlySpan(a, 0, 2); - bool b = span.StartsWith(slice); + bool b = span.StartsWith(slice); Assert.True(b); } @@ -55,7 +55,7 @@ namespace System.SpanTests byte[] b = { 4, 5, 6 }; Span span = new Span(a, 0, 3); ReadOnlySpan slice = new ReadOnlySpan(b, 0, 3); - bool c = span.StartsWith(slice); + bool c = span.StartsWith(slice); Assert.True(c); } @@ -77,7 +77,7 @@ namespace System.SpanTests Span firstSpan = new Span(first); ReadOnlySpan secondSpan = new ReadOnlySpan(second); - bool b = firstSpan.StartsWith(secondSpan); + bool b = firstSpan.StartsWith(secondSpan); Assert.False(b); } } @@ -96,7 +96,7 @@ namespace System.SpanTests second[length + 1] = 100; Span span1 = new Span(first, 1, length); ReadOnlySpan span2 = new ReadOnlySpan(second, 1, length); - bool b = span1.StartsWith(span2); + bool b = span1.StartsWith(span2); Assert.True(b); } } diff --git a/external/corefx/src/System.Memory/tests/System.Memory.Tests.csproj b/external/corefx/src/System.Memory/tests/System.Memory.Tests.csproj index 28cffcde3f..df4a91e183 100644 --- a/external/corefx/src/System.Memory/tests/System.Memory.Tests.csproj +++ b/external/corefx/src/System.Memory/tests/System.Memory.Tests.csproj @@ -10,6 +10,7 @@ + @@ -22,19 +23,35 @@ - + + + + + + + + + + + + + + + + + @@ -46,23 +63,38 @@ + - + + + + + + + + + + + + + + + @@ -72,6 +104,8 @@ + + @@ -83,13 +117,19 @@ + + + + + + + - @@ -97,11 +137,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Memory/tests/TInt.cs b/external/corefx/src/System.Memory/tests/TInt.cs index e6dba18fe8..87942abe8e 100644 --- a/external/corefx/src/System.Memory/tests/TInt.cs +++ b/external/corefx/src/System.Memory/tests/TInt.cs @@ -10,10 +10,10 @@ using System.Linq; namespace System { // A wrapped integer that invokes a custom delegate every time IEquatable.Equals() is invoked. - internal struct TInt : IEquatable + internal struct TInt : IEquatable, IComparable { public TInt(int value) - : this(value, (Action)null) + : this(value, (Action)null) { // This constructor does not report comparisons but is still useful for catching uses of the boxing Equals(). } @@ -39,6 +39,15 @@ namespace System return Value == other.Value; } + public int CompareTo(TInt other) + { + if (_onCompare != null) + { + _onCompare(Value, other.Value); + } + return Value.CompareTo(other.Value); + } + [Obsolete("Don't call this. Call IEquatable.Equals(T)")] public override bool Equals(object obj) { diff --git a/external/corefx/src/System.Memory/tests/TestException.cs b/external/corefx/src/System.Memory/tests/TestException.cs new file mode 100644 index 0000000000..23081af2c2 --- /dev/null +++ b/external/corefx/src/System.Memory/tests/TestException.cs @@ -0,0 +1,20 @@ +// 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; + +// +// Intentionally placed in the global namespace so that we don't have to see an annoying long type name +// in the xunit output. +// +internal sealed class TestException : Exception +{ + public TestException(string message) + : base(message) + { + // This explicit exception type exists mostly so we can do the one useful thing that we can't do with downloaded xunit binaries: + // put a BREAKPOINT to catch a test failure. + } +} + diff --git a/external/corefx/src/System.Memory/tests/TestHelpers.cs b/external/corefx/src/System.Memory/tests/TestHelpers.cs index 8c40b3039f..a4829d7fc3 100644 --- a/external/corefx/src/System.Memory/tests/TestHelpers.cs +++ b/external/corefx/src/System.Memory/tests/TestHelpers.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using Xunit; +using System.Collections.Generic; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; @@ -30,10 +31,18 @@ namespace System AssertThrows(span, (_span) => ignore = _span[expected.Length]); } + public static unsafe void ValidateNonNullEmpty(this Span span) + { + Assert.True(span.IsEmpty); + + // Validate that empty Span is not normalized to null + Assert.True(Unsafe.AsPointer(ref MemoryMarshal.GetReference(span)) != null); + } + public delegate void AssertThrowsAction(Span span); // Cannot use standard Assert.Throws() when testing Span - Span and closures don't get along. - public static void AssertThrows(Span span, AssertThrowsAction action) where E:Exception + public static void AssertThrows(Span span, AssertThrowsAction action) where E : Exception { try { @@ -86,10 +95,18 @@ namespace System AssertThrows(span, (_span) => ignore = _span[expected.Length]); } + public static unsafe void ValidateNonNullEmpty(this ReadOnlySpan span) + { + Assert.True(span.IsEmpty); + + // Validate that empty Span is not normalized to null + Assert.True(Unsafe.AsPointer(ref MemoryMarshal.GetReference(span)) != null); + } + public delegate void AssertThrowsActionReadOnly(ReadOnlySpan span); // Cannot use standard Assert.Throws() when testing Span - Span and closures don't get along. - public static void AssertThrows(ReadOnlySpan span, AssertThrowsActionReadOnly action) where E:Exception + public static void AssertThrows(ReadOnlySpan span, AssertThrowsActionReadOnly action) where E : Exception { try { @@ -163,7 +180,7 @@ namespace System span.Clear(); } - public static TestStructExplicit testExplicitStruct = new TestStructExplicit + public static TestStructExplicit s_testExplicitStruct = new TestStructExplicit { S0 = short.MaxValue, I0 = int.MaxValue, @@ -183,18 +200,18 @@ namespace System { Span spanBE = new byte[Unsafe.SizeOf()]; - WriteInt16BigEndian(spanBE, testExplicitStruct.S0); - WriteInt32BigEndian(spanBE.Slice(2), testExplicitStruct.I0); - WriteInt64BigEndian(spanBE.Slice(6), testExplicitStruct.L0); - WriteUInt16BigEndian(spanBE.Slice(14), testExplicitStruct.US0); - WriteUInt32BigEndian(spanBE.Slice(16), testExplicitStruct.UI0); - WriteUInt64BigEndian(spanBE.Slice(20), testExplicitStruct.UL0); - WriteInt16BigEndian(spanBE.Slice(28), testExplicitStruct.S1); - WriteInt32BigEndian(spanBE.Slice(30), testExplicitStruct.I1); - WriteInt64BigEndian(spanBE.Slice(34), testExplicitStruct.L1); - WriteUInt16BigEndian(spanBE.Slice(42), testExplicitStruct.US1); - WriteUInt32BigEndian(spanBE.Slice(44), testExplicitStruct.UI1); - WriteUInt64BigEndian(spanBE.Slice(48), testExplicitStruct.UL1); + WriteInt16BigEndian(spanBE, s_testExplicitStruct.S0); + WriteInt32BigEndian(spanBE.Slice(2), s_testExplicitStruct.I0); + WriteInt64BigEndian(spanBE.Slice(6), s_testExplicitStruct.L0); + WriteUInt16BigEndian(spanBE.Slice(14), s_testExplicitStruct.US0); + WriteUInt32BigEndian(spanBE.Slice(16), s_testExplicitStruct.UI0); + WriteUInt64BigEndian(spanBE.Slice(20), s_testExplicitStruct.UL0); + WriteInt16BigEndian(spanBE.Slice(28), s_testExplicitStruct.S1); + WriteInt32BigEndian(spanBE.Slice(30), s_testExplicitStruct.I1); + WriteInt64BigEndian(spanBE.Slice(34), s_testExplicitStruct.L1); + WriteUInt16BigEndian(spanBE.Slice(42), s_testExplicitStruct.US1); + WriteUInt32BigEndian(spanBE.Slice(44), s_testExplicitStruct.UI1); + WriteUInt64BigEndian(spanBE.Slice(48), s_testExplicitStruct.UL1); Assert.Equal(56, spanBE.Length); return spanBE; @@ -204,23 +221,23 @@ namespace System { Span spanLE = new byte[Unsafe.SizeOf()]; - WriteInt16LittleEndian(spanLE, testExplicitStruct.S0); - WriteInt32LittleEndian(spanLE.Slice(2), testExplicitStruct.I0); - WriteInt64LittleEndian( spanLE.Slice(6), testExplicitStruct.L0); - WriteUInt16LittleEndian(spanLE.Slice(14), testExplicitStruct.US0); - WriteUInt32LittleEndian(spanLE.Slice(16), testExplicitStruct.UI0); - WriteUInt64LittleEndian(spanLE.Slice(20), testExplicitStruct.UL0); - WriteInt16LittleEndian(spanLE.Slice(28), testExplicitStruct.S1); - WriteInt32LittleEndian(spanLE.Slice(30), testExplicitStruct.I1); - WriteInt64LittleEndian(spanLE.Slice(34), testExplicitStruct.L1); - WriteUInt16LittleEndian(spanLE.Slice(42), testExplicitStruct.US1); - WriteUInt32LittleEndian(spanLE.Slice(44), testExplicitStruct.UI1); - WriteUInt64LittleEndian(spanLE.Slice(48), testExplicitStruct.UL1); + WriteInt16LittleEndian(spanLE, s_testExplicitStruct.S0); + WriteInt32LittleEndian(spanLE.Slice(2), s_testExplicitStruct.I0); + WriteInt64LittleEndian(spanLE.Slice(6), s_testExplicitStruct.L0); + WriteUInt16LittleEndian(spanLE.Slice(14), s_testExplicitStruct.US0); + WriteUInt32LittleEndian(spanLE.Slice(16), s_testExplicitStruct.UI0); + WriteUInt64LittleEndian(spanLE.Slice(20), s_testExplicitStruct.UL0); + WriteInt16LittleEndian(spanLE.Slice(28), s_testExplicitStruct.S1); + WriteInt32LittleEndian(spanLE.Slice(30), s_testExplicitStruct.I1); + WriteInt64LittleEndian(spanLE.Slice(34), s_testExplicitStruct.L1); + WriteUInt16LittleEndian(spanLE.Slice(42), s_testExplicitStruct.US1); + WriteUInt32LittleEndian(spanLE.Slice(44), s_testExplicitStruct.UI1); + WriteUInt64LittleEndian(spanLE.Slice(48), s_testExplicitStruct.UL1); Assert.Equal(56, spanLE.Length); return spanLE; } - + [StructLayout(LayoutKind.Explicit)] public struct TestStructExplicit { @@ -276,6 +293,79 @@ namespace System e3, e4, } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void DoNotIgnore(T value, int consumed) + { + } + + // + // { text, start, length } triplets. A "-1" in start or length means "test the overload that doesn't have that parameter." + // + public static IEnumerable StringSliceTestData + { + get + { + foreach (string text in new string[] { string.Empty, "012" }) + { + yield return new object[] { text, -1, -1 }; + for (int start = 0; start <= text.Length; start++) + { + yield return new object[] { text, start, -1 }; + + for (int length = 0; length <= text.Length - start; length++) + { + yield return new object[] { text, start, length }; + } + } + } + } + } + + public static IEnumerable StringSlice2ArgTestOutOfRangeData + { + get + { + foreach (string text in new string[] { string.Empty, "012" }) + { + yield return new object[] { text, -1 }; + yield return new object[] { text, int.MinValue }; + + yield return new object[] { text, text.Length + 1 }; + yield return new object[] { text, int.MaxValue }; + } + } + } + + public static IEnumerable StringSlice3ArgTestOutOfRangeData + { + get + { + foreach (string text in new string[] { string.Empty, "012" }) + { + yield return new object[] { text, -1, 0 }; + yield return new object[] { text, int.MinValue, 0 }; + + yield return new object[] { text, text.Length + 1, 0 }; + yield return new object[] { text, int.MaxValue, 0 }; + + yield return new object[] { text, 0, -1 }; + yield return new object[] { text, 0, int.MinValue }; + + yield return new object[] { text, 0, text.Length + 1 }; + yield return new object[] { text, 0, int.MaxValue }; + + yield return new object[] { text, 1, text.Length }; + yield return new object[] { text, 1, int.MaxValue }; + + yield return new object[] { text, text.Length - 1, 2 }; + yield return new object[] { text, text.Length - 1, int.MaxValue }; + + yield return new object[] { text, text.Length, 1 }; + yield return new object[] { text, text.Length, int.MaxValue }; + } + } + } } } diff --git a/external/corefx/src/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs b/external/corefx/src/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs index fb4ea89503..aab6a125c8 100644 --- a/external/corefx/src/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs +++ b/external/corefx/src/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs @@ -1333,16 +1333,11 @@ namespace System.Net.Http 0, state.ToIntPtr())) { - int lastError = Marshal.GetLastWin32Error(); - Debug.Assert((unchecked((int)lastError) != Interop.WinHttp.ERROR_INSUFFICIENT_BUFFER && - unchecked((int)lastError) != unchecked((int)0x80090321)), // SEC_E_BUFFER_TOO_SMALL - $"Unexpected async error in WinHttpRequestCallback: {unchecked((int)lastError)}"); - // Dispose (which will unpin) the state object. Since this failed, WinHTTP won't associate // our context value (state object) to the request handle. And thus we won't get HANDLE_CLOSING // notifications which would normally cause the state object to be unpinned and disposed. state.Dispose(); - throw WinHttpException.CreateExceptionUsingError(lastError); + WinHttpException.ThrowExceptionUsingLastError(); } } diff --git a/external/corefx/src/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestCallback.cs b/external/corefx/src/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestCallback.cs index 7bea4857cb..995f306435 100644 --- a/external/corefx/src/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestCallback.cs +++ b/external/corefx/src/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestCallback.cs @@ -317,10 +317,6 @@ namespace System.Net.Http Debug.Assert(state != null, "OnRequestError: state is null"); - Debug.Assert((unchecked((int)asyncResult.dwError) != Interop.WinHttp.ERROR_INSUFFICIENT_BUFFER && - unchecked((int)asyncResult.dwError) != unchecked((int)0x80090321)), // SEC_E_BUFFER_TOO_SMALL - $"Unexpected async error in WinHttpRequestCallback: {unchecked((int)asyncResult.dwError)}, WinHttp API: {unchecked((uint)asyncResult.dwResult.ToInt32())}"); - Exception innerException = WinHttpException.CreateExceptionUsingError(unchecked((int)asyncResult.dwError)); switch (unchecked((uint)asyncResult.dwResult.ToInt32())) diff --git a/external/corefx/src/System.Net.Http/ref/System.Net.Http.cs b/external/corefx/src/System.Net.Http/ref/System.Net.Http.cs index c4ad870c81..5bfa47a7e3 100644 --- a/external/corefx/src/System.Net.Http/ref/System.Net.Http.cs +++ b/external/corefx/src/System.Net.Http/ref/System.Net.Http.cs @@ -62,6 +62,10 @@ namespace System.Net.Http public System.Threading.Tasks.Task GetStreamAsync(System.Uri requestUri) { throw null; } public System.Threading.Tasks.Task GetStringAsync(string requestUri) { throw null; } public System.Threading.Tasks.Task GetStringAsync(System.Uri requestUri) { throw null; } + public System.Threading.Tasks.Task PatchAsync(string requestUri, System.Net.Http.HttpContent content) { throw null; } + public System.Threading.Tasks.Task PatchAsync(string requestUri, System.Net.Http.HttpContent content, System.Threading.CancellationToken cancellationToken) { throw null; } + public System.Threading.Tasks.Task PatchAsync(System.Uri requestUri, System.Net.Http.HttpContent content) { throw null; } + public System.Threading.Tasks.Task PatchAsync(System.Uri requestUri, System.Net.Http.HttpContent content, System.Threading.CancellationToken cancellationToken) { throw null; } public System.Threading.Tasks.Task PostAsync(string requestUri, System.Net.Http.HttpContent content) { throw null; } public System.Threading.Tasks.Task PostAsync(string requestUri, System.Net.Http.HttpContent content, System.Threading.CancellationToken cancellationToken) { throw null; } public System.Threading.Tasks.Task PostAsync(System.Uri requestUri, System.Net.Http.HttpContent content) { throw null; } @@ -150,6 +154,7 @@ namespace System.Net.Http public static System.Net.Http.HttpMethod Head { get { throw null; } } public string Method { get { throw null; } } public static System.Net.Http.HttpMethod Options { get { throw null; } } + public static System.Net.Http.HttpMethod Patch { get { throw null; } } public static System.Net.Http.HttpMethod Post { get { throw null; } } public static System.Net.Http.HttpMethod Put { get { throw null; } } public static System.Net.Http.HttpMethod Trace { get { throw null; } } diff --git a/external/corefx/src/System.Net.Http/src/Resources/Strings.resx b/external/corefx/src/System.Net.Http/src/Resources/Strings.resx index f73a721dc8..ae1716b441 100644 --- a/external/corefx/src/System.Net.Http/src/Resources/Strings.resx +++ b/external/corefx/src/System.Net.Http/src/Resources/Strings.resx @@ -258,9 +258,6 @@ When using WindowsProxyUsePolicy.UseCustomProxy, the Proxy property must not be null. - - The content length of the request content can't be determined. Either set TransferEncodingChunked to true, load content into buffer, or set MaxRequestContentBufferSize. - The specified value must be greater than {0}. @@ -366,9 +363,6 @@ Client certificate was not found in the personal (\"MY\") certificate store. In UWP, client certificates are only supported if they have been added to that certificate store. - - The response code indicates a redirect, but the 'Location' header is missing. - The maximum number of redirects was exceeded. @@ -387,4 +381,10 @@ An attempt was made to move the position before the beginning of the stream. - + + The application protocol list is invalid. + + + The path '{0}' is too long, or a component of the specified path is too long. + + \ No newline at end of file diff --git a/external/corefx/src/System.Net.Http/src/System.Net.Http.csproj b/external/corefx/src/System.Net.Http/src/System.Net.Http.csproj index b048009e70..a0d6e505a1 100644 --- a/external/corefx/src/System.Net.Http/src/System.Net.Http.csproj +++ b/external/corefx/src/System.Net.Http/src/System.Net.Http.csproj @@ -120,8 +120,7 @@ - - + diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/ByteArrayHelpers.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/ByteArrayHelpers.cs index 0105cc7e18..8ae15c6ce6 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/ByteArrayHelpers.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/ByteArrayHelpers.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Runtime.InteropServices; using System.Text; namespace System @@ -44,7 +45,7 @@ namespace System internal static unsafe string GetStringFromByteSpan(ReadOnlySpan bytes) { // TODO #22431: Use new Span-based Encoding overload when available - fixed (byte* p = &bytes.DangerousGetPinnableReference()) + fixed (byte* p = &MemoryMarshal.GetReference(bytes)) { return Encoding.ASCII.GetString(p, bytes.Length); } diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/Headers/HeaderDescriptor.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/Headers/HeaderDescriptor.cs index 3737e44709..16b268a73e 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/Headers/HeaderDescriptor.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/Headers/HeaderDescriptor.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. @@ -10,7 +10,7 @@ namespace System.Net.Http.Headers // if the header is one of our known headers, then it contains a reference to the KnownHeader object; // otherwise, for custom headers, it just contains a string for the header name. // Use HeaderDescriptor.TryGet to resolve an arbitrary header name to a HeaderDescriptor. - internal struct HeaderDescriptor : IEquatable + internal readonly struct HeaderDescriptor : IEquatable { private readonly string _headerName; private readonly KnownHeader _knownHeader; diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/Headers/HttpHeaderValueCollection.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/Headers/HttpHeaderValueCollection.cs index a8e7284f3f..14ed82bcac 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/Headers/HttpHeaderValueCollection.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/Headers/HttpHeaderValueCollection.cs @@ -11,21 +11,21 @@ namespace System.Net.Http.Headers // This type is used for headers supporting a list of values. It essentially just forwards calls to // the actual header-store in HttpHeaders. // - // This type can deal with a so called "special value": The RFC defines some headers which are collection of - // values, but the RFC only defines 1 value, e.g. Transfer-Encoding: chunked, Connection: close, + // This type can deal with a so called "special value": The RFC defines some headers which are collection of + // values, but the RFC only defines 1 value, e.g. Transfer-Encoding: chunked, Connection: close, // Expect: 100-continue. - // We expose strongly typed properties for these special values: TransferEncodingChunked, ConnectionClose, + // We expose strongly typed properties for these special values: TransferEncodingChunked, ConnectionClose, // ExpectContinue. - // So we have 2 properties for each of these headers ('Transfer-Encoding' => TransferEncoding, + // So we have 2 properties for each of these headers ('Transfer-Encoding' => TransferEncoding, // TransferEncodingChunked; 'Connection' => Connection, ConnectionClose; 'Expect' => Expect, ExpectContinue) // // The following solution was chosen: - // - Keep HttpHeaders clean: HttpHeaders is unaware of these "special values"; it just stores the collection of - // headers. + // - Keep HttpHeaders clean: HttpHeaders is unaware of these "special values"; it just stores the collection of + // headers. // - It is the responsibility of "higher level" components (HttpHeaderValueCollection, HttpRequestHeaders, - // HttpResponseHeaders) to deal with special values. + // HttpResponseHeaders) to deal with special values. // - HttpHeaderValueCollection can be configured with an IEqualityComparer and a "special value". - // + // // Example: Server sends header "Transfer-Encoding: gzip, custom, chunked" to the client. // - HttpHeaders: HttpHeaders will have an entry in the header store for "Transfer-Encoding" with values // "gzip", "custom", "chunked" @@ -181,7 +181,7 @@ namespace System.Net.Http.Headers } else { - // We have multiple values. Iterate through the values and return them. + // We have multiple values. Iterate through the values and return them. foreach (object item in storeValues) { Debug.Assert(item is T); @@ -206,15 +206,6 @@ namespace System.Net.Http.Headers return _store.GetHeaderString(_descriptor); } - internal string GetHeaderStringWithoutSpecial() - { - if (!IsSpecialValueSet) - { - return ToString(); - } - return _store.GetHeaderString(_descriptor, _specialValue); - } - internal void SetSpecialValue() { Debug.Assert(_specialValue != null, diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/Headers/KnownHeaders.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/Headers/KnownHeaders.cs index ac209a1fba..2d1d035bf0 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/Headers/KnownHeaders.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/Headers/KnownHeaders.cs @@ -1,8 +1,9 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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.Diagnostics; +using System.Runtime.InteropServices; namespace System.Net.Http.Headers { @@ -101,7 +102,7 @@ namespace System.Net.Http.Headers char this[int index] { get; } } - private struct StringAccessor : IHeaderNameAccessor + private readonly struct StringAccessor : IHeaderNameAccessor { private readonly string _string; @@ -115,7 +116,7 @@ namespace System.Net.Http.Headers } // Can't use Span here as it's unsupported. - private unsafe struct BytePtrAccessor : IHeaderNameAccessor + private unsafe readonly struct BytePtrAccessor : IHeaderNameAccessor { private readonly byte* _p; private readonly int _length; @@ -370,7 +371,7 @@ namespace System.Net.Http.Headers internal unsafe static KnownHeader TryGetKnownHeader(ReadOnlySpan name) { - fixed (byte* p = &name.DangerousGetPinnableReference()) + fixed (byte* p = &MemoryMarshal.GetReference(name)) { KnownHeader candidate = GetCandidate(new BytePtrAccessor(p, name.Length)); if (candidate != null && ByteArrayHelpers.EqualsOrdinalAsciiIgnoreCase(candidate.Name, name)) diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpClient.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpClient.cs index 17ab4f227d..1bcac19a93 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpClient.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpClient.cs @@ -351,6 +351,30 @@ namespace System.Net.Http return SendAsync(request, cancellationToken); } + public Task PatchAsync(string requestUri, HttpContent content) + { + return PatchAsync(CreateUri(requestUri), content); + } + + public Task PatchAsync(Uri requestUri, HttpContent content) + { + return PatchAsync(requestUri, content, CancellationToken.None); + } + + public Task PatchAsync(string requestUri, HttpContent content, + CancellationToken cancellationToken) + { + return PatchAsync(CreateUri(requestUri), content, cancellationToken); + } + + public Task PatchAsync(Uri requestUri, HttpContent content, + CancellationToken cancellationToken) + { + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Patch, requestUri); + request.Content = content; + return SendAsync(request, cancellationToken); + } + public Task DeleteAsync(string requestUri) { return DeleteAsync(CreateUri(requestUri)); diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs index aaadad33e3..119644a254 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs @@ -13,6 +13,7 @@ namespace System.Net.Http { // This partial implementation contains members common to all HttpClientHandler implementations. private const string ManagedHandlerSettingName = "COMPlus_UseManagedHttpClientHandler"; + private const string AppCtxManagedHandlerSettingName = "System.Net.Http.UseManagedHttpClientHandler"; private static LocalDataStoreSlot s_useManagedHandlerSlot; @@ -26,6 +27,11 @@ namespace System.Net.Http return true; } + if (AppContext.TryGetSwitch(AppCtxManagedHandlerSettingName, out bool isManagedEnabled) && isManagedEnabled) + { + return true; + } + // Then check whether a thread local has been set with the same name. // If it's been set to a Boolean true, also use the managed handler. LocalDataStoreSlot slot = LazyInitializer.EnsureInitialized(ref s_useManagedHandlerSlot, () => diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpContent.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpContent.cs index 7f7509509c..191c5f422b 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpContent.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpContent.cs @@ -183,9 +183,9 @@ namespace System.Net.Http internal static string ReadBufferAsString(ArraySegment buffer, HttpContentHeaders headers) { - // We don't validate the Content-Encoding header: If the content was encoded, it's the caller's - // responsibility to make sure to only call ReadAsString() on already decoded content. E.g. if the - // Content-Encoding is 'gzip' the user should set HttpClientHandler.AutomaticDecompression to get a + // We don't validate the Content-Encoding header: If the content was encoded, it's the caller's + // responsibility to make sure to only call ReadAsString() on already decoded content. E.g. if the + // Content-Encoding is 'gzip' the user should set HttpClientHandler.AutomaticDecompression to get a // decoded response stream. Encoding encoding = null; @@ -208,7 +208,7 @@ namespace System.Net.Http } } - // If no content encoding is listed in the ContentType HTTP header, or no Content-Type header present, + // If no content encoding is listed in the ContentType HTTP header, or no Content-Type header present, // then check for a BOM in the data to figure out the encoding. if (encoding == null) { @@ -235,9 +235,9 @@ namespace System.Net.Http internal byte[] ReadBufferedContentAsByteArray() { - // The returned array is exposed out of the library, so use ToArray rather + // The returned array is exposed out of the library, so use ToArray rather // than TryGetBuffer in order to make a copy. - return _bufferedContent.ToArray(); + return _bufferedContent.ToArray(); } public Task ReadAsStreamAsync() @@ -359,7 +359,7 @@ namespace System.Net.Http CheckDisposed(); if (maxBufferSize > HttpContent.MaxBufferSize) { - // This should only be hit when called directly; HttpClient/HttpClientHandler + // This should only be hit when called directly; HttpClient/HttpClientHandler // will not exceed this limit. throw new ArgumentOutOfRangeException(nameof(maxBufferSize), maxBufferSize, string.Format(System.Globalization.CultureInfo.InvariantCulture, @@ -693,8 +693,6 @@ namespace System.Net.Http _maxSize = maxSize; } - public int MaxSize => _maxSize; - public byte[] GetSizedBuffer() { ArraySegment buffer; diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpMethod.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpMethod.cs index 89234ac9d3..8abe00eceb 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpMethod.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpMethod.cs @@ -18,6 +18,7 @@ namespace System.Net.Http private static readonly HttpMethod s_headMethod = new HttpMethod("HEAD"); private static readonly HttpMethod s_optionsMethod = new HttpMethod("OPTIONS"); private static readonly HttpMethod s_traceMethod = new HttpMethod("TRACE"); + private static readonly HttpMethod s_patchMethod = new HttpMethod("PATCH"); // Don't expose CONNECT as static property, since it's used by the transport to connect to a proxy. // CONNECT is not used by users directly. @@ -57,6 +58,11 @@ namespace System.Net.Http get { return s_traceMethod; } } + public static HttpMethod Patch + { + get { return s_patchMethod; } + } + public string Method { get { return _method; } diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpUtilities.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpUtilities.cs index 252e1c3928..6bd04526a1 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpUtilities.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpUtilities.cs @@ -31,46 +31,11 @@ namespace System.Net.Http string.Equals("https", scheme, StringComparison.OrdinalIgnoreCase); } - // Returns true if the task was faulted or canceled and sets tcs accordingly. - internal static bool HandleFaultsAndCancelation(Task task, TaskCompletionSource tcs) - { - Debug.Assert(task.IsCompleted); // Success, faulted, or cancelled - if (task.IsFaulted) - { - tcs.TrySetException(task.Exception.GetBaseException()); - return true; - } - else if (task.IsCanceled) - { - tcs.TrySetCanceled(); - return true; - } - return false; - } - // Always specify TaskScheduler.Default to prevent us from using a user defined TaskScheduler.Current. - // + // // Since we're not doing any CPU and/or I/O intensive operations, continue on the same thread. // This results in better performance since the continuation task doesn't get scheduled by the // scheduler and there are no context switches required. - internal static Task ContinueWithStandard(this Task task, Action continuation) - { - return task.ContinueWith(continuation, CancellationToken.None, - TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); - } - - internal static Task ContinueWithStandard(this Task task, object state, Action continuation) - { - return task.ContinueWith(continuation, state, CancellationToken.None, - TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); - } - - internal static Task ContinueWithStandard(this Task task, Action> continuation) - { - return task.ContinueWith(continuation, CancellationToken.None, - TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); - } - internal static Task ContinueWithStandard(this Task task, object state, Action, object> continuation) { return task.ContinueWith(continuation, state, CancellationToken.None, diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/AuthenticateAndRedirectHandler.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/AuthenticateAndRedirectHandler.cs new file mode 100644 index 0000000000..24f4201eb9 --- /dev/null +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/AuthenticateAndRedirectHandler.cs @@ -0,0 +1,228 @@ +// 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.Diagnostics; +using System.Net.Http.Headers; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Http +{ + internal sealed partial class AuthenticateAndRedirectHandler : HttpMessageHandler + { + private readonly HttpMessageHandler _innerHandler; + private readonly bool _preAuthenticate; + private readonly ICredentials _credentials; + private readonly bool _allowRedirect; + private readonly int _maxAutomaticRedirections; + + public AuthenticateAndRedirectHandler(bool preAuthenticate, ICredentials credentials, bool allowRedirect, int maxAutomaticRedirections, HttpMessageHandler innerHandler) + { + Debug.Assert(innerHandler != null); + + _preAuthenticate = preAuthenticate; + _credentials = credentials; + _allowRedirect = allowRedirect; + + if (allowRedirect && maxAutomaticRedirections < 0) + { + throw new ArgumentOutOfRangeException(nameof(maxAutomaticRedirections)); + } + + _maxAutomaticRedirections = maxAutomaticRedirections; + _innerHandler = innerHandler; + } + + protected internal override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + HttpResponseMessage response; + uint redirectCount = 0; + while (true) + { + // Just as with WinHttpHandler and CurlHandler, for security reasons, we drop the server credential if it is + // anything other than a CredentialCache on redirection. We allow credentials in a CredentialCache since they + // are specifically tied to URIs. + ICredentials currentCredential = redirectCount > 0 ? _credentials as CredentialCache : _credentials; + + if (currentCredential != null && _preAuthenticate) + { + AuthenticationHelper.TrySetBasicAuthToken(request, currentCredential); + } + + response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); + + if (currentCredential != null && response.StatusCode == HttpStatusCode.Unauthorized) + { + AuthenticationHeaderValue selectedAuth = GetSupportedAuthScheme(response.Headers.WwwAuthenticate); + if (selectedAuth != null) + { + switch (selectedAuth.Scheme) + { + case AuthenticationHelper.Digest: + // Update digest response with new parameter from WWWAuthenticate + var digestResponse = new AuthenticationHelper.DigestResponse(selectedAuth.Parameter); + if (await AuthenticationHelper.TrySetDigestAuthToken(request, currentCredential, digestResponse, HttpKnownHeaderNames.Authorization).ConfigureAwait(false)) + { + response.Dispose(); + response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); + + // Retry in case of nonce timeout in server. + if (response.StatusCode == HttpStatusCode.Unauthorized) + { + foreach (AuthenticationHeaderValue ahv in response.Headers.WwwAuthenticate) + { + if (ahv.Scheme == AuthenticationHelper.Digest) + { + digestResponse = new AuthenticationHelper.DigestResponse(ahv.Parameter); + if (AuthenticationHelper.IsServerNonceStale(digestResponse) && + await AuthenticationHelper.TrySetDigestAuthToken(request, currentCredential, digestResponse, HttpKnownHeaderNames.Authorization).ConfigureAwait(false)) + { + response.Dispose(); + response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); + } + + break; + } + } + } + } + break; + + case AuthenticationHelper.Basic: + if (_preAuthenticate) + { + // We already tried these credentials via preauthentication, so no need to try again + break; + } + + if (AuthenticationHelper.TrySetBasicAuthToken(request, currentCredential)) + { + response.Dispose(); + response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); + } + break; + } + } + } + + if (!RequestNeedsRedirect(response)) + { + break; + } + + // Clear the authorization header, if the request requires redirect. + request.Headers.Authorization = null; + + Uri location = response.Headers.Location; + if (location == null) + { + // No location header. Nothing to redirect to. + break; + } + + if (!location.IsAbsoluteUri) + { + location = new Uri(request.RequestUri, location); + } + + // Disallow automatic redirection from secure to non-secure schemes + bool allowed = + (HttpUtilities.IsSupportedNonSecureScheme(request.RequestUri.Scheme) && HttpUtilities.IsSupportedScheme(location.Scheme)) || + (HttpUtilities.IsSupportedSecureScheme(request.RequestUri.Scheme) && HttpUtilities.IsSupportedSecureScheme(location.Scheme)); + if (!allowed) + { + break; + } + + redirectCount++; + if (redirectCount > _maxAutomaticRedirections) + { + throw new HttpRequestException(SR.net_http_max_redirects); + } + + // Set up for the automatic redirect + request.RequestUri = location; + + if (RequestRequiresForceGet(response.StatusCode, request.Method)) + { + request.Method = HttpMethod.Get; + request.Content = null; + } + + // Do the redirect. + response.Dispose(); + } + + return response; + } + + private bool RequestNeedsRedirect(HttpResponseMessage response) + { + // Return if redirect is not requested. + if (!_allowRedirect) + return false; + + bool needRedirect = false; + switch (response.StatusCode) + { + case HttpStatusCode.Moved: + case HttpStatusCode.Found: + case HttpStatusCode.SeeOther: + case HttpStatusCode.TemporaryRedirect: + needRedirect = true; + break; + + case HttpStatusCode.MultipleChoices: + needRedirect = response.Headers.Location != null; // Don't redirect if no Location specified + break; + } + + return needRedirect; + } + + private static bool RequestRequiresForceGet(HttpStatusCode statusCode, HttpMethod requestMethod) + { + if (statusCode == HttpStatusCode.Moved || + statusCode == HttpStatusCode.Found || + statusCode == HttpStatusCode.SeeOther || + statusCode == HttpStatusCode.MultipleChoices) + { + return requestMethod == HttpMethod.Post; + } + + return false; + } + + private static AuthenticationHeaderValue GetSupportedAuthScheme(HttpHeaderValueCollection authenticateValues) + { + AuthenticationHeaderValue basicAuthenticationHeaderValue = null; + + // Only Digest and Basic auth supported, ignore others. + foreach (AuthenticationHeaderValue ahv in authenticateValues) + { + if (ahv.Scheme == AuthenticationHelper.Digest) + { + return ahv; + } + else if (ahv.Scheme == AuthenticationHelper.Basic) + { + basicAuthenticationHeaderValue = ahv; + } + } + + return basicAuthenticationHeaderValue; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _innerHandler.Dispose(); + } + + base.Dispose(disposing); + } + } +} + diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/AuthenticationHandler.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/AuthenticationHandler.cs deleted file mode 100644 index 5ae0562595..0000000000 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/AuthenticationHandler.cs +++ /dev/null @@ -1,119 +0,0 @@ -// 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.Diagnostics; -using System.Net; -using System.Net.Http.Headers; -using System.Threading; -using System.Threading.Tasks; - -namespace System.Net.Http -{ - internal sealed class AuthenticationHandler : HttpMessageHandler - { - private readonly HttpMessageHandler _innerHandler; - private readonly bool _preAuthenticate; - private ICredentials _credentials; - private AuthenticationHelper.DigestResponse _digestResponse; - - public AuthenticationHandler(bool preAuthenticate, ICredentials credentials, HttpMessageHandler innerHandler) - { - Debug.Assert(credentials != null); - Debug.Assert(innerHandler != null); - - _preAuthenticate = preAuthenticate; - _credentials = credentials; - _innerHandler = innerHandler; - } - - protected internal override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - if (_preAuthenticate) - { - // Try using previous digest response WWWAuthenticate header - if (_digestResponse != null) - { - await AuthenticationHelper.TrySetDigestAuthToken(request, _credentials, _digestResponse, HttpKnownHeaderNames.Authorization).ConfigureAwait(false); - } - else - { - AuthenticationHelper.TrySetBasicAuthToken(request, _credentials); - } - } - - HttpResponseMessage response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); - - // In case of redirection, ensure _credentials as CredentialCache - if (AutoRedirectHandler.RequestNeedsRedirect(response)) - { - // Just as with WinHttpHandler and CurlHandler, for security reasons, we drop the server credential if it is - // anything other than a CredentialCache. We allow credentials in a CredentialCache since they - // are specifically tied to URIs. - _credentials = _credentials as CredentialCache; - } - else if (_credentials != null && !_preAuthenticate && response.StatusCode == HttpStatusCode.Unauthorized) - { - HttpHeaderValueCollection authenticateValues = response.Headers.WwwAuthenticate; - - foreach (AuthenticationHeaderValue h in authenticateValues) - { - // We only support Basic and digest auth, ignore others - if (h.Scheme == AuthenticationHelper.Basic) - { - if (AuthenticationHelper.TrySetBasicAuthToken(request, _credentials)) - { - response.Dispose(); - response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); - break; - } - } - else if (h.Scheme == AuthenticationHelper.Digest) - { - // Update digest response with new parameter from WWWAuthenticate - _digestResponse = new AuthenticationHelper.DigestResponse(h.Parameter); - if (await AuthenticationHelper.TrySetDigestAuthToken(request, _credentials, _digestResponse, HttpKnownHeaderNames.Authorization).ConfigureAwait(false)) - { - response.Dispose(); - response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); - - // Retry in case of nonce timeout in server. - if (response.StatusCode == HttpStatusCode.Unauthorized) - { - foreach (AuthenticationHeaderValue ahv in response.Headers.WwwAuthenticate) - { - if (ahv.Scheme == AuthenticationHelper.Digest) - { - _digestResponse = new AuthenticationHelper.DigestResponse(ahv.Parameter); - if (AuthenticationHelper.IsServerNonceStale(_digestResponse) && - await AuthenticationHelper.TrySetDigestAuthToken(request, _credentials, _digestResponse, HttpKnownHeaderNames.Authorization).ConfigureAwait(false)) - { - response.Dispose(); - response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); - } - - break; - } - } - } - - break; - } - } - } - } - - return response; - } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - _innerHandler.Dispose(); - } - - base.Dispose(disposing); - } - } -} diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/AuthenticationHelper.Digest.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/AuthenticationHelper.Digest.cs index 8630562f6d..3f0b911689 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/AuthenticationHelper.Digest.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/AuthenticationHelper.Digest.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Net; using System.Net.Http.Headers; @@ -126,15 +127,8 @@ namespace System.Net.Http if (realm != string.Empty) sb.AppendKeyValue(Realm, realm); - // If nonce is same as previous request, update nonce count. - if (nonce == digestResponse.Nonce) - { - digestResponse.NonceCount++; - } - // Add nonce sb.AppendKeyValue(Nonce, nonce); - digestResponse.Nonce = nonce; // Add uri sb.AppendKeyValue(Uri, request.RequestUri.PathAndQuery); @@ -183,7 +177,7 @@ namespace System.Net.Http string response = ComputeHash(ComputeHash(a1, algorithm) + ":" + nonce + ":" + - digestResponse.NonceCount.ToString("x8") + ":" + + DigestResponse.NonceCount + ":" + cnonce + ":" + qop + ":" + ComputeHash(a2, algorithm), algorithm); @@ -201,7 +195,7 @@ namespace System.Net.Http sb.AppendKeyValue(Qop, qop, includeQuotes: false); // Add nc - sb.AppendKeyValue(NC, digestResponse.NonceCount.ToString("x8"), includeQuotes: false); + sb.AppendKeyValue(NC, DigestResponse.NonceCount, includeQuotes: false); // Add cnonce sb.AppendKeyValue(CNonce, cnonce, includeComma: false); @@ -222,7 +216,7 @@ namespace System.Net.Http s_rng.GetBytes(randomNumbers); StringBuilder sb = StringBuilderCache.Acquire(Length); - for (int i = 0; i < randomNumbers.Length; ) + for (int i = 0; i < randomNumbers.Length;) { // Get a random digit 0-9, a random alphabet in a-z, or a random alphabeta in A-Z int rangeIndex = randomNumbers[i++] % 3; @@ -251,128 +245,144 @@ namespace System.Net.Http } } - public class DigestResponse + internal class DigestResponse { - public readonly Dictionary Parameters = new Dictionary(StringComparer.OrdinalIgnoreCase); + internal readonly Dictionary Parameters = new Dictionary(StringComparer.OrdinalIgnoreCase); + internal const string NonceCount = "00000001"; - // Keep track of request values for this response. - public string Nonce; - public uint NonceCount; - - public DigestResponse(string challenge) + internal DigestResponse(string challenge) { - Nonce = null; - NonceCount = 1; - Parse(challenge); } - private unsafe string GetNextKey(ref char* p) + private static bool CharIsSpaceOrTab(char ch) { - // It is generally cheaper to change a local and then write back to ref at the end - // rather than updating the ref on each operation. - char* temp = p; + return ch == ' ' || ch == '\t'; + } - StringBuilder sb = StringBuilderCache.Acquire(); - - // Skip leading whitespace - while (*temp == ' ') + private string GetNextKey(string data, int currentIndex, out int parsedIndex) + { + // Skip leading space or tab. + while (currentIndex < data.Length && CharIsSpaceOrTab(data[currentIndex])) { - temp++; + currentIndex++; } // Start parsing key - while (*temp != '=') - { - // Key cannot have whitespace - if (*temp == ' ') - break; + int start = currentIndex; - sb.Append(*temp); - temp++; + // Parse till '=' is encountered marking end of key. + // Key cannot contain space or tab, break if either is found. + while (currentIndex < data.Length && data[currentIndex] != '=' && !CharIsSpaceOrTab(data[currentIndex])) + { + currentIndex++; } - // Skip trailing whitespace and '=' - while (*temp == ' ' || *temp == '=') + if (currentIndex == data.Length) { - temp++; + // Key didn't terminate with '=' + parsedIndex = currentIndex; + return null; } - // Set the ref p to temp - p = temp; + // Record end of key. + int length = currentIndex - start; + if (CharIsSpaceOrTab(data[currentIndex])) + { + // Key parsing terminated due to ' ' or '\t'. + // Parse till '=' is found. + while (currentIndex < data.Length && CharIsSpaceOrTab(data[currentIndex])) + { + currentIndex++; + } - return StringBuilderCache.GetStringAndRelease(sb); + if (currentIndex == data.Length || data[currentIndex] != '=') + { + // Key is invalid. + parsedIndex = currentIndex; + return null; + } + } + + // Skip trailing space and tab and '=' + while (currentIndex < data.Length && (CharIsSpaceOrTab(data[currentIndex]) || data[currentIndex] == '=')) + { + currentIndex++; + } + + // Set the parsedIndex to current valid char. + parsedIndex = currentIndex; + return data.Substring(start, length); } - private unsafe string GetNextValue(ref char* p) + private string GetNextValue(string data, int currentIndex, out int parsedIndex) { - // It is generally cheaper to change a local and then write back to ref at the end - // rather than updating the ref on each operation. - char* temp = p; - - StringBuilder sb = StringBuilderCache.Acquire(); - - // Skip leading whitespace - while (*temp == ' ') - { - temp++; - } + Debug.Assert(currentIndex < data.Length && !CharIsSpaceOrTab(data[currentIndex])); // If quoted value, skip first quote. bool quotedValue = false; - if (*temp == '"') + if (data[currentIndex] == '"') { quotedValue = true; - temp++; + currentIndex++; } - while ((quotedValue && *temp != '"') || (!quotedValue && *temp != ',' && *temp != '\0')) + StringBuilder sb = StringBuilderCache.Acquire(); + while (currentIndex < data.Length && ((quotedValue && data[currentIndex] != '"') || (!quotedValue && data[currentIndex] != ','))) { - sb.Append(*temp); - temp++; + sb.Append(data[currentIndex]); + currentIndex++; - if (!quotedValue && *temp == ' ') + if (currentIndex == data.Length) break; - if (quotedValue && *temp == '"' && *(temp - 1) == '\\') + if (!quotedValue && CharIsSpaceOrTab(data[currentIndex])) + break; + + if (quotedValue && data[currentIndex] == '"' && data[currentIndex - 1] == '\\') { // Include the escaped quote. - sb.Append(*temp); - temp++; + sb.Append(data[currentIndex]); + currentIndex++; } } // Return if this is last value. - if (*temp == '\0') - return sb.ToString(); - - // Skip the end quote or ',' or whitespace - temp++; - - // Skip whitespace and , - while (*temp == ' ' || *temp == ',') + if (currentIndex == data.Length) { - temp++; + parsedIndex = currentIndex; + return StringBuilderCache.GetStringAndRelease(sb); } - // Set ref p to temp - p = temp; + // Skip the end quote or ',' or space or tab. + currentIndex++; + // Skip space and tab and , + while (currentIndex < data.Length && (CharIsSpaceOrTab(data[currentIndex]) || data[currentIndex] == ',')) + { + currentIndex++; + } + + // Set parsedIndex to current valid char. + parsedIndex = currentIndex; return StringBuilderCache.GetStringAndRelease(sb); } private unsafe void Parse(string challenge) { - fixed (char* p = challenge) + int parsedIndex = 0; + while (parsedIndex < challenge.Length) { - char* counter = p; - while (*counter != '\0') - { - string key = GetNextKey(ref counter); - string value = GetNextValue(ref counter); + // Get the key. + string key = GetNextKey(challenge, parsedIndex, out parsedIndex); + // Ensure key is not empty and parsedIndex is still in range. + if (string.IsNullOrEmpty(key) || parsedIndex >= challenge.Length) + break; - Parameters.Add(key, value); - } + // Get the value. + string value = GetNextValue(challenge, parsedIndex, out parsedIndex); + // Add the key-value pair to Parameters. + Parameters.Add(key, value); } } } diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/AutoRedirectHandler.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/AutoRedirectHandler.cs deleted file mode 100644 index 193add2fe2..0000000000 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/AutoRedirectHandler.cs +++ /dev/null @@ -1,124 +0,0 @@ -// 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.Threading; -using System.Threading.Tasks; - -namespace System.Net.Http -{ - internal sealed class AutoRedirectHandler : HttpMessageHandler - { - private readonly HttpMessageHandler _innerHandler; - private readonly int _maxAutomaticRedirections; - - public AutoRedirectHandler(int maxAutomaticRedirections, HttpMessageHandler innerHandler) - { - _innerHandler = innerHandler ?? throw new ArgumentNullException(nameof(innerHandler)); - - if (maxAutomaticRedirections < 0) - { - throw new ArgumentOutOfRangeException(nameof(maxAutomaticRedirections)); - } - _maxAutomaticRedirections = maxAutomaticRedirections; - } - - internal static bool RequestNeedsRedirect(HttpResponseMessage response) - { - bool needRedirect = false; - switch (response.StatusCode) - { - case HttpStatusCode.Moved: - case HttpStatusCode.Found: - case HttpStatusCode.SeeOther: - case HttpStatusCode.TemporaryRedirect: - needRedirect = true; - break; - - case HttpStatusCode.MultipleChoices: - needRedirect = response.Headers.Location != null; // Don't redirect if no Location specified - break; - } - - return needRedirect; - } - - private static bool RequestRequiresForceGet(HttpStatusCode statusCode, HttpMethod requestMethod) - { - if (statusCode == HttpStatusCode.Moved || - statusCode == HttpStatusCode.Found || - statusCode == HttpStatusCode.SeeOther || - statusCode == HttpStatusCode.MultipleChoices) - { - return requestMethod == HttpMethod.Post; - } - - return false; - } - - protected internal override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - HttpResponseMessage response; - uint redirectCount = 0; - while (true) - { - response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); - - if (!RequestNeedsRedirect(response)) - { - break; - } - - Uri location = response.Headers.Location; - if (location == null) - { - throw new HttpRequestException(SR.net_http_headers_missing_location); - } - - if (!location.IsAbsoluteUri) - { - location = new Uri(request.RequestUri, location); - } - - // Disallow automatic redirection from https to http - bool allowed = - (request.RequestUri.Scheme == UriScheme.Http && (location.Scheme == UriScheme.Http || location.Scheme == UriScheme.Https)) || - (request.RequestUri.Scheme == UriScheme.Https && location.Scheme == UriScheme.Https); - if (!allowed) - { - break; - } - - redirectCount++; - if (redirectCount > _maxAutomaticRedirections) - { - throw new HttpRequestException(SR.net_http_max_redirects); - } - - // Set up for the automatic redirect - request.RequestUri = location; - - if (RequestRequiresForceGet(response.StatusCode, request.Method)) - { - request.Method = HttpMethod.Get; - request.Content = null; - } - - // Do the redirect. - response.Dispose(); - } - - return response; - } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - _innerHandler.Dispose(); - } - - base.Dispose(disposing); - } - } -} diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/ConnectHelper.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/ConnectHelper.cs index d96ff6547d..3d64d2d660 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/ConnectHelper.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/ConnectHelper.cs @@ -2,31 +2,99 @@ // 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.Diagnostics; using System.IO; using System.Net.Sockets; +using System.Runtime.CompilerServices; +using System.Threading; using System.Threading.Tasks; namespace System.Net.Http { internal static class ConnectHelper { - public static async ValueTask ConnectAsync(string host, int port) + public static async ValueTask ConnectAsync(string host, int port, CancellationToken cancellationToken) { - var socket = new Socket(SocketType.Stream, ProtocolType.Tcp) { NoDelay = true }; try { - // TODO #23151: cancellation support? - await (IPAddress.TryParse(host, out IPAddress address) ? - socket.ConnectAsync(address, port) : - socket.ConnectAsync(host, port)).ConfigureAwait(false); + // Rather than creating a new Socket and calling ConnectAsync on it, we use the static + // Socket.ConnectAsync with a SocketAsyncEventArgs, as we can then use Socket.CancelConnectAsync + // to cancel it if needed. + using (var saea = new BuilderAndCancellationTokenSocketAsyncEventArgs(cancellationToken)) + { + // Configure which server to which to connect. + saea.RemoteEndPoint = IPAddress.TryParse(host, out IPAddress address) ? + (EndPoint)new IPEndPoint(address, port) : + new DnsEndPoint(host, port); + + // Hook up a callback that'll complete the Task when the operation completes. + saea.Completed += (s, e) => + { + var csaea = (BuilderAndCancellationTokenSocketAsyncEventArgs)e; + switch (e.SocketError) + { + case SocketError.Success: + csaea.Builder.SetResult(); + break; + case SocketError.OperationAborted: + case SocketError.ConnectionAborted: + if (cancellationToken.IsCancellationRequested) + { + csaea.Builder.SetException(new OperationCanceledException(csaea.CancellationToken)); + break; + } + goto default; + default: + csaea.Builder.SetException(new SocketException((int)e.SocketError)); + break; + } + }; + + // Initiate the connection. + if (Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, saea)) + { + // Connect completing asynchronously. Enable it to be canceled and wait for it. + using (cancellationToken.Register(s => Socket.CancelConnectAsync((SocketAsyncEventArgs)s), saea)) + { + await saea.Builder.Task.ConfigureAwait(false); + } + } + else if (saea.SocketError != SocketError.Success) + { + // Connect completed synchronously but unsuccessfully. + throw new SocketException((int)saea.SocketError); + } + + Debug.Assert(saea.SocketError == SocketError.Success, $"Expected Success, got {saea.SocketError}."); + Debug.Assert(saea.ConnectSocket != null, "Expected non-null socket"); + Debug.Assert(saea.ConnectSocket.Connected, "Expected socket to be connected"); + + // Configure the socket and return a stream for it. + Socket socket = saea.ConnectSocket; + socket.NoDelay = true; + return new NetworkStream(socket, ownsSocket: true); + } } catch (SocketException se) { - socket.Dispose(); throw new HttpRequestException(se.Message, se); } + } - return new NetworkStream(socket, ownsSocket: true); + /// SocketAsyncEventArgs that carries with it additional state for a Task builder and a CancellationToken. + private sealed class BuilderAndCancellationTokenSocketAsyncEventArgs : SocketAsyncEventArgs + { + public AsyncTaskMethodBuilder Builder { get; } + public CancellationToken CancellationToken { get; } + + public BuilderAndCancellationTokenSocketAsyncEventArgs(CancellationToken cancellationToken) + { + var b = new AsyncTaskMethodBuilder(); + var ignored = b.Task; // force initialization + Builder = b; + + CancellationToken = cancellationToken; + } } } } diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/DecompressionHandler.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/DecompressionHandler.cs index cd72dd3293..261ae26af7 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/DecompressionHandler.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/DecompressionHandler.cs @@ -47,30 +47,22 @@ namespace System.Net.Http HttpResponseMessage response = await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false); - while (true) + ICollection contentEncodings = response.Content.Headers.ContentEncoding; + if (contentEncodings.Count > 0) { - // Get first encoding - using (IEnumerator e = response.Content.Headers.ContentEncoding.GetEnumerator()) + string last = null; + foreach (string encoding in contentEncodings) { - if (!e.MoveNext()) - { - break; - } + last = encoding; + } - string encoding = e.Current; - if (GZipEnabled && encoding == s_gzip) - { - response.Content = new GZipDecompressedContent(response.Content); - } - else if (DeflateEnabled && encoding == s_deflate) - { - response.Content = new DeflateDecompressedContent(response.Content); - } - else - { - // Unknown content encoding. Stop processing. - break; - } + if (GZipEnabled && last == s_gzip) + { + response.Content = new GZipDecompressedContent(response.Content); + } + else if (DeflateEnabled && last == s_deflate) + { + response.Content = new DeflateDecompressedContent(response.Content); } } @@ -99,21 +91,18 @@ namespace System.Net.Http // Copy original response headers, but with the following changes: // Content-Length is removed, since it no longer applies to the decompressed content - // The first Content-Encoding is removed, since we are processing that here + // The last Content-Encoding is removed, since we are processing that here. Headers.AddHeaders(originalContent.Headers); Headers.ContentLength = null; Headers.ContentEncoding.Clear(); - bool first = true; - foreach (var encoding in originalContent.Headers.ContentEncoding) + string prevEncoding = null; + foreach (string encoding in originalContent.Headers.ContentEncoding) { - if (first) + if (prevEncoding != null) { - first = false; - } - else - { - Headers.ContentEncoding.Add(encoding); + Headers.ContentEncoding.Add(prevEncoding); } + prevEncoding = encoding; } } diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnection.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnection.cs index 3524fea3fd..cc083ad605 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnection.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnection.cs @@ -9,6 +9,7 @@ using System.IO; using System.Net.Http.Headers; using System.Net.Security; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -17,14 +18,25 @@ namespace System.Net.Http { internal sealed partial class HttpConnection : IDisposable { + /// Default size of the read buffer used for the connection. private const int InitialReadBufferSize = #if DEBUG 10; #else 4096; #endif + /// Default size of the write buffer used for the connection. private const int InitialWriteBufferSize = InitialReadBufferSize; + /// + /// Delay after which we'll send the request payload for ExpectContinue if + /// the server hasn't yet responded. + /// private const int Expect100TimeoutMilliseconds = 1000; + /// + /// Size after which we'll close the connection rather than send the payload in response + /// to final error status code sent by the server when using Expect: 100-continue. + /// + private const int Expect100ErrorSendThreshold = 1024; private static readonly byte[] s_contentLength0NewlineAsciiBytes = Encoding.ASCII.GetBytes("Content-Length: 0\r\n"); private static readonly byte[] s_spaceHttp10NewlineAsciiBytes = Encoding.ASCII.GetBytes(" HTTP/1.0\r\n"); @@ -186,7 +198,7 @@ namespace System.Net.Http public async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { - TaskCompletionSource allowExpect100ToContinue = null; + TaskCompletionSource allowExpect100ToContinue = null; Debug.Assert(_currentRequest == null, $"Expected null {nameof(_currentRequest)}."); _currentRequest = request; try @@ -287,10 +299,10 @@ namespace System.Net.Http // Create a TCS we'll use to block the request content from being sent, and create a timer that's used // as a fail-safe to unblock the request content if we don't hear back from the server in a timely manner. - // Then kick off the request. - allowExpect100ToContinue = new TaskCompletionSource(); + // Then kick off the request. The TCS' result indicates whether content should be sent or not. + allowExpect100ToContinue = new TaskCompletionSource(); var expect100Timer = new Timer( - s => ((TaskCompletionSource)s).TrySetResult(Expect100ContinueSignal.Timeout), + s => ((TaskCompletionSource)s).TrySetResult(true), allowExpect100ToContinue, TimeSpan.FromMilliseconds(Expect100TimeoutMilliseconds), Timeout.InfiniteTimeSpan); _sendRequestContentTask = SendRequestContentWithExpect100ContinueAsync(request, allowExpect100ToContinue.Task, stream, expect100Timer); } @@ -300,26 +312,39 @@ namespace System.Net.Http var response = new HttpResponseMessage() { RequestMessage = request, Content = new HttpConnectionContent(CancellationToken.None) }; ParseStatusLine(await ReadNextLineAsync(cancellationToken).ConfigureAwait(false), response); + // If we sent an Expect: 100-continue header, handle the response accordingly. if (allowExpect100ToContinue != null) { - // We sent an Expect: 100-continue header. Handle the response accordingly. - if (response.StatusCode == HttpStatusCode.Continue) + if ((int)response.StatusCode >= 300 && + (request.Content.Headers.ContentLength == null || request.Content.Headers.ContentLength.GetValueOrDefault() > Expect100ErrorSendThreshold)) { - // We got our continue header. Read the subsequent \r\n, and allow the request content to continue. - if (!LineIsEmpty(await ReadNextLineAsync(cancellationToken).ConfigureAwait(false))) + // For error final status codes, try to avoid sending the payload if its size is unknown or if it's known to be "big". + // If we already sent a header detailing the size of the payload, if we then don't send that payload, the server may wait + // for it and assume that the next request on the connection is actually this request's payload. Thus we mark the connection + // to be closed. However, we may have also lost a race condition with the Expect: 100-continue timeout, so if it turns out + // we've already started sending the payload (we weren't able to cancel it), then we don't need to force close the connection. + allowExpect100ToContinue.TrySetResult(false); + if (!allowExpect100ToContinue.Task.Result) // if Result is true, the timeout already expired and we started sending content { - ThrowInvalidHttpResponse(); + _connectionClose = true; } - allowExpect100ToContinue.TrySetResult(Expect100ContinueSignal.Received100StatusCode); - - // Then redo the status line read in order to read the real one. - ParseStatusLine(await ReadNextLineAsync(cancellationToken).ConfigureAwait(false), response); } else { - // For any response status code other than 100, we want to try to avoid sending the content - // but otherwise just continue handling this request as we would any other. - allowExpect100ToContinue.TrySetResult(Expect100ContinueSignal.ReceivedOtherStatusCode); + // For any success or informational status codes (including 100 continue), send the payload. + allowExpect100ToContinue.TrySetResult(true); + + // And if this was 100 continue, deal with the extra headers. + if (response.StatusCode == HttpStatusCode.Continue) + { + // We got our continue header. Read the subsequent \r\n and parse the additional status line. + if (!LineIsEmpty(await ReadNextLineAsync(cancellationToken).ConfigureAwait(false))) + { + ThrowInvalidHttpResponse(); + } + + ParseStatusLine(await ReadNextLineAsync(cancellationToken).ConfigureAwait(false), response); + } } } @@ -387,7 +412,7 @@ namespace System.Net.Http catch (Exception error) { // Make sure to complete the allowExpect100ToContinue task if it exists. - allowExpect100ToContinue?.TrySetResult(Expect100ContinueSignal.Error); + allowExpect100ToContinue?.TrySetResult(false); if (NetEventSource.IsEnabled) Trace($"Error sending request: {error}"); Dispose(); @@ -439,23 +464,24 @@ namespace System.Net.Http } private async Task SendRequestContentWithExpect100ContinueAsync( - HttpRequestMessage request, Task allowExpect100ToContinueTask, HttpContentWriteStream stream, Timer expect100Timer) + HttpRequestMessage request, Task allowExpect100ToContinueTask, HttpContentWriteStream stream, Timer expect100Timer) { // Wait until we receive a trigger notification that it's ok to continue sending content. // This will come either when the timer fires or when we receive a response status line from the server. - Expect100ContinueSignal signal = await allowExpect100ToContinueTask.ConfigureAwait(false); - if (NetEventSource.IsEnabled) Trace($"Received signal \"{signal}\" for Expect: 100-continue request content transfer."); + bool sendRequestContent = await allowExpect100ToContinueTask.ConfigureAwait(false); // Clean up the timer; it's no longer needed. expect100Timer.Dispose(); - // If we received a 100 Continue status code or if we timeout waiting for one, send the request content. Otherwise, nothing more to do. - switch (signal) + // Send the content if we're supposed to. Otherwise, we're done. + if (sendRequestContent) { - case Expect100ContinueSignal.Received100StatusCode: - case Expect100ContinueSignal.Timeout: - await SendRequestContentAsync(request.Content.CopyToAsync(stream, _transportContext), stream).ConfigureAwait(false); - break; + if (NetEventSource.IsEnabled) Trace($"Sending request content for Expect: 100-continue."); + await SendRequestContentAsync(request.Content.CopyToAsync(stream, _transportContext), stream).ConfigureAwait(false); + } + else + { + if (NetEventSource.IsEnabled) Trace($"Canceling request content for Expect: 100-continue."); } } @@ -518,7 +544,7 @@ namespace System.Net.Http { try { - fixed (byte* reasonPtr = &reasonBytes.DangerousGetPinnableReference()) + fixed (byte* reasonPtr = &MemoryMarshal.GetReference(reasonBytes)) { response.ReasonPhrase = Encoding.ASCII.GetString(reasonPtr, reasonBytes.Length); } @@ -1047,7 +1073,6 @@ namespace System.Net.Http private void ReturnConnectionToPool() { - Debug.Assert(_writeOffset == 0, "Everything in write buffer should have been flushed."); Debug.Assert(_readAheadTask == null, "Expected a previous initial read to already be consumed."); Debug.Assert(_currentRequest != null, "Expected the connection to be associated with a request."); @@ -1088,6 +1113,7 @@ namespace System.Net.Http private void ReturnConnectionToPoolCore() { Debug.Assert(_sendRequestContentTask == null || _sendRequestContentTask.IsCompleted); + Debug.Assert(_writeOffset == 0, "Everything in write buffer should have been flushed."); if (NetEventSource.IsEnabled) { @@ -1168,17 +1194,5 @@ namespace System.Net.Http _currentRequest?.GetHashCode() ?? 0, // request ID memberName, // method name ToString() + ": " + message); // message - - private enum Expect100ContinueSignal : byte - { - /// Signal to the request content that a 100 Continue status was received from the server. - Received100StatusCode, - /// Signal to the request content that it should send anyway due to not hearing back from the server within the timeout period. - Timeout, - /// Signal to the request content that a non-100 status was received from the server. - ReceivedOtherStatusCode, - /// Signal to the request content that an arbitrary failure occurred during request/response processing. - Error - } } } diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnectionHandler.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnectionHandler.cs index 67d901083b..7f926e7aa6 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnectionHandler.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnectionHandler.cs @@ -28,8 +28,8 @@ namespace System.Net.Http HttpConnectionPool pool = _connectionPools.GetOrAddPool(key); ValueTask connectionTask = pool.GetConnectionAsync( - state => state.handler.CreateConnection(state.request, state.key, state.pool), - (handler: this, request: request, key: key, pool: pool)); + (state, ct) => state.handler.CreateConnection(state.request, state.key, state.pool, ct), + (handler: this, request: request, key: key, pool: pool), cancellationToken); return connectionTask.IsCompletedSuccessfully ? connectionTask.Result.SendAsync(request, cancellationToken) : @@ -43,7 +43,7 @@ namespace System.Net.Http return await connection.SendAsync(request, cancellationToken).ConfigureAwait(false); } - private async ValueTask EstablishSslConnection(string host, HttpRequestMessage request, Stream stream) + private async ValueTask EstablishSslConnection(string host, HttpRequestMessage request, Stream stream, CancellationToken cancellationToken) { RemoteCertificateValidationCallback callback = null; if (_settings._serverCertificateCustomValidationCallback != null) @@ -61,12 +61,18 @@ namespace System.Net.Http }; } - SslStream sslStream = new SslStream(stream, false, callback); + var sslStream = new SslStream(stream); try { - // TODO https://github.com/dotnet/corefx/issues/23077#issuecomment-321807131: No cancellationToken? - await sslStream.AuthenticateAsClientAsync(host, _settings._clientCertificates, _settings._sslProtocols, _settings._checkCertificateRevocationList).ConfigureAwait(false); + await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions + { + TargetHost = host, + ClientCertificates = _settings._clientCertificates, + EnabledSslProtocols = _settings._sslProtocols, + CertificateRevocationCheckMode = _settings._checkCertificateRevocationList ? X509RevocationMode.Online : X509RevocationMode.NoCheck, + RemoteCertificateValidationCallback = callback + }, cancellationToken).ConfigureAwait(false); } catch (Exception e) { @@ -81,17 +87,53 @@ namespace System.Net.Http return sslStream; } - private async ValueTask CreateConnection(HttpRequestMessage request, HttpConnectionKey key, HttpConnectionPool pool) + private async ValueTask CreateConnection( + HttpRequestMessage request, HttpConnectionKey key, HttpConnectionPool pool, CancellationToken cancellationToken) { Uri uri = request.RequestUri; - Stream stream = await ConnectHelper.ConnectAsync(uri.IdnHost, uri.Port).ConfigureAwait(false); + Stream stream = await ConnectHelper.ConnectAsync(uri.IdnHost, uri.Port, cancellationToken).ConfigureAwait(false); TransportContext transportContext = null; if (uri.Scheme == UriScheme.Https) { - SslStream sslStream = await EstablishSslConnection(uri.IdnHost, request, stream).ConfigureAwait(false); + // Get the appropriate host name to use for the SSL connection, allowing a host header to override. + string host = request.Headers.Host; + if (host == null) + { + // No host header, use the host from the Uri. + host = uri.IdnHost; + } + else + { + // There is a host header. Use it, but first see if we need to trim off a port. + int colonPos = host.IndexOf(':'); + if (colonPos >= 0) + { + // There is colon, which could either be a port separator or a separator in + // an IPv6 address. See if this is an IPv6 address; if it's not, use everything + // before the colon as the host name, and if it is, use everything before the last + // colon iff the last colon is after the end of the IPv6 address (otherwise it's a + // part of the address). + int ipV6AddressEnd = host.IndexOf(']'); + if (ipV6AddressEnd == -1) + { + host = host.Substring(0, colonPos); + } + else + { + colonPos = host.LastIndexOf(':'); + if (colonPos > ipV6AddressEnd) + { + host = host.Substring(0, colonPos); + } + } + } + } + + // Establish the connection using the parsed host name. + SslStream sslStream = await EstablishSslConnection(host, request, stream, cancellationToken).ConfigureAwait(false); stream = sslStream; transportContext = sslStream.TransportContext; } diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnectionKey.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnectionKey.cs index d4e4185551..7633841c07 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnectionKey.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnectionKey.cs @@ -1,10 +1,10 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. namespace System.Net.Http { - internal struct HttpConnectionKey : IEquatable + internal readonly struct HttpConnectionKey : IEquatable { public readonly bool UsingSSL; public readonly string Host; diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnectionPool.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnectionPool.cs index c2529d2f61..c0b74d0a0b 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnectionPool.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnectionPool.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. @@ -21,8 +21,11 @@ namespace System.Net.Http private readonly List _idleConnections = new List(); /// The maximum number of connections allowed to be associated with the pool. private readonly int _maxConnections; - /// A queue of waiters waiting for a connection. This will be null if there's no maximum set. - private readonly Queue _waiters; + + /// The head of a list of waiters waiting for a connection. Null if no one's waiting. + private ConnectionWaiter _waitersHead; + /// The tail of a list of waiters waiting for a connection. Null if no one's waiting. + private ConnectionWaiter _waitersTail; /// The number of connections associated with the pool. Some of these may be in , others may be in use. private int _associatedConnectionCount; @@ -36,17 +39,19 @@ namespace System.Net.Http public HttpConnectionPool(int maxConnections = int.MaxValue) // int.MaxValue treated as infinite { _maxConnections = maxConnections; - if (maxConnections < int.MaxValue) - { - _waiters = new Queue(); - } } /// Object used to synchronize access to state in the pool. private object SyncObj => _idleConnections; - public ValueTask GetConnectionAsync(Func> createConnection, TState state) + public ValueTask GetConnectionAsync( + Func> createConnection, TState state, CancellationToken cancellationToken) { + if (cancellationToken.IsCancellationRequested) + { + return new ValueTask(Task.FromCanceled(cancellationToken)); + } + List list = _idleConnections; lock (SyncObj) { @@ -76,11 +81,11 @@ namespace System.Net.Http // there's no limit on the number of connections associated with this // pool, or if we haven't reached such a limit, simply create a new // connection. - if (_waiters == null || _associatedConnectionCount < _maxConnections) + if (_associatedConnectionCount < _maxConnections) { if (NetEventSource.IsEnabled) Trace("Creating new connection for pool."); IncrementConnectionCountNoLock(); - return WaitForCreatedConnectionAsync(createConnection(state)); + return WaitForCreatedConnectionAsync(createConnection(state, cancellationToken)); } else { @@ -92,8 +97,27 @@ namespace System.Net.Http // space is available and the provided creation func has successfully // created the connection to be used. if (NetEventSource.IsEnabled) Trace("Limit reached. Waiting to create new connection."); - var waiter = new ConnectionWaiter(this, createConnection, state); - _waiters.Enqueue(waiter); + var waiter = new ConnectionWaiter(this, createConnection, state, cancellationToken); + EnqueueWaiter(waiter); + if (cancellationToken.CanBeCanceled) + { + // If cancellation could be requested, register a callback for it that'll cancel + // the waiter and remove the waiter from the queue. Note that this registration needs + // to happen under the reentrant lock and after enqueueing the waiter. + waiter._cancellationTokenRegistration = cancellationToken.Register(s => + { + var innerWaiter = (ConnectionWaiter)s; + lock (innerWaiter._pool.SyncObj) + { + // If it's in the list, remove it and cancel it. + if (innerWaiter._pool.RemoveWaiterForCancellation(innerWaiter)) + { + bool canceled = innerWaiter.TrySetCanceled(innerWaiter._cancellationToken); + Debug.Assert(canceled); + } + } + }, waiter); + } return new ValueTask(waiter.Task); } @@ -108,6 +132,87 @@ namespace System.Net.Http } } + /// Enqueues a waiter to the waiters list. + /// The waiter to add. + private void EnqueueWaiter(ConnectionWaiter waiter) + { + Debug.Assert(Monitor.IsEntered(SyncObj)); + Debug.Assert(waiter != null); + Debug.Assert(waiter._next == null); + Debug.Assert(waiter._prev == null); + + waiter._next = _waitersHead; + if (_waitersHead != null) + { + _waitersHead._prev = waiter; + } + else + { + Debug.Assert(_waitersTail == null); + _waitersTail = waiter; + } + _waitersHead = waiter; + } + + /// Dequeues a waiter from the waiters list. The list must not be empty. + /// The dequeued waiter. + private ConnectionWaiter DequeueWaiter() + { + Debug.Assert(Monitor.IsEntered(SyncObj)); + Debug.Assert(_waitersTail != null); + + ConnectionWaiter waiter = _waitersTail; + _waitersTail = waiter._prev; + + if (_waitersTail != null) + { + _waitersTail._next = null; + } + else + { + Debug.Assert(_waitersHead == waiter); + _waitersHead = null; + } + + waiter._next = null; + waiter._prev = null; + + return waiter; + } + + /// Removes the specified waiter from the waiters list as part of a cancellation request. + /// The waiter to remove. + /// true if the waiter was in the list; otherwise, false. + private bool RemoveWaiterForCancellation(ConnectionWaiter waiter) + { + Debug.Assert(Monitor.IsEntered(SyncObj)); + Debug.Assert(waiter != null); + Debug.Assert(waiter._cancellationToken.IsCancellationRequested); + + bool inList = waiter._next != null || waiter._prev != null || _waitersHead == waiter || _waitersTail == waiter; + + if (waiter._next != null) waiter._next._prev = waiter._prev; + if (waiter._prev != null) waiter._prev._next = waiter._next; + + if (_waitersHead == waiter && _waitersTail == waiter) + { + _waitersHead = _waitersTail = null; + } + else if (_waitersHead == waiter) + { + _waitersHead = waiter._next; + } + else if (_waitersTail == waiter) + { + _waitersTail = waiter._prev; + } + + waiter._next = null; + waiter._prev = null; + + return inList; + } + /// Waits for and returns the created connection, decrementing the associated connection count if it fails. private async ValueTask WaitForCreatedConnectionAsync(ValueTask creationTask) { @@ -160,7 +265,7 @@ namespace System.Net.Http // Mark the pool as not being stale. _usedSinceLastCleanup = true; - if (_waiters == null || _waiters.Count == 0) + if (_waitersHead == null) { // There are no waiters to which the count should logically be transferred, // so simply decrement the count. @@ -171,9 +276,10 @@ namespace System.Net.Http // There's at least one waiter to which we should try to logically transfer // the associated count. Get the waiter. Debug.Assert(_idleConnections.Count == 0, $"With {_idleConnections} connections, we shouldn't have a waiter."); - ConnectionWaiter waiter = _waiters.Dequeue(); + ConnectionWaiter waiter = DequeueWaiter(); Debug.Assert(waiter != null, "Expected non-null waiter"); Debug.Assert(waiter.Task.Status == TaskStatus.WaitingForActivation, $"Expected {waiter.Task.Status} == {nameof(TaskStatus.WaitingForActivation)}"); + waiter._cancellationTokenRegistration.Dispose(); // Having a waiter means there must not be any idle connections, so we need to create // one, and we do so using the logic associated with the waiter. @@ -231,10 +337,14 @@ namespace System.Net.Http // If there's someone waiting for a connection, simply // transfer this one to them rather than pooling it. - if (_waiters != null && _waiters.TryDequeue(out ConnectionWaiter waiter)) + if (_waitersTail != null) { + ConnectionWaiter waiter = DequeueWaiter(); + waiter._cancellationTokenRegistration.Dispose(); + if (NetEventSource.IsEnabled) connection.Trace("Transferring connection returned to pool."); waiter.SetResult(connection); + return; } @@ -377,7 +487,7 @@ namespace System.Net.Http /// A cached idle connection and metadata about it. [StructLayout(LayoutKind.Auto)] - private struct CachedConnection : IEquatable + private readonly struct CachedConnection : IEquatable { /// The cached connection. internal readonly HttpConnection _connection; @@ -419,14 +529,15 @@ namespace System.Net.Http private sealed class ConnectionWaiter : ConnectionWaiter { /// The function to invoke if/when is invoked. - private readonly Func> _createConnectionAsync; + private readonly Func> _createConnectionAsync; /// The state to pass to when it's invoked. private readonly TState _state; /// Initializes the waiter. /// The function to invoke if/when is invoked. /// The state to pass to when it's invoked. - public ConnectionWaiter(HttpConnectionPool pool, Func> func, TState state) : base(pool) + public ConnectionWaiter(HttpConnectionPool pool, Func> func, TState state, CancellationToken cancellationToken) : + base(pool, cancellationToken) { _createConnectionAsync = func; _state = state; @@ -437,7 +548,7 @@ namespace System.Net.Http { try { - return _createConnectionAsync(_state); + return _createConnectionAsync(_state, _cancellationToken); } catch (Exception e) { @@ -462,12 +573,21 @@ namespace System.Net.Http { /// The pool with which this waiter is associated. internal readonly HttpConnectionPool _pool; + /// Cancellation token for the waiter. + internal readonly CancellationToken _cancellationToken; + /// Registration that removes the waiter from the registration list. + internal CancellationTokenRegistration _cancellationTokenRegistration; + /// Next waiter in the list. + internal ConnectionWaiter _next; + /// Previous waiter in the list. + internal ConnectionWaiter _prev; /// Initializes the waiter. - public ConnectionWaiter(HttpConnectionPool pool) : base(TaskCreationOptions.RunContinuationsAsynchronously) + public ConnectionWaiter(HttpConnectionPool pool, CancellationToken cancellationToken) : base(TaskCreationOptions.RunContinuationsAsynchronously) { Debug.Assert(pool != null, "Expected non-null pool"); _pool = pool; + _cancellationToken = cancellationToken; } /// Creates a connection. diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnectionPools.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnectionPools.cs index 2650b7bab3..d1d043b543 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnectionPools.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnectionPools.cs @@ -39,7 +39,25 @@ namespace System.Net.Http _maxConnectionsPerServer = maxConnectionsPerServer; _pools = new ConcurrentDictionary(); // Start out with the timer not running, since we have no pools. - _cleaningTimer = new Timer(s => ((HttpConnectionPools)s).RemoveStalePools(), this, Timeout.Infinite, Timeout.Infinite); + + // Don't capture the current ExecutionContext and its AsyncLocals onto the timer causing them to live forever + bool restoreFlow = false; + try + { + if (!ExecutionContext.IsFlowSuppressed()) + { + ExecutionContext.SuppressFlow(); + restoreFlow = true; + } + + _cleaningTimer = new Timer(s => ((HttpConnectionPools)s).RemoveStalePools(), this, Timeout.Infinite, Timeout.Infinite); + } + finally + { + // Restore the current ExecutionContext + if (restoreFlow) + ExecutionContext.RestoreFlow(); + } } /// Gets a pool for the specified endpoint, adding one if none existed. diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpProxyConnectionHandler.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpProxyConnectionHandler.cs index 82374d3cbe..bd5718de4c 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpProxyConnectionHandler.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/HttpProxyConnectionHandler.cs @@ -65,7 +65,7 @@ namespace System.Net.Http throw new NotImplementedException("no support for SSL tunneling through proxy"); } - HttpConnection connection = await GetOrCreateConnection(request, proxyUri).ConfigureAwait(false); + HttpConnection connection = await GetOrCreateConnection(request, proxyUri, cancellationToken).ConfigureAwait(false); HttpResponseMessage response = await connection.SendAsync(request, cancellationToken).ConfigureAwait(false); @@ -88,7 +88,7 @@ namespace System.Net.Http request.Headers.ProxyAuthorization = new AuthenticationHeaderValue(AuthenticationHelper.Basic, AuthenticationHelper.GetBasicTokenForCredential(credential)); - connection = await GetOrCreateConnection(request, proxyUri).ConfigureAwait(false); + connection = await GetOrCreateConnection(request, proxyUri, cancellationToken).ConfigureAwait(false); response = await connection.SendAsync(request, cancellationToken).ConfigureAwait(false); } @@ -140,15 +140,15 @@ namespace System.Net.Http return response; } - private ValueTask GetOrCreateConnection(HttpRequestMessage request, Uri proxyUri) + private ValueTask GetOrCreateConnection(HttpRequestMessage request, Uri proxyUri, CancellationToken cancellationToken) { var key = new HttpConnectionKey(proxyUri); HttpConnectionPool pool = _connectionPools.GetOrAddPool(key); - return pool.GetConnectionAsync(async state => + return pool.GetConnectionAsync(async (state, ct) => { - Stream stream = await ConnectHelper.ConnectAsync(state.proxyUri.IdnHost, state.proxyUri.Port).ConfigureAwait(false); + Stream stream = await ConnectHelper.ConnectAsync(state.proxyUri.IdnHost, state.proxyUri.Port, ct).ConfigureAwait(false); return new HttpConnection(state.pool, state.key, null, stream, null, true); - }, (pool: pool, key: key, request: request, proxyUri: proxyUri)); + }, (pool: pool, key: key, request: request, proxyUri: proxyUri), cancellationToken); } protected override void Dispose(bool disposing) diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/ManagedHandler.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/ManagedHandler.cs index cf1a1798f3..14029888fb 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/ManagedHandler.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/Managed/ManagedHandler.cs @@ -269,19 +269,14 @@ namespace System.Net.Http handler = new HttpProxyConnectionHandler(_settings, handler); } - if (_settings._credentials != null) - { - handler = new AuthenticationHandler(_settings._preAuthenticate, _settings._credentials, handler); - } - if (_settings._useCookies) { handler = new CookieHandler(CookieContainer, handler); } - if (_settings._allowAutoRedirect) + if (_settings._credentials != null || _settings._allowAutoRedirect) { - handler = new AutoRedirectHandler(_settings._maxAutomaticRedirections, handler); + handler = new AuthenticateAndRedirectHandler(_settings._preAuthenticate, _settings._credentials, _settings._allowAutoRedirect, _settings._maxAutomaticRedirections, handler); } if (_settings._automaticDecompression != DecompressionMethods.None) diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/OSX/CurlHandler.SslProvider.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/OSX/CurlHandler.SslProvider.cs index 3e9b3e1f6d..134cfec0dc 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/OSX/CurlHandler.SslProvider.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/OSX/CurlHandler.SslProvider.cs @@ -12,19 +12,6 @@ namespace System.Net.Http { internal partial class CurlHandler : HttpMessageHandler { - static partial void UseSingletonMultiAgent(ref bool result) - { - // Some backends other than OpenSSL need locks initialized in order to use them in a - // multithreaded context, which would happen with multiple HttpClients and thus multiple - // MultiAgents. Since we don't currently have the ability to do so initialization, instead we - // restrict all HttpClients to use the same MultiAgent instance in this case. We know LibreSSL - // is in this camp, so we currently special-case it. - string curlSslVersion = Interop.Http.GetSslVersionDescription(); - result = - !string.IsNullOrEmpty(curlSslVersion) && - curlSslVersion.StartsWith(Interop.Http.LibreSslDescription, StringComparison.OrdinalIgnoreCase); - } - private static class SslProvider { internal static void SetSslOptions(EasyRequest easy, ClientCertificateOption clientCertOption) diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/ReadOnlyMemoryContent.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/ReadOnlyMemoryContent.cs index 2f42bd1973..0a9588d71a 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/ReadOnlyMemoryContent.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/ReadOnlyMemoryContent.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.IO; +using System.Runtime.InteropServices; using System.Threading.Tasks; namespace System.Net.Http @@ -14,7 +15,7 @@ namespace System.Net.Http public ReadOnlyMemoryContent(ReadOnlyMemory content) { _content = content; - if (content.DangerousTryGetArray(out ArraySegment array)) + if (MemoryMarshal.TryGetArray(content, out ArraySegment array)) { // If we have an array, allow HttpClient to take optimized paths by just // giving it the array content to use as its already buffered data. diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/Unix/CurlHandler.EasyRequest.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/Unix/CurlHandler.EasyRequest.cs index 4a8ea50706..b6dc5eba69 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/Unix/CurlHandler.EasyRequest.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/Unix/CurlHandler.EasyRequest.cs @@ -732,9 +732,8 @@ namespace System.Net.Http } // Since libcurl adds an Expect header if it sees enough post data, we need to explicitly block - // it if caller specifically does not want to set the header - if (_requestMessage.Headers.ExpectContinue.HasValue && - !_requestMessage.Headers.ExpectContinue.Value) + // it unless the caller has explicitly opted-in to it. + if (!_requestMessage.Headers.ExpectContinue.GetValueOrDefault()) { ThrowOOMIfFalse(Interop.Http.SListAppend(slist, NoExpect)); } @@ -885,6 +884,11 @@ namespace System.Net.Http ThrowIfCURLEError(Interop.Http.EasySetOptionString(_easyHandle, option, value)); } + internal CURLcode TrySetCurlOption(CURLoption option, string value) + { + return Interop.Http.EasySetOptionString(_easyHandle, option, value); + } + internal void SetCurlOption(CURLoption option, long value) { ThrowIfCURLEError(Interop.Http.EasySetOptionLong(_easyHandle, option, value)); diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/Unix/CurlHandler.SslProvider.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/Unix/CurlHandler.SslProvider.cs index d66abc7b04..993d28b2f8 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/Unix/CurlHandler.SslProvider.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/Unix/CurlHandler.SslProvider.cs @@ -85,6 +85,8 @@ namespace System.Net.Http // cause SSL and libcurl to ignore the result of the server callback. } + SetSslOptionsForCertificateStore(easy); + // The allowed SSL protocols will be set in the configuration callback. break; @@ -99,6 +101,28 @@ namespace System.Net.Http } } + private static void SetSslOptionsForCertificateStore(EasyRequest easy) + { + // Support specifying certificate directory/bundle via environment variables: SSL_CERT_DIR, SSL_CERT_FILE. + string sslCertDir = Environment.GetEnvironmentVariable("SSL_CERT_DIR"); + if (sslCertDir != null) + { + easy.SetCurlOption(Interop.Http.CURLoption.CURLOPT_CAPATH, sslCertDir); + + // https proxy support requires libcurl 7.52.0+ + easy.TrySetCurlOption(Interop.Http.CURLoption.CURLOPT_PROXY_CAPATH, sslCertDir); + } + + string sslCertFile = Environment.GetEnvironmentVariable("SSL_CERT_FILE"); + if (sslCertFile != null) + { + easy.SetCurlOption(Interop.Http.CURLoption.CURLOPT_CAINFO, sslCertFile); + + // https proxy support requires libcurl 7.52.0+ + easy.TrySetCurlOption(Interop.Http.CURLoption.CURLOPT_PROXY_CAINFO, sslCertFile); + } + } + private static void SetSslOptionsForUnsupportedBackend(EasyRequest easy, ClientCertificateProvider certProvider) { if (certProvider != null) @@ -124,6 +148,10 @@ namespace System.Net.Http throw new PlatformNotSupportedException(SR.Format(SR.net_http_libcurl_callback_notsupported, CurlVersionDescription, CurlSslVersionDescription)); } } + else + { + SetSslOptionsForCertificateStore(easy); + } // In case of defaults configure the allowed SSL protocols. SetSslVersion(easy); diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/Unix/CurlHandler.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/Unix/CurlHandler.cs index a9e4e35791..88eae1bf51 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/Unix/CurlHandler.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/Unix/CurlHandler.cs @@ -174,9 +174,7 @@ namespace System.Net.Http // By default every CurlHandler gets its own MultiAgent. But for some backends, // we need to restrict the number of threads involved in processing libcurl work, // so we create a single MultiAgent that's used by all handlers. - bool useSingleton = false; - UseSingletonMultiAgent(ref useSingleton); - if (useSingleton) + if (UseSingletonMultiAgent) { s_singletonSharedAgent = new MultiAgent(null); } @@ -189,14 +187,27 @@ namespace System.Net.Http _agent = s_singletonSharedAgent ?? new MultiAgent(this); } - /// Overridden by another partial implementation to set to true if a single MultiAgent should be used. - static partial void UseSingletonMultiAgent(ref bool result); - #region Properties private static string CurlVersionDescription => s_curlVersionDescription ?? (s_curlVersionDescription = Interop.Http.GetVersionDescription() ?? string.Empty); private static string CurlSslVersionDescription => s_curlSslVersionDescription ?? (s_curlSslVersionDescription = Interop.Http.GetSslVersionDescription() ?? string.Empty); + private static bool UseSingletonMultiAgent + { + get + { + // Some backends other than OpenSSL need locks initialized in order to use them in a + // multithreaded context, which would happen with multiple HttpClients and thus multiple + // MultiAgents. Since we don't currently have the ability to do so initialization, instead we + // restrict all HttpClients to use the same MultiAgent instance in this case. We know LibreSSL + // is in this camp, so we currently special-case it. + string curlSslVersion = Interop.Http.GetSslVersionDescription(); + return + !string.IsNullOrEmpty(curlSslVersion) && + curlSslVersion.StartsWith(Interop.Http.LibreSslDescription, StringComparison.OrdinalIgnoreCase); + } + } + internal bool AllowAutoRedirect { get { return _automaticRedirection; } diff --git a/external/corefx/src/System.Net.Http/src/System/Net/Http/Unix/CurlResponseHeaderReader.cs b/external/corefx/src/System.Net.Http/src/System/Net/Http/Unix/CurlResponseHeaderReader.cs index dd330e728d..ceafdf6478 100644 --- a/external/corefx/src/System.Net.Http/src/System/Net/Http/Unix/CurlResponseHeaderReader.cs +++ b/external/corefx/src/System.Net.Http/src/System/Net/Http/Unix/CurlResponseHeaderReader.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. @@ -7,7 +7,7 @@ using System.Runtime.InteropServices; namespace System.Net.Http { - internal struct CurlResponseHeaderReader + internal readonly struct CurlResponseHeaderReader { private const string HttpPrefix = "HTTP/"; @@ -139,7 +139,7 @@ namespace System.Net.Http return c == ' ' || (c >= '\x0009' && c <= '\x000d') || c == '\x00a0' || c == '\x0085'; } - private unsafe struct HeaderBufferSpan + private unsafe readonly struct HeaderBufferSpan { private readonly byte* _pointer; public readonly int Length; diff --git a/external/corefx/src/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs b/external/corefx/src/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs index f899baa83f..5a991d1d94 100644 --- a/external/corefx/src/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs +++ b/external/corefx/src/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs @@ -582,6 +582,7 @@ namespace System.Net.Http.Functional.Tests }, UseManagedHandler.ToString()).Dispose(); } + [ActiveIssue(23209)] [OuterLoop] // TODO: Issue #11345 [Fact] public void SendAsync_ExpectedDiagnosticCancelledActivityLogging() diff --git a/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.AcceptAllCerts.cs b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.AcceptAllCerts.cs index b1544255b1..ab8429ecba 100644 --- a/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.AcceptAllCerts.cs +++ b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.AcceptAllCerts.cs @@ -14,7 +14,6 @@ namespace System.Net.Http.Functional.Tests public class HttpClientHandler_DangerousAcceptAllCertificatesValidator_Test : HttpClientTestBase { - // TODO: https://github.com/dotnet/corefx/issues/7812 private static bool ClientSupportsDHECipherSuites => (!PlatformDetection.IsWindows || PlatformDetection.IsWindows10Version1607OrGreater); [Fact] diff --git a/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.DefaultProxyCredentials.cs b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.DefaultProxyCredentials.cs index 547ea250aa..ebdf3a648d 100644 --- a/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.DefaultProxyCredentials.cs +++ b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.DefaultProxyCredentials.cs @@ -46,7 +46,7 @@ namespace System.Net.Http.Functional.Tests [ActiveIssue(20010, TargetFrameworkMonikers.Uap)] [OuterLoop] // TODO: Issue #11345 [Fact] - public void ProxyExplicitlyProvided_DefaultCredentials_Ignored() + public async Task ProxyExplicitlyProvided_DefaultCredentials_Ignored() { int port; Task proxyTask = LoopbackGetRequestHttpProxy.StartAsync(out port, requireAuth: true, expectCreds: true); @@ -66,7 +66,7 @@ namespace System.Net.Http.Functional.Tests { using (t.Result) return t.Result.Content.ReadAsStringAsync(); }, TaskScheduler.Default).Unwrap(); - Task.WaitAll(proxyTask, responseTask, responseStringTask); + await (new Task[] { proxyTask, responseTask, responseStringTask }).WhenAllOrAnyFailed(); TestHelper.VerifyResponseBody(responseStringTask.Result, responseTask.Result.Content.Headers.ContentMD5, false, null); Assert.Equal(Encoding.ASCII.GetString(proxyTask.Result.ResponseContent), responseStringTask.Result); @@ -80,14 +80,9 @@ namespace System.Net.Http.Functional.Tests [Theory] [InlineData(false)] [InlineData(true)] + [ActiveIssue(25640, TestPlatforms.Windows)] // TODO It should be enabled for managed Handler on all platforms public void ProxySetViaEnvironmentVariable_DefaultProxyCredentialsUsed(bool useProxy) { - bool envVarsSupported = UseManagedHandler || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - if (!envVarsSupported) - { - return; - } - int port = 0; Task proxyTask = null; if (useProxy) diff --git a/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.ServerCertificates.Unix.cs b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.ServerCertificates.Unix.cs index 132c930e66..3f7d5f264d 100644 --- a/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.ServerCertificates.Unix.cs +++ b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.ServerCertificates.Unix.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Net.Security; using System.Net.Test.Common; using System.Runtime.InteropServices; @@ -70,5 +71,61 @@ namespace System.Net.Http.Functional.Tests [DllImport("System.Net.Http.Native", EntryPoint = "HttpNative_GetSslVersionDescription")] private static extern string CurlSslVersionDescription(); + + [Theory] + [PlatformSpecific(~TestPlatforms.OSX)] // Not implemented + [InlineData(false, false, false, false, false)] // system -> ok + [InlineData(true, true, true, true, true)] // empty dir, empty bundle file -> fail + // It is enough to override the bundle, since all tested platforms don't have a default dir: + [InlineData(false, false, true, true, true)] // empty bundle -> fail + [InlineData(false, false, true, false, true)] // non-existing bundle -> fail + public void HttpClientUsesSslCertEnvironmentVariables(bool setSslCertDir, bool createSslCertDir, + bool setSslCertFile, bool createSslCertFile, bool expectedFailure) + { + // This test sets SSL_CERT_DIR and SSL_CERT_FILE to empty/non-existing locations and then + // checks the http request fails. + // Some platforms will use the system default when not specifying a value, while others + // will not use those certificates. Due to these platform differences, we only check specific + // combinations that are expected to work the same cross-platform. + var psi = new ProcessStartInfo(); + if (setSslCertDir) + { + string sslCertDir = GetTestFilePath(); + if (createSslCertDir) + { + Directory.CreateDirectory(sslCertDir); + } + psi.Environment.Add("SSL_CERT_DIR", sslCertDir); + } + + if (setSslCertFile) + { + string sslCertFile = GetTestFilePath(); + if (createSslCertFile) + { + File.WriteAllText(sslCertFile, ""); + } + psi.Environment.Add("SSL_CERT_FILE", sslCertFile); + } + + RemoteInvoke(async arg => + { + bool shouldFail = bool.Parse(arg); + const string Url = "https://www.microsoft.com"; + + using (HttpClient client = new HttpClient()) + { + if (shouldFail) + { + await Assert.ThrowsAsync(() => client.GetAsync(Url)); + } + else + { + await client.GetAsync(Url); + } + } + return SuccessExitCode; + }, expectedFailure.ToString(), new RemoteInvokeOptions { StartInfo = psi }).Dispose(); + } } } diff --git a/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.ServerCertificates.cs b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.ServerCertificates.cs index 43fef62f82..af3b67fe1b 100644 --- a/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.ServerCertificates.cs +++ b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.ServerCertificates.cs @@ -19,7 +19,6 @@ namespace System.Net.Http.Functional.Tests [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, ".NET Framework throws PNSE for ServerCertificateCustomValidationCallback")] public partial class HttpClientHandler_ServerCertificates_Test : HttpClientTestBase { - // TODO: https://github.com/dotnet/corefx/issues/7812 private static bool ClientSupportsDHECipherSuites => (!PlatformDetection.IsWindows || PlatformDetection.IsWindows10Version1607OrGreater); private bool BackendSupportsCustomCertificateHandlingAndClientSupportsDHECipherSuites => (BackendSupportsCustomCertificateHandling && ClientSupportsDHECipherSuites); @@ -337,29 +336,41 @@ namespace System.Net.Http.Functional.Tests [MemberData(nameof(CertificateValidationServersAndExpectedPolicies))] public async Task UseCallback_BadCertificate_ExpectedPolicyErrors(string url, SslPolicyErrors expectedErrors) { + const int SEC_E_BUFFER_TOO_SMALL = unchecked((int)0x80090321); + if (!BackendSupportsCustomCertificateHandlingAndClientSupportsDHECipherSuites) { return; } - if (PlatformDetection.IsUap) + try { - // UAP HTTP stack caches connections per-process. This causes interference when these tests run in - // the same process as the other tests. Each test needs to be isolated to its own process. - // See dicussion: https://github.com/dotnet/corefx/issues/21945 - RemoteInvoke((remoteUrl, remoteExpectedErrors, useManagedHandlerString) => + if (PlatformDetection.IsUap) { - UseCallback_BadCertificate_ExpectedPolicyErrors_Helper( - remoteUrl, - bool.Parse(useManagedHandlerString), - (SslPolicyErrors)Enum.Parse(typeof(SslPolicyErrors), remoteExpectedErrors)).Wait(); + // UAP HTTP stack caches connections per-process. This causes interference when these tests run in + // the same process as the other tests. Each test needs to be isolated to its own process. + // See dicussion: https://github.com/dotnet/corefx/issues/21945 + RemoteInvoke((remoteUrl, remoteExpectedErrors, useManagedHandlerString) => + { + UseCallback_BadCertificate_ExpectedPolicyErrors_Helper( + remoteUrl, + bool.Parse(useManagedHandlerString), + (SslPolicyErrors)Enum.Parse(typeof(SslPolicyErrors), remoteExpectedErrors)).Wait(); - return SuccessExitCode; - }, url, expectedErrors.ToString(), UseManagedHandler.ToString()).Dispose(); + return SuccessExitCode; + }, url, expectedErrors.ToString(), UseManagedHandler.ToString()).Dispose(); + } + else + { + await UseCallback_BadCertificate_ExpectedPolicyErrors_Helper(url, UseManagedHandler, expectedErrors); + } } - else + catch (HttpRequestException e) when (e.InnerException?.GetType().Name == "WinHttpException" && + e.InnerException.HResult == SEC_E_BUFFER_TOO_SMALL && + !PlatformDetection.IsWindows10Version1607OrGreater) { - await UseCallback_BadCertificate_ExpectedPolicyErrors_Helper(url, UseManagedHandler, expectedErrors); + // Testing on old Windows versions can hit https://github.com/dotnet/corefx/issues/7812 + // Ignore SEC_E_BUFFER_TOO_SMALL error on such cases. } } diff --git a/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.SslProtocols.cs b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.SslProtocols.cs index d7cb6c3a0f..2370fa76a0 100644 --- a/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.SslProtocols.cs +++ b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.SslProtocols.cs @@ -5,6 +5,7 @@ using System.IO; using System.Net.Security; using System.Net.Test.Common; +using System.Runtime.InteropServices; using System.Security.Authentication; using System.Threading.Tasks; using Xunit; @@ -98,9 +99,10 @@ namespace System.Net.Http.Functional.Tests { return; } + if (UseManagedHandler) { - // TODO #23138: The managed handler is failing. + // TODO #26186: The managed handler is failing on some OSes. return; } @@ -123,7 +125,7 @@ namespace System.Net.Http.Functional.Tests } } - public static readonly object [][] SupportedSSLVersionServers = + public static readonly object[][] SupportedSSLVersionServers = { new object[] {SslProtocols.Tls, Configuration.Http.TLSv10RemoteServer}, new object[] {SslProtocols.Tls11, Configuration.Http.TLSv11RemoteServer}, @@ -141,13 +143,13 @@ namespace System.Net.Http.Functional.Tests { if (UseManagedHandler) { - // TODO #23138: The managed handler is failing. + // TODO #26186: The managed handler is failing on some OSes. return; } using (HttpClientHandler handler = CreateHttpClientHandler()) { - if (PlatformDetection.IsCentos7) + if (PlatformDetection.IsRedHatFamily7) { // Default protocol selection is always TLSv1 on Centos7 libcurl 7.29.0 // Hence, set the specific protocol on HttpClient that is required by test @@ -155,11 +157,26 @@ namespace System.Net.Http.Functional.Tests } using (var client = new HttpClient(handler)) { - (await client.GetAsync(url)).Dispose(); + (await RemoteServerQuery.Run(() => client.GetAsync(url), remoteServerExceptionWrapper, url)).Dispose(); } } } + public Func remoteServerExceptionWrapper = (exception) => + { + Type exceptionType = exception.GetType(); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + // On linux, taskcanceledexception is thrown. + return exceptionType.Equals(typeof(TaskCanceledException)); + } + else + { + // The internal exceptions return operation timed out. + return exceptionType.Equals(typeof(HttpRequestException)) && exception.InnerException.Message.Contains("timed out"); + } + }; + public static readonly object[][] NotSupportedSSLVersionServers = { new object[] {"SSLv2", Configuration.Http.SSLv2RemoteServer}, @@ -189,7 +206,7 @@ namespace System.Net.Http.Functional.Tests using (HttpClient client = CreateHttpClient()) { - await Assert.ThrowsAsync(() => client.GetAsync(url)); + await Assert.ThrowsAsync(() => RemoteServerQuery.Run(() => client.GetAsync(url), remoteServerExceptionWrapper, url)); } } @@ -197,7 +214,8 @@ namespace System.Net.Http.Functional.Tests [ConditionalFact(nameof(SslDefaultsToTls12))] public async Task GetAsync_NoSpecifiedProtocol_DefaultsToTls12() { - if (!BackendSupportsSslConfiguration) return; + if (!BackendSupportsSslConfiguration) + return; using (HttpClientHandler handler = CreateHttpClientHandler()) using (var client = new HttpClient(handler)) { @@ -226,7 +244,8 @@ namespace System.Net.Http.Functional.Tests public async Task GetAsync_AllowedSSLVersionDiffersFromServer_ThrowsException( SslProtocols allowedProtocol, SslProtocols acceptedProtocol, Type exceptedServerException) { - if (!BackendSupportsSslConfiguration) return; + if (!BackendSupportsSslConfiguration) + return; using (HttpClientHandler handler = CreateHttpClientHandler()) using (var client = new HttpClient(handler)) { diff --git a/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.cs b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.cs deleted file mode 100644 index 9f2aa8db2f..0000000000 --- a/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.cs +++ /dev/null @@ -1,2128 +0,0 @@ -// 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.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net.Sockets; -using System.Net.Test.Common; -using System.Runtime.InteropServices; -using System.Security.Authentication; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -using Xunit; -using Xunit.Abstractions; - -namespace System.Net.Http.Functional.Tests -{ - using Configuration = System.Net.Test.Common.Configuration; - - // Note: Disposing the HttpClient object automatically disposes the handler within. So, it is not necessary - // to separately Dispose (or have a 'using' statement) for the handler. - public class HttpClientHandlerTest : HttpClientTestBase - { - readonly ITestOutputHelper _output; - private const string ExpectedContent = "Test contest"; - private const string Username = "testuser"; - private const string Password = "password"; - - private readonly NetworkCredential _credential = new NetworkCredential(Username, Password); - - public static bool IsNotWindows7 => !PlatformDetection.IsWindows7; - - public static readonly object[][] EchoServers = Configuration.Http.EchoServers; - public static readonly object[][] VerifyUploadServers = Configuration.Http.VerifyUploadServers; - public static readonly object[][] CompressedServers = Configuration.Http.CompressedServers; - public static readonly object[][] HeaderValueAndUris = { - new object[] { "X-CustomHeader", "x-value", Configuration.Http.RemoteEchoServer }, - new object[] { "X-CustomHeader", "x-value", Configuration.Http.RedirectUriForDestinationUri( - secure:false, - statusCode:302, - destinationUri:Configuration.Http.RemoteEchoServer, - hops:1) }, - }; - public static readonly object[][] HeaderWithEmptyValueAndUris = { - new object[] { "X-Cust-Header-NoValue", "" , Configuration.Http.RemoteEchoServer }, - new object[] { "X-Cust-Header-NoValue", "" , Configuration.Http.RedirectUriForDestinationUri( - secure:false, - statusCode:302, - destinationUri:Configuration.Http.RemoteEchoServer, - hops:1) }, - }; - public static readonly object[][] Http2Servers = Configuration.Http.Http2Servers; - public static readonly object[][] Http2NoPushServers = Configuration.Http.Http2NoPushServers; - - public static readonly object[][] RedirectStatusCodes = { - new object[] { 300 }, - new object[] { 301 }, - new object[] { 302 }, - new object[] { 303 }, - new object[] { 307 } - }; - - public static readonly object[][] RedirectStatusCodesOldMethodsNewMethods = { - new object[] { 300, "GET", "GET" }, - new object[] { 300, "POST", "GET" }, - new object[] { 300, "HEAD", "HEAD" }, - - new object[] { 301, "GET", "GET" }, - new object[] { 301, "POST", "GET" }, - new object[] { 301, "HEAD", "HEAD" }, - - new object[] { 302, "GET", "GET" }, - new object[] { 302, "POST", "GET" }, - new object[] { 302, "HEAD", "HEAD" }, - - new object[] { 303, "GET", "GET" }, - new object[] { 303, "POST", "GET" }, - new object[] { 303, "HEAD", "HEAD" }, - - new object[] { 307, "GET", "GET" }, - new object[] { 307, "POST", "POST" }, - new object[] { 307, "HEAD", "HEAD" }, - }; - - // Standard HTTP methods defined in RFC7231: http://tools.ietf.org/html/rfc7231#section-4.3 - // "GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS", "TRACE" - public static readonly IEnumerable HttpMethods = - GetMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS", "TRACE", "CUSTOM1"); - public static readonly IEnumerable HttpMethodsThatAllowContent = - GetMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "CUSTOM1"); - public static readonly IEnumerable HttpMethodsThatDontAllowContent = - GetMethods("HEAD", "TRACE"); - - private static bool IsWindows10Version1607OrGreater => PlatformDetection.IsWindows10Version1607OrGreater; - - private static IEnumerable GetMethods(params string[] methods) - { - foreach (string method in methods) - { - foreach (bool secure in new[] { true, false }) - { - yield return new object[] { method, secure }; - } - } - } - - public HttpClientHandlerTest(ITestOutputHelper output) - { - _output = output; - if (PlatformDetection.IsFullFramework) - { - // On .NET Framework, the default limit for connections/server is very low (2). - // On .NET Core, the default limit is higher. Since these tests run in parallel, - // the limit needs to be increased to avoid timeouts when running the tests. - System.Net.ServicePointManager.DefaultConnectionLimit = int.MaxValue; - } - } - - [Fact] - public void Ctor_ExpectedDefaultPropertyValues_CommonPlatform() - { - using (HttpClientHandler handler = CreateHttpClientHandler()) - { - // Same as .NET Framework (Desktop). - Assert.Equal(DecompressionMethods.None, handler.AutomaticDecompression); - Assert.True(handler.AllowAutoRedirect); - Assert.Equal(ClientCertificateOption.Manual, handler.ClientCertificateOptions); - CookieContainer cookies = handler.CookieContainer; - Assert.NotNull(cookies); - Assert.Equal(0, cookies.Count); - Assert.Null(handler.Credentials); - Assert.Equal(50, handler.MaxAutomaticRedirections); - Assert.NotNull(handler.Properties); - Assert.Equal(null, handler.Proxy); - Assert.True(handler.SupportsAutomaticDecompression); - Assert.True(handler.UseCookies); - Assert.False(handler.UseDefaultCredentials); - Assert.True(handler.UseProxy); - } - } - - [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] - [Fact] - public void Ctor_ExpectedDefaultPropertyValues_NotUapPlatform() - { - using (HttpClientHandler handler = CreateHttpClientHandler()) - { - // Same as .NET Framework (Desktop). - Assert.Equal(64, handler.MaxResponseHeadersLength); - Assert.False(handler.PreAuthenticate); - Assert.True(handler.SupportsProxy); - Assert.True(handler.SupportsRedirectConfiguration); - - // Changes from .NET Framework (Desktop). - if (!PlatformDetection.IsFullFramework) - { - Assert.False(handler.CheckCertificateRevocationList); - Assert.Equal(0, handler.MaxRequestContentBufferSize); - Assert.Equal(SslProtocols.None, handler.SslProtocols); - } - } - } - - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsUap))] - public void Ctor_ExpectedDefaultPropertyValues_UapPlatform() - { - using (HttpClientHandler handler = CreateHttpClientHandler()) - { - Assert.True(handler.CheckCertificateRevocationList); - Assert.Equal(0, handler.MaxRequestContentBufferSize); - Assert.Equal(-1, handler.MaxResponseHeadersLength); - Assert.True(handler.PreAuthenticate); - Assert.Equal(SslProtocols.None, handler.SslProtocols); - Assert.False(handler.SupportsProxy); - Assert.False(handler.SupportsRedirectConfiguration); - } - } - - [Fact] - public void Credentials_SetGet_Roundtrips() - { - using (HttpClientHandler handler = CreateHttpClientHandler()) - { - var creds = new NetworkCredential("username", "password", "domain"); - - handler.Credentials = null; - Assert.Null(handler.Credentials); - - handler.Credentials = creds; - Assert.Same(creds, handler.Credentials); - - handler.Credentials = CredentialCache.DefaultCredentials; - Assert.Same(CredentialCache.DefaultCredentials, handler.Credentials); - } - } - - [Theory] - [InlineData(-1)] - [InlineData(0)] - public void MaxAutomaticRedirections_InvalidValue_Throws(int redirects) - { - using (HttpClientHandler handler = CreateHttpClientHandler()) - { - Assert.Throws(() => handler.MaxAutomaticRedirections = redirects); - } - } - - [Theory] - [InlineData(-1)] - [InlineData((long)int.MaxValue + (long)1)] - public void MaxRequestContentBufferSize_SetInvalidValue_ThrowsArgumentOutOfRangeException(long value) - { - using (HttpClientHandler handler = CreateHttpClientHandler()) - { - Assert.Throws(() => handler.MaxRequestContentBufferSize = value); - } - } - - [SkipOnTargetFramework(TargetFrameworkMonikers.Uap, "UAP will send default credentials based on other criteria.")] - [Theory] - [InlineData(false)] - [InlineData(true)] - public async Task UseDefaultCredentials_SetToFalseAndServerNeedsAuth_StatusCodeUnauthorized(bool useProxy) - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.UseProxy = useProxy; - handler.UseDefaultCredentials = false; - using (var client = new HttpClient(handler)) - { - Uri uri = Configuration.Http.NegotiateAuthUriForDefaultCreds(secure: false); - _output.WriteLine("Uri: {0}", uri); - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); - } - } - } - - [Fact] - public void Properties_Get_CountIsZero() - { - using (HttpClientHandler handler = CreateHttpClientHandler()) - { - IDictionary dict = handler.Properties; - Assert.Same(dict, handler.Properties); - Assert.Equal(0, dict.Count); - } - } - - [Fact] - public void Properties_AddItemToDictionary_ItemPresent() - { - using (HttpClientHandler handler = CreateHttpClientHandler()) - { - IDictionary dict = handler.Properties; - - var item = new Object(); - dict.Add("item", item); - - object value; - Assert.True(dict.TryGetValue("item", out value)); - Assert.Equal(item, value); - } - } - - [OuterLoop] // TODO: Issue #11345 - [Theory, MemberData(nameof(EchoServers))] - public async Task SendAsync_SimpleGet_Success(Uri remoteServer) - { - using (HttpClient client = CreateHttpClient()) - using (HttpResponseMessage response = await client.GetAsync(remoteServer)) - { - string responseContent = await response.Content.ReadAsStringAsync(); - _output.WriteLine(responseContent); - TestHelper.VerifyResponseBody( - responseContent, - response.Content.Headers.ContentMD5, - false, - null); - } - } - - [ActiveIssue(22158, TargetFrameworkMonikers.Uap)] - [OuterLoop] // TODO: Issue #11345 - [Fact] - public async Task GetAsync_IPv6LinkLocalAddressUri_Success() - { - using (HttpClient client = CreateHttpClient()) - { - var options = new LoopbackServer.Options { Address = LoopbackServer.GetIPv6LinkLocalAddress() }; - await LoopbackServer.CreateServerAsync(async (server, url) => - { - _output.WriteLine(url.ToString()); - await TestHelper.WhenAllCompletedOrAnyFailed( - LoopbackServer.ReadRequestAndSendResponseAsync(server, options: options), - client.GetAsync(url)); - }, options); - } - } - - [OuterLoop] // TODO: Issue #11345 - [Theory] - [MemberData(nameof(GetAsync_IPBasedUri_Success_MemberData))] - public async Task GetAsync_IPBasedUri_Success(IPAddress address) - { - using (HttpClient client = CreateHttpClient()) - { - var options = new LoopbackServer.Options { Address = address }; - await LoopbackServer.CreateServerAsync(async (server, url) => - { - _output.WriteLine(url.ToString()); - await TestHelper.WhenAllCompletedOrAnyFailed( - LoopbackServer.ReadRequestAndSendResponseAsync(server, options: options), - client.GetAsync(url)); - }, options); - } - } - - public static IEnumerable GetAsync_IPBasedUri_Success_MemberData() - { - foreach (var addr in new[] { IPAddress.Loopback, IPAddress.IPv6Loopback }) - { - if (addr != null) - { - yield return new object[] { addr }; - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Fact] - public async Task SendAsync_MultipleRequestsReusingSameClient_Success() - { - using (HttpClient client = CreateHttpClient()) - { - for (int i = 0; i < 3; i++) - { - using (HttpResponseMessage response = await client.GetAsync(Configuration.Http.RemoteEchoServer)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Fact] - public async Task GetAsync_ResponseContentAfterClientAndHandlerDispose_Success() - { - using (HttpClient client = CreateHttpClient()) - using (HttpResponseMessage response = await client.GetAsync(Configuration.Http.SecureRemoteEchoServer)) - { - client.Dispose(); - Assert.NotNull(response); - string responseContent = await response.Content.ReadAsStringAsync(); - _output.WriteLine(responseContent); - TestHelper.VerifyResponseBody(responseContent, response.Content.Headers.ContentMD5, false, null); - } - } - - [OuterLoop] // TODO: Issue #11345 - [Fact] - public async Task SendAsync_Cancel_CancellationTokenPropagates() - { - var cts = new CancellationTokenSource(); - cts.Cancel(); - using (HttpClient client = CreateHttpClient()) - { - var request = new HttpRequestMessage(HttpMethod.Post, Configuration.Http.RemoteEchoServer); - OperationCanceledException ex = await Assert.ThrowsAnyAsync(() => - client.SendAsync(request, cts.Token)); - Assert.True(cts.Token.IsCancellationRequested, "cts token IsCancellationRequested"); - if (!PlatformDetection.IsFullFramework) - { - // .NET Framework has bug where it doesn't propagate token information. - Assert.True(ex.CancellationToken.IsCancellationRequested, "exception token IsCancellationRequested"); - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Theory, MemberData(nameof(CompressedServers))] - public async Task GetAsync_SetAutomaticDecompression_ContentDecompressed(Uri server) - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; - using (var client = new HttpClient(handler)) - { - using (HttpResponseMessage response = await client.GetAsync(server)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - string responseContent = await response.Content.ReadAsStringAsync(); - _output.WriteLine(responseContent); - TestHelper.VerifyResponseBody( - responseContent, - response.Content.Headers.ContentMD5, - false, - null); - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Theory, MemberData(nameof(CompressedServers))] - public async Task GetAsync_SetAutomaticDecompression_HeadersRemoved(Uri server) - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; - using (var client = new HttpClient(handler)) - using (HttpResponseMessage response = await client.GetAsync(server, HttpCompletionOption.ResponseHeadersRead)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - Assert.False(response.Content.Headers.Contains("Content-Encoding"), "Content-Encoding unexpectedly found"); - Assert.False(response.Content.Headers.Contains("Content-Length"), "Content-Length unexpectedly found"); - } - } - - [OuterLoop] // TODO: Issue #11345 - [Fact] - public async Task GetAsync_ServerNeedsBasicAuthAndSetDefaultCredentials_StatusCodeUnauthorized() - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.Credentials = CredentialCache.DefaultCredentials; - using (var client = new HttpClient(handler)) - { - Uri uri = Configuration.Http.BasicAuthUriForCreds(secure: false, userName: Username, password: Password); - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Fact] - public async Task GetAsync_ServerNeedsAuthAndSetCredential_StatusCodeOK() - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.Credentials = _credential; - using (var client = new HttpClient(handler)) - { - Uri uri = Configuration.Http.BasicAuthUriForCreds(secure: false, userName: Username, password: Password); - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Fact] - public void GetAsync_ServerNeedsAuthAndNoCredential_StatusCodeUnauthorized() - { - // UAP HTTP stack caches connections per-process. This causes interference when these tests run in - // the same process as the other tests. Each test needs to be isolated to its own process. - // See dicussion: https://github.com/dotnet/corefx/issues/21945 - RemoteInvoke(async useManagedHandlerString => - { - using (var client = CreateHttpClient(useManagedHandlerString)) - { - Uri uri = Configuration.Http.BasicAuthUriForCreds(secure: false, userName: Username, password: Password); - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); - } - - return SuccessExitCode; - } - }, UseManagedHandler.ToString()).Dispose(); - } - - [OuterLoop] // TODO: Issue #11345 - [Theory] - [InlineData("WWW-Authenticate: CustomAuth\r\n")] - [InlineData("")] // RFC7235 requires servers to send this header with 401 but some servers don't. - public async Task GetAsync_ServerNeedsNonStandardAuthAndSetCredential_StatusCodeUnauthorized(string authHeaders) - { - string responseHeaders = - $"HTTP/1.1 401 Unauthorized\r\nDate: {DateTimeOffset.UtcNow:R}\r\n{authHeaders}Content-Length: 0\r\n\r\n"; - _output.WriteLine(responseHeaders); - await LoopbackServer.CreateServerAsync(async (server, url) => - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.Credentials = new NetworkCredential("unused", "unused"); - using (var client = new HttpClient(handler)) - { - Task getResponseTask = client.GetAsync(url); - Task> serverTask = LoopbackServer.ReadRequestAndSendResponseAsync(server, responseHeaders); - - await TestHelper.WhenAllCompletedOrAnyFailed(getResponseTask, serverTask); - using (HttpResponseMessage response = await getResponseTask) - { - Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); - } - } - }); - } - - [OuterLoop] // TODO: Issue #11345 - [Theory, MemberData(nameof(RedirectStatusCodes))] - public async Task GetAsync_AllowAutoRedirectFalse_RedirectFromHttpToHttp_StatusCodeRedirect(int statusCode) - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AllowAutoRedirect = false; - using (var client = new HttpClient(handler)) - { - Uri uri = Configuration.Http.RedirectUriForDestinationUri( - secure: false, - statusCode: statusCode, - destinationUri: Configuration.Http.RemoteEchoServer, - hops: 1); - _output.WriteLine("Uri: {0}", uri); - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - Assert.Equal(statusCode, (int)response.StatusCode); - Assert.Equal(uri, response.RequestMessage.RequestUri); - } - } - } - - [ActiveIssue(23769)] - [ActiveIssue(22707, TestPlatforms.AnyUnix)] - [OuterLoop] // TODO: Issue #11345 - [Theory, MemberData(nameof(RedirectStatusCodesOldMethodsNewMethods))] - public async Task AllowAutoRedirect_True_ValidateNewMethodUsedOnRedirection( - int statusCode, string oldMethod, string newMethod) - { - HttpClientHandler handler = CreateHttpClientHandler(); - using (var client = new HttpClient(handler)) - { - await LoopbackServer.CreateServerAsync(async (origServer, origUrl) => - { - var request = new HttpRequestMessage(new HttpMethod(oldMethod), origUrl); - - Task getResponseTask = client.SendAsync(request); - - Task> serverTask = LoopbackServer.ReadRequestAndSendResponseAsync(origServer, - $"HTTP/1.1 {statusCode} OK\r\n" + - $"Date: {DateTimeOffset.UtcNow:R}\r\n" + - $"Location: {origUrl}\r\n" + - "\r\n"); - await Task.WhenAny(getResponseTask, serverTask); - Assert.False(getResponseTask.IsCompleted, $"{getResponseTask.Status}: {getResponseTask.Exception}"); - await serverTask; - - serverTask = LoopbackServer.ReadRequestAndSendResponseAsync(origServer, - $"HTTP/1.1 200 OK\r\n" + - $"Date: {DateTimeOffset.UtcNow:R}\r\n" + - "\r\n"); - await TestHelper.WhenAllCompletedOrAnyFailed(getResponseTask, serverTask); - - List receivedRequest = await serverTask; - string[] statusLineParts = receivedRequest[0].Split(' '); - - using (HttpResponseMessage response = await getResponseTask) - { - Assert.Equal(200, (int)response.StatusCode); - Assert.Equal(newMethod, statusLineParts[0]); - } - }); - } - } - - [OuterLoop] // TODO: Issue #11345 - [Theory, MemberData(nameof(RedirectStatusCodes))] - public async Task GetAsync_AllowAutoRedirectTrue_RedirectFromHttpToHttp_StatusCodeOK(int statusCode) - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AllowAutoRedirect = true; - using (var client = new HttpClient(handler)) - { - Uri uri = Configuration.Http.RedirectUriForDestinationUri( - secure: false, - statusCode: statusCode, - destinationUri: Configuration.Http.RemoteEchoServer, - hops: 1); - _output.WriteLine("Uri: {0}", uri); - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(Configuration.Http.RemoteEchoServer, response.RequestMessage.RequestUri); - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Fact] - public async Task GetAsync_AllowAutoRedirectTrue_RedirectFromHttpToHttps_StatusCodeOK() - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AllowAutoRedirect = true; - using (var client = new HttpClient(handler)) - { - Uri uri = Configuration.Http.RedirectUriForDestinationUri( - secure: false, - statusCode: 302, - destinationUri: Configuration.Http.SecureRemoteEchoServer, - hops: 1); - _output.WriteLine("Uri: {0}", uri); - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(Configuration.Http.SecureRemoteEchoServer, response.RequestMessage.RequestUri); - } - } - } - - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, ".NET Framework allows HTTPS to HTTP redirection")] - [OuterLoop] // TODO: Issue #11345 - [Fact] - public async Task GetAsync_AllowAutoRedirectTrue_RedirectFromHttpsToHttp_StatusCodeRedirect() - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AllowAutoRedirect = true; - using (var client = new HttpClient(handler)) - { - Uri uri = Configuration.Http.RedirectUriForDestinationUri( - secure: true, - statusCode: 302, - destinationUri: Configuration.Http.RemoteEchoServer, - hops: 1); - _output.WriteLine("Uri: {0}", uri); - - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); - Assert.Equal(uri, response.RequestMessage.RequestUri); - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Fact] - public async Task GetAsync_AllowAutoRedirectTrue_RedirectToUriWithParams_RequestMsgUriSet() - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AllowAutoRedirect = true; - Uri targetUri = Configuration.Http.BasicAuthUriForCreds(secure: false, userName: Username, password: Password); - using (var client = new HttpClient(handler)) - { - Uri uri = Configuration.Http.RedirectUriForDestinationUri( - secure: false, - statusCode: 302, - destinationUri: targetUri, - hops: 1); - _output.WriteLine("Uri: {0}", uri); - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); - Assert.Equal(targetUri, response.RequestMessage.RequestUri); - } - } - } - - [SkipOnTargetFramework(TargetFrameworkMonikers.Uap, "Not currently supported on UAP")] - [OuterLoop] // TODO: Issue #11345 - [Theory] - [InlineData(3, 2)] - [InlineData(3, 3)] - [InlineData(3, 4)] - public async Task GetAsync_MaxAutomaticRedirectionsNServerHops_ThrowsIfTooMany(int maxHops, int hops) - { - if (PlatformDetection.IsWindows && !PlatformDetection.IsWindows10Version1703OrGreater) - { - // Skip this test if running on Windows but on a release prior to Windows 10 Creators Update. - _output.WriteLine("Skipping test due to Windows 10 version prior to Version 1703."); - return; - } - else if (PlatformDetection.IsFullFramework) - { - // Skip this test if running on .NET Framework. Exceeding max redirections will not throw - // exception. Instead, it simply returns the 3xx response. - _output.WriteLine("Skipping test on .NET Framework due to behavior difference."); - return; - } - - HttpClientHandler handler = CreateHttpClientHandler(); - handler.MaxAutomaticRedirections = maxHops; - using (var client = new HttpClient(handler)) - { - Task t = client.GetAsync(Configuration.Http.RedirectUriForDestinationUri( - secure: false, - statusCode: 302, - destinationUri: Configuration.Http.RemoteEchoServer, - hops: hops)); - - if (hops <= maxHops) - { - using (HttpResponseMessage response = await t) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(Configuration.Http.RemoteEchoServer, response.RequestMessage.RequestUri); - } - } - else - { - await Assert.ThrowsAsync(() => t); - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Fact] - public async Task GetAsync_AllowAutoRedirectTrue_RedirectWithRelativeLocation() - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AllowAutoRedirect = true; - using (var client = new HttpClient(handler)) - { - Uri uri = Configuration.Http.RedirectUriForDestinationUri( - secure: false, - statusCode: 302, - destinationUri: Configuration.Http.RemoteEchoServer, - hops: 1, - relative: true); - _output.WriteLine("Uri: {0}", uri); - - using (HttpResponseMessage response = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(Configuration.Http.RemoteEchoServer, response.RequestMessage.RequestUri); - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Theory] - [InlineData(200)] - [InlineData(201)] - [InlineData(400)] - public async Task GetAsync_AllowAutoRedirectTrue_NonRedirectStatusCode_LocationHeader_NoRedirect(int statusCode) - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AllowAutoRedirect = true; - using (var client = new HttpClient(handler)) - { - await LoopbackServer.CreateServerAsync(async (origServer, origUrl) => - { - await LoopbackServer.CreateServerAsync(async (redirectServer, redirectUrl) => - { - Task getResponseTask = client.GetAsync(origUrl); - - Task redirectTask = LoopbackServer.ReadRequestAndSendResponseAsync(redirectServer); - - await TestHelper.WhenAllCompletedOrAnyFailed( - getResponseTask, - LoopbackServer.ReadRequestAndSendResponseAsync(origServer, - $"HTTP/1.1 {statusCode} OK\r\n" + - $"Date: {DateTimeOffset.UtcNow:R}\r\n" + - $"Location: {redirectUrl}\r\n" + - "\r\n")); - - using (HttpResponseMessage response = await getResponseTask) - { - Assert.Equal(statusCode, (int)response.StatusCode); - Assert.Equal(origUrl, response.RequestMessage.RequestUri); - Assert.False(redirectTask.IsCompleted, "Should not have redirected to Location"); - } - }); - }); - } - } - - [OuterLoop] // TODO: Issue #11345 - [ConditionalTheory(nameof(IsNotWindows7))] // Skip test on Win7 since WinHTTP has bugs w/ fragments. - [InlineData("#origFragment", "", "#origFragment", false)] - [InlineData("#origFragment", "", "#origFragment", true)] - [InlineData("", "#redirFragment", "#redirFragment", false)] - [InlineData("", "#redirFragment", "#redirFragment", true)] - [InlineData("#origFragment", "#redirFragment", "#redirFragment", false)] - [InlineData("#origFragment", "#redirFragment", "#redirFragment", true)] - public async Task GetAsync_AllowAutoRedirectTrue_RetainsOriginalFragmentIfAppropriate( - string origFragment, string redirFragment, string expectedFragment, bool useRelativeRedirect) - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.AllowAutoRedirect = true; - using (var client = new HttpClient(handler)) - { - await LoopbackServer.CreateServerAsync(async (origServer, origUrl) => - { - origUrl = new Uri(origUrl.ToString() + origFragment); - Uri redirectUrl = useRelativeRedirect ? - new Uri(origUrl.PathAndQuery + redirFragment, UriKind.Relative) : - new Uri(origUrl.ToString() + redirFragment); - Uri expectedUrl = new Uri(origUrl.ToString() + expectedFragment); - - Task getResponse = client.GetAsync(origUrl); - Task firstRequest = LoopbackServer.ReadRequestAndSendResponseAsync(origServer, - $"HTTP/1.1 302 OK\r\n" + - $"Date: {DateTimeOffset.UtcNow:R}\r\n" + - $"Location: {redirectUrl}\r\n" + - "\r\n"); - Assert.Equal(firstRequest, await Task.WhenAny(firstRequest, getResponse)); - - Task secondRequest = LoopbackServer.ReadRequestAndSendResponseAsync(origServer, - $"HTTP/1.1 200 OK\r\n" + - $"Date: {DateTimeOffset.UtcNow:R}\r\n" + - "\r\n"); - await TestHelper.WhenAllCompletedOrAnyFailed(secondRequest, getResponse); - - using (HttpResponseMessage response = await getResponse) - { - Assert.Equal(200, (int)response.StatusCode); - Assert.Equal(expectedUrl, response.RequestMessage.RequestUri); - } - }); - } - } - - [Fact] - public async Task GetAsync_CredentialIsNetworkCredentialUriRedirect_StatusCodeUnauthorized() - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.Credentials = _credential; - using (var client = new HttpClient(handler)) - { - Uri redirectUri = Configuration.Http.RedirectUriForCreds( - secure: false, - statusCode: 302, - userName: Username, - password: Password); - using (HttpResponseMessage unAuthResponse = await client.GetAsync(redirectUri)) - { - Assert.Equal(HttpStatusCode.Unauthorized, unAuthResponse.StatusCode); - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Theory, MemberData(nameof(RedirectStatusCodes))] - public async Task GetAsync_CredentialIsCredentialCacheUriRedirect_StatusCodeOK(int statusCode) - { - Uri uri = Configuration.Http.BasicAuthUriForCreds(secure: false, userName: Username, password: Password); - Uri redirectUri = Configuration.Http.RedirectUriForCreds( - secure: false, - statusCode: statusCode, - userName: Username, - password: Password); - _output.WriteLine(uri.AbsoluteUri); - _output.WriteLine(redirectUri.AbsoluteUri); - var credentialCache = new CredentialCache(); - credentialCache.Add(uri, "Basic", _credential); - - HttpClientHandler handler = CreateHttpClientHandler(); - if (PlatformDetection.IsUap) - { - // UAP does not support CredentialCache for Credentials. - Assert.Throws(() => handler.Credentials = credentialCache); - } - else - { - handler.Credentials = credentialCache; - using (var client = new HttpClient(handler)) - { - using (HttpResponseMessage response = await client.GetAsync(redirectUri)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(uri, response.RequestMessage.RequestUri); - } - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Fact] - public async Task GetAsync_DefaultCoookieContainer_NoCookieSent() - { - using (HttpClient client = CreateHttpClient()) - { - using (HttpResponseMessage httpResponse = await client.GetAsync(Configuration.Http.RemoteEchoServer)) - { - string responseText = await httpResponse.Content.ReadAsStringAsync(); - _output.WriteLine(responseText); - Assert.False(TestHelper.JsonMessageContainsKey(responseText, "Cookie")); - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Theory] - [InlineData("cookieName1", "cookieValue1")] - public async Task GetAsync_SetCookieContainer_CookieSent(string cookieName, string cookieValue) - { - HttpClientHandler handler = CreateHttpClientHandler(); - var cookieContainer = new CookieContainer(); - cookieContainer.Add(Configuration.Http.RemoteEchoServer, new Cookie(cookieName, cookieValue)); - handler.CookieContainer = cookieContainer; - using (var client = new HttpClient(handler)) - { - using (HttpResponseMessage httpResponse = await client.GetAsync(Configuration.Http.RemoteEchoServer)) - { - Assert.Equal(HttpStatusCode.OK, httpResponse.StatusCode); - string responseText = await httpResponse.Content.ReadAsStringAsync(); - _output.WriteLine(responseText); - Assert.True(TestHelper.JsonMessageContainsKeyValue(responseText, cookieName, cookieValue)); - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Theory] - [InlineData("cookieName1", "cookieValue1")] - public async Task GetAsync_RedirectResponseHasCookie_CookieSentToFinalUri(string cookieName, string cookieValue) - { - Uri uri = Configuration.Http.RedirectUriForDestinationUri( - secure: false, - statusCode: 302, - destinationUri: Configuration.Http.RemoteEchoServer, - hops: 1); - using (HttpClient client = CreateHttpClient()) - { - client.DefaultRequestHeaders.Add( - "X-SetCookie", - string.Format("{0}={1};Path=/", cookieName, cookieValue)); - using (HttpResponseMessage httpResponse = await client.GetAsync(uri)) - { - string responseText = await httpResponse.Content.ReadAsStringAsync(); - _output.WriteLine(responseText); - Assert.True(TestHelper.JsonMessageContainsKeyValue(responseText, cookieName, cookieValue)); - } - } - } - - [ActiveIssue(22187, TargetFrameworkMonikers.Uap)] - [OuterLoop] // TODO: Issue #11345 - [Theory, MemberData(nameof(HeaderWithEmptyValueAndUris))] - public async Task GetAsync_RequestHeadersAddCustomHeaders_HeaderAndEmptyValueSent(string name, string value, Uri uri) - { - using (HttpClient client = CreateHttpClient()) - { - _output.WriteLine($"name={name}, value={value}"); - client.DefaultRequestHeaders.Add(name, value); - using (HttpResponseMessage httpResponse = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.OK, httpResponse.StatusCode); - string responseText = await httpResponse.Content.ReadAsStringAsync(); - _output.WriteLine(responseText); - Assert.True(TestHelper.JsonMessageContainsKeyValue(responseText, name, value)); - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Theory, MemberData(nameof(HeaderValueAndUris))] - public async Task GetAsync_RequestHeadersAddCustomHeaders_HeaderAndValueSent(string name, string value, Uri uri) - { - using (HttpClient client = CreateHttpClient()) - { - _output.WriteLine($"name={name}, value={value}"); - client.DefaultRequestHeaders.Add(name, value); - using (HttpResponseMessage httpResponse = await client.GetAsync(uri)) - { - Assert.Equal(HttpStatusCode.OK, httpResponse.StatusCode); - string responseText = await httpResponse.Content.ReadAsStringAsync(); - _output.WriteLine(responseText); - Assert.True(TestHelper.JsonMessageContainsKeyValue(responseText, name, value)); - } - } - } - - private static KeyValuePair GenerateCookie(string name, char repeat, int overallHeaderValueLength) - { - string emptyHeaderValue = $"{name}=; Path=/"; - - Debug.Assert(overallHeaderValueLength > emptyHeaderValue.Length); - - int valueCount = overallHeaderValueLength - emptyHeaderValue.Length; - string value = new string(repeat, valueCount); - - return new KeyValuePair(name, value); - } - - public static object[][] CookieNameValues = - { - // WinHttpHandler calls WinHttpQueryHeaders to iterate through multiple Set-Cookie header values, - // using an initial buffer size of 128 chars. If the buffer is not large enough, WinHttpQueryHeaders - // returns an insufficient buffer error, allowing WinHttpHandler to try again with a larger buffer. - // Sometimes when WinHttpQueryHeaders fails due to insufficient buffer, it still advances the - // iteration index, which would cause header values to be missed if not handled correctly. - // - // In particular, WinHttpQueryHeader behaves as follows for the following header value lengths: - // * 0-127 chars: succeeds, index advances from 0 to 1. - // * 128-255 chars: fails due to insufficient buffer, index advances from 0 to 1. - // * 256+ chars: fails due to insufficient buffer, index stays at 0. - // - // The below overall header value lengths were chosen to exercise reading header values at these - // edges, to ensure WinHttpHandler does not miss multiple Set-Cookie headers. - - new object[] { GenerateCookie(name: "foo", repeat: 'a', overallHeaderValueLength: 126) }, - new object[] { GenerateCookie(name: "foo", repeat: 'a', overallHeaderValueLength: 127) }, - new object[] { GenerateCookie(name: "foo", repeat: 'a', overallHeaderValueLength: 128) }, - new object[] { GenerateCookie(name: "foo", repeat: 'a', overallHeaderValueLength: 129) }, - - new object[] { GenerateCookie(name: "foo", repeat: 'a', overallHeaderValueLength: 254) }, - new object[] { GenerateCookie(name: "foo", repeat: 'a', overallHeaderValueLength: 255) }, - new object[] { GenerateCookie(name: "foo", repeat: 'a', overallHeaderValueLength: 256) }, - new object[] { GenerateCookie(name: "foo", repeat: 'a', overallHeaderValueLength: 257) }, - - new object[] - { - new KeyValuePair( - ".AspNetCore.Antiforgery.Xam7_OeLcN4", - "CfDJ8NGNxAt7CbdClq3UJ8_6w_4661wRQZT1aDtUOIUKshbcV4P0NdS8klCL5qGSN-PNBBV7w23G6MYpQ81t0PMmzIN4O04fqhZ0u1YPv66mixtkX3iTi291DgwT3o5kozfQhe08-RAExEmXpoCbueP_QYM") - } - }; - - [OuterLoop] // TODO: Issue #11345 - [Theory] - [MemberData(nameof(CookieNameValues))] - public async Task GetAsync_ResponseWithSetCookieHeaders_AllCookiesRead(KeyValuePair cookie1) - { - var cookie2 = new KeyValuePair(".AspNetCore.Session", "RAExEmXpoCbueP_QYM"); - var cookie3 = new KeyValuePair("name", "value"); - - await LoopbackServer.CreateServerAsync(async (server, url) => - { - using (HttpClientHandler handler = CreateHttpClientHandler()) - using (var client = new HttpClient(handler)) - { - Task getResponseTask = client.GetAsync(url); - await TestHelper.WhenAllCompletedOrAnyFailed( - getResponseTask, - LoopbackServer.ReadRequestAndSendResponseAsync(server, - $"HTTP/1.1 200 OK\r\n" + - $"Date: {DateTimeOffset.UtcNow:R}\r\n" + - $"Set-Cookie: {cookie1.Key}={cookie1.Value}; Path=/\r\n" + - $"Set-Cookie : {cookie2.Key}={cookie2.Value}; Path=/\r\n" + // space before colon to verify header is trimmed and recognized - $"Set-Cookie: {cookie3.Key}={cookie3.Value}; Path=/\r\n" + - "\r\n")); - - using (HttpResponseMessage response = await getResponseTask) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - CookieCollection cookies = handler.CookieContainer.GetCookies(url); - - Assert.Equal(3, cookies.Count); - Assert.Equal(cookie1.Value, cookies[cookie1.Key].Value); - Assert.Equal(cookie2.Value, cookies[cookie2.Key].Value); - Assert.Equal(cookie3.Value, cookies[cookie3.Key].Value); - } - } - }); - } - - [OuterLoop] // TODO: Issue #11345 - [ActiveIssue(17174, TestPlatforms.AnyUnix)] // https://github.com/curl/curl/issues/1354 - [Theory] - [InlineData(false)] - [InlineData(true)] - public async Task GetAsync_TrailingHeaders_Ignored(bool includeTrailerHeader) - { - if (UseManagedHandler) - { - // TODO #23130: The managed handler isn't correctly handling trailing headers. - return; - } - - await LoopbackServer.CreateServerAsync(async (server, url) => - { - using (HttpClientHandler handler = CreateHttpClientHandler()) - using (var client = new HttpClient(handler)) - { - Task getResponseTask = client.GetAsync(url); - await TestHelper.WhenAllCompletedOrAnyFailed( - getResponseTask, - LoopbackServer.ReadRequestAndSendResponseAsync(server, - "HTTP/1.1 200 OK\r\n" + - "Transfer-Encoding: chunked\r\n" + - (includeTrailerHeader ? "Trailer: MyCoolTrailerHeader\r\n" : "") + - "\r\n" + - "4\r\n" + - "data\r\n" + - "0\r\n" + - "MyCoolTrailerHeader: amazingtrailer\r\n" + - "\r\n")); - - using (HttpResponseMessage response = await getResponseTask) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - if (includeTrailerHeader) - { - Assert.Contains("MyCoolTrailerHeader", response.Headers.GetValues("Trailer")); - } - Assert.False(response.Headers.Contains("MyCoolTrailerHeader"), "Trailer should have been ignored"); - } - } - }); - } - - [OuterLoop] // TODO: Issue #11345 - [Theory] - [InlineData("")] // missing size - [InlineData("10000000000000000")] // overflowing size - public async Task GetAsync_InvalidChunkSize_ThrowsHttpRequestException(string chunkSize) - { - await LoopbackServer.CreateServerAsync(async (server, url) => - { - using (HttpClient client = CreateHttpClient()) - { - string partialResponse = "HTTP/1.1 200 OK\r\n" + - "Transfer-Encoding: chunked\r\n" + - "\r\n" + - $"{chunkSize}\r\n"; - - var tcs = new TaskCompletionSource(); - Task serverTask = - LoopbackServer.AcceptSocketAsync(server, async (s, stream, reader, writer) => - { - var list = await LoopbackServer.ReadWriteAcceptedAsync(s, reader, writer, partialResponse, shutdown: false); - await tcs.Task; - return list; - }, null); - - await Assert.ThrowsAsync(() => client.GetAsync(url)); - tcs.SetResult(true); - await serverTask; - } - }); - } - - [OuterLoop] // TODO: Issue #11345 - [Fact] - public async Task GetAsync_ResponseHeadersRead_ReadFromEachIterativelyDoesntDeadlock() - { - using (HttpClient client = CreateHttpClient()) - { - const int NumGets = 5; - Task[] responseTasks = (from _ in Enumerable.Range(0, NumGets) - select client.GetAsync(Configuration.Http.RemoteEchoServer, HttpCompletionOption.ResponseHeadersRead)).ToArray(); - for (int i = responseTasks.Length - 1; i >= 0; i--) // read backwards to increase likelihood that we wait on a different task than has data available - { - using (HttpResponseMessage response = await responseTasks[i]) - { - string responseContent = await response.Content.ReadAsStringAsync(); - _output.WriteLine(responseContent); - TestHelper.VerifyResponseBody( - responseContent, - response.Content.Headers.ContentMD5, - false, - null); - } - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Fact] - public async Task SendAsync_HttpRequestMsgResponseHeadersRead_StatusCodeOK() - { - HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, Configuration.Http.SecureRemoteEchoServer); - using (HttpClient client = CreateHttpClient()) - { - using (HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)) - { - string responseContent = await response.Content.ReadAsStringAsync(); - _output.WriteLine(responseContent); - TestHelper.VerifyResponseBody( - responseContent, - response.Content.Headers.ContentMD5, - false, - null); - } - } - } - - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "netfx's ConnectStream.ReadAsync tries to read beyond data already buffered, causing hangs #18864")] - [OuterLoop] // TODO: Issue #11345 - [Fact] - public async Task SendAsync_ReadFromSlowStreamingServer_PartialDataReturned() - { - await LoopbackServer.CreateServerAsync(async (server, url) => - { - using (HttpClient client = CreateHttpClient()) - { - Task getResponse = client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead); - - await LoopbackServer.AcceptSocketAsync(server, async (s, stream, reader, writer) => - { - while (!string.IsNullOrEmpty(reader.ReadLine())) ; - await writer.WriteAsync( - "HTTP/1.1 200 OK\r\n" + - $"Date: {DateTimeOffset.UtcNow:R}\r\n" + - "Content-Length: 16000\r\n" + - "\r\n" + - "less than 16000 bytes"); - - using (HttpResponseMessage response = await getResponse) - { - var buffer = new byte[8000]; - using (Stream clientStream = await response.Content.ReadAsStreamAsync()) - { - int bytesRead = await clientStream.ReadAsync(buffer, 0, buffer.Length); - _output.WriteLine($"Bytes read from stream: {bytesRead}"); - Assert.True(bytesRead < buffer.Length, "bytesRead should be less than buffer.Length"); - } - } - - return null; - }); - } - }); - } - - [OuterLoop] // TODO: Issue #11345 - [Fact] - public async Task Dispose_DisposingHandlerCancelsActiveOperationsWithoutResponses() - { - if (UseManagedHandler) - { - // TODO #23131: The ManagedHandler isn't correctly handling disposal of the handler. - // It should cause the outstanding requests to be canceled with OperationCanceledExceptions, - // whereas currently it's resulting in ObjectDisposedExceptions. - return; - } - - await LoopbackServer.CreateServerAsync(async (socket1, url1) => - { - await LoopbackServer.CreateServerAsync(async (socket2, url2) => - { - await LoopbackServer.CreateServerAsync(async (socket3, url3) => - { - var unblockServers = new TaskCompletionSource(TaskContinuationOptions.RunContinuationsAsynchronously); - - // First server connects but doesn't send any response yet - Task server1 = LoopbackServer.AcceptSocketAsync(socket1, async (s, stream, reader, writer) => - { - await unblockServers.Task; - return null; - }); - - // Second server connects and sends some but not all headers - Task server2 = LoopbackServer.AcceptSocketAsync(socket2, async (s, stream, reader, writer) => - { - while (!string.IsNullOrEmpty(await reader.ReadLineAsync())) ; - await writer.WriteAsync($"HTTP/1.1 200 OK\r\n"); - await unblockServers.Task; - return null; - }); - - // Third server connects and sends all headers and some but not all of the body - Task server3 = LoopbackServer.AcceptSocketAsync(socket3, async (s, stream, reader, writer) => - { - while (!string.IsNullOrEmpty(await reader.ReadLineAsync())) ; - await writer.WriteAsync($"HTTP/1.1 200 OK\r\nDate: {DateTimeOffset.UtcNow:R}\r\nContent-Length: 20\r\n\r\n"); - await writer.WriteAsync("1234567890"); - await unblockServers.Task; - await writer.WriteAsync("1234567890"); - s.Shutdown(SocketShutdown.Send); - return null; - }); - - // Make three requests - Task get1, get2; - HttpResponseMessage response3; - using (HttpClient client = CreateHttpClient()) - { - get1 = client.GetAsync(url1, HttpCompletionOption.ResponseHeadersRead); - get2 = client.GetAsync(url2, HttpCompletionOption.ResponseHeadersRead); - response3 = await client.GetAsync(url3, HttpCompletionOption.ResponseHeadersRead); - } // Dispose the handler while requests are still outstanding - - // Requests 1 and 2 should be canceled as we haven't finished receiving their headers - await Assert.ThrowsAnyAsync(() => get1); - await Assert.ThrowsAnyAsync(() => get2); - - // Request 3 should still be active, and we should be able to receive all of the data. - unblockServers.SetResult(true); - using (response3) - { - Assert.Equal("12345678901234567890", await response3.Content.ReadAsStringAsync()); - } - }); - }); - }); - } - - [OuterLoop] // TODO: Issue #11345 - [Theory] - [InlineData(99)] - [InlineData(1000)] - public async Task GetAsync_StatusCodeOutOfRange_ExpectedException(int statusCode) - { - if (PlatformDetection.IsUap && statusCode == 99) - { - // UAP platform allows this status code due to historical reasons. - return; - } - - await LoopbackServer.CreateServerAsync(async (server, url) => - { - using (HttpClient client = CreateHttpClient()) - { - Task getResponseTask = client.GetAsync(url); - await LoopbackServer.ReadRequestAndSendResponseAsync(server, - $"HTTP/1.1 {statusCode}\r\n" + - $"Date: {DateTimeOffset.UtcNow:R}\r\n" + - "\r\n"); - - await Assert.ThrowsAsync(() => getResponseTask); - } - }); - } - - #region Post Methods Tests - - [OuterLoop] // TODO: Issue #11345 - [Theory, MemberData(nameof(VerifyUploadServers))] - public async Task PostAsync_CallMethodTwice_StringContent(Uri remoteServer) - { - using (HttpClient client = CreateHttpClient()) - { - string data = "Test String"; - var content = new StringContent(data, Encoding.UTF8); - content.Headers.ContentMD5 = TestHelper.ComputeMD5Hash(data); - HttpResponseMessage response; - using (response = await client.PostAsync(remoteServer, content)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } - - // Repeat call. - content = new StringContent(data, Encoding.UTF8); - content.Headers.ContentMD5 = TestHelper.ComputeMD5Hash(data); - using (response = await client.PostAsync(remoteServer, content)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Theory, MemberData(nameof(VerifyUploadServers))] - public async Task PostAsync_CallMethod_UnicodeStringContent(Uri remoteServer) - { - using (HttpClient client = CreateHttpClient()) - { - string data = "\ub4f1\uffc7\u4e82\u67ab4\uc6d4\ud1a0\uc694\uc77c\uffda3\u3155\uc218\uffdb"; - var content = new StringContent(data, Encoding.UTF8); - content.Headers.ContentMD5 = TestHelper.ComputeMD5Hash(data); - - using (HttpResponseMessage response = await client.PostAsync(remoteServer, content)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Theory, MemberData(nameof(VerifyUploadServersStreamsAndExpectedData))] - public async Task PostAsync_CallMethod_StreamContent(Uri remoteServer, HttpContent content, byte[] expectedData) - { - using (HttpClient client = CreateHttpClient()) - { - content.Headers.ContentMD5 = TestHelper.ComputeMD5Hash(expectedData); - - using (HttpResponseMessage response = await client.PostAsync(remoteServer, content)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } - } - } - - private sealed class StreamContentWithSyncAsyncCopy : StreamContent - { - private readonly Stream _stream; - private readonly bool _syncCopy; - - public StreamContentWithSyncAsyncCopy(Stream stream, bool syncCopy) : base(stream) - { - _stream = stream; - _syncCopy = syncCopy; - } - - protected override Task SerializeToStreamAsync(Stream stream, TransportContext context) - { - if (_syncCopy) - { - try - { - _stream.CopyTo(stream, 128); // arbitrary size likely to require multiple read/writes - return Task.CompletedTask; - } - catch (Exception exc) - { - return Task.FromException(exc); - } - } - - return base.SerializeToStreamAsync(stream, context); - } - } - - public static IEnumerable VerifyUploadServersStreamsAndExpectedData - { - get - { - foreach (object[] serverArr in VerifyUploadServers) // target server - foreach (bool syncCopy in new[] { true, false }) // force the content copy to happen via Read/Write or ReadAsync/WriteAsync - { - Uri server = (Uri)serverArr[0]; - - byte[] data = new byte[1234]; - new Random(42).NextBytes(data); - - // A MemoryStream - { - var memStream = new MemoryStream(data, writable: false); - yield return new object[] { server, new StreamContentWithSyncAsyncCopy(memStream, syncCopy: syncCopy), data }; - } - - // A multipart content that provides its own stream from CreateContentReadStreamAsync - { - var mc = new MultipartContent(); - mc.Add(new ByteArrayContent(data)); - var memStream = new MemoryStream(); - mc.CopyToAsync(memStream).GetAwaiter().GetResult(); - yield return new object[] { server, mc, memStream.ToArray() }; - } - - // A stream that provides the data synchronously and has a known length - { - var wrappedMemStream = new MemoryStream(data, writable: false); - var syncKnownLengthStream = new DelegateStream( - canReadFunc: () => wrappedMemStream.CanRead, - canSeekFunc: () => wrappedMemStream.CanSeek, - lengthFunc: () => wrappedMemStream.Length, - positionGetFunc: () => wrappedMemStream.Position, - positionSetFunc: p => wrappedMemStream.Position = p, - readFunc: (buffer, offset, count) => wrappedMemStream.Read(buffer, offset, count), - readAsyncFunc: (buffer, offset, count, token) => wrappedMemStream.ReadAsync(buffer, offset, count, token)); - yield return new object[] { server, new StreamContentWithSyncAsyncCopy(syncKnownLengthStream, syncCopy: syncCopy), data }; - } - - // A stream that provides the data synchronously and has an unknown length - { - int syncUnknownLengthStreamOffset = 0; - - Func readFunc = (buffer, offset, count) => - { - int bytesRemaining = data.Length - syncUnknownLengthStreamOffset; - int bytesToCopy = Math.Min(bytesRemaining, count); - Array.Copy(data, syncUnknownLengthStreamOffset, buffer, offset, bytesToCopy); - syncUnknownLengthStreamOffset += bytesToCopy; - return bytesToCopy; - }; - - var syncUnknownLengthStream = new DelegateStream( - canReadFunc: () => true, - canSeekFunc: () => false, - readFunc: readFunc, - readAsyncFunc: (buffer, offset, count, token) => Task.FromResult(readFunc(buffer, offset, count))); - yield return new object[] { server, new StreamContentWithSyncAsyncCopy(syncUnknownLengthStream, syncCopy: syncCopy), data }; - } - - // A stream that provides the data asynchronously - { - int asyncStreamOffset = 0, maxDataPerRead = 100; - - Func readFunc = (buffer, offset, count) => - { - int bytesRemaining = data.Length - asyncStreamOffset; - int bytesToCopy = Math.Min(bytesRemaining, Math.Min(maxDataPerRead, count)); - Array.Copy(data, asyncStreamOffset, buffer, offset, bytesToCopy); - asyncStreamOffset += bytesToCopy; - return bytesToCopy; - }; - - var asyncStream = new DelegateStream( - canReadFunc: () => true, - canSeekFunc: () => false, - readFunc: readFunc, - readAsyncFunc: async (buffer, offset, count, token) => - { - await Task.Delay(1).ConfigureAwait(false); - return readFunc(buffer, offset, count); - }); - yield return new object[] { server, new StreamContentWithSyncAsyncCopy(asyncStream, syncCopy: syncCopy), data }; - } - - // Providing data from a FormUrlEncodedContent's stream - { - var formContent = new FormUrlEncodedContent(new[] { new KeyValuePair("key", "val") }); - yield return new object[] { server, formContent, Encoding.GetEncoding("iso-8859-1").GetBytes("key=val") }; - } - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Theory, MemberData(nameof(EchoServers))] - public async Task PostAsync_CallMethod_NullContent(Uri remoteServer) - { - using (HttpClient client = CreateHttpClient()) - { - using (HttpResponseMessage response = await client.PostAsync(remoteServer, null)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - string responseContent = await response.Content.ReadAsStringAsync(); - _output.WriteLine(responseContent); - TestHelper.VerifyResponseBody( - responseContent, - response.Content.Headers.ContentMD5, - false, - string.Empty); - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Theory, MemberData(nameof(EchoServers))] - public async Task PostAsync_CallMethod_EmptyContent(Uri remoteServer) - { - using (HttpClient client = CreateHttpClient()) - { - var content = new StringContent(string.Empty); - using (HttpResponseMessage response = await client.PostAsync(remoteServer, content)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - string responseContent = await response.Content.ReadAsStringAsync(); - _output.WriteLine(responseContent); - TestHelper.VerifyResponseBody( - responseContent, - response.Content.Headers.ContentMD5, - false, - string.Empty); - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Theory] - [InlineData(false)] - [InlineData(true)] - [InlineData(null)] - public async Task PostAsync_ExpectContinue_Success(bool? expectContinue) - { - using (HttpClient client = CreateHttpClient()) - { - var req = new HttpRequestMessage(HttpMethod.Post, Configuration.Http.RemoteEchoServer) - { - Content = new StringContent("Test String", Encoding.UTF8) - }; - req.Headers.ExpectContinue = expectContinue; - - using (HttpResponseMessage response = await client.SendAsync(req)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - if (UseManagedHandler) - { - const string ExpectedReqHeader = "\"Expect\": \"100-continue\""; - if (expectContinue == true) - { - Assert.Contains(ExpectedReqHeader, await response.Content.ReadAsStringAsync()); - } - else - { - Assert.DoesNotContain(ExpectedReqHeader, await response.Content.ReadAsStringAsync()); - } - } - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Theory] - [InlineData(false)] - [InlineData(true)] - public async Task PostAsync_Redirect_ResultingGetFormattedCorrectly(bool secure) - { - const string ContentString = "This is the content string."; - var content = new StringContent(ContentString); - Uri redirectUri = Configuration.Http.RedirectUriForDestinationUri( - secure, - 302, - secure ? Configuration.Http.SecureRemoteEchoServer : Configuration.Http.RemoteEchoServer, - 1); - - using (HttpClient client = CreateHttpClient()) - using (HttpResponseMessage response = await client.PostAsync(redirectUri, content)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - string responseContent = await response.Content.ReadAsStringAsync(); - Assert.DoesNotContain(ContentString, responseContent); - Assert.DoesNotContain("Content-Length", responseContent); - } - } - - [ActiveIssue(23768)] - [ActiveIssue(22191, TargetFrameworkMonikers.Uap)] - [OuterLoop] // takes several seconds - [Fact] - public async Task PostAsync_RedirectWith307_LargePayload() - { - await PostAsync_Redirect_LargePayload_Helper(307, true); - } - - [OuterLoop] // takes several seconds - [Fact] - public async Task PostAsync_RedirectWith302_LargePayload() - { - await PostAsync_Redirect_LargePayload_Helper(302, false); - } - - public async Task PostAsync_Redirect_LargePayload_Helper(int statusCode, bool expectRedirectToPost) - { - using (var fs = new FileStream(Path.Combine(Path.GetTempPath(), Path.GetTempFileName()), FileMode.Create, FileAccess.ReadWrite, FileShare.None, 0x1000, FileOptions.DeleteOnClose)) - { - string contentString = string.Join("", Enumerable.Repeat("Content", 100000)); - byte[] contentBytes = Encoding.UTF32.GetBytes(contentString); - fs.Write(contentBytes, 0, contentBytes.Length); - fs.Flush(flushToDisk: true); - fs.Position = 0; - - Uri redirectUri = Configuration.Http.RedirectUriForDestinationUri(false, statusCode, Configuration.Http.SecureRemoteEchoServer, 1); - - using (HttpClient client = CreateHttpClient()) - using (HttpResponseMessage response = await client.PostAsync(redirectUri, new StreamContent(fs))) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - if (expectRedirectToPost) - { - Assert.InRange(response.Content.Headers.ContentLength.GetValueOrDefault(), contentBytes.Length, int.MaxValue); - } - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Theory, MemberData(nameof(EchoServers))] // NOTE: will not work for in-box System.Net.Http.dll due to disposal of request content - public async Task PostAsync_ReuseRequestContent_Success(Uri remoteServer) - { - const string ContentString = "This is the content string."; - using (HttpClient client = CreateHttpClient()) - { - var content = new StringContent(ContentString); - for (int i = 0; i < 2; i++) - { - using (HttpResponseMessage response = await client.PostAsync(remoteServer, content)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Contains(ContentString, await response.Content.ReadAsStringAsync()); - } - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Theory] - [InlineData(HttpStatusCode.MethodNotAllowed, "Custom description")] - [InlineData(HttpStatusCode.MethodNotAllowed, "")] - public async Task GetAsync_CallMethod_ExpectedStatusLine(HttpStatusCode statusCode, string reasonPhrase) - { - using (HttpClient client = CreateHttpClient()) - { - using (HttpResponseMessage response = await client.GetAsync(Configuration.Http.StatusCodeUri( - false, - (int)statusCode, - reasonPhrase))) - { - Assert.Equal(statusCode, response.StatusCode); - Assert.Equal(reasonPhrase, response.ReasonPhrase); - } - } - } - - #endregion - - #region Various HTTP Method Tests - - [OuterLoop] // TODO: Issue #11345 - [Theory, MemberData(nameof(HttpMethods))] - public async Task SendAsync_SendRequestUsingMethodToEchoServerWithNoContent_MethodCorrectlySent( - string method, - bool secureServer) - { - using (HttpClient client = CreateHttpClient()) - { - var request = new HttpRequestMessage( - new HttpMethod(method), - secureServer ? Configuration.Http.SecureRemoteEchoServer : Configuration.Http.RemoteEchoServer); - - if (PlatformDetection.IsUap && method == "TRACE") - { - HttpRequestException ex = await Assert.ThrowsAsync(() => client.SendAsync(request)); - Assert.IsType(ex.InnerException); - } - else - { - using (HttpResponseMessage response = await client.SendAsync(request)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - TestHelper.VerifyRequestMethod(response, method); - } - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Theory, MemberData(nameof(HttpMethodsThatAllowContent))] - public async Task SendAsync_SendRequestUsingMethodToEchoServerWithContent_Success( - string method, - bool secureServer) - { - if (PlatformDetection.IsFullFramework && method == "GET") - { - // .NET Framework doesn't allow a content body with this HTTP verb. - // It will throw a System.Net.ProtocolViolation exception. - return; - } - - using (HttpClient client = CreateHttpClient()) - { - var request = new HttpRequestMessage( - new HttpMethod(method), - secureServer ? Configuration.Http.SecureRemoteEchoServer : Configuration.Http.RemoteEchoServer); - request.Content = new StringContent(ExpectedContent); - using (HttpResponseMessage response = await client.SendAsync(request)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - TestHelper.VerifyRequestMethod(response, method); - string responseContent = await response.Content.ReadAsStringAsync(); - _output.WriteLine(responseContent); - - Assert.Contains($"\"Content-Length\": \"{request.Content.Headers.ContentLength.Value}\"", responseContent); - TestHelper.VerifyResponseBody( - responseContent, - response.Content.Headers.ContentMD5, - false, - ExpectedContent); - } - } - } - - [ActiveIssue(20010, TargetFrameworkMonikers.Uap)] // Test hangs. But this test seems invalid. An HttpRequestMessage can only be sent once. - [OuterLoop] // TODO: Issue #11345 - [Theory] - [InlineData("12345678910", 0)] - [InlineData("12345678910", 5)] - public async Task SendAsync_SendSameRequestMultipleTimesDirectlyOnHandler_Success(string stringContent, int startingPosition) - { - using (var handler = new HttpMessageInvoker(CreateHttpClientHandler())) - { - byte[] byteContent = Encoding.ASCII.GetBytes(stringContent); - var content = new MemoryStream(); - content.Write(byteContent, 0, byteContent.Length); - content.Position = startingPosition; - var request = new HttpRequestMessage(HttpMethod.Post, Configuration.Http.RemoteEchoServer) { Content = new StreamContent(content) }; - - for (int iter = 0; iter < 2; iter++) - { - using (HttpResponseMessage response = await handler.SendAsync(request, CancellationToken.None)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - string responseContent = await response.Content.ReadAsStringAsync(); - - Assert.Contains($"\"Content-Length\": \"{request.Content.Headers.ContentLength.Value}\"", responseContent); - - Assert.Contains(stringContent.Substring(startingPosition), responseContent); - if (startingPosition != 0) - { - Assert.DoesNotContain(stringContent.Substring(0, startingPosition), responseContent); - } - } - } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Theory, MemberData(nameof(HttpMethodsThatDontAllowContent))] - public async Task SendAsync_SendRequestUsingNoBodyMethodToEchoServerWithContent_NoBodySent( - string method, - bool secureServer) - { - if (PlatformDetection.IsFullFramework && method == "HEAD") - { - // .NET Framework doesn't allow a content body with this HTTP verb. - // It will throw a System.Net.ProtocolViolation exception. - return; - } - - if (PlatformDetection.IsUap && method == "TRACE") - { - // UAP platform doesn't allow a content body with this HTTP verb. - // It will throw an exception HttpRequestException/COMException - // with "The requested operation is invalid" message. - return; - } - - using (HttpClient client = CreateHttpClient()) - { - var request = new HttpRequestMessage( - new HttpMethod(method), - secureServer ? Configuration.Http.SecureRemoteEchoServer : Configuration.Http.RemoteEchoServer) - { - Content = new StringContent(ExpectedContent) - }; - - using (HttpResponseMessage response = await client.SendAsync(request)) - { - if (method == "TRACE" && (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || UseManagedHandler)) - { - // .NET Framework also allows the HttpWebRequest and HttpClient APIs to send a request using 'TRACE' - // verb and a request body. The usual response from a server is "400 Bad Request". - // See here for more info: https://github.com/dotnet/corefx/issues/9023 - Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); - } - else - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - TestHelper.VerifyRequestMethod(response, method); - string responseContent = await response.Content.ReadAsStringAsync(); - Assert.False(responseContent.Contains(ExpectedContent)); - } - } - } - } - #endregion - - #region Version tests - [SkipOnTargetFramework(TargetFrameworkMonikers.Uap, "UAP does not allow HTTP/1.0 requests.")] - [OuterLoop] // TODO: Issue #11345 - [Fact] - public async Task SendAsync_RequestVersion10_ServerReceivesVersion10Request() - { - Version receivedRequestVersion = await SendRequestAndGetRequestVersionAsync(new Version(1, 0)); - Assert.Equal(new Version(1, 0), receivedRequestVersion); - } - - [OuterLoop] // TODO: Issue #11345 - [Fact] - public async Task SendAsync_RequestVersion11_ServerReceivesVersion11Request() - { - Version receivedRequestVersion = await SendRequestAndGetRequestVersionAsync(new Version(1, 1)); - Assert.Equal(new Version(1, 1), receivedRequestVersion); - } - - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Throws exception sending request using Version(0,0)")] - [OuterLoop] // TODO: Issue #11345 - [Fact] - public async Task SendAsync_RequestVersionNotSpecified_ServerReceivesVersion11Request() - { - // The default value for HttpRequestMessage.Version is Version(1,1). - // So, we need to set something different (0,0), to test the "unknown" version. - Version receivedRequestVersion = await SendRequestAndGetRequestVersionAsync(new Version(0, 0)); - Assert.Equal(new Version(1, 1), receivedRequestVersion); - } - - [ActiveIssue(23770, TestPlatforms.AnyUnix)] - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Specifying Version(2,0) throws exception on netfx")] - [OuterLoop] // TODO: Issue #11345 - [Theory] - [MemberData(nameof(Http2Servers))] - public async Task SendAsync_RequestVersion20_ResponseVersion20IfHttp2Supported(Uri server) - { - if (PlatformDetection.IsWindows && !PlatformDetection.IsWindows10Version1703OrGreater) - { - // Skip this test if running on Windows but on a release prior to Windows 10 Creators Update. - _output.WriteLine("Skipping test due to Windows 10 version prior to Version 1703."); - return; - } - if (UseManagedHandler) - { - // TODO #23134: The managed handler doesn't yet support HTTP/2. - return; - } - - // We don't currently have a good way to test whether HTTP/2 is supported without - // using the same mechanism we're trying to test, so for now we allow both 2.0 and 1.1 responses. - var request = new HttpRequestMessage(HttpMethod.Get, server); - request.Version = new Version(2, 0); - - using (HttpClientHandler handler = CreateHttpClientHandler()) - using (var client = new HttpClient(handler, false)) - { - // It is generally expected that the test hosts will be trusted, so we don't register a validation - // callback in the usual case. - // - // However, on our Debian 8 test machines, a combination of a server side TLS chain, - // the client chain processor, and the distribution's CA bundle results in an incomplete/untrusted - // certificate chain. See https://github.com/dotnet/corefx/issues/9244 for more details. - if (PlatformDetection.IsDebian8) - { - // Func - handler.ServerCertificateCustomValidationCallback = (msg, cert, chain, errors) => - { - Assert.InRange(chain.ChainStatus.Length, 0, 1); - - if (chain.ChainStatus.Length > 0) - { - Assert.Equal(X509ChainStatusFlags.PartialChain, chain.ChainStatus[0].Status); - } - - return true; - }; - } - - using (HttpResponseMessage response = await client.SendAsync(request)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.True( - response.Version == new Version(2, 0) || - response.Version == new Version(1, 1), - "Response version " + response.Version); - } - } - } - - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Specifying Version(2,0) throws exception on netfx")] - [OuterLoop] // TODO: Issue #11345 - [ConditionalTheory(nameof(IsWindows10Version1607OrGreater)), MemberData(nameof(Http2NoPushServers))] - public async Task SendAsync_RequestVersion20_ResponseVersion20(Uri server) - { - if (UseManagedHandler) - { - // TODO #23134: The managed handler doesn't yet support HTTP/2. - return; - } - - _output.WriteLine(server.AbsoluteUri.ToString()); - var request = new HttpRequestMessage(HttpMethod.Get, server); - request.Version = new Version(2, 0); - - HttpClientHandler handler = CreateHttpClientHandler(); - using (var client = new HttpClient(handler)) - { - using (HttpResponseMessage response = await client.SendAsync(request)) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(new Version(2, 0), response.Version); - } - } - } - - private async Task SendRequestAndGetRequestVersionAsync(Version requestVersion) - { - Version receivedRequestVersion = null; - - await LoopbackServer.CreateServerAsync(async (server, url) => - { - var request = new HttpRequestMessage(HttpMethod.Get, url); - request.Version = requestVersion; - - using (HttpClient client = CreateHttpClient()) - { - Task getResponse = client.SendAsync(request); - Task> serverTask = LoopbackServer.ReadRequestAndSendResponseAsync(server); - await TestHelper.WhenAllCompletedOrAnyFailed(getResponse, serverTask); - - List receivedRequest = await serverTask; - using (HttpResponseMessage response = await getResponse) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - } - - string statusLine = receivedRequest[0]; - if (statusLine.Contains("/1.0")) - { - receivedRequestVersion = new Version(1, 0); - } - else if (statusLine.Contains("/1.1")) - { - receivedRequestVersion = new Version(1, 1); - } - else - { - Assert.True(false, "Invalid HTTP request version"); - } - - } - }); - - return receivedRequestVersion; - } - #endregion - - #region Proxy tests - [SkipOnTargetFramework(TargetFrameworkMonikers.Uap, "UAP does not support custom proxies.")] - [OuterLoop] // TODO: Issue #11345 - [Theory] - [MemberData(nameof(CredentialsForProxy))] - public async Task Proxy_BypassFalse_GetRequestGoesThroughCustomProxy(ICredentials creds, bool wrapCredsInCache) - { - if (UseManagedHandler) - { - // TODO #23135: ManagedHandler currently gets error "System.NotImplementedException : Basic auth: can't handle ':' in domain "dom:\ain"" - return; - } - - int port; - Task proxyTask = LoopbackGetRequestHttpProxy.StartAsync( - out port, - requireAuth: creds != null && creds != CredentialCache.DefaultCredentials, - expectCreds: true); - Uri proxyUrl = new Uri($"http://localhost:{port}"); - - const string BasicAuth = "Basic"; - if (wrapCredsInCache) - { - Assert.IsAssignableFrom(creds); - var cache = new CredentialCache(); - cache.Add(proxyUrl, BasicAuth, (NetworkCredential)creds); - creds = cache; - } - - using (HttpClientHandler handler = CreateHttpClientHandler()) - using (var client = new HttpClient(handler)) - { - handler.Proxy = new UseSpecifiedUriWebProxy(proxyUrl, creds); - - Task responseTask = client.GetAsync(Configuration.Http.RemoteEchoServer); - Task responseStringTask = responseTask.ContinueWith(t => t.Result.Content.ReadAsStringAsync(), TaskScheduler.Default).Unwrap(); - await TestHelper.WhenAllCompletedOrAnyFailed(proxyTask, responseTask, responseStringTask); - - using (responseTask.Result) - { - TestHelper.VerifyResponseBody(responseStringTask.Result, responseTask.Result.Content.Headers.ContentMD5, false, null); - Assert.Equal(Encoding.ASCII.GetString(proxyTask.Result.ResponseContent), responseStringTask.Result); - - NetworkCredential nc = creds?.GetCredential(proxyUrl, BasicAuth); - string expectedAuth = - nc == null || nc == CredentialCache.DefaultCredentials ? null : - string.IsNullOrEmpty(nc.Domain) ? $"{nc.UserName}:{nc.Password}" : - $"{nc.Domain}\\{nc.UserName}:{nc.Password}"; - Assert.Equal(expectedAuth, proxyTask.Result.AuthenticationHeaderValue); - } - } - } - - [SkipOnTargetFramework(TargetFrameworkMonikers.Uap, "UAP does not support custom proxies.")] - [OuterLoop] // TODO: Issue #11345 - [Theory] - [MemberData(nameof(BypassedProxies))] - public async Task Proxy_BypassTrue_GetRequestDoesntGoesThroughCustomProxy(IWebProxy proxy) - { - HttpClientHandler handler = CreateHttpClientHandler(); - handler.Proxy = proxy; - using (var client = new HttpClient(handler)) - using (HttpResponseMessage response = await client.GetAsync(Configuration.Http.RemoteEchoServer)) - { - TestHelper.VerifyResponseBody( - await response.Content.ReadAsStringAsync(), - response.Content.Headers.ContentMD5, - false, - null); - } - } - - [SkipOnTargetFramework(TargetFrameworkMonikers.Uap, "UAP does not support custom proxies.")] - [OuterLoop] // TODO: Issue #11345 - [Fact] - public void Proxy_HaveNoCredsAndUseAuthenticatedCustomProxy_ProxyAuthenticationRequiredStatusCode() - { - int port; - Task proxyTask = LoopbackGetRequestHttpProxy.StartAsync( - out port, - requireAuth: true, - expectCreds: false); - Uri proxyUrl = new Uri($"http://localhost:{port}"); - - HttpClientHandler handler = CreateHttpClientHandler(); - handler.Proxy = new UseSpecifiedUriWebProxy(proxyUrl, null); - using (var client = new HttpClient(handler)) - { - Task responseTask = client.GetAsync(Configuration.Http.RemoteEchoServer); - Task.WaitAll(proxyTask, responseTask); - using (responseTask.Result) - { - Assert.Equal(HttpStatusCode.ProxyAuthenticationRequired, responseTask.Result.StatusCode); - } - } - } - - private static IEnumerable BypassedProxies() - { - yield return new object[] { null }; - yield return new object[] { new UseSpecifiedUriWebProxy(new Uri($"http://{Guid.NewGuid().ToString().Substring(0, 15)}:12345"), bypass: true) }; - } - - private static IEnumerable CredentialsForProxy() - { - yield return new object[] { null, false }; - foreach (bool wrapCredsInCache in new[] { true, false }) - { - yield return new object[] { new NetworkCredential("user:name", "password"), wrapCredsInCache }; - yield return new object[] { new NetworkCredential("username", "password", "dom:\\ain"), wrapCredsInCache }; - } - } - #endregion - - #region Uri wire transmission encoding tests - [OuterLoop] // TODO: Issue #11345 - [Fact] - public async Task SendRequest_UriPathHasReservedChars_ServerReceivedExpectedPath() - { - await LoopbackServer.CreateServerAsync(async (server, rootUrl) => - { - var uri = new Uri($"http://{rootUrl.Host}:{rootUrl.Port}/test[]"); - _output.WriteLine(uri.AbsoluteUri.ToString()); - var request = new HttpRequestMessage(HttpMethod.Get, uri); - string statusLine = string.Empty; - - using (HttpClient client = CreateHttpClient()) - { - Task getResponseTask = client.SendAsync(request); - Task> serverTask = LoopbackServer.ReadRequestAndSendResponseAsync(server); - - await TestHelper.WhenAllCompletedOrAnyFailed(getResponseTask, serverTask); - List receivedRequest = await serverTask; - using (HttpResponseMessage response = await getResponseTask) - { - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.True(receivedRequest[0].Contains(uri.PathAndQuery), $"statusLine should contain {uri.PathAndQuery}"); - } - } - }); - } - #endregion - } -} diff --git a/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.cs.REMOVED.git-id b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.cs.REMOVED.git-id new file mode 100644 index 0000000000..fea9b1229d --- /dev/null +++ b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.cs.REMOVED.git-id @@ -0,0 +1 @@ +b4de81da4ff514243cfb621c3d54b94e9c695351 \ No newline at end of file diff --git a/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs index 5be76eb659..b8eace674e 100644 --- a/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs +++ b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs @@ -12,7 +12,7 @@ using Xunit; namespace System.Net.Http.Functional.Tests { - public class HttpClientTest : HttpClientTestBase + public partial class HttpClientTest : HttpClientTestBase { [Fact] public void Dispose_MultipleTimes_Success() diff --git a/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientTest.netcoreapp.cs b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientTest.netcoreapp.cs new file mode 100644 index 0000000000..618bc2cb80 --- /dev/null +++ b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpClientTest.netcoreapp.cs @@ -0,0 +1,50 @@ +// 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.Threading; +using System.Threading.Tasks; + +using Xunit; + +namespace System.Net.Http.Functional.Tests +{ + public partial class HttpClientTest + { + [Fact] + public async Task PatchAsync_Canceled_Throws() + { + using (var client = new HttpClient(new CustomResponseHandler((r, c) => WhenCanceled(c)))) + { + var content = new ByteArrayContent(new byte[1]); + var cts = new CancellationTokenSource(); + + Task t1 = client.PatchAsync(CreateFakeUri(), content, cts.Token); + + cts.Cancel(); + + await Assert.ThrowsAnyAsync(() => t1); + } + } + + [Fact] + public async Task PatchAsync_Success() + { + Action verify = message => { using (message) Assert.Equal(HttpStatusCode.OK, message.StatusCode); }; + using (var client = new HttpClient(new CustomResponseHandler((r, c) => Task.FromResult(new HttpResponseMessage())))) + { + verify(await client.PatchAsync(CreateFakeUri(), new ByteArrayContent(new byte[1]))); + verify(await client.PatchAsync(CreateFakeUri(), new ByteArrayContent(new byte[1]), CancellationToken.None)); + } + } + + [Fact] + public void Dispose_UsePatchAfterDispose_Throws() + { + HttpClient client = CreateHttpClient(); + client.Dispose(); + + Assert.Throws(() => { client.PatchAsync(CreateFakeUri(), new ByteArrayContent(new byte[1])); }); + } + } +} diff --git a/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs index dc33c2068c..44caa2a840 100644 --- a/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs +++ b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs @@ -108,7 +108,7 @@ namespace System.Net.Http.Functional.Tests { var data = new byte[10]; var content = new MockContent(data); - content.LoadIntoBufferAsync().Wait(); + await content.LoadIntoBufferAsync(); Assert.Equal(1, content.SerializeToStreamAsyncCount); var destination = new MemoryStream(); diff --git a/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpMethodTest.cs b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpMethodTest.cs index a487a06db8..43f3751d17 100644 --- a/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpMethodTest.cs +++ b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpMethodTest.cs @@ -11,20 +11,27 @@ using Xunit; namespace System.Net.Http.Functional.Tests { - public class HttpMethodTest + public partial class HttpMethodTest { - private static readonly object[][] s_staticHttpMethods = - { - new object[] { HttpMethod.Get }, - new object[] { HttpMethod.Put }, - new object[] { HttpMethod.Post }, - new object[] { HttpMethod.Delete }, - new object[] { HttpMethod.Head }, - new object[] { HttpMethod.Options }, - new object[] { HttpMethod.Trace } - }; + public static IEnumerable StaticHttpMethods { get; } - public static IEnumerable StaticHttpMethods { get { return s_staticHttpMethods; } } + static HttpMethodTest() + { + List staticHttpMethods = new List + { + new object[] { HttpMethod.Get }, + new object[] { HttpMethod.Put }, + new object[] { HttpMethod.Post }, + new object[] { HttpMethod.Delete }, + new object[] { HttpMethod.Head }, + new object[] { HttpMethod.Options }, + new object[] { HttpMethod.Trace } + }; + AddStaticHttpMethods(staticHttpMethods); + StaticHttpMethods = staticHttpMethods; + } + + static partial void AddStaticHttpMethods(List staticHttpMethods); [Fact] public void StaticProperties_VerifyValues_PropertyNameMatchesHttpMethodName() diff --git a/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpMethodTest.netcoreapp.cs b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpMethodTest.netcoreapp.cs new file mode 100644 index 0000000000..3d9324b058 --- /dev/null +++ b/external/corefx/src/System.Net.Http/tests/FunctionalTests/HttpMethodTest.netcoreapp.cs @@ -0,0 +1,24 @@ +// 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.Collections.Generic; + +using Xunit; + +namespace System.Net.Http.Functional.Tests +{ + public partial class HttpMethodTest + { + [Fact] + public void Patch_VerifyValue_PropertyNameMatchesHttpMethodName() + { + Assert.Equal("PATCH", HttpMethod.Patch.Method); + } + + static partial void AddStaticHttpMethods(List staticHttpMethods) + { + staticHttpMethods.Add(new object[] { HttpMethod.Patch }); + } + } +} diff --git a/external/corefx/src/System.Net.Http/tests/FunctionalTests/LoopbackGetRequestHttpProxy.cs b/external/corefx/src/System.Net.Http/tests/FunctionalTests/LoopbackGetRequestHttpProxy.cs index 510af504fe..10f99e938c 100644 --- a/external/corefx/src/System.Net.Http/tests/FunctionalTests/LoopbackGetRequestHttpProxy.cs +++ b/external/corefx/src/System.Net.Http/tests/FunctionalTests/LoopbackGetRequestHttpProxy.cs @@ -44,6 +44,12 @@ namespace System.Net.Http.Functional.Tests { // Get and parse the incoming request. Func getAndReadRequest = async () => { + if (clientSocket != null) + { + clientSocket.Shutdown(SocketShutdown.Send); + clientSocket.Dispose(); + } + clientSocket = await listener.AcceptSocketAsync().ConfigureAwait(false); clientStream = new NetworkStream(clientSocket, ownsSocket: false); clientReader = new StreamReader(clientStream, Encoding.ASCII); @@ -68,9 +74,6 @@ namespace System.Net.Http.Functional.Tests new ArraySegment(Encoding.ASCII.GetBytes("HTTP/1.1 407 Proxy Auth Required\r\nProxy-Authenticate: Basic\r\n\r\n")), SocketFlags.None).ConfigureAwait(false); - clientSocket.Shutdown(SocketShutdown.Send); - clientSocket.Dispose(); - if (expectCreds) { // Wait for a new connection that should have an auth header this time and parse it. @@ -127,6 +130,7 @@ namespace System.Net.Http.Functional.Tests } finally { + clientSocket.Shutdown(SocketShutdown.Send); clientSocket.Dispose(); listener.Stop(); } diff --git a/external/corefx/src/System.Net.Http/tests/FunctionalTests/ReadOnlyMemoryContentTest.cs b/external/corefx/src/System.Net.Http/tests/FunctionalTests/ReadOnlyMemoryContentTest.cs index 01334a8c3f..98bb3426b5 100644 --- a/external/corefx/src/System.Net.Http/tests/FunctionalTests/ReadOnlyMemoryContentTest.cs +++ b/external/corefx/src/System.Net.Http/tests/FunctionalTests/ReadOnlyMemoryContentTest.cs @@ -5,7 +5,6 @@ using System.Buffers; using System.Collections.Generic; using System.IO; -using System.IO.Tests; using System.Threading; using System.Threading.Tasks; using Xunit; diff --git a/external/corefx/src/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj b/external/corefx/src/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj index 27f5a02d77..08a2a2a241 100644 --- a/external/corefx/src/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj +++ b/external/corefx/src/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj @@ -30,6 +30,9 @@ Common\System\IO\DelegateStream.cs + + Common\System\Net\RemoteServerQuery.cs + Common\System\Net\Configuration.Certificates.cs @@ -110,6 +113,8 @@ + + @@ -136,4 +141,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.Net.Http/tests/UnitTests/DigestAuthenticationTests.cs b/external/corefx/src/System.Net.Http/tests/UnitTests/DigestAuthenticationTests.cs new file mode 100644 index 0000000000..6758686447 --- /dev/null +++ b/external/corefx/src/System.Net.Http/tests/UnitTests/DigestAuthenticationTests.cs @@ -0,0 +1,44 @@ +// 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.Collections.Generic; +using System.Net.Http; +using Xunit; + +namespace System.Net.Http.Tests +{ + public class DigestAuthenticationTests + { + private static readonly List s_keyListWithCountTwo = new List { "key1", "key2" }; + private static readonly List s_valueListWithCountTwo = new List { "value1", "value2" }; + private static readonly List s_emptyStringList = new List(); + + [Theory] + [MemberData(nameof(DigestResponse_Challenge_TestData))] + public void DigestResponse_Parse_Succeeds(string challenge, List keys, List values) + { + AuthenticationHelper.DigestResponse digestResponse = new AuthenticationHelper.DigestResponse(challenge); + Assert.Equal(keys.Count, digestResponse.Parameters.Count); + Assert.Equal(values.Count, digestResponse.Parameters.Count); + Assert.Equal(keys, digestResponse.Parameters.Keys); + Assert.Equal(values, digestResponse.Parameters.Values); + } + + public static IEnumerable DigestResponse_Challenge_TestData() + { + yield return new object[] { "key1=value1,key2=value2", s_keyListWithCountTwo, s_valueListWithCountTwo }; + yield return new object[] { "\tkey1===value1,key2 \t===\tvalue2", s_keyListWithCountTwo, s_valueListWithCountTwo }; + yield return new object[] { " key1 = value1, key2 = value2,", s_keyListWithCountTwo, s_valueListWithCountTwo }; + yield return new object[] { "key1 === value1,key2=, value2", s_keyListWithCountTwo, new List { "value1", string.Empty } }; + yield return new object[] { "key1,==value1,,, key2=\"value2\", key3 m", new List { "key1,", "key2" }, s_valueListWithCountTwo }; + yield return new object[] { "key1= \"value1 \",key2 = \"v alu#e2\" ,", s_keyListWithCountTwo, new List { "value1 ", "v alu#e2"} }; + yield return new object[] { "key1 ", s_emptyStringList, s_emptyStringList }; + yield return new object[] { "=====", s_emptyStringList, s_emptyStringList }; + yield return new object[] { ",,", s_emptyStringList, s_emptyStringList }; + yield return new object[] { "=,=", s_emptyStringList, s_emptyStringList }; + yield return new object[] { "=value1,key2=,", s_emptyStringList, s_emptyStringList }; + yield return new object[] { "key1\tm= value1", s_emptyStringList, s_emptyStringList }; + } + } +} diff --git a/external/corefx/src/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj b/external/corefx/src/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj index 2cea1bc17a..fc3a641c44 100644 --- a/external/corefx/src/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj +++ b/external/corefx/src/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj @@ -84,6 +84,9 @@ ProductionCode\Common\System\Threading\Tasks\TaskToApm.cs + + ProductionCode\System\Net\Http\Managed\AuthenticationHelper.Digest.cs + ProductionCode\System\Net\Http\ByteArrayContent.cs @@ -300,6 +303,7 @@ ProductionCode\System\Net\Http\HttpHandlerDefaults.cs + diff --git a/external/corefx/src/System.Net.HttpListener/src/Resources/Strings.resx b/external/corefx/src/System.Net.HttpListener/src/Resources/Strings.resx index 0098ac8fea..182660572e 100644 --- a/external/corefx/src/System.Net.HttpListener/src/Resources/Strings.resx +++ b/external/corefx/src/System.Net.HttpListener/src/Resources/Strings.resx @@ -1,5 +1,64 @@  + @@ -313,18 +372,12 @@ '{0}' is not a supported handle type. - - Offset exceeds the length of buffer. - I/O operation aborted: '{0}'. Invalid path. - - Client certificate not found. - Authentication errors. @@ -337,4 +390,4 @@ The WebSocket is in an invalid state ('{0}') for this operation. Valid states are: '{1}' - + \ No newline at end of file diff --git a/external/corefx/src/System.Net.HttpListener/src/System.Net.HttpListener.csproj b/external/corefx/src/System.Net.HttpListener/src/System.Net.HttpListener.csproj index 16307ee5c9..010e032b2e 100644 --- a/external/corefx/src/System.Net.HttpListener/src/System.Net.HttpListener.csproj +++ b/external/corefx/src/System.Net.HttpListener/src/System.Net.HttpListener.csproj @@ -115,7 +115,6 @@ - @@ -143,6 +142,9 @@ Common\Interop\Windows\Interop.Libraries.cs + + Common\Interop\Windows\SChannel\Interop.SecPkgContext_ApplicationProtocol.cs + Common\Interop\Windows\Interop.BOOL.cs @@ -357,9 +359,6 @@ - - Common\System\Net\WebSockets\ManagedWebSocket.cs - Common\System\Threading\Tasks\TaskToApm.cs diff --git a/external/corefx/src/System.Net.HttpListener/src/System/Net/HttpListenerException.cs b/external/corefx/src/System.Net.HttpListener/src/System/Net/HttpListenerException.cs index e9a96691d5..73e68865d2 100644 --- a/external/corefx/src/System.Net.HttpListener/src/System/Net/HttpListenerException.cs +++ b/external/corefx/src/System.Net.HttpListener/src/System/Net/HttpListenerException.cs @@ -9,6 +9,9 @@ using System.Runtime.Serialization; namespace System.Net { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class HttpListenerException : Win32Exception { public HttpListenerException() : base(Marshal.GetLastWin32Error()) @@ -29,7 +32,7 @@ namespace System.Net protected HttpListenerException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { - throw new PlatformNotSupportedException(); + if (NetEventSource.IsEnabled) NetEventSource.Info(this, NativeErrorCode.ToString() + ":" + Message); } // the base class returns the HResult with this property diff --git a/external/corefx/src/System.Net.HttpListener/src/System/Net/Managed/WebSockets/HttpWebSocket.Managed.cs b/external/corefx/src/System.Net.HttpListener/src/System/Net/Managed/WebSockets/HttpWebSocket.Managed.cs index 12b80b54a6..d7c9ea6e58 100644 --- a/external/corefx/src/System.Net.HttpListener/src/System/Net/Managed/WebSockets/HttpWebSocket.Managed.cs +++ b/external/corefx/src/System.Net.HttpListener/src/System/Net/Managed/WebSockets/HttpWebSocket.Managed.cs @@ -58,7 +58,12 @@ namespace System.Net.WebSockets // Send websocket handshake headers await responseStream.WriteWebSocketHandshakeHeadersAsync().ConfigureAwait(false); - WebSocket webSocket = ManagedWebSocket.CreateFromConnectedStream(context.Connection.ConnectedStream, true, subProtocol, keepAliveInterval, receiveBufferSize, internalBuffer); + const int MinBufferSize = 14; // from ManagedWebSocket.MaxMessageHeaderLength + Memory buffer = + internalBuffer.GetValueOrDefault().Count >= MinBufferSize ? internalBuffer.GetValueOrDefault() : // use provided buffer if it's big enough + receiveBufferSize >= MinBufferSize ? new byte[receiveBufferSize] : // or use provided size if it's big enough + Memory.Empty; // or use the default + WebSocket webSocket = WebSocket.CreateFromStream(context.Connection.ConnectedStream, isServer:true, subProtocol, keepAliveInterval, buffer); HttpListenerWebSocketContext webSocketContext = new HttpListenerWebSocketContext( request.Url, diff --git a/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/AsyncRequestContext.cs b/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/AsyncRequestContext.cs index b2d2d7258f..138db12aff 100644 --- a/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/AsyncRequestContext.cs +++ b/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/AsyncRequestContext.cs @@ -38,7 +38,7 @@ namespace System.Net private Interop.HttpApi.HTTP_REQUEST* Allocate(ThreadPoolBoundHandle boundHandle, uint size) { - uint newSize = size != 0 ? size : RequestBuffer == null ? 4096 : Size; + uint newSize = size != 0 ? size : RequestBuffer == IntPtr.Zero ? 4096 : Size; if (_nativeOverlapped != null) { #if DEBUG @@ -57,7 +57,7 @@ namespace System.Net _boundHandle = boundHandle; _nativeOverlapped = boundHandle.AllocateNativeOverlapped(ListenerAsyncResult.IOCallback, state: _result, pinData: RequestBuffer); - return (Interop.HttpApi.HTTP_REQUEST*)Marshal.UnsafeAddrOfPinnedArrayElement(RequestBuffer, 0); + return (Interop.HttpApi.HTTP_REQUEST*)RequestBuffer.ToPointer(); } internal void Reset(ThreadPoolBoundHandle boundHandle, ulong requestId, uint size) diff --git a/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/ContextFlags.cs b/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/ContextFlags.cs deleted file mode 100644 index befb6578bd..0000000000 --- a/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/ContextFlags.cs +++ /dev/null @@ -1,70 +0,0 @@ -// 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. - -namespace System.Net -{ - [Flags] - internal enum ContextFlags - { - Zero = 0, - // The server in the transport application can - // build new security contexts impersonating the - // client that will be accepted by other servers - // as the client's contexts. - Delegate = 0x00000001, - // The communicating parties must authenticate - // their identities to each other. Without MutualAuth, - // the client authenticates its identity to the server. - // With MutualAuth, the server also must authenticate - // its identity to the client. - MutualAuth = 0x00000002, - // The security package detects replayed packets and - // notifies the caller if a packet has been replayed. - // The use of this flag implies all of the conditions - // specified by the Integrity flag. - ReplayDetect = 0x00000004, - // The context must be allowed to detect out-of-order - // delivery of packets later through the message support - // functions. Use of this flag implies all of the - // conditions specified by the Integrity flag. - SequenceDetect = 0x00000008, - // The context must protect data while in transit. - // Confidentiality is supported for NTLM with Microsoft - // Windows NT version 4.0, SP4 and later and with the - // Kerberos protocol in Microsoft Windows 2000 and later. - Confidentiality = 0x00000010, - UseSessionKey = 0x00000020, - AllocateMemory = 0x00000100, - - // Connection semantics must be used. - Connection = 0x00000800, - - // Client applications requiring extended error messages specify the - // ISC_REQ_EXTENDED_ERROR flag when calling the InitializeSecurityContext - // Server applications requiring extended error messages set - // the ASC_REQ_EXTENDED_ERROR flag when calling AcceptSecurityContext. - InitExtendedError = 0x00004000, - AcceptExtendedError = 0x00008000, - // A transport application requests stream semantics - // by setting the ISC_REQ_STREAM and ASC_REQ_STREAM - // flags in the calls to the InitializeSecurityContext - // and AcceptSecurityContext functions - InitStream = 0x00008000, - AcceptStream = 0x00010000, - // Buffer integrity can be verified; however, replayed - // and out-of-sequence messages will not be detected - InitIntegrity = 0x00010000, // ISC_REQ_INTEGRITY - AcceptIntegrity = 0x00020000, // ASC_REQ_INTEGRITY - - InitManualCredValidation = 0x00080000, // ISC_REQ_MANUAL_CRED_VALIDATION - InitUseSuppliedCreds = 0x00000080, // ISC_REQ_USE_SUPPLIED_CREDS - InitIdentify = 0x00020000, // ISC_REQ_IDENTIFY - AcceptIdentify = 0x00080000, // ASC_REQ_IDENTIFY - - ProxyBindings = 0x04000000, // ASC_REQ_PROXY_BINDINGS - AllowMissingBindings = 0x10000000, // ASC_REQ_ALLOW_MISSING_BINDINGS - - UnverifiedTargetName = 0x20000000, // ISC_REQ_UNVERIFIED_TARGET_NAME - } -} diff --git a/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/HttpListenerRequest.Windows.cs b/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/HttpListenerRequest.Windows.cs index b947bf5b30..177f8222b8 100644 --- a/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/HttpListenerRequest.Windows.cs +++ b/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/HttpListenerRequest.Windows.cs @@ -109,7 +109,7 @@ namespace System.Net // Note: RequestBuffer may get moved in memory. If you dereference a pointer from inside the RequestBuffer, // you must use 'OriginalBlobAddress' below to adjust the location of the pointer to match the location of // RequestBuffer. - internal byte[] RequestBuffer + internal IntPtr RequestBuffer { get { diff --git a/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/RequestContextBase.cs b/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/RequestContextBase.cs index 6c73b70fa5..6f71279318 100644 --- a/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/RequestContextBase.cs +++ b/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/RequestContextBase.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Runtime.InteropServices; namespace System.Net { @@ -10,7 +11,8 @@ namespace System.Net { private Interop.HttpApi.HTTP_REQUEST* _memoryBlob; private Interop.HttpApi.HTTP_REQUEST* _originalBlobAddress; - private byte[] _backingBuffer; + private IntPtr _backingBuffer = IntPtr.Zero; + private int _backingBufferLength = 0; // Must call this from derived class' constructors. protected void BaseConstruction(Interop.HttpApi.HTTP_REQUEST* requestBlob) @@ -29,7 +31,7 @@ namespace System.Net // before an object (HttpListenerRequest) which closes the RequestContext on demand is returned to the application. internal void ReleasePins() { - Debug.Assert(_memoryBlob != null || _backingBuffer == null, "RequestContextBase::ReleasePins()|ReleasePins() called twice."); + Debug.Assert(_memoryBlob != null || _backingBuffer == IntPtr.Zero, "RequestContextBase::ReleasePins()|ReleasePins() called twice."); _originalBlobAddress = _memoryBlob; UnsetBlob(); OnReleasePins(); @@ -48,7 +50,14 @@ namespace System.Net Dispose(true); } - protected virtual void Dispose(bool disposing) { } + protected virtual void Dispose(bool disposing) + { + if (_backingBuffer != IntPtr.Zero) + { + Marshal.FreeHGlobal(_backingBuffer); + _backingBuffer = IntPtr.Zero; + } + } ~RequestContextBase() { @@ -59,12 +68,12 @@ namespace System.Net { get { - Debug.Assert(_memoryBlob != null || _backingBuffer == null, "RequestContextBase::Dispose()|RequestBlob requested after ReleasePins()."); + Debug.Assert(_memoryBlob != null || _backingBuffer == IntPtr.Zero, "RequestContextBase::Dispose()|RequestBlob requested after ReleasePins()."); return _memoryBlob; } } - internal byte[] RequestBuffer + internal IntPtr RequestBuffer { get { @@ -76,7 +85,7 @@ namespace System.Net { get { - return (uint)_backingBuffer.Length; + return (uint)_backingBufferLength; } } @@ -91,7 +100,7 @@ namespace System.Net protected void SetBlob(Interop.HttpApi.HTTP_REQUEST* requestBlob) { - Debug.Assert(_memoryBlob != null || _backingBuffer == null, "RequestContextBase::Dispose()|SetBlob() called after ReleasePins()."); + Debug.Assert(_memoryBlob != null || _backingBuffer == IntPtr.Zero, "RequestContextBase::Dispose()|SetBlob() called after ReleasePins()."); if (requestBlob == null) { UnsetBlob(); @@ -116,7 +125,16 @@ namespace System.Net protected void SetBuffer(int size) { - _backingBuffer = size == 0 ? null : new byte[size]; + if (_backingBuffer != IntPtr.Zero) + { + Marshal.FreeHGlobal(_backingBuffer); + } + + _backingBuffer = size == 0 ? IntPtr.Zero : Marshal.AllocHGlobal(size); + _backingBufferLength = size; + + // Zero out the contents of the buffer. + new Span(_backingBuffer.ToPointer(), size).Fill(0); } } } diff --git a/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/SyncRequestContext.cs b/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/SyncRequestContext.cs index f7ba1e4f1b..b140ab1564 100644 --- a/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/SyncRequestContext.cs +++ b/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/SyncRequestContext.cs @@ -9,30 +9,20 @@ namespace System.Net { internal unsafe class SyncRequestContext : RequestContextBase { - private GCHandle _pinnedHandle; - internal SyncRequestContext(int size) { BaseConstruction(Allocate(size)); } - private Interop.HttpApi.HTTP_REQUEST* Allocate(int size) + private Interop.HttpApi.HTTP_REQUEST* Allocate(int newSize) { - if (_pinnedHandle.IsAllocated) + if (Size > 0 && Size == newSize) { - if (RequestBuffer.Length == size) - { - return RequestBlob; - } - _pinnedHandle.Free(); + return RequestBlob; } - SetBuffer(size); - if (RequestBuffer == null) - { - return null; - } - _pinnedHandle = GCHandle.Alloc(RequestBuffer, GCHandleType.Pinned); - return (Interop.HttpApi.HTTP_REQUEST*)Marshal.UnsafeAddrOfPinnedArrayElement(RequestBuffer, 0); + SetBuffer(newSize); + + return RequestBuffer == IntPtr.Zero ? null : (Interop.HttpApi.HTTP_REQUEST*)RequestBuffer.ToPointer(); } internal void Reset(int size) @@ -40,25 +30,6 @@ namespace System.Net SetBlob(Allocate(size)); } - protected override void OnReleasePins() - { - if (_pinnedHandle.IsAllocated) - { - _pinnedHandle.Free(); - } - } - - protected override void Dispose(bool disposing) - { - if (_pinnedHandle.IsAllocated) - { - Debug.Assert(!disposing, "AsyncRequestContext::Dispose()|Must call ReleasePins() before calling Dispose()."); - if (!Environment.HasShutdownStarted || disposing) - { - _pinnedHandle.Free(); - } - } - base.Dispose(disposing); - } + protected override void OnReleasePins() { } } } diff --git a/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketProtocolComponent.cs b/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketProtocolComponent.cs index ae05bb430f..01fdb2a35e 100644 --- a/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketProtocolComponent.cs +++ b/external/corefx/src/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketProtocolComponent.cs @@ -72,7 +72,6 @@ namespace System.Net.WebSockets Receive = 2, } - [SecuritySafeCritical] static WebSocketProtocolComponent() { s_webSocketDllHandle = Interop.Kernel32.LoadLibraryExW(Interop.Libraries.WebSocket, IntPtr.Zero, 0); @@ -562,4 +561,4 @@ namespace System.Net.WebSockets innerException); } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Net.HttpListener/tests/InvalidClientRequestTests.cs b/external/corefx/src/System.Net.HttpListener/tests/InvalidClientRequestTests.cs index f38c54d57a..928f10cc0c 100644 --- a/external/corefx/src/System.Net.HttpListener/tests/InvalidClientRequestTests.cs +++ b/external/corefx/src/System.Net.HttpListener/tests/InvalidClientRequestTests.cs @@ -1,4 +1,8 @@ -using System.Collections.Generic; +// 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.Collections.Generic; using System.Linq; using System.Net.Sockets; using System.Text; diff --git a/external/corefx/src/System.Net.Mail/src/System.Net.Mail.csproj b/external/corefx/src/System.Net.Mail/src/System.Net.Mail.csproj index 531a297f37..9d5a0a364e 100644 --- a/external/corefx/src/System.Net.Mail/src/System.Net.Mail.csproj +++ b/external/corefx/src/System.Net.Mail/src/System.Net.Mail.csproj @@ -203,6 +203,9 @@ + + Common\Interop\Windows\SChannel\Interop.SecPkgContext_ApplicationProtocol.cs + Common\System\Net\Security\SecurityContextTokenHandle.cs diff --git a/external/corefx/src/System.Net.Mail/src/System/Net/Base64Stream.cs b/external/corefx/src/System.Net.Mail/src/System/Net/Base64Stream.cs index 928983f1b7..6019beebb2 100644 --- a/external/corefx/src/System.Net.Mail/src/System/Net/Base64Stream.cs +++ b/external/corefx/src/System.Net.Mail/src/System/Net/Base64Stream.cs @@ -207,6 +207,7 @@ namespace System.Net { WriteState.LastBits = (byte)((buffer[cur] & 0x0f) << 2); WriteState.Padding = 1; + cur++; return cur - offset; } WriteState.Append(s_base64EncodeMap[((buffer[cur] & 0x0f) << 2) | ((buffer[cur + 1] & 0xc0) >> 6)]); diff --git a/external/corefx/src/System.Net.Mail/src/System/Net/Mail/MailHeaderInfo.cs b/external/corefx/src/System.Net.Mail/src/System/Net/Mail/MailHeaderInfo.cs index 6c952ca9d9..1588b8ef2c 100644 --- a/external/corefx/src/System.Net.Mail/src/System/Net/Mail/MailHeaderInfo.cs +++ b/external/corefx/src/System.Net.Mail/src/System/Net/Mail/MailHeaderInfo.cs @@ -9,7 +9,7 @@ namespace System.Net.Mail internal static class MailHeaderInfo { // Structure that wraps information about a single mail header - private struct HeaderInfo + private readonly struct HeaderInfo { public readonly string NormalizedName; public readonly bool IsSingleton; diff --git a/external/corefx/src/System.Net.Mail/src/System/Net/Mail/SmtpException.cs b/external/corefx/src/System.Net.Mail/src/System/Net/Mail/SmtpException.cs index c30e50192f..57b19b9bfe 100644 --- a/external/corefx/src/System.Net.Mail/src/System/Net/Mail/SmtpException.cs +++ b/external/corefx/src/System.Net.Mail/src/System/Net/Mail/SmtpException.cs @@ -2,13 +2,14 @@ // 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.Diagnostics.CodeAnalysis; using System.Runtime.Serialization; namespace System.Net.Mail { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class SmtpException : Exception, ISerializable { private SmtpStatusCode _statusCode = SmtpStatusCode.GeneralFailure; @@ -96,7 +97,7 @@ namespace System.Net.Mail protected SmtpException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { - throw new PlatformNotSupportedException(); + _statusCode = (SmtpStatusCode)serializationInfo.GetInt32("Status"); } internal SmtpException(SmtpStatusCode statusCode, string serverMessage, bool serverResponse) : base(GetMessageForStatus(statusCode, serverMessage)) @@ -110,12 +111,13 @@ namespace System.Net.Mail void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) { - base.GetObjectData(serializationInfo, streamingContext); + GetObjectData(serializationInfo, streamingContext); } public override void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) { base.GetObjectData(serializationInfo, streamingContext); + serializationInfo.AddValue("Status", (int)_statusCode, typeof(int)); } public SmtpStatusCode StatusCode diff --git a/external/corefx/src/System.Net.Mail/src/System/Net/Mail/SmtpFailedRecipientException.cs b/external/corefx/src/System.Net.Mail/src/System/Net/Mail/SmtpFailedRecipientException.cs index 7551e36d4c..24aba941fd 100644 --- a/external/corefx/src/System.Net.Mail/src/System/Net/Mail/SmtpFailedRecipientException.cs +++ b/external/corefx/src/System.Net.Mail/src/System/Net/Mail/SmtpFailedRecipientException.cs @@ -9,12 +9,14 @@ using System.Runtime.Serialization; namespace System.Net.Mail { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class SmtpFailedRecipientException : SmtpException, ISerializable { private string _failedRecipient; internal bool fatal; - public SmtpFailedRecipientException() : base() { } public SmtpFailedRecipientException(string message) : base(message) { } @@ -23,7 +25,7 @@ namespace System.Net.Mail protected SmtpFailedRecipientException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); + _failedRecipient = info.GetString("failedRecipient"); } public SmtpFailedRecipientException(SmtpStatusCode statusCode, string failedRecipient) : base(statusCode) @@ -52,12 +54,13 @@ namespace System.Net.Mail [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase", Justification = "System.dll is still using pre-v4 security model and needs this demand")] void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) { - base.GetObjectData(serializationInfo, streamingContext); + GetObjectData(serializationInfo, streamingContext); } public override void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) { base.GetObjectData(serializationInfo, streamingContext); + serializationInfo.AddValue("failedRecipient", _failedRecipient, typeof(string)); } } } diff --git a/external/corefx/src/System.Net.Mail/src/System/Net/Mail/SmtpFailedRecipientsException.cs b/external/corefx/src/System.Net.Mail/src/System/Net/Mail/SmtpFailedRecipientsException.cs index 6aa1ac6141..6b4847f1c5 100644 --- a/external/corefx/src/System.Net.Mail/src/System/Net/Mail/SmtpFailedRecipientsException.cs +++ b/external/corefx/src/System.Net.Mail/src/System/Net/Mail/SmtpFailedRecipientsException.cs @@ -9,6 +9,9 @@ using System.Runtime.Serialization; namespace System.Net.Mail { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class SmtpFailedRecipientsException : SmtpFailedRecipientException, ISerializable { private SmtpFailedRecipientException[] _innerExceptions; @@ -31,7 +34,7 @@ namespace System.Net.Mail protected SmtpFailedRecipientsException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); + _innerExceptions = (SmtpFailedRecipientException[])info.GetValue("innerExceptions", typeof(SmtpFailedRecipientException[])); } public SmtpFailedRecipientsException(string message, SmtpFailedRecipientException[] innerExceptions) : @@ -70,12 +73,13 @@ namespace System.Net.Mail [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase", Justification = "System.dll is still using pre-v4 security model and needs this demand")] void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) { - base.GetObjectData(serializationInfo, streamingContext); + GetObjectData(serializationInfo, streamingContext); } public override void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) { base.GetObjectData(serializationInfo, streamingContext); + serializationInfo.AddValue("innerExceptions", _innerExceptions, typeof(SmtpFailedRecipientException[])); } } } diff --git a/external/corefx/src/System.Net.Mail/src/System/Net/Mime/HeaderCollection.cs b/external/corefx/src/System.Net.Mail/src/System/Net/Mime/HeaderCollection.cs index a8c483c417..4166f54d28 100644 --- a/external/corefx/src/System.Net.Mail/src/System/Net/Mime/HeaderCollection.cs +++ b/external/corefx/src/System.Net.Mail/src/System/Net/Mime/HeaderCollection.cs @@ -137,7 +137,7 @@ namespace System.Net.Mime if (value == string.Empty) { - throw new ArgumentException(SR.Format(SR.net_emptystringcall, nameof(value)), nameof(name)); + throw new ArgumentException(SR.Format(SR.net_emptystringcall, nameof(value)), nameof(value)); } if (!MimeBasePart.IsAscii(name, false)) @@ -183,7 +183,7 @@ namespace System.Net.Mime } if (value == string.Empty) { - throw new ArgumentException(SR.Format(SR.net_emptystringcall, nameof(value)), nameof(name)); + throw new ArgumentException(SR.Format(SR.net_emptystringcall, nameof(value)), nameof(value)); } MailBnfHelper.ValidateHeaderName(name); diff --git a/external/corefx/src/System.Net.Mail/tests/Functional/HeaderCollectionTest.cs b/external/corefx/src/System.Net.Mail/tests/Functional/HeaderCollectionTest.cs new file mode 100644 index 0000000000..f828ec70f9 --- /dev/null +++ b/external/corefx/src/System.Net.Mail/tests/Functional/HeaderCollectionTest.cs @@ -0,0 +1,63 @@ +// 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.Net.Mail; +using Xunit; + +namespace System.Net.Mime.Tests +{ + public class HeaderCollectionTest + { + private MailMessage mail = new MailMessage(); + + [Fact] + public void Set_ValidNameAndValue_Success() + { + string validName = "foo"; + string validValue = "bar"; + mail.Headers.Set(validName, validValue); + Assert.Equal(validValue, mail.Headers.Get(validName)); + mail.Headers.Remove(validName); + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] + public void Set_EmptyName_Throws() + { + AssertExtensions.Throws("name", () => mail.Headers.Set(string.Empty, "value")); + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] + public void Set_EmptyValue_Throws() + { + AssertExtensions.Throws("value", () => mail.Headers.Set("name", string.Empty)); + } + + [Fact] + public void Add_ValidNameAndValue_Success() + { + string validName = "foo"; + string validValue = "bar"; + mail.Headers.Add(validName, validValue); + Assert.Equal(validValue, mail.Headers.Get(validName)); + mail.Headers.Remove(validName); + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] + public void Add_EmptyName_Throws() + { + AssertExtensions.Throws("name", () => mail.Headers.Add(string.Empty, "value")); + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] + public void Add_EmptyValue_Throws() + { + AssertExtensions.Throws("value", () => mail.Headers.Add("name", string.Empty)); + } + } +} diff --git a/external/corefx/src/System.Net.Mail/tests/Functional/MailMessageTest.cs b/external/corefx/src/System.Net.Mail/tests/Functional/MailMessageTest.cs index ae3202c925..c4f4a89b62 100644 --- a/external/corefx/src/System.Net.Mail/tests/Functional/MailMessageTest.cs +++ b/external/corefx/src/System.Net.Mail/tests/Functional/MailMessageTest.cs @@ -9,6 +9,8 @@ // (C) 2005, 2006 John Luke // +using System.IO; +using System.Reflection; using System.Text; using Xunit; @@ -142,5 +144,59 @@ namespace System.Net.Mail.Tests msg.Subject = "test\u3067\u3059"; Assert.Equal(Encoding.UTF8.CodePage, msg.SubjectEncoding.CodePage); } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] + public void SentSpecialLengthMailAttachment_Base64Decode_Success() + { + // The special length follows pattern: (3N - 1) * 0x4400 + 1 + // This length will trigger WriteState.Padding = 2 & count = 1 (byte to write) + // The smallest number to match the pattern is 34817. + int specialLength = 34817; + + string stringLength34817 = new string('A', specialLength - 1) + 'Z'; + byte[] toBytes = Encoding.ASCII.GetBytes(stringLength34817); + + using (var tempFile = TempFile.Create(toBytes)) + { + var message = new MailMessage("sender@test.com", "user1@pop.local", "testSubject", "testBody"); + message.Attachments.Add(new Attachment(tempFile.Path)); + string decodedAttachment = DecodeSentMailMessage(message); + + // Make sure last byte is not encoded twice. + Assert.Equal(specialLength, decodedAttachment.Length); + Assert.Equal("AAAAAAAAAAAAAAAAZ", decodedAttachment.Substring(34800)); + } + } + + private static string DecodeSentMailMessage(MailMessage mail) + { + // Create a MIME message that would be sent using System.Net.Mail. + var stream = new MemoryStream(); + var mailWriterType = mail.GetType().Assembly.GetType("System.Net.Mail.MailWriter"); + var mailWriter = Activator.CreateInstance( + type: mailWriterType, + bindingAttr: BindingFlags.Instance | BindingFlags.NonPublic, + binder: null, + args: new object[] { stream }, + culture: null, + activationAttributes: null); + + // Send the message. + mail.GetType().InvokeMember( + name: "Send", + invokeAttr: BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod, + binder: null, + target: mail, + args: new object[] { mailWriter, true, true }); + + // Decode contents. + string result = Encoding.UTF8.GetString(stream.ToArray()); + string encodedAttachment = result.Split(new[] { "attachment" }, StringSplitOptions.None)[1].Trim().Split('-')[0].Trim(); + byte[] data = Convert.FromBase64String(encodedAttachment); + string decodedString = Encoding.UTF8.GetString(data); + + return decodedString; + } } } diff --git a/external/corefx/src/System.Net.Mail/tests/Functional/System.Net.Mail.Functional.Tests.csproj b/external/corefx/src/System.Net.Mail/tests/Functional/System.Net.Mail.Functional.Tests.csproj index da55bbae70..72b77d3fb5 100644 --- a/external/corefx/src/System.Net.Mail/tests/Functional/System.Net.Mail.Functional.Tests.csproj +++ b/external/corefx/src/System.Net.Mail/tests/Functional/System.Net.Mail.Functional.Tests.csproj @@ -13,6 +13,7 @@ + @@ -25,6 +26,9 @@ Common\System\Diagnostics\Tracing\TestEventListener.cs + + Common\System\IO\TempFile.cs + diff --git a/external/corefx/src/System.Net.NameResolution/src/System.Net.NameResolution.csproj b/external/corefx/src/System.Net.NameResolution/src/System.Net.NameResolution.csproj index 6b4b016d26..ef34f9675b 100644 --- a/external/corefx/src/System.Net.NameResolution/src/System.Net.NameResolution.csproj +++ b/external/corefx/src/System.Net.NameResolution/src/System.Net.NameResolution.csproj @@ -176,10 +176,10 @@ - + diff --git a/external/corefx/src/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs b/external/corefx/src/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs index 3e93367e76..139e2740f9 100644 --- a/external/corefx/src/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs +++ b/external/corefx/src/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs @@ -207,7 +207,7 @@ namespace System.Net error = Interop.Sys.GetNameInfo( rawAddress, unchecked((uint)addressBuffer.Length), - addr.AddressFamily == AddressFamily.InterNetworkV6, + addr.AddressFamily == AddressFamily.InterNetworkV6 ? (byte)1 : (byte)0, buffer, Interop.Sys.NI_MAXHOST, null, diff --git a/external/corefx/src/System.Net.NameResolution/tests/FunctionalTests/GetHostAddressesTest.cs b/external/corefx/src/System.Net.NameResolution/tests/FunctionalTests/GetHostAddressesTest.cs index 30c0ffb420..e4b067d170 100644 --- a/external/corefx/src/System.Net.NameResolution/tests/FunctionalTests/GetHostAddressesTest.cs +++ b/external/corefx/src/System.Net.NameResolution/tests/FunctionalTests/GetHostAddressesTest.cs @@ -13,23 +13,17 @@ namespace System.Net.NameResolution.Tests public class GetHostAddressesTest { [Fact] - public void Dns_GetHostAddressesAsync_HostString_Ok() - { - TestGetHostAddressesAsync(() => Dns.GetHostAddressesAsync(TestSettings.LocalHost)); - } + public Task Dns_GetHostAddressesAsync_HostString_Ok() => TestGetHostAddressesAsync(() => Dns.GetHostAddressesAsync(TestSettings.LocalHost)); [Fact] - public void Dns_GetHostAddressesAsync_IPString_Ok() - { - TestGetHostAddressesAsync(() => Dns.GetHostAddressesAsync(TestSettings.LocalIPString)); - } - - private static void TestGetHostAddressesAsync(Func> getHostAddressesFunc) + public Task Dns_GetHostAddressesAsync_IPString_Ok() => TestGetHostAddressesAsync(() => Dns.GetHostAddressesAsync(TestSettings.LocalIPString)); + + private static async Task TestGetHostAddressesAsync(Func> getHostAddressesFunc) { Task hostEntryTask1 = getHostAddressesFunc(); Task hostEntryTask2 = getHostAddressesFunc(); - Task.WaitAll(hostEntryTask1, hostEntryTask2); + await TestSettings.WhenAllOrAnyFailedWithTimeout(hostEntryTask1, hostEntryTask2); IPAddress[] list1 = hostEntryTask1.Result; IPAddress[] list2 = hostEntryTask2.Result; diff --git a/external/corefx/src/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs b/external/corefx/src/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs index e5b5174e43..02981952c3 100644 --- a/external/corefx/src/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs +++ b/external/corefx/src/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs @@ -17,37 +17,28 @@ namespace System.Net.NameResolution.Tests { IPAddress localIPAddress = await TestSettings.GetLocalIPAddress(); - TestGetHostEntryAsync(() => Dns.GetHostEntryAsync(localIPAddress)); + await TestGetHostEntryAsync(() => Dns.GetHostEntryAsync(localIPAddress)); } [Theory] [InlineData("")] [InlineData(TestSettings.LocalHost)] - public void Dns_GetHostEntry_HostString_Ok(string hostName) - { - TestGetHostEntryAsync(() => Task.FromResult(Dns.GetHostEntry(hostName))); - } + public Task Dns_GetHostEntry_HostString_Ok(string hostName) => TestGetHostEntryAsync(() => Task.FromResult(Dns.GetHostEntry(hostName))); [Theory] [InlineData("")] [InlineData(TestSettings.LocalHost)] - public void Dns_GetHostEntryAsync_HostString_Ok(string hostName) - { - TestGetHostEntryAsync(() => Dns.GetHostEntryAsync(hostName)); - } + public Task Dns_GetHostEntryAsync_HostString_Ok(string hostName) => TestGetHostEntryAsync(() => Dns.GetHostEntryAsync(hostName)); [Fact] - public void Dns_GetHostEntryAsync_IPString_Ok() - { - TestGetHostEntryAsync(() => Dns.GetHostEntryAsync(TestSettings.LocalIPString)); - } + public Task Dns_GetHostEntryAsync_IPString_Ok() => TestGetHostEntryAsync(() => Dns.GetHostEntryAsync(TestSettings.LocalIPString)); - private static void TestGetHostEntryAsync(Func> getHostEntryFunc) + private static async Task TestGetHostEntryAsync(Func> getHostEntryFunc) { Task hostEntryTask1 = getHostEntryFunc(); Task hostEntryTask2 = getHostEntryFunc(); - Task.WaitAll(hostEntryTask1, hostEntryTask2); + await TestSettings.WhenAllOrAnyFailedWithTimeout(hostEntryTask1, hostEntryTask2); IPAddress[] list1 = hostEntryTask1.Result.AddressList; IPAddress[] list2 = hostEntryTask2.Result.AddressList; @@ -63,16 +54,10 @@ namespace System.Net.NameResolution.Tests } [Fact] - public async Task Dns_GetHostEntryAsync_NullStringHost_Fail() - { - await Assert.ThrowsAsync(() => Dns.GetHostEntryAsync((string)null)); - } + public Task Dns_GetHostEntryAsync_NullStringHost_Fail() => Assert.ThrowsAsync(() => Dns.GetHostEntryAsync((string)null)); [Fact] - public async Task Dns_GetHostEntryAsync_NullIPAddressHost_Fail() - { - await Assert.ThrowsAsync(() => Dns.GetHostEntryAsync((IPAddress)null)); - } + public Task Dns_GetHostEntryAsync_NullIPAddressHost_Fail() => Assert.ThrowsAsync(() => Dns.GetHostEntryAsync((IPAddress)null)); public static IEnumerable GetInvalidAddresses() { diff --git a/external/corefx/src/System.Net.NameResolution/tests/FunctionalTests/System.Net.NameResolution.Functional.Tests.csproj b/external/corefx/src/System.Net.NameResolution/tests/FunctionalTests/System.Net.NameResolution.Functional.Tests.csproj index 2044ee8ac8..1ee46dc2a5 100644 --- a/external/corefx/src/System.Net.NameResolution/tests/FunctionalTests/System.Net.NameResolution.Functional.Tests.csproj +++ b/external/corefx/src/System.Net.NameResolution/tests/FunctionalTests/System.Net.NameResolution.Functional.Tests.csproj @@ -17,6 +17,10 @@ + + + Common\System\Threading\Tasks\TaskTimeoutExtensions.cs + - \ No newline at end of file + diff --git a/external/corefx/src/System.Net.NameResolution/tests/FunctionalTests/TestSettings.cs b/external/corefx/src/System.Net.NameResolution/tests/FunctionalTests/TestSettings.cs index 3399efc804..6f64fbaef3 100644 --- a/external/corefx/src/System.Net.NameResolution/tests/FunctionalTests/TestSettings.cs +++ b/external/corefx/src/System.Net.NameResolution/tests/FunctionalTests/TestSettings.cs @@ -17,6 +17,9 @@ namespace System.Net.NameResolution.Tests public const string LocalIPString = "127.0.0.1"; + // Timeout values in milliseconds. + public const int PassingTestTimeout = 30_000; + public static Task GetLocalIPAddress() { return ResolveHost(TestSettings.LocalHost, TestSettings.AddressFamily); @@ -33,6 +36,8 @@ namespace System.Net.NameResolution.Tests } } + public static Task WhenAllOrAnyFailedWithTimeout(params Task[] tasks) => tasks.WhenAllOrAnyFailed(PassingTestTimeout); + private static async Task ResolveHost(string host, AddressFamily family) { var hostEntry = await Dns.GetHostEntryAsync(host); diff --git a/external/corefx/src/System.Net.NetworkInformation/src/System.Net.NetworkInformation.csproj b/external/corefx/src/System.Net.NetworkInformation/src/System.Net.NetworkInformation.csproj index 01cebd9657..575d33d0a3 100644 --- a/external/corefx/src/System.Net.NetworkInformation/src/System.Net.NetworkInformation.csproj +++ b/external/corefx/src/System.Net.NetworkInformation/src/System.Net.NetworkInformation.csproj @@ -349,7 +349,6 @@ - diff --git a/external/corefx/src/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxNetworkInterface.cs b/external/corefx/src/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxNetworkInterface.cs index 1b46167ecc..0d9dc1d7e6 100644 --- a/external/corefx/src/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxNetworkInterface.cs +++ b/external/corefx/src/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxNetworkInterface.cs @@ -225,7 +225,7 @@ namespace System.Net.NetworkInformation return OperationalStatus.Unknown; } - // Maps values from /sys/class/net//operstate to OperationStatus values. + // Maps values from /sys/class/net//operstate to OperationalStatus values. private static OperationalStatus MapState(string state) { // diff --git a/external/corefx/src/System.Net.NetworkInformation/src/System/Net/NetworkInformation/NetworkAddressChange.Linux.cs b/external/corefx/src/System.Net.NetworkInformation/src/System/Net/NetworkInformation/NetworkAddressChange.Linux.cs index c1d47a50d2..c7a29ebcef 100644 --- a/external/corefx/src/System.Net.NetworkInformation/src/System/Net/NetworkInformation/NetworkAddressChange.Linux.cs +++ b/external/corefx/src/System.Net.NetworkInformation/src/System/Net/NetworkInformation/NetworkAddressChange.Linux.cs @@ -15,10 +15,8 @@ namespace System.Net.NetworkInformation private static NetworkAddressChangedEventHandler s_addressChangedSubscribers; private static NetworkAvailabilityChangedEventHandler s_availabilityChangedSubscribers; private static volatile int s_socket = 0; - // Lock controlling access to delegate subscriptions and socket initialization. - private static readonly object s_subscriberLock = new object(); - // Lock controlling access to availability-changed state and timer. - private static readonly object s_availabilityLock = new object(); + // Lock controlling access to delegate subscriptions, socket initialization, availability-changed state and timer. + private static readonly object s_gate = new object(); private static readonly Interop.Sys.NetworkChangeEvent s_networkChangeCallback = ProcessEvent; // The "leniency" window for NetworkAvailabilityChanged socket events. @@ -39,7 +37,7 @@ namespace System.Net.NetworkInformation { add { - lock (s_subscriberLock) + lock (s_gate) { if (s_socket == 0) { @@ -51,7 +49,7 @@ namespace System.Net.NetworkInformation } remove { - lock (s_subscriberLock) + lock (s_gate) { if (s_addressChangedSubscribers == null && s_availabilityChangedSubscribers == null) { @@ -72,7 +70,7 @@ namespace System.Net.NetworkInformation { add { - lock (s_subscriberLock) + lock (s_gate) { if (s_socket == 0) { @@ -88,7 +86,7 @@ namespace System.Net.NetworkInformation } remove { - lock (s_subscriberLock) + lock (s_gate) { if (s_addressChangedSubscribers == null && s_availabilityChangedSubscribers == null) { @@ -103,6 +101,7 @@ namespace System.Net.NetworkInformation { s_availabilityTimer.Dispose(); s_availabilityTimer = null; + s_availabilityHasChanged = false; } if (s_addressChangedSubscribers == null) @@ -156,7 +155,7 @@ namespace System.Net.NetworkInformation { if (kind != Interop.Sys.NetworkChangeKind.None) { - lock (s_subscriberLock) + lock (s_gate) { if (socket == s_socket) { @@ -175,14 +174,16 @@ namespace System.Net.NetworkInformation s_addressChangedSubscribers?.Invoke(null, EventArgs.Empty); break; case Interop.Sys.NetworkChangeKind.AvailabilityChanged: - lock (s_availabilityLock) + lock (s_gate) { - if (!s_availabilityHasChanged) + if (s_availabilityTimer != null) { - s_availabilityTimer.Change(AvailabilityTimerWindowMilliseconds, -1); + if (!s_availabilityHasChanged) + { + s_availabilityTimer.Change(AvailabilityTimerWindowMilliseconds, -1); + } + s_availabilityHasChanged = true; } - - s_availabilityHasChanged = true; } break; } @@ -191,7 +192,7 @@ namespace System.Net.NetworkInformation private static void OnAvailabilityTimerFired(object state) { bool changed; - lock (s_availabilityLock) + lock (s_gate) { changed = s_availabilityHasChanged; s_availabilityHasChanged = false; diff --git a/external/corefx/src/System.Net.NetworkInformation/src/System/Net/NetworkInformation/NetworkAddressChange.Windows.cs b/external/corefx/src/System.Net.NetworkInformation/src/System/Net/NetworkInformation/NetworkAddressChange.Windows.cs index ab70bfacd6..f731dc3e79 100644 --- a/external/corefx/src/System.Net.NetworkInformation/src/System/Net/NetworkInformation/NetworkAddressChange.Windows.cs +++ b/external/corefx/src/System.Net.NetworkInformation/src/System/Net/NetworkInformation/NetworkAddressChange.Windows.cs @@ -15,6 +15,8 @@ namespace System.Net.NetworkInformation //introduced for supporting design-time loading of System.Windows.dll [Obsolete("This API supports the .NET Framework infrastructure and is not intended to be used directly from your code.", true)] public static void RegisterNetworkChange(NetworkChange nc) { } + + private static readonly object s_globalLock = new object(); public static event NetworkAvailabilityChangedEventHandler NetworkAvailabilityChanged { @@ -40,17 +42,8 @@ namespace System.Net.NetworkInformation } } - internal static bool CanListenForNetworkChanges - { - get - { - return true; - } - } - internal static class AvailabilityChangeListener { - private static readonly object s_syncObject = new object(); private static readonly Dictionary s_availabilityCallerArray = new Dictionary(); private static readonly NetworkAddressChangedEventHandler s_addressChange = ChangedAddress; @@ -64,28 +57,36 @@ namespace System.Net.NetworkInformation private static void ChangedAddress(object sender, EventArgs eventArgs) { - lock (s_syncObject) + Dictionary copy = null; + + lock (s_globalLock) { bool isAvailableNow = SystemNetworkInterface.InternalGetIsNetworkAvailable(); + // If there is an Availability Change, need to execute user callbacks. if (isAvailableNow != s_isAvailable) { s_isAvailable = isAvailableNow; - var s_copy = + copy = new Dictionary(s_availabilityCallerArray); + } + } - foreach (var handler in s_copy.Keys) + // Executing user callbacks if Availability Change event occured. + if (copy != null) + { + foreach (var entry in copy) + { + NetworkAvailabilityChangedEventHandler handler = entry.Key; + ExecutionContext context = entry.Value; + if (context == null) { - ExecutionContext context = s_copy[handler]; - if (context == null) - { - handler(null, new NetworkAvailabilityEventArgs(s_isAvailable)); - } - else - { - ExecutionContext.Run(context, s_RunHandlerCallback, handler); - } + handler(null, new NetworkAvailabilityEventArgs(s_isAvailable)); + } + else + { + ExecutionContext.Run(context, s_RunHandlerCallback, handler); } } } @@ -93,7 +94,7 @@ namespace System.Net.NetworkInformation internal static void Start(NetworkAvailabilityChangedEventHandler caller) { - lock (s_syncObject) + lock (s_globalLock) { if (s_availabilityCallerArray.Count == 0) { @@ -110,7 +111,7 @@ namespace System.Net.NetworkInformation internal static void Stop(NetworkAvailabilityChangedEventHandler caller) { - lock (s_syncObject) + lock (s_globalLock) { s_availabilityCallerArray.Remove(caller); if (s_availabilityCallerArray.Count == 0) @@ -140,7 +141,9 @@ namespace System.Net.NetworkInformation // Callback fired when an address change occurs. private static void AddressChangedCallback(object stateObject, bool signaled) { - lock (s_callerArray) + Dictionary copy; + + lock (s_globalLock) { // The listener was canceled, which would only happen if we aren't listening for more events. s_isPending = false; @@ -153,7 +156,7 @@ namespace System.Net.NetworkInformation s_isListening = false; // Need to copy the array so the callback can call start and stop - var copy = new Dictionary(s_callerArray); + copy = new Dictionary(s_callerArray); try { @@ -164,10 +167,15 @@ namespace System.Net.NetworkInformation { if (NetEventSource.IsEnabled) NetEventSource.Error(null, nie); } + } - foreach (var handler in copy.Keys) + // Release the lock before calling into user callback. + if (copy.Count > 0) + { + foreach (var entry in copy) { - ExecutionContext context = copy[handler]; + NetworkAddressChangedEventHandler handler = entry.Key; + ExecutionContext context = entry.Value; if (context == null) { handler(null, EventArgs.Empty); @@ -197,7 +205,7 @@ namespace System.Net.NetworkInformation private static void StartHelper(NetworkAddressChangedEventHandler caller, bool captureContext, StartIPOptions startIPOptions) { - lock (s_callerArray) + lock (s_globalLock) { // Setup changedEvent and native overlapped struct. if (s_ipv4Socket == null) @@ -323,7 +331,7 @@ namespace System.Net.NetworkInformation internal static void Stop(NetworkAddressChangedEventHandler caller) { - lock (s_callerArray) + lock (s_globalLock) { s_callerArray.Remove(caller); if (s_callerArray.Count == 0 && s_isListening) diff --git a/external/corefx/src/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemNetworkInterface.cs b/external/corefx/src/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemNetworkInterface.cs index 43bc90f701..e6f4764f32 100644 --- a/external/corefx/src/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemNetworkInterface.cs +++ b/external/corefx/src/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemNetworkInterface.cs @@ -5,7 +5,6 @@ using Microsoft.Win32.SafeHandles; using System.Collections.Generic; -using System.Diagnostics.Contracts; using System.Net.Sockets; using System.Runtime.InteropServices; @@ -81,7 +80,6 @@ namespace System.Net.NetworkInformation internal static NetworkInterface[] GetNetworkInterfaces() { - Contract.Ensures(Contract.Result() != null); AddressFamily family = AddressFamily.Unspecified; uint bufferSize = 0; SafeLocalAllocHandle buffer = null; diff --git a/external/corefx/src/System.Net.NetworkInformation/src/System/Net/NetworkInformation/UnixNetworkInterface.cs b/external/corefx/src/System.Net.NetworkInformation/src/System/Net/NetworkInformation/UnixNetworkInterface.cs index dd0ca01937..b98511a154 100644 --- a/external/corefx/src/System.Net.NetworkInformation/src/System/Net/NetworkInformation/UnixNetworkInterface.cs +++ b/external/corefx/src/System.Net.NetworkInformation/src/System/Net/NetworkInformation/UnixNetworkInterface.cs @@ -11,9 +11,9 @@ namespace System.Net.NetworkInformation internal abstract class UnixNetworkInterface : NetworkInterface { protected string _name; - protected int _index; + protected int _index = -1; protected NetworkInterfaceType _networkInterfaceType = NetworkInterfaceType.Unknown; - protected PhysicalAddress _physicalAddress; + protected PhysicalAddress _physicalAddress = PhysicalAddress.None; protected List _addresses = new List(); protected Dictionary _netMasks = new Dictionary(); // If this is an ipv6 device, contains the Scope ID. @@ -32,11 +32,7 @@ namespace System.Net.NetworkInformation public sealed override NetworkInterfaceType NetworkInterfaceType { get { return _networkInterfaceType; } } - public sealed override PhysicalAddress GetPhysicalAddress() - { - Debug.Assert(_physicalAddress != null, "_physicalAddress was never initialized. This means no address with type AF_PACKET was discovered."); - return _physicalAddress; - } + public sealed override PhysicalAddress GetPhysicalAddress() { return _physicalAddress; } public override bool Supports(NetworkInterfaceComponent networkInterfaceComponent) { @@ -76,6 +72,7 @@ namespace System.Net.NetworkInformation IPAddress netMaskAddress = IPAddressUtil.GetIPAddressFromNativeInfo(netMask); AddAddress(ipAddress); _netMasks[ipAddress] = netMaskAddress; + _index = addressInfo->InterfaceIndex; } protected unsafe void ProcessIpv6Address(Interop.Sys.IpAddressInfo* addressInfo, uint scopeId) @@ -84,6 +81,7 @@ namespace System.Net.NetworkInformation address.ScopeId = scopeId; AddAddress(address); _ipv6ScopeId = scopeId; + _index = addressInfo->InterfaceIndex; } protected unsafe void ProcessLinkLayerAddress(Interop.Sys.LinkLayerAddressInfo* llAddr) diff --git a/external/corefx/src/System.Net.NetworkInformation/tests/FunctionalTests/IPInterfacePropertiesTest_Linux.cs b/external/corefx/src/System.Net.NetworkInformation/tests/FunctionalTests/IPInterfacePropertiesTest_Linux.cs index b2aec957e2..e40fd15d3f 100644 --- a/external/corefx/src/System.Net.NetworkInformation/tests/FunctionalTests/IPInterfacePropertiesTest_Linux.cs +++ b/external/corefx/src/System.Net.NetworkInformation/tests/FunctionalTests/IPInterfacePropertiesTest_Linux.cs @@ -20,7 +20,8 @@ namespace System.Net.NetworkInformation.Tests _log = TestLogging.GetInstance(); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/Microsoft/BashOnWindows/issues/308 + // dotnet: /d/git/corefx/src/Native/Unix/Common/pal_safecrt.h:47: errno_t memcpy_s(void *, size_t, const void *, size_t): Assertion `sizeInBytes >= count' failed. + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/dotnet/corefx/issues/15513 public void IPInfoTest_AccessAllProperties_NoErrors() { foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) @@ -102,7 +103,8 @@ namespace System.Net.NetworkInformation.Tests } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/Microsoft/BashOnWindows/issues/308 + // dotnet: /d/git/corefx/src/Native/Unix/Common/pal_safecrt.h:47: errno_t memcpy_s(void *, size_t, const void *, size_t): Assertion `sizeInBytes >= count' failed. + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/dotnet/corefx/issues/15513 public void IPInfoTest_AccessAllIPv4Properties_NoErrors() { foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) @@ -125,7 +127,8 @@ namespace System.Net.NetworkInformation.Tests } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/Microsoft/BashOnWindows/issues/308 + // dotnet: /d/git/corefx/src/Native/Unix/Common/pal_safecrt.h:47: errno_t memcpy_s(void *, size_t, const void *, size_t): Assertion `sizeInBytes >= count' failed. + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/dotnet/corefx/issues/15513 public void IPInfoTest_AccessAllIPv6Properties_NoErrors() { foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) @@ -150,7 +153,8 @@ namespace System.Net.NetworkInformation.Tests } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/Microsoft/BashOnWindows/issues/308 + // dotnet: /d/git/corefx/src/Native/Unix/Common/pal_safecrt.h:47: errno_t memcpy_s(void *, size_t, const void *, size_t): Assertion `sizeInBytes >= count' failed. + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/dotnet/corefx/issues/15513 [Trait("IPv6", "true")] public void IPv6ScopeId_AccessAllValues_Success() { diff --git a/external/corefx/src/System.Net.NetworkInformation/tests/FunctionalTests/NetworkInterfaceBasicTest.cs b/external/corefx/src/System.Net.NetworkInformation/tests/FunctionalTests/NetworkInterfaceBasicTest.cs index 4c43121085..cbe912e60a 100644 --- a/external/corefx/src/System.Net.NetworkInformation/tests/FunctionalTests/NetworkInterfaceBasicTest.cs +++ b/external/corefx/src/System.Net.NetworkInformation/tests/FunctionalTests/NetworkInterfaceBasicTest.cs @@ -20,7 +20,8 @@ namespace System.Net.NetworkInformation.Tests _log = TestLogging.GetInstance(); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/Microsoft/BashOnWindows/issues/308 + // dotnet: /d/git/corefx/src/Native/Unix/Common/pal_safecrt.h:47: errno_t memcpy_s(void *, size_t, const void *, size_t): Assertion `sizeInBytes >= count' failed. + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/dotnet/corefx/issues/15513 public void BasicTest_GetNetworkInterfaces_AtLeastOne() { Assert.NotEqual(0, NetworkInterface.GetAllNetworkInterfaces().Length); @@ -51,7 +52,8 @@ namespace System.Net.NetworkInformation.Tests } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/Microsoft/BashOnWindows/issues/308 + // dotnet: /d/git/corefx/src/Native/Unix/Common/pal_safecrt.h:47: errno_t memcpy_s(void *, size_t, const void *, size_t): Assertion `sizeInBytes >= count' failed. + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/dotnet/corefx/issues/15513 [PlatformSpecific(TestPlatforms.Linux)] // Some APIs are not supported on Linux public void BasicTest_AccessInstanceProperties_NoExceptions_Linux() { @@ -109,7 +111,8 @@ namespace System.Net.NetworkInformation.Tests } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/Microsoft/BashOnWindows/issues/308 + // dotnet: /d/git/corefx/src/Native/Unix/Common/pal_safecrt.h:47: errno_t memcpy_s(void *, size_t, const void *, size_t): Assertion `sizeInBytes >= count' failed. + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/dotnet/corefx/issues/15513 [Trait("IPv4", "true")] public void BasicTest_StaticLoopbackIndex_MatchesLoopbackNetworkInterface() { @@ -131,7 +134,8 @@ namespace System.Net.NetworkInformation.Tests } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/Microsoft/BashOnWindows/issues/308 + // dotnet: /d/git/corefx/src/Native/Unix/Common/pal_safecrt.h:47: errno_t memcpy_s(void *, size_t, const void *, size_t): Assertion `sizeInBytes >= count' failed. + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/dotnet/corefx/issues/15513 [Trait("IPv4", "true")] public void BasicTest_StaticLoopbackIndex_ExceptionIfV4NotSupported() { @@ -140,7 +144,8 @@ namespace System.Net.NetworkInformation.Tests _log.WriteLine("Loopback IPv4 index: " + NetworkInterface.LoopbackInterfaceIndex); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/Microsoft/BashOnWindows/issues/308 + // dotnet: /d/git/corefx/src/Native/Unix/Common/pal_safecrt.h:47: errno_t memcpy_s(void *, size_t, const void *, size_t): Assertion `sizeInBytes >= count' failed. + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/dotnet/corefx/issues/15513 [Trait("IPv6", "true")] public void BasicTest_StaticIPv6LoopbackIndex_MatchesLoopbackNetworkInterface() { @@ -164,7 +169,8 @@ namespace System.Net.NetworkInformation.Tests } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/Microsoft/BashOnWindows/issues/308 + // dotnet: /d/git/corefx/src/Native/Unix/Common/pal_safecrt.h:47: errno_t memcpy_s(void *, size_t, const void *, size_t): Assertion `sizeInBytes >= count' failed. + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/dotnet/corefx/issues/15513 [Trait("IPv6", "true")] public void BasicTest_StaticIPv6LoopbackIndex_ExceptionIfV6NotSupported() { @@ -196,7 +202,8 @@ namespace System.Net.NetworkInformation.Tests } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/Microsoft/BashOnWindows/issues/308 + // dotnet: /d/git/corefx/src/Native/Unix/Common/pal_safecrt.h:47: errno_t memcpy_s(void *, size_t, const void *, size_t): Assertion `sizeInBytes >= count' failed. + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/dotnet/corefx/issues/15513 [PlatformSpecific(TestPlatforms.Linux)] // Some APIs are not supported on Linux public void BasicTest_GetIPInterfaceStatistics_Success_Linux() { @@ -244,13 +251,16 @@ namespace System.Net.NetworkInformation.Tests } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/Microsoft/BashOnWindows/issues/308 + + // dotnet: /d/git/corefx/src/Native/Unix/Common/pal_safecrt.h:47: errno_t memcpy_s(void *, size_t, const void *, size_t): Assertion `sizeInBytes >= count' failed. + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/dotnet/corefx/issues/15513 public void BasicTest_GetIsNetworkAvailable_Success() { Assert.True(NetworkInterface.GetIsNetworkAvailable()); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/Microsoft/BashOnWindows/issues/308 + // dotnet: /d/git/corefx/src/Native/Unix/Common/pal_safecrt.h:47: errno_t memcpy_s(void *, size_t, const void *, size_t): Assertion `sizeInBytes >= count' failed. + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/dotnet/corefx/issues/15513 [PlatformSpecific(~TestPlatforms.OSX)] [InlineData(false)] [InlineData(true)] diff --git a/external/corefx/src/System.Net.NetworkInformation/tests/FunctionalTests/NetworkInterfaceIPv4Statistics.cs b/external/corefx/src/System.Net.NetworkInformation/tests/FunctionalTests/NetworkInterfaceIPv4Statistics.cs index 2aa59259ef..e1264cb2f7 100644 --- a/external/corefx/src/System.Net.NetworkInformation/tests/FunctionalTests/NetworkInterfaceIPv4Statistics.cs +++ b/external/corefx/src/System.Net.NetworkInformation/tests/FunctionalTests/NetworkInterfaceIPv4Statistics.cs @@ -45,7 +45,8 @@ namespace System.Net.NetworkInformation.Tests } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/Microsoft/BashOnWindows/issues/308 + // dotnet: /d/git/corefx/src/Native/Unix/Common/pal_safecrt.h:47: errno_t memcpy_s(void *, size_t, const void *, size_t): Assertion `sizeInBytes >= count' failed. + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/dotnet/corefx/issues/15513 [PlatformSpecific(TestPlatforms.Linux)] // Some APIs are not supported on Linux public void BasicTest_GetIPv4InterfaceStatistics_Success_Linux() { diff --git a/external/corefx/src/System.Net.Ping/src/System.Net.Ping.csproj b/external/corefx/src/System.Net.Ping/src/System.Net.Ping.csproj index b5b56273c1..61b55ca48d 100644 --- a/external/corefx/src/System.Net.Ping/src/System.Net.Ping.csproj +++ b/external/corefx/src/System.Net.Ping/src/System.Net.Ping.csproj @@ -144,6 +144,7 @@ + diff --git a/external/corefx/src/System.Net.Ping/src/System/Net/NetworkInformation/PingException.cs b/external/corefx/src/System.Net.Ping/src/System/Net/NetworkInformation/PingException.cs index 7af8f88f30..fff555ff61 100644 --- a/external/corefx/src/System.Net.Ping/src/System/Net/NetworkInformation/PingException.cs +++ b/external/corefx/src/System.Net.Ping/src/System/Net/NetworkInformation/PingException.cs @@ -7,6 +7,9 @@ using System.Runtime.Serialization; namespace System.Net.NetworkInformation { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class PingException : InvalidOperationException { public PingException(string message) : @@ -22,7 +25,6 @@ namespace System.Net.NetworkInformation protected PingException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corefx/src/System.Net.Ping/tests/FunctionalTests/PingTest.cs b/external/corefx/src/System.Net.Ping/tests/FunctionalTests/PingTest.cs index c4a31ebe24..eb97c37ab9 100644 --- a/external/corefx/src/System.Net.Ping/tests/FunctionalTests/PingTest.cs +++ b/external/corefx/src/System.Net.Ping/tests/FunctionalTests/PingTest.cs @@ -466,6 +466,7 @@ namespace System.Net.NetworkInformation.Tests } [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.Mono, "GC has different behavior on Mono")] public void CanBeFinalized() { FinalizingPing.CreateAndRelease(); diff --git a/external/corefx/src/System.Net.Primitives/src/System.Net.Primitives.csproj b/external/corefx/src/System.Net.Primitives/src/System.Net.Primitives.csproj index 6e4bc560ae..f759c979af 100644 --- a/external/corefx/src/System.Net.Primitives/src/System.Net.Primitives.csproj +++ b/external/corefx/src/System.Net.Primitives/src/System.Net.Primitives.csproj @@ -197,8 +197,6 @@ - - diff --git a/external/corefx/src/System.Net.Primitives/src/System/Net/Cookie.cs b/external/corefx/src/System.Net.Primitives/src/System/Net/Cookie.cs index 5252ff538d..0a1c452df1 100644 --- a/external/corefx/src/System.Net.Primitives/src/System/Net/Cookie.cs +++ b/external/corefx/src/System.Net.Primitives/src/System/Net/Cookie.cs @@ -68,30 +68,30 @@ namespace System.Net private string m_domainKey = string.Empty; // Do not rename (binary serialization) -/* +/* TODO: #13607 VSO 449560 Reflecting on internal method won't work on AOT without rd.xml and DisableReflection block in toolchain.Networking team will be working on exposing methods from S.Net.Primitive - public, this is a temporary workaround till that happens. + public, this is a temporary workaround till that happens. */ -#if uap +#if uap public -#else +#else internal #endif bool IsQuotedVersion = false; -/* +/* TODO: #13607 VSO 449560 Reflecting on internal method won't work on AOT without rd.xml and DisableReflection block in toolchain.Networking team will be working on exposing methods from S.Net.Primitive - public, this is a temporary workaround till that happens. + public, this is a temporary workaround till that happens. */ -#if uap +#if uap public -#else +#else internal #endif bool IsQuotedDomain = false; @@ -243,16 +243,16 @@ namespace System.Net } } -/* +/* TODO: #13607 VSO 449560 Reflecting on internal method won't work on AOT without rd.xml and DisableReflection block in toolchain.Networking team will be working on exposing methods from S.Net.Primitive - public, this is a temporary workaround till that happens. + public, this is a temporary workaround till that happens. */ -#if uap +#if uap public -#else +#else internal #endif bool InternalSetName(string value) @@ -287,16 +287,16 @@ namespace System.Net } } -/* +/* TODO: #13607 VSO 449560 Reflecting on internal method won't work on AOT without rd.xml and DisableReflection block in toolchain.Networking team will be working on exposing methods from S.Net.Primitive - public, this is a temporary workaround till that happens. + public, this is a temporary workaround till that happens. */ -#if uap +#if uap public -#else +#else internal #endif Cookie Clone() @@ -325,8 +325,8 @@ namespace System.Net clonedCookie.Version = m_version; clonedCookie.Secure = m_secure; - // The variant is set when we set properties like port/version. So, - // we should copy over the variant from the original cookie after + // The variant is set when we set properties like port/version. So, + // we should copy over the variant from the original cookie after // we set all other properties clonedCookie.m_cookieVariant = m_cookieVariant; @@ -704,16 +704,16 @@ namespace System.Net } } -/* +/* TODO: #13607 VSO 449560 Reflecting on internal method won't work on AOT without rd.xml and DisableReflection block in toolchain.Networking team will be working on exposing methods from S.Net.Primitive - public, this is a temporary workaround till that happens. + public, this is a temporary workaround till that happens. */ -#if uap +#if uap public -#else +#else internal #endif CookieVariant Variant @@ -845,16 +845,16 @@ namespace System.Net } } -/* +/* TODO: #13607 VSO 449560 Reflecting on internal method won't work on AOT without rd.xml and DisableReflection block in toolchain.Networking team will be working on exposing methods from S.Net.Primitive - public, this is a temporary workaround till that happens. + public, this is a temporary workaround till that happens. */ -#if uap +#if uap public -#else +#else internal #endif string ToServerString() @@ -902,32 +902,5 @@ namespace System.Net } return result == EqualsLiteral ? null : result; } - -#if DEBUG - internal void Dump() - { - if (NetEventSource.IsEnabled) - { - if (NetEventSource.IsEnabled) NetEventSource.Info(this, - "Cookie: " + ToString() + "->\n" - + "\tComment = " + Comment + "\n" - + "\tCommentUri = " + CommentUri + "\n" - + "\tDiscard = " + Discard + "\n" - + "\tDomain = " + Domain + "\n" - + "\tExpired = " + Expired + "\n" - + "\tExpires = " + Expires + "\n" - + "\tName = " + Name + "\n" - + "\tPath = " + Path + "\n" - + "\tPort = " + Port + "\n" - + "\tSecure = " + Secure + "\n" - + "\tTimeStamp = " + TimeStamp + "\n" - + "\tValue = " + Value + "\n" - + "\tVariant = " + Variant + "\n" - + "\tVersion = " + Version + "\n" - + "\tHttpOnly = " + HttpOnly + "\n" - ); - } - } -#endif } } diff --git a/external/corefx/src/System.Net.Primitives/src/System/Net/CookieCollection.cs b/external/corefx/src/System.Net.Primitives/src/System/Net/CookieCollection.cs index 732aad4857..ae67ad02d2 100644 --- a/external/corefx/src/System.Net.Primitives/src/System/Net/CookieCollection.cs +++ b/external/corefx/src/System.Net.Primitives/src/System/Net/CookieCollection.cs @@ -175,16 +175,16 @@ namespace System.Net // If isStrict == true, replace the cookie if found same with newest Variant. // Returns 1 if added, 0 if replaced or rejected. -/* +/* TODO: #13607 VSO 449560 Reflecting on internal method won't work on AOT without rd.xml and DisableReflection block in toolchain.Networking team will be working on exposing methods from S.Net.Primitive - public, this is a temporary workaround till that happens. + public, this is a temporary workaround till that happens. */ -#if uap +#if uap public -#else +#else internal #endif int InternalAdd(Cookie cookie, bool isStrict) @@ -247,19 +247,5 @@ namespace System.Net { return m_list.GetEnumerator(); } - -#if DEBUG - internal void Dump() - { - if (NetEventSource.IsEnabled) - { - if (NetEventSource.IsEnabled) NetEventSource.Enter(this); - foreach (Cookie cookie in this) - { - cookie.Dump(); - } - } - } -#endif } } diff --git a/external/corefx/src/System.Net.Primitives/src/System/Net/CookieContainer.cs b/external/corefx/src/System.Net.Primitives/src/System/Net/CookieContainer.cs index 32fa5f3951..27dc045528 100644 --- a/external/corefx/src/System.Net.Primitives/src/System/Net/CookieContainer.cs +++ b/external/corefx/src/System.Net.Primitives/src/System/Net/CookieContainer.cs @@ -56,7 +56,7 @@ using System.Text; namespace System.Net { - internal struct HeaderVariantInfo + internal readonly struct HeaderVariantInfo { private readonly string _name; private readonly CookieVariant _variant; @@ -677,7 +677,11 @@ namespace System.Net if (cookie == null) { - break; + if (parser.EndofHeader()) + { + break; + } + continue; } // Parser marks invalid cookies this way @@ -810,8 +814,6 @@ namespace System.Net { for (int i = 0; i < domainAttribute.Count; i++) { - bool found = false; - bool defaultAdded = false; PathList pathList; lock (m_domainTable.SyncRoot) { @@ -831,32 +833,10 @@ namespace System.Net string path = (string)e.Key; if (uri.AbsolutePath.StartsWith(CookieParser.CheckQuoted(path))) { - found = true; - CookieCollection cc = (CookieCollection)e.Value; cc.TimeStamp(CookieCollection.Stamp.Set); MergeUpdateCollections(ref cookies, cc, port, isSecure, matchOnlyPlainCookie); - - if (path == "/") - { - defaultAdded = true; - } } - else if (found) - { - break; - } - } - } - - if (!defaultAdded) - { - CookieCollection cc = (CookieCollection)pathList["/"]; - - if (cc != null) - { - cc.TimeStamp(CookieCollection.Stamp.Set); - MergeUpdateCollections(ref cookies, cc, port, isSecure, matchOnlyPlainCookie); } } diff --git a/external/corefx/src/System.Net.Primitives/src/System/Net/CookieException.cs b/external/corefx/src/System.Net.Primitives/src/System/Net/CookieException.cs index b21e7223a2..e1cf3e858c 100644 --- a/external/corefx/src/System.Net.Primitives/src/System/Net/CookieException.cs +++ b/external/corefx/src/System.Net.Primitives/src/System/Net/CookieException.cs @@ -7,6 +7,9 @@ using System.Runtime.Serialization; namespace System.Net { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class CookieException : FormatException, ISerializable { public CookieException() : base() @@ -24,7 +27,6 @@ namespace System.Net protected CookieException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { - throw new PlatformNotSupportedException(); } void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) diff --git a/external/corefx/src/System.Net.Primitives/src/System/Net/CredentialCache.cs b/external/corefx/src/System.Net.Primitives/src/System/Net/CredentialCache.cs index 88e4f193fe..9b5572b9ee 100644 --- a/external/corefx/src/System.Net.Primitives/src/System/Net/CredentialCache.cs +++ b/external/corefx/src/System.Net.Primitives/src/System/Net/CredentialCache.cs @@ -418,7 +418,7 @@ namespace System.Net } } - internal struct CredentialHostKey : IEquatable + internal readonly struct CredentialHostKey : IEquatable { public readonly string Host; public readonly string AuthenticationType; diff --git a/external/corefx/src/System.Net.Primitives/src/System/Net/IPAddress.cs b/external/corefx/src/System.Net.Primitives/src/System/Net/IPAddress.cs index 16be2f6374..5aeeac6ccc 100644 --- a/external/corefx/src/System.Net.Primitives/src/System/Net/IPAddress.cs +++ b/external/corefx/src/System.Net.Primitives/src/System/Net/IPAddress.cs @@ -2,6 +2,7 @@ // 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.Buffers.Binary; using System.Diagnostics; using System.Net.Sockets; using System.Runtime.CompilerServices; @@ -399,36 +400,17 @@ namespace System.Net public static long HostToNetworkOrder(long host) { -#if BIGENDIAN - return host; -#else - ulong value = (ulong)host; - value = (value << 32) | (value >> 32); - value = (value & 0x0000FFFF0000FFFF) << 16 | (value & 0xFFFF0000FFFF0000) >> 16; - value = (value & 0x00FF00FF00FF00FF) << 8 | (value & 0xFF00FF00FF00FF00) >> 8; - return (long)value; -#endif + return BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(host) : host; } public static int HostToNetworkOrder(int host) { -#if BIGENDIAN - return host; -#else - uint value = (uint)host; - value = (value << 16) | (value >> 16); - value = (value & 0x00FF00FF) << 8 | (value & 0xFF00FF00) >> 8; - return (int)value; -#endif + return BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(host) : host; } public static short HostToNetworkOrder(short host) { -#if BIGENDIAN - return host; -#else - return unchecked((short)((((int)host & 0xFF) << 8) | (int)((host >> 8) & 0xFF))); -#endif + return BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(host) : host; } public static long NetworkToHostOrder(long network) @@ -635,16 +617,17 @@ namespace System.Net Debug.Assert(scopeWritten); hashCode = Marvin.ComputeHash32( - ref addressAndScopeIdSpan[0], - addressAndScopeIdLength, + addressAndScopeIdSpan, Marvin.DefaultSeed); } else { + Span addressOrScopeIdSpan = stackalloc uint[1]; + addressOrScopeIdSpan[0] = _addressOrScopeId; + // For IPv4 addresses, we use Marvin on the integer representation of the Address. hashCode = Marvin.ComputeHash32( - ref Unsafe.As(ref _addressOrScopeId), - sizeof(uint), + addressOrScopeIdSpan.AsBytes(), Marvin.DefaultSeed); } @@ -652,14 +635,6 @@ namespace System.Net return _hashCode; } - // For security, we need to be able to take an IPAddress and make a copy that's immutable and not derived. - internal IPAddress Snapshot() - { - return IsIPv4 ? - new IPAddress(PrivateAddress) : - new IPAddress(_numbers, PrivateScopeId); - } - // IPv4 192.168.1.1 maps as ::FFFF:192.168.1.1 public IPAddress MapToIPv6() { diff --git a/external/corefx/src/System.Net.Primitives/src/System/Net/IPAddressParser.cs b/external/corefx/src/System.Net.Primitives/src/System/Net/IPAddressParser.cs index ba02a6278d..930914f9b2 100644 --- a/external/corefx/src/System.Net.Primitives/src/System/Net/IPAddressParser.cs +++ b/external/corefx/src/System.Net.Primitives/src/System/Net/IPAddressParser.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.IO; using System.Net.Sockets; +using System.Runtime.InteropServices; using System.Text; namespace System.Net @@ -53,7 +54,7 @@ namespace System.Net return false; } - fixed (char* formattedPtr = &formatted.DangerousGetPinnableReference()) + fixed (char* formattedPtr = &MemoryMarshal.GetReference(formatted)) { charsWritten = IPv4AddressToStringHelper(address, formattedPtr); } @@ -160,7 +161,7 @@ namespace System.Net int end = ipSpan.Length; long tmpAddr; - fixed (char* ipStringPtr = &ipSpan.DangerousGetPinnableReference()) + fixed (char* ipStringPtr = &MemoryMarshal.GetReference(ipSpan)) { tmpAddr = IPv4AddressHelper.ParseNonCanonical(ipStringPtr, 0, ref end, notImplicitFile: true); } @@ -192,7 +193,7 @@ namespace System.Net int end = ipSpan.Length; bool isValid = false; - fixed (char* ipStringPtr = &ipSpan.DangerousGetPinnableReference()) + fixed (char* ipStringPtr = &MemoryMarshal.GetReference(ipSpan)) { isValid = IPv6AddressHelper.IsValidStrict(ipStringPtr, 0, ref end); } diff --git a/external/corefx/src/System.Net.Primitives/src/System/Net/IPEndPoint.cs b/external/corefx/src/System.Net.Primitives/src/System/Net/IPEndPoint.cs index db8635632b..f6bf23e53a 100644 --- a/external/corefx/src/System.Net.Primitives/src/System/Net/IPEndPoint.cs +++ b/external/corefx/src/System.Net.Primitives/src/System/Net/IPEndPoint.cs @@ -149,22 +149,12 @@ namespace System.Net public override bool Equals(object comparand) { - if (!(comparand is IPEndPoint)) - { - return false; - } - return ((IPEndPoint)comparand)._address.Equals(_address) && ((IPEndPoint)comparand)._port == _port; + return comparand is IPEndPoint other && other._address.Equals(_address) && other._port == _port; } public override int GetHashCode() { return _address.GetHashCode() ^ _port; } - - // For security, we need to be able to take an IPEndPoint and make a copy that's immutable and not derived. - internal IPEndPoint Snapshot() - { - return new IPEndPoint(Address.Snapshot(), Port); - } } } diff --git a/external/corefx/src/System.Net.Primitives/src/System/Net/SocketException.cs b/external/corefx/src/System.Net.Primitives/src/System/Net/SocketException.cs index 9f7ec9e759..389a4edd02 100644 --- a/external/corefx/src/System.Net.Primitives/src/System/Net/SocketException.cs +++ b/external/corefx/src/System.Net.Primitives/src/System/Net/SocketException.cs @@ -9,6 +9,9 @@ namespace System.Net.Sockets { /// Provides socket exceptions to the application. [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public partial class SocketException : Win32Exception { /// The SocketError or Int32 specified when constructing the exception. @@ -44,7 +47,7 @@ namespace System.Net.Sockets protected SocketException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { - throw new PlatformNotSupportedException(); + if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"{NativeErrorCode}:{Message}"); } public override int ErrorCode => base.NativeErrorCode; diff --git a/external/corefx/src/System.Net.Primitives/tests/UnitTests/CookieContainerTest.cs b/external/corefx/src/System.Net.Primitives/tests/UnitTests/CookieContainerTest.cs index ce3414061f..810465d3b1 100644 --- a/external/corefx/src/System.Net.Primitives/tests/UnitTests/CookieContainerTest.cs +++ b/external/corefx/src/System.Net.Primitives/tests/UnitTests/CookieContainerTest.cs @@ -143,7 +143,9 @@ namespace System.Net.Primitives.Unit.Tests } }; // RFC 2965 - yield return new object[] { u, + yield return new object[] + { + u, "name98=value98; port=\"80, 90\", name99=value99", new Cookie[] { @@ -152,7 +154,8 @@ namespace System.Net.Primitives.Unit.Tests } }; // RFC 2965 (no path) - yield return new object[] { + yield return new object[] + { uSecure, "name98=value98; name98=value98; comment=comment; comment=comment2; commentURL=http://url.com; commentURL=commentURL2; discard; discard; domain=.uri.com; domain=domain2; max-age=400; max-age=400; path=/; path=path; port=\"80, 90, 443\"; port=port2; path=path; expires=Wed, 09 Jun 2021 10:18:14 GMT; expires=expires2; secure; secure; httponly; httponly; Version=100; Version=100, name99=value99", new Cookie[] @@ -162,7 +165,8 @@ namespace System.Net.Primitives.Unit.Tests } }; // Double entries - yield return new object[] { + yield return new object[] + { u, "name98=value98; commentURL=invalidurl", new Cookie[] @@ -171,7 +175,8 @@ namespace System.Net.Primitives.Unit.Tests } }; // Ignore invalid comment url - yield return new object[] { + yield return new object[] + { u6, "name98=value98; unknown1; unknown2=unknown", new Cookie[] @@ -180,7 +185,8 @@ namespace System.Net.Primitives.Unit.Tests } }; // Ignore unknown tokens - yield return new object[] { + yield return new object[] + { u6, "name98=value98; =; token=", new Cookie[] @@ -189,7 +195,8 @@ namespace System.Net.Primitives.Unit.Tests } }; // Ignore invalid tokens - yield return new object[] { + yield return new object[] + { u6, "name98=\"value; domain=\".domain\"; max-age=\"400\"", new Cookie[] @@ -198,7 +205,8 @@ namespace System.Net.Primitives.Unit.Tests } }; // Use escaped values (1) - yield return new object[] { + yield return new object[] + { u6, "name98=\"\"", new Cookie[] @@ -206,6 +214,123 @@ namespace System.Net.Primitives.Unit.Tests new Cookie("name98", "\"\"") } }; // Use escaped values (2) + + yield return new object[] + { + u, + "locale=en, uuid=4b8b2dd7-d91a-49ee-80c6-8cb7df1fae46, country=US, _m_ask_fm_session=session1", + new Cookie[] + { + new Cookie("locale", "en"), + new Cookie("uuid", "4b8b2dd7-d91a-49ee-80c6-8cb7df1fae46"), + new Cookie("country", "US"), + new Cookie("_m_ask_fm_session", "session1") + } + }; // Normal case + + yield return new object[] + { + uSecure, + "locale=en, uuid=4b8b2dd7-d91a-49ee-80c6-8cb7df1fae46, country=US, _m_ask_fm_session=session1", + new Cookie[] + { + new Cookie("locale", "en"), + new Cookie("uuid", "4b8b2dd7-d91a-49ee-80c6-8cb7df1fae46"), + new Cookie("country", "US"), + new Cookie("_m_ask_fm_session", "session1") + } + }; // Normal case with secure URI + + yield return new object[] + { + u, + ",locale=en, uuid=4b8b2dd7-d91a-49ee-80c6-8cb7df1fae46, country=US, _m_ask_fm_session=session1", + new Cookie[] + { + new Cookie("locale", "en"), + new Cookie("uuid", "4b8b2dd7-d91a-49ee-80c6-8cb7df1fae46"), + new Cookie("country", "US"), + new Cookie("_m_ask_fm_session", "session1") + } + }; // Empty header at the beginning + + yield return new object[] + { + uSecure, + " ,locale=en, uuid=4b8b2dd7-d91a-49ee-80c6-8cb7df1fae46, country=US, _m_ask_fm_session=session1", + new Cookie[] + { + new Cookie("locale", "en"), + new Cookie("uuid", "4b8b2dd7-d91a-49ee-80c6-8cb7df1fae46"), + new Cookie("country", "US"), + new Cookie("_m_ask_fm_session", "session1") + } + }; // Empty header composed of spaces at the beginning + + yield return new object[] + { + u, + "locale=en,, uuid=4b8b2dd7-d91a-49ee-80c6-8cb7df1fae46, country=US, _m_ask_fm_session=session1", + new Cookie[] + { + new Cookie("locale", "en"), + new Cookie("uuid", "4b8b2dd7-d91a-49ee-80c6-8cb7df1fae46"), + new Cookie("country", "US"), + new Cookie("_m_ask_fm_session", "session1") + } + }; // Empty header in the middle + + yield return new object[] + { + uSecure, + "locale=en, uuid=4b8b2dd7-d91a-49ee-80c6-8cb7df1fae46, , country=US, _m_ask_fm_session=session1", + new Cookie[] + { + new Cookie("locale", "en"), + new Cookie("uuid", "4b8b2dd7-d91a-49ee-80c6-8cb7df1fae46"), + new Cookie("country", "US"), + new Cookie("_m_ask_fm_session", "session1") + } + }; // Empty header composed of spaces in the middle + + yield return new object[] + { + u, + "locale=en, uuid=4b8b2dd7-d91a-49ee-80c6-8cb7df1fae46, country=US, _m_ask_fm_session=session1,", + new Cookie[] + { + new Cookie("locale", "en"), + new Cookie("uuid", "4b8b2dd7-d91a-49ee-80c6-8cb7df1fae46"), + new Cookie("country", "US"), + new Cookie("_m_ask_fm_session", "session1") + } + }; // Empty header at the end + + yield return new object[] + { + u, + "locale=en, uuid=4b8b2dd7-d91a-49ee-80c6-8cb7df1fae46, country=US, _m_ask_fm_session=session1, ", + new Cookie[] + { + new Cookie("locale", "en"), + new Cookie("uuid", "4b8b2dd7-d91a-49ee-80c6-8cb7df1fae46"), + new Cookie("country", "US"), + new Cookie("_m_ask_fm_session", "session1") + } + }; // Empty header composed of spaces at the end + + yield return new object[] + { + uSecure, + "locale=en, uuid=4b8b2dd7-d91a-49ee-80c6-8cb7df1fae46, country=US, _m_ask_fm_session=session1, ,", + new Cookie[] + { + new Cookie("locale", "en"), + new Cookie("uuid", "4b8b2dd7-d91a-49ee-80c6-8cb7df1fae46"), + new Cookie("country", "US"), + new Cookie("_m_ask_fm_session", "session1") + } + }; // Empty header followed by another empty header at the end } [Theory] @@ -218,6 +343,7 @@ namespace System.Net.Primitives.Unit.Tests [Theory] [MemberData(nameof(SetCookiesData))] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] public void SetCookies_Success(Uri uri, string cookieHeader, Cookie[] expected) { CookieContainer cc = CreateCount11Container(); @@ -518,7 +644,6 @@ namespace System.Net.Primitives.Unit.Tests } [Fact] - [ActiveIssue(24368)] public void GetCookies_DifferentPaths_ReturnsConsistentResults() { Cookie c1 = new Cookie("name1", "value", "/base", ".url.com"); diff --git a/external/corefx/src/System.Net.Requests/src/System.Net.Requests.csproj b/external/corefx/src/System.Net.Requests/src/System.Net.Requests.csproj index 39cbb8f131..1052fe84b7 100644 --- a/external/corefx/src/System.Net.Requests/src/System.Net.Requests.csproj +++ b/external/corefx/src/System.Net.Requests/src/System.Net.Requests.csproj @@ -140,4 +140,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Net.Requests/src/System/Net/FtpControlStream.cs b/external/corefx/src/System.Net.Requests/src/System/Net/FtpControlStream.cs index ee5cd00b5a..d5b88e354b 100644 --- a/external/corefx/src/System.Net.Requests/src/System/Net/FtpControlStream.cs +++ b/external/corefx/src/System.Net.Requests/src/System/Net/FtpControlStream.cs @@ -11,13 +11,6 @@ using System.Text; namespace System.Net { - internal enum FtpPrimitive - { - Upload = 0, - Download = 1, - CommandOnly = 2 - }; - internal enum FtpLoginState : byte { NotLoggedIn, @@ -381,7 +374,7 @@ namespace System.Net // OR set us up for SSL/TLS, after this we'll be writing securely else if (status == FtpStatusCode.ServerWantsSecureSession) { - // If NetworkStream is a TlsStream, then this must be in the async callback + // If NetworkStream is a TlsStream, then this must be in the async callback // from completing the SSL handshake. // So just let the pipeline continue. if (!(NetworkStream is TlsStream)) @@ -743,14 +736,6 @@ namespace System.Net return result; } - // - // A door into protected CloseSocket() method - // - internal void Quit() - { - CloseSocket(); - } - private enum GetPathOption { Normal, diff --git a/external/corefx/src/System.Net.Requests/src/System/Net/FtpWebRequest.cs b/external/corefx/src/System.Net.Requests/src/System/Net/FtpWebRequest.cs index d1526d7f58..0bdbdf7328 100644 --- a/external/corefx/src/System.Net.Requests/src/System/Net/FtpWebRequest.cs +++ b/external/corefx/src/System.Net.Requests/src/System/Net/FtpWebRequest.cs @@ -87,11 +87,6 @@ namespace System.Net get { return (Flags & FtpMethodFlags.IsDownload) != 0; } } - internal bool HasHttpCommand - { - get { return (Flags & FtpMethodFlags.HasHttpCommand) != 0; } - } - /// /// True if we should attempt to get a response uri /// out of a server response @@ -241,15 +236,6 @@ namespace System.Net } } - // Used by FtpControlStream - internal static NetworkCredential DefaultNetworkCredential - { - get - { - return s_defaultFtpNetworkCredential; - } - } - public static new RequestCachePolicy DefaultCachePolicy { get @@ -1503,9 +1489,9 @@ namespace System.Net if (stage >= RequestStage.WriteReady) { // If writeResult == null and this is an upload request, it means - // that the user has called GetResponse() without calling - // GetRequestStream() first. So they are not interested in a - // stream. Therefore we close the stream so that the + // that the user has called GetResponse() without calling + // GetRequestStream() first. So they are not interested in a + // stream. Therefore we close the stream so that the // request/pipeline can continue if (_methodInfo.IsUpload && !_getRequestStreamStarted) { @@ -1807,28 +1793,6 @@ namespace System.Net return; } - /// - /// Returns username string - /// - internal string GetUserString() - { - string name = null; - if (this.Credentials != null) - { - NetworkCredential networkCreds = this.Credentials.GetCredential(_uri, "basic"); - if (networkCreds != null) - { - name = networkCreds.UserName; - string domain = networkCreds.Domain; - if (!string.IsNullOrEmpty(domain)) - { - name = domain + "\\" + name; - } - } - } - return name == null ? null : (String.Compare(name, "anonymous", StringComparison.InvariantCultureIgnoreCase) == 0 ? null : name); - } - internal void DataStreamClosed(CloseExState closeState) { if ((closeState & CloseExState.Abort) == 0) diff --git a/external/corefx/src/System.Net.Requests/src/System/Net/FtpWebResponse.cs b/external/corefx/src/System.Net.Requests/src/System/Net/FtpWebResponse.cs index 53bd353234..c24183aa66 100644 --- a/external/corefx/src/System.Net.Requests/src/System/Net/FtpWebResponse.cs +++ b/external/corefx/src/System.Net.Requests/src/System/Net/FtpWebResponse.cs @@ -98,11 +98,6 @@ namespace System.Net } } - internal void SetContentLength(long value) - { - _contentLength = value; - } - public override WebHeaderCollection Headers { get diff --git a/external/corefx/src/System.Net.Requests/src/System/Net/HttpWebRequest.cs b/external/corefx/src/System.Net.Requests/src/System/Net/HttpWebRequest.cs index 043108c264..1d10fe8d35 100644 --- a/external/corefx/src/System.Net.Requests/src/System/Net/HttpWebRequest.cs +++ b/external/corefx/src/System.Net.Requests/src/System/Net/HttpWebRequest.cs @@ -253,7 +253,7 @@ namespace System.Net } if (value < 0) { - throw new ArgumentOutOfRangeException(nameof(value), SR.net_io_timeout_use_ge_zero); + throw new ArgumentOutOfRangeException(nameof(value), SR.net_clsmall); } SetSpecialHeaders(HttpKnownHeaderNames.ContentLength, value.ToString()); } diff --git a/external/corefx/src/System.Net.Requests/src/System/Net/ProtocolViolationException.cs b/external/corefx/src/System.Net.Requests/src/System/Net/ProtocolViolationException.cs index 4f3b7a6a0c..a682188d47 100644 --- a/external/corefx/src/System.Net.Requests/src/System/Net/ProtocolViolationException.cs +++ b/external/corefx/src/System.Net.Requests/src/System/Net/ProtocolViolationException.cs @@ -13,6 +13,9 @@ namespace System.Net /// /// [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class ProtocolViolationException : InvalidOperationException, ISerializable { /// @@ -37,7 +40,6 @@ namespace System.Net protected ProtocolViolationException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { - throw new PlatformNotSupportedException(); } void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) diff --git a/external/corefx/src/System.Net.Requests/src/System/Net/TimerThread.cs b/external/corefx/src/System.Net.Requests/src/System/Net/TimerThread.cs index 91f6532330..3ae6dc3a4c 100644 --- a/external/corefx/src/System.Net.Requests/src/System/Net/TimerThread.cs +++ b/external/corefx/src/System.Net.Requests/src/System/Net/TimerThread.cs @@ -31,11 +31,6 @@ namespace System.Net /// internal int Duration => _durationMilliseconds; - /// - /// Creates and returns a handle to a new polled timer. - /// - internal Timer CreateTimer() => CreateTimer(null, null); - /// /// Creates and returns a handle to a new timer with attached context. /// @@ -56,11 +51,6 @@ namespace System.Net _startTimeMilliseconds = Environment.TickCount; } - /// - /// The duration in milliseconds of timer. - /// - internal int Duration => _durationMilliseconds; - /// /// The time (relative to Environment.TickCount) when the timer started. /// @@ -71,31 +61,6 @@ namespace System.Net /// internal int Expiration => unchecked(_startTimeMilliseconds + _durationMilliseconds); - /// - /// The amount of time left on the timer. 0 means it has fired. 1 means it has expired but - /// not yet fired. -1 means infinite. Int32.MaxValue is the ceiling - the actual value could be longer. - /// - internal int TimeRemaining - { - get - { - if (HasExpired) - { - return 0; - } - - if (Duration == Timeout.Infinite) - { - return Timeout.Infinite; - } - - int now = Environment.TickCount; - int remaining = IsTickBetween(StartTime, Expiration, now) ? - (int)(Math.Min((uint)unchecked(Expiration - now), (uint)Int32.MaxValue)) : 0; - return remaining < 2 ? remaining + 1 : remaining; - } - } - /// /// Cancels the timer. Returns true if the timer hasn't and won't fire; false if it has or will. /// @@ -610,7 +575,7 @@ namespace System.Net if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"Awoke, cause {(waitResult == WaitHandle.WaitTimeout ? "Timeout" : "Prod")}"); - // If we timed out with nothing to do, shut down. + // If we timed out with nothing to do, shut down. if (waitResult == WaitHandle.WaitTimeout && !haveNextTick) { Interlocked.CompareExchange(ref s_threadState, (int)TimerThreadState.Idle, (int)TimerThreadState.Running); diff --git a/external/corefx/src/System.Net.Requests/src/System/Net/WebException.cs b/external/corefx/src/System.Net.Requests/src/System/Net/WebException.cs index 7496fb05b1..1b9865776e 100644 --- a/external/corefx/src/System.Net.Requests/src/System/Net/WebException.cs +++ b/external/corefx/src/System.Net.Requests/src/System/Net/WebException.cs @@ -10,6 +10,9 @@ using System.Threading.Tasks; namespace System.Net { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public partial class WebException : InvalidOperationException, ISerializable { private const WebExceptionStatus DefaultStatus = WebExceptionStatus.UnknownError; @@ -51,9 +54,9 @@ namespace System.Net } } - protected WebException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) + protected WebException(SerializationInfo serializationInfo, StreamingContext streamingContext) + : base(serializationInfo, streamingContext) { - throw new PlatformNotSupportedException(); } public WebExceptionStatus Status diff --git a/external/corefx/src/System.Net.Security/System.Net.Security.sln b/external/corefx/src/System.Net.Security/System.Net.Security.sln index 60dff9c3d9..6834a7a2bd 100644 --- a/external/corefx/src/System.Net.Security/System.Net.Security.sln +++ b/external/corefx/src/System.Net.Security/System.Net.Security.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26923.0 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Net.Security.Tests", "tests\FunctionalTests\System.Net.Security.Tests.csproj", "{A55A2B9A-830F-4330-A0E7-02A9FB30ABD2}" ProjectSection(ProjectDependencies) = postProject @@ -35,10 +35,10 @@ Global {A55A2B9A-830F-4330-A0E7-02A9FB30ABD2}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU {A55A2B9A-830F-4330-A0E7-02A9FB30ABD2}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU {A55A2B9A-830F-4330-A0E7-02A9FB30ABD2}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU - {0D174EA9-9E61-4519-8D31-7BD2331A1982}.Debug|Any CPU.ActiveCfg = netstandard-Windows_NT-Debug|Any CPU - {0D174EA9-9E61-4519-8D31-7BD2331A1982}.Debug|Any CPU.Build.0 = netstandard-Windows_NT-Debug|Any CPU - {0D174EA9-9E61-4519-8D31-7BD2331A1982}.Release|Any CPU.ActiveCfg = netstandard-Windows_NT-Release|Any CPU - {0D174EA9-9E61-4519-8D31-7BD2331A1982}.Release|Any CPU.Build.0 = netstandard-Windows_NT-Release|Any CPU + {0D174EA9-9E61-4519-8D31-7BD2331A1982}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {0D174EA9-9E61-4519-8D31-7BD2331A1982}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {0D174EA9-9E61-4519-8D31-7BD2331A1982}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {0D174EA9-9E61-4519-8D31-7BD2331A1982}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU {89F37791-6254-4D60-AB96-ACD3CCA0E771}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU {89F37791-6254-4D60-AB96-ACD3CCA0E771}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU {89F37791-6254-4D60-AB96-ACD3CCA0E771}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU @@ -57,7 +57,4 @@ Global {89F37791-6254-4D60-AB96-ACD3CCA0E771} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD} {A7488FC0-9A8F-4EF9-BC3E-C5EBA47E13F8} = {2E666815-2EDB-464B-9DF6-380BF4789AD4} EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72DFF69F-F9E9-4112-91E8-5FC8169B6F1F} - EndGlobalSection EndGlobal diff --git a/external/corefx/src/System.Net.Security/ref/System.Net.Security.cs b/external/corefx/src/System.Net.Security/ref/System.Net.Security.cs index eca6678174..1aad87183a 100644 --- a/external/corefx/src/System.Net.Security/ref/System.Net.Security.cs +++ b/external/corefx/src/System.Net.Security/ref/System.Net.Security.cs @@ -5,6 +5,11 @@ // Changes to this file must follow the http://aka.ms/api-review process. // ------------------------------------------------------------------------------ +using System.Collections.Generic; +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using System.Threading.Tasks; namespace System.Net.Security { @@ -94,6 +99,48 @@ namespace System.Net.Security EncryptAndSign = 2 } public delegate bool RemoteCertificateValidationCallback(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors); + public class SslServerAuthenticationOptions + { + public bool AllowRenegotiation { get { throw null; } set { } } + public X509Certificate ServerCertificate { get { throw null; } set { } } + public bool ClientCertificateRequired { get { throw null; } set { } } + public SslProtocols EnabledSslProtocols { get { throw null; } set { } } + public X509RevocationMode CertificateRevocationCheckMode { get { throw null; } set { } } + public List ApplicationProtocols { get { throw null; } set { } } + public RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get { throw null; } set { } } + public EncryptionPolicy EncryptionPolicy { get { throw null; } set { } } + } + public partial class SslClientAuthenticationOptions + { + public bool AllowRenegotiation { get { throw null; } set { } } + public string TargetHost { get { throw null; } set { } } + public X509CertificateCollection ClientCertificates { get { throw null; } set { } } + public LocalCertificateSelectionCallback LocalCertificateSelectionCallback { get { throw null; } set { } } + public SslProtocols EnabledSslProtocols { get { throw null; } set { } } + public X509RevocationMode CertificateRevocationCheckMode { get { throw null; } set { } } + public List ApplicationProtocols { get { throw null; } set { } } + public RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get { throw null; } set { } } + public EncryptionPolicy EncryptionPolicy { get { throw null; } set { } } + } + public readonly partial struct SslApplicationProtocol : IEquatable + { + private readonly object _dummy; + + public static readonly SslApplicationProtocol Http2; + public static readonly SslApplicationProtocol Http11; + + public SslApplicationProtocol(byte[] protocol) { throw null; } + public SslApplicationProtocol(string protocol) { throw null; } + + public ReadOnlyMemory Protocol { get { throw null; } } + + public bool Equals(SslApplicationProtocol other) { throw null; } + public override bool Equals(object obj) { throw null; } + public override int GetHashCode() { throw null; } + public override string ToString() { throw null; } + public static bool operator ==(SslApplicationProtocol left, SslApplicationProtocol right) { throw null; } + public static bool operator !=(SslApplicationProtocol left, SslApplicationProtocol right) { throw null; } + } public partial class SslStream : AuthenticatedStream { public SslStream(System.IO.Stream innerStream) : base(innerStream, false) { } @@ -101,6 +148,7 @@ namespace System.Net.Security public SslStream(System.IO.Stream innerStream, bool leaveInnerStreamOpen, System.Net.Security.RemoteCertificateValidationCallback userCertificateValidationCallback) : base(innerStream, leaveInnerStreamOpen) { } public SslStream(System.IO.Stream innerStream, bool leaveInnerStreamOpen, System.Net.Security.RemoteCertificateValidationCallback userCertificateValidationCallback, System.Net.Security.LocalCertificateSelectionCallback userCertificateSelectionCallback) : base(innerStream, leaveInnerStreamOpen) { } public SslStream(System.IO.Stream innerStream, bool leaveInnerStreamOpen, System.Net.Security.RemoteCertificateValidationCallback userCertificateValidationCallback, System.Net.Security.LocalCertificateSelectionCallback userCertificateSelectionCallback, System.Net.Security.EncryptionPolicy encryptionPolicy) : base(innerStream, leaveInnerStreamOpen) { } + public SslApplicationProtocol NegotiatedApplicationProtocol { get { throw null; } } public override bool CanRead { get { throw null; } } public override bool CanSeek { get { throw null; } } public override bool CanTimeout { get { throw null; } } @@ -134,9 +182,11 @@ namespace System.Net.Security public virtual System.Threading.Tasks.Task AuthenticateAsClientAsync(string targetHost) { throw null; } public virtual System.Threading.Tasks.Task AuthenticateAsClientAsync(string targetHost, System.Security.Cryptography.X509Certificates.X509CertificateCollection clientCertificates, System.Security.Authentication.SslProtocols enabledSslProtocols, bool checkCertificateRevocation) { throw null; } public virtual System.Threading.Tasks.Task AuthenticateAsClientAsync(string targetHost, System.Security.Cryptography.X509Certificates.X509CertificateCollection clientCertificates, bool checkCertificateRevocation) { throw null; } + public Task AuthenticateAsClientAsync(SslClientAuthenticationOptions sslClientAuthenticationOptions, CancellationToken cancellationToken) { throw null; } public virtual System.Threading.Tasks.Task AuthenticateAsServerAsync(System.Security.Cryptography.X509Certificates.X509Certificate serverCertificate) { throw null; } public virtual System.Threading.Tasks.Task AuthenticateAsServerAsync(System.Security.Cryptography.X509Certificates.X509Certificate serverCertificate, bool clientCertificateRequired, System.Security.Authentication.SslProtocols enabledSslProtocols, bool checkCertificateRevocation) { throw null; } public virtual System.Threading.Tasks.Task AuthenticateAsServerAsync(System.Security.Cryptography.X509Certificates.X509Certificate serverCertificate, bool clientCertificateRequired, bool checkCertificateRevocation) { throw null; } + public Task AuthenticateAsServerAsync(SslServerAuthenticationOptions sslClientAuthenticationOptions, CancellationToken cancellationToken) { throw null; } public virtual System.IAsyncResult BeginAuthenticateAsClient(string targetHost, System.AsyncCallback asyncCallback, object asyncState) { throw null; } public virtual System.IAsyncResult BeginAuthenticateAsClient(string targetHost, System.Security.Cryptography.X509Certificates.X509CertificateCollection clientCertificates, System.Security.Authentication.SslProtocols enabledSslProtocols, bool checkCertificateRevocation, System.AsyncCallback asyncCallback, object asyncState) { throw null; } public virtual System.IAsyncResult BeginAuthenticateAsClient(string targetHost, System.Security.Cryptography.X509Certificates.X509CertificateCollection clientCertificates, bool checkCertificateRevocation, System.AsyncCallback asyncCallback, object asyncState) { throw null; } diff --git a/external/corefx/src/System.Net.Security/ref/System.Net.Security.csproj b/external/corefx/src/System.Net.Security/ref/System.Net.Security.csproj index 7de67fd715..b67b18765f 100644 --- a/external/corefx/src/System.Net.Security/ref/System.Net.Security.csproj +++ b/external/corefx/src/System.Net.Security/ref/System.Net.Security.csproj @@ -13,6 +13,7 @@ + @@ -21,5 +22,8 @@ + + + \ No newline at end of file diff --git a/external/corefx/src/System.Net.Security/src/Resources/Strings.resx b/external/corefx/src/System.Net.Security/src/Resources/Strings.resx index fb3a593245..fe5ef27660 100644 --- a/external/corefx/src/System.Net.Security/src/Resources/Strings.resx +++ b/external/corefx/src/System.Net.Security/src/Resources/Strings.resx @@ -1,5 +1,64 @@  + @@ -70,9 +129,6 @@ This method is not implemented by this class. - - This operation cannot be performed on a completed asynchronous result object. - Unable to read data from the transport connection: {0}. @@ -115,6 +171,9 @@ The handshake failed due to an unexpected packet format. + + The remote party requested renegotiation when AllowRenegotiation was set to false. + The remote certificate is invalid according to the validation procedure. @@ -295,9 +354,6 @@ Encrypt failed with OpenSSL error - {0}. - - Failed to get SSL method '{0}'. Ensure the OpenSSL method exists on the current system. - SSL certificate private key check failed with OpenSSL error - {0}. @@ -355,10 +411,25 @@ Target name should be non empty if default credentials are passed. - + The requested combination of SslProtocols ({0}) is not valid for this platform because it skips intermediate versions. The '{0}' encryption policy is not supported on this platform. + + ALPN configuration failed on this platform. + + + No common application protocol exists between the client and the server. Application negotiation failed. + + + The application protocol list is invalid. + + + The application protocol value is invalid. + + + The '{0}' option was already set in the SslStream constructor. + diff --git a/external/corefx/src/System.Net.Security/src/System.Net.Security.csproj b/external/corefx/src/System.Net.Security/src/System.Net.Security.csproj index 03f8063a06..ef39db997d 100644 --- a/external/corefx/src/System.Net.Security/src/System.Net.Security.csproj +++ b/external/corefx/src/System.Net.Security/src/System.Net.Security.csproj @@ -22,6 +22,10 @@ + + + + @@ -161,6 +165,12 @@ Common\Interop\Windows\SChannel\SecPkgContext_ConnectionInfo.cs + + Common\Interop\Windows\SChannel\Interop.SecPkgContext_ApplicationProtocol.cs + + + Common\Interop\Windows\SChannel\Interop.Sec_Application_Protocols.cs + Common\Interop\Windows\SChannel\UnmanagedCertificateContext.cs @@ -396,6 +406,7 @@ + @@ -416,4 +427,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs b/external/corefx/src/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs index bb6d29c6f8..2007763ab6 100644 --- a/external/corefx/src/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs +++ b/external/corefx/src/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs @@ -22,7 +22,7 @@ namespace System.Net public SafeSslHandle SslContext => _sslContext; - public SafeDeleteSslContext(SafeFreeSslCredentials credential, bool isServer) + public SafeDeleteSslContext(SafeFreeSslCredentials credential, SslAuthenticationOptions sslAuthenticationOptions) : base(credential) { Debug.Assert((null != credential) && !credential.IsInvalid, "Invalid credential used in SafeDeleteSslContext"); @@ -35,7 +35,7 @@ namespace System.Net _writeCallback = WriteToConnection; } - _sslContext = CreateSslContext(credential, isServer); + _sslContext = CreateSslContext(credential, sslAuthenticationOptions.IsServer); int osStatus = Interop.AppleCrypto.SslSetIoCallbacks( _sslContext, diff --git a/external/corefx/src/System.Net.Security/src/System/Net/Security/SecureChannel.cs b/external/corefx/src/System.Net.Security/src/System/Net/Security/SecureChannel.cs index 6f4cadecd2..b444eea312 100644 --- a/external/corefx/src/System.Net.Security/src/System/Net/Security/SecureChannel.cs +++ b/external/corefx/src/System.Net.Security/src/System/Net/Security/SecureChannel.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Runtime.ExceptionServices; +using System.Runtime.InteropServices; using System.Security; using System.Security.Authentication; using System.Security.Authentication.ExtendedProtection; @@ -14,7 +15,7 @@ using System.Security.Cryptography.X509Certificates; namespace System.Net.Security { - // SecureChannel - a wrapper on SSPI based functionality. + // SecureChannel - a wrapper on SSPI based functionality. // Provides an additional abstraction layer over SSPI for SslStream. internal class SecureChannel { @@ -23,68 +24,45 @@ namespace System.Net.Security private SafeFreeCredentials _credentialsHandle; private SafeDeleteContext _securityContext; - private readonly string _destination; - private readonly string _hostName; - private readonly bool _serverMode; - private readonly bool _remoteCertRequired; - private readonly SslProtocols _sslProtocols; - private readonly EncryptionPolicy _encryptionPolicy; private SslConnectionInfo _connectionInfo; - - private X509Certificate _serverCertificate; private X509Certificate _selectedClientCertificate; private bool _isRemoteCertificateAvailable; - private X509CertificateCollection _clientCertificates; - private LocalCertSelectionCallback _certSelectionDelegate; - // These are the MAX encrypt buffer output sizes, not the actual sizes. private int _headerSize = 5; //ATTN must be set to at least 5 by default private int _trailerSize = 16; private int _maxDataSize = 16354; - private bool _checkCertRevocation; - private bool _checkCertName; - private bool _refreshCredentialNeeded; + private SslAuthenticationOptions _sslAuthenticationOptions; + private SslApplicationProtocol _negotiatedApplicationProtocol; + private readonly Oid _serverAuthOid = new Oid("1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.1"); private readonly Oid _clientAuthOid = new Oid("1.3.6.1.5.5.7.3.2", "1.3.6.1.5.5.7.3.2"); - internal SecureChannel(string hostname, bool serverMode, SslProtocols sslProtocols, X509Certificate serverCertificate, X509CertificateCollection clientCertificates, bool remoteCertRequired, bool checkCertName, - bool checkCertRevocationStatus, EncryptionPolicy encryptionPolicy, LocalCertSelectionCallback certSelectionDelegate) + internal SecureChannel(SslAuthenticationOptions sslAuthenticationOptions) { if (NetEventSource.IsEnabled) { - NetEventSource.Enter(this, hostname, clientCertificates); - NetEventSource.Log.SecureChannelCtor(this, hostname, clientCertificates, encryptionPolicy); + NetEventSource.Enter(this, sslAuthenticationOptions.TargetHost, sslAuthenticationOptions.ClientCertificates); + NetEventSource.Log.SecureChannelCtor(this, sslAuthenticationOptions.TargetHost, sslAuthenticationOptions.ClientCertificates, sslAuthenticationOptions.EncryptionPolicy); } SslStreamPal.VerifyPackageInfo(); - _destination = hostname; - - if (hostname == null) + if (sslAuthenticationOptions.TargetHost == null) { - NetEventSource.Fail(this, "hostname == null"); + NetEventSource.Fail(this, "sslAuthenticationOptions.TargetHost == null"); } - _hostName = hostname; - _serverMode = serverMode; - _sslProtocols = sslProtocols; - - _serverCertificate = serverCertificate; - _clientCertificates = clientCertificates; - _remoteCertRequired = remoteCertRequired; _securityContext = null; - _checkCertRevocation = checkCertRevocationStatus; - _checkCertName = checkCertName; - _certSelectionDelegate = certSelectionDelegate; _refreshCredentialNeeded = true; - _encryptionPolicy = encryptionPolicy; - - if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + _sslAuthenticationOptions = sslAuthenticationOptions; + + if (NetEventSource.IsEnabled) + NetEventSource.Exit(this); } // @@ -100,7 +78,7 @@ namespace System.Net.Security { get { - return _serverCertificate; + return _sslAuthenticationOptions.ServerCertificate; } } @@ -122,7 +100,8 @@ namespace System.Net.Security internal ChannelBinding GetChannelBinding(ChannelBindingKind kind) { - if (NetEventSource.IsEnabled) NetEventSource.Enter(this, kind); + if (NetEventSource.IsEnabled) + NetEventSource.Enter(this, kind); ChannelBinding result = null; if (_securityContext != null) @@ -130,23 +109,16 @@ namespace System.Net.Security result = SslStreamPal.QueryContextChannelBinding(_securityContext, kind); } - if (NetEventSource.IsEnabled) NetEventSource.Exit(this, result); + if (NetEventSource.IsEnabled) + NetEventSource.Exit(this, result); return result; } - internal bool CheckCertRevocationStatus + internal X509RevocationMode CheckCertRevocationStatus { get { - return _checkCertRevocation; - } - } - - internal int HeaderSize - { - get - { - return _headerSize; + return _sslAuthenticationOptions.CertificateRevocationCheckMode; } } @@ -178,7 +150,7 @@ namespace System.Net.Security { get { - return _serverMode; + return _sslAuthenticationOptions.IsServer; } } @@ -186,7 +158,15 @@ namespace System.Net.Security { get { - return _remoteCertRequired; + return _sslAuthenticationOptions.RemoteCertRequired; + } + } + + internal SslApplicationProtocol NegotiatedApplicationProtocol + { + get + { + return _negotiatedApplicationProtocol; } } @@ -206,6 +186,8 @@ namespace System.Net.Security { _credentialsHandle.Dispose(); } + + GC.SuppressFinalize(this); } // @@ -219,7 +201,8 @@ namespace System.Net.Security return null; } - if (NetEventSource.IsEnabled) NetEventSource.Log.LocatingPrivateKey(certificate, this); + if (NetEventSource.IsEnabled) + NetEventSource.Log.LocatingPrivateKey(certificate, this); try { @@ -234,7 +217,8 @@ namespace System.Net.Security { if (certEx.HasPrivateKey) { - if (NetEventSource.IsEnabled) NetEventSource.Log.CertIsType2(this); + if (NetEventSource.IsEnabled) + NetEventSource.Log.CertIsType2(this); return certEx; } @@ -249,24 +233,26 @@ namespace System.Net.Security // ELSE Try the MY user and machine stores for private key check. // For server side mode MY machine store takes priority. - X509Store store = CertificateValidationPal.EnsureStoreOpened(_serverMode); + X509Store store = CertificateValidationPal.EnsureStoreOpened(_sslAuthenticationOptions.IsServer); if (store != null) { collectionEx = store.Certificates.Find(X509FindType.FindByThumbprint, certHash, false); if (collectionEx.Count > 0 && collectionEx[0].HasPrivateKey) { - if (NetEventSource.IsEnabled) NetEventSource.Log.FoundCertInStore(_serverMode, this); + if (NetEventSource.IsEnabled) + NetEventSource.Log.FoundCertInStore(_sslAuthenticationOptions.IsServer, this); return collectionEx[0]; } } - store = CertificateValidationPal.EnsureStoreOpened(!_serverMode); + store = CertificateValidationPal.EnsureStoreOpened(!_sslAuthenticationOptions.IsServer); if (store != null) { collectionEx = store.Certificates.Find(X509FindType.FindByThumbprint, certHash, false); if (collectionEx.Count > 0 && collectionEx[0].HasPrivateKey) { - if (NetEventSource.IsEnabled) NetEventSource.Log.FoundCertInStore(_serverMode, this); + if (NetEventSource.IsEnabled) + NetEventSource.Log.FoundCertInStore(_sslAuthenticationOptions.IsServer, this); return collectionEx[0]; } } @@ -275,7 +261,8 @@ namespace System.Net.Security { } - if (NetEventSource.IsEnabled) NetEventSource.Log.NotFoundCertInStore(this); + if (NetEventSource.IsEnabled) + NetEventSource.Log.NotFoundCertInStore(this); return null; } @@ -356,7 +343,8 @@ namespace System.Net.Security private bool AcquireClientCredentials(ref byte[] thumbPrint) { - if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + if (NetEventSource.IsEnabled) + NetEventSource.Enter(this); // Acquire possible Client Certificate information and set it on the handle. X509Certificate clientCertificate = null; // This is a candidate that can come from the user callback or be guessed when targeting a session restart. @@ -365,22 +353,23 @@ namespace System.Net.Security bool sessionRestartAttempt = false; // If true and no cached creds we will use anonymous creds. - if (_certSelectionDelegate != null) + if (_sslAuthenticationOptions.CertSelectionDelegate != null) { issuers = GetRequestCertificateAuthorities(); - if (NetEventSource.IsEnabled) NetEventSource.Info(this, "Calling CertificateSelectionCallback"); + if (NetEventSource.IsEnabled) + NetEventSource.Info(this, "Calling CertificateSelectionCallback"); X509Certificate2 remoteCert = null; try { X509Certificate2Collection dummyCollection; remoteCert = CertificateValidationPal.GetRemoteCertificate(_securityContext, out dummyCollection); - if (_clientCertificates == null) + if (_sslAuthenticationOptions.ClientCertificates == null) { - _clientCertificates = new X509CertificateCollection(); + _sslAuthenticationOptions.ClientCertificates = new X509CertificateCollection(); } - clientCertificate = _certSelectionDelegate(_hostName, _clientCertificates, remoteCert, issuers); + clientCertificate = _sslAuthenticationOptions.CertSelectionDelegate(_sslAuthenticationOptions.TargetHost, _sslAuthenticationOptions.ClientCertificates, remoteCert, issuers); } finally { @@ -399,36 +388,40 @@ namespace System.Net.Security } filteredCerts.Add(clientCertificate); - if (NetEventSource.IsEnabled) NetEventSource.Log.CertificateFromDelegate(this); + if (NetEventSource.IsEnabled) + NetEventSource.Log.CertificateFromDelegate(this); } else { - if (_clientCertificates == null || _clientCertificates.Count == 0) + if (_sslAuthenticationOptions.ClientCertificates == null || _sslAuthenticationOptions.ClientCertificates.Count == 0) { - if (NetEventSource.IsEnabled) NetEventSource.Log.NoDelegateNoClientCert(this); + if (NetEventSource.IsEnabled) + NetEventSource.Log.NoDelegateNoClientCert(this); sessionRestartAttempt = true; } else { - if (NetEventSource.IsEnabled) NetEventSource.Log.NoDelegateButClientCert(this); + if (NetEventSource.IsEnabled) + NetEventSource.Log.NoDelegateButClientCert(this); } } } - else if (_credentialsHandle == null && _clientCertificates != null && _clientCertificates.Count > 0) + else if (_credentialsHandle == null && _sslAuthenticationOptions.ClientCertificates != null && _sslAuthenticationOptions.ClientCertificates.Count > 0) { // This is where we attempt to restart a session by picking the FIRST cert from the collection. // Otherwise it is either server sending a client cert request or the session is renegotiated. - clientCertificate = _clientCertificates[0]; + clientCertificate = _sslAuthenticationOptions.ClientCertificates[0]; sessionRestartAttempt = true; if (clientCertificate != null) { filteredCerts.Add(clientCertificate); } - if (NetEventSource.IsEnabled) NetEventSource.Log.AttemptingRestartUsingCert(clientCertificate, this); + if (NetEventSource.IsEnabled) + NetEventSource.Log.AttemptingRestartUsingCert(clientCertificate, this); } - else if (_clientCertificates != null && _clientCertificates.Count > 0) + else if (_sslAuthenticationOptions.ClientCertificates != null && _sslAuthenticationOptions.ClientCertificates.Count > 0) { // // This should be a server request for the client cert sent over currently anonymous sessions. @@ -447,7 +440,7 @@ namespace System.Net.Security } } - for (int i = 0; i < _clientCertificates.Count; ++i) + for (int i = 0; i < _sslAuthenticationOptions.ClientCertificates.Count; ++i) { // // Make sure we add only if the cert matches one of the issuers. @@ -459,13 +452,14 @@ namespace System.Net.Security X509Chain chain = null; try { - certificateEx = MakeEx(_clientCertificates[i]); + certificateEx = MakeEx(_sslAuthenticationOptions.ClientCertificates[i]); if (certificateEx == null) { continue; } - if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"Root cert: {certificateEx}"); + if (NetEventSource.IsEnabled) + NetEventSource.Info(this, $"Root cert: {certificateEx}"); chain = new X509Chain(); @@ -485,10 +479,12 @@ namespace System.Net.Security found = Array.IndexOf(issuers, issuer) != -1; if (found) { - if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"Matched {issuer}"); + if (NetEventSource.IsEnabled) + NetEventSource.Info(this, $"Matched {issuer}"); break; } - if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"No match: {issuer}"); + if (NetEventSource.IsEnabled) + NetEventSource.Info(this, $"No match: {issuer}"); } } @@ -504,16 +500,17 @@ namespace System.Net.Security chain.Dispose(); } - if (certificateEx != null && (object)certificateEx != (object)_clientCertificates[i]) + if (certificateEx != null && (object)certificateEx != (object)_sslAuthenticationOptions.ClientCertificates[i]) { certificateEx.Dispose(); } } } - if (NetEventSource.IsEnabled) NetEventSource.Log.SelectedCert(_clientCertificates[i], this); + if (NetEventSource.IsEnabled) + NetEventSource.Log.SelectedCert(_sslAuthenticationOptions.ClientCertificates[i], this); - filteredCerts.Add(_clientCertificates[i]); + filteredCerts.Add(_sslAuthenticationOptions.ClientCertificates[i]); } } @@ -555,7 +552,8 @@ namespace System.Net.Security NetEventSource.Fail(this, "'selectedCert' does not match 'clientCertificate'."); } - if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"Selected cert = {selectedCert}"); + if (NetEventSource.IsEnabled) + NetEventSource.Info(this, $"Selected cert = {selectedCert}"); try { @@ -564,7 +562,7 @@ namespace System.Net.Security // SECURITY: selectedCert ref if not null is a safe object that does not depend on possible **user** inherited X509Certificate type. // byte[] guessedThumbPrint = selectedCert == null ? null : selectedCert.GetCertHash(); - SafeFreeCredentials cachedCredentialHandle = SslSessionsCache.TryCachedCredential(guessedThumbPrint, _sslProtocols, _serverMode, _encryptionPolicy); + SafeFreeCredentials cachedCredentialHandle = SslSessionsCache.TryCachedCredential(guessedThumbPrint, _sslAuthenticationOptions.EnabledSslProtocols, _sslAuthenticationOptions.IsServer, _sslAuthenticationOptions.EncryptionPolicy); // We can probably do some optimization here. If the selectedCert is returned by the delegate // we can always go ahead and use the certificate to create our credential @@ -574,7 +572,8 @@ namespace System.Net.Security selectedCert != null && SslStreamPal.StartMutualAuthAsAnonymous) { - if (NetEventSource.IsEnabled) NetEventSource.Info(this, "Reset to anonymous session."); + if (NetEventSource.IsEnabled) + NetEventSource.Info(this, "Reset to anonymous session."); // IIS does not renegotiate a restarted session if client cert is needed. // So we don't want to reuse **anonymous** cached credential for a new SSL connection if the client has passed some certificate. @@ -592,7 +591,8 @@ namespace System.Net.Security if (cachedCredentialHandle != null) { - if (NetEventSource.IsEnabled) NetEventSource.Log.UsingCachedCredential(this); + if (NetEventSource.IsEnabled) + NetEventSource.Log.UsingCachedCredential(this); _credentialsHandle = cachedCredentialHandle; _selectedClientCertificate = clientCertificate; @@ -600,7 +600,7 @@ namespace System.Net.Security } else { - _credentialsHandle = SslStreamPal.AcquireCredentialsHandle(selectedCert, _sslProtocols, _encryptionPolicy, _serverMode); + _credentialsHandle = SslStreamPal.AcquireCredentialsHandle(selectedCert, _sslAuthenticationOptions.EnabledSslProtocols, _sslAuthenticationOptions.EncryptionPolicy, _sslAuthenticationOptions.IsServer); thumbPrint = guessedThumbPrint; // Delay until here in case something above threw. _selectedClientCertificate = clientCertificate; @@ -615,7 +615,8 @@ namespace System.Net.Security } } - if (NetEventSource.IsEnabled) NetEventSource.Exit(this, cachedCred, _credentialsHandle); + if (NetEventSource.IsEnabled) + NetEventSource.Exit(this, cachedCred, _credentialsHandle); return cachedCred; } @@ -625,21 +626,23 @@ namespace System.Net.Security // private bool AcquireServerCredentials(ref byte[] thumbPrint) { - if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + if (NetEventSource.IsEnabled) + NetEventSource.Enter(this); X509Certificate localCertificate = null; bool cachedCred = false; - if (_certSelectionDelegate != null) + if (_sslAuthenticationOptions.CertSelectionDelegate != null) { X509CertificateCollection tempCollection = new X509CertificateCollection(); - tempCollection.Add(_serverCertificate); - localCertificate = _certSelectionDelegate(string.Empty, tempCollection, null, Array.Empty()); - if (NetEventSource.IsEnabled) NetEventSource.Info(this, "Use delegate selected Cert"); + tempCollection.Add(_sslAuthenticationOptions.ServerCertificate); + localCertificate = _sslAuthenticationOptions.CertSelectionDelegate(string.Empty, tempCollection, null, Array.Empty()); + if (NetEventSource.IsEnabled) + NetEventSource.Info(this, "Use delegate selected Cert"); } else { - localCertificate = _serverCertificate; + localCertificate = _sslAuthenticationOptions.ServerCertificate; } if (localCertificate == null) @@ -668,19 +671,19 @@ namespace System.Net.Security byte[] guessedThumbPrint = selectedCert.GetCertHash(); try { - SafeFreeCredentials cachedCredentialHandle = SslSessionsCache.TryCachedCredential(guessedThumbPrint, _sslProtocols, _serverMode, _encryptionPolicy); + SafeFreeCredentials cachedCredentialHandle = SslSessionsCache.TryCachedCredential(guessedThumbPrint, _sslAuthenticationOptions.EnabledSslProtocols, _sslAuthenticationOptions.IsServer, _sslAuthenticationOptions.EncryptionPolicy); if (cachedCredentialHandle != null) { _credentialsHandle = cachedCredentialHandle; - _serverCertificate = localCertificate; + _sslAuthenticationOptions.ServerCertificate = localCertificate; cachedCred = true; } else { - _credentialsHandle = SslStreamPal.AcquireCredentialsHandle(selectedCert, _sslProtocols, _encryptionPolicy, _serverMode); + _credentialsHandle = SslStreamPal.AcquireCredentialsHandle(selectedCert, _sslAuthenticationOptions.EnabledSslProtocols, _sslAuthenticationOptions.EncryptionPolicy, _sslAuthenticationOptions.IsServer); thumbPrint = guessedThumbPrint; - _serverCertificate = localCertificate; + _sslAuthenticationOptions.ServerCertificate = localCertificate; } } finally @@ -692,28 +695,32 @@ namespace System.Net.Security } } - if (NetEventSource.IsEnabled) NetEventSource.Exit(this, cachedCred, _credentialsHandle); + if (NetEventSource.IsEnabled) + NetEventSource.Exit(this, cachedCred, _credentialsHandle); return cachedCred; } // internal ProtocolToken NextMessage(byte[] incoming, int offset, int count) { - if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + if (NetEventSource.IsEnabled) + NetEventSource.Enter(this); byte[] nextmsg = null; SecurityStatusPal status = GenerateToken(incoming, offset, count, ref nextmsg); - if (!_serverMode && status.ErrorCode == SecurityStatusPalErrorCode.CredentialsNeeded) + if (!_sslAuthenticationOptions.IsServer && status.ErrorCode == SecurityStatusPalErrorCode.CredentialsNeeded) { - if (NetEventSource.IsEnabled) NetEventSource.Info(this, "NextMessage() returned SecurityStatusPal.CredentialsNeeded"); + if (NetEventSource.IsEnabled) + NetEventSource.Info(this, "NextMessage() returned SecurityStatusPal.CredentialsNeeded"); SetRefreshCredentialNeeded(); status = GenerateToken(incoming, offset, count, ref nextmsg); } ProtocolToken token = new ProtocolToken(nextmsg, status); - if (NetEventSource.IsEnabled) NetEventSource.Exit(this, token); + if (NetEventSource.IsEnabled) + NetEventSource.Exit(this, token); return token; } @@ -756,13 +763,10 @@ namespace System.Net.Security if (input != null) { incomingSecurity = new SecurityBuffer(input, offset, count, SecurityBufferType.SECBUFFER_TOKEN); - incomingSecurityBuffers = new SecurityBuffer[] - { - incomingSecurity, - new SecurityBuffer(null, 0, 0, SecurityBufferType.SECBUFFER_EMPTY) - }; } + incomingSecurityBuffers = SslStreamPal.GetIncomingSecurityBuffers(_sslAuthenticationOptions, ref incomingSecurity); + SecurityBuffer outgoingSecurity = new SecurityBuffer(null, SecurityBufferType.SECBUFFER_TOKEN); SecurityStatusPal status = default(SecurityStatusPal); @@ -781,39 +785,41 @@ namespace System.Net.Security thumbPrint = null; if (_refreshCredentialNeeded) { - cachedCreds = _serverMode + cachedCreds = _sslAuthenticationOptions.IsServer ? AcquireServerCredentials(ref thumbPrint) : AcquireClientCredentials(ref thumbPrint); } - if (_serverMode) + if (_sslAuthenticationOptions.IsServer) { status = SslStreamPal.AcceptSecurityContext( ref _credentialsHandle, ref _securityContext, - incomingSecurity, + incomingSecurityBuffers, outgoingSecurity, - _remoteCertRequired); + _sslAuthenticationOptions); } else { - if (incomingSecurity == null) + if (incomingSecurityBuffers == null) { status = SslStreamPal.InitializeSecurityContext( ref _credentialsHandle, ref _securityContext, - _destination, + _sslAuthenticationOptions.TargetHost, incomingSecurity, - outgoingSecurity); + outgoingSecurity, + _sslAuthenticationOptions); } else { status = SslStreamPal.InitializeSecurityContext( _credentialsHandle, ref _securityContext, - _destination, + _sslAuthenticationOptions.TargetHost, incomingSecurityBuffers, - outgoingSecurity); + outgoingSecurity, + _sslAuthenticationOptions); } } } while (cachedCreds && _credentialsHandle == null); @@ -839,13 +845,16 @@ namespace System.Net.Security // if (!cachedCreds && _securityContext != null && !_securityContext.IsInvalid && _credentialsHandle != null && !_credentialsHandle.IsInvalid) { - SslSessionsCache.CacheCredential(_credentialsHandle, thumbPrint, _sslProtocols, _serverMode, _encryptionPolicy); + SslSessionsCache.CacheCredential(_credentialsHandle, thumbPrint, _sslAuthenticationOptions.EnabledSslProtocols, _sslAuthenticationOptions.IsServer, _sslAuthenticationOptions.EncryptionPolicy); } } } output = outgoingSecurity.token; - + + byte[] alpnResult = SslStreamPal.GetNegotiatedApplicationProtocol(_securityContext); + _negotiatedApplicationProtocol = alpnResult == null ? default : new SslApplicationProtocol(alpnResult, false); + return status; } @@ -858,7 +867,8 @@ namespace System.Net.Security --*/ internal void ProcessHandshakeSuccess() { - if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + if (NetEventSource.IsEnabled) + NetEventSource.Enter(this); StreamSizes streamSizes; SslStreamPal.QueryContextStreamSizes(_securityContext, out streamSizes); @@ -882,7 +892,8 @@ namespace System.Net.Security SslStreamPal.QueryContextConnectionInfo(_securityContext, out _connectionInfo); - if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + if (NetEventSource.IsEnabled) + NetEventSource.Exit(this); } /*++ @@ -906,7 +917,7 @@ namespace System.Net.Security } byte[] writeBuffer = output; - + SecurityStatusPal secStatus = SslStreamPal.EncryptMessage( _securityContext, buffer, @@ -917,12 +928,14 @@ namespace System.Net.Security if (secStatus.ErrorCode != SecurityStatusPalErrorCode.OK) { - if (NetEventSource.IsEnabled) NetEventSource.Exit(this, $"ERROR {secStatus}"); + if (NetEventSource.IsEnabled) + NetEventSource.Exit(this, $"ERROR {secStatus}"); } else { output = writeBuffer; - if (NetEventSource.IsEnabled) NetEventSource.Exit(this, $"OK data size:{resultSize}"); + if (NetEventSource.IsEnabled) + NetEventSource.Exit(this, $"OK data size:{resultSize}"); } return secStatus; @@ -930,7 +943,8 @@ namespace System.Net.Security internal SecurityStatusPal Decrypt(byte[] payload, ref int offset, ref int count) { - if (NetEventSource.IsEnabled) NetEventSource.Enter(this, payload, offset, count); + if (NetEventSource.IsEnabled) + NetEventSource.Enter(this, payload, offset, count); if (offset < 0 || offset > (payload == null ? 0 : payload.Length)) { @@ -962,7 +976,8 @@ namespace System.Net.Security // internal bool VerifyRemoteCertificate(RemoteCertValidationCallback remoteCertValidationCallback, ref ProtocolToken alertToken) { - if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + if (NetEventSource.IsEnabled) + NetEventSource.Enter(this); SslPolicyErrors sslPolicyErrors = SslPolicyErrors.None; @@ -979,17 +994,18 @@ namespace System.Net.Security if (remoteCertificateEx == null) { - if (NetEventSource.IsEnabled) NetEventSource.Exit(this, "(no remote cert)", !_remoteCertRequired); + if (NetEventSource.IsEnabled) + NetEventSource.Exit(this, "(no remote cert)", !_sslAuthenticationOptions.RemoteCertRequired); sslPolicyErrors |= SslPolicyErrors.RemoteCertificateNotAvailable; } else { chain = new X509Chain(); - chain.ChainPolicy.RevocationMode = _checkCertRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck; + chain.ChainPolicy.RevocationMode = _sslAuthenticationOptions.CertificateRevocationCheckMode; chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; // Authenticate the remote party: (e.g. when operating in server mode, authenticate the client). - chain.ChainPolicy.ApplicationPolicy.Add(_serverMode ? _clientAuthOid : _serverAuthOid); + chain.ChainPolicy.ApplicationPolicy.Add(_sslAuthenticationOptions.IsServer ? _clientAuthOid : _serverAuthOid); if (remoteCertificateStore != null) { @@ -1000,18 +1016,18 @@ namespace System.Net.Security _securityContext, chain, remoteCertificateEx, - _checkCertName, - _serverMode, - _hostName); + _sslAuthenticationOptions.CheckCertName, + _sslAuthenticationOptions.IsServer, + _sslAuthenticationOptions.TargetHost); } if (remoteCertValidationCallback != null) { - success = remoteCertValidationCallback(_hostName, remoteCertificateEx, chain, sslPolicyErrors); + success = remoteCertValidationCallback(_sslAuthenticationOptions.TargetHost, remoteCertificateEx, chain, sslPolicyErrors); } else { - if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateNotAvailable && !_remoteCertRequired) + if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateNotAvailable && !_sslAuthenticationOptions.RemoteCertRequired) { success = true; } @@ -1024,7 +1040,8 @@ namespace System.Net.Security if (NetEventSource.IsEnabled) { LogCertificateValidation(remoteCertValidationCallback, sslPolicyErrors, success, chain); - if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"Cert validation, remote cert = {remoteCertificateEx}"); + if (NetEventSource.IsEnabled) + NetEventSource.Info(this, $"Cert validation, remote cert = {remoteCertificateEx}"); } if (!success) @@ -1047,14 +1064,16 @@ namespace System.Net.Security } } - if (NetEventSource.IsEnabled) NetEventSource.Exit(this, success); + if (NetEventSource.IsEnabled) + NetEventSource.Exit(this, success); return success; } public ProtocolToken CreateFatalHandshakeAlertToken(SslPolicyErrors sslPolicyErrors, X509Chain chain) { - if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + if (NetEventSource.IsEnabled) + NetEventSource.Enter(this); TlsAlertMessage alertMessage; @@ -1072,14 +1091,16 @@ namespace System.Net.Security break; } - if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"alertMessage:{alertMessage}"); + if (NetEventSource.IsEnabled) + NetEventSource.Info(this, $"alertMessage:{alertMessage}"); SecurityStatusPal status; status = SslStreamPal.ApplyAlertToken(ref _credentialsHandle, _securityContext, TlsAlertType.Fatal, alertMessage); if (status.ErrorCode != SecurityStatusPalErrorCode.OK) { - if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"ApplyAlertToken() returned {status.ErrorCode}"); + if (NetEventSource.IsEnabled) + NetEventSource.Info(this, $"ApplyAlertToken() returned {status.ErrorCode}"); if (status.Exception != null) { @@ -1090,20 +1111,23 @@ namespace System.Net.Security } ProtocolToken token = GenerateAlertToken(); - if (NetEventSource.IsEnabled) NetEventSource.Exit(this, token); + if (NetEventSource.IsEnabled) + NetEventSource.Exit(this, token); return token; } public ProtocolToken CreateShutdownToken() { - if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + if (NetEventSource.IsEnabled) + NetEventSource.Enter(this); SecurityStatusPal status; status = SslStreamPal.ApplyShutdownToken(ref _credentialsHandle, _securityContext); if (status.ErrorCode != SecurityStatusPalErrorCode.OK) { - if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"ApplyAlertToken() returned {status.ErrorCode}"); + if (NetEventSource.IsEnabled) + NetEventSource.Info(this, $"ApplyAlertToken() returned {status.ErrorCode}"); if (status.Exception != null) { @@ -1114,7 +1138,8 @@ namespace System.Net.Security } ProtocolToken token = GenerateAlertToken(); - if (NetEventSource.IsEnabled) NetEventSource.Exit(this, token); + if (NetEventSource.IsEnabled) + NetEventSource.Exit(this, token); return token; } @@ -1129,7 +1154,7 @@ namespace System.Net.Security return token; } - + private static TlsAlertMessage GetAlertMessageFromChain(X509Chain chain) { foreach (X509ChainStatus chainStatus in chain.ChainStatus) @@ -1147,7 +1172,7 @@ namespace System.Net.Security } if ((chainStatus.Status & - (X509ChainStatusFlags.Revoked | X509ChainStatusFlags.OfflineRevocation )) != 0) + (X509ChainStatusFlags.Revoked | X509ChainStatusFlags.OfflineRevocation)) != 0) { return TlsAlertMessage.CertificateRevoked; } @@ -1161,7 +1186,7 @@ namespace System.Net.Security if ((chainStatus.Status & X509ChainStatusFlags.CtlNotValidForUsage) != 0) { - return TlsAlertMessage.UnsupportedCert; + return TlsAlertMessage.UnsupportedCert; } if ((chainStatus.Status & @@ -1182,7 +1207,8 @@ namespace System.Net.Security private void LogCertificateValidation(RemoteCertValidationCallback remoteCertValidationCallback, SslPolicyErrors sslPolicyErrors, bool success, X509Chain chain) { - if (!NetEventSource.IsEnabled) return; + if (!NetEventSource.IsEnabled) + return; if (sslPolicyErrors != SslPolicyErrors.None) { diff --git a/external/corefx/src/System.Net.Security/src/System/Net/Security/SslApplicationProtocol.cs b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslApplicationProtocol.cs new file mode 100644 index 0000000000..a999c793c9 --- /dev/null +++ b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslApplicationProtocol.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.Text; + +namespace System.Net.Security +{ + public readonly struct SslApplicationProtocol : IEquatable + { + private readonly ReadOnlyMemory _readOnlyProtocol; + private static readonly Encoding s_utf8 = Encoding.GetEncoding(Encoding.UTF8.CodePage, EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback); + + // Refer IANA on ApplicationProtocols: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids + // h2 + public static readonly SslApplicationProtocol Http2 = new SslApplicationProtocol(new byte[] { 0x68, 0x32 }, false); + // http/1.1 + public static readonly SslApplicationProtocol Http11 = new SslApplicationProtocol(new byte[] { 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31 }, false); + + internal SslApplicationProtocol(byte[] protocol, bool copy) + { + if (protocol == null) + { + throw new ArgumentNullException(nameof(protocol)); + } + + // RFC 7301 states protocol size <= 255 bytes. + if (protocol.Length == 0 || protocol.Length > 255) + { + throw new ArgumentException(SR.net_ssl_app_protocol_invalid, nameof(protocol)); + } + + if (copy) + { + byte[] temp = new byte[protocol.Length]; + Array.Copy(protocol, 0, temp, 0, protocol.Length); + _readOnlyProtocol = new ReadOnlyMemory(temp); + } + else + { + _readOnlyProtocol = new ReadOnlyMemory(protocol); + } + } + + public SslApplicationProtocol(byte[] protocol) : this(protocol, true) { } + + public SslApplicationProtocol(string protocol) : this(s_utf8.GetBytes(protocol), copy: false) { } + + public ReadOnlyMemory Protocol + { + get => _readOnlyProtocol; + } + + public bool Equals(SslApplicationProtocol other) + { + if (_readOnlyProtocol.Length != other._readOnlyProtocol.Length) + return false; + + return (_readOnlyProtocol.IsEmpty && other._readOnlyProtocol.IsEmpty) || + _readOnlyProtocol.Span.SequenceEqual(other._readOnlyProtocol.Span); + } + + public override bool Equals(object obj) + { + if (obj is SslApplicationProtocol protocol) + { + return Equals(protocol); + } + + return false; + } + + public override int GetHashCode() + { + if (_readOnlyProtocol.Length == 0) + return 0; + + int hash1 = 0; + ReadOnlySpan pSpan = _readOnlyProtocol.Span; + for (int i = 0; i < _readOnlyProtocol.Length; i++) + { + hash1 = ((hash1 << 5) + hash1) ^ pSpan[i]; + } + + return hash1; + } + + public override string ToString() + { + try + { + if (_readOnlyProtocol.Length == 0) + { + return null; + } + + return s_utf8.GetString(_readOnlyProtocol.Span); + } + catch + { + // In case of decoding errors, return the byte values as hex string. + int byteCharsLength = _readOnlyProtocol.Length * 5; + char[] byteChars = new char[byteCharsLength]; + int index = 0; + + ReadOnlySpan pSpan = _readOnlyProtocol.Span; + for (int i = 0; i < byteCharsLength; i += 5) + { + byte b = pSpan[index++]; + byteChars[i] = '0'; + byteChars[i + 1] = 'x'; + byteChars[i + 2] = GetHexValue(Math.DivRem(b, 16, out int rem)); + byteChars[i + 3] = GetHexValue(rem); + byteChars[i + 4] = ' '; + } + + return new string(byteChars, 0, byteCharsLength - 1); + + char GetHexValue(int i) + { + if (i < 10) + return (char)(i + '0'); + + return (char)(i - 10 + 'a'); + } + } + } + + public static bool operator ==(SslApplicationProtocol left, SslApplicationProtocol right) + { + return left.Equals(right); + } + + public static bool operator !=(SslApplicationProtocol left, SslApplicationProtocol right) + { + return !(left == right); + } + } +} + diff --git a/external/corefx/src/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs new file mode 100644 index 0000000000..609666aee4 --- /dev/null +++ b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs @@ -0,0 +1,71 @@ +// 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.Collections.Generic; +using System.Runtime.InteropServices; +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; + +namespace System.Net.Security +{ + internal class SslAuthenticationOptions + { + internal SslAuthenticationOptions(SslClientAuthenticationOptions sslClientAuthenticationOptions) + { + // Common options. + AllowRenegotiation = sslClientAuthenticationOptions.AllowRenegotiation; + ApplicationProtocols = sslClientAuthenticationOptions.ApplicationProtocols; + CertValidationDelegate = sslClientAuthenticationOptions._certValidationDelegate; + CheckCertName = true; + EnabledSslProtocols = sslClientAuthenticationOptions.EnabledSslProtocols; + EncryptionPolicy = sslClientAuthenticationOptions.EncryptionPolicy; + IsServer = false; + RemoteCertRequired = true; + RemoteCertificateValidationCallback = sslClientAuthenticationOptions.RemoteCertificateValidationCallback; + TargetHost = sslClientAuthenticationOptions.TargetHost; + + // Client specific options. + CertSelectionDelegate = sslClientAuthenticationOptions._certSelectionDelegate; + CertificateRevocationCheckMode = sslClientAuthenticationOptions.CertificateRevocationCheckMode; + ClientCertificates = sslClientAuthenticationOptions.ClientCertificates; + LocalCertificateSelectionCallback = sslClientAuthenticationOptions.LocalCertificateSelectionCallback; + } + + internal SslAuthenticationOptions(SslServerAuthenticationOptions sslServerAuthenticationOptions) + { + // Common options. + AllowRenegotiation = sslServerAuthenticationOptions.AllowRenegotiation; + ApplicationProtocols = sslServerAuthenticationOptions.ApplicationProtocols; + CertValidationDelegate = sslServerAuthenticationOptions._certValidationDelegate; + CheckCertName = false; + EnabledSslProtocols = sslServerAuthenticationOptions.EnabledSslProtocols; + EncryptionPolicy = sslServerAuthenticationOptions.EncryptionPolicy; + IsServer = true; + RemoteCertRequired = sslServerAuthenticationOptions.ClientCertificateRequired; + RemoteCertificateValidationCallback = sslServerAuthenticationOptions.RemoteCertificateValidationCallback; + TargetHost = string.Empty; + + // Server specific options. + CertificateRevocationCheckMode = sslServerAuthenticationOptions.CertificateRevocationCheckMode; + ServerCertificate = sslServerAuthenticationOptions.ServerCertificate; + } + + internal bool AllowRenegotiation { get; set; } + internal string TargetHost { get; set; } + internal X509CertificateCollection ClientCertificates { get; set; } + internal List ApplicationProtocols { get; } + internal bool IsServer { get; set; } + internal RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; set; } + internal LocalCertificateSelectionCallback LocalCertificateSelectionCallback { get; set; } + internal X509Certificate ServerCertificate { get; set; } + internal SslProtocols EnabledSslProtocols { get; set; } + internal X509RevocationMode CertificateRevocationCheckMode { get; set; } + internal EncryptionPolicy EncryptionPolicy { get; set; } + internal bool RemoteCertRequired { get; set; } + internal bool CheckCertName { get; set; } + internal RemoteCertValidationCallback CertValidationDelegate { get; set; } + internal LocalCertSelectionCallback CertSelectionDelegate { get; set; } + } +} + diff --git a/external/corefx/src/System.Net.Security/src/System/Net/Security/SslClientAuthenticationOptions.cs b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslClientAuthenticationOptions.cs new file mode 100644 index 0000000000..4bc3773708 --- /dev/null +++ b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslClientAuthenticationOptions.cs @@ -0,0 +1,73 @@ +// 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.Collections.Generic; +using System.Runtime.InteropServices; +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; + +namespace System.Net.Security +{ + public class SslClientAuthenticationOptions + { + private EncryptionPolicy _encryptionPolicy = EncryptionPolicy.RequireEncryption; + private X509RevocationMode _checkCertificateRevocation = X509RevocationMode.NoCheck; + private SslProtocols _enabledSslProtocols = SecurityProtocol.SystemDefaultSecurityProtocols; + private bool _allowRenegotiation = true; + + internal RemoteCertValidationCallback _certValidationDelegate; + internal LocalCertSelectionCallback _certSelectionDelegate; + + public bool AllowRenegotiation + { + get => _allowRenegotiation; + set => _allowRenegotiation = value; + } + + public LocalCertificateSelectionCallback LocalCertificateSelectionCallback { get; set; } + + public RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; set; } + + public List ApplicationProtocols { get; set; } + + public string TargetHost { get; set; } + + public X509CertificateCollection ClientCertificates { get; set; } + + public X509RevocationMode CertificateRevocationCheckMode + { + get => _checkCertificateRevocation; + set + { + if (value != X509RevocationMode.NoCheck && value != X509RevocationMode.Offline && value != X509RevocationMode.Online) + { + throw new ArgumentException(SR.Format(SR.net_invalid_enum, nameof(X509RevocationMode)), nameof(value)); + } + + _checkCertificateRevocation = value; + } + } + + public EncryptionPolicy EncryptionPolicy + { + get => _encryptionPolicy; + set + { + if (value != EncryptionPolicy.RequireEncryption && value != EncryptionPolicy.AllowNoEncryption && value != EncryptionPolicy.NoEncryption) + { + throw new ArgumentException(SR.Format(SR.net_invalid_enum, nameof(EncryptionPolicy)), nameof(value)); + } + + _encryptionPolicy = value; + } + } + + public SslProtocols EnabledSslProtocols + { + get => _enabledSslProtocols; + set => _enabledSslProtocols = value; + } + } +} + diff --git a/external/corefx/src/System.Net.Security/src/System/Net/Security/SslServerAuthenticationOptions.cs b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslServerAuthenticationOptions.cs new file mode 100644 index 0000000000..cf5b271a10 --- /dev/null +++ b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslServerAuthenticationOptions.cs @@ -0,0 +1,69 @@ +// 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.Collections.Generic; +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; + +namespace System.Net.Security +{ + public class SslServerAuthenticationOptions + { + private X509RevocationMode _checkCertificateRevocation = X509RevocationMode.NoCheck; + private SslProtocols _enabledSslProtocols = SecurityProtocol.SystemDefaultSecurityProtocols; + private EncryptionPolicy _encryptionPolicy = EncryptionPolicy.RequireEncryption; + private bool _allowRenegotiation = true; + + internal RemoteCertValidationCallback _certValidationDelegate; + + public bool AllowRenegotiation + { + get => _allowRenegotiation; + set => _allowRenegotiation = value; + } + + public bool ClientCertificateRequired { get; set; } + + public List ApplicationProtocols { get; set; } + + public RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; set; } + + public X509Certificate ServerCertificate { get; set; } + + public SslProtocols EnabledSslProtocols + { + get => _enabledSslProtocols; + set => _enabledSslProtocols = value; + } + + public X509RevocationMode CertificateRevocationCheckMode + { + get => _checkCertificateRevocation; + set + { + if (value != X509RevocationMode.NoCheck && value != X509RevocationMode.Offline && value != X509RevocationMode.Online) + { + throw new ArgumentException(SR.Format(SR.net_invalid_enum, nameof(X509RevocationMode)), nameof(value)); + } + + _checkCertificateRevocation = value; + } + } + + public EncryptionPolicy EncryptionPolicy + { + get => _encryptionPolicy; + set + { + if (value != EncryptionPolicy.RequireEncryption && value != EncryptionPolicy.AllowNoEncryption && value != EncryptionPolicy.NoEncryption) + { + throw new ArgumentException(SR.Format(SR.net_invalid_enum, nameof(EncryptionPolicy)), nameof(value)); + } + + _encryptionPolicy = value; + } + } + } +} + diff --git a/external/corefx/src/System.Net.Security/src/System/Net/Security/SslSessionsCache.cs b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslSessionsCache.cs index 3eb80ac5ae..4975228664 100644 --- a/external/corefx/src/System.Net.Security/src/System/Net/Security/SslSessionsCache.cs +++ b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslSessionsCache.cs @@ -19,7 +19,7 @@ namespace System.Net.Security // // Uses certificate thumb-print comparison. // - private struct SslCredKey : IEquatable + private readonly struct SslCredKey : IEquatable { private readonly byte[] _thumbPrint; private readonly int _allowedProtocols; diff --git a/external/corefx/src/System.Net.Security/src/System/Net/Security/SslState.cs b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslState.cs index 100d1c3c5e..d415cf5bde 100644 --- a/external/corefx/src/System.Net.Security/src/System/Net/Security/SslState.cs +++ b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslState.cs @@ -22,8 +22,7 @@ namespace System.Net.Security private static AsyncProtocolCallback s_readFrameCallback = new AsyncProtocolCallback(ReadFrameCallback); private static AsyncCallback s_writeCallback = new AsyncCallback(WriteCallback); - private RemoteCertValidationCallback _certValidationDelegate; - private LocalCertSelectionCallback _certSelectionDelegate; + internal SslAuthenticationOptions _sslAuthenticationOptions; private Stream _innerStream; @@ -33,7 +32,6 @@ namespace System.Net.Security private SecureChannel _context; private bool _handshakeCompleted; - private bool _certValidationFailed; private bool _shutdown; private SecurityStatusPal _securityStatus; @@ -70,31 +68,17 @@ namespace System.Net.Security private int _lockReadState; private object _queuedReadStateRequest; - private readonly EncryptionPolicy _encryptionPolicy; - // // The public Client and Server classes enforce the parameters rules before // calling into this .ctor. // - internal SslState(Stream innerStream, RemoteCertValidationCallback certValidationCallback, LocalCertSelectionCallback certSelectionCallback, EncryptionPolicy encryptionPolicy) + internal SslState(Stream innerStream) { _innerStream = innerStream; - _certValidationDelegate = certValidationCallback; - _certSelectionDelegate = certSelectionCallback; - _encryptionPolicy = encryptionPolicy; } - internal void ValidateCreateContext(bool isServer, string targetHost, SslProtocols enabledSslProtocols, X509Certificate serverCertificate, X509CertificateCollection clientCertificates, bool remoteCertRequired, bool checkCertRevocationStatus) + internal void ValidateCreateContext(SslClientAuthenticationOptions sslClientAuthenticationOptions) { - ValidateCreateContext(isServer, targetHost, enabledSslProtocols, serverCertificate, clientCertificates, remoteCertRequired, - checkCertRevocationStatus, !isServer); - } - - internal void ValidateCreateContext(bool isServer, string targetHost, SslProtocols enabledSslProtocols, X509Certificate serverCertificate, X509CertificateCollection clientCertificates, bool remoteCertRequired, bool checkCertRevocationStatus, bool checkCertName) - { - // - // We don't support SSL alerts right now, hence any exception is fatal and cannot be retried. - // if (_exception != null) { _exception.Throw(); @@ -105,31 +89,26 @@ namespace System.Net.Security throw new InvalidOperationException(SR.net_auth_reauth); } - if (Context != null && IsServer != isServer) + if (Context != null && IsServer) { throw new InvalidOperationException(SR.net_auth_client_server); } - if (targetHost == null) + if (sslClientAuthenticationOptions.TargetHost == null) { - throw new ArgumentNullException(nameof(targetHost)); + throw new ArgumentNullException(nameof(sslClientAuthenticationOptions.TargetHost)); } - if (isServer && serverCertificate == null) + if (sslClientAuthenticationOptions.TargetHost.Length == 0) { - throw new ArgumentNullException(nameof(serverCertificate)); - } - - if (targetHost.Length == 0) - { - targetHost = "?" + Interlocked.Increment(ref s_uniqueNameInteger).ToString(NumberFormatInfo.InvariantInfo); + sslClientAuthenticationOptions.TargetHost = "?" + Interlocked.Increment(ref s_uniqueNameInteger).ToString(NumberFormatInfo.InvariantInfo); } _exception = null; try { - _context = new SecureChannel(targetHost, isServer, enabledSslProtocols, serverCertificate, clientCertificates, remoteCertRequired, - checkCertName, checkCertRevocationStatus, _encryptionPolicy, _certSelectionDelegate); + _sslAuthenticationOptions = new SslAuthenticationOptions(sslClientAuthenticationOptions); + _context = new SecureChannel(_sslAuthenticationOptions); } catch (Win32Exception e) { @@ -137,6 +116,51 @@ namespace System.Net.Security } } + internal void ValidateCreateContext(SslServerAuthenticationOptions sslServerAuthenticationOptions) + { + if (_exception != null) + { + _exception.Throw(); + } + + if (Context != null && Context.IsValidContext) + { + throw new InvalidOperationException(SR.net_auth_reauth); + } + + if (Context != null && !IsServer) + { + throw new InvalidOperationException(SR.net_auth_client_server); + } + + if (sslServerAuthenticationOptions.ServerCertificate == null) + { + throw new ArgumentNullException(nameof(sslServerAuthenticationOptions.ServerCertificate)); + } + + _exception = null; + try + { + _sslAuthenticationOptions = new SslAuthenticationOptions(sslServerAuthenticationOptions); + _context = new SecureChannel(_sslAuthenticationOptions); + } + catch (Win32Exception e) + { + throw new AuthenticationException(SR.net_auth_SSPI, e); + } + } + + internal SslApplicationProtocol NegotiatedApplicationProtocol + { + get + { + if (Context == null) + return default; + + return Context.NegotiatedApplicationProtocol; + } + } + internal bool IsAuthenticated { get @@ -172,14 +196,6 @@ namespace System.Net.Security } } - // - // SSL related properties - // - internal void SetCertValidationDelegate(RemoteCertValidationCallback certValidationCallback) - { - _certValidationDelegate = certValidationCallback; - } - // // This will return selected local cert for both client/server streams // @@ -209,20 +225,7 @@ namespace System.Net.Security { get { - return Context != null && Context.CheckCertRevocationStatus; - } - } - - internal SecurityStatusPal LastSecurityStatus - { - get { return _securityStatus; } - } - - internal bool IsCertValidationFailed - { - get - { - return _certValidationFailed; + return Context != null && Context.CheckCertRevocationStatus != X509RevocationMode.NoCheck; } } @@ -390,14 +393,6 @@ namespace System.Net.Security } } - internal int HeaderSize - { - get - { - return Context.HeaderSize; - } - } - internal int MaxDataSize { get @@ -526,14 +521,14 @@ namespace System.Net.Security // // Must be called under the lock in case concurrent handshake is going. // - internal int CheckOldKeyDecryptedData(byte[] buffer, int offset, int count) + internal int CheckOldKeyDecryptedData(Memory buffer) { CheckThrow(true); if (_queuedReadData != null) { // This is inefficient yet simple and should be a REALLY rare case. - int toCopy = Math.Min(_queuedReadCount, count); - Buffer.BlockCopy(_queuedReadData, 0, buffer, offset, toCopy); + int toCopy = Math.Min(_queuedReadCount, buffer.Length); + new Span(_queuedReadData, 0, toCopy).CopyTo(buffer.Span); _queuedReadCount -= toCopy; if (_queuedReadCount == 0) { @@ -580,14 +575,15 @@ namespace System.Net.Security // Not aync so the connection is completed at this point. if (lazyResult == null && NetEventSource.IsEnabled) { - if (NetEventSource.IsEnabled) NetEventSource.Log.SspiSelectedCipherSuite(nameof(ProcessAuthentication), - SslProtocol, - CipherAlgorithm, - CipherStrength, - HashAlgorithm, - HashStrength, - KeyExchangeAlgorithm, - KeyExchangeStrength); + if (NetEventSource.IsEnabled) + NetEventSource.Log.SspiSelectedCipherSuite(nameof(ProcessAuthentication), + SslProtocol, + CipherAlgorithm, + CipherStrength, + HashAlgorithm, + HashStrength, + KeyExchangeAlgorithm, + KeyExchangeStrength); } } catch (Exception) @@ -714,14 +710,15 @@ namespace System.Net.Security // Connection is completed at this point. if (NetEventSource.IsEnabled) { - if (NetEventSource.IsEnabled) NetEventSource.Log.SspiSelectedCipherSuite(nameof(EndProcessAuthentication), - SslProtocol, - CipherAlgorithm, - CipherStrength, - HashAlgorithm, - HashStrength, - KeyExchangeAlgorithm, - KeyExchangeStrength); + if (NetEventSource.IsEnabled) + NetEventSource.Log.SspiSelectedCipherSuite(nameof(EndProcessAuthentication), + SslProtocol, + CipherAlgorithm, + CipherStrength, + HashAlgorithm, + HashStrength, + KeyExchangeAlgorithm, + KeyExchangeStrength); } } @@ -1014,23 +1011,24 @@ namespace System.Net.Security // private bool CompleteHandshake(ref ProtocolToken alertToken) { - if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + if (NetEventSource.IsEnabled) + NetEventSource.Enter(this); Context.ProcessHandshakeSuccess(); - if (!Context.VerifyRemoteCertificate(_certValidationDelegate, ref alertToken)) + if (!Context.VerifyRemoteCertificate(_sslAuthenticationOptions.CertValidationDelegate, ref alertToken)) { _handshakeCompleted = false; - _certValidationFailed = true; - if (NetEventSource.IsEnabled) NetEventSource.Exit(this, false); + if (NetEventSource.IsEnabled) + NetEventSource.Exit(this, false); return false; } - _certValidationFailed = false; _handshakeCompleted = true; - if (NetEventSource.IsEnabled) NetEventSource.Exit(this, true); + if (NetEventSource.IsEnabled) + NetEventSource.Exit(this, true); return true; } @@ -1088,7 +1086,8 @@ namespace System.Net.Security private static void PartialFrameCallback(AsyncProtocolRequest asyncRequest) { - if (NetEventSource.IsEnabled) NetEventSource.Enter(null); + if (NetEventSource.IsEnabled) + NetEventSource.Enter(null); // Async ONLY completion. SslState sslState = (SslState)asyncRequest.AsyncObject; @@ -1112,7 +1111,8 @@ namespace System.Net.Security // private static void ReadFrameCallback(AsyncProtocolRequest asyncRequest) { - if (NetEventSource.IsEnabled) NetEventSource.Enter(null); + if (NetEventSource.IsEnabled) + NetEventSource.Enter(null); // Async ONLY completion. SslState sslState = (SslState)asyncRequest.AsyncObject; @@ -1183,43 +1183,28 @@ namespace System.Net.Security } _lockReadState = LockRead; - object obj = _queuedReadStateRequest; - if (obj == null) - { - // Other thread did not get under the lock yet. - return; - } - - _queuedReadStateRequest = null; - if (obj is LazyAsyncResult) - { - ((LazyAsyncResult)obj).InvokeCallback(); - } - else - { - ThreadPool.QueueUserWorkItem(new WaitCallback(CompleteRequestWaitCallback), obj); - } + HandleQueuedCallback(ref _queuedReadStateRequest); } } - + // Returns: // -1 - proceed // 0 - queued // X - some bytes are ready, no need for IO - internal int CheckEnqueueRead(byte[] buffer, int offset, int count, AsyncProtocolRequest request) + internal int CheckEnqueueRead(Memory buffer) { int lockState = Interlocked.CompareExchange(ref _lockReadState, LockRead, LockNone); if (lockState != LockHandshake) { // Proceed, no concurrent handshake is ongoing so no need for a lock. - return CheckOldKeyDecryptedData(buffer, offset, count); + return CheckOldKeyDecryptedData(buffer); } LazyAsyncResult lazyResult = null; lock (this) { - int result = CheckOldKeyDecryptedData(buffer, offset, count); + int result = CheckOldKeyDecryptedData(buffer); if (result != -1) { return result; @@ -1235,12 +1220,6 @@ namespace System.Net.Security _lockReadState = LockPendingRead; - if (request != null) - { - // Request queued. - _queuedReadStateRequest = request; - return 0; - } lazyResult = new LazyAsyncResult(null, null, /*must be */ null); _queuedReadStateRequest = lazyResult; } @@ -1248,7 +1227,40 @@ namespace System.Net.Security lazyResult.InternalWaitForCompletion(); lock (this) { - return CheckOldKeyDecryptedData(buffer, offset, count); + return CheckOldKeyDecryptedData(buffer); + } + } + + internal ValueTask CheckEnqueueReadAsync(Memory buffer) + { + int lockState = Interlocked.CompareExchange(ref _lockReadState, LockRead, LockNone); + + if (lockState != LockHandshake) + { + // Proceed, no concurrent handshake is ongoing so no need for a lock. + return new ValueTask(CheckOldKeyDecryptedData(buffer)); + } + + lock (this) + { + int result = CheckOldKeyDecryptedData(buffer); + if (result != -1) + { + return new ValueTask(result); + } + + // Check again under lock. + if (_lockReadState != LockHandshake) + { + // The other thread has finished before we grabbed the lock. + _lockReadState = LockRead; + return new ValueTask(-1); + } + + _lockReadState = LockPendingRead; + TaskCompletionSource taskCompletionSource = new TaskCompletionSource(buffer, TaskCreationOptions.RunContinuationsAsynchronously); + _queuedReadStateRequest = taskCompletionSource; + return new ValueTask(taskCompletionSource.Task); } } @@ -1346,23 +1358,30 @@ namespace System.Net.Security lock (this) { - HandleWriteCallback(); + HandleQueuedCallback(ref _queuedWriteStateRequest); } } - private void HandleWriteCallback() + private void HandleQueuedCallback(ref object queuedStateRequest) { - object obj = _queuedWriteStateRequest; - _queuedWriteStateRequest = null; + object obj = queuedStateRequest; + if (obj == null) + { + return; + } + queuedStateRequest = null; + switch (obj) { - case null: - break; case LazyAsyncResult lazy: lazy.InvokeCallback(); break; - case TaskCompletionSource tsc: - tsc.SetResult(0); + case TaskCompletionSource taskCompletionSource when taskCompletionSource.Task.AsyncState != null: + Memory array = (Memory)taskCompletionSource.Task.AsyncState; + taskCompletionSource.SetResult(CheckOldKeyDecryptedData(array)); + break; + case TaskCompletionSource taskCompletionSource: + taskCompletionSource.SetResult(0); break; default: ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncResumeHandshake), obj); @@ -1427,7 +1446,7 @@ namespace System.Net.Security } _lockWriteState = LockWrite; - HandleWriteCallback(); + HandleQueuedCallback(ref _queuedWriteStateRequest); } } finally @@ -1662,7 +1681,8 @@ namespace System.Net.Security // This is called from SslStream class too. internal int GetRemainingFrameSize(byte[] buffer, int offset, int dataSize) { - if (NetEventSource.IsEnabled) NetEventSource.Enter(this, buffer, offset, dataSize); + if (NetEventSource.IsEnabled) + NetEventSource.Enter(this, buffer, offset, dataSize); int payloadSize = -1; switch (_Framing) @@ -1702,7 +1722,8 @@ namespace System.Net.Security break; } - if (NetEventSource.IsEnabled) NetEventSource.Exit(this, payloadSize); + if (NetEventSource.IsEnabled) + NetEventSource.Exit(this, payloadSize); return payloadSize; } @@ -1824,7 +1845,7 @@ namespace System.Net.Security internal IAsyncResult BeginShutdown(AsyncCallback asyncCallback, object asyncState) { - CheckThrow(authSuccessCheck:true, shutdownCheck:true); + CheckThrow(authSuccessCheck: true, shutdownCheck: true); ProtocolToken message = Context.CreateShutdownToken(); return TaskToApm.Begin(InnerStream.WriteAsync(message.Payload, 0, message.Payload.Length), asyncCallback, asyncState); @@ -1832,7 +1853,7 @@ namespace System.Net.Security internal void EndShutdown(IAsyncResult result) { - CheckThrow(authSuccessCheck: true, shutdownCheck:true); + CheckThrow(authSuccessCheck: true, shutdownCheck: true); TaskToApm.End(result); _shutdown = true; diff --git a/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStream.cs b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStream.cs index c110f932ab..62027859b7 100644 --- a/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStream.cs +++ b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStream.cs @@ -2,7 +2,9 @@ // 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.Collections.Generic; using System.IO; +using System.Runtime.InteropServices; using System.Security.Authentication; using System.Security.Authentication.ExtendedProtection; using System.Security.Cryptography.X509Certificates; @@ -36,10 +38,14 @@ namespace System.Net.Security public class SslStream : AuthenticatedStream { private SslState _sslState; - private RemoteCertificateValidationCallback _userCertificateValidationCallback; - private LocalCertificateSelectionCallback _userCertificateSelectionCallback; private object _remoteCertificateOrBytes; + internal RemoteCertificateValidationCallback _userCertificateValidationCallback; + internal LocalCertificateSelectionCallback _userCertificateSelectionCallback; + internal RemoteCertValidationCallback _certValidationDelegate; + internal LocalCertSelectionCallback _certSelectionDelegate; + internal EncryptionPolicy _encryptionPolicy; + public SslStream(Stream innerStream) : this(innerStream, false, null, null) { @@ -72,9 +78,44 @@ namespace System.Net.Security _userCertificateValidationCallback = userCertificateValidationCallback; _userCertificateSelectionCallback = userCertificateSelectionCallback; - RemoteCertValidationCallback _userCertValidationCallbackWrapper = new RemoteCertValidationCallback(UserCertValidationCallbackWrapper); - LocalCertSelectionCallback _userCertSelectionCallbackWrapper = userCertificateSelectionCallback == null ? null : new LocalCertSelectionCallback(UserCertSelectionCallbackWrapper); - _sslState = new SslState(innerStream, _userCertValidationCallbackWrapper, _userCertSelectionCallbackWrapper, encryptionPolicy); + _encryptionPolicy = encryptionPolicy; + _certValidationDelegate = new RemoteCertValidationCallback(UserCertValidationCallbackWrapper); + _certSelectionDelegate = userCertificateSelectionCallback == null ? null : new LocalCertSelectionCallback(UserCertSelectionCallbackWrapper); + _sslState = new SslState(innerStream); + } + + public SslApplicationProtocol NegotiatedApplicationProtocol + { + get + { + return _sslState.NegotiatedApplicationProtocol; + } + } + + private void SetAndVerifyValidationCallback(RemoteCertificateValidationCallback callback) + { + if (_userCertificateValidationCallback == null) + { + _userCertificateValidationCallback = callback; + _certValidationDelegate = new RemoteCertValidationCallback(UserCertValidationCallbackWrapper); + } + else if (callback != null && _userCertificateValidationCallback != callback) + { + throw new InvalidOperationException(SR.Format(SR.net_conflicting_options, nameof(RemoteCertificateValidationCallback))); + } + } + + private void SetAndVerifySelectionCallback(LocalCertificateSelectionCallback callback) + { + if (_userCertificateSelectionCallback == null) + { + _userCertificateSelectionCallback = callback; + _certSelectionDelegate = _userCertificateSelectionCallback == null ? null : new LocalCertSelectionCallback(UserCertSelectionCallbackWrapper); + } + else if (callback != null && _userCertificateSelectionCallback != callback) + { + throw new InvalidOperationException(SR.Format(SR.net_conflicting_options, nameof(LocalCertificateSelectionCallback))); + } } private bool UserCertValidationCallbackWrapper(string hostName, X509Certificate2 certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) @@ -119,8 +160,29 @@ namespace System.Net.Security SslProtocols enabledSslProtocols, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState) { - SecurityProtocol.ThrowOnNotAllowed(enabledSslProtocols); - _sslState.ValidateCreateContext(false, targetHost, enabledSslProtocols, null, clientCertificates, true, checkCertificateRevocation); + SslClientAuthenticationOptions options = new SslClientAuthenticationOptions + { + TargetHost = targetHost, + ClientCertificates = clientCertificates, + EnabledSslProtocols = enabledSslProtocols, + CertificateRevocationCheckMode = checkCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck, + EncryptionPolicy = _encryptionPolicy, + }; + + return BeginAuthenticateAsClient(options, CancellationToken.None, asyncCallback, asyncState); + } + + internal virtual IAsyncResult BeginAuthenticateAsClient(SslClientAuthenticationOptions sslClientAuthenticationOptions, CancellationToken cancellationToken, AsyncCallback asyncCallback, object asyncState) + { + SecurityProtocol.ThrowOnNotAllowed(sslClientAuthenticationOptions.EnabledSslProtocols); + SetAndVerifyValidationCallback(sslClientAuthenticationOptions.RemoteCertificateValidationCallback); + SetAndVerifySelectionCallback(sslClientAuthenticationOptions.LocalCertificateSelectionCallback); + + // Set the delegates on the options. + sslClientAuthenticationOptions._certValidationDelegate = _certValidationDelegate; + sslClientAuthenticationOptions._certSelectionDelegate = _certSelectionDelegate; + + _sslState.ValidateCreateContext(sslClientAuthenticationOptions); LazyAsyncResult result = new LazyAsyncResult(_sslState, asyncState, asyncCallback); _sslState.ProcessAuthentication(result); @@ -154,8 +216,28 @@ namespace System.Net.Security AsyncCallback asyncCallback, object asyncState) { - SecurityProtocol.ThrowOnNotAllowed(enabledSslProtocols); - _sslState.ValidateCreateContext(true, string.Empty, enabledSslProtocols, serverCertificate, null, clientCertificateRequired, checkCertificateRevocation); + SslServerAuthenticationOptions options = new SslServerAuthenticationOptions + { + ServerCertificate = serverCertificate, + ClientCertificateRequired = clientCertificateRequired, + EnabledSslProtocols = enabledSslProtocols, + CertificateRevocationCheckMode = checkCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck, + EncryptionPolicy = _encryptionPolicy, + }; + + return BeginAuthenticateAsServer(options, CancellationToken.None, asyncCallback, asyncState); + } + + private IAsyncResult BeginAuthenticateAsServer(SslServerAuthenticationOptions sslServerAuthenticationOptions, CancellationToken cancellationToken, AsyncCallback asyncCallback, object asyncState) + { + SecurityProtocol.ThrowOnNotAllowed(sslServerAuthenticationOptions.EnabledSslProtocols); + SetAndVerifyValidationCallback(sslServerAuthenticationOptions.RemoteCertificateValidationCallback); + + // Set the delegate on the options. + sslServerAuthenticationOptions._certValidationDelegate = _certValidationDelegate; + + _sslState.ValidateCreateContext(sslServerAuthenticationOptions); + LazyAsyncResult result = new LazyAsyncResult(_sslState, asyncState, asyncCallback); _sslState.ProcessAuthentication(result); return result; @@ -202,8 +284,29 @@ namespace System.Net.Security public virtual void AuthenticateAsClient(string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation) { - SecurityProtocol.ThrowOnNotAllowed(enabledSslProtocols); - _sslState.ValidateCreateContext(false, targetHost, enabledSslProtocols, null, clientCertificates, true, checkCertificateRevocation); + SslClientAuthenticationOptions options = new SslClientAuthenticationOptions + { + TargetHost = targetHost, + ClientCertificates = clientCertificates, + EnabledSslProtocols = enabledSslProtocols, + CertificateRevocationCheckMode = checkCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck, + EncryptionPolicy = _encryptionPolicy, + }; + + AuthenticateAsClient(options); + } + + private void AuthenticateAsClient(SslClientAuthenticationOptions sslClientAuthenticationOptions) + { + SecurityProtocol.ThrowOnNotAllowed(sslClientAuthenticationOptions.EnabledSslProtocols); + SetAndVerifyValidationCallback(sslClientAuthenticationOptions.RemoteCertificateValidationCallback); + SetAndVerifySelectionCallback(sslClientAuthenticationOptions.LocalCertificateSelectionCallback); + + // Set the delegates on the options. + sslClientAuthenticationOptions._certValidationDelegate = _certValidationDelegate; + sslClientAuthenticationOptions._certSelectionDelegate = _certSelectionDelegate; + + _sslState.ValidateCreateContext(sslClientAuthenticationOptions); _sslState.ProcessAuthentication(null); } @@ -219,8 +322,27 @@ namespace System.Net.Security public virtual void AuthenticateAsServer(X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation) { - SecurityProtocol.ThrowOnNotAllowed(enabledSslProtocols); - _sslState.ValidateCreateContext(true, string.Empty, enabledSslProtocols, serverCertificate, null, clientCertificateRequired, checkCertificateRevocation); + SslServerAuthenticationOptions options = new SslServerAuthenticationOptions + { + ServerCertificate = serverCertificate, + ClientCertificateRequired = clientCertificateRequired, + EnabledSslProtocols = enabledSslProtocols, + CertificateRevocationCheckMode = checkCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck, + EncryptionPolicy = _encryptionPolicy, + }; + + AuthenticateAsServer(options); + } + + private void AuthenticateAsServer(SslServerAuthenticationOptions sslServerAuthenticationOptions) + { + SecurityProtocol.ThrowOnNotAllowed(sslServerAuthenticationOptions.EnabledSslProtocols); + SetAndVerifyValidationCallback(sslServerAuthenticationOptions.RemoteCertificateValidationCallback); + + // Set the delegate on the options. + sslServerAuthenticationOptions._certValidationDelegate = _certValidationDelegate; + + _sslState.ValidateCreateContext(sslServerAuthenticationOptions); _sslState.ProcessAuthentication(null); } #endregion @@ -252,6 +374,15 @@ namespace System.Net.Security this); } + public Task AuthenticateAsClientAsync(SslClientAuthenticationOptions sslClientAuthenticationOptions, CancellationToken cancellationToken) + { + return Task.Factory.FromAsync( + (arg1, arg2, callback, state) => ((SslStream)state).BeginAuthenticateAsClient(arg1, arg2, callback, state), + iar => ((SslStream)iar.AsyncState).EndAuthenticateAsClient(iar), + sslClientAuthenticationOptions, cancellationToken, + this); + } + public virtual Task AuthenticateAsServerAsync(X509Certificate serverCertificate) => Task.Factory.FromAsync( (arg1, callback, state) => ((SslStream)state).BeginAuthenticateAsServer(arg1, callback, state), @@ -278,6 +409,15 @@ namespace System.Net.Security this); } + public Task AuthenticateAsServerAsync(SslServerAuthenticationOptions sslServerAuthenticationOptions, CancellationToken cancellationToken) + { + return Task.Factory.FromAsync( + (arg1, arg2, callback, state) => ((SslStream)state).BeginAuthenticateAsServer(arg1, arg2, callback, state), + iar => ((SslStream)iar.AsyncState).EndAuthenticateAsServer(iar), + sslServerAuthenticationOptions, cancellationToken, + this); + } + public virtual Task ShutdownAsync() => Task.Factory.FromAsync( (callback, state) => ((SslStream)state).BeginShutdown(callback, state), @@ -575,5 +715,15 @@ namespace System.Net.Security { return _sslState.SecureStream.WriteAsync(source, cancellationToken); } + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + return _sslState.SecureStream.ReadAsync(buffer, offset, count, cancellationToken); + } + + public override ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default) + { + return _sslState.SecureStream.ReadAsync(destination, cancellationToken); + } } } diff --git a/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStreamInternal.Adapters.cs b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStreamInternal.Adapters.cs index 48f0c13c57..a94abda4d5 100644 --- a/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStreamInternal.Adapters.cs +++ b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStreamInternal.Adapters.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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.Threading; @@ -15,7 +15,40 @@ namespace System.Net.Security Task WriteAsync(byte[] buffer, int offset, int count); } - private struct SslWriteAsync : ISslWriteAdapter + private interface ISslReadAdapter + { + ValueTask ReadAsync(byte[] buffer, int offset, int count); + ValueTask LockAsync(Memory buffer); + } + + private readonly struct SslReadAsync : ISslReadAdapter + { + private readonly SslState _sslState; + private readonly CancellationToken _cancellationToken; + + public SslReadAsync(SslState sslState, CancellationToken cancellationToken) + { + _cancellationToken = cancellationToken; + _sslState = sslState; + } + + public ValueTask ReadAsync(byte[] buffer, int offset, int count) => _sslState.InnerStream.ReadAsync(new Memory(buffer, offset, count), _cancellationToken); + + public ValueTask LockAsync(Memory buffer) => _sslState.CheckEnqueueReadAsync(buffer); + } + + private readonly struct SslReadSync : ISslReadAdapter + { + private readonly SslState _sslState; + + public SslReadSync(SslState sslState) => _sslState = sslState; + + public ValueTask ReadAsync(byte[] buffer, int offset, int count) => new ValueTask(_sslState.InnerStream.Read(buffer, offset, count)); + + public ValueTask LockAsync(Memory buffer) => new ValueTask(_sslState.CheckEnqueueRead(buffer)); + } + + private readonly struct SslWriteAsync : ISslWriteAdapter { private readonly SslState _sslState; private readonly CancellationToken _cancellationToken; @@ -31,7 +64,7 @@ namespace System.Net.Security public Task WriteAsync(byte[] buffer, int offset, int count) => _sslState.InnerStream.WriteAsync(buffer, offset, count, _cancellationToken); } - private struct SslWriteSync : ISslWriteAdapter + private readonly struct SslWriteSync : ISslWriteAdapter { private readonly SslState _sslState; diff --git a/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs index 97160861ad..99e2f8b18a 100644 --- a/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs +++ b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs @@ -16,17 +16,12 @@ namespace System.Net.Security // internal partial class SslStreamInternal { - private static readonly AsyncProtocolCallback s_resumeAsyncReadCallback = new AsyncProtocolCallback(ResumeAsyncReadCallback); - private static readonly AsyncProtocolCallback s_readHeaderCallback = new AsyncProtocolCallback(ReadHeaderCallback); - private static readonly AsyncProtocolCallback s_readFrameCallback = new AsyncProtocolCallback(ReadFrameCallback); - private const int FrameOverhead = 32; private const int ReadBufferSize = 4096 * 4 + FrameOverhead; // We read in 16K chunks + headers. private readonly SslState _sslState; private int _nestedWrite; private int _nestedRead; - private AsyncProtocolRequest _readProtocolRequest; // cached, reusable AsyncProtocolRequest used for read operations // Never updated directly, special properties are used. This is the read buffer. private byte[] _internalBuffer; @@ -104,7 +99,12 @@ namespace System.Net.Security return bytesRead == 1 ? oneByte[0] : -1; } - internal int Read(byte[] buffer, int offset, int count) => ProcessRead(buffer, offset, count, null); + internal int Read(byte[] buffer, int offset, int count) + { + ValidateParameters(buffer, offset, count); + SslReadSync reader = new SslReadSync(_sslState); + return ReadAsyncInternal(reader, new Memory(buffer, offset, count)).GetAwaiter().GetResult(); + } internal void Write(byte[] buffer, int offset, int count) { @@ -116,45 +116,24 @@ namespace System.Net.Security internal IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState) { - var bufferResult = new BufferAsyncResult(this, buffer, offset, count, asyncState, asyncCallback); - ProcessRead(buffer, offset, count, bufferResult); - return bufferResult; + return TaskToApm.Begin(ReadAsync(buffer, offset, count, CancellationToken.None), asyncCallback, asyncState); } - internal int EndRead(IAsyncResult asyncResult) + internal Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { - if (asyncResult == null) - { - throw new ArgumentNullException(nameof(asyncResult)); - } - - BufferAsyncResult bufferResult = asyncResult as BufferAsyncResult; - if (bufferResult == null) - { - throw new ArgumentException(SR.Format(SR.net_io_async_result, asyncResult.GetType().FullName), nameof(asyncResult)); - } - - if (Interlocked.Exchange(ref _nestedRead, 0) == 0) - { - throw new InvalidOperationException(SR.Format(SR.net_io_invalidendcall, "EndRead")); - } - - // No "artificial" timeouts implemented so far, InnerStream controls timeout. - bufferResult.InternalWaitForCompletion(); - - if (bufferResult.Result is Exception) - { - if (bufferResult.Result is IOException) - { - throw (Exception)bufferResult.Result; - } - - throw new IOException(SR.net_io_read, (Exception)bufferResult.Result); - } - - return bufferResult.Int32Result; + ValidateParameters(buffer, offset, count); + SslReadAsync read = new SslReadAsync(_sslState, cancellationToken); + return ReadAsyncInternal(read, new Memory(buffer, offset, count)).AsTask(); } + internal ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken) + { + SslReadAsync read = new SslReadAsync(_sslState, cancellationToken); + return ReadAsyncInternal(read, buffer); + } + + internal int EndRead(IAsyncResult asyncResult) => TaskToApm.End(asyncResult); + internal IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState) { return TaskToApm.Begin(WriteAsync(buffer, offset, count, CancellationToken.None), asyncCallback, asyncState); @@ -219,25 +198,118 @@ namespace System.Net.Security } } - private AsyncProtocolRequest GetOrCreateProtocolRequest(ref AsyncProtocolRequest aprField, LazyAsyncResult asyncResult) + private async ValueTask ReadAsyncInternal(TReadAdapter adapter, Memory buffer) + where TReadAdapter : ISslReadAdapter { - AsyncProtocolRequest request = null; - if (asyncResult != null) + if (Interlocked.Exchange(ref _nestedRead, 1) == 1) { - // SslStreamInternal supports only a single read and a single write operation at a time. - // As such, we can cache and reuse the AsyncProtocolRequest object that's used throughout - // the implementation. - request = aprField; - if (request != null) + throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, nameof(ReadAsync), "read")); + } + + while (true) + { + int copyBytes; + if (_decryptedBytesCount != 0) { - request.Reset(asyncResult); + copyBytes = CopyDecryptedData(buffer); + + _sslState.FinishRead(null); + _nestedRead = 0; + + return copyBytes; } - else + + copyBytes = await adapter.LockAsync(buffer).ConfigureAwait(false); + try { - aprField = request = new AsyncProtocolRequest(asyncResult); + if (copyBytes > 0) + { + return copyBytes; + } + + ResetReadBuffer(); + int readBytes = await FillBufferAsync(adapter, SecureChannel.ReadHeaderSize).ConfigureAwait(false); + if (readBytes == 0) + { + return 0; + } + + int payloadBytes = _sslState.GetRemainingFrameSize(_internalBuffer, _internalOffset, readBytes); + if (payloadBytes < 0) + { + throw new IOException(SR.net_frame_read_size); + } + + readBytes = await FillBufferAsync(adapter, SecureChannel.ReadHeaderSize + payloadBytes).ConfigureAwait(false); + if (readBytes < 0) + { + throw new IOException(SR.net_frame_read_size); + } + + // At this point, readBytes contains the size of the header plus body. + // Set _decrytpedBytesOffset/Count to the current frame we have (including header) + // DecryptData will decrypt in-place and modify these to point to the actual decrypted data, which may be smaller. + _decryptedBytesOffset = _internalOffset; + _decryptedBytesCount = readBytes; + SecurityStatusPal status = _sslState.DecryptData(_internalBuffer, ref _decryptedBytesOffset, ref _decryptedBytesCount); + + // Treat the bytes we just decrypted as consumed + // Note, we won't do another buffer read until the decrypted bytes are processed + ConsumeBufferedBytes(readBytes); + + if (status.ErrorCode != SecurityStatusPalErrorCode.OK) + { + byte[] extraBuffer = null; + if (_decryptedBytesCount != 0) + { + extraBuffer = new byte[_decryptedBytesCount]; + Buffer.BlockCopy(_internalBuffer, _decryptedBytesOffset, extraBuffer, 0, _decryptedBytesCount); + + _decryptedBytesCount = 0; + } + + ProtocolToken message = new ProtocolToken(null, status); + if (NetEventSource.IsEnabled) + NetEventSource.Info(null, $"***Processing an error Status = {message.Status}"); + + if (message.Renegotiate) + { + if (!_sslState._sslAuthenticationOptions.AllowRenegotiation) + { + throw new IOException(SR.net_ssl_io_renego); + } + + _sslState.ReplyOnReAuthentication(extraBuffer); + + // Loop on read. + continue; + } + + if (message.CloseConnection) + { + _sslState.FinishRead(null); + return 0; + } + + throw new IOException(SR.net_io_decrypt, message.GetException()); + } + } + catch (Exception e) + { + _sslState.FinishRead(null); + + if (e is IOException) + { + throw; + } + + throw new IOException(SR.net_io_read, e); + } + finally + { + _nestedRead = 0; } } - return request; } private Task WriteAsyncInternal(TWriteAdapter writeAdapter, ReadOnlyMemory buffer) @@ -359,136 +431,63 @@ namespace System.Net.Security } while (buffer.Length != 0); } - // Fill the buffer up to the minimum specified size (or more, if possible). - // Returns 0 if EOF on initial read, otherwise throws on EOF. - // Returns minSize on success. - private int FillBuffer(int minSize) - { - Debug.Assert(_internalOffset == 0); - Debug.Assert(minSize > _internalBufferCount); - - int initialCount = _internalBufferCount; - do - { - int bytes = _sslState.InnerStream.Read(_internalBuffer, _internalBufferCount, _internalBuffer.Length - _internalBufferCount); - if (bytes == 0) - { - if (_internalBufferCount != initialCount) - { - // We read some bytes, but not as many as we expected, so throw. - throw new IOException(SR.net_io_eof); - } - - return 0; - } - - _internalBufferCount += bytes; - } while (_internalBufferCount < minSize); - - return minSize; - } - - // Fill the buffer up to the minimum specified size (or more, if possible). - // Returns 0 if EOF on initial read, otherwise throws on EOF. - // Returns minSize on success. - public async Task FillBufferAsync(int minSize) - { - Debug.Assert(_internalOffset == 0); - Debug.Assert(minSize > _internalBufferCount); - - int initialCount = _internalBufferCount; - do - { - int bytes = await _sslState.InnerStream.ReadAsync(_internalBuffer, _internalBufferCount, _internalBuffer.Length - _internalBufferCount, CancellationToken.None).ConfigureAwait(false); - if (bytes == 0) - { - if (_internalBufferCount != initialCount) - { - // We read some bytes, but not as many as we expected, so throw. - throw new IOException(SR.net_io_eof); - } - - return 0; - } - - _internalBufferCount += bytes; - } while (_internalBufferCount < minSize); - - return minSize; - } - - private static void CompleteFromCompletedTask(Task task, AsyncProtocolRequest asyncRequest) - { - Debug.Assert(task.IsCompleted); - if (task.IsCompletedSuccessfully) - { - asyncRequest.CompleteRequest(task.Result); - } - else if (task.IsFaulted) - { - asyncRequest.CompleteUserWithError(task.Exception.InnerException); - } - else - { - asyncRequest.CompleteUserWithError(new OperationCanceledException()); - } - } - - // Returns true if pending, false if completed - private static bool TaskToAsyncProtocolRequest(Task task, AsyncProtocolRequest asyncRequest, AsyncProtocolCallback asyncCallback, out int result) - { - // Parameters other than asyncCallback are not used - asyncRequest.SetNextRequest(null, 0, 0, asyncCallback); - - if (task.IsCompleted) - { - CompleteFromCompletedTask(task, asyncRequest); - } - else - { - task.ContinueWith((t, s) => CompleteFromCompletedTask(t, (AsyncProtocolRequest)s), - asyncRequest, - CancellationToken.None, - TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.DenyChildAttach, - TaskScheduler.Default); - } - - if (asyncRequest.MustCompleteSynchronously) - { - result = asyncRequest.Result; - return false; - } - - result = 0; - return true; - } - - private int EnsureBufferedBytes(int minSize, AsyncProtocolRequest asyncRequest, AsyncProtocolCallback asyncCallback) + private ValueTask FillBufferAsync(TReadAdapter adapter, int minSize) + where TReadAdapter : ISslReadAdapter { if (_internalBufferCount >= minSize) { - return minSize; + return new ValueTask(minSize); } - int bytesRead; - if (asyncRequest != null) + int initialCount = _internalBufferCount; + do { - if (TaskToAsyncProtocolRequest( - FillBufferAsync(minSize), - asyncRequest, - asyncCallback, - out bytesRead)) + ValueTask t = adapter.ReadAsync(_internalBuffer, _internalBufferCount, _internalBuffer.Length - _internalBufferCount); + if (!t.IsCompletedSuccessfully) { - return -1; + return new ValueTask(InternalFillBufferAsync(adapter, t.AsTask(), minSize, initialCount)); + } + int bytes = t.Result; + if (bytes == 0) + { + if (_internalBufferCount != initialCount) + { + // We read some bytes, but not as many as we expected, so throw. + throw new IOException(SR.net_io_eof); + } + + return new ValueTask(0); + } + + _internalBufferCount += bytes; + } while (_internalBufferCount < minSize); + + return new ValueTask(minSize); + + async Task InternalFillBufferAsync(TReadAdapter adap, Task task, int min, int initial) + { + while (true) + { + int b = await task.ConfigureAwait(false); + if (b == 0) + { + if (_internalBufferCount != initial) + { + throw new IOException(SR.net_io_eof); + } + + return 0; + } + + _internalBufferCount += b; + if (_internalBufferCount >= min) + { + return min; + } + + task = adap.ReadAsync(_internalBuffer, _internalBufferCount, _internalBuffer.Length - _internalBufferCount).AsTask(); } } - else - { - bytesRead = FillBuffer(minSize); - } - - Debug.Assert(bytesRead == 0 || bytesRead == minSize); - return bytesRead; } private void ConsumeBufferedBytes(int byteCount) @@ -502,14 +501,14 @@ namespace System.Net.Security ReturnReadBufferIfEmpty(); } - private int CopyDecryptedData(byte[] buffer, int offset, int count) + private int CopyDecryptedData(Memory buffer) { Debug.Assert(_decryptedBytesCount > 0); - int copyBytes = _decryptedBytesCount > count ? count : _decryptedBytesCount; + int copyBytes = Math.Min(_decryptedBytesCount, buffer.Length); if (copyBytes != 0) { - Buffer.BlockCopy(_internalBuffer, _decryptedBytesOffset, buffer, offset, copyBytes); + new Span(_internalBuffer, _decryptedBytesOffset, copyBytes).CopyTo(buffer.Span); _decryptedBytesOffset += copyBytes; _decryptedBytesCount -= copyBytes; @@ -517,284 +516,5 @@ namespace System.Net.Security ReturnReadBufferIfEmpty(); return copyBytes; } - - // - // Combined sync/async read method. For sync request asyncRequest==null. - // - private int ProcessRead(byte[] buffer, int offset, int count, BufferAsyncResult asyncResult) - { - ValidateParameters(buffer, offset, count); - - if (Interlocked.Exchange(ref _nestedRead, 1) == 1) - { - throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, (asyncResult != null ? "BeginRead" : "Read"), "read")); - } - - // If this is an async operation, get the AsyncProtocolRequest to use. - // We do this only after we verify we're the sole write operation in flight. - AsyncProtocolRequest asyncRequest = GetOrCreateProtocolRequest(ref _readProtocolRequest, asyncResult); - - bool failed = false; - - try - { - if (_decryptedBytesCount != 0) - { - int copyBytes = CopyDecryptedData(buffer, offset, count); - - asyncRequest?.CompleteUser(copyBytes); - - return copyBytes; - } - - return StartReading(buffer, offset, count, asyncRequest); - } - catch (Exception e) - { - _sslState.FinishRead(null); - failed = true; - - if (e is IOException) - { - throw; - } - - throw new IOException(SR.net_io_read, e); - } - finally - { - if (asyncRequest == null || failed) - { - _nestedRead = 0; - } - } - } - - // - // To avoid recursion when decrypted 0 bytes this method will loop until a decrypted result at least 1 byte. - // - private int StartReading(byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest) - { - int result = 0; - - if (_decryptedBytesCount != 0) - { - NetEventSource.Fail(this, $"Previous frame was not consumed. _decryptedBytesCount: {_decryptedBytesCount}"); - } - - do - { - if (asyncRequest != null) - { - asyncRequest.SetNextRequest(buffer, offset, count, s_resumeAsyncReadCallback); - } - - int copyBytes = _sslState.CheckEnqueueRead(buffer, offset, count, asyncRequest); - if (copyBytes == 0) - { - // Queued but not completed! - return 0; - } - - if (copyBytes != -1) - { - asyncRequest?.CompleteUser(copyBytes); - - return copyBytes; - } - } - - // When we read -1 bytes means we have decrypted 0 bytes or rehandshaking, need looping. - while ((result = StartFrameHeader(buffer, offset, count, asyncRequest)) == -1); - - return result; - } - - private int StartFrameHeader(byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest) - { - ResetReadBuffer(); - int readBytes = EnsureBufferedBytes(SecureChannel.ReadHeaderSize, asyncRequest, s_readHeaderCallback); - if (readBytes == -1) - { - Debug.Assert(asyncRequest != null); - return 0; - } - - return StartFrameBody(readBytes, buffer, offset, count, asyncRequest); - } - - private int StartFrameBody(int readBytes, byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest) - { - if (readBytes == 0) - { - // EOF - asyncRequest?.CompleteUser(0); - return 0; - } - - Debug.Assert(readBytes == SecureChannel.ReadHeaderSize); - - int payloadBytes = _sslState.GetRemainingFrameSize(_internalBuffer, _internalOffset, readBytes); - if (payloadBytes < 0) - { - throw new IOException(SR.net_frame_read_size); - } - - readBytes = EnsureBufferedBytes(SecureChannel.ReadHeaderSize + payloadBytes, asyncRequest, s_readFrameCallback); - if (readBytes == -1) - { - Debug.Assert(asyncRequest != null); - return 0; - } - - Debug.Assert(readBytes == 0 || readBytes == SecureChannel.ReadHeaderSize + payloadBytes); - - return ProcessFrameBody(readBytes, buffer, offset, count, asyncRequest); - } - - // - // readBytes == SSL Data Payload size on input or 0 on EOF. - // - private int ProcessFrameBody(int readBytes, byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest) - { - if (readBytes == 0) - { - // EOF - throw new IOException(SR.net_io_eof); - } - - // At this point, readBytes contains the size of the header plus body. - // Set _decrytpedBytesOffset/Count to the current frame we have (including header) - // DecryptData will decrypt in-place and modify these to point to the actual decrypted data, which may be smaller. - _decryptedBytesOffset = _internalOffset; - _decryptedBytesCount = readBytes; - SecurityStatusPal status = _sslState.DecryptData(_internalBuffer, ref _decryptedBytesOffset, ref _decryptedBytesCount); - - // Treat the bytes we just decrypted as consumed - // Note, we won't do another buffer read until the decrypted bytes are processed - ConsumeBufferedBytes(readBytes); - - if (status.ErrorCode != SecurityStatusPalErrorCode.OK) - { - byte[] extraBuffer = null; - if (_decryptedBytesCount != 0) - { - extraBuffer = new byte[_decryptedBytesCount]; - Buffer.BlockCopy(_internalBuffer, _decryptedBytesOffset, extraBuffer, 0, _decryptedBytesCount); - - _decryptedBytesCount = 0; - } - - return ProcessReadErrorCode(status, asyncRequest, extraBuffer); - } - - if (_decryptedBytesCount == 0) - { - // Read again since remote side has sent encrypted 0 bytes. - return -1; - } - - int copyBytes = CopyDecryptedData(buffer, offset, count); - - _sslState.FinishRead(null); - asyncRequest?.CompleteUser(copyBytes); - - return copyBytes; - } - - private int ProcessReadErrorCode(SecurityStatusPal status, AsyncProtocolRequest asyncRequest, byte[] extraBuffer) - { - ProtocolToken message = new ProtocolToken(null, status); - if (NetEventSource.IsEnabled) - NetEventSource.Info(null, $"***Processing an error Status = {message.Status}"); - - if (message.Renegotiate) - { - _sslState.ReplyOnReAuthentication(extraBuffer); - - // Loop on read. - return -1; - } - - if (message.CloseConnection) - { - _sslState.FinishRead(null); - asyncRequest?.CompleteUser(0); - - return 0; - } - - throw new IOException(SR.net_io_decrypt, message.GetException()); - } - - // - // This is used in a rare situation when async Read is resumed from completed handshake. - // - private static void ResumeAsyncReadCallback(AsyncProtocolRequest request) - { - try - { - ((SslStreamInternal)request.AsyncObject).StartReading(request.Buffer, request.Offset, request.Count, request); - } - catch (Exception e) - { - if (request.IsUserCompleted) - { - // This will throw on a worker thread. - throw; - } - - ((SslStreamInternal)request.AsyncObject)._sslState.FinishRead(null); - request.CompleteUserWithError(e); - } - } - - private static void ReadHeaderCallback(AsyncProtocolRequest asyncRequest) - { - try - { - SslStreamInternal sslStream = (SslStreamInternal)asyncRequest.AsyncObject; - BufferAsyncResult bufferResult = (BufferAsyncResult)asyncRequest.UserAsyncResult; - if (-1 == sslStream.StartFrameBody(asyncRequest.Result, bufferResult.Buffer, bufferResult.Offset, bufferResult.Count, asyncRequest)) - { - // in case we decrypted 0 bytes start another reading. - sslStream.StartReading(bufferResult.Buffer, bufferResult.Offset, bufferResult.Count, asyncRequest); - } - } - catch (Exception e) - { - if (asyncRequest.IsUserCompleted) - { - // This will throw on a worker thread. - throw; - } - - asyncRequest.CompleteUserWithError(e); - } - } - - private static void ReadFrameCallback(AsyncProtocolRequest asyncRequest) - { - try - { - SslStreamInternal sslStream = (SslStreamInternal)asyncRequest.AsyncObject; - BufferAsyncResult bufferResult = (BufferAsyncResult)asyncRequest.UserAsyncResult; - if (-1 == sslStream.ProcessFrameBody(asyncRequest.Result, bufferResult.Buffer, bufferResult.Offset, bufferResult.Count, asyncRequest)) - { - // in case we decrypted 0 bytes start another reading. - sslStream.StartReading(bufferResult.Buffer, bufferResult.Offset, bufferResult.Count, asyncRequest); - } - } - catch (Exception e) - { - if (asyncRequest.IsUserCompleted) - { - // This will throw on a worker thread. - throw; - } - - asyncRequest.CompleteUserWithError(e); - } - } } } diff --git a/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs index b426ebe466..54f8303ce5 100644 --- a/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs +++ b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs @@ -37,11 +37,21 @@ namespace System.Net.Security public static SecurityStatusPal AcceptSecurityContext( ref SafeFreeCredentials credential, ref SafeDeleteContext context, - SecurityBuffer inputBuffer, + SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, - bool remoteCertRequired) + SslAuthenticationOptions sslAuthenticationOptions) { - return HandshakeInternal(credential, ref context, inputBuffer, outputBuffer, true, remoteCertRequired, null); + if (inputBuffers != null) + { + Debug.Assert(inputBuffers.Length == 2); + Debug.Assert(inputBuffers[1].token == null); + + return HandshakeInternal(credential, ref context, inputBuffers[0], outputBuffer, sslAuthenticationOptions); + } + else + { + return HandshakeInternal(credential, ref context, inputBuffer: null, outputBuffer, sslAuthenticationOptions); + } } public static SecurityStatusPal InitializeSecurityContext( @@ -49,9 +59,10 @@ namespace System.Net.Security ref SafeDeleteContext context, string targetName, SecurityBuffer inputBuffer, - SecurityBuffer outputBuffer) + SecurityBuffer outputBuffer, + SslAuthenticationOptions sslAuthenticationOptions) { - return HandshakeInternal(credential, ref context, inputBuffer, outputBuffer, false, false, targetName); + return HandshakeInternal(credential, ref context, inputBuffer, outputBuffer, sslAuthenticationOptions); } public static SecurityStatusPal InitializeSecurityContext( @@ -59,11 +70,28 @@ namespace System.Net.Security ref SafeDeleteContext context, string targetName, SecurityBuffer[] inputBuffers, - SecurityBuffer outputBuffer) + SecurityBuffer outputBuffer, + SslAuthenticationOptions sslAuthenticationOptions) { Debug.Assert(inputBuffers.Length == 2); Debug.Assert(inputBuffers[1].token == null); - return HandshakeInternal(credential, ref context, inputBuffers[0], outputBuffer, false, false, targetName); + return HandshakeInternal(credential, ref context, inputBuffers[0], outputBuffer, sslAuthenticationOptions); + } + + public static SecurityBuffer[] GetIncomingSecurityBuffers(SslAuthenticationOptions options, ref SecurityBuffer incomingSecurity) + { + SecurityBuffer[] incomingSecurityBuffers = null; + + if (incomingSecurity != null) + { + incomingSecurityBuffers = new SecurityBuffer[] + { + incomingSecurity, + new SecurityBuffer(null, 0, 0, SecurityBufferType.SECBUFFER_EMPTY) + }; + } + + return incomingSecurityBuffers; } public static SafeFreeCredentials AcquireCredentialsHandle( @@ -75,6 +103,12 @@ namespace System.Net.Security return new SafeFreeSslCredentials(certificate, protocols, policy); } + internal static byte[] GetNegotiatedApplicationProtocol(SafeDeleteContext context) + { + // OSX SecureTransport does not export APIs to support ALPN, no-op. + return null; + } + public static SecurityStatusPal EncryptMessage( SafeDeleteContext securityContext, ReadOnlyMemory input, @@ -97,7 +131,16 @@ namespace System.Net.Security MemoryHandle memHandle = input.Retain(pin: true); try { - PAL_TlsIo status = Interop.AppleCrypto.SslWrite(sslHandle, (byte*)memHandle.PinnedPointer, input.Length, out int written); + PAL_TlsIo status; + + lock (sslHandle) + { + status = Interop.AppleCrypto.SslWrite( + sslHandle, + (byte*)memHandle.Pointer, + input.Length, + out int written); + } if (status < 0) { @@ -157,7 +200,12 @@ namespace System.Net.Security fixed (byte* offsetInput = &buffer[offset]) { int written; - PAL_TlsIo status = Interop.AppleCrypto.SslRead(sslHandle, offsetInput, count, out written); + PAL_TlsIo status; + + lock (sslHandle) + { + status = Interop.AppleCrypto.SslRead(sslHandle, offsetInput, count, out written); + } if (status < 0) { @@ -226,9 +274,7 @@ namespace System.Net.Security ref SafeDeleteContext context, SecurityBuffer inputBuffer, SecurityBuffer outputBuffer, - bool isServer, - bool remoteCertRequired, - string targetName) + SslAuthenticationOptions sslAuthenticationOptions) { Debug.Assert(!credential.IsInvalid); @@ -238,18 +284,17 @@ namespace System.Net.Security if ((null == context) || context.IsInvalid) { - sslContext = new SafeDeleteSslContext(credential as SafeFreeSslCredentials, isServer); + sslContext = new SafeDeleteSslContext(credential as SafeFreeSslCredentials, sslAuthenticationOptions); context = sslContext; - if (!string.IsNullOrEmpty(targetName)) + if (!string.IsNullOrEmpty(sslAuthenticationOptions.TargetHost)) { - Debug.Assert(!isServer, "targetName should not be set for server-side handshakes"); - Interop.AppleCrypto.SslSetTargetName(sslContext.SslContext, targetName); + Debug.Assert(!sslAuthenticationOptions.IsServer, "targetName should not be set for server-side handshakes"); + Interop.AppleCrypto.SslSetTargetName(sslContext.SslContext, sslAuthenticationOptions.TargetHost); } - if (remoteCertRequired) + if (sslAuthenticationOptions.IsServer && sslAuthenticationOptions.RemoteCertRequired) { - Debug.Assert(isServer, "remoteCertRequired should not be set for client-side handshakes"); Interop.AppleCrypto.SslSetAcceptClientCert(sslContext.SslContext); } } @@ -259,7 +304,13 @@ namespace System.Net.Security sslContext.Write(inputBuffer.token, inputBuffer.offset, inputBuffer.size); } - SecurityStatusPal status = PerformHandshake(sslContext.SslContext); + SafeSslHandle sslHandle = sslContext.SslContext; + SecurityStatusPal status; + + lock (sslHandle) + { + status = PerformHandshake(sslHandle); + } byte[] output = sslContext.ReadPendingWrites(); outputBuffer.offset = 0; @@ -320,7 +371,13 @@ namespace System.Net.Security SafeDeleteContext securityContext) { SafeDeleteSslContext sslContext = ((SafeDeleteSslContext)securityContext); - int osStatus = Interop.AppleCrypto.SslShutdown(sslContext.SslContext); + SafeSslHandle sslHandle = sslContext.SslContext; + int osStatus; + + lock (sslHandle) + { + osStatus = Interop.AppleCrypto.SslShutdown(sslHandle); + } if (osStatus == 0) { diff --git a/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs index 6eb1b6296e..38461f1996 100644 --- a/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs +++ b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs @@ -2,6 +2,7 @@ // 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.Collections.Generic; using System.Diagnostics; using System.Net.Security; using System.Runtime.InteropServices; @@ -29,22 +30,49 @@ namespace System.Net.Security } public static SecurityStatusPal AcceptSecurityContext(ref SafeFreeCredentials credential, ref SafeDeleteContext context, - SecurityBuffer inputBuffer, SecurityBuffer outputBuffer, bool remoteCertRequired) + SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) { - return HandshakeInternal(credential, ref context, inputBuffer, outputBuffer, true, remoteCertRequired); + if (inputBuffers != null) + { + Debug.Assert(inputBuffers.Length == 2); + Debug.Assert(inputBuffers[1].token == null); + + return HandshakeInternal(credential, ref context, inputBuffers[0], outputBuffer, sslAuthenticationOptions); + } + else + { + return HandshakeInternal(credential, ref context, inputBuffer: null, outputBuffer, sslAuthenticationOptions); + } } public static SecurityStatusPal InitializeSecurityContext(ref SafeFreeCredentials credential, ref SafeDeleteContext context, - string targetName, SecurityBuffer inputBuffer, SecurityBuffer outputBuffer) + string targetName, SecurityBuffer inputBuffer, SecurityBuffer outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) { - return HandshakeInternal(credential, ref context, inputBuffer, outputBuffer, false, false); + return HandshakeInternal(credential, ref context, inputBuffer, outputBuffer, sslAuthenticationOptions); } - public static SecurityStatusPal InitializeSecurityContext(SafeFreeCredentials credential, ref SafeDeleteContext context, string targetName, SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer) + public static SecurityStatusPal InitializeSecurityContext(SafeFreeCredentials credential, ref SafeDeleteContext context, string targetName, SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) { Debug.Assert(inputBuffers.Length == 2); Debug.Assert(inputBuffers[1].token == null); - return HandshakeInternal(credential, ref context, inputBuffers[0], outputBuffer, false, false); + + return HandshakeInternal(credential, ref context, inputBuffers[0], outputBuffer, sslAuthenticationOptions); + } + + public static SecurityBuffer[] GetIncomingSecurityBuffers(SslAuthenticationOptions options, ref SecurityBuffer incomingSecurity) + { + SecurityBuffer[] incomingSecurityBuffers = null; + + if (incomingSecurity != null) + { + incomingSecurityBuffers = new SecurityBuffer[] + { + incomingSecurity, + new SecurityBuffer(null, 0, 0, SecurityBufferType.SECBUFFER_EMPTY) + }; + } + + return incomingSecurityBuffers; } public static SafeFreeCredentials AcquireCredentialsHandle(X509Certificate certificate, @@ -55,7 +83,7 @@ namespace System.Net.Security public static SecurityStatusPal EncryptMessage(SafeDeleteContext securityContext, ReadOnlyMemory input, int headerSize, int trailerSize, ref byte[] output, out int resultSize) { - return EncryptDecryptHelper(securityContext, input, offset:0, size: 0, encrypt: true, output: ref output, resultSize: out resultSize); + return EncryptDecryptHelper(securityContext, input, offset: 0, size: 0, encrypt: true, output: ref output, resultSize: out resultSize); } public static SecurityStatusPal DecryptMessage(SafeDeleteContext securityContext, byte[] buffer, ref int offset, ref int count) @@ -102,8 +130,13 @@ namespace System.Net.Security connectionInfo = new SslConnectionInfo(((SafeDeleteSslContext)securityContext).SslContext); } - private static SecurityStatusPal HandshakeInternal(SafeFreeCredentials credential, ref SafeDeleteContext context, - SecurityBuffer inputBuffer, SecurityBuffer outputBuffer, bool isServer, bool remoteCertRequired) + public static byte[] ConvertAlpnProtocolListToByteArray(List applicationProtocols) + { + return Interop.Ssl.ConvertAlpnProtocolListToByteArray(applicationProtocols); + } + + private static SecurityStatusPal HandshakeInternal(SafeFreeCredentials credential, ref SafeDeleteContext context, SecurityBuffer inputBuffer, + SecurityBuffer outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) { Debug.Assert(!credential.IsInvalid); @@ -111,7 +144,7 @@ namespace System.Net.Security { if ((null == context) || context.IsInvalid) { - context = new SafeDeleteSslContext(credential as SafeFreeSslCredentials, isServer, remoteCertRequired); + context = new SafeDeleteSslContext(credential as SafeFreeSslCredentials, sslAuthenticationOptions); } byte[] output = null; @@ -127,6 +160,16 @@ namespace System.Net.Security done = Interop.OpenSsl.DoSslHandshake(((SafeDeleteSslContext)context).SslContext, inputBuffer.token, inputBuffer.offset, inputBuffer.size, out output, out outputSize); } + // When the handshake is done, and the context is server, check if the alpnHandle target was set to null during ALPN. + // If it was, then that indiciates ALPN failed, send failure. + // We have this workaround, as openssl supports terminating handshake only from version 1.1.0, + // whereas ALPN is supported from version 1.0.2. + SafeSslHandle sslContext = ((SafeDeleteSslContext)context).SslContext; + if (done && sslAuthenticationOptions.IsServer && sslAuthenticationOptions.ApplicationProtocols != null && sslContext.AlpnHandle.IsAllocated && sslContext.AlpnHandle.Target == null) + { + return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, Interop.OpenSsl.CreateSslException(SR.net_alpn_failed)); + } + outputBuffer.size = outputSize; outputBuffer.offset = 0; outputBuffer.token = outputSize > 0 ? output : null; @@ -139,6 +182,14 @@ namespace System.Net.Security } } + internal static byte[] GetNegotiatedApplicationProtocol(SafeDeleteContext context) + { + if (context == null) + return null; + + return Interop.Ssl.SslGetAlpnSelected(((SafeDeleteSslContext)context).SslContext); + } + private static SecurityStatusPal EncryptDecryptHelper(SafeDeleteContext securityContext, ReadOnlyMemory input, int offset, int size, bool encrypt, ref byte[] output, out int resultSize) { resultSize = 0; diff --git a/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs index 54e91d66e8..d2b822b84d 100644 --- a/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs +++ b/external/corefx/src/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs @@ -2,15 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.Win32.SafeHandles; +using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; -using System.Net.Security; using System.Runtime.InteropServices; using System.Security.Authentication; using System.Security.Authentication.ExtendedProtection; using System.Security.Cryptography.X509Certificates; using System.Security.Principal; +using Microsoft.Win32.SafeHandles; namespace System.Net.Security { @@ -41,24 +41,29 @@ namespace System.Net.Security SSPIWrapper.GetVerifyPackageInfo(GlobalSSPI.SSPISecureChannel, SecurityPackage, true); } - public static SecurityStatusPal AcceptSecurityContext(ref SafeFreeCredentials credentialsHandle, ref SafeDeleteContext context, SecurityBuffer inputBuffer, SecurityBuffer outputBuffer, bool remoteCertRequired) + public static byte[] ConvertAlpnProtocolListToByteArray(List protocols) + { + return Interop.Sec_Application_Protocols.ToByteArray(protocols); + } + + public static SecurityStatusPal AcceptSecurityContext(ref SafeFreeCredentials credentialsHandle, ref SafeDeleteContext context, SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) { Interop.SspiCli.ContextFlags unusedAttributes = default(Interop.SspiCli.ContextFlags); int errorCode = SSPIWrapper.AcceptSecurityContext( GlobalSSPI.SSPISecureChannel, - ref credentialsHandle, + credentialsHandle, ref context, - ServerRequiredFlags | (remoteCertRequired ? Interop.SspiCli.ContextFlags.MutualAuth : Interop.SspiCli.ContextFlags.Zero), + ServerRequiredFlags | (sslAuthenticationOptions.RemoteCertRequired ? Interop.SspiCli.ContextFlags.MutualAuth : Interop.SspiCli.ContextFlags.Zero), Interop.SspiCli.Endianness.SECURITY_NATIVE_DREP, - inputBuffer, + inputBuffers, outputBuffer, ref unusedAttributes); return SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(errorCode); } - public static SecurityStatusPal InitializeSecurityContext(ref SafeFreeCredentials credentialsHandle, ref SafeDeleteContext context, string targetName, SecurityBuffer inputBuffer, SecurityBuffer outputBuffer) + public static SecurityStatusPal InitializeSecurityContext(ref SafeFreeCredentials credentialsHandle, ref SafeDeleteContext context, string targetName, SecurityBuffer inputBuffer, SecurityBuffer outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) { Interop.SspiCli.ContextFlags unusedAttributes = default(Interop.SspiCli.ContextFlags); @@ -76,7 +81,7 @@ namespace System.Net.Security return SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(errorCode); } - public static SecurityStatusPal InitializeSecurityContext(SafeFreeCredentials credentialsHandle, ref SafeDeleteContext context, string targetName, SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer) + public static SecurityStatusPal InitializeSecurityContext(SafeFreeCredentials credentialsHandle, ref SafeDeleteContext context, string targetName, SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) { Interop.SspiCli.ContextFlags unusedAttributes = default(Interop.SspiCli.ContextFlags); @@ -94,6 +99,45 @@ namespace System.Net.Security return SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(errorCode); } + public static SecurityBuffer[] GetIncomingSecurityBuffers(SslAuthenticationOptions options, ref SecurityBuffer incomingSecurity) + { + SecurityBuffer alpnBuffer = null; + SecurityBuffer[] incomingSecurityBuffers = null; + + if (options.ApplicationProtocols != null && options.ApplicationProtocols.Count != 0) + { + byte[] alpnBytes = SslStreamPal.ConvertAlpnProtocolListToByteArray(options.ApplicationProtocols); + alpnBuffer = new SecurityBuffer(alpnBytes, 0, alpnBytes.Length, SecurityBufferType.SECBUFFER_APPLICATION_PROTOCOLS); + } + + if (incomingSecurity != null) + { + if (alpnBuffer != null) + { + incomingSecurityBuffers = new SecurityBuffer[] + { + incomingSecurity, + new SecurityBuffer(null, 0, 0, SecurityBufferType.SECBUFFER_EMPTY), + alpnBuffer + }; + } + else + { + incomingSecurityBuffers = new SecurityBuffer[] + { + incomingSecurity, + new SecurityBuffer(null, 0, 0, SecurityBufferType.SECBUFFER_EMPTY) + }; + } + } + else if (alpnBuffer != null) + { + incomingSecurity = alpnBuffer; + } + + return incomingSecurityBuffers; + } + public static SafeFreeCredentials AcquireCredentialsHandle(X509Certificate certificate, SslProtocols protocols, EncryptionPolicy policy, bool isServer) { int protocolFlags = GetProtocolFlagsFromSslProtocols(protocols, isServer); @@ -103,9 +147,9 @@ namespace System.Net.Security if (!isServer) { direction = Interop.SspiCli.CredentialUse.SECPKG_CRED_OUTBOUND; - flags = - Interop.SspiCli.SCHANNEL_CRED.Flags.SCH_CRED_MANUAL_CRED_VALIDATION | - Interop.SspiCli.SCHANNEL_CRED.Flags.SCH_CRED_NO_DEFAULT_CREDS | + flags = + Interop.SspiCli.SCHANNEL_CRED.Flags.SCH_CRED_MANUAL_CRED_VALIDATION | + Interop.SspiCli.SCHANNEL_CRED.Flags.SCH_CRED_NO_DEFAULT_CREDS | Interop.SspiCli.SCHANNEL_CRED.Flags.SCH_SEND_AUX_RECORD; // CoreFX: always opt-in SCH_USE_STRONG_CRYPTO except for SSL3. @@ -131,6 +175,24 @@ namespace System.Net.Security return AcquireCredentialsHandle(direction, secureCredential); } + internal static byte[] GetNegotiatedApplicationProtocol(SafeDeleteContext context) + { + Interop.SecPkgContext_ApplicationProtocol alpnContext = SSPIWrapper.QueryContextAttributes( + GlobalSSPI.SSPISecureChannel, + context, + Interop.SspiCli.ContextAttribute.SECPKG_ATTR_APPLICATION_PROTOCOL) as Interop.SecPkgContext_ApplicationProtocol; + + // Check if the context returned is alpn data, with successful negotiation. + if (alpnContext == null || + alpnContext.ProtoNegoExt != Interop.ApplicationProtocolNegotiationExt.ALPN || + alpnContext.ProtoNegoStatus != Interop.ApplicationProtocolNegotiationStatus.Success) + { + return null; + } + + return alpnContext.Protocol; + } + public static unsafe SecurityStatusPal EncryptMessage(SafeDeleteContext securityContext, ReadOnlyMemory input, int headerSize, int trailerSize, ref byte[] output, out int resultSize) { // Ensure that there is sufficient space for the message output. @@ -182,7 +244,8 @@ namespace System.Net.Security if (errorCode != 0) { - if (NetEventSource.IsEnabled) NetEventSource.Info(securityContext, $"Encrypt ERROR {errorCode:X}"); + if (NetEventSource.IsEnabled) + NetEventSource.Info(securityContext, $"Encrypt ERROR {errorCode:X}"); resultSize = 0; return SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(errorCode); } @@ -266,7 +329,7 @@ namespace System.Net.Security ref securityContext, bufferDesc); - return SecurityStatusAdapterPal.GetSecurityStatusPalFromInterop(errorCode, attachException:true); + return SecurityStatusAdapterPal.GetSecurityStatusPalFromInterop(errorCode, attachException: true); } finally { @@ -287,7 +350,7 @@ namespace System.Net.Security ref securityContext, bufferDesc); - return SecurityStatusAdapterPal.GetSecurityStatusPalFromInterop(errorCode, attachException:true); + return SecurityStatusAdapterPal.GetSecurityStatusPalFromInterop(errorCode, attachException: true); } public static unsafe SafeFreeContextBufferChannelBinding QueryContextChannelBinding(SafeDeleteContext securityContext, ChannelBindingKind attribute) @@ -402,7 +465,7 @@ namespace System.Net.Security return SSPIWrapper.AcquireCredentialsHandle(GlobalSSPI.SSPISecureChannel, SecurityPackage, credUsage, secureCredential); }); } - catch(Exception ex) + catch (Exception ex) { Debug.Fail("AcquireCredentialsHandle failed.", ex.ToString()); return SSPIWrapper.AcquireCredentialsHandle(GlobalSSPI.SSPISecureChannel, SecurityPackage, credUsage, secureCredential); diff --git a/external/corefx/src/System.Net.Security/src/System/Security/Authentication/AuthenticationException.cs b/external/corefx/src/System.Net.Security/src/System/Security/Authentication/AuthenticationException.cs index 845e5d7805..3934ffb4d3 100644 --- a/external/corefx/src/System.Net.Security/src/System/Security/Authentication/AuthenticationException.cs +++ b/external/corefx/src/System.Net.Security/src/System/Security/Authentication/AuthenticationException.cs @@ -12,14 +12,19 @@ namespace System.Security.Authentication /// remote party willingness of accepting that. /// [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class AuthenticationException : SystemException { public AuthenticationException() { } + public AuthenticationException(string message) : base(message) { } + public AuthenticationException(string message, Exception innerException) : base(message, innerException) { } + protected AuthenticationException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { - throw new PlatformNotSupportedException(); } } @@ -31,14 +36,19 @@ namespace System.Security.Authentication /// /// [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class InvalidCredentialException : AuthenticationException { public InvalidCredentialException() { } + public InvalidCredentialException(string message) : base(message) { } + public InvalidCredentialException(string message, Exception innerException) : base(message, innerException) { } + protected InvalidCredentialException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corefx/src/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs b/external/corefx/src/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs index bb2ea9e153..e140430c2d 100644 --- a/external/corefx/src/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs +++ b/external/corefx/src/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs @@ -49,8 +49,8 @@ namespace System.Net.Security.Tests _clientCertificateRemovedByFilter = false; - if (PlatformDetection.IsWindows7 && - !useClientSelectionCallback && + if (PlatformDetection.IsWindows7 && + !useClientSelectionCallback && !Capability.IsTrustedRootCertificateInstalled()) { // https://technet.microsoft.com/en-us/library/hh831771.aspx#BKMK_Changes2012R2 @@ -70,11 +70,7 @@ namespace System.Net.Security.Tests Task clientConnect = clientConnection.ConnectAsync(serverEndPoint.Address, serverEndPoint.Port); Task serverAccept = server.AcceptTcpClientAsync(); - Assert.True( - Task.WaitAll( - new Task[] { clientConnect, serverAccept }, - TestConfiguration.PassingTestTimeoutMilliseconds), - "Client/Server TCP Connect timed out."); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(clientConnect, serverAccept); LocalCertificateSelectionCallback clientCertCallback = null; @@ -115,11 +111,7 @@ namespace System.Net.Security.Tests SslProtocolSupport.DefaultSslProtocols, false); - Assert.True( - Task.WaitAll( - new Task[] { clientAuthentication, serverAuthentication }, - TestConfiguration.PassingTestTimeoutMilliseconds), - "Client/Server Authentication timed out."); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(clientAuthentication, serverAuthentication); if (!_clientCertificateRemovedByFilter) { diff --git a/external/corefx/src/System.Net.Security/tests/FunctionalTests/LoggingTest.cs b/external/corefx/src/System.Net.Security/tests/FunctionalTests/LoggingTest.cs index 5a386c49f3..7d5f970490 100644 --- a/external/corefx/src/System.Net.Security/tests/FunctionalTests/LoggingTest.cs +++ b/external/corefx/src/System.Net.Security/tests/FunctionalTests/LoggingTest.cs @@ -39,8 +39,8 @@ namespace System.Net.Security.Tests { // Invoke tests that'll cause some events to be generated var test = new SslStreamStreamToStreamTest_Async(); - test.SslStream_StreamToStream_Authentication_Success(); - test.SslStream_StreamToStream_Successive_ClientWrite_Sync_Success(); + test.SslStream_StreamToStream_Authentication_Success().GetAwaiter().GetResult(); + test.SslStream_StreamToStream_Successive_ClientWrite_Sync_Success().GetAwaiter().GetResult(); }); Assert.DoesNotContain(events, ev => ev.EventId == 0); // errors from the EventSource itself Assert.InRange(events.Count, 1, int.MaxValue); diff --git a/external/corefx/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamStreamToStreamTest.cs b/external/corefx/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamStreamToStreamTest.cs index eff991e320..30ddb217fd 100644 --- a/external/corefx/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamStreamToStreamTest.cs +++ b/external/corefx/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamStreamToStreamTest.cs @@ -22,7 +22,7 @@ namespace System.Net.Security.Tests protected abstract Task AuthenticateAsServerAsync(NegotiateStream server); [Fact] - public void NegotiateStream_StreamToStream_Authentication_Success() + public async Task NegotiateStream_StreamToStream_Authentication_Success() { VirtualNetwork network = new VirtualNetwork(); @@ -37,9 +37,7 @@ namespace System.Net.Security.Tests Task[] auth = new Task[2]; auth[0] = AuthenticateAsClientAsync(client, CredentialCache.DefaultNetworkCredentials, string.Empty); auth[1] = AuthenticateAsServerAsync(server); - - bool finished = Task.WaitAll(auth, TestConfiguration.PassingTestTimeoutMilliseconds); - Assert.True(finished, "Handshake completed in the allotted time"); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(auth); // Expected Client property values: Assert.True(client.IsAuthenticated); @@ -74,7 +72,7 @@ namespace System.Net.Security.Tests } [Fact] - public void NegotiateStream_StreamToStream_Authentication_TargetName_Success() + public async Task NegotiateStream_StreamToStream_Authentication_TargetName_Success() { string targetName = "testTargetName"; @@ -93,8 +91,7 @@ namespace System.Net.Security.Tests auth[0] = AuthenticateAsClientAsync(client, CredentialCache.DefaultNetworkCredentials, targetName); auth[1] = AuthenticateAsServerAsync(server); - bool finished = Task.WaitAll(auth, TestConfiguration.PassingTestTimeoutMilliseconds); - Assert.True(finished, "Handshake completed in the allotted time"); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(auth); // Expected Client property values: Assert.True(client.IsAuthenticated); @@ -130,7 +127,7 @@ namespace System.Net.Security.Tests [Fact] [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, ".NET Core difference in behavior: https://github.com/dotnet/corefx/issues/5241")] - public void NegotiateStream_StreamToStream_Authentication_EmptyCredentials_Fails() + public async Task NegotiateStream_StreamToStream_Authentication_EmptyCredentials_Fails() { string targetName = "testTargetName"; @@ -155,8 +152,7 @@ namespace System.Net.Security.Tests auth[0] = AuthenticateAsClientAsync(client, emptyNetworkCredential, targetName); auth[1] = AuthenticateAsServerAsync(server); - bool finished = Task.WaitAll(auth, TestConfiguration.PassingTestTimeoutMilliseconds); - Assert.True(finished, "Handshake completed in the allotted time"); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(auth); // Expected Client property values: Assert.True(client.IsAuthenticated); @@ -193,7 +189,7 @@ namespace System.Net.Security.Tests } [Fact] - public void NegotiateStream_StreamToStream_Successive_ClientWrite_Sync_Success() + public async Task NegotiateStream_StreamToStream_Successive_ClientWrite_Sync_Success() { byte[] recvBuf = new byte[_sampleMsg.Length]; VirtualNetwork network = new VirtualNetwork(); @@ -210,8 +206,7 @@ namespace System.Net.Security.Tests auth[0] = AuthenticateAsClientAsync(client, CredentialCache.DefaultNetworkCredentials, string.Empty); auth[1] = AuthenticateAsServerAsync(server); - bool finished = Task.WaitAll(auth, TestConfiguration.PassingTestTimeoutMilliseconds); - Assert.True(finished, "Handshake completed in the allotted time"); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(auth); client.Write(_sampleMsg, 0, _sampleMsg.Length); server.Read(recvBuf, 0, _sampleMsg.Length); @@ -226,7 +221,7 @@ namespace System.Net.Security.Tests } [Fact] - public void NegotiateStream_StreamToStream_Successive_ClientWrite_Async_Success() + public async Task NegotiateStream_StreamToStream_Successive_ClientWrite_Async_Success() { byte[] recvBuf = new byte[_sampleMsg.Length]; VirtualNetwork network = new VirtualNetwork(); @@ -243,19 +238,16 @@ namespace System.Net.Security.Tests auth[0] = AuthenticateAsClientAsync(client, CredentialCache.DefaultNetworkCredentials, string.Empty); auth[1] = AuthenticateAsServerAsync(server); - bool finished = Task.WaitAll(auth, TestConfiguration.PassingTestTimeoutMilliseconds); - Assert.True(finished, "Handshake completed in the allotted time"); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(auth); auth[0] = client.WriteAsync(_sampleMsg, 0, _sampleMsg.Length); auth[1] = server.ReadAsync(recvBuf, 0, _sampleMsg.Length); - finished = Task.WaitAll(auth, TestConfiguration.PassingTestTimeoutMilliseconds); - Assert.True(finished, "Send/receive completed in the allotted time"); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(auth); Assert.True(_sampleMsg.SequenceEqual(recvBuf)); auth[0] = client.WriteAsync(_sampleMsg, 0, _sampleMsg.Length); auth[1] = server.ReadAsync(recvBuf, 0, _sampleMsg.Length); - finished = Task.WaitAll(auth, TestConfiguration.PassingTestTimeoutMilliseconds); - Assert.True(finished, "Send/receive completed in the allotted time"); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(auth); Assert.True(_sampleMsg.SequenceEqual(recvBuf)); } } diff --git a/external/corefx/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamTestForUnix.cs b/external/corefx/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamTest.Linux.cs similarity index 88% rename from external/corefx/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamTestForUnix.cs rename to external/corefx/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamTest.Linux.cs index c97cc82193..ec6ff21444 100644 --- a/external/corefx/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamTestForUnix.cs +++ b/external/corefx/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamTest.Linux.cs @@ -27,7 +27,7 @@ namespace System.Net.Security.Tests private const string ScriptUninstallArgs = "--uninstall --yes"; private const string ScriptInstallArgs = "--password {0} --yes"; private const int InstalledButUnconfiguredExitCode = 2; - private readonly bool _isKrbPreInstalled ; + private readonly bool _isKrbPreInstalled; public readonly string password; private const string NtlmUserFile = "NTLM_USER_FILE"; private readonly bool _successfulSetup = true; @@ -114,7 +114,6 @@ namespace System.Net.Security.Tests } } - [PlatformSpecific(TestPlatforms.Linux)] [Trait(XunitConstants.Category, XunitConstants.RequiresElevation)] public class NegotiateStreamTest : IDisposable, IClassFixture { @@ -154,7 +153,7 @@ namespace System.Net.Security.Tests [Fact, OuterLoop] [PlatformSpecific(TestPlatforms.Linux)] - public void NegotiateStream_StreamToStream_KerberosAuthentication_Success() + public async Task NegotiateStream_StreamToStream_KerberosAuthentication_Success() { if (!_isKrbAvailable) { @@ -179,15 +178,14 @@ namespace System.Net.Security.Tests server.AuthenticateAsServerAsync() }; - bool finished = Task.WaitAll(auth, TestConfiguration.PassingTestTimeoutMilliseconds); - Assert.True(finished, "Handshake completed in the allotted time"); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(auth); AssertClientPropertiesForTarget(client, target); } } [Fact, OuterLoop] [PlatformSpecific(TestPlatforms.Linux)] - public void NegotiateStream_StreamToStream_AuthToHttpTarget_Success() + public async Task NegotiateStream_StreamToStream_AuthToHttpTarget_Success() { if (!_isKrbAvailable) { @@ -211,8 +209,7 @@ namespace System.Net.Security.Tests server.AuthenticateAsServerAsync() }; - bool finished = Task.WaitAll(auth, TestConfiguration.PassingTestTimeoutMilliseconds); - Assert.True(finished, "Handshake completed in the allotted time"); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(auth); AssertClientPropertiesForTarget(client, TestConfiguration.HttpTarget); } @@ -220,7 +217,7 @@ namespace System.Net.Security.Tests [Fact, OuterLoop] [PlatformSpecific(TestPlatforms.Linux)] - public void NegotiateStream_StreamToStream_KerberosAuthWithoutRealm_Success() + public async Task NegotiateStream_StreamToStream_KerberosAuthWithoutRealm_Success() { if (!_isKrbAvailable) { @@ -243,8 +240,7 @@ namespace System.Net.Security.Tests server.AuthenticateAsServerAsync() }; - bool finished = Task.WaitAll(auth, TestConfiguration.PassingTestTimeoutMilliseconds); - Assert.True(finished, "Handshake completed in the allotted time"); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(auth); AssertClientPropertiesForTarget(client, TestConfiguration.HostTarget); } @@ -252,7 +248,7 @@ namespace System.Net.Security.Tests [Fact, OuterLoop] [PlatformSpecific(TestPlatforms.Linux)] - public void NegotiateStream_StreamToStream_KerberosAuthDefaultCredentials_Success() + public async Task NegotiateStream_StreamToStream_KerberosAuthDefaultCredentials_Success() { if (!_isKrbAvailable) { @@ -278,8 +274,7 @@ namespace System.Net.Security.Tests server.AuthenticateAsServerAsync() }; - bool finished = Task.WaitAll(auth, TestConfiguration.PassingTestTimeoutMilliseconds); - Assert.True(finished, "Handshake completed in the allotted time"); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(auth); AssertClientPropertiesForTarget(client, target); } @@ -287,7 +282,7 @@ namespace System.Net.Security.Tests [Fact, OuterLoop] [PlatformSpecific(TestPlatforms.Linux)] - public void NegotiateStream_EchoServer_ClientWriteRead_Successive_Sync_Success() + public async Task NegotiateStream_EchoServer_ClientWriteRead_Successive_Sync_Success() { if (!_isKrbAvailable) { @@ -313,8 +308,7 @@ namespace System.Net.Security.Tests client.AuthenticateAsClientAsync(credential, target), server.AuthenticateAsServerAsync() }; - bool finished = Task.WaitAll(auth, TestConfiguration.PassingTestTimeoutMilliseconds); - Assert.True(finished, "Handshake completed in the allotted time"); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(auth); Task svrMsgTask = server.PollMessageAsync(2); @@ -324,14 +318,13 @@ namespace System.Net.Security.Tests client.Read(secondRecvBuffer, 0, secondRecvBuffer.Length); Assert.True(_firstMessage.SequenceEqual(firstRecvBuffer), "first message received is as expected"); Assert.True(_secondMessage.SequenceEqual(secondRecvBuffer), "second message received is as expected"); - finished = svrMsgTask.Wait(TestConfiguration.PassingTestTimeoutMilliseconds); - Assert.True(finished, "Message roundtrip completed in the allotted time"); + await svrMsgTask.TimeoutAfter(TestConfiguration.PassingTestTimeoutMilliseconds); } } [Fact, OuterLoop] [PlatformSpecific(TestPlatforms.Linux)] - public void NegotiateStream_EchoServer_ClientWriteRead_Successive_Async_Success() + public async Task NegotiateStream_EchoServer_ClientWriteRead_Successive_Async_Success() { if (!_isKrbAvailable) { @@ -357,8 +350,8 @@ namespace System.Net.Security.Tests client.AuthenticateAsClientAsync(credential, target), server.AuthenticateAsServerAsync() }; - bool finished = Task.WaitAll(auth, TestConfiguration.PassingTestTimeoutMilliseconds); - Assert.True(finished, "Handshake completed in the allotted time"); + + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(auth); Task serverTask = server.PollMessageAsync(2); Task[] msgTasks = new Task[] { @@ -368,8 +361,9 @@ namespace System.Net.Security.Tests ReadAllAsync(client, secondRecvBuffer, 0, secondRecvBuffer.Length)).Unwrap(), serverTask }; - finished = Task.WaitAll(msgTasks, TestConfiguration.PassingTestTimeoutMilliseconds); - Assert.True(finished, "Messages sent and received in the allotted time"); + + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(msgTasks); + Assert.True(_firstMessage.SequenceEqual(firstRecvBuffer), "The first message received is as expected"); Assert.True(_secondMessage.SequenceEqual(secondRecvBuffer), "The second message received is as expected"); } @@ -431,7 +425,7 @@ namespace System.Net.Security.Tests string user = string.Format("{0}@{1}", TestConfiguration.KerberosUser, TestConfiguration.Realm); string target = string.Format("{0}@{1}", TestConfiguration.HostTarget, TestConfiguration.Realm); NetworkCredential credential = new NetworkCredential(user.Substring(1), _fixture.password); - Assert.ThrowsAsync(() =>client.AuthenticateAsClientAsync(credential, target)); + Assert.ThrowsAsync(() => client.AuthenticateAsClientAsync(credential, target)); Assert.ThrowsAsync(() => server.AuthenticateAsServerAsync()); } } @@ -490,30 +484,30 @@ namespace System.Net.Security.Tests public static IEnumerable ValidNtlmCredentials() { - - yield return new object[]{new NetworkCredential(TestConfiguration.NtlmUser, _ntlmPassword, TestConfiguration.Domain)}; - yield return new object[] {new NetworkCredential(TestConfiguration.NtlmUser, _ntlmPassword)}; - yield return new object[] - { + + yield return new object[] { new NetworkCredential(TestConfiguration.NtlmUser, _ntlmPassword, TestConfiguration.Domain) }; + yield return new object[] { new NetworkCredential(TestConfiguration.NtlmUser, _ntlmPassword) }; + yield return new object[] + { new NetworkCredential($@"{TestConfiguration.Domain}\{TestConfiguration.NtlmUser}", _ntlmPassword) - }; - yield return new object[] - { + }; + yield return new object[] + { new NetworkCredential($"{TestConfiguration.NtlmUser}@{TestConfiguration.Domain}", _ntlmPassword) - }; + }; } public static IEnumerable InvalidNtlmCredentials { get { - yield return new object[] { new NetworkCredential(TestConfiguration.NtlmUser, _ntlmPassword, TestConfiguration.Domain.Substring(1))}; + yield return new object[] { new NetworkCredential(TestConfiguration.NtlmUser, _ntlmPassword, TestConfiguration.Domain.Substring(1)) }; yield return new object[] { new NetworkCredential(TestConfiguration.NtlmUser.Substring(1), _ntlmPassword, TestConfiguration.Domain) }; yield return new object[] { new NetworkCredential(TestConfiguration.NtlmUser, _ntlmPassword.Substring(1), TestConfiguration.Domain) }; - yield return new object[] { new NetworkCredential($@"{TestConfiguration.Domain}\{TestConfiguration.NtlmUser}", _ntlmPassword, TestConfiguration.Domain.Substring(1))}; - yield return new object[] { new NetworkCredential($"{TestConfiguration.NtlmUser}@{TestConfiguration.Domain.Substring(1)}", _ntlmPassword)}; - yield return new object[] { new NetworkCredential($@"{TestConfiguration.Domain.Substring(1)}\{TestConfiguration.NtlmUser}", _ntlmPassword, TestConfiguration.Domain)}; - yield return new object[] { new NetworkCredential(TestConfiguration.NtlmUser, _ntlmPassword, TestConfiguration.Realm)}; + yield return new object[] { new NetworkCredential($@"{TestConfiguration.Domain}\{TestConfiguration.NtlmUser}", _ntlmPassword, TestConfiguration.Domain.Substring(1)) }; + yield return new object[] { new NetworkCredential($"{TestConfiguration.NtlmUser}@{TestConfiguration.Domain.Substring(1)}", _ntlmPassword) }; + yield return new object[] { new NetworkCredential($@"{TestConfiguration.Domain.Substring(1)}\{TestConfiguration.NtlmUser}", _ntlmPassword, TestConfiguration.Domain) }; + yield return new object[] { new NetworkCredential(TestConfiguration.NtlmUser, _ntlmPassword, TestConfiguration.Realm) }; } } @@ -522,7 +516,7 @@ namespace System.Net.Security.Tests [Theory, OuterLoop] [MemberData(nameof(ValidNtlmCredentials))] [PlatformSpecific(TestPlatforms.Linux)] - public void NegotiateStream_StreamToStream_NtlmAuthentication_ValidCredentials_Success(NetworkCredential credential) + public async Task NegotiateStream_StreamToStream_NtlmAuthentication_ValidCredentials_Success(NetworkCredential credential) { if (!_isNtlmAvailable) { @@ -545,8 +539,7 @@ namespace System.Net.Security.Tests server.AuthenticateAsServerAsync() }; - bool finished = Task.WaitAll(auth, TestConfiguration.PassingTestTimeoutMilliseconds); - Assert.True(finished, "Handshake completed in the allotted time"); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(auth); // Expected Client property values: Assert.True(client.IsAuthenticated, "client.IsAuthenticated"); @@ -564,10 +557,10 @@ namespace System.Net.Security.Tests } } - + [Fact, OuterLoop] [PlatformSpecific(TestPlatforms.Linux)] - public void NegotiateStream_StreamToStream_NtlmAuthentication_Fallback_Success() + public async Task NegotiateStream_StreamToStream_NtlmAuthentication_Fallback_Success() { if (!_isNtlmAvailable) { @@ -592,8 +585,7 @@ namespace System.Net.Security.Tests server.AuthenticateAsServerAsync() }; - bool finished = Task.WaitAll(auth, TestConfiguration.PassingTestTimeoutMilliseconds); - Assert.True(finished, "Handshake completed in the allotted time"); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(auth); // Expected Client property values: Assert.True(client.IsAuthenticated, "client.IsAuthenticated"); @@ -612,7 +604,7 @@ namespace System.Net.Security.Tests [Fact, OuterLoop] [PlatformSpecific(TestPlatforms.Linux)] - public void NegotiateStream_StreamToStream_NtlmAuthentication_KerberosCreds_Success() + public async Task NegotiateStream_StreamToStream_NtlmAuthentication_KerberosCreds_Success() { if (!_isNtlmAvailable) { @@ -636,8 +628,7 @@ namespace System.Net.Security.Tests client.AuthenticateAsClientAsync(credential, TestConfiguration.HttpTarget, ProtectionLevel.None, TokenImpersonationLevel.Identification), server.AuthenticateAsServerAsync() }; - bool finished = Task.WaitAll(auth, TestConfiguration.PassingTestTimeoutMilliseconds); - Assert.True(finished, "Handshake completed in the allotted time"); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(auth); // Expected Client property values: Assert.True(client.IsAuthenticated, "client.IsAuthenticated"); @@ -657,7 +648,7 @@ namespace System.Net.Security.Tests [Fact, OuterLoop] [PlatformSpecific(TestPlatforms.Linux)] - public void NegotiateStream_EchoServer_NTLM_ClientWriteRead_Successive_Sync_Success() + public async Task NegotiateStream_EchoServer_NTLM_ClientWriteRead_Successive_Sync_Success() { if (!_isNtlmAvailable) { @@ -684,8 +675,7 @@ namespace System.Net.Security.Tests client.AuthenticateAsClientAsync(credential, TestConfiguration.HostTarget), server.AuthenticateAsServerAsync() }; - bool finished = Task.WaitAll(auth, TestConfiguration.PassingTestTimeoutMilliseconds); - Assert.True(finished, "Handshake completed in the allotted time"); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(auth); //clearing message queue byte[] junkBytes = new byte[5]; @@ -698,14 +688,13 @@ namespace System.Net.Security.Tests client.Read(secondRecvBuffer, 0, secondRecvBuffer.Length); Assert.True(_firstMessage.SequenceEqual(firstRecvBuffer), "first message received is as expected"); Assert.True(_secondMessage.SequenceEqual(secondRecvBuffer), "second message received is as expected"); - finished = svrMsgTask.Wait(TestConfiguration.PassingTestTimeoutMilliseconds); - Assert.True(finished, "Message roundtrip completed in the allotted time"); + await svrMsgTask.TimeoutAfter(TestConfiguration.PassingTestTimeoutMilliseconds); } } [Fact, OuterLoop] [PlatformSpecific(TestPlatforms.Linux)] - public void NegotiateStream_EchoServer_NTLM_ClientWriteRead_Successive_Async_Success() + public async Task NegotiateStream_EchoServer_NTLM_ClientWriteRead_Successive_Async_Success() { if (!_isNtlmAvailable) { @@ -733,8 +722,7 @@ namespace System.Net.Security.Tests server.AuthenticateAsServerAsync() }; - bool finished = Task.WaitAll(auth, TestConfiguration.PassingTestTimeoutMilliseconds); - Assert.True(finished, "Handshake completed in the allotted time"); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(auth); //clearing message queue byte[] junkBytes = new byte[5]; @@ -749,8 +737,7 @@ namespace System.Net.Security.Tests serverTask }; - finished = Task.WaitAll(msgTasks, TestConfiguration.PassingTestTimeoutMilliseconds); - Assert.True(finished, "Messages sent and received in the allotted time"); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(msgTasks); Assert.True(_firstMessage.SequenceEqual(firstRecvBuffer), "The first message received is as expected"); Assert.True(_secondMessage.SequenceEqual(secondRecvBuffer), "The second message received is as expected"); } diff --git a/external/corefx/src/System.Net.Security/tests/FunctionalTests/SslStreamAllowRenegotiationTests.cs b/external/corefx/src/System.Net.Security/tests/FunctionalTests/SslStreamAllowRenegotiationTests.cs new file mode 100644 index 0000000000..a570d3c75c --- /dev/null +++ b/external/corefx/src/System.Net.Security/tests/FunctionalTests/SslStreamAllowRenegotiationTests.cs @@ -0,0 +1,98 @@ +// 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.IO; +using System.Net.Sockets; +using System.Net.Test.Common; +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +using Xunit; + +namespace System.Net.Security.Tests +{ + using Configuration = System.Net.Test.Common.Configuration; + + public class SslStreamAllowRenegotiationTests + { + [Fact] + [OuterLoop] // Test hits external azure server. + public async Task SslStream_AllowRenegotiation_True_Succeeds() + { + Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + await s.ConnectAsync(Configuration.Security.TlsRenegotiationServer, 443); + using (NetworkStream ns = new NetworkStream(s)) + using (SslStream ssl = new SslStream(ns, true)) + { + X509CertificateCollection certBundle = new X509CertificateCollection(); + certBundle.Add(Configuration.Certificates.GetClientCertificate()); + + SslClientAuthenticationOptions options = new SslClientAuthenticationOptions + { + TargetHost = Configuration.Security.TlsRenegotiationServer, + ClientCertificates = certBundle, + EnabledSslProtocols = SslProtocols.Tls12, + CertificateRevocationCheckMode = X509RevocationMode.NoCheck, + AllowRenegotiation = true + }; + + // Perform handshake to establish secure connection. + await ssl.AuthenticateAsClientAsync(options, CancellationToken.None); + Assert.True(ssl.IsAuthenticated); + Assert.True(ssl.IsEncrypted); + + // Issue request that triggers regotiation from server. + byte[] message = Encoding.UTF8.GetBytes("GET /EchoClientCertificate.ashx HTTP/1.1\r\nHost: corefx-net-tls.azurewebsites.net\r\n\r\n"); + await ssl.WriteAsync(message, 0, message.Length); + + // Initiate Read operation, that results in starting renegotiation as per server response to the above request. + int bytesRead = await ssl.ReadAsync(message, 0, message.Length); + + // There's no good way to ensure renegotiation happened in the test. + // Under the debugger, we can see this test hits the renegotiation codepath. + Assert.InRange(bytesRead, 1, message.Length); + Assert.Contains("HTTP/1.1 200 OK", Encoding.UTF8.GetString(message)); + } + } + + [Fact] + [OuterLoop] // Test hits external azure server. + public async Task SslStream_AllowRenegotiation_False_Throws() + { + Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + await s.ConnectAsync(Configuration.Security.TlsRenegotiationServer, 443); + using (NetworkStream ns = new NetworkStream(s)) + using (SslStream ssl = new SslStream(ns, true)) + { + X509CertificateCollection certBundle = new X509CertificateCollection(); + certBundle.Add(Configuration.Certificates.GetClientCertificate()); + + SslClientAuthenticationOptions options = new SslClientAuthenticationOptions + { + TargetHost = Configuration.Security.TlsRenegotiationServer, + ClientCertificates = certBundle, + EnabledSslProtocols = SslProtocols.Tls12, + CertificateRevocationCheckMode = X509RevocationMode.NoCheck, + AllowRenegotiation = false + }; + + // Perform handshake to establish secure connection. + await ssl.AuthenticateAsClientAsync(options, CancellationToken.None); + Assert.True(ssl.IsAuthenticated); + Assert.True(ssl.IsEncrypted); + + // Issue request that triggers regotiation from server. + byte[] message = Encoding.UTF8.GetBytes("GET /EchoClientCertificate.ashx HTTP/1.1\r\nHost: corefx-net-tls.azurewebsites.net\r\n\r\n"); + await ssl.WriteAsync(message, 0, message.Length); + + // Initiate Read operation, that results in starting renegotiation as per server response to the above request. + // This will throw IOException, since renegotiation is disabled on client side. + await Assert.ThrowsAsync(() => ssl.ReadAsync(message, 0, message.Length)); + } + } + } +} diff --git a/external/corefx/src/System.Net.Security/tests/FunctionalTests/SslStreamAlpnTests.cs b/external/corefx/src/System.Net.Security/tests/FunctionalTests/SslStreamAlpnTests.cs new file mode 100644 index 0000000000..ae468b3d29 --- /dev/null +++ b/external/corefx/src/System.Net.Security/tests/FunctionalTests/SslStreamAlpnTests.cs @@ -0,0 +1,211 @@ +// 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.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Sockets; +using System.Net.Test.Common; +using System.Runtime.InteropServices; +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +using Xunit; + +namespace System.Net.Security.Tests +{ + using Configuration = System.Net.Test.Common.Configuration; + + public class SslStreamAlpnTests + { + // Windows - Schannel supports alpn from win8 and higher. + // Linux - OpenSsl supports alpn from openssl 1.0.2 and higher. + // OSX - SecureTransport doesn't expose alpn APIs. + private static bool BackendSupportsAlpn => (PlatformDetection.IsWindows && !PlatformDetection.IsWindows7) || + (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && + (PlatformDetection.OpenSslVersion.Major >= 1 && (PlatformDetection.OpenSslVersion.Minor >= 1 || PlatformDetection.OpenSslVersion.Build >= 2))); + + private async Task DoHandshakeWithOptions(SslStream clientSslStream, SslStream serverSslStream, SslClientAuthenticationOptions clientOptions, SslServerAuthenticationOptions serverOptions) + { + using (X509Certificate2 certificate = Configuration.Certificates.GetServerCertificate()) + { + clientOptions.RemoteCertificateValidationCallback = AllowAnyServerCertificate; + clientOptions.TargetHost = certificate.GetNameInfo(X509NameType.SimpleName, false); + serverOptions.ServerCertificate = certificate; + + Task t1 = clientSslStream.AuthenticateAsClientAsync(clientOptions, CancellationToken.None); + Task t2 = serverSslStream.AuthenticateAsServerAsync(serverOptions, CancellationToken.None); + + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(t1, t2); + } + } + + protected bool AllowAnyServerCertificate( + object sender, + X509Certificate certificate, + X509Chain chain, + SslPolicyErrors sslPolicyErrors) + { + SslPolicyErrors expectedSslPolicyErrors = SslPolicyErrors.None; + + if (!Capability.IsTrustedRootCertificateInstalled()) + { + expectedSslPolicyErrors = SslPolicyErrors.RemoteCertificateChainErrors; + } + + Assert.Equal(expectedSslPolicyErrors, sslPolicyErrors); + + if (sslPolicyErrors == expectedSslPolicyErrors) + { + return true; + } + else + { + return false; + } + } + + [Fact] + public async Task SslStream_StreamToStream_DuplicateOptions_Throws() + { + RemoteCertificateValidationCallback rCallback = (sender, certificate, chain, errors) => { return true; }; + LocalCertificateSelectionCallback lCallback = (sender, host, localCertificates, remoteCertificate, issuers) => { return null; }; + + VirtualNetwork network = new VirtualNetwork(); + using (var clientStream = new VirtualNetworkStream(network, false)) + using (var serverStream = new VirtualNetworkStream(network, true)) + using (var client = new SslStream(clientStream, false, rCallback, lCallback, EncryptionPolicy.RequireEncryption)) + using (var server = new SslStream(serverStream, false, rCallback)) + using (X509Certificate2 certificate = Configuration.Certificates.GetServerCertificate()) + { + SslClientAuthenticationOptions clientOptions = new SslClientAuthenticationOptions(); + clientOptions.RemoteCertificateValidationCallback = AllowAnyServerCertificate; + clientOptions.TargetHost = certificate.GetNameInfo(X509NameType.SimpleName, false); + + SslServerAuthenticationOptions serverOptions = new SslServerAuthenticationOptions(); + serverOptions.ServerCertificate = certificate; + serverOptions.RemoteCertificateValidationCallback = AllowAnyServerCertificate; + + Task t1 = Assert.ThrowsAsync(() => client.AuthenticateAsClientAsync(clientOptions, CancellationToken.None)); + Task t2 = Assert.ThrowsAsync(() => server.AuthenticateAsServerAsync(serverOptions, CancellationToken.None)); + + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(t1, t2); + } + } + + [Theory] + [MemberData(nameof(Alpn_TestData))] + public async Task SslStream_StreamToStream_Alpn_Success(List clientProtocols, List serverProtocols, SslApplicationProtocol expected) + { + VirtualNetwork network = new VirtualNetwork(); + using (var clientStream = new VirtualNetworkStream(network, false)) + using (var serverStream = new VirtualNetworkStream(network, true)) + using (var client = new SslStream(clientStream, false)) + using (var server = new SslStream(serverStream, false)) + { + SslClientAuthenticationOptions clientOptions = new SslClientAuthenticationOptions + { + ApplicationProtocols = clientProtocols, + }; + + SslServerAuthenticationOptions serverOptions = new SslServerAuthenticationOptions + { + ApplicationProtocols = serverProtocols, + }; + + await DoHandshakeWithOptions(client, server, clientOptions, serverOptions); + + Assert.Equal(expected, client.NegotiatedApplicationProtocol); + Assert.Equal(expected, server.NegotiatedApplicationProtocol); + } + } + + [Fact] + public async Task SslStream_StreamToStream_Alpn_NonMatchingProtocols_Fail() + { + TcpListener listener = new TcpListener(IPAddress.Loopback, 0); + try + { + listener.Start(); + using (TcpClient client = new TcpClient()) + { + Task serverTask = listener.AcceptTcpClientAsync(); + await client.ConnectAsync(IPAddress.Loopback, ((IPEndPoint)listener.LocalEndpoint).Port); + + using (TcpClient server = await serverTask) + using (SslStream serverStream = new SslStream(server.GetStream(), leaveInnerStreamOpen: false)) + using (SslStream clientStream = new SslStream(client.GetStream(), leaveInnerStreamOpen: false)) + using (X509Certificate2 certificate = Configuration.Certificates.GetServerCertificate()) + { + SslServerAuthenticationOptions serverOptions = new SslServerAuthenticationOptions + { + ApplicationProtocols = new List { SslApplicationProtocol.Http2 }, + ServerCertificate = certificate, + }; + SslClientAuthenticationOptions clientOptions = new SslClientAuthenticationOptions + { + ApplicationProtocols = new List { SslApplicationProtocol.Http11 }, + RemoteCertificateValidationCallback = AllowAnyServerCertificate, + TargetHost = certificate.GetNameInfo(X509NameType.SimpleName, false), + }; + + // Test alpn failure only on platforms that supports ALPN. + if (BackendSupportsAlpn) + { + Task t1 = Assert.ThrowsAsync(() => clientStream.AuthenticateAsClientAsync(clientOptions, CancellationToken.None)); + try + { + await serverStream.AuthenticateAsServerAsync(serverOptions, CancellationToken.None); + Assert.True(false, "AuthenticationException was not thrown."); + } + catch (AuthenticationException) { server.Dispose(); } + + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(t1); + } + else + { + Task t1 = clientStream.AuthenticateAsClientAsync(clientOptions, CancellationToken.None); + Task t2 = serverStream.AuthenticateAsServerAsync(serverOptions, CancellationToken.None); + + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(t1, t2); + + Assert.Equal(default(SslApplicationProtocol), clientStream.NegotiatedApplicationProtocol); + Assert.Equal(default(SslApplicationProtocol), serverStream.NegotiatedApplicationProtocol); + } + } + } + } + finally + { + listener.Stop(); + } + } + + internal static IEnumerable Alpn_TestData() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + yield return new object[] { new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, new List { SslApplicationProtocol.Http2 }, null }; + yield return new object[] { new List { SslApplicationProtocol.Http11 }, new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, null }; + yield return new object[] { new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, null }; + yield return new object[] { null, new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, null }; + yield return new object[] { new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, null, null }; + yield return new object[] { new List { SslApplicationProtocol.Http11 }, new List { SslApplicationProtocol.Http2 }, null }; + yield return new object[] { null, null, null }; + } + else + { + yield return new object[] { new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, new List { SslApplicationProtocol.Http2 }, BackendSupportsAlpn ? SslApplicationProtocol.Http2 : default }; + yield return new object[] { new List { SslApplicationProtocol.Http11 }, new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, BackendSupportsAlpn ? SslApplicationProtocol.Http11 : default }; + yield return new object[] { new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, BackendSupportsAlpn ? SslApplicationProtocol.Http11 : default }; + yield return new object[] { null, new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, default(SslApplicationProtocol) }; + yield return new object[] { new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, null, default(SslApplicationProtocol) }; + yield return new object[] { null, null, default(SslApplicationProtocol) }; + } + } + } +} diff --git a/external/corefx/src/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs b/external/corefx/src/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs index 44e9565bff..2d02da12ee 100644 --- a/external/corefx/src/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs +++ b/external/corefx/src/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs @@ -6,6 +6,8 @@ using System.Net.Sockets; using System.Net.Test.Common; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading; using System.Threading.Tasks; using Xunit; @@ -66,16 +68,46 @@ namespace System.Net.Security.Tests byte[] readBuffer = new byte[256]; Task readTask = serverStream.ReadAsync(readBuffer, 0, readBuffer.Length); - bool result = Task.WaitAll(new[] { writeTask, readTask }, - TestConfiguration.PassingTestTimeoutMilliseconds); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(writeTask, readTask); - Assert.True(result, "WriteAsync timed-out."); + Assert.InRange(readTask.Result, 1, 256); } } listener.Stop(); } + [Fact] + [OuterLoop] // Test hits external azure server. + public async Task SslStream_NetworkStream_Renegotiation_Succeeds() + { + Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + await s.ConnectAsync(Configuration.Security.TlsRenegotiationServer, 443); + using (NetworkStream ns = new NetworkStream(s)) + using (SslStream ssl = new SslStream(ns, true)) + { + X509CertificateCollection certBundle = new X509CertificateCollection(); + certBundle.Add(Configuration.Certificates.GetClientCertificate()); + + // Perform handshake to establish secure connection. + await ssl.AuthenticateAsClientAsync(Configuration.Security.TlsRenegotiationServer, certBundle, SslProtocols.Tls12, false); + Assert.True(ssl.IsAuthenticated); + Assert.True(ssl.IsEncrypted); + + // Issue request that triggers regotiation from server. + byte[] message = Encoding.UTF8.GetBytes("GET /EchoClientCertificate.ashx HTTP/1.1\r\nHost: corefx-net-tls.azurewebsites.net\r\n\r\n"); + await ssl.WriteAsync(message, 0, message.Length); + + // Initiate Read operation, that results in starting renegotiation as per server response to the above request. + int bytesRead = await ssl.ReadAsync(message, 0, message.Length); + + // There's no good way to ensure renegotiation happened in the test. + // Under the debugger, we can see this test hits the renegotiation codepath. + Assert.InRange(bytesRead, 1, message.Length); + Assert.Contains("HTTP/1.1 200 OK", Encoding.UTF8.GetString(message)); + } + } + private static bool ValidateServerCertificate( object sender, X509Certificate retrievedServerPublicCertificate, diff --git a/external/corefx/src/System.Net.Security/tests/FunctionalTests/SslStreamStreamToStreamTest.cs b/external/corefx/src/System.Net.Security/tests/FunctionalTests/SslStreamStreamToStreamTest.cs index a0277c5bfa..b02f57468e 100644 --- a/external/corefx/src/System.Net.Security/tests/FunctionalTests/SslStreamStreamToStreamTest.cs +++ b/external/corefx/src/System.Net.Security/tests/FunctionalTests/SslStreamStreamToStreamTest.cs @@ -20,10 +20,10 @@ namespace System.Net.Security.Tests { private readonly byte[] _sampleMsg = Encoding.UTF8.GetBytes("Sample Test Message"); - protected abstract bool DoHandshake(SslStream clientSslStream, SslStream serverSslStream); + protected abstract Task DoHandshake(SslStream clientSslStream, SslStream serverSslStream); [Fact] - public void SslStream_StreamToStream_Authentication_Success() + public async Task SslStream_StreamToStream_Authentication_Success() { VirtualNetwork network = new VirtualNetwork(); @@ -33,12 +33,14 @@ namespace System.Net.Security.Tests using (var server = new SslStream(serverStream)) using (X509Certificate2 certificate = Configuration.Certificates.GetServerCertificate()) { - Assert.True(DoHandshake(client, server), "Handshake completed in the allotted time"); + await DoHandshake(client, server); + Assert.True(client.IsAuthenticated); + Assert.True(server.IsAuthenticated); } } [Fact] - public void SslStream_StreamToStream_Authentication_IncorrectServerName_Fail() + public async Task SslStream_StreamToStream_Authentication_IncorrectServerName_Fail() { VirtualNetwork network = new VirtualNetwork(); @@ -48,21 +50,16 @@ namespace System.Net.Security.Tests using (var server = new SslStream(serverStream)) using (var certificate = Configuration.Certificates.GetServerCertificate()) { - Task[] auth = new Task[2]; - auth[0] = client.AuthenticateAsClientAsync("incorrectServer"); - auth[1] = server.AuthenticateAsServerAsync(certificate); + Task t1 = client.AuthenticateAsClientAsync("incorrectServer"); + Task t2 = server.AuthenticateAsServerAsync(certificate); - Assert.Throws(() => - { - auth[0].GetAwaiter().GetResult(); - }); - - auth[1].GetAwaiter().GetResult(); + await Assert.ThrowsAsync(() => t1); + await t2; } } [Fact] - public void SslStream_StreamToStream_Successive_ClientWrite_Sync_Success() + public async Task SslStream_StreamToStream_Successive_ClientWrite_Sync_Success() { byte[] recvBuf = new byte[_sampleMsg.Length]; VirtualNetwork network = new VirtualNetwork(); @@ -72,10 +69,8 @@ namespace System.Net.Security.Tests using (var clientSslStream = new SslStream(clientStream, false, AllowAnyServerCertificate)) using (var serverSslStream = new SslStream(serverStream)) { - bool result = DoHandshake(clientSslStream, serverSslStream); - - Assert.True(result, "Handshake completed."); - + await DoHandshake(clientSslStream, serverSslStream); + clientSslStream.Write(_sampleMsg); int bytesRead = 0; @@ -99,7 +94,7 @@ namespace System.Net.Security.Tests } [Fact] - public void SslStream_StreamToStream_Successive_ClientWrite_WithZeroBytes_Success() + public async Task SslStream_StreamToStream_Successive_ClientWrite_WithZeroBytes_Success() { byte[] recvBuf = new byte[_sampleMsg.Length]; VirtualNetwork network = new VirtualNetwork(); @@ -109,12 +104,10 @@ namespace System.Net.Security.Tests using (var clientSslStream = new SslStream(clientStream, false, AllowAnyServerCertificate)) using (var serverSslStream = new SslStream(serverStream)) { - bool result = DoHandshake(clientSslStream, serverSslStream); - - Assert.True(result, "Handshake completed."); - + await DoHandshake(clientSslStream, serverSslStream); + clientSslStream.Write(Array.Empty()); - clientSslStream.WriteAsync(Array.Empty(), 0, 0).Wait(); + await clientSslStream.WriteAsync(Array.Empty(), 0, 0); clientSslStream.Write(_sampleMsg); int bytesRead = 0; @@ -126,7 +119,7 @@ namespace System.Net.Security.Tests Assert.True(VerifyOutput(recvBuf, _sampleMsg), "verify first read data is as expected."); clientSslStream.Write(_sampleMsg); - clientSslStream.WriteAsync(Array.Empty(), 0, 0).Wait(); + await clientSslStream.WriteAsync(Array.Empty(), 0, 0); clientSslStream.Write(Array.Empty()); bytesRead = 0; @@ -141,16 +134,16 @@ namespace System.Net.Security.Tests [Theory] [InlineData(false)] [InlineData(true)] - public void SslStream_StreamToStream_LargeWrites_Sync_Success(bool randomizedData) + public async Task SslStream_StreamToStream_LargeWrites_Sync_Success(bool randomizedData) { VirtualNetwork network = new VirtualNetwork(); - using (var clientStream = new VirtualNetworkStream(network, isServer:false)) - using (var serverStream = new VirtualNetworkStream(network, isServer:true)) + using (var clientStream = new VirtualNetworkStream(network, isServer: false)) + using (var serverStream = new VirtualNetworkStream(network, isServer: true)) using (var clientSslStream = new SslStream(clientStream, false, AllowAnyServerCertificate)) using (var serverSslStream = new SslStream(serverStream)) { - Assert.True(DoHandshake(clientSslStream, serverSslStream), "Handshake complete"); + await DoHandshake(clientSslStream, serverSslStream); byte[] largeMsg = new byte[4096 * 5]; // length longer than max read chunk size (16K + headers) if (randomizedData) @@ -197,10 +190,8 @@ namespace System.Net.Security.Tests using (var clientSslStream = new SslStream(clientStream, false, AllowAnyServerCertificate)) using (var serverSslStream = new SslStream(serverStream)) { - bool result = DoHandshake(clientSslStream, serverSslStream); - - Assert.True(result, "Handshake completed."); - + await DoHandshake(clientSslStream, serverSslStream); + await clientSslStream.WriteAsync(_sampleMsg, 0, _sampleMsg.Length) .TimeoutAfter(TestConfiguration.PassingTestTimeoutMilliseconds); @@ -228,18 +219,17 @@ namespace System.Net.Security.Tests } [Fact] - public void SslStream_StreamToStream_Write_ReadByte_Success() + public async Task SslStream_StreamToStream_Write_ReadByte_Success() { VirtualNetwork network = new VirtualNetwork(); - using (var clientStream = new VirtualNetworkStream(network, isServer:false)) - using (var serverStream = new VirtualNetworkStream(network, isServer:true)) + using (var clientStream = new VirtualNetworkStream(network, isServer: false)) + using (var serverStream = new VirtualNetworkStream(network, isServer: true)) using (var clientSslStream = new SslStream(clientStream, false, AllowAnyServerCertificate)) using (var serverSslStream = new SslStream(serverStream)) { - bool result = DoHandshake(clientSslStream, serverSslStream); - Assert.True(result, "Handshake completed."); - + await DoHandshake(clientSslStream, serverSslStream); + for (int i = 0; i < 3; i++) { clientSslStream.Write(_sampleMsg); @@ -261,9 +251,8 @@ namespace System.Net.Security.Tests using (var clientSslStream = new SslStream(clientStream, false, AllowAnyServerCertificate)) using (var serverSslStream = new SslStream(serverStream)) { - bool result = DoHandshake(clientSslStream, serverSslStream); - Assert.True(result, "Handshake completed."); - + await DoHandshake(clientSslStream, serverSslStream); + for (int i = 0; i < 3; i++) { await clientSslStream.WriteAsync(_sampleMsg, 0, _sampleMsg.Length).ConfigureAwait(false); @@ -285,9 +274,8 @@ namespace System.Net.Security.Tests using (var clientSslStream = new SslStream(clientStream, false, AllowAnyServerCertificate)) using (var serverSslStream = new SslStream(serverStream)) { - bool result = DoHandshake(clientSslStream, serverSslStream); - Assert.True(result, "Handshake completed."); - + await DoHandshake(clientSslStream, serverSslStream); + var serverBuffer = new byte[1]; var tcs = new TaskCompletionSource(); serverStream.OnRead += (buffer, offset, count) => @@ -354,7 +342,7 @@ namespace System.Net.Security.Tests return expectedBuffer.SequenceEqual(actualBuffer); } - private bool AllowAnyServerCertificate( + protected bool AllowAnyServerCertificate( object sender, X509Certificate certificate, X509Chain chain, @@ -382,45 +370,39 @@ namespace System.Net.Security.Tests public sealed class SslStreamStreamToStreamTest_Async : SslStreamStreamToStreamTest { - protected override bool DoHandshake(SslStream clientSslStream, SslStream serverSslStream) + protected override async Task DoHandshake(SslStream clientSslStream, SslStream serverSslStream) { using (X509Certificate2 certificate = Configuration.Certificates.GetServerCertificate()) { Task t1 = clientSslStream.AuthenticateAsClientAsync(certificate.GetNameInfo(X509NameType.SimpleName, false)); Task t2 = serverSslStream.AuthenticateAsServerAsync(certificate); - return Task.WaitAll(new[] { t1, t2 }, TestConfiguration.PassingTestTimeoutMilliseconds); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(t1, t2); } } } public sealed class SslStreamStreamToStreamTest_BeginEnd : SslStreamStreamToStreamTest { - protected override bool DoHandshake(SslStream clientSslStream, SslStream serverSslStream) + protected override async Task DoHandshake(SslStream clientSslStream, SslStream serverSslStream) { using (X509Certificate2 certificate = Configuration.Certificates.GetServerCertificate()) { - IAsyncResult a1 = clientSslStream.BeginAuthenticateAsClient(certificate.GetNameInfo(X509NameType.SimpleName, false), null, null); - IAsyncResult a2 = serverSslStream.BeginAuthenticateAsServer(certificate, null, null); - if (WaitHandle.WaitAll(new[] { a1.AsyncWaitHandle, a2.AsyncWaitHandle }, TestConfiguration.PassingTestTimeoutMilliseconds)) - { - clientSslStream.EndAuthenticateAsClient(a1); - serverSslStream.EndAuthenticateAsServer(a2); - return true; - } - return false; + Task t1 = Task.Factory.FromAsync(clientSslStream.BeginAuthenticateAsClient(certificate.GetNameInfo(X509NameType.SimpleName, false), null, null), clientSslStream.EndAuthenticateAsClient); + Task t2 = Task.Factory.FromAsync(serverSslStream.BeginAuthenticateAsServer(certificate, null, null), serverSslStream.EndAuthenticateAsServer); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(t1, t2); } } } public sealed class SslStreamStreamToStreamTest_Sync : SslStreamStreamToStreamTest { - protected override bool DoHandshake(SslStream clientSslStream, SslStream serverSslStream) + protected override async Task DoHandshake(SslStream clientSslStream, SslStream serverSslStream) { using (X509Certificate2 certificate = Configuration.Certificates.GetServerCertificate()) { Task t1 = Task.Run(() => clientSslStream.AuthenticateAsClient(certificate.GetNameInfo(X509NameType.SimpleName, false))); Task t2 = Task.Run(() => serverSslStream.AuthenticateAsServer(certificate)); - return Task.WaitAll(new[] { t1, t2 }, TestConfiguration.PassingTestTimeoutMilliseconds); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(t1, t2); } } } diff --git a/external/corefx/src/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs b/external/corefx/src/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs index 95018f55fd..941d34fce4 100644 --- a/external/corefx/src/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs +++ b/external/corefx/src/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs @@ -2,6 +2,7 @@ // 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.Net.Http; using System.Net.Test.Common; using System.Security.Cryptography.X509Certificates; using System.Security.Authentication; @@ -28,11 +29,12 @@ namespace System.Net.Security.Tests _serverStream = new SslStream(serverNet, false, ServerCertCallback); } + public static bool IsNotWindows7 => !PlatformDetection.IsWindows7; + protected abstract Task AuthenticateClientAsync(string targetHost, X509CertificateCollection clientCertificates, bool checkCertificateRevocation, SslProtocols? protocols = null); protected abstract Task AuthenticateServerAsync(X509Certificate serverCertificate, bool clientCertificateRequired, bool checkCertificateRevocation, SslProtocols? protocols = null); - [ActiveIssue(7812, TestPlatforms.Windows)] - [Theory] + [ConditionalTheory(nameof(IsNotWindows7))] [InlineData(null, null)] [InlineData(SslProtocols.None, null)] [InlineData(null, SslProtocols.None)] @@ -45,6 +47,8 @@ namespace System.Net.Security.Tests [InlineData(null, SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12)] public async Task ClientAndServer_OneOrBothUseDefault_Ok(SslProtocols? clientProtocols, SslProtocols? serverProtocols) { + const int SEC_E_BUFFER_TOO_SMALL = unchecked((int)0x80090321); + X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate(); string serverHost = serverCertificate.GetNameInfo(X509NameType.SimpleName, false); var clientCertificates = new X509CertificateCollection(); @@ -53,14 +57,25 @@ namespace System.Net.Security.Tests var tasks = new Task[2]; tasks[0] = AuthenticateClientAsync(serverHost, clientCertificates, checkCertificateRevocation: false, protocols: clientProtocols); tasks[1] = AuthenticateServerAsync(serverCertificate, clientCertificateRequired: true, checkCertificateRevocation: false, protocols: serverProtocols); - await await Task.WhenAny(tasks); - await Task.WhenAll(tasks); - if (PlatformDetection.IsWindows && PlatformDetection.WindowsVersion >= 10) + try { - Assert.True(_clientStream.HashAlgorithm == HashAlgorithmType.Sha256 || - _clientStream.HashAlgorithm == HashAlgorithmType.Sha384 || - _clientStream.HashAlgorithm == HashAlgorithmType.Sha512); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(tasks); + if (PlatformDetection.IsWindows && PlatformDetection.WindowsVersion >= 10) + { + Assert.True( + (_clientStream.SslProtocol == SslProtocols.Tls11 && _clientStream.HashAlgorithm == HashAlgorithmType.Sha1) || + _clientStream.HashAlgorithm == HashAlgorithmType.Sha256 || + _clientStream.HashAlgorithm == HashAlgorithmType.Sha384 || + _clientStream.HashAlgorithm == HashAlgorithmType.Sha512); + } + } + catch (HttpRequestException e) when (e.InnerException?.GetType().Name == "WinHttpException" && + e.InnerException.HResult == SEC_E_BUFFER_TOO_SMALL && + !PlatformDetection.IsWindows10Version1607OrGreater) + { + // Testing on old Windows versions can hit https://github.com/dotnet/corefx/issues/7812 + // Ignore SEC_E_BUFFER_TOO_SMALL error on such cases. } } diff --git a/external/corefx/src/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj b/external/corefx/src/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj index cd80ae7090..87291673ac 100644 --- a/external/corefx/src/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj +++ b/external/corefx/src/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj @@ -7,7 +7,6 @@ true - @@ -20,13 +19,11 @@ - - @@ -40,12 +37,10 @@ - - Common\System\Net\Capability.Security.cs @@ -96,6 +91,8 @@ + + @@ -129,7 +126,7 @@ Always - + @@ -158,4 +155,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs b/external/corefx/src/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs index a986c11798..e8d6ed313d 100644 --- a/external/corefx/src/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs +++ b/external/corefx/src/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs @@ -7,6 +7,7 @@ using System.IO; using System.Runtime.InteropServices; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; using Xunit; @@ -29,6 +30,9 @@ namespace System.Net.Security.Tests public static bool SupportsNullEncryption { get { return s_supportsNullEncryption.Value; } } + public static Task WhenAllOrAnyFailedWithTimeout(params Task[] tasks) + => tasks.WhenAllOrAnyFailed(PassingTestTimeoutMilliseconds); + private static Lazy s_supportsNullEncryption = new Lazy(() => { // On Windows, null ciphers (no encryption) are supported. diff --git a/external/corefx/src/System.Net.Security/tests/UnitTests/Configurations.props b/external/corefx/src/System.Net.Security/tests/UnitTests/Configurations.props index cfc57211a4..7bdd3a170b 100644 --- a/external/corefx/src/System.Net.Security/tests/UnitTests/Configurations.props +++ b/external/corefx/src/System.Net.Security/tests/UnitTests/Configurations.props @@ -2,8 +2,10 @@ - netstandard-Unix; - netstandard-Windows_NT; + netcoreapp-OSX; + netcoreapp-Unix; + netcoreapp-Windows_NT; + uap-Windows_NT; \ No newline at end of file diff --git a/external/corefx/src/System.Net.Security/tests/UnitTests/Fakes/FakeAuthenticatedStream.cs b/external/corefx/src/System.Net.Security/tests/UnitTests/Fakes/FakeAuthenticatedStream.cs index 89e892b2cd..be2d2483d5 100644 --- a/external/corefx/src/System.Net.Security/tests/UnitTests/Fakes/FakeAuthenticatedStream.cs +++ b/external/corefx/src/System.Net.Security/tests/UnitTests/Fakes/FakeAuthenticatedStream.cs @@ -33,7 +33,8 @@ namespace System.Net.Security public abstract bool IsSigned { get; } public abstract bool IsServer { get; } - public abstract Task WriteAsync(ReadOnlyMemory buffer, CancellationToken token); + public new abstract Task WriteAsync(ReadOnlyMemory buffer, CancellationToken token); + public new abstract ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken); } } diff --git a/external/corefx/src/System.Net.Security/tests/UnitTests/Fakes/FakeSslState.cs b/external/corefx/src/System.Net.Security/tests/UnitTests/Fakes/FakeSslState.cs index caae4c7923..2ee788a9ea 100644 --- a/external/corefx/src/System.Net.Security/tests/UnitTests/Fakes/FakeSslState.cs +++ b/external/corefx/src/System.Net.Security/tests/UnitTests/Fakes/FakeSslState.cs @@ -17,16 +17,26 @@ namespace System.Net.Security // The public Client and Server classes enforce the parameters rules before // calling into this .ctor. // - internal SslState(Stream innerStream, RemoteCertValidationCallback certValidationCallback, LocalCertSelectionCallback certSelectionCallback, EncryptionPolicy encryptionPolicy) + internal SslState(Stream innerStream) { } - // - // - // - internal void ValidateCreateContext(bool isServer, string targetHost, SslProtocols enabledSslProtocols, X509Certificate serverCertificate, X509CertificateCollection clientCertificates, bool remoteCertRequired, bool checkCertRevocationStatus) + + internal void ValidateCreateContext(SslClientAuthenticationOptions sslClientAuthenticationOptions) { } + internal void ValidateCreateContext(SslServerAuthenticationOptions sslServerAuthenticationOptions) + { + } + + internal SslApplicationProtocol NegotiatedApplicationProtocol + { + get + { + return default; + } + } + internal bool IsAuthenticated { get @@ -269,7 +279,12 @@ namespace System.Net.Security throw new NotImplementedException(); } - public Task WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken) + public new Task WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken) { throw new NotImplementedException(); } @@ -284,6 +299,11 @@ namespace System.Net.Security throw new NotImplementedException(); } + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState) { throw new NotImplementedException(); diff --git a/external/corefx/src/System.Net.Security/tests/UnitTests/SslApplicationProtocolTests.cs b/external/corefx/src/System.Net.Security/tests/UnitTests/SslApplicationProtocolTests.cs new file mode 100644 index 0000000000..0706c73300 --- /dev/null +++ b/external/corefx/src/System.Net.Security/tests/UnitTests/SslApplicationProtocolTests.cs @@ -0,0 +1,107 @@ +// 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.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using Xunit; + +namespace System.Net.Security.Tests +{ + public class SslApplicationProtocolTests + { + [Fact] + public void Constants_Values_AreCorrect() + { + Assert.Equal(new SslApplicationProtocol(new byte[] { 0x68, 0x32 }), SslApplicationProtocol.Http2); + Assert.Equal(new SslApplicationProtocol(new byte[] { 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31 }), SslApplicationProtocol.Http11); + } + + [Fact] + public void Constructor_Overloads_Succeeds() + { + const string hello = "hello"; + byte[] expected = Encoding.UTF8.GetBytes(hello); + SslApplicationProtocol byteProtocol = new SslApplicationProtocol(expected); + SslApplicationProtocol stringProtocol = new SslApplicationProtocol(hello); + Assert.Equal(byteProtocol, stringProtocol); + + SslApplicationProtocol defaultProtocol = default; + Assert.True(defaultProtocol.Protocol.IsEmpty); + + Assert.Throws(() => { new SslApplicationProtocol((byte[])null); }); + Assert.Throws(() => { new SslApplicationProtocol((string)null); }); + Assert.Throws(() => { new SslApplicationProtocol(new byte[] { }); }); + Assert.Throws(() => { new SslApplicationProtocol(string.Empty); }); + Assert.Throws(() => { new SslApplicationProtocol(Encoding.UTF8.GetBytes(new string('a', 256))); }); + Assert.Throws(() => { new SslApplicationProtocol(new string('a', 256)); }); + Assert.Throws(() => { new SslApplicationProtocol("\uDC00"); }); + } + + [Fact] + public void Constructor_ByteArray_Copies() + { + byte[] expected = Encoding.UTF8.GetBytes("hello"); + SslApplicationProtocol byteProtocol = new SslApplicationProtocol(expected); + + ArraySegment arraySegment; + Assert.True(MemoryMarshal.TryGetArray(byteProtocol.Protocol, out arraySegment)); + Assert.Equal(expected, arraySegment.Array); + Assert.NotSame(expected, arraySegment.Array); + } + + [Theory] + [MemberData(nameof(Protocol_Equality_TestData))] + public void Equality_Tests_Succeeds(SslApplicationProtocol left, SslApplicationProtocol right) + { + Assert.Equal(left, right); + Assert.True(left == right); + Assert.False(left != right); + Assert.Equal(left.GetHashCode(), right.GetHashCode()); + } + + [Theory] + [MemberData(nameof(Protocol_InEquality_TestData))] + public void InEquality_Tests_Succeeds(SslApplicationProtocol left, SslApplicationProtocol right) + { + Assert.NotEqual(left, right); + Assert.True(left != right); + Assert.False(left == right); + Assert.NotEqual(left.GetHashCode(), right.GetHashCode()); + } + + [Fact] + public void ToString_Rendering_Succeeds() + { + const string expected = "hello"; + SslApplicationProtocol protocol = new SslApplicationProtocol(expected); + Assert.Equal(expected, protocol.ToString()); + + byte[] bytes = new byte[] { 0x0B, 0xEE }; + protocol = new SslApplicationProtocol(bytes); + Assert.Equal("0x0b 0xee", protocol.ToString()); + + protocol = default; + Assert.Null(protocol.ToString()); + } + + public static IEnumerable Protocol_Equality_TestData() + { + yield return new object[] { new SslApplicationProtocol("hello"), new SslApplicationProtocol("hello") }; + yield return new object[] { new SslApplicationProtocol(new byte[] { 0x42 }), new SslApplicationProtocol(new byte[] { 0x42 }) }; + yield return new object[] { null, null }; + yield return new object[] { default, default }; + yield return new object[] { null, default }; + yield return new object[] { default, null }; + } + + public static IEnumerable Protocol_InEquality_TestData() + { + yield return new object[] { new SslApplicationProtocol("hello"), new SslApplicationProtocol("world") }; + yield return new object[] { new SslApplicationProtocol(new byte[] { 0x42 }), new SslApplicationProtocol(new byte[] { 0x52, 0x62 }) }; + yield return new object[] { null, new SslApplicationProtocol(new byte[] { 0x42 }) }; + yield return new object[] { new SslApplicationProtocol(new byte[] { 0x42 }), null }; + } + } +} diff --git a/external/corefx/src/System.Net.Security/tests/UnitTests/SslAuthenticationOptionsTests.cs b/external/corefx/src/System.Net.Security/tests/UnitTests/SslAuthenticationOptionsTests.cs new file mode 100644 index 0000000000..6869c4ee31 --- /dev/null +++ b/external/corefx/src/System.Net.Security/tests/UnitTests/SslAuthenticationOptionsTests.cs @@ -0,0 +1,162 @@ +// 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.Collections.Generic; +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; +using Xunit; + +namespace System.Net.Security.Tests +{ + public class SslAuthenticationOptionsTests + { + private readonly SslClientAuthenticationOptions _clientOptions = new SslClientAuthenticationOptions(); + private readonly SslServerAuthenticationOptions _serverOptions = new SslServerAuthenticationOptions(); + + [Fact] + public void AllowRenegotiation_Get_Set_Succeeds() + { + Assert.True(_clientOptions.AllowRenegotiation); + Assert.True(_serverOptions.AllowRenegotiation); + + _clientOptions.AllowRenegotiation = true; + _serverOptions.AllowRenegotiation = true; + + Assert.True(_clientOptions.AllowRenegotiation); + Assert.True(_serverOptions.AllowRenegotiation); + } + + [Fact] + public void ClientCertificateRequired_Get_Set_Succeeds() + { + Assert.False(_serverOptions.ClientCertificateRequired); + + _serverOptions.ClientCertificateRequired = true; + Assert.True(_serverOptions.ClientCertificateRequired); + } + + [Fact] + public void ApplicationProtocols_Get_Set_Succeeds() + { + Assert.Null(_clientOptions.ApplicationProtocols); + Assert.Null(_serverOptions.ApplicationProtocols); + + List applnProtos = new List { SslApplicationProtocol.Http2, SslApplicationProtocol.Http11 }; + _clientOptions.ApplicationProtocols = applnProtos; + _serverOptions.ApplicationProtocols = applnProtos; + + Assert.Equal(applnProtos, _clientOptions.ApplicationProtocols); + Assert.Equal(applnProtos, _serverOptions.ApplicationProtocols); + } + + [Fact] + public void RemoteCertificateValidationCallback_Get_Set_Succeeds() + { + Assert.Null(_clientOptions.RemoteCertificateValidationCallback); + Assert.Null(_serverOptions.RemoteCertificateValidationCallback); + + RemoteCertificateValidationCallback callback = (sender, certificate, chain, errors) => { return true; }; + _clientOptions.RemoteCertificateValidationCallback = callback; + _serverOptions.RemoteCertificateValidationCallback = callback; + + Assert.Equal(callback, _clientOptions.RemoteCertificateValidationCallback); + Assert.Equal(callback, _serverOptions.RemoteCertificateValidationCallback); + } + + [Fact] + public void LocalCertificateSelectionCallback_Get_Set_Succeeds() + { + Assert.Null(_clientOptions.LocalCertificateSelectionCallback); + + LocalCertificateSelectionCallback callback = (sender, host, localCertificates, remoteCertificate, issuers) => { return new X509Certificate(); }; + _clientOptions.LocalCertificateSelectionCallback = callback; + + Assert.Equal(callback, _clientOptions.LocalCertificateSelectionCallback); + } + + [Theory] + [InlineData("")] + [InlineData("\u0bee")] + [InlineData("hello")] + [InlineData(" \t")] + [InlineData(null)] + public void TargetHost_Get_Set_Succeeds(string expected) + { + Assert.Null(_clientOptions.TargetHost); + _clientOptions.TargetHost = expected; + Assert.Equal(expected, _clientOptions.TargetHost); + } + + [Fact] + public void ClientCertificates_Get_Set_Succeeds() + { + Assert.Null(_clientOptions.ClientCertificates); + + _clientOptions.ClientCertificates = null; + Assert.Null(_clientOptions.ClientCertificates); + + X509CertificateCollection expected = new X509CertificateCollection(); + _clientOptions.ClientCertificates = expected; + Assert.Equal(expected, _clientOptions.ClientCertificates); + } + + [Fact] + public void ServerCertificate_Get_Set_Succeeds() + { + Assert.Null(_serverOptions.ServerCertificate); + _serverOptions.ServerCertificate = null; + + Assert.Null(_serverOptions.ServerCertificate); + X509Certificate cert = new X509Certificate(); + _serverOptions.ServerCertificate = cert; + + Assert.Equal(cert, _serverOptions.ServerCertificate); + } + + [Fact] + public void EnabledSslProtocols_Get_Set_Succeeds() + { + Assert.Equal(SslProtocols.None, _clientOptions.EnabledSslProtocols); + Assert.Equal(SslProtocols.None, _serverOptions.EnabledSslProtocols); + + _clientOptions.EnabledSslProtocols = SslProtocols.Tls12; + _serverOptions.EnabledSslProtocols = SslProtocols.Tls12; + + Assert.Equal(SslProtocols.Tls12, _clientOptions.EnabledSslProtocols); + Assert.Equal(SslProtocols.Tls12, _serverOptions.EnabledSslProtocols); + } + + [Fact] + public void CheckCertificateRevocation_Get_Set_Succeeds() + { + Assert.Equal(X509RevocationMode.NoCheck, _clientOptions.CertificateRevocationCheckMode); + Assert.Equal(X509RevocationMode.NoCheck, _serverOptions.CertificateRevocationCheckMode); + + _clientOptions.CertificateRevocationCheckMode = X509RevocationMode.Online; + _serverOptions.CertificateRevocationCheckMode = X509RevocationMode.Offline; + + Assert.Equal(X509RevocationMode.Online, _clientOptions.CertificateRevocationCheckMode); + Assert.Equal(X509RevocationMode.Offline, _serverOptions.CertificateRevocationCheckMode); + + Assert.Throws(() => _clientOptions.CertificateRevocationCheckMode = (X509RevocationMode)3); + Assert.Throws(() => _serverOptions.CertificateRevocationCheckMode = (X509RevocationMode)3); + } + + [Fact] + public void EncryptionPolicy_Get_Set_Succeeds() + { + Assert.Equal(EncryptionPolicy.RequireEncryption, _clientOptions.EncryptionPolicy); + Assert.Equal(EncryptionPolicy.RequireEncryption, _serverOptions.EncryptionPolicy); + + _clientOptions.EncryptionPolicy = EncryptionPolicy.AllowNoEncryption; + _serverOptions.EncryptionPolicy = EncryptionPolicy.NoEncryption; + + Assert.Equal(EncryptionPolicy.AllowNoEncryption, _clientOptions.EncryptionPolicy); + Assert.Equal(EncryptionPolicy.NoEncryption, _serverOptions.EncryptionPolicy); + + Assert.Throws(() => _clientOptions.EncryptionPolicy = (EncryptionPolicy)3); + Assert.Throws(() => _serverOptions.EncryptionPolicy = (EncryptionPolicy)3); + } + } +} diff --git a/external/corefx/src/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj b/external/corefx/src/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj index db48c79c53..d1181a7f2e 100644 --- a/external/corefx/src/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj +++ b/external/corefx/src/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj @@ -12,13 +12,21 @@ --> 436 - - - - + + + + + + + + + + + + @@ -34,6 +42,18 @@ ProductionCode\System\Net\Security\SslStream.cs + + ProductionCode\System\Net\Security\SslClientAuthenticationOptions.cs + + + ProductionCode\System\Net\Security\SslServerAuthenticationOptions.cs + + + ProductionCode\System\Net\Security\SslAuthenticationOptions.cs + + + ProductionCode\System\Net\Security\SslApplicationProtocol.cs + ProductionCode\System\Net\SslStreamContext.cs @@ -57,5 +77,13 @@ ProductionCode\Common\System\Net\InternalException.cs + + + + + + + + - + \ No newline at end of file diff --git a/external/corefx/src/System.Net.Security/tests/UnitTests/System/Security/Authentication/AuthenticationExceptionTest.cs b/external/corefx/src/System.Net.Security/tests/UnitTests/System/Security/Authentication/AuthenticationExceptionTest.cs new file mode 100644 index 0000000000..707c74fc02 --- /dev/null +++ b/external/corefx/src/System.Net.Security/tests/UnitTests/System/Security/Authentication/AuthenticationExceptionTest.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. +// See the LICENSE file in the project root for more information. + +using System.Security.Authentication; +using Xunit; + +namespace System.Net.Security.Tests +{ + public class AuthenticationExceptionTest + { + [Fact] + public void Constructor_NoParameter_DefaultMessageIsNotNull() + { + AuthenticationException authenticationException = new AuthenticationException(); + + Assert.NotNull(authenticationException.Message); + } + + [Fact] + public void Constructor_String_PassedInMessageCorrect() + { + const string passedInMessage = "base was called"; + + AuthenticationException authenticationException = new AuthenticationException(passedInMessage); + + Assert.Equal(passedInMessage, authenticationException.Message); + } + + [Fact] + public void Constructor_String_Exception_MessagesCorrect() + { + const string passedInMessage = "base was called"; + const string innerExceptionMessage = "this is the inner exception message"; + + AuthenticationException authenticationException = new AuthenticationException(passedInMessage, new Exception(innerExceptionMessage)); + + Assert.Equal(passedInMessage, authenticationException.Message); + Assert.Equal(innerExceptionMessage, authenticationException.InnerException.Message); + } + } +} diff --git a/external/corefx/src/System.Net.Security/tests/UnitTests/System/Security/Authentication/InvalidCredentialExceptionTest.cs b/external/corefx/src/System.Net.Security/tests/UnitTests/System/Security/Authentication/InvalidCredentialExceptionTest.cs new file mode 100644 index 0000000000..163fd8edb1 --- /dev/null +++ b/external/corefx/src/System.Net.Security/tests/UnitTests/System/Security/Authentication/InvalidCredentialExceptionTest.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. +// See the LICENSE file in the project root for more information. + +using System.Security.Authentication; +using Xunit; + +namespace System.Net.Security.Tests +{ + public class InvalidCredentialExceptionTest + { + [Fact] + public void Constructor_NoParameter_DefaultMessageIsNotNull() + { + InvalidCredentialException invalidCredentialException = new InvalidCredentialException(); + + Assert.NotNull(invalidCredentialException.Message); + } + + [Fact] + public void Constructor_String_PassedInMessageCorrect() + { + const string passedInMessage = "base was called"; + + InvalidCredentialException invalidCredentialException = new InvalidCredentialException(passedInMessage); + + Assert.Equal(passedInMessage, invalidCredentialException.Message); + } + + [Fact] + public void Constructor_String_Exception_MessagesCorrect() + { + const string passedInMessage = "base was called"; + const string innerExceptionMessage = "this is the inner exception message"; + + InvalidCredentialException invalidCredentialException = new InvalidCredentialException(passedInMessage, new Exception(innerExceptionMessage)); + + Assert.Equal(passedInMessage, invalidCredentialException.Message); + Assert.Equal(innerExceptionMessage, invalidCredentialException.InnerException.Message); + } + } +} diff --git a/external/corefx/src/System.Net.ServicePoint/tests/ServicePointManagerTest.cs b/external/corefx/src/System.Net.ServicePoint/tests/ServicePointManagerTest.cs index 6793c35c93..f25ad6f7e3 100644 --- a/external/corefx/src/System.Net.ServicePoint/tests/ServicePointManagerTest.cs +++ b/external/corefx/src/System.Net.ServicePoint/tests/ServicePointManagerTest.cs @@ -3,327 +3,382 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics; using System.Net.Security; using System.Security.Cryptography.X509Certificates; using Xunit; namespace System.Net.Tests { - public class ServicePointManagerTest + public class ServicePointManagerTest : RemoteExecutorTestBase { [Fact] public static void RequireEncryption_ExpectedDefault() { - Assert.Equal(EncryptionPolicy.RequireEncryption, ServicePointManager.EncryptionPolicy); + RemoteInvoke(() => Assert.Equal(EncryptionPolicy.RequireEncryption, ServicePointManager.EncryptionPolicy)); } [Fact] public static void CheckCertificateRevocationList_Roundtrips() { - Assert.False(ServicePointManager.CheckCertificateRevocationList); + RemoteInvoke(() => + { + Assert.False(ServicePointManager.CheckCertificateRevocationList); - ServicePointManager.CheckCertificateRevocationList = true; - Assert.True(ServicePointManager.CheckCertificateRevocationList); + ServicePointManager.CheckCertificateRevocationList = true; + Assert.True(ServicePointManager.CheckCertificateRevocationList); - ServicePointManager.CheckCertificateRevocationList = false; - Assert.False(ServicePointManager.CheckCertificateRevocationList); + ServicePointManager.CheckCertificateRevocationList = false; + Assert.False(ServicePointManager.CheckCertificateRevocationList); + }); } [Fact] public static void DefaultConnectionLimit_Roundtrips() { - Assert.Equal(2, ServicePointManager.DefaultConnectionLimit); + RemoteInvoke(() => + { + Assert.Equal(2, ServicePointManager.DefaultConnectionLimit); - ServicePointManager.DefaultConnectionLimit = 20; - Assert.Equal(20, ServicePointManager.DefaultConnectionLimit); + ServicePointManager.DefaultConnectionLimit = 20; + Assert.Equal(20, ServicePointManager.DefaultConnectionLimit); - ServicePointManager.DefaultConnectionLimit = 2; - Assert.Equal(2, ServicePointManager.DefaultConnectionLimit); + ServicePointManager.DefaultConnectionLimit = 2; + Assert.Equal(2, ServicePointManager.DefaultConnectionLimit); + }); } [Fact] public static void DnsRefreshTimeout_Roundtrips() { - Assert.Equal(120000, ServicePointManager.DnsRefreshTimeout); + RemoteInvoke(() => + { + Assert.Equal(120000, ServicePointManager.DnsRefreshTimeout); - ServicePointManager.DnsRefreshTimeout = 42; - Assert.Equal(42, ServicePointManager.DnsRefreshTimeout); + ServicePointManager.DnsRefreshTimeout = 42; + Assert.Equal(42, ServicePointManager.DnsRefreshTimeout); - ServicePointManager.DnsRefreshTimeout = 120000; - Assert.Equal(120000, ServicePointManager.DnsRefreshTimeout); + ServicePointManager.DnsRefreshTimeout = 120000; + Assert.Equal(120000, ServicePointManager.DnsRefreshTimeout); + }); } [Fact] public static void EnableDnsRoundRobin_Roundtrips() { - Assert.False(ServicePointManager.EnableDnsRoundRobin); + RemoteInvoke(() => + { + Assert.False(ServicePointManager.EnableDnsRoundRobin); - ServicePointManager.EnableDnsRoundRobin = true; - Assert.True(ServicePointManager.EnableDnsRoundRobin); + ServicePointManager.EnableDnsRoundRobin = true; + Assert.True(ServicePointManager.EnableDnsRoundRobin); - ServicePointManager.EnableDnsRoundRobin = false; - Assert.False(ServicePointManager.EnableDnsRoundRobin); + ServicePointManager.EnableDnsRoundRobin = false; + Assert.False(ServicePointManager.EnableDnsRoundRobin); + }); } [Fact] public static void Expect100Continue_Roundtrips() { - Assert.True(ServicePointManager.Expect100Continue); + RemoteInvoke(() => + { + Assert.True(ServicePointManager.Expect100Continue); - ServicePointManager.Expect100Continue = false; - Assert.False(ServicePointManager.Expect100Continue); + ServicePointManager.Expect100Continue = false; + Assert.False(ServicePointManager.Expect100Continue); - ServicePointManager.Expect100Continue = true; - Assert.True(ServicePointManager.Expect100Continue); + ServicePointManager.Expect100Continue = true; + Assert.True(ServicePointManager.Expect100Continue); + }); } [Fact] public static void MaxServicePointIdleTime_Roundtrips() { - Assert.Equal(100000, ServicePointManager.MaxServicePointIdleTime); + RemoteInvoke(() => + { + Assert.Equal(100000, ServicePointManager.MaxServicePointIdleTime); - ServicePointManager.MaxServicePointIdleTime = 42; - Assert.Equal(42, ServicePointManager.MaxServicePointIdleTime); + ServicePointManager.MaxServicePointIdleTime = 42; + Assert.Equal(42, ServicePointManager.MaxServicePointIdleTime); - ServicePointManager.MaxServicePointIdleTime = 100000; - Assert.Equal(100000, ServicePointManager.MaxServicePointIdleTime); + ServicePointManager.MaxServicePointIdleTime = 100000; + Assert.Equal(100000, ServicePointManager.MaxServicePointIdleTime); + }); } [Fact] public static void MaxServicePoints_Roundtrips() { - Assert.Equal(0, ServicePointManager.MaxServicePoints); + RemoteInvoke(() => + { + Assert.Equal(0, ServicePointManager.MaxServicePoints); - ServicePointManager.MaxServicePoints = 42; - Assert.Equal(42, ServicePointManager.MaxServicePoints); + ServicePointManager.MaxServicePoints = 42; + Assert.Equal(42, ServicePointManager.MaxServicePoints); - ServicePointManager.MaxServicePoints = 0; - Assert.Equal(0, ServicePointManager.MaxServicePoints); + ServicePointManager.MaxServicePoints = 0; + Assert.Equal(0, ServicePointManager.MaxServicePoints); + }); } [Fact] public static void ReusePort_Roundtrips() { - Assert.False(ServicePointManager.ReusePort); + RemoteInvoke(() => + { + Assert.False(ServicePointManager.ReusePort); - ServicePointManager.ReusePort = true; - Assert.True(ServicePointManager.ReusePort); + ServicePointManager.ReusePort = true; + Assert.True(ServicePointManager.ReusePort); - ServicePointManager.ReusePort = false; - Assert.False(ServicePointManager.ReusePort); + ServicePointManager.ReusePort = false; + Assert.False(ServicePointManager.ReusePort); + }); } [Fact] [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Desktop default SecurityProtocol to Ssl3; explicitly changed to SystemDefault for core.")] public static void SecurityProtocol_Roundtrips() { - var orig = (SecurityProtocolType)0; // SystemDefault. - Assert.Equal(orig, ServicePointManager.SecurityProtocol); + RemoteInvoke(() => + { + var orig = (SecurityProtocolType)0; // SystemDefault. + Assert.Equal(orig, ServicePointManager.SecurityProtocol); - ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11; - Assert.Equal(SecurityProtocolType.Tls11, ServicePointManager.SecurityProtocol); + ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11; + Assert.Equal(SecurityProtocolType.Tls11, ServicePointManager.SecurityProtocol); - ServicePointManager.SecurityProtocol = orig; - Assert.Equal(orig, ServicePointManager.SecurityProtocol); + ServicePointManager.SecurityProtocol = orig; + Assert.Equal(orig, ServicePointManager.SecurityProtocol); + }); } [Fact] public static void ServerCertificateValidationCallback_Roundtrips() { - Assert.Null(ServicePointManager.ServerCertificateValidationCallback); + RemoteInvoke(() => + { + Assert.Null(ServicePointManager.ServerCertificateValidationCallback); - RemoteCertificateValidationCallback callback = delegate { return true; }; - ServicePointManager.ServerCertificateValidationCallback = callback; - Assert.Same(callback, ServicePointManager.ServerCertificateValidationCallback); + RemoteCertificateValidationCallback callback = delegate { return true; }; + ServicePointManager.ServerCertificateValidationCallback = callback; + Assert.Same(callback, ServicePointManager.ServerCertificateValidationCallback); - ServicePointManager.ServerCertificateValidationCallback = null; - Assert.Null(ServicePointManager.ServerCertificateValidationCallback); + ServicePointManager.ServerCertificateValidationCallback = null; + Assert.Null(ServicePointManager.ServerCertificateValidationCallback); + }); } [Fact] public static void UseNagleAlgorithm_Roundtrips() { - Assert.True(ServicePointManager.UseNagleAlgorithm); + RemoteInvoke(() => + { + Assert.True(ServicePointManager.UseNagleAlgorithm); - ServicePointManager.UseNagleAlgorithm = false; - Assert.False(ServicePointManager.UseNagleAlgorithm); + ServicePointManager.UseNagleAlgorithm = false; + Assert.False(ServicePointManager.UseNagleAlgorithm); - ServicePointManager.UseNagleAlgorithm = true; - Assert.True(ServicePointManager.UseNagleAlgorithm); + ServicePointManager.UseNagleAlgorithm = true; + Assert.True(ServicePointManager.UseNagleAlgorithm); + }); } [Fact] public static void InvalidArguments_Throw() { - const int ssl2Client = 0x00000008; - const int ssl2Server = 0x00000004; - const SecurityProtocolType ssl2 = (SecurityProtocolType)(ssl2Client | ssl2Server); - Assert.Throws(() => ServicePointManager.SecurityProtocol = ssl2); + RemoteInvoke(() => + { + const int ssl2Client = 0x00000008; + const int ssl2Server = 0x00000004; + const SecurityProtocolType ssl2 = (SecurityProtocolType)(ssl2Client | ssl2Server); + Assert.Throws(() => ServicePointManager.SecurityProtocol = ssl2); - AssertExtensions.Throws("uriString", () => ServicePointManager.FindServicePoint((string)null, null)); - AssertExtensions.Throws("value", () => ServicePointManager.MaxServicePoints = -1); - AssertExtensions.Throws("value", () => ServicePointManager.DefaultConnectionLimit = 0); - AssertExtensions.Throws("value", () => ServicePointManager.MaxServicePointIdleTime = -2); - AssertExtensions.Throws("keepAliveTime", () => ServicePointManager.SetTcpKeepAlive(true, -1, 1)); - AssertExtensions.Throws("keepAliveInterval", () => ServicePointManager.SetTcpKeepAlive(true, 1, -1)); - AssertExtensions.Throws("address", () => ServicePointManager.FindServicePoint(null)); - AssertExtensions.Throws("uriString", () => ServicePointManager.FindServicePoint((string)null, null)); - AssertExtensions.Throws("address", () => ServicePointManager.FindServicePoint((Uri)null, null)); - Assert.Throws(() => ServicePointManager.FindServicePoint("http://anything", new FixedWebProxy("https://anything"))); + AssertExtensions.Throws("uriString", () => ServicePointManager.FindServicePoint((string)null, null)); + AssertExtensions.Throws("value", () => ServicePointManager.MaxServicePoints = -1); + AssertExtensions.Throws("value", () => ServicePointManager.DefaultConnectionLimit = 0); + AssertExtensions.Throws("value", () => ServicePointManager.MaxServicePointIdleTime = -2); + AssertExtensions.Throws("keepAliveTime", () => ServicePointManager.SetTcpKeepAlive(true, -1, 1)); + AssertExtensions.Throws("keepAliveInterval", () => ServicePointManager.SetTcpKeepAlive(true, 1, -1)); + AssertExtensions.Throws("address", () => ServicePointManager.FindServicePoint(null)); + AssertExtensions.Throws("uriString", () => ServicePointManager.FindServicePoint((string)null, null)); + AssertExtensions.Throws("address", () => ServicePointManager.FindServicePoint((Uri)null, null)); + Assert.Throws(() => ServicePointManager.FindServicePoint("http://anything", new FixedWebProxy("https://anything"))); - ServicePoint sp = ServicePointManager.FindServicePoint("http://" + Guid.NewGuid().ToString("N"), null); - AssertExtensions.Throws("value", () => sp.ConnectionLeaseTimeout = -2); - AssertExtensions.Throws("value", () => sp.ConnectionLimit = 0); - AssertExtensions.Throws("value", () => sp.MaxIdleTime = -2); - AssertExtensions.Throws("value", () => sp.ReceiveBufferSize = -2); - AssertExtensions.Throws("keepAliveTime", () => sp.SetTcpKeepAlive(true, -1, 1)); - AssertExtensions.Throws("keepAliveInterval", () => sp.SetTcpKeepAlive(true, 1, -1)); + ServicePoint sp = ServicePointManager.FindServicePoint("http://" + Guid.NewGuid().ToString("N"), null); + AssertExtensions.Throws("value", () => sp.ConnectionLeaseTimeout = -2); + AssertExtensions.Throws("value", () => sp.ConnectionLimit = 0); + AssertExtensions.Throws("value", () => sp.MaxIdleTime = -2); + AssertExtensions.Throws("value", () => sp.ReceiveBufferSize = -2); + AssertExtensions.Throws("keepAliveTime", () => sp.SetTcpKeepAlive(true, -1, 1)); + AssertExtensions.Throws("keepAliveInterval", () => sp.SetTcpKeepAlive(true, 1, -1)); + }); } [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Ssl3 is supported by desktop but explicitly not by core")] [Fact] public static void SecurityProtocol_Ssl3_NotSupported() { - const int ssl2Client = 0x00000008; - const int ssl2Server = 0x00000004; - const SecurityProtocolType ssl2 = (SecurityProtocolType)(ssl2Client | ssl2Server); + RemoteInvoke(() => + { + const int ssl2Client = 0x00000008; + const int ssl2Server = 0x00000004; + const SecurityProtocolType ssl2 = (SecurityProtocolType)(ssl2Client | ssl2Server); #pragma warning disable 0618 // Ssl2, Ssl3 are deprecated. - Assert.Throws(() => ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3); - Assert.Throws(() => ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | ssl2); + Assert.Throws(() => ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3); + Assert.Throws(() => ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | ssl2); #pragma warning restore + }); } [Fact] public static void FindServicePoint_ReturnsCachedServicePoint() { - const string Localhost = "http://localhost"; - string address1 = "http://" + Guid.NewGuid().ToString("N"); - string address2 = "http://" + Guid.NewGuid().ToString("N"); + RemoteInvoke(() => + { + const string Localhost = "http://localhost"; + string address1 = "http://" + Guid.NewGuid().ToString("N"); + string address2 = "http://" + Guid.NewGuid().ToString("N"); - Assert.NotNull(ServicePointManager.FindServicePoint(new Uri(address1))); + Assert.NotNull(ServicePointManager.FindServicePoint(new Uri(address1))); - Assert.Same( - ServicePointManager.FindServicePoint(address1, null), - ServicePointManager.FindServicePoint(address1, null)); - Assert.Same( - ServicePointManager.FindServicePoint(address1, new FixedWebProxy(address1)), - ServicePointManager.FindServicePoint(address1, new FixedWebProxy(address1))); - Assert.Same( - ServicePointManager.FindServicePoint(address1, new FixedWebProxy(address1)), - ServicePointManager.FindServicePoint(address2, new FixedWebProxy(address1))); - Assert.Same( - ServicePointManager.FindServicePoint(Localhost, new FixedWebProxy(address1)), - ServicePointManager.FindServicePoint(Localhost, new FixedWebProxy(address2))); + Assert.Same( + ServicePointManager.FindServicePoint(address1, null), + ServicePointManager.FindServicePoint(address1, null)); + Assert.Same( + ServicePointManager.FindServicePoint(address1, new FixedWebProxy(address1)), + ServicePointManager.FindServicePoint(address1, new FixedWebProxy(address1))); + Assert.Same( + ServicePointManager.FindServicePoint(address1, new FixedWebProxy(address1)), + ServicePointManager.FindServicePoint(address2, new FixedWebProxy(address1))); + Assert.Same( + ServicePointManager.FindServicePoint(Localhost, new FixedWebProxy(address1)), + ServicePointManager.FindServicePoint(Localhost, new FixedWebProxy(address2))); - Assert.NotSame( - ServicePointManager.FindServicePoint(address1, null), - ServicePointManager.FindServicePoint(address2, null)); - Assert.NotSame( - ServicePointManager.FindServicePoint(address1, null), - ServicePointManager.FindServicePoint(address1, new FixedWebProxy(address1))); - Assert.NotSame( - ServicePointManager.FindServicePoint(address1, new FixedWebProxy(address1)), - ServicePointManager.FindServicePoint(address1, new FixedWebProxy(address2))); + Assert.NotSame( + ServicePointManager.FindServicePoint(address1, null), + ServicePointManager.FindServicePoint(address2, null)); + Assert.NotSame( + ServicePointManager.FindServicePoint(address1, null), + ServicePointManager.FindServicePoint(address1, new FixedWebProxy(address1))); + Assert.NotSame( + ServicePointManager.FindServicePoint(address1, new FixedWebProxy(address1)), + ServicePointManager.FindServicePoint(address1, new FixedWebProxy(address2))); + }); } [Fact] [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Desktop ServicePoint lifetime is slightly longer due to implementation details of real implementation")] public static void FindServicePoint_Collectible() { - string address = "http://" + Guid.NewGuid().ToString("N"); + RemoteInvoke(() => + { + string address = "http://" + Guid.NewGuid().ToString("N"); - bool initial = GetExpect100Continue(address); - SetExpect100Continue(address, !initial); + bool initial = GetExpect100Continue(address); + SetExpect100Continue(address, !initial); - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); - Assert.Equal(initial, GetExpect100Continue(address)); + Assert.Equal(initial, GetExpect100Continue(address)); + }); } [Fact] public static void FindServicePoint_ReturnedServicePointMatchesExpectedValues() { - string address = "http://" + Guid.NewGuid().ToString("N"); + RemoteInvoke(() => + { + string address = "http://" + Guid.NewGuid().ToString("N"); - DateTime start = DateTime.Now; - ServicePoint sp = ServicePointManager.FindServicePoint(address, null); + DateTime start = DateTime.Now; + ServicePoint sp = ServicePointManager.FindServicePoint(address, null); - Assert.InRange(sp.IdleSince, start, DateTime.MaxValue); - Assert.Equal(new Uri(address), sp.Address); - Assert.Null(sp.BindIPEndPointDelegate); - Assert.Null(sp.Certificate); - Assert.Null(sp.ClientCertificate); - Assert.Equal(-1, sp.ConnectionLeaseTimeout); - Assert.Equal("http", sp.ConnectionName); - Assert.Equal(0, sp.CurrentConnections); - Assert.Equal(true, sp.Expect100Continue); - Assert.Equal(100000, sp.MaxIdleTime); - Assert.Equal(new Version(1, 1), sp.ProtocolVersion); - Assert.Equal(-1, sp.ReceiveBufferSize); - Assert.True(sp.SupportsPipelining, "SupportsPipelining"); - Assert.True(sp.UseNagleAlgorithm, "UseNagleAlgorithm"); + Assert.InRange(sp.IdleSince, start, DateTime.MaxValue); + Assert.Equal(new Uri(address), sp.Address); + Assert.Null(sp.BindIPEndPointDelegate); + Assert.Null(sp.Certificate); + Assert.Null(sp.ClientCertificate); + Assert.Equal(-1, sp.ConnectionLeaseTimeout); + Assert.Equal("http", sp.ConnectionName); + Assert.Equal(0, sp.CurrentConnections); + Assert.Equal(true, sp.Expect100Continue); + Assert.Equal(100000, sp.MaxIdleTime); + Assert.Equal(new Version(1, 1), sp.ProtocolVersion); + Assert.Equal(-1, sp.ReceiveBufferSize); + Assert.True(sp.SupportsPipelining, "SupportsPipelining"); + Assert.True(sp.UseNagleAlgorithm, "UseNagleAlgorithm"); + }); } [Fact] public static void FindServicePoint_PropertiesRoundtrip() { - string address = "http://" + Guid.NewGuid().ToString("N"); + RemoteInvoke(() => + { + string address = "http://" + Guid.NewGuid().ToString("N"); - BindIPEndPoint expectedBindIPEndPointDelegate = delegate { return null; }; - int expectedConnectionLeaseTimeout = 42; - int expectedConnectionLimit = 84; - bool expected100Continue = false; - int expectedMaxIdleTime = 200000; - int expectedReceiveBufferSize = 123; - bool expectedUseNagleAlgorithm = false; + BindIPEndPoint expectedBindIPEndPointDelegate = delegate { return null; }; + int expectedConnectionLeaseTimeout = 42; + int expectedConnectionLimit = 84; + bool expected100Continue = false; + int expectedMaxIdleTime = 200000; + int expectedReceiveBufferSize = 123; + bool expectedUseNagleAlgorithm = false; - ServicePoint sp1 = ServicePointManager.FindServicePoint(address, null); - sp1.BindIPEndPointDelegate = expectedBindIPEndPointDelegate; - sp1.ConnectionLeaseTimeout = expectedConnectionLeaseTimeout; - sp1.ConnectionLimit = expectedConnectionLimit; - sp1.Expect100Continue = expected100Continue; - sp1.MaxIdleTime = expectedMaxIdleTime; - sp1.ReceiveBufferSize = expectedReceiveBufferSize; - sp1.UseNagleAlgorithm = expectedUseNagleAlgorithm; + ServicePoint sp1 = ServicePointManager.FindServicePoint(address, null); + sp1.BindIPEndPointDelegate = expectedBindIPEndPointDelegate; + sp1.ConnectionLeaseTimeout = expectedConnectionLeaseTimeout; + sp1.ConnectionLimit = expectedConnectionLimit; + sp1.Expect100Continue = expected100Continue; + sp1.MaxIdleTime = expectedMaxIdleTime; + sp1.ReceiveBufferSize = expectedReceiveBufferSize; + sp1.UseNagleAlgorithm = expectedUseNagleAlgorithm; - ServicePoint sp2 = ServicePointManager.FindServicePoint(address, null); - Assert.Same(expectedBindIPEndPointDelegate, sp2.BindIPEndPointDelegate); - Assert.Equal(expectedConnectionLeaseTimeout, sp2.ConnectionLeaseTimeout); - Assert.Equal(expectedConnectionLimit, sp2.ConnectionLimit); - Assert.Equal(expected100Continue, sp2.Expect100Continue); - Assert.Equal(expectedMaxIdleTime, sp2.MaxIdleTime); - Assert.Equal(expectedReceiveBufferSize, sp2.ReceiveBufferSize); - Assert.Equal(expectedUseNagleAlgorithm, sp2.UseNagleAlgorithm); + ServicePoint sp2 = ServicePointManager.FindServicePoint(address, null); + Assert.Same(expectedBindIPEndPointDelegate, sp2.BindIPEndPointDelegate); + Assert.Equal(expectedConnectionLeaseTimeout, sp2.ConnectionLeaseTimeout); + Assert.Equal(expectedConnectionLimit, sp2.ConnectionLimit); + Assert.Equal(expected100Continue, sp2.Expect100Continue); + Assert.Equal(expectedMaxIdleTime, sp2.MaxIdleTime); + Assert.Equal(expectedReceiveBufferSize, sp2.ReceiveBufferSize); + Assert.Equal(expectedUseNagleAlgorithm, sp2.UseNagleAlgorithm); + }); } [Fact] public static void FindServicePoint_NewServicePointsInheritCurrentValues() { - string address1 = "http://" + Guid.NewGuid().ToString("N"); - string address2 = "http://" + Guid.NewGuid().ToString("N"); + RemoteInvoke(() => + { + string address1 = "http://" + Guid.NewGuid().ToString("N"); + string address2 = "http://" + Guid.NewGuid().ToString("N"); - bool orig100Continue = ServicePointManager.Expect100Continue; - bool origNagle = ServicePointManager.UseNagleAlgorithm; + bool orig100Continue = ServicePointManager.Expect100Continue; + bool origNagle = ServicePointManager.UseNagleAlgorithm; - ServicePointManager.Expect100Continue = false; - ServicePointManager.UseNagleAlgorithm = false; - ServicePoint sp1 = ServicePointManager.FindServicePoint(address1, null); - Assert.False(sp1.Expect100Continue); - Assert.False(sp1.UseNagleAlgorithm); + ServicePointManager.Expect100Continue = false; + ServicePointManager.UseNagleAlgorithm = false; + ServicePoint sp1 = ServicePointManager.FindServicePoint(address1, null); + Assert.False(sp1.Expect100Continue); + Assert.False(sp1.UseNagleAlgorithm); - ServicePointManager.Expect100Continue = true; - ServicePointManager.UseNagleAlgorithm = true; - ServicePoint sp2 = ServicePointManager.FindServicePoint(address2, null); - Assert.True(sp2.Expect100Continue); - Assert.True(sp2.UseNagleAlgorithm); - Assert.False(sp1.Expect100Continue); - Assert.False(sp1.UseNagleAlgorithm); + ServicePointManager.Expect100Continue = true; + ServicePointManager.UseNagleAlgorithm = true; + ServicePoint sp2 = ServicePointManager.FindServicePoint(address2, null); + Assert.True(sp2.Expect100Continue); + Assert.True(sp2.UseNagleAlgorithm); + Assert.False(sp1.Expect100Continue); + Assert.False(sp1.UseNagleAlgorithm); - ServicePointManager.Expect100Continue = orig100Continue; - ServicePointManager.UseNagleAlgorithm = origNagle; + ServicePointManager.Expect100Continue = orig100Continue; + ServicePointManager.UseNagleAlgorithm = origNagle; + }); } // Separated out to avoid the JIT in debug builds interfering with object lifetimes diff --git a/external/corefx/src/System.Net.ServicePoint/tests/System.Net.ServicePoint.Tests.csproj b/external/corefx/src/System.Net.ServicePoint/tests/System.Net.ServicePoint.Tests.csproj index de1881acca..fae232d87c 100644 --- a/external/corefx/src/System.Net.ServicePoint/tests/System.Net.ServicePoint.Tests.csproj +++ b/external/corefx/src/System.Net.ServicePoint/tests/System.Net.ServicePoint.Tests.csproj @@ -15,5 +15,11 @@ + + + {69e46a6f-9966-45a5-8945-2559fe337827} + RemoteExecutorConsoleApp + + \ No newline at end of file diff --git a/external/corefx/src/System.Net.ServicePoint/tests/TlsSystemDefault.cs b/external/corefx/src/System.Net.ServicePoint/tests/TlsSystemDefault.cs index dfc26933db..76e7d8ce7c 100644 --- a/external/corefx/src/System.Net.ServicePoint/tests/TlsSystemDefault.cs +++ b/external/corefx/src/System.Net.ServicePoint/tests/TlsSystemDefault.cs @@ -2,22 +2,23 @@ // 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.Diagnostics; using Xunit; namespace System.Net.Tests { - public class TlsSystemDefault + public class TlsSystemDefault : RemoteExecutorTestBase { [Fact] public void ServicePointManager_SecurityProtocolDefault_Ok() { - Assert.Equal(SecurityProtocolType.SystemDefault, ServicePointManager.SecurityProtocol); + RemoteInvoke(() => Assert.Equal(SecurityProtocolType.SystemDefault, ServicePointManager.SecurityProtocol)); } [Fact] public void ServicePointManager_CheckAllowedProtocols_SystemDefault_Allowed() { - ServicePointManager.SecurityProtocol = SecurityProtocolType.SystemDefault; + RemoteInvoke(() => ServicePointManager.SecurityProtocol = SecurityProtocolType.SystemDefault); } } } diff --git a/external/corefx/src/System.Net.Sockets/System.Net.Sockets.sln b/external/corefx/src/System.Net.Sockets/System.Net.Sockets.sln index 1c41accd25..16af380960 100644 --- a/external/corefx/src/System.Net.Sockets/System.Net.Sockets.sln +++ b/external/corefx/src/System.Net.Sockets/System.Net.Sockets.sln @@ -7,7 +7,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Net.Sockets.Tests", {43311AFB-D7C4-4E5A-B1DE-855407F90D1B} = {43311AFB-D7C4-4E5A-B1DE-855407F90D1B} EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Net.Sockets.Async.Performance.Tests", "tests\PerformanceTests\System.Net.Sockets.Async.Performance.Tests.csproj", "{BB5C85AD-C51A-4903-80E9-6F6E1AC1AD34}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Net.Sockets.Async.Performance.Tests", "tests\ManualPerformanceTests\System.Net.Sockets.Async.Performance.Tests.csproj", "{BB5C85AD-C51A-4903-80E9-6F6E1AC1AD34}" + ProjectSection(ProjectDependencies) = postProject + {43311AFB-D7C4-4E5A-B1DE-855407F90D1B} = {43311AFB-D7C4-4E5A-B1DE-855407F90D1B} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Net.Sockets.Performance.Tests", "tests\Performance\System.Net.Sockets.Performance.Tests.csproj", "{981FC867-9071-444D-9388-BC5826609F76}" ProjectSection(ProjectDependencies) = postProject {43311AFB-D7C4-4E5A-B1DE-855407F90D1B} = {43311AFB-D7C4-4E5A-B1DE-855407F90D1B} EndProjectSection @@ -39,6 +44,10 @@ Global {BB5C85AD-C51A-4903-80E9-6F6E1AC1AD34}.Debug|Any CPU.Build.0 = netstandard-Windows_NT-Debug|Any CPU {BB5C85AD-C51A-4903-80E9-6F6E1AC1AD34}.Release|Any CPU.ActiveCfg = netstandard-Windows_NT-Release|Any CPU {BB5C85AD-C51A-4903-80E9-6F6E1AC1AD34}.Release|Any CPU.Build.0 = netstandard-Windows_NT-Release|Any CPU + {981FC867-9071-444D-9388-BC5826609F76}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {981FC867-9071-444D-9388-BC5826609F76}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {981FC867-9071-444D-9388-BC5826609F76}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {981FC867-9071-444D-9388-BC5826609F76}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU {43311AFB-D7C4-4E5A-B1DE-855407F90D1B}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU {43311AFB-D7C4-4E5A-B1DE-855407F90D1B}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU {43311AFB-D7C4-4E5A-B1DE-855407F90D1B}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU @@ -54,6 +63,7 @@ Global GlobalSection(NestedProjects) = preSolution {8CBA022C-635F-4C8D-9D29-CD8AAC68C8E6} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} {BB5C85AD-C51A-4903-80E9-6F6E1AC1AD34} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} + {981FC867-9071-444D-9388-BC5826609F76} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} {43311AFB-D7C4-4E5A-B1DE-855407F90D1B} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD} {834E3534-6A11-4A8D-923F-35C1E71CCEC3} = {2E666815-2EDB-464B-9DF6-380BF4789AD4} EndGlobalSection diff --git a/external/corefx/src/System.Net.Sockets/ref/System.Net.Sockets.cs b/external/corefx/src/System.Net.Sockets/ref/System.Net.Sockets.cs index 033dc1765b..0cbf93a460 100644 --- a/external/corefx/src/System.Net.Sockets/ref/System.Net.Sockets.cs +++ b/external/corefx/src/System.Net.Sockets/ref/System.Net.Sockets.cs @@ -45,9 +45,9 @@ namespace System.Net.Sockets TranslateHandle = (long)3355443213, UnicastInterface = (long)2550136838, } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct IPPacketInformation { + private object _dummy; public System.Net.IPAddress Address { get { throw null; } } public int Interface { get { throw null; } } public override bool Equals(object comparand) { throw null; } @@ -389,10 +389,10 @@ namespace System.Net.Sockets Peek = 2, Truncated = 256, } -  [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] - public struct SocketInformation + public partial struct SocketInformation { - public SocketInformationOptions Options { get { throw null; } set { } } + private object _dummy; + public System.Net.Sockets.SocketInformationOptions Options { get { throw null; } set { } } public byte[] ProtocolInformation { get { throw null; } set { } } } [Flags] @@ -638,9 +638,9 @@ namespace System.Net.Sockets public System.Threading.Tasks.Task SendAsync(byte[] datagram, int bytes, string hostname, int port) { throw null; } public void AllowNatTraversal(bool allowed) { throw null; } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct UdpReceiveResult : System.IEquatable { + private object _dummy; public UdpReceiveResult(byte[] buffer, System.Net.IPEndPoint remoteEndPoint) { throw null; } public byte[] Buffer { get { throw null; } } public System.Net.IPEndPoint RemoteEndPoint { get { throw null; } } diff --git a/external/corefx/src/System.Net.Sockets/ref/System.Net.Sockets.netcoreapp.cs b/external/corefx/src/System.Net.Sockets/ref/System.Net.Sockets.netcoreapp.cs index 25170e64d1..75030f87a1 100644 --- a/external/corefx/src/System.Net.Sockets/ref/System.Net.Sockets.netcoreapp.cs +++ b/external/corefx/src/System.Net.Sockets/ref/System.Net.Sockets.netcoreapp.cs @@ -18,7 +18,7 @@ namespace System.Net.Sockets public int Send(ReadOnlySpan buffer, System.Net.Sockets.SocketFlags socketFlags, out System.Net.Sockets.SocketError errorCode) { throw null; } } - public partial static class SocketTaskExtensions + public static partial class SocketTaskExtensions { public static System.Threading.Tasks.ValueTask ReceiveAsync(this System.Net.Sockets.Socket socket, System.Memory buffer, System.Net.Sockets.SocketFlags socketFlags, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public static System.Threading.Tasks.ValueTask SendAsync(this System.Net.Sockets.Socket socket, System.ReadOnlyMemory buffer, System.Net.Sockets.SocketFlags socketFlags, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -26,7 +26,12 @@ namespace System.Net.Sockets public partial class SocketAsyncEventArgs : System.EventArgs, System.IDisposable { - public System.Memory GetBuffer() { throw null; } + public System.Memory MemoryBuffer { get { throw null; } } public void SetBuffer(System.Memory buffer) { throw null; } } + + public sealed partial class UnixDomainSocketEndPoint : System.Net.EndPoint + { + public UnixDomainSocketEndPoint(string path) { } + } } diff --git a/external/corefx/src/System.Net.Sockets/src/Resources/Strings.resx b/external/corefx/src/System.Net.Sockets/src/Resources/Strings.resx index b95dfcb716..de08663dd3 100644 --- a/external/corefx/src/System.Net.Sockets/src/Resources/Strings.resx +++ b/external/corefx/src/System.Net.Sockets/src/Resources/Strings.resx @@ -220,6 +220,9 @@ Positive number required. + + The path '{0}' is of an invalid length for use with domain sockets on this platform. The length must be between 1 and {1} characters, inclusive. + Unable to transfer data on the transport connection: {0}. @@ -232,4 +235,7 @@ IP protection level cannot be controlled on this platform. + + This operation may only be performed when the buffer was set using the SetBuffer overload that accepts an array. + diff --git a/external/corefx/src/System.Net.Sockets/src/System.Net.Sockets.csproj b/external/corefx/src/System.Net.Sockets/src/System.Net.Sockets.csproj index 4883cb0ac4..dd4680c6d4 100644 --- a/external/corefx/src/System.Net.Sockets/src/System.Net.Sockets.csproj +++ b/external/corefx/src/System.Net.Sockets/src/System.Net.Sockets.csproj @@ -30,7 +30,6 @@ - @@ -144,6 +143,7 @@ + Common\System\Net\SafeCloseSocket.Windows.cs @@ -267,6 +267,7 @@ + Common\System\Net\ContextAwareResult.Unix.cs @@ -312,6 +313,9 @@ Interop\Unix\System.Native\Interop.GetBytesAvailable.cs + + Common\Interop\Unix\Interop.GetDomainSocketSizes.cs + Interop\Unix\System.Native\Interop.GetPeerName.cs @@ -395,11 +399,11 @@ + - @@ -416,4 +420,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.Net.Sockets/src/System/Net/SocketPerfCounters.cs b/external/corefx/src/System.Net.Sockets/src/System/Net/SocketPerfCounters.cs deleted file mode 100644 index 49f4d3531a..0000000000 --- a/external/corefx/src/System.Net.Sockets/src/System/Net/SocketPerfCounters.cs +++ /dev/null @@ -1,33 +0,0 @@ -// 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.Diagnostics; -using System.Threading; - -namespace System.Net -{ - internal enum SocketPerfCounterName - { - SocketConnectionsEstablished = 0, // these enum values are used as index - SocketBytesReceived, - SocketBytesSent, - SocketDatagramsReceived, - SocketDatagramsSent, - } - - internal sealed class SocketPerfCounter - { - private static SocketPerfCounter s_instance; - - public static SocketPerfCounter Instance => LazyInitializer.EnsureInitialized(ref s_instance); - - public bool Enabled => false; // TODO (#7833): Implement socket perf counters. - - [Conditional("TODO7833")] - public void Increment(SocketPerfCounterName perfCounter, long amount = 1) - { - // TODO (#7833): Implement socket perf counters. - } - } -} diff --git a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/ConnectOverlappedAsyncResult.cs b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/ConnectOverlappedAsyncResult.cs index 02ee7fb832..b6e479aa59 100644 --- a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/ConnectOverlappedAsyncResult.cs +++ b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/ConnectOverlappedAsyncResult.cs @@ -15,7 +15,7 @@ namespace System.Net.Sockets _endPoint = endPoint; } - internal EndPoint RemoteEndPoint + internal override EndPoint RemoteEndPoint { get { return _endPoint; } } diff --git a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/MultipleConnectAsync.cs b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/MultipleConnectAsync.cs index 29080a0376..5a3d9f897e 100644 --- a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/MultipleConnectAsync.cs +++ b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/MultipleConnectAsync.cs @@ -33,8 +33,6 @@ namespace System.Net.Sockets private object _lockObject = new object(); - protected abstract Socket UserSocket { get; } - // Called by Socket to kick off the ConnectAsync process. We'll complete the user's SAEA // when it's done. Returns true if the operation will be asynchronous, false if it has failed synchronously public bool StartConnectAsync(SocketAsyncEventArgs args, DnsEndPoint endPoint) @@ -129,7 +127,7 @@ namespace System.Net.Sockets _internalArgs = new SocketAsyncEventArgs(); _internalArgs.Completed += InternalConnectCallback; - _internalArgs.SetBuffer(_userArgs.Buffer, _userArgs.Offset, _userArgs.Count); + _internalArgs.CopyBufferFrom(_userArgs); exception = AttemptConnection(); @@ -163,7 +161,7 @@ namespace System.Net.Sockets if (_state == State.Canceled) { // If Cancel was called before we got the lock, the Socket will be closed soon. We need to report - // OperationAborted (even though the connection actually completed), or the user will try to use a + // OperationAborted (even though the connection actually completed), or the user will try to use a // closed Socket. exception = new SocketException((int)SocketError.OperationAborted); } @@ -186,7 +184,7 @@ namespace System.Net.Sockets } else { - + // Keep track of this because it will be overwritten by AttemptConnection SocketError currentFailure = args.SocketError; Exception connectException = AttemptConnection(); @@ -254,7 +252,7 @@ namespace System.Net.Sockets } } - private static Exception AttemptConnection(Socket attemptSocket, SocketAsyncEventArgs args) + private Exception AttemptConnection(Socket attemptSocket, SocketAsyncEventArgs args) { try { @@ -263,14 +261,15 @@ namespace System.Net.Sockets NetEventSource.Fail(null, "attemptSocket is null!"); } - if (!attemptSocket.ConnectAsync(args)) + bool pending = attemptSocket.ConnectAsync(args); + if (!pending) { - return new SocketException((int)args.SocketError); + InternalConnectCallback(null, args); } } catch (ObjectDisposedException) { - // This can happen if the user closes the socket, and is equivalent to a call + // This can happen if the user closes the socket, and is equivalent to a call // to CancelConnectAsync return new SocketException((int)SocketError.OperationAborted); } @@ -413,8 +412,6 @@ namespace System.Net.Sockets private Socket _socket; private bool _userSocket; - protected override Socket UserSocket => _socket; - public SingleSocketMultipleConnectAsync(Socket socket, bool userSocket) { _socket = socket; @@ -445,7 +442,7 @@ namespace System.Net.Sockets protected override void OnFail(bool abortive) { - // Close the socket if this is an abortive failure (CancelConnectAsync) + // Close the socket if this is an abortive failure (CancelConnectAsync) // or if we created it internally if (abortive || !_userSocket) { @@ -464,8 +461,6 @@ namespace System.Net.Sockets private Socket _socket4; private Socket _socket6; - protected override Socket UserSocket => null; - public DualSocketMultipleConnectAsync(SocketType socketType, ProtocolType protocolType) { if (Socket.OSSupportsIPv4) diff --git a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/NetEventSource.Sockets.cs b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/NetEventSource.Sockets.cs index 86c8a46afc..9210ded65a 100644 --- a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/NetEventSource.Sockets.cs +++ b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/NetEventSource.Sockets.cs @@ -4,6 +4,7 @@ using System.Diagnostics.Tracing; using System.Net.Sockets; +using System.Runtime.CompilerServices; namespace System.Net { @@ -76,5 +77,41 @@ namespace System.Net { WriteEvent(NotLoggedFileId, filePath, socketHash, (int)completedOperation); } + + /// Logs the contents of a buffer. + /// `this`, or another object that serves to provide context for the operation. + /// The buffer to be logged. + /// The calling member. + [NonEvent] + public static void DumpBuffer(object thisOrContextObject, Memory buffer, [CallerMemberName] string memberName = null) + { + DumpBuffer(thisOrContextObject, buffer, 0, buffer.Length, memberName); + } + + /// Logs the contents of a buffer. + /// `this`, or another object that serves to provide context for the operation. + /// The buffer to be logged. + /// The starting offset from which to log. + /// The number of bytes to log. + /// The calling member. + [NonEvent] + public static void DumpBuffer(object thisOrContextObject, Memory buffer, int offset, int count, [CallerMemberName] string memberName = null) + { + if (IsEnabled) + { + if (offset < 0 || offset > buffer.Length - count) + { + Fail(thisOrContextObject, $"Invalid {nameof(DumpBuffer)} Args. Length={buffer.Length}, Offset={offset}, Count={count}", memberName); + return; + } + + buffer = buffer.Slice(offset, Math.Min(count, MaxDumpSize)); + byte[] slice = buffer.TryGetArray(out ArraySegment arraySegment) && arraySegment.Offset == 0 && arraySegment.Count == buffer.Length ? + arraySegment.Array : + buffer.ToArray(); + + Log.DumpBuffer(IdOf(thisOrContextObject), memberName, slice); + } + } } } diff --git a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs index 65b9a76655..fc796379ec 100644 --- a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs +++ b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -374,7 +375,7 @@ namespace System.Net.Sockets if (saea != null) { // We got a cached instance. Configure the buffer and initate the operation. - ConfigureBuffer(saea, Unsafe.As,Memory>(ref buffer), socketFlags, wrapExceptionsInIOExceptions: fromNetworkStream); + ConfigureBuffer(saea, MemoryMarshal.AsMemory(buffer), socketFlags, wrapExceptionsInIOExceptions: fromNetworkStream); return GetValueTaskForSendReceive(SendAsync(saea), saea, fromNetworkStream, isReceive: false); } else @@ -388,7 +389,7 @@ namespace System.Net.Sockets /// Implements Task-returning SendAsync on top of Begin/EndSend. private Task SendAsyncApm(ReadOnlyMemory buffer, SocketFlags socketFlags) { - if (buffer.DangerousTryGetArray(out ArraySegment bufferArray)) + if (MemoryMarshal.TryGetArray(buffer, out ArraySegment bufferArray)) { var tcs = new TaskCompletionSource(this); BeginSend(bufferArray.Array, bufferArray.Offset, bufferArray.Count, socketFlags, iar => @@ -521,7 +522,7 @@ namespace System.Net.Sockets // so as to minimize overhead if the same buffers are used for subsequent operations (which is likely). // But SAEA doesn't support having both a buffer and a buffer list configured, so clear out a buffer // if there is one before we set the desired buffer list. - if (saea.Buffer != null) saea.SetBuffer(null, 0, 0); + if (!saea.MemoryBuffer.Equals(default)) saea.SetBuffer(default); saea.BufferList = buffers; saea.SocketFlags = socketFlags; } diff --git a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/Socket.cs.REMOVED.git-id b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/Socket.cs.REMOVED.git-id index 28a4fe4897..21302f789f 100644 --- a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/Socket.cs.REMOVED.git-id +++ b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/Socket.cs.REMOVED.git-id @@ -1 +1 @@ -b7edd69b9b150d3c51ac0b5efb82a99dbf85290e \ No newline at end of file +10bd970a50210617dd5f2e7f0b1355084b3ba7a2 \ No newline at end of file diff --git a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs index db4604fe41..2321c9c297 100644 --- a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs +++ b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs @@ -5,6 +5,8 @@ using Microsoft.Win32.SafeHandles; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading; namespace System.Net.Sockets @@ -12,7 +14,7 @@ namespace System.Net.Sockets // Note on asynchronous behavior here: // The asynchronous socket operations here generally do the following: - // (1) If the operation queue is empty, try to perform the operation immediately, non-blocking. + // (1) If the operation queue is Ready (queue is empty), try to perform the operation immediately, non-blocking. // If this completes (i.e. does not return EWOULDBLOCK), then we return the results immediately // for both success (SocketError.Success) or failure. // No callback will happen; callers are expected to handle these synchronous completions themselves. @@ -20,16 +22,92 @@ namespace System.Net.Sockets // appropriate queue and return SocketError.IOPending. // Enqueuing itself may fail because the socket is closed before the operation can be enqueued; // in this case, we return SocketError.OperationAborted (which matches what Winsock would return in this case). - // (3) When the queue completes the operation, it will post a work item to the threadpool - // to call the callback with results (either success or failure). + // (3) When we receive an epoll notification for the socket, we post a work item to the threadpool + // to perform the I/O and invoke the callback with the I/O result. // Synchronous operations generally do the same, except that instead of returning IOPending, // they block on an event handle until the operation is processed by the queue. - // Also, synchronous methods return SocketError.Interrupted when enqueuing fails - // (which again matches Winsock behavior). + + // See comments on OperationQueue below for more details of how the queue coordination works. internal sealed class SocketAsyncContext { + // Cached operation instances for operations commonly repeated on the same socket instance, + // e.g. async accepts, sends/receives with single and multiple buffers. More can be + // added in the future if necessary, at the expense of extra fields here. With a larger + // refactoring, these could also potentially be moved to SocketAsyncEventArgs, which + // would be more invasive but which would allow them to be reused across socket instances + // and also eliminate the interlocked necessary to rent the instances. + private AcceptOperation _cachedAcceptOperation; + private BufferMemoryReceiveOperation _cachedBufferMemoryReceiveOperation; + private BufferListReceiveOperation _cachedBufferListReceiveOperation; + private BufferMemorySendOperation _cachedBufferMemorySendOperation; + private BufferListSendOperation _cachedBufferListSendOperation; + + private void ReturnOperation(AcceptOperation operation) + { + operation.Reset(); + operation.Callback = null; + operation.SocketAddress = null; + Volatile.Write(ref _cachedAcceptOperation, operation); // benign race condition + } + + private void ReturnOperation(BufferMemoryReceiveOperation operation) + { + operation.Reset(); + operation.Buffer = default; + operation.Callback = null; + operation.SocketAddress = null; + Volatile.Write(ref _cachedBufferMemoryReceiveOperation, operation); // benign race condition + } + + private void ReturnOperation(BufferListReceiveOperation operation) + { + operation.Reset(); + operation.Buffers = null; + operation.Callback = null; + operation.SocketAddress = null; + Volatile.Write(ref _cachedBufferListReceiveOperation, operation); // benign race condition + } + + private void ReturnOperation(BufferMemorySendOperation operation) + { + operation.Reset(); + operation.Buffer = default; + operation.Callback = null; + operation.SocketAddress = null; + Volatile.Write(ref _cachedBufferMemorySendOperation, operation); // benign race condition + } + + private void ReturnOperation(BufferListSendOperation operation) + { + operation.Reset(); + operation.Buffers = null; + operation.Callback = null; + operation.SocketAddress = null; + Volatile.Write(ref _cachedBufferListSendOperation, operation); // benign race condition + } + + private AcceptOperation RentAcceptOperation() => + Interlocked.Exchange(ref _cachedAcceptOperation, null) ?? + new AcceptOperation(this); + + private BufferMemoryReceiveOperation RentBufferMemoryReceiveOperation() => + Interlocked.Exchange(ref _cachedBufferMemoryReceiveOperation, null) ?? + new BufferMemoryReceiveOperation(this); + + private BufferListReceiveOperation RentBufferListReceiveOperation() => + Interlocked.Exchange(ref _cachedBufferListReceiveOperation, null) ?? + new BufferListReceiveOperation(this); + + private BufferMemorySendOperation RentBufferMemorySendOperation() => + Interlocked.Exchange(ref _cachedBufferMemorySendOperation, null) ?? + new BufferMemorySendOperation(this); + + private BufferListSendOperation RentBufferListSendOperation() => + Interlocked.Exchange(ref _cachedBufferListSendOperation, null) ?? + new BufferListSendOperation(this); + private abstract class AsyncOperation { private enum State @@ -46,6 +124,7 @@ namespace System.Net.Sockets private int _callbackQueued; // When non-zero, the callback has been queued. #endif + public readonly SocketAsyncContext AssociatedContext; public AsyncOperation Next; protected object CallbackOrEvent; public SocketError ErrorCode; @@ -58,109 +137,152 @@ namespace System.Net.Sockets set { CallbackOrEvent = value; } } - public AsyncOperation() + public AsyncOperation(SocketAsyncContext context) + { + AssociatedContext = context; + Reset(); + } + + public void Reset() { _state = (int)State.Waiting; Next = this; +#if DEBUG + _callbackQueued = 0; +#endif } public bool TryComplete(SocketAsyncContext context) { - Debug.Assert(_state == (int)State.Waiting, $"Unexpected _state: {_state}"); + TraceWithContext(context, "Enter"); - return DoTryComplete(context); + bool result = DoTryComplete(context); + + TraceWithContext(context, $"Exit, result={result}"); + + return result; } - public bool TryCompleteAsync(SocketAsyncContext context) + public bool TrySetRunning() { - return TryCompleteOrAbortAsync(context, abort: false); - } - - public void AbortAsync() - { - bool completed = TryCompleteOrAbortAsync(null, abort: true); - Debug.Assert(completed, $"Expected TryCompleteOrAbortAsync to return true"); - } - - private bool TryCompleteOrAbortAsync(SocketAsyncContext context, bool abort) - { - Debug.Assert(context != null || abort, $"Unexpected values: context={context}, abort={abort}"); - State oldState = (State)Interlocked.CompareExchange(ref _state, (int)State.Running, (int)State.Waiting); if (oldState == State.Cancelled) { - // This operation has been cancelled. The canceller is responsible for - // correctly updating any state that would have been handled by - // AsyncOperation.Abort. - return true; + // This operation has already been cancelled, and had its completion processed. + // Simply return false to indicate no further processing is needed. + return false; } - Debug.Assert(oldState != State.Complete && oldState != State.Running, $"Unexpected oldState: {oldState}"); + Debug.Assert(oldState == (int)State.Waiting); + return true; + } - bool completed; - if (abort) + public bool SetComplete() + { + Debug.Assert(Volatile.Read(ref _state) == (int)State.Running); + + Volatile.Write(ref _state, (int)State.Complete); + + if (CallbackOrEvent is ManualResetEventSlim e) { - Abort(); - ErrorCode = SocketError.OperationAborted; - completed = true; + e.Set(); + + // No callback needed + return false; } else { - completed = DoTryComplete(context); - } - - if (completed) - { - var @event = CallbackOrEvent as ManualResetEventSlim; - if (@event != null) - { - @event.Set(); - } - else - { - Debug.Assert(_state != (int)State.Cancelled, $"Unexpected _state: {_state}"); #if DEBUG - Debug.Assert(Interlocked.CompareExchange(ref _callbackQueued, 1, 0) == 0, $"Unexpected _callbackQueued: {_callbackQueued}"); + Debug.Assert(Interlocked.CompareExchange(ref _callbackQueued, 1, 0) == 0, $"Unexpected _callbackQueued: {_callbackQueued}"); #endif - ThreadPool.QueueUserWorkItem(o => ((AsyncOperation)o).InvokeCallback(), this); - } - - Volatile.Write(ref _state, (int)State.Complete); + // Indicate callback is needed return true; } - - Volatile.Write(ref _state, (int)State.Waiting); - return false; } - public bool Wait(int timeout) + public void SetWaiting() { - if (Event.Wait(timeout)) - { - return true; - } + Debug.Assert(Volatile.Read(ref _state) == (int)State.Running); + Volatile.Write(ref _state, (int)State.Waiting); + } + + public void DoCallback() + { + InvokeCallback(); + } + + public bool TryCancel() + { + Trace("Enter"); + + // Try to transition from Waiting to Cancelled var spinWait = new SpinWait(); - for (;;) + bool keepWaiting = true; + while (keepWaiting) { int state = Interlocked.CompareExchange(ref _state, (int)State.Cancelled, (int)State.Waiting); switch ((State)state) { case State.Running: // A completion attempt is in progress. Keep busy-waiting. + Trace("Busy wait"); spinWait.SpinOnce(); break; case State.Complete: // A completion attempt succeeded. Consider this operation as having completed within the timeout. - return true; + Trace("Exit, previously completed"); + return false; case State.Waiting: // This operation was successfully cancelled. - return false; + // Break out of the loop to handle the cancellation + keepWaiting = false; + break; + + case State.Cancelled: + // Someone else cancelled the operation. + // Just return true to indicate the operation was cancelled. + // The previous canceller will have fired the completion, etc. + Trace("Exit, previously cancelled"); + return true; } } + + Trace("Cancelled, processing completion"); + + // The operation successfully cancelled. + // It's our responsibility to set the error code and queue the completion. + DoAbort(); + + var @event = CallbackOrEvent as ManualResetEventSlim; + if (@event != null) + { + @event.Set(); + } + else + { +#if DEBUG + Debug.Assert(Interlocked.CompareExchange(ref _callbackQueued, 1, 0) == 0, $"Unexpected _callbackQueued: {_callbackQueued}"); +#endif + + ThreadPool.QueueUserWorkItem(o => ((AsyncOperation)o).InvokeCallback(), this); + } + + Trace("Exit"); + + // Note, we leave the operation in the OperationQueue. + // When we get around to processing it, we'll see it's cancelled and skip it. + return true; + } + + // Called when op is not in the queue yet, so can't be otherwise executing + public void DoAbort() + { + Abort(); + ErrorCode = SocketError.OperationAborted; } protected abstract void Abort(); @@ -168,16 +290,30 @@ namespace System.Net.Sockets protected abstract bool DoTryComplete(SocketAsyncContext context); protected abstract void InvokeCallback(); + + [Conditional("SOCKETASYNCCONTEXT_TRACE")] + public void Trace(string message, [CallerMemberName] string memberName = null) + { + OutputTrace($"{IdOf(this)}.{memberName}: {message}"); + } + + [Conditional("SOCKETASYNCCONTEXT_TRACE")] + public void TraceWithContext(SocketAsyncContext context, string message, [CallerMemberName] string memberName = null) + { + OutputTrace($"{IdOf(context)}, {IdOf(this)}.{memberName}: {message}"); + } } // These two abstract classes differentiate the operations that go in the // read queue vs the ones that go in the write queue. private abstract class ReadOperation : AsyncOperation { + public ReadOperation(SocketAsyncContext context) : base(context) { } } private abstract class WriteOperation : AsyncOperation { + public WriteOperation(SocketAsyncContext context) : base(context) { } } private abstract class SendOperation : WriteOperation @@ -187,6 +323,8 @@ namespace System.Net.Sockets public int Offset; public int Count; + public SendOperation(SocketAsyncContext context) : base(context) { } + protected sealed override void Abort() { } public Action Callback @@ -194,18 +332,33 @@ namespace System.Net.Sockets set => CallbackOrEvent = value; } - protected sealed override void InvokeCallback() => + protected override void InvokeCallback() => ((Action)CallbackOrEvent)(BytesTransferred, SocketAddress, SocketAddressLen, SocketFlags.None, ErrorCode); } - private sealed class BufferArraySendOperation : SendOperation + private sealed class BufferMemorySendOperation : SendOperation { - public byte[] Buffer; + public Memory Buffer; + + public BufferMemorySendOperation(SocketAsyncContext context) : base(context) { } protected override bool DoTryComplete(SocketAsyncContext context) { int bufferIndex = 0; - return SocketPal.TryCompleteSendTo(context._socket, Buffer, null, ref bufferIndex, ref Offset, ref Count, Flags, SocketAddress, SocketAddressLen, ref BytesTransferred, out ErrorCode); + return SocketPal.TryCompleteSendTo(context._socket, Buffer.Span, null, ref bufferIndex, ref Offset, ref Count, Flags, SocketAddress, SocketAddressLen, ref BytesTransferred, out ErrorCode); + } + + protected override void InvokeCallback() + { + var cb = (Action)CallbackOrEvent; + int bt = BytesTransferred; + byte[] sa = SocketAddress; + int sal = SocketAddressLen; + SocketError ec = ErrorCode; + + AssociatedContext.ReturnOperation(this); + + cb(bt, sa, sal, SocketFlags.None, ec); } } @@ -214,16 +367,33 @@ namespace System.Net.Sockets public IList> Buffers; public int BufferIndex; + public BufferListSendOperation(SocketAsyncContext context) : base(context) { } + protected override bool DoTryComplete(SocketAsyncContext context) { return SocketPal.TryCompleteSendTo(context._socket, default(ReadOnlySpan), Buffers, ref BufferIndex, ref Offset, ref Count, Flags, SocketAddress, SocketAddressLen, ref BytesTransferred, out ErrorCode); } + + protected override void InvokeCallback() + { + var cb = (Action)CallbackOrEvent; + int bt = BytesTransferred; + byte[] sa = SocketAddress; + int sal = SocketAddressLen; + SocketError ec = ErrorCode; + + AssociatedContext.ReturnOperation(this); + + cb(bt, sa, sal, SocketFlags.None, ec); + } } private sealed unsafe class BufferPtrSendOperation : SendOperation { public byte* BufferPtr; + public BufferPtrSendOperation(SocketAsyncContext context) : base(context) { } + protected override bool DoTryComplete(SocketAsyncContext context) { int bufferIndex = 0; @@ -237,6 +407,8 @@ namespace System.Net.Sockets public SocketFlags ReceivedFlags; public int BytesTransferred; + public ReceiveOperation(SocketAsyncContext context) : base(context) { } + protected sealed override void Abort() { } public Action Callback @@ -244,27 +416,57 @@ namespace System.Net.Sockets set => CallbackOrEvent = value; } - protected sealed override void InvokeCallback() => + protected override void InvokeCallback() => ((Action)CallbackOrEvent)( BytesTransferred, SocketAddress, SocketAddressLen, ReceivedFlags, ErrorCode); } - private sealed class BufferArrayReceiveOperation : ReceiveOperation + private sealed class BufferMemoryReceiveOperation : ReceiveOperation { - public byte[] Buffer; - public int Offset; - public int Count; + public Memory Buffer; + + public BufferMemoryReceiveOperation(SocketAsyncContext context) : base(context) { } protected override bool DoTryComplete(SocketAsyncContext context) => - SocketPal.TryCompleteReceiveFrom(context._socket, new Span(Buffer, Offset, Count), null, Flags, SocketAddress, ref SocketAddressLen, out BytesTransferred, out ReceivedFlags, out ErrorCode); + SocketPal.TryCompleteReceiveFrom(context._socket, Buffer.Span, null, Flags, SocketAddress, ref SocketAddressLen, out BytesTransferred, out ReceivedFlags, out ErrorCode); + + protected override void InvokeCallback() + { + var cb = (Action)CallbackOrEvent; + int bt = BytesTransferred; + byte[] sa = SocketAddress; + int sal = SocketAddressLen; + SocketFlags rf = ReceivedFlags; + SocketError ec = ErrorCode; + + AssociatedContext.ReturnOperation(this); + + cb(bt, sa, sal, rf, ec); + } } private sealed class BufferListReceiveOperation : ReceiveOperation { public IList> Buffers; + public BufferListReceiveOperation(SocketAsyncContext context) : base(context) { } + protected override bool DoTryComplete(SocketAsyncContext context) => SocketPal.TryCompleteReceiveFrom(context._socket, default(Span), Buffers, Flags, SocketAddress, ref SocketAddressLen, out BytesTransferred, out ReceivedFlags, out ErrorCode); + + protected override void InvokeCallback() + { + var cb = (Action)CallbackOrEvent; + int bt = BytesTransferred; + byte[] sa = SocketAddress; + int sal = SocketAddressLen; + SocketFlags rf = ReceivedFlags; + SocketError ec = ErrorCode; + + AssociatedContext.ReturnOperation(this); + + cb(bt, sa, sal, rf, ec); + } } private sealed unsafe class BufferPtrReceiveOperation : ReceiveOperation @@ -272,15 +474,15 @@ namespace System.Net.Sockets public byte* BufferPtr; public int Length; + public BufferPtrReceiveOperation(SocketAsyncContext context) : base(context) { } + protected override bool DoTryComplete(SocketAsyncContext context) => SocketPal.TryCompleteReceiveFrom(context._socket, new Span(BufferPtr, Length), null, Flags, SocketAddress, ref SocketAddressLen, out BytesTransferred, out ReceivedFlags, out ErrorCode); } private sealed class ReceiveMessageFromOperation : ReadOperation { - public byte[] Buffer; - public int Offset; - public int Count; + public Memory Buffer; public SocketFlags Flags; public int BytesTransferred; public SocketFlags ReceivedFlags; @@ -290,6 +492,8 @@ namespace System.Net.Sockets public bool IsIPv6; public IPPacketInformation IPPacketInformation; + public ReceiveMessageFromOperation(SocketAsyncContext context) : base(context) { } + protected sealed override void Abort() { } public Action Callback @@ -298,7 +502,7 @@ namespace System.Net.Sockets } protected override bool DoTryComplete(SocketAsyncContext context) => - SocketPal.TryCompleteReceiveMessageFrom(context._socket, Buffer, Buffers, Offset, Count, Flags, SocketAddress, ref SocketAddressLen, IsIPv4, IsIPv6, out BytesTransferred, out ReceivedFlags, out IPPacketInformation, out ErrorCode); + SocketPal.TryCompleteReceiveMessageFrom(context._socket, Buffer.Span, Buffers, Flags, SocketAddress, ref SocketAddressLen, IsIPv4, IsIPv6, out BytesTransferred, out ReceivedFlags, out IPPacketInformation, out ErrorCode); protected override void InvokeCallback() => ((Action)CallbackOrEvent)( @@ -309,6 +513,8 @@ namespace System.Net.Sockets { public IntPtr AcceptedFileDescriptor; + public AcceptOperation(SocketAsyncContext context) : base(context) { } + public Action Callback { set => CallbackOrEvent = value; @@ -324,13 +530,24 @@ namespace System.Net.Sockets return completed; } - protected override void InvokeCallback() => - ((Action)CallbackOrEvent)( - AcceptedFileDescriptor, SocketAddress, SocketAddressLen, ErrorCode); + protected override void InvokeCallback() + { + var cb = (Action)CallbackOrEvent; + IntPtr fd = AcceptedFileDescriptor; + byte[] sa = SocketAddress; + int sal = SocketAddressLen; + SocketError ec = ErrorCode; + + AssociatedContext.ReturnOperation(this); + + cb(fd, sa, sal, ec); + } } private sealed class ConnectOperation : WriteOperation { + public ConnectOperation(SocketAsyncContext context) : base(context) { } + public Action Callback { set => CallbackOrEvent = value; @@ -356,6 +573,8 @@ namespace System.Net.Sockets public long Count; public long BytesTransferred; + public SendFileOperation(SocketAsyncContext context) : base(context) { } + protected override void Abort() { } public Action Callback @@ -370,118 +589,408 @@ namespace System.Net.Sockets SocketPal.TryCompleteSendFile(context._socket, FileHandle, ref Offset, ref Count, ref BytesTransferred, out ErrorCode); } - private enum QueueState + // In debug builds, this struct guards against: + // (1) Unexpected lock reentrancy, which should never happen + // (2) Deadlock, by setting a reasonably large timeout + private readonly struct LockToken : IDisposable { - Clear = 0, - Set = 1, - Stopped = 2, + private readonly object _lockObject; + + public LockToken(object lockObject) + { + Debug.Assert(lockObject != null); + + _lockObject = lockObject; + + Debug.Assert(!Monitor.IsEntered(_lockObject)); + +#if DEBUG + bool success = Monitor.TryEnter(_lockObject, 10000); + Debug.Assert(success, "Timed out waiting for queue lock"); +#else + Monitor.Enter(_lockObject); +#endif + } + + public void Dispose() + { + Debug.Assert(Monitor.IsEntered(_lockObject)); + Monitor.Exit(_lockObject); + } } private struct OperationQueue where TOperation : AsyncOperation { - private object _queueLock; - private AsyncOperation _tail; + // Quick overview: + // + // When attempting to perform an IO operation, the caller first checks IsReady, + // and if true, attempts to perform the operation itself. + // If this returns EWOULDBLOCK, or if the queue was not ready, then the operation + // is enqueued by calling StartAsyncOperation and the state becomes Waiting. + // When an epoll notification is received, we check if the state is Waiting, + // and if so, change the state to Processing and enqueue a workitem to the threadpool + // to try to perform the enqueued operations. + // If an operation is successfully performed, we remove it from the queue, + // enqueue another threadpool workitem to process the next item in the queue (if any), + // and call the user's completion callback. + // If we successfully process all enqueued operations, then the state becomes Ready; + // otherwise, the state becomes Waiting and we wait for another epoll notification. - public QueueState State { get; set; } - public bool IsStopped { get { return State == QueueState.Stopped; } } - public bool IsEmpty { get { return _tail == null; } } - public object QueueLock { get { return _queueLock; } } + private enum QueueState : byte + { + Ready = 0, // Indicates that data MAY be available on the socket. + // Queue must be empty. + Waiting = 1, // Indicates that data is definitely not available on the socket. + // Queue must not be empty. + Processing = 2, // Indicates that a thread pool item has been scheduled (and may + // be executing) to process the IO operations in the queue. + // Queue must not be empty. + Stopped = 3, // Indicates that the queue has been stopped because the + // socket has been closed. + // Queue must be empty. + } + + // These fields define the queue state. + + private QueueState _state; // See above + private int _sequenceNumber; // This sequence number is updated when we receive an epoll notification. + // It allows us to detect when a new epoll notification has arrived + // since the last time we checked the state of the queue. + // If this happens, we MUST retry the operation, otherwise we risk + // "losing" the notification and causing the operation to pend indefinitely. + private AsyncOperation _tail; // Queue of pending IO operations to process when data becomes available. + + // The _queueLock is used to ensure atomic access to the queue state above. + // The lock is only ever held briefly, to read and/or update queue state, and + // never around any external call, e.g. OS call or user code invocation. + private object _queueLock; + + private LockToken Lock() => new LockToken(_queueLock); + + private static readonly WaitCallback s_processingCallback = + typeof(TOperation) == typeof(ReadOperation) ? ((o) => { var context = ((SocketAsyncContext)o); context._receiveQueue.ProcessQueue(context); }) : + typeof(TOperation) == typeof(WriteOperation) ? ((o) => { var context = ((SocketAsyncContext)o); context._sendQueue.ProcessQueue(context); }) : + (WaitCallback)null; public void Init() { Debug.Assert(_queueLock == null); _queueLock = new object(); + + _state = QueueState.Ready; + _sequenceNumber = 0; } - public void Enqueue(TOperation operation) + // IsReady returns the current _sequenceNumber, which must be passed to StartAsyncOperation below. + public bool IsReady(SocketAsyncContext context, out int observedSequenceNumber) { - Debug.Assert(!IsStopped, "Expected !IsStopped"); - Debug.Assert(operation.Next == operation, "Expected operation.Next == operation"); - - if (!IsEmpty) + using (Lock()) { - operation.Next = _tail.Next; - _tail.Next = operation; - } + observedSequenceNumber = _sequenceNumber; + bool isReady = (_state == QueueState.Ready); - _tail = operation; - } + Trace(context, $"{isReady}"); - private bool TryDequeue(out TOperation operation) - { - if (_tail == null) - { - operation = null; - return false; - } - - AsyncOperation head = _tail.Next; - if (head == _tail) - { - _tail = null; - } - else - { - _tail.Next = head.Next; - } - - head.Next = null; - operation = (TOperation)head; - return true; - } - - private void Requeue(TOperation operation) - { - // Insert at the head of the queue - Debug.Assert(!IsStopped, "Expected !IsStopped"); - Debug.Assert(operation.Next == null, "Operation already in queue"); - - if (IsEmpty) - { - operation.Next = operation; - _tail = operation; - } - else - { - operation.Next = _tail.Next; - _tail.Next = operation; + return isReady; } } - public void Complete(SocketAsyncContext context) + // Return true for pending, false for completed synchronously (including failure and abort) + public bool StartAsyncOperation(SocketAsyncContext context, TOperation operation, int observedSequenceNumber) { - lock (_queueLock) + Trace(context, $"Enter"); + + if (!context._registered) { - if (IsStopped) - return; + context.Register(); + } - State = QueueState.Set; - - TOperation op; - while (TryDequeue(out op)) + while (true) + { + bool doAbort = false; + using (Lock()) { - if (!op.TryCompleteAsync(context)) + switch (_state) { - Requeue(op); - return; + case QueueState.Ready: + if (observedSequenceNumber != _sequenceNumber) + { + // The queue has become ready again since we previously checked it. + // So, we need to retry the operation before we enqueue it. + Debug.Assert(observedSequenceNumber - _sequenceNumber < 10000, "Very large sequence number increase???"); + observedSequenceNumber = _sequenceNumber; + break; + } + + // Caller tried the operation and got an EWOULDBLOCK, so we need to transition. + _state = QueueState.Waiting; + goto case QueueState.Waiting; + + case QueueState.Waiting: + case QueueState.Processing: + // Enqueue the operation. + Debug.Assert(operation.Next == operation, "Expected operation.Next == operation"); + + if (_tail != null) + { + operation.Next = _tail.Next; + _tail.Next = operation; + } + + _tail = operation; + + Trace(context, $"Leave, enqueued {IdOf(operation)}"); + return true; + + case QueueState.Stopped: + Debug.Assert(_tail == null); + doAbort = true; + break; + + default: + Environment.FailFast("unexpected queue state"); + break; } } + + if (doAbort) + { + operation.DoAbort(); + Trace(context, $"Leave, queue stopped"); + return false; + } + + // Retry the operation. + if (operation.TryComplete(context)) + { + Trace(context, $"Leave, retry succeeded"); + return false; + } } } - public void StopAndAbort() + // Called on the epoll thread whenever we receive an epoll notification. + public void HandleEvent(SocketAsyncContext context) { - lock (_queueLock) + using (Lock()) { - State = QueueState.Stopped; + Trace(context, $"Enter"); - TOperation op; - while (TryDequeue(out op)) + switch (_state) { - op.AbortAsync(); + case QueueState.Ready: + Debug.Assert(_tail == null, "State == Ready but queue is not empty!"); + _sequenceNumber++; + Trace(context, $"Exit (previously ready)"); + return; + + case QueueState.Waiting: + Debug.Assert(_tail != null, "State == Waiting but queue is empty!"); + _state = QueueState.Processing; + // Break out and release lock + break; + + case QueueState.Processing: + Debug.Assert(_tail != null, "State == Processing but queue is empty!"); + _sequenceNumber++; + Trace(context, $"Exit (currently processing)"); + return; + + case QueueState.Stopped: + Debug.Assert(_tail == null); + Trace(context, $"Exit (stopped)"); + return; + + default: + Environment.FailFast("unexpected queue state"); + return; } } + + // We just transitioned from Waiting to Processing. + // Spawn a work item to do the actual processing. + ThreadPool.QueueUserWorkItem(s_processingCallback, context); + } + + // Called on the threadpool when data may be available. + public void ProcessQueue(SocketAsyncContext context) + { + int observedSequenceNumber; + AsyncOperation op; + using (Lock()) + { + Trace(context, $"Enter"); + + if (_state == QueueState.Stopped) + { + Debug.Assert(_tail == null); + Trace(context, $"Exit (stopped)"); + return; + } + else + { + Debug.Assert(_state == QueueState.Processing, $"_state={_state} while processing queue!"); + Debug.Assert(_tail != null, "Unexpected empty queue while processing I/O"); + observedSequenceNumber = _sequenceNumber; + op = _tail.Next; // head of queue + } + } + + bool needCallback = false; + AsyncOperation nextOp; + while (true) + { + bool wasCompleted = false; + + // Try to change the op state to Running. + // If this fails, it means the operation was previously cancelled, + // and we should just remove it from the queue without further processing. + bool isRunning = op.TrySetRunning(); + if (isRunning) + { + // Try to perform the IO + wasCompleted = op.TryComplete(context); + if (wasCompleted) + { + needCallback = op.SetComplete(); + } + else + { + op.SetWaiting(); + } + } + + nextOp = null; + if (wasCompleted || !isRunning) + { + // Remove the op from the queue and see if there's more to process. + + using (Lock()) + { + if (_state == QueueState.Stopped) + { + Debug.Assert(_tail == null); + Trace(context, $"Exit (stopped)"); + } + else + { + Debug.Assert(_state == QueueState.Processing, $"_state={_state} while processing queue!"); + Debug.Assert(_tail.Next == op, "Queue modified while processing queue"); + + if (op == _tail) + { + // No more operations to process + _tail = null; + _state = QueueState.Ready; + _sequenceNumber++; + Trace(context, $"Exit (finished queue)"); + } + else + { + // Pop current operation and advance to next + nextOp = _tail.Next = op.Next; + } + } + } + } + else + { + // Check for retry and reset queue state. + + using (Lock()) + { + if (_state == QueueState.Stopped) + { + Debug.Assert(_tail == null); + Trace(context, $"Exit (stopped)"); + } + else + { + Debug.Assert(_state == QueueState.Processing, $"_state={_state} while processing queue!"); + + if (observedSequenceNumber != _sequenceNumber) + { + // We received another epoll notification since we previously checked it. + // So, we need to retry the operation. + Debug.Assert(observedSequenceNumber - _sequenceNumber < 10000, "Very large sequence number increase???"); + observedSequenceNumber = _sequenceNumber; + nextOp = op; + } + else + { + _state = QueueState.Waiting; + Trace(context, $"Exit (received EAGAIN)"); + } + } + } + } + + if (needCallback || nextOp == null) + { + break; + } + + op = nextOp; + } + + if (needCallback) + { + if (nextOp != null) + { + Debug.Assert(_state == QueueState.Processing); + + // Spawn a new work item to continue processing the queue. + ThreadPool.QueueUserWorkItem(s_processingCallback, context); + } + + op.DoCallback(); + } + else + { + Debug.Assert(nextOp == null); + } + } + + // Called when the socket is closed. + public void StopAndAbort(SocketAsyncContext context) + { + // We should be called exactly once, by SafeCloseSocket. + Debug.Assert(_state != QueueState.Stopped); + + using (Lock()) + { + Trace(context, $"Enter"); + + Debug.Assert(_state != QueueState.Stopped); + + _state = QueueState.Stopped; + + if (_tail != null) + { + AsyncOperation op = _tail; + do + { + op.TryCancel(); + op = op.Next; + } while (op != _tail); + } + + _tail = null; + + Trace(context, $"Exit"); + } + } + + [Conditional("SOCKETASYNCCONTEXT_TRACE")] + public void Trace(SocketAsyncContext context, string message, [CallerMemberName] string memberName = null) + { + string queueType = + typeof(TOperation) == typeof(ReadOperation) ? "recv" : + typeof(TOperation) == typeof(WriteOperation) ? "send" : + "???"; + + OutputTrace($"{IdOf(context)}-{queueType}.{memberName}: {message}, {_state}-{_sequenceNumber}, {((_tail == null) ? "empty" : "not empty")}"); } } @@ -489,7 +998,7 @@ namespace System.Net.Sockets private OperationQueue _receiveQueue; private OperationQueue _sendQueue; private SocketAsyncEngine.Token _asyncEngineToken; - private Interop.Sys.SocketEvents _registeredEvents; + private bool _registered; private bool _nonBlockingSet; private readonly object _registerLock = new object(); @@ -502,41 +1011,46 @@ namespace System.Net.Sockets _sendQueue.Init(); } - private void Register(Interop.Sys.SocketEvents events) + private void Register() { + // Note, on OSX, this is not always true because in certain cases, + // the socket can already be in non-blocking mode even though we didn't set that ourselves. + // TODO: Track down exactly why this is + // Debug.Assert(_nonBlockingSet); lock (_registerLock) { - Debug.Assert((_registeredEvents & events) == Interop.Sys.SocketEvents.None, $"Unexpected values: _registeredEvents={_registeredEvents}, events={events}"); - - if (!_asyncEngineToken.WasAllocated) + if (!_registered) { - _asyncEngineToken = new SocketAsyncEngine.Token(this); - } + Debug.Assert(!_asyncEngineToken.WasAllocated); + var token = new SocketAsyncEngine.Token(this); - events |= _registeredEvents; - - Interop.Error errorCode; - if (!_asyncEngineToken.TryRegister(_socket, _registeredEvents, events, out errorCode)) - { - if (errorCode == Interop.Error.ENOMEM || errorCode == Interop.Error.ENOSPC) + Interop.Error errorCode; + if (!token.TryRegister(_socket, out errorCode)) { - throw new OutOfMemoryException(); + token.Free(); + if (errorCode == Interop.Error.ENOMEM || errorCode == Interop.Error.ENOSPC) + { + throw new OutOfMemoryException(); + } + else + { + throw new InternalException(); + } } - else - { - throw new InternalException(); - } - } - _registeredEvents = events; + _asyncEngineToken = token; + _registered = true; + + Trace("Registered"); + } } } public void Close() { // Drain queues - _sendQueue.StopAndAbort(); - _receiveQueue.StopAndAbort(); + _sendQueue.StopAndAbort(this); + _receiveQueue.StopAndAbort(this); lock (_registerLock) { @@ -568,39 +1082,31 @@ namespace System.Net.Sockets } } - private bool TryBeginOperation(ref OperationQueue queue, TOperation operation, Interop.Sys.SocketEvents events, bool maintainOrder, out bool isStopped) + private void PerformSyncOperation(ref OperationQueue queue, TOperation operation, int timeout, int observedSequenceNumber) where TOperation : AsyncOperation { - // Exactly one of the two queue locks must be held by the caller - Debug.Assert(Monitor.IsEntered(_sendQueue.QueueLock) ^ Monitor.IsEntered(_receiveQueue.QueueLock)); - - switch (queue.State) + using (var e = new ManualResetEventSlim(false, 0)) { - case QueueState.Stopped: - isStopped = true; - return false; + operation.Event = e; - case QueueState.Clear: - break; + if (!queue.StartAsyncOperation(this, operation, observedSequenceNumber)) + { + // Completed synchronously + return; + } - case QueueState.Set: - if (queue.IsEmpty || !maintainOrder) - { - isStopped = false; - queue.State = QueueState.Clear; - return false; - } - break; + if (e.Wait(timeout)) + { + // Completed within timeout + return; + } + + bool cancelled = operation.TryCancel(); + if (cancelled) + { + operation.ErrorCode = SocketError.TimedOut; + } } - - if ((_registeredEvents & events) == Interop.Sys.SocketEvents.None) - { - Register(events); - } - - queue.Enqueue(operation); - isStopped = false; - return true; } public SocketError Accept(byte[] socketAddress, ref int socketAddressLen, int timeout, out IntPtr acceptedFd) @@ -610,55 +1116,25 @@ namespace System.Net.Sockets Debug.Assert(timeout == -1 || timeout > 0, $"Unexpected timeout: {timeout}"); SocketError errorCode; - if (SocketPal.TryCompleteAccept(_socket, socketAddress, ref socketAddressLen, out acceptedFd, out errorCode)) + int observedSequenceNumber; + if (_receiveQueue.IsReady(this, out observedSequenceNumber) && + SocketPal.TryCompleteAccept(_socket, socketAddress, ref socketAddressLen, out acceptedFd, out errorCode)) { Debug.Assert(errorCode == SocketError.Success || acceptedFd == (IntPtr)(-1), $"Unexpected values: errorCode={errorCode}, acceptedFd={acceptedFd}"); return errorCode; } - using (var @event = new ManualResetEventSlim(false, 0)) + var operation = new AcceptOperation(this) { - var operation = new AcceptOperation { - Event = @event, - SocketAddress = socketAddress, - SocketAddressLen = socketAddressLen - }; + SocketAddress = socketAddress, + SocketAddressLen = socketAddressLen, + }; - bool isStopped; - while (true) - { - lock (_receiveQueue.QueueLock) - { - if (TryBeginOperation(ref _receiveQueue, operation, Interop.Sys.SocketEvents.Read, maintainOrder: false, isStopped: out isStopped)) - { - break; - } - } + PerformSyncOperation(ref _receiveQueue, operation, timeout, observedSequenceNumber); - if (isStopped) - { - acceptedFd = (IntPtr)(-1); - return SocketError.Interrupted; - } - - if (operation.TryComplete(this)) - { - socketAddressLen = operation.SocketAddressLen; - acceptedFd = operation.AcceptedFileDescriptor; - return operation.ErrorCode; - } - } - - if (!operation.Wait(timeout)) - { - acceptedFd = (IntPtr)(-1); - return SocketError.TimedOut; - } - - socketAddressLen = operation.SocketAddressLen; - acceptedFd = operation.AcceptedFileDescriptor; - return operation.ErrorCode; - } + socketAddressLen = operation.SocketAddressLen; + acceptedFd = operation.AcceptedFileDescriptor; + return operation.ErrorCode; } public SocketError AcceptAsync(byte[] socketAddress, ref int socketAddressLen, out IntPtr acceptedFd, Action callback) @@ -670,42 +1146,31 @@ namespace System.Net.Sockets SetNonBlocking(); SocketError errorCode; - if (SocketPal.TryCompleteAccept(_socket, socketAddress, ref socketAddressLen, out acceptedFd, out errorCode)) + int observedSequenceNumber; + if (_receiveQueue.IsReady(this, out observedSequenceNumber) && + SocketPal.TryCompleteAccept(_socket, socketAddress, ref socketAddressLen, out acceptedFd, out errorCode)) { Debug.Assert(errorCode == SocketError.Success || acceptedFd == (IntPtr)(-1), $"Unexpected values: errorCode={errorCode}, acceptedFd={acceptedFd}"); return errorCode; } - var operation = new AcceptOperation { - Callback = callback, - SocketAddress = socketAddress, - SocketAddressLen = socketAddressLen - }; + AcceptOperation operation = RentAcceptOperation(); + operation.Callback = callback; + operation.SocketAddress = socketAddress; + operation.SocketAddressLen = socketAddressLen; - bool isStopped; - while (true) + if (!_receiveQueue.StartAsyncOperation(this, operation, observedSequenceNumber)) { - lock (_receiveQueue.QueueLock) - { - if (TryBeginOperation(ref _receiveQueue, operation, Interop.Sys.SocketEvents.Read, maintainOrder: false, isStopped: out isStopped)) - { - break; - } - } + socketAddressLen = operation.SocketAddressLen; + acceptedFd = operation.AcceptedFileDescriptor; + errorCode = operation.ErrorCode; - if (isStopped) - { - return SocketError.OperationAborted; - } - - if (operation.TryComplete(this)) - { - socketAddressLen = operation.SocketAddressLen; - acceptedFd = operation.AcceptedFileDescriptor; - return operation.ErrorCode; - } + ReturnOperation(operation); + return errorCode; } + + acceptedFd = (IntPtr)(-1); return SocketError.IOPending; } @@ -715,45 +1180,28 @@ namespace System.Net.Sockets Debug.Assert(socketAddressLen > 0, $"Unexpected socketAddressLen: {socketAddressLen}"); Debug.Assert(timeout == -1 || timeout > 0, $"Unexpected timeout: {timeout}"); + // Connect is different than the usual "readiness" pattern of other operations. + // We need to call TryStartConnect to initiate the connect with the OS, + // before we try to complete it via epoll notification. + // Thus, always call TryStartConnect regardless of readiness. SocketError errorCode; + int observedSequenceNumber; + _sendQueue.IsReady(this, out observedSequenceNumber); if (SocketPal.TryStartConnect(_socket, socketAddress, socketAddressLen, out errorCode)) { _socket.RegisterConnectResult(errorCode); return errorCode; } - using (var @event = new ManualResetEventSlim(false, 0)) + var operation = new ConnectOperation(this) { - var operation = new ConnectOperation { - Event = @event, - SocketAddress = socketAddress, - SocketAddressLen = socketAddressLen - }; + SocketAddress = socketAddress, + SocketAddressLen = socketAddressLen + }; - bool isStopped; - while (true) - { - lock (_sendQueue.QueueLock) - { - if (TryBeginOperation(ref _sendQueue, operation, Interop.Sys.SocketEvents.Write, maintainOrder: false, isStopped: out isStopped)) - { - break; - } - } + PerformSyncOperation(ref _sendQueue, operation, timeout, observedSequenceNumber); - if (isStopped) - { - return SocketError.Interrupted; - } - - if (operation.TryComplete(this)) - { - return operation.ErrorCode; - } - } - - return operation.Wait(timeout) ? operation.ErrorCode : SocketError.TimedOut; - } + return operation.ErrorCode; } public SocketError ConnectAsync(byte[] socketAddress, int socketAddressLen, Action callback) @@ -764,48 +1212,37 @@ namespace System.Net.Sockets SetNonBlocking(); + // Connect is different than the usual "readiness" pattern of other operations. + // We need to initiate the connect before we try to complete it. + // Thus, always call TryStartConnect regardless of readiness. SocketError errorCode; + int observedSequenceNumber; + _sendQueue.IsReady(this, out observedSequenceNumber); if (SocketPal.TryStartConnect(_socket, socketAddress, socketAddressLen, out errorCode)) { _socket.RegisterConnectResult(errorCode); - return errorCode; } - var operation = new ConnectOperation { + var operation = new ConnectOperation(this) + { Callback = callback, SocketAddress = socketAddress, SocketAddressLen = socketAddressLen }; - bool isStopped; - while (true) + if (!_sendQueue.StartAsyncOperation(this, operation, observedSequenceNumber)) { - lock (_sendQueue.QueueLock) - { - if (TryBeginOperation(ref _sendQueue, operation, Interop.Sys.SocketEvents.Write, maintainOrder: false, isStopped: out isStopped)) - { - break; - } - } - - if (isStopped) - { - return SocketError.OperationAborted; - } - - if (operation.TryComplete(this)) - { - return operation.ErrorCode; - } + return operation.ErrorCode; } + return SocketError.IOPending; } - public SocketError Receive(byte[] buffer, int offset, int count, ref SocketFlags flags, int timeout, out int bytesReceived) + public SocketError Receive(Memory buffer, ref SocketFlags flags, int timeout, out int bytesReceived) { int socketAddressLen = 0; - return ReceiveFrom(buffer, offset, count, ref flags, null, ref socketAddressLen, timeout, out bytesReceived); + return ReceiveFrom(buffer, ref flags, null, ref socketAddressLen, timeout, out bytesReceived); } public SocketError Receive(Span buffer, ref SocketFlags flags, int timeout, out int bytesReceived) @@ -814,196 +1251,106 @@ namespace System.Net.Sockets return ReceiveFrom(buffer, ref flags, null, ref socketAddressLen, timeout, out bytesReceived); } - public SocketError ReceiveAsync(byte[] buffer, int offset, int count, SocketFlags flags, out int bytesReceived, out SocketFlags receivedFlags, Action callback) + public SocketError ReceiveAsync(Memory buffer, SocketFlags flags, out int bytesReceived, out SocketFlags receivedFlags, Action callback) { int socketAddressLen = 0; - return ReceiveFromAsync(buffer, offset, count, flags, null, ref socketAddressLen, out bytesReceived, out receivedFlags, callback); + return ReceiveFromAsync(buffer, flags, null, ref socketAddressLen, out bytesReceived, out receivedFlags, callback); } - public SocketError ReceiveFrom(byte[] buffer, int offset, int count, ref SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, int timeout, out int bytesReceived) + public SocketError ReceiveFrom(Memory buffer, ref SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, int timeout, out int bytesReceived) { Debug.Assert(timeout == -1 || timeout > 0, $"Unexpected timeout: {timeout}"); - ManualResetEventSlim @event = null; - try + SocketFlags receivedFlags; + SocketError errorCode; + int observedSequenceNumber; + if (_receiveQueue.IsReady(this, out observedSequenceNumber) && + SocketPal.TryCompleteReceiveFrom(_socket, buffer.Span, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode)) { - ReceiveOperation operation; - lock (_receiveQueue.QueueLock) - { - SocketFlags receivedFlags; - SocketError errorCode; - - if (_receiveQueue.IsEmpty && - SocketPal.TryCompleteReceiveFrom(_socket, buffer, offset, count, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode)) - { - flags = receivedFlags; - return errorCode; - } - - @event = new ManualResetEventSlim(false, 0); - - operation = new BufferArrayReceiveOperation - { - Event = @event, - Buffer = buffer, - Offset = offset, - Count = count, - Flags = flags, - SocketAddress = socketAddress, - SocketAddressLen = socketAddressLen, - }; - - bool isStopped; - while (!TryBeginOperation(ref _receiveQueue, operation, Interop.Sys.SocketEvents.Read, maintainOrder: true, isStopped: out isStopped)) - { - if (isStopped) - { - flags = operation.ReceivedFlags; - bytesReceived = operation.BytesTransferred; - return SocketError.Interrupted; - } - - if (operation.TryComplete(this)) - { - socketAddressLen = operation.SocketAddressLen; - flags = operation.ReceivedFlags; - bytesReceived = operation.BytesTransferred; - return operation.ErrorCode; - } - } - } - - bool signaled = operation.Wait(timeout); - socketAddressLen = operation.SocketAddressLen; - flags = operation.ReceivedFlags; - bytesReceived = operation.BytesTransferred; - return signaled ? operation.ErrorCode : SocketError.TimedOut; + flags = receivedFlags; + return errorCode; } - finally + + var operation = new BufferMemoryReceiveOperation(this) { - @event?.Dispose(); - } + Buffer = buffer, + Flags = flags, + SocketAddress = socketAddress, + SocketAddressLen = socketAddressLen, + }; + + PerformSyncOperation(ref _receiveQueue, operation, timeout, observedSequenceNumber); + + flags = operation.ReceivedFlags; + bytesReceived = operation.BytesTransferred; + return operation.ErrorCode; } public unsafe SocketError ReceiveFrom(Span buffer, ref SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, int timeout, out int bytesReceived) { - Debug.Assert(timeout == -1 || timeout > 0, $"Unexpected timeout: {timeout}"); - - fixed (byte* bufferPtr = &buffer.DangerousGetPinnableReference()) + SocketFlags receivedFlags; + SocketError errorCode; + int observedSequenceNumber; + if (_receiveQueue.IsReady(this, out observedSequenceNumber) && + SocketPal.TryCompleteReceiveFrom(_socket, buffer, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode)) { - ManualResetEventSlim @event = null; - try - { - ReceiveOperation operation; - lock (_receiveQueue.QueueLock) - { - SocketFlags receivedFlags; - SocketError errorCode; - - if (_receiveQueue.IsEmpty && - SocketPal.TryCompleteReceiveFrom(_socket, buffer, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode)) - { - flags = receivedFlags; - return errorCode; - } - - @event = new ManualResetEventSlim(false, 0); - - operation = new BufferPtrReceiveOperation - { - Event = @event, - BufferPtr = bufferPtr, - Length = buffer.Length, - Flags = flags, - SocketAddress = socketAddress, - SocketAddressLen = socketAddressLen, - }; - - bool isStopped; - while (!TryBeginOperation(ref _receiveQueue, operation, Interop.Sys.SocketEvents.Read, maintainOrder: true, isStopped: out isStopped)) - { - if (isStopped) - { - flags = operation.ReceivedFlags; - bytesReceived = operation.BytesTransferred; - return SocketError.Interrupted; - } - - if (operation.TryComplete(this)) - { - socketAddressLen = operation.SocketAddressLen; - flags = operation.ReceivedFlags; - bytesReceived = operation.BytesTransferred; - return operation.ErrorCode; - } - } - } - - bool signaled = operation.Wait(timeout); - socketAddressLen = operation.SocketAddressLen; - flags = operation.ReceivedFlags; - bytesReceived = operation.BytesTransferred; - return signaled ? operation.ErrorCode : SocketError.TimedOut; - } - finally - { - @event?.Dispose(); - } + flags = receivedFlags; + return errorCode; } - } - - public SocketError ReceiveFromAsync(byte[] buffer, int offset, int count, SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, out int bytesReceived, out SocketFlags receivedFlags, Action callback) - { - SetNonBlocking(); - - lock (_receiveQueue.QueueLock) + fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) { - SocketError errorCode; - - if (_receiveQueue.IsEmpty && - SocketPal.TryCompleteReceiveFrom(_socket, buffer, offset, count, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode)) + var operation = new BufferPtrReceiveOperation(this) { - // Synchronous success or failure - return errorCode; - } - - var operation = new BufferArrayReceiveOperation - { - Callback = callback, - Buffer = buffer, - Offset = offset, - Count = count, + BufferPtr = bufferPtr, + Length = buffer.Length, Flags = flags, SocketAddress = socketAddress, SocketAddressLen = socketAddressLen, }; - bool isStopped; - while (!TryBeginOperation(ref _receiveQueue, operation, Interop.Sys.SocketEvents.Read, maintainOrder: true, isStopped: out isStopped)) - { - if (isStopped) - { - bytesReceived = 0; - receivedFlags = SocketFlags.None; - return SocketError.OperationAborted; - } + PerformSyncOperation(ref _receiveQueue, operation, timeout, observedSequenceNumber); - if (operation.TryComplete(this)) - { - socketAddressLen = operation.SocketAddressLen; - receivedFlags = operation.ReceivedFlags; - bytesReceived = operation.BytesTransferred; - return operation.ErrorCode; - } - } - - bytesReceived = 0; - receivedFlags = SocketFlags.None; - return SocketError.IOPending; + flags = operation.ReceivedFlags; + bytesReceived = operation.BytesTransferred; + return operation.ErrorCode; } } + public SocketError ReceiveFromAsync(Memory buffer, SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, out int bytesReceived, out SocketFlags receivedFlags, Action callback) + { + SetNonBlocking(); + + SocketError errorCode; + int observedSequenceNumber; + if (_receiveQueue.IsReady(this, out observedSequenceNumber) && + SocketPal.TryCompleteReceiveFrom(_socket, buffer.Span, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode)) + { + return errorCode; + } + + BufferMemoryReceiveOperation operation = RentBufferMemoryReceiveOperation(); + operation.Callback = callback; + operation.Buffer = buffer; + operation.Flags = flags; + operation.SocketAddress = socketAddress; + operation.SocketAddressLen = socketAddressLen; + + if (!_receiveQueue.StartAsyncOperation(this, operation, observedSequenceNumber)) + { + receivedFlags = operation.ReceivedFlags; + bytesReceived = operation.BytesTransferred; + errorCode = operation.ErrorCode; + + ReturnOperation(operation); + return errorCode; + } + + bytesReceived = 0; + receivedFlags = SocketFlags.None; + return SocketError.IOPending; + } + public SocketError Receive(IList> buffers, ref SocketFlags flags, int timeout, out int bytesReceived) { return ReceiveFrom(buffers, ref flags, null, 0, timeout, out bytesReceived); @@ -1019,244 +1366,140 @@ namespace System.Net.Sockets { Debug.Assert(timeout == -1 || timeout > 0, $"Unexpected timeout: {timeout}"); - ManualResetEventSlim @event = null; - try + SocketFlags receivedFlags; + SocketError errorCode; + int observedSequenceNumber; + if (_receiveQueue.IsReady(this, out observedSequenceNumber) && + SocketPal.TryCompleteReceiveFrom(_socket, buffers, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode)) { - ReceiveOperation operation; - - lock (_receiveQueue.QueueLock) - { - SocketFlags receivedFlags; - SocketError errorCode; - if (_receiveQueue.IsEmpty && - SocketPal.TryCompleteReceiveFrom(_socket, buffers, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode)) - { - flags = receivedFlags; - return errorCode; - } - - @event = new ManualResetEventSlim(false, 0); - - operation = new BufferListReceiveOperation - { - Event = @event, - Buffers = buffers, - Flags = flags, - SocketAddress = socketAddress, - SocketAddressLen = socketAddressLen, - }; - - bool isStopped; - while (!TryBeginOperation(ref _receiveQueue, operation, Interop.Sys.SocketEvents.Read, maintainOrder: true, isStopped: out isStopped)) - { - if (isStopped) - { - flags = operation.ReceivedFlags; - bytesReceived = operation.BytesTransferred; - return SocketError.Interrupted; - } - - if (operation.TryComplete(this)) - { - socketAddressLen = operation.SocketAddressLen; - flags = operation.ReceivedFlags; - bytesReceived = operation.BytesTransferred; - return operation.ErrorCode; - } - } - - } - - bool signaled = operation.Wait(timeout); - socketAddressLen = operation.SocketAddressLen; - flags = operation.ReceivedFlags; - bytesReceived = operation.BytesTransferred; - return signaled ? operation.ErrorCode : SocketError.TimedOut; + flags = receivedFlags; + return errorCode; } - finally + + var operation = new BufferListReceiveOperation(this) { - @event?.Dispose(); - } + Buffers = buffers, + Flags = flags, + SocketAddress = socketAddress, + SocketAddressLen = socketAddressLen + }; + + PerformSyncOperation(ref _receiveQueue, operation, timeout, observedSequenceNumber); + + socketAddressLen = operation.SocketAddressLen; + flags = operation.ReceivedFlags; + bytesReceived = operation.BytesTransferred; + return operation.ErrorCode; } public SocketError ReceiveFromAsync(IList> buffers, SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, out int bytesReceived, out SocketFlags receivedFlags, Action callback) { SetNonBlocking(); - ReceiveOperation operation; - - lock (_receiveQueue.QueueLock) + SocketError errorCode; + int observedSequenceNumber; + if (_receiveQueue.IsReady(this, out observedSequenceNumber) && + SocketPal.TryCompleteReceiveFrom(_socket, buffers, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode)) { - SocketError errorCode; - if (_receiveQueue.IsEmpty && - SocketPal.TryCompleteReceiveFrom(_socket, buffers, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode)) - { - // Synchronous success or failure - return errorCode; - } - - operation = new BufferListReceiveOperation - { - Callback = callback, - Buffers = buffers, - Flags = flags, - SocketAddress = socketAddress, - SocketAddressLen = socketAddressLen, - }; - - bool isStopped; - while (!TryBeginOperation(ref _receiveQueue, operation, Interop.Sys.SocketEvents.Read, maintainOrder: true, isStopped: out isStopped)) - { - if (isStopped) - { - bytesReceived = 0; - receivedFlags = SocketFlags.None; - return SocketError.OperationAborted; - } - - if (operation.TryComplete(this)) - { - socketAddressLen = operation.SocketAddressLen; - receivedFlags = operation.ReceivedFlags; - bytesReceived = operation.BytesTransferred; - return operation.ErrorCode; - } - } - - bytesReceived = 0; - receivedFlags = SocketFlags.None; - return SocketError.IOPending; + // Synchronous success or failure + return errorCode; } + + BufferListReceiveOperation operation = RentBufferListReceiveOperation(); + operation.Callback = callback; + operation.Buffers = buffers; + operation.Flags = flags; + operation.SocketAddress = socketAddress; + operation.SocketAddressLen = socketAddressLen; + + if (!_receiveQueue.StartAsyncOperation(this, operation, observedSequenceNumber)) + { + socketAddressLen = operation.SocketAddressLen; + receivedFlags = operation.ReceivedFlags; + bytesReceived = operation.BytesTransferred; + errorCode = operation.ErrorCode; + + ReturnOperation(operation); + return errorCode; + } + + receivedFlags = SocketFlags.None; + bytesReceived = 0; + return SocketError.IOPending; } public SocketError ReceiveMessageFrom( - byte[] buffer, IList> buffers, int offset, int count, ref SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, bool isIPv4, bool isIPv6, int timeout, out IPPacketInformation ipPacketInformation, out int bytesReceived) + Memory buffer, IList> buffers, ref SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, bool isIPv4, bool isIPv6, int timeout, out IPPacketInformation ipPacketInformation, out int bytesReceived) { Debug.Assert(timeout == -1 || timeout > 0, $"Unexpected timeout: {timeout}"); - ManualResetEventSlim @event = null; - try + SocketFlags receivedFlags; + SocketError errorCode; + int observedSequenceNumber; + if (_receiveQueue.IsReady(this, out observedSequenceNumber) && + SocketPal.TryCompleteReceiveMessageFrom(_socket, buffer.Span, buffers, flags, socketAddress, ref socketAddressLen, isIPv4, isIPv6, out bytesReceived, out receivedFlags, out ipPacketInformation, out errorCode)) { - ReceiveMessageFromOperation operation; - - lock (_receiveQueue.QueueLock) - { - SocketFlags receivedFlags; - SocketError errorCode; - if (_receiveQueue.IsEmpty && - SocketPal.TryCompleteReceiveMessageFrom(_socket, buffer, buffers, offset, count, flags, socketAddress, ref socketAddressLen, isIPv4, isIPv6, out bytesReceived, out receivedFlags, out ipPacketInformation, out errorCode)) - { - flags = receivedFlags; - return errorCode; - } - - @event = new ManualResetEventSlim(false, 0); - - operation = new ReceiveMessageFromOperation - { - Event = @event, - Buffer = buffer, - Buffers = buffers, - Offset = offset, - Count = count, - Flags = flags, - SocketAddress = socketAddress, - SocketAddressLen = socketAddressLen, - IsIPv4 = isIPv4, - IsIPv6 = isIPv6, - }; - - bool isStopped; - while (!TryBeginOperation(ref _receiveQueue, operation, Interop.Sys.SocketEvents.Read, maintainOrder: true, isStopped: out isStopped)) - { - if (isStopped) - { - socketAddressLen = operation.SocketAddressLen; - flags = operation.ReceivedFlags; - ipPacketInformation = operation.IPPacketInformation; - bytesReceived = operation.BytesTransferred; - return SocketError.Interrupted; - } - - if (operation.TryComplete(this)) - { - socketAddressLen = operation.SocketAddressLen; - flags = operation.ReceivedFlags; - ipPacketInformation = operation.IPPacketInformation; - bytesReceived = operation.BytesTransferred; - return operation.ErrorCode; - } - } - } - - bool signaled = operation.Wait(timeout); - socketAddressLen = operation.SocketAddressLen; - flags = operation.ReceivedFlags; - ipPacketInformation = operation.IPPacketInformation; - bytesReceived = operation.BytesTransferred; - return signaled ? operation.ErrorCode : SocketError.TimedOut; + flags = receivedFlags; + return errorCode; } - finally + + var operation = new ReceiveMessageFromOperation(this) { - @event?.Dispose(); - } + Buffer = buffer, + Buffers = buffers, + Flags = flags, + SocketAddress = socketAddress, + SocketAddressLen = socketAddressLen, + IsIPv4 = isIPv4, + IsIPv6 = isIPv6, + }; + + PerformSyncOperation(ref _receiveQueue, operation, timeout, observedSequenceNumber); + + socketAddressLen = operation.SocketAddressLen; + flags = operation.ReceivedFlags; + ipPacketInformation = operation.IPPacketInformation; + bytesReceived = operation.BytesTransferred; + return operation.ErrorCode; } - public SocketError ReceiveMessageFromAsync(byte[] buffer, IList> buffers, int offset, int count, SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, bool isIPv4, bool isIPv6, out int bytesReceived, out SocketFlags receivedFlags, out IPPacketInformation ipPacketInformation, Action callback) + public SocketError ReceiveMessageFromAsync(Memory buffer, IList> buffers, SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, bool isIPv4, bool isIPv6, out int bytesReceived, out SocketFlags receivedFlags, out IPPacketInformation ipPacketInformation, Action callback) { SetNonBlocking(); - lock (_receiveQueue.QueueLock) + SocketError errorCode; + int observedSequenceNumber; + if (_receiveQueue.IsReady(this, out observedSequenceNumber) && + SocketPal.TryCompleteReceiveMessageFrom(_socket, buffer.Span, buffers, flags, socketAddress, ref socketAddressLen, isIPv4, isIPv6, out bytesReceived, out receivedFlags, out ipPacketInformation, out errorCode)) { - SocketError errorCode; - - if (_receiveQueue.IsEmpty && - SocketPal.TryCompleteReceiveMessageFrom(_socket, buffer, buffers, offset, count, flags, socketAddress, ref socketAddressLen, isIPv4, isIPv6, out bytesReceived, out receivedFlags, out ipPacketInformation, out errorCode)) - { - // Synchronous success or failure - return errorCode; - } - - var operation = new ReceiveMessageFromOperation - { - Callback = callback, - Buffer = buffer, - Buffers = buffers, - Offset = offset, - Count = count, - Flags = flags, - SocketAddress = socketAddress, - SocketAddressLen = socketAddressLen, - IsIPv4 = isIPv4, - IsIPv6 = isIPv6, - }; - - bool isStopped; - while (!TryBeginOperation(ref _receiveQueue, operation, Interop.Sys.SocketEvents.Read, maintainOrder: true, isStopped: out isStopped)) - { - if (isStopped) - { - ipPacketInformation = default(IPPacketInformation); - bytesReceived = 0; - receivedFlags = SocketFlags.None; - return SocketError.OperationAborted; - } - - if (operation.TryComplete(this)) - { - socketAddressLen = operation.SocketAddressLen; - receivedFlags = operation.ReceivedFlags; - ipPacketInformation = operation.IPPacketInformation; - bytesReceived = operation.BytesTransferred; - return operation.ErrorCode; - } - } - - ipPacketInformation = default(IPPacketInformation); - bytesReceived = 0; - receivedFlags = SocketFlags.None; - return SocketError.IOPending; + return errorCode; } + + var operation = new ReceiveMessageFromOperation(this) + { + Callback = callback, + Buffer = buffer, + Buffers = buffers, + Flags = flags, + SocketAddress = socketAddress, + SocketAddressLen = socketAddressLen, + IsIPv4 = isIPv4, + IsIPv6 = isIPv6, + }; + + if (!_receiveQueue.StartAsyncOperation(this, operation, observedSequenceNumber)) + { + socketAddressLen = operation.SocketAddressLen; + receivedFlags = operation.ReceivedFlags; + ipPacketInformation = operation.IPPacketInformation; + bytesReceived = operation.BytesTransferred; + return operation.ErrorCode; + } + + ipPacketInformation = default(IPPacketInformation); + bytesReceived = 0; + receivedFlags = SocketFlags.None; + return SocketError.IOPending; } public SocketError Send(ReadOnlySpan buffer, SocketFlags flags, int timeout, out int bytesSent) => @@ -1267,7 +1510,7 @@ namespace System.Net.Sockets return SendTo(buffer, offset, count, flags, null, 0, timeout, out bytesSent); } - public SocketError SendAsync(byte[] buffer, int offset, int count, SocketFlags flags, out int bytesSent, Action callback) + public SocketError SendAsync(Memory buffer, int offset, int count, SocketFlags flags, out int bytesSent, Action callback) { int socketAddressLen = 0; return SendToAsync(buffer, offset, count, flags, null, ref socketAddressLen, out bytesSent, callback); @@ -1277,148 +1520,51 @@ namespace System.Net.Sockets { Debug.Assert(timeout == -1 || timeout > 0, $"Unexpected timeout: {timeout}"); - ManualResetEventSlim @event = null; - try + bytesSent = 0; + SocketError errorCode; + int observedSequenceNumber; + if (_sendQueue.IsReady(this, out observedSequenceNumber) && + SocketPal.TryCompleteSendTo(_socket, buffer, ref offset, ref count, flags, socketAddress, socketAddressLen, ref bytesSent, out errorCode)) { - BufferArraySendOperation operation; - - lock (_sendQueue.QueueLock) - { - bytesSent = 0; - SocketError errorCode; - - if (_sendQueue.IsEmpty && - SocketPal.TryCompleteSendTo(_socket, buffer, ref offset, ref count, flags, socketAddress, socketAddressLen, ref bytesSent, out errorCode)) - { - return errorCode; - } - - @event = new ManualResetEventSlim(false, 0); - - operation = new BufferArraySendOperation - { - Event = @event, - Buffer = buffer, - Offset = offset, - Count = count, - Flags = flags, - SocketAddress = socketAddress, - SocketAddressLen = socketAddressLen, - BytesTransferred = bytesSent - }; - - bool isStopped; - while (!TryBeginOperation(ref _sendQueue, operation, Interop.Sys.SocketEvents.Write, maintainOrder: true, isStopped: out isStopped)) - { - if (isStopped) - { - bytesSent = operation.BytesTransferred; - return SocketError.Interrupted; - } - - if (operation.TryComplete(this)) - { - bytesSent = operation.BytesTransferred; - return operation.ErrorCode; - } - } - } - - bool signaled = operation.Wait(timeout); - bytesSent = operation.BytesTransferred; - return signaled ? operation.ErrorCode : SocketError.TimedOut; + return errorCode; } - finally + + var operation = new BufferMemorySendOperation(this) { - @event?.Dispose(); - } + Buffer = buffer, + Offset = offset, + Count = count, + Flags = flags, + SocketAddress = socketAddress, + SocketAddressLen = socketAddressLen, + BytesTransferred = bytesSent + }; + + PerformSyncOperation(ref _sendQueue, operation, timeout, observedSequenceNumber); + + bytesSent = operation.BytesTransferred; + return operation.ErrorCode; } public unsafe SocketError SendTo(ReadOnlySpan buffer, SocketFlags flags, byte[] socketAddress, int socketAddressLen, int timeout, out int bytesSent) { Debug.Assert(timeout == -1 || timeout > 0, $"Unexpected timeout: {timeout}"); - fixed (byte* bufferPtr = &buffer.DangerousGetPinnableReference()) + bytesSent = 0; + SocketError errorCode; + int bufferIndexIgnored = 0, offset = 0, count = buffer.Length; + int observedSequenceNumber; + if (_sendQueue.IsReady(this, out observedSequenceNumber) && + SocketPal.TryCompleteSendTo(_socket, buffer, null, ref bufferIndexIgnored, ref offset, ref count, flags, socketAddress, socketAddressLen, ref bytesSent, out errorCode)) { - ManualResetEventSlim @event = null; - try - { - BufferPtrSendOperation operation; - - lock (_sendQueue.QueueLock) - { - bytesSent = 0; - SocketError errorCode; - - int bufferIndexIgnored = 0, offset = 0, count = buffer.Length; - if (_sendQueue.IsEmpty && - SocketPal.TryCompleteSendTo(_socket, buffer, null, ref bufferIndexIgnored, ref offset, ref count, flags, socketAddress, socketAddressLen, ref bytesSent, out errorCode)) - { - return errorCode; - } - - @event = new ManualResetEventSlim(false, 0); - - operation = new BufferPtrSendOperation - { - Event = @event, - BufferPtr = bufferPtr, - Offset = offset, - Count = count, - Flags = flags, - SocketAddress = socketAddress, - SocketAddressLen = socketAddressLen, - BytesTransferred = bytesSent - }; - - bool isStopped; - while (!TryBeginOperation(ref _sendQueue, operation, Interop.Sys.SocketEvents.Write, maintainOrder: true, isStopped: out isStopped)) - { - if (isStopped) - { - bytesSent = operation.BytesTransferred; - return SocketError.Interrupted; - } - - if (operation.TryComplete(this)) - { - bytesSent = operation.BytesTransferred; - return operation.ErrorCode; - } - } - } - - bool signaled = operation.Wait(timeout); - bytesSent = operation.BytesTransferred; - return signaled ? operation.ErrorCode : SocketError.TimedOut; - } - finally - { - @event?.Dispose(); - } + return errorCode; } - } - public SocketError SendToAsync(byte[] buffer, int offset, int count, SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, out int bytesSent, Action callback) - { - SetNonBlocking(); - - lock (_sendQueue.QueueLock) + fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) { - bytesSent = 0; - SocketError errorCode; - - if (_sendQueue.IsEmpty && - SocketPal.TryCompleteSendTo(_socket, buffer, ref offset, ref count, flags, socketAddress, socketAddressLen, ref bytesSent, out errorCode)) + var operation = new BufferPtrSendOperation(this) { - // Synchronous success or failure - return errorCode; - } - - var operation = new BufferArraySendOperation - { - Callback = callback, - Buffer = buffer, + BufferPtr = bufferPtr, Offset = offset, Count = count, Flags = flags, @@ -1427,25 +1573,48 @@ namespace System.Net.Sockets BytesTransferred = bytesSent }; - bool isStopped; - while (!TryBeginOperation(ref _sendQueue, operation, Interop.Sys.SocketEvents.Write, maintainOrder: true, isStopped: out isStopped)) - { - if (isStopped) - { - return SocketError.OperationAborted; - } + PerformSyncOperation(ref _sendQueue, operation, timeout, observedSequenceNumber); - if (operation.TryComplete(this)) - { - bytesSent = operation.BytesTransferred; - return operation.ErrorCode; - } - } - - return SocketError.IOPending; + bytesSent = operation.BytesTransferred; + return operation.ErrorCode; } } + public SocketError SendToAsync(Memory buffer, int offset, int count, SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, out int bytesSent, Action callback) + { + SetNonBlocking(); + + bytesSent = 0; + SocketError errorCode; + int observedSequenceNumber; + if (_sendQueue.IsReady(this, out observedSequenceNumber) && + SocketPal.TryCompleteSendTo(_socket, buffer.Span, ref offset, ref count, flags, socketAddress, socketAddressLen, ref bytesSent, out errorCode)) + { + return errorCode; + } + + BufferMemorySendOperation operation = RentBufferMemorySendOperation(); + operation.Callback = callback; + operation.Buffer = buffer; + operation.Offset = offset; + operation.Count = count; + operation.Flags = flags; + operation.SocketAddress = socketAddress; + operation.SocketAddressLen = socketAddressLen; + operation.BytesTransferred = bytesSent; + + if (!_sendQueue.StartAsyncOperation(this, operation, observedSequenceNumber)) + { + bytesSent = operation.BytesTransferred; + errorCode = operation.ErrorCode; + + ReturnOperation(operation); + return errorCode; + } + + return SocketError.IOPending; + } + public SocketError Send(IList> buffers, SocketFlags flags, int timeout, out int bytesSent) { return SendTo(buffers, flags, null, 0, timeout, out bytesSent); @@ -1461,214 +1630,127 @@ namespace System.Net.Sockets { Debug.Assert(timeout == -1 || timeout > 0, $"Unexpected timeout: {timeout}"); - ManualResetEventSlim @event = null; - try + bytesSent = 0; + int bufferIndex = 0; + int offset = 0; + SocketError errorCode; + int observedSequenceNumber; + if (_sendQueue.IsReady(this, out observedSequenceNumber) && + SocketPal.TryCompleteSendTo(_socket, buffers, ref bufferIndex, ref offset, flags, socketAddress, socketAddressLen, ref bytesSent, out errorCode)) { - BufferListSendOperation operation; - - lock (_sendQueue.QueueLock) - { - bytesSent = 0; - int bufferIndex = 0; - int offset = 0; - SocketError errorCode; - - if (_sendQueue.IsEmpty && - SocketPal.TryCompleteSendTo(_socket, buffers, ref bufferIndex, ref offset, flags, socketAddress, socketAddressLen, ref bytesSent, out errorCode)) - { - return errorCode; - } - - @event = new ManualResetEventSlim(false, 0); - - operation = new BufferListSendOperation - { - Event = @event, - Buffers = buffers, - BufferIndex = bufferIndex, - Offset = offset, - Flags = flags, - SocketAddress = socketAddress, - SocketAddressLen = socketAddressLen, - BytesTransferred = bytesSent - }; - - bool isStopped; - while (!TryBeginOperation(ref _sendQueue, operation, Interop.Sys.SocketEvents.Write, maintainOrder: true, isStopped: out isStopped)) - { - if (isStopped) - { - bytesSent = operation.BytesTransferred; - return SocketError.Interrupted; - } - - if (operation.TryComplete(this)) - { - bytesSent = operation.BytesTransferred; - return operation.ErrorCode; - } - } - } - - bool signaled = operation.Wait(timeout); - bytesSent = operation.BytesTransferred; - return signaled ? operation.ErrorCode : SocketError.TimedOut; + return errorCode; } - finally + + var operation = new BufferListSendOperation(this) { - @event?.Dispose(); - } + Buffers = buffers, + BufferIndex = bufferIndex, + Offset = offset, + Flags = flags, + SocketAddress = socketAddress, + SocketAddressLen = socketAddressLen, + BytesTransferred = bytesSent + }; + + PerformSyncOperation(ref _sendQueue, operation, timeout, observedSequenceNumber); + + bytesSent = operation.BytesTransferred; + return operation.ErrorCode; } public SocketError SendToAsync(IList> buffers, SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, out int bytesSent, Action callback) { SetNonBlocking(); - lock (_sendQueue.QueueLock) + bytesSent = 0; + int bufferIndex = 0; + int offset = 0; + SocketError errorCode; + int observedSequenceNumber; + if (_sendQueue.IsReady(this, out observedSequenceNumber) && + SocketPal.TryCompleteSendTo(_socket, buffers, ref bufferIndex, ref offset, flags, socketAddress, socketAddressLen, ref bytesSent, out errorCode)) { - bytesSent = 0; - int bufferIndex = 0; - int offset = 0; - SocketError errorCode; - - if (_sendQueue.IsEmpty && - SocketPal.TryCompleteSendTo(_socket, buffers, ref bufferIndex, ref offset, flags, socketAddress, socketAddressLen, ref bytesSent, out errorCode)) - { - // Synchronous success or failure - return errorCode; - } - - var operation = new BufferListSendOperation - { - Callback = callback, - Buffers = buffers, - BufferIndex = bufferIndex, - Offset = offset, - Flags = flags, - SocketAddress = socketAddress, - SocketAddressLen = socketAddressLen, - BytesTransferred = bytesSent - }; - - bool isStopped; - while (!TryBeginOperation(ref _sendQueue, operation, Interop.Sys.SocketEvents.Write, maintainOrder: true, isStopped: out isStopped)) - { - if (isStopped) - { - return SocketError.OperationAborted; - } - - if (operation.TryComplete(this)) - { - bytesSent = operation.BytesTransferred; - return operation.ErrorCode; - } - } - - return SocketError.IOPending; + return errorCode; } + + BufferListSendOperation operation = RentBufferListSendOperation(); + operation.Callback = callback; + operation.Buffers = buffers; + operation.BufferIndex = bufferIndex; + operation.Offset = offset; + operation.Flags = flags; + operation.SocketAddress = socketAddress; + operation.SocketAddressLen = socketAddressLen; + operation.BytesTransferred = bytesSent; + + if (!_sendQueue.StartAsyncOperation(this, operation, observedSequenceNumber)) + { + bytesSent = operation.BytesTransferred; + errorCode = operation.ErrorCode; + + ReturnOperation(operation); + return errorCode; + } + + return SocketError.IOPending; } public SocketError SendFile(SafeFileHandle fileHandle, long offset, long count, int timeout, out long bytesSent) { Debug.Assert(timeout == -1 || timeout > 0, $"Unexpected timeout: {timeout}"); - ManualResetEventSlim @event = null; - try + bytesSent = 0; + SocketError errorCode; + int observedSequenceNumber; + if (_sendQueue.IsReady(this, out observedSequenceNumber) && + SocketPal.TryCompleteSendFile(_socket, fileHandle, ref offset, ref count, ref bytesSent, out errorCode)) { - SendFileOperation operation; - - lock (_sendQueue.QueueLock) - { - bytesSent = 0; - SocketError errorCode; - - if (_sendQueue.IsEmpty && - SocketPal.TryCompleteSendFile(_socket, fileHandle, ref offset, ref count, ref bytesSent, out errorCode)) - { - return errorCode; - } - - @event = new ManualResetEventSlim(false, 0); - - operation = new SendFileOperation - { - Event = @event, - FileHandle = fileHandle, - Offset = offset, - Count = count, - BytesTransferred = bytesSent - }; - - bool isStopped; - while (!TryBeginOperation(ref _sendQueue, operation, Interop.Sys.SocketEvents.Write, maintainOrder: true, isStopped: out isStopped)) - { - if (isStopped) - { - bytesSent = operation.BytesTransferred; - return SocketError.Interrupted; - } - - if (operation.TryComplete(this)) - { - bytesSent = operation.BytesTransferred; - return operation.ErrorCode; - } - } - } - - bool signaled = operation.Wait(timeout); - bytesSent = operation.BytesTransferred; - return signaled ? operation.ErrorCode : SocketError.TimedOut; + return errorCode; } - finally + + var operation = new SendFileOperation(this) { - @event?.Dispose(); - } + FileHandle = fileHandle, + Offset = offset, + Count = count, + BytesTransferred = bytesSent + }; + + PerformSyncOperation(ref _sendQueue, operation, timeout, observedSequenceNumber); + + bytesSent = operation.BytesTransferred; + return operation.ErrorCode; } public SocketError SendFileAsync(SafeFileHandle fileHandle, long offset, long count, out long bytesSent, Action callback) { SetNonBlocking(); - lock (_sendQueue.QueueLock) + bytesSent = 0; + SocketError errorCode; + int observedSequenceNumber; + if (_sendQueue.IsReady(this, out observedSequenceNumber) && + SocketPal.TryCompleteSendFile(_socket, fileHandle, ref offset, ref count, ref bytesSent, out errorCode)) { - bytesSent = 0; - SocketError errorCode; - - if (_sendQueue.IsEmpty && - SocketPal.TryCompleteSendFile(_socket, fileHandle, ref offset, ref count, ref bytesSent, out errorCode)) - { - // Synchronous success or failure - return errorCode; - } - - var operation = new SendFileOperation - { - Callback = callback, - FileHandle = fileHandle, - Offset = offset, - Count = count, - BytesTransferred = bytesSent - }; - - bool isStopped; - while (!TryBeginOperation(ref _sendQueue, operation, Interop.Sys.SocketEvents.Write, maintainOrder: true, isStopped: out isStopped)) - { - if (isStopped) - { - return SocketError.OperationAborted; - } - - if (operation.TryComplete(this)) - { - bytesSent = operation.BytesTransferred; - return operation.ErrorCode; - } - } - - return SocketError.IOPending; + return errorCode; } + + var operation = new SendFileOperation(this) + { + Callback = callback, + FileHandle = fileHandle, + Offset = offset, + Count = count, + BytesTransferred = bytesSent + }; + + if (!_sendQueue.StartAsyncOperation(this, operation, observedSequenceNumber)) + { + bytesSent = operation.BytesTransferred; + return operation.ErrorCode; + } + + return SocketError.IOPending; } public unsafe void HandleEvents(Interop.Sys.SocketEvents events) @@ -1682,13 +1764,38 @@ namespace System.Net.Sockets if ((events & Interop.Sys.SocketEvents.Read) != 0) { - _receiveQueue.Complete(this); + _receiveQueue.HandleEvent(this); } if ((events & Interop.Sys.SocketEvents.Write) != 0) { - _sendQueue.Complete(this); + _sendQueue.HandleEvent(this); } } + + // + // Tracing stuff + // + + // To enabled tracing: + // (1) Add reference to System.Console in the csproj + // (2) #define SOCKETASYNCCONTEXT_TRACE + + [Conditional("SOCKETASYNCCONTEXT_TRACE")] + public void Trace(string message, [CallerMemberName] string memberName = null) + { + OutputTrace($"{IdOf(this)}.{memberName}: {message}"); + } + + [Conditional("SOCKETASYNCCONTEXT_TRACE")] + public static void OutputTrace(string s) + { + // CONSIDER: Change to NetEventSource +#if SOCKETASYNCCONTEXT_TRACE + Console.WriteLine(s); +#endif + } + + public static string IdOf(object o) => o == null ? "(null)" : $"{o.GetType().Name}#{o.GetHashCode():X2}"; } } diff --git a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs index f9aec91675..39b503551e 100644 --- a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs +++ b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs @@ -16,7 +16,7 @@ namespace System.Net.Sockets // // Encapsulates a particular SocketAsyncContext object's access to a SocketAsyncEngine. // - public struct Token + public readonly struct Token { private readonly SocketAsyncEngine _engine; private readonly IntPtr _handle; @@ -39,10 +39,10 @@ namespace System.Net.Sockets } } - public bool TryRegister(SafeCloseSocket socket, Interop.Sys.SocketEvents current, Interop.Sys.SocketEvents events, out Interop.Error error) + public bool TryRegister(SafeCloseSocket socket, out Interop.Error error) { Debug.Assert(WasAllocated, "Expected WasAllocated to be true"); - return _engine.TryRegister(socket, current, events, _handle, out error); + return _engine.TryRegister(socket, _handle, out error); } } @@ -375,15 +375,10 @@ namespace System.Net.Sockets } } - private bool TryRegister(SafeCloseSocket socket, Interop.Sys.SocketEvents current, Interop.Sys.SocketEvents events, IntPtr handle, out Interop.Error error) + private bool TryRegister(SafeCloseSocket socket, IntPtr handle, out Interop.Error error) { - if (current == events) - { - error = Interop.Error.SUCCESS; - return true; - } - - error = Interop.Sys.TryChangeSocketEventRegistration(_port, socket, current, events, handle); + error = Interop.Sys.TryChangeSocketEventRegistration(_port, socket, Interop.Sys.SocketEvents.None, + Interop.Sys.SocketEvents.Read | Interop.Sys.SocketEvents.Write, handle); return error == Interop.Error.SUCCESS; } } diff --git a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs index 73a896d696..0b011b9c23 100644 --- a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs +++ b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs @@ -15,8 +15,6 @@ namespace System.Net.Sockets private SocketFlags _receivedFlags; private Action _transferCompletionCallback; - internal int? SendPacketsDescriptorCount => _sendPacketsElements?.Length; - private void InitializeInternals() { // No-op for *nix. @@ -37,10 +35,7 @@ namespace System.Net.Sockets // No-op for *nix. } - private void SetupSendPacketsElements() - { - // No-op for *nix. - } + private void CompleteCore() { } private void FinishOperationSync(SocketError socketError, int bytesTransferred, SocketFlags flags) { @@ -56,11 +51,6 @@ namespace System.Net.Sockets } } - private void InnerStartOperationAccept(bool userSuppliedBuffer) - { - _acceptedFileDescriptor = (IntPtr)(-1); - } - private void AcceptCompletionCallback(IntPtr acceptedFileDescriptor, byte[] socketAddress, int socketAddressSize, SocketError socketError) { CompleteAcceptOperation(acceptedFileDescriptor, socketAddress, socketAddressSize, socketError); @@ -77,11 +67,13 @@ namespace System.Net.Sockets internal unsafe SocketError DoOperationAccept(Socket socket, SafeCloseSocket handle, SafeCloseSocket acceptHandle) { - if (_buffer != null) + if (!_buffer.Equals(default)) { throw new PlatformNotSupportedException(SR.net_sockets_accept_receive_notsupported); } + _acceptedFileDescriptor = (IntPtr)(-1); + Debug.Assert(acceptHandle == null, $"Unexpected acceptHandle: {acceptHandle}"); IntPtr acceptedFd; @@ -97,11 +89,6 @@ namespace System.Net.Sockets return socketError; } - private void InnerStartOperationConnect() - { - // No-op for *nix. - } - private void ConnectCompletionCallback(SocketError socketError) { CompletionCallback(0, SocketFlags.None, socketError); @@ -124,10 +111,6 @@ namespace System.Net.Sockets return socketError; } - private void InnerStartOperationDisconnect() - { - } - private Action TransferCompletionCallback => _transferCompletionCallback ?? (_transferCompletionCallback = TransferCompletionCallbackCore); @@ -145,19 +128,17 @@ namespace System.Net.Sockets _receivedFlags = receivedFlags; } - private void InnerStartOperationReceive() + internal unsafe SocketError DoOperationReceive(SafeCloseSocket handle) { _receivedFlags = System.Net.Sockets.SocketFlags.None; _socketAddressSize = 0; - } - internal unsafe SocketError DoOperationReceive(SafeCloseSocket handle, out SocketFlags flags) - { + SocketFlags flags; int bytesReceived; SocketError errorCode; - if (_buffer != null) + if (_bufferList == null) { - errorCode = handle.AsyncContext.ReceiveAsync(_buffer, _offset, _count, _socketFlags, out bytesReceived, out flags, TransferCompletionCallback); + errorCode = handle.AsyncContext.ReceiveAsync(_buffer.Slice(_offset, _count), _socketFlags, out bytesReceived, out flags, TransferCompletionCallback); } else { @@ -173,20 +154,18 @@ namespace System.Net.Sockets return errorCode; } - private void InnerStartOperationReceiveFrom() + internal unsafe SocketError DoOperationReceiveFrom(SafeCloseSocket handle) { _receivedFlags = System.Net.Sockets.SocketFlags.None; _socketAddressSize = 0; - } - internal unsafe SocketError DoOperationReceiveFrom(SafeCloseSocket handle, out SocketFlags flags) - { + SocketFlags flags; SocketError errorCode; int bytesReceived = 0; int socketAddressLen = _socketAddress.Size; - if (_buffer != null) + if (_bufferList == null) { - errorCode = handle.AsyncContext.ReceiveFromAsync(_buffer, _offset, _count, _socketFlags, _socketAddress.Buffer, ref socketAddressLen, out bytesReceived, out flags, TransferCompletionCallback); + errorCode = handle.AsyncContext.ReceiveFromAsync(_buffer.Slice(_offset, _count), _socketFlags, _socketAddress.Buffer, ref socketAddressLen, out bytesReceived, out flags, TransferCompletionCallback); } else { @@ -202,13 +181,6 @@ namespace System.Net.Sockets return errorCode; } - private void InnerStartOperationReceiveMessageFrom() - { - _receiveMessageFromPacketInfo = default(IPPacketInformation); - _receivedFlags = System.Net.Sockets.SocketFlags.None; - _socketAddressSize = 0; - } - private void ReceiveMessageFromCompletionCallback(int bytesTransferred, byte[] socketAddress, int socketAddressSize, SocketFlags receivedFlags, IPPacketInformation ipPacketInformation, SocketError errorCode) { CompleteReceiveMessageFromOperation(bytesTransferred, socketAddress, socketAddressSize, receivedFlags, ipPacketInformation, errorCode); @@ -228,6 +200,10 @@ namespace System.Net.Sockets internal unsafe SocketError DoOperationReceiveMessageFrom(Socket socket, SafeCloseSocket handle) { + _receiveMessageFromPacketInfo = default(IPPacketInformation); + _receivedFlags = System.Net.Sockets.SocketFlags.None; + _socketAddressSize = 0; + bool isIPv4, isIPv6; Socket.GetIPProtocolInformation(socket.AddressFamily, _socketAddress, out isIPv4, out isIPv6); @@ -235,7 +211,7 @@ namespace System.Net.Sockets int bytesReceived; SocketFlags receivedFlags; IPPacketInformation ipPacketInformation; - SocketError socketError = handle.AsyncContext.ReceiveMessageFromAsync(_buffer, _bufferListInternal, _offset, _count, _socketFlags, _socketAddress.Buffer, ref socketAddressSize, isIPv4, isIPv6, out bytesReceived, out receivedFlags, out ipPacketInformation, ReceiveMessageFromCompletionCallback); + SocketError socketError = handle.AsyncContext.ReceiveMessageFromAsync(_buffer.Slice(_offset, _count), _bufferListInternal, _socketFlags, _socketAddress.Buffer, ref socketAddressSize, isIPv4, isIPv6, out bytesReceived, out receivedFlags, out ipPacketInformation, ReceiveMessageFromCompletionCallback); if (socketError != SocketError.IOPending) { CompleteReceiveMessageFromOperation(bytesReceived, _socketAddress.Buffer, socketAddressSize, receivedFlags, ipPacketInformation, socketError); @@ -244,17 +220,14 @@ namespace System.Net.Sockets return socketError; } - private void InnerStartOperationSend() + internal unsafe SocketError DoOperationSend(SafeCloseSocket handle) { _receivedFlags = System.Net.Sockets.SocketFlags.None; _socketAddressSize = 0; - } - internal unsafe SocketError DoOperationSend(SafeCloseSocket handle) - { int bytesSent; SocketError errorCode; - if (_buffer != null) + if (_bufferList == null) { errorCode = handle.AsyncContext.SendAsync(_buffer, _offset, _count, _socketFlags, out bytesSent, TransferCompletionCallback); } @@ -272,11 +245,6 @@ namespace System.Net.Sockets return errorCode; } - private void InnerStartOperationSendPackets() - { - // nop - } - internal SocketError DoOperationSendPackets(Socket socket, SafeCloseSocket handle) { Debug.Assert(_sendPacketsElements != null); @@ -336,18 +304,15 @@ namespace System.Net.Sockets return SocketError.IOPending; } - private void InnerStartOperationSendTo() + internal SocketError DoOperationSendTo(SafeCloseSocket handle) { _receivedFlags = System.Net.Sockets.SocketFlags.None; _socketAddressSize = 0; - } - internal SocketError DoOperationSendTo(SafeCloseSocket handle) - { int bytesSent; int socketAddressLen = _socketAddress.Size; SocketError errorCode; - if (_buffer != null) + if (_bufferList == null) { errorCode = handle.AsyncContext.SendToAsync(_buffer, _offset, _count, _socketFlags, _socketAddress.Buffer, ref socketAddressLen, out bytesSent, TransferCompletionCallback); } @@ -372,7 +337,7 @@ namespace System.Net.Sockets // may fire erroneously. Debug.Assert(NetEventSource.IsEnabled); - if (_buffer != null) + if (_bufferList == null) { NetEventSource.DumpBuffer(this, _buffer, _offset, size); } diff --git a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs index feadd0b32a..7bcabcfba4 100644 --- a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs +++ b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs @@ -2,6 +2,7 @@ // 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.Buffers; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; @@ -11,10 +12,10 @@ namespace System.Net.Sockets { public partial class SocketAsyncEventArgs : EventArgs, IDisposable { - // Buffer,Offset,Count property variables. - private WSABuffer _wsaBuffer; - private IntPtr _ptrSingleBuffer; - private GCHandle _singleBufferGCHandle; + // Single buffer + private MemoryHandle _singleBufferHandle; + private volatile SingleBufferHandleState _singleBufferHandleState; + private enum SingleBufferHandleState : byte { None, InProcess, Set } // BufferList property variables. // Note that these arrays are allocated and then grown as necessary, but never shrunk. @@ -30,81 +31,31 @@ namespace System.Net.Sockets private WSABuffer[] _wsaRecvMsgWSABufferArray; private GCHandle _wsaRecvMsgWSABufferArrayGCHandle; - // Internal buffer for AcceptEx when Buffer not supplied. - private IntPtr _ptrAcceptBuffer; - // Internal SocketAddress buffer private GCHandle _socketAddressGCHandle; private Internals.SocketAddress _pinnedSocketAddress; // SendPacketsElements property variables. - private SendPacketsElement[] _sendPacketsElementsInternal; - private Interop.Winsock.TransmitPacketsElement[] _sendPacketsDescriptor; - private int _sendPacketsElementsFileCount; - private int _sendPacketsElementsBufferCount; - - // Internal variables for SendPackets private FileStream[] _sendPacketsFileStreams; - private SafeHandle[] _sendPacketsFileHandles; // Overlapped object related variables. private PreAllocatedOverlapped _preAllocatedOverlapped; - private enum PinState - { - None = 0, - NoBuffer, - SingleAcceptBuffer, - SingleBuffer, - MultipleBuffer, - SendPackets - } private PinState _pinState; - private byte[] _pinnedAcceptBuffer; - private byte[] _pinnedSingleBuffer; - private int _pinnedSingleBufferOffset; - private int _pinnedSingleBufferCount; - - internal int? SendPacketsDescriptorCount - { - get - { - return _sendPacketsDescriptor == null ? null : (int?)_sendPacketsDescriptor.Length; - } - } + private enum PinState : byte { None = 0, MultipleBuffer, SendPackets } private void InitializeInternals() { _preAllocatedOverlapped = new PreAllocatedOverlapped(s_completionPortCallback, this, null); if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"new PreAllocatedOverlapped {_preAllocatedOverlapped}"); - - // Zero tells TransmitPackets to select a default send size. - _sendPacketsSendSize = 0; } private void FreeInternals() { FreePinHandles(); - - // Free native overlapped data. FreeOverlapped(); } - private void SetupSingleBuffer() - { - CheckPinSingleBuffer(true); - } - - private void SetupMultipleBuffers() - { - CheckPinMultipleBuffers(); - } - - private void SetupSendPacketsElements() - { - _sendPacketsElementsInternal = null; - } - private unsafe NativeOverlapped* AllocateNativeOverlapped() { Debug.Assert(_currentSocket != null, "_currentSocket is null"); @@ -147,31 +98,69 @@ namespace System.Net.Sockets FinishOperationSyncSuccess(bytesTransferred, SocketFlags.None); return SocketError.Success; } - - // Socket handle is going to post a completion to the completion port (may have done so already). - // Return pending and we will continue in the completion port callback. - return SocketError.IOPending; + + // Completed synchronously, but the handle wasn't marked as skip completion port on success, + // so we still need to fall through and behave as if the IO was pending. } - - // Get the socket error (which may be IOPending) - SocketError errorCode = SocketPal.GetLastSocketError(); - - if (errorCode == SocketError.IOPending) + else { - return errorCode; + // Get the socket error (which may be IOPending) + SocketError socketError = SocketPal.GetLastSocketError(); + if (socketError != SocketError.IOPending) + { + // Completed synchronously with a failure. + FinishOperationSyncFailure(socketError, bytesTransferred, SocketFlags.None); + return socketError; + } + + // Fall through to IOPending handling for asynchronous completion. } - FinishOperationSyncFailure(errorCode, bytesTransferred, SocketFlags.None); - - return errorCode; + // Socket handle is going to post a completion to the completion port (may have done so already). + // Return pending and we will continue in the completion port callback. + return SocketError.IOPending; } - private void InnerStartOperationAccept(bool userSuppliedBuffer) + private SocketError ProcessIOCPResultWithSingleBufferHandle(SocketError socketError, int bytesTransferred) { - if (!userSuppliedBuffer) + if (socketError == SocketError.Success) { - CheckPinSingleBuffer(false); + // Synchronous success. + if (_currentSocket.SafeHandle.SkipCompletionPortOnSuccess) + { + // The socket handle is configured to skip completion on success, + // so we can set the results right now. + _singleBufferHandleState = SingleBufferHandleState.None; + FinishOperationSyncSuccess(bytesTransferred, SocketFlags.None); + return SocketError.Success; + } + + // Completed synchronously, but the handle wasn't marked as skip completion port on success, + // so we still need to fall through and behave as if the IO was pending. } + else + { + // Get the socket error (which may be IOPending) + socketError = SocketPal.GetLastSocketError(); + if (socketError != SocketError.IOPending) + { + // Completed synchronously with a failure. + _singleBufferHandleState = SingleBufferHandleState.None; + FinishOperationSyncFailure(socketError, bytesTransferred, SocketFlags.None); + return socketError; + } + + // Fall through to IOPending handling for asynchronous completion. + } + + // Socket handle is going to post a completion to the completion port (may have done so already). + // Return pending and we will continue in the completion port callback. + if (_singleBufferHandleState == SingleBufferHandleState.InProcess) + { + _singleBufferHandle = _buffer.Retain(pin: true); + _singleBufferHandleState = SingleBufferHandleState.Set; + } + return SocketError.IOPending; } internal unsafe SocketError DoOperationAccept(Socket socket, SafeCloseSocket handle, SafeCloseSocket acceptHandle) @@ -180,69 +169,77 @@ namespace System.Net.Sockets NativeOverlapped* overlapped = AllocateNativeOverlapped(); try { - int bytesTransferred; + bool userBuffer = _count != 0; + Debug.Assert(!userBuffer || (!_buffer.Equals(default) && _count >= _acceptAddressBufferCount)); + Memory buffer = userBuffer ? _buffer : _acceptBuffer; + Debug.Assert(_singleBufferHandleState == SingleBufferHandleState.None); + _singleBufferHandle = buffer.Retain(pin: true); + _singleBufferHandleState = SingleBufferHandleState.Set; bool success = socket.AcceptEx( handle, acceptHandle, - (_ptrSingleBuffer != IntPtr.Zero) ? _ptrSingleBuffer : _ptrAcceptBuffer, - (_ptrSingleBuffer != IntPtr.Zero) ? Count - _acceptAddressBufferCount : 0, + userBuffer ? (IntPtr)((byte*)_singleBufferHandle.Pointer + _offset) : (IntPtr)_singleBufferHandle.Pointer, + userBuffer ? _count - _acceptAddressBufferCount : 0, _acceptAddressBufferCount / 2, _acceptAddressBufferCount / 2, - out bytesTransferred, + out int bytesTransferred, overlapped); socketError = ProcessIOCPResult(success, bytesTransferred); return socketError; } + catch + { + _singleBufferHandle.Dispose(); + _singleBufferHandleState = SingleBufferHandleState.None; + throw; + } finally { FreeNativeOverlappedIfNotPending(overlapped, socketError); } } - private void InnerStartOperationConnect() - { - // ConnectEx uses a sockaddr buffer containing he remote address to which to connect. - // It can also optionally take a single buffer of data to send after the connection is complete. - // - // The sockaddr is pinned with a GCHandle to avoid having to use the object array form of UnsafePack. - // The optional buffer is pinned using the Overlapped.UnsafePack method that takes a single object to pin. - PinSocketAddressBuffer(); - CheckPinNoBuffer(); - } - internal unsafe SocketError DoOperationConnect(Socket socket, SafeCloseSocket handle) { + // ConnectEx uses a sockaddr buffer containing the remote address to which to connect. + // It can also optionally take a single buffer of data to send after the connection is complete. + // The sockaddr is pinned with a GCHandle to avoid having to use the object array form of UnsafePack. + PinSocketAddressBuffer(); + SocketError socketError = SocketError.Success; NativeOverlapped* overlapped = AllocateNativeOverlapped(); try { - int bytesTransferred; + Debug.Assert(_singleBufferHandleState == SingleBufferHandleState.None); + _singleBufferHandle = _buffer.Retain(pin: true); + _singleBufferHandleState = SingleBufferHandleState.Set; bool success = socket.ConnectEx( handle, PtrSocketAddressBuffer, _socketAddress.Size, - _ptrSingleBuffer, - Count, - out bytesTransferred, + (IntPtr)((byte*)_singleBufferHandle.Pointer + _offset), + _count, + out int bytesTransferred, overlapped); - + socketError = ProcessIOCPResult(success, bytesTransferred); return socketError; } + catch + { + _singleBufferHandle.Dispose(); + _singleBufferHandleState = SingleBufferHandleState.None; + throw; + } finally { FreeNativeOverlappedIfNotPending(overlapped, socketError); } } - private void InnerStartOperationDisconnect() - { - CheckPinNoBuffer(); - } - internal unsafe SocketError DoOperationDisconnect(Socket socket, SafeCloseSocket handle) { SocketError socketError = SocketError.Success; @@ -264,57 +261,63 @@ namespace System.Net.Sockets } } - private void InnerStartOperationReceive() - { - // WWSARecv uses a WSABuffer array describing buffers of data to send. - // - // Single and multiple buffers are handled differently so as to optimize - // performance for the more common single buffer case. - // - // For a single buffer: - // The Overlapped.UnsafePack method is used that takes a single object to pin. - // A single WSABuffer that pre-exists in SocketAsyncEventArgs is used. - // - // For multiple buffers: - // The Overlapped.UnsafePack method is used that takes an array of objects to pin. - // An array to reference the multiple buffer is allocated. - // An array of WSABuffer descriptors is allocated. - } + internal SocketError DoOperationReceive(SafeCloseSocket handle) => _bufferList == null ? + DoOperationReceiveSingleBuffer(handle) : + DoOperationReceiveMultiBuffer(handle); - internal unsafe SocketError DoOperationReceive(SafeCloseSocket handle, out SocketFlags flags) + internal unsafe SocketError DoOperationReceiveSingleBuffer(SafeCloseSocket handle) { - flags = _socketFlags; - SocketError socketError = SocketError.Success; NativeOverlapped* overlapped = AllocateNativeOverlapped(); try { - int bytesTransferred; + fixed (byte* bufferPtr = &MemoryMarshal.GetReference(_buffer.Span)) + { + Debug.Assert(_singleBufferHandleState == SingleBufferHandleState.None, $"Expected None, got {_singleBufferHandleState}"); + _singleBufferHandleState = SingleBufferHandleState.InProcess; + var wsaBuffer = new WSABuffer { Length = _count, Pointer = (IntPtr)(bufferPtr + _offset) }; + SocketFlags flags = _socketFlags; - if (_buffer != null) - { - // Single buffer case. socketError = Interop.Winsock.WSARecv( handle.DangerousGetHandle(), // to minimize chances of handle recycling from misuse, this should use DangerousAddRef/Release, but it adds too much overhead - ref _wsaBuffer, + ref wsaBuffer, 1, - out bytesTransferred, - ref flags, - overlapped, - IntPtr.Zero); - } - else - { - // Multi buffer case. - socketError = Interop.Winsock.WSARecv( - handle.DangerousGetHandle(), // to minimize chances of handle recycling from misuse, this should use DangerousAddRef/Release, but it adds too much overhead - _wsaBufferArray, - _bufferListInternal.Count, - out bytesTransferred, + out int bytesTransferred, ref flags, overlapped, IntPtr.Zero); + GC.KeepAlive(handle); // small extra safe guard against handle getting collected/finalized while P/Invoke in progress + + socketError = ProcessIOCPResultWithSingleBufferHandle(socketError, bytesTransferred); } + return socketError; + } + catch + { + _singleBufferHandleState = SingleBufferHandleState.None; + throw; + } + finally + { + FreeNativeOverlappedIfNotPending(overlapped, socketError); + } + } + + internal unsafe SocketError DoOperationReceiveMultiBuffer(SafeCloseSocket handle) + { + SocketError socketError = SocketError.Success; + NativeOverlapped* overlapped = AllocateNativeOverlapped(); + try + { + SocketFlags flags = _socketFlags; + socketError = Interop.Winsock.WSARecv( + handle.DangerousGetHandle(), // to minimize chances of handle recycling from misuse, this should use DangerousAddRef/Release, but it adds too much overhead + _wsaBufferArray, + _bufferListInternal.Count, + out int bytesTransferred, + ref flags, + overlapped, + IntPtr.Zero); GC.KeepAlive(handle); // small extra safe guard against handle getting collected/finalized while P/Invoke in progress socketError = ProcessIOCPResult(socketError == SocketError.Success, bytesTransferred); @@ -326,62 +329,79 @@ namespace System.Net.Sockets } } - private void InnerStartOperationReceiveFrom() + internal unsafe SocketError DoOperationReceiveFrom(SafeCloseSocket handle) { - // WSARecvFrom uses e a WSABuffer array describing buffers in which to + // WSARecvFrom uses a WSABuffer array describing buffers in which to // receive data and from which to send data respectively. Single and multiple buffers // are handled differently so as to optimize performance for the more common single buffer case. - // - // For a single buffer: - // The Overlapped.UnsafePack method is used that takes a single object to pin. - // A single WSABuffer that pre-exists in SocketAsyncEventArgs is used. - // - // For multiple buffers: - // The Overlapped.UnsafePack method is used that takes an array of objects to pin. - // An array to reference the multiple buffer is allocated. - // An array of WSABuffer descriptors is allocated. - // // WSARecvFrom and WSASendTo also uses a sockaddr buffer in which to store the address from which the data was received. // The sockaddr is pinned with a GCHandle to avoid having to use the object array form of UnsafePack. PinSocketAddressBuffer(); + + return _bufferList == null ? + DoOperationReceiveFromSingleBuffer(handle) : + DoOperationReceiveFromMultiBuffer(handle); } - internal unsafe SocketError DoOperationReceiveFrom(SafeCloseSocket handle, out SocketFlags flags) + internal unsafe SocketError DoOperationReceiveFromSingleBuffer(SafeCloseSocket handle) { - flags = _socketFlags; SocketError socketError = SocketError.Success; NativeOverlapped* overlapped = AllocateNativeOverlapped(); try { - int bytesTransferred; + fixed (byte* bufferPtr = &MemoryMarshal.GetReference(_buffer.Span)) + { + Debug.Assert(_singleBufferHandleState == SingleBufferHandleState.None); + _singleBufferHandleState = SingleBufferHandleState.InProcess; + var wsaBuffer = new WSABuffer { Length = _count, Pointer = (IntPtr)(bufferPtr + _offset) }; + SocketFlags flags = _socketFlags; - if (_buffer != null) - { socketError = Interop.Winsock.WSARecvFrom( handle.DangerousGetHandle(), // to minimize chances of handle recycling from misuse, this should use DangerousAddRef/Release, but it adds too much overhead - ref _wsaBuffer, + ref wsaBuffer, 1, - out bytesTransferred, - ref flags, - PtrSocketAddressBuffer, - PtrSocketAddressBufferSize, - overlapped, - IntPtr.Zero); - } - else - { - socketError = Interop.Winsock.WSARecvFrom( - handle.DangerousGetHandle(), // to minimize chances of handle recycling from misuse, this should use DangerousAddRef/Release, but it adds too much overhead - _wsaBufferArray, - _bufferListInternal.Count, - out bytesTransferred, + out int bytesTransferred, ref flags, PtrSocketAddressBuffer, PtrSocketAddressBufferSize, overlapped, IntPtr.Zero); + GC.KeepAlive(handle); // small extra safe guard against handle getting collected/finalized while P/Invoke in progress + + socketError = ProcessIOCPResultWithSingleBufferHandle(socketError, bytesTransferred); + return socketError; } + } + catch + { + _singleBufferHandleState = SingleBufferHandleState.None; + throw; + } + finally + { + FreeNativeOverlappedIfNotPending(overlapped, socketError); + } + } + + internal unsafe SocketError DoOperationReceiveFromMultiBuffer(SafeCloseSocket handle) + { + + SocketError socketError = SocketError.Success; + NativeOverlapped* overlapped = AllocateNativeOverlapped(); + try + { + SocketFlags flags = _socketFlags; + socketError = Interop.Winsock.WSARecvFrom( + handle.DangerousGetHandle(), // to minimize chances of handle recycling from misuse, this should use DangerousAddRef/Release, but it adds too much overhead + _wsaBufferArray, + _bufferListInternal.Count, + out int bytesTransferred, + ref flags, + PtrSocketAddressBuffer, + PtrSocketAddressBufferSize, + overlapped, + IntPtr.Zero); GC.KeepAlive(handle); // small extra safe guard against handle getting collected/finalized while P/Invoke in progress socketError = ProcessIOCPResult(socketError == SocketError.Success, bytesTransferred); @@ -393,7 +413,7 @@ namespace System.Net.Sockets } } - private unsafe void InnerStartOperationReceiveMessageFrom() + internal unsafe SocketError DoOperationReceiveMessageFrom(Socket socket, SafeCloseSocket handle) { // WSARecvMsg uses a WSAMsg descriptor. // The WSAMsg buffer is pinned with a GCHandle to avoid complicating the use of Overlapped. @@ -442,13 +462,18 @@ namespace System.Net.Sockets // If single buffer we need a single element WSABuffer. WSABuffer[] wsaRecvMsgWSABufferArray; uint wsaRecvMsgWSABufferCount; - if (_buffer != null) + if (_bufferList == null) { if (_wsaRecvMsgWSABufferArray == null) { _wsaRecvMsgWSABufferArray = new WSABuffer[1]; } - _wsaRecvMsgWSABufferArray[0].Pointer = _ptrSingleBuffer; + + Debug.Assert(_singleBufferHandleState == SingleBufferHandleState.None); + _singleBufferHandle = _buffer.Retain(pin: true); + _singleBufferHandleState = SingleBufferHandleState.Set; + + _wsaRecvMsgWSABufferArray[0].Pointer = (IntPtr)_singleBufferHandle.Pointer; _wsaRecvMsgWSABufferArray[0].Length = _count; wsaRecvMsgWSABufferArray = _wsaRecvMsgWSABufferArray; wsaRecvMsgWSABufferCount = 1; @@ -470,7 +495,7 @@ namespace System.Net.Sockets // Fill in WSAMessageBuffer. unsafe { - Interop.Winsock.WSAMsg* pMessage = (Interop.Winsock.WSAMsg*)PtrWSAMessageBuffer; + Interop.Winsock.WSAMsg* pMessage = (Interop.Winsock.WSAMsg*)Marshal.UnsafeAddrOfPinnedArrayElement(_wsaMessageBuffer, 0); pMessage->socketAddress = PtrSocketAddressBuffer; pMessage->addressLength = (uint)_socketAddress.Size; fixed (void* ptrWSARecvMsgWSABufferArray = &wsaRecvMsgWSABufferArray[0]) @@ -496,96 +521,88 @@ namespace System.Net.Sockets } pMessage->flags = _socketFlags; } - } - private unsafe IntPtr PtrWSAMessageBuffer - { - get - { - Debug.Assert(_wsaMessageBuffer != null); - Debug.Assert(_wsaMessageBuffer.Length == sizeof(Interop.Winsock.WSAMsg)); - Debug.Assert(_wsaMessageBufferGCHandle.IsAllocated); - Debug.Assert(_wsaMessageBufferGCHandle.Target == _wsaMessageBuffer); - fixed (void* ptrWSAMessageBuffer = &_wsaMessageBuffer[0]) - { - return (IntPtr)ptrWSAMessageBuffer; - } - } - } - - internal unsafe SocketError DoOperationReceiveMessageFrom(Socket socket, SafeCloseSocket handle) - { SocketError socketError = SocketError.Success; NativeOverlapped* overlapped = AllocateNativeOverlapped(); try { - int bytesTransferred; - socketError = socket.WSARecvMsg( handle, - PtrWSAMessageBuffer, - out bytesTransferred, + Marshal.UnsafeAddrOfPinnedArrayElement(_wsaMessageBuffer, 0), + out int bytesTransferred, overlapped, IntPtr.Zero); - socketError = ProcessIOCPResult(socketError == SocketError.Success, bytesTransferred); + socketError = ProcessIOCPResultWithSingleBufferHandle(socketError, bytesTransferred); return socketError; } + catch + { + _singleBufferHandle.Dispose(); + _singleBufferHandleState = SingleBufferHandleState.None; + throw; + } finally { FreeNativeOverlappedIfNotPending(overlapped, socketError); } } - private void InnerStartOperationSend() - { - // WSASend uses a WSABuffer array describing buffers of data to send. - // - // Single and multiple buffers are handled differently so as to optimize - // performance for the more common single buffer case. - // - // For a single buffer: - // The Overlapped.UnsafePack method is used that takes a single object to pin. - // A single WSABuffer that pre-exists in SocketAsyncEventArgs is used. - // - // For multiple buffers: - // The Overlapped.UnsafePack method is used that takes an array of objects to pin. - // An array to reference the multiple buffer is allocated. - // An array of WSABuffer descriptors is allocated. - } + internal unsafe SocketError DoOperationSend(SafeCloseSocket handle) => _bufferList == null ? + DoOperationSendSingleBuffer(handle) : + DoOperationSendMultiBuffer(handle); - internal unsafe SocketError DoOperationSend(SafeCloseSocket handle) + internal unsafe SocketError DoOperationSendSingleBuffer(SafeCloseSocket handle) { SocketError socketError = SocketError.Success; NativeOverlapped* overlapped = AllocateNativeOverlapped(); try { - int bytesTransferred; + fixed (byte* bufferPtr = &MemoryMarshal.GetReference(_buffer.Span)) + { + Debug.Assert(_singleBufferHandleState == SingleBufferHandleState.None); + _singleBufferHandleState = SingleBufferHandleState.InProcess; + var wsaBuffer = new WSABuffer { Length = _count, Pointer = (IntPtr)(bufferPtr + _offset) }; - if (_buffer != null) - { - // Single buffer case. socketError = Interop.Winsock.WSASend( handle.DangerousGetHandle(), // to minimize chances of handle recycling from misuse, this should use DangerousAddRef/Release, but it adds too much overhead - ref _wsaBuffer, + ref wsaBuffer, 1, - out bytesTransferred, - _socketFlags, - overlapped, - IntPtr.Zero); - } - else - { - // Multi buffer case. - socketError = Interop.Winsock.WSASend( - handle.DangerousGetHandle(), // to minimize chances of handle recycling from misuse, this should use DangerousAddRef/Release, but it adds too much overhead - _wsaBufferArray, - _bufferListInternal.Count, - out bytesTransferred, + out int bytesTransferred, _socketFlags, overlapped, IntPtr.Zero); + GC.KeepAlive(handle); // small extra safe guard against handle getting collected/finalized while P/Invoke in progress + + socketError = ProcessIOCPResultWithSingleBufferHandle(socketError, bytesTransferred); + return socketError; } + } + catch + { + _singleBufferHandleState = SingleBufferHandleState.None; + throw; + } + finally + { + FreeNativeOverlappedIfNotPending(overlapped, socketError); + } + } + + internal unsafe SocketError DoOperationSendMultiBuffer(SafeCloseSocket handle) + { + SocketError socketError = SocketError.Success; + NativeOverlapped* overlapped = AllocateNativeOverlapped(); + try + { + socketError = Interop.Winsock.WSASend( + handle.DangerousGetHandle(), // to minimize chances of handle recycling from misuse, this should use DangerousAddRef/Release, but it adds too much overhead + _wsaBufferArray, + _bufferListInternal.Count, + out int bytesTransferred, + _socketFlags, + overlapped, + IntPtr.Zero); GC.KeepAlive(handle); // small extra safe guard against handle getting collected/finalized while P/Invoke in progress socketError = ProcessIOCPResult(socketError == SocketError.Success, bytesTransferred); @@ -597,52 +614,44 @@ namespace System.Net.Sockets } } - private void InnerStartOperationSendPackets() + internal unsafe SocketError DoOperationSendPackets(Socket socket, SafeCloseSocket handle) { - // Prevent mutithreaded manipulation of the list. - if (_sendPacketsElements != null) - { - _sendPacketsElementsInternal = (SendPacketsElement[])_sendPacketsElements.Clone(); - } + // Cache copy to avoid problems with concurrent manipulation during the async operation. + Debug.Assert(_sendPacketsElements != null); + SendPacketsElement[] sendPacketsElementsCopy = (SendPacketsElement[])_sendPacketsElements.Clone(); // TransmitPackets uses an array of TRANSMIT_PACKET_ELEMENT structs as // descriptors for buffers and files to be sent. It also takes a send size // and some flags. The TRANSMIT_PACKET_ELEMENT for a file contains a native file handle. - // This function basically opens the files to get the file handles, pins down any buffers - // specified and builds the native TRANSMIT_PACKET_ELEMENT array that will be passed - // to TransmitPackets. + // Opens the files to get the file handles, pin down any buffers specified and builds the + // native TRANSMIT_PACKET_ELEMENT array that will be passed to TransmitPackets. // Scan the elements to count files and buffers. - _sendPacketsElementsFileCount = 0; - _sendPacketsElementsBufferCount = 0; - - Debug.Assert(_sendPacketsElementsInternal != null); - - foreach (SendPacketsElement spe in _sendPacketsElementsInternal) + int sendPacketsElementsFileCount = 0, sendPacketsElementsBufferCount = 0; + foreach (SendPacketsElement spe in sendPacketsElementsCopy) { if (spe != null) { if (spe._filePath != null) { - _sendPacketsElementsFileCount++; + sendPacketsElementsFileCount++; } - if (spe._buffer != null && spe._count > 0) + else if (spe._buffer != null && spe._count > 0) { - _sendPacketsElementsBufferCount++; + sendPacketsElementsBufferCount++; } } } // Attempt to open the files if any were given. - if (_sendPacketsElementsFileCount > 0) + if (sendPacketsElementsFileCount > 0) { - // Create arrays for streams and handles. - _sendPacketsFileStreams = new FileStream[_sendPacketsElementsFileCount]; - _sendPacketsFileHandles = new SafeHandle[_sendPacketsElementsFileCount]; + // Create arrays for streams. + _sendPacketsFileStreams = new FileStream[sendPacketsElementsFileCount]; // Loop through the elements attempting to open each files and get its handle. int index = 0; - foreach (SendPacketsElement spe in _sendPacketsElementsInternal) + foreach (SendPacketsElement spe in sendPacketsElementsCopy) { if (spe != null && spe._filePath != null) { @@ -660,44 +669,33 @@ namespace System.Net.Sockets } if (fileStreamException != null) { - // Got an exception opening a file - do some cleanup then throw. - for (int i = 0; i < _sendPacketsElementsFileCount; i++) + // Got an exception opening a file - close any open streams, then throw. + for (int i = 0; i < sendPacketsElementsFileCount; i++) { - // Drop handles. - _sendPacketsFileHandles[i] = null; - - // Close any open streams. - if (_sendPacketsFileStreams[i] != null) - { - _sendPacketsFileStreams[i].Dispose(); - _sendPacketsFileStreams[i] = null; - } + _sendPacketsFileStreams[i]?.Dispose(); } + _sendPacketsFileStreams = null; throw fileStreamException; } // Get the file handle from the stream. - _sendPacketsFileHandles[index] = _sendPacketsFileStreams[index].SafeFileHandle; index++; } } } - CheckPinSendPackets(); - } + if (sendPacketsElementsFileCount + sendPacketsElementsBufferCount == 0) + { + FinishOperationSyncSuccess(0, SocketFlags.None); + return SocketError.Success; + } - internal unsafe SocketError DoOperationSendPackets(Socket socket, SafeCloseSocket handle) - { - Debug.Assert(_sendPacketsDescriptor != null); - Debug.Assert(_sendPacketsDescriptor.Length > 0); + Interop.Winsock.TransmitPacketsElement[] sendPacketsDescriptor = SetupPinHandlesSendPackets(sendPacketsElementsCopy, sendPacketsElementsFileCount, sendPacketsElementsBufferCount); + Debug.Assert(sendPacketsDescriptor != null); + Debug.Assert(sendPacketsDescriptor.Length > 0); Debug.Assert(_multipleBufferGCHandles != null); Debug.Assert(_multipleBufferGCHandles[0].IsAllocated); - Debug.Assert(_multipleBufferGCHandles[0].Target == _sendPacketsDescriptor); - IntPtr ptrSendPacketsDescriptor; - fixed (void* p = &_sendPacketsDescriptor[0]) - { - ptrSendPacketsDescriptor = (IntPtr)p; - } + Debug.Assert(_multipleBufferGCHandles[0].Target == sendPacketsDescriptor); SocketError socketError = SocketError.Success; NativeOverlapped* overlapped = AllocateNativeOverlapped(); @@ -705,8 +703,8 @@ namespace System.Net.Sockets { bool result = socket.TransmitPackets( handle, - ptrSendPacketsDescriptor, - _sendPacketsDescriptor.Length, + _multipleBufferGCHandles[0].AddrOfPinnedObject(), + sendPacketsDescriptor.Length, _sendPacketsSendSize, overlapped, _sendPacketsFlags); @@ -720,61 +718,76 @@ namespace System.Net.Sockets } } - private void InnerStartOperationSendTo() + internal unsafe SocketError DoOperationSendTo(SafeCloseSocket handle) { // WSASendTo uses a WSABuffer array describing buffers in which to // receive data and from which to send data respectively. Single and multiple buffers // are handled differently so as to optimize performance for the more common single buffer case. // - // For a single buffer: - // The Overlapped.UnsafePack method is used that takes a single object to pin. - // A single WSABuffer that pre-exists in SocketAsyncEventArgs is used. - // - // For multiple buffers: - // The Overlapped.UnsafePack method is used that takes an array of objects to pin. - // An array to reference the multiple buffer is allocated. - // An array of WSABuffer descriptors is allocated. - // // WSARecvFrom and WSASendTo also uses a sockaddr buffer in which to store the address from which the data was received. // The sockaddr is pinned with a GCHandle to avoid having to use the object array form of UnsafePack. PinSocketAddressBuffer(); + + return _bufferList == null ? + DoOperationSendToSingleBuffer(handle) : + DoOperationSendToMultiBuffer(handle); } - internal unsafe SocketError DoOperationSendTo(SafeCloseSocket handle) + internal unsafe SocketError DoOperationSendToSingleBuffer(SafeCloseSocket handle) { SocketError socketError = SocketError.Success; NativeOverlapped* overlapped = AllocateNativeOverlapped(); try { - int bytesTransferred; + fixed (byte* bufferPtr = &MemoryMarshal.GetReference(_buffer.Span)) + { + Debug.Assert(_singleBufferHandleState == SingleBufferHandleState.None); + _singleBufferHandleState = SingleBufferHandleState.InProcess; + var wsaBuffer = new WSABuffer { Length = _count, Pointer = (IntPtr)(bufferPtr + _offset) }; - if (_buffer != null) - { - // Single buffer case. socketError = Interop.Winsock.WSASendTo( handle.DangerousGetHandle(), // to minimize chances of handle recycling from misuse, this should use DangerousAddRef/Release, but it adds too much overhead - ref _wsaBuffer, + ref wsaBuffer, 1, - out bytesTransferred, - _socketFlags, - PtrSocketAddressBuffer, - _socketAddress.Size, - overlapped, - IntPtr.Zero); - } - else - { - socketError = Interop.Winsock.WSASendTo( - handle.DangerousGetHandle(), // to minimize chances of handle recycling from misuse, this should use DangerousAddRef/Release, but it adds too much overhead - _wsaBufferArray, - _bufferListInternal.Count, - out bytesTransferred, + out int bytesTransferred, _socketFlags, PtrSocketAddressBuffer, _socketAddress.Size, overlapped, IntPtr.Zero); + GC.KeepAlive(handle); // small extra safe guard against handle getting collected/finalized while P/Invoke in progress + + socketError = ProcessIOCPResultWithSingleBufferHandle(socketError, bytesTransferred); + return socketError; } + } + catch + { + _singleBufferHandleState = SingleBufferHandleState.None; + throw; + } + finally + { + FreeNativeOverlappedIfNotPending(overlapped, socketError); + } + } + + internal unsafe SocketError DoOperationSendToMultiBuffer(SafeCloseSocket handle) + { + SocketError socketError = SocketError.Success; + NativeOverlapped* overlapped = AllocateNativeOverlapped(); + try + { + socketError = Interop.Winsock.WSASendTo( + handle.DangerousGetHandle(), // to minimize chances of handle recycling from misuse, this should use DangerousAddRef/Release, but it adds too much overhead + _wsaBufferArray, + _bufferListInternal.Count, + out int bytesTransferred, + _socketFlags, + PtrSocketAddressBuffer, + _socketAddress.Size, + overlapped, + IntPtr.Zero); GC.KeepAlive(handle); // small extra safe guard against handle getting collected/finalized while P/Invoke in progress socketError = ProcessIOCPResult(socketError == SocketError.Success, bytesTransferred); @@ -786,68 +799,8 @@ namespace System.Net.Sockets } } - // Ensures Overlapped object exists for operations that need no data buffer. - private void CheckPinNoBuffer() - { - // PreAllocatedOverlapped will be reused. - if (_pinState == PinState.None) - { - SetupPinHandlesSingle(true); - } - } - - // Maintains pinned state of single buffer. - private void CheckPinSingleBuffer(bool pinUsersBuffer) - { - if (pinUsersBuffer) - { - // Using app supplied buffer. - if (_buffer == null) - { - // No user buffer is set so unpin any existing single buffer pinning. - if (_pinState == PinState.SingleBuffer) - { - FreePinHandles(); - } - } - else - { - if (_pinState == PinState.SingleBuffer && _pinnedSingleBuffer == _buffer) - { - // This buffer is already pinned - update if offset or count has changed. - if (_offset != _pinnedSingleBufferOffset) - { - _pinnedSingleBufferOffset = _offset; - _ptrSingleBuffer = Marshal.UnsafeAddrOfPinnedArrayElement(_buffer, _offset); - _wsaBuffer.Pointer = _ptrSingleBuffer; - } - if (_count != _pinnedSingleBufferCount) - { - _pinnedSingleBufferCount = _count; - _wsaBuffer.Length = _count; - } - } - else - { - FreePinHandles(); - SetupPinHandlesSingle(true); - } - } - } - else - { - // Using internal accept buffer. - if (!(_pinState == PinState.SingleAcceptBuffer) || !(_pinnedSingleBuffer == _acceptBuffer)) - { - // Not already pinned - so pin it. - FreePinHandles(); - SetupPinHandlesSingle(false); - } - } - } - // Ensures Overlapped object exists with appropriate multiple buffers pinned. - private void CheckPinMultipleBuffers() + private void SetupMultipleBuffers() { if (_bufferListInternal == null || _bufferListInternal.Count == 0) { @@ -863,7 +816,45 @@ namespace System.Net.Sockets FreePinHandles(); try { - SetupPinHandlesMultiple(); + int bufferCount = _bufferListInternal.Count; + +#if DEBUG + if (_multipleBufferGCHandles != null) + { + foreach (GCHandle gcHandle in _multipleBufferGCHandles) + { + Debug.Assert(!gcHandle.IsAllocated); + } + } +#endif + + // Number of things to pin is number of buffers. + // Ensure we have properly sized object array. + if (_multipleBufferGCHandles == null || (_multipleBufferGCHandles.Length < bufferCount)) + { + _multipleBufferGCHandles = new GCHandle[bufferCount]; + } + + // Pin the buffers. + for (int i = 0; i < bufferCount; i++) + { + Debug.Assert(!_multipleBufferGCHandles[i].IsAllocated); + _multipleBufferGCHandles[i] = GCHandle.Alloc(_bufferListInternal[i].Array, GCHandleType.Pinned); + } + + if (_wsaBufferArray == null || _wsaBufferArray.Length < bufferCount) + { + _wsaBufferArray = new WSABuffer[bufferCount]; + } + + for (int i = 0; i < bufferCount; i++) + { + ArraySegment localCopy = _bufferListInternal[i]; + _wsaBufferArray[i].Pointer = Marshal.UnsafeAddrOfPinnedArrayElement(localCopy.Array, localCopy.Offset); + _wsaBufferArray[i].Length = localCopy.Count; + } + + _pinState = PinState.MultipleBuffer; } catch (Exception) { @@ -873,16 +864,6 @@ namespace System.Net.Sockets } } - // Ensures Overlapped object exists with appropriate buffers pinned. - private void CheckPinSendPackets() - { - if (_pinState != PinState.None) - { - FreePinHandles(); - } - SetupPinHandlesSendPackets(); - } - // Ensures appropriate SocketAddress buffer is pinned. private void PinSocketAddressBuffer() { @@ -938,15 +919,10 @@ namespace System.Net.Sockets { _pinState = PinState.None; - // Free any allocated GCHandles. - if (_singleBufferGCHandle.IsAllocated) + if (_singleBufferHandleState != SingleBufferHandleState.None) { - _singleBufferGCHandle.Free(); - - _pinnedAcceptBuffer = null; - _pinnedSingleBuffer = null; - _pinnedSingleBufferOffset = 0; - _pinnedSingleBufferCount = 0; + _singleBufferHandle.Dispose(); + _singleBufferHandleState = SingleBufferHandleState.None; } if (_multipleBufferGCHandles != null) @@ -982,101 +958,17 @@ namespace System.Net.Sockets } } - // Sets up an Overlapped object with either _buffer or _acceptBuffer pinned. - private unsafe void SetupPinHandlesSingle(bool pinSingleBuffer) - { - Debug.Assert(!_singleBufferGCHandle.IsAllocated); - - // Pin buffer, get native pointers, and fill in WSABuffer descriptor. - if (pinSingleBuffer) - { - if (_buffer != null) - { - _singleBufferGCHandle = GCHandle.Alloc(_buffer, GCHandleType.Pinned); - - _pinnedSingleBuffer = _buffer; - _pinnedSingleBufferOffset = _offset; - _pinnedSingleBufferCount = _count; - _ptrSingleBuffer = Marshal.UnsafeAddrOfPinnedArrayElement(_buffer, _offset); - _ptrAcceptBuffer = IntPtr.Zero; - _wsaBuffer.Pointer = _ptrSingleBuffer; - _wsaBuffer.Length = _count; - _pinState = PinState.SingleBuffer; - } - else - { - _pinnedSingleBuffer = null; - _pinnedSingleBufferOffset = 0; - _pinnedSingleBufferCount = 0; - _ptrSingleBuffer = IntPtr.Zero; - _ptrAcceptBuffer = IntPtr.Zero; - _wsaBuffer.Pointer = _ptrSingleBuffer; - _wsaBuffer.Length = _count; - _pinState = PinState.NoBuffer; - } - } - else - { - _singleBufferGCHandle = GCHandle.Alloc(_acceptBuffer, GCHandleType.Pinned); - - _pinnedAcceptBuffer = _acceptBuffer; - _ptrAcceptBuffer = Marshal.UnsafeAddrOfPinnedArrayElement(_acceptBuffer, 0); - _ptrSingleBuffer = IntPtr.Zero; - _pinState = PinState.SingleAcceptBuffer; - } - } - - // Sets up an Overlapped object with multiple buffers pinned. - private unsafe void SetupPinHandlesMultiple() - { - int bufferCount = _bufferListInternal.Count; - -#if DEBUG - if (_multipleBufferGCHandles != null) - { - foreach (GCHandle gcHandle in _multipleBufferGCHandles) - { - Debug.Assert(!gcHandle.IsAllocated); - } - } -#endif - - // Number of things to pin is number of buffers. - // Ensure we have properly sized object array. - if (_multipleBufferGCHandles == null || (_multipleBufferGCHandles.Length < bufferCount)) - { - _multipleBufferGCHandles = new GCHandle[bufferCount]; - } - - // Pin the buffers. - for (int i = 0; i < bufferCount; i++) - { - Debug.Assert(!_multipleBufferGCHandles[i].IsAllocated); - _multipleBufferGCHandles[i] = GCHandle.Alloc(_bufferListInternal[i].Array, GCHandleType.Pinned); - } - - if (_wsaBufferArray == null || _wsaBufferArray.Length < bufferCount) - { - _wsaBufferArray = new WSABuffer[bufferCount]; - } - - for (int i = 0; i < bufferCount; i++) - { - ArraySegment localCopy = _bufferListInternal[i]; - _wsaBufferArray[i].Pointer = Marshal.UnsafeAddrOfPinnedArrayElement(localCopy.Array, localCopy.Offset); - _wsaBufferArray[i].Length = localCopy.Count; - } - _pinState = PinState.MultipleBuffer; - } - // Sets up an Overlapped object for SendPacketsAsync. - private unsafe void SetupPinHandlesSendPackets() + private unsafe Interop.Winsock.TransmitPacketsElement[] SetupPinHandlesSendPackets( + SendPacketsElement[] sendPacketsElementsCopy, int sendPacketsElementsFileCount, int sendPacketsElementsBufferCount) { - int index; + if (_pinState != PinState.None) + { + FreePinHandles(); + } // Alloc native descriptor. - _sendPacketsDescriptor = - new Interop.Winsock.TransmitPacketsElement[_sendPacketsElementsFileCount + _sendPacketsElementsBufferCount]; + var sendPacketsDescriptor = new Interop.Winsock.TransmitPacketsElement[sendPacketsElementsFileCount + sendPacketsElementsBufferCount]; // Number of things to pin is number of buffers + 1 (native descriptor). // Ensure we have properly sized object array. @@ -1090,16 +982,16 @@ namespace System.Net.Sockets } #endif - if (_multipleBufferGCHandles == null || (_multipleBufferGCHandles.Length < _sendPacketsElementsBufferCount + 1)) + if (_multipleBufferGCHandles == null || (_multipleBufferGCHandles.Length < sendPacketsElementsBufferCount + 1)) { - _multipleBufferGCHandles = new GCHandle[_sendPacketsElementsBufferCount + 1]; + _multipleBufferGCHandles = new GCHandle[sendPacketsElementsBufferCount + 1]; } // Pin objects. Native descriptor buffer first and then user specified buffers. Debug.Assert(!_multipleBufferGCHandles[0].IsAllocated); - _multipleBufferGCHandles[0] = GCHandle.Alloc(_sendPacketsDescriptor, GCHandleType.Pinned); - index = 1; - foreach (SendPacketsElement spe in _sendPacketsElementsInternal) + _multipleBufferGCHandles[0] = GCHandle.Alloc(sendPacketsDescriptor, GCHandleType.Pinned); + int index = 1; + foreach (SendPacketsElement spe in sendPacketsElementsCopy) { if (spe != null && spe._buffer != null && spe._count > 0) { @@ -1113,25 +1005,25 @@ namespace System.Net.Sockets // Fill in native descriptor. int descriptorIndex = 0; int fileIndex = 0; - foreach (SendPacketsElement spe in _sendPacketsElementsInternal) + foreach (SendPacketsElement spe in sendPacketsElementsCopy) { if (spe != null) { if (spe._buffer != null && spe._count > 0) { // This element is a buffer. - _sendPacketsDescriptor[descriptorIndex].buffer = Marshal.UnsafeAddrOfPinnedArrayElement(spe._buffer, spe._offset); - _sendPacketsDescriptor[descriptorIndex].length = (uint)spe._count; - _sendPacketsDescriptor[descriptorIndex].flags = (Interop.Winsock.TransmitPacketsElementFlags)spe._flags; + sendPacketsDescriptor[descriptorIndex].buffer = Marshal.UnsafeAddrOfPinnedArrayElement(spe._buffer, spe._offset); + sendPacketsDescriptor[descriptorIndex].length = (uint)spe._count; + sendPacketsDescriptor[descriptorIndex].flags = (Interop.Winsock.TransmitPacketsElementFlags)spe._flags; descriptorIndex++; } else if (spe._filePath != null) { // This element is a file. - _sendPacketsDescriptor[descriptorIndex].fileHandle = _sendPacketsFileHandles[fileIndex].DangerousGetHandle(); - _sendPacketsDescriptor[descriptorIndex].fileOffset = spe._offset; - _sendPacketsDescriptor[descriptorIndex].length = (uint)spe._count; - _sendPacketsDescriptor[descriptorIndex].flags = (Interop.Winsock.TransmitPacketsElementFlags)spe._flags; + sendPacketsDescriptor[descriptorIndex].fileHandle = _sendPacketsFileStreams[fileIndex].SafeFileHandle.DangerousGetHandle(); + sendPacketsDescriptor[descriptorIndex].fileOffset = spe._offset; + sendPacketsDescriptor[descriptorIndex].length = (uint)spe._count; + sendPacketsDescriptor[descriptorIndex].flags = (Interop.Winsock.TransmitPacketsElementFlags)spe._flags; fileIndex++; descriptorIndex++; } @@ -1139,6 +1031,7 @@ namespace System.Net.Sockets } _pinState = PinState.SendPackets; + return sendPacketsDescriptor; } internal void LogBuffer(int size) @@ -1148,56 +1041,25 @@ namespace System.Net.Sockets // may fire erroneously. Debug.Assert(NetEventSource.IsEnabled); - switch (_pinState) + if (_bufferList != null) { - case PinState.SingleAcceptBuffer: - NetEventSource.DumpBuffer(this, _acceptBuffer, 0, size); - break; - - case PinState.SingleBuffer: - NetEventSource.DumpBuffer(this, _buffer, _offset, size); - break; - - case PinState.MultipleBuffer: - for (int i = 0; i < _bufferListInternal.Count; i++) - { - WSABuffer wsaBuffer = _wsaBufferArray[i]; - NetEventSource.DumpBuffer(this, wsaBuffer.Pointer, Math.Min(wsaBuffer.Length, size)); - if ((size -= wsaBuffer.Length) <= 0) - { - break; - } - } - break; - - default: - break; - } - } - - internal void LogSendPacketsBuffers(int size) - { - if (!NetEventSource.IsEnabled) return; - - foreach (SendPacketsElement spe in _sendPacketsElementsInternal) - { - if (spe != null) + for (int i = 0; i < _bufferListInternal.Count; i++) { - if (spe._buffer != null && spe._count > 0) + WSABuffer wsaBuffer = _wsaBufferArray[i]; + NetEventSource.DumpBuffer(this, wsaBuffer.Pointer, Math.Min(wsaBuffer.Length, size)); + if ((size -= wsaBuffer.Length) <= 0) { - // This element is a buffer. - NetEventSource.DumpBuffer(this, spe._buffer, spe._offset, Math.Min(spe._count, size)); - } - else if (spe._filePath != null) - { - // This element is a file. - NetEventSource.NotLoggedFile(spe._filePath, _currentSocket, _completedOperation); + break; } } } + else if (_buffer.Length != 0) + { + NetEventSource.DumpBuffer(this, _buffer, _offset, size); + } } - private SocketError FinishOperationAccept(Internals.SocketAddress remoteSocketAddress) + private unsafe SocketError FinishOperationAccept(Internals.SocketAddress remoteSocketAddress) { SocketError socketError; IntPtr localAddr; @@ -1206,8 +1068,12 @@ namespace System.Net.Sockets try { + Debug.Assert(_singleBufferHandleState == SingleBufferHandleState.Set); + Debug.Assert(_singleBufferHandle.HasPointer); + bool userBuffer = _count >= _acceptAddressBufferCount; + _currentSocket.GetAcceptExSockaddrs( - _ptrSingleBuffer != IntPtr.Zero ? _ptrSingleBuffer : _ptrAcceptBuffer, + userBuffer ? (IntPtr)((byte*)_singleBufferHandle.Pointer + _offset) : (IntPtr)_singleBufferHandle.Pointer, _count != 0 ? _count - _acceptAddressBufferCount : 0, _acceptAddressBufferCount / 2, _acceptAddressBufferCount / 2, @@ -1243,33 +1109,48 @@ namespace System.Net.Sockets private SocketError FinishOperationConnect() { - SocketError socketError; - - // Update the socket context. try { - socketError = Interop.Winsock.setsockopt( + // Update the socket context. + SocketError socketError = Interop.Winsock.setsockopt( _currentSocket.SafeHandle, SocketOptionLevel.Socket, SocketOptionName.UpdateConnectContext, null, 0); - if (socketError == SocketError.SocketError) - { - socketError = SocketPal.GetLastSocketError(); - } + return socketError == SocketError.SocketError ? + SocketPal.GetLastSocketError() : + socketError; } catch (ObjectDisposedException) { - socketError = SocketError.OperationAborted; + return SocketError.OperationAborted; } - - return socketError; } - private unsafe int GetSocketAddressSize() + private unsafe int GetSocketAddressSize() => *(int*)PtrSocketAddressBufferSize; + + private void CompleteCore() { - return *(int*)PtrSocketAddressBufferSize; + if (_singleBufferHandleState != SingleBufferHandleState.None) + { + CompleteCoreSpin(); + } + + void CompleteCoreSpin() // separate out to help inline the fast path + { + var sw = new SpinWait(); + while (_singleBufferHandleState == SingleBufferHandleState.InProcess) + { + sw.SpinOnce(); + } + + if (_singleBufferHandleState == SingleBufferHandleState.Set) + { + _singleBufferHandle.Dispose(); + _singleBufferHandleState = SingleBufferHandleState.None; + } + } } private unsafe void FinishOperationReceiveMessageFrom() @@ -1298,85 +1179,64 @@ namespace System.Net.Sockets // Close the files if open. if (_sendPacketsFileStreams != null) { - for (int i = 0; i < _sendPacketsElementsFileCount; i++) + for (int i = 0; i < _sendPacketsFileStreams.Length; i++) { - // Drop handles. - _sendPacketsFileHandles[i] = null; - - // Close any open streams. - if (_sendPacketsFileStreams[i] != null) - { - _sendPacketsFileStreams[i].Dispose(); - _sendPacketsFileStreams[i] = null; - } + _sendPacketsFileStreams[i]?.Dispose(); } + + _sendPacketsFileStreams = null; } - _sendPacketsFileStreams = null; - _sendPacketsFileHandles = null; } private static readonly unsafe IOCompletionCallback s_completionPortCallback = delegate (uint errorCode, uint numBytes, NativeOverlapped* nativeOverlapped) { - object state = ThreadPoolBoundHandle.GetNativeOverlappedState(nativeOverlapped); - var saea = (SocketAsyncEventArgs)state; - Debug.Assert(saea != null, $"Expected native overlapped state to contain SAEA, got {state?.GetType().ToString() ?? "(null)"}"); - saea.CompletionPortCallback(errorCode, numBytes, nativeOverlapped); + var saea = (SocketAsyncEventArgs)ThreadPoolBoundHandle.GetNativeOverlappedState(nativeOverlapped); + if ((SocketError)errorCode == SocketError.Success) + { + saea.FreeNativeOverlapped(nativeOverlapped); + saea.FinishOperationAsyncSuccess((int)numBytes, SocketFlags.None); + } + else + { + saea.HandleCompletionPortCallbackError(errorCode, numBytes, nativeOverlapped); + } }; - private unsafe void CompletionPortCallback(uint errorCode, uint numBytes, NativeOverlapped* nativeOverlapped) + private unsafe void HandleCompletionPortCallbackError(uint errorCode, uint numBytes, NativeOverlapped* nativeOverlapped) { -#if DEBUG - DebugThreadTracking.SetThreadSource(ThreadKinds.CompletionPort); - using (DebugThreadTracking.SetThreadKind(ThreadKinds.System)) - { - if (NetEventSource.IsEnabled) NetEventSource.Enter(this, $"errorCode:{errorCode}, numBytes:{numBytes}, overlapped:{(IntPtr)nativeOverlapped}"); -#endif - SocketFlags socketFlags = SocketFlags.None; - SocketError socketError = (SocketError)errorCode; + SocketError socketError = (SocketError)errorCode; + SocketFlags socketFlags = SocketFlags.None; - if (socketError == SocketError.Success) + if (socketError != SocketError.OperationAborted) + { + if (_currentSocket.CleanedUp) { - FreeNativeOverlapped(nativeOverlapped); - FinishOperationAsyncSuccess((int)numBytes, SocketFlags.None); + socketError = SocketError.OperationAborted; } else { - if (socketError != SocketError.OperationAborted) + try { - if (_currentSocket.CleanedUp) - { - socketError = SocketError.OperationAborted; - } - else - { - try - { - // The Async IO completed with a failure. - // here we need to call WSAGetOverlappedResult() just so GetLastSocketError() will return the correct error. - bool success = Interop.Winsock.WSAGetOverlappedResult( - _currentSocket.SafeHandle, - nativeOverlapped, - out numBytes, - false, - out socketFlags); - socketError = SocketPal.GetLastSocketError(); - } - catch - { - // _currentSocket.CleanedUp check above does not always work since this code is subject to race conditions. - socketError = SocketError.OperationAborted; - } - } + // The Async IO completed with a failure. + // here we need to call WSAGetOverlappedResult() just so GetLastSocketError() will return the correct error. + bool success = Interop.Winsock.WSAGetOverlappedResult( + _currentSocket.SafeHandle, + nativeOverlapped, + out numBytes, + false, + out socketFlags); + socketError = SocketPal.GetLastSocketError(); + } + catch + { + // _currentSocket.CleanedUp check above does not always work since this code is subject to race conditions. + socketError = SocketError.OperationAborted; } - - FreeNativeOverlapped(nativeOverlapped); - FinishOperationAsyncFailure(socketError, (int)numBytes, socketFlags); } - -#if DEBUG - if (NetEventSource.IsEnabled) NetEventSource.Exit(this); } -#endif + + FreeNativeOverlapped(nativeOverlapped); + FinishOperationAsyncFailure(socketError, (int)numBytes, socketFlags); } } } diff --git a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs index 5b5eb8c38d..97257b02ff 100644 --- a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs +++ b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs @@ -11,13 +11,14 @@ namespace System.Net.Sockets public partial class SocketAsyncEventArgs : EventArgs, IDisposable { // AcceptSocket property variables. - internal Socket _acceptSocket; + private Socket _acceptSocket; private Socket _connectSocket; - // Buffer,Offset,Count property variables. - internal byte[] _buffer; - internal int _count; - internal int _offset; + // Single buffer. + private Memory _buffer; + private int _offset; + private int _count; + private bool _bufferIsExplicitArray; // BufferList property variables. private IList> _bufferList; @@ -43,27 +44,27 @@ namespace System.Net.Sockets private EndPoint _remoteEndPoint; // SendPacketsSendSize property variable. - internal int _sendPacketsSendSize; + private int _sendPacketsSendSize; // SendPacketsElements property variables. - internal SendPacketsElement[] _sendPacketsElements; + private SendPacketsElement[] _sendPacketsElements; // SendPacketsFlags property variable. - internal TransmitFileOptions _sendPacketsFlags; + private TransmitFileOptions _sendPacketsFlags; // SocketError property variables. private SocketError _socketError; private Exception _connectByNameError; // SocketFlags property variables. - internal SocketFlags _socketFlags; + private SocketFlags _socketFlags; // UserToken property variables. private object _userToken; // Internal buffer for AcceptEx when Buffer not supplied. - internal byte[] _acceptBuffer; - internal int _acceptAddressBufferCount; + private byte[] _acceptBuffer; + private int _acceptAddressBufferCount; // Internal SocketAddress buffer. internal Internals.SocketAddress _socketAddress; @@ -101,27 +102,24 @@ namespace System.Net.Sockets public byte[] Buffer { - get { return _buffer; } + get + { + if (_bufferIsExplicitArray) + { + bool success = _buffer.TryGetArray(out ArraySegment arraySegment); + Debug.Assert(success); + return arraySegment.Array; + } + + return null; + } } - public Memory GetBuffer() - { - // TODO https://github.com/dotnet/corefx/issues/24429: - // Actually support Memory natively. - return _buffer != null ? - new Memory(_buffer, _offset, _count) : - Memory.Empty; - } + public Memory MemoryBuffer => _buffer; - public int Offset - { - get { return _offset; } - } + public int Offset => _offset; - public int Count - { - get { return _count; } - } + public int Count => _count; // SendPacketsFlags property. public TransmitFileOptions SendPacketsFlags @@ -131,7 +129,7 @@ namespace System.Net.Sockets } // NOTE: this property is mutually exclusive with Buffer. - // Setting this property with an existing non-null Buffer will throw. + // Setting this property with an existing non-null Buffer will throw. public IList> BufferList { get { return _bufferList; } @@ -142,7 +140,7 @@ namespace System.Net.Sockets { if (value != null) { - if (_buffer != null) + if (!_buffer.Equals(default)) { // Can't have both set throw new ArgumentException(SR.Format(SR.net_ambiguousbuffers, nameof(Buffer))); @@ -172,7 +170,7 @@ namespace System.Net.Sockets { _bufferListInternal?.Clear(); } - + _bufferList = value; SetupMultipleBuffers(); @@ -205,11 +203,7 @@ namespace System.Net.Sockets protected virtual void OnCompleted(SocketAsyncEventArgs e) { - EventHandler handler = _completed; - if (handler != null) - { - handler(e._currentSocket, e); - } + _completed?.Invoke(e._currentSocket, e); } // DisconnectResuseSocket property. @@ -244,7 +238,6 @@ namespace System.Net.Sockets try { _sendPacketsElements = value; - SetupSendPacketsElements(); } finally { @@ -282,34 +275,53 @@ namespace System.Net.Sockets set { _userToken = value; } } - public void SetBuffer(byte[] buffer, int offset, int count) - { - SetBufferInternal(buffer, offset, count); - } - public void SetBuffer(int offset, int count) { - SetBufferInternal(_buffer, offset, count); - } - - public void SetBuffer(Memory buffer) - { - if (!buffer.TryGetArray(out ArraySegment array)) + StartConfiguring(); + try { - // TODO https://github.com/dotnet/corefx/issues/24429: - // Actually support Memory natively. - throw new ArgumentException(); + if (!_buffer.Equals(default)) + { + if ((uint)offset > _buffer.Length) + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + if ((uint)count > (_buffer.Length - offset)) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + if (!_bufferIsExplicitArray) + { + throw new InvalidOperationException(SR.InvalidOperation_BufferNotExplicitArray); + } + + _offset = offset; + _count = count; + } + } + finally + { + Complete(); } - - SetBuffer(array.Array, array.Offset, array.Count); } - internal bool HasMultipleBuffers + internal void CopyBufferFrom(SocketAsyncEventArgs source) { - get { return _bufferList != null; } + StartConfiguring(); + try + { + _buffer = source._buffer; + _offset = source._offset; + _count = source._count; + _bufferIsExplicitArray = source._bufferIsExplicitArray; + } + finally + { + Complete(); + } } - private void SetBufferInternal(byte[] buffer, int offset, int count) + public void SetBuffer(byte[] buffer, int offset, int count) { StartConfiguring(); try @@ -317,9 +329,10 @@ namespace System.Net.Sockets if (buffer == null) { // Clear out existing buffer. - _buffer = null; + _buffer = default; _offset = 0; _count = 0; + _bufferIsExplicitArray = false; } else { @@ -329,13 +342,13 @@ namespace System.Net.Sockets throw new ArgumentException(SR.Format(SR.net_ambiguousbuffers, nameof(BufferList))); } - // Offset and count can't be negative and the + // Offset and count can't be negative and the // combination must be in bounds of the array. - if (offset < 0 || offset > buffer.Length) + if ((uint)offset > buffer.Length) { throw new ArgumentOutOfRangeException(nameof(offset)); } - if (count < 0 || count > (buffer.Length - offset)) + if ((uint)count > (buffer.Length - offset)) { throw new ArgumentOutOfRangeException(nameof(count)); } @@ -343,10 +356,8 @@ namespace System.Net.Sockets _buffer = buffer; _offset = offset; _count = count; + _bufferIsExplicitArray = true; } - - // Pin new or unpin old buffer if necessary. - SetupSingleBuffer(); } finally { @@ -354,6 +365,29 @@ namespace System.Net.Sockets } } + public void SetBuffer(Memory buffer) + { + StartConfiguring(); + try + { + if (buffer.Length != 0 && _bufferList != null) + { + throw new ArgumentException(SR.Format(SR.net_ambiguousbuffers, nameof(BufferList))); + } + + _buffer = buffer; + _offset = 0; + _count = buffer.Length; + _bufferIsExplicitArray = false; + } + finally + { + Complete(); + } + } + + internal bool HasMultipleBuffers => _bufferList != null; + internal void SetResults(SocketError socketError, int bytesTransferred, SocketFlags flags) { _socketError = socketError; @@ -393,14 +427,16 @@ namespace System.Net.Sockets } // Marks this object as no longer "in-use". Will also execute a Dispose deferred - // because I/O was in progress. + // because I/O was in progress. internal void Complete() { + CompleteCore(); + // Mark as not in-use. _operating = Free; // Check for deferred Dispose(). - // The deferred Dispose is not guaranteed if Dispose is called while an operation is in progress. + // The deferred Dispose is not guaranteed if Dispose is called while an operation is in progress. // The _disposeCalled variable is not managed in a thread-safe manner on purpose for performance. if (_disposeCalled) { @@ -461,7 +497,7 @@ namespace System.Net.Sockets // Prepares for a native async socket call. // This method performs the tasks common to all socket operations. - internal void StartOperationCommon(Socket socket) + internal void StartOperationCommon(Socket socket, SocketAsyncOperation operation) { // Change status to "in-use". int status = Interlocked.CompareExchange(ref _operating, InProgress, Free); @@ -470,6 +506,9 @@ namespace System.Net.Sockets ThrowForNonFreeStatus(status); } + // Set the operation type. + _completedOperation = operation; + // Prepare execution context for callback. // If event delegates have changed or socket has changed // then discard any existing context. @@ -489,9 +528,6 @@ namespace System.Net.Sockets internal void StartOperationAccept() { - // Remember the operation type. - _completedOperation = SocketAsyncOperation.Accept; - // AcceptEx needs a single buffer that's the size of two native sockaddr buffers with 16 // extra bytes each. It can also take additional buffer space in front of those special // sockaddr structures that can be filled in with initial data coming in on a connection. @@ -499,8 +535,8 @@ namespace System.Net.Sockets // If our caller specified a buffer (willing to get received data with the Accept) then // it needs to be large enough for the two special sockaddr buffers that AcceptEx requires. - // Throw if that buffer is not large enough. - bool userSuppliedBuffer = _buffer != null; + // Throw if that buffer is not large enough. + bool userSuppliedBuffer = !_buffer.Equals(default); if (userSuppliedBuffer) { // Caller specified a buffer - see if it is large enough @@ -508,8 +544,6 @@ namespace System.Net.Sockets { throw new ArgumentException(SR.Format(SR.net_buffercounttoosmall, nameof(Count))); } - - // Buffer is already pinned if necessary. } else { @@ -520,24 +554,11 @@ namespace System.Net.Sockets _acceptBuffer = new byte[_acceptAddressBufferCount]; } } - - InnerStartOperationAccept(userSuppliedBuffer); } - internal void StartOperationConnect() + internal void StartOperationConnect(MultipleConnectAsync multipleConnect = null) { - // Remember the operation type. - _completedOperation = SocketAsyncOperation.Connect; - _multipleConnect = null; - _connectSocket = null; - - InnerStartOperationConnect(); - } - - internal void StartOperationWrapperConnect(MultipleConnectAsync args) - { - _completedOperation = SocketAsyncOperation.Connect; - _multipleConnect = args; + _multipleConnect = multipleConnect; _connectSocket = null; } @@ -563,75 +584,6 @@ namespace System.Net.Sockets } } - internal void StartOperationDisconnect() - { - // Remember the operation type. - _completedOperation = SocketAsyncOperation.Disconnect; - InnerStartOperationDisconnect(); - } - - internal void StartOperationReceive() - { - // Remember the operation type. - _completedOperation = SocketAsyncOperation.Receive; - InnerStartOperationReceive(); - } - - internal void StartOperationReceiveFrom() - { - // Remember the operation type. - _completedOperation = SocketAsyncOperation.ReceiveFrom; - InnerStartOperationReceiveFrom(); - } - - internal void StartOperationReceiveMessageFrom() - { - // Remember the operation type. - _completedOperation = SocketAsyncOperation.ReceiveMessageFrom; - InnerStartOperationReceiveMessageFrom(); - } - - internal void StartOperationSend() - { - // Remember the operation type. - _completedOperation = SocketAsyncOperation.Send; - InnerStartOperationSend(); - } - - internal void StartOperationSendPackets() - { - // Remember the operation type. - _completedOperation = SocketAsyncOperation.SendPackets; - InnerStartOperationSendPackets(); - } - - internal void StartOperationSendTo() - { - // Remember the operation type. - _completedOperation = SocketAsyncOperation.SendTo; - InnerStartOperationSendTo(); - } - - internal void UpdatePerfCounters(int size, bool sendOp) - { - if (sendOp) - { - SocketPerfCounter.Instance.Increment(SocketPerfCounterName.SocketBytesSent, size); - if (_currentSocket.Transport == TransportType.Udp) - { - SocketPerfCounter.Instance.Increment(SocketPerfCounterName.SocketDatagramsSent); - } - } - else - { - SocketPerfCounter.Instance.Increment(SocketPerfCounterName.SocketBytesReceived, size); - if (_currentSocket.Transport == TransportType.Udp) - { - SocketPerfCounter.Instance.Increment(SocketPerfCounterName.SocketDatagramsReceived); - } - } - } - internal void FinishOperationSyncFailure(SocketError socketError, int bytesTransferred, SocketFlags flags) { SetResults(socketError, bytesTransferred, flags); @@ -710,9 +662,9 @@ namespace System.Net.Sockets { SetResults(SocketError.Success, bytesTransferred, flags); - if (NetEventSource.IsEnabled || Socket.s_perfCountersEnabled) + if (NetEventSource.IsEnabled && bytesTransferred > 0) { - LogBytesTransferred(bytesTransferred, _completedOperation); + LogBuffer(bytesTransferred); } SocketError socketError = SocketError.Success; @@ -802,32 +754,6 @@ namespace System.Net.Sockets Complete(); } - private void LogBytesTransferred(int bytesTransferred, SocketAsyncOperation operation) - { - if (bytesTransferred > 0) - { - if (NetEventSource.IsEnabled) - { - LogBuffer(bytesTransferred); - } - - if (Socket.s_perfCountersEnabled) - { - bool sendOp = false; - switch (operation) - { - case SocketAsyncOperation.Connect: - case SocketAsyncOperation.Send: - case SocketAsyncOperation.SendPackets: - case SocketAsyncOperation.SendTo: - sendOp = true; - break; - } - UpdatePerfCounters(bytesTransferred, sendOp); - } - } - } - internal void FinishOperationAsyncSuccess(int bytesTransferred, SocketFlags flags) { FinishOperationSyncSuccess(bytesTransferred, flags); diff --git a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs index 0c44554357..3367ac0259 100644 --- a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs +++ b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs @@ -69,7 +69,7 @@ namespace System.Net.Sockets int sockAddrLen = socketAddress != null ? socketAddressLen : 0; fixed (byte* sockAddr = socketAddress) - fixed (byte* b = &buffer.DangerousGetPinnableReference()) + fixed (byte* b = &MemoryMarshal.GetReference(buffer)) { var iov = new Interop.Sys.IOVector { Base = b, @@ -107,7 +107,7 @@ namespace System.Net.Sockets { int sent; fixed (byte* sockAddr = socketAddress) - fixed (byte* b = &buffer.DangerousGetPinnableReference()) + fixed (byte* b = &MemoryMarshal.GetReference(buffer)) { var iov = new Interop.Sys.IOVector { @@ -342,7 +342,7 @@ namespace System.Net.Sockets return checked((int)received); } - private static unsafe int ReceiveMessageFrom(SafeCloseSocket socket, SocketFlags flags, byte[] buffer, int offset, int count, byte[] socketAddress, ref int socketAddressLen, bool isIPv4, bool isIPv6, out SocketFlags receivedFlags, out IPPacketInformation ipPacketInformation, out Interop.Error errno) + private static unsafe int ReceiveMessageFrom(SafeCloseSocket socket, SocketFlags flags, Span buffer, byte[] socketAddress, ref int socketAddressLen, bool isIPv4, bool isIPv6, out SocketFlags receivedFlags, out IPPacketInformation ipPacketInformation, out Interop.Error errno) { Debug.Assert(socketAddress != null, "Expected non-null socketAddress"); @@ -355,17 +355,15 @@ namespace System.Net.Sockets long received; fixed (byte* rawSocketAddress = socketAddress) - fixed (byte* b = buffer) + fixed (byte* b = &MemoryMarshal.GetReference(buffer)) { - var sockAddr = (byte*)rawSocketAddress; - var iov = new Interop.Sys.IOVector { - Base = &b[offset], - Count = (UIntPtr)count + Base = b, + Count = (UIntPtr)buffer.Length }; messageHeader = new Interop.Sys.MessageHeader { - SocketAddress = sockAddr, + SocketAddress = rawSocketAddress, SocketAddressLen = sockAddrLen, IOVectors = &iov, IOVectorCount = 1, @@ -582,9 +580,6 @@ namespace System.Net.Sockets return true; } - public static bool TryCompleteReceiveFrom(SafeCloseSocket socket, byte[] buffer, int offset, int count, SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, out int bytesReceived, out SocketFlags receivedFlags, out SocketError errorCode) => - TryCompleteReceiveFrom(socket, new Span(buffer, offset, count), null, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode); - public static bool TryCompleteReceiveFrom(SafeCloseSocket socket, Span buffer, SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, out int bytesReceived, out SocketFlags receivedFlags, out SocketError errorCode) => TryCompleteReceiveFrom(socket, buffer, null, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode); @@ -651,18 +646,14 @@ namespace System.Net.Sockets } } - public static unsafe bool TryCompleteReceiveMessageFrom(SafeCloseSocket socket, byte[] buffer, IList> buffers, int offset, int count, SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, bool isIPv4, bool isIPv6, out int bytesReceived, out SocketFlags receivedFlags, out IPPacketInformation ipPacketInformation, out SocketError errorCode) + public static unsafe bool TryCompleteReceiveMessageFrom(SafeCloseSocket socket, Span buffer, IList> buffers, SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, bool isIPv4, bool isIPv6, out int bytesReceived, out SocketFlags receivedFlags, out IPPacketInformation ipPacketInformation, out SocketError errorCode) { - Debug.Assert( - (buffer == null) ^ (buffers == null), - "One and only one of buffer and buffers must be null"); - try { Interop.Error errno; - int received = buffer != null ? - ReceiveMessageFrom(socket, flags, buffer, offset, count, socketAddress, ref socketAddressLen, isIPv4, isIPv6, out receivedFlags, out ipPacketInformation, out errno) : + int received = buffers == null ? + ReceiveMessageFrom(socket, flags, buffer, socketAddress, ref socketAddressLen, isIPv4, isIPv6, out receivedFlags, out ipPacketInformation, out errno) : ReceiveMessageFrom(socket, flags, buffers, socketAddress, ref socketAddressLen, isIPv4, isIPv6, out receivedFlags, out ipPacketInformation, out errno); if (received != -1) @@ -694,7 +685,7 @@ namespace System.Net.Sockets } } - public static bool TryCompleteSendTo(SafeCloseSocket socket, byte[] buffer, ref int offset, ref int count, SocketFlags flags, byte[] socketAddress, int socketAddressLen, ref int bytesSent, out SocketError errorCode) + public static bool TryCompleteSendTo(SafeCloseSocket socket, Span buffer, ref int offset, ref int count, SocketFlags flags, byte[] socketAddress, int socketAddressLen, ref int bytesSent, out SocketError errorCode) { int bufferIndex = 0; return TryCompleteSendTo(socket, buffer, null, ref bufferIndex, ref offset, ref count, flags, socketAddress, socketAddressLen, ref bytesSent, out errorCode); @@ -825,6 +816,15 @@ namespace System.Net.Sockets return err == Interop.Error.SUCCESS ? SocketError.Success : GetSocketErrorForErrorCode(err); } + public static unsafe SocketError GetAtOutOfBandMark(SafeCloseSocket handle, out int atOutOfBandMark) + { + int value = 0; + Interop.Error err = Interop.Sys.GetAtOutOfBandMark(handle, &value); + atOutOfBandMark = value; + + return err == Interop.Error.SUCCESS ? SocketError.Success : GetSocketErrorForErrorCode(err); + } + public static unsafe SocketError GetPeerName(SafeCloseSocket handle, byte[] buffer, ref int nameLen) { Interop.Error err; @@ -838,12 +838,12 @@ namespace System.Net.Sockets return err == Interop.Error.SUCCESS ? SocketError.Success : GetSocketErrorForErrorCode(err); } - public static unsafe SocketError Bind(SafeCloseSocket handle, byte[] buffer, int nameLen) + public static unsafe SocketError Bind(SafeCloseSocket handle, ProtocolType socketProtocolType, byte[] buffer, int nameLen) { Interop.Error err; fixed (byte* rawBuffer = buffer) { - err = Interop.Sys.Bind(handle, rawBuffer, nameLen); + err = Interop.Sys.Bind(handle, socketProtocolType, rawBuffer, nameLen); } return err == Interop.Error.SUCCESS ? SocketError.Success : GetSocketErrorForErrorCode(err); @@ -977,12 +977,12 @@ namespace System.Net.Sockets { if (!handle.IsNonBlocking) { - return handle.AsyncContext.Receive(buffer, offset, count, ref socketFlags, handle.ReceiveTimeout, out bytesTransferred); + return handle.AsyncContext.Receive(new Memory(buffer, offset, count), ref socketFlags, handle.ReceiveTimeout, out bytesTransferred); } int socketAddressLen = 0; SocketError errorCode; - bool completed = TryCompleteReceiveFrom(handle, buffer, offset, count, socketFlags, null, ref socketAddressLen, out bytesTransferred, out socketFlags, out errorCode); + bool completed = TryCompleteReceiveFrom(handle, new Span(buffer, offset, count), socketFlags, null, ref socketAddressLen, out bytesTransferred, out socketFlags, out errorCode); return completed ? errorCode : SocketError.WouldBlock; } @@ -1010,11 +1010,11 @@ namespace System.Net.Sockets SocketError errorCode; if (!handle.IsNonBlocking) { - errorCode = handle.AsyncContext.ReceiveMessageFrom(buffer, null, offset, count, ref socketFlags, socketAddressBuffer, ref socketAddressLen, isIPv4, isIPv6, handle.ReceiveTimeout, out ipPacketInformation, out bytesTransferred); + errorCode = handle.AsyncContext.ReceiveMessageFrom(new Memory(buffer, offset, count), null, ref socketFlags, socketAddressBuffer, ref socketAddressLen, isIPv4, isIPv6, handle.ReceiveTimeout, out ipPacketInformation, out bytesTransferred); } else { - if (!TryCompleteReceiveMessageFrom(handle, buffer, null, offset, count, socketFlags, socketAddressBuffer, ref socketAddressLen, isIPv4, isIPv6, out bytesTransferred, out socketFlags, out ipPacketInformation, out errorCode)) + if (!TryCompleteReceiveMessageFrom(handle, new Span(buffer, offset, count), null, socketFlags, socketAddressBuffer, ref socketAddressLen, isIPv4, isIPv6, out bytesTransferred, out socketFlags, out ipPacketInformation, out errorCode)) { errorCode = SocketError.WouldBlock; } @@ -1029,17 +1029,54 @@ namespace System.Net.Sockets { if (!handle.IsNonBlocking) { - return handle.AsyncContext.ReceiveFrom(buffer, offset, count, ref socketFlags, socketAddress, ref socketAddressLen, handle.ReceiveTimeout, out bytesTransferred); + return handle.AsyncContext.ReceiveFrom(new Memory(buffer, offset, count), ref socketFlags, socketAddress, ref socketAddressLen, handle.ReceiveTimeout, out bytesTransferred); } SocketError errorCode; - bool completed = TryCompleteReceiveFrom(handle, buffer, offset, count, socketFlags, socketAddress, ref socketAddressLen, out bytesTransferred, out socketFlags, out errorCode); + bool completed = TryCompleteReceiveFrom(handle, new Span(buffer, offset, count), socketFlags, socketAddress, ref socketAddressLen, out bytesTransferred, out socketFlags, out errorCode); return completed ? errorCode : SocketError.WouldBlock; } public static SocketError WindowsIoctl(SafeCloseSocket handle, int ioControlCode, byte[] optionInValue, byte[] optionOutValue, out int optionLength) - { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_IOControl); + { + // Three codes are called out in the Winsock IOCTLs documentation as "The following Unix IOCTL codes (commands) are supported." They are + // also the three codes available for use with ioctlsocket on Windows. Developers should be discouraged from using Socket.IOControl in + // cross -platform applications, as it accepts Windows-specific values (the value of FIONREAD is different on different platforms), but + // we make a best-effort attempt to at least keep these codes behaving as on Windows. + const int FIONBIO = unchecked((int)IOControlCode.NonBlockingIO); + const int FIONREAD = (int)IOControlCode.DataToRead; + const int SIOCATMARK = (int)IOControlCode.OobDataRead; + + optionLength = 0; + switch (ioControlCode) + { + case FIONBIO: + // The Windows implementation explicitly throws this exception, so that all + // changes to blocking/non-blocking are done via Socket.Blocking. + throw new InvalidOperationException(SR.net_sockets_useblocking); + + case FIONREAD: + case SIOCATMARK: + if (optionOutValue == null || optionOutValue.Length < sizeof(int)) + { + return SocketError.Fault; + } + + int result; + SocketError error = ioControlCode == FIONREAD ? + GetAvailable(handle, out result) : + GetAtOutOfBandMark(handle, out result); + if (error == SocketError.Success) + { + optionLength = sizeof(int); + BitConverter.TryWriteBytes(optionOutValue, result); + } + return error; + + default: + // Every other control code is unknown to us for and is considered unsupported on Unix. + throw new PlatformNotSupportedException(SR.PlatformNotSupported_IOControl); + } } private static SocketError GetErrorAndTrackSetting(SafeCloseSocket handle, SocketOptionLevel optionLevel, SocketOptionName optionName, Interop.Error err) @@ -1639,7 +1676,7 @@ namespace System.Net.Sockets { int bytesReceived; SocketFlags receivedFlags; - SocketError socketError = handle.AsyncContext.ReceiveAsync(buffer, offset, count, socketFlags, out bytesReceived, out receivedFlags, asyncResult.CompletionCallback); + SocketError socketError = handle.AsyncContext.ReceiveAsync(new Memory(buffer, offset, count), socketFlags, out bytesReceived, out receivedFlags, asyncResult.CompletionCallback); if (socketError == SocketError.Success) { asyncResult.CompletionCallback(bytesReceived, null, 0, receivedFlags, SocketError.Success); @@ -1666,7 +1703,7 @@ namespace System.Net.Sockets int socketAddressSize = socketAddress.InternalSize; int bytesReceived; SocketFlags receivedFlags; - SocketError socketError = handle.AsyncContext.ReceiveFromAsync(buffer, offset, count, socketFlags, socketAddress.Buffer, ref socketAddressSize, out bytesReceived, out receivedFlags, asyncResult.CompletionCallback); + SocketError socketError = handle.AsyncContext.ReceiveFromAsync(new Memory(buffer, offset, count), socketFlags, socketAddress.Buffer, ref socketAddressSize, out bytesReceived, out receivedFlags, asyncResult.CompletionCallback); if (socketError == SocketError.Success) { asyncResult.CompletionCallback(bytesReceived, socketAddress.Buffer, socketAddressSize, receivedFlags, SocketError.Success); @@ -1685,7 +1722,7 @@ namespace System.Net.Sockets int bytesReceived; SocketFlags receivedFlags; IPPacketInformation ipPacketInformation; - SocketError socketError = handle.AsyncContext.ReceiveMessageFromAsync(buffer, null, offset, count, socketFlags, socketAddress.Buffer, ref socketAddressSize, isIPv4, isIPv6, out bytesReceived, out receivedFlags, out ipPacketInformation, asyncResult.CompletionCallback); + SocketError socketError = handle.AsyncContext.ReceiveMessageFromAsync(new Memory(buffer, offset, count), null, socketFlags, socketAddress.Buffer, ref socketAddressSize, isIPv4, isIPv6, out bytesReceived, out receivedFlags, out ipPacketInformation, asyncResult.CompletionCallback); if (socketError == SocketError.Success) { asyncResult.CompletionCallback(bytesReceived, socketAddress.Buffer, socketAddressSize, receivedFlags, ipPacketInformation, SocketError.Success); diff --git a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs index 6e8e34f4db..226fabd2d1 100644 --- a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs +++ b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs @@ -34,12 +34,7 @@ namespace System.Net.Sockets public static SocketError GetLastSocketError() { int win32Error = Marshal.GetLastWin32Error(); - - if (win32Error == 0) - { - NetEventSource.Fail(null, "GetLastWin32Error() returned zero."); - } - + Debug.Assert(win32Error != 0, "Expected non-0 error"); return (SocketError)win32Error; } @@ -91,7 +86,7 @@ namespace System.Net.Sockets return errorCode == SocketError.SocketError ? GetLastSocketError() : SocketError.Success; } - public static SocketError Bind(SafeCloseSocket handle, byte[] buffer, int nameLen) + public static SocketError Bind(SafeCloseSocket handle, ProtocolType socketProtocolType, byte[] buffer, int nameLen) { SocketError errorCode = Interop.Winsock.bind(handle, buffer, nameLen); return errorCode == SocketError.SocketError ? GetLastSocketError() : SocketError.Success; @@ -180,7 +175,7 @@ namespace System.Net.Sockets public static unsafe SocketError Send(SafeCloseSocket handle, ReadOnlySpan buffer, SocketFlags socketFlags, out int bytesTransferred) { int bytesSent; - fixed (byte* bufferPtr = &buffer.DangerousGetPinnableReference()) + fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) { bytesSent = Interop.Winsock.send(handle.DangerousGetHandle(), bufferPtr, buffer.Length, socketFlags); } @@ -300,7 +295,7 @@ namespace System.Net.Sockets public static unsafe SocketError Receive(SafeCloseSocket handle, Span buffer, SocketFlags socketFlags, out int bytesTransferred) { int bytesReceived; - fixed (byte* bufferPtr = &buffer.DangerousGetPinnableReference()) + fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) { bytesReceived = Interop.Winsock.recv(handle.DangerousGetHandle(), bufferPtr, buffer.Length, socketFlags); } diff --git a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/UdpReceiveResult.cs b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/UdpReceiveResult.cs index 14babeca6a..74e5d20155 100644 --- a/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/UdpReceiveResult.cs +++ b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/UdpReceiveResult.cs @@ -4,11 +4,19 @@ namespace System.Net.Sockets { - public struct UdpReceiveResult : IEquatable + /// + /// Presents UDP receive result information from a call to the method + /// + public struct UdpReceiveResult : IEquatable { private byte[] _buffer; private IPEndPoint _remoteEndPoint; + /// + /// Initializes a new instance of the class + /// + /// A buffer for data to receive in the UDP packet + /// The remote endpoint of the UDP packet public UdpReceiveResult(byte[] buffer, IPEndPoint remoteEndPoint) { if (buffer == null) @@ -25,6 +33,9 @@ namespace System.Net.Sockets _remoteEndPoint = remoteEndPoint; } + /// + /// Gets a buffer with the data received in the UDP packet + /// public byte[] Buffer { get @@ -33,6 +44,9 @@ namespace System.Net.Sockets } } + /// + /// Gets the remote endpoint from which the UDP packet was received + /// public IPEndPoint RemoteEndPoint { get @@ -41,11 +55,20 @@ namespace System.Net.Sockets } } + /// + /// Returns the hash code for this instance. + /// + /// The hash code public override int GetHashCode() { return (_buffer != null) ? (_buffer.GetHashCode() ^ _remoteEndPoint.GetHashCode()) : 0; } + /// + /// Returns a value that indicates whether this instance is equal to a specified object + /// + /// The object to compare with this instance + /// true if obj is an instance of and equals the value of the instance; otherwise, false public override bool Equals(object obj) { if (!(obj is UdpReceiveResult)) @@ -56,16 +79,33 @@ namespace System.Net.Sockets return Equals((UdpReceiveResult)obj); } + /// + /// Returns a value that indicates whether this instance is equal to a specified object + /// + /// The object to compare with this instance + /// true if other is an instance of and equals the value of the instance; otherwise, false public bool Equals(UdpReceiveResult other) { return object.Equals(_buffer, other._buffer) && object.Equals(_remoteEndPoint, other._remoteEndPoint); } + /// + /// Tests whether two specified instances are equivalent + /// + /// The instance that is to the left of the equality operator + /// The instance that is to the right of the equality operator + /// true if left and right are equal; otherwise, false public static bool operator ==(UdpReceiveResult left, UdpReceiveResult right) { return left.Equals(right); } + /// + /// Tests whether two specified instances are not equal + /// + /// The instance that is to the left of the not equal operator + /// The instance that is to the right of the not equal operator + /// true if left and right are unequal; otherwise, false public static bool operator !=(UdpReceiveResult left, UdpReceiveResult right) { return !left.Equals(right); diff --git a/external/corefx/src/System.IO.Pipes/src/System/Net/Sockets/UnixDomainSocketEndPoint.cs b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/UnixDomainSocketEndPoint.Unix.cs similarity index 65% rename from external/corefx/src/System.IO.Pipes/src/System/Net/Sockets/UnixDomainSocketEndPoint.cs rename to external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/UnixDomainSocketEndPoint.Unix.cs index 4107ff00e5..1dc982dce9 100644 --- a/external/corefx/src/System.IO.Pipes/src/System/Net/Sockets/UnixDomainSocketEndPoint.cs +++ b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/UnixDomainSocketEndPoint.Unix.cs @@ -8,7 +8,7 @@ using System.Text; namespace System.Net.Sockets { /// Represents a Unix Domain Socket endpoint as a path. - internal sealed class UnixDomainSocketEndPoint : EndPoint + public sealed class UnixDomainSocketEndPoint : EndPoint { private const AddressFamily EndPointAddressFamily = AddressFamily.Unix; @@ -27,8 +27,6 @@ namespace System.Net.Sockets Debug.Assert(s_nativePathOffset >= 0, "Expected path offset to be positive"); Debug.Assert(s_nativePathOffset + s_nativePathLength <= s_nativeAddressSize, "Expected address size to include all of the path length"); Debug.Assert(s_nativePathLength >= 92, "Expected max path length to be at least 92"); // per http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_un.h.html - - s_nativePathLength -= 1; // to account for null terminator within the allotted space } public UnixDomainSocketEndPoint(string path) @@ -38,15 +36,27 @@ namespace System.Net.Sockets throw new ArgumentNullException(nameof(path)); } - _path = path; - _encodedPath = s_pathEncoding.GetBytes(_path); + // Pathname socket addresses should be null-terminated. + // Linux abstract socket addresses start with a zero byte, they must not be null-terminated. + bool isAbstract = IsAbstract(path); + int bufferLength = s_pathEncoding.GetByteCount(path); + if (!isAbstract) + { + // for null terminator + bufferLength++; + } - if (path.Length == 0 || _encodedPath.Length > s_nativePathLength) + if (path.Length == 0 || bufferLength > s_nativePathLength) { throw new ArgumentOutOfRangeException( nameof(path), path, SR.Format(SR.ArgumentOutOfRange_PathLengthInvalid, path, s_nativePathLength)); } + + _path = path; + _encodedPath = new byte[bufferLength]; + int bytesEncoded = s_pathEncoding.GetBytes(path, 0, path.Length, _encodedPath, 0); + Debug.Assert(bufferLength - (isAbstract ? 0 : 1) == bytesEncoded); } internal UnixDomainSocketEndPoint(SocketAddress socketAddress) @@ -70,7 +80,17 @@ namespace System.Net.Sockets _encodedPath[i] = socketAddress[s_nativePathOffset + i]; } - _path = s_pathEncoding.GetString(_encodedPath, 0, _encodedPath.Length); + // Strip trailing null of pathname socket addresses. + int length = _encodedPath.Length; + if (!IsAbstract(_encodedPath)) + { + // Since this isn't an abstract path, we're sure our first byte isn't 0. + while (_encodedPath[length - 1] == 0) + { + length--; + } + } + _path = s_pathEncoding.GetString(_encodedPath, 0, length); } else { @@ -81,14 +101,12 @@ namespace System.Net.Sockets public override SocketAddress Serialize() { - var result = new SocketAddress(AddressFamily.Unix, s_nativeAddressSize); - Debug.Assert(_encodedPath.Length + s_nativePathOffset <= result.Size, "Expected path to fit in address"); + var result = new SocketAddress(AddressFamily.Unix, s_nativePathOffset + _encodedPath.Length); for (int index = 0; index < _encodedPath.Length; index++) { result[s_nativePathOffset + index] = _encodedPath[index]; } - result[s_nativePathOffset + _encodedPath.Length] = 0; // path must be null-terminated return result; } @@ -97,6 +115,21 @@ namespace System.Net.Sockets public override AddressFamily AddressFamily => EndPointAddressFamily; - public override string ToString() => _path; + public override string ToString() + { + bool isAbstract = IsAbstract(_path); + if (isAbstract) + { + return "@" + _path.Substring(1); + } + else + { + return _path; + } + } + + private static bool IsAbstract(string path) => path.Length > 0 && path[0] == '\0'; + + private static bool IsAbstract(byte[] encodedPath) => encodedPath.Length > 0 && encodedPath[0] == 0; } } diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Unix/PkcsPal.Unix.cs b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/UnixDomainSocketEndPoint.Windows.cs similarity index 57% rename from external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Unix/PkcsPal.Unix.cs rename to external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/UnixDomainSocketEndPoint.Windows.cs index d33b9a538c..6a6c9925d9 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Unix/PkcsPal.Unix.cs +++ b/external/corefx/src/System.Net.Sockets/src/System/Net/Sockets/UnixDomainSocketEndPoint.Windows.cs @@ -2,16 +2,12 @@ // 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.Diagnostics; - -namespace Internal.Cryptography +namespace System.Net.Sockets { - internal abstract partial class PkcsPal + /// Represents a Unix Domain Socket endpoint as a path. + public sealed class UnixDomainSocketEndPoint : EndPoint { - private static PkcsPal s_instance; - - static PkcsPal() + public UnixDomainSocketEndPoint(string path) { throw new PlatformNotSupportedException(); } diff --git a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/AgnosticListenerTest.cs b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/AgnosticListenerTest.cs index ee0461953e..88b74b9d06 100644 --- a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/AgnosticListenerTest.cs +++ b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/AgnosticListenerTest.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Net.Test.Common; - +using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; @@ -33,59 +33,64 @@ namespace System.Net.Sockets.Tests [OuterLoop] // TODO: Issue #11345 [Fact] - public void ConnectWithV4_Success() + public async Task ConnectWithV4_Success() { - int port; - TcpListener listener = SocketTestExtensions.CreateAndStartTcpListenerOnAnonymousPort(out port); - IAsyncResult asyncResult = listener.BeginAcceptTcpClient(null, null); + TcpListener listener = SocketTestExtensions.CreateAndStartTcpListenerOnAnonymousPort(out int port); + Task acceptTask = Task.Factory.FromAsync(listener.BeginAcceptTcpClient(null, null), listener.EndAcceptTcpClient); TcpClient client = new TcpClient(AddressFamily.InterNetwork); - client.ConnectAsync(IPAddress.Loopback, port).GetAwaiter().GetResult(); + Task connectTask = client.ConnectAsync(IPAddress.Loopback, port); - TcpClient acceptedClient = listener.EndAcceptTcpClient(asyncResult); + await (new Task[] { acceptTask, connectTask }).WhenAllOrAnyFailed(); + client.Dispose(); - acceptedClient.Dispose(); + acceptTask.Result.Dispose(); listener.Stop(); } [OuterLoop] // TODO: Issue #11345 [Fact] - public void ConnectWithV6_Success() + public async Task ConnectWithV6_Success() { - int port; - TcpListener listener = SocketTestExtensions.CreateAndStartTcpListenerOnAnonymousPort(out port); - IAsyncResult asyncResult = listener.BeginAcceptTcpClient(null, null); + TcpListener listener = SocketTestExtensions.CreateAndStartTcpListenerOnAnonymousPort(out int port); + Task acceptTask = Task.Factory.FromAsync(listener.BeginAcceptTcpClient(null, null), listener.EndAcceptTcpClient); TcpClient client = new TcpClient(AddressFamily.InterNetworkV6); - client.ConnectAsync(IPAddress.IPv6Loopback, port).GetAwaiter().GetResult(); + Task connectTask = client.ConnectAsync(IPAddress.IPv6Loopback, port); + + await (new Task[] { acceptTask, connectTask }).WhenAllOrAnyFailed(); - TcpClient acceptedClient = listener.EndAcceptTcpClient(asyncResult); client.Dispose(); - acceptedClient.Dispose(); + acceptTask.Result.Dispose(); listener.Stop(); } [OuterLoop] // TODO: Issue #11345 [Fact] - public void ConnectWithV4AndV6_Success() + public async Task ConnectWithV4AndV6_Success() { - int port; - TcpListener listener = SocketTestExtensions.CreateAndStartTcpListenerOnAnonymousPort(out port); - IAsyncResult asyncResult = listener.BeginAcceptTcpClient(null, null); + TcpListener listener = SocketTestExtensions.CreateAndStartTcpListenerOnAnonymousPort(out int port); + Task acceptTask = Task.Factory.FromAsync(listener.BeginAcceptTcpClient(null, null), listener.EndAcceptTcpClient); TcpClient v6Client = new TcpClient(AddressFamily.InterNetworkV6); - v6Client.ConnectAsync(IPAddress.IPv6Loopback, port).GetAwaiter().GetResult(); + Task connectTask = v6Client.ConnectAsync(IPAddress.IPv6Loopback, port); - TcpClient acceptedV6Client = listener.EndAcceptTcpClient(asyncResult); + Task[] tasks = new Task[] { acceptTask, connectTask }; + await tasks.WhenAllOrAnyFailed(); + + TcpClient acceptedV6Client = acceptTask.Result; Assert.Equal(AddressFamily.InterNetworkV6, acceptedV6Client.Client.RemoteEndPoint.AddressFamily); Assert.Equal(AddressFamily.InterNetworkV6, v6Client.Client.RemoteEndPoint.AddressFamily); - asyncResult = listener.BeginAcceptTcpClient(null, null); + acceptTask = Task.Factory.FromAsync(listener.BeginAcceptTcpClient(null, null), listener.EndAcceptTcpClient); TcpClient v4Client = new TcpClient(AddressFamily.InterNetwork); - v4Client.ConnectAsync(IPAddress.Loopback, port).GetAwaiter().GetResult(); + connectTask = v4Client.ConnectAsync(IPAddress.Loopback, port); + tasks[0] = acceptTask; + tasks[1] = connectTask; + await tasks.WhenAllOrAnyFailed(); - TcpClient acceptedV4Client = listener.EndAcceptTcpClient(asyncResult); + TcpClient acceptedV4Client = acceptTask.Result; Assert.Equal(AddressFamily.InterNetworkV6, acceptedV4Client.Client.RemoteEndPoint.AddressFamily); Assert.Equal(AddressFamily.InterNetwork, v4Client.Client.RemoteEndPoint.AddressFamily); diff --git a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/Connect.cs b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/Connect.cs index 4b1c372ecc..5fe40c29b3 100644 --- a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/Connect.cs +++ b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/Connect.cs @@ -12,7 +12,7 @@ namespace System.Net.Sockets.Tests [OuterLoop] // TODO: Issue #11345 [Theory] [MemberData(nameof(Loopbacks))] - public void Connect_Success(IPAddress listenAt) + public async Task Connect_Success(IPAddress listenAt) { int port; using (SocketTestServer.SocketTestServerFactory(SocketImplementationType.Async, listenAt, out port)) @@ -20,7 +20,7 @@ namespace System.Net.Sockets.Tests using (Socket client = new Socket(listenAt.AddressFamily, SocketType.Stream, ProtocolType.Tcp)) { Task connectTask = ConnectAsync(client, new IPEndPoint(listenAt, port)); - Assert.True(connectTask.Wait(TestSettings.PassingTestTimeout), "IPv4: Timed out while waiting for connection"); + await connectTask; Assert.True(client.Connected); } } @@ -29,7 +29,7 @@ namespace System.Net.Sockets.Tests [OuterLoop] // TODO: Issue #11345 [Theory] [MemberData(nameof(Loopbacks))] - public void Connect_MultipleIPAddresses_Success(IPAddress listenAt) + public async Task Connect_MultipleIPAddresses_Success(IPAddress listenAt) { if (!SupportsMultiConnect) return; @@ -39,7 +39,7 @@ namespace System.Net.Sockets.Tests using (Socket client = new Socket(listenAt.AddressFamily, SocketType.Stream, ProtocolType.Tcp)) { Task connectTask = MultiConnectAsync(client, new IPAddress[] { IPAddress.Loopback, IPAddress.IPv6Loopback }, port); - Assert.True(connectTask.Wait(TestSettings.PassingTestTimeout), "Timed out while waiting for connection"); + await connectTask; Assert.True(client.Connected); } } diff --git a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/DisconnectTest.cs b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/DisconnectTest.cs index f886b66f01..a22d8b8051 100644 --- a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/DisconnectTest.cs +++ b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/DisconnectTest.cs @@ -69,6 +69,8 @@ namespace System.Net.Sockets.Tests client.Disconnect(reuseSocket); + Assert.False(client.Connected); + args.RemoteEndPoint = server2.EndPoint; if (client.ConnectAsync(args)) @@ -114,6 +116,7 @@ namespace System.Net.Sockets.Tests } Assert.Equal(SocketError.Success, args.SocketError); + Assert.False(client.Connected); args.RemoteEndPoint = server2.EndPoint; @@ -155,6 +158,9 @@ namespace System.Net.Sockets.Tests IAsyncResult ar = client.BeginDisconnect(reuseSocket, null, null); client.EndDisconnect(ar); + + Assert.False(client.Connected); + Assert.Throws(() => client.EndDisconnect(ar)); args.RemoteEndPoint = server2.EndPoint; diff --git a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/DualModeSocketTest.cs.REMOVED.git-id b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/DualModeSocketTest.cs.REMOVED.git-id index 4fa3b94bc0..f29b020a79 100644 --- a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/DualModeSocketTest.cs.REMOVED.git-id +++ b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/DualModeSocketTest.cs.REMOVED.git-id @@ -1 +1 @@ -fba1acd2981dbca4cd81d47d3c20272000a172bb \ No newline at end of file +7d137cf0130b6828c9a7055b901946bea8df703a \ No newline at end of file diff --git a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/OSSupport.cs b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/OSSupport.cs index 0adb14abf7..3e1614a737 100644 --- a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/OSSupport.cs +++ b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/OSSupport.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. +using System.Threading; using Xunit; namespace System.Net.Sockets.Tests @@ -42,20 +43,121 @@ namespace System.Net.Sockets.Tests } } - [PlatformSpecific(TestPlatforms.Windows)] // Windows IOCTL [Fact] public void IOControl_FIONREAD_Success() { using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { - byte[] outValue = new byte[sizeof(int)]; + Assert.Throws(() => client.IOControl(IOControlCode.DataToRead, null, null)); + Assert.Throws(() => client.IOControl(IOControlCode.DataToRead, null, new byte[0])); + Assert.Throws(() => client.IOControl(IOControlCode.DataToRead, null, new byte[sizeof(int) - 1])); - const int FIONREAD = 0x4004667F; - Assert.Equal(4, client.IOControl(FIONREAD, null, outValue)); - Assert.Equal(client.Available, BitConverter.ToInt32(outValue, 0)); + byte[] fionreadResult = new byte[sizeof(int)]; - Assert.Equal(4, client.IOControl(IOControlCode.DataToRead, null, outValue)); - Assert.Equal(client.Available, BitConverter.ToInt32(outValue, 0)); + Assert.Equal(4, client.IOControl(IOControlCode.DataToRead, null, fionreadResult)); + Assert.Equal(client.Available, BitConverter.ToInt32(fionreadResult, 0)); + Assert.Equal(0, BitConverter.ToInt32(fionreadResult, 0)); + + Assert.Equal(4, client.IOControl((int)IOControlCode.DataToRead, null, fionreadResult)); + Assert.Equal(client.Available, BitConverter.ToInt32(fionreadResult, 0)); + Assert.Equal(0, BitConverter.ToInt32(fionreadResult, 0)); + + using (var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + listener.Bind(new IPEndPoint(IPAddress.Loopback, 0)); + listener.Listen(1); + + client.Connect(listener.LocalEndPoint); + using (Socket server = listener.Accept()) + { + server.Send(new byte[] { 42 }); + Assert.True(SpinWait.SpinUntil(() => client.Available != 0, 10_000)); + + Assert.Equal(4, client.IOControl(IOControlCode.DataToRead, null, fionreadResult)); + Assert.Equal(client.Available, BitConverter.ToInt32(fionreadResult, 0)); + Assert.Equal(1, BitConverter.ToInt32(fionreadResult, 0)); + } + } + } + } + + [ActiveIssue(25639)] + [Fact] + public void IOControl_SIOCATMARK_Success() + { + using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + Assert.Throws(() => client.IOControl(IOControlCode.OobDataRead, null, null)); + Assert.Throws(() => client.IOControl(IOControlCode.OobDataRead, null, new byte[0])); + Assert.Throws(() => client.IOControl(IOControlCode.OobDataRead, null, new byte[sizeof(int) - 1])); + + using (var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + listener.Bind(new IPEndPoint(IPAddress.Loopback, 0)); + listener.Listen(1); + + client.Connect(listener.LocalEndPoint); + using (Socket server = listener.Accept()) + { + byte[] siocatmarkResult = new byte[sizeof(int)]; + + server.Send(new byte[] { 42 }, SocketFlags.None); + server.Send(new byte[] { 43 }, SocketFlags.OutOfBand); + + Assert.Equal(4, client.IOControl(IOControlCode.OobDataRead, null, siocatmarkResult)); + Assert.Equal(0, BitConverter.ToInt32(siocatmarkResult, 0)); + + var received = new byte[1]; + + Assert.Equal(1, client.Receive(received)); + Assert.Equal(42, received[0]); + + Assert.Equal(1, client.Receive(received, SocketFlags.OutOfBand)); + Assert.Equal(43, received[0]); + + Assert.True(SpinWait.SpinUntil(() => + { + Assert.Equal(4, client.IOControl(IOControlCode.OobDataRead, null, siocatmarkResult)); + return BitConverter.ToInt32(siocatmarkResult, 0) == 1; + }, 10_000)); + } + } + } + } + + [Fact] + public void IOControl_FIONBIO_Throws() + { + using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + Assert.Throws(() => client.IOControl(unchecked((int)IOControlCode.NonBlockingIO), null, null)); + Assert.Throws(() => client.IOControl(IOControlCode.NonBlockingIO, null, null)); + } + } + + [PlatformSpecific(TestPlatforms.AnyUnix)] + [Fact] + public void IOControl_UnknownValues_Unix_Throws() + { + using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + foreach (IOControlCode code in Enum.GetValues(typeof(IOControlCode))) + { + switch (code) + { + case IOControlCode.NonBlockingIO: + case IOControlCode.DataToRead: + case IOControlCode.OobDataRead: + // These three codes are currently enabled on Unix. + break; + + default: + // The rest should throw PNSE. + Assert.Throws(() => client.IOControl((int)code, null, null)); + Assert.Throws(() => client.IOControl(code, null, null)); + break; + } + } } } } diff --git a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SelectTest.cs b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SelectTest.cs index 720eca5913..8a4a601c9b 100644 --- a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SelectTest.cs +++ b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SelectTest.cs @@ -146,7 +146,7 @@ namespace System.Net.Sockets.Tests } [PlatformSpecific(~TestPlatforms.OSX)] // typical OSX install has very low max open file descriptors value - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/Microsoft/BashOnWindows/issues/308 + [Fact] public void Select_Error_OneReadyAtATime() { const int Errors = 90; // value larger than the internal value in SocketPal.Unix that swaps between stack and heap allocation @@ -241,7 +241,7 @@ namespace System.Net.Sockets.Tests [OuterLoop] [Fact] - public static void Select_AcceptNonBlocking_Success() + public static async Task Select_AcceptNonBlocking_Success() { using (Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { @@ -264,8 +264,7 @@ namespace System.Net.Sockets.Tests } // Give the task 5 seconds to complete; if not, assume it's hung. - bool completed = t.Wait(5000); - Assert.True(completed); + await t.TimeoutAfter(5000); } } diff --git a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SendFile.cs b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SendFile.cs index dd6c0e1241..4262009c11 100644 --- a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SendFile.cs +++ b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SendFile.cs @@ -141,7 +141,7 @@ namespace System.Net.Sockets.Tests using (remote) { var recvBuffer = new byte[256]; - for (;;) + for (; ; ) { int received = remote.Receive(recvBuffer, 0, recvBuffer.Length, SocketFlags.None); if (received == 0) @@ -183,7 +183,7 @@ namespace System.Net.Sockets.Tests [OuterLoop] // TODO: Issue #11345 [Theory] [MemberData(nameof(SendFile_MemberData))] - public void SendFile_APM(IPAddress listenAt, bool sendPreAndPostBuffers, int bytesToSend) + public async Task SendFile_APM(IPAddress listenAt, bool sendPreAndPostBuffers, int bytesToSend) { const int ListenBacklog = 1, TestTimeout = 30000; @@ -228,10 +228,7 @@ namespace System.Net.Sockets.Tests }); // Wait for the tasks to complete - Task firstCompleted = Task.WhenAny(serverTask, clientTask); - Assert.True(firstCompleted.Wait(TestTimeout), "Neither client nor server task completed within allowed time"); - firstCompleted.Result.GetAwaiter().GetResult(); - Assert.True(Task.WaitAll(new[] { serverTask, clientTask }, TestTimeout), $"Tasks didn't complete within allowed time. Server:{serverTask.Status} Client:{clientTask.Status}"); + await (new[] { serverTask, clientTask }).WhenAllOrAnyFailed(TestTimeout); // Validate the results Assert.Equal(bytesToSend, bytesReceived); diff --git a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SendReceive.cs b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SendReceive.cs index fa4274f14d..f08b4cfb9a 100644 --- a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SendReceive.cs +++ b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SendReceive.cs @@ -19,7 +19,7 @@ namespace System.Net.Sockets.Tests [InlineData(1, 1, 2)] // count high public async Task InvalidArguments_Throws(int? length, int offset, int count) { - if (length == null && !ValidatesArrayArguments) return; + if (!ValidatesArrayArguments) return; using (Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { @@ -137,7 +137,7 @@ namespace System.Net.Sockets.Tests if (!useMultipleBuffers) { var recvBuffer = new byte[256]; - for (;;) + for (; ; ) { int received = await ReceiveAsync(remote, new ArraySegment(recvBuffer)); if (received == 0) @@ -156,7 +156,7 @@ namespace System.Net.Sockets.Tests new ArraySegment(new byte[256], 2, 100), new ArraySegment(new byte[1], 0, 0), new ArraySegment(new byte[64], 9, 33)}; - for (;;) + for (; ; ) { int received = await ReceiveAsync(remote, recvBuffers); if (received == 0) @@ -553,7 +553,7 @@ namespace System.Net.Sockets.Tests [OuterLoop] // TODO: Issue #11345 [Theory] [MemberData(nameof(LoopbacksAndBuffers))] - public void SendRecvPollSync_TcpListener_Socket(IPAddress listenAt, bool pollBeforeOperation) + public async Task SendRecvPollSync_TcpListener_Socket(IPAddress listenAt, bool pollBeforeOperation) { const int BytesToSend = 123456; const int ListenBacklog = 1; @@ -619,10 +619,12 @@ namespace System.Net.Sockets.Tests bytesSent += sent; sentChecksum.Add(sendBuffer, 0, sent); } + + client.Shutdown(SocketShutdown.Send); } }); - Assert.True(Task.WaitAll(new[] { serverTask, clientTask }, TestTimeout), "Wait timed out"); + await (new[] { serverTask, clientTask }).WhenAllOrAnyFailed(TestTimeout); Assert.Equal(bytesSent, bytesReceived); Assert.Equal(sentChecksum.Sum, receivedChecksum.Sum); @@ -697,6 +699,7 @@ namespace System.Net.Sockets.Tests Assert.False(receive.IsCompleted, $"Task should not have been completed, was {receive.Status}"); // Disconnect the client + server.Shutdown(SocketShutdown.Both); server.Close(); // The client should now wake up @@ -868,6 +871,8 @@ namespace System.Net.Sockets.Tests Assert.Equal(SegmentCount, bytesSent); Assert.Equal(SocketError.Success, error); + + acceptSocket.Shutdown(SocketShutdown.Send); } }); @@ -1041,7 +1046,7 @@ namespace System.Net.Sockets.Tests [OuterLoop] // TODO: Issue #11345 [Theory] [MemberData(nameof(Loopbacks))] - public void SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress loopbackAddress) + public async Task SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress loopbackAddress) { IPAddress leftAddress = loopbackAddress, rightAddress = loopbackAddress; @@ -1107,7 +1112,7 @@ namespace System.Net.Sockets.Tests } }); - Assert.True(Task.WaitAll(new[] { receiverTask, senderTask }, TestTimeout)); + await (new[] { receiverTask, senderTask }).WhenAllOrAnyFailed(TestTimeout); for (int i = 0; i < DatagramsToSend; i++) { Assert.NotNull(receivedChecksums[i]); @@ -1122,7 +1127,7 @@ namespace System.Net.Sockets.Tests [OuterLoop] // TODO: Issue #11345 [Theory] [MemberData(nameof(Loopbacks))] - public void SendRecvAsync_TcpListener_TcpClient(IPAddress listenAt) + public async Task SendRecvAsync_TcpListener_TcpClient(IPAddress listenAt) { const int BytesToSend = 123456; const int ListenBacklog = 1; @@ -1140,7 +1145,7 @@ namespace System.Net.Sockets.Tests using (NetworkStream stream = remote.GetStream()) { var recvBuffer = new byte[256]; - for (;;) + for (; ; ) { int received = await stream.ReadAsync(recvBuffer, 0, recvBuffer.Length); if (received == 0) @@ -1184,8 +1189,7 @@ namespace System.Net.Sockets.Tests } }); - Assert.True(Task.WaitAll(new[] { serverTask, clientTask }, TestTimeout), - $"Time out waiting for serverTask ({serverTask.Status}) and clientTask ({clientTask.Status})"); + await (new[] { serverTask, clientTask }).WhenAllOrAnyFailed(TestTimeout); Assert.Equal(bytesSent, bytesReceived); Assert.Equal(sentChecksum.Sum, receivedChecksum.Sum); diff --git a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SendReceive.netcoreapp.cs b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SendReceive.netcoreapp.cs index 3fb00890bd..9b511de045 100644 --- a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SendReceive.netcoreapp.cs +++ b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SendReceive.netcoreapp.cs @@ -7,4 +7,5 @@ namespace System.Net.Sockets.Tests public sealed class SendReceiveSpanSync : SendReceive { } public sealed class SendReceiveSpanSyncForceNonBlocking : SendReceive { } public sealed class SendReceiveMemoryArrayTask : SendReceive { } + public sealed class SendReceiveMemoryNativeTask : SendReceive { } } diff --git a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.cs b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.cs index 0223850cf7..fe1d3b457e 100644 --- a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.cs +++ b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.cs @@ -10,7 +10,7 @@ using Xunit; namespace System.Net.Sockets.Tests { - public class SocketAsyncEventArgsTest + public partial class SocketAsyncEventArgsTest { [Fact] public void Usertoken_Roundtrips() @@ -154,6 +154,12 @@ namespace System.Net.Sockets.Tests AssertExtensions.Throws("count", () => saea.SetBuffer(new byte[1], 0, -1)); AssertExtensions.Throws("count", () => saea.SetBuffer(new byte[1], 0, 2)); AssertExtensions.Throws("count", () => saea.SetBuffer(new byte[1], 1, 2)); + + saea.SetBuffer(new byte[2], 0, 2); + AssertExtensions.Throws("offset", () => saea.SetBuffer(-1, 2)); + AssertExtensions.Throws("offset", () => saea.SetBuffer(3, 2)); + AssertExtensions.Throws("count", () => saea.SetBuffer(0, -1)); + AssertExtensions.Throws("count", () => saea.SetBuffer(0, 3)); } } @@ -327,7 +333,8 @@ namespace System.Net.Sockets.Tests connectSaea.Completed += (s, e) => tcs.SetResult(e.SocketError); connectSaea.RemoteEndPoint = new IPEndPoint(IPAddress.Loopback, ((IPEndPoint)listen.LocalEndPoint).Port); - Assert.True(client.ConnectAsync(connectSaea), $"ConnectAsync completed synchronously with SocketError == {connectSaea.SocketError}"); + bool pending = client.ConnectAsync(connectSaea); + if (!pending) tcs.SetResult(connectSaea.SocketError); if (tcs.Task.IsCompleted) { Assert.NotEqual(SocketError.Success, tcs.Task.Result); diff --git a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.netcoreapp.cs b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.netcoreapp.cs new file mode 100644 index 0000000000..90edf5b175 --- /dev/null +++ b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.netcoreapp.cs @@ -0,0 +1,195 @@ +// 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.Buffers; +using System.Collections.Generic; +using Xunit; + +namespace System.Net.Sockets.Tests +{ + public partial class SocketAsyncEventArgsTest + { + [Fact] + public void SetBuffer_MemoryBuffer_Roundtrips() + { + using (var saea = new SocketAsyncEventArgs()) + { + Memory memory = new byte[42]; + saea.SetBuffer(memory); + Assert.True(memory.Equals(saea.MemoryBuffer)); + Assert.Equal(0, saea.Offset); + Assert.Equal(memory.Length, saea.Count); + Assert.Null(saea.Buffer); + } + } + + [Fact] + public void SetBufferMemory_ThenSetBufferIntInt_Throws() + { + using (var saea = new SocketAsyncEventArgs()) + { + Memory memory = new byte[42]; + saea.SetBuffer(memory); + Assert.Throws(() => saea.SetBuffer(0, 42)); + Assert.Throws(() => saea.SetBuffer(0, 0)); + Assert.Throws(() => saea.SetBuffer(1, 2)); + Assert.True(memory.Equals(saea.MemoryBuffer)); + Assert.Equal(0, saea.Offset); + Assert.Equal(memory.Length, saea.Count); + } + } + + [Fact] + public void SetBufferArrayIntInt_AvailableFromMemoryBuffer() + { + using (var saea = new SocketAsyncEventArgs()) + { + byte[] array = new byte[42]; + + saea.SetBuffer(array, 0, array.Length); + Assert.True(saea.MemoryBuffer.TryGetArray(out ArraySegment result)); + Assert.Same(array, result.Array); + Assert.Same(saea.Buffer, array); + Assert.Equal(0, result.Offset); + Assert.Equal(array.Length, result.Count); + + saea.SetBuffer(1, 2); + Assert.Same(saea.Buffer, array); + Assert.Equal(1, saea.Offset); + Assert.Equal(2, saea.Count); + + Assert.True(saea.MemoryBuffer.TryGetArray(out result)); + Assert.Same(array, result.Array); + Assert.Equal(0, result.Offset); + Assert.Equal(array.Length, result.Count); + } + } + + [Fact] + public void SetBufferMemory_Default_ResetsCountOffset() + { + using (var saea = new SocketAsyncEventArgs()) + { + saea.SetBuffer(42, 84); + Assert.Equal(0, saea.Offset); + Assert.Equal(0, saea.Count); + + saea.SetBuffer(new byte[3], 1, 2); + Assert.Equal(1, saea.Offset); + Assert.Equal(2, saea.Count); + + saea.SetBuffer(Memory.Empty); + Assert.Null(saea.Buffer); + Assert.Equal(0, saea.Offset); + Assert.Equal(0, saea.Count); + } + } + + [Fact] + public void SetBufferListWhenMemoryBufferSet_Throws() + { + using (var saea = new SocketAsyncEventArgs()) + { + var bufferList = new List> { new ArraySegment(new byte[1]) }; + Memory buffer = new byte[1]; + + saea.SetBuffer(buffer); + AssertExtensions.Throws(null, () => saea.BufferList = bufferList); + Assert.True(buffer.Equals(saea.MemoryBuffer)); + Assert.Equal(0, saea.Offset); + Assert.Equal(buffer.Length, saea.Count); + Assert.Null(saea.BufferList); + + saea.SetBuffer(Memory.Empty); + saea.BufferList = bufferList; // works fine when Buffer has been set back to null + } + } + + [Fact] + public void SetBufferMemoryWhenBufferListSet_Throws() + { + using (var saea = new SocketAsyncEventArgs()) + { + var bufferList = new List> { new ArraySegment(new byte[1]) }; + saea.BufferList = bufferList; + + saea.SetBuffer(Memory.Empty); // nop + + Memory buffer = new byte[2]; + AssertExtensions.Throws(null, () => saea.SetBuffer(buffer)); + Assert.Same(bufferList, saea.BufferList); + Assert.Null(saea.Buffer); + Assert.True(saea.MemoryBuffer.Equals(default)); + + saea.BufferList = null; + saea.SetBuffer(buffer); // works fine when BufferList has been set back to null + } + } + + [Fact] + public void SetBufferMemoryWhenBufferMemorySet_Succeeds() + { + using (var saea = new SocketAsyncEventArgs()) + { + Memory buffer1 = new byte[1]; + Memory buffer2 = new byte[2]; + + for (int i = 0; i < 2; i++) + { + saea.SetBuffer(buffer1); + Assert.Null(saea.Buffer); + Assert.True(saea.MemoryBuffer.Equals(buffer1)); + Assert.Equal(0, saea.Offset); + Assert.Equal(buffer1.Length, saea.Count); + } + + saea.SetBuffer(buffer2); + Assert.Null(saea.Buffer); + Assert.True(saea.MemoryBuffer.Equals(buffer2)); + Assert.Equal(0, saea.Offset); + Assert.Equal(buffer2.Length, saea.Count); + } + } + + [Fact] + public void SetBufferMemoryWhenBufferSet_Succeeds() + { + using (var saea = new SocketAsyncEventArgs()) + { + byte[] buffer1 = new byte[3]; + Memory buffer2 = new byte[4]; + + saea.SetBuffer(buffer1, 0, buffer1.Length); + Assert.Same(buffer1, saea.Buffer); + Assert.Equal(0, saea.Offset); + Assert.Equal(buffer1.Length, saea.Count); + + saea.SetBuffer(1, 2); + Assert.Same(buffer1, saea.Buffer); + Assert.Equal(1, saea.Offset); + Assert.Equal(2, saea.Count); + + saea.SetBuffer(buffer2); + Assert.Null(saea.Buffer); + Assert.True(saea.MemoryBuffer.Equals(buffer2)); + Assert.Equal(0, saea.Offset); + Assert.Equal(buffer2.Length, saea.Count); + } + } + + [Fact] + public void SetBufferMemory_NonArray_BufferReturnsNull() + { + using (var m = new NativeOwnedMemory(42)) + using (var saea = new SocketAsyncEventArgs()) + { + saea.SetBuffer(m.Memory); + Assert.True(saea.MemoryBuffer.Equals(m.Memory)); + Assert.Equal(0, saea.Offset); + Assert.Equal(m.Length, saea.Count); + Assert.Null(saea.Buffer); + } + } + } +} diff --git a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs index 0baa055ae3..d81f9adf92 100644 --- a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs +++ b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs @@ -87,11 +87,6 @@ namespace System.Net.Sockets.Tests [Fact] public async Task MulticastInterface_Set_AnyInterface_Succeeds() { - if (PlatformDetection.IsFedora) - { - return; // [ActiveIssue(24008)] - } - // On all platforms, index 0 means "any interface" await MulticastInterface_Set_Helper(0); } @@ -122,11 +117,7 @@ namespace System.Net.Sockets.Tests receiveSocket.ReceiveTimeout = 1000; receiveSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(multicastAddress, interfaceIndex)); - // https://github.com/Microsoft/BashOnWindows/issues/990 - if (!PlatformDetection.IsWindowsSubsystemForLinux) - { - sendSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastInterface, IPAddress.HostToNetworkOrder(interfaceIndex)); - } + sendSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastInterface, IPAddress.HostToNetworkOrder(interfaceIndex)); var receiveBuffer = new byte[1024]; var receiveTask = receiveSocket.ReceiveAsync(new ArraySegment(receiveBuffer), SocketFlags.None); @@ -159,6 +150,81 @@ namespace System.Net.Sockets.Tests } } + [OuterLoop] // TODO: Issue #11345 + [Fact] + public async Task MulticastInterface_Set_IPv6_AnyInterface_Succeeds() + { + if (PlatformDetection.IsFedora || PlatformDetection.IsRedHatFamily7 || PlatformDetection.IsOSX) + { + return; // [ActiveIssue(24008)] + } + + // On all platforms, index 0 means "any interface" + await MulticastInterface_Set_IPv6_Helper(0); + } + + [OuterLoop] // TODO: Issue #11345 + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + [ActiveIssue(21327, TargetFrameworkMonikers.Uap)] // UWP Apps are forbidden to send network traffic to the local Computer. + public async void MulticastInterface_Set_IPv6_Loopback_Succeeds() + { + // On Windows, we can apparently assume interface 1 is "loopback." On other platforms, this is not a + // valid assumption. We could maybe use NetworkInterface.LoopbackInterfaceIndex to get the index, but + // this would introduce a dependency on System.Net.NetworkInformation, which depends on System.Net.Sockets, + // which is what we're testing here.... So for now, we'll just assume "loopback == 1" and run this on + // Windows only. + await MulticastInterface_Set_IPv6_Helper(1); + } + + private async Task MulticastInterface_Set_IPv6_Helper(int interfaceIndex) + { + IPAddress multicastAddress = IPAddress.Parse("ff11::1:1"); + string message = "hello"; + int port; + + using (Socket receiveSocket = CreateBoundUdpIPv6Socket(out port), + sendSocket = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp)) + { + receiveSocket.ReceiveTimeout = 1000; + receiveSocket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.AddMembership, new IPv6MulticastOption(multicastAddress, interfaceIndex)); + + // https://github.com/Microsoft/BashOnWindows/issues/990 + if (!PlatformDetection.IsWindowsSubsystemForLinux) + { + sendSocket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.MulticastInterface, interfaceIndex); + } + + var receiveBuffer = new byte[1024]; + var receiveTask = receiveSocket.ReceiveAsync(new ArraySegment(receiveBuffer), SocketFlags.None); + + for (int i = 0; i < TestSettings.UDPRedundancy; i++) + { + sendSocket.SendTo(Encoding.UTF8.GetBytes(message), new IPEndPoint(multicastAddress, port)); + } + + var cts = new CancellationTokenSource(); + Assert.True(await Task.WhenAny(receiveTask, Task.Delay(20_000, cts.Token)) == receiveTask, "Waiting for received data timed out"); + cts.Cancel(); + + int bytesReceived = await receiveTask; + string receivedMessage = Encoding.UTF8.GetString(receiveBuffer, 0, bytesReceived); + + Assert.Equal(receivedMessage, message); + } + } + + [Fact] + public void MulticastInterface_Set_IPv6_InvalidIndex_Throws() + { + int interfaceIndex = 31415; + using (Socket s = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp)) + { + Assert.Throws(() => + s.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.MulticastInterface, interfaceIndex)); + } + } + [OuterLoop] // TODO: Issue #11345 [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // In WSL, the connect() call fails immediately. [InlineData(false)] @@ -224,6 +290,21 @@ namespace System.Net.Sockets.Tests return receiveSocket; } + // Create an Udp Socket and binds it to an available port + private static Socket CreateBoundUdpIPv6Socket(out int localPort) + { + Socket receiveSocket = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); + + // sending a message will bind the socket to an available port + string sendMessage = "dummy message"; + int port = 54320; + IPAddress multicastAddress = IPAddress.Parse("ff11::1:1"); + receiveSocket.SendTo(Encoding.UTF8.GetBytes(sendMessage), new IPEndPoint(multicastAddress, port)); + + localPort = (receiveSocket.LocalEndPoint as IPEndPoint).Port; + return receiveSocket; + } + [Theory] [InlineData(null, null, null, true)] [InlineData(null, null, false, true)] @@ -284,6 +365,88 @@ namespace System.Net.Sockets.Tests ReuseAddress(exclusiveAddressUse, firstSocketReuseAddress, secondSocketReuseAddress, expectFailure); } + [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix)] // Windows defaults are different + public void ExclusiveAddress_Default_Unix() + { + using (Socket a = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) + { + Assert.Equal(1, (int)a.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse)); + Assert.Equal(true, a.ExclusiveAddressUse); + Assert.Equal(0, (int)a.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress)); + } + } + + [Theory] + [InlineData(1)] + [InlineData(0)] + [PlatformSpecific(TestPlatforms.AnyUnix)] // Unix does not have separate options for ExclusiveAddressUse and ReuseAddress. + public void SettingExclusiveAddress_SetsReuseAddress(int value) + { + using (Socket a = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) + { + a.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse, value); + + Assert.Equal(value, (int)a.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse)); + Assert.Equal(value == 1 ? 0 : 1, (int)a.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress)); + } + + // SettingReuseAddress_SetsExclusiveAddress + using (Socket a = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) + { + a.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, value); + + Assert.Equal(value, (int)a.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress)); + Assert.Equal(value == 1 ? 0 : 1, (int)a.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse)); + } + } + + [Fact] + public void BindDuringTcpWait_Succeeds() + { + int port = 0; + using (Socket a = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + a.Bind(new IPEndPoint(IPAddress.Loopback, 0)); + port = (a.LocalEndPoint as IPEndPoint).Port; + a.Listen(10); + + // Connect a client + using (Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + client.Connect(new IPEndPoint(IPAddress.Loopback, port)); + // accept socket and close it with zero linger time. + a.Accept().Close(0); + } + } + + // Bind a socket to the same address we just used. + using (Socket b = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + b.Bind(new IPEndPoint(IPAddress.Loopback, port)); + } + } + + [Fact] + public void ExclusiveAddressUseTcp() + { + using (Socket a = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + // ExclusiveAddressUse defaults to true on Unix, on Windows it defaults to false. + a.ExclusiveAddressUse = true; + + a.Bind(new IPEndPoint(IPAddress.Loopback, 0)); + a.Listen(10); + int port = (a.LocalEndPoint as IPEndPoint).Port; + + using (Socket b = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + SocketException ex = Assert.ThrowsAny(() => b.Bind(new IPEndPoint(IPAddress.Loopback, port))); + Assert.Equal(SocketError.AddressAlreadyInUse, ex.SocketErrorCode); + } + } + } + [OuterLoop] // TODO: Issue #11345 [Theory] [PlatformSpecific(TestPlatforms.Windows)] // SetIPProtectionLevel not supported on Unix diff --git a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.netcoreapp.cs b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.netcoreapp.cs index dacd87ac73..3e41627e37 100644 --- a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.netcoreapp.cs +++ b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.netcoreapp.cs @@ -2,12 +2,14 @@ // 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.Buffers; using System.Threading.Tasks; namespace System.Net.Sockets.Tests { public class SocketHelperSpanSync : SocketHelperArraySync { + public override bool ValidatesArrayArguments => false; public override Task ReceiveAsync(Socket s, ArraySegment buffer) => Task.Run(() => s.Receive((Span)buffer, SocketFlags.None)); public override Task SendAsync(Socket s, ArraySegment buffer) => @@ -16,6 +18,7 @@ namespace System.Net.Sockets.Tests public sealed class SocketHelperSpanSyncForceNonBlocking : SocketHelperSpanSync { + public override bool ValidatesArrayArguments => false; public override Task AcceptAsync(Socket s) => Task.Run(() => { s.ForceNonBlocking(true); Socket accepted = s.Accept(); accepted.ForceNonBlocking(true); return accepted; }); public override Task ConnectAsync(Socket s, EndPoint endPoint) => @@ -24,9 +27,32 @@ namespace System.Net.Sockets.Tests public sealed class SocketHelperMemoryArrayTask : SocketHelperTask { + public override bool ValidatesArrayArguments => false; public override Task ReceiveAsync(Socket s, ArraySegment buffer) => s.ReceiveAsync((Memory)buffer, SocketFlags.None).AsTask(); public override Task SendAsync(Socket s, ArraySegment buffer) => s.SendAsync((ReadOnlyMemory)buffer, SocketFlags.None).AsTask(); } + + public sealed class SocketHelperMemoryNativeTask : SocketHelperTask + { + public override bool ValidatesArrayArguments => false; + public override async Task ReceiveAsync(Socket s, ArraySegment buffer) + { + using (var m = new NativeOwnedMemory(buffer.Count)) + { + int bytesReceived = await s.ReceiveAsync(m.Memory, SocketFlags.None).ConfigureAwait(false); + m.Memory.Span.Slice(0, bytesReceived).CopyTo(buffer.AsSpan()); + return bytesReceived; + } + } + public override async Task SendAsync(Socket s, ArraySegment buffer) + { + using (var m = new NativeOwnedMemory(buffer.Count)) + { + buffer.AsSpan().CopyTo(m.Memory.Span); + return await s.SendAsync(m.Memory, SocketFlags.None).ConfigureAwait(false); + } + } + } } diff --git a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj index 088fd2ba4d..47411a841d 100644 --- a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj +++ b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj @@ -3,6 +3,7 @@ {8CBA022C-635F-4C8D-9D29-CD8AAC68C8E6} + true @@ -43,10 +44,12 @@ + + SocketCommon\Configuration.cs @@ -91,12 +94,21 @@ Common\System\ShouldNotBeInvokedException.cs + + Common\System\Threading\Tasks\TaskTimeoutExtensions.cs + + + Common\System\Buffers\NativeOwnedMemory.cs + Common\System\Threading\Tasks\TaskToApm.cs Common\System\Diagnostics\Tracing\TestEventListener.cs + + Common\System\Net\Logging\NetEventSource.Common.cs + @@ -108,4 +120,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/TcpClientTest.cs b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/TcpClientTest.cs index 7afdf02489..2370bf24a1 100644 --- a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/TcpClientTest.cs +++ b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/TcpClientTest.cs @@ -272,7 +272,6 @@ namespace System.Net.Sockets.Tests } [Fact] - [PlatformSpecific(TestPlatforms.Windows)] // ExclusiveAddressUse=false only supported on Windows public void Roundtrip_ExclusiveAddressUse_GetEqualsSet_False() { using (TcpClient client = new TcpClient()) @@ -282,20 +281,6 @@ namespace System.Net.Sockets.Tests } } - [OuterLoop] // TODO: Issue #11345 - [Fact] - [PlatformSpecific(TestPlatforms.AnyUnix)] // ExclusiveAddressUse=false only supported on Windows - public void ExclusiveAddressUse_Set_False_NotSupported() - { - using (TcpClient client = new TcpClient()) - { - Assert.Throws(() => - { - client.ExclusiveAddressUse = false; - }); - } - } - [OuterLoop] // TODO: Issue #11345 [Fact] public void Roundtrip_LingerOption_GetEqualsSet() diff --git a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/TimeoutTest.cs b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/TimeoutTest.cs index 2f5031dfc5..f9ae783210 100644 --- a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/TimeoutTest.cs +++ b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/TimeoutTest.cs @@ -82,7 +82,8 @@ namespace System.Net.Sockets.Tests Assert.True(acceptedSocket.Connected); // Try to ensure that the elapsed timeout is reasonably correct - Assert.InRange(elapsed, Timeout * 0.75, Timeout * 1.5); + // Sometimes test machines run slowly + Assert.InRange(elapsed, Timeout * 0.75, Timeout * 2); } } @@ -127,7 +128,8 @@ namespace System.Net.Sockets.Tests Assert.True(acceptedSocket.Connected); // Try to ensure that the elapsed timeout is reasonably correct - Assert.InRange(elapsed, Timeout * 0.5, Timeout * 2); + // Sometimes test machines run slowly + Assert.InRange(elapsed, Timeout * 0.5, Timeout * 3); } } } diff --git a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs index e6c1dd8a5f..af17a979cb 100644 --- a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs +++ b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs @@ -2,20 +2,14 @@ // 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.Diagnostics; -using System.IO; -using System.Linq; using System.Net.Test.Common; -using System.Text; -using System.Threading; -using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; namespace System.Net.Sockets.Tests { - public class UnixDomainSocketTest + public partial class UnixDomainSocketTest { private readonly ITestOutputHelper _log; @@ -32,411 +26,5 @@ namespace System.Net.Sockets.Tests SocketException e = Assert.Throws(() => new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)); Assert.Equal(SocketError.AddressFamilyNotSupported, e.SocketErrorCode); } - - [OuterLoop] // TODO: Issue #11345 - [Fact] - [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests ConnectAsyncUnixDomainSocketEndPoint success on Unix - public async Task Socket_ConnectAsyncUnixDomainSocketEndPoint_Success() - { - string path = null; - SocketTestServer server = null; - UnixDomainSocketEndPoint endPoint = null; - - for (int attempt = 0; attempt < 5; attempt++) - { - path = GetRandomNonExistingFilePath(); - endPoint = new UnixDomainSocketEndPoint(path); - try - { - server = SocketTestServer.SocketTestServerFactory(SocketImplementationType.Async, endPoint, ProtocolType.Unspecified); - break; - } - catch (SocketException) - { - //Path selection is contingent on a successful Bind(). - //If it fails, the next iteration will try another path. - } - } - - try - { - Assert.NotNull(server); - - SocketAsyncEventArgs args = new SocketAsyncEventArgs(); - args.RemoteEndPoint = endPoint; - args.Completed += (s, e) => ((TaskCompletionSource)e.UserToken).SetResult(true); - - var complete = new TaskCompletionSource(); - args.UserToken = complete; - - using (Socket sock = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) - { - bool willRaiseEvent = sock.ConnectAsync(args); - if (willRaiseEvent) - { - await complete.Task; - } - - Assert.Equal(SocketError.Success, args.SocketError); - Assert.Null(args.ConnectByNameError); - } - } - finally - { - server.Dispose(); - - try { File.Delete(path); } - catch { } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Fact] - [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests ConnectAsyncUnixDomainSocketEndPoint seccess on Unix - public async Task Socket_ConnectAsyncUnixDomainSocketEndPoint_NotServer() - { - string path = GetRandomNonExistingFilePath(); - var endPoint = new UnixDomainSocketEndPoint(path); - try - { - SocketAsyncEventArgs args = new SocketAsyncEventArgs(); - args.RemoteEndPoint = endPoint; - args.Completed += (s, e) => ((TaskCompletionSource)e.UserToken).SetResult(true); - - var complete = new TaskCompletionSource(); - args.UserToken = complete; - - using (Socket sock = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) - { - bool willRaiseEvent = sock.ConnectAsync(args); - if (willRaiseEvent) - { - await complete.Task; - } - - Assert.Equal(SocketError.AddressNotAvailable, args.SocketError); - } - } - finally - { - try { File.Delete(path); } - catch { } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Fact] - [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests SendReceive success for UnixDomainSocketEndPoint on Unix - public void Socket_SendReceive_Success() - { - string path = GetRandomNonExistingFilePath(); - var endPoint = new UnixDomainSocketEndPoint(path); - try - { - using (var server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) - using (var client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) - { - server.Bind(endPoint); - server.Listen(1); - - client.Connect(endPoint); - using (Socket accepted = server.Accept()) - { - var data = new byte[1]; - for (int i = 0; i < 10; i++) - { - data[0] = (byte)i; - - accepted.Send(data); - data[0] = 0; - - Assert.Equal(1, client.Receive(data)); - Assert.Equal(i, data[0]); - } - } - } - } - finally - { - try { File.Delete(path); } - catch { } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Fact] - [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests SendReceiveAsync success for UnixDomainSocketEndPoint on Unix - public async Task Socket_SendReceiveAsync_Success() - { - string path = GetRandomNonExistingFilePath(); - var endPoint = new UnixDomainSocketEndPoint(path); - try - { - using (var server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) - using (var client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) - { - server.Bind(endPoint); - server.Listen(1); - - await client.ConnectAsync(endPoint); - using (Socket accepted = await server.AcceptAsync()) - { - var data = new byte[1]; - for (int i = 0; i < 10; i++) - { - data[0] = (byte)i; - - await accepted.SendAsync(new ArraySegment(data), SocketFlags.None); - data[0] = 0; - - Assert.Equal(1, await client.ReceiveAsync(new ArraySegment(data), SocketFlags.None)); - Assert.Equal(i, data[0]); - } - } - } - } - finally - { - try { File.Delete(path); } - catch { } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Theory] - [InlineData(5000, 1, 1)] - [InlineData(500, 18, 21)] - [InlineData(500, 21, 18)] - [InlineData(5, 128000, 64000)] - [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests SendReceiveAsync success for UnixDomainSocketEndPoint on Unix - public async Task Socket_SendReceiveAsync_PropagateToStream_Success(int iterations, int writeBufferSize, int readBufferSize) - { - var writeBuffer = new byte[writeBufferSize * iterations]; - new Random().NextBytes(writeBuffer); - var readData = new MemoryStream(); - - string path = GetRandomNonExistingFilePath(); - var endPoint = new UnixDomainSocketEndPoint(path); - try - { - using (var server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) - using (var client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) - { - server.Bind(endPoint); - server.Listen(1); - - Task serverAccept = server.AcceptAsync(); - await Task.WhenAll(serverAccept, client.ConnectAsync(endPoint)); - - Task clientReceives = Task.Run(async () => - { - int bytesRead; - byte[] buffer = new byte[readBufferSize]; - while ((bytesRead = await client.ReceiveAsync(new ArraySegment(buffer), SocketFlags.None)) > 0) - { - readData.Write(buffer, 0, bytesRead); - } - }); - - using (Socket accepted = await serverAccept) - { - for (int iter = 0; iter < iterations; iter++) - { - Task sendTask = accepted.SendAsync(new ArraySegment(writeBuffer, iter * writeBufferSize, writeBufferSize), SocketFlags.None); - await await Task.WhenAny(clientReceives, sendTask); - Assert.Equal(writeBufferSize, await sendTask); - } - } - - await clientReceives; - } - - Assert.Equal(writeBuffer.Length, readData.Length); - Assert.Equal(writeBuffer, readData.ToArray()); - } - finally - { - try { File.Delete(path); } - catch { } - } - } - - [OuterLoop] // TODO: Issue #11345 - [Theory] - [InlineData(false)] - [InlineData(true)] - [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests ConcurrentSendReceive success for UnixDomainSocketEndPoint on Unix - public void ConcurrentSendReceive(bool forceNonBlocking) - { - using (Socket server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) - using (Socket client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) - { - const int Iters = 25; - byte[] sendData = new byte[Iters]; - byte[] receiveData = new byte[sendData.Length]; - new Random().NextBytes(sendData); - - string path = GetRandomNonExistingFilePath(); - - server.Bind(new UnixDomainSocketEndPoint(path)); - server.Listen(1); - - Task acceptTask = server.AcceptAsync(); - client.Connect(new UnixDomainSocketEndPoint(path)); - acceptTask.Wait(); - Socket accepted = acceptTask.Result; - - client.ForceNonBlocking(forceNonBlocking); - accepted.ForceNonBlocking(forceNonBlocking); - - Task[] writes = new Task[Iters]; - Task[] reads = new Task[Iters]; - for (int i = 0; i < Iters; i++) - { - reads[i] = Task.Factory.StartNew(s => accepted.Receive(receiveData, (int)s, 1, SocketFlags.None), i, - CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); - } - for (int i = 0; i < Iters; i++) - { - writes[i] = Task.Factory.StartNew(s => client.Send(sendData, (int)s, 1, SocketFlags.None), i, - CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); - } - Task.WaitAll(writes); - Task.WaitAll(reads); - - Assert.Equal(sendData.OrderBy(i => i), receiveData.OrderBy(i => i)); - } - } - - [OuterLoop] // TODO: Issue #11345 - [Fact] - [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests ConcurrentSendReceive success for UnixDomainSocketEndPoint on Unix - public void ConcurrentSendReceiveAsync() - { - using (Socket server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) - using (Socket client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) - { - const int Iters = 2048; - byte[] sendData = new byte[Iters]; - byte[] receiveData = new byte[sendData.Length]; - new Random().NextBytes(sendData); - - string path = GetRandomNonExistingFilePath(); - - server.Bind(new UnixDomainSocketEndPoint(path)); - server.Listen(1); - - Task acceptTask = server.AcceptAsync(); - client.Connect(new UnixDomainSocketEndPoint(path)); - acceptTask.Wait(); - Socket accepted = acceptTask.Result; - - Task[] writes = new Task[Iters]; - Task[] reads = new Task[Iters]; - for (int i = 0; i < Iters; i++) - { - writes[i] = client.SendAsync(new ArraySegment(sendData, i, 1), SocketFlags.None); - } - for (int i = 0; i < Iters; i++) - { - reads[i] = accepted.ReceiveAsync(new ArraySegment(receiveData, i, 1), SocketFlags.None); - } - Task.WaitAll(writes); - Task.WaitAll(reads); - - Assert.Equal(sendData, receiveData); - } - } - - private static string GetRandomNonExistingFilePath() - { - string result; - do - { - result = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); - } - while (File.Exists(result)); - - return result; - } - - private sealed class UnixDomainSocketEndPoint : EndPoint - { - private const AddressFamily EndPointAddressFamily = AddressFamily.Unix; - - private static readonly Encoding s_pathEncoding = Encoding.UTF8; - private static readonly int s_nativePathOffset = 2; // = offsetof(struct sockaddr_un, sun_path). It's the same on Linux and OSX - private static readonly int s_nativePathLength = 91; // sockaddr_un.sun_path at http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_un.h.html, -1 for terminator - private static readonly int s_nativeAddressSize = s_nativePathOffset + s_nativePathLength; - - private readonly string _path; - private readonly byte[] _encodedPath; - - public UnixDomainSocketEndPoint(string path) - { - if (path == null) - { - throw new ArgumentNullException(nameof(path)); - } - - _path = path; - _encodedPath = s_pathEncoding.GetBytes(_path); - - if (path.Length == 0 || _encodedPath.Length > s_nativePathLength) - { - throw new ArgumentOutOfRangeException(nameof(path)); - } - } - - internal UnixDomainSocketEndPoint(SocketAddress socketAddress) - { - if (socketAddress == null) - { - throw new ArgumentNullException(nameof(socketAddress)); - } - - if (socketAddress.Family != EndPointAddressFamily || - socketAddress.Size > s_nativeAddressSize) - { - throw new ArgumentOutOfRangeException(nameof(socketAddress)); - } - - if (socketAddress.Size > s_nativePathOffset) - { - _encodedPath = new byte[socketAddress.Size - s_nativePathOffset]; - for (int i = 0; i < _encodedPath.Length; i++) - { - _encodedPath[i] = socketAddress[s_nativePathOffset + i]; - } - - _path = s_pathEncoding.GetString(_encodedPath, 0, _encodedPath.Length); - } - else - { - _encodedPath = Array.Empty(); - _path = string.Empty; - } - } - - public override SocketAddress Serialize() - { - var result = new SocketAddress(AddressFamily.Unix, s_nativeAddressSize); - Debug.Assert(_encodedPath.Length + s_nativePathOffset <= result.Size, "Expected path to fit in address"); - - for (int index = 0; index < _encodedPath.Length; index++) - { - result[s_nativePathOffset + index] = _encodedPath[index]; - } - result[s_nativePathOffset + _encodedPath.Length] = 0; // path must be null-terminated - - return result; - } - - public override EndPoint Create(SocketAddress socketAddress) => new UnixDomainSocketEndPoint(socketAddress); - - public override AddressFamily AddressFamily => EndPointAddressFamily; - - public override string ToString() => _path; - } } } diff --git a/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.netcoreapp.cs b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.netcoreapp.cs new file mode 100644 index 0000000000..661eb872ff --- /dev/null +++ b/external/corefx/src/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.netcoreapp.cs @@ -0,0 +1,447 @@ +// 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.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +using Xunit; + +namespace System.Net.Sockets.Tests +{ + public partial class UnixDomainSocketTest + { + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] // new UnixDomainSocketEndPoint should throw on Windows + public void UnixDomainSocketEndPoint_Throws_OnWindows() + { + Assert.Throws(() => new UnixDomainSocketEndPoint("/path")); + } + + [OuterLoop] // TODO: Issue #11345 + [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests ConnectAsyncUnixDomainSocketEndPoint success on Unix + public async Task Socket_ConnectAsyncUnixDomainSocketEndPoint_Success() + { + string path = null; + SocketTestServer server = null; + UnixDomainSocketEndPoint endPoint = null; + + for (int attempt = 0; attempt < 5; attempt++) + { + path = GetRandomNonExistingFilePath(); + endPoint = new UnixDomainSocketEndPoint(path); + try + { + server = SocketTestServer.SocketTestServerFactory(SocketImplementationType.Async, endPoint, ProtocolType.Unspecified); + break; + } + catch (SocketException) + { + //Path selection is contingent on a successful Bind(). + //If it fails, the next iteration will try another path. + } + } + + try + { + Assert.NotNull(server); + + SocketAsyncEventArgs args = new SocketAsyncEventArgs(); + args.RemoteEndPoint = endPoint; + args.Completed += (s, e) => ((TaskCompletionSource)e.UserToken).SetResult(true); + + var complete = new TaskCompletionSource(); + args.UserToken = complete; + + using (Socket sock = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + { + bool willRaiseEvent = sock.ConnectAsync(args); + if (willRaiseEvent) + { + await complete.Task; + } + + Assert.Equal(SocketError.Success, args.SocketError); + Assert.Null(args.ConnectByNameError); + } + } + finally + { + server.Dispose(); + + try { File.Delete(path); } + catch { } + } + } + + [OuterLoop] // TODO: Issue #11345 + [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests ConnectAsyncUnixDomainSocketEndPoint seccess on Unix + public async Task Socket_ConnectAsyncUnixDomainSocketEndPoint_NotServer() + { + string path = GetRandomNonExistingFilePath(); + var endPoint = new UnixDomainSocketEndPoint(path); + try + { + SocketAsyncEventArgs args = new SocketAsyncEventArgs(); + args.RemoteEndPoint = endPoint; + args.Completed += (s, e) => ((TaskCompletionSource)e.UserToken).SetResult(true); + + var complete = new TaskCompletionSource(); + args.UserToken = complete; + + using (Socket sock = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + { + bool willRaiseEvent = sock.ConnectAsync(args); + if (willRaiseEvent) + { + await complete.Task; + } + + Assert.Equal(SocketError.AddressNotAvailable, args.SocketError); + } + } + finally + { + try { File.Delete(path); } + catch { } + } + } + + [OuterLoop] // TODO: Issue #11345 + [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests SendReceive success for UnixDomainSocketEndPoint on Unix + public void Socket_SendReceive_Success() + { + string path = GetRandomNonExistingFilePath(); + var endPoint = new UnixDomainSocketEndPoint(path); + try + { + using (var server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + using (var client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + { + server.Bind(endPoint); + server.Listen(1); + + client.Connect(endPoint); + using (Socket accepted = server.Accept()) + { + var data = new byte[1]; + for (int i = 0; i < 10; i++) + { + data[0] = (byte)i; + + accepted.Send(data); + data[0] = 0; + + Assert.Equal(1, client.Receive(data)); + Assert.Equal(i, data[0]); + } + } + } + } + finally + { + try { File.Delete(path); } + catch { } + } + } + + [OuterLoop] // TODO: Issue #11345 + [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests SendReceiveAsync success for UnixDomainSocketEndPoint on Unix + public async Task Socket_SendReceiveAsync_Success() + { + string path = GetRandomNonExistingFilePath(); + var endPoint = new UnixDomainSocketEndPoint(path); + try + { + using (var server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + using (var client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + { + server.Bind(endPoint); + server.Listen(1); + + await client.ConnectAsync(endPoint); + using (Socket accepted = await server.AcceptAsync()) + { + var data = new byte[1]; + for (int i = 0; i < 10; i++) + { + data[0] = (byte)i; + + await accepted.SendAsync(new ArraySegment(data), SocketFlags.None); + data[0] = 0; + + Assert.Equal(1, await client.ReceiveAsync(new ArraySegment(data), SocketFlags.None)); + Assert.Equal(i, data[0]); + } + } + } + } + finally + { + try { File.Delete(path); } + catch { } + } + } + + [OuterLoop] // TODO: Issue #11345 + [Theory] + [InlineData(5000, 1, 1)] + [InlineData(500, 18, 21)] + [InlineData(500, 21, 18)] + [InlineData(5, 128000, 64000)] + [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests SendReceiveAsync success for UnixDomainSocketEndPoint on Unix + public async Task Socket_SendReceiveAsync_PropagateToStream_Success(int iterations, int writeBufferSize, int readBufferSize) + { + var writeBuffer = new byte[writeBufferSize * iterations]; + new Random().NextBytes(writeBuffer); + var readData = new MemoryStream(); + + string path = GetRandomNonExistingFilePath(); + var endPoint = new UnixDomainSocketEndPoint(path); + try + { + using (var server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + using (var client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + { + server.Bind(endPoint); + server.Listen(1); + + Task serverAccept = server.AcceptAsync(); + await Task.WhenAll(serverAccept, client.ConnectAsync(endPoint)); + + Task clientReceives = Task.Run(async () => + { + int bytesRead; + byte[] buffer = new byte[readBufferSize]; + while ((bytesRead = await client.ReceiveAsync(new ArraySegment(buffer), SocketFlags.None)) > 0) + { + readData.Write(buffer, 0, bytesRead); + } + }); + + using (Socket accepted = await serverAccept) + { + for (int iter = 0; iter < iterations; iter++) + { + Task sendTask = accepted.SendAsync(new ArraySegment(writeBuffer, iter * writeBufferSize, writeBufferSize), SocketFlags.None); + await await Task.WhenAny(clientReceives, sendTask); + Assert.Equal(writeBufferSize, await sendTask); + } + } + + await clientReceives; + } + + Assert.Equal(writeBuffer.Length, readData.Length); + Assert.Equal(writeBuffer, readData.ToArray()); + } + finally + { + try { File.Delete(path); } + catch { } + } + } + + [OuterLoop] // TODO: Issue #11345 + [Theory] + [InlineData(false)] + [InlineData(true)] + [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests ConcurrentSendReceive success for UnixDomainSocketEndPoint on Unix + public async Task ConcurrentSendReceive(bool forceNonBlocking) + { + using (Socket server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + using (Socket client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + { + const int Iters = 25; + byte[] sendData = new byte[Iters]; + byte[] receiveData = new byte[sendData.Length]; + new Random().NextBytes(sendData); + + string path = GetRandomNonExistingFilePath(); + + server.Bind(new UnixDomainSocketEndPoint(path)); + server.Listen(1); + + Task acceptTask = server.AcceptAsync(); + client.Connect(new UnixDomainSocketEndPoint(path)); + await acceptTask; + Socket accepted = acceptTask.Result; + + client.ForceNonBlocking(forceNonBlocking); + accepted.ForceNonBlocking(forceNonBlocking); + + Task[] writes = new Task[Iters]; + Task[] reads = new Task[Iters]; + for (int i = 0; i < Iters; i++) + { + reads[i] = Task.Factory.StartNew(s => accepted.Receive(receiveData, (int)s, 1, SocketFlags.None), i, + CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); + } + for (int i = 0; i < Iters; i++) + { + writes[i] = Task.Factory.StartNew(s => client.Send(sendData, (int)s, 1, SocketFlags.None), i, + CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); + } + await TestSettings.WhenAllOrAnyFailedWithTimeout(writes.Concat(reads).ToArray()); + + Assert.Equal(sendData.OrderBy(i => i), receiveData.OrderBy(i => i)); + } + } + + [OuterLoop] // TODO: Issue #11345 + [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests ConcurrentSendReceive success for UnixDomainSocketEndPoint on Unix + public async Task ConcurrentSendReceiveAsync() + { + using (Socket server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + using (Socket client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + { + const int Iters = 2048; + byte[] sendData = new byte[Iters]; + byte[] receiveData = new byte[sendData.Length]; + new Random().NextBytes(sendData); + + string path = GetRandomNonExistingFilePath(); + + server.Bind(new UnixDomainSocketEndPoint(path)); + server.Listen(1); + + Task acceptTask = server.AcceptAsync(); + client.Connect(new UnixDomainSocketEndPoint(path)); + await acceptTask; + Socket accepted = acceptTask.Result; + + Task[] writes = new Task[Iters]; + Task[] reads = new Task[Iters]; + for (int i = 0; i < Iters; i++) + { + writes[i] = client.SendAsync(new ArraySegment(sendData, i, 1), SocketFlags.None); + } + for (int i = 0; i < Iters; i++) + { + reads[i] = accepted.ReceiveAsync(new ArraySegment(receiveData, i, 1), SocketFlags.None); + } + + await TestSettings.WhenAllOrAnyFailedWithTimeout(writes.Concat(reads).ToArray()); + + Assert.Equal(sendData, receiveData); + } + } + + [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix)] // Tests new UnixDomainSocketEndPoint throws the correct exception for invalid args + public void UnixDomainSocketEndPoint_InvalidPaths_Throws() + { + Assert.Throws(() => new UnixDomainSocketEndPoint(null)); + Assert.Throws(() => new UnixDomainSocketEndPoint(string.Empty)); + + int maxNativeSize = (int)typeof(UnixDomainSocketEndPoint) + .GetField("s_nativePathLength", BindingFlags.Static | BindingFlags.NonPublic) + .GetValue(null); + + string invalidLengthString = new string('a', maxNativeSize + 1); + Assert.Throws(() => new UnixDomainSocketEndPoint(invalidLengthString)); + } + + [Theory] + [PlatformSpecific(TestPlatforms.AnyUnix)] + [InlineData(false)] + [InlineData(true)] + public void UnixDomainSocketEndPoint_RemoteEndPointEqualsBindAddress(bool abstractAddress) + { + string serverAddress; + string clientAddress; + string expectedClientAddress; + if (abstractAddress) + { + // abstract socket addresses are a Linux feature. + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return; + } + // An abstract socket address starts with a zero byte. + serverAddress = '\0' + Guid.NewGuid().ToString(); + clientAddress = '\0' + Guid.NewGuid().ToString(); + expectedClientAddress = '@' + clientAddress.Substring(1); + } + else + { + serverAddress = GetRandomNonExistingFilePath(); + clientAddress = GetRandomNonExistingFilePath(); + expectedClientAddress = clientAddress; + } + + try + { + using (Socket server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + { + server.Bind(new UnixDomainSocketEndPoint(serverAddress)); + server.Listen(1); + + using (Socket client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + { + // Bind the client. + client.Bind(new UnixDomainSocketEndPoint(clientAddress)); + client.Connect(new UnixDomainSocketEndPoint(serverAddress)); + using (Socket acceptedClient = server.Accept()) + { + // Verify the client address on the server. + EndPoint clientAddressOnServer = acceptedClient.RemoteEndPoint; + Assert.True(string.CompareOrdinal(expectedClientAddress, clientAddressOnServer.ToString()) == 0); + } + } + } + } + finally + { + if (!abstractAddress) + { + try { File.Delete(serverAddress); } + catch { } + try { File.Delete(clientAddress); } + catch { } + } + } + } + + [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Linux)] // Don't support abstract socket addresses. + public void UnixDomainSocketEndPoint_UsingAbstractSocketAddressOnUnsupported_Throws() + { + // An abstract socket address starts with a zero byte. + string address = '\0' + Guid.NewGuid().ToString(); + + // Bind + using (Socket socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + { + Assert.ThrowsAny(() => socket.Bind(new UnixDomainSocketEndPoint(address))); + } + + // Connect + using (Socket socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + { + Assert.ThrowsAny(() => socket.Connect(new UnixDomainSocketEndPoint(address))); + } + } + + private static string GetRandomNonExistingFilePath() + { + string result; + do + { + result = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + } + while (File.Exists(result)); + + return result; + } + } +} diff --git a/external/corefx/src/System.Net.Sockets/tests/PerformanceTests/Configurations.props b/external/corefx/src/System.Net.Sockets/tests/ManualPerformanceTests/Configurations.props similarity index 100% rename from external/corefx/src/System.Net.Sockets/tests/PerformanceTests/Configurations.props rename to external/corefx/src/System.Net.Sockets/tests/ManualPerformanceTests/Configurations.props diff --git a/external/corefx/src/System.Net.Sockets/tests/PerformanceTests/SocketPerformanceAPMTests.cs b/external/corefx/src/System.Net.Sockets/tests/ManualPerformanceTests/SocketPerformanceAPMTests.cs similarity index 100% rename from external/corefx/src/System.Net.Sockets/tests/PerformanceTests/SocketPerformanceAPMTests.cs rename to external/corefx/src/System.Net.Sockets/tests/ManualPerformanceTests/SocketPerformanceAPMTests.cs diff --git a/external/corefx/src/System.Net.Sockets/tests/PerformanceTests/SocketPerformanceAsyncTests.cs b/external/corefx/src/System.Net.Sockets/tests/ManualPerformanceTests/SocketPerformanceAsyncTests.cs similarity index 100% rename from external/corefx/src/System.Net.Sockets/tests/PerformanceTests/SocketPerformanceAsyncTests.cs rename to external/corefx/src/System.Net.Sockets/tests/ManualPerformanceTests/SocketPerformanceAsyncTests.cs diff --git a/external/corefx/src/System.Net.Sockets/tests/PerformanceTests/SocketTestClientAPMMock.cs b/external/corefx/src/System.Net.Sockets/tests/ManualPerformanceTests/SocketTestClientAPMMock.cs similarity index 100% rename from external/corefx/src/System.Net.Sockets/tests/PerformanceTests/SocketTestClientAPMMock.cs rename to external/corefx/src/System.Net.Sockets/tests/ManualPerformanceTests/SocketTestClientAPMMock.cs diff --git a/external/corefx/src/System.Net.Sockets/tests/PerformanceTests/SocketTestServerAPMMock.cs b/external/corefx/src/System.Net.Sockets/tests/ManualPerformanceTests/SocketTestServerAPMMock.cs similarity index 100% rename from external/corefx/src/System.Net.Sockets/tests/PerformanceTests/SocketTestServerAPMMock.cs rename to external/corefx/src/System.Net.Sockets/tests/ManualPerformanceTests/SocketTestServerAPMMock.cs diff --git a/external/corefx/src/System.Net.Sockets/tests/PerformanceTests/System.Net.Sockets.Async.Performance.Tests.csproj b/external/corefx/src/System.Net.Sockets/tests/ManualPerformanceTests/System.Net.Sockets.Async.Performance.Tests.csproj similarity index 93% rename from external/corefx/src/System.Net.Sockets/tests/PerformanceTests/System.Net.Sockets.Async.Performance.Tests.csproj rename to external/corefx/src/System.Net.Sockets/tests/ManualPerformanceTests/System.Net.Sockets.Async.Performance.Tests.csproj index c09de3302a..f5b5401921 100644 --- a/external/corefx/src/System.Net.Sockets/tests/PerformanceTests/System.Net.Sockets.Async.Performance.Tests.csproj +++ b/external/corefx/src/System.Net.Sockets/tests/ManualPerformanceTests/System.Net.Sockets.Async.Performance.Tests.csproj @@ -2,6 +2,7 @@ + true {BB5C85AD-C51A-4903-80E9-6F6E1AC1AD34} @@ -47,6 +48,9 @@ Common\System\Net\Capability.Sockets.cs + + Common\System\Threading\Tasks\TaskTimeoutExtensions.cs + @@ -65,4 +69,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.Net.Sockets/tests/Performance/Configurations.props b/external/corefx/src/System.Net.Sockets/tests/Performance/Configurations.props new file mode 100644 index 0000000000..2845c11c54 --- /dev/null +++ b/external/corefx/src/System.Net.Sockets/tests/Performance/Configurations.props @@ -0,0 +1,8 @@ + + + + + netcoreapp; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Net.Sockets/tests/Performance/Perf.Socket.SendReceive.cs b/external/corefx/src/System.Net.Sockets/tests/Performance/Perf.Socket.SendReceive.cs new file mode 100644 index 0000000000..5f359277ac --- /dev/null +++ b/external/corefx/src/System.Net.Sockets/tests/Performance/Perf.Socket.SendReceive.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. +// See the LICENSE file in the project root for more information. + +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Xunit.Performance; +using Xunit; + +namespace System.Net.Sockets.Tests +{ + public class SocketSendReceivePerfTest + { + [Benchmark(InnerIterationCount = 10_000), MeasureGCAllocations] + public async Task SendAsyncThenReceiveAsync_Task() + { + await OpenLoopbackConnectionAsync(async (client, server) => + { + byte[] clientBuffer = new byte[1]; + byte[] serverBuffer = new byte[1]; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long iters = Benchmark.InnerIterationCount; + using (iteration.StartMeasurement()) + { + for (int i = 0; i < iters; i++) + { + await client.SendAsync(clientBuffer, SocketFlags.None); + await server.ReceiveAsync(serverBuffer, SocketFlags.None); + } + } + } + }); + } + + [Benchmark(InnerIterationCount = 10_000), MeasureGCAllocations] + public async Task ReceiveAsyncThenSendAsync_Task() + { + await OpenLoopbackConnectionAsync(async (client, server) => + { + byte[] clientBuffer = new byte[1]; + byte[] serverBuffer = new byte[1]; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long iters = Benchmark.InnerIterationCount; + using (iteration.StartMeasurement()) + { + for (int i = 0; i < iters; i++) + { + Task r = server.ReceiveAsync(serverBuffer, SocketFlags.None); + await client.SendAsync(clientBuffer, SocketFlags.None); + await r; + } + } + } + }); + } + + [Benchmark(InnerIterationCount = 1_000_000)] + [InlineData(16)] + public async Task ReceiveAsyncThenSendAsync_Task_Parallel(int numConnections) + { + byte[] clientBuffer = new byte[1]; + byte[] serverBuffer = new byte[1]; + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + await OpenLoopbackConnectionAsync(async (client, server) => + { + long iters = Benchmark.InnerIterationCount; + using (iteration.StartMeasurement()) + { + for (int i = 0; i < iters; i++) + { + Task r = server.ReceiveAsync(serverBuffer, SocketFlags.None); + await client.SendAsync(clientBuffer, SocketFlags.None); + await r; + } + } + }); + } + } + + [Benchmark(InnerIterationCount = 10_000), MeasureGCAllocations] + public async Task SendAsyncThenReceiveAsync_SocketAsyncEventArgs() + { + await OpenLoopbackConnectionAsync(async (client, server) => + { + var clientSaea = new AwaitableSocketAsyncEventArgs(); + var serverSaea = new AwaitableSocketAsyncEventArgs(); + + clientSaea.SetBuffer(new byte[1], 0, 1); + serverSaea.SetBuffer(new byte[1], 0, 1); + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long iters = Benchmark.InnerIterationCount; + using (iteration.StartMeasurement()) + { + for (int i = 0; i < iters; i++) + { + if (client.SendAsync(clientSaea)) + { + await clientSaea; + } + if (server.ReceiveAsync(serverSaea)) + { + await serverSaea; + } + } + } + } + }); + } + + [Benchmark(InnerIterationCount = 10_000), MeasureGCAllocations] + public async Task ReceiveAsyncThenSendAsync_SocketAsyncEventArgs() + { + await OpenLoopbackConnectionAsync(async (client, server) => + { + var clientSaea = new AwaitableSocketAsyncEventArgs(); + var serverSaea = new AwaitableSocketAsyncEventArgs(); + + clientSaea.SetBuffer(new byte[1], 0, 1); + serverSaea.SetBuffer(new byte[1], 0, 1); + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long iters = Benchmark.InnerIterationCount; + using (iteration.StartMeasurement()) + { + for (int i = 0; i < iters; i++) + { + bool pendingServer = server.ReceiveAsync(serverSaea); + + if (client.SendAsync(clientSaea)) + { + await clientSaea; + } + + if (pendingServer) + { + await serverSaea; + } + } + } + } + }); + } + + private static async Task OpenLoopbackConnectionAsync(Func func) + { + using (var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + listener.Bind(new IPEndPoint(IPAddress.Loopback, 0)); + listener.Listen(1); + + Task acceptTask = listener.AcceptAsync(); + Task connectTask = client.ConnectAsync(listener.LocalEndPoint); + + await await Task.WhenAny(acceptTask, connectTask); + await Task.WhenAll(acceptTask, connectTask); + + using (Socket server = await acceptTask) + { + await func(client, server); + } + } + } + + internal sealed class AwaitableSocketAsyncEventArgs : SocketAsyncEventArgs, ICriticalNotifyCompletion + { + private static readonly Action s_completedSentinel = () => { }; + private Action _continuation; + + public AwaitableSocketAsyncEventArgs() + { + Completed += delegate + { + Action c = _continuation; + if (c != null) + { + c(); + } + else + { + Interlocked.CompareExchange(ref _continuation, s_completedSentinel, null)?.Invoke(); + } + }; + } + + public AwaitableSocketAsyncEventArgs GetAwaiter() => this; + + public bool IsCompleted => _continuation != null; + + public void UnsafeOnCompleted(Action continuation) => OnCompleted(continuation); + + public void OnCompleted(Action continuation) + { + if (ReferenceEquals(_continuation, s_completedSentinel) || + ReferenceEquals(Interlocked.CompareExchange(ref _continuation, continuation, null), s_completedSentinel)) + { + Task.Run(continuation); + } + } + + public int GetResult() + { + _continuation = null; + if (SocketError != SocketError.Success) + { + throw new SocketException((int)SocketError); + } + return BytesTransferred; + } + } + } +} diff --git a/external/corefx/src/System.Net.Sockets/tests/Performance/System.Net.Sockets.Performance.Tests.csproj b/external/corefx/src/System.Net.Sockets/tests/Performance/System.Net.Sockets.Performance.Tests.csproj new file mode 100644 index 0000000000..11c4fca607 --- /dev/null +++ b/external/corefx/src/System.Net.Sockets/tests/Performance/System.Net.Sockets.Performance.Tests.csproj @@ -0,0 +1,24 @@ + + + + + true + {981FC867-9071-444D-9388-BC5826609F76} + + + + + + + + Common\System\PerfUtils.cs + + + + + {69e46a6f-9966-45a5-8945-2559fe337827} + PerfRunner + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Net.WebHeaderCollection/src/System.Net.WebHeaderCollection.csproj b/external/corefx/src/System.Net.WebHeaderCollection/src/System.Net.WebHeaderCollection.csproj index 7cfcb3b0e9..8fc538de7a 100644 --- a/external/corefx/src/System.Net.WebHeaderCollection/src/System.Net.WebHeaderCollection.csproj +++ b/external/corefx/src/System.Net.WebHeaderCollection/src/System.Net.WebHeaderCollection.csproj @@ -37,9 +37,11 @@ + + diff --git a/external/corefx/src/System.Net.WebHeaderCollection/src/System/Net/HeaderInfo.cs b/external/corefx/src/System.Net.WebHeaderCollection/src/System/Net/HeaderInfo.cs index cc59624291..dd1e90536f 100644 --- a/external/corefx/src/System.Net.WebHeaderCollection/src/System/Net/HeaderInfo.cs +++ b/external/corefx/src/System.Net.WebHeaderCollection/src/System/Net/HeaderInfo.cs @@ -2,23 +2,28 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -internal class HeaderInfo +namespace System.Net { - internal readonly bool IsRequestRestricted; - internal readonly bool IsResponseRestricted; - // - // Note that the HeaderName field is not always valid, and should not - // be used after initialization. In particular, the HeaderInfo returned - // for an unknown header will not have the correct header name. - // - internal readonly string HeaderName; - internal readonly bool AllowMultiValues; - - internal HeaderInfo(string name, bool requestRestricted, bool responseRestricted, bool multi) + internal class HeaderInfo { - HeaderName = name; - IsRequestRestricted = requestRestricted; - IsResponseRestricted = responseRestricted; - AllowMultiValues = multi; + internal readonly bool IsRequestRestricted; + internal readonly bool IsResponseRestricted; + internal readonly Func Parser; + // + // Note that the HeaderName field is not always valid, and should not + // be used after initialization. In particular, the HeaderInfo returned + // for an unknown header will not have the correct header name. + // + internal readonly string HeaderName; + internal readonly bool AllowMultiValues; + + internal HeaderInfo(string name, bool requestRestricted, bool responseRestricted, bool multi, Func parser) + { + HeaderName = name; + IsRequestRestricted = requestRestricted; + IsResponseRestricted = responseRestricted; + Parser = parser; + AllowMultiValues = multi; + } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Net.WebHeaderCollection/src/System/Net/HeaderInfoTable.cs b/external/corefx/src/System.Net.WebHeaderCollection/src/System/Net/HeaderInfoTable.cs index 3716938f60..6e283f992e 100644 --- a/external/corefx/src/System.Net.WebHeaderCollection/src/System/Net/HeaderInfoTable.cs +++ b/external/corefx/src/System.Net.WebHeaderCollection/src/System/Net/HeaderInfoTable.cs @@ -3,77 +3,141 @@ // See the LICENSE file in the project root for more information. using System.Collections; +using System.Collections.Generic; using System.Collections.Specialized; +using System.Linq; namespace System.Net { internal class HeaderInfoTable { - private static Hashtable HeaderHashTable; - private static HeaderInfo UnknownHeaderInfo = new HeaderInfo(string.Empty, false, false, false); + private static readonly Func s_singleParser = value => new[] { value }; + private static readonly Func s_multiParser = value => ParseValueHelper(value, isSetCookie: false); + private static readonly Func s_setCookieParser = value => ParseValueHelper(value, isSetCookie: true); + private static readonly HeaderInfo s_unknownHeaderInfo = new HeaderInfo(string.Empty, false, false, false, s_singleParser); + private static readonly Hashtable s_headerHashTable; + + private static string[] ParseValueHelper(string value, bool isSetCookie) + { + // RFC 6265: (for Set-Cookie header) + // If the name-value-pair string lacks a %x3D ("=") character, ignore the set-cookie-string entirely. + if (isSetCookie && (value.IndexOf('=') < 0)) return Array.Empty(); + + var tempStringCollection = new List(); + + bool inquote = false; + int startIndex = 0; + int length = 0; + + for (int i = 0; i < value.Length; i++) + { + if (value[i] == '\"') + { + inquote = !inquote; + } + else if ((value[i] == ',') && !inquote) + { + string singleValue = value.SubstringTrim(startIndex, length); + if (!isSetCookie || !IsDuringExpiresAttributeParsing(singleValue)) + { + tempStringCollection.Add(singleValue); + startIndex = i + 1; + length = 0; + continue; + } + } + length++; + } + + // Now add the last of the header values to the string table. + if (startIndex < value.Length && length > 0) + { + tempStringCollection.Add(value.SubstringTrim(startIndex, length)); + } + + return tempStringCollection.ToArray(); + } + + // This method is to check if we are in the middle of parsing the Expires attribute + // for Set-Cookie header. It needs to check two conditions: 1. If current attribute + // is Expires. 2. Have we finished parsing it yet. Because the Expires attribute + // will contain exactly one comma, no comma means we are still parsing it. + private static bool IsDuringExpiresAttributeParsing(string singleValue) + { + // Current cookie doesn't contain any attributes. + if (singleValue.IndexOf(';') < 0) return false; + + string lastElement = singleValue.Split(';').Last(); + bool noComma = lastElement.IndexOf(',') < 0; + + string lastAttribute = lastElement.Split('=')[0].Trim(); + bool isExpires = string.Equals(lastAttribute, "Expires", StringComparison.OrdinalIgnoreCase); + + return (isExpires && noComma); + } static HeaderInfoTable() { HeaderInfo[] InfoArray = new HeaderInfo[] { - new HeaderInfo(HttpKnownHeaderNames.Age, false, false, false ), - new HeaderInfo(HttpKnownHeaderNames.Allow, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.Accept, true, false, true ), - new HeaderInfo(HttpKnownHeaderNames.Authorization, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.AcceptRanges, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.AcceptCharset, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.AcceptEncoding, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.AcceptLanguage, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.Cookie, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.Connection, true, false, true ), - new HeaderInfo(HttpKnownHeaderNames.ContentMD5, false, false, false ), - new HeaderInfo(HttpKnownHeaderNames.ContentType, true, false, false ), - new HeaderInfo(HttpKnownHeaderNames.CacheControl, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.ContentRange, false, false, false ), - new HeaderInfo(HttpKnownHeaderNames.ContentLength, true, true, false ), - new HeaderInfo(HttpKnownHeaderNames.ContentEncoding, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.ContentLanguage, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.ContentLocation, false, false, false ), - new HeaderInfo(HttpKnownHeaderNames.Date, true, false, false ), - new HeaderInfo(HttpKnownHeaderNames.ETag, false, false, false ), - new HeaderInfo(HttpKnownHeaderNames.Expect, true, false, true ), - new HeaderInfo(HttpKnownHeaderNames.Expires, false, false, false ), - new HeaderInfo(HttpKnownHeaderNames.From, false, false, false ), - new HeaderInfo(HttpKnownHeaderNames.Host, true, false, false ), - new HeaderInfo(HttpKnownHeaderNames.IfMatch, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.IfRange, false, false, false ), - new HeaderInfo(HttpKnownHeaderNames.IfNoneMatch, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.IfModifiedSince, true, false, false ), - new HeaderInfo(HttpKnownHeaderNames.IfUnmodifiedSince, false, false, false ), - new HeaderInfo(HttpKnownHeaderNames.KeepAlive, false, true, false ), - new HeaderInfo(HttpKnownHeaderNames.Location, false, false, false ), - new HeaderInfo(HttpKnownHeaderNames.LastModified, false, false, false ), - new HeaderInfo(HttpKnownHeaderNames.MaxForwards, false, false, false ), - new HeaderInfo(HttpKnownHeaderNames.Pragma, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.ProxyAuthenticate, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.ProxyAuthorization, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.ProxyConnection, true, false, true ), - new HeaderInfo(HttpKnownHeaderNames.Range, true, false, true ), - new HeaderInfo(HttpKnownHeaderNames.Referer, true, false, false ), - new HeaderInfo(HttpKnownHeaderNames.RetryAfter, false, false, false ), - new HeaderInfo(HttpKnownHeaderNames.Server, false, false, false ), - new HeaderInfo(HttpKnownHeaderNames.SetCookie, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.SetCookie2, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.TE, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.Trailer, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.TransferEncoding, true, true, true ), - new HeaderInfo(HttpKnownHeaderNames.Upgrade, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.UserAgent, true, false, false ), - new HeaderInfo(HttpKnownHeaderNames.Via, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.Vary, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.Warning, false, false, true ), - new HeaderInfo(HttpKnownHeaderNames.WWWAuthenticate, false, true, true ), + new HeaderInfo(HttpKnownHeaderNames.Age, false, false, false, s_singleParser), + new HeaderInfo(HttpKnownHeaderNames.Allow, false, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.Accept, true, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.Authorization, false, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.AcceptRanges, false, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.AcceptCharset, false, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.AcceptEncoding, false, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.AcceptLanguage, false, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.Cookie, false, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.Connection, true, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.ContentMD5, false, false, false, s_singleParser), + new HeaderInfo(HttpKnownHeaderNames.ContentType, true, false, false, s_singleParser), + new HeaderInfo(HttpKnownHeaderNames.CacheControl, false, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.ContentRange, false, false, false, s_singleParser), + new HeaderInfo(HttpKnownHeaderNames.ContentLength, true, true, false, s_singleParser), + new HeaderInfo(HttpKnownHeaderNames.ContentEncoding, false, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.ContentLanguage, false, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.ContentLocation, false, false, false, s_singleParser), + new HeaderInfo(HttpKnownHeaderNames.Date, true, false, false, s_singleParser), + new HeaderInfo(HttpKnownHeaderNames.ETag, false, false, false, s_singleParser), + new HeaderInfo(HttpKnownHeaderNames.Expect, true, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.Expires, false, false, false, s_singleParser), + new HeaderInfo(HttpKnownHeaderNames.From, false, false, false, s_singleParser), + new HeaderInfo(HttpKnownHeaderNames.Host, true, false, false, s_singleParser), + new HeaderInfo(HttpKnownHeaderNames.IfMatch, false, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.IfRange, false, false, false, s_singleParser), + new HeaderInfo(HttpKnownHeaderNames.IfNoneMatch, false, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.IfModifiedSince, true, false, false, s_singleParser), + new HeaderInfo(HttpKnownHeaderNames.IfUnmodifiedSince, false, false, false, s_singleParser), + new HeaderInfo(HttpKnownHeaderNames.KeepAlive, false, true, false, s_singleParser), + new HeaderInfo(HttpKnownHeaderNames.Location, false, false, false, s_singleParser), + new HeaderInfo(HttpKnownHeaderNames.LastModified, false, false, false, s_singleParser), + new HeaderInfo(HttpKnownHeaderNames.MaxForwards, false, false, false, s_singleParser), + new HeaderInfo(HttpKnownHeaderNames.Pragma, false, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.ProxyAuthenticate, false, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.ProxyAuthorization, false, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.ProxyConnection, true, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.Range, true, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.Referer, true, false, false, s_singleParser), + new HeaderInfo(HttpKnownHeaderNames.RetryAfter, false, false, false, s_singleParser), + new HeaderInfo(HttpKnownHeaderNames.Server, false, false, false, s_singleParser), + new HeaderInfo(HttpKnownHeaderNames.SetCookie, false, false, true, s_setCookieParser), + new HeaderInfo(HttpKnownHeaderNames.SetCookie2, false, false, true, s_setCookieParser), + new HeaderInfo(HttpKnownHeaderNames.TE, false, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.Trailer, false, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.TransferEncoding, true, true, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.Upgrade, false, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.UserAgent, true, false, false, s_singleParser), + new HeaderInfo(HttpKnownHeaderNames.Via, false, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.Vary, false, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.Warning, false, false, true, s_multiParser), + new HeaderInfo(HttpKnownHeaderNames.WWWAuthenticate, false, true, true, s_singleParser) }; - HeaderHashTable = new Hashtable(InfoArray.Length * 2, CaseInsensitiveAscii.StaticInstance); + s_headerHashTable = new Hashtable(InfoArray.Length * 2, CaseInsensitiveAscii.StaticInstance); for (int i = 0; i < InfoArray.Length; i++) { - HeaderHashTable[InfoArray[i].HeaderName] = InfoArray[i]; + s_headerHashTable[InfoArray[i].HeaderName] = InfoArray[i]; } } @@ -81,10 +145,10 @@ namespace System.Net { get { - HeaderInfo tempHeaderInfo = (HeaderInfo)HeaderHashTable[name]; + HeaderInfo tempHeaderInfo = (HeaderInfo)s_headerHashTable[name]; if (tempHeaderInfo == null) { - return UnknownHeaderInfo; + return s_unknownHeaderInfo; } return tempHeaderInfo; } diff --git a/external/corefx/src/System.Net.WebHeaderCollection/src/System/Net/WebHeaderCollection.cs b/external/corefx/src/System.Net.WebHeaderCollection/src/System/Net/WebHeaderCollection.cs index e67747a493..c02914910e 100644 --- a/external/corefx/src/System.Net.WebHeaderCollection/src/System/Net/WebHeaderCollection.cs +++ b/external/corefx/src/System.Net.WebHeaderCollection/src/System/Net/WebHeaderCollection.cs @@ -228,9 +228,71 @@ namespace System.Net return InnerCollection.GetValues(index); } + // GetValues + // Routine Description: + // This method takes a header name and returns a string array representing + // the individual values for that headers. For example, if the headers + // contained the line Accept: text/plain, text/html then + // GetValues("Accept") would return an array of two strings: "text/plain" + // and "text/html". + // Arguments: + // header - Name of the header. + // Return Value: + // string[] - array of parsed string objects public override string[] GetValues(string header) { - return InnerCollection.GetValues(header); + // First get the information about the header and the values for + // the header. + HeaderInfo info = HeaderInfo[header]; + string[] values = InnerCollection.GetValues(header); + // If we have no information about the header or it doesn't allow + // multiple values, just return the values. + if (info == null || values == null || !info.AllowMultiValues) + { + return values; + } + // Here we have a multi value header. We need to go through + // each entry in the multi values array, and if an entry itself + // has multiple values we'll need to combine those in. + // + // We do some optimazation here, where we try not to copy the + // values unless there really is one that have multiple values. + string[] tempValues; + List valueList = null; + for (int i = 0; i < values.Length; i++) + { + // Parse this value header. + tempValues = info.Parser(values[i]); + // If we don't have an array list yet, see if this + // value has multiple values. + if (valueList == null) + { + // If it's not empty, replace valueList. + // Because for invalid WebRequest headers, we will return empty + // valueList instead of the default NameValueCollection.GetValues(). + if (tempValues != null) + { + // It does, so we need to create an array list that + // represents the Values, then trim out this one and + // the ones after it that haven't been parsed yet. + valueList = new List(values); + valueList.RemoveRange(i, values.Length - i); + valueList.AddRange(tempValues); + } + } + else + { + // We already have an List, so just add the values. + valueList.AddRange(tempValues); + } + } + // See if we have an List. If we don't, just return the values. + // Otherwise convert the List to a string array and return that. + if (valueList != null) + { + return valueList.ToArray(); + } + return values; } public override string GetKey(int index) diff --git a/external/corefx/src/System.Net.WebHeaderCollection/tests/WebHeaderCollectionTest.cs b/external/corefx/src/System.Net.WebHeaderCollection/tests/WebHeaderCollectionTest.cs index 3c7e40a763..b4f0bfe1f7 100644 --- a/external/corefx/src/System.Net.WebHeaderCollection/tests/WebHeaderCollectionTest.cs +++ b/external/corefx/src/System.Net.WebHeaderCollection/tests/WebHeaderCollectionTest.cs @@ -529,6 +529,108 @@ namespace System.Net.Tests AssertExtensions.Throws(paramName, () => headers.Add(header)); } + private const string HeaderType = "Set-Cookie"; + private const string Cookie1 = "locale=en; path=/; expires=Fri, 05 Oct 2018 06:28:57 -0000"; + private const string Cookie2 = "uuid=123abc; path=/; expires=Fri, 05 Oct 2018 06:28:57 -0000; secure; HttpOnly"; + private const string Cookie3 = "country=US; path=/; expires=Fri, 05 Oct 2018 06:28:57 -0000"; + private const string Cookie4 = "m_session=session1; path=/; expires=Sun, 08 Oct 2017 00:28:57 -0000; secure; HttpOnly"; + + private const string Cookie1NoAttribute = "locale=en"; + private const string Cookie2NoAttribute = "uuid=123abc"; + private const string Cookie3NoAttribute = "country=US"; + private const string Cookie4NoAttribute = "m_session=session1"; + + private const string CookieInvalid = "helloWorld"; + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] + public void GetValues_MultipleSetCookieHeadersWithExpiresAttribute_Success() + { + WebHeaderCollection w = new WebHeaderCollection(); + w.Add(HeaderType, Cookie1); + w.Add(HeaderType, Cookie2); + w.Add(HeaderType, Cookie3); + w.Add(HeaderType, Cookie4); + + string[] values = w.GetValues(HeaderType); + Assert.Equal(4, values.Length); + Assert.Equal(Cookie1, values[0]); + Assert.Equal(Cookie2, values[1]); + Assert.Equal(Cookie3, values[2]); + Assert.Equal(Cookie4, values[3]); + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] + public void GetValues_SingleSetCookieHeaderWithMultipleCookiesWithExpiresAttribute_Success() + { + WebHeaderCollection w = new WebHeaderCollection(); + w.Add(HeaderType, Cookie1 + "," + Cookie2 + "," + Cookie3 + "," + Cookie4); + + string[] values = w.GetValues(HeaderType); + Assert.Equal(4, values.Length); + Assert.Equal(Cookie1, values[0]); + Assert.Equal(Cookie2, values[1]); + Assert.Equal(Cookie3, values[2]); + Assert.Equal(Cookie4, values[3]); + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] + public void GetValues_MultipleSetCookieHeadersWithNoAttribute_Success() + { + WebHeaderCollection w = new WebHeaderCollection(); + w.Add(HeaderType, Cookie1NoAttribute); + w.Add(HeaderType, Cookie2NoAttribute); + w.Add(HeaderType, Cookie3NoAttribute); + w.Add(HeaderType, Cookie4NoAttribute); + + string[] values = w.GetValues(HeaderType); + Assert.Equal(4, values.Length); + Assert.Equal(Cookie1NoAttribute, values[0]); + Assert.Equal(Cookie2NoAttribute, values[1]); + Assert.Equal(Cookie3NoAttribute, values[2]); + Assert.Equal(Cookie4NoAttribute, values[3]); + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] + public void GetValues_SingleSetCookieHeaderWithMultipleCookiesWithNoAttribute_Success() + { + WebHeaderCollection w = new WebHeaderCollection(); + w.Add(HeaderType, Cookie1NoAttribute + "," + Cookie2NoAttribute + "," + Cookie3NoAttribute + "," + Cookie4NoAttribute); + + string[] values = w.GetValues(HeaderType); + Assert.Equal(4, values.Length); + Assert.Equal(Cookie1NoAttribute, values[0]); + Assert.Equal(Cookie2NoAttribute, values[1]); + Assert.Equal(Cookie3NoAttribute, values[2]); + Assert.Equal(Cookie4NoAttribute, values[3]); + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] + public void GetValues_InvalidSetCookieHeader_Success() + { + WebHeaderCollection w = new WebHeaderCollection(); + w.Add(HeaderType, CookieInvalid); + + string[] values = w.GetValues(HeaderType); + Assert.Equal(0, values.Length); + } + + [Fact] + public void GetValues_MultipleValuesHeader_Success() + { + WebHeaderCollection w = new WebHeaderCollection(); + string headerType = "Accept"; + w.Add(headerType, "text/plain, text/html"); + string[] values = w.GetValues(headerType); + Assert.Equal(2, values.Length); + Assert.Equal("text/plain", values[0]); + Assert.Equal("text/html", values[1]); + } + [Fact] public void HttpRequestHeader_Add_Rmemove_Success() { diff --git a/external/corefx/src/System.Net.WebSockets.Client/System.Net.WebSockets.Client.sln b/external/corefx/src/System.Net.WebSockets.Client/System.Net.WebSockets.Client.sln index 6a66233842..28f4eb342a 100644 --- a/external/corefx/src/System.Net.WebSockets.Client/System.Net.WebSockets.Client.sln +++ b/external/corefx/src/System.Net.WebSockets.Client/System.Net.WebSockets.Client.sln @@ -26,10 +26,10 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7C395A91-D955-444C-98BF-D3F809A56CE1}.Debug|Any CPU.ActiveCfg = netstandard-Windows_NT-Debug|Any CPU - {7C395A91-D955-444C-98BF-D3F809A56CE1}.Debug|Any CPU.Build.0 = netstandard-Windows_NT-Debug|Any CPU - {7C395A91-D955-444C-98BF-D3F809A56CE1}.Release|Any CPU.ActiveCfg = netstandard-Windows_NT-Release|Any CPU - {7C395A91-D955-444C-98BF-D3F809A56CE1}.Release|Any CPU.Build.0 = netstandard-Windows_NT-Release|Any CPU + {7C395A91-D955-444C-98BF-D3F809A56CE1}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {7C395A91-D955-444C-98BF-D3F809A56CE1}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {7C395A91-D955-444C-98BF-D3F809A56CE1}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {7C395A91-D955-444C-98BF-D3F809A56CE1}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU {B8AD98AE-84C3-4313-B3F1-EE8BD5BFF69B}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU {B8AD98AE-84C3-4313-B3F1-EE8BD5BFF69B}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU {B8AD98AE-84C3-4313-B3F1-EE8BD5BFF69B}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU diff --git a/external/corefx/src/System.Net.WebSockets.Client/src/Resources/Strings.resx b/external/corefx/src/System.Net.WebSockets.Client/src/Resources/Strings.resx index 7cb546b8a2..cc93c4738e 100644 --- a/external/corefx/src/System.Net.WebSockets.Client/src/Resources/Strings.resx +++ b/external/corefx/src/System.Net.WebSockets.Client/src/Resources/Strings.resx @@ -1,5 +1,64 @@  + @@ -67,9 +126,6 @@ There is already one outstanding '{0}' call for this WebSocket instance. ReceiveAsync and SendAsync can be called simultaneously, but at most one outstanding operation for each of them is allowed at the same time. - - An internal WebSocket error occurred. Please see the innerException, if present, for more details. - The WebSocket protocol is not supported on this platform. diff --git a/external/corefx/src/System.Net.WebSockets.Client/src/System.Net.WebSockets.Client.csproj b/external/corefx/src/System.Net.WebSockets.Client/src/System.Net.WebSockets.Client.csproj index ef0b4a6ea7..60f70c705f 100644 --- a/external/corefx/src/System.Net.WebSockets.Client/src/System.Net.WebSockets.Client.csproj +++ b/external/corefx/src/System.Net.WebSockets.Client/src/System.Net.WebSockets.Client.csproj @@ -123,6 +123,7 @@ + @@ -142,7 +143,6 @@ - diff --git a/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/ClientWebSocket.cs b/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/ClientWebSocket.cs index 328f4e2fa3..b0409ce728 100644 --- a/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/ClientWebSocket.cs +++ b/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/ClientWebSocket.cs @@ -165,6 +165,12 @@ namespace System.Net.WebSockets return _innerWebSocket.SendAsync(buffer, messageType, endOfMessage, cancellationToken); } + public override Task SendAsync(ReadOnlyMemory buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken) + { + ThrowIfNotConnected(); + return _innerWebSocket.SendAsync(buffer, messageType, endOfMessage, cancellationToken); + } + public override Task ReceiveAsync(ArraySegment buffer, CancellationToken cancellationToken) { @@ -172,6 +178,12 @@ namespace System.Net.WebSockets return _innerWebSocket.ReceiveAsync(buffer, cancellationToken); } + public override ValueTask ReceiveAsync(Memory buffer, CancellationToken cancellationToken) + { + ThrowIfNotConnected(); + return _innerWebSocket.ReceiveAsync(buffer, cancellationToken); + } + public override Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) { diff --git a/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs b/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs index 39d2292754..8405225836 100644 --- a/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs +++ b/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs @@ -62,9 +62,15 @@ namespace System.Net.WebSockets public Task SendAsync(ArraySegment buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken) => _webSocket.SendAsync(buffer, messageType, endOfMessage, cancellationToken); + public Task SendAsync(ReadOnlyMemory buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken) => + _webSocket.SendAsync(buffer, messageType, endOfMessage, cancellationToken); + public Task ReceiveAsync(ArraySegment buffer, CancellationToken cancellationToken) => _webSocket.ReceiveAsync(buffer, cancellationToken); + public ValueTask ReceiveAsync(Memory buffer, CancellationToken cancellationToken) => + _webSocket.ReceiveAsync(buffer, cancellationToken); + public Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken) => _webSocket.CloseAsync(closeStatus, statusDescription, cancellationToken); diff --git a/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Win32.cs b/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Win32.cs index 7824257cdb..d602478f37 100644 --- a/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Win32.cs +++ b/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Win32.cs @@ -16,7 +16,7 @@ using Microsoft.Win32.SafeHandles; namespace System.Net.WebSockets { - internal partial struct WebSocketHandle + internal readonly partial struct WebSocketHandle { private const string WebSocketAvailableApiCheck = "WinHttpWebSocketCompleteUpgrade"; diff --git a/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.WinRT.cs b/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.WinRT.cs index 008d4c042b..6a69057405 100644 --- a/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.WinRT.cs +++ b/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.WinRT.cs @@ -19,7 +19,7 @@ using RTWebSocketError = Windows.Networking.Sockets.WebSocketError; namespace System.Net.WebSockets { - internal partial struct WebSocketHandle + internal readonly partial struct WebSocketHandle { private readonly WinRTWebSocket _webSocket; diff --git a/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Windows.cs b/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Windows.cs index 5962bc3dfd..a7f9d1083d 100644 --- a/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Windows.cs +++ b/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Windows.cs @@ -16,7 +16,7 @@ using Microsoft.Win32.SafeHandles; namespace System.Net.WebSockets { - internal partial struct WebSocketHandle + internal readonly partial struct WebSocketHandle { #region Properties public static bool IsValid(WebSocketHandle handle) @@ -80,6 +80,23 @@ namespace System.Net.WebSockets return _webSocket.SendAsync(buffer, messageType, endOfMessage, cancellationToken); } + public Task SendAsync(ReadOnlyMemory buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken) + { + if (messageType != WebSocketMessageType.Text && messageType != WebSocketMessageType.Binary) + { + string errorMessage = SR.Format( + SR.net_WebSockets_Argument_InvalidMessageType, + nameof(WebSocketMessageType.Close), + nameof(SendAsync), + nameof(WebSocketMessageType.Binary), + nameof(WebSocketMessageType.Text), + nameof(CloseOutputAsync)); + throw new ArgumentException(errorMessage, nameof(messageType)); + } + + return _webSocket.SendAsync(buffer, messageType, endOfMessage, cancellationToken); + } + public Task ReceiveAsync( ArraySegment buffer, CancellationToken cancellationToken) @@ -88,6 +105,9 @@ namespace System.Net.WebSockets return _webSocket.ReceiveAsync(buffer, cancellationToken); } + public ValueTask ReceiveAsync(Memory buffer, CancellationToken cancellationToken) => + _webSocket.ReceiveAsync(buffer, cancellationToken); + public Task CloseAsync( WebSocketCloseStatus closeStatus, string statusDescription, diff --git a/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WinHttpWebSocket.cs b/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WinHttpWebSocket.cs index 4cdf24fe2e..62052b29bb 100644 --- a/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WinHttpWebSocket.cs +++ b/external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WinHttpWebSocket.cs @@ -12,7 +12,7 @@ using System.Threading.Tasks; namespace System.Net.WebSockets { - internal class WinHttpWebSocket : WebSocket + internal sealed class WinHttpWebSocket : WebSocket { #region Constants // TODO (#7893): This code needs to be shared with WinHttpClientHandler @@ -356,7 +356,7 @@ namespace System.Net.WebSockets { _operation.InterlockedCheckValidStates(s_validReceiveStates); - using (CancellationTokenRegistration ctr = ThrowOrRegisterCancellation(cancellationToken)) + using (ThrowOrRegisterCancellation(cancellationToken)) { _operation.PinReceiveBuffer(buffer); @@ -365,15 +365,9 @@ namespace System.Net.WebSockets // Check for abort. _operation.InterlockedCheckValidStates(s_validAfterReceiveStates); - WebSocketMessageType bufferType; bool endOfMessage; - bufferType = WebSocketMessageTypeAdapter.GetWebSocketMessageType(_operation.BufferType, out endOfMessage); - - int bytesTransferred = 0; - checked - { - bytesTransferred = (int)_operation.BytesTransferred; - } + WebSocketMessageType bufferType = WebSocketMessageTypeAdapter.GetWebSocketMessageType(_operation.BufferType, out endOfMessage); + int bytesTransferred = checked((int)_operation.BytesTransferred); WebSocketReceiveResult ret; @@ -391,7 +385,31 @@ namespace System.Net.WebSockets } } - private Task InternalReceiveAsync(ArraySegment buffer) + public override async ValueTask ReceiveAsync(Memory buffer, CancellationToken cancellationToken) + { + _operation.InterlockedCheckValidStates(s_validReceiveStates); + + using (ThrowOrRegisterCancellation(cancellationToken)) + using (buffer.Retain(pin: true)) + { + await InternalReceiveAsync(buffer).ConfigureAwait(false); + + // Check for abort. + _operation.InterlockedCheckValidStates(s_validAfterReceiveStates); + + WebSocketMessageType bufferType = WebSocketMessageTypeAdapter.GetWebSocketMessageType(_operation.BufferType, out bool endOfMessage); + int bytesTransferred = checked((int)_operation.BytesTransferred); + + if (bufferType == WebSocketMessageType.Close) + { + UpdateServerCloseStatus(); + } + + return new ValueWebSocketReceiveResult(bytesTransferred, bufferType, endOfMessage); + } + } + + private Task InternalReceiveAsync(Memory pinnedBuffer) { bool receiveOperationAlreadyPending = false; if (_operation.PendingReadOperation == false) @@ -408,11 +426,19 @@ namespace System.Net.WebSockets uint bytesRead = 0; Interop.WinHttp.WINHTTP_WEB_SOCKET_BUFFER_TYPE winHttpBufferType = 0; + IntPtr pinnedBufferPtr; + unsafe + { + fixed (byte* p = &MemoryMarshal.GetReference(pinnedBuffer.Span)) + { + pinnedBufferPtr = (IntPtr)p; + } + } uint status = Interop.WinHttp.WinHttpWebSocketReceive( _operation.WebSocketHandle, - Marshal.UnsafeAddrOfPinnedArrayElement(buffer.Array, buffer.Offset), - (uint)buffer.Count, + pinnedBufferPtr, + (uint)pinnedBuffer.Length, out bytesRead, // Unused in async mode: ignore. out winHttpBufferType); // Unused in async mode: ignore. diff --git a/external/corefx/src/System.Net.WebSockets.Client/tests/ClientWebSocketOptionsTests.cs b/external/corefx/src/System.Net.WebSockets.Client/tests/ClientWebSocketOptionsTests.cs index cc614458be..3e6615e982 100644 --- a/external/corefx/src/System.Net.WebSockets.Client/tests/ClientWebSocketOptionsTests.cs +++ b/external/corefx/src/System.Net.WebSockets.Client/tests/ClientWebSocketOptionsTests.cs @@ -25,10 +25,10 @@ namespace System.Net.WebSockets.Client.Tests public static bool CanTestClientCertificates => CanTestCertificates && BackendSupportsCustomCertificateHandling; - // Windows 10 Insider Preview Build 16215 introduced the necessary APIs for the UAP version of + // Windows 10 Version 1709 introduced the necessary APIs for the UAP version of // ClientWebSocket.ConnectAsync to carry out mutual TLS authentication. public static bool ClientCertificatesSupported => - !PlatformDetection.IsUap || PlatformDetection.IsWindows10InsiderPreviewBuild16215OrGreater; + !PlatformDetection.IsUap || PlatformDetection.IsWindows10Version1709OrGreater; public ClientWebSocketOptionsTests(ITestOutputHelper output) : base(output) { } diff --git a/external/corefx/src/System.Net.WebSockets.Client/tests/Configurations.props b/external/corefx/src/System.Net.WebSockets.Client/tests/Configurations.props index f333d20c1a..eddfd3a9ac 100644 --- a/external/corefx/src/System.Net.WebSockets.Client/tests/Configurations.props +++ b/external/corefx/src/System.Net.WebSockets.Client/tests/Configurations.props @@ -4,6 +4,8 @@ netstandard-Windows_NT; netstandard-Unix; + netcoreapp-Windows_NT; + netcoreapp-Unix; \ No newline at end of file diff --git a/external/corefx/src/System.Net.WebSockets.Client/tests/ConnectTest.cs b/external/corefx/src/System.Net.WebSockets.Client/tests/ConnectTest.cs index f41b3ffc9a..8f75de2a23 100644 --- a/external/corefx/src/System.Net.WebSockets.Client/tests/ConnectTest.cs +++ b/external/corefx/src/System.Net.WebSockets.Client/tests/ConnectTest.cs @@ -87,9 +87,11 @@ namespace System.Net.WebSockets.Client.Tests [ActiveIssue(18784, TargetFrameworkMonikers.NetFramework)] [OuterLoop] - [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoHeadersServers))] - public async Task ConnectAsync_AddHostHeader_Success(Uri server) + [ConditionalTheory(nameof(WebSocketsSupported))] + public async Task ConnectAsync_AddHostHeader_Success() { + Uri server = System.Net.Test.Common.Configuration.WebSockets.RemoteEchoServer; + // Send via the physical address such as "corefx-net.cloudapp.net" // Set the Host header to logical address like "subdomain.corefx-net.cloudapp.net" // Verify the scenario works and the remote server received "Host: subdomain.corefx-net.cloudapp.net" diff --git a/external/corefx/src/System.Net.WebSockets.Client/tests/SendReceiveTest.cs b/external/corefx/src/System.Net.WebSockets.Client/tests/SendReceiveTest.cs index ec7cc95415..82ec689f2e 100644 --- a/external/corefx/src/System.Net.WebSockets.Client/tests/SendReceiveTest.cs +++ b/external/corefx/src/System.Net.WebSockets.Client/tests/SendReceiveTest.cs @@ -14,8 +14,22 @@ using Xunit.Abstractions; namespace System.Net.WebSockets.Client.Tests { - public class SendReceiveTest : ClientWebSocketTestBase + public sealed class ArraySegmentSendReceiveTest : SendReceiveTest { + public ArraySegmentSendReceiveTest(ITestOutputHelper output) : base(output) { } + + protected override Task ReceiveAsync(WebSocket ws, ArraySegment arraySegment, CancellationToken cancellationToken) => + ws.ReceiveAsync(arraySegment, cancellationToken); + + protected override Task SendAsync(WebSocket ws, ArraySegment arraySegment, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken) => + ws.SendAsync(arraySegment, messageType, endOfMessage, cancellationToken); + } + + public abstract class SendReceiveTest : ClientWebSocketTestBase + { + protected abstract Task SendAsync(WebSocket ws, ArraySegment arraySegment, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken); + protected abstract Task ReceiveAsync(WebSocket ws, ArraySegment arraySegment, CancellationToken cancellationToken); + public static bool PartialMessagesSupported => PlatformDetection.ClientWebSocketPartialMessagesSupported; public SendReceiveTest(ITestOutputHelper output) : base(output) { } @@ -40,16 +54,16 @@ namespace System.Net.WebSockets.Client.Tests // as we read them until we read the complete message payload. for (int i = 0; i < 63; i++) { - await cws.SendAsync(sendSegment, WebSocketMessageType.Binary, false, ctsDefault.Token); + await SendAsync(cws, sendSegment, WebSocketMessageType.Binary, false, ctsDefault.Token); } - await cws.SendAsync(sendSegment, WebSocketMessageType.Binary, true, ctsDefault.Token); + await SendAsync(cws, sendSegment, WebSocketMessageType.Binary, true, ctsDefault.Token); - WebSocketReceiveResult recvResult = await cws.ReceiveAsync(receiveSegment, ctsDefault.Token); + WebSocketReceiveResult recvResult = await ReceiveAsync(cws, receiveSegment, ctsDefault.Token); Assert.Equal(false, recvResult.EndOfMessage); while (recvResult.EndOfMessage == false) { - recvResult = await cws.ReceiveAsync(receiveSegment, ctsDefault.Token); + recvResult = await ReceiveAsync(cws, receiveSegment, ctsDefault.Token); } await cws.CloseAsync(WebSocketCloseStatus.NormalClosure, "PartialMessageDueToSmallReceiveBufferTest", ctsDefault.Token); @@ -75,13 +89,14 @@ namespace System.Net.WebSockets.Client.Tests // Send data to the server; the server will reply back with one or more partial messages. We should be // able to consume that data as it arrives, without having to wait for "end of message" to be signaled. - await cws.SendAsync(sendSegment, WebSocketMessageType.Binary, true, ctsDefault.Token); + await SendAsync(cws, sendSegment, WebSocketMessageType.Binary, true, ctsDefault.Token); int totalBytesReceived = 0; var receiveBuffer = new byte[sendBuffer.Length]; while (totalBytesReceived < receiveBuffer.Length) { - WebSocketReceiveResult recvResult = await cws.ReceiveAsync( + WebSocketReceiveResult recvResult = await ReceiveAsync( + cws, new ArraySegment(receiveBuffer, totalBytesReceived, receiveBuffer.Length - totalBytesReceived), ctsDefault.Token); @@ -118,7 +133,7 @@ namespace System.Net.WebSockets.Client.Tests AssertExtensions.Throws("messageType", () => { - Task t = cws.SendAsync(new ArraySegment(), WebSocketMessageType.Close, true, cts.Token); + Task t = SendAsync(cws, new ArraySegment(), WebSocketMessageType.Close, true, cts.Token); }); Assert.Equal(WebSocketState.Open, cws.State); @@ -139,7 +154,8 @@ namespace System.Net.WebSockets.Client.Tests { for (int i = 0; i < tasks.Length; i++) { - tasks[i] = cws.SendAsync( + tasks[i] = SendAsync( + cws, WebSocketData.GetBufferFromText("hello"), WebSocketMessageType.Text, true, @@ -193,7 +209,8 @@ namespace System.Net.WebSockets.Client.Tests Task[] tasks = new Task[2]; - await cws.SendAsync( + await SendAsync( + cws, WebSocketData.GetBufferFromText(".delay5sec"), WebSocketMessageType.Text, true, @@ -206,7 +223,7 @@ namespace System.Net.WebSockets.Client.Tests { for (int i = 0; i < tasks.Length; i++) { - tasks[i] = cws.ReceiveAsync(recvSegment, cts.Token); + tasks[i] = ReceiveAsync(cws, recvSegment, cts.Token); } Task.WaitAll(tasks); @@ -257,21 +274,24 @@ namespace System.Net.WebSockets.Client.Tests { var cts = new CancellationTokenSource(TimeOutMilliseconds); string message = "hello"; - await cws.SendAsync( - WebSocketData.GetBufferFromText(message), - WebSocketMessageType.Text, - false, - cts.Token); + await SendAsync( + cws, + WebSocketData.GetBufferFromText(message), + WebSocketMessageType.Text, + false, + cts.Token); Assert.Equal(WebSocketState.Open, cws.State); - await cws.SendAsync(new ArraySegment(new byte[0]), - WebSocketMessageType.Text, - true, - cts.Token); + await SendAsync( + cws, + new ArraySegment(new byte[0]), + WebSocketMessageType.Text, + true, + cts.Token); Assert.Equal(WebSocketState.Open, cws.State); var recvBuffer = new byte[100]; var receiveSegment = new ArraySegment(recvBuffer); - WebSocketReceiveResult recvRet = await cws.ReceiveAsync(receiveSegment, cts.Token); + WebSocketReceiveResult recvRet = await ReceiveAsync(cws, receiveSegment, cts.Token); Assert.Equal(WebSocketState.Open, cws.State); Assert.Equal(message.Length, recvRet.Count); @@ -301,13 +321,14 @@ namespace System.Net.WebSockets.Client.Tests { byte[] sendBuffer = new byte[bufferSize]; rand.NextBytes(sendBuffer); - await cws.SendAsync(new ArraySegment(sendBuffer), WebSocketMessageType.Binary, true, ctsDefault.Token); + await SendAsync(cws, new ArraySegment(sendBuffer), WebSocketMessageType.Binary, true, ctsDefault.Token); byte[] receiveBuffer = new byte[bufferSize]; int totalReceived = 0; while (true) { - WebSocketReceiveResult recvResult = await cws.ReceiveAsync( + WebSocketReceiveResult recvResult = await ReceiveAsync( + cws, new ArraySegment(receiveBuffer, totalReceived, receiveBuffer.Length - totalReceived), ctsDefault.Token); @@ -342,8 +363,8 @@ namespace System.Net.WebSockets.Client.Tests for (int i = 0; i < sendBuffer.Length; i++) { - Task receive = cws.ReceiveAsync(new ArraySegment(receiveBuffer, receiveBuffer.Length - i - 1, 1), ctsDefault.Token); - Task send = cws.SendAsync(new ArraySegment(sendBuffer, i, 1), WebSocketMessageType.Binary, true, ctsDefault.Token); + Task receive = ReceiveAsync(cws, new ArraySegment(receiveBuffer, receiveBuffer.Length - i - 1, 1), ctsDefault.Token); + Task send = SendAsync(cws, new ArraySegment(sendBuffer, i, 1), WebSocketMessageType.Binary, true, ctsDefault.Token); await Task.WhenAll(receive, send); Assert.Equal(1, receive.Result.Count); } @@ -387,7 +408,7 @@ namespace System.Net.WebSockets.Client.Tests // Post a pending ReceiveAsync before the TCP connection is torn down. var recvBuffer = new byte[100]; var recvSegment = new ArraySegment(recvBuffer); - Task pendingReceiveAsync = clientSocket.ReceiveAsync(recvSegment, cts.Token); + Task pendingReceiveAsync = ReceiveAsync(clientSocket, recvSegment, cts.Token); pendingReceiveAsyncPosted.Set(); // Wait for the server to close the underlying connection. @@ -402,7 +423,7 @@ namespace System.Net.WebSockets.Client.Tests Assert.Equal(WebSocketError.ConnectionClosedPrematurely, pendingReceiveException.WebSocketErrorCode); WebSocketException newReceiveException = - await Assert.ThrowsAsync(() => clientSocket.ReceiveAsync(recvSegment, cts.Token)); + await Assert.ThrowsAsync(() => ReceiveAsync(clientSocket, recvSegment, cts.Token)); Assert.Equal(WebSocketError.ConnectionClosedPrematurely, newReceiveException.WebSocketErrorCode); Assert.Equal(WebSocketState.Open, clientSocket.State); @@ -416,7 +437,7 @@ namespace System.Net.WebSockets.Client.Tests Assert.Equal(WebSocketError.ConnectionClosedPrematurely, pendingReceiveException.WebSocketErrorCode); WebSocketException newReceiveException = - await Assert.ThrowsAsync(() => clientSocket.ReceiveAsync(recvSegment, cts.Token)); + await Assert.ThrowsAsync(() => ReceiveAsync(clientSocket, recvSegment, cts.Token)); Assert.Equal(WebSocketError.Success, newReceiveException.WebSocketErrorCode); Assert.Equal( ResourceHelper.GetExceptionMessage("net_WebSockets_InvalidState", "Aborted", "Open, CloseSent"), @@ -437,7 +458,7 @@ namespace System.Net.WebSockets.Client.Tests Assert.Equal(WININET_E_CONNECTION_ABORTED, (uint)pendingReceiveException.InnerException.HResult); WebSocketException newReceiveException = - await Assert.ThrowsAsync(() => clientSocket.ReceiveAsync(recvSegment, cts.Token)); + await Assert.ThrowsAsync(() => ReceiveAsync(clientSocket, recvSegment, cts.Token)); Assert.Equal(WebSocketError.Success, newReceiveException.WebSocketErrorCode); Assert.Equal( ResourceHelper.GetExceptionMessage("net_WebSockets_InvalidState", "Aborted", "Open, CloseSent"), @@ -456,7 +477,7 @@ namespace System.Net.WebSockets.Client.Tests Assert.Equal(WININET_E_CONNECTION_RESET, (uint)pendingReceiveException.HResult); Win32Exception newReceiveException = - await Assert.ThrowsAnyAsync(() => clientSocket.ReceiveAsync(recvSegment, cts.Token)); + await Assert.ThrowsAnyAsync(() => ReceiveAsync(clientSocket, recvSegment, cts.Token)); Assert.Equal(WININET_E_CONNECTION_RESET, (uint)newReceiveException.HResult); Assert.Equal(WebSocketState.Open, clientSocket.State); diff --git a/external/corefx/src/System.Net.WebSockets.Client/tests/SendReceiveTest.netcoreapp.cs b/external/corefx/src/System.Net.WebSockets.Client/tests/SendReceiveTest.netcoreapp.cs new file mode 100644 index 0000000000..4c7759cba0 --- /dev/null +++ b/external/corefx/src/System.Net.WebSockets.Client/tests/SendReceiveTest.netcoreapp.cs @@ -0,0 +1,31 @@ +// 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.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; +using Xunit.Abstractions; + +namespace System.Net.WebSockets.Client.Tests +{ + public sealed class MemorySendReceiveTest : SendReceiveTest + { + public MemorySendReceiveTest(ITestOutputHelper output) : base(output) { } + + protected override async Task ReceiveAsync(WebSocket ws, ArraySegment arraySegment, CancellationToken cancellationToken) + { + ValueWebSocketReceiveResult r = await ws.ReceiveAsync( + arraySegment == default(ArraySegment) ? Memory.Empty : (Memory)arraySegment, + cancellationToken); + return new WebSocketReceiveResult(r.Count, r.MessageType, r.EndOfMessage, ws.CloseStatus, ws.CloseStatusDescription); + } + + protected override Task SendAsync(WebSocket ws, ArraySegment arraySegment, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken) => + ws.SendAsync( + arraySegment == default(ArraySegment) ? ReadOnlyMemory.Empty : (ReadOnlyMemory)arraySegment, + messageType, + endOfMessage, + cancellationToken); + } +} diff --git a/external/corefx/src/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj b/external/corefx/src/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj index a29a05b5c5..206d82430f 100644 --- a/external/corefx/src/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj +++ b/external/corefx/src/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj @@ -10,6 +10,10 @@ + + + + @@ -47,6 +51,7 @@ + @@ -55,4 +60,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/System.Net.WebSockets.WebSocketProtocol.sln b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/System.Net.WebSockets.WebSocketProtocol.sln new file mode 100644 index 0000000000..74e571f6e3 --- /dev/null +++ b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/System.Net.WebSockets.WebSocketProtocol.sln @@ -0,0 +1,55 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27214.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Net.WebSockets.WebSocketProtocol", "src\System.Net.WebSockets.WebSocketProtocol.csproj", "{747BE014-7C1D-4460-95AF-B41C35717165}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D5A36F24-E27C-43DF-8658-10C5290423E0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{9D10D7AD-2F8C-4F7A-B0FA-E5EB3ECE287F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Net.WebSockets.WebSocketProtocol", "ref\System.Net.WebSockets.WebSocketProtocol.csproj", "{4EDBE9F5-D10A-4553-AE24-2E0E946B15B0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{F47347C3-433C-4D3B-90F9-487FE7F4F444}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Net.WebSockets.WebSocketProtocol.Tests", "tests\System.Net.WebSockets.WebSocketProtocol.Tests.csproj", "{CF73547B-07D2-4290-A14A-CA2A354F4D21}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + netstandard-Debug|Any CPU = netstandard-Debug|Any CPU + netstandard-Release|Any CPU = netstandard-Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {747BE014-7C1D-4460-95AF-B41C35717165}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {747BE014-7C1D-4460-95AF-B41C35717165}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {747BE014-7C1D-4460-95AF-B41C35717165}.netstandard-Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {747BE014-7C1D-4460-95AF-B41C35717165}.netstandard-Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {747BE014-7C1D-4460-95AF-B41C35717165}.netstandard-Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {747BE014-7C1D-4460-95AF-B41C35717165}.netstandard-Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {4EDBE9F5-D10A-4553-AE24-2E0E946B15B0}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {4EDBE9F5-D10A-4553-AE24-2E0E946B15B0}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {4EDBE9F5-D10A-4553-AE24-2E0E946B15B0}.netstandard-Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {4EDBE9F5-D10A-4553-AE24-2E0E946B15B0}.netstandard-Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {4EDBE9F5-D10A-4553-AE24-2E0E946B15B0}.netstandard-Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {4EDBE9F5-D10A-4553-AE24-2E0E946B15B0}.netstandard-Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {CF73547B-07D2-4290-A14A-CA2A354F4D21}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {CF73547B-07D2-4290-A14A-CA2A354F4D21}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {CF73547B-07D2-4290-A14A-CA2A354F4D21}.netstandard-Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {CF73547B-07D2-4290-A14A-CA2A354F4D21}.netstandard-Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {CF73547B-07D2-4290-A14A-CA2A354F4D21}.netstandard-Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {CF73547B-07D2-4290-A14A-CA2A354F4D21}.netstandard-Release|Any CPU.Build.0 = netstandard-Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {747BE014-7C1D-4460-95AF-B41C35717165} = {D5A36F24-E27C-43DF-8658-10C5290423E0} + {4EDBE9F5-D10A-4553-AE24-2E0E946B15B0} = {9D10D7AD-2F8C-4F7A-B0FA-E5EB3ECE287F} + {CF73547B-07D2-4290-A14A-CA2A354F4D21} = {F47347C3-433C-4D3B-90F9-487FE7F4F444} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6618B298-BA53-4E58-9795-756C6DC1BA38} + EndGlobalSection +EndGlobal diff --git a/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/dir.props b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/dir.props new file mode 100644 index 0000000000..4356decc45 --- /dev/null +++ b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/dir.props @@ -0,0 +1,8 @@ + + + + + 4.0.0.0 + Open + + \ No newline at end of file diff --git a/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/pkg/System.Net.WebSockets.WebSocketProtocol.pkgproj b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/pkg/System.Net.WebSockets.WebSocketProtocol.pkgproj new file mode 100644 index 0000000000..44ceae2df0 --- /dev/null +++ b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/pkg/System.Net.WebSockets.WebSocketProtocol.pkgproj @@ -0,0 +1,11 @@ + + + + + + net461;netcoreapp2.0;$(AllXamarinFrameworks) + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/ref/Configurations.props b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/ref/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/ref/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/ref/System.Net.WebSockets.WebSocketProtocol.cs b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/ref/System.Net.WebSockets.WebSocketProtocol.cs new file mode 100644 index 0000000000..b66dd7b603 --- /dev/null +++ b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/ref/System.Net.WebSockets.WebSocketProtocol.cs @@ -0,0 +1,16 @@ +// 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. +// ------------------------------------------------------------------------------ +// Changes to this file must follow the http://aka.ms/api-review process. +// ------------------------------------------------------------------------------ + +using System.IO; + +namespace System.Net.WebSockets +{ + public static class WebSocketProtocol + { + public static WebSocket CreateFromStream(Stream stream, bool isServer, string subProtocol, TimeSpan keepAliveInterval, Memory buffer = default) { throw null; } + } +} diff --git a/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/ref/System.Net.WebSockets.WebSocketProtocol.csproj b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/ref/System.Net.WebSockets.WebSocketProtocol.csproj new file mode 100644 index 0000000000..702a9a5a3a --- /dev/null +++ b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/ref/System.Net.WebSockets.WebSocketProtocol.csproj @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/src/Configurations.props b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/src/Configurations.props new file mode 100644 index 0000000000..77a4b65bc9 --- /dev/null +++ b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/src/Configurations.props @@ -0,0 +1,9 @@ + + + + + netstandard; + netcoreapp; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/src/Resources/Strings.resx b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/src/Resources/Strings.resx new file mode 100644 index 0000000000..e9cc0e2ee7 --- /dev/null +++ b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/src/Resources/Strings.resx @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Empty string is not a valid subprotocol value. Please use \"null\" to specify no value. + + + The WebSocket is in an invalid state ('{0}') for this operation. Valid states are: '{1}' + + + The WebSocket protocol '{0}' is invalid because it contains the invalid character '{1}'. + + + The close status description '{0}' is invalid. When using close status code '{1}' the description must be null. + + + The close status code '{0}' is reserved for system use only and cannot be specified when calling this method. + + + The close status description '{0}' is too long. The UTF8-representation of the status description must not be longer than {1} bytes. + + + The WebSocket protocol is not supported on this platform. + + + The message type '{0}' is not allowed for the '{1}' operation. Valid message types are: '{2}, {3}'. To close the WebSocket, use the '{4}' operation instead. + + + There is already one outstanding '{0}' call for this WebSocket instance. ReceiveAsync and SendAsync can be called simultaneously, but at most one outstanding operation for each of them is allowed at the same time. + + + The base stream is not readable. + + + The base stream is not writeable. + + + The argument must be a value greater than {0}. + + diff --git a/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/src/System.Net.WebSockets.WebSocketProtocol.csproj b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/src/System.Net.WebSockets.WebSocketProtocol.csproj new file mode 100644 index 0000000000..ae63f2d5f0 --- /dev/null +++ b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/src/System.Net.WebSockets.WebSocketProtocol.csproj @@ -0,0 +1,45 @@ + + + + + System.Net.WebSockets.WebSocketProtocol + True + {747BE014-7C1D-4460-95AF-B41C35717165} + + + + + + + + Common\System\Net\WebSockets\ManagedWebSocket.cs + + + Common\System\Net\WebSockets\WebSocketValidate.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/src/System/Net/WebSockets/ManagedWebSocket.netstandard.cs b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/src/System/Net/WebSockets/ManagedWebSocket.netstandard.cs new file mode 100644 index 0000000000..3b71f388e3 --- /dev/null +++ b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/src/System/Net/WebSockets/ManagedWebSocket.netstandard.cs @@ -0,0 +1,24 @@ +// 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.Threading; +using System.Threading.Tasks; + +namespace System.Net.WebSockets +{ + internal sealed partial class ManagedWebSocket : WebSocket + { + private Task ValidateAndReceiveAsync(Task receiveTask, byte[] buffer, CancellationToken cancellationToken) + { + if (receiveTask == null || + (receiveTask.Status == TaskStatus.RanToCompletion && + !(receiveTask is Task wsrr && wsrr.Result.MessageType == WebSocketMessageType.Close))) + { + receiveTask = ReceiveAsyncPrivate(new ArraySegment(buffer), cancellationToken).AsTask(); + } + + return receiveTask; + } + } +} diff --git a/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/src/System/Net/WebSockets/ManagedWebSocketExtensions.cs b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/src/System/Net/WebSockets/ManagedWebSocketExtensions.cs new file mode 100644 index 0000000000..3f2143990e --- /dev/null +++ b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/src/System/Net/WebSockets/ManagedWebSocketExtensions.cs @@ -0,0 +1,62 @@ +// 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.Buffers; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.WebSockets +{ + internal static class ManagedWebSocketExtensions + { + internal static unsafe string GetString(this UTF8Encoding encoding, Span bytes) + { + fixed (byte* b = &MemoryMarshal.GetReference(bytes)) + { + return encoding.GetString(b, bytes.Length); + } + } + + internal static Task ReadAsync(this Stream stream, Memory destination, CancellationToken cancellationToken) + { + if (destination.TryGetArray(out ArraySegment array)) + { + return stream.ReadAsync(array.Array, array.Offset, array.Count, cancellationToken); + } + else + { + byte[] buffer = ArrayPool.Shared.Rent(destination.Length); + return FinishReadAsync(stream.ReadAsync(buffer, 0, destination.Length, cancellationToken), buffer, destination); + + async Task FinishReadAsync(Task readTask, byte[] localBuffer, Memory localDestination) + { + try + { + int result = await readTask.ConfigureAwait(false); + new Span(localBuffer, 0, result).CopyTo(localDestination.Span); + return result; + } + finally + { + ArrayPool.Shared.Return(localBuffer); + } + } + } + } + } + + internal static class BitConverter + { + internal static unsafe int ToInt32(ReadOnlySpan value) + { + Debug.Assert(value.Length >= sizeof(int)); + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(value)); + } + } +} diff --git a/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/src/System/Net/WebSockets/WebSocketProtocol.cs b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/src/System/Net/WebSockets/WebSocketProtocol.cs new file mode 100644 index 0000000000..e4756eb603 --- /dev/null +++ b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/src/System/Net/WebSockets/WebSocketProtocol.cs @@ -0,0 +1,44 @@ +// 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.IO; +using System.Threading; + +namespace System.Net.WebSockets +{ + public static class WebSocketProtocol + { + public static WebSocket CreateFromStream( + Stream stream, + bool isServer, + string subProtocol, + TimeSpan keepAliveInterval, + Memory buffer = default) + { + if (stream == null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (!stream.CanRead || !stream.CanWrite) + { + throw new ArgumentException(!stream.CanRead ? SR.NotReadableStream : SR.NotWriteableStream, nameof(stream)); + } + + if (subProtocol != null) + { + WebSocketValidate.ValidateSubprotocol(subProtocol); + } + + if (keepAliveInterval != Timeout.InfiniteTimeSpan && keepAliveInterval < TimeSpan.Zero) + { + throw new ArgumentOutOfRangeException(nameof(keepAliveInterval), keepAliveInterval, + SR.Format(SR.net_WebSockets_ArgumentOutOfRange_TooSmall, + 0)); + } + + return ManagedWebSocket.CreateFromConnectedStream(stream, isServer, subProtocol, keepAliveInterval, buffer); + } + } +} diff --git a/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/tests/Configurations.props b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/tests/Configurations.props new file mode 100644 index 0000000000..77a4b65bc9 --- /dev/null +++ b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/tests/Configurations.props @@ -0,0 +1,9 @@ + + + + + netstandard; + netcoreapp; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/tests/System.Net.WebSockets.WebSocketProtocol.Tests.csproj b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/tests/System.Net.WebSockets.WebSocketProtocol.Tests.csproj new file mode 100644 index 0000000000..f0ac7ecbac --- /dev/null +++ b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/tests/System.Net.WebSockets.WebSocketProtocol.Tests.csproj @@ -0,0 +1,22 @@ + + + + + + + {CF73547B-07D2-4290-A14A-CA2A354F4D21} + + + + Common\System\Net\Configuration.cs + + + Common\System\Net\Configuration.WebSockets.cs + + + Common\System\Net\HttpKnownHeaderNames.cs + + + + + diff --git a/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/tests/WebSocketProtocolTests.cs b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/tests/WebSocketProtocolTests.cs new file mode 100644 index 0000000000..2b5964ed66 --- /dev/null +++ b/external/corefx/src/System.Net.WebSockets.WebSocketProtocol/tests/WebSocketProtocolTests.cs @@ -0,0 +1,165 @@ +// 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.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Net.Sockets; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace System.Net.WebSockets.Tests +{ + using Configuration = System.Net.Test.Common.Configuration; + + public sealed class WebSocketProtocolTests + { + [Fact] + public void CreateFromStream_InvalidArguments_Throws() + { + AssertExtensions.Throws("stream", + () => WebSocketProtocol.CreateFromStream(null, true, "subProtocol", TimeSpan.FromSeconds(30), default(Memory))); + AssertExtensions.Throws("stream", + () => WebSocketProtocol.CreateFromStream(new MemoryStream(new byte[100], writable: false), true, "subProtocol", TimeSpan.FromSeconds(30), default(Memory))); + AssertExtensions.Throws("stream", + () => WebSocketProtocol.CreateFromStream(new UnreadableStream(), true, "subProtocol", TimeSpan.FromSeconds(30), default(Memory))); + + AssertExtensions.Throws("subProtocol", + () => WebSocketProtocol.CreateFromStream(new MemoryStream(), true, " ", TimeSpan.FromSeconds(30), default(Memory))); + AssertExtensions.Throws("subProtocol", + () => WebSocketProtocol.CreateFromStream(new MemoryStream(), true, "\xFF", TimeSpan.FromSeconds(30), default(Memory))); + + AssertExtensions.Throws("keepAliveInterval", () => + WebSocketProtocol.CreateFromStream(new MemoryStream(), true, "subProtocol", TimeSpan.FromSeconds(-2), default(Memory))); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(14)] + [InlineData(4096)] + public void CreateFromStream_ValidBufferSizes_Succeed(int bufferSize) + { + Assert.NotNull(WebSocketProtocol.CreateFromStream(new MemoryStream(), false, null, Timeout.InfiniteTimeSpan, new Memory(new byte[bufferSize]))); + Assert.NotNull(WebSocketProtocol.CreateFromStream(new MemoryStream(), true, null, Timeout.InfiniteTimeSpan, new Memory(new byte[bufferSize]))); + } + + [OuterLoop] // Connects to external server. + [Theory] + [MemberData(nameof(EchoServers))] + public async Task WebSocketProtocol_CreateFromConnectedStream_Succeeds(Uri echoUri) + { + Uri uri = new UriBuilder(echoUri) { Scheme = (echoUri.Scheme == "ws") ? "http" : "https" }.Uri; + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, uri); + KeyValuePair secKeyAndSecWebSocketAccept = CreateSecKeyAndSecWebSocketAccept(); + AddWebSocketHeaders(request, secKeyAndSecWebSocketAccept.Key); + DirectManagedHttpClientHandler handler = DirectManagedHttpClientHandler.CreateHandler(); + using (HttpResponseMessage response = await handler.SendAsync(request, CancellationToken.None).ConfigureAwait(false)) + { + Assert.Equal(HttpStatusCode.SwitchingProtocols, response.StatusCode); + using (Stream connectedStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) + { + Assert.True(connectedStream.CanRead); + Assert.True(connectedStream.CanWrite); + using (WebSocket socket = WebSocketProtocol.CreateFromStream(connectedStream, false, null, TimeSpan.FromSeconds(10))) + { + Assert.NotNull(socket); + Assert.Equal(WebSocketState.Open, socket.State); + + string expected = "Hello World!"; + ArraySegment buffer = new ArraySegment(Encoding.UTF8.GetBytes(expected)); + await socket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None); + + buffer = new ArraySegment(new byte[buffer.Count]); + await socket.ReceiveAsync(buffer, CancellationToken.None); + + Assert.Equal(expected, Encoding.UTF8.GetString(buffer.Array)); + } + } + } + } + + public static readonly object[][] EchoServers = System.Net.Test.Common.Configuration.WebSockets.EchoServers; + + /// GUID appended by the server as part of the security key response. Defined in the RFC. + private const string WSServerGuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + + private static KeyValuePair CreateSecKeyAndSecWebSocketAccept() + { + string secKey = Convert.ToBase64String(Guid.NewGuid().ToByteArray()); + using (SHA1 sha = SHA1.Create()) + { + return new KeyValuePair( + secKey, + Convert.ToBase64String(sha.ComputeHash(Encoding.ASCII.GetBytes(secKey + WSServerGuid)))); + } + } + + private static void AddWebSocketHeaders(HttpRequestMessage request, string secKey) + { + request.Headers.TryAddWithoutValidation(HttpKnownHeaderNames.Connection, HttpKnownHeaderNames.Upgrade); + request.Headers.TryAddWithoutValidation(HttpKnownHeaderNames.Upgrade, "websocket"); + request.Headers.TryAddWithoutValidation(HttpKnownHeaderNames.SecWebSocketVersion, "13"); + request.Headers.TryAddWithoutValidation(HttpKnownHeaderNames.SecWebSocketKey, secKey); + } + + private sealed class UnreadableStream : Stream + { + public override bool CanRead => false; + public override bool CanSeek => true; + public override bool CanWrite => true; + public override long Length => throw new NotImplementedException(); + public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public override void Flush() => throw new NotImplementedException(); + public override int Read(byte[] buffer, int offset, int count) => throw new NotImplementedException(); + public override long Seek(long offset, SeekOrigin origin) => throw new NotImplementedException(); + public override void SetLength(long value) => throw new NotImplementedException(); + public override void Write(byte[] buffer, int offset, int count) => throw new NotImplementedException(); + } + + private sealed class DirectManagedHttpClientHandler : HttpClientHandler + { + private const string ManagedHandlerEnvVar = "COMPlus_UseManagedHttpClientHandler"; + private static readonly LocalDataStoreSlot s_managedHandlerSlot = GetSlot(); + private static readonly object s_true = true; + + private static LocalDataStoreSlot GetSlot() + { + LocalDataStoreSlot slot = Thread.GetNamedDataSlot(ManagedHandlerEnvVar); + if (slot != null) + { + return slot; + } + + try + { + return Thread.AllocateNamedDataSlot(ManagedHandlerEnvVar); + } + catch (ArgumentException) // in case of a race condition where multiple threads all try to allocate the slot concurrently + { + return Thread.GetNamedDataSlot(ManagedHandlerEnvVar); + } + } + + public static DirectManagedHttpClientHandler CreateHandler() + { + Thread.SetData(s_managedHandlerSlot, s_true); + try + { + return new DirectManagedHttpClientHandler(); + } + finally { Thread.SetData(s_managedHandlerSlot, null); } + } + + public new Task SendAsync( + HttpRequestMessage request, CancellationToken cancellationToken) => + base.SendAsync(request, cancellationToken); + } + } +} diff --git a/external/corefx/src/System.Net.WebSockets/ref/System.Net.WebSockets.cs b/external/corefx/src/System.Net.WebSockets/ref/System.Net.WebSockets.cs index be135ee0c1..924dc869fe 100644 --- a/external/corefx/src/System.Net.WebSockets/ref/System.Net.WebSockets.cs +++ b/external/corefx/src/System.Net.WebSockets/ref/System.Net.WebSockets.cs @@ -5,33 +5,43 @@ // Changes to this file must follow the http://aka.ms/api-review process. // ------------------------------------------------------------------------------ - namespace System.Net.WebSockets { + public readonly partial struct ValueWebSocketReceiveResult + { + private readonly int _dummy; + public ValueWebSocketReceiveResult(int count, System.Net.WebSockets.WebSocketMessageType messageType, bool endOfMessage) { throw null; } + public int Count { get { throw null; } } + public bool EndOfMessage { get { throw null; } } + public System.Net.WebSockets.WebSocketMessageType MessageType { get { throw null; } } + } public abstract partial class WebSocket : System.IDisposable { protected WebSocket() { } - public static System.TimeSpan DefaultKeepAliveInterval { get { throw null; } } public abstract System.Nullable CloseStatus { get; } public abstract string CloseStatusDescription { get; } + public static System.TimeSpan DefaultKeepAliveInterval { get { throw null; } } public abstract System.Net.WebSockets.WebSocketState State { get; } public abstract string SubProtocol { get; } public abstract void Abort(); public abstract System.Threading.Tasks.Task CloseAsync(System.Net.WebSockets.WebSocketCloseStatus closeStatus, string statusDescription, System.Threading.CancellationToken cancellationToken); public abstract System.Threading.Tasks.Task CloseOutputAsync(System.Net.WebSockets.WebSocketCloseStatus closeStatus, string statusDescription, System.Threading.CancellationToken cancellationToken); public static System.ArraySegment CreateClientBuffer(int receiveBufferSize, int sendBufferSize) { throw null; } - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] public static System.Net.WebSockets.WebSocket CreateClientWebSocket(System.IO.Stream innerStream, string subProtocol, int receiveBufferSize, int sendBufferSize, System.TimeSpan keepAliveInterval, bool useZeroMaskingKey, System.ArraySegment internalBuffer) { throw null; } + public static System.Net.WebSockets.WebSocket CreateFromStream(System.IO.Stream stream, bool isServer, string subProtocol, System.TimeSpan keepAliveInterval, System.Memory buffer=default(System.Memory)) { throw null; } public static System.ArraySegment CreateServerBuffer(int receiveBufferSize) { throw null; } public abstract void Dispose(); - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] - [System.Obsolete("This API supports the .NET Framework infrastructure and is not intended to be used directly from your code.")] + [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] + [System.ObsoleteAttribute("This API supports the .NET Framework infrastructure and is not intended to be used directly from your code.")] public static bool IsApplicationTargeting45() { throw null; } protected static bool IsStateTerminal(System.Net.WebSockets.WebSocketState state) { throw null; } public abstract System.Threading.Tasks.Task ReceiveAsync(System.ArraySegment buffer, System.Threading.CancellationToken cancellationToken); - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Threading.Tasks.ValueTask ReceiveAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] public static void RegisterPrefixes() { } public abstract System.Threading.Tasks.Task SendAsync(System.ArraySegment buffer, System.Net.WebSockets.WebSocketMessageType messageType, bool endOfMessage, System.Threading.CancellationToken cancellationToken); + public virtual System.Threading.Tasks.Task SendAsync(System.ReadOnlyMemory buffer, System.Net.WebSockets.WebSocketMessageType messageType, bool endOfMessage, System.Threading.CancellationToken cancellationToken) { throw null; } protected static void ThrowOnInvalidState(System.Net.WebSockets.WebSocketState state, params System.Net.WebSockets.WebSocketState[] validStates) { } } public enum WebSocketCloseStatus @@ -47,19 +57,19 @@ namespace System.Net.WebSockets PolicyViolation = 1008, ProtocolError = 1002, } - public abstract class WebSocketContext + public abstract partial class WebSocketContext { - public abstract System.Uri RequestUri { get; } - public abstract System.Collections.Specialized.NameValueCollection Headers { get; } - public abstract string Origin { get; } - public abstract System.Collections.Generic.IEnumerable SecWebSocketProtocols { get; } - public abstract string SecWebSocketVersion { get; } - public abstract string SecWebSocketKey { get; } public abstract System.Net.CookieCollection CookieCollection { get; } - public abstract System.Security.Principal.IPrincipal User { get; } + public abstract System.Collections.Specialized.NameValueCollection Headers { get; } public abstract bool IsAuthenticated { get; } public abstract bool IsLocal { get; } public abstract bool IsSecureConnection { get; } + public abstract string Origin { get; } + public abstract System.Uri RequestUri { get; } + public abstract string SecWebSocketKey { get; } + public abstract System.Collections.Generic.IEnumerable SecWebSocketProtocols { get; } + public abstract string SecWebSocketVersion { get; } + public abstract System.Security.Principal.IPrincipal User { get; } public abstract System.Net.WebSockets.WebSocket WebSocket { get; } } public enum WebSocketError @@ -91,9 +101,9 @@ namespace System.Net.WebSockets public WebSocketException(System.Net.WebSockets.WebSocketError error, string message, System.Exception innerException) { } public WebSocketException(string message) { } public WebSocketException(string message, System.Exception innerException) { } - public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } - public System.Net.WebSockets.WebSocketError WebSocketErrorCode { get { throw null; } } public override int ErrorCode { get { throw null; } } + public System.Net.WebSockets.WebSocketError WebSocketErrorCode { get { throw null; } } + public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public enum WebSocketMessageType { diff --git a/external/corefx/src/System.Net.WebSockets/src/System.Net.WebSockets.csproj b/external/corefx/src/System.Net.WebSockets/src/System.Net.WebSockets.csproj index ce2852c015..882d8eed46 100644 --- a/external/corefx/src/System.Net.WebSockets/src/System.Net.WebSockets.csproj +++ b/external/corefx/src/System.Net.WebSockets/src/System.Net.WebSockets.csproj @@ -11,6 +11,8 @@ + + @@ -19,12 +21,12 @@ - - Common\System\Net\WebSockets\ManagedWebSocket.cs - Common\System\Net\WebSockets\WebSocketValidate.cs + + Common\System\Net\WebSockets\ManagedWebSocket.cs + @@ -32,6 +34,7 @@ + @@ -46,4 +49,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.netcoreapp.cs b/external/corefx/src/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.netcoreapp.cs new file mode 100644 index 0000000000..23e5f6949c --- /dev/null +++ b/external/corefx/src/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.netcoreapp.cs @@ -0,0 +1,64 @@ +// 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.Diagnostics; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.WebSockets +{ + internal sealed partial class ManagedWebSocket : WebSocket + { + public override Task SendAsync(ReadOnlyMemory buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken) + { + return SendPrivateAsync(buffer, messageType, endOfMessage, cancellationToken); + } + + public override ValueTask ReceiveAsync(Memory buffer, CancellationToken cancellationToken) + { + try + { + WebSocketValidate.ThrowIfInvalidState(_state, _disposed, s_validReceiveStates); + + Debug.Assert(!Monitor.IsEntered(StateUpdateLock), $"{nameof(StateUpdateLock)} must never be held when acquiring {nameof(ReceiveAsyncLock)}"); + lock (ReceiveAsyncLock) // synchronize with receives in CloseAsync + { + ThrowIfOperationInProgress(_lastReceiveAsync); + ValueTask t = ReceiveAsyncPrivate(buffer, cancellationToken); + _lastReceiveAsync = + t.IsCompletedSuccessfully ? (t.Result.MessageType == WebSocketMessageType.Close ? s_cachedCloseTask : Task.CompletedTask) : + t.AsTask(); + return t; + } + } + catch (Exception exc) + { + return new ValueTask(Task.FromException(exc)); + } + } + + private Task ValidateAndReceiveAsync(Task receiveTask, byte[] buffer, CancellationToken cancellationToken) + { + if (receiveTask == null || + (receiveTask.IsCompletedSuccessfully && + !(receiveTask is Task wsrr && wsrr.Result.MessageType == WebSocketMessageType.Close) && + !(receiveTask is Task vwsrr && vwsrr.Result.MessageType == WebSocketMessageType.Close))) + { + ValueTask vt = ReceiveAsyncPrivate(buffer, cancellationToken); + receiveTask = + vt.IsCompletedSuccessfully ? (vt.Result.MessageType == WebSocketMessageType.Close ? s_cachedCloseTask : Task.CompletedTask) : + vt.AsTask(); + } + + return receiveTask; + } + + /// implementation for . + private readonly struct ValueWebSocketReceiveResultGetter : IWebSocketReceiveResultGetter + { + public ValueWebSocketReceiveResult GetResult(int count, WebSocketMessageType messageType, bool endOfMessage, WebSocketCloseStatus? closeStatus, string closeDescription) => + new ValueWebSocketReceiveResult(count, messageType, endOfMessage); // closeStatus/closeDescription are ignored + } + } +} diff --git a/external/corefx/src/System.Net.WebSockets/src/System/Net/WebSockets/ValueWebSocketReceiveResult.cs b/external/corefx/src/System.Net.WebSockets/src/System/Net/WebSockets/ValueWebSocketReceiveResult.cs new file mode 100644 index 0000000000..c63b4bd0fd --- /dev/null +++ b/external/corefx/src/System.Net.WebSockets/src/System/Net/WebSockets/ValueWebSocketReceiveResult.cs @@ -0,0 +1,39 @@ +// 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.Diagnostics; + +namespace System.Net.WebSockets +{ + /// Represents the result of performing a single operation on a . + public readonly struct ValueWebSocketReceiveResult + { + private readonly uint _countAndEndOfMessage; + private readonly WebSocketMessageType _messageType; + + /// Creates an instance of the value type. + /// The number of bytes received. + /// The type of message that was received. + /// Whether this is the final message. + public ValueWebSocketReceiveResult(int count, WebSocketMessageType messageType, bool endOfMessage) + { + if (count < 0) ThrowCountOutOfRange(); + if ((uint)messageType > (uint)WebSocketMessageType.Close) ThrowMessageTypeOutOfRange(); + + _countAndEndOfMessage = (uint)count | (uint)(endOfMessage ? 1 << 31 : 0); + _messageType = messageType; + + Debug.Assert(count == Count); + Debug.Assert(messageType == MessageType); + Debug.Assert(endOfMessage == EndOfMessage); + } + + public int Count => (int)(_countAndEndOfMessage & 0x7FFFFFFF); + public bool EndOfMessage => (_countAndEndOfMessage & 0x80000000) == 0x80000000; + public WebSocketMessageType MessageType => _messageType; + + private static void ThrowCountOutOfRange() => throw new ArgumentOutOfRangeException("count"); + private static void ThrowMessageTypeOutOfRange() => throw new ArgumentOutOfRangeException("messageType"); + } +} diff --git a/external/corefx/src/System.Net.WebSockets/src/System/Net/WebSockets/WebSocket.cs b/external/corefx/src/System.Net.WebSockets/src/System/Net/WebSockets/WebSocket.cs index 3d7fae3905..8013388494 100644 --- a/external/corefx/src/System.Net.WebSockets/src/System/Net/WebSockets/WebSocket.cs +++ b/external/corefx/src/System.Net.WebSockets/src/System/Net/WebSockets/WebSocket.cs @@ -2,8 +2,10 @@ // 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.Buffers; using System.ComponentModel; using System.IO; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -31,6 +33,52 @@ namespace System.Net.WebSockets bool endOfMessage, CancellationToken cancellationToken); + public virtual async ValueTask ReceiveAsync(Memory buffer, CancellationToken cancellationToken) + { + if (buffer.TryGetArray(out ArraySegment arraySegment)) + { + WebSocketReceiveResult r = await ReceiveAsync(arraySegment, cancellationToken).ConfigureAwait(false); + return new ValueWebSocketReceiveResult(r.Count, r.MessageType, r.EndOfMessage); + } + else + { + byte[] array = ArrayPool.Shared.Rent(buffer.Length); + try + { + WebSocketReceiveResult r = await ReceiveAsync(new ArraySegment(array, 0, buffer.Length), cancellationToken).ConfigureAwait(false); + new Span(array, 0, r.Count).CopyTo(buffer.Span); + return new ValueWebSocketReceiveResult(r.Count, r.MessageType, r.EndOfMessage); + } + finally + { + ArrayPool.Shared.Return(array); + } + } + } + + public virtual Task SendAsync(ReadOnlyMemory buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken) => + MemoryMarshal.TryGetArray(buffer, out ArraySegment arraySegment) ? + SendAsync(arraySegment, messageType, endOfMessage, cancellationToken) : + SendWithArrayPoolAsync(buffer, messageType, endOfMessage, cancellationToken); + + private async Task SendWithArrayPoolAsync( + ReadOnlyMemory buffer, + WebSocketMessageType messageType, + bool endOfMessage, + CancellationToken cancellationToken) + { + byte[] array = ArrayPool.Shared.Rent(buffer.Length); + try + { + buffer.Span.CopyTo(array); + await SendAsync(new ArraySegment(array, 0, buffer.Length), messageType, endOfMessage, cancellationToken).ConfigureAwait(false); + } + finally + { + ArrayPool.Shared.Return(array); + } + } + public static TimeSpan DefaultKeepAliveInterval { // In the .NET Framework, this pulls the value from a P/Invoke. Here we just hardcode it to a reasonable default. @@ -82,6 +130,40 @@ namespace System.Net.WebSockets return new ArraySegment(new byte[receiveBufferSize]); } + /// Creates a that operates on a representing a web socket connection. + /// The for the connection. + /// true if this is the server-side of the connection; false if it's the client side. + /// The agreed upon sub-protocol that was used when creating the connection. + /// The keep-alive interval to use, or to disable keep-alives. + /// A scratch buffer that may be used by the implementation for any purpose. + /// The created . + public static WebSocket CreateFromStream(Stream stream, bool isServer, string subProtocol, TimeSpan keepAliveInterval, Memory buffer = default) + { + if (stream == null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (!stream.CanRead || !stream.CanWrite) + { + throw new ArgumentException(!stream.CanRead ? SR.NotReadableStream : SR.NotWriteableStream, nameof(stream)); + } + + if (subProtocol != null) + { + WebSocketValidate.ValidateSubprotocol(subProtocol); + } + + if (keepAliveInterval != Timeout.InfiniteTimeSpan && keepAliveInterval < TimeSpan.Zero) + { + throw new ArgumentOutOfRangeException(nameof(keepAliveInterval), keepAliveInterval, + SR.Format(SR.net_WebSockets_ArgumentOutOfRange_TooSmall, + 0)); + } + + return ManagedWebSocket.CreateFromConnectedStream(stream, isServer, subProtocol, keepAliveInterval, buffer); + } + [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("This API supports the .NET Framework infrastructure and is not intended to be used directly from your code.")] public static bool IsApplicationTargeting45() => true; @@ -129,9 +211,12 @@ namespace System.Net.WebSockets SR.Format(SR.net_WebSockets_ArgumentOutOfRange_TooSmall, 0)); } - return ManagedWebSocket.CreateFromConnectedStream( - innerStream, false, subProtocol, keepAliveInterval, - receiveBufferSize, internalBuffer); + Memory internalMemoryBuffer = + internalBuffer.Count >= receiveBufferSize ? internalBuffer : + receiveBufferSize >= ManagedWebSocket.MaxMessageHeaderLength ? new byte[receiveBufferSize] : + Memory.Empty; // let ManagedWebSocket create it + + return ManagedWebSocket.CreateFromConnectedStream(innerStream, false, subProtocol, keepAliveInterval, internalMemoryBuffer); } } } diff --git a/external/corefx/src/System.Net.WebSockets/src/System/Net/WebSockets/WebSocketError.cs b/external/corefx/src/System.Net.WebSockets/src/System/Net/WebSockets/WebSocketError.cs index c7f295e8cc..4859bda5a1 100644 --- a/external/corefx/src/System.Net.WebSockets/src/System/Net/WebSockets/WebSocketError.cs +++ b/external/corefx/src/System.Net.WebSockets/src/System/Net/WebSockets/WebSocketError.cs @@ -4,6 +4,9 @@ namespace System.Net.WebSockets { +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public enum WebSocketError { Success = 0, diff --git a/external/corefx/src/System.Net.WebSockets/src/System/Net/WebSockets/WebSocketException.cs b/external/corefx/src/System.Net.WebSockets/src/System/Net/WebSockets/WebSocketException.cs index eddeea2443..2ccef0a912 100644 --- a/external/corefx/src/System.Net.WebSockets/src/System/Net/WebSockets/WebSocketException.cs +++ b/external/corefx/src/System.Net.WebSockets/src/System/Net/WebSockets/WebSocketException.cs @@ -10,6 +10,9 @@ using System.Runtime.Serialization; namespace System.Net.WebSockets { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public sealed class WebSocketException : Win32Exception { private readonly WebSocketError _webSocketErrorCode; @@ -123,9 +126,15 @@ namespace System.Net.WebSockets { } + private WebSocketException(SerializationInfo serializationInfo, StreamingContext streamingContext) + : base(serializationInfo, streamingContext) + { + } + public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + info.AddValue(nameof(WebSocketErrorCode), _webSocketErrorCode); } public override int ErrorCode diff --git a/external/corefx/src/System.Net.WebSockets/tests/System.Net.WebSockets.Tests.csproj b/external/corefx/src/System.Net.WebSockets/tests/System.Net.WebSockets.Tests.csproj index 2ff981eabb..05fa64bdf5 100644 --- a/external/corefx/src/System.Net.WebSockets/tests/System.Net.WebSockets.Tests.csproj +++ b/external/corefx/src/System.Net.WebSockets/tests/System.Net.WebSockets.Tests.csproj @@ -11,8 +11,9 @@ + - + \ No newline at end of file diff --git a/external/corefx/src/System.Net.WebSockets/tests/WebSocketTests.cs b/external/corefx/src/System.Net.WebSockets/tests/WebSocketTests.cs index 78c31ebd55..0aad9a68c4 100644 --- a/external/corefx/src/System.Net.WebSockets/tests/WebSocketTests.cs +++ b/external/corefx/src/System.Net.WebSockets/tests/WebSocketTests.cs @@ -2,15 +2,12 @@ // 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.Threading; -using System.Threading.Tasks; using Xunit; namespace System.Net.WebSockets.Tests { - public sealed class WebSocketTests + public sealed partial class WebSocketTests { [Fact] public static void DefaultKeepAliveInterval_ValidValue() diff --git a/external/corefx/src/System.Net.WebSockets/tests/WebSocketTests.netcoreapp.cs b/external/corefx/src/System.Net.WebSockets/tests/WebSocketTests.netcoreapp.cs new file mode 100644 index 0000000000..a95bb7e319 --- /dev/null +++ b/external/corefx/src/System.Net.WebSockets/tests/WebSocketTests.netcoreapp.cs @@ -0,0 +1,83 @@ +// 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.IO; +using System.Threading; +using Xunit; + +namespace System.Net.WebSockets.Tests +{ + public sealed partial class WebSocketTests + { + [Fact] + public void CreateFromStream_InvalidArguments_Throws() + { + AssertExtensions.Throws("stream", + () => WebSocket.CreateFromStream(null, true, "subProtocol", TimeSpan.FromSeconds(30))); + AssertExtensions.Throws("stream", + () => WebSocket.CreateFromStream(new MemoryStream(new byte[100], writable:false), true, "subProtocol", TimeSpan.FromSeconds(30))); + AssertExtensions.Throws("stream", + () => WebSocket.CreateFromStream(new UnreadableStream(), true, "subProtocol", TimeSpan.FromSeconds(30))); + + AssertExtensions.Throws("subProtocol", + () => WebSocket.CreateFromStream(new MemoryStream(), true, " ", TimeSpan.FromSeconds(30))); + AssertExtensions.Throws("subProtocol", + () => WebSocket.CreateFromStream(new MemoryStream(), true, "\xFF", TimeSpan.FromSeconds(30))); + + AssertExtensions.Throws("keepAliveInterval", () => + WebSocket.CreateFromStream(new MemoryStream(), true, "subProtocol", TimeSpan.FromSeconds(-2))); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(14)] + [InlineData(4096)] + public void CreateFromStream_ValidBufferSizes_Succeed(int bufferSize) + { + Assert.NotNull(WebSocket.CreateFromStream(new MemoryStream(), false, null, Timeout.InfiniteTimeSpan, new byte[bufferSize])); + Assert.NotNull(WebSocket.CreateFromStream(new MemoryStream(), true, null, Timeout.InfiniteTimeSpan, new byte[bufferSize])); + } + + [Fact] + public void ValueWebSocketReceiveResult_Ctor_InvalidArguments_Throws() + { + AssertExtensions.Throws("count", () => new ValueWebSocketReceiveResult(-1, WebSocketMessageType.Text, true)); + AssertExtensions.Throws("count", () => new ValueWebSocketReceiveResult(int.MinValue, WebSocketMessageType.Text, true)); + + AssertExtensions.Throws("messageType", () => new ValueWebSocketReceiveResult(0, (WebSocketMessageType)(-1), true)); + AssertExtensions.Throws("messageType", () => new ValueWebSocketReceiveResult(0, (WebSocketMessageType)(3), true)); + AssertExtensions.Throws("messageType", () => new ValueWebSocketReceiveResult(0, (WebSocketMessageType)(int.MinValue), true)); + AssertExtensions.Throws("messageType", () => new ValueWebSocketReceiveResult(0, (WebSocketMessageType)(int.MaxValue), true)); + } + + [Theory] + [InlineData(0, WebSocketMessageType.Text, true)] + [InlineData(0, WebSocketMessageType.Text, false)] + [InlineData(42, WebSocketMessageType.Binary, false)] + [InlineData(int.MaxValue, WebSocketMessageType.Close, false)] + [InlineData(int.MaxValue, WebSocketMessageType.Close, true)] + public void ValueWebSocketReceiveResult_Ctor_ValidArguments_Roundtrip(int count, WebSocketMessageType messageType, bool endOfMessage) + { + ValueWebSocketReceiveResult r = new ValueWebSocketReceiveResult(count, messageType, endOfMessage); + Assert.Equal(count, r.Count); + Assert.Equal(messageType, r.MessageType); + Assert.Equal(endOfMessage, r.EndOfMessage); + } + + private sealed class UnreadableStream : Stream + { + public override bool CanRead => false; + public override bool CanSeek => true; + public override bool CanWrite => true; + public override long Length => throw new NotImplementedException(); + public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public override void Flush() => throw new NotImplementedException(); + public override int Read(byte[] buffer, int offset, int count) => throw new NotImplementedException(); + public override long Seek(long offset, SeekOrigin origin) => throw new NotImplementedException(); + public override void SetLength(long value) => throw new NotImplementedException(); + public override void Write(byte[] buffer, int offset, int count) => throw new NotImplementedException(); + } + } +} diff --git a/external/corefx/src/System.Numerics.Vectors.WindowsRuntime/src/System.Numerics.Vectors.WindowsRuntime.csproj b/external/corefx/src/System.Numerics.Vectors.WindowsRuntime/src/System.Numerics.Vectors.WindowsRuntime.csproj index 8af4e356c9..49bba1db68 100644 --- a/external/corefx/src/System.Numerics.Vectors.WindowsRuntime/src/System.Numerics.Vectors.WindowsRuntime.csproj +++ b/external/corefx/src/System.Numerics.Vectors.WindowsRuntime/src/System.Numerics.Vectors.WindowsRuntime.csproj @@ -6,7 +6,6 @@ System.Numerics true - diff --git a/external/corefx/src/System.Numerics.Vectors/System.Numerics.Vectors.sln b/external/corefx/src/System.Numerics.Vectors/System.Numerics.Vectors.sln index 7afd32ed8c..e07ec13da9 100644 --- a/external/corefx/src/System.Numerics.Vectors/System.Numerics.Vectors.sln +++ b/external/corefx/src/System.Numerics.Vectors/System.Numerics.Vectors.sln @@ -35,10 +35,10 @@ Global {99E1E564-0EF4-4E33-BECE-8ABE64771349}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {99E1E564-0EF4-4E33-BECE-8ABE64771349}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU {99E1E564-0EF4-4E33-BECE-8ABE64771349}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU - {D9906F1A-A41A-43CD-81D2-BA94CF0001C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D9906F1A-A41A-43CD-81D2-BA94CF0001C9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D9906F1A-A41A-43CD-81D2-BA94CF0001C9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D9906F1A-A41A-43CD-81D2-BA94CF0001C9}.Release|Any CPU.Build.0 = Release|Any CPU + {D9906F1A-A41A-43CD-81D2-BA94CF0001C9}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {D9906F1A-A41A-43CD-81D2-BA94CF0001C9}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {D9906F1A-A41A-43CD-81D2-BA94CF0001C9}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {D9906F1A-A41A-43CD-81D2-BA94CF0001C9}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU {53134B0C-0D57-481B-B84E-D1991E8D54FF}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU {53134B0C-0D57-481B-B84E-D1991E8D54FF}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {53134B0C-0D57-481B-B84E-D1991E8D54FF}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU diff --git a/external/corefx/src/System.Numerics.Vectors/ref/System.Numerics.Vectors.cs b/external/corefx/src/System.Numerics.Vectors/ref/System.Numerics.Vectors.cs index 8d4218f421..486ad29db9 100644 --- a/external/corefx/src/System.Numerics.Vectors/ref/System.Numerics.Vectors.cs +++ b/external/corefx/src/System.Numerics.Vectors/ref/System.Numerics.Vectors.cs @@ -8,7 +8,6 @@ namespace System.Numerics { - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Matrix3x2 : System.IEquatable { public float M11; @@ -53,7 +52,6 @@ namespace System.Numerics public static System.Numerics.Matrix3x2 Subtract(System.Numerics.Matrix3x2 value1, System.Numerics.Matrix3x2 value2) { throw null; } public override string ToString() { throw null; } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Matrix4x4 : System.IEquatable { public float M11; @@ -128,7 +126,6 @@ namespace System.Numerics public static System.Numerics.Matrix4x4 Transform(System.Numerics.Matrix4x4 value, System.Numerics.Quaternion rotation) { throw null; } public static System.Numerics.Matrix4x4 Transpose(System.Numerics.Matrix4x4 matrix) { throw null; } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Plane : System.IEquatable { public float D; @@ -150,7 +147,6 @@ namespace System.Numerics public static System.Numerics.Plane Transform(System.Numerics.Plane plane, System.Numerics.Matrix4x4 matrix) { throw null; } public static System.Numerics.Plane Transform(System.Numerics.Plane plane, System.Numerics.Quaternion rotation) { throw null; } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Quaternion : System.IEquatable { public float W; @@ -299,9 +295,9 @@ namespace System.Numerics public static void Widen(System.Numerics.Vector source, out System.Numerics.Vector dest1, out System.Numerics.Vector dest2) { dest1 = default(System.Numerics.Vector); dest2 = default(System.Numerics.Vector); } public static System.Numerics.Vector Xor(System.Numerics.Vector left, System.Numerics.Vector right) where T : struct { throw null; } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Vector : System.IEquatable>, System.IFormattable where T : struct { + private int _dummy; public Vector(T value) { throw null; } public Vector(T[] values) { throw null; } public Vector(T[] values, int index) { throw null; } @@ -345,7 +341,6 @@ namespace System.Numerics public string ToString(string format) { throw null; } public string ToString(string format, System.IFormatProvider formatProvider) { throw null; } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Vector2 : System.IEquatable, System.IFormattable { public float X; @@ -401,7 +396,6 @@ namespace System.Numerics public static System.Numerics.Vector2 TransformNormal(System.Numerics.Vector2 normal, System.Numerics.Matrix3x2 matrix) { throw null; } public static System.Numerics.Vector2 TransformNormal(System.Numerics.Vector2 normal, System.Numerics.Matrix4x4 matrix) { throw null; } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Vector3 : System.IEquatable, System.IFormattable { public float X; @@ -459,7 +453,6 @@ namespace System.Numerics public static System.Numerics.Vector3 Transform(System.Numerics.Vector3 value, System.Numerics.Quaternion rotation) { throw null; } public static System.Numerics.Vector3 TransformNormal(System.Numerics.Vector3 normal, System.Numerics.Matrix4x4 matrix) { throw null; } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Vector4 : System.IEquatable, System.IFormattable { public float W; diff --git a/external/corefx/src/System.Numerics.Vectors/src/System.Numerics.Vectors.csproj b/external/corefx/src/System.Numerics.Vectors/src/System.Numerics.Vectors.csproj index 2af29cb031..d3f22a00a7 100644 --- a/external/corefx/src/System.Numerics.Vectors/src/System.Numerics.Vectors.csproj +++ b/external/corefx/src/System.Numerics.Vectors/src/System.Numerics.Vectors.csproj @@ -81,7 +81,6 @@ TextTemplatingFileGenerator Vector.cs - @@ -92,4 +91,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.Numerics.Vectors/tests/Performance/System.Numerics.Vectors.Performance.Tests.csproj b/external/corefx/src/System.Numerics.Vectors/tests/Performance/System.Numerics.Vectors.Performance.Tests.csproj index 2659735725..0c1b1634d4 100644 --- a/external/corefx/src/System.Numerics.Vectors/tests/Performance/System.Numerics.Vectors.Performance.Tests.csproj +++ b/external/corefx/src/System.Numerics.Vectors/tests/Performance/System.Numerics.Vectors.Performance.Tests.csproj @@ -8,9 +8,8 @@ {D9906F1A-A41A-43CD-81D2-BA94CF0001C9} true - - - + + diff --git a/external/corefx/src/System.Numerics.Vectors/tests/System.Numerics.Vectors.Tests.csproj b/external/corefx/src/System.Numerics.Vectors/tests/System.Numerics.Vectors.Tests.csproj index def338a5d1..c52b2f9485 100644 --- a/external/corefx/src/System.Numerics.Vectors/tests/System.Numerics.Vectors.Tests.csproj +++ b/external/corefx/src/System.Numerics.Vectors/tests/System.Numerics.Vectors.Tests.csproj @@ -5,7 +5,6 @@ {99E1E564-0EF4-4E33-BECE-8ABE64771349} true - diff --git a/external/corefx/src/System.ObjectModel/src/Resources/Strings.resx b/external/corefx/src/System.ObjectModel/src/Resources/Strings.resx index 325ae4d5b5..ce72a8588d 100644 --- a/external/corefx/src/System.ObjectModel/src/Resources/Strings.resx +++ b/external/corefx/src/System.ObjectModel/src/Resources/Strings.resx @@ -103,4 +103,7 @@ Target array type is not compatible with the type of items in the collection. + + The given key '{0}' was not present in the dictionary. + \ No newline at end of file diff --git a/external/corefx/src/System.ObjectModel/src/System.ObjectModel.csproj b/external/corefx/src/System.ObjectModel/src/System.ObjectModel.csproj index 7216992994..337691f8d4 100644 --- a/external/corefx/src/System.ObjectModel/src/System.ObjectModel.csproj +++ b/external/corefx/src/System.ObjectModel/src/System.ObjectModel.csproj @@ -6,7 +6,6 @@ System.ObjectModel true - @@ -31,7 +30,6 @@ - @@ -39,4 +37,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.ObjectModel/src/System/Collections/ObjectModel/KeyedCollection.cs b/external/corefx/src/System.ObjectModel/src/System/Collections/ObjectModel/KeyedCollection.cs index e3db58289d..149ac1dd5a 100644 --- a/external/corefx/src/System.ObjectModel/src/System/Collections/ObjectModel/KeyedCollection.cs +++ b/external/corefx/src/System.ObjectModel/src/System/Collections/ObjectModel/KeyedCollection.cs @@ -82,7 +82,7 @@ namespace System.Collections.ObjectModel return item; } - throw new KeyNotFoundException(); + throw new KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key.ToString())); } } diff --git a/external/corefx/src/System.ObjectModel/src/System/Collections/ObjectModel/ReadOnlyDictionary.cs b/external/corefx/src/System.ObjectModel/src/System/Collections/ObjectModel/ReadOnlyDictionary.cs index fdf0da3b2c..ee66825f3c 100644 --- a/external/corefx/src/System.ObjectModel/src/System/Collections/ObjectModel/ReadOnlyDictionary.cs +++ b/external/corefx/src/System.ObjectModel/src/System/Collections/ObjectModel/ReadOnlyDictionary.cs @@ -6,7 +6,6 @@ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.Contracts; namespace System.Collections.ObjectModel { @@ -32,7 +31,6 @@ namespace System.Collections.ObjectModel { throw new ArgumentNullException(nameof(dictionary)); } - Contract.EndContractBlock(); m_dictionary = dictionary; } @@ -45,7 +43,6 @@ namespace System.Collections.ObjectModel { get { - Contract.Ensures(Contract.Result() != null); if (_keys == null) { _keys = new KeyCollection(m_dictionary.Keys); @@ -58,7 +55,6 @@ namespace System.Collections.ObjectModel { get { - Contract.Ensures(Contract.Result() != null); if (_values == null) { _values = new ValueCollection(m_dictionary.Values); diff --git a/external/corefx/src/System.Private.DataContractSerialization/src/Resources/Strings.resx b/external/corefx/src/System.Private.DataContractSerialization/src/Resources/Strings.resx index e3c95017a5..fc5819b03f 100644 --- a/external/corefx/src/System.Private.DataContractSerialization/src/Resources/Strings.resx +++ b/external/corefx/src/System.Private.DataContractSerialization/src/Resources/Strings.resx @@ -1,5 +1,64 @@  + @@ -373,9 +432,6 @@ An internal error has occurred. Object table overflow. This could be caused by serializing or deserializing extremely large object graphs. - - Property 'Order' in DataMemberAttribute attribute cannot be a negative number. - Invalid number of parameters to call method '{0}'. Expected '{1}' parameters, but '{2}' were provided. @@ -733,9 +789,6 @@ The 'maximum bytes per Read operation' quota ({0}) has been exceeded while reading XML data. Long element start tags (consisting of the element name, attribute names and attribute values) may trigger this quota. This quota may be increased by changing the MaxBytesPerRead property on the XmlDictionaryReaderQuotas object used when creating the XML reader. - - The maximum nametable character count quota ({0}) has been exceeded while reading XML data. The nametable is a data structure used to store strings encountered during XML processing - long XML documents with non-repeating element names, attribute names and attribute values may trigger this quota. This quota may be increased by changing the MaxNameTableCharCount property on the XmlDictionaryReaderQuotas object used when creating the XML reader. - The maximum read depth ({0}) has been exceeded because XML data being read has more levels of nesting than is allowed by the quota. This quota may be increased by changing the MaxDepth property on the XmlDictionaryReaderQuotas object used when creating the XML reader. @@ -850,25 +903,25 @@ A user callback threw an exception. Check the exception stack and inner exception to determine the callback that failed. - + Encountered unexpected character '{0}'. - + The specified offset exceeds the buffer size ({0} bytes). - + The specified size exceeds the remaining buffer space ('{0}' bytes). - + Encountered invalid character '{0}'. - + Characters with hexadecimal values 0xFFFE and 0xFFFF are not valid. - + DateTime values that are greater than DateTime.MaxValue or smaller than DateTime.MinValue when converted to UTC cannot be serialized to JSON. - + To write JSON arrays, use XML writer methods to write the attribute type='array' followed by methods like WriteStartElement (with the local name 'item'), WriteAttributeString, and WriteEndElement to write the JSON array items. @@ -889,7 +942,7 @@ You must write an attribute '{0}'='{1}' after writing the attribute with local name '{2}'. - + Processing instructions (other than the XML declaration) are not supported. @@ -993,7 +1046,7 @@ Object graph of type '{0}' with Id '{2}' contains a reference to itself. The object has been replaced with a new object of type '{1}' either because it implements IObjectReference or because it is surrogated. The serializer does not support fixing up the nested reference to the new object and cannot deserialize this object. Consider changing the object to remove the nested self-reference. - + Type '{0}' is a recursive collection data contract which is not supported. Consider modifying the definition of collection '{0}' to remove references to itself. @@ -1006,30 +1059,6 @@ DataContract for type '{0}' cannot be added to DataContractSet since type '{1}' with the same data contract name '{2}' in namespace '{3}' is already present and the contracts are not equivalent. - - ReferencedTypes specified via ImportOptions must contain valid types. Cannot contain null. - - - ReferencedCollectionTypes specified via ImportOptions must contain valid types. Cannot contain null. - - - (matching) - - - (not matching) - - - List of referenced types contains more than one type with same data contract name. Need to exclude all but one of the following types. Only matching types can be valid references: {0} - - - List of referenced types contains more than one type with data contract name '{0}' in namespace '{1}'. Need to exclude all but one of the following types. Only matching types can be valid references: {2} - - - List of referenced collection types contains more than one type with same data contract name. Include only one of the following types. Only matching types can be valid references: {0} - - - List of referenced collection types contains more than one type with data contract name '{0}' in namespace '{1}'. Include only one of the following types. Only matching types can be valid references: {2} - Type '{0}' cannot be exported as a schema type because it is an open generic type. You can only export a generic type if all its generic parameter types are actual types. @@ -1098,7 +1127,7 @@ System.Runtime.Serialization.NetDataContractSerializer is not supported on this platform. - + The implementation of the function requires System.Runtime.Serialization.IDataContractSurrogate which is not supported on this platform. @@ -1126,4 +1155,4 @@ Failed to create Delegate for method '{0}' of type '{1}'. - + \ No newline at end of file diff --git a/external/corefx/src/System.Private.DataContractSerialization/src/System.Private.DataContractSerialization.csproj b/external/corefx/src/System.Private.DataContractSerialization/src/System.Private.DataContractSerialization.csproj index 508bd4ec02..0eab7366a8 100644 --- a/external/corefx/src/System.Private.DataContractSerialization/src/System.Private.DataContractSerialization.csproj +++ b/external/corefx/src/System.Private.DataContractSerialization/src/System.Private.DataContractSerialization.csproj @@ -15,7 +15,6 @@ false - diff --git a/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSet.cs b/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSet.cs index 280328db96..6e70089be5 100644 --- a/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSet.cs +++ b/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContractSet.cs @@ -14,11 +14,8 @@ namespace System.Runtime.Serialization { private Dictionary _contracts; private Dictionary _processedContracts; - private DataContractDictionary _knownTypesForObject; private ICollection _referencedTypes; private ICollection _referencedCollectionTypes; - private Dictionary _referencedTypesDictionary; - private Dictionary _referencedCollectionTypesDictionary; #if SUPPORT_SURROGATE private IDataContractSurrogate _dataContractSurrogate; @@ -91,11 +88,6 @@ namespace System.Runtime.Serialization } } #endif - internal DataContractDictionary KnownTypesForObject - { - get { return _knownTypesForObject; } - set { _knownTypesForObject = value; } - } internal void Add(Type type) { @@ -310,35 +302,13 @@ namespace System.Runtime.Serialization { SurrogateDataTable[key] = surrogateData; } -#endif - public DataContract this[XmlQualifiedName key] - { - get - { - DataContract dataContract = DataContract.GetBuiltInDataContract(key.Name, key.Namespace); - if (dataContract == null) - { - Contracts.TryGetValue(key, out dataContract); - } - return dataContract; - } - } - -#if SUPPORT_SURROGATE public IDataContractSurrogate DataContractSurrogate { get { return _dataContractSurrogate; } } #endif - public bool Remove(XmlQualifiedName key) - { - if (DataContract.GetBuiltInDataContract(key.Name, key.Namespace) != null) - return false; - return Contracts.Remove(key); - } - public IEnumerator> GetEnumerator() { return Contracts.GetEnumerator(); @@ -368,178 +338,5 @@ namespace System.Runtime.Serialization ProcessedContracts.Add(dataContract, info); } #endif - - private Dictionary GetReferencedTypes() - { - if (_referencedTypesDictionary == null) - { - _referencedTypesDictionary = new Dictionary(); - //Always include Nullable as referenced type - //Do not allow surrogating Nullable - _referencedTypesDictionary.Add(DataContract.GetStableName(Globals.TypeOfNullable), Globals.TypeOfNullable); - if (_referencedTypes != null) - { - foreach (Type type in _referencedTypes) - { - if (type == null) - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.ReferencedTypesCannotContainNull))); - - AddReferencedType(_referencedTypesDictionary, type); - } - } - } - return _referencedTypesDictionary; - } - - private Dictionary GetReferencedCollectionTypes() - { - if (_referencedCollectionTypesDictionary == null) - { - _referencedCollectionTypesDictionary = new Dictionary(); - if (_referencedCollectionTypes != null) - { - foreach (Type type in _referencedCollectionTypes) - { - if (type == null) - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.ReferencedCollectionTypesCannotContainNull))); - AddReferencedType(_referencedCollectionTypesDictionary, type); - } - } - XmlQualifiedName genericDictionaryName = DataContract.GetStableName(Globals.TypeOfDictionaryGeneric); - if (!_referencedCollectionTypesDictionary.ContainsKey(genericDictionaryName) && GetReferencedTypes().ContainsKey(genericDictionaryName)) - AddReferencedType(_referencedCollectionTypesDictionary, Globals.TypeOfDictionaryGeneric); - } - return _referencedCollectionTypesDictionary; - } - - private void AddReferencedType(Dictionary referencedTypes, Type type) - { - if (IsTypeReferenceable(type)) - { - XmlQualifiedName stableName; - try - { - stableName = this.GetStableName(type); - } - catch (InvalidDataContractException) - { - // Type not referenceable if we can't get a stable name. - return; - } - catch (InvalidOperationException) - { - // Type not referenceable if we can't get a stable name. - return; - } - - object value; - if (referencedTypes.TryGetValue(stableName, out value)) - { - Type referencedType = value as Type; - if (referencedType != null) - { - if (referencedType != type) - { - referencedTypes.Remove(stableName); - List types = new List(); - types.Add(referencedType); - types.Add(type); - referencedTypes.Add(stableName, types); - } - } - else - { - List types = (List)value; - if (!types.Contains(type)) - types.Add(type); - } - } - else - referencedTypes.Add(stableName, type); - } - } - internal bool TryGetReferencedType(XmlQualifiedName stableName, DataContract dataContract, out Type type) - { - return TryGetReferencedType(stableName, dataContract, false/*useReferencedCollectionTypes*/, out type); - } - - internal bool TryGetReferencedCollectionType(XmlQualifiedName stableName, DataContract dataContract, out Type type) - { - return TryGetReferencedType(stableName, dataContract, true/*useReferencedCollectionTypes*/, out type); - } - - private bool TryGetReferencedType(XmlQualifiedName stableName, DataContract dataContract, bool useReferencedCollectionTypes, out Type type) - { - object value; - Dictionary referencedTypes = useReferencedCollectionTypes ? GetReferencedCollectionTypes() : GetReferencedTypes(); - if (referencedTypes.TryGetValue(stableName, out value)) - { - type = value as Type; - if (type != null) - return true; - else - { - // Throw ambiguous type match exception - List types = (List)value; - StringBuilder errorMessage = new StringBuilder(); - bool containsGenericType = false; - for (int i = 0; i < types.Count; i++) - { - Type conflictingType = types[i]; - if (!containsGenericType) - containsGenericType = conflictingType.IsGenericTypeDefinition; - errorMessage.AppendFormat("{0}\"{1}\" ", Environment.NewLine, conflictingType.AssemblyQualifiedName); - if (dataContract != null) - { - DataContract other = this.GetDataContract(conflictingType); - errorMessage.Append(SR.Format(((other != null && other.Equals(dataContract)) ? SR.ReferencedTypeMatchingMessage : SR.ReferencedTypeNotMatchingMessage))); - } - } - if (containsGenericType) - { - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format( - (useReferencedCollectionTypes ? SR.AmbiguousReferencedCollectionTypes1 : SR.AmbiguousReferencedTypes1), - errorMessage.ToString()))); - } - else - { - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format( - (useReferencedCollectionTypes ? SR.AmbiguousReferencedCollectionTypes3 : SR.AmbiguousReferencedTypes3), - XmlConvert.DecodeName(stableName.Name), - stableName.Namespace, - errorMessage.ToString()))); - } - } - } - type = null; - return false; - } - - private static bool IsTypeReferenceable(Type type) - { - Type itemType; - - try - { - return (type.IsSerializable || - type.IsDefined(Globals.TypeOfDataContractAttribute, false) || - (Globals.TypeOfIXmlSerializable.IsAssignableFrom(type) && !type.IsGenericTypeDefinition) || - CollectionDataContract.IsCollection(type, out itemType) || - ClassDataContract.IsNonAttributedTypeValidForSerialization(type)); - } - catch (Exception ex) - { - // An exception can be thrown in the designer when a project has a runtime binding redirection for a referenced assembly or a reference dependent assembly. - // Type.IsDefined is known to throw System.IO.FileLoadException. - // ClassDataContract.IsNonAttributedTypeValidForSerialization is known to throw System.IO.FileNotFoundException. - // We guard against all non-critical exceptions. - if (DiagnosticUtility.IsFatal(ex)) - { - throw; - } - } - - return false; - } } } diff --git a/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs b/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs index 7fc7055332..7c80da1642 100644 --- a/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs +++ b/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs @@ -31,11 +31,6 @@ namespace System.Runtime.Serialization _helper = new CriticalHelper(memberInfo); } - internal DataMember(DataContract memberTypeContract, string name, bool isNullable, bool isRequired, bool emitDefaultValue, int order) - { - _helper = new CriticalHelper(memberTypeContract, name, isNullable, isRequired, emitDefaultValue, order); - } - internal MemberInfo MemberInfo { get @@ -64,7 +59,7 @@ namespace System.Runtime.Serialization { get { return _helper.IsRequired; } - + set { _helper.IsRequired = value; } } @@ -73,7 +68,7 @@ namespace System.Runtime.Serialization { get { return _helper.EmitDefaultValue; } - + set { _helper.EmitDefaultValue = value; } } @@ -186,16 +181,6 @@ namespace System.Runtime.Serialization _memberInfo = memberInfo; } - internal CriticalHelper(DataContract memberTypeContract, string name, bool isNullable, bool isRequired, bool emitDefaultValue, int order) - { - this.MemberTypeContract = memberTypeContract; - this.Name = name; - this.IsNullable = isNullable; - this.IsRequired = isRequired; - this.EmitDefaultValue = emitDefaultValue; - this.Order = order; - } - internal MemberInfo MemberInfo { get { return _memberInfo; } diff --git a/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DiagnosticUtility.cs b/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DiagnosticUtility.cs index c12f574c6f..a36dc387f6 100644 --- a/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DiagnosticUtility.cs +++ b/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DiagnosticUtility.cs @@ -73,11 +73,6 @@ namespace System.Runtime.Serialization return new ArgumentException(message); } - public static Exception ThrowHelperArgument(string paramName, string message) - { - return new ArgumentException(message, paramName); - } - internal static Exception ThrowHelperFatal(string message, Exception innerException) { return ThrowHelperError(new Exception(message, innerException)); diff --git a/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataReader.cs b/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataReader.cs index daa389c6e6..2f0df0418f 100644 --- a/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataReader.cs +++ b/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataReader.cs @@ -41,7 +41,6 @@ namespace System.Runtime.Serialization private XmlNodeReader _xmlNodeReader; #pragma warning restore 0649 - private Queue _deserializedDataNodes; private XmlObjectSerializerReadContext _context; private static Dictionary s_nsToPrefixTable; @@ -63,16 +62,6 @@ namespace System.Runtime.Serialization _context = context; } - internal void SetDeserializedValue(object obj) - { - IDataNode deserializedDataNode = (_deserializedDataNodes == null || _deserializedDataNodes.Count == 0) ? null : _deserializedDataNodes.Dequeue(); - if (deserializedDataNode != null && !(obj is IDataNode)) - { - deserializedDataNode.Value = obj; - deserializedDataNode.IsFinalValue = true; - } - } - internal IDataNode GetCurrentNode() { IDataNode retVal = _element.dataNode; @@ -80,14 +69,6 @@ namespace System.Runtime.Serialization return retVal; } - internal void SetDataNode(IDataNode dataNode, string name, string ns) - { - SetNextElement(dataNode, name, ns, null); - _element = _nextElement; - _nextElement = null; - SetElement(); - } - internal void Reset() { _localName = null; @@ -100,7 +81,6 @@ namespace System.Runtime.Serialization _element = null; _nextElement = null; _elements = null; - _deserializedDataNodes = null; } private bool IsXmlDataNode { get { return (_internalNodeType == ExtensionDataNodeType.Xml); } } @@ -451,11 +431,6 @@ namespace System.Runtime.Serialization throw NotImplemented.ByDesign; } - private void SetNextElement(IDataNode node, string name, string ns, string prefix) - { - throw NotImplemented.ByDesign; - } - private void PushElement() { GrowElementsIfNeeded(); @@ -581,4 +556,4 @@ namespace System.Runtime.Serialization } } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Globals.cs b/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Globals.cs index 89b4e34977..37f31ffa67 100644 --- a/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Globals.cs +++ b/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Globals.cs @@ -759,17 +759,6 @@ namespace System.Runtime.Serialization } } - private static Type s_typeOfListGeneric; - internal static Type TypeOfListGeneric - { - get - { - if (s_typeOfListGeneric == null) - s_typeOfListGeneric = typeof(List<>); - return s_typeOfListGeneric; - } - } - private static Type s_typeOfXmlElement; internal static Type TypeOfXmlElement { @@ -855,8 +844,6 @@ namespace System.Runtime.Serialization #endregion private static Type s_typeOfScriptObject; - private static Func s_serializeFunc; - private static Func s_deserializeFunc; internal static ClassDataContract CreateScriptObjectClassDataContract() { @@ -869,25 +856,6 @@ namespace System.Runtime.Serialization return s_typeOfScriptObject != null && s_typeOfScriptObject.IsAssignableFrom(type); } - internal static void SetScriptObjectJsonSerializer(Type typeOfScriptObject, Func serializeFunc, Func deserializeFunc) - { - Globals.s_typeOfScriptObject = typeOfScriptObject; - Globals.s_serializeFunc = serializeFunc; - Globals.s_deserializeFunc = deserializeFunc; - } - - internal static string ScriptObjectJsonSerialize(object obj) - { - Debug.Assert(s_serializeFunc != null); - return Globals.s_serializeFunc(obj); - } - - internal static object ScriptObjectJsonDeserialize(string json) - { - Debug.Assert(s_deserializeFunc != null); - return Globals.s_deserializeFunc(json); - } - public const bool DefaultIsRequired = false; public const bool DefaultEmitDefaultValue = true; public const int DefaultOrder = 0; diff --git a/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/InvalidDataContractException.cs b/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/InvalidDataContractException.cs index 148f95cf61..b56f5895ff 100644 --- a/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/InvalidDataContractException.cs +++ b/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/InvalidDataContractException.cs @@ -4,22 +4,30 @@ using System; - namespace System.Runtime.Serialization { - public class InvalidDataContractException : Exception + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Runtime.Serialization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public partial class InvalidDataContractException : Exception { - public InvalidDataContractException() : base() + public InvalidDataContractException() + : base() { } - public InvalidDataContractException(String message) : base(message) + public InvalidDataContractException(String message) + : base(message) { } - public InvalidDataContractException(String message, Exception innerException) : base(message, innerException) + public InvalidDataContractException(String message, Exception innerException) + : base(message, innerException) + { + } + + protected InvalidDataContractException(SerializationInfo info, StreamingContext context) + : base(info, context) { } } } - diff --git a/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs b/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs index 47033315c8..3576af65e1 100644 --- a/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs +++ b/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs @@ -106,8 +106,8 @@ namespace System.Runtime.Serialization.Json if (this.knownDataContracts == null && this.knownTypeList != null) { // This assignment may be performed concurrently and thus is a race condition. - // It's safe, however, because at worse a new (and identical) dictionary of - // data contracts will be created and re-assigned to this field. Introduction + // It's safe, however, because at worse a new (and identical) dictionary of + // data contracts will be created and re-assigned to this field. Introduction // of a lock here could lead to deadlocks. this.knownDataContracts = XmlObjectSerializerContext.GetDataContractsForKnownTypes(this.knownTypeList); } @@ -119,13 +119,6 @@ namespace System.Runtime.Serialization.Json { get { return _maxItemsInObjectGraph; } } - internal bool AlwaysEmitTypeInformation - { - get - { - return _emitTypeInformation == EmitTypeInformation.Always; - } - } public DateTimeFormat DateTimeFormat { @@ -527,7 +520,7 @@ namespace System.Runtime.Serialization.Json { } - internal DataContractJsonSerializerImpl(Type type, + internal DataContractJsonSerializerImpl(Type type, XmlDictionaryString rootName, IEnumerable knownTypes, int maxItemsInObjectGraph, @@ -576,8 +569,8 @@ namespace System.Runtime.Serialization.Json if (this.knownDataContracts == null && this.knownTypeList != null) { // This assignment may be performed concurrently and thus is a race condition. - // It's safe, however, because at worse a new (and identical) dictionary of - // data contracts will be created and re-assigned to this field. Introduction + // It's safe, however, because at worse a new (and identical) dictionary of + // data contracts will be created and re-assigned to this field. Introduction // of a lock here could lead to deadlocks. this.knownDataContracts = XmlObjectSerializerContext.GetDataContractsForKnownTypes(this.knownTypeList); } @@ -705,7 +698,7 @@ namespace System.Runtime.Serialization.Json public override void WriteObject(Stream stream, object graph) { CheckNull(stream, nameof(stream)); - XmlDictionaryWriter jsonWriter = JsonReaderWriterFactory.CreateJsonWriter(stream, Encoding.UTF8, false); // ownsStream + XmlDictionaryWriter jsonWriter = JsonReaderWriterFactory.CreateJsonWriter(stream, Encoding.UTF8, false); // ownsStream WriteObject(jsonWriter, graph); jsonWriter.Flush(); } @@ -804,7 +797,7 @@ namespace System.Runtime.Serialization.Json internal static void WriteJsonNull(XmlWriterDelegator writer) { - writer.WriteAttributeString(null, JsonGlobals.typeString, null, JsonGlobals.nullString); // prefix // namespace + writer.WriteAttributeString(null, JsonGlobals.typeString, null, JsonGlobals.nullString); // prefix // namespace } internal static void WriteJsonValue(JsonDataContract contract, XmlWriterDelegator writer, object graph, XmlObjectSerializerWriteContextComplexJson context, RuntimeTypeHandle declaredTypeHandle) @@ -900,12 +893,12 @@ namespace System.Runtime.Serialization.Json if (contract.CanContainReferences) { XmlObjectSerializerWriteContextComplexJson context = XmlObjectSerializerWriteContextComplexJson.CreateContext(this, contract); - context.OnHandleReference(writer, graph, true); // canContainReferences + context.OnHandleReference(writer, graph, true); // canContainReferences context.SerializeWithoutXsiType(contract, writer, graph, declaredType.TypeHandle); } else { - DataContractJsonSerializerImpl.WriteJsonValue(JsonDataContract.GetJsonDataContract(contract), writer, graph, null, declaredType.TypeHandle); // XmlObjectSerializerWriteContextComplexJson + DataContractJsonSerializerImpl.WriteJsonValue(JsonDataContract.GetJsonDataContract(contract), writer, graph, null, declaredType.TypeHandle); // XmlObjectSerializerWriteContextComplexJson } } else @@ -914,7 +907,7 @@ namespace System.Runtime.Serialization.Json contract = DataContractJsonSerializerImpl.GetDataContract(contract, declaredType, graphType); if (contract.CanContainReferences) { - context.OnHandleReference(writer, graph, true); // canContainCyclicReference + context.OnHandleReference(writer, graph, true); // canContainCyclicReference context.SerializeWithXsiTypeAtTopLevel(contract, writer, graph, declaredType.TypeHandle, graphType); } else diff --git a/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs b/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs index 7546651697..47d02786e6 100644 --- a/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs +++ b/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs @@ -213,23 +213,19 @@ namespace System.Runtime.Serialization.Json int memberCount = (classContract.BaseContract == null) ? 0 : WriteMembers(classContract.BaseContract, extensionDataLocal, derivedMostClassContract); - _ilg.Call(_contextArg, XmlFormatGeneratorStatics.IncrementItemCountMethod, classContract.Members.Count); + int classMemberCount = classContract.Members.Count; + _ilg.Call(thisObj: _contextArg, XmlFormatGeneratorStatics.IncrementItemCountMethod, classMemberCount); - for (int i = 0; i < classContract.Members.Count; i++, memberCount++) + for (int i = 0; i < classMemberCount; i++, memberCount++) { DataMember member = classContract.Members[i]; Type memberType = member.MemberType; LocalBuilder memberValue = null; - if (member.IsGetOnlyCollection) - { - _ilg.Load(_contextArg); - _ilg.Call(XmlFormatGeneratorStatics.StoreIsGetOnlyCollectionMethod); - } - else - { - _ilg.Load(_contextArg); - _ilg.Call(XmlFormatGeneratorStatics.ResetIsGetOnlyCollectionMethod); - } + + _ilg.Load(_contextArg); + _ilg.Call(methodInfo: member.IsGetOnlyCollection ? + XmlFormatGeneratorStatics.StoreIsGetOnlyCollectionMethod : + XmlFormatGeneratorStatics.ResetIsGetOnlyCollectionMethod); if (!member.EmitDefaultValue) { @@ -238,17 +234,17 @@ namespace System.Runtime.Serialization.Json } bool requiresNameAttribute = DataContractJsonSerializerImpl.CheckIfXmlNameRequiresMapping(classContract.MemberNames[i]); - if (requiresNameAttribute || !TryWritePrimitive(memberType, memberValue, member.MemberInfo, null /*arrayItemIndex*/, null /*nameLocal*/, i + _childElementIndex)) + if (requiresNameAttribute || !TryWritePrimitive(memberType, memberValue, member.MemberInfo, arrayItemIndex: null, name: null, nameIndex: i + _childElementIndex)) { // Note: DataContractSerializer has member-conflict logic here to deal with the schema export // requirement that the same member can't be of two different types. if (requiresNameAttribute) { - _ilg.Call(null, JsonFormatGeneratorStatics.WriteJsonNameWithMappingMethod, _xmlWriterArg, _memberNamesArg, i + _childElementIndex); + _ilg.Call(thisObj: null, JsonFormatGeneratorStatics.WriteJsonNameWithMappingMethod, _xmlWriterArg, _memberNamesArg, i + _childElementIndex); } else { - WriteStartElement(null /*nameLocal*/, i + _childElementIndex); + WriteStartElement(nameLocal: null, nameIndex: i + _childElementIndex); } if (memberValue == null) memberValue = LoadMemberValue(member); @@ -258,7 +254,7 @@ namespace System.Runtime.Serialization.Json if (classContract.HasExtensionData) { - _ilg.Call(_contextArg, XmlFormatGeneratorStatics.WriteExtensionDataMethod, _xmlWriterArg, extensionDataLocal, memberCount); + _ilg.Call(thisObj: _contextArg, XmlFormatGeneratorStatics.WriteExtensionDataMethod, _xmlWriterArg, extensionDataLocal, memberCount); } if (!member.EmitDefaultValue) @@ -266,14 +262,14 @@ namespace System.Runtime.Serialization.Json if (member.IsRequired) { _ilg.Else(); - _ilg.Call(null, XmlFormatGeneratorStatics.ThrowRequiredMemberMustBeEmittedMethod, member.Name, classContract.UnderlyingType); + _ilg.Call(thisObj: null, XmlFormatGeneratorStatics.ThrowRequiredMemberMustBeEmittedMethod, member.Name, classContract.UnderlyingType); } _ilg.EndIf(); } } _typeIndex++; - _childElementIndex += classContract.Members.Count; + _childElementIndex += classMemberCount; return memberCount; } diff --git a/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlJsonReader.cs b/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlJsonReader.cs index 97bacd2192..9c0da90fa7 100644 --- a/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlJsonReader.cs +++ b/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlJsonReader.cs @@ -354,7 +354,7 @@ namespace System.Runtime.Serialization.Json throw; } - throw new Exception(SR.GenericCallbackException, e); + throw new InvalidOperationException(SR.GenericCallbackException, e); } } base.Dispose(disposing); diff --git a/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaHelper.cs b/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaHelper.cs index 82ed48f2e5..f011f4a92f 100644 --- a/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaHelper.cs +++ b/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaHelper.cs @@ -10,24 +10,6 @@ using System.Collections.Generic; namespace System.Runtime.Serialization { - using SchemaObjectDictionary = System.Collections.Generic.Dictionary; - - internal class SchemaObjectInfo - { - internal XmlSchemaType type; - internal XmlSchemaElement element; - internal XmlSchema schema; - internal List knownTypes; - - internal SchemaObjectInfo(XmlSchemaType type, XmlSchemaElement element, XmlSchema schema, List knownTypes) - { - this.type = type; - this.element = element; - this.schema = schema; - this.knownTypes = knownTypes; - } - } - internal static class SchemaHelper { @@ -62,36 +44,6 @@ namespace System.Runtime.Serialization return null; } - internal static XmlSchemaType GetSchemaType(SchemaObjectDictionary schemaInfo, XmlQualifiedName typeName) - { - SchemaObjectInfo schemaObjectInfo; - if (schemaInfo.TryGetValue(typeName, out schemaObjectInfo)) - { - return schemaObjectInfo.type; - } - return null; - } - - internal static XmlSchema GetSchemaWithType(SchemaObjectDictionary schemaInfo, XmlSchemaSet schemas, XmlQualifiedName typeName) - { - SchemaObjectInfo schemaObjectInfo; - if (schemaInfo.TryGetValue(typeName, out schemaObjectInfo)) - { - if (schemaObjectInfo.schema != null) - return schemaObjectInfo.schema; - } - ICollection currentSchemas = schemas.Schemas(); - string ns = typeName.Namespace; - foreach (XmlSchema schema in currentSchemas) - { - if (NamespacesEqual(ns, schema.TargetNamespace)) - { - return schema; - } - } - return null; - } - internal static XmlSchemaElement GetSchemaElement(XmlSchemaSet schemas, XmlQualifiedName elementQName, out XmlSchema outSchema) { outSchema = null; @@ -115,16 +67,6 @@ namespace System.Runtime.Serialization return null; } - internal static XmlSchemaElement GetSchemaElement(SchemaObjectDictionary schemaInfo, XmlQualifiedName elementName) - { - SchemaObjectInfo schemaObjectInfo; - if (schemaInfo.TryGetValue(elementName, out schemaObjectInfo)) - { - return schemaObjectInfo.element; - } - return null; - } - internal static XmlSchema GetSchema(string ns, XmlSchemaSet schemas) { if (ns == null) { ns = String.Empty; } @@ -183,51 +125,5 @@ namespace System.Runtime.Serialization import.Namespace = ns; schema.Includes.Add(import); } - - internal static XmlSchema GetSchemaWithGlobalElementDeclaration(XmlSchemaElement element, XmlSchemaSet schemas) - { - ICollection currentSchemas = schemas.Schemas(); - foreach (XmlSchema schema in currentSchemas) - { - foreach (XmlSchemaObject schemaObject in schema.Items) - { - XmlSchemaElement schemaElement = schemaObject as XmlSchemaElement; - if (schemaElement == null) - continue; - - if (schemaElement == element) - { - return schema; - } - } - } - return null; - } - - internal static XmlQualifiedName GetGlobalElementDeclaration(XmlSchemaSet schemas, XmlQualifiedName typeQName, out bool isNullable) - { - ICollection currentSchemas = schemas.Schemas(); - string ns = typeQName.Namespace; - if (ns == null) - ns = string.Empty; - isNullable = false; - foreach (XmlSchema schema in currentSchemas) - { - foreach (XmlSchemaObject schemaObject in schema.Items) - { - XmlSchemaElement schemaElement = schemaObject as XmlSchemaElement; - if (schemaElement == null) - continue; - - if (schemaElement.SchemaTypeName.Equals(typeQName)) - { - isNullable = schemaElement.IsNillable; - return new XmlQualifiedName(schemaElement.Name, schema.TargetNamespace); - } - } - } - return null; - } - } } diff --git a/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs b/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs index 4f89c3436e..6f93342d94 100644 --- a/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs +++ b/external/corefx/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs @@ -271,26 +271,25 @@ namespace System.Runtime.Serialization _ilg.LoadMember(XmlFormatGeneratorStatics.NamespaceProperty); } else + { _ilg.LoadArrayElement(_contractNamespacesLocal, _typeIndex - 1); + } + _ilg.Store(namespaceLocal); - _ilg.Call(_contextArg, XmlFormatGeneratorStatics.IncrementItemCountMethod, classContract.Members.Count); + int classMemberCount = classContract.Members.Count; + _ilg.Call(thisObj: _contextArg, XmlFormatGeneratorStatics.IncrementItemCountMethod, classMemberCount); - for (int i = 0; i < classContract.Members.Count; i++, memberCount++) + for (int i = 0; i < classMemberCount; i++, memberCount++) { DataMember member = classContract.Members[i]; Type memberType = member.MemberType; LocalBuilder memberValue = null; - if (member.IsGetOnlyCollection) - { - _ilg.Load(_contextArg); - _ilg.Call(XmlFormatGeneratorStatics.StoreIsGetOnlyCollectionMethod); - } - else - { - _ilg.Load(_contextArg); - _ilg.Call(XmlFormatGeneratorStatics.ResetIsGetOnlyCollectionMethod); - } + + _ilg.Load(_contextArg); + _ilg.Call(methodInfo: member.IsGetOnlyCollection ? + XmlFormatGeneratorStatics.StoreIsGetOnlyCollectionMethod : + XmlFormatGeneratorStatics.ResetIsGetOnlyCollectionMethod); if (!member.EmitDefaultValue) { @@ -298,14 +297,14 @@ namespace System.Runtime.Serialization _ilg.IfNotDefaultValue(memberValue); } bool writeXsiType = CheckIfMemberHasConflict(member, classContract, derivedMostClassContract); - if (writeXsiType || !TryWritePrimitive(memberType, memberValue, member.MemberInfo, null /*arrayItemIndex*/, namespaceLocal, null /*nameLocal*/, i + _childElementIndex)) + if (writeXsiType || !TryWritePrimitive(memberType, memberValue, member.MemberInfo, arrayItemIndex: null, ns: namespaceLocal, name: null, nameIndex: i + _childElementIndex)) { - WriteStartElement(memberType, classContract.Namespace, namespaceLocal, null /*nameLocal*/, i + _childElementIndex); + WriteStartElement(memberType, classContract.Namespace, namespaceLocal, nameLocal: null, nameIndex: i + _childElementIndex); if (classContract.ChildElementNamespaces[i + _childElementIndex] != null) { _ilg.Load(_xmlWriterArg); _ilg.LoadArrayElement(_childElementNamespacesLocal, i + _childElementIndex); - _ilg.Call(XmlFormatGeneratorStatics.WriteNamespaceDeclMethod); + _ilg.Call(methodInfo: XmlFormatGeneratorStatics.WriteNamespaceDeclMethod); } if (memberValue == null) memberValue = LoadMemberValue(member); @@ -315,23 +314,22 @@ namespace System.Runtime.Serialization if (classContract.HasExtensionData) { - _ilg.Call(_contextArg, XmlFormatGeneratorStatics.WriteExtensionDataMethod, _xmlWriterArg, extensionDataLocal, memberCount); + _ilg.Call(thisObj: _contextArg, XmlFormatGeneratorStatics.WriteExtensionDataMethod, _xmlWriterArg, extensionDataLocal, memberCount); } - if (!member.EmitDefaultValue) { if (member.IsRequired) { _ilg.Else(); - _ilg.Call(null, XmlFormatGeneratorStatics.ThrowRequiredMemberMustBeEmittedMethod, member.Name, classContract.UnderlyingType); + _ilg.Call(thisObj: null, XmlFormatGeneratorStatics.ThrowRequiredMemberMustBeEmittedMethod, member.Name, classContract.UnderlyingType); } _ilg.EndIf(); } } _typeIndex++; - _childElementIndex += classContract.Members.Count; + _childElementIndex += classMemberCount; return memberCount; } diff --git a/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/BytesWithOffset.cs b/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/BytesWithOffset.cs index 62617d38ce..20624b9c71 100644 --- a/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/BytesWithOffset.cs +++ b/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/BytesWithOffset.cs @@ -4,7 +4,7 @@ namespace System.Xml { - internal struct BytesWithOffset + internal readonly struct BytesWithOffset { private readonly byte[] _bytes; private readonly int _offset; diff --git a/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/XmlBaseWriter.cs b/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/XmlBaseWriter.cs index daa4ff63e8..5e868e5046 100644 --- a/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/XmlBaseWriter.cs +++ b/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/XmlBaseWriter.cs @@ -824,13 +824,6 @@ namespace System.Xml return _nsMgr.LookupPrefix(ns); } - internal string LookupNamespace(string prefix) - { - if (prefix == null) - return null; - return _nsMgr.LookupNamespace(prefix); - } - private string GetQualifiedNamePrefix(string namespaceUri, XmlDictionaryString xNs) { string prefix = _nsMgr.LookupPrefix(namespaceUri); @@ -921,7 +914,7 @@ namespace System.Xml throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.XmlInvalidDeclaration))); // The only thing the text can legitimately contain is version, encoding, and standalone. - // We only support version 1.0, we can only write whatever encoding we were supplied, + // We only support version 1.0, we can only write whatever encoding we were supplied, // and we don't support DTDs, so whatever values are supplied in the text argument are irrelevant. _writer.WriteDeclaration(); } @@ -952,18 +945,6 @@ namespace System.Xml _documentState = DocumentState.End; } - protected int NamespaceBoundary - { - get - { - return _nsMgr.NamespaceBoundary; - } - set - { - _nsMgr.NamespaceBoundary = value; - } - } - public override void WriteEntityRef(string name) { throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.Format(SR.XmlMethodNotSupported, "WriteEntityRef"))); @@ -1855,7 +1836,6 @@ namespace System.Xml private int _attributeCount; private XmlSpace _space; private string _lang; - private int _namespaceBoundary; private int _nsTop; private Namespace _defaultNamespace; @@ -1898,26 +1878,6 @@ namespace System.Xml _space = XmlSpace.None; _lang = null; _lastNameSpace = null; - _namespaceBoundary = 0; - } - - public int NamespaceBoundary - { - get - { - return _namespaceBoundary; - } - set - { - int i; - for (i = 0; i < _nsCount; i++) - if (_namespaces[i].Depth >= value) - break; - - _nsTop = i; - _namespaceBoundary = value; - _lastNameSpace = null; - } } public void Close() diff --git a/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/XmlBufferReader.cs b/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/XmlBufferReader.cs index 1891fcf49f..8737d33512 100644 --- a/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/XmlBufferReader.cs +++ b/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/XmlBufferReader.cs @@ -676,13 +676,6 @@ namespace System.Xml return new string(chars, 0, charCount); } - public string GetEscapedString(int offset, int length, XmlNameTable nameTable) - { - char[] chars = GetCharBuffer(length); - int charCount = GetEscapedChars(offset, length, chars); - return nameTable.Add(chars, 0, charCount); - } - private int GetLessThanCharEntity(int offset, int length) { byte[] buffer = _buffer; @@ -1347,4 +1340,4 @@ namespace System.Xml return list.ToArray(); } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/XmlExceptionHelper.cs b/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/XmlExceptionHelper.cs index ea8e44145d..f52a42bb74 100644 --- a/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/XmlExceptionHelper.cs +++ b/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/XmlExceptionHelper.cs @@ -132,11 +132,6 @@ namespace System.Xml ThrowXmlException(reader, SR.XmlMaxBytesPerReadExceeded, maxBytesPerRead.ToString(NumberFormatInfo.CurrentInfo)); } - public static void ThrowMaxNameTableCharCountExceeded(XmlDictionaryReader reader, int maxNameTableCharCount) - { - ThrowXmlException(reader, SR.XmlMaxNameTableCharCountExceeded, maxNameTableCharCount.ToString(NumberFormatInfo.CurrentInfo)); - } - public static void ThrowMaxDepthExceeded(XmlDictionaryReader reader, int maxDepth) { ThrowXmlException(reader, SR.XmlMaxDepthExceeded, maxDepth.ToString()); diff --git a/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/XmlStreamNodeWriter.cs b/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/XmlStreamNodeWriter.cs index ea071160d5..714e7ac0b2 100644 --- a/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/XmlStreamNodeWriter.cs +++ b/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/XmlStreamNodeWriter.cs @@ -33,19 +33,6 @@ namespace System.Xml _encoding = encoding; } - // Getting/Setting the Stream exists for fragmenting - public Stream Stream - { - get - { - return _stream; - } - set - { - _stream = value; - } - } - // StreamBuffer/BufferOffset exists only for the BinaryWriter to fix up nodes public byte[] StreamBuffer { @@ -480,4 +467,4 @@ namespace System.Xml } } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/XmlUTF8TextWriter.cs b/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/XmlUTF8TextWriter.cs index 9476c80f49..fbad4ecb0a 100644 --- a/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/XmlUTF8TextWriter.cs +++ b/external/corefx/src/System.Private.DataContractSerialization/src/System/Xml/XmlUTF8TextWriter.cs @@ -115,14 +115,6 @@ namespace System.Xml _inAttribute = false; } - public Encoding Encoding - { - get - { - return _encoding; - } - } - private byte[] GetCharEntityBuffer() { if (_entityChars == null) @@ -823,4 +815,4 @@ namespace System.Xml WriteText(localName); } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Private.Reflection.Metadata.Ecma335/src/System.Private.Reflection.Metadata.Ecma335.csproj b/external/corefx/src/System.Private.Reflection.Metadata.Ecma335/src/System.Private.Reflection.Metadata.Ecma335.csproj index e98873e0d4..9853f6d4f9 100644 --- a/external/corefx/src/System.Private.Reflection.Metadata.Ecma335/src/System.Private.Reflection.Metadata.Ecma335.csproj +++ b/external/corefx/src/System.Private.Reflection.Metadata.Ecma335/src/System.Private.Reflection.Metadata.Ecma335.csproj @@ -14,7 +14,6 @@ $(DefineConstants);CORERT ..\..\System.Reflection.Metadata\src\ - @@ -258,4 +257,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Uri/src/Resources/Strings.resx b/external/corefx/src/System.Private.Uri/src/Resources/Strings.resx index dd564ec2d8..d7d0c4d498 100644 --- a/external/corefx/src/System.Private.Uri/src/Resources/Strings.resx +++ b/external/corefx/src/System.Private.Uri/src/Resources/Strings.resx @@ -1,5 +1,64 @@  + @@ -58,21 +117,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Non-negative number required. - - - Index was out of range. Must be non-negative and less than the size of the collection. - - - Index and count must refer to a location within the buffer. - An item with the same key has already been added. Key: {0} - - String contains invalid Unicode code points. - Invalid URI: The Authority/Host could not be parsed. @@ -142,4 +189,7 @@ The subcomponent, {0}, of this uri is not valid. - + + The given key '{0}' was not present in the dictionary. + + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Uri/src/System.Private.Uri.csproj b/external/corefx/src/System.Private.Uri/src/System.Private.Uri.csproj index 7329a93f26..b7c563e485 100644 --- a/external/corefx/src/System.Private.Uri/src/System.Private.Uri.csproj +++ b/external/corefx/src/System.Private.Uri/src/System.Private.Uri.csproj @@ -6,7 +6,6 @@ System.Private.Uri true - @@ -58,4 +57,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Uri/src/System/IPv6AddressHelper.cs b/external/corefx/src/System.Private.Uri/src/System/IPv6AddressHelper.cs index 6c15656a81..701f0d343a 100644 --- a/external/corefx/src/System.Private.Uri/src/System/IPv6AddressHelper.cs +++ b/external/corefx/src/System.Private.Uri/src/System/IPv6AddressHelper.cs @@ -56,7 +56,7 @@ namespace System // Compression; 1::1, ::1, 1:: if (range.Key == i) - { // Start compression, add : + { // Start compression, add : builder.Append(Separator); } if (range.Key <= i && range.Value == (NumberOfLabels - 1)) @@ -92,7 +92,7 @@ namespace System for (int i = 0; i < NumberOfLabels; i++) { if (numbers[i] == 0) - { // In a sequence + { // In a sequence currentSequenceLength++; if (currentSequenceLength > longestSequenceLength) { @@ -351,43 +351,6 @@ namespace System return InternalIsValid(name, start, ref end, false); } - // - // IsValidStrict - // - // Determine whether a name is a valid IPv6 address. Rules are: - // - // * 8 groups of 16-bit hex numbers, separated by ':' - // * a *single* run of zeros can be compressed using the symbol '::' - // * an optional string of a ScopeID delimited by '%' - // * the last 32 bits in an address can be represented as an IPv4 address - // - // Difference between IsValid() and IsValidStrict() is that IsValid() expects part of the string to - // be ipv6 address where as IsValidStrict() expects strict ipv6 address. - // - // Inputs: - // name - // IPv6 address in string format - // - // Outputs: - // Nothing - // - // Assumes: - // the correct name is terminated by ']' character - // - // Returns: - // true if is IPv6 address, else false - // - // Throws: - // Nothing - // - - // Remarks: MUST NOT be used unless all input indexes are verified and trusted. - // start must be next to '[' position, or error is reported - internal static unsafe bool IsValidStrict(char* name, int start, ref int end) - { - return InternalIsValid(name, start, ref end, true); - } - // // Parse // diff --git a/external/corefx/src/System.Private.Uri/src/System/IriHelper.cs b/external/corefx/src/System.Private.Uri/src/System/IriHelper.cs index 18ac7bae1d..4af8f9b916 100644 --- a/external/corefx/src/System.Private.Uri/src/System/IriHelper.cs +++ b/external/corefx/src/System.Private.Uri/src/System/IriHelper.cs @@ -95,11 +95,12 @@ namespace System { return (component == (UriComponents)0) ? UriHelper.IsGenDelim(ch) : false; } - else + else if (UriParser.DontEnableStrictRFC3986ReservedCharacterSets) { + // Since we aren't enabling strict RFC 3986 reserved sets, we stick with the old behavior + // (for app-compat) which was a broken mix of RFCs 2396 and 3986. switch (component) { - // Reserved chars according to RFC 3987 case UriComponents.UserInfo: if (ch == '/' || ch == '?' || ch == '#' || ch == '[' || ch == ']' || ch == '@') return true; @@ -125,6 +126,10 @@ namespace System } return false; } + else + { + return (UriHelper.RFC3986ReservedMarks.IndexOf(ch) >= 0); + } } // @@ -284,7 +289,7 @@ namespace System { if (CheckIriUnicodeRange(ch, component == UriComponents.Query)) { - if (!UriHelper.IsBidiControlCharacter(ch)) + if (!UriHelper.IsBidiControlCharacter(ch) || !UriParser.DontKeepUnicodeBidiFormattingCharacters) { // copy it Debug.Assert(dest.Length > destOffset, "Destination length exceeded destination offset."); diff --git a/external/corefx/src/System.Private.Uri/src/System/Uri.Unix.cs b/external/corefx/src/System.Private.Uri/src/System/Uri.Unix.cs index 5023cf3684..516afb41ac 100644 --- a/external/corefx/src/System.Private.Uri/src/System/Uri.Unix.cs +++ b/external/corefx/src/System.Private.Uri/src/System/Uri.Unix.cs @@ -1,7 +1,11 @@ +// 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. + namespace System { public partial class Uri { private const bool IsWindowsSystem = false; } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Private.Uri/src/System/Uri.Windows.cs b/external/corefx/src/System.Private.Uri/src/System/Uri.Windows.cs index 2a0a8305da..3fb965ec12 100644 --- a/external/corefx/src/System.Private.Uri/src/System/Uri.Windows.cs +++ b/external/corefx/src/System.Private.Uri/src/System/Uri.Windows.cs @@ -1,7 +1,11 @@ +// 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. + namespace System { public partial class Uri { private const bool IsWindowsSystem = true; } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Private.Uri/src/System/Uri.cs.REMOVED.git-id b/external/corefx/src/System.Private.Uri/src/System/Uri.cs.REMOVED.git-id index 129d2d0098..6a2d6423b9 100644 --- a/external/corefx/src/System.Private.Uri/src/System/Uri.cs.REMOVED.git-id +++ b/external/corefx/src/System.Private.Uri/src/System/Uri.cs.REMOVED.git-id @@ -1 +1 @@ -264ccdb37390f7aa4c199c35004ec15e2c18b94d \ No newline at end of file +ebf064fad2d61a6c755274f7cfa67fb959e7dc48 \ No newline at end of file diff --git a/external/corefx/src/System.Private.Uri/src/System/UriFormatException.cs b/external/corefx/src/System.Private.Uri/src/System/UriFormatException.cs index 5f043cab86..966ee0e293 100644 --- a/external/corefx/src/System.Private.Uri/src/System/UriFormatException.cs +++ b/external/corefx/src/System.Private.Uri/src/System/UriFormatException.cs @@ -10,6 +10,9 @@ namespace System /// An exception class used when an invalid Uniform Resource Identifier is detected. /// [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class UriFormatException : FormatException, ISerializable { public UriFormatException() : base() @@ -26,12 +29,11 @@ namespace System protected UriFormatException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) { - throw new PlatformNotSupportedException(); } void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) { base.GetObjectData(serializationInfo, streamingContext); } - }; // class UriFormatException -} // namespace System + } +} diff --git a/external/corefx/src/System.Private.Uri/src/System/UriHelper.cs b/external/corefx/src/System.Private.Uri/src/System/UriHelper.cs index 6bf0afef92..0c602e26ae 100644 --- a/external/corefx/src/System.Private.Uri/src/System/UriHelper.cs +++ b/external/corefx/src/System.Private.Uri/src/System/UriHelper.cs @@ -569,7 +569,7 @@ namespace System EscapeAsciiChar((char)encodedBytes[l], dest, ref destOffset); } } - else if (!UriHelper.IsBidiControlCharacter(unescapedCharsPtr[j])) + else if (!UriHelper.IsBidiControlCharacter(unescapedCharsPtr[j]) || !UriParser.DontKeepUnicodeBidiFormattingCharacters) { //copy chars Debug.Assert(dest.Length > destOffset, "Destination length exceeded destination offset."); @@ -657,29 +657,44 @@ namespace System + 10))); } - // Do not unescape these in safe mode: - // 1) reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," - // 2) excluded = control | "#" | "%" | "\" + internal const string RFC3986ReservedMarks = @";/?:@&=+$,#[]!'()*"; + private const string RFC2396ReservedMarks = @";/?:@&=+$,"; + private const string RFC3986UnreservedMarks = @"-_.~"; + private const string RFC2396UnreservedMarks = @"-_.~*'()!"; + private const string AdditionalUnsafeToUnescape = @"%\#";// While not specified as reserved, these are still unsafe to unescape. + + // When unescaping in safe mode, do not unescape the RFC 3986 reserved set: + // gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" + // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + // / "*" / "+" / "," / ";" / "=" // - // That will still give plenty characters unescaped by SafeUnesced mode such as - // 1) Unicode characters - // 2) Unreserved = alphanum | "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" - // 3) DelimitersAndUnwise = "<" | ">" | <"> | "{" | "}" | "|" | "^" | "[" | "]" | "`" + // In addition, do not unescape the following unsafe characters: + // excluded = "%" / "\" + // + // This implementation used to use the following variant of the RFC 2396 reserved set. + // That behavior is now disabled by default, and is controlled by a UriSyntax property. + // reserved = ";" | "/" | "?" | "@" | "&" | "=" | "+" | "$" | "," + // excluded = control | "#" | "%" | "\" internal static bool IsNotSafeForUnescape(char ch) { if (ch <= '\x1F' || (ch >= '\x7F' && ch <= '\x9F')) + { return true; - else if ((ch >= ';' && ch <= '@' && (ch | '\x2') != '>') || - (ch >= '#' && ch <= '&') || - ch == '+' || ch == ',' || ch == '/' || ch == '\\') + } + else if (UriParser.DontEnableStrictRFC3986ReservedCharacterSets) + { + if ((ch != ':' && (RFC2396ReservedMarks.IndexOf(ch) >= 0) || (AdditionalUnsafeToUnescape.IndexOf(ch) >= 0))) + { + return true; + } + } + else if ((RFC3986ReservedMarks.IndexOf(ch) >= 0) || (AdditionalUnsafeToUnescape.IndexOf(ch) >= 0)) + { return true; - + } return false; } - private const string RFC3986ReservedMarks = @":/?#[]@!$&'()*+,;="; - private const string RFC3986UnreservedMarks = @"-._~"; - private static unsafe bool IsReservedUnreservedOrHash(char c) { if (IsUnreserved(c)) diff --git a/external/corefx/src/System.Private.Uri/src/System/UriSyntax.cs b/external/corefx/src/System.Private.Uri/src/System/UriSyntax.cs index c12b9e825f..15bb88e852 100644 --- a/external/corefx/src/System.Private.Uri/src/System/UriSyntax.cs +++ b/external/corefx/src/System.Private.Uri/src/System/UriSyntax.cs @@ -100,6 +100,25 @@ namespace System internal static UriParser VsMacrosUri; + internal static bool DontEnableStrictRFC3986ReservedCharacterSets + { + // In .NET Framework this would test against an AppContextSwitch. Since this is a potentially + // breaking change, we'll leave in the system used to disable it. + get + { + return false; + } + } + + internal static bool DontKeepUnicodeBidiFormattingCharacters + { + // In .NET Framework this would test against an AppContextSwitch. Since this is a potentially + // breaking change, we'll leave in the system used to disable it. + get + { + return false; + } + } static UriParser() { diff --git a/external/corefx/src/System.Private.Uri/tests/FunctionalTests/IriEncodingDecodingTests.cs b/external/corefx/src/System.Private.Uri/tests/FunctionalTests/IriEncodingDecodingTests.cs new file mode 100644 index 0000000000..1319ad72ce --- /dev/null +++ b/external/corefx/src/System.Private.Uri/tests/FunctionalTests/IriEncodingDecodingTests.cs @@ -0,0 +1,158 @@ +// 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.Reflection; +using System.Text; + +using Xunit; + +namespace System.PrivateUri.Tests +{ + public class IriEncodingDecodingTest + { + // This array contains potentially problematic URI data strings and their canonical encoding. + private static string[,] RFC3986CompliantDecoding = + { + { "%3B%2F%3F%3A%40%26%3D%2B%24%2C", "%3B%2F%3F%3A%40%26%3D%2B%24%2C" }, // Encoded RFC 2396 Reserved Marks. + { @"-_.~", @"-_.~" }, // RFC3986 Unreserved Marks. + { "%2D%5F%2E%7E", @"-_.~" }, // Encoded RFC 3986 Unreserved Marks. + { "%2F%3F%3A%40%23%5B%5D", "%2F%3F%3A%40%23%5B%5D" }, // Encoded RFC 3986 Gen Delims. + { @";&=+$,!'()*", @";&=+$,!'()*" }, // RFC 3986 Sub Delims. + { "%3B%26%3D%2B%24%2C%21%27%28%29%2A", "%3B%26%3D%2B%24%2C%21%27%28%29%2A" }, // Encoded RFC3986 Sub Delims. + { "%E2%80%8F%E2%80%8E%E2%80%AA%E2%80%AB%E2%80%AC%E2%80%AD%E2%80%AE", + "%E2%80%8F%E2%80%8E%E2%80%AA%E2%80%AB%E2%80%AC%E2%80%AD%E2%80%AE" }, // Encoded Unicode Bidi Control Characters. + { "\u200E\u200F\u202A\u202B\u202C\u202D\u202E", + "%E2%80%8E%E2%80%8F%E2%80%AA%E2%80%AB%E2%80%AC%E2%80%AD%E2%80%AE" }, // Unencoded Unicode Bidi Control Characters + }; + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] + public void AbsoluteUri_DangerousPathSymbols_RFC3986CompliantAbsoluteUri() + { + Uri uri; + string baseUri = "http://a/%C3%88/"; + for (int i = 0; i < RFC3986CompliantDecoding.GetLength(0); i++) + { + string sourceStr = baseUri + RFC3986CompliantDecoding[i, 0]; + uri = new Uri(sourceStr); + Assert.Equal(baseUri + RFC3986CompliantDecoding[i, 1], uri.AbsoluteUri); + } + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] + public void AbsolutePath_DangerousPathSymbols_RFC3986CompliantAbsolutePath() + { + Uri uri; + string host = "http://a"; + string path = "/%C3%88/"; + for (int i = 0; i < RFC3986CompliantDecoding.GetLength(0); i++) + { + string sourceStr = host + path + RFC3986CompliantDecoding[i, 0]; + uri = new Uri(sourceStr); + Assert.Equal(path + RFC3986CompliantDecoding[i, 1], uri.AbsolutePath); + } + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] + public void Segments_DangerousPathSymbols_RFC3986CompliantPathSegments() + { + Uri uri; + string host = "http://a"; + string path = "/%C3%88/"; + for (int i = 0; i < RFC3986CompliantDecoding.GetLength(0); i++) + { + string sourceStr = host + path + RFC3986CompliantDecoding[i, 0]; + uri = new Uri(sourceStr); + Assert.Equal(path + RFC3986CompliantDecoding[i, 1], String.Join(String.Empty, uri.Segments)); + } + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] + public void Equality_DangerousPathSymbols_RFC3986CompliantEquality() + { + string baseUri = "http://a/%C3%88/"; + for (int i = 0; i < RFC3986CompliantDecoding.GetLength(0); i++) + { + Uri uri1 = new Uri(baseUri + RFC3986CompliantDecoding[i, 0]); + Uri uri2 = new Uri(baseUri + RFC3986CompliantDecoding[i, 1]); + Assert.Equal(uri1, uri2); + } + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] + public void PathAndQuery_DangerousQuerySymbols_RFC3986CompliantPathAndQuery() + { + Uri uri; + string host = "http://a"; + string path = "/%C3%88/"; + for (int i = 0; i < RFC3986CompliantDecoding.GetLength(0); i++) + { + string sourceStr = host + path + "?" + RFC3986CompliantDecoding[i, 0]; + uri = new Uri(sourceStr); + Assert.Equal(path + "?" + RFC3986CompliantDecoding[i, 1], uri.PathAndQuery); + } + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] + public void Query_DangerousQuerySymbols_RFC3986CompliantQuery() + { + Uri uri; + string baseUri = "http://a/%C3%88/"; + for (int i = 0; i < RFC3986CompliantDecoding.GetLength(0); i++) + { + string sourceStr = baseUri + "?" + RFC3986CompliantDecoding[i, 0]; + uri = new Uri(sourceStr); + Assert.Equal("?" + RFC3986CompliantDecoding[i, 1], uri.Query); + } + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] + public void Equality_DangerousQuerySymbols_RFC3986CompliantEquality() + { + string baseUri = "http://a/%C3%88/"; + for (int i = 0; i < RFC3986CompliantDecoding.GetLength(0); i++) + { + Uri uriTest = new Uri(baseUri + "?" + RFC3986CompliantDecoding[i, 0]); + Uri uriTest1 = new Uri(baseUri + "?" + RFC3986CompliantDecoding[i, 1]); + Assert.Equal(uriTest, uriTest1); + } + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] + public void Fragment_DangerousFragmentSymbols_RFC3986CompliantFragment() + { + Uri uri; + string baseUri = "http://a/%C3%88/"; + for (int i = 0; i < RFC3986CompliantDecoding.GetLength(0); i++) + { + string sourceStr = baseUri + "#" + RFC3986CompliantDecoding[i, 0]; + uri = new Uri(sourceStr); + Assert.Equal("#" + RFC3986CompliantDecoding[i, 1], uri.Fragment); + } + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] + public void UserInfo_DangerousUserInfoSymbols_RFC3986CompliantUserInfo() + { + Uri uri; + string baseUri = "http://"; + string host = "a"; + string path = "/%C3%88/"; + for (int i = 0; i < RFC3986CompliantDecoding.GetLength(0); i++) + { + string sourceStr = baseUri + RFC3986CompliantDecoding[i, 0] + "@" + host + path; + uri = new Uri(sourceStr); + Assert.Equal(RFC3986CompliantDecoding[i, 1], uri.UserInfo); + } + } + } +} diff --git a/external/corefx/src/System.Private.Uri/tests/FunctionalTests/IriTest.cs b/external/corefx/src/System.Private.Uri/tests/FunctionalTests/IriTest.cs index 4eb7d554de..a7a09da6eb 100644 --- a/external/corefx/src/System.Private.Uri/tests/FunctionalTests/IriTest.cs +++ b/external/corefx/src/System.Private.Uri/tests/FunctionalTests/IriTest.cs @@ -64,32 +64,36 @@ namespace System.PrivateUri.Tests public void Iri_Uri_SchemaParsing_ShouldNotThrowArgumentOutOfRange() { string root = "viewcode://./codeschema_class?"; - string uriDataFra = root + Uri.EscapeDataString("Type=\u00E9"); + string uriDataFra = root + "Type=" + Uri.EscapeDataString("\u00E9"); Uri u1 = new Uri(uriDataFra); - // TODO #8330 : Normalization should produce the same result for escaped/unescaped URIs. - // Assert.Equal(root + "Type=%C3%A9", u1.AbsoluteUri); - - Assert.NotEqual(root + "Type=%C3%A9", u1.AbsoluteUri); + Assert.Equal(root + "Type=%C3%A9", u1.AbsoluteUri); } [Fact] - public void Iri_IncorrectNormalization() + public void Iri_ReservedCharacters_NotNormalized() { Uri u1 = new Uri(@"http://test.com/%2c"); Uri u2 = new Uri(@"http://test.com/,"); - // TODO #8330 : Normalization should produce the same result for escaped/unescaped URIs. - //Assert.Equal( - // u1.ToString(), - // u2.ToString()); - Assert.NotEqual( u1.ToString(), u2.ToString()); } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] + public void Iri_UnknownSchemeWithoutAuthority_DoesNormalize() + { + string[] paths = { "\u00E8", "%C3%A8" }; + foreach (string path in paths) + { + Uri noAuthority = new Uri("scheme:" + path); + Assert.Equal("scheme:\u00E8", noAuthority.ToString()); + } + } + [Fact] public void Iri_804110_TryCreateUri_ShouldNotThrowIndexOutOfRange() { @@ -403,47 +407,47 @@ namespace System.PrivateUri.Tests /// /// First column contains input characters found to be potential issues with the current implementation. - /// The second column contains the current (.Net 4.5.2) Uri behavior for Uri normalization. + /// The second column contains the current (.Net Core 2.1/Framework 4.7.2) Uri behavior for Uri normalization. /// private static string[,] s_checkIsReservedEscapingStrings = { - // : [ ] in host + // : [ ] in host. {"http://user@ser%3Aver.srv:123/path/path/resource.ext?query=expression#fragment", null}, {"http://user@server.srv%3A999/path/path/resource.ext?query=expression#fragment", null}, {"http://user@server.%5Bsrv:123/path/path/resource.ext?query=expression#fragment", null}, {"http://user@ser%5Dver.srv:123/path/path/resource.ext?query=expression#fragment", null}, - // [ ] in userinfo - {"http://us%5Ber@server.srv:123/path/path/resource.ext?query=expression#fragment", + // [ ] in userinfo. + {"http://us%5Ber@server.srv:123/path/path/resource.ext?query=expression#fragment", "http://us%5Ber@server.srv:123/path/path/resource.ext?query=expression#fragment"}, - {"http://u%5Dser@server.srv:123/path/path/resource.ext?query=expression#fragment", + {"http://u%5Dser@server.srv:123/path/path/resource.ext?query=expression#fragment", "http://u%5Dser@server.srv:123/path/path/resource.ext?query=expression#fragment"}, - {"http://us%5B%5Der@server.srv:123/path/path/resource.ext?query=expression#fragment", + {"http://us%5B%5Der@server.srv:123/path/path/resource.ext?query=expression#fragment", "http://us%5B%5Der@server.srv:123/path/path/resource.ext?query=expression#fragment"}, - // [ ] in path - {"http://user@server.srv:123/path/pa%5Bth/resource.ext?query=expression#fragment", - "http://user@server.srv:123/path/pa[th/resource.ext?query=expression#fragment"}, - {"http://user@server.srv:123/pa%5Dth/path%5D/resource.ext?query=expression#fragment", - "http://user@server.srv:123/pa]th/path]/resource.ext?query=expression#fragment"}, - {"http://user@server.srv:123/path/p%5Ba%5Dth/resource.ext?query=expression#fragment", - "http://user@server.srv:123/path/p[a]th/resource.ext?query=expression#fragment"}, + // [ ] : ' in path. + {"http://user@server.srv:123/path/pa%5B%3A%27th/resource.ext?query=expression#fragment", + "http://user@server.srv:123/path/pa%5B%3A%27th/resource.ext?query=expression#fragment"}, + {"http://user@server.srv:123/pa%5D%3A%27th/path%5D%3A%27/resource.ext?query=expression#fragment", + "http://user@server.srv:123/pa%5D%3A%27th/path%5D%3A%27/resource.ext?query=expression#fragment"}, + {"http://user@server.srv:123/path/p%5B%3A%27a%5D%3A%27th/resource.ext?query=expression#fragment", + "http://user@server.srv:123/path/p%5B%3A%27a%5D%3A%27th/resource.ext?query=expression#fragment"}, - // [ ] in query - {"http://user@server.srv:123/path/path/resource.ext?que%5Bry=expression#fragment", - "http://user@server.srv:123/path/path/resource.ext?que[ry=expression#fragment"}, - {"http://user@server.srv:123/path/path/resource.ext?query=exp%5Dression#fragment", - "http://user@server.srv:123/path/path/resource.ext?query=exp]ression#fragment"}, - {"http://user@server.srv:123/path/path/resource.ext?que%5Bry=exp%5Dression#fragment", - "http://user@server.srv:123/path/path/resource.ext?que[ry=exp]ression#fragment"}, + // [ ] : ' in query. + {"http://user@server.srv:123/path/path/resource.ext?que%5B%3A%27ry=expression#fragment", + "http://user@server.srv:123/path/path/resource.ext?que%5B%3A%27ry=expression#fragment"}, + {"http://user@server.srv:123/path/path/resource.ext?query=exp%5D%3A%27ression#fragment", + "http://user@server.srv:123/path/path/resource.ext?query=exp%5D%3A%27ression#fragment"}, + {"http://user@server.srv:123/path/path/resource.ext?que%5B%3A%27ry=exp%5D%3A%27ression#fragment", + "http://user@server.srv:123/path/path/resource.ext?que%5B%3A%27ry=exp%5D%3A%27ression#fragment"}, - // [ ] in fragment - {"http://user@server.srv:123/path/path/resource.ext?query=expression#fr%5Bagment", - "http://user@server.srv:123/path/path/resource.ext?query=expression#fr[agment"}, - {"http://user@server.srv:123/path/path/resource.ext?query=expression#fragment%5D", - "http://user@server.srv:123/path/path/resource.ext?query=expression#fragment]"}, - {"http://user@server.srv:123/path/path/resource.ext?query=expression#fr%5Bagment%5D", - "http://user@server.srv:123/path/path/resource.ext?query=expression#fr[agment]"} + // [ ] : ' in fragment. + {"http://user@server.srv:123/path/path/resource.ext?query=expression#fr%5B%3A%27agment", + "http://user@server.srv:123/path/path/resource.ext?query=expression#fr%5B%3A%27agment"}, + {"http://user@server.srv:123/path/path/resource.ext?query=expression#fragment%5D%3A%27", + "http://user@server.srv:123/path/path/resource.ext?query=expression#fragment%5D%3A%27"}, + {"http://user@server.srv:123/path/path/resource.ext?query=expression#fr%5B%3A%27agment%5D%3A%27", + "http://user@server.srv:123/path/path/resource.ext?query=expression#fr%5B%3A%27agment%5D%3A%27"} }; /// @@ -451,6 +455,7 @@ namespace System.PrivateUri.Tests /// CheckIsReserved(). /// [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] public void Iri_CheckIsReserved_EscapingBehavior() { for (int i = 0; i < s_checkIsReservedEscapingStrings.GetLength(0); i++) @@ -521,5 +526,19 @@ namespace System.PrivateUri.Tests catch (FormatException) { } } + + [Theory] + [InlineData("\u00E8")] + [InlineData("_\u00E8")] + [InlineData("_")] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] + public void Iri_FileUriUncFallback_DoesSupportUnicodeHost(string authority) + { + Uri fileTwoSlashes = new Uri("file://" + authority); + Uri fileFourSlashes = new Uri("file:////" + authority); + + Assert.Equal(authority, fileTwoSlashes.Authority); // Two slashes must be followed by an authority + Assert.Equal(authority, fileFourSlashes.Authority); // More than three slashes looks like a UNC share + } } } diff --git a/external/corefx/src/System.Private.Uri/tests/FunctionalTests/System.Private.Uri.Functional.Tests.csproj b/external/corefx/src/System.Private.Uri/tests/FunctionalTests/System.Private.Uri.Functional.Tests.csproj index 9797bea754..1c41d3e072 100644 --- a/external/corefx/src/System.Private.Uri/tests/FunctionalTests/System.Private.Uri.Functional.Tests.csproj +++ b/external/corefx/src/System.Private.Uri/tests/FunctionalTests/System.Private.Uri.Functional.Tests.csproj @@ -11,6 +11,7 @@ + diff --git a/external/corefx/src/System.Private.Uri/tests/FunctionalTests/UriEscapingTest.cs b/external/corefx/src/System.Private.Uri/tests/FunctionalTests/UriEscapingTest.cs index 0e1a202d82..dedd01885e 100644 --- a/external/corefx/src/System.Private.Uri/tests/FunctionalTests/UriEscapingTest.cs +++ b/external/corefx/src/System.Private.Uri/tests/FunctionalTests/UriEscapingTest.cs @@ -321,13 +321,14 @@ namespace System.PrivateUri.Tests } [Fact] - public void UriAbsoluteUnEscaping_RFC2396UnreservedEscaped_AllUnescaped() + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] + public void UriAbsoluteUnEscaping_RFC3986UnreservedEscaped_AllUnescaped() { - string escaped = Escape(RFC2396Unreserved); + string escaped = Escape(RFC3986Unreserved); string input = "http://" + AlphaNumeric.ToLowerInvariant() + "/" + escaped + "?" + escaped + "#" + escaped; - string expectedOutput = "http://" + AlphaNumeric.ToLowerInvariant() + "/" + RFC2396Unreserved - + "?" + RFC2396Unreserved + "#" + RFC2396Unreserved; + string expectedOutput = "http://" + AlphaNumeric.ToLowerInvariant() + "/" + RFC3986Unreserved + + "?" + RFC3986Unreserved + "#" + RFC3986Unreserved; Uri testUri = new Uri(input); Assert.Equal(expectedOutput, testUri.AbsoluteUri); @@ -398,12 +399,12 @@ namespace System.PrivateUri.Tests } [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Requires fix shipping in .NET 4.7.2")] public void UriAbsoluteEscaping_SurrogatePair_LocaleIndependent() { string uriString = "http://contosotest.conto.soco.ntosoco.com/surrgtest()?$filter="; - string expectedString = uriString + "%E6%95%B0%E6%8D%AE%20eq%20" + - "'%F0%A0%80%80%F0%A0%80%81%F0%A0%80%82%F0%A0%80%83%F0%AA%9B%91%F0%AA%9B" + - "%92%F0%AA%9B%93%F0%AA%9B%94%F0%AA%9B%95%F0%AA%9B%96'"; + string expectedString = uriString + "%E6%95%B0%E6%8D%AE%20eq%20%27%F0%A0%80%80%F0%A0%80%81%F0%A0%80%82%F0%A0%80%83%F0" + + "%AA%9B%91%F0%AA%9B%92%F0%AA%9B%93%F0%AA%9B%94%F0%AA%9B%95%F0%AA%9B%96%27"; using (ThreadCultureChange iriHelper = new ThreadCultureChange()) { diff --git a/external/corefx/src/System.Private.Uri/tests/FunctionalTests/UriTests.cs b/external/corefx/src/System.Private.Uri/tests/FunctionalTests/UriTests.cs index 8a997dbd84..fa4da3d641 100644 --- a/external/corefx/src/System.Private.Uri/tests/FunctionalTests/UriTests.cs +++ b/external/corefx/src/System.Private.Uri/tests/FunctionalTests/UriTests.cs @@ -889,5 +889,12 @@ namespace System.PrivateUri.Tests s = uri.GetComponents(UriComponents.Host, UriFormat.UriEscaped); Assert.Equal(s, "www.contoso.com"); } + + [Fact] + public static void TestCasingWhenCombiningAbsoluteAndRelativeUris() + { + Uri u = new Uri(new Uri("http://example.com/", UriKind.Absolute), new Uri("C(B:G", UriKind.Relative)); + Assert.Equal("http://example.com/C(B:G", u.ToString()); + } } } diff --git a/external/corefx/src/System.Private.Uri/tests/UnitTests/Fakes/FakeUriParser.cs b/external/corefx/src/System.Private.Uri/tests/UnitTests/Fakes/FakeUriParser.cs index 1f5875a80f..6508e1750a 100644 --- a/external/corefx/src/System.Private.Uri/tests/UnitTests/Fakes/FakeUriParser.cs +++ b/external/corefx/src/System.Private.Uri/tests/UnitTests/Fakes/FakeUriParser.cs @@ -6,5 +6,20 @@ namespace System { public class UriParser { + internal static bool DontEnableStrictRFC3986ReservedCharacterSets + { + get + { + return false; + } + } + + internal static bool DontKeepUnicodeBidiFormattingCharacters + { + get + { + return false; + } + } } } diff --git a/external/corefx/src/System.Private.Xml.Linq/src/System.Private.Xml.Linq.csproj b/external/corefx/src/System.Private.Xml.Linq/src/System.Private.Xml.Linq.csproj index a1e1a33153..1d5ade2524 100644 --- a/external/corefx/src/System.Private.Xml.Linq/src/System.Private.Xml.Linq.csproj +++ b/external/corefx/src/System.Private.Xml.Linq/src/System.Private.Xml.Linq.csproj @@ -8,7 +8,6 @@ $(DefineConstants);SILVERLIGHT $(DefineConstants);uap - diff --git a/external/corefx/src/System.Private.Xml.Linq/src/System/Xml/XPath/XNodeNavigator.cs b/external/corefx/src/System.Private.Xml.Linq/src/System/Xml/XPath/XNodeNavigator.cs index 7abd8ea21d..4260cec895 100644 --- a/external/corefx/src/System.Private.Xml.Linq/src/System/Xml/XPath/XNodeNavigator.cs +++ b/external/corefx/src/System.Private.Xml.Linq/src/System/Xml/XPath/XNodeNavigator.cs @@ -853,7 +853,7 @@ namespace System.Xml.XPath } } - internal struct XPathEvaluator + internal readonly struct XPathEvaluator { public object Evaluate(XNode node, string expression, IXmlNamespaceResolver resolver) where T : class { diff --git a/external/corefx/src/System.Private.Xml.Linq/tests/Properties/System.Xml.Linq.Properties.Tests.csproj b/external/corefx/src/System.Private.Xml.Linq/tests/Properties/System.Xml.Linq.Properties.Tests.csproj index 772734494a..4e580632ae 100644 --- a/external/corefx/src/System.Private.Xml.Linq/tests/Properties/System.Xml.Linq.Properties.Tests.csproj +++ b/external/corefx/src/System.Private.Xml.Linq/tests/Properties/System.Xml.Linq.Properties.Tests.csproj @@ -4,7 +4,6 @@ {D24E2563-7A46-4368-94D4-B3A39E9EF1B5} - diff --git a/external/corefx/src/System.Private.Xml.Linq/tests/SDMSample/System.Xml.Linq.SDMSample.Tests.csproj b/external/corefx/src/System.Private.Xml.Linq/tests/SDMSample/System.Xml.Linq.SDMSample.Tests.csproj index efa7daccce..0b423145fb 100644 --- a/external/corefx/src/System.Private.Xml.Linq/tests/SDMSample/System.Xml.Linq.SDMSample.Tests.csproj +++ b/external/corefx/src/System.Private.Xml.Linq/tests/SDMSample/System.Xml.Linq.SDMSample.Tests.csproj @@ -4,7 +4,6 @@ {F6C73170-9333-4B52-B3FA-A536C5EA6A48} - diff --git a/external/corefx/src/System.Private.Xml.Linq/tests/Schema/System.Xml.Schema.Extensions.Tests.csproj b/external/corefx/src/System.Private.Xml.Linq/tests/Schema/System.Xml.Schema.Extensions.Tests.csproj index 892606704e..7e583dd77a 100644 --- a/external/corefx/src/System.Private.Xml.Linq/tests/Schema/System.Xml.Schema.Extensions.Tests.csproj +++ b/external/corefx/src/System.Private.Xml.Linq/tests/Schema/System.Xml.Schema.Extensions.Tests.csproj @@ -4,7 +4,6 @@ {AFB408EA-2EF7-42B3-B98F-BA60F6481313} - diff --git a/external/corefx/src/System.Private.Xml.Linq/tests/Streaming/System.Xml.Linq.Streaming.Tests.csproj b/external/corefx/src/System.Private.Xml.Linq/tests/Streaming/System.Xml.Linq.Streaming.Tests.csproj index 3e8d88ec48..de3a004f66 100644 --- a/external/corefx/src/System.Private.Xml.Linq/tests/Streaming/System.Xml.Linq.Streaming.Tests.csproj +++ b/external/corefx/src/System.Private.Xml.Linq/tests/Streaming/System.Xml.Linq.Streaming.Tests.csproj @@ -4,7 +4,6 @@ {CB11B315-2567-4574-977D-89E3135243C4} - diff --git a/external/corefx/src/System.Private.Xml.Linq/tests/TreeManipulation/System.Xml.Linq.TreeManipulation.Tests.csproj b/external/corefx/src/System.Private.Xml.Linq/tests/TreeManipulation/System.Xml.Linq.TreeManipulation.Tests.csproj index d907197608..887806cf97 100644 --- a/external/corefx/src/System.Private.Xml.Linq/tests/TreeManipulation/System.Xml.Linq.TreeManipulation.Tests.csproj +++ b/external/corefx/src/System.Private.Xml.Linq/tests/TreeManipulation/System.Xml.Linq.TreeManipulation.Tests.csproj @@ -4,7 +4,6 @@ {10EFE488-FAB4-43DA-847D-FF057BFF52AC} - diff --git a/external/corefx/src/System.Private.Xml.Linq/tests/XDocument.Common/XDocument.Common.csproj b/external/corefx/src/System.Private.Xml.Linq/tests/XDocument.Common/XDocument.Common.csproj index cd7a7880f1..4b840eb13d 100644 --- a/external/corefx/src/System.Private.Xml.Linq/tests/XDocument.Common/XDocument.Common.csproj +++ b/external/corefx/src/System.Private.Xml.Linq/tests/XDocument.Common/XDocument.Common.csproj @@ -6,7 +6,6 @@ XDocument.Common CoreXml.Test.XLinq - diff --git a/external/corefx/src/System.Private.Xml.Linq/tests/XDocument.Test.ModuleCore/XDocument.Test.ModuleCore.csproj b/external/corefx/src/System.Private.Xml.Linq/tests/XDocument.Test.ModuleCore/XDocument.Test.ModuleCore.csproj index c4df68c3b5..4d17b52667 100644 --- a/external/corefx/src/System.Private.Xml.Linq/tests/XDocument.Test.ModuleCore/XDocument.Test.ModuleCore.csproj +++ b/external/corefx/src/System.Private.Xml.Linq/tests/XDocument.Test.ModuleCore/XDocument.Test.ModuleCore.csproj @@ -4,7 +4,6 @@ {979510CE-9042-4F8D-9C74-EE03B89194CC} - diff --git a/external/corefx/src/System.Private.Xml.Linq/tests/axes/System.Xml.Linq.Axes.Tests.csproj b/external/corefx/src/System.Private.Xml.Linq/tests/axes/System.Xml.Linq.Axes.Tests.csproj index 574b48d62c..15071a44ce 100644 --- a/external/corefx/src/System.Private.Xml.Linq/tests/axes/System.Xml.Linq.Axes.Tests.csproj +++ b/external/corefx/src/System.Private.Xml.Linq/tests/axes/System.Xml.Linq.Axes.Tests.csproj @@ -4,7 +4,6 @@ {6D9B0285-5E8A-4C20-9C53-9E2084EF64C4} - diff --git a/external/corefx/src/System.Private.Xml.Linq/tests/events/System.Xml.Linq.Events.Tests.csproj b/external/corefx/src/System.Private.Xml.Linq/tests/events/System.Xml.Linq.Events.Tests.csproj index dcadd7fea0..e299c26803 100644 --- a/external/corefx/src/System.Private.Xml.Linq/tests/events/System.Xml.Linq.Events.Tests.csproj +++ b/external/corefx/src/System.Private.Xml.Linq/tests/events/System.Xml.Linq.Events.Tests.csproj @@ -4,7 +4,6 @@ {C560E194-5B14-4112-ABC6-3208491E53E6} - @@ -21,7 +20,6 @@ - diff --git a/external/corefx/src/System.Private.Xml.Linq/tests/misc/System.Xml.Linq.Misc.Tests.csproj b/external/corefx/src/System.Private.Xml.Linq/tests/misc/System.Xml.Linq.Misc.Tests.csproj index ef13353846..37d30161d0 100644 --- a/external/corefx/src/System.Private.Xml.Linq/tests/misc/System.Xml.Linq.Misc.Tests.csproj +++ b/external/corefx/src/System.Private.Xml.Linq/tests/misc/System.Xml.Linq.Misc.Tests.csproj @@ -1,10 +1,9 @@ - + {35FA1FA9-A504-4B9E-93F0-E5D03C21BECA} - @@ -27,4 +26,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml.Linq/tests/xNodeBuilder/System.Xml.Linq.xNodeBuilder.Tests.csproj b/external/corefx/src/System.Private.Xml.Linq/tests/xNodeBuilder/System.Xml.Linq.xNodeBuilder.Tests.csproj index 7ef7bfd746..5586b852f1 100644 --- a/external/corefx/src/System.Private.Xml.Linq/tests/xNodeBuilder/System.Xml.Linq.xNodeBuilder.Tests.csproj +++ b/external/corefx/src/System.Private.Xml.Linq/tests/xNodeBuilder/System.Xml.Linq.xNodeBuilder.Tests.csproj @@ -4,7 +4,6 @@ {5D4FB9ED-C3AC-4EFA-9FEE-619ED4B4B92D} - @@ -32,4 +31,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml.Linq/tests/xNodeReader/System.Xml.Linq.xNodeReader.Tests.csproj b/external/corefx/src/System.Private.Xml.Linq/tests/xNodeReader/System.Xml.Linq.xNodeReader.Tests.csproj index 1d27cd2453..47e40adaeb 100644 --- a/external/corefx/src/System.Private.Xml.Linq/tests/xNodeReader/System.Xml.Linq.xNodeReader.Tests.csproj +++ b/external/corefx/src/System.Private.Xml.Linq/tests/xNodeReader/System.Xml.Linq.xNodeReader.Tests.csproj @@ -4,7 +4,6 @@ {6E1C5358-7F04-4791-8B5F-6A5A4E42ABF1} - diff --git a/external/corefx/src/System.Private.Xml/System.Private.Xml.sln b/external/corefx/src/System.Private.Xml/System.Private.Xml.sln index c5ac809df1..d5a425eff5 100644 --- a/external/corefx/src/System.Private.Xml/System.Private.Xml.sln +++ b/external/corefx/src/System.Private.Xml/System.Private.Xml.sln @@ -154,82 +154,82 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A30BBA60-647C-4565-A42F-BE60B2CA2E8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A30BBA60-647C-4565-A42F-BE60B2CA2E8E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A30BBA60-647C-4565-A42F-BE60B2CA2E8E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A30BBA60-647C-4565-A42F-BE60B2CA2E8E}.Release|Any CPU.Build.0 = Release|Any CPU - {6DC15D23-8213-4700-9815-AD8DEED1CE5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6DC15D23-8213-4700-9815-AD8DEED1CE5F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6DC15D23-8213-4700-9815-AD8DEED1CE5F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6DC15D23-8213-4700-9815-AD8DEED1CE5F}.Release|Any CPU.Build.0 = Release|Any CPU - {B51913C2-478E-46AA-A523-521BD4593651}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B51913C2-478E-46AA-A523-521BD4593651}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B51913C2-478E-46AA-A523-521BD4593651}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B51913C2-478E-46AA-A523-521BD4593651}.Release|Any CPU.Build.0 = Release|Any CPU - {04C5492C-FA54-4F93-8698-44B8BB7C72E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {04C5492C-FA54-4F93-8698-44B8BB7C72E0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {04C5492C-FA54-4F93-8698-44B8BB7C72E0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {04C5492C-FA54-4F93-8698-44B8BB7C72E0}.Release|Any CPU.Build.0 = Release|Any CPU - {2CA30CA9-FADA-4AB6-81E3-EAE61EF44463}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2CA30CA9-FADA-4AB6-81E3-EAE61EF44463}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2CA30CA9-FADA-4AB6-81E3-EAE61EF44463}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2CA30CA9-FADA-4AB6-81E3-EAE61EF44463}.Release|Any CPU.Build.0 = Release|Any CPU - {54B5F207-CC11-4AC3-B0D7-1E7A7E2F08DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {54B5F207-CC11-4AC3-B0D7-1E7A7E2F08DE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {54B5F207-CC11-4AC3-B0D7-1E7A7E2F08DE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {54B5F207-CC11-4AC3-B0D7-1E7A7E2F08DE}.Release|Any CPU.Build.0 = Release|Any CPU - {6DC919A7-CF8F-4CCD-A7E3-1AD9389FEC86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6DC919A7-CF8F-4CCD-A7E3-1AD9389FEC86}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6DC919A7-CF8F-4CCD-A7E3-1AD9389FEC86}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6DC919A7-CF8F-4CCD-A7E3-1AD9389FEC86}.Release|Any CPU.Build.0 = Release|Any CPU - {1C8F67D6-1953-49D3-B716-F298883A79C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1C8F67D6-1953-49D3-B716-F298883A79C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1C8F67D6-1953-49D3-B716-F298883A79C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1C8F67D6-1953-49D3-B716-F298883A79C6}.Release|Any CPU.Build.0 = Release|Any CPU - {95C95878-A9CD-43D4-B1BB-D0DCAA54C3D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {95C95878-A9CD-43D4-B1BB-D0DCAA54C3D7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {95C95878-A9CD-43D4-B1BB-D0DCAA54C3D7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {95C95878-A9CD-43D4-B1BB-D0DCAA54C3D7}.Release|Any CPU.Build.0 = Release|Any CPU - {E6DAD59F-7CB7-4E70-B4C5-FCCBC3376EDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E6DAD59F-7CB7-4E70-B4C5-FCCBC3376EDE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E6DAD59F-7CB7-4E70-B4C5-FCCBC3376EDE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E6DAD59F-7CB7-4E70-B4C5-FCCBC3376EDE}.Release|Any CPU.Build.0 = Release|Any CPU - {3F18D20D-0267-4381-857B-EEDB7B3FC549}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3F18D20D-0267-4381-857B-EEDB7B3FC549}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3F18D20D-0267-4381-857B-EEDB7B3FC549}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3F18D20D-0267-4381-857B-EEDB7B3FC549}.Release|Any CPU.Build.0 = Release|Any CPU - {8A7370E5-BF89-4BCF-ACCD-BA298869055B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8A7370E5-BF89-4BCF-ACCD-BA298869055B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8A7370E5-BF89-4BCF-ACCD-BA298869055B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8A7370E5-BF89-4BCF-ACCD-BA298869055B}.Release|Any CPU.Build.0 = Release|Any CPU - {7EAFC2D8-48D2-4A56-A9C6-6BADF2053499}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7EAFC2D8-48D2-4A56-A9C6-6BADF2053499}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7EAFC2D8-48D2-4A56-A9C6-6BADF2053499}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7EAFC2D8-48D2-4A56-A9C6-6BADF2053499}.Release|Any CPU.Build.0 = Release|Any CPU - {DA6A9B7F-F311-49A4-8BBE-42EF3152C37B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DA6A9B7F-F311-49A4-8BBE-42EF3152C37B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DA6A9B7F-F311-49A4-8BBE-42EF3152C37B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DA6A9B7F-F311-49A4-8BBE-42EF3152C37B}.Release|Any CPU.Build.0 = Release|Any CPU - {507DB29F-74F5-4B34-A240-ABE7BD168DF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {507DB29F-74F5-4B34-A240-ABE7BD168DF6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {507DB29F-74F5-4B34-A240-ABE7BD168DF6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {507DB29F-74F5-4B34-A240-ABE7BD168DF6}.Release|Any CPU.Build.0 = Release|Any CPU - {E4BC1A16-AD0A-4F70-BD2E-3346A4D9BC2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E4BC1A16-AD0A-4F70-BD2E-3346A4D9BC2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E4BC1A16-AD0A-4F70-BD2E-3346A4D9BC2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E4BC1A16-AD0A-4F70-BD2E-3346A4D9BC2B}.Release|Any CPU.Build.0 = Release|Any CPU - {F05DE950-CA99-42A8-B44D-E7DAA5C3C783}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F05DE950-CA99-42A8-B44D-E7DAA5C3C783}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F05DE950-CA99-42A8-B44D-E7DAA5C3C783}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F05DE950-CA99-42A8-B44D-E7DAA5C3C783}.Release|Any CPU.Build.0 = Release|Any CPU - {9EDAADA8-B658-430F-97EE-CCA494883D86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9EDAADA8-B658-430F-97EE-CCA494883D86}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9EDAADA8-B658-430F-97EE-CCA494883D86}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9EDAADA8-B658-430F-97EE-CCA494883D86}.Release|Any CPU.Build.0 = Release|Any CPU - {B0F53AAA-4ABC-44B2-9331-D3802340DD20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B0F53AAA-4ABC-44B2-9331-D3802340DD20}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B0F53AAA-4ABC-44B2-9331-D3802340DD20}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B0F53AAA-4ABC-44B2-9331-D3802340DD20}.Release|Any CPU.Build.0 = Release|Any CPU + {A30BBA60-647C-4565-A42F-BE60B2CA2E8E}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {A30BBA60-647C-4565-A42F-BE60B2CA2E8E}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {A30BBA60-647C-4565-A42F-BE60B2CA2E8E}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {A30BBA60-647C-4565-A42F-BE60B2CA2E8E}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {6DC15D23-8213-4700-9815-AD8DEED1CE5F}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {6DC15D23-8213-4700-9815-AD8DEED1CE5F}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {6DC15D23-8213-4700-9815-AD8DEED1CE5F}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {6DC15D23-8213-4700-9815-AD8DEED1CE5F}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {B51913C2-478E-46AA-A523-521BD4593651}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {B51913C2-478E-46AA-A523-521BD4593651}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {B51913C2-478E-46AA-A523-521BD4593651}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {B51913C2-478E-46AA-A523-521BD4593651}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {04C5492C-FA54-4F93-8698-44B8BB7C72E0}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {04C5492C-FA54-4F93-8698-44B8BB7C72E0}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {04C5492C-FA54-4F93-8698-44B8BB7C72E0}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {04C5492C-FA54-4F93-8698-44B8BB7C72E0}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {2CA30CA9-FADA-4AB6-81E3-EAE61EF44463}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {2CA30CA9-FADA-4AB6-81E3-EAE61EF44463}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {2CA30CA9-FADA-4AB6-81E3-EAE61EF44463}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {2CA30CA9-FADA-4AB6-81E3-EAE61EF44463}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {54B5F207-CC11-4AC3-B0D7-1E7A7E2F08DE}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {54B5F207-CC11-4AC3-B0D7-1E7A7E2F08DE}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {54B5F207-CC11-4AC3-B0D7-1E7A7E2F08DE}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {54B5F207-CC11-4AC3-B0D7-1E7A7E2F08DE}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {6DC919A7-CF8F-4CCD-A7E3-1AD9389FEC86}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {6DC919A7-CF8F-4CCD-A7E3-1AD9389FEC86}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {6DC919A7-CF8F-4CCD-A7E3-1AD9389FEC86}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {6DC919A7-CF8F-4CCD-A7E3-1AD9389FEC86}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {1C8F67D6-1953-49D3-B716-F298883A79C6}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {1C8F67D6-1953-49D3-B716-F298883A79C6}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {1C8F67D6-1953-49D3-B716-F298883A79C6}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {1C8F67D6-1953-49D3-B716-F298883A79C6}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {95C95878-A9CD-43D4-B1BB-D0DCAA54C3D7}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {95C95878-A9CD-43D4-B1BB-D0DCAA54C3D7}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {95C95878-A9CD-43D4-B1BB-D0DCAA54C3D7}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {95C95878-A9CD-43D4-B1BB-D0DCAA54C3D7}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {E6DAD59F-7CB7-4E70-B4C5-FCCBC3376EDE}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {E6DAD59F-7CB7-4E70-B4C5-FCCBC3376EDE}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {E6DAD59F-7CB7-4E70-B4C5-FCCBC3376EDE}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {E6DAD59F-7CB7-4E70-B4C5-FCCBC3376EDE}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {3F18D20D-0267-4381-857B-EEDB7B3FC549}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {3F18D20D-0267-4381-857B-EEDB7B3FC549}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {3F18D20D-0267-4381-857B-EEDB7B3FC549}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {3F18D20D-0267-4381-857B-EEDB7B3FC549}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {8A7370E5-BF89-4BCF-ACCD-BA298869055B}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {8A7370E5-BF89-4BCF-ACCD-BA298869055B}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {8A7370E5-BF89-4BCF-ACCD-BA298869055B}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {8A7370E5-BF89-4BCF-ACCD-BA298869055B}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU + {7EAFC2D8-48D2-4A56-A9C6-6BADF2053499}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {7EAFC2D8-48D2-4A56-A9C6-6BADF2053499}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {7EAFC2D8-48D2-4A56-A9C6-6BADF2053499}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {7EAFC2D8-48D2-4A56-A9C6-6BADF2053499}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {DA6A9B7F-F311-49A4-8BBE-42EF3152C37B}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {DA6A9B7F-F311-49A4-8BBE-42EF3152C37B}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {DA6A9B7F-F311-49A4-8BBE-42EF3152C37B}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {DA6A9B7F-F311-49A4-8BBE-42EF3152C37B}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {507DB29F-74F5-4B34-A240-ABE7BD168DF6}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {507DB29F-74F5-4B34-A240-ABE7BD168DF6}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {507DB29F-74F5-4B34-A240-ABE7BD168DF6}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {507DB29F-74F5-4B34-A240-ABE7BD168DF6}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {E4BC1A16-AD0A-4F70-BD2E-3346A4D9BC2B}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {E4BC1A16-AD0A-4F70-BD2E-3346A4D9BC2B}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {E4BC1A16-AD0A-4F70-BD2E-3346A4D9BC2B}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {E4BC1A16-AD0A-4F70-BD2E-3346A4D9BC2B}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {F05DE950-CA99-42A8-B44D-E7DAA5C3C783}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {F05DE950-CA99-42A8-B44D-E7DAA5C3C783}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {F05DE950-CA99-42A8-B44D-E7DAA5C3C783}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {F05DE950-CA99-42A8-B44D-E7DAA5C3C783}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {9EDAADA8-B658-430F-97EE-CCA494883D86}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {9EDAADA8-B658-430F-97EE-CCA494883D86}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {9EDAADA8-B658-430F-97EE-CCA494883D86}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {9EDAADA8-B658-430F-97EE-CCA494883D86}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {B0F53AAA-4ABC-44B2-9331-D3802340DD20}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {B0F53AAA-4ABC-44B2-9331-D3802340DD20}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {B0F53AAA-4ABC-44B2-9331-D3802340DD20}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {B0F53AAA-4ABC-44B2-9331-D3802340DD20}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU {9891F9AC-9A0A-47DF-8D96-92B21AFC3B93}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU {9891F9AC-9A0A-47DF-8D96-92B21AFC3B93}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {9891F9AC-9A0A-47DF-8D96-92B21AFC3B93}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU @@ -242,18 +242,18 @@ Global {4050F1D1-1DD2-4B48-A17B-E3F90DD18C4B}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU {4050F1D1-1DD2-4B48-A17B-E3F90DD18C4B}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU {4050F1D1-1DD2-4B48-A17B-E3F90DD18C4B}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU - {8CDE71C2-4DA4-4AF6-9897-CD953AE653C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8CDE71C2-4DA4-4AF6-9897-CD953AE653C2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8CDE71C2-4DA4-4AF6-9897-CD953AE653C2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8CDE71C2-4DA4-4AF6-9897-CD953AE653C2}.Release|Any CPU.Build.0 = Release|Any CPU - {7B57D5F1-4E6C-4280-AD5B-C71C73B66B11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7B57D5F1-4E6C-4280-AD5B-C71C73B66B11}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7B57D5F1-4E6C-4280-AD5B-C71C73B66B11}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7B57D5F1-4E6C-4280-AD5B-C71C73B66B11}.Release|Any CPU.Build.0 = Release|Any CPU - {D0DF902A-2486-4A38-B7A7-232B9B6590E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D0DF902A-2486-4A38-B7A7-232B9B6590E1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D0DF902A-2486-4A38-B7A7-232B9B6590E1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D0DF902A-2486-4A38-B7A7-232B9B6590E1}.Release|Any CPU.Build.0 = Release|Any CPU + {8CDE71C2-4DA4-4AF6-9897-CD953AE653C2}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {8CDE71C2-4DA4-4AF6-9897-CD953AE653C2}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {8CDE71C2-4DA4-4AF6-9897-CD953AE653C2}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {8CDE71C2-4DA4-4AF6-9897-CD953AE653C2}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {7B57D5F1-4E6C-4280-AD5B-C71C73B66B11}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {7B57D5F1-4E6C-4280-AD5B-C71C73B66B11}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {7B57D5F1-4E6C-4280-AD5B-C71C73B66B11}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {7B57D5F1-4E6C-4280-AD5B-C71C73B66B11}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {D0DF902A-2486-4A38-B7A7-232B9B6590E1}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {D0DF902A-2486-4A38-B7A7-232B9B6590E1}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {D0DF902A-2486-4A38-B7A7-232B9B6590E1}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {D0DF902A-2486-4A38-B7A7-232B9B6590E1}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU {B01E2AE1-1B52-4518-B32E-016070356A7F}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU {B01E2AE1-1B52-4518-B32E-016070356A7F}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {B01E2AE1-1B52-4518-B32E-016070356A7F}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU diff --git a/external/corefx/src/System.Private.Xml/src/Resources/Strings.resx.REMOVED.git-id b/external/corefx/src/System.Private.Xml/src/Resources/Strings.resx.REMOVED.git-id index 21e9d4f811..837f6399bb 100644 --- a/external/corefx/src/System.Private.Xml/src/Resources/Strings.resx.REMOVED.git-id +++ b/external/corefx/src/System.Private.Xml/src/Resources/Strings.resx.REMOVED.git-id @@ -1 +1 @@ -c3943b26f7a36fe94f98d5240a9c1018939cc2ed \ No newline at end of file +b7c8d8724fc05f407a46e9f5d1a16455fc1f0eb6 \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/src/System.Private.Xml.csproj b/external/corefx/src/System.Private.Xml/src/System.Private.Xml.csproj index 79811fa6f7..bff32c8149 100644 --- a/external/corefx/src/System.Private.Xml/src/System.Private.Xml.csproj +++ b/external/corefx/src/System.Private.Xml/src/System.Private.Xml.csproj @@ -11,7 +11,6 @@ $(DefineConstants);FEATURE_COMPILED_XSL $(DefineConstants);FEATURE_SERIALIZATION_UAPAOT;UAPAOT - @@ -24,6 +23,9 @@ System\StringBuilderCache.cs + + System\Marvin.cs + @@ -516,13 +518,13 @@ - + @@ -532,6 +534,7 @@ + @@ -755,4 +758,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Base64Decoder.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Base64Decoder.cs index f273b647e6..f730ea006c 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Base64Decoder.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Base64Decoder.cs @@ -44,7 +44,6 @@ namespace System.Xml } } - [System.Security.SecuritySafeCritical] internal override unsafe int Decode(char[] chars, int startPos, int len) { if (chars == null) @@ -80,7 +79,6 @@ namespace System.Xml return charsDecoded; } - [System.Security.SecuritySafeCritical] internal override unsafe int Decode(string str, int startPos, int len) { if (str == null) @@ -153,7 +151,6 @@ namespace System.Xml return mapBase64; } - [System.Security.SecurityCritical] private unsafe void Decode(char* pChars, char* pCharsEndPos, byte* pBytes, byte* pBytesEndPos, out int charsDecoded, out int bytesDecoded) diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/BinHexDecoder.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/BinHexDecoder.cs index bb960bb186..169c7fad63 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/BinHexDecoder.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/BinHexDecoder.cs @@ -7,17 +7,6 @@ using System.Diagnostics; namespace System.Xml { -#if XMLSERIALIZERGENERATOR - internal abstract class IncrementalReadDecoder - { - internal abstract int DecodedCount { get; } - internal abstract bool IsFull { get; } - internal abstract void SetNextOutputBuffer(Array array, int offset, int len); - internal abstract int Decode(char[] chars, int startPos, int len); - internal abstract int Decode(string str, int startPos, int len); - internal abstract void Reset(); - } -#endif internal class BinHexDecoder : IncrementalReadDecoder { // @@ -49,7 +38,6 @@ namespace System.Xml } } - [System.Security.SecuritySafeCritical] internal override unsafe int Decode(char[] chars, int startPos, int len) { if (chars == null) @@ -86,7 +74,6 @@ namespace System.Xml return charsDecoded; } - [System.Security.SecuritySafeCritical] internal override unsafe int Decode(string str, int startPos, int len) { if (str == null) @@ -146,7 +133,6 @@ namespace System.Xml // // Static methods // - [System.Security.SecuritySafeCritical] public static unsafe byte[] Decode(char[] chars, bool allowOddChars) { if (chars == null) @@ -175,11 +161,7 @@ namespace System.Xml if (hasHalfByteCached && !allowOddChars) { -#if XMLSERIALIZERGENERATOR - throw new XmlException(SR.Format(SR.Xml_InvalidBinHexValueOddCount, new string(chars))); -#else throw new XmlException(SR.Xml_InvalidBinHexValueOddCount, new string(chars)); -#endif } if (bytesDecoded < bytes.Length) @@ -196,7 +178,6 @@ namespace System.Xml // Private methods // - [System.Security.SecurityCritical] private static unsafe void Decode(char* pChars, char* pCharsEndPos, byte* pBytes, byte* pBytesEndPos, ref bool hasHalfByteCached, ref byte cachedHalfByte, @@ -233,11 +214,7 @@ namespace System.Xml } else { -#if XMLSERIALIZERGENERATOR - throw new XmlException(SR.Format(SR.Xml_InvalidBinHexValue, new string(pChars, 0, (int)(pCharsEndPos - pChars)))); -#else throw new XmlException(SR.Xml_InvalidBinHexValue, new string(pChars, 0, (int)(pCharsEndPos - pChars))); -#endif } if (hasHalfByteCached) diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriterAsync.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriterAsync.cs index 449acc5380..90461887ce 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriterAsync.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlEncodedRawTextWriterAsync.cs @@ -649,7 +649,6 @@ namespace System.Xml // Serialize text that is part of an attribute value. The '&', '<', '>', and '"' characters // are entitized. - [SecuritySafeCritical] protected unsafe int WriteAttributeTextBlockNoFlush(char* pSrc, char* pSrcEnd) { char* pRaw = pSrc; @@ -755,7 +754,6 @@ namespace System.Xml return -1; } - [SecuritySafeCritical] protected unsafe int WriteAttributeTextBlockNoFlush(char[] chars, int index, int count) { if (count == 0) @@ -770,7 +768,6 @@ namespace System.Xml } } - [SecuritySafeCritical] protected unsafe int WriteAttributeTextBlockNoFlush(string text, int index, int count) { if (count == 0) @@ -838,7 +835,6 @@ namespace System.Xml // Serialize text that is part of element content. The '&', '<', and '>' characters // are entitized. - [SecuritySafeCritical] protected unsafe int WriteElementTextBlockNoFlush(char* pSrc, char* pSrcEnd, out bool needWriteNewLine) { needWriteNewLine = false; @@ -947,7 +943,6 @@ namespace System.Xml return -1; } - [SecuritySafeCritical] protected unsafe int WriteElementTextBlockNoFlush(char[] chars, int index, int count, out bool needWriteNewLine) { needWriteNewLine = false; @@ -964,7 +959,6 @@ namespace System.Xml } } - [SecuritySafeCritical] protected unsafe int WriteElementTextBlockNoFlush(string text, int index, int count, out bool needWriteNewLine) { needWriteNewLine = false; @@ -1063,7 +1057,6 @@ namespace System.Xml } while (writeLen >= 0 || needWriteNewLine); } - [SecuritySafeCritical] protected unsafe int RawTextNoFlush(char* pSrcBegin, char* pSrcEnd) { char* pRaw = pSrcBegin; @@ -1112,7 +1105,6 @@ namespace System.Xml return -1; } - [SecuritySafeCritical] protected unsafe int RawTextNoFlush(string text, int index, int count) { if (count == 0) @@ -1221,7 +1213,6 @@ namespace System.Xml } } - [SecuritySafeCritical] protected unsafe int WriteRawWithCharCheckingNoFlush(char* pSrcBegin, char* pSrcEnd, out bool needWriteNewLine) { needWriteNewLine = false; @@ -1317,7 +1308,6 @@ namespace System.Xml return -1; } - [SecuritySafeCritical] protected unsafe int WriteRawWithCharCheckingNoFlush(char[] chars, int index, int count, out bool needWriteNewLine) { needWriteNewLine = false; @@ -1333,7 +1323,6 @@ namespace System.Xml } } - [SecuritySafeCritical] protected unsafe int WriteRawWithCharCheckingNoFlush(string text, int index, int count, out bool needWriteNewLine) { needWriteNewLine = false; @@ -1397,7 +1386,6 @@ namespace System.Xml } while (writeLen >= 0 || needWriteNewLine); } - [SecuritySafeCritical] protected unsafe int WriteCommentOrPiNoFlush(string text, int index, int count, int stopChar, out bool needWriteNewLine) { needWriteNewLine = false; @@ -1567,7 +1555,6 @@ namespace System.Xml } while (writeLen >= 0 || needWriteNewLine); } - [SecuritySafeCritical] protected unsafe int WriteCDataSectionNoFlush(string text, int index, int count, out bool needWriteNewLine) { needWriteNewLine = false; diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlTextEncoder.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlTextEncoder.cs index d2dfcea883..5bed6cea40 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlTextEncoder.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlTextEncoder.cs @@ -110,7 +110,6 @@ namespace System.Xml _textWriter.Write(lowChar); } - [System.Security.SecurityCritical] internal void Write(char[] array, int offset, int count) { if (null == array) @@ -254,7 +253,6 @@ namespace System.Xml _textWriter.Write(';'); } - [System.Security.SecurityCritical] internal void Write(string text) { if (text == null) @@ -399,7 +397,6 @@ namespace System.Xml } } - [System.Security.SecurityCritical] internal void WriteRawWithSurrogateChecking(string text) { if (text == null) diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImpl.cs.REMOVED.git-id b/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImpl.cs.REMOVED.git-id index 71f0515ce1..ead2202dbb 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImpl.cs.REMOVED.git-id +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImpl.cs.REMOVED.git-id @@ -1 +1 @@ -f61ee01bc81ab91baf7cfa91e2518476df8748bb \ No newline at end of file +8f1515f42768a27a4ef6bf701bc3bdfed1a7c357 \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImplAsync.cs.REMOVED.git-id b/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImplAsync.cs.REMOVED.git-id index a74edbc14a..bece86a37e 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImplAsync.cs.REMOVED.git-id +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImplAsync.cs.REMOVED.git-id @@ -1 +1 @@ -4dc66f8de506c8032ca0c6634ccf946e2d4a3f72 \ No newline at end of file +f9f7084fd6a9ca92291c5ce9980c365e31ebba20 \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriterAsync.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriterAsync.cs index 52bfc857b4..f2bd47fa8f 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriterAsync.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlUtf8RawTextWriterAsync.cs @@ -554,7 +554,6 @@ namespace System.Xml // Serialize text that is part of an attribute value. The '&', '<', '>', and '"' characters // are entitized. - [SecuritySafeCritical] protected unsafe int WriteAttributeTextBlockNoFlush(char* pSrc, char* pSrcEnd) { char* pRaw = pSrc; @@ -660,7 +659,6 @@ namespace System.Xml return -1; } - [SecuritySafeCritical] protected unsafe int WriteAttributeTextBlockNoFlush(char[] chars, int index, int count) { if (count == 0) @@ -675,7 +673,6 @@ namespace System.Xml } } - [SecuritySafeCritical] protected unsafe int WriteAttributeTextBlockNoFlush(string text, int index, int count) { if (count == 0) @@ -742,7 +739,6 @@ namespace System.Xml // Serialize text that is part of element content. The '&', '<', and '>' characters // are entitized. - [SecuritySafeCritical] protected unsafe int WriteElementTextBlockNoFlush(char* pSrc, char* pSrcEnd, out bool needWriteNewLine) { needWriteNewLine = false; @@ -851,7 +847,6 @@ namespace System.Xml return -1; } - [SecuritySafeCritical] protected unsafe int WriteElementTextBlockNoFlush(char[] chars, int index, int count, out bool needWriteNewLine) { needWriteNewLine = false; @@ -868,7 +863,6 @@ namespace System.Xml } } - [SecuritySafeCritical] protected unsafe int WriteElementTextBlockNoFlush(string text, int index, int count, out bool needWriteNewLine) { needWriteNewLine = false; @@ -967,7 +961,6 @@ namespace System.Xml } while (writeLen >= 0 || needWriteNewLine); } - [SecuritySafeCritical] protected unsafe int RawTextNoFlush(char* pSrcBegin, char* pSrcEnd) { char* pRaw = pSrcBegin; @@ -1016,7 +1009,6 @@ namespace System.Xml return -1; } - [SecuritySafeCritical] protected unsafe int RawTextNoFlush(string text, int index, int count) { if (count == 0) @@ -1125,7 +1117,6 @@ namespace System.Xml } } - [SecuritySafeCritical] protected unsafe int WriteRawWithCharCheckingNoFlush(char* pSrcBegin, char* pSrcEnd, out bool needWriteNewLine) { needWriteNewLine = false; @@ -1221,7 +1212,6 @@ namespace System.Xml return -1; } - [SecuritySafeCritical] protected unsafe int WriteRawWithCharCheckingNoFlush(char[] chars, int index, int count, out bool needWriteNewLine) { needWriteNewLine = false; @@ -1237,7 +1227,6 @@ namespace System.Xml } } - [SecuritySafeCritical] protected unsafe int WriteRawWithCharCheckingNoFlush(string text, int index, int count, out bool needWriteNewLine) { needWriteNewLine = false; @@ -1301,7 +1290,6 @@ namespace System.Xml } while (writeLen >= 0 || needWriteNewLine); } - [SecuritySafeCritical] protected unsafe int WriteCommentOrPiNoFlush(string text, int index, int count, int stopChar, out bool needWriteNewLine) { needWriteNewLine = false; @@ -1471,7 +1459,6 @@ namespace System.Xml } while (writeLen >= 0 || needWriteNewLine); } - [SecuritySafeCritical] protected unsafe int WriteCDataSectionNoFlush(string text, int index, int count, out bool needWriteNewLine) { needWriteNewLine = false; diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriter.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriter.cs index 26c30405f4..1cc7f3a174 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriter.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriter.cs @@ -2122,7 +2122,6 @@ namespace System.Xml return s; } - [System.Security.SecuritySafeCritical] private unsafe void CheckNCName(string ncname) { Debug.Assert(ncname != null && ncname.Length > 0); diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Dom/XmlNode.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Dom/XmlNode.cs index fd52964ea8..61e60aa652 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Dom/XmlNode.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Dom/XmlNode.cs @@ -1423,7 +1423,7 @@ namespace System.Xml private object debuggerDisplayProxy { get { return new DebuggerDisplayXmlNodeProxy(this); } } [DebuggerDisplay("{ToString()}")] - internal struct DebuggerDisplayXmlNodeProxy + internal readonly struct DebuggerDisplayXmlNodeProxy { private readonly XmlNode _node; diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Extensions/ExtensionMethods.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Extensions/ExtensionMethods.cs index 1ffa891c21..9f968d634f 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Extensions/ExtensionMethods.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Extensions/ExtensionMethods.cs @@ -48,25 +48,6 @@ namespace System.Xml.Extensions return type.GetMethod(methodName, bindingFlags, null, parameterTypes, null); } -#if XMLSERIALIZERGENERATOR - internal static string ToBinHexString(byte[] inArray) - { - if (inArray == null) - { - throw new ArgumentNullException(nameof(inArray)); - } - return BinHexEncoder.Encode(inArray, 0, inArray.Length); - } - - internal static byte[] FromBinHexString(string s, bool allowOddCount) - { - if (s == null) - { - throw new ArgumentNullException(nameof(s)); - } - return BinHexDecoder.Decode(s.ToCharArray(), allowOddCount); - } -#endif #endregion internal static Uri ToUri(string s) diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/NameTable.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/NameTable.cs index 7f9d6f0057..2218a369e8 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/NameTable.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/NameTable.cs @@ -36,7 +36,6 @@ namespace System.Xml private Entry[] _entries; private int _count; private int _mask; - private int _hashCodeRandomizer; // // Constructor @@ -47,7 +46,6 @@ namespace System.Xml { _mask = 31; _entries = new Entry[_mask + 1]; - _hashCodeRandomizer = Environment.TickCount; } // @@ -68,20 +66,7 @@ namespace System.Xml return string.Empty; } - int hashCode; - unchecked - { - hashCode = len + _hashCodeRandomizer; - // use key.Length to eliminate the range check - for (int i = 0; i < key.Length; i++) - { - hashCode += (hashCode << 7) ^ key[i]; - } - // mix it a bit more - hashCode -= hashCode >> 17; - hashCode -= hashCode >> 11; - hashCode -= hashCode >> 5; - } + int hashCode = ComputeHash32(key); for (Entry e = _entries[hashCode & _mask]; e != null; e = e.next) { @@ -104,22 +89,22 @@ namespace System.Xml return string.Empty; } - int hashCode; - unchecked + // Compatibility check to ensure same exception as previous versions + // independently of any exceptions throw by the hashing function. + // note that NullReferenceException is the first one if key is null. + if (start >= key.Length || start < 0 || (long)start + len > (long)key.Length) { - hashCode = len + _hashCodeRandomizer; - hashCode += (hashCode << 7) ^ key[start]; // this will throw IndexOutOfRangeException in case the start index is invalid - int end = start + len; - for (int i = start + 1; i < end; i++) - { - hashCode += (hashCode << 7) ^ key[i]; - } - // mix it a bit more - hashCode -= hashCode >> 17; - hashCode -= hashCode >> 11; - hashCode -= hashCode >> 5; + throw new IndexOutOfRangeException(); } + // Compatibility check for len < 0, just throw the same exception as new string(key, start, len) + if (len < 0) + { + throw new ArgumentOutOfRangeException(); + } + + int hashCode = ComputeHash32(key, start, len); + for (Entry e = _entries[hashCode & _mask]; e != null; e = e.next) { if (e.hashCode == hashCode && TextEquals(e.str, key, start, len)) @@ -144,21 +129,7 @@ namespace System.Xml return string.Empty; } - int hashCode; - unchecked - { - int len = value.Length + _hashCodeRandomizer; - hashCode = len; - // use value.Length to eliminate the range check - for (int i = 0; i < value.Length; i++) - { - hashCode += (hashCode << 7) ^ value[i]; - } - // mix it a bit more - hashCode -= hashCode >> 17; - hashCode -= hashCode >> 11; - hashCode -= hashCode >> 5; - } + int hashCode = ComputeHash32(value); for (Entry e = _entries[hashCode & _mask]; e != null; e = e.next) { @@ -181,22 +152,19 @@ namespace System.Xml return string.Empty; } - int hashCode; - unchecked + if (start >= key.Length || start < 0 || (long)start + len > (long)key.Length) { - hashCode = len + _hashCodeRandomizer; - hashCode += (hashCode << 7) ^ key[start]; // this will throw IndexOutOfRangeException in case the start index is invalid - int end = start + len; - for (int i = start + 1; i < end; i++) - { - hashCode += (hashCode << 7) ^ key[i]; - } - // mix it a bit more - hashCode -= hashCode >> 17; - hashCode -= hashCode >> 11; - hashCode -= hashCode >> 5; + throw new IndexOutOfRangeException(); } + // Compatibility check for len < 0, just return null + if (len < 0) + { + return null; + } + + int hashCode = ComputeHash32(key, start, len); + for (Entry e = _entries[hashCode & _mask]; e != null; e = e.next) { if (e.hashCode == hashCode && TextEquals(e.str, key, start, len)) @@ -263,5 +231,17 @@ namespace System.Xml } return true; } + + private static int ComputeHash32(string key) + { + ReadOnlySpan bytes = key.AsReadOnlySpan().AsBytes(); + return Marvin.ComputeHash32(bytes, Marvin.DefaultSeed); + } + + private static int ComputeHash32(char[] key, int start, int len) + { + ReadOnlySpan bytes = key.AsReadOnlySpan().Slice(start, len).AsBytes(); + return Marvin.ComputeHash32(bytes, Marvin.DefaultSeed); + } } } diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Schema/Inference/XmlSchemaInferenceException.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Schema/Inference/XmlSchemaInferenceException.cs index c1aaaa3ea8..51152d3336 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Schema/Inference/XmlSchemaInferenceException.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Schema/Inference/XmlSchemaInferenceException.cs @@ -2,22 +2,24 @@ // 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.Resources; +using System.Runtime.Serialization; +using System.Text; +using System.Diagnostics; +using System.Globalization; + namespace System.Xml.Schema { - using System; - using System.IO; - using System.Resources; - using System.Runtime.Serialization; - using System.Text; - using System.Diagnostics; - using System.Globalization; - [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class XmlSchemaInferenceException : XmlSchemaException { protected XmlSchemaInferenceException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public override void GetObjectData(SerializationInfo info, StreamingContext context) @@ -52,6 +54,6 @@ namespace System.Xml.Schema { } } -} // namespace System.Xml.Schema +} diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Schema/XmlSchemaException.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Schema/XmlSchemaException.cs index 13fa157b06..79f3b03bac 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Schema/XmlSchemaException.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Schema/XmlSchemaException.cs @@ -2,18 +2,21 @@ // 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.Resources; +using System.Runtime.Serialization; +using System.Globalization; +using System.Diagnostics; + namespace System.Xml.Schema { - using System; - using System.IO; - using System.Text; - using System.Resources; - using System.Runtime.Serialization; - using System.Globalization; - using System.Diagnostics; - /// [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class XmlSchemaException : SystemException { private string _res; @@ -31,13 +34,44 @@ namespace System.Xml.Schema /// protected XmlSchemaException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); + _res = (string)info.GetValue("res", typeof(string)); + _args = (string[])info.GetValue("args", typeof(string[])); + _sourceUri = (string)info.GetValue("sourceUri", typeof(string)); + _lineNumber = (int)info.GetValue("lineNumber", typeof(int)); + _linePosition = (int)info.GetValue("linePosition", typeof(int)); + + // deserialize optional members + string version = null; + foreach (SerializationEntry e in info) + { + if (e.Name == "version") + { + version = (string)e.Value; + } + } + + if (version == null) + { + // deserializing V1 exception + _message = CreateMessage(_res, _args); + } + else + { + // deserializing V2 or higher exception -> exception message is serialized by the base class (Exception._message) + _message = null; + } } /// public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + info.AddValue("res", _res); + info.AddValue("args", _args); + info.AddValue("sourceUri", _sourceUri); + info.AddValue("lineNumber", _lineNumber); + info.AddValue("linePosition", _linePosition); + info.AddValue("version", "2.0"); } /// @@ -196,6 +230,6 @@ namespace System.Xml.Schema } } }; -} // namespace System.Xml.Schema +} diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Schema/XmlSchemaValidationException.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Schema/XmlSchemaValidationException.cs index 91a5f43552..21d2ccf14f 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Schema/XmlSchemaValidationException.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Schema/XmlSchemaValidationException.cs @@ -2,17 +2,20 @@ // 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.Resources; +using System.Runtime.Serialization; +using System.Diagnostics; + namespace System.Xml.Schema { - using System; - using System.IO; - using System.Text; - using System.Resources; - using System.Runtime.Serialization; - using System.Diagnostics; - /// [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class XmlSchemaValidationException : XmlSchemaException { private Object _sourceNodeObject; @@ -20,7 +23,6 @@ namespace System.Xml.Schema /// protected XmlSchemaValidationException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } /// diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/CodeGenerationoptions.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/CodeGenerationoptions.cs index ad75d457e4..fc25ba02f4 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/CodeGenerationoptions.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/CodeGenerationoptions.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System; using System.ComponentModel; @@ -17,11 +13,7 @@ namespace System.Xml.Serialization /// Specifies various flavours of XmlCodeExporter generated code. /// [Flags] -#if XMLSERIALIZERGENERATOR - internal enum CodeGenerationOptions -#else public enum CodeGenerationOptions -#endif { /// /// diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs index 0605e6b52f..77a75dc56e 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs @@ -8,11 +8,7 @@ using System.Resources; using System.Runtime.CompilerServices; #if !FEATURE_SERIALIZATION_UAPAOT -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System; using System.Collections; @@ -1675,4 +1671,4 @@ namespace System.Xml.Serialization } } } -#endif \ No newline at end of file +#endif diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/CodeIdentifier.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/CodeIdentifier.cs index 943a0e9ae0..24b8a8c889 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/CodeIdentifier.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/CodeIdentifier.cs @@ -10,21 +10,13 @@ using System.Globalization; using System.Diagnostics; using System.Reflection; -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { /// /// /// [To be supplied.] /// -#if XMLSERIALIZERGENERATOR - internal class CodeIdentifier -#else public class CodeIdentifier -#endif { internal const int MaxIdentifierLength = 511; diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/CodeIdentifiers.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/CodeIdentifiers.cs index a95a0b492c..eefbccdedb 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/CodeIdentifiers.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/CodeIdentifiers.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System; using System.Collections; @@ -38,11 +34,7 @@ namespace System.Xml.Serialization /// /// [To be supplied.] /// -#if XMLSERIALIZERGENERATOR - internal class CodeIdentifiers -#else public class CodeIdentifiers -#endif { private Hashtable _identifiers; private Hashtable _reservedIdentifiers; diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Compilation.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Compilation.cs index 73f1f8fe54..f4f650a0a7 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Compilation.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Compilation.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System.Configuration; using System.Reflection; @@ -77,7 +73,6 @@ namespace System.Xml.Serialization if (!containsSoapMapping && !TempAssembly.UseLegacySerializerGeneration) { -#if !XMLSERIALIZERGENERATOR try { _assembly = GenerateRefEmitAssembly(xmlMappings, types, defaultNamespace); @@ -89,7 +84,6 @@ namespace System.Xml.Serialization } // Add other known exceptions here... // -#endif } else { @@ -104,7 +98,8 @@ namespace System.Xml.Serialization #if DEBUG // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe - if (_assembly == null) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorDetails, "Failed to generate XmlSerializer assembly, but did not throw")); + if (_assembly == null) + throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorDetails, "Failed to generate XmlSerializer assembly, but did not throw")); #endif InitAssemblyMethods(xmlMappings); } @@ -172,14 +167,29 @@ namespace System.Xml.Serialization name.Name = serializerName; name.CodeBase = null; name.CultureInfo = CultureInfo.InvariantCulture; - string serializerPath = Path.Combine(Path.GetDirectoryName(type.Assembly.Location), serializerName + ".dll"); + + string serializerPath = null; + try { - serializer = Assembly.LoadFile(serializerPath); + if (!string.IsNullOrEmpty(type.Assembly.Location)) + { + serializerPath = Path.Combine(Path.GetDirectoryName(type.Assembly.Location), serializerName + ".dll"); + } + + if ((string.IsNullOrEmpty(serializerPath) || !File.Exists(serializerPath)) && !string.IsNullOrEmpty(Assembly.GetEntryAssembly().Location)) + { + serializerPath = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), serializerName + ".dll"); + } + + if (!string.IsNullOrEmpty(serializerPath)) + { + serializer = Assembly.LoadFile(serializerPath); + } } catch (Exception e) { - if (e is OutOfMemoryException) + if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) { throw; } @@ -190,8 +200,14 @@ namespace System.Xml.Serialization return null; } } + if (serializer == null) { + if (XmlSerializer.Mode == SerializationMode.PreGenOnly) + { + throw new Exception(SR.Format(SR.FailLoadAssemblyUnderPregenMode, serializerName)); + } + return null; } } @@ -232,7 +248,7 @@ namespace System.Xml.Serialization return null; } -#if XMLSERIALIZERGENERATOR +#if !FEATURE_SERIALIZATION_UAPAOT private static string GenerateAssemblyId(Type type) { Module[] modules = type.Assembly.GetModules(); @@ -401,9 +417,6 @@ namespace System.Xml.Serialization } } -#endif -#if !XMLSERIALIZERGENERATOR -#if !FEATURE_SERIALIZATION_UAPAOT [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification = "It is safe because the serialization assembly is generated by the framework code, not by the user.")] internal static Assembly GenerateRefEmitAssembly(XmlMapping[] xmlMappings, Type[] types, string defaultNamespace) { @@ -481,7 +494,6 @@ namespace System.Xml.Serialization return writerType.Assembly; } -#endif #endif private static MethodInfo GetMethodFromType(Type type, string methodName) @@ -499,7 +511,8 @@ namespace System.Xml.Serialization { typeName = GeneratedAssemblyNamespace + "." + typeName; Type type = assembly.GetType(typeName); - if (type == null) throw new InvalidOperationException(SR.Format(SR.XmlMissingType, typeName, assembly.FullName)); + if (type == null) + throw new InvalidOperationException(SR.Format(SR.XmlMissingType, typeName, assembly.FullName)); return type; } @@ -629,7 +642,8 @@ namespace System.Xml.Serialization public override bool Equals(object o) { TempAssemblyCacheKey key = o as TempAssemblyCacheKey; - if (key == null) return false; + if (key == null) + return false; return (key._type == _type && key._ns == _ns); } @@ -659,11 +673,18 @@ namespace System.Xml.Serialization lock (this) { TempAssembly tempAssembly; - if (_cache.TryGetValue(key, out tempAssembly) && tempAssembly == assembly) return; + if (_cache.TryGetValue(key, out tempAssembly) && tempAssembly == assembly) + return; _cache = new Dictionary(_cache); // clone _cache[key] = assembly; } } } + + internal static class ThisAssembly + { + internal const string Version = "1.0.0.0"; + internal const string InformationalVersion = "1.0.0.0"; + } } diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Compiler.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Compiler.cs index b4ce90598d..2ba30b7dc0 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Compiler.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Compiler.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System.Reflection; using System.Reflection.Emit; @@ -27,12 +23,10 @@ namespace System.Xml.Serialization internal class Compiler { -#if !XMLSERIALIZERGENERATOR private bool _debugEnabled = DiagnosticsSwitches.KeepTempFiles.Enabled; -#endif private StringWriter _writer = new StringWriter(CultureInfo.InvariantCulture); -#if XMLSERIALIZERGENERATOR +#if !FEATURE_SERIALIZATION_UAPAOT // SxS: This method does not take any resource name and does not expose any resources to the caller. // It's OK to suppress the SxS warning. internal void AddImport(Type type, Hashtable types) diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Globals.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Globals.cs index b3891a2f15..caaadb7e5c 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Globals.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Globals.cs @@ -8,11 +8,7 @@ using System.Reflection; using System; -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { internal static class Globals { diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/IXmlTextParser.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/IXmlTextParser.cs index 1a317459e0..fedf5bc9b9 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/IXmlTextParser.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/IXmlTextParser.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System.Xml; @@ -15,11 +11,7 @@ namespace System.Xml.Serialization /// /// This class is . /// -#if XMLSERIALIZERGENERATOR - internal interface IXmlTextParser -#else public interface IXmlTextParser -#endif { /// /// diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Mappings.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Mappings.cs index 057f40be3b..bdc35deb2f 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Mappings.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Mappings.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System.Reflection; using System.Collections; @@ -1247,10 +1243,8 @@ namespace System.Xml.Serialization XmlSchemaObjectTable elements = null; if (Schema != null && Schema.TargetNamespace == elementNs) { -#if !XMLSERIALIZERGENERATOR XmlSchemas.Preprocess(Schema); elements = Schema.Elements; -#endif } else if (Schemas != null) { @@ -1337,7 +1331,7 @@ namespace System.Xml.Serialization // make sure that user-specified schemas are valid _schemas.ValidationEventHandler += new ValidationEventHandler(ValidationCallbackWithErrorCode); _schemas.Compile(); -#if !XMLSERIALIZERGENERATOR + // at this point we verified that the information returned by the IXmlSerializable is valid // Now check to see if the type was referenced before: // UNDONE check for the duplcate types @@ -1369,7 +1363,6 @@ namespace System.Xml.Serialization _xsdType = _xsdType.Redefined != null ? _xsdType.Redefined : _xsdType; } } -#endif } else { diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Models.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Models.cs index d5bb2211a1..26735266ea 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Models.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Models.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System; using System.Reflection; diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/NameTable.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/NameTable.cs index a07d4dc2b7..65e907ae5a 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/NameTable.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/NameTable.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System.Xml; using System; diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/PrimitiveXmlSerializers.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/PrimitiveXmlSerializers.cs index 5da34c0437..8ab90c2f82 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/PrimitiveXmlSerializers.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/PrimitiveXmlSerializers.cs @@ -4,15 +4,9 @@ using System; -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -{ - internal class XmlSerializationPrimitiveWriter : Microsoft.XmlSerializer.Generator.XmlSerializationWriter -#else namespace System.Xml.Serialization { internal class XmlSerializationPrimitiveWriter : System.Xml.Serialization.XmlSerializationWriter -#endif { internal void Write_string(object o) { @@ -236,7 +230,6 @@ namespace System.Xml.Serialization } } -#if !XMLSERIALIZERGENERATOR internal class XmlSerializationPrimitiveReader : System.Xml.Serialization.XmlSerializationReader { internal object Read_string() @@ -759,5 +752,4 @@ namespace System.Xml.Serialization _id1_QName = Reader.NameTable.Add(@"QName"); } } -#endif } diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs index 4b14059242..88fc758431 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs @@ -19,11 +19,7 @@ using System.Xml.Serialization; using System.Linq.Expressions; using System.Collections.Concurrent; -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { internal delegate void UnknownNodeAction(object o); diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs index a19d017d6d..86377e0a2a 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs @@ -13,11 +13,7 @@ using System.Threading.Tasks; using System.Xml.Schema; using System.Xml; -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { internal class ReflectionXmlSerializationWriter : XmlSerializationWriter { diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/SoapReflectionImporter.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/SoapReflectionImporter.cs index f1bd443a62..05838f6828 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/SoapReflectionImporter.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/SoapReflectionImporter.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System.Reflection; using System; @@ -23,11 +19,7 @@ namespace System.Xml.Serialization /// /// [To be supplied.] /// -#if XMLSERIALIZERGENERATOR - internal class SoapReflectionImporter -#else public class SoapReflectionImporter -#endif { private TypeScope _typeScope; private SoapAttributeOverrides _attributeOverrides; diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/SoapSchemamember.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/SoapSchemamember.cs index 1f056c0d5a..7840b9eb05 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/SoapSchemamember.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/SoapSchemamember.cs @@ -2,22 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System; using System.Xml; /// /// -#if XMLSERIALIZERGENERATOR - internal class SoapSchemaMember -#else public class SoapSchemaMember -#endif { private string _memberName; private XmlQualifiedName _type = XmlQualifiedName.Empty; diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/SourceInfo.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/SourceInfo.cs index bedf52a1c5..77059f1316 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/SourceInfo.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/SourceInfo.cs @@ -11,11 +11,7 @@ using System.Text.RegularExpressions; using System.Xml.Extensions; #if !FEATURE_SERIALIZATION_UAPAOT -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { internal class SourceInfo { @@ -267,4 +263,4 @@ namespace System.Xml.Serialization } } } -#endif \ No newline at end of file +#endif diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/TypeCode.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/TypeCode.cs index 45be3c59bf..f55392df09 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/TypeCode.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/TypeCode.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System; diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/TypeExtensions.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/TypeExtensions.cs index 5e98bdd677..7bee06dc2b 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/TypeExtensions.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/TypeExtensions.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System; using System.Reflection; diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Types.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Types.cs index 5d0e51d811..079ec5e661 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Types.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Types.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System; using System.IO; @@ -736,7 +732,7 @@ namespace System.Xml.Serialization return typeDesc; } -#if XMLSERIALIZERGENERATOR +#if !FEATURE_SERIALIZATION_UAPAOT internal TypeMapping GetTypeMappingFromTypeDesc(TypeDesc typeDesc) { foreach (TypeMapping typeMapping in TypeMappings) @@ -1392,7 +1388,6 @@ namespace System.Xml.Serialization name = type.Substring(nsLen + 1, nameLen - nsLen - 1); dims = type.Substring(nameLen); -#if !XMLSERIALIZERGENERATOR // parent is not null only in the case when we used XmlSchema.Read(), // in which case we need to fixup the wsdl:arayType attribute value while (parent != null) @@ -1409,7 +1404,6 @@ namespace System.Xml.Serialization parent = parent.Parent; } -#endif return new XmlQualifiedName(name, ns); } diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlAttributeOverrides.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlAttributeOverrides.cs index d24e2666ae..2c0465d99b 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlAttributeOverrides.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlAttributeOverrides.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System.Reflection; using System.Collections; @@ -19,11 +15,7 @@ namespace System.Xml.Serialization /// /// [To be supplied.] /// -#if XMLSERIALIZERGENERATOR - internal class XmlAttributeOverrides -#else public class XmlAttributeOverrides -#endif { private readonly Dictionary> _types = new Dictionary>(); diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlAttributes.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlAttributes.cs index eea7dd42b7..e5885dcaef 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlAttributes.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlAttributes.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System; using System.Reflection; @@ -35,11 +31,7 @@ namespace System.Xml.Serialization /// /// [To be supplied.] /// -#if XMLSERIALIZERGENERATOR - internal class XmlAttributes -#else public class XmlAttributes -#endif { private XmlElementAttributes _xmlElements = new XmlElementAttributes(); private XmlArrayItemAttributes _xmlArrayItems = new XmlArrayItemAttributes(); diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlCountingReader.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlCountingReader.cs index 248947dd5e..8c9d08ff67 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlCountingReader.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlCountingReader.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System.IO; using System.Collections; diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlMapping.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlMapping.cs index f44af553e5..1e77cef8f3 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlMapping.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlMapping.cs @@ -9,18 +9,10 @@ using System.Reflection; using System.Xml.Serialization; -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { [Flags] -#if XMLSERIALIZERGENERATOR - internal enum XmlMappingAccess -#else public enum XmlMappingAccess -#endif { None = 0x00, Read = 0x01, @@ -31,11 +23,7 @@ namespace System.Xml.Serialization /// /// [To be supplied.] /// -#if XMLSERIALIZERGENERATOR - internal abstract class XmlMapping -#else public abstract class XmlMapping -#endif { private TypeScope _scope; private bool _generateSerializer = false; @@ -72,11 +60,7 @@ namespace System.Xml.Serialization /// public string ElementName { -#if XMLSERIALIZERGENERATOR - get { return Microsoft.XmlSerializer.Generator.Accessor.UnescapeName(Accessor.Name); } -#else get { return System.Xml.Serialization.Accessor.UnescapeName(Accessor.Name); } -#endif } /// diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlMemberMapping.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlMemberMapping.cs index 44cfa83562..6b06c25a60 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlMemberMapping.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlMemberMapping.cs @@ -6,18 +6,10 @@ using System.Reflection; using System; -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { /// -#if XMLSERIALIZERGENERATOR - internal class XmlMemberMapping -#else public class XmlMemberMapping -#endif { private MemberMapping _mapping; diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlMembersMapping.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlMembersMapping.cs index 5578e133f5..d9ba799d2d 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlMembersMapping.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlMembersMapping.cs @@ -7,21 +7,13 @@ using System; using System.Text; -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { /// /// /// [To be supplied.] /// -#if XMLSERIALIZERGENERATOR - internal class XmlMembersMapping : XmlMapping -#else public class XmlMembersMapping : XmlMapping -#endif { private XmlMemberMapping[] _mappings; diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlReflectionImporter.cs.REMOVED.git-id b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlReflectionImporter.cs.REMOVED.git-id index ad84bf754e..e32ddb4f1e 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlReflectionImporter.cs.REMOVED.git-id +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlReflectionImporter.cs.REMOVED.git-id @@ -1 +1 @@ -d131f471f55694f3d6068c6a29640a9eeb8e789d \ No newline at end of file +94bbcefb24d7ed5279e0c60f4979aaddd8b3c67a \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlReflectionMember.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlReflectionMember.cs index 2ef1c0373e..8d59d700ff 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlReflectionMember.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlReflectionMember.cs @@ -6,21 +6,13 @@ using System; using System.Xml.Serialization; -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { /// /// /// [To be supplied.] /// -#if XMLSERIALIZERGENERATOR - internal class XmlReflectionMember -#else public class XmlReflectionMember -#endif { private string _memberName; private Type _type; diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationGeneratedCode.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationGeneratedCode.cs index caabe4b045..8a453d4642 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationGeneratedCode.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationGeneratedCode.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System; using System.IO; @@ -18,11 +14,7 @@ namespace System.Xml.Serialization using System.Globalization; /// -#if XMLSERIALIZERGENERATOR - internal abstract class XmlSerializationGeneratedCode -#else public abstract class XmlSerializationGeneratedCode -#endif { internal void Init(TempAssembly tempAssembly) { @@ -33,4 +25,387 @@ namespace System.Xml.Serialization { } } +#if !FEATURE_SERIALIZATION_UAPAOT + internal class XmlSerializationCodeGen + { + private IndentedWriter _writer; + private int _nextMethodNumber = 0; + private Hashtable _methodNames = new Hashtable(); + private ReflectionAwareCodeGen _raCodeGen; + private TypeScope[] _scopes; + private TypeDesc _stringTypeDesc = null; + private TypeDesc _qnameTypeDesc = null; + private string _access; + private string _className; + private TypeMapping[] _referencedMethods; + private int _references = 0; + private Hashtable _generatedMethods = new Hashtable(); + + internal XmlSerializationCodeGen(IndentedWriter writer, TypeScope[] scopes, string access, string className) + { + _writer = writer; + _scopes = scopes; + if (scopes.Length > 0) + { + _stringTypeDesc = scopes[0].GetTypeDesc(typeof(string)); + _qnameTypeDesc = scopes[0].GetTypeDesc(typeof(XmlQualifiedName)); + } + _raCodeGen = new ReflectionAwareCodeGen(writer); + _className = className; + _access = access; + } + + internal IndentedWriter Writer { get { return _writer; } } + internal int NextMethodNumber { get { return _nextMethodNumber; } set { _nextMethodNumber = value; } } + internal ReflectionAwareCodeGen RaCodeGen { get { return _raCodeGen; } } + internal TypeDesc StringTypeDesc { get { return _stringTypeDesc; } } + internal TypeDesc QnameTypeDesc { get { return _qnameTypeDesc; } } + internal string ClassName { get { return _className; } } + internal string Access { get { return _access; } } + internal TypeScope[] Scopes { get { return _scopes; } } + internal Hashtable MethodNames { get { return _methodNames; } } + internal Hashtable GeneratedMethods { get { return _generatedMethods; } } + + internal virtual void GenerateMethod(TypeMapping mapping) { } + + internal void GenerateReferencedMethods() + { + while (_references > 0) + { + TypeMapping mapping = _referencedMethods[--_references]; + GenerateMethod(mapping); + } + } + + internal string ReferenceMapping(TypeMapping mapping) + { + if (!mapping.IsSoap) + { + if (_generatedMethods[mapping] == null) + { + _referencedMethods = EnsureArrayIndex(_referencedMethods, _references); + _referencedMethods[_references++] = mapping; + } + } + return (string)_methodNames[mapping]; + } + + private TypeMapping[] EnsureArrayIndex(TypeMapping[] a, int index) + { + if (a == null) return new TypeMapping[32]; + if (index < a.Length) return a; + TypeMapping[] b = new TypeMapping[a.Length + 32]; + Array.Copy(a, b, index); + return b; + } + + internal void WriteQuotedCSharpString(string value) + { + _raCodeGen.WriteQuotedCSharpString(value); + } + + internal void GenerateHashtableGetBegin(string privateName, string publicName) + { + _writer.Write(typeof(Hashtable).FullName); + _writer.Write(" "); + _writer.Write(privateName); + _writer.WriteLine(" = null;"); + _writer.Write("public override "); + _writer.Write(typeof(Hashtable).FullName); + + _writer.Write(" "); + _writer.Write(publicName); + _writer.WriteLine(" {"); + _writer.Indent++; + + _writer.WriteLine("get {"); + _writer.Indent++; + + _writer.Write("if ("); + _writer.Write(privateName); + _writer.WriteLine(" == null) {"); + _writer.Indent++; + + _writer.Write(typeof(Hashtable).FullName); + _writer.Write(" _tmp = new "); + _writer.Write(typeof(Hashtable).FullName); + _writer.WriteLine("();"); + } + + internal void GenerateHashtableGetEnd(string privateName) + { + _writer.Write("if ("); + _writer.Write(privateName); + _writer.Write(" == null) "); + _writer.Write(privateName); + _writer.WriteLine(" = _tmp;"); + _writer.Indent--; + _writer.WriteLine("}"); + + _writer.Write("return "); + _writer.Write(privateName); + _writer.WriteLine(";"); + _writer.Indent--; + _writer.WriteLine("}"); + + _writer.Indent--; + _writer.WriteLine("}"); + } + internal void GeneratePublicMethods(string privateName, string publicName, string[] methods, XmlMapping[] xmlMappings) + { + GenerateHashtableGetBegin(privateName, publicName); + if (methods != null && methods.Length != 0 && xmlMappings != null && xmlMappings.Length == methods.Length) + { + for (int i = 0; i < methods.Length; i++) + { + if (methods[i] == null) + continue; + _writer.Write("_tmp["); + WriteQuotedCSharpString(xmlMappings[i].Key); + _writer.Write("] = "); + WriteQuotedCSharpString(methods[i]); + _writer.WriteLine(";"); + } + } + GenerateHashtableGetEnd(privateName); + } + + internal void GenerateSupportedTypes(Type[] types) + { + _writer.Write("public override "); + _writer.Write(typeof(bool).FullName); + _writer.Write(" CanSerialize("); + _writer.Write(typeof(Type).FullName); + _writer.WriteLine(" type) {"); + _writer.Indent++; + Hashtable uniqueTypes = new Hashtable(); + for (int i = 0; i < types.Length; i++) + { + Type type = types[i]; + + if (type == null) + continue; + if (!type.IsPublic && !type.IsNestedPublic) + continue; + if (uniqueTypes[type] != null) + continue; + if (DynamicAssemblies.IsTypeDynamic(type)) + continue; + if (type.IsGenericType || type.ContainsGenericParameters && DynamicAssemblies.IsTypeDynamic(type.GetGenericArguments())) + continue; + uniqueTypes[type] = type; + _writer.Write("if (type == typeof("); + _writer.Write(CodeIdentifier.GetCSharpName(type)); + _writer.WriteLine(")) return true;"); + } + _writer.WriteLine("return false;"); + _writer.Indent--; + _writer.WriteLine("}"); + } + + internal string GenerateBaseSerializer(string baseSerializer, string readerClass, string writerClass, CodeIdentifiers classes) + { + baseSerializer = CodeIdentifier.MakeValid(baseSerializer); + baseSerializer = classes.AddUnique(baseSerializer, baseSerializer); + + _writer.WriteLine(); + _writer.Write("public abstract class "); + _writer.Write(CodeIdentifier.GetCSharpName(baseSerializer)); + _writer.Write(" : "); + _writer.Write(typeof(System.Xml.Serialization.XmlSerializer).FullName); + _writer.WriteLine(" {"); + _writer.Indent++; + + _writer.Write("protected override "); + _writer.Write(typeof(System.Xml.Serialization.XmlSerializationReader).FullName); + _writer.WriteLine(" CreateReader() {"); + _writer.Indent++; + _writer.Write("return new "); + _writer.Write(readerClass); + _writer.WriteLine("();"); + _writer.Indent--; + _writer.WriteLine("}"); + + _writer.Write("protected override "); + _writer.Write(typeof(System.Xml.Serialization.XmlSerializationWriter).FullName); + _writer.WriteLine(" CreateWriter() {"); + _writer.Indent++; + _writer.Write("return new "); + _writer.Write(writerClass); + _writer.WriteLine("();"); + _writer.Indent--; + _writer.WriteLine("}"); + + _writer.Indent--; + _writer.WriteLine("}"); + + return baseSerializer; + } + + internal string GenerateTypedSerializer(string readMethod, string writeMethod, XmlMapping mapping, CodeIdentifiers classes, string baseSerializer, string readerClass, string writerClass) + { + string serializerName = CodeIdentifier.MakeValid(Accessor.UnescapeName(mapping.Accessor.Mapping.TypeDesc.Name)); + serializerName = classes.AddUnique(serializerName + "Serializer", mapping); + + _writer.WriteLine(); + _writer.Write("public sealed class "); + _writer.Write(CodeIdentifier.GetCSharpName(serializerName)); + _writer.Write(" : "); + _writer.Write(baseSerializer); + _writer.WriteLine(" {"); + _writer.Indent++; + + _writer.WriteLine(); + _writer.Write("public override "); + _writer.Write(typeof(bool).FullName); + _writer.Write(" CanDeserialize("); + _writer.Write(typeof(XmlReader).FullName); + _writer.WriteLine(" xmlReader) {"); + _writer.Indent++; + + if (mapping.Accessor.Any) + { + _writer.WriteLine("return true;"); + } + else + { + _writer.Write("return xmlReader.IsStartElement("); + WriteQuotedCSharpString(mapping.Accessor.Name); + _writer.Write(", "); + WriteQuotedCSharpString(mapping.Accessor.Namespace); + _writer.WriteLine(");"); + } + _writer.Indent--; + _writer.WriteLine("}"); + + if (writeMethod != null) + { + _writer.WriteLine(); + _writer.Write("protected override void Serialize(object objectToSerialize, "); + _writer.Write(typeof(System.Xml.Serialization.XmlSerializationWriter).FullName); + _writer.WriteLine(" writer) {"); + _writer.Indent++; + _writer.Write("(("); + _writer.Write(writerClass); + _writer.Write(")writer)."); + _writer.Write(writeMethod); + _writer.Write("("); + if (mapping is XmlMembersMapping) + { + _writer.Write("(object[])"); + } + _writer.WriteLine("objectToSerialize);"); + _writer.Indent--; + _writer.WriteLine("}"); + } + if (readMethod != null) + { + _writer.WriteLine(); + _writer.Write("protected override object Deserialize("); + _writer.Write(typeof(System.Xml.Serialization.XmlSerializationReader).FullName); + _writer.WriteLine(" reader) {"); + _writer.Indent++; + _writer.Write("return (("); + _writer.Write(readerClass); + _writer.Write(")reader)."); + _writer.Write(readMethod); + _writer.WriteLine("();"); + _writer.Indent--; + _writer.WriteLine("}"); + } + _writer.Indent--; + _writer.WriteLine("}"); + + return serializerName; + } + + private void GenerateTypedSerializers(Hashtable serializers) + { + string privateName = "typedSerializers"; + GenerateHashtableGetBegin(privateName, "TypedSerializers"); + + foreach (string key in serializers.Keys) + { + _writer.Write("_tmp.Add("); + WriteQuotedCSharpString(key); + _writer.Write(", new "); + _writer.Write((string)serializers[key]); + _writer.WriteLine("());"); + } + GenerateHashtableGetEnd("typedSerializers"); + } + + //GenerateGetSerializer(serializers, xmlMappings); + private void GenerateGetSerializer(Hashtable serializers, XmlMapping[] xmlMappings) + { + _writer.Write("public override "); + _writer.Write(typeof(System.Xml.Serialization.XmlSerializer).FullName); + _writer.Write(" GetSerializer("); + _writer.Write(typeof(Type).FullName); + _writer.WriteLine(" type) {"); + _writer.Indent++; + + for (int i = 0; i < xmlMappings.Length; i++) + { + if (xmlMappings[i] is XmlTypeMapping) + { + Type type = xmlMappings[i].Accessor.Mapping.TypeDesc.Type; + if (type == null) + continue; + if (!type.IsPublic && !type.IsNestedPublic) + continue; + if (DynamicAssemblies.IsTypeDynamic(type)) + continue; + if (type.IsGenericType || type.ContainsGenericParameters && DynamicAssemblies.IsTypeDynamic(type.GetGenericArguments())) + continue; + _writer.Write("if (type == typeof("); + _writer.Write(CodeIdentifier.GetCSharpName(type)); + _writer.Write(")) return new "); + _writer.Write((string)serializers[xmlMappings[i].Key]); + _writer.WriteLine("();"); + } + } + _writer.WriteLine("return null;"); + _writer.Indent--; + _writer.WriteLine("}"); + } + + internal void GenerateSerializerContract(string className, XmlMapping[] xmlMappings, Type[] types, string readerType, string[] readMethods, string writerType, string[] writerMethods, Hashtable serializers) + { + _writer.WriteLine(); + _writer.Write("public class XmlSerializerContract : global::"); + _writer.Write(typeof(System.Xml.Serialization.XmlSerializerImplementation).FullName); + _writer.WriteLine(" {"); + _writer.Indent++; + + _writer.Write("public override global::"); + _writer.Write(typeof(System.Xml.Serialization.XmlSerializationReader).FullName); + _writer.Write(" Reader { get { return new "); + _writer.Write(readerType); + _writer.WriteLine("(); } }"); + + _writer.Write("public override global::"); + _writer.Write(typeof(System.Xml.Serialization.XmlSerializationWriter).FullName); + _writer.Write(" Writer { get { return new "); + _writer.Write(writerType); + _writer.WriteLine("(); } }"); + + GeneratePublicMethods(nameof(readMethods), "ReadMethods", readMethods, xmlMappings); + GeneratePublicMethods("writeMethods", "WriteMethods", writerMethods, xmlMappings); + GenerateTypedSerializers(serializers); + GenerateSupportedTypes(types); + GenerateGetSerializer(serializers, xmlMappings); + + _writer.Indent--; + _writer.WriteLine("}"); + } + + internal static bool IsWildcard(SpecialMapping mapping) + { + if (mapping is SerializableMapping) + return ((SerializableMapping)mapping).IsAny; + return mapping.TypeDesc.CanBeElementValue; + } + } +#endif } diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs deleted file mode 100644 index 8229bf3465..0000000000 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs +++ /dev/null @@ -1,2166 +0,0 @@ -// 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. - -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else -namespace System.Xml.Serialization -#endif -{ - using System.IO; - using System; - using System.Security; - using System.Collections; - using System.Reflection; - using System.Text; - using System.Xml; - using System.Xml.Schema; - using System.ComponentModel; - using System.Globalization; - using System.Diagnostics; - using System.Threading; - using System.Configuration; - using System.Xml.Serialization.Configuration; - - /// -#if XMLSERIALIZERGENERATOR - internal abstract class XmlSerializationReader : XmlSerializationGeneratedCode -#else - public abstract class XmlSerializationReader : XmlSerializationGeneratedCode -#endif - { - private XmlReader _r; - private XmlCountingReader _countingReader; - private XmlDocument _d; - private Hashtable _callbacks; - private Hashtable _types; - private Hashtable _typesReverse; - private XmlDeserializationEvents _events; - private Hashtable _targets; - private Hashtable _referencedTargets; - private ArrayList _targetsWithoutIds; - private ArrayList _fixups; - private ArrayList _collectionFixups; - private bool _soap12; - private bool _isReturnValue; - private bool _decodeName = true; - - private string _schemaNsID; - private string _schemaNs1999ID; - private string _schemaNs2000ID; - private string _schemaNonXsdTypesNsID; - private string _instanceNsID; - private string _instanceNs2000ID; - private string _instanceNs1999ID; - private string _soapNsID; - private string _soap12NsID; - private string _schemaID; - private string _wsdlNsID; - private string _wsdlArrayTypeID; - private string _nullID; - private string _nilID; - private string _typeID; - private string _arrayTypeID; - private string _itemTypeID; - private string _arraySizeID; - private string _arrayID; - private string _urTypeID; - private string _stringID; - private string _intID; - private string _booleanID; - private string _shortID; - private string _longID; - private string _floatID; - private string _doubleID; - private string _decimalID; - private string _dateTimeID; - private string _qnameID; - private string _dateID; - private string _timeID; - private string _hexBinaryID; - private string _base64BinaryID; - private string _base64ID; - private string _unsignedByteID; - private string _byteID; - private string _unsignedShortID; - private string _unsignedIntID; - private string _unsignedLongID; - private string _oldDecimalID; - private string _oldTimeInstantID; - - private string _anyURIID; - private string _durationID; - private string _ENTITYID; - private string _ENTITIESID; - private string _gDayID; - private string _gMonthID; - private string _gMonthDayID; - private string _gYearID; - private string _gYearMonthID; - private string _IDID; - private string _IDREFID; - private string _IDREFSID; - private string _integerID; - private string _languageID; - private string _nameID; - private string _NCNameID; - private string _NMTOKENID; - private string _NMTOKENSID; - private string _negativeIntegerID; - private string _nonPositiveIntegerID; - private string _nonNegativeIntegerID; - private string _normalizedStringID; - private string _NOTATIONID; - private string _positiveIntegerID; - private string _tokenID; - - private string _charID; - private string _guidID; - private string _timeSpanID; - - private static bool s_checkDeserializeAdvances; - - protected abstract void InitIDs(); - -#if FEATURE_SERIALIZATION_UAPAOT - // this method must be called before any generated deserialization methods are called - internal void Init(XmlReader r, XmlDeserializationEvents events, string encodingStyle) - { - _events = events; - _r = r; - _soap12 = (encodingStyle == Soap12.Encoding); - - _schemaNsID = r.NameTable.Add(XmlSchema.Namespace); - _schemaNs2000ID = r.NameTable.Add("http://www.w3.org/2000/10/XMLSchema"); - _schemaNs1999ID = r.NameTable.Add("http://www.w3.org/1999/XMLSchema"); - _schemaNonXsdTypesNsID = r.NameTable.Add(UrtTypes.Namespace); - _instanceNsID = r.NameTable.Add(XmlSchema.InstanceNamespace); - _instanceNs2000ID = r.NameTable.Add("http://www.w3.org/2000/10/XMLSchema-instance"); - _instanceNs1999ID = r.NameTable.Add("http://www.w3.org/1999/XMLSchema-instance"); - _soapNsID = r.NameTable.Add(Soap.Encoding); - _soap12NsID = r.NameTable.Add(Soap12.Encoding); - _schemaID = r.NameTable.Add("schema"); - _wsdlNsID = r.NameTable.Add(Wsdl.Namespace); - _wsdlArrayTypeID = r.NameTable.Add(Wsdl.ArrayType); - _nullID = r.NameTable.Add("null"); - _nilID = r.NameTable.Add("nil"); - _typeID = r.NameTable.Add("type"); - _arrayTypeID = r.NameTable.Add("arrayType"); - _itemTypeID = r.NameTable.Add("itemType"); - _arraySizeID = r.NameTable.Add("arraySize"); - _arrayID = r.NameTable.Add("Array"); - _urTypeID = r.NameTable.Add(Soap.UrType); - InitIDs(); - } -#endif - - // this method must be called before any generated deserialization methods are called - internal void Init(XmlReader r, XmlDeserializationEvents events, string encodingStyle, TempAssembly tempAssembly) - { - _events = events; - if (s_checkDeserializeAdvances) - { - _countingReader = new XmlCountingReader(r); - _r = _countingReader; - } - else - _r = r; - _d = null; - _soap12 = (encodingStyle == Soap12.Encoding); - Init(tempAssembly); - - _schemaNsID = r.NameTable.Add(XmlSchema.Namespace); - _schemaNs2000ID = r.NameTable.Add("http://www.w3.org/2000/10/XMLSchema"); - _schemaNs1999ID = r.NameTable.Add("http://www.w3.org/1999/XMLSchema"); - _schemaNonXsdTypesNsID = r.NameTable.Add(UrtTypes.Namespace); - _instanceNsID = r.NameTable.Add(XmlSchema.InstanceNamespace); - _instanceNs2000ID = r.NameTable.Add("http://www.w3.org/2000/10/XMLSchema-instance"); - _instanceNs1999ID = r.NameTable.Add("http://www.w3.org/1999/XMLSchema-instance"); - _soapNsID = r.NameTable.Add(Soap.Encoding); - _soap12NsID = r.NameTable.Add(Soap12.Encoding); - _schemaID = r.NameTable.Add("schema"); - _wsdlNsID = r.NameTable.Add(Wsdl.Namespace); - _wsdlArrayTypeID = r.NameTable.Add(Wsdl.ArrayType); - _nullID = r.NameTable.Add("null"); - _nilID = r.NameTable.Add("nil"); - _typeID = r.NameTable.Add("type"); - _arrayTypeID = r.NameTable.Add("arrayType"); - _itemTypeID = r.NameTable.Add("itemType"); - _arraySizeID = r.NameTable.Add("arraySize"); - _arrayID = r.NameTable.Add("Array"); - _urTypeID = r.NameTable.Add(Soap.UrType); - InitIDs(); - } - -#if !XMLSERIALIZERGENERATOR - protected bool DecodeName - { - get - { - return _decodeName; - } - set - { - _decodeName = value; - } - } - - protected XmlReader Reader - { - get - { - return _r; - } - } - - protected int ReaderCount - { - get - { - return s_checkDeserializeAdvances ? _countingReader.AdvanceCount : 0; - } - } - - protected XmlDocument Document - { - get - { - if (_d == null) - { - _d = new XmlDocument(_r.NameTable); - _d.SetBaseURI(_r.BaseURI); - } - return _d; - } - } - - /// - protected static Assembly ResolveDynamicAssembly(string assemblyFullName) - { - return DynamicAssemblies.Get(assemblyFullName); - } - - private void InitPrimitiveIDs() - { - if (_tokenID != null) return; - object ns = _r.NameTable.Add(XmlSchema.Namespace); - object ns2 = _r.NameTable.Add(UrtTypes.Namespace); - - _stringID = _r.NameTable.Add("string"); - _intID = _r.NameTable.Add("int"); - _booleanID = _r.NameTable.Add("boolean"); - _shortID = _r.NameTable.Add("short"); - _longID = _r.NameTable.Add("long"); - _floatID = _r.NameTable.Add("float"); - _doubleID = _r.NameTable.Add("double"); - _decimalID = _r.NameTable.Add("decimal"); - _dateTimeID = _r.NameTable.Add("dateTime"); - _qnameID = _r.NameTable.Add("QName"); - _dateID = _r.NameTable.Add("date"); - _timeID = _r.NameTable.Add("time"); - _hexBinaryID = _r.NameTable.Add("hexBinary"); - _base64BinaryID = _r.NameTable.Add("base64Binary"); - _unsignedByteID = _r.NameTable.Add("unsignedByte"); - _byteID = _r.NameTable.Add("byte"); - _unsignedShortID = _r.NameTable.Add("unsignedShort"); - _unsignedIntID = _r.NameTable.Add("unsignedInt"); - _unsignedLongID = _r.NameTable.Add("unsignedLong"); - _oldDecimalID = _r.NameTable.Add("decimal"); - _oldTimeInstantID = _r.NameTable.Add("timeInstant"); - _charID = _r.NameTable.Add("char"); - _guidID = _r.NameTable.Add("guid"); - _timeSpanID = _r.NameTable.Add("TimeSpan"); - _base64ID = _r.NameTable.Add("base64"); - - _anyURIID = _r.NameTable.Add("anyURI"); - _durationID = _r.NameTable.Add("duration"); - _ENTITYID = _r.NameTable.Add("ENTITY"); - _ENTITIESID = _r.NameTable.Add("ENTITIES"); - _gDayID = _r.NameTable.Add("gDay"); - _gMonthID = _r.NameTable.Add("gMonth"); - _gMonthDayID = _r.NameTable.Add("gMonthDay"); - _gYearID = _r.NameTable.Add("gYear"); - _gYearMonthID = _r.NameTable.Add("gYearMonth"); - _IDID = _r.NameTable.Add("ID"); - _IDREFID = _r.NameTable.Add("IDREF"); - _IDREFSID = _r.NameTable.Add("IDREFS"); - _integerID = _r.NameTable.Add("integer"); - _languageID = _r.NameTable.Add("language"); - _nameID = _r.NameTable.Add("Name"); - _NCNameID = _r.NameTable.Add("NCName"); - _NMTOKENID = _r.NameTable.Add("NMTOKEN"); - _NMTOKENSID = _r.NameTable.Add("NMTOKENS"); - _negativeIntegerID = _r.NameTable.Add("negativeInteger"); - _nonNegativeIntegerID = _r.NameTable.Add("nonNegativeInteger"); - _nonPositiveIntegerID = _r.NameTable.Add("nonPositiveInteger"); - _normalizedStringID = _r.NameTable.Add("normalizedString"); - _NOTATIONID = _r.NameTable.Add("NOTATION"); - _positiveIntegerID = _r.NameTable.Add("positiveInteger"); - _tokenID = _r.NameTable.Add("token"); - } - - /// - /// [To be supplied.] - /// - protected XmlQualifiedName GetXsiType() - { - string type = _r.GetAttribute(_typeID, _instanceNsID); - if (type == null) - { - type = _r.GetAttribute(_typeID, _instanceNs2000ID); - if (type == null) - { - type = _r.GetAttribute(_typeID, _instanceNs1999ID); - if (type == null) - return null; - } - } - return ToXmlQualifiedName(type, false); - } - - // throwOnUnknown flag controls whether this method throws an exception or just returns - // null if typeName.Namespace is unknown. the method still throws if typeName.Namespace - // is recognized but typeName.Name isn't. - private Type GetPrimitiveType(XmlQualifiedName typeName, bool throwOnUnknown) - { - InitPrimitiveIDs(); - - if ((object)typeName.Namespace == (object)_schemaNsID || (object)typeName.Namespace == (object)_soapNsID || (object)typeName.Namespace == (object)_soap12NsID) - { - if ((object)typeName.Name == (object)_stringID || - (object)typeName.Name == (object)_anyURIID || - (object)typeName.Name == (object)_durationID || - (object)typeName.Name == (object)_ENTITYID || - (object)typeName.Name == (object)_ENTITIESID || - (object)typeName.Name == (object)_gDayID || - (object)typeName.Name == (object)_gMonthID || - (object)typeName.Name == (object)_gMonthDayID || - (object)typeName.Name == (object)_gYearID || - (object)typeName.Name == (object)_gYearMonthID || - (object)typeName.Name == (object)_IDID || - (object)typeName.Name == (object)_IDREFID || - (object)typeName.Name == (object)_IDREFSID || - (object)typeName.Name == (object)_integerID || - (object)typeName.Name == (object)_languageID || - (object)typeName.Name == (object)_nameID || - (object)typeName.Name == (object)_NCNameID || - (object)typeName.Name == (object)_NMTOKENID || - (object)typeName.Name == (object)_NMTOKENSID || - (object)typeName.Name == (object)_negativeIntegerID || - (object)typeName.Name == (object)_nonPositiveIntegerID || - (object)typeName.Name == (object)_nonNegativeIntegerID || - (object)typeName.Name == (object)_normalizedStringID || - (object)typeName.Name == (object)_NOTATIONID || - (object)typeName.Name == (object)_positiveIntegerID || - (object)typeName.Name == (object)_tokenID) - return typeof(string); - else if ((object)typeName.Name == (object)_intID) - return typeof(int); - else if ((object)typeName.Name == (object)_booleanID) - return typeof(bool); - else if ((object)typeName.Name == (object)_shortID) - return typeof(short); - else if ((object)typeName.Name == (object)_longID) - return typeof(long); - else if ((object)typeName.Name == (object)_floatID) - return typeof(float); - else if ((object)typeName.Name == (object)_doubleID) - return typeof(double); - else if ((object)typeName.Name == (object)_decimalID) - return typeof(decimal); - else if ((object)typeName.Name == (object)_dateTimeID) - return typeof(DateTime); - else if ((object)typeName.Name == (object)_qnameID) - return typeof(XmlQualifiedName); - else if ((object)typeName.Name == (object)_dateID) - return typeof(DateTime); - else if ((object)typeName.Name == (object)_timeID) - return typeof(DateTime); - else if ((object)typeName.Name == (object)_hexBinaryID) - return typeof(byte[]); - else if ((object)typeName.Name == (object)_base64BinaryID) - return typeof(byte[]); - else if ((object)typeName.Name == (object)_unsignedByteID) - return typeof(byte); - else if ((object)typeName.Name == (object)_byteID) - return typeof(SByte); - else if ((object)typeName.Name == (object)_unsignedShortID) - return typeof(UInt16); - else if ((object)typeName.Name == (object)_unsignedIntID) - return typeof(UInt32); - else if ((object)typeName.Name == (object)_unsignedLongID) - return typeof(UInt64); - else - throw CreateUnknownTypeException(typeName); - } - else if ((object)typeName.Namespace == (object)_schemaNs2000ID || (object)typeName.Namespace == (object)_schemaNs1999ID) - { - if ((object)typeName.Name == (object)_stringID || - (object)typeName.Name == (object)_anyURIID || - (object)typeName.Name == (object)_durationID || - (object)typeName.Name == (object)_ENTITYID || - (object)typeName.Name == (object)_ENTITIESID || - (object)typeName.Name == (object)_gDayID || - (object)typeName.Name == (object)_gMonthID || - (object)typeName.Name == (object)_gMonthDayID || - (object)typeName.Name == (object)_gYearID || - (object)typeName.Name == (object)_gYearMonthID || - (object)typeName.Name == (object)_IDID || - (object)typeName.Name == (object)_IDREFID || - (object)typeName.Name == (object)_IDREFSID || - (object)typeName.Name == (object)_integerID || - (object)typeName.Name == (object)_languageID || - (object)typeName.Name == (object)_nameID || - (object)typeName.Name == (object)_NCNameID || - (object)typeName.Name == (object)_NMTOKENID || - (object)typeName.Name == (object)_NMTOKENSID || - (object)typeName.Name == (object)_negativeIntegerID || - (object)typeName.Name == (object)_nonPositiveIntegerID || - (object)typeName.Name == (object)_nonNegativeIntegerID || - (object)typeName.Name == (object)_normalizedStringID || - (object)typeName.Name == (object)_NOTATIONID || - (object)typeName.Name == (object)_positiveIntegerID || - (object)typeName.Name == (object)_tokenID) - return typeof(string); - else if ((object)typeName.Name == (object)_intID) - return typeof(int); - else if ((object)typeName.Name == (object)_booleanID) - return typeof(bool); - else if ((object)typeName.Name == (object)_shortID) - return typeof(short); - else if ((object)typeName.Name == (object)_longID) - return typeof(long); - else if ((object)typeName.Name == (object)_floatID) - return typeof(float); - else if ((object)typeName.Name == (object)_doubleID) - return typeof(double); - else if ((object)typeName.Name == (object)_oldDecimalID) - return typeof(decimal); - else if ((object)typeName.Name == (object)_oldTimeInstantID) - return typeof(DateTime); - else if ((object)typeName.Name == (object)_qnameID) - return typeof(XmlQualifiedName); - else if ((object)typeName.Name == (object)_dateID) - return typeof(DateTime); - else if ((object)typeName.Name == (object)_timeID) - return typeof(DateTime); - else if ((object)typeName.Name == (object)_hexBinaryID) - return typeof(byte[]); - else if ((object)typeName.Name == (object)_byteID) - return typeof(SByte); - else if ((object)typeName.Name == (object)_unsignedShortID) - return typeof(UInt16); - else if ((object)typeName.Name == (object)_unsignedIntID) - return typeof(UInt32); - else if ((object)typeName.Name == (object)_unsignedLongID) - return typeof(UInt64); - else - throw CreateUnknownTypeException(typeName); - } - else if ((object)typeName.Namespace == (object)_schemaNonXsdTypesNsID) - { - if ((object)typeName.Name == (object)_charID) - return typeof(char); - else if ((object)typeName.Name == (object)_guidID) - return typeof(Guid); - else - throw CreateUnknownTypeException(typeName); - } - else if (throwOnUnknown) - throw CreateUnknownTypeException(typeName); - else - return null; - } - - private bool IsPrimitiveNamespace(string ns) - { - return (object)ns == (object)_schemaNsID || - (object)ns == (object)_schemaNonXsdTypesNsID || - (object)ns == (object)_soapNsID || - (object)ns == (object)_soap12NsID || - (object)ns == (object)_schemaNs2000ID || - (object)ns == (object)_schemaNs1999ID; - } - - private string ReadStringValue() - { - if (_r.IsEmptyElement) - { - _r.Skip(); - return string.Empty; - } - _r.ReadStartElement(); - string retVal = _r.ReadString(); - ReadEndElement(); - return retVal; - } - - private XmlQualifiedName ReadXmlQualifiedName() - { - string s; - bool isEmpty = false; - if (_r.IsEmptyElement) - { - s = string.Empty; - isEmpty = true; - } - else - { - _r.ReadStartElement(); - s = _r.ReadString(); - } - - XmlQualifiedName retVal = ToXmlQualifiedName(s); - if (isEmpty) - _r.Skip(); - else - ReadEndElement(); - return retVal; - } - - private byte[] ReadByteArray(bool isBase64) - { - ArrayList list = new ArrayList(); - const int MAX_ALLOC_SIZE = 64 * 1024; - int currentSize = 1024; - byte[] buffer; - int bytes = -1; - int offset = 0; - int total = 0; - buffer = new byte[currentSize]; - list.Add(buffer); - while (bytes != 0) - { - if (offset == buffer.Length) - { - currentSize = Math.Min(currentSize * 2, MAX_ALLOC_SIZE); - buffer = new byte[currentSize]; - offset = 0; - list.Add(buffer); - } - if (isBase64) - { - bytes = _r.ReadElementContentAsBase64(buffer, offset, buffer.Length - offset); - } - else - { - bytes = _r.ReadElementContentAsBinHex(buffer, offset, buffer.Length - offset); - } - offset += bytes; - total += bytes; - } - - byte[] result = new byte[total]; - offset = 0; - foreach (byte[] block in list) - { - currentSize = Math.Min(block.Length, total); - if (currentSize > 0) - { - Buffer.BlockCopy(block, 0, result, offset, currentSize); - offset += currentSize; - total -= currentSize; - } - } - list.Clear(); - return result; - } - - protected object ReadTypedPrimitive(XmlQualifiedName type) - { - return ReadTypedPrimitive(type, false); - } - - private object ReadTypedPrimitive(XmlQualifiedName type, bool elementCanBeType) - { - InitPrimitiveIDs(); - object value = null; - if (!IsPrimitiveNamespace(type.Namespace) || (object)type.Name == (object)_urTypeID) - return ReadXmlNodes(elementCanBeType); - - if ((object)type.Namespace == (object)_schemaNsID || (object)type.Namespace == (object)_soapNsID || (object)type.Namespace == (object)_soap12NsID) - { - if ((object)type.Name == (object)_stringID || - (object)type.Name == (object)_normalizedStringID) - value = ReadStringValue(); - else if ((object)type.Name == (object)_anyURIID || - (object)type.Name == (object)_durationID || - (object)type.Name == (object)_ENTITYID || - (object)type.Name == (object)_ENTITIESID || - (object)type.Name == (object)_gDayID || - (object)type.Name == (object)_gMonthID || - (object)type.Name == (object)_gMonthDayID || - (object)type.Name == (object)_gYearID || - (object)type.Name == (object)_gYearMonthID || - (object)type.Name == (object)_IDID || - (object)type.Name == (object)_IDREFID || - (object)type.Name == (object)_IDREFSID || - (object)type.Name == (object)_integerID || - (object)type.Name == (object)_languageID || - (object)type.Name == (object)_nameID || - (object)type.Name == (object)_NCNameID || - (object)type.Name == (object)_NMTOKENID || - (object)type.Name == (object)_NMTOKENSID || - (object)type.Name == (object)_negativeIntegerID || - (object)type.Name == (object)_nonPositiveIntegerID || - (object)type.Name == (object)_nonNegativeIntegerID || - (object)type.Name == (object)_NOTATIONID || - (object)type.Name == (object)_positiveIntegerID || - (object)type.Name == (object)_tokenID) - value = CollapseWhitespace(ReadStringValue()); - else if ((object)type.Name == (object)_intID) - value = XmlConvert.ToInt32(ReadStringValue()); - else if ((object)type.Name == (object)_booleanID) - value = XmlConvert.ToBoolean(ReadStringValue()); - else if ((object)type.Name == (object)_shortID) - value = XmlConvert.ToInt16(ReadStringValue()); - else if ((object)type.Name == (object)_longID) - value = XmlConvert.ToInt64(ReadStringValue()); - else if ((object)type.Name == (object)_floatID) - value = XmlConvert.ToSingle(ReadStringValue()); - else if ((object)type.Name == (object)_doubleID) - value = XmlConvert.ToDouble(ReadStringValue()); - else if ((object)type.Name == (object)_decimalID) - value = XmlConvert.ToDecimal(ReadStringValue()); - else if ((object)type.Name == (object)_dateTimeID) - value = ToDateTime(ReadStringValue()); - else if ((object)type.Name == (object)_qnameID) - value = ReadXmlQualifiedName(); - else if ((object)type.Name == (object)_dateID) - value = ToDate(ReadStringValue()); - else if ((object)type.Name == (object)_timeID) - value = ToTime(ReadStringValue()); - else if ((object)type.Name == (object)_unsignedByteID) - value = XmlConvert.ToByte(ReadStringValue()); - else if ((object)type.Name == (object)_byteID) - value = XmlConvert.ToSByte(ReadStringValue()); - else if ((object)type.Name == (object)_unsignedShortID) - value = XmlConvert.ToUInt16(ReadStringValue()); - else if ((object)type.Name == (object)_unsignedIntID) - value = XmlConvert.ToUInt32(ReadStringValue()); - else if ((object)type.Name == (object)_unsignedLongID) - value = XmlConvert.ToUInt64(ReadStringValue()); - else if ((object)type.Name == (object)_hexBinaryID) - value = ToByteArrayHex(false); - else if ((object)type.Name == (object)_base64BinaryID) - value = ToByteArrayBase64(false); - else if ((object)type.Name == (object)_base64ID && ((object)type.Namespace == (object)_soapNsID || (object)type.Namespace == (object)_soap12NsID)) - value = ToByteArrayBase64(false); - else - value = ReadXmlNodes(elementCanBeType); - } - else if ((object)type.Namespace == (object)_schemaNs2000ID || (object)type.Namespace == (object)_schemaNs1999ID) - { - if ((object)type.Name == (object)_stringID || - (object)type.Name == (object)_normalizedStringID) - value = ReadStringValue(); - else if ((object)type.Name == (object)_anyURIID || - (object)type.Name == (object)_anyURIID || - (object)type.Name == (object)_durationID || - (object)type.Name == (object)_ENTITYID || - (object)type.Name == (object)_ENTITIESID || - (object)type.Name == (object)_gDayID || - (object)type.Name == (object)_gMonthID || - (object)type.Name == (object)_gMonthDayID || - (object)type.Name == (object)_gYearID || - (object)type.Name == (object)_gYearMonthID || - (object)type.Name == (object)_IDID || - (object)type.Name == (object)_IDREFID || - (object)type.Name == (object)_IDREFSID || - (object)type.Name == (object)_integerID || - (object)type.Name == (object)_languageID || - (object)type.Name == (object)_nameID || - (object)type.Name == (object)_NCNameID || - (object)type.Name == (object)_NMTOKENID || - (object)type.Name == (object)_NMTOKENSID || - (object)type.Name == (object)_negativeIntegerID || - (object)type.Name == (object)_nonPositiveIntegerID || - (object)type.Name == (object)_nonNegativeIntegerID || - (object)type.Name == (object)_NOTATIONID || - (object)type.Name == (object)_positiveIntegerID || - (object)type.Name == (object)_tokenID) - value = CollapseWhitespace(ReadStringValue()); - else if ((object)type.Name == (object)_intID) - value = XmlConvert.ToInt32(ReadStringValue()); - else if ((object)type.Name == (object)_booleanID) - value = XmlConvert.ToBoolean(ReadStringValue()); - else if ((object)type.Name == (object)_shortID) - value = XmlConvert.ToInt16(ReadStringValue()); - else if ((object)type.Name == (object)_longID) - value = XmlConvert.ToInt64(ReadStringValue()); - else if ((object)type.Name == (object)_floatID) - value = XmlConvert.ToSingle(ReadStringValue()); - else if ((object)type.Name == (object)_doubleID) - value = XmlConvert.ToDouble(ReadStringValue()); - else if ((object)type.Name == (object)_oldDecimalID) - value = XmlConvert.ToDecimal(ReadStringValue()); - else if ((object)type.Name == (object)_oldTimeInstantID) - value = ToDateTime(ReadStringValue()); - else if ((object)type.Name == (object)_qnameID) - value = ReadXmlQualifiedName(); - else if ((object)type.Name == (object)_dateID) - value = ToDate(ReadStringValue()); - else if ((object)type.Name == (object)_timeID) - value = ToTime(ReadStringValue()); - else if ((object)type.Name == (object)_unsignedByteID) - value = XmlConvert.ToByte(ReadStringValue()); - else if ((object)type.Name == (object)_byteID) - value = XmlConvert.ToSByte(ReadStringValue()); - else if ((object)type.Name == (object)_unsignedShortID) - value = XmlConvert.ToUInt16(ReadStringValue()); - else if ((object)type.Name == (object)_unsignedIntID) - value = XmlConvert.ToUInt32(ReadStringValue()); - else if ((object)type.Name == (object)_unsignedLongID) - value = XmlConvert.ToUInt64(ReadStringValue()); - else - value = ReadXmlNodes(elementCanBeType); - } - else if ((object)type.Namespace == (object)_schemaNonXsdTypesNsID) - { - if ((object)type.Name == (object)_charID) - value = ToChar(ReadStringValue()); - else if ((object)type.Name == (object)_guidID) - value = new Guid(CollapseWhitespace(ReadStringValue())); - else if ((object)type.Name == (object)_timeSpanID) - value = XmlConvert.ToTimeSpan(ReadStringValue()); - else - value = ReadXmlNodes(elementCanBeType); - } - else - value = ReadXmlNodes(elementCanBeType); - return value; - } - - protected object ReadTypedNull(XmlQualifiedName type) - { - InitPrimitiveIDs(); - object value = null; - if (!IsPrimitiveNamespace(type.Namespace) || (object)type.Name == (object)_urTypeID) - { - return null; - } - - if ((object)type.Namespace == (object)_schemaNsID || (object)type.Namespace == (object)_soapNsID || (object)type.Namespace == (object)_soap12NsID) - { - if ((object)type.Name == (object)_stringID || - (object)type.Name == (object)_anyURIID || - (object)type.Name == (object)_durationID || - (object)type.Name == (object)_ENTITYID || - (object)type.Name == (object)_ENTITIESID || - (object)type.Name == (object)_gDayID || - (object)type.Name == (object)_gMonthID || - (object)type.Name == (object)_gMonthDayID || - (object)type.Name == (object)_gYearID || - (object)type.Name == (object)_gYearMonthID || - (object)type.Name == (object)_IDID || - (object)type.Name == (object)_IDREFID || - (object)type.Name == (object)_IDREFSID || - (object)type.Name == (object)_integerID || - (object)type.Name == (object)_languageID || - (object)type.Name == (object)_nameID || - (object)type.Name == (object)_NCNameID || - (object)type.Name == (object)_NMTOKENID || - (object)type.Name == (object)_NMTOKENSID || - (object)type.Name == (object)_negativeIntegerID || - (object)type.Name == (object)_nonPositiveIntegerID || - (object)type.Name == (object)_nonNegativeIntegerID || - (object)type.Name == (object)_normalizedStringID || - (object)type.Name == (object)_NOTATIONID || - (object)type.Name == (object)_positiveIntegerID || - (object)type.Name == (object)_tokenID) - value = null; - else if ((object)type.Name == (object)_intID) - { - value = default(Nullable); - } - else if ((object)type.Name == (object)_booleanID) - value = default(Nullable); - else if ((object)type.Name == (object)_shortID) - value = default(Nullable); - else if ((object)type.Name == (object)_longID) - value = default(Nullable); - else if ((object)type.Name == (object)_floatID) - value = default(Nullable); - else if ((object)type.Name == (object)_doubleID) - value = default(Nullable); - else if ((object)type.Name == (object)_decimalID) - value = default(Nullable); - else if ((object)type.Name == (object)_dateTimeID) - value = default(Nullable); - else if ((object)type.Name == (object)_qnameID) - value = null; - else if ((object)type.Name == (object)_dateID) - value = default(Nullable); - else if ((object)type.Name == (object)_timeID) - value = default(Nullable); - else if ((object)type.Name == (object)_unsignedByteID) - value = default(Nullable); - else if ((object)type.Name == (object)_byteID) - value = default(Nullable); - else if ((object)type.Name == (object)_unsignedShortID) - value = default(Nullable); - else if ((object)type.Name == (object)_unsignedIntID) - value = default(Nullable); - else if ((object)type.Name == (object)_unsignedLongID) - value = default(Nullable); - else if ((object)type.Name == (object)_hexBinaryID) - value = null; - else if ((object)type.Name == (object)_base64BinaryID) - value = null; - else if ((object)type.Name == (object)_base64ID && ((object)type.Namespace == (object)_soapNsID || (object)type.Namespace == (object)_soap12NsID)) - value = null; - else - value = null; - } - else if ((object)type.Namespace == (object)_schemaNonXsdTypesNsID) - { - if ((object)type.Name == (object)_charID) - value = default(Nullable); - else if ((object)type.Name == (object)_guidID) - value = default(Nullable); - else if ((object)type.Name == (object)_timeSpanID) - value = default(Nullable); - else - value = null; - } - else - value = null; - return value; - } - - protected bool IsXmlnsAttribute(string name) - { - if (!name.StartsWith("xmlns", StringComparison.Ordinal)) return false; - if (name.Length == 5) return true; - return name[5] == ':'; - } - - protected void ParseWsdlArrayType(XmlAttribute attr) - { - if ((object)attr.LocalName == (object)_wsdlArrayTypeID && (object)attr.NamespaceURI == (object)_wsdlNsID) - { - int colon = attr.Value.LastIndexOf(':'); - if (colon < 0) - { - attr.Value = _r.LookupNamespace("") + ":" + attr.Value; - } - else - { - attr.Value = _r.LookupNamespace(attr.Value.Substring(0, colon)) + ":" + - attr.Value.Substring(colon + 1); - } - } - return; - } - - protected bool IsReturnValue - { - // value only valid for soap 1.1 - get { return _isReturnValue && !_soap12; } - set { _isReturnValue = value; } - } - - protected bool ReadNull() - { - if (!GetNullAttr()) return false; - if (_r.IsEmptyElement) - { - _r.Skip(); - return true; - } - _r.ReadStartElement(); - int whileIterations = 0; - int readerCount = ReaderCount; - while (_r.NodeType != XmlNodeType.EndElement) - { - UnknownNode(null); - CheckReaderCount(ref whileIterations, ref readerCount); - } - ReadEndElement(); - return true; - } - - protected bool GetNullAttr() - { - string isNull = _r.GetAttribute(_nilID, _instanceNsID); - if (isNull == null) - isNull = _r.GetAttribute(_nullID, _instanceNsID); - if (isNull == null) - { - isNull = _r.GetAttribute(_nullID, _instanceNs2000ID); - if (isNull == null) - isNull = _r.GetAttribute(_nullID, _instanceNs1999ID); - } - if (isNull == null || !XmlConvert.ToBoolean(isNull)) return false; - return true; - } - - protected string ReadNullableString() - { - if (ReadNull()) return null; - return _r.ReadElementString(); - } - - /// - /// [To be supplied.] - /// - protected XmlQualifiedName ReadNullableQualifiedName() - { - if (ReadNull()) return null; - return ReadElementQualifiedName(); - } - - /// - /// [To be supplied.] - /// - protected XmlQualifiedName ReadElementQualifiedName() - { - if (_r.IsEmptyElement) - { - XmlQualifiedName empty = new XmlQualifiedName(string.Empty, _r.LookupNamespace("")); - _r.Skip(); - return empty; - } - XmlQualifiedName qname = ToXmlQualifiedName(CollapseWhitespace(_r.ReadString())); - _r.ReadEndElement(); - return qname; - } - - protected XmlDocument ReadXmlDocument(bool wrapped) - { - XmlNode n = ReadXmlNode(wrapped); - if (n == null) - return null; - XmlDocument doc = new XmlDocument(); - doc.AppendChild(doc.ImportNode(n, true)); - return doc; - } - - protected string CollapseWhitespace(string value) - { - if (value == null) - return null; - return value.Trim(); - } - - protected XmlNode ReadXmlNode(bool wrapped) - { - XmlNode node = null; - if (wrapped) - { - if (ReadNull()) return null; - _r.ReadStartElement(); - _r.MoveToContent(); - if (_r.NodeType != XmlNodeType.EndElement) - node = Document.ReadNode(_r); - int whileIterations = 0; - int readerCount = ReaderCount; - while (_r.NodeType != XmlNodeType.EndElement) - { - UnknownNode(null); - CheckReaderCount(ref whileIterations, ref readerCount); - } - _r.ReadEndElement(); - } - else - { - node = Document.ReadNode(_r); - } - return node; - } - - protected static byte[] ToByteArrayBase64(string value) - { - return XmlCustomFormatter.ToByteArrayBase64(value); - } - - protected byte[] ToByteArrayBase64(bool isNull) - { - if (isNull) - { - return null; - } - return ReadByteArray(true); //means use Base64 - } - - protected static byte[] ToByteArrayHex(string value) - { - return XmlCustomFormatter.ToByteArrayHex(value); - } - - protected byte[] ToByteArrayHex(bool isNull) - { - if (isNull) - { - return null; - } - return ReadByteArray(false); //means use BinHex - } - - protected int GetArrayLength(string name, string ns) - { - if (GetNullAttr()) return 0; - string arrayType = _r.GetAttribute(_arrayTypeID, _soapNsID); - SoapArrayInfo arrayInfo = ParseArrayType(arrayType); - if (arrayInfo.dimensions != 1) throw new InvalidOperationException(SR.Format(SR.XmlInvalidArrayDimentions, CurrentTag())); - XmlQualifiedName qname = ToXmlQualifiedName(arrayInfo.qname, false); - if (qname.Name != name) throw new InvalidOperationException(SR.Format(SR.XmlInvalidArrayTypeName, qname.Name, name, CurrentTag())); - if (qname.Namespace != ns) throw new InvalidOperationException(SR.Format(SR.XmlInvalidArrayTypeNamespace, qname.Namespace, ns, CurrentTag())); - return arrayInfo.length; - } - - private struct SoapArrayInfo - { - public string qname; - public int dimensions; - public int length; - public int jaggedDimensions; - } - - private SoapArrayInfo ParseArrayType(string value) - { - if (value == null) - { - throw new ArgumentNullException(SR.Format(SR.XmlMissingArrayType, CurrentTag())); - } - - if (value.Length == 0) - { - throw new ArgumentException(SR.Format(SR.XmlEmptyArrayType, CurrentTag()), nameof(value)); - } - - char[] chars = value.ToCharArray(); - int charsLength = chars.Length; - - SoapArrayInfo soapArrayInfo = new SoapArrayInfo(); - - // Parse backwards to get length first, then optional dimensions, then qname. - int pos = charsLength - 1; - - // Must end with ] - if (chars[pos] != ']') - { - throw new ArgumentException(SR.XmlInvalidArraySyntax, nameof(value)); - } - pos--; - - // Find [ - while (pos != -1 && chars[pos] != '[') - { - if (chars[pos] == ',') - throw new ArgumentException(SR.Format(SR.XmlInvalidArrayDimentions, CurrentTag()), nameof(value)); - pos--; - } - if (pos == -1) - { - throw new ArgumentException(SR.XmlMismatchedArrayBrackets, nameof(value)); - } - - int len = charsLength - pos - 2; - if (len > 0) - { - string lengthString = new String(chars, pos + 1, len); - try - { - soapArrayInfo.length = Int32.Parse(lengthString, CultureInfo.InvariantCulture); - } - catch (Exception e) - { - if (e is OutOfMemoryException) - { - throw; - } - throw new ArgumentException(SR.Format(SR.XmlInvalidArrayLength, lengthString), nameof(value)); - } - } - else - { - soapArrayInfo.length = -1; - } - - pos--; - - soapArrayInfo.jaggedDimensions = 0; - while (pos != -1 && chars[pos] == ']') - { - pos--; - if (pos < 0) - throw new ArgumentException(SR.XmlMismatchedArrayBrackets, nameof(value)); - if (chars[pos] == ',') - throw new ArgumentException(SR.Format(SR.XmlInvalidArrayDimentions, CurrentTag()), nameof(value)); - else if (chars[pos] != '[') - throw new ArgumentException(SR.XmlInvalidArraySyntax, nameof(value)); - pos--; - soapArrayInfo.jaggedDimensions++; - } - - soapArrayInfo.dimensions = 1; - - // everything else is qname - validation of qnames? - soapArrayInfo.qname = new String(chars, 0, pos + 1); - return soapArrayInfo; - } - - private SoapArrayInfo ParseSoap12ArrayType(string itemType, string arraySize) - { - SoapArrayInfo soapArrayInfo = new SoapArrayInfo(); - - if (itemType != null && itemType.Length > 0) - soapArrayInfo.qname = itemType; - else - soapArrayInfo.qname = ""; - - string[] dimensions; - if (arraySize != null && arraySize.Length > 0) - dimensions = arraySize.Split(null); - else - dimensions = new string[0]; - - soapArrayInfo.dimensions = 0; - soapArrayInfo.length = -1; - for (int i = 0; i < dimensions.Length; i++) - { - if (dimensions[i].Length > 0) - { - if (dimensions[i] == "*") - { - soapArrayInfo.dimensions++; - } - else - { - try - { - soapArrayInfo.length = Int32.Parse(dimensions[i], CultureInfo.InvariantCulture); - soapArrayInfo.dimensions++; - } - catch (Exception e) - { - if (e is OutOfMemoryException) - { - throw; - } - throw new ArgumentException(SR.Format(SR.XmlInvalidArrayLength, dimensions[i]), "value"); - } - } - } - } - if (soapArrayInfo.dimensions == 0) - soapArrayInfo.dimensions = 1; // default is 1D even if no arraySize is specified - - return soapArrayInfo; - } - - protected static DateTime ToDateTime(string value) - { - return XmlCustomFormatter.ToDateTime(value); - } - - protected static DateTime ToDate(string value) - { - return XmlCustomFormatter.ToDate(value); - } - - protected static DateTime ToTime(string value) - { - return XmlCustomFormatter.ToTime(value); - } - - protected static char ToChar(string value) - { - return XmlCustomFormatter.ToChar(value); - } - - protected static long ToEnum(string value, Hashtable h, string typeName) - { - return XmlCustomFormatter.ToEnum(value, h, typeName, true); - } - - protected static string ToXmlName(string value) - { - return XmlCustomFormatter.ToXmlName(value); - } - - protected static string ToXmlNCName(string value) - { - return XmlCustomFormatter.ToXmlNCName(value); - } - - protected static string ToXmlNmToken(string value) - { - return XmlCustomFormatter.ToXmlNmToken(value); - } - - protected static string ToXmlNmTokens(string value) - { - return XmlCustomFormatter.ToXmlNmTokens(value); - } - - protected XmlQualifiedName ToXmlQualifiedName(string value) - { - return ToXmlQualifiedName(value, DecodeName); - } - - internal XmlQualifiedName ToXmlQualifiedName(string value, bool decodeName) - { - int colon = value == null ? -1 : value.LastIndexOf(':'); - string prefix = colon < 0 ? null : value.Substring(0, colon); - string localName = value.Substring(colon + 1); - - if (decodeName) - { - prefix = XmlConvert.DecodeName(prefix); - localName = XmlConvert.DecodeName(localName); - } - if (prefix == null || prefix.Length == 0) - { - return new XmlQualifiedName(_r.NameTable.Add(value), _r.LookupNamespace(String.Empty)); - } - else - { - string ns = _r.LookupNamespace(prefix); - if (ns == null) - { - // Namespace prefix '{0}' is not defined. - throw new InvalidOperationException(SR.Format(SR.XmlUndefinedAlias, prefix)); - } - return new XmlQualifiedName(_r.NameTable.Add(localName), ns); - } - } - protected void UnknownAttribute(object o, XmlAttribute attr) - { - UnknownAttribute(o, attr, null); - } - - protected void UnknownAttribute(object o, XmlAttribute attr, string qnames) - { - if (_events.OnUnknownAttribute != null) - { - int lineNumber, linePosition; - GetCurrentPosition(out lineNumber, out linePosition); - XmlAttributeEventArgs e = new XmlAttributeEventArgs(attr, lineNumber, linePosition, o, qnames); - _events.OnUnknownAttribute(_events.sender, e); - } - } - - protected void UnknownElement(object o, XmlElement elem) - { - UnknownElement(o, elem, null); - } - - protected void UnknownElement(object o, XmlElement elem, string qnames) - { - if (_events.OnUnknownElement != null) - { - int lineNumber, linePosition; - GetCurrentPosition(out lineNumber, out linePosition); - XmlElementEventArgs e = new XmlElementEventArgs(elem, lineNumber, linePosition, o, qnames); - _events.OnUnknownElement(_events.sender, e); - } - } - - protected void UnknownNode(object o) - { - UnknownNode(o, null); - } - - protected void UnknownNode(object o, string qnames) - { - if (_r.NodeType == XmlNodeType.None || _r.NodeType == XmlNodeType.Whitespace) - { - _r.Read(); - return; - } - if (_r.NodeType == XmlNodeType.EndElement) - return; - if (_events.OnUnknownNode != null) - { - UnknownNode(Document.ReadNode(_r), o, qnames); - } - else if (_r.NodeType == XmlNodeType.Attribute && _events.OnUnknownAttribute == null) - { - return; - } - else if (_r.NodeType == XmlNodeType.Element && _events.OnUnknownElement == null) - { - _r.Skip(); - return; - } - else - { - UnknownNode(Document.ReadNode(_r), o, qnames); - } - } - - private void UnknownNode(XmlNode unknownNode, object o, string qnames) - { - if (unknownNode == null) - return; - if (unknownNode.NodeType != XmlNodeType.None && unknownNode.NodeType != XmlNodeType.Whitespace && _events.OnUnknownNode != null) - { - int lineNumber, linePosition; - GetCurrentPosition(out lineNumber, out linePosition); - XmlNodeEventArgs e = new XmlNodeEventArgs(unknownNode, lineNumber, linePosition, o); - _events.OnUnknownNode(_events.sender, e); - } - if (unknownNode.NodeType == XmlNodeType.Attribute) - { - UnknownAttribute(o, (XmlAttribute)unknownNode, qnames); - } - else if (unknownNode.NodeType == XmlNodeType.Element) - { - UnknownElement(o, (XmlElement)unknownNode, qnames); - } - } - - private void GetCurrentPosition(out int lineNumber, out int linePosition) - { - if (Reader is IXmlLineInfo) - { - IXmlLineInfo lineInfo = (IXmlLineInfo)Reader; - lineNumber = lineInfo.LineNumber; - linePosition = lineInfo.LinePosition; - } - else - lineNumber = linePosition = -1; - } - - protected void UnreferencedObject(string id, object o) - { - if (_events.OnUnreferencedObject != null) - { - UnreferencedObjectEventArgs e = new UnreferencedObjectEventArgs(o, id); - _events.OnUnreferencedObject(_events.sender, e); - } - } - - private string CurrentTag() - { - switch (_r.NodeType) - { - case XmlNodeType.Element: - return "<" + _r.LocalName + " xmlns='" + _r.NamespaceURI + "'>"; - case XmlNodeType.EndElement: - return ">"; - case XmlNodeType.Text: - return _r.Value; - case XmlNodeType.CDATA: - return "CDATA"; - case XmlNodeType.Comment: - return "<--"; - case XmlNodeType.ProcessingInstruction: - return " 0) - { - qname = ToXmlQualifiedName(arrayInfo.qname, false); - elementType = (Type)_types[qname]; - } - else - qname = urTypeName; - - // try again if the best we could come up with was object - if (_soap12 && elementType == typeof(object)) - elementType = null; - - if (elementType == null) - { - if (!_soap12) - { - elementType = GetPrimitiveType(qname, true); - isPrimitive = true; - } - else - { - // try it as a primitive - if (qname != urTypeName) - elementType = GetPrimitiveType(qname, false); - if (elementType != null) - { - isPrimitive = true; - } - else - { - // still nothing: go with fallback type or object - if (fallbackElementType == null) - { - elementType = typeof(object); - isPrimitive = false; - } - else - { - elementType = fallbackElementType; - XmlQualifiedName newQname = (XmlQualifiedName)_typesReverse[elementType]; - if (newQname == null) - { - newQname = XmlSerializationWriter.GetPrimitiveTypeNameInternal(elementType); - isPrimitive = true; - } - else - isPrimitive = elementType.IsPrimitive; - if (newQname != null) qname = newQname; - } - } - } - } - else - isPrimitive = elementType.IsPrimitive; - - if (!_soap12 && arrayInfo.jaggedDimensions > 0) - { - for (int i = 0; i < arrayInfo.jaggedDimensions; i++) - elementType = elementType.MakeArrayType(); - } - - if (_r.IsEmptyElement) - { - _r.Skip(); - return Array.CreateInstance(elementType, 0); - } - - _r.ReadStartElement(); - _r.MoveToContent(); - - int arrayLength = 0; - Array array = null; - - if (elementType.IsValueType) - { - if (!isPrimitive && !elementType.IsEnum) - { - throw new NotSupportedException(SR.Format(SR.XmlRpcArrayOfValueTypes, elementType.FullName)); - } - // CONSIDER, erikc, we could have specialized read functions here - // for primitives, which would avoid boxing. - int whileIterations = 0; - int readerCount = ReaderCount; - while (_r.NodeType != XmlNodeType.EndElement) - { - array = EnsureArrayIndex(array, arrayLength, elementType); - array.SetValue(ReadReferencedElement(qname.Name, qname.Namespace), arrayLength); - arrayLength++; - _r.MoveToContent(); - CheckReaderCount(ref whileIterations, ref readerCount); - } - array = ShrinkArray(array, arrayLength, elementType, false); - } - else - { - string type; - string typens; - string[] ids = null; - int idsLength = 0; - - int whileIterations = 0; - int readerCount = ReaderCount; - while (_r.NodeType != XmlNodeType.EndElement) - { - array = EnsureArrayIndex(array, arrayLength, elementType); - ids = (string[])EnsureArrayIndex(ids, idsLength, typeof(string)); - // CONSIDER: i'm not sure it's correct to let item name take precedence over arrayType - if (_r.NamespaceURI.Length != 0) - { - type = _r.LocalName; - if ((object)_r.NamespaceURI == (object)_soapNsID) - typens = XmlSchema.Namespace; - else - typens = _r.NamespaceURI; - } - else - { - type = qname.Name; - typens = qname.Namespace; - } - array.SetValue(ReadReferencingElement(type, typens, out ids[idsLength]), arrayLength); - arrayLength++; - idsLength++; - // CONSIDER, erikc, sparse arrays, perhaps? - _r.MoveToContent(); - CheckReaderCount(ref whileIterations, ref readerCount); - } - - // special case for soap 1.2: try to get a better fit than object[] when no metadata is known - // this applies in the doc/enc/bare case - if (_soap12 && elementType == typeof(object)) - { - Type itemType = null; - for (int i = 0; i < arrayLength; i++) - { - object currItem = array.GetValue(i); - if (currItem != null) - { - Type currItemType = currItem.GetType(); - if (currItemType.IsValueType) - { - itemType = null; - break; - } - if (itemType == null || currItemType.IsAssignableFrom(itemType)) - { - itemType = currItemType; - } - else if (!itemType.IsAssignableFrom(currItemType)) - { - itemType = null; - break; - } - } - } - if (itemType != null) - elementType = itemType; - } - ids = (string[])ShrinkArray(ids, idsLength, typeof(string), false); - array = ShrinkArray(array, arrayLength, elementType, false); - Fixup fixupArray = new Fixup(array, new XmlSerializationFixupCallback(this.FixupArrayRefs), ids); - AddFixup(fixupArray); - } - - // CONSIDER, erikc, check to see if the specified array length was right, perhaps? - - ReadEndElement(); - return array; - } - - protected abstract void InitCallbacks(); - - protected void ReadReferencedElements() - { - _r.MoveToContent(); - string dummy; - int whileIterations = 0; - int readerCount = ReaderCount; - while (_r.NodeType != XmlNodeType.EndElement && _r.NodeType != XmlNodeType.None) - { - ReadReferencingElement(null, null, true, out dummy); - _r.MoveToContent(); - CheckReaderCount(ref whileIterations, ref readerCount); - } - DoFixups(); - - HandleUnreferencedObjects(); - } - - protected object ReadReferencedElement() - { - return ReadReferencedElement(null, null); - } - - protected object ReadReferencedElement(string name, string ns) - { - string dummy; - return ReadReferencingElement(name, ns, out dummy); - } - - protected object ReadReferencingElement(out string fixupReference) - { - return ReadReferencingElement(null, null, out fixupReference); - } - - protected object ReadReferencingElement(string name, string ns, out string fixupReference) - { - return ReadReferencingElement(name, ns, false, out fixupReference); - } - - protected object ReadReferencingElement(string name, string ns, bool elementCanBeType, out string fixupReference) - { - object o = null; - EnsureCallbackTables(); - - _r.MoveToContent(); - - if (ReadReference(out fixupReference)) return null; - - if (ReadNull()) return null; - - string id = _soap12 ? _r.GetAttribute("id", Soap12.Encoding) : _r.GetAttribute("id", null); - - if ((o = ReadArray(name, ns)) == null) - { - XmlQualifiedName typeId = GetXsiType(); - if (typeId == null) - { - if (name == null) - typeId = new XmlQualifiedName(_r.NameTable.Add(_r.LocalName), _r.NameTable.Add(_r.NamespaceURI)); - else - typeId = new XmlQualifiedName(_r.NameTable.Add(name), _r.NameTable.Add(ns)); - } - XmlSerializationReadCallback callback = (XmlSerializationReadCallback)_callbacks[typeId]; - if (callback != null) - { - o = callback(); - } - else - o = ReadTypedPrimitive(typeId, elementCanBeType); - } - - AddTarget(id, o); - - return o; - } - - internal void EnsureCallbackTables() - { - if (_callbacks == null) - { - _callbacks = new Hashtable(); - _types = new Hashtable(); - XmlQualifiedName urType = new XmlQualifiedName(_urTypeID, _r.NameTable.Add(XmlSchema.Namespace)); - _types.Add(urType, typeof(object)); - _typesReverse = new Hashtable(); - _typesReverse.Add(typeof(object), urType); - InitCallbacks(); - } - } - - protected void AddReadCallback(string name, string ns, Type type, XmlSerializationReadCallback read) - { - XmlQualifiedName typeName = new XmlQualifiedName(_r.NameTable.Add(name), _r.NameTable.Add(ns)); - _callbacks[typeName] = read; - _types[typeName] = type; - _typesReverse[type] = typeName; - } - - protected void ReadEndElement() - { - while (_r.NodeType == XmlNodeType.Whitespace) _r.Skip(); - if (_r.NodeType == XmlNodeType.None) _r.Skip(); - else _r.ReadEndElement(); - } - - private object ReadXmlNodes(bool elementCanBeType) - { - ArrayList xmlNodeList = new ArrayList(); - string elemLocalName = Reader.LocalName; - string elemNs = Reader.NamespaceURI; - string elemName = Reader.Name; - string xsiTypeName = null; - string xsiTypeNs = null; - int skippableNodeCount = 0; - int lineNumber = -1, linePosition = -1; - XmlNode unknownNode = null; - if (Reader.NodeType == XmlNodeType.Attribute) - { - XmlAttribute attr = Document.CreateAttribute(elemName, elemNs); - attr.Value = Reader.Value; - unknownNode = attr; - } - else - unknownNode = Document.CreateElement(elemName, elemNs); - GetCurrentPosition(out lineNumber, out linePosition); - XmlElement unknownElement = unknownNode as XmlElement; - - while (Reader.MoveToNextAttribute()) - { - if (IsXmlnsAttribute(Reader.Name) || (Reader.Name == "id" && (!_soap12 || Reader.NamespaceURI == Soap12.Encoding))) - skippableNodeCount++; - if ((object)Reader.LocalName == (object)_typeID && - ((object)Reader.NamespaceURI == (object)_instanceNsID || - (object)Reader.NamespaceURI == (object)_instanceNs2000ID || - (object)Reader.NamespaceURI == (object)_instanceNs1999ID - ) - ) - { - string value = Reader.Value; - int colon = value.LastIndexOf(':'); - xsiTypeName = (colon >= 0) ? value.Substring(colon + 1) : value; - xsiTypeNs = Reader.LookupNamespace((colon >= 0) ? value.Substring(0, colon) : ""); - } - XmlAttribute xmlAttribute = (XmlAttribute)Document.ReadNode(_r); - xmlNodeList.Add(xmlAttribute); - if (unknownElement != null) unknownElement.SetAttributeNode(xmlAttribute); - } - - // If the node is referenced (or in case of paramStyle = bare) and if xsi:type is not - // specified then the element name is used as the type name. Reveal this to the user - // by adding an extra attribute node "xsi:type" with value as the element name. - if (elementCanBeType && xsiTypeName == null) - { - xsiTypeName = elemLocalName; - xsiTypeNs = elemNs; - XmlAttribute xsiTypeAttribute = Document.CreateAttribute(_typeID, _instanceNsID); - xsiTypeAttribute.Value = elemName; - xmlNodeList.Add(xsiTypeAttribute); - } - if (xsiTypeName == Soap.UrType && - ((object)xsiTypeNs == (object)_schemaNsID || - (object)xsiTypeNs == (object)_schemaNs1999ID || - (object)xsiTypeNs == (object)_schemaNs2000ID - ) - ) - skippableNodeCount++; - - - Reader.MoveToElement(); - if (Reader.IsEmptyElement) - { - Reader.Skip(); - } - else - { - Reader.ReadStartElement(); - Reader.MoveToContent(); - int whileIterations = 0; - int readerCount = ReaderCount; - while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) - { - XmlNode xmlNode = Document.ReadNode(_r); - xmlNodeList.Add(xmlNode); - if (unknownElement != null) unknownElement.AppendChild(xmlNode); - Reader.MoveToContent(); - CheckReaderCount(ref whileIterations, ref readerCount); - } - ReadEndElement(); - } - - - if (xmlNodeList.Count <= skippableNodeCount) - return new object(); - - XmlNode[] childNodes = (XmlNode[])xmlNodeList.ToArray(typeof(XmlNode)); - - UnknownNode(unknownNode, null, null); - return childNodes; - } - - protected void CheckReaderCount(ref int whileIterations, ref int readerCount) - { - if (s_checkDeserializeAdvances) - { - whileIterations++; - if ((whileIterations & 0x80) == 0x80) - { - if (readerCount == ReaderCount) - throw new InvalidOperationException(SR.XmlInternalErrorReaderAdvance); - readerCount = ReaderCount; - } - } - } - - /// - protected class Fixup - { - private XmlSerializationFixupCallback _callback; - private object _source; - private string[] _ids; - - public Fixup(object o, XmlSerializationFixupCallback callback, int count) - : this(o, callback, new string[count]) - { - } - - public Fixup(object o, XmlSerializationFixupCallback callback, string[] ids) - { - _callback = callback; - this.Source = o; - _ids = ids; - } - - public XmlSerializationFixupCallback Callback - { - get { return _callback; } - } - - public object Source - { - get { return _source; } - set { _source = value; } - } - - public string[] Ids - { - get { return _ids; } - } - } - - protected class CollectionFixup - { - private XmlSerializationCollectionFixupCallback _callback; - private object _collection; - private object _collectionItems; - - public CollectionFixup(object collection, XmlSerializationCollectionFixupCallback callback, object collectionItems) - { - _callback = callback; - _collection = collection; - _collectionItems = collectionItems; - } - - public XmlSerializationCollectionFixupCallback Callback - { - get { return _callback; } - } - - public object Collection - { - get { return _collection; } - } - - public object CollectionItems - { - get { return _collectionItems; } - } - } -#endif - } - -#if !XMLSERIALIZERGENERATOR - /// - public delegate void XmlSerializationFixupCallback(object fixup); - - - /// - public delegate void XmlSerializationCollectionFixupCallback(object collection, object collectionItems); - - /// - public delegate object XmlSerializationReadCallback(); -#endif -} diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs.REMOVED.git-id b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs.REMOVED.git-id new file mode 100644 index 0000000000..f5a434ab79 --- /dev/null +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs.REMOVED.git-id @@ -0,0 +1 @@ +c9882b4b25531d053c8b0344643eebb1c770772e \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs deleted file mode 100644 index ef0541fe1d..0000000000 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs +++ /dev/null @@ -1,2209 +0,0 @@ -// 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. - -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else -namespace System.Xml.Serialization -#endif -{ - using System; - using System.IO; - using System.Collections; - using System.Reflection; - using System.Reflection.Emit; - using System.Xml.Schema; - using System.ComponentModel; - using System.Diagnostics; - using System.Globalization; - using System.Text; - using System.Threading; - using System.Runtime.Versioning; - using System.Collections.Generic; - using System.Xml.Serialization; - using System.Xml; - - /// -#if XMLSERIALIZERGENERATOR - internal abstract class XmlSerializationWriter : XmlSerializationGeneratedCode -#else - public abstract class XmlSerializationWriter : XmlSerializationGeneratedCode -#endif - { - private XmlWriter _w; - private XmlSerializerNamespaces _namespaces; - private int _tempNamespacePrefix; - private HashSet _usedPrefixes; - private Hashtable _references; - private string _idBase; - private int _nextId; - private Hashtable _typeEntries; - private ArrayList _referencesToWrite; - private Hashtable _objectsInUse; - private string _aliasBase = "q"; - private bool _soap12; - private bool _escapeName = true; - -#if FEATURE_SERIALIZATION_UAPAOT - // this method must be called before any generated serialization methods are called - internal void Init(XmlWriter w, XmlSerializerNamespaces namespaces, string encodingStyle, string idBase) - { - _w = w; - _namespaces = namespaces; - } -#endif - - // this method must be called before any generated serialization methods are called - internal void Init(XmlWriter w, XmlSerializerNamespaces namespaces, string encodingStyle, string idBase, TempAssembly tempAssembly) - { - _w = w; - _namespaces = namespaces; - _soap12 = (encodingStyle == Soap12.Encoding); - _idBase = idBase; - Init(tempAssembly); - } - - protected bool EscapeName - { - get - { - return _escapeName; - } - set - { - _escapeName = value; - } - } - - protected XmlWriter Writer - { - get - { - return _w; - } - set - { - _w = value; - } - } - - /// - /// [To be supplied.] - /// - protected ArrayList Namespaces - { - get - { - return _namespaces == null ? null : _namespaces.NamespaceList; - } - set - { - if (value == null) - { - _namespaces = null; - } - else - { - XmlQualifiedName[] qnames = (XmlQualifiedName[])value.ToArray(typeof(XmlQualifiedName)); - _namespaces = new XmlSerializerNamespaces(qnames); - } - } - } - - protected static byte[] FromByteArrayBase64(byte[] value) - { - // Unlike other "From" functions that one is just a place holder for automatic code generation. - // The reason is performance and memory consumption for (potentially) big 64base-encoded chunks - // And it is assumed that the caller generates the code that will distinguish between byte[] and string return types - // - return value; - } - - /// - protected static Assembly ResolveDynamicAssembly(string assemblyFullName) - { - return DynamicAssemblies.Get(assemblyFullName); - } - - protected static string FromByteArrayHex(byte[] value) - { - return XmlCustomFormatter.FromByteArrayHex(value); - } - - protected static string FromDateTime(DateTime value) - { - return XmlCustomFormatter.FromDateTime(value); - } - - protected static string FromDate(DateTime value) - { - return XmlCustomFormatter.FromDate(value); - } - - protected static string FromTime(DateTime value) - { - return XmlCustomFormatter.FromTime(value); - } - - protected static string FromChar(char value) - { - return XmlCustomFormatter.FromChar(value); - } - - protected static string FromEnum(long value, string[] values, long[] ids) - { - return XmlCustomFormatter.FromEnum(value, values, ids, null); - } - - protected static string FromEnum(long value, string[] values, long[] ids, string typeName) - { - return XmlCustomFormatter.FromEnum(value, values, ids, typeName); - } - - protected static string FromXmlName(string name) - { - return XmlCustomFormatter.FromXmlName(name); - } - - protected static string FromXmlNCName(string ncName) - { - return XmlCustomFormatter.FromXmlNCName(ncName); - } - - protected static string FromXmlNmToken(string nmToken) - { - return XmlCustomFormatter.FromXmlNmToken(nmToken); - } - - protected static string FromXmlNmTokens(string nmTokens) - { - return XmlCustomFormatter.FromXmlNmTokens(nmTokens); - } - - protected void WriteXsiType(string name, string ns) - { - WriteAttribute("type", XmlSchema.InstanceNamespace, GetQualifiedName(name, ns)); - } - - private XmlQualifiedName GetPrimitiveTypeName(Type type) - { - return GetPrimitiveTypeName(type, true); - } - - private XmlQualifiedName GetPrimitiveTypeName(Type type, bool throwIfUnknown) - { - XmlQualifiedName qname = GetPrimitiveTypeNameInternal(type); - if (throwIfUnknown && qname == null) - throw CreateUnknownTypeException(type); - return qname; - } - - internal static XmlQualifiedName GetPrimitiveTypeNameInternal(Type type) - { - string typeName; - string typeNs = XmlSchema.Namespace; - - switch (type.GetTypeCode()) - { - case TypeCode.String: typeName = "string"; break; - case TypeCode.Int32: typeName = "int"; break; - case TypeCode.Boolean: typeName = "boolean"; break; - case TypeCode.Int16: typeName = "short"; break; - case TypeCode.Int64: typeName = "long"; break; - case TypeCode.Single: typeName = "float"; break; - case TypeCode.Double: typeName = "double"; break; - case TypeCode.Decimal: typeName = "decimal"; break; - case TypeCode.DateTime: typeName = "dateTime"; break; - case TypeCode.Byte: typeName = "unsignedByte"; break; - case TypeCode.SByte: typeName = "byte"; break; - case TypeCode.UInt16: typeName = "unsignedShort"; break; - case TypeCode.UInt32: typeName = "unsignedInt"; break; - case TypeCode.UInt64: typeName = "unsignedLong"; break; - case TypeCode.Char: - typeName = "char"; - typeNs = UrtTypes.Namespace; - break; - default: - if (type == typeof(XmlQualifiedName)) typeName = "QName"; - else if (type == typeof(byte[])) typeName = "base64Binary"; - else if (type == typeof(Guid)) - { - typeName = "guid"; - typeNs = UrtTypes.Namespace; - } - else if (type == typeof(TimeSpan)) - { - typeName = "TimeSpan"; - typeNs = UrtTypes.Namespace; - } - else if (type == typeof(XmlNode[])) - { - typeName = Soap.UrType; - } - else - return null; - break; - } - return new XmlQualifiedName(typeName, typeNs); - } - - protected void WriteTypedPrimitive(string name, string ns, object o, bool xsiType) - { - string value = null; - string type; - string typeNs = XmlSchema.Namespace; - bool writeRaw = true; - bool writeDirect = false; - Type t = o.GetType(); - bool wroteStartElement = false; - - switch (t.GetTypeCode()) - { - case TypeCode.String: - value = (string)o; - type = "string"; - writeRaw = false; - break; - case TypeCode.Int32: - value = XmlConvert.ToString((int)o); - type = "int"; - break; - case TypeCode.Boolean: - value = XmlConvert.ToString((bool)o); - type = "boolean"; - break; - case TypeCode.Int16: - value = XmlConvert.ToString((short)o); - type = "short"; - break; - case TypeCode.Int64: - value = XmlConvert.ToString((long)o); - type = "long"; - break; - case TypeCode.Single: - value = XmlConvert.ToString((float)o); - type = "float"; - break; - case TypeCode.Double: - value = XmlConvert.ToString((double)o); - type = "double"; - break; - case TypeCode.Decimal: - value = XmlConvert.ToString((decimal)o); - type = "decimal"; - break; - case TypeCode.DateTime: - value = FromDateTime((DateTime)o); - type = "dateTime"; - break; - case TypeCode.Char: - value = FromChar((char)o); - type = "char"; - typeNs = UrtTypes.Namespace; - break; - case TypeCode.Byte: - value = XmlConvert.ToString((byte)o); - type = "unsignedByte"; - break; - case TypeCode.SByte: - value = XmlConvert.ToString((sbyte)o); - type = "byte"; - break; - case TypeCode.UInt16: - value = XmlConvert.ToString((UInt16)o); - type = "unsignedShort"; - break; - case TypeCode.UInt32: - value = XmlConvert.ToString((UInt32)o); - type = "unsignedInt"; - break; - case TypeCode.UInt64: - value = XmlConvert.ToString((UInt64)o); - type = "unsignedLong"; - break; - - default: - if (t == typeof(XmlQualifiedName)) - { - type = "QName"; - // need to write start element ahead of time to establish context - // for ns definitions by FromXmlQualifiedName - wroteStartElement = true; - if (name == null) - _w.WriteStartElement(type, typeNs); - else - _w.WriteStartElement(name, ns); - value = FromXmlQualifiedName((XmlQualifiedName)o, false); - } - else if (t == typeof(byte[])) - { - value = String.Empty; - writeDirect = true; - type = "base64Binary"; - } - else if (t == typeof(Guid)) - { - value = XmlConvert.ToString((Guid)o); - type = "guid"; - typeNs = UrtTypes.Namespace; - } - else if (t == typeof(TimeSpan)) - { - value = XmlConvert.ToString((TimeSpan)o); - type = "TimeSpan"; - typeNs = UrtTypes.Namespace; - } - else if (typeof(XmlNode[]).IsAssignableFrom(t)) - { - if (name == null) - _w.WriteStartElement(Soap.UrType, XmlSchema.Namespace); - else - _w.WriteStartElement(name, ns); - - XmlNode[] xmlNodes = (XmlNode[])o; - for (int i = 0; i < xmlNodes.Length; i++) - { - if (xmlNodes[i] == null) - continue; - xmlNodes[i].WriteTo(_w); - } - _w.WriteEndElement(); - return; - } - else - throw CreateUnknownTypeException(t); - break; - } - if (!wroteStartElement) - { - if (name == null) - _w.WriteStartElement(type, typeNs); - else - _w.WriteStartElement(name, ns); - } - - if (xsiType) WriteXsiType(type, typeNs); - - if (value == null) - { - _w.WriteAttributeString("nil", XmlSchema.InstanceNamespace, "true"); - } - else if (writeDirect) - { - // only one type currently writes directly to XML stream - XmlCustomFormatter.WriteArrayBase64(_w, (byte[])o, 0, ((byte[])o).Length); - } - else if (writeRaw) - { - _w.WriteRaw(value); - } - else - _w.WriteString(value); - _w.WriteEndElement(); - } - - private string GetQualifiedName(string name, string ns) - { - if (ns == null || ns.Length == 0) return name; - string prefix = _w.LookupPrefix(ns); - if (prefix == null) - { - if (ns == XmlReservedNs.NsXml) - { - prefix = "xml"; - } - else - { - prefix = NextPrefix(); - WriteAttribute("xmlns", prefix, null, ns); - } - } - else if (prefix.Length == 0) - { - return name; - } - return prefix + ":" + name; - } - - protected string FromXmlQualifiedName(XmlQualifiedName xmlQualifiedName) - { - return FromXmlQualifiedName(xmlQualifiedName, true); - } - - protected string FromXmlQualifiedName(XmlQualifiedName xmlQualifiedName, bool ignoreEmpty) - { - if (xmlQualifiedName == null) return null; - if (xmlQualifiedName.IsEmpty && ignoreEmpty) return null; - return GetQualifiedName(EscapeName ? XmlConvert.EncodeLocalName(xmlQualifiedName.Name) : xmlQualifiedName.Name, xmlQualifiedName.Namespace); - } - - protected void WriteStartElement(string name) - { - WriteStartElement(name, null, null, false, null); - } - - protected void WriteStartElement(string name, string ns) - { - WriteStartElement(name, ns, null, false, null); - } - - protected void WriteStartElement(string name, string ns, bool writePrefixed) - { - WriteStartElement(name, ns, null, writePrefixed, null); - } - - protected void WriteStartElement(string name, string ns, object o) - { - WriteStartElement(name, ns, o, false, null); - } - - protected void WriteStartElement(string name, string ns, object o, bool writePrefixed) - { - WriteStartElement(name, ns, o, writePrefixed, null); - } - - protected void WriteStartElement(string name, string ns, object o, bool writePrefixed, XmlSerializerNamespaces xmlns) - { - if (o != null && _objectsInUse != null) - { - if (_objectsInUse.ContainsKey(o)) throw new InvalidOperationException(SR.Format(SR.XmlCircularReference, o.GetType().FullName)); - _objectsInUse.Add(o, o); - } - - string prefix = null; - bool needEmptyDefaultNamespace = false; - if (_namespaces != null) - { - foreach (string alias in _namespaces.Namespaces.Keys) - { - string aliasNs = (string)_namespaces.Namespaces[alias]; - - if (alias.Length > 0 && aliasNs == ns) - prefix = alias; - if (alias.Length == 0) - { - if (aliasNs == null || aliasNs.Length == 0) - needEmptyDefaultNamespace = true; - if (ns != aliasNs) - writePrefixed = true; - } - } - _usedPrefixes = ListUsedPrefixes(_namespaces.Namespaces, _aliasBase); - } - if (writePrefixed && prefix == null && ns != null && ns.Length > 0) - { - prefix = _w.LookupPrefix(ns); - if (prefix == null || prefix.Length == 0) - { - prefix = NextPrefix(); - } - } - if (prefix == null && xmlns != null) - { - prefix = xmlns.LookupPrefix(ns); - } - if (needEmptyDefaultNamespace && prefix == null && ns != null && ns.Length != 0) - prefix = NextPrefix(); - _w.WriteStartElement(prefix, name, ns); - if (_namespaces != null) - { - foreach (string alias in _namespaces.Namespaces.Keys) - { - string aliasNs = (string)_namespaces.Namespaces[alias]; - if (alias.Length == 0 && (aliasNs == null || aliasNs.Length == 0)) - continue; - if (aliasNs == null || aliasNs.Length == 0) - { - if (alias.Length > 0) - throw new InvalidOperationException(SR.Format(SR.XmlInvalidXmlns, alias)); - WriteAttribute(nameof(xmlns), alias, null, aliasNs); - } - else - { - if (_w.LookupPrefix(aliasNs) == null) - { - // write the default namespace declaration only if we have not written it already, over wise we just ignore one provided by the user - if (prefix == null && alias.Length == 0) - break; - WriteAttribute(nameof(xmlns), alias, null, aliasNs); - } - } - } - } - WriteNamespaceDeclarations(xmlns); - } - - private HashSet ListUsedPrefixes(Dictionary nsList, string prefix) - { - var qnIndexes = new HashSet(); - int prefixLength = prefix.Length; - const string MaxInt32 = "2147483647"; - foreach (string alias in _namespaces.Namespaces.Keys) - { - string name; - if (alias.Length > prefixLength) - { - name = alias; - if (name.Length > prefixLength && name.Length <= prefixLength + MaxInt32.Length && name.StartsWith(prefix, StringComparison.Ordinal)) - { - bool numeric = true; - for (int j = prefixLength; j < name.Length; j++) - { - if (!Char.IsDigit(name, j)) - { - numeric = false; - break; - } - } - if (numeric) - { - Int64 index = Int64.Parse(name.Substring(prefixLength), NumberStyles.Integer, CultureInfo.InvariantCulture); - if (index <= Int32.MaxValue) - { - Int32 newIndex = (Int32)index; - qnIndexes.Add(newIndex); - } - } - } - } - } - if (qnIndexes.Count > 0) - { - return qnIndexes; - } - return null; - } - - protected void WriteNullTagEncoded(string name) - { - WriteNullTagEncoded(name, null); - } - - protected void WriteNullTagEncoded(string name, string ns) - { - if (name == null || name.Length == 0) - return; - WriteStartElement(name, ns, null, true); - _w.WriteAttributeString("nil", XmlSchema.InstanceNamespace, "true"); - _w.WriteEndElement(); - } - - protected void WriteNullTagLiteral(string name) - { - WriteNullTagLiteral(name, null); - } - - protected void WriteNullTagLiteral(string name, string ns) - { - if (name == null || name.Length == 0) - return; - WriteStartElement(name, ns, null, false); - _w.WriteAttributeString("nil", XmlSchema.InstanceNamespace, "true"); - _w.WriteEndElement(); - } - - protected void WriteEmptyTag(string name) - { - WriteEmptyTag(name, null); - } - - protected void WriteEmptyTag(string name, string ns) - { - if (name == null || name.Length == 0) - return; - WriteStartElement(name, ns, null, false); - _w.WriteEndElement(); - } - - protected void WriteEndElement() - { - _w.WriteEndElement(); - } - - protected void WriteEndElement(object o) - { - _w.WriteEndElement(); - - if (o != null && _objectsInUse != null) - { -#if DEBUG - // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe - if (!_objectsInUse.ContainsKey(o)) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorDetails, "missing stack object of type " + o.GetType().FullName)); -#endif - - _objectsInUse.Remove(o); - } - } - - protected void WriteSerializable(IXmlSerializable serializable, string name, string ns, bool isNullable) - { - WriteSerializable(serializable, name, ns, isNullable, true); - } - - protected void WriteSerializable(IXmlSerializable serializable, string name, string ns, bool isNullable, bool wrapped) - { - if (serializable == null) - { - if (isNullable) WriteNullTagLiteral(name, ns); - return; - } - if (wrapped) - { - _w.WriteStartElement(name, ns); - } - serializable.WriteXml(_w); - if (wrapped) - { - _w.WriteEndElement(); - } - } - - protected void WriteNullableStringEncoded(string name, string ns, string value, XmlQualifiedName xsiType) - { - if (value == null) - WriteNullTagEncoded(name, ns); - else - WriteElementString(name, ns, value, xsiType); - } - - protected void WriteNullableStringLiteral(string name, string ns, string value) - { - if (value == null) - WriteNullTagLiteral(name, ns); - else - WriteElementString(name, ns, value, null); - } - - - protected void WriteNullableStringEncodedRaw(string name, string ns, string value, XmlQualifiedName xsiType) - { - if (value == null) - WriteNullTagEncoded(name, ns); - else - WriteElementStringRaw(name, ns, value, xsiType); - } - - protected void WriteNullableStringEncodedRaw(string name, string ns, byte[] value, XmlQualifiedName xsiType) - { - if (value == null) - WriteNullTagEncoded(name, ns); - else - WriteElementStringRaw(name, ns, value, xsiType); - } - - protected void WriteNullableStringLiteralRaw(string name, string ns, string value) - { - if (value == null) - WriteNullTagLiteral(name, ns); - else - WriteElementStringRaw(name, ns, value, null); - } - - protected void WriteNullableStringLiteralRaw(string name, string ns, byte[] value) - { - if (value == null) - WriteNullTagLiteral(name, ns); - else - WriteElementStringRaw(name, ns, value, null); - } - - /// - /// [To be supplied.] - /// - protected void WriteNullableQualifiedNameEncoded(string name, string ns, XmlQualifiedName value, XmlQualifiedName xsiType) - { - if (value == null) - WriteNullTagEncoded(name, ns); - else - WriteElementQualifiedName(name, ns, value, xsiType); - } - - /// - /// [To be supplied.] - /// - protected void WriteNullableQualifiedNameLiteral(string name, string ns, XmlQualifiedName value) - { - if (value == null) - WriteNullTagLiteral(name, ns); - else - WriteElementQualifiedName(name, ns, value, null); - } - - - protected void WriteElementEncoded(XmlNode node, string name, string ns, bool isNullable, bool any) - { - if (node == null) - { - if (isNullable) WriteNullTagEncoded(name, ns); - return; - } - WriteElement(node, name, ns, isNullable, any); - } - - protected void WriteElementLiteral(XmlNode node, string name, string ns, bool isNullable, bool any) - { - if (node == null) - { - if (isNullable) WriteNullTagLiteral(name, ns); - return; - } - WriteElement(node, name, ns, isNullable, any); - } - - private void WriteElement(XmlNode node, string name, string ns, bool isNullable, bool any) - { - if (typeof(XmlAttribute).IsAssignableFrom(node.GetType())) - throw new InvalidOperationException(SR.XmlNoAttributeHere); - if (node is XmlDocument) - { - node = ((XmlDocument)node).DocumentElement; - if (node == null) - { - if (isNullable) WriteNullTagEncoded(name, ns); - return; - } - } - if (any) - { - if (node is XmlElement && name != null && name.Length > 0) - { - // need to check against schema - if (node.LocalName != name || node.NamespaceURI != ns) - throw new InvalidOperationException(SR.Format(SR.XmlElementNameMismatch, node.LocalName, node.NamespaceURI, name, ns)); - } - } - else - _w.WriteStartElement(name, ns); - - node.WriteTo(_w); - - if (!any) - _w.WriteEndElement(); - } - - protected Exception CreateUnknownTypeException(object o) - { - return CreateUnknownTypeException(o.GetType()); - } - - protected Exception CreateUnknownTypeException(Type type) - { - if (typeof(IXmlSerializable).IsAssignableFrom(type)) return new InvalidOperationException(SR.Format(SR.XmlInvalidSerializable, type.FullName)); - TypeDesc typeDesc = new TypeScope().GetTypeDesc(type); - if (!typeDesc.IsStructLike) return new InvalidOperationException(SR.Format(SR.XmlInvalidUseOfType, type.FullName)); - return new InvalidOperationException(SR.Format(SR.XmlUnxpectedType, type.FullName)); - } - - protected Exception CreateMismatchChoiceException(string value, string elementName, string enumValue) - { - // Value of {0} mismatches the type of {1}, you need to set it to {2}. - return new InvalidOperationException(SR.Format(SR.XmlChoiceMismatchChoiceException, elementName, value, enumValue)); - } - - protected Exception CreateUnknownAnyElementException(string name, string ns) - { - return new InvalidOperationException(SR.Format(SR.XmlUnknownAnyElement, name, ns)); - } - - protected Exception CreateInvalidChoiceIdentifierValueException(string type, string identifier) - { - return new InvalidOperationException(SR.Format(SR.XmlInvalidChoiceIdentifierValue, type, identifier)); - } - - protected Exception CreateChoiceIdentifierValueException(string value, string identifier, string name, string ns) - { - // XmlChoiceIdentifierMismatch=Value '{0}' of the choice identifier '{1}' does not match element '{2}' from namespace '{3}'. - return new InvalidOperationException(SR.Format(SR.XmlChoiceIdentifierMismatch, value, identifier, name, ns)); - } - - protected Exception CreateInvalidEnumValueException(object value, string typeName) - { - return new InvalidOperationException(SR.Format(SR.XmlUnknownConstant, value, typeName)); - } - - protected Exception CreateInvalidAnyTypeException(object o) - { - return CreateInvalidAnyTypeException(o.GetType()); - } - - protected Exception CreateInvalidAnyTypeException(Type type) - { - return new InvalidOperationException(SR.Format(SR.XmlIllegalAnyElement, type.FullName)); - } - - protected void WriteReferencingElement(string n, string ns, object o) - { - WriteReferencingElement(n, ns, o, false); - } - - protected void WriteReferencingElement(string n, string ns, object o, bool isNullable) - { - if (o == null) - { - if (isNullable) WriteNullTagEncoded(n, ns); - return; - } - WriteStartElement(n, ns, null, true); - if (_soap12) - _w.WriteAttributeString("ref", Soap12.Encoding, GetId(o, true)); - else - _w.WriteAttributeString("href", "#" + GetId(o, true)); - _w.WriteEndElement(); - } - - private bool IsIdDefined(object o) - { - if (_references != null) return _references.Contains(o); - else return false; - } - - private string GetId(object o, bool addToReferencesList) - { - if (_references == null) - { - _references = new Hashtable(); - _referencesToWrite = new ArrayList(); - } - string id = (string)_references[o]; - if (id == null) - { - id = _idBase + "id" + (++_nextId).ToString(CultureInfo.InvariantCulture); - _references.Add(o, id); - if (addToReferencesList) _referencesToWrite.Add(o); - } - return id; - } - - protected void WriteId(object o) - { - WriteId(o, true); - } - - private void WriteId(object o, bool addToReferencesList) - { - if (_soap12) - _w.WriteAttributeString("id", Soap12.Encoding, GetId(o, addToReferencesList)); - else - _w.WriteAttributeString("id", GetId(o, addToReferencesList)); - } - - protected void WriteXmlAttribute(XmlNode node) - { - WriteXmlAttribute(node, null); - } - - protected void WriteXmlAttribute(XmlNode node, object container) - { - XmlAttribute attr = node as XmlAttribute; - if (attr == null) throw new InvalidOperationException(SR.XmlNeedAttributeHere); - if (attr.Value != null) - { - if (attr.NamespaceURI == Wsdl.Namespace && attr.LocalName == Wsdl.ArrayType) - { - string dims; - XmlQualifiedName qname = TypeScope.ParseWsdlArrayType(attr.Value, out dims, (container is XmlSchemaObject) ? (XmlSchemaObject)container : null); - - string value = FromXmlQualifiedName(qname, true) + dims; - - // - WriteAttribute(Wsdl.ArrayType, Wsdl.Namespace, value); - } - else - { - WriteAttribute(attr.Name, attr.NamespaceURI, attr.Value); - } - } - } - - protected void WriteAttribute(string localName, string ns, string value) - { - if (value == null) return; - if (localName == "xmlns" || localName.StartsWith("xmlns:", StringComparison.Ordinal)) - { - ; - } - else - { - int colon = localName.IndexOf(':'); - if (colon < 0) - { - if (ns == XmlReservedNs.NsXml) - { - string prefix = _w.LookupPrefix(ns); - if (prefix == null || prefix.Length == 0) - prefix = "xml"; - _w.WriteAttributeString(prefix, localName, ns, value); - } - else - { - _w.WriteAttributeString(localName, ns, value); - } - } - else - { - string prefix = localName.Substring(0, colon); - _w.WriteAttributeString(prefix, localName.Substring(colon + 1), ns, value); - } - } - } - - protected void WriteAttribute(string localName, string ns, byte[] value) - { - if (value == null) return; - if (localName == "xmlns" || localName.StartsWith("xmlns:", StringComparison.Ordinal)) - { - ; - } - else - { - int colon = localName.IndexOf(':'); - if (colon < 0) - { - if (ns == XmlReservedNs.NsXml) - { - string prefix = _w.LookupPrefix(ns); - if (prefix == null || prefix.Length == 0) - prefix = "xml"; - _w.WriteStartAttribute("xml", localName, ns); - } - else - { - _w.WriteStartAttribute(null, localName, ns); - } - } - else - { - string prefix = _w.LookupPrefix(ns); - _w.WriteStartAttribute(prefix, localName.Substring(colon + 1), ns); - } - XmlCustomFormatter.WriteArrayBase64(_w, value, 0, value.Length); - _w.WriteEndAttribute(); - } - } - - protected void WriteAttribute(string localName, string value) - { - if (value == null) return; - _w.WriteAttributeString(localName, null, value); - } - - protected void WriteAttribute(string localName, byte[] value) - { - if (value == null) return; - - _w.WriteStartAttribute(null, localName, (string)null); - XmlCustomFormatter.WriteArrayBase64(_w, value, 0, value.Length); - _w.WriteEndAttribute(); - } - - protected void WriteAttribute(string prefix, string localName, string ns, string value) - { - if (value == null) return; - _w.WriteAttributeString(prefix, localName, null, value); - } - - protected void WriteValue(string value) - { - if (value == null) return; - _w.WriteString(value); - } - - protected void WriteValue(byte[] value) - { - if (value == null) return; - XmlCustomFormatter.WriteArrayBase64(_w, value, 0, value.Length); - } - - protected void WriteStartDocument() - { - if (_w.WriteState == WriteState.Start) - { - _w.WriteStartDocument(); - } - } - - protected void WriteElementString(String localName, String value) - { - WriteElementString(localName, null, value, null); - } - - protected void WriteElementString(String localName, String ns, String value) - { - WriteElementString(localName, ns, value, null); - } - - protected void WriteElementString(String localName, String value, XmlQualifiedName xsiType) - { - WriteElementString(localName, null, value, xsiType); - } - - protected void WriteElementString(String localName, String ns, String value, XmlQualifiedName xsiType) - { - if (value == null) return; - if (xsiType == null) - _w.WriteElementString(localName, ns, value); - else - { - _w.WriteStartElement(localName, ns); - WriteXsiType(xsiType.Name, xsiType.Namespace); - _w.WriteString(value); - _w.WriteEndElement(); - } - } - - protected void WriteElementStringRaw(String localName, String value) - { - WriteElementStringRaw(localName, null, value, null); - } - - protected void WriteElementStringRaw(String localName, byte[] value) - { - WriteElementStringRaw(localName, null, value, null); - } - - protected void WriteElementStringRaw(String localName, String ns, String value) - { - WriteElementStringRaw(localName, ns, value, null); - } - - protected void WriteElementStringRaw(String localName, String ns, byte[] value) - { - WriteElementStringRaw(localName, ns, value, null); - } - - protected void WriteElementStringRaw(String localName, String value, XmlQualifiedName xsiType) - { - WriteElementStringRaw(localName, null, value, xsiType); - } - - protected void WriteElementStringRaw(String localName, byte[] value, XmlQualifiedName xsiType) - { - WriteElementStringRaw(localName, null, value, xsiType); - } - - protected void WriteElementStringRaw(String localName, String ns, String value, XmlQualifiedName xsiType) - { - if (value == null) return; - _w.WriteStartElement(localName, ns); - if (xsiType != null) - WriteXsiType(xsiType.Name, xsiType.Namespace); - _w.WriteRaw(value); - _w.WriteEndElement(); - } - - protected void WriteElementStringRaw(String localName, String ns, byte[] value, XmlQualifiedName xsiType) - { - if (value == null) return; - _w.WriteStartElement(localName, ns); - if (xsiType != null) - WriteXsiType(xsiType.Name, xsiType.Namespace); - XmlCustomFormatter.WriteArrayBase64(_w, value, 0, value.Length); - _w.WriteEndElement(); - } - - protected void WriteRpcResult(string name, string ns) - { - if (!_soap12) return; - WriteElementQualifiedName(Soap12.RpcResult, Soap12.RpcNamespace, new XmlQualifiedName(name, ns), null); - } - - /// - /// [To be supplied.] - /// - protected void WriteElementQualifiedName(String localName, XmlQualifiedName value) - { - WriteElementQualifiedName(localName, null, value, null); - } - - protected void WriteElementQualifiedName(string localName, XmlQualifiedName value, XmlQualifiedName xsiType) - { - WriteElementQualifiedName(localName, null, value, xsiType); - } - - /// - /// [To be supplied.] - /// - protected void WriteElementQualifiedName(String localName, String ns, XmlQualifiedName value) - { - WriteElementQualifiedName(localName, ns, value, null); - } - - protected void WriteElementQualifiedName(string localName, string ns, XmlQualifiedName value, XmlQualifiedName xsiType) - { - if (value == null) return; - if (value.Namespace == null || value.Namespace.Length == 0) - { - WriteStartElement(localName, ns, null, true); - WriteAttribute("xmlns", ""); - } - else - _w.WriteStartElement(localName, ns); - if (xsiType != null) - WriteXsiType(xsiType.Name, xsiType.Namespace); - _w.WriteString(FromXmlQualifiedName(value, false)); - _w.WriteEndElement(); - } - - protected void AddWriteCallback(Type type, string typeName, string typeNs, XmlSerializationWriteCallback callback) - { - TypeEntry entry = new TypeEntry(); - entry.typeName = typeName; - entry.typeNs = typeNs; - entry.type = type; - entry.callback = callback; - _typeEntries[type] = entry; - } - - private void WriteArray(string name, string ns, object o, Type type) - { - Type elementType = TypeScope.GetArrayElementType(type, null); - string typeName; - string typeNs; - - StringBuilder arrayDims = new StringBuilder(); - if (!_soap12) - { - while ((elementType.IsArray || typeof(IEnumerable).IsAssignableFrom(elementType)) && GetPrimitiveTypeName(elementType, false) == null) - { - elementType = TypeScope.GetArrayElementType(elementType, null); - arrayDims.Append("[]"); - } - } - - if (elementType == typeof(object)) - { - typeName = Soap.UrType; - typeNs = XmlSchema.Namespace; - } - else - { - TypeEntry entry = GetTypeEntry(elementType); - if (entry != null) - { - typeName = entry.typeName; - typeNs = entry.typeNs; - } - else if (_soap12) - { - XmlQualifiedName qualName = GetPrimitiveTypeName(elementType, false); - if (qualName != null) - { - typeName = qualName.Name; - typeNs = qualName.Namespace; - } - else - { - Type elementBaseType = elementType.BaseType; - while (elementBaseType != null) - { - entry = GetTypeEntry(elementBaseType); - if (entry != null) break; - elementBaseType = elementBaseType.BaseType; - } - if (entry != null) - { - typeName = entry.typeName; - typeNs = entry.typeNs; - } - else - { - typeName = Soap.UrType; - typeNs = XmlSchema.Namespace; - } - } - } - else - { - XmlQualifiedName qualName = GetPrimitiveTypeName(elementType); - typeName = qualName.Name; - typeNs = qualName.Namespace; - } - } - - if (arrayDims.Length > 0) - typeName = typeName + arrayDims.ToString(); - - if (_soap12 && name != null && name.Length > 0) - WriteStartElement(name, ns, null, false); - else - WriteStartElement(Soap.Array, Soap.Encoding, null, true); - - WriteId(o, false); - - if (type.IsArray) - { - Array a = (Array)o; - int arrayLength = a.Length; - if (_soap12) - { - _w.WriteAttributeString("itemType", Soap12.Encoding, GetQualifiedName(typeName, typeNs)); - _w.WriteAttributeString("arraySize", Soap12.Encoding, arrayLength.ToString(CultureInfo.InvariantCulture)); - } - else - { - _w.WriteAttributeString("arrayType", Soap.Encoding, GetQualifiedName(typeName, typeNs) + "[" + arrayLength.ToString(CultureInfo.InvariantCulture) + "]"); - } - for (int i = 0; i < arrayLength; i++) - { - WritePotentiallyReferencingElement("Item", "", a.GetValue(i), elementType, false, true); - } - } - else - { -#if DEBUG - // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe - if (!typeof(IEnumerable).IsAssignableFrom(type)) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorDetails, "not array like type " + type.FullName)); -#endif - - int arrayLength = typeof(ICollection).IsAssignableFrom(type) ? ((ICollection)o).Count : -1; - if (_soap12) - { - _w.WriteAttributeString("itemType", Soap12.Encoding, GetQualifiedName(typeName, typeNs)); - if (arrayLength >= 0) - _w.WriteAttributeString("arraySize", Soap12.Encoding, arrayLength.ToString(CultureInfo.InvariantCulture)); - } - else - { - string brackets = arrayLength >= 0 ? "[" + arrayLength + "]" : "[]"; - _w.WriteAttributeString("arrayType", Soap.Encoding, GetQualifiedName(typeName, typeNs) + brackets); - } - IEnumerator e = ((IEnumerable)o).GetEnumerator(); - if (e != null) - { - while (e.MoveNext()) - { - WritePotentiallyReferencingElement("Item", "", e.Current, elementType, false, true); - } - } - } - _w.WriteEndElement(); - } - protected void WritePotentiallyReferencingElement(string n, string ns, object o) - { - WritePotentiallyReferencingElement(n, ns, o, null, false, false); - } - - protected void WritePotentiallyReferencingElement(string n, string ns, object o, Type ambientType) - { - WritePotentiallyReferencingElement(n, ns, o, ambientType, false, false); - } - - protected void WritePotentiallyReferencingElement(string n, string ns, object o, Type ambientType, bool suppressReference) - { - WritePotentiallyReferencingElement(n, ns, o, ambientType, suppressReference, false); - } - - protected void WritePotentiallyReferencingElement(string n, string ns, object o, Type ambientType, bool suppressReference, bool isNullable) - { - if (o == null) - { - if (isNullable) WriteNullTagEncoded(n, ns); - return; - } - Type t = o.GetType(); - if (t.GetTypeCode() == TypeCode.Object && !(o is Guid) && (t != typeof(XmlQualifiedName)) && !(o is XmlNode[]) && (t != typeof(byte[]))) - { - if ((suppressReference || _soap12) && !IsIdDefined(o)) - { - WriteReferencedElement(n, ns, o, ambientType); - } - else - { - if (n == null) - { - TypeEntry entry = GetTypeEntry(t); - WriteReferencingElement(entry.typeName, entry.typeNs, o, isNullable); - } - else - WriteReferencingElement(n, ns, o, isNullable); - } - } - else - { - // Enums always write xsi:type, so don't write it again here. - bool needXsiType = t != ambientType && !t.IsEnum; - TypeEntry entry = GetTypeEntry(t); - if (entry != null) - { - if (n == null) - WriteStartElement(entry.typeName, entry.typeNs, null, true); - else - WriteStartElement(n, ns, null, true); - - if (needXsiType) WriteXsiType(entry.typeName, entry.typeNs); - entry.callback(o); - _w.WriteEndElement(); - } - else - { - WriteTypedPrimitive(n, ns, o, needXsiType); - } - } - } - - - private void WriteReferencedElement(object o, Type ambientType) - { - WriteReferencedElement(null, null, o, ambientType); - } - - private void WriteReferencedElement(string name, string ns, object o, Type ambientType) - { - if (name == null) name = String.Empty; - Type t = o.GetType(); - if (t.IsArray || typeof(IEnumerable).IsAssignableFrom(t)) - { - WriteArray(name, ns, o, t); - } - else - { - TypeEntry entry = GetTypeEntry(t); - if (entry == null) throw CreateUnknownTypeException(t); - WriteStartElement(name.Length == 0 ? entry.typeName : name, ns == null ? entry.typeNs : ns, null, true); - WriteId(o, false); - if (ambientType != t) WriteXsiType(entry.typeName, entry.typeNs); - entry.callback(o); - _w.WriteEndElement(); - } - } - - private TypeEntry GetTypeEntry(Type t) - { - if (_typeEntries == null) - { - _typeEntries = new Hashtable(); - InitCallbacks(); - } - return (TypeEntry)_typeEntries[t]; - } - - protected abstract void InitCallbacks(); - - protected void WriteReferencedElements() - { - if (_referencesToWrite == null) return; - - for (int i = 0; i < _referencesToWrite.Count; i++) - { - WriteReferencedElement(_referencesToWrite[i], null); - } - } - - protected void TopLevelElement() - { - _objectsInUse = new Hashtable(); - } - - /// - protected void WriteNamespaceDeclarations(XmlSerializerNamespaces xmlns) - { - if (xmlns != null) - { - foreach (KeyValuePair entry in xmlns.Namespaces) - { - string prefix = (string)entry.Key; - string ns = (string)entry.Value; - if (_namespaces != null) - { - string oldNs; - if (_namespaces.Namespaces.TryGetValue(prefix, out oldNs) && oldNs != null && oldNs != ns) - { - throw new InvalidOperationException(SR.Format(SR.XmlDuplicateNs, prefix, ns)); - } - } - string oldPrefix = (ns == null || ns.Length == 0) ? null : Writer.LookupPrefix(ns); - - if (oldPrefix == null || oldPrefix != prefix) - { - WriteAttribute(nameof(xmlns), prefix, null, ns); - } - } - } - _namespaces = null; - } - - private string NextPrefix() - { - if (_usedPrefixes == null) - { - return _aliasBase + (++_tempNamespacePrefix); - } - while (_usedPrefixes.Contains(++_tempNamespacePrefix)) {; } - return _aliasBase + _tempNamespacePrefix; - } - - internal class TypeEntry - { - internal XmlSerializationWriteCallback callback; - internal string typeNs; - internal string typeName; - internal Type type; - } - } - - - /// -#if XMLSERIALIZERGENERATOR - internal delegate void XmlSerializationWriteCallback(object o); -#else - public delegate void XmlSerializationWriteCallback(object o); -#endif - - - internal static class DynamicAssemblies - { - private static ArrayList s_assembliesInConfig = new ArrayList(); - private static volatile Hashtable s_nameToAssemblyMap = new Hashtable(); - private static volatile Hashtable s_assemblyToNameMap = new Hashtable(); - private static Hashtable s_tableIsTypeDynamic = Hashtable.Synchronized(new Hashtable()); - - // SxS: This method does not take any resource name and does not expose any resources to the caller. - // It's OK to suppress the SxS warning. - internal static bool IsTypeDynamic(Type type) - { - object oIsTypeDynamic = s_tableIsTypeDynamic[type]; - if (oIsTypeDynamic == null) - { - Assembly assembly = type.Assembly; - bool isTypeDynamic = assembly.IsDynamic /*|| string.IsNullOrEmpty(assembly.Location)*/; - if (!isTypeDynamic) - { - if (type.IsArray) - { - isTypeDynamic = IsTypeDynamic(type.GetElementType()); - } - else if (type.IsGenericType) - { - Type[] parameterTypes = type.GetGenericArguments(); - if (parameterTypes != null) - { - for (int i = 0; i < parameterTypes.Length; i++) - { - Type parameterType = parameterTypes[i]; - if (!(parameterType == null || parameterType.IsGenericParameter)) - { - isTypeDynamic = IsTypeDynamic(parameterType); - if (isTypeDynamic) - break; - } - } - } - } - } - s_tableIsTypeDynamic[type] = oIsTypeDynamic = isTypeDynamic; - } - return (bool)oIsTypeDynamic; - } - -#if XMLSERIALIZERGENERATOR - internal static bool IsTypeDynamic(Type[] arguments) - { - foreach (Type t in arguments) - { - if (DynamicAssemblies.IsTypeDynamic(t)) - { - return true; - } - } - return false; - } - - internal static void Add(Assembly a) - { - lock (s_nameToAssemblyMap) - { - if (s_assemblyToNameMap[a] != null) - { - //already added - return; - } - Assembly oldAssembly = s_nameToAssemblyMap[a.FullName] as Assembly; - string key = null; - if (oldAssembly == null) - { - key = a.FullName; - } - else if (oldAssembly != a) - { - //more than one assembly with same name - key = a.FullName + ", " + s_nameToAssemblyMap.Count; - } - if (key != null) - { - s_nameToAssemblyMap.Add(key, a); - s_assemblyToNameMap.Add(a, key); - } - } - } -#endif - - internal static Assembly Get(string fullName) - { - return s_nameToAssemblyMap != null ? (Assembly)s_nameToAssemblyMap[fullName] : null; - } - -#if XMLSERIALIZERGENERATOR - internal static string GetName(Assembly a) - { - return s_assemblyToNameMap != null ? (string) s_assemblyToNameMap[a] : null; - } -#endif - } - -#if XMLSERIALIZERGENERATOR - internal class ReflectionAwareCodeGen - { - private const string hexDigits = "0123456789ABCDEF"; - private const string arrayMemberKey = "0"; - // reflectionVariables holds mapping between a reflection entity - // referenced in the generated code (such as TypeInfo, - // FieldInfo) and the variable which represent the entity (and - // initialized before). - // The types of reflection entity and corresponding key is - // given below. - // ---------------------------------------------------------------------------------- - // Entity Key - // ---------------------------------------------------------------------------------- - // Assembly assembly.FullName - // Type CodeIdentifier.EscapedKeywords(type.FullName) - // Field fieldName+":"+CodeIdentifier.EscapedKeywords(containingType.FullName>) - // Property propertyName+":"+CodeIdentifier.EscapedKeywords(containingType.FullName) - // ArrayAccessor "0:"+CodeIdentifier.EscapedKeywords(typeof(Array).FullName) - // MyCollectionAccessor "0:"+CodeIdentifier.EscapedKeywords(typeof(MyCollection).FullName) - // ---------------------------------------------------------------------------------- - private Hashtable _reflectionVariables = null; - private int _nextReflectionVariableNumber = 0; - private IndentedWriter _writer; - internal ReflectionAwareCodeGen(IndentedWriter writer) - { - _writer = writer; - } - - internal void WriteReflectionInit(TypeScope scope) - { - foreach (Type type in scope.Types) - { - TypeDesc typeDesc = scope.GetTypeDesc(type); - if (typeDesc.UseReflection) - WriteTypeInfo(scope, typeDesc, type); - } - } - - private string WriteTypeInfo(TypeScope scope, TypeDesc typeDesc, Type type) - { - InitTheFirstTime(); - string typeFullName = typeDesc.CSharpName; - string typeVariable = (string)_reflectionVariables[typeFullName]; - if (typeVariable != null) - return typeVariable; - - if (type.IsArray) - { - typeVariable = GenerateVariableName("array", typeDesc.CSharpName); - TypeDesc elementTypeDesc = typeDesc.ArrayElementTypeDesc; - if (elementTypeDesc.UseReflection) - { - string elementTypeVariable = WriteTypeInfo(scope, elementTypeDesc, scope.GetTypeFromTypeDesc(elementTypeDesc)); - _writer.WriteLine("static " + typeof(Type).FullName + " " + typeVariable + " = " + elementTypeVariable + ".MakeArrayType();"); - } - else - { - string assemblyVariable = WriteAssemblyInfo(type); - _writer.Write("static " + typeof(Type).FullName + " " + typeVariable + " = " + assemblyVariable + ".GetType("); - WriteQuotedCSharpString(type.FullName); - _writer.WriteLine(");"); - } - } - else - { - typeVariable = GenerateVariableName(nameof(type), typeDesc.CSharpName); - - Type parameterType = Nullable.GetUnderlyingType(type); - if (parameterType != null) - { - string parameterTypeVariable = WriteTypeInfo(scope, scope.GetTypeDesc(parameterType), parameterType); - _writer.WriteLine("static " + typeof(Type).FullName + " " + typeVariable + " = typeof(System.Nullable<>).MakeGenericType(new " + typeof(Type).FullName + "[] {" + parameterTypeVariable + "});"); - } - else - { - string assemblyVariable = WriteAssemblyInfo(type); - _writer.Write("static " + typeof(Type).FullName + " " + typeVariable + " = " + assemblyVariable + ".GetType("); - WriteQuotedCSharpString(type.FullName); - _writer.WriteLine(");"); - } - } - - _reflectionVariables.Add(typeFullName, typeVariable); - - TypeMapping mapping = scope.GetTypeMappingFromTypeDesc(typeDesc); - if (mapping != null) - WriteMappingInfo(mapping, typeVariable, type); - if (typeDesc.IsCollection || typeDesc.IsEnumerable) - {// Arrays use the generic item_Array - TypeDesc elementTypeDesc = typeDesc.ArrayElementTypeDesc; - if (elementTypeDesc.UseReflection) - WriteTypeInfo(scope, elementTypeDesc, scope.GetTypeFromTypeDesc(elementTypeDesc)); - WriteCollectionInfo(typeVariable, typeDesc, type); - } - return typeVariable; - } - - private void InitTheFirstTime() - { - if (_reflectionVariables == null) - { - _reflectionVariables = new Hashtable(); - _writer.Write(String.Format(CultureInfo.InvariantCulture, s_helperClassesForUseReflection, - "object", "string", typeof(Type).FullName, - typeof(FieldInfo).FullName, typeof(PropertyInfo).FullName, - typeof(MemberInfo).FullName /*, typeof(MemberTypes).FullName*/)); - - WriteDefaultIndexerInit(typeof(IList), typeof(Array).FullName, false, false); - } - } - - private void WriteMappingInfo(TypeMapping mapping, string typeVariable, Type type) - { - string typeFullName = mapping.TypeDesc.CSharpName; - if (mapping is StructMapping) - { - StructMapping structMapping = mapping as StructMapping; - for (int i = 0; i < structMapping.Members.Length; i++) - { - MemberMapping member = structMapping.Members[i]; - string memberVariable = WriteMemberInfo(type, typeFullName, typeVariable, member.Name); - if (member.CheckShouldPersist) - { - string memberName = "ShouldSerialize" + member.Name; - memberVariable = WriteMethodInfo(typeFullName, typeVariable, memberName, false); - } - if (member.CheckSpecified != SpecifiedAccessor.None) - { - string memberName = member.Name + "Specified"; - memberVariable = WriteMemberInfo(type, typeFullName, typeVariable, memberName); - } - if (member.ChoiceIdentifier != null) - { - string memberName = member.ChoiceIdentifier.MemberName; - memberVariable = WriteMemberInfo(type, typeFullName, typeVariable, memberName); - } - } - } - else if (mapping is EnumMapping) - { - FieldInfo[] enumFields = type.GetFields(); - for (int i = 0; i < enumFields.Length; i++) - { - WriteMemberInfo(type, typeFullName, typeVariable, enumFields[i].Name); - } - } - } - private void WriteCollectionInfo(string typeVariable, TypeDesc typeDesc, Type type) - { - string typeFullName = CodeIdentifier.GetCSharpName(type); - string elementTypeFullName = typeDesc.ArrayElementTypeDesc.CSharpName; - bool elementUseReflection = typeDesc.ArrayElementTypeDesc.UseReflection; - if (typeDesc.IsCollection) - { - WriteDefaultIndexerInit(type, typeFullName, typeDesc.UseReflection, elementUseReflection); - } - else if (typeDesc.IsEnumerable) - { - if (typeDesc.IsGenericInterface) - { - WriteMethodInfo(typeFullName, typeVariable, "System.Collections.Generic.IEnumerable*", true); - } - else if (!typeDesc.IsPrivateImplementation) - { - WriteMethodInfo(typeFullName, typeVariable, "GetEnumerator", true); - } - } - WriteMethodInfo(typeFullName, typeVariable, "Add", false, GetStringForTypeof(elementTypeFullName, elementUseReflection)); - } - - private string WriteAssemblyInfo(Type type) - { - string assemblyFullName = type.Assembly.FullName; - string assemblyVariable = (string)_reflectionVariables[assemblyFullName]; - if (assemblyVariable == null) - { - int iComma = assemblyFullName.IndexOf(','); - string assemblyName = (iComma > -1) ? assemblyFullName.Substring(0, iComma) : assemblyFullName; - assemblyVariable = GenerateVariableName("assembly", assemblyName); - //writer.WriteLine("static "+ typeof(Assembly).FullName+" "+assemblyVariable+" = "+typeof(Assembly).FullName+".Load("); - _writer.Write("static " + typeof(Assembly).FullName + " " + assemblyVariable + " = " + "ResolveDynamicAssembly("); - WriteQuotedCSharpString(DynamicAssemblies.GetName(type.Assembly)/*assemblyFullName*/); - _writer.WriteLine(");"); - _reflectionVariables.Add(assemblyFullName, assemblyVariable); - } - return assemblyVariable; - } - - private string WriteMemberInfo(Type type, string escapedName, string typeVariable, string memberName) - { - MemberInfo[] memberInfos = type.GetMember(memberName); - for (int i = 0; i < memberInfos.Length; i++) - { - if (memberInfos[i] is PropertyInfo) - { - string propVariable = GenerateVariableName("prop", memberName); - _writer.Write("static XSPropInfo " + propVariable + " = new XSPropInfo(" + typeVariable + ", "); - WriteQuotedCSharpString(memberName); - _writer.WriteLine(");"); - _reflectionVariables.Add(memberName + ":" + escapedName, propVariable); - return propVariable; - } - else if (memberInfos[i] is FieldInfo) - { - string fieldVariable = GenerateVariableName("field", memberName); - _writer.Write("static XSFieldInfo " + fieldVariable + " = new XSFieldInfo(" + typeVariable + ", "); - WriteQuotedCSharpString(memberName); - _writer.WriteLine(");"); - _reflectionVariables.Add(memberName + ":" + escapedName, fieldVariable); - return fieldVariable; - } - } - throw new InvalidOperationException(SR.Format(SR.XmlSerializerUnsupportedType, memberInfos[0].ToString())); - } - - private string WriteMethodInfo(string escapedName, string typeVariable, string memberName, bool isNonPublic, params string[] paramTypes) - { - string methodVariable = GenerateVariableName("method", memberName); - _writer.Write("static " + typeof(MethodInfo).FullName + " " + methodVariable + " = " + typeVariable + ".GetMethod("); - WriteQuotedCSharpString(memberName); - _writer.Write(", "); - - string bindingFlags = typeof(BindingFlags).FullName; - _writer.Write(bindingFlags); - _writer.Write(".Public | "); - _writer.Write(bindingFlags); - _writer.Write(".Instance | "); - _writer.Write(bindingFlags); - _writer.Write(".Static"); - - if (isNonPublic) - { - _writer.Write(" | "); - _writer.Write(bindingFlags); - _writer.Write(".NonPublic"); - } - _writer.Write(", null, "); - _writer.Write("new " + typeof(Type).FullName + "[] { "); - for (int i = 0; i < paramTypes.Length; i++) - { - _writer.Write(paramTypes[i]); - if (i < (paramTypes.Length - 1)) - _writer.Write(", "); - } - _writer.WriteLine("}, null);"); - _reflectionVariables.Add(memberName + ":" + escapedName, methodVariable); - return methodVariable; - } - - private string WriteDefaultIndexerInit(Type type, string escapedName, bool collectionUseReflection, bool elementUseReflection) - { - string itemVariable = GenerateVariableName("item", escapedName); - PropertyInfo defaultIndexer = TypeScope.GetDefaultIndexer(type, null); - _writer.Write("static XSArrayInfo "); - _writer.Write(itemVariable); - _writer.Write("= new XSArrayInfo("); - _writer.Write(GetStringForTypeof(CodeIdentifier.GetCSharpName(type), collectionUseReflection)); - _writer.Write(".GetProperty("); - WriteQuotedCSharpString(defaultIndexer.Name); - _writer.Write(","); - //defaultIndexer.PropertyType is same as TypeDesc.ElementTypeDesc - _writer.Write(GetStringForTypeof(CodeIdentifier.GetCSharpName(defaultIndexer.PropertyType), elementUseReflection)); - _writer.Write(",new "); - _writer.Write(typeof(Type[]).FullName); - _writer.WriteLine("{typeof(int)}));"); - _reflectionVariables.Add(arrayMemberKey + ":" + escapedName, itemVariable); - return itemVariable; - } - - private string GenerateVariableName(string prefix, string fullName) - { - ++_nextReflectionVariableNumber; - return prefix + _nextReflectionVariableNumber + "_" + - CodeIdentifier.MakeValidInternal(fullName.Replace('.', '_')); - } - internal string GetReflectionVariable(string typeFullName, string memberName) - { - string key; - if (memberName == null) - key = typeFullName; - else - key = memberName + ":" + typeFullName; - return (string)_reflectionVariables[key]; - } - - - internal string GetStringForMethodInvoke(string obj, string escapedTypeName, string methodName, bool useReflection, params string[] args) - { - StringBuilder sb = new StringBuilder(); - if (useReflection) - { - sb.Append(GetReflectionVariable(escapedTypeName, methodName)); - sb.Append(".Invoke("); - sb.Append(obj); - sb.Append(", new object[] {"); - } - else - { - sb.Append(obj); - sb.Append(".@"); - sb.Append(methodName); - sb.Append("("); - } - for (int i = 0; i < args.Length; i++) - { - if (i != 0) - sb.Append(", "); - sb.Append(args[i]); - } - if (useReflection) - sb.Append("})"); - else - sb.Append(")"); - return sb.ToString(); - } - - internal string GetStringForEnumCompare(EnumMapping mapping, string memberName, bool useReflection) - { - if (!useReflection) - { - CodeIdentifier.CheckValidIdentifier(memberName); - return mapping.TypeDesc.CSharpName + ".@" + memberName; - } - string memberAccess = GetStringForEnumMember(mapping.TypeDesc.CSharpName, memberName, useReflection); - return GetStringForEnumLongValue(memberAccess, useReflection); - } - internal string GetStringForEnumLongValue(string variable, bool useReflection) - { - if (useReflection) - return typeof(Convert).FullName + ".ToInt64(" + variable + ")"; - return "((" + typeof(long).FullName + ")" + variable + ")"; - } - - internal string GetStringForTypeof(string typeFullName, bool useReflection) - { - if (useReflection) - { - return GetReflectionVariable(typeFullName, null); - } - else - { - return "typeof(" + typeFullName + ")"; - } - } - internal string GetStringForMember(string obj, string memberName, TypeDesc typeDesc) - { - if (!typeDesc.UseReflection) - return obj + ".@" + memberName; - - TypeDesc saveTypeDesc = typeDesc; - while (typeDesc != null) - { - string typeFullName = typeDesc.CSharpName; - string memberInfoName = GetReflectionVariable(typeFullName, memberName); - if (memberInfoName != null) - return memberInfoName + "[" + obj + "]"; - // member may be part of the basetype - typeDesc = typeDesc.BaseTypeDesc; - if (typeDesc != null && !typeDesc.UseReflection) - return "((" + typeDesc.CSharpName + ")" + obj + ").@" + memberName; - } - //throw GetReflectionVariableException(saveTypeDesc.CSharpName,memberName); - // NOTE, sowmys:Must never happen. If it does let the code - // gen continue to help debugging what's gone wrong. - // Eventually the compilation will fail. - return "[" + obj + "]"; - } - /* - Exception GetReflectionVariableException(string typeFullName, string memberName){ - string key; - if(memberName == null) - key = typeFullName; - else - key = memberName+":"+typeFullName; - System.Text.StringBuilder sb = new System.Text.StringBuilder(); - foreach(object varAvail in reflectionVariables.Keys){ - sb.Append(varAvail.ToString()); - sb.Append("\n"); - } - return new Exception("No reflection variable for " + key + "\nAvailable keys\n"+sb.ToString()); - }*/ - - internal string GetStringForEnumMember(string typeFullName, string memberName, bool useReflection) - { - if (!useReflection) - return typeFullName + ".@" + memberName; - - string memberInfoName = GetReflectionVariable(typeFullName, memberName); - return memberInfoName + "[null]"; - } - internal string GetStringForArrayMember(string arrayName, string subscript, TypeDesc arrayTypeDesc) - { - if (!arrayTypeDesc.UseReflection) - { - return arrayName + "[" + subscript + "]"; - } - string typeFullName = arrayTypeDesc.IsCollection ? arrayTypeDesc.CSharpName : typeof(Array).FullName; - string arrayInfo = GetReflectionVariable(typeFullName, arrayMemberKey); - return arrayInfo + "[" + arrayName + ", " + subscript + "]"; - } - internal string GetStringForMethod(string obj, string typeFullName, string memberName, bool useReflection) - { - if (!useReflection) - return obj + "." + memberName + "("; - - string memberInfoName = GetReflectionVariable(typeFullName, memberName); - return memberInfoName + ".Invoke(" + obj + ", new object[]{"; - } - internal string GetStringForCreateInstance(string escapedTypeName, bool useReflection, bool ctorInaccessible, bool cast) - { - return GetStringForCreateInstance(escapedTypeName, useReflection, ctorInaccessible, cast, string.Empty); - } - - internal string GetStringForCreateInstance(string escapedTypeName, bool useReflection, bool ctorInaccessible, bool cast, string arg) - { - if (!useReflection && !ctorInaccessible) - return "new " + escapedTypeName + "(" + arg + ")"; - return GetStringForCreateInstance(GetStringForTypeof(escapedTypeName, useReflection), cast && !useReflection ? escapedTypeName : null, ctorInaccessible, arg); - } - - internal string GetStringForCreateInstance(string type, string cast, bool nonPublic, string arg) - { - StringBuilder createInstance = new StringBuilder(); - if (cast != null && cast.Length > 0) - { - createInstance.Append("("); - createInstance.Append(cast); - createInstance.Append(")"); - } - createInstance.Append(typeof(Activator).FullName); - createInstance.Append(".CreateInstance("); - createInstance.Append(type); - createInstance.Append(", "); - string bindingFlags = typeof(BindingFlags).FullName; - createInstance.Append(bindingFlags); - createInstance.Append(".Instance | "); - createInstance.Append(bindingFlags); - createInstance.Append(".Public | "); - createInstance.Append(bindingFlags); - createInstance.Append(".CreateInstance"); - - if (nonPublic) - { - createInstance.Append(" | "); - createInstance.Append(bindingFlags); - createInstance.Append(".NonPublic"); - } - if (arg == null || arg.Length == 0) - { - createInstance.Append(", null, new object[0], null)"); - } - else - { - createInstance.Append(", null, new object[] { "); - createInstance.Append(arg); - createInstance.Append(" }, null)"); - } - return createInstance.ToString(); - } - - internal void WriteLocalDecl(string typeFullName, string variableName, string initValue, bool useReflection) - { - if (useReflection) - typeFullName = "object"; - _writer.Write(typeFullName); - _writer.Write(" "); - _writer.Write(variableName); - if (initValue != null) - { - _writer.Write(" = "); - if (!useReflection && initValue != "null") - { - _writer.Write("(" + typeFullName + ")"); - } - _writer.Write(initValue); - } - _writer.WriteLine(";"); - } - - internal void WriteCreateInstance(string escapedName, string source, bool useReflection, bool ctorInaccessible) - { - _writer.Write(useReflection ? "object" : escapedName); - _writer.Write(" "); - _writer.Write(source); - _writer.Write(" = "); - _writer.Write(GetStringForCreateInstance(escapedName, useReflection, ctorInaccessible, !useReflection && ctorInaccessible)); - _writer.WriteLine(";"); - } - internal void WriteInstanceOf(string source, string escapedTypeName, bool useReflection) - { - if (!useReflection) - { - _writer.Write(source); - _writer.Write(" is "); - _writer.Write(escapedTypeName); - return; - } - _writer.Write(GetReflectionVariable(escapedTypeName, null)); - _writer.Write(".IsAssignableFrom("); - _writer.Write(source); - _writer.Write(".GetType())"); - } - - internal void WriteArrayLocalDecl(string typeName, string variableName, string initValue, TypeDesc arrayTypeDesc) - { - if (arrayTypeDesc.UseReflection) - { - if (arrayTypeDesc.IsEnumerable) - typeName = typeof(IEnumerable).FullName; - else if (arrayTypeDesc.IsCollection) - typeName = typeof(ICollection).FullName; - else - typeName = typeof(Array).FullName; - } - _writer.Write(typeName); - _writer.Write(" "); - _writer.Write(variableName); - if (initValue != null) - { - _writer.Write(" = "); - if (initValue != "null") - _writer.Write("(" + typeName + ")"); - _writer.Write(initValue); - } - _writer.WriteLine(";"); - } - internal void WriteEnumCase(string fullTypeName, ConstantMapping c, bool useReflection) - { - _writer.Write("case "); - if (useReflection) - { - _writer.Write(c.Value.ToString(CultureInfo.InvariantCulture)); - } - else - { - _writer.Write(fullTypeName); - _writer.Write(".@"); - CodeIdentifier.CheckValidIdentifier(c.Name); - _writer.Write(c.Name); - } - _writer.Write(": "); - } - internal void WriteTypeCompare(string variable, string escapedTypeName, bool useReflection) - { - _writer.Write(variable); - _writer.Write(" == "); - _writer.Write(GetStringForTypeof(escapedTypeName, useReflection)); - } - internal void WriteArrayTypeCompare(string variable, string escapedTypeName, string elementTypeName, bool useReflection) - { - if (!useReflection) - { - _writer.Write(variable); - _writer.Write(" == typeof("); - _writer.Write(escapedTypeName); - _writer.Write(")"); - return; - } - _writer.Write(variable); - _writer.Write(".IsArray "); - _writer.Write(" && "); - WriteTypeCompare(variable + ".GetElementType()", elementTypeName, useReflection); - } - - internal static void WriteQuotedCSharpString(IndentedWriter writer, string value) - { - if (value == null) - { - writer.Write("null"); - return; - } - writer.Write("@\""); - foreach (char ch in value) - { - if (ch < 32) - { - if (ch == '\r') - writer.Write("\\r"); - else if (ch == '\n') - writer.Write("\\n"); - else if (ch == '\t') - writer.Write("\\t"); - else - { - byte b = (byte)ch; - writer.Write("\\x"); - writer.Write(hexDigits[b >> 4]); - writer.Write(hexDigits[b & 0xF]); - } - } - else if (ch == '\"') - { - writer.Write("\"\""); - } - else - { - writer.Write(ch); - } - } - writer.Write("\""); - } - - internal void WriteQuotedCSharpString(string value) - { - WriteQuotedCSharpString(_writer, value); - } - - private static string s_helperClassesForUseReflection = @" - sealed class XSFieldInfo {{ - {3} fieldInfo; - public XSFieldInfo({2} t, {1} memberName){{ - fieldInfo = t.GetField(memberName); - }} - public {0} this[{0} o] {{ - get {{ - return fieldInfo.GetValue(o); - }} - set {{ - fieldInfo.SetValue(o, value); - }} - }} - - }} - sealed class XSPropInfo {{ - {4} propInfo; - public XSPropInfo({2} t, {1} memberName){{ - propInfo = t.GetProperty(memberName); - }} - public {0} this[{0} o] {{ - get {{ - return propInfo.GetValue(o, null); - }} - set {{ - propInfo.SetValue(o, value, null); - }} - }} - }} - sealed class XSArrayInfo {{ - {4} propInfo; - public XSArrayInfo({4} propInfo){{ - this.propInfo = propInfo; - }} - public {0} this[{0} a, int i] {{ - get {{ - return propInfo.GetValue(a, new {0}[]{{i}}); - }} - set {{ - propInfo.SetValue(a, value, new {0}[]{{i}}); - }} - }} - }} -"; - } -#endif -} diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs.REMOVED.git-id b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs.REMOVED.git-id new file mode 100644 index 0000000000..291b292c0f --- /dev/null +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs.REMOVED.git-id @@ -0,0 +1 @@ +766ee3628abf98b82ebc3f3487674f1331e97147 \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs index 18532d5659..e7a3342436 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System.Reflection; using System.Collections; @@ -28,19 +24,13 @@ namespace System.Xml.Serialization /// /// [To be supplied.] /// -#if XMLSERIALIZERGENERATOR - internal struct XmlDeserializationEvents -#else public struct XmlDeserializationEvents -#endif { private XmlNodeEventHandler _onUnknownNode; private XmlAttributeEventHandler _onUnknownAttribute; private XmlElementEventHandler _onUnknownElement; private UnreferencedObjectEventHandler _onUnreferencedObject; -#if !XMLSERIALIZERGENERATOR internal object sender; -#endif /// public XmlNodeEventHandler OnUnknownNode @@ -101,11 +91,7 @@ namespace System.Xml.Serialization /// /// [To be supplied.] /// -#if XMLSERIALIZERGENERATOR - internal abstract class XmlSerializerImplementation -#else public abstract class XmlSerializerImplementation -#endif { /// public virtual XmlSerializationReader Reader { get { throw new NotSupportedException(); } } @@ -143,11 +129,7 @@ namespace System.Xml.Serialization /// /// [To be supplied.] /// -#if XMLSERIALIZERGENERATOR - internal class XmlSerializer -#else public class XmlSerializer -#endif { #if FEATURE_SERIALIZATION_UAPAOT public static SerializationMode Mode { get; set; } = SerializationMode.ReflectionAsBackup; @@ -387,7 +369,6 @@ namespace System.Xml.Serialization return new TempAssembly(new XmlMapping[] { xmlMapping }, new Type[] { type }, defaultNamespace, location); } -#if !XMLSERIALIZERGENERATOR /// /// /// [To be supplied.] @@ -684,7 +665,6 @@ namespace System.Xml.Serialization var reader = new ReflectionXmlSerializationReader(mapping, xmlReader, events, encodingStyle); return reader.ReadObject(); } -#endif private static bool ShouldUseReflectionBasedSerialization(XmlMapping mapping) { @@ -764,6 +744,13 @@ namespace System.Xml.Serialization TempAssembly tempAssembly = null; if (assembly == null) { + if (Mode == SerializationMode.PreGenOnly) + { + AssemblyName name = type.Assembly.GetName(); + string serializerName = Compiler.GetTempAssemblyName(name, null); + throw new FileLoadException(SR.Format(SR.FailLoadAssemblyUnderPregenMode, serializerName)); + } + if (XmlMapping.IsShallow(mappings)) { return Array.Empty(); @@ -815,7 +802,7 @@ namespace System.Xml.Serialization return serializers; } -#if XMLSERIALIZERGENERATOR +#if !FEATURE_SERIALIZATION_UAPAOT [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] [ResourceExposure(ResourceScope.None)] public static bool GenerateSerializer(Type[] types, XmlMapping[] mappings, Stream stream) @@ -1074,7 +1061,6 @@ namespace System.Xml.Serialization return mapping; } -#if !XMLSERIALIZERGENERATOR private void SerializePrimitive(XmlWriter xmlWriter, object o, XmlSerializerNamespaces namespaces) { XmlSerializationPrimitiveWriter writer = new XmlSerializationPrimitiveWriter(); @@ -1231,7 +1217,6 @@ namespace System.Xml.Serialization return o; } -#endif private class XmlSerializerMappingKey { public XmlMapping Mapping; diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializerFactory.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializerFactory.cs index 737106267f..cfd9374aff 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializerFactory.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializerFactory.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System.Reflection; using System.Collections; @@ -25,11 +21,7 @@ namespace System.Xml.Serialization /// /// [To be supplied.] /// -#if XMLSERIALIZERGENERATOR - internal class XmlSerializerFactory -#else public class XmlSerializerFactory -#endif { private static TempAssemblyCache s_cache = new TempAssemblyCache(); diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializerNamespaces.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializerNamespaces.cs index f4444900fa..7d772e0e67 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializerNamespaces.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializerNamespaces.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System.Reflection; using System.Collections; @@ -20,11 +16,7 @@ namespace System.Xml.Serialization /// /// [To be supplied.] /// -#if XMLSERIALIZERGENERATOR - internal class XmlSerializerNamespaces -#else public class XmlSerializerNamespaces -#endif { private Dictionary _namespaces = null; diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlTypeMapping.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlTypeMapping.cs index 59debad11c..ead21258e2 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlTypeMapping.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/XmlTypeMapping.cs @@ -6,20 +6,12 @@ using System.Reflection; using System; -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { /// /// [To be supplied.] /// -#if XMLSERIALIZERGENERATOR - internal class XmlTypeMapping : XmlMapping -#else public class XmlTypeMapping : XmlMapping -#endif { internal XmlTypeMapping(TypeScope scope, ElementAccessor accessor) : base(scope, accessor) { diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Xmlcustomformatter.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Xmlcustomformatter.cs index d13835b485..5fb8b43de1 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Xmlcustomformatter.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/Xmlcustomformatter.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System; using System.Xml; @@ -77,7 +73,7 @@ namespace System.Xml.Serialization return FromXmlNmTokens((string)value); } } - throw new Exception(SR.Format(SR.XmlUnsupportedDefaultType, type.FullName)); + throw new XmlException(SR.Format(SR.XmlUnsupportedDefaultType, type.FullName)); } internal static string FromDate(DateTime value) @@ -164,11 +160,8 @@ namespace System.Xml.Serialization return null; if (value.Length == 0) return ""; -#if XMLSERIALIZERGENERATOR - return System.Xml.Extensions.ExtensionMethods.ToBinHexString(value); -#else + return XmlConvert.ToBinHexString(value); -#endif } internal static string FromEnum(long val, string[] vals, long[] ids, string typeName) @@ -243,7 +236,7 @@ namespace System.Xml.Serialization { return ToXmlNmTokens(value); } - throw new Exception(SR.Format(SR.XmlUnsupportedDefaultValue, formatter)); + throw new XmlException(SR.Format(SR.XmlUnsupportedDefaultValue, formatter)); // Debug.WriteLineIf(CompModSwitches.XmlSerialization.TraceVerbose, "XmlSerialization::Unhandled default value " + value + " formatter " + formatter); // return DBNull.Value; } @@ -436,11 +429,7 @@ namespace System.Xml.Serialization { if (value == null) return null; value = value.Trim(); -#if XMLSERIALIZERGENERATOR - return System.Xml.Extensions.ExtensionMethods.FromBinHexString(value, true); -#else return XmlConvert.FromBinHexString(value); -#endif } internal static long ToEnum(string val, Hashtable vals, string typeName, bool validate) diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/_Events.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/_Events.cs index 0b681f3547..e4d013ba2d 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/_Events.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/_Events.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator -#else namespace System.Xml.Serialization -#endif { using System.IO; using System; @@ -19,21 +15,13 @@ namespace System.Xml.Serialization /// [To be supplied.] /// -#if XMLSERIALIZERGENERATOR - internal delegate void XmlAttributeEventHandler(object sender, XmlAttributeEventArgs e); -#else public delegate void XmlAttributeEventHandler(object sender, XmlAttributeEventArgs e); -#endif /// /// /// [To be supplied.] /// -#if XMLSERIALIZERGENERATOR - internal class XmlAttributeEventArgs : EventArgs -#else public class XmlAttributeEventArgs : EventArgs -#endif { private object _o; private XmlAttribute _attr; @@ -105,18 +93,10 @@ namespace System.Xml.Serialization } /// -#if XMLSERIALIZERGENERATOR - internal delegate void XmlElementEventHandler(object sender, XmlElementEventArgs e); -#else public delegate void XmlElementEventHandler(object sender, XmlElementEventArgs e); -#endif /// -#if XMLSERIALIZERGENERATOR - internal class XmlElementEventArgs : EventArgs -#else public class XmlElementEventArgs : EventArgs -#endif { private object _o; private XmlElement _elem; @@ -173,22 +153,14 @@ namespace System.Xml.Serialization /// /// [To be supplied.] /// -#if XMLSERIALIZERGENERATOR - internal delegate void XmlNodeEventHandler(object sender, XmlNodeEventArgs e); -#else public delegate void XmlNodeEventHandler(object sender, XmlNodeEventArgs e); -#endif /// /// /// [To be supplied.] /// -#if XMLSERIALIZERGENERATOR - internal class XmlNodeEventArgs : EventArgs -#else public class XmlNodeEventArgs : EventArgs -#endif { private object _o; private XmlNode _xmlNode; @@ -283,18 +255,10 @@ namespace System.Xml.Serialization } /// -#if XMLSERIALIZERGENERATOR - internal delegate void UnreferencedObjectEventHandler(object sender, UnreferencedObjectEventArgs e); -#else public delegate void UnreferencedObjectEventHandler(object sender, UnreferencedObjectEventArgs e); -#endif /// -#if XMLSERIALIZERGENERATOR - internal class UnreferencedObjectEventArgs : EventArgs -#else public class UnreferencedObjectEventArgs : EventArgs -#endif { private object _o; private string _id; diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/indentedWriter.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/indentedWriter.cs index 259bc740d8..4bcd9d8ff0 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/indentedWriter.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Serialization/indentedWriter.cs @@ -2,8 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if XMLSERIALIZERGENERATOR -namespace Microsoft.XmlSerializer.Generator +#if !FEATURE_SERIALIZATION_UAPAOT +namespace System.Xml.Serialization { using System.IO; @@ -74,4 +74,4 @@ namespace Microsoft.XmlSerializer.Generator } } } -#endif \ No newline at end of file +#endif diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/ValidateNames.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/ValidateNames.cs index 88421f3efc..69193310e9 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/ValidateNames.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/ValidateNames.cs @@ -72,7 +72,6 @@ namespace System.Xml /// Quits parsing when an invalid Nmtoken char is reached or the end of string is reached. /// Returns the number of valid Nmtoken chars that were parsed. /// - [System.Security.SecuritySafeCritical] internal static unsafe int ParseNmtokenNoNamespaces(string s, int offset) { Debug.Assert(s != null && offset <= s.Length); @@ -117,7 +116,6 @@ namespace System.Xml /// Quits parsing when an invalid Name char is reached or the end of string is reached. /// Returns the number of valid Name chars that were parsed. /// - [System.Security.SecuritySafeCritical] internal static unsafe int ParseNameNoNamespaces(string s, int offset) { Debug.Assert(s != null && offset <= s.Length); @@ -180,7 +178,6 @@ namespace System.Xml /// Quits parsing when an invalid NCName char is reached or the end of string is reached. /// Returns the number of valid NCName chars that were parsed. /// - [System.Security.SecuritySafeCritical] internal static unsafe int ParseNCName(string s, int offset) { Debug.Assert(s != null && offset <= s.Length); diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/XPath/XPathException.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/XPath/XPathException.cs index 26de320deb..319ec520f5 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/XPath/XPathException.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/XPath/XPathException.cs @@ -12,6 +12,9 @@ namespace System.Xml.XPath // Represents the exception that is thrown when there is error processing an // XPath expression. [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class XPathException : SystemException { // we need to keep this members for V1 serialization compatibility @@ -24,12 +27,37 @@ namespace System.Xml.XPath protected XPathException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); + _res = (string)info.GetValue("res", typeof(string)); + _args = (string[])info.GetValue("args", typeof(string[])); + + // deserialize optional members + string version = null; + foreach (SerializationEntry e in info) + { + if (e.Name == "version") + { + version = (string)e.Value; + } + } + + if (version == null) + { + // deserializing V1 exception + _message = CreateMessage(_res, _args); + } + else + { + // deserializing V2 or higher exception -> exception message is serialized by the base class (Exception._message) + _message = null; + } } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + info.AddValue("res", _res); + info.AddValue("args", _args); + info.AddValue("version", "2.0"); } public XPathException() : this(string.Empty, (Exception)null) { } diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/XPath/XPathNavigator.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/XPath/XPathNavigator.cs index 0aa96e6ca8..d986e120fb 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/XPath/XPathNavigator.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/XPath/XPathNavigator.cs @@ -9,7 +9,6 @@ using System.Collections.Generic; using System.Globalization; using System.Xml.Schema; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Security; using System.Text; using System.Xml; @@ -1183,22 +1182,16 @@ namespace System.Xml.XPath public virtual XPathNodeIterator Select(string xpath) { - Contract.Ensures(Contract.Result() != null); - return this.Select(XPathExpression.Compile(xpath)); } public virtual XPathNodeIterator Select(string xpath, IXmlNamespaceResolver resolver) { - Contract.Ensures(Contract.Result() != null); - return this.Select(XPathExpression.Compile(xpath, resolver)); } public virtual XPathNodeIterator Select(XPathExpression expr) { - Contract.Ensures(Contract.Result() != null); - XPathNodeIterator result = Evaluate(expr) as XPathNodeIterator; if (result == null) { @@ -2253,23 +2246,4 @@ namespace System.Xml.XPath } } } - -#if CONTRACTS_FULL - [ContractClassFor(typeof(XPathNavigator))] - internal abstract class XPathNavigatorContract : XPathNavigator - { - public override XPathNavigator Clone() - { - Contract.Ensures(Contract.Result() != null); - return default(XPathNavigator); - } - - public override XmlNameTable NameTable { - get { - Contract.Ensures(Contract.Result() != null); - return default(XmlNameTable); - } - } - } -#endif } diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/XmlConvert.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/XmlConvert.cs index 5effc726cd..167828e0a8 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/XmlConvert.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/XmlConvert.cs @@ -1586,7 +1586,6 @@ namespace System.Xml return false; } - [System.Security.SecuritySafeCritical] private static unsafe long DoubleToInt64Bits(double value) { // NOTE: BitConverter.DoubleToInt64Bits is missing in Silverlight diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/XmlException.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/XmlException.cs index 373c77ea6c..6162187da2 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/XmlException.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/XmlException.cs @@ -17,6 +17,9 @@ namespace System.Xml /// number, line number, character position, and a text description. /// [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class XmlException : SystemException { private string _res; @@ -32,12 +35,48 @@ namespace System.Xml protected XmlException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); + _res = (string)info.GetValue("res", typeof(string)); + _args = (string[])info.GetValue("args", typeof(string[])); + _lineNumber = (int)info.GetValue("lineNumber", typeof(int)); + _linePosition = (int)info.GetValue("linePosition", typeof(int)); + + // deserialize optional members + _sourceUri = string.Empty; + string version = null; + foreach (SerializationEntry e in info) + { + switch (e.Name) + { + case "sourceUri": + _sourceUri = (string)e.Value; + break; + case "version": + version = (string)e.Value; + break; + } + } + + if (version == null) + { + // deserializing V1 exception + _message = CreateMessage(_res, _args, _lineNumber, _linePosition); + } + else + { + // deserializing V2 or higher exception -> exception message is serialized by the base class (Exception._message) + _message = null; + } } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + info.AddValue("res", _res); + info.AddValue("args", _args); + info.AddValue("lineNumber", _lineNumber); + info.AddValue("linePosition", _linePosition); + info.AddValue("sourceUri", _sourceUri); + info.AddValue("version", "2.0"); } //provided to meet the ECMA standards diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Xsl/Xslt/MatcherBuilder.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Xsl/Xslt/MatcherBuilder.cs index efacfecd35..562fe5e17d 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Xsl/Xslt/MatcherBuilder.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Xsl/Xslt/MatcherBuilder.cs @@ -244,7 +244,7 @@ namespace System.Xml.Xsl.Xslt } } - internal struct Pattern + internal readonly struct Pattern { public readonly TemplateMatch Match; diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilGeneratorEnv.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilGeneratorEnv.cs index 101148112f..794dd31ab9 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilGeneratorEnv.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilGeneratorEnv.cs @@ -20,7 +20,7 @@ namespace System.Xml.Xsl.Xslt // Everywhere in this code in case of error in the stylesheet we should throw XslLoadException. // This helper IErrorHelper implementation is used to wrap XmlException's into XslLoadException's. - private struct ThrowErrorHelper : IErrorHelper + private readonly struct ThrowErrorHelper : IErrorHelper { public void ReportError(string res, params string[] args) { diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Xsl/Xslt/XPathPatternBuilder.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Xsl/Xslt/XPathPatternBuilder.cs index e72d268b38..8c1fdc941e 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Xsl/Xslt/XPathPatternBuilder.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Xsl/Xslt/XPathPatternBuilder.cs @@ -321,14 +321,15 @@ namespace System.Xml.Xsl.Xslt } public QilNode String(string value) { return _f.String(value); } // As argument of id() or key() function - public QilNode Number(double value) { return UnexpectedToken("Literal number"); } - public QilNode Variable(string prefix, string name) { return UnexpectedToken("Variable"); } - - private QilNode UnexpectedToken(string tokenName) + public QilNode Number(double value) { - string prompt = string.Format(CultureInfo.InvariantCulture, "Internal Error: {0} is not allowed in XSLT pattern outside of predicate.", tokenName); - Debug.Assert(false, prompt); - throw new Exception(prompt); + //Internal Error: Literal number is not allowed in XSLT pattern outside of predicate. + throw new XmlException(SR.Xml_InternalError); + } + public QilNode Variable(string prefix, string name) + { + //Internal Error: Variable is not allowed in XSLT pattern outside of predicate. + throw new XmlException(SR.Xml_InternalError); } // -------------------------------------- Priority / Parent --------------------------------------- diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Xsl/Xslt/XslAstAnalyzer.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Xsl/Xslt/XslAstAnalyzer.cs index 8704187ac9..f7b760edad 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Xsl/Xslt/XslAstAnalyzer.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Xsl/Xslt/XslAstAnalyzer.cs @@ -938,7 +938,7 @@ namespace System.Xml.Xsl.Xslt // ------------------------------- XPathAnalyzer -------------------------------- // Ignores all errors and warnings - internal struct NullErrorHelper : IErrorHelper + internal readonly struct NullErrorHelper : IErrorHelper { public void ReportError(string res, params string[] args) { } public void ReportWarning(string res, params string[] args) { } diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Xslt/XslTransform.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Xslt/XslTransform.cs index e53671349b..93b8bb38c6 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Xslt/XslTransform.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Xslt/XslTransform.cs @@ -15,7 +15,6 @@ namespace System.Xml.Xsl using System.Xml.Xsl.XsltOld.Debugger; using System.Runtime.Versioning; - [Obsolete("This class has been deprecated. Please use System.Xml.Xsl.XslCompiledTransform instead. http://go.microsoft.com/fwlink/?linkid=14202")] public sealed class XslTransform { private XmlResolver _documentResolver = null; diff --git a/external/corefx/src/System.Private.Xml/src/System/Xml/Xslt/XsltException.cs b/external/corefx/src/System.Private.Xml/src/System/Xml/Xslt/XsltException.cs index de050f3bef..7b040816aa 100644 --- a/external/corefx/src/System.Private.Xml/src/System/Xml/Xslt/XsltException.cs +++ b/external/corefx/src/System.Private.Xml/src/System/Xml/Xslt/XsltException.cs @@ -10,21 +10,60 @@ using System.Xml.XPath; namespace System.Xml.Xsl { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class XsltException : SystemException { private string _res; + private string[] _args; private string _sourceUri; private int _lineNumber; private int _linePosition; + // message != null for V1 & V2 exceptions deserialized in Whidbey + // message == null for created V2 exceptions; the exception message is stored in Exception._message + private string _message; + protected XsltException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); + _res = (string)info.GetValue("res", typeof(string)); + _args = (string[])info.GetValue("args", typeof(string[])); + _sourceUri = (string)info.GetValue("sourceUri", typeof(string)); + _lineNumber = (int)info.GetValue("lineNumber", typeof(int)); + _linePosition = (int)info.GetValue("linePosition", typeof(int)); + + // deserialize optional members + string version = null; + foreach (SerializationEntry e in info) + { + if (e.Name == "version") + { + version = (string)e.Value; + } + } + + if (version == null) + { + // deserializing V1 exception + _message = CreateMessage(_res, _args, _sourceUri, _lineNumber, _linePosition); + } + else + { + // deserializing V2 or higher exception -> exception message is serialized by the base class (Exception._message) + _message = null; + } } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + info.AddValue("res", _res); + info.AddValue("args", _args); + info.AddValue("sourceUri", _sourceUri); + info.AddValue("lineNumber", _lineNumber); + info.AddValue("linePosition", _linePosition); + info.AddValue("version", "2.0"); } public XsltException() : this(string.Empty, (Exception)null) { } @@ -75,7 +114,7 @@ namespace System.Xml.Xsl { get { - return base.Message; + return _message ?? base.Message; } } @@ -108,11 +147,14 @@ namespace System.Xml.Xsl } [Serializable] +<<<<<<< HEAD +======= + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +>>>>>>> upstream/master public class XsltCompileException : XsltException { protected XsltCompileException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public override void GetObjectData(SerializationInfo info, StreamingContext context) diff --git a/external/corefx/src/System.Private.Xml/tests/Misc/Configurations.props b/external/corefx/src/System.Private.Xml/tests/Misc/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Private.Xml/tests/Misc/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/Misc/System.Xml.Misc.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/Misc/System.Xml.Misc.Tests.csproj index 97c0b4f7ac..275891669f 100644 --- a/external/corefx/src/System.Private.Xml/tests/Misc/System.Xml.Misc.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/Misc/System.Xml.Misc.Tests.csproj @@ -5,11 +5,8 @@ {A30BBA60-647C-4565-A42F-BE60B2CA2E8E} XmlMiscTests - - - - - + + diff --git a/external/corefx/src/System.Private.Xml/tests/Readers/CharCheckingReader/Configurations.props b/external/corefx/src/System.Private.Xml/tests/Readers/CharCheckingReader/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Private.Xml/tests/Readers/CharCheckingReader/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/Readers/CharCheckingReader/System.Xml.RW.CharCheckingReader.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/Readers/CharCheckingReader/System.Xml.RW.CharCheckingReader.Tests.csproj index 035805eaff..949cf3db4e 100644 --- a/external/corefx/src/System.Private.Xml/tests/Readers/CharCheckingReader/System.Xml.RW.CharCheckingReader.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/Readers/CharCheckingReader/System.Xml.RW.CharCheckingReader.Tests.csproj @@ -4,12 +4,8 @@ {6DC15D23-8213-4700-9815-AD8DEED1CE5F} - - - ..\..\..\..\..\bin\AnyOS.AnyCPU.Debug\System.Xml.RW.CharCheckingReader.Tests\ - - - + + @@ -21,4 +17,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/Readers/CustomReader/Configurations.props b/external/corefx/src/System.Private.Xml/tests/Readers/CustomReader/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Private.Xml/tests/Readers/CustomReader/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/Readers/CustomReader/System.Xml.RW.CustomReader.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/Readers/CustomReader/System.Xml.RW.CustomReader.Tests.csproj index 77307fafb0..b25396453f 100644 --- a/external/corefx/src/System.Private.Xml/tests/Readers/CustomReader/System.Xml.RW.CustomReader.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/Readers/CustomReader/System.Xml.RW.CustomReader.Tests.csproj @@ -4,12 +4,8 @@ {B51913C2-478E-46AA-A523-521BD4593651} - - - ..\..\..\..\..\bin\AnyOS.AnyCPU.Debug\System.Xml.RW.CustomReader2.Tests\ - - - + + diff --git a/external/corefx/src/System.Private.Xml/tests/Readers/FactoryReader/Configurations.props b/external/corefx/src/System.Private.Xml/tests/Readers/FactoryReader/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Private.Xml/tests/Readers/FactoryReader/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/Readers/FactoryReader/System.Xml.RW.FactoryReader.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/Readers/FactoryReader/System.Xml.RW.FactoryReader.Tests.csproj index 6738ad1ee6..2a2d9a641b 100644 --- a/external/corefx/src/System.Private.Xml/tests/Readers/FactoryReader/System.Xml.RW.FactoryReader.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/Readers/FactoryReader/System.Xml.RW.FactoryReader.Tests.csproj @@ -4,11 +4,8 @@ {04C5492C-FA54-4F93-8698-44B8BB7C72E0} - - - - - + + diff --git a/external/corefx/src/System.Private.Xml/tests/Readers/NameTable/Configurations.props b/external/corefx/src/System.Private.Xml/tests/Readers/NameTable/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Private.Xml/tests/Readers/NameTable/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/Readers/NameTable/System.Xml.RW.NameTable.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/Readers/NameTable/System.Xml.RW.NameTable.Tests.csproj index e73c76f249..ddce4bbad2 100644 --- a/external/corefx/src/System.Private.Xml/tests/Readers/NameTable/System.Xml.RW.NameTable.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/Readers/NameTable/System.Xml.RW.NameTable.Tests.csproj @@ -4,11 +4,8 @@ {2CA30CA9-FADA-4AB6-81E3-EAE61EF44463} - - - - - + + diff --git a/external/corefx/src/System.Private.Xml/tests/Readers/ReaderSettings/Configurations.props b/external/corefx/src/System.Private.Xml/tests/Readers/ReaderSettings/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Private.Xml/tests/Readers/ReaderSettings/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/Readers/ReaderSettings/System.Xml.RW.ReaderSettings.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/Readers/ReaderSettings/System.Xml.RW.ReaderSettings.Tests.csproj index 9597aebbea..01b387e1c3 100644 --- a/external/corefx/src/System.Private.Xml/tests/Readers/ReaderSettings/System.Xml.RW.ReaderSettings.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/Readers/ReaderSettings/System.Xml.RW.ReaderSettings.Tests.csproj @@ -4,11 +4,8 @@ {54B5F207-CC11-4AC3-B0D7-1E7A7E2F08DE} - - - - - + + diff --git a/external/corefx/src/System.Private.Xml/tests/Readers/SubtreeReader/Configurations.props b/external/corefx/src/System.Private.Xml/tests/Readers/SubtreeReader/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Private.Xml/tests/Readers/SubtreeReader/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/Readers/SubtreeReader/System.Xml.RW.SubtreeReader.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/Readers/SubtreeReader/System.Xml.RW.SubtreeReader.Tests.csproj index 93aba863f0..c7e9dbd11f 100644 --- a/external/corefx/src/System.Private.Xml/tests/Readers/SubtreeReader/System.Xml.RW.SubtreeReader.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/Readers/SubtreeReader/System.Xml.RW.SubtreeReader.Tests.csproj @@ -4,11 +4,8 @@ {6DC919A7-CF8F-4CCD-A7E3-1AD9389FEC86} - - - - - + + diff --git a/external/corefx/src/System.Private.Xml/tests/Readers/WrappedReader/Configurations.props b/external/corefx/src/System.Private.Xml/tests/Readers/WrappedReader/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Private.Xml/tests/Readers/WrappedReader/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/Readers/WrappedReader/System.Xml.RW.WrappedReader.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/Readers/WrappedReader/System.Xml.RW.WrappedReader.Tests.csproj index 589a3111b5..130d8dab03 100644 --- a/external/corefx/src/System.Private.Xml/tests/Readers/WrappedReader/System.Xml.RW.WrappedReader.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/Readers/WrappedReader/System.Xml.RW.WrappedReader.Tests.csproj @@ -4,11 +4,8 @@ {1C8F67D6-1953-49D3-B716-F298883A79C6} - - - - - + + diff --git a/external/corefx/src/System.Private.Xml/tests/Writers/RwFactory/Configurations.props b/external/corefx/src/System.Private.Xml/tests/Writers/RwFactory/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Private.Xml/tests/Writers/RwFactory/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/Writers/RwFactory/System.Xml.RW.RwFactory.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/Writers/RwFactory/System.Xml.RW.RwFactory.Tests.csproj index ebfbf9a48b..4f9e303e4b 100644 --- a/external/corefx/src/System.Private.Xml/tests/Writers/RwFactory/System.Xml.RW.RwFactory.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/Writers/RwFactory/System.Xml.RW.RwFactory.Tests.csproj @@ -4,11 +4,8 @@ {95C95878-A9CD-43D4-B1BB-D0DCAA54C3D7} - - - - - + + diff --git a/external/corefx/src/System.Private.Xml/tests/Writers/XmlWriterApi/Configurations.props b/external/corefx/src/System.Private.Xml/tests/Writers/XmlWriterApi/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Private.Xml/tests/Writers/XmlWriterApi/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/Writers/XmlWriterApi/System.Xml.RW.XmlWriterApi.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/Writers/XmlWriterApi/System.Xml.RW.XmlWriterApi.Tests.csproj index c41513faeb..23068aa810 100644 --- a/external/corefx/src/System.Private.Xml/tests/Writers/XmlWriterApi/System.Xml.RW.XmlWriterApi.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/Writers/XmlWriterApi/System.Xml.RW.XmlWriterApi.Tests.csproj @@ -4,11 +4,8 @@ {E6DAD59F-7CB7-4E70-B4C5-FCCBC3376EDE} - - - - - + + diff --git a/external/corefx/src/System.Private.Xml/tests/XPath/XPathDocument/Configurations.props b/external/corefx/src/System.Private.Xml/tests/XPath/XPathDocument/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Private.Xml/tests/XPath/XPathDocument/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/XPath/XPathDocument/System.Xml.XPath.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/XPath/XPathDocument/System.Xml.XPath.Tests.csproj index 09949e7114..446f1e511b 100644 --- a/external/corefx/src/System.Private.Xml/tests/XPath/XPathDocument/System.Xml.XPath.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/XPath/XPathDocument/System.Xml.XPath.Tests.csproj @@ -7,10 +7,8 @@ $(DefineConstants);FEATURE_XML_XPATH_ID System.Xml.XPath.Tests - - - - + + $(CommonPath)\System\Xml\XPath diff --git a/external/corefx/src/System.Private.Xml/tests/XPath/XmlDocument/Configurations.props b/external/corefx/src/System.Private.Xml/tests/XPath/XmlDocument/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Private.Xml/tests/XPath/XmlDocument/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/XPath/XmlDocument/System.Xml.XPath.XmlDocument.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/XPath/XmlDocument/System.Xml.XPath.XmlDocument.Tests.csproj index 2a92a1f3d1..bc890743a7 100644 --- a/external/corefx/src/System.Private.Xml/tests/XPath/XmlDocument/System.Xml.XPath.XmlDocument.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/XPath/XmlDocument/System.Xml.XPath.XmlDocument.Tests.csproj @@ -7,10 +7,8 @@ $(DefineConstants);FEATURE_XML_XPATH_ID System.Xml.XPath.XmlDocument.Tests - - - - + + $(CommonPath)\System\Xml\XPath diff --git a/external/corefx/src/System.Private.Xml/tests/XmlConvert/Configurations.props b/external/corefx/src/System.Private.Xml/tests/XmlConvert/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Private.Xml/tests/XmlConvert/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/XmlConvert/System.Xml.RW.XmlConvert.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/XmlConvert/System.Xml.RW.XmlConvert.Tests.csproj index c954f015e3..55e257a2f7 100644 --- a/external/corefx/src/System.Private.Xml/tests/XmlConvert/System.Xml.RW.XmlConvert.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/XmlConvert/System.Xml.RW.XmlConvert.Tests.csproj @@ -4,11 +4,8 @@ {3F18D20D-0267-4381-857B-EEDB7B3FC549} - - - - - + + diff --git a/external/corefx/src/System.Private.Xml/tests/XmlDocument/Configurations.props b/external/corefx/src/System.Private.Xml/tests/XmlDocument/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Private.Xml/tests/XmlDocument/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/XmlDocument/Performance/System.Xml.XmlDocument.Performance.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/XmlDocument/Performance/System.Xml.XmlDocument.Performance.Tests.csproj index 50eac62abc..3a1b71fc66 100644 --- a/external/corefx/src/System.Private.Xml/tests/XmlDocument/Performance/System.Xml.XmlDocument.Performance.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/XmlDocument/Performance/System.Xml.XmlDocument.Performance.Tests.csproj @@ -8,9 +8,8 @@ true - - - + + diff --git a/external/corefx/src/System.Private.Xml/tests/XmlDocument/System.Xml.XmlDocument.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/XmlDocument/System.Xml.XmlDocument.Tests.csproj index 4c9179e286..aa75eb4568 100644 --- a/external/corefx/src/System.Private.Xml/tests/XmlDocument/System.Xml.XmlDocument.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/XmlDocument/System.Xml.XmlDocument.Tests.csproj @@ -6,11 +6,8 @@ {7EAFC2D8-48D2-4A56-A9C6-6BADF2053499} true - - - - - + + Common\System\ShouldNotBeInvokedException.cs diff --git a/external/corefx/src/System.Private.Xml/tests/XmlReader/ReadContentAs/Configurations.props b/external/corefx/src/System.Private.Xml/tests/XmlReader/ReadContentAs/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Private.Xml/tests/XmlReader/ReadContentAs/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/XmlReader/ReadContentAs/System.Xml.RW.XmlReader.ReadContentAs.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/XmlReader/ReadContentAs/System.Xml.RW.XmlReader.ReadContentAs.Tests.csproj index 439b4271a1..3166c0a81a 100644 --- a/external/corefx/src/System.Private.Xml/tests/XmlReader/ReadContentAs/System.Xml.RW.XmlReader.ReadContentAs.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/XmlReader/ReadContentAs/System.Xml.RW.XmlReader.ReadContentAs.Tests.csproj @@ -4,11 +4,8 @@ {DA6A9B7F-F311-49A4-8BBE-42EF3152C37B} - - - - - + + diff --git a/external/corefx/src/System.Private.Xml/tests/XmlReader/Tests/AsyncReaderLateInitTests.cs b/external/corefx/src/System.Private.Xml/tests/XmlReader/Tests/AsyncReaderLateInitTests.cs index f73ac94562..eec4e8db94 100644 --- a/external/corefx/src/System.Private.Xml/tests/XmlReader/Tests/AsyncReaderLateInitTests.cs +++ b/external/corefx/src/System.Private.Xml/tests/XmlReader/Tests/AsyncReaderLateInitTests.cs @@ -39,10 +39,10 @@ namespace System.Xml.Tests } } - [Fact] - public static void ReadAfterInitializationWithStreamOnAsyncReaderDoesNotThrow() + [Theory, InlineData(true), InlineData(false)] + public static void ReadAfterInitializationWithStreamOnAsyncReaderDoesNotThrow(bool async) { - using (XmlReader reader = XmlReader.Create(GetDummyXmlStream(), new XmlReaderSettings() { Async = true })) + using (XmlReader reader = XmlReader.Create(GetDummyXmlStream(), new XmlReaderSettings() { Async = async })) { reader.Read(); } @@ -57,10 +57,10 @@ namespace System.Xml.Tests } } - [Fact] - public static void ReadAfterInitializationWithTextReaderOnAsyncReaderDoesNotThrow() + [Theory, InlineData(true), InlineData(false)] + public static void ReadAfterInitializationWithTextReaderOnAsyncReaderDoesNotThrow(bool async) { - using (XmlReader reader = XmlReader.Create(GetDummyXmlTextReader(), new XmlReaderSettings() { Async = true })) + using (XmlReader reader = XmlReader.Create(GetDummyXmlTextReader(), new XmlReaderSettings() { Async = async })) { reader.Read(); } @@ -75,14 +75,19 @@ namespace System.Xml.Tests } } - [Fact] + [Fact, SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, ".Net Framework throws AggregateException")] public static void ReadAfterInitializationWithUriOnAsyncReaderTrows() { using (XmlReader reader = XmlReader.Create("http://test.test/test.html", new XmlReaderSettings() { Async = true })) { - AggregateException ae = Assert.Throws(() => reader.Read()); - Assert.Equal(typeof(System.Net.WebException), ae.InnerException.GetType()); + Assert.Throws(() => reader.Read()); } } + + [Fact] + public static void InitializationWithUriOnNonAsyncReaderTrows() + { + Assert.Throws(() => XmlReader.Create("http://test.test/test.html", new XmlReaderSettings() { Async = false })); + } } } diff --git a/external/corefx/src/System.Private.Xml/tests/XmlReader/Tests/Configurations.props b/external/corefx/src/System.Private.Xml/tests/XmlReader/Tests/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Private.Xml/tests/XmlReader/Tests/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/XmlReader/Tests/System.Xml.RW.XmlReader.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/XmlReader/Tests/System.Xml.RW.XmlReader.Tests.csproj index f91333bbd8..105ea258a9 100644 --- a/external/corefx/src/System.Private.Xml/tests/XmlReader/Tests/System.Xml.RW.XmlReader.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/XmlReader/Tests/System.Xml.RW.XmlReader.Tests.csproj @@ -4,11 +4,8 @@ {507DB29F-74F5-4B34-A240-ABE7BD168DF6} - - - - - + + diff --git a/external/corefx/src/System.Private.Xml/tests/XmlReader/XmlResolver/Configurations.props b/external/corefx/src/System.Private.Xml/tests/XmlReader/XmlResolver/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Private.Xml/tests/XmlReader/XmlResolver/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/XmlReader/XmlResolver/System.Xml.RW.XmlSystemPathResolver.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/XmlReader/XmlResolver/System.Xml.RW.XmlSystemPathResolver.Tests.csproj index 15324eeacd..da05c738ed 100644 --- a/external/corefx/src/System.Private.Xml/tests/XmlReader/XmlResolver/System.Xml.RW.XmlSystemPathResolver.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/XmlReader/XmlResolver/System.Xml.RW.XmlSystemPathResolver.Tests.csproj @@ -4,11 +4,8 @@ {E4BC1A16-AD0A-4F70-BD2E-3346A4D9BC2B} - - - - - + + diff --git a/external/corefx/src/System.Private.Xml/tests/XmlReaderLib/Configurations.props b/external/corefx/src/System.Private.Xml/tests/XmlReaderLib/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Private.Xml/tests/XmlReaderLib/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/XmlReaderLib/System.Xml.RW.XmlReaderLib.csproj b/external/corefx/src/System.Private.Xml/tests/XmlReaderLib/System.Xml.RW.XmlReaderLib.csproj index 8ee7108742..e0a9711ed4 100644 --- a/external/corefx/src/System.Private.Xml/tests/XmlReaderLib/System.Xml.RW.XmlReaderLib.csproj +++ b/external/corefx/src/System.Private.Xml/tests/XmlReaderLib/System.Xml.RW.XmlReaderLib.csproj @@ -5,11 +5,8 @@ {F05DE950-CA99-42A8-B44D-E7DAA5C3C783} XmlReaderTest.Common - - - - - + + @@ -87,4 +84,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/XmlSchema/XmlSchemaSet/Configurations.props b/external/corefx/src/System.Private.Xml/tests/XmlSchema/XmlSchemaSet/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Private.Xml/tests/XmlSchema/XmlSchemaSet/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/XmlSchema/XmlSchemaSet/System.Xml.XmlSchemaSet.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/XmlSchema/XmlSchemaSet/System.Xml.XmlSchemaSet.Tests.csproj index f0f80d6f4e..8c04f874bf 100644 --- a/external/corefx/src/System.Private.Xml/tests/XmlSchema/XmlSchemaSet/System.Xml.XmlSchemaSet.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/XmlSchema/XmlSchemaSet/System.Xml.XmlSchemaSet.Tests.csproj @@ -4,11 +4,8 @@ {9EDAADA8-B658-430F-97EE-CCA494883D86} - - - - - + + @@ -50,4 +47,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/XmlSchema/XmlSchemaValidatorApi/Configurations.props b/external/corefx/src/System.Private.Xml/tests/XmlSchema/XmlSchemaValidatorApi/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Private.Xml/tests/XmlSchema/XmlSchemaValidatorApi/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/XmlSchema/XmlSchemaValidatorApi/System.Xml.XmlSchema.XmlSchemaValidatorApi.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/XmlSchema/XmlSchemaValidatorApi/System.Xml.XmlSchema.XmlSchemaValidatorApi.Tests.csproj index 3077526a8c..a948693bca 100644 --- a/external/corefx/src/System.Private.Xml/tests/XmlSchema/XmlSchemaValidatorApi/System.Xml.XmlSchema.XmlSchemaValidatorApi.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/XmlSchema/XmlSchemaValidatorApi/System.Xml.XmlSchema.XmlSchemaValidatorApi.Tests.csproj @@ -4,11 +4,8 @@ {B0F53AAA-4ABC-44B2-9331-D3802340DD20} - - - - - + + @@ -36,4 +33,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs.REMOVED.git-id b/external/corefx/src/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs.REMOVED.git-id index 25b627cf18..f217aa82a1 100644 --- a/external/corefx/src/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs.REMOVED.git-id +++ b/external/corefx/src/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs.REMOVED.git-id @@ -1 +1 @@ -0eb45820c158253fabd99ed1dbdd11060e442cce \ No newline at end of file +9ecde4baf12963ebcd9a58915ca5a6544ba988e1 \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs b/external/corefx/src/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs index 821329e542..10281561d3 100644 --- a/external/corefx/src/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs +++ b/external/corefx/src/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs @@ -1613,6 +1613,15 @@ string.Format(@" Assert.Equal(value, actual); } + [Fact] + public static void Xml_TypeWithMismatchBetweenAttributeAndPropertyType() + { + var value = new TypeWithMismatchBetweenAttributeAndPropertyType(); + var actual = SerializeAndDeserialize(value, +@""); + Assert.StrictEqual(value.IntValue, actual.IntValue); + } + private static readonly string s_defaultNs = "http://tempuri.org/"; private static T RoundTripWithXmlMembersMapping(object requestBodyValue, string memberName, string baseline, bool skipStringCompare = false, string wrapperName = null) { diff --git a/external/corefx/src/System.Private.Xml/tests/XmlWriter/Configurations.props b/external/corefx/src/System.Private.Xml/tests/XmlWriter/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Private.Xml/tests/XmlWriter/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/XmlWriter/System.Xml.RW.XmlWriter.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/XmlWriter/System.Xml.RW.XmlWriter.Tests.csproj index eeb5cac4be..7e370a65d5 100644 --- a/external/corefx/src/System.Private.Xml/tests/XmlWriter/System.Xml.RW.XmlWriter.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/XmlWriter/System.Xml.RW.XmlWriter.Tests.csproj @@ -4,11 +4,8 @@ {8CDE71C2-4DA4-4AF6-9897-CD953AE653C2} - - - - - + + diff --git a/external/corefx/src/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/System.Xml.Xsl.XslCompiledTransformApi.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/System.Xml.Xsl.XslCompiledTransformApi.Tests.csproj index 4b9d9fda5b..d6998dcd80 100644 --- a/external/corefx/src/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/System.Xml.Xsl.XslCompiledTransformApi.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/System.Xml.Xsl.XslCompiledTransformApi.Tests.csproj @@ -8,7 +8,6 @@ - diff --git a/external/corefx/src/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs b/external/corefx/src/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs index ca26f84c93..9e862ce469 100644 --- a/external/corefx/src/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs +++ b/external/corefx/src/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs @@ -1,4 +1,8 @@ -using System.IO; +// 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.IO; using System.Xml.Xsl; using Xunit; diff --git a/external/corefx/src/System.Private.Xml/tests/Xslt/XslTransformApi/CXslTransform.cs.REMOVED.git-id b/external/corefx/src/System.Private.Xml/tests/Xslt/XslTransformApi/CXslTransform.cs.REMOVED.git-id index a1ba491b2d..f08833637c 100644 --- a/external/corefx/src/System.Private.Xml/tests/Xslt/XslTransformApi/CXslTransform.cs.REMOVED.git-id +++ b/external/corefx/src/System.Private.Xml/tests/Xslt/XslTransformApi/CXslTransform.cs.REMOVED.git-id @@ -1 +1 @@ -b0df559ead0e393f58ef4472f37440f994c67812 \ No newline at end of file +0a8ecb612ca4fd205780454c01bffd543dcff218 \ No newline at end of file diff --git a/external/corefx/src/System.Private.Xml/tests/Xslt/XslTransformApi/MyNavigator.cs b/external/corefx/src/System.Private.Xml/tests/Xslt/XslTransformApi/MyNavigator.cs index bc3d365c64..230694afab 100644 --- a/external/corefx/src/System.Private.Xml/tests/Xslt/XslTransformApi/MyNavigator.cs +++ b/external/corefx/src/System.Private.Xml/tests/Xslt/XslTransformApi/MyNavigator.cs @@ -169,7 +169,6 @@ public class MyNavigator : XPathNavigator // Selection /// - //[PermissionSetAttribute(SecurityAction.Deny, Name = "FullTrust")] public override XPathExpression Compile(string xpath) { if (xpath.IndexOf("custom", 0, xpath.Length) >= 0) @@ -177,4 +176,4 @@ public class MyNavigator : XPathNavigator return _xn.Compile(xpath); } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Private.Xml/tests/Xslt/XslTransformApi/System.Xml.Xsl.XslTransformApi.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/Xslt/XslTransformApi/System.Xml.Xsl.XslTransformApi.Tests.csproj index cf5dd08680..02ac5b15ba 100644 --- a/external/corefx/src/System.Private.Xml/tests/Xslt/XslTransformApi/System.Xml.Xsl.XslTransformApi.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/Xslt/XslTransformApi/System.Xml.Xsl.XslTransformApi.Tests.csproj @@ -4,7 +4,6 @@ {ACF79A18-2655-452C-B4AC-10125F0AD7A8} - diff --git a/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/CommonScenarios/XsltcTestBasicFunctionality.cs b/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/CommonScenarios/XsltcTestBasicFunctionality.cs index c2fb77de28..c1f2bd52bc 100644 --- a/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/CommonScenarios/XsltcTestBasicFunctionality.cs +++ b/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/CommonScenarios/XsltcTestBasicFunctionality.cs @@ -29,6 +29,7 @@ namespace System.Xml.Tests [InlineData("/debug+", "bft37.txt")] //[Variation("38", Desc = "Empty string in arguments", Pri = 1, Params = new object[] { "\"\"", "bft38.txt" })] [InlineData("\"\"", "bft38.txt")] + [Trait("category", "XsltcExeRequired")] [ConditionalTheory(nameof(xsltcExeFound))] public void Var2(object param0, object param1) { diff --git a/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/CommonScenarios/XsltcTestFile.cs b/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/CommonScenarios/XsltcTestFile.cs index 9ba53bd970..f79be856d0 100644 --- a/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/CommonScenarios/XsltcTestFile.cs +++ b/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/CommonScenarios/XsltcTestFile.cs @@ -55,6 +55,7 @@ namespace System.Xml.Tests [InlineData("@", "fft20.dll", "no", "fft20", "fft20.pdb", "no", "fft20.txt")] //[Variation("21", Desc = "Exercise @ with not existing filename", Pri = 1, Params = new object[] { "@IDontExist", "fft21.dll", "no", "fft21", "fft21.pdb", "no", "fft21.txt" })] [InlineData("@IDontExist", "fft21.dll", "no", "fft21", "fft21.pdb", "no", "fft21.txt")] + [Trait("category", "XsltcExeRequired")] [ConditionalTheory(nameof(xsltcExeFound))] public void Var1(object param0, object param1, object param2, object param3, object param4, object param5, object param6) { diff --git a/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/CommonScenarios/XsltcTestPlatform.cs b/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/CommonScenarios/XsltcTestPlatform.cs index ab05bc9713..0e41b1cd1c 100644 --- a/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/CommonScenarios/XsltcTestPlatform.cs +++ b/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/CommonScenarios/XsltcTestPlatform.cs @@ -23,6 +23,7 @@ namespace System.Xml.Tests [InlineData("/PLAtforM:ItaNiuM pft8.xsl", "", "pft8.dll", "pft8.txt")] //[Variation("10", Desc = "Exercise keyword case sensitivity -4", Pri = 1, Params = new object[] { "/PLAtforM:AnyCpU pft10.xsl", "", "pft10.dll", "pft10.txt" })] [InlineData("/PLAtforM:AnyCpU pft10.xsl", "", "pft10.dll", "pft10.txt")] + [Trait("category", "XsltcExeRequired")] [ConditionalTheory(nameof(xsltcExeFound))] public void Var1(object param0, object param1, object param2, object param3) { @@ -45,6 +46,7 @@ namespace System.Xml.Tests [InlineData("/platform: pft12.xsl", "pft12.txt")] //[Variation("16", Desc = "Exercise basic use case, an unsupported option value -6", Pri = 1, Params = new object[] { "/platform:x86;x64 pft16.xsl", "pft16.txt" })] [InlineData("/platform:x86;x64 pft16.xsl", "pft16.txt")] + [Trait("category", "XsltcExeRequired")] [ConditionalTheory(nameof(xsltcExeFound))] public void Var2(object param0, object param1) { @@ -56,6 +58,7 @@ namespace System.Xml.Tests //[Variation("19", Desc = "Compile an assembly for different platform and load", Pri = 1, Params = new object[] { "pft19.xsl", "pft19.dll", "yes", "", "pft19.pdb", "no", "pft19.txt", "no" })] [InlineData("pft19.xsl", "pft19.dll", "yes", "", "pft19.pdb", "no", "pft19.txt", "no")] + [Trait("category", "XsltcExeRequired")] [ConditionalTheory(nameof(xsltcExeFound))] public void Var3(object param0, object param1, object param2, object param3, object param4, object param5, object param6, object param7) { diff --git a/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/CommonScenarios/XsltcTestSettings.cs b/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/CommonScenarios/XsltcTestSettings.cs index 39766f8234..36cd5f683a 100644 --- a/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/CommonScenarios/XsltcTestSettings.cs +++ b/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/CommonScenarios/XsltcTestSettings.cs @@ -48,6 +48,7 @@ namespace System.Xml.Tests [InlineData("/document sft49.xsl", "sft49.dll", "no", "sft49", "sft49.pdb", "no", "sft49.txt")] //[Variation("51", Desc = "Regression: Basic /settings test cases, two stylesheet with the same script block", Pri = 0, Params = new object[] { "/settings:script+ sft3.xsl sft4.xsl", "sft3.dll", "yes", "", "sft3.pdb", "no", "sft3.txt" })] [InlineData("/settings:script+ sft3.xsl sft4.xsl", "sft3.dll", "yes", "", "sft3.pdb", "no", "sft3.txt")] + [Trait("category", "XsltcExeRequired")] [ConditionalTheory(nameof(xsltcExeFound))] public void Var1(object param0, object param1, object param2, object param3, object param4, object param5, object param6) { diff --git a/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/XsltCommon.cs b/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/XsltCommon.cs index fd10058cb8..c923bd7326 100644 --- a/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/XsltCommon.cs +++ b/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/XsltCommon.cs @@ -5,6 +5,7 @@ using OLEDB.Test.ModuleCore; using System; using System.Collections; +using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; @@ -332,22 +333,31 @@ namespace XmlCoreTest.Common public static string SearchPath(String fileName) { - const string ToolsDirectorySuffix = @"bin\NETFX 4.5.1 Tools"; + var locations = new HashSet(StringComparer.OrdinalIgnoreCase); - var tools64 = new DirectoryInfo(@"C:\program Files (x86)\Microsoft SDKs\Windows"); - var tools32 = new DirectoryInfo(@"C:\program Files\Microsoft SDKs\Windows"); + // 32 bit if on 64 bit Windows + locations.Add(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), @"Microsoft SDKs\Windows")); + // 32 bit if on 32 bit Windows, otherwise 64 bit + locations.Add(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Microsoft SDKs\Windows")); + // 64 bit if in 32 bit process on 64 bit Windows + locations.Add(Path.Combine(Environment.GetEnvironmentVariable("ProgramW6432"), @"Microsoft SDKs\Windows")); - var directories64 = tools64.Exists ? tools64.EnumerateDirectories() : Enumerable.Empty(); - var directories32 = tools32.Exists ? tools32.EnumerateDirectories() : Enumerable.Empty(); + var files = new List(); - var toolPath = directories64.Concat(directories32) - .Select(dir => Path.Combine(dir.FullName, ToolsDirectorySuffix, fileName)) - .SingleOrDefault(File.Exists); + foreach (var location in locations) + { + if (Directory.Exists(location)) + files.AddRange(Directory.GetFiles(location, fileName, SearchOption.AllDirectories)); + } - if (toolPath == null) + if (files.Count == 0) throw new FileNotFoundException(fileName); - return toolPath; + // Crudely prefer newer versions, eg 4.6.2 over 4.6.1, + // but it currently is not important + files.Sort(StringComparer.OrdinalIgnoreCase); + + return files[files.Count - 1]; } } diff --git a/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/XsltCompiler.Tests.csproj b/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/XsltCompiler.Tests.csproj index ad8ad555e1..b395e52d70 100644 --- a/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/XsltCompiler.Tests.csproj +++ b/external/corefx/src/System.Private.Xml/tests/Xslt/XsltCompiler/XsltCompiler.Tests.csproj @@ -4,7 +4,6 @@ {59B2167E-E2D6-4E7E-AFFE-4060E1F3843C} - diff --git a/external/corefx/src/System.Reflection.DispatchProxy/src/System.Reflection.DispatchProxy.csproj b/external/corefx/src/System.Reflection.DispatchProxy/src/System.Reflection.DispatchProxy.csproj index 96b2fc831a..58e4b88958 100644 --- a/external/corefx/src/System.Reflection.DispatchProxy/src/System.Reflection.DispatchProxy.csproj +++ b/external/corefx/src/System.Reflection.DispatchProxy/src/System.Reflection.DispatchProxy.csproj @@ -12,6 +12,8 @@ + + diff --git a/external/corefx/src/System.Reflection.DispatchProxy/tests/System.Reflection.DispatchProxy.Tests.csproj b/external/corefx/src/System.Reflection.DispatchProxy/tests/System.Reflection.DispatchProxy.Tests.csproj index d94ee6f8d1..fb9bd161bc 100644 --- a/external/corefx/src/System.Reflection.DispatchProxy/tests/System.Reflection.DispatchProxy.Tests.csproj +++ b/external/corefx/src/System.Reflection.DispatchProxy/tests/System.Reflection.DispatchProxy.Tests.csproj @@ -5,7 +5,6 @@ {089444FE-8FF5-4D8F-A51B-32D026425F6B} $(DefineConstants);netcoreapp - diff --git a/external/corefx/src/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.cs b/external/corefx/src/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.cs index e349a3831c..0009996c2a 100644 --- a/external/corefx/src/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.cs +++ b/external/corefx/src/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.cs @@ -30,30 +30,23 @@ namespace System.Reflection.Emit public virtual System.Reflection.Emit.Label DefineLabel() { throw null; } public virtual void Emit(System.Reflection.Emit.OpCode opcode) { } public virtual void Emit(System.Reflection.Emit.OpCode opcode, byte arg) { } - [System.Security.SecuritySafeCriticalAttribute] public virtual void Emit(System.Reflection.Emit.OpCode opcode, double arg) { } public virtual void Emit(System.Reflection.Emit.OpCode opcode, short arg) { } public virtual void Emit(System.Reflection.Emit.OpCode opcode, int arg) { } public virtual void Emit(System.Reflection.Emit.OpCode opcode, long arg) { } - [System.Security.SecuritySafeCriticalAttribute] public virtual void Emit(System.Reflection.Emit.OpCode opcode, System.Reflection.ConstructorInfo con) { } public virtual void Emit(System.Reflection.Emit.OpCode opcode, System.Reflection.Emit.Label label) { } public virtual void Emit(System.Reflection.Emit.OpCode opcode, System.Reflection.Emit.Label[] labels) { } public virtual void Emit(System.Reflection.Emit.OpCode opcode, System.Reflection.Emit.LocalBuilder local) { } public virtual void Emit(System.Reflection.Emit.OpCode opcode, System.Reflection.Emit.SignatureHelper signature) { } public virtual void Emit(System.Reflection.Emit.OpCode opcode, System.Reflection.FieldInfo field) { } - [System.Security.SecuritySafeCriticalAttribute] public virtual void Emit(System.Reflection.Emit.OpCode opcode, System.Reflection.MethodInfo meth) { } [System.CLSCompliantAttribute(false)] public void Emit(System.Reflection.Emit.OpCode opcode, sbyte arg) { } - [System.Security.SecuritySafeCriticalAttribute] public virtual void Emit(System.Reflection.Emit.OpCode opcode, float arg) { } public virtual void Emit(System.Reflection.Emit.OpCode opcode, string str) { } - [System.Security.SecuritySafeCriticalAttribute] public virtual void Emit(System.Reflection.Emit.OpCode opcode, System.Type cls) { } - [System.Security.SecuritySafeCriticalAttribute] public virtual void EmitCall(System.Reflection.Emit.OpCode opcode, System.Reflection.MethodInfo methodInfo, System.Type[] optionalParameterTypes) { } - [System.Security.SecuritySafeCriticalAttribute] public virtual void EmitCalli(System.Reflection.Emit.OpCode opcode, System.Reflection.CallingConventions callingConvention, System.Type returnType, System.Type[] parameterTypes, System.Type[] optionalParameterTypes) { } public virtual void EmitWriteLine(System.Reflection.Emit.LocalBuilder localBuilder) { } public virtual void EmitWriteLine(System.Reflection.FieldInfo fld) { } @@ -64,9 +57,9 @@ namespace System.Reflection.Emit public virtual void ThrowException(System.Type excType) { } public virtual void UsingNamespace(string usingNamespace) { } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Label { + private int _dummy; public override bool Equals(object obj) { throw null; } public bool Equals(System.Reflection.Emit.Label obj) { throw null; } public override int GetHashCode() { throw null; } @@ -89,20 +82,15 @@ namespace System.Reflection.Emit public bool IsOut { get { throw null; } } public virtual string Name { get { throw null; } } public virtual int Position { get { throw null; } } - [System.Security.SecuritySafeCriticalAttribute] public virtual void SetConstant(object defaultValue) { } - [System.Security.SecuritySafeCriticalAttribute] public void SetCustomAttribute(System.Reflection.ConstructorInfo con, byte[] binaryAttribute) { } - [System.Security.SecuritySafeCriticalAttribute] public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } } public sealed partial class SignatureHelper { internal SignatureHelper() { } public void AddArgument(System.Type clsArgument) { } - [System.Security.SecuritySafeCriticalAttribute] public void AddArgument(System.Type argument, bool pinned) { } - [System.Security.SecuritySafeCriticalAttribute] public void AddArgument(System.Type argument, System.Type[] requiredCustomModifiers, System.Type[] optionalCustomModifiers) { } public void AddArguments(System.Type[] arguments, System.Type[][] requiredCustomModifiers, System.Type[][] optionalCustomModifiers) { } public void AddSentinel() { } @@ -112,11 +100,8 @@ namespace System.Reflection.Emit public static System.Reflection.Emit.SignatureHelper GetLocalVarSigHelper() { throw null; } public static System.Reflection.Emit.SignatureHelper GetLocalVarSigHelper(System.Reflection.Module mod) { throw null; } public static System.Reflection.Emit.SignatureHelper GetMethodSigHelper(System.Reflection.CallingConventions callingConvention, System.Type returnType) { throw null; } - [System.Security.SecuritySafeCriticalAttribute] public static System.Reflection.Emit.SignatureHelper GetMethodSigHelper(System.Reflection.Module mod, System.Reflection.CallingConventions callingConvention, System.Type returnType) { throw null; } - [System.Security.SecuritySafeCriticalAttribute] public static System.Reflection.Emit.SignatureHelper GetMethodSigHelper(System.Reflection.Module mod, System.Type returnType, System.Type[] parameterTypes) { throw null; } - [System.Security.SecuritySafeCriticalAttribute] public static System.Reflection.Emit.SignatureHelper GetPropertySigHelper(System.Reflection.Module mod, System.Reflection.CallingConventions callingConvention, System.Type returnType, System.Type[] requiredReturnTypeCustomModifiers, System.Type[] optionalReturnTypeCustomModifiers, System.Type[] parameterTypes, System.Type[][] requiredParameterTypeCustomModifiers, System.Type[][] optionalParameterTypeCustomModifiers) { throw null; } public static System.Reflection.Emit.SignatureHelper GetPropertySigHelper(System.Reflection.Module mod, System.Type returnType, System.Type[] parameterTypes) { throw null; } public static System.Reflection.Emit.SignatureHelper GetPropertySigHelper(System.Reflection.Module mod, System.Type returnType, System.Type[] requiredReturnTypeCustomModifiers, System.Type[] optionalReturnTypeCustomModifiers, System.Type[] parameterTypes, System.Type[][] requiredParameterTypeCustomModifiers, System.Type[][] optionalParameterTypeCustomModifiers) { throw null; } diff --git a/external/corefx/src/System.Reflection.Emit.ILGeneration/tests/System.Reflection.Emit.ILGeneration.Tests.csproj b/external/corefx/src/System.Reflection.Emit.ILGeneration/tests/System.Reflection.Emit.ILGeneration.Tests.csproj index 081ff1781a..55be259a78 100644 --- a/external/corefx/src/System.Reflection.Emit.ILGeneration/tests/System.Reflection.Emit.ILGeneration.Tests.csproj +++ b/external/corefx/src/System.Reflection.Emit.ILGeneration/tests/System.Reflection.Emit.ILGeneration.Tests.csproj @@ -4,7 +4,6 @@ {FB037640-0591-4DF4-A331-0BEFE50A200B} - diff --git a/external/corefx/src/System.Reflection.Emit.Lightweight/ref/System.Reflection.Emit.Lightweight.cs b/external/corefx/src/System.Reflection.Emit.Lightweight/ref/System.Reflection.Emit.Lightweight.cs index e9dfca7877..364e7f3dda 100644 --- a/external/corefx/src/System.Reflection.Emit.Lightweight/ref/System.Reflection.Emit.Lightweight.cs +++ b/external/corefx/src/System.Reflection.Emit.Lightweight/ref/System.Reflection.Emit.Lightweight.cs @@ -10,21 +10,13 @@ namespace System.Reflection.Emit { public sealed partial class DynamicMethod : System.Reflection.MethodInfo { - [System.Security.SecuritySafeCriticalAttribute] public DynamicMethod(string name, System.Reflection.MethodAttributes attributes, System.Reflection.CallingConventions callingConvention, System.Type returnType, System.Type[] parameterTypes, System.Reflection.Module m, bool skipVisibility) { } - [System.Security.SecuritySafeCriticalAttribute] public DynamicMethod(string name, System.Reflection.MethodAttributes attributes, System.Reflection.CallingConventions callingConvention, System.Type returnType, System.Type[] parameterTypes, System.Type owner, bool skipVisibility) { } - [System.Security.SecuritySafeCriticalAttribute] public DynamicMethod(string name, System.Type returnType, System.Type[] parameterTypes) { } - [System.Security.SecuritySafeCriticalAttribute] public DynamicMethod(string name, System.Type returnType, System.Type[] parameterTypes, bool restrictedSkipVisibility) { } - [System.Security.SecuritySafeCriticalAttribute] public DynamicMethod(string name, System.Type returnType, System.Type[] parameterTypes, System.Reflection.Module m) { } - [System.Security.SecuritySafeCriticalAttribute] public DynamicMethod(string name, System.Type returnType, System.Type[] parameterTypes, System.Reflection.Module m, bool skipVisibility) { } - [System.Security.SecuritySafeCriticalAttribute] public DynamicMethod(string name, System.Type returnType, System.Type[] parameterTypes, System.Type owner) { } - [System.Security.SecuritySafeCriticalAttribute] public DynamicMethod(string name, System.Type returnType, System.Type[] parameterTypes, System.Type owner, bool skipVisibility) { } public override System.Reflection.MethodAttributes Attributes { get { throw null; } } public override System.Reflection.CallingConventions CallingConvention { get { throw null; } } @@ -36,15 +28,12 @@ namespace System.Reflection.Emit public override System.Reflection.ParameterInfo ReturnParameter { get { throw null; } } public override System.Type ReturnType { get { throw null; } } public override System.Reflection.ICustomAttributeProvider ReturnTypeCustomAttributes { get { throw null; } } - [System.Security.SecuritySafeCriticalAttribute] public sealed override System.Delegate CreateDelegate(System.Type delegateType) { throw null; } - [System.Security.SecuritySafeCriticalAttribute] public sealed override System.Delegate CreateDelegate(System.Type delegateType, object target) { throw null; } public override System.Reflection.MethodInfo GetBaseDefinition() { throw null; } public override object[] GetCustomAttributes(bool inherit) { throw null; } public override object[] GetCustomAttributes(System.Type attributeType, bool inherit) { throw null; } public System.Reflection.Emit.ILGenerator GetILGenerator() { throw null; } - [System.Security.SecuritySafeCriticalAttribute] public System.Reflection.Emit.ILGenerator GetILGenerator(int streamSize) { throw null; } public override System.Reflection.MethodImplAttributes GetMethodImplementationFlags() { throw null; } public override System.Reflection.ParameterInfo[] GetParameters() { throw null; } diff --git a/external/corefx/src/System.Reflection.Emit.Lightweight/tests/DynamicMethodCtor.cs b/external/corefx/src/System.Reflection.Emit.Lightweight/tests/DynamicMethodCtor.cs index 65c8e362ad..e35fba88e7 100644 --- a/external/corefx/src/System.Reflection.Emit.Lightweight/tests/DynamicMethodCtor.cs +++ b/external/corefx/src/System.Reflection.Emit.Lightweight/tests/DynamicMethodCtor.cs @@ -9,6 +9,8 @@ namespace System.Reflection.Emit.Tests { public class DynamicMethodctor1 { + delegate ref int GetRefIntoArrayDelegate(int[] array, int index); + [Theory] [InlineData("Method", typeof(void), null)] [InlineData("Method", typeof(void), new Type[] { typeof(int), typeof(string) })] @@ -113,24 +115,24 @@ namespace System.Reflection.Emit.Tests } [Fact] - public void ByRefReturnType_ThrowsNotSupportedException() + public void ByRefReturnType_DoesNotThrow() { Module module = typeof(TestClass).GetTypeInfo().Module; - Assert.Throws(() => new DynamicMethod("Method", typeof(int).MakeByRefType(), new Type[0], module)); - Assert.Throws(() => new DynamicMethod("Method", typeof(int).MakeByRefType(), new Type[0], module, true)); - Assert.Throws(() => new DynamicMethod("Method", typeof(int).MakeByRefType(), new Type[0], module, false)); + DynamicMethod method = new DynamicMethod("Method", typeof(int).MakeByRefType(), new[] { typeof(int[]), typeof(int) }); + ILGenerator generator = method.GetILGenerator(); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldarg_1); + generator.Emit(OpCodes.Ldelema, typeof(int)); + generator.Emit(OpCodes.Ret); - Assert.Throws(() => new DynamicMethod("Method", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, typeof(int).MakeByRefType(), new Type[0], module, true)); - Assert.Throws(() => new DynamicMethod("Method", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, typeof(int).MakeByRefType(), new Type[0], module, false)); + var methodDelegate = (GetRefIntoArrayDelegate) method.CreateDelegate(typeof(GetRefIntoArrayDelegate)); - Assert.Throws(() => new DynamicMethod("Method", typeof(int).MakeByRefType(), new Type[0], typeof(TestClass))); + var array = new int[] { 0, 1, 2, 3 }; + ref int element = ref methodDelegate(array, 2); + element = 10; - Assert.Throws(() => new DynamicMethod("Method", typeof(int).MakeByRefType(), new Type[0], typeof(TestClass), true)); - Assert.Throws(() => new DynamicMethod("Method", typeof(int).MakeByRefType(), new Type[0], typeof(TestClass), false)); - - Assert.Throws(() => new DynamicMethod("Method", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, typeof(int).MakeByRefType(), new Type[0], typeof(TestClass), true)); - Assert.Throws(() => new DynamicMethod("Method", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, typeof(int).MakeByRefType(), new Type[0], typeof(TestClass), false)); + Assert.Equal(10, array[2]); } [Fact] diff --git a/external/corefx/src/System.Reflection.Emit.Lightweight/tests/System.Reflection.Emit.Lightweight.Tests.csproj b/external/corefx/src/System.Reflection.Emit.Lightweight/tests/System.Reflection.Emit.Lightweight.Tests.csproj index e197c108e9..8d833da512 100644 --- a/external/corefx/src/System.Reflection.Emit.Lightweight/tests/System.Reflection.Emit.Lightweight.Tests.csproj +++ b/external/corefx/src/System.Reflection.Emit.Lightweight/tests/System.Reflection.Emit.Lightweight.Tests.csproj @@ -4,7 +4,6 @@ {C338DCF7-FB75-407B-A2ED-117FBF3AAA18} - diff --git a/external/corefx/src/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj b/external/corefx/src/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj index 1cd8ecb911..2dbe6359d1 100644 --- a/external/corefx/src/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj +++ b/external/corefx/src/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj @@ -4,7 +4,6 @@ {1104A263-331A-4CA0-B541-759BD20F7B1D} - @@ -112,4 +111,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Reflection.Extensions/tests/System.Reflection.Extensions.Tests.csproj b/external/corefx/src/System.Reflection.Extensions/tests/System.Reflection.Extensions.Tests.csproj index e0d45e8fa0..11ec5bec47 100644 --- a/external/corefx/src/System.Reflection.Extensions/tests/System.Reflection.Extensions.Tests.csproj +++ b/external/corefx/src/System.Reflection.Extensions/tests/System.Reflection.Extensions.Tests.csproj @@ -4,7 +4,6 @@ {A5E6F8C2-8E71-4148-8806-12FFBDBE2974} - diff --git a/external/corefx/src/System.Reflection.Metadata/ref/System.Reflection.Metadata.cs.REMOVED.git-id b/external/corefx/src/System.Reflection.Metadata/ref/System.Reflection.Metadata.cs.REMOVED.git-id index a4a919cda6..ac72ceaae0 100644 --- a/external/corefx/src/System.Reflection.Metadata/ref/System.Reflection.Metadata.cs.REMOVED.git-id +++ b/external/corefx/src/System.Reflection.Metadata/ref/System.Reflection.Metadata.cs.REMOVED.git-id @@ -1 +1 @@ -507b479bf6a15cb7a5f6d50c79d47577cefd2e94 \ No newline at end of file +93e9e802bb048df0a12bc7c17523cc0f099c10c9 \ No newline at end of file diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Internal/MemoryBlocks/StreamConstraints.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Internal/MemoryBlocks/StreamConstraints.cs index 963a457346..b377a84c4f 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Internal/MemoryBlocks/StreamConstraints.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Internal/MemoryBlocks/StreamConstraints.cs @@ -4,7 +4,7 @@ namespace System.Reflection.Internal { - internal struct StreamConstraints + internal readonly struct StreamConstraints { public readonly object GuardOpt; public readonly long ImageStart; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/MemoryBlock.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/MemoryBlock.cs index c79c35a10a..e9232ce2b2 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/MemoryBlock.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/MemoryBlock.cs @@ -11,7 +11,7 @@ using System.Text; namespace System.Reflection.Internal { [DebuggerDisplay("{GetDebuggerDisplay(),nq}")] - internal unsafe struct MemoryBlock + internal unsafe readonly struct MemoryBlock { internal readonly byte* Pointer; internal readonly int Length; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Blob.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Blob.cs index 5428713778..6c73b4cb9c 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Blob.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Blob.cs @@ -4,7 +4,7 @@ namespace System.Reflection.Metadata { - public struct Blob + public readonly struct Blob { internal readonly byte[] Buffer; internal readonly int Start; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobContentId.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobContentId.cs index 373637a622..fa9540f56e 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobContentId.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobContentId.cs @@ -8,7 +8,7 @@ using System.Reflection.Internal; namespace System.Reflection.Metadata { - public struct BlobContentId : IEquatable + public readonly struct BlobContentId : IEquatable { private const int Size = BlobUtilities.SizeOfGuid + sizeof(uint); diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/CustomAttributeDecoder.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/CustomAttributeDecoder.cs index 7993d19028..d2129b66d4 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/CustomAttributeDecoder.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/CustomAttributeDecoder.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. @@ -9,7 +9,7 @@ namespace System.Reflection.Metadata.Ecma335 /// /// Decodes custom attribute blobs. /// - internal struct CustomAttributeDecoder + internal readonly struct CustomAttributeDecoder { private readonly ICustomAttributeTypeProvider _provider; private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/EditAndContinueLogEntry.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/EditAndContinueLogEntry.cs index f4b17378cd..283afcd193 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/EditAndContinueLogEntry.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/EditAndContinueLogEntry.cs @@ -4,7 +4,7 @@ namespace System.Reflection.Metadata.Ecma335 { - public struct EditAndContinueLogEntry : IEquatable + public readonly struct EditAndContinueLogEntry : IEquatable { public EntityHandle Handle { get; } public EditAndContinueOperation Operation { get; } diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/BlobEncoders.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/BlobEncoders.cs index 34902cf246..47c6ca9c8d 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/BlobEncoders.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/BlobEncoders.cs @@ -9,7 +9,7 @@ namespace System.Reflection.Metadata.Ecma335 // TODO: debug metadata blobs // TODO: revisit ctors (public vs internal vs static factories)? - public struct BlobEncoder + public readonly struct BlobEncoder { public BlobBuilder Builder { get; } @@ -191,7 +191,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - public struct MethodSignatureEncoder + public readonly struct MethodSignatureEncoder { public BlobBuilder Builder { get; } public bool HasVarArgs { get; } @@ -242,7 +242,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - public struct LocalVariablesEncoder + public readonly struct LocalVariablesEncoder { public BlobBuilder Builder { get; } @@ -257,7 +257,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - public struct LocalVariableTypeEncoder + public readonly struct LocalVariableTypeEncoder { public BlobBuilder Builder { get; } @@ -292,7 +292,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - public struct ParameterTypeEncoder + public readonly struct ParameterTypeEncoder { public BlobBuilder Builder { get; } @@ -322,7 +322,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - public struct PermissionSetEncoder + public readonly struct PermissionSetEncoder { public BlobBuilder Builder { get; } @@ -378,7 +378,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - public struct GenericTypeArgumentsEncoder + public readonly struct GenericTypeArgumentsEncoder { public BlobBuilder Builder { get; } @@ -393,7 +393,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - public struct FixedArgumentsEncoder + public readonly struct FixedArgumentsEncoder { public BlobBuilder Builder { get; } @@ -408,7 +408,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - public struct LiteralEncoder + public readonly struct LiteralEncoder { public BlobBuilder Builder { get; } @@ -492,7 +492,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - public struct ScalarEncoder + public readonly struct ScalarEncoder { public BlobBuilder Builder { get; } @@ -564,7 +564,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - public struct LiteralsEncoder + public readonly struct LiteralsEncoder { public BlobBuilder Builder { get; } @@ -579,7 +579,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - public struct VectorEncoder + public readonly struct VectorEncoder { public BlobBuilder Builder { get; } @@ -600,7 +600,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - public struct NameEncoder + public readonly struct NameEncoder { public BlobBuilder Builder { get; } @@ -618,7 +618,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - public struct CustomAttributeNamedArgumentsEncoder + public readonly struct CustomAttributeNamedArgumentsEncoder { public BlobBuilder Builder { get; } @@ -639,7 +639,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - public struct NamedArgumentsEncoder + public readonly struct NamedArgumentsEncoder { public BlobBuilder Builder { get; } @@ -688,7 +688,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - public struct NamedArgumentTypeEncoder + public readonly struct NamedArgumentTypeEncoder { public BlobBuilder Builder { get; } @@ -713,7 +713,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - public struct CustomAttributeArrayTypeEncoder + public readonly struct CustomAttributeArrayTypeEncoder { public BlobBuilder Builder { get; } @@ -735,7 +735,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - public struct CustomAttributeElementTypeEncoder + public readonly struct CustomAttributeElementTypeEncoder { public BlobBuilder Builder { get; } @@ -804,7 +804,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - public struct SignatureTypeEncoder + public readonly struct SignatureTypeEncoder { public BlobBuilder Builder { get; } @@ -1057,7 +1057,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - public struct CustomModifiersEncoder + public readonly struct CustomModifiersEncoder { public BlobBuilder Builder { get; } @@ -1094,7 +1094,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - public struct ArrayShapeEncoder + public readonly struct ArrayShapeEncoder { public BlobBuilder Builder { get; } @@ -1174,7 +1174,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - public struct ReturnTypeEncoder + public readonly struct ReturnTypeEncoder { public BlobBuilder Builder { get; } @@ -1209,7 +1209,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - public struct ParametersEncoder + public readonly struct ParametersEncoder { public BlobBuilder Builder { get; } public bool HasVarArgs { get; } diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/ControlFlowBuilder.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/ControlFlowBuilder.cs index 02cbf38a7c..d839cba5ab 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/ControlFlowBuilder.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/ControlFlowBuilder.cs @@ -12,7 +12,7 @@ namespace System.Reflection.Metadata.Ecma335 public sealed class ControlFlowBuilder { // internal for testing: - internal struct BranchInfo + internal readonly struct BranchInfo { internal readonly int ILOffset; internal readonly LabelHandle Label; @@ -49,7 +49,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - internal struct ExceptionHandlerInfo + internal readonly struct ExceptionHandlerInfo { public readonly ExceptionRegionKind Kind; public readonly LabelHandle TryStart, TryEnd, HandlerStart, HandlerEnd, FilterStart; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/ExceptionRegionEncoder.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/ExceptionRegionEncoder.cs index 093fb57eb0..2e3a00915d 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/ExceptionRegionEncoder.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/ExceptionRegionEncoder.cs @@ -6,7 +6,7 @@ using System.Diagnostics; namespace System.Reflection.Metadata.Ecma335 { - public struct ExceptionRegionEncoder + public readonly struct ExceptionRegionEncoder { private const int TableHeaderSize = 4; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/InstructionEncoder.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/InstructionEncoder.cs index 7e6e69b0be..5da7de660a 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/InstructionEncoder.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/InstructionEncoder.cs @@ -9,7 +9,7 @@ namespace System.Reflection.Metadata.Ecma335 /// /// Encodes instructions. /// - public struct InstructionEncoder + public readonly struct InstructionEncoder { /// /// Underlying builder where encoded instructions are written to. diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/LabelHandle.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/LabelHandle.cs index 814dd2ce66..f39a991c1b 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/LabelHandle.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/LabelHandle.cs @@ -6,7 +6,7 @@ using System.Diagnostics; namespace System.Reflection.Metadata.Ecma335 { - public struct LabelHandle : IEquatable + public readonly struct LabelHandle : IEquatable { /// /// 1-based id identifying the label within the context of a . diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/MethodBodyStreamEncoder.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/MethodBodyStreamEncoder.cs index 0aa427038d..1c97e2bc3b 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/MethodBodyStreamEncoder.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/MethodBodyStreamEncoder.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. @@ -7,7 +7,7 @@ namespace System.Reflection.Metadata.Ecma335 /// /// Encodes method body stream. /// - public struct MethodBodyStreamEncoder + public readonly struct MethodBodyStreamEncoder { public BlobBuilder Builder { get; } @@ -75,7 +75,7 @@ namespace System.Reflection.Metadata.Ecma335 return new MethodBody(bodyOffset, instructions, regionEncoder); } - public struct MethodBody + public readonly struct MethodBody { /// /// Offset of the encoded method body in method body stream. diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/SignatureDecoder.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/SignatureDecoder.cs index 115809eea5..fe294a8e28 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/SignatureDecoder.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/SignatureDecoder.cs @@ -11,7 +11,7 @@ namespace System.Reflection.Metadata.Ecma335 /// Decodes signature blobs. /// See Metadata Specification section II.23.2: Blobs and signatures. /// - public struct SignatureDecoder + public readonly struct SignatureDecoder { private readonly ISignatureTypeProvider _provider; private readonly MetadataReader _metadataReaderOpt; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/EntityHandle.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/EntityHandle.cs index 128df0839c..6657302559 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/EntityHandle.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/EntityHandle.cs @@ -14,7 +14,7 @@ namespace System.Reflection.Metadata /// Use to store multiple kinds of entity handles. /// It has smaller memory footprint than . /// - public struct EntityHandle : IEquatable + public readonly struct EntityHandle : IEquatable { // bits: // 31: IsVirtual diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Handle.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Handle.cs index 8ec20ce16a..06af8a45b3 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Handle.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Handle.cs @@ -13,7 +13,7 @@ namespace System.Reflection.Metadata /// /// Use to store multiple kinds of handles. /// - public struct Handle : IEquatable + public readonly struct Handle : IEquatable { private readonly int _value; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/IL/ExceptionRegion.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/IL/ExceptionRegion.cs index 1290cd2b93..0fd3ff901d 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/IL/ExceptionRegion.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/IL/ExceptionRegion.cs @@ -4,7 +4,7 @@ namespace System.Reflection.Metadata { - public struct ExceptionRegion + public readonly struct ExceptionRegion { private readonly ExceptionRegionKind _kind; private readonly int _tryOffset; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/GuidHeap.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/GuidHeap.cs index 484dac901a..8676fadff4 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/GuidHeap.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/GuidHeap.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. @@ -6,7 +6,7 @@ using System.Reflection.Internal; namespace System.Reflection.Metadata.Ecma335 { - internal struct GuidHeap + internal readonly struct GuidHeap { internal readonly MemoryBlock Block; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/Tables.cs.REMOVED.git-id b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/Tables.cs.REMOVED.git-id index f01b22e98b..7be2e779a0 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/Tables.cs.REMOVED.git-id +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/Tables.cs.REMOVED.git-id @@ -1 +1 @@ -4642c736544518860b428f551989e712f153f394 \ No newline at end of file +6a72ec7047f62aa3ef9f833dacce97d476f5eacc \ No newline at end of file diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/UserStringHeap.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/UserStringHeap.cs index ae97afccb5..86fe7624a7 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/UserStringHeap.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/UserStringHeap.cs @@ -6,7 +6,7 @@ using System.Reflection.Internal; namespace System.Reflection.Metadata.Ecma335 { - internal struct UserStringHeap + internal readonly struct UserStringHeap { internal readonly MemoryBlock Block; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/MetadataReader.WinMD.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/MetadataReader.WinMD.cs index 2c1467424b..557e984af8 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/MetadataReader.WinMD.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/MetadataReader.WinMD.cs @@ -29,7 +29,7 @@ namespace System.Reflection.Metadata private static string[] s_projectedTypeNames; private static ProjectionInfo[] s_projectionInfos; - private struct ProjectionInfo + private readonly struct ProjectionInfo { public readonly string WinRTNamespace; public readonly StringHandle.VirtualIndex ClrNamespace; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/MetadataStringComparer.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/MetadataStringComparer.cs index 6c35cb9570..1117c93dc2 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/MetadataStringComparer.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/MetadataStringComparer.cs @@ -46,7 +46,7 @@ namespace System.Reflection.Metadata /// /// The choice between them is therefore one of style and not performance. /// - public struct MetadataStringComparer + public readonly struct MetadataStringComparer { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/CustomDebugInformation.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/CustomDebugInformation.cs index 9f18ce64e2..6cff765fa4 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/CustomDebugInformation.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/CustomDebugInformation.cs @@ -6,7 +6,7 @@ using System.Diagnostics; namespace System.Reflection.Metadata { - public struct CustomDebugInformation + public readonly struct CustomDebugInformation { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/Document.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/Document.cs index 4821f7b21b..6f322eefac 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/Document.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/Document.cs @@ -12,7 +12,7 @@ namespace System.Reflection.Metadata /// /// See also https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md#document-table-0x30. /// - public struct Document + public readonly struct Document { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/DocumentNameBlobHandle.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/DocumentNameBlobHandle.cs index b50204bb8f..0c6ccf52a2 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/DocumentNameBlobHandle.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/DocumentNameBlobHandle.cs @@ -16,7 +16,7 @@ namespace System.Reflection.Metadata /// The kind of the handle is . /// The handle is a specialization of and doesn't have a distinct kind. /// - public struct DocumentNameBlobHandle : IEquatable + public readonly struct DocumentNameBlobHandle : IEquatable { // bits: // 29..31: 0 diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/HandleCollections.Debug.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/HandleCollections.Debug.cs index 184f3eef2e..799e887144 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/HandleCollections.Debug.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/HandleCollections.Debug.cs @@ -9,7 +9,7 @@ using System.Reflection.Metadata.Ecma335; namespace System.Reflection.Metadata { - public struct DocumentHandleCollection : IReadOnlyCollection + public readonly struct DocumentHandleCollection : IReadOnlyCollection { private readonly MetadataReader _reader; @@ -108,7 +108,7 @@ namespace System.Reflection.Metadata } } - public struct MethodDebugInformationHandleCollection : IReadOnlyCollection + public readonly struct MethodDebugInformationHandleCollection : IReadOnlyCollection { private readonly MetadataReader _reader; @@ -207,7 +207,7 @@ namespace System.Reflection.Metadata } } - public struct LocalScopeHandleCollection : IReadOnlyCollection + public readonly struct LocalScopeHandleCollection : IReadOnlyCollection { private readonly MetadataReader _reader; @@ -415,7 +415,7 @@ namespace System.Reflection.Metadata } } - public struct LocalVariableHandleCollection : IReadOnlyCollection + public readonly struct LocalVariableHandleCollection : IReadOnlyCollection { private readonly MetadataReader _reader; @@ -521,7 +521,7 @@ namespace System.Reflection.Metadata } } - public struct LocalConstantHandleCollection : IReadOnlyCollection + public readonly struct LocalConstantHandleCollection : IReadOnlyCollection { private readonly MetadataReader _reader; @@ -627,7 +627,7 @@ namespace System.Reflection.Metadata } } - public struct ImportScopeCollection : IReadOnlyCollection + public readonly struct ImportScopeCollection : IReadOnlyCollection { private readonly MetadataReader _reader; @@ -726,7 +726,7 @@ namespace System.Reflection.Metadata } } - public struct CustomDebugInformationHandleCollection : IReadOnlyCollection + public readonly struct CustomDebugInformationHandleCollection : IReadOnlyCollection { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/Handles.Debug.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/Handles.Debug.cs index b94243942f..1df779cadf 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/Handles.Debug.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/Handles.Debug.cs @@ -7,7 +7,7 @@ using System.Reflection.Metadata.Ecma335; namespace System.Reflection.Metadata { - public struct DocumentHandle : IEquatable + public readonly struct DocumentHandle : IEquatable { private const uint tokenType = TokenTypeIds.Document; private const byte tokenTypeSmall = (byte)HandleType.Document; @@ -90,7 +90,7 @@ namespace System.Reflection.Metadata } } - public struct MethodDebugInformationHandle : IEquatable + public readonly struct MethodDebugInformationHandle : IEquatable { private const uint tokenType = TokenTypeIds.MethodDebugInformation; private const byte tokenTypeSmall = (byte)HandleType.MethodDebugInformation; @@ -185,7 +185,7 @@ namespace System.Reflection.Metadata } } - public struct LocalScopeHandle : IEquatable + public readonly struct LocalScopeHandle : IEquatable { private const uint tokenType = TokenTypeIds.LocalScope; private const byte tokenTypeSmall = (byte)HandleType.LocalScope; @@ -268,7 +268,7 @@ namespace System.Reflection.Metadata } } - public struct LocalVariableHandle : IEquatable + public readonly struct LocalVariableHandle : IEquatable { private const uint tokenType = TokenTypeIds.LocalVariable; private const byte tokenTypeSmall = (byte)HandleType.LocalVariable; @@ -351,7 +351,7 @@ namespace System.Reflection.Metadata } } - public struct LocalConstantHandle : IEquatable + public readonly struct LocalConstantHandle : IEquatable { private const uint tokenType = TokenTypeIds.LocalConstant; private const byte tokenTypeSmall = (byte)HandleType.LocalConstant; @@ -434,7 +434,7 @@ namespace System.Reflection.Metadata } } - public struct ImportScopeHandle : IEquatable + public readonly struct ImportScopeHandle : IEquatable { private const uint tokenType = TokenTypeIds.ImportScope; private const byte tokenTypeSmall = (byte)HandleType.ImportScope; @@ -517,7 +517,7 @@ namespace System.Reflection.Metadata } } - public struct CustomDebugInformationHandle : IEquatable + public readonly struct CustomDebugInformationHandle : IEquatable { private const uint tokenType = TokenTypeIds.CustomDebugInformation; private const byte tokenTypeSmall = (byte)HandleType.CustomDebugInformation; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/ImportDefinition.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/ImportDefinition.cs index eccd79ac74..014a883cc7 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/ImportDefinition.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/ImportDefinition.cs @@ -6,7 +6,7 @@ using System.Diagnostics; namespace System.Reflection.Metadata { - public struct ImportDefinition + public readonly struct ImportDefinition { public ImportDefinitionKind Kind { get; } public BlobHandle Alias { get; } diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/ImportDefinitionCollection.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/ImportDefinitionCollection.cs index 98bb31e78b..b4d62b1e3a 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/ImportDefinitionCollection.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/ImportDefinitionCollection.cs @@ -10,7 +10,7 @@ using System.Reflection.Metadata.Ecma335; namespace System.Reflection.Metadata { - public struct ImportDefinitionCollection : IEnumerable + public readonly struct ImportDefinitionCollection : IEnumerable { private readonly MemoryBlock _block; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/ImportScope.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/ImportScope.cs index eee67d94eb..bafb119490 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/ImportScope.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/ImportScope.cs @@ -12,7 +12,7 @@ namespace System.Reflection.Metadata /// /// See https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md#importscope-table-0x35 /// - public struct ImportScope + public readonly struct ImportScope { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/LocalConstant.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/LocalConstant.cs index 61f562e803..d989d84e17 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/LocalConstant.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/LocalConstant.cs @@ -12,7 +12,7 @@ namespace System.Reflection.Metadata /// /// See https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md#localconstant-table-0x34. /// - public struct LocalConstant + public readonly struct LocalConstant { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/LocalScope.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/LocalScope.cs index d19377d2a8..d9294ce51c 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/LocalScope.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/LocalScope.cs @@ -12,7 +12,7 @@ namespace System.Reflection.Metadata /// /// See https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md#localscope-table-0x32. /// - public struct LocalScope + public readonly struct LocalScope { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/LocalVariable.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/LocalVariable.cs index 91d4539c36..0c96e8dd41 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/LocalVariable.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/LocalVariable.cs @@ -12,7 +12,7 @@ namespace System.Reflection.Metadata /// /// See https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md#localvariable-table-0x33. /// - public struct LocalVariable + public readonly struct LocalVariable { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/MethodDebugInformation.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/MethodDebugInformation.cs index 45e13a4389..40e8ee844a 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/MethodDebugInformation.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/MethodDebugInformation.cs @@ -12,7 +12,7 @@ namespace System.Reflection.Metadata /// /// See https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md#methoddebuginformation-table-0x31. /// - public struct MethodDebugInformation + public readonly struct MethodDebugInformation { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/SequencePoint.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/SequencePoint.cs index e3a21bcce9..7822d62aae 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/SequencePoint.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/SequencePoint.cs @@ -8,7 +8,7 @@ using System.Reflection.Internal; namespace System.Reflection.Metadata { [DebuggerDisplay("{GetDebuggerDisplay(),nq}")] - public struct SequencePoint : IEquatable + public readonly struct SequencePoint : IEquatable { public const int HiddenLine = 0xfeefee; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/SequencePointCollection.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/SequencePointCollection.cs index 0eaac0efaa..e8a8369823 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/SequencePointCollection.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/SequencePointCollection.cs @@ -10,7 +10,7 @@ using System.Reflection.Metadata.Ecma335; namespace System.Reflection.Metadata { - public struct SequencePointCollection : IEnumerable + public readonly struct SequencePointCollection : IEnumerable { private readonly MemoryBlock _block; private readonly DocumentHandle _document; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/Tables.Debug.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/Tables.Debug.cs index e40e7651b1..4ae0b89629 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/Tables.Debug.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/PortablePdb/Tables.Debug.cs @@ -6,7 +6,7 @@ using System.Reflection.Internal; namespace System.Reflection.Metadata.Ecma335 { - internal struct DocumentTableReader + internal readonly struct DocumentTableReader { internal readonly int NumberOfRows; @@ -65,7 +65,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - internal struct MethodDebugInformationTableReader + internal readonly struct MethodDebugInformationTableReader { internal readonly int NumberOfRows; @@ -108,7 +108,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - internal struct LocalScopeTableReader + internal readonly struct LocalScopeTableReader { internal readonly int NumberOfRows; @@ -236,7 +236,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - internal struct LocalVariableTableReader + internal readonly struct LocalVariableTableReader { internal readonly int NumberOfRows; private readonly bool _isStringHeapRefSizeSmall; @@ -283,7 +283,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - internal struct LocalConstantTableReader + internal readonly struct LocalConstantTableReader { internal readonly int NumberOfRows; private readonly bool _isStringHeapRefSizeSmall; @@ -325,7 +325,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - internal struct StateMachineMethodTableReader + internal readonly struct StateMachineMethodTableReader { internal readonly int NumberOfRows; @@ -383,7 +383,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - internal struct ImportScopeTableReader + internal readonly struct ImportScopeTableReader { internal readonly int NumberOfRows; @@ -426,7 +426,7 @@ namespace System.Reflection.Metadata.Ecma335 } } - internal struct CustomDebugInformationTableReader + internal readonly struct CustomDebugInformationTableReader { internal readonly int NumberOfRows; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/ReservedBlob.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/ReservedBlob.cs index afa34a554c..f8a91b77b1 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/ReservedBlob.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/ReservedBlob.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. @@ -7,7 +7,7 @@ namespace System.Reflection.Metadata /// /// Represents a handle and a corresponding blob on a metadata heap that was reserved for future content update. /// - public struct ReservedBlob + public readonly struct ReservedBlob where THandle : struct { public THandle Handle { get; } diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Signatures/ArrayShape.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Signatures/ArrayShape.cs index 9df53faa68..88a474c361 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Signatures/ArrayShape.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Signatures/ArrayShape.cs @@ -9,7 +9,7 @@ namespace System.Reflection.Metadata /// /// Represents the shape of an array type. /// - public struct ArrayShape + public readonly struct ArrayShape { /// /// Gets the number of dimensions in the array. diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Signatures/CustomAttributeNamedArgument.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Signatures/CustomAttributeNamedArgument.cs index b7c0a1adcb..6da00b91dd 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Signatures/CustomAttributeNamedArgument.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Signatures/CustomAttributeNamedArgument.cs @@ -1,10 +1,10 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. namespace System.Reflection.Metadata { - public struct CustomAttributeNamedArgument + public readonly struct CustomAttributeNamedArgument { public string Name { get; } public CustomAttributeNamedArgumentKind Kind { get; } diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Signatures/CustomAttributeTypedArgument.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Signatures/CustomAttributeTypedArgument.cs index a5eb4ecbcf..f5adb776f5 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Signatures/CustomAttributeTypedArgument.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Signatures/CustomAttributeTypedArgument.cs @@ -1,10 +1,10 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. namespace System.Reflection.Metadata { - public struct CustomAttributeTypedArgument + public readonly struct CustomAttributeTypedArgument { public TType Type { get; } public object Value { get; } diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Signatures/CustomAttributeValue.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Signatures/CustomAttributeValue.cs index 8206752bcf..90cddae77d 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Signatures/CustomAttributeValue.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Signatures/CustomAttributeValue.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. @@ -6,7 +6,7 @@ using System.Collections.Immutable; namespace System.Reflection.Metadata { - public struct CustomAttributeValue + public readonly struct CustomAttributeValue { public ImmutableArray> FixedArguments { get; } public ImmutableArray> NamedArguments { get; } diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Signatures/MethodSignature.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Signatures/MethodSignature.cs index ae253db3a4..85498a1a11 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Signatures/MethodSignature.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/Signatures/MethodSignature.cs @@ -11,7 +11,7 @@ namespace System.Reflection.Metadata /// Represents a method (definition, reference, or standalone) or property signature. /// In the case of properties, the signature matches that of a getter with a distinguishing . /// - public struct MethodSignature + public readonly struct MethodSignature { /// /// Represents the information in the leading byte of the signature (kind, calling convention, flags). diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/AssemblyDefinition.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/AssemblyDefinition.cs index 3558a0c62b..a89218e922 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/AssemblyDefinition.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/AssemblyDefinition.cs @@ -6,7 +6,7 @@ using System.Diagnostics; namespace System.Reflection.Metadata { - public partial struct AssemblyDefinition + public readonly partial struct AssemblyDefinition { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/AssemblyDefinition.netstandard.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/AssemblyDefinition.netstandard.cs index 9191f5f6d2..38d5ab7f75 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/AssemblyDefinition.netstandard.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/AssemblyDefinition.netstandard.cs @@ -1,10 +1,10 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. namespace System.Reflection.Metadata { - public partial struct AssemblyDefinition + public readonly partial struct AssemblyDefinition { public AssemblyName GetAssemblyName() { diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/AssemblyFile.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/AssemblyFile.cs index 6779b5f5cc..8a50b24500 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/AssemblyFile.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/AssemblyFile.cs @@ -6,7 +6,7 @@ using System.Diagnostics; namespace System.Reflection.Metadata { - public struct AssemblyFile + public readonly struct AssemblyFile { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/AssemblyReference.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/AssemblyReference.cs index 9a435c5fd4..bb4c63a537 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/AssemblyReference.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/AssemblyReference.cs @@ -7,7 +7,7 @@ using System.Reflection.Metadata.Ecma335; namespace System.Reflection.Metadata { - public partial struct AssemblyReference + public readonly partial struct AssemblyReference { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/AssemblyReference.netstandard.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/AssemblyReference.netstandard.cs index 5b0d1a92d9..2d6e7d4908 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/AssemblyReference.netstandard.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/AssemblyReference.netstandard.cs @@ -1,10 +1,10 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. namespace System.Reflection.Metadata { - public partial struct AssemblyReference + public readonly partial struct AssemblyReference { public AssemblyName GetAssemblyName() { diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/Constant.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/Constant.cs index 3c7dd4d757..e9638b649a 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/Constant.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/Constant.cs @@ -6,7 +6,7 @@ using System.Diagnostics; namespace System.Reflection.Metadata { - public struct Constant + public readonly struct Constant { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/CustomAttribute.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/CustomAttribute.cs index 0a5dc5a305..17c74ad665 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/CustomAttribute.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/CustomAttribute.cs @@ -7,7 +7,7 @@ using System.Reflection.Metadata.Ecma335; namespace System.Reflection.Metadata { - public struct CustomAttribute + public readonly struct CustomAttribute { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/DeclarativeSecurityAttribute.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/DeclarativeSecurityAttribute.cs index d778b392de..fcb370e033 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/DeclarativeSecurityAttribute.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/DeclarativeSecurityAttribute.cs @@ -7,7 +7,7 @@ using System.Reflection.Metadata.Ecma335; namespace System.Reflection.Metadata { - public struct DeclarativeSecurityAttribute + public readonly struct DeclarativeSecurityAttribute { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/EventDefinition.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/EventDefinition.cs index a5f5dcfa93..647c636d45 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/EventDefinition.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/EventDefinition.cs @@ -8,7 +8,7 @@ using System.Reflection.Metadata.Ecma335; namespace System.Reflection.Metadata { - public struct EventDefinition + public readonly struct EventDefinition { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/ExportedType.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/ExportedType.cs index bcf0164f2c..c601ea2e6a 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/ExportedType.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/ExportedType.cs @@ -6,7 +6,7 @@ using System.Diagnostics; namespace System.Reflection.Metadata { - public struct ExportedType + public readonly struct ExportedType { internal readonly MetadataReader reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/FieldDefinition.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/FieldDefinition.cs index 808f7991ab..48c67a2cbd 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/FieldDefinition.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/FieldDefinition.cs @@ -7,7 +7,7 @@ using System.Reflection.Metadata.Ecma335; namespace System.Reflection.Metadata { - public struct FieldDefinition + public readonly struct FieldDefinition { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/GenericParameter.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/GenericParameter.cs index 5c4aae06d6..d5951d4d73 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/GenericParameter.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/GenericParameter.cs @@ -6,7 +6,7 @@ using System.Diagnostics; namespace System.Reflection.Metadata { - public struct GenericParameter + public readonly struct GenericParameter { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/GenericParameterConstraint.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/GenericParameterConstraint.cs index 4518fa69c5..44082b6656 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/GenericParameterConstraint.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/GenericParameterConstraint.cs @@ -6,7 +6,7 @@ using System.Diagnostics; namespace System.Reflection.Metadata { - public struct GenericParameterConstraint + public readonly struct GenericParameterConstraint { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/HandleCollections.TypeSystem.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/HandleCollections.TypeSystem.cs index 51b5c1fc9c..8830a29dd1 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/HandleCollections.TypeSystem.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/HandleCollections.TypeSystem.cs @@ -13,7 +13,7 @@ namespace System.Reflection.Metadata /// /// Represents generic type parameters of a method or type. /// - public struct GenericParameterHandleCollection : IReadOnlyList + public readonly struct GenericParameterHandleCollection : IReadOnlyList { private readonly int _firstRowId; private readonly ushort _count; @@ -121,7 +121,7 @@ namespace System.Reflection.Metadata /// /// Represents constraints of a generic type parameter. /// - public struct GenericParameterConstraintHandleCollection : IReadOnlyList + public readonly struct GenericParameterConstraintHandleCollection : IReadOnlyList { private readonly int _firstRowId; private readonly ushort _count; @@ -226,7 +226,7 @@ namespace System.Reflection.Metadata } } - public struct CustomAttributeHandleCollection : IReadOnlyCollection + public readonly struct CustomAttributeHandleCollection : IReadOnlyCollection { private readonly MetadataReader _reader; @@ -346,7 +346,7 @@ namespace System.Reflection.Metadata } } - public struct DeclarativeSecurityAttributeHandleCollection : IReadOnlyCollection + public readonly struct DeclarativeSecurityAttributeHandleCollection : IReadOnlyCollection { private readonly MetadataReader _reader; @@ -453,7 +453,7 @@ namespace System.Reflection.Metadata } } - public struct MethodDefinitionHandleCollection : IReadOnlyCollection + public readonly struct MethodDefinitionHandleCollection : IReadOnlyCollection { private readonly MetadataReader _reader; @@ -573,7 +573,7 @@ namespace System.Reflection.Metadata } } - public struct FieldDefinitionHandleCollection : IReadOnlyCollection + public readonly struct FieldDefinitionHandleCollection : IReadOnlyCollection { private readonly MetadataReader _reader; @@ -693,7 +693,7 @@ namespace System.Reflection.Metadata } } - public struct PropertyDefinitionHandleCollection : IReadOnlyCollection + public readonly struct PropertyDefinitionHandleCollection : IReadOnlyCollection { private readonly MetadataReader _reader; @@ -813,7 +813,7 @@ namespace System.Reflection.Metadata } } - public struct EventDefinitionHandleCollection : IReadOnlyCollection + public readonly struct EventDefinitionHandleCollection : IReadOnlyCollection { private readonly MetadataReader _reader; @@ -933,7 +933,7 @@ namespace System.Reflection.Metadata } } - public struct MethodImplementationHandleCollection : IReadOnlyCollection + public readonly struct MethodImplementationHandleCollection : IReadOnlyCollection { private readonly int _firstRowId; private readonly int _lastRowId; @@ -1036,7 +1036,7 @@ namespace System.Reflection.Metadata /// /// Collection of parameters of a specified method. /// - public struct ParameterHandleCollection : IReadOnlyCollection + public readonly struct ParameterHandleCollection : IReadOnlyCollection { private readonly MetadataReader _reader; @@ -1148,7 +1148,7 @@ namespace System.Reflection.Metadata } } - public struct InterfaceImplementationHandleCollection : IReadOnlyCollection + public readonly struct InterfaceImplementationHandleCollection : IReadOnlyCollection { private readonly MetadataReader _reader; @@ -1249,7 +1249,7 @@ namespace System.Reflection.Metadata /// /// Represents a collection of . /// - public struct TypeDefinitionHandleCollection : IReadOnlyCollection + public readonly struct TypeDefinitionHandleCollection : IReadOnlyCollection { private readonly int _lastRowId; @@ -1339,7 +1339,7 @@ namespace System.Reflection.Metadata /// /// Represents a collection of . /// - public struct TypeReferenceHandleCollection : IReadOnlyCollection + public readonly struct TypeReferenceHandleCollection : IReadOnlyCollection { private readonly int _lastRowId; @@ -1429,7 +1429,7 @@ namespace System.Reflection.Metadata /// /// Represents a collection of . /// - public struct ExportedTypeHandleCollection : IReadOnlyCollection + public readonly struct ExportedTypeHandleCollection : IReadOnlyCollection { private readonly int _lastRowId; @@ -1519,7 +1519,7 @@ namespace System.Reflection.Metadata /// /// Represents a collection of . /// - public struct MemberReferenceHandleCollection : IReadOnlyCollection + public readonly struct MemberReferenceHandleCollection : IReadOnlyCollection { private readonly int _lastRowId; @@ -1606,7 +1606,7 @@ namespace System.Reflection.Metadata } } - public struct PropertyAccessors + public readonly struct PropertyAccessors { // Workaround: JIT doesn't generate good code for nested structures, so use uints. @@ -1626,7 +1626,7 @@ namespace System.Reflection.Metadata } } - public struct EventAccessors + public readonly struct EventAccessors { // Workaround: JIT doesn't generate good code for nested structures, so use uints. @@ -1652,7 +1652,7 @@ namespace System.Reflection.Metadata /// /// Collection of assembly references. /// - public struct AssemblyReferenceHandleCollection : IReadOnlyCollection + public readonly struct AssemblyReferenceHandleCollection : IReadOnlyCollection { private readonly MetadataReader _reader; @@ -1763,7 +1763,7 @@ namespace System.Reflection.Metadata /// /// Represents a collection of . /// - public struct ManifestResourceHandleCollection : IReadOnlyCollection + public readonly struct ManifestResourceHandleCollection : IReadOnlyCollection { private readonly int _lastRowId; @@ -1853,7 +1853,7 @@ namespace System.Reflection.Metadata /// /// Represents a collection of . /// - public struct AssemblyFileHandleCollection : IReadOnlyCollection + public readonly struct AssemblyFileHandleCollection : IReadOnlyCollection { private readonly int _lastRowId; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/Handles.TypeSystem.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/Handles.TypeSystem.cs index 8f8a5259c0..0b3af4893d 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/Handles.TypeSystem.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/Handles.TypeSystem.cs @@ -7,7 +7,7 @@ using System.Reflection.Metadata.Ecma335; namespace System.Reflection.Metadata { - public struct ModuleDefinitionHandle : IEquatable + public readonly struct ModuleDefinitionHandle : IEquatable { private const uint tokenType = TokenTypeIds.Module; private const byte tokenTypeSmall = (byte)HandleType.Module; @@ -90,7 +90,7 @@ namespace System.Reflection.Metadata } } - public struct AssemblyDefinitionHandle : IEquatable + public readonly struct AssemblyDefinitionHandle : IEquatable { private const uint tokenType = TokenTypeIds.Assembly; private const byte tokenTypeSmall = (byte)HandleType.Assembly; @@ -173,7 +173,7 @@ namespace System.Reflection.Metadata } } - public struct InterfaceImplementationHandle : IEquatable + public readonly struct InterfaceImplementationHandle : IEquatable { private const uint tokenType = TokenTypeIds.InterfaceImpl; private const byte tokenTypeSmall = (byte)HandleType.InterfaceImpl; @@ -256,7 +256,7 @@ namespace System.Reflection.Metadata } } - public struct MethodDefinitionHandle : IEquatable + public readonly struct MethodDefinitionHandle : IEquatable { private const uint tokenType = TokenTypeIds.MethodDef; private const byte tokenTypeSmall = (byte)HandleType.MethodDef; @@ -351,7 +351,7 @@ namespace System.Reflection.Metadata } } - public struct MethodImplementationHandle : IEquatable + public readonly struct MethodImplementationHandle : IEquatable { private const uint tokenType = TokenTypeIds.MethodImpl; private const byte tokenTypeSmall = (byte)HandleType.MethodImpl; @@ -434,7 +434,7 @@ namespace System.Reflection.Metadata } } - public struct MethodSpecificationHandle : IEquatable + public readonly struct MethodSpecificationHandle : IEquatable { private const uint tokenType = TokenTypeIds.MethodSpec; private const byte tokenTypeSmall = (byte)HandleType.MethodSpec; @@ -517,7 +517,7 @@ namespace System.Reflection.Metadata } } - public struct TypeDefinitionHandle : IEquatable + public readonly struct TypeDefinitionHandle : IEquatable { private const uint tokenType = TokenTypeIds.TypeDef; private const byte tokenTypeSmall = (byte)HandleType.TypeDef; @@ -600,7 +600,7 @@ namespace System.Reflection.Metadata } } - public struct ExportedTypeHandle : IEquatable + public readonly struct ExportedTypeHandle : IEquatable { private const uint tokenType = TokenTypeIds.ExportedType; private const byte tokenTypeSmall = (byte)HandleType.ExportedType; @@ -683,7 +683,7 @@ namespace System.Reflection.Metadata } } - public struct TypeReferenceHandle : IEquatable + public readonly struct TypeReferenceHandle : IEquatable { private const uint tokenType = TokenTypeIds.TypeRef; private const byte tokenTypeSmall = (byte)HandleType.TypeRef; @@ -766,7 +766,7 @@ namespace System.Reflection.Metadata } } - public struct TypeSpecificationHandle : IEquatable + public readonly struct TypeSpecificationHandle : IEquatable { private const uint tokenType = TokenTypeIds.TypeSpec; private const byte tokenTypeSmall = (byte)HandleType.TypeSpec; @@ -849,7 +849,7 @@ namespace System.Reflection.Metadata } } - public struct MemberReferenceHandle : IEquatable + public readonly struct MemberReferenceHandle : IEquatable { private const uint tokenType = TokenTypeIds.MemberRef; private const byte tokenTypeSmall = (byte)HandleType.MemberRef; @@ -932,7 +932,7 @@ namespace System.Reflection.Metadata } } - public struct FieldDefinitionHandle : IEquatable + public readonly struct FieldDefinitionHandle : IEquatable { private const uint tokenType = TokenTypeIds.FieldDef; private const byte tokenTypeSmall = (byte)HandleType.FieldDef; @@ -1015,7 +1015,7 @@ namespace System.Reflection.Metadata } } - public struct EventDefinitionHandle : IEquatable + public readonly struct EventDefinitionHandle : IEquatable { private const uint tokenType = TokenTypeIds.Event; private const byte tokenTypeSmall = (byte)HandleType.Event; @@ -1098,7 +1098,7 @@ namespace System.Reflection.Metadata } } - public struct PropertyDefinitionHandle : IEquatable + public readonly struct PropertyDefinitionHandle : IEquatable { private const uint tokenType = TokenTypeIds.Property; private const byte tokenTypeSmall = (byte)HandleType.Property; @@ -1181,7 +1181,7 @@ namespace System.Reflection.Metadata } } - public struct StandaloneSignatureHandle : IEquatable + public readonly struct StandaloneSignatureHandle : IEquatable { private const uint tokenType = TokenTypeIds.Signature; private const byte tokenTypeSmall = (byte)HandleType.Signature; @@ -1264,7 +1264,7 @@ namespace System.Reflection.Metadata } } - public struct ParameterHandle : IEquatable + public readonly struct ParameterHandle : IEquatable { private const uint tokenType = TokenTypeIds.ParamDef; private const byte tokenTypeSmall = (byte)HandleType.ParamDef; @@ -1347,7 +1347,7 @@ namespace System.Reflection.Metadata } } - public struct GenericParameterHandle : IEquatable + public readonly struct GenericParameterHandle : IEquatable { private const uint tokenType = TokenTypeIds.GenericParam; private const byte tokenTypeSmall = (byte)HandleType.GenericParam; @@ -1430,7 +1430,7 @@ namespace System.Reflection.Metadata } } - public struct GenericParameterConstraintHandle : IEquatable + public readonly struct GenericParameterConstraintHandle : IEquatable { private const uint tokenType = TokenTypeIds.GenericParamConstraint; private const byte tokenTypeSmall = (byte)HandleType.GenericParamConstraint; @@ -1513,7 +1513,7 @@ namespace System.Reflection.Metadata } } - public struct ModuleReferenceHandle : IEquatable + public readonly struct ModuleReferenceHandle : IEquatable { private const uint tokenType = TokenTypeIds.ModuleRef; private const byte tokenTypeSmall = (byte)HandleType.ModuleRef; @@ -1596,7 +1596,7 @@ namespace System.Reflection.Metadata } } - public struct AssemblyReferenceHandle : IEquatable + public readonly struct AssemblyReferenceHandle : IEquatable { private const uint tokenType = TokenTypeIds.AssemblyRef; private const byte tokenTypeSmall = (byte)HandleType.AssemblyRef; @@ -1714,7 +1714,7 @@ namespace System.Reflection.Metadata } } - public struct CustomAttributeHandle : IEquatable + public readonly struct CustomAttributeHandle : IEquatable { private const uint tokenType = TokenTypeIds.CustomAttribute; private const byte tokenTypeSmall = (byte)HandleType.CustomAttribute; @@ -1797,7 +1797,7 @@ namespace System.Reflection.Metadata } } - public struct DeclarativeSecurityAttributeHandle : IEquatable + public readonly struct DeclarativeSecurityAttributeHandle : IEquatable { private const uint tokenType = TokenTypeIds.DeclSecurity; private const byte tokenTypeSmall = (byte)HandleType.DeclSecurity; @@ -1880,7 +1880,7 @@ namespace System.Reflection.Metadata } } - public struct ConstantHandle : IEquatable + public readonly struct ConstantHandle : IEquatable { private const uint tokenType = TokenTypeIds.Constant; private const byte tokenTypeSmall = (byte)HandleType.Constant; @@ -1963,7 +1963,7 @@ namespace System.Reflection.Metadata } } - public struct ManifestResourceHandle : IEquatable + public readonly struct ManifestResourceHandle : IEquatable { private const uint tokenType = TokenTypeIds.ManifestResource; private const byte tokenTypeSmall = (byte)HandleType.ManifestResource; @@ -2046,7 +2046,7 @@ namespace System.Reflection.Metadata } } - public struct AssemblyFileHandle : IEquatable + public readonly struct AssemblyFileHandle : IEquatable { private const uint tokenType = TokenTypeIds.File; private const byte tokenTypeSmall = (byte)HandleType.File; @@ -2135,7 +2135,7 @@ namespace System.Reflection.Metadata /// /// The handle is 32-bit wide. /// - public struct UserStringHandle : IEquatable + public readonly struct UserStringHandle : IEquatable { // bits: // 31: 0 @@ -2207,7 +2207,7 @@ namespace System.Reflection.Metadata } // #String heap handle - public struct StringHandle : IEquatable + public readonly struct StringHandle : IEquatable { // bits: // 31: IsVirtual @@ -2430,7 +2430,7 @@ namespace System.Reflection.Metadata /// /// A handle that represents a namespace definition. /// - public struct NamespaceDefinitionHandle : IEquatable + public readonly struct NamespaceDefinitionHandle : IEquatable { // Non-virtual (namespace having at least one type or forwarder of its own) // heap offset is to the null-terminated full name of the namespace in the @@ -2546,7 +2546,7 @@ namespace System.Reflection.Metadata } // #Blob heap handle - public struct BlobHandle : IEquatable + public readonly struct BlobHandle : IEquatable { // bits: // 31: IsVirtual @@ -2677,7 +2677,7 @@ namespace System.Reflection.Metadata } // #Guid heap handle - public struct GuidHandle : IEquatable + public readonly struct GuidHandle : IEquatable { // The Guid heap is an array of GUIDs, each 16 bytes wide. // Its first element is numbered 1, its second 2, and so on. diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/InterfaceImplementation.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/InterfaceImplementation.cs index 92be423980..e65318a094 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/InterfaceImplementation.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/InterfaceImplementation.cs @@ -6,7 +6,7 @@ using System.Diagnostics; namespace System.Reflection.Metadata { - public struct InterfaceImplementation + public readonly struct InterfaceImplementation { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/ManifestResource.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/ManifestResource.cs index d13e6047c6..07797cfbcd 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/ManifestResource.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/ManifestResource.cs @@ -6,7 +6,7 @@ using System.Diagnostics; namespace System.Reflection.Metadata { - public struct ManifestResource + public readonly struct ManifestResource { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/MemberReference.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/MemberReference.cs index 23df36b9a5..c544852078 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/MemberReference.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/MemberReference.cs @@ -7,7 +7,7 @@ using System.Reflection.Metadata.Ecma335; namespace System.Reflection.Metadata { - public struct MemberReference + public readonly struct MemberReference { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/MethodDefinition.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/MethodDefinition.cs index 779f60c9e7..16a730f513 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/MethodDefinition.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/MethodDefinition.cs @@ -7,7 +7,7 @@ using System.Reflection.Metadata.Ecma335; namespace System.Reflection.Metadata { - public struct MethodDefinition + public readonly struct MethodDefinition { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/MethodImplementation.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/MethodImplementation.cs index 8da31783ae..bfbc817da2 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/MethodImplementation.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/MethodImplementation.cs @@ -6,7 +6,7 @@ using System.Diagnostics; namespace System.Reflection.Metadata { - public struct MethodImplementation + public readonly struct MethodImplementation { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/MethodImport.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/MethodImport.cs index 8cfc18791e..44f8e83d67 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/MethodImport.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/MethodImport.cs @@ -4,7 +4,7 @@ namespace System.Reflection.Metadata { - public struct MethodImport + public readonly struct MethodImport { private readonly MethodImportAttributes _attributes; private readonly StringHandle _name; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/MethodSpecification.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/MethodSpecification.cs index 612894fbd6..286eec7bc9 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/MethodSpecification.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/MethodSpecification.cs @@ -7,7 +7,7 @@ using System.Diagnostics; namespace System.Reflection.Metadata { - public struct MethodSpecification + public readonly struct MethodSpecification { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/ModuleDefinition.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/ModuleDefinition.cs index 67756922bd..44a7236963 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/ModuleDefinition.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/ModuleDefinition.cs @@ -6,7 +6,7 @@ using System.Diagnostics; namespace System.Reflection.Metadata { - public struct ModuleDefinition + public readonly struct ModuleDefinition { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/ModuleReference.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/ModuleReference.cs index 273da0d21b..d7a5b18fce 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/ModuleReference.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/ModuleReference.cs @@ -6,7 +6,7 @@ using System.Diagnostics; namespace System.Reflection.Metadata { - public struct ModuleReference + public readonly struct ModuleReference { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/Parameter.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/Parameter.cs index 085bab79a6..b1a407dfc5 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/Parameter.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/Parameter.cs @@ -6,7 +6,7 @@ using System.Diagnostics; namespace System.Reflection.Metadata { - public struct Parameter + public readonly struct Parameter { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/PropertyDefinition.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/PropertyDefinition.cs index 69cabb48ce..bf70a1be8d 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/PropertyDefinition.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/PropertyDefinition.cs @@ -8,7 +8,7 @@ using System.Reflection.Metadata.Ecma335; namespace System.Reflection.Metadata { - public struct PropertyDefinition + public readonly struct PropertyDefinition { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/StandaloneSignature.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/StandaloneSignature.cs index ece8da32ec..32e411bc23 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/StandaloneSignature.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/StandaloneSignature.cs @@ -8,7 +8,7 @@ using System.Reflection.Metadata.Ecma335; namespace System.Reflection.Metadata { - public struct StandaloneSignature + public readonly struct StandaloneSignature { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/TypeDefinition.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/TypeDefinition.cs index 92fedeafd7..7d8d7f6a26 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/TypeDefinition.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/TypeDefinition.cs @@ -8,7 +8,7 @@ using System.Reflection.Metadata.Ecma335; namespace System.Reflection.Metadata { - public struct TypeDefinition + public readonly struct TypeDefinition { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/TypeLayout.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/TypeLayout.cs index 239b73d7d6..3f9bdb6fac 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/TypeLayout.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/TypeLayout.cs @@ -4,7 +4,7 @@ namespace System.Reflection.Metadata { - public struct TypeLayout + public readonly struct TypeLayout { private readonly int _size; private readonly int _packingSize; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/TypeReference.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/TypeReference.cs index 0bc129f92b..b0023aedd6 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/TypeReference.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/TypeReference.cs @@ -7,7 +7,7 @@ using System.Reflection.Metadata.Ecma335; namespace System.Reflection.Metadata { - public struct TypeReference + public readonly struct TypeReference { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/TypeSpecification.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/TypeSpecification.cs index 79695d2e35..1aa9bd2707 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/TypeSpecification.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeSystem/TypeSpecification.cs @@ -7,7 +7,7 @@ using System.Reflection.Metadata.Ecma335; namespace System.Reflection.Metadata { - public struct TypeSpecification + public readonly struct TypeSpecification { private readonly MetadataReader _reader; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/DebugDirectory/CodeViewDebugDirectoryData.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/DebugDirectory/CodeViewDebugDirectoryData.cs index 48539c000b..b70e6be96c 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/DebugDirectory/CodeViewDebugDirectoryData.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/DebugDirectory/CodeViewDebugDirectoryData.cs @@ -6,7 +6,7 @@ using System.Diagnostics; namespace System.Reflection.PortableExecutable { - public struct CodeViewDebugDirectoryData + public readonly struct CodeViewDebugDirectoryData { /// /// GUID (Globally Unique Identifier) of the associated PDB. diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/DebugDirectory/DebugDirectoryEntry.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/DebugDirectory/DebugDirectoryEntry.cs index 7e5166c747..d8d90d4bf8 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/DebugDirectory/DebugDirectoryEntry.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/DebugDirectory/DebugDirectoryEntry.cs @@ -9,7 +9,7 @@ namespace System.Reflection.PortableExecutable /// /// Identifies the location, size and format of a block of debug information. /// - public struct DebugDirectoryEntry + public readonly struct DebugDirectoryEntry { internal const int Size = sizeof(uint) + // Characteristics diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/DirectoryEntry.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/DirectoryEntry.cs index cd3e926c0a..3cf0b9bff6 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/DirectoryEntry.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/DirectoryEntry.cs @@ -4,7 +4,7 @@ namespace System.Reflection.PortableExecutable { - public struct DirectoryEntry + public readonly struct DirectoryEntry { public readonly int RelativeVirtualAddress; public readonly int Size; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/Machine.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/Machine.cs index a8a057bab7..01127a6eeb 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/Machine.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/Machine.cs @@ -125,5 +125,10 @@ namespace System.Reflection.PortableExecutable /// M32R little-endian /// M32R = 0x9041, + + /// + /// ARM64 + /// + Arm64 = 0xAA64, } } diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/ManagedPEBuilder.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/ManagedPEBuilder.cs index d0eb31ca0f..3600b599e8 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/ManagedPEBuilder.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/ManagedPEBuilder.cs @@ -221,7 +221,7 @@ namespace System.Reflection.PortableExecutable builder.WriteUInt32((((uint)entryPointAddress + 2) / 0x1000) * 0x1000); builder.WriteUInt32((machine == Machine.IA64) ? 14u : 12u); uint offsetWithinPage = ((uint)entryPointAddress + 2) % 0x1000; - uint relocType = (machine == Machine.Amd64 || machine == Machine.IA64) ? 10u : 3u; + uint relocType = (machine == Machine.Amd64 || machine == Machine.IA64 || machine == Machine.Arm64) ? 10u : 3u; ushort s = (ushort)((relocType << 12) | offsetWithinPage); builder.WriteUInt16(s); if (machine == Machine.IA64) diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/ManagedTextSection.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/ManagedTextSection.cs index f5ff1afbb9..11163138ca 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/ManagedTextSection.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/ManagedTextSection.cs @@ -91,7 +91,7 @@ namespace System.Reflection.PortableExecutable /// If set, the module contains instructions that assume a 64 bit instruction set. For example it may depend on an address being 64 bits. /// This may be true even if the module contains only IL instructions because of PlatformInvoke and COM interop. /// - internal bool Requires64bits => Machine == Machine.Amd64 || Machine == Machine.IA64; + internal bool Requires64bits => Machine == Machine.Amd64 || Machine == Machine.IA64 || Machine == Machine.Arm64; public bool Is32Bit => !Requires64bits; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEBinaryReader.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEBinaryReader.cs index 7a68afe19e..b0c64d9e41 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEBinaryReader.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEBinaryReader.cs @@ -18,7 +18,7 @@ namespace System.Reflection.PortableExecutable /// /// Only methods that are needed to read PE headers are implemented. /// - internal struct PEBinaryReader + internal readonly struct PEBinaryReader { private readonly long _startOffset; private readonly long _maxOffset; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEBuilder.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEBuilder.cs index 8dc287dde0..9ae1214ad7 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEBuilder.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEBuilder.cs @@ -19,7 +19,7 @@ namespace System.Reflection.PortableExecutable private readonly Lazy> _lazySections; private Blob _lazyChecksum; - protected struct Section + protected readonly struct Section { public readonly string Name; public readonly SectionCharacteristics Characteristics; @@ -36,7 +36,7 @@ namespace System.Reflection.PortableExecutable } } - private struct SerializedSection + private readonly struct SerializedSection { public readonly BlobBuilder Builder; @@ -502,11 +502,12 @@ namespace System.Reflection.PortableExecutable throw new InvalidOperationException(SR.SignatureProviderReturnedInvalidSignature); } - uint checksum = CalculateChecksum(peImage, _lazyChecksum); - new BlobWriter(_lazyChecksum).WriteUInt32(checksum); - var writer = new BlobWriter(strongNameSignatureFixup); writer.WriteBytes(signature); + + // Calculate the checksum after the strong name signature has been written. + uint checksum = CalculateChecksum(peImage, _lazyChecksum); + new BlobWriter(_lazyChecksum).WriteUInt32(checksum); } // internal for testing diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEHeaderBuilder.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEHeaderBuilder.cs index 2adbfcd8e1..1dfdf0f018 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEHeaderBuilder.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEHeaderBuilder.cs @@ -106,7 +106,7 @@ namespace System.Reflection.PortableExecutable return new PEHeaderBuilder(imageCharacteristics: Characteristics.Dll); } - internal bool Is32Bit => Machine != Machine.Amd64 && Machine != Machine.IA64; + internal bool Is32Bit => Machine != Machine.Amd64 && Machine != Machine.IA64 && Machine != Machine.Arm64; internal int ComputeSizeOfPEHeaders(int sectionCount) => PEBuilder.DosHeaderSize + diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEMemoryBlock.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEMemoryBlock.cs index 5a456b048f..0962a84370 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEMemoryBlock.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEMemoryBlock.cs @@ -10,7 +10,7 @@ using System.Runtime.InteropServices; namespace System.Reflection.PortableExecutable { - public struct PEMemoryBlock + public readonly struct PEMemoryBlock { private readonly AbstractMemoryBlock _block; private readonly int _offset; diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/SectionHeader.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/SectionHeader.cs index bdb48eee1e..52f29d68f7 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/SectionHeader.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/SectionHeader.cs @@ -4,7 +4,7 @@ namespace System.Reflection.PortableExecutable { - public struct SectionHeader + public readonly struct SectionHeader { /// /// The name of the section. diff --git a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/SectionLocation.cs b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/SectionLocation.cs index a09e74b614..8a7d9004f8 100644 --- a/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/SectionLocation.cs +++ b/external/corefx/src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/SectionLocation.cs @@ -4,7 +4,7 @@ namespace System.Reflection.PortableExecutable { - public struct SectionLocation + public readonly struct SectionLocation { public int RelativeVirtualAddress { get; } public int PointerToRawData { get; } diff --git a/external/corefx/src/System.Reflection.Metadata/tests/PortableExecutable/PEBuilderTests.cs b/external/corefx/src/System.Reflection.Metadata/tests/PortableExecutable/PEBuilderTests.cs index 6873d3b529..38a3a1638b 100644 --- a/external/corefx/src/System.Reflection.Metadata/tests/PortableExecutable/PEBuilderTests.cs +++ b/external/corefx/src/System.Reflection.Metadata/tests/PortableExecutable/PEBuilderTests.cs @@ -17,7 +17,7 @@ namespace System.Reflection.PortableExecutable.Tests { #region Helpers - private void VerifyPE(Stream peStream, byte[] expectedSignature = null) + private void VerifyPE(Stream peStream, Machine machine, byte[] expectedSignature = null) { peStream.Position = 0; @@ -32,6 +32,11 @@ namespace System.Reflection.PortableExecutable.Tests Assert.Equal(s_contentId.Stamp, unchecked((uint)peReader.PEHeaders.CoffHeader.TimeDateStamp)); Assert.Equal(s_guid, mdReader.GetGuid(mdReader.GetModuleDefinition().Mvid)); + + if (machine == Machine.Unknown) // Unknown machine type translates into AnyCpu, which is marked as I386 in the PE file + Assert.Equal(Machine.I386, headers.CoffHeader.Machine); + else + Assert.Equal(machine, headers.CoffHeader.Machine); } } @@ -58,14 +63,19 @@ namespace System.Reflection.PortableExecutable.Tests BlobBuilder ilBuilder, MethodDefinitionHandle entryPointHandle, Blob mvidFixup = default(Blob), - byte[] privateKeyOpt = null) + byte[] privateKeyOpt = null, + bool publicSigned = false, + Machine machine = 0) { + var peHeaderBuilder = new PEHeaderBuilder(imageCharacteristics: entryPointHandle.IsNil ? Characteristics.Dll : Characteristics.ExecutableImage, + machine: machine); + var peBuilder = new ManagedPEBuilder( - entryPointHandle.IsNil ? PEHeaderBuilder.CreateLibraryHeader() : PEHeaderBuilder.CreateExecutableHeader(), + peHeaderBuilder, new MetadataRootBuilder(metadataBuilder), ilBuilder, entryPoint: entryPointHandle, - flags: CorFlags.ILOnly | (privateKeyOpt != null ? CorFlags.StrongNameSigned : 0), + flags: CorFlags.ILOnly | (privateKeyOpt != null || publicSigned ? CorFlags.StrongNameSigned : 0), deterministicIdProvider: content => s_contentId); var peBlob = new BlobBuilder(); @@ -85,6 +95,11 @@ namespace System.Reflection.PortableExecutable.Tests peBlob.WriteContentTo(peStream); } + public static IEnumerable AllMachineTypes() + { + return ((Machine[])Enum.GetValues(typeof(Machine))).Select(m => new object[]{(object)m}); + } + #endregion [Fact] @@ -100,17 +115,22 @@ namespace System.Reflection.PortableExecutable.Tests Assert.Throws(() => new ManagedPEBuilder(hdr, ms, il, strongNameSignatureSize: -1)); } - [Fact] - public void BasicValidation() + [Theory] // Do BasicValidation on all machine types listed in the Machine enum + [MemberData(nameof(AllMachineTypes))] + public void BasicValidation(Machine machine) { using (var peStream = new MemoryStream()) { var ilBuilder = new BlobBuilder(); var metadataBuilder = new MetadataBuilder(); var entryPoint = BasicValidationEmit(metadataBuilder, ilBuilder); - WritePEImage(peStream, metadataBuilder, ilBuilder, entryPoint); + WritePEImage(peStream, metadataBuilder, ilBuilder, entryPoint, publicSigned: true, machine: machine); - VerifyPE(peStream); + peStream.Position = 0; + var actualChecksum = new PEHeaders(peStream).PEHeader.CheckSum; + Assert.Equal(0U, actualChecksum); + + VerifyPE(peStream, machine); } } @@ -124,7 +144,15 @@ namespace System.Reflection.PortableExecutable.Tests var entryPoint = BasicValidationEmit(metadataBuilder, ilBuilder); WritePEImage(peStream, metadataBuilder, ilBuilder, entryPoint, privateKeyOpt: Misc.KeyPair); - VerifyPE(peStream, expectedSignature: new byte[] + // The expected checksum can be determined by saving the PE stream to a file, + // running "sn -R test.dll KeyPair.snk" and inspecting the resulting binary. + // The re-signed binary should be the same as the original one. + // See https://github.com/dotnet/corefx/issues/25829. + peStream.Position = 0; + var actualChecksum = new PEHeaders(peStream).PEHeader.CheckSum; + Assert.Equal(0x0000319cU, actualChecksum); + + VerifyPE(peStream, Machine.Unknown, expectedSignature: new byte[] { 0x58, 0xD4, 0xD7, 0x88, 0x3B, 0xF9, 0x19, 0x9F, 0x3A, 0x55, 0x8F, 0x1B, 0x88, 0xBE, 0xA8, 0x42, 0x09, 0x2B, 0xE3, 0xB4, 0xC7, 0x09, 0xD5, 0x96, 0x35, 0x50, 0x0F, 0x3C, 0x87, 0x95, 0x6A, 0x31, @@ -307,8 +335,9 @@ namespace System.Reflection.PortableExecutable.Tests return mainMethodDef; } - [Fact] - public void Complex() + [Theory] // Do BasicValidation on common machine types + [MemberData(nameof(AllMachineTypes))] + public void Complex(Machine machine) { using (var peStream = new MemoryStream()) { @@ -317,8 +346,8 @@ namespace System.Reflection.PortableExecutable.Tests Blob mvidFixup; var entryPoint = ComplexEmit(metadataBuilder, ilBuilder, out mvidFixup); - WritePEImage(peStream, metadataBuilder, ilBuilder, entryPoint, mvidFixup); - VerifyPE(peStream); + WritePEImage(peStream, metadataBuilder, ilBuilder, entryPoint, mvidFixup, machine: machine); + VerifyPE(peStream, machine); } } diff --git a/external/corefx/src/System.Reflection.Metadata/tests/PortableExecutable/PEHeadersTests.cs b/external/corefx/src/System.Reflection.Metadata/tests/PortableExecutable/PEHeadersTests.cs index 00813631ab..d09c324eec 100644 --- a/external/corefx/src/System.Reflection.Metadata/tests/PortableExecutable/PEHeadersTests.cs +++ b/external/corefx/src/System.Reflection.Metadata/tests/PortableExecutable/PEHeadersTests.cs @@ -24,6 +24,9 @@ namespace System.Reflection.PortableExecutable.Tests Assert.Equal(128 + 4 + 20 + 224 + 16, new PEHeaderBuilder(Machine.Amd64).ComputeSizeOfPEHeaders(0)); Assert.Equal(128 + 4 + 20 + 224 + 16 + 40 * 1, new PEHeaderBuilder(Machine.Amd64).ComputeSizeOfPEHeaders(1)); Assert.Equal(128 + 4 + 20 + 224 + 16 + 40 * 2, new PEHeaderBuilder(Machine.Amd64).ComputeSizeOfPEHeaders(2)); + Assert.Equal(128 + 4 + 20 + 224 + 16, new PEHeaderBuilder(Machine.Arm64).ComputeSizeOfPEHeaders(0)); + Assert.Equal(128 + 4 + 20 + 224 + 16 + 40 * 1, new PEHeaderBuilder(Machine.Arm64).ComputeSizeOfPEHeaders(1)); + Assert.Equal(128 + 4 + 20 + 224 + 16 + 40 * 2, new PEHeaderBuilder(Machine.Arm64).ComputeSizeOfPEHeaders(2)); } [Fact] diff --git a/external/corefx/src/System.Reflection.Metadata/tests/PortableExecutable/PEReaderTests.cs b/external/corefx/src/System.Reflection.Metadata/tests/PortableExecutable/PEReaderTests.cs index d385aadcce..af5d099afe 100644 --- a/external/corefx/src/System.Reflection.Metadata/tests/PortableExecutable/PEReaderTests.cs +++ b/external/corefx/src/System.Reflection.Metadata/tests/PortableExecutable/PEReaderTests.cs @@ -298,6 +298,7 @@ namespace System.Reflection.PortableExecutable.Tests } [Fact] + [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] public void TryOpenAssociatedPortablePdb_Args() { var peStream = new MemoryStream(PortablePdbs.DocumentsDll); @@ -313,6 +314,23 @@ namespace System.Reflection.PortableExecutable.Tests } } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + public void TryOpenAssociatedPortablePdb_Args_Core() + { + var peStream = new MemoryStream(PortablePdbs.DocumentsDll); + using (var reader = new PEReader(peStream)) + { + MetadataReaderProvider pdbProvider; + string pdbPath; + + Assert.False(reader.TryOpenAssociatedPortablePdb(@"b.dll", _ => null, out pdbProvider, out pdbPath)); + Assert.Throws(() => reader.TryOpenAssociatedPortablePdb(@"b.dll", null, out pdbProvider, out pdbPath)); + Assert.Throws(() => reader.TryOpenAssociatedPortablePdb(null, _ => null, out pdbProvider, out pdbPath)); + Assert.False(reader.TryOpenAssociatedPortablePdb("C:\\a\\\0\\b", _ => null, out pdbProvider, out pdbPath)); + } + } + [Fact] public void TryOpenAssociatedPortablePdb_CollocatedFile() { diff --git a/external/corefx/src/System.Reflection.Metadata/tests/System.Reflection.Metadata.Tests.csproj b/external/corefx/src/System.Reflection.Metadata/tests/System.Reflection.Metadata.Tests.csproj index 8850ce334e..d3cceecb20 100644 --- a/external/corefx/src/System.Reflection.Metadata/tests/System.Reflection.Metadata.Tests.csproj +++ b/external/corefx/src/System.Reflection.Metadata/tests/System.Reflection.Metadata.Tests.csproj @@ -78,6 +78,7 @@ + @@ -144,4 +145,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Reflection.Metadata/tests/Utilities/HashTests.cs b/external/corefx/src/System.Reflection.Metadata/tests/Utilities/HashTests.cs new file mode 100644 index 0000000000..741fef3984 --- /dev/null +++ b/external/corefx/src/System.Reflection.Metadata/tests/Utilities/HashTests.cs @@ -0,0 +1,43 @@ +// 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.Reflection.Internal; +using System.Collections.Immutable; +using Xunit; + +namespace System.Reflection.Metadata.Tests +{ + public class HashTests + { + [Fact] + public void GetFNVHashCodeByteTest() + { + Assert.Equal(-1088511923, Hash.GetFNVHashCode(new byte[] { 0xFF, 0xD1 })); + } + + [Fact] + public void GetFNVHashCodeImmutableByteTest() + { + Assert.Equal(-1088511923, Hash.GetFNVHashCode(ImmutableArray.Create((byte)0xFF, (byte)0xD1))); + } + + [Fact] + public void CombineIntInt() + { + Assert.Equal(536869063, Hash.Combine(13, 42)); + } + + [Fact] + public void CombineUIntInt() + { + Assert.Equal(536869063, Hash.Combine((uint)13, 42)); + } + + [Fact] + public void CombineBoolInt() + { + Assert.Equal(-1521134253, Hash.Combine(true, 42)); + } + } +} diff --git a/external/corefx/src/System.Reflection.Primitives/ref/System.Reflection.Primitives.cs b/external/corefx/src/System.Reflection.Primitives/ref/System.Reflection.Primitives.cs index 733a927a45..8133f9b7ba 100644 --- a/external/corefx/src/System.Reflection.Primitives/ref/System.Reflection.Primitives.cs +++ b/external/corefx/src/System.Reflection.Primitives/ref/System.Reflection.Primitives.cs @@ -31,9 +31,9 @@ namespace System.Reflection.Emit Return = 7, Throw = 8, } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct OpCode { + private object _dummy; public System.Reflection.Emit.FlowControl FlowControl { get { throw null; } } public string Name { get { throw null; } } public System.Reflection.Emit.OpCodeType OpCodeType { get { throw null; } } diff --git a/external/corefx/src/System.Reflection.Primitives/src/System/Reflection/Emit/Opcode.cs b/external/corefx/src/System.Reflection.Primitives/src/System/Reflection/Emit/Opcode.cs index b8eebb748a..539836dd5d 100644 --- a/external/corefx/src/System.Reflection.Primitives/src/System/Reflection/Emit/Opcode.cs +++ b/external/corefx/src/System.Reflection.Primitives/src/System/Reflection/Emit/Opcode.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Threading; -using System.Diagnostics.Contracts; namespace System.Reflection.Emit { @@ -157,7 +156,6 @@ namespace System.Reflection.Emit } } - [Pure] public override bool Equals(Object obj) { if (obj is OpCode) @@ -166,19 +164,16 @@ namespace System.Reflection.Emit return false; } - [Pure] public bool Equals(OpCode obj) { return obj.Value == Value; } - [Pure] public static bool operator ==(OpCode a, OpCode b) { return a.Equals(b); } - [Pure] public static bool operator !=(OpCode a, OpCode b) { return !(a == b); diff --git a/external/corefx/src/System.Reflection.TypeExtensions/tests/CoreCLR/System.Reflection.TypeExtensions.CoreCLR.Tests.csproj b/external/corefx/src/System.Reflection.TypeExtensions/tests/CoreCLR/System.Reflection.TypeExtensions.CoreCLR.Tests.csproj index cb7d587972..3998f258c9 100644 --- a/external/corefx/src/System.Reflection.TypeExtensions/tests/CoreCLR/System.Reflection.TypeExtensions.CoreCLR.Tests.csproj +++ b/external/corefx/src/System.Reflection.TypeExtensions/tests/CoreCLR/System.Reflection.TypeExtensions.CoreCLR.Tests.csproj @@ -4,7 +4,6 @@ {BED9F8D5-7420-404E-9EAD-D9148C16EAC1} - @@ -23,4 +22,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Reflection.TypeExtensions/tests/System.Reflection.TypeExtensions.Tests.csproj b/external/corefx/src/System.Reflection.TypeExtensions/tests/System.Reflection.TypeExtensions.Tests.csproj index 60fb209071..7096f32c60 100644 --- a/external/corefx/src/System.Reflection.TypeExtensions/tests/System.Reflection.TypeExtensions.Tests.csproj +++ b/external/corefx/src/System.Reflection.TypeExtensions/tests/System.Reflection.TypeExtensions.Tests.csproj @@ -4,7 +4,6 @@ {EC8FFAA7-CA1E-4631-A375-D54B1FC764F6} - @@ -26,4 +25,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_0_0_0_0.csproj b/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_0_0_0_0.csproj index bcb5ccf8c1..0bc970bf0f 100644 --- a/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_0_0_0_0.csproj +++ b/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_0_0_0_0.csproj @@ -5,7 +5,6 @@ {5B003EB4-DD06-4BC6-B2E9-A9F0E445CB86} 0.0.0.0 - diff --git a/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_0_0_0.csproj b/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_0_0_0.csproj index 581206e9db..a04666e023 100644 --- a/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_0_0_0.csproj +++ b/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_0_0_0.csproj @@ -5,7 +5,6 @@ {80696796-DE63-42CA-ACB0-A83E5AA7AE06} 1.0.0.0 - diff --git a/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_0_0.csproj b/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_0_0.csproj index 86783e98bf..ff16bb5b21 100644 --- a/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_0_0.csproj +++ b/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_0_0.csproj @@ -5,7 +5,6 @@ {E28C7916-1BCE-434A-B045-9F755A00A7C4} 1.1.0.0 - diff --git a/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_1_0.csproj b/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_1_0.csproj index 014ad0f796..8b6ac69ab8 100644 --- a/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_1_0.csproj +++ b/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_1_0.csproj @@ -5,7 +5,6 @@ {0AC47423-D050-46D4-8C4C-E2D44102FAB6} 1.1.1.0 - diff --git a/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_1_2.csproj b/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_1_2.csproj index add28faa6f..965e6cc30f 100644 --- a/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_1_2.csproj +++ b/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_1_2.csproj @@ -5,7 +5,6 @@ {652B7191-D7FE-4889-8E45-1AF7739C0EAD} 1.1.1.2 - diff --git a/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_1_3.csproj b/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_1_3.csproj index 7665416966..a891d4c5b3 100644 --- a/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_1_3.csproj +++ b/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_1_3.csproj @@ -5,7 +5,6 @@ {072D1E70-A0A4-44AB-92B8-2F6B772626CC} 1.1.1.3 - diff --git a/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_2_0.csproj b/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_2_0.csproj index f0d52a56ca..b2286d149c 100644 --- a/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_2_0.csproj +++ b/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_2_0.csproj @@ -5,7 +5,6 @@ {3E30213F-6E59-4BE8-BF4A-64D2AAED5B2E} 1.1.2.0 - diff --git a/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_3_0.csproj b/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_3_0.csproj index 0454ebc2d4..d09f87b7f1 100644 --- a/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_3_0.csproj +++ b/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_1_3_0.csproj @@ -5,7 +5,6 @@ {02758899-6A37-4FF1-B765-F1C38B40BC9C} 1.1.3.0 - diff --git a/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_2_0_0.csproj b/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_2_0_0.csproj index 0ef38b7a69..072ada04cc 100644 --- a/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_2_0_0.csproj +++ b/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_2_0_0.csproj @@ -5,7 +5,6 @@ {510CC907-F5AA-432D-AEEB-14A10AE3F811} 1.2.0.0 - diff --git a/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_3_0_0.csproj b/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_3_0_0.csproj index d4bdbd776a..a0cb962c83 100644 --- a/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_3_0_0.csproj +++ b/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_1_3_0_0.csproj @@ -5,7 +5,6 @@ {68AD3675-F57E-4FB3-9943-49E602678BCA} 1.3.0.0 - diff --git a/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_3_0_0_0.csproj b/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_3_0_0_0.csproj index f6702adc9a..1ce4577f4e 100644 --- a/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_3_0_0_0.csproj +++ b/external/corefx/src/System.Reflection/tests/AssemblyVersion/System.Reflection.Tests.Assembly_3_0_0_0.csproj @@ -5,7 +5,6 @@ {42E66302-6F46-47BE-936B-4264DFD6004F} 3.0.0.0 - diff --git a/external/corefx/src/System.Reflection/tests/CoreCLR/System.Reflection.CoreCLR.Tests.csproj b/external/corefx/src/System.Reflection/tests/CoreCLR/System.Reflection.CoreCLR.Tests.csproj index 5fecd0d9b5..7d4e5fec10 100644 --- a/external/corefx/src/System.Reflection/tests/CoreCLR/System.Reflection.CoreCLR.Tests.csproj +++ b/external/corefx/src/System.Reflection/tests/CoreCLR/System.Reflection.CoreCLR.Tests.csproj @@ -4,7 +4,6 @@ {C8049356-559D-4F34-AC17-56F3AE62C897} - diff --git a/external/corefx/src/System.Reflection/tests/System.Reflection.Tests.csproj b/external/corefx/src/System.Reflection/tests/System.Reflection.Tests.csproj index 11f327df82..93277f5d3a 100644 --- a/external/corefx/src/System.Reflection/tests/System.Reflection.Tests.csproj +++ b/external/corefx/src/System.Reflection/tests/System.Reflection.Tests.csproj @@ -4,7 +4,6 @@ {B027C72E-F04E-42E0-A7F7-993AEF8400D2} - @@ -51,4 +50,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Reflection/tests/TestExe/System.Reflection.TestExe.csproj b/external/corefx/src/System.Reflection/tests/TestExe/System.Reflection.TestExe.csproj index 3fc1f8ebbd..3639f4a5fd 100644 --- a/external/corefx/src/System.Reflection/tests/TestExe/System.Reflection.TestExe.csproj +++ b/external/corefx/src/System.Reflection/tests/TestExe/System.Reflection.TestExe.csproj @@ -5,7 +5,6 @@ {8C19B991-41E9-4B38-9602-E19375397F1D} Exe - diff --git a/external/corefx/src/System.Resources.Reader/src/System.Resources.Reader.csproj b/external/corefx/src/System.Resources.Reader/src/System.Resources.Reader.csproj index e975c91e2f..4e873d721b 100644 --- a/external/corefx/src/System.Resources.Reader/src/System.Resources.Reader.csproj +++ b/external/corefx/src/System.Resources.Reader/src/System.Resources.Reader.csproj @@ -8,7 +8,6 @@ {5C13260D-C55D-46AF-9DCB-1B015D48E2E4} true - diff --git a/external/corefx/src/System.Resources.Reader/tests/System.Resources.Reader.Tests.csproj b/external/corefx/src/System.Resources.Reader/tests/System.Resources.Reader.Tests.csproj index 8711032818..07ce647393 100644 --- a/external/corefx/src/System.Resources.Reader/tests/System.Resources.Reader.Tests.csproj +++ b/external/corefx/src/System.Resources.Reader/tests/System.Resources.Reader.Tests.csproj @@ -5,7 +5,6 @@ {8D7202E8-084A-4C20-AB93-3BF70D2E0651} - diff --git a/external/corefx/src/System.Resources.ResourceManager/tests/System.Resources.ResourceManager.Tests.csproj b/external/corefx/src/System.Resources.ResourceManager/tests/System.Resources.ResourceManager.Tests.csproj index 8a7dc9d48b..9a230f61de 100644 --- a/external/corefx/src/System.Resources.ResourceManager/tests/System.Resources.ResourceManager.Tests.csproj +++ b/external/corefx/src/System.Resources.ResourceManager/tests/System.Resources.ResourceManager.Tests.csproj @@ -5,7 +5,6 @@ {1D51A16C-B6D8-4E8F-98DE-21AD9A7062A1} System.Resources.Tests - @@ -40,4 +39,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Resources.Writer/src/System.Resources.Writer.csproj b/external/corefx/src/System.Resources.Writer/src/System.Resources.Writer.csproj index 21d53e4222..b03a8ef322 100644 --- a/external/corefx/src/System.Resources.Writer/src/System.Resources.Writer.csproj +++ b/external/corefx/src/System.Resources.Writer/src/System.Resources.Writer.csproj @@ -7,7 +7,6 @@ System.Resources {98CA9A7F-AD5C-418B-BC22-735AC2BE21A6} - @@ -23,7 +22,6 @@ - @@ -31,4 +29,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.Resources.Writer/src/System/Resources/ResourceWriter.cs b/external/corefx/src/System.Resources.Writer/src/System/Resources/ResourceWriter.cs index 226687e318..063436484d 100644 --- a/external/corefx/src/System.Resources.Writer/src/System/Resources/ResourceWriter.cs +++ b/external/corefx/src/System.Resources.Writer/src/System/Resources/ResourceWriter.cs @@ -23,7 +23,6 @@ using System.Globalization; using System.Runtime.Versioning; using System.Runtime.Serialization; using System.Diagnostics; -using System.Diagnostics.Contracts; namespace System.Resources { @@ -59,7 +58,7 @@ namespace System.Resources { if (fileName == null) throw new ArgumentNullException(nameof(fileName)); - Contract.EndContractBlock(); + _output = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); _resourceList = new SortedDictionary(FastResourceComparer.Default); _caseInsensitiveDups = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -71,7 +70,7 @@ namespace System.Resources throw new ArgumentNullException(nameof(stream)); if (!stream.CanWrite) throw new ArgumentException(SR.Argument_StreamNotWritable); - Contract.EndContractBlock(); + _output = stream; _resourceList = new SortedDictionary(FastResourceComparer.Default); _caseInsensitiveDups = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -84,7 +83,7 @@ namespace System.Resources { if (name == null) throw new ArgumentNullException(nameof(name)); - Contract.EndContractBlock(); + if (_resourceList == null) throw new InvalidOperationException(SR.InvalidOperation_ResourceWriterSaved); @@ -100,7 +99,7 @@ namespace System.Resources { if (name == null) throw new ArgumentNullException(nameof(name)); - Contract.EndContractBlock(); + if (_resourceList == null) throw new InvalidOperationException(SR.InvalidOperation_ResourceWriterSaved); @@ -125,7 +124,7 @@ namespace System.Resources { if (name == null) throw new ArgumentNullException(nameof(name)); - Contract.EndContractBlock(); + if (_resourceList == null) throw new InvalidOperationException(SR.InvalidOperation_ResourceWriterSaved); @@ -140,7 +139,7 @@ namespace System.Resources { if (name == null) throw new ArgumentNullException(nameof(name)); - Contract.EndContractBlock(); + if (_resourceList == null) throw new InvalidOperationException(SR.InvalidOperation_ResourceWriterSaved); @@ -174,7 +173,7 @@ namespace System.Resources { if (name == null) throw new ArgumentNullException(nameof(name)); - Contract.EndContractBlock(); + if (_resourceList == null) throw new InvalidOperationException(SR.InvalidOperation_ResourceWriterSaved); @@ -191,7 +190,7 @@ namespace System.Resources throw new ArgumentNullException(nameof(typeName)); if (serializedData == null) throw new ArgumentNullException(nameof(serializedData)); - Contract.EndContractBlock(); + if (_resourceList == null) throw new InvalidOperationException(SR.InvalidOperation_ResourceWriterSaved); @@ -525,7 +524,7 @@ namespace System.Resources private void WriteValue(ResourceTypeCode typeCode, object value, BinaryWriter writer) { - Contract.Requires(writer != null); + Debug.Assert(writer != null); switch (typeCode) { @@ -643,7 +642,7 @@ namespace System.Resources } default: - Contract.Assert(typeCode >= ResourceTypeCode.StartOfUserTypes, string.Format(CultureInfo.InvariantCulture, "ResourceReader: Unsupported ResourceTypeCode in .resources file! {0}", typeCode)); + Debug.Assert(typeCode >= ResourceTypeCode.StartOfUserTypes, string.Format(CultureInfo.InvariantCulture, "ResourceReader: Unsupported ResourceTypeCode in .resources file! {0}", typeCode)); throw new PlatformNotSupportedException(SR.NotSupported_BinarySerializedResources); } } diff --git a/external/corefx/src/System.Resources.Writer/src/System/Runtime/Versioning/MultitargetingHelpers.cs b/external/corefx/src/System.Resources.Writer/src/System/Runtime/Versioning/MultitargetingHelpers.cs index 2e6ef534f5..d9a4830c21 100644 --- a/external/corefx/src/System.Resources.Writer/src/System/Runtime/Versioning/MultitargetingHelpers.cs +++ b/external/corefx/src/System.Resources.Writer/src/System/Runtime/Versioning/MultitargetingHelpers.cs @@ -5,7 +5,6 @@ using System; using System.IO; using System.Text; -using System.Diagnostics.Contracts; namespace System.Runtime.Versioning { diff --git a/external/corefx/src/System.Resources.Writer/tests/System.Resources.Writer.Tests.csproj b/external/corefx/src/System.Resources.Writer/tests/System.Resources.Writer.Tests.csproj index 22dab774ab..c0a774ec23 100644 --- a/external/corefx/src/System.Resources.Writer/tests/System.Resources.Writer.Tests.csproj +++ b/external/corefx/src/System.Resources.Writer/tests/System.Resources.Writer.Tests.csproj @@ -5,7 +5,6 @@ {EFA745C2-3741-4D54-B2A1-1979E43354E4} - diff --git a/external/corefx/src/System.Runtime.Caching/System.Runtime.Caching.sln b/external/corefx/src/System.Runtime.Caching/System.Runtime.Caching.sln new file mode 100644 index 0000000000..88875dcdcf --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/System.Runtime.Caching.sln @@ -0,0 +1,90 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27108.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{F19CC87B-3230-42BF-8C1F-88F4F5EDE153}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Runtime.Caching", "ref\System.Runtime.Caching.csproj", "{3B7A97BD-F4DC-4FF9-AB6D-3B714E5D9280}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{9EC38C79-BC41-4675-AB3D-A509BDC14FE0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Runtime.Caching", "src\System.Runtime.Caching.csproj", "{A7B6FB6E-F484-42D7-8A5E-F7D0DCC03829}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{D4DA8C10-76FA-49D3-B4F2-BCB0C94330F6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Runtime.Caching.Tests", "tests\System.Runtime.Caching.Tests.csproj", "{397E49A7-EB26-4368-8F46-D78B98F4A971}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + netcoreapp-Windows_NT-Debug|Any CPU = netcoreapp-Windows_NT-Debug|Any CPU + netcoreapp-Windows_NT-Release|Any CPU = netcoreapp-Windows_NT-Release|Any CPU + netfx-Debug|Any CPU = netfx-Debug|Any CPU + netfx-Release|Any CPU = netfx-Release|Any CPU + netstandard-Debug|Any CPU = netstandard-Debug|Any CPU + netstandard-Release|Any CPU = netstandard-Release|Any CPU + netstandard-Windows_NT-Debug|Any CPU = netstandard-Windows_NT-Debug|Any CPU + netstandard-Windows_NT-Release|Any CPU = netstandard-Windows_NT-Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3B7A97BD-F4DC-4FF9-AB6D-3B714E5D9280}.netcoreapp-Windows_NT-Debug|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {3B7A97BD-F4DC-4FF9-AB6D-3B714E5D9280}.netcoreapp-Windows_NT-Debug|Any CPU.Build.0 = netstandard-Release|Any CPU + {3B7A97BD-F4DC-4FF9-AB6D-3B714E5D9280}.netcoreapp-Windows_NT-Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {3B7A97BD-F4DC-4FF9-AB6D-3B714E5D9280}.netcoreapp-Windows_NT-Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {3B7A97BD-F4DC-4FF9-AB6D-3B714E5D9280}.netfx-Debug|Any CPU.ActiveCfg = netfx-Debug|Any CPU + {3B7A97BD-F4DC-4FF9-AB6D-3B714E5D9280}.netfx-Debug|Any CPU.Build.0 = netfx-Debug|Any CPU + {3B7A97BD-F4DC-4FF9-AB6D-3B714E5D9280}.netfx-Release|Any CPU.ActiveCfg = netfx-Release|Any CPU + {3B7A97BD-F4DC-4FF9-AB6D-3B714E5D9280}.netfx-Release|Any CPU.Build.0 = netfx-Release|Any CPU + {3B7A97BD-F4DC-4FF9-AB6D-3B714E5D9280}.netstandard-Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {3B7A97BD-F4DC-4FF9-AB6D-3B714E5D9280}.netstandard-Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {3B7A97BD-F4DC-4FF9-AB6D-3B714E5D9280}.netstandard-Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {3B7A97BD-F4DC-4FF9-AB6D-3B714E5D9280}.netstandard-Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {3B7A97BD-F4DC-4FF9-AB6D-3B714E5D9280}.netstandard-Windows_NT-Debug|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {3B7A97BD-F4DC-4FF9-AB6D-3B714E5D9280}.netstandard-Windows_NT-Debug|Any CPU.Build.0 = netstandard-Release|Any CPU + {3B7A97BD-F4DC-4FF9-AB6D-3B714E5D9280}.netstandard-Windows_NT-Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {3B7A97BD-F4DC-4FF9-AB6D-3B714E5D9280}.netstandard-Windows_NT-Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {A7B6FB6E-F484-42D7-8A5E-F7D0DCC03829}.netcoreapp-Windows_NT-Debug|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {A7B6FB6E-F484-42D7-8A5E-F7D0DCC03829}.netcoreapp-Windows_NT-Debug|Any CPU.Build.0 = netstandard-Release|Any CPU + {A7B6FB6E-F484-42D7-8A5E-F7D0DCC03829}.netcoreapp-Windows_NT-Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {A7B6FB6E-F484-42D7-8A5E-F7D0DCC03829}.netcoreapp-Windows_NT-Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {A7B6FB6E-F484-42D7-8A5E-F7D0DCC03829}.netfx-Debug|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {A7B6FB6E-F484-42D7-8A5E-F7D0DCC03829}.netfx-Debug|Any CPU.Build.0 = netstandard-Release|Any CPU + {A7B6FB6E-F484-42D7-8A5E-F7D0DCC03829}.netfx-Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {A7B6FB6E-F484-42D7-8A5E-F7D0DCC03829}.netfx-Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {A7B6FB6E-F484-42D7-8A5E-F7D0DCC03829}.netstandard-Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {A7B6FB6E-F484-42D7-8A5E-F7D0DCC03829}.netstandard-Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {A7B6FB6E-F484-42D7-8A5E-F7D0DCC03829}.netstandard-Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {A7B6FB6E-F484-42D7-8A5E-F7D0DCC03829}.netstandard-Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {A7B6FB6E-F484-42D7-8A5E-F7D0DCC03829}.netstandard-Windows_NT-Debug|Any CPU.ActiveCfg = netstandard-Windows_NT-Debug|Any CPU + {A7B6FB6E-F484-42D7-8A5E-F7D0DCC03829}.netstandard-Windows_NT-Debug|Any CPU.Build.0 = netstandard-Windows_NT-Debug|Any CPU + {A7B6FB6E-F484-42D7-8A5E-F7D0DCC03829}.netstandard-Windows_NT-Release|Any CPU.ActiveCfg = netstandard-Windows_NT-Release|Any CPU + {A7B6FB6E-F484-42D7-8A5E-F7D0DCC03829}.netstandard-Windows_NT-Release|Any CPU.Build.0 = netstandard-Windows_NT-Release|Any CPU + {397E49A7-EB26-4368-8F46-D78B98F4A971}.netcoreapp-Windows_NT-Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {397E49A7-EB26-4368-8F46-D78B98F4A971}.netcoreapp-Windows_NT-Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {397E49A7-EB26-4368-8F46-D78B98F4A971}.netcoreapp-Windows_NT-Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {397E49A7-EB26-4368-8F46-D78B98F4A971}.netcoreapp-Windows_NT-Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU + {397E49A7-EB26-4368-8F46-D78B98F4A971}.netfx-Debug|Any CPU.ActiveCfg = netfx-Debug|Any CPU + {397E49A7-EB26-4368-8F46-D78B98F4A971}.netfx-Debug|Any CPU.Build.0 = netfx-Debug|Any CPU + {397E49A7-EB26-4368-8F46-D78B98F4A971}.netfx-Release|Any CPU.ActiveCfg = netfx-Release|Any CPU + {397E49A7-EB26-4368-8F46-D78B98F4A971}.netfx-Release|Any CPU.Build.0 = netfx-Release|Any CPU + {397E49A7-EB26-4368-8F46-D78B98F4A971}.netstandard-Debug|Any CPU.ActiveCfg = netfx-Release|Any CPU + {397E49A7-EB26-4368-8F46-D78B98F4A971}.netstandard-Debug|Any CPU.Build.0 = netfx-Release|Any CPU + {397E49A7-EB26-4368-8F46-D78B98F4A971}.netstandard-Release|Any CPU.ActiveCfg = netfx-Release|Any CPU + {397E49A7-EB26-4368-8F46-D78B98F4A971}.netstandard-Release|Any CPU.Build.0 = netfx-Release|Any CPU + {397E49A7-EB26-4368-8F46-D78B98F4A971}.netstandard-Windows_NT-Debug|Any CPU.ActiveCfg = netfx-Release|Any CPU + {397E49A7-EB26-4368-8F46-D78B98F4A971}.netstandard-Windows_NT-Debug|Any CPU.Build.0 = netfx-Release|Any CPU + {397E49A7-EB26-4368-8F46-D78B98F4A971}.netstandard-Windows_NT-Release|Any CPU.ActiveCfg = netfx-Release|Any CPU + {397E49A7-EB26-4368-8F46-D78B98F4A971}.netstandard-Windows_NT-Release|Any CPU.Build.0 = netfx-Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {3B7A97BD-F4DC-4FF9-AB6D-3B714E5D9280} = {F19CC87B-3230-42BF-8C1F-88F4F5EDE153} + {A7B6FB6E-F484-42D7-8A5E-F7D0DCC03829} = {9EC38C79-BC41-4675-AB3D-A509BDC14FE0} + {397E49A7-EB26-4368-8F46-D78B98F4A971} = {D4DA8C10-76FA-49D3-B4F2-BCB0C94330F6} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D3D6D992-CDEE-4C0C-83A6-E2EF77383E5C} + EndGlobalSection +EndGlobal diff --git a/external/corefx/src/System.Runtime.Caching/dir.props b/external/corefx/src/System.Runtime.Caching/dir.props new file mode 100644 index 0000000000..3a2542eb5f --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/dir.props @@ -0,0 +1,11 @@ + + + + + + 4.0.0.0 + MSFT + + \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Caching/pkg/System.Runtime.Caching.pkgproj b/external/corefx/src/System.Runtime.Caching/pkg/System.Runtime.Caching.pkgproj new file mode 100644 index 0000000000..a2ce422ccb --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/pkg/System.Runtime.Caching.pkgproj @@ -0,0 +1,18 @@ + + + + + + netcoreapp2.0;net45;$(AllXamarinFrameworks) + + + + true + + + + runtimes/win/lib/net45 + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Caching/ref/Configurations.props b/external/corefx/src/System.Runtime.Caching/ref/Configurations.props new file mode 100644 index 0000000000..78953dfc88 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/ref/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + diff --git a/external/corefx/src/System.Runtime.Caching/ref/System.Runtime.Caching.cs b/external/corefx/src/System.Runtime.Caching/ref/System.Runtime.Caching.cs new file mode 100644 index 0000000000..5128fdb55e --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/ref/System.Runtime.Caching.cs @@ -0,0 +1,187 @@ +// 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. +// ------------------------------------------------------------------------------ +// Changes to this file must follow the http://aka.ms/api-review process. +// ------------------------------------------------------------------------------ + +namespace System.Runtime.Caching +{ + public abstract partial class CacheEntryChangeMonitor : System.Runtime.Caching.ChangeMonitor + { + protected CacheEntryChangeMonitor() { } + public abstract System.Collections.ObjectModel.ReadOnlyCollection CacheKeys { get; } + public abstract System.DateTimeOffset LastModified { get; } + public abstract string RegionName { get; } + } + public partial class CacheEntryRemovedArguments + { + public CacheEntryRemovedArguments(System.Runtime.Caching.ObjectCache source, System.Runtime.Caching.CacheEntryRemovedReason reason, System.Runtime.Caching.CacheItem cacheItem) { } + public System.Runtime.Caching.CacheItem CacheItem { get { throw null; } } + public System.Runtime.Caching.CacheEntryRemovedReason RemovedReason { get { throw null; } } + public System.Runtime.Caching.ObjectCache Source { get { throw null; } } + } + public delegate void CacheEntryRemovedCallback(System.Runtime.Caching.CacheEntryRemovedArguments arguments); + public enum CacheEntryRemovedReason + { + CacheSpecificEviction = 4, + ChangeMonitorChanged = 3, + Evicted = 2, + Expired = 1, + Removed = 0, + } + public partial class CacheEntryUpdateArguments + { + public CacheEntryUpdateArguments(System.Runtime.Caching.ObjectCache source, System.Runtime.Caching.CacheEntryRemovedReason reason, string key, string regionName) { } + public string Key { get { throw null; } } + public string RegionName { get { throw null; } } + public System.Runtime.Caching.CacheEntryRemovedReason RemovedReason { get { throw null; } } + public System.Runtime.Caching.ObjectCache Source { get { throw null; } } + public System.Runtime.Caching.CacheItem UpdatedCacheItem { get { throw null; } set { } } + public System.Runtime.Caching.CacheItemPolicy UpdatedCacheItemPolicy { get { throw null; } set { } } + } + public delegate void CacheEntryUpdateCallback(System.Runtime.Caching.CacheEntryUpdateArguments arguments); + public partial class CacheItem + { + public CacheItem(string key) { } + public CacheItem(string key, object value) { } + public CacheItem(string key, object value, string regionName) { } + public string Key { get { throw null; } set { } } + public string RegionName { get { throw null; } set { } } + public object Value { get { throw null; } set { } } + } + public partial class CacheItemPolicy + { + public CacheItemPolicy() { } + public System.DateTimeOffset AbsoluteExpiration { get { throw null; } set { } } + public System.Collections.ObjectModel.Collection ChangeMonitors { get { throw null; } } + public System.Runtime.Caching.CacheItemPriority Priority { get { throw null; } set { } } + public System.Runtime.Caching.CacheEntryRemovedCallback RemovedCallback { get { throw null; } set { } } + public System.TimeSpan SlidingExpiration { get { throw null; } set { } } + public System.Runtime.Caching.CacheEntryUpdateCallback UpdateCallback { get { throw null; } set { } } + } + public enum CacheItemPriority + { + Default = 0, + NotRemovable = 1, + } + public abstract partial class ChangeMonitor : System.IDisposable + { + protected ChangeMonitor() { } + public bool HasChanged { get { throw null; } } + public bool IsDisposed { get { throw null; } } + public abstract string UniqueId { get; } + public void Dispose() { } + protected abstract void Dispose(bool disposing); + protected void InitializationComplete() { } + public void NotifyOnChanged(System.Runtime.Caching.OnChangedCallback onChangedCallback) { } + protected void OnChanged(object state) { } + } + [System.FlagsAttribute] + public enum DefaultCacheCapabilities + { + AbsoluteExpirations = 8, + CacheEntryChangeMonitors = 4, + CacheEntryRemovedCallback = 64, + CacheEntryUpdateCallback = 32, + CacheRegions = 128, + InMemoryProvider = 1, + None = 0, + OutOfProcessProvider = 2, + SlidingExpirations = 16, + } + public abstract partial class FileChangeMonitor : System.Runtime.Caching.ChangeMonitor + { + protected FileChangeMonitor() { } + public abstract System.Collections.ObjectModel.ReadOnlyCollection FilePaths { get; } + public abstract System.DateTimeOffset LastModified { get; } + } + public sealed partial class HostFileChangeMonitor : System.Runtime.Caching.FileChangeMonitor + { + public HostFileChangeMonitor(System.Collections.Generic.IList filePaths) { } + public override System.Collections.ObjectModel.ReadOnlyCollection FilePaths { get { throw null; } } + public override System.DateTimeOffset LastModified { get { throw null; } } + public override string UniqueId { get { throw null; } } + protected override void Dispose(bool disposing) { } + } + public partial class MemoryCache : System.Runtime.Caching.ObjectCache, System.Collections.IEnumerable, System.IDisposable + { + public MemoryCache(string name, System.Collections.Specialized.NameValueCollection config=null) { } + public MemoryCache(string name, System.Collections.Specialized.NameValueCollection config, bool ignoreConfigSection) { } + public long CacheMemoryLimit { get { throw null; } } + public static System.Runtime.Caching.MemoryCache Default { get { throw null; } } + public override System.Runtime.Caching.DefaultCacheCapabilities DefaultCacheCapabilities { get { throw null; } } + public override object this[string key] { get { throw null; } set { } } + public override string Name { get { throw null; } } + public long PhysicalMemoryLimit { get { throw null; } } + public System.TimeSpan PollingInterval { get { throw null; } } + public override bool Add(System.Runtime.Caching.CacheItem item, System.Runtime.Caching.CacheItemPolicy policy) { throw null; } + public override System.Runtime.Caching.CacheItem AddOrGetExisting(System.Runtime.Caching.CacheItem item, System.Runtime.Caching.CacheItemPolicy policy) { throw null; } + public override object AddOrGetExisting(string key, object value, System.DateTimeOffset absoluteExpiration, string regionName=null) { throw null; } + public override object AddOrGetExisting(string key, object value, System.Runtime.Caching.CacheItemPolicy policy, string regionName=null) { throw null; } + public override bool Contains(string key, string regionName=null) { throw null; } + public override System.Runtime.Caching.CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(System.Collections.Generic.IEnumerable keys, string regionName=null) { throw null; } + public void Dispose() { } + public override object Get(string key, string regionName=null) { throw null; } + public override System.Runtime.Caching.CacheItem GetCacheItem(string key, string regionName=null) { throw null; } + public override long GetCount(string regionName=null) { throw null; } + protected override System.Collections.Generic.IEnumerator> GetEnumerator() { throw null; } + public long GetLastSize(string regionName=null) { throw null; } + public override System.Collections.Generic.IDictionary GetValues(System.Collections.Generic.IEnumerable keys, string regionName=null) { throw null; } + public object Remove(string key, System.Runtime.Caching.CacheEntryRemovedReason reason, string regionName=null) { throw null; } + public override object Remove(string key, string regionName=null) { throw null; } + public override void Set(System.Runtime.Caching.CacheItem item, System.Runtime.Caching.CacheItemPolicy policy) { } + public override void Set(string key, object value, System.DateTimeOffset absoluteExpiration, string regionName=null) { } + public override void Set(string key, object value, System.Runtime.Caching.CacheItemPolicy policy, string regionName=null) { } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } + public long Trim(int percent) { throw null; } + } + public abstract partial class ObjectCache : System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable + { + public static readonly System.DateTimeOffset InfiniteAbsoluteExpiration; + public static readonly System.TimeSpan NoSlidingExpiration; + protected ObjectCache() { } + public abstract System.Runtime.Caching.DefaultCacheCapabilities DefaultCacheCapabilities { get; } + public static System.IServiceProvider Host { get { throw null; } set { } } + public abstract object this[string key] { get; set; } + public abstract string Name { get; } + public virtual bool Add(System.Runtime.Caching.CacheItem item, System.Runtime.Caching.CacheItemPolicy policy) { throw null; } + public virtual bool Add(string key, object value, System.DateTimeOffset absoluteExpiration, string regionName=null) { throw null; } + public virtual bool Add(string key, object value, System.Runtime.Caching.CacheItemPolicy policy, string regionName=null) { throw null; } + public abstract System.Runtime.Caching.CacheItem AddOrGetExisting(System.Runtime.Caching.CacheItem value, System.Runtime.Caching.CacheItemPolicy policy); + public abstract object AddOrGetExisting(string key, object value, System.DateTimeOffset absoluteExpiration, string regionName=null); + public abstract object AddOrGetExisting(string key, object value, System.Runtime.Caching.CacheItemPolicy policy, string regionName=null); + public abstract bool Contains(string key, string regionName=null); + public abstract System.Runtime.Caching.CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(System.Collections.Generic.IEnumerable keys, string regionName=null); + public abstract object Get(string key, string regionName=null); + public abstract System.Runtime.Caching.CacheItem GetCacheItem(string key, string regionName=null); + public abstract long GetCount(string regionName=null); + protected abstract System.Collections.Generic.IEnumerator> GetEnumerator(); + public abstract System.Collections.Generic.IDictionary GetValues(System.Collections.Generic.IEnumerable keys, string regionName=null); + public virtual System.Collections.Generic.IDictionary GetValues(string regionName, params string[] keys) { throw null; } + public abstract object Remove(string key, string regionName=null); + public abstract void Set(System.Runtime.Caching.CacheItem item, System.Runtime.Caching.CacheItemPolicy policy); + public abstract void Set(string key, object value, System.DateTimeOffset absoluteExpiration, string regionName=null); + public abstract void Set(string key, object value, System.Runtime.Caching.CacheItemPolicy policy, string regionName=null); + System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } + } + public delegate void OnChangedCallback(object state); +} +namespace System.Runtime.Caching.Hosting +{ + public partial interface IApplicationIdentifier + { + string GetApplicationId(); + } + public partial interface IFileChangeNotificationSystem + { + void StartMonitoring(string filePath, System.Runtime.Caching.OnChangedCallback onChangedCallback, out object state, out System.DateTimeOffset lastWriteTime, out long fileSize); + void StopMonitoring(string filePath, object state); + } + public partial interface IMemoryCacheManager + { + void ReleaseCache(System.Runtime.Caching.MemoryCache cache); + void UpdateCacheSize(long size, System.Runtime.Caching.MemoryCache cache); + } +} diff --git a/external/corefx/src/System.Runtime.Caching/ref/System.Runtime.Caching.csproj b/external/corefx/src/System.Runtime.Caching/ref/System.Runtime.Caching.csproj new file mode 100644 index 0000000000..170edc5a35 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/ref/System.Runtime.Caching.csproj @@ -0,0 +1,13 @@ + + + + + + + {3B7A97BD-F4DC-4FF9-AB6D-3B714E5D9280} + + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Caching/src/Configurations.props b/external/corefx/src/System.Runtime.Caching/src/Configurations.props new file mode 100644 index 0000000000..84577c8a06 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/Configurations.props @@ -0,0 +1,14 @@ + + + + + netstandard; + netcoreapp2.0-Windows_NT; + + + $(PackageConfigurations); + netcoreapp-Windows_NT; + _netfx; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Caching/src/Resources/Strings.resx b/external/corefx/src/System.Runtime.Caching/src/Resources/Strings.resx new file mode 100644 index 0000000000..89884bf464 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/Resources/Strings.resx @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Unable to retrieve configuration section '{0}'. + + + Invalid configuration: {0}="{1}". The {0} value must be a time interval that can be parsed by System.TimeSpan.Parse. + + + Invalid configuration: {0}="{1}". The {0} value must be a non-negative 32-bit integer. + + + Invalid configuration: {0}="{1}". The {0} value must be a positive 32-bit integer. + + + Invalid configuration: {0}="{1}". The {0} value cannot be greater than '{2}'. + + + The collection '{0}' is empty. + + + The collection '{0}' contains a null element. + + + The collection '{0}' contains a null or empty string. + + + The method has already been invoked, and can only be invoked once. + + + The property has already been set, and can only be set once. + + + Invalid state. + + + Initialization has not completed yet. The InitializationComplete method must be invoked before Dispose is invoked. + + + Default is a reserved MemoryCache name. + + + AbsoluteExpiration must be DateTimeOffset.MaxValue or SlidingExpiration must be TimeSpan.Zero. + + + Only one callback can be specified. Either RemovedCallback or UpdateCallback must be null. + + + One of the following parameters must be specified: dependencies, absoluteExpiration, slidingExpiration. + + + CacheItemUpdateCallback must be null. + + + '{0}' must be greater than or equal to '{1}' and less than or equal to '{2}'. + + + An empty string is invalid. + + + The parameter regionName must be null. + + + Invalid configuration: {0}="{1}". The {0} value must be a boolean. + + + System.Runtime.Caching is not supported on this platform. + + \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Caching/src/System.Runtime.Caching.csproj b/external/corefx/src/System.Runtime.Caching/src/System.Runtime.Caching.csproj new file mode 100644 index 0000000000..004b4ea2ab --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System.Runtime.Caching.csproj @@ -0,0 +1,102 @@ + + + + + System.Runtime.Caching + {A7B6FB6E-F484-42D7-8A5E-F7D0DCC03829} + true + + false + SR.PlatformNotSupported_Caching + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Common\Interop\Windows\kernel32\Interop.GlobalMemoryStatusEx.cs + + + Common\Interop\Windows\kernel32\Interop.MEMORY_BASIC_INFO.cs + + + Common\Interop\Windows\Interop.Libraries.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheEntryChangeMonitor.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheEntryChangeMonitor.cs new file mode 100644 index 0000000000..5fe8d0fa67 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheEntryChangeMonitor.cs @@ -0,0 +1,17 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; + +namespace System.Runtime.Caching +{ + public abstract class CacheEntryChangeMonitor : ChangeMonitor + { + public abstract ReadOnlyCollection CacheKeys { get; } + public abstract DateTimeOffset LastModified { get; } + public abstract String RegionName { get; } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheEntryRemovedArguments.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheEntryRemovedArguments.cs new file mode 100644 index 0000000000..de3f2407b4 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheEntryRemovedArguments.cs @@ -0,0 +1,43 @@ +// 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. + +namespace System.Runtime.Caching +{ + public class CacheEntryRemovedArguments + { + private CacheItem _cacheItem; + private ObjectCache _source; + private CacheEntryRemovedReason _reason; + + public CacheItem CacheItem + { + get { return _cacheItem; } + } + + public CacheEntryRemovedReason RemovedReason + { + get { return _reason; } + } + + public ObjectCache Source + { + get { return _source; } + } + + public CacheEntryRemovedArguments(ObjectCache source, CacheEntryRemovedReason reason, CacheItem cacheItem) + { + if (source == null) + { + throw new ArgumentNullException("source"); + } + if (cacheItem == null) + { + throw new ArgumentNullException("cacheItem"); + } + _source = source; + _reason = reason; + _cacheItem = cacheItem; + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheEntryRemovedCallback.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheEntryRemovedCallback.cs new file mode 100644 index 0000000000..c1ec95b059 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheEntryRemovedCallback.cs @@ -0,0 +1,10 @@ +// 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; + +namespace System.Runtime.Caching +{ + public delegate void CacheEntryRemovedCallback(CacheEntryRemovedArguments arguments); +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheEntryRemovedReason.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheEntryRemovedReason.cs new file mode 100644 index 0000000000..d74ba917ae --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheEntryRemovedReason.cs @@ -0,0 +1,17 @@ +// 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; + +namespace System.Runtime.Caching +{ + public enum CacheEntryRemovedReason + { + Removed = 0, //Explicitly removed via API call + Expired, + Evicted, //Evicted to free up space + ChangeMonitorChanged, //An associated programmatic dependency triggered eviction + CacheSpecificEviction //Catch-all for custom providers + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheEntryUpdateArguments.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheEntryUpdateArguments.cs new file mode 100644 index 0000000000..2814f52edf --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheEntryUpdateArguments.cs @@ -0,0 +1,66 @@ +// 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; + +namespace System.Runtime.Caching +{ + public class CacheEntryUpdateArguments + { + private String _key; + private CacheEntryRemovedReason _reason; + private String _regionName; + private ObjectCache _source; + private CacheItem _updatedCacheItem; + private CacheItemPolicy _updatedCacheItemPolicy; + + public String Key + { + get { return _key; } + } + + public CacheEntryRemovedReason RemovedReason + { + get { return _reason; } + } + + public String RegionName + { + get { return _regionName; } + } + + public ObjectCache Source + { + get { return _source; } + } + + public CacheItem UpdatedCacheItem + { + get { return _updatedCacheItem; } + set { _updatedCacheItem = value; } + } + + public CacheItemPolicy UpdatedCacheItemPolicy + { + get { return _updatedCacheItemPolicy; } + set { _updatedCacheItemPolicy = value; } + } + + public CacheEntryUpdateArguments(ObjectCache source, CacheEntryRemovedReason reason, String key, String regionName) + { + if (source == null) + { + throw new ArgumentNullException("source"); + } + if (key == null) + { + throw new ArgumentNullException("key"); + } + _source = source; + _reason = reason; + _key = key; + _regionName = regionName; + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheEntryUpdateCallback.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheEntryUpdateCallback.cs new file mode 100644 index 0000000000..8912a0f7d2 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheEntryUpdateCallback.cs @@ -0,0 +1,10 @@ +// 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; + +namespace System.Runtime.Caching +{ + public delegate void CacheEntryUpdateCallback(CacheEntryUpdateArguments arguments); +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheExpires.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheExpires.cs new file mode 100644 index 0000000000..9dcd141b54 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheExpires.cs @@ -0,0 +1,996 @@ +// 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.Collections; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using System.Security; +using System.Text; +using System.Threading; + +namespace System.Runtime.Caching +{ + internal struct ExpiresEntryRef + { + static internal readonly ExpiresEntryRef INVALID = new ExpiresEntryRef(0, 0); + + private const uint ENTRY_MASK = 0x000000ffu; + private const uint PAGE_MASK = 0xffffff00u; + private const int PAGE_SHIFT = 8; + + private uint _ref; + + internal ExpiresEntryRef(int pageIndex, int entryIndex) + { + Dbg.Assert((pageIndex & 0x00ffffff) == pageIndex, "(pageIndex & 0x00ffffff) == pageIndex"); + Dbg.Assert((entryIndex & ENTRY_MASK) == entryIndex, "(entryIndex & ENTRY_MASK) == entryIndex"); + Dbg.Assert(entryIndex != 0 || pageIndex == 0, "entryIndex != 0 || pageIndex == 0"); + + _ref = ((((uint)pageIndex) << PAGE_SHIFT) | (((uint)(entryIndex)) & ENTRY_MASK)); + } + + public override bool Equals(object value) + { + if (value is ExpiresEntryRef) + { + return _ref == ((ExpiresEntryRef)value)._ref; + } + + return false; + } + + public static bool operator !=(ExpiresEntryRef r1, ExpiresEntryRef r2) + { + return r1._ref != r2._ref; + } + public static bool operator ==(ExpiresEntryRef r1, ExpiresEntryRef r2) + { + return r1._ref == r2._ref; + } + + public override int GetHashCode() + { + return (int)_ref; + } + + internal int PageIndex + { + get + { + int result = (int)(_ref >> PAGE_SHIFT); + return result; + } + } + + internal int Index + { + get + { + int result = (int)(_ref & ENTRY_MASK); + return result; + } + } + + internal bool IsInvalid + { + get + { + return _ref == 0; + } + } + } + + [SuppressMessage("Microsoft.Portability", "CA1900:ValueTypeFieldsShouldBePortable", Justification = "Grandfathered suppression from original caching code checkin")] + [StructLayout(LayoutKind.Explicit)] + internal struct ExpiresEntry + { + [FieldOffset(0)] + internal DateTime _utcExpires; + + [FieldOffset(0)] + internal ExpiresEntryRef _next; + + [FieldOffset(4)] + internal int _cFree; + + [FieldOffset(8)] + internal MemoryCacheEntry _cacheEntry; + } + + internal struct ExpiresPage + { + internal ExpiresEntry[] _entries; + internal int _pageNext; + internal int _pagePrev; + } + + internal struct ExpiresPageList + { + internal int _head; + internal int _tail; + } + + internal sealed class ExpiresBucket + { + private const int NUM_ENTRIES = 127; + private const int LENGTH_ENTRIES = 128; + + private const int MIN_PAGES_INCREMENT = 10; + private const int MAX_PAGES_INCREMENT = 340; + private const double MIN_LOAD_FACTOR = 0.5; + + private const int COUNTS_LENGTH = 4; + + private static readonly TimeSpan s_COUNT_INTERVAL = new TimeSpan(CacheExpires._tsPerBucket.Ticks / COUNTS_LENGTH); + + private readonly CacheExpires _cacheExpires; + private readonly byte _bucket; + + private ExpiresPage[] _pages; + + private int _cEntriesInUse; + private int _cPagesInUse; + private int _cEntriesInFlush; + private int _minEntriesInUse; + + private ExpiresPageList _freePageList; + private ExpiresPageList _freeEntryList; + private bool _blockReduce; + private DateTime _utcMinExpires; + private int[] _counts; + private DateTime _utcLastCountReset; + + internal ExpiresBucket(CacheExpires cacheExpires, byte bucket, DateTime utcNow) + { + _cacheExpires = cacheExpires; + _bucket = bucket; + _counts = new int[COUNTS_LENGTH]; + ResetCounts(utcNow); + InitZeroPages(); + + Dbg.Validate("CacheValidateExpires", this); + } + + private void InitZeroPages() + { + Dbg.Assert(_cPagesInUse == 0, "_cPagesInUse == 0"); + Dbg.Assert(_cEntriesInUse == 0, "_cEntriesInUse == 0"); + Dbg.Assert(_cEntriesInFlush == 0, "_cEntriesInFlush == 0"); + + _pages = null; + _minEntriesInUse = -1; + _freePageList._head = -1; + _freePageList._tail = -1; + _freeEntryList._head = -1; + _freeEntryList._tail = -1; + } + private void ResetCounts(DateTime utcNow) + { + _utcLastCountReset = utcNow; + _utcMinExpires = DateTime.MaxValue; + + for (int i = 0; i < _counts.Length; i++) + { + _counts[i] = 0; + } + } + + private int GetCountIndex(DateTime utcExpires) + { + return Math.Max(0, (int)((utcExpires - _utcLastCountReset).Ticks / s_COUNT_INTERVAL.Ticks)); + } + + private void AddCount(DateTime utcExpires) + { + int ci = GetCountIndex(utcExpires); + for (int i = _counts.Length - 1; i >= ci; i--) + { + _counts[i]++; + } + + if (utcExpires < _utcMinExpires) + { + _utcMinExpires = utcExpires; + } + } + + private void RemoveCount(DateTime utcExpires) + { + int ci = GetCountIndex(utcExpires); + for (int i = _counts.Length - 1; i >= ci; i--) + { + _counts[i]--; + } + } + + private int GetExpiresCount(DateTime utcExpires) + { + if (utcExpires < _utcMinExpires) + return 0; + + int ci = GetCountIndex(utcExpires); + if (ci >= _counts.Length) + return _cEntriesInUse; + + return _counts[ci]; + } + + private void AddToListHead(int pageIndex, ref ExpiresPageList list) + { + Dbg.Assert((list._head == -1) == (list._tail == -1), "(list._head == -1) == (list._tail == -1)"); + + (_pages[(pageIndex)]._pagePrev) = -1; + (_pages[(pageIndex)]._pageNext) = list._head; + if (list._head != -1) + { + Dbg.Assert((_pages[(list._head)]._pagePrev) == -1, "PagePrev(list._head) == -1"); + (_pages[(list._head)]._pagePrev) = pageIndex; + } + else + { + list._tail = pageIndex; + } + + list._head = pageIndex; + } + + private void AddToListTail(int pageIndex, ref ExpiresPageList list) + { + Dbg.Assert((list._head == -1) == (list._tail == -1), "(list._head == -1) == (list._tail == -1)"); + + (_pages[(pageIndex)]._pageNext) = -1; + (_pages[(pageIndex)]._pagePrev) = list._tail; + if (list._tail != -1) + { + Dbg.Assert((_pages[(list._tail)]._pageNext) == -1, "PageNext(list._tail) == -1"); + (_pages[(list._tail)]._pageNext) = pageIndex; + } + else + { + list._head = pageIndex; + } + + list._tail = pageIndex; + } + + private int RemoveFromListHead(ref ExpiresPageList list) + { + Dbg.Assert(list._head != -1, "list._head != -1"); + + int oldHead = list._head; + RemoveFromList(oldHead, ref list); + return oldHead; + } + + private void RemoveFromList(int pageIndex, ref ExpiresPageList list) + { + Dbg.Assert((list._head == -1) == (list._tail == -1), "(list._head == -1) == (list._tail == -1)"); + + if ((_pages[(pageIndex)]._pagePrev) != -1) + { + Dbg.Assert((_pages[((_pages[(pageIndex)]._pagePrev))]._pageNext) == pageIndex, "PageNext(PagePrev(pageIndex)) == pageIndex"); + (_pages[((_pages[(pageIndex)]._pagePrev))]._pageNext) = (_pages[(pageIndex)]._pageNext); + } + else + { + Dbg.Assert(list._head == pageIndex, "list._head == pageIndex"); + list._head = (_pages[(pageIndex)]._pageNext); + } + + if ((_pages[(pageIndex)]._pageNext) != -1) + { + Dbg.Assert((_pages[((_pages[(pageIndex)]._pageNext))]._pagePrev) == pageIndex, "PagePrev(PageNext(pageIndex)) == pageIndex"); + (_pages[((_pages[(pageIndex)]._pageNext))]._pagePrev) = (_pages[(pageIndex)]._pagePrev); + } + else + { + Dbg.Assert(list._tail == pageIndex, "list._tail == pageIndex"); + list._tail = (_pages[(pageIndex)]._pagePrev); + } + + (_pages[(pageIndex)]._pagePrev) = -1; + (_pages[(pageIndex)]._pageNext) = -1; + } + + private void MoveToListHead(int pageIndex, ref ExpiresPageList list) + { + Dbg.Assert(list._head != -1, "list._head != -1"); + Dbg.Assert(list._tail != -1, "list._tail != -1"); + + if (list._head == pageIndex) + return; + + RemoveFromList(pageIndex, ref list); + + AddToListHead(pageIndex, ref list); + } + + private void MoveToListTail(int pageIndex, ref ExpiresPageList list) + { + Dbg.Assert(list._head != -1, "list._head != -1"); + Dbg.Assert(list._tail != -1, "list._tail != -1"); + + if (list._tail == pageIndex) + { + return; + } + + RemoveFromList(pageIndex, ref list); + AddToListTail(pageIndex, ref list); + } + + private void UpdateMinEntries() + { + if (_cPagesInUse <= 1) + { + _minEntriesInUse = -1; + } + else + { + int capacity = _cPagesInUse * NUM_ENTRIES; + Dbg.Assert(capacity > 0, "capacity > 0"); + Dbg.Assert(MIN_LOAD_FACTOR < 1.0, "MIN_LOAD_FACTOR < 1.0"); + + _minEntriesInUse = (int)(capacity * MIN_LOAD_FACTOR); + + if ((_minEntriesInUse - 1) > ((_cPagesInUse - 1) * NUM_ENTRIES)) + { + _minEntriesInUse = -1; + } + } + } + + private void RemovePage(int pageIndex) + { + Dbg.Assert((((_pages[(pageIndex)]._entries))[0]._cFree) == NUM_ENTRIES, "FreeEntryCount(EntriesI(pageIndex)) == NUM_ENTRIES"); + + RemoveFromList(pageIndex, ref _freeEntryList); + AddToListHead(pageIndex, ref _freePageList); + + Dbg.Assert((_pages[(pageIndex)]._entries) != null, "EntriesI(pageIndex) != null"); + (_pages[(pageIndex)]._entries) = null; + + _cPagesInUse--; + if (_cPagesInUse == 0) + { + InitZeroPages(); + } + else + { + UpdateMinEntries(); + } + } + + private ExpiresEntryRef GetFreeExpiresEntry() + { + Dbg.Assert(_freeEntryList._head >= 0, "_freeEntryList._head >= 0"); + int pageIndex = _freeEntryList._head; + + ExpiresEntry[] entries = (_pages[(pageIndex)]._entries); + int entryIndex = ((entries)[0]._next).Index; + + ((entries)[0]._next) = entries[entryIndex]._next; + ((entries)[0]._cFree)--; + if (((entries)[0]._cFree) == 0) + { + Dbg.Assert(((entries)[0]._next).IsInvalid, "FreeEntryHead(entries).IsInvalid"); + RemoveFromList(pageIndex, ref _freeEntryList); + } + return new ExpiresEntryRef(pageIndex, entryIndex); + } + + private void AddExpiresEntryToFreeList(ExpiresEntryRef entryRef) + { + ExpiresEntry[] entries = (_pages[(entryRef.PageIndex)]._entries); + int entryIndex = entryRef.Index; + + Dbg.Assert(entries[entryIndex]._cacheEntry == null, "entries[entryIndex]._cacheEntry == null"); + entries[entryIndex]._cFree = 0; + + entries[entryIndex]._next = ((entries)[0]._next); + ((entries)[0]._next) = entryRef; + + _cEntriesInUse--; + int pageIndex = entryRef.PageIndex; + ((entries)[0]._cFree)++; + if (((entries)[0]._cFree) == 1) + { + AddToListHead(pageIndex, ref _freeEntryList); + } + else if (((entries)[0]._cFree) == NUM_ENTRIES) + { + RemovePage(pageIndex); + } + } + + private void Expand() + { + Dbg.Assert(_cPagesInUse * NUM_ENTRIES == _cEntriesInUse, "_cPagesInUse * NUM_ENTRIES == _cEntriesInUse"); + Dbg.Assert(_freeEntryList._head == -1, "_freeEntryList._head == -1"); + Dbg.Assert(_freeEntryList._tail == -1, "_freeEntryList._tail == -1"); + + if (_freePageList._head == -1) + { + int oldLength; + if (_pages == null) + { + oldLength = 0; + } + else + { + oldLength = _pages.Length; + } + + Dbg.Assert(_cPagesInUse == oldLength, "_cPagesInUse == oldLength"); + Dbg.Assert(_cEntriesInUse == oldLength * NUM_ENTRIES, "_cEntriesInUse == oldLength * ExpiresEntryRef.NUM_ENTRIES"); + + int newLength = oldLength * 2; + newLength = Math.Max(oldLength + MIN_PAGES_INCREMENT, newLength); + newLength = Math.Min(newLength, oldLength + MAX_PAGES_INCREMENT); + Dbg.Assert(newLength > oldLength, "newLength > oldLength"); + + ExpiresPage[] newPages = new ExpiresPage[newLength]; + + for (int i = 0; i < oldLength; i++) + { + newPages[i] = _pages[i]; + } + + for (int i = oldLength; i < newPages.Length; i++) + { + newPages[i]._pagePrev = i - 1; + newPages[i]._pageNext = i + 1; + } + + newPages[oldLength]._pagePrev = -1; + newPages[newPages.Length - 1]._pageNext = -1; + + _freePageList._head = oldLength; + _freePageList._tail = newPages.Length - 1; + + _pages = newPages; + } + + int pageIndex = RemoveFromListHead(ref _freePageList); + AddToListHead(pageIndex, ref _freeEntryList); + + ExpiresEntry[] entries = new ExpiresEntry[LENGTH_ENTRIES]; + ((entries)[0]._cFree) = NUM_ENTRIES; + + for (int i = 0; i < entries.Length - 1; i++) + { + entries[i]._next = new ExpiresEntryRef(pageIndex, i + 1); + } + entries[entries.Length - 1]._next = ExpiresEntryRef.INVALID; + + (_pages[(pageIndex)]._entries) = entries; + + _cPagesInUse++; + UpdateMinEntries(); + } + + private void Reduce() + { + if (_cEntriesInUse >= _minEntriesInUse || _blockReduce) + return; + + Dbg.Assert(_freeEntryList._head != -1, "_freeEntryList._head != -1"); + Dbg.Assert(_freeEntryList._tail != -1, "_freeEntryList._tail != -1"); + Dbg.Assert(_freeEntryList._head != _freeEntryList._tail, "_freeEntryList._head != _freeEntryList._tail"); + + int meanFree = (int)(NUM_ENTRIES - (NUM_ENTRIES * MIN_LOAD_FACTOR)); + int pageIndexLast = _freeEntryList._tail; + int pageIndexCurrent = _freeEntryList._head; + int pageIndexNext; + ExpiresEntry[] entries; + + for (; ;) + { + pageIndexNext = (_pages[(pageIndexCurrent)]._pageNext); + + if ((((_pages[(pageIndexCurrent)]._entries))[0]._cFree) > meanFree) + { + MoveToListTail(pageIndexCurrent, ref _freeEntryList); + } + else + { + MoveToListHead(pageIndexCurrent, ref _freeEntryList); + } + + if (pageIndexCurrent == pageIndexLast) + { + break; + } + + pageIndexCurrent = pageIndexNext; + } + + for (; ;) + { + if (_freeEntryList._tail == -1) + break; + + entries = (_pages[(_freeEntryList._tail)]._entries); + Dbg.Assert(((entries)[0]._cFree) > 0, "FreeEntryCount(entries) > 0"); + int availableFreeEntries = (_cPagesInUse * NUM_ENTRIES) - ((entries)[0]._cFree) - _cEntriesInUse; + if (availableFreeEntries < (NUM_ENTRIES - ((entries)[0]._cFree))) + break; + + for (int i = 1; i < entries.Length; i++) + { + if (entries[i]._cacheEntry == null) + continue; + + Dbg.Assert(_freeEntryList._head != _freeEntryList._tail, "_freeEntryList._head != _freeEntryList._tail"); + ExpiresEntryRef newRef = GetFreeExpiresEntry(); + Dbg.Assert(newRef.PageIndex != _freeEntryList._tail, "newRef.PageIndex != _freeEntryList._tail"); + + MemoryCacheEntry cacheEntry = entries[i]._cacheEntry; + + cacheEntry.ExpiresEntryRef = newRef; + + ExpiresEntry[] newEntries = (_pages[(newRef.PageIndex)]._entries); + newEntries[newRef.Index] = entries[i]; + + ((entries)[0]._cFree)++; + } + + RemovePage(_freeEntryList._tail); + + Dbg.Validate("CacheValidateExpires", this); + } + } + + internal void AddCacheEntry(MemoryCacheEntry cacheEntry) + { + lock (this) + { + if ((cacheEntry.State & (EntryState.AddedToCache | EntryState.AddingToCache)) == 0) + return; + + ExpiresEntryRef entryRef = cacheEntry.ExpiresEntryRef; + Dbg.Assert((cacheEntry.ExpiresBucket == 0xff) == entryRef.IsInvalid, "(cacheEntry.ExpiresBucket == 0xff) == entryRef.IsInvalid"); + if (cacheEntry.ExpiresBucket != 0xff || !entryRef.IsInvalid) + return; + + if (_freeEntryList._head == -1) + { + Expand(); + } + + ExpiresEntryRef freeRef = GetFreeExpiresEntry(); + Dbg.Assert(cacheEntry.ExpiresBucket == 0xff, "cacheEntry.ExpiresBucket == 0xff"); + Dbg.Assert(cacheEntry.ExpiresEntryRef.IsInvalid, "cacheEntry.ExpiresEntryRef.IsInvalid"); + cacheEntry.ExpiresBucket = _bucket; + cacheEntry.ExpiresEntryRef = freeRef; + + ExpiresEntry[] entries = (_pages[(freeRef.PageIndex)]._entries); + int entryIndex = freeRef.Index; + entries[entryIndex]._cacheEntry = cacheEntry; + entries[entryIndex]._utcExpires = cacheEntry.UtcAbsExp; + + AddCount(cacheEntry.UtcAbsExp); + + _cEntriesInUse++; + + if ((cacheEntry.State & (EntryState.AddedToCache | EntryState.AddingToCache)) == 0) + { + RemoveCacheEntryNoLock(cacheEntry); + } + } + } + + private void RemoveCacheEntryNoLock(MemoryCacheEntry cacheEntry) + { + ExpiresEntryRef entryRef = cacheEntry.ExpiresEntryRef; + if (cacheEntry.ExpiresBucket != _bucket || entryRef.IsInvalid) + return; + + ExpiresEntry[] entries = (_pages[(entryRef.PageIndex)]._entries); + int entryIndex = entryRef.Index; + + RemoveCount(entries[entryIndex]._utcExpires); + + cacheEntry.ExpiresBucket = 0xff; + cacheEntry.ExpiresEntryRef = ExpiresEntryRef.INVALID; + entries[entryIndex]._cacheEntry = null; + + AddExpiresEntryToFreeList(entryRef); + + if (_cEntriesInUse == 0) + { + ResetCounts(DateTime.UtcNow); + } + + Reduce(); + + Dbg.Trace("CacheExpiresRemove", + "Removed item=" + cacheEntry.Key + + ",_bucket=" + _bucket + + ",ref=" + entryRef + + ",now=" + Dbg.FormatLocalDate(DateTime.Now) + + ",expires=" + cacheEntry.UtcAbsExp.ToLocalTime()); + + Dbg.Validate("CacheValidateExpires", this); + Dbg.Dump("CacheExpiresRemove", this); + } + + internal void RemoveCacheEntry(MemoryCacheEntry cacheEntry) + { + lock (this) + { + RemoveCacheEntryNoLock(cacheEntry); + } + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Grandfathered suppression from original caching code checkin")] + internal void UtcUpdateCacheEntry(MemoryCacheEntry cacheEntry, DateTime utcExpires) + { + lock (this) + { + ExpiresEntryRef entryRef = cacheEntry.ExpiresEntryRef; + if (cacheEntry.ExpiresBucket != _bucket || entryRef.IsInvalid) + return; + + ExpiresEntry[] entries = (_pages[(entryRef.PageIndex)]._entries); + int entryIndex = entryRef.Index; + + Dbg.Assert(cacheEntry == entries[entryIndex]._cacheEntry); + + RemoveCount(entries[entryIndex]._utcExpires); + AddCount(utcExpires); + + entries[entryIndex]._utcExpires = utcExpires; + + cacheEntry.UtcAbsExp = utcExpires; + + Dbg.Validate("CacheValidateExpires", this); + Dbg.Trace("CacheExpiresUpdate", "Updated item " + cacheEntry.Key + " in bucket " + _bucket); + } + } + + internal int FlushExpiredItems(DateTime utcNow, bool useInsertBlock) + { + if (_cEntriesInUse == 0 || GetExpiresCount(utcNow) == 0) + return 0; + + Dbg.Assert(_cEntriesInFlush == 0, "_cEntriesInFlush == 0"); + + ExpiresEntryRef inFlushHead = ExpiresEntryRef.INVALID; + + ExpiresEntry[] entries; + int entryIndex; + MemoryCacheEntry cacheEntry; + int flushed = 0; + + try + { + if (useInsertBlock) + { + _cacheExpires.MemoryCacheStore.BlockInsert(); + } + + lock (this) + { + Dbg.Assert(_blockReduce == false, "_blockReduce == false"); + + if (_cEntriesInUse == 0 || GetExpiresCount(utcNow) == 0) + return 0; + + ResetCounts(utcNow); + int cPages = _cPagesInUse; + for (int i = 0; i < _pages.Length; i++) + { + entries = _pages[i]._entries; + if (entries != null) + { + int cEntries = NUM_ENTRIES - ((entries)[0]._cFree); + for (int j = 1; j < entries.Length; j++) + { + cacheEntry = entries[j]._cacheEntry; + if (cacheEntry != null) + { + if (entries[j]._utcExpires > utcNow) + { + AddCount(entries[j]._utcExpires); + } + else + { + cacheEntry.ExpiresBucket = 0xff; + cacheEntry.ExpiresEntryRef = ExpiresEntryRef.INVALID; + + entries[j]._cFree = 1; + + entries[j]._next = inFlushHead; + inFlushHead = new ExpiresEntryRef(i, j); + + flushed++; + _cEntriesInFlush++; + } + + cEntries--; + if (cEntries == 0) + break; + } + } + + cPages--; + if (cPages == 0) + break; + } + } + + if (flushed == 0) + { + Dbg.Trace("CacheExpiresFlushTotal", "FlushExpiredItems flushed " + flushed + + " expired items, bucket=" + _bucket + "; Time=" + Dbg.FormatLocalDate(DateTime.Now)); + + return 0; + } + + _blockReduce = true; + } + } + finally + { + if (useInsertBlock) + { + _cacheExpires.MemoryCacheStore.UnblockInsert(); + } + } + + Dbg.Assert(!inFlushHead.IsInvalid, "!inFlushHead.IsInvalid"); + + MemoryCacheStore cacheStore = _cacheExpires.MemoryCacheStore; + ExpiresEntryRef current = inFlushHead; + ExpiresEntryRef next; + while (!current.IsInvalid) + { + entries = (_pages[(current.PageIndex)]._entries); + entryIndex = current.Index; + + next = entries[entryIndex]._next; + + cacheEntry = entries[entryIndex]._cacheEntry; + entries[entryIndex]._cacheEntry = null; + Dbg.Assert(cacheEntry.ExpiresEntryRef.IsInvalid, "cacheEntry.ExpiresEntryRef.IsInvalid"); + cacheStore.Remove(cacheEntry, cacheEntry, CacheEntryRemovedReason.Expired); + + current = next; + } + + try + { + if (useInsertBlock) + { + _cacheExpires.MemoryCacheStore.BlockInsert(); + } + + lock (this) + { + current = inFlushHead; + while (!current.IsInvalid) + { + entries = (_pages[(current.PageIndex)]._entries); + entryIndex = current.Index; + + next = entries[entryIndex]._next; + + _cEntriesInFlush--; + AddExpiresEntryToFreeList(current); + + current = next; + } + + Dbg.Assert(_cEntriesInFlush == 0, "_cEntriesInFlush == 0"); + _blockReduce = false; + Reduce(); + + Dbg.Trace("CacheExpiresFlushTotal", "FlushExpiredItems flushed " + flushed + + " expired items, bucket=" + _bucket + "; Time=" + Dbg.FormatLocalDate(DateTime.Now)); + + Dbg.Validate("CacheValidateExpires", this); + Dbg.Dump("CacheExpiresFlush", this); + } + } + finally + { + if (useInsertBlock) + { + _cacheExpires.MemoryCacheStore.UnblockInsert(); + } + } + + return flushed; + } + } + + internal sealed class CacheExpires + { + internal static readonly TimeSpan MIN_UPDATE_DELTA = new TimeSpan(0, 0, 1); + internal static readonly TimeSpan MIN_FLUSH_INTERVAL = new TimeSpan(0, 0, 1); + internal static readonly TimeSpan _tsPerBucket = new TimeSpan(0, 0, 20); + + private const int NUMBUCKETS = 30; + private static readonly TimeSpan s_tsPerCycle = new TimeSpan(NUMBUCKETS * _tsPerBucket.Ticks); + + private readonly MemoryCacheStore _cacheStore; + private readonly ExpiresBucket[] _buckets; + private GCHandleRef _timerHandleRef; + private DateTime _utcLastFlush; + private int _inFlush; + + internal CacheExpires(MemoryCacheStore cacheStore) + { + Dbg.Assert(NUMBUCKETS < Byte.MaxValue); + + DateTime utcNow = DateTime.UtcNow; + + _cacheStore = cacheStore; + _buckets = new ExpiresBucket[NUMBUCKETS]; + for (byte b = 0; b < _buckets.Length; b++) + { + _buckets[b] = new ExpiresBucket(this, b, utcNow); + } + } + + private int UtcCalcExpiresBucket(DateTime utcDate) + { + long ticksFromCycleStart = utcDate.Ticks % s_tsPerCycle.Ticks; + int bucket = (int)(((ticksFromCycleStart / _tsPerBucket.Ticks) + 1) % NUMBUCKETS); + + return bucket; + } + + private int FlushExpiredItems(bool checkDelta, bool useInsertBlock) + { + int flushed = 0; + + if (Interlocked.Exchange(ref _inFlush, 1) == 0) + { + try + { + if (_timerHandleRef == null) + { + return 0; + } + DateTime utcNow = DateTime.UtcNow; + if (!checkDelta || utcNow - _utcLastFlush >= MIN_FLUSH_INTERVAL || utcNow < _utcLastFlush) + { + _utcLastFlush = utcNow; + foreach (ExpiresBucket bucket in _buckets) + { + flushed += bucket.FlushExpiredItems(utcNow, useInsertBlock); + } + + Dbg.Trace("CacheExpiresFlushTotal", "FlushExpiredItems flushed a total of " + flushed + " items; Time=" + Dbg.FormatLocalDate(DateTime.Now)); + Dbg.Dump("CacheExpiresFlush", this); + } + } + finally + { + Interlocked.Exchange(ref _inFlush, 0); + } + } + + return flushed; + } + + internal int FlushExpiredItems(bool useInsertBlock) + { + return FlushExpiredItems(true, useInsertBlock); + } + + private void TimerCallback(object state) + { + FlushExpiredItems(false, false); + } + + internal void EnableExpirationTimer(bool enable) + { + if (enable) + { + if (_timerHandleRef == null) + { + DateTime utcNow = DateTime.UtcNow; + TimeSpan due = _tsPerBucket - (new TimeSpan(utcNow.Ticks % _tsPerBucket.Ticks)); + Timer timer; + // Don't capture the current ExecutionContext and its AsyncLocals onto the timer causing them to live forever + bool restoreFlow = false; + try + { + if (!ExecutionContext.IsFlowSuppressed()) + { + ExecutionContext.SuppressFlow(); + restoreFlow = true; + } + + timer = new Timer(new TimerCallback(this.TimerCallback), null, + due.Ticks / TimeSpan.TicksPerMillisecond, _tsPerBucket.Ticks / TimeSpan.TicksPerMillisecond); + } + finally + { + // Restore the current ExecutionContext + if (restoreFlow) + ExecutionContext.RestoreFlow(); + } + _timerHandleRef = new GCHandleRef(timer); + + Dbg.Trace("Cache", "Cache expiration timer created."); + } + } + else + { + GCHandleRef timerHandleRef = _timerHandleRef; + if (timerHandleRef != null && Interlocked.CompareExchange(ref _timerHandleRef, null, timerHandleRef) == timerHandleRef) + { + timerHandleRef.Dispose(); + + Dbg.Trace("Cache", "Cache expiration timer disposed."); + while (_inFlush != 0) + { + Thread.Sleep(100); + } + } + } + } + + internal MemoryCacheStore MemoryCacheStore + { + get + { + return _cacheStore; + } + } + + internal void Add(MemoryCacheEntry cacheEntry) + { + DateTime utcNow = DateTime.UtcNow; + if (utcNow > cacheEntry.UtcAbsExp) + { + cacheEntry.UtcAbsExp = utcNow; + } + + int bucket = UtcCalcExpiresBucket(cacheEntry.UtcAbsExp); + _buckets[bucket].AddCacheEntry(cacheEntry); + } + + internal void Remove(MemoryCacheEntry cacheEntry) + { + byte bucket = cacheEntry.ExpiresBucket; + if (bucket != 0xff) + { + _buckets[bucket].RemoveCacheEntry(cacheEntry); + } + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Grandfathered suppression from original caching code checkin")] + internal void UtcUpdate(MemoryCacheEntry cacheEntry, DateTime utcNewExpires) + { + int oldBucket = cacheEntry.ExpiresBucket; + int newBucket = UtcCalcExpiresBucket(utcNewExpires); + + if (oldBucket != newBucket) + { + Dbg.Trace("CacheExpiresUpdate", + "Updating item " + cacheEntry.Key + " from bucket " + oldBucket + " to new bucket " + newBucket); + + if (oldBucket != 0xff) + { + _buckets[oldBucket].RemoveCacheEntry(cacheEntry); + cacheEntry.UtcAbsExp = utcNewExpires; + _buckets[newBucket].AddCacheEntry(cacheEntry); + } + } + else + { + if (oldBucket != 0xff) + { + _buckets[oldBucket].UtcUpdateCacheEntry(cacheEntry, utcNewExpires); + } + } + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheItem.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheItem.cs new file mode 100644 index 0000000000..73e7e29bcb --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheItem.cs @@ -0,0 +1,32 @@ +// 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; + +namespace System.Runtime.Caching +{ + public class CacheItem + { + public string Key { get; set; } + public object Value { get; set; } + public string RegionName { get; set; } + + private CacheItem() { } // hide default constructor + + public CacheItem(string key) + { + Key = key; + } + + public CacheItem(string key, object value) : this(key) + { + Value = value; + } + + public CacheItem(string key, object value, string regionName) : this(key, value) + { + RegionName = regionName; + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheItemPolicy.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheItemPolicy.cs new file mode 100644 index 0000000000..0803ba8f76 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheItemPolicy.cs @@ -0,0 +1,68 @@ +// 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.Collections.ObjectModel; + +namespace System.Runtime.Caching +{ + public class CacheItemPolicy + { + private DateTimeOffset _absExpiry; + private TimeSpan _sldExpiry; + private Collection _changeMonitors; + private CacheItemPriority _priority; + private CacheEntryRemovedCallback _removedCallback; + private CacheEntryUpdateCallback _updateCallback; + + public DateTimeOffset AbsoluteExpiration + { + get { return _absExpiry; } + set { _absExpiry = value; } + } + + public Collection ChangeMonitors + { + get + { + if (_changeMonitors == null) + { + _changeMonitors = new Collection(); + } + return _changeMonitors; + } + } + + public CacheItemPriority Priority + { + get { return _priority; } + set { _priority = value; } + } + + public CacheEntryRemovedCallback RemovedCallback + { + get { return _removedCallback; } + set { _removedCallback = value; } + } + + public TimeSpan SlidingExpiration + { + get { return _sldExpiry; } + set { _sldExpiry = value; } + } + + public CacheEntryUpdateCallback UpdateCallback + { + get { return _updateCallback; } + set { _updateCallback = value; } + } + + public CacheItemPolicy() + { + _absExpiry = ObjectCache.InfiniteAbsoluteExpiration; + _sldExpiry = ObjectCache.NoSlidingExpiration; + _priority = CacheItemPriority.Default; + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheItemPriority.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheItemPriority.cs new file mode 100644 index 0000000000..1d0b98e61e --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheItemPriority.cs @@ -0,0 +1,14 @@ +// 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; + +namespace System.Runtime.Caching +{ + public enum CacheItemPriority + { + Default = 0, + NotRemovable + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheMemoryMonitor.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheMemoryMonitor.cs new file mode 100644 index 0000000000..bc30c0554c --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheMemoryMonitor.cs @@ -0,0 +1,298 @@ +// 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.Runtime.Caching.Configuration; +using System.Runtime.Caching.Hosting; +using System.Diagnostics; +using System.Security; +using System.Security.Permissions; +using System.Threading; + +namespace System.Runtime.Caching +{ + // CacheMemoryMonitor uses the internal System.SizedReference type to determine + // the size of the cache itselt, and helps us know when to drop entries to avoid + // exceeding the cache's memory limit. The limit is configurable (see ConfigUtil.cs). + internal sealed class CacheMemoryMonitor : MemoryMonitor, IDisposable + { + private const long PRIVATE_BYTES_LIMIT_2GB = 800 * MEGABYTE; + private const long PRIVATE_BYTES_LIMIT_3GB = 1800 * MEGABYTE; + private const long PRIVATE_BYTES_LIMIT_64BIT = 1L * TERABYTE; + private const int SAMPLE_COUNT = 2; + + private static IMemoryCacheManager s_memoryCacheManager; + private static long s_autoPrivateBytesLimit = -1; + private static long s_effectiveProcessMemoryLimit = -1; + + private MemoryCache _memoryCache; + private long[] _cacheSizeSamples; + private DateTime[] _cacheSizeSampleTimes; + private int _idx; + private SRefMultiple _sizedRefMultiple; + private int _gen2Count; + private long _memoryLimit; + + internal long MemoryLimit + { + get { return _memoryLimit; } + } + + private CacheMemoryMonitor() + { + // hide default ctor + } + + internal CacheMemoryMonitor(MemoryCache memoryCache, int cacheMemoryLimitMegabytes) + { + _memoryCache = memoryCache; + _gen2Count = GC.CollectionCount(2); + _cacheSizeSamples = new long[SAMPLE_COUNT]; + _cacheSizeSampleTimes = new DateTime[SAMPLE_COUNT]; + if (memoryCache.UseMemoryCacheManager) + { + InitMemoryCacheManager(); // This magic thing connects us to ObjectCacheHost magically. :/ + } + InitDisposableMembers(cacheMemoryLimitMegabytes); + } + + private void InitDisposableMembers(int cacheMemoryLimitMegabytes) + { + bool dispose = true; + try + { + _sizedRefMultiple = new SRefMultiple(_memoryCache.AllSRefTargets); + SetLimit(cacheMemoryLimitMegabytes); + InitHistory(); + dispose = false; + } + finally + { + if (dispose) + { + Dispose(); + } + } + } + + // Auto-generate the private bytes limit: + // - On 64bit, the auto value is MIN(60% physical_ram, 1 TB) + // - On x86, for 2GB, the auto value is MIN(60% physical_ram, 800 MB) + // - On x86, for 3GB, the auto value is MIN(60% physical_ram, 1800 MB) + // + // - If it's not a hosted environment (e.g. console app), the 60% in the above + // formulas will become 100% because in un-hosted environment we don't launch + // other processes such as compiler, etc. + private static long AutoPrivateBytesLimit + { + get + { + long memoryLimit = s_autoPrivateBytesLimit; + if (memoryLimit == -1) + { + bool is64bit = (IntPtr.Size == 8); + + long totalPhysical = TotalPhysical; + long totalVirtual = TotalVirtual; + if (totalPhysical != 0) + { + long recommendedPrivateByteLimit; + if (is64bit) + { + recommendedPrivateByteLimit = PRIVATE_BYTES_LIMIT_64BIT; + } + else + { + // Figure out if it's 2GB or 3GB + + if (totalVirtual > 2 * GIGABYTE) + { + recommendedPrivateByteLimit = PRIVATE_BYTES_LIMIT_3GB; + } + else + { + recommendedPrivateByteLimit = PRIVATE_BYTES_LIMIT_2GB; + } + } + + // use 60% of physical RAM + long usableMemory = totalPhysical * 3 / 5; + memoryLimit = Math.Min(usableMemory, recommendedPrivateByteLimit); + } + else + { + // If GlobalMemoryStatusEx fails, we'll use these as our auto-gen private bytes limit + memoryLimit = is64bit ? PRIVATE_BYTES_LIMIT_64BIT : PRIVATE_BYTES_LIMIT_2GB; + } + Interlocked.Exchange(ref s_autoPrivateBytesLimit, memoryLimit); + } + + return memoryLimit; + } + } + + public void Dispose() + { + SRefMultiple sref = _sizedRefMultiple; + if (sref != null && Interlocked.CompareExchange(ref _sizedRefMultiple, null, sref) == sref) + { + sref.Dispose(); + } + IMemoryCacheManager memoryCacheManager = s_memoryCacheManager; + if (memoryCacheManager != null) + { + memoryCacheManager.ReleaseCache(_memoryCache); + } + } + + internal static long EffectiveProcessMemoryLimit + { + get + { + long memoryLimit = s_effectiveProcessMemoryLimit; + if (memoryLimit == -1) + { + memoryLimit = AutoPrivateBytesLimit; + Interlocked.Exchange(ref s_effectiveProcessMemoryLimit, memoryLimit); + } + return memoryLimit; + } + } + + protected override int GetCurrentPressure() + { + // Call GetUpdatedTotalCacheSize to update the total + // cache size, if there has been a recent Gen 2 Collection. + // This update must happen, otherwise the CacheManager won't + // know the total cache size. + int gen2Count = GC.CollectionCount(2); + SRefMultiple sref = _sizedRefMultiple; + if (gen2Count != _gen2Count && sref != null) + { + // update _gen2Count + _gen2Count = gen2Count; + + // the SizedRef is only updated after a Gen2 Collection + + // increment the index (it's either 1 or 0) + Dbg.Assert(SAMPLE_COUNT == 2); + _idx = _idx ^ 1; + // remember the sample time + _cacheSizeSampleTimes[_idx] = DateTime.UtcNow; + // remember the sample value + _cacheSizeSamples[_idx] = sref.ApproximateSize; +#if DEBUG + Dbg.Trace("MemoryCacheStats", "SizedRef.ApproximateSize=" + _cacheSizeSamples[_idx]); +#endif + IMemoryCacheManager memoryCacheManager = s_memoryCacheManager; + if (memoryCacheManager != null) + { + memoryCacheManager.UpdateCacheSize(_cacheSizeSamples[_idx], _memoryCache); + } + } + + // if there's no memory limit, then there's nothing more to do + if (_memoryLimit <= 0) + { + return 0; + } + + long cacheSize = _cacheSizeSamples[_idx]; + + // use _memoryLimit as an upper bound so that pressure is a percentage (between 0 and 100, inclusive). + if (cacheSize > _memoryLimit) + { + cacheSize = _memoryLimit; + } + + // PerfCounter: Cache Percentage Process Memory Limit Used + // = memory used by this process / process memory limit at pressureHigh + // Set private bytes used in kilobytes because the counter is a DWORD + + // PerfCounters.SetCounter(AppPerfCounter.CACHE_PERCENT_PROC_MEM_LIMIT_USED, (int)(cacheSize >> KILOBYTE_SHIFT)); + + int result = (int)(cacheSize * 100 / _memoryLimit); + return result; + } + + internal override int GetPercentToTrim(DateTime lastTrimTime, int lastTrimPercent) + { + int percent = 0; + if (IsAboveHighPressure()) + { + long cacheSize = _cacheSizeSamples[_idx]; + if (cacheSize > _memoryLimit) + { + percent = Math.Min(100, (int)((cacheSize - _memoryLimit) * 100L / cacheSize)); + } + +#if PERF + Debug.WriteLine(String.Format("CacheMemoryMonitor.GetPercentToTrim: percent={0:N}, lastTrimPercent={1:N}\n", + percent, + lastTrimPercent)); +#endif + } + return percent; + } + + internal void SetLimit(int cacheMemoryLimitMegabytes) + { + long cacheMemoryLimit = cacheMemoryLimitMegabytes; + cacheMemoryLimit = cacheMemoryLimit << MEGABYTE_SHIFT; + + _memoryLimit = 0; + + // never override what the user specifies as the limit; + // only call AutoPrivateBytesLimit when the user does not specify one. + if (cacheMemoryLimit == 0 && _memoryLimit == 0) + { + // Zero means we impose a limit + _memoryLimit = EffectiveProcessMemoryLimit; + } + else if (cacheMemoryLimit != 0 && _memoryLimit != 0) + { + // Take the min of "cache memory limit" and the host's "process memory limit". + _memoryLimit = Math.Min(_memoryLimit, cacheMemoryLimit); + } + else if (cacheMemoryLimit != 0) + { + // _memoryLimit is 0, but "cache memory limit" is non-zero, so use it as the limit + _memoryLimit = cacheMemoryLimit; + } + + Dbg.Trace("MemoryCacheStats", "CacheMemoryMonitor.SetLimit: _memoryLimit=" + (_memoryLimit >> MEGABYTE_SHIFT) + "Mb"); + + if (_memoryLimit > 0) + { + _pressureHigh = 100; + _pressureLow = 80; + } + else + { + _pressureHigh = 99; + _pressureLow = 97; + } + + Dbg.Trace("MemoryCacheStats", "CacheMemoryMonitor.SetLimit: _pressureHigh=" + _pressureHigh + + ", _pressureLow=" + _pressureLow); + } + + private static void InitMemoryCacheManager() + { + if (s_memoryCacheManager == null) + { + IMemoryCacheManager memoryCacheManager = null; + IServiceProvider host = ObjectCache.Host; + if (host != null) + { + memoryCacheManager = host.GetService(typeof(IMemoryCacheManager)) as IMemoryCacheManager; + } + if (memoryCacheManager != null) + { + Interlocked.CompareExchange(ref s_memoryCacheManager, memoryCacheManager, null); + } + } + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheUsage.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheUsage.cs new file mode 100644 index 0000000000..6d9e57d383 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/CacheUsage.cs @@ -0,0 +1,964 @@ +// 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.Collections; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using System.Security; +using System.Text; +using System.Threading; + +namespace System.Runtime.Caching +{ + internal struct UsageEntryRef + { + static internal readonly UsageEntryRef INVALID = new UsageEntryRef(0, 0); + + private const uint ENTRY_MASK = 0x000000ffu; + private const uint PAGE_MASK = 0xffffff00u; + private const int PAGE_SHIFT = 8; + + private uint _ref; + + internal UsageEntryRef(int pageIndex, int entryIndex) + { + Dbg.Assert((pageIndex & 0x00ffffff) == pageIndex, "(pageIndex & 0x00ffffff) == pageIndex"); + Dbg.Assert((Math.Abs(entryIndex) & ENTRY_MASK) == (Math.Abs(entryIndex)), "(Math.Abs(entryIndex) & ENTRY_MASK) == Math.Abs(entryIndex)"); + Dbg.Assert(entryIndex != 0 || pageIndex == 0, "entryIndex != 0 || pageIndex == 0"); + + _ref = ((((uint)pageIndex) << PAGE_SHIFT) | (((uint)(entryIndex)) & ENTRY_MASK)); + } + + public override bool Equals(object value) + { + if (value is UsageEntryRef) + { + return _ref == ((UsageEntryRef)value)._ref; + } + + return false; + } + public static bool operator ==(UsageEntryRef r1, UsageEntryRef r2) + { + return r1._ref == r2._ref; + } + + public static bool operator !=(UsageEntryRef r1, UsageEntryRef r2) + { + return r1._ref != r2._ref; + } + + public override int GetHashCode() + { + return (int)_ref; + } + + internal int PageIndex + { + get + { + int result = (int)(_ref >> PAGE_SHIFT); + return result; + } + } + + internal int Ref1Index + { + get + { + int result = (int)(sbyte)(_ref & ENTRY_MASK); + Dbg.Assert(result > 0, "result > 0"); + return result; + } + } + + internal int Ref2Index + { + get + { + int result = (int)(sbyte)(_ref & ENTRY_MASK); + Dbg.Assert(result < 0, "result < 0"); + return -result; + } + } + + internal bool IsRef1 + { + get + { + return ((int)(sbyte)(_ref & ENTRY_MASK)) > 0; + } + } + + internal bool IsRef2 + { + get + { + return ((int)(sbyte)(_ref & ENTRY_MASK)) < 0; + } + } + + internal bool IsInvalid + { + get + { + return _ref == 0; + } + } + } + + internal struct UsageEntryLink + { + internal UsageEntryRef _next; + internal UsageEntryRef _prev; + } + + [SuppressMessage("Microsoft.Portability", "CA1900:ValueTypeFieldsShouldBePortable", Justification = "Grandfathered suppression from original caching code checkin")] + [StructLayout(LayoutKind.Explicit)] + internal struct UsageEntry + { + [FieldOffset(0)] + internal UsageEntryLink _ref1; + + [FieldOffset(4)] + internal int _cFree; + + [FieldOffset(8)] + internal UsageEntryLink _ref2; + + [FieldOffset(16)] + internal DateTime _utcDate; + + [FieldOffset(24)] + internal MemoryCacheEntry _cacheEntry; + } + + internal struct UsagePage + { + internal UsageEntry[] _entries; + internal int _pageNext; + internal int _pagePrev; + } + + internal struct UsagePageList + { + internal int _head; + internal int _tail; + } + + internal sealed class UsageBucket + { + private const int NUM_ENTRIES = 127; + private const int LENGTH_ENTRIES = 128; + + private const int MIN_PAGES_INCREMENT = 10; + private const int MAX_PAGES_INCREMENT = 340; + private const double MIN_LOAD_FACTOR = 0.5; + + private CacheUsage _cacheUsage; + private byte _bucket; + + private UsagePage[] _pages; + private int _cEntriesInUse; + private int _cPagesInUse; + private int _cEntriesInFlush; + private int _minEntriesInUse; + + private UsagePageList _freePageList; + private UsagePageList _freeEntryList; + + private UsageEntryRef _lastRefHead; + private UsageEntryRef _lastRefTail; + private UsageEntryRef _addRef2Head; + + private bool _blockReduce; + + internal UsageBucket(CacheUsage cacheUsage, byte bucket) + { + _cacheUsage = cacheUsage; + _bucket = bucket; + InitZeroPages(); + } + + private void InitZeroPages() + { + Dbg.Assert(_cPagesInUse == 0, "_cPagesInUse == 0"); + Dbg.Assert(_cEntriesInUse == 0, "_cEntriesInUse == 0"); + Dbg.Assert(_cEntriesInFlush == 0, "_cEntriesInFlush == 0"); + Dbg.Assert(_lastRefHead.IsInvalid, "_lastRefHead.IsInvalid"); + Dbg.Assert(_lastRefTail.IsInvalid, "_lastRefTail.IsInvalid"); + Dbg.Assert(_addRef2Head.IsInvalid, "_addRef2Head.IsInvalid"); + + _pages = null; + _minEntriesInUse = -1; + _freePageList._head = -1; + _freePageList._tail = -1; + _freeEntryList._head = -1; + _freeEntryList._tail = -1; + } + + private void AddToListHead(int pageIndex, ref UsagePageList list) + { + Dbg.Assert((list._head == -1) == (list._tail == -1), "(list._head == -1) == (list._tail == -1)"); + + (_pages[(pageIndex)]._pagePrev) = -1; + (_pages[(pageIndex)]._pageNext) = list._head; + if (list._head != -1) + { + Dbg.Assert((_pages[(list._head)]._pagePrev) == -1, "PagePrev(list._head) == -1"); + (_pages[(list._head)]._pagePrev) = pageIndex; + } + else + { + list._tail = pageIndex; + } + + list._head = pageIndex; + } + + private void AddToListTail(int pageIndex, ref UsagePageList list) + { + Dbg.Assert((list._head == -1) == (list._tail == -1), "(list._head == -1) == (list._tail == -1)"); + + (_pages[(pageIndex)]._pageNext) = -1; + (_pages[(pageIndex)]._pagePrev) = list._tail; + if (list._tail != -1) + { + Dbg.Assert((_pages[(list._tail)]._pageNext) == -1, "PageNext(list._tail) == -1"); + (_pages[(list._tail)]._pageNext) = pageIndex; + } + else + { + list._head = pageIndex; + } + + list._tail = pageIndex; + } + + private int RemoveFromListHead(ref UsagePageList list) + { + Dbg.Assert(list._head != -1, "list._head != -1"); + + int oldHead = list._head; + RemoveFromList(oldHead, ref list); + return oldHead; + } + + private void RemoveFromList(int pageIndex, ref UsagePageList list) + { + Dbg.Assert((list._head == -1) == (list._tail == -1), "(list._head == -1) == (list._tail == -1)"); + + if ((_pages[(pageIndex)]._pagePrev) != -1) + { + Dbg.Assert((_pages[((_pages[(pageIndex)]._pagePrev))]._pageNext) == pageIndex, "PageNext(PagePrev(pageIndex)) == pageIndex"); + (_pages[((_pages[(pageIndex)]._pagePrev))]._pageNext) = (_pages[(pageIndex)]._pageNext); + } + else + { + Dbg.Assert(list._head == pageIndex, "list._head == pageIndex"); + list._head = (_pages[(pageIndex)]._pageNext); + } + + if ((_pages[(pageIndex)]._pageNext) != -1) + { + Dbg.Assert((_pages[((_pages[(pageIndex)]._pageNext))]._pagePrev) == pageIndex, "PagePrev(PageNext(pageIndex)) == pageIndex"); + (_pages[((_pages[(pageIndex)]._pageNext))]._pagePrev) = (_pages[(pageIndex)]._pagePrev); + } + else + { + Dbg.Assert(list._tail == pageIndex, "list._tail == pageIndex"); + list._tail = (_pages[(pageIndex)]._pagePrev); + } + + (_pages[(pageIndex)]._pagePrev) = -1; + (_pages[(pageIndex)]._pageNext) = -1; + } + + private void MoveToListHead(int pageIndex, ref UsagePageList list) + { + Dbg.Assert(list._head != -1, "list._head != -1"); + Dbg.Assert(list._tail != -1, "list._tail != -1"); + + if (list._head == pageIndex) + return; + + RemoveFromList(pageIndex, ref list); + AddToListHead(pageIndex, ref list); + } + + private void MoveToListTail(int pageIndex, ref UsagePageList list) + { + Dbg.Assert(list._head != -1, "list._head != -1"); + Dbg.Assert(list._tail != -1, "list._tail != -1"); + + if (list._tail == pageIndex) + return; + + RemoveFromList(pageIndex, ref list); + AddToListTail(pageIndex, ref list); + } + + private void UpdateMinEntries() + { + if (_cPagesInUse <= 1) + { + _minEntriesInUse = -1; + } + else + { + int capacity = _cPagesInUse * NUM_ENTRIES; + Dbg.Assert(capacity > 0, "capacity > 0"); + Dbg.Assert(MIN_LOAD_FACTOR < 1.0, "MIN_LOAD_FACTOR < 1.0"); + + _minEntriesInUse = (int)(capacity * MIN_LOAD_FACTOR); + + if ((_minEntriesInUse - 1) > ((_cPagesInUse - 1) * NUM_ENTRIES)) + { + _minEntriesInUse = -1; + } + } + } + + private void RemovePage(int pageIndex) + { + Dbg.Assert((((_pages[(pageIndex)]._entries))[0]._cFree) == NUM_ENTRIES, "FreeEntryCount(EntriesI(pageIndex)) == NUM_ENTRIES"); + + RemoveFromList(pageIndex, ref _freeEntryList); + AddToListHead(pageIndex, ref _freePageList); + + Dbg.Assert((_pages[(pageIndex)]._entries) != null, "EntriesI(pageIndex) != null"); + (_pages[(pageIndex)]._entries) = null; + + _cPagesInUse--; + if (_cPagesInUse == 0) + { + InitZeroPages(); + } + else + { + UpdateMinEntries(); + } + } + + private UsageEntryRef GetFreeUsageEntry() + { + Dbg.Assert(_freeEntryList._head >= 0, "_freeEntryList._head >= 0"); + int pageIndex = _freeEntryList._head; + + UsageEntry[] entries = (_pages[(pageIndex)]._entries); + int entryIndex = ((entries)[0]._ref1._next).Ref1Index; + + ((entries)[0]._ref1._next) = entries[entryIndex]._ref1._next; + ((entries)[0]._cFree)--; + if (((entries)[0]._cFree) == 0) + { + Dbg.Assert(((entries)[0]._ref1._next).IsInvalid, "FreeEntryHead(entries).IsInvalid"); + RemoveFromList(pageIndex, ref _freeEntryList); + } + return new UsageEntryRef(pageIndex, entryIndex); + } + + private void AddUsageEntryToFreeList(UsageEntryRef entryRef) + { + Dbg.Assert(entryRef.IsRef1, "entryRef.IsRef1"); + + UsageEntry[] entries = (_pages[(entryRef.PageIndex)]._entries); + int entryIndex = entryRef.Ref1Index; + + Dbg.Assert(entries[entryIndex]._cacheEntry == null, "entries[entryIndex]._cacheEntry == null"); + entries[entryIndex]._utcDate = DateTime.MinValue; + entries[entryIndex]._ref1._prev = UsageEntryRef.INVALID; + entries[entryIndex]._ref2._next = UsageEntryRef.INVALID; + entries[entryIndex]._ref2._prev = UsageEntryRef.INVALID; + + entries[entryIndex]._ref1._next = ((entries)[0]._ref1._next); + ((entries)[0]._ref1._next) = entryRef; + + _cEntriesInUse--; + int pageIndex = entryRef.PageIndex; + ((entries)[0]._cFree)++; + if (((entries)[0]._cFree) == 1) + { + AddToListHead(pageIndex, ref _freeEntryList); + } + else if (((entries)[0]._cFree) == NUM_ENTRIES) + { + RemovePage(pageIndex); + } + } + + private void Expand() + { + Dbg.Assert(_cPagesInUse * NUM_ENTRIES == _cEntriesInUse, "_cPagesInUse * NUM_ENTRIES == _cEntriesInUse"); + Dbg.Assert(_freeEntryList._head == -1, "_freeEntryList._head == -1"); + Dbg.Assert(_freeEntryList._tail == -1, "_freeEntryList._tail == -1"); + + if (_freePageList._head == -1) + { + int oldLength; + if (_pages == null) + { + oldLength = 0; + } + else + { + oldLength = _pages.Length; + } + + Dbg.Assert(_cPagesInUse == oldLength, "_cPagesInUse == oldLength"); + Dbg.Assert(_cEntriesInUse == oldLength * NUM_ENTRIES, "_cEntriesInUse == oldLength * ExpiresEntryRef.NUM_ENTRIES"); + + int newLength = oldLength * 2; + newLength = Math.Max(oldLength + MIN_PAGES_INCREMENT, newLength); + newLength = Math.Min(newLength, oldLength + MAX_PAGES_INCREMENT); + Dbg.Assert(newLength > oldLength, "newLength > oldLength"); + + UsagePage[] newPages = new UsagePage[newLength]; + + for (int i = 0; i < oldLength; i++) + { + newPages[i] = _pages[i]; + } + + for (int i = oldLength; i < newPages.Length; i++) + { + newPages[i]._pagePrev = i - 1; + newPages[i]._pageNext = i + 1; + } + + newPages[oldLength]._pagePrev = -1; + newPages[newPages.Length - 1]._pageNext = -1; + + _freePageList._head = oldLength; + _freePageList._tail = newPages.Length - 1; + + _pages = newPages; + } + + int pageIndex = RemoveFromListHead(ref _freePageList); + AddToListHead(pageIndex, ref _freeEntryList); + + UsageEntry[] entries = new UsageEntry[LENGTH_ENTRIES]; + ((entries)[0]._cFree) = NUM_ENTRIES; + + for (int i = 0; i < entries.Length - 1; i++) + { + entries[i]._ref1._next = new UsageEntryRef(pageIndex, i + 1); + } + entries[entries.Length - 1]._ref1._next = UsageEntryRef.INVALID; + + (_pages[(pageIndex)]._entries) = entries; + + _cPagesInUse++; + UpdateMinEntries(); + } + + private void Reduce() + { + if (_cEntriesInUse >= _minEntriesInUse || _blockReduce) + return; + + Dbg.Assert(_freeEntryList._head != -1, "_freeEntryList._head != -1"); + Dbg.Assert(_freeEntryList._tail != -1, "_freeEntryList._tail != -1"); + Dbg.Assert(_freeEntryList._head != _freeEntryList._tail, "_freeEntryList._head != _freeEntryList._tail"); + + int meanFree = (int)(NUM_ENTRIES - (NUM_ENTRIES * MIN_LOAD_FACTOR)); + int pageIndexLast = _freeEntryList._tail; + int pageIndexCurrent = _freeEntryList._head; + int pageIndexNext; + UsageEntry[] entries; + + for (; ;) + { + pageIndexNext = (_pages[(pageIndexCurrent)]._pageNext); + + if ((((_pages[(pageIndexCurrent)]._entries))[0]._cFree) > meanFree) + { + MoveToListTail(pageIndexCurrent, ref _freeEntryList); + } + else + { + MoveToListHead(pageIndexCurrent, ref _freeEntryList); + } + + if (pageIndexCurrent == pageIndexLast) + break; + + pageIndexCurrent = pageIndexNext; + } + + for (; ;) + { + if (_freeEntryList._tail == -1) + break; + + entries = (_pages[(_freeEntryList._tail)]._entries); + Dbg.Assert(((entries)[0]._cFree) > 0, "FreeEntryCount(entries) > 0"); + int availableFreeEntries = (_cPagesInUse * NUM_ENTRIES) - ((entries)[0]._cFree) - _cEntriesInUse; + if (availableFreeEntries < (NUM_ENTRIES - ((entries)[0]._cFree))) + break; + + for (int i = 1; i < entries.Length; i++) + { + if (entries[i]._cacheEntry == null) + continue; + + Dbg.Assert(_freeEntryList._head != _freeEntryList._tail, "_freeEntryList._head != _freeEntryList._tail"); + UsageEntryRef newRef1 = GetFreeUsageEntry(); + UsageEntryRef newRef2 = (new UsageEntryRef((newRef1).PageIndex, -(newRef1).Ref1Index)); + Dbg.Assert(newRef1.PageIndex != _freeEntryList._tail, "newRef1.PageIndex != _freeEntryList._tail"); + + UsageEntryRef oldRef1 = new UsageEntryRef(_freeEntryList._tail, i); + UsageEntryRef oldRef2 = (new UsageEntryRef((oldRef1).PageIndex, -(oldRef1).Ref1Index)); + + MemoryCacheEntry cacheEntry = entries[i]._cacheEntry; + Dbg.Assert(cacheEntry.UsageEntryRef == oldRef1, "cacheEntry.UsageEntryRef == oldRef1"); + cacheEntry.UsageEntryRef = newRef1; + + UsageEntry[] newEntries = (_pages[(newRef1.PageIndex)]._entries); + newEntries[newRef1.Ref1Index] = entries[i]; + + ((entries)[0]._cFree)++; + + UsageEntryRef prev = newEntries[newRef1.Ref1Index]._ref1._prev; + Dbg.Assert(prev != oldRef2, "prev != oldRef2"); + + UsageEntryRef next = newEntries[newRef1.Ref1Index]._ref1._next; + if (next == oldRef2) + { + next = newRef2; + } + + { if ((prev).IsRef1) { (_pages[((prev).PageIndex)]._entries)[(prev).Ref1Index]._ref1._next = (newRef1); } else if ((prev).IsRef2) { (_pages[((prev).PageIndex)]._entries)[(prev).Ref2Index]._ref2._next = (newRef1); } else { _lastRefHead = (newRef1); } }; + { if ((next).IsRef1) { (_pages[((next).PageIndex)]._entries)[(next).Ref1Index]._ref1._prev = (newRef1); } else if ((next).IsRef2) { (_pages[((next).PageIndex)]._entries)[(next).Ref2Index]._ref2._prev = (newRef1); } else { _lastRefTail = (newRef1); } }; + + prev = newEntries[newRef1.Ref1Index]._ref2._prev; + if (prev == oldRef1) + { + prev = newRef1; + } + + next = newEntries[newRef1.Ref1Index]._ref2._next; + Dbg.Assert(next != oldRef1, "next != oldRef1"); + + { if ((prev).IsRef1) { (_pages[((prev).PageIndex)]._entries)[(prev).Ref1Index]._ref1._next = (newRef2); } else if ((prev).IsRef2) { (_pages[((prev).PageIndex)]._entries)[(prev).Ref2Index]._ref2._next = (newRef2); } else { _lastRefHead = (newRef2); } }; + { if ((next).IsRef1) { (_pages[((next).PageIndex)]._entries)[(next).Ref1Index]._ref1._prev = (newRef2); } else if ((next).IsRef2) { (_pages[((next).PageIndex)]._entries)[(next).Ref2Index]._ref2._prev = (newRef2); } else { _lastRefTail = (newRef2); } }; + + if (_addRef2Head == oldRef2) + { + _addRef2Head = newRef2; + } + } + + RemovePage(_freeEntryList._tail); + + Dbg.Validate("CacheValidateUsage", this); + } + } + + internal void AddCacheEntry(MemoryCacheEntry cacheEntry) + { + lock (this) + { + if (_freeEntryList._head == -1) + { + Expand(); + } + + UsageEntryRef freeRef1 = GetFreeUsageEntry(); + UsageEntryRef freeRef2 = (new UsageEntryRef((freeRef1).PageIndex, -(freeRef1).Ref1Index)); + Dbg.Assert(cacheEntry.UsageEntryRef.IsInvalid, "cacheEntry.UsageEntryRef.IsInvalid"); + cacheEntry.UsageEntryRef = freeRef1; + + UsageEntry[] entries = (_pages[(freeRef1.PageIndex)]._entries); + int entryIndex = freeRef1.Ref1Index; + entries[entryIndex]._cacheEntry = cacheEntry; + entries[entryIndex]._utcDate = DateTime.UtcNow; + + entries[entryIndex]._ref1._prev = UsageEntryRef.INVALID; + entries[entryIndex]._ref2._next = _addRef2Head; + if (_lastRefHead.IsInvalid) + { + entries[entryIndex]._ref1._next = freeRef2; + entries[entryIndex]._ref2._prev = freeRef1; + _lastRefTail = freeRef2; + } + else + { + entries[entryIndex]._ref1._next = _lastRefHead; + { if ((_lastRefHead).IsRef1) { (_pages[((_lastRefHead).PageIndex)]._entries)[(_lastRefHead).Ref1Index]._ref1._prev = (freeRef1); } else if ((_lastRefHead).IsRef2) { (_pages[((_lastRefHead).PageIndex)]._entries)[(_lastRefHead).Ref2Index]._ref2._prev = (freeRef1); } else { _lastRefTail = (freeRef1); } }; + + UsageEntryRef next, prev; + if (_addRef2Head.IsInvalid) + { + prev = _lastRefTail; + next = UsageEntryRef.INVALID; + } + else + { + prev = (_pages[(_addRef2Head.PageIndex)]._entries)[_addRef2Head.Ref2Index]._ref2._prev; + next = _addRef2Head; + } + + entries[entryIndex]._ref2._prev = prev; + { if ((prev).IsRef1) { (_pages[((prev).PageIndex)]._entries)[(prev).Ref1Index]._ref1._next = (freeRef2); } else if ((prev).IsRef2) { (_pages[((prev).PageIndex)]._entries)[(prev).Ref2Index]._ref2._next = (freeRef2); } else { _lastRefHead = (freeRef2); } }; + { if ((next).IsRef1) { (_pages[((next).PageIndex)]._entries)[(next).Ref1Index]._ref1._prev = (freeRef2); } else if ((next).IsRef2) { (_pages[((next).PageIndex)]._entries)[(next).Ref2Index]._ref2._prev = (freeRef2); } else { _lastRefTail = (freeRef2); } }; + } + + _lastRefHead = freeRef1; + _addRef2Head = freeRef2; + + _cEntriesInUse++; + + Dbg.Trace("CacheUsageAdd", + "Added item=" + cacheEntry.Key + + ",_bucket=" + _bucket + + ",ref=" + freeRef1); + + Dbg.Validate("CacheValidateUsage", this); + Dbg.Dump("CacheUsageAdd", this); + } + } + + private void RemoveEntryFromLastRefList(UsageEntryRef entryRef) + { + Dbg.Assert(entryRef.IsRef1, "entryRef.IsRef1"); + UsageEntry[] entries = (_pages[(entryRef.PageIndex)]._entries); + int entryIndex = entryRef.Ref1Index; + + UsageEntryRef prev = entries[entryIndex]._ref1._prev; + UsageEntryRef next = entries[entryIndex]._ref1._next; + + { if ((prev).IsRef1) { (_pages[((prev).PageIndex)]._entries)[(prev).Ref1Index]._ref1._next = (next); } else if ((prev).IsRef2) { (_pages[((prev).PageIndex)]._entries)[(prev).Ref2Index]._ref2._next = (next); } else { _lastRefHead = (next); } }; + { if ((next).IsRef1) { (_pages[((next).PageIndex)]._entries)[(next).Ref1Index]._ref1._prev = (prev); } else if ((next).IsRef2) { (_pages[((next).PageIndex)]._entries)[(next).Ref2Index]._ref2._prev = (prev); } else { _lastRefTail = (prev); } }; + + prev = entries[entryIndex]._ref2._prev; + next = entries[entryIndex]._ref2._next; + + UsageEntryRef entryRef2 = (new UsageEntryRef((entryRef).PageIndex, -(entryRef).Ref1Index)); + + { if ((prev).IsRef1) { (_pages[((prev).PageIndex)]._entries)[(prev).Ref1Index]._ref1._next = (next); } else if ((prev).IsRef2) { (_pages[((prev).PageIndex)]._entries)[(prev).Ref2Index]._ref2._next = (next); } else { _lastRefHead = (next); } }; + { if ((next).IsRef1) { (_pages[((next).PageIndex)]._entries)[(next).Ref1Index]._ref1._prev = (prev); } else if ((next).IsRef2) { (_pages[((next).PageIndex)]._entries)[(next).Ref2Index]._ref2._prev = (prev); } else { _lastRefTail = (prev); } }; + + if (_addRef2Head == entryRef2) + { + _addRef2Head = next; + } + } + + internal void RemoveCacheEntry(MemoryCacheEntry cacheEntry) + { + lock (this) + { + UsageEntryRef entryRef = cacheEntry.UsageEntryRef; + if (entryRef.IsInvalid) + return; + + UsageEntry[] entries = (_pages[(entryRef.PageIndex)]._entries); + int entryIndex = entryRef.Ref1Index; + + cacheEntry.UsageEntryRef = UsageEntryRef.INVALID; + entries[entryIndex]._cacheEntry = null; + + RemoveEntryFromLastRefList(entryRef); + + AddUsageEntryToFreeList(entryRef); + + Reduce(); + + Dbg.Trace("CacheUsageRemove", + "Removed item=" + cacheEntry.Key + + ",_bucket=" + _bucket + + ",ref=" + entryRef); + + Dbg.Validate("CacheValidateUsage", this); + Dbg.Dump("CacheUsageRemove", this); + } + } + + internal void UpdateCacheEntry(MemoryCacheEntry cacheEntry) + { + lock (this) + { + UsageEntryRef entryRef = cacheEntry.UsageEntryRef; + if (entryRef.IsInvalid) + return; + + UsageEntry[] entries = (_pages[(entryRef.PageIndex)]._entries); + int entryIndex = entryRef.Ref1Index; + UsageEntryRef entryRef2 = (new UsageEntryRef((entryRef).PageIndex, -(entryRef).Ref1Index)); + + UsageEntryRef prev = entries[entryIndex]._ref2._prev; + UsageEntryRef next = entries[entryIndex]._ref2._next; + + { if ((prev).IsRef1) { (_pages[((prev).PageIndex)]._entries)[(prev).Ref1Index]._ref1._next = (next); } else if ((prev).IsRef2) { (_pages[((prev).PageIndex)]._entries)[(prev).Ref2Index]._ref2._next = (next); } else { _lastRefHead = (next); } }; + { if ((next).IsRef1) { (_pages[((next).PageIndex)]._entries)[(next).Ref1Index]._ref1._prev = (prev); } else if ((next).IsRef2) { (_pages[((next).PageIndex)]._entries)[(next).Ref2Index]._ref2._prev = (prev); } else { _lastRefTail = (prev); } }; + + if (_addRef2Head == entryRef2) + { + _addRef2Head = next; + } + + entries[entryIndex]._ref2 = entries[entryIndex]._ref1; + prev = entries[entryIndex]._ref2._prev; + next = entries[entryIndex]._ref2._next; + + { if ((prev).IsRef1) { (_pages[((prev).PageIndex)]._entries)[(prev).Ref1Index]._ref1._next = (entryRef2); } else if ((prev).IsRef2) { (_pages[((prev).PageIndex)]._entries)[(prev).Ref2Index]._ref2._next = (entryRef2); } else { _lastRefHead = (entryRef2); } }; + { if ((next).IsRef1) { (_pages[((next).PageIndex)]._entries)[(next).Ref1Index]._ref1._prev = (entryRef2); } else if ((next).IsRef2) { (_pages[((next).PageIndex)]._entries)[(next).Ref2Index]._ref2._prev = (entryRef2); } else { _lastRefTail = (entryRef2); } }; + + entries[entryIndex]._ref1._prev = UsageEntryRef.INVALID; + entries[entryIndex]._ref1._next = _lastRefHead; + + { if ((_lastRefHead).IsRef1) { (_pages[((_lastRefHead).PageIndex)]._entries)[(_lastRefHead).Ref1Index]._ref1._prev = (entryRef); } else if ((_lastRefHead).IsRef2) { (_pages[((_lastRefHead).PageIndex)]._entries)[(_lastRefHead).Ref2Index]._ref2._prev = (entryRef); } else { _lastRefTail = (entryRef); } }; + _lastRefHead = entryRef; + + Dbg.Trace("CacheUsageUpdate", + "Updated item=" + cacheEntry.Key + + ",_bucket=" + _bucket + + ",ref=" + entryRef); + + Dbg.Validate("CacheValidateUsage", this); + Dbg.Dump("CacheUsageUpdate", this); + } + } + + internal int FlushUnderUsedItems(int maxFlush, bool force) + { + if (_cEntriesInUse == 0) + return 0; + + Dbg.Assert(maxFlush > 0, "maxFlush is not greater than 0, instead is " + maxFlush); + Dbg.Assert(_cEntriesInFlush == 0, "_cEntriesInFlush == 0"); + + UsageEntryRef inFlushHead = UsageEntryRef.INVALID; + + UsageEntryRef prev, prevNext; + DateTime utcDate; + UsageEntry[] entries; + int entryIndex; + MemoryCacheEntry cacheEntry; + int flushed = 0; + + try + { + _cacheUsage.MemoryCacheStore.BlockInsert(); + + lock (this) + { + Dbg.Assert(_blockReduce == false, "_blockReduce == false"); + + if (_cEntriesInUse == 0) + return 0; + + DateTime utcNow = DateTime.UtcNow; + + for (prev = _lastRefTail; _cEntriesInFlush < maxFlush && !prev.IsInvalid; prev = prevNext) + { + Dbg.Assert(_cEntriesInUse > 0, "_cEntriesInUse > 0"); + + prevNext = (_pages[(prev.PageIndex)]._entries)[prev.Ref2Index]._ref2._prev; + while (prevNext.IsRef1) + { + prevNext = (_pages[(prevNext.PageIndex)]._entries)[prevNext.Ref1Index]._ref1._prev; + } + + entries = (_pages[(prev.PageIndex)]._entries); + entryIndex = prev.Ref2Index; + + if (!force) + { + utcDate = entries[entryIndex]._utcDate; + Dbg.Assert(utcDate != DateTime.MinValue, "utcDate != DateTime.MinValue"); + + if (utcNow - utcDate <= CacheUsage.NEWADD_INTERVAL && utcNow >= utcDate) + continue; + } + + UsageEntryRef prev1 = (new UsageEntryRef((prev).PageIndex, (prev).Ref2Index)); + cacheEntry = entries[entryIndex]._cacheEntry; + Dbg.Assert(cacheEntry.UsageEntryRef == prev1, "cacheEntry.UsageEntryRef == prev1"); + Dbg.Trace("CacheUsageFlushUnderUsedItem", "Flushing underused items, item=" + cacheEntry.Key + ", bucket=" + _bucket); + + cacheEntry.UsageEntryRef = UsageEntryRef.INVALID; + + RemoveEntryFromLastRefList(prev1); + + entries[entryIndex]._ref1._next = inFlushHead; + inFlushHead = prev1; + + flushed++; + _cEntriesInFlush++; + } + + if (flushed == 0) + { + Dbg.Trace("CacheUsageFlushTotal", "Flush(" + maxFlush + "," + force + ") removed " + flushed + + " underused items; Time=" + Dbg.FormatLocalDate(DateTime.Now)); + + return 0; + } + + _blockReduce = true; + } + } + finally + { + _cacheUsage.MemoryCacheStore.UnblockInsert(); + } + + Dbg.Assert(!inFlushHead.IsInvalid, "!inFlushHead.IsInvalid"); + + MemoryCacheStore cacheStore = _cacheUsage.MemoryCacheStore; + UsageEntryRef current = inFlushHead; + UsageEntryRef next; + while (!current.IsInvalid) + { + entries = (_pages[(current.PageIndex)]._entries); + entryIndex = current.Ref1Index; + + next = entries[entryIndex]._ref1._next; + + cacheEntry = entries[entryIndex]._cacheEntry; + entries[entryIndex]._cacheEntry = null; + Dbg.Assert(cacheEntry.UsageEntryRef.IsInvalid, "cacheEntry.UsageEntryRef.IsInvalid"); + cacheStore.Remove(cacheEntry, cacheEntry, CacheEntryRemovedReason.Evicted); + + current = next; + } + + try + { + _cacheUsage.MemoryCacheStore.BlockInsert(); + + lock (this) + { + current = inFlushHead; + while (!current.IsInvalid) + { + entries = (_pages[(current.PageIndex)]._entries); + entryIndex = current.Ref1Index; + + next = entries[entryIndex]._ref1._next; + + _cEntriesInFlush--; + AddUsageEntryToFreeList(current); + + current = next; + } + + Dbg.Assert(_cEntriesInFlush == 0, "_cEntriesInFlush == 0"); + + _blockReduce = false; + Reduce(); + + Dbg.Trace("CacheUsageFlushTotal", "Flush(" + maxFlush + "," + force + ") removed " + flushed + + " underused items; Time=" + Dbg.FormatLocalDate(DateTime.Now)); + + Dbg.Validate("CacheValidateUsage", this); + Dbg.Dump("CacheUsageFlush", this); + } + } + finally + { + _cacheUsage.MemoryCacheStore.UnblockInsert(); + } + + return flushed; + } + } + + internal class CacheUsage + { + internal static readonly TimeSpan NEWADD_INTERVAL = new TimeSpan(0, 0, 10); + internal static readonly TimeSpan CORRELATED_REQUEST_TIMEOUT = new TimeSpan(0, 0, 1); + internal static readonly TimeSpan MIN_LIFETIME_FOR_USAGE = NEWADD_INTERVAL; + private const byte NUMBUCKETS = 1; + private const int MAX_REMOVE = 1024; + + private readonly MemoryCacheStore _cacheStore; + internal readonly UsageBucket[] _buckets; + private int _inFlush; + + internal CacheUsage(MemoryCacheStore cacheStore) + { + _cacheStore = cacheStore; + _buckets = new UsageBucket[NUMBUCKETS]; + for (byte b = 0; b < _buckets.Length; b++) + { + _buckets[b] = new UsageBucket(this, b); + } + } + + internal MemoryCacheStore MemoryCacheStore + { + get + { + return _cacheStore; + } + } + + internal void Add(MemoryCacheEntry cacheEntry) + { + byte bucket = cacheEntry.UsageBucket; + Dbg.Assert(bucket != 0xff, "bucket != 0xff"); + _buckets[bucket].AddCacheEntry(cacheEntry); + } + + internal void Remove(MemoryCacheEntry cacheEntry) + { + byte bucket = cacheEntry.UsageBucket; + if (bucket != 0xff) + { + _buckets[bucket].RemoveCacheEntry(cacheEntry); + } + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Grandfathered suppression from original caching code checkin")] + internal void Update(MemoryCacheEntry cacheEntry) + { + byte bucket = cacheEntry.UsageBucket; + if (bucket != 0xff) + { + _buckets[bucket].UpdateCacheEntry(cacheEntry); + } + } + + internal int FlushUnderUsedItems(int toFlush) + { + int flushed = 0; + + if (Interlocked.Exchange(ref _inFlush, 1) == 0) + { + try + { + foreach (UsageBucket usageBucket in _buckets) + { + int flushedOne = usageBucket.FlushUnderUsedItems(toFlush - flushed, false); + flushed += flushedOne; + if (flushed >= toFlush) + break; + } + + if (flushed < toFlush) + { + foreach (UsageBucket usageBucket in _buckets) + { + int flushedOne = usageBucket.FlushUnderUsedItems(toFlush - flushed, true); + flushed += flushedOne; + if (flushed >= toFlush) + break; + } + } + } + finally + { + Interlocked.Exchange(ref _inFlush, 0); + } + } + + return flushed; + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/ChangeMonitor.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/ChangeMonitor.cs new file mode 100644 index 0000000000..040abad3fe --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/ChangeMonitor.cs @@ -0,0 +1,243 @@ +// 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.Runtime.Caching.Resources; +using System.Diagnostics.CodeAnalysis; +using System.Threading; + +// Every member of this class is thread-safe. +// +// Derived classes begin monitoring during construction, so that a user can know if the +// dependency changed any time after construction. For example, suppose we have a +// FileChangeMonitor class that derives from ChangeMonitor. A user might create an instance +// of FileChangeMonitor for an XML file, and then read the file to populate an object representation. +// The user would then cache the object with the FileChangeMonitor. The user could optionally check the +// HasChanged property of the FileChangeMonitor, to see if the XML file changed while the object +// was being populated, and if it had changed, they could call Dispose and start over, without +// inserting the item into the cache. However, in a multi-threaded environment, for cleaner, easier +// to maintain code, it's usually appropriate to just insert without checking HasChanged, since the +// cache implementer will handle this for you, and the next thread to attempt to get the object +// will recreate and insert it. +// +// The following contract must be followed by derived classes, cache implementers, and users of the +// derived class: +// +// 1. The constructor of a derived class must set UniqueId, begin monitoring for dependency +// changes, and call InitializationComplete before returning. If a dependency changes +// before initialization is complete, for example, if a dependent cache key is not found +// in the cache, the constructor must invoke OnChanged. The constructor can only call +// Dispose after InitializationComplete is called, because Dispose will throw +// InvalidOperationException if initialization is not complete. +// 2. Once constructed, the user must either insert the ChangeMonitor into an ObjectCache, or +// if they're not going to use it, they must call Dispose. +// 3. Once inserted into an ObjectCache, the ObjectCache implementation must ensure that the +// ChangeMonitor is eventually disposed. Even if the insert is invalid, and results in an +// exception being thrown, the ObjectCache implementation must call Dispose. If this we're not +// a requirement, users of the ChangeMonitor would need exception handling around each insert +// into the cache that carefully ensures the dependency is disposed. While this would work, we +// think it is better to put this burden on the ObjectCache implementer, since users are far more +// numerous than cache implementers. +// 4. After the ChangeMonitor is inserted into a cache, the ObjectCache implementer must call +// NotifyOnChanged, passing in an OnChangedCallback. NotifyOnChanged can only be called once, +// and will throw InvalidOperationException on subsequent calls. If the dependency has already +// changed, the OnChangedCallback will be called when NotifyOnChanged is called. Otherwise, the +// OnChangedCallback will be called exactly once, when OnChanged is invoked or when Dispose +// is invoked, which ever happens first. +// 5. The OnChangedCallback provided by the cache implementer should remove the cache entry, and specify +// a reason of CacheEntryRemovedReason.DependencyChanged. Care should be taken to remove the specific +// entry having this dependency, and not it's replacement, which will have the same key. +// 6. In general, it is okay for OnChanged to be called at any time. If OnChanged is called before +// NotifyOnChanged is called, the "state" from the original call to OnChanged will be saved, and the +// callback to NotifyOnChange will be called immediately when NotifyOnChanged is invoked. +// 7. A derived class must implement Dispose(bool disposing) to release all managed and unmanaged +// resources when "disposing" is true. Dispose(true) is only called once, when the instance is +// disposed. The derived class must not call Dispose(true) directly--it should only be called by +// the ChangeMonitor class, when disposed. Although a derived class could implement a finalizer and +// invoke Dispose(false), this is generally not necessary. Dependency monitoring is typically performed +// by a service that maintains a reference to the ChangeMonitor, preventing it from being garbage collected, +// and making finalizers useless. To help prevent leaks, when a dependency changes, OnChanged disposes +// the ChangeMonitor, unless initialization has not yet completed. +// 8. Dispose() must be called, and is designed to be called, in one of the following three ways: +// - The user must call Dispose() if they decide not to insert the ChangeMonitor into a cache. Otherwise, +// the ChangeMonitor will continue monitoring for changes and be unavailable for garbage collection. +// - The cache implementor is responsible for calling Dispose() once an attempt is made to insert it. +// Even if the insert throws, the cache implementor must dispose the dependency. +// Even if the entry is removed, the cache implementor must dispose the dependency. +// - The OnChanged method will automatically call Dispose if initialization is complete. Otherwise, when +// the derived class' constructor calls InitializationComplete, the instance will be automatically disposed. +// +// Before inserted into the cache, the user must ensure the dependency is disposed. Once inserted into the +// cache, the cache implementer must ensure that Dispose is called, even if the insert fails. After being inserted +// into a cache, the user should not dispose the dependency. When Dispose is called, it is treated as if the dependency +// changed, and OnChanged is automatically invoked. +// 9. HasChanged will be true after OnChanged is called by the derived class, regardless of whether an OnChangedCallback has been set +// by a call to NotifyOnChanged. + +namespace System.Runtime.Caching +{ + public abstract class ChangeMonitor : IDisposable + { + private const int INITIALIZED = 0x01; // initialization complete + private const int CHANGED = 0x02; // dependency changed + private const int INVOKED = 0x04; // OnChangedCallback has been invoked + private const int DISPOSED = 0x08; // Dispose(true) called, or about to be called + private readonly static object s_NOT_SET = new object(); + + private SafeBitVector32 _flags; + private OnChangedCallback _onChangedCallback; + private Object _onChangedState = s_NOT_SET; + + // The helper routines (OnChangedHelper and DisposeHelper) are used to prevent + // an infinite loop, where Dispose calls OnChanged and OnChanged calls Dispose. + [SuppressMessage("Microsoft.Performance", "CA1816:DisposeMethodsShouldCallSuppressFinalize", Justification = "Grandfathered suppression from original caching code checkin")] + private void DisposeHelper() + { + // if not initialized, return without doing anything. + if (_flags[INITIALIZED]) + { + if (_flags.ChangeValue(DISPOSED, true)) + { + Dispose(true); + GC.SuppressFinalize(this); + } + } + } + + // The helper routines (OnChangedHelper and DisposeHelper) are used to prevent + // an infinite loop, where Dispose calls OnChanged and OnChanged calls Dispose. + private void OnChangedHelper(Object state) + { + _flags[CHANGED] = true; + + // the callback is only invoked once, after NotifyOnChanged is called, so + // remember "state" on the first call and use it when invoking the callback + Interlocked.CompareExchange(ref _onChangedState, state, s_NOT_SET); + + OnChangedCallback onChangedCallback = _onChangedCallback; + if (onChangedCallback != null) + { + // only invoke the callback once + if (_flags.ChangeValue(INVOKED, true)) + { + onChangedCallback(_onChangedState); + } + } + } + + // + // protected members + // + + // Derived classes must implement this. When "disposing" is true, + // all managed and unmanaged resources are disposed and any references to this + // object are released so that the ChangeMonitor can be garbage collected. + // It is guaranteed that ChangeMonitor.Dispose() will only invoke + // Dispose(bool disposing) once. + protected abstract void Dispose(bool disposing); + + // Derived classes must call InitializationComplete + protected void InitializationComplete() + { + _flags[INITIALIZED] = true; + + // If the dependency has already changed, or someone tried to dispose us, then call Dispose now. + Dbg.Assert(_flags[INITIALIZED], "It is critical that INITIALIZED is set before CHANGED is checked below"); + if (_flags[CHANGED]) + { + Dispose(); + } + } + + // Derived classes call OnChanged when the dependency changes. Optionally, + // they may pass state which will be passed to the OnChangedCallback. The + // OnChangedCallback is only invoked once, and only after NotifyOnChanged is + // called by the cache implementer. OnChanged is also invoked when the instance + // is disposed, but only has an affect if the callback has not already been invoked. + protected void OnChanged(Object state) + { + OnChangedHelper(state); + + // OnChanged will also invoke Dispose, but only after initialization is complete + Dbg.Assert(_flags[CHANGED], "It is critical that CHANGED is set before INITIALIZED is checked below."); + if (_flags[INITIALIZED]) + { + DisposeHelper(); + } + } + + // + // public members + // + + // set to true when the dependency changes, specifically, when OnChanged is called. + public bool HasChanged { get { return _flags[CHANGED]; } } + + // set to true when this instance is disposed, specifically, after + // Dispose(bool disposing) is called by Dispose(). + public bool IsDisposed { get { return _flags[DISPOSED]; } } + + // a unique ID representing this ChangeMonitor, typically consisting of + // the dependency names and last-modified times. + public abstract string UniqueId { get; } + + // Dispose must be called to release the ChangeMonitor. In order to + // prevent derived classes from overriding Dispose, it is not an explicit + // interface implementation. + // + // Before cache insertion, if the user decides not to do a cache insert, they + // must call this to dispose the dependency; otherwise, the ChangeMonitor will + // be referenced and unable to be garbage collected until the dependency changes. + // + // After cache insertion, the cache implementer must call this when the cache entry + // is removed, for whatever reason. Even if an exception is thrown during insert. + // + // After cache insertion, the user should not call Dispose. However, since there's + // no way to prevent this, doing so will invoke the OnChanged event handler, if it + // hasn't already been invoked, and the cache entry will be notified as if the + // dependency has changed. + // + // Dispose() will only invoke the Dispose(bool disposing) method of derived classes + // once, the first time it is called. Subsequent calls to Dispose() perform no + // operation. After Dispose is called, the IsDisposed property will be true. + [SuppressMessage("Microsoft.Performance", "CA1816:DisposeMethodsShouldCallSuppressFinalize", Justification = "Grandfathered suppression from original caching code checkin")] + public void Dispose() + { + OnChangedHelper(null); + + // If not initialized, throw, so the derived class understands that it must call InitializeComplete before Dispose. + Dbg.Assert(_flags[CHANGED], "It is critical that CHANGED is set before INITIALIZED is checked below."); + if (!_flags[INITIALIZED]) + { + throw new InvalidOperationException(SR.Init_not_complete); + } + + DisposeHelper(); + } + + // Cache implementers must call this to be notified of any dependency changes. + // NotifyOnChanged can only be invoked once, and will throw InvalidOperationException + // on subsequent calls. The OnChangedCallback is guaranteed to be called exactly once. + // It will be called when the dependency changes, or if it has already changed, it will + // be called immediately (on the same thread??). + public void NotifyOnChanged(OnChangedCallback onChangedCallback) + { + if (onChangedCallback == null) + { + throw new ArgumentNullException("onChangedCallback"); + } + + if (Interlocked.CompareExchange(ref _onChangedCallback, onChangedCallback, null) != null) + { + throw new InvalidOperationException(SR.Method_already_invoked); + } + + // if it already changed, raise the event now. + if (_flags[CHANGED]) + { + OnChanged(null); + } + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/CachingSectionGroup.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/CachingSectionGroup.cs new file mode 100644 index 0000000000..e9a6219214 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/CachingSectionGroup.cs @@ -0,0 +1,26 @@ +// 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.Configuration; + +namespace System.Runtime.Caching.Configuration +{ + public sealed class CachingSectionGroup : ConfigurationSectionGroup + { + public CachingSectionGroup() + { + } + + // public properties + [ConfigurationProperty("memoryCache")] + public MemoryCacheSection MemoryCaches + { + get + { + return (MemoryCacheSection)Sections["memoryCache"]; + } + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/ConfigUtil.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/ConfigUtil.cs new file mode 100644 index 0000000000..9f6b3bd4f8 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/ConfigUtil.cs @@ -0,0 +1,97 @@ +// 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.Runtime.Caching.Resources; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Globalization; + +namespace System.Runtime.Caching.Configuration +{ + internal static class ConfigUtil + { + internal const string CacheMemoryLimitMegabytes = "cacheMemoryLimitMegabytes"; + internal const string PhysicalMemoryLimitPercentage = "physicalMemoryLimitPercentage"; + internal const string PollingInterval = "pollingInterval"; + internal const string UseMemoryCacheManager = "useMemoryCacheManager"; + internal const int DefaultPollingTimeMilliseconds = 120000; + + internal static int GetIntValue(NameValueCollection config, string valueName, int defaultValue, bool zeroAllowed, int maxValueAllowed) + { + string sValue = config[valueName]; + + if (sValue == null) + { + return defaultValue; + } + + int iValue; + if (!Int32.TryParse(sValue, out iValue) + || iValue < 0 + || (!zeroAllowed && iValue == 0)) + { + if (zeroAllowed) + { + throw new ArgumentException(RH.Format(SR.Value_must_be_non_negative_integer, valueName, sValue), "config"); + } + + throw new ArgumentException(RH.Format(SR.Value_must_be_positive_integer, valueName, sValue), "config"); + } + + if (maxValueAllowed > 0 && iValue > maxValueAllowed) + { + throw new ArgumentException(RH.Format(SR.Value_too_big, + valueName, + sValue, + maxValueAllowed.ToString(CultureInfo.InvariantCulture)), "config"); + } + + return iValue; + } + + internal static int GetIntValueFromTimeSpan(NameValueCollection config, string valueName, int defaultValue) + { + string sValue = config[valueName]; + + if (sValue == null) + { + return defaultValue; + } + + if (sValue == "Infinite") + { + return Int32.MaxValue; + } + + TimeSpan tValue; + if (!TimeSpan.TryParse(sValue, out tValue) || tValue <= TimeSpan.Zero) + { + throw new ArgumentException(RH.Format(SR.TimeSpan_invalid_format, valueName, sValue), "config"); + } + + double milliseconds = tValue.TotalMilliseconds; + int iValue = (milliseconds < (double)Int32.MaxValue) ? (int)milliseconds : Int32.MaxValue; + return iValue; + } + + internal static bool GetBooleanValue(NameValueCollection config, string valueName, bool defaultValue) + { + string sValue = config[valueName]; + + if (sValue == null) + { + return defaultValue; + } + + bool bValue; + if (!Boolean.TryParse(sValue, out bValue)) + { + throw new ArgumentException(RH.Format(SR.Value_must_be_boolean, valueName, sValue), "config"); + } + + return bValue; + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/MemoryCacheElement.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/MemoryCacheElement.cs new file mode 100644 index 0000000000..cba10e3936 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/MemoryCacheElement.cs @@ -0,0 +1,137 @@ +// 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.ComponentModel; +using System.Configuration; + +namespace System.Runtime.Caching.Configuration +{ + public sealed class MemoryCacheElement : ConfigurationElement + { + private static ConfigurationPropertyCollection s_properties; + private static readonly ConfigurationProperty s_propName; + private static readonly ConfigurationProperty s_propPhysicalMemoryLimitPercentage; + private static readonly ConfigurationProperty s_propCacheMemoryLimitMegabytes; + private static readonly ConfigurationProperty s_propPollingInterval; + + static MemoryCacheElement() + { + // Property initialization + s_properties = new ConfigurationPropertyCollection(); + + s_propName = + new ConfigurationProperty("name", + typeof(string), + null, + new WhiteSpaceTrimStringConverter(), + new StringValidator(1), + ConfigurationPropertyOptions.IsRequired | + ConfigurationPropertyOptions.IsKey); + + s_propPhysicalMemoryLimitPercentage = + new ConfigurationProperty("physicalMemoryLimitPercentage", + typeof(int), + (int)0, + null, + new IntegerValidator(0, 100), + ConfigurationPropertyOptions.None); + + s_propCacheMemoryLimitMegabytes = + new ConfigurationProperty("cacheMemoryLimitMegabytes", + typeof(int), + (int)0, + null, + new IntegerValidator(0, Int32.MaxValue), + ConfigurationPropertyOptions.None); + + s_propPollingInterval = + new ConfigurationProperty("pollingInterval", + typeof(TimeSpan), + TimeSpan.FromMilliseconds(ConfigUtil.DefaultPollingTimeMilliseconds), + new InfiniteTimeSpanConverter(), + new PositiveTimeSpanValidator(), + ConfigurationPropertyOptions.None); + + s_properties.Add(s_propName); + s_properties.Add(s_propPhysicalMemoryLimitPercentage); + s_properties.Add(s_propCacheMemoryLimitMegabytes); + s_properties.Add(s_propPollingInterval); + } + + internal MemoryCacheElement() + { + } + + public MemoryCacheElement(string name) + { + Name = name; + } + + protected override ConfigurationPropertyCollection Properties + { + get + { + return s_properties; + } + } + + [ConfigurationProperty("name", DefaultValue = "", IsRequired = true, IsKey = true)] + [TypeConverter(typeof(WhiteSpaceTrimStringConverter))] + [StringValidator(MinLength = 1)] + public string Name + { + get + { + return (string)base["name"]; + } + set + { + base["name"] = value; + } + } + + [ConfigurationProperty("physicalMemoryLimitPercentage", DefaultValue = (int)0)] + [IntegerValidator(MinValue = 0, MaxValue = 100)] + public int PhysicalMemoryLimitPercentage + { + get + { + return (int)base["physicalMemoryLimitPercentage"]; + } + set + { + base["physicalMemoryLimitPercentage"] = value; + } + } + + [ConfigurationProperty("cacheMemoryLimitMegabytes", DefaultValue = (int)0)] + [IntegerValidator(MinValue = 0)] + public int CacheMemoryLimitMegabytes + { + get + { + return (int)base["cacheMemoryLimitMegabytes"]; + } + set + { + base["cacheMemoryLimitMegabytes"] = value; + } + } + + [ConfigurationProperty("pollingInterval", DefaultValue = "00:02:00")] + [TypeConverter(typeof(InfiniteTimeSpanConverter))] + public TimeSpan PollingInterval + { + get + { + return (TimeSpan)base["pollingInterval"]; + } + set + { + base["pollingInterval"] = value; + } + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/MemoryCacheSection.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/MemoryCacheSection.cs new file mode 100644 index 0000000000..06afa86dd9 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/MemoryCacheSection.cs @@ -0,0 +1,61 @@ +// 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.Collections.Specialized; +using System.Configuration; +using System.Runtime.Caching.Resources; + +namespace System.Runtime.Caching.Configuration +{ + /* + + + + + + + + + + */ + + public sealed class MemoryCacheSection : ConfigurationSection + { + private static ConfigurationPropertyCollection s_properties; + private static readonly ConfigurationProperty s_propNamedCaches; + + static MemoryCacheSection() + { + s_propNamedCaches = new ConfigurationProperty("namedCaches", + typeof(MemoryCacheSettingsCollection), + null, // defaultValue + ConfigurationPropertyOptions.None); + + s_properties = new ConfigurationPropertyCollection(); + s_properties.Add(s_propNamedCaches); + } + + public MemoryCacheSection() + { + } + + protected override ConfigurationPropertyCollection Properties + { + get + { + return s_properties; + } + } + + [ConfigurationProperty("namedCaches")] + public MemoryCacheSettingsCollection NamedCaches + { + get + { + return (MemoryCacheSettingsCollection)base[s_propNamedCaches]; + } + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/MemoryCacheSettingsCollection.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/MemoryCacheSettingsCollection.cs new file mode 100644 index 0000000000..f35e5625f4 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Configuration/MemoryCacheSettingsCollection.cs @@ -0,0 +1,103 @@ +// 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.Configuration; + +namespace System.Runtime.Caching.Configuration +{ + [ConfigurationCollection(typeof(MemoryCacheElement), + CollectionType = ConfigurationElementCollectionType.AddRemoveClearMap)] + public sealed class MemoryCacheSettingsCollection : ConfigurationElementCollection + { + private static ConfigurationPropertyCollection s_properties; + + static MemoryCacheSettingsCollection() + { + // Property initialization + s_properties = new ConfigurationPropertyCollection(); + } + + protected override ConfigurationPropertyCollection Properties + { + get + { + return s_properties; + } + } + + public MemoryCacheSettingsCollection() + { + } + + public MemoryCacheElement this[int index] + { + get { return (MemoryCacheElement)base.BaseGet(index); } + set + { + if (base.BaseGet(index) != null) + { + base.BaseRemoveAt(index); + } + base.BaseAdd(index, value); + } + } + + public new MemoryCacheElement this[string key] + { + get + { + return (MemoryCacheElement)BaseGet(key); + } + } + + public override ConfigurationElementCollectionType CollectionType + { + get + { + return ConfigurationElementCollectionType.AddRemoveClearMapAlternate; + } + } + + public int IndexOf(MemoryCacheElement cache) + { + return BaseIndexOf(cache); + } + + public void Add(MemoryCacheElement cache) + { + BaseAdd(cache); + } + + public void Remove(MemoryCacheElement cache) + { + BaseRemove(cache.Name); + } + + public void RemoveAt(int index) + { + BaseRemoveAt(index); + } + + public void Clear() + { + BaseClear(); + } + + protected override ConfigurationElement CreateNewElement() + { + return new MemoryCacheElement(); + } + + protected override ConfigurationElement CreateNewElement(string elementName) + { + return new MemoryCacheElement(elementName); + } + + protected override object GetElementKey(ConfigurationElement element) + { + return ((MemoryCacheElement)element).Name; + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Dbg.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Dbg.cs new file mode 100644 index 0000000000..c2b638de06 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Dbg.cs @@ -0,0 +1,1041 @@ +// 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 Microsoft.Win32; +using Microsoft.Win32.SafeHandles; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; +using System.Security; +using System.Security.Permissions; +using System.Threading; +using System.Runtime.Versioning; + +namespace System.Runtime.Caching +{ + internal static class Dbg + { +#if DEBUG + static readonly DateTime MinValuePlusOneDay = DateTime.MinValue.AddDays(1); + static readonly DateTime MaxValueMinusOneDay = DateTime.MaxValue.AddDays(-1); +#endif + + internal const string TAG_INTERNAL = "Internal"; + internal const string TAG_EXTERNAL = "External"; + internal const string TAG_ALL = "*"; + + internal const string DATE_FORMAT = @"yyyy/MM/dd HH:mm:ss.ffff"; + internal const string TIME_FORMAT = @"HH:mm:ss:ffff"; + +#if DEBUG + private static class NativeMethods { + [DllImport("kernel32.dll")] + internal extern static void DebugBreak(); + + [DllImport("kernel32.dll")] + internal extern static int GetCurrentProcessId(); + + [DllImport("kernel32.dll")] + internal extern static int GetCurrentThreadId(); + + [DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)] + internal extern static IntPtr GetCurrentProcess(); + + [DllImport("kernel32.dll")] + internal extern static bool IsDebuggerPresent(); + + [DllImport("kernel32.dll", SetLastError=true)] + internal extern static bool TerminateProcess(HandleRef processHandle, int exitCode); + + internal const int PM_NOREMOVE = 0x0000; + internal const int PM_REMOVE = 0x0001; + + [StructLayout(LayoutKind.Sequential)] + internal struct MSG { + internal IntPtr hwnd; + internal int message; + internal IntPtr wParam; + internal IntPtr lParam; + internal int time; + internal int pt_x; + internal int pt_y; + } + + //[DllImport("user32.dll", CharSet=CharSet.Auto)] + //internal extern static bool PeekMessage([In, Out] ref MSG msg, HandleRef hwnd, int msgMin, int msgMax, int remove); + + internal const int + MB_OK = 0x00000000, + MB_OKCANCEL = 0x00000001, + MB_ABORTRETRYIGNORE = 0x00000002, + MB_YESNOCANCEL = 0x00000003, + MB_YESNO = 0x00000004, + MB_RETRYCANCEL = 0x00000005, + MB_ICONHAND = 0x00000010, + MB_ICONQUESTION = 0x00000020, + MB_ICONEXCLAMATION = 0x00000030, + MB_ICONASTERISK = 0x00000040, + MB_USERICON = 0x00000080, + MB_ICONWARNING = 0x00000030, + MB_ICONERROR = 0x00000010, + MB_ICONINFORMATION = 0x00000040, + MB_DEFBUTTON1 = 0x00000000, + MB_DEFBUTTON2 = 0x00000100, + MB_DEFBUTTON3 = 0x00000200, + MB_DEFBUTTON4 = 0x00000300, + MB_APPLMODAL = 0x00000000, + MB_SYSTEMMODAL = 0x00001000, + MB_TASKMODAL = 0x00002000, + MB_HELP = 0x00004000, + MB_NOFOCUS = 0x00008000, + MB_SETFOREGROUND = 0x00010000, + MB_DEFAULT_DESKTOP_ONLY = 0x00020000, + MB_TOPMOST = 0x00040000, + MB_RIGHT = 0x00080000, + MB_RTLREADING = 0x00100000, + MB_SERVICE_NOTIFICATION = 0x00200000, + MB_SERVICE_NOTIFICATION_NT3X = 0x00040000, + MB_TYPEMASK = 0x0000000F, + MB_ICONMASK = 0x000000F0, + MB_DEFMASK = 0x00000F00, + MB_MODEMASK = 0x00003000, + MB_MISCMASK = 0x0000C000; + + internal const int + IDOK = 1, + IDCANCEL = 2, + IDABORT = 3, + IDRETRY = 4, + IDIGNORE = 5, + IDYES = 6, + IDNO = 7, + IDCLOSE = 8, + IDHELP = 9; + + //[DllImport("user32.dll", CharSet=CharSet.Auto, BestFitMapping=false)] + //internal extern static int MessageBox(HandleRef hWnd, string text, string caption, int type); + + internal static readonly IntPtr HKEY_LOCAL_MACHINE = unchecked((IntPtr)(int)0x80000002); + + internal const int READ_CONTROL = 0x00020000; + internal const int STANDARD_RIGHTS_READ = READ_CONTROL; + + internal const int SYNCHRONIZE = 0x00100000; + + internal const int KEY_QUERY_VALUE = 0x0001; + internal const int KEY_ENUMERATE_SUB_KEYS = 0x0008; + internal const int KEY_NOTIFY = 0x0010; + + internal const int KEY_READ = ((STANDARD_RIGHTS_READ | + KEY_QUERY_VALUE | + KEY_ENUMERATE_SUB_KEYS | + KEY_NOTIFY) + & + (~SYNCHRONIZE)); + + internal const int REG_NOTIFY_CHANGE_NAME = 1; + internal const int REG_NOTIFY_CHANGE_LAST_SET = 4; + + [DllImport("advapi32.dll", CharSet=CharSet.Auto, BestFitMapping=false, SetLastError=true)] + internal extern static int RegOpenKeyEx(IntPtr hKey, string lpSubKey, int ulOptions, int samDesired, out SafeRegistryHandle hkResult); + + [DllImport("advapi32.dll", ExactSpelling=true, SetLastError=true)] + internal extern static int RegNotifyChangeKeyValue(SafeRegistryHandle hKey, bool watchSubTree, uint notifyFilter, SafeWaitHandle regEvent, bool async); + } + + private enum TagValue { + Disabled = 0, + Enabled = 1, + Break = 2, + + Min = Disabled, + Max = Break, + } + + private const string TAG_ASSERT = "Assert"; + private const string TAG_ASSERT_BREAK = "AssertBreak"; + + private const string TAG_DEBUG_VERBOSE = "DebugVerbose"; + private const string TAG_DEBUG_MONITOR = "DebugMonitor"; + private const string TAG_DEBUG_PREFIX = "DebugPrefix"; + private const string TAG_DEBUG_THREAD_PREFIX = "DebugThreadPrefix"; + + private const string PRODUCT = "Microsoft .NET Framework"; + private const string COMPONENT = "System.Web"; + + private static string s_regKeyName = @"Software\Microsoft\ASP.NET\Debug"; + private static string s_listenKeyName = @"Software\Microsoft"; + + private static bool s_assert; + private static bool s_assertBreak; + + private static bool s_includePrefix; + private static bool s_includeThreadPrefix; + private static bool s_monitor; + + private static object s_lock; + private static volatile bool s_inited; + private static ReadOnlyCollection s_tagDefaults; + private static List s_tags; + + private static AutoResetEvent s_notifyEvent; + private static RegisteredWaitHandle s_waitHandle; + private static SafeRegistryHandle s_regHandle; + private static bool s_stopMonitoring; + + private static Hashtable s_tableAlwaysValidate; + private static Type[] s_DumpArgs; + private static Type[] s_ValidateArgs; + + private class Tag { + string _name; + TagValue _value; + int _prefixLength; + + internal Tag(string name, TagValue value) { + _name = name; + _value = value; + + if (_name[_name.Length - 1] == '*') { + _prefixLength = _name.Length - 1; + } + else { + _prefixLength = -1; + } + } + + internal string Name { + get {return _name;} + } + + internal TagValue Value { + get {return _value;} + } + + internal int PrefixLength { + get {return _prefixLength;} + } + } + + static Dbg() { + s_lock = new object(); + } + + private static void EnsureInit() { + bool continueInit = false; + + if (!s_inited) { + lock (s_lock) { + if (!s_inited) { + s_tableAlwaysValidate = new Hashtable(); + s_DumpArgs = new Type[1] {typeof(string)}; + s_ValidateArgs = new Type[0]; + + List tagDefaults = new List(); + tagDefaults.Add(new Tag(TAG_ALL, TagValue.Disabled)); + tagDefaults.Add(new Tag(TAG_INTERNAL, TagValue.Enabled)); + tagDefaults.Add(new Tag(TAG_EXTERNAL, TagValue.Enabled)); + tagDefaults.Add(new Tag(TAG_ASSERT, TagValue.Break)); + tagDefaults.Add(new Tag(TAG_ASSERT_BREAK, TagValue.Disabled)); + tagDefaults.Add(new Tag(TAG_DEBUG_VERBOSE, TagValue.Enabled)); + tagDefaults.Add(new Tag(TAG_DEBUG_MONITOR, TagValue.Enabled)); + tagDefaults.Add(new Tag(TAG_DEBUG_PREFIX, TagValue.Enabled)); + tagDefaults.Add(new Tag(TAG_DEBUG_THREAD_PREFIX, TagValue.Enabled)); + + s_tagDefaults = tagDefaults.AsReadOnly(); + s_tags = new List(s_tagDefaults); + GetBuiltinTagValues(); + + continueInit = true; + s_inited = true; + } + } + } + + // Work to do outside the init lock. + if (continueInit) { + ReadTagsFromRegistry(); + Trace(TAG_DEBUG_VERBOSE, "Debugging package initialized"); + + // Need to read tags before starting to monitor in order to get TAG_DEBUG_MONITOR + StartRegistryMonitor(); + } + } + + private static bool StringEqualsIgnoreCase(string s1, string s2) { + return StringComparer.OrdinalIgnoreCase.Equals(s1, s2); + } + + private static void WriteTagsToRegistry() { + try { + using (RegistryKey key = Registry.LocalMachine.CreateSubKey(s_regKeyName)) { + List tags = s_tags; + foreach (Tag tag in tags) { + key.SetValue(tag.Name, tag.Value, RegistryValueKind.DWord); + } + } + } + catch { + } + } + + private static void GetBuiltinTagValues() { + // Use GetTagValue instead of IsTagEnabled because it does not call EnsureInit + // and potentially recurse. + s_assert = (GetTagValue(TAG_ASSERT) != TagValue.Disabled); + s_assertBreak = (GetTagValue(TAG_ASSERT_BREAK) != TagValue.Disabled); + s_includePrefix = (GetTagValue(TAG_DEBUG_PREFIX) != TagValue.Disabled); + s_includeThreadPrefix = (GetTagValue(TAG_DEBUG_THREAD_PREFIX) != TagValue.Disabled); + s_monitor = (GetTagValue(TAG_DEBUG_MONITOR) != TagValue.Disabled); + } + + private static void ReadTagsFromRegistry() { + lock (s_lock) { + try { + List tags = new List(s_tagDefaults); + string[] names = null; + + bool writeTags = false; + using (RegistryKey key = Registry.LocalMachine.OpenSubKey(s_regKeyName, false)) { + if (key != null) { + names = key.GetValueNames(); + foreach (string name in names) { + TagValue value = TagValue.Disabled; + try { + TagValue keyvalue = (TagValue) key.GetValue(name); + if (TagValue.Min <= keyvalue && keyvalue <= TagValue.Max) { + value = keyvalue; + } + else { + writeTags = true; + } + } + catch { + writeTags = true; + } + + // Add tag to list, making sure it is unique. + Tag tag = new Tag(name, (TagValue) value); + bool found = false; + for (int i = 0; i < s_tagDefaults.Count; i++) { + if (StringEqualsIgnoreCase(name, tags[i].Name)) { + found = true; + tags[i] = tag; + break; + } + } + + if (!found) { + tags.Add(tag); + } + } + } + } + + s_tags = tags; + GetBuiltinTagValues(); + + // Write tags out if there was an invalid value or + // not all default tags are present. + if (writeTags || (names != null && names.Length < tags.Count)) { + WriteTagsToRegistry(); + } + } + catch { + s_tags = new List(s_tagDefaults); + } + } + } + + private static void StartRegistryMonitor() { + if (!s_monitor) { + Trace(TAG_DEBUG_VERBOSE, "WARNING: Registry monitoring disabled, changes during process execution will not be recognized."); + return; + } + + Trace(TAG_DEBUG_VERBOSE, "Monitoring registry key " + s_listenKeyName + " for changes."); + + // Event used to notify of changes. + s_notifyEvent = new AutoResetEvent(false); + + // Register a wait on the event. + s_waitHandle = ThreadPool.RegisterWaitForSingleObject(s_notifyEvent, OnRegChangeKeyValue, null, -1, false); + + // Monitor the registry. + MonitorRegistryForOneChange(); + } + + private static void StopRegistryMonitor() { + // Cleanup allocated handles + s_stopMonitoring = true; + + if (s_regHandle != null) { + s_regHandle.Close(); + s_regHandle = null; + } + + if (s_waitHandle != null) { + s_waitHandle.Unregister(s_notifyEvent); + s_waitHandle = null; + } + + if (s_notifyEvent != null) { + s_notifyEvent.Close(); + s_notifyEvent = null; + } + + Trace(TAG_DEBUG_VERBOSE, "Registry monitoring stopped."); + } + + public static void OnRegChangeKeyValue(object state, bool timedOut) { + if (!s_stopMonitoring) { + if (timedOut) { + StopRegistryMonitor(); + } + else { + // Monitor again + MonitorRegistryForOneChange(); + + // Once we're monitoring, read the changes to the registry. + // We have to do this after we start monitoring in order + // to catch all changes to the registry. + ReadTagsFromRegistry(); + } + } + } + + private static void MonitorRegistryForOneChange() { + // Close the open reg handle + if (s_regHandle != null) { + s_regHandle.Close(); + s_regHandle = null; + } + + // Open the reg key + int result = NativeMethods.RegOpenKeyEx(NativeMethods.HKEY_LOCAL_MACHINE, s_listenKeyName, 0, NativeMethods.KEY_READ, out s_regHandle); + if (result != 0) { + StopRegistryMonitor(); + return; + } + + // Listen for changes. + result = NativeMethods.RegNotifyChangeKeyValue( + s_regHandle, + true, + NativeMethods.REG_NOTIFY_CHANGE_NAME | NativeMethods.REG_NOTIFY_CHANGE_LAST_SET, + s_notifyEvent.SafeWaitHandle, + true); + + if (result != 0) { + StopRegistryMonitor(); + } + } + + private static Tag FindMatchingTag(string name, bool exact) { + List tags = s_tags; + + // Look for exact match first + foreach (Tag tag in tags) { + if (StringEqualsIgnoreCase(name, tag.Name)) { + return tag; + } + } + + if (exact) { + return null; + } + + Tag longestTag = null; + int longestPrefix = -1; + foreach (Tag tag in tags) { + if ( tag.PrefixLength > longestPrefix && + 0 == string.Compare(name, 0, tag.Name, 0, tag.PrefixLength, StringComparison.OrdinalIgnoreCase)) { + + longestTag = tag; + longestPrefix = tag.PrefixLength; + } + } + + return longestTag; + } + + private static TagValue GetTagValue(string name) { + Tag tag = FindMatchingTag(name, false); + if (tag != null) { + return tag.Value; + } + else { + return TagValue.Disabled; + } + } + + private static bool TraceBreak(string tagName, string message, Exception e, bool includePrefix) { + EnsureInit(); + + TagValue tagValue = GetTagValue(tagName); + if (tagValue == TagValue.Disabled) { + return false; + } + + bool isAssert = object.ReferenceEquals(tagName, TAG_ASSERT); + if (isAssert) { + tagName = ""; + } + + string exceptionMessage = null; + if (e != null) { + string httpCode = null; + string errorCode = null; + + if (e is ExternalException) { + // note that HttpExceptions are ExternalExceptions + errorCode = "_hr=0x" + ((ExternalException)e).ErrorCode.ToString("x", CultureInfo.InvariantCulture); + } + + // Use e.ToString() in order to get inner exception + if (errorCode != null) { + exceptionMessage = "Exception " + e.ToString() + "\n" + httpCode + errorCode; + } + else { + exceptionMessage = "Exception " + e.ToString(); + } + } + + if (string.IsNullOrEmpty(message) & exceptionMessage != null) { + message = exceptionMessage; + exceptionMessage = null; + } + + string traceFormat; + int idThread = 0; + int idProcess = 0; + + if (!includePrefix || !s_includePrefix) { + traceFormat = "{4}\n{5}"; + } + else { + if (s_includeThreadPrefix) { + idThread = NativeMethods.GetCurrentThreadId(); + idProcess = NativeMethods.GetCurrentProcessId(); + traceFormat = "[0x{0:x}.{1:x} {2} {3}] {4}\n{5}"; + } + else { + traceFormat = "[{2} {3}] {4}\n{5}"; + } + } + + string suffix = ""; + if (exceptionMessage != null) { + suffix += exceptionMessage + "\n"; + } + + bool doBreak = (tagValue == TagValue.Break); + if (doBreak && !isAssert) { + suffix += "Breaking into debugger...\n"; + } + + string traceMessage = string.Format(CultureInfo.InvariantCulture, traceFormat, idProcess, idThread, COMPONENT, tagName, message, suffix); + + Debug.WriteLine(traceMessage); + + return doBreak; + } + + //private class MBResult { + // internal int Result; + //} + + [ResourceExposure(ResourceScope.None)] + static bool DoAssert(string message) { + if (!s_assert) { + return false; + } + + // Skip 2 frames - one for this function, one for + // the public Assert function that called this function. + StackFrame frame = new StackFrame(2, true); + StackTrace trace = new StackTrace(2, true); + + string fileName = frame.GetFileName(); + int lineNumber = frame.GetFileLineNumber(); + + string traceFormat; + if (!string.IsNullOrEmpty(fileName)) { + traceFormat = "ASSERTION FAILED: {0}\nFile: {1}:{2}\nStack trace:\n{3}"; + } + else { + traceFormat = "ASSERTION FAILED: {0}\nStack trace:\n{3}"; + } + + string traceMessage = string.Format(CultureInfo.InvariantCulture, traceFormat, message, fileName, lineNumber, trace.ToString()); + + if (!TraceBreak(TAG_ASSERT, traceMessage, null, true)) { + // If the value of "Assert" is not TagValue.Break, then don't even ask user. + return false; + } + + if (s_assertBreak) { + // If "AssertBreak" is enabled, then always break. + return true; + } + + string dialogFormat; + if (!string.IsNullOrEmpty(fileName)) { + dialogFormat = +@"Failed expression: {0} +File: {1}:{2} +Component: {3} +PID={4} TID={5} +Stack trace: +{6} + +A=Exit process R=Debug I=Continue"; + } + else { + dialogFormat = +@"Failed expression: {0} +(no file information available) +Component: {3} +PID={4} TID={5} +Stack trace: +{6} + +A=Exit process R=Debug I=Continue"; + } + + string dialogMessage = string.Format( + CultureInfo.InvariantCulture, + dialogFormat, + message, + fileName, lineNumber, + COMPONENT, + NativeMethods.GetCurrentProcessId(), NativeMethods.GetCurrentThreadId(), + trace.ToString()); + + //MBResult mbResult = new MBResult(); + + //Thread thread = new Thread( + // delegate() { + // for (int i = 0; i < 100; i++) { + // NativeMethods.MSG msg = new NativeMethods.MSG(); + // NativeMethods.PeekMessage(ref msg, new HandleRef(mbResult, IntPtr.Zero), 0, 0, NativeMethods.PM_REMOVE); + // } + + // mbResult.Result = NativeMethods.MessageBox(new HandleRef(mbResult, IntPtr.Zero), dialogMessage, PRODUCT + " Assertion", + // NativeMethods.MB_SERVICE_NOTIFICATION | + // NativeMethods.MB_TOPMOST | + // NativeMethods.MB_ABORTRETRYIGNORE | + // NativeMethods.MB_ICONEXCLAMATION); + // } + //); + + //thread.Start(); + //thread.Join(); + + //if (mbResult.Result == NativeMethods.IDABORT) { + // IntPtr currentProcess = NativeMethods.GetCurrentProcess(); + // NativeMethods.TerminateProcess(new HandleRef(mbResult, currentProcess), 1); + //} + + //return mbResult.Result == NativeMethods.IDRETRY; + Debug.Fail(dialogMessage); + return true; + } +#endif + + // + // Sends the message to the debugger if the tag is enabled. + // Also breaks into the debugger the value of the tag is 2 (TagValue.Break). + // + [Conditional("DEBUG")] + internal static void Trace(string tagName, string message) + { +#if DEBUG + if (TraceBreak(tagName, message, null, true)) { + Break(); + } +#endif + } + + // + // Sends the message to the debugger if the tag is enabled. + // Also breaks into the debugger the value of the tag is 2 (TagValue.Break). + // + [Conditional("DEBUG")] + internal static void Trace(string tagName, string message, bool includePrefix) + { +#if DEBUG + if (TraceBreak(tagName, message, null, includePrefix)) { + Break(); + } +#endif + } + + // + // Sends the message to the debugger if the tag is enabled. + // Also breaks into the debugger the value of the tag is 2 (TagValue.Break). + // + [Conditional("DEBUG")] + internal static void Trace(string tagName, string message, Exception e) + { +#if DEBUG + if (TraceBreak(tagName, message, e, true)) { + Break(); + } +#endif + } + + // + // Sends the message to the debugger if the tag is enabled. + // Also breaks into the debugger the value of the tag is 2 (TagValue.Break). + // + [Conditional("DEBUG")] + internal static void Trace(string tagName, Exception e) + { +#if DEBUG + if (TraceBreak(tagName, null, e, true)) { + Break(); + } +#endif + } + + // + // Sends the message to the debugger if the tag is enabled. + // Also breaks into the debugger the value of the tag is 2 (TagValue.Break). + // + [Conditional("DEBUG")] + internal static void Trace(string tagName, string message, Exception e, bool includePrefix) + { +#if DEBUG + if (TraceBreak(tagName, message, e, includePrefix)) { + Break(); + } +#endif + } + +#if DEBUG +#endif + + [Conditional("DEBUG")] + public static void TraceException(String tagName, Exception e) + { +#if DEBUG + if (TraceBreak(tagName, null, e, true)) { + Break(); + } +#endif + } + + + // + // If the assertion is false and the 'Assert' tag is enabled: + // * Send a message to the debugger. + // * If the 'AssertBreak' tag is enabled, immediately break into the debugger + // * Else display a dialog box asking the user to Abort, Retry (break), or Ignore + // + [Conditional("DEBUG")] + internal static void Assert(bool assertion, string message) + { +#if DEBUG + EnsureInit(); + if (assertion == false) { + if (DoAssert(message)) { + Break(); + } + } +#endif + } + + // + // If the assertion is false and the 'Assert' tag is enabled: + // * Send a message to the debugger. + // * If the 'AssertBreak' tag is enabled, immediately break into the debugger + // * Else display a dialog box asking the user to Abort, Retry (break), or Ignore + // + [Conditional("DEBUG")] + [ResourceExposure(ResourceScope.None)] + internal static void Assert(bool assertion) + { +#if DEBUG + EnsureInit(); + if (assertion == false) { + if (DoAssert(null)) { + Break(); + } + } +#endif + } + + // + // Like Assert, but the assertion is always considered to be false. + // + [Conditional("DEBUG")] + [ResourceExposure(ResourceScope.None)] + internal static void Fail(string message) + { +#if DEBUG + Assert(false, message); +#endif + } + + // + // Returns true if the tag is enabled, false otherwise. + // Note that the tag needn't be an exact match. + // + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Grandfathered suppression from original caching code checkin")] + [ResourceExposure(ResourceScope.None)] + internal static bool IsTagEnabled(string tagName) + { +#if DEBUG + EnsureInit(); + return GetTagValue(tagName) != TagValue.Disabled; +#else + return false; +#endif + } + + // + // Returns true if the tag present. + // This function chekcs for an exact match. + // + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Grandfathered suppression from original caching code checkin")] + [ResourceExposure(ResourceScope.None)] + internal static bool IsTagPresent(string tagName) + { +#if DEBUG + EnsureInit(); + return FindMatchingTag(tagName, true) != null; +#else + return false; +#endif + } + + // + // Breaks into the debugger, or launches one if not yet attached. + // + [Conditional("DEBUG")] + [ResourceExposure(ResourceScope.None)] + internal static void Break() + { +#if DEBUG + if (NativeMethods.IsDebuggerPresent()) { + NativeMethods.DebugBreak(); + } + else if (!Debugger.IsAttached) { + Debugger.Launch(); + } + else { + Debugger.Break(); + } +#endif + } + + // + // Tells the debug system to always validate calls for a + // particular tag. This is useful for temporarily enabling + // validation in stress tests or other situations where you + // may not have control over the debug tags that are enabled + // on a particular machine. + // + [Conditional("DEBUG")] + internal static void AlwaysValidate(string tagName) + { +#if DEBUG + EnsureInit(); + s_tableAlwaysValidate[tagName] = tagName; +#endif + } + + // + // Throws an exception if the assertion is not valid. + // Use this function from a DebugValidate method where + // you would otherwise use Assert. + // + [Conditional("DEBUG")] + internal static void CheckValid(bool assertion, string message) + { +#if DEBUG + if (!assertion) { + throw new Exception(message); + } +#endif + } + + // + // Calls DebugValidate on an object if such a method exists. + // + // This method should be used from implementations of DebugValidate + // where it is unknown whether an object has a DebugValidate method. + // For example, the DoubleLink class uses it to validate the + // item of type Object which it points to. + // + // This method should NOT be used when code wants to conditionally + // validate an object and have a failed validation caught in an assert. + // Use Debug.Validate(tagName, obj) for that purpose. + // + [Conditional("DEBUG")] + [ResourceExposure(ResourceScope.None)] + internal static void Validate(Object obj) + { +#if DEBUG + Type type; + MethodInfo mi; + + if (obj != null) { + type = obj.GetType(); + + mi = type.GetMethod( + "DebugValidate", + BindingFlags.NonPublic | BindingFlags.Instance, + null, + s_ValidateArgs, + null); + + if (mi != null) { + object[] tempIndex = null; + mi.Invoke(obj, tempIndex); + } + } +#endif + } + + // + // Validates an object is the "Validate" tag is enabled, or when + // the "Validate" tag is not disabled and the given 'tag' is enabled. + // An Assertion is made if the validation fails. + // + [Conditional("DEBUG")] + [ResourceExposure(ResourceScope.None)] + internal static void Validate(string tagName, Object obj) + { +#if DEBUG + EnsureInit(); + + if ( obj != null + && ( IsTagEnabled("Validate") + || ( !IsTagPresent("Validate") + && ( s_tableAlwaysValidate[tagName] != null + || IsTagEnabled(tagName))))) { + try { + Validate(obj); + } + catch (Exception e) { + Assert(false, "Validate failed: " + e.InnerException.Message); + } +#pragma warning disable 1058 + catch { + Assert(false, "Validate failed. Non-CLS compliant exception caught."); + } +#pragma warning restore 1058 + } +#endif + } + +#if DEBUG + + // + // Calls DebugDescription on an object to get its description. + // + // This method should only be used in implementations of DebugDescription + // where it is not known whether a nested objects has an implementation + // of DebugDescription. For example, the double linked list class uses + // GetDescription to get the description of the item it points to. + // + // This method should NOT be used when you want to conditionally + // dump an object. Use Debug.Dump instead. + // + // @param obj The object to call DebugDescription on. May be null. + // @param indent A prefix for each line in the description. This is used + // to allow the nested display of objects within other objects. + // The indent is usually a multiple of four spaces. + // + // @return The description. + // + internal static string GetDescription(Object obj, string indent) { + string description; + Type type; + MethodInfo mi; + Object[] parameters; + + if (obj == null) { + description = "\n"; + } + else { + type = obj.GetType(); + mi = type.GetMethod( + "DebugDescription", + BindingFlags.NonPublic | BindingFlags.Instance, + null, + s_DumpArgs, + null); + + if (mi == null || mi.ReturnType != typeof(string)) { + description = indent + obj.ToString(); + } + else { + parameters = new Object[1] {(Object) indent}; + description = (string) mi.Invoke(obj, parameters); + } + } + + return description; + } +#endif + + // + // Dumps an object to the debugger if the "Dump" tag is enabled, + // or if the "Dump" tag is not present and the 'tag' is enabled. + // + // @param tagName The tag to Dump with. + // @param obj The object to dump. + // + [Conditional("DEBUG")] + internal static void Dump(string tagName, Object obj) + { +#if DEBUG + EnsureInit(); + + string description; + string traceTag = null; + bool dumpEnabled, dumpPresent; + + if (obj != null) { + dumpEnabled = IsTagEnabled("Dump"); + dumpPresent = IsTagPresent("Dump"); + if (dumpEnabled || !dumpPresent) { + if (IsTagEnabled(tagName)) { + traceTag = tagName; + } + else if (dumpEnabled) { + traceTag = "Dump"; + } + + if (traceTag != null) { + description = GetDescription(obj, string.Empty); + Trace(traceTag, "Dump\n" + description); + } + } + } +#endif + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Grandfathered suppression from original caching code checkin")] + static internal string FormatLocalDate(DateTime localTime) + { +#if DEBUG + return localTime.ToString(DATE_FORMAT, CultureInfo.InvariantCulture); +#else + return string.Empty; +#endif + } + } +} + diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/DefaultCacheCapabilities.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/DefaultCacheCapabilities.cs new file mode 100644 index 0000000000..a1618e8d1a --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/DefaultCacheCapabilities.cs @@ -0,0 +1,22 @@ +// 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; + +namespace System.Runtime.Caching +{ + [Flags] + public enum DefaultCacheCapabilities + { + None = 0x0, + InMemoryProvider = 0x1, + OutOfProcessProvider = 0x2, + CacheEntryChangeMonitors = 0x4, + AbsoluteExpirations = 0x8, + SlidingExpirations = 0x10, + CacheEntryUpdateCallback = 0x20, + CacheEntryRemovedCallback = 0x40, + CacheRegions = 0x80, + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/EntryState.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/EntryState.cs new file mode 100644 index 0000000000..f9e46133df --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/EntryState.cs @@ -0,0 +1,18 @@ +// 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; + +namespace System.Runtime.Caching +{ + internal enum EntryState : byte + { + NotInCache = 0x00, // Created but not in hashtable + AddingToCache = 0x01, // In hashtable only + AddedToCache = 0x02, // In hashtable + expires + usage + RemovingFromCache = 0x04, // Removed from hashtable only + RemovedFromCache = 0x08, // Removed from hashtable & expires & usage + Closed = 0x10, + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/FileChangeMonitor.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/FileChangeMonitor.cs new file mode 100644 index 0000000000..4876833aa8 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/FileChangeMonitor.cs @@ -0,0 +1,15 @@ +// 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.Collections.ObjectModel; + +namespace System.Runtime.Caching +{ + public abstract class FileChangeMonitor : ChangeMonitor + { + public abstract ReadOnlyCollection FilePaths { get; } + public abstract DateTimeOffset LastModified { get; } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/FileChangeNotificationSystem.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/FileChangeNotificationSystem.cs new file mode 100644 index 0000000000..b2651ae952 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/FileChangeNotificationSystem.cs @@ -0,0 +1,171 @@ +// 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.Runtime.Caching.Hosting; +using System.Runtime.Caching.Resources; +using System.Collections; +using System.IO; +using System.Security; +using System.Security.Permissions; + +namespace System.Runtime.Caching +{ + internal sealed class FileChangeNotificationSystem : IFileChangeNotificationSystem + { + private Hashtable _dirMonitors; + private object _lock; + + internal class DirectoryMonitor + { + internal FileSystemWatcher Fsw; + } + + internal class FileChangeEventTarget + { + private string _fileName; + private OnChangedCallback _onChangedCallback; + private FileSystemEventHandler _changedHandler; + private ErrorEventHandler _errorHandler; + private RenamedEventHandler _renamedHandler; + + private static bool EqualsIgnoreCase(string s1, string s2) + { + if (String.IsNullOrEmpty(s1) && String.IsNullOrEmpty(s2)) + { + return true; + } + if (String.IsNullOrEmpty(s1) || String.IsNullOrEmpty(s2)) + { + return false; + } + if (s2.Length != s1.Length) + { + return false; + } + return 0 == string.Compare(s1, 0, s2, 0, s2.Length, StringComparison.OrdinalIgnoreCase); + } + + private void OnChanged(Object sender, FileSystemEventArgs e) + { + if (EqualsIgnoreCase(_fileName, e.Name)) + { + _onChangedCallback(null); + } + } + + private void OnError(Object sender, ErrorEventArgs e) + { + _onChangedCallback(null); + } + + private void OnRenamed(Object sender, RenamedEventArgs e) + { + if (EqualsIgnoreCase(_fileName, e.Name) || EqualsIgnoreCase(_fileName, e.OldName)) + { + _onChangedCallback(null); + } + } + + internal FileSystemEventHandler ChangedHandler { get { return _changedHandler; } } + internal ErrorEventHandler ErrorHandler { get { return _errorHandler; } } + internal RenamedEventHandler RenamedHandler { get { return _renamedHandler; } } + + internal FileChangeEventTarget(string fileName, OnChangedCallback onChangedCallback) + { + _fileName = fileName; + _onChangedCallback = onChangedCallback; + _changedHandler = new FileSystemEventHandler(this.OnChanged); + _errorHandler = new ErrorEventHandler(this.OnError); + _renamedHandler = new RenamedEventHandler(this.OnRenamed); + } + } + + internal FileChangeNotificationSystem() + { + _dirMonitors = Hashtable.Synchronized(new Hashtable(StringComparer.OrdinalIgnoreCase)); + _lock = new object(); + } + + void IFileChangeNotificationSystem.StartMonitoring(string filePath, OnChangedCallback onChangedCallback, out Object state, out DateTimeOffset lastWriteTime, out long fileSize) + { + if (filePath == null) + { + throw new ArgumentNullException("filePath"); + } + if (onChangedCallback == null) + { + throw new ArgumentNullException("onChangedCallback"); + } + FileInfo fileInfo = new FileInfo(filePath); + string dir = Path.GetDirectoryName(filePath); + DirectoryMonitor dirMon = _dirMonitors[dir] as DirectoryMonitor; + if (dirMon == null) + { + lock (_lock) + { + dirMon = _dirMonitors[dir] as DirectoryMonitor; + if (dirMon == null) + { + dirMon = new DirectoryMonitor(); + dirMon.Fsw = new FileSystemWatcher(dir); + dirMon.Fsw.NotifyFilter = NotifyFilters.FileName + | NotifyFilters.DirectoryName + | NotifyFilters.CreationTime + | NotifyFilters.Size + | NotifyFilters.LastWrite + | NotifyFilters.Security; + dirMon.Fsw.EnableRaisingEvents = true; + } + _dirMonitors[dir] = dirMon; + } + } + + FileChangeEventTarget target = new FileChangeEventTarget(fileInfo.Name, onChangedCallback); + + lock (dirMon) + { + dirMon.Fsw.Changed += target.ChangedHandler; + dirMon.Fsw.Created += target.ChangedHandler; + dirMon.Fsw.Deleted += target.ChangedHandler; + dirMon.Fsw.Error += target.ErrorHandler; + dirMon.Fsw.Renamed += target.RenamedHandler; + } + + state = target; + lastWriteTime = File.GetLastWriteTime(filePath); + fileSize = (fileInfo.Exists) ? fileInfo.Length : -1; + } + + void IFileChangeNotificationSystem.StopMonitoring(string filePath, Object state) + { + if (filePath == null) + { + throw new ArgumentNullException("filePath"); + } + if (state == null) + { + throw new ArgumentNullException("state"); + } + FileChangeEventTarget target = state as FileChangeEventTarget; + if (target == null) + { + throw new ArgumentException(SR.Invalid_state, "state"); + } + string dir = Path.GetDirectoryName(filePath); + DirectoryMonitor dirMon = _dirMonitors[dir] as DirectoryMonitor; + if (dirMon != null) + { + lock (dirMon) + { + dirMon.Fsw.Changed -= target.ChangedHandler; + dirMon.Fsw.Created -= target.ChangedHandler; + dirMon.Fsw.Deleted -= target.ChangedHandler; + dirMon.Fsw.Error -= target.ErrorHandler; + dirMon.Fsw.Renamed -= target.RenamedHandler; + } + } + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/HostFileChangeMonitor.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/HostFileChangeMonitor.cs new file mode 100644 index 0000000000..fe73458a92 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/HostFileChangeMonitor.cs @@ -0,0 +1,189 @@ +// 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.Runtime.Caching.Hosting; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Caching.Resources; +using System.Globalization; +using System.Security; +using System.Security.Permissions; +using System.Text; +using System.Threading; + +namespace System.Runtime.Caching +{ + public sealed class HostFileChangeMonitor : FileChangeMonitor + { + private const int MAX_CHAR_COUNT_OF_LONG_CONVERTED_TO_HEXADECIMAL_STRING = 16; + private static IFileChangeNotificationSystem s_fcn; + private readonly ReadOnlyCollection _filePaths; + private String _uniqueId; + private Object _fcnState; + private DateTimeOffset _lastModified; + + private HostFileChangeMonitor() { } // hide default .ctor + + private void InitDisposableMembers() + { + bool dispose = true; + try + { + string uniqueId = null; + if (_filePaths.Count == 1) + { + string path = _filePaths[0]; + DateTimeOffset lastWrite; + long fileSize; + s_fcn.StartMonitoring(path, new OnChangedCallback(OnChanged), out _fcnState, out lastWrite, out fileSize); + uniqueId = path + lastWrite.UtcDateTime.Ticks.ToString("X", CultureInfo.InvariantCulture) + fileSize.ToString("X", CultureInfo.InvariantCulture); + _lastModified = lastWrite; + } + else + { + int capacity = 0; + foreach (string path in _filePaths) + { + capacity += path.Length + (2 * MAX_CHAR_COUNT_OF_LONG_CONVERTED_TO_HEXADECIMAL_STRING); + } + Hashtable fcnState = new Hashtable(_filePaths.Count); + _fcnState = fcnState; + StringBuilder sb = new StringBuilder(capacity); + foreach (string path in _filePaths) + { + if (fcnState.Contains(path)) + { + continue; + } + DateTimeOffset lastWrite; + long fileSize; + object state; + s_fcn.StartMonitoring(path, new OnChangedCallback(OnChanged), out state, out lastWrite, out fileSize); + fcnState[path] = state; + sb.Append(path); + sb.Append(lastWrite.UtcDateTime.Ticks.ToString("X", CultureInfo.InvariantCulture)); + sb.Append(fileSize.ToString("X", CultureInfo.InvariantCulture)); + if (lastWrite > _lastModified) + { + _lastModified = lastWrite; + } + } + uniqueId = sb.ToString(); + } + _uniqueId = uniqueId; + dispose = false; + } + finally + { + InitializationComplete(); + if (dispose) + { + Dispose(); + } + } + } + + private static void InitFCN() + { + if (s_fcn == null) + { + IFileChangeNotificationSystem fcn = null; + IServiceProvider host = ObjectCache.Host; + if (host != null) + { + fcn = host.GetService(typeof(IFileChangeNotificationSystem)) as IFileChangeNotificationSystem; + } + if (fcn == null) + { + fcn = new FileChangeNotificationSystem(); + } + Interlocked.CompareExchange(ref s_fcn, fcn, null); + } + } + + // + // protected members + // + + protected override void Dispose(bool disposing) + { + if (disposing && s_fcn != null) + { + if (_filePaths != null && _fcnState != null) + { + if (_filePaths.Count > 1) + { + Hashtable fcnState = _fcnState as Hashtable; + foreach (string path in _filePaths) + { + if (path != null) + { + object state = fcnState[path]; + if (state != null) + { + s_fcn.StopMonitoring(path, state); + } + } + } + } + else + { + string path = _filePaths[0]; + if (path != null && _fcnState != null) + { + s_fcn.StopMonitoring(path, _fcnState); + } + } + } + } + } + + // + // public and internal members + // + + public override ReadOnlyCollection FilePaths { get { return _filePaths; } } + public override String UniqueId { get { return _uniqueId; } } + public override DateTimeOffset LastModified { get { return _lastModified; } } + + public HostFileChangeMonitor(IList filePaths) + { + if (filePaths == null) + { + throw new ArgumentNullException("filePaths"); + } + if (filePaths.Count == 0) + { + throw new ArgumentException(RH.Format(SR.Empty_collection, "filePaths")); + } + + _filePaths = SanitizeFilePathsList(filePaths); + + InitFCN(); + InitDisposableMembers(); + } + + private static ReadOnlyCollection SanitizeFilePathsList(IList filePaths) + { + List newList = new List(filePaths.Count); + + foreach (string path in filePaths) + { + if (String.IsNullOrEmpty(path)) + { + throw new ArgumentException(RH.Format(SR.Collection_contains_null_or_empty_string, "filePaths")); + } + else + { + newList.Add(path); + } + } + + return newList.AsReadOnly(); + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Hosting/IApplicationIdentifier.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Hosting/IApplicationIdentifier.cs new file mode 100644 index 0000000000..4c3bceb889 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Hosting/IApplicationIdentifier.cs @@ -0,0 +1,13 @@ +// 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; + +namespace System.Runtime.Caching.Hosting +{ + public interface IApplicationIdentifier + { + String GetApplicationId(); + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Hosting/IFileChangeNotificationSystem.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Hosting/IFileChangeNotificationSystem.cs new file mode 100644 index 0000000000..48b7f02a5e --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Hosting/IFileChangeNotificationSystem.cs @@ -0,0 +1,15 @@ +// 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; + +namespace System.Runtime.Caching.Hosting +{ + public interface IFileChangeNotificationSystem + { + void StartMonitoring(string filePath, OnChangedCallback onChangedCallback, out Object state, out DateTimeOffset lastWriteTime, out long fileSize); + + void StopMonitoring(string filePath, Object state); + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Hosting/IMemoryCacheManager.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Hosting/IMemoryCacheManager.cs new file mode 100644 index 0000000000..3af77da53f --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Hosting/IMemoryCacheManager.cs @@ -0,0 +1,15 @@ +// 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; + +namespace System.Runtime.Caching.Hosting +{ + public interface IMemoryCacheManager + { + void UpdateCacheSize(long size, MemoryCache cache); + void ReleaseCache(MemoryCache cache); + } +} + diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCache.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCache.cs new file mode 100644 index 0000000000..a16077e0a7 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCache.cs @@ -0,0 +1,884 @@ +// 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.Runtime.Caching.Configuration; +using System.Runtime.Caching.Resources; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Collections.ObjectModel; +using System.Configuration; +using System.Diagnostics.CodeAnalysis; +using System.Security; +using System.Security.Permissions; +using System.Threading; + +namespace System.Runtime.Caching +{ + [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification = "The class represents a type of cache")] + public class MemoryCache : ObjectCache, IEnumerable, IDisposable + { + private const DefaultCacheCapabilities CAPABILITIES = DefaultCacheCapabilities.InMemoryProvider + | DefaultCacheCapabilities.CacheEntryChangeMonitors + | DefaultCacheCapabilities.AbsoluteExpirations + | DefaultCacheCapabilities.SlidingExpirations + | DefaultCacheCapabilities.CacheEntryUpdateCallback + | DefaultCacheCapabilities.CacheEntryRemovedCallback; + private static readonly TimeSpan s_oneYear = new TimeSpan(365, 0, 0, 0); + private static object s_initLock = new object(); + private static MemoryCache s_defaultCache; + private static CacheEntryRemovedCallback s_sentinelRemovedCallback = new CacheEntryRemovedCallback(SentinelEntry.OnCacheEntryRemovedCallback); + private GCHandleRef[] _storeRefs; + private int _storeCount; + private int _disposed; + private MemoryCacheStatistics _stats; + private string _name; + private PerfCounters _perfCounters; + private bool _configLess; + private bool _useMemoryCacheManager = true; + private EventHandler _onAppDomainUnload; + private UnhandledExceptionEventHandler _onUnhandledException; + + private bool IsDisposed { get { return (_disposed == 1); } } + internal bool ConfigLess { get { return _configLess; } } + + private class SentinelEntry + { + private string _key; + private ChangeMonitor _expensiveObjectDependency; + private CacheEntryUpdateCallback _updateCallback; + + internal SentinelEntry(string key, ChangeMonitor expensiveObjectDependency, CacheEntryUpdateCallback callback) + { + _key = key; + _expensiveObjectDependency = expensiveObjectDependency; + _updateCallback = callback; + } + + internal string Key + { + get { return _key; } + } + + internal ChangeMonitor ExpensiveObjectDependency + { + get { return _expensiveObjectDependency; } + } + + internal CacheEntryUpdateCallback CacheEntryUpdateCallback + { + get { return _updateCallback; } + } + + private static bool IsPolicyValid(CacheItemPolicy policy) + { + if (policy == null) + { + return false; + } + // see if any change monitors have changed + bool hasChanged = false; + Collection changeMonitors = policy.ChangeMonitors; + if (changeMonitors != null) + { + foreach (ChangeMonitor monitor in changeMonitors) + { + if (monitor != null && monitor.HasChanged) + { + hasChanged = true; + break; + } + } + } + // if the monitors haven't changed yet and we have an update callback + // then the policy is valid + if (!hasChanged && policy.UpdateCallback != null) + { + return true; + } + // if the monitors have changed we need to dispose them + if (hasChanged) + { + foreach (ChangeMonitor monitor in changeMonitors) + { + if (monitor != null) + { + monitor.Dispose(); + } + } + } + return false; + } + + internal static void OnCacheEntryRemovedCallback(CacheEntryRemovedArguments arguments) + { + MemoryCache cache = arguments.Source as MemoryCache; + SentinelEntry entry = arguments.CacheItem.Value as SentinelEntry; + CacheEntryRemovedReason reason = arguments.RemovedReason; + switch (reason) + { + case CacheEntryRemovedReason.Expired: + break; + case CacheEntryRemovedReason.ChangeMonitorChanged: + if (entry.ExpensiveObjectDependency.HasChanged) + { + // If the expensiveObject has been removed explicitly by Cache.Remove, + // return from the SentinelEntry removed callback + // thus effectively removing the SentinelEntry from the cache. + return; + } + break; + case CacheEntryRemovedReason.Evicted: + Dbg.Fail("Reason should never be CacheEntryRemovedReason.Evicted since the entry was inserted as NotRemovable."); + return; + default: + // do nothing if reason is Removed or CacheSpecificEviction + return; + } + + // invoke update callback + try + { + CacheEntryUpdateArguments args = new CacheEntryUpdateArguments(cache, reason, entry.Key, null); + entry.CacheEntryUpdateCallback(args); + Object expensiveObject = (args.UpdatedCacheItem != null) ? args.UpdatedCacheItem.Value : null; + CacheItemPolicy policy = args.UpdatedCacheItemPolicy; + // Only update the "expensive" object if the user returns a new object, + // a policy with update callback, and the change monitors haven't changed. (Inserting + // with change monitors that have already changed will cause recursion.) + if (expensiveObject != null && IsPolicyValid(policy)) + { + cache.Set(entry.Key, expensiveObject, policy); + } + else + { + cache.Remove(entry.Key); + } + } + catch + { + cache.Remove(entry.Key); + // Review: What should we do with this exception? + } + } + } + + // private and internal + + internal MemoryCacheStore GetStore(MemoryCacheKey cacheKey) + { + int hashCode = cacheKey.Hash; + if (hashCode < 0) + { + hashCode = (hashCode == Int32.MinValue) ? 0 : -hashCode; + } + int idx = hashCode % _storeCount; + return _storeRefs[idx].Target; + } + + internal object[] AllSRefTargets + { + get + { + var allStores = new MemoryCacheStore[_storeCount]; + for (int i = 0; i < _storeCount; i++) + { + allStores[i] = _storeRefs[i].Target; + } + return allStores; + } + } + + private void InitDisposableMembers(NameValueCollection config) + { + bool dispose = true; + try + { + try + { + _perfCounters = new PerfCounters(_name); + } + catch + { + // ignore exceptions from perf counters + } + for (int i = 0; i < _storeCount; i++) + { + _storeRefs[i] = new GCHandleRef(new MemoryCacheStore(this, _perfCounters)); + } + _stats = new MemoryCacheStatistics(this, config); + AppDomain appDomain = Thread.GetDomain(); + EventHandler onAppDomainUnload = new EventHandler(OnAppDomainUnload); + appDomain.DomainUnload += onAppDomainUnload; + _onAppDomainUnload = onAppDomainUnload; + UnhandledExceptionEventHandler onUnhandledException = new UnhandledExceptionEventHandler(OnUnhandledException); + appDomain.UnhandledException += onUnhandledException; + _onUnhandledException = onUnhandledException; + dispose = false; + } + finally + { + if (dispose) + { + Dispose(); + } + } + } + + private void OnAppDomainUnload(Object unusedObject, EventArgs unusedEventArgs) + { + Dispose(); + } + + private void OnUnhandledException(Object sender, UnhandledExceptionEventArgs eventArgs) + { + // if the CLR is terminating, dispose the cache. + // This will dispose the perf counters + if (eventArgs.IsTerminating) + { + Dispose(); + } + } + + private void ValidatePolicy(CacheItemPolicy policy) + { + if (policy.AbsoluteExpiration != ObjectCache.InfiniteAbsoluteExpiration + && policy.SlidingExpiration != ObjectCache.NoSlidingExpiration) + { + throw new ArgumentException(SR.Invalid_expiration_combination, "policy"); + } + if (policy.SlidingExpiration < ObjectCache.NoSlidingExpiration || s_oneYear < policy.SlidingExpiration) + { + throw new ArgumentOutOfRangeException("policy", RH.Format(SR.Argument_out_of_range, "SlidingExpiration", ObjectCache.NoSlidingExpiration, s_oneYear)); + } + if (policy.RemovedCallback != null + && policy.UpdateCallback != null) + { + throw new ArgumentException(SR.Invalid_callback_combination, "policy"); + } + if (policy.Priority != CacheItemPriority.Default && policy.Priority != CacheItemPriority.NotRemovable) + { + throw new ArgumentOutOfRangeException("policy", RH.Format(SR.Argument_out_of_range, "Priority", CacheItemPriority.Default, CacheItemPriority.NotRemovable)); + } + } + + // public + + // Amount of memory that can be used before + // the cache begins to forcibly remove items. + public long CacheMemoryLimit + { + get + { + return _stats.CacheMemoryLimit; + } + } + + public static MemoryCache Default + { + get + { + if (s_defaultCache == null) + { + lock (s_initLock) + { + if (s_defaultCache == null) + { + s_defaultCache = new MemoryCache(); + } + } + } + return s_defaultCache; + } + } + + public override DefaultCacheCapabilities DefaultCacheCapabilities + { + get + { + return CAPABILITIES; + } + } + + public override string Name + { + get { return _name; } + } + + internal bool UseMemoryCacheManager + { + get { return _useMemoryCacheManager; } + } + + // Percentage of physical memory that can be used before + // the cache begins to forcibly remove items. + public long PhysicalMemoryLimit + { + get + { + return _stats.PhysicalMemoryLimit; + } + } + + // The maximum interval of time afterwhich the cache + // will update its memory statistics. + public TimeSpan PollingInterval + { + get + { + return _stats.PollingInterval; + } + } + + // Only used for Default MemoryCache + private MemoryCache() + { + _name = "Default"; + Init(null); + } + + public MemoryCache(string name, NameValueCollection config = null) + { + if (name == null) + { + throw new ArgumentNullException("name"); + } + if (name == String.Empty) + { + throw new ArgumentException(SR.Empty_string_invalid, "name"); + } + if (String.Equals(name, "default", StringComparison.OrdinalIgnoreCase)) + { + throw new ArgumentException(SR.Default_is_reserved, "name"); + } + _name = name; + Init(config); + } + + // ignoreConfigSection is used when redirecting ASP.NET cache into the MemoryCache. This avoids infinite recursion + // due to the fact that the (ASP.NET) config system uses the cache, and the cache uses the config system. + public MemoryCache(string name, NameValueCollection config, bool ignoreConfigSection) + { + if (name == null) + { + throw new ArgumentNullException("name"); + } + if (name == String.Empty) + { + throw new ArgumentException(SR.Empty_string_invalid, "name"); + } + if (String.Equals(name, "default", StringComparison.OrdinalIgnoreCase)) + { + throw new ArgumentException(SR.Default_is_reserved, "name"); + } + _name = name; + _configLess = ignoreConfigSection; + Init(config); + } + + private void Init(NameValueCollection config) + { + _storeCount = Environment.ProcessorCount; + _storeRefs = new GCHandleRef[_storeCount]; + if (config != null) + { + _useMemoryCacheManager = ConfigUtil.GetBooleanValue(config, ConfigUtil.UseMemoryCacheManager, true); + } + InitDisposableMembers(config); + } + + private object AddOrGetExistingInternal(string key, object value, CacheItemPolicy policy) + { + if (key == null) + { + throw new ArgumentNullException("key"); + } + DateTimeOffset absExp = ObjectCache.InfiniteAbsoluteExpiration; + TimeSpan slidingExp = ObjectCache.NoSlidingExpiration; + CacheItemPriority priority = CacheItemPriority.Default; + Collection changeMonitors = null; + CacheEntryRemovedCallback removedCallback = null; + if (policy != null) + { + ValidatePolicy(policy); + if (policy.UpdateCallback != null) + { + throw new ArgumentException(SR.Update_callback_must_be_null, "policy"); + } + absExp = policy.AbsoluteExpiration; + slidingExp = policy.SlidingExpiration; + priority = policy.Priority; + changeMonitors = policy.ChangeMonitors; + removedCallback = policy.RemovedCallback; + } + if (IsDisposed) + { + if (changeMonitors != null) + { + foreach (ChangeMonitor monitor in changeMonitors) + { + if (monitor != null) + { + monitor.Dispose(); + } + } + } + return null; + } + MemoryCacheKey cacheKey = new MemoryCacheKey(key); + MemoryCacheStore store = GetStore(cacheKey); + MemoryCacheEntry entry = store.AddOrGetExisting(cacheKey, new MemoryCacheEntry(key, value, absExp, slidingExp, priority, changeMonitors, removedCallback, this)); + return (entry != null) ? entry.Value : null; + } + + public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(IEnumerable keys, String regionName = null) + { + if (regionName != null) + { + throw new NotSupportedException(SR.RegionName_not_supported); + } + if (keys == null) + { + throw new ArgumentNullException("keys"); + } + List keysClone = new List(keys); + if (keysClone.Count == 0) + { + throw new ArgumentException(RH.Format(SR.Empty_collection, "keys")); + } + + foreach (string key in keysClone) + { + if (key == null) + { + throw new ArgumentException(RH.Format(SR.Collection_contains_null_element, "keys")); + } + } + + return new MemoryCacheEntryChangeMonitor(keysClone.AsReadOnly(), regionName, this); + } + + public void Dispose() + { + if (Interlocked.Exchange(ref _disposed, 1) == 0) + { + // unhook domain events + DisposeSafeCritical(); + // stats must be disposed prior to disposing the stores. + if (_stats != null) + { + _stats.Dispose(); + } + if (_storeRefs != null) + { + foreach (var storeRef in _storeRefs) + { + if (storeRef != null) + { + storeRef.Dispose(); + } + } + } + if (_perfCounters != null) + { + _perfCounters.Dispose(); + } + GC.SuppressFinalize(this); + } + } + + private void DisposeSafeCritical() + { + AppDomain appDomain = Thread.GetDomain(); + if (_onAppDomainUnload != null) + { + appDomain.DomainUnload -= _onAppDomainUnload; + } + if (_onUnhandledException != null) + { + appDomain.UnhandledException -= _onUnhandledException; + } + } + + private object GetInternal(string key, string regionName) + { + if (regionName != null) + { + throw new NotSupportedException(SR.RegionName_not_supported); + } + if (key == null) + { + throw new ArgumentNullException("key"); + } + MemoryCacheEntry entry = GetEntry(key); + return (entry != null) ? entry.Value : null; + } + + internal MemoryCacheEntry GetEntry(String key) + { + if (IsDisposed) + { + return null; + } + MemoryCacheKey cacheKey = new MemoryCacheKey(key); + MemoryCacheStore store = GetStore(cacheKey); + return store.Get(cacheKey); + } + + IEnumerator IEnumerable.GetEnumerator() + { + Hashtable h = new Hashtable(); + if (!IsDisposed) + { + foreach (var storeRef in _storeRefs) + { + storeRef.Target.CopyTo(h); + } + } + return h.GetEnumerator(); + } + + protected override IEnumerator> GetEnumerator() + { + Dictionary h = new Dictionary(); + if (!IsDisposed) + { + foreach (var storeRef in _storeRefs) + { + storeRef.Target.CopyTo(h); + } + } + return h.GetEnumerator(); + } + + internal MemoryCacheEntry RemoveEntry(string key, MemoryCacheEntry entry, CacheEntryRemovedReason reason) + { + MemoryCacheKey cacheKey = new MemoryCacheKey(key); + MemoryCacheStore store = GetStore(cacheKey); + return store.Remove(cacheKey, entry, reason); + } + + public long Trim(int percent) + { + if (percent > 100) + { + percent = 100; + } + long trimmed = 0; + if (_disposed == 0) + { + foreach (var storeRef in _storeRefs) + { + trimmed += storeRef.Target.TrimInternal(percent); + } + } + return trimmed; + } + + //Default indexer property + public override object this[string key] + { + get + { + return GetInternal(key, null); + } + set + { + Set(key, value, ObjectCache.InfiniteAbsoluteExpiration); + } + } + + //Existence check for a single item + public override bool Contains(string key, string regionName = null) + { + return (GetInternal(key, regionName) != null); + } + + public override bool Add(CacheItem item, CacheItemPolicy policy) + { + CacheItem existingEntry = AddOrGetExisting(item, policy); + return (existingEntry == null || existingEntry.Value == null); + } + + public override object AddOrGetExisting(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null) + { + if (regionName != null) + { + throw new NotSupportedException(SR.RegionName_not_supported); + } + CacheItemPolicy policy = new CacheItemPolicy(); + policy.AbsoluteExpiration = absoluteExpiration; + return AddOrGetExistingInternal(key, value, policy); + } + + public override CacheItem AddOrGetExisting(CacheItem item, CacheItemPolicy policy) + { + if (item == null) + { + throw new ArgumentNullException("item"); + } + return new CacheItem(item.Key, AddOrGetExistingInternal(item.Key, item.Value, policy)); + } + + public override object AddOrGetExisting(string key, object value, CacheItemPolicy policy, string regionName = null) + { + if (regionName != null) + { + throw new NotSupportedException(SR.RegionName_not_supported); + } + return AddOrGetExistingInternal(key, value, policy); + } + + public override object Get(string key, string regionName = null) + { + return GetInternal(key, regionName); + } + + public override CacheItem GetCacheItem(string key, string regionName = null) + { + object value = GetInternal(key, regionName); + return (value != null) ? new CacheItem(key, value) : null; + } + + public override void Set(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null) + { + if (regionName != null) + { + throw new NotSupportedException(SR.RegionName_not_supported); + } + CacheItemPolicy policy = new CacheItemPolicy(); + policy.AbsoluteExpiration = absoluteExpiration; + Set(key, value, policy); + } + + public override void Set(CacheItem item, CacheItemPolicy policy) + { + if (item == null) + { + throw new ArgumentNullException("item"); + } + Set(item.Key, item.Value, policy); + } + + public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null) + { + if (regionName != null) + { + throw new NotSupportedException(SR.RegionName_not_supported); + } + if (key == null) + { + throw new ArgumentNullException("key"); + } + DateTimeOffset absExp = ObjectCache.InfiniteAbsoluteExpiration; + TimeSpan slidingExp = ObjectCache.NoSlidingExpiration; + CacheItemPriority priority = CacheItemPriority.Default; + Collection changeMonitors = null; + CacheEntryRemovedCallback removedCallback = null; + if (policy != null) + { + ValidatePolicy(policy); + if (policy.UpdateCallback != null) + { + Set(key, value, policy.ChangeMonitors, policy.AbsoluteExpiration, policy.SlidingExpiration, policy.UpdateCallback); + return; + } + absExp = policy.AbsoluteExpiration; + slidingExp = policy.SlidingExpiration; + priority = policy.Priority; + changeMonitors = policy.ChangeMonitors; + removedCallback = policy.RemovedCallback; + } + if (IsDisposed) + { + if (changeMonitors != null) + { + foreach (ChangeMonitor monitor in changeMonitors) + { + if (monitor != null) + { + monitor.Dispose(); + } + } + } + return; + } + MemoryCacheKey cacheKey = new MemoryCacheKey(key); + MemoryCacheStore store = GetStore(cacheKey); + store.Set(cacheKey, new MemoryCacheEntry(key, value, absExp, slidingExp, priority, changeMonitors, removedCallback, this)); + } + + internal void Set(string key, + object value, + Collection changeMonitors, + DateTimeOffset absoluteExpiration, + TimeSpan slidingExpiration, + CacheEntryUpdateCallback onUpdateCallback) + { + if (key == null) + { + throw new ArgumentNullException("key"); + } + if (changeMonitors == null + && absoluteExpiration == ObjectCache.InfiniteAbsoluteExpiration + && slidingExpiration == ObjectCache.NoSlidingExpiration) + { + throw new ArgumentException(SR.Invalid_argument_combination); + } + if (onUpdateCallback == null) + { + throw new ArgumentNullException("onUpdateCallback"); + } + if (IsDisposed) + { + if (changeMonitors != null) + { + foreach (ChangeMonitor monitor in changeMonitors) + { + if (monitor != null) + { + monitor.Dispose(); + } + } + } + return; + } + // Insert updatable cache entry + MemoryCacheKey cacheKey = new MemoryCacheKey(key); + MemoryCacheStore store = GetStore(cacheKey); + MemoryCacheEntry cacheEntry = new MemoryCacheEntry(key, + value, + ObjectCache.InfiniteAbsoluteExpiration, + ObjectCache.NoSlidingExpiration, + CacheItemPriority.NotRemovable, + null, + null, + this); + store.Set(cacheKey, cacheEntry); + + // Ensure the sentinel depends on its updatable entry + string[] cacheKeys = { key }; + ChangeMonitor expensiveObjectDep = CreateCacheEntryChangeMonitor(cacheKeys); + if (changeMonitors == null) + { + changeMonitors = new Collection(); + } + changeMonitors.Add(expensiveObjectDep); + + // Insert sentinel entry for the updatable cache entry + MemoryCacheKey sentinelCacheKey = new MemoryCacheKey("OnUpdateSentinel" + key); + MemoryCacheStore sentinelStore = GetStore(sentinelCacheKey); + MemoryCacheEntry sentinelCacheEntry = new MemoryCacheEntry(sentinelCacheKey.Key, + new SentinelEntry(key, expensiveObjectDep, onUpdateCallback), + absoluteExpiration, + slidingExpiration, + CacheItemPriority.NotRemovable, + changeMonitors, + s_sentinelRemovedCallback, + this); + sentinelStore.Set(sentinelCacheKey, sentinelCacheEntry); + cacheEntry.ConfigureUpdateSentinel(sentinelStore, sentinelCacheEntry); + } + + public override object Remove(string key, string regionName = null) + { + return Remove(key, CacheEntryRemovedReason.Removed, regionName); + } + + public object Remove(string key, CacheEntryRemovedReason reason, string regionName = null) + { + if (regionName != null) + { + throw new NotSupportedException(SR.RegionName_not_supported); + } + if (key == null) + { + throw new ArgumentNullException("key"); + } + if (IsDisposed) + { + return null; + } + MemoryCacheEntry entry = RemoveEntry(key, null, reason); + return (entry != null) ? entry.Value : null; + } + + public override long GetCount(string regionName = null) + { + if (regionName != null) + { + throw new NotSupportedException(SR.RegionName_not_supported); + } + long count = 0; + if (!IsDisposed) + { + foreach (var storeRef in _storeRefs) + { + count += storeRef.Target.Count; + } + } + return count; + } + + public long GetLastSize(string regionName = null) + { + if (regionName != null) + { + throw new NotSupportedException(SR.RegionName_not_supported); + } + + return _stats.GetLastSize(); + } + + public override IDictionary GetValues(IEnumerable keys, string regionName = null) + { + if (regionName != null) + { + throw new NotSupportedException(SR.RegionName_not_supported); + } + if (keys == null) + { + throw new ArgumentNullException("keys"); + } + Dictionary values = null; + if (!IsDisposed) + { + foreach (string key in keys) + { + if (key == null) + { + throw new ArgumentException(RH.Format(SR.Collection_contains_null_element, "keys")); + } + object value = GetInternal(key, null); + if (value != null) + { + if (values == null) + { + values = new Dictionary(); + } + values[key] = value; + } + } + } + return values; + } + + // used when redirecting ASP.NET cache into the MemoryCache. This avoids infinite recursion + // due to the fact that the (ASP.NET) config system uses the cache, and the cache uses the + // config system. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Grandfathered suppression from original caching code checkin")] + internal void UpdateConfig(NameValueCollection config) + { + if (config == null) + { + throw new ArgumentNullException("config"); + } + if (!IsDisposed) + { + _stats.UpdateConfig(config); + } + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheEntry.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheEntry.cs new file mode 100644 index 0000000000..7c24f26a7b --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheEntry.cs @@ -0,0 +1,354 @@ +// 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.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; +using System.Threading; + +namespace System.Runtime.Caching +{ + internal class MemoryCacheEntry : MemoryCacheKey + { + private Object _value; + private DateTime _utcCreated; + private int _state; + // expiration + private DateTime _utcAbsExp; + private TimeSpan _slidingExp; + private ExpiresEntryRef _expiresEntryRef; + private byte _expiresBucket; // index of the expiration list (bucket) + // usage + private byte _usageBucket; // index of the usage list (== priority-1) + private UsageEntryRef _usageEntryRef; // ref into the usage list + private DateTime _utcLastUpdateUsage; // time we last updated usage + + private CacheEntryRemovedCallback _callback; + private SeldomUsedFields _fields; // optimization to reduce workingset when the entry hasn't any dependencies + + private class SeldomUsedFields + { + internal Collection _dependencies; // the entry's dependency needs to be disposed when the entry is released + internal Dictionary _dependents; // dependents must be notified when this entry is removed + internal MemoryCache _cache; + internal Tuple _updateSentinel; // the MemoryCacheEntry (and its associated store) of the OnUpdateSentinel for this entry, if there is one + } + + internal Object Value + { + get { return _value; } + } + + internal bool HasExpiration() + { + return _utcAbsExp < DateTime.MaxValue; + } + + internal DateTime UtcAbsExp + { + get { return _utcAbsExp; } + set { _utcAbsExp = value; } + } + + internal DateTime UtcCreated + { + get { return _utcCreated; } + } + + internal ExpiresEntryRef ExpiresEntryRef + { + get { return _expiresEntryRef; } + set { _expiresEntryRef = value; } + } + + internal byte ExpiresBucket + { + get { return _expiresBucket; } + set { _expiresBucket = value; } + } + + internal bool InExpires() + { + return !_expiresEntryRef.IsInvalid; + } + + internal TimeSpan SlidingExp + { + get { return _slidingExp; } + } + + internal EntryState State + { + get { return (EntryState)_state; } + set { _state = (int)value; } + } + + internal byte UsageBucket + { + get { return _usageBucket; } + } + + internal UsageEntryRef UsageEntryRef + { + get { return _usageEntryRef; } + set { _usageEntryRef = value; } + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Grandfathered suppression from original caching code checkin")] + internal DateTime UtcLastUpdateUsage + { + get { return _utcLastUpdateUsage; } + set { _utcLastUpdateUsage = value; } + } + + internal MemoryCacheEntry(String key, + Object value, + DateTimeOffset absExp, + TimeSpan slidingExp, + CacheItemPriority priority, + Collection dependencies, + CacheEntryRemovedCallback removedCallback, + MemoryCache cache) : base(key) + { + if (value == null) + { + throw new ArgumentNullException("value"); + } + _utcCreated = DateTime.UtcNow; + _value = value; + + _slidingExp = slidingExp; + if (_slidingExp > TimeSpan.Zero) + { + _utcAbsExp = _utcCreated + _slidingExp; + } + else + { + _utcAbsExp = absExp.UtcDateTime; + } + + _expiresEntryRef = ExpiresEntryRef.INVALID; + _expiresBucket = 0xff; + + _usageEntryRef = UsageEntryRef.INVALID; + if (priority == CacheItemPriority.NotRemovable) + { + _usageBucket = 0xff; + } + else + { + _usageBucket = 0; + } + + _callback = removedCallback; + + if (dependencies != null) + { + _fields = new SeldomUsedFields(); + _fields._dependencies = dependencies; + _fields._cache = cache; + } + } + + internal void AddDependent(MemoryCache cache, MemoryCacheEntryChangeMonitor dependent) + { + lock (this) + { + if (State > EntryState.AddedToCache) + { + return; + } + if (_fields == null) + { + _fields = new SeldomUsedFields(); + } + if (_fields._cache == null) + { + _fields._cache = cache; + } + if (_fields._dependents == null) + { + _fields._dependents = new Dictionary(); + } + _fields._dependents[dependent] = dependent; + } + } + + private void CallCacheEntryRemovedCallback(MemoryCache cache, CacheEntryRemovedReason reason) + { + if (_callback == null) + { + return; + } + CacheEntryRemovedArguments args = new CacheEntryRemovedArguments(cache, reason, new CacheItem(Key, _value)); + try + { + _callback(args); + } + catch + { + // + } + } + + internal void CallNotifyOnChanged() + { + if (_fields != null && _fields._dependencies != null) + { + foreach (ChangeMonitor monitor in _fields._dependencies) + { + monitor.NotifyOnChanged(new OnChangedCallback(this.OnDependencyChanged)); + } + } + } + + internal bool CompareExchangeState(EntryState value, EntryState comparand) + { + return (Interlocked.CompareExchange(ref _state, (int)value, (int)comparand) == (int)comparand); + } + + // Associates this entry with an update sentinel. If this entry has a sliding expiration, we need to + // touch the sentinel so that it doesn't expire. + internal void ConfigureUpdateSentinel(MemoryCacheStore sentinelStore, MemoryCacheEntry sentinelEntry) + { + lock (this) + { + if (_fields == null) + { + _fields = new SeldomUsedFields(); + } + _fields._updateSentinel = Tuple.Create(sentinelStore, sentinelEntry); + } + } + + internal bool HasUsage() + { + return _usageBucket != 0xff; + } + + internal bool InUsage() + { + return !_usageEntryRef.IsInvalid; + } + + private void OnDependencyChanged(Object state) + { + if (State == EntryState.AddedToCache) + { + _fields._cache.RemoveEntry(this.Key, this, CacheEntryRemovedReason.ChangeMonitorChanged); + } + } + + internal void Release(MemoryCache cache, CacheEntryRemovedReason reason) + { + State = EntryState.Closed; + + // Are there any cache entries that depend on this entry? + // If so, we need to fire their dependencies. + Dictionary.KeyCollection deps = null; + // clone the dependents + lock (this) + { + if (_fields != null && _fields._dependents != null && _fields._dependents.Count > 0) + { + deps = _fields._dependents.Keys; + // set to null so RemoveDependent does not attempt to access it, since we're not + // using a copy of the KeyCollection. + _fields._dependents = null; + Dbg.Assert(_fields._dependents == null, "_fields._dependents == null"); + } + } + if (deps != null) + { + foreach (MemoryCacheEntryChangeMonitor dependent in deps) + { + if (dependent != null) + { + dependent.OnCacheEntryReleased(); + } + } + } + + CallCacheEntryRemovedCallback(cache, reason); + + // Dispose any dependencies + if (_fields != null && _fields._dependencies != null) + { + foreach (ChangeMonitor monitor in _fields._dependencies) + { + monitor.Dispose(); + } + } + } + + internal void RemoveDependent(MemoryCacheEntryChangeMonitor dependent) + { + lock (this) + { + if (_fields != null && _fields._dependents != null) + { + _fields._dependents.Remove(dependent); + } + } + } + + internal void UpdateSlidingExp(DateTime utcNow, CacheExpires expires) + { + if (_slidingExp > TimeSpan.Zero) + { + DateTime utcNewExpires = utcNow + _slidingExp; + if (utcNewExpires - _utcAbsExp >= CacheExpires.MIN_UPDATE_DELTA || utcNewExpires < _utcAbsExp) + { + expires.UtcUpdate(this, utcNewExpires); + } + } + } + + internal void UpdateSlidingExpForUpdateSentinel() + { + // We don't need a lock to get information about the update sentinel + SeldomUsedFields fields = _fields; + if (fields != null) + { + Tuple sentinelInfo = fields._updateSentinel; + + // touch the update sentinel to keep it from expiring + if (sentinelInfo != null) + { + MemoryCacheStore sentinelStore = sentinelInfo.Item1; + MemoryCacheEntry sentinelEntry = sentinelInfo.Item2; + sentinelStore.UpdateExpAndUsage(sentinelEntry, updatePerfCounters: false); // perf counters shouldn't be polluted by touching update sentinel entry + } + } + } + + internal void UpdateUsage(DateTime utcNow, CacheUsage usage) + { + // update, but not more frequently than once per second. + if (InUsage() && _utcLastUpdateUsage < utcNow - CacheUsage.CORRELATED_REQUEST_TIMEOUT) + { + _utcLastUpdateUsage = utcNow; + usage.Update(this); + if (_fields != null && _fields._dependencies != null) + { + foreach (ChangeMonitor monitor in _fields._dependencies) + { + MemoryCacheEntryChangeMonitor m = monitor as MemoryCacheEntryChangeMonitor; + if (m == null) + { + continue; + } + foreach (MemoryCacheEntry e in m.Dependencies) + { + MemoryCacheStore store = e._fields._cache.GetStore(e); + e.UpdateUsage(utcNow, store.Usage); + } + } + } + } + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheEntryChangeMonitor.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheEntryChangeMonitor.cs new file mode 100644 index 0000000000..25a6e101fd --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheEntryChangeMonitor.cs @@ -0,0 +1,151 @@ +// 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.Runtime.Caching.Resources; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Text; + +namespace System.Runtime.Caching +{ + internal sealed class MemoryCacheEntryChangeMonitor : CacheEntryChangeMonitor + { + // use UTC minimum DateTime for error free conversions to DateTimeOffset + private static readonly DateTime s_DATETIME_MINVALUE_UTC = new DateTime(0, DateTimeKind.Utc); + private const int MAX_CHAR_COUNT_OF_LONG_CONVERTED_TO_HEXADECIMAL_STRING = 16; + private ReadOnlyCollection _keys; + private String _regionName; + private String _uniqueId; + private DateTimeOffset _lastModified; + private List _dependencies; + + private MemoryCacheEntryChangeMonitor() { } // hide default .ctor + + private void InitDisposableMembers(MemoryCache cache) + { + bool dispose = true; + try + { + bool hasChanged = false; + string uniqueId = null; + _dependencies = new List(_keys.Count); + if (_keys.Count == 1) + { + string k = _keys[0]; + MemoryCacheEntry entry = cache.GetEntry(k); + DateTime utcCreated = s_DATETIME_MINVALUE_UTC; + StartMonitoring(cache, entry, ref hasChanged, ref utcCreated); + uniqueId = k + utcCreated.Ticks.ToString("X", CultureInfo.InvariantCulture); + _lastModified = utcCreated; + } + else + { + int capacity = 0; + foreach (string key in _keys) + { + capacity += key.Length + MAX_CHAR_COUNT_OF_LONG_CONVERTED_TO_HEXADECIMAL_STRING; + } + StringBuilder sb = new StringBuilder(capacity); + foreach (string key in _keys) + { + MemoryCacheEntry entry = cache.GetEntry(key); + DateTime utcCreated = s_DATETIME_MINVALUE_UTC; + StartMonitoring(cache, entry, ref hasChanged, ref utcCreated); + sb.Append(key); + sb.Append(utcCreated.Ticks.ToString("X", CultureInfo.InvariantCulture)); + if (utcCreated > _lastModified) + { + _lastModified = utcCreated; + } + } + uniqueId = sb.ToString(); + } + _uniqueId = uniqueId; + if (hasChanged) + { + OnChanged(null); + } + dispose = false; + } + finally + { + InitializationComplete(); + if (dispose) + { + Dispose(); + } + } + } + + private void StartMonitoring(MemoryCache cache, MemoryCacheEntry entry, ref bool hasChanged, ref DateTime utcCreated) + { + if (entry != null) + { + // pass reference to self so the dependency can notify us when it changes + entry.AddDependent(cache, this); + // add dependency to collection so we can dispose it later + _dependencies.Add(entry); + // has the entry already changed? + if (entry.State != EntryState.AddedToCache) + { + hasChanged = true; + } + utcCreated = entry.UtcCreated; + } + else + { + // the entry does not exist--set hasChanged to true so the user is notified + hasChanged = true; + } + } + + // + // protected members + // + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (_dependencies != null) + { + foreach (MemoryCacheEntry entry in _dependencies) + { + if (entry != null) + { + entry.RemoveDependent(this); + } + } + } + } + } + + // + // public and internal members + // + + public override ReadOnlyCollection CacheKeys { get { return new ReadOnlyCollection(_keys); } } + public override string RegionName { get { return _regionName; } } + public override string UniqueId { get { return _uniqueId; } } + public override DateTimeOffset LastModified { get { return _lastModified; } } + internal List Dependencies { get { return _dependencies; } } + + internal MemoryCacheEntryChangeMonitor(ReadOnlyCollection keys, String regionName, MemoryCache cache) + { + Dbg.Assert(keys != null && keys.Count > 0, "keys != null && keys.Count > 0"); + _keys = keys; + _regionName = regionName; + InitDisposableMembers(cache); + } + + // invoked by a cache entry dependency when it is released from the cache + internal void OnCacheEntryReleased() + { + OnChanged(null); + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheKey.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheKey.cs new file mode 100644 index 0000000000..f1e4fb844a --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheKey.cs @@ -0,0 +1,25 @@ +// 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.Collections; +using System.Diagnostics.CodeAnalysis; + +namespace System.Runtime.Caching +{ + internal class MemoryCacheKey + { + private String _key; + private int _hash; + + internal int Hash { get { return _hash; } } + internal String Key { get { return _key; } } + + internal MemoryCacheKey(String key) + { + _key = key; + _hash = key.GetHashCode(); + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheKeyEqualityComparer.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheKeyEqualityComparer.cs new file mode 100644 index 0000000000..4c98fcb62f --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheKeyEqualityComparer.cs @@ -0,0 +1,30 @@ +// 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.Collections; + +namespace System.Runtime.Caching +{ + internal class MemoryCacheEqualityComparer : IEqualityComparer + { + bool IEqualityComparer.Equals(Object x, Object y) + { + Dbg.Assert(x != null && x is MemoryCacheKey); + Dbg.Assert(y != null && y is MemoryCacheKey); + + MemoryCacheKey a, b; + a = (MemoryCacheKey)x; + b = (MemoryCacheKey)y; + + return (String.Compare(a.Key, b.Key, StringComparison.Ordinal) == 0); + } + + int IEqualityComparer.GetHashCode(Object obj) + { + MemoryCacheKey cacheKey = (MemoryCacheKey)obj; + return cacheKey.Hash; + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheStatistics.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheStatistics.cs new file mode 100644 index 0000000000..7b1430dbdb --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheStatistics.cs @@ -0,0 +1,390 @@ +// 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.Collections.Specialized; +using System.Configuration; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Runtime.Caching.Configuration; +using System.Runtime.InteropServices; +using System.Security; +using System.Threading; + +namespace System.Runtime.Caching +{ + internal sealed class MemoryCacheStatistics : IDisposable + { + private const int MEMORYSTATUS_INTERVAL_5_SECONDS = 5 * 1000; + private const int MEMORYSTATUS_INTERVAL_30_SECONDS = 30 * 1000; + + private int _configCacheMemoryLimitMegabytes; + private int _configPhysicalMemoryLimitPercentage; + private int _configPollingInterval; + private int _inCacheManagerThread; + private int _disposed; + private long _lastTrimCount; + private long _lastTrimDurationTicks; // used only for debugging + private int _lastTrimGen2Count; + private int _lastTrimPercent; + private DateTime _lastTrimTime; + private int _pollingInterval; + private GCHandleRef _timerHandleRef; + private Object _timerLock; + private long _totalCountBeforeTrim; + + private CacheMemoryMonitor _cacheMemoryMonitor; + private MemoryCache _memoryCache; + private PhysicalMemoryMonitor _physicalMemoryMonitor; + + // private + + private MemoryCacheStatistics() + { + //hide default ctor + } + + private void AdjustTimer() + { + lock (_timerLock) + { + if (_timerHandleRef == null) + return; + + Timer timer = _timerHandleRef.Target; + + // the order of these if statements is important + + // When above the high pressure mark, interval should be 5 seconds or less + if (_physicalMemoryMonitor.IsAboveHighPressure() || _cacheMemoryMonitor.IsAboveHighPressure()) + { + if (_pollingInterval > MEMORYSTATUS_INTERVAL_5_SECONDS) + { + _pollingInterval = MEMORYSTATUS_INTERVAL_5_SECONDS; + timer.Change(_pollingInterval, _pollingInterval); + } + return; + } + + // When above half the low pressure mark, interval should be 30 seconds or less + if ((_cacheMemoryMonitor.PressureLast > _cacheMemoryMonitor.PressureLow / 2) + || (_physicalMemoryMonitor.PressureLast > _physicalMemoryMonitor.PressureLow / 2)) + { + // allow interval to fall back down when memory pressure goes away + int newPollingInterval = Math.Min(_configPollingInterval, MEMORYSTATUS_INTERVAL_30_SECONDS); + if (_pollingInterval != newPollingInterval) + { + _pollingInterval = newPollingInterval; + timer.Change(_pollingInterval, _pollingInterval); + } + return; + } + + // there is no pressure, interval should be the value from config + if (_pollingInterval != _configPollingInterval) + { + _pollingInterval = _configPollingInterval; + timer.Change(_pollingInterval, _pollingInterval); + } + } + } + + // timer callback + private void CacheManagerTimerCallback(object state) + { + CacheManagerThread(0); + } + + internal long GetLastSize() + { + return _cacheMemoryMonitor.PressureLast; + } + + private int GetPercentToTrim() + { + int gen2Count = GC.CollectionCount(2); + // has there been a Gen 2 Collection since the last trim? + if (gen2Count != _lastTrimGen2Count) + { + return Math.Max(_physicalMemoryMonitor.GetPercentToTrim(_lastTrimTime, _lastTrimPercent), _cacheMemoryMonitor.GetPercentToTrim(_lastTrimTime, _lastTrimPercent)); + } + else + { + return 0; + } + } + + private void InitializeConfiguration(NameValueCollection config) + { + MemoryCacheElement element = null; + if (!_memoryCache.ConfigLess) + { + MemoryCacheSection section = ConfigurationManager.GetSection("system.runtime.caching/memoryCache") as MemoryCacheSection; + if (section != null) + { + element = section.NamedCaches[_memoryCache.Name]; + } + } + + if (element != null) + { + _configCacheMemoryLimitMegabytes = element.CacheMemoryLimitMegabytes; + _configPhysicalMemoryLimitPercentage = element.PhysicalMemoryLimitPercentage; + double milliseconds = element.PollingInterval.TotalMilliseconds; + _configPollingInterval = (milliseconds < (double)Int32.MaxValue) ? (int)milliseconds : Int32.MaxValue; + } + else + { + _configPollingInterval = ConfigUtil.DefaultPollingTimeMilliseconds; + _configCacheMemoryLimitMegabytes = 0; + _configPhysicalMemoryLimitPercentage = 0; + } + + if (config != null) + { + _configPollingInterval = ConfigUtil.GetIntValueFromTimeSpan(config, ConfigUtil.PollingInterval, _configPollingInterval); + _configCacheMemoryLimitMegabytes = ConfigUtil.GetIntValue(config, ConfigUtil.CacheMemoryLimitMegabytes, _configCacheMemoryLimitMegabytes, true, Int32.MaxValue); + _configPhysicalMemoryLimitPercentage = ConfigUtil.GetIntValue(config, ConfigUtil.PhysicalMemoryLimitPercentage, _configPhysicalMemoryLimitPercentage, true, 100); + } + } + + private void InitDisposableMembers() + { + bool dispose = true; + try + { + _cacheMemoryMonitor = new CacheMemoryMonitor(_memoryCache, _configCacheMemoryLimitMegabytes); + Timer timer; + // Don't capture the current ExecutionContext and its AsyncLocals onto the timer causing them to live forever + bool restoreFlow = false; + try + { + if (!ExecutionContext.IsFlowSuppressed()) + { + ExecutionContext.SuppressFlow(); + restoreFlow = true; + } + + timer = new Timer(new TimerCallback(CacheManagerTimerCallback), null, _configPollingInterval, _configPollingInterval); + } + finally + { + // Restore the current ExecutionContext + if (restoreFlow) + ExecutionContext.RestoreFlow(); + } + + _timerHandleRef = new GCHandleRef(timer); + dispose = false; + } + finally + { + if (dispose) + { + Dispose(); + } + } + } + + private void SetTrimStats(long trimDurationTicks, long totalCountBeforeTrim, long trimCount) + { + _lastTrimDurationTicks = trimDurationTicks; + + int gen2Count = GC.CollectionCount(2); + // has there been a Gen 2 Collection since the last trim? + if (gen2Count != _lastTrimGen2Count) + { + _lastTrimTime = DateTime.UtcNow; + _totalCountBeforeTrim = totalCountBeforeTrim; + _lastTrimCount = trimCount; + } + else + { + // we've done multiple trims between Gen 2 collections, so only add to the trim count + _lastTrimCount += trimCount; + } + _lastTrimGen2Count = gen2Count; + + _lastTrimPercent = (int)((_lastTrimCount * 100L) / _totalCountBeforeTrim); + } + + private void Update() + { + _physicalMemoryMonitor.Update(); + _cacheMemoryMonitor.Update(); + } + + // public/internal + + internal long CacheMemoryLimit + { + get + { + return _cacheMemoryMonitor.MemoryLimit; + } + } + + internal long PhysicalMemoryLimit + { + get + { + return _physicalMemoryMonitor.MemoryLimit; + } + } + + internal TimeSpan PollingInterval + { + get + { + return TimeSpan.FromMilliseconds(_configPollingInterval); + } + } + + internal MemoryCacheStatistics(MemoryCache memoryCache, NameValueCollection config) + { + _memoryCache = memoryCache; + _lastTrimGen2Count = -1; + _lastTrimTime = DateTime.MinValue; + _timerLock = new Object(); + InitializeConfiguration(config); + _pollingInterval = _configPollingInterval; + _physicalMemoryMonitor = new PhysicalMemoryMonitor(_configPhysicalMemoryLimitPercentage); + InitDisposableMembers(); + } + + internal long CacheManagerThread(int minPercent) + { + if (Interlocked.Exchange(ref _inCacheManagerThread, 1) != 0) + return 0; + try + { + if (_disposed == 1) + { + return 0; + } +#if DEBUG + Dbg.Trace("MemoryCacheStats", "**BEG** CacheManagerThread " + DateTime.Now.ToString("T", CultureInfo.InvariantCulture)); +#endif + // The timer thread must always call Update so that the CacheManager + // knows the size of the cache. + Update(); + AdjustTimer(); + + int percent = Math.Max(minPercent, GetPercentToTrim()); + long beginTotalCount = _memoryCache.GetCount(); + Stopwatch sw = Stopwatch.StartNew(); + long trimmedOrExpired = _memoryCache.Trim(percent); + sw.Stop(); + // 1) don't update stats if the trim happend because MAX_COUNT was exceeded + // 2) don't update stats unless we removed at least one entry + if (percent > 0 && trimmedOrExpired > 0) + { + SetTrimStats(sw.Elapsed.Ticks, beginTotalCount, trimmedOrExpired); + } + +#if DEBUG + Dbg.Trace("MemoryCacheStats", "**END** CacheManagerThread: " + + ", percent=" + percent + + ", beginTotalCount=" + beginTotalCount + + ", trimmed=" + trimmedOrExpired + + ", Milliseconds=" + sw.ElapsedMilliseconds); +#endif + +#if PERF + Debug.WriteLine("CacheCommon.CacheManagerThread:" + + " minPercent= " + minPercent + + ", percent= " + percent + + ", beginTotalCount=" + beginTotalCount + + ", trimmed=" + trimmedOrExpired + + ", Milliseconds=" + sw.ElapsedMilliseconds + "\n"); +#endif + return trimmedOrExpired; + } + finally + { + Interlocked.Exchange(ref _inCacheManagerThread, 0); + } + } + + public void Dispose() + { + if (Interlocked.Exchange(ref _disposed, 1) == 0) + { + lock (_timerLock) + { + GCHandleRef timerHandleRef = _timerHandleRef; + if (timerHandleRef != null && Interlocked.CompareExchange(ref _timerHandleRef, null, timerHandleRef) == timerHandleRef) + { + timerHandleRef.Dispose(); + Dbg.Trace("MemoryCacheStats", "Stopped CacheMemoryTimers"); + } + } + while (_inCacheManagerThread != 0) + { + Thread.Sleep(100); + } + if (_cacheMemoryMonitor != null) + { + _cacheMemoryMonitor.Dispose(); + } + // Don't need to call GC.SuppressFinalize(this) for sealed types without finalizers. + } + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Grandfathered suppression from original caching code checkin")] + internal void UpdateConfig(NameValueCollection config) + { + int pollingInterval = ConfigUtil.GetIntValueFromTimeSpan(config, ConfigUtil.PollingInterval, _configPollingInterval); + int cacheMemoryLimitMegabytes = ConfigUtil.GetIntValue(config, ConfigUtil.CacheMemoryLimitMegabytes, _configCacheMemoryLimitMegabytes, true, Int32.MaxValue); + int physicalMemoryLimitPercentage = ConfigUtil.GetIntValue(config, ConfigUtil.PhysicalMemoryLimitPercentage, _configPhysicalMemoryLimitPercentage, true, 100); + + if (pollingInterval != _configPollingInterval) + { + lock (_timerLock) + { + _configPollingInterval = pollingInterval; + } + } + + if (cacheMemoryLimitMegabytes == _configCacheMemoryLimitMegabytes + && physicalMemoryLimitPercentage == _configPhysicalMemoryLimitPercentage) + { + return; + } + + try + { + try + { + } + finally + { + // prevent ThreadAbortEx from interrupting + while (Interlocked.Exchange(ref _inCacheManagerThread, 1) != 0) + { + Thread.Sleep(100); + } + } + if (_disposed == 0) + { + if (cacheMemoryLimitMegabytes != _configCacheMemoryLimitMegabytes) + { + _cacheMemoryMonitor.SetLimit(cacheMemoryLimitMegabytes); + _configCacheMemoryLimitMegabytes = cacheMemoryLimitMegabytes; + } + if (physicalMemoryLimitPercentage != _configPhysicalMemoryLimitPercentage) + { + _physicalMemoryMonitor.SetLimit(physicalMemoryLimitPercentage); + _configPhysicalMemoryLimitPercentage = physicalMemoryLimitPercentage; + } + } + } + finally + { + Interlocked.Exchange(ref _inCacheManagerThread, 0); + } + } + } +} + diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheStore.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheStore.cs new file mode 100644 index 0000000000..31451d7931 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheStore.cs @@ -0,0 +1,431 @@ +// 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.Collections; +using System.Collections.Specialized; +using System.Threading; +using System.Diagnostics; +using System.Security; +using System.Security.Permissions; +using System.Diagnostics.CodeAnalysis; + +namespace System.Runtime.Caching +{ + internal sealed class MemoryCacheStore : IDisposable + { + private const int INSERT_BLOCK_WAIT = 10000; + private const int MAX_COUNT = Int32.MaxValue / 2; + + private Hashtable _entries; + private Object _entriesLock; + private CacheExpires _expires; + private CacheUsage _usage; + private int _disposed; + private ManualResetEvent _insertBlock; + private volatile bool _useInsertBlock; + private MemoryCache _cache; + private PerfCounters _perfCounters; + + internal MemoryCacheStore(MemoryCache cache, PerfCounters perfCounters) + { + _cache = cache; + _perfCounters = perfCounters; + _entries = new Hashtable(new MemoryCacheEqualityComparer()); + _entriesLock = new Object(); + _expires = new CacheExpires(this); + _usage = new CacheUsage(this); + InitDisposableMembers(); + } + + // private members + + private void AddToCache(MemoryCacheEntry entry) + { + // add outside of lock + if (entry == null) + { + return; + } + + if (entry.HasExpiration()) + { + _expires.Add(entry); + } + + if (entry.HasUsage() + && (!entry.HasExpiration() || entry.UtcAbsExp - DateTime.UtcNow >= CacheUsage.MIN_LIFETIME_FOR_USAGE)) + { + _usage.Add(entry); + } + + // One last sanity check to be sure we didn't fall victim to an Add concurrency + if (!entry.CompareExchangeState(EntryState.AddedToCache, EntryState.AddingToCache)) + { + if (entry.InExpires()) + { + _expires.Remove(entry); + } + + if (entry.InUsage()) + { + _usage.Remove(entry); + } + } + + entry.CallNotifyOnChanged(); + if (_perfCounters != null) + { + _perfCounters.Increment(PerfCounterName.Entries); + _perfCounters.Increment(PerfCounterName.Turnover); + } + } + + private void InitDisposableMembers() + { + _insertBlock = new ManualResetEvent(true); + _expires.EnableExpirationTimer(true); + } + + private void RemoveFromCache(MemoryCacheEntry entry, CacheEntryRemovedReason reason, bool delayRelease = false) + { + // release outside of lock + if (entry != null) + { + if (entry.InExpires()) + { + _expires.Remove(entry); + } + + if (entry.InUsage()) + { + _usage.Remove(entry); + } + + Dbg.Assert(entry.State == EntryState.RemovingFromCache, "entry.State = EntryState.RemovingFromCache"); + + entry.State = EntryState.RemovedFromCache; + if (!delayRelease) + { + entry.Release(_cache, reason); + } + if (_perfCounters != null) + { + _perfCounters.Decrement(PerfCounterName.Entries); + _perfCounters.Increment(PerfCounterName.Turnover); + } + } + } + + // 'updatePerfCounters' defaults to true since this method is called by all Get() operations + // to update both the performance counters and the sliding expiration. Callers that perform + // nested sliding expiration updates (like a MemoryCacheEntry touching its update sentinel) + // can pass false to prevent these from unintentionally showing up in the perf counters. + internal void UpdateExpAndUsage(MemoryCacheEntry entry, bool updatePerfCounters = true) + { + if (entry != null) + { + if (entry.InUsage() || entry.SlidingExp > TimeSpan.Zero) + { + DateTime utcNow = DateTime.UtcNow; + entry.UpdateSlidingExp(utcNow, _expires); + entry.UpdateUsage(utcNow, _usage); + } + + // If this entry has an update sentinel, the sliding expiration is actually associated + // with that sentinel, not with this entry. We need to update the sentinel's sliding expiration to + // keep the sentinel from expiring, which in turn would force a removal of this entry from the cache. + entry.UpdateSlidingExpForUpdateSentinel(); + + if (updatePerfCounters && _perfCounters != null) + { + _perfCounters.Increment(PerfCounterName.Hits); + _perfCounters.Increment(PerfCounterName.HitRatio); + _perfCounters.Increment(PerfCounterName.HitRatioBase); + } + } + else + { + if (updatePerfCounters && _perfCounters != null) + { + _perfCounters.Increment(PerfCounterName.Misses); + _perfCounters.Increment(PerfCounterName.HitRatioBase); + } + } + } + + private void WaitInsertBlock() + { + _insertBlock.WaitOne(INSERT_BLOCK_WAIT, false); + } + + // public/internal members + + internal CacheUsage Usage { get { return _usage; } } + + internal MemoryCacheEntry AddOrGetExisting(MemoryCacheKey key, MemoryCacheEntry entry) + { + if (_useInsertBlock && entry.HasUsage()) + { + WaitInsertBlock(); + } + MemoryCacheEntry existingEntry = null; + MemoryCacheEntry toBeReleasedEntry = null; + bool added = false; + lock (_entriesLock) + { + if (_disposed == 0) + { + existingEntry = _entries[key] as MemoryCacheEntry; + // has it expired? + if (existingEntry != null && existingEntry.UtcAbsExp <= DateTime.UtcNow) + { + toBeReleasedEntry = existingEntry; + toBeReleasedEntry.State = EntryState.RemovingFromCache; + existingEntry = null; + } + // can we add entry to the cache? + if (existingEntry == null) + { + entry.State = EntryState.AddingToCache; + added = true; + _entries[key] = entry; + } + } + } + // release outside of lock + RemoveFromCache(toBeReleasedEntry, CacheEntryRemovedReason.Expired, delayRelease: true); + if (added) + { + // add outside of lock + AddToCache(entry); + } + // update outside of lock + UpdateExpAndUsage(existingEntry); + + // Call Release after the new entry has been completely added so + // that the CacheItemRemovedCallback can take a dependency on the newly inserted item. + if (toBeReleasedEntry != null) + { + toBeReleasedEntry.Release(_cache, CacheEntryRemovedReason.Expired); + } + return existingEntry; + } + + internal void BlockInsert() + { + _insertBlock.Reset(); + _useInsertBlock = true; + } + + internal void CopyTo(IDictionary h) + { + lock (_entriesLock) + { + if (_disposed == 0) + { + foreach (DictionaryEntry e in _entries) + { + MemoryCacheKey key = e.Key as MemoryCacheKey; + MemoryCacheEntry entry = e.Value as MemoryCacheEntry; + if (entry.UtcAbsExp > DateTime.UtcNow) + { + h[key.Key] = entry.Value; + } + } + } + } + } + + internal int Count + { + get + { + return _entries.Count; + } + } + + public void Dispose() + { + if (Interlocked.Exchange(ref _disposed, 1) == 0) + { + // disable CacheExpires timer + _expires.EnableExpirationTimer(false); + // build array list of entries + ArrayList entries = new ArrayList(_entries.Count); + lock (_entriesLock) + { + foreach (DictionaryEntry e in _entries) + { + MemoryCacheEntry entry = e.Value as MemoryCacheEntry; + entries.Add(entry); + } + foreach (MemoryCacheEntry entry in entries) + { + MemoryCacheKey key = entry as MemoryCacheKey; + entry.State = EntryState.RemovingFromCache; + _entries.Remove(key); + } + } + // release entries outside of lock + foreach (MemoryCacheEntry entry in entries) + { + RemoveFromCache(entry, CacheEntryRemovedReason.CacheSpecificEviction); + } + + // MemoryCacheStatistics has been disposed, and therefore nobody should be using + // _insertBlock except for potential threads in WaitInsertBlock (which won't care if we call Close). + Dbg.Assert(_useInsertBlock == false, "_useInsertBlock == false"); + _insertBlock.Close(); + + // Don't need to call GC.SuppressFinalize(this) for sealed types without finalizers. + } + } + + internal MemoryCacheEntry Get(MemoryCacheKey key) + { + MemoryCacheEntry entry = _entries[key] as MemoryCacheEntry; + // has it expired? + if (entry != null && entry.UtcAbsExp <= DateTime.UtcNow) + { + Remove(key, entry, CacheEntryRemovedReason.Expired); + entry = null; + } + // update outside of lock + UpdateExpAndUsage(entry); + return entry; + } + + internal MemoryCacheEntry Remove(MemoryCacheKey key, MemoryCacheEntry entryToRemove, CacheEntryRemovedReason reason) + { + MemoryCacheEntry entry = null; + lock (_entriesLock) + { + if (_disposed == 0) + { + // get current entry + entry = _entries[key] as MemoryCacheEntry; + // remove if it matches the entry to be removed (but always remove if entryToRemove is null) + if (entryToRemove == null || Object.ReferenceEquals(entry, entryToRemove)) + { + if (entry != null) + { + entry.State = EntryState.RemovingFromCache; + _entries.Remove(key); + } + } + else + { + entry = null; + } + } + } + // release outside of lock + RemoveFromCache(entry, reason); + return entry; + } + + internal void Set(MemoryCacheKey key, MemoryCacheEntry entry) + { + if (_useInsertBlock && entry.HasUsage()) + { + WaitInsertBlock(); + } + MemoryCacheEntry existingEntry = null; + bool added = false; + lock (_entriesLock) + { + if (_disposed == 0) + { + existingEntry = _entries[key] as MemoryCacheEntry; + if (existingEntry != null) + { + existingEntry.State = EntryState.RemovingFromCache; + } + entry.State = EntryState.AddingToCache; + added = true; + _entries[key] = entry; + } + } + + CacheEntryRemovedReason reason = CacheEntryRemovedReason.Removed; + if (existingEntry != null) + { + if (existingEntry.UtcAbsExp <= DateTime.UtcNow) + { + reason = CacheEntryRemovedReason.Expired; + } + RemoveFromCache(existingEntry, reason, delayRelease: true); + } + if (added) + { + AddToCache(entry); + } + + // Call Release after the new entry has been completely added so + // that the CacheItemRemovedCallback can take a dependency on the newly inserted item. + if (existingEntry != null) + { + existingEntry.Release(_cache, reason); + } + } + + internal long TrimInternal(int percent) + { + Dbg.Assert(percent <= 100, "percent <= 100"); + + int count = Count; + int toTrim = 0; + // do we need to drop a percentage of entries? + if (percent > 0) + { + toTrim = (int)Math.Ceiling(((long)count * (long)percent) / 100D); + // would this leave us above MAX_COUNT? + int minTrim = count - MAX_COUNT; + if (toTrim < minTrim) + { + toTrim = minTrim; + } + } + // do we need to trim? + if (toTrim <= 0 || _disposed == 1) + { + return 0; + } + int trimmed = 0; // total number of entries trimmed + int trimmedOrExpired = 0; +#if DEBUG + int beginTotalCount = count; +#endif + + trimmedOrExpired = _expires.FlushExpiredItems(true); + if (trimmedOrExpired < toTrim) + { + trimmed = _usage.FlushUnderUsedItems(toTrim - trimmedOrExpired); + trimmedOrExpired += trimmed; + } + + if (trimmed > 0 && _perfCounters != null) + { + // Update values for perfcounters + _perfCounters.IncrementBy(PerfCounterName.Trims, trimmed); + } + +#if DEBUG + Dbg.Trace("MemoryCacheStore", "TrimInternal:" + + " beginTotalCount=" + beginTotalCount + + ", endTotalCount=" + count + + ", percent=" + percent + + ", trimmed=" + trimmed); +#endif + return trimmedOrExpired; + } + + internal void UnblockInsert() + { + _useInsertBlock = false; + _insertBlock.Set(); + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryMonitor.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryMonitor.cs new file mode 100644 index 0000000000..7318621c1f --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/MemoryMonitor.cs @@ -0,0 +1,106 @@ +// 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.Collections.Specialized; +using System.Security; +using System.Runtime.InteropServices; + + +namespace System.Runtime.Caching +{ + // MemoryMonitor is the base class for memory monitors. The MemoryCache has two + // types of monitors: PhysicalMemoryMonitor and CacheMemoryMonitor. The first monitors + // the amount of physical memory used on the machine, and helps determine when we should + // drop cache entries to avoid paging. The second monitors the amount of memory used by + // the cache itself, and helps determine when we should drop cache entries to avoid + // exceeding the cache's memory limit. Both are configurable (see ConfigUtil.cs). + internal abstract class MemoryMonitor + { + protected const int TERABYTE_SHIFT = 40; + protected const long TERABYTE = 1L << TERABYTE_SHIFT; + + protected const int GIGABYTE_SHIFT = 30; + protected const long GIGABYTE = 1L << GIGABYTE_SHIFT; + + protected const int MEGABYTE_SHIFT = 20; + protected const long MEGABYTE = 1L << MEGABYTE_SHIFT; // 1048576 + + protected const int KILOBYTE_SHIFT = 10; + protected const long KILOBYTE = 1L << KILOBYTE_SHIFT; // 1024 + + protected const int HISTORY_COUNT = 6; + + protected int _pressureHigh; // high pressure level + protected int _pressureLow; // low pressure level - slow growth here + + protected int _i0; + protected int[] _pressureHist; + protected int _pressureTotal; + + private static long s_totalPhysical; + private static long s_totalVirtual; + + static MemoryMonitor() + { + Interop.Kernel32.MEMORYSTATUSEX memoryStatusEx; + memoryStatusEx.dwLength = (uint)Marshal.SizeOf(typeof(Interop.Kernel32.MEMORYSTATUSEX)); + if (Interop.Kernel32.GlobalMemoryStatusEx(out memoryStatusEx) != 0) + { + s_totalPhysical = (long)memoryStatusEx.ullTotalPhys; + s_totalVirtual = (long)memoryStatusEx.ullTotalVirtual; + } + } + + internal static long TotalPhysical { get { return s_totalPhysical; } } + internal static long TotalVirtual { get { return s_totalVirtual; } } + + internal int PressureLast { get { return _pressureHist[_i0]; } } + internal int PressureHigh { get { return _pressureHigh; } } + internal int PressureLow { get { return _pressureLow; } } + + internal bool IsAboveHighPressure() + { + return PressureLast >= PressureHigh; + } + + protected abstract int GetCurrentPressure(); + + internal abstract int GetPercentToTrim(DateTime lastTrimTime, int lastTrimPercent); + + protected void InitHistory() + { + Dbg.Assert(_pressureHigh > 0, "_pressureHigh > 0"); + Dbg.Assert(_pressureLow > 0, "_pressureLow > 0"); + Dbg.Assert(_pressureLow <= _pressureHigh, "_pressureLow <= _pressureHigh"); + + int pressure = GetCurrentPressure(); + + _pressureHist = new int[HISTORY_COUNT]; + for (int i = 0; i < HISTORY_COUNT; i++) + { + _pressureHist[i] = pressure; + _pressureTotal += pressure; + } + } + + // Get current pressure and update history + internal void Update() + { + int pressure = GetCurrentPressure(); + + _i0 = (_i0 + 1) % HISTORY_COUNT; + _pressureTotal -= _pressureHist[_i0]; + _pressureTotal += pressure; + _pressureHist[_i0] = pressure; + +#if DEBUG + Dbg.Trace("MemoryCacheStats", this.GetType().Name + ".Update: last=" + pressure + + ",high=" + PressureHigh + + ",low=" + PressureLow + + " " + Dbg.FormatLocalDate(DateTime.Now)); +#endif + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/ObjectCache.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/ObjectCache.cs new file mode 100644 index 0000000000..365b683b57 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/ObjectCache.cs @@ -0,0 +1,116 @@ +// 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.Runtime.Caching.Resources; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; +using System.Security; +using System.Security.Permissions; +using System.Threading; + +namespace System.Runtime.Caching +{ + [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification = "The class represents a type of cache")] + public abstract class ObjectCache : IEnumerable> + { + private static IServiceProvider s_host; + + public static readonly DateTimeOffset InfiniteAbsoluteExpiration = DateTimeOffset.MaxValue; + public static readonly TimeSpan NoSlidingExpiration = TimeSpan.Zero; + + public static IServiceProvider Host + { + get + { + return s_host; + } + + set + { + if (value == null) + { + throw new ArgumentNullException("value"); + } + if (Interlocked.CompareExchange(ref s_host, value, null) != null) + { + throw new InvalidOperationException(SR.Property_already_set); + } + } + } + + public abstract DefaultCacheCapabilities DefaultCacheCapabilities { get; } + + public abstract string Name { get; } + + //Default indexer property + public abstract object this[string key] { get; set; } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable>)this).GetEnumerator(); + } + + public abstract CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(IEnumerable keys, String regionName = null); + + IEnumerator> IEnumerable>.GetEnumerator() + { + return GetEnumerator(); + } + + protected abstract IEnumerator> GetEnumerator(); + + //Existence check for a single item + public abstract bool Contains(string key, string regionName = null); + + //The Add overloads are for adding an item without requiring the existing item to be returned. This was requested for Velocity. + public virtual bool Add(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null) + { + return (AddOrGetExisting(key, value, absoluteExpiration, regionName) == null); + } + + public virtual bool Add(CacheItem item, CacheItemPolicy policy) + { + return (AddOrGetExisting(item, policy) == null); + } + + public virtual bool Add(string key, object value, CacheItemPolicy policy, string regionName = null) + { + return (AddOrGetExisting(key, value, policy, regionName) == null); + } + + public abstract object AddOrGetExisting(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null); + public abstract CacheItem AddOrGetExisting(CacheItem value, CacheItemPolicy policy); + + public abstract object AddOrGetExisting(string key, object value, CacheItemPolicy policy, string regionName = null); + + [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", Justification = "The name best represents an operation on a cache")] + public abstract object Get(string key, string regionName = null); + + public abstract CacheItem GetCacheItem(string key, string regionName = null); + + [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", Justification = "The name best represents an operation on a cache")] + public abstract void Set(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null); + + [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", Justification = "The name best represents an operation on a cache")] + public abstract void Set(CacheItem item, CacheItemPolicy policy); + + [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", Justification = "The name best represents an operation on a cache")] + public abstract void Set(string key, object value, CacheItemPolicy policy, string regionName = null); + + //Get multiple items by keys + public abstract IDictionary GetValues(IEnumerable keys, string regionName = null); + + public virtual IDictionary GetValues(string regionName, params string[] keys) + { + return GetValues(keys, regionName); + } + + public abstract object Remove(string key, string regionName = null); + + public abstract long GetCount(string regionName = null); + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/OnChangedCallback.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/OnChangedCallback.cs new file mode 100644 index 0000000000..46758ebb7b --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/OnChangedCallback.cs @@ -0,0 +1,10 @@ +// 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; + +namespace System.Runtime.Caching +{ + public delegate void OnChangedCallback(object state); +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/PerfCounterName.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/PerfCounterName.cs new file mode 100644 index 0000000000..bdbd6646b4 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/PerfCounterName.cs @@ -0,0 +1,19 @@ +// 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; + +namespace System.Runtime.Caching +{ + internal enum PerfCounterName + { + Entries = 0, + Hits, + HitRatio, + HitRatioBase, + Misses, + Trims, + Turnover + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.cs new file mode 100644 index 0000000000..0029db1f72 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.cs @@ -0,0 +1,133 @@ +// 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.Runtime.Caching.Configuration; +using System.Runtime.InteropServices; +using System.Security; + +namespace System.Runtime.Caching +{ + // PhysicalMemoryMonitor monitors the amound of physical memory used on the machine + // and helps us determine when to drop entries to avoid paging and GC thrashing. + // The limit is configurable (see ConfigUtil.cs). + internal sealed class PhysicalMemoryMonitor : MemoryMonitor + { + private const int MIN_TOTAL_MEMORY_TRIM_PERCENT = 10; + private static readonly long s_TARGET_TOTAL_MEMORY_TRIM_INTERVAL_TICKS = 5 * TimeSpan.TicksPerMinute; + + // Returns the percentage of physical machine memory that can be consumed by an + // application before the cache starts forcibly removing items. + internal long MemoryLimit + { + get { return _pressureHigh; } + } + + private PhysicalMemoryMonitor() + { + // hide default ctor + } + + internal PhysicalMemoryMonitor(int physicalMemoryLimitPercentage) + { + /* + The chart below shows physical memory in megabytes, and the 1, 3, and 10% values. + When we reach "middle" pressure, we begin trimming the cache. + + RAM 1% 3% 10% + ----------------------------- + 128 1.28 3.84 12.8 + 256 2.56 7.68 25.6 + 512 5.12 15.36 51.2 + 1024 10.24 30.72 102.4 + 2048 20.48 61.44 204.8 + 4096 40.96 122.88 409.6 + 8192 81.92 245.76 819.2 + + */ + + long memory = TotalPhysical; + Dbg.Assert(memory != 0, "memory != 0"); + if (memory >= 0x100000000) + { + _pressureHigh = 99; + } + else if (memory >= 0x80000000) + { + _pressureHigh = 98; + } + else if (memory >= 0x40000000) + { + _pressureHigh = 97; + } + else if (memory >= 0x30000000) + { + _pressureHigh = 96; + } + else + { + _pressureHigh = 95; + } + + _pressureLow = _pressureHigh - 9; + + SetLimit(physicalMemoryLimitPercentage); + InitHistory(); + } + + protected override int GetCurrentPressure() + { + Interop.Kernel32.MEMORYSTATUSEX memoryStatusEx = default; + memoryStatusEx.dwLength = (uint)Marshal.SizeOf(typeof(Interop.Kernel32.MEMORYSTATUSEX)); + if (Interop.Kernel32.GlobalMemoryStatusEx(out memoryStatusEx) == 0) + { + return 0; + } + + int memoryLoad = (int)memoryStatusEx.dwMemoryLoad; + return memoryLoad; + } + + internal override int GetPercentToTrim(DateTime lastTrimTime, int lastTrimPercent) + { + int percent = 0; + if (IsAboveHighPressure()) + { + // choose percent such that we don't repeat this for ~5 (TARGET_TOTAL_MEMORY_TRIM_INTERVAL) minutes, + // but keep the percentage between 10 and 50. + DateTime utcNow = DateTime.UtcNow; + long ticksSinceTrim = utcNow.Subtract(lastTrimTime).Ticks; + if (ticksSinceTrim > 0) + { + percent = Math.Min(50, (int)((lastTrimPercent * s_TARGET_TOTAL_MEMORY_TRIM_INTERVAL_TICKS) / ticksSinceTrim)); + percent = Math.Max(MIN_TOTAL_MEMORY_TRIM_PERCENT, percent); + } + +#if PERF + Debug.WriteLine(String.Format("PhysicalMemoryMonitor.GetPercentToTrim: percent={0:N}, lastTrimPercent={1:N}, secondsSinceTrim={2:N}\n", + percent, + lastTrimPercent, + ticksSinceTrim/TimeSpan.TicksPerSecond)); +#endif + } + + return percent; + } + + internal void SetLimit(int physicalMemoryLimitPercentage) + { + if (physicalMemoryLimitPercentage == 0) + { + // use defaults + return; + } + _pressureHigh = Math.Max(3, physicalMemoryLimitPercentage); + _pressureLow = Math.Max(1, _pressureHigh - 9); +#if DEBUG + Dbg.Trace("MemoryCacheStats", "PhysicalMemoryMonitor.SetLimit: _pressureHigh=" + _pressureHigh + + ", _pressureLow=" + _pressureLow); +#endif + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Resources/RH.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Resources/RH.cs new file mode 100644 index 0000000000..35da4525a5 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/Resources/RH.cs @@ -0,0 +1,17 @@ +// 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.Globalization; + +namespace System.Runtime.Caching.Resources +{ + internal static class RH + { + public static string Format(string resource, params object[] args) + { + return String.Format(CultureInfo.CurrentCulture, resource, args); + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/SRef.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/SRef.cs new file mode 100644 index 0000000000..4b8dd7f23c --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/SRef.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.Globalization; +using System.Reflection; +using System.Security; +using System.Security.Permissions; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace System.Runtime.Caching +{ + // CoreClr 2.0 supports GC sized refs but does not expose System.SizedReference +#if NETCOREAPP21 + internal class SRef { + private static Type s_type = Type.GetType("System.SizedReference", true, false); + private Object _sizedRef; + + internal SRef(Object target) { + _sizedRef = Activator.CreateInstance(s_type, + BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.CreateInstance, + null, + new object[] { target }, + null); + } + + internal long ApproximateSize { + get { + object o = s_type.InvokeMember("ApproximateSize", + BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty, + null, // binder + _sizedRef, // target + null, // args + CultureInfo.InvariantCulture); + return (long)o; + } + } + + internal void Dispose() { + s_type.InvokeMember("Dispose", + BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, + null, // binder + _sizedRef, // target + null, // args + CultureInfo.InvariantCulture); + } + } + + internal class SRefMultiple { + private SRef[] _srefs; + private long[] _sizes; // Getting SRef size in the debugger is extremely tedious so we keep the last read value here + + internal SRefMultiple(object[] targets) { + _srefs = new SRef[targets.Length]; + _sizes = new long[targets.Length]; + for (int i = 0; i < targets.Length; i++) { + _srefs[i] = new SRef(targets[i]); + } + } + + internal long ApproximateSize { + get { + long size = 0; + for (int i = 0; i < _srefs.Length; i++) { + size += (_sizes[i] = _srefs[i].ApproximateSize); + } + return size; + } + } + + internal void Dispose() { + foreach (SRef s in _srefs) { + s.Dispose(); + } + } + } + +#else + // until then we provide a stub + internal class SRefMultiple + { + internal SRefMultiple(object[] targets) + { + } + internal long ApproximateSize => 0; + internal void Dispose() + { + } + } +#endif + + internal class GCHandleRef : IDisposable + where T : class, IDisposable + { + private GCHandle _handle; + private T _t; + + public GCHandleRef(T t) + { + _handle = GCHandle.Alloc(t); + } + + public T Target + { + get + { + try + { + T t = (T)_handle.Target; + if (t != null) + { + return t; + } + } + catch (InvalidOperationException) + { + // use the normal reference instead of throwing an exception when _handle is already freed + } + return _t; + } + } + + public void Dispose() + { + Target.Dispose(); + // Safe to call Dispose more than once but not thread-safe + if (_handle.IsAllocated) + { + // We must free the GC handle to avoid leaks. + // However after _handle is freed we no longer have access to its Target + // which will cause AVs and various concurrency issues under stress. + // We revert to using normal references after disposing the GC handle + _t = (T)_handle.Target; + _handle.Free(); + } + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/SafeBitVector32.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/SafeBitVector32.cs new file mode 100644 index 0000000000..6c876c4ae2 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/SafeBitVector32.cs @@ -0,0 +1,84 @@ +// 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.Threading; + +namespace System.Runtime.Caching +{ + // + // This is a multithreadsafe version of System.Collections.Specialized.BitVector32. + // + [Serializable] + internal struct SafeBitVector32 + { + private volatile int _data; + + internal bool this[int bit] + { + get + { + int data = _data; + return (data & bit) == bit; + } + set + { + for (; ;) + { + int oldData = _data; + int newData; + if (value) + { + newData = oldData | bit; + } + else + { + newData = oldData & ~bit; + } + +#pragma warning disable 0420 + int result = Interlocked.CompareExchange(ref _data, newData, oldData); +#pragma warning restore 0420 + + if (result == oldData) + { + break; + } + } + } + } + + internal bool ChangeValue(int bit, bool value) + { + for (; ;) + { + int oldData = _data; + int newData; + if (value) + { + newData = oldData | bit; + } + else + { + newData = oldData & ~bit; + } + + if (oldData == newData) + { + return false; + } + +#pragma warning disable 0420 + int result = Interlocked.CompareExchange(ref _data, newData, oldData); +#pragma warning restore 0420 + + if (result == oldData) + { + return true; + } + } + } + } +} + diff --git a/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/_shims.cs b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/_shims.cs new file mode 100644 index 0000000000..faba955999 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/src/System/Runtime/Caching/_shims.cs @@ -0,0 +1,28 @@ +// 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.Collections.Generic; + +namespace System.Runtime.Caching +{ + internal sealed class PerfCounters : IDisposable + { + internal PerfCounters(string cacheName) + { + } + public void Dispose() + { + } + internal void Increment(PerfCounterName name) + { + } + internal void IncrementBy(PerfCounterName name, long value) + { + } + internal void Decrement(PerfCounterName name) + { + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/tests/AdditionalCacheTests/AdditionalCacheTests.cs b/external/corefx/src/System.Runtime.Caching/tests/AdditionalCacheTests/AdditionalCacheTests.cs new file mode 100644 index 0000000000..b8c5593e72 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/tests/AdditionalCacheTests/AdditionalCacheTests.cs @@ -0,0 +1,36 @@ +// 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 Xunit; +using System; +using System.Runtime.Caching; + +namespace System.Runtime.Caching.Tests +{ + // These are the tests to fill in some of the coverage in ported Mono caching tests + public class AdditionalCacheTests + { + [Fact] + public void DisposedCacheTest() + { + var mc = new MemoryCache("my disposed cache 1"); + mc.Add("aa", "bb", new CacheItemPolicy()); + mc.Dispose(); + + Assert.Null(mc["aa"]); + + mc = new MemoryCache("my disposed cache 2"); + CacheEntryRemovedReason reason = (CacheEntryRemovedReason)1111; + var cip = new CacheItemPolicy(); + cip.RemovedCallback = (CacheEntryRemovedArguments args) => + { + reason = args.RemovedReason; + }; + + mc.Set("key", "value", cip); + mc.Dispose(); + Assert.Equal(reason, CacheEntryRemovedReason.CacheSpecificEviction); + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/tests/Common/PokerChangeMonitor.cs b/external/corefx/src/System.Runtime.Caching/tests/Common/PokerChangeMonitor.cs new file mode 100644 index 0000000000..7c34c1d136 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/tests/Common/PokerChangeMonitor.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Runtime.Caching; +using System.Text; + +namespace MonoTests.Common +{ + internal class PokerChangeMonitor : ChangeMonitor + { + private List _calls; + private string _uniqueId; + + public List Calls + { + get + { + if (_calls == null) + _calls = new List(); + + return _calls; + } + } + + public override string UniqueId + { + get { return _uniqueId; } + } + + public PokerChangeMonitor() + { + _uniqueId = "UniqueID"; + InitializationComplete(); + } + + public void SignalChange() + { + OnChanged(null); + } + + protected override void Dispose(bool disposing) + { + Calls.Add("Dispose (bool disposing)"); + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/tests/Common/PokerMemoryCache.cs b/external/corefx/src/System.Runtime.Caching/tests/Common/PokerMemoryCache.cs new file mode 100644 index 0000000000..3a83be7490 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/tests/Common/PokerMemoryCache.cs @@ -0,0 +1,160 @@ +// Licensed to the .NET Foundation under one or more agreements. +// See the LICENSE file in the project root for more information. + +// +// +// Authors: +// Marek Habersack +// +// Copyright (C) 2010 Novell, Inc. (http://novell.com/) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Runtime.Caching; +using System.Text; + +namespace MonoTests.Common +{ + internal class PokerMemoryCache : MemoryCache + { + private List _calls; + + public List Calls + { + get + { + if (_calls == null) + _calls = new List(); + return _calls; + } + } + + public override object this[string key] + { + get + { + Calls.Add("get_this [string key]"); + return base[key]; + } + set + { + Calls.Add("set_this [string key]"); + base[key] = value; + } + } + + public PokerMemoryCache(string name, NameValueCollection config = null) + : base(name, config) + { } + + public override CacheItem AddOrGetExisting(CacheItem item, CacheItemPolicy policy) + { + Calls.Add("AddOrGetExisting (CacheItem item, CacheItemPolicy policy)"); + return base.AddOrGetExisting(item, policy); + } + + public override object AddOrGetExisting(string key, object value, CacheItemPolicy policy, string regionName = null) + { + Calls.Add("AddOrGetExisting (string key, object value, CacheItemPolicy policy, string regionName = null)"); + return base.AddOrGetExisting(key, value, policy, regionName); + } + + public override object AddOrGetExisting(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null) + { + Calls.Add("AddOrGetExisting (string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)"); + return base.AddOrGetExisting(key, value, absoluteExpiration, regionName); + } + + public override object Get(string key, string regionName = null) + { + Calls.Add("Get (string key, string regionName = null)"); + return base.Get(key, regionName); + } + + public override CacheItem GetCacheItem(string key, string regionName = null) + { + Calls.Add("GetCacheItem (string key, string regionName = null)"); + return base.GetCacheItem(key, regionName); + } + + public override long GetCount(string regionName = null) + { + Calls.Add("GetCount (string regionName = null)"); + return base.GetCount(regionName); + } + + public override bool Contains(string key, string regionName = null) + { + Calls.Add("Contains (string key, string regionName = null)"); + return base.Contains(key, regionName); + } + + public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(IEnumerable keys, string regionName = null) + { + Calls.Add("CreateCacheEntryChangeMonitor (IEnumerable keys, string regionName = null)"); + return base.CreateCacheEntryChangeMonitor(keys, regionName); + } + + protected override IEnumerator> GetEnumerator() + { + Calls.Add("IEnumerator> GetEnumerator ()"); + return base.GetEnumerator(); + } + + public IEnumerator> DoGetEnumerator() + { + return GetEnumerator(); + } + + public override IDictionary GetValues(IEnumerable keys, string regionName = null) + { + Calls.Add("IDictionary GetValues (IEnumerable keys, string regionName = null)"); + return base.GetValues(keys, regionName); + } + + public override IDictionary GetValues(string regionName, params string[] keys) + { + Calls.Add("IDictionary GetValues (string regionName, params string [] keys)"); + return base.GetValues(regionName, keys); + } + + public override void Set(CacheItem item, CacheItemPolicy policy) + { + Calls.Add("Set (CacheItem item, CacheItemPolicy policy)"); + base.Set(item, policy); + } + + public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null) + { + Calls.Add("Set (string key, object value, CacheItemPolicy policy, string regionName = null)"); + base.Set(key, value, policy, regionName); + } + + public override void Set(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null) + { + Calls.Add("Set (string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)"); + base.Set(key, value, absoluteExpiration, regionName); + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/tests/Common/PokerObjectCache.cs b/external/corefx/src/System.Runtime.Caching/tests/Common/PokerObjectCache.cs new file mode 100644 index 0000000000..aaf08dcd6d --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/tests/Common/PokerObjectCache.cs @@ -0,0 +1,192 @@ +// Licensed to the .NET Foundation under one or more agreements. +// See the LICENSE file in the project root for more information. + + +// +// +// Authors: +// Marek Habersack +// +// Copyright (C) 2010 Novell, Inc. (http://novell.com/) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.Caching; + +namespace MonoTests.Common +{ + internal class PokerObjectCache : ObjectCache + { + private Dictionary _cache; + + private Dictionary Cache + { + get + { + if (_cache == null) + _cache = new Dictionary(); + return _cache; + } + } + + public string MethodCalled { get; private set; } + + public override object AddOrGetExisting(string key, object value, CacheItemPolicy policy, string regionName = null) + { + MethodCalled = "AddOrGetExisting (string key, object value, CacheItemPolicy policy, string regionName = null)"; + if (String.IsNullOrEmpty(key) || value == null) + return null; + + object item; + if (Cache.TryGetValue(key, out item)) + return item; + + Cache.Add(key, value); + return null; + } + + public override CacheItem AddOrGetExisting(CacheItem value, CacheItemPolicy policy) + { + MethodCalled = "AddOrGetExisting (CacheItem value, CacheItemPolicy policy)"; + if (value == null) + return null; + + object item; + if (Cache.TryGetValue(value.Key, out item)) + return item as CacheItem; + + Cache.Add(value.Key, value); + return null; + } + + public override object AddOrGetExisting(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null) + { + MethodCalled = "AddOrGetExisting (string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)"; + if (String.IsNullOrEmpty(key) || value == null) + return null; + + object item; + if (Cache.TryGetValue(key, out item)) + return item; + + Cache.Add(key, value); + return null; + } + + public override bool Contains(string key, string regionName = null) + { + throw new NotImplementedException(); + } + + public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(IEnumerable keys, string regionName = null) + { + throw new NotImplementedException(); + } + + public override DefaultCacheCapabilities DefaultCacheCapabilities + { + get { throw new NotImplementedException(); } + } + + public override object Get(string key, string regionName = null) + { + throw new NotImplementedException(); + } + + public override CacheItem GetCacheItem(string key, string regionName = null) + { + throw new NotImplementedException(); + } + + public override long GetCount(string regionName = null) + { + throw new NotImplementedException(); + } + + protected override IEnumerator> GetEnumerator() + { + throw new NotImplementedException(); + } + + public override IDictionary GetValues(IEnumerable keys, string regionName = null) + { + MethodCalled = "IDictionary GetValues (IEnumerable keys, string regionName = null)"; + var ret = new Dictionary(); + if (keys == null) + return ret; + + Dictionary cache = Cache; + if (cache.Count == 0) + return ret; + + object value; + foreach (string key in keys) + { + if (!cache.TryGetValue(key, out value)) + continue; + + ret.Add(key, value); + } + + return ret; + } + + public override string Name + { + get { throw new NotImplementedException(); } + } + + public override object Remove(string key, string regionName = null) + { + throw new NotImplementedException(); + } + + public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null) + { + throw new NotImplementedException(); + } + + public override void Set(CacheItem item, CacheItemPolicy policy) + { + throw new NotImplementedException(); + } + + public override void Set(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null) + { + throw new NotImplementedException(); + } + + public override object this[string key] + { + get + { + throw new NotImplementedException(); + } + set + { + throw new NotImplementedException(); + } + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/tests/Common/TestNotificationSystem.cs b/external/corefx/src/System.Runtime.Caching/tests/Common/TestNotificationSystem.cs new file mode 100644 index 0000000000..9396217151 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/tests/Common/TestNotificationSystem.cs @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// See the LICENSE file in the project root for more information. + +// +// +// Authors: +// Marek Habersack +// +// Copyright (C) 2010 Novell, Inc. (http://novell.com/) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Runtime.Caching; +using System.Runtime.Caching.Hosting; + +namespace MonoTests.Common +{ + internal class TestNotificationSystem : IServiceProvider, IFileChangeNotificationSystem + { + private OnChangedCallback _callback; + + public bool StartMonitoringCalled { get; private set; } + public uint StartMonitoringCallCount { get; private set; } + public bool StopMonitoringCalled { get; private set; } + public uint StopMonitoringCallCount { get; private set; } + public bool UseNullState { get; set; } + + public object GetService(Type serviceType) + { + return this; + } + + object IServiceProvider.GetService(Type serviceType) + { + return GetService(serviceType); + } + + public void FakeChanged(string filePath) + { + if (_callback == null) + return; + + _callback(null); + } + + public void StartMonitoring(string filePath, OnChangedCallback onChangedCallback, out object state, out DateTimeOffset lastWriteTime, out long fileSize) + { + if (UseNullState) + state = null; + else + state = filePath; + lastWriteTime = DateTimeOffset.FromFileTime(DateTime.Now.Ticks); + _callback = onChangedCallback; + fileSize = 10; + StartMonitoringCalled = true; + StartMonitoringCallCount++; + } + + public void StopMonitoring(string filePath, object state) + { + StopMonitoringCalled = true; + StopMonitoringCallCount++; + } + + void IFileChangeNotificationSystem.StartMonitoring(string filePath, OnChangedCallback onChangedCallback, out object state, out DateTimeOffset lastWriteTime, out long fileSize) + { + StartMonitoring(filePath, onChangedCallback, out state, out lastWriteTime, out fileSize); + } + + void IFileChangeNotificationSystem.StopMonitoring(string filePath, object state) + { + StopMonitoring(filePath, state); + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/tests/Configurations.props b/external/corefx/src/System.Runtime.Caching/tests/Configurations.props new file mode 100644 index 0000000000..237253623a --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/tests/Configurations.props @@ -0,0 +1,9 @@ + + + + + netcoreapp-Windows_NT; + netfx; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Caching/tests/System.Runtime.Caching.Tests.csproj b/external/corefx/src/System.Runtime.Caching/tests/System.Runtime.Caching.Tests.csproj new file mode 100644 index 0000000000..30c9b7f0ae --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/tests/System.Runtime.Caching.Tests.csproj @@ -0,0 +1,22 @@ + + + + + + + + + {397E49A7-EB26-4368-8F46-D78B98F4A971} + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Caching/tests/System.Runtime.Caching/HostFileChangeMonitorTest.cs b/external/corefx/src/System.Runtime.Caching/tests/System.Runtime.Caching/HostFileChangeMonitorTest.cs new file mode 100644 index 0000000000..4ea89df6e0 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/tests/System.Runtime.Caching/HostFileChangeMonitorTest.cs @@ -0,0 +1,314 @@ +// Licensed to the .NET Foundation under one or more agreements. +// See the LICENSE file in the project root for more information. + +// +// +// Authors: +// Marek Habersack +// +// Copyright (C) 2010 Novell, Inc. (http://novell.com/) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Runtime.Caching; +using System.Runtime.Caching.Hosting; +using System.Text; + +using Xunit; +using MonoTests.Common; + +namespace MonoTests.System.Runtime.Caching +{ + public class HostFileChangeMonitorTest + { + [Fact] + public void Constructor_Exceptions() + { + string relPath = Path.Combine("relative", "file", "path"); + var paths = new List { + relPath + }; + + Assert.Throws(() => + { + new HostFileChangeMonitor(paths); + }); + + paths.Clear(); + paths.Add(null); + Assert.Throws(() => + { + new HostFileChangeMonitor(paths); + }); + + paths.Clear(); + paths.Add(String.Empty); + Assert.Throws(() => + { + new HostFileChangeMonitor(paths); + }); + + Assert.Throws(() => + { + new HostFileChangeMonitor(null); + }); + + paths.Clear(); + Assert.Throws(() => + { + new HostFileChangeMonitor(paths); + }); + } + + [Fact] + private static void Constructor_MissingFiles_Handler() + { + HostFileChangeMonitor monitor; + string missingFile = Path.GetFullPath(Path.Combine(Guid.NewGuid().ToString("N"), "file", "path")); + + var paths = new List { + missingFile + }; + + // Actually thrown by FileSystemWatcher constructor - note that the exception message suggests the file's + // parent directory is being watched, not the file itself: + // + // MonoTests.System.Runtime.Caching.HostFileChangeMonitorTest.Constructor_MissingFiles: + // System.ArgumentException : The directory name c:\missing\file is invalid. + // at System.IO.FileSystemWatcher..ctor(String path, String filter) + // at System.IO.FileSystemWatcher..ctor(String path) + // at System.Runtime.Caching.FileChangeNotificationSystem.System.Runtime.Caching.Hosting.IFileChangeNotificationSystem.StartMonitoring(String filePath, OnChangedCallback onChangedCallback, Object& state, DateTimeOffset& lastWriteTime, Int64& fileSize) + // at System.Runtime.Caching.HostFileChangeMonitor.InitDisposableMembers() + // at System.Runtime.Caching.HostFileChangeMonitor..ctor(IList`1 filePaths) + // at MonoTests.System.Runtime.Caching.HostFileChangeMonitorTest.Constructor_MissingFiles() in c:\users\grendel\documents\visual studio 2010\Projects\System.Runtime.Caching.Test\System.Runtime.Caching.Test\System.Runtime.Caching\HostFileChangeMonitorTest.cs:line 68 + Assert.Throws(() => + { + new HostFileChangeMonitor(paths); + }); + + missingFile = Path.GetFullPath(Guid.NewGuid().ToString("N")); + + paths.Clear(); + paths.Add(missingFile); + monitor = new HostFileChangeMonitor(paths); + Assert.Equal(1, monitor.FilePaths.Count); + Assert.Equal(missingFile, monitor.FilePaths[0]); + //?? + Assert.Equal(missingFile + "701CE1722770000FFFFFFFFFFFFFFFF", monitor.UniqueId); + monitor.Dispose(); + + paths.Add(missingFile); + monitor = new HostFileChangeMonitor(paths); + Assert.Equal(2, monitor.FilePaths.Count); + Assert.Equal(missingFile, monitor.FilePaths[0]); + Assert.Equal(missingFile, monitor.FilePaths[1]); + //?? + Assert.Equal(missingFile + "701CE1722770000FFFFFFFFFFFFFFFF", monitor.UniqueId); + monitor.Dispose(); + } + + [Fact] + public void Constructor_Duplicates() + { + HostFileChangeMonitor monitor; + string missingFile = Path.GetFullPath(Guid.NewGuid().ToString("N")); + + var paths = new List { + missingFile, + missingFile + }; + + // Just checks if it doesn't throw any exception for dupes + monitor = new HostFileChangeMonitor(paths); + monitor.Dispose(); + } + + private static Tuple> SetupMonitoring() + { + string testPath = Path.Combine(Path.GetTempPath(), "HostFileChangeMonitorTest", "Dispose_Calls_StopMonitoring"); + if (!Directory.Exists(testPath)) + Directory.CreateDirectory(testPath); + + string firstFile = Path.Combine(testPath, "FirstFile.txt"); + string secondFile = Path.Combine(testPath, "SecondFile.txt"); + + File.WriteAllText(firstFile, "I am the first file."); + File.WriteAllText(secondFile, "I am the second file."); + + var paths = new List { + firstFile, + secondFile + }; + + return new Tuple>(testPath, firstFile, secondFile, paths); + } + + private static void CleanupMonitoring(Tuple> setup) + { + string testPath = setup != null ? setup.Item1 : null; + if (String.IsNullOrEmpty(testPath) || !Directory.Exists(testPath)) + return; + + foreach (string f in Directory.EnumerateFiles(testPath)) + { + try + { + File.Delete(f); + } + catch + { + // ignore + } + } + + try + { + // 2 nested folders were created by SetupMonitoring, so we'll delete both + var dirInfo = new DirectoryInfo(testPath); + var parentDirInfo = dirInfo.Parent; + dirInfo.Delete(recursive: true); + parentDirInfo.Delete(recursive: true); + } + catch + { + // ignore + } + } + + + [Fact] + [ActiveIssue(25168)] + private static void Constructor_Calls_StartMonitoring_Handler() + { + Tuple> setup = null; + try + { + var tns = new TestNotificationSystem(); + ObjectCache.Host = tns; + setup = SetupMonitoring(); + var monitor = new HostFileChangeMonitor(setup.Item4); + + Assert.True(tns.StartMonitoringCalled); + Assert.Equal(2U, tns.StartMonitoringCallCount); + } + finally + { + CleanupMonitoring(setup); + } + } + + [Fact] + [ActiveIssue(25168)] + private static void Dispose_Calls_StopMonitoring_Handler() + { + Tuple> setup = null; + try + { + var tns = new TestNotificationSystem(); + ObjectCache.Host = tns; + setup = SetupMonitoring(); + var monitor = new HostFileChangeMonitor(setup.Item4); + tns.FakeChanged(setup.Item2); + + Assert.True(tns.StopMonitoringCalled); + Assert.Equal(2U, tns.StopMonitoringCallCount); + } + finally + { + CleanupMonitoring(setup); + } + } + + [Fact] + [ActiveIssue(25168)] + private static void Dispose_NullState_NoStopMonitoring_Handler() + { + Tuple> setup = null; + try + { + var tns = new TestNotificationSystem(); + tns.UseNullState = true; + ObjectCache.Host = tns; + setup = SetupMonitoring(); + var monitor = new HostFileChangeMonitor(setup.Item4); + tns.FakeChanged(setup.Item2); + + Assert.False(tns.StopMonitoringCalled); + Assert.Equal(0U, tns.StopMonitoringCallCount); + } + finally + { + CleanupMonitoring(setup); + } + } + + [Fact] + public void UniqueId() + { + Tuple> setup = null; + try + { + setup = SetupMonitoring(); + FileInfo fi; + var monitor = new HostFileChangeMonitor(setup.Item4); + var sb = new StringBuilder(); + + fi = new FileInfo(setup.Item2); + sb.AppendFormat("{0}{1:X}{2:X}", + setup.Item2, + fi.LastWriteTimeUtc.Ticks, + fi.Length); + + fi = new FileInfo(setup.Item3); + sb.AppendFormat("{0}{1:X}{2:X}", + setup.Item3, + fi.LastWriteTimeUtc.Ticks, + fi.Length); + + Assert.Equal(sb.ToString(), monitor.UniqueId); + + var list = new List(setup.Item4); + list.Add(setup.Item1); + + monitor = new HostFileChangeMonitor(list); + var di = new DirectoryInfo(setup.Item1); + sb.AppendFormat("{0}{1:X}{2:X}", + setup.Item1, + di.LastWriteTimeUtc.Ticks, + -1L); + Assert.Equal(sb.ToString(), monitor.UniqueId); + + list.Add(setup.Item1); + monitor = new HostFileChangeMonitor(list); + Assert.Equal(sb.ToString(), monitor.UniqueId); + monitor.Dispose(); + } + finally + { + CleanupMonitoring(setup); + } + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/tests/System.Runtime.Caching/MemoryCacheTest.cs b/external/corefx/src/System.Runtime.Caching/tests/System.Runtime.Caching/MemoryCacheTest.cs new file mode 100644 index 0000000000..1e6d59bc18 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/tests/System.Runtime.Caching/MemoryCacheTest.cs @@ -0,0 +1,1415 @@ +// Licensed to the .NET Foundation under one or more agreements. +// See the LICENSE file in the project root for more information. + +// +// +// Authors: +// Marek Habersack +// +// Copyright (C) 2010 Novell, Inc. (http://novell.com/) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Runtime.Caching; +using System.Threading; +using System.Threading.Tasks; +using Xunit; +using MonoTests.Common; + +namespace MonoTests.System.Runtime.Caching +{ + public class MemoryCacheTest + { + [Fact] + public void ConstructorParameters() + { + MemoryCache mc; + Assert.Throws(() => + { + mc = new MemoryCache(null); + }); + + Assert.Throws(() => + { + mc = new MemoryCache(String.Empty); + }); + + Assert.Throws(() => + { + mc = new MemoryCache("default"); + }); + + var config = new NameValueCollection(); + config.Add("CacheMemoryLimitMegabytes", "invalid"); + Assert.Throws(() => + { + mc = new MemoryCache("MyCache", config); + }); + + config.Clear(); + config.Add("PhysicalMemoryLimitPercentage", "invalid"); + Assert.Throws(() => + { + mc = new MemoryCache("MyCache", config); + }); + + config.Clear(); + config.Add("PollingInterval", "invalid"); + Assert.Throws(() => + { + mc = new MemoryCache("MyCache", config); + }); + + config.Clear(); + config.Add("CacheMemoryLimitMegabytes", "-1"); + Assert.Throws(() => + { + mc = new MemoryCache("MyCache", config); + }); + + config.Clear(); + config.Add("CacheMemoryLimitMegabytes", UInt64.MaxValue.ToString()); + Assert.Throws(() => + { + mc = new MemoryCache("MyCache", config); + }); + + config.Clear(); + config.Add("PhysicalMemoryLimitPercentage", "-1"); + Assert.Throws(() => + { + mc = new MemoryCache("MyCache", config); + }); + + config.Clear(); + config.Add("PhysicalMemoryLimitPercentage", UInt64.MaxValue.ToString()); + Assert.Throws(() => + { + mc = new MemoryCache("MyCache", config); + }); + + config.Clear(); + config.Add("PhysicalMemoryLimitPercentage", UInt32.MaxValue.ToString()); + Assert.Throws(() => + { + mc = new MemoryCache("MyCache", config); + }); + + config.Clear(); + config.Add("PhysicalMemoryLimitPercentage", "-10"); + Assert.Throws(() => + { + mc = new MemoryCache("MyCache", config); + }); + + config.Clear(); + config.Add("PhysicalMemoryLimitPercentage", "0"); + // Just make sure it doesn't throw any exception + mc = new MemoryCache("MyCache", config); + + config.Clear(); + config.Add("PhysicalMemoryLimitPercentage", "101"); + Assert.Throws(() => + { + mc = new MemoryCache("MyCache", config); + }); + + // Just make sure it doesn't throw any exception + config.Clear(); + config.Add("UnsupportedSetting", "123"); + mc = new MemoryCache("MyCache", config); + } + + [Fact] + public void Defaults() + { + var mc = new MemoryCache("MyCache"); + Assert.Equal("MyCache", mc.Name); + Assert.Equal(TimeSpan.FromMinutes(2), mc.PollingInterval); + Assert.Equal( + DefaultCacheCapabilities.InMemoryProvider | + DefaultCacheCapabilities.CacheEntryChangeMonitors | + DefaultCacheCapabilities.AbsoluteExpirations | + DefaultCacheCapabilities.SlidingExpirations | + DefaultCacheCapabilities.CacheEntryRemovedCallback | + DefaultCacheCapabilities.CacheEntryUpdateCallback, + mc.DefaultCacheCapabilities); + } + + [Fact] + public void DefaultInstanceDefaults() + { + var mc = MemoryCache.Default; + Assert.Equal("Default", mc.Name); + Assert.Equal(TimeSpan.FromMinutes(2), mc.PollingInterval); + Assert.Equal( + DefaultCacheCapabilities.InMemoryProvider | + DefaultCacheCapabilities.CacheEntryChangeMonitors | + DefaultCacheCapabilities.AbsoluteExpirations | + DefaultCacheCapabilities.SlidingExpirations | + DefaultCacheCapabilities.CacheEntryRemovedCallback | + DefaultCacheCapabilities.CacheEntryUpdateCallback, + mc.DefaultCacheCapabilities); + } + + [Fact] + public void ConstructorValues() + { + var config = new NameValueCollection(); + config.Add("CacheMemoryLimitMegabytes", "1"); + config.Add("pollingInterval", "00:10:00"); + + var mc = new MemoryCache("MyCache", config); + Assert.Equal(1048576, mc.CacheMemoryLimit); + Assert.Equal(TimeSpan.FromMinutes(10), mc.PollingInterval); + + config.Clear(); + config.Add("PhysicalMemoryLimitPercentage", "10"); + config.Add("CacheMemoryLimitMegabytes", "5"); + config.Add("PollingInterval", "01:10:00"); + + mc = new MemoryCache("MyCache", config); + Assert.Equal(10, mc.PhysicalMemoryLimit); + Assert.Equal(5242880, mc.CacheMemoryLimit); + Assert.Equal(TimeSpan.FromMinutes(70), mc.PollingInterval); + } + + [Fact] + public void Indexer() + { + var mc = new PokerMemoryCache("MyCache"); + + Assert.Throws(() => + { + mc[null] = "value"; + }); + + Assert.Throws(() => + { + object v = mc[null]; + }); + + Assert.Throws(() => + { + mc["key"] = null; + }); + + mc.Calls.Clear(); + mc["key"] = "value"; + Assert.Equal(3, mc.Calls.Count); + Assert.Equal("set_this [string key]", mc.Calls[0]); + Assert.Equal("Set (string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)", mc.Calls[1]); + Assert.Equal("Set (string key, object value, CacheItemPolicy policy, string regionName = null)", mc.Calls[2]); + Assert.True(mc.Contains("key")); + + mc.Calls.Clear(); + object value = mc["key"]; + Assert.Equal(1, mc.Calls.Count); + Assert.Equal("get_this [string key]", mc.Calls[0]); + Assert.Equal("value", value); + } + + [Fact] + public void Contains() + { + var mc = new PokerMemoryCache("MyCache"); + + Assert.Throws(() => + { + mc.Contains(null); + }); + + Assert.Throws(() => + { + mc.Contains("key", "region"); + }); + + mc.Set("key", "value", ObjectCache.InfiniteAbsoluteExpiration); + Assert.True(mc.Contains("key")); + + var cip = new CacheItemPolicy(); + cip.Priority = CacheItemPriority.NotRemovable; + cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds(50); + mc.Set("key", "value", cip); + Assert.True(mc.Contains("key")); + + // wait past cip.AbsoluteExpiration + Thread.Sleep(500); + + // Attempt to retrieve an expired entry + Assert.False(mc.Contains("key")); + } + + [Fact] + public void CreateCacheEntryChangeMonitor() + { + var mc = new PokerMemoryCache("MyCache"); + + Assert.Throws(() => + { + mc.CreateCacheEntryChangeMonitor(new string[] { "key" }, "region"); + }); + + Assert.Throws(() => + { + mc.CreateCacheEntryChangeMonitor(null); + }); + + Assert.Throws(() => + { + mc.CreateCacheEntryChangeMonitor(new string[] { }); + }); + + Assert.Throws(() => + { + mc.CreateCacheEntryChangeMonitor(new string[] { "key", null }); + }); + + mc.Set("key1", "value1", ObjectCache.InfiniteAbsoluteExpiration); + mc.Set("key2", "value2", ObjectCache.InfiniteAbsoluteExpiration); + mc.Set("key3", "value3", ObjectCache.InfiniteAbsoluteExpiration); + + CacheEntryChangeMonitor monitor = mc.CreateCacheEntryChangeMonitor(new string[] { "key1", "key2" }); + Assert.NotNull(monitor); + Assert.Equal("System.Runtime.Caching.MemoryCacheEntryChangeMonitor", monitor.GetType().ToString()); + Assert.Equal(2, monitor.CacheKeys.Count); + Assert.Equal("key1", monitor.CacheKeys[0]); + Assert.Equal("key2", monitor.CacheKeys[1]); + Assert.Null(monitor.RegionName); + Assert.False(monitor.HasChanged); + + // The actual unique id is constructed from key names followed by the hex value of ticks of their last modifed time + Assert.False(String.IsNullOrEmpty(monitor.UniqueId)); + + monitor = mc.CreateCacheEntryChangeMonitor (new string [] { "key1", "doesnotexist" }); + Assert.NotNull (monitor); + Assert.Equal ("System.Runtime.Caching.MemoryCacheEntryChangeMonitor", monitor.GetType ().ToString ()); + Assert.Equal (2, monitor.CacheKeys.Count); + Assert.Equal ("key1", monitor.CacheKeys [0]); + Assert.Null (monitor.RegionName); + Assert.True (monitor.HasChanged); + } + + [Fact] + public void AddOrGetExisting_String_Object_DateTimeOffset_String() + { + var mc = new PokerMemoryCache("MyCache"); + + Assert.Throws(() => + { + mc.AddOrGetExisting(null, "value", DateTimeOffset.Now); + }); + + Assert.Throws(() => + { + mc.AddOrGetExisting("key", null, DateTimeOffset.Now); + }); + + Assert.Throws(() => + { + mc.AddOrGetExisting("key", "value", DateTimeOffset.Now, "region"); + }); + + object value = mc.AddOrGetExisting("key3_A2-1", "value", DateTimeOffset.Now.AddMinutes(1)); + Assert.True(mc.Contains("key3_A2-1")); + Assert.Null(value); + + mc.Calls.Clear(); + value = mc.AddOrGetExisting("key3_A2-1", "value2", DateTimeOffset.Now.AddMinutes(1)); + Assert.True(mc.Contains("key3_A2-1")); + Assert.NotNull(value); + Assert.Equal("value", value); + Assert.Equal(2, mc.Calls.Count); + Assert.Equal("AddOrGetExisting (string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)", mc.Calls[0]); + + value = mc.AddOrGetExisting("key_expired", "value", DateTimeOffset.MinValue); + Assert.False(mc.Contains("key_expired")); + Assert.Null(value); + } + + [Fact] + public void AddOrGetExisting_String_Object_CacheItemPolicy_String() + { + var mc = new PokerMemoryCache("MyCache"); + + Assert.Throws(() => + { + mc.AddOrGetExisting(null, "value", null); + }); + + Assert.Throws(() => + { + mc.AddOrGetExisting("key", null, null); + }); + + var cip = new CacheItemPolicy(); + cip.AbsoluteExpiration = DateTime.Now.AddMinutes(1); + cip.SlidingExpiration = TimeSpan.FromMinutes(1); + + Assert.Throws(() => + { + mc.AddOrGetExisting("key", "value", cip); + }); + + cip = new CacheItemPolicy(); + cip.SlidingExpiration = TimeSpan.MinValue; + Assert.Throws(() => + { + mc.AddOrGetExisting("key3", "value", cip); + }); + + Assert.Throws(() => + { + mc.AddOrGetExisting("key", "value", null, "region"); + }); + + cip = new CacheItemPolicy(); + cip.SlidingExpiration = TimeSpan.FromDays(500); + Assert.Throws(() => + { + mc.AddOrGetExisting("key3", "value", cip); + }); + + cip = new CacheItemPolicy(); + cip.Priority = (CacheItemPriority)20; + Assert.Throws(() => + { + mc.AddOrGetExisting("key3", "value", cip); + }); + + cip = new CacheItemPolicy(); + cip.SlidingExpiration = TimeSpan.FromTicks(0L); + mc.AddOrGetExisting("key3_A2-1", "value", cip); + Assert.True(mc.Contains("key3_A2-1")); + + cip = new CacheItemPolicy(); + cip.SlidingExpiration = TimeSpan.FromDays(365); + mc.AddOrGetExisting("key3_A2-2", "value", cip); + Assert.True(mc.Contains("key3_A2-2")); + + cip = new CacheItemPolicy(); + cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { }; + object value = mc.AddOrGetExisting("key3_A2-3", "value", cip); + Assert.True(mc.Contains("key3_A2-3")); + Assert.Null(value); + + mc.Calls.Clear(); + value = mc.AddOrGetExisting("key3_A2-3", "value2", null); + Assert.True(mc.Contains("key3_A2-3")); + Assert.NotNull(value); + Assert.Equal("value", value); + Assert.Equal(2, mc.Calls.Count); + Assert.Equal("AddOrGetExisting (string key, object value, CacheItemPolicy policy, string regionName = null)", mc.Calls[0]); + + cip = new CacheItemPolicy(); + cip.AbsoluteExpiration = DateTimeOffset.MinValue; + value = mc.AddOrGetExisting("key_expired", "value", cip); + Assert.False(mc.Contains("key_expired")); + Assert.Null(value); + } + + [Fact] + public void AddOrGetExisting_CacheItem_CacheItemPolicy() + { + var mc = new PokerMemoryCache("MyCache"); + CacheItem ci, ci2; + + Assert.Throws(() => + { + ci = mc.AddOrGetExisting(null, new CacheItemPolicy()); + }); + + ci = new CacheItem("key", "value"); + ci2 = mc.AddOrGetExisting(ci, null); + + Assert.NotNull(ci2); + Assert.NotEqual(ci, ci2); + Assert.Null(ci2.Value); + Assert.True(mc.Contains(ci.Key)); + Assert.Equal(ci.Key, ci2.Key); + + ci = new CacheItem("key", "value"); + ci2 = mc.AddOrGetExisting(ci, null); + Assert.NotNull(ci2); + Assert.NotEqual(ci, ci2); + Assert.NotNull(ci2.Value); + Assert.Equal(ci.Value, ci2.Value); + Assert.Equal(ci.Key, ci2.Key); + + Assert.Throws(() => + { + ci = new CacheItem(null, "value"); + ci2 = mc.AddOrGetExisting(ci, null); + }); + + ci = new CacheItem(String.Empty, "value"); + ci2 = mc.AddOrGetExisting(ci, null); + Assert.NotNull(ci2); + Assert.NotEqual(ci, ci2); + Assert.Null(ci2.Value); + Assert.True(mc.Contains(ci.Key)); + Assert.Equal(ci.Key, ci2.Key); + + ci = new CacheItem("key2", null); + + // Thrown from: + // at System.Runtime.Caching.MemoryCacheEntry..ctor(String key, Object value, DateTimeOffset absExp, TimeSpan slidingExp, CacheItemPriority priority, Collection`1 dependencies, CacheEntryRemovedCallback removedCallback, MemoryCache cache) + // at System.Runtime.Caching.MemoryCache.AddOrGetExistingInternal(String key, Object value, CacheItemPolicy policy) + // at System.Runtime.Caching.MemoryCache.AddOrGetExisting(CacheItem item, CacheItemPolicy policy) + // at MonoTests.System.Runtime.Caching.MemoryCacheTest.AddOrGetExisting_CacheItem_CacheItemPolicy() in C:\Users\grendel\documents\visual studio 2010\Projects\System.Runtime.Caching.Test\System.Runtime.Caching.Test\System.Runtime.Caching\MemoryCacheTest.cs:line 211 + Assert.Throws(() => + { + ci2 = mc.AddOrGetExisting(ci, null); + }); + + ci = new CacheItem("key3", "value"); + var cip = new CacheItemPolicy(); + cip.UpdateCallback = (CacheEntryUpdateArguments arguments) => { }; + Assert.Throws(() => + { + ci2 = mc.AddOrGetExisting(ci, cip); + }); + + ci = new CacheItem("key3", "value"); + cip = new CacheItemPolicy(); + cip.AbsoluteExpiration = DateTimeOffset.Now; + cip.SlidingExpiration = TimeSpan.FromTicks(DateTime.Now.Ticks); + Assert.Throws(() => + { + mc.AddOrGetExisting(ci, cip); + }); + + ci = new CacheItem("key3", "value"); + cip = new CacheItemPolicy(); + cip.SlidingExpiration = TimeSpan.MinValue; + Assert.Throws(() => + { + mc.AddOrGetExisting(ci, cip); + }); + + ci = new CacheItem("key4_#B4-2", "value"); + cip = new CacheItemPolicy(); + cip.SlidingExpiration = TimeSpan.FromTicks(0L); + mc.AddOrGetExisting(ci, cip); + Assert.True(mc.Contains("key4_#B4-2")); + + ci = new CacheItem("key3", "value"); + cip = new CacheItemPolicy(); + cip.SlidingExpiration = TimeSpan.FromDays(500); + Assert.Throws(() => + { + mc.AddOrGetExisting(ci, cip); + }); + + ci = new CacheItem("key5_#B5-2", "value"); + cip = new CacheItemPolicy(); + cip.SlidingExpiration = TimeSpan.FromDays(365); + mc.AddOrGetExisting(ci, cip); + Assert.True(mc.Contains("key5_#B5-2")); + + ci = new CacheItem("key3", "value"); + cip = new CacheItemPolicy(); + cip.Priority = (CacheItemPriority)20; + Assert.Throws(() => + { + mc.AddOrGetExisting(ci, cip); + }); + + ci = new CacheItem("key3_B7", "value"); + cip = new CacheItemPolicy(); + cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { }; + ci2 = mc.AddOrGetExisting(ci, cip); + Assert.True(mc.Contains("key3_B7")); + + Assert.NotNull(ci2); + Assert.NotEqual(ci, ci2); + Assert.Null(ci2.Value); + Assert.True(mc.Contains(ci.Key)); + Assert.Equal(ci.Key, ci2.Key); + + // The entry is never inserted as its expiration date is before now + ci = new CacheItem("key_D1", "value_D1"); + cip = new CacheItemPolicy(); + cip.AbsoluteExpiration = DateTimeOffset.MinValue; + ci2 = mc.AddOrGetExisting(ci, cip); + Assert.False(mc.Contains("key_D1")); + Assert.NotNull(ci2); + Assert.Null(ci2.Value); + Assert.Equal("key_D1", ci2.Key); + + mc.Calls.Clear(); + ci = new CacheItem("key_D2", "value_D2"); + cip = new CacheItemPolicy(); + cip.AbsoluteExpiration = DateTimeOffset.MaxValue; + mc.AddOrGetExisting(ci, cip); + Assert.True(mc.Contains("key_D2")); + Assert.Equal(2, mc.Calls.Count); + Assert.Equal("AddOrGetExisting (CacheItem item, CacheItemPolicy policy)", mc.Calls[0]); + } + + [Fact] + public void Set_String_Object_CacheItemPolicy_String() + { + var mc = new PokerMemoryCache("MyCache"); + + Assert.Throws(() => + { + mc.Set("key", "value", new CacheItemPolicy(), "region"); + }); + + Assert.Throws(() => + { + mc.Set(null, "value", new CacheItemPolicy()); + }); + + Assert.Throws(() => + { + mc.Set("key", null, new CacheItemPolicy()); + }); + + var cip = new CacheItemPolicy(); + cip.UpdateCallback = (CacheEntryUpdateArguments arguments) => { }; + cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { }; + Assert.Throws(() => + { + mc.Set("key", "value", cip); + }); + + cip = new CacheItemPolicy(); + cip.SlidingExpiration = TimeSpan.MinValue; + Assert.Throws(() => + { + mc.Set("key", "value", cip); + }); + + cip = new CacheItemPolicy(); + cip.SlidingExpiration = TimeSpan.FromTicks(0L); + mc.Set("key_A1-6", "value", cip); + Assert.True(mc.Contains("key_A1-6")); + + cip = new CacheItemPolicy(); + cip.SlidingExpiration = TimeSpan.FromDays(500); + Assert.Throws(() => + { + mc.Set("key", "value", cip); + }); + + cip = new CacheItemPolicy(); + cip.SlidingExpiration = TimeSpan.FromDays(365); + mc.Set("key_A1-8", "value", cip); + Assert.True(mc.Contains("key_A1-8")); + + cip = new CacheItemPolicy(); + cip.Priority = (CacheItemPriority)20; + Assert.Throws(() => + { + mc.Set("key", "value", cip); + }); + + cip = new CacheItemPolicy(); + cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { }; + mc.Set("key_A2", "value_A2", cip); + Assert.True(mc.Contains("key_A2")); + + mc.Set("key_A3", "value_A3", new CacheItemPolicy()); + Assert.True(mc.Contains("key_A3")); + Assert.Equal("value_A3", mc.Get("key_A3")); + + // The entry is never inserted as its expiration date is before now + cip = new CacheItemPolicy(); + cip.AbsoluteExpiration = DateTimeOffset.MinValue; + mc.Set("key_A4", "value_A4", cip); + Assert.False(mc.Contains("key_A4")); + + mc.Calls.Clear(); + cip = new CacheItemPolicy(); + cip.AbsoluteExpiration = DateTimeOffset.MaxValue; + mc.Set("key_A5", "value_A5", cip); + Assert.True(mc.Contains("key_A5")); + Assert.Equal(2, mc.Calls.Count); + Assert.Equal("Set (string key, object value, CacheItemPolicy policy, string regionName = null)", mc.Calls[0]); + } + + [Fact] + public void Set_String_Object_DateTimeOffset_String() + { + var mc = new PokerMemoryCache("MyCache"); + + Assert.Throws(() => + { + mc.Set("key", "value", DateTimeOffset.MaxValue, "region"); + }); + + Assert.Throws(() => + { + mc.Set(null, "value", DateTimeOffset.MaxValue); + }); + + Assert.Throws(() => + { + mc.Set("key", null, DateTimeOffset.MaxValue); + }); + + // The entry is never inserted as its expiration date is before now + mc.Set("key_A2", "value_A2", DateTimeOffset.MinValue); + Assert.False(mc.Contains("key_A2")); + + mc.Calls.Clear(); + mc.Set("key", "value", DateTimeOffset.MaxValue); + + Assert.Equal(2, mc.Calls.Count); + Assert.Equal("Set (string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)", mc.Calls[0]); + Assert.Equal("Set (string key, object value, CacheItemPolicy policy, string regionName = null)", mc.Calls[1]); + } + + [Fact] + public void Set_CacheItem_CacheItemPolicy() + { + var mc = new PokerMemoryCache("MyCache"); + + Assert.Throws(() => + { + mc.Set(null, new CacheItemPolicy()); + }); + + // Actually thrown from the Set (string, object, CacheItemPolicy, string) overload + var ci = new CacheItem(null, "value"); + Assert.Throws(() => + { + mc.Set(ci, new CacheItemPolicy()); + }); + + ci = new CacheItem("key", null); + Assert.Throws(() => + { + mc.Set(ci, new CacheItemPolicy()); + }); + + ci = new CacheItem("key", "value"); + var cip = new CacheItemPolicy(); + cip.UpdateCallback = (CacheEntryUpdateArguments arguments) => { }; + cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { }; + Assert.Throws(() => + { + mc.Set(ci, cip); + }); + + ci = new CacheItem("key", "value"); + cip = new CacheItemPolicy(); + cip.SlidingExpiration = TimeSpan.MinValue; + Assert.Throws(() => + { + mc.Set(ci, cip); + }); + + ci = new CacheItem("key_A1-6", "value"); + cip = new CacheItemPolicy(); + cip.SlidingExpiration = TimeSpan.FromTicks(0L); + mc.Set(ci, cip); + Assert.True(mc.Contains("key_A1-6")); + + ci = new CacheItem("key", "value"); + cip = new CacheItemPolicy(); + cip.SlidingExpiration = TimeSpan.FromDays(500); + Assert.Throws(() => + { + mc.Set(ci, cip); + }); + + ci = new CacheItem("key_A1-8", "value"); + cip = new CacheItemPolicy(); + cip.SlidingExpiration = TimeSpan.FromDays(365); + mc.Set(ci, cip); + Assert.True(mc.Contains("key_A1-8")); + + ci = new CacheItem("key", "value"); + cip = new CacheItemPolicy(); + cip.Priority = (CacheItemPriority)20; + Assert.Throws(() => + { + mc.Set(ci, cip); + }); + + ci = new CacheItem("key_A2", "value_A2"); + cip = new CacheItemPolicy(); + cip.RemovedCallback = (CacheEntryRemovedArguments arguments) => { }; + mc.Set(ci, cip); + Assert.True(mc.Contains("key_A2")); + + ci = new CacheItem("key_A3", "value_A3"); + mc.Set(ci, new CacheItemPolicy()); + Assert.True(mc.Contains("key_A3")); + Assert.Equal("value_A3", mc.Get("key_A3")); + + // The entry is never inserted as its expiration date is before now + ci = new CacheItem("key_A4", "value"); + cip = new CacheItemPolicy(); + cip.AbsoluteExpiration = DateTimeOffset.MinValue; + mc.Set(ci, cip); + Assert.False(mc.Contains("key_A4")); + + ci = new CacheItem("key_A5", "value"); + mc.Calls.Clear(); + mc.Set(ci, new CacheItemPolicy()); + + Assert.Equal(2, mc.Calls.Count); + Assert.Equal("Set (CacheItem item, CacheItemPolicy policy)", mc.Calls[0]); + Assert.Equal("Set (string key, object value, CacheItemPolicy policy, string regionName = null)", mc.Calls[1]); + } + + [Fact] + public void Remove() + { + var mc = new PokerMemoryCache("MyCache"); + + Assert.Throws(() => + { + mc.Remove("key", "region"); + }); + + Assert.Throws(() => + { + mc.Remove(null); + }); + + bool callbackInvoked; + CacheEntryRemovedReason reason = (CacheEntryRemovedReason)1000; + var cip = new CacheItemPolicy(); + cip.Priority = CacheItemPriority.NotRemovable; + mc.Set("key2", "value1", cip); + object value = mc.Remove("key2"); + + Assert.NotNull(value); + Assert.False(mc.Contains("key2")); + + cip = new CacheItemPolicy(); + cip.RemovedCallback = (CacheEntryRemovedArguments args) => + { + callbackInvoked = true; + reason = args.RemovedReason; + }; + + mc.Set("key", "value", cip); + callbackInvoked = false; + reason = (CacheEntryRemovedReason)1000; + value = mc.Remove("key"); + Assert.NotNull(value); + Assert.True(callbackInvoked); + Assert.Equal(CacheEntryRemovedReason.Removed, reason); + + cip = new CacheItemPolicy(); + cip.RemovedCallback = (CacheEntryRemovedArguments args) => + { + callbackInvoked = true; + reason = args.RemovedReason; + throw new ApplicationException("test"); + }; + + mc.Set("key", "value", cip); + callbackInvoked = false; + reason = (CacheEntryRemovedReason)1000; + value = mc.Remove("key"); + Assert.NotNull(value); + Assert.True(callbackInvoked); + Assert.Equal(CacheEntryRemovedReason.Removed, reason); + + cip = new CacheItemPolicy(); + cip.UpdateCallback = (CacheEntryUpdateArguments args) => + { + callbackInvoked = true; + reason = args.RemovedReason; + }; + + mc.Set("key", "value", cip); + callbackInvoked = false; + reason = (CacheEntryRemovedReason)1000; + value = mc.Remove("key"); + Assert.NotNull(value); + Assert.False(callbackInvoked); + + cip = new CacheItemPolicy(); + cip.UpdateCallback = (CacheEntryUpdateArguments args) => + { + callbackInvoked = true; + reason = args.RemovedReason; + throw new ApplicationException("test"); + }; + + mc.Set("key", "value", cip); + callbackInvoked = false; + reason = (CacheEntryRemovedReason)1000; + value = mc.Remove("key"); + Assert.NotNull(value); + Assert.False(callbackInvoked); + } + + [Fact] + public void GetValues() + { + var mc = new PokerMemoryCache("MyCache"); + + Assert.Throws(() => + { + mc.GetValues((string[])null); + }); + + Assert.Throws(() => + { + mc.GetValues(new string[] { }, "region"); + }); + + Assert.Throws(() => + { + mc.GetValues(new string[] { "key", null }); + }); + + IDictionary value = mc.GetValues(new string[] { }); + Assert.Null(value); + + mc.Set("key1", "value1", null); + mc.Set("key2", "value2", null); + mc.Set("key3", "value3", null); + + Assert.True(mc.Contains("key1")); + Assert.True(mc.Contains("key2")); + Assert.True(mc.Contains("key3")); + + value = mc.GetValues(new string[] { "key1", "key3" }); + Assert.NotNull(value); + Assert.Equal(2, value.Count); + Assert.Equal("value1", value["key1"]); + Assert.Equal("value3", value["key3"]); + Assert.Equal(typeof(Dictionary), value.GetType()); + + // MSDN says the number of items in the returned dictionary should be the same as in the + // 'keys' collection - this is not the case. The returned dictionary contains only entries for keys + // that exist in the cache. + value = mc.GetValues(new string[] { "key1", "key3", "nosuchkey" }); + Assert.NotNull(value); + Assert.Equal(2, value.Count); + Assert.Equal("value1", value["key1"]); + Assert.Equal("value3", value["key3"]); + Assert.False(value.ContainsKey("Key1")); + } + + [Fact] + public void ChangeMonitors() + { + bool removed = false; + var mc = new PokerMemoryCache("MyCache"); + var cip = new CacheItemPolicy(); + var monitor = new PokerChangeMonitor(); + cip.ChangeMonitors.Add(monitor); + cip.RemovedCallback = (CacheEntryRemovedArguments args) => + { + removed = true; + }; + + mc.Set("key", "value", cip); + Assert.Equal(0, monitor.Calls.Count); + + monitor.SignalChange(); + Assert.True(removed); + + bool onChangedCalled = false; + monitor = new PokerChangeMonitor(); + monitor.NotifyOnChanged((object state) => + { + onChangedCalled = true; + }); + + cip = new CacheItemPolicy(); + cip.ChangeMonitors.Add(monitor); + + // Thrown by ChangeMonitor.NotifyOnChanged + Assert.Throws(() => + { + mc.Set("key1", "value1", cip); + }); + + Assert.False(onChangedCalled); + } + + // Due to internal implementation details Trim has very few easily verifiable scenarios + [Fact] + public void Trim() + { + var config = new NameValueCollection(); + config["__MonoEmulateOneCPU"] = "true"; + var mc = new MemoryCache("MyCache", config); + + var numCpuCores = Environment.ProcessorCount; + var numItems = numCpuCores > 1 ? numCpuCores / 2 : 1; + + for (int i = 0; i < numItems;) + { + var key = "key" + i*i*i + "key" + ++i; + mc.Set(key, "value" + i.ToString(), null); + } + + Assert.Equal(numItems, mc.GetCount()); + + // Trimming 75% for such a small number of items (supposedly each in its cache store) will end up trimming all of them + long trimmed = mc.Trim(75); + Assert.Equal(numItems, trimmed); + Assert.Equal(0, mc.GetCount()); + + mc = new MemoryCache("MyCache", config); + var cip = new CacheItemPolicy(); + cip.Priority = CacheItemPriority.NotRemovable; + for (int i = 0; i < 11; i++) + { + mc.Set("key" + i.ToString(), "value" + i.ToString(), cip); + } + + Assert.Equal(11, mc.GetCount()); + trimmed = mc.Trim(50); + Assert.Equal(11, mc.GetCount()); + } + + [Fact] + public void TestExpiredGetValues() + { + var config = new NameValueCollection(); + config["cacheMemoryLimitMegabytes"] = 0.ToString(); + config["physicalMemoryLimitPercentage"] = 100.ToString(); + config["pollingInterval"] = new TimeSpan(0, 0, 10).ToString(); + + using (var mc = new MemoryCache("TestExpiredGetValues", config)) + { + Assert.Equal(0, mc.GetCount()); + + var keys = new List(); + + // add some short duration entries + for (int i = 0; i < 10; i++) + { + var key = "short-" + i; + var expireAt = DateTimeOffset.Now.AddMilliseconds(50); + mc.Add(key, i.ToString(), expireAt); + + keys.Add(key); + } + + Assert.Equal(10, mc.GetCount()); + + // wait past expiration and call GetValues() - this does not affect the count + Thread.Sleep(100); + mc.GetValues(keys); + Assert.Equal(0, mc.GetCount()); + } + } + + [Fact] + public void TestCacheSliding() + { + var config = new NameValueCollection(); + config["cacheMemoryLimitMegabytes"] = 0.ToString(); + config["physicalMemoryLimitPercentage"] = 100.ToString(); + config["pollingInterval"] = new TimeSpan(0, 0, 1).ToString(); + + using (var mc = new MemoryCache("TestCacheSliding", config)) + { + Assert.Equal(0, mc.GetCount()); + + var cip = new CacheItemPolicy(); + // The sliding expiration timeout has to be greater than 1 second because + // .NET implementation ignores timeouts updates smaller than + // CacheExpires.MIN_UPDATE_DELTA which is equal to 1. + cip.SlidingExpiration = TimeSpan.FromSeconds(2); + mc.Add("slidingtest", "42", cip); + + mc.Add("expire1", "1", cip); + mc.Add("expire2", "2", cip); + mc.Add("expire3", "3", cip); + mc.Add("expire4", "4", cip); + mc.Add("expire5", "5", cip); + + Assert.Equal(6, mc.GetCount()); + + for (int i = 0; i < 50; i++) + { + Thread.Sleep(100); + + var item = mc.Get("slidingtest"); + Assert.NotEqual(null, item); + } + + Assert.Null(mc.Get("expire1")); + Assert.Null(mc.Get("expire2")); + Assert.Null(mc.Get("expire3")); + Assert.Null(mc.Get("expire4")); + Assert.Null(mc.Get("expire5")); + Assert.Equal(1, mc.GetCount()); + + Thread.Sleep(3000); + + Assert.Null(mc.Get("slidingtest")); + Assert.Equal(0, mc.GetCount()); + } + } + } + + public class MemoryCacheTestExpires1 + { + [Fact] + [OuterLoop] // makes long wait + public async Task TimedExpirationAsync() + { + bool removed = false; + CacheEntryRemovedReason reason = CacheEntryRemovedReason.CacheSpecificEviction; + int sleepPeriod = 20000; + + var mc = new PokerMemoryCache("MyCache"); + var cip = new CacheItemPolicy(); + + cip.RemovedCallback = (CacheEntryRemovedArguments args) => + { + removed = true; + reason = args.RemovedReason; + }; + cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds(50); + mc.Set("key", "value", cip); + + // Wait past cip.AbsoluteExpiration + Thread.Sleep(500); + object value = mc.Get("key"); + Assert.Null(value); + + // Rather than waiting for the expiration callback to fire, + // we replace the cache item and verify that the reason is still Expired + mc.Set("key", "value2", cip); + Assert.True(removed); + Assert.Equal(CacheEntryRemovedReason.Expired, reason); + + removed = false; + cip = new CacheItemPolicy(); + cip.RemovedCallback = (CacheEntryRemovedArguments args) => + { + removed = true; + reason = args.RemovedReason; + }; + cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds(50); + mc.Set("key", "value", cip); + await Task.Delay(sleepPeriod); + + Assert.Null(mc.Get("key")); + Assert.True(removed); + Assert.Equal(CacheEntryRemovedReason.Expired, reason); + } + } + + public class MemoryCacheTestExpires11 + { + [Fact] + [OuterLoop] // makes long wait + public async Task TimedExpirationAsync() + { + int sleepPeriod = 20000; + + var mc = new PokerMemoryCache("MyCache"); + var cip = new CacheItemPolicy(); + + int expiredCount = 0; + object expiredCountLock = new object(); + CacheEntryRemovedCallback removedCb = (CacheEntryRemovedArguments args) => + { + lock (expiredCountLock) + { + expiredCount++; + } + }; + + cip = new CacheItemPolicy(); + cip.RemovedCallback = removedCb; + cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds(20); + mc.Set("key1", "value1", cip); + + cip = new CacheItemPolicy(); + cip.RemovedCallback = removedCb; + cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds(200); + mc.Set("key2", "value2", cip); + + cip = new CacheItemPolicy(); + cip.RemovedCallback = removedCb; + cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds(600); + mc.Set("key3", "value3", cip); + + cip = new CacheItemPolicy(); + cip.RemovedCallback = removedCb; + cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds(sleepPeriod + 55500); + mc.Set("key4", "value4", cip); + + await Task.Delay(sleepPeriod); + Assert.Null(mc.Get("key1")); + Assert.Null(mc.Get("key2")); + Assert.Null(mc.Get("key3")); + Assert.NotNull(mc.Get("key4")); + Assert.Equal(3, expiredCount); + } + } + + public class MemoryCacheTestExpires2 + { + [Fact] + [OuterLoop] // makes long wait + public async Task GetEnumeratorAsync() + { + var mc = new PokerMemoryCache("MyCache"); + + // This one is a Hashtable enumerator + IEnumerator enumerator = ((IEnumerable)mc).GetEnumerator(); + + // This one is a Dictionary enumerator + IEnumerator enumerator2 = mc.DoGetEnumerator(); + + Assert.NotNull(enumerator); + Assert.NotNull(enumerator2); + Assert.True(enumerator.GetType() != enumerator2.GetType()); + + mc.Set("key1", "value1", null); + mc.Set("key2", "value2", null); + mc.Set("key3", "value3", null); + + bool expired4 = false; + var cip = new CacheItemPolicy(); + cip.AbsoluteExpiration = DateTime.Now.AddMilliseconds(50); + cip.RemovedCallback = (CacheEntryRemovedArguments args) => + { + expired4 = true; + }; + + mc.Set("key4", "value4", cip); + // wait past "key4" AbsoluteExpiration + Thread.Sleep(500); + + enumerator = ((IEnumerable)mc).GetEnumerator(); + int count = 0; + while (enumerator.MoveNext()) + { + count++; + } + + Assert.Equal(3, count); + + bool expired5 = false; + cip = new CacheItemPolicy(); + cip.AbsoluteExpiration = DateTime.Now.AddMilliseconds(50); + cip.RemovedCallback = (CacheEntryRemovedArguments args) => + { + expired5 = true; + }; + + mc.Set("key5", "value5", cip); + await Task.Delay(20500); + + enumerator2 = mc.DoGetEnumerator(); + count = 0; + while (enumerator2.MoveNext()) + { + count++; + } + + Assert.True(expired4); + Assert.True(expired5); + Assert.Equal(3, count); + } + } + + public class MemoryCacheTestExpires3 + { + [Fact] + [OuterLoop] // makes long wait + public async Task GetCacheItem() + { + var mc = new PokerMemoryCache("MyCache"); + + Assert.Throws(() => + { + mc.GetCacheItem("key", "region"); + }); + + Assert.Throws(() => + { + mc.GetCacheItem(null); + }); + + CacheItem value; + mc.Set("key", "value", null); + value = mc.GetCacheItem("key"); + Assert.NotNull(value); + Assert.Equal("value", value.Value); + Assert.Equal("key", value.Key); + + value = mc.GetCacheItem("doesnotexist"); + Assert.Null(value); + + var cip = new CacheItemPolicy(); + bool callbackInvoked = false; + CacheEntryRemovedReason reason = (CacheEntryRemovedReason)1000; + + cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds(50); + cip.RemovedCallback = (CacheEntryRemovedArguments args) => + { + callbackInvoked = true; + reason = args.RemovedReason; + }; + mc.Set("key", "value", cip); + // wait past the expiration time and verify that the item is gone + await Task.Delay(500); + value = mc.GetCacheItem("key"); + Assert.Null(value); + + // add a new item with the same key + cip = new CacheItemPolicy(); + cip.AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds(50); + cip.RemovedCallback = (CacheEntryRemovedArguments args) => + { + callbackInvoked = true; + reason = args.RemovedReason; + throw new ApplicationException("test"); + }; + mc.Set("key", "value", cip); + + // and verify that the old item callback is called + Assert.True(callbackInvoked); + Assert.Equal(CacheEntryRemovedReason.Expired, reason); + + callbackInvoked = false; + reason = (CacheEntryRemovedReason)1000; + + // wait for both expiration and the callback of the new item + await Task.Delay(20500); + + value = mc.GetCacheItem("key"); + Assert.Null(value); + Assert.True(callbackInvoked); + Assert.Equal(CacheEntryRemovedReason.Expired, reason); + } + } + + public class MemoryCacheTestExpires4 + { + [Fact] + public async Task TestCacheShrink() + { + const int HEAP_RESIZE_THRESHOLD = 8192 + 2; + const int HEAP_RESIZE_SHORT_ENTRIES = 2048; + const int HEAP_RESIZE_LONG_ENTRIES = HEAP_RESIZE_THRESHOLD - HEAP_RESIZE_SHORT_ENTRIES; + + var config = new NameValueCollection(); + config["cacheMemoryLimitMegabytes"] = 0.ToString(); + config["physicalMemoryLimitPercentage"] = 100.ToString(); + config["pollingInterval"] = new TimeSpan(0, 0, 1).ToString(); + + using (var mc = new MemoryCache("TestCacheShrink", config)) + { + Assert.Equal(0, mc.GetCount()); + + // add some short duration entries + for (int i = 0; i < HEAP_RESIZE_SHORT_ENTRIES; i++) + { + var expireAt = DateTimeOffset.Now.AddSeconds(3); + mc.Add("short-" + i, i.ToString(), expireAt); + } + + Assert.Equal(HEAP_RESIZE_SHORT_ENTRIES, mc.GetCount()); + + // add some long duration entries + for (int i = 0; i < HEAP_RESIZE_LONG_ENTRIES; i++) + { + var expireAt = DateTimeOffset.Now.AddSeconds(42); + mc.Add("long-" + i, i.ToString(), expireAt); + } + + Assert.Equal(HEAP_RESIZE_LONG_ENTRIES + HEAP_RESIZE_SHORT_ENTRIES, mc.GetCount()); + + // wait past the short duration items expiration time + await Task.Delay(4000); + + /// the following will also shrink the size of the cache + for (int i = 0; i < HEAP_RESIZE_SHORT_ENTRIES; i++) + { + Assert.Null(mc.Get("short-" + i)); + } + Assert.Equal(HEAP_RESIZE_LONG_ENTRIES, mc.GetCount()); + + // add some new items into the cache, this will grow the cache again + for (int i = 0; i < HEAP_RESIZE_LONG_ENTRIES; i++) + { + mc.Add("final-" + i, i.ToString(), DateTimeOffset.Now.AddSeconds(4)); + } + + Assert.Equal(HEAP_RESIZE_LONG_ENTRIES + HEAP_RESIZE_LONG_ENTRIES, mc.GetCount()); + } + } + } + + public class MemoryCacheTestExpires5 + { + [Fact] + public async Task TestCacheExpiryOrdering() + { + var config = new NameValueCollection(); + config["cacheMemoryLimitMegabytes"] = 0.ToString(); + config["physicalMemoryLimitPercentage"] = 100.ToString(); + config["pollingInterval"] = new TimeSpan(0, 0, 1).ToString(); + + using (var mc = new MemoryCache("TestCacheExpiryOrdering", config)) + { + Assert.Equal(0, mc.GetCount()); + + // add long lived items into the cache first + for (int i = 0; i < 100; i++) + { + var cip = new CacheItemPolicy(); + cip.SlidingExpiration = new TimeSpan(0, 0, 4); + mc.Add("long-" + i, i, cip); + } + + Assert.Equal(100, mc.GetCount()); + + // add shorter lived items into the cache, these should expire first + for (int i = 0; i < 100; i++) + { + var cip = new CacheItemPolicy(); + cip.SlidingExpiration = new TimeSpan(0, 0, 1); + mc.Add("short-" + i, i, cip); + } + + Assert.Equal(200, mc.GetCount()); + + await Task.Delay(2000); + + for (int i = 0; i < 100; i++) + { + Assert.Null(mc.Get("short-" + i)); + } + Assert.Equal(100, mc.GetCount()); + } + } + } +} diff --git a/external/corefx/src/System.Runtime.Caching/tests/System.Runtime.Caching/ObjectCacheTest.cs b/external/corefx/src/System.Runtime.Caching/tests/System.Runtime.Caching/ObjectCacheTest.cs new file mode 100644 index 0000000000..9400fb3a04 --- /dev/null +++ b/external/corefx/src/System.Runtime.Caching/tests/System.Runtime.Caching/ObjectCacheTest.cs @@ -0,0 +1,148 @@ +// Licensed to the .NET Foundation under one or more agreements. +// See the LICENSE file in the project root for more information. + +// +// +// Authors: +// Marek Habersack +// +// Copyright (C) 2010 Novell, Inc. (http://novell.com/) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.Runtime.Caching; + +using Xunit; +using MonoTests.Common; + +namespace MonoTests.System.Runtime.Caching +{ + public class ObjectCacheTest + { + [Fact] + [ActiveIssue(25168)] + private static void Host_SetToProvider() + { + var tns1 = new TestNotificationSystem(); + var tns2 = new TestNotificationSystem(); + ObjectCache.Host = tns1; + Assert.NotNull(ObjectCache.Host); + Assert.Equal(tns1, ObjectCache.Host); + + Assert.Throws(() => + { + ObjectCache.Host = tns2; + }); + } + + [Fact] + public void Add_CacheItem_CacheItemPolicy() + { + var poker = new PokerObjectCache(); + bool ret; + + ret = poker.Add(null, null); + Assert.True(ret); + Assert.Equal("AddOrGetExisting (CacheItem value, CacheItemPolicy policy)", poker.MethodCalled); + + var item = new CacheItem("key", 1234); + ret = poker.Add(item, null); + Assert.True(ret); + Assert.Equal("AddOrGetExisting (CacheItem value, CacheItemPolicy policy)", poker.MethodCalled); + + ret = poker.Add(item, null); + Assert.False(ret); + Assert.Equal("AddOrGetExisting (CacheItem value, CacheItemPolicy policy)", poker.MethodCalled); + } + + [Fact] + public void Add_String_Object_CacheItemPolicy_String() + { + var poker = new PokerObjectCache(); + bool ret; + + ret = poker.Add(null, null, null, null); + Assert.True(ret); + Assert.Equal("AddOrGetExisting (string key, object value, CacheItemPolicy policy, string regionName = null)", poker.MethodCalled); + + ret = poker.Add("key", 1234, null, null); + Assert.True(ret); + Assert.Equal("AddOrGetExisting (string key, object value, CacheItemPolicy policy, string regionName = null)", poker.MethodCalled); + + ret = poker.Add("key", 1234, null, null); + Assert.False(ret); + Assert.Equal("AddOrGetExisting (string key, object value, CacheItemPolicy policy, string regionName = null)", poker.MethodCalled); + } + + [Fact] + public void Add_String_Object_DateTimeOffset_String() + { + var poker = new PokerObjectCache(); + bool ret; + + ret = poker.Add(null, null, DateTimeOffset.Now, null); + Assert.True(ret); + Assert.Equal("AddOrGetExisting (string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)", poker.MethodCalled); + + ret = poker.Add("key", 1234, DateTimeOffset.Now, null); + Assert.True(ret); + Assert.Equal("AddOrGetExisting (string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)", poker.MethodCalled); + + ret = poker.Add("key", 1234, DateTimeOffset.Now, null); + Assert.False(ret); + Assert.Equal("AddOrGetExisting (string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)", poker.MethodCalled); + } + + [Fact] + public void GetValues() + { + var poker = new PokerObjectCache(); + + IDictionary values = poker.GetValues(null, (string[])null); + Assert.NotNull(values); + Assert.Equal(0, values.Count); + Assert.Equal("IDictionary GetValues (IEnumerable keys, string regionName = null)", poker.MethodCalled); + + poker.Add("key1", 1, null); + poker.Add("key2", 2, null); + poker.Add("key3", 3, null); + + values = poker.GetValues(new string[] { "key1", "key2", "key3" }); + Assert.NotNull(values); + Assert.Equal(3, values.Count); + Assert.Equal("IDictionary GetValues (IEnumerable keys, string regionName = null)", poker.MethodCalled); + + values = poker.GetValues(new string[] { "key1", "key22", "key3" }); + Assert.NotNull(values); + Assert.Equal(2, values.Count); + Assert.Equal("IDictionary GetValues (IEnumerable keys, string regionName = null)", poker.MethodCalled); + } + + [Fact] + public void Defaults() + { + Assert.Equal(DateTimeOffset.MaxValue, ObjectCache.InfiniteAbsoluteExpiration); + Assert.Equal(TimeSpan.Zero, ObjectCache.NoSlidingExpiration); + } + } +} diff --git a/external/corefx/src/System.Runtime.CompilerServices.Unsafe/System.Runtime.CompilerServices.Unsafe.sln b/external/corefx/src/System.Runtime.CompilerServices.Unsafe/System.Runtime.CompilerServices.Unsafe.sln index f3576202c9..bc068f13fa 100644 --- a/external/corefx/src/System.Runtime.CompilerServices.Unsafe/System.Runtime.CompilerServices.Unsafe.sln +++ b/external/corefx/src/System.Runtime.CompilerServices.Unsafe/System.Runtime.CompilerServices.Unsafe.sln @@ -26,10 +26,10 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8012DD70-A6D7-45C0-BC8E-DFFB48D86E08}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU - {8012DD70-A6D7-45C0-BC8E-DFFB48D86E08}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU - {8012DD70-A6D7-45C0-BC8E-DFFB48D86E08}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU - {8012DD70-A6D7-45C0-BC8E-DFFB48D86E08}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU + {8012DD70-A6D7-45C0-BC8E-DFFB48D86E08}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {8012DD70-A6D7-45C0-BC8E-DFFB48D86E08}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {8012DD70-A6D7-45C0-BC8E-DFFB48D86E08}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {8012DD70-A6D7-45C0-BC8E-DFFB48D86E08}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU {04BA3E3C-6979-4792-B19E-C797AD607F42}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU {04BA3E3C-6979-4792-B19E-C797AD607F42}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {04BA3E3C-6979-4792-B19E-C797AD607F42}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU diff --git a/external/corefx/src/System.Runtime.CompilerServices.Unsafe/dir.props b/external/corefx/src/System.Runtime.CompilerServices.Unsafe/dir.props index 3e15820477..0d81e0a9e8 100644 --- a/external/corefx/src/System.Runtime.CompilerServices.Unsafe/dir.props +++ b/external/corefx/src/System.Runtime.CompilerServices.Unsafe/dir.props @@ -4,7 +4,5 @@ 4.0.4.0 MSFT - true - true \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.CompilerServices.Unsafe/pkg/System.Runtime.CompilerServices.Unsafe.pkgproj b/external/corefx/src/System.Runtime.CompilerServices.Unsafe/pkg/System.Runtime.CompilerServices.Unsafe.pkgproj index 310dd2420a..d73c618a40 100644 --- a/external/corefx/src/System.Runtime.CompilerServices.Unsafe/pkg/System.Runtime.CompilerServices.Unsafe.pkgproj +++ b/external/corefx/src/System.Runtime.CompilerServices.Unsafe/pkg/System.Runtime.CompilerServices.Unsafe.pkgproj @@ -7,15 +7,6 @@ net45;netcore45;wp8;wpa81;netcoreapp1.0;$(AllXamarinFrameworks) - - - - .NETCoreApp;UAP - \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.CompilerServices.Unsafe/ref/System.Runtime.CompilerServices.Unsafe.cs b/external/corefx/src/System.Runtime.CompilerServices.Unsafe/ref/System.Runtime.CompilerServices.Unsafe.cs index b53c5d496c..fe7fcc03c0 100644 --- a/external/corefx/src/System.Runtime.CompilerServices.Unsafe/ref/System.Runtime.CompilerServices.Unsafe.cs +++ b/external/corefx/src/System.Runtime.CompilerServices.Unsafe/ref/System.Runtime.CompilerServices.Unsafe.cs @@ -16,7 +16,7 @@ namespace System.Runtime.CompilerServices public static bool AreSame(ref T left, ref T right) { throw null; } public unsafe static void* AsPointer(ref T value) { throw null; } public unsafe static ref T AsRef(void* source) { throw null; } - public static ref T AsRef(ref readonly T source) { throw null; } + public static ref T AsRef(in T source) { throw null; } public static T As(object o) where T : class { throw null; } public static ref TTo As(ref TFrom source) { throw null; } public static System.IntPtr ByteOffset(ref T origin, ref T target) { throw null; } diff --git a/external/corefx/src/System.Runtime.CompilerServices.Unsafe/src/Configurations.props b/external/corefx/src/System.Runtime.CompilerServices.Unsafe/src/Configurations.props index ba6b9bda8a..d13cce3482 100644 --- a/external/corefx/src/System.Runtime.CompilerServices.Unsafe/src/Configurations.props +++ b/external/corefx/src/System.Runtime.CompilerServices.Unsafe/src/Configurations.props @@ -4,7 +4,6 @@ netstandard1.0; netstandard; - netcoreapp; diff --git a/external/corefx/src/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.xml b/external/corefx/src/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.xml index c6a85cc991..8c24ae6194 100644 --- a/external/corefx/src/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.xml +++ b/external/corefx/src/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.xml @@ -17,6 +17,22 @@ The location to read from. An object of type read from the given location. + + + Reads a value of type from the given location. + + The type to read. + The location to read from. + An object of type read from the given location. + + + + Reads a value of type from the given location. + + The type to read. + The location to read from. + An object of type read from the given location. + Writes a value of type to the given location. @@ -25,6 +41,22 @@ The location to write to. The value to write. + + + Writes a value of type to the given location. + + The type of value to write. + The location to write to. + The value to write. + + + + Writes a value of type to the given location. + + The type of value to write. + The location to write to. + The value to write. + Copies a value of type to the given location. @@ -58,7 +90,7 @@ - Casts the given object to the specified type. + Casts the given object to the specified type, performs no dynamic type checking. The type which the object will be cast to. The object to cast. @@ -98,6 +130,15 @@ The offset to add. A new reference that reflects the addition of offset to pointer. + + + Adds an element offset to the given pointer. + + The type of reference. + The pointer to add the offset to. + The offset to add. + A new pointer that reflects the addition of offset to pointer. + Adds an element offset to the given reference. diff --git a/external/corefx/src/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs b/external/corefx/src/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs index 4a71020ae3..fce1ae27d3 100644 --- a/external/corefx/src/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs +++ b/external/corefx/src/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs @@ -792,9 +792,12 @@ namespace System.Runtime.CompilerServices public fixed byte Bytes[512]; } + [StructLayout(LayoutKind.Explicit, Size=16)] public unsafe struct Int32Double { + [FieldOffset(0)] public int Int32; + [FieldOffset(8)] public double Double; public static unsafe byte[] Unaligned(int i, double d) diff --git a/external/corefx/src/System.Runtime.Extensions/System.Runtime.Extensions.sln b/external/corefx/src/System.Runtime.Extensions/System.Runtime.Extensions.sln index 8f39f620ce..a0f5ddf3da 100644 --- a/external/corefx/src/System.Runtime.Extensions/System.Runtime.Extensions.sln +++ b/external/corefx/src/System.Runtime.Extensions/System.Runtime.Extensions.sln @@ -59,10 +59,10 @@ Global {E60AFAE8-2EA7-471C-9E24-52A99453B26B}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU {E60AFAE8-2EA7-471C-9E24-52A99453B26B}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU {E60AFAE8-2EA7-471C-9E24-52A99453B26B}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU - {978FA427-F87B-46E2-B276-F5B727C50A01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {978FA427-F87B-46E2-B276-F5B727C50A01}.Debug|Any CPU.Build.0 = Debug|Any CPU - {978FA427-F87B-46E2-B276-F5B727C50A01}.Release|Any CPU.ActiveCfg = Release|Any CPU - {978FA427-F87B-46E2-B276-F5B727C50A01}.Release|Any CPU.Build.0 = Release|Any CPU + {978FA427-F87B-46E2-B276-F5B727C50A01}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {978FA427-F87B-46E2-B276-F5B727C50A01}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {978FA427-F87B-46E2-B276-F5B727C50A01}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {978FA427-F87B-46E2-B276-F5B727C50A01}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU {24BCEC6B-B9D2-47BC-9D66-725BD6B526FA}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU {24BCEC6B-B9D2-47BC-9D66-725BD6B526FA}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU {24BCEC6B-B9D2-47BC-9D66-725BD6B526FA}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU diff --git a/external/corefx/src/System.Runtime.Extensions/ref/System.Runtime.Extensions.cs.REMOVED.git-id b/external/corefx/src/System.Runtime.Extensions/ref/System.Runtime.Extensions.cs.REMOVED.git-id index b87bf8378e..45a985388b 100644 --- a/external/corefx/src/System.Runtime.Extensions/ref/System.Runtime.Extensions.cs.REMOVED.git-id +++ b/external/corefx/src/System.Runtime.Extensions/ref/System.Runtime.Extensions.cs.REMOVED.git-id @@ -1 +1 @@ -312c20682d4fe5a6eddb4b88be373f41aa9e09ff \ No newline at end of file +c21a0bca889034baada9df6932fe27eb643fa041 \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Extensions/src/ApiCompatBaseline.uapaot.txt b/external/corefx/src/System.Runtime.Extensions/src/ApiCompatBaseline.uapaot.txt deleted file mode 100644 index 9e3f3ef566..0000000000 --- a/external/corefx/src/System.Runtime.Extensions/src/ApiCompatBaseline.uapaot.txt +++ /dev/null @@ -1,15 +0,0 @@ -Compat issues with assembly System.Runtime.Extensions: -MembersMustExist : Member 'System.Math.Clamp(System.Byte, System.Byte, System.Byte)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Math.Clamp(System.Decimal, System.Decimal, System.Decimal)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Math.Clamp(System.Double, System.Double, System.Double)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Math.Clamp(System.Int16, System.Int16, System.Int16)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Math.Clamp(System.Int32, System.Int32, System.Int32)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Math.Clamp(System.Int64, System.Int64, System.Int64)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Math.Clamp(System.SByte, System.SByte, System.SByte)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Math.Clamp(System.Single, System.Single, System.Single)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Math.Clamp(System.UInt16, System.UInt16, System.UInt16)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Math.Clamp(System.UInt32, System.UInt32, System.UInt32)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Math.Clamp(System.UInt64, System.UInt64, System.UInt64)' does not exist in the implementation but it does exist in the contract. -CannotRemoveBaseTypeOrInterface : Type 'System.IO.BufferedStream' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract. -CannotRemoveBaseTypeOrInterface : Type 'System.IO.MemoryStream' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract. -Total Issues: 13 diff --git a/external/corefx/src/System.Runtime.Extensions/src/Resources/Strings.resx b/external/corefx/src/System.Runtime.Extensions/src/Resources/Strings.resx index 1b7afc3757..d34633f60c 100644 --- a/external/corefx/src/System.Runtime.Extensions/src/Resources/Strings.resx +++ b/external/corefx/src/System.Runtime.Extensions/src/Resources/Strings.resx @@ -154,7 +154,7 @@ Specified file length was too large for the file system. - Illegal characters in path. + Illegal characters in path '{0}'. Path cannot be the empty string or all whitespace. @@ -360,9 +360,6 @@ Cannot access a closed Stream. - - The stream is currently in use by a previous operation on the stream. - Empty path name is not legal. @@ -411,7 +408,10 @@ AppDomain resource monitoring is not supported on this platform. - - The read operation returned an invalid length. + + The path '{0}' is too long, or a component of the specified path is too long. - \ No newline at end of file + + The given key '{0}' was not present in the dictionary. + + diff --git a/external/corefx/src/System.Runtime.Extensions/src/System.Runtime.Extensions.csproj b/external/corefx/src/System.Runtime.Extensions/src/System.Runtime.Extensions.csproj index 3141f76790..7e5768f0ba 100644 --- a/external/corefx/src/System.Runtime.Extensions/src/System.Runtime.Extensions.csproj +++ b/external/corefx/src/System.Runtime.Extensions/src/System.Runtime.Extensions.csproj @@ -8,9 +8,8 @@ true true true - $(DefineConstants);uapaot + $(DefineConstants);uapaot - @@ -35,13 +34,8 @@ - - - - - @@ -55,7 +49,6 @@ - Common\System\Collections\CompatibleComparer.cs @@ -94,7 +87,7 @@ - + Common\Interop\Windows\secur32\Interop.GetUserNameExW.cs @@ -139,9 +132,6 @@ Common\Interop\Windows\Interop.GetLogicalDrive.cs - - Common\Interop\Windows\kernel32\Interop.GetLogicalProcessorInformationEx.cs - Common\Interop\Windows\kernel32\Interop.GetLongPathName.cs @@ -271,4 +261,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Extensions/src/System/AppDomainUnloadedException.cs b/external/corefx/src/System.Runtime.Extensions/src/System/AppDomainUnloadedException.cs index 7f7bebbe69..0d0d688015 100644 --- a/external/corefx/src/System.Runtime.Extensions/src/System/AppDomainUnloadedException.cs +++ b/external/corefx/src/System.Runtime.Extensions/src/System/AppDomainUnloadedException.cs @@ -7,6 +7,9 @@ using System.Runtime.Serialization; namespace System { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class AppDomainUnloadedException : SystemException { internal const int COR_E_APPDOMAINUNLOADED = unchecked((int)0x80131014); // corresponds to __HResults.COR_E_APPDOMAINUNLOADED in corelib @@ -28,12 +31,8 @@ namespace System HResult = COR_E_APPDOMAINUNLOADED; } - // - //This constructor is required for serialization. - // protected AppDomainUnloadedException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Runtime.Extensions/src/System/CannotUnloadAppDomainException.cs b/external/corefx/src/System.Runtime.Extensions/src/System/CannotUnloadAppDomainException.cs index f78515ecdf..4bd280222e 100644 --- a/external/corefx/src/System.Runtime.Extensions/src/System/CannotUnloadAppDomainException.cs +++ b/external/corefx/src/System.Runtime.Extensions/src/System/CannotUnloadAppDomainException.cs @@ -7,6 +7,9 @@ using System.Runtime.Serialization; namespace System { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class CannotUnloadAppDomainException : SystemException { internal const int COR_E_CANNOTUNLOADAPPDOMAIN = unchecked((int)0x80131015); // corresponds to __HResults.COR_E_CANNOTUNLOADAPPDOMAIN in corelib @@ -28,12 +31,8 @@ namespace System HResult = COR_E_CANNOTUNLOADAPPDOMAIN; } - // - //This constructor is required for serialization. - // protected CannotUnloadAppDomainException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corefx/src/System.Runtime.Extensions/src/System/Collections/ArrayList.cs.REMOVED.git-id b/external/corefx/src/System.Runtime.Extensions/src/System/Collections/ArrayList.cs.REMOVED.git-id index c5366a83c2..26a662edb8 100644 --- a/external/corefx/src/System.Runtime.Extensions/src/System/Collections/ArrayList.cs.REMOVED.git-id +++ b/external/corefx/src/System.Runtime.Extensions/src/System/Collections/ArrayList.cs.REMOVED.git-id @@ -1 +1 @@ -a75a07503254acc305956d1320fe94a031eec954 \ No newline at end of file +7be72dcce0f898d75f33813e20b867c8955add24 \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Extensions/src/System/Collections/Hashtable.cs b/external/corefx/src/System.Runtime.Extensions/src/System/Collections/Hashtable.cs index 7916a38227..996fee0615 100644 --- a/external/corefx/src/System.Runtime.Extensions/src/System/Collections/Hashtable.cs +++ b/external/corefx/src/System.Runtime.Extensions/src/System/Collections/Hashtable.cs @@ -13,7 +13,6 @@ ===========================================================*/ using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Threading; @@ -277,7 +276,6 @@ namespace System.Collections throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_NeedNonNegNum); if (!(loadFactor >= 0.1f && loadFactor <= 1.0f)) throw new ArgumentOutOfRangeException(nameof(loadFactor), SR.Format(SR.ArgumentOutOfRange_HashtableLoadFactor, .1, 1.0)); - Contract.EndContractBlock(); // Based on perf work, .72 is the optimal load factor for this table. _loadFactor = 0.72f * loadFactor; @@ -364,7 +362,6 @@ namespace System.Collections { if (d == null) throw new ArgumentNullException(nameof(d), SR.ArgumentNull_Dictionary); - Contract.EndContractBlock(); IDictionaryEnumerator e = d.GetEnumerator(); while (e.MoveNext()) Add(e.Key, e.Value); @@ -375,7 +372,6 @@ namespace System.Collections { if (d == null) throw new ArgumentNullException(nameof(d), SR.ArgumentNull_Dictionary); - Contract.EndContractBlock(); IDictionaryEnumerator e = d.GetEnumerator(); while (e.MoveNext()) Add(e.Key, e.Value); @@ -498,7 +494,6 @@ namespace System.Collections { throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); } - Contract.EndContractBlock(); uint seed; uint incr; @@ -604,7 +599,7 @@ namespace System.Collections throw new ArgumentOutOfRangeException(nameof(arrayIndex), SR.ArgumentOutOfRange_NeedNonNegNum); if (array.Length - arrayIndex < Count) throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); - Contract.EndContractBlock(); + CopyEntries(array, arrayIndex); } @@ -660,7 +655,6 @@ namespace System.Collections { throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); } - Contract.EndContractBlock(); uint seed; uint incr; @@ -898,7 +892,7 @@ namespace System.Collections { throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); } - Contract.EndContractBlock(); + if (_count >= _loadsize) { expand(); @@ -1041,7 +1035,7 @@ namespace System.Collections { throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); } - Contract.EndContractBlock(); + Debug.Assert(!_isWriterInProgress, "Race condition detected in usages of Hashtable - multiple threads appear to be writing to a Hashtable instance simultaneously! Don't do that - use Hashtable.Synchronized."); uint seed; @@ -1105,7 +1099,6 @@ namespace System.Collections { if (table == null) throw new ArgumentNullException(nameof(table)); - Contract.EndContractBlock(); return new SyncHashtable(table); } @@ -1115,7 +1108,7 @@ namespace System.Collections { throw new ArgumentNullException(nameof(info)); } - Contract.EndContractBlock(); + // This is imperfect - it only works well if all other writes are // also using our synchronized wrapper. But it's still a good idea. lock (SyncRoot) @@ -1286,7 +1279,6 @@ namespace System.Collections throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); if (array.Length - arrayIndex < _hashtable._count) throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); _hashtable.CopyKeys(array, arrayIndex); @@ -1333,7 +1325,6 @@ namespace System.Collections throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); if (array.Length - arrayIndex < _hashtable._count) throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); _hashtable.CopyValues(array, arrayIndex); @@ -1448,7 +1439,6 @@ namespace System.Collections { throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); } - Contract.EndContractBlock(); return _table.ContainsKey(key); } @@ -1642,7 +1632,6 @@ namespace System.Collections { throw new ArgumentNullException(nameof(hashtable)); } - Contract.EndContractBlock(); _hashtable = hashtable; } @@ -1697,7 +1686,6 @@ namespace System.Collections { if (min < 0) throw new ArgumentException(SR.Arg_HTCapacityOverflow); - Contract.EndContractBlock(); for (int i = 0; i < primes.Length; i++) { diff --git a/external/corefx/src/System.Runtime.Extensions/src/System/Context.cs b/external/corefx/src/System.Runtime.Extensions/src/System/Context.cs index e57690f31a..11f942a0dd 100644 --- a/external/corefx/src/System.Runtime.Extensions/src/System/Context.cs +++ b/external/corefx/src/System.Runtime.Extensions/src/System/Context.cs @@ -1,9 +1,7 @@ // 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.Reflection; -using System.Runtime.ExceptionServices; + using System.Runtime.Serialization; namespace System @@ -15,6 +13,9 @@ namespace System } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class ContextMarshalException : SystemException { public ContextMarshalException() : this(SR.Arg_ContextMarshalException, null) @@ -32,7 +33,6 @@ namespace System protected ContextMarshalException(SerializationInfo info, StreamingContext context): base(info, context) { - throw new PlatformNotSupportedException(); } } diff --git a/external/corefx/src/System.Runtime.Extensions/src/System/Environment.Unix.cs b/external/corefx/src/System.Runtime.Extensions/src/System/Environment.Unix.cs index 2ec6483609..a10fc04349 100644 --- a/external/corefx/src/System.Runtime.Extensions/src/System/Environment.Unix.cs +++ b/external/corefx/src/System.Runtime.Extensions/src/System/Environment.Unix.cs @@ -358,11 +358,9 @@ namespace System return num; } - public static int ProcessorCount => (int)Interop.Sys.SysConf(Interop.Sys.SysConfName._SC_NPROCESSORS_ONLN); - public static string SystemDirectory => GetFolderPathCore(SpecialFolder.System, SpecialFolderOption.None); - public static int SystemPageSize => (int)Interop.Sys.SysConf(Interop.Sys.SysConfName._SC_PAGESIZE); + public static int SystemPageSize => CheckedSysConf(Interop.Sys.SysConfName._SC_PAGESIZE); public static unsafe string UserName { @@ -433,5 +431,19 @@ namespace System } public static string UserDomainName => MachineName; + + /// Invoke , throwing if it fails. + private static int CheckedSysConf(Interop.Sys.SysConfName name) + { + long result = Interop.Sys.SysConf(name); + if (result == -1) + { + Interop.ErrorInfo errno = Interop.Sys.GetLastErrorInfo(); + throw errno.Error == Interop.Error.EINVAL ? + new ArgumentOutOfRangeException(nameof(name), name, errno.GetErrorMessage()) : + Interop.GetIOException(errno); + } + return (int)result; + } } } diff --git a/external/corefx/src/System.Runtime.Extensions/src/System/Environment.Windows.cs b/external/corefx/src/System.Runtime.Extensions/src/System/Environment.Windows.cs index 72103c01c4..2caff315a0 100644 --- a/external/corefx/src/System.Runtime.Extensions/src/System/Environment.Windows.cs +++ b/external/corefx/src/System.Runtime.Extensions/src/System/Environment.Windows.cs @@ -68,16 +68,6 @@ namespace System public static string NewLine => "\r\n"; - private static int ProcessorCountFromSystemInfo - { - get - { - var info = default(Interop.Kernel32.SYSTEM_INFO); - Interop.Kernel32.GetSystemInfo(out info); - return info.dwNumberOfProcessors; - } - } - public static int SystemPageSize { get @@ -156,59 +146,6 @@ namespace System Marshal.PtrToStringUni((IntPtr)version.szCSDVersion)); }); - public static int ProcessorCount - { - get - { - // First try GetLogicalProcessorInformationEx, caching the result as desktop/coreclr does. - // If that fails for some reason, fall back to a non-cached result from GetSystemInfo. - // (See SystemNative::GetProcessorCount in coreclr for a comparison.) - int pc = s_processorCountFromGetLogicalProcessorInformationEx.Value; - return pc != 0 ? pc : ProcessorCountFromSystemInfo; - } - } - - private static readonly unsafe Lazy s_processorCountFromGetLogicalProcessorInformationEx = new Lazy(() => - { - // Determine how much size we need for a call to GetLogicalProcessorInformationEx - uint len = 0; - if (!Interop.Kernel32.GetLogicalProcessorInformationEx(Interop.Kernel32.LOGICAL_PROCESSOR_RELATIONSHIP.RelationGroup, IntPtr.Zero, ref len) && - Marshal.GetLastWin32Error() == Interop.Errors.ERROR_INSUFFICIENT_BUFFER) - { - // Allocate that much space - Debug.Assert(len > 0); - var buffer = new byte[len]; - fixed (byte* bufferPtr = buffer) - { - // Call GetLogicalProcessorInformationEx with the allocated buffer - if (Interop.Kernel32.GetLogicalProcessorInformationEx(Interop.Kernel32.LOGICAL_PROCESSOR_RELATIONSHIP.RelationGroup, (IntPtr)bufferPtr, ref len)) - { - // Walk each SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX in the buffer, where the Size of each dictates how - // much space it's consuming. For each group relation, count the number of active processors in each of its group infos. - int processorCount = 0; - byte* ptr = bufferPtr, endPtr = bufferPtr + len; - while (ptr < endPtr) - { - var current = (Interop.Kernel32.SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)ptr; - if (current->Relationship == Interop.Kernel32.LOGICAL_PROCESSOR_RELATIONSHIP.RelationGroup) - { - Interop.Kernel32.PROCESSOR_GROUP_INFO* groupInfo = ¤t->Group.GroupInfo; - int groupCount = current->Group.ActiveGroupCount; - for (int i = 0; i < groupCount; i++) - { - processorCount += (groupInfo + i)->ActiveProcessorCount; - } - } - ptr += current->Size; - } - return processorCount; - } - } - } - - return 0; - }); - public static string SystemDirectory { get diff --git a/external/corefx/src/System.Runtime.Extensions/src/System/Environment.cs b/external/corefx/src/System.Runtime.Extensions/src/System/Environment.cs index a725f2145a..213ec53ee0 100644 --- a/external/corefx/src/System.Runtime.Extensions/src/System/Environment.cs +++ b/external/corefx/src/System.Runtime.Extensions/src/System/Environment.cs @@ -166,6 +166,8 @@ namespace System public static OperatingSystem OSVersion => s_osVersion.Value; + public static int ProcessorCount => EnvironmentAugments.ProcessorCount; + public static string StackTrace { [MethodImpl(MethodImplOptions.NoInlining)] // Prevent inlining from affecting where the stacktrace starts diff --git a/external/corefx/src/System.Runtime.Extensions/src/System/Globalization/Extensions.cs b/external/corefx/src/System.Runtime.Extensions/src/System/Globalization/Extensions.cs index 74717cbd33..bf48d06e06 100644 --- a/external/corefx/src/System.Runtime.Extensions/src/System/Globalization/Extensions.cs +++ b/external/corefx/src/System.Runtime.Extensions/src/System/Globalization/Extensions.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics; -using System.Diagnostics.Contracts; namespace System.Globalization { @@ -75,7 +74,6 @@ namespace System.Globalization { throw new ArgumentNullException(nameof(obj)); } - Contract.EndContractBlock(); // StringSort used in compare operation and not with the hashing return _compareInfo.GetHashCode(obj, _options & (~CompareOptions.StringSort)); diff --git a/external/corefx/src/System.Runtime.Extensions/src/System/IO/BufferedStream.cs b/external/corefx/src/System.Runtime.Extensions/src/System/IO/BufferedStream.cs index 212f821186..90ac28ce24 100644 --- a/external/corefx/src/System.Runtime.Extensions/src/System/IO/BufferedStream.cs +++ b/external/corefx/src/System.Runtime.Extensions/src/System/IO/BufferedStream.cs @@ -6,7 +6,6 @@ using System.Runtime.InteropServices; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -using System.Diagnostics.Contracts; namespace System.IO { @@ -165,7 +164,6 @@ namespace System.IO public override bool CanRead { - [Pure] get { return _stream != null && _stream.CanRead; @@ -174,7 +172,6 @@ namespace System.IO public override bool CanWrite { - [Pure] get { return _stream != null && _stream.CanWrite; @@ -183,7 +180,6 @@ namespace System.IO public override bool CanSeek { - [Pure] get { return _stream != null && _stream.CanSeek; diff --git a/external/corefx/src/System.Runtime.Extensions/src/System/IO/InvalidDataException.cs b/external/corefx/src/System.Runtime.Extensions/src/System/IO/InvalidDataException.cs index 0e2bc46dcb..faa6e298e3 100644 --- a/external/corefx/src/System.Runtime.Extensions/src/System/IO/InvalidDataException.cs +++ b/external/corefx/src/System.Runtime.Extensions/src/System/IO/InvalidDataException.cs @@ -7,6 +7,9 @@ using System.Runtime.Serialization; namespace System.IO { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public sealed class InvalidDataException : SystemException { public InvalidDataException() @@ -23,5 +26,9 @@ namespace System.IO : base(message, innerException) { } + + private InvalidDataException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } } } diff --git a/external/corefx/src/System.Runtime.Extensions/src/System/IO/StringReader.cs b/external/corefx/src/System.Runtime.Extensions/src/System/IO/StringReader.cs index 15a881c262..d3d50adb96 100644 --- a/external/corefx/src/System.Runtime.Extensions/src/System/IO/StringReader.cs +++ b/external/corefx/src/System.Runtime.Extensions/src/System/IO/StringReader.cs @@ -2,7 +2,6 @@ // 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.Diagnostics.Contracts; using System.Threading; using System.Threading.Tasks; @@ -45,7 +44,6 @@ namespace System.IO // changed by this operation. The returned value is -1 if no further // characters are available. // - [Pure] public override int Peek() { if (_s == null) @@ -119,13 +117,13 @@ namespace System.IO return n; } - public override int Read(Span destination) + public override int Read(Span buffer) { if (GetType() != typeof(StringReader)) { // This overload was added affter the Read(char[], ...) overload, and so in case // a derived type may have overridden it, we need to delegate to it, which the base does. - return base.Read(destination); + return base.Read(buffer); } if (_s == null) @@ -136,19 +134,19 @@ namespace System.IO int n = _length - _pos; if (n > 0) { - if (n > destination.Length) + if (n > buffer.Length) { - n = destination.Length; + n = buffer.Length; } - _s.AsReadOnlySpan().Slice(_pos, n).CopyTo(destination); + _s.AsReadOnlySpan().Slice(_pos, n).CopyTo(buffer); _pos += n; } return n; } - public override int ReadBlock(Span destination) => Read(destination); + public override int ReadBlock(Span buffer) => Read(buffer); public override string ReadToEnd() { @@ -242,9 +240,9 @@ namespace System.IO return Task.FromResult(ReadBlock(buffer, index, count)); } - public override ValueTask ReadBlockAsync(Memory destination, CancellationToken cancellationToken = default) => + public override ValueTask ReadBlockAsync(Memory buffer, CancellationToken cancellationToken = default) => cancellationToken.IsCancellationRequested ? new ValueTask(Task.FromCanceled(cancellationToken)) : - new ValueTask(ReadBlock(destination.Span)); + new ValueTask(ReadBlock(buffer.Span)); public override Task ReadAsync(char[] buffer, int index, int count) { @@ -264,9 +262,9 @@ namespace System.IO return Task.FromResult(Read(buffer, index, count)); } - public override ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default) => + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) => cancellationToken.IsCancellationRequested ? new ValueTask(Task.FromCanceled(cancellationToken)) : - new ValueTask(Read(destination.Span)); + new ValueTask(Read(buffer.Span)); #endregion } } diff --git a/external/corefx/src/System.Runtime.Extensions/src/System/IO/StringWriter.cs b/external/corefx/src/System.Runtime.Extensions/src/System/IO/StringWriter.cs index 41603ef090..8d7dffaa9a 100644 --- a/external/corefx/src/System.Runtime.Extensions/src/System/IO/StringWriter.cs +++ b/external/corefx/src/System.Runtime.Extensions/src/System/IO/StringWriter.cs @@ -126,13 +126,13 @@ namespace System.IO _sb.Append(buffer, index, count); } - public override void Write(ReadOnlySpan source) + public override void Write(ReadOnlySpan buffer) { if (GetType() != typeof(StringWriter)) { // This overload was added affter the Write(char[], ...) overload, and so in case // a derived type may have overridden it, we need to delegate to it, which the base does. - base.Write(source); + base.Write(buffer); return; } @@ -141,7 +141,7 @@ namespace System.IO throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed); } - _sb.Append(source); + _sb.Append(buffer); } // Writes a string to the underlying string buffer. If the given string is @@ -160,13 +160,13 @@ namespace System.IO } } - public override void WriteLine(ReadOnlySpan source) + public override void WriteLine(ReadOnlySpan buffer) { if (GetType() != typeof(StringWriter)) { // This overload was added affter the WriteLine(char[], ...) overload, and so in case // a derived type may have overridden it, we need to delegate to it, which the base does. - base.WriteLine(source); + base.WriteLine(buffer); return; } @@ -175,7 +175,7 @@ namespace System.IO throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed); } - _sb.Append(source); + _sb.Append(buffer); WriteLine(); } @@ -199,14 +199,14 @@ namespace System.IO return Task.CompletedTask; } - public override Task WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken = default) + public override Task WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) { if (cancellationToken.IsCancellationRequested) { return Task.FromCanceled(cancellationToken); } - Write(source.Span); + Write(buffer.Span); return Task.CompletedTask; } @@ -228,14 +228,14 @@ namespace System.IO return Task.CompletedTask; } - public override Task WriteLineAsync(ReadOnlyMemory source, CancellationToken cancellationToken = default) + public override Task WriteLineAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) { if (cancellationToken.IsCancellationRequested) { return Task.FromCanceled(cancellationToken); } - WriteLine(source.Span); + WriteLine(buffer.Span); return Task.CompletedTask; } diff --git a/external/corefx/src/System.Runtime.Extensions/src/System/Runtime/Versioning/VersioningHelper.cs b/external/corefx/src/System.Runtime.Extensions/src/System/Runtime/Versioning/VersioningHelper.cs index 5d548c2ae4..3f1750eb96 100644 --- a/external/corefx/src/System.Runtime.Extensions/src/System/Runtime/Versioning/VersioningHelper.cs +++ b/external/corefx/src/System.Runtime.Extensions/src/System/Runtime/Versioning/VersioningHelper.cs @@ -30,7 +30,6 @@ namespace System.Runtime.Versioning return MakeVersionSafeName(name, from, to, null); } - [System.Security.SecuritySafeCritical] // auto-generated [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] public static string MakeVersionSafeName(String name, ResourceScope from, ResourceScope to, Type type) diff --git a/external/corefx/src/System.Runtime.Extensions/tests/AssemblyResolveTests/AssemblyResolveTests.csproj b/external/corefx/src/System.Runtime.Extensions/tests/AssemblyResolveTests/AssemblyResolveTests.csproj index 93ad3ceb18..af864641e8 100644 --- a/external/corefx/src/System.Runtime.Extensions/tests/AssemblyResolveTests/AssemblyResolveTests.csproj +++ b/external/corefx/src/System.Runtime.Extensions/tests/AssemblyResolveTests/AssemblyResolveTests.csproj @@ -5,7 +5,6 @@ {E60AFAE8-2EA7-471C-9E24-52A99453B26B} true - diff --git a/external/corefx/src/System.Runtime.Extensions/tests/Performance/Perf.StreamWriter.cs b/external/corefx/src/System.Runtime.Extensions/tests/Performance/Perf.StreamWriter.cs index 3660f8464b..61fd7ad01d 100644 --- a/external/corefx/src/System.Runtime.Extensions/tests/Performance/Perf.StreamWriter.cs +++ b/external/corefx/src/System.Runtime.Extensions/tests/Performance/Perf.StreamWriter.cs @@ -1,4 +1,4 @@ -// Licensed to the.NET Foundation under one or more agreements. +// 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. diff --git a/external/corefx/src/System.Runtime.Extensions/tests/Performance/System.Runtime.Extensions.Performance.Tests.csproj b/external/corefx/src/System.Runtime.Extensions/tests/Performance/System.Runtime.Extensions.Performance.Tests.csproj index c5aea70f70..7e7d8683cd 100644 --- a/external/corefx/src/System.Runtime.Extensions/tests/Performance/System.Runtime.Extensions.Performance.Tests.csproj +++ b/external/corefx/src/System.Runtime.Extensions/tests/Performance/System.Runtime.Extensions.Performance.Tests.csproj @@ -6,9 +6,8 @@ true {978FA427-F87B-46E2-B276-F5B727C50A01} - - - + + @@ -17,6 +16,7 @@ Common\System\PerfUtils.cs + diff --git a/external/corefx/src/System.Runtime.Extensions/tests/Performance/System/Perf.Convert.cs b/external/corefx/src/System.Runtime.Extensions/tests/Performance/System/Perf.Convert.cs new file mode 100644 index 0000000000..cc24b8ba2e --- /dev/null +++ b/external/corefx/src/System.Runtime.Extensions/tests/Performance/System/Perf.Convert.cs @@ -0,0 +1,122 @@ +// 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 Microsoft.Xunit.Performance; +using Xunit; + +namespace System +{ + public class Perf_Convert + { + private byte[] InitializeBinaryDataCollection(int size) + { + var random = new Random(30000); + byte[] binaryData = new byte[size]; + random.NextBytes(binaryData); + + return binaryData; + } + + [Benchmark(InnerIterationCount = 20000000)] + public void GetTypeCode() + { + int innerIterationCount = (int)Benchmark.InnerIterationCount; + object value = "Hello World!"; + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < innerIterationCount; i++) + { + Convert.GetTypeCode(value); + } + } + } + } + + [Benchmark(InnerIterationCount = 6000000)] + public void ChangeType() + { + int innerIterationCount = (int)Benchmark.InnerIterationCount; + object value = 1000; + Type type = typeof(string); + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < innerIterationCount; i++) + { + Convert.ChangeType(value, type); + } + } + } + } + + [Benchmark(InnerIterationCount = 100000)] + [InlineData(1024, Base64FormattingOptions.InsertLineBreaks)] + [InlineData(1024, Base64FormattingOptions.None)] + public void ToBase64CharArray(int binaryDataSize, Base64FormattingOptions formattingOptions) + { + int innerIterationCount = (int)Benchmark.InnerIterationCount; + byte[] binaryData = InitializeBinaryDataCollection(binaryDataSize); + int arraySize = Convert.ToBase64String(binaryData, formattingOptions).Length; + char[] base64CharArray = new char[arraySize]; + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < innerIterationCount; i++) + { + Convert.ToBase64CharArray(binaryData, 0, binaryDataSize, base64CharArray, 0, formattingOptions); + } + } + } + } + + [Benchmark(InnerIterationCount = 100000)] + [InlineData(Base64FormattingOptions.InsertLineBreaks)] + [InlineData(Base64FormattingOptions.None)] + public void ToBase64String(Base64FormattingOptions formattingOptions) + { + int innerIterationCount = (int)Benchmark.InnerIterationCount; + byte[] binaryData = InitializeBinaryDataCollection(1024); + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < innerIterationCount; i++) + { + Convert.ToBase64String(binaryData, formattingOptions); + } + } + } + } + + [Benchmark(InnerIterationCount = 400000)] + [InlineData("Fri, 27 Feb 2009 03:11:21 GMT")] + [InlineData("Thursday, February 26, 2009")] + [InlineData("February 26, 2009")] + [InlineData("12/31/1999 11:59:59 PM")] + [InlineData("12/31/1999")] + public void ToDateTime_String(string value) + { + int innerIterationCount = (int)Benchmark.InnerIterationCount; + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < innerIterationCount; i++) + { + Convert.ToDateTime(value); + } + } + } + } + } +} diff --git a/external/corefx/src/System.Runtime.Extensions/tests/System.Runtime.Extensions.Tests.csproj b/external/corefx/src/System.Runtime.Extensions/tests/System.Runtime.Extensions.Tests.csproj index a7edd58b60..39599cc9b8 100644 --- a/external/corefx/src/System.Runtime.Extensions/tests/System.Runtime.Extensions.Tests.csproj +++ b/external/corefx/src/System.Runtime.Extensions/tests/System.Runtime.Extensions.Tests.csproj @@ -8,7 +8,6 @@ $(DefineConstants);netcoreapp $(DefineConstants);uapaot - @@ -127,4 +126,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Extensions/tests/System/Environment.GetEnvironmentVariable.cs b/external/corefx/src/System.Runtime.Extensions/tests/System/Environment.GetEnvironmentVariable.cs index d63d8184a1..a3ab49f078 100644 --- a/external/corefx/src/System.Runtime.Extensions/tests/System/Environment.GetEnvironmentVariable.cs +++ b/external/corefx/src/System.Runtime.Extensions/tests/System/Environment.GetEnvironmentVariable.cs @@ -19,7 +19,6 @@ namespace System.Tests AssertExtensions.Throws("variable", () => Environment.GetEnvironmentVariable(null)); AssertExtensions.Throws("variable", () => Environment.SetEnvironmentVariable(null, "test")); AssertExtensions.Throws("variable", () => Environment.SetEnvironmentVariable("", "test")); - AssertExtensions.Throws("value", null, () => Environment.SetEnvironmentVariable("test", new string('s', 65 * 1024))); AssertExtensions.Throws("variable", () => Environment.SetEnvironmentVariable("", "test", EnvironmentVariableTarget.Machine)); AssertExtensions.Throws("variable", () => Environment.SetEnvironmentVariable(null, "test", EnvironmentVariableTarget.User)); diff --git a/external/corefx/src/System.Runtime.Extensions/tests/System/Environment.SetEnvironmentVariable.cs b/external/corefx/src/System.Runtime.Extensions/tests/System/Environment.SetEnvironmentVariable.cs index c4a3f7f419..0c8bb3e7be 100644 --- a/external/corefx/src/System.Runtime.Extensions/tests/System/Environment.SetEnvironmentVariable.cs +++ b/external/corefx/src/System.Runtime.Extensions/tests/System/Environment.SetEnvironmentVariable.cs @@ -3,15 +3,15 @@ // See the LICENSE file in the project root for more information. using System.Collections; +using System.Diagnostics; using System.Runtime.InteropServices; using System.Security; using Xunit; namespace System.Tests { - public class SetEnvironmentVariable + public class SetEnvironmentVariable : RemoteExecutorTestBase { - private const int MAX_VAR_LENGTH_ALLOWED = 32767; private const string NullString = "\u0000"; internal static bool IsSupportedTarget(EnvironmentVariableTarget target) @@ -31,9 +31,116 @@ namespace System.Tests AssertExtensions.Throws("variable", () => Environment.SetEnvironmentVariable(string.Empty, "test")); AssertExtensions.Throws("variable", () => Environment.SetEnvironmentVariable(NullString, "test")); AssertExtensions.Throws("variable", null, () => Environment.SetEnvironmentVariable("Variable=Something", "test")); + } - string varWithLenLongerThanAllowed = new string('c', MAX_VAR_LENGTH_ALLOWED + 1); - AssertExtensions.Throws("variable", null, () => Environment.SetEnvironmentVariable(varWithLenLongerThanAllowed, "test")); + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, ".NET Framework does not have the fix to allow arbitrary length environment variables.")] + public void AllowAnyVariableLengths() + { + // longer than 32767 + string longVar = new string('c', 40000); + string val = "test"; + + try + { + Environment.SetEnvironmentVariable(longVar, val); + Assert.Equal(val, Environment.GetEnvironmentVariable(longVar)); + } + finally + { + Environment.SetEnvironmentVariable(longVar, null); + } + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, ".NET Framework does not have the fix to allow arbitrary length environment variables.")] + public void AllowAnyVariableValueLengths() + { + string var = "Test_SetEnvironmentVariable_AllowAnyVariableValueLengths"; + // longer than 32767 + string longVal = new string('c', 40000); + + try + { + Environment.SetEnvironmentVariable(var, longVal); + Assert.Equal(longVal, Environment.GetEnvironmentVariable(var)); + } + finally + { + Environment.SetEnvironmentVariable(var, null); + } + } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, ".NET Framework does not have the fix to allow arbitrary length environment variables.")] + public void EnvironmentVariableTooLarge_Throws() + { + RemoteInvoke(() => + { + string longVar; + string val = "Test_SetEnvironmentVariable_EnvironmentVariableTooLarge_Throws"; + + try + { + // string slightly less than 2 GiB (1 GiB for x86) so the constructor doesn't fail + var count = (Environment.Is64BitProcess ? 1024 * 1024 * 1024 : 512 * 1024 * 1024) - 64; + longVar = new string('c', count); + } + catch (OutOfMemoryException) + { + // not enough memory to allocate a string at test time + return SuccessExitCode; + } + + try + { + Environment.SetEnvironmentVariable(longVar, val); + // no exception is ok since we cannot construct an argument long enough to break the function + // in that particular environment + } + catch (OutOfMemoryException) + { + // expected + } + return SuccessExitCode; + }).Dispose(); + } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, ".NET Framework does not have the fix to allow arbitrary length environment variables.")] + public void EnvironmentVariableValueTooLarge_Throws() + { + RemoteInvoke(() => + { + string var = "Test_SetEnvironmentVariable_EnvironmentVariableValueTooLarge_Throws"; + string longVal; + + try + { + // string slightly less than 2 GiB (1 GiB for x86) so the constructor doesn't fail + var count = (Environment.Is64BitProcess ? 1024 * 1024 * 1024 : 512 * 1024 * 1024) - 64; + longVal = new string('c', count); + } + catch (OutOfMemoryException) + { + // not enough memory to allocate a string at test time + return SuccessExitCode; + } + + try + { + Environment.SetEnvironmentVariable(var, longVal); + // no exception is ok since we cannot construct an argument long enough to break the function + // in that particular environment + } + catch (OutOfMemoryException) + { + // expected + } + return SuccessExitCode; + }).Dispose(); } private static void ExecuteAgainstTarget( diff --git a/external/corefx/src/System.Runtime.Extensions/tests/System/Environment.UserDomainName.cs b/external/corefx/src/System.Runtime.Extensions/tests/System/Environment.UserDomainName.cs index b879bf5fe1..9cbdbed483 100644 --- a/external/corefx/src/System.Runtime.Extensions/tests/System/Environment.UserDomainName.cs +++ b/external/corefx/src/System.Runtime.Extensions/tests/System/Environment.UserDomainName.cs @@ -1,4 +1,8 @@ -using Xunit; +// 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 Xunit; namespace System.Tests { diff --git a/external/corefx/src/System.Runtime.Extensions/tests/System/Environment.UserName.cs b/external/corefx/src/System.Runtime.Extensions/tests/System/Environment.UserName.cs index bd7f7cc764..3f88f165eb 100644 --- a/external/corefx/src/System.Runtime.Extensions/tests/System/Environment.UserName.cs +++ b/external/corefx/src/System.Runtime.Extensions/tests/System/Environment.UserName.cs @@ -1,4 +1,8 @@ -using Xunit; +// 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 Xunit; namespace System.Tests { diff --git a/external/corefx/src/System.Runtime.Extensions/tests/System/EnvironmentTests.cs b/external/corefx/src/System.Runtime.Extensions/tests/System/EnvironmentTests.cs index 18dc2f937d..3ac079082d 100644 --- a/external/corefx/src/System.Runtime.Extensions/tests/System/EnvironmentTests.cs +++ b/external/corefx/src/System.Runtime.Extensions/tests/System/EnvironmentTests.cs @@ -275,7 +275,7 @@ namespace System.Tests } // Requires recent RS3 builds and needs to run inside AppContainer - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows10Version16251OrGreater), nameof(PlatformDetection.IsInAppContainer))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows10Version1709OrGreater), nameof(PlatformDetection.IsInAppContainer))] [InlineData(Environment.SpecialFolder.LocalApplicationData)] [InlineData(Environment.SpecialFolder.Cookies)] [InlineData(Environment.SpecialFolder.History)] @@ -291,7 +291,7 @@ namespace System.Tests } // Requires recent RS3 builds and needs to run inside AppContainer - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows10Version16251OrGreater), nameof(PlatformDetection.IsInAppContainer))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows10Version1709OrGreater), nameof(PlatformDetection.IsInAppContainer))] [InlineData(Environment.SpecialFolder.ApplicationData)] [InlineData(Environment.SpecialFolder.MyMusic)] [InlineData(Environment.SpecialFolder.MyPictures)] diff --git a/external/corefx/src/System.Runtime.Extensions/tests/System/IO/Path.Combine.cs b/external/corefx/src/System.Runtime.Extensions/tests/System/IO/Path.Combine.cs index a58cdba84f..292c5480d2 100644 --- a/external/corefx/src/System.Runtime.Extensions/tests/System/IO/Path.Combine.cs +++ b/external/corefx/src/System.Runtime.Extensions/tests/System/IO/Path.Combine.cs @@ -128,22 +128,30 @@ namespace System.IO.Tests } [Fact] - public static void PathIsNullWihtoutRootedAfterArgumentNull() + public static void PathIsNullWihtoutRooted() { //any path is null without rooted after (ANE) CommonCasesException(null); } [Fact] - public static void ContainsInvalidCharWithoutRootedAfterArgumentNull() + [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] + public static void ContainsInvalidCharWithoutRooted() { - //any path contains invalid character without rooted after (AE) CommonCasesException("ab\0cd"); } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + public static void ContainsInvalidCharWithoutRooted_Core() + { + Assert.Equal("ab\0cd", Path.Combine("ab\0cd")); + } + [Fact] [PlatformSpecific(TestPlatforms.Windows)] // Tests Windows-specific invalid paths - public static void ContainsInvalidCharWithoutRootedAfterArgumentNull_Windows() + [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] + public static void ContainsInvalidCharWithoutRooted_Windows() { //any path contains invalid character without rooted after (AE) CommonCasesException("ab|cd"); @@ -153,15 +161,35 @@ namespace System.IO.Tests } [Fact] - public static void ContainsInvalidCharWithRootedAfterArgumentNull() + [PlatformSpecific(TestPlatforms.Windows)] // Tests Windows-specific invalid paths + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + public static void ContainsInvalidCharWithoutRooted_Windows_Core() + { + Assert.Equal("ab|cd", Path.Combine("ab|cd")); + Assert.Equal("ab\bcd", Path.Combine("ab\bcd")); + Assert.Equal("ab\0cd", Path.Combine("ab\0cd")); + Assert.Equal("ab\tcd", Path.Combine("ab\tcd")); + } + + [Fact] + [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] + public static void ContainsInvalidCharWithRooted() { //any path contains invalid character with rooted after (AE) CommonCasesException("ab\0cd", s_separator + "abc"); } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + public static void ContainsInvalidCharWithRooted_Core() + { + Assert.Equal(s_separator + "abc", Path.Combine("ab\0cd", s_separator + "abc")); + } + [Fact] [PlatformSpecific(TestPlatforms.Windows)] // Tests Windows-specific invalid paths - public static void ContainsInvalidCharWithRootedAfterArgumentNull_Windows() + [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] + public static void ContainsInvalidCharWithRooted_Windows() { //any path contains invalid character with rooted after (AE) CommonCasesException("ab|cd", s_separator + "abc"); @@ -169,6 +197,16 @@ namespace System.IO.Tests CommonCasesException("ab\tcd", s_separator + "abc"); } + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] // Tests Windows-specific invalid paths + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + public static void ContainsInvalidCharWithRooted_Windows_core() + { + Assert.Equal(s_separator + "abc", Path.Combine("ab|cd", s_separator + "abc")); + Assert.Equal(s_separator + "abc", Path.Combine("ab\bcd", s_separator + "abc")); + Assert.Equal(s_separator + "abc", Path.Combine("ab\tcd", s_separator + "abc")); + } + private static void VerifyException(string[] paths) where T : Exception { Assert.Throws(() => Path.Combine(paths)); diff --git a/external/corefx/src/System.Runtime.Extensions/tests/System/IO/PathTests.cs b/external/corefx/src/System.Runtime.Extensions/tests/System/IO/PathTests.cs index 64148884b0..4f690d7d10 100644 --- a/external/corefx/src/System.Runtime.Extensions/tests/System/IO/PathTests.cs +++ b/external/corefx/src/System.Runtime.Extensions/tests/System/IO/PathTests.cs @@ -44,6 +44,7 @@ namespace System.IO.Tests [Theory, InlineData(" "), InlineData("\r\n")] + [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] public static void GetDirectoryName_SpaceOrControlCharsThrowOnWindows(string path) { Action action = () => Path.GetDirectoryName(path); @@ -58,6 +59,23 @@ namespace System.IO.Tests } } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + public static void GetDirectoryName_SpaceThrowOnWindows_Core() + { + string path = " "; + Action action = () => Path.GetDirectoryName(path); + if (PlatformDetection.IsWindows) + { + AssertExtensions.Throws("path", null, () => Path.GetDirectoryName(path)); + } + else + { + // This is a valid path on Unix + action(); + } + } + [Theory] [InlineData("\u00A0")] // Non-breaking Space [InlineData("\u2028")] // Line separator @@ -326,6 +344,7 @@ namespace System.IO.Tests } [Fact] + [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] public static void GetInvalidPathChars() { Assert.NotNull(Path.GetInvalidPathChars()); @@ -350,6 +369,32 @@ namespace System.IO.Tests }); } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + public static void GetInvalidPathChars_Core() + { + Assert.NotNull(Path.GetInvalidPathChars()); + Assert.NotSame(Path.GetInvalidPathChars(), Path.GetInvalidPathChars()); + Assert.Equal((IEnumerable)Path.GetInvalidPathChars(), Path.GetInvalidPathChars()); + Assert.True(Path.GetInvalidPathChars().Length > 0); + Assert.All(Path.GetInvalidPathChars(), c => + { + string bad = c.ToString(); + Assert.Equal(bad + ".ok", Path.ChangeExtension(bad, "ok")); + Assert.Equal(bad + Path.DirectorySeparatorChar + "ok", Path.Combine(bad, "ok")); + Assert.Equal("ok" + Path.DirectorySeparatorChar + "ok" + Path.DirectorySeparatorChar + bad, Path.Combine("ok", "ok", bad)); + Assert.Equal("ok" + Path.DirectorySeparatorChar + "ok" + Path.DirectorySeparatorChar + bad + Path.DirectorySeparatorChar + "ok", Path.Combine("ok", "ok", bad, "ok")); + Assert.Equal(bad + Path.DirectorySeparatorChar + bad + Path.DirectorySeparatorChar + bad + Path.DirectorySeparatorChar + bad + Path.DirectorySeparatorChar + bad, Path.Combine(bad, bad, bad, bad, bad)); + Assert.Equal("", Path.GetDirectoryName(bad)); + Assert.Equal(string.Empty, Path.GetExtension(bad)); + Assert.Equal(bad, Path.GetFileName(bad)); + Assert.Equal(bad, Path.GetFileNameWithoutExtension(bad)); + AssertExtensions.Throws(c == 124 ? null : "path", null, () => Path.GetFullPath(bad)); + Assert.Equal(string.Empty, Path.GetPathRoot(bad)); + Assert.False(Path.IsPathRooted(bad)); + }); + } + [Fact] public static void GetInvalidFileNameChars() { diff --git a/external/corefx/src/System.Runtime.Extensions/tests/System/UnloadingAndProcessExitTests.netcoreapp.cs b/external/corefx/src/System.Runtime.Extensions/tests/System/UnloadingAndProcessExitTests.netcoreapp.cs index 094a9eeb55..99290d458d 100644 --- a/external/corefx/src/System.Runtime.Extensions/tests/System/UnloadingAndProcessExitTests.netcoreapp.cs +++ b/external/corefx/src/System.Runtime.Extensions/tests/System/UnloadingAndProcessExitTests.netcoreapp.cs @@ -1,4 +1,8 @@ -using System.Diagnostics; +// 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.Diagnostics; using System.IO; using System.Threading; using Xunit; diff --git a/external/corefx/src/System.Runtime.Extensions/tests/TestApp/TestApp.csproj b/external/corefx/src/System.Runtime.Extensions/tests/TestApp/TestApp.csproj index 9b30e2e22e..3e81feb433 100644 --- a/external/corefx/src/System.Runtime.Extensions/tests/TestApp/TestApp.csproj +++ b/external/corefx/src/System.Runtime.Extensions/tests/TestApp/TestApp.csproj @@ -6,7 +6,6 @@ Exe true - diff --git a/external/corefx/src/System.Runtime.Extensions/tests/TestAppOutsideOfTPA/TestAppOutsideOfTPA.csproj b/external/corefx/src/System.Runtime.Extensions/tests/TestAppOutsideOfTPA/TestAppOutsideOfTPA.csproj index 3a416dcd4a..0c9431bb82 100644 --- a/external/corefx/src/System.Runtime.Extensions/tests/TestAppOutsideOfTPA/TestAppOutsideOfTPA.csproj +++ b/external/corefx/src/System.Runtime.Extensions/tests/TestAppOutsideOfTPA/TestAppOutsideOfTPA.csproj @@ -6,7 +6,6 @@ Exe true - diff --git a/external/corefx/src/System.Runtime.Extensions/tests/VoidMainWithExitCodeApp/VoidMainWithExitCodeApp.csproj b/external/corefx/src/System.Runtime.Extensions/tests/VoidMainWithExitCodeApp/VoidMainWithExitCodeApp.csproj index 9b03ecc2ed..ac0ac63aae 100644 --- a/external/corefx/src/System.Runtime.Extensions/tests/VoidMainWithExitCodeApp/VoidMainWithExitCodeApp.csproj +++ b/external/corefx/src/System.Runtime.Extensions/tests/VoidMainWithExitCodeApp/VoidMainWithExitCodeApp.csproj @@ -6,7 +6,6 @@ Exe true - diff --git a/external/corefx/src/System.Runtime.Handles/src/System.Runtime.Handles.csproj b/external/corefx/src/System.Runtime.Handles/src/System.Runtime.Handles.csproj index 6c365a660d..b2678c15cb 100644 --- a/external/corefx/src/System.Runtime.Handles/src/System.Runtime.Handles.csproj +++ b/external/corefx/src/System.Runtime.Handles/src/System.Runtime.Handles.csproj @@ -6,7 +6,6 @@ System.Runtime.Handles true - @@ -17,4 +16,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Handles/tests/System.Runtime.Handles.Tests.csproj b/external/corefx/src/System.Runtime.Handles/tests/System.Runtime.Handles.Tests.csproj index d3a71c4d3d..c562fb5b6d 100644 --- a/external/corefx/src/System.Runtime.Handles/tests/System.Runtime.Handles.Tests.csproj +++ b/external/corefx/src/System.Runtime.Handles/tests/System.Runtime.Handles.Tests.csproj @@ -1,10 +1,9 @@ - + {9C77C3CA-7067-4D45-BDFE-CC62AB5C1ED5} - @@ -14,4 +13,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.InteropServices.RuntimeInformation/ref/System.Runtime.InteropServices.RuntimeInformation.cs b/external/corefx/src/System.Runtime.InteropServices.RuntimeInformation/ref/System.Runtime.InteropServices.RuntimeInformation.cs index f18b400588..4a64dfdd5f 100644 --- a/external/corefx/src/System.Runtime.InteropServices.RuntimeInformation/ref/System.Runtime.InteropServices.RuntimeInformation.cs +++ b/external/corefx/src/System.Runtime.InteropServices.RuntimeInformation/ref/System.Runtime.InteropServices.RuntimeInformation.cs @@ -5,12 +5,18 @@ // Changes to this file must follow the http://aka.ms/api-review process. // ------------------------------------------------------------------------------ - namespace System.Runtime.InteropServices { - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] - public partial struct OSPlatform : System.IEquatable + public enum Architecture { + Arm = 2, + Arm64 = 3, + X64 = 1, + X86 = 0, + } + public readonly partial struct OSPlatform : System.IEquatable + { + private readonly object _dummy; public static System.Runtime.InteropServices.OSPlatform Linux { get { throw null; } } public static System.Runtime.InteropServices.OSPlatform OSX { get { throw null; } } public static System.Runtime.InteropServices.OSPlatform Windows { get { throw null; } } @@ -22,21 +28,12 @@ namespace System.Runtime.InteropServices public static bool operator !=(System.Runtime.InteropServices.OSPlatform left, System.Runtime.InteropServices.OSPlatform right) { throw null; } public override string ToString() { throw null; } } - - public enum Architecture - { - X86, - X64, - Arm, - Arm64 - } - public static partial class RuntimeInformation { - public static Architecture ProcessArchitecture { get { throw null; } } - public static Architecture OSArchitecture { get { throw null; } } - public static string OSDescription { get { throw null; } } public static string FrameworkDescription { get { throw null; } } + public static System.Runtime.InteropServices.Architecture OSArchitecture { get { throw null; } } + public static string OSDescription { get { throw null; } } + public static System.Runtime.InteropServices.Architecture ProcessArchitecture { get { throw null; } } public static bool IsOSPlatform(System.Runtime.InteropServices.OSPlatform osPlatform) { throw null; } } } diff --git a/external/corefx/src/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/OSPlatform.cs b/external/corefx/src/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/OSPlatform.cs index 98642bd86f..fbc36e614d 100644 --- a/external/corefx/src/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/OSPlatform.cs +++ b/external/corefx/src/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/OSPlatform.cs @@ -4,7 +4,7 @@ namespace System.Runtime.InteropServices { - public struct OSPlatform : IEquatable + public readonly struct OSPlatform : IEquatable { private readonly string _osPlatform; @@ -18,7 +18,7 @@ namespace System.Runtime.InteropServices { if (osPlatform == null) throw new ArgumentNullException(nameof(osPlatform)); if (osPlatform.Length == 0) throw new ArgumentException(SR.Argument_EmptyValue, nameof(osPlatform)); - + _osPlatform = osPlatform; } diff --git a/external/corefx/src/System.Runtime.InteropServices.RuntimeInformation/tests/DescriptionNameTests.cs b/external/corefx/src/System.Runtime.InteropServices.RuntimeInformation/tests/DescriptionNameTests.cs index f69335ea31..4432b6850b 100644 --- a/external/corefx/src/System.Runtime.InteropServices.RuntimeInformation/tests/DescriptionNameTests.cs +++ b/external/corefx/src/System.Runtime.InteropServices.RuntimeInformation/tests/DescriptionNameTests.cs @@ -80,6 +80,12 @@ namespace System.Runtime.InteropServices.RuntimeInformationTests Assert.Contains("netbsd", RuntimeInformation.OSDescription, StringComparison.OrdinalIgnoreCase); } + [Fact, PlatformSpecific(TestPlatforms.FreeBSD)] // Checks FreeBSD debug name in RuntimeInformation + public void VerifyFreeBSDDebugName() + { + Assert.Contains("FreeBSD", RuntimeInformation.OSDescription, StringComparison.OrdinalIgnoreCase); + } + [Fact, PlatformSpecific(TestPlatforms.OSX)] // Checks OSX debug name in RuntimeInformation public void VerifyOSXDebugName() { diff --git a/external/corefx/src/System.Runtime.InteropServices.RuntimeInformation/tests/System.Runtime.InteropServices.RuntimeInformation.Tests.csproj b/external/corefx/src/System.Runtime.InteropServices.RuntimeInformation/tests/System.Runtime.InteropServices.RuntimeInformation.Tests.csproj index e669059993..57ab295337 100644 --- a/external/corefx/src/System.Runtime.InteropServices.RuntimeInformation/tests/System.Runtime.InteropServices.RuntimeInformation.Tests.csproj +++ b/external/corefx/src/System.Runtime.InteropServices.RuntimeInformation/tests/System.Runtime.InteropServices.RuntimeInformation.Tests.csproj @@ -5,7 +5,6 @@ Properties {9B4D1DA9-AA4C-428F-9F66-D45C924025A5} - diff --git a/external/corefx/src/System.Runtime.InteropServices.WindowsRuntime/ref/System.Runtime.InteropServices.WindowsRuntime.cs b/external/corefx/src/System.Runtime.InteropServices.WindowsRuntime/ref/System.Runtime.InteropServices.WindowsRuntime.cs index 4bf48052c4..1ec0987603 100644 --- a/external/corefx/src/System.Runtime.InteropServices.WindowsRuntime/ref/System.Runtime.InteropServices.WindowsRuntime.cs +++ b/external/corefx/src/System.Runtime.InteropServices.WindowsRuntime/ref/System.Runtime.InteropServices.WindowsRuntime.cs @@ -14,9 +14,9 @@ namespace System.Runtime.InteropServices.WindowsRuntime public DefaultInterfaceAttribute(global::System.Type defaultInterface) { } public global::System.Type DefaultInterface { get { throw null; } } } - [global::System.Runtime.InteropServices.StructLayoutAttribute(global::System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct EventRegistrationToken { + private int _dummy; public override bool Equals(object obj) { throw null; } public override int GetHashCode() { throw null; } public static bool operator ==(global::System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken left, global::System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken right) { throw null; } @@ -58,19 +58,12 @@ namespace System.Runtime.InteropServices.WindowsRuntime } public static partial class WindowsRuntimeMarshal { - [global::System.Security.SecurityCriticalAttribute] public static void AddEventHandler(global::System.Func addMethod, global::System.Action removeMethod, T handler) { } - [global::System.Security.SecurityCriticalAttribute] public static void FreeHString(global::System.IntPtr ptr) { } - [global::System.Security.SecurityCriticalAttribute] public static global::System.Runtime.InteropServices.WindowsRuntime.IActivationFactory GetActivationFactory(global::System.Type type) { throw null; } - [global::System.Security.SecurityCriticalAttribute] public static string PtrToStringHString(global::System.IntPtr ptr) { throw null; } - [global::System.Security.SecurityCriticalAttribute] public static void RemoveAllEventHandlers(global::System.Action removeMethod) { } - [global::System.Security.SecurityCriticalAttribute] public static void RemoveEventHandler(global::System.Action removeMethod, T handler) { } - [global::System.Security.SecurityCriticalAttribute] public static global::System.IntPtr StringToHString(string s) { throw null; } } [global::System.AttributeUsageAttribute((global::System.AttributeTargets)(2048), Inherited = false, AllowMultiple = false)] diff --git a/external/corefx/src/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/external/corefx/src/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index 3f36a1dd88..bf31dbc987 100644 --- a/external/corefx/src/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/external/corefx/src/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -5,10 +5,11 @@ // Changes to this file must follow the http://aka.ms/api-review process. // ------------------------------------------------------------------------------ -// Types moved down into System.Runtime.Handles +// Types moved down into System.Runtime [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.CriticalHandle))] [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.GCHandle))] [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.GCHandleType))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.InAttribute))] [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.SafeHandle))] [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Reflection.Missing))] @@ -138,9 +139,9 @@ namespace System.Runtime.InteropServices public AutomationProxyAttribute(bool val) { } public bool Value { get { throw null; } } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct ArrayWithOffset { + private object _dummy; public ArrayWithOffset(object array, int offset) { throw null; } public override bool Equals(object obj) { throw null; } public bool Equals(System.Runtime.InteropServices.ArrayWithOffset obj) { throw null; } @@ -414,6 +415,7 @@ namespace System.Runtime.InteropServices } public struct HandleRef { + private object _dummy; public HandleRef(object wrapper, System.IntPtr handle) : this() { } public System.IntPtr Handle { get; } public object Wrapper { get; } @@ -448,11 +450,6 @@ namespace System.Runtime.InteropServices { public ImportedFromTypeLibAttribute(String tlbFile) { } public String Value { get { throw null; } } - } - [System.AttributeUsageAttribute((System.AttributeTargets)(2048), Inherited = false)] - public sealed partial class InAttribute : System.Attribute - { - public InAttribute() { } } [System.AttributeUsageAttribute((System.AttributeTargets)(1024), Inherited = false)] public sealed partial class InterfaceTypeAttribute : System.Attribute @@ -887,7 +884,6 @@ namespace System.Runtime.InteropServices public TypeLibVarFlags Value { get { throw null; } } } [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] - [System.Runtime.InteropServices.ComVisible(true)] public sealed class TypeLibVersionAttribute : Attribute { public TypeLibVersionAttribute(int major, int minor) {} diff --git a/external/corefx/src/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.csproj b/external/corefx/src/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.csproj index e73cea11e8..048a810f26 100644 --- a/external/corefx/src/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.csproj +++ b/external/corefx/src/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.csproj @@ -4,7 +4,7 @@ - 618 + $(NoWarn);618 true {B17014F1-D902-417F-89B0-271204695831} diff --git a/external/corefx/src/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj b/external/corefx/src/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj index 53ade3a076..c1da9fae71 100644 --- a/external/corefx/src/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj +++ b/external/corefx/src/System.Runtime.InteropServices/src/System.Runtime.InteropServices.csproj @@ -9,7 +9,6 @@ true 0436;3001 - @@ -45,7 +44,7 @@ - + @@ -54,4 +53,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.InteropServices/src/System/Runtime/InteropServices/Attributes.cs b/external/corefx/src/System.Runtime.InteropServices/src/System/Runtime/InteropServices/Attributes.cs index 4c52d0ca5b..f58992a90a 100644 --- a/external/corefx/src/System.Runtime.InteropServices/src/System/Runtime/InteropServices/Attributes.cs +++ b/external/corefx/src/System.Runtime.InteropServices/src/System/Runtime/InteropServices/Attributes.cs @@ -228,7 +228,6 @@ namespace System.Runtime.InteropServices } [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] - [ComVisible(true)] public sealed class TypeLibVersionAttribute : Attribute { public TypeLibVersionAttribute(int major, int minor) diff --git a/external/corefx/src/System.Runtime.InteropServices/src/System/Runtime/InteropServices/ComAwareEventInfo.cs b/external/corefx/src/System.Runtime.InteropServices/src/System/Runtime/InteropServices/ComAwareEventInfo.cs index 6e39874f2e..016a1fe553 100644 --- a/external/corefx/src/System.Runtime.InteropServices/src/System/Runtime/InteropServices/ComAwareEventInfo.cs +++ b/external/corefx/src/System.Runtime.InteropServices/src/System/Runtime/InteropServices/ComAwareEventInfo.cs @@ -8,7 +8,6 @@ using System.Security; namespace System.Runtime.InteropServices { - [SecuritySafeCritical] public class ComAwareEventInfo : EventInfo { private EventInfo _innerEventInfo; @@ -18,7 +17,6 @@ namespace System.Runtime.InteropServices _innerEventInfo = type.GetEvent(eventName); } - [SecuritySafeCritical] public override void AddEventHandler(object target, Delegate handler) { if (Marshal.IsComObject(target)) @@ -34,7 +32,6 @@ namespace System.Runtime.InteropServices } } - [SecuritySafeCritical] public override void RemoveEventHandler(object target, Delegate handler) { if (Marshal.IsComObject(target)) diff --git a/external/corefx/src/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/MarshalTests.cs b/external/corefx/src/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/MarshalTests.cs index ebdbfb305a..f8fd5f5c35 100644 --- a/external/corefx/src/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/MarshalTests.cs +++ b/external/corefx/src/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/MarshalTests.cs @@ -346,6 +346,59 @@ namespace System.Runtime.InteropServices { Assert.Throws(() => Marshal.SetComObjectData(null, null, null)); } + + [Theory] + [MemberData(nameof(Encode_TestData))] + public static void TestUTF8Marshalling(string chars, byte[] expected) + { + IntPtr pString = IntPtr.Zero; + try + { + pString = Marshal.StringToCoTaskMemUTF8(chars); + string utf8String = Marshal.PtrToStringUTF8(pString); + Assert.Equal(chars, utf8String); + } + finally + { + if (pString != IntPtr.Zero) + Marshal.ZeroFreeCoTaskMemUTF8(pString); + } + } + + public static System.Collections.Generic.IEnumerable Encode_TestData() + { + yield return new object[] { "FooBA\u0400R", new byte[] { 70, 111, 111, 66, 65, 208, 128, 82 } }; + + yield return new object[] { "\u00C0nima\u0300l", new byte[] { 195, 128, 110, 105, 109, 97, 204, 128, 108 } }; + + yield return new object[] { "Test\uD803\uDD75Test", new byte[] { 84, 101, 115, 116, 240, 144, 181, 181, 84, 101, 115, 116 } }; + + yield return new object[] { "\u0130", new byte[] { 196, 176 } }; + + yield return new object[] { "\uD803\uDD75\uD803\uDD75\uD803\uDD75", new byte[] { 240, 144, 181, 181, 240, 144, 181, 181, 240, 144, 181, 181 } }; + + yield return new object[] { "za\u0306\u01FD\u03B2\uD8FF\uDCFF", new byte[] { 122, 97, 204, 134, 199, 189, 206, 178, 241, 143, 179, 191 } }; + + yield return new object[] { "za\u0306\u01FD\u03B2\uD8FF\uDCFF", new byte[] { 206, 178, 241, 143, 179, 191 } }; + + yield return new object[] { "\u0023\u0025\u03a0\u03a3", new byte[] { 37, 206, 160 } }; + + yield return new object[] { "\u00C5", new byte[] { 0xC3, 0x85 } }; + + yield return new object[] { "\u0065\u0065\u00E1\u0065\u0065\u8000\u00E1\u0065\uD800\uDC00\u8000\u00E1\u0065\u0065\u0065", new byte[] { 0x65, 0x65, 0xC3, 0xA1, 0x65, 0x65, 0xE8, 0x80, 0x80, 0xC3, 0xA1, 0x65, 0xF0, 0x90, 0x80, 0x80, 0xE8, 0x80, 0x80, 0xC3, 0xA1, 0x65, 0x65, 0x65 } }; + + yield return new object[] { "\u00A4\u00D0aR|{AnGe\u00A3\u00A4", new byte[] { 0xC2, 0xA4, 0xC3, 0x90, 0x61, 0x52, 0x7C, 0x7B, 0x41, 0x6E, 0x47, 0x65, 0xC2, 0xA3, 0xC2, 0xA4 } }; + + yield return new object[] { "\uD800\uDC00\uD800\uDC00\uD800\uDC00", new byte[] { 0xF0, 0x90, 0x80, 0x80, 0xF0, 0x90, 0x80, 0x80, 0xF0, 0x90, 0x80, 0x80 } }; + + yield return new object[] { "\uD800\uDC00\u0065\uD800\uDC00", new byte[] { 0xF0, 0x90, 0x80, 0x80, 0x65, 0xF0, 0x90, 0x80, 0x80 } }; + + yield return new object[] { string.Empty, new byte[] { } }; + + // bug fixed in coreclr + //yield return new object[] { null, new byte[] {}}; + } + #endif // netcoreapp [Fact] diff --git a/external/corefx/src/System.Runtime.Intrinsics.X86/System.Runtime.Intrinsics.X86.sln b/external/corefx/src/System.Runtime.Intrinsics.X86/System.Runtime.Intrinsics.X86.sln deleted file mode 100644 index 1278c3d520..0000000000 --- a/external/corefx/src/System.Runtime.Intrinsics.X86/System.Runtime.Intrinsics.X86.sln +++ /dev/null @@ -1,23 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{F8DD26AC-C306-481B-B0DA-E7D4CD4EF6D7}" - ProjectSection(SolutionItems) = preProject - ref\Configurations.props = ref\Configurations.props - ref\System.Runtime.Intrinsics.X86.cs = ref\System.Runtime.Intrinsics.X86.cs - ref\System.Runtime.Intrinsics.X86.csproj = ref\System.Runtime.Intrinsics.X86.csproj - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4F13FF59-6559-4930-B226-5610D85685CD}" - ProjectSection(SolutionItems) = preProject - src\Configurations.props = src\Configurations.props - src\System.Runtime.Intrinsics.X86.csproj = src\System.Runtime.Intrinsics.X86.csproj - EndProjectSection -EndProject -Global - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/external/corefx/src/System.Runtime.Intrinsics.X86/ref/System.Runtime.Intrinsics.X86.cs.REMOVED.git-id b/external/corefx/src/System.Runtime.Intrinsics.X86/ref/System.Runtime.Intrinsics.X86.cs.REMOVED.git-id deleted file mode 100644 index f567078865..0000000000 --- a/external/corefx/src/System.Runtime.Intrinsics.X86/ref/System.Runtime.Intrinsics.X86.cs.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -c9f0274438bb6177311f1ab1eb284484b6a30b57 \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Intrinsics/System.Runtime.Intrinsics.sln b/external/corefx/src/System.Runtime.Intrinsics/System.Runtime.Intrinsics.sln new file mode 100644 index 0000000000..16efecee03 --- /dev/null +++ b/external/corefx/src/System.Runtime.Intrinsics/System.Runtime.Intrinsics.sln @@ -0,0 +1,38 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Runtime.Intrinsics", "src\System.Runtime.Intrinsics.csproj", "{543FBFE5-E9E4-4631-8242-911A707FE818}" + ProjectSection(ProjectDependencies) = postProject + {4074AF8C-8C65-486C-960E-F45DAAB0BE0D} = {4074AF8C-8C65-486C-960E-F45DAAB0BE0D} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Runtime.Intrinsics", "ref\System.Runtime.Intrinsics.csproj", "{4074AF8C-8C65-486C-960E-F45DAAB0BE0D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E107E9C1-E893-4E87-987E-04EF0DCEAEFD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{2E666815-2EDB-464B-9DF6-380BF4789AD4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {543FBFE5-E9E4-4631-8242-911A707FE818}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {543FBFE5-E9E4-4631-8242-911A707FE818}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {543FBFE5-E9E4-4631-8242-911A707FE818}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {543FBFE5-E9E4-4631-8242-911A707FE818}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU + {4074AF8C-8C65-486C-960E-F45DAAB0BE0D}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {4074AF8C-8C65-486C-960E-F45DAAB0BE0D}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {4074AF8C-8C65-486C-960E-F45DAAB0BE0D}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {4074AF8C-8C65-486C-960E-F45DAAB0BE0D}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {543FBFE5-E9E4-4631-8242-911A707FE818} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD} + {4074AF8C-8C65-486C-960E-F45DAAB0BE0D} = {2E666815-2EDB-464B-9DF6-380BF4789AD4} + EndGlobalSection +EndGlobal diff --git a/external/corefx/src/System.Runtime.Intrinsics.X86/dir.props b/external/corefx/src/System.Runtime.Intrinsics/dir.props similarity index 100% rename from external/corefx/src/System.Runtime.Intrinsics.X86/dir.props rename to external/corefx/src/System.Runtime.Intrinsics/dir.props diff --git a/external/corefx/src/System.Runtime.Intrinsics/ref/Configurations.props b/external/corefx/src/System.Runtime.Intrinsics/ref/Configurations.props new file mode 100644 index 0000000000..2845c11c54 --- /dev/null +++ b/external/corefx/src/System.Runtime.Intrinsics/ref/Configurations.props @@ -0,0 +1,8 @@ + + + + + netcoreapp; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.X86.cs.REMOVED.git-id b/external/corefx/src/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.X86.cs.REMOVED.git-id new file mode 100644 index 0000000000..1ca474f97b --- /dev/null +++ b/external/corefx/src/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.X86.cs.REMOVED.git-id @@ -0,0 +1 @@ +0f3f24effb78c6b12c6d718fab9bfbe77ec0e518 \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Intrinsics.X86/ref/System.Runtime.Intrinsics.X86.csproj b/external/corefx/src/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.csproj similarity index 92% rename from external/corefx/src/System.Runtime.Intrinsics.X86/ref/System.Runtime.Intrinsics.X86.csproj rename to external/corefx/src/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.csproj index 50432ac3c5..78a1f4b8fd 100644 --- a/external/corefx/src/System.Runtime.Intrinsics.X86/ref/System.Runtime.Intrinsics.X86.csproj +++ b/external/corefx/src/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.csproj @@ -4,6 +4,7 @@ true false + {4074AF8C-8C65-486C-960E-F45DAAB0BE0D} diff --git a/external/corefx/src/System.Runtime.Intrinsics/src/Configurations.props b/external/corefx/src/System.Runtime.Intrinsics/src/Configurations.props new file mode 100644 index 0000000000..e75400d142 --- /dev/null +++ b/external/corefx/src/System.Runtime.Intrinsics/src/Configurations.props @@ -0,0 +1,9 @@ + + + + + netcoreapp-Windows_NT; + netcoreapp-Unix; + + + diff --git a/external/corefx/src/System.Runtime.Intrinsics.X86/src/System.Runtime.Intrinsics.X86.csproj b/external/corefx/src/System.Runtime.Intrinsics/src/System.Runtime.Intrinsics.csproj similarity index 88% rename from external/corefx/src/System.Runtime.Intrinsics.X86/src/System.Runtime.Intrinsics.X86.csproj rename to external/corefx/src/System.Runtime.Intrinsics/src/System.Runtime.Intrinsics.csproj index 361d1abfa5..548805ab48 100644 --- a/external/corefx/src/System.Runtime.Intrinsics.X86/src/System.Runtime.Intrinsics.X86.csproj +++ b/external/corefx/src/System.Runtime.Intrinsics/src/System.Runtime.Intrinsics.csproj @@ -2,8 +2,9 @@ - System.Runtime.Intrinsics.X86 + System.Runtime.Intrinsics true + {543FBFE5-E9E4-4631-8242-911A707FE818} diff --git a/external/corefx/src/System.Runtime.Numerics/System.Runtime.Numerics.sln b/external/corefx/src/System.Runtime.Numerics/System.Runtime.Numerics.sln index 780e3dd8ff..9649c432d1 100644 --- a/external/corefx/src/System.Runtime.Numerics/System.Runtime.Numerics.sln +++ b/external/corefx/src/System.Runtime.Numerics/System.Runtime.Numerics.sln @@ -35,10 +35,10 @@ Global {28AE24F8-BEF4-4358-B612-ADD9D587C8E1}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {28AE24F8-BEF4-4358-B612-ADD9D587C8E1}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU {28AE24F8-BEF4-4358-B612-ADD9D587C8E1}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU - {3842BE38-1A99-41B2-81B5-186E64077B95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3842BE38-1A99-41B2-81B5-186E64077B95}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3842BE38-1A99-41B2-81B5-186E64077B95}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3842BE38-1A99-41B2-81B5-186E64077B95}.Release|Any CPU.Build.0 = Release|Any CPU + {3842BE38-1A99-41B2-81B5-186E64077B95}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {3842BE38-1A99-41B2-81B5-186E64077B95}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {3842BE38-1A99-41B2-81B5-186E64077B95}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {3842BE38-1A99-41B2-81B5-186E64077B95}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU {D2C99D27-0BEF-4319-ADB3-05CBEBA8F69B}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU {D2C99D27-0BEF-4319-ADB3-05CBEBA8F69B}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {D2C99D27-0BEF-4319-ADB3-05CBEBA8F69B}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU diff --git a/external/corefx/src/System.Runtime.Numerics/ref/System.Runtime.Numerics.cs b/external/corefx/src/System.Runtime.Numerics/ref/System.Runtime.Numerics.cs index d1e87b9a0a..c24590a22d 100644 --- a/external/corefx/src/System.Runtime.Numerics/ref/System.Runtime.Numerics.cs +++ b/external/corefx/src/System.Runtime.Numerics/ref/System.Runtime.Numerics.cs @@ -5,19 +5,18 @@ // Changes to this file must follow the http://aka.ms/api-review process. // ------------------------------------------------------------------------------ - namespace System.Numerics { - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] - public partial struct BigInteger : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable + public readonly partial struct BigInteger : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable { + private readonly object _dummy; [System.CLSCompliantAttribute(false)] public BigInteger(byte[] value) { throw null; } - public BigInteger(System.ReadOnlySpan value) { throw null; } public BigInteger(decimal value) { throw null; } public BigInteger(double value) { throw null; } public BigInteger(int value) { throw null; } public BigInteger(long value) { throw null; } + public BigInteger(System.ReadOnlySpan value, bool isUnsigned=false, bool isBigEndian=false) { throw null; } public BigInteger(float value) { throw null; } [System.CLSCompliantAttribute(false)] public BigInteger(uint value) { throw null; } @@ -35,8 +34,8 @@ namespace System.Numerics public static System.Numerics.BigInteger Add(System.Numerics.BigInteger left, System.Numerics.BigInteger right) { throw null; } public static int Compare(System.Numerics.BigInteger left, System.Numerics.BigInteger right) { throw null; } public int CompareTo(long other) { throw null; } - public int CompareTo(object obj) { throw null; } public int CompareTo(System.Numerics.BigInteger other) { throw null; } + public int CompareTo(object obj) { throw null; } [System.CLSCompliantAttribute(false)] public int CompareTo(ulong other) { throw null; } public static System.Numerics.BigInteger Divide(System.Numerics.BigInteger dividend, System.Numerics.BigInteger divisor) { throw null; } @@ -46,8 +45,8 @@ namespace System.Numerics public override bool Equals(object obj) { throw null; } [System.CLSCompliantAttribute(false)] public bool Equals(ulong other) { throw null; } + public int GetByteCount(bool isUnsigned=false) { throw null; } public override int GetHashCode() { throw null; } - public int GetByteCount() { throw null; } public static System.Numerics.BigInteger GreatestCommonDivisor(System.Numerics.BigInteger left, System.Numerics.BigInteger right) { throw null; } public static double Log(System.Numerics.BigInteger value) { throw null; } public static double Log(System.Numerics.BigInteger value, double baseValue) { throw null; } @@ -70,8 +69,8 @@ namespace System.Numerics [System.CLSCompliantAttribute(false)] public static bool operator ==(ulong left, System.Numerics.BigInteger right) { throw null; } public static System.Numerics.BigInteger operator ^(System.Numerics.BigInteger left, System.Numerics.BigInteger right) { throw null; } - public static explicit operator System.Numerics.BigInteger(decimal value) { throw null; } - public static explicit operator System.Numerics.BigInteger(double value) { throw null; } + public static explicit operator System.Numerics.BigInteger (decimal value) { throw null; } + public static explicit operator System.Numerics.BigInteger (double value) { throw null; } public static explicit operator byte (System.Numerics.BigInteger value) { throw null; } public static explicit operator decimal (System.Numerics.BigInteger value) { throw null; } public static explicit operator double (System.Numerics.BigInteger value) { throw null; } @@ -87,7 +86,7 @@ namespace System.Numerics public static explicit operator uint (System.Numerics.BigInteger value) { throw null; } [System.CLSCompliantAttribute(false)] public static explicit operator ulong (System.Numerics.BigInteger value) { throw null; } - public static explicit operator System.Numerics.BigInteger(float value) { throw null; } + public static explicit operator System.Numerics.BigInteger (float value) { throw null; } public static bool operator >(long left, System.Numerics.BigInteger right) { throw null; } public static bool operator >(System.Numerics.BigInteger left, long right) { throw null; } public static bool operator >(System.Numerics.BigInteger left, System.Numerics.BigInteger right) { throw null; } @@ -102,18 +101,18 @@ namespace System.Numerics public static bool operator >=(System.Numerics.BigInteger left, ulong right) { throw null; } [System.CLSCompliantAttribute(false)] public static bool operator >=(ulong left, System.Numerics.BigInteger right) { throw null; } - public static implicit operator System.Numerics.BigInteger(byte value) { throw null; } - public static implicit operator System.Numerics.BigInteger(short value) { throw null; } - public static implicit operator System.Numerics.BigInteger(int value) { throw null; } - public static implicit operator System.Numerics.BigInteger(long value) { throw null; } + public static implicit operator System.Numerics.BigInteger (byte value) { throw null; } + public static implicit operator System.Numerics.BigInteger (short value) { throw null; } + public static implicit operator System.Numerics.BigInteger (int value) { throw null; } + public static implicit operator System.Numerics.BigInteger (long value) { throw null; } [System.CLSCompliantAttribute(false)] - public static implicit operator System.Numerics.BigInteger(sbyte value) { throw null; } + public static implicit operator System.Numerics.BigInteger (sbyte value) { throw null; } [System.CLSCompliantAttribute(false)] - public static implicit operator System.Numerics.BigInteger(ushort value) { throw null; } + public static implicit operator System.Numerics.BigInteger (ushort value) { throw null; } [System.CLSCompliantAttribute(false)] - public static implicit operator System.Numerics.BigInteger(uint value) { throw null; } + public static implicit operator System.Numerics.BigInteger (uint value) { throw null; } [System.CLSCompliantAttribute(false)] - public static implicit operator System.Numerics.BigInteger(ulong value) { throw null; } + public static implicit operator System.Numerics.BigInteger (ulong value) { throw null; } public static System.Numerics.BigInteger operator ++(System.Numerics.BigInteger value) { throw null; } public static bool operator !=(long left, System.Numerics.BigInteger right) { throw null; } public static bool operator !=(System.Numerics.BigInteger left, long right) { throw null; } @@ -144,27 +143,30 @@ namespace System.Numerics public static System.Numerics.BigInteger operator -(System.Numerics.BigInteger left, System.Numerics.BigInteger right) { throw null; } public static System.Numerics.BigInteger operator -(System.Numerics.BigInteger value) { throw null; } public static System.Numerics.BigInteger operator +(System.Numerics.BigInteger value) { throw null; } + public static System.Numerics.BigInteger Parse(System.ReadOnlySpan value, System.Globalization.NumberStyles style=(System.Globalization.NumberStyles)(7), System.IFormatProvider provider=null) { throw null; } public static System.Numerics.BigInteger Parse(string value) { throw null; } public static System.Numerics.BigInteger Parse(string value, System.Globalization.NumberStyles style) { throw null; } public static System.Numerics.BigInteger Parse(string value, System.Globalization.NumberStyles style, System.IFormatProvider provider) { throw null; } public static System.Numerics.BigInteger Parse(string value, System.IFormatProvider provider) { throw null; } - public static System.Numerics.BigInteger Parse(System.ReadOnlySpan value, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.Integer, System.IFormatProvider provider = null) { throw null; } public static System.Numerics.BigInteger Pow(System.Numerics.BigInteger value, int exponent) { throw null; } public static System.Numerics.BigInteger Remainder(System.Numerics.BigInteger dividend, System.Numerics.BigInteger divisor) { throw null; } public static System.Numerics.BigInteger Subtract(System.Numerics.BigInteger left, System.Numerics.BigInteger right) { throw null; } public byte[] ToByteArray() { throw null; } + public byte[] ToByteArray(bool isUnsigned=false, bool isBigEndian=false) { throw null; } public override string ToString() { throw null; } public string ToString(System.IFormatProvider provider) { throw null; } public string ToString(string format) { throw null; } public string ToString(string format, System.IFormatProvider provider) { throw null; } + public bool TryFormat(System.Span destination, out int charsWritten, System.ReadOnlySpan format=default(System.ReadOnlySpan), System.IFormatProvider provider=null) { throw null; } + public static bool TryParse(System.ReadOnlySpan value, System.Globalization.NumberStyles style, System.IFormatProvider provider, out System.Numerics.BigInteger result) { throw null; } + public static bool TryParse(System.ReadOnlySpan value, out System.Numerics.BigInteger result) { throw null; } public static bool TryParse(string value, System.Globalization.NumberStyles style, System.IFormatProvider provider, out System.Numerics.BigInteger result) { throw null; } public static bool TryParse(string value, out System.Numerics.BigInteger result) { throw null; } - public static bool TryParse(ReadOnlySpan value, out System.Numerics.BigInteger result, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.Integer, System.IFormatProvider provider = null) { throw null; } - public bool TryWriteBytes(System.Span destination, out int bytesWritten) { throw null; } + public bool TryWriteBytes(System.Span destination, out int bytesWritten, bool isUnsigned=false, bool isBigEndian=false) { throw null; } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Complex : System.IEquatable, System.IFormattable { + private int _dummy; public static readonly System.Numerics.Complex ImaginaryOne; public static readonly System.Numerics.Complex One; public static readonly System.Numerics.Complex Zero; @@ -195,22 +197,22 @@ namespace System.Numerics public static System.Numerics.Complex operator +(System.Numerics.Complex left, System.Numerics.Complex right) { throw null; } public static System.Numerics.Complex operator /(System.Numerics.Complex left, System.Numerics.Complex right) { throw null; } public static bool operator ==(System.Numerics.Complex left, System.Numerics.Complex right) { throw null; } - public static explicit operator System.Numerics.Complex(decimal value) { throw null; } - public static explicit operator System.Numerics.Complex(System.Numerics.BigInteger value) { throw null; } - public static implicit operator System.Numerics.Complex(byte value) { throw null; } - public static implicit operator System.Numerics.Complex(double value) { throw null; } - public static implicit operator System.Numerics.Complex(short value) { throw null; } - public static implicit operator System.Numerics.Complex(int value) { throw null; } - public static implicit operator System.Numerics.Complex(long value) { throw null; } + public static explicit operator System.Numerics.Complex (decimal value) { throw null; } + public static explicit operator System.Numerics.Complex (System.Numerics.BigInteger value) { throw null; } + public static implicit operator System.Numerics.Complex (byte value) { throw null; } + public static implicit operator System.Numerics.Complex (double value) { throw null; } + public static implicit operator System.Numerics.Complex (short value) { throw null; } + public static implicit operator System.Numerics.Complex (int value) { throw null; } + public static implicit operator System.Numerics.Complex (long value) { throw null; } [System.CLSCompliantAttribute(false)] - public static implicit operator System.Numerics.Complex(sbyte value) { throw null; } - public static implicit operator System.Numerics.Complex(float value) { throw null; } + public static implicit operator System.Numerics.Complex (sbyte value) { throw null; } + public static implicit operator System.Numerics.Complex (float value) { throw null; } [System.CLSCompliantAttribute(false)] - public static implicit operator System.Numerics.Complex(ushort value) { throw null; } + public static implicit operator System.Numerics.Complex (ushort value) { throw null; } [System.CLSCompliantAttribute(false)] - public static implicit operator System.Numerics.Complex(uint value) { throw null; } + public static implicit operator System.Numerics.Complex (uint value) { throw null; } [System.CLSCompliantAttribute(false)] - public static implicit operator System.Numerics.Complex(ulong value) { throw null; } + public static implicit operator System.Numerics.Complex (ulong value) { throw null; } public static bool operator !=(System.Numerics.Complex left, System.Numerics.Complex right) { throw null; } public static System.Numerics.Complex operator *(System.Numerics.Complex left, System.Numerics.Complex right) { throw null; } public static System.Numerics.Complex operator -(System.Numerics.Complex left, System.Numerics.Complex right) { throw null; } diff --git a/external/corefx/src/System.Runtime.Numerics/src/Resources/Strings.resx b/external/corefx/src/System.Runtime.Numerics/src/Resources/Strings.resx index c4184f129f..a8d6577fec 100644 --- a/external/corefx/src/System.Runtime.Numerics/src/Resources/Strings.resx +++ b/external/corefx/src/System.Runtime.Numerics/src/Resources/Strings.resx @@ -100,4 +100,7 @@ Value was either too large or too small for a Decimal. - \ No newline at end of file + + Negative values do not have an unsigned representation. + + diff --git a/external/corefx/src/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj b/external/corefx/src/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj index 566dc8fd30..f017bb6b57 100644 --- a/external/corefx/src/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj +++ b/external/corefx/src/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj @@ -8,7 +8,6 @@ true {D2C99D27-0BEF-4319-ADB3-05CBEBA8F69B} - @@ -30,11 +29,15 @@ System\Globalization\FormatProvider.Number.cs + + System\Text\ValueStringBuilder.cs + - + + diff --git a/external/corefx/src/System.Runtime.Numerics/src/System/Globalization/FormatProvider.BigInteger.cs b/external/corefx/src/System.Runtime.Numerics/src/System/Globalization/FormatProvider.BigInteger.cs index c8ff75f9e6..0bd585fffa 100644 --- a/external/corefx/src/System.Runtime.Numerics/src/System/Globalization/FormatProvider.BigInteger.cs +++ b/external/corefx/src/System.Runtime.Numerics/src/System/Globalization/FormatProvider.BigInteger.cs @@ -9,29 +9,31 @@ namespace System.Globalization { internal partial class FormatProvider { - [SecurityCritical] - internal static string FormatBigInteger(int precision, int scale, bool sign, string format, NumberFormatInfo numberFormatInfo, char[] digits, int startIndex) + internal static void FormatBigInteger(ref ValueStringBuilder sb, int precision, int scale, bool sign, ReadOnlySpan format, NumberFormatInfo numberFormatInfo, char[] digits, int startIndex) { unsafe { - int maxDigits; - char fmt = Number.ParseFormatSpecifier(format, out maxDigits); - fixed (char* overrideDigits = digits) { - FormatProvider.Number.NumberBuffer numberBuffer = new FormatProvider.Number.NumberBuffer(); + var numberBuffer = new Number.NumberBuffer(); numberBuffer.overrideDigits = overrideDigits + startIndex; numberBuffer.precision = precision; numberBuffer.scale = scale; numberBuffer.sign = sign; + + char fmt = Number.ParseFormatSpecifier(format, out int maxDigits); if (fmt != 0) - return Number.NumberToString(numberBuffer, fmt, maxDigits, numberFormatInfo, isDecimal: false); - return Number.NumberToStringFormat(numberBuffer, format, numberFormatInfo); + { + Number.NumberToString(ref sb, ref numberBuffer, fmt, maxDigits, numberFormatInfo, isDecimal: false); + } + else + { + Number.NumberToStringFormat(ref sb, ref numberBuffer, format, numberFormatInfo); + } } } } - [SecurityCritical] internal static bool TryStringToBigInteger( ReadOnlySpan s, NumberStyles styles, diff --git a/external/corefx/src/System.Runtime.Numerics/src/System/Globalization/FormatProvider.NumberBuffer.cs b/external/corefx/src/System.Runtime.Numerics/src/System/Globalization/FormatProvider.NumberBuffer.cs index d6e9f242b8..4464fb5f29 100644 --- a/external/corefx/src/System.Runtime.Numerics/src/System/Globalization/FormatProvider.NumberBuffer.cs +++ b/external/corefx/src/System.Runtime.Numerics/src/System/Globalization/FormatProvider.NumberBuffer.cs @@ -20,14 +20,12 @@ namespace System.Globalization public char* digits { - [SecurityCritical] get { return overrideDigits; } } - [SecurityCritical] public char* overrideDigits; // Used for BigNumber support which can't be limited to 32 characters. } } diff --git a/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 82ee609fe8..ce4f81e82a 100644 --- a/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Globalization; namespace System.Numerics @@ -12,7 +11,7 @@ namespace System.Numerics #if !MONO [System.Runtime.CompilerServices.TypeForwardedFrom("System.Numerics, Version=4.0.0.0, PublicKeyToken=b77a5c561934e089")] #endif - public struct BigInteger : IFormattable, IComparable, IComparable, IEquatable + public readonly struct BigInteger : IFormattable, IComparable, IComparable, IEquatable { private const int knMaskHighBit = int.MinValue; private const uint kuMaskHighBit = unchecked((uint)int.MinValue); @@ -146,7 +145,6 @@ namespace System.Numerics throw new OverflowException(SR.Overflow_NotANumber); } } - Contract.EndContractBlock(); _sign = 0; _bits = null; @@ -258,21 +256,42 @@ namespace System.Numerics { } - public BigInteger(ReadOnlySpan value) + public BigInteger(ReadOnlySpan value, bool isUnsigned=false, bool isBigEndian=false) { int byteCount = value.Length; bool isNegative; if (byteCount > 0) { - byte lastByte = value[byteCount - 1]; - isNegative = (lastByte & 0x80) != 0; - if (lastByte == 0) + byte mostSignificantByte = isBigEndian ? value[0] : value[byteCount - 1]; + isNegative = (mostSignificantByte & 0x80) != 0 && !isUnsigned; + + if (mostSignificantByte == 0) { // Try to conserve space as much as possible by checking for wasted leading byte[] entries - byteCount -= 2; - while (byteCount >= 0 && value[byteCount] == 0) byteCount--; - byteCount++; + if (isBigEndian) + { + int offset = 1; + + while (offset < byteCount && value[offset] == 0) + { + offset++; + } + + value = value.Slice(offset); + byteCount = value.Length; + } + else + { + byteCount -= 2; + + while (byteCount >= 0 && value[byteCount] == 0) + { + byteCount--; + } + + byteCount++; + } } } else @@ -292,9 +311,20 @@ namespace System.Numerics if (byteCount <= 4) { _sign = isNegative ? unchecked((int)0xffffffff) : 0; - for (int i = byteCount - 1; i >= 0; i--) + + if (isBigEndian) { - _sign = (_sign << 8) | value[i]; + for (int i = 0; i < byteCount; i++) + { + _sign = (_sign << 8) | value[i]; + } + } + else + { + for (int i = byteCount - 1; i >= 0; i--) + { + _sign = (_sign << 8) | value[i]; + } } _bits = null; @@ -317,28 +347,65 @@ namespace System.Numerics int unalignedBytes = byteCount % 4; int dwordCount = byteCount / 4 + (unalignedBytes == 0 ? 0 : 1); uint[] val = new uint[dwordCount]; + int byteCountMinus1 = byteCount - 1; - // Copy all dwords, except but don't do the last one if it's not a full four bytes - int curDword, curByte = 3; - for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++) + // Copy all dwords, except don't do the last one if it's not a full four bytes + int curDword, curByte; + + if (isBigEndian) { - for (int byteInDword = 0; byteInDword < 4; byteInDword++) + curByte = byteCount - sizeof(int); + for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++) { - byte curByteValue = value[curByte]; - val[curDword] = (val[curDword] << 8) | curByteValue; - curByte--; + for (int byteInDword = 0; byteInDword < 4; byteInDword++) + { + byte curByteValue = value[curByte]; + val[curDword] = (val[curDword] << 8) | curByteValue; + curByte++; + } + + curByte -= 8; + } + } + else + { + curByte = sizeof(int) - 1; + for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++) + { + for (int byteInDword = 0; byteInDword < 4; byteInDword++) + { + byte curByteValue = value[curByte]; + val[curDword] = (val[curDword] << 8) | curByteValue; + curByte--; + } + + curByte += 8; } - curByte += 8; } // Copy the last dword specially if it's not aligned if (unalignedBytes != 0) { - if (isNegative) val[dwordCount - 1] = 0xffffffff; - for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--) + if (isNegative) { - byte curByteValue = value[curByte]; - val[curDword] = (val[curDword] << 8) | curByteValue; + val[dwordCount - 1] = 0xffffffff; + } + + if (isBigEndian) + { + for (curByte = 0; curByte < unalignedBytes; curByte++) + { + byte curByteValue = value[curByte]; + val[curDword] = (val[curDword] << 8) | curByteValue; + } + } + else + { + for (curByte = byteCountMinus1; curByte >= byteCount - unalignedBytes; curByte--) + { + byte curByteValue = value[curByte]; + val[curDword] = (val[curDword] << 8) | curByteValue; + } } } @@ -414,7 +481,6 @@ namespace System.Numerics { if (value == null) throw new ArgumentNullException(nameof(value)); - Contract.EndContractBlock(); int len; @@ -624,7 +690,12 @@ namespace System.Numerics return BigNumber.ParseBigInteger(value, style, NumberFormatInfo.GetInstance(provider)); } - public static bool TryParse(ReadOnlySpan value, out BigInteger result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + public static bool TryParse(ReadOnlySpan value, out BigInteger result) + { + return BigNumber.TryParseBigInteger(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(ReadOnlySpan value, NumberStyles style, IFormatProvider provider, out BigInteger result) { return BigNumber.TryParseBigInteger(value, style, NumberFormatInfo.GetInstance(provider), out result); } @@ -832,7 +903,6 @@ namespace System.Numerics { if (exponent.Sign < 0) throw new ArgumentOutOfRangeException(nameof(exponent), SR.ArgumentOutOfRange_MustBeNonNeg); - Contract.EndContractBlock(); value.AssertValid(); exponent.AssertValid(); @@ -866,7 +936,6 @@ namespace System.Numerics { if (exponent < 0) throw new ArgumentOutOfRangeException(nameof(exponent), SR.ArgumentOutOfRange_MustBeNonNeg); - Contract.EndContractBlock(); value.AssertValid(); @@ -1046,10 +1115,48 @@ namespace System.Numerics /// return an array of one byte whose element is 0x00. /// /// - public byte[] ToByteArray() + public byte[] ToByteArray() => ToByteArray(isUnsigned: false, isBigEndian: false); + + /// + /// Returns the value of this BigInteger as a byte array using the fewest number of bytes possible. + /// If the value is zero, returns an array of one byte whose element is 0x00. + /// + /// Whether or not an unsigned encoding is to be used + /// Whether or not to write the bytes in a big-endian byte order + /// + /// + /// If is true and is negative. + /// + /// + /// The integer value 33022 can be exported as four different arrays. + /// + /// + /// + /// + /// (isUnsigned: false, isBigEndian: false) => new byte[] { 0xFE, 0x80, 0x00 } + /// + /// + /// + /// + /// (isUnsigned: false, isBigEndian: true) => new byte[] { 0x00, 0x80, 0xFE } + /// + /// + /// + /// + /// (isUnsigned: true, isBigEndian: false) => new byte[] { 0xFE, 0x80 } + /// + /// + /// + /// + /// (isUnsigned: true, isBigEndian: true) => new byte[] { 0x80, 0xFE } + /// + /// + /// + /// + public byte[] ToByteArray(bool isUnsigned=false, bool isBigEndian=false) { int ignored = 0; - return TryGetBytes(GetBytesMode.AllocateArray, default(Span), ref ignored); + return TryGetBytes(GetBytesMode.AllocateArray, default, isUnsigned, isBigEndian, ref ignored); } /// @@ -1059,42 +1166,61 @@ namespace System.Numerics /// /// The destination span to which the resulting bytes should be written. /// The number of bytes written to . + /// Whether or not an unsigned encoding is to be used + /// Whether or not to write the bytes in a big-endian byte order /// true if the bytes fit in ; false if not all bytes could be written due to lack of space. - public bool TryWriteBytes(Span destination, out int bytesWritten) + /// If is true and is negative. + public bool TryWriteBytes(Span destination, out int bytesWritten, bool isUnsigned=false, bool isBigEndian=false) { bytesWritten = 0; - return TryGetBytes(GetBytesMode.Span, destination, ref bytesWritten) != null; + if (TryGetBytes(GetBytesMode.Span, destination, isUnsigned, isBigEndian, ref bytesWritten) == null) + { + bytesWritten = 0; + return false; + } + return true; } - /// Gets the number of bytes that will be output by and . + internal bool TryWriteOrCountBytes(Span destination, out int bytesWritten, bool isUnsigned = false, bool isBigEndian = false) + { + bytesWritten = 0; + return TryGetBytes(GetBytesMode.Span, destination, isUnsigned, isBigEndian, ref bytesWritten) != null; + } + + /// Gets the number of bytes that will be output by and . /// The number of bytes. - public int GetByteCount() + public int GetByteCount(bool isUnsigned=false) { int count = 0; - TryGetBytes(GetBytesMode.Count, default(Span), ref count); + // Big or Little Endian doesn't matter for the byte count. + const bool IsBigEndian = false; + TryGetBytes(GetBytesMode.Count, default(Span), isUnsigned, IsBigEndian, ref count); return count; } - /// Mode used to enable sharing for multiple purposes. + /// Mode used to enable sharing for multiple purposes. private enum GetBytesMode { AllocateArray, Count, Span } /// Dummy array returned from TryGetBytes to indicate success when in span mode. private static readonly byte[] s_success = Array.Empty(); - /// Shared logic for , , and . + /// Shared logic for , , and . /// Which entry point is being used. /// The destination span, if mode is . + /// True to never write a padding byte, false to write it if the high bit is set. + /// True for big endian byte ordering, false for little endian byte ordering. /// /// If ==, ignored. /// If ==, the number of bytes that would be written. - /// If ==, the number of bytes written to the span. + /// If ==, the number of bytes written to the span or that would be written if it were long enough. /// /// /// If ==, the result array. /// If ==, null. /// If ==, non-null if the span was long enough, null if there wasn't enough room. /// - private byte[] TryGetBytes(GetBytesMode mode, Span destination, ref int bytesWritten) + /// If is true and is negative. + private byte[] TryGetBytes(GetBytesMode mode, Span destination, bool isUnsigned, bool isBigEndian, ref int bytesWritten) { Debug.Assert(mode == GetBytesMode.AllocateArray || mode == GetBytesMode.Count || mode == GetBytesMode.Span, $"Unexpected mode {mode}."); Debug.Assert(mode == GetBytesMode.Span || destination.IsEmpty, $"If we're not in span mode, we shouldn't have been passed a destination."); @@ -1110,16 +1236,21 @@ namespace System.Numerics bytesWritten = 1; return null; default: // case GetBytesMode.Span: + bytesWritten = 1; if (destination.Length != 0) { destination[0] = 0; - bytesWritten = 1; return s_success; } return null; } } + if (isUnsigned && sign < 0) + { + throw new OverflowException(SR.Overflow_Negative_Unsigned); + } + byte highByte; int nonZeroDwordIndex = 0; uint highDword; @@ -1185,7 +1316,7 @@ namespace System.Numerics } // Ensure high bit is 0 if positive, 1 if negative - bool needExtraByte = (msb & 0x80) != (highByte & 0x80); + bool needExtraByte = (msb & 0x80) != (highByte & 0x80) && !isUnsigned; int length = msbIndex + 1 + (needExtraByte ? 1 : 0); if (bits != null) { @@ -1202,16 +1333,18 @@ namespace System.Numerics bytesWritten = length; return null; default: // case GetBytesMode.Span: + bytesWritten = length; if (destination.Length < length) { return null; } - bytesWritten = length; array = s_success; break; } - int curByte = 0; + int curByte = isBigEndian ? length - 1 : 0; + int increment = isBigEndian ? -1 : 1; + if (bits != null) { for (int i = 0; i < bits.Length - 1; i++) @@ -1227,10 +1360,14 @@ namespace System.Numerics } } - destination[curByte++] = unchecked((byte)dword); - destination[curByte++] = unchecked((byte)(dword >> 8)); - destination[curByte++] = unchecked((byte)(dword >> 16)); - destination[curByte++] = unchecked((byte)(dword >> 24)); + destination[curByte] = unchecked((byte)dword); + curByte += increment; + destination[curByte] = unchecked((byte)(dword >> 8)); + curByte += increment; + destination[curByte] = unchecked((byte)(dword >> 16)); + curByte += increment; + destination[curByte] = unchecked((byte)(dword >> 24)); + curByte += increment; } } @@ -1238,21 +1375,29 @@ namespace System.Numerics destination[curByte] = unchecked((byte)highDword); if (msbIndex != 0) { - destination[++curByte] = unchecked((byte)(highDword >> 8)); + curByte += increment; + destination[curByte] = unchecked((byte)(highDword >> 8)); if (msbIndex != 1) { - destination[++curByte] = unchecked((byte)(highDword >> 16)); + curByte += increment; + destination[curByte] = unchecked((byte)(highDword >> 16)); if (msbIndex != 2) { - destination[++curByte] = unchecked((byte)(highDword >> 24)); + curByte += increment; + destination[curByte] = unchecked((byte)(highDword >> 24)); } } } - Debug.Assert((!needExtraByte && curByte == length - 1) || (needExtraByte && curByte == length - 2)); + // Assert we're big endian, or little endian consistency holds. + Debug.Assert(isBigEndian || (!needExtraByte && curByte == length - 1) || (needExtraByte && curByte == length - 2)); + // Assert we're little endian, or big endian consistency holds. + Debug.Assert(!isBigEndian || (!needExtraByte && curByte == 0) || (needExtraByte && curByte == 1)); + if (needExtraByte) { - destination[length - 1] = highByte; + curByte += increment; + destination[curByte] = highByte; } return array; @@ -1325,6 +1470,11 @@ namespace System.Numerics return BigNumber.FormatBigInteger(this, format, NumberFormatInfo.GetInstance(provider)); } + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) // TODO: change format to ReadOnlySpan + { + return BigNumber.TryFormatBigInteger(this, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); + } + private static BigInteger Add(uint[] leftBits, int leftSign, uint[] rightBits, int rightSign) { bool trivialLeft = leftBits == null; diff --git a/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs b/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs index c395078c0b..e47ea385fd 100644 --- a/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs +++ b/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs @@ -35,7 +35,6 @@ namespace System.Numerics return bits; } - [SecuritySafeCritical] public static unsafe uint[] Add(uint[] left, uint[] right) { Debug.Assert(left != null); @@ -57,7 +56,6 @@ namespace System.Numerics return bits; } - [SecuritySafeCritical] private static unsafe void Add(uint* left, int leftLength, uint* right, int rightLength, uint* bits, int bitsLength) @@ -90,7 +88,6 @@ namespace System.Numerics bits[i] = (uint)carry; } - [SecuritySafeCritical] private static unsafe void AddSelf(uint* left, int leftLength, uint* right, int rightLength) { @@ -147,7 +144,6 @@ namespace System.Numerics return bits; } - [SecuritySafeCritical] public static unsafe uint[] Subtract(uint[] left, uint[] right) { Debug.Assert(left != null); @@ -170,7 +166,6 @@ namespace System.Numerics return bits; } - [SecuritySafeCritical] private static unsafe void Subtract(uint* left, int leftLength, uint* right, int rightLength, uint* bits, int bitsLength) @@ -205,7 +200,6 @@ namespace System.Numerics Debug.Assert(carry == 0); } - [SecuritySafeCritical] private static unsafe void SubtractSelf(uint* left, int leftLength, uint* right, int rightLength) { @@ -258,7 +252,6 @@ namespace System.Numerics return 0; } - [SecuritySafeCritical] private static unsafe int Compare(uint* left, int leftLength, uint* right, int rightLength) { diff --git a/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs b/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs index 47764a9ac3..d271843f21 100644 --- a/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs +++ b/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs @@ -44,7 +44,6 @@ namespace System.Numerics Array.Copy(value, 0, _bits, 0, _length); } - [SecuritySafeCritical] public unsafe void MultiplySelf(ref BitsBuffer value, ref BitsBuffer temp) { @@ -73,7 +72,6 @@ namespace System.Numerics Apply(ref temp, _length + value._length); } - [SecuritySafeCritical] public unsafe void SquareSelf(ref BitsBuffer temp) { Debug.Assert(temp._length == 0); @@ -99,7 +97,6 @@ namespace System.Numerics _length = reducer.Reduce(_bits, _length); } - [SecuritySafeCritical] public unsafe void Reduce(uint[] modulus) { Debug.Assert(modulus != null); @@ -120,7 +117,6 @@ namespace System.Numerics } } - [SecuritySafeCritical] public unsafe void Reduce(ref BitsBuffer modulus) { // Executes a modulo operation using the divide operation. diff --git a/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index 661f6b3332..cb3464ac14 100644 --- a/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -72,7 +72,6 @@ namespace System.Numerics return (uint)carry; } - [SecuritySafeCritical] public static unsafe uint[] Divide(uint[] left, uint[] right, out uint[] remainder) { @@ -102,7 +101,6 @@ namespace System.Numerics return bits; } - [SecuritySafeCritical] public static unsafe uint[] Divide(uint[] left, uint[] right) { Debug.Assert(left != null); @@ -128,7 +126,6 @@ namespace System.Numerics return bits; } - [SecuritySafeCritical] public static unsafe uint[] Remainder(uint[] left, uint[] right) { Debug.Assert(left != null); @@ -153,7 +150,6 @@ namespace System.Numerics return localLeft; } - [SecuritySafeCritical] private static unsafe void Divide(uint* left, int leftLength, uint* right, int rightLength, uint* bits, int bitsLength) @@ -240,7 +236,6 @@ namespace System.Numerics } } - [SecuritySafeCritical] private static unsafe uint AddDivisor(uint* left, int leftLength, uint* right, int rightLength) { @@ -262,7 +257,6 @@ namespace System.Numerics return (uint)carry; } - [SecuritySafeCritical] private static unsafe uint SubtractDivisor(uint* left, int leftLength, uint* right, int rightLength, ulong q) diff --git a/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index 869594a4c5..9e7c4ccbd0 100644 --- a/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -15,7 +15,7 @@ namespace System.Numerics // see https://en.wikipedia.org/wiki/Barrett_reduction - internal struct FastReducer + internal readonly struct FastReducer { private readonly uint[] _modulus; private readonly uint[] _mu; @@ -66,7 +66,6 @@ namespace System.Numerics _modulus, _modulus.Length + 1); } - [SecuritySafeCritical] private static unsafe int DivMul(uint[] left, int leftLength, uint[] right, int rightLength, uint[] bits, int k) @@ -111,7 +110,6 @@ namespace System.Numerics return 0; } - [SecuritySafeCritical] private static unsafe int SubMod(uint[] left, int leftLength, uint[] right, int rightLength, uint[] modulus, int k) diff --git a/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs b/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs index ce32f15ff9..7d4b5e28eb 100644 --- a/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs +++ b/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs @@ -9,7 +9,6 @@ namespace System.Numerics { internal static partial class BigIntegerCalculator { - [SecuritySafeCritical] public static unsafe uint[] Square(uint[] value) { Debug.Assert(value != null); @@ -32,7 +31,6 @@ namespace System.Numerics private static int SquareThreshold = 32; private static int AllocationThreshold = 256; - [SecuritySafeCritical] private static unsafe void Square(uint* value, int valueLength, uint* bits, int bitsLength) { @@ -185,7 +183,6 @@ namespace System.Numerics return bits; } - [SecuritySafeCritical] public static unsafe uint[] Multiply(uint[] left, uint[] right) { Debug.Assert(left != null); @@ -210,7 +207,6 @@ namespace System.Numerics // Mutable for unit testing... private static int MultiplyThreshold = 32; - [SecuritySafeCritical] private static unsafe void Multiply(uint* left, int leftLength, uint* right, int rightLength, uint* bits, int bitsLength) @@ -358,7 +354,6 @@ namespace System.Numerics } } - [SecuritySafeCritical] private static unsafe void SubtractCore(uint* left, int leftLength, uint* right, int rightLength, uint* core, int coreLength) diff --git a/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs b/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs index ebf3e34593..8361b744a2 100644 --- a/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs +++ b/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs @@ -271,9 +271,9 @@ // NaNs or Infinities. // +using System.Buffers; using System.Diagnostics; using System.Globalization; -using System.Security; using System.Text; namespace System.Numerics @@ -322,7 +322,6 @@ namespace System.Numerics return true; } - [SecuritySafeCritical] internal static bool TryParseBigInteger(string value, NumberStyles style, NumberFormatInfo info, out BigInteger result) { if (value == null) @@ -331,10 +330,9 @@ namespace System.Numerics return false; } - return TryParseBigInteger(AsReadOnlySpan(value), style, info, out result); + return TryParseBigInteger(value.AsReadOnlySpan(), style, info, out result); } - [SecuritySafeCritical] internal static bool TryParseBigInteger(ReadOnlySpan value, NumberStyles style, NumberFormatInfo info, out BigInteger result) { unsafe @@ -373,18 +371,7 @@ namespace System.Numerics throw new ArgumentNullException(nameof(value)); } - return ParseBigInteger(AsReadOnlySpan(value), style, info); - } - - // TODO #22688: Remove this and replace it with the real AsReadOnlySpan extension - // method from System.Memory once the System.Memory package is marked stable - // and the package validation system allows us to take a dependency on it. - private static unsafe ReadOnlySpan AsReadOnlySpan(string s) - { - fixed (char* c = s) - { - return new ReadOnlySpan(c, s.Length); - } + return ParseBigInteger(value.AsReadOnlySpan(), style, info); } internal static BigInteger ParseBigInteger(ReadOnlySpan value, NumberStyles style, NumberFormatInfo info) @@ -482,10 +469,10 @@ namespace System.Numerics } // This function is consistent with VM\COMNumber.cpp!COMNumber::ParseFormatSpecifier - internal static char ParseFormatSpecifier(string format, out int digits) + internal static char ParseFormatSpecifier(ReadOnlySpan format, out int digits) { digits = -1; - if (string.IsNullOrEmpty(format)) + if (format.Length == 0) { return 'R'; } @@ -516,13 +503,25 @@ namespace System.Numerics return (char)0; // Custom format } - private static string FormatBigIntegerToHexString(BigInteger value, char format, int digits, NumberFormatInfo info) + private static string FormatBigIntegerToHex(bool targetSpan, BigInteger value, char format, int digits, NumberFormatInfo info, Span destination, out int charsWritten, out bool spanSuccess) { - StringBuilder sb = new StringBuilder(); - byte[] bits = value.ToByteArray(); - string fmt = null; - int cur = bits.Length - 1; + Debug.Assert(format == 'x' || format == 'X'); + // Get the bytes that make up the BigInteger. + byte[] arrayToReturnToPool = null; + Span bits = stackalloc byte[64]; // arbitrary threshold + if (!value.TryWriteOrCountBytes(bits, out int bytesWrittenOrNeeded)) + { + bits = arrayToReturnToPool = ArrayPool.Shared.Rent(bytesWrittenOrNeeded); + bool success = value.TryWriteBytes(bits, out bytesWrittenOrNeeded); + Debug.Assert(success); + } + bits = bits.Slice(0, bytesWrittenOrNeeded); + + Span stackSpace = stackalloc char[128]; // each byte is typically two chars + var sb = new ValueStringBuilder(stackSpace); + + int cur = bits.Length - 1; if (cur > -1) { // [FF..F8] drop the high F as the two's complement negative number remains clear @@ -530,58 +529,109 @@ namespace System.Numerics // [07..00] drop the high 0 as the two's complement positive number remains clear bool clearHighF = false; byte head = bits[cur]; + if (head > 0xF7) { head -= 0xF0; clearHighF = true; } + if (head < 0x08 || clearHighF) { // {0xF8-0xFF} print as {8-F} // {0x00-0x07} print as {0-7} - fmt = string.Format(CultureInfo.InvariantCulture, "{0}1", format); - sb.Append(head.ToString(fmt, info)); + sb.Append(head < 10 ? + (char)(head + '0') : + format == 'X' ? (char)((head & 0xF) - 10 + 'A') : (char)((head & 0xF) - 10 + 'a')); cur--; } } + if (cur > -1) { - fmt = string.Format(CultureInfo.InvariantCulture, "{0}2", format); + Span chars = sb.AppendSpan((cur + 1) * 2); + int charsPos = 0; + string hexValues = format == 'x' ? "0123456789abcdef" : "0123456789ABCDEF"; while (cur > -1) { - sb.Append(bits[cur--].ToString(fmt, info)); + byte b = bits[cur--]; + chars[charsPos++] = hexValues[b >> 4]; + chars[charsPos++] = hexValues[b & 0xF]; } } - if (digits > 0 && digits > sb.Length) + + if (digits > sb.Length) { - // Insert leading zeros. User specified "X5" so we create "0ABCD" instead of "ABCD" - sb.Insert(0, (value._sign >= 0 ? ("0") : (format == 'x' ? "f" : "F")), digits - sb.Length); + // Insert leading zeros, e.g. user specified "X5" so we create "0ABCD" instead of "ABCD" + sb.Insert( + 0, + value._sign >= 0 ? '0' : (format == 'x') ? 'f' : 'F', + digits - sb.Length); + } + + if (arrayToReturnToPool != null) + { + ArrayPool.Shared.Return(arrayToReturnToPool); + } + + if (targetSpan) + { + spanSuccess = sb.TryCopyTo(destination, out charsWritten); + return null; + } + else + { + charsWritten = 0; + spanSuccess = false; + return sb.ToString(); } - return sb.ToString(); } - [SecuritySafeCritical] internal static string FormatBigInteger(BigInteger value, string format, NumberFormatInfo info) { - int digits = 0; - char fmt = ParseFormatSpecifier(format, out digits); - if (fmt == 'x' || fmt == 'X') - return FormatBigIntegerToHexString(value, fmt, digits, info); + return FormatBigInteger(targetSpan: false, value, format, format, info, default, out _, out _); + } + + internal static bool TryFormatBigInteger(BigInteger value, ReadOnlySpan format, NumberFormatInfo info, Span destination, out int charsWritten) + { + FormatBigInteger(targetSpan: true, value, null, format, info, destination, out charsWritten, out bool spanSuccess); + return spanSuccess; + } + + private static string FormatBigInteger( + bool targetSpan, BigInteger value, + string formatString, ReadOnlySpan formatSpan, + NumberFormatInfo info, Span destination, out int charsWritten, out bool spanSuccess) + { + Debug.Assert(formatString == null || formatString.Length == formatSpan.Length); + + int digits = 0; + char fmt = ParseFormatSpecifier(formatSpan, out digits); + if (fmt == 'x' || fmt == 'X') + { + return FormatBigIntegerToHex(targetSpan, value, fmt, digits, info, destination, out charsWritten, out spanSuccess); + } - bool decimalFmt = (fmt == 'g' || fmt == 'G' || fmt == 'd' || fmt == 'D' || fmt == 'r' || fmt == 'R'); if (value._bits == null) { if (fmt == 'g' || fmt == 'G' || fmt == 'r' || fmt == 'R') { - if (digits > 0) - format = string.Format(CultureInfo.InvariantCulture, "D{0}", digits.ToString(CultureInfo.InvariantCulture)); - else - format = "D"; + formatSpan = formatString = digits > 0 ? string.Format("D{0}", digits) : "D"; } - return value._sign.ToString(format, info); - } + if (targetSpan) + { + spanSuccess = value._sign.TryFormat(destination, out charsWritten, formatSpan, info); + return null; + } + else + { + charsWritten = 0; + spanSuccess = false; + return value._sign.ToString(formatString, info); + } + } // First convert to base 10^9. const uint kuBase = 1000000000; // 10^9 @@ -624,6 +674,7 @@ namespace System.Numerics } catch (OverflowException e) { throw new FormatException(SR.Format_TooLarge, e); } + bool decimalFmt = (fmt == 'g' || fmt == 'G' || fmt == 'd' || fmt == 'D' || fmt == 'r' || fmt == 'R'); if (decimalFmt) { if (digits > 0 && digits > cchMax) @@ -677,7 +728,21 @@ namespace System.Numerics int precision = 29; int scale = cchMax - ichDst; - return FormatProvider.FormatBigInteger(precision, scale, sign, format, info, rgch, ichDst); + Span stackSpace = stackalloc char[128]; // arbitrary stack cut-off + var sb = new ValueStringBuilder(stackSpace); + FormatProvider.FormatBigInteger(ref sb, precision, scale, sign, formatSpan, info, rgch, ichDst); + + if (targetSpan) + { + spanSuccess = sb.TryCopyTo(destination, out charsWritten); + return null; + } + else + { + charsWritten = 0; + spanSuccess = false; + return sb.ToString(); + } } // Format Round-trip decimal @@ -699,7 +764,26 @@ namespace System.Numerics for (int i = info.NegativeSign.Length - 1; i > -1; i--) rgch[--ichDst] = info.NegativeSign[i]; } - return new string(rgch, ichDst, cchMax - ichDst); + + int resultLength = cchMax - ichDst; + if (!targetSpan) + { + charsWritten = 0; + spanSuccess = false; + return new string(rgch, ichDst, cchMax - ichDst); + } + else if (new ReadOnlySpan(rgch, ichDst, cchMax - ichDst).TryCopyTo(destination)) + { + charsWritten = resultLength; + spanSuccess = true; + return null; + } + else + { + charsWritten = 0; + spanSuccess = false; + return null; + } } } } diff --git a/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs b/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs index e56e01cc77..88958839ee 100644 --- a/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs +++ b/external/corefx/src/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.InteropServices; namespace System.Numerics @@ -23,8 +22,6 @@ namespace System.Numerics public static void GetDoubleParts(double dbl, out int sign, out int exp, out ulong man, out bool fFinite) { - Contract.Ensures(Contract.ValueAtReturn(out sign) == +1 || Contract.ValueAtReturn(out sign) == -1); - DoubleUlong du; du.uu = 0; du.dbl = dbl; diff --git a/external/corefx/src/System.Runtime.Numerics/tests/BigInteger/BigIntegerToStringTests.cs b/external/corefx/src/System.Runtime.Numerics/tests/BigInteger/BigIntegerToStringTests.cs index d0650cedd1..a145d10c58 100644 --- a/external/corefx/src/System.Runtime.Numerics/tests/BigInteger/BigIntegerToStringTests.cs +++ b/external/corefx/src/System.Runtime.Numerics/tests/BigInteger/BigIntegerToStringTests.cs @@ -9,7 +9,7 @@ using Xunit; namespace System.Numerics.Tests { - public class ToStringTest + public partial class ToStringTest { private static bool s_noZeroOut = true; @@ -100,10 +100,14 @@ namespace System.Numerics.Tests RunStandardFormatToStringTests(s_random, "D", CultureInfo.CurrentCulture.NumberFormat.NegativeSign, 0, DecimalFormatter); RunStandardFormatToStringTests(s_random, "d0", CultureInfo.CurrentCulture.NumberFormat.NegativeSign, 0, DecimalFormatter); RunStandardFormatToStringTests(s_random, "D1", CultureInfo.CurrentCulture.NumberFormat.NegativeSign, 1, DecimalFormatter); + RunStandardFormatToStringTests(s_random, "D0000001", CultureInfo.CurrentCulture.NumberFormat.NegativeSign, 1, DecimalFormatter); RunStandardFormatToStringTests(s_random, "d2", CultureInfo.CurrentCulture.NumberFormat.NegativeSign, 2, DecimalFormatter); RunStandardFormatToStringTests(s_random, "D5", CultureInfo.CurrentCulture.NumberFormat.NegativeSign, 5, DecimalFormatter); RunStandardFormatToStringTests(s_random, "d33", CultureInfo.CurrentCulture.NumberFormat.NegativeSign, 33, DecimalFormatter); RunStandardFormatToStringTests(s_random, "D99", CultureInfo.CurrentCulture.NumberFormat.NegativeSign, 99, DecimalFormatter); + RunStandardFormatToStringTests(s_random, "D\0", CultureInfo.CurrentCulture.NumberFormat.NegativeSign, 0, DecimalFormatter); + RunStandardFormatToStringTests(s_random, "D4\0", CultureInfo.CurrentCulture.NumberFormat.NegativeSign, 4, DecimalFormatter); + RunStandardFormatToStringTests(s_random, "D4\0Z", CultureInfo.CurrentCulture.NumberFormat.NegativeSign, 4, DecimalFormatter); // Exponential (note: negative precision means lower case e) RunStandardFormatToStringTests(s_random, "E", CultureInfo.CurrentCulture.NumberFormat.NegativeSign, 6, ExponentialFormatter); @@ -180,7 +184,7 @@ namespace System.Numerics.Tests VerifyToString(test, format, null, true, null); } } - + [Fact] public static void RunRegionSpecificStandardFormatToStringTests() { @@ -1418,37 +1422,42 @@ namespace System.Numerics.Tests Assert.False(expectError, "Expected exception not encountered."); - if (expectedResult != result) - { - Assert.Equal(expectedResult.Length, result.Length); - - int index = expectedResult.LastIndexOf("E", StringComparison.OrdinalIgnoreCase); - Assert.False(index == 0, "'E' found at beginning of expectedResult"); - - bool equal = false; - if (index > 0) - { - var dig1 = (byte)expectedResult[index - 1]; - var dig2 = (byte)result[index - 1]; - - equal |= (dig2 == dig1 - 1 || dig2 == dig1 + 1); - equal |= (dig1 == '9' && dig2 == '0' || dig2 == '9' && dig1 == '0'); - equal |= (index == 1 && (dig1 == '9' && dig2 == '1' || dig2 == '9' && dig1 == '1')); - } - - Assert.True(equal); - } - else - { - Assert.Equal(expectedResult, result); - } + VerifyExpectedStringResult(expectedResult, result); } - catch (Exception e) + catch (FormatException) { - Assert.True(expectError && e.GetType() == typeof(FormatException), "Unexpected Exception:" + e); + Assert.True(expectError); + } + + VerifyTryFormat(test, format, provider, expectError, expectedResult); + } + + private static void VerifyExpectedStringResult(string expectedResult, string result) + { + if (expectedResult != result) + { + Assert.Equal(expectedResult.Length, result.Length); + + int index = expectedResult.LastIndexOf("E", StringComparison.OrdinalIgnoreCase); + Assert.False(index == 0, "'E' found at beginning of expectedResult"); + + bool equal = false; + if (index > 0) + { + var dig1 = (byte)expectedResult[index - 1]; + var dig2 = (byte)result[index - 1]; + + equal |= (dig2 == dig1 - 1 || dig2 == dig1 + 1); + equal |= (dig1 == '9' && dig2 == '0' || dig2 == '9' && dig1 == '0'); + equal |= (index == 1 && (dig1 == '9' && dig2 == '1' || dig2 == '9' && dig1 == '1')); + } + + Assert.True(equal); } } + static partial void VerifyTryFormat(string test, string format, IFormatProvider provider, bool expectError, string expectedResult); + private static String GetDigitSequence(int min, int max, Random random) { String result = String.Empty; diff --git a/external/corefx/src/System.Runtime.Numerics/tests/BigInteger/BigIntegerToStringTests.netcoreapp.cs b/external/corefx/src/System.Runtime.Numerics/tests/BigInteger/BigIntegerToStringTests.netcoreapp.cs new file mode 100644 index 0000000000..449f0e3490 --- /dev/null +++ b/external/corefx/src/System.Runtime.Numerics/tests/BigInteger/BigIntegerToStringTests.netcoreapp.cs @@ -0,0 +1,35 @@ +// 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 Xunit; + +namespace System.Numerics.Tests +{ + public partial class ToStringTest + { + static partial void VerifyTryFormat(string test, string format, IFormatProvider provider, bool expectError, string expectedResult) + { + try + { + BigInteger bi = BigInteger.Parse(test, provider); + + char[] destination = expectedResult != null ? new char[expectedResult.Length] : Array.Empty(); + Assert.True(bi.TryFormat(destination, out int charsWritten, format, provider)); + Assert.False(expectError); + + VerifyExpectedStringResult(expectedResult, new string(destination, 0, charsWritten)); + + if (expectedResult.Length > 0) + { + Assert.False(bi.TryFormat(new char[expectedResult.Length - 1], out charsWritten, format, provider)); + Assert.Equal(0, charsWritten); + } + } + catch (FormatException) + { + Assert.True(expectError); + } + } + } +} diff --git a/external/corefx/src/System.Runtime.Numerics/tests/BigInteger/ToByteArray.cs b/external/corefx/src/System.Runtime.Numerics/tests/BigInteger/ToByteArray.cs index 224e23f27e..0a9c20f34a 100644 --- a/external/corefx/src/System.Runtime.Numerics/tests/BigInteger/ToByteArray.cs +++ b/external/corefx/src/System.Runtime.Numerics/tests/BigInteger/ToByteArray.cs @@ -9,7 +9,9 @@ namespace System.Numerics.Tests { public partial class ExtractBytesMembersTests { - public static IEnumerable FromIntTests_MemberData() + // int => signed little endian byte representation + // the matrix of unsigned / big-endian is built from this. + public static IEnumerable FromIntTests_MemberDataSeed() { yield return new object[] { 0, new byte[] { 0x00 } }; yield return new object[] { 3, new byte[] { 0x03 } }; @@ -36,7 +38,7 @@ namespace System.Numerics.Tests } [Theory] - [MemberData(nameof(FromIntTests_MemberData))] + [MemberData(nameof(FromIntTests_MemberDataSeed))] public void ToByteArray_FromIntTests(int i, byte[] expectedBytes) { BigInteger bi = new BigInteger(i); @@ -46,7 +48,9 @@ namespace System.Numerics.Tests Assert.Equal(bi, bi2); } - public static IEnumerable FromLongTests_MemberData() + // long => signed little endian byte representation + // the matrix of unsigned / big-endian is built from this. + public static IEnumerable FromLongTests_MemberDataSeed() { yield return new object[] { 0x100112233L, new byte[] { 0x33, 0x22, 0x11, 0x00, 0x01 } }; yield return new object[] { 0x300112233L, new byte[] { 0x33, 0x22, 0x11, 0x00, 0x03 } }; @@ -77,7 +81,7 @@ namespace System.Numerics.Tests } [Theory] - [MemberData(nameof(FromLongTests_MemberData))] + [MemberData(nameof(FromLongTests_MemberDataSeed))] public void ToByteArray_FromLongTests(long l, byte[] expectedBytes) { BigInteger bi = new BigInteger(l); @@ -87,8 +91,15 @@ namespace System.Numerics.Tests Assert.Equal(bi, bi2); } - public static IEnumerable FromStringTests_MemberData() + // string => signed big endian byte representation + // the matrix of unsigned / little-endian is built from this. + public static IEnumerable FromStringTests_MemberDataSeed() { + yield return new object[] { "0", new byte[] { 0 } }; + yield return new object[] { "127", new byte[] { 0x7F } }; + yield return new object[] { "128", new byte[] { 0x00, 0x80 } }; + yield return new object[] { "33022", new byte[] { 0x00, 0x80, 0xFE } }; + yield return new object[] { "-32514", new byte[] { 0x80, 0xFE } }; yield return new object[] { "-18374686475376656384", new byte[] { 0xff, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 } }; yield return new object[] { "-18446744069414584320", new byte[] { 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 } }; yield return new object[] { "12364093636075302621581796971036731159910277399901099375861080574506898189967563102391591201943540153492149033597384108294298661111799425262742266671957323486506857011293831406641316917374615487590434666756862524718179898130435474398176187353530443750986128762955255137790631223519091107256083370837751230886741470719554141609004126665185940207631783192780186624919167901565253877397154522582982120840364212333366972597473045167409184762879376102731157263235904816835054250885637739932914015117504485702897712016943555929710070193511128651378003670520306509417025958556935067307123832592173743652726898616663215056178200135054604939862749863002480827458494339065850786495122398411385446068167146399066035389699059499954747263948595492570959736940353095030589033792026029085438901525807032848103038290802923997216273712349905230080849822000411239032166420681788646737098078876258256753058713515144604794792139863186822520361187685685356628044518258989638970651859178568755065879235553215218267011024902142765549091793970027321040036512883269922219608216030987714165823965404563131018846411947569009598716649463301689241563037584803938711917477764863093448004918315253652379776337794243677630540042379049027", new byte[] { 0x00, 0xf0, 0x1d, 0x7b, 0xa2, 0xfd, 0x50, 0xf9, 0x50, 0x07, 0xce, 0x2a, 0xdf, 0xfb, 0x75, 0x70, 0x49, 0x5e, 0x5d, 0xf7, 0x1b, 0x0a, 0x76, 0x1f, 0xe7, 0xe3, 0xc7, 0x0a, 0xc7, 0xee, 0xca, 0x4e, 0xa2, 0xe6, 0xae, 0xe8, 0x1a, 0x2c, 0x3b, 0xa9, 0x12, 0x77, 0xb1, 0x47, 0x3c, 0xb5, 0xab, 0x32, 0x26, 0x18, 0x0d, 0x43, 0x51, 0x6b, 0x11, 0x0c, 0x45, 0xed, 0x52, 0x7d, 0xce, 0xe8, 0x75, 0x6c, 0xe1, 0xda, 0xaf, 0x42, 0x1c, 0xc6, 0x21, 0x23, 0x1f, 0x5d, 0x2d, 0x48, 0xf8, 0x20, 0x67, 0x2f, 0x49, 0x3d, 0x3e, 0x5d, 0xc2, 0x53, 0xc8, 0xe0, 0x3d, 0xe2, 0xdc, 0x51, 0x2f, 0x44, 0xc9, 0x72, 0xb6, 0x04, 0xa0, 0x19, 0x1f, 0x03, 0xb9, 0x0f, 0x57, 0x80, 0x55, 0x41, 0x10, 0x7e, 0x7c, 0x78, 0xdc, 0xbb, 0x82, 0x6e, 0x6a, 0xd9, 0x0e, 0xac, 0x87, 0x0c, 0xfd, 0xc0, 0x79, 0xdf, 0xf5, 0x98, 0xfe, 0x9c, 0x58, 0xa9, 0x63, 0xc2, 0x99, 0x31, 0x09, 0x1c, 0x50, 0xcf, 0x03, 0x4f, 0x96, 0xef, 0x98, 0x56, 0x44, 0xc2, 0x0f, 0x63, 0x20, 0x19, 0x0b, 0x60, 0x28, 0x09, 0xb2, 0x3e, 0xe5, 0xe3, 0x91, 0xc2, 0x98, 0x3d, 0xc0, 0x81, 0x0b, 0x5a, 0xdd, 0x88, 0x6a, 0xc1, 0xff, 0x94, 0x7f, 0xe0, 0x96, 0x6b, 0x67, 0x75, 0x80, 0x53, 0xa1, 0x89, 0x14, 0x19, 0x59, 0xdc, 0x13, 0x67, 0x01, 0x7c, 0x64, 0xfe, 0xeb, 0x7b, 0x8c, 0x97, 0x2d, 0x1a, 0x90, 0x48, 0x4b, 0xea, 0x48, 0xc0, 0x77, 0x23, 0x68, 0x65, 0x19, 0xcf, 0xcf, 0x1c, 0x89, 0x93, 0xe0, 0xd2, 0xfd, 0x81, 0x6a, 0x6e, 0xd8, 0x76, 0xad, 0x65, 0xb5, 0xe0, 0xe1, 0xe2, 0x9c, 0x13, 0x70, 0x0e, 0x79, 0x1c, 0xaa, 0xac, 0x0d, 0x3f, 0x10, 0x4e, 0x47, 0x36, 0xb9, 0x23, 0xd3, 0x6a, 0x50, 0x9a, 0xe2, 0x83, 0x94, 0xf8, 0x1b, 0x41, 0x4c, 0x22, 0x92, 0x58, 0x9f, 0xc8, 0xad, 0x36, 0x51, 0x59, 0xa6, 0xbb, 0x86, 0x4f, 0x9d, 0x3a, 0x9f, 0x1d, 0x0b, 0x2e, 0x7f, 0xf8, 0x83, 0xfd, 0x1c, 0x3b, 0xe1, 0x3d, 0xb2, 0xdc, 0x13, 0x18, 0x71, 0xec, 0x63, 0xf3, 0xfc, 0x41, 0x6f, 0x14, 0xec, 0xcc, 0x7f, 0x28, 0xeb, 0x12, 0xc6, 0x4c, 0xa4, 0x92, 0x7f, 0x7a, 0x65, 0xcf, 0x8f, 0x63, 0x47, 0x3e, 0x4f, 0xeb, 0x03, 0x7b, 0x20, 0xf8, 0x01, 0xa2, 0x9d, 0xe2, 0x5f, 0x85, 0xfb, 0x7b, 0xdf, 0x10, 0x45, 0x2f, 0x6f, 0xb2, 0x6b, 0xff, 0x69, 0xf5, 0x64, 0x8e, 0x18, 0x71, 0x7a, 0x04, 0x31, 0xc3, 0xf8, 0xa0, 0x6c, 0x0f, 0x0a, 0x57, 0x83, 0x83, 0x58, 0xda, 0xaf, 0x99, 0xe6, 0x68, 0x13, 0xe8, 0x15, 0xbc, 0xd7, 0xef, 0xb2, 0x4b, 0x08, 0x18, 0x97, 0xb2, 0x77, 0x0c, 0xb5, 0x96, 0xdb, 0x21, 0x14, 0x39, 0x52, 0x9f, 0x83, 0x47, 0x96, 0xe8, 0x9f, 0x94, 0xe2, 0x73, 0x40, 0x02, 0x7f, 0xbd, 0xb9, 0x65, 0x6a, 0x33, 0x15, 0xb8, 0xc8, 0x9d, 0x12, 0xf9, 0x14, 0x04, 0x77, 0x56, 0xdc, 0x87, 0xd2, 0xca, 0xb2, 0x05, 0x99, 0xad, 0xa6, 0xd2, 0x81, 0x8f, 0x64, 0xcd, 0xe5, 0x70, 0x2c, 0xbf, 0xc0, 0xb6, 0xb7, 0xa8, 0x4a, 0x45, 0x6b, 0x98, 0x8f, 0x5b, 0xb8, 0x87, 0xf5, 0x64, 0xd1, 0x67, 0xd6, 0xdd, 0x76, 0x29, 0xf0, 0x77, 0x45, 0xfc, 0x27, 0xa4, 0x4a, 0xac, 0xaa, 0x1a, 0x90, 0xbe, 0xf8, 0x91, 0x50, 0x30, 0x6d, 0x73, 0x49, 0x3c, 0xab, 0xa4, 0xaf, 0x13, 0xa8, 0xdd, 0x0a, 0xf5, 0xc3, 0x34, 0x98, 0xb4, 0x7f, 0x0b, 0xb4, 0x63, 0x0f, 0xe3, 0x6e, 0xcc, 0xf8, 0x1d, 0xea, 0x34, 0x71, 0x8b, 0x06, 0xa5, 0x4f, 0x81, 0x12, 0xa9, 0xfd, 0xb8, 0x43 } }; @@ -96,17 +107,20 @@ namespace System.Numerics.Tests } [Theory] - public void ToByteArray_FromStringTests(string str, byte[] expectedBigEndianBytes) + [MemberData(nameof(FromStringTests_MemberDataSeed))] + public void ToByteArray_FromStringTests(string str, byte[] expectedBytes) { - byte[] expectedBytes = new byte[expectedBigEndianBytes.Length]; - for (var i = 0; i < expectedBigEndianBytes.Length; i++) - { - expectedBytes[i] = expectedBigEndianBytes[expectedBigEndianBytes.Length - 1 - i]; - } - BigInteger bi = BigInteger.Parse(str); byte[] bytes = bi.ToByteArray(); + + // The expected data is big endian, the export is little endian. + // Since this method already owns the exported data array reverse it instead of the input. + Array.Reverse(bytes); Assert.Equal(expectedBytes, bytes); + + // Now put it back. + Array.Reverse(bytes); + BigInteger bi2 = new BigInteger(bytes); Assert.Equal(bi, bi2); } diff --git a/external/corefx/src/System.Runtime.Numerics/tests/BigInteger/ToByteArray.netcoreapp.cs b/external/corefx/src/System.Runtime.Numerics/tests/BigInteger/ToByteArray.netcoreapp.cs new file mode 100644 index 0000000000..2411ced08b --- /dev/null +++ b/external/corefx/src/System.Runtime.Numerics/tests/BigInteger/ToByteArray.netcoreapp.cs @@ -0,0 +1,120 @@ +// 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.Collections.Generic; +using Xunit; + +namespace System.Numerics.Tests +{ + public partial class ExtractBytesMembersTests + { + public static IEnumerable FromIntTests_MemberData() => + MatrixGenerator(FromIntTests_MemberDataSeed(), false); + + [Theory] + [MemberData(nameof(FromIntTests_MemberData))] + public void ToByteArray_FromIntTests(int i, bool isUnsigned, bool isBigEndian, byte[] expectedBytes) + { + BigInteger bi = new BigInteger(i); + + if (i < 0 && isUnsigned) + { + Assert.Throws(() => bi.ToByteArray(isUnsigned, isBigEndian)); + return; + } + + byte[] bytes = bi.ToByteArray(isUnsigned, isBigEndian); + Assert.Equal(expectedBytes, bytes); + BigInteger bi2 = new BigInteger(bytes, isUnsigned, isBigEndian); + Assert.Equal(bi, bi2); + } + + public static IEnumerable FromLongTests_MemberData() => + MatrixGenerator(FromLongTests_MemberDataSeed(), false); + + [Theory] + [MemberData(nameof(FromLongTests_MemberData))] + public void ToByteArray_FromLongTests(long l, bool isUnsigned, bool isBigEndian, byte[] expectedBytes) + { + BigInteger bi = new BigInteger(l); + + if (l < 0 && isUnsigned) + { + Assert.Throws(() => bi.ToByteArray(isUnsigned, isBigEndian)); + return; + } + + byte[] bytes = bi.ToByteArray(isUnsigned, isBigEndian); + Assert.Equal(expectedBytes, bytes); + BigInteger bi2 = new BigInteger(bytes, isUnsigned, isBigEndian); + Assert.Equal(bi, bi2); + } + + public static IEnumerable FromStringTests_MemberData() => + MatrixGenerator(FromStringTests_MemberDataSeed(), true); + + [Theory] + public void ToByteArray_FromStringTests(string str, bool isUnsigned, bool isBigEndian, byte[] expectedBytes) + { + BigInteger bi = BigInteger.Parse(str); + + if (str[0] == '-' && isUnsigned) + { + Assert.Throws(() => bi.ToByteArray(isUnsigned, isBigEndian)); + return; + } + + byte[] bytes = bi.ToByteArray(isUnsigned, isBigEndian); + Assert.Equal(expectedBytes, bytes); + BigInteger bi2 = new BigInteger(bytes, isUnsigned, isBigEndian); + Assert.Equal(bi, bi2); + } + + private static IEnumerable MatrixGenerator(IEnumerable seedData, bool dataIsBigEndian) + { + foreach (object[] seed in seedData) + { + object value = seed[0]; + byte[] leSignedBytes = (byte[])seed[1]; + byte[] beSignedBytes = (byte[])leSignedBytes.Clone(); + Array.Reverse(beSignedBytes); + + if (dataIsBigEndian) + { + var tmp = leSignedBytes; + leSignedBytes = beSignedBytes; + beSignedBytes = tmp; + } + + // Signed Little Endian + yield return new object[] { value, false, false, leSignedBytes }; + + // Signed Big Endian + yield return new object[] { value, false, true, beSignedBytes }; + + byte[] leUnsignedBytes; + byte[] beUnsignedBytes; + + if (beSignedBytes.Length > 1 && + beSignedBytes[0] == 0) + { + leUnsignedBytes = new Span(leSignedBytes, 0, leSignedBytes.Length - 1).ToArray(); + beUnsignedBytes = new Span(beSignedBytes, 1, beSignedBytes.Length - 1).ToArray(); + } + else + { + // No padding was required, the unsigned data is the same as the signed data. + leUnsignedBytes = leSignedBytes; + beUnsignedBytes = beSignedBytes; + } + + // Unsigned Big Endian + yield return new object[] { value, true, true, beUnsignedBytes }; + + // Unsigned Little Endian + yield return new object[] { value, true, false, leUnsignedBytes }; + } + } + } +} diff --git a/external/corefx/src/System.Runtime.Numerics/tests/BigInteger/TryWriteBytes.cs b/external/corefx/src/System.Runtime.Numerics/tests/BigInteger/TryWriteBytes.cs index 66e37b46b7..f392e0483f 100644 --- a/external/corefx/src/System.Runtime.Numerics/tests/BigInteger/TryWriteBytes.cs +++ b/external/corefx/src/System.Runtime.Numerics/tests/BigInteger/TryWriteBytes.cs @@ -10,29 +10,32 @@ namespace System.Numerics.Tests { [Theory] [MemberData(nameof(FromIntTests_MemberData))] - public void TryWriteBytes_FromIntTests(int i, byte[] expectedBytes) => - ValidateGetByteCountAndTryWriteBytes(new BigInteger(i), expectedBytes); + public void TryWriteBytes_FromIntTests(int i, bool isUnsigned, bool isBigEndian, byte[] expectedBytes) => + ValidateGetByteCountAndTryWriteBytes(new BigInteger(i), isUnsigned, isBigEndian, expectedBytes); [Theory] [MemberData(nameof(FromLongTests_MemberData))] - public void TryWriteBytes_FromLongTests(long l, byte[] expectedBytes) => - ValidateGetByteCountAndTryWriteBytes(new BigInteger(l), expectedBytes); + public void TryWriteBytes_FromLongTests(long l, bool isUnsigned, bool isBigEndian, byte[] expectedBytes) => + ValidateGetByteCountAndTryWriteBytes(new BigInteger(l), isUnsigned, isBigEndian, expectedBytes); [Theory] [MemberData(nameof(FromStringTests_MemberData))] - public void TryWriteBytes_FromStringTests(string str, byte[] expectedBigEndianBytes) - { - byte[] expectedBytes = (byte[])expectedBigEndianBytes.Clone(); - Array.Reverse(expectedBytes); - ValidateGetByteCountAndTryWriteBytes(BigInteger.Parse(str), expectedBytes); - } + public void TryWriteBytes_FromStringTests(string str, bool isUnsigned, bool isBigEndian, byte[] expectedBytes) => + ValidateGetByteCountAndTryWriteBytes(BigInteger.Parse(str), isUnsigned, isBigEndian, expectedBytes); - private void ValidateGetByteCountAndTryWriteBytes(BigInteger bi, byte[] expectedBytes) + private void ValidateGetByteCountAndTryWriteBytes(BigInteger bi, bool isUnsigned, bool isBigEndian, byte[] expectedBytes) { - byte[] bytes = bi.ToByteArray(); + if (bi.Sign < 0 && isUnsigned) + { + Assert.Throws(() => bi.GetByteCount(isUnsigned)); + Assert.Throws(() => bi.TryWriteBytes(Span.Empty, out _, isUnsigned, isBigEndian)); + return; + } + + byte[] bytes = bi.ToByteArray(isUnsigned, isBigEndian); Assert.Equal(expectedBytes, bytes); - int count = bi.GetByteCount(); + int count = bi.GetByteCount(isUnsigned); Assert.Equal(expectedBytes.Length, count); Validate(new byte[expectedBytes.Length]); // make sure it works with a span just long enough @@ -42,11 +45,11 @@ namespace System.Numerics.Tests { // Fails if the span is too small int bytesWritten; - Assert.False(bi.TryWriteBytes(destination.Slice(0, expectedBytes.Length - 1), out bytesWritten)); + Assert.False(bi.TryWriteBytes(destination.Slice(0, expectedBytes.Length - 1), out bytesWritten, isUnsigned, isBigEndian)); Assert.Equal(0, bytesWritten); // Succeeds if the span is sufficiently large - Assert.True(bi.TryWriteBytes(destination, out bytesWritten)); + Assert.True(bi.TryWriteBytes(destination, out bytesWritten, isUnsigned, isBigEndian)); Assert.Equal(expectedBytes.Length, bytesWritten); Assert.Equal(expectedBytes, destination.Slice(0, bytesWritten).ToArray()); } diff --git a/external/corefx/src/System.Runtime.Numerics/tests/BigInteger/parse.netcoreapp.cs b/external/corefx/src/System.Runtime.Numerics/tests/BigInteger/parse.netcoreapp.cs index fc3d966713..31650dd358 100644 --- a/external/corefx/src/System.Runtime.Numerics/tests/BigInteger/parse.netcoreapp.cs +++ b/external/corefx/src/System.Runtime.Numerics/tests/BigInteger/parse.netcoreapp.cs @@ -2,9 +2,7 @@ // 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.Collections.Generic; using System.Globalization; -using System.Threading; using Xunit; namespace System.Numerics.Tests @@ -16,13 +14,26 @@ namespace System.Numerics.Tests if (failureNotExpected) { Eval(BigInteger.Parse(num1.AsReadOnlySpan(), ns), expected); - Assert.True(BigInteger.TryParse(num1.AsReadOnlySpan(), out BigInteger test, ns)); + + Assert.True(BigInteger.TryParse(num1.AsReadOnlySpan(), ns, provider: null, out BigInteger test)); Eval(test, expected); + + if (ns == NumberStyles.Integer) + { + Assert.True(BigInteger.TryParse(num1.AsReadOnlySpan(), out test)); + Eval(test, expected); + } } else { Assert.Throws(() => { BigInteger.Parse(num1.AsReadOnlySpan(), ns); }); - Assert.False(BigInteger.TryParse(num1.AsReadOnlySpan(), out BigInteger test, ns), String.Format("Expected TryParse to fail on {0}", num1)); + + Assert.False(BigInteger.TryParse(num1.AsReadOnlySpan(), ns, provider: null, out BigInteger test)); + + if (ns == NumberStyles.Integer) + { + Assert.False(BigInteger.TryParse(num1.AsReadOnlySpan(), out test)); + } } } @@ -31,13 +42,13 @@ namespace System.Numerics.Tests if (!failureExpected) { Assert.Equal(expected, BigInteger.Parse(num1.AsReadOnlySpan(), provider: nfi)); - Assert.True(BigInteger.TryParse(num1.AsReadOnlySpan(), out BigInteger test, NumberStyles.Any, nfi)); + Assert.True(BigInteger.TryParse(num1.AsReadOnlySpan(), NumberStyles.Any, nfi, out BigInteger test)); Assert.Equal(expected, test); } else { Assert.Throws(() => { BigInteger.Parse(num1.AsReadOnlySpan(), provider: nfi); }); - Assert.False(BigInteger.TryParse(num1.AsReadOnlySpan(), out BigInteger test, NumberStyles.Any, nfi), String.Format("Expected TryParse to fail on {0}", num1)); + Assert.False(BigInteger.TryParse(num1.AsReadOnlySpan(), NumberStyles.Any, nfi, out BigInteger test), String.Format("Expected TryParse to fail on {0}", num1)); } } @@ -46,13 +57,13 @@ namespace System.Numerics.Tests if (!failureExpected) { Assert.Equal(expected, BigInteger.Parse(num1.AsReadOnlySpan(), ns, nfi)); - Assert.True(BigInteger.TryParse(num1.AsReadOnlySpan(), out BigInteger test, NumberStyles.Any, nfi)); + Assert.True(BigInteger.TryParse(num1.AsReadOnlySpan(), NumberStyles.Any, nfi, out BigInteger test)); Assert.Equal(expected, test); } else { Assert.Throws(() => { BigInteger.Parse(num1.AsReadOnlySpan(), ns, nfi); }); - Assert.False(BigInteger.TryParse(num1.AsReadOnlySpan(), out BigInteger test, ns, nfi), String.Format("Expected TryParse to fail on {0}", num1)); + Assert.False(BigInteger.TryParse(num1.AsReadOnlySpan(), ns, nfi, out BigInteger test), String.Format("Expected TryParse to fail on {0}", num1)); } } } diff --git a/external/corefx/src/System.Runtime.Numerics/tests/Performance/Perf.BigInteger.netcoreapp.cs b/external/corefx/src/System.Runtime.Numerics/tests/Performance/Perf.BigInteger.netcoreapp.cs index 20d70c9bd6..de19886705 100644 --- a/external/corefx/src/System.Runtime.Numerics/tests/Performance/Perf.BigInteger.netcoreapp.cs +++ b/external/corefx/src/System.Runtime.Numerics/tests/Performance/Perf.BigInteger.netcoreapp.cs @@ -28,6 +28,24 @@ namespace System.Numerics.Tests } } + [Benchmark] + [MemberData(nameof(NumberStrings))] + public void Ctor_ByteSpan_BigEndian(string numberString) + { + Span input = BigInteger.Parse(numberString).ToByteArray(isBigEndian: true); + + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < 1000000; i++) + { + var bi = new BigInteger(input, isBigEndian: true); + } + } + } + } + [Benchmark] [MemberData(nameof(NumberStrings))] public void TryWriteBytes(string numberString) @@ -45,5 +63,23 @@ namespace System.Numerics.Tests } } } + + [Benchmark] + [MemberData(nameof(NumberStrings))] + public void TryWriteBytes_BigEndian(string numberString) + { + BigInteger bi = BigInteger.Parse(numberString); + Span destination = new byte[bi.GetByteCount()]; + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < 1000000; i++) + { + bi.TryWriteBytes(destination, out int bytesWritten, isBigEndian: true); + } + } + } + } } } diff --git a/external/corefx/src/System.Runtime.Numerics/tests/Performance/System.Runtime.Numerics.Performance.Tests.csproj b/external/corefx/src/System.Runtime.Numerics/tests/Performance/System.Runtime.Numerics.Performance.Tests.csproj index ed5094c1be..99e80c7c38 100644 --- a/external/corefx/src/System.Runtime.Numerics/tests/Performance/System.Runtime.Numerics.Performance.Tests.csproj +++ b/external/corefx/src/System.Runtime.Numerics/tests/Performance/System.Runtime.Numerics.Performance.Tests.csproj @@ -6,9 +6,8 @@ {3842BE38-1A99-41B2-81B5-186E64077B95} true - - - + + diff --git a/external/corefx/src/System.Runtime.Numerics/tests/System.Runtime.Numerics.Tests.csproj b/external/corefx/src/System.Runtime.Numerics/tests/System.Runtime.Numerics.Tests.csproj index f9e1bf76ef..12e983d3d8 100644 --- a/external/corefx/src/System.Runtime.Numerics/tests/System.Runtime.Numerics.Tests.csproj +++ b/external/corefx/src/System.Runtime.Numerics/tests/System.Runtime.Numerics.Tests.csproj @@ -4,7 +4,6 @@ {28AE24F8-BEF4-4358-B612-ADD9D587C8E1} - @@ -12,6 +11,7 @@ + @@ -56,8 +56,9 @@ + - \ No newline at end of file + diff --git a/external/corefx/src/System.Runtime.Serialization.Formatters/src/System.Runtime.Serialization.Formatters.csproj b/external/corefx/src/System.Runtime.Serialization.Formatters/src/System.Runtime.Serialization.Formatters.csproj index 58ac65d8c1..7752ff9879 100644 --- a/external/corefx/src/System.Runtime.Serialization.Formatters/src/System.Runtime.Serialization.Formatters.csproj +++ b/external/corefx/src/System.Runtime.Serialization.Formatters/src/System.Runtime.Serialization.Formatters.csproj @@ -6,7 +6,6 @@ System.Runtime.Serialization.Formatters System.Runtime.Serialization.Formatters - diff --git a/external/corefx/src/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatter.cs b/external/corefx/src/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatter.cs index ad1e0d1336..6c1eb10416 100644 --- a/external/corefx/src/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatter.cs +++ b/external/corefx/src/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatter.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.IO; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Runtime.InteropServices; @@ -10,7 +11,7 @@ namespace System.Runtime.Serialization.Formatters.Binary { public sealed class BinaryFormatter : IFormatter { - private static readonly Dictionary s_typeNameCache = new Dictionary(); + private static readonly ConcurrentDictionary s_typeNameCache = new ConcurrentDictionary(); internal ISurrogateSelector _surrogates; internal StreamingContext _context; @@ -88,21 +89,11 @@ namespace System.Runtime.Serialization.Formatters.Binary _crossAppDomainArray = sow._crossAppDomainArray; } - - internal static TypeInformation GetTypeInformation(Type type) - { - lock (s_typeNameCache) + internal static TypeInformation GetTypeInformation(Type type) => + s_typeNameCache.GetOrAdd(type, t => { - TypeInformation typeInformation; - if (!s_typeNameCache.TryGetValue(type, out typeInformation)) - { - bool hasTypeForwardedFrom; - string assemblyName = FormatterServices.GetClrAssemblyName(type, out hasTypeForwardedFrom); - typeInformation = new TypeInformation(FormatterServices.GetClrTypeFullName(type), assemblyName, hasTypeForwardedFrom); - s_typeNameCache.Add(type, typeInformation); - } - return typeInformation; - } - } + string assemblyName = FormatterServices.GetClrAssemblyName(t, out bool hasTypeForwardedFrom); + return new TypeInformation(FormatterServices.GetClrTypeFullName(t), assemblyName, hasTypeForwardedFrom); + }); } } diff --git a/external/corefx/src/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTestData.cs.REMOVED.git-id b/external/corefx/src/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTestData.cs.REMOVED.git-id index 8b95c10c91..f88fa67d1c 100644 --- a/external/corefx/src/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTestData.cs.REMOVED.git-id +++ b/external/corefx/src/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTestData.cs.REMOVED.git-id @@ -1 +1 @@ -54c78feee065866d1fc7b662d89a2b7e6c479830 \ No newline at end of file +9d15e2597a82a25f5421e6b8c3b5ef37c7a6276b \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs b/external/corefx/src/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs index 690071e3d3..0caa876e40 100644 --- a/external/corefx/src/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs +++ b/external/corefx/src/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.ComponentModel; +using System.Data.SqlClient; using System.Diagnostics; using System.IO; using System.Linq; @@ -27,7 +29,7 @@ namespace System.Runtime.Serialization.Formatters.Tests Assert.NotSame(obj, clone); } - CheckForAnyEquals(obj, clone); + EqualityExtensions.CheckEquals(obj, clone, isSamePlatform: true); } // Used for updating blobs in BinaryFormatterTestData.cs @@ -71,17 +73,31 @@ namespace System.Runtime.Serialization.Formatters.Tests SanityCheckBlob(obj, blobs); - foreach (string blob in blobs) + // SqlException isn't deserializable from Desktop --> Core. + // Therefore we remove the second blob which is the one from Desktop. + if (!PlatformDetection.IsFullFramework && (obj is SqlException || obj is ReflectionTypeLoadException || obj is LicenseException)) { + var tmpList = new List(blobs); + tmpList.RemoveAt(1); + blobs = tmpList.ToArray(); + } + + // We store our framework blobs in index 1 + int platformBlobIndex = PlatformDetection.IsFullFramework ? 1 : 0; + for (int i = 0; i < blobs.Length; i++) + { + // Check if the current blob is from the current running platform. + bool isSamePlatform = i == platformBlobIndex; + if (isEqualityComparer) { - ValidateEqualityComparer(BinaryFormatterHelpers.FromBase64String(blob, FormatterAssemblyStyle.Simple)); - ValidateEqualityComparer(BinaryFormatterHelpers.FromBase64String(blob, FormatterAssemblyStyle.Full)); + ValidateEqualityComparer(BinaryFormatterHelpers.FromBase64String(blobs[i], FormatterAssemblyStyle.Simple)); + ValidateEqualityComparer(BinaryFormatterHelpers.FromBase64String(blobs[i], FormatterAssemblyStyle.Full)); } else { - CheckForAnyEquals(obj, BinaryFormatterHelpers.FromBase64String(blob, FormatterAssemblyStyle.Simple)); - CheckForAnyEquals(obj, BinaryFormatterHelpers.FromBase64String(blob, FormatterAssemblyStyle.Full)); + EqualityExtensions.CheckEquals(obj, BinaryFormatterHelpers.FromBase64String(blobs[i], FormatterAssemblyStyle.Simple), isSamePlatform); + EqualityExtensions.CheckEquals(obj, BinaryFormatterHelpers.FromBase64String(blobs[i], FormatterAssemblyStyle.Full), isSamePlatform); } } } @@ -94,8 +110,8 @@ namespace System.Runtime.Serialization.Formatters.Tests object obj = new ArraySegment(); string corefxBlob = "AAEAAAD/////AQAAAAAAAAAEAQAAAHJTeXN0ZW0uQXJyYXlTZWdtZW50YDFbW1N5c3RlbS5JbnQzMiwgbXNjb3JsaWIsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5XV0DAAAABl9hcnJheQdfb2Zmc2V0Bl9jb3VudAcAAAgICAoAAAAAAAAAAAs="; string netfxBlob = "AAEAAAD/////AQAAAAAAAAAEAQAAAHJTeXN0ZW0uQXJyYXlTZWdtZW50YDFbW1N5c3RlbS5JbnQzMiwgbXNjb3JsaWIsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5XV0DAAAABl9hcnJheQdfb2Zmc2V0Bl9jb3VudAcAAAgICAoAAAAAAAAAAAs="; - CheckForAnyEquals(obj, BinaryFormatterHelpers.FromBase64String(corefxBlob, FormatterAssemblyStyle.Full)); - CheckForAnyEquals(obj, BinaryFormatterHelpers.FromBase64String(netfxBlob, FormatterAssemblyStyle.Full)); + EqualityExtensions.CheckEquals(obj, BinaryFormatterHelpers.FromBase64String(corefxBlob, FormatterAssemblyStyle.Full), isSamePlatform: true); + EqualityExtensions.CheckEquals(obj, BinaryFormatterHelpers.FromBase64String(netfxBlob, FormatterAssemblyStyle.Full), isSamePlatform: true); } [Fact] @@ -137,7 +153,7 @@ namespace System.Runtime.Serialization.Formatters.Tests foreach (object[] obj in objects) { object clone = f.Deserialize(s); - CheckForAnyEquals(obj[0], clone); + EqualityExtensions.CheckEquals(obj[0], clone, isSamePlatform: true); } } @@ -414,7 +430,7 @@ namespace System.Runtime.Serialization.Formatters.Tests for (long i = s.Length - 1; i >= 0; i--) { s.Position = 0; - var data = new byte[i]; + byte[] data = new byte[i]; Assert.Equal(data.Length, s.Read(data, 0, data.Length)); Assert.Throws(() => f.Deserialize(new MemoryStream(data))); } @@ -462,11 +478,6 @@ namespace System.Runtime.Serialization.Formatters.Tests BinaryFormatterHelpers.Clone(Array.CreateInstance(typeof(uint[]), new[] { 5 }, new[] { 1 })); } - private static void CheckForAnyEquals(object obj, object deserializedObj) - { - Assert.True(EqualityExtensions.CheckEquals(obj, deserializedObj), "Error during equality check of type " + obj?.GetType()?.FullName); - } - private static void ValidateEqualityComparer(object obj) { Type objType = obj.GetType(); @@ -485,8 +496,9 @@ namespace System.Runtime.Serialization.Formatters.Tests return; } - // Exceptions in Net Native can't be reflected and therefore skipping blob sanity check - if (obj is Exception && PlatformDetection.IsNetNative) + // In most cases exceptions in Core have a different layout than in Desktop, + // therefore we are skipping the string comparison of the blobs. + if (obj is Exception) { return; } diff --git a/external/corefx/src/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.rd.xml b/external/corefx/src/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.rd.xml index 2d07efabf9..52f9ed3cc9 100644 --- a/external/corefx/src/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.rd.xml +++ b/external/corefx/src/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.rd.xml @@ -2,14 +2,35 @@ - - + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file + diff --git a/external/corefx/src/System.Runtime.Serialization.Formatters/tests/EqualityExtensions.cs b/external/corefx/src/System.Runtime.Serialization.Formatters/tests/EqualityExtensions.cs index d0bd9245b1..f06c63c889 100644 --- a/external/corefx/src/System.Runtime.Serialization.Formatters/tests/EqualityExtensions.cs +++ b/external/corefx/src/System.Runtime.Serialization.Formatters/tests/EqualityExtensions.cs @@ -8,12 +8,18 @@ using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; using System.Data; +using System.Data.SqlClient; using System.Data.SqlTypes; +using System.DirectoryServices.ActiveDirectory; using System.Globalization; using System.Linq; using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; using System.Reflection; using System.Runtime.CompilerServices; +using System.Security; +using System.Threading; using Xunit; namespace System.Runtime.Serialization.Formatters.Tests @@ -24,23 +30,31 @@ namespace System.Runtime.Serialization.Formatters.Tests { if (extendedType.IsGenericType) { + IEnumerable x = typeof(EqualityExtensions).GetMethods() + ?.Where(m => + m.Name == "IsEqual" && + m.GetParameters().Length == 3 && + m.IsGenericMethodDefinition); + MethodInfo method = typeof(EqualityExtensions).GetMethods() - ?.SingleOrDefault(m => - m.Name == "IsEqual" && - m.GetParameters().Length == 2 && - m.GetParameters()[0].ParameterType.Name == extendedType.Name && - m.IsGenericMethodDefinition); + ?.SingleOrDefault(m => + m.Name == "IsEqual" && + m.GetParameters().Length == 3 && + m.GetParameters()[0].ParameterType.Name == extendedType.Name && + m.IsGenericMethodDefinition); + + // If extension method found, make it generic and return if (method != null) return method.MakeGenericMethod(extendedType.GenericTypeArguments[0]); } - return typeof(EqualityExtensions).GetMethod("IsEqual", new[] { extendedType, extendedType }); + return typeof(EqualityExtensions).GetMethod("IsEqual", new[] { extendedType, extendedType, typeof(bool) }); } - public static bool CheckEquals(object objA, object objB) + public static void CheckEquals(object objA, object objB, bool isSamePlatform) { if (objA == null && objB == null) - return true; + return; if (objA != null && objB != null) { @@ -51,7 +65,8 @@ namespace System.Runtime.Serialization.Formatters.Tests MethodInfo customEqualityCheck = GetExtensionMethod(objType); if (customEqualityCheck != null) { - equalityResult = customEqualityCheck.Invoke(objA, new object[] { objA, objB }); + customEqualityCheck.Invoke(objA, new object[] { objA, objB, isSamePlatform }); + return; } else { @@ -65,32 +80,30 @@ namespace System.Runtime.Serialization.Formatters.Tests if (equalsMethod.DeclaringType != typeof(object)) { equalityResult = equalsMethod.Invoke(objA, new object[] { objB }); + Assert.True((bool)equalityResult); + return; } } } - - if (equalityResult != null) - { - return (bool)equalityResult; - } } if (objA is IEnumerable objAEnumerable && objB is IEnumerable objBEnumerable) { - return CheckSequenceEquals(objAEnumerable, objBEnumerable); + CheckSequenceEquals(objAEnumerable, objBEnumerable, isSamePlatform); + return; } - return objA.Equals(objB); + Assert.True(objA.Equals(objB)); } - public static bool CheckSequenceEquals(this IEnumerable @this, IEnumerable other) + public static void CheckSequenceEquals(this IEnumerable @this, IEnumerable other, bool isSamePlatform) { if (@this == null || other == null) - return @this == other; - - if (@this.GetType() != other.GetType()) - return false; + { + Assert.Equal(@this, other); + } + Assert.Equal(@this.GetType(), other.GetType()); IEnumerator eA = null; IEnumerator eB = null; @@ -102,13 +115,12 @@ namespace System.Runtime.Serialization.Formatters.Tests { bool moved = eA.MoveNext(); if (moved != eB.MoveNext()) - return false; + return; if (!moved) - return true; + return; if (eA.Current == null && eB.Current == null) - return true; - if (!CheckEquals(eA.Current, eB.Current)) - return true; + return; + CheckEquals(eA.Current, eB.Current, isSamePlatform); } } finally @@ -118,13 +130,14 @@ namespace System.Runtime.Serialization.Formatters.Tests } } - public static bool IsEqual(this WeakReference @this, WeakReference other) + public static void IsEqual(this WeakReference @this, WeakReference other, bool isSamePlatform) { - if (@this == null || other == null) - return false; + if (@this == null && other == null) + return; - if (@this.TrackResurrection != other.TrackResurrection) - return false; + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.TrackResurrection, other.TrackResurrection); // When WeakReference is deserialized, the object it wraps may blip into and out of // existence before we get a chance to compare it, since there are no strong references @@ -132,87 +145,108 @@ namespace System.Runtime.Serialization.Formatters.Tests // values, great, compare them. Otherwise, consider them equal. object a = @this.Target; object b = other.Target; - return a != null && b != null ? Equals(a, b) : true; + + if (a != null && b != null) + { + Assert.Equal(a, b); + } } - public static bool IsEqual(this WeakReference @this, WeakReference other) + public static void IsEqual(this WeakReference @this, WeakReference other, bool isSamePlatform) where T : class { - if (@this == null || other == null) - return false; + if(@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); // When WeakReference is deserialized, the object it wraps may blip into and out of // existence before we get a chance to compare it, since there are no strong references // to it such that it can then be immediately collected. Therefore, if we can get both // values, great, compare them. Otherwise, consider them equal. - return @this.TryGetTarget(out T thisTarget) && other.TryGetTarget(out T otherTarget) ? - Equals(thisTarget, otherTarget) : - true; + if (@this.TryGetTarget(out T thisTarget) && other.TryGetTarget(out T otherTarget)) + { + Assert.Equal(thisTarget, otherTarget); + } } - public static bool IsEqual(this Lazy @this, Lazy other) + public static void IsEqual(this Lazy @this, Lazy other, bool isSamePlatform) { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + // Force value creation for lazy original object T thisVal = @this.Value; T otherVal = other.Value; - return @this != null && - other != null && - @this.IsValueCreated == other.IsValueCreated && - Object.Equals(thisVal, otherVal); + Assert.Equal(@this.IsValueCreated, other.IsValueCreated); + CheckEquals(thisVal, otherVal, isSamePlatform); } - public static bool IsEqual(this StreamingContext @this, StreamingContext other) + public static void IsEqual(this StreamingContext @this, StreamingContext other, bool isSamePlatform) { - return @this.State == @other.State && - Object.Equals(@this.Context, other.Context); + Assert.Equal(@this.State, other.State); + CheckEquals(@this.Context, other.Context, isSamePlatform); } - public static bool IsEqual(this CookieContainer @this, CookieContainer other) + public static void IsEqual(this CookieContainer @this, CookieContainer other, bool isSamePlatform) { - return @this != null && - other != null && - @this.Capacity == other.Capacity && - @this.Count == other.Count && - @this.MaxCookieSize == other.MaxCookieSize && - @this.PerDomainCapacity == other.PerDomainCapacity; + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Capacity, other.Capacity); + Assert.Equal(@this.Count, other.Count); + Assert.Equal(@this.MaxCookieSize, other.MaxCookieSize); + Assert.Equal(@this.PerDomainCapacity, other.PerDomainCapacity); } - public static bool IsEqual(this DataSet @this, DataSet other) + public static void IsEqual(this DataSet @this, DataSet other, bool isSamePlatform) { - return @this != null && - @other != null && - @this.DataSetName == other.DataSetName && - @this.Namespace == other.Namespace && - @this.Prefix == other.Prefix && - @this.CaseSensitive == other.CaseSensitive && - @this.Locale.LCID == other.Locale.LCID && - @this.EnforceConstraints == other.EnforceConstraints && - @this.ExtendedProperties?.Count == other.ExtendedProperties?.Count && - CheckEquals(@this.ExtendedProperties, other.ExtendedProperties); + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.DataSetName, other.DataSetName); + Assert.Equal(@this.Namespace, other.Namespace); + Assert.Equal(@this.Prefix, other.Prefix); + Assert.Equal(@this.CaseSensitive, other.CaseSensitive); + Assert.Equal(@this.Locale.LCID, other.Locale.LCID); + Assert.Equal(@this.EnforceConstraints, other.EnforceConstraints); + Assert.Equal(@this.ExtendedProperties?.Count, other.ExtendedProperties?.Count); + CheckEquals(@this.ExtendedProperties, other.ExtendedProperties, isSamePlatform); } - public static bool IsEqual(this DataTable @this, DataTable other) + public static void IsEqual(this DataTable @this, DataTable other, bool isSamePlatform) { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.RemotingFormat, other.RemotingFormat); Assert.Equal(@this.TableName, other.TableName); - return @this != null && - other != null && - @this.RemotingFormat == other.RemotingFormat && - @this.TableName == other.TableName && - @this.Namespace == other.Namespace && - @this.Prefix == other.Prefix && - @this.CaseSensitive == other.CaseSensitive && - @this.Locale.LCID == other.Locale.LCID && - @this.MinimumCapacity == other.MinimumCapacity; + Assert.Equal(@this.Namespace, other.Namespace); + Assert.Equal(@this.Prefix, other.Prefix); + Assert.Equal(@this.CaseSensitive, other.CaseSensitive); + Assert.Equal(@this.Locale.LCID, other.Locale.LCID); + Assert.Equal(@this.MinimumCapacity, other.MinimumCapacity); } - public static bool IsEqual(this Comparer @this, Comparer other) + public static void IsEqual(this Comparer @this, Comparer other, bool isSamePlatform) { - if(@this == null || other == null) - { - return false; - } + if (@this == null && other == null) + return; + Assert.NotNull(@this); + Assert.NotNull(other); + // The compareInfos are internal and get reflection blocked on .NET Native, so use // GetObjectData to get them SerializationInfo thisInfo = new SerializationInfo(typeof(Comparer), new FormatterConverter()); @@ -223,483 +257,516 @@ namespace System.Runtime.Serialization.Formatters.Tests other.GetObjectData(otherInfo, new StreamingContext()); CompareInfo otherCompareInfo = (CompareInfo)otherInfo.GetValue("CompareInfo", typeof(CompareInfo)); - return Object.Equals(thisCompareInfo, otherCompareInfo); + Assert.Equal(thisCompareInfo, otherCompareInfo); + } + + public static void IsEqual(this DictionaryEntry @this, DictionaryEntry other, bool isSamePlatform) + { + CheckEquals(@this.Key, other.Key, isSamePlatform); + CheckEquals(@this.Value, other.Value, isSamePlatform); } - - public static bool IsEqual(this DictionaryEntry @this, DictionaryEntry other) + public static void IsEqual(this StringDictionary @this, StringDictionary other, bool isSamePlatform) { - return Object.Equals(@this.Key, other.Key) && Object.Equals(@this.Value, other.Value); + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Count, other.Count); } - public static bool IsEqual(this StringDictionary @this, StringDictionary other) + public static void IsEqual(this ArrayList @this, ArrayList other, bool isSamePlatform) { - return @this != null && - other != null && - @this.Count == other.Count; - } + if (@this == null && other == null) + return; - public static bool IsEqual(this ArrayList @this, ArrayList other) - { - if (!(@this != null && - other != null && - @this.Capacity == other.Capacity && - @this.Count == other.Count && - @this.IsFixedSize == other.IsFixedSize && - @this.IsReadOnly == other.IsReadOnly && - @this.IsSynchronized == other.IsSynchronized)) - return false; + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Count, other.Count); + Assert.Equal(@this.Capacity, other.Capacity); + Assert.Equal(@this.IsFixedSize, other.IsFixedSize); + Assert.Equal(@this.IsReadOnly, other.IsReadOnly); + Assert.Equal(@this.IsSynchronized, other.IsSynchronized); for (int i = 0; i < @this.Count; i++) { - if (!CheckEquals(@this[i], other[i])) - return false; + CheckEquals(@this[i], other[i], isSamePlatform); } - - return true; } - public static bool IsEqual(this BitArray @this, BitArray other) - { - if (!(@this != null && - other != null && - @this.Length == other.Length && - @this.Count == other.Count && - @this.IsReadOnly == other.IsReadOnly && - @this.IsSynchronized == other.IsSynchronized)) - return false; - - return CheckSequenceEquals(@this, other); - } - - public static bool IsEqual(this Dictionary @this, Dictionary other) - { - if (!(@this != null && - other != null && - CheckEquals(@this.Comparer, other.Comparer) && - @this.Count == other.Count && - @this.Keys.CheckSequenceEquals(other.Keys) && - @this.Values.CheckSequenceEquals(other.Values))) - return false; - - foreach (var kv in @this) - { - if (@this[kv.Key] != other[kv.Key]) - return false; - } - - return true; - } - - public static bool IsEqual(this PointEqualityComparer @this, PointEqualityComparer other) - { - return @this != null && - other != null; - } - - public static bool IsEqual(this HashSet @this, HashSet other) - { - if (!(@this != null && - other != null && - @this.Count == other.Count && - CheckEquals(@this.Comparer, other.Comparer))) - return false; - - return @this.CheckSequenceEquals(other); - } - - public static bool IsEqual(this LinkedListNode @this, LinkedListNode other) + public static void IsEqual(this BitArray @this, BitArray other, bool isSamePlatform) { if (@this == null && other == null) - return true; + return; - return @this != null - && other != null && - CheckEquals(@this.Value, other.Value); + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Length, other.Length); + Assert.Equal(@this.Count, other.Count); + Assert.Equal(@this.IsReadOnly, other.IsReadOnly); + Assert.Equal(@this.IsSynchronized, other.IsSynchronized); + CheckSequenceEquals(@this, other, isSamePlatform); } - public static bool IsEqual(this LinkedList @this, LinkedList other) + public static void IsEqual(this Dictionary @this, Dictionary other, bool isSamePlatform) { - if (!(@this != null && - other != null && - @this.Count == other.Count && - IsEqual(@this.First, other.First) && - IsEqual(@this.Last, other.Last))) - return false; + if (@this == null && other == null) + return; - return @this.CheckSequenceEquals(other); - } + Assert.NotNull(@this); + Assert.NotNull(other); + CheckEquals(@this.Comparer, other.Comparer, isSamePlatform); + Assert.Equal(@this.Count, other.Count); + @this.Keys.CheckSequenceEquals(other.Keys, isSamePlatform); + @this.Values.CheckSequenceEquals(other.Values, isSamePlatform); - public static bool IsEqual(this List @this, List other) - { - if (!(@this != null && - other != null && - @this.Capacity == other.Capacity && - @this.Count == other.Count)) - return false; - - return @this.CheckSequenceEquals(other); - } - - public static bool IsEqual(this Queue @this, Queue other) - { - if (!(@this != null && - other != null && - @this.Count == other.Count)) - return false; - - return @this.CheckSequenceEquals(other); - } - - public static bool IsEqual(this SortedList @this, SortedList other) - { - if (!(@this != null && - other != null && - @this.Capacity == other.Capacity && - CheckEquals(@this.Comparer, other.Comparer) && - @this.Count == other.Count && - @this.Keys.CheckSequenceEquals(other.Keys) && - @this.Values.CheckSequenceEquals(other.Values))) - return false; - - return @this.CheckSequenceEquals(other); - } - - public static bool IsEqual(this SortedSet @this, SortedSet other) - { - if (!(@this != null && - other != null && - @this.Count == other.Count && - CheckEquals(@this.Comparer, other.Comparer) && - CheckEquals(@this.Min, other.Min) && - CheckEquals(@this.Max, other.Max))) - return false; - - return @this.CheckSequenceEquals(other); - } - - public static bool IsEqual(this Stack @this, Stack other) - { - if (!(@this != null && - other != null && - @this.Count == other.Count)) - return false; - - return @this.CheckSequenceEquals(other); - } - - public static bool IsEqual(this Hashtable @this, Hashtable other) - { - if (!(@this != null && - other != null && - @this.IsReadOnly == other.IsReadOnly && - @this.IsFixedSize == other.IsFixedSize && - @this.IsSynchronized == other.IsSynchronized && - @this.Keys.CheckSequenceEquals(other.Keys) && - @this.Values.CheckSequenceEquals(other.Values) && - @this.Count == other.Count)) - return false; - - foreach (var key in @this.Keys) + foreach (KeyValuePair kv in @this) { - if (!CheckEquals(@this[key], other[key])) - return false; + Assert.Equal(@this[kv.Key], other[kv.Key]); } - - return true; } - public static bool IsEqual(this Collection @this, Collection other) + public static void IsEqual(this PointEqualityComparer @this, PointEqualityComparer other, bool isSamePlatform) { - if (!(@this != null && - other != null && - @this.Count == other.Count)) - return false; + if (@this == null && other == null) + return; - return @this.CheckSequenceEquals(other); + Assert.NotNull(@this); + Assert.NotNull(other); } - public static bool IsEqual(this ObservableCollection @this, ObservableCollection other) + public static void IsEqual(this HashSet @this, HashSet other, bool isSamePlatform) { - if (!(@this != null && - other != null && - @this.Count == other.Count)) - return false; + if (@this == null && other == null) + return; - return @this.CheckSequenceEquals(other); + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Count, other.Count); + CheckEquals(@this.Comparer, other.Comparer, isSamePlatform); + @this.CheckSequenceEquals(other, isSamePlatform); } - public static bool IsEqual(this ReadOnlyCollection @this, ReadOnlyCollection other) + public static void IsEqual(this LinkedListNode @this, LinkedListNode other, bool isSamePlatform) { - if (!(@this != null && - other != null && - @this.Count == other.Count)) - return false; + if (@this == null && other == null) + return; - return @this.CheckSequenceEquals(other); + Assert.NotNull(@this); + Assert.NotNull(other); + CheckEquals(@this.Value, other.Value, isSamePlatform); } - public static bool IsEqual(this ReadOnlyDictionary @this, ReadOnlyDictionary other) + public static void IsEqual(this LinkedList @this, LinkedList other, bool isSamePlatform) { - if (!(@this != null && - other != null && - @this.Keys.CheckSequenceEquals(other.Keys) && - @this.Values.CheckSequenceEquals(other.Values) && - @this.Count == other.Count)) - return false; + if (@this == null && other == null) + return; - foreach (var kv in @this) - { - if (kv.Value != other[kv.Key]) - return false; - } - - return true; + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Count, other.Count); + CheckEquals(@this.First, other.First, isSamePlatform); + CheckEquals(@this.Last, other.Last, isSamePlatform); + @this.CheckSequenceEquals(other, isSamePlatform); } - public static bool IsEqual(this ReadOnlyObservableCollection @this, ReadOnlyObservableCollection other) + public static void IsEqual(this List @this, List other, bool isSamePlatform) { - if (!(@this != null && - other != null && - @this.Count == other.Count)) - return false; + if (@this == null && other == null) + return; - return @this.CheckSequenceEquals(other); + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Count, other.Count); + Assert.Equal(@this.Capacity, other.Capacity); + @this.CheckSequenceEquals(other, isSamePlatform); } - public static bool IsEqual(this Queue @this, Queue other) + public static void IsEqual(this Queue @this, Queue other, bool isSamePlatform) { - if (!(@this != null && - other != null && - @this.Count == other.Count && - @this.IsSynchronized == other.IsSynchronized)) - return false; + if (@this == null && other == null) + return; - return @this.CheckSequenceEquals(other); + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Count, other.Count); + @this.CheckSequenceEquals(other, isSamePlatform); } - public static bool IsEqual(this SortedList @this, SortedList other) + public static void IsEqual(this SortedList @this, SortedList other, bool isSamePlatform) { - if (!(@this != null && - other != null && - @this.Capacity == other.Capacity && - @this.Count == other.Count && - @this.Keys.CheckSequenceEquals(other.Keys) && - @this.Values.CheckSequenceEquals(other.Values) && - @this.IsReadOnly == other.IsReadOnly && - @this.IsFixedSize == other.IsFixedSize && - @this.IsSynchronized == other.IsSynchronized)) - return false; + if (@this == null && other == null) + return; - return @this.CheckSequenceEquals(other); + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Capacity, other.Capacity); + CheckEquals(@this.Comparer, other.Comparer, isSamePlatform); + Assert.Equal(@this.Count, other.Count); + @this.Keys.CheckSequenceEquals(other.Keys, isSamePlatform); + @this.Values.CheckSequenceEquals(other.Values, isSamePlatform); + @this.CheckSequenceEquals(other, isSamePlatform); } - public static bool IsEqual(this HybridDictionary @this, HybridDictionary other) + public static void IsEqual(this SortedSet @this, SortedSet other, bool isSamePlatform) { - if (!(@this != null && - other != null && - @this.Count == other.Count && - @this.Keys.CheckSequenceEquals(other.Keys) && - @this.IsReadOnly == other.IsReadOnly && - @this.IsFixedSize == other.IsFixedSize && - @this.IsSynchronized == other.IsSynchronized && - @this.Values.CheckSequenceEquals(other.Values))) - return false; + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Count, other.Count); + CheckEquals(@this.Comparer, other.Comparer, isSamePlatform); + CheckEquals(@this.Min, other.Min, isSamePlatform); + CheckEquals(@this.Max, other.Max, isSamePlatform); + @this.CheckSequenceEquals(other, isSamePlatform); + } + + public static void IsEqual(this Stack @this, Stack other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Count, other.Count); + @this.CheckSequenceEquals(other, isSamePlatform); + } + + public static void IsEqual(this Hashtable @this, Hashtable other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.IsReadOnly, other.IsReadOnly); + Assert.Equal(@this.IsFixedSize, other.IsFixedSize); + Assert.Equal(@this.IsSynchronized, other.IsSynchronized); + @this.Keys.CheckSequenceEquals(other.Keys, isSamePlatform); + @this.Values.CheckSequenceEquals(other.Values, isSamePlatform); + Assert.Equal(@this.Count, other.Count); foreach (var key in @this.Keys) { - if (!CheckEquals(@this[key], other[key])) - return false; + CheckEquals(@this[key], other[key], isSamePlatform); } - - return true; } - public static bool IsEqual(this ListDictionary @this, ListDictionary other) + public static void IsEqual(this Collection @this, Collection other, bool isSamePlatform) { - if (!(@this != null && - other != null && - @this.Count == other.Count && - @this.Keys.CheckSequenceEquals(other.Keys) && - @this.IsReadOnly == other.IsReadOnly && - @this.IsFixedSize == other.IsFixedSize && - @this.IsSynchronized == other.IsSynchronized && - @this.Values.CheckSequenceEquals(other.Values))) - return false; + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Count, other.Count); + @this.CheckSequenceEquals(other, isSamePlatform); + } + + public static void IsEqual(this ObservableCollection @this, ObservableCollection other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Count, other.Count); + @this.CheckSequenceEquals(other, isSamePlatform); + } + + public static void IsEqual(this ReadOnlyCollection @this, ReadOnlyCollection other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Count, other.Count); + @this.CheckSequenceEquals(other, isSamePlatform); + } + + public static void IsEqual(this ReadOnlyDictionary @this, ReadOnlyDictionary other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + @this.Keys.CheckSequenceEquals(other.Keys, isSamePlatform); + @this.Values.CheckSequenceEquals(other.Values, isSamePlatform); + Assert.Equal(@this.Count, other.Count); + + foreach (KeyValuePair kv in @this) + { + Assert.Equal(kv.Value, other[kv.Key]); + } + } + + public static void IsEqual(this ReadOnlyObservableCollection @this, ReadOnlyObservableCollection other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Count, other.Count); + @this.CheckSequenceEquals(other, isSamePlatform); + } + + public static void IsEqual(this Queue @this, Queue other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Count, other.Count); + Assert.Equal(@this.IsSynchronized, other.IsSynchronized); + @this.CheckSequenceEquals(other, isSamePlatform); + } + + public static void IsEqual(this SortedList @this, SortedList other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Capacity, other.Capacity); + Assert.Equal(@this.Count, other.Count); + @this.Keys.CheckSequenceEquals(other.Keys, isSamePlatform); + @this.Values.CheckSequenceEquals(other.Values, isSamePlatform); + Assert.Equal(@this.IsReadOnly, other.IsReadOnly); + Assert.Equal(@this.IsFixedSize, other.IsFixedSize); + Assert.Equal(@this.IsSynchronized, other.IsSynchronized); + @this.CheckSequenceEquals(other, isSamePlatform); + } + + public static void IsEqual(this HybridDictionary @this, HybridDictionary other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Count, other.Count); + @this.Keys.CheckSequenceEquals(other.Keys, isSamePlatform); + Assert.Equal(@this.IsReadOnly, other.IsReadOnly); + Assert.Equal(@this.IsFixedSize, other.IsFixedSize); + Assert.Equal(@this.IsSynchronized, other.IsSynchronized); + @this.Values.CheckSequenceEquals(other.Values, isSamePlatform); foreach (var key in @this.Keys) { - if (!CheckEquals(@this[key], other[key])) - return false; + CheckEquals(@this[key], other[key], isSamePlatform); } - - return true; } - public static bool IsEqual(this NameValueCollection @this, NameValueCollection other) + public static void IsEqual(this ListDictionary @this, ListDictionary other, bool isSamePlatform) { - if (!(@this != null && - other != null && - @this.AllKeys.CheckSequenceEquals(other.AllKeys) && - @this.Count == other.Count && - @this.Keys.CheckSequenceEquals(other.Keys))) - return false; + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Count, other.Count); + @this.Keys.CheckSequenceEquals(other.Keys, isSamePlatform); + Assert.Equal(@this.IsReadOnly, other.IsReadOnly); + Assert.Equal(@this.IsFixedSize, other.IsFixedSize); + Assert.Equal(@this.IsSynchronized, other.IsSynchronized); + @this.Values.CheckSequenceEquals(other.Values, isSamePlatform); + + foreach (var key in @this.Keys) + { + CheckEquals(@this[key], other[key], isSamePlatform); + } + } + + public static void IsEqual(this NameValueCollection @this, NameValueCollection other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + @this.AllKeys.CheckSequenceEquals(other.AllKeys, isSamePlatform); + Assert.Equal(@this.Count, other.Count); + @this.Keys.CheckSequenceEquals(other.Keys, isSamePlatform); foreach (var key in @this.AllKeys) { - if (!CheckEquals(@this[key], other[key])) - return false; + CheckEquals(@this[key], other[key], isSamePlatform); } - - return true; } - public static bool IsEqual(this OrderedDictionary @this, OrderedDictionary other) + public static void IsEqual(this OrderedDictionary @this, OrderedDictionary other, bool isSamePlatform) { - if (!(@this != null && - other != null && - @this.Count == other.Count && - @this.IsReadOnly == other.IsReadOnly && - CheckEquals(@this.Keys, other.Keys) && - CheckEquals(@this.Values, other.Values))) - return false; + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Count, other.Count); + Assert.Equal(@this.IsReadOnly, other.IsReadOnly); + CheckEquals(@this.Keys, other.Keys, isSamePlatform); + CheckEquals(@this.Values, other.Values, isSamePlatform); foreach (var key in @this.Keys) { - if (!CheckEquals(@this[key], other[key])) - return false; + CheckEquals(@this[key], other[key], isSamePlatform); } - - return true; } - public static bool IsEqual(this StringCollection @this, StringCollection other) + public static void IsEqual(this StringCollection @this, StringCollection other, bool isSamePlatform) { - if (!(@this != null && - other != null && - @this.Count == other.Count && - @this.IsReadOnly == other.IsReadOnly && - @this.IsSynchronized == other.IsSynchronized)) - return false; + if (@this == null && other == null) + return; - return @this.CheckSequenceEquals(other); + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Count, other.Count); + Assert.Equal(@this.IsReadOnly, other.IsReadOnly); + Assert.Equal(@this.IsSynchronized, other.IsSynchronized); + @this.CheckSequenceEquals(other, isSamePlatform); } - public static bool IsEqual(this Stack @this, Stack other) + public static void IsEqual(this Stack @this, Stack other, bool isSamePlatform) { - if (!(@this != null && - other != null && - @this.Count == other.Count && - @this.IsSynchronized == other.IsSynchronized)) - return false; + if (@this == null && other == null) + return; - return @this.CheckSequenceEquals(other); + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Count, other.Count); + Assert.Equal(@this.IsSynchronized, other.IsSynchronized); + @this.CheckSequenceEquals(other, isSamePlatform); } - public static bool IsEqual(this BindingList @this, BindingList other) + public static void IsEqual(this BindingList @this, BindingList other, bool isSamePlatform) { - if (!(@this != null && - other != null && - @this.RaiseListChangedEvents == other.RaiseListChangedEvents && - @this.AllowNew == other.AllowNew && - @this.AllowEdit == other.AllowEdit && - @this.AllowRemove == other.AllowRemove && - @this.Count == other.Count)) - return false; + if (@this == null && other == null) + return; - return @this.CheckSequenceEquals(other); + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.RaiseListChangedEvents, other.RaiseListChangedEvents); + Assert.Equal(@this.AllowNew, other.AllowNew); + Assert.Equal(@this.AllowEdit, other.AllowEdit); + Assert.Equal(@this.AllowRemove, other.AllowRemove); + Assert.Equal(@this.Count, other.Count); + @this.CheckSequenceEquals(other, isSamePlatform); } - public static bool IsEqual(this BindingList @this, BindingList other) + public static void IsEqual(this BindingList @this, BindingList other, bool isSamePlatform) { - if (!(@this != null && - other != null && - @this.RaiseListChangedEvents == other.RaiseListChangedEvents && - @this.AllowNew == other.AllowNew && - @this.AllowEdit == other.AllowEdit && - @this.AllowRemove == other.AllowRemove && - @this.Count == other.Count)) - return false; + if (@this == null && other == null) + return; - return @this.CheckSequenceEquals(other); + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.RaiseListChangedEvents, other.RaiseListChangedEvents); + Assert.Equal(@this.AllowNew, other.AllowNew); + Assert.Equal(@this.AllowEdit, other.AllowEdit); + Assert.Equal(@this.AllowRemove, other.AllowRemove); + Assert.Equal(@this.Count, other.Count); + @this.CheckSequenceEquals(other, isSamePlatform); } - public static bool IsEqual(this PropertyCollection @this, PropertyCollection other) + public static void IsEqual(this PropertyCollection @this, PropertyCollection other, bool isSamePlatform) { - if (!(@this != null && - other != null && - @this.IsReadOnly == other.IsReadOnly && - @this.IsFixedSize == other.IsFixedSize && - @this.IsSynchronized == other.IsSynchronized && - @this.Keys.CheckSequenceEquals(other.Keys) && - @this.Values.CheckSequenceEquals(other.Values) && - @this.Count == other.Count)) - return false; + if (@this == null && other == null) + return; - return @this.CheckSequenceEquals(other); + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.IsReadOnly, other.IsReadOnly); + Assert.Equal(@this.IsFixedSize, other.IsFixedSize); + Assert.Equal(@this.IsSynchronized, other.IsSynchronized); + @this.Keys.CheckSequenceEquals(other.Keys, isSamePlatform); + @this.Values.CheckSequenceEquals(other.Values, isSamePlatform); + Assert.Equal(@this.Count, other.Count); + @this.CheckSequenceEquals(other, isSamePlatform); } - public static bool IsEqual(this CompareInfo @this, CompareInfo other) + public static void IsEqual(this CompareInfo @this, CompareInfo other, bool isSamePlatform) { - return @this != null && - other != null && - @this.Name == other.Name && - @this.LCID == other.LCID && - // we do not want to compare Version because it can change when changing OS - // we do want to make sure that they are either both null or both not null - (@this.Version != null) == (other.Version != null); + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Name, other.Name); + Assert.Equal(@this.LCID, other.LCID); + // we do not want to compare Version because it can change when changing OS + // we do want to make sure that they are either both null or both not null + Assert.True((@this.Version != null) == (other.Version != null)); } - public static bool IsEqual(this SortVersion @this, SortVersion other) + public static void IsEqual(this SortVersion @this, SortVersion other, bool isSamePlatform) { - return @this != null && - other != null && - @this.FullVersion == other.FullVersion && - @this.SortId == other.SortId; + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.FullVersion, other.FullVersion); + Assert.Equal(@this.SortId, other.SortId); } - public static bool IsEqual(this Cookie @this, Cookie other) + public static void IsEqual(this Cookie @this, Cookie other, bool isSamePlatform) { - return @this != null && - other != null && - @this.Comment == other.Comment && - IsEqual(@this.CommentUri, other.CommentUri) && - @this.HttpOnly == other.HttpOnly && - @this.Discard == other.Discard && - @this.Domain == other.Domain && - @this.Expired == other.Expired && - CheckEquals(@this.Expires, other.Expires) && - @this.Name == other.Name && - @this.Path == other.Path && - @this.Port == other.Port && - @this.Secure == other.Secure && - // This needs to have m_Timestamp set by reflection in order to roundtrip correctly - // otherwise this field will change each time you create an object and cause this to fail - CheckEquals(@this.TimeStamp, other.TimeStamp) && - @this.Value == other.Value && - @this.Version == other.Version; + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Comment, other.Comment); + IsEqual(@this.CommentUri, other.CommentUri, isSamePlatform); + Assert.Equal(@this.HttpOnly, other.HttpOnly); + Assert.Equal(@this.Discard, other.Discard); + Assert.Equal(@this.Domain, other.Domain); + Assert.Equal(@this.Expired, other.Expired); + CheckEquals(@this.Expires, other.Expires, isSamePlatform); + Assert.Equal(@this.Name, other.Name); + Assert.Equal(@this.Path, other.Path); + Assert.Equal(@this.Port, other.Port); + Assert.Equal(@this.Secure, other.Secure); + // This needs to have m_Timestamp set by reflection in order to roundtrip correctly + // otherwise this field will change each time you create an object and cause this to fail + CheckEquals(@this.TimeStamp, other.TimeStamp, isSamePlatform); + Assert.Equal(@this.Value, other.Value); + Assert.Equal(@this.Version, other.Version); } - public static bool IsEqual(this CookieCollection @this, CookieCollection other) + public static void IsEqual(this CookieCollection @this, CookieCollection other, bool isSamePlatform) { - if (!(@this != null && - other != null && - @this.Count == other.Count)) - return false; + if (@this == null && other == null) + return; - return @this.CheckSequenceEquals(other); + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Count, other.Count); + @this.CheckSequenceEquals(other, isSamePlatform); } - public static bool IsEqual(this BasicISerializableObject @this, BasicISerializableObject other) + public static void IsEqual(this BasicISerializableObject @this, BasicISerializableObject other, bool isSamePlatform) { - return @this != null && - other != null; + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); } - public static bool IsEqual(this DerivedISerializableWithNonPublicDeserializationCtor @this, DerivedISerializableWithNonPublicDeserializationCtor other) + public static void IsEqual(this DerivedISerializableWithNonPublicDeserializationCtor @this, DerivedISerializableWithNonPublicDeserializationCtor other, bool isSamePlatform) { - return @this != null && - other != null; + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); } private static void GetIdsForGraphDFS(Graph n, Dictionary, int> ids) @@ -707,7 +774,7 @@ namespace System.Runtime.Serialization.Formatters.Tests if (!ids.ContainsKey(n)) { ids[n] = ids.Count; - foreach (var link in n.Links) + foreach (Graph link in n.Links) { GetIdsForGraphDFS(link, ids); } @@ -717,7 +784,7 @@ namespace System.Runtime.Serialization.Formatters.Tests private static Dictionary> InvertDictionary(Dictionary, int> dict) { var ret = new Dictionary>(); - foreach (var kv in dict) + foreach (KeyValuePair, int> kv in dict) { Assert.False(ret.ContainsKey(kv.Value)); ret[kv.Value] = kv.Key; @@ -744,10 +811,10 @@ namespace System.Runtime.Serialization.Formatters.Tests edges.Add(new List()); } - foreach (var kv in nodes) + foreach (KeyValuePair, int> kv in nodes) { List links = edges[kv.Value]; - foreach (var link in kv.Key.Links) + foreach (Graph link in kv.Key.Links) { links.Add(nodes[link]); } @@ -756,274 +823,373 @@ namespace System.Runtime.Serialization.Formatters.Tests return new Tuple>, List>>(InvertDictionary(nodes), edges); } - public static bool IsEqual(this Graph @this, Graph other) + public static void IsEqual(this Graph @this, Graph other, bool isSamePlatform) { - var thisFlattened = FlattenGraph(@this); - var otherFlattened = FlattenGraph(other); + Tuple>, List>> thisFlattened = FlattenGraph(@this); + Tuple>, List>> otherFlattened = FlattenGraph(other); - if (thisFlattened.Item1.Count != otherFlattened.Item1.Count || - thisFlattened.Item2.Count != otherFlattened.Item2.Count) - return false; + Assert.Equal(thisFlattened.Item1.Count, otherFlattened.Item1.Count); + Assert.Equal(thisFlattened.Item2.Count, otherFlattened.Item2.Count); + Assert.Equal(thisFlattened.Item1.Values, otherFlattened.Item1.Values); + CheckEquals(thisFlattened.Item2, otherFlattened.Item2, isSamePlatform); + } - for (int i = 0; i < thisFlattened.Item1.Count; i++) + public static void IsEqual(this ArraySegment @this, ArraySegment other, bool isSamePlatform) + { + Assert.True((@this.Array != null) == (other.Array != null)); + Assert.Equal(@this.Count, other.Count); + Assert.Equal(@this.Offset, other.Offset); + if (@this.Array != null) { - if (thisFlattened.Item1[i].Value != otherFlattened.Item1[i].Value) - return false; + @this.CheckSequenceEquals(other, isSamePlatform); + } + } + + public static void IsEqual(this ObjectWithArrays @this, ObjectWithArrays other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + CheckEquals(@this.IntArray, other.IntArray, isSamePlatform); + CheckEquals(@this.StringArray, other.StringArray, isSamePlatform); + //CheckEquals(@this.TreeArray, other.TreeArray); + CheckEquals(@this.ByteArray, other.ByteArray, isSamePlatform); + CheckEquals(@this.JaggedArray, other.JaggedArray, isSamePlatform); + CheckEquals(@this.MultiDimensionalArray, other.MultiDimensionalArray, isSamePlatform); + } + + public static void IsEqual(this ObjectWithIntStringUShortUIntULongAndCustomObjectFields @this, ObjectWithIntStringUShortUIntULongAndCustomObjectFields other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Member1, other.Member1); + Assert.Equal(@this.Member2, other.Member2); + Assert.Equal(@this._member3, other._member3); + IsEqual(@this.Member4, other.Member4, isSamePlatform); + IsEqual(@this.Member4shared, other.Member4shared, isSamePlatform); + IsEqual(@this.Member5, other.Member5, isSamePlatform); + Assert.Equal(@this.Member6, other.Member6); + Assert.Equal(@this.str1, other.str1); + Assert.Equal(@this.str2, other.str2); + Assert.Equal(@this.str3, other.str3); + Assert.Equal(@this.str4, other.str4); + Assert.Equal(@this.u16, other.u16); + Assert.Equal(@this.u32, other.u32); + Assert.Equal(@this.u64, other.u64); + } + + public static void IsEqual(this Point @this, Point other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.X, other.X); + Assert.Equal(@this.Y, other.Y); + } + + public static void IsEqual(this SqlGuid @this, SqlGuid other, bool isSamePlatform) + { + Assert.Equal(@this.IsNull, other.IsNull); + Assert.True(@this.IsNull || @this.Value == other.Value); + } + + public static void IsEqual(this SealedObjectWithIntStringFields @this, SealedObjectWithIntStringFields other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Member1, other.Member1); + Assert.Equal(@this.Member2, other.Member2); + Assert.Equal(@this.Member3, other.Member3); + } + + public static void IsEqual(this SimpleKeyedCollection @this, SimpleKeyedCollection other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Comparer, other.Comparer); + Assert.Equal(@this.Count, other.Count); + @this.CheckSequenceEquals(other, isSamePlatform); + } + + public static void IsEqual(this Tree @this, Tree other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Value, other.Value); + IsEqual(@this.Left, other.Left, isSamePlatform); + IsEqual(@this.Right, other.Right, isSamePlatform); + } + + public static void IsEqual(this TimeZoneInfo @this, TimeZoneInfo other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Id, other.Id); + Assert.Equal(@this.DisplayName, other.DisplayName); + Assert.Equal(@this.StandardName, other.StandardName); + Assert.Equal(@this.DaylightName, other.DaylightName); + Assert.Equal(@this.BaseUtcOffset, other.BaseUtcOffset); + Assert.Equal(@this.SupportsDaylightSavingTime, other.SupportsDaylightSavingTime); + } + + public static void IsEqual(this Tuple @this, Tuple other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Item1, other.Item1); + } + + public static void IsEqual(this Tuple @this, Tuple other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Item1, other.Item1); + Assert.Equal(@this.Item2, other.Item2); + } + + public static void IsEqual(this Tuple @this, Tuple other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Item1, other.Item1); + Assert.Equal(@this.Item2, other.Item2); + Assert.Equal(@this.Item3, other.Item3); + } + + public static void IsEqual(this Tuple @this, Tuple other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Item1, other.Item1); + Assert.Equal(@this.Item2, other.Item2); + Assert.Equal(@this.Item3, other.Item3); + Assert.Equal(@this.Item4, other.Item4); + } + + public static void IsEqual(this Tuple @this, Tuple other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Item1, other.Item1); + Assert.Equal(@this.Item2, other.Item2); + Assert.Equal(@this.Item3, other.Item3); + Assert.Equal(@this.Item4, other.Item4); + Assert.Equal(@this.Item5, other.Item5); + } + + public static void IsEqual(this Tuple @this, Tuple other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Item1, other.Item1); + Assert.Equal(@this.Item2, other.Item2); + Assert.Equal(@this.Item3, other.Item3); + Assert.Equal(@this.Item4, other.Item4); + Assert.Equal(@this.Item5, other.Item5); + Assert.Equal(@this.Item6, other.Item6); + } + + public static void IsEqual(this Tuple @this, Tuple other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Item1, other.Item1); + Assert.Equal(@this.Item2, other.Item2); + Assert.Equal(@this.Item3, other.Item3); + Assert.Equal(@this.Item4, other.Item4); + Assert.Equal(@this.Item5, other.Item5); + Assert.Equal(@this.Item6, other.Item6); + Assert.Equal(@this.Item7, other.Item7); + } + + public static void IsEqual(this Tuple>> @this, Tuple>> other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Item1, other.Item1); + Assert.Equal(@this.Item2, other.Item2); + Assert.Equal(@this.Item3, other.Item3); + Assert.Equal(@this.Item4, other.Item4); + Assert.Equal(@this.Item5, other.Item5); + Assert.Equal(@this.Item6, other.Item6); + Assert.Equal(@this.Item7, other.Item7); + Assert.Equal(@this.Rest.Item1.Item1, other.Rest.Item1.Item1); + } + + public static void IsEqual(this Uri @this, Uri other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.AbsolutePath, other.AbsolutePath); + Assert.Equal(@this.AbsoluteUri, other.AbsoluteUri); + Assert.Equal(@this.LocalPath, other.LocalPath); + Assert.Equal(@this.Authority, other.Authority); + Assert.Equal(@this.HostNameType, other.HostNameType); + Assert.Equal(@this.IsDefaultPort, other.IsDefaultPort); + Assert.Equal(@this.IsFile, other.IsFile); + Assert.Equal(@this.IsLoopback, other.IsLoopback); + Assert.Equal(@this.PathAndQuery, other.PathAndQuery); + Assert.True(@this.Segments.SequenceEqual(other.Segments)); + Assert.Equal(@this.IsUnc, other.IsUnc); + Assert.Equal(@this.Host, other.Host); + Assert.Equal(@this.Port, other.Port); + Assert.Equal(@this.Query, other.Query); + Assert.Equal(@this.Fragment, other.Fragment); + Assert.Equal(@this.Scheme, other.Scheme); + Assert.Equal(@this.DnsSafeHost, other.DnsSafeHost); + Assert.Equal(@this.IdnHost, other.IdnHost); + Assert.Equal(@this.IsAbsoluteUri, other.IsAbsoluteUri); + Assert.Equal(@this.UserEscaped, other.UserEscaped); + Assert.Equal(@this.UserInfo, other.UserInfo); + } + + public static void IsEqual(this Version @this, Version other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + Assert.Equal(@this.Major, other.Major); + Assert.Equal(@this.Minor, other.Minor); + Assert.Equal(@this.Build, other.Build); + Assert.Equal(@this.Revision, other.Revision); + Assert.Equal(@this.MajorRevision, other.MajorRevision); + Assert.Equal(@this.MinorRevision, other.MinorRevision); + } + + public static void IsEqual(this Exception @this, Exception other, bool isSamePlatform) + { + if (@this == null && other == null) + return; + + Assert.NotNull(@this); + Assert.NotNull(other); + @this.Data.CheckSequenceEquals(other.Data, isSamePlatform); + + // Different by design for those exceptions + if (!((@this is SecurityException || + @this is XmlSyntaxException || + @this is ThreadAbortException || + @this is SqlException) && !isSamePlatform)) + { + if (!(@this is ActiveDirectoryServerDownException || + @this is SocketException || + @this is NetworkInformationException)) + { + Assert.Equal(@this.Message, other.Message); + } } - return CheckEquals(thisFlattened.Item2, otherFlattened.Item2); + // Different by design for those exceptions + if (!(@this is SqlException)) + { + Assert.Equal(@this.Source, other.Source); + } + + Assert.Equal(@this.HelpLink, other.HelpLink); + + // Different by design for those exceptions + if (!(@this is XmlSyntaxException && !isSamePlatform)) + { + CheckEquals(@this.InnerException, other.InnerException, isSamePlatform); + } + + if (!PlatformDetection.IsFullFramework && !PlatformDetection.IsNetNative) + { + // Different by design for those exceptions + if (!((@this is NetworkInformationException || @this is SocketException) && !isSamePlatform)) + { + Assert.Equal(@this.StackTrace, other.StackTrace); + } + + + // Different by design for those exceptions + if (!((@this is SecurityException || + @this is XmlSyntaxException || + @this is ThreadAbortException) && !isSamePlatform)) + { + if (!(@this is ActiveDirectoryServerDownException || + @this is SqlException || + @this is NetworkInformationException || + @this is SocketException)) + { + Assert.Equal(@this.ToString(), other.ToString()); + } + } + } + + if (!PlatformDetection.IsNetNative) + { + // Different by design for those exceptions + if (!((@this is NetworkInformationException || @this is SocketException) && !isSamePlatform)) + { + Assert.Equal(@this.HResult, other.HResult); + } + } } - public static bool IsEqual(this ArraySegment @this, ArraySegment other) - { - if (!((@this.Array != null) == (other.Array != null) && - @this.Count == other.Count && - @this.Offset == other.Offset)) - return false; - - return @this.Array == null || @this.CheckSequenceEquals(other); - } - - public static bool IsEqual(this ObjectWithArrays @this, ObjectWithArrays other) - { - return @this != null && - other != null && - CheckEquals(@this.IntArray, other.IntArray) && - CheckEquals(@this.StringArray, other.StringArray) && - CheckEquals(@this.TreeArray, other.TreeArray) && - CheckEquals(@this.ByteArray, other.ByteArray) && - CheckEquals(@this.JaggedArray, other.JaggedArray) && - CheckEquals(@this.MultiDimensionalArray, other.MultiDimensionalArray); - } - - public static bool IsEqual(this ObjectWithIntStringUShortUIntULongAndCustomObjectFields @this, ObjectWithIntStringUShortUIntULongAndCustomObjectFields other) - { - return @this != null && - other != null && - @this.Member1 == other.Member1 && - @this.Member2 == other.Member2 && - @this._member3 == other._member3 && - IsEqual(@this.Member4, other.Member4) && - IsEqual(@this.Member4shared, other.Member4shared) && - IsEqual(@this.Member5, other.Member5) && - @this.Member6 == other.Member6 && - @this.str1 == other.str1 && - @this.str2 == other.str2 && - @this.str3 == other.str3 && - @this.str4 == other.str4 && - @this.u16 == other.u16 && - @this.u32 == other.u32 && - @this.u64 == other.u64; - } - - public static bool IsEqual(this Point @this, Point other) + public static void IsEqual(this AggregateException @this, AggregateException other, bool isSamePlatform) { if (@this == null && other == null) - return true; + return; - return @this != null && - other != null && - @this.X == other.X && - @this.Y == other.Y; + Assert.NotNull(@this); + Assert.NotNull(other); + IsEqual(@this as Exception, other as Exception, isSamePlatform); + @this.InnerExceptions.CheckSequenceEquals(other.InnerExceptions, isSamePlatform); } - public static bool IsEqual(this SqlGuid @this, SqlGuid other) + public static void IsEqual(this EventArgs @this, EventArgs other, bool isSamePlatform) { - return @this.IsNull == other.IsNull && (@this.IsNull || @this.Value == other.Value); - } - - public static bool IsEqual(this SealedObjectWithIntStringFields @this, SealedObjectWithIntStringFields other) - { - return @this != null && - other != null && - @this.Member1 == other.Member1 && - @this.Member2 == other.Member2 && - @this.Member3 == other.Member3; - } - - public static bool IsEqual(this SimpleKeyedCollection @this, SimpleKeyedCollection other) - { - if (!(@this != null && - other != null && - @this.Comparer.Equals(other.Comparer) && - @this.Count == other.Count)) - return false; - - return @this.CheckSequenceEquals(other); - } - - public static bool IsEqual(this Tree @this, Tree other) - { - if (@this == null && other == null) - return true; - - return @this != null && - other != null && - @this.Value == other.Value && - IsEqual(@this.Left, other.Left) && - IsEqual(@this.Right, other.Right); - } - - public static bool IsEqual(this TimeZoneInfo @this, TimeZoneInfo other) - { - return @this != null && - other != null && - @this.Id == other.Id && - @this.DisplayName == other.DisplayName && - @this.StandardName == other.StandardName && - @this.DaylightName == other.DaylightName && - @this.BaseUtcOffset == other.BaseUtcOffset && - @this.SupportsDaylightSavingTime == other.SupportsDaylightSavingTime; - } - - public static bool IsEqual(this Tuple @this, Tuple other) - { - return @this != null && - other != null && - @this.Item1 == other.Item1; - } - - public static bool IsEqual(this Tuple @this, Tuple other) - { - return @this != null && - other != null && - @this.Item1 == other.Item1 && - @this.Item2 == other.Item2; - } - - public static bool IsEqual(this Tuple @this, Tuple other) - { - return @this != null && - other != null && - @this.Item1 == other.Item1 && - @this.Item2 == other.Item2 && - @this.Item3 == other.Item3; - } - - public static bool IsEqual(this Tuple @this, Tuple other) - { - return @this != null && - other != null && - @this.Item1 == other.Item1 && - @this.Item2 == other.Item2 && - @this.Item3 == other.Item3 && - @this.Item4 == other.Item4; - } - - public static bool IsEqual(this Tuple @this, Tuple other) - { - return @this != null && - other != null && - @this.Item1 == other.Item1 && - @this.Item2 == other.Item2 && - @this.Item3 == other.Item3 && - @this.Item4 == other.Item4 && - @this.Item5 == other.Item5; - } - - public static bool IsEqual(this Tuple @this, Tuple other) - { - return @this != null && - other != null && - @this.Item1 == other.Item1 && - @this.Item2 == other.Item2 && - @this.Item3 == other.Item3 && - @this.Item4 == other.Item4 && - @this.Item5 == other.Item5 && - @this.Item6 == other.Item6; - } - - public static bool IsEqual(this Tuple @this, Tuple other) - { - return @this != null && - other != null && - @this.Item1 == other.Item1 && - @this.Item2 == other.Item2 && - @this.Item3 == other.Item3 && - @this.Item4 == other.Item4 && - @this.Item5 == other.Item5 && - @this.Item6 == other.Item6 && - @this.Item7 == other.Item7; - } - - public static bool IsEqual(this Tuple>> @this, Tuple>> other) - { - return @this != null && - other != null && - @this.Item1 == other.Item1 && - @this.Item2 == other.Item2 && - @this.Item3 == other.Item3 && - @this.Item4 == other.Item4 && - @this.Item5 == other.Item5 && - @this.Item6 == other.Item6 && - @this.Item7 == other.Item7 && - @this.Rest.Item1.Item1 == other.Rest.Item1.Item1; - } - - public static bool IsEqual(this Uri @this, Uri other) - { - if (@this == null && other == null) - return true; - - return @this != null && - other != null && - @this.AbsolutePath == other.AbsolutePath && - @this.AbsoluteUri == other.AbsoluteUri && - @this.LocalPath == other.LocalPath && - @this.Authority == other.Authority && - @this.HostNameType == other.HostNameType && - @this.IsDefaultPort == other.IsDefaultPort && - @this.IsFile == other.IsFile && - @this.IsLoopback == other.IsLoopback && - @this.PathAndQuery == other.PathAndQuery && - @this.Segments.SequenceEqual(other.Segments) && - @this.IsUnc == other.IsUnc && - @this.Host == other.Host && - @this.Port == other.Port && - @this.Query == other.Query && - @this.Fragment == other.Fragment && - @this.Scheme == other.Scheme && - @this.DnsSafeHost == other.DnsSafeHost && - @this.IdnHost == other.IdnHost && - @this.IsAbsoluteUri == other.IsAbsoluteUri && - @this.UserEscaped == other.UserEscaped && - @this.UserInfo == other.UserInfo; - } - - public static bool IsEqual(this Version @this, Version other) - { - return @this != null && - other != null && - @this.Major == other.Major && - @this.Minor == other.Minor && - @this.Build == other.Build && - @this.Revision == other.Revision && - @this.MajorRevision == other.MajorRevision && - @this.MinorRevision == other.MinorRevision; - } - - public static bool IsEqual(this Exception @this, Exception other) - { - return @this != null && - other != null && - // On full framework, line number may be method body start - // On Net Native we can't reflect on Exceptions and change its StackTrace - ((PlatformDetection.IsFullFramework || PlatformDetection.IsNetNative) ? true : - (@this.StackTrace == other.StackTrace && - @this.ToString() == other.ToString())) && - @this.Data.CheckSequenceEquals(other.Data) && - @this.Message == other.Message && - @this.Source == other.Source && - // On Net Native we can't reflect on Exceptions and change its HResult - (PlatformDetection.IsNetNative ? true : @this.HResult == other.HResult) && - @this.HelpLink == other.HelpLink && - CheckEquals(@this.InnerException, other.InnerException); - } - - public static bool IsEqual(this AggregateException @this, AggregateException other) - { - return IsEqual(@this as Exception, other as Exception) && - @this.InnerExceptions.CheckSequenceEquals(other.InnerExceptions); + Assert.NotNull(@this); + Assert.NotNull(other); } public class ReferenceComparer : IEqualityComparer where T: class diff --git a/external/corefx/src/System.Runtime.Serialization.Formatters/tests/System.Runtime.Serialization.Formatters.Tests.csproj b/external/corefx/src/System.Runtime.Serialization.Formatters/tests/System.Runtime.Serialization.Formatters.Tests.csproj index 4dcd801d24..b59b9f9d5e 100644 --- a/external/corefx/src/System.Runtime.Serialization.Formatters/tests/System.Runtime.Serialization.Formatters.Tests.csproj +++ b/external/corefx/src/System.Runtime.Serialization.Formatters/tests/System.Runtime.Serialization.Formatters.Tests.csproj @@ -29,14 +29,18 @@ Common\System\Runtime\Serialization\Formatters\BinaryFormatterHelpers.cs + + + + + + + {69e46a6f-9966-45a5-8945-2559fe337827} RemoteExecutorConsoleApp - - - diff --git a/external/corefx/src/System.Runtime.Serialization.Json/tests/DataContractJsonSerializer.cs.REMOVED.git-id b/external/corefx/src/System.Runtime.Serialization.Json/tests/DataContractJsonSerializer.cs.REMOVED.git-id index 0a29e46e80..f956a59468 100644 --- a/external/corefx/src/System.Runtime.Serialization.Json/tests/DataContractJsonSerializer.cs.REMOVED.git-id +++ b/external/corefx/src/System.Runtime.Serialization.Json/tests/DataContractJsonSerializer.cs.REMOVED.git-id @@ -1 +1 @@ -3b859dcc55dcc1fe42e53cf46dd4eb55195475e3 \ No newline at end of file +986794616502117b81050eb0b95297064b925ecd \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Serialization.Primitives/src/System.Runtime.Serialization.Primitives.csproj b/external/corefx/src/System.Runtime.Serialization.Primitives/src/System.Runtime.Serialization.Primitives.csproj index 944f2e2e4e..607ba67551 100644 --- a/external/corefx/src/System.Runtime.Serialization.Primitives/src/System.Runtime.Serialization.Primitives.csproj +++ b/external/corefx/src/System.Runtime.Serialization.Primitives/src/System.Runtime.Serialization.Primitives.csproj @@ -11,7 +11,6 @@ {CDF0ACB5-1361-4E48-8ECB-22E8022F5F01} true - @@ -28,7 +27,6 @@ - diff --git a/external/corefx/src/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/InvalidDataContractException.Serialization.cs b/external/corefx/src/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/InvalidDataContractException.Serialization.cs deleted file mode 100644 index ea25d723cb..0000000000 --- a/external/corefx/src/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/InvalidDataContractException.Serialization.cs +++ /dev/null @@ -1,19 +0,0 @@ -// 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; - -namespace System.Runtime.Serialization -{ - [Serializable] - public partial class InvalidDataContractException : Exception - { - protected InvalidDataContractException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - throw new PlatformNotSupportedException(); - } - } -} - diff --git a/external/corefx/src/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/InvalidDataContractException.cs b/external/corefx/src/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/InvalidDataContractException.cs index afa8dfbf5b..fc660372d8 100644 --- a/external/corefx/src/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/InvalidDataContractException.cs +++ b/external/corefx/src/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/InvalidDataContractException.cs @@ -6,7 +6,9 @@ using System; namespace System.Runtime.Serialization { - public partial class InvalidDataContractException : Exception + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Runtime.Serialization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class InvalidDataContractException : Exception { public InvalidDataContractException() : base() @@ -22,5 +24,10 @@ namespace System.Runtime.Serialization : base(message, innerException) { } + + protected InvalidDataContractException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/external/corefx/src/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs.REMOVED.git-id b/external/corefx/src/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs.REMOVED.git-id index 0472bef766..be3bb409b5 100644 --- a/external/corefx/src/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs.REMOVED.git-id +++ b/external/corefx/src/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs.REMOVED.git-id @@ -1 +1 @@ -65c14ab817ffcb1d434c1403bd9dc6baacc7936b \ No newline at end of file +823d7e3759b9a513516cd177a2b3c460e9308c45 \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Serialization.Xml/tests/ReflectionOnly/System.Runtime.Serialization.Xml.ReflectionOnly.Tests.csproj b/external/corefx/src/System.Runtime.Serialization.Xml/tests/ReflectionOnly/System.Runtime.Serialization.Xml.ReflectionOnly.Tests.csproj index a60b5fdddb..bbb1216009 100644 --- a/external/corefx/src/System.Runtime.Serialization.Xml/tests/ReflectionOnly/System.Runtime.Serialization.Xml.ReflectionOnly.Tests.csproj +++ b/external/corefx/src/System.Runtime.Serialization.Xml/tests/ReflectionOnly/System.Runtime.Serialization.Xml.ReflectionOnly.Tests.csproj @@ -22,6 +22,9 @@ Common\System\IO\TempFile.cs + + Common\System\Runtime\Serialization\DataContractSerializerHelper.cs + SerializationTestTypes\ObjRefSample.cs diff --git a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/Collections.cs b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/Collections.cs index aed139d9dc..16f36f12c5 100644 --- a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/Collections.cs +++ b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/Collections.cs @@ -1,4 +1,8 @@ -using System; +// 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.Collections.Generic; using System.Runtime.Serialization; @@ -461,4 +465,4 @@ namespace SerializationTestTypes } #endregion -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/ComparisonHelper.cs b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/ComparisonHelper.cs index 92ef79fdd8..7f17d509c0 100644 --- a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/ComparisonHelper.cs +++ b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/ComparisonHelper.cs @@ -1,4 +1,8 @@ -using System; +// 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.Collections; using System.Diagnostics; using System.Reflection; diff --git a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DCRImplVariations.cs b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DCRImplVariations.cs index 38575d46df..5815f6b572 100644 --- a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DCRImplVariations.cs +++ b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DCRImplVariations.cs @@ -1,3 +1,7 @@ +// 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.Runtime.Serialization; using System.Xml; @@ -94,4 +98,4 @@ namespace SerializationTestTypes [DataMember] public int ZipCode; } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DCRSampleType.cs b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DCRSampleType.cs index 49eb8dbdbd..06e1e32381 100644 --- a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DCRSampleType.cs +++ b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DCRSampleType.cs @@ -1,4 +1,8 @@ -using System; +// 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.Collections.Generic; using System.Runtime.Serialization; using System.Xml; diff --git a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DCRTypeLibrary.cs b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DCRTypeLibrary.cs index d7186f8813..4e6ab243b5 100644 --- a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DCRTypeLibrary.cs +++ b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DCRTypeLibrary.cs @@ -1,4 +1,8 @@ -using System; +// 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.Collections; using System.Collections.Generic; using System.Runtime.Serialization; diff --git a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DataContract.cs b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DataContract.cs index 3b08048095..05d1183a13 100644 --- a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DataContract.cs +++ b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DataContract.cs @@ -1,3 +1,7 @@ +// 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.Collections; using System.Collections.Generic; diff --git a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DataContractResolverLibrary.cs b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DataContractResolverLibrary.cs index 0f4bfd0f5b..1bd96a3cb5 100644 --- a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DataContractResolverLibrary.cs +++ b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/DataContractResolverLibrary.cs @@ -1,4 +1,8 @@ -using System; +// 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.Collections.Generic; using System.Runtime.Serialization; using System.Xml; diff --git a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/InheritenceCases.cs b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/InheritenceCases.cs index 7f8ff062f2..5e85041238 100644 --- a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/InheritenceCases.cs +++ b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/InheritenceCases.cs @@ -1,4 +1,8 @@ -using System; +// 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.Runtime.Serialization; namespace SerializationTestTypes diff --git a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/InheritenceObjectRef.cs b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/InheritenceObjectRef.cs index 5bc9b145bc..787e901fc3 100644 --- a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/InheritenceObjectRef.cs +++ b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/InheritenceObjectRef.cs @@ -1,4 +1,8 @@ -using System; +// 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.Collections; using System.Collections.Generic; using System.Runtime.Serialization; @@ -570,4 +574,4 @@ namespace SerializationTestTypes RefData = SimpleDCWithRefData; } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/ObjRefSample.cs b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/ObjRefSample.cs index dcd88632b2..af7f97a261 100644 --- a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/ObjRefSample.cs +++ b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/ObjRefSample.cs @@ -1,4 +1,8 @@ -using System; +// 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.Runtime.Serialization; namespace SerializationTestTypes diff --git a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/Primitives.cs b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/Primitives.cs index 12fbd62c9c..a9ece16178 100644 --- a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/Primitives.cs +++ b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/Primitives.cs @@ -1,4 +1,8 @@ -using System; +// 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.Collections; using System.Collections.Generic; using System.Runtime.Serialization; diff --git a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/SampleIObjectRef.cs b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/SampleIObjectRef.cs index 0f71db0f9b..a934a481da 100644 --- a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/SampleIObjectRef.cs +++ b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/SampleIObjectRef.cs @@ -1,4 +1,8 @@ -using System; +// 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.Runtime.Serialization; namespace SerializationTestTypes diff --git a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/SampleTypes.cs.REMOVED.git-id b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/SampleTypes.cs.REMOVED.git-id index b2182762f7..bed1c5afb7 100644 --- a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/SampleTypes.cs.REMOVED.git-id +++ b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/SampleTypes.cs.REMOVED.git-id @@ -1 +1 @@ -fb958b20497afc6008ddb2a552427e692816a79b \ No newline at end of file +191315472c62489b9838239c37caa445a4360e76 \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/SelfRefAndCycles.cs b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/SelfRefAndCycles.cs index 89344be268..68f6266c16 100644 --- a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/SelfRefAndCycles.cs +++ b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/SelfRefAndCycles.cs @@ -1,4 +1,8 @@ -using System.Runtime.Serialization; +// 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.Runtime.Serialization; namespace SerializationTestTypes { diff --git a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTypes.RuntimeOnly.cs.REMOVED.git-id b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTypes.RuntimeOnly.cs.REMOVED.git-id index d0b8dad30b..1b2d265304 100644 --- a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTypes.RuntimeOnly.cs.REMOVED.git-id +++ b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTypes.RuntimeOnly.cs.REMOVED.git-id @@ -1 +1 @@ -3a7bba746b850908848db78f2af33d9698797046 \ No newline at end of file +c6f13b2839534f23dbe5da074c10c1fde0761401 \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs index 07ab269f50..8db9d14c14 100644 --- a/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs +++ b/external/corefx/src/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs @@ -1167,22 +1167,21 @@ public class DefaultValuesSetToNaN } } -public class TypeWithoutPublicSetter +[XmlRoot("RootElement")] +public class TypeWithMismatchBetweenAttributeAndPropertyType { - public string Name { get; private set; } + private int _intValue = 120; - [XmlIgnore] - public int Age { get; private set; } - - public Type MyType { get; private set; } - - public string ValidProperty { get; set; } - - public string PropertyWrapper + [DefaultValue(true), XmlAttribute("IntValue")] + public int IntValue { get { - return ValidProperty; + return _intValue; + } + set + { + _intValue = value; } } } \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj b/external/corefx/src/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj index 1e93b0e108..95fc9e94b3 100644 --- a/external/corefx/src/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj +++ b/external/corefx/src/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj @@ -26,6 +26,9 @@ Common\System\IO\TempFile.cs + + Common\System\Runtime\Serialization\DataContractSerializerHelper.cs + diff --git a/external/corefx/src/System.Runtime.WindowsRuntime.UI.Xaml/ref/System.Runtime.WindowsRuntime.UI.Xaml.cs b/external/corefx/src/System.Runtime.WindowsRuntime.UI.Xaml/ref/System.Runtime.WindowsRuntime.UI.Xaml.cs index 27a2ac43ea..e02872c640 100644 --- a/external/corefx/src/System.Runtime.WindowsRuntime.UI.Xaml/ref/System.Runtime.WindowsRuntime.UI.Xaml.cs +++ b/external/corefx/src/System.Runtime.WindowsRuntime.UI.Xaml/ref/System.Runtime.WindowsRuntime.UI.Xaml.cs @@ -8,30 +8,25 @@ namespace Windows.UI.Xaml { - [global::System.Security.SecurityCriticalAttribute] - [global::System.Runtime.InteropServices.StructLayoutAttribute(global::System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct CornerRadius { + private int _dummy; public CornerRadius(double uniformRadius) { throw null; } public CornerRadius(double topLeft, double topRight, double bottomRight, double bottomLeft) { throw null; } public double BottomLeft { get { throw null; } set { } } public double BottomRight { get { throw null; } set { } } public double TopLeft { get { throw null; } set { } } public double TopRight { get { throw null; } set { } } - [global::System.Security.SecuritySafeCriticalAttribute] public override bool Equals(object obj) { throw null; } public bool Equals(global::Windows.UI.Xaml.CornerRadius cornerRadius) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override int GetHashCode() { throw null; } public static bool operator ==(global::Windows.UI.Xaml.CornerRadius cr1, global::Windows.UI.Xaml.CornerRadius cr2) { throw null; } public static bool operator !=(global::Windows.UI.Xaml.CornerRadius cr1, global::Windows.UI.Xaml.CornerRadius cr2) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override string ToString() { throw null; } } - [global::System.Security.SecurityCriticalAttribute] - [global::System.Runtime.InteropServices.StructLayoutAttribute(global::System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Duration { + private int _dummy; public Duration(global::System.TimeSpan timeSpan) { throw null; } public static global::Windows.UI.Xaml.Duration Automatic { get { throw null; } } public static global::Windows.UI.Xaml.Duration Forever { get { throw null; } } @@ -39,11 +34,9 @@ namespace Windows.UI.Xaml public global::System.TimeSpan TimeSpan { get { throw null; } } public global::Windows.UI.Xaml.Duration Add(global::Windows.UI.Xaml.Duration duration) { throw null; } public static int Compare(global::Windows.UI.Xaml.Duration t1, global::Windows.UI.Xaml.Duration t2) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override bool Equals(object value) { throw null; } public bool Equals(global::Windows.UI.Xaml.Duration duration) { throw null; } public static bool Equals(global::Windows.UI.Xaml.Duration t1, global::Windows.UI.Xaml.Duration t2) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override int GetHashCode() { throw null; } public static global::Windows.UI.Xaml.Duration operator +(global::Windows.UI.Xaml.Duration t1, global::Windows.UI.Xaml.Duration t2) { throw null; } public static bool operator ==(global::Windows.UI.Xaml.Duration t1, global::Windows.UI.Xaml.Duration t2) { throw null; } @@ -56,20 +49,17 @@ namespace Windows.UI.Xaml public static global::Windows.UI.Xaml.Duration operator -(global::Windows.UI.Xaml.Duration t1, global::Windows.UI.Xaml.Duration t2) { throw null; } public static global::Windows.UI.Xaml.Duration operator +(global::Windows.UI.Xaml.Duration duration) { throw null; } public global::Windows.UI.Xaml.Duration Subtract(global::Windows.UI.Xaml.Duration duration) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override string ToString() { throw null; } } - [global::System.Security.SecurityCriticalAttribute] public enum DurationType { Automatic = 0, Forever = 2, TimeSpan = 1, } - [global::System.Security.SecurityCriticalAttribute] - [global::System.Runtime.InteropServices.StructLayoutAttribute(global::System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct GridLength { + private int _dummy; public GridLength(double pixels) { throw null; } public GridLength(double value, global::Windows.UI.Xaml.GridUnitType type) { throw null; } public static global::Windows.UI.Xaml.GridLength Auto { get { throw null; } } @@ -78,61 +68,50 @@ namespace Windows.UI.Xaml public bool IsAuto { get { throw null; } } public bool IsStar { get { throw null; } } public double Value { get { throw null; } } - [global::System.Security.SecuritySafeCriticalAttribute] public override bool Equals(object oCompare) { throw null; } public bool Equals(global::Windows.UI.Xaml.GridLength gridLength) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override int GetHashCode() { throw null; } public static bool operator ==(global::Windows.UI.Xaml.GridLength gl1, global::Windows.UI.Xaml.GridLength gl2) { throw null; } public static bool operator !=(global::Windows.UI.Xaml.GridLength gl1, global::Windows.UI.Xaml.GridLength gl2) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override string ToString() { throw null; } } - [global::System.Security.SecurityCriticalAttribute] public enum GridUnitType { Auto = 0, Pixel = 1, Star = 2, } - [global::System.Security.SecurityCriticalAttribute] public partial class LayoutCycleException : global::System.Exception { public LayoutCycleException() { } public LayoutCycleException(string message) { } public LayoutCycleException(string message, global::System.Exception innerException) { } } - [global::System.Security.SecurityCriticalAttribute] - [global::System.Runtime.InteropServices.StructLayoutAttribute(global::System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Thickness { + private int _dummy; public Thickness(double uniformLength) { throw null; } public Thickness(double left, double top, double right, double bottom) { throw null; } public double Bottom { get { throw null; } set { } } public double Left { get { throw null; } set { } } public double Right { get { throw null; } set { } } public double Top { get { throw null; } set { } } - [global::System.Security.SecuritySafeCriticalAttribute] public override bool Equals(object obj) { throw null; } public bool Equals(global::Windows.UI.Xaml.Thickness thickness) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override int GetHashCode() { throw null; } public static bool operator ==(global::Windows.UI.Xaml.Thickness t1, global::Windows.UI.Xaml.Thickness t2) { throw null; } public static bool operator !=(global::Windows.UI.Xaml.Thickness t1, global::Windows.UI.Xaml.Thickness t2) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override string ToString() { throw null; } } } namespace Windows.UI.Xaml.Automation { - [global::System.Security.SecurityCriticalAttribute] public partial class ElementNotAvailableException : global::System.Exception { public ElementNotAvailableException() { } public ElementNotAvailableException(string message) { } public ElementNotAvailableException(string message, global::System.Exception innerException) { } } - [global::System.Security.SecurityCriticalAttribute] public partial class ElementNotEnabledException : global::System.Exception { public ElementNotEnabledException() { } @@ -142,26 +121,21 @@ namespace Windows.UI.Xaml.Automation } namespace Windows.UI.Xaml.Controls.Primitives { - [global::System.Security.SecurityCriticalAttribute] - [global::System.Runtime.InteropServices.StructLayoutAttribute(global::System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct GeneratorPosition { + private int _dummy; public GeneratorPosition(int index, int offset) { throw null; } public int Index { get { throw null; } set { } } public int Offset { get { throw null; } set { } } - [global::System.Security.SecuritySafeCriticalAttribute] public override bool Equals(object o) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override int GetHashCode() { throw null; } public static bool operator ==(global::Windows.UI.Xaml.Controls.Primitives.GeneratorPosition gp1, global::Windows.UI.Xaml.Controls.Primitives.GeneratorPosition gp2) { throw null; } public static bool operator !=(global::Windows.UI.Xaml.Controls.Primitives.GeneratorPosition gp1, global::Windows.UI.Xaml.Controls.Primitives.GeneratorPosition gp2) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override string ToString() { throw null; } } } namespace Windows.UI.Xaml.Markup { - [global::System.Security.SecurityCriticalAttribute] public partial class XamlParseException : global::System.Exception { public XamlParseException() { } @@ -171,10 +145,9 @@ namespace Windows.UI.Xaml.Markup } namespace Windows.UI.Xaml.Media { - [global::System.Security.SecurityCriticalAttribute] - [global::System.Runtime.InteropServices.StructLayoutAttribute(global::System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Matrix : global::System.IFormattable { + private int _dummy; public Matrix(double m11, double m12, double m21, double m22, double offsetX, double offsetY) { throw null; } public static global::Windows.UI.Xaml.Media.Matrix Identity { get { throw null; } } public bool IsIdentity { get { throw null; } } @@ -184,16 +157,12 @@ namespace Windows.UI.Xaml.Media public double M22 { get { throw null; } set { } } public double OffsetX { get { throw null; } set { } } public double OffsetY { get { throw null; } set { } } - [global::System.Security.SecuritySafeCriticalAttribute] public override bool Equals(object o) { throw null; } public bool Equals(global::Windows.UI.Xaml.Media.Matrix value) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override int GetHashCode() { throw null; } public static bool operator ==(global::Windows.UI.Xaml.Media.Matrix matrix1, global::Windows.UI.Xaml.Media.Matrix matrix2) { throw null; } public static bool operator !=(global::Windows.UI.Xaml.Media.Matrix matrix1, global::Windows.UI.Xaml.Media.Matrix matrix2) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] string System.IFormattable.ToString(string format, global::System.IFormatProvider provider) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override string ToString() { throw null; } public string ToString(global::System.IFormatProvider provider) { throw null; } public global::Windows.Foundation.Point Transform(global::Windows.Foundation.Point point) { throw null; } @@ -201,28 +170,23 @@ namespace Windows.UI.Xaml.Media } namespace Windows.UI.Xaml.Media.Animation { - [global::System.Security.SecurityCriticalAttribute] - [global::System.Runtime.InteropServices.StructLayoutAttribute(global::System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct KeyTime { + private int _dummy; public global::System.TimeSpan TimeSpan { get { throw null; } } - [global::System.Security.SecuritySafeCriticalAttribute] public override bool Equals(object value) { throw null; } public bool Equals(global::Windows.UI.Xaml.Media.Animation.KeyTime value) { throw null; } public static bool Equals(global::Windows.UI.Xaml.Media.Animation.KeyTime keyTime1, global::Windows.UI.Xaml.Media.Animation.KeyTime keyTime2) { throw null; } public static global::Windows.UI.Xaml.Media.Animation.KeyTime FromTimeSpan(global::System.TimeSpan timeSpan) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override int GetHashCode() { throw null; } public static bool operator ==(global::Windows.UI.Xaml.Media.Animation.KeyTime keyTime1, global::Windows.UI.Xaml.Media.Animation.KeyTime keyTime2) { throw null; } public static implicit operator global::Windows.UI.Xaml.Media.Animation.KeyTime(global::System.TimeSpan timeSpan) { throw null; } public static bool operator !=(global::Windows.UI.Xaml.Media.Animation.KeyTime keyTime1, global::Windows.UI.Xaml.Media.Animation.KeyTime keyTime2) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override string ToString() { throw null; } } - [global::System.Security.SecurityCriticalAttribute] - [global::System.Runtime.InteropServices.StructLayoutAttribute(global::System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct RepeatBehavior : global::System.IFormattable { + private int _dummy; public RepeatBehavior(double count) { throw null; } public RepeatBehavior(global::System.TimeSpan duration) { throw null; } public double Count { get { throw null; } set { } } @@ -231,21 +195,16 @@ namespace Windows.UI.Xaml.Media.Animation public bool HasCount { get { throw null; } } public bool HasDuration { get { throw null; } } public global::Windows.UI.Xaml.Media.Animation.RepeatBehaviorType Type { get { throw null; } set { } } - [global::System.Security.SecuritySafeCriticalAttribute] public override bool Equals(object value) { throw null; } public bool Equals(global::Windows.UI.Xaml.Media.Animation.RepeatBehavior repeatBehavior) { throw null; } public static bool Equals(global::Windows.UI.Xaml.Media.Animation.RepeatBehavior repeatBehavior1, global::Windows.UI.Xaml.Media.Animation.RepeatBehavior repeatBehavior2) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override int GetHashCode() { throw null; } public static bool operator ==(global::Windows.UI.Xaml.Media.Animation.RepeatBehavior repeatBehavior1, global::Windows.UI.Xaml.Media.Animation.RepeatBehavior repeatBehavior2) { throw null; } public static bool operator !=(global::Windows.UI.Xaml.Media.Animation.RepeatBehavior repeatBehavior1, global::Windows.UI.Xaml.Media.Animation.RepeatBehavior repeatBehavior2) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] string System.IFormattable.ToString(string format, global::System.IFormatProvider formatProvider) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override string ToString() { throw null; } public string ToString(global::System.IFormatProvider formatProvider) { throw null; } } - [global::System.Security.SecurityCriticalAttribute] public enum RepeatBehaviorType { Count = 0, @@ -255,10 +214,9 @@ namespace Windows.UI.Xaml.Media.Animation } namespace Windows.UI.Xaml.Media.Media3D { - [global::System.Security.SecurityCriticalAttribute] - [global::System.Runtime.InteropServices.StructLayoutAttribute(global::System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Matrix3D : global::System.IFormattable { + private int _dummy; public Matrix3D(double m11, double m12, double m13, double m14, double m21, double m22, double m23, double m24, double m31, double m32, double m33, double m34, double offsetX, double offsetY, double offsetZ, double m44) { throw null; } public bool HasInverse { get { throw null; } } public static global::Windows.UI.Xaml.Media.Media3D.Matrix3D Identity { get { throw null; } } @@ -279,18 +237,14 @@ namespace Windows.UI.Xaml.Media.Media3D public double OffsetX { get { throw null; } set { } } public double OffsetY { get { throw null; } set { } } public double OffsetZ { get { throw null; } set { } } - [global::System.Security.SecuritySafeCriticalAttribute] public override bool Equals(object o) { throw null; } public bool Equals(global::Windows.UI.Xaml.Media.Media3D.Matrix3D value) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override int GetHashCode() { throw null; } public void Invert() { } public static bool operator ==(global::Windows.UI.Xaml.Media.Media3D.Matrix3D matrix1, global::Windows.UI.Xaml.Media.Media3D.Matrix3D matrix2) { throw null; } public static bool operator !=(global::Windows.UI.Xaml.Media.Media3D.Matrix3D matrix1, global::Windows.UI.Xaml.Media.Media3D.Matrix3D matrix2) { throw null; } public static global::Windows.UI.Xaml.Media.Media3D.Matrix3D operator *(global::Windows.UI.Xaml.Media.Media3D.Matrix3D matrix1, global::Windows.UI.Xaml.Media.Media3D.Matrix3D matrix2) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] string System.IFormattable.ToString(string format, global::System.IFormatProvider provider) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override string ToString() { throw null; } public string ToString(global::System.IFormatProvider provider) { throw null; } } diff --git a/external/corefx/src/System.Runtime.WindowsRuntime.UI.Xaml/src/System.Runtime.WindowsRuntime.UI.Xaml.csproj b/external/corefx/src/System.Runtime.WindowsRuntime.UI.Xaml/src/System.Runtime.WindowsRuntime.UI.Xaml.csproj index 62381faf59..29b72e0f00 100644 --- a/external/corefx/src/System.Runtime.WindowsRuntime.UI.Xaml/src/System.Runtime.WindowsRuntime.UI.Xaml.csproj +++ b/external/corefx/src/System.Runtime.WindowsRuntime.UI.Xaml/src/System.Runtime.WindowsRuntime.UI.Xaml.csproj @@ -37,4 +37,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.WindowsRuntime/ref/System.Runtime.WindowsRuntime.Manual.cs b/external/corefx/src/System.Runtime.WindowsRuntime/ref/System.Runtime.WindowsRuntime.Manual.cs index 0ff07e31a5..dafbd53c95 100644 --- a/external/corefx/src/System.Runtime.WindowsRuntime/ref/System.Runtime.WindowsRuntime.Manual.cs +++ b/external/corefx/src/System.Runtime.WindowsRuntime/ref/System.Runtime.WindowsRuntime.Manual.cs @@ -18,12 +18,10 @@ namespace Windows.Foundation { public partial struct Point : IFormattable { - [SecuritySafeCriticalAttribute] string IFormattable.ToString(string format, IFormatProvider provider) { throw null; } } public partial struct Rect : IFormattable { - [SecuritySafeCriticalAttribute] string IFormattable.ToString(string format, IFormatProvider provider) { throw null; } } } @@ -31,7 +29,6 @@ namespace Windows.UI { public partial struct Color : IFormattable { - [SecuritySafeCriticalAttribute] string IFormattable.ToString(string format, IFormatProvider provider) { throw null; } } } diff --git a/external/corefx/src/System.Runtime.WindowsRuntime/ref/System.Runtime.WindowsRuntime.cs b/external/corefx/src/System.Runtime.WindowsRuntime/ref/System.Runtime.WindowsRuntime.cs index a517747d7b..408ac71df9 100644 --- a/external/corefx/src/System.Runtime.WindowsRuntime/ref/System.Runtime.WindowsRuntime.cs +++ b/external/corefx/src/System.Runtime.WindowsRuntime/ref/System.Runtime.WindowsRuntime.cs @@ -9,7 +9,6 @@ namespace System { [global::System.CLSCompliantAttribute(false)] - [global::System.Security.SecurityCriticalAttribute] public static partial class WindowsRuntimeSystemExtensions { public static global::Windows.Foundation.IAsyncAction AsAsyncAction(this global::System.Threading.Tasks.Task source) { throw null; } @@ -38,7 +37,6 @@ namespace System } namespace System.IO { - [global::System.Security.SecurityCriticalAttribute] public static partial class WindowsRuntimeStorageExtensions { [global::System.CLSCompliantAttribute(false)] @@ -69,7 +67,6 @@ namespace System.IO global::System.IO.FileShare share = global::System.IO.FileShare.Read, global::System.IO.FileOptions options = global::System.IO.FileOptions.None) { throw null; } } - [global::System.Security.SecurityCriticalAttribute] public static partial class WindowsRuntimeStreamExtensions { [global::System.CLSCompliantAttribute(false)] @@ -95,7 +92,6 @@ namespace System.IO namespace System.Runtime.InteropServices.WindowsRuntime { [global::System.CLSCompliantAttribute(false)] - [global::System.Security.SecurityCriticalAttribute] public static partial class AsyncInfo { public static global::Windows.Foundation.IAsyncAction Run(global::System.Func taskProvider) { throw null; } @@ -103,7 +99,6 @@ namespace System.Runtime.InteropServices.WindowsRuntime public static global::Windows.Foundation.IAsyncOperation Run(global::System.Func> taskProvider) { throw null; } public static global::Windows.Foundation.IAsyncOperationWithProgress Run(global::System.Func, global::System.Threading.Tasks.Task> taskProvider) { throw null; } } - [global::System.Security.SecurityCriticalAttribute] public sealed partial class WindowsRuntimeBuffer { internal WindowsRuntimeBuffer() { } @@ -112,7 +107,6 @@ namespace System.Runtime.InteropServices.WindowsRuntime [global::System.CLSCompliantAttribute(false)] public static global::Windows.Storage.Streams.IBuffer Create(int capacity) { throw null; } } - [global::System.Security.SecurityCriticalAttribute] public static partial class WindowsRuntimeBufferExtensions { [global::System.CLSCompliantAttribute(false)] @@ -151,28 +145,23 @@ namespace System.Runtime.InteropServices.WindowsRuntime } namespace Windows.Foundation { - [global::System.Security.SecurityCriticalAttribute] - [global::System.Runtime.InteropServices.StructLayoutAttribute(global::System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Point { + private int _dummy; public Point(double x, double y) { throw null; } public double X { get { throw null; } set { } } public double Y { get { throw null; } set { } } - [global::System.Security.SecuritySafeCriticalAttribute] public override bool Equals(object o) { throw null; } public bool Equals(global::Windows.Foundation.Point value) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override int GetHashCode() { throw null; } public static bool operator ==(global::Windows.Foundation.Point point1, global::Windows.Foundation.Point point2) { throw null; } public static bool operator !=(global::Windows.Foundation.Point point1, global::Windows.Foundation.Point point2) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override string ToString() { throw null; } public string ToString(global::System.IFormatProvider provider) { throw null; } } - [global::System.Security.SecurityCriticalAttribute] - [global::System.Runtime.InteropServices.StructLayoutAttribute(global::System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Rect { + private int _dummy; public Rect(double x, double y, double width, double height) { throw null; } public Rect(global::Windows.Foundation.Point point1, global::Windows.Foundation.Point point2) { throw null; } public Rect(global::Windows.Foundation.Point location, global::Windows.Foundation.Size size) { throw null; } @@ -187,59 +176,48 @@ namespace Windows.Foundation public double X { get { throw null; } set { } } public double Y { get { throw null; } set { } } public bool Contains(global::Windows.Foundation.Point point) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override bool Equals(object o) { throw null; } public bool Equals(global::Windows.Foundation.Rect value) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override int GetHashCode() { throw null; } public void Intersect(global::Windows.Foundation.Rect rect) { } public static bool operator ==(global::Windows.Foundation.Rect rect1, global::Windows.Foundation.Rect rect2) { throw null; } public static bool operator !=(global::Windows.Foundation.Rect rect1, global::Windows.Foundation.Rect rect2) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override string ToString() { throw null; } public string ToString(global::System.IFormatProvider provider) { throw null; } public void Union(global::Windows.Foundation.Point point) { } public void Union(global::Windows.Foundation.Rect rect) { } } - [global::System.Security.SecurityCriticalAttribute] - [global::System.Runtime.InteropServices.StructLayoutAttribute(global::System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Size { + private int _dummy; public Size(double width, double height) { throw null; } public static global::Windows.Foundation.Size Empty { get { throw null; } } public double Height { get { throw null; } set { } } public bool IsEmpty { get { throw null; } } public double Width { get { throw null; } set { } } - [global::System.Security.SecuritySafeCriticalAttribute] public override bool Equals(object o) { throw null; } public bool Equals(global::Windows.Foundation.Size value) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override int GetHashCode() { throw null; } public static bool operator ==(global::Windows.Foundation.Size size1, global::Windows.Foundation.Size size2) { throw null; } public static bool operator !=(global::Windows.Foundation.Size size1, global::Windows.Foundation.Size size2) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override string ToString() { throw null; } } } namespace Windows.UI { - [global::System.Security.SecurityCriticalAttribute] - [global::System.Runtime.InteropServices.StructLayoutAttribute(global::System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct Color { + private int _dummy; public byte A { get { throw null; } set { } } public byte B { get { throw null; } set { } } public byte G { get { throw null; } set { } } public byte R { get { throw null; } set { } } - [global::System.Security.SecuritySafeCriticalAttribute] public override bool Equals(object o) { throw null; } public bool Equals(global::Windows.UI.Color color) { throw null; } public static global::Windows.UI.Color FromArgb(byte a, byte r, byte g, byte b) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override int GetHashCode() { throw null; } public static bool operator ==(global::Windows.UI.Color color1, global::Windows.UI.Color color2) { throw null; } public static bool operator !=(global::Windows.UI.Color color1, global::Windows.UI.Color color2) { throw null; } - [global::System.Security.SecuritySafeCriticalAttribute] public override string ToString() { throw null; } public string ToString(global::System.IFormatProvider provider) { throw null; } } diff --git a/external/corefx/src/System.Runtime.WindowsRuntime/ref/System.Runtime.WindowsRuntime.csproj b/external/corefx/src/System.Runtime.WindowsRuntime/ref/System.Runtime.WindowsRuntime.csproj index 02e0a93c09..fd48a9a229 100644 --- a/external/corefx/src/System.Runtime.WindowsRuntime/ref/System.Runtime.WindowsRuntime.csproj +++ b/external/corefx/src/System.Runtime.WindowsRuntime/ref/System.Runtime.WindowsRuntime.csproj @@ -11,7 +11,7 @@ Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' does not match the output assembly name 'System.Runtime.WindowsRuntime, Version=4.0.10.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. --> - 1698 + $(NoWarn);1698 {FDDA3E4A-B182-4CD1-B624-EBD72D8A05DA} diff --git a/external/corefx/src/System.Runtime.WindowsRuntime/src/Resources/Strings.resx b/external/corefx/src/System.Runtime.WindowsRuntime/src/Resources/Strings.resx index 33ffef0ad7..de230dbdd3 100644 --- a/external/corefx/src/System.Runtime.WindowsRuntime/src/Resources/Strings.resx +++ b/external/corefx/src/System.Runtime.WindowsRuntime/src/Resources/Strings.resx @@ -1,5 +1,64 @@  + @@ -274,4 +333,7 @@ The file '{0}' already exists. - + + The path '{0}' is too long, or a component of the specified path is too long. + + \ No newline at end of file diff --git a/external/corefx/src/System.Runtime.WindowsRuntime/src/System.Runtime.WindowsRuntime.csproj b/external/corefx/src/System.Runtime.WindowsRuntime/src/System.Runtime.WindowsRuntime.csproj index af91b457d0..79e53dc4db 100644 --- a/external/corefx/src/System.Runtime.WindowsRuntime/src/System.Runtime.WindowsRuntime.csproj +++ b/external/corefx/src/System.Runtime.WindowsRuntime/src/System.Runtime.WindowsRuntime.csproj @@ -76,6 +76,7 @@ + @@ -96,7 +97,6 @@ Common\System\IO\Win32Marshal.cs - @@ -130,4 +130,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/IO/IsolatedStorage/IsolatedStorageFileIOHelper.cs b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/IO/IsolatedStorage/IsolatedStorageFileIOHelper.cs deleted file mode 100644 index 87eab9bdce..0000000000 --- a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/IO/IsolatedStorage/IsolatedStorageFileIOHelper.cs +++ /dev/null @@ -1,38 +0,0 @@ -// 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.Runtime.CompilerServices; -using System.Security; -using System.Threading.Tasks; -using Windows.Storage; - -namespace System.IO.IsolatedStorage -{ - [FriendAccessAllowed] - internal class IsolatedStorageFileIOHelper : IsolatedStorageFileIOHelperBase - { - internal override void UnsafeMoveFile(string sourceFileFullPath, string destinationFileFullPath) - { - if (Directory.Exists(destinationFileFullPath)) - { - string destinationFileName = Path.GetFileName(sourceFileFullPath); - Task.Run(() => { UnsafeMoveFileAsync(sourceFileFullPath, destinationFileFullPath, destinationFileName).Wait(); }).Wait(); - } - else - { - string destinationFileName = Path.GetFileName(destinationFileFullPath); - string destinationDirectoryFullPath = Path.GetDirectoryName(destinationFileFullPath); - Task.Run(() => { UnsafeMoveFileAsync(sourceFileFullPath, destinationDirectoryFullPath, destinationFileName).Wait(); }).Wait(); - } - } - - private static async Task UnsafeMoveFileAsync(string sourceFileFullPath, string destinationFolderFullPath, string destinationFileName) - { - StorageFile sourceStorageFile = await StorageFile.GetFileFromPathAsync(sourceFileFullPath).ConfigureAwait(false); - StorageFolder destinationStorageFolder = await StorageFolder.GetFolderFromPathAsync(destinationFolderFullPath).ConfigureAwait(false); - await sourceStorageFile.MoveAsync(destinationStorageFolder, destinationFileName, NameCollisionOption.FailIfExists).ConfigureAwait(false); - } - } -} diff --git a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/IO/NetFxToWinRtStreamAdapter.cs b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/IO/NetFxToWinRtStreamAdapter.cs index 4bbf2fc978..3ee7f1755f 100644 --- a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/IO/NetFxToWinRtStreamAdapter.cs +++ b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/IO/NetFxToWinRtStreamAdapter.cs @@ -7,7 +7,6 @@ using System.Diagnostics.Contracts; using System.IO; using System.Runtime.InteropServices.WindowsRuntime; using System.Runtime.InteropServices; -using System.Runtime.WindowsRuntime.Internal; using System.Threading.Tasks; using System.Threading; using Windows.Foundation; diff --git a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/IO/WinRtToNetFxStreamAdapter.cs b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/IO/WinRtToNetFxStreamAdapter.cs index 0fb672027b..ca3a4e4d5b 100644 --- a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/IO/WinRtToNetFxStreamAdapter.cs +++ b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/IO/WinRtToNetFxStreamAdapter.cs @@ -6,7 +6,6 @@ using System.Diagnostics; using System.Diagnostics.Contracts; using System.Runtime.InteropServices.WindowsRuntime; using System.Runtime.InteropServices; -using System.Runtime.WindowsRuntime.Internal; using System.Threading.Tasks; using System.Threading; using Windows.Foundation; diff --git a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/InternalHelpers.cs b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/InternalHelpers.cs index b28a9cb163..43fbe0f017 100644 --- a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/InternalHelpers.cs +++ b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/InternalHelpers.cs @@ -5,30 +5,10 @@ using System.Threading.Tasks; using System.Threading; -namespace System.Runtime.WindowsRuntime.Internal +namespace System { - internal static class __Error + internal static class InternalHelpers { - internal static void StreamIsClosed() - { - throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); - } - - internal static void SeekNotSupported() - { - throw new NotSupportedException(SR.NotSupported_UnseekableStream); - } - - internal static void ReadNotSupported() - { - throw new NotSupportedException(SR.NotSupported_UnreadableStream); - } - - internal static void WriteNotSupported() - { - throw new NotSupportedException(SR.NotSupported_UnwritableStream); - } - internal static void SetErrorCode(this Exception ex, int code) { // Stub, until COM interop guys fix the exception logic @@ -39,13 +19,4 @@ namespace System.Runtime.WindowsRuntime.Internal //nothing to do for projectN } } - - internal class Helpers - { - internal static unsafe void ZeroMemory(byte* src, long len) - { - while (len-- > 0) - *(src + len) = 0; - } - } } diff --git a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Resources/WindowsRuntimeResourceManager.cs b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Resources/WindowsRuntimeResourceManager.cs index 6e3f67857b..ff81e32ddb 100644 --- a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Resources/WindowsRuntimeResourceManager.cs +++ b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Resources/WindowsRuntimeResourceManager.cs @@ -18,7 +18,6 @@ using Windows.Storage; namespace System.Resources { #if FEATURE_APPX - [FriendAccessAllowed] // Please see the comments regarding thread safety preceding the implementations // of Initialize() and GetString() below. internal sealed class WindowsRuntimeResourceManager : WindowsRuntimeResourceManagerBase diff --git a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Runtime/InteropServices/WindowsRuntime/MarshalingHelpers.cs b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Runtime/InteropServices/WindowsRuntime/MarshalingHelpers.cs index 8fa8c7217a..c17dcd15c4 100644 --- a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Runtime/InteropServices/WindowsRuntime/MarshalingHelpers.cs +++ b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Runtime/InteropServices/WindowsRuntime/MarshalingHelpers.cs @@ -11,6 +11,8 @@ using System.Runtime.CompilerServices; using System.Security; using System.Windows.Input; +using Internal.Runtime.CompilerServices; + namespace System.Runtime.InteropServices.WindowsRuntime { // Local definition of Windows.UI.Xaml.Interop.INotifyCollectionChangedEventArgs diff --git a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeBuffer.cs b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeBuffer.cs index 070b2b6adf..e31f6326ac 100644 --- a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeBuffer.cs +++ b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeBuffer.cs @@ -6,7 +6,6 @@ using System.Diagnostics; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.WindowsRuntime.Internal; using System.Security; using System.Threading; using Windows.Foundation; diff --git a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeBufferExtensions.cs b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeBufferExtensions.cs index 5f588d11d7..c51cd2849d 100644 --- a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeBufferExtensions.cs +++ b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeBufferExtensions.cs @@ -5,7 +5,6 @@ using System.Diagnostics.Contracts; using System.Diagnostics; using System.IO; -using System.Runtime.WindowsRuntime.Internal; using Windows.Foundation; using Windows.Storage.Streams; diff --git a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Threading/Tasks/AsyncInfoToTaskBridge.CoreRT.cs b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Threading/Tasks/AsyncInfoToTaskBridge.CoreRT.cs index 1ea0aba3f4..eec0c38d48 100644 --- a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Threading/Tasks/AsyncInfoToTaskBridge.CoreRT.cs +++ b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Threading/Tasks/AsyncInfoToTaskBridge.CoreRT.cs @@ -11,7 +11,6 @@ using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Runtime.InteropServices.WindowsRuntime; using System.Runtime.InteropServices; -using System.Runtime.WindowsRuntime.Internal; using System.Threading; using Windows.Foundation; diff --git a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Threading/Tasks/TaskToAsyncInfoAdapter.cs b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Threading/Tasks/TaskToAsyncInfoAdapter.cs index 9b9c8880b8..6f17498e38 100644 --- a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Threading/Tasks/TaskToAsyncInfoAdapter.cs +++ b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Threading/Tasks/TaskToAsyncInfoAdapter.cs @@ -9,7 +9,6 @@ using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; using System.Threading; using Windows.Foundation; -using System.Runtime.WindowsRuntime.Internal; namespace System.Threading.Tasks { diff --git a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Threading/WindowsRuntimeSynchronizationContext.cs b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Threading/WindowsRuntimeSynchronizationContext.cs index e4e9274c32..d59269e2fe 100644 --- a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Threading/WindowsRuntimeSynchronizationContext.cs +++ b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/Threading/WindowsRuntimeSynchronizationContext.cs @@ -48,7 +48,6 @@ namespace System.Threading #region class WinRTSynchronizationContextFactory - [FriendAccessAllowed] internal sealed class WinRTSynchronizationContextFactory : WinRTSynchronizationContextFactoryBase { // @@ -115,7 +114,6 @@ namespace System.Threading _dispatcher = dispatcher; } - [SecuritySafeCritical] public override void Post(SendOrPostCallback d, object state) { if (d == null) @@ -139,7 +137,6 @@ namespace System.Threading _dispatcherQueue = dispatcherQueue; } - [SecuritySafeCritical] public override void Post(SendOrPostCallback d, object state) { if (d == null) @@ -281,7 +278,6 @@ namespace System.Threading #endregion class WinRTSynchronizationContext.Invoker - [SecuritySafeCritical] public override void Send(SendOrPostCallback d, object state) { throw new NotSupportedException(SR.InvalidOperation_SendNotSupportedOnWindowsRTSynchronizationContext); diff --git a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/WindowsRuntimeSystemExtensions.CoreRT.cs b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/WindowsRuntimeSystemExtensions.CoreRT.cs index a465dd597c..139e6c941d 100644 --- a/external/corefx/src/System.Runtime.WindowsRuntime/src/System/WindowsRuntimeSystemExtensions.CoreRT.cs +++ b/external/corefx/src/System.Runtime.WindowsRuntime/src/System/WindowsRuntimeSystemExtensions.CoreRT.cs @@ -6,7 +6,6 @@ using System.ComponentModel; using System.Diagnostics.Contracts; using System.Diagnostics; using System.Runtime.CompilerServices; -using System.Runtime.WindowsRuntime.Internal; using System.Threading.Tasks; using System.Threading; using Windows.Foundation; diff --git a/external/corefx/src/System.Runtime/System.Runtime.sln b/external/corefx/src/System.Runtime/System.Runtime.sln index c4830a9773..077d109061 100644 --- a/external/corefx/src/System.Runtime/System.Runtime.sln +++ b/external/corefx/src/System.Runtime/System.Runtime.sln @@ -64,10 +64,10 @@ Global {3A3DE9F8-6295-4B68-BAAB-406F1CA0CD9C}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU {3A3DE9F8-6295-4B68-BAAB-406F1CA0CD9C}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU {3A3DE9F8-6295-4B68-BAAB-406F1CA0CD9C}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU - {E9560F70-79F5-453A-B518-0292CFE4A6AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E9560F70-79F5-453A-B518-0292CFE4A6AD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E9560F70-79F5-453A-B518-0292CFE4A6AD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E9560F70-79F5-453A-B518-0292CFE4A6AD}.Release|Any CPU.Build.0 = Release|Any CPU + {E9560F70-79F5-453A-B518-0292CFE4A6AD}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {E9560F70-79F5-453A-B518-0292CFE4A6AD}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {E9560F70-79F5-453A-B518-0292CFE4A6AD}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {E9560F70-79F5-453A-B518-0292CFE4A6AD}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU {2EF7D710-A7BD-4BB3-8EF6-3F0298CD4986}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU {2EF7D710-A7BD-4BB3-8EF6-3F0298CD4986}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU {2EF7D710-A7BD-4BB3-8EF6-3F0298CD4986}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU diff --git a/external/corefx/src/System.Runtime/ref/System.Runtime.Manual.cs b/external/corefx/src/System.Runtime/ref/System.Runtime.Manual.cs index 2003d97e1d..b5f4565170 100644 --- a/external/corefx/src/System.Runtime/ref/System.Runtime.Manual.cs +++ b/external/corefx/src/System.Runtime/ref/System.Runtime.Manual.cs @@ -44,6 +44,6 @@ namespace System.Reflection // In the meantime, these will be exposed via wrapper factory methods in System.Private.Reflection.Extensibility. public partial struct CustomAttributeNamedArgument { - internal CustomAttributeNamedArgument(Type attributeType, string memberName, bool isField, CustomAttributeTypedArgument typedValue) { } + internal CustomAttributeNamedArgument(Type attributeType, string memberName, bool isField, CustomAttributeTypedArgument typedValue) { throw null; } } } diff --git a/external/corefx/src/System.Runtime/ref/System.Runtime.cs.REMOVED.git-id b/external/corefx/src/System.Runtime/ref/System.Runtime.cs.REMOVED.git-id index 5927ecc76a..04a819f2de 100644 --- a/external/corefx/src/System.Runtime/ref/System.Runtime.cs.REMOVED.git-id +++ b/external/corefx/src/System.Runtime/ref/System.Runtime.cs.REMOVED.git-id @@ -1 +1 @@ -c4a8cb2085a7ac4198b983266ce8af643adc23b8 \ No newline at end of file +bfd34ca190b443de8549f160fa0f8a620042ea77 \ No newline at end of file diff --git a/external/corefx/src/System.Runtime/ref/System.Runtime.csproj b/external/corefx/src/System.Runtime/ref/System.Runtime.csproj index 2947fad3fe..3bf3278f93 100644 --- a/external/corefx/src/System.Runtime/ref/System.Runtime.csproj +++ b/external/corefx/src/System.Runtime/ref/System.Runtime.csproj @@ -5,6 +5,10 @@ true true {ADBCF120-3454-4A3C-9D1D-AC4293E795D6} + + $(NoWarn);0809;0618 + + $(DefineConstants);FEATURE_DEFAULT_INTERFACES diff --git a/external/corefx/src/System.Runtime/src/ApiCompatBaseline.uap.txt b/external/corefx/src/System.Runtime/src/ApiCompatBaseline.uap.txt deleted file mode 100644 index fcc74cf864..0000000000 --- a/external/corefx/src/System.Runtime/src/ApiCompatBaseline.uap.txt +++ /dev/null @@ -1 +0,0 @@ -Total Issues: 0 diff --git a/external/corefx/src/System.Runtime/src/ApiCompatBaseline.uapaot.txt b/external/corefx/src/System.Runtime/src/ApiCompatBaseline.uapaot.txt deleted file mode 100644 index 66a1315693..0000000000 --- a/external/corefx/src/System.Runtime/src/ApiCompatBaseline.uapaot.txt +++ /dev/null @@ -1,8 +0,0 @@ -Compat issues with assembly System.Runtime: -MembersMustExist : Member 'System.GC.GetAllocatedBytesForCurrentThread()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.String.EndsWith(System.Char)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.String.Replace(System.String, System.String, System.Boolean, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.String.Replace(System.String, System.String, System.StringComparison)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.String.StartsWith(System.Char)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.String.GetHashCode(System.StringComparison)' does not exist in the implementation but it does exist in the contract. -Total Issues: 7 diff --git a/external/corefx/src/System.Runtime/src/System.Runtime.csproj b/external/corefx/src/System.Runtime/src/System.Runtime.csproj index 9d633654fc..0129b51f90 100644 --- a/external/corefx/src/System.Runtime/src/System.Runtime.csproj +++ b/external/corefx/src/System.Runtime/src/System.Runtime.csproj @@ -6,7 +6,6 @@ System.Runtime true - diff --git a/external/corefx/src/System.Runtime/src/System/Threading/WaitHandleExtensions.cs b/external/corefx/src/System.Runtime/src/System/Threading/WaitHandleExtensions.cs index cd9ff9c110..782c1dfe20 100644 --- a/external/corefx/src/System.Runtime/src/System/Threading/WaitHandleExtensions.cs +++ b/external/corefx/src/System.Runtime/src/System/Threading/WaitHandleExtensions.cs @@ -14,7 +14,6 @@ namespace System.Threading /// /// The to operate on. /// A representing the native operating system handle. - [SecurityCritical] public static SafeWaitHandle GetSafeWaitHandle(this WaitHandle waitHandle) { if (waitHandle == null) @@ -30,7 +29,6 @@ namespace System.Threading /// /// The to operate on. /// A representing the native operating system handle. - [SecurityCritical] public static void SetSafeWaitHandle(this WaitHandle waitHandle, SafeWaitHandle value) { if (waitHandle == null) diff --git a/external/corefx/src/System.Runtime/tests/ForwardedTypesAssembly/ForwardedTypesAssembly.csproj b/external/corefx/src/System.Runtime/tests/ForwardedTypesAssembly/ForwardedTypesAssembly.csproj index b3396e95ed..860b94058a 100644 --- a/external/corefx/src/System.Runtime/tests/ForwardedTypesAssembly/ForwardedTypesAssembly.csproj +++ b/external/corefx/src/System.Runtime/tests/ForwardedTypesAssembly/ForwardedTypesAssembly.csproj @@ -5,7 +5,6 @@ {3A3DE9F8-6295-4B68-BAAB-406F1CA0CD9C} true - @@ -23,4 +22,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Runtime/tests/Performance/Perf.HashCode.cs b/external/corefx/src/System.Runtime/tests/Performance/Perf.HashCode.cs new file mode 100644 index 0000000000..d3def90ff7 --- /dev/null +++ b/external/corefx/src/System.Runtime/tests/Performance/Perf.HashCode.cs @@ -0,0 +1,209 @@ +// 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.Runtime.CompilerServices; +using Microsoft.Xunit.Performance; + +namespace System.Tests +{ + public class Perf_HashCode + { + private static volatile int _valueStorage; + + // Prevents the jitter from eliminating code that + // we want to test. + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void DontDiscard(int value) + { + _valueStorage = value; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static int DontFold(int value) + { + return value + _valueStorage; + } + + [Benchmark] + public void Add() + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < 100; i++) + { + var hc = new HashCode(); + for (int j = 0; j < 100; j++) + { + hc.Add(i); hc.Add(i); hc.Add(i); + hc.Add(i); hc.Add(i); hc.Add(i); + hc.Add(i); hc.Add(i); hc.Add(i); + } + DontDiscard(hc.ToHashCode()); + } + } + } + } + + + [Benchmark] + public void Combine_1() + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < 10000; i++) + { + DontDiscard(HashCode.Combine( + DontFold(i))); + } + } + } + } + + + [Benchmark] + public void Combine_2() + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < 10000; i++) + { + DontDiscard(HashCode.Combine( + DontFold(i), + DontFold(i))); + } + } + } + } + + [Benchmark] + public void Combine_3() + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < 10000; i++) + { + DontDiscard(HashCode.Combine( + DontFold(i), + DontFold(i), + DontFold(i))); + } + } + } + } + + [Benchmark] + public void Combine_4() + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < 10000; i++) + { + DontDiscard(HashCode.Combine( + DontFold(i), + DontFold(i), + DontFold(i), + DontFold(i))); + } + } + } + } + + [Benchmark] + public void Combine_5() + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < 10000; i++) + { + DontDiscard(HashCode.Combine( + DontFold(i), + DontFold(i), + DontFold(i), + DontFold(i), + DontFold(i))); + } + } + } + } + + [Benchmark] + public void Combine_6() + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < 10000; i++) + { + DontDiscard(HashCode.Combine( + DontFold(i), + DontFold(i), + DontFold(i), + DontFold(i), + DontFold(i), + DontFold(i))); + } + } + } + } + + [Benchmark] + public void Combine_7() + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < 10000; i++) + { + DontDiscard(HashCode.Combine( + DontFold(i), + DontFold(i), + DontFold(i), + DontFold(i), + DontFold(i), + DontFold(i), + DontFold(i))); + } + } + } + } + + [Benchmark] + public void Combine_8() + { + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < 10000; i++) + { + DontDiscard(HashCode.Combine( + DontFold(i), + DontFold(i), + DontFold(i), + DontFold(i), + DontFold(i), + DontFold(i), + DontFold(i), + DontFold(i))); + } + } + } + } + } +} diff --git a/external/corefx/src/System.Runtime/tests/Performance/Perf.String.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/Performance/Perf.String.netcoreapp.cs index 2aee5b5282..ebd56b1080 100644 --- a/external/corefx/src/System.Runtime/tests/Performance/Perf.String.netcoreapp.cs +++ b/external/corefx/src/System.Runtime/tests/Performance/Perf.String.netcoreapp.cs @@ -1,4 +1,8 @@ -using System.Collections.Generic; +// 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.Collections.Generic; using Microsoft.Xunit.Performance; using Xunit; diff --git a/external/corefx/src/System.Runtime/tests/Performance/System.Runtime.Performance.Tests.csproj b/external/corefx/src/System.Runtime/tests/Performance/System.Runtime.Performance.Tests.csproj index c3b8d6276b..d65f1a0c6e 100644 --- a/external/corefx/src/System.Runtime/tests/Performance/System.Runtime.Performance.Tests.csproj +++ b/external/corefx/src/System.Runtime/tests/Performance/System.Runtime.Performance.Tests.csproj @@ -7,15 +7,15 @@ true {E9560F70-79F5-453A-B518-0292CFE4A6AD} - - - + + + diff --git a/external/corefx/src/System.Runtime/tests/System.Runtime.Tests.csproj b/external/corefx/src/System.Runtime/tests/System.Runtime.Tests.csproj index 3e24ee11aa..b4dbe71660 100644 --- a/external/corefx/src/System.Runtime/tests/System.Runtime.Tests.csproj +++ b/external/corefx/src/System.Runtime/tests/System.Runtime.Tests.csproj @@ -8,7 +8,6 @@ $(DefineConstants);netcoreapp $(DefineConstants);uapaot - @@ -48,6 +47,7 @@ Common\System\Reflection\MockParameterInfo.cs + @@ -154,6 +154,7 @@ + @@ -175,12 +176,15 @@ + + + @@ -211,10 +215,10 @@ + + - - diff --git a/external/corefx/src/System.Runtime/tests/System/BooleanTests.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/System/BooleanTests.netcoreapp.cs index 195c8c8f15..07cb323e3c 100644 --- a/external/corefx/src/System.Runtime/tests/System/BooleanTests.netcoreapp.cs +++ b/external/corefx/src/System.Runtime/tests/System/BooleanTests.netcoreapp.cs @@ -30,5 +30,34 @@ namespace System.Tests Assert.Equal(false, result); } } + + [Theory] + [InlineData(true, "True")] + [InlineData(false, "False")] + public static void TryFormat(bool i, string expected) + { + char[] actual; + int charsWritten; + + // Just right + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual)); + + // Longer than needed + actual = new char[expected.Length + 1]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual, 0, charsWritten)); + + // Too short + if (expected.Length > 0) + { + actual = new char[expected.Length - 1]; + Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten)); + Assert.Equal(0, charsWritten); + } + } } } diff --git a/external/corefx/src/System.Runtime/tests/System/ByteTests.cs b/external/corefx/src/System.Runtime/tests/System/ByteTests.cs index d917c8d8f8..fdec3c7fb8 100644 --- a/external/corefx/src/System.Runtime/tests/System/ByteTests.cs +++ b/external/corefx/src/System.Runtime/tests/System/ByteTests.cs @@ -88,19 +88,35 @@ namespace System.Tests public static IEnumerable ToString_TestData() { - NumberFormatInfo emptyFormat = NumberFormatInfo.CurrentInfo; - yield return new object[] { (byte)0, "G", emptyFormat, "0" }; - yield return new object[] { (byte)123, "G", emptyFormat, "123" }; - yield return new object[] { byte.MaxValue, "G", emptyFormat, "255" }; + foreach (NumberFormatInfo emptyFormat in new[] { null, NumberFormatInfo.CurrentInfo }) + { + yield return new object[] { (byte)0, "G", emptyFormat, "0" }; + yield return new object[] { (byte)123, "G", emptyFormat, "123" }; + yield return new object[] { byte.MaxValue, "G", emptyFormat, "255" }; - yield return new object[] { (byte)0x24, "x", emptyFormat, "24" }; - yield return new object[] { (byte)24, "N", emptyFormat, string.Format("{0:N}", 24.00) }; + yield return new object[] { (byte)123, "D", emptyFormat, "123" }; + yield return new object[] { (byte)123, "D99", emptyFormat, "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123" }; - NumberFormatInfo customFormat = new NumberFormatInfo(); - customFormat.NegativeSign = "#"; - customFormat.NumberDecimalSeparator = "~"; - customFormat.NumberGroupSeparator = "*"; + yield return new object[] { (byte)0x24, "x", emptyFormat, "24" }; + yield return new object[] { (byte)24, "N", emptyFormat, string.Format("{0:N}", 24.00) }; + } + + var customFormat = new NumberFormatInfo() + { + NegativeSign = "#", + NumberDecimalSeparator = "~", + NumberGroupSeparator = "*", + PositiveSign = "&", + NumberDecimalDigits = 2, + PercentSymbol = "@", + PercentGroupSeparator = ",", + PercentDecimalSeparator = ".", + PercentDecimalDigits = 5 + }; yield return new object[] { (byte)24, "N", customFormat, "24~00" }; + yield return new object[] { (byte)123, "E", customFormat, "1~230000E&002" }; + yield return new object[] { (byte)123, "F", customFormat, "123~00" }; + yield return new object[] { (byte)123, "P", customFormat, "12,300.00000 @" }; } [Theory] diff --git a/external/corefx/src/System.Runtime/tests/System/ByteTests.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/System/ByteTests.netcoreapp.cs index 027f3771ca..7b95e90915 100644 --- a/external/corefx/src/System.Runtime/tests/System/ByteTests.netcoreapp.cs +++ b/external/corefx/src/System.Runtime/tests/System/ByteTests.netcoreapp.cs @@ -15,7 +15,7 @@ namespace System.Tests { Assert.Equal(expected, byte.Parse(value.AsReadOnlySpan(), style, provider)); - Assert.True(byte.TryParse(value.AsReadOnlySpan(), out byte result, style, provider)); + Assert.True(byte.TryParse(value.AsReadOnlySpan(), style, provider, out byte result)); Assert.Equal(expected, result); } @@ -27,9 +27,52 @@ namespace System.Tests { Assert.Throws(exceptionType, () => byte.Parse(value.AsReadOnlySpan(), style, provider)); - Assert.False(byte.TryParse(value.AsReadOnlySpan(), out byte result, style, provider)); + Assert.False(byte.TryParse(value.AsReadOnlySpan(), style, provider, out byte result)); Assert.Equal(0, result); } } + + [Theory] + [MemberData(nameof(ToString_TestData))] + public static void TryFormat(byte i, string format, IFormatProvider provider, string expected) + { + char[] actual; + int charsWritten; + + // Just right + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual)); + + // Longer than needed + actual = new char[expected.Length + 1]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual, 0, charsWritten)); + + // Too short + if (expected.Length > 0) + { + actual = new char[expected.Length - 1]; + Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(0, charsWritten); + } + + if (format != null) + { + // Upper format + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToUpperInvariant(), new string(actual)); + + // Lower format + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToLowerInvariant(), new string(actual)); + } + } } } diff --git a/external/corefx/src/System.Runtime/tests/System/DateTimeOffsetTests.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/System/DateTimeOffsetTests.netcoreapp.cs new file mode 100644 index 0000000000..9b65d1fefe --- /dev/null +++ b/external/corefx/src/System.Runtime/tests/System/DateTimeOffsetTests.netcoreapp.cs @@ -0,0 +1,74 @@ +// 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.Globalization; +using Xunit; + +namespace System.Tests +{ + public static partial class DateTimeOffsetTests + { + [Fact] + public static void ToString_ParseSpan_RoundtripsSuccessfully() + { + DateTimeOffset expected = DateTimeOffset.MaxValue; + string expectedString = expected.ToString(); + + Assert.Equal(expectedString, DateTimeOffset.Parse(expectedString.AsReadOnlySpan()).ToString()); + Assert.Equal(expectedString, DateTimeOffset.Parse(expectedString.AsReadOnlySpan(), null).ToString()); + Assert.Equal(expectedString, DateTimeOffset.Parse(expectedString.AsReadOnlySpan(), null, DateTimeStyles.None).ToString()); + + Assert.True(DateTimeOffset.TryParse(expectedString.AsReadOnlySpan(), out DateTimeOffset actual)); + Assert.Equal(expectedString, actual.ToString()); + Assert.True(DateTimeOffset.TryParse(expectedString.AsReadOnlySpan(), null, DateTimeStyles.None, out actual)); + Assert.Equal(expectedString, actual.ToString()); + } + + [Fact] + public static void ToString_ParseExactSpan_RoundtripsSuccessfully() + { + DateTimeOffset expected = DateTimeOffset.MaxValue; + string expectedString = expected.ToString("u"); + + Assert.Equal(expectedString, DateTimeOffset.ParseExact(expectedString, "u", null, DateTimeStyles.None).ToString("u")); + Assert.Equal(expectedString, DateTimeOffset.ParseExact(expectedString, new[] { "u" }, null, DateTimeStyles.None).ToString("u")); + + Assert.True(DateTimeOffset.TryParseExact(expectedString, "u", null, DateTimeStyles.None, out DateTimeOffset actual)); + Assert.Equal(expectedString, actual.ToString("u")); + Assert.True(DateTimeOffset.TryParseExact(expectedString, new[] { "u" }, null, DateTimeStyles.None, out actual)); + Assert.Equal(expectedString, actual.ToString("u")); + } + + [Fact] + public static void TryFormat_ToString_EqualResults() + { + DateTimeOffset expected = DateTimeOffset.MaxValue; + string expectedString = expected.ToString(); + + // Just the right amount of space, succeeds + Span actual = new char[expectedString.Length]; + Assert.True(expected.TryFormat(actual, out int charsWritten)); + Assert.Equal(expectedString.Length, charsWritten); + Assert.Equal(expectedString.ToCharArray(), actual.ToArray()); + + // Too little space, fails + actual = new char[expectedString.Length - 1]; + Assert.False(expected.TryFormat(actual, out charsWritten)); + Assert.Equal(0, charsWritten); + + // More than enough space, succeeds + actual = new char[expectedString.Length + 1]; + Assert.True(expected.TryFormat(actual, out charsWritten)); + Assert.Equal(expectedString.Length, charsWritten); + Assert.Equal(expectedString.ToCharArray(), actual.Slice(0, expectedString.Length).ToArray()); + Assert.Equal(0, actual[actual.Length - 1]); + } + + [Fact] + public static void UnixEpoch() + { + VerifyDateTimeOffset(DateTimeOffset.UnixEpoch, 1970, 1, 1, 0, 0, 0, 0, TimeSpan.Zero); + } + } +} diff --git a/external/corefx/src/System.Runtime/tests/System/DateTimeTests.cs b/external/corefx/src/System.Runtime/tests/System/DateTimeTests.cs index f6d97d3b32..27a4ed0d98 100644 --- a/external/corefx/src/System.Runtime/tests/System/DateTimeTests.cs +++ b/external/corefx/src/System.Runtime/tests/System/DateTimeTests.cs @@ -10,7 +10,7 @@ using Xunit; namespace System.Tests { - public static class DateTimeTests + public static partial class DateTimeTests { [Fact] public static void MaxValue() @@ -555,7 +555,7 @@ namespace System.Tests { AssertExtensions.Throws("s", () => DateTime.Parse(null)); AssertExtensions.Throws("s", () => DateTime.Parse(null, new MyFormatter())); - AssertExtensions.Throws("s", () => DateTime.Parse(null, new MyFormatter(), DateTimeStyles.NoCurrentDateDefault)); + AssertExtensions.Throws("s", () => DateTime.Parse((string)null, new MyFormatter(), DateTimeStyles.NoCurrentDateDefault)); Assert.Throws(() => DateTime.Parse("")); Assert.Throws(() => DateTime.Parse("", new MyFormatter())); @@ -575,8 +575,8 @@ namespace System.Tests public static void ParseExact_InvalidArguments_Throws() { AssertExtensions.Throws("s", () => DateTime.ParseExact(null, "d", new MyFormatter())); - AssertExtensions.Throws("s", () => DateTime.ParseExact(null, "d", new MyFormatter(), DateTimeStyles.None)); - AssertExtensions.Throws("s", () => DateTime.ParseExact(null, new[] { "d" }, new MyFormatter(), DateTimeStyles.NoCurrentDateDefault)); + AssertExtensions.Throws("s", () => DateTime.ParseExact((string)null, "d", new MyFormatter(), DateTimeStyles.None)); + AssertExtensions.Throws("s", () => DateTime.ParseExact((string)null, new[] { "d" }, new MyFormatter(), DateTimeStyles.NoCurrentDateDefault)); Assert.Throws(() => DateTime.ParseExact("", "d", new MyFormatter())); Assert.Throws(() => DateTime.ParseExact("", "d", new MyFormatter(), DateTimeStyles.None)); @@ -596,8 +596,8 @@ namespace System.Tests [Fact] public static void TryParseExact_InvalidArguments_ReturnsFalse() { - Assert.False(DateTime.TryParseExact(null, "d", new MyFormatter(), DateTimeStyles.None, out DateTime result)); - Assert.False(DateTime.TryParseExact(null, new[] { "d" }, new MyFormatter(), DateTimeStyles.None, out result)); + Assert.False(DateTime.TryParseExact((string)null, "d", new MyFormatter(), DateTimeStyles.None, out DateTime result)); + Assert.False(DateTime.TryParseExact((string)null, new[] { "d" }, new MyFormatter(), DateTimeStyles.None, out result)); Assert.False(DateTime.TryParseExact("", "d", new MyFormatter(), DateTimeStyles.None, out result)); Assert.False(DateTime.TryParseExact("", new[] { "d" }, new MyFormatter(), DateTimeStyles.None, out result)); diff --git a/external/corefx/src/System.Runtime/tests/System/DateTimeTests.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/System/DateTimeTests.netcoreapp.cs new file mode 100644 index 0000000000..44e806cadf --- /dev/null +++ b/external/corefx/src/System.Runtime/tests/System/DateTimeTests.netcoreapp.cs @@ -0,0 +1,92 @@ +// 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.Globalization; +using Xunit; + +namespace System.Tests +{ + public static partial class DateTimeTests + { + [Theory] + [MemberData(nameof(StandardFormatSpecifiers))] + public static void TryFormat_MatchesToString(string format) + { + DateTime dt = DateTime.UtcNow; + string expected = dt.ToString(format); + + // Just the right length, succeeds + Span dest = new char[expected.Length]; + Assert.True(dt.TryFormat(dest, out int charsWritten, format)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToCharArray(), dest.ToArray()); + + // Too short, fails + dest = new char[expected.Length - 1]; + Assert.False(dt.TryFormat(dest, out charsWritten, format)); + Assert.Equal(0, charsWritten); + + // Longer than needed, succeeds + dest = new char[expected.Length + 1]; + Assert.True(dt.TryFormat(dest, out charsWritten, format)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToCharArray(), dest.Slice(0, expected.Length).ToArray()); + Assert.Equal(0, dest[dest.Length - 1]); + } + + [Theory] + [MemberData(nameof(Parse_ValidInput_Suceeds_MemberData))] + public static void Parse_Span_ValidInput_Suceeds(string input, CultureInfo culture, DateTime? expected) + { + Assert.Equal(expected, DateTime.Parse(input.AsReadOnlySpan(), culture)); + } + + [Theory] + [MemberData(nameof(ParseExact_ValidInput_Succeeds_MemberData))] + public static void ParseExact_Span_ValidInput_Succeeds(string input, string format, CultureInfo culture, DateTimeStyles style, DateTime? expected) + { + DateTime result1 = DateTime.ParseExact(input.AsReadOnlySpan(), format, culture, style); + DateTime result2 = DateTime.ParseExact(input.AsReadOnlySpan(), new[] { format }, culture, style); + + Assert.True(DateTime.TryParseExact(input.AsReadOnlySpan(), format, culture, style, out DateTime result3)); + Assert.True(DateTime.TryParseExact(input.AsReadOnlySpan(), new[] { format }, culture, style, out DateTime result4)); + + Assert.Equal(result1, result2); + Assert.Equal(result1, result3); + Assert.Equal(result1, result4); + + if (expected != null) // some inputs don't roundtrip well + { + // Normalize values to make comparison easier + if (expected.Value.Kind != DateTimeKind.Utc) + { + expected = expected.Value.ToUniversalTime(); + } + if (result1.Kind != DateTimeKind.Utc) + { + result1 = result1.ToUniversalTime(); + } + + Assert.Equal(expected, result1); + } + } + + [Theory] + [MemberData(nameof(ParseExact_InvalidInputs_Fail_MemberData))] + public static void ParseExact_Span_InvalidInputs_Fail(string input, string format, CultureInfo culture, DateTimeStyles style) + { + Assert.Throws(() => DateTime.ParseExact(input.AsReadOnlySpan(), format, culture, style)); + Assert.Throws(() => DateTime.ParseExact(input.AsReadOnlySpan(), new[] { format }, culture, style)); + + Assert.False(DateTime.TryParseExact(input.AsReadOnlySpan(), format, culture, style, out DateTime result)); + Assert.False(DateTime.TryParseExact(input.AsReadOnlySpan(), new[] { format }, culture, style, out result)); + } + + [Fact] + public static void UnixEpoch() + { + VerifyDateTime(DateTime.UnixEpoch, 1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + } + } +} diff --git a/external/corefx/src/System.Runtime/tests/System/DecimalTests.cs b/external/corefx/src/System.Runtime/tests/System/DecimalTests.cs index ce30b774fd..c82adf4d8c 100644 --- a/external/corefx/src/System.Runtime/tests/System/DecimalTests.cs +++ b/external/corefx/src/System.Runtime/tests/System/DecimalTests.cs @@ -1167,19 +1167,24 @@ namespace System.Tests public static IEnumerable ToString_TestData() { - yield return new object[] { decimal.MinValue, "G", null, "-79228162514264337593543950335" }; - yield return new object[] { (decimal)-4567, "G", null, "-4567" }; - yield return new object[] { (decimal)-4567.89101, "G", null, "-4567.89101" }; - yield return new object[] { (decimal)0, "G", null, "0" }; - yield return new object[] { (decimal)4567, "G", null, "4567" }; - yield return new object[] { (decimal)4567.89101, "G", null, "4567.89101" }; - yield return new object[] { decimal.MaxValue, "G", null, "79228162514264337593543950335" }; + foreach (NumberFormatInfo defaultFormat in new[] { null, NumberFormatInfo.CurrentInfo }) + { + yield return new object[] { decimal.MinValue, "G", defaultFormat, "-79228162514264337593543950335" }; + yield return new object[] { (decimal)-4567, "G", defaultFormat, "-4567" }; + yield return new object[] { (decimal)-4567.89101, "G", defaultFormat, "-4567.89101" }; + yield return new object[] { (decimal)0, "G", defaultFormat, "0" }; + yield return new object[] { (decimal)4567, "G", defaultFormat, "4567" }; + yield return new object[] { (decimal)4567.89101, "G", defaultFormat, "4567.89101" }; + yield return new object[] { decimal.MaxValue, "G", defaultFormat, "79228162514264337593543950335" }; - yield return new object[] { decimal.MinusOne, "G", null, "-1" }; - yield return new object[] { decimal.Zero, "G", null, "0" }; - yield return new object[] { decimal.One, "G", null, "1" }; + yield return new object[] { decimal.MinusOne, "G", defaultFormat, "-1" }; + yield return new object[] { decimal.Zero, "G", defaultFormat, "0" }; + yield return new object[] { decimal.One, "G", defaultFormat, "1" }; - yield return new object[] { (decimal)2468, "N", null, "2,468.00" }; + yield return new object[] { (decimal)2468, "N", defaultFormat, "2,468.00" }; + + yield return new object[] { (decimal)2467, "[#-##-#]", defaultFormat, "[2-46-7]" }; + } // Changing the negative pattern doesn't do anything without also passing in a format string var customFormat1 = new NumberFormatInfo(); @@ -1198,6 +1203,22 @@ namespace System.Tests customFormat3.NumberGroupSeparator = "*"; customFormat3.NumberNegativePattern = 0; yield return new object[] { (decimal)-2468, "N", customFormat3, "(2*468.00)" }; + + var customFormat4 = new NumberFormatInfo() + { + NegativeSign = "#", + NumberDecimalSeparator = "~", + NumberGroupSeparator = "*", + PositiveSign = "&", + NumberDecimalDigits = 2, + PercentSymbol = "@", + PercentGroupSeparator = ",", + PercentDecimalSeparator = ".", + PercentDecimalDigits = 5 + }; + yield return new object[] { (decimal)123, "E", customFormat4, "1~230000E&002" }; + yield return new object[] { (decimal)123, "F", customFormat4, "123~00" }; + yield return new object[] { (decimal)123, "P", customFormat4, "12,300.00000 @" }; } [Fact] diff --git a/external/corefx/src/System.Runtime/tests/System/DecimalTests.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/System/DecimalTests.netcoreapp.cs index da9c64a502..6afd958d71 100644 --- a/external/corefx/src/System.Runtime/tests/System/DecimalTests.netcoreapp.cs +++ b/external/corefx/src/System.Runtime/tests/System/DecimalTests.netcoreapp.cs @@ -15,7 +15,7 @@ namespace System.Tests { Assert.Equal(expected, decimal.Parse(value.AsReadOnlySpan(), style, provider)); - Assert.True(decimal.TryParse(value.AsReadOnlySpan(), out decimal result, style, provider)); + Assert.True(decimal.TryParse(value.AsReadOnlySpan(), style, provider, out decimal result)); Assert.Equal(expected, result); } @@ -27,9 +27,73 @@ namespace System.Tests { Assert.Throws(exceptionType, () => decimal.Parse(value.AsReadOnlySpan(), style, provider)); - Assert.False(decimal.TryParse(value.AsReadOnlySpan(), out decimal result, style, provider)); + Assert.False(decimal.TryParse(value.AsReadOnlySpan(), style, provider, out decimal result)); Assert.Equal(0, result); } } + + [Fact] + public static void TryFormat() + { + RemoteInvoke(() => + { + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + + foreach (var testdata in ToString_TestData()) + { + decimal localI = (decimal)testdata[0]; + string localFormat = (string)testdata[1]; + IFormatProvider localProvider = (IFormatProvider)testdata[2]; + string localExpected = (string)testdata[3]; + + try + { + char[] actual; + int charsWritten; + + // Just right + actual = new char[localExpected.Length]; + Assert.True(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat, localProvider)); + Assert.Equal(localExpected.Length, charsWritten); + Assert.Equal(localExpected, new string(actual)); + + // Longer than needed + actual = new char[localExpected.Length + 1]; + Assert.True(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat, localProvider)); + Assert.Equal(localExpected.Length, charsWritten); + Assert.Equal(localExpected, new string(actual, 0, charsWritten)); + + // Too short + if (localExpected.Length > 0) + { + actual = new char[localExpected.Length - 1]; + Assert.False(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat, localProvider)); + Assert.Equal(0, charsWritten); + } + + if (localFormat != null) + { + // Upper localFormat + actual = new char[localExpected.Length]; + Assert.True(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat.ToUpperInvariant(), localProvider)); + Assert.Equal(localExpected.Length, charsWritten); + Assert.Equal(localExpected.ToUpperInvariant(), new string(actual)); + + // Lower format + actual = new char[localExpected.Length]; + Assert.True(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat.ToLowerInvariant(), localProvider)); + Assert.Equal(localExpected.Length, charsWritten); + Assert.Equal(localExpected.ToLowerInvariant(), new string(actual)); + } + } + catch (Exception exc) + { + throw new Exception($"Failed on `{localI}`, `{localFormat}`, `{localProvider}`, `{localExpected}`. {exc}"); + } + } + + return SuccessExitCode; + }).Dispose(); + } } } diff --git a/external/corefx/src/System.Runtime/tests/System/DelegateTests.cs b/external/corefx/src/System.Runtime/tests/System/DelegateTests.cs index 840402c933..39314a91e4 100644 --- a/external/corefx/src/System.Runtime/tests/System/DelegateTests.cs +++ b/external/corefx/src/System.Runtime/tests/System/DelegateTests.cs @@ -681,6 +681,16 @@ namespace System.Tests Assert.NotNull(ex.Message); } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "dotnet/coreclr#15196")] + public static void CreateDelegate2_Target_GenericTypeParameter() + { + + Type theT = typeof(G<>).GetTypeInfo().GenericTypeParameters[0]; + Type delegateType = typeof(Func); + AssertExtensions.Throws("target", () => Delegate.CreateDelegate(delegateType, theT, "ReferenceEquals")); + } + [Fact] public static void CreateDelegate2_Type_Null() { diff --git a/external/corefx/src/System.Runtime/tests/System/DoubleTests.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/System/DoubleTests.netcoreapp.cs index bb7a26e610..90b5aaa60d 100644 --- a/external/corefx/src/System.Runtime/tests/System/DoubleTests.netcoreapp.cs +++ b/external/corefx/src/System.Runtime/tests/System/DoubleTests.netcoreapp.cs @@ -93,7 +93,7 @@ namespace System.Tests { Assert.Equal(expected, double.Parse(value.AsReadOnlySpan(), style, provider)); - Assert.True(double.TryParse(value.AsReadOnlySpan(), out double result, style, provider)); + Assert.True(double.TryParse(value.AsReadOnlySpan(), style, provider, out double result)); Assert.Equal(expected, result); } @@ -105,9 +105,58 @@ namespace System.Tests { Assert.Throws(exceptionType, () => double.Parse(value.AsReadOnlySpan(), style, provider)); - Assert.False(double.TryParse(value.AsReadOnlySpan(), out double result, style, provider)); + Assert.False(double.TryParse(value.AsReadOnlySpan(), style, provider, out double result)); Assert.Equal(0, result); } } + + [Fact] + public static void TryFormat() + { + RemoteInvoke(() => + { + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + + foreach (var testdata in ToString_TestData()) + { + double localI = (double)testdata[0]; + string localFormat = (string)testdata[1]; + IFormatProvider localProvider = (IFormatProvider)testdata[2]; + string localExpected = (string)testdata[3]; + + try + { + char[] actual; + int charsWritten; + + // Just right + actual = new char[localExpected.Length]; + Assert.True(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat, localProvider)); + Assert.Equal(localExpected.Length, charsWritten); + Assert.Equal(localExpected, new string(actual)); + + // Longer than needed + actual = new char[localExpected.Length + 1]; + Assert.True(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat, localProvider)); + Assert.Equal(localExpected.Length, charsWritten); + Assert.Equal(localExpected, new string(actual, 0, charsWritten)); + + // Too short + if (localExpected.Length > 0) + { + actual = new char[localExpected.Length - 1]; + Assert.False(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat, localProvider)); + Assert.Equal(0, charsWritten); + } + } + catch (Exception exc) + { + throw new Exception($"Failed on `{localI}`, `{localFormat}`, `{localProvider}`, `{localExpected}`. {exc}"); + } + } + + return SuccessExitCode; + }).Dispose(); + } } } diff --git a/external/corefx/src/System.Runtime/tests/System/GCTests.cs b/external/corefx/src/System.Runtime/tests/System/GCTests.cs index d602880789..91ff25ec33 100644 --- a/external/corefx/src/System.Runtime/tests/System/GCTests.cs +++ b/external/corefx/src/System.Runtime/tests/System/GCTests.cs @@ -721,7 +721,7 @@ namespace System.Tests options.TimeOut = TimeoutMilliseconds; RemoteInvoke(sizeString => { - Assert.Throws("totalSize", () => GC.TryStartNoGCRegion(long.Parse(sizeString))); + AssertExtensions.Throws("totalSize", () => GC.TryStartNoGCRegion(long.Parse(sizeString))); return SuccessExitCode; }, size.ToString(), options).Dispose(); } @@ -738,7 +738,7 @@ namespace System.Tests options.TimeOut = TimeoutMilliseconds; RemoteInvoke(sizeString => { - Assert.Throws("lohSize", () => GC.TryStartNoGCRegion(1024, long.Parse(sizeString))); + AssertExtensions.Throws("lohSize", () => GC.TryStartNoGCRegion(1024, long.Parse(sizeString))); return SuccessExitCode; }, size.ToString(), options).Dispose(); } diff --git a/external/corefx/src/System.Runtime/tests/System/HandleTests.cs b/external/corefx/src/System.Runtime/tests/System/HandleTests.cs index 6bb95e5560..50b3ae20bf 100644 --- a/external/corefx/src/System.Runtime/tests/System/HandleTests.cs +++ b/external/corefx/src/System.Runtime/tests/System/HandleTests.cs @@ -19,6 +19,15 @@ public static class HandleTests Assert.True(h.Value != null); } + [Fact] + public static void DefaultRuntimeFieldHandleHashCodeTest() + { + RuntimeFieldHandle rfh1 = new RuntimeFieldHandle(); + RuntimeFieldHandle rfh2 = new RuntimeFieldHandle(); + + Assert.Equal(rfh1.GetHashCode(), rfh2.GetHashCode()); + } + [Fact] public static void RuntimeMethodHandleTest() { @@ -27,6 +36,15 @@ public static class HandleTests Assert.Equal(mi1, mi2); } + [Fact] + public static void DefaultRuntimeMethodHandleHashCodeTest() + { + RuntimeMethodHandle rmh1 = new RuntimeMethodHandle(); + RuntimeMethodHandle rmh2 = new RuntimeMethodHandle(); + + Assert.Equal(rmh1.GetHashCode(), rmh2.GetHashCode()); + } + [Fact] public static void GenericMethodRuntimeMethodHandleTest() { diff --git a/external/corefx/src/System.Runtime/tests/System/HashCodeTests.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/System/HashCodeTests.netcoreapp.cs new file mode 100644 index 0000000000..80c93f7de0 --- /dev/null +++ b/external/corefx/src/System.Runtime/tests/System/HashCodeTests.netcoreapp.cs @@ -0,0 +1,295 @@ +// 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.Collections.Generic; +using Xunit; + +public static class HashCodeTests +{ + [Fact] + public static void HashCode_Add() + { + // The version of xUnit used by corefx does not support params theories. + void Theory(uint expected, params uint[] vector) + { + var hc = new HashCode(); + for (int i = 0; i < vector.Length; i++) + hc.Add(vector[i]); + +#if SYSTEM_HASHCODE_TESTVECTORS + // HashCode is not deterministic across AppDomains by design. This means + // that these tests can not be executed against the version that exists + // within CoreCLR. Copy HashCode and set m_seed to 0 in order to execute + // these tests. + + Assert.Equal(expected, (uint)hc.ToHashCode()); +#else + // Validate that the HashCode.m_seed is randomized. This has a 1 in 4 + // billion chance of resulting in a false negative, as HashCode.m_seed + // can be 0. + + Assert.NotEqual(expected, (uint)hc.ToHashCode()); +#endif + } + + // These test vectors were created using https://asecuritysite.com/encryption/xxHash + // 1. Find the hash for "". + // 2. Find the hash for "abcd". ASCII "abcd" and bit convert to uint. + // 3. Find the hash for "abcd1234". ASCII [ "abcd", "1234"] and bit convert to 2 uints. + // n. Continue until "abcd0123efgh4567ijkl8901mnop2345qrst6789uvwx0123yzab". + + Theory(0x02cc5d05U); + Theory(0xa3643705U, 0x64636261U ); + Theory(0x4603e94cU, 0x64636261U, 0x33323130U ); + Theory(0xd8a1e80fU, 0x64636261U, 0x33323130U, 0x68676665U ); + Theory(0x4b62a7cfU, 0x64636261U, 0x33323130U, 0x68676665U, 0x37363534U ); + Theory(0xc33a7641U, 0x64636261U, 0x33323130U, 0x68676665U, 0x37363534U, 0x6c6b6a69U ); + Theory(0x1a794705U, 0x64636261U, 0x33323130U, 0x68676665U, 0x37363534U, 0x6c6b6a69U, 0x31303938U ); + Theory(0x4d79177dU, 0x64636261U, 0x33323130U, 0x68676665U, 0x37363534U, 0x6c6b6a69U, 0x31303938U, 0x706f6e6dU ); + Theory(0x59d79205U, 0x64636261U, 0x33323130U, 0x68676665U, 0x37363534U, 0x6c6b6a69U, 0x31303938U, 0x706f6e6dU, 0x35343332U ); + Theory(0x49585aaeU, 0x64636261U, 0x33323130U, 0x68676665U, 0x37363534U, 0x6c6b6a69U, 0x31303938U, 0x706f6e6dU, 0x35343332U, 0x74737271U ); + Theory(0x2f005ff1U, 0x64636261U, 0x33323130U, 0x68676665U, 0x37363534U, 0x6c6b6a69U, 0x31303938U, 0x706f6e6dU, 0x35343332U, 0x74737271U, 0x39383736U ); + Theory(0x0ce339bdU, 0x64636261U, 0x33323130U, 0x68676665U, 0x37363534U, 0x6c6b6a69U, 0x31303938U, 0x706f6e6dU, 0x35343332U, 0x74737271U, 0x39383736U, 0x78777675U ); + Theory(0xb31bd2ffU, 0x64636261U, 0x33323130U, 0x68676665U, 0x37363534U, 0x6c6b6a69U, 0x31303938U, 0x706f6e6dU, 0x35343332U, 0x74737271U, 0x39383736U, 0x78777675U, 0x33323130U ); + Theory(0xa821efa3U, 0x64636261U, 0x33323130U, 0x68676665U, 0x37363534U, 0x6c6b6a69U, 0x31303938U, 0x706f6e6dU, 0x35343332U, 0x74737271U, 0x39383736U, 0x78777675U, 0x33323130U, 0x62617a79U ); + } + + [Fact] + public static void HashCode_Add_HashCode() + { + var hc1 = new HashCode(); + hc1.Add("Hello"); + + var hc2 = new HashCode(); + hc2.Add("Hello".GetHashCode()); + + Assert.Equal(hc1.ToHashCode(), hc2.ToHashCode()); + } + + [Fact] + public static void HashCode_Add_Generic() + { + var hc = new HashCode(); + hc.Add(1); + hc.Add(new ConstHashCodeType()); + + var expected = new HashCode(); + expected.Add(1); + expected.Add(ConstComparer.ConstantValue); + + Assert.Equal(expected.ToHashCode(), hc.ToHashCode()); + } + + [Fact] + public static void HashCode_Add_Null() + { + var hc = new HashCode(); + hc.Add(null); + + var expected = new HashCode(); + expected.Add(EqualityComparer.Default.GetHashCode(null)); + + Assert.Equal(expected.ToHashCode(), hc.ToHashCode()); + } + + [Fact] + public static void HashCode_Add_GenericEqualityComparer() + { + var hc = new HashCode(); + hc.Add(1); + hc.Add("Hello", new ConstComparer()); + + var expected = new HashCode(); + expected.Add(1); + expected.Add(ConstComparer.ConstantValue); + + Assert.Equal(expected.ToHashCode(), hc.ToHashCode()); + } + + [Fact] + public static void HashCode_Add_NullEqualityComparer() + { + var hc = new HashCode(); + hc.Add(1); + hc.Add("Hello", null); + + var expected = new HashCode(); + expected.Add(1); + expected.Add("Hello"); + + Assert.Equal(expected.ToHashCode(), hc.ToHashCode()); + } + + [Fact] + public static void HashCode_Combine() + { + var hcs = new int[] + { + HashCode.Combine(1), + HashCode.Combine(1, 2), + HashCode.Combine(1, 2, 3), + HashCode.Combine(1, 2, 3, 4), + HashCode.Combine(1, 2, 3, 4, 5), + HashCode.Combine(1, 2, 3, 4, 5, 6), + HashCode.Combine(1, 2, 3, 4, 5, 6, 7), + HashCode.Combine(1, 2, 3, 4, 5, 6, 7, 8), + + HashCode.Combine(2), + HashCode.Combine(2, 3), + HashCode.Combine(2, 3, 4), + HashCode.Combine(2, 3, 4, 5), + HashCode.Combine(2, 3, 4, 5, 6), + HashCode.Combine(2, 3, 4, 5, 6, 7), + HashCode.Combine(2, 3, 4, 5, 6, 7, 8), + HashCode.Combine(2, 3, 4, 5, 6, 7, 8, 9), + }; + + for (int i = 0; i < hcs.Length; i++) + for (int j = 0; j < hcs.Length; j++) + { + if (i == j) continue; + Assert.NotEqual(hcs[i], hcs[j]); + } + } + + [Fact] + public static void HashCode_Combine_Add_1() + { + var hc = new HashCode(); + hc.Add(1); + Assert.Equal(hc.ToHashCode(), HashCode.Combine(1)); + } + + [Fact] + public static void HashCode_Combine_Add_2() + { + var hc = new HashCode(); + hc.Add(1); + hc.Add(2); + Assert.Equal(hc.ToHashCode(), HashCode.Combine(1, 2)); + } + + [Fact] + public static void HashCode_Combine_Add_3() + { + var hc = new HashCode(); + hc.Add(1); + hc.Add(2); + hc.Add(3); + Assert.Equal(hc.ToHashCode(), HashCode.Combine(1, 2, 3)); + } + + [Fact] + public static void HashCode_Combine_Add_4() + { + var hc = new HashCode(); + hc.Add(1); + hc.Add(2); + hc.Add(3); + hc.Add(4); + Assert.Equal(hc.ToHashCode(), HashCode.Combine(1, 2, 3, 4)); + } + + [Fact] + public static void HashCode_Combine_Add_5() + { + var hc = new HashCode(); + hc.Add(1); + hc.Add(2); + hc.Add(3); + hc.Add(4); + hc.Add(5); + Assert.Equal(hc.ToHashCode(), HashCode.Combine(1, 2, 3, 4, 5)); + } + + [Fact] + public static void HashCode_Combine_Add_6() + { + var hc = new HashCode(); + hc.Add(1); + hc.Add(2); + hc.Add(3); + hc.Add(4); + hc.Add(5); + hc.Add(6); + Assert.Equal(hc.ToHashCode(), HashCode.Combine(1, 2, 3, 4, 5, 6)); + } + + [Fact] + public static void HashCode_Combine_Add_7() + { + var hc = new HashCode(); + hc.Add(1); + hc.Add(2); + hc.Add(3); + hc.Add(4); + hc.Add(5); + hc.Add(6); + hc.Add(7); + Assert.Equal(hc.ToHashCode(), HashCode.Combine(1, 2, 3, 4, 5, 6, 7)); + } + + [Fact] + public static void HashCode_Combine_Add_8() + { + var hc = new HashCode(); + hc.Add(1); + hc.Add(2); + hc.Add(3); + hc.Add(4); + hc.Add(5); + hc.Add(6); + hc.Add(7); + hc.Add(8); + Assert.Equal(hc.ToHashCode(), HashCode.Combine(1, 2, 3, 4, 5, 6, 7, 8)); + } + + [Fact] + public static void HashCode_GetHashCode() + { + var hc = new HashCode(); + + Assert.Throws(() => hc.GetHashCode()); + } + + [Fact] + public static void HashCode_Equals() + { + var hc = new HashCode(); + + Assert.Throws(() => hc.Equals(hc)); + } + + [Fact] + public static void HashCode_GetHashCode_Boxed() + { + var hc = new HashCode(); + var obj = (object)hc; + + Assert.Throws(() => obj.GetHashCode()); + } + + [Fact] + public static void HashCode_Equals_Boxed() + { + var hc = new HashCode(); + var obj = (object)hc; + + Assert.Throws(() => obj.Equals(obj)); + } + + public class ConstComparer : System.Collections.Generic.IEqualityComparer + { + public const int ConstantValue = 1234; + + public bool Equals(string x, string y) => false; + public int GetHashCode(string obj) => ConstantValue; + } + + public class ConstHashCodeType + { + public override int GetHashCode() => ConstComparer.ConstantValue; + } +} diff --git a/external/corefx/src/System.Runtime/tests/System/Int16Tests.cs b/external/corefx/src/System.Runtime/tests/System/Int16Tests.cs index a9764b208f..130be0058c 100644 --- a/external/corefx/src/System.Runtime/tests/System/Int16Tests.cs +++ b/external/corefx/src/System.Runtime/tests/System/Int16Tests.cs @@ -95,24 +95,40 @@ namespace System.Tests public static IEnumerable ToString_TestData() { - NumberFormatInfo emptyFormat = NumberFormatInfo.CurrentInfo; - yield return new object[] { short.MinValue, "G", emptyFormat, "-32768" }; - yield return new object[] { (short)-4567, "G", emptyFormat, "-4567" }; - yield return new object[] { (short)0, "G", emptyFormat, "0" }; - yield return new object[] { (short)4567, "G", emptyFormat, "4567" }; - yield return new object[] { short.MaxValue, "G", emptyFormat, "32767" }; + foreach (NumberFormatInfo defaultFormat in new[] { null, NumberFormatInfo.CurrentInfo }) + { + yield return new object[] { short.MinValue, "G", defaultFormat, "-32768" }; + yield return new object[] { (short)-4567, "G", defaultFormat, "-4567" }; + yield return new object[] { (short)0, "G", defaultFormat, "0" }; + yield return new object[] { (short)4567, "G", defaultFormat, "4567" }; + yield return new object[] { short.MaxValue, "G", defaultFormat, "32767" }; - yield return new object[] { (short)0x2468, "x", emptyFormat, "2468" }; - yield return new object[] { (short)-0x2468, "x", emptyFormat, "DB98" }; + yield return new object[] { (short)4567, "D", defaultFormat, "4567" }; + yield return new object[] { (short)4567, "D99", defaultFormat, "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004567" }; - yield return new object[] { (short)2468, "N", emptyFormat, string.Format("{0:N}", 2468.00) }; + yield return new object[] { (short)0x2468, "x", defaultFormat, "2468" }; + yield return new object[] { (short)-0x2468, "x", defaultFormat, "db98" }; - NumberFormatInfo customFormat = new NumberFormatInfo(); - customFormat.NegativeSign = "#"; - customFormat.NumberDecimalSeparator = "~"; - customFormat.NumberGroupSeparator = "*"; + yield return new object[] { (short)2468, "N", defaultFormat, string.Format("{0:N}", 2468.00) }; + } + + var customFormat = new NumberFormatInfo() + { + NegativeSign = "#", + NumberDecimalSeparator = "~", + NumberGroupSeparator = "*", + PositiveSign = "&", + NumberDecimalDigits = 2, + PercentSymbol = "@", + PercentGroupSeparator = ",", + PercentDecimalSeparator = ".", + PercentDecimalDigits = 5 + }; yield return new object[] { (short)-2468, "N", customFormat, "#2*468~00" }; yield return new object[] { (short)2468, "N", customFormat, "2*468~00" }; + yield return new object[] { (short)123, "E", customFormat, "1~230000E&002" }; + yield return new object[] { (short)123, "F", customFormat, "123~00" }; + yield return new object[] { (short)123, "P", customFormat, "12,300.00000 @" }; } [Theory] diff --git a/external/corefx/src/System.Runtime/tests/System/Int16Tests.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/System/Int16Tests.netcoreapp.cs index 5e2d99e0c8..4cd5f400c5 100644 --- a/external/corefx/src/System.Runtime/tests/System/Int16Tests.netcoreapp.cs +++ b/external/corefx/src/System.Runtime/tests/System/Int16Tests.netcoreapp.cs @@ -15,7 +15,7 @@ namespace System.Tests { Assert.Equal(expected, short.Parse(value.AsReadOnlySpan(), style, provider)); - Assert.True(short.TryParse(value.AsReadOnlySpan(), out short result, style, provider)); + Assert.True(short.TryParse(value.AsReadOnlySpan(), style, provider, out short result)); Assert.Equal(expected, result); } @@ -27,9 +27,52 @@ namespace System.Tests { Assert.Throws(exceptionType, () => short.Parse(value.AsReadOnlySpan(), style, provider)); - Assert.False(short.TryParse(value.AsReadOnlySpan(), out short result, style, provider)); + Assert.False(short.TryParse(value.AsReadOnlySpan(), style, provider, out short result)); Assert.Equal(0, result); } } + + [Theory] + [MemberData(nameof(ToString_TestData))] + public static void TryFormat(short i, string format, IFormatProvider provider, string expected) + { + char[] actual; + int charsWritten; + + // Just right + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual)); + + // Longer than needed + actual = new char[expected.Length + 1]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual, 0, charsWritten)); + + // Too short + if (expected.Length > 0) + { + actual = new char[expected.Length - 1]; + Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(0, charsWritten); + } + + if (format != null) + { + // Upper format + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToUpperInvariant(), new string(actual)); + + // Lower format + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToLowerInvariant(), new string(actual)); + } + } } } diff --git a/external/corefx/src/System.Runtime/tests/System/Int32Tests.cs b/external/corefx/src/System.Runtime/tests/System/Int32Tests.cs index 1c8dde8707..12edc48532 100644 --- a/external/corefx/src/System.Runtime/tests/System/Int32Tests.cs +++ b/external/corefx/src/System.Runtime/tests/System/Int32Tests.cs @@ -95,22 +95,42 @@ namespace System.Tests public static IEnumerable ToString_TestData() { - NumberFormatInfo emptyFormat = NumberFormatInfo.CurrentInfo; - yield return new object[] { int.MinValue, "G", emptyFormat, "-2147483648" }; - yield return new object[] { -4567, "G", emptyFormat, "-4567" }; - yield return new object[] { 0, "G", emptyFormat, "0" }; - yield return new object[] { 4567, "G", emptyFormat, "4567" }; - yield return new object[] { int.MaxValue, "G", emptyFormat, "2147483647" }; + foreach (NumberFormatInfo defaultFormat in new[] { null, NumberFormatInfo.CurrentInfo }) + { + foreach (string defaultSpecifier in new[] { "G", "G\0", "\0N222", "\0", "" }) + { + yield return new object[] { int.MinValue, defaultSpecifier, defaultFormat, "-2147483648" }; + yield return new object[] { -4567, defaultSpecifier, defaultFormat, "-4567" }; + yield return new object[] { 0, defaultSpecifier, defaultFormat, "0" }; + yield return new object[] { 4567, defaultSpecifier, defaultFormat, "4567" }; + yield return new object[] { int.MaxValue, defaultSpecifier, defaultFormat, "2147483647" }; + } - yield return new object[] { 0x2468, "x", emptyFormat, "2468" }; - yield return new object[] { 2468, "N", emptyFormat, string.Format("{0:N}", 2468.00) }; + yield return new object[] { 4567, "D", defaultFormat, "4567" }; + yield return new object[] { 4567, "D99", defaultFormat, "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004567" }; + yield return new object[] { 4567, "D99\09", defaultFormat, "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004567" }; - NumberFormatInfo customFormat = new NumberFormatInfo(); - customFormat.NegativeSign = "#"; - customFormat.NumberDecimalSeparator = "~"; - customFormat.NumberGroupSeparator = "*"; + yield return new object[] { 0x2468, "x", defaultFormat, "2468" }; + yield return new object[] { 2468, "N", defaultFormat, string.Format("{0:N}", 2468.00) }; + } + + var customFormat = new NumberFormatInfo() + { + NegativeSign = "#", + NumberDecimalSeparator = "~", + NumberGroupSeparator = "*", + PositiveSign = "&", + NumberDecimalDigits = 2, + PercentSymbol = "@", + PercentGroupSeparator = ",", + PercentDecimalSeparator = ".", + PercentDecimalDigits = 5 + }; yield return new object[] { -2468, "N", customFormat, "#2*468~00" }; yield return new object[] { 2468, "N", customFormat, "2*468~00" }; + yield return new object[] { 123, "E", customFormat, "1~230000E&002" }; + yield return new object[] { 123, "F", customFormat, "123~00" }; + yield return new object[] { 123, "P", customFormat, "12,300.00000 @" }; } [Theory] diff --git a/external/corefx/src/System.Runtime/tests/System/Int32Tests.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/System/Int32Tests.netcoreapp.cs index 170dfd7d25..b4d8193a71 100644 --- a/external/corefx/src/System.Runtime/tests/System/Int32Tests.netcoreapp.cs +++ b/external/corefx/src/System.Runtime/tests/System/Int32Tests.netcoreapp.cs @@ -15,7 +15,7 @@ namespace System.Tests { Assert.Equal(expected, int.Parse(value.AsReadOnlySpan(), style, provider)); - Assert.True(int.TryParse(value.AsReadOnlySpan(), out int result, style, provider)); + Assert.True(int.TryParse(value.AsReadOnlySpan(), style, provider, out int result)); Assert.Equal(expected, result); } @@ -27,9 +27,52 @@ namespace System.Tests { Assert.Throws(exceptionType, () => int.Parse(value.AsReadOnlySpan(), style, provider)); - Assert.False(int.TryParse(value.AsReadOnlySpan(), out int result, style, provider)); + Assert.False(int.TryParse(value.AsReadOnlySpan(), style, provider, out int result)); Assert.Equal(0, result); } } + + [Theory] + [MemberData(nameof(ToString_TestData))] + public static void TryFormat(int i, string format, IFormatProvider provider, string expected) + { + char[] actual; + int charsWritten; + + // Just right + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual)); + + // Longer than needed + actual = new char[expected.Length + 1]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual, 0, charsWritten)); + + // Too short + if (expected.Length > 0) + { + actual = new char[expected.Length - 1]; + Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(0, charsWritten); + } + + if (format != null) + { + // Upper format + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToUpperInvariant(), new string(actual)); + + // Lower format + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToLowerInvariant(), new string(actual)); + } + } } } diff --git a/external/corefx/src/System.Runtime/tests/System/Int64Tests.cs b/external/corefx/src/System.Runtime/tests/System/Int64Tests.cs index 197a973767..49373f41b5 100644 --- a/external/corefx/src/System.Runtime/tests/System/Int64Tests.cs +++ b/external/corefx/src/System.Runtime/tests/System/Int64Tests.cs @@ -95,22 +95,38 @@ namespace System.Tests public static IEnumerable ToString_TestData() { - NumberFormatInfo emptyFormat = NumberFormatInfo.CurrentInfo; - yield return new object[] { long.MinValue, "G", emptyFormat, "-9223372036854775808" }; - yield return new object[] { (long)-4567, "G", emptyFormat, "-4567" }; - yield return new object[] { (long)0, "G", emptyFormat, "0" }; - yield return new object[] { (long)4567, "G", emptyFormat, "4567" }; - yield return new object[] { long.MaxValue, "G", emptyFormat, "9223372036854775807" }; + foreach (NumberFormatInfo defaultFormat in new[] { null, NumberFormatInfo.CurrentInfo }) + { + yield return new object[] { long.MinValue, "G", defaultFormat, "-9223372036854775808" }; + yield return new object[] { (long)-4567, "G", defaultFormat, "-4567" }; + yield return new object[] { (long)0, "G", defaultFormat, "0" }; + yield return new object[] { (long)4567, "G", defaultFormat, "4567" }; + yield return new object[] { long.MaxValue, "G", defaultFormat, "9223372036854775807" }; - yield return new object[] { (long)0x2468, "x", emptyFormat, "2468" }; - yield return new object[] { (long)2468, "N", emptyFormat, string.Format("{0:N}", 2468.00) }; + yield return new object[] { (long)4567, "D", defaultFormat, "4567" }; + yield return new object[] { long.MaxValue, "D99", defaultFormat, "000000000000000000000000000000000000000000000000000000000000000000000000000000009223372036854775807" }; - NumberFormatInfo customFormat = new NumberFormatInfo(); - customFormat.NegativeSign = "#"; - customFormat.NumberDecimalSeparator = "~"; - customFormat.NumberGroupSeparator = "*"; + yield return new object[] { (long)0x2468, "x", defaultFormat, "2468" }; + yield return new object[] { (long)2468, "N", defaultFormat, string.Format("{0:N}", 2468.00) }; + } + + var customFormat = new NumberFormatInfo() + { + NegativeSign = "#", + NumberDecimalSeparator = "~", + NumberGroupSeparator = "*", + PositiveSign = "&", + NumberDecimalDigits = 2, + PercentSymbol = "@", + PercentGroupSeparator = ",", + PercentDecimalSeparator = ".", + PercentDecimalDigits = 5 + }; yield return new object[] { (long)-2468, "N", customFormat, "#2*468~00" }; yield return new object[] { (long)2468, "N", customFormat, "2*468~00" }; + yield return new object[] { (long)123, "E", customFormat, "1~230000E&002" }; + yield return new object[] { (long)123, "F", customFormat, "123~00" }; + yield return new object[] { (long)123, "P", customFormat, "12,300.00000 @" }; } [Theory] diff --git a/external/corefx/src/System.Runtime/tests/System/Int64Tests.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/System/Int64Tests.netcoreapp.cs index 403b5e6523..7291c5ebbb 100644 --- a/external/corefx/src/System.Runtime/tests/System/Int64Tests.netcoreapp.cs +++ b/external/corefx/src/System.Runtime/tests/System/Int64Tests.netcoreapp.cs @@ -15,7 +15,7 @@ namespace System.Tests { Assert.Equal(expected, long.Parse(value.AsReadOnlySpan(), style, provider)); - Assert.True(long.TryParse(value.AsReadOnlySpan(), out long result, style, provider)); + Assert.True(long.TryParse(value.AsReadOnlySpan(), style, provider, out long result)); Assert.Equal(expected, result); } @@ -27,9 +27,52 @@ namespace System.Tests { Assert.Throws(exceptionType, () => long.Parse(value.AsReadOnlySpan(), style, provider)); - Assert.False(long.TryParse(value.AsReadOnlySpan(), out long result, style, provider)); + Assert.False(long.TryParse(value.AsReadOnlySpan(), style, provider, out long result)); Assert.Equal(0, result); } } + + [Theory] + [MemberData(nameof(ToString_TestData))] + public static void TryFormat(long i, string format, IFormatProvider provider, string expected) + { + char[] actual; + int charsWritten; + + // Just right + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual)); + + // Longer than needed + actual = new char[expected.Length + 1]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual, 0, charsWritten)); + + // Too short + if (expected.Length > 0) + { + actual = new char[expected.Length - 1]; + Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(0, charsWritten); + } + + if (format != null) + { + // Upper format + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToUpperInvariant(), new string(actual)); + + // Lower format + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToLowerInvariant(), new string(actual)); + } + } } } diff --git a/external/corefx/src/System.Runtime/tests/System/LazyOfTMetadataTests.cs b/external/corefx/src/System.Runtime/tests/System/LazyOfTMetadataTests.cs index d72425c896..cd3de9a7f7 100644 --- a/external/corefx/src/System.Runtime/tests/System/LazyOfTMetadataTests.cs +++ b/external/corefx/src/System.Runtime/tests/System/LazyOfTMetadataTests.cs @@ -10,6 +10,7 @@ namespace System.Tests public static class LazyOfTMetadataTests { [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public static void Ctor_TMetadata() { var lazy = new Lazy("metadata1"); @@ -20,6 +21,7 @@ namespace System.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public static void Ctor_TMetadata_Bool() { var lazy = new Lazy("metadata2", false); @@ -27,6 +29,7 @@ namespace System.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public static void Ctor_TMetadata_LazyThreadSaftetyMode() { var lazy = new Lazy("metadata3", LazyThreadSafetyMode.PublicationOnly); @@ -34,6 +37,7 @@ namespace System.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public static void Ctor_TMetadata_LazyThreadSaftetyMode_InvalidMode_ThrowsArgumentOutOfRangeException() { AssertExtensions.Throws("mode", () => new Lazy("test", LazyThreadSafetyMode.None - 1)); // Invalid mode @@ -41,6 +45,7 @@ namespace System.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public static void Ctor_ValueFactory_TMetadata() { var lazy = new Lazy(() => "foo", 4); @@ -48,12 +53,14 @@ namespace System.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public static void Ctor_ValueFactory_TMetadata_NullValueFactory_ThrowsArgumentNullException() { AssertExtensions.Throws("valueFactory", () => new Lazy(null, "test")); // Value factory is null } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public static void Ctor_ValueFactory_TMetadata_Bool() { var lazy = new Lazy(() => "foo", 5, false); @@ -61,12 +68,14 @@ namespace System.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public static void Ctor_ValueFactory_TMetadata_Bool_NullValueFactory_ThrowsArgumentNullException() { AssertExtensions.Throws("valueFactory", () => new Lazy(null, "test", false)); // Value factory is null } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public static void Ctor_ValueFactory_TMetadata_LazyThreadSaftetyMode() { var lazy = new Lazy(() => "foo", 6, LazyThreadSafetyMode.None); @@ -74,6 +83,7 @@ namespace System.Tests } [Fact] + [ActiveIssue(24903, TargetFrameworkMonikers.NetFramework)] public static void Ctor_ValueFactory_TMetadata_LazyThreadSaftetyMode_Invalid() { AssertExtensions.Throws("valueFactory", () => new Lazy(null, "test", LazyThreadSafetyMode.PublicationOnly)); // Value factory is null diff --git a/external/corefx/src/System.Runtime/tests/System/NullableTests.cs b/external/corefx/src/System.Runtime/tests/System/NullableTests.cs index 1f0a0dd583..9a8428e51f 100644 --- a/external/corefx/src/System.Runtime/tests/System/NullableTests.cs +++ b/external/corefx/src/System.Runtime/tests/System/NullableTests.cs @@ -113,6 +113,47 @@ namespace System.Tests Assert.Equal(expected, Nullable.Compare(n1, n2)); } + [Fact] + public static void MutatingMethods_MutationsAffectOriginal() + { + MutatingStruct? ms = new MutatingStruct() { Value = 1 }; + + for (int i = 1; i <= 2; i++) + { + Assert.Equal(i.ToString(), ms.Value.ToString()); + Assert.Equal(i, ms.Value.Value); + + Assert.Equal(i.ToString(), ms.ToString()); + Assert.Equal(i + 1, ms.Value.Value); + } + + for (int i = 3; i <= 4; i++) + { + Assert.Equal(i, ms.Value.GetHashCode()); + Assert.Equal(i, ms.Value.Value); + + Assert.Equal(i, ms.GetHashCode()); + Assert.Equal(i + 1, ms.Value.Value); + } + + for (int i = 5; i <= 6; i++) + { + ms.Value.Equals(new object()); + Assert.Equal(i, ms.Value.Value); + + ms.Equals(new object()); + Assert.Equal(i + 1, ms.Value.Value); + } + } + + private struct MutatingStruct + { + public int Value; + public override string ToString() => Value++.ToString(); + public override bool Equals(object obj) => Value++.Equals(null); + public override int GetHashCode() => Value++.GetHashCode(); + } + private class G { } } } diff --git a/external/corefx/src/System.Runtime/tests/System/Reflection/ReflectionTypeLoadExceptionTests.cs b/external/corefx/src/System.Runtime/tests/System/Reflection/ReflectionTypeLoadExceptionTests.cs new file mode 100644 index 0000000000..a1ee4e6003 --- /dev/null +++ b/external/corefx/src/System.Runtime/tests/System/Reflection/ReflectionTypeLoadExceptionTests.cs @@ -0,0 +1,35 @@ +// 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 Xunit; + +namespace System.Reflection.Tests +{ + public static class ReflectionTypeLoadExceptionTests + { + [Fact] + public static void NullExceptionsNoNullPointerException() + { + Type[] types = new Type[1]; + Exception[] exceptions = new Exception[1]; + ReflectionTypeLoadException rtle = new ReflectionTypeLoadException(types, exceptions, "Null elements in Exceptions array"); + Assert.NotNull(rtle.ToString()); + Assert.NotNull(rtle.Message); + Assert.Equal(1, rtle.LoaderExceptions.Length); + Assert.Null(rtle.LoaderExceptions[0]); + Assert.Equal(1, rtle.Types.Length); + Assert.Null(rtle.Types[0]); + } + + [Fact] + public static void NullArgumentsNoNullPointerException() + { + ReflectionTypeLoadException rtle = new ReflectionTypeLoadException(null, null, "Null arguments"); + Assert.NotNull(rtle.ToString()); + Assert.NotNull(rtle.Message); + Assert.Null(rtle.LoaderExceptions); + Assert.Null(rtle.Types); + } + } +} diff --git a/external/corefx/src/System.Runtime/tests/System/Reflection/RuntimeReflectionExtensionsTests.cs b/external/corefx/src/System.Runtime/tests/System/Reflection/RuntimeReflectionExtensionsTests.cs index 89336a480d..c5330bd033 100644 --- a/external/corefx/src/System.Runtime/tests/System/Reflection/RuntimeReflectionExtensionsTests.cs +++ b/external/corefx/src/System.Runtime/tests/System/Reflection/RuntimeReflectionExtensionsTests.cs @@ -17,6 +17,9 @@ namespace System.Reflection.Tests Assert.Equal(typeof(RuntimeReflectionExtensionsTests).GetMethod("GetMethodInfo"), ((Action)GetMethodInfo).GetMethodInfo()); } + [Fact] + public void GetMethodInfoOnNull() => AssertExtensions.Throws("del", () => default(Action).GetMethodInfo()); + [Fact] public void GetRuntimeBaseDefinition() { @@ -26,6 +29,10 @@ namespace System.Reflection.Tests Assert.Equal(baseFoo, actual); } + [Fact] + public void GetRuntimeBaseDefinitionOnNull() => + Assert.Throws(() => default(MethodInfo).GetRuntimeBaseDefinition()); + private abstract class TestBase { public abstract void Foo(); @@ -42,24 +49,67 @@ namespace System.Reflection.Tests Assert.Equal(typeof(TestType).GetEvent("StuffHappened"), typeof(TestType).GetRuntimeEvent("StuffHappened")); } + [Fact] + public void GetRuntimeEventOnNull() => AssertExtensions.Throws("type", () => default(Type).GetRuntimeEvent("")); + + [Fact] + public void GetRuntimeEventWithNull() => + Assert.Throws(null, () => typeof(TestType).GetRuntimeEvent(null)); + + [Fact] + public void GetRuntimeEventEmptyName() => Assert.Null(typeof(TestType).GetRuntimeEvent("")); + [Fact] public void GetRuntimeField() { Assert.Equal(typeof(TestType).GetField("_pizzaSize"), typeof(TestType).GetRuntimeField("_pizzaSize")); } + [Fact] + public void GetRuntimeFieldOnNull() => AssertExtensions.Throws("type", () => default(Type).GetRuntimeField("")); + + [Fact] + public void GetRuntimeFieldWithNull() => + Assert.Throws(null, () => typeof(TestType).GetRuntimeField(null)); + + [Fact] + public void GetRuntimeFieldEmptyName() => Assert.Null(typeof(TestType).GetRuntimeField("")); + [Fact] public void GetRuntimeMethod() { Assert.Equal(typeof(TestType).GetMethod("Flush"), typeof(TestType).GetRuntimeMethod("Flush", Array.Empty())); } + [Fact] + public void GetRuntimeMethodOnNull() => AssertExtensions.Throws("type", () => default(Type).GetRuntimeMethod("", Type.EmptyTypes)); + + [Fact] + public void GetRuntimeMethodWithNullName() => AssertExtensions.Throws("name", () => typeof(TestType).GetRuntimeMethod(null, Type.EmptyTypes)); + + [Fact] + public void GetRuntimeMethodWithNullTypes() => AssertExtensions.Throws("types", () => typeof(TestType).GetRuntimeMethod("", null)); + + [Fact] + public void GetRuntimeMethodEmptyName() => Assert.Null(typeof(TestType).GetRuntimeMethod("", Type.EmptyTypes)); + [Fact] public void GetRuntimeProperty() { Assert.Equal(typeof(TestType).GetProperty("Length"), typeof(TestType).GetRuntimeProperty("Length")); } + [Fact] + public void GetRuntimePropertyOnNull() => + AssertExtensions.Throws("type", () => default(Type).GetRuntimeProperty("")); + + [Fact] + public void GetRuntimePropertyWithNull() => + AssertExtensions.Throws("name", () => typeof(TestType).GetRuntimeProperty(null)); + + [Fact] + public void GetRuntimePropertyEmptyName() => Assert.Null(typeof(TestType).GetRuntimeProperty("")); + [Fact] public void GetRuntimeEvents() { @@ -68,6 +118,10 @@ namespace System.Reflection.Tests Assert.Equal("StuffHappened", events[0].Name); } + [Fact] + public void GetRuntimeEventsOnNull() => AssertExtensions.Throws("type", () => default(Type).GetRuntimeEvents()); + + [Fact] public void GetRuntimeFields() { @@ -78,6 +132,9 @@ namespace System.Reflection.Tests Assert.Contains("_pizzaSize", fieldNames); } + [Fact] + public void GetRuntimeFieldsOnNull() => AssertExtensions.Throws("type", () => default(Type).GetRuntimeFields()); + [Fact] public void GetRuntimeMethods() { @@ -92,6 +149,9 @@ namespace System.Reflection.Tests Assert.Contains("Read", methodNames); } + [Fact] + public void GetRuntimeMethodsOnNull() => AssertExtensions.Throws("type", () => default(Type).GetRuntimeMethods()); + [Fact] public void GetRuntimeProperties() { @@ -104,5 +164,43 @@ namespace System.Reflection.Tests Assert.Contains("CanWrite", propertyNames); Assert.Contains("CanSeek", propertyNames); } + + [Fact] + public void GetRuntimePropertiesOnNull() => + AssertExtensions.Throws("type", () => default(Type).GetRuntimeProperties()); + + [Fact] + public void GetRuntimeInterfaceMap() + { + InterfaceMapping map = typeof(TestType).GetTypeInfo().GetRuntimeInterfaceMap(typeof(IDisposable)); + Assert.Same(typeof(TestType), map.TargetType); + Assert.Same(typeof(IDisposable), map.InterfaceType); + Assert.Equal(1, map.InterfaceMethods.Length); + Assert.Equal(1, map.TargetMethods.Length); + MethodInfo ifaceDispose = map.InterfaceMethods[0]; + MethodInfo targetDispose = map.TargetMethods[0]; + Assert.Equal(ifaceDispose.CallingConvention, targetDispose.CallingConvention); + Assert.Equal(ifaceDispose.Name, targetDispose.Name); + Assert.Same(ifaceDispose.ReturnType, targetDispose.ReturnType); + Assert.Equal(ifaceDispose.GetParameters().Length, targetDispose.GetParameters().Length); + Assert.Same(typeof(TestTypeBase), targetDispose.DeclaringType); + Assert.Same(typeof(IDisposable), ifaceDispose.DeclaringType); + } + + [Fact] + public void GetRuntimeInterfaceMapOnNull() => + AssertExtensions.Throws("typeInfo", () => default(TypeInfo).GetRuntimeInterfaceMap(typeof(ICloneable))); + + [Fact] + public void GetRuntimeInterfaceMapWithNull() => + AssertExtensions.Throws("ifaceType", () => typeof(TestType).GetTypeInfo().GetRuntimeInterfaceMap(null)); + + [Fact] + public void GetRuntimeInterfaceMapNotImplemented() => + Assert.Throws(() => typeof(TestType).GetTypeInfo().GetRuntimeInterfaceMap(typeof(ICloneable))); + + [Fact] + public void GetRuntimeInterfaceMapNotInterface() => + Assert.Throws(() => typeof(TestType).GetTypeInfo().GetRuntimeInterfaceMap(typeof(string))); } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Runtime/tests/System/Reflection/TypeDelegatorTests.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/System/Reflection/TypeDelegatorTests.netcoreapp.cs index d996ae73a6..feafcd2154 100644 --- a/external/corefx/src/System.Runtime/tests/System/Reflection/TypeDelegatorTests.netcoreapp.cs +++ b/external/corefx/src/System.Runtime/tests/System/Reflection/TypeDelegatorTests.netcoreapp.cs @@ -1,3 +1,7 @@ +// 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.Collections.Generic; using Xunit; @@ -43,4 +47,4 @@ namespace System.Reflection.Tests Assert.Equal(expected, new TypeDelegator(type).IsSZArray); } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Runtime/tests/System/Reflection/TypeInfoTests.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/System/Reflection/TypeInfoTests.netcoreapp.cs index bb2a2dcc53..d3b411a776 100644 --- a/external/corefx/src/System.Runtime/tests/System/Reflection/TypeInfoTests.netcoreapp.cs +++ b/external/corefx/src/System.Runtime/tests/System/Reflection/TypeInfoTests.netcoreapp.cs @@ -1,3 +1,7 @@ +// 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.Collections.Generic; using Xunit; @@ -43,4 +47,4 @@ namespace System.Reflection.Tests Assert.Equal(expected, type.GetTypeInfo().IsSZArray); } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeFeatureTests.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeFeatureTests.netcoreapp.cs new file mode 100644 index 0000000000..85956dc875 --- /dev/null +++ b/external/corefx/src/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeFeatureTests.netcoreapp.cs @@ -0,0 +1,19 @@ +// 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.Runtime.CompilerServices; +using Xunit; + +namespace System.Runtime.CompilerServices.Tests +{ + public static partial class RuntimeFeatureTests + { + [Fact] + public static void PortablePdb() + { + Assert.True(RuntimeFeature.IsSupported("PortablePdb")); + } + } +} diff --git a/external/corefx/src/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeWrappedExceptionTests.cs b/external/corefx/src/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeWrappedExceptionTests.cs new file mode 100644 index 0000000000..58f8bd5f61 --- /dev/null +++ b/external/corefx/src/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeWrappedExceptionTests.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. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Runtime.CompilerServices.Tests +{ + public static class RuntimeWrappedExceptionTests + { + private const int COR_E_RUNTIMEWRAPPED = -2146233026; + + [Fact] + public static void Ctor_Null() + { + var exception = new RuntimeWrappedException(null); + Assert.NotEmpty(exception.Message); + Assert.Equal(COR_E_RUNTIMEWRAPPED, exception.HResult); + Assert.Null(exception.WrappedException); + } + + [Fact] + public static void Ctor_Exception() + { + var wrappedException = new Exception("Created inner exception"); + var exception = new RuntimeWrappedException(wrappedException); + Assert.NotEmpty(exception.Message); + Assert.Equal(COR_E_RUNTIMEWRAPPED, exception.HResult); + Assert.Same(wrappedException, exception.WrappedException); + } + + [Fact] + public static void Ctor_Not_Exception() + { + object runtimeWrappedExceptionArgument = 1; + var exception = new RuntimeWrappedException(runtimeWrappedExceptionArgument); + Assert.NotEmpty(exception.Message); + Assert.Equal(COR_E_RUNTIMEWRAPPED, exception.HResult); + Assert.Same(runtimeWrappedExceptionArgument, exception.WrappedException); + } + } +} diff --git a/external/corefx/src/System.Runtime/tests/System/Runtime/ConstrainedExecution/PrePrepareMethodAttributeTests.cs b/external/corefx/src/System.Runtime/tests/System/Runtime/ConstrainedExecution/PrePrepareMethodAttributeTests.cs new file mode 100644 index 0000000000..e1505db377 --- /dev/null +++ b/external/corefx/src/System.Runtime/tests/System/Runtime/ConstrainedExecution/PrePrepareMethodAttributeTests.cs @@ -0,0 +1,38 @@ +// 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.Reflection; +using Xunit; + +namespace System.Runtime.ConstrainedExecution.Tests +{ + public class PrePrepareMethodAttributeTests + { + public sealed class ConstrainedType + { + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), PrePrepareMethod] + public ConstrainedType() + { + } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), PrePrepareMethod] + public bool SomeMethod() => false; + } + + [Fact] + public void SettableOnMethods() + { + Assert.NotNull( + typeof(ConstrainedType).GetMethod(nameof(ConstrainedType.SomeMethod)) + .GetCustomAttribute()); + } + + [Fact] + public void SettableOnConstructors() + { + Assert.NotNull( + typeof(ConstrainedType).GetConstructors()[0].GetCustomAttribute()); + } + } +} diff --git a/external/corefx/src/System.Runtime/tests/System/SByteTests.cs b/external/corefx/src/System.Runtime/tests/System/SByteTests.cs index 29f2d4656e..1b16ba8047 100644 --- a/external/corefx/src/System.Runtime/tests/System/SByteTests.cs +++ b/external/corefx/src/System.Runtime/tests/System/SByteTests.cs @@ -91,22 +91,39 @@ namespace System.Tests public static IEnumerable ToString_TestData() { - NumberFormatInfo emptyFormat = NumberFormatInfo.CurrentInfo; - yield return new object[] { sbyte.MinValue, "G", emptyFormat, "-128" }; - yield return new object[] { (sbyte)-123, "G", emptyFormat, "-123" }; - yield return new object[] { (sbyte)0, "G", emptyFormat, "0" }; - yield return new object[] { (sbyte)123, "G", emptyFormat, "123" }; - yield return new object[] { sbyte.MaxValue, "G", emptyFormat, "127" }; + foreach (NumberFormatInfo defaultFormat in new[] { null, NumberFormatInfo.CurrentInfo }) + { + yield return new object[] { sbyte.MinValue, "G", defaultFormat, "-128" }; + yield return new object[] { (sbyte)-123, "G", defaultFormat, "-123" }; + yield return new object[] { (sbyte)0, "G", defaultFormat, "0" }; + yield return new object[] { (sbyte)123, "G", defaultFormat, "123" }; + yield return new object[] { sbyte.MaxValue, "G", defaultFormat, "127" }; - yield return new object[] { (sbyte)0x24, "x", emptyFormat, "24" }; - yield return new object[] { (sbyte)24, "N", emptyFormat, string.Format("{0:N}", 24.00) }; + yield return new object[] { (sbyte)123, "D", defaultFormat, "123" }; + yield return new object[] { (sbyte)123, "D99", defaultFormat, "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123" }; + yield return new object[] { (sbyte)(-123), "D99", defaultFormat, "-000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123" }; - NumberFormatInfo customFormat = new NumberFormatInfo(); - customFormat.NegativeSign = "#"; - customFormat.NumberDecimalSeparator = "~"; - customFormat.NumberGroupSeparator = "*"; + yield return new object[] { (sbyte)0x24, "x", defaultFormat, "24" }; + yield return new object[] { (sbyte)24, "N", defaultFormat, string.Format("{0:N}", 24.00) }; + } + + var customFormat = new NumberFormatInfo() + { + NegativeSign = "#", + NumberDecimalSeparator = "~", + NumberGroupSeparator = "*", + PositiveSign = "&", + NumberDecimalDigits = 2, + PercentSymbol = "@", + PercentGroupSeparator = ",", + PercentDecimalSeparator = ".", + PercentDecimalDigits = 5 + }; yield return new object[] { (sbyte)-24, "N", customFormat, "#24~00" }; yield return new object[] { (sbyte)24, "N", customFormat, "24~00" }; + yield return new object[] { (sbyte)123, "E", customFormat, "1~230000E&002" }; + yield return new object[] { (sbyte)123, "F", customFormat, "123~00" }; + yield return new object[] { (sbyte)123, "P", customFormat, "12,300.00000 @" }; } [Theory] diff --git a/external/corefx/src/System.Runtime/tests/System/SByteTests.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/System/SByteTests.netcoreapp.cs index 8caa325618..2c1ad7abd6 100644 --- a/external/corefx/src/System.Runtime/tests/System/SByteTests.netcoreapp.cs +++ b/external/corefx/src/System.Runtime/tests/System/SByteTests.netcoreapp.cs @@ -15,7 +15,7 @@ namespace System.Tests { Assert.Equal(expected, sbyte.Parse(value.AsReadOnlySpan(), style, provider)); - Assert.True(sbyte.TryParse(value.AsReadOnlySpan(), out sbyte result, style, provider)); + Assert.True(sbyte.TryParse(value.AsReadOnlySpan(), style, provider, out sbyte result)); Assert.Equal(expected, result); } @@ -27,9 +27,52 @@ namespace System.Tests { Assert.Throws(exceptionType, () => sbyte.Parse(value.AsReadOnlySpan(), style, provider)); - Assert.False(sbyte.TryParse(value.AsReadOnlySpan(), out sbyte result, style, provider)); + Assert.False(sbyte.TryParse(value.AsReadOnlySpan(), style, provider, out sbyte result)); Assert.Equal(0, result); } } + + [Theory] + [MemberData(nameof(ToString_TestData))] + public static void TryFormat(sbyte i, string format, IFormatProvider provider, string expected) + { + char[] actual; + int charsWritten; + + // Just right + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual)); + + // Longer than needed + actual = new char[expected.Length + 1]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual, 0, charsWritten)); + + // Too short + if (expected.Length > 0) + { + actual = new char[expected.Length - 1]; + Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(0, charsWritten); + } + + if (format != null) + { + // Upper format + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToUpperInvariant(), new string(actual)); + + // Lower format + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToLowerInvariant(), new string(actual)); + } + } } } diff --git a/external/corefx/src/System.Runtime/tests/System/SingleTests.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/System/SingleTests.netcoreapp.cs index 6ee509bb4b..2d68e56c1f 100644 --- a/external/corefx/src/System.Runtime/tests/System/SingleTests.netcoreapp.cs +++ b/external/corefx/src/System.Runtime/tests/System/SingleTests.netcoreapp.cs @@ -93,7 +93,7 @@ namespace System.Tests { Assert.Equal(expected, float.Parse(value.AsReadOnlySpan(), style, provider)); - Assert.True(float.TryParse(value.AsReadOnlySpan(), out float result, style, provider)); + Assert.True(float.TryParse(value.AsReadOnlySpan(), style, provider, out float result)); Assert.Equal(expected, result); } @@ -105,9 +105,58 @@ namespace System.Tests { Assert.Throws(exceptionType, () => float.Parse(value.AsReadOnlySpan(), style, provider)); - Assert.False(float.TryParse(value.AsReadOnlySpan(), out float result, style, provider)); + Assert.False(float.TryParse(value.AsReadOnlySpan(), style, provider, out float result)); Assert.Equal(0, result); } } + + [Fact] + public static void TryFormat() + { + RemoteInvoke(() => + { + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + + foreach (var testdata in ToString_TestData()) + { + float localI = (float)testdata[0]; + string localFormat = (string)testdata[1]; + IFormatProvider localProvider = (IFormatProvider)testdata[2]; + string localExpected = (string)testdata[3]; + + try + { + char[] actual; + int charsWritten; + + // Just right + actual = new char[localExpected.Length]; + Assert.True(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat, localProvider)); + Assert.Equal(localExpected.Length, charsWritten); + Assert.Equal(localExpected, new string(actual)); + + // Longer than needed + actual = new char[localExpected.Length + 1]; + Assert.True(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat, localProvider)); + Assert.Equal(localExpected.Length, charsWritten); + Assert.Equal(localExpected, new string(actual, 0, charsWritten)); + + // Too short + if (localExpected.Length > 0) + { + actual = new char[localExpected.Length - 1]; + Assert.False(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat, localProvider)); + Assert.Equal(0, charsWritten); + } + } + catch (Exception exc) + { + throw new Exception($"Failed on `{localI}`, `{localFormat}`, `{localProvider}`, `{localExpected}`. {exc}"); + } + } + + return SuccessExitCode; + }).Dispose(); + } } } diff --git a/external/corefx/src/System.Runtime/tests/System/StringTests.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/System/StringTests.netcoreapp.cs index f2a5039afa..8f14fe13e6 100644 --- a/external/corefx/src/System.Runtime/tests/System/StringTests.netcoreapp.cs +++ b/external/corefx/src/System.Runtime/tests/System/StringTests.netcoreapp.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Runtime.InteropServices; using Xunit; namespace System.Tests @@ -90,6 +91,59 @@ namespace System.Tests Assert.Equal(expected, result); } + [Theory] + [InlineData("Hello", 'H', true)] + [InlineData("Hello", 'Z', false)] + [InlineData("Hello", 'e', true)] + [InlineData("Hello", 'E', false)] + [InlineData("", 'H', false)] + public static void Contains(string s, char value, bool expected) + { + Assert.Equal(expected, s.Contains(value)); + } + + [Theory] + // CurrentCulture + [InlineData("Hello", 'H', StringComparison.CurrentCulture, true)] + [InlineData("Hello", 'Z', StringComparison.CurrentCulture, false)] + [InlineData("Hello", 'e', StringComparison.CurrentCulture, true)] + [InlineData("Hello", 'E', StringComparison.CurrentCulture, false)] + [InlineData("", 'H', StringComparison.CurrentCulture, false)] + // CurrentCultureIgnoreCase + [InlineData("Hello", 'H', StringComparison.CurrentCultureIgnoreCase, true)] + [InlineData("Hello", 'Z', StringComparison.CurrentCultureIgnoreCase, false)] + [InlineData("Hello", 'e', StringComparison.CurrentCultureIgnoreCase, true)] + [InlineData("Hello", 'E', StringComparison.CurrentCultureIgnoreCase, true)] + [InlineData("", 'H', StringComparison.CurrentCultureIgnoreCase, false)] + // InvariantCulture + [InlineData("Hello", 'H', StringComparison.InvariantCulture, true)] + [InlineData("Hello", 'Z', StringComparison.InvariantCulture, false)] + [InlineData("Hello", 'e', StringComparison.InvariantCulture, true)] + [InlineData("Hello", 'E', StringComparison.InvariantCulture, false)] + [InlineData("", 'H', StringComparison.InvariantCulture, false)] + // InvariantCultureIgnoreCase + [InlineData("Hello", 'H', StringComparison.InvariantCultureIgnoreCase, true)] + [InlineData("Hello", 'Z', StringComparison.InvariantCultureIgnoreCase, false)] + [InlineData("Hello", 'e', StringComparison.InvariantCultureIgnoreCase, true)] + [InlineData("Hello", 'E', StringComparison.InvariantCultureIgnoreCase, true)] + [InlineData("", 'H', StringComparison.InvariantCultureIgnoreCase, false)] + // Ordinal + [InlineData("Hello", 'H', StringComparison.Ordinal, true)] + [InlineData("Hello", 'Z', StringComparison.Ordinal, false)] + [InlineData("Hello", 'e', StringComparison.Ordinal, true)] + [InlineData("Hello", 'E', StringComparison.Ordinal, false)] + [InlineData("", 'H', StringComparison.Ordinal, false)] + // OrdinalIgnoreCase + [InlineData("Hello", 'H', StringComparison.OrdinalIgnoreCase, true)] + [InlineData("Hello", 'Z', StringComparison.OrdinalIgnoreCase, false)] + [InlineData("Hello", 'e', StringComparison.OrdinalIgnoreCase, true)] + [InlineData("Hello", 'E', StringComparison.OrdinalIgnoreCase, true)] + [InlineData("", 'H', StringComparison.OrdinalIgnoreCase, false)] + public static void Contains(string s, char value, StringComparison comparisionType, bool expected) + { + Assert.Equal(expected, s.Contains(value, comparisionType)); + } + [Theory] // CurrentCulture [InlineData("Hello", "ello", StringComparison.CurrentCulture, true)] @@ -501,5 +555,207 @@ namespace System.Tests { AssertExtensions.Throws("comparisonType", () => "abc".GetHashCode(comparisonType)); } + + [Theory] + [InlineData("")] + [InlineData("a")] + [InlineData("\0")] + [InlineData("abc")] + public static unsafe void ImplicitCast_ResultingSpanMatches(string s) + { + ReadOnlySpan span = s; + Assert.Equal(s.Length, span.Length); + fixed (char* stringPtr = s) + fixed (char* spanPtr = &MemoryMarshal.GetReference(span)) + { + Assert.Equal((IntPtr)stringPtr, (IntPtr)spanPtr); + } + } + + [Fact] + public static void ImplicitCast_NullString_ReturnsDefaultSpan() + { + ReadOnlySpan span = (string)null; + Assert.True(span == default); + } + + [Theory] + [InlineData("Hello", 'l', StringComparison.Ordinal, 2)] + [InlineData("Hello", 'x', StringComparison.Ordinal, -1)] + [InlineData("Hello", 'h', StringComparison.Ordinal, -1)] + [InlineData("Hello", 'o', StringComparison.Ordinal, 4)] + [InlineData("Hello", 'h', StringComparison.OrdinalIgnoreCase, 0)] + [InlineData("HelLo", 'L', StringComparison.OrdinalIgnoreCase, 2)] + [InlineData("HelLo", 'L', StringComparison.Ordinal, 3)] + [InlineData("HelLo", '\0', StringComparison.Ordinal, -1)] + [InlineData("!@#$%", '%', StringComparison.Ordinal, 4)] + [InlineData("!@#$", '!', StringComparison.Ordinal, 0)] + [InlineData("!@#$", '@', StringComparison.Ordinal, 1)] + [InlineData("!@#$%", '%', StringComparison.OrdinalIgnoreCase, 4)] + [InlineData("!@#$", '!', StringComparison.OrdinalIgnoreCase, 0)] + [InlineData("!@#$", '@', StringComparison.OrdinalIgnoreCase, 1)] + [InlineData("_____________\u807f", '\u007f', StringComparison.Ordinal, -1)] + [InlineData("_____________\u807f__", '\u007f', StringComparison.Ordinal, -1)] + [InlineData("_____________\u807f\u007f_", '\u007f', StringComparison.Ordinal, 14)] + [InlineData("__\u807f_______________", '\u007f', StringComparison.Ordinal, -1)] + [InlineData("__\u807f___\u007f___________", '\u007f', StringComparison.Ordinal, 6)] + [InlineData("_____________\u807f", '\u007f', StringComparison.OrdinalIgnoreCase, -1)] + [InlineData("_____________\u807f__", '\u007f', StringComparison.OrdinalIgnoreCase, -1)] + [InlineData("_____________\u807f\u007f_", '\u007f', StringComparison.OrdinalIgnoreCase, 14)] + [InlineData("__\u807f_______________", '\u007f', StringComparison.OrdinalIgnoreCase, -1)] + [InlineData("__\u807f___\u007f___________", '\u007f', StringComparison.OrdinalIgnoreCase, 6)] + public static void IndexOf_SingleLetter(string s, char target, StringComparison stringComparison, int expected) + { + Assert.Equal(expected, s.IndexOf(target, stringComparison)); + } + + [Fact] + public static void IndexOf_TurkishI_TurkishCulture_Char() + { + RemoteInvoke(() => + { + CultureInfo.CurrentCulture = new CultureInfo("tr-TR"); + + string s = "Turkish I \u0131s TROUBL\u0130NG!"; + char value = '\u0130'; + Assert.Equal(19, s.IndexOf(value)); + Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(4, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + Assert.Equal(19, s.IndexOf(value, StringComparison.Ordinal)); + Assert.Equal(19, s.IndexOf(value, StringComparison.OrdinalIgnoreCase)); + + value = '\u0131'; + Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + Assert.Equal(10, s.IndexOf(value, StringComparison.Ordinal)); + Assert.Equal(10, s.IndexOf(value, StringComparison.OrdinalIgnoreCase)); + + return SuccessExitCode; + }).Dispose(); + } + + [Fact] + public static void IndexOf_TurkishI_InvariantCulture_Char() + { + RemoteInvoke(() => + { + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + + string s = "Turkish I \u0131s TROUBL\u0130NG!"; + char value = '\u0130'; + + Assert.Equal(19, s.IndexOf(value)); + Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + + value = '\u0131'; + Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + + return SuccessExitCode; + }).Dispose(); + } + + [Fact] + public static void IndexOf_TurkishI_EnglishUSCulture_Char() + { + RemoteInvoke(() => + { + CultureInfo.CurrentCulture = new CultureInfo("en-US"); + + string s = "Turkish I \u0131s TROUBL\u0130NG!"; + char value = '\u0130'; + + Assert.Equal(19, s.IndexOf(value)); + Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + + value = '\u0131'; + Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + + return SuccessExitCode; + }).Dispose(); + } + + [Fact] + public static void IndexOf_EquivalentDiacritics_EnglishUSCulture_Char() + { + RemoteInvoke(() => + { + string s = "Exhibit a\u0300\u00C0"; + char value = '\u00C0'; + + CultureInfo.CurrentCulture = new CultureInfo("en-US"); + Assert.Equal(10, s.IndexOf(value)); + Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + Assert.Equal(10, s.IndexOf(value, StringComparison.Ordinal)); + Assert.Equal(10, s.IndexOf(value, StringComparison.OrdinalIgnoreCase)); + + return SuccessExitCode; + }).Dispose(); + } + + [Fact] + public static void IndexOf_EquivalentDiacritics_InvariantCulture_Char() + { + RemoteInvoke(() => + { + string s = "Exhibit a\u0300\u00C0"; + char value = '\u00C0'; + + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + Assert.Equal(10, s.IndexOf(value)); + Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + + return SuccessExitCode; + }).Dispose(); + } + + [Fact] + public static void IndexOf_CyrillicE_EnglishUSCulture_Char() + { + RemoteInvoke(() => + { + string s = "Foo\u0400Bar"; + char value = '\u0400'; + + CultureInfo.CurrentCulture = new CultureInfo("en-US"); + Assert.Equal(3, s.IndexOf(value)); + Assert.Equal(3, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(3, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + Assert.Equal(3, s.IndexOf(value, StringComparison.Ordinal)); + Assert.Equal(3, s.IndexOf(value, StringComparison.OrdinalIgnoreCase)); + + return SuccessExitCode; + }).Dispose(); + } + + [Fact] + public static void IndexOf_CyrillicE_InvariantCulture_Char() + { + RemoteInvoke(() => + { + string s = "Foo\u0400Bar"; + char value = '\u0400'; + + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + Assert.Equal(3, s.IndexOf(value)); + Assert.Equal(3, s.IndexOf(value, StringComparison.CurrentCulture)); + Assert.Equal(3, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase)); + + return SuccessExitCode; + }).Dispose(); + } + + [Fact] + public static void IndexOf_Invalid_Char() + { + // Invalid comparison type + AssertExtensions.Throws("comparisonType", () => "foo".IndexOf('o', StringComparison.CurrentCulture - 1)); + AssertExtensions.Throws("comparisonType", () => "foo".IndexOf('o', StringComparison.OrdinalIgnoreCase + 1)); + } } } diff --git a/external/corefx/src/System.Runtime/tests/System/Text/StringBuilderTests.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/System/Text/StringBuilderTests.netcoreapp.cs index 799489568d..acc91e312c 100644 --- a/external/corefx/src/System.Runtime/tests/System/Text/StringBuilderTests.netcoreapp.cs +++ b/external/corefx/src/System.Runtime/tests/System/Text/StringBuilderTests.netcoreapp.cs @@ -199,5 +199,133 @@ namespace System.Text.Tests AssertExtensions.Throws("index", () => builder.Insert(builder.Length + 1, new ReadOnlySpan(new char[0]))); // Index > builder.Length AssertExtensions.Throws("requiredLength", () => builder.Insert(builder.Length, new ReadOnlySpan(new char[1]))); // New length > builder.MaxCapacity } + + private static IEnumerable Append_StringBuilder_TestData() + { + string mediumString = new string('a', 30); + string largeString = new string('b', 1000); + + var sb1 = new StringBuilder("Hello"); + var sb2 = new StringBuilder("one"); + var sb3 = new StringBuilder(20).Append(mediumString); + + yield return new object[] { new StringBuilder("Hello"), sb1, "HelloHello" }; + yield return new object[] { new StringBuilder("Hello"), sb2, "Helloone" }; + yield return new object[] { new StringBuilder("Hello"), new StringBuilder(), "Hello" }; + + yield return new object[] { new StringBuilder("one"), sb3, "one" + mediumString }; + + yield return new object[] { new StringBuilder(20).Append(mediumString), sb3, mediumString + mediumString }; + yield return new object[] { new StringBuilder(10).Append(mediumString), sb3, mediumString + mediumString }; + + yield return new object[] { new StringBuilder(20).Append(largeString), sb3, largeString + mediumString }; + yield return new object[] { new StringBuilder(10).Append(largeString), sb3, largeString + mediumString }; + + yield return new object[] { new StringBuilder(10), sb3, mediumString }; + yield return new object[] { new StringBuilder(30), sb3, mediumString }; + yield return new object[] { new StringBuilder(10), new StringBuilder(20), string.Empty}; + + yield return new object[] { sb1, null, "Hello" }; + yield return new object[] { sb1, sb1, "HelloHello" }; + } + + [Theory] + [MemberData(nameof(Append_StringBuilder_TestData))] + public static void Append_StringBuilder(StringBuilder s1, StringBuilder s2, string s) + { + Assert.Equal(s, s1.Append(s2).ToString()); + } + + private static IEnumerable Append_StringBuilder_Substring_TestData() + { + string mediumString = new string('a', 30); + string largeString = new string('b', 1000); + + var sb1 = new StringBuilder("Hello"); + var sb2 = new StringBuilder("one"); + var sb3 = new StringBuilder(20).Append(mediumString); + + yield return new object[] { new StringBuilder("Hello"), sb1, 0, 5, "HelloHello" }; + yield return new object[] { new StringBuilder("Hello"), sb1, 0, 0, "Hello" }; + yield return new object[] { new StringBuilder("Hello"), sb1, 2, 3, "Hellollo" }; + yield return new object[] { new StringBuilder("Hello"), sb1, 2, 2, "Helloll" }; + yield return new object[] { new StringBuilder("Hello"), sb1, 2, 0, "Hello" }; + yield return new object[] { new StringBuilder("Hello"), new StringBuilder(), 0, 0, "Hello" }; + yield return new object[] { new StringBuilder("Hello"), null, 0, 0, "Hello" }; + yield return new object[] { new StringBuilder(), new StringBuilder("Hello"), 2, 3, "llo" }; + yield return new object[] { new StringBuilder("Hello"), sb2, 0, 3, "Helloone" }; + + yield return new object[] { new StringBuilder("one"), sb3, 5, 25, "one" + new string('a', 25) }; + yield return new object[] { new StringBuilder("one"), sb3, 5, 20, "one" + new string('a', 20) }; + yield return new object[] { new StringBuilder("one"), sb3, 10, 10, "one" + new string('a', 10) }; + + yield return new object[] { new StringBuilder(20).Append(mediumString), sb3, 20, 10, new string('a', 40) }; + yield return new object[] { new StringBuilder(10).Append(mediumString), sb3, 10, 10, new string('a', 40) }; + + yield return new object[] { new StringBuilder(20).Append(largeString), new StringBuilder(20).Append(largeString), 100, 50, largeString + new string('b', 50) }; + yield return new object[] { new StringBuilder(10).Append(mediumString), new StringBuilder(20).Append(largeString), 20, 10, mediumString + new string('b', 10) }; + yield return new object[] { new StringBuilder(10).Append(mediumString), new StringBuilder(20).Append(largeString), 100, 50, mediumString + new string('b', 50) }; + + yield return new object[] { sb1, sb1, 2, 3, "Hellollo" }; + yield return new object[] { sb2, sb2, 2, 0, "one" }; + } + + [Theory] + [MemberData(nameof(Append_StringBuilder_Substring_TestData))] + public static void Append_StringBuilder_Substring(StringBuilder s1, StringBuilder s2, int startIndex, int count, string s) + { + Assert.Equal(s, s1.Append(s2, startIndex, count).ToString()); + } + + [Fact] + public static void Append_StringBuilder_InvalidInput() + { + StringBuilder sb = new StringBuilder(5, 5).Append("Hello"); + + Assert.Throws(() => sb.Append(sb, -1, 0)); + Assert.Throws(() => sb.Append(sb, 0, -1)); + Assert.Throws(() => sb.Append(sb, 4, 5)); + + Assert.Throws(() => sb.Append( (StringBuilder)null, 2, 2)); + Assert.Throws(() => sb.Append((StringBuilder)null, 2, 3)); + Assert.Throws(() => new StringBuilder(3, 6).Append("Hello").Append(sb)); + Assert.Throws(() => new StringBuilder(3, 6).Append("Hello").Append("Hello")); + + Assert.Throws(() => sb.Append(sb)); + } + + public static IEnumerable Equals_String_TestData() + { + string mediumString = new string('a', 30); + string largeString = new string('a', 1000); + string extraLargeString = new string('a', 41000); // 8000 is the maximum chunk size + + var sb1 = new StringBuilder("Hello"); + var sb2 = new StringBuilder(20).Append(mediumString); + var sb3 = new StringBuilder(20).Append(largeString); + var sb4 = new StringBuilder(20).Append(extraLargeString); + + yield return new object[] { sb1, "Hello", true }; + yield return new object[] { sb1, "Hel", false }; + yield return new object[] { sb1, "Hellz", false }; + yield return new object[] { sb1, "Helloz", false }; + yield return new object[] { sb1, "", false }; + yield return new object[] { new StringBuilder(), "", true }; + yield return new object[] { new StringBuilder(), "Hello", false }; + yield return new object[] { sb2, mediumString, true }; + yield return new object[] { sb2, "H", false }; + yield return new object[] { sb3, largeString, true }; + yield return new object[] { sb3, "H", false }; + yield return new object[] { sb3, new string('a', 999) + 'b', false }; + yield return new object[] { sb4, extraLargeString, true }; + yield return new object[] { sb4, "H", false }; + } + + [Theory] + [MemberData(nameof(Equals_String_TestData))] + public static void Equals(StringBuilder sb1, string value, bool expected) + { + Assert.Equal(expected, sb1.Equals(value.AsReadOnlySpan())); + } } } diff --git a/external/corefx/src/System.Runtime/tests/System/Threading/WaitHandleTests.cs b/external/corefx/src/System.Runtime/tests/System/Threading/WaitHandleTests.cs index c9442d00dd..983011c9dd 100644 --- a/external/corefx/src/System.Runtime/tests/System/Threading/WaitHandleTests.cs +++ b/external/corefx/src/System.Runtime/tests/System/Threading/WaitHandleTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using Microsoft.Win32.SafeHandles; using Xunit; namespace System.Threading.Tests @@ -159,6 +160,28 @@ namespace System.Threading.Tests Assert.Throws(() => testWaitHandle.WaitOne(0)); } + [Fact] + public static void SafeWaitHandleViaExtension() + { + ManualResetEvent eventWaitHandle = new ManualResetEvent(false); + SafeWaitHandle eventSafeWaitHandle = eventWaitHandle.GetSafeWaitHandle(); + TestWaitHandle testWaitHandle = new TestWaitHandle(); + testWaitHandle.SetSafeWaitHandle(eventSafeWaitHandle); + Assert.False(testWaitHandle.WaitOne(0)); + eventWaitHandle.Set(); + Assert.True(testWaitHandle.WaitOne(0)); + testWaitHandle.SetSafeWaitHandle(null); + Assert.Throws(() => testWaitHandle.WaitOne(0)); + } + + [Fact] + public static void SetSafeWaitHandleOnNull() => + AssertExtensions.Throws("waitHandle", () => default(WaitHandle).SetSafeWaitHandle(null)); + + [Fact] + public static void GetSafeWaitHandleOnNull() => + AssertExtensions.Throws("waitHandle", () => default(WaitHandle).GetSafeWaitHandle()); + private static void Unsignal(WaitHandle wh) { var eventWh = wh as ManualResetEvent; diff --git a/external/corefx/src/System.Runtime/tests/System/TimeSpanTests.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/System/TimeSpanTests.netcoreapp.cs index 7afccf4d8e..c7b5a82eaa 100644 --- a/external/corefx/src/System.Runtime/tests/System/TimeSpanTests.netcoreapp.cs +++ b/external/corefx/src/System.Runtime/tests/System/TimeSpanTests.netcoreapp.cs @@ -119,7 +119,7 @@ namespace System.Tests TimeSpan result; Assert.Equal(expected, TimeSpan.Parse(input, provider)); - Assert.True(TimeSpan.TryParse(input, out result, provider)); + Assert.True(TimeSpan.TryParse(input, provider, out result)); Assert.Equal(expected, result); // Also negate @@ -129,7 +129,7 @@ namespace System.Tests expected = -expected; Assert.Equal(expected, TimeSpan.Parse(input, provider)); - Assert.True(TimeSpan.TryParse(input, out result, provider)); + Assert.True(TimeSpan.TryParse(input, provider, out result)); Assert.Equal(expected, result); } } @@ -141,7 +141,7 @@ namespace System.Tests if (inputString != null) { Assert.Throws(exceptionType, () => TimeSpan.Parse(inputString.AsReadOnlySpan(), provider)); - Assert.False(TimeSpan.TryParse(inputString.AsReadOnlySpan(), out TimeSpan result, provider)); + Assert.False(TimeSpan.TryParse(inputString.AsReadOnlySpan(), provider, out TimeSpan result)); Assert.Equal(TimeSpan.Zero, result); } } @@ -169,7 +169,7 @@ namespace System.Tests // TimeSpanStyles is interpreted only for custom formats Assert.Equal(expected.Negate(), TimeSpan.ParseExact(input, format, new CultureInfo("en-US"), TimeSpanStyles.AssumeNegative)); - Assert.True(TimeSpan.TryParseExact(input, format, new CultureInfo("en-US"), out result, TimeSpanStyles.AssumeNegative)); + Assert.True(TimeSpan.TryParseExact(input, format, new CultureInfo("en-US"), TimeSpanStyles.AssumeNegative, out result)); Assert.Equal(expected.Negate(), result); } else @@ -177,16 +177,16 @@ namespace System.Tests // Inputs that can be parsed in standard formats with ParseExact should also be parsable with Parse Assert.Equal(expected, TimeSpan.Parse(input, CultureInfo.InvariantCulture)); - Assert.True(TimeSpan.TryParse(input, out result, CultureInfo.InvariantCulture)); + Assert.True(TimeSpan.TryParse(input, CultureInfo.InvariantCulture, out result)); Assert.Equal(expected, result); } } [Theory] [MemberData(nameof(ParseExact_Invalid_TestData))] - public static void ParseExactTest__Span_Invalid(string inputString, string format, Type exceptionType) + public static void ParseExactTest_Span_Invalid(string inputString, string format, Type exceptionType) { - if (inputString != null) + if (inputString != null && format != null) { Assert.Throws(exceptionType, () => TimeSpan.ParseExact(inputString.AsReadOnlySpan(), format, new CultureInfo("en-US"))); diff --git a/external/corefx/src/System.Runtime/tests/System/TypeTests.cs b/external/corefx/src/System.Runtime/tests/System/TypeTests.cs index 7dca0b3e64..34aced8b59 100644 --- a/external/corefx/src/System.Runtime/tests/System/TypeTests.cs +++ b/external/corefx/src/System.Runtime/tests/System/TypeTests.cs @@ -256,6 +256,15 @@ namespace System.Tests Assert.Throws(expectedException, () => Type.GetType(typeName, throwOnError: true, ignoreCase: false)); } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot, "Stackwalking is not supported on UaoAot")] + public static void GetTypeByName_ViaReflection() + { + MethodInfo method = typeof(Type).GetMethod("GetType", new[] { typeof(string) }); + object result = method.Invoke(null, new object[] { "System.Tests.TypeTests" }); + Assert.Equal(typeof(TypeTests), result); + } + [Fact] public static void Delimiter() { diff --git a/external/corefx/src/System.Runtime/tests/System/TypeTests.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/System/TypeTests.netcoreapp.cs index c47e6c6fb7..fd15a5d327 100644 --- a/external/corefx/src/System.Runtime/tests/System/TypeTests.netcoreapp.cs +++ b/external/corefx/src/System.Runtime/tests/System/TypeTests.netcoreapp.cs @@ -1,3 +1,7 @@ +// 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.Collections.Generic; using System.Linq; using System.Reflection; @@ -215,6 +219,7 @@ namespace System.Tests Type theT = typeof(Outside<>).GetTypeInfo().GenericTypeParameters[0]; yield return new object[] { typeof(TypedReference), true }; + yield return new object[] { typeof(RuntimeArgumentHandle), true }; yield return new object[] { typeof(Span<>), true }; yield return new object[] { typeof(Span), true }; yield return new object[] { typeof(Span<>).MakeGenericType(theT), true }; diff --git a/external/corefx/src/System.Runtime/tests/System/UInt16Tests.cs b/external/corefx/src/System.Runtime/tests/System/UInt16Tests.cs index eea5d9fb1a..c853a36506 100644 --- a/external/corefx/src/System.Runtime/tests/System/UInt16Tests.cs +++ b/external/corefx/src/System.Runtime/tests/System/UInt16Tests.cs @@ -88,19 +88,35 @@ namespace System.Tests public static IEnumerable ToString_TestData() { - NumberFormatInfo emptyFormat = NumberFormatInfo.CurrentInfo; - yield return new object[] { (ushort)0, "G", emptyFormat, "0" }; - yield return new object[] { (ushort)4567, "G", emptyFormat, "4567" }; - yield return new object[] { ushort.MaxValue, "G", emptyFormat, "65535" }; + foreach (NumberFormatInfo defaultFormat in new[] { null, NumberFormatInfo.CurrentInfo }) + { + yield return new object[] { (ushort)0, "G", defaultFormat, "0" }; + yield return new object[] { (ushort)4567, "G", defaultFormat, "4567" }; + yield return new object[] { ushort.MaxValue, "G", defaultFormat, "65535" }; - yield return new object[] { (ushort)0x2468, "x", emptyFormat, "2468" }; - yield return new object[] { (ushort)2468, "N", emptyFormat, string.Format("{0:N}", 2468.00) }; + yield return new object[] { (ushort)123, "D", defaultFormat, "123" }; + yield return new object[] { (ushort)123, "D99", defaultFormat, "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123" }; - NumberFormatInfo customFormat = new NumberFormatInfo(); - customFormat.NegativeSign = "#"; - customFormat.NumberDecimalSeparator = "~"; - customFormat.NumberGroupSeparator = "*"; + yield return new object[] { (ushort)0x2468, "x", defaultFormat, "2468" }; + yield return new object[] { (ushort)2468, "N", defaultFormat, string.Format("{0:N}", 2468.00) }; + } + + var customFormat = new NumberFormatInfo() + { + NegativeSign = "#", + NumberDecimalSeparator = "~", + NumberGroupSeparator = "*", + PositiveSign = "&", + NumberDecimalDigits = 2, + PercentSymbol = "@", + PercentGroupSeparator = ",", + PercentDecimalSeparator = ".", + PercentDecimalDigits = 5 + }; yield return new object[] { (ushort)2468, "N", customFormat, "2*468~00" }; + yield return new object[] { (ushort)123, "E", customFormat, "1~230000E&002" }; + yield return new object[] { (ushort)123, "F", customFormat, "123~00" }; + yield return new object[] { (ushort)123, "P", customFormat, "12,300.00000 @" }; } [Theory] diff --git a/external/corefx/src/System.Runtime/tests/System/UInt16Tests.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/System/UInt16Tests.netcoreapp.cs index 5252be020a..b183f168da 100644 --- a/external/corefx/src/System.Runtime/tests/System/UInt16Tests.netcoreapp.cs +++ b/external/corefx/src/System.Runtime/tests/System/UInt16Tests.netcoreapp.cs @@ -15,7 +15,7 @@ namespace System.Tests { Assert.Equal(expected, ushort.Parse(value.AsReadOnlySpan(), style, provider)); - Assert.True(ushort.TryParse(value.AsReadOnlySpan(), out ushort result, style, provider)); + Assert.True(ushort.TryParse(value.AsReadOnlySpan(), style, provider, out ushort result)); Assert.Equal(expected, result); } @@ -27,9 +27,52 @@ namespace System.Tests { Assert.Throws(exceptionType, () => ushort.Parse(value.AsReadOnlySpan(), style, provider)); - Assert.False(ushort.TryParse(value.AsReadOnlySpan(), out ushort result, style, provider)); + Assert.False(ushort.TryParse(value.AsReadOnlySpan(), style, provider, out ushort result)); Assert.Equal(0, (short)result); } } + + [Theory] + [MemberData(nameof(ToString_TestData))] + public static void TryFormat(ushort i, string format, IFormatProvider provider, string expected) + { + char[] actual; + int charsWritten; + + // Just right + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual)); + + // Longer than needed + actual = new char[expected.Length + 1]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual, 0, charsWritten)); + + // Too short + if (expected.Length > 0) + { + actual = new char[expected.Length - 1]; + Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(0, charsWritten); + } + + if (format != null) + { + // Upper format + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToUpperInvariant(), new string(actual)); + + // Lower format + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToLowerInvariant(), new string(actual)); + } + } } } diff --git a/external/corefx/src/System.Runtime/tests/System/UInt32Tests.cs b/external/corefx/src/System.Runtime/tests/System/UInt32Tests.cs index 7e1bcc0144..d657b41eb1 100644 --- a/external/corefx/src/System.Runtime/tests/System/UInt32Tests.cs +++ b/external/corefx/src/System.Runtime/tests/System/UInt32Tests.cs @@ -89,19 +89,35 @@ namespace System.Tests public static IEnumerable ToString_TestData() { - NumberFormatInfo emptyFormat = NumberFormatInfo.CurrentInfo; - yield return new object[] { (uint)0, "G", emptyFormat, "0" }; - yield return new object[] { (uint)4567, "G", emptyFormat, "4567" }; - yield return new object[] { uint.MaxValue, "G", emptyFormat, "4294967295" }; + foreach (NumberFormatInfo defaultFormat in new[] { null, NumberFormatInfo.CurrentInfo }) + { + yield return new object[] { (uint)0, "G", defaultFormat, "0" }; + yield return new object[] { (uint)4567, "G", defaultFormat, "4567" }; + yield return new object[] { uint.MaxValue, "G", defaultFormat, "4294967295" }; - yield return new object[] { (uint)0x2468, "x", emptyFormat, "2468" }; - yield return new object[] { (uint)2468, "N", emptyFormat, string.Format("{0:N}", 2468.00) }; + yield return new object[] { (uint)4567, "D", defaultFormat, "4567" }; + yield return new object[] { (uint)4567, "D18", defaultFormat, "000000000000004567" }; - NumberFormatInfo customFormat = new NumberFormatInfo(); - customFormat.NegativeSign = "#"; - customFormat.NumberDecimalSeparator = "~"; - customFormat.NumberGroupSeparator = "*"; + yield return new object[] { (uint)0x2468, "x", defaultFormat, "2468" }; + yield return new object[] { (uint)2468, "N", defaultFormat, string.Format("{0:N}", 2468.00) }; + } + + var customFormat = new NumberFormatInfo() + { + NegativeSign = "#", + NumberDecimalSeparator = "~", + NumberGroupSeparator = "*", + PositiveSign = "&", + NumberDecimalDigits = 2, + PercentSymbol = "@", + PercentGroupSeparator = ",", + PercentDecimalSeparator = ".", + PercentDecimalDigits = 5 + }; yield return new object[] { (uint)2468, "N", customFormat, "2*468~00" }; + yield return new object[] { (uint)123, "E", customFormat, "1~230000E&002" }; + yield return new object[] { (uint)123, "F", customFormat, "123~00" }; + yield return new object[] { (uint)123, "P", customFormat, "12,300.00000 @" }; } [Theory] diff --git a/external/corefx/src/System.Runtime/tests/System/UInt32Tests.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/System/UInt32Tests.netcoreapp.cs index 11ac18b41a..594a0230ee 100644 --- a/external/corefx/src/System.Runtime/tests/System/UInt32Tests.netcoreapp.cs +++ b/external/corefx/src/System.Runtime/tests/System/UInt32Tests.netcoreapp.cs @@ -15,7 +15,7 @@ namespace System.Tests { Assert.Equal(expected, uint.Parse(value.AsReadOnlySpan(), style, provider)); - Assert.True(uint.TryParse(value.AsReadOnlySpan(), out uint result, style, provider)); + Assert.True(uint.TryParse(value.AsReadOnlySpan(), style, provider, out uint result)); Assert.Equal(expected, result); } @@ -27,9 +27,52 @@ namespace System.Tests { Assert.Throws(exceptionType, () => uint.Parse(value.AsReadOnlySpan(), style, provider)); - Assert.False(uint.TryParse(value.AsReadOnlySpan(), out uint result, style, provider)); + Assert.False(uint.TryParse(value.AsReadOnlySpan(), style, provider, out uint result)); Assert.Equal(0, (int)result); } } + + [Theory] + [MemberData(nameof(ToString_TestData))] + public static void TryFormat(uint i, string format, IFormatProvider provider, string expected) + { + char[] actual; + int charsWritten; + + // Just right + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual)); + + // Longer than needed + actual = new char[expected.Length + 1]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual, 0, charsWritten)); + + // Too short + if (expected.Length > 0) + { + actual = new char[expected.Length - 1]; + Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(0, charsWritten); + } + + if (format != null) + { + // Upper format + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToUpperInvariant(), new string(actual)); + + // Lower format + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToLowerInvariant(), new string(actual)); + } + } } } diff --git a/external/corefx/src/System.Runtime/tests/System/UInt64Tests.cs b/external/corefx/src/System.Runtime/tests/System/UInt64Tests.cs index 6202f1156e..242b6b7e07 100644 --- a/external/corefx/src/System.Runtime/tests/System/UInt64Tests.cs +++ b/external/corefx/src/System.Runtime/tests/System/UInt64Tests.cs @@ -86,25 +86,41 @@ namespace System.Tests Assert.Equal(TypeCode.UInt64, ((ulong)1).GetTypeCode()); } - public static IEnumerable ToStringTestData() + public static IEnumerable ToString_TestData() { - NumberFormatInfo emptyFormat = NumberFormatInfo.CurrentInfo; - yield return new object[] { (ulong)0, "G", emptyFormat, "0" }; - yield return new object[] { (ulong)4567, "G", emptyFormat, "4567" }; - yield return new object[] { ulong.MaxValue, "G", emptyFormat, "18446744073709551615" }; + foreach (NumberFormatInfo defaultFormat in new[] { null, NumberFormatInfo.CurrentInfo }) + { + yield return new object[] { (ulong)0, "G", defaultFormat, "0" }; + yield return new object[] { (ulong)4567, "G", defaultFormat, "4567" }; + yield return new object[] { ulong.MaxValue, "G", defaultFormat, "18446744073709551615" }; - yield return new object[] { (ulong)0x2468, "x", emptyFormat, "2468" }; - yield return new object[] { (ulong)2468, "N", emptyFormat, string.Format("{0:N}", 2468.00) }; + yield return new object[] { (ulong)4567, "D", defaultFormat, "4567" }; + yield return new object[] { (ulong)4567, "D18", defaultFormat, "000000000000004567" }; - NumberFormatInfo customFormat = new NumberFormatInfo(); - customFormat.NegativeSign = "#"; - customFormat.NumberDecimalSeparator = "~"; - customFormat.NumberGroupSeparator = "*"; + yield return new object[] { (ulong)0x2468, "x", defaultFormat, "2468" }; + yield return new object[] { (ulong)2468, "N", defaultFormat, string.Format("{0:N}", 2468.00) }; + } + + var customFormat = new NumberFormatInfo() + { + NegativeSign = "#", + NumberDecimalSeparator = "~", + NumberGroupSeparator = "*", + PositiveSign = "&", + NumberDecimalDigits = 2, + PercentSymbol = "@", + PercentGroupSeparator = ",", + PercentDecimalSeparator = ".", + PercentDecimalDigits = 5 + }; yield return new object[] { (ulong)2468, "N", customFormat, "2*468~00" }; + yield return new object[] { (ulong)123, "E", customFormat, "1~230000E&002" }; + yield return new object[] { (ulong)123, "F", customFormat, "123~00" }; + yield return new object[] { (ulong)123, "P", customFormat, "12,300.00000 @" }; } [Theory] - [MemberData(nameof(ToStringTestData))] + [MemberData(nameof(ToString_TestData))] public static void ToString(ulong i, string format, IFormatProvider provider, string expected) { // Format is case insensitive diff --git a/external/corefx/src/System.Runtime/tests/System/UInt64Tests.netcoreapp.cs b/external/corefx/src/System.Runtime/tests/System/UInt64Tests.netcoreapp.cs index 7262572eaa..8be57929f1 100644 --- a/external/corefx/src/System.Runtime/tests/System/UInt64Tests.netcoreapp.cs +++ b/external/corefx/src/System.Runtime/tests/System/UInt64Tests.netcoreapp.cs @@ -15,7 +15,7 @@ namespace System.Tests { Assert.Equal(expected, ulong.Parse(value.AsReadOnlySpan(), style, provider)); - Assert.True(ulong.TryParse(value.AsReadOnlySpan(), out ulong result, style, provider)); + Assert.True(ulong.TryParse(value.AsReadOnlySpan(), style, provider, out ulong result)); Assert.Equal(expected, result); } @@ -27,9 +27,52 @@ namespace System.Tests { Assert.Throws(exceptionType, () => ulong.Parse(value.AsReadOnlySpan(), style, provider)); - Assert.False(ulong.TryParse(value.AsReadOnlySpan(), out ulong result, style, provider)); + Assert.False(ulong.TryParse(value.AsReadOnlySpan(), style, provider, out ulong result)); Assert.Equal(0, (long)result); } } + + [Theory] + [MemberData(nameof(ToString_TestData))] + public static void TryFormat(ulong i, string format, IFormatProvider provider, string expected) + { + char[] actual; + int charsWritten; + + // Just right + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual)); + + // Longer than needed + actual = new char[expected.Length + 1]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual, 0, charsWritten)); + + // Too short + if (expected.Length > 0) + { + actual = new char[expected.Length - 1]; + Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(0, charsWritten); + } + + if (format != null) + { + // Upper format + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToUpperInvariant(), new string(actual)); + + // Lower format + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToLowerInvariant(), new string(actual)); + } + } } } diff --git a/external/corefx/src/System.Runtime/tests/TestAssembly/TestAssembly.csproj b/external/corefx/src/System.Runtime/tests/TestAssembly/TestAssembly.csproj index b185e3f2e8..bbeff7db09 100644 --- a/external/corefx/src/System.Runtime/tests/TestAssembly/TestAssembly.csproj +++ b/external/corefx/src/System.Runtime/tests/TestAssembly/TestAssembly.csproj @@ -5,7 +5,6 @@ {2EF7D710-A7BD-4BB3-8EF6-3F0298CD4986} true - diff --git a/external/corefx/src/System.Runtime/tests/TestLoadAssembly/TestLoadAssembly.csproj b/external/corefx/src/System.Runtime/tests/TestLoadAssembly/TestLoadAssembly.csproj index 7a13ac873d..4094699726 100644 --- a/external/corefx/src/System.Runtime/tests/TestLoadAssembly/TestLoadAssembly.csproj +++ b/external/corefx/src/System.Runtime/tests/TestLoadAssembly/TestLoadAssembly.csproj @@ -6,7 +6,6 @@ true 1.0.0.0 - diff --git a/external/corefx/src/System.Runtime/tests/UnloadableAssembly/UnloadableAssembly.csproj b/external/corefx/src/System.Runtime/tests/UnloadableAssembly/UnloadableAssembly.csproj index 46624025b6..73a7a358a6 100644 --- a/external/corefx/src/System.Runtime/tests/UnloadableAssembly/UnloadableAssembly.csproj +++ b/external/corefx/src/System.Runtime/tests/UnloadableAssembly/UnloadableAssembly.csproj @@ -5,16 +5,10 @@ {CA43AAD6-59A6-4BEB-AC9E-52DC9FD04B8B} true - - - - Common\System\Diagnostics\CodeAnalysis\ExcludeFromCodeCoverageAssemblyAttribute.cs - - diff --git a/external/corefx/src/System.Security.AccessControl/System.Security.AccessControl.sln b/external/corefx/src/System.Security.AccessControl/System.Security.AccessControl.sln index d314cdf92f..3529cd0c4d 100644 --- a/external/corefx/src/System.Security.AccessControl/System.Security.AccessControl.sln +++ b/external/corefx/src/System.Security.AccessControl/System.Security.AccessControl.sln @@ -30,10 +30,10 @@ Global {70FAC855-CAC6-4523-8477-880548D58A1B}.Debug|Any CPU.Build.0 = netstandard-Windows_NT-Debug|Any CPU {70FAC855-CAC6-4523-8477-880548D58A1B}.Release|Any CPU.ActiveCfg = netstandard-Windows_NT-Release|Any CPU {70FAC855-CAC6-4523-8477-880548D58A1B}.Release|Any CPU.Build.0 = netstandard-Windows_NT-Release|Any CPU - {D27FFA1F-B446-4D24-B60A-1F88385CDB6D}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU - {D27FFA1F-B446-4D24-B60A-1F88385CDB6D}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU - {D27FFA1F-B446-4D24-B60A-1F88385CDB6D}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU - {D27FFA1F-B446-4D24-B60A-1F88385CDB6D}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {D27FFA1F-B446-4D24-B60A-1F88385CDB6D}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {D27FFA1F-B446-4D24-B60A-1F88385CDB6D}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {D27FFA1F-B446-4D24-B60A-1F88385CDB6D}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {D27FFA1F-B446-4D24-B60A-1F88385CDB6D}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU {F80C478C-48EE-46A5-89C4-EE0CFB23A14F}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU {F80C478C-48EE-46A5-89C4-EE0CFB23A14F}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU {F80C478C-48EE-46A5-89C4-EE0CFB23A14F}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU diff --git a/external/corefx/src/System.Security.AccessControl/src/Configurations.props b/external/corefx/src/System.Security.AccessControl/src/Configurations.props index e7003b2c30..72769303fe 100644 --- a/external/corefx/src/System.Security.AccessControl/src/Configurations.props +++ b/external/corefx/src/System.Security.AccessControl/src/Configurations.props @@ -4,11 +4,11 @@ netfx-Windows_NT; netcoreapp2.0-Windows_NT; - netcoreapp2.0-Unix; netstandard; - $(PackageConfigurations) + $(PackageConfigurations); + netcoreapp-Windows_NT; uap-Windows_NT; diff --git a/external/corefx/src/System.Security.AccessControl/src/System.Security.AccessControl.csproj b/external/corefx/src/System.Security.AccessControl/src/System.Security.AccessControl.csproj index 157093fb41..477bf20686 100644 --- a/external/corefx/src/System.Security.AccessControl/src/System.Security.AccessControl.csproj +++ b/external/corefx/src/System.Security.AccessControl/src/System.Security.AccessControl.csproj @@ -5,13 +5,13 @@ System.Security.AccessControl true true - SR.PlatformNotSupported_AccessControl + SR.PlatformNotSupported_AccessControl {D27FFA1F-B446-4D24-B60A-1F88385CDB6D} - - + + @@ -21,7 +21,7 @@ - + @@ -125,14 +125,14 @@ - + - + \ No newline at end of file diff --git a/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/ACE.cs b/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/ACE.cs index 646c7247ef..512ef47c0a 100644 --- a/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/ACE.cs +++ b/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/ACE.cs @@ -11,7 +11,6 @@ using System; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Globalization; using System.Security.Principal; @@ -691,7 +690,6 @@ nameof(binaryForm)); { throw new ArgumentNullException(nameof(securityIdentifier)); } - Contract.EndContractBlock(); // // The values are set by invoking the properties. @@ -742,7 +740,6 @@ nameof(binaryForm)); { throw new ArgumentNullException(nameof(value)); } - Contract.EndContractBlock(); _sid = value; } @@ -791,7 +788,6 @@ nameof(binaryForm)); nameof(type), SR.ArgumentOutOfRange_InvalidUserDefinedAceType); } - Contract.EndContractBlock(); SetOpaque(opaque); } diff --git a/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/ACL.cs.REMOVED.git-id b/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/ACL.cs.REMOVED.git-id index 8b127a9750..fadb069524 100644 --- a/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/ACL.cs.REMOVED.git-id +++ b/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/ACL.cs.REMOVED.git-id @@ -1 +1 @@ -8ef5bb63b88c7c40c11af099df58bad53b2b5125 \ No newline at end of file +1ef6fea67c532152b297d97beb1596946a7b2276 \ No newline at end of file diff --git a/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/CommonObjectSecurity.cs b/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/CommonObjectSecurity.cs index f0612d46b0..b635db4f4c 100644 --- a/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/CommonObjectSecurity.cs +++ b/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/CommonObjectSecurity.cs @@ -13,7 +13,6 @@ using Microsoft.Win32; using System; using System.Collections; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.InteropServices; using System.Security.Principal; @@ -229,7 +228,6 @@ nameof(targetType)); { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); try @@ -360,7 +358,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); try @@ -444,7 +441,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); @@ -465,7 +461,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); @@ -486,7 +481,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); @@ -509,7 +503,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); @@ -535,7 +528,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); @@ -563,7 +555,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); @@ -589,7 +580,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); @@ -610,7 +600,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); @@ -631,7 +620,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); @@ -652,7 +640,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); @@ -673,7 +660,6 @@ nameof(modification), { throw new ArgumentNullException(nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); diff --git a/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/NativeObjectSecurity.cs b/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/NativeObjectSecurity.cs index 8599c1c800..1f58aa2ded 100644 --- a/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/NativeObjectSecurity.cs +++ b/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/NativeObjectSecurity.cs @@ -13,7 +13,6 @@ using Microsoft.Win32; using System; using System.Collections; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Globalization; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -346,7 +345,6 @@ nameof(name)); { throw new ArgumentNullException(nameof(name)); } - Contract.EndContractBlock(); Persist(name, null, includeSections, exceptionContext); } @@ -368,7 +366,6 @@ nameof(name)); { throw new ArgumentNullException(nameof(handle)); } - Contract.EndContractBlock(); Persist(null, handle, includeSections, exceptionContext); } diff --git a/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/ObjectSecurity.cs b/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/ObjectSecurity.cs index 61b47ec00c..c284b32693 100644 --- a/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/ObjectSecurity.cs +++ b/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/ObjectSecurity.cs @@ -13,7 +13,6 @@ using Microsoft.Win32; using System; using System.Collections; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -84,7 +83,6 @@ namespace System.Security.AccessControl { throw new ArgumentNullException( nameof(securityDescriptor)); } - Contract.EndContractBlock(); _securityDescriptor = securityDescriptor; } @@ -369,7 +367,6 @@ namespace System.Security.AccessControl { throw new ArgumentNullException( nameof(identity)); } - Contract.EndContractBlock(); WriteLock(); @@ -413,7 +410,6 @@ namespace System.Security.AccessControl { throw new ArgumentNullException( nameof(identity)); } - Contract.EndContractBlock(); WriteLock(); @@ -434,7 +430,6 @@ namespace System.Security.AccessControl { throw new ArgumentNullException( nameof(identity)); } - Contract.EndContractBlock(); WriteLock(); @@ -455,7 +450,6 @@ namespace System.Security.AccessControl { throw new ArgumentNullException( nameof(identity)); } - Contract.EndContractBlock(); WriteLock(); @@ -605,7 +599,6 @@ namespace System.Security.AccessControl SR.Arg_EnumAtLeastOneFlag, nameof(includeSections)); } - Contract.EndContractBlock(); WriteLock(); @@ -655,7 +648,6 @@ nameof(includeSections)); SR.Arg_EnumAtLeastOneFlag, nameof(includeSections)); } - Contract.EndContractBlock(); WriteLock(); @@ -689,7 +681,6 @@ nameof(includeSections)); SR.AccessControl_InvalidAccessRuleType, nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); @@ -716,7 +707,6 @@ nameof(rule)); SR.AccessControl_InvalidAuditRuleType, nameof(rule)); } - Contract.EndContractBlock(); WriteLock(); diff --git a/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/Privilege.cs b/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/Privilege.cs index 2f40bc147c..a7a154b8f4 100644 --- a/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/Privilege.cs +++ b/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/Privilege.cs @@ -17,7 +17,6 @@ using Microsoft.Win32.SafeHandles; using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -366,7 +365,6 @@ namespace System.Security.AccessControl public SafeTokenHandle ThreadHandle { - [System.Security.SecurityCritical] // auto-generated get { return this.threadHandle; } } @@ -386,7 +384,6 @@ namespace System.Security.AccessControl { throw new ArgumentNullException(nameof(privilegeName)); } - Contract.EndContractBlock(); this.luid = LuidFromPrivilege(privilegeName); } @@ -419,7 +416,6 @@ namespace System.Security.AccessControl #endregion - // [SecurityPermission( SecurityAction.Demand, TogglePrivileges=true )] private void ToggleState(bool enable) { int error = 0; @@ -554,7 +550,6 @@ namespace System.Security.AccessControl } } - // [SecurityPermission( SecurityAction.Demand, TogglePrivileges=true )] public void Revert() { int error = 0; @@ -650,7 +645,6 @@ namespace System.Security.AccessControl { throw new ArgumentNullException( "helper" ); } - Contract.EndContractBlock(); Privilege p = new Privilege( privilege ); diff --git a/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/PrivilegeNotHeldException.cs b/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/PrivilegeNotHeldException.cs index 5c8d6e7a19..e639c2a2ea 100644 --- a/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/PrivilegeNotHeldException.cs +++ b/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/PrivilegeNotHeldException.cs @@ -9,6 +9,9 @@ using System.Runtime.Serialization; namespace System.Security.AccessControl { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public sealed class PrivilegeNotHeldException : UnauthorizedAccessException, ISerializable { private readonly string _privilegeName = null; @@ -30,9 +33,15 @@ namespace System.Security.AccessControl _privilegeName = privilege; } + private PrivilegeNotHeldException(SerializationInfo info, StreamingContext context) : base(info, context) + { + _privilegeName = info.GetString(nameof(PrivilegeName)); + } + public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + info.AddValue(nameof(PrivilegeName), _privilegeName, typeof(string)); } public string PrivilegeName diff --git a/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/Rules.cs b/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/Rules.cs index 4c52c626b6..674e2abe55 100644 --- a/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/Rules.cs +++ b/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/Rules.cs @@ -6,7 +6,6 @@ using System; using System.Collections; using System.Collections.Generic; using System.Security.Principal; -using System.Diagnostics.Contracts; namespace System.Security.AccessControl { @@ -64,7 +63,6 @@ nameof(inheritanceFlags), nameof(propagationFlags), SR.Format(SR.Argument_InvalidEnumValue, inheritanceFlags, "PropagationFlags")); } - Contract.EndContractBlock(); if (identity.IsValidTargetType(typeof(SecurityIdentifier)) == false) { @@ -161,7 +159,6 @@ nameof(inheritanceFlags), nameof(propagationFlags), SR.Format(SR.Argument_InvalidEnumValue, inheritanceFlags, "PropagationFlags")); } - Contract.EndContractBlock(); _type = type; } diff --git a/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/SecurityDescriptor.cs b/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/SecurityDescriptor.cs index 42d50e967b..c4902931de 100644 --- a/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/SecurityDescriptor.cs +++ b/external/corefx/src/System.Security.AccessControl/src/System/Security/AccessControl/SecurityDescriptor.cs @@ -12,11 +12,10 @@ using Microsoft.Win32; using System; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Globalization; using System.Runtime.InteropServices; using System.Security.Principal; - +using System.ComponentModel; namespace System.Security.AccessControl { [Flags] @@ -276,7 +275,6 @@ namespace System.Security.AccessControl nameof(binaryForm), SR.ArgumentOutOfRange_ArrayTooSmall); } - Contract.EndContractBlock(); // // the offset will grow as we go for each additional field (owner, group, @@ -512,7 +510,6 @@ nameof(binaryForm), throw new ArgumentOutOfRangeException(nameof(binaryForm), SR.AccessControl_InvalidSecurityDescriptorRevision); } - Contract.EndContractBlock(); ControlFlags flags; @@ -632,7 +629,6 @@ nameof(binaryForm)); { throw new ArgumentNullException(nameof(sddlForm)); } - Contract.EndContractBlock(); int error; IntPtr byteArray = IntPtr.Zero; @@ -671,8 +667,7 @@ nameof(sddlForm)); else if (error != Interop.Errors.ERROR_SUCCESS) { Debug.Assert(false, string.Format(CultureInfo.InvariantCulture, "Unexpected error out of Win32.ConvertStringSdToSd: {0}", error)); - // TODO : This should be a Win32Exception once that type is available - throw new Exception(); + throw new Win32Exception(error, SR.Format(SR.AccessControl_UnexpectedError, error)); } } @@ -952,7 +947,6 @@ nameof(discretionaryAcl)); { throw new ArgumentNullException(nameof(rawSecurityDescriptor)); } - Contract.EndContractBlock(); CreateFromParts( isContainer, @@ -1213,7 +1207,6 @@ nameof(value)); { throw new ArgumentNullException(nameof(sid)); } - Contract.EndContractBlock(); if (DiscretionaryAcl != null) { @@ -1227,7 +1220,6 @@ nameof(value)); { throw new ArgumentNullException(nameof(sid)); } - Contract.EndContractBlock(); if (SystemAcl != null) { diff --git a/external/corefx/src/System.Security.Claims/ref/System.Security.Claims.cs b/external/corefx/src/System.Security.Claims/ref/System.Security.Claims.cs index 0248ad5319..92d0e3f169 100644 --- a/external/corefx/src/System.Security.Claims/ref/System.Security.Claims.cs +++ b/external/corefx/src/System.Security.Claims/ref/System.Security.Claims.cs @@ -53,7 +53,7 @@ namespace System.Security.Claims protected ClaimsIdentity(System.Runtime.Serialization.SerializationInfo info) { } public System.Security.Claims.ClaimsIdentity Actor { get { throw null; } set { } } public virtual string AuthenticationType { get { throw null; } } - public object BootstrapContext { get { throw null; }[System.Security.SecurityCriticalAttribute]set { } } + public object BootstrapContext { get { throw null; } set { } } public virtual System.Collections.Generic.IEnumerable Claims { get { throw null; } } protected virtual byte[] CustomSerializationData { get { throw null; } } public virtual bool IsAuthenticated { get { throw null; } } @@ -61,9 +61,7 @@ namespace System.Security.Claims public virtual string Name { get { throw null; } } public string NameClaimType { get { throw null; } } public string RoleClaimType { get { throw null; } } - [System.Security.SecurityCriticalAttribute] public virtual void AddClaim(System.Security.Claims.Claim claim) { } - [System.Security.SecurityCriticalAttribute] public virtual void AddClaims(System.Collections.Generic.IEnumerable claims) { } public virtual System.Security.Claims.ClaimsIdentity Clone() { throw null; } protected virtual System.Security.Claims.Claim CreateClaim(System.IO.BinaryReader reader) { throw null; } @@ -74,9 +72,7 @@ namespace System.Security.Claims protected virtual void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public virtual bool HasClaim(System.Predicate match) { throw null; } public virtual bool HasClaim(string type, string value) { throw null; } - [System.Security.SecurityCriticalAttribute] public virtual void RemoveClaim(System.Security.Claims.Claim claim) { } - [System.Security.SecurityCriticalAttribute] public virtual bool TryRemoveClaim(System.Security.Claims.Claim claim) { throw null; } public virtual void WriteTo(System.IO.BinaryWriter writer) { } protected virtual void WriteTo(System.IO.BinaryWriter writer, byte[] userData) { } @@ -90,15 +86,13 @@ namespace System.Security.Claims public ClaimsPrincipal(System.Security.Principal.IPrincipal principal) { } protected ClaimsPrincipal(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public virtual System.Collections.Generic.IEnumerable Claims { get { throw null; } } - public static System.Func ClaimsPrincipalSelector { get { throw null; }[System.Security.SecurityCriticalAttribute]set { } } + public static System.Func ClaimsPrincipalSelector { get { throw null; } set { } } public static System.Security.Claims.ClaimsPrincipal Current { get { throw null; } } protected virtual byte[] CustomSerializationData { get { throw null; } } public virtual System.Collections.Generic.IEnumerable Identities { get { throw null; } } public virtual System.Security.Principal.IIdentity Identity { get { throw null; } } - public static System.Func, System.Security.Claims.ClaimsIdentity> PrimaryIdentitySelector { get { throw null; }[System.Security.SecurityCriticalAttribute]set { } } - [System.Security.SecurityCriticalAttribute] + public static System.Func, System.Security.Claims.ClaimsIdentity> PrimaryIdentitySelector { get { throw null; } set { } } public virtual void AddIdentities(System.Collections.Generic.IEnumerable identities) { } - [System.Security.SecurityCriticalAttribute] public virtual void AddIdentity(System.Security.Claims.ClaimsIdentity identity) { } public virtual System.Security.Claims.ClaimsPrincipal Clone() { throw null; } protected virtual System.Security.Claims.ClaimsIdentity CreateClaimsIdentity(System.IO.BinaryReader reader) { throw null; } diff --git a/external/corefx/src/System.Security.Claims/src/System.Security.Claims.csproj b/external/corefx/src/System.Security.Claims/src/System.Security.Claims.csproj index 3dad906704..75bd0d8293 100644 --- a/external/corefx/src/System.Security.Claims/src/System.Security.Claims.csproj +++ b/external/corefx/src/System.Security.Claims/src/System.Security.Claims.csproj @@ -21,7 +21,6 @@ - diff --git a/external/corefx/src/System.Security.Claims/src/System/Security/Claims/Claim.cs b/external/corefx/src/System.Security.Claims/src/System/Security/Claims/Claim.cs index b85e6808e6..e08593a1a2 100644 --- a/external/corefx/src/System.Security.Claims/src/System/Security/Claims/Claim.cs +++ b/external/corefx/src/System.Security.Claims/src/System/Security/Claims/Claim.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Diagnostics.Contracts; using System.IO; namespace System.Security.Claims @@ -265,8 +264,6 @@ namespace System.Security.Claims throw new ArgumentNullException(nameof(value)); } - Contract.EndContractBlock(); - _type = type; _value = value; _valueType = string.IsNullOrEmpty(valueType) ? ClaimValueTypes.String : valueType; diff --git a/external/corefx/src/System.Security.Claims/src/System/Security/Claims/ClaimsIdentity.cs b/external/corefx/src/System.Security.Claims/src/System/Security/Claims/ClaimsIdentity.cs index 6e1f14ed81..0c2c9542e1 100644 --- a/external/corefx/src/System.Security.Claims/src/System/Security/Claims/ClaimsIdentity.cs +++ b/external/corefx/src/System.Security.Claims/src/System/Security/Claims/ClaimsIdentity.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Diagnostics.Contracts; using System.IO; using System.Runtime.Serialization; using System.Security.Principal; @@ -256,7 +255,6 @@ namespace System.Security.Claims /// The to read from. /// /// Thrown is the is null. - [SecurityCritical] protected ClaimsIdentity(SerializationInfo info) { throw new PlatformNotSupportedException(); @@ -435,8 +433,6 @@ namespace System.Security.Claims throw new ArgumentNullException(nameof(claim)); } - Contract.EndContractBlock(); - if (object.ReferenceEquals(claim.Subject, this)) { _instanceClaims.Add(claim); @@ -460,8 +456,6 @@ namespace System.Security.Claims throw new ArgumentNullException(nameof(claims)); } - Contract.EndContractBlock(); - foreach (Claim claim in claims) { if (claim == null) @@ -579,8 +573,6 @@ namespace System.Security.Claims throw new ArgumentNullException(nameof(match)); } - Contract.EndContractBlock(); - foreach (Claim claim in Claims) { if (match(claim)) @@ -604,8 +596,6 @@ namespace System.Security.Claims throw new ArgumentNullException(nameof(type)); } - Contract.EndContractBlock(); - foreach (Claim claim in Claims) { if (claim != null) @@ -631,8 +621,6 @@ namespace System.Security.Claims throw new ArgumentNullException(nameof(match)); } - Contract.EndContractBlock(); - foreach (Claim claim in Claims) { if (match(claim)) @@ -658,8 +646,6 @@ namespace System.Security.Claims throw new ArgumentNullException(nameof(type)); } - Contract.EndContractBlock(); - foreach (Claim claim in Claims) { if (claim != null) @@ -687,8 +673,6 @@ namespace System.Security.Claims throw new ArgumentNullException(nameof(match)); } - Contract.EndContractBlock(); - foreach (Claim claim in Claims) { if (match(claim)) @@ -721,8 +705,6 @@ namespace System.Security.Claims throw new ArgumentNullException(nameof(value)); } - Contract.EndContractBlock(); - foreach (Claim claim in Claims) { if (claim != null diff --git a/external/corefx/src/System.Security.Claims/src/System/Security/Claims/ClaimsPrincipal.cs b/external/corefx/src/System.Security.Claims/src/System/Security/Claims/ClaimsPrincipal.cs index d942edcb69..4456d9e097 100644 --- a/external/corefx/src/System.Security.Claims/src/System/Security/Claims/ClaimsPrincipal.cs +++ b/external/corefx/src/System.Security.Claims/src/System/Security/Claims/ClaimsPrincipal.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Diagnostics.Contracts; using System.IO; using System.Runtime.Serialization; using System.Security.Principal; @@ -98,8 +97,6 @@ namespace System.Security.Claims throw new ArgumentNullException(nameof(identities)); } - Contract.EndContractBlock(); - _identities.AddRange(identities); } @@ -115,8 +112,6 @@ namespace System.Security.Claims throw new ArgumentNullException(nameof(identity)); } - Contract.EndContractBlock(); - ClaimsIdentity ci = identity as ClaimsIdentity; if (ci != null) { @@ -140,8 +135,6 @@ namespace System.Security.Claims throw new ArgumentNullException(nameof(principal)); } - Contract.EndContractBlock(); - // // If IPrincipal is a ClaimsPrincipal add all of the identities // If IPrincipal is not a ClaimsPrincipal, create a new identity from IPrincipal.Identity @@ -212,8 +205,6 @@ namespace System.Security.Claims throw new ArgumentNullException(nameof(identity)); } - Contract.EndContractBlock(); - _identities.Add(identity); } @@ -229,8 +220,6 @@ namespace System.Security.Claims throw new ArgumentNullException(nameof(identities)); } - Contract.EndContractBlock(); - _identities.AddRange(identities); } @@ -317,8 +306,6 @@ namespace System.Security.Claims throw new ArgumentNullException(nameof(match)); } - Contract.EndContractBlock(); - foreach (ClaimsIdentity identity in Identities) { if (identity != null) @@ -345,7 +332,6 @@ namespace System.Security.Claims throw new ArgumentNullException(nameof(type)); } - Contract.EndContractBlock(); foreach (ClaimsIdentity identity in Identities) { if (identity != null) @@ -372,8 +358,6 @@ namespace System.Security.Claims throw new ArgumentNullException(nameof(match)); } - Contract.EndContractBlock(); - Claim claim = null; foreach (ClaimsIdentity identity in Identities) @@ -405,8 +389,6 @@ namespace System.Security.Claims throw new ArgumentNullException(nameof(type)); } - Contract.EndContractBlock(); - Claim claim = null; for (int i = 0; i < _identities.Count; i++) @@ -438,8 +420,6 @@ namespace System.Security.Claims throw new ArgumentNullException(nameof(match)); } - Contract.EndContractBlock(); - for (int i = 0; i < _identities.Count; i++) { if (_identities[i] != null) @@ -475,8 +455,6 @@ namespace System.Security.Claims throw new ArgumentNullException(nameof(value)); } - Contract.EndContractBlock(); - for (int i = 0; i < _identities.Count; i++) { if (_identities[i] != null) diff --git a/external/corefx/src/System.Security.Claims/src/System/Security/Claims/GenericIdentity.cs b/external/corefx/src/System.Security.Claims/src/System/Security/Claims/GenericIdentity.cs index 3685c91a63..78886e41b6 100644 --- a/external/corefx/src/System.Security.Claims/src/System/Security/Claims/GenericIdentity.cs +++ b/external/corefx/src/System.Security.Claims/src/System/Security/Claims/GenericIdentity.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Diagnostics.Contracts; using System.Security.Claims; namespace System.Security.Principal @@ -18,7 +17,6 @@ namespace System.Security.Principal { if (name == null) throw new ArgumentNullException(nameof(name)); - Contract.EndContractBlock(); m_name = name; m_type = ""; @@ -32,7 +30,6 @@ namespace System.Security.Principal throw new ArgumentNullException(nameof(name)); if (type == null) throw new ArgumentNullException(nameof(type)); - Contract.EndContractBlock(); m_name = name; m_type = type; diff --git a/external/corefx/src/System.Security.Claims/src/System/Security/Claims/GenericPrincipal.cs b/external/corefx/src/System.Security.Claims/src/System/Security/Claims/GenericPrincipal.cs index dda094a27b..e3235fbc47 100644 --- a/external/corefx/src/System.Security.Claims/src/System/Security/Claims/GenericPrincipal.cs +++ b/external/corefx/src/System.Security.Claims/src/System/Security/Claims/GenericPrincipal.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Diagnostics.Contracts; using System.Security.Claims; namespace System.Security.Principal @@ -18,7 +17,6 @@ namespace System.Security.Principal { if (identity == null) throw new ArgumentNullException(nameof(identity)); - Contract.EndContractBlock(); m_identity = identity; if (roles != null) diff --git a/external/corefx/src/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs b/external/corefx/src/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs index 83724876ab..b5030ad693 100644 --- a/external/corefx/src/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs +++ b/external/corefx/src/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs @@ -5,7 +5,6 @@ // Changes to this file must follow the http://aka.ms/api-review process. // ------------------------------------------------------------------------------ - namespace System.Security.Cryptography { public abstract partial class Aes : System.Security.Cryptography.SymmetricAlgorithm @@ -18,8 +17,8 @@ namespace System.Security.Cryptography public sealed partial class AesManaged : System.Security.Cryptography.Aes { public AesManaged() { } - public override int FeedbackSize { get { throw null; } set { } } public override int BlockSize { get { throw null; } set { } } + public override int FeedbackSize { get { throw null; } set { } } public override byte[] IV { get { throw null; } set { } } public override byte[] Key { get { throw null; } set { } } public override int KeySize { get { throw null; } set { } } @@ -66,14 +65,6 @@ namespace System.Security.Cryptography public abstract void SetHashAlgorithm(string strName); public abstract void SetKey(System.Security.Cryptography.AsymmetricAlgorithm key); } - public abstract partial class DeriveBytes : System.IDisposable - { - protected DeriveBytes() { } - public void Dispose() { } - protected virtual void Dispose(bool disposing) { } - public abstract byte[] GetBytes(int cb); - public abstract void Reset(); - } public partial class CryptoConfig { public CryptoConfig() { } @@ -85,6 +76,14 @@ namespace System.Security.Cryptography public static byte[] EncodeOID(string str) { throw null; } public static string MapNameToOID(string name) { throw null; } } + public abstract partial class DeriveBytes : System.IDisposable + { + protected DeriveBytes() { } + public void Dispose() { } + protected virtual void Dispose(bool disposing) { } + public abstract byte[] GetBytes(int cb); + public abstract void Reset(); + } [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] public abstract partial class DES : System.Security.Cryptography.SymmetricAlgorithm { @@ -117,7 +116,6 @@ namespace System.Security.Cryptography public virtual bool VerifyData(System.IO.Stream data, byte[] signature, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; } public abstract bool VerifySignature(byte[] rgbHash, byte[] rgbSignature); } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct DSAParameters { public int Counter; @@ -145,9 +143,9 @@ namespace System.Security.Cryptography public override void SetHashAlgorithm(string strName) { } public override void SetKey(System.Security.Cryptography.AsymmetricAlgorithm key) { } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct ECCurve { + private object _dummy; public byte[] A; public byte[] B; public byte[] Cofactor; @@ -231,7 +229,6 @@ namespace System.Security.Cryptography public bool VerifyData(System.IO.Stream data, byte[] signature, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; } public abstract bool VerifyHash(byte[] hash, byte[] signature); } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct ECParameters { public System.Security.Cryptography.ECCurve Curve; @@ -239,7 +236,6 @@ namespace System.Security.Cryptography public System.Security.Cryptography.ECPoint Q; public void Validate() { } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct ECPoint { public byte[] X; @@ -252,8 +248,10 @@ namespace System.Security.Cryptography public override byte[] Key { get { throw null; } set { } } protected override void Dispose(bool disposing) { } protected override void HashCore(byte[] rgb, int ib, int cb) { } + protected override void HashCore(System.ReadOnlySpan source) { } protected override byte[] HashFinal() { throw null; } public override void Initialize() { } + protected override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } public partial class HMACSHA1 : System.Security.Cryptography.HMAC { @@ -264,8 +262,10 @@ namespace System.Security.Cryptography public override byte[] Key { get { throw null; } set { } } protected override void Dispose(bool disposing) { } protected override void HashCore(byte[] rgb, int ib, int cb) { } + protected override void HashCore(System.ReadOnlySpan source) { } protected override byte[] HashFinal() { throw null; } public override void Initialize() { } + protected override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } public partial class HMACSHA256 : System.Security.Cryptography.HMAC { @@ -274,30 +274,36 @@ namespace System.Security.Cryptography public override byte[] Key { get { throw null; } set { } } protected override void Dispose(bool disposing) { } protected override void HashCore(byte[] rgb, int ib, int cb) { } + protected override void HashCore(System.ReadOnlySpan source) { } protected override byte[] HashFinal() { throw null; } public override void Initialize() { } + protected override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } public partial class HMACSHA384 : System.Security.Cryptography.HMAC { public HMACSHA384() { } public HMACSHA384(byte[] key) { } - public bool ProduceLegacyHmacValues { get; set; } public override byte[] Key { get { throw null; } set { } } + public bool ProduceLegacyHmacValues { get { throw null; } set { } } protected override void Dispose(bool disposing) { } protected override void HashCore(byte[] rgb, int ib, int cb) { } + protected override void HashCore(System.ReadOnlySpan source) { } protected override byte[] HashFinal() { throw null; } public override void Initialize() { } + protected override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } public partial class HMACSHA512 : System.Security.Cryptography.HMAC { public HMACSHA512() { } public HMACSHA512(byte[] key) { } - public bool ProduceLegacyHmacValues { get; set; } public override byte[] Key { get { throw null; } set { } } + public bool ProduceLegacyHmacValues { get { throw null; } set { } } protected override void Dispose(bool disposing) { } protected override void HashCore(byte[] rgb, int ib, int cb) { } + protected override void HashCore(System.ReadOnlySpan source) { } protected override byte[] HashFinal() { throw null; } public override void Initialize() { } + protected override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } public sealed partial class IncrementalHash : System.IDisposable { @@ -310,17 +316,17 @@ namespace System.Security.Cryptography public void Dispose() { } public byte[] GetHashAndReset() { throw null; } } + public abstract partial class MaskGenerationMethod + { + public abstract byte[] GenerateMask(byte[] rgbSeed, int cbReturn); + } public abstract partial class MD5 : System.Security.Cryptography.HashAlgorithm { protected MD5() { } public static new System.Security.Cryptography.MD5 Create() { throw null; } public static new System.Security.Cryptography.MD5 Create(string algName) { throw null; } } - public abstract class MaskGenerationMethod - { - public abstract byte[] GenerateMask(byte[] rgbSeed, int cbReturn); - } - public class PKCS1MaskGenerationMethod : MaskGenerationMethod + public partial class PKCS1MaskGenerationMethod : System.Security.Cryptography.MaskGenerationMethod { public PKCS1MaskGenerationMethod() { } public string HashName { get { throw null; } set { } } @@ -347,6 +353,24 @@ namespace System.Security.Cryptography public static new System.Security.Cryptography.RC2 Create() { throw null; } public static new System.Security.Cryptography.RC2 Create(string AlgName) { throw null; } } + public partial class Rfc2898DeriveBytes : System.Security.Cryptography.DeriveBytes + { + public Rfc2898DeriveBytes(byte[] password, byte[] salt, int iterations) { } + public Rfc2898DeriveBytes(byte[] password, byte[] salt, int iterations, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { } + public Rfc2898DeriveBytes(string password, byte[] salt) { } + public Rfc2898DeriveBytes(string password, byte[] salt, int iterations) { } + public Rfc2898DeriveBytes(string password, byte[] salt, int iterations, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { } + public Rfc2898DeriveBytes(string password, int saltSize) { } + public Rfc2898DeriveBytes(string password, int saltSize, int iterations) { } + public Rfc2898DeriveBytes(string password, int saltSize, int iterations, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { } + public System.Security.Cryptography.HashAlgorithmName HashAlgorithm { get { throw null; } } + public int IterationCount { get { throw null; } set { } } + public byte[] Salt { get { throw null; } set { } } + public byte[] CryptDeriveKey(string algname, string alghashname, int keySize, byte[] rgbIV) { throw null; } + protected override void Dispose(bool disposing) { } + public override byte[] GetBytes(int cb) { throw null; } + public override void Reset() { } + } [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))] public abstract partial class Rijndael : System.Security.Cryptography.SymmetricAlgorithm { @@ -373,24 +397,6 @@ namespace System.Security.Cryptography public override void GenerateIV() { } public override void GenerateKey() { } } - public partial class Rfc2898DeriveBytes : System.Security.Cryptography.DeriveBytes - { - public Rfc2898DeriveBytes(byte[] password, byte[] salt, int iterations) { } - public Rfc2898DeriveBytes(byte[] password, byte[] salt, int iterations, HashAlgorithmName hashAlgorithm) { } - public Rfc2898DeriveBytes(string password, byte[] salt) { } - public Rfc2898DeriveBytes(string password, byte[] salt, int iterations) { } - public Rfc2898DeriveBytes(string password, byte[] salt, int iterations, HashAlgorithmName hashAlgorithm) { } - public Rfc2898DeriveBytes(string password, int saltSize) { } - public Rfc2898DeriveBytes(string password, int saltSize, int iterations) { } - public Rfc2898DeriveBytes(string password, int saltSize, int iterations, HashAlgorithmName hashAlgorithm) { } - public HashAlgorithmName HashAlgorithm { get { throw null; } } - public int IterationCount { get { throw null; } set { } } - public byte[] Salt { get { throw null; } set { } } - public byte[] CryptDeriveKey(string algname, string alghashname, int keySize, byte[] rgbIV) { throw null; } - protected override void Dispose(bool disposing) { } - public override byte[] GetBytes(int cb) { throw null; } - public override void Reset() { } - } public abstract partial class RSA : System.Security.Cryptography.AsymmetricAlgorithm { protected RSA() { } @@ -398,16 +404,16 @@ namespace System.Security.Cryptography public override string SignatureAlgorithm { get { throw null; } } public static new System.Security.Cryptography.RSA Create() { throw null; } public static System.Security.Cryptography.RSA Create(int keySizeInBits) { throw null; } - public static new System.Security.Cryptography.RSA Create(string algName) { throw null; } public static System.Security.Cryptography.RSA Create(System.Security.Cryptography.RSAParameters parameters) { throw null; } + public static new System.Security.Cryptography.RSA Create(string algName) { throw null; } public virtual byte[] Decrypt(byte[] data, System.Security.Cryptography.RSAEncryptionPadding padding) { throw null; } public virtual byte[] DecryptValue(byte[] rgb) { throw null; } public virtual byte[] Encrypt(byte[] data, System.Security.Cryptography.RSAEncryptionPadding padding) { throw null; } public virtual byte[] EncryptValue(byte[] rgb) { throw null; } public abstract System.Security.Cryptography.RSAParameters ExportParameters(bool includePrivateParameters); + public override void FromXmlString(string xmlString) { } protected virtual byte[] HashData(byte[] data, int offset, int count, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; } protected virtual byte[] HashData(System.IO.Stream data, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; } - public override void FromXmlString(string xmlString) { } public abstract void ImportParameters(System.Security.Cryptography.RSAParameters parameters); public virtual byte[] SignData(byte[] data, int offset, int count, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.RSASignaturePadding padding) { throw null; } public byte[] SignData(byte[] data, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.RSASignaturePadding padding) { throw null; } @@ -456,12 +462,11 @@ namespace System.Security.Cryptography public RSAOAEPKeyExchangeFormatter(System.Security.Cryptography.AsymmetricAlgorithm key) { } public byte[] Parameter { get { throw null; } set { } } public override string Parameters { get { throw null; } } - public RandomNumberGenerator Rng { get { throw null; } set { } } + public System.Security.Cryptography.RandomNumberGenerator Rng { get { throw null; } set { } } public override byte[] CreateKeyExchange(byte[] rgbData) { throw null; } public override byte[] CreateKeyExchange(byte[] rgbData, System.Type symAlgType) { throw null; } public override void SetKey(System.Security.Cryptography.AsymmetricAlgorithm key) { } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct RSAParameters { public byte[] D; @@ -477,8 +482,8 @@ namespace System.Security.Cryptography { public RSAPKCS1KeyExchangeDeformatter() { } public RSAPKCS1KeyExchangeDeformatter(System.Security.Cryptography.AsymmetricAlgorithm key) { } - public RandomNumberGenerator RNG { get { throw null; } set { } } public override string Parameters { get { throw null; } set { } } + public System.Security.Cryptography.RandomNumberGenerator RNG { get { throw null; } set { } } public override byte[] DecryptKeyExchange(byte[] rgbIn) { throw null; } public override void SetKey(System.Security.Cryptography.AsymmetricAlgorithm key) { } } @@ -487,7 +492,7 @@ namespace System.Security.Cryptography public RSAPKCS1KeyExchangeFormatter() { } public RSAPKCS1KeyExchangeFormatter(System.Security.Cryptography.AsymmetricAlgorithm key) { } public override string Parameters { get { throw null; } } - public RandomNumberGenerator Rng { get { throw null; } set { } } + public System.Security.Cryptography.RandomNumberGenerator Rng { get { throw null; } set { } } public override byte[] CreateKeyExchange(byte[] rgbData) { throw null; } public override byte[] CreateKeyExchange(byte[] rgbData, System.Type symAlgType) { throw null; } public override void SetKey(System.Security.Cryptography.AsymmetricAlgorithm key) { } @@ -538,8 +543,10 @@ namespace System.Security.Cryptography public SHA1Managed() { } protected sealed override void Dispose(bool disposing) { } protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) { } + protected sealed override void HashCore(System.ReadOnlySpan source) { } protected sealed override byte[] HashFinal() { throw null; } public sealed override void Initialize() { } + protected sealed override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } public abstract partial class SHA256 : System.Security.Cryptography.HashAlgorithm { @@ -553,8 +560,10 @@ namespace System.Security.Cryptography public SHA256Managed() { } protected sealed override void Dispose(bool disposing) { } protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) { } + protected sealed override void HashCore(System.ReadOnlySpan source) { } protected sealed override byte[] HashFinal() { throw null; } public sealed override void Initialize() { } + protected sealed override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } public abstract partial class SHA384 : System.Security.Cryptography.HashAlgorithm { @@ -568,8 +577,10 @@ namespace System.Security.Cryptography public SHA384Managed() { } protected sealed override void Dispose(bool disposing) { } protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) { } + protected sealed override void HashCore(System.ReadOnlySpan source) { } protected sealed override byte[] HashFinal() { throw null; } public sealed override void Initialize() { } + protected sealed override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } public abstract partial class SHA512 : System.Security.Cryptography.HashAlgorithm { @@ -583,16 +594,19 @@ namespace System.Security.Cryptography public SHA512Managed() { } protected sealed override void Dispose(bool disposing) { } protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) { } + protected sealed override void HashCore(System.ReadOnlySpan source) { } protected sealed override byte[] HashFinal() { throw null; } public sealed override void Initialize() { } + protected sealed override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } - public partial class SignatureDescription { + public partial class SignatureDescription + { public SignatureDescription() { } public SignatureDescription(System.Security.SecurityElement el) { } public string DeformatterAlgorithm { get { throw null; } set { } } - public string DigestAlgorithm { get { throw null;} set { } } - public string FormatterAlgorithm { get { throw null;} set { } } - public string KeyAlgorithm { get { throw null;} set { } } + public string DigestAlgorithm { get { throw null; } set { } } + public string FormatterAlgorithm { get { throw null; } set { } } + public string KeyAlgorithm { get { throw null; } set { } } public virtual System.Security.Cryptography.AsymmetricSignatureDeformatter CreateDeformatter(System.Security.Cryptography.AsymmetricAlgorithm key) { throw null; } public virtual System.Security.Cryptography.HashAlgorithm CreateDigest() { throw null; } public virtual System.Security.Cryptography.AsymmetricSignatureFormatter CreateFormatter(System.Security.Cryptography.AsymmetricAlgorithm key) { throw null; } diff --git a/external/corefx/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs b/external/corefx/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs index 01f5b4da0a..999167d6d1 100644 --- a/external/corefx/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs +++ b/external/corefx/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs @@ -95,7 +95,7 @@ namespace Internal.Cryptography } uint length = (uint)destination.Length; - Check(Interop.Crypto.EvpDigestFinalEx(_ctx, ref destination.DangerousGetPinnableReference(), ref length)); + Check(Interop.Crypto.EvpDigestFinalEx(_ctx, ref MemoryMarshal.GetReference(destination), ref length)); Debug.Assert(length == _hashSize); bytesWritten = (int)length; @@ -132,7 +132,7 @@ namespace Internal.Cryptography throw new CryptographicException(); } - _hmacCtx = Interop.Crypto.HmacCreate(ref new Span(key).DangerousGetPinnableReference(), key.Length, algorithmEvp); + _hmacCtx = Interop.Crypto.HmacCreate(ref MemoryMarshal.GetReference(new Span(key)), key.Length, algorithmEvp); Interop.Crypto.CheckValidOpenSslHandle(_hmacCtx); } @@ -157,7 +157,7 @@ namespace Internal.Cryptography } int length = destination.Length; - Check(Interop.Crypto.HmacFinal(_hmacCtx, ref destination.DangerousGetPinnableReference(), ref length)); + Check(Interop.Crypto.HmacFinal(_hmacCtx, ref MemoryMarshal.GetReference(destination), ref length)); Debug.Assert(length == _hashSize); bytesWritten = length; diff --git a/external/corefx/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.cs b/external/corefx/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.cs index 57e0be7b79..5d528f158f 100644 --- a/external/corefx/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.cs +++ b/external/corefx/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.cs @@ -2,6 +2,8 @@ // 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.Runtime.InteropServices; + namespace System.Security.Cryptography { internal sealed partial class RandomNumberGeneratorImplementation : RandomNumberGenerator @@ -22,7 +24,7 @@ namespace System.Security.Cryptography { if (data.Length > 0) { - GetBytes(ref data.DangerousGetPinnableReference(), data.Length); + GetBytes(ref MemoryMarshal.GetReference(data), data.Length); } } diff --git a/external/corefx/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj b/external/corefx/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj index 2b1d29be34..3ca3bbc338 100644 --- a/external/corefx/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj +++ b/external/corefx/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj @@ -478,9 +478,9 @@ - + @@ -500,4 +500,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.Security.Cryptography.Algorithms/tests/CryptoConfigTests.cs b/external/corefx/src/System.Security.Cryptography.Algorithms/tests/CryptoConfigTests.cs index fdc43d93cf..9e78d3bddb 100644 --- a/external/corefx/src/System.Security.Cryptography.Algorithms/tests/CryptoConfigTests.cs +++ b/external/corefx/src/System.Security.Cryptography.Algorithms/tests/CryptoConfigTests.cs @@ -245,11 +245,11 @@ namespace System.Security.Cryptography.CryptoConfigTests yield return new object[] { "X509Chain", "System.Security.Cryptography.X509Certificates.X509Chain", true }; // PKCS9 attributes - yield return new object[] { "1.2.840.113549.1.9.3", "System.Security.Cryptography.Pkcs.Pkcs9ContentType", false }; - yield return new object[] { "1.2.840.113549.1.9.4", "System.Security.Cryptography.Pkcs.Pkcs9MessageDigest", false }; - yield return new object[] { "1.2.840.113549.1.9.5", "System.Security.Cryptography.Pkcs.Pkcs9SigningTime", false }; - yield return new object[] { "1.3.6.1.4.1.311.88.2.1", "System.Security.Cryptography.Pkcs.Pkcs9DocumentName", false }; - yield return new object[] { "1.3.6.1.4.1.311.88.2.2", "System.Security.Cryptography.Pkcs.Pkcs9DocumentDescription", false }; + yield return new object[] { "1.2.840.113549.1.9.3", "System.Security.Cryptography.Pkcs.Pkcs9ContentType", true }; + yield return new object[] { "1.2.840.113549.1.9.4", "System.Security.Cryptography.Pkcs.Pkcs9MessageDigest", true }; + yield return new object[] { "1.2.840.113549.1.9.5", "System.Security.Cryptography.Pkcs.Pkcs9SigningTime", true }; + yield return new object[] { "1.3.6.1.4.1.311.88.2.1", "System.Security.Cryptography.Pkcs.Pkcs9DocumentName", true }; + yield return new object[] { "1.3.6.1.4.1.311.88.2.2", "System.Security.Cryptography.Pkcs.Pkcs9DocumentDescription", true }; } } diff --git a/external/corefx/src/System.Security.Cryptography.Cng/pkg/System.Security.Cryptography.Cng.pkgproj b/external/corefx/src/System.Security.Cryptography.Cng/pkg/System.Security.Cryptography.Cng.pkgproj index e51943696b..149f0eaee0 100644 --- a/external/corefx/src/System.Security.Cryptography.Cng/pkg/System.Security.Cryptography.Cng.pkgproj +++ b/external/corefx/src/System.Security.Cryptography.Cng/pkg/System.Security.Cryptography.Cng.pkgproj @@ -3,7 +3,8 @@ - net461;netcoreapp2.1;$(UAPvNextTFM);$(AllXamarinFrameworks) + + net461;netcoreapp2.1;$(UAPvNextTFM) diff --git a/external/corefx/src/System.Security.Cryptography.Cng/ref/System.Security.Cryptography.Cng.cs b/external/corefx/src/System.Security.Cryptography.Cng/ref/System.Security.Cryptography.Cng.cs index 56e7dc46ad..1d6c944bf9 100644 --- a/external/corefx/src/System.Security.Cryptography.Cng/ref/System.Security.Cryptography.Cng.cs +++ b/external/corefx/src/System.Security.Cryptography.Cng/ref/System.Security.Cryptography.Cng.cs @@ -5,7 +5,6 @@ // Changes to this file must follow the http://aka.ms/api-review process. // ------------------------------------------------------------------------------ - namespace Microsoft.Win32.SafeHandles { public abstract partial class SafeNCryptHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid @@ -197,9 +196,9 @@ namespace System.Security.Cryptography None = 0, Signing = 2, } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct CngProperty : System.IEquatable { + private object _dummy; public CngProperty(string name, byte[] value, System.Security.Cryptography.CngPropertyOptions options) { throw null; } public string Name { get { throw null; } } public System.Security.Cryptography.CngPropertyOptions Options { get { throw null; } } diff --git a/external/corefx/src/System.Security.Cryptography.Cng/ref/System.Security.Cryptography.Cng.csproj b/external/corefx/src/System.Security.Cryptography.Cng/ref/System.Security.Cryptography.Cng.csproj index 2eb01023c3..dc15bd1da2 100644 --- a/external/corefx/src/System.Security.Cryptography.Cng/ref/System.Security.Cryptography.Cng.csproj +++ b/external/corefx/src/System.Security.Cryptography.Cng/ref/System.Security.Cryptography.Cng.csproj @@ -6,9 +6,11 @@ {9FD12550-3A7C-49D3-9A1E-C4B7410989DD} $(DefineConstants);FEATURE_HASHDATA true - - 4.3.1.0 + + 4.3.0.0 diff --git a/external/corefx/src/System.Security.Cryptography.Cng/src/Configurations.props b/external/corefx/src/System.Security.Cryptography.Cng/src/Configurations.props index 2eb688563f..cedf82dd10 100644 --- a/external/corefx/src/System.Security.Cryptography.Cng/src/Configurations.props +++ b/external/corefx/src/System.Security.Cryptography.Cng/src/Configurations.props @@ -7,6 +7,7 @@ netstandard1.4; netfx-Windows_NT; netcoreapp-Windows_NT; + netcoreapp; net462-Windows_NT; net47-Windows_NT; diff --git a/external/corefx/src/System.Security.Cryptography.Cng/src/Internal/Cryptography/CngAlgorithmCore.cs b/external/corefx/src/System.Security.Cryptography.Cng/src/Internal/Cryptography/CngAlgorithmCore.cs index 7507e22853..08c1404c19 100644 --- a/external/corefx/src/System.Security.Cryptography.Cng/src/Internal/Cryptography/CngAlgorithmCore.cs +++ b/external/corefx/src/System.Security.Cryptography.Cng/src/Internal/Cryptography/CngAlgorithmCore.cs @@ -23,14 +23,6 @@ namespace Internal.Cryptography } } - public bool IsKeyGenerated - { - get - { - return (_lazyKey != null); - } - } - public bool IsKeyGeneratedNamedCurve() { return (_lazyKey != null && _lazyKey.IsECNamedCurve()); diff --git a/external/corefx/src/System.Security.Cryptography.Cng/src/Internal/Cryptography/SymmetricImportExportExtensions.cs b/external/corefx/src/System.Security.Cryptography.Cng/src/Internal/Cryptography/SymmetricImportExportExtensions.cs index 1937428966..00126f23a4 100644 --- a/external/corefx/src/System.Security.Cryptography.Cng/src/Internal/Cryptography/SymmetricImportExportExtensions.cs +++ b/external/corefx/src/System.Security.Cryptography.Cng/src/Internal/Cryptography/SymmetricImportExportExtensions.cs @@ -13,35 +13,6 @@ namespace Internal.Cryptography { internal static class SymmetricImportExportExtensions { - public static CngKey ToCngKey(this byte[] key, string algorithm) - { - int capacity = SizeOf_NCRYPT_KEY_BLOB_HEADER_SIZE + (algorithm.Length + 1) * 2 + SizeOf_BCRYPT_KEY_DATA_BLOB_HEADER + key.Length; - using (MemoryStream ms = new MemoryStream(capacity)) - { - using (BinaryWriter bw = new BinaryWriter(ms, Encoding.Unicode)) - { - // Write out a NCRYPT_KEY_BLOB_HEADER - bw.Write((int)SizeOf_NCRYPT_KEY_BLOB_HEADER_SIZE); // NCRYPT_KEY_BLOB_HEADER.cbSize - bw.Write((int)Interop.NCrypt.NCRYPT_CIPHER_KEY_BLOB_MAGIC); // NCRYPT_KEY_BLOB_HEADER.dwMagic - bw.Write((int)((algorithm.Length + 1) * 2)); // NCRYPT_KEY_BLOB_HEADER.cbAlgName - bw.Write((int)(SizeOf_BCRYPT_KEY_DATA_BLOB_HEADER + key.Length)); // NCRYPT_KEY_BLOB_HEADER.cbKey = sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) + key.Length - - bw.Write(algorithm.ToCharArray()); // Write out the algorithm name (unicode null-terminated string) - bw.Write((char)0); - - // Write out a BCRYPT_KEY_DATA_BLOB_HEADER - bw.Write((int)Interop.BCrypt.BCRYPT_KEY_DATA_BLOB_MAGIC); // BCRYPT_KEY_DATA_BLOB_HEADER.dwMagic - bw.Write((int)Interop.BCrypt.BCRYPT_KEY_DATA_BLOB_VERSION1); // BCRYPT_KEY_DATA_BLOB_HEADER.dwVersion - bw.Write((int)(key.Length)); // BCRYPT_KEY_DATA_BLOB_HEADER.cbKeyData - - bw.Write((byte[])key); // Write out the key data. - } - byte[] keyBlob = ms.ToArray(); - - CngKey cngKey = CngKey.Import(keyBlob, s_cipherKeyBlobFormat); - return cngKey; - } - } /// /// Note! This can and likely will throw if the algorithm was given a hardware-based key. diff --git a/external/corefx/src/System.Security.Cryptography.Cng/src/System.Security.Cryptography.Cng.csproj b/external/corefx/src/System.Security.Cryptography.Cng/src/System.Security.Cryptography.Cng.csproj index 3da22dc865..2037845560 100644 --- a/external/corefx/src/System.Security.Cryptography.Cng/src/System.Security.Cryptography.Cng.csproj +++ b/external/corefx/src/System.Security.Cryptography.Cng/src/System.Security.Cryptography.Cng.csproj @@ -11,10 +11,11 @@ true $(DefineConstants);uap - + SR.PlatformNotSupported_CryptographyCng 4.0.0.0 4.1.0.0 + 4.3.0.0 @@ -279,6 +280,7 @@ + diff --git a/external/corefx/src/System.Security.Cryptography.Cng/src/System/Security/Cryptography/CngKey.EC.cs b/external/corefx/src/System.Security.Cryptography.Cng/src/System/Security/Cryptography/CngKey.EC.cs index 02e197eaa3..442c0e5e3e 100644 --- a/external/corefx/src/System.Security.Cryptography.Cng/src/System/Security/Cryptography/CngKey.EC.cs +++ b/external/corefx/src/System.Security.Cryptography.Cng/src/System/Security/Cryptography/CngKey.EC.cs @@ -10,16 +10,6 @@ namespace System.Security.Cryptography { public sealed partial class CngKey : IDisposable { - /// - /// Does the key use elliptic curve cryptography - /// - /// - internal bool IsEcc() - { - return (AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman || - AlgorithmGroup == CngAlgorithmGroup.ECDsa); - } - /// /// Does the key represent a named curve (Win10+) /// @@ -43,14 +33,14 @@ namespace System.Security.Cryptography } // Use hard-coded values (for use with pre-Win10 APIs) - return GetECSpecificCurveName(); + return GetECSpecificCurveName(); } private string GetECSpecificCurveName() { string algorithm = Algorithm.Algorithm; - if (algorithm == CngAlgorithm.ECDiffieHellmanP256.Algorithm || + if (algorithm == CngAlgorithm.ECDiffieHellmanP256.Algorithm || algorithm == CngAlgorithm.ECDsaP256.Algorithm) { return "nistP256"; diff --git a/external/corefx/src/System.Security.Cryptography.Cng/tests/RsaCngTests.cs b/external/corefx/src/System.Security.Cryptography.Cng/tests/RsaCngTests.cs index b2dea70e92..8750e202eb 100644 --- a/external/corefx/src/System.Security.Cryptography.Cng/tests/RsaCngTests.cs +++ b/external/corefx/src/System.Security.Cryptography.Cng/tests/RsaCngTests.cs @@ -16,7 +16,7 @@ namespace System.Security.Cryptography.Cng.Tests { public static class RsaCngTests { - public static bool KeySizeTrustsOSValue => !PlatformDetection.IsFullFramework || PlatformDetection.IsNetfx462OrNewer(); + public static bool KeySizeTrustsOSValue => !PlatformDetection.IsFullFramework || PlatformDetection.IsNetfx462OrNewer; [Fact] public static void SignVerifyHashRoundTrip() diff --git a/external/corefx/src/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj b/external/corefx/src/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj index a6f8f53639..cfd10b865d 100644 --- a/external/corefx/src/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj +++ b/external/corefx/src/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj @@ -6,7 +6,6 @@ $(DefineConstants);netcoreapp $(DefineConstants);TESTING_CNG_IMPLEMENTATION - @@ -172,4 +171,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Security.Cryptography.Csp/src/Resources/Strings.resx b/external/corefx/src/System.Security.Cryptography.Csp/src/Resources/Strings.resx index e7e999017f..f7e02552f0 100644 --- a/external/corefx/src/System.Security.Cryptography.Csp/src/Resources/Strings.resx +++ b/external/corefx/src/System.Security.Cryptography.Csp/src/Resources/Strings.resx @@ -120,9 +120,6 @@ Illegal enum value: {0}. - - Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection. - Value was invalid. @@ -219,10 +216,4 @@ CSPParameters cannot be null - - Stream does not support reading. - - - Stream does not support writing. - \ No newline at end of file diff --git a/external/corefx/src/System.Security.Cryptography.Csp/src/System.Security.Cryptography.Csp.csproj b/external/corefx/src/System.Security.Cryptography.Csp/src/System.Security.Cryptography.Csp.csproj index d0f760658a..8d1718024f 100644 --- a/external/corefx/src/System.Security.Cryptography.Csp/src/System.Security.Cryptography.Csp.csproj +++ b/external/corefx/src/System.Security.Cryptography.Csp/src/System.Security.Cryptography.Csp.csproj @@ -72,6 +72,9 @@ Internal\Cryptography\Windows\CryptoThrowHelper.cs + + Common\Interop\Windows\advapi32\Interop.CryptGetProvParam.cs + Common\Interop\Windows\Crypt32\Interop.FindOidInfo.cs @@ -87,9 +90,9 @@ - + diff --git a/external/corefx/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/CapiHelper.Windows.cs b/external/corefx/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/CapiHelper.Windows.cs index 09b50c4ac0..b86d500bad 100644 --- a/external/corefx/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/CapiHelper.Windows.cs +++ b/external/corefx/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/CapiHelper.Windows.cs @@ -13,6 +13,7 @@ using System.Security.Cryptography; using System.Text; using static Interop.Crypt32; using Libraries = Interop.Libraries; +using CryptProvParam = global::Interop.Advapi32.CryptProvParam; namespace Internal.NativeCrypto { @@ -167,7 +168,6 @@ namespace Internal.NativeCrypto return ret; } - /// /// Acquire a handle to a crypto service provider and optionally a key container /// @@ -306,7 +306,7 @@ namespace Internal.NativeCrypto { IntPtr parentWindowHandle = parameters.ParentWindowHandle; - if (!Interop.CryptSetProvParamIndirectPtr(safeProvHandle, CryptGetProvParam.PP_CLIENT_HWND, ref parentWindowHandle, 0)) + if (!Interop.CryptSetProvParamIndirectPtr(safeProvHandle, CryptProvParam.PP_CLIENT_HWND, ref parentWindowHandle, 0)) { throw GetErrorCode().ToCryptographicException(); } @@ -317,10 +317,10 @@ namespace Internal.NativeCrypto IntPtr password = Marshal.SecureStringToCoTaskMemAnsi(parameters.KeyPassword); try { - CryptGetProvParam param = + CryptProvParam param = (parameters.KeyNumber == (int)KeySpec.AT_SIGNATURE) ? - CryptGetProvParam.PP_SIGNATURE_PIN : - CryptGetProvParam.PP_KEYEXCHANGE_PIN; + CryptProvParam.PP_SIGNATURE_PIN : + CryptProvParam.PP_KEYEXCHANGE_PIN; if (!Interop.CryptSetProvParam(safeProvHandle, param, password, 0)) { throw GetErrorCode().ToCryptographicException(); @@ -352,10 +352,10 @@ namespace Internal.NativeCrypto /// /// This method helps reduce the duplicate code in the GetProviderParameter method /// - internal static int GetProviderParameterWorker(SafeProvHandle safeProvHandle, byte[] impType, ref int cb, CryptGetProvParam flags) + internal static int GetProviderParameterWorker(SafeProvHandle safeProvHandle, byte[] impType, ref int cb, CryptProvParam flags) { int impTypeReturn = 0; - if (!Interop.CryptGetProvParam(safeProvHandle, (int)flags, impType, ref cb, 0)) + if (!Interop.CryptGetProvParam(safeProvHandle, flags, impType, ref cb, 0)) { throw GetErrorCode().ToCryptographicException(); } @@ -387,7 +387,7 @@ namespace Internal.NativeCrypto { case Constants.CLR_EXPORTABLE: { - impTypeReturn = GetProviderParameterWorker(safeProvHandle, impType, ref cb, CryptGetProvParam.PP_IMPTYPE); + impTypeReturn = GetProviderParameterWorker(safeProvHandle, impType, ref cb, CryptProvParam.PP_IMPTYPE); //If implementation type is not HW if (!IsFlagBitSet((uint)impTypeReturn, (uint)CryptGetProvParamPPImpTypeFlags.CRYPT_IMPL_HARDWARE)) { @@ -416,14 +416,14 @@ namespace Internal.NativeCrypto } case Constants.CLR_REMOVABLE: { - impTypeReturn = GetProviderParameterWorker(safeProvHandle, impType, ref cb, CryptGetProvParam.PP_IMPTYPE); + impTypeReturn = GetProviderParameterWorker(safeProvHandle, impType, ref cb, CryptProvParam.PP_IMPTYPE); retVal = IsFlagBitSet((uint)impTypeReturn, (uint)CryptGetProvParamPPImpTypeFlags.CRYPT_IMPL_REMOVABLE); break; } case Constants.CLR_HARDWARE: case Constants.CLR_PROTECTED: { - impTypeReturn = GetProviderParameterWorker(safeProvHandle, impType, ref cb, CryptGetProvParam.PP_IMPTYPE); + impTypeReturn = GetProviderParameterWorker(safeProvHandle, impType, ref cb, CryptProvParam.PP_IMPTYPE); retVal = IsFlagBitSet((uint)impTypeReturn, (uint)CryptGetProvParamPPImpTypeFlags.CRYPT_IMPL_HARDWARE); break; } @@ -436,9 +436,9 @@ namespace Internal.NativeCrypto { returnType = 1; byte[] pb = null; - impTypeReturn = GetProviderParameterWorker(safeProvHandle, pb, ref cb, CryptGetProvParam.PP_UNIQUE_CONTAINER); + impTypeReturn = GetProviderParameterWorker(safeProvHandle, pb, ref cb, CryptProvParam.PP_UNIQUE_CONTAINER); pb = new byte[cb]; - impTypeReturn = GetProviderParameterWorker(safeProvHandle, pb, ref cb, CryptGetProvParam.PP_UNIQUE_CONTAINER); + impTypeReturn = GetProviderParameterWorker(safeProvHandle, pb, ref cb, CryptProvParam.PP_UNIQUE_CONTAINER); // GetProviderParameterWorker allocated the null character, we want to not interpret that. Debug.Assert(cb > 0); Debug.Assert(pb[cb - 1] == 0); @@ -559,7 +559,7 @@ namespace Internal.NativeCrypto ///Method helps get the different key properties /// /// Key handle - /// Key property you want to get + /// Key property you want to get /// Returns the key property internal static byte[] GetKeyParameter(SafeKeyHandle safeKeyHandle, int keyParam) { @@ -1509,18 +1509,41 @@ namespace Internal.NativeCrypto public static extern bool CryptAcquireContext(out SafeProvHandle psafeProvHandle, string pszContainer, string pszProvider, int dwProvType, uint dwFlags); - [DllImport(Libraries.Advapi32, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool CryptGetProvParam(SafeProvHandle safeProvHandle, int dwParam, byte[] pbData, - ref int dwDataLen, int dwFlags); + public static unsafe bool CryptGetProvParam( + SafeProvHandle safeProvHandle, + CryptProvParam dwParam, + byte[] pbData, + ref int dwDataLen, + int dwFlags) + { + if (dwDataLen > pbData?.Length) + { + throw new IndexOutOfRangeException(); + } + + fixed (byte* bytePtr = pbData) + { + return global::Interop.Advapi32.CryptGetProvParam( + safeProvHandle, + dwParam, + (IntPtr)bytePtr, + ref dwDataLen, + dwFlags); + } + } [DllImport(Libraries.Advapi32, SetLastError = true, EntryPoint = "CryptSetProvParam")] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool CryptSetProvParamIndirectPtr(SafeProvHandle safeProvHandle, CryptGetProvParam dwParam, ref IntPtr pbData, int dwFlags); + public static extern bool CryptSetProvParamIndirectPtr(SafeProvHandle safeProvHandle, CryptProvParam dwParam, ref IntPtr pbData, int dwFlags); - [DllImport(Libraries.Advapi32, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool CryptSetProvParam(SafeProvHandle safeProvHandle, CryptGetProvParam dwParam, IntPtr pbData, int dwFlags); + public static bool CryptSetProvParam( + SafeProvHandle safeProvHandle, + CryptProvParam dwParam, + IntPtr pbData, + int dwFlags) + { + return global::Interop.Advapi32.CryptSetProvParam(safeProvHandle, dwParam, pbData, dwFlags); + } [DllImport(Libraries.Advapi32, SetLastError = true, EntryPoint = "CryptGetUserKey")] [return: MarshalAs(UnmanagedType.Bool)] @@ -1718,15 +1741,6 @@ namespace Internal.NativeCrypto KP_PERMISSIONS = 6, } - internal enum CryptGetProvParam : int - { - PP_CLIENT_HWND = 1, - PP_IMPTYPE = 3, - PP_KEYEXCHANGE_PIN = 32, - PP_SIGNATURE_PIN = 33, - PP_UNIQUE_CONTAINER = 36 - } - [Flags] internal enum CryptGetProvParamPPImpTypeFlags : int { diff --git a/external/corefx/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/RSACryptoServiceProvider.Windows.cs b/external/corefx/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/RSACryptoServiceProvider.Windows.cs index 2aa0329364..e1493968f1 100644 --- a/external/corefx/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/RSACryptoServiceProvider.Windows.cs +++ b/external/corefx/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/RSACryptoServiceProvider.Windows.cs @@ -61,7 +61,7 @@ namespace System.Security.Cryptography _keySize = useDefaultKeySize ? 1024 : keySize; - // If this is not a random container we generate, create it eagerly + // If this is not a random container we generate, create it eagerly // in the constructor so we can report any errors now. if (!_randomKeyContainer) { @@ -203,7 +203,7 @@ namespace System.Security.Cryptography } /// - /// get set Persisted key in CSP + /// get set Persisted key in CSP /// public bool PersistKeyInCsp { @@ -282,7 +282,7 @@ namespace System.Security.Cryptography public override byte[] DecryptValue(byte[] rgb) => base.DecryptValue(rgb); /// - /// Dispose the key handles + /// Dispose the key handles /// protected override void Dispose(bool disposing) { @@ -324,7 +324,7 @@ namespace System.Security.Cryptography /// /// This method is not supported. Use Encrypt(byte[], RSAEncryptionPadding) instead. /// - public override byte[] EncryptValue(byte[] rgb) => base.EncryptValue(rgb); + public override byte[] EncryptValue(byte[] rgb) => base.EncryptValue(rgb); /// ///Exports a blob containing the key information associated with an RSACryptoServiceProvider object. @@ -538,16 +538,6 @@ namespace System.Security.Cryptography return true; } - /// - /// Since P is required, we will assume its presence is synonymous to a private key. - /// - /// - /// - private static bool IsPublic(RSAParameters rsaParams) - { - return (rsaParams.P == null); - } - protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) { // we're sealed and the base should have checked this already diff --git a/external/corefx/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/SafeCryptoHandles.cs b/external/corefx/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/SafeCryptoHandles.cs index 9149e2a8d2..b4ee8349d7 100644 --- a/external/corefx/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/SafeCryptoHandles.cs +++ b/external/corefx/src/System.Security.Cryptography.Csp/src/System/Security/Cryptography/SafeCryptoHandles.cs @@ -12,7 +12,6 @@ namespace System.Security.Cryptography /// /// Safehandle representing HCRYPTPROV /// - [SecurityCritical] internal sealed class SafeProvHandle : SafeHandleZeroOrMinusOneIsInvalid { private string _containerName; @@ -137,7 +136,6 @@ namespace System.Security.Cryptography /// of the key handle and provider handle. This also applies to hash handles, which point to a /// CRYPT_HASH_CTX. Those structures are defined in COMCryptography.h /// - [SecurityCritical] // auto-generated internal sealed class SafeKeyHandle : SafeHandleZeroOrMinusOneIsInvalid { private int _keySpec; @@ -205,7 +203,6 @@ namespace System.Security.Cryptography } } - [SecurityCritical] protected override bool ReleaseHandle() { bool successfullyFreed = CapiHelper.CryptDestroyKey(handle); @@ -222,7 +219,6 @@ namespace System.Security.Cryptography /// /// SafeHandle representing HCRYPTHASH handle /// - [SecurityCritical] internal sealed class SafeHashHandle : SafeHandleZeroOrMinusOneIsInvalid { private SafeProvHandle _parent; @@ -262,7 +258,6 @@ namespace System.Security.Cryptography } } - [SecurityCritical] protected override bool ReleaseHandle() { bool successfullyFreed = CapiHelper.CryptDestroyHash(handle); diff --git a/external/corefx/src/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj b/external/corefx/src/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj index e631cf012b..4b0983cf44 100644 --- a/external/corefx/src/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj +++ b/external/corefx/src/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj @@ -4,7 +4,6 @@ {A05C2EF2-A986-448C-9C63-735CC17409AA} - @@ -120,4 +119,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/src/Internal/Cryptography/OidLookup.OSX.cs b/external/corefx/src/System.Security.Cryptography.Encoding/src/Internal/Cryptography/OidLookup.OSX.cs index ab9465349d..b7aa4c21a5 100644 --- a/external/corefx/src/System.Security.Cryptography.Encoding/src/Internal/Cryptography/OidLookup.OSX.cs +++ b/external/corefx/src/System.Security.Cryptography.Encoding/src/Internal/Cryptography/OidLookup.OSX.cs @@ -55,6 +55,10 @@ namespace Internal.Cryptography private static readonly Dictionary s_extraFriendlyNameToOid = new Dictionary(StringComparer.OrdinalIgnoreCase) { + { "pkcs7-data", "1.2.840.113549.1.7.1" }, + { "contentType", "1.2.840.113549.1.9.3" }, + { "messageDigest", "1.2.840.113549.1.9.4" }, + { "signingTime", "1.2.840.113549.1.9.5" }, { "X509v3 Subject Key Identifier", "2.5.29.14" }, { "X509v3 Key Usage", "2.5.29.15" }, { "X509v3 Basic Constraints", "2.5.29.19" }, diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj b/external/corefx/src/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj index dbe3c951c3..5ebbedf3e9 100644 --- a/external/corefx/src/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj +++ b/external/corefx/src/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj @@ -98,7 +98,6 @@ - diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/Asn1ReaderTests.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/Asn1ReaderTests.cs new file mode 100644 index 0000000000..a609c6661e --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/Asn1ReaderTests.cs @@ -0,0 +1,26 @@ +// 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.Security.Cryptography.Asn1; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public abstract partial class Asn1ReaderTests + { + public enum PublicTagClass : byte + { + Universal = TagClass.Universal, + Application = TagClass.Application, + ContextSpecific = TagClass.ContextSpecific, + Private = TagClass.Private, + } + + public enum PublicEncodingRules + { + BER = AsnEncodingRules.BER, + CER = AsnEncodingRules.CER, + DER = AsnEncodingRules.DER, + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ComprehensiveReadTests.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ComprehensiveReadTests.cs new file mode 100644 index 0000000000..0d80672dad --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ComprehensiveReadTests.cs @@ -0,0 +1,266 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +using X509KeyUsageCSharpStyle=System.Security.Cryptography.Tests.Asn1.ReadNamedBitList.X509KeyUsageCSharpStyle; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public static class ComprehensiveReadTests + { + [Fact] + public static void ReadMicrosoftComCert() + { + byte[] bytes = MicrosoftDotComSslCertBytes; + AsnReader fileReader = new AsnReader(bytes, AsnEncodingRules.DER); + + AsnReader certReader = fileReader.ReadSequence(); + Assert.False(fileReader.HasData, "fileReader.HasData"); + + AsnReader tbsCertReader = certReader.ReadSequence(); + AsnReader sigAlgReader = certReader.ReadSequence(); + + Assert.True( + certReader.TryGetPrimitiveBitStringValue( + out int unusedBitCount, + out ReadOnlyMemory signature), + "certReader.TryGetBitStringBytes"); + + Assert.Equal(0, unusedBitCount); + AssertRefSame(signature, ref bytes[1176], "Signature is a ref to bytes[1176]"); + + Assert.False(certReader.HasData, "certReader.HasData"); + + AsnReader versionExplicitWrapper = tbsCertReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); + Assert.True(versionExplicitWrapper.TryReadInt32(out int certVersion)); + Assert.Equal(2, certVersion); + Assert.False(versionExplicitWrapper.HasData, "versionExplicitWrapper.HasData"); + + ReadOnlyMemory serialBytes = tbsCertReader.GetIntegerBytes(); + AssertRefSame(serialBytes, ref bytes[15], "Serial number starts at bytes[15]"); + + AsnReader tbsSigAlgReader = tbsCertReader.ReadSequence(); + Assert.Equal("1.2.840.113549.1.1.11", tbsSigAlgReader.ReadObjectIdentifierAsString()); + Assert.True(tbsSigAlgReader.HasData, "tbsSigAlgReader.HasData before ReadNull"); + tbsSigAlgReader.ReadNull(); + Assert.False(tbsSigAlgReader.HasData, "tbsSigAlgReader.HasData after ReadNull"); + + AsnReader issuerReader = tbsCertReader.ReadSequence(); + Asn1Tag printableString = new Asn1Tag(UniversalTagNumber.PrintableString); + AssertRdn(issuerReader, "2.5.4.6", 57, printableString, bytes, "issuer[C]"); + AssertRdn(issuerReader, "2.5.4.10", 70, printableString, bytes, "issuer[O]"); + AssertRdn(issuerReader, "2.5.4.11", 101, printableString, bytes, "issuer[OU]"); + AssertRdn(issuerReader, "2.5.4.3", 134, printableString, bytes, "issuer[CN]"); + Assert.False(issuerReader.HasData, "issuerReader.HasData"); + + AsnReader validityReader = tbsCertReader.ReadSequence(); + Assert.Equal(new DateTimeOffset(2014, 10, 15, 0, 0, 0, TimeSpan.Zero), validityReader.GetUtcTime()); + Assert.Equal(new DateTimeOffset(2016, 10, 15, 23, 59, 59, TimeSpan.Zero), validityReader.GetUtcTime()); + Assert.False(validityReader.HasData, "validityReader.HasData"); + + AsnReader subjectReader = tbsCertReader.ReadSequence(); + Asn1Tag utf8String = new Asn1Tag(UniversalTagNumber.UTF8String); + AssertRdn(subjectReader, "1.3.6.1.4.1.311.60.2.1.3", 220, printableString, bytes, "subject[EV Country]"); + AssertRdn(subjectReader, "1.3.6.1.4.1.311.60.2.1.2", 241, utf8String, bytes, "subject[EV State]", "Washington"); + AssertRdn(subjectReader, "2.5.4.15", 262, printableString, bytes, "subject[Business Category]"); + AssertRdn(subjectReader, "2.5.4.5", 293, printableString, bytes, "subject[Serial Number]"); + AssertRdn(subjectReader, "2.5.4.6", 313, printableString, bytes, "subject[C]"); + AssertRdn(subjectReader, "2.5.4.17", 326, utf8String, bytes, "subject[Postal Code]", "98052"); + AssertRdn(subjectReader, "2.5.4.8", 342, utf8String, bytes, "subject[ST]", "Washington"); + AssertRdn(subjectReader, "2.5.4.7", 363, utf8String, bytes, "subject[L]", "Redmond"); + AssertRdn(subjectReader, "2.5.4.9", 381, utf8String, bytes, "subject[Street Address]", "1 Microsoft Way"); + AssertRdn(subjectReader, "2.5.4.10", 407, utf8String, bytes, "subject[O]", "Microsoft Corporation"); + AssertRdn(subjectReader, "2.5.4.11", 439, utf8String, bytes, "subject[OU]", "MSCOM"); + AssertRdn(subjectReader, "2.5.4.3", 455, utf8String, bytes, "subject[CN]", "www.microsoft.com"); + Assert.False(subjectReader.HasData, "subjectReader.HasData"); + + AsnReader subjectPublicKeyInfo = tbsCertReader.ReadSequence(); + AsnReader spkiAlgorithm = subjectPublicKeyInfo.ReadSequence(); + Assert.Equal("1.2.840.113549.1.1.1", spkiAlgorithm.ReadObjectIdentifierAsString()); + spkiAlgorithm.ReadNull(); + Assert.False(spkiAlgorithm.HasData, "spkiAlgorithm.HasData"); + + Assert.True( + subjectPublicKeyInfo.TryGetPrimitiveBitStringValue( + out unusedBitCount, + out ReadOnlyMemory encodedPublicKey), + "subjectPublicKeyInfo.TryGetBitStringBytes"); + + Assert.Equal(0, unusedBitCount); + AssertRefSame(encodedPublicKey, ref bytes[498], "Encoded public key starts at byte 498"); + + Assert.False(subjectPublicKeyInfo.HasData, "subjectPublicKeyInfo.HasData"); + + AsnReader publicKeyReader = new AsnReader(encodedPublicKey, AsnEncodingRules.DER); + AsnReader rsaPublicKeyReader = publicKeyReader.ReadSequence(); + AssertRefSame(rsaPublicKeyReader.GetIntegerBytes(), ref bytes[506], "RSA Modulus is at bytes[502]"); + Assert.True(rsaPublicKeyReader.TryReadInt32(out int rsaExponent)); + Assert.Equal(65537, rsaExponent); + Assert.False(rsaPublicKeyReader.HasData, "rsaPublicKeyReader.HasData"); + Assert.False(publicKeyReader.HasData, "publicKeyReader.HasData"); + + AsnReader extensionsContainer = tbsCertReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); + AsnReader extensions = extensionsContainer.ReadSequence(); + Assert.False(extensionsContainer.HasData, "extensionsContainer.HasData"); + + AsnReader sanExtension = extensions.ReadSequence(); + Assert.Equal("2.5.29.17", sanExtension.ReadObjectIdentifierAsString()); + Assert.True(sanExtension.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory sanExtensionBytes)); + Assert.False(sanExtension.HasData, "sanExtension.HasData"); + + AsnReader sanExtensionPayload = new AsnReader(sanExtensionBytes, AsnEncodingRules.DER); + AsnReader sanExtensionValue = sanExtensionPayload.ReadSequence(); + Assert.False(sanExtensionPayload.HasData, "sanExtensionPayload.HasData"); + Asn1Tag dnsName = new Asn1Tag(TagClass.ContextSpecific, 2); + Assert.Equal("www.microsoft.com", sanExtensionValue.GetCharacterString(dnsName, UniversalTagNumber.IA5String)); + Assert.Equal("wwwqa.microsoft.com", sanExtensionValue.GetCharacterString(dnsName, UniversalTagNumber.IA5String)); + Assert.False(sanExtensionValue.HasData, "sanExtensionValue.HasData"); + + AsnReader basicConstraints = extensions.ReadSequence(); + Assert.Equal("2.5.29.19", basicConstraints.ReadObjectIdentifierAsString()); + Assert.True(basicConstraints.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory basicConstraintsBytes)); + + AsnReader basicConstraintsPayload = new AsnReader(basicConstraintsBytes, AsnEncodingRules.DER); + AsnReader basicConstraintsValue = basicConstraintsPayload.ReadSequence(); + Assert.False(basicConstraintsValue.HasData, "basicConstraintsValue.HasData"); + Assert.False(basicConstraintsPayload.HasData, "basicConstraintsPayload.HasData"); + + AsnReader keyUsageExtension = extensions.ReadSequence(); + Assert.Equal("2.5.29.15", keyUsageExtension.ReadObjectIdentifierAsString()); + Assert.True(keyUsageExtension.ReadBoolean(), "keyUsageExtension.ReadBoolean() (IsCritical)"); + Assert.True(keyUsageExtension.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory keyUsageBytes)); + + AsnReader keyUsagePayload = new AsnReader(keyUsageBytes, AsnEncodingRules.DER); + + Assert.Equal( + X509KeyUsageCSharpStyle.DigitalSignature | X509KeyUsageCSharpStyle.KeyEncipherment, + keyUsagePayload.GetNamedBitListValue()); + + Assert.False(keyUsagePayload.HasData, "keyUsagePayload.HasData"); + + AssertExtension(extensions, "2.5.29.37", false, 863, bytes); + AssertExtension(extensions, "2.5.29.32", false, 894, bytes); + AssertExtension(extensions, "2.5.29.35", false, 998, bytes); + AssertExtension(extensions, "2.5.29.31", false, 1031, bytes); + AssertExtension(extensions, "1.3.6.1.5.5.7.1.1", false, 1081, bytes); + Assert.False(extensions.HasData, "extensions.HasData"); + + Assert.Equal("1.2.840.113549.1.1.11", sigAlgReader.ReadObjectIdentifierAsString()); + sigAlgReader.ReadNull(); + Assert.False(sigAlgReader.HasData); + } + + private static void AssertExtension(AsnReader extensions, string oid, bool critical, int index, byte[] bytes) + { + AsnReader extension = extensions.ReadSequence(); + Assert.Equal(oid, extension.ReadObjectIdentifierAsString()); + + if (critical) + { + Assert.True(extension.ReadBoolean(), $"{oid} is critical"); + } + + Assert.True(extension.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory extensionBytes)); + AssertRefSame(extensionBytes, ref bytes[index], $"{oid} extension value is at byte {index}"); + } + + private static void AssertRdn( + AsnReader reader, + string atvOid, + int offset, + Asn1Tag valueTag, + byte[] bytes, + string label, + string stringValue=null) + { + AsnReader rdn = reader.ReadSetOf(); + AsnReader attributeTypeAndValue = rdn.ReadSequence(); + Assert.Equal(atvOid, attributeTypeAndValue.ReadObjectIdentifierAsString()); + + ReadOnlyMemory value = attributeTypeAndValue.GetEncodedValue(); + ReadOnlySpan valueSpan = value.Span; + + Assert.True(Asn1Tag.TryParse(valueSpan, out Asn1Tag actualTag, out int bytesRead)); + Assert.Equal(1, bytesRead); + Assert.Equal(valueTag, actualTag); + + AssertRefSame( + ref MemoryMarshal.GetReference(valueSpan), + ref bytes[offset], + $"{label} is at bytes[{offset}]"); + + if (stringValue != null) + { + AsnReader valueReader = new AsnReader(value, AsnEncodingRules.DER); + Assert.Equal(stringValue, valueReader.GetCharacterString((UniversalTagNumber)valueTag.TagValue)); + Assert.False(valueReader.HasData, "valueReader.HasData"); + } + + Assert.False(attributeTypeAndValue.HasData, $"attributeTypeAndValue.HasData ({label})"); + Assert.False(rdn.HasData, $"rdn.HasData ({label})"); + } + + private static void AssertRefSame(ReadOnlyMemory a, ref byte b, string msg) + { + AssertRefSame(ref MemoryMarshal.GetReference(a.Span), ref b, msg); + } + + private static void AssertRefSame(ref byte a, ref byte b, string msg) + { + Assert.True(Unsafe.AreSame(ref a, ref b), msg); + } + + internal static readonly byte[] MicrosoftDotComSslCertBytes = ( + "308205943082047CA00302010202103DF70C5D9903F8D8868B9B8CCF20DF6930" + + "0D06092A864886F70D01010B05003077310B3009060355040613025553311D30" + + "1B060355040A131453796D616E74656320436F72706F726174696F6E311F301D" + + "060355040B131653796D616E746563205472757374204E6574776F726B312830" + + "260603550403131F53796D616E74656320436C61737320332045562053534C20" + + "4341202D204733301E170D3134313031353030303030305A170D313631303135" + + "3233353935395A3082010F31133011060B2B0601040182373C02010313025553" + + "311B3019060B2B0601040182373C0201020C0A57617368696E67746F6E311D30" + + "1B060355040F131450726976617465204F7267616E697A6174696F6E31123010" + + "06035504051309363030343133343835310B3009060355040613025553310E30" + + "0C06035504110C0539383035323113301106035504080C0A57617368696E6774" + + "6F6E3110300E06035504070C075265646D6F6E643118301606035504090C0F31" + + "204D6963726F736F667420576179311E301C060355040A0C154D6963726F736F" + + "667420436F72706F726174696F6E310E300C060355040B0C054D53434F4D311A" + + "301806035504030C117777772E6D6963726F736F66742E636F6D30820122300D" + + "06092A864886F70D01010105000382010F003082010A0282010100A46861FA9D" + + "5DB763633BF5A64EF6E7C2C2367F48D2D46643A22DFCFCCB24E58A14D0F06BDC" + + "956437F2A56BA4BEF70BA361BF12964A0D665AFD84B0F7494C8FA4ABC5FCA2E0" + + "17C06178AEF2CDAD1B5F18E997A14B965C074E8F564970607276B00583932240" + + "FE6E2DD013026F9AE13D7C91CC07C4E1E8E87737DC06EF2B575B89D62EFE4685" + + "9F8255A123692A706C68122D4DAFE11CB205A7B3DE06E553F7B95F978EF8601A" + + "8DF819BF32040BDF92A0DE0DF269B4514282E17AC69934E8440A48AB9D1F5DF8" + + "9A502CEF6DFDBE790045BD45E0C94E5CA8ADD76A013E9C978440FC8A9E2A9A49" + + "40B2460819C3E302AA9C9F355AD754C86D3ED77DDAA3DA13810B4D0203010001" + + "A38201803082017C30310603551D11042A302882117777772E6D6963726F736F" + + "66742E636F6D821377777771612E6D6963726F736F66742E636F6D3009060355" + + "1D1304023000300E0603551D0F0101FF0404030205A0301D0603551D25041630" + + "1406082B0601050507030106082B0601050507030230660603551D20045F305D" + + "305B060B6086480186F84501071706304C302306082B06010505070201161768" + + "747470733A2F2F642E73796D63622E636F6D2F637073302506082B0601050507" + + "020230191A1768747470733A2F2F642E73796D63622E636F6D2F727061301F06" + + "03551D230418301680140159ABE7DD3A0B59A66463D6CF200757D591E76A302B" + + "0603551D1F042430223020A01EA01C861A687474703A2F2F73722E73796D6362" + + "2E636F6D2F73722E63726C305706082B06010505070101044B3049301F06082B" + + "060105050730018613687474703A2F2F73722E73796D63642E636F6D30260608" + + "2B06010505073002861A687474703A2F2F73722E73796D63622E636F6D2F7372" + + "2E637274300D06092A864886F70D01010B0500038201010015F8505B627ED7F9" + + "F96707097E93A51E7A7E05A3D420A5C258EC7A1CFE1843EC20ACF728AAFA7A1A" + + "1BC222A7CDBF4AF90AA26DEEB3909C0B3FB5C78070DAE3D645BFCF840A4A3FDD" + + "988C7B3308BFE4EB3FD66C45641E96CA3352DBE2AEB4488A64A9C5FB96932BA7" + + "0059CE92BD278B41299FD213471BD8165F924285AE3ECD666C703885DCA65D24" + + "DA66D3AFAE39968521995A4C398C7DF38DFA82A20372F13D4A56ADB21B582254" + + "9918015647B5F8AC131CC5EB24534D172BC60218A88B65BCF71C7F388CE3E0EF" + + "697B4203720483BB5794455B597D80D48CD3A1D73CBBC609C058767D1FF060A6" + + "09D7E3D4317079AF0CD0A8A49251AB129157F9894A036487").HexToByteArray(); + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ParseTag.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ParseTag.cs new file mode 100644 index 0000000000..a9211778f1 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ParseTag.cs @@ -0,0 +1,160 @@ +// 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.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public sealed class ParseTag : Asn1ReaderTests + { + [Theory] + [InlineData(PublicTagClass.Universal, false, 0, "00")] + [InlineData(PublicTagClass.Universal, false, 1, "01")] + [InlineData(PublicTagClass.Application, true, 1, "61")] + [InlineData(PublicTagClass.ContextSpecific, false, 1, "81")] + [InlineData(PublicTagClass.ContextSpecific, true, 1, "A1")] + [InlineData(PublicTagClass.Private, false, 1, "C1")] + [InlineData(PublicTagClass.Universal, false, 30, "1E")] + [InlineData(PublicTagClass.Application, false, 30, "5E")] + [InlineData(PublicTagClass.ContextSpecific, false, 30, "9E")] + [InlineData(PublicTagClass.Private, false, 30, "DE")] + [InlineData(PublicTagClass.Universal, false, 31, "1F1F")] + [InlineData(PublicTagClass.Application, false, 31, "5F1F")] + [InlineData(PublicTagClass.ContextSpecific, false, 31, "9F1F")] + [InlineData(PublicTagClass.Private, false, 31, "DF1F")] + [InlineData(PublicTagClass.Private, false, 127, "DF7F")] + [InlineData(PublicTagClass.Private, false, 128, "DF8100")] + [InlineData(PublicTagClass.Private, false, 253, "DF817D")] + [InlineData(PublicTagClass.Private, false, 255, "DF817F")] + [InlineData(PublicTagClass.Private, false, 256, "DF8200")] + [InlineData(PublicTagClass.Private, false, 1 << 9, "DF8400")] + [InlineData(PublicTagClass.Private, false, 1 << 10, "DF8800")] + [InlineData(PublicTagClass.Private, false, 0b0011_1101_1110_0111, "DFFB67")] + [InlineData(PublicTagClass.Private, false, 1 << 14, "DF818000")] + [InlineData(PublicTagClass.Private, false, 1 << 18, "DF908000")] + [InlineData(PublicTagClass.Private, false, 1 << 18 | 1 << 9, "DF908400")] + [InlineData(PublicTagClass.Private, false, 1 << 20, "DFC08000")] + [InlineData(PublicTagClass.Private, false, 0b0001_1110_1010_0111_0000_0001, "DFFACE01")] + [InlineData(PublicTagClass.Private, false, 1 << 21, "DF81808000")] + [InlineData(PublicTagClass.Private, false, 1 << 27, "DFC0808000")] + [InlineData(PublicTagClass.Private, false, 1 << 28, "DF8180808000")] + [InlineData(PublicTagClass.Private, true, int.MaxValue, "FF87FFFFFF7F")] + [InlineData(PublicTagClass.Universal, false, 119, "1F77")] + public static void ParseValidTag( + PublicTagClass tagClass, + bool isConstructed, + int tagValue, + string inputHex) + { + byte[] inputBytes = inputHex.HexToByteArray(); + + bool parsed = Asn1Tag.TryParse(inputBytes, out Asn1Tag tag, out int bytesRead); + + Assert.True(parsed, "Asn1Tag.TryParse"); + Assert.Equal(inputBytes.Length, bytesRead); + Assert.Equal((TagClass)tagClass, tag.TagClass); + Assert.Equal(tagValue, tag.TagValue); + + if (isConstructed) + { + Assert.True(tag.IsConstructed, "tag.IsConstructed"); + } + else + { + Assert.False(tag.IsConstructed, "tag.IsConstructed"); + } + + byte[] secondBytes = new byte[inputBytes.Length]; + int written; + Assert.False(tag.TryWrite(secondBytes.AsSpan().Slice(0, inputBytes.Length - 1), out written)); + Assert.Equal(0, written); + Assert.True(tag.TryWrite(secondBytes, out written)); + Assert.Equal(inputBytes.Length, written); + Assert.Equal(inputHex, secondBytes.ByteArrayToHex()); + } + + [Theory] + [InlineData("Empty", "")] + [InlineData("MultiByte-NoFollow", "1F")] + [InlineData("MultiByte-NoFollow2", "1F81")] + [InlineData("MultiByte-NoFollow3", "1F8180")] + [InlineData("MultiByte-TooLow", "1F01")] + [InlineData("MultiByte-TooLowMax", "1F1E")] + [InlineData("MultiByte-Leading0", "1F807F")] + [InlineData("MultiByte-ValueTooBig", "FF8880808000")] + [InlineData("MultiByte-ValueSubtlyTooBig", "DFC1C0808000")] + public static void ParseCorruptTag(string description, string inputHex) + { + byte[] inputBytes = inputHex.HexToByteArray(); + + Assert.False(Asn1Tag.TryParse(inputBytes, out Asn1Tag tag, out var bytesRead)); + + Assert.Equal(default(Asn1Tag), tag); + Assert.Equal(0, bytesRead); + } + + [Fact] + public static void TestEquals() + { + Asn1Tag integer = new Asn1Tag(TagClass.Universal, 2); + Asn1Tag integerAgain = new Asn1Tag(TagClass.Universal, 2); + Asn1Tag context2 = new Asn1Tag(TagClass.ContextSpecific, 2); + Asn1Tag constructedContext2 = new Asn1Tag(TagClass.ContextSpecific, 2, true); + Asn1Tag application2 = new Asn1Tag(TagClass.Application, 2); + + Assert.False(integer.Equals(null)); + Assert.False(integer.Equals(0x02)); + Assert.False(integer.Equals(context2)); + Assert.False(context2.Equals(constructedContext2)); + Assert.False(context2.Equals(application2)); + + Assert.Equal(integer, integerAgain); + Assert.True(integer == integerAgain); + Assert.True(integer != context2); + Assert.False(integer == context2); + Assert.False(context2 == constructedContext2); + Assert.False(context2 == application2); + + Assert.NotEqual(integer.GetHashCode(), context2.GetHashCode()); + Assert.NotEqual(context2.GetHashCode(), constructedContext2.GetHashCode()); + Assert.NotEqual(context2.GetHashCode(), application2.GetHashCode()); + Assert.Equal(integer.GetHashCode(), integerAgain.GetHashCode()); + } + + [Theory] + [InlineData(PublicTagClass.Universal, false, 0, "00")] + [InlineData(PublicTagClass.ContextSpecific, true, 1, "A1")] + [InlineData(PublicTagClass.Application, false, 31, "5F1F")] + [InlineData(PublicTagClass.Private, false, 128, "DF8100")] + [InlineData(PublicTagClass.Private, false, 0b0001_1110_1010_0111_0000_0001, "DFFACE01")] + [InlineData(PublicTagClass.Private, true, int.MaxValue, "FF87FFFFFF7F")] + public static void ParseTagWithMoreData( + PublicTagClass tagClass, + bool isConstructed, + int tagValue, + string inputHex) + { + byte[] inputBytes = inputHex.HexToByteArray(); + Array.Resize(ref inputBytes, inputBytes.Length + 3); + + bool parsed = Asn1Tag.TryParse(inputBytes, out Asn1Tag tag, out int bytesRead); + + Assert.True(parsed, "Asn1Tag.TryParse"); + Assert.Equal(inputHex.Length / 2, bytesRead); + Assert.Equal((TagClass)tagClass, tag.TagClass); + Assert.Equal(tagValue, tag.TagValue); + + if (isConstructed) + { + Assert.True(tag.IsConstructed, "tag.IsConstructed"); + } + else + { + Assert.False(tag.IsConstructed, "tag.IsConstructed"); + } + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/PeekTests.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/PeekTests.cs new file mode 100644 index 0000000000..f09e92095e --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/PeekTests.cs @@ -0,0 +1,214 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public sealed class PeekTests : Asn1ReaderTests + { + [Fact] + public static void ReaderPeekTag_Valid() + { + // SEQUENCE(NULL) + byte[] data = { 0x30, 0x02, 0x05, 0x00 }; + AsnReader reader = new AsnReader(data, AsnEncodingRules.BER); + Asn1Tag tag = reader.PeekTag(); + + Assert.Equal((int)UniversalTagNumber.Sequence, tag.TagValue); + Assert.True(tag.IsConstructed, "tag.IsConstructed"); + Assert.Equal(TagClass.Universal, tag.TagClass); + } + + [Fact] + public static void ReaderPeekTag_Invalid() + { + // (UNIVERSAL [continue into next byte]) + byte[] data = { 0x1F }; + AsnReader reader = new AsnReader(data, AsnEncodingRules.BER); + + try + { + reader.PeekTag(); + Assert.True(false, "CryptographicException was thrown"); + } + catch (CryptographicException) + { + } + } + + [Fact] + public static void PeekEncodedValue_Primitive() + { + const string EncodedContents = "010203040506"; + const string EncodedValue = "0406" + EncodedContents; + + byte[] data = (EncodedValue + "0500").HexToByteArray(); + + AsnReader reader = new AsnReader(data, AsnEncodingRules.BER); + Assert.Equal(EncodedValue, reader.PeekEncodedValue().ByteArrayToHex()); + + // It's Peek, so it's reproducible. + Assert.Equal(EncodedValue, reader.PeekEncodedValue().ByteArrayToHex()); + } + + [Fact] + public static void PeekEncodedValue_Indefinite() + { + const string EncodedContents = "040101" + "04050203040506"; + const string EncodedValue = "2480" + EncodedContents + "0000"; + + byte[] data = (EncodedValue + "0500").HexToByteArray(); + + AsnReader reader = new AsnReader(data, AsnEncodingRules.BER); + Assert.Equal(EncodedValue, reader.PeekEncodedValue().ByteArrayToHex()); + + // It's Peek, so it's reproducible. + Assert.Equal(EncodedValue, reader.PeekEncodedValue().ByteArrayToHex()); + } + + [Fact] + public static void PeekEncodedValue_Corrupt_Throws() + { + const string EncodedContents = "040101" + "04050203040506"; + // Constructed bit isn't set, so indefinite length is invalid. + const string EncodedValue = "0480" + EncodedContents + "0000"; + + byte[] data = (EncodedValue + "0500").HexToByteArray(); + + Assert.Throws( + () => + { + AsnReader reader = new AsnReader(data, AsnEncodingRules.BER); + reader.PeekEncodedValue(); + }); + } + + [Fact] + public static void PeekContentSpan_Primitive() + { + const string EncodedContents = "010203040506"; + const string EncodedValue = "0406" + EncodedContents; + + byte[] data = (EncodedValue + "0500").HexToByteArray(); + + AsnReader reader = new AsnReader(data, AsnEncodingRules.BER); + Assert.Equal(EncodedContents, reader.PeekContentBytes().ByteArrayToHex()); + + // It's Peek, so it's reproducible. + Assert.Equal(EncodedValue, reader.PeekEncodedValue().ByteArrayToHex()); + } + + [Fact] + public static void PeekContentSpan_Indefinite() + { + const string EncodedContents = "040101" + "04050203040506"; + const string EncodedValue = "2480" + EncodedContents + "0000"; + + byte[] data = (EncodedValue + "0500").HexToByteArray(); + + AsnReader reader = new AsnReader(data, AsnEncodingRules.BER); + Assert.Equal(EncodedContents, reader.PeekContentBytes().ByteArrayToHex()); + + // It's Peek, so it's reproducible. + Assert.Equal(EncodedValue, reader.PeekEncodedValue().ByteArrayToHex()); + } + + [Fact] + public static void PeekContentSpan_Corrupt_Throws() + { + const string EncodedContents = "040101" + "04050203040506"; + // Constructed bit isn't set, so indefinite length is invalid. + const string EncodedValue = "0480" + EncodedContents + "0000"; + + byte[] data = (EncodedValue + "0500").HexToByteArray(); + + Assert.Throws( + () => + { + AsnReader reader = new AsnReader(data, AsnEncodingRules.BER); + reader.PeekContentBytes(); + }); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public static void PeekContentSpan_ExtremelyNested(bool fullArray) + { + byte[] dataBytes = new byte[4 * 16384]; + + // For a full array this will build 2^14 nested indefinite length values. + // PeekContentBytes should return dataBytes.Slice(2, dataBytes.Length - 4) + // + // For what it's worth, the initial algorithm succeeded at 1650, and StackOverflowed with 1651. + // + // With the counter-and-no-recursion algorithm a nesting depth of 534773759 was verified, + // at a cost of 10 minutes of execution and a 2139095036 byte array. + // (The size was "a little bit less than int.MaxValue, since that OOMed my 32-bit process") + int end = dataBytes.Length / 2; + int expectedLength = dataBytes.Length - 4; + + if (!fullArray) + { + // Use 3/4 of what's available, just to prove we're not counting from the end. + // So with "full" being a nesting value 16384 this will use 12288 + end = end / 4 * 3; + expectedLength = 2 * end - 4; + } + + for (int i = 0; i < end; i += 2) + { + // Context-Specific 0 [Constructed] + dataBytes[i] = 0xA0; + // Indefinite length + dataBytes[i + 1] = 0x80; + } + + AsnReader reader = new AsnReader(dataBytes, AsnEncodingRules.BER); + ReadOnlyMemory contents = reader.PeekContentBytes(); + Assert.Equal(expectedLength, contents.Length); + Assert.True(Unsafe.AreSame(ref dataBytes[2], ref MemoryMarshal.GetReference(contents.Span))); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public static void PeekEncodedValue_ExtremelyNested(bool fullArray) + { + byte[] dataBytes = new byte[4 * 16384]; + + // For a full array this will build 2^14 nested indefinite length values. + // PeekEncodedValue should return the whole array. + int end = dataBytes.Length / 2; + int expectedLength = dataBytes.Length; + + if (!fullArray) + { + // Use 3/4 of what's available, just to prove we're not counting from the end. + // So with "full" being a nesting value 16384 this will use 12288, and + // PeekEncodedValue should give us back 48k, not 64k. + end = end / 4 * 3; + expectedLength = 2 * end; + } + + for (int i = 0; i < end; i += 2) + { + // Context-Specific 0 [Constructed] + dataBytes[i] = 0xA0; + // Indefinite length + dataBytes[i + 1] = 0x80; + } + + AsnReader reader = new AsnReader(dataBytes, AsnEncodingRules.BER); + ReadOnlyMemory contents = reader.PeekEncodedValue(); + Assert.Equal(expectedLength, contents.Length); + Assert.True(Unsafe.AreSame(ref dataBytes[0], ref MemoryMarshal.GetReference(contents.Span))); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBMPString.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBMPString.cs new file mode 100644 index 0000000000..62b8439e0a --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBMPString.cs @@ -0,0 +1,756 @@ +// 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.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public sealed class ReadBMPString : Asn1ReaderTests + { + public static IEnumerable ValidEncodingData { get; } = + new object[][] + { + new object[] + { + PublicEncodingRules.BER, + "1E1A004A006F0068006E00200051002E00200053006D006900740068", + "John Q. Smith", + }, + new object[] + { + PublicEncodingRules.CER, + "1E1A004A006F0068006E00200051002E00200053006D006900740068", + "John Q. Smith", + }, + new object[] + { + PublicEncodingRules.DER, + "1E1A004A006F0068006E00200051002E00200053006D006900740068", + "John Q. Smith", + }, + new object[] + { + PublicEncodingRules.BER, + "3E80" + "041A004A006F0068006E00200051002E00200053006D006900740068" + "0000", + "John Q. Smith", + }, + new object[] + { + PublicEncodingRules.BER, + "3E1C" + "041A004A006F0068006E00200051002E00200053006D006900740068", + "John Q. Smith", + }, + new object[] + { + PublicEncodingRules.BER, + "1E00", + "", + }, + new object[] + { + PublicEncodingRules.CER, + "1E00", + "", + }, + new object[] + { + PublicEncodingRules.DER, + "1E00", + "", + }, + new object[] + { + PublicEncodingRules.BER, + "3E00", + "", + }, + new object[] + { + PublicEncodingRules.BER, + "3E80" + "0000", + "", + }, + new object[] + { + PublicEncodingRules.BER, + "3E80" + + "2480" + + // "Dr." + "040600440072002E" + + // " & " + "0406002000260020" + + // "Mrs." + "0408004D00720073002E" + + "0000" + + // " " + "04020020" + + "2480" + + "2410" + + // "Smith" + "040A0053006D006900740068" + + // hyphen (U+2010) + "04022010" + + "0000" + + // "Jones" + "040A004A006F006E00650073" + + "2480" + + // " " + "04020020" + + "2480" + + // The next two bytes are U+FE60, small ampersand + // Since UCS-2 would always chunk evenly under CER the odds of + // misaligned data are low in reality, but maybe some BER encoder + // chunks odd, so a split scenario could still happen. + "0401FE" + + "040160" + + "0000" + + // " " + "04020020" + + // "children" + "0410006300680069006C006400720065006E" + + "0000" + + "0000", + "Dr. & Mrs. Smith\u2010Jones \uFE60 children", + }, + }; + + [Theory] + [MemberData(nameof(ValidEncodingData))] + public static void GetBMPString_Success( + PublicEncodingRules ruleSet, + string inputHex, + string expectedValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + string value = reader.GetCharacterString(UniversalTagNumber.BMPString); + + Assert.Equal(expectedValue, value); + } + + [Theory] + [MemberData(nameof(ValidEncodingData))] + public static void TryCopyBMPString( + PublicEncodingRules ruleSet, + string inputHex, + string expectedValue) + { + byte[] inputData = inputHex.HexToByteArray(); + char[] output = new char[expectedValue.Length]; + + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + bool copied; + int charsWritten; + + if (output.Length > 0) + { + output[0] = 'a'; + + copied = reader.TryCopyBMPString( + output.AsSpan().Slice(0, expectedValue.Length - 1), + out charsWritten); + + Assert.False(copied, "reader.TryCopyBMPString - too short"); + Assert.Equal(0, charsWritten); + Assert.Equal('a', output[0]); + } + + copied = reader.TryCopyBMPString( + output, + out charsWritten); + + Assert.True(copied, "reader.TryCopyBMPString"); + + string actualValue = new string(output, 0, charsWritten); + Assert.Equal(expectedValue, actualValue); + } + + [Theory] + [MemberData(nameof(ValidEncodingData))] + public static void TryCopyBMPStringBytes( + PublicEncodingRules ruleSet, + string inputHex, + string expectedString) + { + byte[] inputData = inputHex.HexToByteArray(); + string expectedHex = Text.Encoding.BigEndianUnicode.GetBytes(expectedString).ByteArrayToHex(); + byte[] output = new byte[expectedHex.Length / 2]; + + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + bool copied; + int bytesWritten; + + if (output.Length > 0) + { + output[0] = 32; + + copied = reader.TryCopyBMPStringBytes(output.AsSpan().Slice(0, output.Length - 1), + out bytesWritten); + + Assert.False(copied, "reader.TryCopyBMPStringBytes - too short"); + Assert.Equal(0, bytesWritten); + Assert.Equal(32, output[0]); + } + + copied = reader.TryCopyBMPStringBytes(output, + out bytesWritten); + + Assert.True(copied, "reader.TryCopyBMPStringBytes"); + + Assert.Equal( + expectedHex, + new ReadOnlySpan(output, 0, bytesWritten).ByteArrayToHex()); + + Assert.Equal(output.Length, bytesWritten); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "1E020020", true)] + [InlineData(PublicEncodingRules.BER, "3E80" + "04020020" + "0000", false)] + [InlineData(PublicEncodingRules.BER, "3E04" + "04020020", false)] + public static void TryGetBMPStringBytes( + PublicEncodingRules ruleSet, + string inputHex, + bool expectSuccess) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + bool got = reader.TryGetBMPStringBytes(out ReadOnlyMemory contents); + + if (expectSuccess) + { + Assert.True(got, "reader.TryGetBMPStringBytes"); + + Assert.True( + Unsafe.AreSame( + ref MemoryMarshal.GetReference(contents.Span), + ref inputData[2])); + } + else + { + Assert.False(got, "reader.TryGetBMPStringBytes"); + Assert.True(contents.IsEmpty, "contents.IsEmpty"); + } + } + + [Theory] + [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] + [InlineData("Missing Length", PublicEncodingRules.BER, "1E")] + [InlineData("Missing Length", PublicEncodingRules.CER, "1E")] + [InlineData("Missing Length", PublicEncodingRules.DER, "1E")] + [InlineData("Missing Contents", PublicEncodingRules.BER, "1E02")] + [InlineData("Missing Contents", PublicEncodingRules.CER, "1E02")] + [InlineData("Missing Contents", PublicEncodingRules.DER, "1E02")] + [InlineData("Length Too Long", PublicEncodingRules.BER, "1E0600480069")] + [InlineData("Length Too Long", PublicEncodingRules.CER, "1E0600480069")] + [InlineData("Length Too Long", PublicEncodingRules.DER, "1E0600480069")] + [InlineData("Constructed Form", PublicEncodingRules.DER, "3E0404020049")] + public static void TryGetBMPStringBytes_Throws( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws( + () => + { + reader.TryGetBMPStringBytes(out ReadOnlyMemory contents); + }); + } + + [Theory] + [InlineData("Empty", PublicEncodingRules.BER, "")] + [InlineData("Empty", PublicEncodingRules.CER, "")] + [InlineData("Empty", PublicEncodingRules.DER, "")] + [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] + [InlineData("Missing Length", PublicEncodingRules.BER, "1E")] + [InlineData("Missing Length", PublicEncodingRules.CER, "1E")] + [InlineData("Missing Length", PublicEncodingRules.DER, "1E")] + [InlineData("Missing Contents", PublicEncodingRules.BER, "1E02")] + [InlineData("Missing Contents", PublicEncodingRules.CER, "1E02")] + [InlineData("Missing Contents", PublicEncodingRules.DER, "1E02")] + [InlineData("Missing Contents - Constructed", PublicEncodingRules.BER, "3E02")] + [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.BER, "3E80")] + [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.CER, "3E80")] + [InlineData("Length Too Long", PublicEncodingRules.BER, "1E034869")] + [InlineData("Length Too Long", PublicEncodingRules.CER, "1E034869")] + [InlineData("Length Too Long", PublicEncodingRules.DER, "1E034869")] + [InlineData("Definite Constructed Form", PublicEncodingRules.CER, "3E03040149")] + [InlineData("Definite Constructed Form", PublicEncodingRules.DER, "3E03040149")] + [InlineData("Indefinite Constructed Form - Short Payload", PublicEncodingRules.CER, "3E800401490000")] + [InlineData("Indefinite Constructed Form", PublicEncodingRules.DER, "3E800401490000")] + [InlineData("No nested content", PublicEncodingRules.CER, "3E800000")] + [InlineData("No EoC", PublicEncodingRules.BER, "3E80" + "04024869")] + [InlineData("Wrong Tag - Primitive", PublicEncodingRules.BER, "04024869")] + [InlineData("Wrong Tag - Primitive", PublicEncodingRules.CER, "04024869")] + [InlineData("Wrong Tag - Primitive", PublicEncodingRules.DER, "04024869")] + [InlineData("Wrong Tag - Constructed", PublicEncodingRules.BER, "240404024869")] + [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.BER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.CER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed", PublicEncodingRules.DER, "240404024869")] + [InlineData("Nested Bad Tag", PublicEncodingRules.BER, "3E04" + "1E024869")] + [InlineData("Nested context-specific", PublicEncodingRules.BER, "3E04800400FACE")] + [InlineData("Nested context-specific (indef)", PublicEncodingRules.BER, "3E80800400FACE0000")] + [InlineData("Nested context-specific (indef)", PublicEncodingRules.CER, "3E80800400FACE0000")] + [InlineData("Nested Length Too Long", PublicEncodingRules.BER, "3E07" + ("2402" + "0404") + "04020049")] + [InlineData("Nested Simple Length Too Long", PublicEncodingRules.BER, "3E03" + "040548656C6C6F")] + [InlineData("Constructed EndOfContents", PublicEncodingRules.BER, "3E8020000000")] + [InlineData("Constructed EndOfContents", PublicEncodingRules.CER, "3E8020000000")] + [InlineData("NonEmpty EndOfContents", PublicEncodingRules.BER, "3E80000100")] + [InlineData("NonEmpty EndOfContents", PublicEncodingRules.CER, "3E80000100")] + [InlineData("LongLength EndOfContents", PublicEncodingRules.BER, "3E80008100")] + public static void TryCopyBMPStringBytes_Throws( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + byte[] outputData = new byte[inputData.Length + 1]; + outputData[0] = 252; + + int bytesWritten = -1; + + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws( + () => + { + reader.TryCopyBMPStringBytes(outputData, out bytesWritten); + }); + + Assert.Equal(-1, bytesWritten); + Assert.Equal(252, outputData[0]); + } + + private static void TryCopyBMPString_Throws(PublicEncodingRules ruleSet, byte[] inputData) + { + char[] outputData = new char[inputData.Length + 1]; + outputData[0] = 'a'; + + int bytesWritten = -1; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws( + () => + { + reader.TryCopyBMPString( + outputData, + out bytesWritten); + }); + + Assert.Equal(-1, bytesWritten); + Assert.Equal('a', outputData[0]); + } + + [Theory] + [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] + [InlineData("Missing Length", PublicEncodingRules.BER, "1E")] + [InlineData("Missing Length", PublicEncodingRules.CER, "1E")] + [InlineData("Missing Length", PublicEncodingRules.DER, "1E")] + [InlineData("Missing Contents", PublicEncodingRules.BER, "1E02")] + [InlineData("Missing Contents", PublicEncodingRules.CER, "1E02")] + [InlineData("Missing Contents", PublicEncodingRules.DER, "1E02")] + [InlineData("Length Too Long", PublicEncodingRules.BER, "1E0600480069")] + [InlineData("Length Too Long", PublicEncodingRules.CER, "1E0600480069")] + [InlineData("Length Too Long", PublicEncodingRules.DER, "1E0600480069")] + [InlineData("Constructed Form", PublicEncodingRules.DER, "3E0404020049")] + [InlineData("Bad BMP value (odd length)", PublicEncodingRules.BER, "1E0120")] + [InlineData("Bad BMP value (high surrogate)", PublicEncodingRules.BER, "1E02D800")] + [InlineData("Bad BMP value (high private surrogate)", PublicEncodingRules.BER, "1E02DB81")] + [InlineData("Bad BMP value (low surrogate)", PublicEncodingRules.BER, "1E02DC00")] + [InlineData("Wrong Tag", PublicEncodingRules.BER, "04024869")] + public static void GetBMPString_Throws( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws( + () => + { + reader.GetCharacterString(UniversalTagNumber.BMPString); + }); + } + + [Theory] + [InlineData("Empty", PublicEncodingRules.BER, "")] + [InlineData("Empty", PublicEncodingRules.CER, "")] + [InlineData("Empty", PublicEncodingRules.DER, "")] + [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] + [InlineData("Missing Length", PublicEncodingRules.BER, "1E")] + [InlineData("Missing Length", PublicEncodingRules.CER, "1E")] + [InlineData("Missing Length", PublicEncodingRules.DER, "1E")] + [InlineData("Missing Contents", PublicEncodingRules.BER, "1E02")] + [InlineData("Missing Contents", PublicEncodingRules.CER, "1E02")] + [InlineData("Missing Contents", PublicEncodingRules.DER, "1E02")] + [InlineData("Missing Contents - Constructed", PublicEncodingRules.BER, "3E02")] + [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.BER, "3E80")] + [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.CER, "3E80")] + [InlineData("Length Too Long", PublicEncodingRules.BER, "1E034869")] + [InlineData("Length Too Long", PublicEncodingRules.CER, "1E034869")] + [InlineData("Length Too Long", PublicEncodingRules.DER, "1E034869")] + [InlineData("Definite Constructed Form", PublicEncodingRules.CER, "3E03040149")] + [InlineData("Definite Constructed Form", PublicEncodingRules.DER, "3E03040149")] + [InlineData("Indefinite Constructed Form - Short Payload", PublicEncodingRules.CER, "3E800401490000")] + [InlineData("Indefinite Constructed Form", PublicEncodingRules.DER, "3E800401490000")] + [InlineData("No nested content", PublicEncodingRules.CER, "3E800000")] + [InlineData("No EoC", PublicEncodingRules.BER, "3E80" + "04024869")] + [InlineData("Wrong Tag - Primitive", PublicEncodingRules.BER, "04024869")] + [InlineData("Wrong Tag - Primitive", PublicEncodingRules.CER, "04024869")] + [InlineData("Wrong Tag - Primitive", PublicEncodingRules.DER, "04024869")] + [InlineData("Wrong Tag - Constructed", PublicEncodingRules.BER, "240404024869")] + [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.BER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.CER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed", PublicEncodingRules.DER, "240404024869")] + [InlineData("Nested Bad Tag", PublicEncodingRules.BER, "3E04" + "1E024869")] + [InlineData("Nested context-specific", PublicEncodingRules.BER, "3E04800400FACE")] + [InlineData("Nested context-specific (indef)", PublicEncodingRules.BER, "3E80800400FACE0000")] + [InlineData("Nested context-specific (indef)", PublicEncodingRules.CER, "3E80800400FACE0000")] + [InlineData("Nested Length Too Long", PublicEncodingRules.BER, "3E07" + ("2402" + "0404") + "04020049")] + [InlineData("Nested Simple Length Too Long", PublicEncodingRules.BER, "3E03" + "040548656C6C6F")] + [InlineData("Constructed EndOfContents", PublicEncodingRules.BER, "3E8020000000")] + [InlineData("Constructed EndOfContents", PublicEncodingRules.CER, "3E8020000000")] + [InlineData("NonEmpty EndOfContents", PublicEncodingRules.BER, "3E80000100")] + [InlineData("NonEmpty EndOfContents", PublicEncodingRules.CER, "3E80000100")] + [InlineData("LongLength EndOfContents", PublicEncodingRules.BER, "3E80008100")] + [InlineData("Bad BMP value (odd length)", PublicEncodingRules.BER, "1E0120")] + [InlineData("Bad BMP value (high surrogate)", PublicEncodingRules.BER, "1E02D800")] + [InlineData("Bad BMP value (high private surrogate)", PublicEncodingRules.BER, "1E02DB81")] + [InlineData("Bad BMP value (low surrogate)", PublicEncodingRules.BER, "1E02DC00")] + public static void TryCopyBMPString_Throws( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + TryCopyBMPString_Throws(ruleSet, inputData); + } + + [Fact] + public static void TryCopyBMPString_Throws_CER_NestedTooLong() + { + // CER says that the maximum encoding length for a BMPString primitive + // is 1000. + // + // This test checks it for a primitive contained within a constructed. + // + // So we need 04 [1001] { 1001 0x00s } + // 1001 => 0x3E9, so the length encoding is 82 03 E9. + // 1001 + 3 + 1 == 1005 + // + // Plus a leading 3E 80 (indefinite length constructed) + // and a trailing 00 00 (End of contents) + // == 1009 + byte[] input = new byte[1009]; + // CONSTRUCTED BMPSTRING (indefinite) + input[0] = 0x3E; + input[1] = 0x80; + // OCTET STRING (1001) + input[2] = 0x04; + input[3] = 0x82; + input[4] = 0x03; + input[5] = 0xE9; + // EOC implicit since the byte[] initializes to zeros + + TryCopyBMPString_Throws(PublicEncodingRules.CER, input); + } + + [Fact] + public static void TryCopyBMPString_Throws_CER_NestedTooShortIntermediate() + { + // CER says that the maximum encoding length for a BMPString primitive + // is 1000, and in the constructed form the lengths must be + // [ 1000, 1000, 1000, ..., len%1000 ] + // + // So 1000, 2, 2 is illegal. + // + // 3E 80 (indefinite constructed BMP string) + // 04 82 03 08 (octet string, 1000 bytes) + // [1000 content bytes] + // 04 02 (octet string, 2 bytes) + // [2 content bytes] + // 04 02 (octet string, 2 bytes) + // [2 content bytes] + // 00 00 (end of contents) + // Looks like 1,016 bytes. + byte[] input = new byte[1016]; + // CONSTRUCTED BMP STRING (indefinite) + input[0] = 0x3E; + input[1] = 0x80; + // OCTET STRING (1000) + input[2] = 0x03; + input[3] = 0x82; + input[4] = 0x03; + input[5] = 0xE8; + // OCTET STRING (2) + input[1006] = 0x04; + input[1007] = 0x02; + // OCTET STRING (2) + input[1010] = 0x04; + input[1011] = 0x02; + // EOC implicit since the byte[] initializes to zeros + + TryCopyBMPString_Throws(PublicEncodingRules.CER, input); + } + + [Fact] + public static void TryCopyBMPStringBytes_Success_CER_MaxPrimitiveLength() + { + // CER says that the maximum encoding length for a BMPString primitive + // is 1000. + // + // So we need 1E [1000] { 1000 anythings } + // 1000 => 0x3E8, so the length encoding is 82 03 E8. + // 1000 + 3 + 1 == 1004 + byte[] input = new byte[1004]; + input[0] = 0x1E; + input[1] = 0x82; + input[2] = 0x03; + input[3] = 0xE8; + + // Content + input[4] = 0x65; + input[5] = 0x65; + input[1002] = 0x61; + input[1003] = 0x61; + + byte[] output = new byte[1000]; + + AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); + bool success = reader.TryCopyBMPStringBytes(output, out int bytesWritten); + + Assert.True(success, "reader.TryCopyBMPStringBytes"); + Assert.Equal(1000, bytesWritten); + + Assert.Equal( + input.AsReadOnlySpan().Slice(4).ByteArrayToHex(), + output.ByteArrayToHex()); + } + + [Fact] + public static void TryCopyBMPStringBytes_Success_CER_MinConstructedLength() + { + // CER says that the maximum encoding length for a BMPString primitive + // is 1000, and that a constructed form must be used for values greater + // than 1000 bytes, with segments dividing up for each thousand + // [1000, 1000, ..., len%1000]. + // + // So our smallest constructed form is 1001 bytes, [1000, 1] + // + // 3E 80 (indefinite constructed BMPString) + // 04 82 03 E9 (primitive octet string, 1000 bytes) + // [1000 content bytes] + // 04 01 (primitive octet string, 1 byte) + // pp + // 00 00 (end of contents, 0 bytes) + // 1011 total. + byte[] input = new byte[1011]; + int offset = 0; + // CONSTRUCTED BMPSTRING (Indefinite) + input[offset++] = 0x3E; + input[offset++] = 0x80; + // OCTET STRING (1000) + input[offset++] = 0x04; + input[offset++] = 0x82; + input[offset++] = 0x03; + input[offset++] = 0xE8; + + // Primitive 1: (65 65 :: 61 61) (1000) + input[offset++] = 0x65; + input[offset] = 0x65; + offset += 997; + input[offset++] = 0x61; + input[offset++] = 0x61; + + // OCTET STRING (1) + input[offset++] = 0x04; + input[offset++] = 0x01; + + // Primitive 2: One more byte + input[offset] = 0x2E; + + byte[] expected = new byte[1001]; + offset = 0; + expected[offset++] = 0x65; + expected[offset] = 0x65; + offset += 997; + expected[offset++] = 0x61; + expected[offset++] = 0x61; + expected[offset] = 0x2E; + + byte[] output = new byte[1001]; + + AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); + bool success = reader.TryCopyBMPStringBytes(output, out int bytesWritten); + + Assert.True(success, "reader.TryCopyBMPStringBytes"); + Assert.Equal(1001, bytesWritten); + + Assert.Equal( + expected.ByteArrayToHex(), + output.ByteArrayToHex()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) + { + byte[] inputData = { 0x1E, 4, 0, (byte)'h', 0, (byte)'i' }; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.TryGetBMPStringBytes(Asn1Tag.Null, out _)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.TryGetBMPStringBytes(new Asn1Tag(TagClass.ContextSpecific, 0), out _)); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + Assert.True(reader.TryGetBMPStringBytes(out ReadOnlyMemory value)); + Assert.Equal("00680069", value.ByteArrayToHex()); + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) + { + byte[] inputData = { 0x87, 2, 0x20, 0x10 }; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.TryGetBMPStringBytes(Asn1Tag.Null, out _)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.TryGetBMPStringBytes(out _)); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.TryGetBMPStringBytes(new Asn1Tag(TagClass.Application, 0), out _)); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.TryGetBMPStringBytes(new Asn1Tag(TagClass.ContextSpecific, 1), out _)); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + Assert.True( + reader.TryGetBMPStringBytes( + new Asn1Tag(TagClass.ContextSpecific, 7), + out ReadOnlyMemory value)); + + Assert.Equal("2010", value.ByteArrayToHex()); + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "1E022010", PublicTagClass.Universal, 30)] + [InlineData(PublicEncodingRules.CER, "1E022010", PublicTagClass.Universal, 30)] + [InlineData(PublicEncodingRules.DER, "1E022010", PublicTagClass.Universal, 30)] + [InlineData(PublicEncodingRules.BER, "8002FE60", PublicTagClass.ContextSpecific, 0)] + [InlineData(PublicEncodingRules.CER, "4C02FE60", PublicTagClass.Application, 12)] + [InlineData(PublicEncodingRules.DER, "DF8A4602FE60", PublicTagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + PublicEncodingRules ruleSet, + string inputHex, + PublicTagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.True( + reader.TryGetBMPStringBytes( + new Asn1Tag((TagClass)tagClass, tagValue, true), + out ReadOnlyMemory val1)); + + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.True( + reader.TryGetBMPStringBytes( + new Asn1Tag((TagClass)tagClass, tagValue, false), + out ReadOnlyMemory val2)); + + Assert.False(reader.HasData); + + Assert.Equal(val1.ByteArrayToHex(), val2.ByteArrayToHex()); + } + } + + internal static class ReaderBMPExtensions + { + public static bool TryGetBMPStringBytes( + this AsnReader reader, + out ReadOnlyMemory contents) + { + return reader.TryGetPrimitiveCharacterStringBytes( + UniversalTagNumber.BMPString, + out contents); + } + + public static bool TryGetBMPStringBytes( + this AsnReader reader, + Asn1Tag expectedTag, + out ReadOnlyMemory contents) + { + return reader.TryGetPrimitiveCharacterStringBytes( + expectedTag, + UniversalTagNumber.BMPString, + out contents); + } + + public static bool TryCopyBMPStringBytes( + this AsnReader reader, + Span destination, + out int bytesWritten) + { + return reader.TryCopyCharacterStringBytes( + UniversalTagNumber.BMPString, + destination, + out bytesWritten); + } + + public static bool TryCopyBMPString( + this AsnReader reader, + Span destination, + out int charsWritten) + { + return reader.TryCopyCharacterString( + UniversalTagNumber.BMPString, + destination, + out charsWritten); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBitString.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBitString.cs new file mode 100644 index 0000000000..7d4bae41d3 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBitString.cs @@ -0,0 +1,672 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public sealed class ReadBitString : Asn1ReaderTests + { + [Theory] + [InlineData("Uncleared unused bit", PublicEncodingRules.BER, "030201FF")] + [InlineData("Constructed Payload", PublicEncodingRules.BER, "2302030100")] + [InlineData("Constructed Payload-Indefinite", PublicEncodingRules.BER, "238003010000")] + // This value is actually invalid CER, but it returns false since it's not primitive and + // it isn't worth preempting the descent to find out it was invalid. + [InlineData("Constructed Payload-Indefinite", PublicEncodingRules.CER, "238003010000")] + public static void TryGetBitStringBytes_Fails( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryGetPrimitiveBitStringValue( + out int unusedBitCount, + out ReadOnlyMemory contents); + + Assert.False(didRead, "reader.TryGetBitStringBytes"); + Assert.Equal(0, unusedBitCount); + Assert.Equal(0, contents.Length); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, 0, 0, "030100")] + [InlineData(PublicEncodingRules.BER, 1, 1, "030201FE")] + [InlineData(PublicEncodingRules.CER, 2, 4, "030502FEEFF00C")] + [InlineData(PublicEncodingRules.DER, 7, 1, "03020780")] + [InlineData(PublicEncodingRules.DER, 0, 4, "030500FEEFF00D" + "0500")] + public static void TryGetBitStringBytes_Success( + PublicEncodingRules ruleSet, + int expectedUnusedBitCount, + int expectedLength, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryGetPrimitiveBitStringValue( + out int unusedBitCount, + out ReadOnlyMemory contents); + + Assert.True(didRead, "reader.TryGetBitStringBytes"); + Assert.Equal(expectedUnusedBitCount, unusedBitCount); + Assert.Equal(expectedLength, contents.Length); + } + + [Theory] + [InlineData("Wrong Tag", PublicEncodingRules.BER, "0500")] + [InlineData("Wrong Tag", PublicEncodingRules.CER, "0500")] + [InlineData("Wrong Tag", PublicEncodingRules.DER, "0500")] + [InlineData("Zero Length", PublicEncodingRules.BER, "0300")] + [InlineData("Zero Length", PublicEncodingRules.CER, "0300")] + [InlineData("Zero Length", PublicEncodingRules.DER, "0300")] + [InlineData("Bad Length", PublicEncodingRules.BER, "030200")] + [InlineData("Bad Length", PublicEncodingRules.CER, "030200")] + [InlineData("Bad Length", PublicEncodingRules.DER, "030200")] + [InlineData("Constructed Form", PublicEncodingRules.DER, "2303030100")] + public static void TryGetBitStringBytes_Throws( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws( + () => + { + reader.TryGetPrimitiveBitStringValue( + out int unusedBitCount, + out ReadOnlyMemory contents); + }); + } + + [Fact] + public static void TryGetBitStringBytes_Throws_CER_TooLong() + { + // CER says that the maximum encoding length for a BitString primitive is + // 1000 (999 value bytes and 1 unused bit count byte). + // + // So we need 03 [1001] { 1001 0x00s } + // 1001 => 0x3E9, so the length encoding is 82 03 E9. + // 1001 + 3 + 1 == 1005 + byte[] input = new byte[1005]; + input[0] = 0x03; + input[1] = 0x82; + input[2] = 0x03; + input[3] = 0xE9; + + AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); + + Assert.Throws( + () => + { + reader.TryGetPrimitiveBitStringValue( + out int unusedBitCount, + out ReadOnlyMemory contents); + }); + } + + [Fact] + public static void TryGetBitStringBytes_Success_CER_MaxLength() + { + // CER says that the maximum encoding length for a BitString primitive is + // 1000 (999 value bytes and 1 unused bit count byte). + // + // So we need 03 [1000] [0x00-0x07] { 998 anythings } [a byte that's legal for the bitmask] + // 1000 => 0x3E8, so the length encoding is 82 03 E8. + // 1000 + 3 + 1 == 1004 + byte[] input = new byte[1004]; + input[0] = 0x03; + input[1] = 0x82; + input[2] = 0x03; + input[3] = 0xE8; + + // Unused bits + input[4] = 0x02; + + // Payload + input[5] = 0xA0; + input[1002] = 0xA5; + input[1003] = 0xFC; + + AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); + + bool success = reader.TryGetPrimitiveBitStringValue( + out int unusedBitCount, + out ReadOnlyMemory contents); + + Assert.True(success, "reader.TryGetBitStringBytes"); + Assert.Equal(input[4], unusedBitCount); + Assert.Equal(999, contents.Length); + + // Check that it is, in fact, the same memory. No copies with this API. + Assert.True( + Unsafe.AreSame( + ref MemoryMarshal.GetReference(contents.Span), + ref input[5])); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "03020780")] + [InlineData(PublicEncodingRules.BER, "030207FF")] + [InlineData(PublicEncodingRules.CER, "03020780")] + [InlineData(PublicEncodingRules.DER, "03020780")] + [InlineData( + PublicEncodingRules.BER, + "2380" + + "2380" + + "0000" + + "03020000" + + "0000")] + public static void TryCopyBitStringBytes_Fails(PublicEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryCopyBitStringBytes( + Span.Empty, + out int unusedBitCount, + out int bytesWritten); + + Assert.False(didRead, "reader.TryCopyBitStringBytes"); + Assert.Equal(0, unusedBitCount); + Assert.Equal(0, bytesWritten); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "03020780", "80", 7)] + [InlineData(PublicEncodingRules.BER, "030207FF", "80", 7)] + [InlineData(PublicEncodingRules.CER, "03020780", "80", 7)] + [InlineData(PublicEncodingRules.DER, "03020680", "80", 6)] + [InlineData(PublicEncodingRules.BER, "23800000", "", 0)] + [InlineData(PublicEncodingRules.BER, "2300", "", 0)] + [InlineData(PublicEncodingRules.BER, "2300" + "0500", "", 0)] + [InlineData(PublicEncodingRules.BER, "0303010203" + "0500", "0202", 1)] + [InlineData( + PublicEncodingRules.BER, + "2380" + + "2380" + + "0000" + + "03020000" + + "0000", + "00", + 0)] + [InlineData( + PublicEncodingRules.BER, + "230C" + + "2380" + + "2380" + + "0000" + + "03020000" + + "0000", + "00", + 0)] + [InlineData( + PublicEncodingRules.BER, + "2380" + + "2308" + + "030200FA" + + "030200CE" + + "2380" + + "2380" + + "2380" + + "030300F00D" + + "0000" + + "0000" + + "03020001" + + "0000" + + "0303000203" + + "030203FF" + + "2380" + + "0000" + + "0000", + "FACEF00D010203F8", + 3)] + public static void TryCopyBitStringBytes_Success( + PublicEncodingRules ruleSet, + string inputHex, + string expectedHex, + int expectedUnusedBitCount) + { + byte[] inputData = inputHex.HexToByteArray(); + byte[] output = new byte[expectedHex.Length / 2]; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryCopyBitStringBytes( + output, + out int unusedBitCount, + out int bytesWritten); + + Assert.True(didRead, "reader.TryCopyBitStringBytes"); + Assert.Equal(expectedUnusedBitCount, unusedBitCount); + Assert.Equal(expectedHex, output.AsReadOnlySpan().Slice(0, bytesWritten).ByteArrayToHex()); + } + + private static void TryCopyBitStringBytes_Throws( + PublicEncodingRules ruleSet, + byte[] input) + { + AsnReader reader = new AsnReader(input, (AsnEncodingRules)ruleSet); + + Assert.Throws( + () => + { + reader.TryCopyBitStringBytes( + Span.Empty, + out int unusedBitCount, + out int bytesWritten); + }); + } + + [Theory] + [InlineData("Wrong Tag", PublicEncodingRules.BER, "0500")] + [InlineData("Wrong Tag", PublicEncodingRules.CER, "0500")] + [InlineData("Wrong Tag", PublicEncodingRules.DER, "0500")] + [InlineData("Zero Length", PublicEncodingRules.BER, "0300")] + [InlineData("Zero Length", PublicEncodingRules.CER, "0300")] + [InlineData("Zero Length", PublicEncodingRules.DER, "0300")] + [InlineData("Bad Length", PublicEncodingRules.BER, "030200")] + [InlineData("Bad Length", PublicEncodingRules.CER, "030200")] + [InlineData("Bad Length", PublicEncodingRules.DER, "030200")] + [InlineData("Constructed Form", PublicEncodingRules.DER, "2303030100")] + [InlineData("Bad unused bits", PublicEncodingRules.BER, "03020800")] + [InlineData("Bad unused bits", PublicEncodingRules.CER, "03020800")] + [InlineData("Bad unused bits", PublicEncodingRules.DER, "03020800")] + [InlineData("Bad unused bits-nodata", PublicEncodingRules.BER, "030101")] + [InlineData("Bad unused bits-nodata", PublicEncodingRules.CER, "030101")] + [InlineData("Bad unused bits-nodata", PublicEncodingRules.DER, "030101")] + [InlineData("Bad nested unused bits", PublicEncodingRules.BER, "230403020800")] + [InlineData("Bad nested unused bits-indef", PublicEncodingRules.BER, "2380030208000000")] + [InlineData("Bad nested unused bits-indef", PublicEncodingRules.CER, "2380030208000000")] + [InlineData("Bad nested unused bits-nodata", PublicEncodingRules.BER, "2303030101")] + [InlineData("Bad nested unused bits-nodata-indef", PublicEncodingRules.BER, "23800301010000")] + [InlineData("Bad nested unused bits-nodata-indef", PublicEncodingRules.CER, "23800301010000")] + [InlineData("Bad mask", PublicEncodingRules.CER, "030201FF")] + [InlineData("Bad mask", PublicEncodingRules.DER, "030201FF")] + [InlineData("Bad nested mask", PublicEncodingRules.CER, "2380030201FF0000")] + [InlineData("Nested context-specific", PublicEncodingRules.BER, "2304800300FACE")] + [InlineData("Nested context-specific (indef)", PublicEncodingRules.BER, "2380800300FACE0000")] + [InlineData("Nested context-specific (indef)", PublicEncodingRules.CER, "2380800300FACE0000")] + [InlineData("Nested boolean", PublicEncodingRules.BER, "2303010100")] + [InlineData("Nested boolean (indef)", PublicEncodingRules.BER, "23800101000000")] + [InlineData("Nested boolean (indef)", PublicEncodingRules.CER, "23800101000000")] + [InlineData("Nested constructed form", PublicEncodingRules.CER, "2380" + "2380" + "03010" + "000000000")] + [InlineData("No terminator", PublicEncodingRules.BER, "2380" + "03020000" + "")] + [InlineData("No terminator", PublicEncodingRules.CER, "2380" + "03020000" + "")] + [InlineData("No content", PublicEncodingRules.BER, "2380")] + [InlineData("No content", PublicEncodingRules.CER, "2380")] + [InlineData("No nested content", PublicEncodingRules.CER, "23800000")] + [InlineData("Nested value too long", PublicEncodingRules.BER, "2380030A00")] + [InlineData("Nested value too long - constructed", PublicEncodingRules.BER, "2380230A00")] + [InlineData("Nested value too long - simple", PublicEncodingRules.BER, "2303" + "03050000000000")] + [InlineData( + "Unused bits in intermediate segment", + PublicEncodingRules.BER, + "2380" + + "0303000102" + + "0303020304" + + "0303010506" + + "0000")] + [InlineData("Constructed EndOfContents", PublicEncodingRules.BER, "238020000000")] + [InlineData("Constructed EndOfContents", PublicEncodingRules.CER, "238020000000")] + [InlineData("NonEmpty EndOfContents", PublicEncodingRules.BER, "2380000100")] + [InlineData("NonEmpty EndOfContents", PublicEncodingRules.CER, "2380000100")] + [InlineData("LongLength EndOfContents", PublicEncodingRules.BER, "2380008100")] + [InlineData("Constructed Payload-TooShort", PublicEncodingRules.CER, "23800301000000")] + public static void TryCopyBitStringBytes_Throws( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + TryCopyBitStringBytes_Throws(ruleSet, inputData); + } + + [Fact] + public static void TryCopyBitStringBytes_Throws_CER_TooLong() + { + // CER says that the maximum encoding length for a BitString primitive is + // 1000 (999 value bytes and 1 unused bit count byte). + // + // So we need 03 [1001] { 1001 0x00s } + // 1001 => 0x3E9, so the length encoding is 82 03 E9. + // 1001 + 3 + 1 == 1004 + byte[] input = new byte[1004]; + input[0] = 0x03; + input[1] = 0x82; + input[2] = 0x03; + input[3] = 0xE9; + + TryCopyBitStringBytes_Throws(PublicEncodingRules.CER, input); + } + + [Fact] + public static void TryCopyBitStringBytes_Throws_CER_NestedTooLong() + { + // CER says that the maximum encoding length for a BitString primitive is + // 1000 (999 value bytes and 1 unused bit count byte). + // + // This test checks it for a primitive contained within a constructed. + // + // So we need 03 [1001] { 1001 0x00s } + // 1001 => 0x3E9, so the length encoding is 82 03 E9. + // 1001 + 3 + 1 == 1005 + // + // Plus a leading 23 80 (indefinite length constructed) + // and a trailing 00 00 (End of contents) + // == 1009 + byte[] input = new byte[1009]; + // CONSTRUCTED BIT STRING (indefinite) + input[0] = 0x23; + input[1] = 0x80; + // BIT STRING (1001) + input[2] = 0x03; + input[3] = 0x82; + input[4] = 0x03; + input[5] = 0xE9; + // EOC implicit since the byte[] initializes to zeros + + TryCopyBitStringBytes_Throws(PublicEncodingRules.CER, input); + } + + [Fact] + public static void TryCopyBitStringBytes_Throws_CER_NestedTooShortIntermediate() + { + // CER says that the maximum encoding length for a BitString primitive is + // 1000 (999 value bytes and 1 unused bit count byte), and in the constructed + // form the lengths must be + // [ 1000, 1000, 1000, ..., len%1000 ] + // + // So 1000, 2, 2 is illegal. + // + // 23 80 (indefinite constructed bit string) + // 03 82 03 08 (bit string, 1000 bytes) + // [1000 content bytes] + // 03 02 (bit string, 2 bytes) + // [2 content bytes] + // 03 02 (bit string, 2 bytes) + // [2 content bytes] + // 00 00 (end of contents) + // Looks like 1,016 bytes. + byte[] input = new byte[1016]; + // CONSTRUCTED BIT STRING (indefinite) + input[0] = 0x23; + input[1] = 0x80; + // BIT STRING (1000) + input[2] = 0x03; + input[3] = 0x82; + input[4] = 0x03; + input[5] = 0xE8; + // BIT STRING (2) + input[1006] = 0x03; + input[1007] = 0x02; + // BIT STRING (2) + input[1010] = 0x03; + input[1011] = 0x02; + // EOC implicit since the byte[] initializes to zeros + + TryCopyBitStringBytes_Throws(PublicEncodingRules.CER, input); + } + + [Fact] + public static void TryCopyBitStringBytes_Success_CER_MaxPrimitiveLength() + { + // CER says that the maximum encoding length for a BitString primitive is + // 1000 (999 value bytes and 1 unused bit count byte). + // + // So we need 03 [1000] [0x00-0x07] { 998 anythings } [a byte that's legal for the bitmask] + // 1000 => 0x3E8, so the length encoding is 82 03 E8. + // 1000 + 3 + 1 == 1003 + byte[] input = new byte[1004]; + input[0] = 0x03; + input[1] = 0x82; + input[2] = 0x03; + input[3] = 0xE8; + + // Unused bits + input[4] = 0x02; + + // Payload + input[5] = 0xA0; + input[1002] = 0xA5; + input[1003] = 0xFC; + + byte[] output = new byte[999]; + + AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); + + bool success = reader.TryCopyBitStringBytes( + output, + out int unusedBitCount, + out int bytesWritten); + + Assert.True(success, "reader.TryCopyBitStringBytes"); + Assert.Equal(input[4], unusedBitCount); + Assert.Equal(999, bytesWritten); + + Assert.Equal( + input.AsReadOnlySpan().Slice(5).ByteArrayToHex(), + output.ByteArrayToHex()); + } + + [Fact] + public static void TryCopyBitStringBytes_Success_CER_MinConstructedLength() + { + // CER says that the maximum encoding length for a BitString primitive is + // 1000 (999 value bytes and 1 unused bit count byte), and that a constructed + // form must be used for values greater than 1000 bytes, with segments dividing + // up for each thousand [1000, 1000, ..., len%1000]. + // + // Bit string primitives are one byte of "unused bits" and the rest are payload, + // so the minimum constructed payload has total content length 1002: + // [1000 (1+999), 2 (1+1)] + // + // 23 80 (indefinite constructed bit string) + // 03 82 03 E9 (primitive bit string, 1000 bytes) + // 00 [999 more payload bytes] + // 03 02 (primitive bit string, 2 bytes) + // uu pp + // 00 00 (end of contents, 0 bytes) + // 1010 total. + byte[] input = new byte[1012]; + int offset = 0; + // CONSTRUCTED BIT STRING (Indefinite) + input[offset++] = 0x23; + input[offset++] = 0x80; + // BIT STRING (1000) + input[offset++] = 0x03; + input[offset++] = 0x82; + input[offset++] = 0x03; + input[offset++] = 0xE8; + + // Primitive 1: Unused bits MUST be 0. + input[offset++] = 0x00; + + // Payload (A0 :: A5 FC) (999) + input[offset] = 0xA0; + offset += 997; + input[offset++] = 0xA5; + input[offset++] = 0xFC; + + // BIT STRING (2) + input[offset++] = 0x03; + input[offset++] = 0x02; + + // Primitive 2: Unused bits 0-7 + input[offset++] = 0x3; + + // Payload (must have the three least significant bits unset) + input[offset] = 0b0000_1000; + + byte[] expected = new byte[1000]; + offset = 0; + expected[offset] = 0xA0; + offset += 997; + expected[offset++] = 0xA5; + expected[offset++] = 0xFC; + expected[offset] = 0b0000_1000; + + byte[] output = new byte[1000]; + + AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); + + bool success = reader.TryCopyBitStringBytes( + output, + out int unusedBitCount, + out int bytesWritten); + + Assert.True(success, "reader.TryCopyBitStringBytes"); + Assert.Equal(input[1006], unusedBitCount); + Assert.Equal(1000, bytesWritten); + + Assert.Equal( + expected.ByteArrayToHex(), + output.ByteArrayToHex()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) + { + byte[] inputData = { 3, 2, 1, 0x7E }; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.TryGetPrimitiveBitStringValue(Asn1Tag.Null, out _, out _)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.TryGetPrimitiveBitStringValue(new Asn1Tag(TagClass.ContextSpecific, 0), out _, out _)); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + Assert.True(reader.TryGetPrimitiveBitStringValue(out int unusedBitCount, out ReadOnlyMemory contents)); + Assert.Equal("7E", contents.ByteArrayToHex()); + Assert.Equal(1, unusedBitCount); + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) + { + byte[] inputData = { 0x87, 2, 0, 0x80 }; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.TryGetPrimitiveBitStringValue(Asn1Tag.Null, out _, out _)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.TryGetPrimitiveBitStringValue(out _, out _)); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.TryGetPrimitiveBitStringValue(new Asn1Tag(TagClass.Application, 0), out _, out _)); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.TryGetPrimitiveBitStringValue(new Asn1Tag(TagClass.ContextSpecific, 1), out _, out _)); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + Assert.True( + reader.TryGetPrimitiveBitStringValue( + new Asn1Tag(TagClass.ContextSpecific, 7), + out int unusedBitCount, + out ReadOnlyMemory contents)); + + Assert.Equal("80", contents.ByteArrayToHex()); + Assert.Equal(0, unusedBitCount); + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "030400010203", PublicTagClass.Universal, 3)] + [InlineData(PublicEncodingRules.CER, "030400010203", PublicTagClass.Universal, 3)] + [InlineData(PublicEncodingRules.DER, "030400010203", PublicTagClass.Universal, 3)] + [InlineData(PublicEncodingRules.BER, "800200FF", PublicTagClass.ContextSpecific, 0)] + [InlineData(PublicEncodingRules.CER, "4C0200FF", PublicTagClass.Application, 12)] + [InlineData(PublicEncodingRules.DER, "DF8A460200FF", PublicTagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + PublicEncodingRules ruleSet, + string inputHex, + PublicTagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.True( + reader.TryGetPrimitiveBitStringValue( + new Asn1Tag((TagClass)tagClass, tagValue, true), + out int ubc1, + out ReadOnlyMemory val1)); + + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.True( + reader.TryGetPrimitiveBitStringValue( + new Asn1Tag((TagClass)tagClass, tagValue, false), + out int ubc2, + out ReadOnlyMemory val2)); + + Assert.False(reader.HasData); + + Assert.Equal(val1.ByteArrayToHex(), val2.ByteArrayToHex()); + Assert.Equal(ubc1, ubc2); + } + + [Fact] + public static void TryCopyBitStringBytes_ExtremelyNested() + { + byte[] dataBytes = new byte[4 * 16384]; + + // This will build 2^14 nested indefinite length values. + // In the end, none of them contain any content. + // + // For what it's worth, the initial algorithm succeeded at 1017, and StackOverflowed with 1018. + int end = dataBytes.Length / 2; + + // UNIVERSAL BIT STRING [Constructed] + const byte Tag = 0x20 | (byte)UniversalTagNumber.BitString; + + for (int i = 0; i < end; i += 2) + { + dataBytes[i] = Tag; + // Indefinite length + dataBytes[i + 1] = 0x80; + } + + AsnReader reader = new AsnReader(dataBytes, AsnEncodingRules.BER); + + int bytesWritten; + int unusedBitCount; + + Assert.True( + reader.TryCopyBitStringBytes(Span.Empty, out unusedBitCount, out bytesWritten)); + + Assert.Equal(0, bytesWritten); + Assert.Equal(0, unusedBitCount); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBoolean.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBoolean.cs new file mode 100644 index 0000000000..6aaa15048e --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBoolean.cs @@ -0,0 +1,220 @@ +// 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.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public sealed class ReadBoolean : Asn1ReaderTests + { + [Theory] + [InlineData(PublicEncodingRules.BER, false, 3, "010100")] + [InlineData(PublicEncodingRules.BER, true, 3, "010101")] + // Padded length + [InlineData(PublicEncodingRules.BER, true, 4, "01810101")] + [InlineData(PublicEncodingRules.BER, true, 3, "0101FF0500")] + [InlineData(PublicEncodingRules.CER, false, 3, "0101000500")] + [InlineData(PublicEncodingRules.CER, true, 3, "0101FF")] + [InlineData(PublicEncodingRules.DER, false, 3, "010100")] + [InlineData(PublicEncodingRules.DER, true, 3, "0101FF0500")] + // Context Specific 0 + [InlineData(PublicEncodingRules.DER, true, 3, "8001FF0500")] + // Application 31 + [InlineData(PublicEncodingRules.DER, true, 4, "5F1F01FF0500")] + // Private 253 + [InlineData(PublicEncodingRules.CER, false, 5, "DF817D01000500")] + public static void ReadBoolean_Success( + PublicEncodingRules ruleSet, + bool expectedValue, + int expectedBytesRead, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Asn1Tag tag = reader.PeekTag(); + bool value; + + if (tag.TagClass == TagClass.Universal) + { + value = reader.ReadBoolean(); + } + else + { + value = reader.ReadBoolean(tag); + } + + if (inputData.Length == expectedBytesRead) + { + Assert.False(reader.HasData, "reader.HasData"); + } + else + { + Assert.True(reader.HasData, "reader.HasData"); + } + + if (expectedValue) + { + Assert.True(value, "value"); + } + else + { + Assert.False(value, "value"); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) + { + byte[] inputData = { 1, 1, 0 }; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadBoolean(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadBoolean(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + bool value = reader.ReadBoolean(); + Assert.False(value, "value"); + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) + { + byte[] inputData = { 0x80, 1, 0xFF }; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadBoolean(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadBoolean()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws(() => reader.ReadBoolean(new Asn1Tag(TagClass.Application, 0))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws(() => reader.ReadBoolean(new Asn1Tag(TagClass.ContextSpecific, 1))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + bool value = reader.ReadBoolean(new Asn1Tag(TagClass.ContextSpecific, 0)); + Assert.True(value, "value"); + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "0101FF", PublicTagClass.Universal, 1)] + [InlineData(PublicEncodingRules.CER, "0101FF", PublicTagClass.Universal, 1)] + [InlineData(PublicEncodingRules.DER, "0101FF", PublicTagClass.Universal, 1)] + [InlineData(PublicEncodingRules.BER, "8001FF", PublicTagClass.ContextSpecific, 0)] + [InlineData(PublicEncodingRules.CER, "4C01FF", PublicTagClass.Application, 12)] + [InlineData(PublicEncodingRules.DER, "DF8A4601FF", PublicTagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + PublicEncodingRules ruleSet, + string inputHex, + PublicTagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + bool val1 = reader.ReadBoolean(new Asn1Tag((TagClass)tagClass, tagValue, true)); + Assert.False(reader.HasData); + reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + bool val2 = reader.ReadBoolean(new Asn1Tag((TagClass)tagClass, tagValue, false)); + Assert.False(reader.HasData); + + Assert.Equal(val1, val2); + } + + [Theory] + [InlineData("Empty", PublicEncodingRules.DER, "")] + [InlineData("Empty", PublicEncodingRules.CER, "")] + [InlineData("Empty", PublicEncodingRules.BER, "")] + [InlineData("TagOnly", PublicEncodingRules.BER, "01")] + [InlineData("TagOnly", PublicEncodingRules.CER, "01")] + [InlineData("TagOnly", PublicEncodingRules.DER, "01")] + [InlineData("MultiByte TagOnly", PublicEncodingRules.DER, "9F1F")] + [InlineData("MultiByte TagOnly", PublicEncodingRules.CER, "9F1F")] + [InlineData("MultiByte TagOnly", PublicEncodingRules.BER, "9F1F")] + [InlineData("TagAndLength", PublicEncodingRules.BER, "0101")] + [InlineData("Tag and MultiByteLength", PublicEncodingRules.BER, "01820001")] + [InlineData("TagAndLength", PublicEncodingRules.CER, "8001")] + [InlineData("TagAndLength", PublicEncodingRules.DER, "C001")] + [InlineData("MultiByteTagAndLength", PublicEncodingRules.DER, "9F2001")] + [InlineData("MultiByteTagAndLength", PublicEncodingRules.CER, "9F2001")] + [InlineData("MultiByteTagAndLength", PublicEncodingRules.BER, "9F2001")] + [InlineData("MultiByteTagAndMultiByteLength", PublicEncodingRules.BER, "9F28200001")] + [InlineData("TooShort", PublicEncodingRules.BER, "0100")] + [InlineData("TooShort", PublicEncodingRules.CER, "8000")] + [InlineData("TooShort", PublicEncodingRules.DER, "0100")] + [InlineData("TooLong", PublicEncodingRules.DER, "C0020000")] + [InlineData("TooLong", PublicEncodingRules.CER, "01020000")] + [InlineData("TooLong", PublicEncodingRules.BER, "C081020000")] + [InlineData("MissingContents", PublicEncodingRules.BER, "C001")] + [InlineData("MissingContents", PublicEncodingRules.CER, "0101")] + [InlineData("MissingContents", PublicEncodingRules.DER, "8001")] + [InlineData("NonCanonical", PublicEncodingRules.DER, "0101FE")] + [InlineData("NonCanonical", PublicEncodingRules.CER, "800101")] + [InlineData("Constructed", PublicEncodingRules.BER, "2103010101")] + [InlineData("Constructed", PublicEncodingRules.CER, "2103010101")] + [InlineData("Constructed", PublicEncodingRules.DER, "2103010101")] + [InlineData("WrongTag", PublicEncodingRules.DER, "0400")] + [InlineData("WrongTag", PublicEncodingRules.CER, "0400")] + [InlineData("WrongTag", PublicEncodingRules.BER, "0400")] + public static void ReadBoolean_Failure( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + Asn1Tag tag = default(Asn1Tag); + + if (inputData.Length > 0) + { + tag = reader.PeekTag(); + } + + if (tag.TagClass == TagClass.Universal) + { + Assert.Throws(() => reader.ReadBoolean()); + } + else + { + Assert.Throws(() => reader.ReadBoolean(tag)); + } + + if (inputData.Length == 0) + { + // If we started with nothing, where did the data come from? + Assert.False(reader.HasData, "reader.HasData"); + } + else + { + // Nothing should have moved + Assert.True(reader.HasData, "reader.HasData"); + } + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadEnumerated.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadEnumerated.cs new file mode 100644 index 0000000000..b921496d79 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadEnumerated.cs @@ -0,0 +1,760 @@ +// 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.Reflection; +using System.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public sealed class ReadEnumerated : Asn1ReaderTests + { + public enum ByteBacked : byte + { + Zero = 0, + NotFluffy = 11, + Fluff = 12, + } + + public enum SByteBacked : sbyte + { + Zero = 0, + Fluff = 83, + Pillow = -17, + } + + public enum ShortBacked : short + { + Zero = 0, + Fluff = 521, + Pillow = -1024, + } + + public enum UShortBacked : ushort + { + Zero = 0, + Fluff = 32768, + } + + public enum IntBacked : int + { + Zero = 0, + Fluff = 0x010001, + Pillow = -Fluff, + } + + public enum UIntBacked : uint + { + Zero = 0, + Fluff = 0x80000005, + } + + public enum LongBacked : long + { + Zero = 0, + Fluff = 0x0200000441, + Pillow = -0x100000000L, + } + + public enum ULongBacked : ulong + { + Zero = 0, + Fluff = 0xFACEF00DCAFEBEEF, + } + + private static void GetExpectedValue( + PublicEncodingRules ruleSet, + TEnum expectedValue, + string inputHex) + where TEnum : struct + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + TEnum value = reader.GetEnumeratedValue(); + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, ByteBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.CER, ByteBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.DER, ByteBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.BER, ByteBacked.Fluff, "0A010C")] + [InlineData(PublicEncodingRules.CER, ByteBacked.Fluff, "0A010C")] + [InlineData(PublicEncodingRules.DER, ByteBacked.Fluff, "0A010C")] + [InlineData(PublicEncodingRules.BER, (ByteBacked)255, "0A0200FF")] + [InlineData(PublicEncodingRules.CER, (ByteBacked)128, "0A020080")] + [InlineData(PublicEncodingRules.DER, (ByteBacked)129, "0A020081")] + [InlineData(PublicEncodingRules.BER, (ByteBacked)254, "0A82000200FE")] + public static void GetExpectedValue_ByteBacked( + PublicEncodingRules ruleSet, + ByteBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, SByteBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.CER, SByteBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.DER, SByteBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.BER, SByteBacked.Fluff, "0A0153")] + [InlineData(PublicEncodingRules.CER, SByteBacked.Fluff, "0A0153")] + [InlineData(PublicEncodingRules.DER, SByteBacked.Fluff, "0A0153")] + [InlineData(PublicEncodingRules.BER, SByteBacked.Pillow, "0A01EF")] + [InlineData(PublicEncodingRules.CER, (SByteBacked)sbyte.MinValue, "0A0180")] + [InlineData(PublicEncodingRules.DER, (SByteBacked)sbyte.MinValue + 1, "0A0181")] + [InlineData(PublicEncodingRules.BER, SByteBacked.Pillow, "0A820001EF")] + public static void GetExpectedValue_SByteBacked( + PublicEncodingRules ruleSet, + SByteBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, ShortBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.CER, ShortBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.DER, ShortBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.BER, ShortBacked.Fluff, "0A020209")] + [InlineData(PublicEncodingRules.CER, ShortBacked.Fluff, "0A020209")] + [InlineData(PublicEncodingRules.DER, ShortBacked.Fluff, "0A020209")] + [InlineData(PublicEncodingRules.BER, ShortBacked.Pillow, "0A02FC00")] + [InlineData(PublicEncodingRules.CER, (ShortBacked)short.MinValue, "0A028000")] + [InlineData(PublicEncodingRules.DER, (ShortBacked)short.MinValue + 1, "0A028001")] + [InlineData(PublicEncodingRules.BER, ShortBacked.Pillow, "0A820002FC00")] + public static void GetExpectedValue_ShortBacked( + PublicEncodingRules ruleSet, + ShortBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, UShortBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.CER, UShortBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.DER, UShortBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.BER, UShortBacked.Fluff, "0A03008000")] + [InlineData(PublicEncodingRules.CER, UShortBacked.Fluff, "0A03008000")] + [InlineData(PublicEncodingRules.DER, UShortBacked.Fluff, "0A03008000")] + [InlineData(PublicEncodingRules.BER, (UShortBacked)255, "0A0200FF")] + [InlineData(PublicEncodingRules.CER, (UShortBacked)256, "0A020100")] + [InlineData(PublicEncodingRules.DER, (UShortBacked)0x7FED, "0A027FED")] + [InlineData(PublicEncodingRules.BER, (UShortBacked)ushort.MaxValue, "0A82000300FFFF")] + [InlineData(PublicEncodingRules.BER, (UShortBacked)0x8123, "0A820003008123")] + public static void GetExpectedValue_UShortBacked( + PublicEncodingRules ruleSet, + UShortBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, IntBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.CER, IntBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.DER, IntBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.BER, IntBacked.Fluff, "0A03010001")] + [InlineData(PublicEncodingRules.CER, IntBacked.Fluff, "0A03010001")] + [InlineData(PublicEncodingRules.DER, IntBacked.Fluff, "0A03010001")] + [InlineData(PublicEncodingRules.BER, IntBacked.Pillow, "0A03FEFFFF")] + [InlineData(PublicEncodingRules.CER, (IntBacked)int.MinValue, "0A0480000000")] + [InlineData(PublicEncodingRules.DER, (IntBacked)int.MinValue + 1, "0A0480000001")] + [InlineData(PublicEncodingRules.BER, IntBacked.Pillow, "0A820003FEFFFF")] + public static void GetExpectedValue_IntBacked( + PublicEncodingRules ruleSet, + IntBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, UIntBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.CER, UIntBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.DER, UIntBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.BER, UIntBacked.Fluff, "0A050080000005")] + [InlineData(PublicEncodingRules.CER, UIntBacked.Fluff, "0A050080000005")] + [InlineData(PublicEncodingRules.DER, UIntBacked.Fluff, "0A050080000005")] + [InlineData(PublicEncodingRules.BER, (UIntBacked)255, "0A0200FF")] + [InlineData(PublicEncodingRules.CER, (UIntBacked)256, "0A020100")] + [InlineData(PublicEncodingRules.DER, (UIntBacked)0x7FED, "0A027FED")] + [InlineData(PublicEncodingRules.BER, (UIntBacked)uint.MaxValue, "0A82000500FFFFFFFF")] + [InlineData(PublicEncodingRules.BER, (UIntBacked)0x8123, "0A820003008123")] + public static void GetExpectedValue_UIntBacked( + PublicEncodingRules ruleSet, + UIntBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, LongBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.CER, LongBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.DER, LongBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.BER, LongBacked.Fluff, "0A050200000441")] + [InlineData(PublicEncodingRules.CER, LongBacked.Fluff, "0A050200000441")] + [InlineData(PublicEncodingRules.DER, LongBacked.Fluff, "0A050200000441")] + [InlineData(PublicEncodingRules.BER, LongBacked.Pillow, "0A05FF00000000")] + [InlineData(PublicEncodingRules.CER, (LongBacked)short.MinValue, "0A028000")] + [InlineData(PublicEncodingRules.DER, (LongBacked)short.MinValue + 1, "0A028001")] + [InlineData(PublicEncodingRules.BER, LongBacked.Pillow, "0A820005FF00000000")] + public static void GetExpectedValue_LongBacked( + PublicEncodingRules ruleSet, + LongBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, ULongBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.CER, ULongBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.DER, ULongBacked.Zero, "0A0100")] + [InlineData(PublicEncodingRules.BER, ULongBacked.Fluff, "0A0900FACEF00DCAFEBEEF")] + [InlineData(PublicEncodingRules.CER, ULongBacked.Fluff, "0A0900FACEF00DCAFEBEEF")] + [InlineData(PublicEncodingRules.DER, ULongBacked.Fluff, "0A0900FACEF00DCAFEBEEF")] + [InlineData(PublicEncodingRules.BER, (ULongBacked)255, "0A0200FF")] + [InlineData(PublicEncodingRules.CER, (ULongBacked)256, "0A020100")] + [InlineData(PublicEncodingRules.DER, (ULongBacked)0x7FED, "0A027FED")] + [InlineData(PublicEncodingRules.BER, (ULongBacked)uint.MaxValue, "0A82000500FFFFFFFF")] + [InlineData(PublicEncodingRules.BER, (ULongBacked)ulong.MaxValue, "0A82000900FFFFFFFFFFFFFFFF")] + [InlineData(PublicEncodingRules.BER, (ULongBacked)0x8123, "0A820003008123")] + public static void GetExpectedValue_ULongBacked( + PublicEncodingRules ruleSet, + ULongBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "")] + [InlineData(PublicEncodingRules.CER, "")] + [InlineData(PublicEncodingRules.DER, "")] + [InlineData(PublicEncodingRules.BER, "0A")] + [InlineData(PublicEncodingRules.CER, "0A")] + [InlineData(PublicEncodingRules.DER, "0A")] + [InlineData(PublicEncodingRules.BER, "0A00")] + [InlineData(PublicEncodingRules.CER, "0A00")] + [InlineData(PublicEncodingRules.DER, "0A00")] + [InlineData(PublicEncodingRules.BER, "0A01")] + [InlineData(PublicEncodingRules.CER, "0A01")] + [InlineData(PublicEncodingRules.DER, "0A01")] + [InlineData(PublicEncodingRules.BER, "0A81")] + [InlineData(PublicEncodingRules.CER, "0A81")] + [InlineData(PublicEncodingRules.DER, "0A81")] + [InlineData(PublicEncodingRules.BER, "9F00")] + [InlineData(PublicEncodingRules.CER, "9F00")] + [InlineData(PublicEncodingRules.DER, "9F00")] + [InlineData(PublicEncodingRules.BER, "0A01FF")] + [InlineData(PublicEncodingRules.CER, "0A01FF")] + [InlineData(PublicEncodingRules.DER, "0A01FF")] + [InlineData(PublicEncodingRules.BER, "0A02007F")] + [InlineData(PublicEncodingRules.CER, "0A02007F")] + [InlineData(PublicEncodingRules.DER, "0A02007F")] + [InlineData(PublicEncodingRules.BER, "0A020102")] + [InlineData(PublicEncodingRules.CER, "0A020102")] + [InlineData(PublicEncodingRules.DER, "0A020102")] + [InlineData(PublicEncodingRules.BER, "0A02FF80")] + [InlineData(PublicEncodingRules.CER, "0A02FF80")] + [InlineData(PublicEncodingRules.DER, "0A02FF80")] + [InlineData(PublicEncodingRules.BER, "0A03010203")] + [InlineData(PublicEncodingRules.CER, "0A03010203")] + [InlineData(PublicEncodingRules.DER, "0A03010203")] + [InlineData(PublicEncodingRules.BER, "0A0401020304")] + [InlineData(PublicEncodingRules.CER, "0A0401020304")] + [InlineData(PublicEncodingRules.DER, "0A0401020304")] + [InlineData(PublicEncodingRules.BER, "0A050102030405")] + [InlineData(PublicEncodingRules.CER, "0A050102030405")] + [InlineData(PublicEncodingRules.DER, "0A050102030405")] + [InlineData(PublicEncodingRules.BER, "0A080102030405060708")] + [InlineData(PublicEncodingRules.CER, "0A080102030405060708")] + [InlineData(PublicEncodingRules.DER, "0A080102030405060708")] + [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.BER, "2A030A0100")] + public static void GetEnumeratedValue_Invalid_Byte(PublicEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws(() => reader.GetEnumeratedValue()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "")] + [InlineData(PublicEncodingRules.CER, "")] + [InlineData(PublicEncodingRules.DER, "")] + [InlineData(PublicEncodingRules.BER, "0A")] + [InlineData(PublicEncodingRules.CER, "0A")] + [InlineData(PublicEncodingRules.DER, "0A")] + [InlineData(PublicEncodingRules.BER, "0A00")] + [InlineData(PublicEncodingRules.CER, "0A00")] + [InlineData(PublicEncodingRules.DER, "0A00")] + [InlineData(PublicEncodingRules.BER, "0A01")] + [InlineData(PublicEncodingRules.CER, "0A01")] + [InlineData(PublicEncodingRules.DER, "0A01")] + [InlineData(PublicEncodingRules.BER, "0A81")] + [InlineData(PublicEncodingRules.CER, "0A81")] + [InlineData(PublicEncodingRules.DER, "0A81")] + [InlineData(PublicEncodingRules.BER, "9F00")] + [InlineData(PublicEncodingRules.CER, "9F00")] + [InlineData(PublicEncodingRules.DER, "9F00")] + [InlineData(PublicEncodingRules.BER, "0A02007F")] + [InlineData(PublicEncodingRules.CER, "0A02007F")] + [InlineData(PublicEncodingRules.DER, "0A02007F")] + [InlineData(PublicEncodingRules.BER, "0A020102")] + [InlineData(PublicEncodingRules.CER, "0A020102")] + [InlineData(PublicEncodingRules.DER, "0A020102")] + [InlineData(PublicEncodingRules.BER, "0A02FF80")] + [InlineData(PublicEncodingRules.CER, "0A02FF80")] + [InlineData(PublicEncodingRules.DER, "0A02FF80")] + [InlineData(PublicEncodingRules.BER, "0A03010203")] + [InlineData(PublicEncodingRules.CER, "0A03010203")] + [InlineData(PublicEncodingRules.DER, "0A03010203")] + [InlineData(PublicEncodingRules.BER, "0A0401020304")] + [InlineData(PublicEncodingRules.CER, "0A0401020304")] + [InlineData(PublicEncodingRules.DER, "0A0401020304")] + [InlineData(PublicEncodingRules.BER, "0A050102030405")] + [InlineData(PublicEncodingRules.CER, "0A050102030405")] + [InlineData(PublicEncodingRules.DER, "0A050102030405")] + [InlineData(PublicEncodingRules.BER, "0A080102030405060708")] + [InlineData(PublicEncodingRules.CER, "0A080102030405060708")] + [InlineData(PublicEncodingRules.DER, "0A080102030405060708")] + [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.BER, "2A030A0100")] + public static void GetEnumeratedValue_Invalid_SByte(PublicEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws(() => reader.GetEnumeratedValue()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "")] + [InlineData(PublicEncodingRules.CER, "")] + [InlineData(PublicEncodingRules.DER, "")] + [InlineData(PublicEncodingRules.BER, "0A")] + [InlineData(PublicEncodingRules.CER, "0A")] + [InlineData(PublicEncodingRules.DER, "0A")] + [InlineData(PublicEncodingRules.BER, "0A00")] + [InlineData(PublicEncodingRules.CER, "0A00")] + [InlineData(PublicEncodingRules.DER, "0A00")] + [InlineData(PublicEncodingRules.BER, "0A01")] + [InlineData(PublicEncodingRules.CER, "0A01")] + [InlineData(PublicEncodingRules.DER, "0A01")] + [InlineData(PublicEncodingRules.BER, "0A81")] + [InlineData(PublicEncodingRules.CER, "0A81")] + [InlineData(PublicEncodingRules.DER, "0A81")] + [InlineData(PublicEncodingRules.BER, "9F00")] + [InlineData(PublicEncodingRules.CER, "9F00")] + [InlineData(PublicEncodingRules.DER, "9F00")] + [InlineData(PublicEncodingRules.BER, "0A02007F")] + [InlineData(PublicEncodingRules.CER, "0A02007F")] + [InlineData(PublicEncodingRules.DER, "0A02007F")] + [InlineData(PublicEncodingRules.BER, "0A02FF80")] + [InlineData(PublicEncodingRules.CER, "0A02FF80")] + [InlineData(PublicEncodingRules.DER, "0A02FF80")] + [InlineData(PublicEncodingRules.BER, "0A03010203")] + [InlineData(PublicEncodingRules.CER, "0A03010203")] + [InlineData(PublicEncodingRules.DER, "0A03010203")] + [InlineData(PublicEncodingRules.BER, "0A0401020304")] + [InlineData(PublicEncodingRules.CER, "0A0401020304")] + [InlineData(PublicEncodingRules.DER, "0A0401020304")] + [InlineData(PublicEncodingRules.BER, "0A050102030405")] + [InlineData(PublicEncodingRules.CER, "0A050102030405")] + [InlineData(PublicEncodingRules.DER, "0A050102030405")] + [InlineData(PublicEncodingRules.BER, "0A080102030405060708")] + [InlineData(PublicEncodingRules.CER, "0A080102030405060708")] + [InlineData(PublicEncodingRules.DER, "0A080102030405060708")] + [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.BER, "2A030A0100")] + public static void GetEnumeratedValue_Invalid_Short(PublicEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws(() => reader.GetEnumeratedValue()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "")] + [InlineData(PublicEncodingRules.CER, "")] + [InlineData(PublicEncodingRules.DER, "")] + [InlineData(PublicEncodingRules.BER, "0A")] + [InlineData(PublicEncodingRules.CER, "0A")] + [InlineData(PublicEncodingRules.DER, "0A")] + [InlineData(PublicEncodingRules.BER, "0A00")] + [InlineData(PublicEncodingRules.CER, "0A00")] + [InlineData(PublicEncodingRules.DER, "0A00")] + [InlineData(PublicEncodingRules.BER, "0A01")] + [InlineData(PublicEncodingRules.CER, "0A01")] + [InlineData(PublicEncodingRules.DER, "0A01")] + [InlineData(PublicEncodingRules.BER, "0A81")] + [InlineData(PublicEncodingRules.CER, "0A81")] + [InlineData(PublicEncodingRules.DER, "0A81")] + [InlineData(PublicEncodingRules.BER, "9F00")] + [InlineData(PublicEncodingRules.CER, "9F00")] + [InlineData(PublicEncodingRules.DER, "9F00")] + [InlineData(PublicEncodingRules.BER, "0A01FF")] + [InlineData(PublicEncodingRules.CER, "0A01FF")] + [InlineData(PublicEncodingRules.DER, "0A01FF")] + [InlineData(PublicEncodingRules.BER, "0A02007F")] + [InlineData(PublicEncodingRules.CER, "0A02007F")] + [InlineData(PublicEncodingRules.DER, "0A02007F")] + [InlineData(PublicEncodingRules.BER, "0A02FF80")] + [InlineData(PublicEncodingRules.CER, "0A02FF80")] + [InlineData(PublicEncodingRules.DER, "0A02FF80")] + [InlineData(PublicEncodingRules.BER, "0A03010203")] + [InlineData(PublicEncodingRules.CER, "0A03010203")] + [InlineData(PublicEncodingRules.DER, "0A03010203")] + [InlineData(PublicEncodingRules.BER, "0A0401020304")] + [InlineData(PublicEncodingRules.CER, "0A0401020304")] + [InlineData(PublicEncodingRules.DER, "0A0401020304")] + [InlineData(PublicEncodingRules.BER, "0A050102030405")] + [InlineData(PublicEncodingRules.CER, "0A050102030405")] + [InlineData(PublicEncodingRules.DER, "0A050102030405")] + [InlineData(PublicEncodingRules.BER, "0A080102030405060708")] + [InlineData(PublicEncodingRules.CER, "0A080102030405060708")] + [InlineData(PublicEncodingRules.DER, "0A080102030405060708")] + [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.BER, "2A030A0100")] + public static void GetEnumeratedValue_Invalid_UShort(PublicEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws(() => reader.GetEnumeratedValue()); + } + + + [Theory] + [InlineData(PublicEncodingRules.BER, "")] + [InlineData(PublicEncodingRules.CER, "")] + [InlineData(PublicEncodingRules.DER, "")] + [InlineData(PublicEncodingRules.BER, "0A")] + [InlineData(PublicEncodingRules.CER, "0A")] + [InlineData(PublicEncodingRules.DER, "0A")] + [InlineData(PublicEncodingRules.BER, "0A00")] + [InlineData(PublicEncodingRules.CER, "0A00")] + [InlineData(PublicEncodingRules.DER, "0A00")] + [InlineData(PublicEncodingRules.BER, "0A01")] + [InlineData(PublicEncodingRules.CER, "0A01")] + [InlineData(PublicEncodingRules.DER, "0A01")] + [InlineData(PublicEncodingRules.BER, "0A81")] + [InlineData(PublicEncodingRules.CER, "0A81")] + [InlineData(PublicEncodingRules.DER, "0A81")] + [InlineData(PublicEncodingRules.BER, "9F00")] + [InlineData(PublicEncodingRules.CER, "9F00")] + [InlineData(PublicEncodingRules.DER, "9F00")] + [InlineData(PublicEncodingRules.BER, "0A02007F")] + [InlineData(PublicEncodingRules.CER, "0A02007F")] + [InlineData(PublicEncodingRules.DER, "0A02007F")] + [InlineData(PublicEncodingRules.BER, "0A02FF80")] + [InlineData(PublicEncodingRules.CER, "0A02FF80")] + [InlineData(PublicEncodingRules.DER, "0A02FF80")] + [InlineData(PublicEncodingRules.BER, "0A050102030405")] + [InlineData(PublicEncodingRules.CER, "0A050102030405")] + [InlineData(PublicEncodingRules.DER, "0A050102030405")] + [InlineData(PublicEncodingRules.BER, "0A080102030405060708")] + [InlineData(PublicEncodingRules.CER, "0A080102030405060708")] + [InlineData(PublicEncodingRules.DER, "0A080102030405060708")] + [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.BER, "2A030A0100")] + public static void GetEnumeratedValue_Invalid_Int(PublicEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws(() => reader.GetEnumeratedValue()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "")] + [InlineData(PublicEncodingRules.CER, "")] + [InlineData(PublicEncodingRules.DER, "")] + [InlineData(PublicEncodingRules.BER, "0A")] + [InlineData(PublicEncodingRules.CER, "0A")] + [InlineData(PublicEncodingRules.DER, "0A")] + [InlineData(PublicEncodingRules.BER, "0A00")] + [InlineData(PublicEncodingRules.CER, "0A00")] + [InlineData(PublicEncodingRules.DER, "0A00")] + [InlineData(PublicEncodingRules.BER, "0A01")] + [InlineData(PublicEncodingRules.CER, "0A01")] + [InlineData(PublicEncodingRules.DER, "0A01")] + [InlineData(PublicEncodingRules.BER, "0A81")] + [InlineData(PublicEncodingRules.CER, "0A81")] + [InlineData(PublicEncodingRules.DER, "0A81")] + [InlineData(PublicEncodingRules.BER, "9F00")] + [InlineData(PublicEncodingRules.CER, "9F00")] + [InlineData(PublicEncodingRules.DER, "9F00")] + [InlineData(PublicEncodingRules.BER, "0A01FF")] + [InlineData(PublicEncodingRules.CER, "0A01FF")] + [InlineData(PublicEncodingRules.DER, "0A01FF")] + [InlineData(PublicEncodingRules.BER, "0A02007F")] + [InlineData(PublicEncodingRules.CER, "0A02007F")] + [InlineData(PublicEncodingRules.DER, "0A02007F")] + [InlineData(PublicEncodingRules.BER, "0A050102030405")] + [InlineData(PublicEncodingRules.CER, "0A050102030405")] + [InlineData(PublicEncodingRules.DER, "0A050102030405")] + [InlineData(PublicEncodingRules.BER, "0A080102030405060708")] + [InlineData(PublicEncodingRules.CER, "0A080102030405060708")] + [InlineData(PublicEncodingRules.DER, "0A080102030405060708")] + [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.BER, "2A030A0100")] + public static void GetEnumeratedValue_Invalid_UInt(PublicEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws(() => reader.GetEnumeratedValue()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "")] + [InlineData(PublicEncodingRules.CER, "")] + [InlineData(PublicEncodingRules.DER, "")] + [InlineData(PublicEncodingRules.BER, "0A")] + [InlineData(PublicEncodingRules.CER, "0A")] + [InlineData(PublicEncodingRules.DER, "0A")] + [InlineData(PublicEncodingRules.BER, "0A00")] + [InlineData(PublicEncodingRules.CER, "0A00")] + [InlineData(PublicEncodingRules.DER, "0A00")] + [InlineData(PublicEncodingRules.BER, "0A01")] + [InlineData(PublicEncodingRules.CER, "0A01")] + [InlineData(PublicEncodingRules.DER, "0A01")] + [InlineData(PublicEncodingRules.BER, "0A81")] + [InlineData(PublicEncodingRules.CER, "0A81")] + [InlineData(PublicEncodingRules.DER, "0A81")] + [InlineData(PublicEncodingRules.BER, "9F00")] + [InlineData(PublicEncodingRules.CER, "9F00")] + [InlineData(PublicEncodingRules.DER, "9F00")] + [InlineData(PublicEncodingRules.BER, "0A02007F")] + [InlineData(PublicEncodingRules.CER, "0A02007F")] + [InlineData(PublicEncodingRules.DER, "0A02007F")] + [InlineData(PublicEncodingRules.BER, "0A02FF80")] + [InlineData(PublicEncodingRules.CER, "0A02FF80")] + [InlineData(PublicEncodingRules.DER, "0A02FF80")] + [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.BER, "2A030A0100")] + public static void GetEnumeratedValue_Invalid_Long(PublicEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws(() => reader.GetEnumeratedValue()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "")] + [InlineData(PublicEncodingRules.CER, "")] + [InlineData(PublicEncodingRules.DER, "")] + [InlineData(PublicEncodingRules.BER, "0A")] + [InlineData(PublicEncodingRules.CER, "0A")] + [InlineData(PublicEncodingRules.DER, "0A")] + [InlineData(PublicEncodingRules.BER, "0A00")] + [InlineData(PublicEncodingRules.CER, "0A00")] + [InlineData(PublicEncodingRules.DER, "0A00")] + [InlineData(PublicEncodingRules.BER, "0A01")] + [InlineData(PublicEncodingRules.CER, "0A01")] + [InlineData(PublicEncodingRules.DER, "0A01")] + [InlineData(PublicEncodingRules.BER, "0A81")] + [InlineData(PublicEncodingRules.CER, "0A81")] + [InlineData(PublicEncodingRules.DER, "0A81")] + [InlineData(PublicEncodingRules.BER, "9F00")] + [InlineData(PublicEncodingRules.CER, "9F00")] + [InlineData(PublicEncodingRules.DER, "9F00")] + [InlineData(PublicEncodingRules.BER, "0A01FF")] + [InlineData(PublicEncodingRules.CER, "0A01FF")] + [InlineData(PublicEncodingRules.DER, "0A01FF")] + [InlineData(PublicEncodingRules.BER, "0A02007F")] + [InlineData(PublicEncodingRules.CER, "0A02007F")] + [InlineData(PublicEncodingRules.DER, "0A02007F")] + [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] + [InlineData(PublicEncodingRules.BER, "2A030A0100")] + public static void GetEnumeratedValue_Invalid_ULong(PublicEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws(() => reader.GetEnumeratedValue()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void GetEnumeratedValue_NonEnumType(PublicEncodingRules ruleSet) + { + byte[] data = { 0x0A, 0x01, 0x00 }; + AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + + Assert.Throws(() => reader.GetEnumeratedValue()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void GetEnumeratedValue_FlagsEnum(PublicEncodingRules ruleSet) + { + byte[] data = { 0x0A, 0x01, 0x00 }; + AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "tEnum", + () => reader.GetEnumeratedValue()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void GetEnumeratedBytes(PublicEncodingRules ruleSet) + { + const string Payload = "0102030405060708090A0B0C0D0E0F10"; + + // ENUMERATED (payload) followed by INTEGER (0) + byte[] data = ("0A10" + Payload + "020100").HexToByteArray(); + AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + + ReadOnlyMemory contents = reader.GetEnumeratedBytes(); + Assert.Equal(0x10, contents.Length); + Assert.Equal(Payload, contents.ByteArrayToHex()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "")] + [InlineData(PublicEncodingRules.CER, "")] + [InlineData(PublicEncodingRules.DER, "")] + [InlineData(PublicEncodingRules.BER, "0A")] + [InlineData(PublicEncodingRules.CER, "0A")] + [InlineData(PublicEncodingRules.DER, "0A")] + [InlineData(PublicEncodingRules.BER, "0A00")] + [InlineData(PublicEncodingRules.CER, "0A00")] + [InlineData(PublicEncodingRules.DER, "0A00")] + [InlineData(PublicEncodingRules.BER, "0A01")] + [InlineData(PublicEncodingRules.CER, "0A01")] + [InlineData(PublicEncodingRules.DER, "0A01")] + [InlineData(PublicEncodingRules.BER, "010100")] + [InlineData(PublicEncodingRules.CER, "010100")] + [InlineData(PublicEncodingRules.DER, "010100")] + [InlineData(PublicEncodingRules.BER, "9F00")] + [InlineData(PublicEncodingRules.CER, "9F00")] + [InlineData(PublicEncodingRules.DER, "9F00")] + [InlineData(PublicEncodingRules.BER, "0A81")] + [InlineData(PublicEncodingRules.CER, "0A81")] + [InlineData(PublicEncodingRules.DER, "0A81")] + public static void GetEnumeratedBytes_Throws(PublicEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws(() => reader.GetEnumeratedBytes()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) + { + byte[] inputData = { 0x0A, 1, 0x7E }; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.GetEnumeratedValue(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.GetEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + ShortBacked value = reader.GetEnumeratedValue(); + Assert.Equal((ShortBacked)0x7E, value); + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) + { + byte[] inputData = { 0x87, 2, 0, 0x80 }; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.GetEnumeratedValue(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.GetEnumeratedValue()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.GetEnumeratedValue(new Asn1Tag(TagClass.Application, 0))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.GetEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 1))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + ShortBacked value = reader.GetEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 7)); + Assert.Equal((ShortBacked)0x80, value); + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "0A01FF", PublicTagClass.Universal, 10)] + [InlineData(PublicEncodingRules.CER, "0A01FF", PublicTagClass.Universal, 10)] + [InlineData(PublicEncodingRules.DER, "0A01FF", PublicTagClass.Universal, 10)] + [InlineData(PublicEncodingRules.BER, "8001FF", PublicTagClass.ContextSpecific, 0)] + [InlineData(PublicEncodingRules.CER, "4C01FF", PublicTagClass.Application, 12)] + [InlineData(PublicEncodingRules.DER, "DF8A4601FF", PublicTagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + PublicEncodingRules ruleSet, + string inputHex, + PublicTagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + ShortBacked val1 = reader.GetEnumeratedValue(new Asn1Tag((TagClass)tagClass, tagValue, true)); + Assert.False(reader.HasData); + reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + ShortBacked val2 = reader.GetEnumeratedValue(new Asn1Tag((TagClass)tagClass, tagValue, false)); + Assert.False(reader.HasData); + + Assert.Equal(val1, val2); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadGeneralizedTime.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadGeneralizedTime.cs new file mode 100644 index 0000000000..6798b068a4 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadGeneralizedTime.cs @@ -0,0 +1,544 @@ +// 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.Linq; +using System.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public sealed class ReadGeneralizedTime : Asn1ReaderTests + { + [Theory] + // yyyyMMddHH (2017090821) + [InlineData(PublicEncodingRules.BER, "180A32303137303930383231", 2017, 9, 8, 21, 0, 0, 0, null, 0)] + // yyyyMMddHHZ (2017090821Z) + [InlineData(PublicEncodingRules.BER, "180B323031373039303832315A", 2017, 9, 8, 21, 0, 0, 0, 0, 0)] + // yyyyMMddHH-HH (2017090821-01) + [InlineData(PublicEncodingRules.BER, "180D323031373039303832312D3031", 2017, 9, 8, 21, 0, 0, 0, -1, 0)] + // yyyyMMddHH+HHmm (2017090821+0118) + [InlineData(PublicEncodingRules.BER, "180F323031373039303832312B30313138", 2017, 9, 8, 21, 0, 0, 0, 1, 18)] + // yyyyMMddHH,hourFrac (2017090821.1) + [InlineData(PublicEncodingRules.BER, "180C323031373039303832312C31", 2017, 9, 8, 21, 6, 0, 0, null, 0)] + // yyyyMMddHH.hourFracZ (2017090821.2010Z) + [InlineData(PublicEncodingRules.BER, "1810323031373039303832312E323031305A", 2017, 9, 8, 21, 12, 3, 600, 0, 0)] + // yyyyMMddHH,hourFrac-HH (2017090821,3099-01) + [InlineData(PublicEncodingRules.BER, "1812323031373039303832312C333039392D3031", 2017, 9, 8, 21, 18, 35, 640, -1, 0)] + // yyyyMMddHH.hourFrac+HHmm (2017090821.201+0118) + [InlineData(PublicEncodingRules.BER, "1813323031373039303832312E3230312B30313138", 2017, 9, 8, 21, 12, 3, 600, 1, 18)] + // yyyyMMddHHmm (201709082358) + [InlineData(PublicEncodingRules.BER, "180C323031373039303832333538", 2017, 9, 8, 23, 58, 0, 0, null, 0)] + // yyyyMMddHHmmZ (201709082358Z) + [InlineData(PublicEncodingRules.BER, "180D3230313730393038323335385A", 2017, 9, 8, 23, 58, 0, 0, 0, 0)] + // yyyyMMddHHmm-HH (201709082358-01) + [InlineData(PublicEncodingRules.BER, "180F3230313730393038323335382D3031", 2017, 9, 8, 23, 58, 0, 0, -1, 0)] + // yyyyMMddHHmm+HHmm (201709082358+0118) + [InlineData(PublicEncodingRules.BER, "18113230313730393038323335382B30313138", 2017, 9, 8, 23, 58, 0, 0, 1, 18)] + // yyyyMMddHHmm.minuteFrac (201709082358.01) + [InlineData(PublicEncodingRules.BER, "180F3230313730393038323335382E3031", 2017, 9, 8, 23, 58, 0, 600, null, 0)] + // yyyyMMddHHmm,minuteFracZ (201709082358,11Z) + [InlineData(PublicEncodingRules.BER, "18103230313730393038323335382C31315A", 2017, 9, 8, 23, 58, 6, 600, 0, 0)] + // yyyyMMddHHmm.minuteFrac-HH (201709082358.05-01) + [InlineData(PublicEncodingRules.BER, "18123230313730393038323335382E30352D3031", 2017, 9, 8, 23, 58, 3, 0, -1, 0)] + // yyyyMMddHHmm,minuteFrac+HHmm (201709082358,007+0118) + [InlineData(PublicEncodingRules.BER, "18153230313730393038323335382C3030372B30313138", 2017, 9, 8, 23, 58, 0, 420, 1, 18)] + // yyyyMMddHHmmss (20161106012345) - Ambiguous time due to DST "fall back" in US & Canada + [InlineData(PublicEncodingRules.BER, "180E3230313631313036303132333435", 2016, 11, 6, 1, 23, 45, 0, null, 0)] + // yyyyMMddHHmmssZ (20161106012345Z) + [InlineData(PublicEncodingRules.BER, "180F32303136313130363031323334355A", 2016, 11, 6, 1, 23, 45, 0, 0, 0)] + // yyyyMMddHHmmss-HH (20161106012345-01) + [InlineData(PublicEncodingRules.BER, "181132303136313130363031323334352D3031", 2016, 11, 6, 1, 23, 45, 0, -1, 0)] + // yyyyMMddHHmmss+HHmm (20161106012345+0118) + [InlineData(PublicEncodingRules.BER, "181332303136313130363031323334352B30313138", 2016, 11, 6, 1, 23, 45, 0, 1, 18)] + // yyyyMMddHHmmss.secondFrac (20161106012345.6789) - Ambiguous time due to DST "fall back" in US & Canada + [InlineData(PublicEncodingRules.BER, "181332303136313130363031323334352E36373839", 2016, 11, 6, 1, 23, 45, 678, null, 0)] + // yyyyMMddHHmmss,secondFracZ (20161106012345,7654Z) + [InlineData(PublicEncodingRules.BER, "181432303136313130363031323334352C373635345A", 2016, 11, 6, 1, 23, 45, 765, 0, 0)] + // yyyyMMddHHmmss.secondFrac-HH (20161106012345.001-01) + [InlineData(PublicEncodingRules.BER, "181532303136313130363031323334352E3030312D3031", 2016, 11, 6, 1, 23, 45, 1, -1, 0)] + // yyyyMMddHHmmss,secondFrac+HHmm (20161106012345,0009+0118) + [InlineData(PublicEncodingRules.BER, "181832303136313130363031323334352C303030392B30313138", 2016, 11, 6, 1, 23, 45, 0, 1, 18)] + + // yyyyMMddHHmmssZ (20161106012345Z) + [InlineData(PublicEncodingRules.CER, "180F32303136313130363031323334355A", 2016, 11, 6, 1, 23, 45, 0, 0, 0)] + [InlineData(PublicEncodingRules.DER, "180F32303136313130363031323334355A", 2016, 11, 6, 1, 23, 45, 0, 0, 0)] + + // yyyyMMddHHmmss.secondFracZ (20161106012345,7654Z) + [InlineData(PublicEncodingRules.CER, "181432303136313130363031323334352E373635345A", 2016, 11, 6, 1, 23, 45, 765, 0, 0)] + [InlineData(PublicEncodingRules.DER, "181432303136313130363031323334352E373635345A", 2016, 11, 6, 1, 23, 45, 765, 0, 0)] + public static void ParseTime_Valid( + PublicEncodingRules ruleSet, + string inputHex, + int year, + int month, + int day, + int hour, + int minute, + int second, + int millisecond, + int? offsetHour, + int offsetMinute) + { + byte[] inputData = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + DateTimeOffset value = reader.GetGeneralizedTime(); + Assert.False(reader.HasData, "reader.HasData"); + + Assert.Equal(year, value.Year); + Assert.Equal(month, value.Month); + Assert.Equal(day, value.Day); + Assert.Equal(hour, value.Hour); + Assert.Equal(minute, value.Minute); + Assert.Equal(second, value.Second); + Assert.Equal(millisecond, value.Millisecond); + + TimeSpan timeOffset; + + if (offsetHour == null) + { + // Ask the system what offset it thinks was relevant for that time. + // Includes DST ambiguity. + timeOffset = new DateTimeOffset(value.LocalDateTime).Offset; + } + else + { + timeOffset = new TimeSpan(offsetHour.Value, offsetMinute, 0); + } + + Assert.Equal(timeOffset, value.Offset); + } + + [Theory] + // yyyyMMddHH (2017090821) + [InlineData("180A32303137303930383231")] + // yyyyMMddHHZ (2017090821Z) + [InlineData("180B323031373039303832315A")] + // yyyyMMddHH-HH (2017090821-01) + [InlineData("180D323031373039303832312D3031")] + // yyyyMMddHH+HHmm (2017090821+0118) + [InlineData("180F323031373039303832312B30313138")] + // yyyyMMddHH,hourFrac (2017090821,1) + [InlineData("180C323031373039303832312C31")] + // yyyyMMddHH.hourFrac (2017090821.1) + [InlineData("180C323031373039303832312E31")] + // yyyyMMddHH,hourFracZ (2017090821,2010Z) + [InlineData("1810323031373039303832312C323031305A")] + // yyyyMMddHH.hourFracZ (2017090821.2010Z) + [InlineData("1810323031373039303832312E323031305A")] + // yyyyMMddHH,hourFrac-HH (2017090821,3099-01) + [InlineData("1812323031373039303832312C333039392D3031")] + // yyyyMMddHH.hourFrac-HH (2017090821.3099-01) + [InlineData("1812323031373039303832312E333039392D3031")] + // yyyyMMddHH,hourFrac+HHmm (2017090821,201+0118) + [InlineData("1813323031373039303832312C3230312B30313138")] + // yyyyMMddHH.hourFrac+HHmm (2017090821.201+0118) + [InlineData("1813323031373039303832312E3230312B30313138")] + // yyyyMMddHHmm (201709082358) + [InlineData("180C323031373039303832333538")] + // yyyyMMddHHmmZ (201709082358Z) + [InlineData("180D3230313730393038323335385A")] + // yyyyMMddHHmm-HH (201709082358-01) + [InlineData("180F3230313730393038323335382D3031")] + // yyyyMMddHHmm+HHmm (201709082358+0118) + [InlineData("18113230313730393038323335382B30313138")] + // yyyyMMddHHmm,minuteFrac (201709082358,01) + [InlineData("180F3230313730393038323335382C3031")] + // yyyyMMddHHmm.minuteFrac (201709082358.01) + [InlineData("180F3230313730393038323335382E3031")] + // yyyyMMddHHmm,minuteFracZ (201709082358,11Z) + [InlineData("18103230313730393038323335382C31315A")] + // yyyyMMddHHmm.minuteFracZ (201709082358.11Z) + [InlineData("18103230313730393038323335382E31315A")] + // yyyyMMddHHmm,minuteFrac-HH (201709082358m05-01) + [InlineData("18123230313730393038323335382C30352D3031")] + // yyyyMMddHHmm.minuteFrac-HH (201709082358.05-01) + [InlineData("18123230313730393038323335382E30352D3031")] + // yyyyMMddHHmm,minuteFrac+HHmm (201709082358,007+0118) + [InlineData("18153230313730393038323335382C3030372B30313138")] + // yyyyMMddHHmm.minuteFrac+HHmm (201709082358.007+0118) + [InlineData("18153230313730393038323335382E3030372B30313138")] + // yyyyMMddHHmmss (20161106012345) + [InlineData("180E3230313631313036303132333435")] + // yyyyMMddHHmmss-HH (20161106012345-01) + [InlineData("181132303136313130363031323334352D3031")] + // yyyyMMddHHmmss+HHmm (20161106012345+0118) + [InlineData("181332303136313130363031323334352B30313138")] + // yyyyMMddHHmmss,secondFrac (20161106012345,6789) + [InlineData("181332303136313130363031323334352C36373839")] + // yyyyMMddHHmmss.secondFrac (20161106012345.6789) + [InlineData("181332303136313130363031323334352E36373839")] + // yyyyMMddHHmmss,secondFracZ (20161106012345,7654Z) + [InlineData("181432303136313130363031323334352C373635345A")] + // yyyyMMddHHmmss,secondFrac-HH (20161106012345,001-01) + [InlineData("181532303136313130363031323334352C3030312D3031")] + // yyyyMMddHHmmss.secondFrac-HH (20161106012345.001-01) + [InlineData("181532303136313130363031323334352E3030312D3031")] + // yyyyMMddHHmmss,secondFrac+HHmm (20161106012345,0009+0118) + [InlineData("181832303136313130363031323334352C303030392B30313138")] + // yyyyMMddHHmmss.secondFrac+HHmm (20161106012345.0009+0118) + [InlineData("181832303136313130363031323334352E303030392B30313138")] + // yyyyMMddHHmmss.secondFrac0Z (20161106012345.76540Z) + [InlineData("181532303136313130363031323334352E37363534305A")] + // Constructed encoding of yyyyMMddHHmmssZ + [InlineData( + "3880" + + "040432303136" + + "04023131" + + "0403303630" + + "040131" + + "0405323334355A" + + "0000")] + public static void ParseTime_BerOnly(string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader cerReader = new AsnReader(inputData, AsnEncodingRules.CER); + AsnReader derReader = new AsnReader(inputData, AsnEncodingRules.DER); + + Assert.Throws(() => cerReader.GetGeneralizedTime()); + Assert.Throws(() => derReader.GetGeneralizedTime()); + + // Prove it was not just corrupt input + AsnReader berReader = new AsnReader(inputData, AsnEncodingRules.BER); + berReader.GetGeneralizedTime(); + Assert.False(berReader.HasData, "berReader.HasData"); + Assert.True(cerReader.HasData, "cerReader.HasData"); + Assert.True(derReader.HasData, "derReader.HasData"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "2017121900.06861111087Z")] + [InlineData(PublicEncodingRules.BER, "201712190004.11666665167Z")] + [InlineData(PublicEncodingRules.BER, "201712190004.11666665167Z")] + [InlineData(PublicEncodingRules.BER, "20171219000406.9999991Z")] + [InlineData(PublicEncodingRules.CER, "20171219000406.9999991Z")] + [InlineData(PublicEncodingRules.DER, "20171219000406.9999991Z")] + public static void MaximumEffectivePrecision(PublicEncodingRules ruleSet, string dateAscii) + { + DateTimeOffset expectedTime = new DateTimeOffset(2017, 12, 19, 0, 4, 6, TimeSpan.Zero); + expectedTime += new TimeSpan(TimeSpan.TicksPerSecond - 9); + + byte[] inputData = new byte[dateAscii.Length + 2]; + inputData[0] = 0x18; + inputData[1] = (byte)dateAscii.Length; + Text.Encoding.ASCII.GetBytes(dateAscii, 0, dateAscii.Length, inputData, 2); + + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + Assert.Equal(expectedTime, reader.GetGeneralizedTime()); + } + + [Fact] + public static void ExcessivelyPreciseFraction() + { + byte[] inputData = Text.Encoding.ASCII.GetBytes("\u0018\u002A2017092118.012345678901234567890123456789Z"); + + AsnReader berReader = new AsnReader(inputData, AsnEncodingRules.BER); + DateTimeOffset value = berReader.GetGeneralizedTime(); + Assert.False(berReader.HasData, "berReader.HasData"); + + DateTimeOffset expected = new DateTimeOffset(2017, 9, 21, 18, 0, 44, 444, TimeSpan.Zero); + expected += new TimeSpan(4440); + + Assert.Equal(expected, value); + } + + [Fact] + public static void ExcessivelyPreciseFraction_OneTenthPlusEpsilon() + { + byte[] inputData = Text.Encoding.ASCII.GetBytes("\u0018\u002A20170921180044.10000000000000000000000001Z"); + + AsnReader derReader = new AsnReader(inputData, AsnEncodingRules.DER); + DateTimeOffset value = derReader.GetGeneralizedTime(); + Assert.False(derReader.HasData, "derReader.HasData"); + + DateTimeOffset expected = new DateTimeOffset(2017, 9, 21, 18, 0, 44, 100, TimeSpan.Zero); + + Assert.Equal(expected, value); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + public static void MultiSegmentExcessivelyPreciseFraction(PublicEncodingRules ruleSet) + { + // This builds "20171207173522.0000...0001Z" where the Z required a second CER segment. + // This is a bit of nonsense, really, because it is encoding 1e-985 seconds, which is + // oodles of orders of magnitude smaller than Planck time (~5e-44). + // But, the spec says "any number of decimal places", and 985 is a number. + + // A0 80 (context specifc 0, constructed, indefinite length) + // 04 82 03 E8 (octet string, primitive, 1000 bytes) + // ASCII("20171207173522." + new string('0', 984) + '1') + // 04 01 (octet string, primitive, 1 byte) + // ASCII("Z") + // 00 00 (end of contents) + // + // 1001 content bytes + 10 bytes of structure. + byte[] header = "A080048203E8".HexToByteArray(); + byte[] contents0 = Text.Encoding.ASCII.GetBytes("20171207173522." + new string('0', 984) + "1"); + byte[] cdr = { 0x04, 0x01, (byte)'Z', 0x00, 0x00 }; + byte[] inputData = header.Concat(contents0).Concat(cdr).ToArray(); + + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + DateTimeOffset value = reader.GetGeneralizedTime(new Asn1Tag(TagClass.ContextSpecific, 0)); + DateTimeOffset expected = new DateTimeOffset(2017, 12, 7, 17, 35, 22, TimeSpan.Zero); + Assert.Equal(expected, value); + } + + [Fact] + public static void ExcessivelyPreciseFraction_OneTenthPlusEpsilonAndZero() + { + byte[] inputData = Text.Encoding.ASCII.GetBytes("\u0018\u002A20170921180044.10000000000000000000000010Z"); + + AsnReader berReader = new AsnReader(inputData, AsnEncodingRules.BER); + DateTimeOffset value = berReader.GetGeneralizedTime(); + Assert.False(berReader.HasData, "berReader.HasData"); + + DateTimeOffset expected = new DateTimeOffset(2017, 9, 21, 18, 0, 44, 100, TimeSpan.Zero); + Assert.Equal(expected, value); + + AsnReader cerReader = new AsnReader(inputData, AsnEncodingRules.CER); + AsnReader derReader = new AsnReader(inputData, AsnEncodingRules.DER); + Assert.Throws(() => cerReader.GetGeneralizedTime()); + Assert.Throws(() => derReader.GetGeneralizedTime()); + } + + [Fact] + public static void ExcessivelyPreciseNonFraction() + { + byte[] inputData = Text.Encoding.ASCII.GetBytes("\u0018\u002A2017092118.012345678901234567890123Q56789Z"); + AsnReader berReader = new AsnReader(inputData, AsnEncodingRules.BER); + + Assert.Throws(() => berReader.GetGeneralizedTime()); + } + + [Theory] + // yyyyMMddHH,hourFrac (2017090821,1) + [InlineData("180C323031373039303832312C31")] + // yyyyMMddHH.hourFrac (2017090821.1) + [InlineData("180C323031373039303832312E31")] + // yyyyMMddHH,hourFracZ (2017090821,2010Z) + [InlineData("1810323031373039303832312C323031305A")] + // yyyyMMddHH.hourFracZ (2017090821.2010Z) + [InlineData("1810323031373039303832312E323031305A")] + // yyyyMMddHH,hourFrac-HH (2017090821,3099-01) + [InlineData("1812323031373039303832312C333039392D3031")] + // yyyyMMddHH.hourFrac-HH (2017090821.3099-01) + [InlineData("1812323031373039303832312E333039392D3031")] + // yyyyMMddHH,hourFrac+HHmm (2017090821,201+0118) + [InlineData("1813323031373039303832312C3230312B30313138")] + // yyyyMMddHH.hourFrac+HHmm (2017090821.201+0118) + [InlineData("1813323031373039303832312E3230312B30313138")] + // yyyyMMddHHmm,minuteFrac (201709082358,01) + [InlineData("180F3230313730393038323335382C3031")] + // yyyyMMddHHmm.minuteFrac (201709082358.01) + [InlineData("180F3230313730393038323335382E3031")] + // yyyyMMddHHmm,minuteFracZ (201709082358,11Z) + [InlineData("18103230313730393038323335382C31315A")] + // yyyyMMddHHmm.minuteFracZ (201709082358.11Z) + [InlineData("18103230313730393038323335382E31315A")] + // yyyyMMddHHmm,minuteFrac-HH (201709082358m05-01) + [InlineData("18123230313730393038323335382C30352D3031")] + // yyyyMMddHHmm.minuteFrac-HH (201709082358.05-01) + [InlineData("18123230313730393038323335382E30352D3031")] + // yyyyMMddHHmm,minuteFrac+HHmm (201709082358,007+0118) + [InlineData("18153230313730393038323335382C3030372B30313138")] + // yyyyMMddHHmm.minuteFrac+HHmm (201709082358.007+0118) + [InlineData("18153230313730393038323335382E3030372B30313138")] + // yyyyMMddHHmmss,secondFrac (20161106012345,6789) + [InlineData("181332303136313130363031323334352C36373839")] + // yyyyMMddHHmmss.secondFrac (20161106012345.6789) + [InlineData("181332303136313130363031323334352E36373839")] + // yyyyMMddHHmmss,secondFracZ (20161106012345,7654Z) + [InlineData("181432303136313130363031323334352C373635345A")] + // yyyyMMddHHmmss,secondFrac-HH (20161106012345,001-01) + [InlineData("181532303136313130363031323334352C3030312D3031")] + // yyyyMMddHHmmss.secondFrac-HH (20161106012345.001-01) + [InlineData("181532303136313130363031323334352E3030312D3031")] + // yyyyMMddHHmmss,secondFrac+HHmm (20161106012345,0009+0118) + [InlineData("181832303136313130363031323334352C303030392B30313138")] + // yyyyMMddHHmmss.secondFrac+HHmm (20161106012345.0009+0118) + [InlineData("181832303136313130363031323334352E303030392B30313138")] + // yyyyMMddHHmmss.secondFrac0Z (20161106012345.76540Z) + [InlineData("181532303136313130363031323334352E37363534305A")] + public static void VerifyDisallowFraction_BER(string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + + AsnReader berReader = new AsnReader(inputData, AsnEncodingRules.BER); + + Assert.Throws(() => berReader.GetGeneralizedTime(disallowFractions: true)); + + // Legit if the fraction is allowed + berReader.GetGeneralizedTime(); + Assert.False(berReader.HasData, "berReader.HasData"); + } + + [Theory] + [InlineData("Wrong Tag", "170F32303136313130363031323334355A")] + [InlineData("Incomplete Tag", "1F")] + [InlineData("No Length", "18")] + [InlineData("No payload", "180F")] + [InlineData("Length exceeds content", "181032303136313130363031323334355A")] + [InlineData("yyyyMMdd", "18083230313631313036")] + [InlineData("yyyyMMddZ", "180932303136313130365A")] + [InlineData("yyyyMMdd+HH", "180B32303136313130362D3030")] + [InlineData("yyyyMMdd-HHmm", "180D32303136313130362B30303030")] + [InlineData("yyyyMMddH", "1809323031363131303630")] + [InlineData("yyyyMMddHZ", "180A3230313631313036305A")] + [InlineData("yQyyMMddHH", "180A32513136313130363031")] + [InlineData("yyyyMQddHH", "180A32303136315130363031")] + [InlineData("yyyyMMQdHH", "180A32303136313151363031")] + [InlineData("yyyyMMddHQ", "180A32303136313130363051")] + [InlineData("yyyyMMddHH,", "180B323031363131303630312C")] + [InlineData("yyyyMMddHH.", "180B323031363131303630312E")] + [InlineData("yyyyMMddHHQ", "180B3230313631313036303151")] + [InlineData("yyyyMMddHH,Q", "180C323031363131303630312C51")] + [InlineData("yyyyMMddHH.Q", "180C323031363131303630312E51")] + [InlineData("yyyyMMddHH..", "180C323031363131303630312E2E")] + [InlineData("yyyyMMddHH-", "180B323031363131303630312B")] + [InlineData("yyyyMMddHH+", "180B323031363131303630312D")] + [InlineData("yyyyMMddHHmQ", "180C323031363131303630313251")] + [InlineData("yyyyMMddHHm+", "180C32303136313130363031322D")] + [InlineData("yyyyMMddHHmmQ", "180D32303136313130363031323351")] + [InlineData("yyyyMMddHHmm-", "180D3230313631313036303132332B")] + [InlineData("yyyyMMddHHmm,", "180D3230313631313036303132332C")] + [InlineData("yyyyMMddHHmm+", "180D3230313631313036303132332D")] + [InlineData("yyyyMMddHHmm.", "180D3230313631313036303132332E")] + [InlineData("yyyyMMddHHmm+Q", "180E3230313631313036303132332D51")] + [InlineData("yyyyMMddHHmm.Q", "180E3230313631313036303132332E51")] + [InlineData("yyyyMMddHHmm..", "180E3230313631313036303132332E2E")] + [InlineData("yyyyMMddHHmm.ss,", "18103230313631313036303132332E31312E")] + [InlineData("yyyyMMddHHmms", "180D32303136313130363031323334")] + [InlineData("yyyyMMddHHmmsQ", "180E3230313631313036303132333451")] + [InlineData("yyyyMMddHHmmss-", "180F32303136313130363031323334352B")] + [InlineData("yyyyMMddHHmmss,", "180F32303136313130363031323334352C")] + [InlineData("yyyyMMddHHmmss+", "180F32303136313130363031323334352D")] + [InlineData("yyyyMMddHHmmss.", "180F32303136313130363031323334352E")] + [InlineData("yyyyMMddHHmmssQ", "180F323031363131303630313233343551")] + [InlineData("yyyyMMddHHmmss.Q", "181032303136313130363031323334352E51")] + [InlineData("yyyyMMddHHmmss.Q", "181032303136313130363031323334352E2E")] + [InlineData("yyyyMMddHHZmm", "180D323031363131303630315A3233")] + [InlineData("yyyyMMddHHmmZss", "180F3230313631313036303132335A3435")] + [InlineData("yyyyMMddHHmmssZ.s", "181232303136313130363031323334355A2E36")] + [InlineData("yyyyMMddHH+H", "180C323031363131303630312D30")] + [InlineData("yyyyMMddHH+HQ", "180D323031363131303630312D3051")] + [InlineData("yyyyMMddHH+HHQ", "180E323031363131303630312D303051")] + [InlineData("yyyyMMddHH+HHmQ", "180F323031363131303630312D30303051")] + [InlineData("yyyyMMddHH+HHmmQ", "1810323031363131303630312D3030303151")] + [InlineData("yyyyMMdd+H", "180A32303137313230382D30")] + [InlineData("yyyyMMddH+", "180A3230313731323038302D")] + [InlineData("yyyyMMddHH+0060", "180F323031373132303830392D30303630")] + [InlineData("yyyyMMddHH-2400", "180F323031373132303830392D32343030")] + [InlineData("yyyyMMddHH-HH:mm", "1810323031373039303832312D30313A3138")] + [InlineData("yyyyMMddHHmm-HH:mm", "18123230313730393038323131302D30313A3138")] + [InlineData("yyyyMMddHHmmss-HH:mm", "181432303137303930383231313032302D30313A3138")] + [InlineData("yyyyMMddHH,hourFrac-HH:mm", "1812323031373039303832312C312D30313A3138")] + [InlineData("yyyyMMddHH.hourFrac-HH:mm", "1812323031373039303832312E312D30313A3138")] + [InlineData("yyyyMMddHHmm,minuteFrac-HH:mm", "18183230313730393038323335382C30303030352D30313A3138")] + [InlineData("yyyyMMddHHmm.minuteFrac-HH:mm", "18183230313730393038323335382E30303030352D30313A3138")] + [InlineData("yyyyMMddHHmmss,secondFrac-HH:mm", "181932303136313130363031323334352C393939392D30313A3138")] + [InlineData("yyyyMMddHHmmss.secondFrac-HH:mm", "181932303136313130363031323334352E393939392D30313A3138")] + public static void GetGeneralizedTime_Throws(string description, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); + + Assert.Throws(() => reader.GetGeneralizedTime()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) + { + byte[] inputData = "180F32303136313130363031323334355A".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.GetGeneralizedTime(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.GetGeneralizedTime(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + Assert.Equal( + new DateTimeOffset(2016, 11, 6, 1, 23, 45, TimeSpan.Zero), + reader.GetGeneralizedTime()); + + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) + { + byte[] inputData = "850F32303136313130363031323334355A".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.GetGeneralizedTime(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.GetUtcTime()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.GetGeneralizedTime(new Asn1Tag(TagClass.Application, 5))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.GetGeneralizedTime(new Asn1Tag(TagClass.ContextSpecific, 7))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + Assert.Equal( + new DateTimeOffset(2016, 11, 6, 1, 23, 45, TimeSpan.Zero), + reader.GetGeneralizedTime(new Asn1Tag(TagClass.ContextSpecific, 5))); + + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "180F32303136313130363031323334355A", PublicTagClass.Universal, 24)] + [InlineData(PublicEncodingRules.CER, "180F32303136313130363031323334355A", PublicTagClass.Universal, 24)] + [InlineData(PublicEncodingRules.DER, "180F32303136313130363031323334355A", PublicTagClass.Universal, 24)] + [InlineData(PublicEncodingRules.BER, "800F31393530303130323132333435365A", PublicTagClass.ContextSpecific, 0)] + [InlineData(PublicEncodingRules.CER, "4C0F31393530303130323132333435365A", PublicTagClass.Application, 12)] + [InlineData(PublicEncodingRules.DER, "DF8A460F31393530303130323132333435365A", PublicTagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + PublicEncodingRules ruleSet, + string inputHex, + PublicTagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + DateTimeOffset val1 = reader.GetGeneralizedTime(new Asn1Tag((TagClass)tagClass, tagValue, true)); + + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + DateTimeOffset val2 = reader.GetGeneralizedTime(new Asn1Tag((TagClass)tagClass, tagValue, false)); + + Assert.False(reader.HasData); + + Assert.Equal(val1, val2); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadIA5String.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadIA5String.cs new file mode 100644 index 0000000000..42dc62a77e --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadIA5String.cs @@ -0,0 +1,722 @@ +// 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.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public sealed class ReadIA5String : Asn1ReaderTests + { + public static IEnumerable ValidEncodingData { get; } = + new object[][] + { + new object[] + { + PublicEncodingRules.BER, + "160D4A6F686E20512E20536D697468", + "John Q. Smith", + }, + new object[] + { + PublicEncodingRules.CER, + "160D4A6F686E20512E20536D697468", + "John Q. Smith", + }, + new object[] + { + PublicEncodingRules.DER, + "160D4A6F686E20512E20536D697468", + "John Q. Smith", + }, + new object[] + { + PublicEncodingRules.BER, + "3680" + "040D4A6F686E20512E20536D697468" + "0000", + "John Q. Smith", + }, + new object[] + { + PublicEncodingRules.BER, + "360F" + "040D4A6F686E20512E20536D697468", + "John Q. Smith", + }, + new object[] + { + PublicEncodingRules.BER, + "1600", + "", + }, + new object[] + { + PublicEncodingRules.CER, + "1600", + "", + }, + new object[] + { + PublicEncodingRules.DER, + "1600", + "", + }, + new object[] + { + PublicEncodingRules.BER, + "3600", + "", + }, + new object[] + { + PublicEncodingRules.BER, + "3680" + "0000", + "", + }, + new object[] + { + PublicEncodingRules.BER, + "3680" + + "2480" + + // "Dr." + "040344722E" + + // " & " + "0403202620" + + // "Mrs." + "04044D72732E" + + "0000" + + // " " + "040120" + + "2480" + + "240A" + + // "Smith" + "0405536D697468" + + // hyphen (U+2010) is not valid, so use hyphen-minus + "04012D" + + "0000" + + // "Jones" + "04054A6F6E6573" + + "2480" + + // " " + "040120" + + "2480" + + // small ampersand (U+FE60) is not valid, so use ampersand. + "040126" + + "0000" + + // " " + "040120" + + // "children" + "04086368696C6472656E" + + "0000" + + "0000", + "Dr. & Mrs. Smith-Jones & children", + }, + }; + + [Theory] + [MemberData(nameof(ValidEncodingData))] + public static void GetIA5String_Success( + PublicEncodingRules ruleSet, + string inputHex, + string expectedValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + string value = reader.GetCharacterString(UniversalTagNumber.IA5String); + + Assert.Equal(expectedValue, value); + } + + [Theory] + [MemberData(nameof(ValidEncodingData))] + public static void TryCopyIA5String( + PublicEncodingRules ruleSet, + string inputHex, + string expectedValue) + { + byte[] inputData = inputHex.HexToByteArray(); + char[] output = new char[expectedValue.Length]; + + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + bool copied; + int charsWritten; + + if (output.Length > 0) + { + output[0] = 'a'; + + copied = reader.TryCopyIA5String(output.AsSpan().Slice(0, expectedValue.Length - 1), + out charsWritten); + + Assert.False(copied, "reader.TryCopyIA5String - too short"); + Assert.Equal(0, charsWritten); + Assert.Equal('a', output[0]); + } + + copied = reader.TryCopyIA5String(output, + out charsWritten); + + Assert.True(copied, "reader.TryCopyIA5String"); + + string actualValue = new string(output, 0, charsWritten); + Assert.Equal(expectedValue, actualValue); + } + + [Theory] + [MemberData(nameof(ValidEncodingData))] + public static void TryCopyIA5StringBytes( + PublicEncodingRules ruleSet, + string inputHex, + string expectedString) + { + byte[] inputData = inputHex.HexToByteArray(); + string expectedHex = Text.Encoding.ASCII.GetBytes(expectedString).ByteArrayToHex(); + byte[] output = new byte[expectedHex.Length / 2]; + + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + bool copied; + int bytesWritten; + + if (output.Length > 0) + { + output[0] = 32; + + copied = reader.TryCopyIA5StringBytes(output.AsSpan().Slice(0, output.Length - 1), + out bytesWritten); + + Assert.False(copied, "reader.TryCopyIA5StringBytes - too short"); + Assert.Equal(0, bytesWritten); + Assert.Equal(32, output[0]); + } + + copied = reader.TryCopyIA5StringBytes(output, + out bytesWritten); + + Assert.True(copied, "reader.TryCopyIA5StringBytes"); + + Assert.Equal( + expectedHex, + new ReadOnlySpan(output, 0, bytesWritten).ByteArrayToHex()); + + Assert.Equal(output.Length, bytesWritten); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "160120", true)] + [InlineData(PublicEncodingRules.BER, "3680" + "040120" + "0000", false)] + [InlineData(PublicEncodingRules.BER, "3603" + "040120", false)] + public static void TryGetIA5StringBytes( + PublicEncodingRules ruleSet, + string inputHex, + bool expectSuccess) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + bool got = reader.TryGetIA5StringBytes(out ReadOnlyMemory contents); + + if (expectSuccess) + { + Assert.True(got, "reader.TryGetIA5StringBytes"); + + Assert.True( + Unsafe.AreSame( + ref MemoryMarshal.GetReference(contents.Span), + ref inputData[2])); + } + else + { + Assert.False(got, "reader.TryGetIA5StringBytes"); + Assert.True(contents.IsEmpty, "contents.IsEmpty"); + } + } + + [Theory] + [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] + [InlineData("Missing Length", PublicEncodingRules.BER, "16")] + [InlineData("Missing Length", PublicEncodingRules.CER, "16")] + [InlineData("Missing Length", PublicEncodingRules.DER, "16")] + [InlineData("Missing Contents", PublicEncodingRules.BER, "1601")] + [InlineData("Missing Contents", PublicEncodingRules.CER, "1601")] + [InlineData("Missing Contents", PublicEncodingRules.DER, "1601")] + [InlineData("Length Too Long", PublicEncodingRules.BER, "16034869")] + [InlineData("Length Too Long", PublicEncodingRules.CER, "16034869")] + [InlineData("Length Too Long", PublicEncodingRules.DER, "16034869")] + [InlineData("Constructed Form", PublicEncodingRules.DER, "3603040149")] + public static void TryGetIA5StringBytes_Throws( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws( + () => reader.TryGetIA5StringBytes(out ReadOnlyMemory contents)); + } + + [Theory] + [InlineData("Empty", PublicEncodingRules.BER, "")] + [InlineData("Empty", PublicEncodingRules.CER, "")] + [InlineData("Empty", PublicEncodingRules.DER, "")] + [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] + [InlineData("Missing Length", PublicEncodingRules.BER, "16")] + [InlineData("Missing Length", PublicEncodingRules.CER, "16")] + [InlineData("Missing Length", PublicEncodingRules.DER, "16")] + [InlineData("Missing Contents", PublicEncodingRules.BER, "1601")] + [InlineData("Missing Contents", PublicEncodingRules.CER, "1601")] + [InlineData("Missing Contents", PublicEncodingRules.DER, "1601")] + [InlineData("Missing Contents - Constructed", PublicEncodingRules.BER, "3601")] + [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.BER, "3680")] + [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.CER, "3680")] + [InlineData("Length Too Long", PublicEncodingRules.BER, "16034869")] + [InlineData("Length Too Long", PublicEncodingRules.CER, "16034869")] + [InlineData("Length Too Long", PublicEncodingRules.DER, "16034869")] + [InlineData("Definite Constructed Form", PublicEncodingRules.CER, "3603040149")] + [InlineData("Definite Constructed Form", PublicEncodingRules.DER, "3603040149")] + [InlineData("Indefinite Constructed Form - Short Payload", PublicEncodingRules.CER, "36800401490000")] + [InlineData("Indefinite Constructed Form", PublicEncodingRules.DER, "36800401490000")] + [InlineData("No nested content", PublicEncodingRules.CER, "36800000")] + [InlineData("No EoC", PublicEncodingRules.BER, "3680" + "04024869")] + [InlineData("Wrong Tag - Primitive", PublicEncodingRules.BER, "04024869")] + [InlineData("Wrong Tag - Primitive", PublicEncodingRules.CER, "04024869")] + [InlineData("Wrong Tag - Primitive", PublicEncodingRules.DER, "04024869")] + [InlineData("Wrong Tag - Constructed", PublicEncodingRules.BER, "240404024869")] + [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.BER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.CER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed", PublicEncodingRules.DER, "240404024869")] + [InlineData("Nested Bad Tag", PublicEncodingRules.BER, "3604" + "16024869")] + [InlineData("Nested context-specific", PublicEncodingRules.BER, "3604800400FACE")] + [InlineData("Nested context-specific (indef)", PublicEncodingRules.BER, "3680800400FACE0000")] + [InlineData("Nested context-specific (indef)", PublicEncodingRules.CER, "3680800400FACE0000")] + [InlineData("Nested Length Too Long", PublicEncodingRules.BER, "3607" + ("2402" + "0403") + "040149")] + [InlineData("Nested Simple Length Too Long", PublicEncodingRules.BER, "3603" + "040548656C6C6F")] + [InlineData("Constructed EndOfContents", PublicEncodingRules.BER, "368020000000")] + [InlineData("Constructed EndOfContents", PublicEncodingRules.CER, "368020000000")] + [InlineData("NonEmpty EndOfContents", PublicEncodingRules.BER, "3680000100")] + [InlineData("NonEmpty EndOfContents", PublicEncodingRules.CER, "3680000100")] + [InlineData("LongLength EndOfContents", PublicEncodingRules.BER, "3680008100")] + public static void TryCopyIA5StringBytes_Throws( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + byte[] outputData = new byte[inputData.Length + 1]; + outputData[0] = 252; + + int bytesWritten = -1; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws( + () => reader.TryCopyIA5StringBytes(outputData, out bytesWritten)); + + Assert.Equal(-1, bytesWritten); + Assert.Equal(252, outputData[0]); + } + + private static void TryCopyIA5String_Throws(PublicEncodingRules ruleSet, byte[] inputData) + { + char[] outputData = new char[inputData.Length + 1]; + outputData[0] = 'a'; + + int bytesWritten = -1; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws( + () => reader.TryCopyIA5String(outputData, out bytesWritten)); + + Assert.Equal(-1, bytesWritten); + Assert.Equal('a', outputData[0]); + } + + [Theory] + [InlineData("Bad IA5 value", PublicEncodingRules.BER, "1602E280")] + [InlineData("Bad IA5 value", PublicEncodingRules.CER, "1602E280")] + [InlineData("Bad IA5 value", PublicEncodingRules.DER, "1602E280")] + [InlineData("Wrong Tag", PublicEncodingRules.BER, "04024869")] + public static void GetIA5String_Throws( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws( + () => reader.GetCharacterString(UniversalTagNumber.IA5String)); + } + + [Theory] + [InlineData("Empty", PublicEncodingRules.BER, "")] + [InlineData("Empty", PublicEncodingRules.CER, "")] + [InlineData("Empty", PublicEncodingRules.DER, "")] + [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] + [InlineData("Missing Length", PublicEncodingRules.BER, "16")] + [InlineData("Missing Length", PublicEncodingRules.CER, "16")] + [InlineData("Missing Length", PublicEncodingRules.DER, "16")] + [InlineData("Missing Contents", PublicEncodingRules.BER, "1601")] + [InlineData("Missing Contents", PublicEncodingRules.CER, "1601")] + [InlineData("Missing Contents", PublicEncodingRules.DER, "1601")] + [InlineData("Missing Contents - Constructed", PublicEncodingRules.BER, "3601")] + [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.BER, "3680")] + [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.CER, "3680")] + [InlineData("Length Too Long", PublicEncodingRules.BER, "16034869")] + [InlineData("Length Too Long", PublicEncodingRules.CER, "16034869")] + [InlineData("Length Too Long", PublicEncodingRules.DER, "16034869")] + [InlineData("Definite Constructed Form", PublicEncodingRules.CER, "3603040149")] + [InlineData("Definite Constructed Form", PublicEncodingRules.DER, "3603040149")] + [InlineData("Indefinite Constructed Form - Short Payload", PublicEncodingRules.CER, "36800401490000")] + [InlineData("Indefinite Constructed Form", PublicEncodingRules.DER, "36800401490000")] + [InlineData("No nested content", PublicEncodingRules.CER, "36800000")] + [InlineData("No EoC", PublicEncodingRules.BER, "3680" + "04024869")] + [InlineData("Wrong Tag - Primitive", PublicEncodingRules.BER, "04024869")] + [InlineData("Wrong Tag - Primitive", PublicEncodingRules.CER, "04024869")] + [InlineData("Wrong Tag - Primitive", PublicEncodingRules.DER, "04024869")] + [InlineData("Wrong Tag - Constructed", PublicEncodingRules.BER, "240404024869")] + [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.BER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.CER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed", PublicEncodingRules.DER, "240404024869")] + [InlineData("Nested Bad Tag", PublicEncodingRules.BER, "3604" + "16024869")] + [InlineData("Nested context-specific", PublicEncodingRules.BER, "3604800400FACE")] + [InlineData("Nested context-specific (indef)", PublicEncodingRules.BER, "3680800400FACE0000")] + [InlineData("Nested context-specific (indef)", PublicEncodingRules.CER, "3680800400FACE0000")] + [InlineData("Nested Length Too Long", PublicEncodingRules.BER, "3607" + ("2402" + "0403") + "040149")] + [InlineData("Nested Simple Length Too Long", PublicEncodingRules.BER, "3603" + "040548656C6C6F")] + [InlineData("Constructed EndOfContents", PublicEncodingRules.BER, "368020000000")] + [InlineData("Constructed EndOfContents", PublicEncodingRules.CER, "368020000000")] + [InlineData("NonEmpty EndOfContents", PublicEncodingRules.BER, "3680000100")] + [InlineData("NonEmpty EndOfContents", PublicEncodingRules.CER, "3680000100")] + [InlineData("LongLength EndOfContents", PublicEncodingRules.BER, "3680008100")] + [InlineData("Bad IA5 value", PublicEncodingRules.BER, "1602E280")] + public static void TryCopyIA5String_Throws( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + TryCopyIA5String_Throws(ruleSet, inputData); + } + + [Fact] + public static void TryCopyIA5String_Throws_CER_NestedTooLong() + { + // CER says that the maximum encoding length for a IA5String primitive + // is 1000. + // + // This test checks it for a primitive contained within a constructed. + // + // So we need 04 [1001] { 1001 0x00s } + // 1001 => 0x3E9, so the length encoding is 82 03 E9. + // 1001 + 3 + 1 == 1005 + // + // Plus a leading 36 80 (indefinite length constructed) + // and a trailing 00 00 (End of contents) + // == 1009 + byte[] input = new byte[1009]; + // CONSTRUCTED IA5STRING (indefinite) + input[0] = 0x36; + input[1] = 0x80; + // OCTET STRING (1001) + input[2] = 0x04; + input[3] = 0x82; + input[4] = 0x03; + input[5] = 0xE9; + // EOC implicit since the byte[] initializes to zeros + + TryCopyIA5String_Throws(PublicEncodingRules.CER, input); + } + + [Fact] + public static void TryCopyIA5String_Throws_CER_NestedTooShortIntermediate() + { + // CER says that the maximum encoding length for a IA5String primitive + // is 1000, and in the constructed form the lengths must be + // [ 1000, 1000, 1000, ..., len%1000 ] + // + // So 1000, 2, 2 is illegal. + // + // 36 80 (indefinite constructed IA5 string) + // 04 82 03 08 (octet string, 1000 bytes) + // [1000 content bytes] + // 04 02 (octet string, 2 bytes) + // [2 content bytes] + // 04 02 (octet string, 2 bytes) + // [2 content bytes] + // 00 00 (end of contents) + // Looks like 1,016 bytes. + byte[] input = new byte[1016]; + // CONSTRUCTED IA5STRING (indefinite) + input[0] = 0x36; + input[1] = 0x80; + // OCTET STRING (1000) + input[2] = 0x03; + input[3] = 0x82; + input[4] = 0x03; + input[5] = 0xE8; + // OCTET STRING (2) + input[1006] = 0x04; + input[1007] = 0x02; + // OCTET STRING (2) + input[1010] = 0x04; + input[1011] = 0x02; + // EOC implicit since the byte[] initializes to zeros + + TryCopyIA5String_Throws(PublicEncodingRules.CER, input); + } + + [Fact] + public static void TryCopyIA5StringBytes_Success_CER_MaxPrimitiveLength() + { + // CER says that the maximum encoding length for a IA5String primitive + // is 1000. + // + // So we need 16 [1000] { 1000 anythings } + // 1000 => 0x3E8, so the length encoding is 82 03 E8. + // 1000 + 3 + 1 == 1004 + byte[] input = new byte[1004]; + input[0] = 0x16; + input[1] = 0x82; + input[2] = 0x03; + input[3] = 0xE8; + + // Content + input[4] = 0x65; + input[5] = 0x65; + input[1002] = 0x61; + input[1003] = 0x61; + + byte[] output = new byte[1000]; + + AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); + + bool success = reader.TryCopyIA5StringBytes(output, + out int bytesWritten); + + Assert.True(success, "reader.TryCopyIA5StringBytes"); + Assert.Equal(1000, bytesWritten); + + Assert.Equal( + input.AsReadOnlySpan().Slice(4).ByteArrayToHex(), + output.ByteArrayToHex()); + } + + [Fact] + public static void TryCopyIA5StringBytes_Success_CER_MinConstructedLength() + { + // CER says that the maximum encoding length for a IA5String primitive + // is 1000, and that a constructed form must be used for values greater + // than 1000 bytes, with segments dividing up for each thousand + // [1000, 1000, ..., len%1000]. + // + // So our smallest constructed form is 1001 bytes, [1000, 1] + // + // 36 80 (indefinite constructed IA5 string) + // 04 82 03 E9 (primitive octet string, 1000 bytes) + // [1000 content bytes] + // 04 01 (primitive octet string, 1 byte) + // pp + // 00 00 (end of contents, 0 bytes) + // 1011 total. + byte[] input = new byte[1011]; + int offset = 0; + // CONSTRUCTED IA5STRING (Indefinite) + input[offset++] = 0x36; + input[offset++] = 0x80; + // OCTET STRING (1000) + input[offset++] = 0x04; + input[offset++] = 0x82; + input[offset++] = 0x03; + input[offset++] = 0xE8; + + // Primitive 1: (65 65 :: 61 61) (1000) + input[offset++] = 0x65; + input[offset] = 0x65; + offset += 997; + input[offset++] = 0x61; + input[offset++] = 0x61; + + // OCTET STRING (1) + input[offset++] = 0x04; + input[offset++] = 0x01; + + // Primitive 2: One more byte + input[offset] = 0x2E; + + byte[] expected = new byte[1001]; + offset = 0; + expected[offset++] = 0x65; + expected[offset] = 0x65; + offset += 997; + expected[offset++] = 0x61; + expected[offset++] = 0x61; + expected[offset] = 0x2E; + + byte[] output = new byte[1001]; + + AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); + + bool success = reader.TryCopyIA5StringBytes(output, + out int bytesWritten); + + Assert.True(success, "reader.TryCopyIA5StringBytes"); + Assert.Equal(1001, bytesWritten); + + Assert.Equal( + expected.ByteArrayToHex(), + output.ByteArrayToHex()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) + { + byte[] inputData = { 0x16, 2, (byte)'e', (byte)'l' }; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.TryGetIA5StringBytes(Asn1Tag.Null, out _)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.TryGetIA5StringBytes(new Asn1Tag(TagClass.ContextSpecific, 0), out _)); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + Assert.True(reader.TryGetIA5StringBytes(out ReadOnlyMemory value)); + Assert.Equal("656C", value.ByteArrayToHex()); + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) + { + byte[] inputData = { 0x87, 2, (byte)'h', (byte)'i' }; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.TryGetIA5StringBytes(Asn1Tag.Null, out _)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.TryGetIA5StringBytes(out _)); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.TryGetIA5StringBytes(new Asn1Tag(TagClass.Application, 0), out _)); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.TryGetIA5StringBytes(new Asn1Tag(TagClass.ContextSpecific, 1), out _)); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + Assert.True( + reader.TryGetIA5StringBytes( + new Asn1Tag(TagClass.ContextSpecific, 7), + out ReadOnlyMemory value)); + + Assert.Equal("6869", value.ByteArrayToHex()); + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "16026869", PublicTagClass.Universal, 22)] + [InlineData(PublicEncodingRules.CER, "16026869", PublicTagClass.Universal, 22)] + [InlineData(PublicEncodingRules.DER, "16026869", PublicTagClass.Universal, 22)] + [InlineData(PublicEncodingRules.BER, "80023132", PublicTagClass.ContextSpecific, 0)] + [InlineData(PublicEncodingRules.CER, "4C023132", PublicTagClass.Application, 12)] + [InlineData(PublicEncodingRules.DER, "DF8A46023132", PublicTagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + PublicEncodingRules ruleSet, + string inputHex, + PublicTagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.True( + reader.TryGetIA5StringBytes( + new Asn1Tag((TagClass)tagClass, tagValue, true), + out ReadOnlyMemory val1)); + + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.True( + reader.TryGetIA5StringBytes( + new Asn1Tag((TagClass)tagClass, tagValue, false), + out ReadOnlyMemory val2)); + + Assert.False(reader.HasData); + + Assert.Equal(val1.ByteArrayToHex(), val2.ByteArrayToHex()); + } + } + + internal static class ReaderIA5Extensions + { + public static bool TryGetIA5StringBytes( + this AsnReader reader, + out ReadOnlyMemory contents) + { + return reader.TryGetPrimitiveCharacterStringBytes( + UniversalTagNumber.IA5String, + out contents); + } + + public static bool TryGetIA5StringBytes( + this AsnReader reader, + Asn1Tag expectedTag, + out ReadOnlyMemory contents) + { + return reader.TryGetPrimitiveCharacterStringBytes( + expectedTag, + UniversalTagNumber.IA5String, + out contents); + } + + public static bool TryCopyIA5StringBytes( + this AsnReader reader, + Span destination, + out int bytesWritten) + { + return reader.TryCopyCharacterStringBytes( + UniversalTagNumber.IA5String, + destination, + out bytesWritten); + } + + public static bool TryCopyIA5String( + this AsnReader reader, + Span destination, + out int charsWritten) + { + return reader.TryCopyCharacterString( + UniversalTagNumber.IA5String, + destination, + out charsWritten); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadInteger.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadInteger.cs new file mode 100644 index 0000000000..148e7127f5 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadInteger.cs @@ -0,0 +1,605 @@ +// 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.Numerics; +using System.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public sealed class ReadInteger : Asn1ReaderTests + { + [Theory] + [InlineData("Constructed Encoding", PublicEncodingRules.BER, "2203020100")] + [InlineData("Constructed Encoding-Indefinite", PublicEncodingRules.BER, "228002010000")] + [InlineData("Constructed Encoding-Indefinite", PublicEncodingRules.CER, "228002010000")] + [InlineData("Constructed Encoding", PublicEncodingRules.DER, "2203020100")] + [InlineData("Wrong Universal Tag", PublicEncodingRules.BER, "030100")] + [InlineData("Bad Length", PublicEncodingRules.BER, "02030102")] + [InlineData("Incorrect Zero Encoding", PublicEncodingRules.BER, "0200")] + [InlineData("Incorrect Zero Encoding", PublicEncodingRules.CER, "0200")] + [InlineData("Incorrect Zero Encoding", PublicEncodingRules.DER, "0200")] + [InlineData("Redundant Leading 0x00", PublicEncodingRules.BER, "0202007F")] + [InlineData("Redundant Leading 0x00", PublicEncodingRules.CER, "0202007F")] + [InlineData("Redundant Leading 0x00", PublicEncodingRules.DER, "0202007F")] + [InlineData("Redundant Leading 0xFF", PublicEncodingRules.BER, "0202FF80")] + [InlineData("Redundant Leading 0xFF", PublicEncodingRules.CER, "0202FF80")] + [InlineData("Redundant Leading 0xFF", PublicEncodingRules.DER, "0202FF80")] + public static void InvalidData( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + + Assert.Throws(() => reader.GetIntegerBytes()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "020100", 0)] + [InlineData(PublicEncodingRules.CER, "020100", 0)] + [InlineData(PublicEncodingRules.DER, "020100", 0)] + [InlineData(PublicEncodingRules.DER, "02017F", sbyte.MaxValue)] + [InlineData(PublicEncodingRules.DER, "020180", sbyte.MinValue)] + [InlineData(PublicEncodingRules.DER, "0201FF", -1)] + public static void ReadInt8_Success( + PublicEncodingRules ruleSet, + string inputHex, + sbyte expectedValue) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryReadInt8(out sbyte value); + + Assert.True(didRead, "reader.TryReadInt8"); + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "02020102")] + [InlineData(PublicEncodingRules.CER, "02020102")] + [InlineData(PublicEncodingRules.DER, "02020102")] + public static void ReadInt8_TooMuchData( + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryReadInt8(out sbyte value); + + Assert.False(didRead, "reader.TryReadInt8"); + Assert.Equal(0, value); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "020100", 0)] + [InlineData(PublicEncodingRules.CER, "02017F", 0x7F)] + [InlineData(PublicEncodingRules.CER, "02020080", 0x80)] + [InlineData(PublicEncodingRules.CER, "020200FF", 0xFF)] + public static void ReadUInt8_Success( + PublicEncodingRules ruleSet, + string inputHex, + byte expectedValue) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryReadUInt8(out byte value); + + Assert.True(didRead, "reader.TryReadUInt8"); + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "020180")] + [InlineData(PublicEncodingRules.CER, "020180")] + [InlineData(PublicEncodingRules.DER, "020180")] + [InlineData(PublicEncodingRules.BER, "0201FF")] + [InlineData(PublicEncodingRules.CER, "0201FF")] + [InlineData(PublicEncodingRules.DER, "0201FF")] + public static void ReadUInt8_Failure(PublicEncodingRules ruleSet, string inputHex) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryReadUInt8(out byte value); + + Assert.False(didRead, "reader.TryReadUInt8"); + Assert.Equal((byte)0, value); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "020100", 0)] + [InlineData(PublicEncodingRules.CER, "020100", 0)] + [InlineData(PublicEncodingRules.DER, "020100", 0)] + [InlineData(PublicEncodingRules.DER, "0201FF", -1)] + [InlineData(PublicEncodingRules.CER, "0202FEFF", unchecked((short)0xFEFF))] + [InlineData(PublicEncodingRules.BER, "028102FEEF", unchecked((short)0xFEEF))] + [InlineData(PublicEncodingRules.BER, "0281028000", short.MinValue)] + [InlineData(PublicEncodingRules.CER, "02028000", short.MinValue)] + [InlineData(PublicEncodingRules.DER, "02027FFF", short.MaxValue)] + [InlineData(PublicEncodingRules.DER, "02026372", 0x6372)] + [InlineData(PublicEncodingRules.CER, "0202008A", 0x8A)] + [InlineData(PublicEncodingRules.CER, "02028ACE", unchecked((short)0x8ACE))] + public static void ReadInt16_Success( + PublicEncodingRules ruleSet, + string inputHex, + short expectedValue) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryReadInt16(out short value); + + Assert.True(didRead, "reader.TryReadInt16"); + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "0203010203")] + [InlineData(PublicEncodingRules.CER, "0203010203")] + [InlineData(PublicEncodingRules.DER, "0203010203")] + public static void ReadInt16_TooMuchData( + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryReadInt16(out short value); + + Assert.False(didRead, "reader.TryReadInt16"); + Assert.Equal(0, value); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "020100", 0)] + [InlineData(PublicEncodingRules.CER, "02020080", 0x80)] + [InlineData(PublicEncodingRules.DER, "02027F80", 0x7F80)] + [InlineData(PublicEncodingRules.DER, "0203008180", 0x8180)] + public static void ReadUInt16_Success( + PublicEncodingRules ruleSet, + string inputHex, + ushort expectedValue) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryReadUInt16(out ushort value); + + Assert.True(didRead, "reader.TryReadUInt16"); + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "020180")] + [InlineData(PublicEncodingRules.CER, "020180")] + [InlineData(PublicEncodingRules.DER, "020180")] + [InlineData(PublicEncodingRules.BER, "0201FF")] + [InlineData(PublicEncodingRules.CER, "0201FF")] + [InlineData(PublicEncodingRules.DER, "0201FF")] + [InlineData(PublicEncodingRules.DER, "02028000")] + public static void ReadUInt16_Failure(PublicEncodingRules ruleSet, string inputHex) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryReadUInt16(out ushort value); + + Assert.False(didRead, "reader.TryReadUInt16"); + Assert.Equal((ushort)0, value); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "020100", 0)] + [InlineData(PublicEncodingRules.CER, "020100", 0)] + [InlineData(PublicEncodingRules.DER, "020100", 0)] + [InlineData(PublicEncodingRules.DER, "0201FF", -1)] + [InlineData(PublicEncodingRules.CER, "0202FEFF", unchecked((int)0xFFFF_FEFF))] + [InlineData(PublicEncodingRules.BER, "028102FEEF", unchecked((int)0xFFFF_FEEF))] + [InlineData(PublicEncodingRules.BER, "02810480000000", int.MinValue)] + [InlineData(PublicEncodingRules.CER, "020480000000", int.MinValue)] + [InlineData(PublicEncodingRules.DER, "02047FFFFFFF", int.MaxValue)] + [InlineData(PublicEncodingRules.DER, "02026372", 0x6372)] + [InlineData(PublicEncodingRules.CER, "0203008ACE", 0x8ACE)] + [InlineData(PublicEncodingRules.BER, "0203FACE01", unchecked((int)0xFFFA_CE01))] + [InlineData(PublicEncodingRules.BER, "02820003FACE01", unchecked((int)0xFFFA_CE01))] + public static void ReadInt32_Success( + PublicEncodingRules ruleSet, + string inputHex, + int expectedValue) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryReadInt32(out int value); + + Assert.True(didRead, "reader.TryReadInt32"); + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "02050102030405")] + [InlineData(PublicEncodingRules.CER, "02050102030405")] + [InlineData(PublicEncodingRules.DER, "02050102030405")] + public static void ReadInt32_TooMuchData( + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryReadInt32(out int value); + + Assert.False(didRead, "reader.TryReadInt32"); + Assert.Equal(0, value); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "020100", 0)] + [InlineData(PublicEncodingRules.CER, "02020080", 0x80)] + [InlineData(PublicEncodingRules.DER, "02027F80", 0x7F80)] + [InlineData(PublicEncodingRules.DER, "0203008180", 0x8180)] + [InlineData(PublicEncodingRules.DER, "02030A8180", 0xA8180)] + [InlineData(PublicEncodingRules.DER, "020400828180", 0x828180)] + [InlineData(PublicEncodingRules.DER, "020475828180", 0x75828180)] + [InlineData(PublicEncodingRules.DER, "02050083828180", 0x83828180)] + [InlineData(PublicEncodingRules.BER, "02830000050083828180", 0x83828180)] + public static void ReadUInt32_Success( + PublicEncodingRules ruleSet, + string inputHex, + uint expectedValue) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryReadUInt32(out uint value); + + Assert.True(didRead, "reader.TryReadUInt32"); + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "020180")] + [InlineData(PublicEncodingRules.CER, "020180")] + [InlineData(PublicEncodingRules.DER, "020180")] + [InlineData(PublicEncodingRules.BER, "0201FF")] + [InlineData(PublicEncodingRules.CER, "0201FF")] + [InlineData(PublicEncodingRules.DER, "0201FF")] + [InlineData(PublicEncodingRules.DER, "02028000")] + [InlineData(PublicEncodingRules.DER, "0203800000")] + [InlineData(PublicEncodingRules.DER, "020480000000")] + [InlineData(PublicEncodingRules.DER, "02050100000000")] + public static void ReadUInt32_Failure(PublicEncodingRules ruleSet, string inputHex) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryReadUInt32(out uint value); + + Assert.False(didRead, "reader.TryReadUInt32"); + Assert.Equal((uint)0, value); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "020100", 0)] + [InlineData(PublicEncodingRules.CER, "02020080", 0x80)] + [InlineData(PublicEncodingRules.DER, "02027F80", 0x7F80)] + [InlineData(PublicEncodingRules.DER, "0203008180", 0x8180)] + [InlineData(PublicEncodingRules.DER, "02030A8180", 0xA8180)] + [InlineData(PublicEncodingRules.DER, "020400828180", 0x828180)] + [InlineData(PublicEncodingRules.DER, "020475828180", 0x75828180)] + [InlineData(PublicEncodingRules.DER, "02050083828180", 0x83828180)] + [InlineData(PublicEncodingRules.BER, "02830000050083828180", 0x83828180)] + [InlineData(PublicEncodingRules.DER, "02050183828180", 0x0183828180)] + [InlineData(PublicEncodingRules.DER, "0206018483828180", 0x018483828180)] + [InlineData(PublicEncodingRules.DER, "020701858483828180", 0x01858483828180)] + [InlineData(PublicEncodingRules.DER, "02080186858483828180", 0x0186858483828180)] + [InlineData(PublicEncodingRules.DER, "02087F86858483828180", 0x7F86858483828180)] + [InlineData(PublicEncodingRules.DER, "02087FFFFFFFFFFFFFFF", long.MaxValue)] + [InlineData(PublicEncodingRules.DER, "0201FF", -1)] + [InlineData(PublicEncodingRules.DER, "0201FE", -2)] + [InlineData(PublicEncodingRules.DER, "02028012", unchecked((long)0xFFFFFFFF_FFFF8012))] + [InlineData(PublicEncodingRules.DER, "0203818012", unchecked((long)0xFFFFFFFF_FF818012))] + [InlineData(PublicEncodingRules.DER, "020482818012", unchecked((long)0xFFFFFFFF_82818012))] + [InlineData(PublicEncodingRules.DER, "02058382818012", unchecked((long)0xFFFFFF83_82818012))] + [InlineData(PublicEncodingRules.DER, "0206848382818012", unchecked((long)0xFFFF8483_82818012))] + [InlineData(PublicEncodingRules.DER, "020785848382818012", unchecked((long)0xFF858483_82818012))] + [InlineData(PublicEncodingRules.DER, "02088685848382818012", unchecked((long)0x86858483_82818012))] + [InlineData(PublicEncodingRules.DER, "02088000000000000000", long.MinValue)] + [InlineData(PublicEncodingRules.BER, "028800000000000000088000000000000000", long.MinValue)] + public static void ReadInt64_Success( + PublicEncodingRules ruleSet, + string inputHex, + long expectedValue) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryReadInt64(out long value); + + Assert.True(didRead, "reader.TryReadInt64"); + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "0209010203040506070809")] + [InlineData(PublicEncodingRules.CER, "0209010203040506070809")] + [InlineData(PublicEncodingRules.DER, "0209010203040506070809")] + public static void ReadInt64_TooMuchData( + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryReadInt64(out long value); + + Assert.False(didRead, "reader.TryReadInt64"); + Assert.Equal(0, value); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "020100", 0)] + [InlineData(PublicEncodingRules.CER, "02020080", 0x80)] + [InlineData(PublicEncodingRules.DER, "02027F80", 0x7F80)] + [InlineData(PublicEncodingRules.DER, "0203008180", 0x8180)] + [InlineData(PublicEncodingRules.DER, "02030A8180", 0xA8180)] + [InlineData(PublicEncodingRules.DER, "020400828180", 0x828180)] + [InlineData(PublicEncodingRules.DER, "020475828180", 0x75828180)] + [InlineData(PublicEncodingRules.DER, "02050083828180", 0x83828180)] + [InlineData(PublicEncodingRules.BER, "02830000050083828180", 0x83828180)] + [InlineData(PublicEncodingRules.DER, "02050183828180", 0x0183828180)] + [InlineData(PublicEncodingRules.DER, "0206018483828180", 0x018483828180)] + [InlineData(PublicEncodingRules.DER, "020701858483828180", 0x01858483828180)] + [InlineData(PublicEncodingRules.DER, "02080186858483828180", 0x0186858483828180)] + [InlineData(PublicEncodingRules.DER, "02087F86858483828180", 0x7F86858483828180)] + [InlineData(PublicEncodingRules.DER, "02087FFFFFFFFFFFFFFF", long.MaxValue)] + [InlineData(PublicEncodingRules.DER, "0209008000000000000000", 0x80000000_00000000)] + [InlineData(PublicEncodingRules.DER, "020900FFFFFFFFFFFFFFFF", ulong.MaxValue)] + public static void ReadUInt64_Success( + PublicEncodingRules ruleSet, + string inputHex, + ulong expectedValue) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryReadUInt64(out ulong value); + + Assert.True(didRead, "reader.TryReadUInt64"); + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "020180")] + [InlineData(PublicEncodingRules.CER, "020180")] + [InlineData(PublicEncodingRules.DER, "020180")] + [InlineData(PublicEncodingRules.BER, "0201FF")] + [InlineData(PublicEncodingRules.CER, "0201FF")] + [InlineData(PublicEncodingRules.DER, "0201FF")] + [InlineData(PublicEncodingRules.DER, "02028000")] + [InlineData(PublicEncodingRules.DER, "0203800000")] + [InlineData(PublicEncodingRules.DER, "020480000000")] + [InlineData(PublicEncodingRules.DER, "02058000000000")] + [InlineData(PublicEncodingRules.DER, "0206800000000000")] + [InlineData(PublicEncodingRules.DER, "020780000000000000")] + [InlineData(PublicEncodingRules.DER, "02088000000000000000")] + [InlineData(PublicEncodingRules.DER, "0209010000000000000000")] + public static void ReadUInt64_Failure(PublicEncodingRules ruleSet, string inputHex) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryReadUInt64(out ulong value); + + Assert.False(didRead, "reader.TryReadUInt64"); + Assert.Equal((uint)0, value); + } + + [Fact] + public static void GetIntegerBytes() + { + const string Payload = "0102030405060708090A0B0C0D0E0F10"; + + // INTEGER (payload) followed by INTEGER (0) + byte[] data = ("0210" + Payload + "020100").HexToByteArray(); + AsnReader reader = new AsnReader(data, AsnEncodingRules.DER); + + ReadOnlyMemory contents = reader.GetIntegerBytes(); + Assert.Equal(0x10, contents.Length); + Assert.Equal(Payload, contents.ByteArrayToHex()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void GetBigInteger(PublicEncodingRules ruleSet) + { + byte[] inputData = ( + "0282010100A46861FA9D5DB763633BF5A64EF6E7C2C2367F48D2D46643A22DFC" + + "FCCB24E58A14D0F06BDC956437F2A56BA4BEF70BA361BF12964A0D665AFD84B0" + + "F7494C8FA4ABC5FCA2E017C06178AEF2CDAD1B5F18E997A14B965C074E8F5649" + + "70607276B00583932240FE6E2DD013026F9AE13D7C91CC07C4E1E8E87737DC06" + + "EF2B575B89D62EFE46859F8255A123692A706C68122D4DAFE11CB205A7B3DE06" + + "E553F7B95F978EF8601A8DF819BF32040BDF92A0DE0DF269B4514282E17AC699" + + "34E8440A48AB9D1F5DF89A502CEF6DFDBE790045BD45E0C94E5CA8ADD76A013E" + + "9C978440FC8A9E2A9A4940B2460819C3E302AA9C9F355AD754C86D3ED77DDAA3" + + "DA13810B4D").HexToByteArray(); + + BigInteger expected = BigInteger.Parse( + "2075455505718444046766086325128514549301113944667492053189486607" + + "5638152321436011512404808361119326026027238444019992518081753153" + + "5965931647037093368608713442955529617501657176146245891571745113" + + "4028700771890451167051818999837042261788828826028681595867897235" + + "7967091503500375497498573022675671178275171110498592645868107163" + + "8525996766798322809764200941677343791419428587801897366593842552" + + "7272226864578661449281241619675217353931828233756506947863330597" + + "8338073826285687331647183058971791153730741973483420110408271570" + + "1367336140572971505716740825623220507359429297584634909330541150" + + "79473593821332264673455059897928082590541"); + + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + Assert.Equal(expected, reader.GetInteger()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void GetNegativeBigInteger(PublicEncodingRules ruleSet) + { + // This uses a length that doesn't line up with the array pool size so + // the fill code gets tested on netstandard. + // It's the same data as above, minus the padding and lead byte (and the + // next byte changed from 0x68 to 0x88) + byte[] inputData = ( + "0281FF8861FA9D5DB763633BF5A64EF6E7C2C2367F48D2D46643A22DFC" + + "FCCB24E58A14D0F06BDC956437F2A56BA4BEF70BA361BF12964A0D665AFD84B0" + + "F7494C8FA4ABC5FCA2E017C06178AEF2CDAD1B5F18E997A14B965C074E8F5649" + + "70607276B00583932240FE6E2DD013026F9AE13D7C91CC07C4E1E8E87737DC06" + + "EF2B575B89D62EFE46859F8255A123692A706C68122D4DAFE11CB205A7B3DE06" + + "E553F7B95F978EF8601A8DF819BF32040BDF92A0DE0DF269B4514282E17AC699" + + "34E8440A48AB9D1F5DF89A502CEF6DFDBE790045BD45E0C94E5CA8ADD76A013E" + + "9C978440FC8A9E2A9A4940B2460819C3E302AA9C9F355AD754C86D3ED77DDAA3" + + "DA13810B4D").HexToByteArray(); + + BigInteger expected = BigInteger.Parse( + "-" + + "5898547409447487884446992857601985651316300515844052200158198046" + + "7814538020408452501006415149581619776188797413593169277984980446" + + "1302361382932378450492052337986628823880000831383555853860116718" + + "5361729331647715885538858385106930514758305144777880150203212976" + + "6715081632226412951106013360243549075631850526067219857352295397" + + "2308328327377769665309386917336850273904442596855844458638806936" + + "6169824439111394938336579524651037239551388910737675470211780509" + + "8035477769907389338548451561341965157059382875181284370047601682" + + "6924486017215979427815833587119797658480104671279234402026468966" + + "86109928634475300812601680679147599027"); + + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + Assert.Equal(expected, reader.GetInteger()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void GetDiminutiveBigInteger(PublicEncodingRules ruleSet) + { + // GetBigInteger with the last byte removed. + // Since it is no longer an ArrayPool alignment size the fill code gets tested on netstandard. + byte[] inputData = ( + "0282010000A46861FA9D5DB763633BF5A64EF6E7C2C2367F48D2D46643A22DFC" + + "FCCB24E58A14D0F06BDC956437F2A56BA4BEF70BA361BF12964A0D665AFD84B0" + + "F7494C8FA4ABC5FCA2E017C06178AEF2CDAD1B5F18E997A14B965C074E8F5649" + + "70607276B00583932240FE6E2DD013026F9AE13D7C91CC07C4E1E8E87737DC06" + + "EF2B575B89D62EFE46859F8255A123692A706C68122D4DAFE11CB205A7B3DE06" + + "E553F7B95F978EF8601A8DF819BF32040BDF92A0DE0DF269B4514282E17AC699" + + "34E8440A48AB9D1F5DF89A502CEF6DFDBE790045BD45E0C94E5CA8ADD76A013E" + + "9C978440FC8A9E2A9A4940B2460819C3E302AA9C9F355AD754C86D3ED77DDAA3" + + "DA13810B").HexToByteArray(); + + BigInteger expected = BigInteger.Parse( + "2075455505718444046766086325128514549301113944667492053189486607" + + "5638152321436011512404808361119326026027238444019992518081753153" + + "5965931647037093368608713442955529617501657176146245891571745113" + + "4028700771890451167051818999837042261788828826028681595867897235" + + "7967091503500375497498573022675671178275171110498592645868107163" + + "8525996766798322809764200941677343791419428587801897366593842552" + + "7272226864578661449281241619675217353931828233756506947863330597" + + "8338073826285687331647183058971791153730741973483420110408271570" + + "1367336140572971505716740825623220507359429297584634909330541150" + + "79473593821332264673455059897928082590541") >> 8; + + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + Assert.Equal(expected, reader.GetInteger()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) + { + byte[] inputData = { 2, 1, 0x7E }; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.GetIntegerBytes(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.GetIntegerBytes(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + ReadOnlyMemory value = reader.GetIntegerBytes(); + Assert.Equal("7E", value.ByteArrayToHex()); + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) + { + byte[] inputData = { 0x87, 2, 0, 0x80 }; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.GetIntegerBytes(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.GetIntegerBytes()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws(() => reader.GetIntegerBytes(new Asn1Tag(TagClass.Application, 0))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws(() => reader.GetIntegerBytes(new Asn1Tag(TagClass.ContextSpecific, 1))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + ReadOnlyMemory value = reader.GetIntegerBytes(new Asn1Tag(TagClass.ContextSpecific, 7)); + Assert.Equal("0080", value.ByteArrayToHex()); + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "0201FF", PublicTagClass.Universal, 2)] + [InlineData(PublicEncodingRules.CER, "0201FF", PublicTagClass.Universal, 2)] + [InlineData(PublicEncodingRules.DER, "0201FF", PublicTagClass.Universal, 2)] + [InlineData(PublicEncodingRules.BER, "8001FF", PublicTagClass.ContextSpecific, 0)] + [InlineData(PublicEncodingRules.CER, "4C01FF", PublicTagClass.Application, 12)] + [InlineData(PublicEncodingRules.DER, "DF8A4601FF", PublicTagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + PublicEncodingRules ruleSet, + string inputHex, + PublicTagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + ReadOnlyMemory val1 = reader.GetIntegerBytes(new Asn1Tag((TagClass)tagClass, tagValue, true)); + Assert.False(reader.HasData); + reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + ReadOnlyMemory val2 = reader.GetIntegerBytes(new Asn1Tag((TagClass)tagClass, tagValue, false)); + Assert.False(reader.HasData); + + Assert.Equal(val1.ByteArrayToHex(), val2.ByteArrayToHex()); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadLength.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadLength.cs new file mode 100644 index 0000000000..5b8ebe9574 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadLength.cs @@ -0,0 +1,162 @@ +// 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.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public sealed class ReadLength : Asn1ReaderTests + { + [Theory] + [InlineData(4, 0, "0400")] + [InlineData(1, 1, "0101")] + [InlineData(4, 127, "047F")] + [InlineData(4, 128, "048180")] + [InlineData(4, 255, "0481FF")] + [InlineData(2, 256, "02820100")] + [InlineData(4, int.MaxValue, "04847FFFFFFF")] + public static void MinimalPrimitiveLength(int tagValue, int length, string inputHex) + { + byte[] inputBytes = inputHex.HexToByteArray(); + + foreach (PublicEncodingRules rules in Enum.GetValues(typeof(PublicEncodingRules))) + { + AsnReader reader = new AsnReader(inputBytes, (AsnEncodingRules)rules); + + Asn1Tag tag = reader.ReadTagAndLength(out int ? parsedLength, out int bytesRead); + + Assert.Equal(inputBytes.Length, bytesRead); + Assert.False(tag.IsConstructed, "tag.IsConstructed"); + Assert.Equal(tagValue, tag.TagValue); + Assert.Equal(length, parsedLength.Value); + + // ReadTagAndLength doesn't move the _data span forward. + Assert.True(reader.HasData, "reader.HasData"); + } + } + + [Theory] + [InlineData(-1)] + [InlineData(3)] + public static void ReadWithUnknownRuleSet(int invalidRuleSetValue) + { + byte[] data = { 0x05, 0x00 }; + + Assert.Throws( + () => new AsnReader(data, (AsnEncodingRules)invalidRuleSetValue)); + } + + [Theory] + [InlineData("")] + [InlineData("05")] + [InlineData("0481")] + [InlineData("048201")] + [InlineData("04830102")] + [InlineData("0484010203")] + public static void ReadWithInsufficientData(string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, AsnEncodingRules.DER); + + Assert.Throws(() => reader.ReadTagAndLength(out _, out _)); + } + + [Theory] + [InlineData("DER indefinite constructed", PublicEncodingRules.DER, "3080" + "0500" + "0000")] + [InlineData("0xFF-BER", PublicEncodingRules.BER, "04FF")] + [InlineData("0xFF-CER", PublicEncodingRules.CER, "04FF")] + [InlineData("0xFF-DER", PublicEncodingRules.DER, "04FF")] + [InlineData("CER definite constructed", PublicEncodingRules.CER, "30820500")] + [InlineData("BER indefinite primitive", PublicEncodingRules.BER, "0480" + "0000")] + [InlineData("CER indefinite primitive", PublicEncodingRules.CER, "0480" + "0000")] + [InlineData("DER indefinite primitive", PublicEncodingRules.DER, "0480" + "0000")] + [InlineData("DER non-minimal 0", PublicEncodingRules.DER, "048100")] + [InlineData("DER non-minimal 7F", PublicEncodingRules.DER, "04817F")] + [InlineData("DER non-minimal 80", PublicEncodingRules.DER, "04820080")] + [InlineData("CER non-minimal 0", PublicEncodingRules.CER, "048100")] + [InlineData("CER non-minimal 7F", PublicEncodingRules.CER, "04817F")] + [InlineData("CER non-minimal 80", PublicEncodingRules.CER, "04820080")] + [InlineData("BER too large", PublicEncodingRules.BER, "048480000000")] + [InlineData("CER too large", PublicEncodingRules.CER, "048480000000")] + [InlineData("DER too large", PublicEncodingRules.DER, "048480000000")] + [InlineData("BER padded too large", PublicEncodingRules.BER, "0486000080000000")] + [InlineData("BER uint.MaxValue", PublicEncodingRules.BER, "0484FFFFFFFF")] + [InlineData("CER uint.MaxValue", PublicEncodingRules.CER, "0484FFFFFFFF")] + [InlineData("DER uint.MaxValue", PublicEncodingRules.DER, "0484FFFFFFFF")] + [InlineData("BER padded uint.MaxValue", PublicEncodingRules.BER, "048800000000FFFFFFFF")] + [InlineData("BER 5 byte spread", PublicEncodingRules.BER, "04850100000000")] + [InlineData("CER 5 byte spread", PublicEncodingRules.CER, "04850100000000")] + [InlineData("DER 5 byte spread", PublicEncodingRules.DER, "04850100000000")] + [InlineData("BER padded 5 byte spread", PublicEncodingRules.BER, "0486000100000000")] + public static void InvalidLengths( + string description, + PublicEncodingRules rules, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)rules); + + Assert.Throws(() => reader.ReadTagAndLength(out _, out _)); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + public static void IndefiniteLength(PublicEncodingRules ruleSet) + { + // SEQUENCE (indefinite) + // NULL + // End-of-Contents + byte[] data = { 0x30, 0x80, 0x05, 0x00, 0x00, 0x00 }; + AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + + Asn1Tag tag = reader.ReadTagAndLength(out int? length, out int bytesRead); + + Assert.Equal(2, bytesRead); + Assert.False(length.HasValue, "length.HasValue"); + Assert.Equal((int)UniversalTagNumber.Sequence, tag.TagValue); + Assert.True(tag.IsConstructed, "tag.IsConstructed"); + } + + [Theory] + [InlineData(0, "0483000000")] + [InlineData(1, "048A00000000000000000001")] + [InlineData(128, "049000000000000000000000000000000080")] + public static void BerNonMinimalLength(int expectedLength, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); + + Asn1Tag tag = reader.ReadTagAndLength(out int? length, out int bytesRead); + + Assert.Equal(inputData.Length, bytesRead); + Assert.Equal(expectedLength, length.Value); + // ReadTagAndLength doesn't move the _data span forward. + Assert.True(reader.HasData, "reader.HasData"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, 4, 0, 5, "0483000000" + "0500")] + [InlineData(PublicEncodingRules.DER, 1, 1, 2, "0101" + "FF")] + [InlineData(PublicEncodingRules.CER, 0x10, null, 2, "3080" + "0500" + "0000")] + public static void ReadWithDataRemaining( + PublicEncodingRules ruleSet, + int tagValue, + int? expectedLength, + int expectedBytesRead, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Asn1Tag tag = reader.ReadTagAndLength(out int? length, out int bytesRead); + + Assert.Equal(expectedBytesRead, bytesRead); + Assert.Equal(tagValue, tag.TagValue); + Assert.Equal(expectedLength, length); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNamedBitList.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNamedBitList.cs new file mode 100644 index 0000000000..687831de4f --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNamedBitList.cs @@ -0,0 +1,315 @@ +// 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.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public sealed class ReadNamedBitList : Asn1ReaderTests + { + [Flags] + public enum X509KeyUsageCSharpStyle + { + None = 0, + DigitalSignature = 1, + NonRepudiation = 1 << 1, + KeyEncipherment = 1 << 2, + DataEncipherment = 1 << 3, + KeyAgreement = 1 << 4, + KeyCertSign = 1 << 5, + CrlSign = 1 << 6, + EncipherOnly = 1 << 7, + DecipherOnly = 1 << 8, + } + + [Flags] + public enum ULongFlags : ulong + { + None = 0, + Min = 1, + Mid = 1L << 32, + AlmostMax = 1L << 62, + Max = 1UL << 63, + } + + [Flags] + public enum LongFlags : long + { + None = 0, + Mid = 1L << 32, + Max = 1L << 62, + Min = long.MinValue, + } + + [Theory] + [InlineData( + PublicEncodingRules.BER, + typeof(X509KeyUsageCSharpStyle), + X509KeyUsageCSharpStyle.None, + "030100")] + [InlineData( + PublicEncodingRules.CER, + typeof(X509KeyUsageCSharpStyle), + X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign, + "0303070480")] + [InlineData( + PublicEncodingRules.DER, + typeof(X509KeyUsageCSharpStyle), + X509KeyUsageCSharpStyle.KeyAgreement, + "03020308")] + [InlineData( + PublicEncodingRules.BER, + typeof(LongFlags), + LongFlags.Mid | LongFlags.Max, + "0309010000000080000002")] + [InlineData( + PublicEncodingRules.CER, + typeof(LongFlags), + LongFlags.Mid | LongFlags.Min, + "0309000000000080000001")] + [InlineData( + PublicEncodingRules.DER, + typeof(LongFlags), + LongFlags.Min | LongFlags.Max, + "0309000000000000000003")] + // BER: Unused bits are unmapped, regardless of value. + [InlineData( + PublicEncodingRules.BER, + typeof(X509KeyUsageCSharpStyle), + X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign, + "030307048F")] + // BER: Trailing zeros are permitted. + [InlineData( + PublicEncodingRules.BER, + typeof(X509KeyUsageCSharpStyle), + X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign | X509KeyUsageCSharpStyle.DataEncipherment, + "03050014800000")] + // BER: Trailing 0-bits don't have to be declared "unused" + [InlineData( + PublicEncodingRules.BER, + typeof(X509KeyUsageCSharpStyle), + X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign | X509KeyUsageCSharpStyle.DataEncipherment, + "0303001480")] + public static void VerifyReadNamedBitListEncodings( + PublicEncodingRules ruleSet, + Type enumType, + long enumValue, + string inputHex) + { + byte[] inputBytes = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputBytes, (AsnEncodingRules)ruleSet); + Enum readValue = reader.GetNamedBitListValue(enumType); + + Assert.Equal(Enum.ToObject(enumType, enumValue), readValue); + } + + [Theory] + [InlineData( + PublicEncodingRules.BER, + typeof(ULongFlags), + ULongFlags.Mid | ULongFlags.Max, + "0309000000000080000001")] + [InlineData( + PublicEncodingRules.CER, + typeof(ULongFlags), + ULongFlags.Min | ULongFlags.Mid, + "0306078000000080")] + [InlineData( + PublicEncodingRules.DER, + typeof(ULongFlags), + ULongFlags.Min | ULongFlags.Max, + "0309008000000000000001")] + public static void VerifyReadNamedBitListEncodings_ULong( + PublicEncodingRules ruleSet, + Type enumType, + ulong enumValue, + string inputHex) + { + byte[] inputBytes = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputBytes, (AsnEncodingRules)ruleSet); + Enum readValue = reader.GetNamedBitListValue(enumType); + + Assert.Equal(Enum.ToObject(enumType, enumValue), readValue); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void VerifyGenericReadNamedBitList(PublicEncodingRules ruleSet) + { + string inputHex = "0306078000000080" + "0309010000000080000002"; + AsnReader reader = new AsnReader(inputHex.HexToByteArray(), (AsnEncodingRules)ruleSet); + + ULongFlags uLongFlags = reader.GetNamedBitListValue(); + LongFlags longFlags = reader.GetNamedBitListValue(); + + Assert.False(reader.HasData); + Assert.Equal(ULongFlags.Mid | ULongFlags.Min, uLongFlags); + Assert.Equal(LongFlags.Mid | LongFlags.Max, longFlags); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void ReadNamedBitList_RequiresFlags(PublicEncodingRules ruleSet) + { + string inputHex = "030100"; + AsnReader reader = new AsnReader(inputHex.HexToByteArray(), (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "tFlagsEnum", + () => reader.GetNamedBitListValue()); + + Assert.True(reader.HasData, "reader.HasData"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void ReadNamedBitList_DataOutOfRange(PublicEncodingRules ruleSet) + { + string inputHex = "0309000000000100000001"; + + AsnReader reader = new AsnReader(inputHex.HexToByteArray(), (AsnEncodingRules)ruleSet); + + Assert.Throws( + () => reader.GetNamedBitListValue()); + + Assert.True(reader.HasData, "reader.HasData"); + } + + [Theory] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void ReadNamedBitList_ExcessiveBytes(PublicEncodingRules ruleSet) + { + string inputHex = "03050014800000"; + + AsnReader reader = new AsnReader(inputHex.HexToByteArray(), (AsnEncodingRules)ruleSet); + + Assert.Throws( + () => reader.GetNamedBitListValue()); + + Assert.True(reader.HasData, "reader.HasData"); + } + + [Theory] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void ReadNamedBitList_ExcessiveBits(PublicEncodingRules ruleSet) + { + string inputHex = "0303061480"; + + AsnReader reader = new AsnReader(inputHex.HexToByteArray(), (AsnEncodingRules)ruleSet); + + Assert.Throws( + () => reader.GetNamedBitListValue()); + + Assert.True(reader.HasData, "reader.HasData"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) + { + byte[] inputData = { 3, 2, 1, 2 }; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.GetNamedBitListValue(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.GetNamedBitListValue(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + Assert.Equal( + X509KeyUsageCSharpStyle.CrlSign, + reader.GetNamedBitListValue()); + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) + { + byte[] inputData = { 0x87, 2, 2, 4 }; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.GetNamedBitListValue(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.GetNamedBitListValue()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.GetNamedBitListValue(new Asn1Tag(TagClass.Application, 0))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.GetNamedBitListValue(new Asn1Tag(TagClass.ContextSpecific, 1))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + Assert.Equal( + X509KeyUsageCSharpStyle.KeyCertSign, + reader.GetNamedBitListValue(new Asn1Tag(TagClass.ContextSpecific, 7))); + + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "0303070080", PublicTagClass.Universal, 3)] + [InlineData(PublicEncodingRules.CER, "0303070080", PublicTagClass.Universal, 3)] + [InlineData(PublicEncodingRules.DER, "0303070080", PublicTagClass.Universal, 3)] + [InlineData(PublicEncodingRules.BER, "8003070080", PublicTagClass.ContextSpecific, 0)] + [InlineData(PublicEncodingRules.CER, "4C03070080", PublicTagClass.Application, 12)] + [InlineData(PublicEncodingRules.DER, "DF8A4603070080", PublicTagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + PublicEncodingRules ruleSet, + string inputHex, + PublicTagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Equal( + X509KeyUsageCSharpStyle.DecipherOnly, + reader.GetNamedBitListValue( + new Asn1Tag((TagClass)tagClass, tagValue, true))); + + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Equal( + X509KeyUsageCSharpStyle.DecipherOnly, + reader.GetNamedBitListValue( + new Asn1Tag((TagClass)tagClass, tagValue, false))); + + Assert.False(reader.HasData); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNull.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNull.cs new file mode 100644 index 0000000000..570f3dabd3 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNull.cs @@ -0,0 +1,130 @@ +// 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.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public sealed class ReadNull : Asn1ReaderTests + { + [Theory] + [InlineData(PublicEncodingRules.BER, "0500")] + [InlineData(PublicEncodingRules.CER, "0500")] + [InlineData(PublicEncodingRules.DER, "0500")] + [InlineData(PublicEncodingRules.BER, "0583000000")] + public static void ReadNull_Success(PublicEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + reader.ReadNull(); + Assert.False(reader.HasData, "reader.HasData"); + } + + [Theory] + [InlineData("Long length", PublicEncodingRules.CER, "0583000000")] + [InlineData("Long length", PublicEncodingRules.DER, "0583000000")] + [InlineData("Constructed definite length", PublicEncodingRules.BER, "2500")] + [InlineData("Constructed definite length", PublicEncodingRules.DER, "2500")] + [InlineData("Constructed indefinite length", PublicEncodingRules.BER, "25800000")] + [InlineData("Constructed indefinite length", PublicEncodingRules.CER, "25800000")] + [InlineData("No length", PublicEncodingRules.BER, "05")] + [InlineData("No length", PublicEncodingRules.CER, "05")] + [InlineData("No length", PublicEncodingRules.DER, "05")] + [InlineData("No data", PublicEncodingRules.BER, "")] + [InlineData("No data", PublicEncodingRules.CER, "")] + [InlineData("No data", PublicEncodingRules.DER, "")] + [InlineData("NonEmpty", PublicEncodingRules.BER, "050100")] + [InlineData("NonEmpty", PublicEncodingRules.CER, "050100")] + [InlineData("NonEmpty", PublicEncodingRules.DER, "050100")] + [InlineData("Incomplete length", PublicEncodingRules.BER, "0581")] + public static void ReadNull_Throws(string description, PublicEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws(() => reader.ReadNull()); + } + + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) + { + byte[] inputData = { 5, 0 }; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadNull(new Asn1Tag(UniversalTagNumber.Integer))); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadNull(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + reader.ReadNull(); + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) + { + byte[] inputData = { 0x87, 0 }; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadNull(new Asn1Tag(UniversalTagNumber.Integer))); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadNull()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws(() => reader.ReadNull(new Asn1Tag(TagClass.Application, 0))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws(() => reader.ReadNull(new Asn1Tag(TagClass.ContextSpecific, 1))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + reader.ReadNull(new Asn1Tag(TagClass.ContextSpecific, 7)); + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "0500", PublicTagClass.Universal, 5)] + [InlineData(PublicEncodingRules.CER, "0500", PublicTagClass.Universal, 5)] + [InlineData(PublicEncodingRules.DER, "0500", PublicTagClass.Universal, 5)] + [InlineData(PublicEncodingRules.BER, "8000", PublicTagClass.ContextSpecific, 0)] + [InlineData(PublicEncodingRules.CER, "4C00", PublicTagClass.Application, 12)] + [InlineData(PublicEncodingRules.DER, "DF8A4600", PublicTagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + PublicEncodingRules ruleSet, + string inputHex, + PublicTagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + reader.ReadNull(new Asn1Tag((TagClass)tagClass, tagValue, true)); + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + reader.ReadNull(new Asn1Tag((TagClass)tagClass, tagValue, false)); + Assert.False(reader.HasData); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadObjectIdentifier.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadObjectIdentifier.cs new file mode 100644 index 0000000000..92322468e7 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadObjectIdentifier.cs @@ -0,0 +1,286 @@ +// 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.Security.Cryptography.Asn1; +using System.Text; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public sealed class ReadObjectIdentifier : Asn1ReaderTests + { + [Theory] + [InlineData("Wrong tag", PublicEncodingRules.BER, "010100")] + [InlineData("Wrong tag", PublicEncodingRules.CER, "010100")] + [InlineData("Wrong tag", PublicEncodingRules.DER, "010100")] + [InlineData("Overreaching length", PublicEncodingRules.BER, "0608883703")] + [InlineData("Overreaching length", PublicEncodingRules.CER, "0608883703")] + [InlineData("Overreaching length", PublicEncodingRules.DER, "0608883703")] + [InlineData("Zero length", PublicEncodingRules.BER, "0600")] + [InlineData("Zero length", PublicEncodingRules.CER, "0600")] + [InlineData("Zero length", PublicEncodingRules.DER, "0600")] + [InlineData("Constructed Definite Form", PublicEncodingRules.BER, "2605" + "0603883703")] + [InlineData("Constructed Indefinite Form", PublicEncodingRules.BER, "2680" + "0603883703" + "0000")] + [InlineData("Constructed Indefinite Form", PublicEncodingRules.CER, "2680" + "0603883703" + "0000")] + [InlineData("Unresolved carry-bit (first sub-identifier)", PublicEncodingRules.BER, "060188")] + [InlineData("Unresolved carry-bit (first sub-identifier)", PublicEncodingRules.CER, "060188")] + [InlineData("Unresolved carry-bit (first sub-identifier)", PublicEncodingRules.DER, "060188")] + [InlineData("Unresolved carry-bit (later sub-identifier)", PublicEncodingRules.BER, "0603883781")] + [InlineData("Unresolved carry-bit (later sub-identifier)", PublicEncodingRules.CER, "0603883781")] + [InlineData("Unresolved carry-bit (later sub-identifier)", PublicEncodingRules.DER, "0603883781")] + [InlineData("Sub-Identifier with leading 0x80", PublicEncodingRules.BER, "060488378001")] + [InlineData("Sub-Identifier with leading 0x80", PublicEncodingRules.CER, "060488378001")] + [InlineData("Sub-Identifier with leading 0x80", PublicEncodingRules.DER, "060488378001")] + public static void ReadObjectIdentifier_Throws( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws(() => reader.ReadObjectIdentifier(true)); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "0603883703", "2.999.3")] + [InlineData(PublicEncodingRules.CER, "06028837", "2.999")] + [InlineData(PublicEncodingRules.DER, "06068837C27B0302", "2.999.8571.3.2")] + [InlineData(PublicEncodingRules.BER, "0603550406", "2.5.4.6")] + [InlineData(PublicEncodingRules.CER, "06092A864886F70D010105", "1.2.840.113549.1.1.5")] + [InlineData(PublicEncodingRules.DER, "060100", "0.0")] + [InlineData(PublicEncodingRules.BER, "06080992268993F22C63", "0.9.2342.19200300.99")] + [InlineData( + PublicEncodingRules.DER, + "0616824F83F09DA7EBCFDEE0C7A1A7B2C0948CC8F9D77603", + // Using the rules of ITU-T-REC-X.667-201210 for 2.25.{UUID} unregistered arcs, and + // their sample value of f81d4fae-7dec-11d0-a765-00a0c91e6bf6 + // this is + // { joint-iso-itu-t(2) uuid(255) thatuuid(329800735698586629295641978511506172918) three(3) } + "2.255.329800735698586629295641978511506172918.3")] + public static void ReadObjectIdentifierAsString_Success( + PublicEncodingRules ruleSet, + string inputHex, + string expectedValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + string oidValue = reader.ReadObjectIdentifierAsString(); + Assert.Equal(expectedValue, oidValue); + } + + [Theory] + // Start at a UUID as a big integer. 128 semantic bits takes 19 + // content bytes to write down. Walk it backwards to 1. + // This uses the OID from the last case of ReadObjectIdentifierAsString_Success, but + // without the "255" arc (therefore the initial second arc is the UUID decimal value - 80) + [InlineData("061383F09DA7EBCFDEE0C7A1A7B2C0948CC8F9D776", "2.329800735698586629295641978511506172838")] + // Drop the last byte, clear the high bit in the last remaining byte, secondArc = (secondArc + 80) >> 7 - 80. + [InlineData("061283F09DA7EBCFDEE0C7A1A7B2C0948CC8F957", "2.2576568247645208041372202957121141895")] + [InlineData("061183F09DA7EBCFDEE0C7A1A7B2C0948CC879", "2.20129439434728187823220335602508841")] + [InlineData("061083F09DA7EBCFDEE0C7A1A7B2C0948C48", "2.157261245583813967368908871894520")] + [InlineData("060F83F09DA7EBCFDEE0C7A1A7B2C0940C", "2.1228603481123546620069600561596")] + [InlineData("060E83F09DA7EBCFDEE0C7A1A7B2C014", "2.9598464696277707969293754308")] + [InlineData("060D83F09DA7EBCFDEE0C7A1A7B240", "2.74988005439669593510107376")] + [InlineData("060C83F09DA7EBCFDEE0C7A1A732", "2.585843792497418699297634")] + [InlineData("060B83F09DA7EBCFDEE0C7A127", "2.4576904628886083588183")] + [InlineData("060A83F09DA7EBCFDEE0C721", "2.35757067413172527953")] + [InlineData("060983F09DA7EBCFDEE047", "2.279352089165410295")] + [InlineData("060883F09DA7EBCFDE60", "2.2182438196604688")] + [InlineData("060783F09DA7EBCF5E", "2.17050298410894")] + [InlineData("060683F09DA7EB4F", "2.133205456255")] + [InlineData("060583F09DA76B", "2.1040667547")] + [InlineData("060483F09D27", "2.8130135")] + [InlineData("060383F01D", "2.63437")] + [InlineData("06028370", "2.416")] + [InlineData("060103", "0.3")] + public static void VerifyMultiByteParsing(string inputHex, string expectedValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, AsnEncodingRules.DER); + + string oidValue = reader.ReadObjectIdentifierAsString(); + Assert.Equal(expectedValue, oidValue); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "06082A864886F70D0307", false, "3des")] + [InlineData(PublicEncodingRules.CER, "06082A864886F70D0307", true, "1.2.840.113549.3.7")] + [InlineData(PublicEncodingRules.DER, "0609608648016503040201", true, "2.16.840.1.101.3.4.2.1")] + [InlineData(PublicEncodingRules.BER, "0609608648016503040201", false, "sha256")] + public static void ReadObjectIdentifier_SkipFriendlyName( + PublicEncodingRules ruleSet, + string inputHex, + bool skipFriendlyName, + string expectedFriendlyName) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Oid oid = reader.ReadObjectIdentifier(skipFriendlyName); + Assert.Equal(expectedFriendlyName, oid.FriendlyName); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) + { + byte[] inputData = "06028837".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.GetIntegerBytes(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadObjectIdentifierAsString(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + Assert.Equal("2.999", reader.ReadObjectIdentifierAsString()); + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) + { + byte[] inputData = "87028837".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.GetIntegerBytes(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadObjectIdentifierAsString()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.ReadObjectIdentifierAsString(new Asn1Tag(TagClass.Application, 0))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadObjectIdentifierAsString(new Asn1Tag(TagClass.ContextSpecific, 1))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + Assert.Equal( + "2.999", + reader.ReadObjectIdentifierAsString(new Asn1Tag(TagClass.ContextSpecific, 7))); + + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "06028837", PublicTagClass.Universal, 6)] + [InlineData(PublicEncodingRules.CER, "06028837", PublicTagClass.Universal, 6)] + [InlineData(PublicEncodingRules.DER, "06028837", PublicTagClass.Universal, 6)] + [InlineData(PublicEncodingRules.BER, "80028837", PublicTagClass.ContextSpecific, 0)] + [InlineData(PublicEncodingRules.CER, "4C028837", PublicTagClass.Application, 12)] + [InlineData(PublicEncodingRules.DER, "DF8A46028837", PublicTagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + PublicEncodingRules ruleSet, + string inputHex, + PublicTagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + string val1 = reader.ReadObjectIdentifierAsString(new Asn1Tag((TagClass)tagClass, tagValue, true)); + + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + string val2 = reader.ReadObjectIdentifierAsString(new Asn1Tag((TagClass)tagClass, tagValue, false)); + + Assert.False(reader.HasData); + + Assert.Equal(val1, val2); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void ReadVeryLongOid(PublicEncodingRules ruleSet) + { + byte[] inputData = new byte[100000]; + // 06 83 02 00 00 (OBJECT IDENTIFIER, 65536 bytes). + inputData[0] = 0x06; + inputData[1] = 0x83; + inputData[2] = 0x01; + inputData[3] = 0x00; + inputData[4] = 0x00; + // and the rest are all zero. + + // The first byte produces "0.0". Each of the remaining 65535 bytes produce + // another ".0". + const int ExpectedLength = 65536 * 2 + 1; + StringBuilder builder = new StringBuilder(ExpectedLength); + builder.Append('0'); + + for (int i = 0; i <= ushort.MaxValue; i++) + { + builder.Append('.'); + builder.Append(0); + } + + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + string oidString = reader.ReadObjectIdentifierAsString(); + + Assert.Equal(ExpectedLength, oidString.Length); + Assert.Equal(builder.ToString(), oidString); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void ReadVeryLongOidArc(PublicEncodingRules ruleSet) + { + byte[] inputData = new byte[255]; + // 06 81 93 (OBJECT IDENTIFIER, 147 bytes). + inputData[0] = 0x06; + inputData[1] = 0x81; + inputData[2] = 0x93; + + // With 147 bytes we get 147*7 = 1029 value bits. + // The smallest legal number to encode would have a top byte of 0x81, + // leaving 1022 bits remaining. If they're all zero then we have 2^1022. + // + // Since it's our first sub-identifier it's really encoding "2.(2^1022 - 80)". + inputData[3] = 0x81; + // Leave the last byte as 0. + new Span(inputData, 4, 145).Fill(0x80); + + const string ExpectedOid = + "2." + + "449423283715578976932326297697256183404494244735576643183575" + + "202894331689513752407831771193306018840052800284699678483394" + + "146974422036041556232118576598685310944419733562163713190755" + + "549003115235298632707380212514422095376705856157203684782776" + + "352068092908376276711465745599868114846199290762088390824060" + + "56034224"; + + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + string oidString = reader.ReadObjectIdentifierAsString(); + Assert.Equal(ExpectedOid, oidString); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadOctetString.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadOctetString.cs new file mode 100644 index 0000000000..2283f4f95a --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadOctetString.cs @@ -0,0 +1,560 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public sealed class ReadOctetString : Asn1ReaderTests + { + [Theory] + [InlineData("Constructed Payload", PublicEncodingRules.BER, "2402040100")] + [InlineData("Constructed Payload-Indefinite", PublicEncodingRules.BER, "248004010000")] + // This value is actually invalid CER, but it returns false since it's not primitive and + // it isn't worth preempting the descent to find out it was invalid. + [InlineData("Constructed Payload-Indefinite", PublicEncodingRules.CER, "248004010000")] + public static void TryGetOctetStringBytes_Fails( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory contents); + + Assert.False(didRead, "reader.TryGetOctetStringBytes"); + Assert.Equal(0, contents.Length); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, 0, "0400")] + [InlineData(PublicEncodingRules.BER, 1, "040100")] + [InlineData(PublicEncodingRules.BER, 2, "040201FE")] + [InlineData(PublicEncodingRules.CER, 5, "040502FEEFF00C")] + [InlineData(PublicEncodingRules.DER, 2, "04020780")] + [InlineData(PublicEncodingRules.DER, 5, "040500FEEFF00D" + "0500")] + public static void TryGetOctetStringBytes_Success( + PublicEncodingRules ruleSet, + int expectedLength, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory contents); + + Assert.True(didRead, "reader.TryGetOctetStringBytes"); + Assert.Equal(expectedLength, contents.Length); + } + + [Theory] + [InlineData("Wrong Tag", PublicEncodingRules.BER, "0500")] + [InlineData("Wrong Tag", PublicEncodingRules.CER, "0500")] + [InlineData("Wrong Tag", PublicEncodingRules.DER, "0500")] + [InlineData("Bad Length", PublicEncodingRules.BER, "040200")] + [InlineData("Bad Length", PublicEncodingRules.CER, "040200")] + [InlineData("Bad Length", PublicEncodingRules.DER, "040200")] + [InlineData("Constructed Form", PublicEncodingRules.DER, "2403040100")] + public static void TryGetOctetStringBytes_Throws( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws( + () => reader.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory contents)); + } + + [Fact] + public static void TryGetOctetStringBytes_Throws_CER_TooLong() + { + // CER says that the maximum encoding length for an OctetString primitive + // is 1000. + // + // So we need 04 [1001] { 1001 0x00s } + // 1001 => 0x3E9, so the length encoding is 82 03 E9. + // 1001 + 3 + 1 == 1005 + byte[] input = new byte[1005]; + input[0] = 0x04; + input[1] = 0x82; + input[2] = 0x03; + input[3] = 0xE9; + + AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); + + Assert.Throws( + () => reader.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory contents)); + } + + [Fact] + public static void TryGetOctetStringBytes_Success_CER_MaxLength() + { + // CER says that the maximum encoding length for an OctetString primitive + // is 1000. + // + // So we need 04 [1000] { 1000 anythings } + // 1000 => 0x3E8, so the length encoding is 82 03 E8. + // 1000 + 3 + 1 == 1004 + byte[] input = new byte[1004]; + input[0] = 0x04; + input[1] = 0x82; + input[2] = 0x03; + input[3] = 0xE8; + + // Contents + input[4] = 0x02; + input[5] = 0xA0; + input[1002] = 0xA5; + input[1003] = 0xFC; + + AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); + + bool success = reader.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory contents); + + Assert.True(success, "reader.TryGetOctetStringBytes"); + Assert.Equal(1000, contents.Length); + + // Check that it is, in fact, the same memory. No copies with this API. + Assert.True( + Unsafe.AreSame( + ref MemoryMarshal.GetReference(contents.Span), + ref input[4])); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "04020780")] + [InlineData(PublicEncodingRules.BER, "040207FF")] + [InlineData(PublicEncodingRules.CER, "04020780")] + [InlineData(PublicEncodingRules.DER, "04020780")] + [InlineData( + PublicEncodingRules.BER, + "2480" + + "2480" + + "0000" + + "04020000" + + "0000")] + public static void TryCopyOctetStringBytes_Fails(PublicEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryCopyOctetStringBytes( + Span.Empty, + out int bytesWritten); + + Assert.False(didRead, "reader.TryCopyOctetStringBytes"); + Assert.Equal(0, bytesWritten); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "04020780", "0780")] + [InlineData(PublicEncodingRules.BER, "040207FF", "07FF")] + [InlineData(PublicEncodingRules.CER, "04020780", "0780")] + [InlineData(PublicEncodingRules.DER, "04020680", "0680")] + [InlineData(PublicEncodingRules.BER, "24800000", "")] + [InlineData(PublicEncodingRules.BER, "2400", "")] + [InlineData(PublicEncodingRules.BER, "2400" + "0500", "")] + [InlineData( + PublicEncodingRules.BER, + "2480" + + "2480" + + "0000" + + "04020005" + + "0000", + "0005")] + [InlineData( + PublicEncodingRules.BER, + "2480" + + "2406" + + "0401FA" + + "0401CE" + + "2480" + + "2480" + + "2480" + + "0402F00D" + + "0000" + + "0000" + + "04020001" + + "0000" + + "0403000203" + + "040203FF" + + "2480" + + "0000" + + "0000", + "FACEF00D000100020303FF")] + public static void TryCopyOctetStringBytes_Success( + PublicEncodingRules ruleSet, + string inputHex, + string expectedHex) + { + byte[] inputData = inputHex.HexToByteArray(); + byte[] output = new byte[expectedHex.Length / 2]; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + bool didRead = reader.TryCopyOctetStringBytes( + output, + out int bytesWritten); + + Assert.True(didRead, "reader.TryCopyOctetStringBytes"); + Assert.Equal(expectedHex, output.AsReadOnlySpan().Slice(0, bytesWritten).ByteArrayToHex()); + } + + private static void TryCopyOctetStringBytes_Throws( + PublicEncodingRules ruleSet, + byte[] input) + { + Assert.Throws( + () => + { + AsnReader reader = new AsnReader(input, (AsnEncodingRules)ruleSet); + reader.TryCopyOctetStringBytes( + Span.Empty, + out int bytesWritten); + }); + } + + [Theory] + [InlineData("Wrong Tag", PublicEncodingRules.BER, "0500")] + [InlineData("Wrong Tag", PublicEncodingRules.CER, "0500")] + [InlineData("Wrong Tag", PublicEncodingRules.DER, "0500")] + [InlineData("Bad Length", PublicEncodingRules.BER, "040200")] + [InlineData("Bad Length", PublicEncodingRules.CER, "040200")] + [InlineData("Bad Length", PublicEncodingRules.DER, "040200")] + [InlineData("Constructed Form", PublicEncodingRules.DER, "2403040100")] + [InlineData("Nested context-specific", PublicEncodingRules.BER, "2404800400FACE")] + [InlineData("Nested context-specific (indef)", PublicEncodingRules.BER, "2480800400FACE0000")] + [InlineData("Nested context-specific (indef)", PublicEncodingRules.CER, "2480800400FACE0000")] + [InlineData("Nested boolean", PublicEncodingRules.BER, "2403010100")] + [InlineData("Nested boolean (indef)", PublicEncodingRules.BER, "24800101000000")] + [InlineData("Nested boolean (indef)", PublicEncodingRules.CER, "24800101000000")] + [InlineData("Nested constructed form", PublicEncodingRules.CER, "2480" + "2480" + "04010" + "000000000")] + [InlineData("No terminator", PublicEncodingRules.BER, "2480" + "04020000" + "")] + [InlineData("No terminator", PublicEncodingRules.CER, "2480" + "04020000" + "")] + [InlineData("No content", PublicEncodingRules.BER, "2480")] + [InlineData("No content", PublicEncodingRules.CER, "2480")] + [InlineData("No nested content", PublicEncodingRules.CER, "24800000")] + [InlineData("Nested value too long", PublicEncodingRules.BER, "2480040A00")] + [InlineData("Nested value too long - constructed", PublicEncodingRules.BER, "2480240A00")] + [InlineData("Nested value too long - simple", PublicEncodingRules.BER, "2403" + "04050000000000")] + [InlineData("Constructed EndOfContents", PublicEncodingRules.BER, "248020000000")] + [InlineData("Constructed EndOfContents", PublicEncodingRules.CER, "248020000000")] + [InlineData("NonEmpty EndOfContents", PublicEncodingRules.BER, "2480000100")] + [InlineData("NonEmpty EndOfContents", PublicEncodingRules.CER, "2480000100")] + [InlineData("LongLength EndOfContents", PublicEncodingRules.BER, "2480008100")] + [InlineData("Constructed Payload-TooShort", PublicEncodingRules.CER, "24800401000000")] + public static void TryCopyOctetStringBytes_Throws( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + TryCopyOctetStringBytes_Throws(ruleSet, inputData); + } + + [Fact] + public static void TryCopyOctetStringBytes_Throws_CER_NestedTooLong() + { + // CER says that the maximum encoding length for an OctetString primitive + // is 1000. + // + // This test checks it for a primitive contained within a constructed. + // + // So we need 04 [1001] { 1001 0x00s } + // 1001 => 0x3E9, so the length encoding is 82 03 E9. + // 1001 + 3 + 1 == 1005 + // + // Plus a leading 24 80 (indefinite length constructed) + // and a trailing 00 00 (End of contents) + // == 1009 + byte[] input = new byte[1009]; + // CONSTRUCTED OCTET STRING (indefinite) + input[0] = 0x24; + input[1] = 0x80; + // OCTET STRING (1001) + input[2] = 0x04; + input[3] = 0x82; + input[4] = 0x03; + input[5] = 0xE9; + // EOC implicit since the byte[] initializes to zeros + + TryCopyOctetStringBytes_Throws(PublicEncodingRules.CER, input); + } + + [Fact] + public static void TryCopyOctetStringBytes_Throws_CER_NestedTooShortIntermediate() + { + // CER says that the maximum encoding length for an OctetString primitive + // is 1000, and in the constructed form the lengths must be + // [ 1000, 1000, 1000, ..., len%1000 ] + // + // So 1000, 2, 2 is illegal. + // + // 24 80 (indefinite constructed octet string) + // 04 82 03 08 (octet string, 1000 bytes) + // [1000 content bytes] + // 04 02 (octet string, 2 bytes) + // [2 content bytes] + // 04 02 (octet string, 2 bytes) + // [2 content bytes] + // 00 00 (end of contents) + // Looks like 1,016 bytes. + byte[] input = new byte[1016]; + // CONSTRUCTED OCTET STRING (indefinite) + input[0] = 0x23; + input[1] = 0x80; + // OCTET STRING (1000) + input[2] = 0x04; + input[3] = 0x82; + input[4] = 0x03; + input[5] = 0xE8; + // OCTET STRING (2) + input[1006] = 0x04; + input[1007] = 0x02; + // OCTET STRING (2) + input[1010] = 0x04; + input[1011] = 0x02; + // EOC implicit since the byte[] initializes to zeros + + TryCopyOctetStringBytes_Throws(PublicEncodingRules.CER, input); + } + + [Fact] + public static void TryCopyOctetStringBytes_Success_CER_MaxPrimitiveLength() + { + // CER says that the maximum encoding length for an OctetString primitive + // is 1000. + // + // So we need 04 [1000] { 1000 anythings } + // 1000 => 0x3E8, so the length encoding is 82 03 E8. + // 1000 + 3 + 1 == 1004 + byte[] input = new byte[1004]; + input[0] = 0x04; + input[1] = 0x82; + input[2] = 0x03; + input[3] = 0xE8; + + // Content + input[4] = 0x02; + input[5] = 0xA0; + input[1002] = 0xA5; + input[1003] = 0xFC; + + byte[] output = new byte[1000]; + + AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); + + bool success = reader.TryCopyOctetStringBytes( + output, + out int bytesWritten); + + Assert.True(success, "reader.TryCopyOctetStringBytes"); + Assert.Equal(1000, bytesWritten); + + Assert.Equal( + input.AsReadOnlySpan().Slice(4).ByteArrayToHex(), + output.ByteArrayToHex()); + } + + [Fact] + public static void TryCopyOctetStringBytes_Success_CER_MinConstructedLength() + { + // CER says that the maximum encoding length for an OctetString primitive + // is 1000, and that a constructed form must be used for values greater + // than 1000 bytes, with segments dividing up for each thousand + // [1000, 1000, ..., len%1000]. + // + // So our smallest constructed form is 1001 bytes, [1000, 1] + // + // 24 80 (indefinite constructed octet string) + // 04 82 03 E9 (primitive octet string, 1000 bytes) + // [1000 content bytes] + // 04 01 (primitive octet string, 1 byte) + // pp + // 00 00 (end of contents, 0 bytes) + // 1011 total. + byte[] input = new byte[1011]; + int offset = 0; + // CONSTRUCTED OCTET STRING (Indefinite) + input[offset++] = 0x24; + input[offset++] = 0x80; + // OCTET STRING (1000) + input[offset++] = 0x04; + input[offset++] = 0x82; + input[offset++] = 0x03; + input[offset++] = 0xE8; + + // Primitive 1: (55 A0 :: A5 FC) (1000) + input[offset++] = 0x55; + input[offset] = 0xA0; + offset += 997; + input[offset++] = 0xA5; + input[offset++] = 0xFC; + + // OCTET STRING (1) + input[offset++] = 0x04; + input[offset++] = 0x01; + + // Primitive 2: One more byte + input[offset] = 0xF7; + + byte[] expected = new byte[1001]; + offset = 0; + expected[offset++] = 0x55; + expected[offset] = 0xA0; + offset += 997; + expected[offset++] = 0xA5; + expected[offset++] = 0xFC; + expected[offset] = 0xF7; + + byte[] output = new byte[1001]; + + AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); + + bool success = reader.TryCopyOctetStringBytes( + output, + out int bytesWritten); + + Assert.True(success, "reader.TryCopyOctetStringBytes"); + Assert.Equal(1001, bytesWritten); + + Assert.Equal( + expected.ByteArrayToHex(), + output.ByteArrayToHex()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) + { + byte[] inputData = { 4, 1, 0x7E }; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.TryGetPrimitiveOctetStringBytes(Asn1Tag.Null, out _)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.TryGetPrimitiveOctetStringBytes(new Asn1Tag(TagClass.ContextSpecific, 0), out _)); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + Assert.True(reader.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory value)); + Assert.Equal("7E", value.ByteArrayToHex()); + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) + { + byte[] inputData = { 0x87, 2, 0, 0x80 }; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.TryGetPrimitiveOctetStringBytes(Asn1Tag.Null, out _)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.TryGetPrimitiveOctetStringBytes(out _)); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.TryGetPrimitiveOctetStringBytes(new Asn1Tag(TagClass.Application, 0), out _)); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.TryGetPrimitiveOctetStringBytes(new Asn1Tag(TagClass.ContextSpecific, 1), out _)); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + Assert.True( + reader.TryGetPrimitiveOctetStringBytes( + new Asn1Tag(TagClass.ContextSpecific, 7), + out ReadOnlyMemory value)); + + Assert.Equal("0080", value.ByteArrayToHex()); + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "0401FF", PublicTagClass.Universal, 4)] + [InlineData(PublicEncodingRules.CER, "0401FF", PublicTagClass.Universal, 4)] + [InlineData(PublicEncodingRules.DER, "0401FF", PublicTagClass.Universal, 4)] + [InlineData(PublicEncodingRules.BER, "8001FF", PublicTagClass.ContextSpecific, 0)] + [InlineData(PublicEncodingRules.CER, "4C01FF", PublicTagClass.Application, 12)] + [InlineData(PublicEncodingRules.DER, "DF8A4601FF", PublicTagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + PublicEncodingRules ruleSet, + string inputHex, + PublicTagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.True( + reader.TryGetPrimitiveOctetStringBytes( + new Asn1Tag((TagClass)tagClass, tagValue, true), + out ReadOnlyMemory val1)); + + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.True( + reader.TryGetPrimitiveOctetStringBytes( + new Asn1Tag((TagClass)tagClass, tagValue, false), + out ReadOnlyMemory val2)); + + Assert.False(reader.HasData); + + Assert.Equal(val1.ByteArrayToHex(), val2.ByteArrayToHex()); + } + + [Fact] + public static void TryCopyOctetStringBytes_ExtremelyNested() + { + byte[] dataBytes = new byte[4 * 16384]; + + // This will build 2^14 nested indefinite length values. + // In the end, none of them contain any content. + // + // For what it's worth, the initial algorithm succeeded at 1061, and StackOverflowed with 1062. + int end = dataBytes.Length / 2; + + // UNIVERSAL OCTET STRING [Constructed] + const byte Tag = 0x20 | (byte)UniversalTagNumber.OctetString; + + for (int i = 0; i < end; i += 2) + { + dataBytes[i] = Tag; + // Indefinite length + dataBytes[i + 1] = 0x80; + } + + AsnReader reader = new AsnReader(dataBytes, AsnEncodingRules.BER); + + int bytesWritten; + + Assert.True(reader.TryCopyOctetStringBytes(Span.Empty, out bytesWritten)); + Assert.Equal(0, bytesWritten); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSequence.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSequence.cs new file mode 100644 index 0000000000..2c22fc89e9 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSequence.cs @@ -0,0 +1,338 @@ +// 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.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public sealed class ReadSequence : Asn1ReaderTests + { + [Theory] + [InlineData(PublicEncodingRules.BER, "3000", false, -1)] + [InlineData(PublicEncodingRules.BER, "30800000", false, -1)] + [InlineData(PublicEncodingRules.BER, "3083000000", false, -1)] + [InlineData(PublicEncodingRules.CER, "30800000", false, -1)] + [InlineData(PublicEncodingRules.DER, "3000", false, -1)] + [InlineData(PublicEncodingRules.BER, "3000" + "0500", true, -1)] + [InlineData(PublicEncodingRules.BER, "3002" + "0500", false, 5)] + [InlineData(PublicEncodingRules.CER, "3080" + "0500" + "0000", false, 5)] + [InlineData(PublicEncodingRules.CER, "3080" + "010100" + "0000" + "0500", true, 1)] + [InlineData(PublicEncodingRules.DER, "3005" + "0500" + "0101FF", false, 5)] + public static void ReadSequence_Success( + PublicEncodingRules ruleSet, + string inputHex, + bool expectDataRemaining, + int expectedSequenceTagNumber) + { + byte[] inputData = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader sequence = reader.ReadSequence(); + + if (expectDataRemaining) + { + Assert.True(reader.HasData, "reader.HasData"); + } + else + { + Assert.False(reader.HasData, "reader.HasData"); + } + + if (expectedSequenceTagNumber < 0) + { + Assert.False(sequence.HasData, "sequence.HasData"); + } + else + { + Assert.True(sequence.HasData, "sequence.HasData"); + + Asn1Tag firstTag = sequence.PeekTag(); + Assert.Equal(expectedSequenceTagNumber, firstTag.TagValue); + } + } + + [Theory] + [InlineData("Empty", PublicEncodingRules.BER, "")] + [InlineData("Empty", PublicEncodingRules.CER, "")] + [InlineData("Empty", PublicEncodingRules.DER, "")] + [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] + [InlineData("Missing Length", PublicEncodingRules.BER, "30")] + [InlineData("Missing Length", PublicEncodingRules.CER, "30")] + [InlineData("Missing Length", PublicEncodingRules.DER, "30")] + [InlineData("Primitive Encoding", PublicEncodingRules.BER, "1000")] + [InlineData("Primitive Encoding", PublicEncodingRules.CER, "1000")] + [InlineData("Primitive Encoding", PublicEncodingRules.DER, "1000")] + [InlineData("Definite Length Encoding", PublicEncodingRules.CER, "3000")] + [InlineData("Indefinite Length Encoding", PublicEncodingRules.DER, "3080" + "0000")] + [InlineData("Missing Content", PublicEncodingRules.BER, "3001")] + [InlineData("Missing Content", PublicEncodingRules.DER, "3001")] + [InlineData("Length Out Of Bounds", PublicEncodingRules.BER, "3005" + "010100")] + [InlineData("Length Out Of Bounds", PublicEncodingRules.DER, "3005" + "010100")] + [InlineData("Missing Content - Indefinite", PublicEncodingRules.BER, "3080")] + [InlineData("Missing Content - Indefinite", PublicEncodingRules.CER, "3080")] + [InlineData("Missing EoC", PublicEncodingRules.BER, "3080" + "010100")] + [InlineData("Missing EoC", PublicEncodingRules.CER, "3080" + "010100")] + [InlineData("Missing Outer EoC", PublicEncodingRules.BER, "3080" + "010100" + ("3080" + "0000"))] + [InlineData("Missing Outer EoC", PublicEncodingRules.CER, "3080" + "010100" + ("3080" + "0000"))] + [InlineData("Wrong Tag - Definite", PublicEncodingRules.BER, "3100")] + [InlineData("Wrong Tag - Definite", PublicEncodingRules.DER, "3100")] + [InlineData("Wrong Tag - Indefinite", PublicEncodingRules.BER, "3180" + "0000")] + [InlineData("Wrong Tag - Indefinite", PublicEncodingRules.CER, "3180" + "0000")] + public static void ReadSequence_Throws( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws(() => reader.ReadSequence()); + } + + private static void ReadEcPublicKey(AsnEncodingRules ruleSet, byte[] inputData) + { + AsnReader mainReader = new AsnReader(inputData, ruleSet); + + AsnReader spkiReader = mainReader.ReadSequence(); + Assert.False(mainReader.HasData, "mainReader.HasData after reading SPKI"); + + AsnReader algorithmReader = spkiReader.ReadSequence(); + Assert.True(spkiReader.HasData, "spkiReader.HasData after reading algorithm"); + + ReadOnlyMemory publicKeyValue; + int unusedBitCount; + + if (!spkiReader.TryGetPrimitiveBitStringValue(out unusedBitCount, out publicKeyValue)) + { + // The correct answer is 65 bytes. + for (int i = 10; ; i *= 2) + { + byte[] buf = new byte[i]; + + if (spkiReader.TryCopyBitStringBytes(buf, out unusedBitCount, out int bytesWritten)) + { + publicKeyValue = new ReadOnlyMemory(buf, 0, bytesWritten); + break; + } + } + } + + Assert.False(spkiReader.HasData, "spkiReader.HasData after reading subjectPublicKey"); + Assert.True(algorithmReader.HasData, "algorithmReader.HasData before reading"); + + Oid algorithmOid = algorithmReader.ReadObjectIdentifier(true); + Assert.True(algorithmReader.HasData, "algorithmReader.HasData after reading first OID"); + + Assert.Equal("1.2.840.10045.2.1", algorithmOid.Value); + + Oid curveOid = algorithmReader.ReadObjectIdentifier(true); + Assert.False(algorithmReader.HasData, "algorithmReader.HasData after reading second OID"); + + Assert.Equal("1.2.840.10045.3.1.7", curveOid.Value); + + const string PublicKeyValue = + "04" + + "2363DD131DA65E899A2E63E9E05E50C830D4994662FFE883DB2B9A767DCCABA2" + + "F07081B5711BE1DEE90DFC8DE17970C2D937A16CD34581F52B8D59C9E9532D13"; + + Assert.Equal(PublicKeyValue, publicKeyValue.ByteArrayToHex()); + Assert.Equal(0, unusedBitCount); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.DER)] + public static void ReadEcPublicKey_DefiniteLength(PublicEncodingRules ruleSet) + { + const string InputHex = + "3059" + + "3013" + + "06072A8648CE3D0201" + + "06082A8648CE3D030107" + + "0342" + + "00" + + "04" + + "2363DD131DA65E899A2E63E9E05E50C830D4994662FFE883DB2B9A767DCCABA2" + + "F07081B5711BE1DEE90DFC8DE17970C2D937A16CD34581F52B8D59C9E9532D13"; + + byte[] inputData = InputHex.HexToByteArray(); + ReadEcPublicKey((AsnEncodingRules)ruleSet, inputData); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + public static void ReadEcPublicKey_IndefiniteLength(PublicEncodingRules ruleSet) + { + const string InputHex = + "3080" + + "3080" + + "06072A8648CE3D0201" + + "06082A8648CE3D030107" + + "0000" + + "0342" + + "00" + + "04" + + "2363DD131DA65E899A2E63E9E05E50C830D4994662FFE883DB2B9A767DCCABA2" + + "F07081B5711BE1DEE90DFC8DE17970C2D937A16CD34581F52B8D59C9E9532D13" + + "0000"; + + byte[] inputData = InputHex.HexToByteArray(); + ReadEcPublicKey((AsnEncodingRules)ruleSet, inputData); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Universal_Definite(PublicEncodingRules ruleSet) + { + byte[] inputData = "30020500".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSequence(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + AsnReader seq = reader.ReadSequence(); + Assert.Equal("0500", seq.GetEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + public static void TagMustBeCorrect_Universal_Indefinite(PublicEncodingRules ruleSet) + { + byte[] inputData = "308005000000".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSequence(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + AsnReader seq = reader.ReadSequence(); + Assert.Equal("0500", seq.GetEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Custom_Definite(PublicEncodingRules ruleSet) + { + byte[] inputData = "A5020500".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSequence(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadSequence()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.ReadSequence(new Asn1Tag(TagClass.Application, 5))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 7))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + AsnReader seq = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); + Assert.Equal("0500", seq.GetEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + public static void TagMustBeCorrect_Custom_Indefinite(PublicEncodingRules ruleSet) + { + byte[] inputData = "A58005000000".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSequence(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadSequence()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.ReadSequence(new Asn1Tag(TagClass.Application, 5))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 7))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + AsnReader seq = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); + Assert.Equal("0500", seq.GetEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "30030101FF", PublicTagClass.Universal, 16)] + [InlineData(PublicEncodingRules.BER, "30800101000000", PublicTagClass.Universal, 16)] + [InlineData(PublicEncodingRules.CER, "30800101000000", PublicTagClass.Universal, 16)] + [InlineData(PublicEncodingRules.DER, "30030101FF", PublicTagClass.Universal, 16)] + [InlineData(PublicEncodingRules.BER, "A0030101FF", PublicTagClass.ContextSpecific, 0)] + [InlineData(PublicEncodingRules.BER, "A1800101000000", PublicTagClass.ContextSpecific, 1)] + [InlineData(PublicEncodingRules.CER, "6C800101000000", PublicTagClass.Application, 12)] + [InlineData(PublicEncodingRules.DER, "FF8A46030101FF", PublicTagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + PublicEncodingRules ruleSet, + string inputHex, + PublicTagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AsnReader val1 = reader.ReadSequence(new Asn1Tag((TagClass)tagClass, tagValue, true)); + + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AsnReader val2 = reader.ReadSequence(new Asn1Tag((TagClass)tagClass, tagValue, false)); + + Assert.False(reader.HasData); + + Assert.Equal(val1.GetEncodedValue().ByteArrayToHex(), val2.GetEncodedValue().ByteArrayToHex()); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSetOf.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSetOf.cs new file mode 100644 index 0000000000..369b203492 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSetOf.cs @@ -0,0 +1,304 @@ +// 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.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public sealed class ReadSetOf : Asn1ReaderTests + { + [Theory] + [InlineData(PublicEncodingRules.BER, "3100", false, -1)] + [InlineData(PublicEncodingRules.BER, "31800000", false, -1)] + [InlineData(PublicEncodingRules.BER, "3183000000", false, -1)] + [InlineData(PublicEncodingRules.CER, "31800000", false, -1)] + [InlineData(PublicEncodingRules.DER, "3100", false, -1)] + [InlineData(PublicEncodingRules.BER, "3100" + "0500", true, -1)] + [InlineData(PublicEncodingRules.BER, "3102" + "0500", false, 5)] + [InlineData(PublicEncodingRules.CER, "3180" + "0500" + "0000", false, 5)] + [InlineData(PublicEncodingRules.CER, "3180" + "010100" + "0000" + "0500", true, 1)] + [InlineData(PublicEncodingRules.CER, "3180" + "010100" + "0101FF" + "0500" + "0000", false, 1)] + [InlineData(PublicEncodingRules.DER, "3105" + "0101FF" + "0500", false, 1)] + public static void ReadSetOf_Success( + PublicEncodingRules ruleSet, + string inputHex, + bool expectDataRemaining, + int expectedSequenceTagNumber) + { + byte[] inputData = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader sequence = reader.ReadSetOf(); + + if (expectDataRemaining) + { + Assert.True(reader.HasData, "reader.HasData"); + } + else + { + Assert.False(reader.HasData, "reader.HasData"); + } + + if (expectedSequenceTagNumber < 0) + { + Assert.False(sequence.HasData, "sequence.HasData"); + } + else + { + Assert.True(sequence.HasData, "sequence.HasData"); + + Asn1Tag firstTag = sequence.PeekTag(); + Assert.Equal(expectedSequenceTagNumber, firstTag.TagValue); + } + } + + [Theory] + [InlineData("Empty", PublicEncodingRules.BER, "")] + [InlineData("Empty", PublicEncodingRules.CER, "")] + [InlineData("Empty", PublicEncodingRules.DER, "")] + [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] + [InlineData("Missing Length", PublicEncodingRules.BER, "31")] + [InlineData("Missing Length", PublicEncodingRules.CER, "31")] + [InlineData("Missing Length", PublicEncodingRules.DER, "31")] + [InlineData("Primitive Encoding", PublicEncodingRules.BER, "1100")] + [InlineData("Primitive Encoding", PublicEncodingRules.CER, "1100")] + [InlineData("Primitive Encoding", PublicEncodingRules.DER, "1100")] + [InlineData("Definite Length Encoding", PublicEncodingRules.CER, "3100")] + [InlineData("Indefinite Length Encoding", PublicEncodingRules.DER, "3180" + "0000")] + [InlineData("Missing Content", PublicEncodingRules.BER, "3101")] + [InlineData("Missing Content", PublicEncodingRules.DER, "3101")] + [InlineData("Length Out Of Bounds", PublicEncodingRules.BER, "3105" + "010100")] + [InlineData("Length Out Of Bounds", PublicEncodingRules.DER, "3105" + "010100")] + [InlineData("Missing Content - Indefinite", PublicEncodingRules.BER, "3180")] + [InlineData("Missing Content - Indefinite", PublicEncodingRules.CER, "3180")] + [InlineData("Missing EoC", PublicEncodingRules.BER, "3180" + "010100")] + [InlineData("Missing EoC", PublicEncodingRules.CER, "3180" + "010100")] + [InlineData("Missing Outer EoC", PublicEncodingRules.BER, "3180" + "010100" + ("3180" + "0000"))] + [InlineData("Missing Outer EoC", PublicEncodingRules.CER, "3180" + "010100" + ("3180" + "0000"))] + [InlineData("Wrong Tag - Definite", PublicEncodingRules.BER, "3000")] + [InlineData("Wrong Tag - Definite", PublicEncodingRules.DER, "3000")] + [InlineData("Wrong Tag - Indefinite", PublicEncodingRules.BER, "3080" + "0000")] + [InlineData("Wrong Tag - Indefinite", PublicEncodingRules.CER, "3080" + "0000")] + public static void ReadSetOf_Throws( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws(() => reader.ReadSetOf()); + } + + [Theory] + // BER can read out of order (indefinite) + [InlineData(PublicEncodingRules.BER, "3180" + "0101FF" + "010100" + "0000", true, 1)] + // BER can read out of order (definite) + [InlineData(PublicEncodingRules.BER, "3106" + "0101FF" + "010100", true, 1)] + // CER will not read out of order + [InlineData(PublicEncodingRules.CER, "3180" + "0500" + "010100" + "0000", false, 1)] + [InlineData(PublicEncodingRules.CER, "3180" + "0101FF" + "010100" + "0000", false, 1)] + // CER is happy in order: + [InlineData(PublicEncodingRules.CER, "3180" + "010100" + "0500" + "0000", true, 5)] + [InlineData(PublicEncodingRules.CER, "3180" + "010100" + "0101FF" + "0500" + "0000", true, 5)] + [InlineData(PublicEncodingRules.CER, "3180" + "010100" + "010100" + "0500" + "0000", true, 5)] + // DER will not read out of order + [InlineData(PublicEncodingRules.DER, "3106" + "0101FF" + "010100", false, 1)] + [InlineData(PublicEncodingRules.DER, "3105" + "0500" + "010100", false, 1)] + // DER is happy in order: + [InlineData(PublicEncodingRules.DER, "3105" + "010100" + "0500", true, 5)] + [InlineData(PublicEncodingRules.DER, "3108" + "010100" + "0101FF" + "0500", true, 5)] + [InlineData(PublicEncodingRules.DER, "3108" + "010100" + "010100" + "0500", true, 5)] + public static void ReadSetOf_DataSorting( + PublicEncodingRules ruleSet, + string inputHex, + bool expectSuccess, + int lastTagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader setOf; + + if (expectSuccess) + { + setOf = reader.ReadSetOf(); + } + else + { + AsnReader alsoReader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws(() => alsoReader.ReadSetOf()); + + setOf = reader.ReadSetOf(skipSortOrderValidation: true); + } + + int lastTag = -1; + + while (setOf.HasData) + { + Asn1Tag tag = setOf.PeekTag(); + lastTag = tag.TagValue; + + // Ignore the return, just drain it. + setOf.GetEncodedValue(); + } + + Assert.Equal(lastTagValue, lastTag); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Universal_Definite(PublicEncodingRules ruleSet) + { + byte[] inputData = "31020500".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSetOf(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + AsnReader seq = reader.ReadSetOf(); + Assert.Equal("0500", seq.GetEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + public static void TagMustBeCorrect_Universal_Indefinite(PublicEncodingRules ruleSet) + { + byte[] inputData = "318005000000".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSetOf(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + AsnReader seq = reader.ReadSetOf(); + Assert.Equal("0500", seq.GetEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Custom_Definite(PublicEncodingRules ruleSet) + { + byte[] inputData = "A5020500".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSetOf(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadSetOf()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.ReadSetOf(new Asn1Tag(TagClass.Application, 5))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 7))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + AsnReader seq = reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 5)); + Assert.Equal("0500", seq.GetEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + public static void TagMustBeCorrect_Custom_Indefinite(PublicEncodingRules ruleSet) + { + byte[] inputData = "A58005000000".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSetOf(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadSetOf()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.ReadSetOf(new Asn1Tag(TagClass.Application, 5))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 7))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + AsnReader seq = reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 5)); + Assert.Equal("0500", seq.GetEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "31030101FF", PublicTagClass.Universal, 17)] + [InlineData(PublicEncodingRules.BER, "31800101000000", PublicTagClass.Universal, 17)] + [InlineData(PublicEncodingRules.CER, "31800101000000", PublicTagClass.Universal, 17)] + [InlineData(PublicEncodingRules.DER, "31030101FF", PublicTagClass.Universal, 17)] + [InlineData(PublicEncodingRules.BER, "A0030101FF", PublicTagClass.ContextSpecific, 0)] + [InlineData(PublicEncodingRules.BER, "A1800101000000", PublicTagClass.ContextSpecific, 1)] + [InlineData(PublicEncodingRules.CER, "6C800101000000", PublicTagClass.Application, 12)] + [InlineData(PublicEncodingRules.DER, "FF8A46030101FF", PublicTagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + PublicEncodingRules ruleSet, + string inputHex, + PublicTagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AsnReader val1 = reader.ReadSetOf(new Asn1Tag((TagClass)tagClass, tagValue, true)); + + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AsnReader val2 = reader.ReadSetOf(new Asn1Tag((TagClass)tagClass, tagValue, false)); + + Assert.False(reader.HasData); + + Assert.Equal(val1.GetEncodedValue().ByteArrayToHex(), val2.GetEncodedValue().ByteArrayToHex()); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadUTF8String.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadUTF8String.cs new file mode 100644 index 0000000000..5ab1b6dc51 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadUTF8String.cs @@ -0,0 +1,702 @@ +// 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.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public sealed class ReadUTF8String : Asn1ReaderTests + { + public static IEnumerable ValidEncodingData { get; } = + new object[][] + { + new object[] + { + PublicEncodingRules.BER, + "0C0D4A6F686E20512E20536D697468", + "John Q. Smith", + }, + new object[] + { + PublicEncodingRules.CER, + "0C0D4A6F686E20512E20536D697468", + "John Q. Smith", + }, + new object[] + { + PublicEncodingRules.DER, + "0C0D4A6F686E20512E20536D697468", + "John Q. Smith", + }, + new object[] + { + PublicEncodingRules.BER, + "2C80" + "040D4A6F686E20512E20536D697468" + "0000", + "John Q. Smith", + }, + new object[] + { + PublicEncodingRules.BER, + "2C0F" + "040D4A6F686E20512E20536D697468", + "John Q. Smith", + }, + new object[] + { + PublicEncodingRules.BER, + "0C00", + "", + }, + new object[] + { + PublicEncodingRules.CER, + "0C00", + "", + }, + new object[] + { + PublicEncodingRules.DER, + "0C00", + "", + }, + new object[] + { + PublicEncodingRules.BER, + "2C00", + "", + }, + new object[] + { + PublicEncodingRules.BER, + "2C80" + "0000", + "", + }, + new object[] + { + PublicEncodingRules.BER, + "2C80" + + "2480" + + // "Dr." + "040344722E" + + // " & " + "0403202620" + + // "Mrs." + "04044D72732E" + + "0000" + + // " " + "040120" + + "2480" + + "240C" + + // "Smith" + "0405536D697468" + + // hyphen (U+2010) + "0403E28090" + + "0000" + + // "Jones" + "04054A6F6E6573" + + "2480" + + // " " + "040120" + + "2480" + + // The next three bytes are U+FE60, small ampersand + // Don't know why any system would break this one character + // into three primitives, but it should work. + "0401EF" + + "0401B9" + + "0401A0" + + "0000" + + // " " + "040120" + + // "children" + "04086368696C6472656E" + + "0000" + + "0000", + "Dr. & Mrs. Smith\u2010Jones \uFE60 children", + }, + }; + + [Theory] + [MemberData(nameof(ValidEncodingData))] + public static void GetUTF8String_Success( + PublicEncodingRules ruleSet, + string inputHex, + string expectedValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + string value = reader.GetCharacterString(UniversalTagNumber.UTF8String); + + Assert.Equal(expectedValue, value); + } + + [Theory] + [MemberData(nameof(ValidEncodingData))] + public static void TryCopyUTF8String( + PublicEncodingRules ruleSet, + string inputHex, + string expectedValue) + { + byte[] inputData = inputHex.HexToByteArray(); + char[] output = new char[expectedValue.Length]; + + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + bool copied; + int charsWritten; + + if (output.Length > 0) + { + output[0] = 'a'; + + copied = reader.TryCopyCharacterString( + UniversalTagNumber.UTF8String, + output.AsSpan().Slice(0, expectedValue.Length - 1), + out charsWritten); + + Assert.False(copied, "reader.TryCopyUTF8String - too short"); + Assert.Equal(0, charsWritten); + Assert.Equal('a', output[0]); + } + + copied = reader.TryCopyCharacterString( + UniversalTagNumber.UTF8String, + output, + out charsWritten); + + Assert.True(copied, "reader.TryCopyUTF8String"); + + string actualValue = new string(output, 0, charsWritten); + Assert.Equal(expectedValue, actualValue); + } + + [Theory] + [MemberData(nameof(ValidEncodingData))] + public static void TryCopyUTF8StringBytes( + PublicEncodingRules ruleSet, + string inputHex, + string expectedString) + { + byte[] inputData = inputHex.HexToByteArray(); + string expectedHex = Text.Encoding.UTF8.GetBytes(expectedString).ByteArrayToHex(); + byte[] output = new byte[expectedHex.Length / 2]; + + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + bool copied; + int bytesWritten; + + if (output.Length > 0) + { + output[0] = 32; + + copied = reader.TryCopyCharacterStringBytes( + UniversalTagNumber.UTF8String, + output.AsSpan().Slice(0, output.Length - 1), + out bytesWritten); + + Assert.False(copied, "reader.TryCopyUTF8StringBytes - too short"); + Assert.Equal(0, bytesWritten); + Assert.Equal(32, output[0]); + } + + copied = reader.TryCopyCharacterStringBytes( + UniversalTagNumber.UTF8String, + output, + out bytesWritten); + + Assert.True(copied, "reader.TryCopyUTF8StringBytes"); + + Assert.Equal( + expectedHex, + new ReadOnlySpan(output, 0, bytesWritten).ByteArrayToHex()); + + Assert.Equal(output.Length, bytesWritten); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "0C0120", true)] + [InlineData(PublicEncodingRules.BER, "2C80" + "040120" + "0000", false)] + [InlineData(PublicEncodingRules.BER, "2C03" + "040120", false)] + public static void TryGetUTF8StringBytes( + PublicEncodingRules ruleSet, + string inputHex, + bool expectSuccess) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + bool got = reader.TryGetPrimitiveCharacterStringBytes( + UniversalTagNumber.UTF8String, + out ReadOnlyMemory contents); + + if (expectSuccess) + { + Assert.True(got, "reader.TryGetUTF8StringBytes"); + + Assert.True( + Unsafe.AreSame( + ref MemoryMarshal.GetReference(contents.Span), + ref inputData[2])); + } + else + { + Assert.False(got, "reader.TryGetUTF8StringBytes"); + Assert.True(contents.IsEmpty, "contents.IsEmpty"); + } + } + + [Theory] + [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] + [InlineData("Missing Length", PublicEncodingRules.BER, "0C")] + [InlineData("Missing Length", PublicEncodingRules.CER, "0C")] + [InlineData("Missing Length", PublicEncodingRules.DER, "0C")] + [InlineData("Missing Contents", PublicEncodingRules.BER, "0C01")] + [InlineData("Missing Contents", PublicEncodingRules.CER, "0C01")] + [InlineData("Missing Contents", PublicEncodingRules.DER, "0C01")] + [InlineData("Length Too Long", PublicEncodingRules.BER, "0C034869")] + [InlineData("Length Too Long", PublicEncodingRules.CER, "0C034869")] + [InlineData("Length Too Long", PublicEncodingRules.DER, "0C034869")] + [InlineData("Constructed Form", PublicEncodingRules.DER, "2C03040149")] + public static void TryGetUTF8StringBytes_Throws( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws( + () => reader.TryGetPrimitiveCharacterStringBytes(UniversalTagNumber.UTF8String, out _)); + } + + [Theory] + [InlineData("Empty", PublicEncodingRules.BER, "")] + [InlineData("Empty", PublicEncodingRules.CER, "")] + [InlineData("Empty", PublicEncodingRules.DER, "")] + [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] + [InlineData("Missing Length", PublicEncodingRules.BER, "0C")] + [InlineData("Missing Length", PublicEncodingRules.CER, "0C")] + [InlineData("Missing Length", PublicEncodingRules.DER, "0C")] + [InlineData("Missing Contents", PublicEncodingRules.BER, "0C01")] + [InlineData("Missing Contents", PublicEncodingRules.CER, "0C01")] + [InlineData("Missing Contents", PublicEncodingRules.DER, "0C01")] + [InlineData("Missing Contents - Constructed", PublicEncodingRules.BER, "2C01")] + [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.BER, "2C80")] + [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.CER, "2C80")] + [InlineData("Length Too Long", PublicEncodingRules.BER, "0C034869")] + [InlineData("Length Too Long", PublicEncodingRules.CER, "0C034869")] + [InlineData("Length Too Long", PublicEncodingRules.DER, "0C034869")] + [InlineData("Definite Constructed Form", PublicEncodingRules.CER, "2C03040149")] + [InlineData("Definite Constructed Form", PublicEncodingRules.DER, "2C03040149")] + [InlineData("Indefinite Constructed Form - Short Payload", PublicEncodingRules.CER, "2C800401490000")] + [InlineData("Indefinite Constructed Form", PublicEncodingRules.DER, "2C800401490000")] + [InlineData("No nested content", PublicEncodingRules.CER, "2C800000")] + [InlineData("No EoC", PublicEncodingRules.BER, "2C80" + "04024869")] + [InlineData("Wrong Tag - Primitive", PublicEncodingRules.BER, "04024869")] + [InlineData("Wrong Tag - Primitive", PublicEncodingRules.CER, "04024869")] + [InlineData("Wrong Tag - Primitive", PublicEncodingRules.DER, "04024869")] + [InlineData("Wrong Tag - Constructed", PublicEncodingRules.BER, "240404024869")] + [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.BER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.CER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed", PublicEncodingRules.DER, "240404024869")] + [InlineData("Nested Bad Tag", PublicEncodingRules.BER, "2C04" + "0C024869")] + [InlineData("Nested context-specific", PublicEncodingRules.BER, "2C04800400FACE")] + [InlineData("Nested context-specific (indef)", PublicEncodingRules.BER, "2C80800400FACE0000")] + [InlineData("Nested context-specific (indef)", PublicEncodingRules.CER, "2C80800400FACE0000")] + [InlineData("Nested Length Too Long", PublicEncodingRules.BER, "2C07" + ("2402" + "0403") + "040149")] + [InlineData("Nested Simple Length Too Long", PublicEncodingRules.BER, "2C03" + "040548656C6C6F")] + [InlineData("Constructed EndOfContents", PublicEncodingRules.BER, "2C8020000000")] + [InlineData("Constructed EndOfContents", PublicEncodingRules.CER, "2C8020000000")] + [InlineData("NonEmpty EndOfContents", PublicEncodingRules.BER, "2C80000100")] + [InlineData("NonEmpty EndOfContents", PublicEncodingRules.CER, "2C80000100")] + [InlineData("LongLength EndOfContents", PublicEncodingRules.BER, "2C80008100")] + public static void TryCopyUTF8StringBytes_Throws( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + byte[] outputData = new byte[inputData.Length + 1]; + outputData[0] = 252; + + int bytesWritten = -1; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws( + () => reader.TryCopyCharacterStringBytes(UniversalTagNumber.UTF8String, outputData, out bytesWritten)); + + Assert.Equal(-1, bytesWritten); + Assert.Equal(252, outputData[0]); + } + + private static void TryCopyUTF8String_Throws(PublicEncodingRules ruleSet, byte[] inputData) + { + char[] outputData = new char[inputData.Length + 1]; + outputData[0] = 'a'; + + int bytesWritten = -1; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws( + () => reader.TryCopyCharacterString(UniversalTagNumber.UTF8String, outputData, out bytesWritten)); + + Assert.Equal(-1, bytesWritten); + Assert.Equal('a', outputData[0]); + } + + [Theory] + [InlineData("Bad UTF8 value", PublicEncodingRules.BER, "0C02E280")] + [InlineData("Bad UTF8 value", PublicEncodingRules.CER, "0C02E280")] + [InlineData("Bad UTF8 value", PublicEncodingRules.DER, "0C02E280")] + [InlineData("Wrong Tag", PublicEncodingRules.BER, "04024869")] + public static void GetUTF8String_Throws( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws( + () => reader.GetCharacterString(UniversalTagNumber.UTF8String)); + } + + [Theory] + [InlineData("Empty", PublicEncodingRules.BER, "")] + [InlineData("Empty", PublicEncodingRules.CER, "")] + [InlineData("Empty", PublicEncodingRules.DER, "")] + [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] + [InlineData("Missing Length", PublicEncodingRules.BER, "0C")] + [InlineData("Missing Length", PublicEncodingRules.CER, "0C")] + [InlineData("Missing Length", PublicEncodingRules.DER, "0C")] + [InlineData("Missing Contents", PublicEncodingRules.BER, "0C01")] + [InlineData("Missing Contents", PublicEncodingRules.CER, "0C01")] + [InlineData("Missing Contents", PublicEncodingRules.DER, "0C01")] + [InlineData("Missing Contents - Constructed", PublicEncodingRules.BER, "2C01")] + [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.BER, "2C80")] + [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.CER, "2C80")] + [InlineData("Length Too Long", PublicEncodingRules.BER, "0C034869")] + [InlineData("Length Too Long", PublicEncodingRules.CER, "0C034869")] + [InlineData("Length Too Long", PublicEncodingRules.DER, "0C034869")] + [InlineData("Definite Constructed Form", PublicEncodingRules.CER, "2C03040149")] + [InlineData("Definite Constructed Form", PublicEncodingRules.DER, "2C03040149")] + [InlineData("Indefinite Constructed Form - Short Payload", PublicEncodingRules.CER, "2C800401490000")] + [InlineData("Indefinite Constructed Form", PublicEncodingRules.DER, "2C800401490000")] + [InlineData("No nested content", PublicEncodingRules.CER, "2C800000")] + [InlineData("No EoC", PublicEncodingRules.BER, "2C80" + "04024869")] + [InlineData("Wrong Tag - Primitive", PublicEncodingRules.BER, "04024869")] + [InlineData("Wrong Tag - Primitive", PublicEncodingRules.CER, "04024869")] + [InlineData("Wrong Tag - Primitive", PublicEncodingRules.DER, "04024869")] + [InlineData("Wrong Tag - Constructed", PublicEncodingRules.BER, "240404024869")] + [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.BER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.CER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed", PublicEncodingRules.DER, "240404024869")] + [InlineData("Nested Bad Tag", PublicEncodingRules.BER, "2C04" + "0C024869")] + [InlineData("Nested context-specific", PublicEncodingRules.BER, "2C04800400FACE")] + [InlineData("Nested context-specific (indef)", PublicEncodingRules.BER, "2C80800400FACE0000")] + [InlineData("Nested context-specific (indef)", PublicEncodingRules.CER, "2C80800400FACE0000")] + [InlineData("Nested Length Too Long", PublicEncodingRules.BER, "2C07" + ("2402" + "0403") + "040149")] + [InlineData("Nested Simple Length Too Long", PublicEncodingRules.BER, "2C03" + "040548656C6C6F")] + [InlineData("Constructed EndOfContents", PublicEncodingRules.BER, "2C8020000000")] + [InlineData("Constructed EndOfContents", PublicEncodingRules.CER, "2C8020000000")] + [InlineData("NonEmpty EndOfContents", PublicEncodingRules.BER, "2C80000100")] + [InlineData("NonEmpty EndOfContents", PublicEncodingRules.CER, "2C80000100")] + [InlineData("LongLength EndOfContents", PublicEncodingRules.BER, "2C80008100")] + [InlineData("Bad UTF8 value", PublicEncodingRules.BER, "0C02E280")] + public static void TryCopyUTF8String_Throws( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + TryCopyUTF8String_Throws(ruleSet, inputData); + } + + [Fact] + public static void TryCopyUTF8String_Throws_CER_NestedTooLong() + { + // CER says that the maximum encoding length for a UTF8String primitive + // is 1000. + // + // This test checks it for a primitive contained within a constructed. + // + // So we need 04 [1001] { 1001 0x00s } + // 1001 => 0x3E9, so the length encoding is 82 03 E9. + // 1001 + 3 + 1 == 1005 + // + // Plus a leading 2C 80 (indefinite length constructed) + // and a trailing 00 00 (End of contents) + // == 1009 + byte[] input = new byte[1009]; + // CONSTRUCTED UTF8 STRING (indefinite) + input[0] = 0x2C; + input[1] = 0x80; + // OCTET STRING (1001) + input[2] = 0x04; + input[3] = 0x82; + input[4] = 0x03; + input[5] = 0xE9; + // EOC implicit since the byte[] initializes to zeros + + TryCopyUTF8String_Throws(PublicEncodingRules.CER, input); + } + + [Fact] + public static void TryCopyUTF8String_Throws_CER_NestedTooShortIntermediate() + { + // CER says that the maximum encoding length for a UTF8String primitive + // is 1000, and in the constructed form the lengths must be + // [ 1000, 1000, 1000, ..., len%1000 ] + // + // So 1000, 2, 2 is illegal. + // + // 2C 80 (indefinite constructed utf8 string) + // 04 82 03 08 (octet string, 1000 bytes) + // [1000 content bytes] + // 04 02 (octet string, 2 bytes) + // [2 content bytes] + // 04 02 (octet string, 2 bytes) + // [2 content bytes] + // 00 00 (end of contents) + // Looks like 1,016 bytes. + byte[] input = new byte[1016]; + // CONSTRUCTED UTF8 STRING (indefinite) + input[0] = 0x2C; + input[1] = 0x80; + // OCTET STRING (1000) + input[2] = 0x03; + input[3] = 0x82; + input[4] = 0x03; + input[5] = 0xE8; + // OCTET STRING (2) + input[1006] = 0x04; + input[1007] = 0x02; + // OCTET STRING (2) + input[1010] = 0x04; + input[1011] = 0x02; + // EOC implicit since the byte[] initializes to zeros + + TryCopyUTF8String_Throws(PublicEncodingRules.CER, input); + } + + [Fact] + public static void TryCopyUTF8StringBytes_Success_CER_MaxPrimitiveLength() + { + // CER says that the maximum encoding length for a UTF8String primitive + // is 1000. + // + // So we need 0C [1000] { 1000 anythings } + // 1000 => 0x3E8, so the length encoding is 82 03 E8. + // 1000 + 3 + 1 == 1004 + byte[] input = new byte[1004]; + input[0] = 0x0C; + input[1] = 0x82; + input[2] = 0x03; + input[3] = 0xE8; + + // Content + input[4] = 0x65; + input[5] = 0x65; + input[1002] = 0x61; + input[1003] = 0x61; + + byte[] output = new byte[1000]; + + AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); + + bool success = reader.TryCopyCharacterStringBytes( + UniversalTagNumber.UTF8String, + output, + out int bytesWritten); + + Assert.True(success, "reader.TryCopyUTF8StringBytes"); + Assert.Equal(1000, bytesWritten); + + Assert.Equal( + input.AsReadOnlySpan().Slice(4).ByteArrayToHex(), + output.ByteArrayToHex()); + } + + [Fact] + public static void TryCopyUTF8StringBytes_Success_CER_MinConstructedLength() + { + // CER says that the maximum encoding length for a UTF8String primitive + // is 1000, and that a constructed form must be used for values greater + // than 1000 bytes, with segments dividing up for each thousand + // [1000, 1000, ..., len%1000]. + // + // So our smallest constructed form is 1001 bytes, [1000, 1] + // + // 2C 80 (indefinite constructed utf8 string) + // 04 82 03 E9 (primitive octet string, 1000 bytes) + // [1000 content bytes] + // 04 01 (primitive octet string, 1 byte) + // pp + // 00 00 (end of contents, 0 bytes) + // 1011 total. + byte[] input = new byte[1011]; + int offset = 0; + // CONSTRUCTED UTF8 STRING (Indefinite) + input[offset++] = 0x2C; + input[offset++] = 0x80; + // OCTET STRING (1000) + input[offset++] = 0x04; + input[offset++] = 0x82; + input[offset++] = 0x03; + input[offset++] = 0xE8; + + // Primitive 1: (65 65 :: 61 61) (1000) + input[offset++] = 0x65; + input[offset] = 0x65; + offset += 997; + input[offset++] = 0x61; + input[offset++] = 0x61; + + // OCTET STRING (1) + input[offset++] = 0x04; + input[offset++] = 0x01; + + // Primitive 2: One more byte + input[offset] = 0x2E; + + byte[] expected = new byte[1001]; + offset = 0; + expected[offset++] = 0x65; + expected[offset] = 0x65; + offset += 997; + expected[offset++] = 0x61; + expected[offset++] = 0x61; + expected[offset] = 0x2E; + + byte[] output = new byte[1001]; + + AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); + + bool success = reader.TryCopyCharacterStringBytes( + UniversalTagNumber.UTF8String, + output, + out int bytesWritten); + + Assert.True(success, "reader.TryCopyUTF8StringBytes"); + Assert.Equal(1001, bytesWritten); + + Assert.Equal( + expected.ByteArrayToHex(), + output.ByteArrayToHex()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) + { + byte[] inputData = { 0x0C, 2, (byte)'e', (byte)'l' }; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + const UniversalTagNumber EncodingType = UniversalTagNumber.UTF8String; + + AssertExtensions.Throws( + "expectedTag", + () => reader.TryGetPrimitiveCharacterStringBytes(Asn1Tag.Null, EncodingType, out _)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.TryGetPrimitiveCharacterStringBytes(new Asn1Tag(TagClass.ContextSpecific, 0), EncodingType, out _)); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + Assert.True(reader.TryGetPrimitiveCharacterStringBytes(EncodingType, out ReadOnlyMemory value)); + Assert.Equal("656C", value.ByteArrayToHex()); + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) + { + byte[] inputData = { 0x87, 2, (byte)'h', (byte)'i' }; + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + const UniversalTagNumber EncodingType = UniversalTagNumber.UTF8String; + + AssertExtensions.Throws( + "expectedTag", + () => reader.TryGetPrimitiveCharacterStringBytes(Asn1Tag.Null, EncodingType, out _)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.TryGetPrimitiveCharacterStringBytes(EncodingType, out _)); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.TryGetPrimitiveCharacterStringBytes(new Asn1Tag(TagClass.Application, 0), EncodingType, out _)); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.TryGetPrimitiveCharacterStringBytes(new Asn1Tag(TagClass.ContextSpecific, 1), EncodingType, out _)); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + Assert.True( + reader.TryGetPrimitiveCharacterStringBytes( + new Asn1Tag(TagClass.ContextSpecific, 7), + EncodingType, + out ReadOnlyMemory value)); + + Assert.Equal("6869", value.ByteArrayToHex()); + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "0C026869", PublicTagClass.Universal, 12)] + [InlineData(PublicEncodingRules.CER, "0C026869", PublicTagClass.Universal, 12)] + [InlineData(PublicEncodingRules.DER, "0C026869", PublicTagClass.Universal, 12)] + [InlineData(PublicEncodingRules.BER, "80023132", PublicTagClass.ContextSpecific, 0)] + [InlineData(PublicEncodingRules.CER, "4C023132", PublicTagClass.Application, 12)] + [InlineData(PublicEncodingRules.DER, "DF8A46023132", PublicTagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + PublicEncodingRules ruleSet, + string inputHex, + PublicTagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.True( + reader.TryGetPrimitiveCharacterStringBytes( + new Asn1Tag((TagClass)tagClass, tagValue, true), + UniversalTagNumber.UTF8String, + out ReadOnlyMemory val1)); + + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.True( + reader.TryGetPrimitiveCharacterStringBytes( + new Asn1Tag((TagClass)tagClass, tagValue, false), + UniversalTagNumber.UTF8String, + out ReadOnlyMemory val2)); + + Assert.False(reader.HasData); + + Assert.Equal(val1.ByteArrayToHex(), val2.ByteArrayToHex()); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadUtcTime.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadUtcTime.cs new file mode 100644 index 0000000000..290c7b13fa --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadUtcTime.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. +// See the LICENSE file in the project root for more information. + +using System.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public sealed class ReadUtcTime : Asn1ReaderTests + { + [Theory] + // A, B2, C2 + [InlineData(PublicEncodingRules.BER, "17113137303930383130333530332D30373030", 2017, 9, 8, 10, 35, 3, -7, 0)] + [InlineData(PublicEncodingRules.BER, "17113137303930383130333530332D30303530", 2017, 9, 8, 10, 35, 3, 0, -50)] + [InlineData(PublicEncodingRules.BER, "17113137303930383130333530332B30373030", 2017, 9, 8, 10, 35, 3, 7, 0)] + [InlineData(PublicEncodingRules.BER, "17113030303130313030303030302B30303030", 2000, 1, 1, 0, 0, 0, 0, 0)] + [InlineData(PublicEncodingRules.BER, "17113030303130313030303030302D31343030", 2000, 1, 1, 0, 0, 0, -14, 0)] + // A, B2, C1 (only legal form for CER or DER) + [InlineData(PublicEncodingRules.BER, "170D3132303130323233353935395A", 2012, 1, 2, 23, 59, 59, 0, 0)] + [InlineData(PublicEncodingRules.CER, "170D3439313233313233353935395A", 2049, 12, 31, 23, 59, 59, 0, 0)] + [InlineData(PublicEncodingRules.DER, "170D3530303130323132333435365A", 1950, 1, 2, 12, 34, 56, 0, 0)] + // A, B1, C2 + [InlineData(PublicEncodingRules.BER, "170F313730393038313033352D30373030", 2017, 9, 8, 10, 35, 0, -7, 0)] + [InlineData(PublicEncodingRules.BER, "170F323730393038313033352B30393132", 2027, 9, 8, 10, 35, 0, 9, 12)] + // A, B1, C1 + [InlineData(PublicEncodingRules.BER, "170B313230313032323335395A", 2012, 1, 2, 23, 59, 0, 0, 0)] + [InlineData(PublicEncodingRules.BER, "170B343931323331323335395A", 2049, 12, 31, 23, 59, 0, 0, 0)] + // BER Constructed form + [InlineData( + PublicEncodingRules.BER, + "3780" + + "04023132" + + "04023031" + + "2480" + "040130" + "040132" + "0000" + + "040432333539" + + "04830000015A" + + "0000", + 2012, 1, 2, 23, 59, 0, 0, 0)] + public static void ParseTime_Valid( + PublicEncodingRules ruleSet, + string inputHex, + int year, + int month, + int day, + int hour, + int minute, + int second, + int offsetHour, + int offsetMinute) + { + byte[] inputData = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + DateTimeOffset value = reader.GetUtcTime(); + + Assert.Equal(year, value.Year); + Assert.Equal(month, value.Month); + Assert.Equal(day, value.Day); + Assert.Equal(hour, value.Hour); + Assert.Equal(minute, value.Minute); + Assert.Equal(second, value.Second); + Assert.Equal(0, value.Millisecond); + Assert.Equal(new TimeSpan(offsetHour, offsetMinute, 0), value.Offset); + } + + [Fact] + public static void ParseTime_InvalidValue_LegalString() + { + byte[] inputData = "17113030303030303030303030302D31353030".HexToByteArray(); + + var exception = Assert.Throws( + () => + { + AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); + reader.GetUtcTime(); + }); + + Assert.NotNull(exception.InnerException); + Assert.IsType(exception.InnerException); + } + + [Theory] + [InlineData(2011, 1912)] + [InlineData(2012, 2012)] + [InlineData(2013, 2012)] + [InlineData(2111, 2012)] + [InlineData(2112, 2112)] + [InlineData(2113, 2112)] + [InlineData(12, 12)] + [InlineData(99, 12)] + [InlineData(111, 12)] + public static void ReadUtcTime_TwoYearMaximum(int maximum, int interpretedYear) + { + byte[] inputData = "170D3132303130323233353935395A".HexToByteArray(); + + AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); + DateTimeOffset value = reader.GetUtcTime(maximum); + + Assert.Equal(interpretedYear, value.Year); + } + + [Theory] + [InlineData("A,B2,C2", PublicEncodingRules.CER, "17113137303930383130333530332D30373030")] + [InlineData("A,B2,C2", PublicEncodingRules.DER, "17113137303930383130333530332D30373030")] + [InlineData("A,B1,C2", PublicEncodingRules.CER, "170F313730393038313033352D30373030")] + [InlineData("A,B1,C2", PublicEncodingRules.DER, "170F313730393038313033352D30373030")] + [InlineData("A,B1,C1", PublicEncodingRules.CER, "170B313230313032323335395A")] + [InlineData("A,B1,C1", PublicEncodingRules.DER, "170B313230313032323335395A")] + [InlineData("A,B1,C1-NotZ", PublicEncodingRules.BER, "170B313230313032323335392B")] + [InlineData("A,B1,C2-NotPlusMinus", PublicEncodingRules.BER, "170F313730393038313033352C30373030")] + [InlineData("A,B2,C2-NotPlusMinus", PublicEncodingRules.BER, "17113137303930383130333530332C30373030")] + [InlineData("A,B2,C2-MinuteOutOfRange", PublicEncodingRules.BER, "17113030303030303030303030302D31353630")] + [InlineData("A,B1,C2-MinuteOutOfRange", PublicEncodingRules.BER, "170F303030303030303030302D31353630")] + [InlineData("A1,B2,C1-NotZ", PublicEncodingRules.DER, "170D3530303130323132333435365B")] + [InlineData("A,B2,C2-MissingDigit", PublicEncodingRules.BER, "17103137303930383130333530332C303730")] + [InlineData("A,B2,C2-TooLong", PublicEncodingRules.BER, "17123137303930383130333530332B3037303030")] + [InlineData("WrongTag", PublicEncodingRules.BER, "1A0D3132303130323233353935395A")] + public static void ReadUtcTime_Throws( + string description, + PublicEncodingRules ruleSet, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + Assert.Throws(() => reader.GetUtcTime()); + } + + [Fact] + public static void ReadUtcTime_WayTooBig_Throws() + { + // Need to exceed the length that the shared pool will return for 17: + byte[] inputData = new byte[4097+4]; + inputData[0] = 0x17; + inputData[1] = 0x82; + inputData[2] = 0x10; + inputData[3] = 0x01; + + AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); + + Assert.Throws(() => reader.GetUtcTime()); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) + { + byte[] inputData = "170D3530303130323132333435365A".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.GetUtcTime(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.GetUtcTime(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + Assert.Equal( + new DateTimeOffset(1950, 1, 2, 12, 34, 56, TimeSpan.Zero), + reader.GetUtcTime()); + + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) + { + byte[] inputData = "850D3530303130323132333435365A".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.GetUtcTime(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.GetUtcTime()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.GetUtcTime(new Asn1Tag(TagClass.Application, 5))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.GetUtcTime(new Asn1Tag(TagClass.ContextSpecific, 7))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + Assert.Equal( + new DateTimeOffset(1950, 1, 2, 12, 34, 56, TimeSpan.Zero), + reader.GetUtcTime(new Asn1Tag(TagClass.ContextSpecific, 5))); + + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "170D3530303130323132333435365A", PublicTagClass.Universal, 23)] + [InlineData(PublicEncodingRules.CER, "170D3530303130323132333435365A", PublicTagClass.Universal, 23)] + [InlineData(PublicEncodingRules.DER, "170D3530303130323132333435365A", PublicTagClass.Universal, 23)] + [InlineData(PublicEncodingRules.BER, "800D3530303130323132333435365A", PublicTagClass.ContextSpecific, 0)] + [InlineData(PublicEncodingRules.CER, "4C0D3530303130323132333435365A", PublicTagClass.Application, 12)] + [InlineData(PublicEncodingRules.DER, "DF8A460D3530303130323132333435365A", PublicTagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + PublicEncodingRules ruleSet, + string inputHex, + PublicTagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + DateTimeOffset val1 = reader.GetUtcTime(new Asn1Tag((TagClass)tagClass, tagValue, true)); + + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + + DateTimeOffset val2 = reader.GetUtcTime(new Asn1Tag((TagClass)tagClass, tagValue, false)); + + Assert.False(reader.HasData); + + Assert.Equal(val1, val2); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReaderStateTests.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReaderStateTests.cs new file mode 100644 index 0000000000..2cbfb57018 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReaderStateTests.cs @@ -0,0 +1,36 @@ +// 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.Security.Cryptography.Asn1; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public static class ReaderStateTests + { + [Fact] + public static void HasDataAndThrowIfNotEmpty() + { + AsnReader reader = new AsnReader(new byte[] { 0x01, 0x01, 0x00 }, AsnEncodingRules.BER); + Assert.True(reader.HasData); + Assert.Throws(() => reader.ThrowIfNotEmpty()); + + // Consume the current value and move on. + reader.GetEncodedValue(); + + Assert.False(reader.HasData); + // Assert.NoThrow + reader.ThrowIfNotEmpty(); + } + + [Fact] + public static void HasDataAndThrowIfNotEmpty_StartsEmpty() + { + AsnReader reader = new AsnReader(ReadOnlyMemory.Empty, AsnEncodingRules.BER); + Assert.False(reader.HasData); + // Assert.NoThrow + reader.ThrowIfNotEmpty(); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Serializer/SimpleDeserialize.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Serializer/SimpleDeserialize.cs new file mode 100644 index 0000000000..6ca6a21036 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Serializer/SimpleDeserialize.cs @@ -0,0 +1,826 @@ +// 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.Globalization; +using System.IO; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +using PublicEncodingRules=System.Security.Cryptography.Tests.Asn1.Asn1ReaderTests.PublicEncodingRules; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public static class SimpleDeserialize + { + [Theory] + [InlineData( + PublicEncodingRules.BER, + "3080" + "06072A8648CE3D0201" + "06082A8648CE3D030107" + "0000", + "1.2.840.10045.3.1.7")] + // More! + public static void AlgorithmIdentifier_ECC_WithCurves( + PublicEncodingRules ruleSet, + string inputHex, + string curveOid) + { + byte[] inputData = inputHex.HexToByteArray(); + + var algorithmIdentifier = AsnSerializer.Deserialize( + inputData, + (AsnEncodingRules)ruleSet); + + Assert.Equal("1.2.840.10045.2.1", algorithmIdentifier.Algorithm.Value); + + var reader = new AsnReader(algorithmIdentifier.Parameters, (AsnEncodingRules)ruleSet); + Oid curveId = reader.ReadObjectIdentifier(skipFriendlyName: true); + Assert.Equal(curveOid, curveId.Value); + } + + [Fact] + public static void AllTheSimpleThings() + { + const string InputHex = + "3080" + + "0101FF" + + "0201FE" + + "020101" + + "0202FEFF" + + "02020101" + + "0204FEFFFFFF" + + "020401000001" + + "0208FEFFFFFFFFFFFFFF" + + "02080100000000000001" + + "0209010000000000000001" + + "0303000102" + + "0404FF0055AA" + + "0500" + + "06082A8648CE3D030107" + + "06072A8648CE3D0201" + + "06092A864886F70D010101" + + "0A011E" + + "0C2544722E2026204D72732E20536D697468E280904A6F6E657320EFB9A0206368696C6472656E" + + "162144722E2026204D72732E20536D6974682D4A6F6E65732026206368696C6472656E" + + "1E42" + + "00440072002E002000260020004D00720073002E00200053006D006900740068" + + "2010004A006F006E006500730020FE600020006300680069006C006400720065" + + "006E" + + "3080" + + "010100" + + "010100" + + "0101FF" + + "0101FF" + + "010100" + + "0000" + + "3180" + + "020100" + + "020101" + + "0201FE" + + "0201FF" + + "02020100" + + "0000" + + "3080" + + "020100" + + "020101" + + "020200FE" + + "02017F" + + "020200FF" + + "0000" + + "170D3530303130323132333435365A" + + "170D3530303130323132333435365A" + + "181432303136313130363031323334352E373635345A" + + "180F32303136313130363031323334355A" + + "020F0102030405060708090A0B0C0D0E0F" + + "0000"; + + byte[] inputData = InputHex.HexToByteArray(); + + var atst = AsnSerializer.Deserialize( + inputData, + AsnEncodingRules.BER); + + const string UnicodeVerifier = "Dr. & Mrs. Smith\u2010Jones \uFE60 children"; + const string AsciiVerifier = "Dr. & Mrs. Smith-Jones & children"; + + Assert.False(atst.NotBool, "atst.NotBool"); + Assert.Equal(-2, atst.SByte); + Assert.Equal(1, atst.Byte); + Assert.Equal(unchecked((short)0xFEFF), atst.Short); + Assert.Equal(0x0101, atst.UShort); + Assert.Equal(unchecked((int)0xFEFFFFFF), atst.Int); + Assert.Equal((uint)0x01000001, atst.UInt); + Assert.Equal(unchecked((long)0xFEFFFFFFFFFFFFFF), atst.Long); + Assert.Equal(0x0100000000000001UL, atst.ULong); + Assert.Equal("010000000000000001", atst.BigIntBytes.ByteArrayToHex()); + Assert.Equal("0102", atst.BitStringBytes.ByteArrayToHex()); + Assert.Equal("FF0055AA", atst.OctetStringBytes.ByteArrayToHex()); + Assert.Equal("0500", atst.Null.ByteArrayToHex()); + Assert.Equal("1.2.840.10045.3.1.7", atst.UnattrOid.Value); + Assert.Equal("1.2.840.10045.3.1.7", atst.UnattrOid.FriendlyName); + Assert.Equal("1.2.840.10045.2.1", atst.WithName.Value); + Assert.Equal("ECC", atst.WithName.FriendlyName); + Assert.Equal("1.2.840.113549.1.1.1", atst.OidString); + Assert.Equal(UniversalTagNumber.BMPString, atst.LinearEnum); + Assert.Equal(UnicodeVerifier, atst.Utf8Encoded); + Assert.Equal(AsciiVerifier, atst.Ia5Encoded); + Assert.Equal(UnicodeVerifier, atst.BmpEncoded); + Assert.Equal(new[] { false, false, true, true, false }, atst.Bools); + Assert.Equal(new[] { 0, 1, -2, -1, 256 }, atst.Ints); + Assert.Equal(new byte[] { 0, 1, 254, 127, 255 }, atst.LittleUInts); + Assert.Equal(new DateTimeOffset(1950, 1, 2, 12, 34, 56, TimeSpan.Zero), atst.UtcTime2049); + Assert.Equal(new DateTimeOffset(2050, 1, 2, 12, 34, 56, TimeSpan.Zero), atst.UtcTime2099); + Assert.Equal(new DateTimeOffset(2016, 11, 6, 1, 23, 45, TimeSpan.Zero) + new TimeSpan(7654000), atst.GeneralizedTimeWithFractions); + Assert.Equal(new DateTimeOffset(2016, 11, 6, 1, 23, 45, TimeSpan.Zero), atst.GeneralizedTimeNoFractions); + Assert.Equal(BigInteger.Parse("0102030405060708090A0B0C0D0E0F", NumberStyles.HexNumber), atst.BigInteger); + } + + [Fact] + public static void ReadEcPublicKey() + { + const string PublicKeyValue = + "04" + + "2363DD131DA65E899A2E63E9E05E50C830D4994662FFE883DB2B9A767DCCABA2" + + "F07081B5711BE1DEE90DFC8DE17970C2D937A16CD34581F52B8D59C9E9532D13"; + + const string InputHex = + "3059" + + "3013" + + "06072A8648CE3D0201" + + "06082A8648CE3D030107" + + "0342" + + "00" + + PublicKeyValue; + + byte[] inputData = InputHex.HexToByteArray(); + + var spki = AsnSerializer.Deserialize( + inputData, + AsnEncodingRules.DER); + + Assert.Equal("1.2.840.10045.2.1", spki.AlgorithmIdentifier.Algorithm.Value); + Assert.Equal(PublicKeyValue, spki.PublicKey.ByteArrayToHex()); + + AsnReader reader = new AsnReader(spki.AlgorithmIdentifier.Parameters, AsnEncodingRules.DER); + string curveOid = reader.ReadObjectIdentifierAsString(); + Assert.False(reader.HasData, "reader.HasData"); + Assert.Equal("1.2.840.10045.3.1.7", curveOid); + } + + [Fact] + public static void ReadDirectoryString() + { + const string BmpInputHex = "1E0400480069"; + const string Utf8InputHex = "0C024869"; + + var ds1 = AsnSerializer.Deserialize( + BmpInputHex.HexToByteArray(), + AsnEncodingRules.DER); + + var ds2 = AsnSerializer.Deserialize( + Utf8InputHex.HexToByteArray(), + AsnEncodingRules.DER); + + Assert.NotNull(ds1); + Assert.NotNull(ds2); + Assert.Null(ds1.Utf8String); + Assert.Null(ds2.BmpString); + Assert.Equal("Hi", ds1.BmpString); + Assert.Equal("Hi", ds2.Utf8String); + } + + [Fact] + public static void ReadFlexibleString() + { + const string BmpInputHex = "1E0400480069"; + const string Utf8InputHex = "0C024869"; + const string Ia5InputHex = "16024869"; + + var fs1 = AsnSerializer.Deserialize( + BmpInputHex.HexToByteArray(), + AsnEncodingRules.DER); + + var fs2 = AsnSerializer.Deserialize( + Utf8InputHex.HexToByteArray(), + AsnEncodingRules.DER); + + var fs3 = AsnSerializer.Deserialize( + Ia5InputHex.HexToByteArray(), + AsnEncodingRules.DER); + + Assert.Null(fs1.DirectoryString?.Utf8String); + Assert.Null(fs1.Ascii); + Assert.Null(fs2.DirectoryString?.BmpString); + Assert.Null(fs2.Ascii); + Assert.Null(fs3.DirectoryString?.BmpString); + Assert.Null(fs3.DirectoryString?.Utf8String); + Assert.False(fs3.DirectoryString.HasValue, "fs3.DirectoryString.HasValue"); + Assert.Equal("Hi", fs1.DirectoryString?.BmpString); + Assert.Equal("Hi", fs2.DirectoryString?.Utf8String); + Assert.Equal("Hi", fs3.Ascii); + } + + [Fact] + public static void ReadFlexibleString_Class() + { + const string BmpInputHex = "1E0400480069"; + const string Utf8InputHex = "0C024869"; + const string Ia5InputHex = "16024869"; + + var fs1 = AsnSerializer.Deserialize( + BmpInputHex.HexToByteArray(), + AsnEncodingRules.DER); + + var fs2 = AsnSerializer.Deserialize( + Utf8InputHex.HexToByteArray(), + AsnEncodingRules.DER); + + var fs3 = AsnSerializer.Deserialize( + Ia5InputHex.HexToByteArray(), + AsnEncodingRules.DER); + + Assert.Null(fs1.DirectoryString?.Utf8String); + Assert.Null(fs1.Ascii); + Assert.Null(fs2.DirectoryString?.BmpString); + Assert.Null(fs2.Ascii); + Assert.Null(fs3.DirectoryString?.BmpString); + Assert.Null(fs3.DirectoryString?.Utf8String); + Assert.Null(fs3.DirectoryString); + Assert.Equal("Hi", fs1.DirectoryString?.BmpString); + Assert.Equal("Hi", fs2.DirectoryString?.Utf8String); + Assert.Equal("Hi", fs3.Ascii); + } + + [Fact] + public static void ReadFlexibleString_ClassHybrid() + { + const string BmpInputHex = "1E0400480069"; + const string Utf8InputHex = "0C024869"; + const string Ia5InputHex = "16024869"; + + var fs1 = AsnSerializer.Deserialize( + BmpInputHex.HexToByteArray(), + AsnEncodingRules.DER); + + var fs2 = AsnSerializer.Deserialize( + Utf8InputHex.HexToByteArray(), + AsnEncodingRules.DER); + + var fs3 = AsnSerializer.Deserialize( + Ia5InputHex.HexToByteArray(), + AsnEncodingRules.DER); + + Assert.Null(fs1.DirectoryString?.Utf8String); + Assert.Null(fs1.Ascii); + Assert.Null(fs2.DirectoryString?.BmpString); + Assert.Null(fs2.Ascii); + Assert.Null(fs3.DirectoryString?.BmpString); + Assert.Null(fs3.DirectoryString?.Utf8String); + Assert.False(fs3.DirectoryString.HasValue, "fs3.DirectoryString.HasValue"); + Assert.Equal("Hi", fs1.DirectoryString?.BmpString); + Assert.Equal("Hi", fs2.DirectoryString?.Utf8String); + Assert.Equal("Hi", fs3.Ascii); + } + + [Fact] + public static void ReadFlexibleString_StructHybrid() + { + const string BmpInputHex = "1E0400480069"; + const string Utf8InputHex = "0C024869"; + const string Ia5InputHex = "16024869"; + + var fs1 = AsnSerializer.Deserialize( + BmpInputHex.HexToByteArray(), + AsnEncodingRules.DER); + + var fs2 = AsnSerializer.Deserialize( + Utf8InputHex.HexToByteArray(), + AsnEncodingRules.DER); + + var fs3 = AsnSerializer.Deserialize( + Ia5InputHex.HexToByteArray(), + AsnEncodingRules.DER); + + Assert.Null(fs1.DirectoryString?.Utf8String); + Assert.Null(fs1.Ascii); + Assert.Null(fs2.DirectoryString?.BmpString); + Assert.Null(fs2.Ascii); + Assert.Null(fs3.DirectoryString?.BmpString); + Assert.Null(fs3.DirectoryString?.Utf8String); + Assert.Null(fs3.DirectoryString); + Assert.Equal("Hi", fs1.DirectoryString?.BmpString); + Assert.Equal("Hi", fs2.DirectoryString?.Utf8String); + Assert.Equal("Hi", fs3.Ascii); + } + + [Fact] + public static void Choice_CycleRoot_Throws() + { + byte[] inputBytes = { 0x01, 0x01, 0x00 }; + + Assert.Throws( + () => + AsnSerializer.Deserialize( + inputBytes, + AsnEncodingRules.DER) + ); + } + + [Fact] + public static void DirectoryStringClass_AsNull() + { + byte[] inputBytes = { 0x05, 0x00 }; + + DirectoryStringClass ds = AsnSerializer.Deserialize( + inputBytes, + AsnEncodingRules.DER); + + Assert.Null(ds); + } + + [Fact] + public static void Deserialize_ContextSpecific_Choice() + { + byte[] inputBytes = { 0x82, 0x00 }; + + ContextSpecificChoice choice = AsnSerializer.Deserialize( + inputBytes, + AsnEncodingRules.DER); + + Assert.Null(choice.Utf8String); + Assert.Equal(string.Empty, choice.IA5String); + } + + [Fact] + public static void Deserialize_UtcTime_WithTwoYearMax() + { + const string UtcTimeValue = "170D3132303130323233353935395A"; + + const string InputHex = + "3080" + UtcTimeValue + UtcTimeValue + UtcTimeValue + "0000"; + + byte[] inputBytes = InputHex.HexToByteArray(); + + UtcTimeTwoDigitYears dates = AsnSerializer.Deserialize( + inputBytes, + AsnEncodingRules.BER); + + Assert.Equal(new DateTimeOffset(1912, 1, 2, 23, 59, 59, TimeSpan.Zero), dates.ErnestoSabatoLifetime); + Assert.Equal(new DateTimeOffset(2012, 1, 2, 23, 59, 59, TimeSpan.Zero), dates.MayanPhenomenon); + Assert.Equal(new DateTimeOffset(2012, 1, 2, 23, 59, 59, TimeSpan.Zero), dates.ImplicitMax); + } + + [Fact] + public static void Deserialize_NamedBitLists() + { + const string InputHex = + "3080" + + "0303000841" + + "0000"; + + byte[] inputBytes = InputHex.HexToByteArray(); + + var variants = AsnSerializer.Deserialize( + inputBytes, + AsnEncodingRules.BER); + + Assert.Equal( + SomeFlagsEnum.BitFour | SomeFlagsEnum.BitNine | SomeFlagsEnum.BitFifteen, + variants.DefaultMode); + } + + [Fact] + public static void ReadAnyValueWithExpectedTag() + { + byte[] inputData = "308006010030030101000000".HexToByteArray(); + + var data = AsnSerializer.Deserialize( + inputData, + AsnEncodingRules.BER); + + Assert.Equal("0.0", data.Id); + Assert.Equal(5, data.Data.Length); + Assert.True(Unsafe.AreSame(ref MemoryMarshal.GetReference(data.Data.Span), ref inputData[5])); + + // Change [Constructed] SEQUENCE to [Constructed] Context-Specific 0. + inputData[5] = 0xA0; + + Assert.Throws( + () => AsnSerializer.Deserialize(inputData, AsnEncodingRules.BER)); + } + + [Theory] + [InlineData("3000", false, false)] + [InlineData("30051603494135", false, true)] + [InlineData("30060C0455544638", true, false)] + [InlineData("300B0C04555446381603494135", true, true)] + public static void ReadOptionals(string inputHex, bool hasUtf8, bool hasIa5) + { + byte[] inputData = inputHex.HexToByteArray(); + var data = AsnSerializer.Deserialize(inputData, AsnEncodingRules.BER); + + if (hasUtf8) + { + Assert.Equal("UTF8", data.Utf8String); + } + else + { + Assert.Null(data.Utf8String); + } + + if (hasIa5) + { + Assert.Equal("IA5", data.IA5String); + } + else + { + Assert.Null(data.IA5String); + } + } + + [Fact] + public static void TooMuchData() + { + // This is { IA5String("IA5"), UTF8String("UTF8") }, which is the opposite + // of the field order of OptionalValues. SO it will see the UTF8String as null, + // then the IA5String as present, but then data remains. + byte[] inputData = "300B16034941350C0455544638".HexToByteArray(); + + Assert.Throws( + () => AsnSerializer.Deserialize(inputData, AsnEncodingRules.BER)); + } + } + + // RFC 3280 / ITU-T X.509 + [StructLayout(LayoutKind.Sequential)] + internal struct AlgorithmIdentifier + { + public Oid Algorithm; + [AnyValue] + public ReadOnlyMemory Parameters; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct SubjectPublicKeyInfo + { + public AlgorithmIdentifier AlgorithmIdentifier; + [BitString] + public ReadOnlyMemory PublicKey; + } + + [StructLayout(LayoutKind.Sequential)] + internal sealed class AllTheSimpleThings + { + private bool _bool; + private sbyte _sbyte; + private byte _byte; + private short _short; + private ushort _ushort; + private int _int; + private uint _uint; + private long _long; + private ulong _ulong; + [Integer] + private ReadOnlyMemory _bigInt; + [BitString] + private ReadOnlyMemory _bitString; + [OctetString] + private ReadOnlyMemory _octetString; + [AnyValue] + private ReadOnlyMemory _null; + private Oid _oidNoName; + [ObjectIdentifier(PopulateFriendlyName = true)] + private Oid _oid; + [ObjectIdentifier] + private string _oidString; + private UniversalTagNumber _nonFlagsEnum; + [UTF8String] + private string _utf8String; + [IA5String] + private string _ia5String; + [BMPString] + private string _bmpString; + private bool[] _bools; + [SetOf] + private int[] _ints; + [SequenceOf] + private byte[] _littleUInts; + [UtcTime] + public DateTimeOffset UtcTime2049; + [UtcTime(TwoDigitYearMax = 2099)] + public DateTimeOffset UtcTime2099; + [GeneralizedTime] + public DateTimeOffset GeneralizedTimeWithFractions; + [GeneralizedTime(DisallowFractions = true)] + public DateTimeOffset GeneralizedTimeNoFractions; + public BigInteger BigInteger; + + public bool NotBool + { + get => !_bool; + set => _bool = !value; + } + + public sbyte SByte + { + get => _sbyte; + set => _sbyte = value; + } + + public byte Byte + { + get => _byte; + set => _byte = value; + } + + public short Short + { + get => _short; + set => _short = value; + } + + public ushort UShort + { + get => _ushort; + set => _ushort = value; + } + + public int Int + { + get => _int; + set => _int = value; + } + + public uint UInt + { + get => _uint; + set => _uint = value; + } + + public long Long + { + get => _long; + set => _long = value; + } + + public ulong ULong + { + get => _ulong; + set => _ulong = value; + } + + public ReadOnlyMemory BigIntBytes + { + get => _bigInt; + set => _bigInt = value.ToArray(); + } + + public ReadOnlyMemory BitStringBytes + { + get => _bitString; + set => _bitString = value.ToArray(); + } + + public ReadOnlyMemory OctetStringBytes + { + get => _octetString; + set => _octetString = value.ToArray(); + } + + public ReadOnlyMemory Null + { + get => _null; + set => _null = value.ToArray(); + } + + public Oid UnattrOid + { + get => _oidNoName; + set => _oidNoName = value; + } + + public Oid WithName + { + get => _oid; + set => _oid = value; + } + + public string OidString + { + get => _oidString; + set => _oidString = value; + } + + public UniversalTagNumber LinearEnum + { + get => _nonFlagsEnum; + set => _nonFlagsEnum = value; + } + + public string Utf8Encoded + { + get => _utf8String; + set => _utf8String = value; + } + + public string Ia5Encoded + { + get => _ia5String; + set => _ia5String = value; + } + + public string BmpEncoded + { + get => _bmpString; + set => _bmpString = value; + } + + public bool[] Bools + { + get => _bools; + set => _bools = value; + } + + public int[] Ints + { + get => _ints; + set => _ints = value; + } + + public byte[] LittleUInts + { + get => _littleUInts; + set => _littleUInts = value; + } + } + + [Choice] + [StructLayout(LayoutKind.Sequential)] + public struct DirectoryString + { + [UTF8String] + public string Utf8String; + [BMPString] + public string BmpString; + } + + [Choice] + [StructLayout(LayoutKind.Sequential)] + public struct FlexibleString + { + public DirectoryString? DirectoryString; + + [IA5String] + public string Ascii; + } + + [Choice(AllowNull = true)] + [StructLayout(LayoutKind.Sequential)] + public sealed class DirectoryStringClass + { + [UTF8String] + public string Utf8String; + [BMPString] + public string BmpString; + [PrintableString] + public string PrintableString; + } + + [Choice] + [StructLayout(LayoutKind.Sequential)] + public sealed class FlexibleStringClass + { + public DirectoryStringClass DirectoryString; + + [IA5String] + public string Ascii; + } + + [Choice] + [StructLayout(LayoutKind.Sequential)] + public sealed class FlexibleStringClassHybrid + { + public DirectoryString? DirectoryString; + + [IA5String] + public string Ascii; + } + + [Choice] + [StructLayout(LayoutKind.Sequential)] + public struct FlexibleStringStructHybrid + { + public DirectoryStringClass DirectoryString; + + [IA5String] + public string Ascii; + } + + [Choice] + [StructLayout(LayoutKind.Sequential)] + public sealed class CycleRoot + { + public Cycle2 C2; + } + + [Choice] + [StructLayout(LayoutKind.Sequential)] + public sealed class Cycle2 + { + public Cycle3 C3; + } + + [Choice] + [StructLayout(LayoutKind.Sequential)] + public sealed class Cycle3 + { + public CycleRoot CycleRoot; + } + + [Choice] + [StructLayout(LayoutKind.Sequential)] + public struct ContextSpecificChoice + { + [UTF8String] + [ExpectedTag(3)] + public string Utf8String; + + [IA5String] + [ExpectedTag(2)] + public string IA5String; + } + + [StructLayout(LayoutKind.Sequential)] + public struct UtcTimeTwoDigitYears + { + [UtcTime(TwoDigitYearMax = 2011)] + public DateTimeOffset ErnestoSabatoLifetime; + + [UtcTime(TwoDigitYearMax = 2012)] + public DateTimeOffset MayanPhenomenon; + + [UtcTime] + public DateTimeOffset ImplicitMax; + } + + [Flags] + public enum SomeFlagsEnum : short + { + None = 0, + BitZero = 1 << 0, + BitOne = 1 << 1, + BitTwo = 1 << 2, + BitThree = 1 << 3, + BitFour = 1 << 4, + BitFive = 1 << 5, + BitSix = 1 << 6, + BitSeven = 1 << 7, + BitEight = 1 << 8, + BitNine = 1 << 9, + BitTen = 1 << 10, + BitEleven = 1 << 11, + BitTwelve = 1 << 12, + BitThirteen = 1 << 13, + BitFourteen = 1 << 14, + BitFifteen = short.MinValue, + } + + [StructLayout(LayoutKind.Sequential)] + public struct NamedBitListModeVariants + { + public SomeFlagsEnum DefaultMode; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ExplicitValueStruct + { + [ExpectedTag(0, ExplicitTag = true)] + public int ExplicitInt; + + public int ImplicitInt; + } + + [StructLayout(LayoutKind.Sequential)] + public struct AnyWithExpectedTag + { + [ObjectIdentifier] + public string Id; + + [AnyValue] + [ExpectedTag(TagClass.Universal, (int)UniversalTagNumber.Sequence)] + public ReadOnlyMemory Data; + } + + [StructLayout(LayoutKind.Sequential)] + public struct OptionalValues + { + [UTF8String, OptionalValue] + public string Utf8String; + + [IA5String, OptionalValue] + public string IA5String; + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Serializer/SimpleSerialize.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Serializer/SimpleSerialize.cs new file mode 100644 index 0000000000..157f56ab71 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Serializer/SimpleSerialize.cs @@ -0,0 +1,431 @@ +// 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.Globalization; +using System.Numerics; +using System.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public static class SimpleSerialize + { + [Fact] + public static void SerializeAlgorithmIdentifier() + { + AlgorithmIdentifier identifier = new AlgorithmIdentifier + { + Algorithm = new Oid("2.16.840.1.101.3.4.2.1", "SHA-2-256"), + Parameters = new byte[] { 5, 0 }, + }; + + using (AsnWriter writer = AsnSerializer.Serialize(identifier, AsnEncodingRules.DER)) + { + + const string ExpectedHex = + "300D" + + "0609608648016503040201" + + "0500"; + + Assert.Equal(ExpectedHex, writer.Encode().ByteArrayToHex()); + } + } + + [Fact] + public static void SerializeAlgorithmIdentifier_CER() + { + AlgorithmIdentifier identifier = new AlgorithmIdentifier + { + Algorithm = new Oid("2.16.840.1.101.3.4.2.1", "SHA-2-256"), + Parameters = new byte[] { 5, 0 }, + }; + + using (AsnWriter writer = AsnSerializer.Serialize(identifier, AsnEncodingRules.CER)) + { + const string ExpectedHex = + "3080" + + "0609608648016503040201" + + "0500" + + "0000"; + + Assert.Equal(ExpectedHex, writer.Encode().ByteArrayToHex()); + } + } + + [Fact] + public static void SerializeAllTheSimpleThings_CER() + { + const string ExpectedHex = + "3080" + + "0101FF" + + "0201FE" + + "020101" + + "0202FEFF" + + "02020101" + + "0204FEFFFFFF" + + "020401000001" + + "0208FEFFFFFFFFFFFFFF" + + "02080100000000000001" + + "0209010000000000000001" + + "0303000102" + + "0404FF0055AA" + + "0500" + + "06082A8648CE3D030107" + + "06072A8648CE3D0201" + + "06092A864886F70D010101" + + "0A011E" + + "0C2544722E2026204D72732E20536D697468E280904A6F6E657320EFB9A0206368696C6472656E" + + "162144722E2026204D72732E20536D6974682D4A6F6E65732026206368696C6472656E" + + "1E42" + + "00440072002E002000260020004D00720073002E00200053006D006900740068" + + "2010004A006F006E006500730020FE600020006300680069006C006400720065" + + "006E" + + "3080" + + "010100" + + "010100" + + "0101FF" + + "0101FF" + + "010100" + + "0000" + + "3180" + + "020100" + + "020101" + + "0201FE" + + "0201FF" + + "02020100" + + "0000" + + "3080" + + "020100" + + "020101" + + "020200FE" + + "02017F" + + "020200FF" + + "0000" + + "170D3530303130323132333435365A" + + "170D3530303130323132333435365A" + + // This is different than what we read in deserialize, + // because we don't write back the .0004 second. + "181332303136313130363031323334352E3736355A" + + "180F32303136313130363031323334355A" + + "020F0102030405060708090A0B0C0D0E0F" + + "0000"; + + const string UnicodeVerifier = "Dr. & Mrs. Smith\u2010Jones \uFE60 children"; + const string AsciiVerifier = "Dr. & Mrs. Smith-Jones & children"; + + var allTheThings = new AllTheSimpleThings + { + NotBool = false, + SByte = -2, + Byte = 1, + Short = unchecked((short)0xFEFF), + UShort = 0x0101, + Int = unchecked((int)0xFEFFFFFF), + UInt = 0x01000001U, + Long = unchecked((long)0xFEFFFFFFFFFFFFFF), + ULong = 0x0100000000000001UL, + BigIntBytes = "010000000000000001".HexToByteArray(), + BitStringBytes = new byte[] { 1, 2 }, + OctetStringBytes = new byte[] { 0xFF, 0, 0x55, 0xAA }, + Null = new byte[] { 5, 0 }, + UnattrOid = new Oid("1.2.840.10045.3.1.7", "1.2.840.10045.3.1.7"), + WithName = new Oid("1.2.840.10045.2.1", "ECC"), + OidString = "1.2.840.113549.1.1.1", + LinearEnum = UniversalTagNumber.BMPString, + Utf8Encoded = UnicodeVerifier, + Ia5Encoded = AsciiVerifier, + BmpEncoded = UnicodeVerifier, + Bools = new[] { false, false, true, true, false }, + Ints = new [] { 0, 1, -2, -1, 256 }, + LittleUInts = new byte[] { 0, 1, 254, 127, 255 }, + UtcTime2049 = new DateTimeOffset(1950, 1, 2, 12, 34, 56, TimeSpan.Zero), + // 1950 is out of range for the reader, but the writer just does mod 100. + UtcTime2099 = new DateTimeOffset(1950, 1, 2, 12, 34, 56, TimeSpan.Zero), + GeneralizedTimeWithFractions = new DateTimeOffset(2016, 11, 6, 1, 23, 45, 765, TimeSpan.Zero), + // The fractions will be dropped off by the serializer/writer, to simplify + // the cases where the time was computed and isn't an integer number of seconds. + GeneralizedTimeNoFractions = new DateTimeOffset(2016, 11, 6, 1, 23, 45, 765, TimeSpan.Zero), + BigInteger = BigInteger.Parse("0102030405060708090A0B0C0D0E0F", NumberStyles.HexNumber), + }; + + using (AsnWriter writer = AsnSerializer.Serialize(allTheThings, AsnEncodingRules.CER)) + { + Assert.Equal(ExpectedHex, writer.Encode().ByteArrayToHex()); + } + } + + [Fact] + public static void SerializeChoice_Null() + { + DirectoryStringClass directoryString = default; + + using (AsnWriter writer = AsnSerializer.Serialize(directoryString, AsnEncodingRules.DER)) + { + Assert.Equal("0500", writer.Encode().ByteArrayToHex()); + } + } + + [Fact] + public static void SerializeChoice_First() + { + DirectoryStringClass directoryString = new DirectoryStringClass + { + Utf8String = "UTF8", + }; + + using (AsnWriter writer = AsnSerializer.Serialize(directoryString, AsnEncodingRules.DER)) + { + Assert.Equal("0C0455544638", writer.Encode().ByteArrayToHex()); + } + } + + [Fact] + public static void SerializeChoice_Second() + { + DirectoryStringClass directoryString = new DirectoryStringClass + { + BmpString = "BMP", + }; + + using (AsnWriter writer = AsnSerializer.Serialize(directoryString, AsnEncodingRules.DER)) + { + Assert.Equal("1E060042004D0050", writer.Encode().ByteArrayToHex()); + } + } + + [Fact] + public static void SerializeChoice_Third() + { + DirectoryStringClass directoryString = new DirectoryStringClass + { + PrintableString = "Printable", + }; + + using (AsnWriter writer = AsnSerializer.Serialize(directoryString, AsnEncodingRules.DER)) + { + Assert.Equal("13095072696E7461626C65", writer.Encode().ByteArrayToHex()); + } + } + + [Fact] + public static void SerializeChoice_NoSelection() + { + DirectoryStringClass directoryString = new DirectoryStringClass(); + + Assert.ThrowsAny( + () => AsnSerializer.Serialize(directoryString, AsnEncodingRules.DER)); + } + + [Fact] + public static void SerializeChoice_MultipleSelections() + { + DirectoryStringClass directoryString = new DirectoryStringClass + { + BmpString = "BMP", + PrintableString = "Printable", + }; + + Assert.ThrowsAny( + () => AsnSerializer.Serialize(directoryString, AsnEncodingRules.DER)); + } + + [Fact] + public static void SerializeChoice_WithinChoice() + { + var hybrid = new FlexibleStringClassHybrid + { + Ascii = "IA5", + }; + + using (AsnWriter writer = AsnSerializer.Serialize(hybrid, AsnEncodingRules.DER)) + { + Assert.Equal("1603494135", writer.Encode().ByteArrayToHex()); + } + } + + [Fact] + public static void SerializeChoice_WithinChoice2() + { + var hybrid = new FlexibleStringClassHybrid + { + DirectoryString = new DirectoryString + { + Utf8String = "Marco", + }, + }; + + using (AsnWriter writer = AsnSerializer.Serialize(hybrid, AsnEncodingRules.DER)) + { + Assert.Equal("0C054D6172636F", writer.Encode().ByteArrayToHex()); + } + } + + [Fact] + public static void SerializeChoice_WithinChoice3() + { + var hybrid = new FlexibleStringClassHybrid + { + DirectoryString = new DirectoryString + { + BmpString = "Polo", + }, + }; + + using (AsnWriter writer = AsnSerializer.Serialize(hybrid, AsnEncodingRules.DER)) + { + Assert.Equal("1E080050006F006C006F", writer.Encode().ByteArrayToHex()); + } + } + + [Fact] + public static void SerializeChoice_WithinChoice4() + { + var hybrid = new FlexibleStringStructHybrid + { + DirectoryString = new DirectoryStringClass + { + BmpString = "Polo", + }, + }; + + using (AsnWriter writer = AsnSerializer.Serialize(hybrid, AsnEncodingRules.DER)) + { + Assert.Equal("1E080050006F006C006F", writer.Encode().ByteArrayToHex()); + } + } + + [Fact] + public static void SerializeChoice_WithinChoice5() + { + var hybrid = new FlexibleStringStructHybrid + { + DirectoryString = new DirectoryStringClass + { + Utf8String = "Marco", + }, + }; + + using (AsnWriter writer = AsnSerializer.Serialize(hybrid, AsnEncodingRules.DER)) + { + Assert.Equal("0C054D6172636F", writer.Encode().ByteArrayToHex()); + } + } + + [Fact] + public static void SerializeChoice_WithinChoice6() + { + var hybrid = new FlexibleStringStructHybrid + { + Ascii = "IA5", + }; + + using (AsnWriter writer = AsnSerializer.Serialize(hybrid, AsnEncodingRules.DER)) + { + Assert.Equal("1603494135", writer.Encode().ByteArrayToHex()); + } + } + + [Fact] + public static void SerializeNamedBitList() + { + var flagsContainer = new NamedBitListModeVariants + { + DefaultMode = SomeFlagsEnum.BitEleven | SomeFlagsEnum.BitTwo | SomeFlagsEnum.BitFourteen + }; + + using (AsnWriter writer = AsnSerializer.Serialize(flagsContainer, AsnEncodingRules.DER)) + { + Assert.Equal("30050303012012", writer.Encode().ByteArrayToHex()); + } + } + + [Fact] + public static void SerializeDefaultValue_AsDefault() + { + var extension = new X509DeserializeTests.Extension + { + ExtnId = "2.5.29.19", + Critical = false, + ExtnValue = new byte[] { 0x30, 0x00 }, + }; + + using (AsnWriter writer = AsnSerializer.Serialize(extension, AsnEncodingRules.DER)) + { + Assert.Equal("30090603551D1304023000", writer.Encode().ByteArrayToHex()); + } + } + + [Fact] + public static void SerializeDefaultValue_AsNonDefault() + { + var extension = new X509DeserializeTests.Extension + { + ExtnId = "2.5.29.15", + Critical = true, + ExtnValue = new byte[] { 0x03, 0x02, 0x05, 0xA0 }, + }; + + using (AsnWriter writer = AsnSerializer.Serialize(extension, AsnEncodingRules.DER)) + { + Assert.Equal("300E0603551D0F0101FF0404030205A0", writer.Encode().ByteArrayToHex()); + } + } + + [Fact] + public static void SerializeExplicitValue() + { + var data = new ExplicitValueStruct + { + ExplicitInt = 3, + ImplicitInt = 0x17, + }; + + using (AsnWriter writer = AsnSerializer.Serialize(data, AsnEncodingRules.DER)) + { + Assert.Equal("3008A003020103020117", writer.Encode().ByteArrayToHex()); + } + } + + [Fact] + public static void WriteAnyValueWithExpectedTag() + { + byte[] anyValue = "3003010100".HexToByteArray(); + + var data = new AnyWithExpectedTag + { + Id = "0.0", + Data = anyValue, + }; + + using (AsnWriter writer = AsnSerializer.Serialize(data, AsnEncodingRules.DER)) + { + Assert.Equal("30080601003003010100", writer.Encode().ByteArrayToHex()); + } + + anyValue[0] = 0xA0; + + Assert.Throws(() => AsnSerializer.Serialize(data, AsnEncodingRules.DER)); + } + + [Theory] + [InlineData("3000", false, false)] + [InlineData("30051603494135", false, true)] + [InlineData("30060C0455544638", true, false)] + [InlineData("300B0C04555446381603494135", true, true)] + public static void WriteOptionals(string expectedHex, bool hasUtf8, bool hasIa5) + { + var data = new OptionalValues + { + Utf8String = hasUtf8 ? "UTF8" : null, + IA5String = hasIa5 ? "IA5" : null, + }; + + using (AsnWriter writer = AsnSerializer.Serialize(data, AsnEncodingRules.DER)) + { + Assert.Equal(expectedHex, writer.Encode().ByteArrayToHex()); + } + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Serializer/X509DeserializeTests.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Serializer/X509DeserializeTests.cs new file mode 100644 index 0000000000..f781f6329a --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Serializer/X509DeserializeTests.cs @@ -0,0 +1,188 @@ +// 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.Runtime.InteropServices; +using System.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public static class X509DeserializeTests + { + [Fact] + public static void ReadMicrosoftDotCom() + { + byte[] buf = Convert.FromBase64String(MicrosoftDotComBase64); + + Certificate cert = AsnSerializer.Deserialize( + buf, + AsnEncodingRules.DER); + + ref TbsCertificate tbsCertificate = ref cert.TbsCertificate; + ref SubjectPublicKeyInfo spki = ref tbsCertificate.SubjectPublicKeyInfo; + + Assert.Equal(2, tbsCertificate.Version); + Assert.Equal("3DF70C5D9903F8D8868B9B8CCF20DF69", tbsCertificate.SerialNumber.ByteArrayToHex()); + + Assert.Equal("1.2.840.113549.1.1.11", tbsCertificate.Signature.Algorithm.Value); + Assert.Equal("0500", tbsCertificate.Signature.Parameters.ByteArrayToHex()); + + // Issuer goes here + + Assert.Equal(new DateTimeOffset(2014, 10, 15, 0, 0, 0, TimeSpan.Zero), tbsCertificate.Validity.NotBefore.Value); + Assert.Equal(new DateTimeOffset(2016, 10, 15, 23, 59, 59, TimeSpan.Zero), tbsCertificate.Validity.NotAfter.Value); + + // Subject goes here + + Assert.Equal("1.2.840.113549.1.1.1", spki.AlgorithmIdentifier.Algorithm.Value); + Assert.Equal("0500", spki.AlgorithmIdentifier.Parameters.ByteArrayToHex()); + Assert.Equal( + "3082010A0282010100A46861FA9D5DB763633BF5A64EF6E7C2C2367F48D2D466" + + "43A22DFCFCCB24E58A14D0F06BDC956437F2A56BA4BEF70BA361BF12964A0D66" + + "5AFD84B0F7494C8FA4ABC5FCA2E017C06178AEF2CDAD1B5F18E997A14B965C07" + + "4E8F564970607276B00583932240FE6E2DD013026F9AE13D7C91CC07C4E1E8E8" + + "7737DC06EF2B575B89D62EFE46859F8255A123692A706C68122D4DAFE11CB205" + + "A7B3DE06E553F7B95F978EF8601A8DF819BF32040BDF92A0DE0DF269B4514282" + + "E17AC69934E8440A48AB9D1F5DF89A502CEF6DFDBE790045BD45E0C94E5CA8AD" + + "D76A013E9C978440FC8A9E2A9A4940B2460819C3E302AA9C9F355AD754C86D3E" + + "D77DDAA3DA13810B4D0203010001", + spki.PublicKey.ByteArrayToHex()); + + Assert.Null(tbsCertificate.IssuerUniqueId); + Assert.Null(tbsCertificate.SubjectUniqueId); + + Assert.Equal(8, tbsCertificate.Extensions.Length); + Assert.Equal("2.5.29.17", tbsCertificate.Extensions[0].ExtnId); + Assert.Equal("2.5.29.19", tbsCertificate.Extensions[1].ExtnId); + Assert.Equal("2.5.29.15", tbsCertificate.Extensions[2].ExtnId); + Assert.Equal("2.5.29.37", tbsCertificate.Extensions[3].ExtnId); + Assert.Equal("2.5.29.32", tbsCertificate.Extensions[4].ExtnId); + Assert.Equal("2.5.29.35", tbsCertificate.Extensions[5].ExtnId); + Assert.Equal("2.5.29.31", tbsCertificate.Extensions[6].ExtnId); + Assert.Equal("1.3.6.1.5.5.7.1.1", tbsCertificate.Extensions[7].ExtnId); + + Assert.Equal("1.2.840.113549.1.1.11", cert.SignatureAlgorithm.Algorithm.Value); + Assert.Equal("0500", cert.SignatureAlgorithm.Parameters.ByteArrayToHex()); + + Assert.Equal( + "15F8505B627ED7F9F96707097E93A51E7A7E05A3D420A5C258EC7A1CFE1843EC" + + "20ACF728AAFA7A1A1BC222A7CDBF4AF90AA26DEEB3909C0B3FB5C78070DAE3D6" + + "45BFCF840A4A3FDD988C7B3308BFE4EB3FD66C45641E96CA3352DBE2AEB4488A" + + "64A9C5FB96932BA70059CE92BD278B41299FD213471BD8165F924285AE3ECD66" + + "6C703885DCA65D24DA66D3AFAE39968521995A4C398C7DF38DFA82A20372F13D" + + "4A56ADB21B5822549918015647B5F8AC131CC5EB24534D172BC60218A88B65BC" + + "F71C7F388CE3E0EF697B4203720483BB5794455B597D80D48CD3A1D73CBBC609" + + "C058767D1FF060A609D7E3D4317079AF0CD0A8A49251AB129157F9894A036487", + cert.Signature.ByteArrayToHex()); + } + + [StructLayout(LayoutKind.Sequential)] + internal struct Certificate + { + public TbsCertificate TbsCertificate; + public AlgorithmIdentifier SignatureAlgorithm; + [BitString] + public ReadOnlyMemory Signature; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct TbsCertificate + { + [ExpectedTag(0, ExplicitTag = true)] + [DefaultValue(0x02, 0x01, 0x01)] + public int Version; + + [Integer] + public ReadOnlyMemory SerialNumber; + + public AlgorithmIdentifier Signature; + + [AnyValue] + public ReadOnlyMemory Issuer; + + public Validity Validity; + + [AnyValue] + public ReadOnlyMemory Subject; + + public SubjectPublicKeyInfo SubjectPublicKeyInfo; + + [ExpectedTag(1), BitString, OptionalValue] + public ReadOnlyMemory? IssuerUniqueId; + + [ExpectedTag(2), BitString, OptionalValue] + public ReadOnlyMemory? SubjectUniqueId; + + [ExpectedTag(3, ExplicitTag = true), OptionalValue] + public Extension[] Extensions; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct Extension + { + [ObjectIdentifier] + public string ExtnId; + + [DefaultValue(0x01, 0x01, 0x00)] + public bool Critical; + + [OctetString] + public ReadOnlyMemory ExtnValue; + } + + [StructLayout(LayoutKind.Sequential)] + public struct Validity + { + public Time NotBefore; + public Time NotAfter; + } + + [Choice] + [StructLayout(LayoutKind.Sequential)] + public struct Time + { + [UtcTime] + public DateTimeOffset? UtcTime; + [GeneralizedTime(DisallowFractions = true)] + public DateTimeOffset? GeneralTime; + + public DateTimeOffset Value => UtcTime ?? GeneralTime.Value; + } + + private const string MicrosoftDotComBase64 = + @" +MIIFlDCCBHygAwIBAgIQPfcMXZkD+NiGi5uMzyDfaTANBgkqhkiG9w0BAQsFADB3 +MQswCQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAd +BgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMTH1N5bWFudGVj +IENsYXNzIDMgRVYgU1NMIENBIC0gRzMwHhcNMTQxMDE1MDAwMDAwWhcNMTYxMDE1 +MjM1OTU5WjCCAQ8xEzARBgsrBgEEAYI3PAIBAxMCVVMxGzAZBgsrBgEEAYI3PAIB +AgwKV2FzaGluZ3RvbjEdMBsGA1UEDxMUUHJpdmF0ZSBPcmdhbml6YXRpb24xEjAQ +BgNVBAUTCTYwMDQxMzQ4NTELMAkGA1UEBhMCVVMxDjAMBgNVBBEMBTk4MDUyMRMw +EQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdSZWRtb25kMRgwFgYDVQQJDA8x +IE1pY3Jvc29mdCBXYXkxHjAcBgNVBAoMFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEO +MAwGA1UECwwFTVNDT00xGjAYBgNVBAMMEXd3dy5taWNyb3NvZnQuY29tMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApGhh+p1dt2NjO/WmTvbnwsI2f0jS +1GZDoi38/Msk5YoU0PBr3JVkN/Kla6S+9wujYb8SlkoNZlr9hLD3SUyPpKvF/KLg +F8BheK7yza0bXxjpl6FLllwHTo9WSXBgcnawBYOTIkD+bi3QEwJvmuE9fJHMB8Th +6Oh3N9wG7ytXW4nWLv5GhZ+CVaEjaSpwbGgSLU2v4RyyBaez3gblU/e5X5eO+GAa +jfgZvzIEC9+SoN4N8mm0UUKC4XrGmTToRApIq50fXfiaUCzvbf2+eQBFvUXgyU5c +qK3XagE+nJeEQPyKniqaSUCyRggZw+MCqpyfNVrXVMhtPtd92qPaE4ELTQIDAQAB +o4IBgDCCAXwwMQYDVR0RBCowKIIRd3d3Lm1pY3Jvc29mdC5jb22CE3d3d3FhLm1p +Y3Jvc29mdC5jb20wCQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYw +FAYIKwYBBQUHAwEGCCsGAQUFBwMCMGYGA1UdIARfMF0wWwYLYIZIAYb4RQEHFwYw +TDAjBggrBgEFBQcCARYXaHR0cHM6Ly9kLnN5bWNiLmNvbS9jcHMwJQYIKwYBBQUH +AgIwGRoXaHR0cHM6Ly9kLnN5bWNiLmNvbS9ycGEwHwYDVR0jBBgwFoAUAVmr5906 +C1mmZGPWzyAHV9WR52owKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL3NyLnN5bWNi +LmNvbS9zci5jcmwwVwYIKwYBBQUHAQEESzBJMB8GCCsGAQUFBzABhhNodHRwOi8v +c3Iuc3ltY2QuY29tMCYGCCsGAQUFBzAChhpodHRwOi8vc3Iuc3ltY2IuY29tL3Ny +LmNydDANBgkqhkiG9w0BAQsFAAOCAQEAFfhQW2J+1/n5ZwcJfpOlHnp+BaPUIKXC +WOx6HP4YQ+wgrPcoqvp6GhvCIqfNv0r5CqJt7rOQnAs/tceAcNrj1kW/z4QKSj/d +mIx7Mwi/5Os/1mxFZB6WyjNS2+KutEiKZKnF+5aTK6cAWc6SvSeLQSmf0hNHG9gW +X5JCha4+zWZscDiF3KZdJNpm06+uOZaFIZlaTDmMffON+oKiA3LxPUpWrbIbWCJU +mRgBVke1+KwTHMXrJFNNFyvGAhioi2W89xx/OIzj4O9pe0IDcgSDu1eURVtZfYDU +jNOh1zy7xgnAWHZ9H/BgpgnX49QxcHmvDNCopJJRqxKRV/mJSgNkhw== +"; + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/Asn1WriterTests.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/Asn1WriterTests.cs new file mode 100644 index 0000000000..316eb28b59 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/Asn1WriterTests.cs @@ -0,0 +1,54 @@ +// 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.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public abstract partial class Asn1WriterTests : Asn1ReaderTests + { + internal static void Verify(AsnWriter writer, string expectedHex) + { + byte[] encoded = writer.Encode(); + Assert.Equal(expectedHex, encoded.ByteArrayToHex()); + + // Now verify TryEncode's boundary conditions. + byte[] encoded2 = new byte[encoded.Length + 3]; + encoded2[0] = 255; + encoded2[encoded.Length] = 254; + + Span dest = encoded2.AsSpan().Slice(0, encoded.Length - 1); + Assert.False(writer.TryEncode(dest, out int bytesWritten), "writer.TryEncode (too small)"); + Assert.Equal(0, bytesWritten); + Assert.Equal(255, encoded2[0]); + Assert.Equal(254, encoded2[encoded.Length]); + + dest = encoded2.AsSpan().Slice(0, encoded.Length); + Assert.True(writer.TryEncode(dest, out bytesWritten), "writer.TryEncode (exact length)"); + Assert.Equal(encoded.Length, bytesWritten); + Assert.True(dest.SequenceEqual(encoded), "dest.SequenceEqual(encoded2) (exact length)"); + Assert.Equal(254, encoded2[encoded.Length]); + + // Start marker was obliterated, but the stop marker is still intact. Keep it there. + Array.Clear(encoded2, 0, bytesWritten); + + dest = encoded2.AsSpan(); + Assert.True(writer.TryEncode(dest, out bytesWritten), "writer.TryEncode (overly big)"); + Assert.Equal(encoded.Length, bytesWritten); + Assert.True(dest.Slice(0, bytesWritten).SequenceEqual(encoded), "dest.SequenceEqual(encoded2) (overly big)"); + Assert.Equal(254, encoded2[encoded.Length]); + } + + internal static unsafe string Stringify(Asn1Tag tag) + { + byte* stackspace = stackalloc byte[10]; + Span dest = new Span(stackspace, 10); + + Assert.True(tag.TryWrite(dest, out int size)); + return dest.Slice(0, size).ByteArrayToHex(); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/ComprehensiveWriteTest.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/ComprehensiveWriteTest.cs new file mode 100644 index 0000000000..157e19830d --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/ComprehensiveWriteTest.cs @@ -0,0 +1,290 @@ +// 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.Numerics; +using System.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +using X509KeyUsageCSharpStyle = System.Security.Cryptography.Tests.Asn1.ReadNamedBitList.X509KeyUsageCSharpStyle; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public static class ComprehensiveWriteTest + { + [Fact] + public static void WriteMicrosoftDotComCert() + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + { + // Certificate + writer.PushSequence(); + + // tbsCertificate + writer.PushSequence(); + + // version ([0] EXPLICIT INTEGER) + Asn1Tag context0 = new Asn1Tag(TagClass.ContextSpecific, 0, true); + writer.PushSequence(context0); + writer.WriteInteger(2); + writer.PopSequence(context0); + + BigInteger serialValue = BigInteger.Parse("82365655871428336739211871484630851433"); + writer.WriteInteger(serialValue); + + // signature (algorithm) + writer.PushSequence(); + writer.WriteObjectIdentifier("1.2.840.113549.1.1.11"); + writer.WriteNull(); + writer.PopSequence(); + + // issuer + writer.PushSequence(); + WriteRdn(writer, "2.5.4.6", "US", UniversalTagNumber.PrintableString); + WriteRdn(writer, "2.5.4.10", "Symantec Corporation", UniversalTagNumber.PrintableString); + WriteRdn(writer, "2.5.4.11", "Symantec Trust Network", UniversalTagNumber.PrintableString); + WriteRdn(writer, "2.5.4.3", "Symantec Class 3 EV SSL CA - G3", UniversalTagNumber.PrintableString); + writer.PopSequence(); + + // validity + writer.PushSequence(); + writer.WriteUtcTime(new DateTimeOffset(2014, 10, 15, 0, 0, 0, TimeSpan.Zero)); + writer.WriteUtcTime(new DateTimeOffset(2016, 10, 15, 23, 59, 59, TimeSpan.Zero)); + writer.PopSequence(); + + // subject + writer.PushSequence(); + WriteRdn(writer, "1.3.6.1.4.1.311.60.2.1.3", "US", UniversalTagNumber.PrintableString); + WriteRdn(writer, "1.3.6.1.4.1.311.60.2.1.2", "Washington", UniversalTagNumber.UTF8String); + WriteRdn(writer, "2.5.4.15", "Private Organization", UniversalTagNumber.PrintableString); + WriteRdn(writer, "2.5.4.5", "600413485", UniversalTagNumber.PrintableString); + WriteRdn(writer, "2.5.4.6", "US", UniversalTagNumber.PrintableString); + WriteRdn(writer, "2.5.4.17", "98052", UniversalTagNumber.UTF8String); + WriteRdn(writer, "2.5.4.8", "Washington", UniversalTagNumber.UTF8String); + WriteRdn(writer, "2.5.4.7", "Redmond", UniversalTagNumber.UTF8String); + WriteRdn(writer, "2.5.4.9", "1 Microsoft Way", UniversalTagNumber.UTF8String); + WriteRdn(writer, "2.5.4.10", "Microsoft Corporation", UniversalTagNumber.UTF8String); + WriteRdn(writer, "2.5.4.11", "MSCOM", UniversalTagNumber.UTF8String); + WriteRdn(writer, "2.5.4.3", "www.microsoft.com", UniversalTagNumber.UTF8String); + writer.PopSequence(); + + // subjectPublicKeyInfo + writer.PushSequence(); + // subjectPublicKeyInfo.algorithm + writer.PushSequence(); + writer.WriteObjectIdentifier("1.2.840.113549.1.1.1"); + writer.WriteNull(); + writer.PopSequence(); + + using (AsnWriter publicKeyWriter = new AsnWriter(AsnEncodingRules.DER)) + { + publicKeyWriter.PushSequence(); + BigInteger modulus = BigInteger.Parse( + "207545550571844404676608632512851454930111394466749205318948660756381" + + "523214360115124048083611193260260272384440199925180817531535965931647" + + "037093368608713442955529617501657176146245891571745113402870077189045" + + "116705181899983704226178882882602868159586789723579670915035003754974" + + "985730226756711782751711104985926458681071638525996766798322809764200" + + "941677343791419428587801897366593842552727222686457866144928124161967" + + "521735393182823375650694786333059783380738262856873316471830589717911" + + "537307419734834201104082715701367336140572971505716740825623220507359" + + "42929758463490933054115079473593821332264673455059897928082590541"); + publicKeyWriter.WriteInteger(modulus); + publicKeyWriter.WriteInteger(65537); + publicKeyWriter.PopSequence(); + + // subjectPublicKeyInfo.subjectPublicKey + writer.WriteBitString(publicKeyWriter.Encode()); + writer.PopSequence(); + } + + // extensions ([3] EXPLICIT Extensions) + Asn1Tag context3 = new Asn1Tag(TagClass.ContextSpecific, 3); + writer.PushSequence(context3); + writer.PushSequence(); + + Asn1Tag dnsName = new Asn1Tag(TagClass.ContextSpecific, 2); + + using (AsnWriter extensionValueWriter = new AsnWriter(AsnEncodingRules.DER)) + { + extensionValueWriter.PushSequence(); + extensionValueWriter.WriteCharacterString(dnsName, UniversalTagNumber.IA5String, "www.microsoft.com"); + extensionValueWriter.WriteCharacterString(dnsName, UniversalTagNumber.IA5String, "wwwqa.microsoft.com"); + extensionValueWriter.PopSequence(); + + writer.PushSequence(); + writer.WriteObjectIdentifier("2.5.29.17"); + writer.WriteOctetString(extensionValueWriter.Encode()); + writer.PopSequence(); + } + + writer.PushSequence(); + writer.WriteObjectIdentifier("2.5.29.19"); + // Empty sequence as the payload for a non-CA basic constraint. + writer.WriteOctetString(new byte[] { 0x30, 0x00 }); + writer.PopSequence(); + + using (AsnWriter extensionValueWriter = new AsnWriter(AsnEncodingRules.DER)) + { + // This extension doesn't use a sequence at all, just Named Bit List. + extensionValueWriter.WriteNamedBitList( + X509KeyUsageCSharpStyle.DigitalSignature | X509KeyUsageCSharpStyle.KeyEncipherment); + + writer.PushSequence(); + writer.WriteObjectIdentifier("2.5.29.15"); + // critical: true + writer.WriteBoolean(true); + writer.WriteOctetString(extensionValueWriter.Encode()); + writer.PopSequence(); + } + + using (AsnWriter extensionValueWriter = new AsnWriter(AsnEncodingRules.DER)) + { + extensionValueWriter.PushSequence(); + extensionValueWriter.WriteObjectIdentifier("1.3.6.1.5.5.7.3.1"); + extensionValueWriter.WriteObjectIdentifier("1.3.6.1.5.5.7.3.2"); + extensionValueWriter.PopSequence(); + + writer.PushSequence(); + writer.WriteObjectIdentifier("2.5.29.37"); + writer.WriteOctetString(extensionValueWriter.Encode()); + writer.PopSequence(); + } + + using (AsnWriter extensionValueWriter = new AsnWriter(AsnEncodingRules.DER)) + { + extensionValueWriter.PushSequence(); + extensionValueWriter.PushSequence(); + extensionValueWriter.WriteObjectIdentifier("2.16.840.1.113733.1.7.23.6"); + extensionValueWriter.PushSequence(); + extensionValueWriter.PushSequence(); + extensionValueWriter.WriteObjectIdentifier("1.3.6.1.5.5.7.2.1"); + extensionValueWriter.WriteCharacterString(UniversalTagNumber.IA5String, "https://d.symcb.com/cps"); + extensionValueWriter.PopSequence(); + extensionValueWriter.PushSequence(); + extensionValueWriter.WriteObjectIdentifier("1.3.6.1.5.5.7.2.2"); + extensionValueWriter.PushSequence(); + extensionValueWriter.WriteCharacterString(UniversalTagNumber.VisibleString, "https://d.symcb.com/rpa"); + extensionValueWriter.PopSequence(); + extensionValueWriter.PopSequence(); + extensionValueWriter.PopSequence(); + extensionValueWriter.PopSequence(); + extensionValueWriter.PopSequence(); + + writer.PushSequence(); + writer.WriteObjectIdentifier("2.5.29.32"); + writer.WriteOctetString(extensionValueWriter.Encode()); + writer.PopSequence(); + } + + byte[] authorityKeyIdentifier = "0159ABE7DD3A0B59A66463D6CF200757D591E76A".HexToByteArray(); + Asn1Tag keyIdentifier = context0; + using (AsnWriter extensionValueWriter = new AsnWriter(AsnEncodingRules.DER)) + { + extensionValueWriter.PushSequence(); + extensionValueWriter.WriteOctetString(keyIdentifier, authorityKeyIdentifier); + extensionValueWriter.PopSequence(); + + writer.PushSequence(); + writer.WriteObjectIdentifier("2.5.29.35"); + writer.WriteOctetString(extensionValueWriter.Encode()); + writer.PopSequence(); + } + + Asn1Tag distributionPointChoice = context0; + Asn1Tag fullNameChoice = context0; + Asn1Tag generalNameUriChoice = new Asn1Tag(TagClass.ContextSpecific, 6); + using (AsnWriter extensionValueWriter = new AsnWriter(AsnEncodingRules.DER)) + { + extensionValueWriter.PushSequence(); + extensionValueWriter.PushSequence(); + extensionValueWriter.PushSequence(distributionPointChoice); + extensionValueWriter.PushSequence(fullNameChoice); + extensionValueWriter.WriteCharacterString( + generalNameUriChoice, + UniversalTagNumber.IA5String, + "http://sr.symcb.com/sr.crl"); + extensionValueWriter.PopSequence(fullNameChoice); + extensionValueWriter.PopSequence(distributionPointChoice); + extensionValueWriter.PopSequence(); + extensionValueWriter.PopSequence(); + + writer.PushSequence(); + writer.WriteObjectIdentifier("2.5.29.31"); + writer.WriteOctetString(extensionValueWriter.Encode()); + writer.PopSequence(); + } + + using (AsnWriter extensionValueWriter = new AsnWriter(AsnEncodingRules.DER)) + { + extensionValueWriter.PushSequence(); + extensionValueWriter.PushSequence(); + extensionValueWriter.WriteObjectIdentifier("1.3.6.1.5.5.7.48.1"); + extensionValueWriter.WriteCharacterString( + generalNameUriChoice, + UniversalTagNumber.IA5String, + "http://sr.symcd.com"); + extensionValueWriter.PopSequence(); + extensionValueWriter.PushSequence(); + extensionValueWriter.WriteObjectIdentifier("1.3.6.1.5.5.7.48.2"); + extensionValueWriter.WriteCharacterString( + generalNameUriChoice, + UniversalTagNumber.IA5String, + "http://sr.symcb.com/sr.crt"); + extensionValueWriter.PopSequence(); + extensionValueWriter.PopSequence(); + + writer.PushSequence(); + writer.WriteObjectIdentifier("1.3.6.1.5.5.7.1.1"); + writer.WriteOctetString(extensionValueWriter.Encode()); + writer.PopSequence(); + } + + writer.PopSequence(); + writer.PopSequence(context3); + + // tbsCertificate + writer.PopSequence(); + + // signatureAlgorithm + writer.PushSequence(); + writer.WriteObjectIdentifier("1.2.840.113549.1.1.11"); + writer.WriteNull(); + writer.PopSequence(); + + // signature + byte[] containsSignature = ( + "010203040506070809" + + "15F8505B627ED7F9F96707097E93A51E7A7E05A3D420A5C258EC7A1CFE1843EC" + + "20ACF728AAFA7A1A1BC222A7CDBF4AF90AA26DEEB3909C0B3FB5C78070DAE3D6" + + "45BFCF840A4A3FDD988C7B3308BFE4EB3FD66C45641E96CA3352DBE2AEB4488A" + + "64A9C5FB96932BA70059CE92BD278B41299FD213471BD8165F924285AE3ECD66" + + "6C703885DCA65D24DA66D3AFAE39968521995A4C398C7DF38DFA82A20372F13D" + + "4A56ADB21B5822549918015647B5F8AC131CC5EB24534D172BC60218A88B65BC" + + "F71C7F388CE3E0EF697B4203720483BB5794455B597D80D48CD3A1D73CBBC609" + + "C058767D1FF060A609D7E3D4317079AF0CD0A8A49251AB129157F9894A036487" + + "090807060504030201").HexToByteArray(); + + writer.WriteBitString(containsSignature.AsReadOnlySpan().Slice(9, 256)); + + // certificate + writer.PopSequence(); + + Assert.Equal( + ComprehensiveReadTests.MicrosoftDotComSslCertBytes.ByteArrayToHex(), + writer.Encode().ByteArrayToHex()); + } + } + + private static void WriteRdn(AsnWriter writer, string oid, string value, UniversalTagNumber valueType) + { + writer.PushSetOf(); + writer.PushSequence(); + writer.WriteObjectIdentifier(oid); + writer.WriteCharacterString(valueType, value); + writer.PopSequence(); + writer.PopSetOf(); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSequence.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSequence.cs new file mode 100644 index 0000000000..c112ee7181 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSequence.cs @@ -0,0 +1,557 @@ +// 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.Numerics; +using System.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public class PushPopSequence : Asn1WriterTests + { + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void PopNewWriter(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + // Maybe ArgumentException isn't right for this, since no argument was provided. + AssertExtensions.Throws( + "tag", + () => writer.PopSequence()); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void PopNewWriter_CustomTag(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + AssertExtensions.Throws( + "tag", + () => writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void PopBalancedWriter(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.PushSequence(); + writer.PopSequence(); + + // Maybe ArgumentException isn't right for this, since no argument was provided. + AssertExtensions.Throws( + "tag", + () => writer.PopSequence()); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void PopBalancedWriter_CustomTag(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.PushSequence(); + writer.PopSequence(); + + AssertExtensions.Throws( + "tag", + () => writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void PushCustom_PopStandard(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); + + // Maybe ArgumentException isn't right for this, since no argument was provided. + AssertExtensions.Throws( + "tag", + () => writer.PopSequence()); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void PushStandard_PopCustom(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.PushSequence(); + + AssertExtensions.Throws( + "tag", + () => writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void PushPrimitive_PopStandard(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.PushSequence(new Asn1Tag(UniversalTagNumber.Sequence)); + writer.PopSequence(); + + if (ruleSet == PublicEncodingRules.CER) + { + Verify(writer, "30800000"); + } + else + { + Verify(writer, "3000"); + } + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void PushCustomPrimitive_PopConstructed(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.PushSequence(new Asn1Tag(TagClass.Private, 5)); + writer.PopSequence(new Asn1Tag(TagClass.Private, 5, true)); + + if (ruleSet == PublicEncodingRules.CER) + { + Verify(writer, "E5800000"); + } + else + { + Verify(writer, "E500"); + } + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void PushStandard_PopPrimitive(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.PushSequence(); + writer.PopSequence(new Asn1Tag(UniversalTagNumber.Sequence, isConstructed: false)); + + if (ruleSet == PublicEncodingRules.CER) + { + Verify(writer, "30800000"); + } + else + { + Verify(writer, "3000"); + } + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void PushCustomConstructed_PopPrimitive(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.PushSequence(new Asn1Tag(TagClass.Private, (int)ruleSet, true)); + writer.PopSequence(new Asn1Tag(TagClass.Private, (int)ruleSet)); + + byte tag = (byte)((int)ruleSet | 0b1110_0000); + string tagHex = tag.ToString("X2"); + string rest = ruleSet == PublicEncodingRules.CER ? "800000" : "00"; + + Verify(writer, tagHex + rest); + } + } + + [Fact] + public static void BER_WritesDefinite_Empty() + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + writer.PushSequence(); + writer.PopSequence(); + + Verify(writer, "3000"); + } + } + + [Fact] + public static void CER_WritesIndefinite_Empty() + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + writer.PushSequence(); + writer.PopSequence(); + + Verify(writer, "30800000"); + } + } + + [Fact] + public static void DER_WritesDefinite_CustomTag_Empty() + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + { + writer.PushSequence(); + writer.PopSequence(); + + Verify(writer, "3000"); + } + } + + [Fact] + public static void BER_WritesDefinite_CustomTag__Empty() + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.Private, 15, true); + writer.PushSequence(tag); + writer.PopSequence(tag); + + Verify(writer, "EF00"); + } + } + + [Fact] + public static void CER_WritesIndefinite_CustomTag__Empty() + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.Application, 91, true); + writer.PushSequence(tag); + writer.PopSequence(tag); + + Verify(writer, "7F5B800000"); + } + } + + [Fact] + public static void DER_WritesDefinite_CustomTag__Empty() + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 30, true); + writer.PushSequence(tag); + writer.PopSequence(tag); + + Verify(writer, "BE00"); + } + } + + private static void TestNested(AsnWriter writer, Asn1Tag alt, string expectedHex) + { + writer.PushSequence(); + { + writer.PushSequence(alt); + writer.PopSequence(alt); + + writer.PushSequence(); + { + writer.PushSequence(alt); + { + writer.PushSequence(); + writer.PopSequence(); + } + + writer.PopSequence(alt); + } + + writer.PopSequence(); + } + + writer.PopSequence(); + + Verify(writer, expectedHex); + } + + [Fact] + public static void BER_Nested() + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + Asn1Tag alt = new Asn1Tag(TagClass.Private, 127, true); + + TestNested(writer, alt, "300AFF7F003005FF7F023000"); + } + } + + [Fact] + public static void CER_Nested() + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + Asn1Tag alt = new Asn1Tag(TagClass.ContextSpecific, 12, true); + + TestNested(writer, alt, "3080AC8000003080AC8030800000000000000000"); + } + } + + [Fact] + public static void DER_Nested() + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + { + Asn1Tag alt = new Asn1Tag(TagClass.Application, 5, true); + + TestNested(writer, alt, "30086500300465023000"); + } + } + + private static void SimpleContentShift(AsnWriter writer, string expectedHex) + { + writer.PushSequence(); + + // F00DF00D...F00DF00D + byte[] contentBytes = new byte[126]; + + for (int i = 0; i < contentBytes.Length; i += 2) + { + contentBytes[i] = 0xF0; + contentBytes[i + 1] = 0x0D; + } + + writer.WriteOctetString(contentBytes); + writer.PopSequence(); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.DER)] + public static void SimpleContentShift(PublicEncodingRules ruleSet) + { + const string ExpectedHex = + "308180" + + "047E" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D"; + + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + SimpleContentShift(writer, ExpectedHex); + } + } + + [Fact] + public static void SimpleContentShift_CER() + { + const string ExpectedHex = + "3080" + + "047E" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "0000"; + + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + SimpleContentShift(writer, ExpectedHex); + } + } + + private static void WriteRSAPublicKey(AsnEncodingRules ruleSet, string expectedHex) + { + using (AsnWriter innerWriter = new AsnWriter(ruleSet)) + { + byte[] paddedBigEndianN = ( + "00" + + "AF81C1CBD8203F624A539ED6608175372393A2837D4890E48A19DED369731156" + + "20968D6BE0D3DAA38AA777BE02EE0B6B93B724E8DCC12B632B4FA80BBC925BCE" + + "624F4CA7CC606306B39403E28C932D24DD546FFE4EF6A37F10770B2215EA8CBB" + + "5BF427E8C4D89B79EB338375100C5F83E55DE9B4466DDFBEEE42539AEF33EF18" + + "7B7760C3B1A1B2103C2D8144564A0C1039A09C85CF6B5974EB516FC8D6623C94" + + "AE3A5A0BB3B4C792957D432391566CF3E2A52AFB0C142B9E0681B8972671AF2B" + + "82DD390A39B939CF719568687E4990A63050CA7768DCD6B378842F18FDB1F6D9" + + "FF096BAF7BEB98DCF930D66FCFD503F58D41BFF46212E24E3AFC45EA42BD8847").HexToByteArray(); + + // Now it's padded little-endian. + Array.Reverse(paddedBigEndianN); + BigInteger n = new BigInteger(paddedBigEndianN); + const long e = 8589935681; + + innerWriter.PushSequence(); + innerWriter.WriteInteger(n); + innerWriter.WriteInteger(e); + innerWriter.PopSequence(); + + using (AsnWriter outerWriter = new AsnWriter(ruleSet)) + { + // RSAPublicKey + outerWriter.PushSequence(); + + // AlgorithmIdentifier + outerWriter.PushSequence(); + outerWriter.WriteObjectIdentifier("1.2.840.113549.1.1.1"); + outerWriter.WriteNull(); + outerWriter.PopSequence(); + + outerWriter.WriteBitString(innerWriter.Encode()); + outerWriter.PopSequence(); + + Verify(outerWriter, expectedHex); + } + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.DER)] + public static void WriteRSAPublicKey(PublicEncodingRules ruleSet) + { + const string ExpectedHex = + // CONSTRUCTED SEQUENCE + "30820124" + + // CONSTRUCTED SEQUENCE + "300D" + + // OBJECT IDENTIFIER (1.2.840.113549.1.1.1, rsaEncryption) + "06092A864886F70D010101" + + // NULL + "0500" + + // BIT STRING + "03820111" + + // 0 unused bits + "00" + + // sneaky inspection of the payload bytes + // CONSTRUCTED SEQUENCE + "3082010C" + + // INTEGER (n) + "02820101" + + "00AF81C1CBD8203F624A539ED6608175372393A2837D4890E48A19DED3697311" + + "5620968D6BE0D3DAA38AA777BE02EE0B6B93B724E8DCC12B632B4FA80BBC925B" + + "CE624F4CA7CC606306B39403E28C932D24DD546FFE4EF6A37F10770B2215EA8C" + + "BB5BF427E8C4D89B79EB338375100C5F83E55DE9B4466DDFBEEE42539AEF33EF" + + "187B7760C3B1A1B2103C2D8144564A0C1039A09C85CF6B5974EB516FC8D6623C" + + "94AE3A5A0BB3B4C792957D432391566CF3E2A52AFB0C142B9E0681B8972671AF" + + "2B82DD390A39B939CF719568687E4990A63050CA7768DCD6B378842F18FDB1F6" + + "D9FF096BAF7BEB98DCF930D66FCFD503F58D41BFF46212E24E3AFC45EA42BD88" + + "47" + + // INTEGER (e) + "02050200000441"; + + WriteRSAPublicKey((AsnEncodingRules)ruleSet, ExpectedHex); + } + + [Fact] + public static void WriteRSAPublicKey_CER() + { + const string ExpectedHex = + // CONSTRUCTED SEQUENCE + "3080" + + // CONSTRUCTED SEQUENCE + "3080" + + // OBJECT IDENTIFIER (1.2.840.113549.1.1.1, rsaEncryption) + "06092A864886F70D010101" + + // NULL + "0500" + + // End-of-Contents + "0000" + + // BIT STRING + "03820111" + + // 0 unused bits + "00" + + // sneaky inspection of the payload bytes + // CONSTRUCTED SEQUENCE + "3080" + + // INTEGER (n) + "02820101" + + "00AF81C1CBD8203F624A539ED6608175372393A2837D4890E48A19DED3697311" + + "5620968D6BE0D3DAA38AA777BE02EE0B6B93B724E8DCC12B632B4FA80BBC925B" + + "CE624F4CA7CC606306B39403E28C932D24DD546FFE4EF6A37F10770B2215EA8C" + + "BB5BF427E8C4D89B79EB338375100C5F83E55DE9B4466DDFBEEE42539AEF33EF" + + "187B7760C3B1A1B2103C2D8144564A0C1039A09C85CF6B5974EB516FC8D6623C" + + "94AE3A5A0BB3B4C792957D432391566CF3E2A52AFB0C142B9E0681B8972671AF" + + "2B82DD390A39B939CF719568687E4990A63050CA7768DCD6B378842F18FDB1F6" + + "D9FF096BAF7BEB98DCF930D66FCFD503F58D41BFF46212E24E3AFC45EA42BD88" + + "47" + + // INTEGER (e) + "02050200000441" + + // End-of-Contents + "0000" + + // (no EoC for the BIT STRING) + // End-of-Contents + "0000"; + + WriteRSAPublicKey(AsnEncodingRules.CER, ExpectedHex); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, false)] + [InlineData(PublicEncodingRules.CER, false)] + [InlineData(PublicEncodingRules.DER, false)] + [InlineData(PublicEncodingRules.BER, true)] + [InlineData(PublicEncodingRules.CER, true)] + [InlineData(PublicEncodingRules.DER, true)] + public static void CannotEncodeWhileUnbalanced(PublicEncodingRules ruleSet, bool customTag) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + if (customTag) + { + writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); + } + else + { + writer.PushSequence(); + } + + int written = -5; + + Assert.Throws(() => writer.Encode()); + Assert.Throws(() => writer.TryEncode(Span.Empty, out written)); + Assert.Equal(-5, written); + + byte[] buf = new byte[10]; + Assert.Throws(() => writer.TryEncode(buf, out written)); + Assert.Equal(-5, written); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void PushSequence_EndOfContents(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + AssertExtensions.Throws( + "tag", + () => writer.PushSequence(Asn1Tag.EndOfContents)); + } + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSetOf.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSetOf.cs new file mode 100644 index 0000000000..bb6b179d51 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSetOf.cs @@ -0,0 +1,540 @@ +// 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.Numerics; +using System.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public class PushPopSetOf : Asn1WriterTests + { + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void PopNewWriter(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + // Maybe ArgumentException isn't right for this, since no argument was provided. + AssertExtensions.Throws( + "tag", + () => writer.PopSetOf()); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void PopNewWriter_CustomTag(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + AssertExtensions.Throws( + "tag", + () => writer.PopSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void PopBalancedWriter(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.PushSetOf(); + writer.PopSetOf(); + + // Maybe ArgumentException isn't right for this, since no argument was provided. + AssertExtensions.Throws( + "tag", + () => writer.PopSetOf()); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void PopBalancedWriter_CustomTag(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.PushSetOf(); + writer.PopSetOf(); + + AssertExtensions.Throws( + "tag", + () => writer.PopSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void PushCustom_PopStandard(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.PushSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); + + // Maybe ArgumentException isn't right for this, since no argument was provided. + AssertExtensions.Throws( + "tag", + () => writer.PopSetOf()); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void PushStandard_PopCustom(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.PushSetOf(); + + AssertExtensions.Throws( + "tag", + () => writer.PopSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void PushPrimitive_PopStandard(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.PushSetOf(new Asn1Tag(UniversalTagNumber.SetOf)); + writer.PopSetOf(); + + if (ruleSet == PublicEncodingRules.CER) + { + Verify(writer, "31800000"); + } + else + { + Verify(writer, "3100"); + } + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void PushCustomPrimitive_PopConstructed(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.PushSetOf(new Asn1Tag(TagClass.Private, 5)); + writer.PopSetOf(new Asn1Tag(TagClass.Private, 5, true)); + + if (ruleSet == PublicEncodingRules.CER) + { + Verify(writer, "E5800000"); + } + else + { + Verify(writer, "E500"); + } + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void PushStandard_PopPrimitive(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.PushSetOf(); + writer.PopSetOf(new Asn1Tag(UniversalTagNumber.SetOf)); + + if (ruleSet == PublicEncodingRules.CER) + { + Verify(writer, "31800000"); + } + else + { + Verify(writer, "3100"); + } + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void PushCustomConstructed_PopPrimitive(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.PushSetOf(new Asn1Tag(TagClass.Private, (int)ruleSet, true)); + writer.PopSetOf(new Asn1Tag(TagClass.Private, (int)ruleSet)); + + byte tag = (byte)((int)ruleSet | 0b1110_0000); + string tagHex = tag.ToString("X2"); + string rest = ruleSet == PublicEncodingRules.CER ? "800000" : "00"; + + Verify(writer, tagHex + rest); + } + } + + [Fact] + public static void BER_WritesDefinite_Empty() + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + writer.PushSetOf(); + writer.PopSetOf(); + + Verify(writer, "3100"); + } + } + + [Fact] + public static void CER_WritesIndefinite_Empty() + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + writer.PushSetOf(); + writer.PopSetOf(); + + Verify(writer, "31800000"); + } + } + + [Fact] + public static void DER_WritesDefinite_CustomTag_Empty() + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + { + writer.PushSetOf(); + writer.PopSetOf(); + + Verify(writer, "3100"); + } + } + + [Fact] + public static void BER_WritesDefinite_CustomTag__Empty() + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.Private, 15, true); + writer.PushSetOf(tag); + writer.PopSetOf(tag); + + Verify(writer, "EF00"); + } + } + + [Fact] + public static void CER_WritesIndefinite_CustomTag__Empty() + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.Application, 91, true); + writer.PushSetOf(tag); + writer.PopSetOf(tag); + + Verify(writer, "7F5B800000"); + } + } + + [Fact] + public static void DER_WritesDefinite_CustomTag__Empty() + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 30, true); + writer.PushSetOf(tag); + writer.PopSetOf(tag); + + Verify(writer, "BE00"); + } + } + + private static void TestNested(AsnWriter writer, Asn1Tag alt, string expectedHex) + { + // Written in pre-sorted order, since sorting is a different test. + writer.PushSetOf(); + { + writer.PushSetOf(); + { + writer.PushSetOf(alt); + { + writer.PushSetOf(); + writer.PopSetOf(); + } + + writer.PopSetOf(alt); + } + + writer.PopSetOf(); + + writer.PushSetOf(alt); + writer.PopSetOf(alt); + } + + writer.PopSetOf(); + + Verify(writer, expectedHex); + } + + [Fact] + public static void BER_Nested() + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + Asn1Tag alt = new Asn1Tag(TagClass.Private, 127, true); + + TestNested(writer, alt, "310A3105FF7F023100FF7F00"); + } + } + + [Fact] + public static void CER_Nested() + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + Asn1Tag alt = new Asn1Tag(TagClass.ContextSpecific, 12, true); + + TestNested(writer, alt, "31803180AC803180000000000000AC8000000000"); + } + } + + [Fact] + public static void DER_Nested() + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + { + Asn1Tag alt = new Asn1Tag(TagClass.Application, 5, true); + + TestNested(writer, alt, "31083104650231006500"); + } + } + + private static void SimpleContentShift(AsnWriter writer, string expectedHex) + { + writer.PushSetOf(); + + // F00DF00D...F00DF00D + byte[] contentBytes = new byte[126]; + + for (int i = 0; i < contentBytes.Length; i += 2) + { + contentBytes[i] = 0xF0; + contentBytes[i + 1] = 0x0D; + } + + writer.WriteOctetString(contentBytes); + writer.PopSetOf(); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.DER)] + public static void SimpleContentShift(PublicEncodingRules ruleSet) + { + const string ExpectedHex = + "318180" + + "047E" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D"; + + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + SimpleContentShift(writer, ExpectedHex); + } + } + + [Fact] + public static void SimpleContentShift_CER() + { + const string ExpectedHex = + "3180" + + "047E" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "0000"; + + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + SimpleContentShift(writer, ExpectedHex); + } + } + + private static void ValidateDataSorting(AsnEncodingRules ruleSet, string expectedHex) + { + using (AsnWriter writer = new AsnWriter(ruleSet)) + { + writer.PushSetOf(); + + // 02 01 FF + writer.WriteInteger(-1); + // 02 01 00 + writer.WriteInteger(0); + // 02 02 00 FF + writer.WriteInteger(255); + // 01 01 FF + writer.WriteBoolean(true); + // 45 01 00 + writer.WriteBoolean(new Asn1Tag(TagClass.Application, 5), false); + // 02 01 7F + writer.WriteInteger(127); + // 02 01 80 + writer.WriteInteger(sbyte.MinValue); + // 02 02 00 FE + writer.WriteInteger(254); + // 02 01 00 + writer.WriteInteger(0); + + writer.PopSetOf(); + + // The correct sort order (CER, DER) is + // Universal Boolean: true + // Universal Integer: 0 + // Universal Integer: 0 + // Universal Integer: 127 + // Universal Integer: -128 + // Universal Integer: -1 + // Universal Integer: 254 + // Universal Integer: 255 + // Application 5 (Boolean): false + + // This test would be + // + // GrabBag ::= SET OF GrabBagItem + // + // GrabBagItem ::= CHOICE ( + // value INTEGER + // bool BOOLEAN + // grr [APPLICATION 5] IMPLICIT BOOLEAN + // ) + + Verify(writer, expectedHex); + } + } + + [Fact] + public static void BER_DoesNotSort() + { + const string ExpectedHex = + "311D" + + "0201FF" + + "020100" + + "020200FF" + + "0101FF" + + "450100" + + "02017F" + + "020180" + + "020200FE" + + "020100"; + + ValidateDataSorting(AsnEncodingRules.BER, ExpectedHex); + } + + [Fact] + public static void CER_SortsData() + { + const string ExpectedHex = + "3180" + + "0101FF" + + "020100" + + "020100" + + "02017F" + + "020180" + + "0201FF" + + "020200FE" + + "020200FF" + + "450100" + + "0000"; + + ValidateDataSorting(AsnEncodingRules.CER, ExpectedHex); + } + + [Fact] + public static void DER_SortsData() + { + const string ExpectedHex = + "311D" + + "0101FF" + + "020100" + + "020100" + + "02017F" + + "020180" + + "0201FF" + + "020200FE" + + "020200FF" + + "450100"; + + ValidateDataSorting(AsnEncodingRules.DER, ExpectedHex); + } + + [Theory] + [InlineData(PublicEncodingRules.BER, false)] + [InlineData(PublicEncodingRules.CER, false)] + [InlineData(PublicEncodingRules.DER, false)] + [InlineData(PublicEncodingRules.BER, true)] + [InlineData(PublicEncodingRules.CER, true)] + [InlineData(PublicEncodingRules.DER, true)] + public static void CannotEncodeWhileUnbalanced(PublicEncodingRules ruleSet, bool customTag) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + if (customTag) + { + writer.PushSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); + } + else + { + writer.PushSetOf(); + } + + int written = -5; + + Assert.Throws(() => writer.Encode()); + Assert.Throws(() => writer.TryEncode(Span.Empty, out written)); + Assert.Equal(-5, written); + + byte[] buf = new byte[10]; + Assert.Throws(() => writer.TryEncode(buf, out written)); + Assert.Equal(-5, written); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void PushSetOf_EndOfContents(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + AssertExtensions.Throws( + "tag", + () => writer.PushSetOf(Asn1Tag.EndOfContents)); + } + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBMPString.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBMPString.cs new file mode 100644 index 0000000000..12c44833e2 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBMPString.cs @@ -0,0 +1,294 @@ +// 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.Collections.Generic; +using System.Security.Cryptography.Asn1; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public class WriteBMPString : WriteCharacterString + { + public static IEnumerable ShortValidCases { get; } = new object[][] + { + new object[] + { + string.Empty, + "00", + }, + new object[] + { + "hi", + "0400680069", + }, + new object[] + { + "Dr. & Mrs. Smith\u2010Jones \uFE60 children", + "42" + + "00440072002E002000260020004D00720073002E00200053006D0069" + + "007400682010004A006F006E006500730020FE600020006300680069" + + "006C006400720065006E", + }, + }; + + public static IEnumerable LongValidCases { get; } = new object[][] + { + new object[] + { + // 498 Han-fragrant, 497 Han-dark, 23 Han-rich + new string('\u9999', 498) + new string('\u6666', 497) + new string('\u4444', 23), + "8207F4" + new string('9', 498 * 4) + new string('6', 497 * 4) + new string('4', 23 * 4), + }, + }; + + public static IEnumerable CERSegmentedCases { get; } = new object[][] + { + new object[] + { + GettysburgAddress, + 1458 * 2, + }, + new object[] + { + // 498 Han-fragrant, 497 Han-dark, 5 Han-rich + new string('\u9999', 498) + new string('\u6666', 497) + new string('\u4444', 5), + 2000, + }, + }; + + public static IEnumerable InvalidInputs { get; } = new object[][] + { + // Surrogate pair for "Deseret Small Letter Yee" (U+10437) + new object[] { "\uD801\uDC37" }, + }; + + internal override void WriteString(AsnWriter writer, string s) => + writer.WriteCharacterString(UniversalTagNumber.BMPString, s); + + internal override void WriteString(AsnWriter writer, Asn1Tag tag, string s) => + writer.WriteCharacterString(tag, UniversalTagNumber.BMPString, s); + + internal override void WriteSpan(AsnWriter writer, ReadOnlySpan s) => + writer.WriteCharacterString(UniversalTagNumber.BMPString, s); + + internal override void WriteSpan(AsnWriter writer, Asn1Tag tag, ReadOnlySpan s) => + writer.WriteCharacterString(tag, UniversalTagNumber.BMPString, s); + + internal override Asn1Tag StandardTag => new Asn1Tag(UniversalTagNumber.BMPString); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_String(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_String(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_String_CustomTag(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_String_CustomTag(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_String(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_String(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_String_CustomTag(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_String_CustomTag(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_String(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_String(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_String_CustomTag(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_String_CustomTag(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_Span(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_Span(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_Span_CustomTag(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_Span_CustomTag(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_Span(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_Span(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_Span_CustomTag(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_Span_CustomTag(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_Span(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_Span(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_Span_CustomTag(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_Span_CustomTag(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_String_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_String_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_String_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_String_CustomTag_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_Span_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_Span_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_Span_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_Span_CustomTag_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_String_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_String_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_String_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_String_CustomTag_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_Span_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_Span_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_Span_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_Span_CustomTag_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_String_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_String_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_String_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_String_CustomTag_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_Span_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_Span_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_Span_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_Span_CustomTag_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public new void VerifyWrite_String_Null(PublicEncodingRules ruleSet) => + base.VerifyWrite_String_Null(ruleSet); + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public new void VerifyWrite_String_Null_CustomTag(PublicEncodingRules ruleSet) => + base.VerifyWrite_String_Null_CustomTag(ruleSet); + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public new void VerifyWrite_EndOfContents_String(PublicEncodingRules ruleSet) => + base.VerifyWrite_EndOfContents_String(ruleSet); + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public new void VerifyWrite_EndOfContents_Span(PublicEncodingRules ruleSet) => + base.VerifyWrite_EndOfContents_Span(ruleSet); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_String(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_String(input, contentByteCount); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_String_CustomTag(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_String_CustomTag(input, contentByteCount); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_String_ConstructedTag(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_String_ConstructedTag(input, contentByteCount); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_String_CustomPrimitiveTag(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_String_CustomPrimitiveTag(input, contentByteCount); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_Span(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_Span(input, contentByteCount); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_Span_CustomTag(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_Span_CustomTag(input, contentByteCount); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_Span_ConstructedTag(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_Span_ConstructedTag(input, contentByteCount); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_Span_CustomPrimitiveTag(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_Span_CustomPrimitiveTag(input, contentByteCount); + + [Theory] + [MemberData(nameof(InvalidInputs))] + public new void VerifyWrite_String_NonEncodable(string input) => + base.VerifyWrite_String_NonEncodable(input); + + [Theory] + [MemberData(nameof(InvalidInputs))] + public new void VerifyWrite_Span_NonEncodable(string input) => + base.VerifyWrite_Span_NonEncodable(input); + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBitString.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBitString.cs new file mode 100644 index 0000000000..4394e2a603 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBitString.cs @@ -0,0 +1,325 @@ +// 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.Diagnostics; +using System.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public class WriteBitString : Asn1WriterTests + { + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public void WriteEmpty(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteBitString(ReadOnlySpan.Empty); + + Verify(writer, "030100"); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, 1, 1, "030201")] + [InlineData(PublicEncodingRules.CER, 2, 1, "030301")] + [InlineData(PublicEncodingRules.DER, 3, 1, "030401")] + [InlineData(PublicEncodingRules.BER, 126, 0, "037F00")] + [InlineData(PublicEncodingRules.CER, 127, 3, "03818003")] + [InlineData(PublicEncodingRules.BER, 999, 0, "038203E800")] + [InlineData(PublicEncodingRules.CER, 999, 0, "038203E800")] + [InlineData(PublicEncodingRules.DER, 999, 0, "038203E800")] + [InlineData(PublicEncodingRules.BER, 1000, 0, "038203E900")] + [InlineData(PublicEncodingRules.DER, 1000, 0, "038203E900")] + [InlineData(PublicEncodingRules.BER, 2000, 0, "038207D100")] + [InlineData(PublicEncodingRules.DER, 2000, 0, "038207D100")] + public void WritePrimitive(PublicEncodingRules ruleSet, int length, int unusedBitCount, string hexStart) + { + string payloadHex = new string('0', 2 * length); + string expectedHex = hexStart + payloadHex; + byte[] data = new byte[length]; + + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteBitString(data, unusedBitCount); + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData(1000, 1, "2380038203E800", "030201")] + [InlineData(999*2, 3, "2380038203E800", "038203E803")] + public void WriteSegmentedCER(int length, int unusedBitCount, string hexStart, string hexStart2) + { + string payload1Hex = new string('8', 999 * 2); + string payload2Hex = new string('8', (length - 999) * 2); + string expectedHex = hexStart + payload1Hex + hexStart2 + payload2Hex + "0000"; + byte[] data = new byte[length]; + + for (int i = 0; i < data.Length; i++) + { + data[i] = 0x88; + } + + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + writer.WriteBitString(data, unusedBitCount); + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, 0, false)] + [InlineData(PublicEncodingRules.CER, 0, false)] + [InlineData(PublicEncodingRules.DER, 0, false)] + [InlineData(PublicEncodingRules.BER, 999, false)] + [InlineData(PublicEncodingRules.CER, 999, false)] + [InlineData(PublicEncodingRules.DER, 999, false)] + [InlineData(PublicEncodingRules.BER, 1000, false)] + [InlineData(PublicEncodingRules.CER, 1000, true)] + [InlineData(PublicEncodingRules.DER, 1000, false)] + [InlineData(PublicEncodingRules.BER, 1998, false)] + [InlineData(PublicEncodingRules.CER, 1998, true)] + [InlineData(PublicEncodingRules.BER, 4096, false)] + [InlineData(PublicEncodingRules.CER, 4096, true)] + [InlineData(PublicEncodingRules.DER, 4096, false)] + public void VerifyWriteBitString_PrimitiveOrConstructed( + PublicEncodingRules ruleSet, + int payloadLength, + bool expectConstructed) + { + byte[] data = new byte[payloadLength]; + + Asn1Tag[] tagsToTry = + { + new Asn1Tag(UniversalTagNumber.BitString), + new Asn1Tag(UniversalTagNumber.BitString, isConstructed: true), + new Asn1Tag(TagClass.Private, 87), + new Asn1Tag(TagClass.ContextSpecific, 13, isConstructed: true), + }; + + byte[] answerBuf = new byte[payloadLength + 100]; + + foreach (Asn1Tag toTry in tagsToTry) + { + Asn1Tag writtenTag; + + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteBitString(toTry, data); + + Assert.True(writer.TryEncode(answerBuf, out _)); + Assert.True(Asn1Tag.TryParse(answerBuf, out writtenTag, out _)); + } + + if (expectConstructed) + { + Assert.True(writtenTag.IsConstructed, $"writtenTag.IsConstructed ({toTry})"); + } + else + { + Assert.False(writtenTag.IsConstructed, $"writtenTag.IsConstructed ({toTry})"); + } + + Assert.Equal(toTry.TagClass, writtenTag.TagClass); + Assert.Equal(toTry.TagValue, writtenTag.TagValue); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, 0, "FF", false)] + [InlineData(PublicEncodingRules.BER, 1, "FE", false)] + [InlineData(PublicEncodingRules.CER, 1, "FE", false)] + [InlineData(PublicEncodingRules.DER, 1, "FE", false)] + [InlineData(PublicEncodingRules.BER, 1, "FF", true)] + [InlineData(PublicEncodingRules.CER, 1, "FF", true)] + [InlineData(PublicEncodingRules.DER, 1, "FF", true)] + [InlineData(PublicEncodingRules.BER, 7, "C0", true)] + [InlineData(PublicEncodingRules.CER, 7, "C0", true)] + [InlineData(PublicEncodingRules.DER, 7, "C0", true)] + [InlineData(PublicEncodingRules.BER, 7, "80", false)] + [InlineData(PublicEncodingRules.CER, 7, "80", false)] + [InlineData(PublicEncodingRules.DER, 7, "80", false)] + [InlineData(PublicEncodingRules.DER, 7, "40", true)] + [InlineData(PublicEncodingRules.DER, 6, "40", false)] + [InlineData(PublicEncodingRules.DER, 6, "C0", false)] + [InlineData(PublicEncodingRules.DER, 6, "20", true)] + [InlineData(PublicEncodingRules.DER, 5, "20", false)] + [InlineData(PublicEncodingRules.DER, 5, "A0", false)] + [InlineData(PublicEncodingRules.DER, 5, "10", true)] + [InlineData(PublicEncodingRules.DER, 4, "10", false)] + [InlineData(PublicEncodingRules.DER, 4, "90", false)] + [InlineData(PublicEncodingRules.DER, 4, "30", false)] + [InlineData(PublicEncodingRules.DER, 4, "08", true)] + [InlineData(PublicEncodingRules.DER, 4, "88", true)] + [InlineData(PublicEncodingRules.DER, 3, "08", false)] + [InlineData(PublicEncodingRules.DER, 3, "A8", false)] + [InlineData(PublicEncodingRules.DER, 3, "04", true)] + [InlineData(PublicEncodingRules.DER, 3, "14", true)] + [InlineData(PublicEncodingRules.DER, 2, "04", false)] + [InlineData(PublicEncodingRules.DER, 2, "0C", false)] + [InlineData(PublicEncodingRules.DER, 2, "FC", false)] + [InlineData(PublicEncodingRules.DER, 2, "02", true)] + [InlineData(PublicEncodingRules.DER, 2, "82", true)] + [InlineData(PublicEncodingRules.DER, 2, "FE", true)] + [InlineData(PublicEncodingRules.DER, 1, "02", false)] + [InlineData(PublicEncodingRules.DER, 1, "82", false)] + [InlineData(PublicEncodingRules.DER, 1, "FE", false)] + [InlineData(PublicEncodingRules.DER, 1, "80", false)] + public static void WriteBitString_UnusedBitCount_MustBeValid( + PublicEncodingRules ruleSet, + int unusedBitCount, + string inputHex, + bool expectThrow) + { + byte[] inputBytes = inputHex.HexToByteArray(); + + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + if (expectThrow) + { + Assert.Throws( + () => writer.WriteBitString(inputBytes, unusedBitCount)); + + Assert.Throws( + () => writer.WriteBitString( + new Asn1Tag(TagClass.ContextSpecific, 3), + inputBytes, + unusedBitCount)); + + return; + } + + byte[] output = new byte[512]; + writer.WriteBitString(inputBytes, unusedBitCount); + Assert.True(writer.TryEncode(output, out int bytesWritten)); + + // This assumes that inputBytes is never more than 999 (and avoids CER constructed forms) + Assert.Equal(unusedBitCount, output[bytesWritten - inputBytes.Length - 1]); + + writer.WriteBitString(new Asn1Tag(TagClass.ContextSpecific, 9), inputBytes, unusedBitCount); + Assert.True(writer.TryEncode(output, out bytesWritten)); + + Assert.Equal(unusedBitCount, output[bytesWritten - inputBytes.Length - 1]); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, -1)] + [InlineData(PublicEncodingRules.CER, -1)] + [InlineData(PublicEncodingRules.DER, -1)] + [InlineData(PublicEncodingRules.BER, -2)] + [InlineData(PublicEncodingRules.CER, -2)] + [InlineData(PublicEncodingRules.DER, -2)] + [InlineData(PublicEncodingRules.BER, 8)] + [InlineData(PublicEncodingRules.CER, 8)] + [InlineData(PublicEncodingRules.DER, 8)] + [InlineData(PublicEncodingRules.BER, 9)] + [InlineData(PublicEncodingRules.CER, 9)] + [InlineData(PublicEncodingRules.DER, 9)] + [InlineData(PublicEncodingRules.BER, 1048576)] + [InlineData(PublicEncodingRules.CER, 1048576)] + [InlineData(PublicEncodingRules.DER, 1048576)] + public static void UnusedBitCounts_Bounds(PublicEncodingRules ruleSet, int unusedBitCount) + { + byte[] data = new byte[5]; + + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + ArgumentOutOfRangeException exception = AssertExtensions.Throws( + nameof(unusedBitCount), + () => writer.WriteBitString(data, unusedBitCount)); + + Assert.Equal(unusedBitCount, exception.ActualValue); + + exception = AssertExtensions.Throws( + nameof(unusedBitCount), + () => writer.WriteBitString(new Asn1Tag(TagClass.ContextSpecific, 5), data, unusedBitCount)); + + Assert.Equal(unusedBitCount, exception.ActualValue); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void EmptyData_Requires0UnusedBits(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + Assert.Throws( + () => writer.WriteBitString(ReadOnlySpan.Empty, 1)); + + Assert.Throws( + () => writer.WriteBitString(ReadOnlySpan.Empty, 7)); + + Asn1Tag contextTag = new Asn1Tag(TagClass.ContextSpecific, 19); + + Assert.Throws( + () => writer.WriteBitString(contextTag, ReadOnlySpan.Empty, 1)); + + Assert.Throws( + () => writer.WriteBitString(contextTag, ReadOnlySpan.Empty, 7)); + + writer.WriteBitString(ReadOnlySpan.Empty, 0); + writer.WriteBitString(contextTag, ReadOnlySpan.Empty, 0); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, PublicTagClass.Universal, 3, "030100")] + [InlineData(PublicEncodingRules.CER, PublicTagClass.Universal, 3, "030100")] + [InlineData(PublicEncodingRules.DER, PublicTagClass.Universal, 3, "030100")] + [InlineData(PublicEncodingRules.BER, PublicTagClass.Private, 1, "C10100")] + [InlineData(PublicEncodingRules.CER, PublicTagClass.Application, 5, "450100")] + [InlineData(PublicEncodingRules.DER, PublicTagClass.ContextSpecific, 32, "9F200100")] + public static void EmptyData_Allows0UnusedBits( + PublicEncodingRules ruleSet, + PublicTagClass tagClass, + int tagValue, + string expectedHex) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + + if (tagClass == PublicTagClass.Universal) + { + Debug.Assert(tagValue == (int)UniversalTagNumber.BitString); + writer.WriteBitString(ReadOnlySpan.Empty, 0); + } + else + { + writer.WriteBitString(new Asn1Tag((TagClass)tagClass, tagValue), ReadOnlySpan.Empty, 0); + } + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public void VerifyWriteBitString_EndOfContents(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + AssertExtensions.Throws( + "tag", + () => writer.WriteBitString(Asn1Tag.EndOfContents, ReadOnlySpan.Empty)); + + AssertExtensions.Throws( + "tag", + () => writer.WriteBitString(Asn1Tag.EndOfContents, new byte[1])); + } + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBoolean.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBoolean.cs new file mode 100644 index 0000000000..6b86aa02aa --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBoolean.cs @@ -0,0 +1,88 @@ +// 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.Security.Cryptography.Asn1; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public class WriteBoolean : Asn1WriterTests + { + [Theory] + [InlineData(PublicEncodingRules.BER, false, "010100")] + [InlineData(PublicEncodingRules.BER, true, "0101FF")] + [InlineData(PublicEncodingRules.CER, false, "010100")] + [InlineData(PublicEncodingRules.CER, true, "0101FF")] + [InlineData(PublicEncodingRules.DER, false, "010100")] + [InlineData(PublicEncodingRules.DER, true, "0101FF")] + public void VerifyWriteBoolean(PublicEncodingRules ruleSet, bool value, string expectedHex) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteBoolean(value); + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, false, "830100")] + [InlineData(PublicEncodingRules.BER, true, "8301FF")] + [InlineData(PublicEncodingRules.CER, false, "830100")] + [InlineData(PublicEncodingRules.CER, true, "8301FF")] + [InlineData(PublicEncodingRules.DER, false, "830100")] + [InlineData(PublicEncodingRules.DER, true, "8301FF")] + public void VerifyWriteBoolean_Context3(PublicEncodingRules ruleSet, bool value, string expectedHex) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteBoolean(new Asn1Tag(TagClass.ContextSpecific, 3), value); + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, false)] + [InlineData(PublicEncodingRules.BER, true)] + [InlineData(PublicEncodingRules.CER, false)] + [InlineData(PublicEncodingRules.CER, true)] + [InlineData(PublicEncodingRules.DER, false)] + [InlineData(PublicEncodingRules.DER, true)] + public void VerifyWriteBoolean_EndOfContents(PublicEncodingRules ruleSet, bool value) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + AssertExtensions.Throws( + "tag", + () => writer.WriteBoolean(Asn1Tag.EndOfContents, value)); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, false)] + [InlineData(PublicEncodingRules.BER, true)] + [InlineData(PublicEncodingRules.CER, false)] + [InlineData(PublicEncodingRules.CER, true)] + [InlineData(PublicEncodingRules.DER, false)] + [InlineData(PublicEncodingRules.DER, true)] + public void VerifyWriteBoolean_ConstructedIgnored(PublicEncodingRules ruleSet, bool value) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteBoolean(new Asn1Tag(TagClass.ContextSpecific, 7, true), value); + writer.WriteBoolean(new Asn1Tag(UniversalTagNumber.Boolean, true), value); + + if (value) + { + Verify(writer, "8701FF0101FF"); + } + else + { + Verify(writer, "870100010100"); + } + } + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteCharacterString.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteCharacterString.cs new file mode 100644 index 0000000000..b158615e23 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteCharacterString.cs @@ -0,0 +1,498 @@ +// 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.Security.Cryptography.Asn1; +using System.Text; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public abstract class WriteCharacterString : Asn1WriterTests + { + internal abstract void WriteString(AsnWriter writer, string s); + internal abstract void WriteString(AsnWriter writer, Asn1Tag tag, string s); + + internal abstract void WriteSpan(AsnWriter writer, ReadOnlySpan s); + internal abstract void WriteSpan(AsnWriter writer, Asn1Tag tag, ReadOnlySpan s); + + internal abstract Asn1Tag StandardTag { get; } + + protected const string GettysburgAddress = + "Four score and seven years ago our fathers brought forth on this continent, a new nation, " + + "conceived in Liberty, and dedicated to the proposition that all men are created equal.\r\n" + + "\r\n" + + "Now we are engaged in a great civil war, testing whether that nation, or any nation so " + + "conceived and so dedicated, can long endure. We are met on a great battle-field of that " + + "war. We have come to dedicate a portion of that field, as a final resting place for those " + + "who here gave their lives that that nation might live. It is altogether fitting and proper " + + "that we should do this.\r\n" + + "\r\n" + + "But, in a larger sense, we can not dedicate-we can not consecrate-we can not hallow-this " + + "ground. The brave men, living and dead, who struggled here, have consecrated it, far above " + + "our poor power to add or detract. The world will little note, nor long remember what we say " + + "here, but it can never forget what they did here. It is for us the living, rather, to be " + + "dedicated here to the unfinished work which they who fought here have thus far so nobly " + + "advanced. It is rather for us to be here dedicated to the great task remaining before " + + "us-that from these honored dead we take increased devotion to that cause for which they " + + "gave the last full measure of devotion-that we here highly resolve that these dead shall " + + "not have died in vain-that this nation, under God, shall have a new birth of freedom-and " + + "that government of the people, by the people, for the people, shall not perish from the earth."; + + protected void VerifyWrite_BER_String(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + WriteString(writer, input); + + Verify(writer, Stringify(StandardTag) + expectedPayloadHex); + } + } + + protected void VerifyWrite_BER_String_CustomTag(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 14); + WriteString(writer, tag, input); + + Verify(writer, Stringify(tag) + expectedPayloadHex); + } + } + + protected void VerifyWrite_CER_String(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + WriteString(writer, input); + + Verify(writer, Stringify(StandardTag) + expectedPayloadHex); + } + } + + protected void VerifyWrite_CER_String_CustomTag(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.Private, 19); + WriteString(writer, tag, input); + + Verify(writer, Stringify(tag) + expectedPayloadHex); + } + } + + protected void VerifyWrite_DER_String(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + WriteString(writer, input); + + Verify(writer, Stringify(StandardTag) + expectedPayloadHex); + } + } + + protected void VerifyWrite_DER_String_CustomTag(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.Application, 2); + WriteString(writer, tag, input); + + Verify(writer, Stringify(tag) + expectedPayloadHex); + } + } + + protected void VerifyWrite_BER_Span(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + WriteSpan(writer, input.AsReadOnlySpan()); + + Verify(writer, Stringify(StandardTag) + expectedPayloadHex); + } + } + + protected void VerifyWrite_BER_Span_CustomTag(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.Private, int.MaxValue >> 1); + WriteSpan(writer, tag, input.AsReadOnlySpan()); + + Verify(writer, Stringify(tag) + expectedPayloadHex); + } + } + + protected void VerifyWrite_CER_Span(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + WriteSpan(writer, input.AsReadOnlySpan()); + + Verify(writer, Stringify(StandardTag) + expectedPayloadHex); + } + } + + protected void VerifyWrite_CER_Span_CustomTag(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.Application, 30); + WriteSpan(writer, tag, input.AsReadOnlySpan()); + + Verify(writer, Stringify(tag) + expectedPayloadHex); + } + } + + protected void VerifyWrite_DER_Span(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + WriteSpan(writer, input.AsReadOnlySpan()); + + Verify(writer, Stringify(StandardTag) + expectedPayloadHex); + } + } + + protected void VerifyWrite_DER_Span_CustomTag(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 31); + WriteSpan(writer, tag, input.AsReadOnlySpan()); + + Verify(writer, Stringify(tag) + expectedPayloadHex); + } + } + + protected void VerifyWrite_BER_String_ClearsConstructed(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); + WriteString(writer, tag, input); + + Verify(writer, Stringify(standard) + expectedPayloadHex); + } + } + + protected void VerifyWrite_BER_String_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.Application, 19, isConstructed: true); + Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); + WriteString(writer, tag, input); + + Verify(writer, Stringify(expected) + expectedPayloadHex); + } + } + + protected void VerifyWrite_BER_Span_ClearsConstructed(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); + WriteSpan(writer, tag, input.AsReadOnlySpan()); + + Verify(writer, Stringify(standard) + expectedPayloadHex); + } + } + + protected void VerifyWrite_BER_Span_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.Private, 24601, isConstructed: true); + Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); + WriteSpan(writer, tag, input.AsReadOnlySpan()); + + Verify(writer, Stringify(expected) + expectedPayloadHex); + } + } + + protected void VerifyWrite_CER_String_ClearsConstructed(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); + WriteString(writer, tag, input); + + Verify(writer, Stringify(standard) + expectedPayloadHex); + } + } + + protected void VerifyWrite_CER_String_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 1701, isConstructed: true); + Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); + WriteString(writer, tag, input); + + Verify(writer, Stringify(expected) + expectedPayloadHex); + } + } + + protected void VerifyWrite_CER_Span_ClearsConstructed(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); + WriteSpan(writer, tag, input.AsReadOnlySpan()); + + Verify(writer, Stringify(standard) + expectedPayloadHex); + } + } + + protected void VerifyWrite_CER_Span_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.Application, 11, isConstructed: true); + Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); + WriteSpan(writer, tag, input.AsReadOnlySpan()); + + Verify(writer, Stringify(expected) + expectedPayloadHex); + } + } + + protected void VerifyWrite_DER_String_ClearsConstructed(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + { + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); + WriteString(writer, tag, input); + + Verify(writer, Stringify(standard) + expectedPayloadHex); + } + } + + protected void VerifyWrite_DER_String_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.Application, 19, isConstructed: true); + Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); + WriteString(writer, tag, input); + + Verify(writer, Stringify(expected) + expectedPayloadHex); + } + } + + protected void VerifyWrite_DER_Span_ClearsConstructed(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + { + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); + WriteSpan(writer, tag, input.AsReadOnlySpan()); + + Verify(writer, Stringify(standard) + expectedPayloadHex); + } + } + + protected void VerifyWrite_DER_Span_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.Private, 24601, isConstructed: true); + Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); + WriteSpan(writer, tag, input.AsReadOnlySpan()); + + Verify(writer, Stringify(expected) + expectedPayloadHex); + } + } + + protected void VerifyWrite_String_Null(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + AssertExtensions.Throws( + "str", + () => WriteString(writer, null)); + } + } + + protected void VerifyWrite_String_Null_CustomTag(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + AssertExtensions.Throws( + "str", + () => WriteString(writer, new Asn1Tag(TagClass.ContextSpecific, 3), null)); + } + } + + protected void VerifyWrite_EndOfContents_String(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + AssertExtensions.Throws( + "tag", + () => WriteString(writer, Asn1Tag.EndOfContents, "hi")); + } + } + + protected void VerifyWrite_EndOfContents_Span(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + AssertExtensions.Throws( + "tag", + () => WriteSpan(writer, Asn1Tag.EndOfContents, "hi".AsReadOnlySpan())); + } + } + + private void VerifyWrite_CERSegmented(AsnWriter writer, string tagHex, int contentByteCount) + { + int div = Math.DivRem(contentByteCount, 1000, out int rem); + + // tag, length(80), div full segments at 1004 bytes each, and the end of contents. + int encodedSize = (tagHex.Length / 2) + 1 + 1004 * div + 2; + + if (rem != 0) + { + // tag, contents (length TBD) + encodedSize += 1 + rem; + + if (encodedSize < 0x80) + encodedSize++; + else if (encodedSize <= 0xFF) + encodedSize += 2; + else + encodedSize += 3; + } + + byte[] encoded = writer.Encode(); + + Assert.Equal(tagHex, encoded.AsReadOnlySpan().Slice(0, tagHex.Length / 2).ByteArrayToHex()); + Assert.Equal("0000", encoded.AsReadOnlySpan().Slice(encoded.Length - 2).ByteArrayToHex()); + Assert.Equal(encodedSize, encoded.Length); + } + + protected void VerifyWrite_CERSegmented_String(string input, int contentByteCount) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, true); + string tagHex = Stringify(tag); + + WriteString(writer, input); + VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); + } + } + + protected void VerifyWrite_CERSegmented_String_CustomTag(string input, int contentByteCount) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.Private, 7, true); + string tagHex = Stringify(tag); + + WriteString(writer, tag, input); + VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); + } + } + + protected void VerifyWrite_CERSegmented_String_ConstructedTag(string input, int contentByteCount) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, true); + string tagHex = Stringify(tag); + + WriteString(writer, tag, input); + VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); + } + } + + protected void VerifyWrite_CERSegmented_String_CustomPrimitiveTag(string input, int contentByteCount) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + Asn1Tag prim = new Asn1Tag(TagClass.Application, 42); + Asn1Tag constr = new Asn1Tag(prim.TagClass, prim.TagValue, true); + string tagHex = Stringify(constr); + + WriteString(writer, prim, input); + VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); + } + } + + protected void VerifyWrite_CERSegmented_Span(string input, int contentByteCount) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, true); + string tagHex = Stringify(tag); + + WriteSpan(writer, input.AsReadOnlySpan()); + VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); + } + } + + protected void VerifyWrite_CERSegmented_Span_CustomTag(string input, int contentByteCount) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.Private, 7, true); + string tagHex = Stringify(tag); + + WriteSpan(writer, tag, input.AsReadOnlySpan()); + VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); + } + } + + protected void VerifyWrite_CERSegmented_Span_ConstructedTag(string input, int contentByteCount) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, true); + string tagHex = Stringify(tag); + + WriteSpan(writer, tag, input.AsReadOnlySpan()); + VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); + } + } + + protected void VerifyWrite_CERSegmented_Span_CustomPrimitiveTag(string input, int contentByteCount) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + Asn1Tag prim = new Asn1Tag(TagClass.Application, 42); + Asn1Tag constr = new Asn1Tag(prim.TagClass, prim.TagValue, true); + string tagHex = Stringify(constr); + + WriteSpan(writer, prim, input.AsReadOnlySpan()); + VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); + } + } + + protected void VerifyWrite_String_NonEncodable(string input) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + Assert.Throws(() => WriteString(writer, input)); + } + } + + protected void VerifyWrite_Span_NonEncodable(string input) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + Assert.Throws(() => WriteSpan(writer, input.AsReadOnlySpan())); + } + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteEnumerated.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteEnumerated.cs new file mode 100644 index 0000000000..1fa2bfc0f1 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteEnumerated.cs @@ -0,0 +1,417 @@ +// 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.Collections.Generic; +using System.Security.Cryptography.Asn1; +using System.Security.Cryptography.X509Certificates; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public class WriteEnumerated : Asn1WriterTests + { + [Theory] + [InlineData(PublicEncodingRules.BER, ReadEnumerated.SByteBacked.Zero, false, "0A0100")] + [InlineData(PublicEncodingRules.CER, ReadEnumerated.SByteBacked.Pillow, true, "9E01EF")] + [InlineData(PublicEncodingRules.DER, ReadEnumerated.SByteBacked.Fluff, false, "0A0153")] + [InlineData(PublicEncodingRules.BER, ReadEnumerated.SByteBacked.Fluff, true, "9E0153")] + [InlineData(PublicEncodingRules.CER, (ReadEnumerated.SByteBacked)(-127), true, "9E0181")] + public void VerifyWriteEnumerated_SByte( + PublicEncodingRules ruleSet, + ReadEnumerated.SByteBacked value, + bool customTag, + string expectedHex) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + if (customTag) + { + writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 30), value); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, ReadEnumerated.ByteBacked.Zero, false, "0A0100")] + [InlineData(PublicEncodingRules.CER, ReadEnumerated.ByteBacked.NotFluffy, true, "9A010B")] + [InlineData(PublicEncodingRules.DER, ReadEnumerated.ByteBacked.Fluff, false, "0A010C")] + [InlineData(PublicEncodingRules.BER, ReadEnumerated.ByteBacked.Fluff, true, "9A010C")] + [InlineData(PublicEncodingRules.CER, (ReadEnumerated.ByteBacked)253, false, "0A0200FD")] + public void VerifyWriteEnumerated_Byte( + PublicEncodingRules ruleSet, + ReadEnumerated.ByteBacked value, + bool customTag, + string expectedHex) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + if (customTag) + { + writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 26), value); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, ReadEnumerated.ShortBacked.Zero, true, "DF81540100")] + [InlineData(PublicEncodingRules.CER, ReadEnumerated.ShortBacked.Pillow, true, "DF815402FC00")] + [InlineData(PublicEncodingRules.DER, ReadEnumerated.ShortBacked.Fluff, false, "0A020209")] + [InlineData(PublicEncodingRules.BER, ReadEnumerated.ShortBacked.Fluff, true, "DF8154020209")] + [InlineData(PublicEncodingRules.CER, (ReadEnumerated.ShortBacked)25321, false, "0A0262E9")] + [InlineData(PublicEncodingRules.CER, (ReadEnumerated.ShortBacked)(-12345), false, "0A02CFC7")] + [InlineData(PublicEncodingRules.CER, (ReadEnumerated.ShortBacked)(-1), true, "DF815401FF")] + public void VerifyWriteEnumerated_Short( + PublicEncodingRules ruleSet, + ReadEnumerated.ShortBacked value, + bool customTag, + string expectedHex) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + if (customTag) + { + writer.WriteEnumeratedValue(new Asn1Tag(TagClass.Private, 212), value); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, ReadEnumerated.UShortBacked.Zero, false, "0A0100")] + [InlineData(PublicEncodingRules.BER, ReadEnumerated.UShortBacked.Zero, true, "4D0100")] + [InlineData(PublicEncodingRules.DER, ReadEnumerated.UShortBacked.Fluff, false, "0A03008000")] + [InlineData(PublicEncodingRules.BER, ReadEnumerated.UShortBacked.Fluff, true, "4D03008000")] + [InlineData(PublicEncodingRules.CER, (ReadEnumerated.UShortBacked)11, false, "0A010B")] + [InlineData(PublicEncodingRules.DER, (ReadEnumerated.UShortBacked)short.MaxValue, false, "0A027FFF")] + [InlineData(PublicEncodingRules.BER, (ReadEnumerated.UShortBacked)ushort.MaxValue, true, "4D0300FFFF")] + public void VerifyWriteEnumerated_UShort( + PublicEncodingRules ruleSet, + ReadEnumerated.UShortBacked value, + bool customTag, + string expectedHex) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + if (customTag) + { + writer.WriteEnumeratedValue(new Asn1Tag(TagClass.Application, 13), value); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, ReadEnumerated.IntBacked.Zero, true, "5F81FF7F0100")] + [InlineData(PublicEncodingRules.CER, ReadEnumerated.IntBacked.Pillow, true, "5F81FF7F03FEFFFF")] + [InlineData(PublicEncodingRules.DER, ReadEnumerated.IntBacked.Fluff, false, "0A03010001")] + [InlineData(PublicEncodingRules.BER, ReadEnumerated.IntBacked.Fluff, true, "5F81FF7F03010001")] + [InlineData(PublicEncodingRules.CER, (ReadEnumerated.IntBacked)25321, false, "0A0262E9")] + [InlineData(PublicEncodingRules.DER, (ReadEnumerated.IntBacked)(-12345), false, "0A02CFC7")] + [InlineData(PublicEncodingRules.BER, (ReadEnumerated.IntBacked)(-1), true, "5F81FF7F01FF")] + [InlineData(PublicEncodingRules.CER, (ReadEnumerated.IntBacked)int.MinValue, true, "5F81FF7F0480000000")] + [InlineData(PublicEncodingRules.CER, (ReadEnumerated.IntBacked)int.MaxValue, false, "0A047FFFFFFF")] + public void VerifyWriteEnumerated_Int( + PublicEncodingRules ruleSet, + ReadEnumerated.IntBacked value, + bool customTag, + string expectedHex) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + if (customTag) + { + writer.WriteEnumeratedValue(new Asn1Tag(TagClass.Application, short.MaxValue), value); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, ReadEnumerated.UIntBacked.Zero, false, "0A0100")] + [InlineData(PublicEncodingRules.BER, ReadEnumerated.UIntBacked.Zero, true, "9F610100")] + [InlineData(PublicEncodingRules.DER, ReadEnumerated.UIntBacked.Fluff, false, "0A050080000005")] + [InlineData(PublicEncodingRules.BER, ReadEnumerated.UIntBacked.Fluff, true, "9F61050080000005")] + [InlineData(PublicEncodingRules.CER, (ReadEnumerated.UIntBacked)11, false, "0A010B")] + [InlineData(PublicEncodingRules.DER, (ReadEnumerated.UIntBacked)short.MaxValue, false, "0A027FFF")] + [InlineData(PublicEncodingRules.BER, (ReadEnumerated.UIntBacked)ushort.MaxValue, true, "9F610300FFFF")] + public void VerifyWriteEnumerated_UInt( + PublicEncodingRules ruleSet, + ReadEnumerated.UIntBacked value, + bool customTag, + string expectedHex) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + if (customTag) + { + writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 97), value); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, ReadEnumerated.LongBacked.Zero, true, "800100")] + [InlineData(PublicEncodingRules.CER, ReadEnumerated.LongBacked.Pillow, true, "8005FF00000000")] + [InlineData(PublicEncodingRules.DER, ReadEnumerated.LongBacked.Fluff, false, "0A050200000441")] + [InlineData(PublicEncodingRules.BER, ReadEnumerated.LongBacked.Fluff, true, "80050200000441")] + [InlineData(PublicEncodingRules.CER, (ReadEnumerated.LongBacked)25321, false, "0A0262E9")] + [InlineData(PublicEncodingRules.DER, (ReadEnumerated.LongBacked)(-12345), false, "0A02CFC7")] + [InlineData(PublicEncodingRules.BER, (ReadEnumerated.LongBacked)(-1), true, "8001FF")] + [InlineData(PublicEncodingRules.CER, (ReadEnumerated.LongBacked)int.MinValue, true, "800480000000")] + [InlineData(PublicEncodingRules.DER, (ReadEnumerated.LongBacked)int.MaxValue, false, "0A047FFFFFFF")] + [InlineData(PublicEncodingRules.BER, (ReadEnumerated.LongBacked)long.MinValue, false, "0A088000000000000000")] + [InlineData(PublicEncodingRules.CER, (ReadEnumerated.LongBacked)long.MaxValue, true, "80087FFFFFFFFFFFFFFF")] + public void VerifyWriteEnumerated_Long( + PublicEncodingRules ruleSet, + ReadEnumerated.LongBacked value, + bool customTag, + string expectedHex) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + if (customTag) + { + writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 0), value); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, ReadEnumerated.ULongBacked.Zero, false, "0A0100")] + [InlineData(PublicEncodingRules.BER, ReadEnumerated.ULongBacked.Zero, true, "C10100")] + [InlineData(PublicEncodingRules.DER, ReadEnumerated.ULongBacked.Fluff, false, "0A0900FACEF00DCAFEBEEF")] + [InlineData(PublicEncodingRules.BER, ReadEnumerated.ULongBacked.Fluff, true, "C10900FACEF00DCAFEBEEF")] + [InlineData(PublicEncodingRules.CER, (ReadEnumerated.ULongBacked)11, false, "0A010B")] + [InlineData(PublicEncodingRules.DER, (ReadEnumerated.ULongBacked)short.MaxValue, false, "0A027FFF")] + [InlineData(PublicEncodingRules.BER, (ReadEnumerated.ULongBacked)ushort.MaxValue, true, "C10300FFFF")] + [InlineData(PublicEncodingRules.CER, (ReadEnumerated.ULongBacked)long.MaxValue, true, "C1087FFFFFFFFFFFFFFF")] + [InlineData(PublicEncodingRules.DER, (ReadEnumerated.ULongBacked)ulong.MaxValue, false, "0A0900FFFFFFFFFFFFFFFF")] + public void VerifyWriteEnumerated_ULong( + PublicEncodingRules ruleSet, + ReadEnumerated.ULongBacked value, + bool customTag, + string expectedHex) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + if (customTag) + { + writer.WriteEnumeratedValue(new Asn1Tag(TagClass.Private, 1), value); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public void VerifyFlagsBased(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + AssertExtensions.Throws( + "tEnum", + () => writer.WriteEnumeratedValue(OpenFlags.IncludeArchived)); + + AssertExtensions.Throws( + "tEnum", + () => writer.WriteEnumeratedValue( + new Asn1Tag(TagClass.ContextSpecific, 13), + OpenFlags.IncludeArchived)); + + AssertExtensions.Throws( + "tEnum", + () => writer.WriteEnumeratedValue((object)OpenFlags.IncludeArchived)); + + AssertExtensions.Throws( + "tEnum", + () => writer.WriteEnumeratedValue( + new Asn1Tag(TagClass.ContextSpecific, 13), + (object)OpenFlags.IncludeArchived)); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public void VerifyNonEnum(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + Assert.Throws( + () => writer.WriteEnumeratedValue(5)); + + Assert.Throws( + () => writer.WriteEnumeratedValue((object)"hi")); + + Assert.Throws( + () => writer.WriteEnumeratedValue((object)5)); + + Assert.Throws( + () => writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 3), 5)); + + Assert.Throws( + () => writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 3), (object)"hi")); + + Assert.Throws( + () => writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 3), (object)5)); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public void VerifyEndOfContents(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + AssertExtensions.Throws( + "tag", + () => writer.WriteEnumeratedValue(Asn1Tag.EndOfContents, ReadEnumerated.IntBacked.Pillow)); + + AssertExtensions.Throws( + "tag", + () => writer.WriteEnumeratedValue(Asn1Tag.EndOfContents, (object)ReadEnumerated.IntBacked.Pillow)); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void VerifyWriteEnumeratedValue_NonNull(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + AssertExtensions.Throws( + "enumValue", + () => writer.WriteEnumeratedValue(null)); + + AssertExtensions.Throws( + "enumValue", + () => writer.WriteEnumeratedValue( + new Asn1Tag(TagClass.ContextSpecific, 1), + null)); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void VerifyWriteEnumeratedValue_Object(PublicEncodingRules ruleSet) + { + using (AsnWriter objWriter = new AsnWriter((AsnEncodingRules)ruleSet)) + using (AsnWriter genWriter = new AsnWriter((AsnEncodingRules)ruleSet)) + { + genWriter.WriteEnumeratedValue(ReadEnumerated.UIntBacked.Fluff); + objWriter.WriteEnumeratedValue((object)ReadEnumerated.UIntBacked.Fluff); + + genWriter.WriteEnumeratedValue(ReadEnumerated.SByteBacked.Fluff); + objWriter.WriteEnumeratedValue((object)ReadEnumerated.SByteBacked.Fluff); + + genWriter.WriteEnumeratedValue(ReadEnumerated.ULongBacked.Fluff); + objWriter.WriteEnumeratedValue((object)ReadEnumerated.ULongBacked.Fluff); + + Verify(objWriter, genWriter.Encode().ByteArrayToHex()); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void VerifyWriteEnumeratedValue_Object_WithTag(PublicEncodingRules ruleSet) + { + using (AsnWriter objWriter = new AsnWriter((AsnEncodingRules)ruleSet)) + using (AsnWriter genWriter = new AsnWriter((AsnEncodingRules)ruleSet)) + { + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 52); + + genWriter.WriteEnumeratedValue(tag, ReadEnumerated.UIntBacked.Fluff); + objWriter.WriteEnumeratedValue(tag, (object)ReadEnumerated.UIntBacked.Fluff); + + tag = new Asn1Tag(TagClass.Private, 4); + + genWriter.WriteEnumeratedValue(tag, ReadEnumerated.SByteBacked.Fluff); + objWriter.WriteEnumeratedValue(tag, (object)ReadEnumerated.SByteBacked.Fluff); + + tag = new Asn1Tag(TagClass.Application, 75); + + genWriter.WriteEnumeratedValue(tag, ReadEnumerated.ULongBacked.Fluff); + objWriter.WriteEnumeratedValue(tag, (object)ReadEnumerated.ULongBacked.Fluff); + + Verify(objWriter, genWriter.Encode().ByteArrayToHex()); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public void VerifyWriteEnumeratedValue_ConstructedIgnored(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteEnumeratedValue( + new Asn1Tag(UniversalTagNumber.Enumerated, isConstructed: true), + ReadEnumerated.ULongBacked.Fluff); + + writer.WriteEnumeratedValue( + new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true), + (object)ReadEnumerated.SByteBacked.Fluff); + + Verify(writer, "0A0900FACEF00DCAFEBEEF" + "800153"); + } + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteGeneralizedTime.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteGeneralizedTime.cs new file mode 100644 index 0000000000..1f7f2043a4 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteGeneralizedTime.cs @@ -0,0 +1,259 @@ +// 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.Collections.Generic; +using System.Security.Cryptography.Asn1; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public class WriteGeneralizedTime : Asn1WriterTests + { + public static IEnumerable TestCases { get; } = new object[][] + { + new object[] + { + new DateTimeOffset(2017, 10, 16, 8, 24, 3, TimeSpan.FromHours(-7)), + false, + "0F32303137313031363135323430335A", + }, + new object[] + { + new DateTimeOffset(1817, 10, 16, 21, 24, 3, TimeSpan.FromHours(6)), + false, + "0F31383137313031363135323430335A", + }, + new object[] + { + new DateTimeOffset(3000, 1, 1, 0, 0, 0, TimeSpan.Zero), + false, + "0F33303030303130313030303030305A", + }, + new object[] + { + new DateTimeOffset(1999, 12, 31, 23, 59, 59, 999, TimeSpan.Zero), + false, + "1331393939313233313233353935392E3939395A" + }, + new object[] + { + new DateTimeOffset(1999, 12, 31, 23, 59, 59, 999, TimeSpan.Zero), + true, + "0F31393939313233313233353935395A" + }, + new object[] + { + new DateTimeOffset(1999, 12, 31, 23, 59, 59, 880, TimeSpan.Zero), + false, + "1231393939313233313233353935392E38385A" + }, + new object[] + { + new DateTimeOffset(1999, 12, 31, 23, 59, 59, 880, TimeSpan.Zero), + true, + "0F31393939313233313233353935395A" + }, + new object[] + { + new DateTimeOffset(1999, 12, 31, 23, 59, 59, 700, TimeSpan.Zero), + false, + "1131393939313233313233353935392E375A" + }, + new object[] + { + new DateTimeOffset(1999, 12, 31, 23, 59, 59, 700, TimeSpan.Zero), + true, + "0F31393939313233313233353935395A" + }, + new object[] + { + new DateTimeOffset(1999, 12, 31, 23, 59, 59, 123, TimeSpan.Zero) + TimeSpan.FromTicks(4567), + false, + "1731393939313233313233353935392E313233343536375A" + }, + new object[] + { + new DateTimeOffset(1999, 12, 31, 23, 59, 59, 123, TimeSpan.Zero) + TimeSpan.FromTicks(4567), + true, + "0F31393939313233313233353935395A" + }, + new object[] + { + new DateTimeOffset(1999, 12, 31, 23, 59, 59, 12, TimeSpan.Zero) + TimeSpan.FromTicks(3450), + false, + "1631393939313233313233353935392E3031323334355A" + }, + new object[] + { + new DateTimeOffset(1999, 12, 31, 23, 59, 59, 12, TimeSpan.Zero) + TimeSpan.FromTicks(3450), + true, + "0F31393939313233313233353935395A" + }, + new object[] + { + new DateTimeOffset(1999, 12, 31, 23, 59, 59, 1, TimeSpan.Zero) + TimeSpan.FromTicks(2300), + false, + "1531393939313233313233353935392E30303132335A" + }, + new object[] + { + new DateTimeOffset(1999, 12, 31, 23, 59, 59, 1, TimeSpan.Zero) + TimeSpan.FromTicks(2300), + true, + "0F31393939313233313233353935395A" + }, + new object[] + { + new DateTimeOffset(1999, 12, 31, 23, 59, 59, 0, TimeSpan.Zero) + TimeSpan.FromTicks(1000), + false, + "1431393939313233313233353935392E303030315A" + }, + new object[] + { + new DateTimeOffset(1999, 12, 31, 23, 59, 59, 0, TimeSpan.Zero) + TimeSpan.FromTicks(1000), + true, + "0F31393939313233313233353935395A" + }, + new object[] + { + new DateTimeOffset(1999, 12, 31, 23, 59, 59, TimeSpan.Zero) + TimeSpan.FromTicks(1), + false, + "1731393939313233313233353935392E303030303030315A" + }, + new object[] + { + new DateTimeOffset(1999, 12, 31, 23, 59, 59, TimeSpan.Zero) + TimeSpan.FromTicks(1), + true, + "0F31393939313233313233353935395A" + }, + }; + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteGeneralizedTime_BER( + DateTimeOffset input, + bool omitFractionalSeconds, + string expectedHexPayload) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + writer.WriteGeneralizedTime(input, omitFractionalSeconds); + + Verify(writer, "18" + expectedHexPayload); + } + } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteGeneralizedTime_BER_CustomTag( + DateTimeOffset input, + bool omitFractionalSeconds, + string expectedHexPayload) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.Application, 11); + writer.WriteGeneralizedTime(tag, input, omitFractionalSeconds); + + Verify(writer, Stringify(tag) + expectedHexPayload); + } + } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteGeneralizedTime_CER( + DateTimeOffset input, + bool omitFractionalSeconds, + string expectedHexPayload) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + writer.WriteGeneralizedTime(input, omitFractionalSeconds); + + Verify(writer, "18" + expectedHexPayload); + } + } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteGeneralizedTime_CER_CustomTag( + DateTimeOffset input, + bool omitFractionalSeconds, + string expectedHexPayload) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.Private, 95); + writer.WriteGeneralizedTime(tag, input, omitFractionalSeconds); + + Verify(writer, Stringify(tag) + expectedHexPayload); + } + } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteGeneralizedTime_DER( + DateTimeOffset input, + bool omitFractionalSeconds, + string expectedHexPayload) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + { + writer.WriteGeneralizedTime(input, omitFractionalSeconds); + + Verify(writer, "18" + expectedHexPayload); + } + } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteGeneralizedTime_DER_CustomTag( + DateTimeOffset input, + bool omitFractionalSeconds, + string expectedHexPayload) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); + writer.WriteGeneralizedTime(tag, input, omitFractionalSeconds); + + Verify(writer, Stringify(tag) + expectedHexPayload); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, false)] + [InlineData(PublicEncodingRules.CER, false)] + [InlineData(PublicEncodingRules.DER, false)] + [InlineData(PublicEncodingRules.BER, true)] + [InlineData(PublicEncodingRules.CER, true)] + [InlineData(PublicEncodingRules.DER, true)] + public void VerifyWriteGeneralizedTime_EndOfContents( + PublicEncodingRules ruleSet, + bool omitFractionalSeconds) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + AssertExtensions.Throws( + "tag", + () => writer.WriteGeneralizedTime(Asn1Tag.EndOfContents, DateTimeOffset.Now, omitFractionalSeconds)); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public void VerifyWriteGeneralizedTime_IgnoresConstructed(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + DateTimeOffset value = new DateTimeOffset(2017, 11, 16, 17, 35, 1, TimeSpan.Zero); + + writer.WriteGeneralizedTime(new Asn1Tag(UniversalTagNumber.GeneralizedTime, true), value); + writer.WriteGeneralizedTime(new Asn1Tag(TagClass.ContextSpecific, 3, true), value); + Verify(writer, "180F32303137313131363137333530315A" + "830F32303137313131363137333530315A"); + } + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteIA5String.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteIA5String.cs new file mode 100644 index 0000000000..8bcda287a5 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteIA5String.cs @@ -0,0 +1,288 @@ +// 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.Collections.Generic; +using System.Security.Cryptography.Asn1; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public class WriteIA5String : WriteCharacterString + { + public static IEnumerable ShortValidCases { get; } = new object[][] + { + new object[] + { + string.Empty, + "00", + }, + new object[] + { + "hi", + "026869", + }, + new object[] + { + "Dr. & Mrs. Smith-Jones & children", + "2144722E2026204D72732E20536D6974682D4A6F6E65732026206368696C6472656E", + }, + }; + + public static IEnumerable LongValidCases { get; } = new object[][] + { + new object[] + { + new string('f', 957) + new string('w', 182), + "820473" + new string('6', 957 * 2) + new string('7', 182 * 2), + }, + }; + + public static IEnumerable CERSegmentedCases { get; } = new object[][] + { + new object[] + { + GettysburgAddress, + 1458, + }, + new object[] + { + new string ('Q', 2000), + 2000, + }, + }; + + public static IEnumerable InvalidInputs { get; } = new object[][] + { + new object[] { "Dr. & Mrs. Smith\u2010Jones \uFE60 children", }, + }; + + internal override void WriteString(AsnWriter writer, string s) => + writer.WriteCharacterString(UniversalTagNumber.IA5String, s); + + internal override void WriteString(AsnWriter writer, Asn1Tag tag, string s) => + writer.WriteCharacterString(tag, UniversalTagNumber.IA5String, s); + + internal override void WriteSpan(AsnWriter writer, ReadOnlySpan s) => + writer.WriteCharacterString(UniversalTagNumber.IA5String, s); + + internal override void WriteSpan(AsnWriter writer, Asn1Tag tag, ReadOnlySpan s) => + writer.WriteCharacterString(tag, UniversalTagNumber.IA5String, s); + + internal override Asn1Tag StandardTag => new Asn1Tag(UniversalTagNumber.IA5String); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_String(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_String(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_String_CustomTag(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_String_CustomTag(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_String(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_String(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_String_CustomTag(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_String_CustomTag(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_String(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_String(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_String_CustomTag(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_String_CustomTag(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_Span(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_Span(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_Span_CustomTag(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_Span_CustomTag(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_Span(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_Span(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_Span_CustomTag(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_Span_CustomTag(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_Span(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_Span(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_Span_CustomTag(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_Span_CustomTag(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_String_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_String_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_String_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_String_CustomTag_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_Span_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_Span_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_Span_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_Span_CustomTag_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_String_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_String_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_String_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_String_CustomTag_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_Span_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_Span_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_Span_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_Span_CustomTag_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_String_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_String_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_String_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_String_CustomTag_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_Span_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_Span_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_Span_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_Span_CustomTag_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public new void VerifyWrite_String_Null(PublicEncodingRules ruleSet) => + base.VerifyWrite_String_Null(ruleSet); + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public new void VerifyWrite_String_Null_CustomTag(PublicEncodingRules ruleSet) => + base.VerifyWrite_String_Null_CustomTag(ruleSet); + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public new void VerifyWrite_EndOfContents_String(PublicEncodingRules ruleSet) => + base.VerifyWrite_EndOfContents_String(ruleSet); + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public new void VerifyWrite_EndOfContents_Span(PublicEncodingRules ruleSet) => + base.VerifyWrite_EndOfContents_Span(ruleSet); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_String(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_String(input, contentByteCount); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_String_CustomTag(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_String_CustomTag(input, contentByteCount); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_String_ConstructedTag(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_String_ConstructedTag(input, contentByteCount); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_String_CustomPrimitiveTag(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_String_CustomPrimitiveTag(input, contentByteCount); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_Span(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_Span(input, contentByteCount); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_Span_CustomTag(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_Span_CustomTag(input, contentByteCount); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_Span_ConstructedTag(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_Span_ConstructedTag(input, contentByteCount); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_Span_CustomPrimitiveTag(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_Span_CustomPrimitiveTag(input, contentByteCount); + + [Theory] + [MemberData(nameof(InvalidInputs))] + public new void VerifyWrite_String_NonEncodable(string input) => + base.VerifyWrite_String_NonEncodable(input); + + [Theory] + [MemberData(nameof(InvalidInputs))] + public new void VerifyWrite_Span_NonEncodable(string input) => + base.VerifyWrite_Span_NonEncodable(input); + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteInteger.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteInteger.cs new file mode 100644 index 0000000000..de17502ca9 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteInteger.cs @@ -0,0 +1,323 @@ +// 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.Numerics; +using System.Security.Cryptography.Asn1; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public class WriteInteger : Asn1WriterTests + { + [Theory] + [InlineData(PublicEncodingRules.BER, 0, "020100")] + [InlineData(PublicEncodingRules.CER, 0, "020100")] + [InlineData(PublicEncodingRules.DER, 0, "020100")] + [InlineData(PublicEncodingRules.BER, -1, "0201FF")] + [InlineData(PublicEncodingRules.CER, -1, "0201FF")] + [InlineData(PublicEncodingRules.DER, -1, "0201FF")] + [InlineData(PublicEncodingRules.BER, -2, "0201FE")] + [InlineData(PublicEncodingRules.DER, sbyte.MinValue, "020180")] + [InlineData(PublicEncodingRules.BER, sbyte.MinValue + 1, "020181")] + [InlineData(PublicEncodingRules.CER, sbyte.MinValue - 1, "0202FF7F")] + [InlineData(PublicEncodingRules.DER, sbyte.MinValue - 2, "0202FF7E")] + [InlineData(PublicEncodingRules.BER, -256, "0202FF00")] + [InlineData(PublicEncodingRules.CER, -257, "0202FEFF")] + [InlineData(PublicEncodingRules.DER, short.MinValue, "02028000")] + [InlineData(PublicEncodingRules.BER, short.MinValue + 1, "02028001")] + [InlineData(PublicEncodingRules.CER, short.MinValue + byte.MaxValue, "020280FF")] + [InlineData(PublicEncodingRules.DER, short.MinValue - 1, "0203FF7FFF")] + [InlineData(PublicEncodingRules.BER, short.MinValue - 2, "0203FF7FFE")] + [InlineData(PublicEncodingRules.CER, -65281, "0203FF00FF")] + [InlineData(PublicEncodingRules.DER, -8388608, "0203800000")] + [InlineData(PublicEncodingRules.BER, -8388607, "0203800001")] + [InlineData(PublicEncodingRules.CER, -8388609, "0204FF7FFFFF")] + [InlineData(PublicEncodingRules.DER, -16777216, "0204FF000000")] + [InlineData(PublicEncodingRules.BER, -16777217, "0204FEFFFFFF")] + [InlineData(PublicEncodingRules.CER, int.MinValue, "020480000000")] + [InlineData(PublicEncodingRules.DER, int.MinValue + 1, "020480000001")] + [InlineData(PublicEncodingRules.BER, (long)int.MinValue - 1, "0205FF7FFFFFFF")] + [InlineData(PublicEncodingRules.CER, (long)int.MinValue - 2, "0205FF7FFFFFFE")] + [InlineData(PublicEncodingRules.DER, -4294967296, "0205FF00000000")] + [InlineData(PublicEncodingRules.BER, -4294967295, "0205FF00000001")] + [InlineData(PublicEncodingRules.CER, -4294967294, "0205FF00000002")] + [InlineData(PublicEncodingRules.DER, -4294967297, "0205FEFFFFFFFF")] + [InlineData(PublicEncodingRules.BER, -549755813888, "02058000000000")] + [InlineData(PublicEncodingRules.CER, -549755813887, "02058000000001")] + [InlineData(PublicEncodingRules.DER, -549755813889, "0206FF7FFFFFFFFF")] + [InlineData(PublicEncodingRules.BER, -549755813890, "0206FF7FFFFFFFFE")] + [InlineData(PublicEncodingRules.CER, -140737488355328, "0206800000000000")] + [InlineData(PublicEncodingRules.DER, -140737488355327, "0206800000000001")] + [InlineData(PublicEncodingRules.BER, -140737488355329, "0207FF7FFFFFFFFFFF")] + [InlineData(PublicEncodingRules.CER, -281474976710656, "0207FF000000000000")] + [InlineData(PublicEncodingRules.DER, -281474976710655, "0207FF000000000001")] + [InlineData(PublicEncodingRules.BER, -281474976710657, "0207FEFFFFFFFFFFFF")] + [InlineData(PublicEncodingRules.CER, -36028797018963968, "020780000000000000")] + [InlineData(PublicEncodingRules.DER, -36028797018963967, "020780000000000001")] + [InlineData(PublicEncodingRules.DER, -36028797018963969, "0208FF7FFFFFFFFFFFFF")] + [InlineData(PublicEncodingRules.BER, -36028797018963970, "0208FF7FFFFFFFFFFFFE")] + [InlineData(PublicEncodingRules.CER, -72057594037927936, "0208FF00000000000000")] + [InlineData(PublicEncodingRules.DER, -72057594037927935, "0208FF00000000000001")] + [InlineData(PublicEncodingRules.BER, -72057594037927937, "0208FEFFFFFFFFFFFFFF")] + [InlineData(PublicEncodingRules.CER, long.MinValue + 1, "02088000000000000001")] + [InlineData(PublicEncodingRules.DER, long.MinValue, "02088000000000000000")] + [InlineData(PublicEncodingRules.BER, 1, "020101")] + [InlineData(PublicEncodingRules.CER, 127, "02017F")] + [InlineData(PublicEncodingRules.DER, 126, "02017E")] + [InlineData(PublicEncodingRules.BER, 128, "02020080")] + [InlineData(PublicEncodingRules.CER, 129, "02020081")] + [InlineData(PublicEncodingRules.DER, 254, "020200FE")] + [InlineData(PublicEncodingRules.BER, 255, "020200FF")] + [InlineData(PublicEncodingRules.CER, 256, "02020100")] + [InlineData(PublicEncodingRules.DER, 32767, "02027FFF")] + [InlineData(PublicEncodingRules.BER, 32766, "02027FFE")] + [InlineData(PublicEncodingRules.CER, 32768, "0203008000")] + [InlineData(PublicEncodingRules.DER, 32769, "0203008001")] + [InlineData(PublicEncodingRules.BER, 65535, "020300FFFF")] + [InlineData(PublicEncodingRules.CER, 65534, "020300FFFE")] + [InlineData(PublicEncodingRules.DER, 65536, "0203010000")] + [InlineData(PublicEncodingRules.BER, 65537, "0203010001")] + [InlineData(PublicEncodingRules.CER, 8388607, "02037FFFFF")] + [InlineData(PublicEncodingRules.DER, 8388606, "02037FFFFE")] + [InlineData(PublicEncodingRules.BER, 8388608, "020400800000")] + [InlineData(PublicEncodingRules.CER, 8388609, "020400800001")] + [InlineData(PublicEncodingRules.DER, 16777215, "020400FFFFFF")] + [InlineData(PublicEncodingRules.BER, 16777214, "020400FFFFFE")] + [InlineData(PublicEncodingRules.CER, 16777216, "020401000000")] + [InlineData(PublicEncodingRules.DER, 16777217, "020401000001")] + [InlineData(PublicEncodingRules.BER, 2147483647, "02047FFFFFFF")] + [InlineData(PublicEncodingRules.CER, 2147483646, "02047FFFFFFE")] + [InlineData(PublicEncodingRules.DER, 2147483648, "02050080000000")] + [InlineData(PublicEncodingRules.BER, 2147483649, "02050080000001")] + [InlineData(PublicEncodingRules.BER, 4294967295, "020500FFFFFFFF")] + [InlineData(PublicEncodingRules.CER, 4294967294, "020500FFFFFFFE")] + [InlineData(PublicEncodingRules.DER, 4294967296, "02050100000000")] + [InlineData(PublicEncodingRules.BER, 4294967297, "02050100000001")] + [InlineData(PublicEncodingRules.CER, 549755813887, "02057FFFFFFFFF")] + [InlineData(PublicEncodingRules.DER, 549755813886, "02057FFFFFFFFE")] + [InlineData(PublicEncodingRules.BER, 549755813888, "0206008000000000")] + [InlineData(PublicEncodingRules.CER, 549755813889, "0206008000000001")] + [InlineData(PublicEncodingRules.DER, 1099511627775, "020600FFFFFFFFFF")] + [InlineData(PublicEncodingRules.BER, 1099511627774, "020600FFFFFFFFFE")] + [InlineData(PublicEncodingRules.CER, 1099511627776, "0206010000000000")] + [InlineData(PublicEncodingRules.DER, 1099511627777, "0206010000000001")] + [InlineData(PublicEncodingRules.BER, 140737488355327, "02067FFFFFFFFFFF")] + [InlineData(PublicEncodingRules.CER, 140737488355326, "02067FFFFFFFFFFE")] + [InlineData(PublicEncodingRules.DER, 140737488355328, "020700800000000000")] + [InlineData(PublicEncodingRules.BER, 140737488355329, "020700800000000001")] + [InlineData(PublicEncodingRules.CER, 281474976710655, "020700FFFFFFFFFFFF")] + [InlineData(PublicEncodingRules.DER, 281474976710654, "020700FFFFFFFFFFFE")] + [InlineData(PublicEncodingRules.BER, 281474976710656, "020701000000000000")] + [InlineData(PublicEncodingRules.CER, 281474976710657, "020701000000000001")] + [InlineData(PublicEncodingRules.DER, 36028797018963967, "02077FFFFFFFFFFFFF")] + [InlineData(PublicEncodingRules.BER, 36028797018963966, "02077FFFFFFFFFFFFE")] + [InlineData(PublicEncodingRules.CER, 36028797018963968, "02080080000000000000")] + [InlineData(PublicEncodingRules.DER, 36028797018963969, "02080080000000000001")] + [InlineData(PublicEncodingRules.BER, 72057594037927935, "020800FFFFFFFFFFFFFF")] + [InlineData(PublicEncodingRules.CER, 72057594037927934, "020800FFFFFFFFFFFFFE")] + [InlineData(PublicEncodingRules.DER, 72057594037927936, "02080100000000000000")] + [InlineData(PublicEncodingRules.BER, 72057594037927937, "02080100000000000001")] + [InlineData(PublicEncodingRules.CER, 9223372036854775807, "02087FFFFFFFFFFFFFFF")] + [InlineData(PublicEncodingRules.DER, 9223372036854775806, "02087FFFFFFFFFFFFFFE")] + public void VerifyWriteInteger_Long(PublicEncodingRules ruleSet, long value, string expectedHex) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteInteger(value); + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, 0, "020100")] + [InlineData(PublicEncodingRules.CER, 0, "020100")] + [InlineData(PublicEncodingRules.DER, 0, "020100")] + [InlineData(PublicEncodingRules.BER, 1, "020101")] + [InlineData(PublicEncodingRules.CER, 127, "02017F")] + [InlineData(PublicEncodingRules.DER, 126, "02017E")] + [InlineData(PublicEncodingRules.BER, 128, "02020080")] + [InlineData(PublicEncodingRules.CER, 129, "02020081")] + [InlineData(PublicEncodingRules.DER, 254, "020200FE")] + [InlineData(PublicEncodingRules.BER, 255, "020200FF")] + [InlineData(PublicEncodingRules.CER, 256, "02020100")] + [InlineData(PublicEncodingRules.DER, 32767, "02027FFF")] + [InlineData(PublicEncodingRules.BER, 32766, "02027FFE")] + [InlineData(PublicEncodingRules.CER, 32768, "0203008000")] + [InlineData(PublicEncodingRules.DER, 32769, "0203008001")] + [InlineData(PublicEncodingRules.BER, 65535, "020300FFFF")] + [InlineData(PublicEncodingRules.CER, 65534, "020300FFFE")] + [InlineData(PublicEncodingRules.DER, 65536, "0203010000")] + [InlineData(PublicEncodingRules.BER, 65537, "0203010001")] + [InlineData(PublicEncodingRules.CER, 8388607, "02037FFFFF")] + [InlineData(PublicEncodingRules.DER, 8388606, "02037FFFFE")] + [InlineData(PublicEncodingRules.BER, 8388608, "020400800000")] + [InlineData(PublicEncodingRules.CER, 8388609, "020400800001")] + [InlineData(PublicEncodingRules.DER, 16777215, "020400FFFFFF")] + [InlineData(PublicEncodingRules.BER, 16777214, "020400FFFFFE")] + [InlineData(PublicEncodingRules.CER, 16777216, "020401000000")] + [InlineData(PublicEncodingRules.DER, 16777217, "020401000001")] + [InlineData(PublicEncodingRules.BER, 2147483647, "02047FFFFFFF")] + [InlineData(PublicEncodingRules.CER, 2147483646, "02047FFFFFFE")] + [InlineData(PublicEncodingRules.DER, 2147483648, "02050080000000")] + [InlineData(PublicEncodingRules.BER, 2147483649, "02050080000001")] + [InlineData(PublicEncodingRules.BER, 4294967295, "020500FFFFFFFF")] + [InlineData(PublicEncodingRules.CER, 4294967294, "020500FFFFFFFE")] + [InlineData(PublicEncodingRules.DER, 4294967296, "02050100000000")] + [InlineData(PublicEncodingRules.BER, 4294967297, "02050100000001")] + [InlineData(PublicEncodingRules.CER, 549755813887, "02057FFFFFFFFF")] + [InlineData(PublicEncodingRules.DER, 549755813886, "02057FFFFFFFFE")] + [InlineData(PublicEncodingRules.BER, 549755813888, "0206008000000000")] + [InlineData(PublicEncodingRules.CER, 549755813889, "0206008000000001")] + [InlineData(PublicEncodingRules.DER, 1099511627775, "020600FFFFFFFFFF")] + [InlineData(PublicEncodingRules.BER, 1099511627774, "020600FFFFFFFFFE")] + [InlineData(PublicEncodingRules.CER, 1099511627776, "0206010000000000")] + [InlineData(PublicEncodingRules.DER, 1099511627777, "0206010000000001")] + [InlineData(PublicEncodingRules.BER, 140737488355327, "02067FFFFFFFFFFF")] + [InlineData(PublicEncodingRules.CER, 140737488355326, "02067FFFFFFFFFFE")] + [InlineData(PublicEncodingRules.DER, 140737488355328, "020700800000000000")] + [InlineData(PublicEncodingRules.BER, 140737488355329, "020700800000000001")] + [InlineData(PublicEncodingRules.CER, 281474976710655, "020700FFFFFFFFFFFF")] + [InlineData(PublicEncodingRules.DER, 281474976710654, "020700FFFFFFFFFFFE")] + [InlineData(PublicEncodingRules.BER, 281474976710656, "020701000000000000")] + [InlineData(PublicEncodingRules.CER, 281474976710657, "020701000000000001")] + [InlineData(PublicEncodingRules.DER, 36028797018963967, "02077FFFFFFFFFFFFF")] + [InlineData(PublicEncodingRules.BER, 36028797018963966, "02077FFFFFFFFFFFFE")] + [InlineData(PublicEncodingRules.CER, 36028797018963968, "02080080000000000000")] + [InlineData(PublicEncodingRules.DER, 36028797018963969, "02080080000000000001")] + [InlineData(PublicEncodingRules.BER, 72057594037927935, "020800FFFFFFFFFFFFFF")] + [InlineData(PublicEncodingRules.CER, 72057594037927934, "020800FFFFFFFFFFFFFE")] + [InlineData(PublicEncodingRules.DER, 72057594037927936, "02080100000000000000")] + [InlineData(PublicEncodingRules.BER, 72057594037927937, "02080100000000000001")] + [InlineData(PublicEncodingRules.CER, 9223372036854775807, "02087FFFFFFFFFFFFFFF")] + [InlineData(PublicEncodingRules.DER, 9223372036854775806, "02087FFFFFFFFFFFFFFE")] + [InlineData(PublicEncodingRules.BER, 9223372036854775808, "0209008000000000000000")] + [InlineData(PublicEncodingRules.CER, 9223372036854775809, "0209008000000000000001")] + [InlineData(PublicEncodingRules.DER, ulong.MaxValue, "020900FFFFFFFFFFFFFFFF")] + [InlineData(PublicEncodingRules.BER, ulong.MaxValue-1, "020900FFFFFFFFFFFFFFFE")] + public void VerifyWriteInteger_ULong(PublicEncodingRules ruleSet, ulong value, string expectedHex) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteInteger(value); + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, "0", "020100")] + [InlineData(PublicEncodingRules.CER, "127", "02017F")] + [InlineData(PublicEncodingRules.DER, "128", "02020080")] + [InlineData(PublicEncodingRules.BER, "32767", "02027FFF")] + [InlineData(PublicEncodingRules.CER, "32768", "0203008000")] + [InlineData(PublicEncodingRules.DER, "9223372036854775807", "02087FFFFFFFFFFFFFFF")] + [InlineData(PublicEncodingRules.BER, "9223372036854775808", "0209008000000000000000")] + [InlineData(PublicEncodingRules.CER, "18446744073709551615", "020900FFFFFFFFFFFFFFFF")] + [InlineData(PublicEncodingRules.DER, "18446744073709551616", "0209010000000000000000")] + [InlineData(PublicEncodingRules.BER, "1339673755198158349044581307228491520", "02100102030405060708090A0B0C0D0E0F00")] + [InlineData(PublicEncodingRules.CER, "320182027492359845421654932427609477120", "021100F0E0D0C0B0A090807060504030201000")] + [InlineData(PublicEncodingRules.DER, "-1339673755198158349044581307228491520", "0210FEFDFCFBFAF9F8F7F6F5F4F3F2F1F100")] + public void VerifyWriteInteger_BigInteger(PublicEncodingRules ruleSet, string decimalValue, string expectedHex) + { + BigInteger value = BigInteger.Parse(decimalValue); + + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteInteger(value); + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, 0, "470100")] + [InlineData(PublicEncodingRules.CER, long.MinValue + 1, "47088000000000000001")] + [InlineData(PublicEncodingRules.DER, 9223372036854775806, "47087FFFFFFFFFFFFFFE")] + public void VerifyWriteInteger_Application7_Long(PublicEncodingRules ruleSet, long value, string expectedHex) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteInteger(new Asn1Tag(TagClass.Application, 7), value); + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, 0, "890100")] + [InlineData(PublicEncodingRules.CER, 9223372036854775809, "8909008000000000000001")] + [InlineData(PublicEncodingRules.DER, 9223372036854775806, "89087FFFFFFFFFFFFFFE")] + public void VerifyWriteInteger_Context9_ULong(PublicEncodingRules ruleSet, ulong value, string expectedHex) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteInteger(new Asn1Tag(TagClass.ContextSpecific, 9), value); + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, 0, "D00100")] + [InlineData(PublicEncodingRules.BER, "1339673755198158349044581307228491520", "D0100102030405060708090A0B0C0D0E0F00")] + [InlineData(PublicEncodingRules.CER, "320182027492359845421654932427609477120", "D01100F0E0D0C0B0A090807060504030201000")] + [InlineData(PublicEncodingRules.DER, "-1339673755198158349044581307228491520", "D010FEFDFCFBFAF9F8F7F6F5F4F3F2F1F100")] + public void VerifyWriteInteger_Private16_BigInteger( + PublicEncodingRules ruleSet, + string decimalValue, + string expectedHex) + { + BigInteger value = BigInteger.Parse(decimalValue); + + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteInteger(new Asn1Tag(TagClass.Private, 16), value); + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public void VerifyWriteInteger_EndOfContents(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + AssertExtensions.Throws( + "tag", + () => writer.WriteInteger(Asn1Tag.EndOfContents, 0L)); + + AssertExtensions.Throws( + "tag", + () => writer.WriteInteger(Asn1Tag.EndOfContents, 0UL)); + + AssertExtensions.Throws( + "tag", + () => writer.WriteInteger(Asn1Tag.EndOfContents, BigInteger.Zero)); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public void VerifyWriteInteger_ConstructedIgnored(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteInteger(new Asn1Tag(UniversalTagNumber.Integer, isConstructed: true), 0L); + writer.WriteInteger(new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true), 0L); + writer.WriteInteger(new Asn1Tag(UniversalTagNumber.Integer, isConstructed: true), 0UL); + writer.WriteInteger(new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true), 0UL); + writer.WriteInteger(new Asn1Tag(UniversalTagNumber.Integer, isConstructed: true), BigInteger.Zero); + writer.WriteInteger(new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true), BigInteger.Zero); + + Verify(writer, "020100800100020100800100020100800100"); + } + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNamedBitList.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNamedBitList.cs new file mode 100644 index 0000000000..d24ca5c6f4 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNamedBitList.cs @@ -0,0 +1,237 @@ +// 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.Security.Cryptography.Asn1; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public class WriteNamedBitList : Asn1WriterTests + { + [Theory] + [InlineData( + PublicEncodingRules.BER, + "030100", + ReadNamedBitList.ULongFlags.None)] + [InlineData( + PublicEncodingRules.CER, + "030100", + ReadNamedBitList.ULongFlags.None)] + [InlineData( + PublicEncodingRules.DER, + "030100", + ReadNamedBitList.ULongFlags.None)] + [InlineData( + PublicEncodingRules.BER, + "0309000000000000000003", + ReadNamedBitList.ULongFlags.Max | ReadNamedBitList.ULongFlags.AlmostMax)] + [InlineData( + PublicEncodingRules.CER, + "0309010000000080000002", + ReadNamedBitList.LongFlags.Max | ReadNamedBitList.LongFlags.Mid)] + [InlineData( + PublicEncodingRules.DER, + "030204B0", + ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | + ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | + ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment)] + public static void VerifyWriteNamedBitList( + PublicEncodingRules ruleSet, + string expectedHex, + object value) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteNamedBitList(value); + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData( + PublicEncodingRules.BER, + "C00100", + ReadNamedBitList.ULongFlags.None)] + [InlineData( + PublicEncodingRules.CER, + "410100", + ReadNamedBitList.ULongFlags.None)] + [InlineData( + PublicEncodingRules.DER, + "820100", + ReadNamedBitList.ULongFlags.None)] + [InlineData( + PublicEncodingRules.BER, + "C009000000000000000003", + ReadNamedBitList.ULongFlags.Max | ReadNamedBitList.ULongFlags.AlmostMax)] + [InlineData( + PublicEncodingRules.CER, + "4109010000000080000002", + ReadNamedBitList.LongFlags.Max | ReadNamedBitList.LongFlags.Mid)] + [InlineData( + PublicEncodingRules.DER, + "820204B0", + ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | + ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | + ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment)] + public static void VerifyWriteNamedBitList_WithTag( + PublicEncodingRules ruleSet, + string expectedHex, + object value) + { + int ruleSetVal = (int)ruleSet; + TagClass tagClass = (TagClass)(byte)(ruleSetVal << 6); + + if (tagClass == TagClass.Universal) + tagClass = TagClass.Private; + + Asn1Tag tag = new Asn1Tag(tagClass, ruleSetVal); + + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteNamedBitList(tag, value); + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void VerifyWriteNamedBitList_Generic(PublicEncodingRules ruleSet) + { + using (AsnWriter objWriter = new AsnWriter((AsnEncodingRules)ruleSet)) + using (AsnWriter genWriter = new AsnWriter((AsnEncodingRules)ruleSet)) + { + var flagsValue = + ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | + ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | + ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment; + + genWriter.WriteNamedBitList(flagsValue); + objWriter.WriteNamedBitList((object)flagsValue); + + Verify(genWriter, objWriter.Encode().ByteArrayToHex()); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void VerifyWriteNamedBitList_Generic_WithTag(PublicEncodingRules ruleSet) + { + using (AsnWriter objWriter = new AsnWriter((AsnEncodingRules)ruleSet)) + using (AsnWriter genWriter = new AsnWriter((AsnEncodingRules)ruleSet)) + { + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 52); + + var flagsValue = + ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | + ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | + ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment; + + genWriter.WriteNamedBitList(tag, flagsValue); + objWriter.WriteNamedBitList(tag, (object)flagsValue); + + Verify(genWriter, objWriter.Encode().ByteArrayToHex()); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void VerifyWriteNamedBitList_NonNull(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + AssertExtensions.Throws( + "enumValue", + () => writer.WriteNamedBitList(null)); + + AssertExtensions.Throws( + "enumValue", + () => writer.WriteNamedBitList(new Asn1Tag(TagClass.ContextSpecific, 1), null)); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void VerifyWriteNamedBitList_EnumRequired(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + Assert.Throws( + () => writer.WriteNamedBitList(3)); + + Assert.Throws( + () => writer.WriteNamedBitList(new Asn1Tag(TagClass.ContextSpecific, 1), 3)); + + Assert.Throws( + () => writer.WriteNamedBitList((object)3)); + + Assert.Throws( + () => writer.WriteNamedBitList(new Asn1Tag(TagClass.ContextSpecific, 1), (object)3)); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void VerifyWriteNamedBitList_FlagsEnumRequired(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + AssertExtensions.Throws( + "tEnum", + () => writer.WriteNamedBitList(AsnEncodingRules.BER)); + + AssertExtensions.Throws( + "tEnum", + () => writer.WriteNamedBitList( + new Asn1Tag(TagClass.ContextSpecific, 1), + AsnEncodingRules.BER)); + + AssertExtensions.Throws( + "tEnum", + () => writer.WriteNamedBitList((object)AsnEncodingRules.BER)); + + AssertExtensions.Throws( + "tEnum", + () => writer.WriteNamedBitList( + new Asn1Tag(TagClass.ContextSpecific, 1), + (object)AsnEncodingRules.BER)); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public static void VerifyWriteNamedBitList_EndOfContents(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + AssertExtensions.Throws( + "tag", + () => writer.WriteNamedBitList( + Asn1Tag.EndOfContents, + StringSplitOptions.RemoveEmptyEntries)); + + AssertExtensions.Throws( + "tag", + () => writer.WriteNamedBitList( + Asn1Tag.EndOfContents, + (object)StringSplitOptions.RemoveEmptyEntries)); + } + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNull.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNull.cs new file mode 100644 index 0000000000..a22a40f69f --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNull.cs @@ -0,0 +1,55 @@ +// 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.Security.Cryptography.Asn1; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public class WriteNull : Asn1WriterTests + { + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.DER)] + [InlineData(PublicEncodingRules.CER)] + public void VerifyWriteNull(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteNull(); + + Verify(writer, "0500"); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.DER)] + [InlineData(PublicEncodingRules.CER)] + public void VerifyWriteNull_EndOfContents(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + AssertExtensions.Throws( + "tag", + () => writer.WriteNull(Asn1Tag.EndOfContents)); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.DER)] + [InlineData(PublicEncodingRules.CER)] + public void VerifyWriteNull_ConstructedIgnored(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteNull(new Asn1Tag(TagClass.ContextSpecific, 7, true)); + writer.WriteNull(new Asn1Tag(UniversalTagNumber.Null, true)); + + Verify(writer, "87000500"); + } + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteObjectIdentifier.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteObjectIdentifier.cs new file mode 100644 index 0000000000..5d480a0082 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteObjectIdentifier.cs @@ -0,0 +1,313 @@ +// 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.Collections.Generic; +using System.Security.Cryptography.Asn1; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public class WriteObjectIdentifier : Asn1WriterTests + { + [Theory] + [MemberData(nameof(ValidOidData))] + public void VerifyWriteObjectIdentifier_String( + PublicEncodingRules ruleSet, + string oidValue, + string expectedHex) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteObjectIdentifier(oidValue); + + Verify(writer, expectedHex); + } + } + + [Theory] + [MemberData(nameof(ValidOidData))] + public void VerifyWriteObjectIdentifier_Span( + PublicEncodingRules ruleSet, + string oidValue, + string expectedHex) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteObjectIdentifier(oidValue.AsReadOnlySpan()); + + Verify(writer, expectedHex); + } + } + + [Theory] + [MemberData(nameof(ValidOidData))] + public void VerifyWriteObjectIdentifier_Oid( + PublicEncodingRules ruleSet, + string oidValue, + string expectedHex) + { + Oid oidObj = new Oid(oidValue, "FriendlyName does not matter"); + + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteObjectIdentifier(oidObj); + + Verify(writer, expectedHex); + } + } + + [Theory] + [MemberData(nameof(InvalidOidData))] + public void VerifyWriteOid_InvalidValue_String( + string description, + PublicEncodingRules ruleSet, + string nonOidValue) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + Assert.Throws( + () => writer.WriteObjectIdentifier(nonOidValue)); + } + } + + [Theory] + [MemberData(nameof(InvalidOidData))] + public void VerifyWriteOid_InvalidValue_Span( + string description, + PublicEncodingRules ruleSet, + string nonOidValue) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + Assert.Throws( + () => writer.WriteObjectIdentifier(nonOidValue.AsReadOnlySpan())); + } + } + + [Theory] + [MemberData(nameof(InvalidOidData))] + public void VerifyWriteOid_InvalidValue_Oid( + string description, + PublicEncodingRules ruleSet, + string nonOidValue) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + Oid nonOidObj = new Oid(nonOidValue, "FriendlyName does not matter"); + + Assert.Throws( + () => writer.WriteObjectIdentifier(nonOidObj)); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public void WriteObjectIdentifier_CustomTag_String(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteObjectIdentifier(new Asn1Tag(TagClass.ContextSpecific, 3), "1.3.14.3.2.26"); + + Verify(writer, "83052B0E03021A"); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public void WriteObjectIdentifier_CustomTag_Span(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteObjectIdentifier(new Asn1Tag(TagClass.Application, 2), "1.3.14.3.2.26".AsReadOnlySpan()); + + Verify(writer, "42052B0E03021A"); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public void WriteObjectIdentifier_CustomTag_Oid(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteObjectIdentifier( + new Asn1Tag(TagClass.Private, 36), + Oid.FromFriendlyName("SHA1", OidGroup.HashAlgorithm)); + + Verify(writer, "DF24052B0E03021A"); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void WriteObjectIdentifier_NullString(bool defaultTag) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + AssertExtensions.Throws( + "oidValue", + () => + { + if (defaultTag) + { + writer.WriteObjectIdentifier((string)null); + } + else + { + writer.WriteObjectIdentifier(new Asn1Tag(TagClass.ContextSpecific, 6), (string)null); + } + }); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void WriteObjectIdentifier_NullOid(bool defaultTag) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + AssertExtensions.Throws( + "oid", + () => + { + if (defaultTag) + { + writer.WriteObjectIdentifier((Oid)null); + } + else + { + writer.WriteObjectIdentifier(new Asn1Tag(TagClass.Application, 2), (Oid)null); + } + }); + } + } + + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public void VerifyWriteObjectIdentifier_EndOfContents(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + AssertExtensions.Throws( + "tag", + () => writer.WriteObjectIdentifier(Asn1Tag.EndOfContents, "1.1")); + + AssertExtensions.Throws( + "tag", + () => writer.WriteObjectIdentifier(Asn1Tag.EndOfContents, "1.1".AsReadOnlySpan())); + + AssertExtensions.Throws( + "tag", + () => writer.WriteObjectIdentifier(Asn1Tag.EndOfContents, new Oid("1.1", "1.1"))); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public void VerifyWriteObjectIdentifier_ConstructedIgnored(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + const string OidValue = "1.1"; + Asn1Tag constructedOid = new Asn1Tag(UniversalTagNumber.ObjectIdentifier, isConstructed: true); + Asn1Tag constructedContext0 = new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true); + + writer.WriteObjectIdentifier(constructedOid, OidValue); + writer.WriteObjectIdentifier(constructedContext0, OidValue); + writer.WriteObjectIdentifier(constructedOid, OidValue.AsReadOnlySpan()); + writer.WriteObjectIdentifier(constructedContext0, OidValue.AsReadOnlySpan()); + writer.WriteObjectIdentifier(constructedOid, new Oid(OidValue, OidValue)); + writer.WriteObjectIdentifier(constructedContext0, new Oid(OidValue, OidValue)); + + Verify(writer, "060129800129060129800129060129800129"); + } + } + + public static IEnumerable ValidOidData { get; } = + new object[][] + { + new object[] + { + PublicEncodingRules.BER, + "0.0", + "060100", + }, + new object[] + { + PublicEncodingRules.CER, + "1.0", + "060128", + }, + new object[] + { + PublicEncodingRules.DER, + "2.0", + "060150", + }, + new object[] + { + PublicEncodingRules.BER, + "1.3.14.3.2.26", + "06052B0E03021A", + }, + new object[] + { + PublicEncodingRules.CER, + "2.999.19427512891.25", + "06088837C8AFE1A43B19", + }, + new object[] + { + PublicEncodingRules.DER, + "1.2.840.113549.1.1.10", + "06092A864886F70D01010A", + }, + new object[] + { + // Using the rules of ITU-T-REC-X.667-201210 for 2.25.{UUID} unregistered arcs, and + // their sample value of f81d4fae-7dec-11d0-a765-00a0c91e6bf6 + // this is + // { joint-iso-itu-t(2) uuid(255) thatuuid(329800735698586629295641978511506172918) three(3) } + PublicEncodingRules.DER, + "2.25.329800735698586629295641978511506172918.3", + "06156983F09DA7EBCFDEE0C7A1A7B2C0948CC8F9D77603", + }, + }; + + public static IEnumerable InvalidOidData { get; } = + new object[][] + { + new object[] { "Empty string", PublicEncodingRules.BER, "" }, + new object[] { "No period", PublicEncodingRules.CER, "1" }, + new object[] { "No second RID", PublicEncodingRules.DER, "1." }, + new object[] { "Invalid first RID", PublicEncodingRules.BER, "3.0" }, + new object[] { "Invalid first RID - multichar", PublicEncodingRules.CER, "27.0" }, + new object[] { "Double zero - First RID", PublicEncodingRules.DER, "00.0" }, + new object[] { "Leading zero - First RID", PublicEncodingRules.BER, "01.0" }, + new object[] { "Double zero - second RID", PublicEncodingRules.CER, "0.00" }, + new object[] { "Leading zero - second RID", PublicEncodingRules.DER, "0.01" }, + new object[] { "Ends with period - second RID", PublicEncodingRules.BER, "0.0." }, + new object[] { "Ends with period - third RID", PublicEncodingRules.BER, "0.1.30." }, + new object[] { "Double zero - third RID", PublicEncodingRules.CER, "0.1.00" }, + new object[] { "Leading zero - third RID", PublicEncodingRules.DER, "0.1.023" }, + new object[] { "Invalid character first position", PublicEncodingRules.BER, "a.1.23" }, + new object[] { "Invalid character second position", PublicEncodingRules.CER, "0,1.23" }, + new object[] { "Invalid character second rid", PublicEncodingRules.DER, "0.1q.23" }, + new object[] { "Invalid character third rid", PublicEncodingRules.BER, "0.1.23q" }, + }; + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteOctetString.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteOctetString.cs new file mode 100644 index 0000000000..dbd20b3680 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteOctetString.cs @@ -0,0 +1,165 @@ +// 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.Security.Cryptography.Asn1; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public class WriteOctetString : Asn1WriterTests + { + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public void WriteEmpty(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteOctetString(ReadOnlySpan.Empty); + + Verify(writer, "0400"); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, 1, "0401")] + [InlineData(PublicEncodingRules.CER, 2, "0402")] + [InlineData(PublicEncodingRules.DER, 3, "0403")] + [InlineData(PublicEncodingRules.BER, 126, "047E")] + [InlineData(PublicEncodingRules.CER, 127, "047F")] + [InlineData(PublicEncodingRules.DER, 128, "048180")] + [InlineData(PublicEncodingRules.BER, 1000, "048203E8")] + [InlineData(PublicEncodingRules.CER, 1000, "048203E8")] + [InlineData(PublicEncodingRules.DER, 1000, "048203E8")] + [InlineData(PublicEncodingRules.BER, 1001, "048203E9")] + [InlineData(PublicEncodingRules.DER, 1001, "048203E9")] + [InlineData(PublicEncodingRules.BER, 2001, "048207D1")] + [InlineData(PublicEncodingRules.DER, 2001, "048207D1")] + public void WritePrimitive(PublicEncodingRules ruleSet, int length, string hexStart) + { + string payloadHex = new string('0', 2 * length); + string expectedHex = hexStart + payloadHex; + byte[] data = new byte[length]; + + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteOctetString(data); + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData(1001, "2480048203E8", "0401")] + [InlineData(1999, "2480048203E8", "048203E7")] + [InlineData(2000, "2480048203E8", "048203E8")] + public void WriteSegmentedCER(int length, string hexStart, string hexStart2) + { + string payload1Hex = new string('8', 2000); + string payload2Hex = new string('8', (length - 1000) * 2); + string expectedHex = hexStart + payload1Hex + hexStart2 + payload2Hex + "0000"; + byte[] data = new byte[length]; + + for (int i = 0; i < data.Length; i++) + { + data[i] = 0x88; + } + + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + writer.WriteOctetString(data); + + Verify(writer, expectedHex); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER, 0, false)] + [InlineData(PublicEncodingRules.CER, 0, false)] + [InlineData(PublicEncodingRules.DER, 0, false)] + [InlineData(PublicEncodingRules.BER, 999, false)] + [InlineData(PublicEncodingRules.CER, 999, false)] + [InlineData(PublicEncodingRules.DER, 999, false)] + [InlineData(PublicEncodingRules.BER, 1000, false)] + [InlineData(PublicEncodingRules.CER, 1000, false)] + [InlineData(PublicEncodingRules.DER, 1000, false)] + [InlineData(PublicEncodingRules.BER, 1001, false)] + [InlineData(PublicEncodingRules.CER, 1001, true)] + [InlineData(PublicEncodingRules.DER, 1001, false)] + [InlineData(PublicEncodingRules.BER, 1998, false)] + [InlineData(PublicEncodingRules.CER, 1998, true)] + [InlineData(PublicEncodingRules.DER, 1998, false)] + [InlineData(PublicEncodingRules.BER, 1999, false)] + [InlineData(PublicEncodingRules.CER, 1999, true)] + [InlineData(PublicEncodingRules.DER, 1999, false)] + [InlineData(PublicEncodingRules.BER, 2000, false)] + [InlineData(PublicEncodingRules.CER, 2000, true)] + [InlineData(PublicEncodingRules.DER, 2000, false)] + [InlineData(PublicEncodingRules.BER, 2001, false)] + [InlineData(PublicEncodingRules.CER, 2001, true)] + [InlineData(PublicEncodingRules.DER, 2001, false)] + [InlineData(PublicEncodingRules.BER, 4096, false)] + [InlineData(PublicEncodingRules.CER, 4096, true)] + [InlineData(PublicEncodingRules.DER, 4096, false)] + public void VerifyWriteOctetString_PrimitiveOrConstructed( + PublicEncodingRules ruleSet, + int payloadLength, + bool expectConstructed) + { + byte[] data = new byte[payloadLength]; + + Asn1Tag[] tagsToTry = + { + new Asn1Tag(UniversalTagNumber.OctetString), + new Asn1Tag(UniversalTagNumber.OctetString, isConstructed: true), + new Asn1Tag(TagClass.Private, 87), + new Asn1Tag(TagClass.ContextSpecific, 13, isConstructed: true), + }; + + byte[] answerBuf = new byte[payloadLength + 100]; + + foreach (Asn1Tag toTry in tagsToTry) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + writer.WriteOctetString(toTry, data); + + Assert.True(writer.TryEncode(answerBuf, out _)); + } + Assert.True(Asn1Tag.TryParse(answerBuf, out Asn1Tag writtenTag, out _)); + + if (expectConstructed) + { + Assert.True(writtenTag.IsConstructed, $"writtenTag.IsConstructed ({toTry})"); + } + else + { + Assert.False(writtenTag.IsConstructed, $"writtenTag.IsConstructed ({toTry})"); + } + + Assert.Equal(toTry.TagClass, writtenTag.TagClass); + Assert.Equal(toTry.TagValue, writtenTag.TagValue); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public void VerifyWriteOctetString_EndOfContents(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + AssertExtensions.Throws( + "tag", + () => writer.WriteOctetString(Asn1Tag.EndOfContents, ReadOnlySpan.Empty)); + + AssertExtensions.Throws( + "tag", + () => writer.WriteOctetString(Asn1Tag.EndOfContents, new byte[1])); + } + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteUtcTime.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteUtcTime.cs new file mode 100644 index 0000000000..b9e1021124 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteUtcTime.cs @@ -0,0 +1,137 @@ +// 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.Collections.Generic; +using System.Security.Cryptography.Asn1; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public class WriteUtcTime : Asn1WriterTests + { + public static IEnumerable TestCases { get; } = new object[][] + { + new object[] + { + new DateTimeOffset(2017, 10, 16, 8, 24, 3, TimeSpan.FromHours(-7)), + "0D3137313031363135323430335A", + }, + new object[] + { + new DateTimeOffset(1817, 10, 16, 21, 24, 3, TimeSpan.FromHours(6)), + "0D3137313031363135323430335A", + }, + new object[] + { + new DateTimeOffset(3000, 1, 1, 0, 0, 0, TimeSpan.Zero), + "0D3030303130313030303030305A", + }, + }; + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteUtcTime_BER(DateTimeOffset input, string expectedHexPayload) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + writer.WriteUtcTime(input); + + Verify(writer, "17" + expectedHexPayload); + } + } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteUtcTime_BER_CustomTag(DateTimeOffset input, string expectedHexPayload) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.Application, 11); + writer.WriteUtcTime(tag, input); + + Verify(writer, Stringify(tag) + expectedHexPayload); + } + } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteUtcTime_CER(DateTimeOffset input, string expectedHexPayload) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + writer.WriteUtcTime(input); + + Verify(writer, "17" + expectedHexPayload); + } + } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteUtcTime_CER_CustomTag(DateTimeOffset input, string expectedHexPayload) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.Private, 95); + writer.WriteUtcTime(tag, input); + + Verify(writer, Stringify(tag) + expectedHexPayload); + } + } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteUtcTime_DER(DateTimeOffset input, string expectedHexPayload) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + { + writer.WriteUtcTime(input); + + Verify(writer, "17" + expectedHexPayload); + } + } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteUtcTime_DER_CustomTag(DateTimeOffset input, string expectedHexPayload) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + { + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); + writer.WriteUtcTime(tag, input); + + Verify(writer, Stringify(tag) + expectedHexPayload); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public void VerifyWriteUtcTime_EndOfContents(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + AssertExtensions.Throws( + "tag", + () => writer.WriteUtcTime(Asn1Tag.EndOfContents, DateTimeOffset.Now)); + } + } + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public void VerifyWriteUtcTime_IgnoresConstructed(PublicEncodingRules ruleSet) + { + using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + { + DateTimeOffset value = new DateTimeOffset(2017, 11, 16, 17, 35, 1, TimeSpan.Zero); + + writer.WriteUtcTime(new Asn1Tag(UniversalTagNumber.UtcTime, true), value); + writer.WriteUtcTime(new Asn1Tag(TagClass.ContextSpecific, 3, true), value); + Verify(writer, "170D3137313131363137333530315A" + "830D3137313131363137333530315A"); + } + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteUtf8String.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteUtf8String.cs new file mode 100644 index 0000000000..c8dce7a0d0 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteUtf8String.cs @@ -0,0 +1,287 @@ +// 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.Collections.Generic; +using System.Security.Cryptography.Asn1; +using Xunit; + +namespace System.Security.Cryptography.Tests.Asn1 +{ + public class WriteUtf8String : WriteCharacterString + { + public static IEnumerable ShortValidCases { get; } = new object[][] + { + new object[] + { + string.Empty, + "00", + }, + new object[] + { + "hi", + "026869", + }, + new object[] + { + "Dr. & Mrs. Smith\u2010Jones \uFE60 children", + "2544722E2026204D72732E20536D697468E280904A6F6E657320EFB9A0206368696C6472656E", + }, + }; + + public static IEnumerable LongValidCases { get; } = new object[][] + { + new object[] + { + new string('f', 957) + new string('w', 182), + "820473" + new string('6', 957 * 2) + new string('7', 182 * 2), + }, + }; + + public static IEnumerable CERSegmentedCases { get; } = new object[][] + { + new object[] + { + GettysburgAddress, + 1458, + }, + new object[] + { + // A whole bunch of "small ampersand" values (3 bytes each UTF-8), + // then one inverted exclamation (2 bytes UTF-8) + new string('\uFE60', 2000 / 3) + '\u00A1', + 2000, + }, + }; + + public static IEnumerable InvalidInputs => Array.Empty(); + + internal override void WriteString(AsnWriter writer, string s) => + writer.WriteCharacterString(UniversalTagNumber.UTF8String, s); + + internal override void WriteString(AsnWriter writer, Asn1Tag tag, string s) => + writer.WriteCharacterString(tag, UniversalTagNumber.UTF8String, s); + + internal override void WriteSpan(AsnWriter writer, ReadOnlySpan s) => + writer.WriteCharacterString(UniversalTagNumber.UTF8String, s); + + internal override void WriteSpan(AsnWriter writer, Asn1Tag tag, ReadOnlySpan s) => + writer.WriteCharacterString(tag, UniversalTagNumber.UTF8String, s); + + internal override Asn1Tag StandardTag => new Asn1Tag(UniversalTagNumber.UTF8String); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_String(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_String(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_String_CustomTag(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_String_CustomTag(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_String(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_String(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_String_CustomTag(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_String_CustomTag(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_String(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_String(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_String_CustomTag(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_String_CustomTag(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_Span(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_Span(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_Span_CustomTag(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_Span_CustomTag(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_Span(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_Span(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_Span_CustomTag(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_Span_CustomTag(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_Span(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_Span(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_Span_CustomTag(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_Span_CustomTag(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_String_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_String_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_String_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_String_CustomTag_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_Span_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_Span_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_BER_Span_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_BER_Span_CustomTag_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_String_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_String_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_String_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_String_CustomTag_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_Span_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_Span_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + public new void VerifyWrite_CER_Span_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_CER_Span_CustomTag_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_String_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_String_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_String_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_String_CustomTag_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_Span_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_Span_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [MemberData(nameof(ShortValidCases))] + [MemberData(nameof(LongValidCases))] + public new void VerifyWrite_DER_Span_CustomTag_ClearsConstructed(string input, string expectedPayloadHex) => + base.VerifyWrite_DER_Span_CustomTag_ClearsConstructed(input, expectedPayloadHex); + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public new void VerifyWrite_String_Null(PublicEncodingRules ruleSet) => + base.VerifyWrite_String_Null(ruleSet); + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public new void VerifyWrite_String_Null_CustomTag(PublicEncodingRules ruleSet) => + base.VerifyWrite_String_Null_CustomTag(ruleSet); + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public new void VerifyWrite_EndOfContents_String(PublicEncodingRules ruleSet) => + base.VerifyWrite_EndOfContents_String(ruleSet); + + [Theory] + [InlineData(PublicEncodingRules.BER)] + [InlineData(PublicEncodingRules.CER)] + [InlineData(PublicEncodingRules.DER)] + public new void VerifyWrite_EndOfContents_Span(PublicEncodingRules ruleSet) => + base.VerifyWrite_EndOfContents_Span(ruleSet); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_String(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_String(input, contentByteCount); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_String_CustomTag(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_String_CustomTag(input, contentByteCount); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_String_ConstructedTag(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_String_ConstructedTag(input, contentByteCount); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_String_CustomPrimitiveTag(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_String_CustomPrimitiveTag(input, contentByteCount); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_Span(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_Span(input, contentByteCount); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_Span_CustomTag(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_Span_CustomTag(input, contentByteCount); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_Span_ConstructedTag(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_Span_ConstructedTag(input, contentByteCount); + + [Theory] + [MemberData(nameof(CERSegmentedCases))] + public new void VerifyWrite_CERSegmented_Span_CustomPrimitiveTag(string input, int contentByteCount) => + base.VerifyWrite_CERSegmented_Span_CustomPrimitiveTag(input, contentByteCount); + + [Theory] + [MemberData(nameof(InvalidInputs))] + public new void VerifyWrite_String_NonEncodable(string input) => + base.VerifyWrite_String_NonEncodable(input); + + [Theory] + [MemberData(nameof(InvalidInputs))] + public new void VerifyWrite_Span_NonEncodable(string input) => + base.VerifyWrite_Span_NonEncodable(input); + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/DerSequenceReaderTests.cs b/external/corefx/src/System.Security.Cryptography.Encoding/tests/DerSequenceReaderTests.cs index e0591e55da..f63802b790 100644 --- a/external/corefx/src/System.Security.Cryptography.Encoding/tests/DerSequenceReaderTests.cs +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/DerSequenceReaderTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Numerics; +using Test.Cryptography; using Xunit; namespace System.Security.Cryptography.Encoding.Tests @@ -82,5 +83,128 @@ namespace System.Security.Cryptography.Encoding.Tests // And... done. Assert.False(reader.HasData); } + + [Theory] + [InlineData("Universal 31", "1F1F" + "0C" + "50323031375930324D313844")] + [InlineData("Universal 32", "1F20" + "0A" + "5030304830304D303053")] + [InlineData("Universal 127", "1F7F" + "01" + "00")] + [InlineData("Universal 128", "1F8100" + "01" + "00")] + [InlineData("Application 31", "5F1F" + "01" + "00")] + [InlineData("Application 32", "5F20" + "01" + "00")] + [InlineData("Application 127", "5F7F" + "01" + "00")] + [InlineData("Application 128", "5F8100" + "01" + "00")] + [InlineData("Context 31", "9F1F" + "01" + "00")] + [InlineData("Context 32", "9F20" + "01" + "00")] + [InlineData("Context 127", "9F7F" + "01" + "00")] + [InlineData("Context 128", "9F8100" + "01" + "00")] + [InlineData("Private 31", "DF1F" + "01" + "00")] + [InlineData("Private 32", "DF20" + "01" + "00")] + [InlineData("Private 127", "DF7F" + "01" + "00")] + [InlineData("Private 128", "DF8100" + "01" + "00")] + public static void NoSupportForMultiByteTags(string caseName, string hexInput) + { + byte[] bytes = hexInput.HexToByteArray(); + DerSequenceReader reader = DerSequenceReader.CreateForPayload(bytes); + + Assert.Throws(() => reader.PeekTag()); + Assert.Throws(() => reader.SkipValue()); + Assert.Throws(() => reader.ReadNextEncodedValue()); + } + + [Theory] + [InlineData("0401")] + [InlineData("0485")] + [InlineData("048500000000")] + [InlineData("04850000000000")] + [InlineData("048480000000")] + [InlineData("0484FFFFFFFF")] + [InlineData("0484FFFFFFFA")] + [InlineData("0485FF00000000")] + public static void InvalidLengthSpecified(string hexInput) + { + byte[] bytes = hexInput.HexToByteArray(); + DerSequenceReader reader = DerSequenceReader.CreateForPayload(bytes); + + // Doesn't throw. + reader.PeekTag(); + + // Since EatTag will have succeeded the reader needs to be reconstructed after each test. + Assert.Throws(() => reader.SkipValue()); + reader = DerSequenceReader.CreateForPayload(bytes); + + Assert.Throws(() => reader.ReadOctetString()); + reader = DerSequenceReader.CreateForPayload(bytes); + + Assert.Throws(() => reader.ReadNextEncodedValue()); + } + + [Fact] + public static void InteriorLengthTooLong() + { + byte[] bytes = + { + // CONSTRUCTED SEQUENCE (8 bytes) + 0x30, 0x08, + + // CONSTRUCTED SEQUENCE (2 bytes) + 0x30, 0x02, + + // OCTET STRING (0 bytes) + 0x04, 0x00, + + // OCTET STRING (after the inner sequence, 3 bytes, but that exceeds the sequence bounds) + 0x04, 0x03, 0x01, 0x02, 0x03 + }; + + DerSequenceReader reader = new DerSequenceReader(bytes); + DerSequenceReader nested = reader.ReadSequence(); + Assert.Equal(0, nested.ReadOctetString().Length); + Assert.False(nested.HasData); + Assert.Throws(() => reader.ReadOctetString()); + } + + [Fact] + public static void InteriorLengthTooLong_Nested() + { + byte[] bytes = + { + // CONSTRUCTED SEQUENCE (9 bytes) + 0x30, 0x09, + + // CONSTRUCTED SEQUENCE (2 bytes) + 0x30, 0x02, + + // OCTET STRING (1 byte, but 0 remain for the inner sequence) + 0x04, 0x01, + + // OCTET STRING (in the outer sequence, after the inner sequence, 3 bytes) + 0x04, 0x03, 0x01, 0x02, 0x03 + }; + + DerSequenceReader reader = new DerSequenceReader(bytes); + DerSequenceReader nested = reader.ReadSequence(); + Assert.Throws(() => nested.ReadOctetString()); + } + + [Fact] + public static void LengthTooLong_ForBounds() + { + byte[] bytes = + { + // CONSTRUCTED SEQUENCE (9 bytes) + 0x30, 0x09, + + // CONSTRUCTED SEQUENCE (2 bytes) + 0x30, 0x02, + + // OCTET STRING (0 bytes) + 0x04, 0x00, + + // OCTET STRING (after the inner sequence, 3 bytes) + 0x04, 0x03, 0x01, 0x02, 0x03 + }; + + Assert.Throws(() => new DerSequenceReader(bytes, 0, bytes.Length - 1)); + } } } diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Resources/Strings.resx b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Resources/Strings.resx index 59c5bc4f97..f0515f179c 100644 --- a/external/corefx/src/System.Security.Cryptography.Encoding/tests/Resources/Strings.resx +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/Resources/Strings.resx @@ -61,10 +61,91 @@ The OID value was invalid. + + ASN.1 Enumerated values only apply to enum types without the [Flags] attribute. + + + Named bit list operations require an enum with the [Flags] attribute. + + + The encoded named bit list value is larger than the value size of the '{0}' enum. + + + Tags with TagClass Universal must have the appropriate TagValue value for the data type being read or written. + + + Unused bit count must be between 0 and 7, inclusive. + + + Field '{0}' of type '{1}' has ambiguous type '{2}', an attribute derived from AsnTypeAttribute is required. + + + [Choice].AllowNull=true is not valid because type '{0}' cannot have a null value. + + + The tag ({0} {1}) for field '{2}' on type '{3}' already is associated in this context with field '{4}' on type '{5}'. + + + Field '{0}' on [Choice] type '{1}' has a default value, which is not permitted. + + + An instance of [Choice] type '{0}' has no non-null fields. + + + Field '{0}' on [Choice] type '{1}' can not be assigned a null value. + + + Fields '{0}' and '{1}' on type '{2}' are both non-null when only one value is permitted. + + + Field '{0}' on [Choice] type '{1}' has introduced a type chain cycle. + + + Field '{0}' on type '{1}' has multiple attributes deriving from '{2}' when at most one is permitted. + + + Type '{0}' cannot be serialized or deserialized because it is an array of arrays. + + + Type '{0}' cannot be serialized or deserialized because it is a multi-dimensional array. + + + Type '{0}' cannot be serialized or deserialized because it is not sealed or has unbound generic parameters. + + + Field '{0}' on type '{1}' is declared [OptionalValue], but it can not be assigned a null value. + + + Field '{0}' on type '{1}' has [ObjectIdentifier].PopulateFriendlyName set to true, which is not applicable to a string. Change the field to '{2}' or set PopulateFriendlyName to false. + + + Unable to set field {0} on type {1}. + + + Field '{0}' on type '{1}' has specified an implicit tag value via [ExpectedTag] for [Choice] type '{2}'. ExplicitTag must be true, or the [ExpectedTag] attribute removed. + + + Field '{0}' of type '{1}' has an effective type of '{2}' when one of ({3}) was expected. + + + Field '{0}' on type '{1}' has a [UtcTime] TwoDigitYearMax value ({2}) smaller than the minimum (99). + + + Could not determine how to serialize or deserialize type '{0}'. + + + Encode cannot be called while a Sequence or SetOf is still open. + + + Cannot pop the requested tag as it is not currently in progress. + ASN1 corrupted data. The string contains a character not in the 7 bit ASCII character set. + + The input to WriteEncodedValue must represent a single encoded value with no trailing data. + diff --git a/external/corefx/src/System.Security.Cryptography.Encoding/tests/System.Security.Cryptography.Encoding.Tests.csproj b/external/corefx/src/System.Security.Cryptography.Encoding/tests/System.Security.Cryptography.Encoding.Tests.csproj index 8026270d7c..e8d377c7e7 100644 --- a/external/corefx/src/System.Security.Cryptography.Encoding/tests/System.Security.Cryptography.Encoding.Tests.csproj +++ b/external/corefx/src/System.Security.Cryptography.Encoding/tests/System.Security.Cryptography.Encoding.Tests.csproj @@ -10,6 +10,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -17,6 +59,18 @@ + + Common\System\Security\Cryptography\Asn1V2.cs + + + Common\System\Security\Cryptography\Asn1V2.Serializer.cs + + + Common\System\Security\Cryptography\AsnReader.cs + + + Common\System\Security\Cryptography\AsnWriter.cs + Common\System\Security\Cryptography\DerEncoder.cs diff --git a/external/corefx/src/System.Security.Cryptography.OpenSsl/pkg/System.Security.Cryptography.OpenSsl.pkgproj b/external/corefx/src/System.Security.Cryptography.OpenSsl/pkg/System.Security.Cryptography.OpenSsl.pkgproj index d63c390a11..9908aeeb64 100644 --- a/external/corefx/src/System.Security.Cryptography.OpenSsl/pkg/System.Security.Cryptography.OpenSsl.pkgproj +++ b/external/corefx/src/System.Security.Cryptography.OpenSsl/pkg/System.Security.Cryptography.OpenSsl.pkgproj @@ -3,7 +3,8 @@ - net461;netcoreapp2.1;$(AllXamarinFrameworks) + + netcoreapp2.1 diff --git a/external/corefx/src/System.Security.Cryptography.OpenSsl/ref/System.Security.Cryptography.OpenSsl.csproj b/external/corefx/src/System.Security.Cryptography.OpenSsl/ref/System.Security.Cryptography.OpenSsl.csproj index 908595f05a..d0e1c9d8d1 100644 --- a/external/corefx/src/System.Security.Cryptography.OpenSsl/ref/System.Security.Cryptography.OpenSsl.csproj +++ b/external/corefx/src/System.Security.Cryptography.OpenSsl/ref/System.Security.Cryptography.OpenSsl.csproj @@ -3,9 +3,12 @@ {8DEA82EF-2214-4295-8CC1-9FFB9B18838F} - - - $(DefineConstants);netcoreapp + $(DefineConstants);netcoreapp + + 4.1.0.0 diff --git a/external/corefx/src/System.Security.Cryptography.OpenSsl/src/Configurations.props b/external/corefx/src/System.Security.Cryptography.OpenSsl/src/Configurations.props index 4b47a491b6..71cac7fa93 100644 --- a/external/corefx/src/System.Security.Cryptography.OpenSsl/src/Configurations.props +++ b/external/corefx/src/System.Security.Cryptography.OpenSsl/src/Configurations.props @@ -3,6 +3,7 @@ netcoreapp-Unix; + netcoreapp; netstandard; diff --git a/external/corefx/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj b/external/corefx/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj index dc98a3b03c..736a7b7b9d 100644 --- a/external/corefx/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj +++ b/external/corefx/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj @@ -6,10 +6,11 @@ Library System.Security.Cryptography.OpenSsl true + 4.1.0.0 - + SR.PlatformNotSupported_CryptographyOpenSSL - @@ -20,6 +21,8 @@ + + @@ -118,9 +121,9 @@ - + diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/System.Security.Cryptography.Pkcs.sln b/external/corefx/src/System.Security.Cryptography.Pkcs/System.Security.Cryptography.Pkcs.sln index f0d6bdedec..e50253a361 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/System.Security.Cryptography.Pkcs.sln +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/System.Security.Cryptography.Pkcs.sln @@ -30,14 +30,14 @@ Global {2DD8DFFA-09FF-46C6-8313-4A9CC1849A44}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU {2DD8DFFA-09FF-46C6-8313-4A9CC1849A44}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU {2DD8DFFA-09FF-46C6-8313-4A9CC1849A44}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU - {03D84CBD-896D-4B2F-9A22-07034F51E73D}.Debug|Any CPU.ActiveCfg = netstandard-Windows_NT-Debug|Any CPU - {03D84CBD-896D-4B2F-9A22-07034F51E73D}.Debug|Any CPU.Build.0 = netstandard-Windows_NT-Debug|Any CPU - {03D84CBD-896D-4B2F-9A22-07034F51E73D}.Release|Any CPU.ActiveCfg = netstandard-Windows_NT-Release|Any CPU - {03D84CBD-896D-4B2F-9A22-07034F51E73D}.Release|Any CPU.Build.0 = netstandard-Windows_NT-Release|Any CPU - {881269F5-9F22-4427-8DC5-63E2C05875BA}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU - {881269F5-9F22-4427-8DC5-63E2C05875BA}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU - {881269F5-9F22-4427-8DC5-63E2C05875BA}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU - {881269F5-9F22-4427-8DC5-63E2C05875BA}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {03D84CBD-896D-4B2F-9A22-07034F51E73D}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {03D84CBD-896D-4B2F-9A22-07034F51E73D}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {03D84CBD-896D-4B2F-9A22-07034F51E73D}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {03D84CBD-896D-4B2F-9A22-07034F51E73D}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU + {881269F5-9F22-4427-8DC5-63E2C05875BA}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {881269F5-9F22-4427-8DC5-63E2C05875BA}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {881269F5-9F22-4427-8DC5-63E2C05875BA}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {881269F5-9F22-4427-8DC5-63E2C05875BA}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/pkg/System.Security.Cryptography.Pkcs.pkgproj b/external/corefx/src/System.Security.Cryptography.Pkcs/pkg/System.Security.Cryptography.Pkcs.pkgproj index 67f27b2e29..688cc3f92a 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/pkg/System.Security.Cryptography.Pkcs.pkgproj +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/pkg/System.Security.Cryptography.Pkcs.pkgproj @@ -3,12 +3,11 @@ - net461;netcoreapp2.0;$(AllXamarinFrameworks) + net461;netcoreapp2.1;$(AllXamarinFrameworks) - - \ No newline at end of file + diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/ref/Configurations.props b/external/corefx/src/System.Security.Cryptography.Pkcs/ref/Configurations.props index d9777d8275..46bc45833f 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/ref/Configurations.props +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/ref/Configurations.props @@ -3,7 +3,8 @@ netstandard; + netcoreapp; netfx; - \ No newline at end of file + diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.cs index 285e860244..a6f9682cd6 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.cs +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.cs @@ -80,6 +80,22 @@ namespace System.Security.Cryptography.Pkcs public bool MoveNext() { throw null; } public void Reset() { } } + public sealed partial class CmsSigner + { + public CmsSigner() => throw null; + public CmsSigner(SubjectIdentifierType signerIdentifierType) => throw null; + public CmsSigner(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) => throw null; + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + public CmsSigner(CspParameters parameters) => throw null; + public CmsSigner(SubjectIdentifierType signerIdentifierType, System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) => throw null; + public SubjectIdentifierType SignerIdentifierType { get => throw null; set => throw null; } + public System.Security.Cryptography.X509Certificates.X509Certificate2 Certificate { get => throw null; set => throw null; } + public Oid DigestAlgorithm { get => throw null; set => throw null; } + public CryptographicAttributeObjectCollection SignedAttributes { get => throw null; set => throw null; } + public CryptographicAttributeObjectCollection UnsignedAttributes { get => throw null; set => throw null; } + public System.Security.Cryptography.X509Certificates.X509Certificate2Collection Certificates { get => throw null; set => throw null; } + public System.Security.Cryptography.X509Certificates.X509IncludeOption IncludeOption { get => throw null; set => throw null; } + } public sealed partial class ContentInfo { public ContentInfo(byte[] content) { } @@ -213,6 +229,70 @@ namespace System.Security.Cryptography.Pkcs KeyTransport = 1, Unknown = 0, } + public sealed partial class SignedCms + { + public SignedCms() => throw null; + public SignedCms(SubjectIdentifierType signerIdentifierType) => throw null; + public SignedCms(ContentInfo contentInfo) => throw null; + public SignedCms(SubjectIdentifierType signerIdentifierType, ContentInfo contentInfo) => throw null; + public SignedCms(ContentInfo contentInfo, bool detached) => throw null; + public SignedCms(SubjectIdentifierType signerIdentifierType, ContentInfo contentInfo, bool detached) => throw null; + public int Version => throw null; + public ContentInfo ContentInfo => throw null; + public bool Detached => throw null; + public System.Security.Cryptography.X509Certificates.X509Certificate2Collection Certificates => throw null; + public SignerInfoCollection SignerInfos => throw null; + public byte[] Encode() => throw null; + public void Decode(byte[] encodedMessage) => throw null; + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + public void ComputeSignature() => throw null; + public void ComputeSignature(CmsSigner signer) => throw null; + public void ComputeSignature(CmsSigner signer, bool silent) => throw null; + public void RemoveSignature(int index) => throw null; + public void RemoveSignature(SignerInfo signerInfo) => throw null; + public void CheckSignature(bool verifySignatureOnly) => throw null; + public void CheckSignature(System.Security.Cryptography.X509Certificates.X509Certificate2Collection extraStore, bool verifySignatureOnly) => throw null; + public void CheckHash() => throw null; + } + public sealed partial class SignerInfo + { + private SignerInfo() => throw null; + public int Version => throw null; + public System.Security.Cryptography.X509Certificates.X509Certificate2 Certificate => throw null; + public SubjectIdentifier SignerIdentifier => throw null; + public Oid DigestAlgorithm => throw null; + public CryptographicAttributeObjectCollection SignedAttributes => throw null; + public CryptographicAttributeObjectCollection UnsignedAttributes => throw null; + public SignerInfoCollection CounterSignerInfos => throw null; + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + public void ComputeCounterSignature() => throw null; + public void ComputeCounterSignature(CmsSigner signer) => throw null; + public void RemoveCounterSignature(int index) => throw null; + public void RemoveCounterSignature(SignerInfo counterSignerInfo) => throw null; + public void CheckSignature(bool verifySignatureOnly) => throw null; + public void CheckSignature(System.Security.Cryptography.X509Certificates.X509Certificate2Collection extraStore, bool verifySignatureOnly) => throw null; + public void CheckHash() => throw null; + } + public sealed partial class SignerInfoCollection : System.Collections.ICollection, System.Collections.IEnumerable + { + internal SignerInfoCollection() => throw null; + public SignerInfo this[int index] => throw null; + public int Count => throw null; + public SignerInfoEnumerator GetEnumerator() => throw null; + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null; + public void CopyTo(Array array, int index) => throw null; + public void CopyTo(SignerInfo[] array, int index) => throw null; + public bool IsSynchronized => throw null; + public object SyncRoot => throw null; + } + public sealed partial class SignerInfoEnumerator : System.Collections.IEnumerator + { + private SignerInfoEnumerator() { } + public SignerInfo Current => throw null; + object System.Collections.IEnumerator.Current => throw null; + public bool MoveNext() => throw null; + public void Reset() => throw null; + } public sealed partial class SubjectIdentifier { internal SubjectIdentifier() { } @@ -242,9 +322,9 @@ namespace System.Security.Cryptography.Pkcs } namespace System.Security.Cryptography.Xml { - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct X509IssuerSerial { + private object _dummy; public string IssuerName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public string SerialNumber { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } } diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.csproj b/external/corefx/src/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.csproj index 01da94672f..e5d055ae1f 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.csproj +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.csproj @@ -5,17 +5,26 @@ {881269F5-9F22-4427-8DC5-63E2C05875BA} true + + + + + + + + + - \ No newline at end of file + diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.netcoreapp.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.netcoreapp.cs new file mode 100644 index 0000000000..1d65c813f0 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.netcoreapp.cs @@ -0,0 +1,15 @@ +// 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. +// ------------------------------------------------------------------------------ +// Changes to this file must follow the http://aka.ms/api-review process. +// ------------------------------------------------------------------------------ + +namespace System.Security.Cryptography.Pkcs +{ + public sealed partial class SignerInfo + { + public Oid SignatureAlgorithm => throw null; + public byte[] GetSignature() => throw null; + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/Configurations.props b/external/corefx/src/System.Security.Cryptography.Pkcs/src/Configurations.props index 4e5de27086..5ff70d5ab9 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/src/Configurations.props +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/Configurations.props @@ -2,9 +2,11 @@ - netstandard-Windows_NT; netstandard; + netstandard-Windows_NT; + netcoreapp-Windows_NT; + netcoreapp; netfx-Windows_NT; - \ No newline at end of file + diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Helpers.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Helpers.cs index 1ad431a96a..c972345356 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Helpers.cs +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Helpers.cs @@ -3,10 +3,14 @@ // See the LICENSE file in the project root for more information. using System; +using System.Buffers; using System.Text; using System.Diagnostics; +using System.Runtime.InteropServices; using System.Security.Cryptography; +using System.Security.Cryptography.Asn1; using System.Security.Cryptography.Pkcs; +using System.Security.Cryptography.Pkcs.Asn1; using System.Security.Cryptography.X509Certificates; using X509IssuerSerial = System.Security.Cryptography.Xml.X509IssuerSerial; @@ -19,6 +23,39 @@ namespace Internal.Cryptography return (byte[])(a.Clone()); } +#if !netcoreapp + // Compatibility API. + internal static void AppendData(this IncrementalHash hasher, ReadOnlySpan data) + { + hasher.AppendData(data.ToArray()); + } +#endif + + internal static HashAlgorithmName GetDigestAlgorithm(Oid oid) + { + Debug.Assert(oid != null); + return GetDigestAlgorithm(oid.Value); + } + + internal static HashAlgorithmName GetDigestAlgorithm(string oidValue) + { + switch (oidValue) + { + case Oids.Md5: + return HashAlgorithmName.MD5; + case Oids.Sha1: + return HashAlgorithmName.SHA1; + case Oids.Sha256: + return HashAlgorithmName.SHA256; + case Oids.Sha384: + return HashAlgorithmName.SHA384; + case Oids.Sha512: + return HashAlgorithmName.SHA512; + default: + throw new CryptographicException(SR.Cryptography_UnknownHashAlgorithm, oidValue); + } + } + /// /// This is not just a convenience wrapper for Array.Resize(). In DEBUG builds, it forces the array to move in memory even if no resize is needed. This should be used by /// helper methods that do anything of the form "call a native api once to get the estimated size, call it again to get the data and return the data in a byte[] array." @@ -34,6 +71,54 @@ namespace Internal.Cryptography return a; } + public static void RemoveAt(ref T[] arr, int idx) + { + Debug.Assert(arr != null); + Debug.Assert(idx >= 0); + Debug.Assert(idx < arr.Length); + + if (arr.Length == 1) + { + arr = Array.Empty(); + return; + } + + T[] tmp = new T[arr.Length - 1]; + + if (idx != 0) + { + Array.Copy(arr, 0, tmp, 0, idx); + } + + if (idx < tmp.Length) + { + Array.Copy(arr, idx + 1, tmp, idx, tmp.Length - idx); + } + + arr = tmp; + } + + public static T[] NormalizeSet( + T[] setItems, + Action encodedValueProcessor=null) + { + AsnSet set = new AsnSet + { + SetData = setItems, + }; + + AsnWriter writer = AsnSerializer.Serialize(set, AsnEncodingRules.DER); + byte[] normalizedValue = writer.Encode(); + set = AsnSerializer.Deserialize>(normalizedValue, AsnEncodingRules.DER); + + if (encodedValueProcessor != null) + { + encodedValueProcessor(normalizedValue); + } + + return set.SetData; + } + public static CmsRecipientCollection DeepCopy(this CmsRecipientCollection recipients) { CmsRecipientCollection recipientsCopy = new CmsRecipientCollection(); @@ -149,9 +234,14 @@ namespace Internal.Cryptography return skiString.UpperHexStringToByteArray(); } + public static string ToSkiString(this ReadOnlySpan skiBytes) + { + return ToUpperHexString(skiBytes); + } + public static string ToSkiString(this byte[] skiBytes) { - return skiBytes.ToUpperHexString(); + return ToUpperHexString(skiBytes); } /// @@ -170,16 +260,18 @@ namespace Internal.Cryptography { serialBytes = serialBytes.CloneByteArray(); Array.Reverse(serialBytes); - return serialBytes.ToUpperHexString(); + return ToUpperHexString(serialBytes); } - private static string ToUpperHexString(this byte[] ba) + private static string ToUpperHexString(ReadOnlySpan ba) { StringBuilder sb = new StringBuilder(ba.Length * 2); - foreach (byte b in ba) + + for (int i = 0; i < ba.Length; i++) { - sb.Append(b.ToString("X2")); + sb.Append(ba[i].ToString("X2")); } + return sb.ToString(); } @@ -256,5 +348,122 @@ namespace Internal.Cryptography enhancedAttribute.CopyFrom(basicAttribute); return enhancedAttribute; } + + public static byte[] GetSubjectKeyIdentifier(this X509Certificate2 certificate) + { + Debug.Assert(certificate != null); + + X509Extension extension = certificate.Extensions[Oids.SubjectKeyIdentifier]; + + if (extension != null) + { + // Certificates are DER encoded. + AsnReader reader = new AsnReader(extension.RawData, AsnEncodingRules.DER); + + if (reader.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory contents)) + { + return contents.ToArray(); + } + + // TryGetPrimitiveOctetStringBytes will have thrown if the next tag wasn't + // Universal (primitive) OCTET STRING, since we're in DER mode. + // So there's really no way we can get here. + Debug.Fail($"TryGetPrimitiveOctetStringBytes returned false in DER mode"); + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + // The Desktop/Windows version of this method use CertGetCertificateContextProperty + // with a property ID of CERT_KEY_IDENTIFIER_PROP_ID. + // + // MSDN says that when there's no extension, this method takes the SHA-1 of the + // SubjectPublicKeyInfo block, and returns that. + // + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa376079%28v=vs.85%29.aspx + +#pragma warning disable CA5350 // SHA-1 is required for compat. + using (HashAlgorithm hash = SHA1.Create()) +#pragma warning restore CA5350 // Do not use insecure cryptographic algorithm SHA1. + { + ReadOnlyMemory publicKeyInfoBytes = GetSubjectPublicKeyInfo(certificate); + return hash.ComputeHash(publicKeyInfoBytes.ToArray()); + } + } + + internal static void DigestWriter(IncrementalHash hasher, AsnWriter writer) + { +#if netcoreapp + hasher.AppendData(writer.EncodeAsSpan()); +#else + hasher.AppendData(writer.Encode()); +#endif + } + + private static ReadOnlyMemory GetSubjectPublicKeyInfo(X509Certificate2 certificate) + { + var parsedCertificate = AsnSerializer.Deserialize(certificate.RawData, AsnEncodingRules.DER); + return parsedCertificate.TbsCertificate.SubjectPublicKeyInfo; + } + + [StructLayout(LayoutKind.Sequential)] + private struct Certificate + { + internal TbsCertificateLite TbsCertificate; + internal AlgorithmIdentifierAsn AlgorithmIdentifier; + [BitString] + internal ReadOnlyMemory SignatureValue; + } + + [StructLayout(LayoutKind.Sequential)] + private struct TbsCertificateLite + { + [ExpectedTag(0, ExplicitTag = true)] +#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant + [DefaultValue(0xA0, 0x03, 0x02, 0x01, 0x00)] +#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant + internal int Version; + + [Integer] + internal ReadOnlyMemory SerialNumber; + + internal AlgorithmIdentifierAsn AlgorithmIdentifier; + + [AnyValue] + [ExpectedTag(TagClass.Universal, (int)UniversalTagNumber.SequenceOf)] + internal ReadOnlyMemory Issuer; + + [AnyValue] + [ExpectedTag(TagClass.Universal, (int)UniversalTagNumber.Sequence)] + internal ReadOnlyMemory Validity; + + [AnyValue] + [ExpectedTag(TagClass.Universal, (int)UniversalTagNumber.SequenceOf)] + internal ReadOnlyMemory Subject; + + [AnyValue] + [ExpectedTag(TagClass.Universal, (int)UniversalTagNumber.Sequence)] + internal ReadOnlyMemory SubjectPublicKeyInfo; + + [ExpectedTag(1)] + [OptionalValue] + [BitString] + internal ReadOnlyMemory? IssuerUniqueId; + + [ExpectedTag(2)] + [OptionalValue] + [BitString] + internal ReadOnlyMemory? SubjectUniqueId; + + [OptionalValue] + [AnyValue] + [ExpectedTag(3)] + internal ReadOnlyMemory? Extensions; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct AsnSet + { + [SetOf] + public T[] SetData; + } } } diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Oids.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Oids.cs index 922f707b4d..0abf2df115 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Oids.cs +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Oids.cs @@ -15,6 +15,7 @@ namespace Internal.Cryptography // Asymmetric encryption algorithms public const string Rsa = "1.2.840.113549.1.1.1"; + public const string RsaPss = "1.2.840.113549.1.1.10"; public const string Esdh = "1.2.840.113549.1.9.16.3.5"; // Cryptographic Attribute Types @@ -22,6 +23,7 @@ namespace Internal.Cryptography public const string ContentType = "1.2.840.113549.1.9.3"; public const string DocumentDescription = "1.3.6.1.4.1.311.88.2.2"; public const string MessageDigest = "1.2.840.113549.1.9.4"; + public const string CounterSigner = "1.2.840.113549.1.9.6"; public const string DocumentName = "1.3.6.1.4.1.311.88.2.1"; // Key wrap algorithms @@ -35,5 +37,32 @@ namespace Internal.Cryptography public const string Pkcs7SignedEnveloped = "1.2.840.113549.1.7.4"; public const string Pkcs7Hashed = "1.2.840.113549.1.7.5"; public const string Pkcs7Encrypted = "1.2.840.113549.1.7.6"; + + public const string Md5 = "1.2.840.113549.2.5"; + public const string Sha1 = "1.3.14.3.2.26"; + public const string Sha256 = "2.16.840.1.101.3.4.2.1"; + public const string Sha384 = "2.16.840.1.101.3.4.2.2"; + public const string Sha512 = "2.16.840.1.101.3.4.2.3"; + + // DSA CMS uses the combined signature+digest OID + public const string DsaPublicKey = "1.2.840.10040.4.1"; + public const string DsaWithSha1 = "1.2.840.10040.4.3"; + public const string DsaWithSha256 = "2.16.840.1.101.3.4.3.2"; + public const string DsaWithSha384 = "2.16.840.1.101.3.4.3.3"; + public const string DsaWithSha512 = "2.16.840.1.101.3.4.3.4"; + + // ECDSA CMS uses the combined signature+digest OID + // https://tools.ietf.org/html/rfc5753#section-2.1.1 + public const string EcPublicKey = "1.2.840.10045.2.1"; + public const string ECDsaWithSha1 = "1.2.840.10045.4.1"; + public const string ECDsaWithSha256 = "1.2.840.10045.4.3.2"; + public const string ECDsaWithSha384 = "1.2.840.10045.4.3.3"; + public const string ECDsaWithSha512 = "1.2.840.10045.4.3.4"; + + public const string Mgf1 = "1.2.840.113549.1.1.8"; + + // Cert Extensions + public const string SubjectKeyIdentifier = "2.5.29.14"; + public const string KeyUsage = "2.5.29.15"; } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/PkcsPal.AnyOS.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/PkcsPal.AnyOS.cs new file mode 100644 index 0000000000..919b1280a7 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/PkcsPal.AnyOS.cs @@ -0,0 +1,221 @@ +// 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.Buffers; +using System.Diagnostics; +using System.Security.Cryptography; +using System.Security.Cryptography.Asn1; +using System.Security.Cryptography.Pkcs; +using System.Security.Cryptography.X509Certificates; + +namespace Internal.Cryptography +{ + internal abstract partial class PkcsPal + { + private static PkcsPal s_instance = new ManagedPkcsPal(); + + private class ManagedPkcsPal : PkcsPal + { + public override byte[] Encrypt( + CmsRecipientCollection recipients, + ContentInfo contentInfo, + AlgorithmIdentifier contentEncryptionAlgorithm, + X509Certificate2Collection originatorCerts, + CryptographicAttributeObjectCollection unprotectedAttributes) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyPkcs); + } + + public override DecryptorPal Decode( + byte[] encodedMessage, + out int version, + out ContentInfo contentInfo, + out AlgorithmIdentifier contentEncryptionAlgorithm, + out X509Certificate2Collection originatorCerts, + out CryptographicAttributeObjectCollection unprotectedAttributes) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyPkcs); + } + + public override byte[] EncodeOctetString(byte[] octets) + { + // Write using DER to support the most readers. + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + { + writer.WriteOctetString(octets); + return writer.Encode(); + } + } + + public override byte[] DecodeOctetString(byte[] encodedOctets) + { + // Read using BER because the CMS specification says the encoding is BER. + AsnReader reader = new AsnReader(encodedOctets, AsnEncodingRules.BER); + + const int ArbitraryStackLimit = 256; + Span tmp = stackalloc byte[ArbitraryStackLimit]; + // Use stackalloc 0 so data can later hold a slice of tmp. + ReadOnlySpan data = stackalloc byte[0]; + byte[] poolBytes = null; + + try + { + if (!reader.TryGetPrimitiveOctetStringBytes(out var contents)) + { + if (reader.TryCopyOctetStringBytes(tmp, out int bytesWritten)) + { + data = tmp.Slice(0, bytesWritten); + } + else + { + poolBytes = ArrayPool.Shared.Rent(reader.PeekContentBytes().Length); + + if (!reader.TryCopyOctetStringBytes(poolBytes, out bytesWritten)) + { + Debug.Fail("TryCopyOctetStringBytes failed with a provably-large-enough buffer"); + throw new CryptographicException(); + } + + data = new ReadOnlySpan(poolBytes, 0, bytesWritten); + } + } + else + { + data = contents.Span; + } + + if (reader.HasData) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + return data.ToArray(); + } + finally + { + if (poolBytes != null) + { + Array.Clear(poolBytes, 0, data.Length); + ArrayPool.Shared.Return(poolBytes); + } + } + } + + public override byte[] EncodeUtcTime(DateTime utcTime) + { + // Write using DER to support the most readers. + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + { + // Sending the DateTime through ToLocalTime here will cause the right normalization + // of DateTimeKind.Unknown. + // + // Local => Local (noop) => UTC (in WriteUtcTime) (adjust, correct) + // UTC => Local (adjust) => UTC (adjust back, correct) + // Unknown => Local (adjust) => UTC (adjust "back", add Z marker; matches Windows) + writer.WriteUtcTime(utcTime.ToLocalTime()); + return writer.Encode(); + } + } + + public override DateTime DecodeUtcTime(byte[] encodedUtcTime) + { + // Read using BER because the CMS specification says the encoding is BER. + AsnReader reader = new AsnReader(encodedUtcTime, AsnEncodingRules.BER); + DateTimeOffset value = reader.GetUtcTime(); + + if (reader.HasData) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + return value.UtcDateTime; + } + + public override string DecodeOid(byte[] encodedOid) + { + Span emptyInvalidOid = stackalloc byte[2]; + emptyInvalidOid[0] = 0x06; + emptyInvalidOid[1] = 0x00; + + // Windows compat. + if (emptyInvalidOid.SequenceEqual(encodedOid)) + { + return string.Empty; + } + + // Read using BER because the CMS specification says the encoding is BER. + AsnReader reader = new AsnReader(encodedOid, AsnEncodingRules.BER); + string value = reader.ReadObjectIdentifierAsString(); + + if (reader.HasData) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + return value; + } + + public override Oid GetEncodedMessageType(byte[] encodedMessage) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyPkcs); + } + + public override void AddCertsFromStoreForDecryption(X509Certificate2Collection certs) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyPkcs); + } + + public override Exception CreateRecipientsNotFoundException() + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyPkcs); + } + + public override Exception CreateRecipientInfosAfterEncryptException() + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyPkcs); + } + + public override Exception CreateDecryptAfterEncryptException() + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyPkcs); + } + + public override Exception CreateDecryptTwiceException() + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyPkcs); + } + + public override byte[] GetSubjectKeyIdentifier(X509Certificate2 certificate) + { + return certificate.GetSubjectKeyIdentifier(); + } + + public override T GetPrivateKeyForSigning(X509Certificate2 certificate, bool silent) + { + return GetPrivateKey(certificate); + } + + public override T GetPrivateKeyForDecryption(X509Certificate2 certificate, bool silent) + { + return GetPrivateKey(certificate); + } + + private T GetPrivateKey(X509Certificate2 certificate) where T : AsymmetricAlgorithm + { + if (typeof(T) == typeof(RSA)) + return (T)(object)certificate.GetRSAPrivateKey(); + if (typeof(T) == typeof(ECDsa)) + return (T)(object)certificate.GetECDsaPrivateKey(); +#if netcoreapp + if (typeof(T) == typeof(DSA)) + return (T)(object)certificate.GetDSAPrivateKey(); +#endif + + Debug.Fail($"Unknown key type requested: {typeof(T).FullName}"); + return null; + } + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/DecryptorPalWindows.Decrypt.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/DecryptorPalWindows.Decrypt.cs index 51595ed831..efbd3936c4 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/DecryptorPalWindows.Decrypt.cs +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/DecryptorPalWindows.Decrypt.cs @@ -30,7 +30,12 @@ namespace Internal.Cryptography.Pal.Windows return null; // Desktop compat: We pass false for "silent" here (thus allowing crypto providers to display UI.) - using (SafeProvOrNCryptKeyHandle hKey = TryGetCertificatePrivateKey(cert, false, out exception)) + const bool Silent = false; + // Note: Using CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG rather than CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG + // because wrapping an NCrypt wrapper over CAPI keys unconditionally causes some legacy features + // (such as RC4 support) to break. + const bool PreferNCrypt = false; + using (SafeProvOrNCryptKeyHandle hKey = PkcsPalWindows.GetCertificatePrivateKey(cert, Silent, PreferNCrypt, out _, out exception)) { if (hKey == null) return null; @@ -104,69 +109,6 @@ namespace Internal.Cryptography.Pal.Windows } } - private static SafeProvOrNCryptKeyHandle TryGetCertificatePrivateKey(X509Certificate2 cert, bool silent, out Exception exception) - { - CryptAcquireCertificatePrivateKeyFlags flags = - CryptAcquireCertificatePrivateKeyFlags.CRYPT_ACQUIRE_USE_PROV_INFO_FLAG - | CryptAcquireCertificatePrivateKeyFlags.CRYPT_ACQUIRE_COMPARE_KEY_FLAG - // Note: Using CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG rather than CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG because wrapping an NCrypt wrapper over CAPI keys unconditionally - // causes some legacy features (such as RC4 support) to break. - | CryptAcquireCertificatePrivateKeyFlags.CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG; - if (silent) - { - flags |= CryptAcquireCertificatePrivateKeyFlags.CRYPT_ACQUIRE_SILENT_FLAG; - } - - bool mustFree; - using (SafeCertContextHandle hCertContext = cert.CreateCertContextHandle()) - { - IntPtr hKey; - int cbSize = IntPtr.Size; - - if (Interop.Crypt32.CertGetCertificateContextProperty( - hCertContext, - CertContextPropId.CERT_NCRYPT_KEY_HANDLE_PROP_ID, - out hKey, - ref cbSize)) - { - exception = null; - return new SafeProvOrNCryptKeyHandleUwp(hKey, hCertContext); - } - - CryptKeySpec keySpec; - - if (!Interop.Crypt32.CryptAcquireCertificatePrivateKey(hCertContext, flags, IntPtr.Zero, out hKey, out keySpec, out mustFree)) - { - exception = Marshal.GetHRForLastWin32Error().ToCryptographicException(); - return null; - } - - // We need to know whether we got back a CRYPTPROV or NCrypt handle. Unfortunately, NCryptIsKeyHandle() is a prohibited api on UWP. - // Fortunately, CryptAcquireCertificatePrivateKey() is documented to tell us which one we got through the keySpec value. - bool isNCrypt; - switch (keySpec) - { - case CryptKeySpec.AT_KEYEXCHANGE: - case CryptKeySpec.AT_SIGNATURE: - isNCrypt = false; - break; - - case CryptKeySpec.CERT_NCRYPT_KEY_SPEC: - isNCrypt = true; - break; - - default: - // As of this writing, we've exhausted all the known values of keySpec. We have no idea what kind of key handle we got so - // play it safe and fail fast. - throw new NotSupportedException(SR.Format(SR.Cryptography_Cms_UnknownKeySpec, keySpec)); - } - - SafeProvOrNCryptKeyHandleUwp hProvOrNCryptKey = new SafeProvOrNCryptKeyHandleUwp(hKey, ownsHandle: mustFree, isNcrypt: isNCrypt); - exception = null; - return hProvOrNCryptKey; - } - } - private Exception TryDecryptTrans(KeyTransRecipientInfo recipientInfo, SafeProvOrNCryptKeyHandle hKey, CryptKeySpec keySpec) { KeyTransRecipientInfoPalWindows pal = (KeyTransRecipientInfoPalWindows)(recipientInfo.Pal); diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/HelpersWindows.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/HelpersWindows.cs index bd4f2e8ebe..4dc1252a8f 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/HelpersWindows.cs +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/HelpersWindows.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System; +using System.Buffers; +using System.Buffers.Binary; using System.Diagnostics; using System.Text; using System.Runtime.InteropServices; @@ -13,6 +15,7 @@ using X509IssuerSerial = System.Security.Cryptography.Xml.X509IssuerSerial; using Microsoft.Win32.SafeHandles; +using CryptProvParam=Interop.Advapi32.CryptProvParam; using static Interop.Crypt32; namespace Internal.Cryptography.Pal.Windows @@ -326,6 +329,124 @@ namespace Internal.Cryptography.Pal.Windows } } + public static CspParameters GetProvParameters(this SafeProvOrNCryptKeyHandle handle) + { + // A normal key container name is a GUID (~34 bytes ASCII) + // The longest standard provider name is 64 bytes (including the \0), + // but we shouldn't have a CAPI call with a software CSP. + // + // In debug builds use a buffer which will need to be resized, but is big + // enough to hold the DWORD "can't fail" values. + Span stackSpan = stackalloc byte[ +#if DEBUG + sizeof(int) +#else + 64 +#endif + ]; + + stackSpan.Clear(); + int size = stackSpan.Length; + + if (!Interop.Advapi32.CryptGetProvParam(handle, CryptProvParam.PP_PROVTYPE, stackSpan, ref size)) + { + throw Marshal.GetLastWin32Error().ToCryptographicException(); + } + + if (size != sizeof(int)) + { + Debug.Fail("PP_PROVTYPE writes a DWORD - enum misalignment?"); + throw new CryptographicException(); + } + + int provType = BinaryPrimitives.ReadMachineEndian(stackSpan.Slice(0, size)); + + size = stackSpan.Length; + if (!Interop.Advapi32.CryptGetProvParam(handle, CryptProvParam.PP_KEYSET_TYPE, stackSpan, ref size)) + { + throw Marshal.GetLastWin32Error().ToCryptographicException(); + } + + if (size != sizeof(int)) + { + Debug.Fail("PP_KEYSET_TYPE writes a DWORD - enum misalignment?"); + throw new CryptographicException(); + } + + int keysetType = BinaryPrimitives.ReadMachineEndian(stackSpan.Slice(0, size)); + + // Only CRYPT_MACHINE_KEYSET is described as coming back, but be defensive. + CspProviderFlags provFlags = + ((CspProviderFlags)keysetType & CspProviderFlags.UseMachineKeyStore) | + CspProviderFlags.UseExistingKey; + + byte[] rented = null; + Span asciiStringBuf = stackSpan; + + string provName = GetStringProvParam(handle, CryptProvParam.PP_NAME, ref asciiStringBuf, ref rented, 0); + int maxClear = provName.Length; + string keyName = GetStringProvParam(handle, CryptProvParam.PP_CONTAINER, ref asciiStringBuf, ref rented, maxClear); + maxClear = Math.Max(maxClear, keyName.Length); + + if (rented != null) + { + Array.Clear(rented, 0, maxClear); + ArrayPool.Shared.Return(rented); + } + + return new CspParameters(provType) + { + Flags = provFlags, + KeyContainerName = keyName, + ProviderName = provName, + }; + } + + private static string GetStringProvParam( + SafeProvOrNCryptKeyHandle handle, + CryptProvParam dwParam, + ref Span buf, + ref byte[] rented, + int clearLen) + { + int len = buf.Length; + + if (!Interop.Advapi32.CryptGetProvParam(handle, dwParam, buf, ref len)) + { + if (len > buf.Length) + { + ArrayPool pool = ArrayPool.Shared; + + if (rented != null) + { + Array.Clear(rented, 0, clearLen); + pool.Return(rented); + } + + rented = pool.Rent(len); + buf = rented; + len = rented.Length; + } + else + { + throw Marshal.GetLastWin32Error().ToCryptographicException(); + } + + if (!Interop.Advapi32.CryptGetProvParam(handle, dwParam, buf, ref len)) + { + throw Marshal.GetLastWin32Error().ToCryptographicException(); + } + } + + unsafe + { + fixed (byte* asciiPtr = &MemoryMarshal.GetReference(buf)) + { + return Marshal.PtrToStringAnsi((IntPtr)asciiPtr, len); + } + } + } + private static unsafe CryptographicAttributeObjectCollection ToCryptographicAttributeObjectCollection(CRYPT_ATTRIBUTES* pCryptAttributes) { CryptographicAttributeObjectCollection collection = new CryptographicAttributeObjectCollection(); diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/PkcsPalWindows.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/PkcsPalWindows.cs index b8b47ac473..6b62a3ab46 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/PkcsPalWindows.cs +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/PkcsPalWindows.cs @@ -6,6 +6,7 @@ using System; using System.Text; using System.Diagnostics; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; using System.Security.Cryptography; using System.Security.Cryptography.Pkcs; using System.Security.Cryptography.X509Certificates; @@ -165,5 +166,169 @@ namespace Internal.Cryptography.Pal.Windows return ski; } } + + public override T GetPrivateKeyForSigning(X509Certificate2 certificate, bool silent) + { + return GetPrivateKey(certificate, silent, preferNCrypt: true); + } + + public override T GetPrivateKeyForDecryption(X509Certificate2 certificate, bool silent) + { + return GetPrivateKey(certificate, silent, preferNCrypt: false); + } + + private T GetPrivateKey(X509Certificate2 certificate, bool silent, bool preferNCrypt) where T : AsymmetricAlgorithm + { + if (!certificate.HasPrivateKey) + { + return null; + } + + SafeProvOrNCryptKeyHandle handle = GetCertificatePrivateKey( + certificate, + silent, + preferNCrypt, + out CryptKeySpec keySpec, + out Exception exception); + + using (handle) + { + if (handle == null || handle.IsInvalid) + { + if (exception != null) + { + throw exception; + } + + return null; + } + + if (keySpec == CryptKeySpec.CERT_NCRYPT_KEY_SPEC) + { + using (SafeNCryptKeyHandle keyHandle = new SafeNCryptKeyHandle(handle.DangerousGetHandle(), handle)) + using (CngKey cngKey = CngKey.Open(keyHandle, CngKeyHandleOpenOptions.None)) + { + if (typeof(T) == typeof(RSA)) + return (T)(object)new RSACng(cngKey); + if (typeof(T) == typeof(ECDsa)) + return (T)(object)new ECDsaCng(cngKey); + if (typeof(T) == typeof(DSA)) + return (T)(object)new DSACng(cngKey); + + Debug.Fail($"Unknown CNG key type request: {typeof(T).FullName}"); + return null; + } + } + + // The key handle is for CAPI. + // Our CAPI types don't allow usage from a handle, so we have a few choices: + // 1) Extract the information we need to re-open the key handle. + // 2) Re-implement {R|D}SACryptoServiceProvider + // 3) PNSE. + // 4) Defer to cert.Get{R|D}SAPrivateKey if not silent, throw otherwise. + CspParameters cspParams = handle.GetProvParameters(); + Debug.Assert((cspParams.Flags & CspProviderFlags.UseExistingKey) != 0); + cspParams.KeyNumber = (int)keySpec; + + if (silent) + { + cspParams.Flags |= CspProviderFlags.NoPrompt; + } + + if (typeof(T) == typeof(RSA)) + return (T)(object)new RSACryptoServiceProvider(cspParams); + if (typeof(T) == typeof(DSA)) + return (T)(object)new DSACryptoServiceProvider(cspParams); + + Debug.Fail($"Unknown CAPI key type request: {typeof(T).FullName}"); + return null; + } + } + + internal static SafeProvOrNCryptKeyHandle GetCertificatePrivateKey( + X509Certificate2 cert, + bool silent, + bool preferNCrypt, + out CryptKeySpec keySpec, + out Exception exception) + { + CryptAcquireCertificatePrivateKeyFlags flags = + CryptAcquireCertificatePrivateKeyFlags.CRYPT_ACQUIRE_USE_PROV_INFO_FLAG + | CryptAcquireCertificatePrivateKeyFlags.CRYPT_ACQUIRE_COMPARE_KEY_FLAG; + + if (preferNCrypt) + { + flags |= CryptAcquireCertificatePrivateKeyFlags.CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG; + } + else + { + flags |= CryptAcquireCertificatePrivateKeyFlags.CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG; + } + + if (silent) + { + flags |= CryptAcquireCertificatePrivateKeyFlags.CRYPT_ACQUIRE_SILENT_FLAG; + } + + bool isNCrypt; + bool mustFree; + using (SafeCertContextHandle hCertContext = cert.CreateCertContextHandle()) + { + IntPtr hKey; + int cbSize = IntPtr.Size; + + if (Interop.Crypt32.CertGetCertificateContextProperty( + hCertContext, + CertContextPropId.CERT_NCRYPT_KEY_HANDLE_PROP_ID, + out hKey, + ref cbSize)) + { + exception = null; + keySpec = CryptKeySpec.CERT_NCRYPT_KEY_SPEC; + return new SafeProvOrNCryptKeyHandleUwp(hKey, hCertContext); + } + + if (!Interop.Crypt32.CryptAcquireCertificatePrivateKey( + hCertContext, + flags, + IntPtr.Zero, + out hKey, + out keySpec, + out mustFree)) + { + exception = Marshal.GetHRForLastWin32Error().ToCryptographicException(); + return null; + } + + // We need to know whether we got back a CRYPTPROV or NCrypt handle. + // Unfortunately, NCryptIsKeyHandle() is a prohibited api on UWP. + // Fortunately, CryptAcquireCertificatePrivateKey() is documented to tell us which + // one we got through the keySpec value. + switch (keySpec) + { + case CryptKeySpec.AT_KEYEXCHANGE: + case CryptKeySpec.AT_SIGNATURE: + isNCrypt = false; + break; + + case CryptKeySpec.CERT_NCRYPT_KEY_SPEC: + isNCrypt = true; + break; + + default: + // As of this writing, we've exhausted all the known values of keySpec. + // We have no idea what kind of key handle we got so play it safe and fail fast. + throw new NotSupportedException(SR.Format(SR.Cryptography_Cms_UnknownKeySpec, keySpec)); + } + + SafeProvOrNCryptKeyHandleUwp hProvOrNCryptKey = new SafeProvOrNCryptKeyHandleUwp( + hKey, + ownsHandle: mustFree, + isNcrypt: isNCrypt); + + exception = null; + return hProvOrNCryptKey; + } + } } } diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/PkcsPal.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/PkcsPal.cs index 069d1ba79a..b05971a0ff 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/PkcsPal.cs +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/PkcsPal.cs @@ -80,6 +80,16 @@ namespace Internal.Cryptography /// public abstract byte[] GetSubjectKeyIdentifier(X509Certificate2 certificate); + /// + /// Retrieve a private key object for the certificate to use with signing. + /// + public abstract T GetPrivateKeyForSigning(X509Certificate2 certificate, bool silent) where T : AsymmetricAlgorithm; + + /// + /// Retrieve a private key object for the certificate to use with decryption. + /// + public abstract T GetPrivateKeyForDecryption(X509Certificate2 certificate, bool silent) where T : AsymmetricAlgorithm; + /// /// Get the one (and only) instance of PkcsPal. /// diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/Resources/Strings.resx b/external/corefx/src/System.Security.Cryptography.Pkcs/src/Resources/Strings.resx index 1423017d91..a755844916 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/src/Resources/Strings.resx +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/Resources/Strings.resx @@ -67,33 +67,183 @@ Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection. + + The OID value was invalid. + Index was out of range. Must be non-negative and less than the size of the collection. + + ASN.1 Enumerated values only apply to enum types without the [Flags] attribute. + + + Named bit list operations require an enum with the [Flags] attribute. + + + The encoded named bit list value is larger than the value size of the '{0}' enum. + + + Tags with TagClass Universal must have the appropriate TagValue value for the data type being read or written. + + + Unused bit count must be between 0 and 7, inclusive. + + + Field '{0}' of type '{1}' has ambiguous type '{2}', an attribute derived from AsnTypeAttribute is required. + + + [Choice].AllowNull=true is not valid because type '{0}' cannot have a null value. + + + The tag ({0} {1}) for field '{2}' on type '{3}' already is associated in this context with field '{4}' on type '{5}'. + + + Field '{0}' on [Choice] type '{1}' has a default value, which is not permitted. + + + An instance of [Choice] type '{0}' has no non-null fields. + + + Field '{0}' on [Choice] type '{1}' can not be assigned a null value. + + + Fields '{0}' and '{1}' on type '{2}' are both non-null when only one value is permitted. + + + Field '{0}' on [Choice] type '{1}' has introduced a type chain cycle. + + + Field '{0}' on type '{1}' has multiple attributes deriving from '{2}' when at most one is permitted. + + + Type '{0}' cannot be serialized or deserialized because it is an array of arrays. + + + Type '{0}' cannot be serialized or deserialized because it is a multi-dimensional array. + + + Type '{0}' cannot be serialized or deserialized because it is not sealed or has unbound generic parameters. + + + Field '{0}' on type '{1}' is declared [OptionalValue], but it can not be assigned a null value. + + + Field '{0}' on type '{1}' has [ObjectIdentifier].PopulateFriendlyName set to true, which is not applicable to a string. Change the field to '{2}' or set PopulateFriendlyName to false. + + + Unable to set field {0} on type {1}. + + + Field '{0}' on type '{1}' has specified an implicit tag value via [ExpectedTag] for [Choice] type '{2}'. ExplicitTag must be true, or the [ExpectedTag] attribute removed. + + + Field '{0}' of type '{1}' has an effective type of '{2}' when one of ({3}) was expected. + + + Field '{0}' on type '{1}' has a [UtcTime] TwoDigitYearMax value ({2}) smaller than the minimum (99). + + + Could not determine how to serialize or deserialize type '{0}'. + + + Encode cannot be called while a Sequence or SetOf is still open. + + + Cannot pop the requested tag as it is not currently in progress. + + + The hash value is not correct. + + + Invalid signature. + + + Could not determine signature algorithm for the signer certificate. + + + The certificate chain is incomplete, the self-signed root authority could not be determined. + Invalid originator identifier choice {0} found in decoded CMS. The subject identifier type {0} is not valid. + + Invalid cryptographic message type. + + + SignerInfo digest algorithm '{0}' is not valid for signature algorithm '{1}'. + The Date property is not available for none KID key agree recipient. The CMS message is not encrypted. + + The CMS message is not signed. + + + The cryptographic message does not contain an expected authenticated attribute. + + + Only one level of counter-signatures are supported on this platform. + The recipients collection is empty. You must specify at least one recipient. This platform does not implement the certificate picker UI. + + No signer certificate was provided. This platform does not implement the certificate picker UI. + + + The signed cryptographic message does not have a signer for the specified signer index. + + + Cannot create CMS signature for empty content. + + + Cannot find the original signer. + + + A certificate with a private key is required. + + + Certificate trust could not be established. The first reported error is: {0} + + + Unknown algorithm '{0}'. + Unable to determine the type of key handle from this keyspec {0}. + + The certificate is not valid for the requested usage. + + + Invalid signature paramters. + The parameter should be a PKCS 9 attribute. Cannot add multiple PKCS 9 signing time attributes. + + PSS parameters were not present. + + + This platform requires that the PSS hash algorithm ({0}) match the data digest algorithm ({1}). + + + This platform does not support the MGF hash algorithm ({0}) being different from the signature hash algorithm ({1}). + + + Mask generation function '{0}' is not supported by this platform. + + + PSS salt size {0} is not supported by this platform with hash algorithm {1}. + Duplicate items are not allowed in the collection. @@ -103,4 +253,16 @@ System.Security.Cryptography.Pkcs is only supported on Windows platforms. + + ASN1 corrupted data. + + + The string contains a character not in the 7 bit ASCII character set. + + + '{0}' is not a known hash algorithm. + + + The input to WriteEncodedValue must represent a single encoded value with no trailing data. + diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj index 12ca937a07..b02bd42ac4 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj @@ -7,20 +7,26 @@ true true None - SR.PlatformNotSupported_CryptographyPkcs true true + + + + - + + $(DefineConstants);netcoreapp + + @@ -50,7 +56,7 @@ - + @@ -82,6 +88,9 @@ + + Common\Interop\Windows\advapi32\Interop.CryptGetProvParam.cs + @@ -164,13 +173,75 @@ Common\Interop\Windows\NCrypt\Interop.NCryptFreeObject.cs - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Common\System\Security\Cryptography\Asn1V2.cs + + + Common\System\Security\Cryptography\Asn1V2.Serializer.cs + + + Common\System\Security\Cryptography\AsnReader.cs + + + Common\System\Security\Cryptography\AsnWriter.cs + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file + diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/CryptographicAttributeObjectCollection.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/CryptographicAttributeObjectCollection.cs index 915152c091..ce4b5af3bf 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/CryptographicAttributeObjectCollection.cs +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/CryptographicAttributeObjectCollection.cs @@ -72,6 +72,12 @@ namespace System.Security.Cryptography return indexOfNewItem; } + internal void AddWithoutMerge(CryptographicAttributeObject attribute) + { + Debug.Assert(attribute != null); + _list.Add(attribute); + } + public void Remove(CryptographicAttributeObject attribute) { if (attribute == null) diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/AlgorithmIdentifierAsn.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/AlgorithmIdentifierAsn.cs new file mode 100644 index 0000000000..575dbb2a5d --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/AlgorithmIdentifierAsn.cs @@ -0,0 +1,69 @@ +// 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.Runtime.InteropServices; +using System.Security.Cryptography.Asn1; + +namespace System.Security.Cryptography.Pkcs.Asn1 +{ + [StructLayout(LayoutKind.Sequential)] + // https://tools.ietf.org/html/rfc3280#section-4.1.1.2 + // + // AlgorithmIdentifier ::= SEQUENCE { + // algorithm OBJECT IDENTIFIER, + // parameters ANY DEFINED BY algorithm OPTIONAL } + internal struct AlgorithmIdentifierAsn + { + [ObjectIdentifier(PopulateFriendlyName = true)] + public Oid Algorithm; + + [AnyValue, OptionalValue] + public ReadOnlyMemory? Parameters; + + internal bool Equals(ref AlgorithmIdentifierAsn other) + { + if (Algorithm.Value != other.Algorithm.Value) + { + return false; + } + + bool isNull = RepresentsNull(Parameters); + bool isOtherNull = RepresentsNull(other.Parameters); + + if (isNull != isOtherNull) + { + return false; + } + + if (isNull) + { + return true; + } + + return Parameters.Value.Span.SequenceEqual(other.Parameters.Value.Span); + } + + private static bool RepresentsNull(ReadOnlyMemory? parameters) + { + if (parameters == null) + { + return true; + } + + ReadOnlySpan span = parameters.Value.Span; + + if (span.Length != 2) + { + return false; + } + + if (span[0] != 0x05) + { + return false; + } + + return span[1] == 0; + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/AttributeAsn.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/AttributeAsn.cs new file mode 100644 index 0000000000..a5bce9fe13 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/AttributeAsn.cs @@ -0,0 +1,25 @@ +// 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.Runtime.InteropServices; +using System.Security.Cryptography.Asn1; + +namespace System.Security.Cryptography.Pkcs.Asn1 +{ + // https://tools.ietf.org/html/rfc5652#section-5.3 + // + // Attribute ::= SEQUENCE { + // attrType OBJECT IDENTIFIER, + // attrValues SET OF AttributeValue } + // + // AttributeValue ::= ANY + [StructLayout(LayoutKind.Sequential)] + internal struct AttributeAsn + { + public Oid AttrType; + + [AnyValue] + public ReadOnlyMemory AttrValues; + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/CertificateChoiceAsn.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/CertificateChoiceAsn.cs new file mode 100644 index 0000000000..614f59f4f3 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/CertificateChoiceAsn.cs @@ -0,0 +1,32 @@ +// 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.Runtime.InteropServices; +using System.Security.Cryptography.Asn1; + +namespace System.Security.Cryptography.Pkcs.Asn1 +{ + // https://tools.ietf.org/html/rfc5652#section-10.2.2 + // + // CertificateChoices ::= CHOICE { + // certificate Certificate, + // extendedCertificate[0] IMPLICIT ExtendedCertificate, -- Obsolete + // v1AttrCert[1] IMPLICIT AttributeCertificateV1, -- Obsolete + // v2AttrCert[2] IMPLICIT AttributeCertificateV2, + // other[3] IMPLICIT OtherCertificateFormat } + // + // OtherCertificateFormat ::= SEQUENCE { + // otherCertFormat OBJECT IDENTIFIER, + // otherCert ANY DEFINED BY otherCertFormat } + // + // Except we only support public key certificates, so just trim the choice here. + [StructLayout(LayoutKind.Sequential)] + [Choice] + internal struct CertificateChoiceAsn + { + [ExpectedTag(TagClass.Universal, (int)UniversalTagNumber.Sequence)] + [AnyValue] + public ReadOnlyMemory? Certificate; + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/ContentInfoAsn.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/ContentInfoAsn.cs new file mode 100644 index 0000000000..2b71fa7a8a --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/ContentInfoAsn.cs @@ -0,0 +1,27 @@ +// 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.Runtime.InteropServices; +using System.Security.Cryptography.Asn1; + +namespace System.Security.Cryptography.Pkcs.Asn1 +{ + // https://tools.ietf.org/html/rfc5652#section-3 + // + // ContentInfo ::= SEQUENCE { + // contentType ContentType, + // content[0] EXPLICIT ANY DEFINED BY contentType } + // + // ContentType::= OBJECT IDENTIFIER + [StructLayout(LayoutKind.Sequential)] + internal struct ContentInfoAsn + { + [ObjectIdentifier] + public string ContentType; + + [ExpectedTag(0, ExplicitTag = true)] + [AnyValue] + public ReadOnlyMemory Content; + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EncapsulatedContentInfoAsn.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EncapsulatedContentInfoAsn.cs new file mode 100644 index 0000000000..47d0bf6417 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EncapsulatedContentInfoAsn.cs @@ -0,0 +1,28 @@ +// 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.Runtime.InteropServices; +using System.Security.Cryptography.Asn1; + +namespace System.Security.Cryptography.Pkcs.Asn1 +{ + // https://tools.ietf.org/html/rfc5652#section-5.2 + // + // EncapsulatedContentInfo ::= SEQUENCE { + // eContentType ContentType, + // eContent[0] EXPLICIT OCTET STRING OPTIONAL } + // + // ContentType::= OBJECT IDENTIFIER + [StructLayout(LayoutKind.Sequential)] + internal struct EncapsulatedContentInfoAsn + { + [ObjectIdentifier] + public string ContentType; + + [OptionalValue] + [ExpectedTag(0, ExplicitTag = true)] + [OctetString] + public ReadOnlyMemory? Content; + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/IssuerAndSerialNumberAsn.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/IssuerAndSerialNumberAsn.cs new file mode 100644 index 0000000000..d2f52f05d5 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/IssuerAndSerialNumberAsn.cs @@ -0,0 +1,29 @@ +// 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.Runtime.InteropServices; +using System.Security.Cryptography.Asn1; + +namespace System.Security.Cryptography.Pkcs.Asn1 +{ + // https://tools.ietf.org/html/rfc5652#section-10.2.4 + // + // IssuerAndSerialNumber ::= SEQUENCE { + // issuer Name, + // serialNumber CertificateSerialNumber } + // + // CertificateSerialNumber::= INTEGER + // + [StructLayout(LayoutKind.Sequential)] + internal struct IssuerAndSerialNumberAsn + { + // X.500 name is a difficult type for the serialization engine. + // Just throw this at X500DistinguishedName. + [AnyValue] + public ReadOnlyMemory Issuer; + + [Integer] + public ReadOnlyMemory SerialNumber; + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/PssParamsAsn.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/PssParamsAsn.cs new file mode 100644 index 0000000000..ac34a8bc00 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/PssParamsAsn.cs @@ -0,0 +1,44 @@ +// 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.Runtime.InteropServices; +using System.Security.Cryptography.Asn1; + +namespace System.Security.Cryptography.Pkcs.Asn1 +{ + // https://tools.ietf.org/html/rfc8017#appendix-A.2.3 + // + // RSASSA-PSS-params ::= SEQUENCE { + // hashAlgorithm[0] HashAlgorithm DEFAULT sha1, + // maskGenAlgorithm[1] MaskGenAlgorithm DEFAULT mgf1SHA1, + // saltLength[2] INTEGER DEFAULT 20, + // trailerField[3] TrailerField DEFAULT trailerFieldBC } + // + [StructLayout(LayoutKind.Sequential)] + internal struct PssParamsAsn + { +#pragma warning disable CS3016 + // SEQUENCE(id-sha1, NULL) + [DefaultValue(0xA0, 0x09, 0x30, 0x07, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A)] + [ExpectedTag(0, ExplicitTag = true)] + public AlgorithmIdentifierAsn HashAlgorithm; + + // SEQUENCE(id-mgf1, SEQUENCE(id-sha1, NULL)) + [DefaultValue( + 0xA1, 0x16, + 0x30, 0x14, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x08, 0x30, 0x09, 0x06, + 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A)] + [ExpectedTag(1, ExplicitTag = true)] + public AlgorithmIdentifierAsn MaskGenAlgorithm; + + [DefaultValue(0xA2, 0x03, 0x02, 0x01, 0x14)] + [ExpectedTag(2, ExplicitTag = true)] + public int SaltLength; + + [DefaultValue(0xA3, 0x03, 0x02, 0x01, 0x01)] + [ExpectedTag(3, ExplicitTag = true)] + public int TrailerField; +#pragma warning restore CS3016 + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignedDataAsn.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignedDataAsn.cs new file mode 100644 index 0000000000..c2b31e1283 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignedDataAsn.cs @@ -0,0 +1,47 @@ +// 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.Runtime.InteropServices; +using System.Security.Cryptography.Asn1; + +namespace System.Security.Cryptography.Pkcs.Asn1 +{ + // https://tools.ietf.org/html/rfc5652#section-5.1 + // + // SignedData ::= SEQUENCE { + // version CMSVersion, + // digestAlgorithms DigestAlgorithmIdentifiers, + // encapContentInfo EncapsulatedContentInfo, + // certificates[0] IMPLICIT CertificateSet OPTIONAL, + // crls[1] IMPLICIT RevocationInfoChoices OPTIONAL, + // signerInfos SignerInfos } + // + // DigestAlgorithmIdentifiers::= SET OF DigestAlgorithmIdentifier + // SignerInfos ::= SET OF SignerInfo + // CertificateSet ::= SET OF CertificateChoices + // + [StructLayout(LayoutKind.Sequential)] + internal struct SignedDataAsn + { + public int Version; + + [SetOf] + public AlgorithmIdentifierAsn[] DigestAlgorithms; + + public EncapsulatedContentInfoAsn EncapContentInfo; + + [OptionalValue] + [ExpectedTag(0)] + [SetOf] + public CertificateChoiceAsn[] CertificateSet; + + [OptionalValue] + [ExpectedTag(1)] + [AnyValue] + public ReadOnlyMemory? RevocationInfoChoices; + + [SetOf] + public SignerInfoAsn[] SignerInfos; + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignerIdentifierAsn.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignerIdentifierAsn.cs new file mode 100644 index 0000000000..3765f3788b --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignerIdentifierAsn.cs @@ -0,0 +1,28 @@ +// 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.Runtime.InteropServices; +using System.Security.Cryptography.Asn1; + +namespace System.Security.Cryptography.Pkcs.Asn1 +{ + // https://tools.ietf.org/html/rfc5652#section-5.3 + // + // SignerIdentifier ::= CHOICE { + // issuerAndSerialNumber IssuerAndSerialNumber, + // subjectKeyIdentifier[0] SubjectKeyIdentifier } + // + // SubjectKeyIdentifier ::= OCTET STRING + // + [Choice] + [StructLayout(LayoutKind.Sequential)] + internal struct SignerIdentifierAsn + { + public IssuerAndSerialNumberAsn? IssuerAndSerialNumber; + + [OctetString] + [ExpectedTag(0)] + public ReadOnlyMemory? SubjectKeyIdentifier; + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignerInfoAsn.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignerInfoAsn.cs new file mode 100644 index 0000000000..3d201bd3d2 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignerInfoAsn.cs @@ -0,0 +1,52 @@ +// 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.Runtime.InteropServices; +using System.Security.Cryptography.Asn1; + +namespace System.Security.Cryptography.Pkcs.Asn1 +{ + // https://tools.ietf.org/html/rfc5652#section-5.3 + // + // SignerInfo ::= SEQUENCE { + // version CMSVersion, + // sid SignerIdentifier, + // digestAlgorithm DigestAlgorithmIdentifier, + // signedAttrs[0] IMPLICIT SignedAttributes OPTIONAL, + // signatureAlgorithm SignatureAlgorithmIdentifier, + // signature SignatureValue, + // unsignedAttrs[1] IMPLICIT UnsignedAttributes OPTIONAL } + // + // CMSVersion ::= INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4), v5(5) } + // DigestAlgorithmIdentifier ::= AlgorithmIdentifier + // SignedAttributes ::= SET SIZE (1..MAX) OF Attribute + // SignatureAlgorithmIdentifier ::= AlgorithmIdentifier + // SignatureValue ::= OCTET STRING + // UnsignedAttributes ::= SET SIZE(1..MAX) OF Attribute + // + [StructLayout(LayoutKind.Sequential)] + internal struct SignerInfoAsn + { + public int Version; + + public SignerIdentifierAsn Sid; + + public AlgorithmIdentifierAsn DigestAlgorithm; + + [ExpectedTag(0)] + [SetOf] + [OptionalValue] + public AttributeAsn[] SignedAttributes; + + public AlgorithmIdentifierAsn SignatureAlgorithm; + + [OctetString] + public ReadOnlyMemory SignatureValue; + + [ExpectedTag(1)] + [SetOf] + [OptionalValue] + public AttributeAsn[] UnsignedAttributes; + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.DSA.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.DSA.cs new file mode 100644 index 0000000000..e85cfbd3fc --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.DSA.cs @@ -0,0 +1,141 @@ +// 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.Buffers; +using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; +using Internal.Cryptography; + +namespace System.Security.Cryptography.Pkcs +{ + internal partial class CmsSignature + { + static partial void PrepareRegistrationDsa(Dictionary lookup) + { + lookup.Add(Oids.DsaWithSha1, new DSACmsSignature(Oids.DsaWithSha1, HashAlgorithmName.SHA1)); + lookup.Add(Oids.DsaWithSha256, new DSACmsSignature(Oids.DsaWithSha256, HashAlgorithmName.SHA256)); + lookup.Add(Oids.DsaWithSha384, new DSACmsSignature(Oids.DsaWithSha384, HashAlgorithmName.SHA384)); + lookup.Add(Oids.DsaWithSha512, new DSACmsSignature(Oids.DsaWithSha512, HashAlgorithmName.SHA512)); + lookup.Add(Oids.DsaPublicKey, new DSACmsSignature(null, default)); + } + + private class DSACmsSignature : CmsSignature + { + private readonly HashAlgorithmName _expectedDigest; + private readonly string _signatureAlgorithm; + + internal DSACmsSignature(string signatureAlgorithm, HashAlgorithmName expectedDigest) + { + _signatureAlgorithm = signatureAlgorithm; + _expectedDigest = expectedDigest; + } + + internal override bool VerifySignature( + ReadOnlySpan valueHash, + ReadOnlyMemory signature, + string digestAlgorithmOid, + HashAlgorithmName digestAlgorithmName, + ReadOnlyMemory? signatureParameters, + X509Certificate2 certificate) + { + if (_expectedDigest != digestAlgorithmName) + { + throw new CryptographicException( + SR.Format( + SR.Cryptography_Cms_InvalidSignerHashForSignatureAlg, + digestAlgorithmOid, + _signatureAlgorithm)); + } + + DSA dsa = certificate.GetDSAPublicKey(); + + if (dsa == null) + { + return false; + } + + DSAParameters dsaParameters = dsa.ExportParameters(false); + int bufSize = 2 * dsaParameters.Q.Length; + + ArrayPool pool = ArrayPool.Shared; + byte[] rented = pool.Rent(bufSize); + Span ieee = new Span(rented, 0, bufSize); + + try + { + if (!DsaDerToIeee(signature, ieee)) + { + return false; + } + + return dsa.VerifySignature(valueHash, ieee); + } + finally + { + ieee.Clear(); + pool.Return(rented); + } + } + + protected override bool Sign( + ReadOnlySpan dataHash, + HashAlgorithmName hashAlgorithmName, + X509Certificate2 certificate, + bool silent, + out Oid signatureAlgorithm, + out byte[] signatureValue) + { + // If there's no private key, fall back to the public key for a "no private key" exception. + DSA dsa = + PkcsPal.Instance.GetPrivateKeyForSigning(certificate, silent) ?? + certificate.GetDSAPublicKey(); + + if (dsa == null) + { + signatureAlgorithm = null; + signatureValue = null; + return false; + } + + string oidValue = + hashAlgorithmName == HashAlgorithmName.SHA1 ? Oids.DsaWithSha1 : + hashAlgorithmName == HashAlgorithmName.SHA256 ? Oids.DsaWithSha256 : + hashAlgorithmName == HashAlgorithmName.SHA384 ? Oids.DsaWithSha384 : + hashAlgorithmName == HashAlgorithmName.SHA512 ? Oids.DsaWithSha512 : + null; + + if (oidValue == null) + { + signatureAlgorithm = null; + signatureValue = null; + return false; + } + + signatureAlgorithm = new Oid(oidValue, oidValue); + + ArrayPool pool = ArrayPool.Shared; + // The Q size cannot be bigger than the KeySize. + byte[] rented = pool.Rent(dsa.KeySize / 8); + int bytesWritten = 0; + + try + { + if (dsa.TryCreateSignature(dataHash, rented, out bytesWritten)) + { + signatureValue = DsaIeeeToDer(new ReadOnlySpan(rented, 0, bytesWritten)); + return true; + } + } + finally + { + Array.Clear(rented, 0, bytesWritten); + pool.Return(rented); + } + + signatureValue = null; + return false; + } + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.ECDsa.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.ECDsa.cs new file mode 100644 index 0000000000..c50356690b --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.ECDsa.cs @@ -0,0 +1,165 @@ +// 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.Buffers; +using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; +using Internal.Cryptography; + +namespace System.Security.Cryptography.Pkcs +{ + internal partial class CmsSignature + { + static partial void PrepareRegistrationECDsa(Dictionary lookup) + { + lookup.Add(Oids.ECDsaWithSha1, new ECDsaCmsSignature(Oids.ECDsaWithSha1, HashAlgorithmName.SHA1)); + lookup.Add(Oids.ECDsaWithSha256, new ECDsaCmsSignature(Oids.ECDsaWithSha256, HashAlgorithmName.SHA256)); + lookup.Add(Oids.ECDsaWithSha384, new ECDsaCmsSignature(Oids.ECDsaWithSha384, HashAlgorithmName.SHA384)); + lookup.Add(Oids.ECDsaWithSha512, new ECDsaCmsSignature(Oids.ECDsaWithSha512, HashAlgorithmName.SHA512)); + lookup.Add(Oids.EcPublicKey, new ECDsaCmsSignature(null, default)); + } + + private partial class ECDsaCmsSignature : CmsSignature + { + private readonly HashAlgorithmName _expectedDigest; + private readonly string _signatureAlgorithm; + + internal ECDsaCmsSignature(string signatureAlgorithm, HashAlgorithmName expectedDigest) + { + _signatureAlgorithm = signatureAlgorithm; + _expectedDigest = expectedDigest; + } + + internal override bool VerifySignature( +#if netcoreapp + ReadOnlySpan valueHash, + ReadOnlyMemory signature, +#else + byte[] valueHash, + byte[] signature, +#endif + string digestAlgorithmOid, + HashAlgorithmName digestAlgorithmName, + ReadOnlyMemory? signatureParameters, + X509Certificate2 certificate) + { + if (_expectedDigest != digestAlgorithmName) + { + throw new CryptographicException( + SR.Format( + SR.Cryptography_Cms_InvalidSignerHashForSignatureAlg, + digestAlgorithmOid, + _signatureAlgorithm)); + } + + ECDsa key = certificate.GetECDsaPublicKey(); + + if (key == null) + { + return false; + } + + // 2 * KeySizeBytes => 2 * KeySizeBits / 8 => KeySizeBits / 4 + int bufSize = key.KeySize / 4; + +#if netcoreapp + ArrayPool pool = ArrayPool.Shared; + byte[] rented = pool.Rent(bufSize); + Span ieee = new Span(rented, 0, bufSize); + + try + { +#else + byte[] ieee = new byte[bufSize]; +#endif + if (!DsaDerToIeee(signature, ieee)) + { + return false; + } + + return key.VerifyHash(valueHash, ieee); +#if netcoreapp + } + finally + { + ieee.Clear(); + pool.Return(rented); + } +#endif + } + + protected override bool Sign( +#if netcoreapp + ReadOnlySpan dataHash, +#else + byte[] dataHash, +#endif + HashAlgorithmName hashAlgorithmName, + X509Certificate2 certificate, + bool silent, + out Oid signatureAlgorithm, + out byte[] signatureValue) + { + // If there's no private key, fall back to the public key for a "no private key" exception. + ECDsa key = + PkcsPal.Instance.GetPrivateKeyForSigning(certificate, silent) ?? + certificate.GetECDsaPublicKey(); + + if (key == null) + { + signatureAlgorithm = null; + signatureValue = null; + return false; + } + + string oidValue = + hashAlgorithmName == HashAlgorithmName.SHA1 ? Oids.ECDsaWithSha1 : + hashAlgorithmName == HashAlgorithmName.SHA256 ? Oids.ECDsaWithSha256 : + hashAlgorithmName == HashAlgorithmName.SHA384 ? Oids.ECDsaWithSha384 : + hashAlgorithmName == HashAlgorithmName.SHA512 ? Oids.ECDsaWithSha512 : + null; + + if (oidValue == null) + { + signatureAlgorithm = null; + signatureValue = null; + return false; + } + + signatureAlgorithm = new Oid(oidValue, oidValue); + +#if netcoreapp + ArrayPool pool = ArrayPool.Shared; + // The signature size is twice the KeySize. + // 2 * KeySizeInBytes => 2 * KeySizeInBits / 8 => KeySizeInBits / 4 + byte[] rented = pool.Rent(key.KeySize / 4); + int bytesWritten = 0; + + try + { + if (key.TrySignHash(dataHash, rented, out bytesWritten)) + { + signatureValue = DsaIeeeToDer(new ReadOnlySpan(rented, 0, bytesWritten)); + return true; + } + } + finally + { + Array.Clear(rented, 0, bytesWritten); + pool.Return(rented); + } +#endif + + signatureValue = DsaIeeeToDer(key.SignHash( +#if netcoreapp + dataHash.ToArray() +#else + dataHash +#endif + )); + return true; + } + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.RSA.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.RSA.cs new file mode 100644 index 0000000000..525b4644ef --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.RSA.cs @@ -0,0 +1,233 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Security.Cryptography.Asn1; +using System.Security.Cryptography.Pkcs.Asn1; +using System.Security.Cryptography.X509Certificates; +using Internal.Cryptography; + +namespace System.Security.Cryptography.Pkcs +{ + internal partial class CmsSignature + { + static partial void PrepareRegistrationRsa(Dictionary lookup) + { + lookup.Add(Oids.Rsa, new RSAPkcs1CmsSignature()); + lookup.Add(Oids.RsaPss, new RSAPssCmsSignature()); + } + + private abstract class RSACmsSignature : CmsSignature + { + internal override bool VerifySignature( +#if netcoreapp + ReadOnlySpan valueHash, + ReadOnlyMemory signature, +#else + byte[] valueHash, + byte[] signature, +#endif + string digestAlgorithmOid, + HashAlgorithmName digestAlgorithmName, + ReadOnlyMemory? signatureParameters, + X509Certificate2 certificate) + { + RSASignaturePadding padding = GetSignaturePadding( + signatureParameters, + digestAlgorithmOid, + digestAlgorithmName, + valueHash.Length); + + RSA publicKey = certificate.GetRSAPublicKey(); + + if (publicKey == null) + { + return false; + } + + return publicKey.VerifyHash( + valueHash, +#if netcoreapp + signature.Span, +#else + signature, +#endif + digestAlgorithmName, + padding); + } + + protected abstract RSASignaturePadding GetSignaturePadding( + ReadOnlyMemory? signatureParameters, + string digestAlgorithmOid, + HashAlgorithmName digestAlgorithmName, + int digestValueLength); + } + + private sealed class RSAPkcs1CmsSignature : RSACmsSignature + { + protected override RSASignaturePadding GetSignaturePadding( + ReadOnlyMemory? signatureParameters, + string digestAlgorithmOid, + HashAlgorithmName digestAlgorithmName, + int digestValueLength) + { + if (signatureParameters == null) + { + return RSASignaturePadding.Pkcs1; + } + + Span expectedParameters = stackalloc byte[2]; + expectedParameters[0] = 0x05; + expectedParameters[1] = 0x00; + + if (expectedParameters.SequenceEqual(signatureParameters.Value.Span)) + { + return RSASignaturePadding.Pkcs1; + } + + throw new CryptographicException(SR.Cryptography_Pkcs_InvalidSignatureParameters); + } + + protected override bool Sign( +#if netcoreapp + ReadOnlySpan dataHash, +#else + byte[] dataHash, +#endif + HashAlgorithmName hashAlgorithmName, + X509Certificate2 certificate, + bool silent, + out Oid signatureAlgorithm, + out byte[] signatureValue) + { + // If there's no private key, fall back to the public key for a "no private key" exception. + RSA privateKey = + PkcsPal.Instance.GetPrivateKeyForSigning(certificate, silent) ?? + certificate.GetRSAPublicKey(); + + if (privateKey == null) + { + signatureAlgorithm = null; + signatureValue = null; + return false; + } + + signatureAlgorithm = new Oid(Oids.Rsa, Oids.Rsa); + +#if netcoreapp + byte[] signature = new byte[privateKey.KeySize / 8]; + + bool signed = privateKey.TrySignHash( + dataHash, + signature, + hashAlgorithmName, + RSASignaturePadding.Pkcs1, + out int bytesWritten); + + if (signed && signature.Length == bytesWritten) + { + signatureValue = signature; + return true; + } +#endif + signatureValue = privateKey.SignHash( +#if netcoreapp + dataHash.ToArray(), +#else + dataHash, +#endif + hashAlgorithmName, + RSASignaturePadding.Pkcs1); + + return true; + } + } + + private class RSAPssCmsSignature : RSACmsSignature + { + protected override RSASignaturePadding GetSignaturePadding( + ReadOnlyMemory? signatureParameters, + string digestAlgorithmOid, + HashAlgorithmName digestAlgorithmName, + int digestValueLength) + { + if (signatureParameters == null) + { + throw new CryptographicException(SR.Cryptography_Pkcs_PssParametersMissing); + } + + PssParamsAsn pssParams = + AsnSerializer.Deserialize(signatureParameters.Value, AsnEncodingRules.DER); + + if (pssParams.HashAlgorithm.Algorithm.Value != digestAlgorithmOid) + { + throw new CryptographicException( + SR.Format( + SR.Cryptography_Pkcs_PssParametersHashMismatch, + pssParams.HashAlgorithm.Algorithm.Value, + digestAlgorithmOid)); + } + + if (pssParams.TrailerField != 1) + { + throw new CryptographicException(SR.Cryptography_Pkcs_InvalidSignatureParameters); + } + + if (pssParams.SaltLength != digestValueLength) + { + throw new CryptographicException( + SR.Format( + SR.Cryptography_Pkcs_PssParametersSaltMismatch, + pssParams.SaltLength, + digestAlgorithmName.Name)); + } + + if (pssParams.MaskGenAlgorithm.Algorithm.Value != Oids.Mgf1) + { + throw new CryptographicException( + SR.Cryptography_Pkcs_PssParametersMgfNotSupported, + pssParams.MaskGenAlgorithm.Algorithm.Value); + } + + if (pssParams.MaskGenAlgorithm.Parameters == null) + { + throw new CryptographicException(SR.Cryptography_Pkcs_InvalidSignatureParameters); + } + + AlgorithmIdentifierAsn mgfParams = AsnSerializer.Deserialize( + pssParams.MaskGenAlgorithm.Parameters.Value, + AsnEncodingRules.DER); + + if (mgfParams.Algorithm.Value != digestAlgorithmOid) + { + throw new CryptographicException( + SR.Format( + SR.Cryptography_Pkcs_PssParametersMgfHashMismatch, + mgfParams.Algorithm.Value, + digestAlgorithmOid)); + } + + // When RSASignaturePadding supports custom salt sizes this return will look different. + return RSASignaturePadding.Pss; + } + + protected override bool Sign( +#if netcoreapp + ReadOnlySpan dataHash, +#else + byte[] dataHash, +#endif + HashAlgorithmName hashAlgorithmName, + X509Certificate2 certificate, + bool silent, + out Oid signatureAlgorithm, + out byte[] signatureValue) + { + Debug.Fail("RSA-PSS requires building parameters, which has no API."); + throw new CryptographicException(); + } + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.cs new file mode 100644 index 0000000000..385c5fa3d4 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.cs @@ -0,0 +1,198 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Numerics; +using System.Security.Cryptography.Asn1; +using System.Security.Cryptography.X509Certificates; + +namespace System.Security.Cryptography.Pkcs +{ + internal abstract partial class CmsSignature + { + private static readonly Dictionary s_lookup = + new Dictionary(); + + static CmsSignature() + { + PrepareRegistrationRsa(s_lookup); + PrepareRegistrationDsa(s_lookup); + PrepareRegistrationECDsa(s_lookup); + } + + static partial void PrepareRegistrationRsa(Dictionary lookup); + static partial void PrepareRegistrationDsa(Dictionary lookup); + static partial void PrepareRegistrationECDsa(Dictionary lookup); + + internal abstract bool VerifySignature( +#if netcoreapp + ReadOnlySpan valueHash, + ReadOnlyMemory signature, +#else + byte[] valueHash, + byte[] signature, +#endif + string digestAlgorithmOid, + HashAlgorithmName digestAlgorithmName, + ReadOnlyMemory? signatureParameters, + X509Certificate2 certificate); + + protected abstract bool Sign( +#if netcoreapp + ReadOnlySpan dataHash, +#else + byte[] dataHash, +#endif + HashAlgorithmName hashAlgorithmName, + X509Certificate2 certificate, + bool silent, + out Oid signatureAlgorithm, + out byte[] signatureValue); + + internal static CmsSignature Resolve(string signatureAlgorithmOid) + { + if (s_lookup.TryGetValue(signatureAlgorithmOid, out CmsSignature processor)) + { + return processor; + } + + return null; + } + + internal static bool Sign( +#if netcoreapp + ReadOnlySpan dataHash, +#else + byte[] dataHash, +#endif + HashAlgorithmName hashAlgorithmName, + X509Certificate2 certificate, + bool silent, + out Oid oid, + out ReadOnlyMemory signatureValue) + { + CmsSignature processor = Resolve(certificate.GetKeyAlgorithm()); + + if (processor == null) + { + oid = null; + signatureValue = default; + return false; + } + + byte[] signature; + bool signed = processor.Sign(dataHash, hashAlgorithmName, certificate, silent, out oid, out signature); + signatureValue = signature; + return signed; + } + + private static bool DsaDerToIeee( + ReadOnlyMemory derSignature, + Span ieeeSignature) + { + int fieldSize = ieeeSignature.Length / 2; + + Debug.Assert( + fieldSize * 2 == ieeeSignature.Length, + $"ieeeSignature.Length ({ieeeSignature.Length}) must be even"); + + try + { + AsnReader reader = new AsnReader(derSignature, AsnEncodingRules.DER); + AsnReader sequence = reader.ReadSequence(); + + if (reader.HasData) + { + return false; + } + + // Fill it with zeros so that small data is correctly zero-prefixed. + // this buffer isn't very large, so there's not really a reason to the + // partial-fill gymnastics. + ieeeSignature.Clear(); + + ReadOnlySpan val = sequence.GetIntegerBytes().Span; + + if (val.Length > fieldSize && val[0] == 0) + { + val = val.Slice(1); + } + + if (val.Length <= fieldSize) + { + val.CopyTo(ieeeSignature.Slice(fieldSize - val.Length, val.Length)); + } + + val = sequence.GetIntegerBytes().Span; + + if (val.Length > fieldSize && val[0] == 0) + { + val = val.Slice(1); + } + + if (val.Length <= fieldSize) + { + val.CopyTo(ieeeSignature.Slice(fieldSize + fieldSize - val.Length, val.Length)); + } + + return !sequence.HasData; + } + catch (CryptographicException) + { + return false; + } + } + + private static byte[] DsaIeeeToDer(ReadOnlySpan ieeeSignature) + { + int fieldSize = ieeeSignature.Length / 2; + + Debug.Assert( + fieldSize * 2 == ieeeSignature.Length, + $"ieeeSignature.Length ({ieeeSignature.Length}) must be even"); + + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + { + writer.PushSequence(); + +#if netcoreapp + // r + BigInteger val = new BigInteger( + ieeeSignature.Slice(0, fieldSize), + isUnsigned: true, + isBigEndian: true); + + writer.WriteInteger(val); + + // s + val = new BigInteger( + ieeeSignature.Slice(fieldSize, fieldSize), + isUnsigned: true, + isBigEndian: true); + + writer.WriteInteger(val); +#else + byte[] buf = new byte[fieldSize + 1]; + Span bufWriter = new Span(buf, 1, fieldSize); + + ieeeSignature.Slice(0, fieldSize).CopyTo(bufWriter); + Array.Reverse(buf); + BigInteger val = new BigInteger(buf); + writer.WriteInteger(val); + + buf[0] = 0; + ieeeSignature.Slice(fieldSize, fieldSize).CopyTo(bufWriter); + Array.Reverse(buf); + val = new BigInteger(buf); + writer.WriteInteger(val); +#endif + + writer.PopSequence(); + + return writer.Encode(); + } + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs new file mode 100644 index 0000000000..954c342292 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs @@ -0,0 +1,307 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Security.Cryptography.Asn1; +using System.Security.Cryptography.Pkcs.Asn1; +using System.Security.Cryptography.X509Certificates; +using Internal.Cryptography; + +namespace System.Security.Cryptography.Pkcs +{ + public sealed class CmsSigner + { + private static readonly Oid s_defaultAlgorithm = Oid.FromOidValue(Oids.Sha256, OidGroup.HashAlgorithm); + + public X509Certificate2 Certificate { get; set; } + public X509Certificate2Collection Certificates { get; set; } = new X509Certificate2Collection(); + public Oid DigestAlgorithm { get; set; } + public X509IncludeOption IncludeOption { get; set; } + public CryptographicAttributeObjectCollection SignedAttributes { get; set; } = new CryptographicAttributeObjectCollection(); + public SubjectIdentifierType SignerIdentifierType { get; set; } + public CryptographicAttributeObjectCollection UnsignedAttributes { get; set; } = new CryptographicAttributeObjectCollection(); + + public CmsSigner() + : this(SubjectIdentifierType.IssuerAndSerialNumber, null) + { + } + + public CmsSigner(SubjectIdentifierType signerIdentifierType) + : this(signerIdentifierType, null) + { + } + + public CmsSigner(X509Certificate2 certificate) + : this(SubjectIdentifierType.IssuerAndSerialNumber, certificate) + { + } + + // This can be implemented with netcoreapp20 with the cert creation API. + // * Open the parameters as RSACSP (RSA PKCS#1 signature was hard-coded in netfx) + // * Which will fail on non-Windows + // * Create a certificate with subject CN=CMS Signer Dummy Certificate + // * Need to check against NetFx to find out what the NotBefore/NotAfter values are + // * No extensions + // + // Since it would only work on Windows, it could also be just done as P/Invokes to + // CertCreateSelfSignedCertificate on a split Windows/netstandard implementation. + public CmsSigner(CspParameters parameters) => throw new PlatformNotSupportedException(); + + public CmsSigner(SubjectIdentifierType signerIdentifierType, X509Certificate2 certificate) + { + switch (signerIdentifierType) + { + case SubjectIdentifierType.Unknown: + SignerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber; + IncludeOption = X509IncludeOption.ExcludeRoot; + break; + case SubjectIdentifierType.IssuerAndSerialNumber: + SignerIdentifierType = signerIdentifierType; + IncludeOption = X509IncludeOption.ExcludeRoot; + break; + case SubjectIdentifierType.SubjectKeyIdentifier: + SignerIdentifierType = signerIdentifierType; + IncludeOption = X509IncludeOption.ExcludeRoot; + break; + case SubjectIdentifierType.NoSignature: + SignerIdentifierType = signerIdentifierType; + IncludeOption = X509IncludeOption.None; + break; + default: + SignerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber; + IncludeOption = X509IncludeOption.ExcludeRoot; + break; + } + + Certificate = certificate; + DigestAlgorithm = new Oid(s_defaultAlgorithm); + } + + internal void CheckCertificateValue() + { + if (SignerIdentifierType == SubjectIdentifierType.NoSignature) + { + return; + } + + if (Certificate == null) + { + throw new PlatformNotSupportedException(SR.Cryptography_Cms_NoSignerCert); + } + + if (!Certificate.HasPrivateKey) + { + throw new CryptographicException(SR.Cryptography_Cms_Signing_RequiresPrivateKey); + } + } + + internal SignerInfoAsn Sign( + ReadOnlyMemory data, + string contentTypeOid, + bool silent, + out X509Certificate2Collection chainCerts) + { + HashAlgorithmName hashAlgorithmName = Helpers.GetDigestAlgorithm(DigestAlgorithm); + IncrementalHash hasher = IncrementalHash.CreateHash(hashAlgorithmName); + + hasher.AppendData(data.Span); + byte[] dataHash = hasher.GetHashAndReset(); + + SignerInfoAsn newSignerInfo = new SignerInfoAsn(); + newSignerInfo.DigestAlgorithm.Algorithm = DigestAlgorithm; + + if ((SignedAttributes != null && SignedAttributes.Count > 0) || contentTypeOid == null) + { + List signedAttrs = BuildAttributes(SignedAttributes); + + using (var writer = new AsnWriter(AsnEncodingRules.DER)) + { + writer.PushSetOf(); + writer.WriteOctetString(dataHash); + writer.PopSetOf(); + + signedAttrs.Add( + new AttributeAsn + { + AttrType = new Oid(Oids.MessageDigest, Oids.MessageDigest), + AttrValues = writer.Encode(), + }); + } + + if (contentTypeOid != null) + { + using (var writer = new AsnWriter(AsnEncodingRules.DER)) + { + writer.PushSetOf(); + writer.WriteObjectIdentifier(contentTypeOid); + writer.PopSetOf(); + + signedAttrs.Add( + new AttributeAsn + { + AttrType = new Oid(Oids.ContentType, Oids.ContentType), + AttrValues = writer.Encode(), + }); + } + } + + // Use the serializer/deserializer to DER-normalize the attribute order. + newSignerInfo.SignedAttributes = Helpers.NormalizeSet( + signedAttrs.ToArray(), + normalized => + { + AsnReader reader = new AsnReader(normalized, AsnEncodingRules.DER); + hasher.AppendData(reader.PeekContentBytes().Span); + }); + + dataHash = hasher.GetHashAndReset(); + } + + switch (SignerIdentifierType) + { + case SubjectIdentifierType.IssuerAndSerialNumber: + byte[] serial = Certificate.GetSerialNumber(); + Array.Reverse(serial); + + newSignerInfo.Sid.IssuerAndSerialNumber = new IssuerAndSerialNumberAsn + { + Issuer = Certificate.IssuerName.RawData, + SerialNumber = serial, + }; + + newSignerInfo.Version = 1; + break; + case SubjectIdentifierType.SubjectKeyIdentifier: + newSignerInfo.Sid.SubjectKeyIdentifier = Certificate.GetSubjectKeyIdentifier(); + newSignerInfo.Version = 3; + break; + case SubjectIdentifierType.NoSignature: + newSignerInfo.Sid.IssuerAndSerialNumber = new IssuerAndSerialNumberAsn + { + Issuer = SubjectIdentifier.DummySignerEncodedValue, + SerialNumber = new byte[1], + }; + newSignerInfo.Version = 1; + break; + default: + Debug.Fail($"Unresolved SignerIdentifierType value: {SignerIdentifierType}"); + throw new CryptographicException(); + } + + if (UnsignedAttributes != null && UnsignedAttributes.Count > 0) + { + List attrs = BuildAttributes(UnsignedAttributes); + + newSignerInfo.UnsignedAttributes = Helpers.NormalizeSet(attrs.ToArray()); + } + + bool signed = CmsSignature.Sign( + dataHash, + hashAlgorithmName, + Certificate, + silent, + out Oid signatureAlgorithm, + out ReadOnlyMemory signatureValue); + + if (!signed) + { + throw new CryptographicException(SR.Cryptography_Cms_CannotDetermineSignatureAlgorithm); + } + + newSignerInfo.SignatureValue = signatureValue; + newSignerInfo.SignatureAlgorithm.Algorithm = signatureAlgorithm; + + X509Certificate2Collection certs = new X509Certificate2Collection(); + certs.AddRange(Certificates); + + if (SignerIdentifierType != SubjectIdentifierType.NoSignature) + { + if (IncludeOption == X509IncludeOption.EndCertOnly) + { + certs.Add(Certificate); + } + else if (IncludeOption != X509IncludeOption.None) + { + X509Chain chain = new X509Chain(); + chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; + chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags; + + if (!chain.Build(Certificate)) + { + foreach (X509ChainStatus status in chain.ChainStatus) + { + if (status.Status == X509ChainStatusFlags.PartialChain) + { + throw new CryptographicException(SR.Cryptography_Cms_IncompleteCertChain); + } + } + } + + X509ChainElementCollection elements = chain.ChainElements; + int count = elements.Count; + int last = count - 1; + + if (last == 0) + { + // If there's always one cert treat it as EE, not root. + last = -1; + } + + for (int i = 0; i < count; i++) + { + X509Certificate2 cert = elements[i].Certificate; + + if (i == last && + IncludeOption == X509IncludeOption.ExcludeRoot && + cert.SubjectName.RawData.AsReadOnlySpan().SequenceEqual(cert.IssuerName.RawData)) + { + break; + } + + certs.Add(cert); + } + } + } + + chainCerts = certs; + return newSignerInfo; + } + + private static List BuildAttributes(CryptographicAttributeObjectCollection attributes) + { + List signedAttrs = new List(); + + if (attributes == null || attributes.Count == 0) + { + return signedAttrs; + } + + foreach (CryptographicAttributeObject attributeObject in attributes) + { + using (var writer = new AsnWriter(AsnEncodingRules.DER)) + { + writer.PushSetOf(); + + foreach (AsnEncodedData objectValue in attributeObject.Values) + { + writer.WriteEncodedValue(objectValue.RawData); + } + + writer.PopSetOf(); + + AttributeAsn newAttr = new AttributeAsn + { + AttrType = attributeObject.Oid, + AttrValues = writer.Encode(), + }; + + signedAttrs.Add(newAttr); + } + } + + return signedAttrs; + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs9MessageDigest.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs9MessageDigest.cs index 3ed3b31b5c..7349d6d602 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs9MessageDigest.cs +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs9MessageDigest.cs @@ -4,7 +4,7 @@ using System; using System.Diagnostics; - +using System.Security.Cryptography.Asn1; using Internal.Cryptography; namespace System.Security.Cryptography.Pkcs @@ -20,6 +20,15 @@ namespace System.Security.Cryptography.Pkcs { } + internal Pkcs9MessageDigest(ReadOnlySpan signatureDigest) + { + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + { + writer.WriteOctetString(signatureDigest); + RawData = writer.Encode(); + } + } + // // Public properties. // diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignedCms.CtorOverloads.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignedCms.CtorOverloads.cs new file mode 100644 index 0000000000..4e2e183701 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignedCms.CtorOverloads.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. +// See the LICENSE file in the project root for more information. + +using Internal.Cryptography; + +namespace System.Security.Cryptography.Pkcs +{ + public sealed partial class SignedCms + { + // Let the lookup happen once, then clone it. + private static readonly Oid s_cmsDataOid = Oid.FromOidValue(Oids.Pkcs7Data, OidGroup.ExtensionOrAttribute); + + private static ContentInfo MakeEmptyContentInfo() => + new ContentInfo(new Oid(s_cmsDataOid), Array.Empty()); + + public SignedCms() + : this(SubjectIdentifierType.IssuerAndSerialNumber, MakeEmptyContentInfo(), false) + { + } + + public SignedCms(SubjectIdentifierType signerIdentifierType) + : this(signerIdentifierType, MakeEmptyContentInfo(), false) + { + } + + public SignedCms(ContentInfo contentInfo) + : this(SubjectIdentifierType.IssuerAndSerialNumber, contentInfo, false) + { + } + + public SignedCms(SubjectIdentifierType signerIdentifierType, ContentInfo contentInfo) + : this(signerIdentifierType, contentInfo, false) + { + } + + public SignedCms(ContentInfo contentInfo, bool detached) + : this(SubjectIdentifierType.IssuerAndSerialNumber, contentInfo, detached) + { + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignedCms.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignedCms.cs new file mode 100644 index 0000000000..64bd9fe8f7 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignedCms.cs @@ -0,0 +1,482 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Security.Cryptography.Asn1; +using System.Security.Cryptography.Pkcs.Asn1; +using System.Security.Cryptography.X509Certificates; +using Internal.Cryptography; + +namespace System.Security.Cryptography.Pkcs +{ + public sealed partial class SignedCms + { + private SignedDataAsn _signedData; + private bool _hasData; + + // A defensive copy of the relevant portions of the data to Decode + private Memory _heldData; + + // Due to the way the underlying Windows CMS API behaves a copy of the content + // bytes will be held separate once the content is "bound" (first signature or decode) + private ReadOnlyMemory? _heldContent; + + // Similar to _heldContent, the Windows CMS API held this separate internally, + // and thus we need to be reslilient against modification. + private string _contentType; + + public int Version { get; private set; } + public ContentInfo ContentInfo { get; private set; } + public bool Detached { get; private set; } + + public SignedCms(SubjectIdentifierType signerIdentifierType, ContentInfo contentInfo, bool detached) + { + if (contentInfo == null) + throw new ArgumentNullException(nameof(contentInfo)); + if (contentInfo.Content == null) + throw new ArgumentNullException("contentInfo.Content"); + + // signerIdentifierType is ignored. + // In .NET Framework it is used for the signer type of a prompt-for-certificate signer. + // In .NET Core we don't support prompting. + // + // .NET Framework turned any unknown value into IssuerAndSerialNumber, so no exceptions + // are required, either. + + ContentInfo = contentInfo; + Detached = detached; + Version = 0; + } + + public X509Certificate2Collection Certificates + { + get + { + var coll = new X509Certificate2Collection(); + + if (!_hasData) + { + return coll; + } + + CertificateChoiceAsn[] certChoices = _signedData.CertificateSet; + + if (certChoices == null) + { + return coll; + } + + foreach (CertificateChoiceAsn choice in certChoices) + { + coll.Add(new X509Certificate2(choice.Certificate.Value.ToArray())); + } + + return coll; + } + } + + public SignerInfoCollection SignerInfos + { + get + { + if (!_hasData) + { + return new SignerInfoCollection(); + } + + return new SignerInfoCollection(_signedData.SignerInfos, this); + } + } + + public byte[] Encode() + { + if (!_hasData) + { + throw new InvalidOperationException(SR.Cryptography_Cms_MessageNotSigned); + } + + // Write as DER, so everyone can read it. + AsnWriter writer = AsnSerializer.Serialize(_signedData, AsnEncodingRules.DER); + byte[] signedData = writer.Encode(); + + ContentInfoAsn contentInfo = new ContentInfoAsn + { + Content = signedData, + ContentType = Oids.Pkcs7Signed, + }; + + // Write as DER, so everyone can read it. + writer = AsnSerializer.Serialize(contentInfo, AsnEncodingRules.DER); + return writer.Encode(); + } + + public void Decode(byte[] encodedMessage) + { + if (encodedMessage == null) + throw new ArgumentNullException(nameof(encodedMessage)); + + // Windows (and thus NetFx) reads the leading data and ignores extra. + // The deserializer will complain if too much data is given, so use the reader + // to ask how much we want to deserialize. + AsnReader reader = new AsnReader(encodedMessage, AsnEncodingRules.BER); + ReadOnlyMemory cmsSegment = reader.GetEncodedValue(); + + ContentInfoAsn contentInfo = AsnSerializer.Deserialize(cmsSegment, AsnEncodingRules.BER); + + if (contentInfo.ContentType != Oids.Pkcs7Signed) + { + throw new CryptographicException(SR.Cryptography_Cms_InvalidMessageType); + } + + // Hold a copy of the SignedData memory so we are protected against memory reuse by the caller. + _heldData = contentInfo.Content.ToArray(); + _signedData = AsnSerializer.Deserialize(_heldData, AsnEncodingRules.BER); + _contentType = _signedData.EncapContentInfo.ContentType; + + if (!Detached) + { + ReadOnlyMemory? content = _signedData.EncapContentInfo.Content; + + // This is in _heldData, so we don't need a defensive copy. + _heldContent = content ?? ReadOnlyMemory.Empty; + + // The ContentInfo object/property DOES need a defensive copy, because + // a) it is mutable by the user, and + // b) it is no longer authoritative + // + // (and c: it takes a byte[] and we have a ReadOnlyMemory) + ContentInfo = new ContentInfo(new Oid(_contentType), _heldContent.Value.ToArray()); + } + else + { + // Hold a defensive copy of the content bytes, (Windows/NetFx compat) + _heldContent = ContentInfo.Content.CloneByteArray(); + } + + Version = _signedData.Version; + _hasData = true; + } + + public void ComputeSignature() + { + throw new PlatformNotSupportedException(SR.Cryptography_Cms_NoSignerCert); + } + + public void ComputeSignature(CmsSigner signer) => ComputeSignature(signer, true); + + public void ComputeSignature(CmsSigner signer, bool silent) + { + if (signer == null) + { + throw new ArgumentNullException(nameof(signer)); + } + + // While it shouldn't be possible to change the length of ContentInfo.Content + // after it's built, use the property at this stage, then use the saved value + // (if applicable) after this point. + if (ContentInfo.Content.Length == 0) + { + throw new CryptographicException(SR.Cryptography_Cms_Sign_Empty_Content); + } + + // If we had content already, use that now. + // (The second signer doesn't inherit edits to signedCms.ContentInfo.Content) + ReadOnlyMemory content = _heldContent ?? ContentInfo.Content; + string contentType = _contentType ?? ContentInfo.ContentType.Value; + + X509Certificate2Collection chainCerts; + SignerInfoAsn newSigner = signer.Sign(content, contentType, silent, out chainCerts); + bool firstSigner = false; + + if (!_hasData) + { + firstSigner = true; + + _signedData = new SignedDataAsn + { + DigestAlgorithms = Array.Empty(), + SignerInfos = Array.Empty(), + EncapContentInfo = new EncapsulatedContentInfoAsn { ContentType = contentType }, + }; + + // Since we're going to call Decode before this method exits we don't need to save + // the copy of _heldContent or _contentType here if we're attached. + if (!Detached) + { + _signedData.EncapContentInfo.Content = content; + } + + _hasData = true; + } + + int newIdx = _signedData.SignerInfos.Length; + Array.Resize(ref _signedData.SignerInfos, newIdx + 1); + _signedData.SignerInfos[newIdx] = newSigner; + UpdateCertificatesFromAddition(chainCerts); + ConsiderDigestAddition(newSigner.DigestAlgorithm); + UpdateMetadata(); + + if (firstSigner) + { + Reencode(); + + Debug.Assert(_heldContent != null); + Debug.Assert(_contentType == contentType); + } + } + + public void RemoveSignature(int index) + { + if (!_hasData) + { + throw new InvalidOperationException(SR.Cryptography_Cms_MessageNotSigned); + } + + if (index < 0 || index >= _signedData.SignerInfos.Length) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + } + + AlgorithmIdentifierAsn signerAlgorithm = _signedData.SignerInfos[index].DigestAlgorithm; + Helpers.RemoveAt(ref _signedData.SignerInfos, index); + + ConsiderDigestRemoval(signerAlgorithm); + UpdateMetadata(); + } + + public void RemoveSignature(SignerInfo signerInfo) + { + if (signerInfo == null) + throw new ArgumentNullException(nameof(signerInfo)); + + int idx = SignerInfos.FindIndexForSigner(signerInfo); + + if (idx < 0) + { + throw new CryptographicException(SR.Cryptography_Cms_SignerNotFound); + } + + RemoveSignature(idx); + } + + internal ReadOnlySpan GetContentSpan() => _heldContent.Value.Span; + + internal void Reencode() + { + // When NetFx re-encodes it just resets the CMS handle, the ContentInfo property + // does not get changed. + // See ReopenToDecode + ContentInfo save = ContentInfo; + + try + { + byte[] encoded = Encode(); + + if (Detached) + { + // At this point the _heldContent becomes whatever ContentInfo says it should be. + _heldContent = null; + } + + Decode(encoded); + Debug.Assert(_heldContent != null); + } + finally + { + ContentInfo = save; + } + } + + private void UpdateMetadata() + { + // Version 5: any certificate of type Other or CRL of type Other. We don't support this. + // Version 4: any certificates are V2 attribute certificates. We don't support this. + // Version 3a: any certificates are V1 attribute certificates. We don't support this. + // Version 3b: any signerInfos are v3 + // Version 3c: eContentType != data + // Version 2: does not exist for signed-data + // Version 1: default + + // The versions 3 are OR conditions, so we need to check the content type and the signerinfos. + int version = 1; + + if ((_contentType ?? ContentInfo.ContentType.Value) != Oids.Pkcs7Data) + { + version = 3; + } + else if (_signedData.SignerInfos.Any(si => si.Version == 3)) + { + version = 3; + } + + Version = version; + _signedData.Version = version; + } + + private void ConsiderDigestAddition(AlgorithmIdentifierAsn candidate) + { + int curLength = _signedData.DigestAlgorithms.Length; + + for (int i = 0; i < curLength; i++) + { + ref AlgorithmIdentifierAsn alg = ref _signedData.DigestAlgorithms[i]; + + if (candidate.Equals(ref alg)) + { + return; + } + } + + Array.Resize(ref _signedData.DigestAlgorithms, curLength + 1); + _signedData.DigestAlgorithms[curLength] = candidate; + } + + private void ConsiderDigestRemoval(AlgorithmIdentifierAsn candidate) + { + bool remove = true; + + for (int i = 0; i < _signedData.SignerInfos.Length; i++) + { + ref AlgorithmIdentifierAsn signerAlg = ref _signedData.SignerInfos[i].DigestAlgorithm; + + if (candidate.Equals(ref signerAlg)) + { + remove = false; + break; + } + } + + if (!remove) + { + return; + } + + for (int i = 0; i < _signedData.DigestAlgorithms.Length; i++) + { + ref AlgorithmIdentifierAsn alg = ref _signedData.DigestAlgorithms[i]; + + if (candidate.Equals(ref alg)) + { + Helpers.RemoveAt(ref _signedData.DigestAlgorithms, i); + break; + } + } + } + + internal void UpdateCertificatesFromAddition(X509Certificate2Collection newCerts) + { + if (newCerts.Count == 0) + { + return; + } + + int existingLength = _signedData.CertificateSet?.Length ?? 0; + + if (existingLength > 0 || newCerts.Count > 1) + { + var certs = new HashSet(Certificates.OfType()); + + for (int i = 0; i < newCerts.Count; i++) + { + X509Certificate2 candidate = newCerts[i]; + + if (!certs.Add(candidate)) + { + newCerts.RemoveAt(i); + i--; + } + } + } + + if (newCerts.Count == 0) + { + return; + } + + if (_signedData.CertificateSet == null) + { + _signedData.CertificateSet = new CertificateChoiceAsn[newCerts.Count]; + } + else + { + Array.Resize(ref _signedData.CertificateSet, existingLength + newCerts.Count); + } + + for (int i = existingLength; i < _signedData.CertificateSet.Length; i++) + { + _signedData.CertificateSet[i] = new CertificateChoiceAsn + { + Certificate = newCerts[i - existingLength].RawData + }; + } + } + + public void CheckSignature(bool verifySignatureOnly) => + CheckSignature(new X509Certificate2Collection(), verifySignatureOnly); + + public void CheckSignature(X509Certificate2Collection extraStore, bool verifySignatureOnly) + { + if (!_hasData) + throw new InvalidOperationException(SR.Cryptography_Cms_MessageNotSigned); + if (extraStore == null) + throw new ArgumentNullException(nameof(extraStore)); + + CheckSignatures(SignerInfos, extraStore, verifySignatureOnly); + } + + private static void CheckSignatures( + SignerInfoCollection signers, + X509Certificate2Collection extraStore, + bool verifySignatureOnly) + { + Debug.Assert(signers != null); + + if (signers.Count < 1) + { + throw new CryptographicException(SR.Cryptography_Cms_NoSignerAtIndex); + } + + foreach (SignerInfo signer in signers) + { + signer.CheckSignature(extraStore, verifySignatureOnly); + + SignerInfoCollection counterSigners = signer.CounterSignerInfos; + + if (counterSigners.Count > 0) + { + CheckSignatures(counterSigners, extraStore, verifySignatureOnly); + } + } + } + + public void CheckHash() + { + if (!_hasData) + throw new InvalidOperationException(SR.Cryptography_Cms_MessageNotSigned); + + SignerInfoCollection signers = SignerInfos; + Debug.Assert(signers != null); + + if (signers.Count < 1) + { + throw new CryptographicException(SR.Cryptography_Cms_NoSignerAtIndex); + } + + foreach (SignerInfo signer in signers) + { + if (signer.SignerIdentifier.Type == SubjectIdentifierType.NoSignature) + { + signer.CheckHash(); + } + } + } + + internal ref SignedDataAsn GetRawData() + { + return ref _signedData; + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignerInfo.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignerInfo.cs new file mode 100644 index 0000000000..31b22a8c2f --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignerInfo.cs @@ -0,0 +1,663 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Security.Cryptography.Asn1; +using System.Security.Cryptography.Pkcs.Asn1; +using System.Security.Cryptography.X509Certificates; +using System.Security.Cryptography.Xml; + +using Internal.Cryptography; + +namespace System.Security.Cryptography.Pkcs +{ + public sealed class SignerInfo + { + public int Version { get; } + public SubjectIdentifier SignerIdentifier { get; } + + private readonly Oid _digestAlgorithm; + private readonly AttributeAsn[] _signedAttributes; + private readonly Oid _signatureAlgorithm; + private readonly ReadOnlyMemory? _signatureAlgorithmParameters; + private readonly ReadOnlyMemory _signature; + private readonly AttributeAsn[] _unsignedAttributes; + + private readonly SignedCms _document; + private X509Certificate2 _signerCertificate; + private SignerInfo _parentSignerInfo; + private CryptographicAttributeObjectCollection _parsedSignedAttrs; + private CryptographicAttributeObjectCollection _parsedUnsignedAttrs; + + internal SignerInfo(ref SignerInfoAsn parsedData, SignedCms ownerDocument) + { + Version = parsedData.Version; + SignerIdentifier = new SubjectIdentifier(parsedData.Sid); + _digestAlgorithm = parsedData.DigestAlgorithm.Algorithm; + _signedAttributes = parsedData.SignedAttributes; + _signatureAlgorithm = parsedData.SignatureAlgorithm.Algorithm; + _signatureAlgorithmParameters = parsedData.SignatureAlgorithm.Parameters; + _signature = parsedData.SignatureValue; + _unsignedAttributes = parsedData.UnsignedAttributes; + + _document = ownerDocument; + } + + public CryptographicAttributeObjectCollection SignedAttributes + { + get + { + if (_parsedSignedAttrs == null) + { + _parsedSignedAttrs = MakeAttributeCollection(_signedAttributes); + } + + return _parsedSignedAttrs; + } + } + + public CryptographicAttributeObjectCollection UnsignedAttributes + { + get + { + if (_parsedUnsignedAttrs == null) + { + _parsedUnsignedAttrs = MakeAttributeCollection(_unsignedAttributes); + } + + return _parsedUnsignedAttrs; + } + } + + public byte[] GetSignature() => _signature.ToArray(); + + public X509Certificate2 Certificate + { + get + { + if (_signerCertificate == null) + { + _signerCertificate = FindSignerCertificate(); + } + + return _signerCertificate; + } + } + + public SignerInfoCollection CounterSignerInfos + { + get + { + // We only support one level of counter signing. + if (_parentSignerInfo != null || + _unsignedAttributes == null || + _unsignedAttributes.Length == 0) + { + return new SignerInfoCollection(); + } + + return GetCounterSigners(_unsignedAttributes); + } + } + + public Oid DigestAlgorithm => new Oid(_digestAlgorithm); + + public Oid SignatureAlgorithm => new Oid(_signatureAlgorithm); + + private SignerInfoCollection GetCounterSigners(AttributeAsn[] unsignedAttrs) + { + // Since each "attribute" can have multiple "attribute values" there's no real + // correlation to a predictive size here. + List signerInfos = new List(); + + foreach (AttributeAsn attributeAsn in unsignedAttrs) + { + if (attributeAsn.AttrType.Value == Oids.CounterSigner) + { + AsnReader reader = new AsnReader(attributeAsn.AttrValues, AsnEncodingRules.BER); + AsnReader collReader = reader.ReadSetOf(); + + if (reader.HasData) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + while (collReader.HasData) + { + SignerInfoAsn parsedData = + AsnSerializer.Deserialize(collReader.GetEncodedValue(), AsnEncodingRules.BER); + + SignerInfo signerInfo = new SignerInfo(ref parsedData, _document) + { + _parentSignerInfo = this + }; + + signerInfos.Add(signerInfo); + } + } + } + + return new SignerInfoCollection(signerInfos.ToArray()); + } + + public void ComputeCounterSignature() + { + throw new PlatformNotSupportedException(SR.Cryptography_Cms_NoSignerCert); + } + + public void ComputeCounterSignature(CmsSigner signer) + { + if (_parentSignerInfo != null) + throw new CryptographicException(SR.Cryptography_Cms_NoCounterCounterSigner); + if (signer == null) + throw new ArgumentNullException(nameof(signer)); + + signer.CheckCertificateValue(); + + int myIdx = _document.SignerInfos.FindIndexForSigner(this); + + if (myIdx < 0) + { + throw new CryptographicException(SR.Cryptography_Cms_SignerNotFound); + } + + // Make sure that we're using the most up-to-date version of this that we can. + SignerInfo effectiveThis = _document.SignerInfos[myIdx]; + X509Certificate2Collection chain; + SignerInfoAsn newSignerInfo = signer.Sign(effectiveThis._signature, null, false, out chain); + + AttributeAsn newUnsignedAttr; + + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + { + writer.PushSetOf(); + AsnSerializer.Serialize(newSignerInfo, writer); + writer.PopSetOf(); + + newUnsignedAttr = new AttributeAsn + { + AttrType = new Oid(Oids.CounterSigner, Oids.CounterSigner), + AttrValues = writer.Encode(), + }; + } + + ref SignedDataAsn signedData = ref _document.GetRawData(); + ref SignerInfoAsn mySigner = ref signedData.SignerInfos[myIdx]; + + int newExtensionIdx; + + if (mySigner.UnsignedAttributes == null) + { + mySigner.UnsignedAttributes = new AttributeAsn[1]; + newExtensionIdx = 0; + } + else + { + newExtensionIdx = mySigner.UnsignedAttributes.Length; + Array.Resize(ref mySigner.UnsignedAttributes, newExtensionIdx + 1); + } + + mySigner.UnsignedAttributes[newExtensionIdx] = newUnsignedAttr; + _document.UpdateCertificatesFromAddition(chain); + // Re-normalize the document + _document.Reencode(); + } + + public void RemoveCounterSignature(int index) + { + if (index < 0) + { + // In NetFx RemoveCounterSignature doesn't bounds check, but the helper it calls does. + // In the helper the argument is called "childIndex". + throw new ArgumentOutOfRangeException("childIndex"); + } + + // The SignerInfo class is a projection of data contained within the SignedCms. + // The projection is applied at construction time, and is not live. + // So RemoveCounterSignature modifies _document, not this. + // (Because that's what NetFx does) + + int myIdx = _document.SignerInfos.FindIndexForSigner(this); + + // We've been removed. + if (myIdx < 0) + { + throw new CryptographicException(SR.Cryptography_Cms_SignerNotFound); + } + + ref SignedDataAsn parentData = ref _document.GetRawData(); + ref SignerInfoAsn myData = ref parentData.SignerInfos[myIdx]; + + if (myData.UnsignedAttributes == null) + { + throw new CryptographicException(SR.Cryptography_Cms_NoSignerAtIndex); + } + + int removeAttrIdx = -1; + int removeValueIndex = -1; + bool removeWholeAttr = false; + int csIndex = 0; + + AttributeAsn[] unsignedAttrs = myData.UnsignedAttributes; + + for (var i = 0; i < unsignedAttrs.Length; i++) + { + AttributeAsn attributeAsn = unsignedAttrs[i]; + + if (attributeAsn.AttrType.Value == Oids.CounterSigner) + { + AsnReader reader = new AsnReader(attributeAsn.AttrValues, AsnEncodingRules.BER); + AsnReader collReader = reader.ReadSetOf(); + + if (reader.HasData) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + int j = 0; + + while (collReader.HasData) + { + collReader.GetEncodedValue(); + + if (csIndex == index) + { + removeAttrIdx = i; + removeValueIndex = j; + } + + csIndex++; + j++; + } + + if (removeValueIndex == 0 && j == 1) + { + removeWholeAttr = true; + } + + if (removeAttrIdx >= 0) + { + break; + } + } + } + + if (removeAttrIdx < 0) + { + throw new CryptographicException(SR.Cryptography_Cms_NoSignerAtIndex); + } + + // The easy path: + if (removeWholeAttr) + { + // Empty needs to normalize to null. + if (unsignedAttrs.Length == 1) + { + myData.UnsignedAttributes = null; + } + else + { + Helpers.RemoveAt(ref myData.UnsignedAttributes, removeAttrIdx); + } + } + else + { + ref AttributeAsn modifiedAttr = ref unsignedAttrs[removeAttrIdx]; + + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + { + writer.PushSetOf(); + + AsnReader reader = new AsnReader(modifiedAttr.AttrValues, writer.RuleSet); + + int i = 0; + + while (reader.HasData) + { + ReadOnlyMemory encodedValue = reader.GetEncodedValue(); + + if (i != removeValueIndex) + { + writer.WriteEncodedValue(encodedValue); + } + + i++; + } + + writer.PopSetOf(); + modifiedAttr.AttrValues = writer.Encode(); + } + } + } + + public void RemoveCounterSignature(SignerInfo counterSignerInfo) + { + if (counterSignerInfo == null) + throw new ArgumentNullException(nameof(counterSignerInfo)); + + SignerInfoCollection docSigners = _document.SignerInfos; + int index = docSigners.FindIndexForSigner(this); + + if (index < 0) + { + throw new CryptographicException(SR.Cryptography_Cms_SignerNotFound); + } + + SignerInfo liveThis = docSigners[index]; + index = liveThis.CounterSignerInfos.FindIndexForSigner(counterSignerInfo); + + if (index < 0) + { + throw new CryptographicException(SR.Cryptography_Cms_SignerNotFound); + } + + RemoveCounterSignature(index); + } + + public void CheckSignature(bool verifySignatureOnly) => + CheckSignature(new X509Certificate2Collection(), verifySignatureOnly); + + public void CheckSignature(X509Certificate2Collection extraStore, bool verifySignatureOnly) + { + if (extraStore == null) + throw new ArgumentNullException(nameof(extraStore)); + + X509Certificate2 certificate = Certificate; + + if (certificate == null) + { + certificate = FindSignerCertificate(SignerIdentifier, extraStore); + + if (certificate == null) + { + throw new CryptographicException(SR.Cryptography_Cms_SignerNotFound); + } + } + + Verify(extraStore, certificate, verifySignatureOnly); + } + + public void CheckHash() + { + IncrementalHash hasher = PrepareDigest(); + byte[] expectedSignature = hasher.GetHashAndReset(); + + if (!_signature.Span.SequenceEqual(expectedSignature)) + { + throw new CryptographicException(SR.Cryptography_BadSignature); + } + } + + private X509Certificate2 FindSignerCertificate() + { + return FindSignerCertificate(SignerIdentifier, _document.Certificates); + } + + private static X509Certificate2 FindSignerCertificate( + SubjectIdentifier signerIdentifier, + X509Certificate2Collection extraStore) + { + if (extraStore == null || extraStore.Count == 0) + { + return null; + } + + X509Certificate2Collection filtered = null; + X509Certificate2 match = null; + + switch (signerIdentifier.Type) + { + case SubjectIdentifierType.IssuerAndSerialNumber: + { + X509IssuerSerial issuerSerial = (X509IssuerSerial)signerIdentifier.Value; + filtered = extraStore.Find(X509FindType.FindBySerialNumber, issuerSerial.SerialNumber, false); + + foreach (X509Certificate2 cert in filtered) + { + if (cert.IssuerName.Name == issuerSerial.IssuerName) + { + match = cert; + break; + } + } + + break; + } + case SubjectIdentifierType.SubjectKeyIdentifier: + { + filtered = extraStore.Find(X509FindType.FindBySubjectKeyIdentifier, signerIdentifier.Value, false); + + if (filtered.Count > 0) + { + match = filtered[0]; + } + + break; + } + } + + if (filtered != null) + { + foreach (X509Certificate2 cert in filtered) + { + if (!ReferenceEquals(cert, match)) + { + cert.Dispose(); + } + } + } + + return match; + } + + private IncrementalHash PrepareDigest() + { + HashAlgorithmName hashAlgorithmName = GetDigestAlgorithm(); + + IncrementalHash hasher = IncrementalHash.CreateHash(hashAlgorithmName); + + if (_parentSignerInfo == null) + { + // Windows compatibility: If a document was loaded in detached mode, + // but had content, hash both parts of the content. + if (_document.Detached) + { + ref SignedDataAsn documentData = ref _document.GetRawData(); + ReadOnlyMemory? embeddedContent = documentData.EncapContentInfo.Content; + + if (embeddedContent != null) + { + hasher.AppendData(embeddedContent.Value.Span); + } + + } + + hasher.AppendData(_document.GetContentSpan()); + } + else + { + hasher.AppendData(_parentSignerInfo._signature.Span); + } + + // A Counter-Signer always requires signed attributes. + // If any signed attributes are present, message-digest is required. + bool invalid = _parentSignerInfo != null || _signedAttributes != null; + + if (_signedAttributes != null) + { + byte[] contentDigest = hasher.GetHashAndReset(); + + using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + { + writer.PushSetOf(); + + foreach (AttributeAsn attr in _signedAttributes) + { + AsnSerializer.Serialize(attr, writer); + + // .NET Framework doesn't seem to validate the content type attribute, + // so we won't, either. + + if (attr.AttrType.Value == Oids.MessageDigest) + { + CryptographicAttributeObject obj = MakeAttribute(attr); + + if (obj.Values.Count != 1) + { + throw new CryptographicException(SR.Cryptography_BadHashValue); + } + + var digestAttr = (Pkcs9MessageDigest)obj.Values[0]; + + if (!contentDigest.AsSpan().SequenceEqual(digestAttr.MessageDigest.AsReadOnlySpan())) + { + throw new CryptographicException(SR.Cryptography_BadHashValue); + } + + invalid = false; + } + } + + writer.PopSetOf(); + Helpers.DigestWriter(hasher, writer); + } + } + + if (invalid) + { + throw new CryptographicException(SR.Cryptography_Cms_MissingAuthenticatedAttribute); + } + + return hasher; + } + + private void Verify( + X509Certificate2Collection extraStore, + X509Certificate2 certificate, + bool verifySignatureOnly) + { + IncrementalHash hasher = PrepareDigest(); + CmsSignature signatureProcessor = CmsSignature.Resolve(SignatureAlgorithm.Value); + + if (signatureProcessor == null) + { + throw new CryptographicException(SR.Cryptography_Cms_UnknownAlgorithm, SignatureAlgorithm.Value); + } + +#if netcoreapp + // SHA-2-512 is the biggest digest type we know about. + Span digestValue = stackalloc byte[512 / 8]; + ReadOnlySpan digest = digestValue; + ReadOnlyMemory signature = _signature; + + if (hasher.TryGetHashAndReset(digestValue, out int bytesWritten)) + { + digest = digestValue.Slice(0, bytesWritten); + } + else + { + digest = hasher.GetHashAndReset(); + } +#else + byte[] digest = hasher.GetHashAndReset(); + byte[] signature = _signature.ToArray(); +#endif + + bool signatureValid = signatureProcessor.VerifySignature( + digest, + signature, + DigestAlgorithm.Value, + hasher.AlgorithmName, + _signatureAlgorithmParameters, + certificate); + + if (!signatureValid) + { + throw new CryptographicException(SR.Cryptography_BadSignature); + } + + if (!verifySignatureOnly) + { + X509Chain chain = new X509Chain(); + chain.ChainPolicy.ExtraStore.AddRange(extraStore); + chain.ChainPolicy.RevocationMode = X509RevocationMode.Online; + chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; + + if (!chain.Build(certificate)) + { + X509ChainStatus status = chain.ChainStatus.FirstOrDefault(); + throw new CryptographicException(SR.Cryptography_Cms_TrustFailure, status.StatusInformation); + } + + // NetFx checks for either of these + const X509KeyUsageFlags SufficientFlags = + X509KeyUsageFlags.DigitalSignature | + X509KeyUsageFlags.NonRepudiation; + + foreach (X509Extension ext in certificate.Extensions) + { + if (ext.Oid.Value == Oids.KeyUsage) + { + if (!(ext is X509KeyUsageExtension keyUsage)) + { + keyUsage = new X509KeyUsageExtension(); + keyUsage.CopyFrom(ext); + } + + if ((keyUsage.KeyUsages & SufficientFlags) == 0) + { + throw new CryptographicException(SR.Cryptography_Cms_WrongKeyUsage); + } + } + } + } + } + + private HashAlgorithmName GetDigestAlgorithm() + { + return Helpers.GetDigestAlgorithm(DigestAlgorithm.Value); + } + + private static CryptographicAttributeObjectCollection MakeAttributeCollection(AttributeAsn[] attributes) + { + var coll = new CryptographicAttributeObjectCollection(); + + if (attributes == null) + return coll; + + foreach (AttributeAsn attribute in attributes) + { + coll.AddWithoutMerge(MakeAttribute(attribute)); + } + + return coll; + } + + private static CryptographicAttributeObject MakeAttribute(AttributeAsn attribute) + { + Oid type = new Oid(attribute.AttrType); + + ReadOnlyMemory attrSetBytes = attribute.AttrValues; + + AsnReader reader = new AsnReader(attrSetBytes, AsnEncodingRules.BER); + AsnReader collReader = reader.ReadSetOf(); + + if (reader.HasData) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + AsnEncodedDataCollection valueColl = new AsnEncodedDataCollection(); + + while (collReader.HasData) + { + byte[] attrBytes = collReader.GetEncodedValue().ToArray(); + valueColl.Add(Helpers.CreateBestPkcs9AttributeObjectAvailable(type, attrBytes)); + } + + return new CryptographicAttributeObject(type, valueColl); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignerInfoCollection.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignerInfoCollection.cs new file mode 100644 index 0000000000..b221876e8b --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignerInfoCollection.cs @@ -0,0 +1,132 @@ +// 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.Collections; +using System.Diagnostics; +using System.Security.Cryptography.Pkcs.Asn1; +using System.Security.Cryptography.Xml; + +namespace System.Security.Cryptography.Pkcs +{ + public sealed class SignerInfoCollection : ICollection, IEnumerable + { + private readonly SignerInfo[] _signerInfos; + + internal SignerInfoCollection() + { + _signerInfos = Array.Empty(); + } + + internal SignerInfoCollection(SignerInfo[] signerInfos) + { + Debug.Assert(signerInfos != null); + + _signerInfos = signerInfos; + } + + internal SignerInfoCollection(SignerInfoAsn[] signedDataSignerInfos, SignedCms ownerDocument) + { + Debug.Assert(signedDataSignerInfos != null); + + _signerInfos = new SignerInfo[signedDataSignerInfos.Length]; + + for (int i = 0; i < signedDataSignerInfos.Length; i++) + { + _signerInfos[i] = new SignerInfo(ref signedDataSignerInfos[i], ownerDocument); + } + } + + public SignerInfo this[int index] => _signerInfos[index]; + + public int Count => _signerInfos.Length; + + public SignerInfoEnumerator GetEnumerator() => new SignerInfoEnumerator(this); + IEnumerator IEnumerable.GetEnumerator() => new SignerInfoEnumerator(this); + + public void CopyTo(Array array, int index) + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + if (array.Rank != 1) + throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); + if (index < 0 || index >= array.Length) + throw new ArgumentOutOfRangeException(nameof(index),SR.ArgumentOutOfRange_Index); + if (index + Count > array.Length) + throw new ArgumentException(SR.Argument_InvalidOffLen); + + for (int i = 0; i < Count; i++) + { + array.SetValue(this[i], index + i); + } + } + + // The collections are usually small (usually Count == 1) so there's not value in repeating + // the validation of the Array overload to defer to a faster copy routine. + public void CopyTo(SignerInfo[] array, int index) => ((ICollection)this).CopyTo(array, index); + + public bool IsSynchronized => false; + public object SyncRoot => this; + + internal int FindIndexForSigner(SignerInfo signer) + { + Debug.Assert(signer != null); + SubjectIdentifier id = signer.SignerIdentifier; + X509IssuerSerial issuerSerial = default; + + if (id.Type == SubjectIdentifierType.IssuerAndSerialNumber) + { + issuerSerial = (X509IssuerSerial)id.Value; + } + + for (int i = 0; i < _signerInfos.Length; i++) + { + SignerInfo current = _signerInfos[i]; + SubjectIdentifier currentId = current.SignerIdentifier; + + if (currentId.Type != id.Type) + { + continue; + } + + bool equal = false; + + switch (id.Type) + { + case SubjectIdentifierType.IssuerAndSerialNumber: + { + X509IssuerSerial currentIssuerSerial = (X509IssuerSerial)currentId.Value; + + if (currentIssuerSerial.IssuerName == issuerSerial.IssuerName && + currentIssuerSerial.SerialNumber == issuerSerial.SerialNumber) + { + equal = true; + } + + break; + } + case SubjectIdentifierType.SubjectKeyIdentifier: + if ((string)id.Value == (string)currentId.Value) + { + equal = true; + } + + break; + case SubjectIdentifierType.NoSignature: + equal = true; + break; + default: + Debug.Fail($"No match logic for SubjectIdentifierType {id.Type}"); + throw new CryptographicException(); + } + + if (equal) + { + return i; + } + } + + return -1; + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignerInfoEnumerator.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignerInfoEnumerator.cs new file mode 100644 index 0000000000..d3c483bc66 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignerInfoEnumerator.cs @@ -0,0 +1,44 @@ +// 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.Collections; +using System.Diagnostics; + +namespace System.Security.Cryptography.Pkcs +{ + public sealed class SignerInfoEnumerator : IEnumerator + { + private readonly SignerInfoCollection _signerInfos; + private int _position; + + private SignerInfoEnumerator() { } + + internal SignerInfoEnumerator(SignerInfoCollection signerInfos) + { + Debug.Assert(signerInfos != null); + + _signerInfos = signerInfos; + _position = -1; + } + + public SignerInfo Current => _signerInfos[_position]; + object IEnumerator.Current => _signerInfos[_position]; + + public bool MoveNext() + { + int next = _position + 1; + + if (next >= _signerInfos.Count) + return false; + + _position = next; + return true; + } + + public void Reset() + { + _position = -1; + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SubjectIdentifier.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SubjectIdentifier.cs index 175279d006..891b072762 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SubjectIdentifier.cs +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SubjectIdentifier.cs @@ -2,14 +2,24 @@ // 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.Diagnostics; +using System.Linq; +using System.Security.Cryptography.Asn1; +using System.Security.Cryptography.Pkcs.Asn1; +using System.Security.Cryptography.X509Certificates; + +using Internal.Cryptography; + using X509IssuerSerial = System.Security.Cryptography.Xml.X509IssuerSerial; namespace System.Security.Cryptography.Pkcs { public sealed class SubjectIdentifier { + private const string DummySignerSubjectName = "CN=Dummy Signer"; + internal static readonly byte[] DummySignerEncodedValue = + new X500DistinguishedName(DummySignerSubjectName).RawData; + internal SubjectIdentifier(SubjectIdentifierType type, object value) { Debug.Assert(value != null); @@ -33,6 +43,52 @@ namespace System.Security.Cryptography.Pkcs Value = value; } + internal SubjectIdentifier(SignerIdentifierAsn signerIdentifierAsn) + { + if (signerIdentifierAsn.IssuerAndSerialNumber.HasValue) + { + ReadOnlySpan issuerNameSpan = signerIdentifierAsn.IssuerAndSerialNumber.Value.Issuer.Span; + ReadOnlySpan serial = signerIdentifierAsn.IssuerAndSerialNumber.Value.SerialNumber.Span; + + bool nonZero = false; + + for (int i = 0; i < serial.Length; i++) + { + if (serial[i] != 0) + { + nonZero = true; + break; + } + } + + // If the serial number is zero and the subject is exactly "CN=Dummy Signer" + // then this is the special "NoSignature" signer. + if (!nonZero && + DummySignerEncodedValue.AsReadOnlySpan().SequenceEqual(issuerNameSpan)) + { + Type = SubjectIdentifierType.NoSignature; + Value = null; + } + else + { + Type = SubjectIdentifierType.IssuerAndSerialNumber; + + var name = new X500DistinguishedName(issuerNameSpan.ToArray()); + Value = new X509IssuerSerial(name.Name, serial.ToSkiString()); + } + } + else if (signerIdentifierAsn.SubjectKeyIdentifier != null) + { + Type = SubjectIdentifierType.SubjectKeyIdentifier; + Value = signerIdentifierAsn.SubjectKeyIdentifier.Value.Span.ToSkiString(); + } + else + { + Debug.Fail("Do not know how to decode value"); + throw new CryptographicException(); + } + } + public SubjectIdentifierType Type { get; } public object Value { get; } } diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/tests/CertLoader.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/CertLoader.cs index 629c7df756..19cd19a2bd 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/tests/CertLoader.cs +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/CertLoader.cs @@ -3,21 +3,44 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; namespace Test.Cryptography { internal abstract partial class CertLoader { + private static readonly X509KeyStorageFlags s_defaultKeyStorageFlags = GetBestKeyStorageFlags(); + // Prefer ephemeral when available - protected X509KeyStorageFlags KeyStorageFlags = + private static X509KeyStorageFlags GetBestKeyStorageFlags() + { #if netcoreapp - X509KeyStorageFlags.EphemeralKeySet; -#else - X509KeyStorageFlags.DefaultKeySet; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // On Windows 7 ephemeral keys with a key usage embedded in the PFX + // are treated differently than Windows 8. So just use the default + // storage flags for Win7. + Version win8 = new Version(6, 2, 9200); + + if (Environment.OSVersion.Version >= win8) + { + return X509KeyStorageFlags.EphemeralKeySet; + } + } + else if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + // OSX doesn't allow ephemeral, but every other Unix does. + return X509KeyStorageFlags.EphemeralKeySet; + } #endif + return X509KeyStorageFlags.DefaultKeySet; + } + + protected X509KeyStorageFlags KeyStorageFlags = s_defaultKeyStorageFlags; + /// /// Returns a freshly allocated X509Certificate2 instance that has a public key only. /// @@ -106,10 +129,18 @@ namespace Test.Cryptography using (X509Certificate2 cer = new X509Certificate2(CerData)) { X509Certificate2Collection matches = new X509Certificate2Collection(); + using (X509Store store = new X509Store(storeName, storeLocation)) { - store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); - + try + { + store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); + } + catch (CryptographicException) + { + return matches; + } + foreach (X509Certificate2 candidate in store.Certificates) { // X509Certificate2.Equals() compares issuer and serial. diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/tests/Certificates.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/Certificates.cs index b2645c46c2..4644b764f4 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/tests/Certificates.cs +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/Certificates.cs @@ -2,10 +2,6 @@ // 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.IO; -using System.Text; -using Xunit; - using Test.Cryptography; namespace System.Security.Cryptography.Pkcs.Tests @@ -20,6 +16,9 @@ namespace System.Security.Cryptography.Pkcs.Tests public static readonly CertLoader RSASha384KeyTransfer1 = new CertLoaderFromRawData(RawData.s_RSASha384KeyTransfer1Cer, RawData.s_RSASha384KeyTransfer1Pfx, "1111"); public static readonly CertLoader RSASha512KeyTransfer1 = new CertLoaderFromRawData(RawData.s_RSASha512KeyTransfer1Cer, RawData.s_RSASha512KeyTransfer1Pfx, "1111"); public static readonly CertLoader DHKeyAgree1 = new CertLoaderFromRawData(RawData.s_DHKeyAgree1Cer); + public static readonly CertLoader RSA2048SignatureOnly = new CertLoaderFromRawData(RawData.s_Rsa2048SignatureOnlyCer, RawData.s_Rsa2048SignatureOnlyPfx, "12345"); + public static readonly CertLoader Dsa1024 = new CertLoaderFromRawData(RawData.s_dsa1024Cert, RawData.s_dsa1024Pfx, "1234"); + public static readonly CertLoader ECDsaP256Win = new CertLoaderFromRawData(RawData.ECDsaP256_DigitalSignature_Cert, RawData.ECDsaP256_DigitalSignature_Pfx_Windows, "Test"); // Note: the raw data is its own (nested) class to avoid problems with static field initialization ordering. private static class RawData @@ -374,6 +373,256 @@ namespace System.Security.Cryptography.Pkcs.Tests + "28f70f3a0a37358e7917fc94dfeb6e7cb176e8f5dbfa1ace2af6c0a4306e22eb3051e7705306152ce87328b24f7f153d565b" + "73aef677d25ae8657f81ca1cd5dd50404b70b9373eadcd2d276e263105c00607a86f0c10ab26d1aafd986313a36c70389a4d" + "1a8e88").HexToByteArray(); + + public static byte[] s_Rsa2048SignatureOnlyCer = ( + "3082032C30820214A003020102020900E0D8AB6819D7306E300D06092A864886" + + "F70D01010B05003038313630340603550403132D54776F2074686F7573616E64" + + "20666F7274792065696768742062697473206F662052534120676F6F646E6573" + + "73301E170D3137313130333233353131355A170D313831313033323335313135" + + "5A3038313630340603550403132D54776F2074686F7573616E6420666F727479" + + "2065696768742062697473206F662052534120676F6F646E6573733082012230" + + "0D06092A864886F70D01010105000382010F003082010A028201010096C114A5" + + "898D09133EF859F89C1D848BA8CB5258793E05B92D499C55EEFACE274BBBC268" + + "03FB813B9C11C6898153CC1745DED2C4D2672F807F0B2D957BC4B65EBC9DDE26" + + "E2EA7B2A6FE9A7C4D8BD1EF6032B8F0BB6AA33C8B57248B3D5E3901D8A38A283" + + "D7E25FF8E6F522381EE5484234CFF7B30C174635418FA89E14C468AD89DCFCBB" + + "B535E5AF53510F9EA7F9DA8C1B53375B6DAB95A291439A5648726EE1012E4138" + + "8E100691642CF6917F5569D8351F2782F435A579014E8448EEA0C4AECAFF2F47" + + "6799D88457E2C8BCB56E5E128782B4FE26AFF0720D91D52CCAFE344255808F52" + + "71D09F784F787E8323182080915BE0AE15A71D66476D0F264DD084F302030100" + + "01A3393037301D0603551D0E04160414745B5F12EF962E84B897E246D399A2BA" + + "DEA9C5AC30090603551D1304023000300B0603551D0F040403020780300D0609" + + "2A864886F70D01010B0500038201010087A15DF37FBD6E9DED7A8FFF25E60B73" + + "1F635469BA01DD14BC03B2A24D99EFD8B894E9493D63EC88C496CB04B33DF252" + + "22544F23D43F4023612C4D97B719C1F9431E4DB7A580CDF66A3E5F0DAF89A267" + + "DD187ABFFB08361B1F79232376AA5FC5AD384CC2F98FE36C1CEA0B943E1E3961" + + "190648889C8ABE8397A5A338843CBFB1D8B212BE46685ACE7B80475CC7C97FC0" + + "377936ABD5F664E9C09C463897726650711A1110FA9866BC1C278D95E5636AB9" + + "6FAE95CCD67FD572A8C727E2C03E7B242457318BEC1BE52CA5BD9454A0A41140" + + "AE96ED1C56D220D1FD5DD3B1B4FB2AA0E04FC94F7E3C7D476F29896224556395" + + "3AD7225EDCEAC8B8509E49292E62D8BF").HexToByteArray(); + + public static byte[] s_Rsa2048SignatureOnlyPfx = ( + "308209E3020103308209A306092A864886F70D010701A0820994048209903082" + + "098C308205BD06092A864886F70D010701A08205AE048205AA308205A6308205" + + "A2060B2A864886F70D010C0A0102A08204FE308204FA301C060A2A864886F70D" + + "010C0103300E04083EF905F0EA26FBF7020207D0048204D82297B5546DA6CC49" + + "BD8C1444E3FE1845A2C9E9BDB8B83E78235DF4ADF7A97496A62D31D4EEEB76B0" + + "71C0B183ACC3663272F88CF4F31E2E00D76357C0A051B8D6E0BB0BCF4CCDD064" + + "CCBAF546EABA80DA56CD11FE952C61154792559D65F26B0476CF7A5FDB8CC794" + + "B89F6ACD50003459054FE82C48D8791B226A0EEEC01F048AC3CE716C9F3BB313" + + "D64BEBBF0037D83133DD9C15D04F15BB11652D793B613A68AFE580245724E5D1" + + "110040B332B5C39BE04086BA4DFC58E905BC2FE8B3C696181E2879AF197EE24D" + + "91D8AD67013F14C4864C8D0FB19C134B766CF3E48B8C9E363A11EB19F1E82E74" + + "D25EDD96517D64A94314B40C11651030D561E742E63856E8D1A3EE9FDFD6CF64" + + "7140CFC354AE7EA1D14C157C2985F82D54296F7D3DE456AF7513F5F30A0421E4" + + "3A9DAD6DF8A2BF69005B35CA8066F80755D848DA73EF03BC0CC129C5799911D9" + + "3A1ED43F8E76732AF56FD62DC6D0B0DBA6AAC6DCDE77D0E8AC9F6A5EB5A02B61" + + "BC477706D4F1873240AB45E0291EF21E6034D48F1AE8EB139DE7ACD8B8A821E6" + + "70B395C3EC4B0E75C34BF0067F052FCED835CAC1F17C3FBEA2FC9FD281FCDE21" + + "D5B27CF31A07E90164A979ACEF0E1C67DBA6C33082E9B189D4BDA2D2D504776F" + + "455843437BDF10D4AF48639EC03344BCC36EFEA7CDE08D7F94DDEAF98BCB5D65" + + "207AE0C349EECE3032DE19F3B337F29A3457AA0AAF4306FA8301619AB01B7235" + + "BE16CB93728E142DAA6C1CBBCC5BD82D913596994DA40FB916CF2DB5FBCC20CF" + + "E893DC62BBC5FC59E00BC0A704A9DB25BBF9291971F2377FC1A20F2C954869DB" + + "6FFCC90C625AABE97ED4CF7C0209D39AD780003C437152D636ACB3B484C46885" + + "DC1584733D2153A3F9B968F12CDD5937CDF9DD2581D72EE67C83AE2530197AA7" + + "C6110613BEFF0B75E586C28394EA8EBCF7F9DB133295B33DC86C8DBA92BF8BD1" + + "ADCAF8D2CD2E018B08D59FF1C30A13484AB11468F7DCEB1FE53A6DAF309B0510" + + "7772CB735314A5B2F053E60A653F0496BCB9CADF5E50339A4D2EF2382056B768" + + "558EB9230D996C636E6D29664F92F70A088DE3EE4EC4BBD8A9C4C98C7892D122" + + "28806622B87E581A321AD835B8F4B964A17B5BE6D9DA50133D494732A41884E2" + + "9E891FE2D40ACCFD585C8BF626C1E8A412D2EE7CDE060E2CCDA826BF79D80F1B" + + "F6B8400473BCE0C19D03ACF55D1FAA994C04A8CD11D49743B1F45F48DFFDD701" + + "18B5FA82ECDF67714F5DE5D3D3DDDCB76ED0EA6A6E151665A4AA351DB1A99F8C" + + "7502D3795C2C358CCA589C390C1F4810615130B91BA42A85E77FA37197E1B083" + + "FE1246B067C6444D49B369D45B130A6D7B463C3F0459EB41D68009CABD2F5C60" + + "49DB706FA742C9773FB5791AF123FBE485E05F87759ADD25281BE337B6095EC3" + + "4EFF9FC692798FB4217EF4B2B59902D930F28181933FAA278C041123CAE3CA63" + + "6DFD3AD4E04EB751A30D50C26288EA4D01C7B323E4FD6387F88E020BC433BF60" + + "C4406398C44EA5C7A6EB24134B0811E4F94DFAF5553172306FA5543C254E7E04" + + "DEEC84DBF9FAF7BFEA8D61E094CBB18DD45C5BAB9199DD719F9A305E205605CC" + + "671DCD566FEBA2C8F4C1A445625C4F42D1CFE32087F095591798D1D48DA46DE9" + + "230F5102B56A1EF879D48936D5331D6B3D9F1B564CF08FD3C641CFF3B02CB4FC" + + "8995E5EC5DD1D183704940C02DEA7430FD594E54800DCC74B7732731C63FBBA2" + + "A2F6DC031174390A74781D352B09FB4F318190301306092A864886F70D010915" + + "3106040401000000307906092B0601040182371101316C1E6A004D0069006300" + + "72006F0073006F0066007400200045006E00680061006E006300650064002000" + + "520053004100200061006E006400200041004500530020004300720079007000" + + "74006F0067007200610070006800690063002000500072006F00760069006400" + + "650072308203C706092A864886F70D010706A08203B8308203B4020100308203" + + "AD06092A864886F70D010701301C060A2A864886F70D010C0106300E04087CB7" + + "E0256AD1AD80020207D0808203800C21CEBEF9F3765F11A188B56702050E3DCA" + + "78AA27123654D066399DD56E62187C89A30940B5B63493950EEFA06C04B5CAF0" + + "329143AF30EE0B47406E49D4E6241817986F864780B743B58F03DF13523F5C01" + + "C889046356623AFA816B163E57A36672FAC9CA72294B2A17F75F5ADB1A4CBDB7" + + "B3F5C33C643DA0CC00CB79E54FAB25D1881B81C03BA5762BAA551A7E8BA38144" + + "353B07285B288BC2747F75B7AF249040C338CFC585D0B1CECFED46BCAE7FAF09" + + "60BB3EE996E30E626CB544A38393BC7DFDB7A27A21A6CF09332B544F448DF5B3" + + "31E000F7CCD5CE5C8E8765A2339919C713352FCD30FA52B994C25EA95E548C4B" + + "5EC23B3BDEC7342D0676B9227D3405758DBA5BD09F9253791FAA03F158F04848" + + "D5073DD240F466F57770353528B3AE83A626F33D05BD1BBB4E28CB067FFAA97D" + + "B4C79EEAAFB4B30BE738C1AA5DB1830B3968CDF6BAF778494AE40EF003DCDA54" + + "486E9952EB44628385E149C348E0E431928B85608622B994CF43433DA9C19482" + + "360121560E53E85FE7CBB7C31E27AD335BC247F284EAC3CA94C30DBB4DF2AB02" + + "DF1154626838240213D910D5B7476A025CA7565CECBA0051320FC7EECD6C74FF" + + "505566F75804D1E2BD2B0181B235CE911EAD9260C0799C817F956AE290E00EF0" + + "997F7B6BD059B315915D580CF0C019A23A6D4993F6E8B8106A1AB6CE1991B609" + + "1B42B6D33EE01EC96CB475430365E9C710C5EB4C6010260D12108022449F7E6D" + + "1A2F28838304DB2A60B9FF714FC887579A4CDC139DAF30A18D3910D82313CCB1" + + "FA43A8930E0F10DE24652AC1E5B797084BEBDB8AB5FA6DCE03E44ABF35EDEB1A" + + "FFEAD3F7C9CB342CCA2882D945EB52C20DC595FA10161866EB9426281CF13341" + + "311B59FDE8E69F9B853117740D92F4AC1B2E4597D41B8A097E1DAA688FFF1C5C" + + "846DF96CA75224EC26F4FF328164F5D1EC06B697211BDB42E6C97EB294A5798C" + + "0FCE6104C950A5207F74EC0DED8AEE10463EF2D9ACD7473D2BE48EBBF0A550B9" + + "AA19A465147B378B078229E8804918136633D7FCE5340AC61A1418D7D9BB18D1" + + "98B7B7866C4D7DC562B1F93F3F322484BDDCEB23680B8EB9904EC783D5CD7177" + + "CFE9CA9D1893104E97760E871DE0907D4BDF6263E7BB0F47414AF31E377C7447" + + "B881E68AE3E80D06597F12D5EF5ED861D055D494D89F04A70800DA3FD4E53877" + + "87FBEED7B772E3A24E7F4832A956FEC0C81847C68373ED4760ABF542F77DC794" + + "249519BDDF5F846EB8C5078BCC053037301F300706052B0E03021A0414461F5B" + + "19C6933240012EFEB95F734C648CCD13460414FA1743400686D25BA1CB28D736" + + "F2B1ED97699EA4").HexToByteArray(); + + public static byte[] s_dsa1024Pfx = ( + "308206EE020103308206B406092A864886F70D010701A08206A5048206A13082" + + "069D3082043706092A864886F70D010706A0820428308204240201003082041D" + + "06092A864886F70D010701301C060A2A864886F70D010C0106300E04084AF212" + + "89D5D7E2E702020800808203F0DECCF218AC91F26BAB026998AB77C7629D20DB" + + "E2FB7022A3C4A1CECD743C0F932E944AE229DAFB61AD76C4DEB6995DF4F4BA01" + + "2DBAD5C63A4C846E0807FCA0BC4A162CDFBAB4B3C4D304F473B3ACC1D268436E" + + "F537DAE97ECC3C634C8DF2A294CC23E904A169F369021A0C024A03DE98A65B0F" + + "3F14D6910525D76AD98B91E67BB7398E245CF48A4D2A5603CFCCF4E547D7EDAB" + + "669D9A8597C6839119EB9FD932D1E4BA8B45D3317186CDA2EFF247BCFD64A5CA" + + "ED604BF7033E423CC21CEC6454FE3B74E03A26C51A1C3519CE339FBE9F10B81D" + + "DF6A0AAB4F8166D90B6F52B3439AB4B5273D0A506E3E01869F8FEBD1521EF8E5" + + "BFB357FA630E3C988926EF3ACC0A0F4176FE8A93337C1A5C6DEAB5758EC2F07C" + + "11E8B2495ECDE58D12312CCCA2E8B2EE8564B533D18C7A26A9290394C2A9942C" + + "295EBB0317F5695103627519567960908323FFE6560AD054C97800218A52F37A" + + "DDE4E7F18EF3BF3718A9D7BF57B700DBEB5AB86598C9604A4546995E34DBABBB" + + "6A9FB483A3C2DFE6046DFD54F2D7AC61C062AF04B7FBAC395C5DD19408D6926A" + + "93B896BFB92DA6F7F5A4E54EDBE2CFBB56576878150676ADB0D37E0177B91E0D" + + "F09D7B37769E66842DD40C7B1422127F152A165BC9669168885BA0243C9641B4" + + "48F68575AA6AB9247A49A61AC3C683EE057B7676B9610CF9100096FC46BDC8B9" + + "BAA03535815D5E98BA3ABC1E18E39B50A8AF8D81E30F2DFD6AF5D0F9FC3636AB" + + "69E128C793571723A79E42FC7C1BD7F39BD45FBE9C39EEB010005435BEC19844" + + "2058033D2601B83124BD369DADB831317E0B2C28CE7535A2E89D8A0E5E34E252" + + "3B0FCEC34FF26A2B80566F4D86F958F70106BF3322FA70A3312E48EAA130246A" + + "07412E93FDE91F633F758BC49311F6CBBAEC5D2F22AFCD696F72BC22E7DE6C00" + + "3275DFEC47E3848226FE9DBA184EA711E051B267C584749F897EFE7EAFD02F1D" + + "BF3FD8E882474CA1F45509EF2E7B82F35B677CB88ED42AF729848EE2B424B0CE" + + "2E9AAC945BABA550C20D5B25075A30FE70D8CAA5A527A35F1DF17BCCB91930C1" + + "7120C625667120E0806C2B51EDFF540F928BD555FB48DBCB83CCCE0C385E78C8" + + "65BE715AE6F8BE472E5FC187EBE3FEFD8D7FE62D4DB2EE61F42D24D81FAA9179" + + "0FB17E8EBC8E219B6F9E039F5AB3BC4870821D474B36C8F8D0583D9DC06E4383" + + "D03424420B8C8B26276877166A0F51E22F0D8FA60A070CFBD47EAFBC717C879C" + + "B5A1EA69C4C2A38F26A1EEF96A0C32BFCECCE4EA97E90A425066B1DD0891353F" + + "766EB9F2BFA2563A815DAF3639EBB147E1E8757A6BFAB902C4A8F037AD47E03F" + + "AF2E019FCF6CA7430BDFEA4B45B28ED746BB90E09BEF7B370A75E7924BBA0920" + + "25FE654A9A197A5B8BBBE43DC7C892FF14E75A37EB97FC489AB121A43E308202" + + "5E06092A864886F70D010701A082024F0482024B3082024730820243060B2A86" + + "4886F70D010C0A0102A082017630820172301C060A2A864886F70D010C010330" + + "0E0408ECB4D1550DA52C6302020800048201509322DC0193DD9E79ADAFD38827" + + "AD6DE9299327DDDF6E9DF4FB70D53A64951E4B814E90D2A19B3F4B8E39A2F851" + + "A3E5E9B9EB947DD248A3E5F5EB458F3323D4656709E97C6BD59238C4D1F26AB6" + + "7D73235FAE7780D98705957B6650AC0DE3E2D46E22455D0A105D138F16A84839" + + "14EDDF5C518B748558704ED3AE4A8C4914F667BBDE07978E4A4FC66194F6B86B" + + "AB9F558EDE890C25DFB97C59653906CC573B5DEB62165CFF8A5F4F8059A478EB" + + "F6FED75F1DACDC612C2E271E25A7083E15D33697270FD442D79FFCB25DB135F9" + + "8E580DC9CE14F73C3B847931AF821C77718455F595CA15B86386F3FCC5962262" + + "5FC916DDB4A08479DCB49FF7444333FA99FBB22F1AEC1876CF1E099F7A4ECA85" + + "A325A8623E071EEA9359194EEE712F73076C5EB72AA243D0C0978B934BC8596F" + + "8353FD3CA859EEA457C6175E82AE5854CC7B6598A1E980332F56AB1EE1208277" + + "4A91A63181B9302306092A864886F70D01091531160414E6335FA7097AB6DE4A" + + "1CDB0C678D7A929883FB6430819106092B06010401823711013181831E818000" + + "4D006900630072006F0073006F0066007400200045006E00680061006E006300" + + "650064002000440053005300200061006E006400200044006900660066006900" + + "65002D00480065006C006C006D0061006E002000430072007900700074006F00" + + "67007200610070006800690063002000500072006F0076006900640065007230" + + "313021300906052B0E03021A0500041466FD3518CEBBD69877BA663C9E8D7092" + + "8E8A98F30408DFB5AE610308BCF802020800").HexToByteArray(); + + public static byte[] s_dsa1024Cert = ( + "3082038D3082034AA003020102020900AB740A714AA83C92300B060960864801" + + "650304030230818D310B3009060355040613025553311330110603550408130A" + + "57617368696E67746F6E3110300E060355040713075265646D6F6E64311E301C" + + "060355040A13154D6963726F736F667420436F72706F726174696F6E3120301E" + + "060355040B13172E4E4554204672616D65776F726B2028436F72654658293115" + + "30130603550403130C313032342D62697420445341301E170D31353131323531" + + "34343030335A170D3135313232353134343030335A30818D310B300906035504" + + "0613025553311330110603550408130A57617368696E67746F6E3110300E0603" + + "55040713075265646D6F6E64311E301C060355040A13154D6963726F736F6674" + + "20436F72706F726174696F6E3120301E060355040B13172E4E4554204672616D" + + "65776F726B2028436F7265465829311530130603550403130C313032342D6269" + + "7420445341308201B73082012C06072A8648CE3804013082011F02818100AEE3" + + "309FC7C9DB750D4C3797D333B3B9B234B462868DB6FFBDED790B7FC8DDD574C2" + + "BD6F5E749622507AB2C09DF5EAAD84859FC0706A70BB8C9C8BE22B4890EF2325" + + "280E3A7F9A3CE341DBABEF6058D063EA6783478FF8B3B7A45E0CA3F7BAC9995D" + + "CFDDD56DF168E91349130F719A4E717351FAAD1A77EAC043611DC5CC5A7F0215" + + "00D23428A76743EA3B49C62EF0AA17314A85415F0902818100853F830BDAA738" + + "465300CFEE02418E6B07965658EAFDA7E338A2EB1531C0E0CA5EF1A12D9DDC7B" + + "550A5A205D1FF87F69500A4E4AF5759F3F6E7F0C48C55396B738164D9E35FB50" + + "6BD50E090F6A497C70E7E868C61BD4477C1D62922B3DBB40B688DE7C175447E2" + + "E826901A109FAD624F1481B276BF63A665D99C87CEE9FD063303818400028180" + + "25B8E7078E149BAC352667623620029F5E4A5D4126E336D56F1189F9FF71EA67" + + "1B844EBD351514F27B69685DDF716B32F102D60EA520D56F544D19B2F08F5D9B" + + "DDA3CBA3A73287E21E559E6A07586194AFAC4F6E721EDCE49DE0029627626D7B" + + "D30EEB337311DB4FF62D7608997B6CC32E9C42859820CA7EF399590D5A388C48" + + "A330302E302C0603551D110425302387047F0000018710000000000000000000" + + "0000000000000182096C6F63616C686F7374300B060960864801650304030203" + + "3000302D021500B9316CC7E05C9F79197E0B41F6FD4E3FCEB72A8A0214075505" + + "CCAECB18B7EF4C00F9C069FA3BC78014DE").HexToByteArray(); + + // Password: "Test" + internal static readonly byte[] ECDsaP256_DigitalSignature_Pfx_Windows = ( + "308204470201033082040306092A864886F70D010701A08203F4048203F03082" + + "03EC3082016D06092A864886F70D010701A082015E0482015A30820156308201" + + "52060B2A864886F70D010C0A0102A081CC3081C9301C060A2A864886F70D010C" + + "0103300E0408EC154269C5878209020207D00481A80BAA4AF8660E6FAB7B050B" + + "8EF604CFC378652B54FE005DC3C7E2F12E5EFC7FE2BB0E1B3828CAFE752FD64C" + + "7CA04AF9FBC5A1F36E30D7D299C52BF6AE65B54B9240CC37C04E7E06330C24E9" + + "6D19A67B7015A6BF52C172FFEA719B930DBE310EEBC756BDFF2DF2846EE973A6" + + "6C63F4E9130083D64487B35C1941E98B02B6D5A92972293742383C62CCAFB996" + + "EAD71A1DF5D0380EFFF25BA60B233A39210FD7D55A9B95CD8A440DF666317430" + + "1306092A864886F70D0109153106040401000000305D06092B06010401823711" + + "0131501E4E004D006900630072006F0073006F0066007400200053006F006600" + + "7400770061007200650020004B00650079002000530074006F00720061006700" + + "65002000500072006F007600690064006500723082027706092A864886F70D01" + + "0706A0820268308202640201003082025D06092A864886F70D010701301C060A" + + "2A864886F70D010C0106300E0408175CCB1790C48584020207D080820230E956" + + "E38768A035D8EA911283A63F2E5B6E5B73231CFC4FFD386481DE24B7BB1B0995" + + "D614A0D1BD086215CE0054E01EF9CF91B7D80A4ACB6B596F1DFD6CBCA71476F6" + + "10C0D6DD24A301E4B79BA6993F15D34A8ADB7115A8605E797A2C6826A4379B65" + + "90B56CA29F7C36997119257A827C3CA0EC7F8F819536208C650E324C8F884794" + + "78705F833155463A4EFC02B5D5E2608B83F3CAF6C9BB97C1BBBFC6C5584BDCD3" + + "9C46A3944915B3845C41429C7792EB4FA3A7EDECCD801F31A4B6EF57D808AEEA" + + "AF3D1F55F378EF8EF9632CED16EDA3EFBE4A9D5C5F608CA90A9AC8D3F86462AC" + + "219BFFD0B8A87DDD22CF029230369B33FC2B488B5F82702EFC3F270F912EAD2E" + + "2402D99F8324164C5CD5959F22DEC0D1D212345B4B3F62848E7D9CFCE2224B61" + + "976C107E1B218B4B7614FF65BCCA388F85D6920270D4C588DEED323C416D014F" + + "5F648CC2EE941855EB3C889DCB9A345ED11CAE94041A86ED23E5789137A3DE22" + + "5F4023D260BB686901F2149B5D7E37102FFF5282995892BDC2EAB48BD5DA155F" + + "72B1BD05EE3EDD32160AC852E5B47CA9AEACE24946062E9D7DCDA642F945C9E7" + + "C98640DFAC7A2B88E76A560A0B4156611F9BE8B3613C71870F035062BD4E3D9F" + + "D896CF373CBFBFD31410972CDE50739FFB8EC9180A52D7F5415EBC997E5A4221" + + "349B4BB7D53614630EEEA729A74E0C0D20726FDE5814321D6C265A7DC6BA24CA" + + "F2FCE8C8C162733D58E02E08921E70EF838B95C96A5818489782563AE8A2A85F" + + "64A95EB350FF8EF6D625AD031BCD303B301F300706052B0E03021A0414C8D96C" + + "ED140F5CA3CB92BEFCA32C690804576ABF0414B59D4FECA9944D40EEFDE7FB96" + + "196D167B0FA511020207D0").HexToByteArray(); + + internal static readonly byte[] ECDsaP256_DigitalSignature_Cert = ( + "308201583081FFA003020102021035428F3B3C5107AD49E776D6E74C4DC8300A" + + "06082A8648CE3D04030230153113301106035504030C0A454344534120546573" + + "74301E170D3135303530313030333730335A170D313630353031303035373033" + + "5A30153113301106035504030C0A454344534120546573743059301306072A86" + + "48CE3D020106082A8648CE3D030107034200047590F69CA114E92927E034C997" + + "B7C882A8C992AC00CEFB4EB831901536F291E1B515263BCD20E1EA32496FDAC8" + + "4E2D8D1B703266A9088F6EAF652549D9BB63D5A331302F300E0603551D0F0101" + + "FF040403020388301D0603551D0E0416041411218A92C5EB12273B3C5CCFB822" + + "0CCCFDF387DB300A06082A8648CE3D040302034800304502201AFE595E19F1AE" + + "4B6A4B231E8851926438C55B5DDE632E6ADF13C1023A65898E022100CBDF434F" + + "DD197D8B594E8026E44263BADE773C2BEBD060CC4109484A498E7C7E").HexToByteArray(); } } } diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/tests/Configurations.props b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/Configurations.props index 6ee732e703..bd00aa7f0f 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/tests/Configurations.props +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/Configurations.props @@ -3,7 +3,8 @@ netcoreapp-Windows_NT; - netstandard-Windows_NT; + netcoreapp; + netfx-Windows_NT; - \ No newline at end of file + diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/DecryptTests.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/DecryptTests.cs index 8d9b8f9845..fb31e70a15 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/DecryptTests.cs +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/DecryptTests.cs @@ -21,7 +21,7 @@ namespace System.Security.Cryptography.Pkcs.EnvelopedCmsTests.Tests { public static partial class DecryptTests { - public static bool SupportsCngCertificates { get; } = (!PlatformDetection.IsFullFramework || PlatformDetection.IsNetfx462OrNewer()); + public static bool SupportsCngCertificates { get; } = (!PlatformDetection.IsFullFramework || PlatformDetection.IsNetfx462OrNewer); [ConditionalFact(nameof(SupportsCngCertificates))] [OuterLoop(/* Leaks key on disk if interrupted */)] diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/EdgeCasesTests.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/EdgeCasesTests.cs index a4d2e39d5e..b7de03720f 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/EdgeCasesTests.cs +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/EdgeCasesTests.cs @@ -21,7 +21,7 @@ namespace System.Security.Cryptography.Pkcs.EnvelopedCmsTests.Tests { public static partial class EdgeCasesTests { - public static bool SupportsCngCertificates { get; } = (!PlatformDetection.IsFullFramework || PlatformDetection.IsNetfx462OrNewer()); + public static bool SupportsCngCertificates { get; } = (!PlatformDetection.IsFullFramework || PlatformDetection.IsNetfx462OrNewer); [ConditionalFact(nameof(SupportsCngCertificates))] [OuterLoop(/* Leaks key on disk if interrupted */)] diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/StateTests.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/StateTests.cs index 80492dd131..19a0ba344b 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/StateTests.cs +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/StateTests.cs @@ -21,7 +21,7 @@ namespace System.Security.Cryptography.Pkcs.EnvelopedCmsTests.Tests { public static partial class StateTests { - public static bool SupportsCngCertificates { get; } = (!PlatformDetection.IsFullFramework || PlatformDetection.IsNetfx462OrNewer()); + public static bool SupportsCngCertificates { get; } = (!PlatformDetection.IsFullFramework || PlatformDetection.IsNetfx462OrNewer); // // Exercises various edge cases when EnvelopedCms methods and properties are called out of the "expected" order. diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/tests/Oids.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/Oids.cs index 0a41c32ca7..98de58953c 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/tests/Oids.cs +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/Oids.cs @@ -32,6 +32,8 @@ namespace System.Security.Cryptography.Pkcs.Tests public const string DocumentDescription = "1.3.6.1.4.1.311.88.2.2"; public const string MessageDigest = "1.2.840.113549.1.9.4"; public const string DocumentName = "1.3.6.1.4.1.311.88.2.1"; + public const string CounterSigner = "1.2.840.113549.1.9.6"; + // Key wrap algorithms public const string CmsRc2Wrap = "1.2.840.113549.1.9.16.3.7"; @@ -46,6 +48,14 @@ namespace System.Security.Cryptography.Pkcs.Tests public const string Pkcs7Encrypted = "1.2.840.113549.1.7.6"; // X509 extensions + public const string SubjectKeyIdentifier = "2.5.29.14"; public const string BasicConstraints2 = "2.5.29.19"; + + // Hash algorithms + public const string Md5 = "1.2.840.113549.2.5"; + public const string Sha1 = "1.3.14.3.2.26"; + public const string Sha256 = "2.16.840.1.101.3.4.2.1"; + public const string Sha384 = "2.16.840.1.101.3.4.2.2"; + public const string Sha512 = "2.16.840.1.101.3.4.2.3"; } } diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/tests/SignedCms/CounterSigningDerOrder.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/SignedCms/CounterSigningDerOrder.cs new file mode 100644 index 0000000000..b7caf557ff --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/SignedCms/CounterSigningDerOrder.cs @@ -0,0 +1,226 @@ +// 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.Collections; +using System.Security.Cryptography.X509Certificates; +using Xunit; + +namespace System.Security.Cryptography.Pkcs.Tests +{ + public static class CounterSigningDerOrder + { + [Fact] + public static void CounterSigningReindexes() + { + ContentInfo content = new ContentInfo(new byte[] { 7 }); + SignedCms cms = new SignedCms(content); + + using (X509Certificate2 cert1 = Certificates.RSA2048SignatureOnly.TryGetCertificateWithPrivateKey()) + using (X509Certificate2 cert2 = Certificates.RSAKeyTransferCapi1.TryGetCertificateWithPrivateKey()) + { + CmsSigner signer = new CmsSigner(SubjectIdentifierType.SubjectKeyIdentifier, cert1); + cms.ComputeSignature(signer); + + SignerProfile yellow = new SignerProfile( + cert1, + SubjectIdentifierType.SubjectKeyIdentifier, + hasSignedAttrs: false, + hasUnsignedAttrs: false, + hasCounterSigners: false); + + AssertSignerTraits(cms.SignerInfos[0], yellow); + + signer.SignedAttributes.Add(new Pkcs9SigningTime()); + signer.SignerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber; + cms.ComputeSignature(signer); + + SignerProfile green = new SignerProfile( + cert1, + SubjectIdentifierType.IssuerAndSerialNumber, + hasSignedAttrs: true, + hasUnsignedAttrs: false, + hasCounterSigners: false); + + // No reordering. 0 stayed 0, new entry becomes 1. + AssertSignerTraits(cms.SignerInfos[0], yellow); + AssertSignerTraits(cms.SignerInfos[1], green); + + signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, cert2); + cms.ComputeSignature(signer); + + SignerProfile grey = new SignerProfile( + cert2, + SubjectIdentifierType.IssuerAndSerialNumber, + hasSignedAttrs: false, + hasUnsignedAttrs: false, + hasCounterSigners: false); + + // No reordering. 0 stayed 0, 1 stays 1, new entry is 2. + AssertSignerTraits(cms.SignerInfos[0], yellow); + AssertSignerTraits(cms.SignerInfos[1], green); + AssertSignerTraits(cms.SignerInfos[2], grey); + + CmsSigner counterSigner = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, cert1); + SignerInfo toCounterSign = cms.SignerInfos[0]; + toCounterSign.ComputeCounterSignature(counterSigner); + + // Reordering just happened. + // We counter-signed the first element, so it gets bigger by ~a signerinfo, or 100% bigger. + // The sizes of the three were + // yellow: 311 bytes + // green: 455 bytes (IssuerAndSerialNumber takes more bytes, and it has attributes) + // grey: 212 bytes (1024-bit RSA signature instead of 2048-bit) + // + // Because yellow also contains cyan (444) bytes (then also some overhead) it has grown + // to 763 bytes. So the size-sorted order (DER SET-OF sorting) is { grey, green, yellow }. + + // Record that yellow gained a countersigner (and thus an unsigned attribute) + yellow.HasUnsignedAttrs = true; + yellow.HasCounterSigners = true; + + SignerProfile cyan = new SignerProfile( + cert1, + SubjectIdentifierType.IssuerAndSerialNumber, + hasSignedAttrs: true, + hasUnsignedAttrs: false, + hasCounterSigners: false); + + AssertSignerTraits(cms.SignerInfos[0], grey); + AssertSignerTraits(cms.SignerInfos[1], green); + AssertSignerTraits(cms.SignerInfos[2], yellow); + AssertSignerTraits(cms.SignerInfos[2].CounterSignerInfos[0], cyan); + + counterSigner.UnsignedAttributes.Add(new Pkcs9SigningTime()); + toCounterSign.ComputeCounterSignature(counterSigner); + + SignerProfile red = new SignerProfile( + cert1, + SubjectIdentifierType.IssuerAndSerialNumber, + hasSignedAttrs: true, + hasUnsignedAttrs: true, + hasCounterSigners: false); + + // Since "red" has one more attribute than "cyan", but they're otherwise the same + // it will sort later. + + AssertSignerTraits(cms.SignerInfos[0], grey); + AssertSignerTraits(cms.SignerInfos[1], green); + AssertSignerTraits(cms.SignerInfos[2], yellow); + AssertSignerTraits(cms.SignerInfos[2].CounterSignerInfos[0], cyan); + AssertSignerTraits(cms.SignerInfos[2].CounterSignerInfos[1], red); + + counterSigner.SignerIdentifierType = SubjectIdentifierType.SubjectKeyIdentifier; + toCounterSign.ComputeCounterSignature(counterSigner); + + SignerProfile clear = new SignerProfile( + cert1, + SubjectIdentifierType.SubjectKeyIdentifier, + hasSignedAttrs: true, + hasUnsignedAttrs: true, + hasCounterSigners: false); + + // By changing from IssuerAndSerialNumber to SubjectKeyIdentifier, this copy will + // sort higher. It saves so many bytes, in this specific case, that it goes first. + + AssertSignerTraits(cms.SignerInfos[0], grey); + AssertSignerTraits(cms.SignerInfos[1], green); + AssertSignerTraits(cms.SignerInfos[2], yellow); + AssertSignerTraits(cms.SignerInfos[2].CounterSignerInfos[0], clear); + AssertSignerTraits(cms.SignerInfos[2].CounterSignerInfos[1], cyan); + AssertSignerTraits(cms.SignerInfos[2].CounterSignerInfos[2], red); + + // Now start removing things. + + cms.SignerInfos[2].RemoveCounterSignature(1); + + // Fairly predictable. + AssertSignerTraits(cms.SignerInfos[0], grey); + AssertSignerTraits(cms.SignerInfos[1], green); + AssertSignerTraits(cms.SignerInfos[2], yellow); + AssertSignerTraits(cms.SignerInfos[2].CounterSignerInfos[0], clear); + AssertSignerTraits(cms.SignerInfos[2].CounterSignerInfos[1], red); + + cms.SignerInfos[2].RemoveCounterSignature(1); + + // Fairly predictable. + AssertSignerTraits(cms.SignerInfos[0], grey); + AssertSignerTraits(cms.SignerInfos[1], green); + AssertSignerTraits(cms.SignerInfos[2], yellow); + AssertSignerTraits(cms.SignerInfos[2].CounterSignerInfos[0], clear); + + cms.SignerInfos[2].RemoveCounterSignature(0); + + // We have removed the last counter-signer. + // yellow is now smaller than grey. + // But the document only re-normalizes on addition. + + yellow.HasCounterSigners = false; + yellow.HasUnsignedAttrs = false; + + AssertSignerTraits(cms.SignerInfos[0], grey); + AssertSignerTraits(cms.SignerInfos[1], green); + AssertSignerTraits(cms.SignerInfos[2], yellow); + + // Export/import to normalize. Everyone's back to their original size, + // but they were unsorted before. { grey, yellow, green } was the right order. + cms.Decode(cms.Encode()); + + AssertSignerTraits(cms.SignerInfos[0], grey); + AssertSignerTraits(cms.SignerInfos[1], yellow); + AssertSignerTraits(cms.SignerInfos[2], green); + + cms.SignerInfos[0].ComputeCounterSignature(counterSigner); + + // Move to the end of the line, Mr. Grey + grey.HasUnsignedAttrs = true; + grey.HasCounterSigners = true; + + AssertSignerTraits(cms.SignerInfos[0], yellow); + AssertSignerTraits(cms.SignerInfos[1], green); + AssertSignerTraits(cms.SignerInfos[2], grey); + AssertSignerTraits(cms.SignerInfos[2].CounterSignerInfos[0], clear); + } + } + + private static void AssertSignerTraits(SignerInfo signerInfo, SignerProfile profile) + { + Assert.Equal(profile.Type, signerInfo.SignerIdentifier.Type); + Assert.Equal(profile.Cert, signerInfo.Certificate); + AssertMaybeEmpty(profile.HasSignedAttrs, signerInfo.SignedAttributes); + AssertMaybeEmpty(profile.HasUnsignedAttrs, signerInfo.UnsignedAttributes); + AssertMaybeEmpty(profile.HasCounterSigners, signerInfo.CounterSignerInfos); + } + + private static void AssertMaybeEmpty(bool shouldHaveData, IEnumerable collection) + { + if (shouldHaveData) + Assert.NotEmpty(collection); + else + Assert.Empty(collection); + } + + private class SignerProfile + { + public X509Certificate2 Cert { get; } + public SubjectIdentifierType Type { get; } + public bool HasSignedAttrs { get; } + public bool HasUnsignedAttrs { get; set; } + public bool HasCounterSigners { get; set; } + + internal SignerProfile( + X509Certificate2 cert, + SubjectIdentifierType type, + bool hasSignedAttrs, + bool hasUnsignedAttrs, + bool hasCounterSigners) + { + Cert = cert; + Type = type; + HasSignedAttrs = hasSignedAttrs; + HasUnsignedAttrs = hasUnsignedAttrs; + HasCounterSigners = hasCounterSigners; + } + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedCmsTests.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedCmsTests.cs new file mode 100644 index 0000000000..4d131a5ea8 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedCmsTests.cs @@ -0,0 +1,894 @@ +// 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.Linq; +using System.Security.Cryptography.X509Certificates; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Pkcs.Tests +{ + public static class SignedCmsTests + { + [Fact] + public static void DefaultStateBehavior() + { + SignedCms cms = new SignedCms(); + + Assert.Equal(0, cms.Version); + Assert.False(cms.Detached, "cms.Detached"); + + X509Certificate2Collection certificates = cms.Certificates; + X509Certificate2Collection certificates2 = cms.Certificates; + + Assert.NotSame(certificates, certificates2); + Assert.Equal(0, certificates.Count); + Assert.Equal(0, certificates2.Count); + + ContentInfo content = cms.ContentInfo; + ContentInfo content2 = cms.ContentInfo; + Assert.Same(content, content2); + + Assert.Equal("1.2.840.113549.1.7.1", content.ContentType.Value); + Assert.Equal(Array.Empty(), content.Content); + + SignerInfoCollection signers = cms.SignerInfos; + SignerInfoCollection signers2 = cms.SignerInfos; + + Assert.NotSame(signers, signers2); + Assert.Equal(0, signers.Count); + Assert.Equal(0, signers2.Count); + + Assert.Throws(() => cms.CheckSignature(true)); + Assert.Throws(() => cms.CheckHash()); + Assert.Throws(() => cms.RemoveSignature(0)); + Assert.Throws(() => cms.RemoveSignature(-1)); + Assert.Throws(() => cms.RemoveSignature(10000)); + Assert.Throws(() => cms.Encode()); + } + + [Fact] + public static void DecodeNull() + { + SignedCms cms = new SignedCms(); + Assert.Throws(() => cms.Decode(null)); + } + + [Theory] + [InlineData("Empty", "")] + [InlineData("Not a sequence", "010100")] + [InlineData("Too-long BER length", "3005")] + public static void DecodeInvalid(string description, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + + SignedCms cms = new SignedCms(); + Assert.Throws(() => cms.Decode(inputData)); + } + + [Fact] + public static void Decode_WrongContentType() + { + const string InputHex = + "3080" + + "0609608648016503040201" + + "A080" + + "3002" + + "0500" + + "0000" + + "0000"; + + byte[] inputData = InputHex.HexToByteArray(); + + SignedCms cms = new SignedCms(); + Assert.Throws(() => cms.Decode(inputData)); + } + + [Fact] + public static void Decode_OverwritesAttachedContentInfo() + { + ContentInfo original = new ContentInfo(new byte [] { 1, 2, 3, 4, 5 }); + SignedCms cms = new SignedCms(original, false); + Assert.False(cms.Detached); + + cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber); + + Assert.False(cms.Detached); + + ContentInfo newInfo = cms.ContentInfo; + ContentInfo newInfo2 = cms.ContentInfo; + + Assert.NotSame(original, newInfo); + Assert.Same(newInfo, newInfo2); + Assert.NotEqual(original.Content, newInfo.Content); + } + + [Fact] + public static void Decode_PreservesDetachedContentInfo() + { + ContentInfo original = new ContentInfo(new byte[] { 1, 2, 3, 4, 5 }); + SignedCms cms = new SignedCms(original, true); + Assert.True(cms.Detached); + + cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber); + + Assert.True(cms.Detached); + + ContentInfo newInfo = cms.ContentInfo; + ContentInfo newInfo2 = cms.ContentInfo; + + Assert.Same(original, newInfo); + Assert.Same(newInfo, newInfo2); + } + + [Fact] + public static void SignedCms_SignerInfos_UniquePerCall() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber); + + SignerInfoCollection signers = cms.SignerInfos; + SignerInfoCollection signers2 = cms.SignerInfos; + + Assert.NotSame(signers, signers2); + Assert.Single(signers); + Assert.Single(signers2); + Assert.NotSame(signers[0], signers2[0]); + + Assert.NotSame(signers[0].Certificate, signers2[0].Certificate); + Assert.Equal(signers[0].Certificate, signers2[0].Certificate); + } + + [Fact] + public static void SignedCms_Certificates_UniquePerCall() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPssDocument); + + X509Certificate2Collection certs = cms.Certificates; + X509Certificate2Collection certs2 = cms.Certificates; + Assert.NotSame(certs, certs2); + Assert.Single(certs); + Assert.Single(certs2); + + Assert.NotSame(certs[0], certs2[0]); + Assert.Equal(certs[0], certs2[0]); + } + + [Fact] + public static void CheckSignature_ThrowsOnNullStore() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPssDocument); + + AssertExtensions.Throws( + "extraStore", + () => cms.CheckSignature(null, true)); + + AssertExtensions.Throws( + "extraStore", + () => cms.CheckSignature(null, false)); + } + + [Fact] + public static void Ctor_NoContent_Throws() + { + Assert.Throws(() => new SignedCms(null)); + Assert.Throws(() => new SignedCms(null, false)); + Assert.Throws(() => new SignedCms(null, true)); + + Assert.Throws( + () => new SignedCms(SubjectIdentifierType.SubjectKeyIdentifier, null)); + Assert.Throws( + () => new SignedCms(SubjectIdentifierType.SubjectKeyIdentifier, null, false)); + Assert.Throws( + () => new SignedCms(SubjectIdentifierType.SubjectKeyIdentifier, null, true)); + } + + [Fact] + public static void CheckSignature_ExtraStore_IsAdditional() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber); + + // Assert.NotThrows + cms.CheckSignature(true); + + // Assert.NotThrows + cms.CheckSignature(new X509Certificate2Collection(), true); + } + + [Fact] + public static void Decode_IgnoresExtraData() + { + byte[] basis = SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber; + byte[] data = new byte[basis.Length + 60]; + data.AsSpan().Slice(basis.Length).Fill(0x5E); + basis.AsSpan().CopyTo(data); + + SignedCms cms = new SignedCms(); + cms.Decode(data); + + // Assert.NotThrows + cms.CheckSignature(true); + + byte[] encoded = cms.Encode(); + + Assert.Equal(basis.Length, encoded.Length); + Assert.Equal(basis.ByteArrayToHex(), encoded.ByteArrayToHex()); + } + + [Fact] + public static void CheckSignatures_AllRemoved() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber); + Assert.Single(cms.SignerInfos); + + cms.RemoveSignature(0); + Assert.Empty(cms.SignerInfos); + + Assert.Throws(() => cms.CheckSignature(true)); + } + + [Fact] + public static void CheckHash_AllRemoved() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber); + Assert.Single(cms.SignerInfos); + + cms.RemoveSignature(0); + Assert.Empty(cms.SignerInfos); + + Assert.Throws(() => cms.CheckHash()); + } + + [Fact] + public static void RemoveSignature_MatchesIssuerAndSerialNumber() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber); + Assert.Single(cms.SignerInfos); + + SignerInfo signerInfo = cms.SignerInfos[0]; + Assert.Equal(SubjectIdentifierType.IssuerAndSerialNumber, signerInfo.SignerIdentifier.Type); + + int certCount = cms.Certificates.Count; + cms.RemoveSignature(signerInfo); + Assert.Empty(cms.SignerInfos); + Assert.Equal(certCount, cms.Certificates.Count); + } + + [Fact] + public static void RemoveSignature_MatchesSubjectKeyIdentifier() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPssDocument); + Assert.Single(cms.SignerInfos); + + SignerInfo signerInfo = cms.SignerInfos[0]; + Assert.Equal(SubjectIdentifierType.SubjectKeyIdentifier, signerInfo.SignerIdentifier.Type); + + int certCount = cms.Certificates.Count; + cms.RemoveSignature(signerInfo); + Assert.Empty(cms.SignerInfos); + Assert.Equal(certCount, cms.Certificates.Count); + } + + [Fact] + public static void RemoveSignature_MatchesNoSignature() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.NoSignatureSignedWithAttributesAndCounterSignature); + Assert.Single(cms.SignerInfos); + + SignerInfo signerInfo = cms.SignerInfos[0]; + Assert.Equal(SubjectIdentifierType.NoSignature, signerInfo.SignerIdentifier.Type); + + cms.RemoveSignature(signerInfo); + Assert.Empty(cms.SignerInfos); + } + + [Fact] + public static void RemoveSignature_WithNoMatch() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber); + SignerInfo wrongSignerInfo = cms.SignerInfos[0]; + cms.Decode(SignedDocuments.RsaPssDocument); + Assert.Single(cms.SignerInfos); + + Assert.Throws(() => cms.RemoveSignature(wrongSignerInfo)); + Assert.Single(cms.SignerInfos); + Assert.Single(cms.Certificates); + } + + [Fact] + public static void RemoveSignature_Null() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber); + + AssertExtensions.Throws( + "signerInfo", + () => cms.RemoveSignature(null)); + + Assert.Single(cms.SignerInfos); + Assert.Single(cms.Certificates); + } + + [Fact] + public static void RemoveSignature_OutOfRange() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber); + + ArgumentOutOfRangeException ex = AssertExtensions.Throws( + "index", + () => cms.RemoveSignature(-1)); + + Assert.Equal(null, ex.ActualValue); + Assert.Single(cms.SignerInfos); + Assert.Single(cms.Certificates); + + ex = AssertExtensions.Throws( + "index", + () => cms.RemoveSignature(1)); + + Assert.Equal(null, ex.ActualValue); + + Assert.Single(cms.SignerInfos); + Assert.Single(cms.Certificates); + } + + [Fact] + public static void DetachedContent_ConcatEmbeddedContent() + { + // 1: Prove the document works. + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.NoSignatureWithNoAttributes); + cms.SignerInfos[0].CheckHash(); + + ContentInfo save = cms.ContentInfo; + + // 2: Using the empty detached content, see that the document still works. + cms = new SignedCms(new ContentInfo(Array.Empty()), true); + cms.Decode(SignedDocuments.NoSignatureWithNoAttributes); + cms.SignerInfos[0].CheckHash(); + + //// 3: Using the saved content, prove that the document no longer works. + cms = new SignedCms(save, true); + cms.Decode(SignedDocuments.NoSignatureWithNoAttributes); + Assert.Throws(() => cms.SignerInfos[0].CheckHash()); + + // 4: Modify the contained hash, see that it previously didn't work for the "right" reason. + string inputHex = SignedDocuments.NoSignatureWithNoAttributes.ByteArrayToHex(); + inputHex = inputHex.Replace( + // SHA1("Microsoft Corporation") + "A5F085E7F326F3D6CA3BFD6280A3DE8EBC2EA60E", + // SHA1("Microsoft CorporationMicrosoft Corporation") + "346804FD67B37C27A203CD514B267711CFB39118"); + + cms = new SignedCms(save, true); + cms.Decode(inputHex.HexToByteArray()); + cms.SignerInfos[0].CheckHash(); + } + + [Theory] + [InlineData(SubjectIdentifierType.IssuerAndSerialNumber, false)] + [InlineData(SubjectIdentifierType.IssuerAndSerialNumber, true)] + [InlineData(SubjectIdentifierType.SubjectKeyIdentifier, false)] + [InlineData(SubjectIdentifierType.SubjectKeyIdentifier, true)] + public static void AddFirstSigner_RSA(SubjectIdentifierType identifierType, bool detached) + { + ContentInfo contentInfo = new ContentInfo(new byte[] { 9, 8, 7, 6, 5 }); + SignedCms cms = new SignedCms(contentInfo, detached); + + using (X509Certificate2 signerCert = Certificates.RSA2048SignatureOnly.TryGetCertificateWithPrivateKey()) + { + CmsSigner signer = new CmsSigner(identifierType, signerCert); + cms.ComputeSignature(signer); + } + + Assert.Same(contentInfo.Content, cms.ContentInfo.Content); + Assert.Single(cms.SignerInfos); + Assert.Single(cms.Certificates); + + int expectedVersion = identifierType == SubjectIdentifierType.SubjectKeyIdentifier ? 3 : 1; + Assert.Equal(expectedVersion, cms.Version); + + SignerInfo firstSigner = cms.SignerInfos[0]; + Assert.Equal(identifierType, firstSigner.SignerIdentifier.Type); + Assert.NotNull(firstSigner.Certificate); + Assert.NotSame(cms.Certificates[0], firstSigner.Certificate); + Assert.Equal(cms.Certificates[0], firstSigner.Certificate); + + cms.CheckSignature(true); + byte[] encoded = cms.Encode(); + + cms = new SignedCms(); + cms.Decode(encoded); + Assert.Single(cms.SignerInfos); + Assert.Single(cms.Certificates); + Assert.Equal(expectedVersion, cms.Version); + Assert.Equal(identifierType, cms.SignerInfos[0].SignerIdentifier.Type); + Assert.Equal(firstSigner.Certificate, cms.SignerInfos[0].Certificate); + + if (detached) + { + Assert.Throws(() => cms.CheckSignature(true)); + cms = new SignedCms(contentInfo, detached); + cms.Decode(encoded); + } + + cms.CheckSignature(true); + } + + [Theory] + [InlineData(SubjectIdentifierType.IssuerAndSerialNumber, false)] + [InlineData(SubjectIdentifierType.IssuerAndSerialNumber, true)] + public static void AddFirstSigner_DSA(SubjectIdentifierType identifierType, bool detached) + { + ContentInfo contentInfo = new ContentInfo(new byte[] { 9, 8, 7, 6, 5 }); + SignedCms cms = new SignedCms(contentInfo, detached); + + using (X509Certificate2 signerCert = Certificates.Dsa1024.TryGetCertificateWithPrivateKey()) + { + CmsSigner signer = new CmsSigner(identifierType, signerCert); + signer.IncludeOption = X509IncludeOption.EndCertOnly; + // Best compatibility for DSA is SHA-1 (FIPS 186-2) + signer.DigestAlgorithm = new Oid(Oids.Sha1, Oids.Sha1); + cms.ComputeSignature(signer); + } + + Assert.Single(cms.SignerInfos); + Assert.Single(cms.Certificates); + + int expectedVersion = identifierType == SubjectIdentifierType.SubjectKeyIdentifier ? 3 : 1; + Assert.Equal(expectedVersion, cms.Version); + + SignerInfo firstSigner = cms.SignerInfos[0]; + Assert.Equal(identifierType, firstSigner.SignerIdentifier.Type); + Assert.NotNull(firstSigner.Certificate); + Assert.NotSame(cms.Certificates[0], firstSigner.Certificate); + Assert.Equal(cms.Certificates[0], firstSigner.Certificate); + +#if netcoreapp + byte[] signature = firstSigner.GetSignature(); + Assert.NotEmpty(signature); + // DSA PKIX signature format is a DER SEQUENCE. + Assert.Equal(0x30, signature[0]); +#endif + + cms.CheckSignature(true); + byte[] encoded = cms.Encode(); + + cms = new SignedCms(); + cms.Decode(encoded); + + Assert.Single(cms.SignerInfos); + Assert.Single(cms.Certificates); + Assert.Equal(expectedVersion, cms.Version); + Assert.Equal(identifierType, cms.SignerInfos[0].SignerIdentifier.Type); + Assert.Equal(firstSigner.Certificate, cms.SignerInfos[0].Certificate); + +#if netcoreapp + byte[] sig2 = cms.SignerInfos[0].GetSignature(); + Assert.Equal(signature, sig2); +#endif + + if (detached) + { + Assert.Throws(() => cms.CheckSignature(true)); + cms = new SignedCms(contentInfo, detached); + cms.Decode(encoded); + } + + cms.CheckSignature(true); + } + + [Theory] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + [InlineData(SubjectIdentifierType.IssuerAndSerialNumber, false, Oids.Sha256)] + [InlineData(SubjectIdentifierType.IssuerAndSerialNumber, true, Oids.Sha256)] + [InlineData(SubjectIdentifierType.SubjectKeyIdentifier, false, Oids.Sha256)] + [InlineData(SubjectIdentifierType.SubjectKeyIdentifier, true, Oids.Sha256)] + [InlineData(SubjectIdentifierType.IssuerAndSerialNumber, false, Oids.Sha1)] + [InlineData(SubjectIdentifierType.SubjectKeyIdentifier, true, Oids.Sha1)] + [InlineData(SubjectIdentifierType.IssuerAndSerialNumber, true, Oids.Sha384)] + [InlineData(SubjectIdentifierType.SubjectKeyIdentifier, false, Oids.Sha384)] + [InlineData(SubjectIdentifierType.IssuerAndSerialNumber, false, Oids.Sha512)] + [InlineData(SubjectIdentifierType.SubjectKeyIdentifier, true, Oids.Sha512)] + public static void AddFirstSigner_ECDSA(SubjectIdentifierType identifierType, bool detached, string digestOid) + { + ContentInfo contentInfo = new ContentInfo(new byte[] { 9, 8, 7, 6, 5 }); + SignedCms cms = new SignedCms(contentInfo, detached); + + using (X509Certificate2 signerCert = Certificates.ECDsaP256Win.TryGetCertificateWithPrivateKey()) + { + CmsSigner signer = new CmsSigner(identifierType, signerCert); + signer.IncludeOption = X509IncludeOption.EndCertOnly; + signer.DigestAlgorithm = new Oid(digestOid, digestOid); + cms.ComputeSignature(signer); + } + + Assert.Single(cms.SignerInfos); + Assert.Single(cms.Certificates); + + int expectedVersion = identifierType == SubjectIdentifierType.SubjectKeyIdentifier ? 3 : 1; + Assert.Equal(expectedVersion, cms.Version); + + SignerInfo firstSigner = cms.SignerInfos[0]; + Assert.Equal(identifierType, firstSigner.SignerIdentifier.Type); + Assert.NotNull(firstSigner.Certificate); + Assert.NotSame(cms.Certificates[0], firstSigner.Certificate); + Assert.Equal(cms.Certificates[0], firstSigner.Certificate); + +#if netcoreapp + byte[] signature = firstSigner.GetSignature(); + Assert.NotEmpty(signature); + // ECDSA PKIX signature format is a DER SEQUENCE. + Assert.Equal(0x30, signature[0]); + + // ECDSA Oids are all under 1.2.840.10045.4. + Assert.StartsWith("1.2.840.10045.4.", firstSigner.SignatureAlgorithm.Value); +#endif + + cms.CheckSignature(true); + byte[] encoded = cms.Encode(); + + cms = new SignedCms(); + cms.Decode(encoded); + + Assert.Single(cms.SignerInfos); + Assert.Single(cms.Certificates); + Assert.Equal(expectedVersion, cms.Version); + Assert.Equal(identifierType, cms.SignerInfos[0].SignerIdentifier.Type); + Assert.Equal(firstSigner.Certificate, cms.SignerInfos[0].Certificate); + +#if netcoreapp + byte[] sig2 = cms.SignerInfos[0].GetSignature(); + Assert.Equal(signature, sig2); +#endif + + if (detached) + { + Assert.Throws(() => cms.CheckSignature(true)); + cms = new SignedCms(contentInfo, detached); + cms.Decode(encoded); + } + + cms.CheckSignature(true); + } + + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, true)] + [InlineData(true, false)] + public static void AddSigner_DuplicateCert_RSA(bool skidFirst, bool detached) + { + ContentInfo contentInfo = new ContentInfo(new byte[] { 9, 8, 7, 6, 5 }); + SignedCms cms = new SignedCms(contentInfo, detached); + + SubjectIdentifierType first; + SubjectIdentifierType second; + int expectedInitialVersion; + + if (skidFirst) + { + first = SubjectIdentifierType.SubjectKeyIdentifier; + second = SubjectIdentifierType.IssuerAndSerialNumber; + expectedInitialVersion = 3; + } + else + { + first = SubjectIdentifierType.IssuerAndSerialNumber; + second = SubjectIdentifierType.SubjectKeyIdentifier; + expectedInitialVersion = 1; + } + + byte[] firstEncoding; + + using (X509Certificate2 signerCert = Certificates.RSA2048SignatureOnly.TryGetCertificateWithPrivateKey()) + { + CmsSigner signer = new CmsSigner(first, signerCert); + cms.ComputeSignature(signer); + + Assert.Single(cms.Certificates); + Assert.Single(cms.SignerInfos); + Assert.Equal(expectedInitialVersion, cms.Version); + + Assert.Equal(first, cms.SignerInfos[0].SignerIdentifier.Type); + Assert.Equal(expectedInitialVersion, cms.SignerInfos[0].Version); + + firstEncoding = cms.Encode(); + + CmsSigner signer2 = new CmsSigner(second, signerCert); + cms.ComputeSignature(signer2); + } + + Assert.Single(cms.Certificates); + Assert.Equal(2, cms.SignerInfos.Count); + + // One of them is a V3 signer, so the whole document is V3. +#if netfx + // Windows CMS computes the version on the first signer, and doesn't + // seem to lift it on the second one. + // It encoded the message as + // SignedData.version=1, + // SignedData.SignerInfos[0].version=3 + // SignedData.SignerInfos[1].version=1 + if (skidFirst) + { +#endif + Assert.Equal(3, cms.Version); +#if netfx + } +#endif + + Assert.Equal(first, cms.SignerInfos[0].SignerIdentifier.Type); + Assert.Equal(second, cms.SignerInfos[1].SignerIdentifier.Type); + Assert.Equal(cms.SignerInfos[0].Certificate, cms.SignerInfos[1].Certificate); + + cms.CheckSignature(true); + + byte[] secondEncoding = cms.Encode(); + Assert.True(secondEncoding.Length > firstEncoding.Length); + } + + [Fact] + public static void CannotSignEmptyContent() + { + SignedCms cms = new SignedCms(); + + using (X509Certificate2 cert = Certificates.RSA2048SignatureOnly.TryGetCertificateWithPrivateKey()) + { + CmsSigner signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, cert); + + Assert.Throws(() => cms.ComputeSignature(signer)); + } + } + + [Fact] + public static void EncodeDoesNotPreserveOrder_DecodeDoes() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.UnsortedSignerInfos); + + // The document here was built by the CounterSigningDerOrder tests, + // then editing the binary to flip the one-counter-signer "yellow" + // into the first position. + + Assert.Equal(3, cms.SignerInfos.Count); + // Enough data to prove the order. + Assert.Single(cms.SignerInfos[0].CounterSignerInfos); + + Assert.Empty(cms.SignerInfos[1].CounterSignerInfos); + Assert.Empty(cms.SignerInfos[1].SignedAttributes); + + Assert.Empty(cms.SignerInfos[2].CounterSignerInfos); + Assert.NotEmpty(cms.SignerInfos[2].SignedAttributes); + + cms.Decode(cms.Encode()); + + // { 0, 1, 2 } => { 1, 2, 0 } + + Assert.Empty(cms.SignerInfos[0].CounterSignerInfos); + Assert.Empty(cms.SignerInfos[0].SignedAttributes); + + Assert.Empty(cms.SignerInfos[1].CounterSignerInfos); + Assert.NotEmpty(cms.SignerInfos[1].SignedAttributes); + + Assert.Single(cms.SignerInfos[2].CounterSignerInfos); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public static void EnsureExtraCertsAdded(bool newDocument) + { + SignedCms cms; + + if (newDocument) + { + ContentInfo data = new ContentInfo(new byte[] { 1, 2, 3 }); + cms = new SignedCms(data, false); + } + else + { + cms = new SignedCms(); + cms.Decode(SignedDocuments.OneDsa1024); + } + + int preCount = cms.Certificates.Count; + + using (X509Certificate2 unrelated1 = Certificates.DHKeyAgree1.GetCertificate()) + using (X509Certificate2 unrelated1Copy = Certificates.DHKeyAgree1.GetCertificate()) + using (X509Certificate2 unrelated2 = Certificates.RSAKeyTransfer2.GetCertificate()) + using (X509Certificate2 unrelated3 = Certificates.RSAKeyTransfer3.GetCertificate()) + using (X509Certificate2 signerCert = Certificates.RSAKeyTransferCapi1.TryGetCertificateWithPrivateKey()) + { + var signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, signerCert); + signer.Certificates.Add(unrelated1); + signer.Certificates.Add(unrelated2); + signer.Certificates.Add(unrelated3); + signer.Certificates.Add(unrelated1Copy); + cms.ComputeSignature(signer); + + bool ExpectCopyRemoved = +#if !netfx + true +#else + false +#endif + ; + + int expectedAddedCount = 4; + + if (!ExpectCopyRemoved) + { + expectedAddedCount++; + } + + // In .NET Framework adding (document) signers adds certificates at the end + // EXCEPT for the first signer, which triggers an internal Decode(Encode()) + // which is only observable if there were multiple certificates. + int u1Idx; + int u1CopyIdx; + int u2Idx; + int u3Idx; + int sIdx; + + if (newDocument) + { + // These indicies are manually computable by observing the certificate sizes. + // But they'll be stable unless a cert changes. + u1Idx = 3; + u1CopyIdx = 4; + u2Idx = 0; + u3Idx = 1; + sIdx = 2; + } + else + { + u1Idx = 0; + u1CopyIdx = 3; + u2Idx = 1; + u3Idx = 2; + sIdx = ExpectCopyRemoved ? 3 : 4; + } + + X509Certificate2Collection certs = cms.Certificates; + Assert.Equal(preCount + expectedAddedCount, certs.Count); + + Assert.Equal(unrelated1, certs[preCount + u1Idx]); + Assert.NotSame(unrelated1, certs[preCount + u1Idx]); + + Assert.Equal(unrelated2, certs[preCount + u2Idx]); + Assert.NotSame(unrelated2, certs[preCount + u2Idx]); + + Assert.Equal(unrelated3, certs[preCount + u3Idx]); + Assert.NotSame(unrelated3, certs[preCount + u3Idx]); + + if (!ExpectCopyRemoved) + { + Assert.Equal(unrelated1, certs[preCount + u1CopyIdx]); + Assert.NotSame(unrelated1, certs[preCount + u1CopyIdx]); + } + + Assert.Equal(signerCert, certs[preCount + sIdx]); + Assert.NotSame(signerCert, certs[preCount + sIdx]); + } + + cms.CheckSignature(true); + } + + [Fact] + public static void UntrustedCertFails_WhenTrustChecked() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber); + + // Assert.NoThrow + cms.CheckSignature(true); + + Assert.Throws(() => cms.CheckSignature(false)); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public static void EnsureDataIsolation_NewDocument(bool detached) + { + byte[] contentBytes = { 9, 8, 7, 6, 5 }; + ContentInfo contentInfo = new ContentInfo(contentBytes); + SignedCms cms = new SignedCms(contentInfo, detached); + + SubjectIdentifierType firstType = SubjectIdentifierType.IssuerAndSerialNumber; + SubjectIdentifierType secondType = SubjectIdentifierType.SubjectKeyIdentifier; + + using (X509Certificate2 signerCert = Certificates.RSA2048SignatureOnly.TryGetCertificateWithPrivateKey()) + { + CmsSigner signer = new CmsSigner(firstType, signerCert); + signer.SignedAttributes.Add(new Pkcs9SigningTime()); + cms.ComputeSignature(signer); + } + + // CheckSignature doesn't read the public mutable data + contentInfo.Content[0] ^= 0xFF; + contentInfo.ContentType.Value = Oids.Pkcs7Hashed; + cms.CheckSignature(true); + + using (X509Certificate2 signerCert = Certificates.RSA2048SignatureOnly.TryGetCertificateWithPrivateKey()) + { + CmsSigner signer = new CmsSigner(secondType, signerCert); + signer.SignedAttributes.Add(new Pkcs9SigningTime()); + + // A second ComputeSignature uses the content value from the first one. + cms.ComputeSignature(signer); + } + + // They should have the same content digests. + AsnEncodedData firstDigest = cms.SignerInfos[0].SignedAttributes + .OfType().First(cao => cao.Oid.Value == Oids.MessageDigest).Values[0]; + + AsnEncodedData secondDigest = cms.SignerInfos[1].SignedAttributes + .OfType().First(cao => cao.Oid.Value == Oids.MessageDigest).Values[0]; + + Assert.Equal(firstDigest.RawData.ByteArrayToHex(), secondDigest.RawData.ByteArrayToHex()); + + byte[] encoded = cms.Encode(); + + if (detached) + { + cms.Decode(encoded); + + // Because Decode leaves ContentInfo alone, and Decode resets the + // "known" content, this will fail due to the tampered content. + Assert.Throws(() => cms.CheckSignature(true)); + + // So put it back. + cms.ContentInfo.Content[0] ^= 0xFF; + } + + cms.Decode(encoded); + + if (detached) + { + // And break it again. + cms.ContentInfo.Content[0] ^= 0xFF; + } + + // Destroy the content that just got decoded. + encoded.AsSpan().Fill(0x55); + cms.CheckSignature(true); + } + + [Fact] + public static void SignWithImplicitSubjectKeyIdentifier() + { + byte[] contentBytes = { 9, 8, 7, 6, 5 }; + ContentInfo contentInfo = new ContentInfo(contentBytes); + SignedCms cms = new SignedCms(contentInfo, false); + + using (X509Certificate2 signerCert = Certificates.RSAKeyTransferCapi1.TryGetCertificateWithPrivateKey()) + { + // This cert has no Subject Key Identifier extension. + Assert.Null(signerCert.Extensions[Oids.SubjectKeyIdentifier]); + + CmsSigner signer = new CmsSigner(SubjectIdentifierType.SubjectKeyIdentifier, signerCert); + cms.ComputeSignature(signer); + } + + Assert.Equal( + "6B4A6B92FDED07EE0119F3674A96D1A70D2A588D", + (string)cms.SignerInfos[0].SignerIdentifier.Value); + + // Assert.NoThrow + cms.CheckSignature(true); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedCmsWholeDocumentTests.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedCmsWholeDocumentTests.cs new file mode 100644 index 0000000000..1278e65e3b --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedCmsWholeDocumentTests.cs @@ -0,0 +1,602 @@ +// 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.Security.Cryptography.X509Certificates; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Pkcs.Tests +{ + public static class SignedCmsWholeDocumentTests + { + [Fact] + public static void ReadRsaPssDocument() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPssDocument); + + Assert.Equal(3, cms.Version); + + ContentInfo contentInfo = cms.ContentInfo; + + Assert.Equal("1.2.840.113549.1.7.1", contentInfo.ContentType.Value); + Assert.Equal("54686973206973206120746573740D0A", contentInfo.Content.ByteArrayToHex()); + + X509Certificate2Collection certs = cms.Certificates; + Assert.Single(certs); + + X509Certificate2 topLevelCert = certs[0]; + Assert.Equal("localhost", topLevelCert.GetNameInfo(X509NameType.SimpleName, false)); + + Assert.Equal( + new DateTimeOffset(2016, 3, 2, 2, 37, 54, TimeSpan.Zero), + new DateTimeOffset(topLevelCert.NotBefore)); + + Assert.Equal( + new DateTimeOffset(2017, 3, 2, 2, 37, 54, TimeSpan.Zero), + new DateTimeOffset(topLevelCert.NotAfter)); + + SignerInfoCollection signers = cms.SignerInfos; + Assert.Single(signers); + + SignerInfo signer = signers[0]; + Assert.Equal(3, signer.Version); + Assert.Equal(SubjectIdentifierType.SubjectKeyIdentifier, signer.SignerIdentifier.Type); + Assert.Equal("1063CAB14FB14C47DC211C0E0285F3EE5946BF2D", signer.SignerIdentifier.Value); + Assert.Equal("2.16.840.1.101.3.4.2.1", signer.DigestAlgorithm.Value); +#if netcoreapp + Assert.Equal("1.2.840.113549.1.1.10", signer.SignatureAlgorithm.Value); +#endif + + CryptographicAttributeObjectCollection signedAttrs = signer.SignedAttributes; + Assert.Equal(4, signedAttrs.Count); + + Assert.Equal("1.2.840.113549.1.9.3", signedAttrs[0].Oid.Value); + Assert.Equal("1.2.840.113549.1.9.5", signedAttrs[1].Oid.Value); + Assert.Equal("1.2.840.113549.1.9.4", signedAttrs[2].Oid.Value); + Assert.Equal("1.2.840.113549.1.9.15", signedAttrs[3].Oid.Value); + + Assert.Equal(1, signedAttrs[0].Values.Count); + Assert.Equal(1, signedAttrs[1].Values.Count); + Assert.Equal(1, signedAttrs[2].Values.Count); + Assert.Equal(1, signedAttrs[3].Values.Count); + + Pkcs9ContentType contentTypeAttr = (Pkcs9ContentType)signedAttrs[0].Values[0]; + Assert.Equal("1.2.840.113549.1.7.1", contentTypeAttr.ContentType.Value); + + Pkcs9SigningTime signingTimeAttr = (Pkcs9SigningTime)signedAttrs[1].Values[0]; + Assert.Equal( + new DateTimeOffset(2017, 10, 26, 1, 6, 25, TimeSpan.Zero), + new DateTimeOffset(signingTimeAttr.SigningTime)); + + Pkcs9MessageDigest messageDigestAttr = (Pkcs9MessageDigest)signedAttrs[2].Values[0]; + Assert.Equal( + "07849DC26FCBB2F3BD5F57BDF214BAE374575F1BD4E6816482324799417CB379", + messageDigestAttr.MessageDigest.ByteArrayToHex()); + + Assert.IsType(signedAttrs[3].Values[0]); + Assert.NotSame(signedAttrs[3].Oid, signedAttrs[3].Values[0].Oid); + Assert.Equal( + "306A300B060960864801650304012A300B0609608648016503040116300B0609" + + "608648016503040102300A06082A864886F70D0307300E06082A864886F70D03" + + "0202020080300D06082A864886F70D0302020140300706052B0E030207300D06" + + "082A864886F70D0302020128", + signedAttrs[3].Values[0].RawData.ByteArrayToHex()); + +#if netcoreapp + Assert.Equal( + "B93E81D141B3C9F159AB0021910635DC72E8E860BE43C28E5D53243D6DC247B7" + + "D4F18C20195E80DEDCC75B29C43CE5047AD775B65BFC93589BD748B950C68BAD" + + "DF1A4673130302BBDA8667D5DDE5EA91ECCB13A9B4C04F1C4842FEB1697B7669" + + "C7692DD3BDAE13B5AA8EE3EB5679F3729D1DC4F2EB9DC89B7E8773F2F8C6108C" + + "05", + signer.GetSignature().ByteArrayToHex()); +#endif + + CryptographicAttributeObjectCollection unsignedAttrs = signer.UnsignedAttributes; + Assert.Empty(unsignedAttrs); + + SignerInfoCollection counterSigners = signer.CounterSignerInfos; + Assert.Empty(counterSigners); + + X509Certificate2 signerCertificate = signer.Certificate; + Assert.Equal( + "CN=localhost, OU=.NET Framework, O=Microsoft Corp., L=Redmond, S=Washington, C=US", + signerCertificate.SubjectName.Name); + + // CheckHash always throws for certificate-based signers. + Assert.Throws(() => signer.CheckHash()); + + // At this time we cannot support the PSS parameters for this document. + Assert.Throws(() => signer.CheckSignature(true)); + + // Since there are no NoSignature signers the document CheckHash will succeed. + // Assert.NotThrows + cms.CheckHash(); + + // Since at least one signer fails, the document signature will fail + Assert.Throws(() => cms.CheckSignature(true)); + } + + [Fact] + public static void ReadRsaPkcs1SimpleDocument() + { + SignedCms cms = new SignedCms(); + + cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber); + + Assert.Equal(1, cms.Version); + + ContentInfo contentInfo = cms.ContentInfo; + + Assert.Equal("1.2.840.113549.1.7.1", contentInfo.ContentType.Value); + Assert.Equal("4D6963726F736F667420436F72706F726174696F6E", contentInfo.Content.ByteArrayToHex()); + + SignerInfoCollection signers = cms.SignerInfos; + Assert.Single(signers); + SignerInfo signer = signers[0]; + + using (X509Certificate2 cert = Certificates.RSAKeyTransferCapi1.GetCertificate()) + { + X509Certificate2Collection certs = cms.Certificates; + + Assert.Single(certs); + + Assert.Equal(cert, certs[0]); + Assert.Equal(cert, signer.Certificate); + Assert.NotSame(certs[0], signer.Certificate); + Assert.NotSame(cert, signer.Certificate); + Assert.NotSame(cert, certs[0]); + } + + Assert.Equal(SubjectIdentifierType.IssuerAndSerialNumber, signer.SignerIdentifier.Type); + Assert.Equal(Oids.Sha1, signer.DigestAlgorithm.Value); + +#if netcoreapp + Assert.Equal(Oids.Rsa, signer.SignatureAlgorithm.Value); + + Assert.Equal( + "5A1717621D450130B3463662160EEC06F7AE77E017DD95F294E97A0BDD433FE6" + + "B2CCB34FAAC33AEA50BFD7D9E78DC7174836284619F744278AE77B8495091E09" + + "6EEF682D9CA95F6E81C7DDCEDDA6A12316B453C894B5000701EB09DF57A53B73" + + "3A4E80DA27FA710870BD88C86E2FDB9DCA14D18BEB2F0C87E9632ABF02BE2FE3", + signer.GetSignature().ByteArrayToHex()); +#endif + + CryptographicAttributeObjectCollection signedAttrs = signer.SignedAttributes; + Assert.Empty(signedAttrs); + + CryptographicAttributeObjectCollection unsignedAttrs = signer.UnsignedAttributes; + Assert.Empty(unsignedAttrs); + + SignerInfoCollection counterSigners = signer.CounterSignerInfos; + Assert.Empty(counterSigners); + + // Assert.NotThrows + signer.CheckSignature(true); + + // CheckHash always throws for certificate-based signers. + Assert.Throws(() => signer.CheckHash()); + + // Since there are no NoSignature signers the document CheckHash will succeed. + // Assert.NotThrows + cms.CheckHash(); + + // Since all the signers succeed the document will succeed + cms.CheckSignature(true); + } + + [Fact] + public static void ReadRsaPkcs1CounterSigned() + { + SignedCms cms = new SignedCms(); + + cms.Decode(SignedDocuments.CounterSignedRsaPkcs1OneSigner); + + Assert.Equal(1, cms.Version); + + ContentInfo contentInfo = cms.ContentInfo; + Assert.Equal("1.2.840.113549.1.7.1", contentInfo.ContentType.Value); + Assert.Equal("4D6963726F736F667420436F72706F726174696F6E", contentInfo.Content.ByteArrayToHex()); + + SignerInfoCollection signers = cms.SignerInfos; + Assert.Single(signers); + SignerInfo signer = signers[0]; + + using (X509Certificate2 cert = Certificates.RSAKeyTransferCapi1.GetCertificate()) + { + X509Certificate2Collection certs = cms.Certificates; + + Assert.Single(certs); + + Assert.Equal(cert, certs[0]); + Assert.Equal(cert, signer.Certificate); + Assert.NotSame(certs[0], signer.Certificate); + Assert.NotSame(cert, signer.Certificate); + Assert.NotSame(cert, certs[0]); + } + + Assert.Equal(SubjectIdentifierType.IssuerAndSerialNumber, signer.SignerIdentifier.Type); + Assert.Equal(Oids.Sha1, signer.DigestAlgorithm.Value); + +#if netcoreapp + Assert.Equal(Oids.Rsa, signer.SignatureAlgorithm.Value); + + Assert.Equal( + "5A1717621D450130B3463662160EEC06F7AE77E017DD95F294E97A0BDD433FE6" + + "B2CCB34FAAC33AEA50BFD7D9E78DC7174836284619F744278AE77B8495091E09" + + "6EEF682D9CA95F6E81C7DDCEDDA6A12316B453C894B5000701EB09DF57A53B73" + + "3A4E80DA27FA710870BD88C86E2FDB9DCA14D18BEB2F0C87E9632ABF02BE2FE3", + signer.GetSignature().ByteArrayToHex()); +#endif + + CryptographicAttributeObjectCollection signedAttrs = signer.SignedAttributes; + Assert.Empty(signedAttrs); + + CryptographicAttributeObjectCollection unsignedAttrs = signer.UnsignedAttributes; + Assert.Single(unsignedAttrs); + + Assert.Equal(Oids.CounterSigner, unsignedAttrs[0].Oid.Value); + + SignerInfoCollection counterSigners = signer.CounterSignerInfos; + Assert.Single(counterSigners); + SignerInfo counterSigner = counterSigners[0]; + + Assert.Equal(SubjectIdentifierType.SubjectKeyIdentifier, counterSigner.SignerIdentifier.Type); + + // Not universally true, but is in this case. + Assert.Equal(signer.Certificate, counterSigner.Certificate); + Assert.NotSame(signer.Certificate, counterSigner.Certificate); + + // Assert.NotThrows + signer.CheckSignature(true); + + // Assert.NotThrows + counterSigner.CheckSignature(true); + + // The document should be validly signed, then. + // Assert.NotThrows + cms.CheckSignature(true); + + // If CheckSignature succeeds then CheckHash cannot. + Assert.Throws(() => counterSigner.CheckHash()); + Assert.Throws(() => signer.CheckHash()); + + // Since there are no NoSignature signers, CheckHash won't throw. + // Assert.NotThrows + cms.CheckHash(); + } + + [Fact] + public static void CheckNoSignatureDocument() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.NoSignatureSignedWithAttributesAndCounterSignature); + + Assert.Equal(1, cms.Version); + Assert.Equal(Oids.Pkcs7Data, cms.ContentInfo.ContentType.Value); + + Assert.Equal( + "4D6963726F736F667420436F72706F726174696F6E", + cms.ContentInfo.Content.ByteArrayToHex()); + + X509Certificate2Collection cmsCerts = cms.Certificates; + Assert.Single(cmsCerts); + + SignerInfoCollection signers = cms.SignerInfos; + Assert.Single(signers); + + SignerInfo signer = signers[0]; + + Assert.Equal(SubjectIdentifierType.NoSignature, signer.SignerIdentifier.Type); + Assert.Null(signer.SignerIdentifier.Value); + Assert.Null(signer.Certificate); + Assert.Equal(Oids.Sha1, signer.DigestAlgorithm.Value); + +#if netcoreapp + Assert.Equal("1.3.6.1.5.5.7.6.2", signer.SignatureAlgorithm.Value); + + Assert.Equal( + "8B70D20D0477A35CD84AB962C10DC52FBA6FAD6B", + signer.GetSignature().ByteArrayToHex()); +#endif + + CryptographicAttributeObjectCollection signedAttrs = signer.SignedAttributes; + Assert.Equal(3, signedAttrs.Count); + + Assert.Single(signedAttrs[0].Values); + Assert.Single(signedAttrs[1].Values); + Assert.Single(signedAttrs[2].Values); + + Pkcs9ContentType contentType = (Pkcs9ContentType)signedAttrs[0].Values[0]; + Pkcs9SigningTime signingTime = (Pkcs9SigningTime)signedAttrs[1].Values[0]; + Pkcs9MessageDigest messageDigest = (Pkcs9MessageDigest)signedAttrs[2].Values[0]; + + Assert.Equal(Oids.Pkcs7Data, contentType.ContentType.Value); + Assert.Equal( + new DateTimeOffset(2017, 11, 1, 17, 17, 17, TimeSpan.Zero), + signingTime.SigningTime); + + Assert.Equal(DateTimeKind.Utc, signingTime.SigningTime.Kind); + + using (SHA1 sha1 = SHA1.Create()) + { + Assert.Equal( + sha1.ComputeHash(cms.ContentInfo.Content).ByteArrayToHex(), + messageDigest.MessageDigest.ByteArrayToHex()); + } + + CryptographicAttributeObjectCollection unsignedAttrs = signer.UnsignedAttributes; + Assert.Single(unsignedAttrs); + // No need to check the contents, it's a CounterSigner (tested next) + + SignerInfoCollection counterSigners = signer.CounterSignerInfos; + Assert.Single(counterSigners); + + SignerInfo counterSigner = counterSigners[0]; + Assert.Equal(3, counterSigner.Version); + Assert.Equal(SubjectIdentifierType.SubjectKeyIdentifier, counterSigner.SignerIdentifier.Type); + Assert.Equal("6B4A6B92FDED07EE0119F3674A96D1A70D2A588D", (string)counterSigner.SignerIdentifier.Value); + Assert.Equal(Oids.Sha1, counterSigner.DigestAlgorithm.Value); + + CryptographicAttributeObjectCollection csSignedAttrs = counterSigner.SignedAttributes; + Assert.Equal(2, csSignedAttrs.Count); + + // RFC3369 says that the content-type attribute must not be present for counter-signers, but it is. + // RFC2630 said that it "is not required". + Pkcs9ContentType csContentType = (Pkcs9ContentType)csSignedAttrs[0].Values[0]; + Pkcs9MessageDigest csMessageDigest = (Pkcs9MessageDigest)csSignedAttrs[1].Values[0]; + + Assert.Equal(Oids.Pkcs7Data, csContentType.ContentType.Value); + Assert.Equal( + "833378066BDCCBA7047EF6919843D181A57D6479", + csMessageDigest.MessageDigest.ByteArrayToHex()); + +#if netcoreapp + Assert.Equal(Oids.Rsa, counterSigner.SignatureAlgorithm.Value); + + Assert.Equal( + "2155D226DD744166E582D040E60535210195050EA00F2C179897198521DABD0E" + + "6B27750FD8BA5F9AAF58B4863B6226456F38553A22453CAF0A0F106766C7AB6F" + + "3D6AFD106753DC50F8A6E4F9E5508426D236C2DBB4BCB8162FA42E995CBA16A3" + + "40FD7C793569DF1B71368E68253299BC74E38312B40B8F52EAEDE10DF414A522", + counterSigner.GetSignature().ByteArrayToHex()); +#endif + + using (X509Certificate2 capiCert = Certificates.RSAKeyTransferCapi1.GetCertificate()) + { + Assert.Equal(capiCert, cmsCerts[0]); + Assert.Equal(capiCert, counterSigner.Certificate); + } + + // The counter-signer has a (real) signature, and a certificate, so CheckSignature + // will pass + counterSigner.CheckSignature(true); + + // The (primary) signer has a hash-only signature with no certificate, + // so CheckSignature will fail. + Assert.Throws(() => signer.CheckSignature(true)); + + // The (primary) signer is a NoSignature type, so CheckHash will succeed + signer.CheckHash(); + + // The document has a NoSignature signer, so CheckHash will do something (and succeed). + cms.CheckHash(); + + // Since the document's primary signer is NoSignature, CheckSignature will fail + Assert.Throws(() => cms.CheckSignature(true)); + } + + [Fact] + public static void NonEmbeddedCertificate() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaCapiTransfer1_NoEmbeddedCert); + + Assert.Equal(3, cms.Version); + Assert.Equal(Oids.Pkcs7Data, cms.ContentInfo.ContentType.Value); + + Assert.Equal( + "4D6963726F736F667420436F72706F726174696F6E", + cms.ContentInfo.Content.ByteArrayToHex()); + + Assert.Empty(cms.Certificates); + + SignerInfoCollection signers = cms.SignerInfos; + Assert.Single(signers); + + SignerInfo signer = signers[0]; + Assert.Equal(3, signer.Version); + Assert.Equal(SubjectIdentifierType.SubjectKeyIdentifier, signer.SignerIdentifier.Type); + Assert.Equal("6B4A6B92FDED07EE0119F3674A96D1A70D2A588D", (string)signer.SignerIdentifier.Value); + Assert.Equal(Oids.Sha1, signer.DigestAlgorithm.Value); + + CryptographicAttributeObjectCollection signedAttrs = signer.SignedAttributes; + Assert.Equal(3, signedAttrs.Count); + + Pkcs9ContentType contentType = (Pkcs9ContentType)signedAttrs[0].Values[0]; + Pkcs9SigningTime signingTime = (Pkcs9SigningTime)signedAttrs[1].Values[0]; + Pkcs9MessageDigest messageDigest = (Pkcs9MessageDigest)signedAttrs[2].Values[0]; + + Assert.Equal(Oids.Pkcs7Data, contentType.ContentType.Value); + Assert.Equal( + new DateTimeOffset(2017, 11, 2, 15, 34, 4, TimeSpan.Zero), + signingTime.SigningTime); + + Assert.Equal(DateTimeKind.Utc, signingTime.SigningTime.Kind); + + Assert.Empty(signer.UnsignedAttributes); + Assert.Empty(signer.CounterSignerInfos); + Assert.Null(signer.Certificate); + +#if netcoreapp + Assert.Equal(Oids.Rsa, signer.SignatureAlgorithm.Value); + + Assert.Equal( + "0EDE3870B8A80B45A21BAEC4681D059B46502E1B1AA6B8920CF50D4D837646A5" + + "5559B4C05849126C655D95FF3C6C1B420E07DC42629F294EE69822FEA56F32D4" + + "1B824CBB6BF809B7583C27E77B7AC58DFC925B1C60EA4A67AA84D73FC9E9191D" + + "33B36645F17FD6748A2D8B12C6C384C3C734D27273386211E4518FE2B4ED0147", + signer.GetSignature().ByteArrayToHex()); +#endif + + using (SHA1 sha1 = SHA1.Create()) + { + Assert.Equal( + sha1.ComputeHash(cms.ContentInfo.Content).ByteArrayToHex(), + messageDigest.MessageDigest.ByteArrayToHex()); + } + + // Since it's not NoSignature CheckHash will throw. + Assert.Throws(() => signer.CheckHash()); + + // Since there's no matched certificate CheckSignature will throw. + Assert.Throws(() => signer.CheckSignature(true)); + + // Since there are no NoSignature signers, SignedCms.CheckHash will succeed + cms.CheckHash(); + + // Since there are no matched certificates SignedCms.CheckSignature will throw. + Assert.Throws(() => cms.CheckSignature(true)); + + using (X509Certificate2 wrongCert = Certificates.RSAKeyTransfer1.GetCertificate()) + using (X509Certificate2 rightCert = Certificates.RSAKeyTransferCapi1.GetCertificate()) + { + X509Certificate2Collection coll = new X509Certificate2Collection(wrongCert); + + // Wrong certificate, still throw. + Assert.Throws(() => signer.CheckSignature(coll, true)); + Assert.Throws(() => cms.CheckSignature(coll, true)); + + coll = new X509Certificate2Collection(rightCert); + + // Right cert, success + signer.CheckSignature(coll, true); + Assert.Null(signer.Certificate); + cms.CheckSignature(coll, true); + Assert.Null(cms.SignerInfos[0].Certificate); + + coll.Add(wrongCert); + + // Both right and wrong, success + signer.CheckSignature(coll, true); + Assert.Null(signer.Certificate); + cms.CheckSignature(coll, true); + Assert.Null(cms.SignerInfos[0].Certificate); + + coll = new X509Certificate2Collection(wrongCert); + + // Just wrong again, no accidental stateful match + Assert.Throws(() => signer.CheckSignature(coll, true)); + Assert.Throws(() => cms.CheckSignature(coll, true)); + + coll.Add(rightCert); + + // Wrong then right, success + signer.CheckSignature(coll, true); + Assert.Null(signer.Certificate); + cms.CheckSignature(coll, true); + Assert.Null(cms.SignerInfos[0].Certificate); + } + } + + [Fact] + public static void ReadRsaPkcs1DoubleCounterSigned() + { + SignedCms cms = new SignedCms(); + + cms.Decode(SignedDocuments.OneRsaSignerTwoRsaCounterSigners); + + Assert.Equal(1, cms.Version); + + ContentInfo contentInfo = cms.ContentInfo; + Assert.Equal("1.2.840.113549.1.7.1", contentInfo.ContentType.Value); + Assert.Equal("4D6963726F736F667420436F72706F726174696F6E", contentInfo.Content.ByteArrayToHex()); + + SignerInfoCollection signers = cms.SignerInfos; + Assert.Single(signers); + SignerInfo signer = signers[0]; + + using (X509Certificate2 cert = Certificates.RSAKeyTransferCapi1.GetCertificate()) + { + X509Certificate2Collection certs = cms.Certificates; + + Assert.Equal(2, certs.Count); + + Assert.Equal(cert, certs[1]); + Assert.Equal(cert, signer.Certificate); + Assert.NotSame(certs[1], signer.Certificate); + Assert.NotSame(cert, signer.Certificate); + Assert.NotSame(cert, certs[1]); + } + + Assert.Equal(SubjectIdentifierType.IssuerAndSerialNumber, signer.SignerIdentifier.Type); + Assert.Equal(Oids.Sha1, signer.DigestAlgorithm.Value); + +#if netcoreapp + Assert.Equal(Oids.Rsa, signer.SignatureAlgorithm.Value); + + Assert.Equal( + "5A1717621D450130B3463662160EEC06F7AE77E017DD95F294E97A0BDD433FE6" + + "B2CCB34FAAC33AEA50BFD7D9E78DC7174836284619F744278AE77B8495091E09" + + "6EEF682D9CA95F6E81C7DDCEDDA6A12316B453C894B5000701EB09DF57A53B73" + + "3A4E80DA27FA710870BD88C86E2FDB9DCA14D18BEB2F0C87E9632ABF02BE2FE3", + signer.GetSignature().ByteArrayToHex()); +#endif + + CryptographicAttributeObjectCollection signedAttrs = signer.SignedAttributes; + Assert.Empty(signedAttrs); + + CryptographicAttributeObjectCollection unsignedAttrs = signer.UnsignedAttributes; + Assert.Equal(2, unsignedAttrs.Count); + + Assert.Equal(Oids.CounterSigner, unsignedAttrs[0].Oid.Value); + + SignerInfoCollection counterSigners = signer.CounterSignerInfos; + Assert.Equal(2, counterSigners.Count); + SignerInfo cs1 = counterSigners[0]; + SignerInfo cs2 = counterSigners[1]; + + Assert.Equal(SubjectIdentifierType.SubjectKeyIdentifier, cs1.SignerIdentifier.Type); + + // Not universally true, but is in this case. + Assert.Equal(signer.Certificate, cs1.Certificate); + Assert.NotSame(signer.Certificate, cs1.Certificate); + + Assert.Equal(SubjectIdentifierType.IssuerAndSerialNumber, cs2.SignerIdentifier.Type); + Assert.NotEqual(signer.Certificate, cs2.Certificate); + + // Assert.NotThrows + signer.CheckSignature(true); + + // Assert.NotThrows + cs1.CheckSignature(true); + + // Assert.NotThrows + cs2.CheckSignature(true); + + // The document should be validly signed, then. + // Assert.NotThrows + cms.CheckSignature(true); + +#if netcoreapp + Assert.Equal( + "1AA282DBED4D862D7CEA30F803E790BDB0C97EE852778CEEDDCD94BB9304A155" + + "2E60A8D36052AC8C2D28755F3B2F473824100AB3A6ABD4C15ABD77E0FFE13D0D" + + "F253BCD99C718FA673B6CB0CBBC68CE5A4AC671298C0A07C7223522E0E7FFF15" + + "CEDBAB55AAA99588517674671691065EB083FB729D1E9C04B2BF99A9953DAA5E", + cs2.GetSignature().ByteArrayToHex()); +#endif + + // If CheckSignature succeeds then CheckHash cannot. + Assert.Throws(() => cs1.CheckHash()); + Assert.Throws(() => cs2.CheckHash()); + Assert.Throws(() => signer.CheckHash()); + + // Since there are no NoSignature signers, CheckHash won't throw. + // Assert.NotThrows + cms.CheckHash(); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedDocuments.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedDocuments.cs new file mode 100644 index 0000000000..7d0e1fbe06 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedDocuments.cs @@ -0,0 +1,399 @@ +// 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 Test.Cryptography; + +namespace System.Security.Cryptography.Pkcs.Tests +{ + internal static class SignedDocuments + { + internal static readonly byte[] RsaPssDocument = ( + "308204EC06092A864886F70D010702A08204DD308204D9020103310D300B0609" + + "608648016503040201301F06092A864886F70D010701A0120410546869732069" + + "73206120746573740D0AA08202CA308202C63082022EA003020102020900F399" + + "4D1706DEC3F8300D06092A864886F70D01010B0500307B310B30090603550406" + + "130255533113301106035504080C0A57617368696E67746F6E3110300E060355" + + "04070C075265646D6F6E6431183016060355040A0C0F4D6963726F736F667420" + + "436F72702E31173015060355040B0C0E2E4E4554204672616D65776F726B3112" + + "301006035504030C096C6F63616C686F7374301E170D31363033303230323337" + + "35345A170D3137303330323032333735345A307B310B30090603550406130255" + + "533113301106035504080C0A57617368696E67746F6E3110300E06035504070C" + + "075265646D6F6E6431183016060355040A0C0F4D6963726F736F667420436F72" + + "702E31173015060355040B0C0E2E4E4554204672616D65776F726B3112301006" + + "035504030C096C6F63616C686F73743081A0300D06092A864886F70D01010105" + + "0003818E0030818A02818200BCACB1A5349D7B35A580AC3B3998EB15EBF900EC" + + "B329BF1F75717A00B2199C8A18D791B592B7EC52BD5AF2DB0D3B635F0595753D" + + "FF7BA7C9872DBF7E3226DEF44A07CA568D1017992C2B41BFE5EC3570824CF1F4" + + "B15919FED513FDA56204AF2034A2D08FF04C2CCA49D168FA03FA2FA32FCCD348" + + "4C15F0A2E5467C76FC760B55090203010001A350304E301D0603551D0E041604" + + "141063CAB14FB14C47DC211C0E0285F3EE5946BF2D301F0603551D2304183016" + + "80141063CAB14FB14C47DC211C0E0285F3EE5946BF2D300C0603551D13040530" + + "030101FF300D06092A864886F70D01010B050003818200435774FB66802AB3CE" + + "2F1392C079483B48CC8913E0BF3B7AD88351E4C15B55CAD3061AA5875900C56B" + + "2E7E84BB49CA2A0C1895BD60149C6A0AE983E48370E2144052943B066BD85F70" + + "543CF6F2F255C028AE1DC8FB898AD3DCA97BF1D607370287077A4C147268C911" + + "8CF9CAD318D2830D3468727E0A3247B3FEB8D87A7DE4F1E2318201D4308201D0" + + "02010380141063CAB14FB14C47DC211C0E0285F3EE5946BF2D300B0609608648" + + "016503040201A081E4301806092A864886F70D010903310B06092A864886F70D" + + "010701301C06092A864886F70D010905310F170D313731303236303130363235" + + "5A302F06092A864886F70D0109043122042007849DC26FCBB2F3BD5F57BDF214" + + "BAE374575F1BD4E6816482324799417CB379307906092A864886F70D01090F31" + + "6C306A300B060960864801650304012A300B0609608648016503040116300B06" + + "09608648016503040102300A06082A864886F70D0307300E06082A864886F70D" + + "030202020080300D06082A864886F70D0302020140300706052B0E030207300D" + + "06082A864886F70D0302020128303D06092A864886F70D01010A3030A00D300B" + + "0609608648016503040201A11A301806092A864886F70D010108300B06096086" + + "48016503040201A20302015F048181B93E81D141B3C9F159AB0021910635DC72" + + "E8E860BE43C28E5D53243D6DC247B7D4F18C20195E80DEDCC75B29C43CE5047A" + + "D775B65BFC93589BD748B950C68BADDF1A4673130302BBDA8667D5DDE5EA91EC" + + "CB13A9B4C04F1C4842FEB1697B7669C7692DD3BDAE13B5AA8EE3EB5679F3729D" + + "1DC4F2EB9DC89B7E8773F2F8C6108C05").HexToByteArray(); + + public static byte[] RsaPkcs1OneSignerIssuerAndSerialNumber = ( + "3082033706092A864886F70D010702A082032830820324020101310B30090605" + + "2B0E03021A0500302406092A864886F70D010701A01704154D6963726F736F66" + + "7420436F72706F726174696F6EA08202103082020C30820179A0030201020210" + + "5D2FFFF863BABC9B4D3C80AB178A4CCA300906052B0E03021D0500301E311C30" + + "1A060355040313135253414B65795472616E736665724361706931301E170D31" + + "35303431353037303030305A170D3235303431353037303030305A301E311C30" + + "1A060355040313135253414B65795472616E73666572436170693130819F300D" + + "06092A864886F70D010101050003818D0030818902818100AA272700586C0CC4" + + "1B05C65C7D846F5A2BC27B03E301C37D9BFF6D75B6EB6671BA9596C5C63BA2B1" + + "AF5C318D9CA39E7400D10C238AC72630579211B86570D1A1D44EC86AA8F6C9D2" + + "B4E283EA3535923F398A312A23EAEACD8D34FAACA965CD910B37DA4093EF76C1" + + "3B337C1AFAB7D1D07E317B41A336BAA4111299F99424408D0203010001A35330" + + "51304F0603551D0104483046801015432DB116B35D07E4BA89EDB2469D7AA120" + + "301E311C301A060355040313135253414B65795472616E736665724361706931" + + "82105D2FFFF863BABC9B4D3C80AB178A4CCA300906052B0E03021D0500038181" + + "0081E5535D8ECEEF265ACBC82F6C5F8BC9D84319265F3CCF23369FA533C8DC19" + + "38952C5931662D9ECD8B1E7B81749E48468167E2FCE3D019FA70D54646975B6D" + + "C2A3BA72D5A5274C1866DA6D7A5DF47938E034A075D11957D653B5C78E5291E4" + + "401045576F6D4EDA81BEF3C369AF56121E49A083C8D1ADB09F291822E99A4296" + + "463181D73081D40201013032301E311C301A060355040313135253414B657954" + + "72616E73666572436170693102105D2FFFF863BABC9B4D3C80AB178A4CCA3009" + + "06052B0E03021A0500300D06092A864886F70D01010105000481805A1717621D" + + "450130B3463662160EEC06F7AE77E017DD95F294E97A0BDD433FE6B2CCB34FAA" + + "C33AEA50BFD7D9E78DC7174836284619F744278AE77B8495091E096EEF682D9C" + + "A95F6E81C7DDCEDDA6A12316B453C894B5000701EB09DF57A53B733A4E80DA27" + + "FA710870BD88C86E2FDB9DCA14D18BEB2F0C87E9632ABF02BE2FE3").HexToByteArray(); + + public static byte[] CounterSignedRsaPkcs1OneSigner = ( + "3082044906092A864886F70D010702A082043A30820436020101310B30090605" + + "2B0E03021A0500302406092A864886F70D010701A01704154D6963726F736F66" + + "7420436F72706F726174696F6EA08202103082020C30820179A0030201020210" + + "5D2FFFF863BABC9B4D3C80AB178A4CCA300906052B0E03021D0500301E311C30" + + "1A060355040313135253414B65795472616E736665724361706931301E170D31" + + "35303431353037303030305A170D3235303431353037303030305A301E311C30" + + "1A060355040313135253414B65795472616E73666572436170693130819F300D" + + "06092A864886F70D010101050003818D0030818902818100AA272700586C0CC4" + + "1B05C65C7D846F5A2BC27B03E301C37D9BFF6D75B6EB6671BA9596C5C63BA2B1" + + "AF5C318D9CA39E7400D10C238AC72630579211B86570D1A1D44EC86AA8F6C9D2" + + "B4E283EA3535923F398A312A23EAEACD8D34FAACA965CD910B37DA4093EF76C1" + + "3B337C1AFAB7D1D07E317B41A336BAA4111299F99424408D0203010001A35330" + + "51304F0603551D0104483046801015432DB116B35D07E4BA89EDB2469D7AA120" + + "301E311C301A060355040313135253414B65795472616E736665724361706931" + + "82105D2FFFF863BABC9B4D3C80AB178A4CCA300906052B0E03021D0500038181" + + "0081E5535D8ECEEF265ACBC82F6C5F8BC9D84319265F3CCF23369FA533C8DC19" + + "38952C5931662D9ECD8B1E7B81749E48468167E2FCE3D019FA70D54646975B6D" + + "C2A3BA72D5A5274C1866DA6D7A5DF47938E034A075D11957D653B5C78E5291E4" + + "401045576F6D4EDA81BEF3C369AF56121E49A083C8D1ADB09F291822E99A4296" + + "46318201E8308201E40201013032301E311C301A060355040313135253414B65" + + "795472616E73666572436170693102105D2FFFF863BABC9B4D3C80AB178A4CCA" + + "300906052B0E03021A0500300D06092A864886F70D01010105000481805A1717" + + "621D450130B3463662160EEC06F7AE77E017DD95F294E97A0BDD433FE6B2CCB3" + + "4FAAC33AEA50BFD7D9E78DC7174836284619F744278AE77B8495091E096EEF68" + + "2D9CA95F6E81C7DDCEDDA6A12316B453C894B5000701EB09DF57A53B733A4E80" + + "DA27FA710870BD88C86E2FDB9DCA14D18BEB2F0C87E9632ABF02BE2FE3A18201" + + "0C3082010806092A864886F70D0109063181FA3081F702010380146B4A6B92FD" + + "ED07EE0119F3674A96D1A70D2A588D300906052B0E03021A0500A03F30180609" + + "2A864886F70D010903310B06092A864886F70D010701302306092A864886F70D" + + "010904311604148C054D6DF2B08E69A86D8DB23C1A509123F9DBA4300D06092A" + + "864886F70D0101010500048180962518DEF789B0886C7E6295754ECDBDC4CB9D" + + "153ECE5EBBE7A82142B92C30DDBBDFC22B5B954F5D844CBAEDCA9C4A068B2483" + + "0E2A96141A5D0320B69EA5DFCFEA441E162D04506F8FFA79D7312524F111A9B9" + + "B0184007139F94E46C816E0E33F010AEB949F5D884DC8987765002F7A643F34B" + + "7654E3B2FD5FB34A420279B1EA").HexToByteArray(); + + public static byte[] NoSignatureSignedWithAttributesAndCounterSignature = ( + "3082042406092A864886F70D010702A082041530820411020101310B30090605" + + "2B0E03021A0500302406092A864886F70D010701A01704154D6963726F736F66" + + "7420436F72706F726174696F6EA08202103082020C30820179A0030201020210" + + "5D2FFFF863BABC9B4D3C80AB178A4CCA300906052B0E03021D0500301E311C30" + + "1A060355040313135253414B65795472616E736665724361706931301E170D31" + + "35303431353037303030305A170D3235303431353037303030305A301E311C30" + + "1A060355040313135253414B65795472616E73666572436170693130819F300D" + + "06092A864886F70D010101050003818D0030818902818100AA272700586C0CC4" + + "1B05C65C7D846F5A2BC27B03E301C37D9BFF6D75B6EB6671BA9596C5C63BA2B1" + + "AF5C318D9CA39E7400D10C238AC72630579211B86570D1A1D44EC86AA8F6C9D2" + + "B4E283EA3535923F398A312A23EAEACD8D34FAACA965CD910B37DA4093EF76C1" + + "3B337C1AFAB7D1D07E317B41A336BAA4111299F99424408D0203010001A35330" + + "51304F0603551D0104483046801015432DB116B35D07E4BA89EDB2469D7AA120" + + "301E311C301A060355040313135253414B65795472616E736665724361706931" + + "82105D2FFFF863BABC9B4D3C80AB178A4CCA300906052B0E03021D0500038181" + + "0081E5535D8ECEEF265ACBC82F6C5F8BC9D84319265F3CCF23369FA533C8DC19" + + "38952C5931662D9ECD8B1E7B81749E48468167E2FCE3D019FA70D54646975B6D" + + "C2A3BA72D5A5274C1866DA6D7A5DF47938E034A075D11957D653B5C78E5291E4" + + "401045576F6D4EDA81BEF3C369AF56121E49A083C8D1ADB09F291822E99A4296" + + "46318201C3308201BF020101301C3017311530130603550403130C44756D6D79" + + "205369676E6572020100300906052B0E03021A0500A05D301806092A864886F7" + + "0D010903310B06092A864886F70D010701301C06092A864886F70D010905310F" + + "170D3137313130313137313731375A302306092A864886F70D01090431160414" + + "A5F085E7F326F3D6CA3BFD6280A3DE8EBC2EA60E300C06082B06010505070602" + + "050004148B70D20D0477A35CD84AB962C10DC52FBA6FAD6BA182010C30820108" + + "06092A864886F70D0109063181FA3081F702010380146B4A6B92FDED07EE0119" + + "F3674A96D1A70D2A588D300906052B0E03021A0500A03F301806092A864886F7" + + "0D010903310B06092A864886F70D010701302306092A864886F70D0109043116" + + "0414833378066BDCCBA7047EF6919843D181A57D6479300D06092A864886F70D" + + "01010105000481802155D226DD744166E582D040E60535210195050EA00F2C17" + + "9897198521DABD0E6B27750FD8BA5F9AAF58B4863B6226456F38553A22453CAF" + + "0A0F106766C7AB6F3D6AFD106753DC50F8A6E4F9E5508426D236C2DBB4BCB816" + + "2FA42E995CBA16A340FD7C793569DF1B71368E68253299BC74E38312B40B8F52" + + "EAEDE10DF414A522").HexToByteArray(); + + public static byte[] NoSignatureWithNoAttributes = ( + "30819B06092A864886F70D010702A0818D30818A020101310B300906052B0E03" + + "021A0500302406092A864886F70D010701A01704154D6963726F736F66742043" + + "6F72706F726174696F6E31523050020101301C3017311530130603550403130C" + + "44756D6D79205369676E6572020100300906052B0E03021A0500300C06082B06" + + "01050507060205000414A5F085E7F326F3D6CA3BFD6280A3DE8EBC2EA60E").HexToByteArray(); + + public static byte[] RsaCapiTransfer1_NoEmbeddedCert = ( + "3082016606092A864886F70D010702A082015730820153020103310B30090605" + + "2B0E03021A0500302406092A864886F70D010701A01704154D6963726F736F66" + + "7420436F72706F726174696F6E318201193082011502010380146B4A6B92FDED" + + "07EE0119F3674A96D1A70D2A588D300906052B0E03021A0500A05D301806092A" + + "864886F70D010903310B06092A864886F70D010701301C06092A864886F70D01" + + "0905310F170D3137313130323135333430345A302306092A864886F70D010904" + + "31160414A5F085E7F326F3D6CA3BFD6280A3DE8EBC2EA60E300D06092A864886" + + "F70D01010105000481800EDE3870B8A80B45A21BAEC4681D059B46502E1B1AA6" + + "B8920CF50D4D837646A55559B4C05849126C655D95FF3C6C1B420E07DC42629F" + + "294EE69822FEA56F32D41B824CBB6BF809B7583C27E77B7AC58DFC925B1C60EA" + + "4A67AA84D73FC9E9191D33B36645F17FD6748A2D8B12C6C384C3C734D2727338" + + "6211E4518FE2B4ED0147").HexToByteArray(); + + public static byte[] OneRsaSignerTwoRsaCounterSigners = ( + "3082075106092A864886F70D010702A08207423082073E020101310B30090605" + + "2B0E03021A0500302406092A864886F70D010701A01704154D6963726F736F66" + + "7420436F72706F726174696F6EA08203F9308201E530820152A0030201020210" + + "D5B5BC1C458A558845BFF51CB4DFF31C300906052B0E03021D05003011310F30" + + "0D060355040313064D794E616D65301E170D3130303430313038303030305A17" + + "0D3131303430313038303030305A3011310F300D060355040313064D794E616D" + + "6530819F300D06092A864886F70D010101050003818D0030818902818100B11E" + + "30EA87424A371E30227E933CE6BE0E65FF1C189D0D888EC8FF13AA7B42B68056" + + "128322B21F2B6976609B62B6BC4CF2E55FF5AE64E9B68C78A3C2DACC916A1BC7" + + "322DD353B32898675CFB5B298B176D978B1F12313E3D865BC53465A11CCA1068" + + "70A4B5D50A2C410938240E92B64902BAEA23EB093D9599E9E372E48336730203" + + "010001A346304430420603551D01043B3039801024859EBF125E76AF3F0D7979" + + "B4AC7A96A1133011310F300D060355040313064D794E616D658210D5B5BC1C45" + + "8A558845BFF51CB4DFF31C300906052B0E03021D0500038181009BF6E2CF830E" + + "D485B86D6B9E8DFFDCD65EFC7EC145CB9348923710666791FCFA3AB59D689FFD" + + "7234B7872611C5C23E5E0714531ABADB5DE492D2C736E1C929E648A65CC9EB63" + + "CD84E57B5909DD5DDF5DBBBA4A6498B9CA225B6E368B94913BFC24DE6B2BD9A2" + + "6B192B957304B89531E902FFC91B54B237BB228BE8AFCDA264763082020C3082" + + "0179A00302010202105D2FFFF863BABC9B4D3C80AB178A4CCA300906052B0E03" + + "021D0500301E311C301A060355040313135253414B65795472616E7366657243" + + "61706931301E170D3135303431353037303030305A170D323530343135303730" + + "3030305A301E311C301A060355040313135253414B65795472616E7366657243" + + "6170693130819F300D06092A864886F70D010101050003818D00308189028181" + + "00AA272700586C0CC41B05C65C7D846F5A2BC27B03E301C37D9BFF6D75B6EB66" + + "71BA9596C5C63BA2B1AF5C318D9CA39E7400D10C238AC72630579211B86570D1" + + "A1D44EC86AA8F6C9D2B4E283EA3535923F398A312A23EAEACD8D34FAACA965CD" + + "910B37DA4093EF76C13B337C1AFAB7D1D07E317B41A336BAA4111299F9942440" + + "8D0203010001A3533051304F0603551D0104483046801015432DB116B35D07E4" + + "BA89EDB2469D7AA120301E311C301A060355040313135253414B65795472616E" + + "73666572436170693182105D2FFFF863BABC9B4D3C80AB178A4CCA300906052B" + + "0E03021D05000381810081E5535D8ECEEF265ACBC82F6C5F8BC9D84319265F3C" + + "CF23369FA533C8DC1938952C5931662D9ECD8B1E7B81749E48468167E2FCE3D0" + + "19FA70D54646975B6DC2A3BA72D5A5274C1866DA6D7A5DF47938E034A075D119" + + "57D653B5C78E5291E4401045576F6D4EDA81BEF3C369AF56121E49A083C8D1AD" + + "B09F291822E99A42964631820307308203030201013032301E311C301A060355" + + "040313135253414B65795472616E73666572436170693102105D2FFFF863BABC" + + "9B4D3C80AB178A4CCA300906052B0E03021A0500300D06092A864886F70D0101" + + "0105000481805A1717621D450130B3463662160EEC06F7AE77E017DD95F294E9" + + "7A0BDD433FE6B2CCB34FAAC33AEA50BFD7D9E78DC7174836284619F744278AE7" + + "7B8495091E096EEF682D9CA95F6E81C7DDCEDDA6A12316B453C894B5000701EB" + + "09DF57A53B733A4E80DA27FA710870BD88C86E2FDB9DCA14D18BEB2F0C87E963" + + "2ABF02BE2FE3A182022B3082010806092A864886F70D0109063181FA3081F702" + + "010380146B4A6B92FDED07EE0119F3674A96D1A70D2A588D300906052B0E0302" + + "1A0500A03F301806092A864886F70D010903310B06092A864886F70D01070130" + + "2306092A864886F70D010904311604148C054D6DF2B08E69A86D8DB23C1A5091" + + "23F9DBA4300D06092A864886F70D0101010500048180962518DEF789B0886C7E" + + "6295754ECDBDC4CB9D153ECE5EBBE7A82142B92C30DDBBDFC22B5B954F5D844C" + + "BAEDCA9C4A068B24830E2A96141A5D0320B69EA5DFCFEA441E162D04506F8FFA" + + "79D7312524F111A9B9B0184007139F94E46C816E0E33F010AEB949F5D884DC89" + + "87765002F7A643F34B7654E3B2FD5FB34A420279B1EA3082011B06092A864886" + + "F70D0109063182010C3082010802010130253011310F300D060355040313064D" + + "794E616D650210D5B5BC1C458A558845BFF51CB4DFF31C300906052B0E03021A" + + "0500A03F301806092A864886F70D010903310B06092A864886F70D0107013023" + + "06092A864886F70D010904311604148C054D6DF2B08E69A86D8DB23C1A509123" + + "F9DBA4300D06092A864886F70D01010105000481801AA282DBED4D862D7CEA30" + + "F803E790BDB0C97EE852778CEEDDCD94BB9304A1552E60A8D36052AC8C2D2875" + + "5F3B2F473824100AB3A6ABD4C15ABD77E0FFE13D0DF253BCD99C718FA673B6CB" + + "0CBBC68CE5A4AC671298C0A07C7223522E0E7FFF15CEDBAB55AAA99588517674" + + "671691065EB083FB729D1E9C04B2BF99A9953DAA5E").HexToByteArray(); + + public static readonly byte[] RsaPkcs1CounterSignedWithNoSignature = ( + "308203E106092A864886F70D010702A08203D2308203CE020101310B30090605" + + "2B0E03021A0500302406092A864886F70D010701A01704154D6963726F736F66" + + "7420436F72706F726174696F6EA08202103082020C30820179A0030201020210" + + "5D2FFFF863BABC9B4D3C80AB178A4CCA300906052B0E03021D0500301E311C30" + + "1A060355040313135253414B65795472616E736665724361706931301E170D31" + + "35303431353037303030305A170D3235303431353037303030305A301E311C30" + + "1A060355040313135253414B65795472616E73666572436170693130819F300D" + + "06092A864886F70D010101050003818D0030818902818100AA272700586C0CC4" + + "1B05C65C7D846F5A2BC27B03E301C37D9BFF6D75B6EB6671BA9596C5C63BA2B1" + + "AF5C318D9CA39E7400D10C238AC72630579211B86570D1A1D44EC86AA8F6C9D2" + + "B4E283EA3535923F398A312A23EAEACD8D34FAACA965CD910B37DA4093EF76C1" + + "3B337C1AFAB7D1D07E317B41A336BAA4111299F99424408D0203010001A35330" + + "51304F0603551D0104483046801015432DB116B35D07E4BA89EDB2469D7AA120" + + "301E311C301A060355040313135253414B65795472616E736665724361706931" + + "82105D2FFFF863BABC9B4D3C80AB178A4CCA300906052B0E03021D0500038181" + + "0081E5535D8ECEEF265ACBC82F6C5F8BC9D84319265F3CCF23369FA533C8DC19" + + "38952C5931662D9ECD8B1E7B81749E48468167E2FCE3D019FA70D54646975B6D" + + "C2A3BA72D5A5274C1866DA6D7A5DF47938E034A075D11957D653B5C78E5291E4" + + "401045576F6D4EDA81BEF3C369AF56121E49A083C8D1ADB09F291822E99A4296" + + "46318201803082017C0201013032301E311C301A060355040313135253414B65" + + "795472616E73666572436170693102105D2FFFF863BABC9B4D3C80AB178A4CCA" + + "300906052B0E03021A0500300D06092A864886F70D01010105000481805A1717" + + "621D450130B3463662160EEC06F7AE77E017DD95F294E97A0BDD433FE6B2CCB3" + + "4FAAC33AEA50BFD7D9E78DC7174836284619F744278AE77B8495091E096EEF68" + + "2D9CA95F6E81C7DDCEDDA6A12316B453C894B5000701EB09DF57A53B733A4E80" + + "DA27FA710870BD88C86E2FDB9DCA14D18BEB2F0C87E9632ABF02BE2FE3A181A5" + + "3081A206092A864886F70D010906318194308191020101301C30173115301306" + + "03550403130C44756D6D79205369676E6572020100300906052B0E03021A0500" + + "A03F301806092A864886F70D010903310B06092A864886F70D01070130230609" + + "2A864886F70D010904311604148C054D6DF2B08E69A86D8DB23C1A509123F9DB" + + "A4300C06082B060105050706020500041466124B3D99FE06A19BBD3C83C593AB" + + "55D875E28B").HexToByteArray(); + + public static readonly byte[] UnsortedSignerInfos = ( + "30820B1E06092A864886F70D010702A0820B0F30820B0B020103310B30090605" + + "2B0E03021A0500301006092A864886F70D010701A003040107A0820540308202" + + "0C30820179A00302010202105D2FFFF863BABC9B4D3C80AB178A4CCA30090605" + + "2B0E03021D0500301E311C301A060355040313135253414B65795472616E7366" + + "65724361706931301E170D3135303431353037303030305A170D323530343135" + + "3037303030305A301E311C301A060355040313135253414B65795472616E7366" + + "6572436170693130819F300D06092A864886F70D010101050003818D00308189" + + "02818100AA272700586C0CC41B05C65C7D846F5A2BC27B03E301C37D9BFF6D75" + + "B6EB6671BA9596C5C63BA2B1AF5C318D9CA39E7400D10C238AC72630579211B8" + + "6570D1A1D44EC86AA8F6C9D2B4E283EA3535923F398A312A23EAEACD8D34FAAC" + + "A965CD910B37DA4093EF76C13B337C1AFAB7D1D07E317B41A336BAA4111299F9" + + "9424408D0203010001A3533051304F0603551D0104483046801015432DB116B3" + + "5D07E4BA89EDB2469D7AA120301E311C301A060355040313135253414B657954" + + "72616E73666572436170693182105D2FFFF863BABC9B4D3C80AB178A4CCA3009" + + "06052B0E03021D05000381810081E5535D8ECEEF265ACBC82F6C5F8BC9D84319" + + "265F3CCF23369FA533C8DC1938952C5931662D9ECD8B1E7B81749E48468167E2" + + "FCE3D019FA70D54646975B6DC2A3BA72D5A5274C1866DA6D7A5DF47938E034A0" + + "75D11957D653B5C78E5291E4401045576F6D4EDA81BEF3C369AF56121E49A083" + + "C8D1ADB09F291822E99A4296463082032C30820214A003020102020900E0D8AB" + + "6819D7306E300D06092A864886F70D01010B0500303831363034060355040313" + + "2D54776F2074686F7573616E6420666F7274792065696768742062697473206F" + + "662052534120676F6F646E657373301E170D3137313130333233353131355A17" + + "0D3138313130333233353131355A3038313630340603550403132D54776F2074" + + "686F7573616E6420666F7274792065696768742062697473206F662052534120" + + "676F6F646E65737330820122300D06092A864886F70D01010105000382010F00" + + "3082010A028201010096C114A5898D09133EF859F89C1D848BA8CB5258793E05" + + "B92D499C55EEFACE274BBBC26803FB813B9C11C6898153CC1745DED2C4D2672F" + + "807F0B2D957BC4B65EBC9DDE26E2EA7B2A6FE9A7C4D8BD1EF6032B8F0BB6AA33" + + "C8B57248B3D5E3901D8A38A283D7E25FF8E6F522381EE5484234CFF7B30C1746" + + "35418FA89E14C468AD89DCFCBBB535E5AF53510F9EA7F9DA8C1B53375B6DAB95" + + "A291439A5648726EE1012E41388E100691642CF6917F5569D8351F2782F435A5" + + "79014E8448EEA0C4AECAFF2F476799D88457E2C8BCB56E5E128782B4FE26AFF0" + + "720D91D52CCAFE344255808F5271D09F784F787E8323182080915BE0AE15A71D" + + "66476D0F264DD084F30203010001A3393037301D0603551D0E04160414745B5F" + + "12EF962E84B897E246D399A2BADEA9C5AC30090603551D1304023000300B0603" + + "551D0F040403020780300D06092A864886F70D01010B0500038201010087A15D" + + "F37FBD6E9DED7A8FFF25E60B731F635469BA01DD14BC03B2A24D99EFD8B894E9" + + "493D63EC88C496CB04B33DF25222544F23D43F4023612C4D97B719C1F9431E4D" + + "B7A580CDF66A3E5F0DAF89A267DD187ABFFB08361B1F79232376AA5FC5AD384C" + + "C2F98FE36C1CEA0B943E1E3961190648889C8ABE8397A5A338843CBFB1D8B212" + + "BE46685ACE7B80475CC7C97FC0377936ABD5F664E9C09C463897726650711A11" + + "10FA9866BC1C278D95E5636AB96FAE95CCD67FD572A8C727E2C03E7B24245731" + + "8BEC1BE52CA5BD9454A0A41140AE96ED1C56D220D1FD5DD3B1B4FB2AA0E04FC9" + + "4F7E3C7D476F298962245563953AD7225EDCEAC8B8509E49292E62D8BF318205" + + "A1308202FB0201038014745B5F12EF962E84B897E246D399A2BADEA9C5AC3009" + + "06052B0E03021A0500300D06092A864886F70D0101010500048201005E03C5E2" + + "E736792EFB1C8632C3A864AA6F0E930717FE02C755C0F94DC671244A371926F6" + + "09878DC8CBFCBA6F83A841B24F48952DA5344F2210BFE9B744E3367B1F8399C8" + + "96F675923A57E084EBD7DC76A24A1530CD513F0DF6A7703246BF335CC3D09776" + + "442942150F1C31B9B212AF48850B44B95EB5BD64105F09723EF6AD4711FD81CD" + + "1FC0418E68EA4428CED9E184126761BF2B25756B6D9BC1A0530E56D38F2A0B78" + + "3F21D6A5C0703C38F29A2B701B13CAFFCA1DC21C39059E4388E54AEA2519C4E8" + + "83C7A6BD78200DCB931CA6AB3D18DBBF46A5444C89B6DFE2F48F32C44BA9C030" + + "F399AC677AA323203137D33CEBFBF1BBF9A506309953B23C4100CA7CA18201C0" + + "308201BC06092A864886F70D010906318201AD308201A9020101304530383136" + + "30340603550403132D54776F2074686F7573616E6420666F7274792065696768" + + "742062697473206F662052534120676F6F646E657373020900E0D8AB6819D730" + + "6E300906052B0E03021A0500A03F301806092A864886F70D010903310B06092A" + + "864886F70D010701302306092A864886F70D0109043116041481BF56A6550A60" + + "A649B0D97971C49897635953D0300D06092A864886F70D010101050004820100" + + "6E41B7585FEB419005362FEAAAAFB2059E98F8905221A7564F7B0B5510CB221D" + + "F3DD914A4CD441EAC1C6746A6EC4FC8399C12A61C6B0F50DDA090F564F3D65B2" + + "6D4BDBC1CE3D39CF47CF33B0D269D15A9FAF2169C60887C3E2CC9828B5E16D45" + + "DC27A94BAF8D6650EE63D2DBB7DA319B3F61DD18E28AF6FE6DF2CC15C2910BD6" + + "0B7E038F2C6E8BAEC35CBBBF9484D4C76ECE041DF534B8713B6537854EFE6D58" + + "41768CCBB9A3B729FDDAE07780CB143A3EE5972DCDDF60A38C65CD3FFF35D1B6" + + "B76227C1B53831773DA441603F4FB5764D33AADE102F9B85D2CDAEC0E3D6C6E8" + + "C24C434BFAA3E12E02202142784ED0EB2D9CDCC276D21474747DCD3E4F4D54FC" + + "3081D40201013032301E311C301A060355040313135253414B65795472616E73" + + "666572436170693102105D2FFFF863BABC9B4D3C80AB178A4CCA300906052B0E" + + "03021A0500300D06092A864886F70D01010105000481805EB33C6A9ED5B62240" + + "90C431E79F51D70B4F2A7D31ED4ED8C3465F6E01281C3FFA44116238B2D168D8" + + "9154136DDB8B4EB31EA685FB719B7384510F5EF077A10DE6A5CA86F4F6D28B58" + + "79AFD6CFF0BDA005C2D7CFF53620D28988CBAA44F18AA2D50229FA930B0A7262" + + "D780DFDEC0334A97DF872F1D95087DC11A881568AF5B88308201C70201013045" + + "3038313630340603550403132D54776F2074686F7573616E6420666F72747920" + + "65696768742062697473206F662052534120676F6F646E657373020900E0D8AB" + + "6819D7306E300906052B0E03021A0500A05D301806092A864886F70D01090331" + + "0B06092A864886F70D010701301C06092A864886F70D010905310F170D313731" + + "3130393136303934315A302306092A864886F70D010904311604145D1BE7E9DD" + + "A1EE8896BE5B7E34A85EE16452A7B4300D06092A864886F70D01010105000482" + + "01000BB9410F23CFD9C1FCB16179612DB871224F5B88A8E2C012DCDBB3699780" + + "A3311FD330FFDD6DF1434C52DADD6E07D81FEF145B806E71AF471223914B98CD" + + "588CCCDFB50ABE3D991B11D62BD83DE158A9001BAED3549BC49B8C204D25C17B" + + "D042756B026692959E321ACC1AFE6BF52C9356FD49936116D2B3D1F6569F8A8B" + + "F0FBB2E403AD5788681F3AD131E57390ACB9B8C2EA0BE717F22EFE577EFB1063" + + "6AC465469191B7E4B3F03CF8DC6C310A20D2B0891BC27350C7231BC2EAABF129" + + "83755B4C0EDF8A0EE99A615D4E8B381C67A7CDB1405D98C2A6285FEDCED5A65F" + + "C45C31CD33E3CEB96223DB45E9156B9BD7C8E442C40ED1BB6866C03548616061" + + "3DAF").HexToByteArray(); + + public static byte[] OneDsa1024 = ( + "3082044206092A864886F70D010702A08204333082042F020103310B30090605" + + "2B0E03021A0500302406092A864886F70D010701A01704154D6963726F736F66" + + "7420436F72706F726174696F6EA08203913082038D3082034AA0030201020209" + + "00AB740A714AA83C92300B060960864801650304030230818D310B3009060355" + + "040613025553311330110603550408130A57617368696E67746F6E3110300E06" + + "0355040713075265646D6F6E64311E301C060355040A13154D6963726F736F66" + + "7420436F72706F726174696F6E3120301E060355040B13172E4E455420467261" + + "6D65776F726B2028436F7265465829311530130603550403130C313032342D62" + + "697420445341301E170D3135313132353134343030335A170D31353132323531" + + "34343030335A30818D310B300906035504061302555331133011060355040813" + + "0A57617368696E67746F6E3110300E060355040713075265646D6F6E64311E30" + + "1C060355040A13154D6963726F736F667420436F72706F726174696F6E312030" + + "1E060355040B13172E4E4554204672616D65776F726B2028436F726546582931" + + "1530130603550403130C313032342D62697420445341308201B73082012C0607" + + "2A8648CE3804013082011F02818100AEE3309FC7C9DB750D4C3797D333B3B9B2" + + "34B462868DB6FFBDED790B7FC8DDD574C2BD6F5E749622507AB2C09DF5EAAD84" + + "859FC0706A70BB8C9C8BE22B4890EF2325280E3A7F9A3CE341DBABEF6058D063" + + "EA6783478FF8B3B7A45E0CA3F7BAC9995DCFDDD56DF168E91349130F719A4E71" + + "7351FAAD1A77EAC043611DC5CC5A7F021500D23428A76743EA3B49C62EF0AA17" + + "314A85415F0902818100853F830BDAA738465300CFEE02418E6B07965658EAFD" + + "A7E338A2EB1531C0E0CA5EF1A12D9DDC7B550A5A205D1FF87F69500A4E4AF575" + + "9F3F6E7F0C48C55396B738164D9E35FB506BD50E090F6A497C70E7E868C61BD4" + + "477C1D62922B3DBB40B688DE7C175447E2E826901A109FAD624F1481B276BF63" + + "A665D99C87CEE9FD06330381840002818025B8E7078E149BAC35266762362002" + + "9F5E4A5D4126E336D56F1189F9FF71EA671B844EBD351514F27B69685DDF716B" + + "32F102D60EA520D56F544D19B2F08F5D9BDDA3CBA3A73287E21E559E6A075861" + + "94AFAC4F6E721EDCE49DE0029627626D7BD30EEB337311DB4FF62D7608997B6C" + + "C32E9C42859820CA7EF399590D5A388C48A330302E302C0603551D1104253023" + + "87047F00000187100000000000000000000000000000000182096C6F63616C68" + + "6F7374300B0609608648016503040302033000302D021500B9316CC7E05C9F79" + + "197E0B41F6FD4E3FCEB72A8A0214075505CCAECB18B7EF4C00F9C069FA3BC780" + + "14DE31623060020103801428A2CB1D204C2656A79C931EFAE351AB548248D030" + + "0906052B0E03021A0500300906072A8648CE380403042F302D021476DCB780CE" + + "D5B308A3630726A85DB97FBC50DFD1021500CDF2649B50500BB7428B9DCA6BEF" + + "2C7E7EF1B79C").HexToByteArray(); + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/tests/SignedCms/SignerInfoTests.cs b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/SignedCms/SignerInfoTests.cs new file mode 100644 index 0000000000..e97223c1f7 --- /dev/null +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/SignedCms/SignerInfoTests.cs @@ -0,0 +1,740 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Pkcs.Tests +{ + public static class SignerInfoTests + { + [Fact] + public static void SignerInfo_SignedAttributes_Cached_WhenEmpty() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber); + SignerInfo signer = cms.SignerInfos[0]; + + CryptographicAttributeObjectCollection attrs = signer.SignedAttributes; + CryptographicAttributeObjectCollection attrs2 = signer.SignedAttributes; + Assert.Same(attrs, attrs2); + Assert.Empty(attrs); + } + + [Fact] + public static void SignerInfo_SignedAttributes_Cached_WhenNonEmpty() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPssDocument); + SignerInfo signer = cms.SignerInfos[0]; + + CryptographicAttributeObjectCollection attrs = signer.SignedAttributes; + CryptographicAttributeObjectCollection attrs2 = signer.SignedAttributes; + Assert.Same(attrs, attrs2); + Assert.Equal(4, attrs.Count); + } + + [Fact] + public static void SignerInfo_UnsignedAttributes_Cached_WhenEmpty() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber); + SignerInfo signer = cms.SignerInfos[0]; + + CryptographicAttributeObjectCollection attrs = signer.UnsignedAttributes; + CryptographicAttributeObjectCollection attrs2 = signer.UnsignedAttributes; + Assert.Same(attrs, attrs2); + Assert.Empty(attrs); + Assert.Empty(attrs2); + } + + [Fact] + public static void SignerInfo_UnsignedAttributes_Cached_WhenNonEmpty() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.CounterSignedRsaPkcs1OneSigner); + SignerInfo signer = cms.SignerInfos[0]; + + CryptographicAttributeObjectCollection attrs = signer.UnsignedAttributes; + CryptographicAttributeObjectCollection attrs2 = signer.UnsignedAttributes; + Assert.Same(attrs, attrs2); + Assert.Single(attrs); + } + + [Fact] + public static void SignerInfo_CounterSignerInfos_UniquePerCall_WhenEmpty() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber); + SignerInfo signer = cms.SignerInfos[0]; + + SignerInfoCollection counterSigners = signer.CounterSignerInfos; + SignerInfoCollection counterSigners2 = signer.CounterSignerInfos; + Assert.NotSame(counterSigners, counterSigners2); + Assert.Empty(counterSigners); + Assert.Empty(counterSigners2); + } + + [Fact] + public static void SignerInfo_CounterSignerInfos_UniquePerCall_WhenNonEmpty() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.CounterSignedRsaPkcs1OneSigner); + SignerInfo signer = cms.SignerInfos[0]; + + SignerInfoCollection counterSigners = signer.CounterSignerInfos; + SignerInfoCollection counterSigners2 = signer.CounterSignerInfos; + Assert.NotSame(counterSigners, counterSigners2); + Assert.Single(counterSigners); + Assert.Single(counterSigners2); + + for (int i = 0; i < counterSigners.Count; i++) + { + SignerInfo counterSigner = counterSigners[i]; + SignerInfo counterSigner2 = counterSigners2[i]; + + Assert.NotSame(counterSigner, counterSigner2); + Assert.NotSame(counterSigner.Certificate, counterSigner2.Certificate); + Assert.Equal(counterSigner.Certificate, counterSigner2.Certificate); + +#if netcoreapp + byte[] signature = counterSigner.GetSignature(); + byte[] signature2 = counterSigner2.GetSignature(); + + Assert.NotSame(signature, signature2); + Assert.Equal(signature, signature2); +#endif + } + } + +#if netcoreapp + [Fact] + public static void SignerInfo_GetSignature_UniquePerCall() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.CounterSignedRsaPkcs1OneSigner); + SignerInfo signer = cms.SignerInfos[0]; + + byte[] signature = signer.GetSignature(); + byte[] signature2 = signer.GetSignature(); + + Assert.NotSame(signature, signature2); + Assert.Equal(signature, signature2); + } +#endif + + [Fact] + public static void SignerInfo_DigestAlgorithm_NotSame() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.CounterSignedRsaPkcs1OneSigner); + SignerInfo signer = cms.SignerInfos[0]; + + Oid oid = signer.DigestAlgorithm; + Oid oid2 = signer.DigestAlgorithm; + + Assert.NotSame(oid, oid2); + } + +#if netcoreapp + [Fact] + public static void SignerInfo_SignatureAlgorithm_NotSame() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.CounterSignedRsaPkcs1OneSigner); + SignerInfo signer = cms.SignerInfos[0]; + + Oid oid = signer.SignatureAlgorithm; + Oid oid2 = signer.SignatureAlgorithm; + + Assert.NotSame(oid, oid2); + } +#endif + + [Fact] + public static void SignerInfo_Certificate_Same() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.CounterSignedRsaPkcs1OneSigner); + SignerInfo signer = cms.SignerInfos[0]; + + X509Certificate2 cert = signer.Certificate; + X509Certificate2 cert2 = signer.Certificate; + + Assert.Same(cert, cert2); + } + + [Fact] + public static void CheckSignature_ThrowsOnNullStore() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPssDocument); + SignerInfo signer = cms.SignerInfos[0]; + + AssertExtensions.Throws( + "extraStore", + () => signer.CheckSignature(null, true)); + + AssertExtensions.Throws( + "extraStore", + () => signer.CheckSignature(null, false)); + } + + [Fact] + public static void CheckSignature_ExtraStore_IsAdditional() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber); + SignerInfo signer = cms.SignerInfos[0]; + Assert.NotNull(signer.Certificate); + + // Assert.NotThrows + signer.CheckSignature(true); + + // Assert.NotThrows + signer.CheckSignature(new X509Certificate2Collection(), true); + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "NetFx bug in matching logic")] + public static void RemoveCounterSignature_MatchesIssuerAndSerialNumber() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.OneRsaSignerTwoRsaCounterSigners); + SignerInfo signerInfo = cms.SignerInfos[0]; + SignerInfo counterSigner = signerInfo.CounterSignerInfos[1]; + + Assert.Equal( + SubjectIdentifierType.IssuerAndSerialNumber, + counterSigner.SignerIdentifier.Type); + + int countBefore = cms.Certificates.Count; + Assert.NotEqual(signerInfo.Certificate, counterSigner.Certificate); + + signerInfo.RemoveCounterSignature(counterSigner); + Assert.Single(cms.SignerInfos); + + // Removing a CounterSigner doesn't update the current object, it updates + // the underlying SignedCms object, and a new signer has to be retrieved. + Assert.Equal(2, signerInfo.CounterSignerInfos.Count); + Assert.Single(cms.SignerInfos[0].CounterSignerInfos); + + Assert.Equal(countBefore, cms.Certificates.Count); + + // Assert.NotThrows + cms.CheckSignature(true); + cms.CheckHash(); + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "NetFx bug in matching logic")] + public static void RemoveCounterSignature_MatchesSubjectKeyIdentifier() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.OneRsaSignerTwoRsaCounterSigners); + SignerInfo signerInfo = cms.SignerInfos[0]; + SignerInfo counterSigner = signerInfo.CounterSignerInfos[0]; + + Assert.Equal( + SubjectIdentifierType.SubjectKeyIdentifier, + counterSigner.SignerIdentifier.Type); + + int countBefore = cms.Certificates.Count; + Assert.Equal(signerInfo.Certificate, counterSigner.Certificate); + + signerInfo.RemoveCounterSignature(counterSigner); + Assert.Single(cms.SignerInfos); + + // Removing a CounterSigner doesn't update the current object, it updates + // the underlying SignedCms object, and a new signer has to be retrieved. + Assert.Equal(2, signerInfo.CounterSignerInfos.Count); + Assert.Single(cms.SignerInfos[0].CounterSignerInfos); + + // This certificate is still in use, since we counter-signed ourself, + // and the remaining countersigner is us. + Assert.Equal(countBefore, cms.Certificates.Count); + + // Assert.NotThrows + cms.CheckSignature(true); + cms.CheckHash(); + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "NetFx bug in matching logic")] + public static void RemoveCounterSignature_MatchesNoSignature() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPkcs1CounterSignedWithNoSignature); + SignerInfo signerInfo = cms.SignerInfos[0]; + SignerInfo counterSigner = signerInfo.CounterSignerInfos[0]; + + Assert.Single(signerInfo.CounterSignerInfos); + Assert.Equal(SubjectIdentifierType.NoSignature, counterSigner.SignerIdentifier.Type); + + int countBefore = cms.Certificates.Count; + + // cms.CheckSignature fails because there's a NoSignature countersigner: + Assert.Throws(() => cms.CheckSignature(true)); + + signerInfo.RemoveCounterSignature(counterSigner); + + // Removing a CounterSigner doesn't update the current object, it updates + // the underlying SignedCms object, and a new signer has to be retrieved. + Assert.Single(signerInfo.CounterSignerInfos); + Assert.Empty(cms.SignerInfos[0].CounterSignerInfos); + + // This certificate is still in use, since we counter-signed ourself, + // and the remaining countersigner is us. + Assert.Equal(countBefore, cms.Certificates.Count); + + // And we succeed now, because we got rid of the NoSignature signer. + cms.CheckSignature(true); + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "NetFx bug in matching logic")] + public static void RemoveCounterSignature_UsesLiveState() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.OneRsaSignerTwoRsaCounterSigners); + SignerInfo signerInfo = cms.SignerInfos[0]; + SignerInfo counterSigner = signerInfo.CounterSignerInfos[0]; + + Assert.Equal( + SubjectIdentifierType.SubjectKeyIdentifier, + counterSigner.SignerIdentifier.Type); + + int countBefore = cms.Certificates.Count; + Assert.Equal(signerInfo.Certificate, counterSigner.Certificate); + + signerInfo.RemoveCounterSignature(counterSigner); + Assert.Single(cms.SignerInfos); + + // Removing a CounterSigner doesn't update the current object, it updates + // the underlying SignedCms object, and a new signer has to be retrieved. + Assert.Equal(2, signerInfo.CounterSignerInfos.Count); + Assert.Single(cms.SignerInfos[0].CounterSignerInfos); + Assert.Equal(countBefore, cms.Certificates.Count); + + // Even though the CounterSignerInfos collection still contains this, the live + // document doesn't. + Assert.Throws( + () => signerInfo.RemoveCounterSignature(counterSigner)); + + // Assert.NotThrows + cms.CheckSignature(true); + cms.CheckHash(); + } + + [Fact] + public static void RemoveCounterSignature_WithNoMatch() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.OneRsaSignerTwoRsaCounterSigners); + SignerInfo signerInfo = cms.SignerInfos[0]; + + // Even though we counter-signed ourself, the counter-signer version of us + // is SubjectKeyIdentifier, and we're IssuerAndSerialNumber, so no match. + Assert.Throws( + () => signerInfo.RemoveCounterSignature(signerInfo)); + } + + [Fact] + public static void RemoveCounterSignature_Null() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.OneRsaSignerTwoRsaCounterSigners); + + Assert.Equal(2, cms.SignerInfos[0].CounterSignerInfos.Count); + + AssertExtensions.Throws( + "counterSignerInfo", + () => cms.SignerInfos[0].RemoveCounterSignature(null)); + + Assert.Equal(2, cms.SignerInfos[0].CounterSignerInfos.Count); + } + + [Fact] + public static void RemoveCounterSignature_Negative() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.OneRsaSignerTwoRsaCounterSigners); + SignerInfo signer = cms.SignerInfos[0]; + + ArgumentOutOfRangeException ex = AssertExtensions.Throws( + "childIndex", + () => signer.RemoveCounterSignature(-1)); + + Assert.Equal(null, ex.ActualValue); + } + + [Fact] + public static void RemoveCounterSignature_TooBigByValue() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.OneRsaSignerTwoRsaCounterSigners); + SignerInfo signer = cms.SignerInfos[0]; + + Assert.Throws( + () => signer.RemoveCounterSignature(2)); + + signer.RemoveCounterSignature(1); + Assert.Equal(2, signer.CounterSignerInfos.Count); + + Assert.Throws( + () => signer.RemoveCounterSignature(1)); + } + + [Fact] + public static void RemoveCounterSignature_TooBigByValue_Past0() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.OneRsaSignerTwoRsaCounterSigners); + SignerInfo signer = cms.SignerInfos[0]; + + signer.RemoveCounterSignature(0); + signer.RemoveCounterSignature(0); + Assert.Equal(2, signer.CounterSignerInfos.Count); + + Assert.Throws( + () => signer.RemoveCounterSignature(0)); + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "NetFx bug in matching logic")] + public static void RemoveCounterSignature_TooBigByMatch() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.OneRsaSignerTwoRsaCounterSigners); + SignerInfo signer = cms.SignerInfos[0]; + SignerInfo counterSigner = signer.CounterSignerInfos[1]; + + // This succeeeds, but reduces the real count to 1. + signer.RemoveCounterSignature(counterSigner); + Assert.Equal(2, signer.CounterSignerInfos.Count); + Assert.Single(cms.SignerInfos[0].CounterSignerInfos); + + Assert.Throws( + () => signer.RemoveCounterSignature(counterSigner)); + } + + [Fact] + public static void RemoveCounterSignature_BySignerInfo_OnRemovedSigner() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.OneRsaSignerTwoRsaCounterSigners); + SignerInfo signer = cms.SignerInfos[0]; + SignerInfo counterSigner = signer.CounterSignerInfos[0]; + + cms.RemoveSignature(signer); + Assert.NotEmpty(signer.CounterSignerInfos); + + Assert.Throws( + () => signer.RemoveCounterSignature(counterSigner)); + } + + [Fact] + public static void RemoveCounterSignature_ByIndex_OnRemovedSigner() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.OneRsaSignerTwoRsaCounterSigners); + SignerInfo signer = cms.SignerInfos[0]; + + cms.RemoveSignature(signer); + Assert.NotEmpty(signer.CounterSignerInfos); + + Assert.Throws( + () => signer.RemoveCounterSignature(0)); + } + + [Fact] + public static void AddCounterSigner_DuplicateCert_RSA() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber); + Assert.Single(cms.Certificates); + + SignerInfo firstSigner = cms.SignerInfos[0]; + Assert.Empty(firstSigner.CounterSignerInfos); + Assert.Empty(firstSigner.UnsignedAttributes); + + using (X509Certificate2 signerCert = Certificates.RSAKeyTransferCapi1.TryGetCertificateWithPrivateKey()) + { + CmsSigner signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, signerCert); + firstSigner.ComputeCounterSignature(signer); + } + + Assert.Empty(firstSigner.CounterSignerInfos); + Assert.Empty(firstSigner.UnsignedAttributes); + + SignerInfo firstSigner2 = cms.SignerInfos[0]; + Assert.Single(firstSigner2.CounterSignerInfos); + Assert.Single(firstSigner2.UnsignedAttributes); + + SignerInfo counterSigner = firstSigner2.CounterSignerInfos[0]; + + Assert.Equal(SubjectIdentifierType.IssuerAndSerialNumber, counterSigner.SignerIdentifier.Type); + + // On NetFx there will be two attributes, because Windows emits the + // content-type attribute even for counter-signers. + int expectedAttrCount = 1; + // One of them is a V3 signer. +#if netfx + expectedAttrCount = 2; +#endif + Assert.Equal(expectedAttrCount, counterSigner.SignedAttributes.Count); + Assert.Equal(Oids.MessageDigest, counterSigner.SignedAttributes[expectedAttrCount - 1].Oid.Value); + + Assert.Equal(firstSigner2.Certificate, counterSigner.Certificate); + Assert.Single(cms.Certificates); + + counterSigner.CheckSignature(true); + firstSigner2.CheckSignature(true); + cms.CheckSignature(true); + } + + [Theory] + [InlineData(SubjectIdentifierType.IssuerAndSerialNumber)] + [InlineData(SubjectIdentifierType.SubjectKeyIdentifier)] + public static void AddCounterSigner_RSA(SubjectIdentifierType identifierType) + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber); + Assert.Single(cms.Certificates); + + SignerInfo firstSigner = cms.SignerInfos[0]; + Assert.Empty(firstSigner.CounterSignerInfos); + Assert.Empty(firstSigner.UnsignedAttributes); + + using (X509Certificate2 signerCert = Certificates.RSA2048SignatureOnly.TryGetCertificateWithPrivateKey()) + { + CmsSigner signer = new CmsSigner(identifierType, signerCert); + firstSigner.ComputeCounterSignature(signer); + } + + Assert.Empty(firstSigner.CounterSignerInfos); + Assert.Empty(firstSigner.UnsignedAttributes); + + SignerInfo firstSigner2 = cms.SignerInfos[0]; + Assert.Single(firstSigner2.CounterSignerInfos); + Assert.Single(firstSigner2.UnsignedAttributes); + + SignerInfo counterSigner = firstSigner2.CounterSignerInfos[0]; + + Assert.Equal(identifierType, counterSigner.SignerIdentifier.Type); + + // On NetFx there will be two attributes, because Windows emits the + // content-type attribute even for counter-signers. + int expectedCount = 1; +#if netfx + expectedCount = 2; +#endif + Assert.Equal(expectedCount, counterSigner.SignedAttributes.Count); + Assert.Equal(Oids.MessageDigest, counterSigner.SignedAttributes[expectedCount - 1].Oid.Value); + + Assert.NotEqual(firstSigner2.Certificate, counterSigner.Certificate); + Assert.Equal(2, cms.Certificates.Count); + + counterSigner.CheckSignature(true); + firstSigner2.CheckSignature(true); + cms.CheckSignature(true); + } + + [Fact] + public static void AddCounterSigner_DSA() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber); + Assert.Single(cms.Certificates); + + SignerInfo firstSigner = cms.SignerInfos[0]; + Assert.Empty(firstSigner.CounterSignerInfos); + Assert.Empty(firstSigner.UnsignedAttributes); + + using (X509Certificate2 signerCert = Certificates.Dsa1024.TryGetCertificateWithPrivateKey()) + { + CmsSigner signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, signerCert); + signer.IncludeOption = X509IncludeOption.EndCertOnly; + // Best compatibility for DSA is SHA-1 (FIPS 186-2) + signer.DigestAlgorithm = new Oid(Oids.Sha1, Oids.Sha1); + firstSigner.ComputeCounterSignature(signer); + } + + Assert.Empty(firstSigner.CounterSignerInfos); + Assert.Empty(firstSigner.UnsignedAttributes); + + SignerInfo firstSigner2 = cms.SignerInfos[0]; + Assert.Single(firstSigner2.CounterSignerInfos); + Assert.Single(firstSigner2.UnsignedAttributes); + + Assert.Single(cms.SignerInfos); + Assert.Equal(2, cms.Certificates.Count); + + SignerInfo counterSigner = firstSigner2.CounterSignerInfos[0]; + + Assert.Equal(1, counterSigner.Version); + + // On NetFx there will be two attributes, because Windows emits the + // content-type attribute even for counter-signers. + int expectedCount = 1; +#if netfx + expectedCount = 2; +#endif + Assert.Equal(expectedCount, counterSigner.SignedAttributes.Count); + Assert.Equal(Oids.MessageDigest, counterSigner.SignedAttributes[expectedCount - 1].Oid.Value); + + Assert.NotEqual(firstSigner2.Certificate, counterSigner.Certificate); + Assert.Equal(2, cms.Certificates.Count); + +#if netcoreapp + byte[] signature = counterSigner.GetSignature(); + Assert.NotEmpty(signature); + // DSA PKIX signature format is a DER SEQUENCE. + Assert.Equal(0x30, signature[0]); +#endif + + cms.CheckSignature(true); + byte[] encoded = cms.Encode(); + cms.Decode(encoded); + cms.CheckSignature(true); + } + + [Theory] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + [InlineData(SubjectIdentifierType.IssuerAndSerialNumber, Oids.Sha1)] + [InlineData(SubjectIdentifierType.SubjectKeyIdentifier, Oids.Sha1)] + [InlineData(SubjectIdentifierType.IssuerAndSerialNumber, Oids.Sha256)] + [InlineData(SubjectIdentifierType.SubjectKeyIdentifier, Oids.Sha256)] + [InlineData(SubjectIdentifierType.IssuerAndSerialNumber, Oids.Sha384)] + [InlineData(SubjectIdentifierType.SubjectKeyIdentifier, Oids.Sha384)] + [InlineData(SubjectIdentifierType.IssuerAndSerialNumber, Oids.Sha512)] + [InlineData(SubjectIdentifierType.SubjectKeyIdentifier, Oids.Sha512)] + public static void AddCounterSigner_ECDSA(SubjectIdentifierType identifierType, string digestOid) + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber); + Assert.Single(cms.Certificates); + + SignerInfo firstSigner = cms.SignerInfos[0]; + Assert.Empty(firstSigner.CounterSignerInfos); + Assert.Empty(firstSigner.UnsignedAttributes); + + using (X509Certificate2 signerCert = Certificates.ECDsaP256Win.TryGetCertificateWithPrivateKey()) + { + CmsSigner signer = new CmsSigner(identifierType, signerCert); + signer.IncludeOption = X509IncludeOption.EndCertOnly; + signer.DigestAlgorithm = new Oid(digestOid, digestOid); + firstSigner.ComputeCounterSignature(signer); + } + + Assert.Empty(firstSigner.CounterSignerInfos); + Assert.Empty(firstSigner.UnsignedAttributes); + + SignerInfo firstSigner2 = cms.SignerInfos[0]; + Assert.Single(firstSigner2.CounterSignerInfos); + Assert.Single(firstSigner2.UnsignedAttributes); + + Assert.Single(cms.SignerInfos); + Assert.Equal(2, cms.Certificates.Count); + + SignerInfo counterSigner = firstSigner2.CounterSignerInfos[0]; + + int expectedVersion = identifierType == SubjectIdentifierType.IssuerAndSerialNumber ? 1 : 3; + Assert.Equal(expectedVersion, counterSigner.Version); + + // On NetFx there will be two attributes, because Windows emits the + // content-type attribute even for counter-signers. + int expectedCount = 1; +#if netfx + expectedCount = 2; +#endif + Assert.Equal(expectedCount, counterSigner.SignedAttributes.Count); + Assert.Equal(Oids.MessageDigest, counterSigner.SignedAttributes[expectedCount - 1].Oid.Value); + + Assert.NotEqual(firstSigner2.Certificate, counterSigner.Certificate); + Assert.Equal(2, cms.Certificates.Count); + +#if netcoreapp + byte[] signature = counterSigner.GetSignature(); + Assert.NotEmpty(signature); + // DSA PKIX signature format is a DER SEQUENCE. + Assert.Equal(0x30, signature[0]); + + // ECDSA Oids are all under 1.2.840.10045.4. + Assert.StartsWith("1.2.840.10045.4.", counterSigner.SignatureAlgorithm.Value); +#endif + + cms.CheckSignature(true); + byte[] encoded = cms.Encode(); + cms.Decode(encoded); + cms.CheckSignature(true); + } + + [Fact] + public static void EnsureExtraCertsAdded() + { + SignedCms cms = new SignedCms(); + cms.Decode(SignedDocuments.OneDsa1024); + + int preCount = cms.Certificates.Count; + + using (X509Certificate2 unrelated1 = Certificates.DHKeyAgree1.GetCertificate()) + using (X509Certificate2 unrelated1Copy = Certificates.DHKeyAgree1.GetCertificate()) + using (X509Certificate2 unrelated2 = Certificates.RSAKeyTransfer2.GetCertificate()) + using (X509Certificate2 unrelated3 = Certificates.RSAKeyTransfer3.GetCertificate()) + using (X509Certificate2 signerCert = Certificates.RSAKeyTransferCapi1.TryGetCertificateWithPrivateKey()) + { + var signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, signerCert); + signer.Certificates.Add(unrelated1); + signer.Certificates.Add(unrelated2); + signer.Certificates.Add(unrelated3); + signer.Certificates.Add(unrelated1Copy); + cms.SignerInfos[0].ComputeCounterSignature(signer); + + bool ExpectCopyRemoved = +#if netfx + false +#else + true +#endif + ; + + int expectedAddedCount = 4; + + if (!ExpectCopyRemoved) + { + expectedAddedCount++; + } + + // Since adding a counter-signer DER-normalizes the document the certificates + // get rewritten to be smallest cert first. + X509Certificate2Collection certs = cms.Certificates; + List certList = new List(certs.OfType()); + + int lastSize = -1; + + for (int i = 0; i < certList.Count; i++) + { + byte[] rawData = certList[i].RawData; + + Assert.True( + rawData.Length >= lastSize, + $"Certificate {i} has an encoded size ({rawData.Length}) no smaller than its predecessor ({lastSize})"); + } + + Assert.Contains(unrelated1, certList); + Assert.Contains(unrelated2, certList); + Assert.Contains(unrelated3, certList); + Assert.Contains(signerCert, certList); + + Assert.Equal(ExpectCopyRemoved ? 1 : 2, certList.Count(c => c.Equals(unrelated1))); + } + + cms.CheckSignature(true); + } + } +} diff --git a/external/corefx/src/System.Security.Cryptography.Pkcs/tests/System.Security.Cryptography.Pkcs.Tests.csproj b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/System.Security.Cryptography.Pkcs.Tests.csproj index 23bdacd0a8..b5edb59ea4 100644 --- a/external/corefx/src/System.Security.Cryptography.Pkcs/tests/System.Security.Cryptography.Pkcs.Tests.csproj +++ b/external/corefx/src/System.Security.Cryptography.Pkcs/tests/System.Security.Cryptography.Pkcs.Tests.csproj @@ -5,24 +5,35 @@ {2DD8DFFA-09FF-46C6-8313-4A9CC1849A44} true $(DefineConstants);netcoreapp + $(DefineConstants);netfx + + - - + + CommonTest\System\Security\Cryptography\ByteUtils.cs - - + + + + + + + + + + @@ -32,12 +43,9 @@ - - - - + - \ No newline at end of file + diff --git a/external/corefx/src/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs b/external/corefx/src/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs index a0df0ea2fd..73d5d1e6bb 100644 --- a/external/corefx/src/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs +++ b/external/corefx/src/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs @@ -17,10 +17,10 @@ namespace System.Security.Cryptography public virtual string KeyExchangeAlgorithm { get { throw null; } } public virtual int KeySize { get { throw null; } set { } } public virtual System.Security.Cryptography.KeySizes[] LegalKeySizes { get { throw null; } } - public static System.Security.Cryptography.AsymmetricAlgorithm Create() { throw null; } - public static System.Security.Cryptography.AsymmetricAlgorithm Create(string algName) { throw null; } public virtual string SignatureAlgorithm { get { throw null; } } public void Clear() { } + public static System.Security.Cryptography.AsymmetricAlgorithm Create() { throw null; } + public static System.Security.Cryptography.AsymmetricAlgorithm Create(string algName) { throw null; } public void Dispose() { } protected virtual void Dispose(bool disposing) { } public virtual void FromXmlString(string xmlString) { } @@ -51,11 +51,15 @@ namespace System.Security.Cryptography public override bool CanRead { get { throw null; } } public override bool CanSeek { get { throw null; } } public override bool CanWrite { get { throw null; } } - public void Clear() { } public bool HasFlushedFinalBlock { get { throw null; } } public override long Length { get { throw null; } } public override long Position { get { throw null; } set { } } + public override System.IAsyncResult BeginRead(byte[] buffer, int offset, int count, System.AsyncCallback callback, object state) { throw null; } + public override System.IAsyncResult BeginWrite(byte[] buffer, int offset, int count, System.AsyncCallback callback, object state) { throw null; } + public void Clear() { } protected override void Dispose(bool disposing) { } + public override int EndRead(System.IAsyncResult asyncResult) { throw null; } + public override void EndWrite(System.IAsyncResult asyncResult) { } public override void Flush() { } public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) { throw null; } public void FlushFinalBlock() { } @@ -75,8 +79,8 @@ namespace System.Security.Cryptography } public abstract partial class HashAlgorithm : System.IDisposable, System.Security.Cryptography.ICryptoTransform { - protected internal byte[] HashValue; protected int HashSizeValue; + protected internal byte[] HashValue; protected int State; protected HashAlgorithm() { } public virtual bool CanReuseTransform { get { throw null; } } @@ -94,17 +98,17 @@ namespace System.Security.Cryptography public void Dispose() { } protected virtual void Dispose(bool disposing) { } protected abstract void HashCore(byte[] array, int ibStart, int cbSize); - protected virtual void HashCore(ReadOnlySpan source) { throw null; } + protected virtual void HashCore(System.ReadOnlySpan source) { } protected abstract byte[] HashFinal(); public abstract void Initialize(); public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { throw null; } public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) { throw null; } - public bool TryComputeHash(ReadOnlySpan source, Span destination, out int bytesWritten) { throw null; } - protected virtual bool TryHashFinal(Span destination, out int bytesWritten) { throw null; } + public bool TryComputeHash(System.ReadOnlySpan source, System.Span destination, out int bytesWritten) { throw null; } + protected virtual bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] - public partial struct HashAlgorithmName : System.IEquatable + public readonly partial struct HashAlgorithmName : System.IEquatable { + private readonly object _dummy; public HashAlgorithmName(string name) { throw null; } public static System.Security.Cryptography.HashAlgorithmName MD5 { get { throw null; } } public string Name { get { throw null; } } @@ -122,15 +126,17 @@ namespace System.Security.Cryptography public abstract partial class HMAC : System.Security.Cryptography.KeyedHashAlgorithm { protected HMAC() { } - protected int BlockSizeValue { get { throw null; } set {} } + protected int BlockSizeValue { get { throw null; } set { } } public string HashName { get { throw null; } set { } } public override byte[] Key { get { throw null; } set { } } public static new System.Security.Cryptography.HMAC Create() { throw null; } public static new System.Security.Cryptography.HMAC Create(string algorithmName) { throw null; } protected override void Dispose(bool disposing) { } protected override void HashCore(byte[] rgb, int ib, int cb) { } + protected override void HashCore(System.ReadOnlySpan source) { } protected override byte[] HashFinal() { throw null; } public override void Initialize() { } + protected override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } public partial interface ICryptoTransform : System.IDisposable { @@ -159,16 +165,16 @@ namespace System.Security.Cryptography } public enum PaddingMode { + ANSIX923 = 4, + ISO10126 = 5, None = 1, PKCS7 = 2, Zeros = 3, - ANSIX923 = 4, - ISO10126 = 5, } public abstract partial class SymmetricAlgorithm : System.IDisposable { - protected int FeedbackSizeValue; protected int BlockSizeValue; + protected int FeedbackSizeValue; protected byte[] IVValue; protected int KeySizeValue; protected byte[] KeyValue; @@ -177,8 +183,8 @@ namespace System.Security.Cryptography protected System.Security.Cryptography.CipherMode ModeValue; protected System.Security.Cryptography.PaddingMode PaddingValue; protected SymmetricAlgorithm() { } - public virtual int FeedbackSize { get { throw null; } set { } } public virtual int BlockSize { get { throw null; } set { } } + public virtual int FeedbackSize { get { throw null; } set { } } public virtual byte[] IV { get { throw null; } set { } } public virtual byte[] Key { get { throw null; } set { } } public virtual int KeySize { get { throw null; } set { } } @@ -186,13 +192,13 @@ namespace System.Security.Cryptography public virtual System.Security.Cryptography.KeySizes[] LegalKeySizes { get { throw null; } } public virtual System.Security.Cryptography.CipherMode Mode { get { throw null; } set { } } public virtual System.Security.Cryptography.PaddingMode Padding { get { throw null; } set { } } + public void Clear() { } public static System.Security.Cryptography.SymmetricAlgorithm Create() { throw null; } public static System.Security.Cryptography.SymmetricAlgorithm Create(string algName) { throw null; } public virtual System.Security.Cryptography.ICryptoTransform CreateDecryptor() { throw null; } public abstract System.Security.Cryptography.ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] rgbIV); public virtual System.Security.Cryptography.ICryptoTransform CreateEncryptor() { throw null; } public abstract System.Security.Cryptography.ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] rgbIV); - public void Clear() { } public void Dispose() { } protected virtual void Dispose(bool disposing) { } public abstract void GenerateIV(); diff --git a/external/corefx/src/System.Security.Cryptography.Primitives/src/System.Security.Cryptography.Primitives.csproj b/external/corefx/src/System.Security.Cryptography.Primitives/src/System.Security.Cryptography.Primitives.csproj index 68cd0ce6fc..042b0a1d36 100644 --- a/external/corefx/src/System.Security.Cryptography.Primitives/src/System.Security.Cryptography.Primitives.csproj +++ b/external/corefx/src/System.Security.Cryptography.Primitives/src/System.Security.Cryptography.Primitives.csproj @@ -34,7 +34,6 @@ - diff --git a/external/corefx/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptoStream.cs b/external/corefx/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptoStream.cs index ac6b1ca2fc..c9f420d593 100644 --- a/external/corefx/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptoStream.cs +++ b/external/corefx/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptoStream.cs @@ -2,7 +2,6 @@ // 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.Diagnostics.Contracts; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -59,7 +58,6 @@ namespace System.Security.Cryptography public override bool CanRead { - [Pure] get { return _canRead; } } @@ -67,13 +65,11 @@ namespace System.Security.Cryptography // and get the state right. This is too strict. public override bool CanSeek { - [Pure] get { return false; } } public override bool CanWrite { - [Pure] get { return _canWrite; } } diff --git a/external/corefx/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptographicUnexpectedOperationException.cs b/external/corefx/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptographicUnexpectedOperationException.cs index 1713a7782d..0f12c78201 100644 --- a/external/corefx/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptographicUnexpectedOperationException.cs +++ b/external/corefx/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptographicUnexpectedOperationException.cs @@ -8,6 +8,9 @@ using System.Runtime.Serialization; namespace System.Security.Cryptography { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class CryptographicUnexpectedOperationException : CryptographicException { public CryptographicUnexpectedOperationException() @@ -33,7 +36,6 @@ namespace System.Security.Cryptography protected CryptographicUnexpectedOperationException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corefx/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithmName.cs b/external/corefx/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithmName.cs index 5ec20e1485..a1f1b01622 100644 --- a/external/corefx/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithmName.cs +++ b/external/corefx/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithmName.cs @@ -20,7 +20,7 @@ namespace System.Security.Cryptography /// * Must recognize at least "MD5", "SHA1", "SHA256", "SHA384", and "SHA512". /// * Should recognize additional CNG IDs for any other hash algorithms that they also support. /// - public struct HashAlgorithmName : IEquatable + public readonly struct HashAlgorithmName : IEquatable { // Returning a new instance every time is free here since HashAlgorithmName is a struct with // a single string field. The optimized codegen should be equivalent to return "MD5". diff --git a/external/corefx/src/System.Security.Cryptography.ProtectedData/pkg/System.Security.Cryptography.ProtectedData.pkgproj b/external/corefx/src/System.Security.Cryptography.ProtectedData/pkg/System.Security.Cryptography.ProtectedData.pkgproj index 51e87f46ab..7068e9d0d7 100644 --- a/external/corefx/src/System.Security.Cryptography.ProtectedData/pkg/System.Security.Cryptography.ProtectedData.pkgproj +++ b/external/corefx/src/System.Security.Cryptography.ProtectedData/pkg/System.Security.Cryptography.ProtectedData.pkgproj @@ -10,12 +10,7 @@ - - - - - - + \ No newline at end of file diff --git a/external/corefx/src/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs b/external/corefx/src/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs index dd9eb997e1..fb501f6b7a 100644 --- a/external/corefx/src/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs +++ b/external/corefx/src/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs @@ -9,13 +9,11 @@ namespace Microsoft.Win32.SafeHandles { public sealed partial class SafeX509ChainHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid { - internal SafeX509ChainHandle() : base(default(bool)) { } - public override bool IsInvalid { get { throw null; } } + internal SafeX509ChainHandle() : base (default(bool)) { } protected override void Dispose(bool disposing) { } protected override bool ReleaseHandle() { throw null; } } } - namespace System.Security.Cryptography.X509Certificates { public sealed partial class CertificateRequest @@ -131,7 +129,7 @@ namespace System.Security.Cryptography.X509Certificates public int PathLengthConstraint { get { throw null; } } public override void CopyFrom(System.Security.Cryptography.AsnEncodedData asnEncodedData) { } } - public partial class X509Certificate : System.IDisposable, System.Runtime.Serialization.ISerializable, System.Runtime.Serialization.IDeserializationCallback + public partial class X509Certificate : System.IDisposable, System.Runtime.Serialization.IDeserializationCallback, System.Runtime.Serialization.ISerializable { public X509Certificate() { } public X509Certificate(byte[] data) { } @@ -166,7 +164,9 @@ namespace System.Security.Cryptography.X509Certificates public virtual byte[] Export(System.Security.Cryptography.X509Certificates.X509ContentType contentType, string password) { throw null; } protected static string FormatDate(System.DateTime date) { throw null; } public virtual byte[] GetCertHash() { throw null; } + public virtual byte[] GetCertHash(System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; } public virtual string GetCertHashString() { throw null; } + public virtual string GetCertHashString(System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; } public virtual string GetEffectiveDateString() { throw null; } public virtual string GetExpirationDateString() { throw null; } public virtual string GetFormat() { throw null; } @@ -197,6 +197,7 @@ namespace System.Security.Cryptography.X509Certificates void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public override string ToString() { throw null; } public virtual string ToString(bool fVerbose) { throw null; } + public virtual bool TryGetCertHash(System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Span destination, out int bytesWritten) { throw null; } } public partial class X509Certificate2 : System.Security.Cryptography.X509Certificates.X509Certificate { @@ -253,12 +254,12 @@ namespace System.Security.Cryptography.X509Certificates { public X509Certificate2Collection() { } public X509Certificate2Collection(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { } - public X509Certificate2Collection(System.Security.Cryptography.X509Certificates.X509Certificate2[] certificates) { } public X509Certificate2Collection(System.Security.Cryptography.X509Certificates.X509Certificate2Collection certificates) { } + public X509Certificate2Collection(System.Security.Cryptography.X509Certificates.X509Certificate2[] certificates) { } public new System.Security.Cryptography.X509Certificates.X509Certificate2 this[int index] { get { throw null; } set { } } public int Add(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { throw null; } - public void AddRange(System.Security.Cryptography.X509Certificates.X509Certificate2[] certificates) { } public void AddRange(System.Security.Cryptography.X509Certificates.X509Certificate2Collection certificates) { } + public void AddRange(System.Security.Cryptography.X509Certificates.X509Certificate2[] certificates) { } public bool Contains(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { throw null; } public byte[] Export(System.Security.Cryptography.X509Certificates.X509ContentType contentType) { throw null; } public byte[] Export(System.Security.Cryptography.X509Certificates.X509ContentType contentType, string password) { throw null; } @@ -270,8 +271,8 @@ namespace System.Security.Cryptography.X509Certificates public void Import(string fileName, string password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } public void Insert(int index, System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { } public void Remove(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { } - public void RemoveRange(System.Security.Cryptography.X509Certificates.X509Certificate2[] certificates) { } public void RemoveRange(System.Security.Cryptography.X509Certificates.X509Certificate2Collection certificates) { } + public void RemoveRange(System.Security.Cryptography.X509Certificates.X509Certificate2[] certificates) { } } public sealed partial class X509Certificate2Enumerator : System.Collections.IEnumerator { @@ -286,18 +287,19 @@ namespace System.Security.Cryptography.X509Certificates public partial class X509CertificateCollection : System.Collections.CollectionBase { public X509CertificateCollection() { } - public X509CertificateCollection(System.Security.Cryptography.X509Certificates.X509Certificate[] value) { } public X509CertificateCollection(System.Security.Cryptography.X509Certificates.X509CertificateCollection value) { } + public X509CertificateCollection(System.Security.Cryptography.X509Certificates.X509Certificate[] value) { } public System.Security.Cryptography.X509Certificates.X509Certificate this[int index] { get { throw null; } set { } } public int Add(System.Security.Cryptography.X509Certificates.X509Certificate value) { throw null; } - public void AddRange(System.Security.Cryptography.X509Certificates.X509Certificate[] value) { } public void AddRange(System.Security.Cryptography.X509Certificates.X509CertificateCollection value) { } + public void AddRange(System.Security.Cryptography.X509Certificates.X509Certificate[] value) { } public bool Contains(System.Security.Cryptography.X509Certificates.X509Certificate value) { throw null; } public void CopyTo(System.Security.Cryptography.X509Certificates.X509Certificate[] array, int index) { } public new System.Security.Cryptography.X509Certificates.X509CertificateCollection.X509CertificateEnumerator GetEnumerator() { throw null; } public override int GetHashCode() { throw null; } public int IndexOf(System.Security.Cryptography.X509Certificates.X509Certificate value) { throw null; } public void Insert(int index, System.Security.Cryptography.X509Certificates.X509Certificate value) { } + protected override void OnValidate(object value) { } public void Remove(System.Security.Cryptography.X509Certificates.X509Certificate value) { } public partial class X509CertificateEnumerator : System.Collections.IEnumerator { @@ -315,13 +317,13 @@ namespace System.Security.Cryptography.X509Certificates public X509Chain() { } public X509Chain(bool useMachineContext) { } public X509Chain(System.IntPtr chainContext) { } - public static X509Chain Create() { throw null; } public System.IntPtr ChainContext { get { throw null; } } public System.Security.Cryptography.X509Certificates.X509ChainElementCollection ChainElements { get { throw null; } } public System.Security.Cryptography.X509Certificates.X509ChainPolicy ChainPolicy { get { throw null; } set { } } public System.Security.Cryptography.X509Certificates.X509ChainStatus[] ChainStatus { get { throw null; } } public Microsoft.Win32.SafeHandles.SafeX509ChainHandle SafeHandle { get { throw null; } } public bool Build(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Chain Create() { throw null; } public void Dispose() { } protected virtual void Dispose(bool disposing) { } public void Reset() { } @@ -366,9 +368,9 @@ namespace System.Security.Cryptography.X509Certificates public System.DateTime VerificationTime { get { throw null; } set { } } public void Reset() { } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct X509ChainStatus { + private object _dummy; public System.Security.Cryptography.X509Certificates.X509ChainStatusFlags Status { get { throw null; } set { } } public string StatusInformation { get { throw null; } set { } } } diff --git a/external/corefx/src/System.Security.Cryptography.X509Certificates/src/ApiCompatBaseline.uap.txt b/external/corefx/src/System.Security.Cryptography.X509Certificates/src/ApiCompatBaseline.uap.txt deleted file mode 100644 index fcc74cf864..0000000000 --- a/external/corefx/src/System.Security.Cryptography.X509Certificates/src/ApiCompatBaseline.uap.txt +++ /dev/null @@ -1 +0,0 @@ -Total Issues: 0 diff --git a/external/corefx/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/ChainPal.cs b/external/corefx/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/ChainPal.cs index 175dfa8b8f..4c429ff1ac 100644 --- a/external/corefx/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/ChainPal.cs +++ b/external/corefx/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/ChainPal.cs @@ -459,7 +459,7 @@ namespace Internal.Cryptography.Pal return new X509ChainElement(cert, statuses.ToArray(), ""); } - private struct X509ChainErrorMapping + private readonly struct X509ChainErrorMapping { internal static readonly X509ChainErrorMapping[] s_chainErrorMappings = { diff --git a/external/corefx/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ExportProvider.cs b/external/corefx/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ExportProvider.cs index 06ee32ebfe..a7b2b2c95d 100644 --- a/external/corefx/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ExportProvider.cs +++ b/external/corefx/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ExportProvider.cs @@ -117,13 +117,15 @@ namespace Internal.Cryptography.Pal } privateCert = cert; + var certPal = (OpenSslX509CertificateReader)cert.Pal; + privateCertHandle = certPal.SafeHandle; + privateCertKeyHandle = certPal.PrivateKeyHandle; } else { PushHandle(cert.Handle, publicCerts); } - GC.KeepAlive(cert); // ensure cert's safe handle isn't finalized while raw handle is in use } } @@ -138,11 +140,17 @@ namespace Internal.Cryptography.Pal throw Interop.Crypto.CreateOpenSslCryptographicException(); } - return Interop.Crypto.OpenSslEncode( + byte[] result = Interop.Crypto.OpenSslEncode( Interop.Crypto.GetPkcs12DerSize, Interop.Crypto.EncodePkcs12, pkcs12); + + // ensure cert handles aren't finalized while the raw handles are in use + GC.KeepAlive(_certs); + return result; } + + } } diff --git a/external/corefx/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/ChainPal.GetChainStatusInformation.cs b/external/corefx/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/ChainPal.GetChainStatusInformation.cs index 7abde9e490..18de38235c 100644 --- a/external/corefx/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/ChainPal.GetChainStatusInformation.cs +++ b/external/corefx/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/ChainPal.GetChainStatusInformation.cs @@ -77,7 +77,7 @@ namespace Internal.Cryptography.Pal return SR.Unknown_Error; } - private struct X509ChainErrorMapping + private readonly struct X509ChainErrorMapping { public readonly CertTrustErrorStatus Win32Flag; public readonly int Win32ErrorCode; diff --git a/external/corefx/src/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj b/external/corefx/src/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj index c235728e73..a63a66ff53 100644 --- a/external/corefx/src/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj +++ b/external/corefx/src/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj @@ -412,6 +412,7 @@ + diff --git a/external/corefx/src/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate.cs b/external/corefx/src/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate.cs index 4099dde040..1cc9c1b75b 100644 --- a/external/corefx/src/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate.cs +++ b/external/corefx/src/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate.cs @@ -317,12 +317,44 @@ namespace System.Security.Cryptography.X509Certificates return GetRawCertHash().CloneByteArray(); } + public virtual byte[] GetCertHash(HashAlgorithmName hashAlgorithm) + { + ThrowIfInvalid(); + + using (IncrementalHash hasher = IncrementalHash.CreateHash(hashAlgorithm)) + { + hasher.AppendData(Pal.RawData); + return hasher.GetHashAndReset(); + } + } + + public virtual bool TryGetCertHash( + HashAlgorithmName hashAlgorithm, + Span destination, + out int bytesWritten) + { + ThrowIfInvalid(); + + using (IncrementalHash hasher = IncrementalHash.CreateHash(hashAlgorithm)) + { + hasher.AppendData(Pal.RawData); + return hasher.TryGetHashAndReset(destination, out bytesWritten); + } + } + public virtual string GetCertHashString() { ThrowIfInvalid(); return GetRawCertHash().ToHexStringUpper(); } + public virtual string GetCertHashString(HashAlgorithmName hashAlgorithm) + { + ThrowIfInvalid(); + + return GetCertHash(hashAlgorithm).ToHexStringUpper(); + } + // Only use for internal purposes when the returned byte[] will not be mutated private byte[] GetRawCertHash() { diff --git a/external/corefx/src/System.Security.Cryptography.X509Certificates/tests/CertTests.cs b/external/corefx/src/System.Security.Cryptography.X509Certificates/tests/CertTests.cs index 46ae557017..559c576c5a 100644 --- a/external/corefx/src/System.Security.Cryptography.X509Certificates/tests/CertTests.cs +++ b/external/corefx/src/System.Security.Cryptography.X509Certificates/tests/CertTests.cs @@ -289,6 +289,9 @@ namespace System.Security.Cryptography.X509Certificates.Tests // State held on X509Certificate Assert.ThrowsAny(() => c.GetCertHash()); + Assert.ThrowsAny(() => c.GetCertHash(HashAlgorithmName.SHA256)); + Assert.ThrowsAny(() => c.GetCertHashString()); + Assert.ThrowsAny(() => c.GetCertHashString(HashAlgorithmName.SHA256)); Assert.ThrowsAny(() => c.GetKeyAlgorithm()); Assert.ThrowsAny(() => c.GetKeyAlgorithmParameters()); Assert.ThrowsAny(() => c.GetKeyAlgorithmParametersString()); @@ -299,6 +302,9 @@ namespace System.Security.Cryptography.X509Certificates.Tests Assert.ThrowsAny(() => c.NotBefore); Assert.ThrowsAny(() => c.NotAfter); + Assert.ThrowsAny( + () => c.TryGetCertHash(HashAlgorithmName.SHA256, Array.Empty(), out _)); + // State held on X509Certificate2 Assert.ThrowsAny(() => c.RawData); Assert.ThrowsAny(() => c.SignatureAlgorithm); diff --git a/external/corefx/src/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs b/external/corefx/src/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs index 7530010331..0891532760 100644 --- a/external/corefx/src/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs +++ b/external/corefx/src/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs @@ -863,6 +863,33 @@ namespace System.Security.Cryptography.X509Certificates.Tests } } + [Fact] + [ActiveIssue(26397, TestPlatforms.OSX)] + public static void CanAddMultipleCertsWithSinglePrivateKey() + { + using (var oneWithKey = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.Exportable | Cert.EphemeralIfPossible)) + using (var twoWithoutKey = new X509Certificate2(TestData.ComplexNameInfoCert)) + { + Assert.True(oneWithKey.HasPrivateKey); + + var col = new X509Certificate2Collection + { + oneWithKey, + twoWithoutKey, + }; + + Assert.Equal(1, col.Cast().Count(x => x.HasPrivateKey)); + Assert.Equal(2, col.Count); + + byte[] buffer = col.Export(X509ContentType.Pfx); + + using (ImportedCollection newCollection = Cert.Import(buffer)) + { + Assert.Equal(2, newCollection.Collection.Count); + } + } + } + [Fact] public static void X509CertificateCollectionCopyTo() { diff --git a/external/corefx/src/System.Security.Cryptography.X509Certificates/tests/CtorTests.cs b/external/corefx/src/System.Security.Cryptography.X509Certificates/tests/CtorTests.cs index c35c454d1d..04abb8df92 100644 --- a/external/corefx/src/System.Security.Cryptography.X509Certificates/tests/CtorTests.cs +++ b/external/corefx/src/System.Security.Cryptography.X509Certificates/tests/CtorTests.cs @@ -25,6 +25,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests object ignored; Assert.Equal(IntPtr.Zero, h); Assert.ThrowsAny(() => c.GetCertHash()); + Assert.ThrowsAny(() => c.GetCertHash(HashAlgorithmName.SHA256)); Assert.ThrowsAny(() => c.GetKeyAlgorithm()); Assert.ThrowsAny(() => c.GetKeyAlgorithmParameters()); Assert.ThrowsAny(() => c.GetKeyAlgorithmParametersString()); @@ -43,6 +44,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests Assert.ThrowsAny(() => ignored = c.SubjectName); Assert.ThrowsAny(() => ignored = c.IssuerName); Assert.ThrowsAny(() => c.GetCertHashString()); + Assert.ThrowsAny(() => c.GetCertHashString(HashAlgorithmName.SHA256)); Assert.ThrowsAny(() => c.GetEffectiveDateString()); Assert.ThrowsAny(() => c.GetExpirationDateString()); Assert.ThrowsAny(() => c.GetPublicKeyString()); @@ -53,12 +55,15 @@ namespace System.Security.Cryptography.X509Certificates.Tests Assert.ThrowsAny(() => c.GetIssuerName()); Assert.ThrowsAny(() => c.GetName()); #pragma warning restore 0618 + + Assert.ThrowsAny( + () => c.TryGetCertHash(HashAlgorithmName.SHA256, Array.Empty(), out _)); } [Fact] public static void TestConstructor_DER() { - byte[] expectedThumbPrint = new byte[] + byte[] expectedThumbPrintSha1 = { 0x10, 0x8e, 0x2b, 0xa2, 0x36, 0x32, 0x62, 0x0c, 0x42, 0x7c, 0x57, 0x0b, 0x6d, 0x9d, 0xb5, 0x1a, @@ -70,7 +75,10 @@ namespace System.Security.Cryptography.X509Certificates.Tests IntPtr h = c.Handle; Assert.NotEqual(IntPtr.Zero, h); byte[] actualThumbprint = c.GetCertHash(); - Assert.Equal(expectedThumbPrint, actualThumbprint); + Assert.Equal(expectedThumbPrintSha1, actualThumbprint); + + byte[] specifiedAlgThumbprint = c.GetCertHash(HashAlgorithmName.SHA1); + Assert.Equal(expectedThumbPrintSha1, specifiedAlgThumbprint); }; using (X509Certificate2 c = new X509Certificate2(TestData.MsCertificate)) @@ -86,7 +94,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests [Fact] public static void TestConstructor_PEM() { - byte[] expectedThumbPrint = + byte[] expectedThumbPrintSha1 = { 0x10, 0x8e, 0x2b, 0xa2, 0x36, 0x32, 0x62, 0x0c, 0x42, 0x7c, 0x57, 0x0b, 0x6d, 0x9d, 0xb5, 0x1a, @@ -98,7 +106,10 @@ namespace System.Security.Cryptography.X509Certificates.Tests IntPtr h = cert.Handle; Assert.NotEqual(IntPtr.Zero, h); byte[] actualThumbprint = cert.GetCertHash(); - Assert.Equal(expectedThumbPrint, actualThumbprint); + Assert.Equal(expectedThumbPrintSha1, actualThumbprint); + + byte[] specifiedAlgThumbprint = cert.GetCertHash(HashAlgorithmName.SHA1); + Assert.Equal(expectedThumbPrintSha1, specifiedAlgThumbprint); }; using (X509Certificate2 c = new X509Certificate2(TestData.MsCertificatePemBytes)) @@ -168,6 +179,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests using (var c2 = new X509Certificate2(c1)) { Assert.Equal(c1.GetCertHash(), c2.GetCertHash()); + Assert.Equal(c1.GetCertHash(HashAlgorithmName.SHA256), c2.GetCertHash(HashAlgorithmName.SHA256)); Assert.Equal(c1.GetKeyAlgorithm(), c2.GetKeyAlgorithm()); Assert.Equal(c1.GetKeyAlgorithmParameters(), c2.GetKeyAlgorithmParameters()); Assert.Equal(c1.GetKeyAlgorithmParametersString(), c2.GetKeyAlgorithmParametersString()); @@ -184,6 +196,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests Assert.Equal(c1.SubjectName.Name, c2.SubjectName.Name); Assert.Equal(c1.IssuerName.Name, c2.IssuerName.Name); Assert.Equal(c1.GetCertHashString(), c2.GetCertHashString()); + Assert.Equal(c1.GetCertHashString(HashAlgorithmName.SHA256), c2.GetCertHashString(HashAlgorithmName.SHA256)); Assert.Equal(c1.GetEffectiveDateString(), c2.GetEffectiveDateString()); Assert.Equal(c1.GetExpirationDateString(), c2.GetExpirationDateString()); Assert.Equal(c1.GetPublicKeyString(), c2.GetPublicKeyString()); diff --git a/external/corefx/src/System.Security.Cryptography.X509Certificates/tests/LoadFromFileTests.cs b/external/corefx/src/System.Security.Cryptography.X509Certificates/tests/LoadFromFileTests.cs index ad94bdfa85..68ec3359b8 100644 --- a/external/corefx/src/System.Security.Cryptography.X509Certificates/tests/LoadFromFileTests.cs +++ b/external/corefx/src/System.Security.Cryptography.X509Certificates/tests/LoadFromFileTests.cs @@ -66,6 +66,89 @@ namespace System.Security.Cryptography.X509Certificates.Tests } } + [Theory] + [InlineData("SHA1", false)] + [InlineData("SHA1", true)] + [InlineData("SHA256", false)] + [InlineData("SHA256", true)] + [InlineData("SHA384", false)] + [InlineData("SHA384", true)] + [InlineData("SHA512", false)] + [InlineData("SHA512", true)] + public static void TestThumbprint(string hashAlgName, bool viaSpan) + { + string expectedThumbprintHex; + + switch (hashAlgName) + { + case "SHA1": + expectedThumbprintHex = + "108E2BA23632620C427C570B6D9DB51AC31387FE"; + break; + case "SHA256": + expectedThumbprintHex = + "73FCF982974387FB164C91D0168FE8C3B957DE6526AE239AAD32825C5A63D2A4"; + break; + case "SHA384": + expectedThumbprintHex = + "E6DCEF0840DAB43E1DBE9BE23142182BD05106AB25F7043BDE6A551928DFB4C7082791B86A5FB5E77B0F43DD92B7A3E5"; + break; + case "SHA512": + expectedThumbprintHex = + "8435635A12915A1A9C28BC2BCE7C3CAD08EB723FE276F13CD37D1C3B21416994" + + "0661A27B419882DBA643B23A557CA9EBC03ACC3D7EE3D4D591AB4BA0E553B945"; + break; + default: + throw new ArgumentOutOfRangeException(nameof(hashAlgName)); + } + + HashAlgorithmName alg = new HashAlgorithmName(hashAlgName); + + using (X509Certificate2 c = LoadCertificateFromFile()) + { + if (viaSpan) + { + const int WriteOffset = 3; + const byte FillByte = 0x55; + + int expectedSize = expectedThumbprintHex.Length / 2; + byte[] thumbPrint = new byte[expectedSize + 10]; + thumbPrint.AsSpan().Fill(FillByte); + + Span writeDest = thumbPrint.AsSpan().Slice(WriteOffset); + int bytesWritten; + + // Too small. + Assert.False(c.TryGetCertHash(alg, writeDest.Slice(0, expectedSize - 1), out bytesWritten)); + Assert.Equal(0, bytesWritten); + // Still all 0x55s. + Assert.Equal(new string('5', thumbPrint.Length * 2), thumbPrint.ByteArrayToHex()); + + // Large enough (+7) + Assert.True(c.TryGetCertHash(alg, writeDest, out bytesWritten)); + Assert.Equal(expectedSize, bytesWritten); + + Assert.Equal(expectedThumbprintHex, writeDest.Slice(0, bytesWritten).ByteArrayToHex()); + Assert.Equal(FillByte, thumbPrint[expectedSize + WriteOffset]); + + // Try again with a perfectly sized value + thumbPrint.AsSpan().Fill(FillByte); + Assert.True(c.TryGetCertHash(alg, writeDest.Slice(0, expectedSize), out bytesWritten)); + Assert.Equal(expectedSize, bytesWritten); + + Assert.Equal(expectedThumbprintHex, writeDest.Slice(0, bytesWritten).ByteArrayToHex()); + Assert.Equal(FillByte, thumbPrint[expectedSize + WriteOffset]); + } + else + { + byte[] thumbPrint = c.GetCertHash(alg); + Assert.Equal(expectedThumbprintHex, thumbPrint.ByteArrayToHex()); + string thumbPrintHex = c.GetCertHashString(alg); + Assert.Equal(expectedThumbprintHex, thumbPrintHex); + } + } + } + [Fact] public static void TestGetFormat() { diff --git a/external/corefx/src/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj b/external/corefx/src/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj index e550ad4504..9c209605c3 100644 --- a/external/corefx/src/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj +++ b/external/corefx/src/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj @@ -94,4 +94,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CryptoSignedXmlRecursionException.cs b/external/corefx/src/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CryptoSignedXmlRecursionException.cs index fd3a4bc7f1..ef901e867c 100644 --- a/external/corefx/src/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CryptoSignedXmlRecursionException.cs +++ b/external/corefx/src/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CryptoSignedXmlRecursionException.cs @@ -2,7 +2,7 @@ // 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.Runtime.Serialization; using System.Xml; namespace System.Security.Cryptography.Xml @@ -14,17 +14,14 @@ namespace System.Security.Cryptography.Xml /// This unique exception helps catch the recursion limit issue. /// [Serializable] - internal class CryptoSignedXmlRecursionException : XmlException +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Security, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] +#endif + public class CryptoSignedXmlRecursionException : XmlException { public CryptoSignedXmlRecursionException() : base() { } public CryptoSignedXmlRecursionException(string message) : base(message) { } - public CryptoSignedXmlRecursionException(string message, System.Exception inner) : base(message, inner) { } - // A constructor is needed for serialization when an - // exception propagates from a remoting server to the client. - protected CryptoSignedXmlRecursionException(System.Runtime.Serialization.SerializationInfo info, - System.Runtime.Serialization.StreamingContext context) - { - throw new PlatformNotSupportedException(); - } + public CryptoSignedXmlRecursionException(string message, Exception inner) : base(message, inner) { } + protected CryptoSignedXmlRecursionException(SerializationInfo info, StreamingContext context) : base(info, context) { } } } diff --git a/external/corefx/src/System.Security.Cryptography.Xml/tests/Samples/SigningVerifyingX509Cert.cs b/external/corefx/src/System.Security.Cryptography.Xml/tests/Samples/SigningVerifyingX509Cert.cs index 79044a0890..66187ac17c 100644 --- a/external/corefx/src/System.Security.Cryptography.Xml/tests/Samples/SigningVerifyingX509Cert.cs +++ b/external/corefx/src/System.Security.Cryptography.Xml/tests/Samples/SigningVerifyingX509Cert.cs @@ -20,7 +20,7 @@ namespace System.Security.Cryptography.Xml.Tests some text node "; - private static bool SupportsNewRsaTypes => !PlatformDetection.IsFullFramework || PlatformDetection.IsNetfx462OrNewer(); + private static bool SupportsNewRsaTypes => !PlatformDetection.IsFullFramework || PlatformDetection.IsNetfx462OrNewer; private static void SignXml(XmlDocument doc, AsymmetricAlgorithm key) { diff --git a/external/corefx/src/System.Security.Permissions/ref/System.Security.Permissions.cs.REMOVED.git-id b/external/corefx/src/System.Security.Permissions/ref/System.Security.Permissions.cs.REMOVED.git-id index 30b056b6a2..1f4e59ade2 100644 --- a/external/corefx/src/System.Security.Permissions/ref/System.Security.Permissions.cs.REMOVED.git-id +++ b/external/corefx/src/System.Security.Permissions/ref/System.Security.Permissions.cs.REMOVED.git-id @@ -1 +1 @@ -f9464b4f2feb4f3dc8b6bc4083f3876368f419a3 \ No newline at end of file +66c2dfdaf4d5ee369e42aa928da2ec16b84518f8 \ No newline at end of file diff --git a/external/corefx/src/System.Security.Permissions/src/Resources/Strings.resx b/external/corefx/src/System.Security.Permissions/src/Resources/Strings.resx index 2f0e3e3b67..fa01deef12 100644 --- a/external/corefx/src/System.Security.Permissions/src/Resources/Strings.resx +++ b/external/corefx/src/System.Security.Permissions/src/Resources/Strings.resx @@ -70,10 +70,16 @@ Operation on type '{0}' attempted with target of incorrect type. + + The protected resources (only available with full trust) were: + + + The demanded resources were: + Request for principal permission failed. Code Access Security is not supported on this platform. - \ No newline at end of file + diff --git a/external/corefx/src/System.Security.Permissions/src/System/Security/HostProtectionException.cs b/external/corefx/src/System.Security.Permissions/src/System/Security/HostProtectionException.cs index 9551f59611..f2e3ade20e 100644 --- a/external/corefx/src/System.Security.Permissions/src/System/Security/HostProtectionException.cs +++ b/external/corefx/src/System.Security.Permissions/src/System/Security/HostProtectionException.cs @@ -4,23 +4,84 @@ using System.Runtime.Serialization; using System.Security.Permissions; +using System.Text; namespace System.Security { [Serializable] - public partial class HostProtectionException : System.SystemException +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif + public partial class HostProtectionException : SystemException { - public HostProtectionException() { } - protected HostProtectionException(SerializationInfo info, StreamingContext context) + private const string ProtectedResourcesName = "ProtectedResources"; + private const string DemandedResourcesName = "DemandedResources"; + private const int E_HostProtection = -2146232768; + + public HostProtectionException() : base() { - throw new PlatformNotSupportedException(); + ProtectedResources = HostProtectionResource.None; + DemandedResources = HostProtectionResource.None; } - public HostProtectionException(string message) { } - public HostProtectionException(string message, Exception e) { } - public HostProtectionException(string message, HostProtectionResource protectedResources, HostProtectionResource demandedResources) { } - public HostProtectionResource DemandedResources { get { return default(HostProtectionResource); } } - public HostProtectionResource ProtectedResources { get { return default(HostProtectionResource); } } - public override string ToString() => base.ToString(); + public HostProtectionException(string message) : base(message) + { + ProtectedResources = HostProtectionResource.None; + DemandedResources = HostProtectionResource.None; + } + + public HostProtectionException(string message, Exception e) : base(message, e) + { + ProtectedResources = HostProtectionResource.None; + DemandedResources = HostProtectionResource.None; + } + + public HostProtectionException(string message, HostProtectionResource protectedResources, HostProtectionResource demandedResources) + : base(message) + { + HResult = E_HostProtection; + ProtectedResources = protectedResources; + DemandedResources = demandedResources; + } + + protected HostProtectionException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + ProtectedResources = (HostProtectionResource)info.GetValue(ProtectedResourcesName, typeof(HostProtectionResource)); + DemandedResources = (HostProtectionResource)info.GetValue(DemandedResourcesName, typeof(HostProtectionResource)); + } + + public HostProtectionResource DemandedResources { get; } = default; + + public HostProtectionResource ProtectedResources { get; } = default; + + private void AppendResourceString(string resourceString, object attr, StringBuilder sb) + { + if (attr == null) + return; + + sb.Append(Environment.NewLine); + sb.Append(Environment.NewLine); + sb.Append(resourceString); + sb.Append(Environment.NewLine); + sb.Append(attr); + } + + public override string ToString() + { + var sb = new StringBuilder(); + sb.Append(base.ToString()); + AppendResourceString(SR.HostProtection_ProtectedResources, ProtectedResources, sb); + AppendResourceString(SR.HostProtection_DemandedResources, DemandedResources, sb); + + return sb.ToString(); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue(ProtectedResourcesName, ProtectedResources, typeof(HostProtectionResource)); + info.AddValue(DemandedResourcesName, DemandedResources, typeof(HostProtectionResource)); + } } } diff --git a/external/corefx/src/System.Security.Permissions/src/System/Security/Permissions/HostProtectionResource.cs b/external/corefx/src/System.Security.Permissions/src/System/Security/Permissions/HostProtectionResource.cs index bf7ef9f891..bd9e557492 100644 --- a/external/corefx/src/System.Security.Permissions/src/System/Security/Permissions/HostProtectionResource.cs +++ b/external/corefx/src/System.Security.Permissions/src/System/Security/Permissions/HostProtectionResource.cs @@ -5,6 +5,7 @@ namespace System.Security.Permissions { [Flags] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public enum HostProtectionResource { All = 511, diff --git a/external/corefx/src/System.Security.Permissions/src/System/Security/Permissions/IsolatedStoragePermission.cs b/external/corefx/src/System.Security.Permissions/src/System/Security/Permissions/IsolatedStoragePermission.cs index 2b0dbbdc99..f6340b205a 100644 --- a/external/corefx/src/System.Security.Permissions/src/System/Security/Permissions/IsolatedStoragePermission.cs +++ b/external/corefx/src/System.Security.Permissions/src/System/Security/Permissions/IsolatedStoragePermission.cs @@ -4,7 +4,6 @@ namespace System.Security.Permissions { - [SecurityPermission(SecurityAction.InheritanceDemand, ControlEvidence = true, ControlPolicy = true)] public abstract class IsolatedStoragePermission : CodeAccessPermission, IUnrestrictedPermission { protected IsolatedStoragePermission(PermissionState state) { } diff --git a/external/corefx/src/System.Security.Permissions/src/System/Security/Permissions/ResourcePermissionBase.cs b/external/corefx/src/System.Security.Permissions/src/System/Security/Permissions/ResourcePermissionBase.cs index e34ae4d471..e1ac557bb4 100644 --- a/external/corefx/src/System.Security.Permissions/src/System/Security/Permissions/ResourcePermissionBase.cs +++ b/external/corefx/src/System.Security.Permissions/src/System/Security/Permissions/ResourcePermissionBase.cs @@ -6,7 +6,6 @@ using System.Collections; namespace System.Security.Permissions { - [SecurityPermission(SecurityAction.InheritanceDemand, ControlEvidence = true, ControlPolicy = true)] public abstract class ResourcePermissionBase : CodeAccessPermission, IUnrestrictedPermission { public const string Any = "*"; diff --git a/external/corefx/src/System.Security.Permissions/src/System/Security/Policy/PolicyException.cs b/external/corefx/src/System.Security.Permissions/src/System/Security/Policy/PolicyException.cs index b92c0b3f96..f7edd206b7 100644 --- a/external/corefx/src/System.Security.Permissions/src/System/Security/Policy/PolicyException.cs +++ b/external/corefx/src/System.Security.Permissions/src/System/Security/Policy/PolicyException.cs @@ -7,15 +7,14 @@ using System.Runtime.Serialization; namespace System.Security.Policy { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public partial class PolicyException : System.SystemException { public PolicyException() { } - protected PolicyException(SerializationInfo info, StreamingContext context) - { - throw new PlatformNotSupportedException(); - } - - public PolicyException(string message) { } - public PolicyException(string message, Exception exception) { } + protected PolicyException(SerializationInfo info, StreamingContext context) : base(info, context) { } + public PolicyException(string message) : base(message) { } + public PolicyException(string message, Exception exception) : base(message, exception) { } } } diff --git a/external/corefx/src/System.Security.Permissions/src/System/Security/XmlSyntaxException.cs b/external/corefx/src/System.Security.Permissions/src/System/Security/XmlSyntaxException.cs index 7d2ee515c9..81482ecbe3 100644 --- a/external/corefx/src/System.Security.Permissions/src/System/Security/XmlSyntaxException.cs +++ b/external/corefx/src/System.Security.Permissions/src/System/Security/XmlSyntaxException.cs @@ -7,6 +7,9 @@ using System.Runtime.Serialization; namespace System.Security { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public sealed partial class XmlSyntaxException : SystemException { public XmlSyntaxException() { } @@ -14,5 +17,6 @@ namespace System.Security public XmlSyntaxException(int lineNumber, string message) { } public XmlSyntaxException(string message) { } public XmlSyntaxException(string message, Exception inner) { } + private XmlSyntaxException(SerializationInfo info, StreamingContext context) : base(info, context) { } } } diff --git a/external/corefx/src/System.Security.Permissions/tests/System.Security.Permissions.Tests.csproj b/external/corefx/src/System.Security.Permissions/tests/System.Security.Permissions.Tests.csproj index 1e875bc523..a88f6b65e3 100644 --- a/external/corefx/src/System.Security.Permissions/tests/System.Security.Permissions.Tests.csproj +++ b/external/corefx/src/System.Security.Permissions/tests/System.Security.Permissions.Tests.csproj @@ -5,7 +5,6 @@ Properties {7517F1E9-EEB4-4676-A054-CE4A44A66B66} - @@ -31,4 +30,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Security.Principal.Windows/System.Security.Principal.Windows.sln b/external/corefx/src/System.Security.Principal.Windows/System.Security.Principal.Windows.sln index a22152fb88..c1d97cbedb 100644 --- a/external/corefx/src/System.Security.Principal.Windows/System.Security.Principal.Windows.sln +++ b/external/corefx/src/System.Security.Principal.Windows/System.Security.Principal.Windows.sln @@ -30,10 +30,10 @@ Global {6C36F3AC-54A1-4021-9F5D-CDEFF7347277}.Debug|Any CPU.Build.0 = netstandard-Windows_NT-Debug|Any CPU {6C36F3AC-54A1-4021-9F5D-CDEFF7347277}.Release|Any CPU.ActiveCfg = netstandard-Windows_NT-Release|Any CPU {6C36F3AC-54A1-4021-9F5D-CDEFF7347277}.Release|Any CPU.Build.0 = netstandard-Windows_NT-Release|Any CPU - {F9E9894E-2513-4085-9046-311AD49D8AE6}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU - {F9E9894E-2513-4085-9046-311AD49D8AE6}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU - {F9E9894E-2513-4085-9046-311AD49D8AE6}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU - {F9E9894E-2513-4085-9046-311AD49D8AE6}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {F9E9894E-2513-4085-9046-311AD49D8AE6}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {F9E9894E-2513-4085-9046-311AD49D8AE6}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {F9E9894E-2513-4085-9046-311AD49D8AE6}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {F9E9894E-2513-4085-9046-311AD49D8AE6}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU {25A02E40-D12C-4184-B599-E4F954D142DB}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU {25A02E40-D12C-4184-B599-E4F954D142DB}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU {25A02E40-D12C-4184-B599-E4F954D142DB}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU diff --git a/external/corefx/src/System.Security.Principal.Windows/ref/System.Security.Principal.Windows.cs b/external/corefx/src/System.Security.Principal.Windows/ref/System.Security.Principal.Windows.cs index debf533ac5..fadfe480b1 100644 --- a/external/corefx/src/System.Security.Principal.Windows/ref/System.Security.Principal.Windows.cs +++ b/external/corefx/src/System.Security.Principal.Windows/ref/System.Security.Principal.Windows.cs @@ -8,13 +8,11 @@ namespace Microsoft.Win32.SafeHandles { - [System.Security.SecurityCriticalAttribute] public sealed partial class SafeAccessTokenHandle : System.Runtime.InteropServices.SafeHandle { public SafeAccessTokenHandle(System.IntPtr handle) : base(default(System.IntPtr), default(bool)) { } - public static Microsoft.Win32.SafeHandles.SafeAccessTokenHandle InvalidHandle {[System.Security.SecurityCriticalAttribute]get { throw null; } } - public override bool IsInvalid {[System.Security.SecurityCriticalAttribute]get { throw null; } } - [System.Security.SecurityCriticalAttribute] + public static Microsoft.Win32.SafeHandles.SafeAccessTokenHandle InvalidHandle { get { throw null; } } + public override bool IsInvalid { get { throw null; } } protected override bool ReleaseHandle() { throw null; } } } @@ -327,7 +325,7 @@ namespace System.Security.Principal public WindowsIdentity(System.IntPtr userToken, string type) { } public WindowsIdentity(string sUserPrincipalName) { } public WindowsIdentity(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } - public Microsoft.Win32.SafeHandles.SafeAccessTokenHandle AccessToken {[System.Security.SecurityCriticalAttribute]get { throw null; } } + public Microsoft.Win32.SafeHandles.SafeAccessTokenHandle AccessToken { get { throw null; } } public sealed override string AuthenticationType { get { throw null; } } public override System.Collections.Generic.IEnumerable Claims { get { throw null; } } public System.Security.Principal.IdentityReferenceCollection Groups { get { throw null; } } diff --git a/external/corefx/src/System.Security.Principal.Windows/src/Configurations.props b/external/corefx/src/System.Security.Principal.Windows/src/Configurations.props index b16dc428ed..c0c9d3a56b 100644 --- a/external/corefx/src/System.Security.Principal.Windows/src/Configurations.props +++ b/external/corefx/src/System.Security.Principal.Windows/src/Configurations.props @@ -2,13 +2,15 @@ + netstandard; netcoreapp2.0-Windows_NT; netcoreapp2.0-Unix; - netstandard; netfx-Windows_NT; $(PackageConfigurations) + netcoreapp-Windows_NT; + netcoreapp-Unix; uap-Windows_NT; diff --git a/external/corefx/src/System.Security.Principal.Windows/src/PinvokeAnalyzerExceptionList.analyzerdata.netcoreapp b/external/corefx/src/System.Security.Principal.Windows/src/PinvokeAnalyzerExceptionList.analyzerdata.netcoreapp new file mode 100644 index 0000000000..6012e5aa02 --- /dev/null +++ b/external/corefx/src/System.Security.Principal.Windows/src/PinvokeAnalyzerExceptionList.analyzerdata.netcoreapp @@ -0,0 +1 @@ +advapi32.dll!LsaNtStatusToWinError \ No newline at end of file diff --git a/external/corefx/src/System.Security.Principal.Windows/src/System.Security.Principal.Windows.csproj b/external/corefx/src/System.Security.Principal.Windows/src/System.Security.Principal.Windows.csproj index ae96f2e72f..8840631204 100644 --- a/external/corefx/src/System.Security.Principal.Windows/src/System.Security.Principal.Windows.csproj +++ b/external/corefx/src/System.Security.Principal.Windows/src/System.Security.Principal.Windows.csproj @@ -12,18 +12,21 @@ $(DefineConstants);uap - - - + + + + + + - + @@ -183,7 +186,7 @@ Common\Microsoft\Win32\SafeHandles\SafeLsaHandle.cs - + Common\Interop\Interop.CheckTokenMembership.cs @@ -199,7 +202,6 @@ - diff --git a/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/IRCollection.cs b/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/IRCollection.cs index 0106de77a5..4d476dbd7e 100644 --- a/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/IRCollection.cs +++ b/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/IRCollection.cs @@ -6,7 +6,6 @@ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Reflection; namespace System.Security.Principal @@ -74,7 +73,6 @@ namespace System.Security.Principal { throw new ArgumentNullException(nameof(identity)); } - Contract.EndContractBlock(); _Identities.Add(identity); } @@ -85,7 +83,6 @@ namespace System.Security.Principal { throw new ArgumentNullException(nameof(identity)); } - Contract.EndContractBlock(); if (Contains(identity)) { @@ -106,7 +103,6 @@ namespace System.Security.Principal { throw new ArgumentNullException(nameof(identity)); } - Contract.EndContractBlock(); return _Identities.Contains(identity); } @@ -142,7 +138,6 @@ namespace System.Security.Principal { throw new ArgumentNullException(nameof(value)); } - Contract.EndContractBlock(); _Identities[index] = value; } @@ -176,7 +171,6 @@ namespace System.Security.Principal { throw new ArgumentException(SR.IdentityReference_MustBeIdentityReference, nameof(targetType)); } - Contract.EndContractBlock(); // // if the source collection is empty, just return an empty collection @@ -408,7 +402,6 @@ namespace System.Security.Principal { throw new ArgumentNullException(nameof(collection)); } - Contract.EndContractBlock(); _collection = collection; _current = -1; diff --git a/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/IdentityNotMappedException.cs b/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/IdentityNotMappedException.cs index 7696e68e26..da68815dd1 100644 --- a/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/IdentityNotMappedException.cs +++ b/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/IdentityNotMappedException.cs @@ -7,6 +7,9 @@ using System.Runtime.Serialization; namespace System.Security.Principal { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public sealed class IdentityNotMappedException : SystemException { private IdentityReferenceCollection _unmappedIdentities; @@ -32,6 +35,10 @@ namespace System.Security.Principal _unmappedIdentities = unmappedIdentities; } + private IdentityNotMappedException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + public override void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) { base.GetObjectData(serializationInfo, streamingContext); diff --git a/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/NTAccount.cs b/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/NTAccount.cs index 11aec4c8ed..6b02969198 100644 --- a/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/NTAccount.cs +++ b/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/NTAccount.cs @@ -6,7 +6,6 @@ using Microsoft.Win32; using Microsoft.Win32.SafeHandles; using System.ComponentModel; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Globalization; using System.Runtime.InteropServices; @@ -53,7 +52,6 @@ namespace System.Security.Principal { throw new ArgumentException(SR.IdentityReference_DomainNameTooLong, nameof(domainName)); } - Contract.EndContractBlock(); if (domainName == null || domainName.Length == 0) { @@ -81,7 +79,6 @@ namespace System.Security.Principal { throw new ArgumentException(SR.IdentityReference_AccountNameTooLong, nameof(name)); } - Contract.EndContractBlock(); _name = name; } @@ -119,7 +116,6 @@ namespace System.Security.Principal { throw new ArgumentNullException(nameof(targetType)); } - Contract.EndContractBlock(); if (targetType == typeof(NTAccount)) { @@ -187,7 +183,6 @@ namespace System.Security.Principal { throw new ArgumentNullException(nameof(sourceAccounts)); } - Contract.EndContractBlock(); if (targetType == typeof(SecurityIdentifier)) { @@ -241,7 +236,6 @@ namespace System.Security.Principal { throw new ArgumentException(SR.Arg_EmptyCollection, nameof(sourceAccounts)); } - Contract.EndContractBlock(); SafeLsaPolicyHandle LsaHandle = SafeLsaPolicyHandle.InvalidHandle; SafeLsaMemoryHandle ReferencedDomainsPtr = SafeLsaMemoryHandle.InvalidHandle; diff --git a/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/SID.cs b/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/SID.cs index 8e17a48366..c8a9510f18 100644 --- a/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/SID.cs +++ b/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/SID.cs @@ -6,7 +6,6 @@ using Microsoft.Win32.SafeHandles; using System; using System.ComponentModel; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Globalization; using System.Runtime.InteropServices; using System.Text; @@ -318,8 +317,6 @@ namespace System.Security.Principal throw new ArgumentNullException(nameof(subAuthorities)); } - Contract.EndContractBlock(); - // // Check the number of subauthorities passed in // @@ -430,7 +427,6 @@ nameof(offset), nameof(binaryForm), SR.ArgumentOutOfRange_ArrayTooSmall); } - Contract.EndContractBlock(); IdentifierAuthority Authority; int[] SubAuthorities; @@ -527,7 +523,6 @@ nameof(binaryForm)); { throw new ArgumentNullException(nameof(sddlForm)); } - Contract.EndContractBlock(); // // Call into the underlying O/S conversion routine @@ -595,7 +590,6 @@ nameof(binaryForm)); { throw new ArgumentException(SR.IdentityReference_CannotCreateLogonIdsSid, nameof(sidType)); } - Contract.EndContractBlock(); byte[] resultSid; int Error; @@ -895,7 +889,6 @@ nameof(binaryForm)); { throw new ArgumentNullException(nameof(targetType)); } - Contract.EndContractBlock(); if (targetType == typeof(SecurityIdentifier)) { @@ -955,7 +948,6 @@ nameof(binaryForm)); { throw new ArgumentNullException(nameof(sid)); } - Contract.EndContractBlock(); if (this.IdentifierAuthority < sid.IdentifierAuthority) { @@ -1040,7 +1032,6 @@ nameof(binaryForm)); { throw new ArgumentException(SR.Arg_EmptyCollection, nameof(sourceSids)); } - Contract.EndContractBlock(); IntPtr[] SidArrayPtr = new IntPtr[sourceSids.Count]; GCHandle[] HandleArray = new GCHandle[sourceSids.Count]; @@ -1221,7 +1212,6 @@ nameof(binaryForm)); { throw new ArgumentNullException(nameof(sourceSids)); } - Contract.EndContractBlock(); if (targetType == typeof(NTAccount)) { diff --git a/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/WindowsIdentity.cs b/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/WindowsIdentity.cs index afa5ddeca7..486d640a03 100644 --- a/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/WindowsIdentity.cs +++ b/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/WindowsIdentity.cs @@ -6,7 +6,6 @@ using Microsoft.Win32.SafeHandles; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.InteropServices; using System.Security.Claims; using System.Text; @@ -213,7 +212,6 @@ namespace System.Security.Principal { if (userToken == IntPtr.Zero) throw new ArgumentException(SR.Argument_TokenZero); - Contract.EndContractBlock(); // Find out if the specified token is a valid. uint dwLength = (uint)sizeof(uint); @@ -373,8 +371,6 @@ namespace System.Security.Principal } private bool CheckNtTokenForSid(SecurityIdentifier sid) { - Contract.EndContractBlock(); - // special case the anonymous identity. if (_safeTokenHandle.IsInvalid) return false; diff --git a/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/WindowsPrincipal.cs b/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/WindowsPrincipal.cs index 1181e4dcd7..87c9855f25 100644 --- a/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/WindowsPrincipal.cs +++ b/external/corefx/src/System.Security.Principal.Windows/src/System/Security/Principal/WindowsPrincipal.cs @@ -5,7 +5,6 @@ using Microsoft.Win32.SafeHandles; using System.Collections.Generic; using System.ComponentModel; -using System.Diagnostics.Contracts; using System.Security.Claims; namespace System.Security.Principal @@ -39,7 +38,6 @@ namespace System.Security.Principal { if (ntIdentity == null) throw new ArgumentNullException(nameof(ntIdentity)); - Contract.EndContractBlock(); _identity = ntIdentity; } @@ -132,7 +130,6 @@ namespace System.Security.Principal { if (role < WindowsBuiltInRole.Administrator || role > WindowsBuiltInRole.Replicator) throw new ArgumentException(SR.Format(SR.Arg_EnumIllegalVal, (int)role), nameof(role)); - Contract.EndContractBlock(); return IsInRole((int)role); } @@ -154,7 +151,6 @@ namespace System.Security.Principal { if (sid == null) throw new ArgumentNullException(nameof(sid)); - Contract.EndContractBlock(); // special case the anonymous identity. if (_identity.AccessToken.IsInvalid) diff --git a/external/corefx/src/System.Security.SecureString/tests/System.Security.SecureString.Tests.csproj b/external/corefx/src/System.Security.SecureString/tests/System.Security.SecureString.Tests.csproj index a8086cee21..f620b19326 100644 --- a/external/corefx/src/System.Security.SecureString/tests/System.Security.SecureString.Tests.csproj +++ b/external/corefx/src/System.Security.SecureString/tests/System.Security.SecureString.Tests.csproj @@ -5,7 +5,6 @@ {69609238-62C7-479D-A8CE-709F41101D3C} true - diff --git a/external/corefx/src/System.ServiceModel.Syndication/System.ServiceModel.Syndication.sln b/external/corefx/src/System.ServiceModel.Syndication/System.ServiceModel.Syndication.sln index 7a9b5eb4a8..c00cdf5e86 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/System.ServiceModel.Syndication.sln +++ b/external/corefx/src/System.ServiceModel.Syndication/System.ServiceModel.Syndication.sln @@ -1,31 +1,50 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26906.1 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.ServiceModel.Syndication", "src\System.ServiceModel.Syndication.csproj", "{C2AB129B-E3EC-465B-B1B7-0F7D414B1E74}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.ServiceModel.Syndication.Tests", "tests\System.ServiceModel.Syndication.Tests.csproj", "{A622B2C0-DD74-4218-9CF0-F9B2E52F4E91}" + ProjectSection(ProjectDependencies) = postProject + {C2AB129B-E3EC-465B-B1B7-0F7D414B1E74} = {C2AB129B-E3EC-465B-B1B7-0F7D414B1E74} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.ServiceModel.Syndication", "src\System.ServiceModel.Syndication.csproj", "{C2AB129B-E3EC-465B-B1B7-0F7D414B1E74}" + ProjectSection(ProjectDependencies) = postProject + {E81F4C7C-2000-4449-BEE6-B2E84DE218F7} = {E81F4C7C-2000-4449-BEE6-B2E84DE218F7} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.ServiceModel.Syndication", "ref\System.ServiceModel.Syndication.csproj", "{E81F4C7C-2000-4449-BEE6-B2E84DE218F7}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{1A2F9F4A-A032-433E-B914-ADD5992BB178}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E107E9C1-E893-4E87-987E-04EF0DCEAEFD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{2E666815-2EDB-464B-9DF6-380BF4789AD4}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - netstandard-Debug|Any CPU = netstandard-Debug|Any CPU - netstandard-Release|Any CPU = netstandard-Release|Any CPU + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C2AB129B-E3EC-465B-B1B7-0F7D414B1E74}.netstandard-Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU - {C2AB129B-E3EC-465B-B1B7-0F7D414B1E74}.netstandard-Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU - {C2AB129B-E3EC-465B-B1B7-0F7D414B1E74}.netstandard-Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU - {C2AB129B-E3EC-465B-B1B7-0F7D414B1E74}.netstandard-Release|Any CPU.Build.0 = netstandard-Release|Any CPU - {A622B2C0-DD74-4218-9CF0-F9B2E52F4E91}.netstandard-Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU - {A622B2C0-DD74-4218-9CF0-F9B2E52F4E91}.netstandard-Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU - {A622B2C0-DD74-4218-9CF0-F9B2E52F4E91}.netstandard-Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU - {A622B2C0-DD74-4218-9CF0-F9B2E52F4E91}.netstandard-Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {A622B2C0-DD74-4218-9CF0-F9B2E52F4E91}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {A622B2C0-DD74-4218-9CF0-F9B2E52F4E91}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {A622B2C0-DD74-4218-9CF0-F9B2E52F4E91}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {A622B2C0-DD74-4218-9CF0-F9B2E52F4E91}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU + {C2AB129B-E3EC-465B-B1B7-0F7D414B1E74}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {C2AB129B-E3EC-465B-B1B7-0F7D414B1E74}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {C2AB129B-E3EC-465B-B1B7-0F7D414B1E74}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {C2AB129B-E3EC-465B-B1B7-0F7D414B1E74}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {E81F4C7C-2000-4449-BEE6-B2E84DE218F7}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {E81F4C7C-2000-4449-BEE6-B2E84DE218F7}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {E81F4C7C-2000-4449-BEE6-B2E84DE218F7}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {E81F4C7C-2000-4449-BEE6-B2E84DE218F7}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {C509160C-D427-4B6F-A515-169B6BB2B210} + GlobalSection(NestedProjects) = preSolution + {A622B2C0-DD74-4218-9CF0-F9B2E52F4E91} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} + {C2AB129B-E3EC-465B-B1B7-0F7D414B1E74} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD} + {E81F4C7C-2000-4449-BEE6-B2E84DE218F7} = {2E666815-2EDB-464B-9DF6-380BF4789AD4} EndGlobalSection EndGlobal diff --git a/external/corefx/src/System.ServiceModel.Syndication/ref/Configurations.props b/external/corefx/src/System.ServiceModel.Syndication/ref/Configurations.props index 934f7517ba..05299f6226 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/ref/Configurations.props +++ b/external/corefx/src/System.ServiceModel.Syndication/ref/Configurations.props @@ -3,6 +3,7 @@ netfx; + netcoreapp; netstandard; diff --git a/external/corefx/src/System.ServiceModel.Syndication/ref/System.ServiceModel.Syndication.cs b/external/corefx/src/System.ServiceModel.Syndication/ref/System.ServiceModel.Syndication.cs index 0ab8cc4152..a1ab83fa4c 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/ref/System.ServiceModel.Syndication.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/ref/System.ServiceModel.Syndication.cs @@ -7,55 +7,8 @@ namespace System.ServiceModel.Syndication { - public static partial class Atom10Constants + public partial class Atom10FeedFormatter : System.ServiceModel.Syndication.SyndicationFeedFormatter, System.Xml.Serialization.IXmlSerializable { - public const string AlternateTag = "alternate"; - public const string Atom10Namespace = "http://www.w3.org/2005/Atom"; - public const string Atom10Prefix = "a10"; - public const string AtomMediaType = "application/atom+xml"; - public const string AuthorTag = "author"; - public const string CategoryTag = "category"; - public const string ContentTag = "content"; - public const string ContributorTag = "contributor"; - public const string EmailTag = "email"; - public const string EntryTag = "entry"; - public const string FeedTag = "feed"; - public const string GeneratorTag = "generator"; - public const string HrefTag = "href"; - public const string HtmlMediaType = "text/html"; - public const string HtmlType = "html"; - public const string IconTag = "icon"; - public const string IdTag = "id"; - public const string LabelTag = "label"; - public const string LengthTag = "length"; - public const string LinkTag = "link"; - public const string LogoTag = "logo"; - public const string NameTag = "name"; - public const string PlaintextType = "text"; - public const string PublishedTag = "published"; - public const string RelativeTag = "rel"; - public const string RightsTag = "rights"; - public const string SchemeTag = "scheme"; - public const string SelfTag = "self"; - public const string SourceFeedTag = "source"; - public const string SourceTag = "src"; - public const string SpecificationLink = "http://atompub.org/2005/08/17/draft-ietf-atompub-format-11.html"; - public const string SubtitleTag = "subtitle"; - public const string SummaryTag = "summary"; - public const string TermTag = "term"; - public const string TitleTag = "title"; - public const string TypeTag = "type"; - public const string UpdatedTag = "updated"; - public const string UriTag = "uri"; - public const string XHtmlMediaType = "application/xhtml+xml"; - public const string XHtmlType = "xhtml"; - public const string XmlMediaType = "text/xml"; - } - public partial class Atom10FeedFormatter : System.ServiceModel.Syndication.SyndicationFeedFormatter - { - public System.Func dateParser; - public System.Func stringParser; - public System.Func uriParser; public Atom10FeedFormatter() { } public Atom10FeedFormatter(System.ServiceModel.Syndication.SyndicationFeed feedToWrite) { } public Atom10FeedFormatter(System.Type feedTypeToCreate) { } @@ -65,12 +18,15 @@ namespace System.ServiceModel.Syndication public override string Version { get { throw null; } } public override bool CanRead(System.Xml.XmlReader reader) { throw null; } protected override System.ServiceModel.Syndication.SyndicationFeed CreateFeedInstance() { throw null; } - public override System.Threading.Tasks.Task ReadFromAsync(System.Xml.XmlReader reader, System.Threading.CancellationToken ct) { throw null; } - protected virtual System.Threading.Tasks.Task ReadItemAsync(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationFeed feed) { throw null; } - protected virtual System.Threading.Tasks.Task> ReadItemsAsync(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationFeed feed) { throw null; } - protected virtual System.Threading.Tasks.Task WriteItemAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationItem item, System.Uri feedBaseUri) { throw null; } - protected virtual System.Threading.Tasks.Task WriteItemsAsync(System.Xml.XmlWriter writer, System.Collections.Generic.IEnumerable items, System.Uri feedBaseUri) { throw null; } - public override System.Threading.Tasks.Task WriteToAsync(System.Xml.XmlWriter writer, System.Threading.CancellationToken ct) { throw null; } + public override void ReadFrom(System.Xml.XmlReader reader) { } + protected virtual System.ServiceModel.Syndication.SyndicationItem ReadItem(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationFeed feed) { throw null; } + protected virtual System.Collections.Generic.IEnumerable ReadItems(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationFeed feed, out bool areAllItemsRead) { areAllItemsRead = default(bool); throw null; } + System.Xml.Schema.XmlSchema System.Xml.Serialization.IXmlSerializable.GetSchema() { throw null; } + void System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader reader) { } + void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { } + protected virtual void WriteItem(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationItem item, System.Uri feedBaseUri) { } + protected virtual void WriteItems(System.Xml.XmlWriter writer, System.Collections.Generic.IEnumerable items, System.Uri feedBaseUri) { } + public override void WriteTo(System.Xml.XmlWriter writer) { } } public partial class Atom10FeedFormatter : System.ServiceModel.Syndication.Atom10FeedFormatter where TSyndicationFeed : System.ServiceModel.Syndication.SyndicationFeed, new() { @@ -78,7 +34,7 @@ namespace System.ServiceModel.Syndication public Atom10FeedFormatter(TSyndicationFeed feedToWrite) { } protected override System.ServiceModel.Syndication.SyndicationFeed CreateFeedInstance() { throw null; } } - public partial class Atom10ItemFormatter : System.ServiceModel.Syndication.SyndicationItemFormatter + public partial class Atom10ItemFormatter : System.ServiceModel.Syndication.SyndicationItemFormatter, System.Xml.Serialization.IXmlSerializable { public Atom10ItemFormatter() { } public Atom10ItemFormatter(System.ServiceModel.Syndication.SyndicationItem itemToWrite) { } @@ -89,8 +45,11 @@ namespace System.ServiceModel.Syndication public override string Version { get { throw null; } } public override bool CanRead(System.Xml.XmlReader reader) { throw null; } protected override System.ServiceModel.Syndication.SyndicationItem CreateItemInstance() { throw null; } - public override System.Threading.Tasks.Task ReadFromAsync(System.Xml.XmlReader reader) { throw null; } - public override System.Threading.Tasks.Task WriteToAsync(System.Xml.XmlWriter writer) { throw null; } + public override void ReadFrom(System.Xml.XmlReader reader) { } + System.Xml.Schema.XmlSchema System.Xml.Serialization.IXmlSerializable.GetSchema() { throw null; } + void System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader reader) { } + void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { } + public override void WriteTo(System.Xml.XmlWriter writer) { } } public partial class Atom10ItemFormatter : System.ServiceModel.Syndication.Atom10ItemFormatter where TSyndicationItem : System.ServiceModel.Syndication.SyndicationItem, new() { @@ -98,28 +57,34 @@ namespace System.ServiceModel.Syndication public Atom10ItemFormatter(TSyndicationItem itemToWrite) { } protected override System.ServiceModel.Syndication.SyndicationItem CreateItemInstance() { throw null; } } - public partial class AtomPub10CategoriesDocumentFormatter : System.ServiceModel.Syndication.CategoriesDocumentFormatter + public partial class AtomPub10CategoriesDocumentFormatter : System.ServiceModel.Syndication.CategoriesDocumentFormatter, System.Xml.Serialization.IXmlSerializable { public AtomPub10CategoriesDocumentFormatter() { } public AtomPub10CategoriesDocumentFormatter(System.ServiceModel.Syndication.CategoriesDocument documentToWrite) { } public AtomPub10CategoriesDocumentFormatter(System.Type inlineDocumentType, System.Type referencedDocumentType) { } public override string Version { get { throw null; } } - public override System.Threading.Tasks.Task CanReadAsync(System.Xml.XmlReader reader) { throw null; } + public override bool CanRead(System.Xml.XmlReader reader) { throw null; } protected override System.ServiceModel.Syndication.InlineCategoriesDocument CreateInlineCategoriesDocument() { throw null; } protected override System.ServiceModel.Syndication.ReferencedCategoriesDocument CreateReferencedCategoriesDocument() { throw null; } - public override System.Threading.Tasks.Task ReadFromAsync(System.Xml.XmlReader reader) { throw null; } - public override System.Threading.Tasks.Task WriteTo(System.Xml.XmlWriter writer) { throw null; } + public override void ReadFrom(System.Xml.XmlReader reader) { } + System.Xml.Schema.XmlSchema System.Xml.Serialization.IXmlSerializable.GetSchema() { throw null; } + void System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader reader) { } + void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { } + public override void WriteTo(System.Xml.XmlWriter writer) { } } - public partial class AtomPub10ServiceDocumentFormatter : System.ServiceModel.Syndication.ServiceDocumentFormatter + public partial class AtomPub10ServiceDocumentFormatter : System.ServiceModel.Syndication.ServiceDocumentFormatter, System.Xml.Serialization.IXmlSerializable { public AtomPub10ServiceDocumentFormatter() { } public AtomPub10ServiceDocumentFormatter(System.ServiceModel.Syndication.ServiceDocument documentToWrite) { } public AtomPub10ServiceDocumentFormatter(System.Type documentTypeToCreate) { } public override string Version { get { throw null; } } - public override System.Threading.Tasks.Task CanReadAsync(System.Xml.XmlReader reader) { throw null; } + public override bool CanRead(System.Xml.XmlReader reader) { throw null; } protected override System.ServiceModel.Syndication.ServiceDocument CreateDocumentInstance() { throw null; } - public override System.Threading.Tasks.Task ReadFromAsync(System.Xml.XmlReader reader) { throw null; } - public override System.Threading.Tasks.Task WriteToAsync(System.Xml.XmlWriter writer) { throw null; } + public override void ReadFrom(System.Xml.XmlReader reader) { } + System.Xml.Schema.XmlSchema System.Xml.Serialization.IXmlSerializable.GetSchema() { throw null; } + void System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader reader) { } + void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { } + public override void WriteTo(System.Xml.XmlWriter writer) { } } public partial class AtomPub10ServiceDocumentFormatter : System.ServiceModel.Syndication.AtomPub10ServiceDocumentFormatter where TServiceDocument : System.ServiceModel.Syndication.ServiceDocument, new() { @@ -138,25 +103,26 @@ namespace System.ServiceModel.Syndication public static System.ServiceModel.Syndication.InlineCategoriesDocument Create(System.Collections.ObjectModel.Collection categories, bool isFixed, string scheme) { throw null; } public static System.ServiceModel.Syndication.ReferencedCategoriesDocument Create(System.Uri linkToCategoriesDocument) { throw null; } public System.ServiceModel.Syndication.CategoriesDocumentFormatter GetFormatter() { throw null; } - public static System.Threading.Tasks.Task LoadAsync(System.Xml.XmlReader reader) { throw null; } + public static System.ServiceModel.Syndication.CategoriesDocument Load(System.Xml.XmlReader reader) { throw null; } public void Save(System.Xml.XmlWriter writer) { } protected internal virtual bool TryParseAttribute(string name, string ns, string value, string version) { throw null; } protected internal virtual bool TryParseElement(System.Xml.XmlReader reader, string version) { throw null; } - protected internal virtual System.Threading.Tasks.Task WriteAttributeExtensionsAsync(System.Xml.XmlWriter writer, string version) { throw null; } - protected internal virtual System.Threading.Tasks.Task WriteElementExtensionsAsync(System.Xml.XmlWriter writer, string version) { throw null; } + protected internal virtual void WriteAttributeExtensions(System.Xml.XmlWriter writer, string version) { } + protected internal virtual void WriteElementExtensions(System.Xml.XmlWriter writer, string version) { } } + [System.Runtime.Serialization.DataContractAttribute] public abstract partial class CategoriesDocumentFormatter { protected CategoriesDocumentFormatter() { } protected CategoriesDocumentFormatter(System.ServiceModel.Syndication.CategoriesDocument documentToWrite) { } public System.ServiceModel.Syndication.CategoriesDocument Document { get { throw null; } } public abstract string Version { get; } - public abstract System.Threading.Tasks.Task CanReadAsync(System.Xml.XmlReader reader); + public abstract bool CanRead(System.Xml.XmlReader reader); protected virtual System.ServiceModel.Syndication.InlineCategoriesDocument CreateInlineCategoriesDocument() { throw null; } protected virtual System.ServiceModel.Syndication.ReferencedCategoriesDocument CreateReferencedCategoriesDocument() { throw null; } - public abstract System.Threading.Tasks.Task ReadFromAsync(System.Xml.XmlReader reader); + public abstract void ReadFrom(System.Xml.XmlReader reader); protected virtual void SetDocument(System.ServiceModel.Syndication.CategoriesDocument document) { } - public abstract System.Threading.Tasks.Task WriteTo(System.Xml.XmlWriter writer); + public abstract void WriteTo(System.Xml.XmlWriter writer); } public partial class InlineCategoriesDocument : System.ServiceModel.Syndication.CategoriesDocument { @@ -192,32 +158,32 @@ namespace System.ServiceModel.Syndication protected internal virtual System.ServiceModel.Syndication.ReferencedCategoriesDocument CreateReferencedCategoriesDocument() { throw null; } protected internal virtual bool TryParseAttribute(string name, string ns, string value, string version) { throw null; } protected internal virtual bool TryParseElement(System.Xml.XmlReader reader, string version) { throw null; } - protected internal virtual System.Threading.Tasks.Task WriteAttributeExtensionsAsync(System.Xml.XmlWriter writer, string version) { throw null; } - protected internal virtual System.Threading.Tasks.Task WriteElementExtensionsAsync(System.Xml.XmlWriter writer, string version) { throw null; } + protected internal virtual void WriteAttributeExtensions(System.Xml.XmlWriter writer, string version) { } + protected internal virtual void WriteElementExtensions(System.Xml.XmlWriter writer, string version) { } } - public partial class Rss20FeedFormatter : System.ServiceModel.Syndication.SyndicationFeedFormatter + public partial class Rss20FeedFormatter : System.ServiceModel.Syndication.SyndicationFeedFormatter, System.Xml.Serialization.IXmlSerializable { public Rss20FeedFormatter() { } public Rss20FeedFormatter(System.ServiceModel.Syndication.SyndicationFeed feedToWrite) { } public Rss20FeedFormatter(System.ServiceModel.Syndication.SyndicationFeed feedToWrite, bool serializeExtensionsAsAtom) { } public Rss20FeedFormatter(System.Type feedTypeToCreate) { } - public System.Func DateParser { get { throw null; } set { } } protected System.Type FeedType { get { throw null; } } public bool PreserveAttributeExtensions { get { throw null; } set { } } public bool PreserveElementExtensions { get { throw null; } set { } } public bool SerializeExtensionsAsAtom { get { throw null; } set { } } - public System.Func StringParser { get { throw null; } set { } } - public System.Func UriParser { get { throw null; } set { } } public override string Version { get { throw null; } } public override bool CanRead(System.Xml.XmlReader reader) { throw null; } protected override System.ServiceModel.Syndication.SyndicationFeed CreateFeedInstance() { throw null; } - public static System.DateTimeOffset DefaultDateParser(string dateTimeString, string localName, string ns) { throw null; } - public override System.Threading.Tasks.Task ReadFromAsync(System.Xml.XmlReader reader, System.Threading.CancellationToken ct) { throw null; } - protected virtual System.Threading.Tasks.Task ReadItemAsync(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationFeed feed) { throw null; } + public override void ReadFrom(System.Xml.XmlReader reader) { } + protected virtual System.ServiceModel.Syndication.SyndicationItem ReadItem(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationFeed feed) { throw null; } + protected virtual System.Collections.Generic.IEnumerable ReadItems(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationFeed feed, out bool areAllItemsRead) { areAllItemsRead = default(bool); throw null; } protected internal override void SetFeed(System.ServiceModel.Syndication.SyndicationFeed feed) { } - protected virtual System.Threading.Tasks.Task WriteItemAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationItem item, System.Uri feedBaseUri) { throw null; } - protected virtual System.Threading.Tasks.Task WriteItemsAsync(System.Xml.XmlWriter writer, System.Collections.Generic.IEnumerable items, System.Uri feedBaseUri) { throw null; } - public override System.Threading.Tasks.Task WriteToAsync(System.Xml.XmlWriter writer, System.Threading.CancellationToken ct) { throw null; } + System.Xml.Schema.XmlSchema System.Xml.Serialization.IXmlSerializable.GetSchema() { throw null; } + void System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader reader) { } + void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { } + protected virtual void WriteItem(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationItem item, System.Uri feedBaseUri) { } + protected virtual void WriteItems(System.Xml.XmlWriter writer, System.Collections.Generic.IEnumerable items, System.Uri feedBaseUri) { } + public override void WriteTo(System.Xml.XmlWriter writer) { } } public partial class Rss20FeedFormatter : System.ServiceModel.Syndication.Rss20FeedFormatter where TSyndicationFeed : System.ServiceModel.Syndication.SyndicationFeed, new() { @@ -226,7 +192,7 @@ namespace System.ServiceModel.Syndication public Rss20FeedFormatter(TSyndicationFeed feedToWrite, bool serializeExtensionsAsAtom) { } protected override System.ServiceModel.Syndication.SyndicationFeed CreateFeedInstance() { throw null; } } - public partial class Rss20ItemFormatter : System.ServiceModel.Syndication.SyndicationItemFormatter + public partial class Rss20ItemFormatter : System.ServiceModel.Syndication.SyndicationItemFormatter, System.Xml.Serialization.IXmlSerializable { public Rss20ItemFormatter() { } public Rss20ItemFormatter(System.ServiceModel.Syndication.SyndicationItem itemToWrite) { } @@ -239,10 +205,13 @@ namespace System.ServiceModel.Syndication public override string Version { get { throw null; } } public override bool CanRead(System.Xml.XmlReader reader) { throw null; } protected override System.ServiceModel.Syndication.SyndicationItem CreateItemInstance() { throw null; } - public override System.Threading.Tasks.Task ReadFromAsync(System.Xml.XmlReader reader) { throw null; } - public override System.Threading.Tasks.Task WriteToAsync(System.Xml.XmlWriter writer) { throw null; } + public override void ReadFrom(System.Xml.XmlReader reader) { } + System.Xml.Schema.XmlSchema System.Xml.Serialization.IXmlSerializable.GetSchema() { throw null; } + void System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader reader) { } + void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { } + public override void WriteTo(System.Xml.XmlWriter writer) { } } - public partial class Rss20ItemFormatter : System.ServiceModel.Syndication.Rss20ItemFormatter where TSyndicationItem : System.ServiceModel.Syndication.SyndicationItem, new() + public partial class Rss20ItemFormatter : System.ServiceModel.Syndication.Rss20ItemFormatter, System.Xml.Serialization.IXmlSerializable where TSyndicationItem : System.ServiceModel.Syndication.SyndicationItem, new() { public Rss20ItemFormatter() { } public Rss20ItemFormatter(TSyndicationItem itemToWrite) { } @@ -260,12 +229,13 @@ namespace System.ServiceModel.Syndication public System.Collections.ObjectModel.Collection Workspaces { get { throw null; } } protected internal virtual System.ServiceModel.Syndication.Workspace CreateWorkspace() { throw null; } public System.ServiceModel.Syndication.ServiceDocumentFormatter GetFormatter() { throw null; } - public static System.Threading.Tasks.Task LoadAsync(System.Xml.XmlReader reader) where TServiceDocument : System.ServiceModel.Syndication.ServiceDocument, new() { throw null; } - public System.Threading.Tasks.Task Save(System.Xml.XmlWriter writer) { throw null; } + public static System.ServiceModel.Syndication.ServiceDocument Load(System.Xml.XmlReader reader) { throw null; } + public static TServiceDocument Load(System.Xml.XmlReader reader) where TServiceDocument : System.ServiceModel.Syndication.ServiceDocument, new() { throw null; } + public void Save(System.Xml.XmlWriter writer) { } protected internal virtual bool TryParseAttribute(string name, string ns, string value, string version) { throw null; } protected internal virtual bool TryParseElement(System.Xml.XmlReader reader, string version) { throw null; } - protected internal virtual System.Threading.Tasks.Task WriteAttributeExtensionsAsync(System.Xml.XmlWriter writer, string version) { throw null; } - protected internal virtual System.Threading.Tasks.Task WriteElementExtensionsAsync(System.Xml.XmlWriter writer, string version) { throw null; } + protected internal virtual void WriteAttributeExtensions(System.Xml.XmlWriter writer, string version) { } + protected internal virtual void WriteElementExtensions(System.Xml.XmlWriter writer, string version) { } } [System.Runtime.Serialization.DataContractAttribute] public abstract partial class ServiceDocumentFormatter @@ -274,7 +244,7 @@ namespace System.ServiceModel.Syndication protected ServiceDocumentFormatter(System.ServiceModel.Syndication.ServiceDocument documentToWrite) { } public System.ServiceModel.Syndication.ServiceDocument Document { get { throw null; } } public abstract string Version { get; } - public abstract System.Threading.Tasks.Task CanReadAsync(System.Xml.XmlReader reader); + public abstract bool CanRead(System.Xml.XmlReader reader); protected static System.ServiceModel.Syndication.SyndicationCategory CreateCategory(System.ServiceModel.Syndication.InlineCategoriesDocument inlineCategories) { throw null; } protected static System.ServiceModel.Syndication.ResourceCollectionInfo CreateCollection(System.ServiceModel.Syndication.Workspace workspace) { throw null; } protected virtual System.ServiceModel.Syndication.ServiceDocument CreateDocumentInstance() { throw null; } @@ -285,7 +255,7 @@ namespace System.ServiceModel.Syndication protected static void LoadElementExtensions(System.Xml.XmlReader reader, System.ServiceModel.Syndication.ResourceCollectionInfo collection, int maxExtensionSize) { } protected static void LoadElementExtensions(System.Xml.XmlReader reader, System.ServiceModel.Syndication.ServiceDocument document, int maxExtensionSize) { } protected static void LoadElementExtensions(System.Xml.XmlReader reader, System.ServiceModel.Syndication.Workspace workspace, int maxExtensionSize) { } - public abstract System.Threading.Tasks.Task ReadFromAsync(System.Xml.XmlReader reader); + public abstract void ReadFrom(System.Xml.XmlReader reader); protected virtual void SetDocument(System.ServiceModel.Syndication.ServiceDocument document) { } protected static bool TryParseAttribute(string name, string ns, string value, System.ServiceModel.Syndication.CategoriesDocument categories, string version) { throw null; } protected static bool TryParseAttribute(string name, string ns, string value, System.ServiceModel.Syndication.ResourceCollectionInfo collection, string version) { throw null; } @@ -295,15 +265,15 @@ namespace System.ServiceModel.Syndication protected static bool TryParseElement(System.Xml.XmlReader reader, System.ServiceModel.Syndication.ResourceCollectionInfo collection, string version) { throw null; } protected static bool TryParseElement(System.Xml.XmlReader reader, System.ServiceModel.Syndication.ServiceDocument document, string version) { throw null; } protected static bool TryParseElement(System.Xml.XmlReader reader, System.ServiceModel.Syndication.Workspace workspace, string version) { throw null; } + protected static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.CategoriesDocument categories, string version) { } + protected static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.ResourceCollectionInfo collection, string version) { } protected static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.ServiceDocument document, string version) { } protected static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.Workspace workspace, string version) { } - protected static System.Threading.Tasks.Task WriteAttributeExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.CategoriesDocument categories, string version) { throw null; } - protected static System.Threading.Tasks.Task WriteAttributeExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.ResourceCollectionInfo collection, string version) { throw null; } - protected static System.Threading.Tasks.Task WriteElementExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.CategoriesDocument categories, string version) { throw null; } - protected static System.Threading.Tasks.Task WriteElementExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.ResourceCollectionInfo collection, string version) { throw null; } - protected static System.Threading.Tasks.Task WriteElementExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.ServiceDocument document, string version) { throw null; } - protected static System.Threading.Tasks.Task WriteElementExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.Workspace workspace, string version) { throw null; } - public abstract System.Threading.Tasks.Task WriteToAsync(System.Xml.XmlWriter writer); + protected static void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.CategoriesDocument categories, string version) { } + protected static void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.ResourceCollectionInfo collection, string version) { } + protected static void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.ServiceDocument document, string version) { } + protected static void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.Workspace workspace, string version) { } + public abstract void WriteTo(System.Xml.XmlWriter writer); } public partial class SyndicationCategory { @@ -319,8 +289,8 @@ namespace System.ServiceModel.Syndication public virtual System.ServiceModel.Syndication.SyndicationCategory Clone() { throw null; } protected internal virtual bool TryParseAttribute(string name, string ns, string value, string version) { throw null; } protected internal virtual bool TryParseElement(System.Xml.XmlReader reader, string version) { throw null; } - protected internal virtual System.Threading.Tasks.Task WriteAttributeExtensionsAsync(System.Xml.XmlWriter writer, string version) { throw null; } - protected internal virtual System.Threading.Tasks.Task WriteElementExtensionsAsync(System.Xml.XmlWriter writer, string version) { throw null; } + protected internal virtual void WriteAttributeExtensions(System.Xml.XmlWriter writer, string version) { } + protected internal virtual void WriteElementExtensions(System.Xml.XmlWriter writer, string version) { } } public abstract partial class SyndicationContent { @@ -336,9 +306,9 @@ namespace System.ServiceModel.Syndication public static System.ServiceModel.Syndication.XmlSyndicationContent CreateXmlContent(object dataContractObject) { throw null; } public static System.ServiceModel.Syndication.XmlSyndicationContent CreateXmlContent(object dataContractObject, System.Runtime.Serialization.XmlObjectSerializer dataContractSerializer) { throw null; } public static System.ServiceModel.Syndication.XmlSyndicationContent CreateXmlContent(object xmlSerializerObject, System.Xml.Serialization.XmlSerializer serializer) { throw null; } - public static System.ServiceModel.Syndication.XmlSyndicationContent CreateXmlContent(System.Xml.XmlReader XmlReaderWrapper) { throw null; } + public static System.ServiceModel.Syndication.XmlSyndicationContent CreateXmlContent(System.Xml.XmlReader xmlReader) { throw null; } protected abstract void WriteContentsTo(System.Xml.XmlWriter writer); - public System.Threading.Tasks.Task WriteToAsync(System.Xml.XmlWriter writer, string outerElementName, string outerElementNamespace) { throw null; } + public void WriteTo(System.Xml.XmlWriter writer, string outerElementName, string outerElementNamespace) { } } public partial class SyndicationElementExtension { @@ -347,14 +317,14 @@ namespace System.ServiceModel.Syndication public SyndicationElementExtension(object xmlSerializerExtension, System.Xml.Serialization.XmlSerializer serializer) { } public SyndicationElementExtension(string outerName, string outerNamespace, object dataContractExtension) { } public SyndicationElementExtension(string outerName, string outerNamespace, object dataContractExtension, System.Runtime.Serialization.XmlObjectSerializer dataContractSerializer) { } - public SyndicationElementExtension(System.Xml.XmlReader reader) { } + public SyndicationElementExtension(System.Xml.XmlReader xmlReader) { } public string OuterName { get { throw null; } } public string OuterNamespace { get { throw null; } } - public System.Threading.Tasks.Task GetObject() { throw null; } - public System.Threading.Tasks.Task GetObject(System.Runtime.Serialization.XmlObjectSerializer serializer) { throw null; } - public System.Threading.Tasks.Task GetObject(System.Xml.Serialization.XmlSerializer serializer) { throw null; } - public System.Threading.Tasks.Task GetReaderAsync() { throw null; } - public System.Threading.Tasks.Task WriteToAsync(System.Xml.XmlWriter writer) { throw null; } + public TExtension GetObject() { throw null; } + public TExtension GetObject(System.Runtime.Serialization.XmlObjectSerializer serializer) { throw null; } + public TExtension GetObject(System.Xml.Serialization.XmlSerializer serializer) { throw null; } + public System.Xml.XmlReader GetReader() { throw null; } + public void WriteTo(System.Xml.XmlWriter writer) { } } public sealed partial class SyndicationElementExtensionCollection : System.Collections.ObjectModel.Collection { @@ -364,13 +334,13 @@ namespace System.ServiceModel.Syndication public void Add(object xmlSerializerExtension, System.Xml.Serialization.XmlSerializer serializer) { } public void Add(string outerName, string outerNamespace, object dataContractExtension) { } public void Add(string outerName, string outerNamespace, object dataContractExtension, System.Runtime.Serialization.XmlObjectSerializer dataContractSerializer) { } - public void Add(System.Xml.XmlReader reader) { } + public void Add(System.Xml.XmlReader xmlReader) { } protected override void ClearItems() { } - public System.Threading.Tasks.Task GetReaderAtElementExtensions() { throw null; } + public System.Xml.XmlReader GetReaderAtElementExtensions() { throw null; } protected override void InsertItem(int index, System.ServiceModel.Syndication.SyndicationElementExtension item) { } - public System.Threading.Tasks.Task> ReadElementExtensions(string extensionName, string extensionNamespace) { throw null; } - public System.Threading.Tasks.Task> ReadElementExtensions(string extensionName, string extensionNamespace, System.Runtime.Serialization.XmlObjectSerializer serializer) { throw null; } - public System.Threading.Tasks.Task> ReadElementExtensions(string extensionName, string extensionNamespace, System.Xml.Serialization.XmlSerializer serializer) { throw null; } + public System.Collections.ObjectModel.Collection ReadElementExtensions(string extensionName, string extensionNamespace) { throw null; } + public System.Collections.ObjectModel.Collection ReadElementExtensions(string extensionName, string extensionNamespace, System.Runtime.Serialization.XmlObjectSerializer serializer) { throw null; } + public System.Collections.ObjectModel.Collection ReadElementExtensions(string extensionName, string extensionNamespace, System.Xml.Serialization.XmlSerializer serializer) { throw null; } protected override void RemoveItem(int index) { } protected override void SetItem(int index, System.ServiceModel.Syndication.SyndicationElementExtension item) { } } @@ -390,22 +360,14 @@ namespace System.ServiceModel.Syndication public System.Collections.ObjectModel.Collection Contributors { get { throw null; } } public System.ServiceModel.Syndication.TextSyndicationContent Copyright { get { throw null; } set { } } public System.ServiceModel.Syndication.TextSyndicationContent Description { get { throw null; } set { } } - public System.ServiceModel.Syndication.SyndicationLink Documentation { get { throw null; } set { } } public System.ServiceModel.Syndication.SyndicationElementExtensionCollection ElementExtensions { get { throw null; } } public string Generator { get { throw null; } set { } } - public System.Uri IconImage { get { throw null; } set { } } public string Id { get { throw null; } set { } } - public System.Uri ImageLink { get { throw null; } set { } } - public System.ServiceModel.Syndication.TextSyndicationContent ImageTitle { get { throw null; } set { } } public System.Uri ImageUrl { get { throw null; } set { } } public System.Collections.Generic.IEnumerable Items { get { throw null; } set { } } public string Language { get { throw null; } set { } } public System.DateTimeOffset LastUpdatedTime { get { throw null; } set { } } public System.Collections.ObjectModel.Collection Links { get { throw null; } } - public System.Collections.ObjectModel.Collection SkipDays { get { throw null; } } - public System.Collections.ObjectModel.Collection SkipHours { get { throw null; } } - public System.ServiceModel.Syndication.SyndicationTextInput TextInput { get { throw null; } set { } } - public int TimeToLive { get { throw null; } set { } } public System.ServiceModel.Syndication.TextSyndicationContent Title { get { throw null; } set { } } public virtual System.ServiceModel.Syndication.SyndicationFeed Clone(bool cloneItems) { throw null; } protected internal virtual System.ServiceModel.Syndication.SyndicationCategory CreateCategory() { throw null; } @@ -416,18 +378,13 @@ namespace System.ServiceModel.Syndication public System.ServiceModel.Syndication.Rss20FeedFormatter GetRss20Formatter() { throw null; } public System.ServiceModel.Syndication.Rss20FeedFormatter GetRss20Formatter(bool serializeExtensionsAsAtom) { throw null; } public static System.ServiceModel.Syndication.SyndicationFeed Load(System.Xml.XmlReader reader) { throw null; } - public static System.Threading.Tasks.Task LoadAsync(System.Xml.XmlReader reader, System.ServiceModel.Syndication.Atom10FeedFormatter formatter, System.Threading.CancellationToken ct) { throw null; } - public static System.Threading.Tasks.Task LoadAsync(System.Xml.XmlReader reader, System.ServiceModel.Syndication.Rss20FeedFormatter Rssformatter, System.ServiceModel.Syndication.Atom10FeedFormatter Atomformatter, System.Threading.CancellationToken ct) { throw null; } - public static System.Threading.Tasks.Task LoadAsync(System.Xml.XmlReader reader, System.ServiceModel.Syndication.Rss20FeedFormatter formatter, System.Threading.CancellationToken ct) { throw null; } - public static System.Threading.Tasks.Task LoadAsync(System.Xml.XmlReader reader, System.Threading.CancellationToken ct) { throw null; } - public static System.Threading.Tasks.Task LoadAsync(System.Xml.XmlReader reader, System.Threading.CancellationToken ct) where TSyndicationFeed : System.ServiceModel.Syndication.SyndicationFeed, new() { throw null; } public static TSyndicationFeed Load(System.Xml.XmlReader reader) where TSyndicationFeed : System.ServiceModel.Syndication.SyndicationFeed, new() { throw null; } - public System.Threading.Tasks.Task SaveAsAtom10Async(System.Xml.XmlWriter writer, System.Threading.CancellationToken ct) { throw null; } - public System.Threading.Tasks.Task SaveAsRss20Async(System.Xml.XmlWriter writer, System.Threading.CancellationToken ct) { throw null; } + public void SaveAsAtom10(System.Xml.XmlWriter writer) { } + public void SaveAsRss20(System.Xml.XmlWriter writer) { } protected internal virtual bool TryParseAttribute(string name, string ns, string value, string version) { throw null; } protected internal virtual bool TryParseElement(System.Xml.XmlReader reader, string version) { throw null; } - protected internal virtual System.Threading.Tasks.Task WriteAttributeExtensionsAsync(System.Xml.XmlWriter writer, string version) { throw null; } - protected internal virtual System.Threading.Tasks.Task WriteElementExtensionsAsync(System.Xml.XmlWriter writer, string version) { throw null; } + protected internal virtual void WriteAttributeExtensions(System.Xml.XmlWriter writer, string version) { } + protected internal virtual void WriteElementExtensions(System.Xml.XmlWriter writer, string version) { } } [System.Runtime.Serialization.DataContractAttribute] public abstract partial class SyndicationFeedFormatter @@ -450,7 +407,7 @@ namespace System.ServiceModel.Syndication protected internal static void LoadElementExtensions(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationItem item, int maxExtensionSize) { } protected internal static void LoadElementExtensions(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationLink link, int maxExtensionSize) { } protected internal static void LoadElementExtensions(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationPerson person, int maxExtensionSize) { } - public abstract System.Threading.Tasks.Task ReadFromAsync(System.Xml.XmlReader reader, System.Threading.CancellationToken ct); + public abstract void ReadFrom(System.Xml.XmlReader reader); protected internal virtual void SetFeed(System.ServiceModel.Syndication.SyndicationFeed feed) { } public override string ToString() { throw null; } protected internal static bool TryParseAttribute(string name, string ns, string value, System.ServiceModel.Syndication.SyndicationCategory category, string version) { throw null; } @@ -464,17 +421,17 @@ namespace System.ServiceModel.Syndication protected internal static bool TryParseElement(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationItem item, string version) { throw null; } protected internal static bool TryParseElement(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationLink link, string version) { throw null; } protected internal static bool TryParseElement(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationPerson person, string version) { throw null; } - protected internal static System.Threading.Tasks.Task WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationLink link, string version) { throw null; } - protected internal static System.Threading.Tasks.Task WriteAttributeExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationCategory category, string version) { throw null; } - protected internal static System.Threading.Tasks.Task WriteAttributeExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationFeed feed, string version) { throw null; } - protected internal static System.Threading.Tasks.Task WriteAttributeExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationItem item, string version) { throw null; } - protected internal static System.Threading.Tasks.Task WriteAttributeExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationPerson person, string version) { throw null; } - protected internal static System.Threading.Tasks.Task WriteElementExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationCategory category, string version) { throw null; } - protected internal static System.Threading.Tasks.Task WriteElementExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationFeed feed, string version) { throw null; } - protected internal static System.Threading.Tasks.Task WriteElementExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationItem item, string version) { throw null; } - protected internal static System.Threading.Tasks.Task WriteElementExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationLink link, string version) { throw null; } - protected internal static System.Threading.Tasks.Task WriteElementExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationPerson person, string version) { throw null; } - public abstract System.Threading.Tasks.Task WriteToAsync(System.Xml.XmlWriter writer, System.Threading.CancellationToken ct); + protected internal static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationCategory category, string version) { } + protected internal static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationFeed feed, string version) { } + protected internal static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationItem item, string version) { } + protected internal static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationLink link, string version) { } + protected internal static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationPerson person, string version) { } + protected internal static void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationCategory category, string version) { } + protected internal static void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationFeed feed, string version) { } + protected internal static void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationItem item, string version) { } + protected internal static void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationLink link, string version) { } + protected internal static void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationPerson person, string version) { } + public abstract void WriteTo(System.Xml.XmlWriter writer); } public partial class SyndicationItem { @@ -506,15 +463,15 @@ namespace System.ServiceModel.Syndication public System.ServiceModel.Syndication.Atom10ItemFormatter GetAtom10Formatter() { throw null; } public System.ServiceModel.Syndication.Rss20ItemFormatter GetRss20Formatter() { throw null; } public System.ServiceModel.Syndication.Rss20ItemFormatter GetRss20Formatter(bool serializeExtensionsAsAtom) { throw null; } - public static System.Threading.Tasks.Task LoadAsync(System.Xml.XmlReader reader) { throw null; } - public static System.Threading.Tasks.Task LoadAsync(System.Xml.XmlReader reader) where TSyndicationItem : System.ServiceModel.Syndication.SyndicationItem, new() { throw null; } - public System.Threading.Tasks.Task SaveAsAtom10(System.Xml.XmlWriter writer) { throw null; } - public System.Threading.Tasks.Task SaveAsRss20(System.Xml.XmlWriter writer) { throw null; } + public static System.ServiceModel.Syndication.SyndicationItem Load(System.Xml.XmlReader reader) { throw null; } + public static TSyndicationItem Load(System.Xml.XmlReader reader) where TSyndicationItem : System.ServiceModel.Syndication.SyndicationItem, new() { throw null; } + public void SaveAsAtom10(System.Xml.XmlWriter writer) { } + public void SaveAsRss20(System.Xml.XmlWriter writer) { } protected internal virtual bool TryParseAttribute(string name, string ns, string value, string version) { throw null; } protected internal virtual bool TryParseContent(System.Xml.XmlReader reader, string contentType, string version, out System.ServiceModel.Syndication.SyndicationContent content) { content = default(System.ServiceModel.Syndication.SyndicationContent); throw null; } protected internal virtual bool TryParseElement(System.Xml.XmlReader reader, string version) { throw null; } - protected internal virtual System.Threading.Tasks.Task WriteAttributeExtensionsAsync(System.Xml.XmlWriter writer, string version) { throw null; } - protected internal virtual System.Threading.Tasks.Task WriteElementExtensionsAsync(System.Xml.XmlWriter writer, string version) { throw null; } + protected internal virtual void WriteAttributeExtensions(System.Xml.XmlWriter writer, string version) { } + protected internal virtual void WriteElementExtensions(System.Xml.XmlWriter writer, string version) { } } [System.Runtime.Serialization.DataContractAttribute] public abstract partial class SyndicationItemFormatter @@ -532,7 +489,7 @@ namespace System.ServiceModel.Syndication protected static void LoadElementExtensions(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationItem item, int maxExtensionSize) { } protected static void LoadElementExtensions(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationLink link, int maxExtensionSize) { } protected static void LoadElementExtensions(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationPerson person, int maxExtensionSize) { } - public abstract System.Threading.Tasks.Task ReadFromAsync(System.Xml.XmlReader reader); + public abstract void ReadFrom(System.Xml.XmlReader reader); protected internal virtual void SetItem(System.ServiceModel.Syndication.SyndicationItem item) { } public override string ToString() { throw null; } protected static bool TryParseAttribute(string name, string ns, string value, System.ServiceModel.Syndication.SyndicationCategory category, string version) { throw null; } @@ -544,15 +501,15 @@ namespace System.ServiceModel.Syndication protected static bool TryParseElement(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationItem item, string version) { throw null; } protected static bool TryParseElement(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationLink link, string version) { throw null; } protected static bool TryParseElement(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationPerson person, string version) { throw null; } - protected static System.Threading.Tasks.Task WriteAttributeExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationCategory category, string version) { throw null; } - protected static System.Threading.Tasks.Task WriteAttributeExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationItem item, string version) { throw null; } - protected static System.Threading.Tasks.Task WriteAttributeExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationLink link, string version) { throw null; } - protected static System.Threading.Tasks.Task WriteAttributeExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationPerson person, string version) { throw null; } - protected System.Threading.Tasks.Task WriteElementExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationCategory category, string version) { throw null; } - protected static System.Threading.Tasks.Task WriteElementExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationItem item, string version) { throw null; } - protected System.Threading.Tasks.Task WriteElementExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationLink link, string version) { throw null; } - protected System.Threading.Tasks.Task WriteElementExtensionsAsync(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationPerson person, string version) { throw null; } - public abstract System.Threading.Tasks.Task WriteToAsync(System.Xml.XmlWriter writer); + protected static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationCategory category, string version) { } + protected static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationItem item, string version) { } + protected static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationLink link, string version) { } + protected static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationPerson person, string version) { } + protected void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationCategory category, string version) { } + protected static void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationItem item, string version) { } + protected void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationLink link, string version) { } + protected void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationPerson person, string version) { } + public abstract void WriteTo(System.Xml.XmlWriter writer); } public partial class SyndicationLink { @@ -577,8 +534,8 @@ namespace System.ServiceModel.Syndication public System.Uri GetAbsoluteUri() { throw null; } protected internal virtual bool TryParseAttribute(string name, string ns, string value, string version) { throw null; } protected internal virtual bool TryParseElement(System.Xml.XmlReader reader, string version) { throw null; } - protected internal virtual System.Threading.Tasks.Task WriteAttributeExtensionsAsync(System.Xml.XmlWriter writer, string version) { throw null; } - protected internal virtual System.Threading.Tasks.Task WriteElementExtensionsAsync(System.Xml.XmlWriter writer, string version) { throw null; } + protected internal virtual void WriteAttributeExtensions(System.Xml.XmlWriter writer, string version) { } + protected internal virtual void WriteElementExtensions(System.Xml.XmlWriter writer, string version) { } } public partial class SyndicationPerson { @@ -594,16 +551,8 @@ namespace System.ServiceModel.Syndication public virtual System.ServiceModel.Syndication.SyndicationPerson Clone() { throw null; } protected internal virtual bool TryParseAttribute(string name, string ns, string value, string version) { throw null; } protected internal virtual bool TryParseElement(System.Xml.XmlReader reader, string version) { throw null; } - protected internal virtual System.Threading.Tasks.Task WriteAttributeExtensionsAsync(System.Xml.XmlWriter writer, string version) { throw null; } - protected internal virtual System.Threading.Tasks.Task WriteElementExtensionsAsync(System.Xml.XmlWriter writer, string version) { throw null; } - } - public partial class SyndicationTextInput - { - public string Description; - public System.ServiceModel.Syndication.SyndicationLink link; - public string name; - public string title; - public SyndicationTextInput() { } + protected internal virtual void WriteAttributeExtensions(System.Xml.XmlWriter writer, string version) { } + protected internal virtual void WriteElementExtensions(System.Xml.XmlWriter writer, string version) { } } public static partial class SyndicationVersions { @@ -648,8 +597,8 @@ namespace System.ServiceModel.Syndication protected internal virtual System.ServiceModel.Syndication.ResourceCollectionInfo CreateResourceCollection() { throw null; } protected internal virtual bool TryParseAttribute(string name, string ns, string value, string version) { throw null; } protected internal virtual bool TryParseElement(System.Xml.XmlReader reader, string version) { throw null; } - protected internal virtual System.Threading.Tasks.Task WriteAttributeExtensions(System.Xml.XmlWriter writer, string version) { throw null; } - protected internal virtual System.Threading.Tasks.Task WriteElementExtensionsAsync(System.Xml.XmlWriter writer, string version) { throw null; } + protected internal virtual void WriteAttributeExtensions(System.Xml.XmlWriter writer, string version) { } + protected internal virtual void WriteElementExtensions(System.Xml.XmlWriter writer, string version) { } } public partial class XmlSyndicationContent : System.ServiceModel.Syndication.SyndicationContent { @@ -661,10 +610,10 @@ namespace System.ServiceModel.Syndication public System.ServiceModel.Syndication.SyndicationElementExtension Extension { get { throw null; } } public override string Type { get { throw null; } } public override System.ServiceModel.Syndication.SyndicationContent Clone() { throw null; } - public System.Threading.Tasks.Task GetReaderAtContent() { throw null; } - public System.Threading.Tasks.Task ReadContent() { throw null; } - public System.Threading.Tasks.Task ReadContent(System.Runtime.Serialization.XmlObjectSerializer dataContractSerializer) { throw null; } - public System.Threading.Tasks.Task ReadContent(System.Xml.Serialization.XmlSerializer serializer) { throw null; } + public System.Xml.XmlDictionaryReader GetReaderAtContent() { throw null; } + public TContent ReadContent() { throw null; } + public TContent ReadContent(System.Runtime.Serialization.XmlObjectSerializer dataContractSerializer) { throw null; } + public TContent ReadContent(System.Xml.Serialization.XmlSerializer serializer) { throw null; } protected override void WriteContentsTo(System.Xml.XmlWriter writer) { } } } diff --git a/external/corefx/src/System.ServiceModel.Syndication/ref/System.ServiceModel.Syndication.csproj b/external/corefx/src/System.ServiceModel.Syndication/ref/System.ServiceModel.Syndication.csproj index 4b651d43f7..99991de4b5 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/ref/System.ServiceModel.Syndication.csproj +++ b/external/corefx/src/System.ServiceModel.Syndication/ref/System.ServiceModel.Syndication.csproj @@ -3,23 +3,31 @@ {E81F4C7C-2000-4449-BEE6-B2E84DE218F7} - true + + - + - - + + + + + + + + + + - diff --git a/external/corefx/src/System.ServiceModel.Syndication/ref/System.ServiceModel.Syndication.netcoreapp.cs b/external/corefx/src/System.ServiceModel.Syndication/ref/System.ServiceModel.Syndication.netcoreapp.cs new file mode 100644 index 0000000000..f5b16b6138 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/ref/System.ServiceModel.Syndication.netcoreapp.cs @@ -0,0 +1,14 @@ +// 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. +// ------------------------------------------------------------------------------ +// Changes to this file must follow the http://aka.ms/api-review process. +// ------------------------------------------------------------------------------ + +namespace System.ServiceModel.Syndication +{ + public abstract partial class SyndicationFeedFormatter + { + + } +} diff --git a/external/corefx/src/System.ServiceModel.Syndication/ref/System.ServiceModel.Syndication.netfx.cs b/external/corefx/src/System.ServiceModel.Syndication/ref/System.ServiceModel.Syndication.netfx.cs deleted file mode 100644 index e463c141e8..0000000000 --- a/external/corefx/src/System.ServiceModel.Syndication/ref/System.ServiceModel.Syndication.netfx.cs +++ /dev/null @@ -1,619 +0,0 @@ -// 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. -// ------------------------------------------------------------------------------ -// Changes to this file must follow the http://aka.ms/api-review process. -// ------------------------------------------------------------------------------ - -namespace System.ServiceModel.Syndication -{ - public partial class Atom10FeedFormatter : System.ServiceModel.Syndication.SyndicationFeedFormatter, System.Xml.Serialization.IXmlSerializable - { - public Atom10FeedFormatter() { } - public Atom10FeedFormatter(System.ServiceModel.Syndication.SyndicationFeed feedToWrite) { } - public Atom10FeedFormatter(System.Type feedTypeToCreate) { } - protected System.Type FeedType { get { throw null; } } - public bool PreserveAttributeExtensions { get { throw null; } set { } } - public bool PreserveElementExtensions { get { throw null; } set { } } - public override string Version { get { throw null; } } - public override bool CanRead(System.Xml.XmlReader reader) { throw null; } - protected override System.ServiceModel.Syndication.SyndicationFeed CreateFeedInstance() { throw null; } - public override void ReadFrom(System.Xml.XmlReader reader) { } - protected virtual System.ServiceModel.Syndication.SyndicationItem ReadItem(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationFeed feed) { throw null; } - protected virtual System.Collections.Generic.IEnumerable ReadItems(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationFeed feed, out bool areAllItemsRead) { areAllItemsRead = default(bool); throw null; } - System.Xml.Schema.XmlSchema System.Xml.Serialization.IXmlSerializable.GetSchema() { throw null; } - void System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader reader) { } - void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { } - protected virtual void WriteItem(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationItem item, System.Uri feedBaseUri) { } - protected virtual void WriteItems(System.Xml.XmlWriter writer, System.Collections.Generic.IEnumerable items, System.Uri feedBaseUri) { } - public override void WriteTo(System.Xml.XmlWriter writer) { } - } - public partial class Atom10FeedFormatter : System.ServiceModel.Syndication.Atom10FeedFormatter where TSyndicationFeed : System.ServiceModel.Syndication.SyndicationFeed, new() - { - public Atom10FeedFormatter() { } - public Atom10FeedFormatter(TSyndicationFeed feedToWrite) { } - protected override System.ServiceModel.Syndication.SyndicationFeed CreateFeedInstance() { throw null; } - } - public partial class Atom10ItemFormatter : System.ServiceModel.Syndication.SyndicationItemFormatter, System.Xml.Serialization.IXmlSerializable - { - public Atom10ItemFormatter() { } - public Atom10ItemFormatter(System.ServiceModel.Syndication.SyndicationItem itemToWrite) { } - public Atom10ItemFormatter(System.Type itemTypeToCreate) { } - protected System.Type ItemType { get { throw null; } } - public bool PreserveAttributeExtensions { get { throw null; } set { } } - public bool PreserveElementExtensions { get { throw null; } set { } } - public override string Version { get { throw null; } } - public override bool CanRead(System.Xml.XmlReader reader) { throw null; } - protected override System.ServiceModel.Syndication.SyndicationItem CreateItemInstance() { throw null; } - public override void ReadFrom(System.Xml.XmlReader reader) { } - System.Xml.Schema.XmlSchema System.Xml.Serialization.IXmlSerializable.GetSchema() { throw null; } - void System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader reader) { } - void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { } - public override void WriteTo(System.Xml.XmlWriter writer) { } - } - public partial class Atom10ItemFormatter : System.ServiceModel.Syndication.Atom10ItemFormatter where TSyndicationItem : System.ServiceModel.Syndication.SyndicationItem, new() - { - public Atom10ItemFormatter() { } - public Atom10ItemFormatter(TSyndicationItem itemToWrite) { } - protected override System.ServiceModel.Syndication.SyndicationItem CreateItemInstance() { throw null; } - } - public partial class AtomPub10CategoriesDocumentFormatter : System.ServiceModel.Syndication.CategoriesDocumentFormatter, System.Xml.Serialization.IXmlSerializable - { - public AtomPub10CategoriesDocumentFormatter() { } - public AtomPub10CategoriesDocumentFormatter(System.ServiceModel.Syndication.CategoriesDocument documentToWrite) { } - public AtomPub10CategoriesDocumentFormatter(System.Type inlineDocumentType, System.Type referencedDocumentType) { } - public override string Version { get { throw null; } } - public override bool CanRead(System.Xml.XmlReader reader) { throw null; } - protected override System.ServiceModel.Syndication.InlineCategoriesDocument CreateInlineCategoriesDocument() { throw null; } - protected override System.ServiceModel.Syndication.ReferencedCategoriesDocument CreateReferencedCategoriesDocument() { throw null; } - public override void ReadFrom(System.Xml.XmlReader reader) { } - System.Xml.Schema.XmlSchema System.Xml.Serialization.IXmlSerializable.GetSchema() { throw null; } - void System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader reader) { } - void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { } - public override void WriteTo(System.Xml.XmlWriter writer) { } - } - public partial class AtomPub10ServiceDocumentFormatter : System.ServiceModel.Syndication.ServiceDocumentFormatter, System.Xml.Serialization.IXmlSerializable - { - public AtomPub10ServiceDocumentFormatter() { } - public AtomPub10ServiceDocumentFormatter(System.ServiceModel.Syndication.ServiceDocument documentToWrite) { } - public AtomPub10ServiceDocumentFormatter(System.Type documentTypeToCreate) { } - public override string Version { get { throw null; } } - public override bool CanRead(System.Xml.XmlReader reader) { throw null; } - protected override System.ServiceModel.Syndication.ServiceDocument CreateDocumentInstance() { throw null; } - public override void ReadFrom(System.Xml.XmlReader reader) { } - System.Xml.Schema.XmlSchema System.Xml.Serialization.IXmlSerializable.GetSchema() { throw null; } - void System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader reader) { } - void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { } - public override void WriteTo(System.Xml.XmlWriter writer) { } - } - public partial class AtomPub10ServiceDocumentFormatter : System.ServiceModel.Syndication.AtomPub10ServiceDocumentFormatter where TServiceDocument : System.ServiceModel.Syndication.ServiceDocument, new() - { - public AtomPub10ServiceDocumentFormatter() { } - public AtomPub10ServiceDocumentFormatter(TServiceDocument documentToWrite) { } - protected override System.ServiceModel.Syndication.ServiceDocument CreateDocumentInstance() { throw null; } - } - public abstract partial class CategoriesDocument - { - internal CategoriesDocument() { } - public System.Collections.Generic.Dictionary AttributeExtensions { get { throw null; } } - public System.Uri BaseUri { get { throw null; } set { } } - public System.ServiceModel.Syndication.SyndicationElementExtensionCollection ElementExtensions { get { throw null; } } - public string Language { get { throw null; } set { } } - public static System.ServiceModel.Syndication.InlineCategoriesDocument Create(System.Collections.ObjectModel.Collection categories) { throw null; } - public static System.ServiceModel.Syndication.InlineCategoriesDocument Create(System.Collections.ObjectModel.Collection categories, bool isFixed, string scheme) { throw null; } - public static System.ServiceModel.Syndication.ReferencedCategoriesDocument Create(System.Uri linkToCategoriesDocument) { throw null; } - public System.ServiceModel.Syndication.CategoriesDocumentFormatter GetFormatter() { throw null; } - public static System.ServiceModel.Syndication.CategoriesDocument Load(System.Xml.XmlReader reader) { throw null; } - public void Save(System.Xml.XmlWriter writer) { } - protected internal virtual bool TryParseAttribute(string name, string ns, string value, string version) { throw null; } - protected internal virtual bool TryParseElement(System.Xml.XmlReader reader, string version) { throw null; } - protected internal virtual void WriteAttributeExtensions(System.Xml.XmlWriter writer, string version) { } - protected internal virtual void WriteElementExtensions(System.Xml.XmlWriter writer, string version) { } - } - [System.Runtime.Serialization.DataContractAttribute] - public abstract partial class CategoriesDocumentFormatter - { - protected CategoriesDocumentFormatter() { } - protected CategoriesDocumentFormatter(System.ServiceModel.Syndication.CategoriesDocument documentToWrite) { } - public System.ServiceModel.Syndication.CategoriesDocument Document { get { throw null; } } - public abstract string Version { get; } - public abstract bool CanRead(System.Xml.XmlReader reader); - protected virtual System.ServiceModel.Syndication.InlineCategoriesDocument CreateInlineCategoriesDocument() { throw null; } - protected virtual System.ServiceModel.Syndication.ReferencedCategoriesDocument CreateReferencedCategoriesDocument() { throw null; } - public abstract void ReadFrom(System.Xml.XmlReader reader); - protected virtual void SetDocument(System.ServiceModel.Syndication.CategoriesDocument document) { } - public abstract void WriteTo(System.Xml.XmlWriter writer); - } - public partial class InlineCategoriesDocument : System.ServiceModel.Syndication.CategoriesDocument - { - public InlineCategoriesDocument() { } - public InlineCategoriesDocument(System.Collections.Generic.IEnumerable categories) { } - public InlineCategoriesDocument(System.Collections.Generic.IEnumerable categories, bool isFixed, string scheme) { } - public System.Collections.ObjectModel.Collection Categories { get { throw null; } } - public bool IsFixed { get { throw null; } set { } } - public string Scheme { get { throw null; } set { } } - protected internal virtual System.ServiceModel.Syndication.SyndicationCategory CreateCategory() { throw null; } - } - public partial class ReferencedCategoriesDocument : System.ServiceModel.Syndication.CategoriesDocument - { - public ReferencedCategoriesDocument() { } - public ReferencedCategoriesDocument(System.Uri link) { } - public System.Uri Link { get { throw null; } set { } } - } - public partial class ResourceCollectionInfo - { - public ResourceCollectionInfo() { } - public ResourceCollectionInfo(System.ServiceModel.Syndication.TextSyndicationContent title, System.Uri link) { } - public ResourceCollectionInfo(System.ServiceModel.Syndication.TextSyndicationContent title, System.Uri link, System.Collections.Generic.IEnumerable categories, bool allowsNewEntries) { } - public ResourceCollectionInfo(System.ServiceModel.Syndication.TextSyndicationContent title, System.Uri link, System.Collections.Generic.IEnumerable categories, System.Collections.Generic.IEnumerable accepts) { } - public ResourceCollectionInfo(string title, System.Uri link) { } - public System.Collections.ObjectModel.Collection Accepts { get { throw null; } } - public System.Collections.Generic.Dictionary AttributeExtensions { get { throw null; } } - public System.Uri BaseUri { get { throw null; } set { } } - public System.Collections.ObjectModel.Collection Categories { get { throw null; } } - public System.ServiceModel.Syndication.SyndicationElementExtensionCollection ElementExtensions { get { throw null; } } - public System.Uri Link { get { throw null; } set { } } - public System.ServiceModel.Syndication.TextSyndicationContent Title { get { throw null; } set { } } - protected internal virtual System.ServiceModel.Syndication.InlineCategoriesDocument CreateInlineCategoriesDocument() { throw null; } - protected internal virtual System.ServiceModel.Syndication.ReferencedCategoriesDocument CreateReferencedCategoriesDocument() { throw null; } - protected internal virtual bool TryParseAttribute(string name, string ns, string value, string version) { throw null; } - protected internal virtual bool TryParseElement(System.Xml.XmlReader reader, string version) { throw null; } - protected internal virtual void WriteAttributeExtensions(System.Xml.XmlWriter writer, string version) { } - protected internal virtual void WriteElementExtensions(System.Xml.XmlWriter writer, string version) { } - } - public partial class Rss20FeedFormatter : System.ServiceModel.Syndication.SyndicationFeedFormatter, System.Xml.Serialization.IXmlSerializable - { - public Rss20FeedFormatter() { } - public Rss20FeedFormatter(System.ServiceModel.Syndication.SyndicationFeed feedToWrite) { } - public Rss20FeedFormatter(System.ServiceModel.Syndication.SyndicationFeed feedToWrite, bool serializeExtensionsAsAtom) { } - public Rss20FeedFormatter(System.Type feedTypeToCreate) { } - protected System.Type FeedType { get { throw null; } } - public bool PreserveAttributeExtensions { get { throw null; } set { } } - public bool PreserveElementExtensions { get { throw null; } set { } } - public bool SerializeExtensionsAsAtom { get { throw null; } set { } } - public override string Version { get { throw null; } } - public override bool CanRead(System.Xml.XmlReader reader) { throw null; } - protected override System.ServiceModel.Syndication.SyndicationFeed CreateFeedInstance() { throw null; } - public override void ReadFrom(System.Xml.XmlReader reader) { } - protected virtual System.ServiceModel.Syndication.SyndicationItem ReadItem(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationFeed feed) { throw null; } - protected virtual System.Collections.Generic.IEnumerable ReadItems(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationFeed feed, out bool areAllItemsRead) { areAllItemsRead = default(bool); throw null; } - protected internal override void SetFeed(System.ServiceModel.Syndication.SyndicationFeed feed) { } - System.Xml.Schema.XmlSchema System.Xml.Serialization.IXmlSerializable.GetSchema() { throw null; } - void System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader reader) { } - void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { } - protected virtual void WriteItem(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationItem item, System.Uri feedBaseUri) { } - protected virtual void WriteItems(System.Xml.XmlWriter writer, System.Collections.Generic.IEnumerable items, System.Uri feedBaseUri) { } - public override void WriteTo(System.Xml.XmlWriter writer) { } - } - public partial class Rss20FeedFormatter : System.ServiceModel.Syndication.Rss20FeedFormatter where TSyndicationFeed : System.ServiceModel.Syndication.SyndicationFeed, new() - { - public Rss20FeedFormatter() { } - public Rss20FeedFormatter(TSyndicationFeed feedToWrite) { } - public Rss20FeedFormatter(TSyndicationFeed feedToWrite, bool serializeExtensionsAsAtom) { } - protected override System.ServiceModel.Syndication.SyndicationFeed CreateFeedInstance() { throw null; } - } - public partial class Rss20ItemFormatter : System.ServiceModel.Syndication.SyndicationItemFormatter, System.Xml.Serialization.IXmlSerializable - { - public Rss20ItemFormatter() { } - public Rss20ItemFormatter(System.ServiceModel.Syndication.SyndicationItem itemToWrite) { } - public Rss20ItemFormatter(System.ServiceModel.Syndication.SyndicationItem itemToWrite, bool serializeExtensionsAsAtom) { } - public Rss20ItemFormatter(System.Type itemTypeToCreate) { } - protected System.Type ItemType { get { throw null; } } - public bool PreserveAttributeExtensions { get { throw null; } set { } } - public bool PreserveElementExtensions { get { throw null; } set { } } - public bool SerializeExtensionsAsAtom { get { throw null; } set { } } - public override string Version { get { throw null; } } - public override bool CanRead(System.Xml.XmlReader reader) { throw null; } - protected override System.ServiceModel.Syndication.SyndicationItem CreateItemInstance() { throw null; } - public override void ReadFrom(System.Xml.XmlReader reader) { } - System.Xml.Schema.XmlSchema System.Xml.Serialization.IXmlSerializable.GetSchema() { throw null; } - void System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader reader) { } - void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { } - public override void WriteTo(System.Xml.XmlWriter writer) { } - } - public partial class Rss20ItemFormatter : System.ServiceModel.Syndication.Rss20ItemFormatter, System.Xml.Serialization.IXmlSerializable where TSyndicationItem : System.ServiceModel.Syndication.SyndicationItem, new() - { - public Rss20ItemFormatter() { } - public Rss20ItemFormatter(TSyndicationItem itemToWrite) { } - public Rss20ItemFormatter(TSyndicationItem itemToWrite, bool serializeExtensionsAsAtom) { } - protected override System.ServiceModel.Syndication.SyndicationItem CreateItemInstance() { throw null; } - } - public partial class ServiceDocument - { - public ServiceDocument() { } - public ServiceDocument(System.Collections.Generic.IEnumerable workspaces) { } - public System.Collections.Generic.Dictionary AttributeExtensions { get { throw null; } } - public System.Uri BaseUri { get { throw null; } set { } } - public System.ServiceModel.Syndication.SyndicationElementExtensionCollection ElementExtensions { get { throw null; } } - public string Language { get { throw null; } set { } } - public System.Collections.ObjectModel.Collection Workspaces { get { throw null; } } - protected internal virtual System.ServiceModel.Syndication.Workspace CreateWorkspace() { throw null; } - public System.ServiceModel.Syndication.ServiceDocumentFormatter GetFormatter() { throw null; } - public static System.ServiceModel.Syndication.ServiceDocument Load(System.Xml.XmlReader reader) { throw null; } - public static TServiceDocument Load(System.Xml.XmlReader reader) where TServiceDocument : System.ServiceModel.Syndication.ServiceDocument, new() { throw null; } - public void Save(System.Xml.XmlWriter writer) { } - protected internal virtual bool TryParseAttribute(string name, string ns, string value, string version) { throw null; } - protected internal virtual bool TryParseElement(System.Xml.XmlReader reader, string version) { throw null; } - protected internal virtual void WriteAttributeExtensions(System.Xml.XmlWriter writer, string version) { } - protected internal virtual void WriteElementExtensions(System.Xml.XmlWriter writer, string version) { } - } - [System.Runtime.Serialization.DataContractAttribute] - public abstract partial class ServiceDocumentFormatter - { - protected ServiceDocumentFormatter() { } - protected ServiceDocumentFormatter(System.ServiceModel.Syndication.ServiceDocument documentToWrite) { } - public System.ServiceModel.Syndication.ServiceDocument Document { get { throw null; } } - public abstract string Version { get; } - public abstract bool CanRead(System.Xml.XmlReader reader); - protected static System.ServiceModel.Syndication.SyndicationCategory CreateCategory(System.ServiceModel.Syndication.InlineCategoriesDocument inlineCategories) { throw null; } - protected static System.ServiceModel.Syndication.ResourceCollectionInfo CreateCollection(System.ServiceModel.Syndication.Workspace workspace) { throw null; } - protected virtual System.ServiceModel.Syndication.ServiceDocument CreateDocumentInstance() { throw null; } - protected static System.ServiceModel.Syndication.InlineCategoriesDocument CreateInlineCategories(System.ServiceModel.Syndication.ResourceCollectionInfo collection) { throw null; } - protected static System.ServiceModel.Syndication.ReferencedCategoriesDocument CreateReferencedCategories(System.ServiceModel.Syndication.ResourceCollectionInfo collection) { throw null; } - protected static System.ServiceModel.Syndication.Workspace CreateWorkspace(System.ServiceModel.Syndication.ServiceDocument document) { throw null; } - protected static void LoadElementExtensions(System.Xml.XmlReader reader, System.ServiceModel.Syndication.CategoriesDocument categories, int maxExtensionSize) { } - protected static void LoadElementExtensions(System.Xml.XmlReader reader, System.ServiceModel.Syndication.ResourceCollectionInfo collection, int maxExtensionSize) { } - protected static void LoadElementExtensions(System.Xml.XmlReader reader, System.ServiceModel.Syndication.ServiceDocument document, int maxExtensionSize) { } - protected static void LoadElementExtensions(System.Xml.XmlReader reader, System.ServiceModel.Syndication.Workspace workspace, int maxExtensionSize) { } - public abstract void ReadFrom(System.Xml.XmlReader reader); - protected virtual void SetDocument(System.ServiceModel.Syndication.ServiceDocument document) { } - protected static bool TryParseAttribute(string name, string ns, string value, System.ServiceModel.Syndication.CategoriesDocument categories, string version) { throw null; } - protected static bool TryParseAttribute(string name, string ns, string value, System.ServiceModel.Syndication.ResourceCollectionInfo collection, string version) { throw null; } - protected static bool TryParseAttribute(string name, string ns, string value, System.ServiceModel.Syndication.ServiceDocument document, string version) { throw null; } - protected static bool TryParseAttribute(string name, string ns, string value, System.ServiceModel.Syndication.Workspace workspace, string version) { throw null; } - protected static bool TryParseElement(System.Xml.XmlReader reader, System.ServiceModel.Syndication.CategoriesDocument categories, string version) { throw null; } - protected static bool TryParseElement(System.Xml.XmlReader reader, System.ServiceModel.Syndication.ResourceCollectionInfo collection, string version) { throw null; } - protected static bool TryParseElement(System.Xml.XmlReader reader, System.ServiceModel.Syndication.ServiceDocument document, string version) { throw null; } - protected static bool TryParseElement(System.Xml.XmlReader reader, System.ServiceModel.Syndication.Workspace workspace, string version) { throw null; } - protected static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.CategoriesDocument categories, string version) { } - protected static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.ResourceCollectionInfo collection, string version) { } - protected static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.ServiceDocument document, string version) { } - protected static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.Workspace workspace, string version) { } - protected static void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.CategoriesDocument categories, string version) { } - protected static void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.ResourceCollectionInfo collection, string version) { } - protected static void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.ServiceDocument document, string version) { } - protected static void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.Workspace workspace, string version) { } - public abstract void WriteTo(System.Xml.XmlWriter writer); - } - public partial class SyndicationCategory - { - public SyndicationCategory() { } - protected SyndicationCategory(System.ServiceModel.Syndication.SyndicationCategory source) { } - public SyndicationCategory(string name) { } - public SyndicationCategory(string name, string scheme, string label) { } - public System.Collections.Generic.Dictionary AttributeExtensions { get { throw null; } } - public System.ServiceModel.Syndication.SyndicationElementExtensionCollection ElementExtensions { get { throw null; } } - public string Label { get { throw null; } set { } } - public string Name { get { throw null; } set { } } - public string Scheme { get { throw null; } set { } } - public virtual System.ServiceModel.Syndication.SyndicationCategory Clone() { throw null; } - protected internal virtual bool TryParseAttribute(string name, string ns, string value, string version) { throw null; } - protected internal virtual bool TryParseElement(System.Xml.XmlReader reader, string version) { throw null; } - protected internal virtual void WriteAttributeExtensions(System.Xml.XmlWriter writer, string version) { } - protected internal virtual void WriteElementExtensions(System.Xml.XmlWriter writer, string version) { } - } - public abstract partial class SyndicationContent - { - protected SyndicationContent() { } - protected SyndicationContent(System.ServiceModel.Syndication.SyndicationContent source) { } - public System.Collections.Generic.Dictionary AttributeExtensions { get { throw null; } } - public abstract string Type { get; } - public abstract System.ServiceModel.Syndication.SyndicationContent Clone(); - public static System.ServiceModel.Syndication.TextSyndicationContent CreateHtmlContent(string content) { throw null; } - public static System.ServiceModel.Syndication.TextSyndicationContent CreatePlaintextContent(string content) { throw null; } - public static System.ServiceModel.Syndication.UrlSyndicationContent CreateUrlContent(System.Uri url, string mediaType) { throw null; } - public static System.ServiceModel.Syndication.TextSyndicationContent CreateXhtmlContent(string content) { throw null; } - public static System.ServiceModel.Syndication.XmlSyndicationContent CreateXmlContent(object dataContractObject) { throw null; } - public static System.ServiceModel.Syndication.XmlSyndicationContent CreateXmlContent(object dataContractObject, System.Runtime.Serialization.XmlObjectSerializer dataContractSerializer) { throw null; } - public static System.ServiceModel.Syndication.XmlSyndicationContent CreateXmlContent(object xmlSerializerObject, System.Xml.Serialization.XmlSerializer serializer) { throw null; } - public static System.ServiceModel.Syndication.XmlSyndicationContent CreateXmlContent(System.Xml.XmlReader xmlReader) { throw null; } - protected abstract void WriteContentsTo(System.Xml.XmlWriter writer); - public void WriteTo(System.Xml.XmlWriter writer, string outerElementName, string outerElementNamespace) { } - } - public partial class SyndicationElementExtension - { - public SyndicationElementExtension(object dataContractExtension) { } - public SyndicationElementExtension(object dataContractExtension, System.Runtime.Serialization.XmlObjectSerializer dataContractSerializer) { } - public SyndicationElementExtension(object xmlSerializerExtension, System.Xml.Serialization.XmlSerializer serializer) { } - public SyndicationElementExtension(string outerName, string outerNamespace, object dataContractExtension) { } - public SyndicationElementExtension(string outerName, string outerNamespace, object dataContractExtension, System.Runtime.Serialization.XmlObjectSerializer dataContractSerializer) { } - public SyndicationElementExtension(System.Xml.XmlReader xmlReader) { } - public string OuterName { get { throw null; } } - public string OuterNamespace { get { throw null; } } - public TExtension GetObject() { throw null; } - public TExtension GetObject(System.Runtime.Serialization.XmlObjectSerializer serializer) { throw null; } - public TExtension GetObject(System.Xml.Serialization.XmlSerializer serializer) { throw null; } - public System.Xml.XmlReader GetReader() { throw null; } - public void WriteTo(System.Xml.XmlWriter writer) { } - } - public sealed partial class SyndicationElementExtensionCollection : System.Collections.ObjectModel.Collection - { - internal SyndicationElementExtensionCollection() { } - public void Add(object extension) { } - public void Add(object dataContractExtension, System.Runtime.Serialization.DataContractSerializer serializer) { } - public void Add(object xmlSerializerExtension, System.Xml.Serialization.XmlSerializer serializer) { } - public void Add(string outerName, string outerNamespace, object dataContractExtension) { } - public void Add(string outerName, string outerNamespace, object dataContractExtension, System.Runtime.Serialization.XmlObjectSerializer dataContractSerializer) { } - public void Add(System.Xml.XmlReader xmlReader) { } - protected override void ClearItems() { } - public System.Xml.XmlReader GetReaderAtElementExtensions() { throw null; } - protected override void InsertItem(int index, System.ServiceModel.Syndication.SyndicationElementExtension item) { } - public System.Collections.ObjectModel.Collection ReadElementExtensions(string extensionName, string extensionNamespace) { throw null; } - public System.Collections.ObjectModel.Collection ReadElementExtensions(string extensionName, string extensionNamespace, System.Runtime.Serialization.XmlObjectSerializer serializer) { throw null; } - public System.Collections.ObjectModel.Collection ReadElementExtensions(string extensionName, string extensionNamespace, System.Xml.Serialization.XmlSerializer serializer) { throw null; } - protected override void RemoveItem(int index) { } - protected override void SetItem(int index, System.ServiceModel.Syndication.SyndicationElementExtension item) { } - } - public partial class SyndicationFeed - { - public SyndicationFeed() { } - public SyndicationFeed(System.Collections.Generic.IEnumerable items) { } - protected SyndicationFeed(System.ServiceModel.Syndication.SyndicationFeed source, bool cloneItems) { } - public SyndicationFeed(string title, string description, System.Uri feedAlternateLink) { } - public SyndicationFeed(string title, string description, System.Uri feedAlternateLink, System.Collections.Generic.IEnumerable items) { } - public SyndicationFeed(string title, string description, System.Uri feedAlternateLink, string id, System.DateTimeOffset lastUpdatedTime) { } - public SyndicationFeed(string title, string description, System.Uri feedAlternateLink, string id, System.DateTimeOffset lastUpdatedTime, System.Collections.Generic.IEnumerable items) { } - public System.Collections.Generic.Dictionary AttributeExtensions { get { throw null; } } - public System.Collections.ObjectModel.Collection Authors { get { throw null; } } - public System.Uri BaseUri { get { throw null; } set { } } - public System.Collections.ObjectModel.Collection Categories { get { throw null; } } - public System.Collections.ObjectModel.Collection Contributors { get { throw null; } } - public System.ServiceModel.Syndication.TextSyndicationContent Copyright { get { throw null; } set { } } - public System.ServiceModel.Syndication.TextSyndicationContent Description { get { throw null; } set { } } - public System.ServiceModel.Syndication.SyndicationElementExtensionCollection ElementExtensions { get { throw null; } } - public string Generator { get { throw null; } set { } } - public string Id { get { throw null; } set { } } - public System.Uri ImageUrl { get { throw null; } set { } } - public System.Collections.Generic.IEnumerable Items { get { throw null; } set { } } - public string Language { get { throw null; } set { } } - public System.DateTimeOffset LastUpdatedTime { get { throw null; } set { } } - public System.Collections.ObjectModel.Collection Links { get { throw null; } } - public System.ServiceModel.Syndication.TextSyndicationContent Title { get { throw null; } set { } } - public virtual System.ServiceModel.Syndication.SyndicationFeed Clone(bool cloneItems) { throw null; } - protected internal virtual System.ServiceModel.Syndication.SyndicationCategory CreateCategory() { throw null; } - protected internal virtual System.ServiceModel.Syndication.SyndicationItem CreateItem() { throw null; } - protected internal virtual System.ServiceModel.Syndication.SyndicationLink CreateLink() { throw null; } - protected internal virtual System.ServiceModel.Syndication.SyndicationPerson CreatePerson() { throw null; } - public System.ServiceModel.Syndication.Atom10FeedFormatter GetAtom10Formatter() { throw null; } - public System.ServiceModel.Syndication.Rss20FeedFormatter GetRss20Formatter() { throw null; } - public System.ServiceModel.Syndication.Rss20FeedFormatter GetRss20Formatter(bool serializeExtensionsAsAtom) { throw null; } - public static System.ServiceModel.Syndication.SyndicationFeed Load(System.Xml.XmlReader reader) { throw null; } - public static TSyndicationFeed Load(System.Xml.XmlReader reader) where TSyndicationFeed : System.ServiceModel.Syndication.SyndicationFeed, new() { throw null; } - public void SaveAsAtom10(System.Xml.XmlWriter writer) { } - public void SaveAsRss20(System.Xml.XmlWriter writer) { } - protected internal virtual bool TryParseAttribute(string name, string ns, string value, string version) { throw null; } - protected internal virtual bool TryParseElement(System.Xml.XmlReader reader, string version) { throw null; } - protected internal virtual void WriteAttributeExtensions(System.Xml.XmlWriter writer, string version) { } - protected internal virtual void WriteElementExtensions(System.Xml.XmlWriter writer, string version) { } - } - [System.Runtime.Serialization.DataContractAttribute] - public abstract partial class SyndicationFeedFormatter - { - protected SyndicationFeedFormatter() { } - protected SyndicationFeedFormatter(System.ServiceModel.Syndication.SyndicationFeed feedToWrite) { } - public System.ServiceModel.Syndication.SyndicationFeed Feed { get { throw null; } } - public abstract string Version { get; } - public abstract bool CanRead(System.Xml.XmlReader reader); - protected internal static System.ServiceModel.Syndication.SyndicationCategory CreateCategory(System.ServiceModel.Syndication.SyndicationFeed feed) { throw null; } - protected internal static System.ServiceModel.Syndication.SyndicationCategory CreateCategory(System.ServiceModel.Syndication.SyndicationItem item) { throw null; } - protected abstract System.ServiceModel.Syndication.SyndicationFeed CreateFeedInstance(); - protected internal static System.ServiceModel.Syndication.SyndicationItem CreateItem(System.ServiceModel.Syndication.SyndicationFeed feed) { throw null; } - protected internal static System.ServiceModel.Syndication.SyndicationLink CreateLink(System.ServiceModel.Syndication.SyndicationFeed feed) { throw null; } - protected internal static System.ServiceModel.Syndication.SyndicationLink CreateLink(System.ServiceModel.Syndication.SyndicationItem item) { throw null; } - protected internal static System.ServiceModel.Syndication.SyndicationPerson CreatePerson(System.ServiceModel.Syndication.SyndicationFeed feed) { throw null; } - protected internal static System.ServiceModel.Syndication.SyndicationPerson CreatePerson(System.ServiceModel.Syndication.SyndicationItem item) { throw null; } - protected internal static void LoadElementExtensions(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationCategory category, int maxExtensionSize) { } - protected internal static void LoadElementExtensions(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationFeed feed, int maxExtensionSize) { } - protected internal static void LoadElementExtensions(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationItem item, int maxExtensionSize) { } - protected internal static void LoadElementExtensions(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationLink link, int maxExtensionSize) { } - protected internal static void LoadElementExtensions(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationPerson person, int maxExtensionSize) { } - public abstract void ReadFrom(System.Xml.XmlReader reader); - protected internal virtual void SetFeed(System.ServiceModel.Syndication.SyndicationFeed feed) { } - public override string ToString() { throw null; } - protected internal static bool TryParseAttribute(string name, string ns, string value, System.ServiceModel.Syndication.SyndicationCategory category, string version) { throw null; } - protected internal static bool TryParseAttribute(string name, string ns, string value, System.ServiceModel.Syndication.SyndicationFeed feed, string version) { throw null; } - protected internal static bool TryParseAttribute(string name, string ns, string value, System.ServiceModel.Syndication.SyndicationItem item, string version) { throw null; } - protected internal static bool TryParseAttribute(string name, string ns, string value, System.ServiceModel.Syndication.SyndicationLink link, string version) { throw null; } - protected internal static bool TryParseAttribute(string name, string ns, string value, System.ServiceModel.Syndication.SyndicationPerson person, string version) { throw null; } - protected internal static bool TryParseContent(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationItem item, string contentType, string version, out System.ServiceModel.Syndication.SyndicationContent content) { content = default(System.ServiceModel.Syndication.SyndicationContent); throw null; } - protected internal static bool TryParseElement(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationCategory category, string version) { throw null; } - protected internal static bool TryParseElement(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationFeed feed, string version) { throw null; } - protected internal static bool TryParseElement(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationItem item, string version) { throw null; } - protected internal static bool TryParseElement(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationLink link, string version) { throw null; } - protected internal static bool TryParseElement(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationPerson person, string version) { throw null; } - protected internal static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationCategory category, string version) { } - protected internal static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationFeed feed, string version) { } - protected internal static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationItem item, string version) { } - protected internal static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationLink link, string version) { } - protected internal static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationPerson person, string version) { } - protected internal static void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationCategory category, string version) { } - protected internal static void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationFeed feed, string version) { } - protected internal static void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationItem item, string version) { } - protected internal static void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationLink link, string version) { } - protected internal static void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationPerson person, string version) { } - public abstract void WriteTo(System.Xml.XmlWriter writer); - } - public partial class SyndicationItem - { - public SyndicationItem() { } - protected SyndicationItem(System.ServiceModel.Syndication.SyndicationItem source) { } - public SyndicationItem(string title, System.ServiceModel.Syndication.SyndicationContent content, System.Uri itemAlternateLink, string id, System.DateTimeOffset lastUpdatedTime) { } - public SyndicationItem(string title, string content, System.Uri itemAlternateLink) { } - public SyndicationItem(string title, string content, System.Uri itemAlternateLink, string id, System.DateTimeOffset lastUpdatedTime) { } - public System.Collections.Generic.Dictionary AttributeExtensions { get { throw null; } } - public System.Collections.ObjectModel.Collection Authors { get { throw null; } } - public System.Uri BaseUri { get { throw null; } set { } } - public System.Collections.ObjectModel.Collection Categories { get { throw null; } } - public System.ServiceModel.Syndication.SyndicationContent Content { get { throw null; } set { } } - public System.Collections.ObjectModel.Collection Contributors { get { throw null; } } - public System.ServiceModel.Syndication.TextSyndicationContent Copyright { get { throw null; } set { } } - public System.ServiceModel.Syndication.SyndicationElementExtensionCollection ElementExtensions { get { throw null; } } - public string Id { get { throw null; } set { } } - public System.DateTimeOffset LastUpdatedTime { get { throw null; } set { } } - public System.Collections.ObjectModel.Collection Links { get { throw null; } } - public System.DateTimeOffset PublishDate { get { throw null; } set { } } - public System.ServiceModel.Syndication.SyndicationFeed SourceFeed { get { throw null; } set { } } - public System.ServiceModel.Syndication.TextSyndicationContent Summary { get { throw null; } set { } } - public System.ServiceModel.Syndication.TextSyndicationContent Title { get { throw null; } set { } } - public void AddPermalink(System.Uri permalink) { } - public virtual System.ServiceModel.Syndication.SyndicationItem Clone() { throw null; } - protected internal virtual System.ServiceModel.Syndication.SyndicationCategory CreateCategory() { throw null; } - protected internal virtual System.ServiceModel.Syndication.SyndicationLink CreateLink() { throw null; } - protected internal virtual System.ServiceModel.Syndication.SyndicationPerson CreatePerson() { throw null; } - public System.ServiceModel.Syndication.Atom10ItemFormatter GetAtom10Formatter() { throw null; } - public System.ServiceModel.Syndication.Rss20ItemFormatter GetRss20Formatter() { throw null; } - public System.ServiceModel.Syndication.Rss20ItemFormatter GetRss20Formatter(bool serializeExtensionsAsAtom) { throw null; } - public static System.ServiceModel.Syndication.SyndicationItem Load(System.Xml.XmlReader reader) { throw null; } - public static TSyndicationItem Load(System.Xml.XmlReader reader) where TSyndicationItem : System.ServiceModel.Syndication.SyndicationItem, new() { throw null; } - public void SaveAsAtom10(System.Xml.XmlWriter writer) { } - public void SaveAsRss20(System.Xml.XmlWriter writer) { } - protected internal virtual bool TryParseAttribute(string name, string ns, string value, string version) { throw null; } - protected internal virtual bool TryParseContent(System.Xml.XmlReader reader, string contentType, string version, out System.ServiceModel.Syndication.SyndicationContent content) { content = default(System.ServiceModel.Syndication.SyndicationContent); throw null; } - protected internal virtual bool TryParseElement(System.Xml.XmlReader reader, string version) { throw null; } - protected internal virtual void WriteAttributeExtensions(System.Xml.XmlWriter writer, string version) { } - protected internal virtual void WriteElementExtensions(System.Xml.XmlWriter writer, string version) { } - } - [System.Runtime.Serialization.DataContractAttribute] - public abstract partial class SyndicationItemFormatter - { - protected SyndicationItemFormatter() { } - protected SyndicationItemFormatter(System.ServiceModel.Syndication.SyndicationItem itemToWrite) { } - public System.ServiceModel.Syndication.SyndicationItem Item { get { throw null; } } - public abstract string Version { get; } - public abstract bool CanRead(System.Xml.XmlReader reader); - protected static System.ServiceModel.Syndication.SyndicationCategory CreateCategory(System.ServiceModel.Syndication.SyndicationItem item) { throw null; } - protected abstract System.ServiceModel.Syndication.SyndicationItem CreateItemInstance(); - protected static System.ServiceModel.Syndication.SyndicationLink CreateLink(System.ServiceModel.Syndication.SyndicationItem item) { throw null; } - protected static System.ServiceModel.Syndication.SyndicationPerson CreatePerson(System.ServiceModel.Syndication.SyndicationItem item) { throw null; } - protected static void LoadElementExtensions(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationCategory category, int maxExtensionSize) { } - protected static void LoadElementExtensions(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationItem item, int maxExtensionSize) { } - protected static void LoadElementExtensions(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationLink link, int maxExtensionSize) { } - protected static void LoadElementExtensions(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationPerson person, int maxExtensionSize) { } - public abstract void ReadFrom(System.Xml.XmlReader reader); - protected internal virtual void SetItem(System.ServiceModel.Syndication.SyndicationItem item) { } - public override string ToString() { throw null; } - protected static bool TryParseAttribute(string name, string ns, string value, System.ServiceModel.Syndication.SyndicationCategory category, string version) { throw null; } - protected static bool TryParseAttribute(string name, string ns, string value, System.ServiceModel.Syndication.SyndicationItem item, string version) { throw null; } - protected static bool TryParseAttribute(string name, string ns, string value, System.ServiceModel.Syndication.SyndicationLink link, string version) { throw null; } - protected static bool TryParseAttribute(string name, string ns, string value, System.ServiceModel.Syndication.SyndicationPerson person, string version) { throw null; } - protected static bool TryParseContent(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationItem item, string contentType, string version, out System.ServiceModel.Syndication.SyndicationContent content) { content = default(System.ServiceModel.Syndication.SyndicationContent); throw null; } - protected static bool TryParseElement(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationCategory category, string version) { throw null; } - protected static bool TryParseElement(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationItem item, string version) { throw null; } - protected static bool TryParseElement(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationLink link, string version) { throw null; } - protected static bool TryParseElement(System.Xml.XmlReader reader, System.ServiceModel.Syndication.SyndicationPerson person, string version) { throw null; } - protected static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationCategory category, string version) { } - protected static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationItem item, string version) { } - protected static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationLink link, string version) { } - protected static void WriteAttributeExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationPerson person, string version) { } - protected void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationCategory category, string version) { } - protected static void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationItem item, string version) { } - protected void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationLink link, string version) { } - protected void WriteElementExtensions(System.Xml.XmlWriter writer, System.ServiceModel.Syndication.SyndicationPerson person, string version) { } - public abstract void WriteTo(System.Xml.XmlWriter writer); - } - public partial class SyndicationLink - { - public SyndicationLink() { } - protected SyndicationLink(System.ServiceModel.Syndication.SyndicationLink source) { } - public SyndicationLink(System.Uri uri) { } - public SyndicationLink(System.Uri uri, string relationshipType, string title, string mediaType, long length) { } - public System.Collections.Generic.Dictionary AttributeExtensions { get { throw null; } } - public System.Uri BaseUri { get { throw null; } set { } } - public System.ServiceModel.Syndication.SyndicationElementExtensionCollection ElementExtensions { get { throw null; } } - public long Length { get { throw null; } set { } } - public string MediaType { get { throw null; } set { } } - public string RelationshipType { get { throw null; } set { } } - public string Title { get { throw null; } set { } } - public System.Uri Uri { get { throw null; } set { } } - public virtual System.ServiceModel.Syndication.SyndicationLink Clone() { throw null; } - public static System.ServiceModel.Syndication.SyndicationLink CreateAlternateLink(System.Uri uri) { throw null; } - public static System.ServiceModel.Syndication.SyndicationLink CreateAlternateLink(System.Uri uri, string mediaType) { throw null; } - public static System.ServiceModel.Syndication.SyndicationLink CreateMediaEnclosureLink(System.Uri uri, string mediaType, long length) { throw null; } - public static System.ServiceModel.Syndication.SyndicationLink CreateSelfLink(System.Uri uri) { throw null; } - public static System.ServiceModel.Syndication.SyndicationLink CreateSelfLink(System.Uri uri, string mediaType) { throw null; } - public System.Uri GetAbsoluteUri() { throw null; } - protected internal virtual bool TryParseAttribute(string name, string ns, string value, string version) { throw null; } - protected internal virtual bool TryParseElement(System.Xml.XmlReader reader, string version) { throw null; } - protected internal virtual void WriteAttributeExtensions(System.Xml.XmlWriter writer, string version) { } - protected internal virtual void WriteElementExtensions(System.Xml.XmlWriter writer, string version) { } - } - public partial class SyndicationPerson - { - public SyndicationPerson() { } - protected SyndicationPerson(System.ServiceModel.Syndication.SyndicationPerson source) { } - public SyndicationPerson(string email) { } - public SyndicationPerson(string email, string name, string uri) { } - public System.Collections.Generic.Dictionary AttributeExtensions { get { throw null; } } - public System.ServiceModel.Syndication.SyndicationElementExtensionCollection ElementExtensions { get { throw null; } } - public string Email { get { throw null; } set { } } - public string Name { get { throw null; } set { } } - public string Uri { get { throw null; } set { } } - public virtual System.ServiceModel.Syndication.SyndicationPerson Clone() { throw null; } - protected internal virtual bool TryParseAttribute(string name, string ns, string value, string version) { throw null; } - protected internal virtual bool TryParseElement(System.Xml.XmlReader reader, string version) { throw null; } - protected internal virtual void WriteAttributeExtensions(System.Xml.XmlWriter writer, string version) { } - protected internal virtual void WriteElementExtensions(System.Xml.XmlWriter writer, string version) { } - } - public static partial class SyndicationVersions - { - public const string Atom10 = "Atom10"; - public const string Rss20 = "Rss20"; - } - public partial class TextSyndicationContent : System.ServiceModel.Syndication.SyndicationContent - { - protected TextSyndicationContent(System.ServiceModel.Syndication.TextSyndicationContent source) { } - public TextSyndicationContent(string text) { } - public TextSyndicationContent(string text, System.ServiceModel.Syndication.TextSyndicationContentKind textKind) { } - public string Text { get { throw null; } } - public override string Type { get { throw null; } } - public override System.ServiceModel.Syndication.SyndicationContent Clone() { throw null; } - protected override void WriteContentsTo(System.Xml.XmlWriter writer) { } - } - public enum TextSyndicationContentKind - { - Html = 1, - Plaintext = 0, - XHtml = 2, - } - public partial class UrlSyndicationContent : System.ServiceModel.Syndication.SyndicationContent - { - protected UrlSyndicationContent(System.ServiceModel.Syndication.UrlSyndicationContent source) { } - public UrlSyndicationContent(System.Uri url, string mediaType) { } - public override string Type { get { throw null; } } - public System.Uri Url { get { throw null; } } - public override System.ServiceModel.Syndication.SyndicationContent Clone() { throw null; } - protected override void WriteContentsTo(System.Xml.XmlWriter writer) { } - } - public partial class Workspace - { - public Workspace() { } - public Workspace(System.ServiceModel.Syndication.TextSyndicationContent title, System.Collections.Generic.IEnumerable collections) { } - public Workspace(string title, System.Collections.Generic.IEnumerable collections) { } - public System.Collections.Generic.Dictionary AttributeExtensions { get { throw null; } } - public System.Uri BaseUri { get { throw null; } set { } } - public System.Collections.ObjectModel.Collection Collections { get { throw null; } } - public System.ServiceModel.Syndication.SyndicationElementExtensionCollection ElementExtensions { get { throw null; } } - public System.ServiceModel.Syndication.TextSyndicationContent Title { get { throw null; } set { } } - protected internal virtual System.ServiceModel.Syndication.ResourceCollectionInfo CreateResourceCollection() { throw null; } - protected internal virtual bool TryParseAttribute(string name, string ns, string value, string version) { throw null; } - protected internal virtual bool TryParseElement(System.Xml.XmlReader reader, string version) { throw null; } - protected internal virtual void WriteAttributeExtensions(System.Xml.XmlWriter writer, string version) { } - protected internal virtual void WriteElementExtensions(System.Xml.XmlWriter writer, string version) { } - } - public partial class XmlSyndicationContent : System.ServiceModel.Syndication.SyndicationContent - { - protected XmlSyndicationContent(System.ServiceModel.Syndication.XmlSyndicationContent source) { } - public XmlSyndicationContent(string type, object dataContractExtension, System.Runtime.Serialization.XmlObjectSerializer dataContractSerializer) { } - public XmlSyndicationContent(string type, object xmlSerializerExtension, System.Xml.Serialization.XmlSerializer serializer) { } - public XmlSyndicationContent(string type, System.ServiceModel.Syndication.SyndicationElementExtension extension) { } - public XmlSyndicationContent(System.Xml.XmlReader reader) { } - public System.ServiceModel.Syndication.SyndicationElementExtension Extension { get { throw null; } } - public override string Type { get { throw null; } } - public override System.ServiceModel.Syndication.SyndicationContent Clone() { throw null; } - public System.Xml.XmlDictionaryReader GetReaderAtContent() { throw null; } - public TContent ReadContent() { throw null; } - public TContent ReadContent(System.Runtime.Serialization.XmlObjectSerializer dataContractSerializer) { throw null; } - public TContent ReadContent(System.Xml.Serialization.XmlSerializer serializer) { throw null; } - protected override void WriteContentsTo(System.Xml.XmlWriter writer) { } - } -} \ No newline at end of file diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/Resources/Strings.resx b/external/corefx/src/System.ServiceModel.Syndication/src/Resources/Strings.resx new file mode 100644 index 0000000000..8f81e0922f --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/src/Resources/Strings.resx @@ -0,0 +1,258 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The Atom10 specification requires '{0}' to have one of these values: \"text\", \"html\", \"xhtml\", however this value is '{1}' in the document being deserialized. + + + The document formatter must be configured with a document. + + + Error in line {0} position {1}. + + + An error was encountered when parsing a DateTime value in the XML. + + + An error was encountered when parsing the document's XML. Refer to the inner exception for more details. + + + An error was encountered when parsing the feed's XML. Refer to the inner exception for more details. + + + An error was encountered when parsing the item's XML. Refer to the inner exception for more details. + + + The name of the extension element must be specified. + + + The feed's authors were not serialized as part of serializing the feed in RSS 2.0 format. + + + The feed's contributors were not serialized as part of serializing the feed in RSS 2.0 format. + + + The feed created a null category. + + + =The feed created a null item. + + + The feed created a null person. + + + The syndication feed formatter must be configured with a syndication feed. + + + The feed being deserialized has non-contiguous sets of items in it. This is not supported by '{0}'. + + + The feed's id was not serialized as part of serializing the feed in RSS 2.0 format. + + + The feed's links were not serialized as part of serializing the feed in RSS 2.0 format. + + + The Type of object passed as parameter '{0}' is not derived from {1}. Ensure that the type of object passed is either of type {1} or derived from {1}. + + + The item's authors were not serialized as part of serializing the feed in RSS 2.0 format. + + + The item's content was not serialized as part of serializing the feed in RSS 2.0 format. + + + The item's contributors were not serialized as part of serializing the feed in RSS 2.0 format. + + + The item's copyrights were not serialized as part of serializing the feed in RSS 2.0 format. + + + The item created a null category. + + + The item created a null person. + + + The syndication item formatter must be configured with a syndication item. + + + The item's last updated time was not serialized as part of serializing the feed in RSS 2.0 format. + + + The item's links were not serialized as part of serializing the feed in RSS 2.0 format. + + + The outer element name must be specified. + + + The outer name of the element extension cannot be empty. + + + A feed containing items that are not buffered (i.e. the items are not stored in an IList) cannot clone its items. Buffer the items in the feed before calling Clone on it or pass false to the Clone method. + + + The element with name '{0}' and namespace '{1}' is not an allowed document format. + + + The element with name '{0}' and namespace '{1}' is not an allowed feed format. + + + The element with name '{0}' and namespace '{1}' is not an allowed item format. + + + The Rss20Serializer does not support RSS version '{0}'. + + + The scheme parameter must not be empty. + + + The value of this argument must be non-negative. + + + An internal error has occurred. The XML buffer is not in the correct state to perform the operation. + + + cdata '{0}' + + + comment '{0}' + + + element '{0}' from namespace '{1}' + + + end element '{0}' from namespace '{1}' + + + end of file + + + node {0} + + + text '{0}' + + + Line {0}, position {1}. + + + Start element expected. Found {0}. + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/Resources/Strings.resx.REMOVED.git-id b/external/corefx/src/System.ServiceModel.Syndication/src/Resources/Strings.resx.REMOVED.git-id deleted file mode 100644 index f3c6ad29e0..0000000000 --- a/external/corefx/src/System.ServiceModel.Syndication/src/Resources/Strings.resx.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -5534250e892af7b6d8156f5b934427eb38bf7d79 \ No newline at end of file diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Channels/UriGenerator.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Channels/UriGenerator.cs index 5e9bec3aaf..fb9fb72a9a 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Channels/UriGenerator.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Channels/UriGenerator.cs @@ -1,13 +1,12 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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.Threading; +using System.Globalization; + namespace System.ServiceModel.Channels { - using System.Threading; - using System.Globalization; - using System.ServiceModel; - internal class UriGenerator { private long _id; @@ -26,11 +25,10 @@ namespace System.ServiceModel.Channels public UriGenerator(string scheme, string delimiter) { if (scheme == null) - throw new ArgumentException("scheme"); - //throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("scheme")); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("scheme")); if (scheme.Length == 0) - throw new ArgumentException(string.Format(SR.UriGeneratorSchemeMustNotBeEmpty, "scheme")); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.UriGeneratorSchemeMustNotBeEmpty), "scheme")); _prefix = string.Concat(scheme, ":", Guid.NewGuid().ToString(), delimiter, "id="); } diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/DiagnosticUtility.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/DiagnosticUtility.cs new file mode 100644 index 0000000000..ef0050dfdc --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/DiagnosticUtility.cs @@ -0,0 +1,49 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace System.ServiceModel +{ + /// + /// This is the Management utility class. + /// Adding Xml + /// + internal static class DiagnosticUtility + { + public static bool ShouldTraceInformation { get; internal set; } + + internal static class ExceptionUtility + { + internal static ArgumentException ThrowHelperArgument(string message) + { + return (ArgumentException)ThrowHelperError(new ArgumentException(message)); + } + + internal static ArgumentException ThrowHelperArgument(string paramName, string message) + { + return (ArgumentException)ThrowHelperError(new ArgumentException(message, paramName)); + } + + internal static ArgumentNullException ThrowHelperArgumentNull(string paramName) + { + return (ArgumentNullException)ThrowHelperError(new ArgumentNullException(paramName)); + } + + internal static Exception ThrowHelperError(Exception exception) + { + return exception; + } + + internal static Exception ThrowHelperWarning(Exception exception) + { + return exception; + } + } + } +} diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/App10Constants.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/App10Constants.cs index c1e8663b49..35dac05c0a 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/App10Constants.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/App10Constants.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. - namespace System.ServiceModel.Syndication { internal static class App10Constants diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Atom10Constants.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Atom10Constants.cs index 55f12e230f..d2bebfcc14 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Atom10Constants.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Atom10Constants.cs @@ -2,11 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. - - namespace System.ServiceModel.Syndication { - public static class Atom10Constants + internal static class Atom10Constants { public const string AlternateTag = "alternate"; public const string Atom10Namespace = "http://www.w3.org/2005/Atom"; @@ -48,6 +46,5 @@ namespace System.ServiceModel.Syndication public const string XHtmlMediaType = "application/xhtml+xml"; public const string XHtmlType = "xhtml"; public const string XmlMediaType = "text/xml"; - public const string IconTag = "icon"; } } diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Atom10FeedFormatter.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Atom10FeedFormatter.cs index 177f84ba53..db52a42c56 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Atom10FeedFormatter.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Atom10FeedFormatter.cs @@ -8,17 +8,20 @@ namespace System.ServiceModel.Syndication using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; - using System.Runtime.CompilerServices; - using System.ServiceModel.Channels; - using System.Threading; - using System.Threading.Tasks; + using System.Text; using System.Xml; using System.Xml.Serialization; + using System.Diagnostics.CodeAnalysis; + using System.Xml.Schema; + using System.Collections; + using DiagnosticUtility = System.ServiceModel.DiagnosticUtility; + using System.ServiceModel.Channels; + using System.Runtime.CompilerServices; [XmlRoot(ElementName = Atom10Constants.FeedTag, Namespace = Atom10Constants.Atom10Namespace)] - public class Atom10FeedFormatter : SyndicationFeedFormatter + public class Atom10FeedFormatter : SyndicationFeedFormatter, IXmlSerializable { - internal static readonly TimeSpan zeroOffset = new TimeSpan(0, 0, 0); + internal static readonly TimeSpan ZeroOffset = new TimeSpan(0, 0, 0); internal const string XmlNs = "http://www.w3.org/XML/1998/namespace"; internal const string XmlNsNs = "http://www.w3.org/2000/xmlns/"; private static readonly XmlQualifiedName s_atom10Href = new XmlQualifiedName(Atom10Constants.HrefTag, string.Empty); @@ -37,22 +40,6 @@ namespace System.ServiceModel.Syndication private bool _preserveAttributeExtensions; private bool _preserveElementExtensions; - //Custom parsing - // value, localname , ns , result - public Func stringParser = DefaultStringParser; - public Func dateParser = DefaultDateFromString; - public Func uriParser = DefaultUriParser; - - private static string DefaultStringParser(string value, string localName, string ns) - { - return value; - } - - private static Uri DefaultUriParser(string value, UriKind kind, string localName, string ns) - { - return new Uri(value, kind); - } - public Atom10FeedFormatter() : this(typeof(SyndicationFeed)) { @@ -63,11 +50,12 @@ namespace System.ServiceModel.Syndication { if (feedTypeToCreate == null) { - throw new ArgumentException(nameof(feedTypeToCreate)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feedTypeToCreate"); } if (!typeof(SyndicationFeed).IsAssignableFrom(feedTypeToCreate)) { - throw new ArgumentException(string.Format(SR.InvalidObjectTypePassed, nameof(feedTypeToCreate), nameof(SyndicationFeed))); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("feedTypeToCreate", + SR.Format(SR.InvalidObjectTypePassed, "feedTypeToCreate", "SyndicationFeed")); } _maxExtensionSize = int.MaxValue; _preserveAttributeExtensions = _preserveElementExtensions = true; @@ -83,6 +71,11 @@ namespace System.ServiceModel.Syndication _feedType = feedToWrite.GetType(); } + internal override Func GetDefaultDateTimeParser() + { + return DateTimeHelper.DefaultAtom10DateTimeParser; + } + public bool PreserveAttributeExtensions { get { return _preserveAttributeExtensions; } @@ -112,75 +105,86 @@ namespace System.ServiceModel.Syndication { if (reader == null) { - throw new ArgumentNullException(nameof(reader)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); } - return reader.IsStartElement(Atom10Constants.FeedTag, Atom10Constants.Atom10Namespace); } - public override async Task ReadFromAsync(XmlReader reader, CancellationToken ct) + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "The IXmlSerializable implementation is only for exposing under WCF DataContractSerializer. The funcionality is exposed to derived class through the ReadFrom\\WriteTo methods")] + XmlSchema IXmlSerializable.GetSchema() { - if (!CanRead(reader)) - { - throw new XmlException(string.Format(SR.UnknownFeedXml, reader.LocalName, reader.NamespaceURI)); - } - - SetFeed(CreateFeedInstance()); - await ReadFeedFromAsync(XmlReaderWrapper.CreateFromReader(reader), this.Feed, false); + return null; } - public override async Task WriteToAsync(XmlWriter writer, CancellationToken ct) + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "The IXmlSerializable implementation is only for exposing under WCF DataContractSerializer. The funcionality is exposed to derived class through the ReadFrom\\WriteTo methods")] + void IXmlSerializable.ReadXml(XmlReader reader) + { + if (reader == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); + } + TraceFeedReadBegin(); + ReadFeed(reader); + TraceFeedReadEnd(); + } + + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "The IXmlSerializable implementation is only for exposing under WCF DataContractSerializer. The funcionality is exposed to derived class through the ReadFrom\\WriteTo methods")] + void IXmlSerializable.WriteXml(XmlWriter writer) { if (writer == null) { - throw new ArgumentNullException(nameof(writer)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); } - - writer = XmlWriterWrapper.CreateFromWriter(writer); - - await writer.WriteStartElementAsync(Atom10Constants.FeedTag, Atom10Constants.Atom10Namespace); - await WriteFeedAsync(writer); - await writer.WriteEndElementAsync(); + TraceFeedWriteBegin(); + WriteFeed(writer); + TraceFeedWriteEnd(); } - internal static async Task ReadCategoryAsync(XmlReaderWrapper reader, SyndicationCategory category, string version, bool preserveAttributeExtensions, bool preserveElementExtensions, int _maxExtensionSize) + public override void ReadFrom(XmlReader reader) { - await MoveToStartElementAsync(reader); + TraceFeedReadBegin(); + if (!CanRead(reader)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.UnknownFeedXml, reader.LocalName, reader.NamespaceURI))); + } + ReadFeed(reader); + TraceFeedReadEnd(); + } + + public override void WriteTo(XmlWriter writer) + { + if (writer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); + } + TraceFeedWriteBegin(); + writer.WriteStartElement(Atom10Constants.FeedTag, Atom10Constants.Atom10Namespace); + WriteFeed(writer); + writer.WriteEndElement(); + TraceFeedWriteEnd(); + } + + internal static void ReadCategory(XmlReader reader, SyndicationCategory category, string version, bool preserveAttributeExtensions, bool preserveElementExtensions, int maxExtensionSize) + { + MoveToStartElement(reader); bool isEmpty = reader.IsEmptyElement; if (reader.HasAttributes) { while (reader.MoveToNextAttribute()) { - string value = await reader.GetValueAsync(); - bool notHandled = false; - - if (reader.NamespaceURI == string.Empty) + if (reader.LocalName == Atom10Constants.TermTag && reader.NamespaceURI == string.Empty) { - switch (reader.LocalName) - { - case Atom10Constants.TermTag: - category.Name = value; - break; - - case Atom10Constants.SchemeTag: - category.Scheme = value; - break; - - case Atom10Constants.LabelTag: - category.Label = value; - break; - - default: - notHandled = true; - break; - } + category.Name = reader.Value; + } + else if (reader.LocalName == Atom10Constants.SchemeTag && reader.NamespaceURI == string.Empty) + { + category.Scheme = reader.Value; + } + else if (reader.LocalName == Atom10Constants.LabelTag && reader.NamespaceURI == string.Empty) + { + category.Label = reader.Value; } else - { - notHandled = true; - } - - if (notHandled) { string ns = reader.NamespaceURI; string name = reader.LocalName; @@ -188,12 +192,17 @@ namespace System.ServiceModel.Syndication { continue; } + string val = reader.Value; + if (!TryParseAttribute(name, ns, val, category, version)) - if (!TryParseAttribute(name, ns, value, category, version)) { if (preserveAttributeExtensions) { - category.AttributeExtensions.Add(new XmlQualifiedName(name, ns), value); + category.AttributeExtensions.Add(new XmlQualifiedName(name, ns), val); + } + else + { + TraceSyndicationElementIgnoredOnRead(reader); } } } @@ -202,12 +211,12 @@ namespace System.ServiceModel.Syndication if (!isEmpty) { - await reader.ReadStartElementAsync(); + reader.ReadStartElement(); XmlBuffer buffer = null; XmlDictionaryWriter extWriter = null; try { - while (await reader.IsStartElementAsync()) + while (reader.IsStartElement()) { if (TryParseElement(reader, category, version)) { @@ -215,16 +224,14 @@ namespace System.ServiceModel.Syndication } else if (!preserveElementExtensions) { - await reader.SkipAsync(); + TraceSyndicationElementIgnoredOnRead(reader); + reader.Skip(); } else { - var tuple = await CreateBufferIfRequiredAndWriteNodeAsync(buffer, extWriter, reader, _maxExtensionSize); - buffer = tuple.Item1; - extWriter = tuple.Item2; + CreateBufferIfRequiredAndWriteNode(ref buffer, ref extWriter, reader, maxExtensionSize); } } - LoadElementExtensions(buffer, extWriter, category); } finally @@ -234,307 +241,302 @@ namespace System.ServiceModel.Syndication ((IDisposable)extWriter).Dispose(); } } - - await reader.ReadEndElementAsync(); + reader.ReadEndElement(); } else { - await reader.ReadStartElementAsync(); + reader.ReadStartElement(); } - - return category; } - internal Task ReadTextContentFromAsync(XmlReaderWrapper reader, string context, bool preserveAttributeExtensions) + internal static TextSyndicationContent ReadTextContentFrom(XmlReader reader, string context, bool preserveAttributeExtensions) { string type = reader.GetAttribute(Atom10Constants.TypeTag); - return ReadTextContentFromHelperAsync(reader, type, context, preserveAttributeExtensions); + return ReadTextContentFromHelper(reader, type, context, preserveAttributeExtensions); } - internal static async Task WriteCategoryAsync(XmlWriter writer, SyndicationCategory category, string version) + internal static void WriteCategory(XmlWriter writer, SyndicationCategory category, string version) { - await writer.WriteStartElementAsync(Atom10Constants.CategoryTag, Atom10Constants.Atom10Namespace); - await WriteAttributeExtensionsAsync(writer, category, version); + writer.WriteStartElement(Atom10Constants.CategoryTag, Atom10Constants.Atom10Namespace); + WriteAttributeExtensions(writer, category, version); string categoryName = category.Name ?? string.Empty; if (!category.AttributeExtensions.ContainsKey(s_atom10Term)) { - await writer.WriteAttributeStringAsync(Atom10Constants.TermTag, categoryName); + writer.WriteAttributeString(Atom10Constants.TermTag, categoryName); } - if (!string.IsNullOrEmpty(category.Label) && !category.AttributeExtensions.ContainsKey(s_atom10Label)) { - await writer.WriteAttributeStringAsync(Atom10Constants.LabelTag, category.Label); + writer.WriteAttributeString(Atom10Constants.LabelTag, category.Label); } - if (!string.IsNullOrEmpty(category.Scheme) && !category.AttributeExtensions.ContainsKey(s_atom10Scheme)) { - await writer.WriteAttributeStringAsync(Atom10Constants.SchemeTag, category.Scheme); + writer.WriteAttributeString(Atom10Constants.SchemeTag, category.Scheme); } - - await WriteElementExtensionsAsync(writer, category, version); - await writer.WriteEndElementAsync(); + WriteElementExtensions(writer, category, version); + writer.WriteEndElement(); } - internal async Task ReadItemFromAsync(XmlReaderWrapper reader, SyndicationItem result) + internal void ReadItemFrom(XmlReader reader, SyndicationItem result) { - await ReadItemFromAsync(reader, result, null); + ReadItemFrom(reader, result, null); } - internal async Task TryParseFeedElementFromAsync(XmlReaderWrapper reader, SyndicationFeed result) + internal bool TryParseFeedElementFrom(XmlReader reader, SyndicationFeed result) { - if (await reader.MoveToContentAsync() != XmlNodeType.Element) + if (reader.IsStartElement(Atom10Constants.AuthorTag, Atom10Constants.Atom10Namespace)) + { + result.Authors.Add(ReadPersonFrom(reader, result)); + } + else if (reader.IsStartElement(Atom10Constants.CategoryTag, Atom10Constants.Atom10Namespace)) + { + result.Categories.Add(ReadCategoryFrom(reader, result)); + } + else if (reader.IsStartElement(Atom10Constants.ContributorTag, Atom10Constants.Atom10Namespace)) + { + result.Contributors.Add(ReadPersonFrom(reader, result)); + } + else if (reader.IsStartElement(Atom10Constants.GeneratorTag, Atom10Constants.Atom10Namespace)) + { + result.Generator = reader.ReadElementString(); + } + else if (reader.IsStartElement(Atom10Constants.IdTag, Atom10Constants.Atom10Namespace)) + { + result.Id = reader.ReadElementString(); + } + else if (reader.IsStartElement(Atom10Constants.LinkTag, Atom10Constants.Atom10Namespace)) + { + result.Links.Add(ReadLinkFrom(reader, result)); + } + else if (reader.IsStartElement(Atom10Constants.LogoTag, Atom10Constants.Atom10Namespace)) + { + result.ImageUrl = UriParser(reader.ReadElementString(), UriKind.RelativeOrAbsolute, Atom10Constants.LogoTag, Atom10Constants.Atom10Namespace); + } + else if (reader.IsStartElement(Atom10Constants.RightsTag, Atom10Constants.Atom10Namespace)) + { + result.Copyright = ReadTextContentFrom(reader, "//atom:feed/atom:rights[@type]"); + } + else if (reader.IsStartElement(Atom10Constants.SubtitleTag, Atom10Constants.Atom10Namespace)) + { + result.Description = ReadTextContentFrom(reader, "//atom:feed/atom:subtitle[@type]"); + } + else if (reader.IsStartElement(Atom10Constants.TitleTag, Atom10Constants.Atom10Namespace)) + { + result.Title = ReadTextContentFrom(reader, "//atom:feed/atom:title[@type]"); + } + else if (reader.IsStartElement(Atom10Constants.UpdatedTag, Atom10Constants.Atom10Namespace)) + { + reader.ReadStartElement(); + string dtoString = reader.ReadString(); + try + { + result.LastUpdatedTime = DateFromString(dtoString, reader); + } + catch (XmlException e) + { + result.LastUpdatedTimeException = e; + } + + reader.ReadEndElement(); + } + else { return false; } - - string name = reader.LocalName; - string ns = reader.NamespaceURI; - - if (ns == Atom10Constants.Atom10Namespace) - { - switch (name) - { - case Atom10Constants.AuthorTag: - result.Authors.Add(await ReadPersonFromAsync(reader, result)); - break; - case Atom10Constants.CategoryTag: - result.Categories.Add(await ReadCategoryFromAsync(reader, result)); - break; - case Atom10Constants.ContributorTag: - result.Contributors.Add(await ReadPersonFromAsync(reader, result)); - break; - case Atom10Constants.GeneratorTag: - result.Generator = stringParser(await reader.ReadElementStringAsync(), Atom10Constants.GeneratorTag, ns); - break; - case Atom10Constants.IdTag: - result.Id = stringParser(await reader.ReadElementStringAsync(), Atom10Constants.IdTag, ns); - break; - case Atom10Constants.LinkTag: - result.Links.Add(await ReadLinkFromAsync(reader, result)); - break; - case Atom10Constants.LogoTag: - result.ImageUrl = uriParser(await reader.ReadElementStringAsync(), UriKind.RelativeOrAbsolute, Atom10Constants.LogoTag, ns); //new Uri(await reader.ReadElementStringAsync(), UriKind.RelativeOrAbsolute); - break; - case Atom10Constants.RightsTag: - result.Copyright = await ReadTextContentFromAsync(reader, "//atom:feed/atom:rights[@type]"); - break; - case Atom10Constants.SubtitleTag: - result.Description = await ReadTextContentFromAsync(reader, "//atom:feed/atom:subtitle[@type]"); - break; - case Atom10Constants.TitleTag: - result.Title = await ReadTextContentFromAsync(reader, "//atom:feed/atom:title[@type]"); - break; - case Atom10Constants.UpdatedTag: - await reader.ReadStartElementAsync(); - result.LastUpdatedTime = dateParser(await reader.ReadStringAsync(), Atom10Constants.UpdatedTag, ns); - await reader.ReadEndElementAsync(); - break; - case Atom10Constants.IconTag: - result.IconImage = uriParser(await reader.ReadElementStringAsync(), UriKind.RelativeOrAbsolute, Atom10Constants.IconTag, ns); - break; - default: - return false; - } - - return true; - } - - return false; + return true; } - internal async Task TryParseItemElementFromAsync(XmlReaderWrapper reader, SyndicationItem result) + internal bool TryParseItemElementFrom(XmlReader reader, SyndicationItem result) { - if (await reader.MoveToContentAsync() != XmlNodeType.Element) + if (reader.IsStartElement(Atom10Constants.AuthorTag, Atom10Constants.Atom10Namespace)) + { + result.Authors.Add(ReadPersonFrom(reader, result)); + } + else if (reader.IsStartElement(Atom10Constants.CategoryTag, Atom10Constants.Atom10Namespace)) + { + result.Categories.Add(ReadCategoryFrom(reader, result)); + } + else if (reader.IsStartElement(Atom10Constants.ContentTag, Atom10Constants.Atom10Namespace)) + { + result.Content = ReadContentFrom(reader, result); + } + else if (reader.IsStartElement(Atom10Constants.ContributorTag, Atom10Constants.Atom10Namespace)) + { + result.Contributors.Add(ReadPersonFrom(reader, result)); + } + else if (reader.IsStartElement(Atom10Constants.IdTag, Atom10Constants.Atom10Namespace)) + { + result.Id = reader.ReadElementString(); + } + else if (reader.IsStartElement(Atom10Constants.LinkTag, Atom10Constants.Atom10Namespace)) + { + result.Links.Add(ReadLinkFrom(reader, result)); + } + else if (reader.IsStartElement(Atom10Constants.PublishedTag, Atom10Constants.Atom10Namespace)) + { + reader.ReadStartElement(); + string dtoString = reader.ReadString(); + try + { + result.PublishDate = DateFromString(dtoString, reader); + } + catch (XmlException e) + { + result.PublishDateException = e; + } + + reader.ReadEndElement(); + } + else if (reader.IsStartElement(Atom10Constants.RightsTag, Atom10Constants.Atom10Namespace)) + { + result.Copyright = ReadTextContentFrom(reader, "//atom:feed/atom:entry/atom:rights[@type]"); + } + else if (reader.IsStartElement(Atom10Constants.SourceFeedTag, Atom10Constants.Atom10Namespace)) + { + reader.ReadStartElement(); + result.SourceFeed = ReadFeedFrom(reader, new SyndicationFeed(), true); // isSourceFeed + reader.ReadEndElement(); + } + else if (reader.IsStartElement(Atom10Constants.SummaryTag, Atom10Constants.Atom10Namespace)) + { + result.Summary = ReadTextContentFrom(reader, "//atom:feed/atom:entry/atom:summary[@type]"); + } + else if (reader.IsStartElement(Atom10Constants.TitleTag, Atom10Constants.Atom10Namespace)) + { + result.Title = ReadTextContentFrom(reader, "//atom:feed/atom:entry/atom:title[@type]"); + } + else if (reader.IsStartElement(Atom10Constants.UpdatedTag, Atom10Constants.Atom10Namespace)) + { + reader.ReadStartElement(); + string dtoString = reader.ReadString(); + try + { + result.LastUpdatedTime = DateFromString(dtoString, reader); + } + catch (XmlException e) + { + result.LastUpdatedTimeException = e; + } + + reader.ReadEndElement(); + } + else { return false; } - - string name = reader.LocalName; - string ns = reader.NamespaceURI; - - if (ns == Atom10Constants.Atom10Namespace) - { - switch (name) - { - case Atom10Constants.AuthorTag: - result.Authors.Add(await ReadPersonFromAsync(reader, result)); - break; - case Atom10Constants.CategoryTag: - result.Categories.Add(await ReadCategoryFromAsync(reader, result)); - break; - case Atom10Constants.ContentTag: - result.Content = await ReadContentFromAsync(reader, result); - break; - case Atom10Constants.ContributorTag: - result.Contributors.Add(await ReadPersonFromAsync(reader, result)); - break; - case Atom10Constants.IdTag: - result.Id = stringParser(await reader.ReadElementStringAsync(), Atom10Constants.IdTag, ns); - break; - case Atom10Constants.LinkTag: - result.Links.Add(await ReadLinkFromAsync(reader, result)); - break; - case Atom10Constants.PublishedTag: - await reader.ReadStartElementAsync(); - result.PublishDate = dateParser(await reader.ReadStringAsync(), Atom10Constants.UpdatedTag, ns); - await reader.ReadEndElementAsync(); - break; - case Atom10Constants.RightsTag: - result.Copyright = await ReadTextContentFromAsync(reader, "//atom:feed/atom:entry/atom:rights[@type]"); - break; - case Atom10Constants.SourceFeedTag: - await reader.ReadStartElementAsync(); - result.SourceFeed = await ReadFeedFromAsync(reader, new SyndicationFeed(), true); // isSourceFeed - await reader.ReadEndElementAsync(); - break; - case Atom10Constants.SummaryTag: - result.Summary = await ReadTextContentFromAsync(reader, "//atom:feed/atom:entry/atom:summary[@type]"); - break; - case Atom10Constants.TitleTag: - result.Title = await ReadTextContentFromAsync(reader, "//atom:feed/atom:entry/atom:title[@type]"); - break; - case Atom10Constants.UpdatedTag: - await reader.ReadStartElementAsync(); - result.LastUpdatedTime = dateParser(await reader.ReadStringAsync(), Atom10Constants.UpdatedTag, ns); - await reader.ReadEndElementAsync(); - break; - default: - return false; - } - - return true; - } - - return false; + return true; } - internal Task WriteContentToAsync(XmlWriter writer, string elementName, SyndicationContent content) + internal void WriteContentTo(XmlWriter writer, string elementName, SyndicationContent content) { if (content != null) { - return content.WriteToAsync(writer, elementName, Atom10Constants.Atom10Namespace); + content.WriteTo(writer, elementName, Atom10Constants.Atom10Namespace); } - return Task.CompletedTask; } - internal async Task WriteElementAsync(XmlWriter writer, string elementName, string value) + internal void WriteElement(XmlWriter writer, string elementName, string value) { if (value != null) { - await writer.WriteElementStringAsync(elementName, Atom10Constants.Atom10Namespace, value); + writer.WriteElementString(elementName, Atom10Constants.Atom10Namespace, value); } } - internal async Task WriteFeedAuthorsToAsync(XmlWriter writer, Collection authors) + internal void WriteFeedAuthorsTo(XmlWriter writer, Collection authors) { - writer = XmlWriterWrapper.CreateFromWriter(writer); for (int i = 0; i < authors.Count; ++i) { SyndicationPerson p = authors[i]; - await WritePersonToAsync(writer, p, Atom10Constants.AuthorTag); + WritePersonTo(writer, p, Atom10Constants.AuthorTag); } } - internal async Task WriteFeedContributorsToAsync(XmlWriter writer, Collection contributors) + internal void WriteFeedContributorsTo(XmlWriter writer, Collection contributors) { - writer = XmlWriterWrapper.CreateFromWriter(writer); for (int i = 0; i < contributors.Count; ++i) { SyndicationPerson p = contributors[i]; - await WritePersonToAsync(writer, p, Atom10Constants.ContributorTag); + WritePersonTo(writer, p, Atom10Constants.ContributorTag); } } - internal Task WriteFeedLastUpdatedTimeToAsync(XmlWriter writer, DateTimeOffset lastUpdatedTime, bool isRequired) + internal void WriteFeedLastUpdatedTimeTo(XmlWriter writer, DateTimeOffset lastUpdatedTime, bool isRequired) { if (lastUpdatedTime == DateTimeOffset.MinValue && isRequired) { lastUpdatedTime = DateTimeOffset.UtcNow; } - if (lastUpdatedTime != DateTimeOffset.MinValue) { - return WriteElementAsync(writer, Atom10Constants.UpdatedTag, AsString(lastUpdatedTime)); + WriteElement(writer, Atom10Constants.UpdatedTag, AsString(lastUpdatedTime)); } - - return Task.CompletedTask; } - internal async Task WriteItemAuthorsToAsync(XmlWriter writer, Collection authors) + internal void WriteItemAuthorsTo(XmlWriter writer, Collection authors) { - writer = XmlWriterWrapper.CreateFromWriter(writer); for (int i = 0; i < authors.Count; ++i) { SyndicationPerson p = authors[i]; - await WritePersonToAsync(writer, p, Atom10Constants.AuthorTag); + WritePersonTo(writer, p, Atom10Constants.AuthorTag); } } - internal Task WriteItemContentsAsync(XmlWriter dictWriter, SyndicationItem item) + internal void WriteItemContents(XmlWriter dictWriter, SyndicationItem item) { - return WriteItemContentsAsync(dictWriter, item, null); + WriteItemContents(dictWriter, item, null); } - internal async Task WriteItemContributorsToAsync(XmlWriter writer, Collection contributors) + internal void WriteItemContributorsTo(XmlWriter writer, Collection contributors) { - writer = XmlWriterWrapper.CreateFromWriter(writer); - for (int i = 0; i < contributors.Count; ++i) { SyndicationPerson p = contributors[i]; - await WritePersonToAsync(writer, p, Atom10Constants.ContributorTag); + WritePersonTo(writer, p, Atom10Constants.ContributorTag); } } - internal Task WriteItemLastUpdatedTimeToAsync(XmlWriter writer, DateTimeOffset lastUpdatedTime) + internal void WriteItemLastUpdatedTimeTo(XmlWriter writer, DateTimeOffset lastUpdatedTime) { if (lastUpdatedTime == DateTimeOffset.MinValue) { lastUpdatedTime = DateTimeOffset.UtcNow; } - - return writer.WriteElementStringAsync(Atom10Constants.UpdatedTag, + writer.WriteElementString(Atom10Constants.UpdatedTag, Atom10Constants.Atom10Namespace, AsString(lastUpdatedTime)); } - internal async Task WriteLinkAsync(XmlWriter writer, SyndicationLink link, Uri baseUri) + internal void WriteLink(XmlWriter writer, SyndicationLink link, Uri baseUri) { - await writer.WriteStartElementAsync(Atom10Constants.LinkTag, Atom10Constants.Atom10Namespace); + writer.WriteStartElement(Atom10Constants.LinkTag, Atom10Constants.Atom10Namespace); Uri baseUriToWrite = FeedUtils.GetBaseUriToWrite(baseUri, link.BaseUri); if (baseUriToWrite != null) { - await writer.WriteAttributeStringAsync("xml", "base", XmlNs, FeedUtils.GetUriString(baseUriToWrite)); + writer.WriteAttributeString("xml", "base", XmlNs, FeedUtils.GetUriString(baseUriToWrite)); } - - await link.WriteAttributeExtensionsAsync(writer, SyndicationVersions.Atom10); + link.WriteAttributeExtensions(writer, SyndicationVersions.Atom10); if (!string.IsNullOrEmpty(link.RelationshipType) && !link.AttributeExtensions.ContainsKey(s_atom10Relative)) { - await writer.WriteAttributeStringAsync(Atom10Constants.RelativeTag, link.RelationshipType); + writer.WriteAttributeString(Atom10Constants.RelativeTag, link.RelationshipType); } - if (!string.IsNullOrEmpty(link.MediaType) && !link.AttributeExtensions.ContainsKey(s_atom10Type)) { - await writer.WriteAttributeStringAsync(Atom10Constants.TypeTag, link.MediaType); + writer.WriteAttributeString(Atom10Constants.TypeTag, link.MediaType); } - if (!string.IsNullOrEmpty(link.Title) && !link.AttributeExtensions.ContainsKey(s_atom10Title)) { - await writer.WriteAttributeStringAsync(Atom10Constants.TitleTag, link.Title); + writer.WriteAttributeString(Atom10Constants.TitleTag, link.Title); } - if (link.Length != 0 && !link.AttributeExtensions.ContainsKey(s_atom10Length)) { - await writer.WriteAttributeStringAsync(Atom10Constants.LengthTag, Convert.ToString(link.Length, CultureInfo.InvariantCulture)); + writer.WriteAttributeString(Atom10Constants.LengthTag, Convert.ToString(link.Length, CultureInfo.InvariantCulture)); } - if (!link.AttributeExtensions.ContainsKey(s_atom10Href)) { - await writer.WriteAttributeStringAsync(Atom10Constants.HrefTag, FeedUtils.GetUriString(link.Uri)); + writer.WriteAttributeString(Atom10Constants.HrefTag, FeedUtils.GetUriString(link.Uri)); } - - await link.WriteElementExtensionsAsync(writer, SyndicationVersions.Atom10); - await writer.WriteEndElementAsync(); + link.WriteElementExtensions(writer, SyndicationVersions.Atom10); + writer.WriteEndElement(); } protected override SyndicationFeed CreateFeedInstance() @@ -542,77 +544,72 @@ namespace System.ServiceModel.Syndication return SyndicationFeedFormatter.CreateFeedInstance(_feedType); } - protected virtual async Task ReadItemAsync(XmlReader reader, SyndicationFeed feed) + protected virtual SyndicationItem ReadItem(XmlReader reader, SyndicationFeed feed) { if (feed == null) { - throw new ArgumentNullException(nameof(feed)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feed"); } - if (reader == null) { - throw new ArgumentNullException(nameof(reader)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); } - SyndicationItem item = CreateItem(feed); - XmlReaderWrapper readerWrapper = XmlReaderWrapper.CreateFromReader(reader); - await ReadItemFromAsync(readerWrapper, item, feed.BaseUri); + TraceItemReadBegin(); + ReadItemFrom(reader, item, feed.BaseUri); + TraceItemReadEnd(); return item; } - //not referenced anymore - protected virtual async Task> ReadItemsAsync(XmlReader reader, SyndicationFeed feed) + [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Justification = "The out parameter is needed to enable implementations that read in items from the stream on demand")] + protected virtual IEnumerable ReadItems(XmlReader reader, SyndicationFeed feed, out bool areAllItemsRead) { if (feed == null) { - throw new ArgumentNullException(nameof(feed)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feed"); } - if (reader == null) { - throw new ArgumentNullException(nameof(reader)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); } - NullNotAllowedCollection items = new NullNotAllowedCollection(); - XmlReaderWrapper readerWrapper = XmlReaderWrapper.CreateFromReader(reader); - - while (await readerWrapper.IsStartElementAsync(Atom10Constants.EntryTag, Atom10Constants.Atom10Namespace)) + while (reader.IsStartElement(Atom10Constants.EntryTag, Atom10Constants.Atom10Namespace)) { - items.Add(await ReadItemAsync(reader, feed)); + items.Add(ReadItem(reader, feed)); } - + areAllItemsRead = true; return items; } - protected virtual async Task WriteItemAsync(XmlWriter writer, SyndicationItem item, Uri feedBaseUri) + protected virtual void WriteItem(XmlWriter writer, SyndicationItem item, Uri feedBaseUri) { - writer = XmlWriterWrapper.CreateFromWriter(writer); - await writer.WriteStartElementAsync(Atom10Constants.EntryTag, Atom10Constants.Atom10Namespace); - await WriteItemContentsAsync(writer, item, feedBaseUri); - await writer.WriteEndElementAsync(); + TraceItemWriteBegin(); + writer.WriteStartElement(Atom10Constants.EntryTag, Atom10Constants.Atom10Namespace); + WriteItemContents(writer, item, feedBaseUri); + writer.WriteEndElement(); + TraceItemWriteEnd(); } - protected virtual async Task WriteItemsAsync(XmlWriter writer, IEnumerable items, Uri feedBaseUri) + protected virtual void WriteItems(XmlWriter writer, IEnumerable items, Uri feedBaseUri) { if (items == null) { return; } - foreach (SyndicationItem item in items) { - await this.WriteItemAsync(writer, item, feedBaseUri); + this.WriteItem(writer, item, feedBaseUri); } } - private async Task ReadTextContentFromHelperAsync(XmlReaderWrapper reader, string type, string context, bool preserveAttributeExtensions) + private static TextSyndicationContent ReadTextContentFromHelper(XmlReader reader, string type, string context, bool preserveAttributeExtensions) { if (string.IsNullOrEmpty(type)) { type = Atom10Constants.PlaintextType; } - TextSyndicationContentKind kind = new TextSyndicationContentKind(); + TextSyndicationContentKind kind; switch (type) { case Atom10Constants.PlaintextType: @@ -624,8 +621,8 @@ namespace System.ServiceModel.Syndication case Atom10Constants.XHtmlType: kind = TextSyndicationContentKind.XHtml; break; - - throw new XmlException(string.Format(SR.Atom10SpecRequiresTextConstruct, context, type)); + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(FeedUtils.AddLineInfo(reader, SR.Format(SR.Atom10SpecRequiresTextConstruct, context, type)))); } Dictionary attrs = null; @@ -645,19 +642,23 @@ namespace System.ServiceModel.Syndication } if (preserveAttributeExtensions) { - string value = await reader.GetValueAsync(); + string value = reader.Value; if (attrs == null) { attrs = new Dictionary(); } attrs.Add(new XmlQualifiedName(name, ns), value); } + else + { + TraceSyndicationElementIgnoredOnRead(reader); + } } } reader.MoveToElement(); string localName = reader.LocalName; string nameSpace = reader.NamespaceURI; - string val = (kind == TextSyndicationContentKind.XHtml) ? await reader.ReadInnerXmlAsync() : stringParser(await reader.ReadElementStringAsync(), localName, nameSpace); // cant custom parse because its static + string val = (kind == TextSyndicationContentKind.XHtml) ? reader.ReadInnerXml() : reader.ReadElementString(); TextSyndicationContent result = new TextSyndicationContent(val, kind); if (attrs != null) { @@ -674,7 +675,7 @@ namespace System.ServiceModel.Syndication private string AsString(DateTimeOffset dateTime) { - if (dateTime.Offset == zeroOffset) + if (dateTime.Offset == ZeroOffset) { return dateTime.ToUniversalTime().ToString(Rfc3339UTCDateTimeFormat, CultureInfo.InvariantCulture); } @@ -684,66 +685,28 @@ namespace System.ServiceModel.Syndication } } - private static DateTimeOffset DefaultDateFromString(string dateTimeString, string localName, string ns) + private void ReadCategory(XmlReader reader, SyndicationCategory category) { - dateTimeString = dateTimeString.Trim(); - if (dateTimeString.Length < 20) - { - throw new XmlException(SR.ErrorParsingDateTime); - } - if (dateTimeString[19] == '.') - { - // remove any fractional seconds, we choose to ignore them - int i = 20; - while (dateTimeString.Length > i && char.IsDigit(dateTimeString[i])) - { - ++i; - } - dateTimeString = dateTimeString.Substring(0, 19) + dateTimeString.Substring(i); - } - DateTimeOffset localTime; - if (DateTimeOffset.TryParseExact(dateTimeString, Rfc3339LocalDateTimeFormat, - CultureInfo.InvariantCulture.DateTimeFormat, - DateTimeStyles.None, out localTime)) - { - return localTime; - } - DateTimeOffset utcTime; - if (DateTimeOffset.TryParseExact(dateTimeString, Rfc3339UTCDateTimeFormat, - CultureInfo.InvariantCulture.DateTimeFormat, - DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out utcTime)) - { - return utcTime; - } - - //throw new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingDateTime)); - - //if imposible to parse, return default date - return new DateTimeOffset(); + ReadCategory(reader, category, this.Version, this.PreserveAttributeExtensions, this.PreserveElementExtensions, _maxExtensionSize); } - private Task ReadCategoryAsync(XmlReaderWrapper reader, SyndicationCategory category) - { - return ReadCategoryAsync(reader, category, this.Version, this.PreserveAttributeExtensions, this.PreserveElementExtensions, _maxExtensionSize); - } - - private Task ReadCategoryFromAsync(XmlReaderWrapper reader, SyndicationFeed feed) - + private SyndicationCategory ReadCategoryFrom(XmlReader reader, SyndicationFeed feed) { SyndicationCategory result = CreateCategory(feed); - return ReadCategoryAsync(reader, result); - } - - private async Task ReadCategoryFromAsync(XmlReaderWrapper reader, SyndicationItem item) - { - SyndicationCategory result = CreateCategory(item); - await ReadCategoryAsync(reader, result); + ReadCategory(reader, result); return result; } - private async Task ReadContentFromAsync(XmlReaderWrapper reader, SyndicationItem item) + private SyndicationCategory ReadCategoryFrom(XmlReader reader, SyndicationItem item) { - await MoveToStartElementAsync(reader); + SyndicationCategory result = CreateCategory(item); + ReadCategory(reader, result); + return result; + } + + private SyndicationContent ReadContentFrom(XmlReader reader, SyndicationItem item) + { + MoveToStartElement(reader); string type = reader.GetAttribute(Atom10Constants.TypeTag, string.Empty); SyndicationContent result; @@ -765,7 +728,7 @@ namespace System.ServiceModel.Syndication if (!string.IsNullOrEmpty(src)) { - result = new UrlSyndicationContent(new Uri(src, UriKind.RelativeOrAbsolute), type); + result = new UrlSyndicationContent(UriParser(src, UriKind.RelativeOrAbsolute, Atom10Constants.ContentTag, Atom10Constants.Atom10Namespace), type); bool isEmpty = reader.IsEmptyElement; if (reader.HasAttributes) { @@ -783,153 +746,166 @@ namespace System.ServiceModel.Syndication { if (_preserveAttributeExtensions) { - result.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), await reader.GetValueAsync()); + result.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), reader.Value); } else { - //result.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), await reader.GetValueAsync()); + TraceSyndicationElementIgnoredOnRead(reader); } } } } - - await reader.ReadStartElementAsync(); + reader.ReadStartElement(); if (!isEmpty) { - await reader.ReadEndElementAsync(); + reader.ReadEndElement(); } - return result; } else { - return await ReadTextContentFromHelperAsync(reader, type, "//atom:feed/atom:entry/atom:content[@type]", _preserveAttributeExtensions); + return ReadTextContentFromHelper(reader, type, "//atom:feed/atom:entry/atom:content[@type]", _preserveAttributeExtensions); } } - private async Task ReadFeedFromAsync(XmlReaderWrapper reader, SyndicationFeed result, bool isSourceFeed) + private void ReadFeed(XmlReader reader) { - await reader.MoveToContentAsync(); - //fix to accept non contiguous items - NullNotAllowedCollection feedItems = new NullNotAllowedCollection(); + SetFeed(CreateFeedInstance()); + ReadFeedFrom(reader, this.Feed, false); + } - bool elementIsEmpty = false; - if (!isSourceFeed) + private SyndicationFeed ReadFeedFrom(XmlReader reader, SyndicationFeed result, bool isSourceFeed) + { + reader.MoveToContent(); + try { - await MoveToStartElementAsync(reader); - elementIsEmpty = reader.IsEmptyElement; - if (reader.HasAttributes) + bool elementIsEmpty = false; + if (!isSourceFeed) { - while (reader.MoveToNextAttribute()) + MoveToStartElement(reader); + elementIsEmpty = reader.IsEmptyElement; + if (reader.HasAttributes) { - if (reader.LocalName == "lang" && reader.NamespaceURI == XmlNs) + while (reader.MoveToNextAttribute()) { - result.Language = await reader.GetValueAsync(); - } - else if (reader.LocalName == "base" && reader.NamespaceURI == XmlNs) - { - result.BaseUri = FeedUtils.CombineXmlBase(result.BaseUri, await reader.GetValueAsync()); - } - else - { - string ns = reader.NamespaceURI; - string name = reader.LocalName; - if (FeedUtils.IsXmlns(name, ns) || FeedUtils.IsXmlSchemaType(name, ns)) + if (reader.LocalName == "lang" && reader.NamespaceURI == XmlNs) { - continue; + result.Language = reader.Value; } - - string val = await reader.GetValueAsync(); - - if (!TryParseAttribute(name, ns, val, result, this.Version)) + else if (reader.LocalName == "base" && reader.NamespaceURI == XmlNs) { - if (_preserveAttributeExtensions) + result.BaseUri = FeedUtils.CombineXmlBase(result.BaseUri, reader.Value); + } + else + { + string ns = reader.NamespaceURI; + string name = reader.LocalName; + if (FeedUtils.IsXmlns(name, ns) || FeedUtils.IsXmlSchemaType(name, ns)) { - result.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), val); + continue; } - } - } - } - } - await reader.ReadStartElementAsync(); - } - - XmlBuffer buffer = null; - XmlDictionaryWriter extWriter = null; - - if (!elementIsEmpty) - { - try - { - while (await reader.IsStartElementAsync()) - { - if (await TryParseFeedElementFromAsync(reader, result)) - { - // nothing, we parsed something, great - } - else if (await reader.IsStartElementAsync(Atom10Constants.EntryTag, Atom10Constants.Atom10Namespace) && !isSourceFeed) - { - while (await reader.IsStartElementAsync(Atom10Constants.EntryTag, Atom10Constants.Atom10Namespace)) - { - feedItems.Add(await ReadItemAsync(reader, result)); - } - } - else - { - if (!TryParseElement(reader, result, this.Version)) - { - if (_preserveElementExtensions) + string val = reader.Value; + if (!TryParseAttribute(name, ns, val, result, this.Version)) { - if (buffer == null) + if (_preserveAttributeExtensions) { - buffer = new XmlBuffer(_maxExtensionSize); - extWriter = buffer.OpenSection(XmlDictionaryReaderQuotas.Max); - extWriter.WriteStartElement(Rss20Constants.ExtensionWrapperTag); + result.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), reader.Value); + } + else + { + TraceSyndicationElementIgnoredOnRead(reader); } - - await XmlReaderWrapper.WriteNodeAsync(extWriter, reader, false); - } - else - { - await reader.SkipAsync(); } } } } - //Add all read items to the feed - result.Items = feedItems; - LoadElementExtensions(buffer, extWriter, result); + reader.ReadStartElement(); } - catch (FormatException e) + + XmlBuffer buffer = null; + XmlDictionaryWriter extWriter = null; + bool areAllItemsRead = true; + NullNotAllowedCollection feedItems = null; + + if (!elementIsEmpty) { - throw new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingFeed), e); - } - catch (ArgumentException e) - { - throw new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingFeed), e); - } - finally - { - if (extWriter != null) + try { - ((IDisposable)extWriter).Dispose(); + while (reader.IsStartElement()) + { + if (TryParseFeedElementFrom(reader, result)) + { + // nothing, we parsed something, great + } + else if (reader.IsStartElement(Atom10Constants.EntryTag, Atom10Constants.Atom10Namespace) && !isSourceFeed) + { + feedItems = feedItems ?? new NullNotAllowedCollection(); + IEnumerable items = ReadItems(reader, result, out areAllItemsRead); + foreach(SyndicationItem item in items) + { + feedItems.Add(item); + } + + // if the derived class is reading the items lazily, then stop reading from the stream + if (!areAllItemsRead) + { + break; + } + } + else + { + if (!TryParseElement(reader, result, this.Version)) + { + if (_preserveElementExtensions) + { + CreateBufferIfRequiredAndWriteNode(ref buffer, ref extWriter, reader, _maxExtensionSize); + } + else + { + TraceSyndicationElementIgnoredOnRead(reader); + reader.Skip(); + } + } + } + } + + if (feedItems != null) + { + result.Items = feedItems; + } + + LoadElementExtensions(buffer, extWriter, result); + } + finally + { + if (extWriter != null) + { + ((IDisposable)extWriter).Dispose(); + } } } + if (!isSourceFeed && areAllItemsRead) + { + reader.ReadEndElement(); // feed + } } - if (!isSourceFeed) + catch (FormatException e) { - await reader.ReadEndElementAsync(); // feed + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingFeed), e)); + } + catch (ArgumentException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingFeed), e)); } - return result; } - private async Task ReadItemFromAsync(XmlReaderWrapper reader, SyndicationItem result, Uri feedBaseUri) + private void ReadItemFrom(XmlReader reader, SyndicationItem result, Uri feedBaseUri) { try { result.BaseUri = feedBaseUri; - await MoveToStartElementAsync(reader); + MoveToStartElement(reader); bool isEmpty = reader.IsEmptyElement; if (reader.HasAttributes) { @@ -939,36 +915,37 @@ namespace System.ServiceModel.Syndication string name = reader.LocalName; if (name == "base" && ns == XmlNs) { - result.BaseUri = FeedUtils.CombineXmlBase(result.BaseUri, await reader.GetValueAsync()); + result.BaseUri = FeedUtils.CombineXmlBase(result.BaseUri, reader.Value); continue; } - if (FeedUtils.IsXmlns(name, ns) || FeedUtils.IsXmlSchemaType(name, ns)) { continue; } - - string val = await reader.GetValueAsync(); + string val = reader.Value; if (!TryParseAttribute(name, ns, val, result, this.Version)) { if (_preserveAttributeExtensions) { result.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), reader.Value); } + else + { + TraceSyndicationElementIgnoredOnRead(reader); + } } } } - await reader.ReadStartElementAsync(); - + reader.ReadStartElement(); if (!isEmpty) { XmlBuffer buffer = null; XmlDictionaryWriter extWriter = null; try { - while (await reader.IsStartElementAsync()) + while (reader.IsStartElement()) { - if (await TryParseItemElementFromAsync(reader, result)) + if (TryParseItemElementFrom(reader, result)) { // nothing, we parsed something, great } @@ -978,13 +955,12 @@ namespace System.ServiceModel.Syndication { if (_preserveElementExtensions) { - var tuple = await CreateBufferIfRequiredAndWriteNodeAsync(buffer, extWriter, reader, _maxExtensionSize); - buffer = tuple.Item1; - extWriter = tuple.Item2; + CreateBufferIfRequiredAndWriteNode(ref buffer, ref extWriter, reader, _maxExtensionSize); } else { - await reader.SkipAsync(); + TraceSyndicationElementIgnoredOnRead(reader); + reader.Skip(); } } } @@ -998,20 +974,20 @@ namespace System.ServiceModel.Syndication ((IDisposable)extWriter).Dispose(); } } - await reader.ReadEndElementAsync(); // item + reader.ReadEndElement(); // item } } catch (FormatException e) { - throw new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingItem), e); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingItem), e)); } catch (ArgumentException e) { - throw new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingItem), e); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingItem), e)); } } - private async Task ReadLinkAsync(XmlReaderWrapper reader, SyndicationLink link, Uri baseUri) + private void ReadLink(XmlReader reader, SyndicationLink link, Uri baseUri) { bool isEmpty = reader.IsEmptyElement; string mediaType = null; @@ -1019,52 +995,44 @@ namespace System.ServiceModel.Syndication string title = null; string lengthStr = null; string val = null; - string ns = null; link.BaseUri = baseUri; if (reader.HasAttributes) { while (reader.MoveToNextAttribute()) { - bool notHandled = false; if (reader.LocalName == "base" && reader.NamespaceURI == XmlNs) { - link.BaseUri = FeedUtils.CombineXmlBase(link.BaseUri, await reader.GetValueAsync()); + link.BaseUri = FeedUtils.CombineXmlBase(link.BaseUri, reader.Value); } - else if (reader.NamespaceURI == string.Empty) + else if (reader.LocalName == Atom10Constants.TypeTag && reader.NamespaceURI == string.Empty) { - switch (reader.LocalName) - { - case Atom10Constants.TypeTag: - mediaType = await reader.GetValueAsync(); - break; - case Atom10Constants.RelativeTag: - relationship = await reader.GetValueAsync(); - break; - case Atom10Constants.TitleTag: - title = await reader.GetValueAsync(); - break; - case Atom10Constants.LengthTag: - lengthStr = await reader.GetValueAsync(); - break; - case Atom10Constants.HrefTag: - val = await reader.GetValueAsync(); - ns = reader.NamespaceURI; - break; - default: - notHandled = true; - break; - } + mediaType = reader.Value; } - else + else if (reader.LocalName == Atom10Constants.RelativeTag && reader.NamespaceURI == string.Empty) { - notHandled = true; + relationship = reader.Value; } - - if (notHandled && !FeedUtils.IsXmlns(reader.LocalName, reader.NamespaceURI)) + else if (reader.LocalName == Atom10Constants.TitleTag && reader.NamespaceURI == string.Empty) + { + title = reader.Value; + } + else if (reader.LocalName == Atom10Constants.LengthTag && reader.NamespaceURI == string.Empty) + { + lengthStr = reader.Value; + } + else if (reader.LocalName == Atom10Constants.HrefTag && reader.NamespaceURI == string.Empty) + { + val = reader.Value; + } + else if (!FeedUtils.IsXmlns(reader.LocalName, reader.NamespaceURI)) { if (_preserveAttributeExtensions) { - link.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), await reader.GetValueAsync()); + link.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), reader.Value); + } + else + { + TraceSyndicationElementIgnoredOnRead(reader); } } } @@ -1075,15 +1043,14 @@ namespace System.ServiceModel.Syndication { length = Convert.ToInt64(lengthStr, CultureInfo.InvariantCulture.NumberFormat); } - - await reader.ReadStartElementAsync(); + reader.ReadStartElement(); if (!isEmpty) { XmlBuffer buffer = null; XmlDictionaryWriter extWriter = null; try { - while (await reader.IsStartElementAsync()) + while (reader.IsStartElement()) { if (TryParseElement(reader, link, this.Version)) { @@ -1091,21 +1058,14 @@ namespace System.ServiceModel.Syndication } else if (!_preserveElementExtensions) { - await reader.SkipAsync(); + SyndicationFeedFormatter.TraceSyndicationElementIgnoredOnRead(reader); + reader.Skip(); } else { - if (buffer == null) - { - buffer = new XmlBuffer(_maxExtensionSize); - extWriter = buffer.OpenSection(XmlDictionaryReaderQuotas.Max); - extWriter.WriteStartElement(Rss20Constants.ExtensionWrapperTag); - } - - await XmlReaderWrapper.WriteNodeAsync(extWriter, reader, false); + CreateBufferIfRequiredAndWriteNode(ref buffer, ref extWriter, reader, _maxExtensionSize); } } - LoadElementExtensions(buffer, extWriter, link); } finally @@ -1115,46 +1075,44 @@ namespace System.ServiceModel.Syndication ((IDisposable)extWriter).Dispose(); } } - - await reader.ReadEndElementAsync(); + reader.ReadEndElement(); } - link.Length = length; link.MediaType = mediaType; link.RelationshipType = relationship; link.Title = title; - link.Uri = (val != null) ? uriParser(val, UriKind.RelativeOrAbsolute, Atom10Constants.LinkTag, ns) /*new Uri(val, UriKind.RelativeOrAbsolute)*/ : null; + link.Uri = (val != null) ? UriParser(val, UriKind.RelativeOrAbsolute, Atom10Constants.LinkTag, Atom10Constants.Atom10Namespace) : null; } - private async Task ReadLinkFromAsync(XmlReaderWrapper reader, SyndicationFeed feed) + private SyndicationLink ReadLinkFrom(XmlReader reader, SyndicationFeed feed) { SyndicationLink result = CreateLink(feed); - await ReadLinkAsync(reader, result, feed.BaseUri); + ReadLink(reader, result, feed.BaseUri); return result; } - private async Task ReadLinkFromAsync(XmlReaderWrapper reader, SyndicationItem item) + private SyndicationLink ReadLinkFrom(XmlReader reader, SyndicationItem item) { SyndicationLink result = CreateLink(item); - await ReadLinkAsync(reader, result, item.BaseUri); + ReadLink(reader, result, item.BaseUri); return result; } - private async Task ReadPersonFromAsync(XmlReaderWrapper reader, SyndicationFeed feed) + private SyndicationPerson ReadPersonFrom(XmlReader reader, SyndicationFeed feed) { SyndicationPerson result = CreatePerson(feed); - await ReadPersonFromAsync(reader, result); + ReadPersonFrom(reader, result); return result; } - private async Task ReadPersonFromAsync(XmlReaderWrapper reader, SyndicationItem item) + private SyndicationPerson ReadPersonFrom(XmlReader reader, SyndicationItem item) { SyndicationPerson result = CreatePerson(item); - await ReadPersonFromAsync(reader, result); + ReadPersonFrom(reader, result); return result; } - private async Task ReadPersonFromAsync(XmlReaderWrapper reader, SyndicationPerson result) + private void ReadPersonFrom(XmlReader reader, SyndicationPerson result) { bool isEmpty = reader.IsEmptyElement; if (reader.HasAttributes) @@ -1167,66 +1125,57 @@ namespace System.ServiceModel.Syndication { continue; } - string val = await reader.GetValueAsync(); + string val = reader.Value; if (!TryParseAttribute(name, ns, val, result, this.Version)) { if (_preserveAttributeExtensions) { - result.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), await reader.GetValueAsync()); + result.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), reader.Value); + } + else + { + TraceSyndicationElementIgnoredOnRead(reader); } } } } - - await reader.ReadStartElementAsync(); + reader.ReadStartElement(); if (!isEmpty) { XmlBuffer buffer = null; XmlDictionaryWriter extWriter = null; try { - while (await reader.IsStartElementAsync()) + while (reader.IsStartElement()) { - string name = reader.LocalName; - string ns = reader.NamespaceURI; - bool notHandled = false; - - switch (name) + if (reader.IsStartElement(Atom10Constants.NameTag, Atom10Constants.Atom10Namespace)) { - case Atom10Constants.NameTag: - result.Name = stringParser(await reader.ReadElementStringAsync(), Atom10Constants.NameTag, ns); - break; - case Atom10Constants.UriTag: - result.Uri = stringParser(await reader.ReadElementStringAsync(), Atom10Constants.UriTag, ns); - break; - case Atom10Constants.EmailTag: - result.Email = stringParser(await reader.ReadElementStringAsync(), Atom10Constants.EmailTag, ns); - break; - default: - notHandled = true; - break; + result.Name = reader.ReadElementString(); } - - if (notHandled && !TryParseElement(reader, result, this.Version)) + else if (reader.IsStartElement(Atom10Constants.UriTag, Atom10Constants.Atom10Namespace)) { - if (_preserveElementExtensions) + result.Uri = reader.ReadElementString(); + } + else if (reader.IsStartElement(Atom10Constants.EmailTag, Atom10Constants.Atom10Namespace)) + { + result.Email = reader.ReadElementString(); + } + else + { + if (!TryParseElement(reader, result, this.Version)) { - if (buffer == null) + if (_preserveElementExtensions) { - buffer = new XmlBuffer(_maxExtensionSize); - extWriter = buffer.OpenSection(XmlDictionaryReaderQuotas.Max); - extWriter.WriteStartElement(Rss20Constants.ExtensionWrapperTag); + CreateBufferIfRequiredAndWriteNode(ref buffer, ref extWriter, reader, _maxExtensionSize); + } + else + { + TraceSyndicationElementIgnoredOnRead(reader); + reader.Skip(); } - - await XmlReaderWrapper.WriteNodeAsync(extWriter, reader, false); - } - else - { - await reader.SkipAsync(); } } } - LoadElementExtensions(buffer, extWriter, result); } finally @@ -1236,47 +1185,45 @@ namespace System.ServiceModel.Syndication ((IDisposable)extWriter).Dispose(); } } - - await reader.ReadEndElementAsync(); + reader.ReadEndElement(); } } - private Task ReadTextContentFromAsync(XmlReaderWrapper reader, string context) + private TextSyndicationContent ReadTextContentFrom(XmlReader reader, string context) { - return ReadTextContentFromAsync(reader, context, this.PreserveAttributeExtensions); + return ReadTextContentFrom(reader, context, this.PreserveAttributeExtensions); } - private async Task WriteCategoriesToAsync(XmlWriter writer, Collection categories) + private void WriteCategoriesTo(XmlWriter writer, Collection categories) { - writer = XmlWriterWrapper.CreateFromWriter(writer); for (int i = 0; i < categories.Count; ++i) { - await WriteCategoryAsync(writer, categories[i], this.Version); + WriteCategory(writer, categories[i], this.Version); } } - private Task WriteFeedAsync(XmlWriter writer) + private void WriteFeed(XmlWriter writer) { if (this.Feed == null) { - throw new InvalidOperationException(SR.FeedFormatterDoesNotHaveFeed); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.FeedFormatterDoesNotHaveFeed))); } - return WriteFeedToAsync(writer, this.Feed, false); // isSourceFeed + WriteFeedTo(writer, this.Feed, false); // isSourceFeed } - private async Task WriteFeedToAsync(XmlWriter writer, SyndicationFeed feed, bool isSourceFeed) + private void WriteFeedTo(XmlWriter writer, SyndicationFeed feed, bool isSourceFeed) { if (!isSourceFeed) { if (!string.IsNullOrEmpty(feed.Language)) { - await writer.WriteAttributeStringAsync("xml", "lang", XmlNs, feed.Language); + writer.WriteAttributeString("xml", "lang", XmlNs, feed.Language); } if (feed.BaseUri != null) { - await writer.InternalWriteAttributeStringAsync("xml", "base", XmlNs, FeedUtils.GetUriString(feed.BaseUri)); + writer.WriteAttributeString("xml", "base", XmlNs, FeedUtils.GetUriString(feed.BaseUri)); } - await WriteAttributeExtensionsAsync(writer, feed, this.Version); + WriteAttributeExtensions(writer, feed, this.Version); } bool isElementRequired = !isSourceFeed; TextSyndicationContent title = feed.Title; @@ -1284,98 +1231,93 @@ namespace System.ServiceModel.Syndication { title = title ?? new TextSyndicationContent(string.Empty); } - await WriteContentToAsync(writer, Atom10Constants.TitleTag, title); - await WriteContentToAsync(writer, Atom10Constants.SubtitleTag, feed.Description); + WriteContentTo(writer, Atom10Constants.TitleTag, title); + WriteContentTo(writer, Atom10Constants.SubtitleTag, feed.Description); string id = feed.Id; if (isElementRequired) { id = id ?? s_idGenerator.Next(); } - await WriteElementAsync(writer, Atom10Constants.IdTag, id); - await WriteContentToAsync(writer, Atom10Constants.RightsTag, feed.Copyright); - await WriteFeedLastUpdatedTimeToAsync(writer, feed.LastUpdatedTime, isElementRequired); - await WriteCategoriesToAsync(writer, feed.Categories); + WriteElement(writer, Atom10Constants.IdTag, id); + WriteContentTo(writer, Atom10Constants.RightsTag, feed.Copyright); + WriteFeedLastUpdatedTimeTo(writer, feed.LastUpdatedTime, isElementRequired); + WriteCategoriesTo(writer, feed.Categories); if (feed.ImageUrl != null) { - await WriteElementAsync(writer, Atom10Constants.LogoTag, feed.ImageUrl.ToString()); - } - await WriteFeedAuthorsToAsync(writer, feed.Authors); - await WriteFeedContributorsToAsync(writer, feed.Contributors); - await WriteElementAsync(writer, Atom10Constants.GeneratorTag, feed.Generator); - - if (feed.IconImage != null) - { - await WriteElementAsync(writer, Atom10Constants.IconTag, feed.IconImage.AbsoluteUri); + WriteElement(writer, Atom10Constants.LogoTag, feed.ImageUrl.ToString()); } + WriteFeedAuthorsTo(writer, feed.Authors); + WriteFeedContributorsTo(writer, feed.Contributors); + WriteElement(writer, Atom10Constants.GeneratorTag, feed.Generator); for (int i = 0; i < feed.Links.Count; ++i) { - await WriteLinkAsync(writer, feed.Links[i], feed.BaseUri); + WriteLink(writer, feed.Links[i], feed.BaseUri); } - await WriteElementExtensionsAsync(writer, feed, this.Version); + WriteElementExtensions(writer, feed, this.Version); if (!isSourceFeed) { - await WriteItemsAsync(writer, feed.Items, feed.BaseUri); + WriteItems(writer, feed.Items, feed.BaseUri); } } - private async Task WriteItemContentsAsync(XmlWriter dictWriter, SyndicationItem item, Uri feedBaseUri) + private void WriteItemContents(XmlWriter dictWriter, SyndicationItem item, Uri feedBaseUri) { Uri baseUriToWrite = FeedUtils.GetBaseUriToWrite(feedBaseUri, item.BaseUri); if (baseUriToWrite != null) { - await dictWriter.InternalWriteAttributeStringAsync("xml", "base", XmlNs, FeedUtils.GetUriString(baseUriToWrite)); + dictWriter.WriteAttributeString("xml", "base", XmlNs, FeedUtils.GetUriString(baseUriToWrite)); } - await WriteAttributeExtensionsAsync(dictWriter, item, this.Version); + WriteAttributeExtensions(dictWriter, item, this.Version); string id = item.Id ?? s_idGenerator.Next(); - await WriteElementAsync(dictWriter, Atom10Constants.IdTag, id); + WriteElement(dictWriter, Atom10Constants.IdTag, id); TextSyndicationContent title = item.Title ?? new TextSyndicationContent(string.Empty); - await WriteContentToAsync(dictWriter, Atom10Constants.TitleTag, title); - await WriteContentToAsync(dictWriter, Atom10Constants.SummaryTag, item.Summary); + WriteContentTo(dictWriter, Atom10Constants.TitleTag, title); + WriteContentTo(dictWriter, Atom10Constants.SummaryTag, item.Summary); if (item.PublishDate != DateTimeOffset.MinValue) { - await dictWriter.WriteElementStringAsync(Atom10Constants.PublishedTag, + dictWriter.WriteElementString(Atom10Constants.PublishedTag, Atom10Constants.Atom10Namespace, AsString(item.PublishDate)); } - await WriteItemLastUpdatedTimeToAsync(dictWriter, item.LastUpdatedTime); - await WriteItemAuthorsToAsync(dictWriter, item.Authors); - await WriteItemContributorsToAsync(dictWriter, item.Contributors); + WriteItemLastUpdatedTimeTo(dictWriter, item.LastUpdatedTime); + WriteItemAuthorsTo(dictWriter, item.Authors); + WriteItemContributorsTo(dictWriter, item.Contributors); for (int i = 0; i < item.Links.Count; ++i) { - await WriteLinkAsync(dictWriter, item.Links[i], item.BaseUri); + WriteLink(dictWriter, item.Links[i], item.BaseUri); } - await WriteCategoriesToAsync(dictWriter, item.Categories); - await WriteContentToAsync(dictWriter, Atom10Constants.ContentTag, item.Content); - await WriteContentToAsync(dictWriter, Atom10Constants.RightsTag, item.Copyright); + WriteCategoriesTo(dictWriter, item.Categories); + WriteContentTo(dictWriter, Atom10Constants.ContentTag, item.Content); + WriteContentTo(dictWriter, Atom10Constants.RightsTag, item.Copyright); if (item.SourceFeed != null) { - await dictWriter.WriteStartElementAsync(Atom10Constants.SourceFeedTag, Atom10Constants.Atom10Namespace); - await WriteFeedToAsync(dictWriter, item.SourceFeed, true); // isSourceFeed - await dictWriter.WriteEndElementAsync(); + dictWriter.WriteStartElement(Atom10Constants.SourceFeedTag, Atom10Constants.Atom10Namespace); + WriteFeedTo(dictWriter, item.SourceFeed, true); // isSourceFeed + dictWriter.WriteEndElement(); } - await WriteElementExtensionsAsync(dictWriter, item, this.Version); + WriteElementExtensions(dictWriter, item, this.Version); } - private async Task WritePersonToAsync(XmlWriter writer, SyndicationPerson p, string elementName) + private void WritePersonTo(XmlWriter writer, SyndicationPerson p, string elementName) { - await writer.WriteStartElementAsync(elementName, Atom10Constants.Atom10Namespace); - await WriteAttributeExtensionsAsync(writer, p, this.Version); - await WriteElementAsync(writer, Atom10Constants.NameTag, p.Name); + writer.WriteStartElement(elementName, Atom10Constants.Atom10Namespace); + WriteAttributeExtensions(writer, p, this.Version); + WriteElement(writer, Atom10Constants.NameTag, p.Name); if (!string.IsNullOrEmpty(p.Uri)) { - await writer.WriteElementStringAsync(Atom10Constants.UriTag, Atom10Constants.Atom10Namespace, p.Uri); + writer.WriteElementString(Atom10Constants.UriTag, Atom10Constants.Atom10Namespace, p.Uri); } if (!string.IsNullOrEmpty(p.Email)) { - await writer.WriteElementStringAsync(Atom10Constants.EmailTag, Atom10Constants.Atom10Namespace, p.Email); + writer.WriteElementString(Atom10Constants.EmailTag, Atom10Constants.Atom10Namespace, p.Email); } - await WriteElementExtensionsAsync(writer, p, this.Version); - await writer.WriteEndElementAsync(); + WriteElementExtensions(writer, p, this.Version); + writer.WriteEndElement(); } } diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Atom10ItemFormatter.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Atom10ItemFormatter.cs index 2e6774cc1f..c3b3aa6eaa 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Atom10ItemFormatter.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Atom10ItemFormatter.cs @@ -2,24 +2,21 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. - -/* - * Some diagnostic lines have been commented - * - * */ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Globalization; +using System.Text; +using System.Xml; +using System.Xml.Serialization; +using System.Diagnostics.CodeAnalysis; +using System.Xml.Schema; +using System.Runtime.CompilerServices; namespace System.ServiceModel.Syndication { - using System; - using System.Diagnostics.CodeAnalysis; - using System.Runtime.CompilerServices; - using System.Threading.Tasks; - using System.Xml; - using System.Xml.Schema; - using System.Xml.Serialization; - [XmlRoot(ElementName = Atom10Constants.EntryTag, Namespace = Atom10Constants.Atom10Namespace)] - public class Atom10ItemFormatter : SyndicationItemFormatter + public class Atom10ItemFormatter : SyndicationItemFormatter, IXmlSerializable { private Atom10FeedFormatter _feedSerializer; private Type _itemType; @@ -36,11 +33,12 @@ namespace System.ServiceModel.Syndication { if (itemTypeToCreate == null) { - throw new ArgumentNullException(nameof(itemTypeToCreate)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("itemTypeToCreate"); } if (!typeof(SyndicationItem).IsAssignableFrom(itemTypeToCreate)) { - throw new ArgumentException(string.Format(SR.InvalidObjectTypePassed, nameof(itemTypeToCreate), nameof(SyndicationItem))); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("itemTypeToCreate", + SR.Format(SR.InvalidObjectTypePassed, "itemTypeToCreate", "SyndicationItem")); } _feedSerializer = new Atom10FeedFormatter(); _feedSerializer.PreserveAttributeExtensions = _preserveAttributeExtensions = true; @@ -95,33 +93,63 @@ namespace System.ServiceModel.Syndication { if (reader == null) { - throw new ArgumentNullException(nameof(reader)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); } return reader.IsStartElement(Atom10Constants.EntryTag, Atom10Constants.Atom10Namespace); } - public override Task ReadFromAsync(XmlReader reader) + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "The IXmlSerializable implementation is only for exposing under WCF DataContractSerializer. The funcionality is exposed to derived class through the ReadFrom\\WriteTo methods")] + XmlSchema IXmlSerializable.GetSchema() { - if (!CanRead(reader)) - { - throw new XmlException(string.Format(SR.UnknownItemXml, reader.LocalName, reader.NamespaceURI)); - } - - return ReadItemAsync(reader); + return null; } - public override async Task WriteToAsync(XmlWriter writer) + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "The IXmlSerializable implementation is only for exposing under WCF DataContractSerializer. The funcionality is exposed to derived class through the ReadFrom\\WriteTo methods")] + void IXmlSerializable.ReadXml(XmlReader reader) + { + if (reader == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); + } + SyndicationFeedFormatter.TraceItemReadBegin(); + ReadItem(reader); + SyndicationFeedFormatter.TraceItemReadEnd(); + } + + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "The IXmlSerializable implementation is only for exposing under WCF DataContractSerializer. The funcionality is exposed to derived class through the ReadFrom\\WriteTo methods")] + void IXmlSerializable.WriteXml(XmlWriter writer) { if (writer == null) { - throw new ArgumentNullException(nameof(writer)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); } + SyndicationFeedFormatter.TraceItemWriteBegin(); + WriteItem(writer); + SyndicationFeedFormatter.TraceItemWriteEnd(); + } - writer = XmlWriterWrapper.CreateFromWriter(writer); + public override void ReadFrom(XmlReader reader) + { + SyndicationFeedFormatter.TraceItemReadBegin(); + if (!CanRead(reader)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.UnknownItemXml, reader.LocalName, reader.NamespaceURI))); + } + ReadItem(reader); + SyndicationFeedFormatter.TraceItemReadEnd(); + } - await writer.WriteStartElementAsync(Atom10Constants.EntryTag, Atom10Constants.Atom10Namespace); - await WriteItemAsync(writer); - await writer.WriteEndElementAsync(); + public override void WriteTo(XmlWriter writer) + { + if (writer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); + } + SyndicationFeedFormatter.TraceItemWriteBegin(); + writer.WriteStartElement(Atom10Constants.EntryTag, Atom10Constants.Atom10Namespace); + WriteItem(writer); + writer.WriteEndElement(); + SyndicationFeedFormatter.TraceItemWriteEnd(); } protected override SyndicationItem CreateItemInstance() @@ -129,20 +157,20 @@ namespace System.ServiceModel.Syndication return SyndicationItemFormatter.CreateItemInstance(_itemType); } - private Task ReadItemAsync(XmlReader reader) + private void ReadItem(XmlReader reader) { SetItem(CreateItemInstance()); - return _feedSerializer.ReadItemFromAsync(XmlReaderWrapper.CreateFromReader(XmlDictionaryReader.CreateDictionaryReader(reader)), this.Item); + _feedSerializer.ReadItemFrom(XmlDictionaryReader.CreateDictionaryReader(reader), this.Item); } - private Task WriteItemAsync(XmlWriter writer) + private void WriteItem(XmlWriter writer) { if (this.Item == null) { - throw new InvalidOperationException(SR.ItemFormatterDoesNotHaveItem); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.ItemFormatterDoesNotHaveItem))); } XmlDictionaryWriter w = XmlDictionaryWriter.CreateDictionaryWriter(writer); - return _feedSerializer.WriteItemContentsAsync(w, this.Item); + _feedSerializer.WriteItemContents(w, this.Item); } } diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/AtomPub10CategoriesDocumentFormatter.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/AtomPub10CategoriesDocumentFormatter.cs index a1f12f3568..86bff74014 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/AtomPub10CategoriesDocumentFormatter.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/AtomPub10CategoriesDocumentFormatter.cs @@ -2,17 +2,21 @@ // 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.Collections.ObjectModel; +using System.Runtime.Serialization; +using System.Xml.Serialization; +using System.Collections.Generic; +using System.Xml; +using System.Xml.Schema; +using System.ServiceModel.Channels; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; namespace System.ServiceModel.Syndication { - using System; - using System.Runtime.CompilerServices; - using System.Threading.Tasks; - using System.Xml; - using System.Xml.Serialization; - [XmlRoot(ElementName = App10Constants.Categories, Namespace = App10Constants.Namespace)] - public class AtomPub10CategoriesDocumentFormatter : CategoriesDocumentFormatter + public class AtomPub10CategoriesDocumentFormatter : CategoriesDocumentFormatter, IXmlSerializable { private Type _inlineDocumentType; private int _maxExtensionSize; @@ -30,24 +34,22 @@ namespace System.ServiceModel.Syndication { if (inlineDocumentType == null) { - throw new ArgumentNullException(nameof(inlineDocumentType)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("inlineDocumentType"); } - if (!typeof(InlineCategoriesDocument).IsAssignableFrom(inlineDocumentType)) { - throw new ArgumentException(string.Format(SR.InvalidObjectTypePassed, nameof(inlineDocumentType), nameof(InlineCategoriesDocument))); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("inlineDocumentType", + SR.Format(SR.InvalidObjectTypePassed, "inlineDocumentType", "InlineCategoriesDocument")); } - if (referencedDocumentType == null) { - throw new ArgumentNullException(nameof(referencedDocumentType)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("referencedDocumentType"); } - if (!typeof(ReferencedCategoriesDocument).IsAssignableFrom(referencedDocumentType)) { - throw new ArgumentException(string.Format(SR.InvalidObjectTypePassed, nameof(referencedDocumentType), nameof(ReferencedCategoriesDocument))); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("referencedDocumentType", + SR.Format(SR.InvalidObjectTypePassed, "referencedDocumentType", "ReferencedCategoriesDocument")); } - _maxExtensionSize = int.MaxValue; _preserveAttributeExtensions = true; _preserveElementExtensions = true; @@ -79,74 +81,95 @@ namespace System.ServiceModel.Syndication get { return App10Constants.Namespace; } } - public override Task CanReadAsync(XmlReader reader) + public override bool CanRead(XmlReader reader) { if (reader == null) { - throw new ArgumentNullException(nameof(reader)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); } - - XmlReaderWrapper wrappedReader = XmlReaderWrapper.CreateFromReader(reader); - return wrappedReader.IsStartElementAsync(App10Constants.Categories, App10Constants.Namespace); + return reader.IsStartElement(App10Constants.Categories, App10Constants.Namespace); } + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "The IXmlSerializable implementation is only for exposing under WCF DataContractSerializer. The funcionality is exposed to derived class through the ReadFrom\\WriteTo methods")] + XmlSchema IXmlSerializable.GetSchema() + { + return null; + } - - private Task ReadXmlAsync(XmlReaderWrapper reader) + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "The IXmlSerializable implementation is only for exposing under WCF DataContractSerializer. The funcionality is exposed to derived class through the ReadFrom\\WriteTo methods")] + void IXmlSerializable.ReadXml(XmlReader reader) { if (reader == null) { - throw new ArgumentNullException(nameof(reader)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); } - - return ReadDocumentAsync(reader); + TraceCategoriesDocumentReadBegin(); + ReadDocument(reader); + TraceCategoriesDocumentReadEnd(); } - private Task WriteXmlAsync(XmlWriter writer) + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "The IXmlSerializable implementation is only for exposing under WCF DataContractSerializer. The funcionality is exposed to derived class through the ReadFrom\\WriteTo methods")] + void IXmlSerializable.WriteXml(XmlWriter writer) { if (writer == null) { - throw new ArgumentNullException(nameof(writer)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); } - if (this.Document == null) { - throw new InvalidOperationException(SR.DocumentFormatterDoesNotHaveDocument); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.DocumentFormatterDoesNotHaveDocument))); } - - return WriteDocumentAsync(writer); + TraceCategoriesDocumentWriteBegin(); + WriteDocument(writer); + TraceCategoriesDocumentWriteEnd(); } - public override async Task ReadFromAsync(XmlReader reader) + public override void ReadFrom(XmlReader reader) { if (reader == null) { - throw new ArgumentNullException(nameof(reader)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); } - - if (!await CanReadAsync(reader)) + if (!CanRead(reader)) { - throw new XmlException(string.Format(SR.UnknownDocumentXml, reader.LocalName, reader.NamespaceURI)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.UnknownDocumentXml, reader.LocalName, reader.NamespaceURI))); } - - await ReadDocumentAsync(XmlReaderWrapper.CreateFromReader(reader)); + TraceCategoriesDocumentReadBegin(); + ReadDocument(reader); + TraceCategoriesDocumentReadEnd(); } - public override async Task WriteTo(XmlWriter writer) + public override void WriteTo(XmlWriter writer) { if (writer == null) { - throw new ArgumentNullException(nameof(writer)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); } - if (this.Document == null) { - throw new InvalidOperationException(SR.DocumentFormatterDoesNotHaveDocument); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.DocumentFormatterDoesNotHaveDocument))); } - + TraceCategoriesDocumentWriteBegin(); writer.WriteStartElement(App10Constants.Prefix, App10Constants.Categories, App10Constants.Namespace); - await WriteDocumentAsync(writer); + WriteDocument(writer); writer.WriteEndElement(); + TraceCategoriesDocumentWriteEnd(); + } + + internal static void TraceCategoriesDocumentReadBegin() + { + } + + internal static void TraceCategoriesDocumentReadEnd() + { + } + + internal static void TraceCategoriesDocumentWriteBegin() + { + } + + internal static void TraceCategoriesDocumentWriteEnd() + { } protected override InlineCategoriesDocument CreateInlineCategoriesDocument() @@ -173,12 +196,12 @@ namespace System.ServiceModel.Syndication } } - private async Task ReadDocumentAsync(XmlReaderWrapper reader) + private void ReadDocument(XmlReader reader) { try { - await SyndicationFeedFormatter.MoveToStartElementAsync(reader); - SetDocument(await AtomPub10ServiceDocumentFormatter.ReadCategories(reader, null, + SyndicationFeedFormatter.MoveToStartElement(reader); + SetDocument(AtomPub10ServiceDocumentFormatter.ReadCategories(reader, null, delegate () { return this.CreateInlineCategoriesDocument(); @@ -195,19 +218,19 @@ namespace System.ServiceModel.Syndication } catch (FormatException e) { - throw new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingDocument), e); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingDocument), e)); } catch (ArgumentException e) { - throw new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingDocument), e); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingDocument), e)); } } - private Task WriteDocumentAsync(XmlWriter writer) + private void WriteDocument(XmlWriter writer) { // declare the atom10 namespace upfront for compactness writer.WriteAttributeString(Atom10Constants.Atom10Prefix, Atom10FeedFormatter.XmlNsNs, Atom10Constants.Atom10Namespace); - return AtomPub10ServiceDocumentFormatter.WriteCategoriesInnerXml(writer, this.Document, null, this.Version); + AtomPub10ServiceDocumentFormatter.WriteCategoriesInnerXml(writer, this.Document, null, this.Version); } } } diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/AtomPub10ServiceDocumentFormatter.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/AtomPub10ServiceDocumentFormatter.cs index d114695ef2..ee670f1aea 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/AtomPub10ServiceDocumentFormatter.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/AtomPub10ServiceDocumentFormatter.cs @@ -2,21 +2,24 @@ // 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.Collections.ObjectModel; +using System.Runtime.Serialization; +using System.Xml.Serialization; +using System.Collections.Generic; +using System.Xml; +using System.Xml.Schema; +using System.ServiceModel.Channels; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + namespace System.ServiceModel.Syndication { - using System; - using System.Diagnostics.CodeAnalysis; - using System.Runtime.CompilerServices; - using System.Threading.Tasks; - using System.Xml; - using System.Xml.Schema; - using System.Xml.Serialization; - internal delegate InlineCategoriesDocument CreateInlineCategoriesDelegate(); internal delegate ReferencedCategoriesDocument CreateReferencedCategoriesDelegate(); [XmlRoot(ElementName = App10Constants.Service, Namespace = App10Constants.Namespace)] - public class AtomPub10ServiceDocumentFormatter : ServiceDocumentFormatter + public class AtomPub10ServiceDocumentFormatter : ServiceDocumentFormatter, IXmlSerializable { private Type _documentType; private int _maxExtensionSize; @@ -33,11 +36,12 @@ namespace System.ServiceModel.Syndication { if (documentTypeToCreate == null) { - throw new ArgumentNullException(nameof(documentTypeToCreate)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("documentTypeToCreate"); } if (!typeof(ServiceDocument).IsAssignableFrom(documentTypeToCreate)) { - throw new ArgumentException(string.Format(SR.InvalidObjectTypePassed, nameof(documentTypeToCreate), nameof(ServiceDocument))); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("documentTypeToCreate", + SR.Format(SR.InvalidObjectTypePassed, "documentTypeToCreate", "ServiceDocument")); } _maxExtensionSize = int.MaxValue; _preserveAttributeExtensions = true; @@ -60,114 +64,129 @@ namespace System.ServiceModel.Syndication get { return App10Constants.Namespace; } } - public override Task CanReadAsync(XmlReader reader) + public override bool CanRead(XmlReader reader) { if (reader == null) { - throw new ArgumentNullException(nameof(reader)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); } - - XmlReaderWrapper readerWrapper = XmlReaderWrapper.CreateFromReader(reader); - return readerWrapper.IsStartElementAsync(App10Constants.Service, App10Constants.Namespace); + return reader.IsStartElement(App10Constants.Service, App10Constants.Namespace); } - private Task ReadXml(XmlReaderWrapper reader) + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "The IXmlSerializable implementation is only for exposing under WCF DataContractSerializer. The funcionality is exposed to derived class through the ReadFrom\\WriteTo methods")] + XmlSchema IXmlSerializable.GetSchema() + { + return null; + } + + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "The IXmlSerializable implementation is only for exposing under WCF DataContractSerializer. The funcionality is exposed to derived class through the ReadFrom\\WriteTo methods")] + void IXmlSerializable.ReadXml(XmlReader reader) { if (reader == null) { - throw new ArgumentNullException(nameof(reader)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); } - - return ReadDocumentAsync(reader); + TraceServiceDocumentReadBegin(); + ReadDocument(reader); + TraceServiceDocumentReadEnd(); } - private Task WriteXml(XmlWriter writer) + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "The IXmlSerializable implementation is only for exposing under WCF DataContractSerializer. The funcionality is exposed to derived class through the ReadFrom\\WriteTo methods")] + void IXmlSerializable.WriteXml(XmlWriter writer) { if (writer == null) { - throw new ArgumentNullException(nameof(writer)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); } - if (this.Document == null) { - throw new InvalidOperationException(SR.DocumentFormatterDoesNotHaveDocument); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.DocumentFormatterDoesNotHaveDocument))); } - - return WriteDocumentAsync(XmlWriterWrapper.CreateFromWriter(writer)); + TraceServiceDocumentWriteBegin(); + WriteDocument(writer); + TraceServiceDocumentWriteEnd(); } - public override async Task ReadFromAsync(XmlReader reader) + public override void ReadFrom(XmlReader reader) { if (reader == null) { - throw new ArgumentNullException(nameof(reader)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); } - - XmlReaderWrapper wrappedReader = XmlReaderWrapper.CreateFromReader(reader); - await wrappedReader.MoveToContentAsync(); - - if (!await CanReadAsync(reader)) + reader.MoveToContent(); + if (!CanRead(reader)) { - throw new XmlException(string.Format(SR.UnknownDocumentXml, reader.LocalName, reader.NamespaceURI)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.UnknownDocumentXml, reader.LocalName, reader.NamespaceURI))); } - - await ReadDocumentAsync(wrappedReader); + TraceServiceDocumentReadBegin(); + ReadDocument(reader); + TraceServiceDocumentReadEnd(); } - public override async Task WriteToAsync(XmlWriter writer) + public override void WriteTo(XmlWriter writer) { if (writer == null) { - throw new ArgumentNullException(nameof(writer)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); } - if (this.Document == null) { - throw new InvalidOperationException(SR.DocumentFormatterDoesNotHaveDocument); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.DocumentFormatterDoesNotHaveDocument))); } - - writer = XmlWriterWrapper.CreateFromWriter(writer); - - await writer.WriteStartElementAsync(App10Constants.Prefix, App10Constants.Service, App10Constants.Namespace); - await WriteDocumentAsync(writer); - await writer.WriteEndElementAsync(); + TraceServiceDocumentWriteBegin(); + writer.WriteStartElement(App10Constants.Prefix, App10Constants.Service, App10Constants.Namespace); + WriteDocument(writer); + writer.WriteEndElement(); + TraceServiceDocumentWriteEnd(); } - internal static async Task ReadCategories(XmlReaderWrapper reader, Uri baseUri, CreateInlineCategoriesDelegate inlineCategoriesFactory, CreateReferencedCategoriesDelegate referencedCategoriesFactory, string version, bool preserveElementExtensions, bool preserveAttributeExtensions, int maxExtensionSize) + internal static CategoriesDocument ReadCategories(XmlReader reader, Uri baseUri, CreateInlineCategoriesDelegate inlineCategoriesFactory, CreateReferencedCategoriesDelegate referencedCategoriesFactory, string version, bool preserveElementExtensions, bool preserveAttributeExtensions, int maxExtensionSize) { string link = reader.GetAttribute(App10Constants.Href, string.Empty); if (string.IsNullOrEmpty(link)) { InlineCategoriesDocument inlineCategories = inlineCategoriesFactory(); - await ReadInlineCategoriesAsync(reader, inlineCategories, baseUri, version, preserveElementExtensions, preserveAttributeExtensions, maxExtensionSize); + ReadInlineCategories(reader, inlineCategories, baseUri, version, preserveElementExtensions, preserveAttributeExtensions, maxExtensionSize); return inlineCategories; } else { ReferencedCategoriesDocument referencedCategories = referencedCategoriesFactory(); - await ReadReferencedCategoriesAsync(reader, referencedCategories, baseUri, new Uri(link, UriKind.RelativeOrAbsolute), version, preserveElementExtensions, preserveAttributeExtensions, maxExtensionSize); + ReadReferencedCategories(reader, referencedCategories, baseUri, new Uri(link, UriKind.RelativeOrAbsolute), version, preserveElementExtensions, preserveAttributeExtensions, maxExtensionSize); return referencedCategories; } } + internal static void TraceServiceDocumentReadBegin() + { + } + internal static void TraceServiceDocumentReadEnd() + { + } - internal static async Task WriteCategoriesInnerXml(XmlWriter writer, CategoriesDocument categories, Uri baseUri, string version) + internal static void TraceServiceDocumentWriteBegin() + { + } + + internal static void TraceServiceDocumentWriteEnd() + { + } + + internal static void WriteCategoriesInnerXml(XmlWriter writer, CategoriesDocument categories, Uri baseUri, string version) { Uri baseUriToWrite = FeedUtils.GetBaseUriToWrite(baseUri, categories.BaseUri); if (baseUriToWrite != null) { WriteXmlBase(writer, baseUriToWrite); } - if (!string.IsNullOrEmpty(categories.Language)) { WriteXmlLang(writer, categories.Language); } - if (categories.IsInline) { - await WriteInlineCategoriesContentAsync(XmlWriterWrapper.CreateFromWriter(writer), (InlineCategoriesDocument)categories, version); + WriteInlineCategoriesContent(writer, (InlineCategoriesDocument)categories, version); } else { @@ -187,7 +206,7 @@ namespace System.ServiceModel.Syndication } } - private static async Task ReadInlineCategoriesAsync(XmlReaderWrapper reader, InlineCategoriesDocument inlineCategories, Uri baseUri, string version, bool preserveElementExtensions, bool preserveAttributeExtensions, int _maxExtensionSize) + private static void ReadInlineCategories(XmlReader reader, InlineCategoriesDocument inlineCategories, Uri baseUri, string version, bool preserveElementExtensions, bool preserveAttributeExtensions, int maxExtensionSize) { inlineCategories.BaseUri = baseUri; if (reader.HasAttributes) @@ -196,11 +215,11 @@ namespace System.ServiceModel.Syndication { if (reader.LocalName == "base" && reader.NamespaceURI == Atom10FeedFormatter.XmlNs) { - inlineCategories.BaseUri = FeedUtils.CombineXmlBase(inlineCategories.BaseUri, await reader.GetValueAsync()); + inlineCategories.BaseUri = FeedUtils.CombineXmlBase(inlineCategories.BaseUri, reader.Value); } else if (reader.LocalName == "lang" && reader.NamespaceURI == Atom10FeedFormatter.XmlNs) { - inlineCategories.Language = await reader.GetValueAsync(); + inlineCategories.Language = reader.Value; } else if (reader.LocalName == App10Constants.Fixed && reader.NamespaceURI == string.Empty) { @@ -208,7 +227,7 @@ namespace System.ServiceModel.Syndication } else if (reader.LocalName == Atom10Constants.SchemeTag && reader.NamespaceURI == string.Empty) { - inlineCategories.Scheme = await reader.GetValueAsync(); + inlineCategories.Scheme = reader.Value; } else { @@ -218,51 +237,52 @@ namespace System.ServiceModel.Syndication { continue; } - string val = await reader.GetValueAsync(); + string val = reader.Value; if (!TryParseAttribute(name, ns, val, inlineCategories, version)) { if (preserveAttributeExtensions) { - inlineCategories.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), await reader.GetValueAsync()); + inlineCategories.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), reader.Value); + } + else + { + SyndicationFeedFormatter.TraceSyndicationElementIgnoredOnRead(reader); } } } } } - - await SyndicationFeedFormatter.MoveToStartElementAsync(reader); + SyndicationFeedFormatter.MoveToStartElement(reader); bool isEmptyElement = reader.IsEmptyElement; - await reader.ReadStartElementAsync(); + reader.ReadStartElement(); if (!isEmptyElement) { XmlBuffer buffer = null; XmlDictionaryWriter extWriter = null; try { - while (await reader.IsStartElementAsync()) + while (reader.IsStartElement()) { - if (await reader.IsStartElementAsync(Atom10Constants.CategoryTag, Atom10Constants.Atom10Namespace)) + if (reader.IsStartElement(Atom10Constants.CategoryTag, Atom10Constants.Atom10Namespace)) { SyndicationCategory category = CreateCategory(inlineCategories); - await Atom10FeedFormatter.ReadCategoryAsync(reader, category, version, preserveAttributeExtensions, preserveElementExtensions, _maxExtensionSize); + Atom10FeedFormatter.ReadCategory(reader, category, version, preserveAttributeExtensions, preserveElementExtensions, maxExtensionSize); if (category.Scheme == null) { category.Scheme = inlineCategories.Scheme; } - inlineCategories.Categories.Add(category); } else if (!TryParseElement(reader, inlineCategories, version)) { if (preserveElementExtensions) { - var tuple = await SyndicationFeedFormatter.CreateBufferIfRequiredAndWriteNodeAsync(buffer, extWriter, reader, _maxExtensionSize); - buffer = tuple.Item1; - extWriter = tuple.Item2; + SyndicationFeedFormatter.CreateBufferIfRequiredAndWriteNode(ref buffer, ref extWriter, reader, maxExtensionSize); } else { - await reader.SkipAsync(); + SyndicationFeedFormatter.TraceSyndicationElementIgnoredOnRead(reader); + reader.Skip(); } } } @@ -275,12 +295,11 @@ namespace System.ServiceModel.Syndication extWriter.Close(); } } - - await reader.ReadEndElementAsync(); + reader.ReadEndElement(); } } - private static async Task ReadReferencedCategoriesAsync(XmlReaderWrapper reader, ReferencedCategoriesDocument referencedCategories, Uri baseUri, Uri link, string version, bool preserveElementExtensions, bool preserveAttributeExtensions, int maxExtensionSize) + private static void ReadReferencedCategories(XmlReader reader, ReferencedCategoriesDocument referencedCategories, Uri baseUri, Uri link, string version, bool preserveElementExtensions, bool preserveAttributeExtensions, int maxExtensionSize) { referencedCategories.BaseUri = baseUri; referencedCategories.Link = link; @@ -290,11 +309,11 @@ namespace System.ServiceModel.Syndication { if (reader.LocalName == "base" && reader.NamespaceURI == Atom10FeedFormatter.XmlNs) { - referencedCategories.BaseUri = FeedUtils.CombineXmlBase(referencedCategories.BaseUri, await reader.GetValueAsync()); + referencedCategories.BaseUri = FeedUtils.CombineXmlBase(referencedCategories.BaseUri, reader.Value); } else if (reader.LocalName == "lang" && reader.NamespaceURI == Atom10FeedFormatter.XmlNs) { - referencedCategories.Language = await reader.GetValueAsync(); + referencedCategories.Language = reader.Value; } else if (reader.LocalName == App10Constants.Href && reader.NamespaceURI == string.Empty) { @@ -308,41 +327,45 @@ namespace System.ServiceModel.Syndication { continue; } - - string val = await reader.GetValueAsync(); + string val = reader.Value; if (!TryParseAttribute(name, ns, val, referencedCategories, version)) { if (preserveAttributeExtensions) { - referencedCategories.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), await reader.GetValueAsync()); + referencedCategories.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), reader.Value); + } + else + { + SyndicationFeedFormatter.TraceSyndicationElementIgnoredOnRead(reader); } } } } } - reader.MoveToElement(); bool isEmptyElement = reader.IsEmptyElement; - await reader.ReadStartElementAsync(); + reader.ReadStartElement(); if (!isEmptyElement) { XmlBuffer buffer = null; XmlDictionaryWriter extWriter = null; try { - while (await reader.IsStartElementAsync()) + while (reader.IsStartElement()) { if (!TryParseElement(reader, referencedCategories, version)) { if (preserveElementExtensions) { - var tuple = await SyndicationFeedFormatter.CreateBufferIfRequiredAndWriteNodeAsync(buffer, extWriter, reader, maxExtensionSize); - buffer = tuple.Item1; - extWriter = tuple.Item2; + SyndicationFeedFormatter.CreateBufferIfRequiredAndWriteNode(ref buffer, ref extWriter, reader, maxExtensionSize); + } + else + { + SyndicationFeedFormatter.TraceSyndicationElementIgnoredOnRead(reader); + reader.Skip(); } } } - LoadElementExtensions(buffer, extWriter, referencedCategories); } finally @@ -352,39 +375,34 @@ namespace System.ServiceModel.Syndication extWriter.Close(); } } - - await reader.ReadEndElementAsync(); + reader.ReadEndElement(); } } - private static async Task WriteCategoriesAsync(XmlWriter writer, CategoriesDocument categories, Uri baseUri, string version) + private static void WriteCategories(XmlWriter writer, CategoriesDocument categories, Uri baseUri, string version) { - await writer.WriteStartElementAsync(App10Constants.Prefix, App10Constants.Categories, App10Constants.Namespace); - await WriteCategoriesInnerXml(writer, categories, baseUri, version); - await writer.WriteEndElementAsync(); + writer.WriteStartElement(App10Constants.Prefix, App10Constants.Categories, App10Constants.Namespace); + WriteCategoriesInnerXml(writer, categories, baseUri, version); + writer.WriteEndElement(); } - private static async Task WriteInlineCategoriesContentAsync(XmlWriter writer, InlineCategoriesDocument categories, string version) + private static void WriteInlineCategoriesContent(XmlWriter writer, InlineCategoriesDocument categories, string version) { - writer = XmlWriterWrapper.CreateFromWriter(writer); if (!string.IsNullOrEmpty(categories.Scheme)) { - await writer.WriteAttributeStringAsync(Atom10Constants.SchemeTag, categories.Scheme); + writer.WriteAttributeString(Atom10Constants.SchemeTag, categories.Scheme); } // by default, categories are not fixed if (categories.IsFixed) { - await writer.WriteAttributeStringAsync(App10Constants.Fixed, "yes"); + writer.WriteAttributeString(App10Constants.Fixed, "yes"); } - - await WriteAttributeExtensionsAsync(writer, categories, version); - + WriteAttributeExtensions(writer, categories, version); for (int i = 0; i < categories.Categories.Count; ++i) { - await Atom10FeedFormatter.WriteCategoryAsync(writer, categories.Categories[i], version); + Atom10FeedFormatter.WriteCategory(writer, categories.Categories[i], version); } - - await WriteElementExtensionsAsync(writer, categories, version); + WriteElementExtensions(writer, categories, version); } private static void WriteReferencedCategoriesContent(XmlWriter writer, ReferencedCategoriesDocument categories, string version) @@ -393,9 +411,8 @@ namespace System.ServiceModel.Syndication { writer.WriteAttributeString(App10Constants.Href, FeedUtils.GetUriString(categories.Link)); } - - WriteAttributeExtensionsAsync(writer, categories, version); - WriteElementExtensionsAsync(writer, categories, version); + WriteAttributeExtensions(writer, categories, version); + WriteElementExtensions(writer, categories, version); } private static void WriteXmlBase(XmlWriter writer, Uri baseUri) @@ -408,7 +425,7 @@ namespace System.ServiceModel.Syndication writer.WriteAttributeString("xml", "lang", Atom10FeedFormatter.XmlNs, lang); } - private async Task ReadCollection(XmlReaderWrapper reader, Workspace workspace) + private ResourceCollectionInfo ReadCollection(XmlReader reader, Workspace workspace) { ResourceCollectionInfo result = CreateCollection(workspace); result.BaseUri = workspace.BaseUri; @@ -418,11 +435,11 @@ namespace System.ServiceModel.Syndication { if (reader.LocalName == "base" && reader.NamespaceURI == Atom10FeedFormatter.XmlNs) { - result.BaseUri = FeedUtils.CombineXmlBase(result.BaseUri, await reader.GetValueAsync()); + result.BaseUri = FeedUtils.CombineXmlBase(result.BaseUri, reader.Value); } else if (reader.LocalName == App10Constants.Href && reader.NamespaceURI == string.Empty) { - result.Link = new Uri(await reader.GetValueAsync(), UriKind.RelativeOrAbsolute); + result.Link = new Uri(reader.Value, UriKind.RelativeOrAbsolute); } else { @@ -432,13 +449,16 @@ namespace System.ServiceModel.Syndication { continue; } - - string val = await reader.GetValueAsync(); + string val = reader.Value; if (!TryParseAttribute(name, ns, val, result, this.Version)) { if (_preserveAttributeExtensions) { - result.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), val); + result.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), reader.Value); + } + else + { + SyndicationFeedFormatter.TraceSyndicationElementIgnoredOnRead(reader); } } } @@ -451,15 +471,15 @@ namespace System.ServiceModel.Syndication reader.ReadStartElement(); try { - while (await reader.IsStartElementAsync()) + while (reader.IsStartElement()) { - if (await reader.IsStartElementAsync(Atom10Constants.TitleTag, Atom10Constants.Atom10Namespace)) + if (reader.IsStartElement(Atom10Constants.TitleTag, Atom10Constants.Atom10Namespace)) { - result.Title = await new Atom10FeedFormatter().ReadTextContentFromAsync(reader, "//app:service/app:workspace/app:collection/atom:title[@type]", _preserveAttributeExtensions); + result.Title = Atom10FeedFormatter.ReadTextContentFrom(reader, "//app:service/app:workspace/app:collection/atom:title[@type]", _preserveAttributeExtensions); } - else if (await reader.IsStartElementAsync(App10Constants.Categories, App10Constants.Namespace)) + else if (reader.IsStartElement(App10Constants.Categories, App10Constants.Namespace)) { - result.Categories.Add(await ReadCategories(reader, + result.Categories.Add(ReadCategories(reader, result.BaseUri, delegate () { @@ -475,7 +495,7 @@ namespace System.ServiceModel.Syndication _preserveAttributeExtensions, _maxExtensionSize)); } - else if (await reader.IsStartElementAsync(App10Constants.Accept, App10Constants.Namespace)) + else if (reader.IsStartElement(App10Constants.Accept, App10Constants.Namespace)) { result.Accepts.Add(reader.ReadElementString()); } @@ -483,22 +503,15 @@ namespace System.ServiceModel.Syndication { if (_preserveElementExtensions) { - if (buffer == null) - { - buffer = new XmlBuffer(_maxExtensionSize); - extWriter = buffer.OpenSection(XmlDictionaryReaderQuotas.Max); - extWriter.WriteStartElement(Rss20Constants.ExtensionWrapperTag); - } - - await XmlReaderWrapper.WriteNodeAsync(extWriter, reader, false); + SyndicationFeedFormatter.CreateBufferIfRequiredAndWriteNode(ref buffer, ref extWriter, reader, _maxExtensionSize); } else { + SyndicationFeedFormatter.TraceSyndicationElementIgnoredOnRead(reader); reader.Skip(); } } } - LoadElementExtensions(buffer, extWriter, result); } finally @@ -508,17 +521,16 @@ namespace System.ServiceModel.Syndication extWriter.Close(); } } - reader.ReadEndElement(); return result; } - private async Task ReadDocumentAsync(XmlReaderWrapper reader) + private void ReadDocument(XmlReader reader) { ServiceDocument result = CreateDocumentInstance(); try { - await SyndicationFeedFormatter.MoveToStartElementAsync(reader); + SyndicationFeedFormatter.MoveToStartElement(reader); bool elementIsEmpty = reader.IsEmptyElement; if (reader.HasAttributes) { @@ -526,11 +538,11 @@ namespace System.ServiceModel.Syndication { if (reader.LocalName == "lang" && reader.NamespaceURI == Atom10FeedFormatter.XmlNs) { - result.Language = await reader.GetValueAsync(); + result.Language = reader.Value; } else if (reader.LocalName == "base" && reader.NamespaceURI == Atom10FeedFormatter.XmlNs) { - result.BaseUri = new Uri(await reader.GetValueAsync(), UriKind.RelativeOrAbsolute); + result.BaseUri = new Uri(reader.Value, UriKind.RelativeOrAbsolute); } else { @@ -540,13 +552,16 @@ namespace System.ServiceModel.Syndication { continue; } - - string val = await reader.GetValueAsync(); + string val = reader.Value; if (!TryParseAttribute(name, ns, val, result, this.Version)) { if (_preserveAttributeExtensions) { - result.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), val); + result.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), reader.Value); + } + else + { + SyndicationFeedFormatter.TraceSyndicationElementIgnoredOnRead(reader); } } } @@ -555,32 +570,30 @@ namespace System.ServiceModel.Syndication XmlBuffer buffer = null; XmlDictionaryWriter extWriter = null; - await reader.ReadStartElementAsync(); + reader.ReadStartElement(); if (!elementIsEmpty) { try { - while (await reader.IsStartElementAsync()) + while (reader.IsStartElement()) { - if (await reader.IsStartElementAsync(App10Constants.Workspace, App10Constants.Namespace)) + if (reader.IsStartElement(App10Constants.Workspace, App10Constants.Namespace)) { - result.Workspaces.Add(ReadWorkspace(reader, result).Result); + result.Workspaces.Add(ReadWorkspace(reader, result)); } else if (!TryParseElement(reader, result, this.Version)) { if (_preserveElementExtensions) { - var tuple = await SyndicationFeedFormatter.CreateBufferIfRequiredAndWriteNodeAsync(buffer, extWriter, reader, _maxExtensionSize); - buffer = tuple.Item1; - extWriter = tuple.Item2; + SyndicationFeedFormatter.CreateBufferIfRequiredAndWriteNode(ref buffer, ref extWriter, reader, _maxExtensionSize); } else { - await reader.SkipAsync(); + SyndicationFeedFormatter.TraceSyndicationElementIgnoredOnRead(reader); + reader.Skip(); } } } - LoadElementExtensions(buffer, extWriter, result); } finally @@ -591,22 +604,20 @@ namespace System.ServiceModel.Syndication } } } - - await reader.ReadEndElementAsync(); + reader.ReadEndElement(); } catch (FormatException e) { - throw new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingDocument), e); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingDocument), e)); } catch (ArgumentException e) { - new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingDocument), e); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingDocument), e)); } - SetDocument(result); } - private async Task ReadWorkspace(XmlReaderWrapper reader, ServiceDocument document) + private Workspace ReadWorkspace(XmlReader reader, ServiceDocument document) { Workspace result = CreateWorkspace(document); result.BaseUri = document.BaseUri; @@ -616,7 +627,7 @@ namespace System.ServiceModel.Syndication { if (reader.LocalName == "base" && reader.NamespaceURI == Atom10FeedFormatter.XmlNs) { - result.BaseUri = FeedUtils.CombineXmlBase(result.BaseUri, await reader.GetValueAsync()); + result.BaseUri = FeedUtils.CombineXmlBase(result.BaseUri, reader.Value); } else { @@ -626,13 +637,16 @@ namespace System.ServiceModel.Syndication { continue; } - - string val = await reader.GetValueAsync(); + string val = reader.Value; if (!TryParseAttribute(name, ns, val, result, this.Version)) { if (_preserveAttributeExtensions) { - result.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), val); + result.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), reader.Value); + } + else + { + SyndicationFeedFormatter.TraceSyndicationElementIgnoredOnRead(reader); } } } @@ -641,34 +655,33 @@ namespace System.ServiceModel.Syndication XmlBuffer buffer = null; XmlDictionaryWriter extWriter = null; - await reader.ReadStartElementAsync(); + + reader.ReadStartElement(); try { - while (await reader.IsStartElementAsync()) + while (reader.IsStartElement()) { - if (await reader.IsStartElementAsync(Atom10Constants.TitleTag, Atom10Constants.Atom10Namespace)) + if (reader.IsStartElement(Atom10Constants.TitleTag, Atom10Constants.Atom10Namespace)) { - result.Title = await new Atom10FeedFormatter().ReadTextContentFromAsync(reader, "//app:service/app:workspace/atom:title[@type]", _preserveAttributeExtensions); + result.Title = Atom10FeedFormatter.ReadTextContentFrom(reader, "//app:service/app:workspace/atom:title[@type]", _preserveAttributeExtensions); } - else if (await reader.IsStartElementAsync(App10Constants.Collection, App10Constants.Namespace)) + else if (reader.IsStartElement(App10Constants.Collection, App10Constants.Namespace)) { - result.Collections.Add(ReadCollection(reader, result).Result); + result.Collections.Add(ReadCollection(reader, result)); } else if (!TryParseElement(reader, result, this.Version)) { if (_preserveElementExtensions) { - var tuple = await SyndicationFeedFormatter.CreateBufferIfRequiredAndWriteNodeAsync(buffer, extWriter, reader, _maxExtensionSize); - buffer = tuple.Item1; - extWriter = tuple.Item2; + SyndicationFeedFormatter.CreateBufferIfRequiredAndWriteNode(ref buffer, ref extWriter, reader, _maxExtensionSize); } else { - await reader.SkipAsync(); + SyndicationFeedFormatter.TraceSyndicationElementIgnoredOnRead(reader); + reader.Skip(); } } } - LoadElementExtensions(buffer, extWriter, result); } finally @@ -678,94 +691,82 @@ namespace System.ServiceModel.Syndication extWriter.Close(); } } - - await reader.ReadEndElementAsync(); + reader.ReadEndElement(); return result; } - private async Task WriteCollectionAsync(XmlWriter writer, ResourceCollectionInfo collection, Uri baseUri) + private void WriteCollection(XmlWriter writer, ResourceCollectionInfo collection, Uri baseUri) { - await writer.WriteStartElementAsync(App10Constants.Prefix, App10Constants.Collection, App10Constants.Namespace); + writer.WriteStartElement(App10Constants.Prefix, App10Constants.Collection, App10Constants.Namespace); Uri baseUriToWrite = FeedUtils.GetBaseUriToWrite(baseUri, collection.BaseUri); if (baseUriToWrite != null) { baseUri = collection.BaseUri; WriteXmlBase(writer, baseUriToWrite); } - if (collection.Link != null) { - await writer.WriteAttributeStringAsync(App10Constants.Href, FeedUtils.GetUriString(collection.Link)); + writer.WriteAttributeString(App10Constants.Href, FeedUtils.GetUriString(collection.Link)); } - - await WriteAttributeExtensionsAsync(writer, collection, this.Version); + WriteAttributeExtensions(writer, collection, this.Version); if (collection.Title != null) { - await collection.Title.WriteToAsync(writer, Atom10Constants.TitleTag, Atom10Constants.Atom10Namespace); + collection.Title.WriteTo(writer, Atom10Constants.TitleTag, Atom10Constants.Atom10Namespace); } - for (int i = 0; i < collection.Accepts.Count; ++i) { - await writer.WriteElementStringAsync(App10Constants.Prefix, App10Constants.Accept, App10Constants.Namespace, collection.Accepts[i]); + writer.WriteElementString(App10Constants.Prefix, App10Constants.Accept, App10Constants.Namespace, collection.Accepts[i]); } - for (int i = 0; i < collection.Categories.Count; ++i) { - await WriteCategoriesAsync(writer, collection.Categories[i], baseUri, this.Version); + WriteCategories(writer, collection.Categories[i], baseUri, this.Version); } - - await WriteElementExtensionsAsync(writer, collection, this.Version); - await writer.WriteEndElementAsync(); + WriteElementExtensions(writer, collection, this.Version); + writer.WriteEndElement(); } - private async Task WriteDocumentAsync(XmlWriter writer) + private void WriteDocument(XmlWriter writer) { // declare the atom10 namespace upfront for compactness - await writer.WriteAttributeStringAsync(Atom10Constants.Atom10Prefix, Atom10FeedFormatter.XmlNsNs, Atom10Constants.Atom10Namespace); + writer.WriteAttributeString(Atom10Constants.Atom10Prefix, Atom10FeedFormatter.XmlNsNs, Atom10Constants.Atom10Namespace); if (!string.IsNullOrEmpty(this.Document.Language)) { WriteXmlLang(writer, this.Document.Language); } - Uri baseUri = this.Document.BaseUri; if (baseUri != null) { WriteXmlBase(writer, baseUri); } - WriteAttributeExtensions(writer, this.Document, this.Version); for (int i = 0; i < this.Document.Workspaces.Count; ++i) { - await WriteWorkspaceAsync(writer, this.Document.Workspaces[i], baseUri); + WriteWorkspace(writer, this.Document.Workspaces[i], baseUri); } - - await WriteElementExtensionsAsync(writer, this.Document, this.Version); + WriteElementExtensions(writer, this.Document, this.Version); } - private async Task WriteWorkspaceAsync(XmlWriter writer, Workspace workspace, Uri baseUri) + private void WriteWorkspace(XmlWriter writer, Workspace workspace, Uri baseUri) { - await writer.WriteStartElementAsync(App10Constants.Prefix, App10Constants.Workspace, App10Constants.Namespace); + writer.WriteStartElement(App10Constants.Prefix, App10Constants.Workspace, App10Constants.Namespace); Uri baseUriToWrite = FeedUtils.GetBaseUriToWrite(baseUri, workspace.BaseUri); if (baseUriToWrite != null) { baseUri = workspace.BaseUri; WriteXmlBase(writer, baseUriToWrite); } - WriteAttributeExtensions(writer, workspace, this.Version); if (workspace.Title != null) { - await workspace.Title.WriteToAsync(writer, Atom10Constants.TitleTag, Atom10Constants.Atom10Namespace); + workspace.Title.WriteTo(writer, Atom10Constants.TitleTag, Atom10Constants.Atom10Namespace); } - for (int i = 0; i < workspace.Collections.Count; ++i) { - await WriteCollectionAsync(writer, workspace.Collections[i], baseUri); + WriteCollection(writer, workspace.Collections[i], baseUri); } - - await WriteElementExtensionsAsync(writer, workspace, this.Version); - await writer.WriteEndElementAsync(); + WriteElementExtensions(writer, workspace, this.Version); + writer.WriteEndElement(); } } diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/CategoriesDocument.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/CategoriesDocument.cs index e9f74202d9..ce5490f8cc 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/CategoriesDocument.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/CategoriesDocument.cs @@ -2,15 +2,16 @@ // 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.Xml; +using System.Collections.ObjectModel; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization; +using System.Xml.Serialization; +using System.Runtime.CompilerServices; + namespace System.ServiceModel.Syndication { - using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Runtime.CompilerServices; - using System.Threading.Tasks; - using System.Xml; - public abstract class CategoriesDocument : IExtensibleSyndicationObject { private Uri _baseUri; @@ -69,10 +70,10 @@ namespace System.ServiceModel.Syndication return new ReferencedCategoriesDocument(linkToCategoriesDocument); } - public static async Task LoadAsync(XmlReader reader) + public static CategoriesDocument Load(XmlReader reader) { AtomPub10CategoriesDocumentFormatter formatter = new AtomPub10CategoriesDocumentFormatter(); - await formatter.ReadFromAsync(reader); + formatter.ReadFrom(reader); return formatter.Document; } @@ -96,17 +97,17 @@ namespace System.ServiceModel.Syndication return false; } - protected internal virtual Task WriteAttributeExtensionsAsync(XmlWriter writer, string version) + protected internal virtual void WriteAttributeExtensions(XmlWriter writer, string version) { - return _extensions.WriteAttributeExtensionsAsync(writer); + _extensions.WriteAttributeExtensions(writer); } - protected internal virtual Task WriteElementExtensionsAsync(XmlWriter writer, string version) + protected internal virtual void WriteElementExtensions(XmlWriter writer, string version) { - return _extensions.WriteElementExtensionsAsync(writer); + _extensions.WriteElementExtensions(writer); } - internal void LoadElementExtensions(XmlReaderWrapper readerOverUnparsedExtensions, int maxExtensionSize) + internal void LoadElementExtensions(XmlReader readerOverUnparsedExtensions, int maxExtensionSize) { _extensions.LoadElementExtensions(readerOverUnparsedExtensions, maxExtensionSize); } diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/CategoriesDocumentFormatter.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/CategoriesDocumentFormatter.cs index c3883c2a14..5a4d9dc373 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/CategoriesDocumentFormatter.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/CategoriesDocumentFormatter.cs @@ -2,14 +2,16 @@ // 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.Collections.ObjectModel; +using System.Runtime.Serialization; +using System.Xml.Serialization; +using System.Collections.Generic; +using System.Xml; +using System.Runtime.CompilerServices; + namespace System.ServiceModel.Syndication { - using System; - using System.Runtime.CompilerServices; - using System.Runtime.Serialization; - using System.Threading.Tasks; - using System.Xml; - + [DataContract] public abstract class CategoriesDocumentFormatter { private CategoriesDocument _document; @@ -21,7 +23,7 @@ namespace System.ServiceModel.Syndication { if (documentToWrite == null) { - throw new ArgumentNullException(nameof(documentToWrite)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("documentToWrite"); } _document = documentToWrite; } @@ -34,9 +36,9 @@ namespace System.ServiceModel.Syndication public abstract string Version { get; } - public abstract Task CanReadAsync(XmlReader reader); - public abstract Task ReadFromAsync(XmlReader reader); - public abstract Task WriteTo(XmlWriter writer); + public abstract bool CanRead(XmlReader reader); + public abstract void ReadFrom(XmlReader reader); + public abstract void WriteTo(XmlWriter writer); protected virtual InlineCategoriesDocument CreateInlineCategoriesDocument() { diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/DateTimeHelper.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/DateTimeHelper.cs new file mode 100644 index 0000000000..9ca15bf074 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/DateTimeHelper.cs @@ -0,0 +1,217 @@ +// 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.Globalization; +using System.Text; + +namespace System.ServiceModel.Syndication +{ + internal static class DateTimeHelper + { + private const string Rfc3339DateTimeFormat = "yyyy-MM-ddTHH:mm:ssK"; + + public static DateTimeOffset DefaultRss20DateTimeParser(string dateTimeString, string localName, string ns) + { + DateTimeOffset dto; + + // First check if DateTimeOffset default parsing can parse the date + if (DateTimeOffset.TryParse(dateTimeString, out dto)) + { + return dto; + } + + // RSS specifies RFC822 + if (Rfc822DateTimeParser(dateTimeString, out dto)) + { + return dto; + } + + // Event though RCS3339 is for Atom, someone might be using this for RSS + if (Rfc3339DateTimeParser(dateTimeString, out dto)) + { + return dto; + } + + // Unable to parse - using a default date; + throw new FormatException(SR.ErrorParsingDateTime); + } + + public static DateTimeOffset DefaultAtom10DateTimeParser(string dateTimeString, string localName, string ns) + { + if (Rfc3339DateTimeParser(dateTimeString, out DateTimeOffset dto)) + { + return dto; + } + + throw new FormatException(SR.ErrorParsingDateTime); + } + + private static bool Rfc3339DateTimeParser(string dateTimeString, out DateTimeOffset dto) + { + dateTimeString = dateTimeString.Trim(); + if (dateTimeString.Length < 20) + { + return false; + } + + if (dateTimeString[19] == '.') + { + // remove any fractional seconds, we choose to ignore them + int i = 20; + while (dateTimeString.Length > i && char.IsDigit(dateTimeString[i])) + { + ++i; + } + + dateTimeString = dateTimeString.Substring(0, 19) + dateTimeString.Substring(i); + } + + return DateTimeOffset.TryParseExact(dateTimeString, Rfc3339DateTimeFormat, CultureInfo.InvariantCulture.DateTimeFormat, DateTimeStyles.None, out dto); + } + + private static bool Rfc822DateTimeParser(string dateTimeString, out DateTimeOffset dto) + { + StringBuilder dateTimeStringBuilder = new StringBuilder(dateTimeString.Trim()); + if (dateTimeStringBuilder.Length < 18) + { + return false; + } + + int timeZoneStartIndex; + for (timeZoneStartIndex = dateTimeStringBuilder.Length - 1; dateTimeStringBuilder[timeZoneStartIndex] != ' '; timeZoneStartIndex--) + ; + timeZoneStartIndex++; + + int timeZoneLength = dateTimeStringBuilder.Length - timeZoneStartIndex; + string timeZoneSuffix = dateTimeStringBuilder.ToString(timeZoneStartIndex, timeZoneLength); + dateTimeStringBuilder.Remove(timeZoneStartIndex, timeZoneLength); + bool isUtc; + dateTimeStringBuilder.Append(NormalizeTimeZone(timeZoneSuffix, out isUtc)); + string wellFormattedString = dateTimeStringBuilder.ToString(); + + DateTimeOffset theTime; + string[] parseFormat = + { + "ddd, dd MMMM yyyy HH:mm:ss zzz", + "dd MMMM yyyy HH:mm:ss zzz", + "ddd, dd MMM yyyy HH:mm:ss zzz", + "dd MMM yyyy HH:mm:ss zzz", + + "ddd, dd MMMM yyyy HH:mm zzz", + "dd MMMM yyyy HH:mm zzz", + "ddd, dd MMM yyyy HH:mm zzz", + "dd MMM yyyy HH:mm zzz", + + // The original RFC822 spec listed 2 digit years. RFC1123 updated the format to include 4 digit years and states that you should use 4 digits. + // Technically RSS2.0 specifies RFC822 but it's presumed that RFC1123 will be used as we're now past Y2K and everyone knows better. The 4 digit + // formats are listed first for performance reasons as it's presumed they will be more likely to match first. + "ddd, dd MMMM yy HH:mm:ss zzz", + "dd MMMM yyyy HH:mm:ss zzz", + "ddd, dd MMM yy HH:mm:ss zzz", + "dd MMM yyyy HH:mm:ss zzz", + + "ddd, dd MMMM yy HH:mm zzz", + "dd MMMM yyyy HH:mm zzz", + "ddd, dd MMM yy HH:mm zzz", + "dd MMM yyyy HH:mm zzz" + }; + + if (DateTimeOffset.TryParseExact(wellFormattedString, parseFormat, + CultureInfo.InvariantCulture.DateTimeFormat, + (isUtc ? DateTimeStyles.AdjustToUniversal : DateTimeStyles.None), out theTime)) + { + dto = theTime; + return true; + } + + return false; + } + + private static string NormalizeTimeZone(string rfc822TimeZone, out bool isUtc) + { + isUtc = false; + // return a string in "-08:00" format + if (rfc822TimeZone[0] == '+' || rfc822TimeZone[0] == '-') + { + // the time zone is supposed to be 4 digits but some feeds omit the initial 0 + StringBuilder result = new StringBuilder(rfc822TimeZone); + if (result.Length == 4) + { + // the timezone is +/-HMM. Convert to +/-HHMM + result.Insert(1, '0'); + } + result.Insert(3, ':'); + return result.ToString(); + } + switch (rfc822TimeZone) + { + case "UT": + case "Z": + isUtc = true; + return "-00:00"; + case "GMT": + return "-00:00"; + case "A": + return "-01:00"; + case "B": + return "-02:00"; + case "C": + return "-03:00"; + case "D": + case "EDT": + return "-04:00"; + case "E": + case "EST": + case "CDT": + return "-05:00"; + case "F": + case "CST": + case "MDT": + return "-06:00"; + case "G": + case "MST": + case "PDT": + return "-07:00"; + case "H": + case "PST": + return "-08:00"; + case "I": + return "-09:00"; + case "K": + return "-10:00"; + case "L": + return "-11:00"; + case "M": + return "-12:00"; + case "N": + return "+01:00"; + case "O": + return "+02:00"; + case "P": + return "+03:00"; + case "Q": + return "+04:00"; + case "R": + return "+05:00"; + case "S": + return "+06:00"; + case "T": + return "+07:00"; + case "U": + return "+08:00"; + case "V": + return "+09:00"; + case "W": + return "+10:00"; + case "X": + return "+11:00"; + case "Y": + return "+12:00"; + default: + return ""; + } + } + + } +} diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/ExtensibleSyndicationObject.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/ExtensibleSyndicationObject.cs index c3ad0d3f41..300a42b729 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/ExtensibleSyndicationObject.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/ExtensibleSyndicationObject.cs @@ -2,14 +2,14 @@ // 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.Collections.ObjectModel; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Xml.Serialization; +using System.Xml; + namespace System.ServiceModel.Syndication { - using System; - using System.Collections.ObjectModel; - using System.Collections.Generic; - using System.Xml; - using System.Threading.Tasks; - // NOTE: This class implements Clone so if you add any members, please update the copy ctor internal struct ExtensibleSyndicationObject : IExtensibleSyndicationObject { @@ -85,12 +85,11 @@ namespace System.ServiceModel.Syndication { if (readerOverUnparsedExtensions == null) { - throw new ArgumentNullException(nameof(readerOverUnparsedExtensions)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("readerOverUnparsedExtensions"); } - if (maxExtensionSize < 0) { - throw new ArgumentOutOfRangeException(nameof(maxExtensionSize)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("maxExtensionSize")); } XmlDictionaryReader r = XmlDictionaryReader.CreateDictionaryReader(readerOverUnparsedExtensions); _elementExtensions = new SyndicationElementExtensionCollection(CreateXmlBuffer(r, maxExtensionSize)); @@ -102,35 +101,31 @@ namespace System.ServiceModel.Syndication _elementExtensions = new SyndicationElementExtensionCollection(buffer); } - internal async Task WriteAttributeExtensionsAsync(XmlWriter writer) + internal void WriteAttributeExtensions(XmlWriter writer) { if (writer == null) { - throw new ArgumentNullException(nameof(writer)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); } - - writer = XmlWriterWrapper.CreateFromWriter(writer); - if (_attributeExtensions != null) { foreach (XmlQualifiedName qname in _attributeExtensions.Keys) { string value = _attributeExtensions[qname]; - await writer.WriteAttributeStringAsync(qname.Name, qname.Namespace, value); + writer.WriteAttributeString(qname.Name, qname.Namespace, value); } } } - internal async Task WriteElementExtensionsAsync(XmlWriter writer) + internal void WriteElementExtensions(XmlWriter writer) { if (writer == null) { - throw new ArgumentNullException(nameof(writer)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); } - if (_elementExtensions != null) { - await _elementExtensions.WriteToAsync(writer); + _elementExtensions.WriteTo(writer); } } diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/FeedUtils.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/FeedUtils.cs index 51ac061848..297a684cda 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/FeedUtils.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/FeedUtils.cs @@ -2,13 +2,14 @@ // 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.Collections.ObjectModel; +using System.Xml; +using System.Xml.Serialization; +using System.Runtime.Serialization; +using System.Globalization; + namespace System.ServiceModel.Syndication { - using System; - using System.Collections.ObjectModel; - using System.Globalization; - using System.Xml; - internal static class FeedUtils { public static string AddLineInfo(XmlReader reader, string error) @@ -16,7 +17,7 @@ namespace System.ServiceModel.Syndication IXmlLineInfo lineInfo = reader as IXmlLineInfo; if (lineInfo != null && lineInfo.HasLineInfo()) { - error = string.Format(CultureInfo.InvariantCulture, "{0} {1}", string.Format(SR.ErrorInLine, lineInfo.LineNumber, lineInfo.LinePosition), error); + error = String.Format(CultureInfo.InvariantCulture, "{0} {1}", SR.Format(SR.ErrorInLine, lineInfo.LineNumber, lineInfo.LinePosition), SR.Format(error)); } return error; } @@ -78,13 +79,11 @@ namespace System.ServiceModel.Syndication { return rootBase; } - Uri newBaseUri = new Uri(newBase, UriKind.RelativeOrAbsolute); if (rootBase == null || newBaseUri.IsAbsoluteUri) { return newBaseUri; } - return new Uri(rootBase, newBase); } diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/IExtensibleSyndicationObject.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/IExtensibleSyndicationObject.cs index cc1f32f82f..0af8bbe389 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/IExtensibleSyndicationObject.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/IExtensibleSyndicationObject.cs @@ -2,11 +2,14 @@ // 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.Collections.ObjectModel; +using System.Runtime.Serialization; +using System.Xml.Serialization; +using System.Collections.Generic; +using System.Xml; + namespace System.ServiceModel.Syndication { - using System.Collections.Generic; - using System.Xml; - internal interface IExtensibleSyndicationObject { Dictionary AttributeExtensions diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/InlineCategoriesDocument.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/InlineCategoriesDocument.cs index 48cbb9ce60..02059d6f23 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/InlineCategoriesDocument.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/InlineCategoriesDocument.cs @@ -2,12 +2,16 @@ // 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.Xml; +using System.Collections.ObjectModel; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization; +using System.Xml.Serialization; +using System.Runtime.CompilerServices; + namespace System.ServiceModel.Syndication { - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Runtime.CompilerServices; - public class InlineCategoriesDocument : CategoriesDocument { private Collection _categories; diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/NullNotAllowedCollection.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/NullNotAllowedCollection.cs index 420d089816..38d51d4a75 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/NullNotAllowedCollection.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/NullNotAllowedCollection.cs @@ -2,11 +2,10 @@ // 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.Collections.ObjectModel; + namespace System.ServiceModel.Syndication { - using System; - using System.Collections.ObjectModel; - internal class NullNotAllowedCollection : Collection where TCollectionItem : class { @@ -19,7 +18,7 @@ namespace System.ServiceModel.Syndication { if (item == null) { - throw new ArgumentNullException(nameof(item)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); } base.InsertItem(index, item); } @@ -28,7 +27,7 @@ namespace System.ServiceModel.Syndication { if (item == null) { - throw new ArgumentNullException(nameof(item)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); } base.SetItem(index, item); } diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/ReferencedCategoriesDocument.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/ReferencedCategoriesDocument.cs index 75686a2f30..9642f74581 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/ReferencedCategoriesDocument.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/ReferencedCategoriesDocument.cs @@ -2,11 +2,16 @@ // 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.Xml; +using System.Collections.ObjectModel; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization; +using System.Xml.Serialization; +using System.Runtime.CompilerServices; + namespace System.ServiceModel.Syndication { - using System; - using System.Runtime.CompilerServices; - public class ReferencedCategoriesDocument : CategoriesDocument { private Uri _link; @@ -20,7 +25,7 @@ namespace System.ServiceModel.Syndication { if (link == null) { - throw new ArgumentNullException(nameof(link)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("link"); } _link = link; } diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/ResourceCollectionInfo.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/ResourceCollectionInfo.cs index 33605efe0a..928a502887 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/ResourceCollectionInfo.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/ResourceCollectionInfo.cs @@ -2,15 +2,15 @@ // 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.Collections.ObjectModel; +using System.Runtime.Serialization; +using System.Xml.Serialization; +using System.Collections.Generic; +using System.Xml; +using System.Runtime.CompilerServices; + namespace System.ServiceModel.Syndication { - using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Runtime.CompilerServices; - using System.Threading.Tasks; - using System.Xml; - public class ResourceCollectionInfo : IExtensibleSyndicationObject { private static IEnumerable s_singleEmptyAccept; @@ -44,11 +44,11 @@ namespace System.ServiceModel.Syndication { if (title == null) { - throw new ArgumentNullException(nameof(title)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("title"); } if (link == null) { - throw new ArgumentNullException(nameof(link)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("link"); } _title = title; _link = link; @@ -148,17 +148,17 @@ namespace System.ServiceModel.Syndication return false; } - protected internal virtual Task WriteAttributeExtensionsAsync(XmlWriter writer, string version) + protected internal virtual void WriteAttributeExtensions(XmlWriter writer, string version) { - return _extensions.WriteAttributeExtensionsAsync(writer); + _extensions.WriteAttributeExtensions(writer); } - protected internal virtual Task WriteElementExtensionsAsync(XmlWriter writer, string version) + protected internal virtual void WriteElementExtensions(XmlWriter writer, string version) { - return _extensions.WriteElementExtensionsAsync(writer); + _extensions.WriteElementExtensions(writer); } - internal void LoadElementExtensions(XmlReaderWrapper readerOverUnparsedExtensions, int maxExtensionSize) + internal void LoadElementExtensions(XmlReader readerOverUnparsedExtensions, int maxExtensionSize) { _extensions.LoadElementExtensions(readerOverUnparsedExtensions, maxExtensionSize); } diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Rss20Constants.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Rss20Constants.cs index a811e7ec76..5b2b0e8859 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Rss20Constants.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Rss20Constants.cs @@ -34,13 +34,5 @@ namespace System.ServiceModel.Syndication public const string UrlTag = "url"; public const string Version = "2.0"; public const string VersionTag = "version"; - public const string DocumentationTag = "docs"; - public const string TimeToLiveTag = "ttl"; - public const string TextInputTag = "textInput"; - public const string SkipHoursTag = "skipHours"; - public const string SkipDaysTag = "skipDays"; - public const string HourTag = "hour"; - public const string DayTag = "day"; - public const string NameTag = "name"; } } diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Rss20FeedFormatter.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Rss20FeedFormatter.cs index c5fdcb189b..409c24b87d 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Rss20FeedFormatter.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Rss20FeedFormatter.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#pragma warning disable 1634, 1691 + namespace System.ServiceModel.Syndication { using System; @@ -9,22 +11,21 @@ namespace System.ServiceModel.Syndication using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; + using System.Runtime; using System.Text; - using System.Threading; - using System.Threading.Tasks; using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; - + using DiagnosticUtility = System.ServiceModel.DiagnosticUtility; + using System.Runtime.CompilerServices; [XmlRoot(ElementName = Rss20Constants.RssTag, Namespace = Rss20Constants.Rss20Namespace)] - public class Rss20FeedFormatter : SyndicationFeedFormatter + public class Rss20FeedFormatter : SyndicationFeedFormatter, IXmlSerializable { private static readonly XmlQualifiedName s_rss20Domain = new XmlQualifiedName(Rss20Constants.DomainTag, string.Empty); private static readonly XmlQualifiedName s_rss20Length = new XmlQualifiedName(Rss20Constants.LengthTag, string.Empty); private static readonly XmlQualifiedName s_rss20Type = new XmlQualifiedName(Rss20Constants.TypeTag, string.Empty); private static readonly XmlQualifiedName s_rss20Url = new XmlQualifiedName(Rss20Constants.UrlTag, string.Empty); - private static List s_acceptedDays = new List { "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" }; private const string Rfc822OutputLocalDateTimeFormat = "ddd, dd MMM yyyy HH:mm:ss zzz"; private const string Rfc822OutputUtcDateTimeFormat = "ddd, dd MMM yyyy HH:mm:ss Z"; @@ -35,46 +36,6 @@ namespace System.ServiceModel.Syndication private bool _preserveElementExtensions; private bool _serializeExtensionsAsAtom; - //Custom Parsers - // value, localname , ns , result - public Func StringParser { get; set; } = DefaultStringParser; - public Func DateParser { get; set; } = DefaultDateParser; - public Func UriParser { get; set; } = DefaultUriParser; - - static private string DefaultStringParser(string value, string localName, string ns) - { - return value; - } - - static private Uri DefaultUriParser(string value, string localName, string ns) - { - return new Uri(value, UriKind.RelativeOrAbsolute); - } - - private async Task OnReadImage(XmlReaderWrapper reader, SyndicationFeed result) - { - await reader.ReadStartElementAsync(); - string localName = string.Empty; - - while (await reader.IsStartElementAsync()) - { - if (await reader.IsStartElementAsync(Rss20Constants.UrlTag, Rss20Constants.Rss20Namespace)) - { - result.ImageUrl = UriParser(await reader.ReadElementStringAsync(), Rss20Constants.UrlTag, Rss20Constants.Rss20Namespace); - } - else if (await reader.IsStartElementAsync(Rss20Constants.LinkTag, Rss20Constants.Rss20Namespace)) - { - result.ImageLink = UriParser(await reader.ReadElementStringAsync(), Rss20Constants.LinkTag, Rss20Constants.Rss20Namespace); - } - else if (await reader.IsStartElementAsync(Rss20Constants.TitleTag, Rss20Constants.Rss20Namespace)) - { - result.ImageTitle = new TextSyndicationContent(StringParser(await reader.ReadElementStringAsync(), Rss20Constants.TitleTag, Rss20Constants.Rss20Namespace)); - } - } - await reader.ReadEndElementAsync(); // image - return true; - } - public Rss20FeedFormatter() : this(typeof(SyndicationFeed)) { @@ -85,11 +46,12 @@ namespace System.ServiceModel.Syndication { if (feedTypeToCreate == null) { - throw new ArgumentNullException(nameof(feedTypeToCreate)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feedTypeToCreate"); } if (!typeof(SyndicationFeed).IsAssignableFrom(feedTypeToCreate)) { - throw new ArgumentException(string.Format(SR.InvalidObjectTypePassed, nameof(feedTypeToCreate), nameof(SyndicationFeed))); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("feedTypeToCreate", + SR.Format(SR.InvalidObjectTypePassed, "feedTypeToCreate", "SyndicationFeed")); } _serializeExtensionsAsAtom = true; _maxExtensionSize = int.MaxValue; @@ -116,6 +78,11 @@ namespace System.ServiceModel.Syndication _feedType = feedToWrite.GetType(); } + internal override Func GetDefaultDateTimeParser() + { + return DateTimeHelper.DefaultRss20DateTimeParser; + } + public bool PreserveAttributeExtensions { get { return _preserveAttributeExtensions; } @@ -151,45 +118,63 @@ namespace System.ServiceModel.Syndication { if (reader == null) { - throw new ArgumentNullException(nameof(reader)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); } - return reader.IsStartElement(Rss20Constants.RssTag, Rss20Constants.Rss20Namespace); } - public override Task ReadFromAsync(XmlReader reader, CancellationToken ct) + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "The IXmlSerializable implementation is only for exposing under WCF DataContractSerializer. The funcionality is exposed to derived class through the ReadFrom\\WriteTo methods")] + XmlSchema IXmlSerializable.GetSchema() { + return null; + } + + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "The IXmlSerializable implementation is only for exposing under WCF DataContractSerializer. The funcionality is exposed to derived class through the ReadFrom\\WriteTo methods")] + void IXmlSerializable.ReadXml(XmlReader reader) + { + if (reader == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); + } + TraceFeedReadBegin(); + ReadFeed(reader); + TraceFeedReadEnd(); + } + + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "The IXmlSerializable implementation is only for exposing under WCF DataContractSerializer. The funcionality is exposed to derived class through the ReadFrom\\WriteTo methods")] + void IXmlSerializable.WriteXml(XmlWriter writer) + { + if (writer == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); + } + TraceFeedWriteBegin(); + WriteFeed(writer); + TraceFeedWriteEnd(); + } + + public override void ReadFrom(XmlReader reader) + { + TraceFeedReadBegin(); if (!CanRead(reader)) { - throw new XmlException(string.Format(SR.UnknownFeedXml, reader.LocalName, reader.NamespaceURI)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.UnknownFeedXml, reader.LocalName, reader.NamespaceURI))); } - - SetFeed(CreateFeedInstance()); - return ReadXmlAsync(XmlReaderWrapper.CreateFromReader(reader), this.Feed); + ReadFeed(reader); + TraceFeedReadEnd(); } - private Task WriteXmlAsync(XmlWriter writer) + public override void WriteTo(XmlWriter writer) { if (writer == null) { - throw new ArgumentNullException(nameof(writer)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); } - - return WriteFeedAsync(writer); - } - - public override async Task WriteToAsync(XmlWriter writer, CancellationToken ct) - { - if (writer == null) - { - throw new ArgumentNullException(nameof(writer)); - } - - writer = XmlWriterWrapper.CreateFromWriter(writer); - - await writer.WriteStartElementAsync(Rss20Constants.RssTag, Rss20Constants.Rss20Namespace); - await WriteFeedAsync(writer); - await writer.WriteEndElementAsync(); + TraceFeedWriteBegin(); + writer.WriteStartElement(Rss20Constants.RssTag, Rss20Constants.Rss20Namespace); + WriteFeed(writer); + writer.WriteEndElement(); + TraceFeedWriteEnd(); } protected internal override void SetFeed(SyndicationFeed feed) @@ -198,230 +183,18 @@ namespace System.ServiceModel.Syndication _atomSerializer.SetFeed(this.Feed); } - private async Task ReadItemFromAsync(XmlReaderWrapper reader, SyndicationItem result, Uri feedBaseUri) + internal static void TraceExtensionsIgnoredOnWrite(string message) { - result.BaseUri = feedBaseUri; - await reader.MoveToContentAsync(); - bool isEmpty = reader.IsEmptyElement; - if (reader.HasAttributes) - { - while (reader.MoveToNextAttribute()) - { - string ns = reader.NamespaceURI; - string name = reader.LocalName; - if (name == "base" && ns == Atom10FeedFormatter.XmlNs) - { - result.BaseUri = FeedUtils.CombineXmlBase(result.BaseUri, await reader.GetValueAsync()); - continue; - } - - if (FeedUtils.IsXmlns(name, ns) || FeedUtils.IsXmlSchemaType(name, ns)) - { - continue; - } - - string val = await reader.GetValueAsync(); - if (!TryParseAttribute(name, ns, val, result, this.Version)) - { - if (_preserveAttributeExtensions) - { - result.AttributeExtensions.Add(new XmlQualifiedName(name, ns), val); - } - } - } - } - - await reader.ReadStartElementAsync(); - - if (!isEmpty) - { - string fallbackAlternateLink = null; - XmlDictionaryWriter extWriter = null; - bool readAlternateLink = false; - try - { - XmlBuffer buffer = null; - while (await reader.IsStartElementAsync()) - { - bool notHandled = false; - if (await reader.MoveToContentAsync() == XmlNodeType.Element && reader.NamespaceURI == Rss20Constants.Rss20Namespace) - { - switch (reader.LocalName) - { - case Rss20Constants.TitleTag: - result.Title = new TextSyndicationContent(StringParser(await reader.ReadElementStringAsync(), Rss20Constants.TitleTag, Rss20Constants.Rss20Namespace)); - break; - - case Rss20Constants.LinkTag: - result.Links.Add(await ReadAlternateLinkAsync(reader, result.BaseUri)); - readAlternateLink = true; - break; - - case Rss20Constants.DescriptionTag: - result.Summary = new TextSyndicationContent(StringParser(await reader.ReadElementStringAsync(), Rss20Constants.DescriptionTag, Rss20Constants.Rss20Namespace)); - break; - - case Rss20Constants.AuthorTag: - result.Authors.Add(await ReadPersonAsync(reader, result)); - break; - - case Rss20Constants.CategoryTag: - result.Categories.Add(await ReadCategoryAsync(reader, result)); - break; - - case Rss20Constants.EnclosureTag: - result.Links.Add(await ReadMediaEnclosureAsync(reader, result.BaseUri)); - break; - - case Rss20Constants.GuidTag: - { - bool isPermalink = true; - string permalinkString = reader.GetAttribute(Rss20Constants.IsPermaLinkTag, Rss20Constants.Rss20Namespace); - if (permalinkString != null && permalinkString.Equals("false", StringComparison.OrdinalIgnoreCase)) - { - isPermalink = false; - } - string localName = reader.LocalName; - string namespaceUri = reader.NamespaceURI; - result.Id = StringParser(await reader.ReadElementStringAsync(), localName, namespaceUri); - if (isPermalink) - { - fallbackAlternateLink = result.Id; - } - - break; - } - - case Rss20Constants.PubDateTag: - { - bool canReadContent = !reader.IsEmptyElement; - await reader.ReadStartElementAsync(); - if (canReadContent) - { - string str = await reader.ReadStringAsync(); - if (!string.IsNullOrEmpty(str)) - { - result.PublishDate = DateParser(str, reader.LocalName, reader.NamespaceURI); - } - - await reader.ReadEndElementAsync(); - } - - break; - } - - case Rss20Constants.SourceTag: - { - SyndicationFeed feed = new SyndicationFeed(); - if (reader.HasAttributes) - { - while (reader.MoveToNextAttribute()) - { - string ns = reader.NamespaceURI; - string name = reader.LocalName; - if (FeedUtils.IsXmlns(name, ns)) - { - continue; - } - string val = await reader.GetValueAsync(); - if (name == Rss20Constants.UrlTag && ns == Rss20Constants.Rss20Namespace) - { - feed.Links.Add(SyndicationLink.CreateSelfLink(UriParser(val, Rss20Constants.UrlTag, ns))); - } - else if (!FeedUtils.IsXmlns(name, ns)) - { - if (_preserveAttributeExtensions) - { - feed.AttributeExtensions.Add(new XmlQualifiedName(name, ns), val); - } - } - } - } - string localName = reader.LocalName; - string namespaceUri = reader.NamespaceURI; - string feedTitle = StringParser(await reader.ReadElementStringAsync(), localName, namespaceUri); - feed.Title = new TextSyndicationContent(feedTitle); - result.SourceFeed = feed; - - break; - } - - default: - notHandled = true; - break; - } - } - else - { - notHandled = true; - } - - if (notHandled) - { - bool parsedExtension = _serializeExtensionsAsAtom && _atomSerializer.TryParseItemElementFromAsync(reader, result).Result; - - if (!parsedExtension) - { - parsedExtension = TryParseElement(reader, result, this.Version); - } - - if (!parsedExtension) - { - if (_preserveElementExtensions) - { - if (buffer == null) - { - buffer = new XmlBuffer(_maxExtensionSize); - extWriter = buffer.OpenSection(XmlDictionaryReaderQuotas.Max); - extWriter.WriteStartElement(Rss20Constants.ExtensionWrapperTag); - } - - await XmlReaderWrapper.WriteNodeAsync(extWriter, reader, false); - } - else - { - await reader.SkipAsync(); - } - } - } - } - - LoadElementExtensions(buffer, extWriter, result); - } - finally - { - if (extWriter != null) - { - ((IDisposable)extWriter).Dispose(); - } - } - - await reader.ReadEndElementAsync(); // item - - if (!readAlternateLink && fallbackAlternateLink != null) - { - result.Links.Add(SyndicationLink.CreateAlternateLink(new Uri(fallbackAlternateLink, UriKind.RelativeOrAbsolute))); - readAlternateLink = true; - } - - // if there's no content and no alternate link set the summary as the item content - if (result.Content == null && !readAlternateLink) - { - result.Content = result.Summary; - result.Summary = null; - } - } } - internal Task ReadItemFromAsync(XmlReaderWrapper reader, SyndicationItem result) + internal void ReadItemFrom(XmlReader reader, SyndicationItem result) { - return ReadItemFromAsync(reader, result, null); + ReadItemFrom(reader, result, null); } - internal Task WriteItemContentsAsync(XmlWriter writer, SyndicationItem item) + internal void WriteItemContents(XmlWriter writer, SyndicationItem item) { - writer = XmlWriterWrapper.CreateFromWriter(writer); - return WriteItemContentsAsync(writer, item, null); + WriteItemContents(writer, item, null); } protected override SyndicationFeed CreateFeedInstance() @@ -429,250 +202,67 @@ namespace System.ServiceModel.Syndication return SyndicationFeedFormatter.CreateFeedInstance(_feedType); } - protected virtual async Task ReadItemAsync(XmlReader reader, SyndicationFeed feed) + protected virtual SyndicationItem ReadItem(XmlReader reader, SyndicationFeed feed) { if (feed == null) { - throw new ArgumentNullException(nameof(feed)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feed"); } if (reader == null) { - throw new ArgumentNullException(nameof(reader)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); } SyndicationItem item = CreateItem(feed); - XmlReaderWrapper readerWrapper = XmlReaderWrapper.CreateFromReader(reader); - await ReadItemFromAsync(readerWrapper, item, feed.BaseUri); // delegate => ItemParser(reader,item,feed.BaseUri);// + TraceItemReadBegin(); + ReadItemFrom(reader, item, feed.BaseUri); + TraceItemReadEnd(); return item; } - protected virtual async Task WriteItemAsync(XmlWriter writer, SyndicationItem item, Uri feedBaseUri) + [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Justification = "The out parameter is needed to enable implementations that read in items from the stream on demand")] + protected virtual IEnumerable ReadItems(XmlReader reader, SyndicationFeed feed, out bool areAllItemsRead) { - writer = XmlWriterWrapper.CreateFromWriter(writer); - - await writer.WriteStartElementAsync(Rss20Constants.ItemTag, Rss20Constants.Rss20Namespace); - await WriteItemContentsAsync(writer, item, feedBaseUri); - await writer.WriteEndElementAsync(); + if (feed == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feed"); + } + if (reader == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); + } + NullNotAllowedCollection items = new NullNotAllowedCollection(); + while (reader.IsStartElement(Rss20Constants.ItemTag, Rss20Constants.Rss20Namespace)) + { + items.Add(ReadItem(reader, feed)); + } + areAllItemsRead = true; + return items; } - protected virtual async Task WriteItemsAsync(XmlWriter writer, IEnumerable items, Uri feedBaseUri) + protected virtual void WriteItem(XmlWriter writer, SyndicationItem item, Uri feedBaseUri) + { + TraceItemWriteBegin(); + writer.WriteStartElement(Rss20Constants.ItemTag, Rss20Constants.Rss20Namespace); + WriteItemContents(writer, item, feedBaseUri); + writer.WriteEndElement(); + TraceItemWriteEnd(); + } + + protected virtual void WriteItems(XmlWriter writer, IEnumerable items, Uri feedBaseUri) { if (items == null) { return; } - foreach (SyndicationItem item in items) { - await this.WriteItemAsync(writer, item, feedBaseUri); + this.WriteItem(writer, item, feedBaseUri); } } - - private static string NormalizeTimeZone(string rfc822TimeZone, out bool isUtc) - { - isUtc = false; - // return a string in "-08:00" format - if (rfc822TimeZone[0] == '+' || rfc822TimeZone[0] == '-') - { - // the time zone is supposed to be 4 digits but some feeds omit the initial 0 - StringBuilder result = new StringBuilder(rfc822TimeZone); - if (result.Length == 4) - { - // the timezone is +/-HMM. Convert to +/-HHMM - result.Insert(1, '0'); - } - result.Insert(3, ':'); - return result.ToString(); - } - switch (rfc822TimeZone) - { - case "UT": - case "Z": - isUtc = true; - return "-00:00"; - case "GMT": - return "-00:00"; - case "A": - return "-01:00"; - case "B": - return "-02:00"; - case "C": - return "-03:00"; - case "D": - case "EDT": - return "-04:00"; - case "E": - case "EST": - case "CDT": - return "-05:00"; - case "F": - case "CST": - case "MDT": - return "-06:00"; - case "G": - case "MST": - case "PDT": - return "-07:00"; - case "H": - case "PST": - return "-08:00"; - case "I": - return "-09:00"; - case "K": - return "-10:00"; - case "L": - return "-11:00"; - case "M": - return "-12:00"; - case "N": - return "+01:00"; - case "O": - return "+02:00"; - case "P": - return "+03:00"; - case "Q": - return "+04:00"; - case "R": - return "+05:00"; - case "S": - return "+06:00"; - case "T": - return "+07:00"; - case "U": - return "+08:00"; - case "V": - return "+09:00"; - case "W": - return "+10:00"; - case "X": - return "+11:00"; - case "Y": - return "+12:00"; - default: - return ""; - } - } - - private async Task ReadSkipHoursAsync(XmlReaderWrapper reader, SyndicationFeed result) - { - await reader.ReadStartElementAsync(); - - while (await reader.IsStartElementAsync()) - { - if (reader.LocalName == Rss20Constants.HourTag) - { - string val = StringParser(await reader.ReadElementStringAsync(), Rss20Constants.HourTag, Rss20Constants.Rss20Namespace); - int hour = int.Parse(val); - bool parsed = false; - parsed = int.TryParse(val, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out hour); - - if (parsed == false) - { - throw new ArgumentException("The number on skip hours must be an integer betwen 0 and 23."); - } - - if (hour < 0 || hour > 23) - { - throw new ArgumentException("The hour can't be lower than 0 or greater than 23."); - } - - result.SkipHours.Add(hour); - } - else - { - await reader.SkipAsync(); - } - } - - await reader.ReadEndElementAsync(); - } - - - private bool checkDay(string day) - { - if (s_acceptedDays.Contains(day.ToLower())) - { - return true; - } - - return false; - } - - private async Task ReadSkipDaysAsync(XmlReaderWrapper reader, SyndicationFeed result) - { - await reader.ReadStartElementAsync(); - - while (await reader.IsStartElementAsync()) - { - if (reader.LocalName == Rss20Constants.DayTag) - { - string day = StringParser(await reader.ReadElementStringAsync(), Rss20Constants.DayTag, Rss20Constants.Rss20Namespace); - - //Check if the day is actually an accepted day. - if (checkDay(day)) - { - result.SkipDays.Add(day); - } - } - else - { - await reader.SkipAsync(); - } - } - - await reader.ReadEndElementAsync(); - } - - internal static void RemoveExtraWhiteSpaceAtStart(StringBuilder stringBuilder) - { - int i = 0; - while (i < stringBuilder.Length) - { - if (!char.IsWhiteSpace(stringBuilder[i])) - { - break; - } - ++i; - } - if (i > 0) - { - stringBuilder.Remove(0, i); - } - } - - private static void ReplaceMultipleWhiteSpaceWithSingleWhiteSpace(StringBuilder builder) - { - int index = 0; - int whiteSpaceStart = -1; - while (index < builder.Length) - { - if (char.IsWhiteSpace(builder[index])) - { - if (whiteSpaceStart < 0) - { - whiteSpaceStart = index; - // normalize all white spaces to be ' ' so that the date time parsing works - builder[index] = ' '; - } - } - else if (whiteSpaceStart >= 0) - { - if (index > whiteSpaceStart + 1) - { - // there are at least 2 spaces... replace by 1 - builder.Remove(whiteSpaceStart, index - whiteSpaceStart - 1); - index = whiteSpaceStart + 1; - } - whiteSpaceStart = -1; - } - ++index; - } - // we have already trimmed the start and end so there cannot be a trail of white spaces in the end - Debug.Assert(builder.Length == 0 || builder[builder.Length - 1] != ' ', "The string builder doesnt end in a white space"); - } - + private string AsString(DateTimeOffset dateTime) { - if (dateTime.Offset == Atom10FeedFormatter.zeroOffset) + if (dateTime.Offset == Atom10FeedFormatter.ZeroOffset) { return dateTime.ToUniversalTime().ToString(Rfc822OutputUtcDateTimeFormat, CultureInfo.InvariantCulture); } @@ -685,7 +275,7 @@ namespace System.ServiceModel.Syndication } } - private async Task ReadAlternateLinkAsync(XmlReaderWrapper reader, Uri baseUri) + private SyndicationLink ReadAlternateLink(XmlReader reader, Uri baseUri) { SyndicationLink link = new SyndicationLink(); link.BaseUri = baseUri; @@ -696,31 +286,42 @@ namespace System.ServiceModel.Syndication { if (reader.LocalName == "base" && reader.NamespaceURI == Atom10FeedFormatter.XmlNs) { - link.BaseUri = FeedUtils.CombineXmlBase(link.BaseUri, await reader.GetValueAsync()); + link.BaseUri = FeedUtils.CombineXmlBase(link.BaseUri, reader.Value); } else if (!FeedUtils.IsXmlns(reader.LocalName, reader.NamespaceURI)) { if (this.PreserveAttributeExtensions) { - link.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), await reader.GetValueAsync()); + link.AttributeExtensions.Add(new XmlQualifiedName(reader.LocalName, reader.NamespaceURI), reader.Value); + } + else + { + TraceSyndicationElementIgnoredOnRead(reader); } } } } - string localName = reader.LocalName; - string namespaceUri = reader.NamespaceURI; - link.Uri = UriParser(await reader.ReadElementStringAsync(), localName, namespaceUri);//new Uri(uri, UriKind.RelativeOrAbsolute); + + string uri = reader.ReadElementString(); + link.Uri = UriParser(uri, UriKind.RelativeOrAbsolute, Rss20Constants.LinkTag, Rss20Constants.Rss20Namespace); return link; } - private async Task ReadCategoryAsync(XmlReaderWrapper reader, SyndicationFeed feed) + private SyndicationCategory ReadCategory(XmlReader reader, SyndicationFeed feed) { SyndicationCategory result = CreateCategory(feed); - await ReadCategoryAsync(reader, result); + ReadCategory(reader, result); return result; } - private async Task ReadCategoryAsync(XmlReaderWrapper reader, SyndicationCategory category) + private SyndicationCategory ReadCategory(XmlReader reader, SyndicationItem item) + { + SyndicationCategory result = CreateCategory(item); + ReadCategory(reader, result); + return result; + } + + private void ReadCategory(XmlReader reader, SyndicationCategory category) { bool isEmpty = reader.IsEmptyElement; if (reader.HasAttributes) @@ -733,7 +334,7 @@ namespace System.ServiceModel.Syndication { continue; } - string val = await reader.GetValueAsync(); + string val = reader.Value; if (name == Rss20Constants.DomainTag && ns == Rss20Constants.Rss20Namespace) { category.Scheme = val; @@ -744,28 +345,231 @@ namespace System.ServiceModel.Syndication { category.AttributeExtensions.Add(new XmlQualifiedName(name, ns), val); } + else + { + TraceSyndicationElementIgnoredOnRead(reader); + } } } } - - await reader.ReadStartElementAsync(Rss20Constants.CategoryTag, Rss20Constants.Rss20Namespace); - + reader.ReadStartElement(Rss20Constants.CategoryTag, Rss20Constants.Rss20Namespace); if (!isEmpty) { - category.Name = StringParser(await reader.ReadStringAsync(), reader.LocalName, Rss20Constants.Rss20Namespace); - await reader.ReadEndElementAsync(); + category.Name = reader.ReadString(); + reader.ReadEndElement(); } } - private async Task ReadCategoryAsync(XmlReaderWrapper reader, SyndicationItem item) + private void ReadFeed(XmlReader reader) { - SyndicationCategory result = CreateCategory(item); - await ReadCategoryAsync(reader, result); - return result; + SetFeed(CreateFeedInstance()); + ReadXml(reader, this.Feed); } + private void ReadItemFrom(XmlReader reader, SyndicationItem result, Uri feedBaseUri) + { + try + { + result.BaseUri = feedBaseUri; + reader.MoveToContent(); + bool isEmpty = reader.IsEmptyElement; + if (reader.HasAttributes) + { + while (reader.MoveToNextAttribute()) + { + string ns = reader.NamespaceURI; + string name = reader.LocalName; + if (name == "base" && ns == Atom10FeedFormatter.XmlNs) + { + result.BaseUri = FeedUtils.CombineXmlBase(result.BaseUri, reader.Value); + continue; + } + if (FeedUtils.IsXmlns(name, ns) || FeedUtils.IsXmlSchemaType(name, ns)) + { + continue; + } + string val = reader.Value; + if (!TryParseAttribute(name, ns, val, result, this.Version)) + { + if (_preserveAttributeExtensions) + { + result.AttributeExtensions.Add(new XmlQualifiedName(name, ns), val); + } + else + { + TraceSyndicationElementIgnoredOnRead(reader); + } + } + } + } + reader.ReadStartElement(); + if (!isEmpty) + { + string fallbackAlternateLink = null; + string fallbackAlternateLinkLocalName = null; + string fallbackAlternateLinkNamespace = null; + XmlDictionaryWriter extWriter = null; + bool readAlternateLink = false; + try + { + XmlBuffer buffer = null; + while (reader.IsStartElement()) + { + if (reader.IsStartElement(Rss20Constants.TitleTag, Rss20Constants.Rss20Namespace)) + { + result.Title = new TextSyndicationContent(reader.ReadElementString()); + } + else if (reader.IsStartElement(Rss20Constants.LinkTag, Rss20Constants.Rss20Namespace)) + { + result.Links.Add(ReadAlternateLink(reader, result.BaseUri)); + readAlternateLink = true; + } + else if (reader.IsStartElement(Rss20Constants.DescriptionTag, Rss20Constants.Rss20Namespace)) + { + result.Summary = new TextSyndicationContent(reader.ReadElementString()); + } + else if (reader.IsStartElement(Rss20Constants.AuthorTag, Rss20Constants.Rss20Namespace)) + { + result.Authors.Add(ReadPerson(reader, result)); + } + else if (reader.IsStartElement(Rss20Constants.CategoryTag, Rss20Constants.Rss20Namespace)) + { + result.Categories.Add(ReadCategory(reader, result)); + } + else if (reader.IsStartElement(Rss20Constants.EnclosureTag, Rss20Constants.Rss20Namespace)) + { + result.Links.Add(ReadMediaEnclosure(reader, result.BaseUri)); + } + else if (reader.IsStartElement(Rss20Constants.GuidTag, Rss20Constants.Rss20Namespace)) + { + bool isPermalink = true; + string permalinkString = reader.GetAttribute(Rss20Constants.IsPermaLinkTag, Rss20Constants.Rss20Namespace); + if ((permalinkString != null) && (permalinkString.ToUpperInvariant() == "FALSE")) + { + isPermalink = false; + } - private async Task ReadMediaEnclosureAsync(XmlReaderWrapper reader, Uri baseUri) + result.Id = reader.ReadElementString(); + if (isPermalink) + { + fallbackAlternateLink = result.Id; + fallbackAlternateLinkLocalName = Rss20Constants.GuidTag; + fallbackAlternateLinkNamespace = Rss20Constants.Rss20Namespace; + } + } + else if (reader.IsStartElement(Rss20Constants.PubDateTag, Rss20Constants.Rss20Namespace)) + { + bool canReadContent = !reader.IsEmptyElement; + reader.ReadStartElement(); + if (canReadContent) + { + string str = reader.ReadString(); + if (!string.IsNullOrEmpty(str)) + { + try + { + result.PublishDate = DateFromString(str, reader); + } + catch (XmlException e) + { + result.PublishDateException = e; + } + } + reader.ReadEndElement(); + } + } + else if (reader.IsStartElement(Rss20Constants.SourceTag, Rss20Constants.Rss20Namespace)) + { + SyndicationFeed feed = new SyndicationFeed(); + if (reader.HasAttributes) + { + while (reader.MoveToNextAttribute()) + { + string ns = reader.NamespaceURI; + string name = reader.LocalName; + if (FeedUtils.IsXmlns(name, ns)) + { + continue; + } + string val = reader.Value; + if (name == Rss20Constants.UrlTag && ns == Rss20Constants.Rss20Namespace) + { + feed.Links.Add(SyndicationLink.CreateSelfLink(UriParser(val, UriKind.RelativeOrAbsolute, Rss20Constants.UrlTag, Rss20Constants.Rss20Namespace))); + } + else if (!FeedUtils.IsXmlns(name, ns)) + { + if (_preserveAttributeExtensions) + { + feed.AttributeExtensions.Add(new XmlQualifiedName(name, ns), val); + } + else + { + TraceSyndicationElementIgnoredOnRead(reader); + } + } + } + } + + string feedTitle = reader.ReadElementString(); + feed.Title = new TextSyndicationContent(feedTitle); + result.SourceFeed = feed; + } + else + { + bool parsedExtension = _serializeExtensionsAsAtom && _atomSerializer.TryParseItemElementFrom(reader, result); + if (!parsedExtension) + { + parsedExtension = TryParseElement(reader, result, this.Version); + } + if (!parsedExtension) + { + if (_preserveElementExtensions) + { + CreateBufferIfRequiredAndWriteNode(ref buffer, ref extWriter, reader, _maxExtensionSize); + } + else + { + TraceSyndicationElementIgnoredOnRead(reader); + reader.Skip(); + } + } + } + } + LoadElementExtensions(buffer, extWriter, result); + } + finally + { + if (extWriter != null) + { + ((IDisposable)extWriter).Dispose(); + } + } + reader.ReadEndElement(); // item + if (!readAlternateLink && fallbackAlternateLink != null) + { + result.Links.Add(SyndicationLink.CreateAlternateLink(UriParser(fallbackAlternateLink, UriKind.RelativeOrAbsolute, fallbackAlternateLinkLocalName, fallbackAlternateLinkNamespace))); + readAlternateLink = true; + } + + // if there's no content and no alternate link set the summary as the item content + if (result.Content == null && !readAlternateLink) + { + result.Content = result.Summary; + result.Summary = null; + } + } + } + catch (FormatException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingItem), e)); + } + catch (ArgumentException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingItem), e)); + } + } + + private SyndicationLink ReadMediaEnclosure(XmlReader reader, Uri baseUri) { SyndicationLink link = new SyndicationLink(); link.BaseUri = baseUri; @@ -779,17 +583,17 @@ namespace System.ServiceModel.Syndication string name = reader.LocalName; if (name == "base" && ns == Atom10FeedFormatter.XmlNs) { - link.BaseUri = FeedUtils.CombineXmlBase(link.BaseUri, await reader.GetValueAsync()); + link.BaseUri = FeedUtils.CombineXmlBase(link.BaseUri, reader.Value); continue; } if (FeedUtils.IsXmlns(name, ns)) { continue; } - string val = await reader.GetValueAsync(); + string val = reader.Value; if (name == Rss20Constants.UrlTag && ns == Rss20Constants.Rss20Namespace) { - link.Uri = new Uri(val, UriKind.RelativeOrAbsolute); + link.Uri = UriParser(val, UriKind.RelativeOrAbsolute, Rss20Constants.EnclosureTag, Rss20Constants.Rss20Namespace); } else if (name == Rss20Constants.TypeTag && ns == Rss20Constants.Rss20Namespace) { @@ -805,28 +609,36 @@ namespace System.ServiceModel.Syndication { link.AttributeExtensions.Add(new XmlQualifiedName(name, ns), val); } + else + { + TraceSyndicationElementIgnoredOnRead(reader); + } } } } - - await reader.ReadStartElementAsync(Rss20Constants.EnclosureTag, Rss20Constants.Rss20Namespace); - + reader.ReadStartElement(Rss20Constants.EnclosureTag, Rss20Constants.Rss20Namespace); if (!isEmptyElement) { - await reader.ReadEndElementAsync(); + reader.ReadEndElement(); } - return link; } - private async Task ReadPersonAsync(XmlReaderWrapper reader, SyndicationFeed feed) + private SyndicationPerson ReadPerson(XmlReader reader, SyndicationFeed feed) { SyndicationPerson result = CreatePerson(feed); - await ReadPersonAsync(reader, result); + ReadPerson(reader, result); return result; } - private async Task ReadPersonAsync(XmlReaderWrapper reader, SyndicationPerson person) + private SyndicationPerson ReadPerson(XmlReader reader, SyndicationItem item) + { + SyndicationPerson result = CreatePerson(item); + ReadPerson(reader, result); + return result; + } + + private void ReadPerson(XmlReader reader, SyndicationPerson person) { bool isEmpty = reader.IsEmptyElement; if (reader.HasAttributes) @@ -839,370 +651,292 @@ namespace System.ServiceModel.Syndication { continue; } - string val = await reader.GetValueAsync(); + string val = reader.Value; if (!TryParseAttribute(name, ns, val, person, this.Version)) { if (_preserveAttributeExtensions) { person.AttributeExtensions.Add(new XmlQualifiedName(name, ns), val); } + else + { + TraceSyndicationElementIgnoredOnRead(reader); + } } } } - - await reader.ReadStartElementAsync(); + reader.ReadStartElement(); if (!isEmpty) { - string email = StringParser(await reader.ReadStringAsync(), reader.LocalName, reader.NamespaceURI); - await reader.ReadEndElementAsync(); + string email = reader.ReadString(); + reader.ReadEndElement(); person.Email = email; } } - private async Task ReadPersonAsync(XmlReaderWrapper reader, SyndicationItem item) + private void ReadXml(XmlReader reader, SyndicationFeed result) { - SyndicationPerson result = CreatePerson(item); - await ReadPersonAsync(reader, result); - return result; - } - - private bool checkTextInput(SyndicationTextInput textInput) - { - //All textInput items are required, we check if all items were instantiated. - return (textInput.Description != null && textInput.title != null && textInput.name != null && textInput.link != null); - } - - private async Task readTextInputTag(XmlReaderWrapper reader, SyndicationFeed result) - { - await reader.ReadStartElementAsync(); - - SyndicationTextInput textInput = new SyndicationTextInput(); - string val = String.Empty; - - while (await reader.IsStartElementAsync()) - { - string name = reader.LocalName; - string namespaceUri = reader.NamespaceURI; - val = StringParser(await reader.ReadElementStringAsync(), name, Rss20Constants.Rss20Namespace); - - switch (name) - { - case Rss20Constants.DescriptionTag: - textInput.Description = val; - break; - - case Rss20Constants.TitleTag: - textInput.title = val; - break; - - case Rss20Constants.LinkTag: - textInput.link = new SyndicationLink(UriParser(val, name, namespaceUri)); - break; - - case Rss20Constants.NameTag: - textInput.name = val; - break; - - default: - //ignore! - break; - } - } - - if (checkTextInput(textInput) == true) - { - result.TextInput = textInput; - } - - await reader.ReadEndElementAsync(); - } - - private async Task ReadXmlAsync(XmlReaderWrapper reader, SyndicationFeed result) - { - string baseUri = null; - await reader.MoveToContentAsync(); - - string version = reader.GetAttribute(Rss20Constants.VersionTag, Rss20Constants.Rss20Namespace); - if (version != Rss20Constants.Version) - { - throw new NotSupportedException(FeedUtils.AddLineInfo(reader, (string.Format(SR.UnsupportedRssVersion, version)))); - } - - if (reader.AttributeCount > 1) - { - string tmp = reader.GetAttribute("base", Atom10FeedFormatter.XmlNs); - if (!string.IsNullOrEmpty(tmp)) - { - baseUri = tmp; - } - } - - await reader.ReadStartElementAsync(); - await reader.MoveToContentAsync(); - - if (reader.HasAttributes) - { - while (reader.MoveToNextAttribute()) - { - string ns = reader.NamespaceURI; - string name = reader.LocalName; - - if (name == "base" && ns == Atom10FeedFormatter.XmlNs) - { - baseUri = await reader.GetValueAsync(); - continue; - } - - if (FeedUtils.IsXmlns(name, ns) || FeedUtils.IsXmlSchemaType(name, ns)) - { - continue; - } - - string val = await reader.GetValueAsync(); - if (!TryParseAttribute(name, ns, val, result, this.Version)) - { - if (_preserveAttributeExtensions) - { - result.AttributeExtensions.Add(new XmlQualifiedName(name, ns), val); - } - } - } - } - - if (!string.IsNullOrEmpty(baseUri)) - { - result.BaseUri = new Uri(baseUri, UriKind.RelativeOrAbsolute); - } - - bool areAllItemsRead = true; - await reader.ReadStartElementAsync(Rss20Constants.ChannelTag, Rss20Constants.Rss20Namespace); - - XmlBuffer buffer = null; - XmlDictionaryWriter extWriter = null; - NullNotAllowedCollection feedItems = new NullNotAllowedCollection(); - try { - while (await reader.IsStartElementAsync()) + string baseUri = null; + string baseUriLocalName = null; + string baseUriNamespace = null; + reader.MoveToContent(); + string elementLocalName = reader.LocalName; + string elementNamespace = reader.NamespaceURI; + string version = reader.GetAttribute(Rss20Constants.VersionTag, Rss20Constants.Rss20Namespace); + if (version != Rss20Constants.Version) { - bool notHandled = false; - if (await reader.MoveToContentAsync() == XmlNodeType.Element && reader.NamespaceURI == Rss20Constants.Rss20Namespace) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(FeedUtils.AddLineInfo(reader, (SR.Format(SR.UnsupportedRssVersion, version))))); + } + if (reader.AttributeCount > 1) + { + string tmp = reader.GetAttribute("base", Atom10FeedFormatter.XmlNs); + if (!string.IsNullOrEmpty(tmp)) { - switch (reader.LocalName) - { - case Rss20Constants.TitleTag: - result.Title = new TextSyndicationContent(StringParser(await reader.ReadElementStringAsync(), Rss20Constants.TitleTag, Rss20Constants.Rss20Namespace)); - break; - - case Rss20Constants.LinkTag: - result.Links.Add(await ReadAlternateLinkAsync(reader, result.BaseUri)); - break; - - case Rss20Constants.DescriptionTag: - result.Description = new TextSyndicationContent(StringParser(await reader.ReadElementStringAsync(), Rss20Constants.DescriptionTag, Rss20Constants.Rss20Namespace)); - break; - - case Rss20Constants.LanguageTag: - - result.Language = StringParser(await reader.ReadElementStringAsync(), Rss20Constants.LanguageTag, Rss20Constants.Rss20Namespace); - break; - - case Rss20Constants.CopyrightTag: - result.Copyright = new TextSyndicationContent(StringParser(await reader.ReadElementStringAsync(), Rss20Constants.CopyrightTag, Rss20Constants.Rss20Namespace)); - break; - - case Rss20Constants.ManagingEditorTag: - result.Authors.Add(await ReadPersonAsync(reader, result)); - break; - - case Rss20Constants.LastBuildDateTag: - { - bool canReadContent = !reader.IsEmptyElement; - await reader.ReadStartElementAsync(); - if (canReadContent) - { - string str = await reader.ReadStringAsync(); - - if (!string.IsNullOrEmpty(str)) - { - result.LastUpdatedTime = DateParser(str, Rss20Constants.LastBuildDateTag, reader.NamespaceURI); - } - - await reader.ReadEndElementAsync(); - } - - break; - } - - case Rss20Constants.CategoryTag: - result.Categories.Add(await ReadCategoryAsync(reader, result)); - break; - - case Rss20Constants.GeneratorTag: - result.Generator = StringParser(await reader.ReadElementStringAsync(), Rss20Constants.GeneratorTag, Rss20Constants.Rss20Namespace); - break; - - case Rss20Constants.ImageTag: - { - await OnReadImage(reader, result); - break; - } - - case Rss20Constants.ItemTag: - { - NullNotAllowedCollection items = new NullNotAllowedCollection(); - while (await reader.IsStartElementAsync(Rss20Constants.ItemTag, Rss20Constants.Rss20Namespace)) - { - feedItems.Add(await ReadItemAsync(reader, result)); - } - - - areAllItemsRead = true; - break; - } - - //Optional tags - case Rss20Constants.DocumentationTag: - result.Documentation = await ReadAlternateLinkAsync(reader, result.BaseUri); - break; - - case Rss20Constants.TimeToLiveTag: - string value = StringParser(await reader.ReadElementStringAsync(), Rss20Constants.TimeToLiveTag, Rss20Constants.Rss20Namespace); - int timeToLive = int.Parse(value); - result.TimeToLive = timeToLive; - break; - - case Rss20Constants.TextInputTag: - await readTextInputTag(reader, result); - break; - - case Rss20Constants.SkipHoursTag: - await ReadSkipHoursAsync(reader, result); - break; - - case Rss20Constants.SkipDaysTag: - await ReadSkipDaysAsync(reader, result); - break; - - default: - notHandled = true; - break; - } + baseUri = tmp; + baseUriLocalName = elementLocalName; + baseUriNamespace = elementNamespace; } - else + } + reader.ReadStartElement(); + reader.MoveToContent(); + elementLocalName = reader.LocalName; + elementNamespace = reader.NamespaceURI; + if (reader.HasAttributes) + { + while (reader.MoveToNextAttribute()) { - notHandled = true; - } - - if (notHandled) - { - bool parsedExtension = _serializeExtensionsAsAtom && await _atomSerializer.TryParseFeedElementFromAsync(reader, result); - - if (!parsedExtension) + string ns = reader.NamespaceURI; + string name = reader.LocalName; + if (name == "base" && ns == Atom10FeedFormatter.XmlNs) { - parsedExtension = TryParseElement(reader, result, this.Version); + baseUri = reader.Value; + baseUriLocalName = elementLocalName; + baseUriNamespace = elementNamespace; + continue; } - - if (!parsedExtension) + if (FeedUtils.IsXmlns(name, ns) || FeedUtils.IsXmlSchemaType(name, ns)) { - if (_preserveElementExtensions) + continue; + } + string val = reader.Value; + if (!TryParseAttribute(name, ns, val, result, this.Version)) + { + if (_preserveAttributeExtensions) { - if (buffer == null) - { - buffer = new XmlBuffer(_maxExtensionSize); - extWriter = buffer.OpenSection(XmlDictionaryReaderQuotas.Max); - extWriter.WriteStartElement(Rss20Constants.ExtensionWrapperTag); - } - - await XmlReaderWrapper.WriteNodeAsync(extWriter, reader, false); + result.AttributeExtensions.Add(new XmlQualifiedName(name, ns), val); } else { - await reader.SkipAsync(); + TraceSyndicationElementIgnoredOnRead(reader); + } + } + } + } + + if (!string.IsNullOrEmpty(baseUri)) + { + result.BaseUri = UriParser(baseUri, UriKind.RelativeOrAbsolute, baseUriLocalName, baseUriNamespace); + } + + bool areAllItemsRead = true; + reader.ReadStartElement(Rss20Constants.ChannelTag, Rss20Constants.Rss20Namespace); + + XmlBuffer buffer = null; + XmlDictionaryWriter extWriter = null; + NullNotAllowedCollection feedItems = null; + + try + { + while (reader.IsStartElement()) + { + if (reader.IsStartElement(Rss20Constants.TitleTag, Rss20Constants.Rss20Namespace)) + { + result.Title = new TextSyndicationContent(reader.ReadElementString()); + } + else if (reader.IsStartElement(Rss20Constants.LinkTag, Rss20Constants.Rss20Namespace)) + { + result.Links.Add(ReadAlternateLink(reader, result.BaseUri)); + } + else if (reader.IsStartElement(Rss20Constants.DescriptionTag, Rss20Constants.Rss20Namespace)) + { + result.Description = new TextSyndicationContent(reader.ReadElementString()); + } + else if (reader.IsStartElement(Rss20Constants.LanguageTag, Rss20Constants.Rss20Namespace)) + { + result.Language = reader.ReadElementString(); + } + else if (reader.IsStartElement(Rss20Constants.CopyrightTag, Rss20Constants.Rss20Namespace)) + { + result.Copyright = new TextSyndicationContent(reader.ReadElementString()); + } + else if (reader.IsStartElement(Rss20Constants.ManagingEditorTag, Rss20Constants.Rss20Namespace)) + { + result.Authors.Add(ReadPerson(reader, result)); + } + else if (reader.IsStartElement(Rss20Constants.LastBuildDateTag, Rss20Constants.Rss20Namespace)) + { + bool canReadContent = !reader.IsEmptyElement; + reader.ReadStartElement(); + if (canReadContent) + { + string str = reader.ReadString(); + if (!string.IsNullOrEmpty(str)) + { + try + { + result.LastUpdatedTime = DateFromString(str, reader); + } + catch (XmlException e) + { + result.LastUpdatedTimeException = e; + } + } + reader.ReadEndElement(); + } + } + else if (reader.IsStartElement(Rss20Constants.CategoryTag, Rss20Constants.Rss20Namespace)) + { + result.Categories.Add(ReadCategory(reader, result)); + } + else if (reader.IsStartElement(Rss20Constants.GeneratorTag, Rss20Constants.Rss20Namespace)) + { + result.Generator = reader.ReadElementString(); + } + else if (reader.IsStartElement(Rss20Constants.ImageTag, Rss20Constants.Rss20Namespace)) + { + reader.ReadStartElement(); + while (reader.IsStartElement()) + { + if (reader.IsStartElement(Rss20Constants.UrlTag, Rss20Constants.Rss20Namespace)) + { + result.ImageUrl = UriParser(reader.ReadElementString(), UriKind.RelativeOrAbsolute, Rss20Constants.UrlTag, Rss20Constants.Rss20Namespace); + } + else + { + // ignore other content + TraceSyndicationElementIgnoredOnRead(reader); + reader.Skip(); + } + } + reader.ReadEndElement(); // image + } + else if (reader.IsStartElement(Rss20Constants.ItemTag, Rss20Constants.Rss20Namespace)) + { + feedItems = feedItems ?? new NullNotAllowedCollection(); + IEnumerable items = ReadItems(reader, result, out areAllItemsRead); + foreach(SyndicationItem item in items) + { + feedItems.Add(item); + } + + // if the derived class is reading the items lazily, then stop reading from the stream + if (!areAllItemsRead) + { + break; + } + } + else + { + bool parsedExtension = _serializeExtensionsAsAtom && _atomSerializer.TryParseFeedElementFrom(reader, result); + if (!parsedExtension) + { + parsedExtension = TryParseElement(reader, result, this.Version); + } + if (!parsedExtension) + { + if (_preserveElementExtensions) + { + CreateBufferIfRequiredAndWriteNode(ref buffer, ref extWriter, reader, _maxExtensionSize); + } + else + { + TraceSyndicationElementIgnoredOnRead(reader); + reader.Skip(); + } } } } - if (!areAllItemsRead) + if (feedItems != null) { - break; + result.Items = feedItems; + } + + LoadElementExtensions(buffer, extWriter, result); + } + finally + { + if (extWriter != null) + { + ((IDisposable)extWriter).Dispose(); } } - - //asign all read items to feed items. - result.Items = feedItems; - LoadElementExtensions(buffer, extWriter, result); + if (areAllItemsRead) + { + reader.ReadEndElement(); // channel + reader.ReadEndElement(); // rss + } } catch (FormatException e) { - throw new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingFeed), e); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingFeed), e)); } catch (ArgumentException e) { - throw new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingFeed), e); - } - finally - { - if (extWriter != null) - { - ((IDisposable)extWriter).Dispose(); - } - } - if (areAllItemsRead) - { - await reader.ReadEndElementAsync(); // channel - await reader.ReadEndElementAsync(); // rss + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingFeed), e)); } } - private async Task WriteAlternateLinkAsync(XmlWriter writer, SyndicationLink link, Uri baseUri) + private void WriteAlternateLink(XmlWriter writer, SyndicationLink link, Uri baseUri) { - await writer.WriteStartElementAsync(Rss20Constants.LinkTag, Rss20Constants.Rss20Namespace); + writer.WriteStartElement(Rss20Constants.LinkTag, Rss20Constants.Rss20Namespace); Uri baseUriToWrite = FeedUtils.GetBaseUriToWrite(baseUri, link.BaseUri); if (baseUriToWrite != null) { - await writer.WriteAttributeStringAsync("xml", "base", Atom10FeedFormatter.XmlNs, FeedUtils.GetUriString(baseUriToWrite)); + writer.WriteAttributeString("xml", "base", Atom10FeedFormatter.XmlNs, FeedUtils.GetUriString(baseUriToWrite)); } - await link.WriteAttributeExtensionsAsync(writer, SyndicationVersions.Rss20); - await writer.WriteStringAsync(FeedUtils.GetUriString(link.Uri)); - await writer.WriteEndElementAsync(); + link.WriteAttributeExtensions(writer, SyndicationVersions.Rss20); + writer.WriteString(FeedUtils.GetUriString(link.Uri)); + writer.WriteEndElement(); } - private async Task WriteCategoryAsync(XmlWriter writer, SyndicationCategory category) + private void WriteCategory(XmlWriter writer, SyndicationCategory category) { if (category == null) { return; } - await writer.WriteStartElementAsync(Rss20Constants.CategoryTag, Rss20Constants.Rss20Namespace); - await WriteAttributeExtensionsAsync(writer, category, this.Version); + writer.WriteStartElement(Rss20Constants.CategoryTag, Rss20Constants.Rss20Namespace); + WriteAttributeExtensions(writer, category, this.Version); if (!string.IsNullOrEmpty(category.Scheme) && !category.AttributeExtensions.ContainsKey(s_rss20Domain)) { - await writer.WriteAttributeStringAsync(Rss20Constants.DomainTag, Rss20Constants.Rss20Namespace, category.Scheme); + writer.WriteAttributeString(Rss20Constants.DomainTag, Rss20Constants.Rss20Namespace, category.Scheme); } - await writer.WriteStringAsync(category.Name); - await writer.WriteEndElementAsync(); + writer.WriteString(category.Name); + writer.WriteEndElement(); } - private async Task WriteFeedAsync(XmlWriter writer) + private void WriteFeed(XmlWriter writer) { if (this.Feed == null) { - throw new InvalidOperationException(SR.FeedFormatterDoesNotHaveFeed); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.FeedFormatterDoesNotHaveFeed))); } if (_serializeExtensionsAsAtom) { - await writer.InternalWriteAttributeStringAsync("xmlns", Atom10Constants.Atom10Prefix, null, Atom10Constants.Atom10Namespace); + writer.WriteAttributeString("xmlns", Atom10Constants.Atom10Prefix, null, Atom10Constants.Atom10Namespace); } - await writer.WriteAttributeStringAsync(Rss20Constants.VersionTag, Rss20Constants.Version); - await writer.WriteStartElementAsync(Rss20Constants.ChannelTag, Rss20Constants.Rss20Namespace); + writer.WriteAttributeString(Rss20Constants.VersionTag, Rss20Constants.Version); + writer.WriteStartElement(Rss20Constants.ChannelTag, Rss20Constants.Rss20Namespace); if (this.Feed.BaseUri != null) { - await writer.InternalWriteAttributeStringAsync("xml", "base", Atom10FeedFormatter.XmlNs, FeedUtils.GetUriString(this.Feed.BaseUri)); + writer.WriteAttributeString("xml", "base", Atom10FeedFormatter.XmlNs, FeedUtils.GetUriString(this.Feed.BaseUri)); } - await WriteAttributeExtensionsAsync(writer, this.Feed, this.Version); + WriteAttributeExtensions(writer, this.Feed, this.Version); string title = this.Feed.Title != null ? this.Feed.Title.Text : string.Empty; - await writer.WriteElementStringAsync(Rss20Constants.TitleTag, Rss20Constants.Rss20Namespace, title); + writer.WriteElementString(Rss20Constants.TitleTag, Rss20Constants.Rss20Namespace, title); SyndicationLink alternateLink = null; for (int i = 0; i < this.Feed.Links.Count; ++i) @@ -1210,127 +944,90 @@ namespace System.ServiceModel.Syndication if (this.Feed.Links[i].RelationshipType == Atom10Constants.AlternateTag) { alternateLink = this.Feed.Links[i]; - await WriteAlternateLinkAsync(writer, alternateLink, this.Feed.BaseUri); + WriteAlternateLink(writer, alternateLink, this.Feed.BaseUri); break; } } string description = this.Feed.Description != null ? this.Feed.Description.Text : string.Empty; - await writer.WriteElementStringAsync(Rss20Constants.DescriptionTag, Rss20Constants.Rss20Namespace, description); + writer.WriteElementString(Rss20Constants.DescriptionTag, Rss20Constants.Rss20Namespace, description); if (this.Feed.Language != null) { - await writer.WriteElementStringAsync(Rss20Constants.LanguageTag, this.Feed.Language); + writer.WriteElementString(Rss20Constants.LanguageTag, this.Feed.Language); } if (this.Feed.Copyright != null) { - await writer.WriteElementStringAsync(Rss20Constants.CopyrightTag, Rss20Constants.Rss20Namespace, this.Feed.Copyright.Text); + writer.WriteElementString(Rss20Constants.CopyrightTag, Rss20Constants.Rss20Namespace, this.Feed.Copyright.Text); } // if there's a single author with an email address, then serialize as the managingEditor // else serialize the authors as Atom extensions +#pragma warning disable 56506 // tvish: this.Feed.Authors is never null if ((this.Feed.Authors.Count == 1) && (this.Feed.Authors[0].Email != null)) +#pragma warning restore 56506 { - await WritePersonAsync(writer, Rss20Constants.ManagingEditorTag, this.Feed.Authors[0]); + WritePerson(writer, Rss20Constants.ManagingEditorTag, this.Feed.Authors[0]); } else { if (_serializeExtensionsAsAtom) { - await _atomSerializer.WriteFeedAuthorsToAsync(writer, this.Feed.Authors); + _atomSerializer.WriteFeedAuthorsTo(writer, this.Feed.Authors); + } + else + { + TraceExtensionsIgnoredOnWrite(SR.FeedAuthorsIgnoredOnWrite); } } if (this.Feed.LastUpdatedTime > DateTimeOffset.MinValue) { - await writer.WriteStartElementAsync(Rss20Constants.LastBuildDateTag); - await writer.WriteStringAsync(AsString(this.Feed.LastUpdatedTime)); - await writer.WriteEndElementAsync(); + writer.WriteStartElement(Rss20Constants.LastBuildDateTag); + writer.WriteString(AsString(this.Feed.LastUpdatedTime)); + writer.WriteEndElement(); } +#pragma warning disable 56506 // tvish: this.Feed.Categories is never null for (int i = 0; i < this.Feed.Categories.Count; ++i) +#pragma warning restore 56506 { - await WriteCategoryAsync(writer, this.Feed.Categories[i]); + WriteCategory(writer, this.Feed.Categories[i]); } if (!string.IsNullOrEmpty(this.Feed.Generator)) { - await writer.WriteElementStringAsync(Rss20Constants.GeneratorTag, this.Feed.Generator); + writer.WriteElementString(Rss20Constants.GeneratorTag, this.Feed.Generator); } +#pragma warning disable 56506 // tvish: this.Feed.Contributors is never null if (this.Feed.Contributors.Count > 0) +#pragma warning restore 56506 { if (_serializeExtensionsAsAtom) { - await _atomSerializer.WriteFeedContributorsToAsync(writer, this.Feed.Contributors); + _atomSerializer.WriteFeedContributorsTo(writer, this.Feed.Contributors); + } + else + { + TraceExtensionsIgnoredOnWrite(SR.FeedContributorsIgnoredOnWrite); } } if (this.Feed.ImageUrl != null) { - await writer.WriteStartElementAsync(Rss20Constants.ImageTag); - await writer.WriteElementStringAsync(Rss20Constants.UrlTag, FeedUtils.GetUriString(this.Feed.ImageUrl)); - - string imageTitle = Feed.ImageTitle == null ? title : Feed.ImageTitle.Text; - await writer.WriteElementStringAsync(Rss20Constants.TitleTag, Rss20Constants.Rss20Namespace, imageTitle); - - string imgAlternateLink = alternateLink != null ? FeedUtils.GetUriString(alternateLink.Uri) : string.Empty; - - string imageLink = Feed.ImageLink == null ? imgAlternateLink : FeedUtils.GetUriString(Feed.ImageLink); - await writer.WriteElementStringAsync(Rss20Constants.LinkTag, Rss20Constants.Rss20Namespace, imageLink); - await writer.WriteEndElementAsync(); // image - } - - //Optional spec items - //time to live - if (this.Feed.TimeToLive != 0) - { - await writer.WriteElementStringAsync(Rss20Constants.TimeToLiveTag, this.Feed.TimeToLive.ToString()); - } - - //skiphours - if (this.Feed.SkipHours.Count > 0) - { - await writer.WriteStartElementAsync(Rss20Constants.SkipHoursTag); - - foreach (int hour in this.Feed.SkipHours) - { - writer.WriteElementString(Rss20Constants.HourTag, hour.ToString()); - } - - await writer.WriteEndElementAsync(); - } - - //skipDays - if (this.Feed.SkipDays.Count > 0) - { - await writer.WriteStartElementAsync(Rss20Constants.SkipDaysTag); - - foreach (string day in this.Feed.SkipDays) - { - await writer.WriteElementStringAsync(Rss20Constants.DayTag, day); - } - - await writer.WriteEndElementAsync(); - } - - //textinput - if (this.Feed.TextInput != null) - { - await writer.WriteStartElementAsync(Rss20Constants.TextInputTag); - - await writer.WriteElementStringAsync(Rss20Constants.DescriptionTag, this.Feed.TextInput.Description); - await writer.WriteElementStringAsync(Rss20Constants.TitleTag, this.Feed.TextInput.title); - await writer.WriteElementStringAsync(Rss20Constants.LinkTag, this.Feed.TextInput.link.GetAbsoluteUri().ToString()); - await writer.WriteElementStringAsync(Rss20Constants.NameTag, this.Feed.TextInput.name); - - await writer.WriteEndElementAsync(); + writer.WriteStartElement(Rss20Constants.ImageTag); + writer.WriteElementString(Rss20Constants.UrlTag, FeedUtils.GetUriString(this.Feed.ImageUrl)); + writer.WriteElementString(Rss20Constants.TitleTag, Rss20Constants.Rss20Namespace, title); + string imgAlternateLink = (alternateLink != null) ? FeedUtils.GetUriString(alternateLink.Uri) : string.Empty; + writer.WriteElementString(Rss20Constants.LinkTag, Rss20Constants.Rss20Namespace, imgAlternateLink); + writer.WriteEndElement(); // image } if (_serializeExtensionsAsAtom) { - await _atomSerializer.WriteElementAsync(writer, Atom10Constants.IdTag, this.Feed.Id); + _atomSerializer.WriteElement(writer, Atom10Constants.IdTag, this.Feed.Id); // dont write out the 1st alternate link since that would have been written out anyway bool isFirstAlternateLink = true; @@ -1341,23 +1038,34 @@ namespace System.ServiceModel.Syndication isFirstAlternateLink = false; continue; } - await _atomSerializer.WriteLinkAsync(writer, this.Feed.Links[i], this.Feed.BaseUri); + _atomSerializer.WriteLink(writer, this.Feed.Links[i], this.Feed.BaseUri); + } + } + else + { + if (this.Feed.Id != null) + { + TraceExtensionsIgnoredOnWrite(SR.FeedIdIgnoredOnWrite); + } + if (this.Feed.Links.Count > 1) + { + TraceExtensionsIgnoredOnWrite(SR.FeedLinksIgnoredOnWrite); } } - await WriteElementExtensionsAsync(writer, this.Feed, this.Version); - await WriteItemsAsync(writer, this.Feed.Items, this.Feed.BaseUri); - await writer.WriteEndElementAsync(); // channel + WriteElementExtensions(writer, this.Feed, this.Version); + WriteItems(writer, this.Feed.Items, this.Feed.BaseUri); + writer.WriteEndElement(); // channel } - private async Task WriteItemContentsAsync(XmlWriter writer, SyndicationItem item, Uri feedBaseUri) + private void WriteItemContents(XmlWriter writer, SyndicationItem item, Uri feedBaseUri) { Uri baseUriToWrite = FeedUtils.GetBaseUriToWrite(feedBaseUri, item.BaseUri); if (baseUriToWrite != null) { - await writer.InternalWriteAttributeStringAsync("xml", "base", Atom10FeedFormatter.XmlNs, FeedUtils.GetUriString(baseUriToWrite)); + writer.WriteAttributeString("xml", "base", Atom10FeedFormatter.XmlNs, FeedUtils.GetUriString(baseUriToWrite)); } - await WriteAttributeExtensionsAsync(writer, item, this.Version); + WriteAttributeExtensions(writer, item, this.Version); string guid = item.Id ?? string.Empty; bool isPermalink = false; SyndicationLink firstAlternateLink = null; @@ -1378,44 +1086,52 @@ namespace System.ServiceModel.Syndication } if (!string.IsNullOrEmpty(guid)) { - await writer.WriteStartElementAsync(Rss20Constants.GuidTag); + writer.WriteStartElement(Rss20Constants.GuidTag); if (isPermalink) { - await writer.WriteAttributeStringAsync(Rss20Constants.IsPermaLinkTag, "true"); + writer.WriteAttributeString(Rss20Constants.IsPermaLinkTag, "true"); } else { - await writer.WriteAttributeStringAsync(Rss20Constants.IsPermaLinkTag, "false"); + writer.WriteAttributeString(Rss20Constants.IsPermaLinkTag, "false"); } - await writer.WriteStringAsync(guid); - await writer.WriteEndElementAsync(); + writer.WriteString(guid); + writer.WriteEndElement(); } if (firstAlternateLink != null) { - await WriteAlternateLinkAsync(writer, firstAlternateLink, (item.BaseUri != null ? item.BaseUri : feedBaseUri)); + WriteAlternateLink(writer, firstAlternateLink, (item.BaseUri != null ? item.BaseUri : feedBaseUri)); } +#pragma warning disable 56506 // tvish, item.Authors is never null if (item.Authors.Count == 1 && !string.IsNullOrEmpty(item.Authors[0].Email)) +#pragma warning restore 56506 { - await WritePersonAsync(writer, Rss20Constants.AuthorTag, item.Authors[0]); + WritePerson(writer, Rss20Constants.AuthorTag, item.Authors[0]); } else { if (_serializeExtensionsAsAtom) { - await _atomSerializer.WriteItemAuthorsToAsync(writer, item.Authors); + _atomSerializer.WriteItemAuthorsTo(writer, item.Authors); + } + else + { + TraceExtensionsIgnoredOnWrite(SR.ItemAuthorsIgnoredOnWrite); } } +#pragma warning disable 56506 // tvish, item.Categories is never null for (int i = 0; i < item.Categories.Count; ++i) +#pragma warning restore 56506 { - await WriteCategoryAsync(writer, item.Categories[i]); + WriteCategory(writer, item.Categories[i]); } bool serializedTitle = false; if (item.Title != null) { - await writer.WriteElementStringAsync(Rss20Constants.TitleTag, item.Title.Text); + writer.WriteElementString(Rss20Constants.TitleTag, item.Title.Text); serializedTitle = true; } @@ -1433,13 +1149,13 @@ namespace System.ServiceModel.Syndication } if (summary != null) { - await writer.WriteElementStringAsync(Rss20Constants.DescriptionTag, Rss20Constants.Rss20Namespace, summary.Text); + writer.WriteElementString(Rss20Constants.DescriptionTag, Rss20Constants.Rss20Namespace, summary.Text); } if (item.SourceFeed != null) { - await writer.WriteStartElementAsync(Rss20Constants.SourceTag, Rss20Constants.Rss20Namespace); - await WriteAttributeExtensionsAsync(writer, item.SourceFeed, this.Version); + writer.WriteStartElement(Rss20Constants.SourceTag, Rss20Constants.Rss20Namespace); + WriteAttributeExtensions(writer, item.SourceFeed, this.Version); SyndicationLink selfLink = null; for (int i = 0; i < item.SourceFeed.Links.Count; ++i) { @@ -1451,22 +1167,22 @@ namespace System.ServiceModel.Syndication } if (selfLink != null && !item.SourceFeed.AttributeExtensions.ContainsKey(s_rss20Url)) { - await writer.WriteAttributeStringAsync(Rss20Constants.UrlTag, Rss20Constants.Rss20Namespace, FeedUtils.GetUriString(selfLink.Uri)); + writer.WriteAttributeString(Rss20Constants.UrlTag, Rss20Constants.Rss20Namespace, FeedUtils.GetUriString(selfLink.Uri)); } string title = (item.SourceFeed.Title != null) ? item.SourceFeed.Title.Text : string.Empty; - await writer.WriteStringAsync(title); - await writer.WriteEndElementAsync(); + writer.WriteString(title); + writer.WriteEndElement(); } if (item.PublishDate > DateTimeOffset.MinValue) { - await writer.WriteElementStringAsync(Rss20Constants.PubDateTag, Rss20Constants.Rss20Namespace, AsString(item.PublishDate)); + writer.WriteElementString(Rss20Constants.PubDateTag, Rss20Constants.Rss20Namespace, AsString(item.PublishDate)); } // serialize the enclosures SyndicationLink firstEnclosureLink = null; bool passedFirstAlternateLink = false; - + bool isLinkIgnored = false; for (int i = 0; i < item.Links.Count; ++i) { if (item.Links[i].RelationshipType == Rss20Constants.EnclosureTag) @@ -1474,7 +1190,7 @@ namespace System.ServiceModel.Syndication if (firstEnclosureLink == null) { firstEnclosureLink = item.Links[i]; - await WriteMediaEnclosureAsync(writer, item.Links[i], item.BaseUri); + WriteMediaEnclosure(writer, item.Links[i], item.BaseUri); continue; } } @@ -1488,135 +1204,98 @@ namespace System.ServiceModel.Syndication } if (_serializeExtensionsAsAtom) { - await _atomSerializer.WriteLinkAsync(writer, item.Links[i], item.BaseUri); + _atomSerializer.WriteLink(writer, item.Links[i], item.BaseUri); + } + else + { + isLinkIgnored = true; } } - + if (isLinkIgnored) + { + TraceExtensionsIgnoredOnWrite(SR.ItemLinksIgnoredOnWrite); + } if (item.LastUpdatedTime > DateTimeOffset.MinValue) { if (_serializeExtensionsAsAtom) { - await _atomSerializer.WriteItemLastUpdatedTimeToAsync(writer, item.LastUpdatedTime); + _atomSerializer.WriteItemLastUpdatedTimeTo(writer, item.LastUpdatedTime); + } + else + { + TraceExtensionsIgnoredOnWrite(SR.ItemLastUpdatedTimeIgnoredOnWrite); } } if (_serializeExtensionsAsAtom) { - await _atomSerializer.WriteContentToAsync(writer, Atom10Constants.RightsTag, item.Copyright); + _atomSerializer.WriteContentTo(writer, Atom10Constants.RightsTag, item.Copyright); + } + else + { + TraceExtensionsIgnoredOnWrite(SR.ItemCopyrightIgnoredOnWrite); } if (!serializedContentAsDescription) { if (_serializeExtensionsAsAtom) { - await _atomSerializer.WriteContentToAsync(writer, Atom10Constants.ContentTag, item.Content); + _atomSerializer.WriteContentTo(writer, Atom10Constants.ContentTag, item.Content); + } + else + { + TraceExtensionsIgnoredOnWrite(SR.ItemContentIgnoredOnWrite); } } +#pragma warning disable 56506 // tvish, item.COntributors is never null if (item.Contributors.Count > 0) +#pragma warning restore 56506 { if (_serializeExtensionsAsAtom) { - await _atomSerializer.WriteItemContributorsToAsync(writer, item.Contributors); + _atomSerializer.WriteItemContributorsTo(writer, item.Contributors); + } + else + { + TraceExtensionsIgnoredOnWrite(SR.ItemContributorsIgnoredOnWrite); } } - await WriteElementExtensionsAsync(writer, item, this.Version); + WriteElementExtensions(writer, item, this.Version); } - private async Task WriteMediaEnclosureAsync(XmlWriter writer, SyndicationLink link, Uri baseUri) + private void WriteMediaEnclosure(XmlWriter writer, SyndicationLink link, Uri baseUri) { - await writer.WriteStartElementAsync(Rss20Constants.EnclosureTag, Rss20Constants.Rss20Namespace); + writer.WriteStartElement(Rss20Constants.EnclosureTag, Rss20Constants.Rss20Namespace); Uri baseUriToWrite = FeedUtils.GetBaseUriToWrite(baseUri, link.BaseUri); if (baseUriToWrite != null) { - await writer.InternalWriteAttributeStringAsync("xml", "base", Atom10FeedFormatter.XmlNs, FeedUtils.GetUriString(baseUriToWrite)); + writer.WriteAttributeString("xml", "base", Atom10FeedFormatter.XmlNs, FeedUtils.GetUriString(baseUriToWrite)); } - await link.WriteAttributeExtensionsAsync(writer, SyndicationVersions.Rss20); + link.WriteAttributeExtensions(writer, SyndicationVersions.Rss20); if (!link.AttributeExtensions.ContainsKey(s_rss20Url)) { - await writer.WriteAttributeStringAsync(Rss20Constants.UrlTag, Rss20Constants.Rss20Namespace, FeedUtils.GetUriString(link.Uri)); + writer.WriteAttributeString(Rss20Constants.UrlTag, Rss20Constants.Rss20Namespace, FeedUtils.GetUriString(link.Uri)); } if (link.MediaType != null && !link.AttributeExtensions.ContainsKey(s_rss20Type)) { - await writer.WriteAttributeStringAsync(Rss20Constants.TypeTag, Rss20Constants.Rss20Namespace, link.MediaType); + writer.WriteAttributeString(Rss20Constants.TypeTag, Rss20Constants.Rss20Namespace, link.MediaType); } if (link.Length != 0 && !link.AttributeExtensions.ContainsKey(s_rss20Length)) { - await writer.WriteAttributeStringAsync(Rss20Constants.LengthTag, Rss20Constants.Rss20Namespace, Convert.ToString(link.Length, CultureInfo.InvariantCulture)); + writer.WriteAttributeString(Rss20Constants.LengthTag, Rss20Constants.Rss20Namespace, Convert.ToString(link.Length, CultureInfo.InvariantCulture)); } - await writer.WriteEndElementAsync(); + writer.WriteEndElement(); } - private async Task WritePersonAsync(XmlWriter writer, string elementTag, SyndicationPerson person) + private void WritePerson(XmlWriter writer, string elementTag, SyndicationPerson person) { - await writer.WriteStartElementAsync(elementTag, Rss20Constants.Rss20Namespace); - await WriteAttributeExtensionsAsync(writer, person, this.Version); - await writer.WriteStringAsync(person.Email); - await writer.WriteEndElementAsync(); - } - - private static bool OriginalDateParser(string dateTimeString, out DateTimeOffset dto) - { - StringBuilder dateTimeStringBuilder = new StringBuilder(dateTimeString.Trim()); - if (dateTimeStringBuilder.Length < 18) - { - return false; - } - - int timeZoneStartIndex; - for (timeZoneStartIndex = dateTimeStringBuilder.Length - 1; dateTimeStringBuilder[timeZoneStartIndex] != ' '; timeZoneStartIndex--) ; - timeZoneStartIndex++; - - string timeZoneSuffix = dateTimeStringBuilder.ToString().Substring(timeZoneStartIndex); - dateTimeStringBuilder.Remove(timeZoneStartIndex, dateTimeStringBuilder.Length - timeZoneStartIndex); - bool isUtc; - dateTimeStringBuilder.Append(NormalizeTimeZone(timeZoneSuffix, out isUtc)); - string wellFormattedString = dateTimeStringBuilder.ToString(); - - DateTimeOffset theTime; - string[] parseFormat = - { - "ddd, dd MMMM yyyy HH:mm:ss zzz", - "dd MMMM yyyy HH:mm:ss zzz", - "ddd, dd MMM yyyy HH:mm:ss zzz", - "dd MMM yyyy HH:mm:ss zzz", - - "ddd, dd MMMM yyyy HH:mm zzz", - "dd MMMM yyyy HH:mm zzz", - "ddd, dd MMM yyyy HH:mm zzz", - "dd MMM yyyy HH:mm zzz" - }; - - if (DateTimeOffset.TryParseExact(wellFormattedString, parseFormat, - CultureInfo.InvariantCulture.DateTimeFormat, - (isUtc ? DateTimeStyles.AdjustToUniversal : DateTimeStyles.None), out theTime)) - { - dto = theTime; - return true; - } - - return false; - } - - // Custom parsers - public static DateTimeOffset DefaultDateParser(string dateTimeString, string localName, string ns) - { - bool parsed = false; - DateTimeOffset dto; - parsed = DateTimeOffset.TryParse(dateTimeString, out dto); - if (parsed) - return dto; - - - //original parser here - parsed = OriginalDateParser(dateTimeString, out dto); - if (parsed) - return dto; - - //Impossible to parse - using a default date; - return new DateTimeOffset(); + writer.WriteStartElement(elementTag, Rss20Constants.Rss20Namespace); + WriteAttributeExtensions(writer, person, this.Version); + writer.WriteString(person.Email); + writer.WriteEndElement(); } } @@ -1643,17 +1322,4 @@ namespace System.ServiceModel.Syndication return new TSyndicationFeed(); } } - - - internal class ItemParseOptions - { - public bool readItemsAtLeastOnce; - public bool areAllItemsRead; - - public ItemParseOptions(bool readItemsAtLeastOnce, bool areAllItemsRead) - { - this.readItemsAtLeastOnce = readItemsAtLeastOnce; - this.areAllItemsRead = areAllItemsRead; - } - } } diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Rss20ItemFormatter.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Rss20ItemFormatter.cs index a432f7a496..939ebe4866 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Rss20ItemFormatter.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Rss20ItemFormatter.cs @@ -2,19 +2,21 @@ // 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.Globalization; +using System.Text; +using System.Xml; +using System.Xml.Serialization; +using System.Diagnostics.CodeAnalysis; +using System.Xml.Schema; +using System.Runtime.CompilerServices; + namespace System.ServiceModel.Syndication { - using System; - using System.Diagnostics.CodeAnalysis; - using System.Runtime.CompilerServices; - using System.Threading.Tasks; - using System.Xml; - using System.Xml.Schema; - using System.Xml.Serialization; - - [XmlRoot(ElementName = Rss20Constants.ItemTag, Namespace = Rss20Constants.Rss20Namespace)] - public class Rss20ItemFormatter : SyndicationItemFormatter + public class Rss20ItemFormatter : SyndicationItemFormatter, IXmlSerializable { private Rss20FeedFormatter _feedSerializer; private Type _itemType; @@ -32,11 +34,12 @@ namespace System.ServiceModel.Syndication { if (itemTypeToCreate == null) { - throw new ArgumentNullException(nameof(itemTypeToCreate)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("itemTypeToCreate"); } if (!typeof(SyndicationItem).IsAssignableFrom(itemTypeToCreate)) { - throw new ArgumentException(string.Format(SR.InvalidObjectTypePassed, nameof(itemTypeToCreate), nameof(SyndicationItem))); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("itemTypeToCreate", + SR.Format(SR.InvalidObjectTypePassed, "itemTypeToCreate", "SyndicationItem")); } _feedSerializer = new Rss20FeedFormatter(); _feedSerializer.PreserveAttributeExtensions = _preserveAttributeExtensions = true; @@ -108,44 +111,63 @@ namespace System.ServiceModel.Syndication { if (reader == null) { - throw new ArgumentNullException(nameof(reader)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); } - return reader.IsStartElement(Rss20Constants.ItemTag, Rss20Constants.Rss20Namespace); } + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "The IXmlSerializable implementation is only for exposing under WCF DataContractSerializer. The funcionality is exposed to derived class through the ReadFrom\\WriteTo methods")] + XmlSchema IXmlSerializable.GetSchema() + { + return null; + } - private async Task WriteXml(XmlWriter writer) + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "The IXmlSerializable implementation is only for exposing under WCF DataContractSerializer. The funcionality is exposed to derived class through the ReadFrom\\WriteTo methods")] + void IXmlSerializable.ReadXml(XmlReader reader) + { + if (reader == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); + } + SyndicationFeedFormatter.TraceItemReadBegin(); + ReadItem(reader); + SyndicationFeedFormatter.TraceItemReadEnd(); + } + + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "The IXmlSerializable implementation is only for exposing under WCF DataContractSerializer. The funcionality is exposed to derived class through the ReadFrom\\WriteTo methods")] + void IXmlSerializable.WriteXml(XmlWriter writer) { if (writer == null) { - throw new ArgumentNullException(nameof(writer)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); } - await WriteItem(writer); + SyndicationFeedFormatter.TraceItemWriteBegin(); + WriteItem(writer); + SyndicationFeedFormatter.TraceItemWriteEnd(); } - public override Task ReadFromAsync(XmlReader reader) + public override void ReadFrom(XmlReader reader) { + SyndicationFeedFormatter.TraceItemReadBegin(); if (!CanRead(reader)) { - throw new XmlException(string.Format(SR.UnknownItemXml, reader.LocalName, reader.NamespaceURI)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.UnknownItemXml, reader.LocalName, reader.NamespaceURI))); } - - return ReadItemAsync(XmlReaderWrapper.CreateFromReader(reader)); + ReadItem(reader); + SyndicationFeedFormatter.TraceItemReadEnd(); } - public override async Task WriteToAsync(XmlWriter writer) + public override void WriteTo(XmlWriter writer) { if (writer == null) { - throw new ArgumentNullException(nameof(writer)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); } - - writer = XmlWriterWrapper.CreateFromWriter(writer); - - await writer.WriteStartElementAsync(Rss20Constants.ItemTag, Rss20Constants.Rss20Namespace); - await WriteItem(writer); - await writer.WriteEndElementAsync(); + SyndicationFeedFormatter.TraceItemWriteBegin(); + writer.WriteStartElement(Rss20Constants.ItemTag, Rss20Constants.Rss20Namespace); + WriteItem(writer); + writer.WriteEndElement(); + SyndicationFeedFormatter.TraceItemWriteEnd(); } protected override SyndicationItem CreateItemInstance() @@ -153,25 +175,25 @@ namespace System.ServiceModel.Syndication return SyndicationItemFormatter.CreateItemInstance(_itemType); } - private Task ReadItemAsync(XmlReaderWrapper reader) + private void ReadItem(XmlReader reader) { SetItem(CreateItemInstance()); - return _feedSerializer.ReadItemFromAsync(XmlReaderWrapper.CreateFromReader(XmlDictionaryReader.CreateDictionaryReader(reader)), this.Item); + _feedSerializer.ReadItemFrom(XmlDictionaryReader.CreateDictionaryReader(reader), this.Item); } - private Task WriteItem(XmlWriter writer) + private void WriteItem(XmlWriter writer) { if (this.Item == null) { - throw new InvalidOperationException(SR.ItemFormatterDoesNotHaveItem); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.ItemFormatterDoesNotHaveItem))); } XmlDictionaryWriter w = XmlDictionaryWriter.CreateDictionaryWriter(writer); - return _feedSerializer.WriteItemContentsAsync(w, this.Item); + _feedSerializer.WriteItemContents(w, this.Item); } } [XmlRoot(ElementName = Rss20Constants.ItemTag, Namespace = Rss20Constants.Rss20Namespace)] - public class Rss20ItemFormatter : Rss20ItemFormatter + public class Rss20ItemFormatter : Rss20ItemFormatter, IXmlSerializable where TSyndicationItem : SyndicationItem, new() { public Rss20ItemFormatter() diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/ServiceDocument.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/ServiceDocument.cs index 140800a71b..63107b72b9 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/ServiceDocument.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/ServiceDocument.cs @@ -2,15 +2,15 @@ // 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.Collections.ObjectModel; +using System.Runtime.Serialization; +using System.Xml.Serialization; +using System.Collections.Generic; +using System.Xml; +using System.Runtime.CompilerServices; + namespace System.ServiceModel.Syndication { - using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Runtime.CompilerServices; - using System.Threading.Tasks; - using System.Xml; - public class ServiceDocument : IExtensibleSyndicationObject { private Uri _baseUri; @@ -68,11 +68,16 @@ namespace System.ServiceModel.Syndication } } - public static async Task LoadAsync(XmlReader reader) + public static ServiceDocument Load(XmlReader reader) + { + return Load(reader); + } + + public static TServiceDocument Load(XmlReader reader) where TServiceDocument : ServiceDocument, new() { AtomPub10ServiceDocumentFormatter formatter = new AtomPub10ServiceDocumentFormatter(); - await formatter.ReadFromAsync(reader); + formatter.ReadFrom(reader); return (TServiceDocument)(object)formatter.Document; } @@ -81,9 +86,9 @@ namespace System.ServiceModel.Syndication return new AtomPub10ServiceDocumentFormatter(this); } - public Task Save(XmlWriter writer) + public void Save(XmlWriter writer) { - return new AtomPub10ServiceDocumentFormatter(this).WriteToAsync(writer); + new AtomPub10ServiceDocumentFormatter(this).WriteTo(writer); } protected internal virtual Workspace CreateWorkspace() @@ -101,17 +106,17 @@ namespace System.ServiceModel.Syndication return false; } - protected internal virtual Task WriteAttributeExtensionsAsync(XmlWriter writer, string version) + protected internal virtual void WriteAttributeExtensions(XmlWriter writer, string version) { - return _extensions.WriteAttributeExtensionsAsync(writer); + _extensions.WriteAttributeExtensions(writer); } - protected internal virtual Task WriteElementExtensionsAsync(XmlWriter writer, string version) + protected internal virtual void WriteElementExtensions(XmlWriter writer, string version) { - return _extensions.WriteElementExtensionsAsync(writer); + _extensions.WriteElementExtensions(writer); } - internal void LoadElementExtensions(XmlReaderWrapper readerOverUnparsedExtensions, int maxExtensionSize) + internal void LoadElementExtensions(XmlReader readerOverUnparsedExtensions, int maxExtensionSize) { _extensions.LoadElementExtensions(readerOverUnparsedExtensions, maxExtensionSize); } diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/ServiceDocumentFormatter.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/ServiceDocumentFormatter.cs index 1dcfd04bc4..23ea181cfd 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/ServiceDocumentFormatter.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/ServiceDocumentFormatter.cs @@ -2,14 +2,15 @@ // 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.Collections.ObjectModel; +using System.Runtime.Serialization; +using System.Xml.Serialization; +using System.Collections.Generic; +using System.Xml; +using System.Runtime.CompilerServices; + namespace System.ServiceModel.Syndication { - using System; - using System.Runtime.CompilerServices; - using System.Runtime.Serialization; - using System.Threading.Tasks; - using System.Xml; - [DataContract] public abstract class ServiceDocumentFormatter { @@ -22,7 +23,7 @@ namespace System.ServiceModel.Syndication { if (documentToWrite == null) { - throw new ArgumentNullException(nameof(documentToWrite)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("documentToWrite"); } _document = documentToWrite; } @@ -35,15 +36,15 @@ namespace System.ServiceModel.Syndication public abstract string Version { get; } - public abstract Task CanReadAsync(XmlReader reader); - public abstract Task ReadFromAsync(XmlReader reader); - public abstract Task WriteToAsync(XmlWriter writer); + public abstract bool CanRead(XmlReader reader); + public abstract void ReadFrom(XmlReader reader); + public abstract void WriteTo(XmlWriter writer); internal static void LoadElementExtensions(XmlBuffer buffer, XmlDictionaryWriter writer, CategoriesDocument categories) { if (categories == null) { - throw new ArgumentNullException(nameof(categories)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("categories"); } Atom10FeedFormatter.CloseBuffer(buffer, writer); categories.LoadElementExtensions(buffer); @@ -53,7 +54,7 @@ namespace System.ServiceModel.Syndication { if (collection == null) { - throw new ArgumentNullException(nameof(collection)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("collection"); } Atom10FeedFormatter.CloseBuffer(buffer, writer); collection.LoadElementExtensions(buffer); @@ -63,7 +64,7 @@ namespace System.ServiceModel.Syndication { if (workspace == null) { - throw new ArgumentNullException(nameof(workspace)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("workspace"); } Atom10FeedFormatter.CloseBuffer(buffer, writer); workspace.LoadElementExtensions(buffer); @@ -73,7 +74,7 @@ namespace System.ServiceModel.Syndication { if (document == null) { - throw new ArgumentNullException(nameof(document)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("document"); } Atom10FeedFormatter.CloseBuffer(buffer, writer); document.LoadElementExtensions(buffer); @@ -83,7 +84,7 @@ namespace System.ServiceModel.Syndication { if (inlineCategories == null) { - throw new ArgumentNullException(nameof(inlineCategories)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("inlineCategories"); } return inlineCategories.CreateCategory(); } @@ -92,7 +93,7 @@ namespace System.ServiceModel.Syndication { if (workspace == null) { - throw new ArgumentNullException(nameof(workspace)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("workspace"); } return workspace.CreateResourceCollection(); } @@ -111,7 +112,7 @@ namespace System.ServiceModel.Syndication { if (document == null) { - throw new ArgumentNullException(nameof(document)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("document"); } return document.CreateWorkspace(); } @@ -120,49 +121,44 @@ namespace System.ServiceModel.Syndication { if (categories == null) { - throw new ArgumentNullException(nameof(categories)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("categories"); } - - categories.LoadElementExtensions(XmlReaderWrapper.CreateFromReader(reader), maxExtensionSize); + categories.LoadElementExtensions(reader, maxExtensionSize); } protected static void LoadElementExtensions(XmlReader reader, ResourceCollectionInfo collection, int maxExtensionSize) { if (collection == null) { - throw new ArgumentNullException(nameof(collection)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("collection"); } - - collection.LoadElementExtensions(XmlReaderWrapper.CreateFromReader(reader), maxExtensionSize); + collection.LoadElementExtensions(reader, maxExtensionSize); } protected static void LoadElementExtensions(XmlReader reader, Workspace workspace, int maxExtensionSize) { if (workspace == null) { - throw new ArgumentNullException(nameof(workspace)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("workspace"); } - - workspace.LoadElementExtensions(XmlReaderWrapper.CreateFromReader(reader), maxExtensionSize); + workspace.LoadElementExtensions(reader, maxExtensionSize); } protected static void LoadElementExtensions(XmlReader reader, ServiceDocument document, int maxExtensionSize) { if (document == null) { - throw new ArgumentNullException(nameof(document)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("document"); } - - document.LoadElementExtensions(XmlReaderWrapper.CreateFromReader(reader), maxExtensionSize); + document.LoadElementExtensions(reader, maxExtensionSize); } protected static bool TryParseAttribute(string name, string ns, string value, ServiceDocument document, string version) { if (document == null) { - throw new ArgumentNullException(nameof(document)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("document"); } - return document.TryParseAttribute(name, ns, value, version); } @@ -170,9 +166,8 @@ namespace System.ServiceModel.Syndication { if (collection == null) { - throw new ArgumentNullException(nameof(collection)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("collection"); } - return collection.TryParseAttribute(name, ns, value, version); } @@ -180,9 +175,8 @@ namespace System.ServiceModel.Syndication { if (categories == null) { - throw new ArgumentNullException(nameof(categories)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("categories"); } - return categories.TryParseAttribute(name, ns, value, version); } @@ -190,9 +184,8 @@ namespace System.ServiceModel.Syndication { if (workspace == null) { - throw new ArgumentNullException(nameof(workspace)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("workspace"); } - return workspace.TryParseAttribute(name, ns, value, version); } @@ -200,120 +193,108 @@ namespace System.ServiceModel.Syndication { if (collection == null) { - throw new ArgumentNullException(nameof(collection)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("collection"); } - - return collection.TryParseElement(XmlReaderWrapper.CreateFromReader(reader), version); + return collection.TryParseElement(reader, version); } protected static bool TryParseElement(XmlReader reader, ServiceDocument document, string version) { if (document == null) { - throw new ArgumentNullException(nameof(document)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("document"); } - - return document.TryParseElement(XmlReaderWrapper.CreateFromReader(reader), version); + return document.TryParseElement(reader, version); } protected static bool TryParseElement(XmlReader reader, Workspace workspace, string version) { if (workspace == null) { - throw new ArgumentNullException(nameof(workspace)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("workspace"); } - - return workspace.TryParseElement(XmlReaderWrapper.CreateFromReader(reader), version); + return workspace.TryParseElement(reader, version); } protected static bool TryParseElement(XmlReader reader, CategoriesDocument categories, string version) { if (categories == null) { - throw new ArgumentNullException(nameof(categories)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("categories"); } - - return categories.TryParseElement(XmlReaderWrapper.CreateFromReader(reader), version); + return categories.TryParseElement(reader, version); } protected static void WriteAttributeExtensions(XmlWriter writer, ServiceDocument document, string version) { if (document == null) { - throw new ArgumentNullException(nameof(document)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("document"); } - - document.WriteAttributeExtensionsAsync(writer, version); + document.WriteAttributeExtensions(writer, version); } protected static void WriteAttributeExtensions(XmlWriter writer, Workspace workspace, string version) { if (workspace == null) { - throw new ArgumentNullException(nameof(workspace)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("workspace"); } - workspace.WriteAttributeExtensions(writer, version); } - protected static Task WriteAttributeExtensionsAsync(XmlWriter writer, ResourceCollectionInfo collection, string version) + protected static void WriteAttributeExtensions(XmlWriter writer, ResourceCollectionInfo collection, string version) { if (collection == null) { - throw new ArgumentNullException(nameof(collection)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("collection"); } - - return collection.WriteAttributeExtensionsAsync(writer, version); + collection.WriteAttributeExtensions(writer, version); } - protected static Task WriteAttributeExtensionsAsync(XmlWriter writer, CategoriesDocument categories, string version) + protected static void WriteAttributeExtensions(XmlWriter writer, CategoriesDocument categories, string version) { if (categories == null) { - throw new ArgumentNullException(nameof(categories)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("categories"); } - - return categories.WriteAttributeExtensionsAsync(writer, version); + categories.WriteAttributeExtensions(writer, version); } - protected static Task WriteElementExtensionsAsync(XmlWriter writer, ServiceDocument document, string version) + protected static void WriteElementExtensions(XmlWriter writer, ServiceDocument document, string version) { if (document == null) { - throw new ArgumentNullException(nameof(document)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("document"); } - - return document.WriteElementExtensionsAsync(writer, version); + document.WriteElementExtensions(writer, version); } - protected static Task WriteElementExtensionsAsync(XmlWriter writer, Workspace workspace, string version) + protected static void WriteElementExtensions(XmlWriter writer, Workspace workspace, string version) { if (workspace == null) { - throw new ArgumentNullException(nameof(workspace)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("workspace"); } - - return workspace.WriteElementExtensionsAsync(writer, version); + workspace.WriteElementExtensions(writer, version); } - protected static Task WriteElementExtensionsAsync(XmlWriter writer, ResourceCollectionInfo collection, string version) + protected static void WriteElementExtensions(XmlWriter writer, ResourceCollectionInfo collection, string version) { if (collection == null) { - throw new ArgumentNullException(nameof(collection)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("collection"); } - - return collection.WriteElementExtensionsAsync(writer, version); + collection.WriteElementExtensions(writer, version); } - protected static Task WriteElementExtensionsAsync(XmlWriter writer, CategoriesDocument categories, string version) + protected static void WriteElementExtensions(XmlWriter writer, CategoriesDocument categories, string version) { if (categories == null) { - throw new ArgumentNullException(nameof(categories)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("categories"); } - - return categories.WriteElementExtensionsAsync(writer, version); + categories.WriteElementExtensions(writer, version); } protected virtual ServiceDocument CreateDocumentInstance() diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationCategory.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationCategory.cs index 77660de24f..6d9f89637d 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationCategory.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationCategory.cs @@ -2,14 +2,16 @@ // 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.Xml; +using System.Collections.ObjectModel; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization; +using System.Xml.Serialization; +using System.Runtime.CompilerServices; + namespace System.ServiceModel.Syndication { - using System; - using System.Collections.Generic; - using System.Runtime.CompilerServices; - using System.Threading.Tasks; - using System.Xml; - // NOTE: This class implements Clone so if you add any members, please update the copy ctor public class SyndicationCategory : IExtensibleSyndicationObject { @@ -39,7 +41,7 @@ namespace System.ServiceModel.Syndication { if (source == null) { - throw new ArgumentNullException(nameof(source)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("source"); } _label = source._label; _name = source._name; @@ -90,14 +92,14 @@ namespace System.ServiceModel.Syndication return false; } - protected internal virtual Task WriteAttributeExtensionsAsync(XmlWriter writer, string version) + protected internal virtual void WriteAttributeExtensions(XmlWriter writer, string version) { - return _extensions.WriteAttributeExtensionsAsync(writer); + _extensions.WriteAttributeExtensions(writer); } - protected internal virtual Task WriteElementExtensionsAsync(XmlWriter writer, string version) + protected internal virtual void WriteElementExtensions(XmlWriter writer, string version) { - return _extensions.WriteElementExtensionsAsync(writer); + _extensions.WriteElementExtensions(writer); } internal void LoadElementExtensions(XmlReader readerOverUnparsedExtensions, int maxExtensionSize) diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationContent.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationContent.cs index 7465df37a5..79cde9cf8a 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationContent.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationContent.cs @@ -2,16 +2,17 @@ // 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.Collections.Generic; +using System.Text; +using System.Xml; +using System.Xml.Serialization; +using System.Runtime.Serialization; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + namespace System.ServiceModel.Syndication { - using System; - using System.Collections.Generic; - using System.Runtime.CompilerServices; - using System.Runtime.Serialization; - using System.Threading.Tasks; - using System.Xml; - using System.Xml.Serialization; - public abstract class SyndicationContent { private Dictionary _attributeExtensions; @@ -72,9 +73,9 @@ namespace System.ServiceModel.Syndication return new XmlSyndicationContent(Atom10Constants.XmlMediaType, dataContractObject, dataContractSerializer); } - public static XmlSyndicationContent CreateXmlContent(XmlReader XmlReaderWrapper) + public static XmlSyndicationContent CreateXmlContent(XmlReader xmlReader) { - return new XmlSyndicationContent(XmlReaderWrapper); + return new XmlSyndicationContent(xmlReader); } public static XmlSyndicationContent CreateXmlContent(object xmlSerializerObject, XmlSerializer serializer) @@ -84,21 +85,18 @@ namespace System.ServiceModel.Syndication public abstract SyndicationContent Clone(); - public async Task WriteToAsync(XmlWriter writer, string outerElementName, string outerElementNamespace) + public void WriteTo(XmlWriter writer, string outerElementName, string outerElementNamespace) { if (writer == null) { - throw new ArgumentNullException(nameof(writer)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); } if (string.IsNullOrEmpty(outerElementName)) { - throw new ArgumentException(SR.OuterElementNameNotSpecified); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.Format(SR.OuterElementNameNotSpecified)); } - - writer = XmlWriterWrapper.CreateFromWriter(writer); - - await writer.WriteStartElementAsync(outerElementName, outerElementNamespace); - await writer.WriteAttributeStringAsync(Atom10Constants.TypeTag, string.Empty, this.Type); + writer.WriteStartElement(outerElementName, outerElementNamespace); + writer.WriteAttributeString(Atom10Constants.TypeTag, string.Empty, this.Type); if (_attributeExtensions != null) { foreach (XmlQualifiedName key in _attributeExtensions.Keys) @@ -110,19 +108,19 @@ namespace System.ServiceModel.Syndication string attrValue; if (_attributeExtensions.TryGetValue(key, out attrValue)) { - await writer.WriteAttributeStringAsync(key.Name, key.Namespace, attrValue); + writer.WriteAttributeString(key.Name, key.Namespace, attrValue); } } } WriteContentsTo(writer); - await writer.WriteEndElementAsync(); + writer.WriteEndElement(); } internal void CopyAttributeExtensions(SyndicationContent source) { if (source == null) { - throw new ArgumentNullException(nameof(source)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("source"); } if (source._attributeExtensions != null) { diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationElementExtension.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationElementExtension.cs index e62e626424..b74d8576dd 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationElementExtension.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationElementExtension.cs @@ -2,17 +2,16 @@ // 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.IO; +using System.Runtime; +using System.Runtime.Serialization; +using System.Xml; +using System.Xml.Serialization; +using System.Runtime.CompilerServices; +using System.Diagnostics; + namespace System.ServiceModel.Syndication { - using System; - using System.Diagnostics; - using System.IO; - using System.Runtime.CompilerServices; - using System.Runtime.Serialization; - using System.Threading.Tasks; - using System.Xml; - using System.Xml.Serialization; - public class SyndicationElementExtension { private XmlBuffer _buffer; @@ -23,20 +22,20 @@ namespace System.ServiceModel.Syndication private string _outerName; private string _outerNamespace; - public SyndicationElementExtension(XmlReader reader) + public SyndicationElementExtension(XmlReader xmlReader) { - if (reader == null) + if (xmlReader == null) { - throw new ArgumentNullException(nameof(reader)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("xmlReader"); } - SyndicationFeedFormatter.MoveToStartElement(reader); - _outerName = reader.LocalName; - _outerNamespace = reader.NamespaceURI; + SyndicationFeedFormatter.MoveToStartElement(xmlReader); + _outerName = xmlReader.LocalName; + _outerNamespace = xmlReader.NamespaceURI; _buffer = new XmlBuffer(int.MaxValue); using (XmlDictionaryWriter writer = _buffer.OpenSection(XmlDictionaryReaderQuotas.Max)) { writer.WriteStartElement(Rss20Constants.ExtensionWrapperTag); - writer.WriteNode(reader, false); + writer.WriteNode(xmlReader, false); writer.WriteEndElement(); } _buffer.CloseSection(); @@ -63,11 +62,11 @@ namespace System.ServiceModel.Syndication { if (dataContractExtension == null) { - throw new ArgumentNullException(nameof(dataContractExtension)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("dataContractExtension"); } if (outerName == string.Empty) { - throw new ArgumentNullException(SR.OuterNameOfElementExtensionEmpty); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.Format(SR.OuterNameOfElementExtensionEmpty)); } if (dataContractSerializer == null) { @@ -83,7 +82,7 @@ namespace System.ServiceModel.Syndication { if (xmlSerializerExtension == null) { - throw new ArgumentNullException(nameof(xmlSerializerExtension)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("xmlSerializerExtension"); } if (serializer == null) { @@ -125,49 +124,47 @@ namespace System.ServiceModel.Syndication } } - public Task GetObject() + public TExtension GetObject() { return GetObject(new DataContractSerializer(typeof(TExtension))); } - public async Task GetObject(XmlObjectSerializer serializer) + public TExtension GetObject(XmlObjectSerializer serializer) { if (serializer == null) { - throw new ArgumentNullException(nameof(serializer)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer"); } if (_extensionData != null && typeof(TExtension).IsAssignableFrom(_extensionData.GetType())) { return (TExtension)_extensionData; } - - using (XmlReader reader = await GetReaderAsync()) + using (XmlReader reader = GetReader()) { return (TExtension)serializer.ReadObject(reader, false); } } - public async Task GetObject(XmlSerializer serializer) + public TExtension GetObject(XmlSerializer serializer) { if (serializer == null) { - throw new ArgumentNullException(nameof(serializer)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer"); } if (_extensionData != null && typeof(TExtension).IsAssignableFrom(_extensionData.GetType())) { return (TExtension)_extensionData; } - - using (XmlReader reader = await GetReaderAsync()) + using (XmlReader reader = GetReader()) { return (TExtension)serializer.Deserialize(reader); } } - public async Task GetReaderAsync() + public XmlReader GetReader() { - await this.EnsureBuffer(); - XmlReaderWrapper reader = XmlReaderWrapper.CreateFromReader(_buffer.GetReader(0)); + this.EnsureBuffer(); + XmlReader reader = _buffer.GetReader(0); int index = 0; reader.ReadStartElement(Rss20Constants.ExtensionWrapperTag); while (reader.IsStartElement()) @@ -177,39 +174,31 @@ namespace System.ServiceModel.Syndication break; } ++index; - - await reader.SkipAsync(); + reader.Skip(); } - return reader; } - //public Task GetReader() - //{ - // return GetReaderAsync(); - //} - - public async Task WriteToAsync(XmlWriter writer) + public void WriteTo(XmlWriter writer) { if (writer == null) { - throw new ArgumentNullException(nameof(writer)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); } if (_extensionDataWriter != null) { - _extensionDataWriter.WriteToAsync(writer); + _extensionDataWriter.WriteTo(writer); } else { - writer = XmlWriterWrapper.CreateFromWriter(writer); - using (XmlReader reader = await GetReaderAsync()) + using (XmlReader reader = GetReader()) { - await writer.WriteNodeAsync(reader, false); + writer.WriteNode(reader, false); } } } - private async Task EnsureBuffer() + private void EnsureBuffer() { if (_buffer == null) { @@ -217,7 +206,7 @@ namespace System.ServiceModel.Syndication using (XmlDictionaryWriter writer = _buffer.OpenSection(XmlDictionaryReaderQuotas.Max)) { writer.WriteStartElement(Rss20Constants.ExtensionWrapperTag); - await this.WriteToAsync(writer); + this.WriteTo(writer); writer.WriteEndElement(); } _buffer.CloseSection(); @@ -244,7 +233,6 @@ namespace System.ServiceModel.Syndication public ExtensionDataWriter(object extensionData, XmlObjectSerializer dataContractSerializer, string outerName, string outerNamespace) { Debug.Assert(extensionData != null && dataContractSerializer != null, "null check"); - _dataContractSerializer = dataContractSerializer; _extensionData = extensionData; _outerName = outerName; @@ -258,7 +246,7 @@ namespace System.ServiceModel.Syndication _extensionData = extensionData; } - public void WriteToAsync(XmlWriter writer) + public void WriteTo(XmlWriter writer) { if (_xmlSerializer != null) { @@ -268,12 +256,11 @@ namespace System.ServiceModel.Syndication else { Debug.Assert(_xmlSerializer == null, "Xml serializer cannot be configured"); - writer = XmlWriterWrapper.CreateFromWriter(writer); if (_outerName != null) { - writer.WriteStartElementAsync(_outerName, _outerNamespace); + writer.WriteStartElement(_outerName, _outerNamespace); _dataContractSerializer.WriteObjectContent(writer, _extensionData); - writer.WriteEndElementAsync(); + writer.WriteEndElement(); } else { @@ -330,11 +317,9 @@ namespace System.ServiceModel.Syndication { using (XmlWriter writer = XmlWriter.Create(stream)) { - this.WriteToAsync(writer); + this.WriteTo(writer); } - stream.Seek(0, SeekOrigin.Begin); - using (XmlReader reader = XmlReader.Create(stream)) { SyndicationFeedFormatter.MoveToStartElement(reader); diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationElementExtensionCollection.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationElementExtensionCollection.cs index cb09d65b1f..aacb074bc6 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationElementExtensionCollection.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationElementExtensionCollection.cs @@ -2,17 +2,16 @@ // 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.Collections.ObjectModel; +using System.Runtime; +using System.Runtime.Serialization; +using System.Xml; +using System.Xml.Serialization; +using System.Runtime.CompilerServices; +using System.Diagnostics; + namespace System.ServiceModel.Syndication { - using System; - using System.Collections.ObjectModel; - using System.Diagnostics; - using System.Runtime.CompilerServices; - using System.Runtime.Serialization; - using System.Threading.Tasks; - using System.Xml; - using System.Xml.Serialization; - // sealed because the ctor results in a call to the virtual InsertItem method public sealed class SyndicationElementExtensionCollection : Collection { @@ -72,7 +71,7 @@ namespace System.ServiceModel.Syndication { if (dataContractExtension == null) { - throw new ArgumentNullException(nameof(dataContractExtension)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("dataContractExtension"); } if (dataContractSerializer == null) { @@ -85,7 +84,7 @@ namespace System.ServiceModel.Syndication { if (xmlSerializerExtension == null) { - throw new ArgumentNullException(nameof(xmlSerializerExtension)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("xmlSerializerExtension"); } if (serializer == null) { @@ -94,47 +93,47 @@ namespace System.ServiceModel.Syndication base.Add(new SyndicationElementExtension(xmlSerializerExtension, serializer)); } - public void Add(XmlReader reader) + public void Add(XmlReader xmlReader) { - if (reader == null) + if (xmlReader == null) { - throw new ArgumentNullException(nameof(reader)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("xmlReader"); } - base.Add(new SyndicationElementExtension(reader)); + base.Add(new SyndicationElementExtension(xmlReader)); } - public async Task GetReaderAtElementExtensions() + public XmlReader GetReaderAtElementExtensions() { - XmlBuffer extensionsBuffer = await GetOrCreateBufferOverExtensions(); + XmlBuffer extensionsBuffer = GetOrCreateBufferOverExtensions(); XmlReader reader = extensionsBuffer.GetReader(0); reader.ReadStartElement(); return reader; } - public Task> ReadElementExtensions(string extensionName, string extensionNamespace) + public Collection ReadElementExtensions(string extensionName, string extensionNamespace) { return ReadElementExtensions(extensionName, extensionNamespace, new DataContractSerializer(typeof(TExtension))); } - public Task> ReadElementExtensions(string extensionName, string extensionNamespace, XmlObjectSerializer serializer) + public Collection ReadElementExtensions(string extensionName, string extensionNamespace, XmlObjectSerializer serializer) { if (serializer == null) { - throw new ArgumentNullException(nameof(serializer)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer"); } return ReadExtensions(extensionName, extensionNamespace, serializer, null); } - public Task> ReadElementExtensions(string extensionName, string extensionNamespace, XmlSerializer serializer) + public Collection ReadElementExtensions(string extensionName, string extensionNamespace, XmlSerializer serializer) { if (serializer == null) { - throw new ArgumentNullException(nameof(serializer)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer"); } return ReadExtensions(extensionName, extensionNamespace, null, serializer); } - internal async Task WriteToAsync(XmlWriter writer) + internal void WriteTo(XmlWriter writer) { if (_buffer != null) { @@ -143,7 +142,7 @@ namespace System.ServiceModel.Syndication reader.ReadStartElement(); while (reader.IsStartElement()) { - await writer.WriteNodeAsync(reader, false); + writer.WriteNode(reader, false); } } } @@ -151,7 +150,7 @@ namespace System.ServiceModel.Syndication { for (int i = 0; i < this.Items.Count; ++i) { - await this.Items[i].WriteToAsync(writer); + this.Items[i].WriteTo(writer); } } } @@ -170,7 +169,7 @@ namespace System.ServiceModel.Syndication { if (item == null) { - throw new ArgumentNullException(nameof(item)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); } base.InsertItem(index, item); // clear the cached buffer if the operation is happening outside the constructor @@ -194,7 +193,7 @@ namespace System.ServiceModel.Syndication { if (item == null) { - throw new ArgumentNullException(nameof(item)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); } base.SetItem(index, item); // clear the cached buffer if the operation is happening outside the constructor @@ -204,7 +203,7 @@ namespace System.ServiceModel.Syndication } } - private async Task GetOrCreateBufferOverExtensions() + private XmlBuffer GetOrCreateBufferOverExtensions() { if (_buffer != null) { @@ -216,7 +215,7 @@ namespace System.ServiceModel.Syndication writer.WriteStartElement(Rss20Constants.ExtensionWrapperTag); for (int i = 0; i < this.Count; ++i) { - await this[i].WriteToAsync(writer); + this[i].WriteTo(writer); } writer.WriteEndElement(); } @@ -241,11 +240,11 @@ namespace System.ServiceModel.Syndication } } - private async Task> ReadExtensions(string extensionName, string extensionNamespace, XmlObjectSerializer dcSerializer, XmlSerializer xmlSerializer) + private Collection ReadExtensions(string extensionName, string extensionNamespace, XmlObjectSerializer dcSerializer, XmlSerializer xmlSerializer) { if (string.IsNullOrEmpty(extensionName)) { - throw new ArgumentNullException(SR.ExtensionNameNotSpecified); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.Format(SR.ExtensionNameNotSpecified)); } Debug.Assert((dcSerializer == null) != (xmlSerializer == null), "exactly one serializer should be supplied"); // normalize the null and empty namespace @@ -253,7 +252,6 @@ namespace System.ServiceModel.Syndication { extensionNamespace = string.Empty; } - Collection results = new Collection(); for (int i = 0; i < this.Count; ++i) { @@ -261,14 +259,13 @@ namespace System.ServiceModel.Syndication { continue; } - if (dcSerializer != null) { - results.Add(await this[i].GetObject(dcSerializer)); + results.Add(this[i].GetObject(dcSerializer)); } else { - results.Add(await this[i].GetObject(xmlSerializer)); + results.Add(this[i].GetObject(xmlSerializer)); } } return results; diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationElementExtensionKindHelper.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationElementExtensionKindHelper.cs index f1ebc22e56..2deb980cdf 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationElementExtensionKindHelper.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationElementExtensionKindHelper.cs @@ -2,6 +2,9 @@ // 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.Globalization; +using System.Collections.ObjectModel; + namespace System.ServiceModel.Syndication { internal static class SyndicationElementExtensionKindHelper diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationFeed.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationFeed.cs index e6530c817c..621c98994f 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationFeed.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationFeed.cs @@ -2,18 +2,19 @@ // 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; +using System.Xml; +using System.Runtime.Serialization; +using System.Globalization; +using System.Xml.Serialization; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + namespace System.ServiceModel.Syndication { - using System.ServiceModel; - using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Runtime.CompilerServices; - using System.Threading; - using System.Threading.Tasks; - using System.Xml; - - // NOTE: This class implements Clone so if you add any members, please update the copy ctor public class SyndicationFeed : IExtensibleSyndicationObject { @@ -26,99 +27,13 @@ namespace System.ServiceModel.Syndication private ExtensibleSyndicationObject _extensions = new ExtensibleSyndicationObject(); private string _generator; private string _id; - private Uri _imageUrl; - private TextSyndicationContent _imageTitle; - private Uri _imageLink; - private IEnumerable _items; private string _language; private DateTimeOffset _lastUpdatedTime; private Collection _links; private TextSyndicationContent _title; - // optional RSS tags - private SyndicationLink _documentation; - private int _timeToLive; - private Collection _skipHours; - private Collection _skipDays; - private SyndicationTextInput _textInput; - private Uri _iconImage; - - public Uri IconImage - { - get - { - return _iconImage; - } - set - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - _iconImage = value; - } - } - - public SyndicationTextInput TextInput - { - get - { - return _textInput; - } - set - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - _textInput = value; - } - } - - public SyndicationLink Documentation - { - get - { - return _documentation; - } - set - { - _documentation = value; - } - } - - public int TimeToLive - { - get - { - return _timeToLive; - } - set - { - _timeToLive = value; - } - } - - public Collection SkipHours - { - get - { - if (_skipHours == null) - _skipHours = new Collection(); - return _skipHours; - } - } - - public Collection SkipDays - { - get - { - if (_skipDays == null) - _skipDays = new Collection(); - return _skipDays; - } - } - - //====================================== - public SyndicationFeed() : this((IEnumerable)null) { @@ -167,7 +82,7 @@ namespace System.ServiceModel.Syndication { if (source == null) { - throw new ArgumentNullException(nameof(source)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("source"); } _authors = FeedUtils.ClonePersons(source._authors); _categories = FeedUtils.CloneCategories(source._categories); @@ -197,7 +112,7 @@ namespace System.ServiceModel.Syndication { if (cloneItems) { - throw new InvalidOperationException(SR.UnbufferedItemsCannotBeCloned); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.UnbufferedItemsCannotBeCloned))); } _items = source._items; } @@ -286,18 +201,6 @@ namespace System.ServiceModel.Syndication set { _imageUrl = value; } } - public TextSyndicationContent ImageTitle - { - get { return _imageTitle; } - set { _imageTitle = value; } - } - - public Uri ImageLink - { - get { return _imageLink; } - set { _imageLink = value; } - } - public IEnumerable Items { get @@ -312,7 +215,7 @@ namespace System.ServiceModel.Syndication { if (value == null) { - throw new ArgumentNullException(nameof(value)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); } _items = value; } @@ -324,10 +227,24 @@ namespace System.ServiceModel.Syndication set { _language = value; } } + internal Exception LastUpdatedTimeException { get; set; } + public DateTimeOffset LastUpdatedTime { - get { return _lastUpdatedTime; } - set { _lastUpdatedTime = value; } + get + { + if (LastUpdatedTimeException != null) + { + throw LastUpdatedTimeException; + } + + return _lastUpdatedTime; + } + set + { + LastUpdatedTimeException = null; + _lastUpdatedTime = value; + } } public Collection Links @@ -348,43 +265,6 @@ namespace System.ServiceModel.Syndication set { _title = value; } } - //// Custom Parsing - public static async Task LoadAsync(XmlReader reader, Rss20FeedFormatter formatter, CancellationToken ct) - { - return await LoadAsync(reader, formatter, new Atom10FeedFormatter(), ct); - } - - public static async Task LoadAsync(XmlReader reader, Atom10FeedFormatter formatter, CancellationToken ct) - { - return await LoadAsync(reader, new Rss20FeedFormatter(), formatter, ct); - } - - public static async Task LoadAsync(XmlReader reader, Rss20FeedFormatter Rssformatter, Atom10FeedFormatter Atomformatter, CancellationToken ct) - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - XmlReaderWrapper wrappedReader = XmlReaderWrapper.CreateFromReader(reader); - - Atom10FeedFormatter atomSerializer = Atomformatter; - if (atomSerializer.CanRead(wrappedReader)) - { - await atomSerializer.ReadFromAsync(wrappedReader, new CancellationToken()); - return atomSerializer.Feed; - } - Rss20FeedFormatter rssSerializer = Rssformatter; - if (rssSerializer.CanRead(wrappedReader)) - { - await rssSerializer.ReadFromAsync(wrappedReader, new CancellationToken()); - return rssSerializer.Feed; - } - throw new XmlException(string.Format(SR.UnknownFeedXml, wrappedReader.LocalName, wrappedReader.NamespaceURI)); - } - - //================================= - public static SyndicationFeed Load(XmlReader reader) { return Load(reader); @@ -393,33 +273,23 @@ namespace System.ServiceModel.Syndication public static TSyndicationFeed Load(XmlReader reader) where TSyndicationFeed : SyndicationFeed, new() { - CancellationToken ct = new CancellationToken(); - return LoadAsync(reader, ct).GetAwaiter().GetResult(); - } - - public static async Task LoadAsync(XmlReader reader, CancellationToken ct) - { - return await LoadAsync(reader, ct); - } - - public static async Task LoadAsync(XmlReader reader, CancellationToken ct) - where TSyndicationFeed : SyndicationFeed, new() - { + if (reader == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); + } Atom10FeedFormatter atomSerializer = new Atom10FeedFormatter(); if (atomSerializer.CanRead(reader)) { - await atomSerializer.ReadFromAsync(reader, ct); + atomSerializer.ReadFrom(reader); return atomSerializer.Feed as TSyndicationFeed; } - Rss20FeedFormatter rssSerializer = new Rss20FeedFormatter(); if (rssSerializer.CanRead(reader)) { - await rssSerializer.ReadFromAsync(reader, ct); + rssSerializer.ReadFrom(reader); return rssSerializer.Feed as TSyndicationFeed; } - - throw new XmlException(string.Format(SR.UnknownFeedXml, reader.LocalName, reader.NamespaceURI)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.UnknownFeedXml, reader.LocalName, reader.NamespaceURI))); } public virtual SyndicationFeed Clone(bool cloneItems) @@ -442,14 +312,14 @@ namespace System.ServiceModel.Syndication return new Rss20FeedFormatter(this, serializeExtensionsAsAtom); } - public Task SaveAsAtom10Async(XmlWriter writer, CancellationToken ct) + public void SaveAsAtom10(XmlWriter writer) { - return GetAtom10Formatter().WriteToAsync(writer, ct); + this.GetAtom10Formatter().WriteTo(writer); } - public Task SaveAsRss20Async(XmlWriter writer, CancellationToken ct) + public void SaveAsRss20(XmlWriter writer) { - return GetRss20Formatter().WriteToAsync(writer, ct); + this.GetRss20Formatter().WriteTo(writer); } protected internal virtual SyndicationCategory CreateCategory() @@ -482,14 +352,14 @@ namespace System.ServiceModel.Syndication return false; } - protected internal virtual Task WriteAttributeExtensionsAsync(XmlWriter writer, string version) + protected internal virtual void WriteAttributeExtensions(XmlWriter writer, string version) { - return _extensions.WriteAttributeExtensionsAsync(writer); + _extensions.WriteAttributeExtensions(writer); } - protected internal virtual Task WriteElementExtensionsAsync(XmlWriter writer, string version) + protected internal virtual void WriteElementExtensions(XmlWriter writer, string version) { - return _extensions.WriteElementExtensionsAsync(writer); + _extensions.WriteElementExtensions(writer); } internal void LoadElementExtensions(XmlReader readerOverUnparsedExtensions, int maxExtensionSize) diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationFeedFormatter.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationFeedFormatter.cs index 1fc464787b..750563c606 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationFeedFormatter.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationFeedFormatter.cs @@ -2,16 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -// DEBUG!!! #if disabled statements - namespace System.ServiceModel.Syndication { using System; + using System.Diagnostics; using System.Globalization; using System.Runtime.Serialization; - using System.Threading; - using System.Threading.Tasks; using System.Xml; + using DiagnosticUtility = System.ServiceModel.DiagnosticUtility; [DataContract] public abstract class SyndicationFeedFormatter @@ -21,15 +19,17 @@ namespace System.ServiceModel.Syndication protected SyndicationFeedFormatter() { _feed = null; + DateTimeParser = GetDefaultDateTimeParser(); } protected SyndicationFeedFormatter(SyndicationFeed feedToWrite) { if (feedToWrite == null) { - throw new ArgumentNullException(nameof(feedToWrite)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feedToWrite"); } _feed = feedToWrite; + DateTimeParser = GetDefaultDateTimeParser(); } public SyndicationFeed Feed @@ -40,24 +40,40 @@ namespace System.ServiceModel.Syndication } } - public abstract string Version { get; } + public Func UriParser { get; set; } = DefaultUriParser; + + // Different DateTimeParsers are needed for Atom and Rss so can't set inline + public Func DateTimeParser { get; set; } + + internal virtual Func GetDefaultDateTimeParser() + { + return NotImplementedDateTimeParser; + } + + private DateTimeOffset NotImplementedDateTimeParser(string dtoString, string localName, string ns) + { + throw new NotImplementedException(); + } + + public abstract string Version + { get; } public abstract bool CanRead(XmlReader reader); - public abstract Task ReadFromAsync(XmlReader reader, CancellationToken ct); + public abstract void ReadFrom(XmlReader reader); public override string ToString() { - return string.Format(CultureInfo.CurrentCulture, "{0}, SyndicationVersion={1}", this.GetType(), this.Version); + return String.Format(CultureInfo.CurrentCulture, "{0}, SyndicationVersion={1}", this.GetType(), this.Version); } - public abstract Task WriteToAsync(XmlWriter writer, CancellationToken ct); + public abstract void WriteTo(XmlWriter writer); internal static protected SyndicationCategory CreateCategory(SyndicationFeed feed) { if (feed == null) { - throw new ArgumentNullException(nameof(feed)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feed"); } return GetNonNullValue(feed.CreateCategory(), SR.FeedCreatedNullCategory); } @@ -66,7 +82,7 @@ namespace System.ServiceModel.Syndication { if (item == null) { - throw new ArgumentNullException(nameof(item)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); } return GetNonNullValue(item.CreateCategory(), SR.ItemCreatedNullCategory); } @@ -75,7 +91,7 @@ namespace System.ServiceModel.Syndication { if (feed == null) { - throw new ArgumentNullException(nameof(feed)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feed"); } return GetNonNullValue(feed.CreateItem(), SR.FeedCreatedNullItem); } @@ -84,7 +100,7 @@ namespace System.ServiceModel.Syndication { if (feed == null) { - throw new ArgumentNullException(nameof(feed)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feed"); } return GetNonNullValue(feed.CreateLink(), SR.FeedCreatedNullPerson); } @@ -93,7 +109,7 @@ namespace System.ServiceModel.Syndication { if (item == null) { - throw new ArgumentNullException(nameof(item)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); } return GetNonNullValue(item.CreateLink(), SR.ItemCreatedNullPerson); } @@ -102,7 +118,7 @@ namespace System.ServiceModel.Syndication { if (feed == null) { - throw new ArgumentNullException(nameof(feed)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feed"); } return GetNonNullValue(feed.CreatePerson(), SR.FeedCreatedNullPerson); } @@ -111,7 +127,7 @@ namespace System.ServiceModel.Syndication { if (item == null) { - throw new ArgumentNullException(nameof(item)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); } return GetNonNullValue(item.CreatePerson(), SR.ItemCreatedNullPerson); } @@ -120,7 +136,7 @@ namespace System.ServiceModel.Syndication { if (feed == null) { - throw new ArgumentNullException(nameof(feed)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feed"); } feed.LoadElementExtensions(reader, maxExtensionSize); } @@ -129,7 +145,7 @@ namespace System.ServiceModel.Syndication { if (item == null) { - throw new ArgumentNullException(nameof(item)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); } item.LoadElementExtensions(reader, maxExtensionSize); } @@ -138,7 +154,7 @@ namespace System.ServiceModel.Syndication { if (category == null) { - throw new ArgumentNullException(nameof(category)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("category"); } category.LoadElementExtensions(reader, maxExtensionSize); } @@ -147,7 +163,7 @@ namespace System.ServiceModel.Syndication { if (link == null) { - throw new ArgumentNullException(nameof(link)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("link"); } link.LoadElementExtensions(reader, maxExtensionSize); } @@ -156,7 +172,7 @@ namespace System.ServiceModel.Syndication { if (person == null) { - throw new ArgumentNullException(nameof(person)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("person"); } person.LoadElementExtensions(reader, maxExtensionSize); } @@ -165,14 +181,12 @@ namespace System.ServiceModel.Syndication { if (feed == null) { - throw new ArgumentNullException(nameof(feed)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feed"); } - if (FeedUtils.IsXmlns(name, ns)) { return true; } - return feed.TryParseAttribute(name, ns, value, version); } @@ -180,7 +194,7 @@ namespace System.ServiceModel.Syndication { if (item == null) { - throw new ArgumentNullException(nameof(item)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); } if (FeedUtils.IsXmlns(name, ns)) { @@ -193,7 +207,7 @@ namespace System.ServiceModel.Syndication { if (category == null) { - throw new ArgumentNullException(nameof(category)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("category"); } if (FeedUtils.IsXmlns(name, ns)) { @@ -206,7 +220,7 @@ namespace System.ServiceModel.Syndication { if (link == null) { - throw new ArgumentNullException(nameof(link)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("link"); } if (FeedUtils.IsXmlns(name, ns)) { @@ -219,7 +233,7 @@ namespace System.ServiceModel.Syndication { if (person == null) { - throw new ArgumentNullException(nameof(person)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("person"); } if (FeedUtils.IsXmlns(name, ns)) { @@ -237,9 +251,8 @@ namespace System.ServiceModel.Syndication { if (feed == null) { - throw new ArgumentNullException(nameof(feed)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feed"); } - return feed.TryParseElement(reader, version); } @@ -247,7 +260,7 @@ namespace System.ServiceModel.Syndication { if (item == null) { - throw new ArgumentNullException(nameof(item)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); } return item.TryParseElement(reader, version); } @@ -256,7 +269,7 @@ namespace System.ServiceModel.Syndication { if (category == null) { - throw new ArgumentNullException(nameof(category)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("category"); } return category.TryParseElement(reader, version); } @@ -265,7 +278,7 @@ namespace System.ServiceModel.Syndication { if (link == null) { - throw new ArgumentNullException(nameof(link)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("link"); } return link.TryParseElement(reader, version); } @@ -274,105 +287,126 @@ namespace System.ServiceModel.Syndication { if (person == null) { - throw new ArgumentNullException(nameof(person)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("person"); } return person.TryParseElement(reader, version); } - internal static protected async Task WriteAttributeExtensionsAsync(XmlWriter writer, SyndicationFeed feed, string version) + internal static protected void WriteAttributeExtensions(XmlWriter writer, SyndicationFeed feed, string version) { if (feed == null) { - throw new ArgumentNullException(nameof(feed)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feed"); } - await feed.WriteAttributeExtensionsAsync(writer, version); + feed.WriteAttributeExtensions(writer, version); } - internal static protected Task WriteAttributeExtensionsAsync(XmlWriter writer, SyndicationItem item, string version) + internal static protected void WriteAttributeExtensions(XmlWriter writer, SyndicationItem item, string version) { if (item == null) { - throw new ArgumentNullException(nameof(item)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); } - return item.WriteAttributeExtensionsAsync(writer, version); + item.WriteAttributeExtensions(writer, version); } - internal static protected Task WriteAttributeExtensionsAsync(XmlWriter writer, SyndicationCategory category, string version) + internal static protected void WriteAttributeExtensions(XmlWriter writer, SyndicationCategory category, string version) { if (category == null) { - throw new ArgumentNullException(nameof(category)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("category"); } - return category.WriteAttributeExtensionsAsync(writer, version); + category.WriteAttributeExtensions(writer, version); } - internal static protected Task WriteAttributeExtensions(XmlWriter writer, SyndicationLink link, string version) + internal static protected void WriteAttributeExtensions(XmlWriter writer, SyndicationLink link, string version) { if (link == null) { - throw new ArgumentNullException(nameof(link)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("link"); } - return link.WriteAttributeExtensionsAsync(writer, version); + link.WriteAttributeExtensions(writer, version); } - internal static protected Task WriteAttributeExtensionsAsync(XmlWriter writer, SyndicationPerson person, string version) + internal static protected void WriteAttributeExtensions(XmlWriter writer, SyndicationPerson person, string version) { if (person == null) { - throw new ArgumentNullException(nameof(person)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("person"); } - return person.WriteAttributeExtensionsAsync(writer, version); + person.WriteAttributeExtensions(writer, version); } - internal static protected Task WriteElementExtensionsAsync(XmlWriter writer, SyndicationFeed feed, string version) + internal static protected void WriteElementExtensions(XmlWriter writer, SyndicationFeed feed, string version) { if (feed == null) { - throw new ArgumentNullException(nameof(feed)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feed"); } - return feed.WriteElementExtensionsAsync(writer, version); + feed.WriteElementExtensions(writer, version); } - internal static protected Task WriteElementExtensionsAsync(XmlWriter writer, SyndicationItem item, string version) + internal static protected void WriteElementExtensions(XmlWriter writer, SyndicationItem item, string version) { if (item == null) { - throw new ArgumentNullException(nameof(item)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); } - return item.WriteElementExtensionsAsync(writer, version); + item.WriteElementExtensions(writer, version); } - internal static protected Task WriteElementExtensionsAsync(XmlWriter writer, SyndicationCategory category, string version) + internal static protected void WriteElementExtensions(XmlWriter writer, SyndicationCategory category, string version) { if (category == null) { - throw new ArgumentNullException(nameof(category)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("category"); } - return category.WriteElementExtensionsAsync(writer, version); + category.WriteElementExtensions(writer, version); } - internal static protected Task WriteElementExtensionsAsync(XmlWriter writer, SyndicationLink link, string version) + internal static protected void WriteElementExtensions(XmlWriter writer, SyndicationLink link, string version) { if (link == null) { - throw new ArgumentNullException(nameof(link)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("link"); } - return link.WriteElementExtensionsAsync(writer, version); + link.WriteElementExtensions(writer, version); } - internal static protected Task WriteElementExtensionsAsync(XmlWriter writer, SyndicationPerson person, string version) + internal static protected void WriteElementExtensions(XmlWriter writer, SyndicationPerson person, string version) { if (person == null) { - throw new ArgumentNullException(nameof(person)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("person"); } - - return person.WriteElementExtensionsAsync(writer, version); + person.WriteElementExtensions(writer, version); } internal protected virtual void SetFeed(SyndicationFeed feed) { - _feed = feed ?? throw new ArgumentNullException(nameof(feed)); + if (feed == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feed"); + } + _feed = feed; + } + + internal DateTimeOffset DateFromString(string dateTimeString, XmlReader reader) + { + try + { + return DateTimeParser(dateTimeString, reader.LocalName, reader.NamespaceURI); + } + catch (FormatException e) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new XmlException(FeedUtils.AddLineInfo(reader, SR.ErrorParsingDateTime), e)); + } + } + + private static Uri DefaultUriParser(string value, UriKind kind, string localName, string ns) + { + return new Uri(value, kind); } internal static void CloseBuffer(XmlBuffer buffer, XmlDictionaryWriter extWriter) @@ -386,7 +420,7 @@ namespace System.ServiceModel.Syndication buffer.Close(); } - internal static async Task> CreateBufferIfRequiredAndWriteNodeAsync(XmlBuffer buffer, XmlDictionaryWriter extWriter, XmlReader reader, int maxExtensionSize) + internal static void CreateBufferIfRequiredAndWriteNode(ref XmlBuffer buffer, ref XmlDictionaryWriter extWriter, XmlReader reader, int maxExtensionSize) { if (buffer == null) { @@ -394,21 +428,7 @@ namespace System.ServiceModel.Syndication extWriter = buffer.OpenSection(XmlDictionaryReaderQuotas.Max); extWriter.WriteStartElement(Rss20Constants.ExtensionWrapperTag); } - - XmlDictionaryReader dictionaryReader = reader as XmlDictionaryReader; - if (dictionaryReader != null) - { - // Reimplementing WriteNode for XmlDictionaryWriter asynchronously depends on multiple internal methods - // so isn't feasible to reimplement here. As the primary scenario will be usage with an XmlReader which - // isn't an XmlDictionaryReader, deferring to the synchronous implementation is a reasonable fallback. - extWriter.WriteNode(reader, false); - } - else - { - await extWriter.WriteNodeAsync(reader, false); - } - - return Tuple.Create(buffer, extWriter); + extWriter.WriteNode(reader, false); } internal static SyndicationFeed CreateFeedInstance(Type feedType) @@ -427,9 +447,8 @@ namespace System.ServiceModel.Syndication { if (feed == null) { - throw new ArgumentNullException(nameof(feed)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feed"); } - CloseBuffer(buffer, writer); feed.LoadElementExtensions(buffer); } @@ -438,7 +457,7 @@ namespace System.ServiceModel.Syndication { if (item == null) { - throw new ArgumentNullException(nameof(item)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); } CloseBuffer(buffer, writer); item.LoadElementExtensions(buffer); @@ -448,9 +467,8 @@ namespace System.ServiceModel.Syndication { if (category == null) { - throw new ArgumentNullException(nameof(category)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("category"); } - CloseBuffer(buffer, writer); category.LoadElementExtensions(buffer); } @@ -459,9 +477,8 @@ namespace System.ServiceModel.Syndication { if (link == null) { - throw new ArgumentNullException(nameof(link)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("link"); } - CloseBuffer(buffer, writer); link.LoadElementExtensions(buffer); } @@ -470,34 +487,65 @@ namespace System.ServiceModel.Syndication { if (person == null) { - throw new ArgumentNullException(nameof(person)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("person"); } - CloseBuffer(buffer, writer); person.LoadElementExtensions(buffer); } - internal static async Task MoveToStartElementAsync(XmlReaderWrapper reader) - { - if (!await reader.IsStartElementAsync()) - { - XmlExceptionHelper.ThrowStartElementExpected(XmlDictionaryReader.CreateDictionaryReader(reader)); - } - } - internal static void MoveToStartElement(XmlReader reader) { + Debug.Assert(reader != null, "reader != null"); if (!reader.IsStartElement()) { XmlExceptionHelper.ThrowStartElementExpected(XmlDictionaryReader.CreateDictionaryReader(reader)); } } + internal static void TraceFeedReadBegin() + { + } + + internal static void TraceFeedReadEnd() + { + } + + internal static void TraceFeedWriteBegin() + { + } + + internal static void TraceFeedWriteEnd() + { + } + + internal static void TraceItemReadBegin() + { + } + + internal static void TraceItemReadEnd() + { + } + + internal static void TraceItemWriteBegin() + { + } + + internal static void TraceItemWriteEnd() + { + } + + internal static void TraceSyndicationElementIgnoredOnRead(XmlReader reader) + { + } protected abstract SyndicationFeed CreateFeedInstance(); private static T GetNonNullValue(T value, string errorMsg) { + if (value == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(errorMsg))); + } return value; } @@ -505,14 +553,14 @@ namespace System.ServiceModel.Syndication { private static void ThrowXmlException(XmlDictionaryReader reader, string res, string arg1) { - string s = string.Format(res, arg1); + string s = SR.Format(res, arg1); IXmlLineInfo lineInfo = reader as IXmlLineInfo; if (lineInfo != null && lineInfo.HasLineInfo()) { - s += " " + string.Format(SR.XmlLineInfo, lineInfo.LineNumber, lineInfo.LinePosition); + s += " " + SR.Format(SR.XmlLineInfo, lineInfo.LineNumber, lineInfo.LinePosition); } - throw new XmlException(s); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(s)); } private static string GetName(string prefix, string localName) @@ -526,23 +574,23 @@ namespace System.ServiceModel.Syndication private static string GetWhatWasFound(XmlDictionaryReader reader) { if (reader.EOF) - return SR.XmlFoundEndOfFile; + return SR.Format(SR.XmlFoundEndOfFile); switch (reader.NodeType) { case XmlNodeType.Element: - return string.Format(SR.XmlFoundElement, GetName(reader.Prefix, reader.LocalName), reader.NamespaceURI); + return SR.Format(SR.XmlFoundElement, GetName(reader.Prefix, reader.LocalName), reader.NamespaceURI); case XmlNodeType.EndElement: - return string.Format(SR.XmlFoundEndElement, GetName(reader.Prefix, reader.LocalName), reader.NamespaceURI); + return SR.Format(SR.XmlFoundEndElement, GetName(reader.Prefix, reader.LocalName), reader.NamespaceURI); case XmlNodeType.Text: case XmlNodeType.Whitespace: case XmlNodeType.SignificantWhitespace: - return string.Format(SR.XmlFoundText, reader.Value); + return SR.Format(SR.XmlFoundText, reader.Value); case XmlNodeType.Comment: - return string.Format(SR.XmlFoundComment, reader.Value); + return SR.Format(SR.XmlFoundComment, reader.Value); case XmlNodeType.CDATA: - return string.Format(SR.XmlFoundCData, reader.Value); + return SR.Format(SR.XmlFoundCData, reader.Value); } - return string.Format(SR.XmlFoundNodeType, reader.NodeType); + return SR.Format(SR.XmlFoundNodeType, reader.NodeType); } static public void ThrowStartElementExpected(XmlDictionaryReader reader) diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationItem.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationItem.cs index ca308a4522..08cf46b42e 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationItem.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationItem.cs @@ -2,14 +2,18 @@ // 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; +using System.Xml; +using System.Runtime.Serialization; +using System.Xml.Serialization; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + namespace System.ServiceModel.Syndication { - using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Threading.Tasks; - using System.Xml; - // NOTE: This class implements Clone so if you add any members, please update the copy ctor public class SyndicationItem : IExtensibleSyndicationObject { @@ -49,7 +53,6 @@ namespace System.ServiceModel.Syndication { this.Title = new TextSyndicationContent(title); } - _content = content; if (itemAlternateLink != null) { @@ -63,7 +66,7 @@ namespace System.ServiceModel.Syndication { if (source == null) { - throw new ArgumentNullException(nameof(source)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("source"); } _extensions = source._extensions.Clone(); _authors = FeedUtils.ClonePersons(source._authors); @@ -155,10 +158,24 @@ namespace System.ServiceModel.Syndication set { _id = value; } } + internal Exception LastUpdatedTimeException { get; set; } + public DateTimeOffset LastUpdatedTime { - get { return _lastUpdatedTime; } - set { _lastUpdatedTime = value; } + get + { + if (LastUpdatedTimeException != null) + { + throw LastUpdatedTimeException; + } + + return _lastUpdatedTime; + } + set + { + LastUpdatedTimeException = null; + _lastUpdatedTime = value; + } } public Collection Links @@ -173,10 +190,24 @@ namespace System.ServiceModel.Syndication } } + internal Exception PublishDateException { get; set; } + public DateTimeOffset PublishDate { - get { return _publishDate; } - set { _publishDate = value; } + get + { + if (PublishDateException != null) + { + throw PublishDateException; + } + + return _publishDate; + } + set + { + PublishDateException = null; + _publishDate = value; + } } public SyndicationFeed SourceFeed @@ -197,36 +228,41 @@ namespace System.ServiceModel.Syndication set { _title = value; } } - public static Task LoadAsync(XmlReader reader) + public static SyndicationItem Load(XmlReader reader) { - return LoadAsync(reader); + return Load(reader); } - public static async Task LoadAsync(XmlReader reader) + public static TSyndicationItem Load(XmlReader reader) where TSyndicationItem : SyndicationItem, new() { if (reader == null) { - throw new ArgumentNullException(nameof(reader)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); + } + Atom10ItemFormatter atomSerializer = new Atom10ItemFormatter(); + if (atomSerializer.CanRead(reader)) + { + atomSerializer.ReadFrom(reader); + return atomSerializer.Item as TSyndicationItem; } - Rss20ItemFormatter rssSerializer = new Rss20ItemFormatter(); - if (rssSerializer.CanRead(reader)) { - await rssSerializer.ReadFromAsync(reader); + rssSerializer.ReadFrom(reader); return rssSerializer.Item as TSyndicationItem; } - - throw new XmlException(string.Format(SR.UnknownItemXml, reader.LocalName, reader.NamespaceURI)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.UnknownItemXml, reader.LocalName, reader.NamespaceURI))); } + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "0#permalink", Justification = "permalink is a term defined in the RSS format")] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Permalink", Justification = "permalink is a term defined in the RSS format")] public void AddPermalink(Uri permalink) { if (permalink == null) { - throw new ArgumentNullException(nameof(permalink)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("permalink"); } this.Id = permalink.AbsoluteUri; this.Links.Add(SyndicationLink.CreateAlternateLink(permalink)); @@ -252,14 +288,14 @@ namespace System.ServiceModel.Syndication return new Rss20ItemFormatter(this, serializeExtensionsAsAtom); } - public Task SaveAsAtom10(XmlWriter writer) + public void SaveAsAtom10(XmlWriter writer) { - return GetAtom10Formatter().WriteToAsync(writer); + this.GetAtom10Formatter().WriteTo(writer); } - public Task SaveAsRss20(XmlWriter writer) + public void SaveAsRss20(XmlWriter writer) { - return GetRss20Formatter().WriteToAsync(writer); + this.GetRss20Formatter().WriteTo(writer); } protected internal virtual SyndicationCategory CreateCategory() @@ -293,14 +329,14 @@ namespace System.ServiceModel.Syndication return false; } - protected internal virtual Task WriteAttributeExtensionsAsync(XmlWriter writer, string version) + protected internal virtual void WriteAttributeExtensions(XmlWriter writer, string version) { - return _extensions.WriteAttributeExtensionsAsync(writer); + _extensions.WriteAttributeExtensions(writer); } - protected internal virtual Task WriteElementExtensionsAsync(XmlWriter writer, string version) + protected internal virtual void WriteElementExtensions(XmlWriter writer, string version) { - return _extensions.WriteElementExtensionsAsync(writer); + _extensions.WriteElementExtensions(writer); } internal void LoadElementExtensions(XmlReader readerOverUnparsedExtensions, int maxExtensionSize) diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationItemFormatter.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationItemFormatter.cs index 4483f94ba0..435c212652 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationItemFormatter.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationItemFormatter.cs @@ -5,11 +5,15 @@ namespace System.ServiceModel.Syndication { using System; - using System.Globalization; - using System.Runtime.CompilerServices; - using System.Runtime.Serialization; - using System.Threading.Tasks; + using System.Collections.Generic; using System.Xml; + using System.Runtime.Serialization; + using System.Globalization; + using System.Xml.Serialization; + using System.Xml.Schema; + using System.Diagnostics.CodeAnalysis; + using DiagnosticUtility = System.ServiceModel.DiagnosticUtility; + using System.Runtime.CompilerServices; [DataContract] public abstract class SyndicationItemFormatter @@ -25,7 +29,7 @@ namespace System.ServiceModel.Syndication { if (itemToWrite == null) { - throw new ArgumentNullException(nameof(itemToWrite)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("itemToWrite"); } _item = itemToWrite; } @@ -43,24 +47,29 @@ namespace System.ServiceModel.Syndication public abstract bool CanRead(XmlReader reader); - public abstract Task ReadFromAsync(XmlReader reader); + public abstract void ReadFrom(XmlReader reader); public override string ToString() { - return string.Format(CultureInfo.CurrentCulture, "{0}, SyndicationVersion={1}", this.GetType(), this.Version); + return String.Format(CultureInfo.CurrentCulture, "{0}, SyndicationVersion={1}", this.GetType(), this.Version); } - public abstract Task WriteToAsync(XmlWriter writer); + public abstract void WriteTo(XmlWriter writer); internal protected virtual void SetItem(SyndicationItem item) { if (item == null) { - throw new ArgumentNullException(nameof(item)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); } _item = item; } + internal static void CreateBufferIfRequiredAndWriteNode(ref XmlBuffer buffer, ref XmlDictionaryWriter extWriter, XmlDictionaryReader reader, int maxExtensionSize) + { + SyndicationFeedFormatter.CreateBufferIfRequiredAndWriteNode(ref buffer, ref extWriter, reader, maxExtensionSize); + } + internal static SyndicationItem CreateItemInstance(Type itemType) { if (itemType.Equals(typeof(SyndicationItem))) @@ -175,46 +184,46 @@ namespace System.ServiceModel.Syndication return SyndicationFeedFormatter.TryParseElement(reader, person, version); } - protected static async Task WriteAttributeExtensionsAsync(XmlWriter writer, SyndicationItem item, string version) + protected static void WriteAttributeExtensions(XmlWriter writer, SyndicationItem item, string version) { - await SyndicationFeedFormatter.WriteAttributeExtensionsAsync(writer, item, version); + SyndicationFeedFormatter.WriteAttributeExtensions(writer, item, version); } - protected static async Task WriteAttributeExtensionsAsync(XmlWriter writer, SyndicationCategory category, string version) + protected static void WriteAttributeExtensions(XmlWriter writer, SyndicationCategory category, string version) { - await SyndicationFeedFormatter.WriteAttributeExtensionsAsync(writer, category, version); + SyndicationFeedFormatter.WriteAttributeExtensions(writer, category, version); } - protected static async Task WriteAttributeExtensionsAsync(XmlWriter writer, SyndicationLink link, string version) + protected static void WriteAttributeExtensions(XmlWriter writer, SyndicationLink link, string version) { - await SyndicationFeedFormatter.WriteAttributeExtensions(writer, link, version); + SyndicationFeedFormatter.WriteAttributeExtensions(writer, link, version); } - protected static async Task WriteAttributeExtensionsAsync(XmlWriter writer, SyndicationPerson person, string version) + protected static void WriteAttributeExtensions(XmlWriter writer, SyndicationPerson person, string version) { - await SyndicationFeedFormatter.WriteAttributeExtensionsAsync(writer, person, version); + SyndicationFeedFormatter.WriteAttributeExtensions(writer, person, version); } - protected static async Task WriteElementExtensionsAsync(XmlWriter writer, SyndicationItem item, string version) + protected static void WriteElementExtensions(XmlWriter writer, SyndicationItem item, string version) { - await SyndicationFeedFormatter.WriteElementExtensionsAsync(writer, item, version); + SyndicationFeedFormatter.WriteElementExtensions(writer, item, version); } protected abstract SyndicationItem CreateItemInstance(); - protected Task WriteElementExtensionsAsync(XmlWriter writer, SyndicationCategory category, string version) + protected void WriteElementExtensions(XmlWriter writer, SyndicationCategory category, string version) { - return SyndicationFeedFormatter.WriteElementExtensionsAsync(writer, category, version); + SyndicationFeedFormatter.WriteElementExtensions(writer, category, version); } - protected Task WriteElementExtensionsAsync(XmlWriter writer, SyndicationLink link, string version) + protected void WriteElementExtensions(XmlWriter writer, SyndicationLink link, string version) { - return SyndicationFeedFormatter.WriteElementExtensionsAsync(writer, link, version); + SyndicationFeedFormatter.WriteElementExtensions(writer, link, version); } - protected Task WriteElementExtensionsAsync(XmlWriter writer, SyndicationPerson person, string version) + protected void WriteElementExtensions(XmlWriter writer, SyndicationPerson person, string version) { - return SyndicationFeedFormatter.WriteElementExtensionsAsync(writer, person, version); + SyndicationFeedFormatter.WriteElementExtensions(writer, person, version); } } } diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationLink.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationLink.cs index 9743cb0cb9..14a1b71a6b 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationLink.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationLink.cs @@ -2,14 +2,15 @@ // 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.Collections.Generic; +using System.Xml; +using System.Collections.ObjectModel; +using System.Xml.Serialization; +using System.Runtime.Serialization; +using System.Runtime.CompilerServices; + namespace System.ServiceModel.Syndication { - using System; - using System.Collections.Generic; - using System.Runtime.CompilerServices; - using System.Threading.Tasks; - using System.Xml; - // NOTE: This class implements Clone so if you add any members, please update the copy ctor public class SyndicationLink : IExtensibleSyndicationObject { @@ -30,7 +31,7 @@ namespace System.ServiceModel.Syndication { if (length < 0) { - throw new ArgumentOutOfRangeException(nameof(length)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("length")); } _baseUri = null; _uri = uri; @@ -49,7 +50,7 @@ namespace System.ServiceModel.Syndication { if (source == null) { - throw new ArgumentNullException(nameof(source)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("source"); } _length = source._length; _mediaType = source._mediaType; @@ -83,7 +84,7 @@ namespace System.ServiceModel.Syndication { if (value < 0) { - throw new ArgumentOutOfRangeException(nameof(value)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value")); } _length = value; } @@ -176,14 +177,14 @@ namespace System.ServiceModel.Syndication return false; } - protected internal virtual Task WriteAttributeExtensionsAsync(XmlWriter writer, string version) + protected internal virtual void WriteAttributeExtensions(XmlWriter writer, string version) { - return _extensions.WriteAttributeExtensionsAsync(writer); + _extensions.WriteAttributeExtensions(writer); } - protected internal virtual Task WriteElementExtensionsAsync(XmlWriter writer, string version) + protected internal virtual void WriteElementExtensions(XmlWriter writer, string version) { - return _extensions.WriteElementExtensionsAsync(writer); + _extensions.WriteElementExtensions(writer); } internal void LoadElementExtensions(XmlReader readerOverUnparsedExtensions, int maxExtensionSize) diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationPerson.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationPerson.cs index e69a76f300..55c9f7e83c 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationPerson.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationPerson.cs @@ -2,15 +2,17 @@ // 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.Collections.ObjectModel; +using System.Collections.Generic; +using System.Xml; +using System.Runtime.Serialization; +using System.Xml.Serialization; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + namespace System.ServiceModel.Syndication { - using System; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.Runtime.CompilerServices; - using System.Threading.Tasks; - using System.Xml; - // NOTE: This class implements Clone so if you add any members, please update the copy ctor public class SyndicationPerson : IExtensibleSyndicationObject { @@ -41,7 +43,7 @@ namespace System.ServiceModel.Syndication { if (source == null) { - throw new ArgumentNullException(nameof(source)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("source"); } _email = source._email; _name = source._name; @@ -93,14 +95,14 @@ namespace System.ServiceModel.Syndication return false; } - protected internal virtual Task WriteAttributeExtensionsAsync(XmlWriter writer, string version) + protected internal virtual void WriteAttributeExtensions(XmlWriter writer, string version) { - return _extensions.WriteAttributeExtensionsAsync(writer); + _extensions.WriteAttributeExtensions(writer); } - protected internal virtual Task WriteElementExtensionsAsync(XmlWriter writer, string version) + protected internal virtual void WriteElementExtensions(XmlWriter writer, string version) { - return _extensions.WriteElementExtensionsAsync(writer); + _extensions.WriteElementExtensions(writer); } internal void LoadElementExtensions(XmlReader readerOverUnparsedExtensions, int maxExtensionSize) diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationVersions.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationVersions.cs index cca8ac3133..48647f4c92 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationVersions.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationVersions.cs @@ -2,10 +2,10 @@ // 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.Runtime.CompilerServices; + namespace System.ServiceModel.Syndication { - using System.Runtime.CompilerServices; - public static class SyndicationVersions { public const string Atom10 = "Atom10"; diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/TextSyndicationContent.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/TextSyndicationContent.cs index 7953f66636..90ec47876b 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/TextSyndicationContent.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/TextSyndicationContent.cs @@ -2,12 +2,16 @@ // 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.Collections.Generic; +using System.Text; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using System.Runtime.CompilerServices; + namespace System.ServiceModel.Syndication { - using System; - using System.Runtime.CompilerServices; - using System.Xml; - // NOTE: This class implements Clone so if you add any members, please update the copy ctor public class TextSyndicationContent : SyndicationContent { @@ -22,7 +26,7 @@ namespace System.ServiceModel.Syndication { if (!TextSyndicationContentKindHelper.IsDefined(textKind)) { - throw new ArgumentOutOfRangeException(nameof(textKind)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("textKind")); } _text = text; _textKind = textKind; @@ -33,7 +37,7 @@ namespace System.ServiceModel.Syndication { if (source == null) { - throw new ArgumentNullException(nameof(source)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("source"); } _text = source._text; _textKind = source._textKind; diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/TextSyndicationContentKind.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/TextSyndicationContentKind.cs index 1dda2a2e36..8274f0b83e 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/TextSyndicationContentKind.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/TextSyndicationContentKind.cs @@ -2,10 +2,10 @@ // 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.Runtime.CompilerServices; + namespace System.ServiceModel.Syndication { - using System.Runtime.CompilerServices; - public enum TextSyndicationContentKind { Plaintext, diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/TextSyndicationContentKindHelper.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/TextSyndicationContentKindHelper.cs index 2fdb263005..13c81b6888 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/TextSyndicationContentKindHelper.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/TextSyndicationContentKindHelper.cs @@ -2,6 +2,13 @@ // 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.Collections.Generic; +using System.Text; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; + namespace System.ServiceModel.Syndication { internal static class TextSyndicationContentKindHelper diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/UrlSyndicationContent.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/UrlSyndicationContent.cs index 86bce8777f..18ccf06f55 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/UrlSyndicationContent.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/UrlSyndicationContent.cs @@ -2,12 +2,11 @@ // 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.Xml; +using System.Runtime.CompilerServices; + namespace System.ServiceModel.Syndication { - using System; - using System.Runtime.CompilerServices; - using System.Xml; - // NOTE: This class implements Clone so if you add any members, please update the copy ctor public class UrlSyndicationContent : SyndicationContent { @@ -18,7 +17,7 @@ namespace System.ServiceModel.Syndication { if (url == null) { - throw new ArgumentNullException(nameof(url)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("url"); } _url = url; _mediaType = mediaType; @@ -29,7 +28,7 @@ namespace System.ServiceModel.Syndication { if (source == null) { - throw new ArgumentNullException(nameof(source)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("source"); } _url = source._url; _mediaType = source._mediaType; diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Workspace.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Workspace.cs index b8d2641343..a7962a8a07 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Workspace.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/Workspace.cs @@ -2,15 +2,15 @@ // 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.Collections.ObjectModel; +using System.Runtime.Serialization; +using System.Xml.Serialization; +using System.Collections.Generic; +using System.Xml; +using System.Runtime.CompilerServices; + namespace System.ServiceModel.Syndication { - using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Runtime.CompilerServices; - using System.Threading.Tasks; - using System.Xml; - public class Workspace : IExtensibleSyndicationObject { private Uri _baseUri; @@ -95,17 +95,17 @@ namespace System.ServiceModel.Syndication return false; } - protected internal virtual Task WriteAttributeExtensions(XmlWriter writer, string version) + protected internal virtual void WriteAttributeExtensions(XmlWriter writer, string version) { - return _extensions.WriteAttributeExtensionsAsync(writer); + _extensions.WriteAttributeExtensions(writer); } - protected internal virtual Task WriteElementExtensionsAsync(XmlWriter writer, string version) + protected internal virtual void WriteElementExtensions(XmlWriter writer, string version) { - return _extensions.WriteElementExtensionsAsync(writer); + _extensions.WriteElementExtensions(writer); } - internal void LoadElementExtensions(XmlReaderWrapper readerOverUnparsedExtensions, int maxExtensionSize) + internal void LoadElementExtensions(XmlReader readerOverUnparsedExtensions, int maxExtensionSize) { _extensions.LoadElementExtensions(readerOverUnparsedExtensions, maxExtensionSize); } diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/XmlReaderWrapper.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/XmlReaderWrapper.cs deleted file mode 100644 index f7c66af2ed..0000000000 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/XmlReaderWrapper.cs +++ /dev/null @@ -1,548 +0,0 @@ -// 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.Diagnostics; -using System.Threading.Tasks; -using System.Xml; - -namespace System.ServiceModel.Syndication -{ - internal class XmlReaderWrapper : XmlReader - { - private XmlReader _reader; - - private Func> _getValueFunc; - private Func> _moveToContentFunc; - private Func _skipFunc; - private Func> _readFunc; - private Func> _readInnerXmlFunc; - - private XmlReaderWrapper(XmlReader reader) - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - _reader = reader; - - if (_reader.Settings.Async) - { - InitAsync(); - } - else - { - Init(); - } - } - - public static XmlReaderWrapper CreateFromReader(XmlReader reader) - { - XmlReaderWrapper wrappedReader = reader as XmlReaderWrapper; - return wrappedReader != null ? wrappedReader : new XmlReaderWrapper(reader); - } - - private void InitAsync() - { - _getValueFunc = new Func>((thisPtr) => { return thisPtr._reader.GetValueAsync(); }); - _moveToContentFunc = new Func>((thisPtr) => { return thisPtr._reader.MoveToContentAsync(); }); - _skipFunc = new Func((thisPtr) => { return thisPtr._reader.SkipAsync(); }); - _readFunc = new Func>((thisPtr) => { return thisPtr._reader.ReadAsync(); }); - _readInnerXmlFunc = new Func>((thisPtr) => { return thisPtr._reader.ReadInnerXmlAsync(); }); - } - - private void Init() - { - _getValueFunc = new Func>((thisPtr) => { return Task.FromResult(thisPtr._reader.Value); }); - _moveToContentFunc = new Func>((thisPtr) => { return Task.FromResult(_reader.MoveToContent()); }); - _skipFunc = new Func((thisPtr) => { _reader.Skip(); return Task.CompletedTask; }); - _readFunc = new Func>((thisPtr) => { return Task.FromResult(_reader.Read()); }); - _readInnerXmlFunc = new Func>((thisPtr) => { return Task.FromResult(_reader.ReadInnerXml()); }); - } - - public override XmlNodeType NodeType - { - get - { - return _reader.NodeType; - } - } - - public override string LocalName - { - get - { - return _reader.LocalName; - } - } - - public override string NamespaceURI - { - get - { - return _reader.NamespaceURI; - } - } - - public override string Prefix - { - get - { - return _reader.Prefix; - } - } - - public override string Value - { - get - { - return _reader.Value; - } - } - - public override int Depth - { - get - { - return _reader.Depth; - } - } - - public override string BaseURI - { - get - { - return _reader.BaseURI; - } - } - - public override bool IsEmptyElement - { - get - { - return _reader.IsEmptyElement; - } - } - - public override int AttributeCount - { - get - { - return _reader.AttributeCount; - } - } - - public override bool EOF - { - get - { - return _reader.EOF; - } - } - - public override ReadState ReadState - { - get - { - return _reader.ReadState; - } - } - - public override XmlNameTable NameTable - { - get - { - return _reader.NameTable; - } - } - - public override string GetAttribute(string name) - { - return _reader.GetAttribute(name); - } - - public override string GetAttribute(string name, string namespaceURI) - { - return _reader.GetAttribute(name, namespaceURI); - } - - public override string GetAttribute(int i) - { - return _reader.GetAttribute(i); - } - - public override string LookupNamespace(string prefix) - { - return _reader.LookupNamespace(prefix); - } - - public override bool MoveToAttribute(string name) - { - return _reader.MoveToAttribute(name); - } - - public override bool MoveToAttribute(string name, string ns) - { - return _reader.MoveToAttribute(name, ns); - } - - public override bool MoveToElement() - { - return _reader.MoveToElement(); - } - - public override bool MoveToFirstAttribute() - { - return _reader.MoveToFirstAttribute(); - } - - public override bool MoveToNextAttribute() - { - return _reader.MoveToNextAttribute(); - } - - public override bool Read() - { - return _reader.Read(); - } - - public override bool ReadAttributeValue() - { - return _reader.ReadAttributeValue(); - } - - public override void ResolveEntity() - { - _reader.ResolveEntity(); - } - - public override Task GetValueAsync() - { - return _getValueFunc(this); - } - - public override Task MoveToContentAsync() - { - return _moveToContentFunc(this); - } - - public override Task SkipAsync() - { - return _skipFunc(this); - } - - public override Task ReadAsync() - { - return _readFunc(this); - } - - public override Task ReadInnerXmlAsync() - { - return _readInnerXmlFunc(this); - } - - public static async Task WriteNodeAsync(XmlDictionaryWriter writer, XmlReader reader, bool defattr) - { - char[] writeNodeBuffer = null; - const int WriteNodeBufferSize = 1024; - - if (null == reader) - { - throw new ArgumentNullException(nameof(reader)); - } - - bool canReadChunk = reader.CanReadValueChunk; - int d = reader.NodeType == XmlNodeType.None ? -1 : reader.Depth; - do - { - switch (reader.NodeType) - { - case XmlNodeType.Element: - writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI); - writer.WriteAttributes(reader, defattr); - if (reader.IsEmptyElement) - { - writer.WriteEndElement(); - break; - } - break; - case XmlNodeType.Text: - if (canReadChunk) - { - if (writeNodeBuffer == null) - { - writeNodeBuffer = new char[WriteNodeBufferSize]; - } - int read; - while ((read = reader.ReadValueChunk(writeNodeBuffer, 0, WriteNodeBufferSize)) > 0) - { - writer.WriteChars(writeNodeBuffer, 0, read); - } - } - else - { - writer.WriteString(await reader.GetValueAsync()); - } - break; - - case XmlNodeType.Whitespace: - case XmlNodeType.SignificantWhitespace: - writer.WriteWhitespace(await reader.GetValueAsync()); - break; - - case XmlNodeType.CDATA: - writer.WriteCData(await reader.GetValueAsync()); - break; - - case XmlNodeType.EntityReference: - writer.WriteEntityRef(reader.Name); - break; - - case XmlNodeType.XmlDeclaration: - case XmlNodeType.ProcessingInstruction: - writer.WriteProcessingInstruction(reader.Name, await reader.GetValueAsync()); - break; - - case XmlNodeType.DocumentType: - writer.WriteDocType(reader.Name, reader.GetAttribute("PUBLIC"), reader.GetAttribute("SYSTEM"), await reader.GetValueAsync()); - break; - - case XmlNodeType.Comment: - writer.WriteComment(await reader.GetValueAsync()); - break; - - case XmlNodeType.EndElement: - writer.WriteFullEndElement(); - break; - } - } while (await reader.ReadAsync() && (d < reader.Depth || (d == reader.Depth && reader.NodeType == XmlNodeType.EndElement))); - } - } - - internal static class XmlReaderExtensions - { - private const uint IsTextualNodeBitmap = 0x6018; // 00 0110 0000 0001 1000 - - public static async Task ReadStartElementAsync(this XmlReader reader) - { - if (await reader.MoveToContentAsync() != XmlNodeType.Element) - { - throw new InvalidOperationException(reader.NodeType.ToString() + " is an invalid XmlNodeType"); - } - - await reader.ReadAsync(); - } - - public static async Task ReadElementStringAsync(this XmlReader reader) - { - string result = string.Empty; - - if (await reader.MoveToContentAsync() != XmlNodeType.Element) - { - //throw new XmlException(Res.Xml_InvalidNodeType, this.NodeType.ToString(), this as IXmlLineInfo); - throw new InvalidOperationException(reader.NodeType.ToString() + " is an invalid XmlNodeType"); - } - if (!reader.IsEmptyElement) - { - await reader.ReadAsync(); - result = reader.ReadString(); - if (reader.NodeType != XmlNodeType.EndElement) - { - throw new XmlException(); - throw new XmlException(reader.NodeType.ToString() + " is an invalid XmlNodeType"); - //throw new XmlException(Res.Xml_UnexpectedNodeInSimpleContent, new string[] { this.NodeType.ToString(), "ReadElementString" }, this as IXmlLineInfo); - } - await reader.ReadAsync(); - } - else - { - await reader.ReadAsync(); - } - return result; - } - - public static async Task ReadStringAsync(this XmlReader reader) - { - if (reader.ReadState != ReadState.Interactive) - { - return string.Empty; - } - reader.MoveToElement(); - if (reader.NodeType == XmlNodeType.Element) - { - if (reader.IsEmptyElement) - { - return string.Empty; - } - else if (!await reader.ReadAsync()) - { - throw new XmlException(reader.NodeType.ToString() + " is an invalid XmlNodeType"); - //throw new InvalidOperationException(Res.GetString(Res.Xml_InvalidOperation)); - } - if (reader.NodeType == XmlNodeType.EndElement) - { - return string.Empty; - } - } - string result = string.Empty; - while (IsTextualNode(reader.NodeType)) - { - result += await reader.GetValueAsync(); - if (!await reader.ReadAsync()) - { - break; - } - } - return result; - } - - static internal bool IsTextualNode(XmlNodeType nodeType) - { -#if DEBUG - // This code verifies IsTextualNodeBitmap mapping of XmlNodeType to a bool specifying - // whether the node is 'textual' = Text, CDATA, Whitespace or SignificantWhitespace. - Debug.Assert(0 == (IsTextualNodeBitmap & (1 << (int)XmlNodeType.None))); - Debug.Assert(0 == (IsTextualNodeBitmap & (1 << (int)XmlNodeType.Element))); - Debug.Assert(0 == (IsTextualNodeBitmap & (1 << (int)XmlNodeType.Attribute))); - Debug.Assert(0 != (IsTextualNodeBitmap & (1 << (int)XmlNodeType.Text))); - Debug.Assert(0 != (IsTextualNodeBitmap & (1 << (int)XmlNodeType.CDATA))); - Debug.Assert(0 == (IsTextualNodeBitmap & (1 << (int)XmlNodeType.EntityReference))); - Debug.Assert(0 == (IsTextualNodeBitmap & (1 << (int)XmlNodeType.Entity))); - Debug.Assert(0 == (IsTextualNodeBitmap & (1 << (int)XmlNodeType.ProcessingInstruction))); - Debug.Assert(0 == (IsTextualNodeBitmap & (1 << (int)XmlNodeType.Comment))); - Debug.Assert(0 == (IsTextualNodeBitmap & (1 << (int)XmlNodeType.Document))); - Debug.Assert(0 == (IsTextualNodeBitmap & (1 << (int)XmlNodeType.DocumentType))); - Debug.Assert(0 == (IsTextualNodeBitmap & (1 << (int)XmlNodeType.DocumentFragment))); - Debug.Assert(0 == (IsTextualNodeBitmap & (1 << (int)XmlNodeType.Notation))); - Debug.Assert(0 != (IsTextualNodeBitmap & (1 << (int)XmlNodeType.Whitespace))); - Debug.Assert(0 != (IsTextualNodeBitmap & (1 << (int)XmlNodeType.SignificantWhitespace))); - Debug.Assert(0 == (IsTextualNodeBitmap & (1 << (int)XmlNodeType.EndElement))); - Debug.Assert(0 == (IsTextualNodeBitmap & (1 << (int)XmlNodeType.EndEntity))); - Debug.Assert(0 == (IsTextualNodeBitmap & (1 << (int)XmlNodeType.XmlDeclaration))); -#endif - return 0 != (IsTextualNodeBitmap & (1 << (int)nodeType)); - } - - public static async Task ReadEndElementAsync(this XmlReader reader) - { - if (await reader.MoveToContentAsync() != XmlNodeType.EndElement) - { - throw new XmlException(reader.NodeType.ToString() + " is an invalid XmlNodeType"); - //throw new XmlException(Res.Xml_InvalidNodeType, this.NodeType.ToString(), this as IXmlLineInfo); - } - await reader.ReadAsync(); - } - - public static async Task ReadStartElementAsync(this XmlReader reader, string localname, string ns) - { - if (await reader.MoveToContentAsync() != XmlNodeType.Element) - { - //throw new XmlException(Res.Xml_InvalidNodeType, this.NodeType.ToString(), this as IXmlLineInfo); - throw new XmlException(reader.NodeType.ToString() + " is an invalid XmlNodeType"); - } - if (reader.LocalName == localname && reader.NamespaceURI == ns) - { - await reader.ReadAsync(); - } - else - { - throw new XmlException(reader.NodeType.ToString() + " is an invalid XmlNodeType"); - //throw new XmlException(Res.Xml_ElementNotFoundNs, new string[2] { localname, ns }, this as IXmlLineInfo); - } - } - - public static async Task IsStartElementAsync(this XmlReader reader) - { - return await reader.MoveToContentAsync() == XmlNodeType.Element; - } - - public static async Task IsStartElementAsync(this XmlReader reader, string localname, string ns) - { - return (await reader.MoveToContentAsync() == XmlNodeType.Element) && (reader.LocalName == localname && reader.NamespaceURI == ns); - } - - private static async Task ReadStartElementAsync(this XmlReader reader, string name) - { - if (await reader.MoveToContentAsync() != XmlNodeType.Element) - { - throw new InvalidOperationException(reader.NodeType.ToString() + " is an invalid XmlNodeType"); - } - if (reader.Name == name) - { - await reader.ReadAsync(); - } - else - { - throw new InvalidOperationException("name doesn\u2019t match"); - } - } - - private static async Task IsStartElementAsync(this XmlReader reader, string name) - { - return await reader.MoveToContentAsync() == XmlNodeType.Element && reader.Name == name; - } - - private static async Task ReadElementStringAsync(this XmlReader reader, string name) - { - string result = string.Empty; - - if (await reader.MoveToContentAsync() != XmlNodeType.Element) - { - throw new XmlException("InvalidNodeType"); - } - if (reader.Name != name) - { - throw new XmlException("ElementNotFound"); - } - - if (!reader.IsEmptyElement) - { - result = await ReadStringAsync(reader); - - if (reader.NodeType != XmlNodeType.EndElement) - { - throw new XmlException("InvalidNodeType"); - } - await reader.ReadAsync(); - } - else - { - await reader.ReadAsync(); - } - return result; - } - - private static async Task ReadElementStringAsync(XmlReader reader, string localname, string ns) - { - string result = string.Empty; - - if (await reader.MoveToContentAsync() != XmlNodeType.Element) - { - throw new XmlException("InvalidNodeType"); - } - - if (reader.LocalName != localname || reader.NamespaceURI != ns) - { - throw new XmlException("ElementNotFound"); - } - - if (!reader.IsEmptyElement) - { - result = await ReadStringAsync(reader); - - if (reader.NodeType != XmlNodeType.EndElement) - { - throw new XmlException("InvalidNodeType"); - } - await reader.ReadAsync(); - } - else - { - await reader.ReadAsync(); - } - return result; - } - } -} diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/XmlSyndicationContent.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/XmlSyndicationContent.cs index a50234921a..47fbc2025a 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/XmlSyndicationContent.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/XmlSyndicationContent.cs @@ -2,16 +2,16 @@ // 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.Runtime; +using System.Runtime.Serialization; +using System.Xml; +using System.Xml.Serialization; +using System.Runtime.CompilerServices; +using System.Diagnostics; + namespace System.ServiceModel.Syndication { - using System; - using System.Diagnostics; - using System.Runtime.CompilerServices; - using System.Runtime.Serialization; - using System.Threading.Tasks; - using System.Xml; - using System.Xml.Serialization; - + // NOTE: This class implements Clone so if you add any members, please update the copy ctor public class XmlSyndicationContent : SyndicationContent { private XmlBuffer _contentBuffer; @@ -25,7 +25,7 @@ namespace System.ServiceModel.Syndication { if (reader == null) { - throw new ArgumentNullException(nameof(reader)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); } SyndicationFeedFormatter.MoveToStartElement(reader); if (reader.HasAttributes) @@ -72,7 +72,7 @@ namespace System.ServiceModel.Syndication { if (extension == null) { - throw new ArgumentNullException(nameof(extension)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("extension"); } _type = string.IsNullOrEmpty(type) ? Atom10Constants.XmlMediaType : type; _extension = extension; @@ -83,7 +83,7 @@ namespace System.ServiceModel.Syndication { if (source == null) { - throw new ArgumentNullException(nameof(source)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("source"); } _contentBuffer = source._contentBuffer; _extension = source._extension; @@ -108,18 +108,18 @@ namespace System.ServiceModel.Syndication return new XmlSyndicationContent(this); } - public async Task GetReaderAtContent() + public XmlDictionaryReader GetReaderAtContent() { - await EnsureContentBufferAsync(); + EnsureContentBuffer(); return _contentBuffer.GetReader(0); } - public Task ReadContent() + public TContent ReadContent() { return ReadContent((DataContractSerializer)null); } - public async Task ReadContent(XmlObjectSerializer dataContractSerializer) + public TContent ReadContent(XmlObjectSerializer dataContractSerializer) { if (dataContractSerializer == null) { @@ -127,7 +127,7 @@ namespace System.ServiceModel.Syndication } if (_extension != null) { - return await _extension.GetObject(dataContractSerializer); + return _extension.GetObject(dataContractSerializer); } else { @@ -141,7 +141,7 @@ namespace System.ServiceModel.Syndication } } - public Task ReadContent(XmlSerializer serializer) + public TContent ReadContent(XmlSerializer serializer) { if (serializer == null) { @@ -158,8 +158,7 @@ namespace System.ServiceModel.Syndication { // skip past the content element reader.ReadStartElement(); - return Task.FromResult((TContent)serializer.Deserialize(reader)); - //return (TContent)serializer.Deserialize(reader); + return (TContent)serializer.Deserialize(reader); } } } @@ -169,11 +168,11 @@ namespace System.ServiceModel.Syndication { if (writer == null) { - throw new ArgumentNullException(nameof(writer)); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); } if (_extension != null) { - _extension.WriteToAsync(writer).GetAwaiter().GetResult(); + _extension.WriteTo(writer); } else if (_contentBuffer != null) { @@ -192,14 +191,14 @@ namespace System.ServiceModel.Syndication } } - private async Task EnsureContentBufferAsync() + private void EnsureContentBuffer() { if (_contentBuffer == null) { XmlBuffer tmp = new XmlBuffer(int.MaxValue); using (XmlDictionaryWriter writer = tmp.OpenSection(XmlDictionaryReaderQuotas.Max)) { - await this.WriteToAsync(writer, Atom10Constants.ContentTag, Atom10Constants.Atom10Namespace); + this.WriteTo(writer, Atom10Constants.ContentTag, Atom10Constants.Atom10Namespace); } tmp.CloseSection(); tmp.Close(); diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/XmlWriterWrapper.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/XmlWriterWrapper.cs deleted file mode 100644 index 232d25161e..0000000000 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/XmlWriterWrapper.cs +++ /dev/null @@ -1,271 +0,0 @@ -// 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.Threading.Tasks; -using System.Xml; - -namespace System.ServiceModel.Syndication -{ - internal class XmlWriterWrapper : XmlWriter - { - private XmlWriter _writer; - - private Func _writeStringFunc; - private Func _writeStartElementFunc2; - private Func _writeEndElementFunc; - private Func _writeAttributeStringFunc2; - private Func _writeAttributeStringFunc3; - private Func _writeAttributeStringFunc4; - private Func _writeNodeFunc; - - - private void InitAsync() - { - _writeStringFunc = new Func((thisPtr, text) => { return thisPtr._writer.WriteStringAsync(text); }); - _writeStartElementFunc2 = new Func((thisPtr, localName, ns) => { return _writer.WriteStartElementAsync("", localName, ns); }); - _writeEndElementFunc = new Func((thisPtr) => { return thisPtr._writer.WriteEndElementAsync(); }); - _writeAttributeStringFunc2 = new Func((thisPtr, localname, value) => { return thisPtr._writer.WriteAttributeStringAsync("", localname, "", value); }); - _writeAttributeStringFunc3 = new Func((thisPtr, localName, ns, value) => { return thisPtr._writer.WriteAttributeStringAsync("", localName, ns, value); }); - _writeAttributeStringFunc4 = new Func((thisPtr, prefix, localName, ns, value) => { return thisPtr._writer.WriteAttributeStringAsync(prefix, localName, ns, value); }); - _writeNodeFunc = new Func((thisPtr, reader, defattr) => { return thisPtr._writer.WriteNodeAsync(reader, defattr); }); - } - - private void Init() - { - _writeStringFunc = new Func((thisPtr, text) => { thisPtr._writer.WriteString(text); return Task.CompletedTask; }); - _writeStartElementFunc2 = new Func((thisPtr, localName, ns) => { _writer.WriteStartElement(localName, ns); return Task.CompletedTask; }); - _writeEndElementFunc = new Func((thisPtr) => { thisPtr._writer.WriteEndElement(); return Task.CompletedTask; }); - _writeAttributeStringFunc2 = new Func((thisPtr, localname, value) => { thisPtr._writer.WriteAttributeString("", localname, "", value); return Task.CompletedTask; }); - _writeAttributeStringFunc3 = new Func((thisPtr, localName, ns, value) => { thisPtr._writer.WriteAttributeString(localName, ns, value); return Task.CompletedTask; }); - _writeAttributeStringFunc4 = new Func((thisPtr, prefix, localName, ns, value) => { thisPtr._writer.WriteAttributeString(prefix, localName, ns, value); return Task.CompletedTask; }); - _writeNodeFunc = new Func((thisPtr, reader, defattr) => { thisPtr._writer.WriteNode(reader, defattr); return Task.CompletedTask; }); - } - - public static XmlWriter CreateFromWriter(XmlWriter writer) - { - if (writer is XmlWriterWrapper || writer.Settings.Async) - { - return writer; - } - - return new XmlWriterWrapper(writer); - } - - - public XmlWriterWrapper(XmlWriter writer) - { - if (writer == null) - throw new ArgumentNullException(nameof(writer)); - - _writer = writer; - - if (_writer.Settings.Async) - { - InitAsync(); - } - else - { - Init(); - } - } - - // wrapper methods - - public override Task WriteNodeAsync(XmlReader reader, bool defattr) - { - return _writeNodeFunc(this, reader, defattr); - } - - public override Task WriteStringAsync(string text) - { - return _writeStringFunc(this, text); - } - - public override Task WriteStartElementAsync(string prefix, string localName, string ns) - { - _writer.WriteStartElement(prefix, localName, ns); - return Task.CompletedTask; - } - - public override Task WriteEndElementAsync() - { - return _writeEndElementFunc(this); - } - - public override Task WriteAttributesAsync(XmlReader reader, bool defattr) - { - _writer.WriteAttributes(reader, defattr); - return Task.CompletedTask; - } - - // inherited methods - public override WriteState WriteState - { - get - { - return _writer.WriteState; - } - } - - public override void Flush() - { - _writer.Flush(); - } - - public override string LookupPrefix(string ns) - { - return _writer.LookupPrefix(ns); - } - - public override void WriteBase64(byte[] buffer, int index, int count) - { - _writer.WriteBase64(buffer, index, count); - } - - public override void WriteCData(string text) - { - _writer.WriteCData(text); - } - - public override void WriteCharEntity(char ch) - { - _writer.WriteCharEntity(ch); - } - - public override void WriteChars(char[] buffer, int index, int count) - { - _writer.WriteChars(buffer, index, count); - } - - public override void WriteComment(string text) - { - _writer.WriteComment(text); - } - - public override void WriteDocType(string name, string pubid, string sysid, string subset) - { - _writer.WriteDocType(name, pubid, sysid, subset); - } - - public override void WriteEndAttribute() - { - _writer.WriteEndAttribute(); - } - - public override void WriteEndDocument() - { - _writer.WriteEndDocument(); - } - - public override void WriteEndElement() - { - _writer.WriteEndElement(); - } - - public override void WriteEntityRef(string name) - { - _writer.WriteEntityRef(name); - } - - public override void WriteFullEndElement() - { - _writer.WriteFullEndElement(); - } - - public override void WriteProcessingInstruction(string name, string text) - { - _writer.WriteProcessingInstruction(name, text); - } - - public override void WriteRaw(char[] buffer, int index, int count) - { - _writer.WriteRaw(buffer, index, count); - } - - public override void WriteRaw(string data) - { - _writer.WriteRaw(data); - } - - public override void WriteStartAttribute(string prefix, string localName, string ns) - { - _writer.WriteStartAttribute(prefix, localName, ns); - } - - public override void WriteStartDocument() - { - _writer.WriteStartDocument(); - } - - public override void WriteStartDocument(bool standalone) - { - _writer.WriteStartDocument(standalone); - } - - public override void WriteStartElement(string prefix, string localName, string ns) - { - _writer.WriteStartElement(prefix, localName, ns); - } - - public override void WriteString(string text) - { - _writer.WriteString(text); - } - - public override void WriteSurrogateCharEntity(char lowChar, char highChar) - { - _writer.WriteSurrogateCharEntity(lowChar, highChar); - } - - public override void WriteWhitespace(string ws) - { - _writer.WriteWhitespace(ws); - } - } - - internal static class XmlWriterExtensions - { - public static Task WriteStartElementAsync(this XmlWriter writer, string localName) - { - return writer.WriteStartElementAsync(null, localName, (string)null); - } - - public static Task WriteStartElementAsync(this XmlWriter writer, string localName, string ns) - { - return writer.WriteStartElementAsync(null, localName, ns); - } - - public static Task WriteElementStringAsync(this XmlWriter writer, string localName, string value) - { - return writer.WriteElementStringAsync(null, localName, null, value); - } - - public static Task WriteElementStringAsync(this XmlWriter writer, string localName, string ns, string value) - { - return writer.WriteElementStringAsync(null, localName, ns, value); - } - - public static Task WriteAttributeStringAsync(this XmlWriter writer, string localName, string ns, string value) - { - return writer.InternalWriteAttributeStringAsync(null, localName, ns, value); - } - - public static Task WriteAttributeStringAsync(this XmlWriter writer, string localName, string value) - { - return writer.InternalWriteAttributeStringAsync(null, localName, null, value); - } - - public static Task InternalWriteAttributeStringAsync(this XmlWriter writer, string prefix, string localName, string ns, string value) - { - if (writer is XmlWriterWrapper) - { - writer.WriteAttributeString(prefix, localName, ns, value); - return Task.CompletedTask; - } - - return writer.WriteAttributeStringAsync(prefix, localName, ns, value); - } - } -} diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/XmlBuffer.cs b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/XmlBuffer.cs index 6fee3f374b..2f6b23c0cb 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/XmlBuffer.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/XmlBuffer.cs @@ -2,13 +2,13 @@ // 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.Collections.Generic; +using System.IO; +using System.Xml; + namespace System.ServiceModel { - using System; - using System.Collections.Generic; - using System.IO; - using System.Xml; - internal class XmlBuffer { private List
_sections; diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/BasicScenarioTests.cs b/external/corefx/src/System.ServiceModel.Syndication/tests/BasicScenarioTests.cs index c4c6a918f5..f6c858c0a2 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/tests/BasicScenarioTests.cs +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/BasicScenarioTests.cs @@ -8,13 +8,12 @@ using System.Text; using System.ServiceModel.Syndication; using System.Xml; using System.IO; -using System.Threading.Tasks; -using System.Threading; using Xunit; +using System.Linq; namespace System.ServiceModel.Syndication.Tests { - public static class BasicScenarioTests + public static partial class BasicScenarioTests { [Fact] public static void SyndicationFeed_CreateNewFeed() @@ -31,8 +30,7 @@ namespace System.ServiceModel.Syndication.Tests Rss20FeedFormatter rssf = new Rss20FeedFormatter(sf); // *** EXECUTE *** \\ - CancellationToken ct = new CancellationToken(); - rssf.WriteToAsync(xmlw, ct).GetAwaiter().GetResult(); + rssf.WriteTo(xmlw); xmlw.Close(); // *** VALIDATE *** \\ @@ -56,13 +54,12 @@ namespace System.ServiceModel.Syndication.Tests XmlReader xmlr = XmlReader.Create(@"SimpleRssFeed.xml"); SyndicationFeed sf = SyndicationFeed.Load(xmlr); Assert.True(sf != null); - CancellationToken ct = new CancellationToken(); // *** EXECUTE *** \\ //Write the same feed that was read. XmlWriter xmlw = XmlWriter.Create(path); Rss20FeedFormatter atomFeed = new Rss20FeedFormatter(sf); - atomFeed.WriteToAsync(xmlw, ct).GetAwaiter().GetResult(); + atomFeed.WriteTo(xmlw); xmlw.Close(); // *** VALIDATE *** \\ @@ -76,7 +73,7 @@ namespace System.ServiceModel.Syndication.Tests } [Fact] - public static void SyndicationFeed_Load_Write_RSS_Feed_Async() + public static void SyndicationFeed_Load_Write_RSS_Feed_() { string path = Path.GetTempFileName(); @@ -84,26 +81,16 @@ namespace System.ServiceModel.Syndication.Tests { // *** SETUP *** \\\ XmlReaderSettings settingsReader = new XmlReaderSettings(); - settingsReader.Async = true; XmlReader xmlr = XmlReader.Create(@"rssSpecExample.xml", settingsReader); - SyndicationFeed sf; - Task rss = null; - CancellationToken ct = new CancellationToken(); - rss = SyndicationFeed.LoadAsync(xmlr, ct); - - Task.WhenAll(rss); - sf = rss.Result; + SyndicationFeed sf = SyndicationFeed.Load(xmlr); Assert.True(sf != null); // *** EXECUTE *** \\ //Write the same feed that was read. XmlWriterSettings settingsWriter = new XmlWriterSettings(); - settingsWriter.Async = true; XmlWriter xmlw = XmlWriter.Create(path, settingsWriter); Rss20FeedFormatter atomFeed = new Rss20FeedFormatter(sf); - Task write = atomFeed.WriteToAsync(xmlw, ct); - - Task.WhenAll(write); + atomFeed.WriteTo(xmlw); xmlw.Close(); @@ -126,17 +113,15 @@ namespace System.ServiceModel.Syndication.Tests { // *** SETUP *** \\\ XmlReaderSettings setting = new XmlReaderSettings(); - setting.Async = true; XmlReader xmlr = XmlReader.Create(@"SimpleAtomFeed.xml", setting); SyndicationFeed sf = SyndicationFeed.Load(xmlr); Assert.True(sf != null); - CancellationToken ct = new CancellationToken(); // *** EXECUTE *** \\ //Write the same feed that was read. XmlWriter xmlw = XmlWriter.Create(path); Atom10FeedFormatter atomFeed = new Atom10FeedFormatter(sf); - atomFeed.WriteToAsync(xmlw, ct).GetAwaiter().GetResult(); + atomFeed.WriteTo(xmlw); xmlw.Close(); // *** VALIDATE *** \\ @@ -150,7 +135,7 @@ namespace System.ServiceModel.Syndication.Tests } [Fact] - public static void SyndicationFeed_Load_Write_Atom_Feed_Async() + public static void SyndicationFeed_Load_Write_Atom_Feed_() { string path = Path.GetTempFileName(); @@ -158,23 +143,17 @@ namespace System.ServiceModel.Syndication.Tests { // *** SETUP *** \\\ XmlReaderSettings readerSettings = new XmlReaderSettings(); - readerSettings.Async = true; XmlReader xmlr = XmlReader.Create(@"atom_spec_example.xml", readerSettings); - CancellationToken ct = new CancellationToken(); - Task rss = SyndicationFeed.LoadAsync(xmlr, ct); - SyndicationFeed sf = rss.Result; + SyndicationFeed sf = SyndicationFeed.Load(xmlr); Assert.True(sf != null); // *** EXECUTE *** \\ //Write the same feed that was read. XmlWriterSettings writerSettings = new XmlWriterSettings(); - writerSettings.Async = true; XmlWriter xmlw = XmlWriter.Create(path, writerSettings); Atom10FeedFormatter atomFeed = new Atom10FeedFormatter(sf); - Task write = atomFeed.WriteToAsync(xmlw, ct); - - Task.WhenAll(write); + atomFeed.WriteTo(xmlw); xmlw.Close(); // *** VALIDATE *** \\ @@ -197,7 +176,6 @@ namespace System.ServiceModel.Syndication.Tests { // *** SETUP *** \\ SyndicationFeed feed = new SyndicationFeed("Contoso News", "
Most recent news from Contoso
", new Uri("http://www.Contoso.com/news"), "123FeedID", DateTime.Now); - CancellationToken ct = new CancellationToken(); //Add an author SyndicationPerson author = new SyndicationPerson("jerry@Contoso.com"); @@ -213,15 +191,11 @@ namespace System.ServiceModel.Syndication.Tests //add an image feed.ImageUrl = new Uri("http://2.bp.blogspot.com/-NA5Jb-64eUg/URx8CSdcj_I/AAAAAAAAAUo/eCx0irI0rq0/s1600/bg_Contoso_logo3-20120824073001907469-620x349.jpg"); - //feed.ImageTitle = new TextSyndicationContent("Titulo loco"); feed.BaseUri = new Uri("http://mypage.com"); // Write to XML > rss - XmlWriterSettings settings = new XmlWriterSettings(); - settings.Async = true; - XmlWriter xmlwRss = XmlWriter.Create(RssPath, settings); Rss20FeedFormatter rssff = new Rss20FeedFormatter(feed); @@ -232,12 +206,10 @@ namespace System.ServiceModel.Syndication.Tests // *** EXECUTE *** \\ - Task rss = rssff.WriteToAsync(xmlwRss, ct); - Task.WaitAll(rss); - + rssff.WriteTo(xmlwRss); xmlwRss.Close(); - atomf.WriteToAsync(xmlwAtom, ct).GetAwaiter().GetResult(); ; + atomf.WriteTo(xmlwAtom); ; xmlwAtom.Close(); // *** ASSERT *** \\ @@ -253,444 +225,413 @@ namespace System.ServiceModel.Syndication.Tests } [Fact] - public static void SyndicationFeed_RSS20_Load_customImageDataInFeed() + public static void SyndicationFeed_Load_Rss() { - // *** SETUP *** \\ - XmlReader reader = XmlReader.Create(@"RssFeedWithCustomImageName.xml"); - - // *** EXECUTE *** \\ - SyndicationFeed sf = SyndicationFeed.Load(reader); - - // *** ASSERT *** \\ - Assert.True("The title is not the same to the original one" == sf.ImageTitle.Text); - Assert.True(sf.ImageLink.AbsoluteUri != sf.Links[0].GetAbsoluteUri().AbsoluteUri); - - // *** CLEANUP *** \\ - reader.Close(); - } - - [Fact] - public static void SyndicationFeed_RSS20_Write_customImageDataInFeed() - { - // *** SETUP *** \\ - SyndicationFeed sf = new SyndicationFeed(); - string feedTitle = "Feed title"; - string imageTitle = "Image title"; - string resultPath = Path.GetTempFileName(); - - sf.Title = new TextSyndicationContent(feedTitle); - sf.ImageTitle = new TextSyndicationContent(imageTitle); - sf.ImageLink = new Uri("http://myimage.com"); - sf.ImageUrl = new Uri("http://www.myownimagesrc.com"); - XmlWriter writer = XmlWriter.Create(resultPath); - Rss20FeedFormatter rssff = sf.GetRss20Formatter(); - CancellationToken ct = new CancellationToken(); - - try - { - // *** EXECUTE *** \\ - rssff.WriteToAsync(writer, ct).GetAwaiter().GetResult(); ; - writer.Close(); - - // *** ASSERT *** \\ - Assert.True(File.Exists(resultPath)); - } - finally - { - // *** CLEANUP *** \\ - File.Delete(resultPath); - } - } - - [Fact] - public static async Task SyndicationFeed_LoadAsync_Rss() - { - // *** SETUP *** \\ XmlReaderSettings setting = new XmlReaderSettings(); - setting.Async = true; - XmlReader reader = null; - Task rss = null; - CancellationToken ct = new CancellationToken(); - - try + using (XmlReader reader = XmlReader.Create(@"rssSpecExample.xml", setting)) { - // *** EXECUTE *** \\ - reader = XmlReader.Create(@"rssSpecExample.xml", setting); - rss = SyndicationFeed.LoadAsync(reader, ct); - await Task.WhenAll(rss); - - // *** ASSERT *** \\ - } - finally - { - // *** CLEANUP *** \\ - Assert.True(rss.Result.Items != null); - reader.Close(); + SyndicationFeed rss = SyndicationFeed.Load(reader); + Assert.True(rss.Items != null); } } [Fact] - public static async Task SyndicationFeed_LoadAsync_Atom() + public static void SyndicationFeed_Load_Atom() { - // *** SETUP *** \\ XmlReaderSettings setting = new XmlReaderSettings(); - setting.Async = true; - XmlReader reader = null; - CancellationToken ct = new CancellationToken(); - - try + using (XmlReader reader = XmlReader.Create(@"atom_spec_example.xml", setting)) { - reader = XmlReader.Create(@"atom_spec_example.xml", setting); - // *** EXECUTE *** \\ - Task atom = SyndicationFeed.LoadAsync(reader, ct); - await Task.WhenAll(atom); - // *** ASSERT *** \\ - Assert.True(atom.Result.Items != null); - } - finally - { - // *** CLEANUP *** \\ - reader.Close(); + SyndicationFeed atom = SyndicationFeed.Load(reader); + Assert.True(atom.Items != null); } } [Fact] public static void SyndicationFeed_Rss_TestDisjointItems() { - // *** SETUP *** \\ - XmlReader reader = XmlReader.Create(@"RssDisjointItems.xml"); - - try + using (XmlReader reader = XmlReader.Create(@"RssDisjointItems.xml")) { // *** EXECUTE *** \\ SyndicationFeed sf = SyndicationFeed.Load(reader); // *** ASSERT *** \\ int count = 0; - foreach (var item in sf.Items) + foreach (SyndicationItem item in sf.Items) { count++; } Assert.True(count == 2); } - catch - { - // *** CLEANUP *** \\ - reader.Close(); - } } [Fact] public static void SyndicationFeed_Atom_TestDisjointItems() { - // *** SETUP *** \\ - XmlReader reader = XmlReader.Create(@"AtomDisjointItems.xml"); - - try + using (XmlReader reader = XmlReader.Create(@"AtomDisjointItems.xml")) { // *** EXECUTE *** \\ SyndicationFeed sf = SyndicationFeed.Load(reader); // *** ASSERT *** \\ int count = 0; - foreach (var item in sf.Items) + foreach (SyndicationItem item in sf.Items) { count++; } Assert.True(count == 2); } - finally - { - // *** CLEANUP *** \\ - reader.Close(); - } - } - - [Fact] - public static async Task SyndicationFeed_RSS_Optional_Documentation() - { - // *** SETUP *** \\ - XmlReaderSettings setting = new XmlReaderSettings(); - setting.Async = true; - XmlReader reader = null; - Task rss = null; - CancellationToken ct = new CancellationToken(); - - try - { - // *** EXECUTE *** \\ - reader = XmlReader.Create(@"rssSpecExample.xml", setting); - rss = SyndicationFeed.LoadAsync(reader, ct); - await Task.WhenAll(rss); - - // *** ASSERT *** \\ - Assert.True(rss.Result.Documentation.GetAbsoluteUri().ToString() == "http://blogs.law.harvard.edu/tech/rss"); - } - finally - { - // *** CLEANUP *** \\ - Assert.True(rss.Result.Items != null); - reader.Close(); - } - } - - [Fact] - public static async Task SyndicationFeed_RSS_Optional_TimeToLiveTag() - { - // *** SETUP *** \\ - XmlReaderSettings setting = new XmlReaderSettings(); - setting.Async = true; - XmlReader reader = null; - Task rss = null; - CancellationToken ct = new CancellationToken(); - - try - { - // *** EXECUTE *** \\ - reader = XmlReader.Create(@"rssSpecExample.xml", setting); - rss = SyndicationFeed.LoadAsync(reader, ct); - await Task.WhenAll(rss); - - // *** ASSERT *** \\ - Assert.True(rss.Result.TimeToLive == 60); - } - finally - { - // *** CLEANUP *** \\ - Assert.True(rss.Result.Items != null); - reader.Close(); - } - } - - [Fact] - public static async Task SyndicationFeed_RSS_Optional_SkipHours() - { - // *** SETUP *** \\ - XmlReaderSettings setting = new XmlReaderSettings(); - setting.Async = true; - XmlReader reader = null; - Task rss = null; - CancellationToken ct = new CancellationToken(); - - try - { - // *** EXECUTE *** \\ - reader = XmlReader.Create(@"rssSpecExample.xml", setting); - rss = SyndicationFeed.LoadAsync(reader, ct); - await Task.WhenAll(rss); - - // *** ASSERT *** \\ - Assert.True(rss.Result.SkipHours.Count == 3); - } - finally - { - // *** CLEANUP *** \\ - Assert.True(rss.Result.Items != null); - reader.Close(); - } - } - - [Fact] - public static async Task SyndicationFeed_RSS_Optional_SkipDays() - { - // *** SETUP *** \\ - XmlReaderSettings setting = new XmlReaderSettings(); - setting.Async = true; - XmlReader reader = null; - Task rss = null; - CancellationToken ct = new CancellationToken(); - - try - { - // *** EXECUTE *** \\ - reader = XmlReader.Create(@"rssSpecExample.xml", setting); - rss = SyndicationFeed.LoadAsync(reader, ct); - await Task.WhenAll(rss); - - // *** ASSERT *** \\ - Assert.True(rss.Result.SkipDays.Count == 2); - Assert.True(rss.Result.SkipDays[0] == "Saturday"); - Assert.True(rss.Result.SkipDays[1] == "Sunday"); - } - finally - { - // *** CLEANUP *** \\ - Assert.True(rss.Result.Items != null); - reader.Close(); - } - } - - [Fact] - public static async Task SyndicationFeed_RSS_Optional_TextInput() - { - // *** SETUP *** \\ - XmlReaderSettings setting = new XmlReaderSettings(); - setting.Async = true; - XmlReader reader = null; - Task rss = null; - CancellationToken ct = new CancellationToken(); - - try - { - // *** EXECUTE *** \\ - reader = XmlReader.Create(@"rssSpecExample.xml", setting); - rss = SyndicationFeed.LoadAsync(reader, ct); - await Task.WhenAll(rss); - - // *** ASSERT *** \\ - Assert.True(rss.Result.TextInput.Description == "Search Online"); - Assert.True(rss.Result.TextInput.title == "Search"); - Assert.True(rss.Result.TextInput.name == "input Name"); - Assert.True(rss.Result.TextInput.link.GetAbsoluteUri().ToString() == "http://www.contoso.no/search?"); - } - finally - { - // *** CLEANUP *** \\ - Assert.True(rss.Result.Items != null); - reader.Close(); - } - } - - [Fact] - public static async Task SyndicationFeed__Atom_Optional_Icon() - { - // *** SETUP *** \\ - XmlReaderSettings setting = new XmlReaderSettings(); - setting.Async = true; - XmlReader reader = null; - CancellationToken ct = new CancellationToken(); - - try - { - reader = XmlReader.Create(@"atom_spec_example.xml", setting); - // *** EXECUTE *** \\ - Task atom = SyndicationFeed.LoadAsync(reader, ct); - await Task.WhenAll(atom); - // *** ASSERT *** \\ - Assert.True(atom.Result.IconImage.AbsoluteUri == "https://avatars0.githubusercontent.com/u/9141961"); - } - finally - { - // *** CLEANUP *** \\ - reader.Close(); - } - } - - [Fact] - public static void SyndicationFeed_Rss_TestCustomParsing() - { - // *** SETUP *** \\ - Rss20FeedFormatter rssformatter = new Rss20FeedFormatter(); - - rssformatter.StringParser = (val, name, ns) => - { - Assert.False(string.IsNullOrEmpty(name)); - switch (name) - { - case "ttl": - case "hour": - return "5"; - case "link": - case "image": - case "url": - return "http://customparsedlink.com"; - case "title": - return "new title"; - default: - return "Custom Text"; - } - }; - - XmlReader reader = XmlReader.Create(@"rssSpecExample.xml"); - CancellationToken ct = new CancellationToken(); - - // *** EXECUTE *** \\ - Task task = SyndicationFeed.LoadAsync(reader, rssformatter, ct); - Task.WhenAll(task); - SyndicationFeed res = task.Result; - - // *** ASSERT *** \\ - Assert.True(res.Title.Text == "new title"); - foreach (int hour in res.SkipHours) - { - Assert.True(hour == 5); - } - } - - [Fact] - public static void SyndicationFeed_Atom_TestCustomParsing() - { - // *** SETUP *** \\ - Atom10FeedFormatter atomformatter = new Atom10FeedFormatter(); - - atomformatter.stringParser = (val, name, ns) => - { - Assert.False(string.IsNullOrEmpty(name)); - switch (name) - { - case Atom10Constants.IdTag: - return "No id!"; - case Atom10Constants.NameTag: - return "new name"; - case Atom10Constants.TitleTag: - return "new title"; - default: - return "Custom Text"; - } - }; - - XmlReader reader = XmlReader.Create(@"atom_spec_example.xml"); - CancellationToken ct = new CancellationToken(); - - // *** EXECUTE *** \\ - Task task = SyndicationFeed.LoadAsync(reader, atomformatter, ct); - Task.WhenAll(task); - SyndicationFeed res = task.Result; - - // *** ASSERT *** \\ - Assert.True(res.Id == "No id!"); - Assert.True(res.Title.Text == "new title"); - } - - [Fact] - public static void SyndicationFeed_Rss_TestWrongSkipDays() - { - // *** SETUP *** \\ - Rss20FeedFormatter rssformatter = new Rss20FeedFormatter(); - - XmlReader reader = XmlReader.Create(@"rssSpecExampleWrongSkipDays.xml"); - CancellationToken ct = new CancellationToken(); - - // *** EXECUTE *** \\ - Task task = SyndicationFeed.LoadAsync(reader, ct); - Task.WhenAll(task); - SyndicationFeed res = task.Result; - - // *** ASSERT *** \\ - Assert.True(res.SkipDays.Count == 2); - Assert.True(res.SkipDays[0] == "Saturday"); - Assert.True(res.SkipDays[1] == "Sunday"); } [Fact] public static void SyndicationFeed_Rss_WrongDateFormat() { // *** SETUP *** \\ - Rss20FeedFormatter rssformatter = new Rss20FeedFormatter(); - XmlReader reader = XmlReader.Create(@"rssSpecExampleWrongDateFormat.xml"); - CancellationToken ct = new CancellationToken(); // *** EXECUTE *** \\ - Task task = SyndicationFeed.LoadAsync(reader, ct); - Task.WhenAll(task); - SyndicationFeed res = task.Result; + SyndicationFeed res = SyndicationFeed.Load(reader); // *** ASSERT *** \\ - Assert.True(!res.LastUpdatedTime.Equals(new DateTimeOffset())); + Assert.True(res != null, "res was null."); + Assert.Equal(new DateTimeOffset(2016, 8, 23, 16, 8, 0, new TimeSpan(-4, 0, 0)), res.LastUpdatedTime); + Assert.True(res.Items != null, "res.Items was null."); + Assert.True(res.Items.Count() == 4, $"res.Items.Count() was not as expected. Expected: 4; Actual: {res.Items.Count()}"); + SyndicationItem[] items = res.Items.ToArray(); + DateTimeOffset dateTimeOffset; + Assert.Throws(() => dateTimeOffset = items[2].PublishDate); } + + [Fact] + public static void AtomEntryPositiveTest() + { + string file = @"brief-entry-noerror.xml"; + ReadWriteSyndicationItem(file, (itemObject) => new Atom10ItemFormatter(itemObject)); + } + + [Fact] + public static void AtomEntryPositiveTest_write() + { + string file = @"AtomEntryTest.xml"; + string serializeFilePath = Path.GetTempFileName(); + bool toDeletedFile = true; + + SyndicationItem item = new SyndicationItem("SyndicationFeed released for .net Core", "A lot of text describing the release of .net core feature", new Uri("http://contoso.com/news/path")); + item.Id = "uuid:43481a10-d881-40d1-adf2-99b438c57e21;id=1"; + item.LastUpdatedTime = new DateTimeOffset(Convert.ToDateTime("2017-10-11T11:25:55Z")).UtcDateTime; + + try + { + using (FileStream fileStream = new FileStream(serializeFilePath, FileMode.OpenOrCreate)) + { + using (XmlWriter writer = XmlDictionaryWriter.CreateTextWriter(fileStream)) + { + Atom10ItemFormatter f = new Atom10ItemFormatter(item); + f.WriteTo(writer); + } + } + + CompareHelper ch = new CompareHelper + { + Diff = new XmlDiff() + { + Option = XmlDiffOption.IgnoreComments | XmlDiffOption.IgnorePrefix | XmlDiffOption.IgnoreWhitespace | XmlDiffOption.IgnoreChildOrder | XmlDiffOption.IgnoreAttributeOrder + } + }; + + string diffNode = string.Empty; + if (!ch.Compare(file, serializeFilePath, out diffNode)) + { + toDeletedFile = false; + string errorMessage = $"The generated file was different from the baseline file:{Environment.NewLine}Baseline: {file}{Environment.NewLine}Actual: {serializeFilePath}{Environment.NewLine}Different Nodes:{Environment.NewLine}{diffNode}"; + Assert.True(false, errorMessage); + } + } + finally + { + if (toDeletedFile) + { + File.Delete(serializeFilePath); + } + } + } + + [Fact] + public static void AtomFeedPositiveTest() + { + string dataFile = @"atom_feeds.dat"; + List fileList = GetTestFilesForFeedTest(dataFile); + List allowableDifferences = GetAtomFeedPositiveTestAllowableDifferences(); + + foreach (string file in fileList) + { + ReadWriteSyndicationFeed(file, (feedObject) => new Atom10FeedFormatter(feedObject), allowableDifferences); + } + } + + [Fact] + public static void RssEntryPositiveTest() + { + string file = @"RssEntry.xml"; + ReadWriteSyndicationItem(file, (itemObject) => new Rss20ItemFormatter(itemObject)); + } + + [Fact] + public static void RssFeedPositiveTest() + { + string dataFile = @"rss_feeds.dat"; + List fileList = GetTestFilesForFeedTest(dataFile); + List allowableDifferences = GetRssFeedPositiveTestAllowableDifferences(); + + foreach (string file in fileList) + { + ReadWriteSyndicationFeed(file, (feedObject) => new Rss20FeedFormatter(feedObject), allowableDifferences); + } + } + + [Fact] + public static void DiffAtomNsTest() + { + string file = @"diff_atom_ns.xml"; + using (XmlReader reader = XmlReader.Create(file)) + { + Assert.Throws(typeof(XmlException), () => { SyndicationItem.Load(reader); }); + } + } + + [Fact] + public static void DiffRssNsTest() + { + string file = @"diff_rss_ns.xml"; + using (XmlReader reader = XmlReader.Create(file)) + { + Assert.Throws(typeof(XmlException), () => { SyndicationItem.Load(reader); }); + } + } + + [Fact] + public static void DiffRssVersionTest() + { + string file = @"diff_rss_version.xml"; + using (XmlReader reader = XmlReader.Create(file)) + { + Assert.Throws(typeof(XmlException), () => { SyndicationItem.Load(reader); }); + } + } + + [Fact] + public static void NoRssVersionTest() + { + string file = @"no_rss_version.xml"; + using (XmlReader reader = XmlReader.Create(file)) + { + Assert.Throws(typeof(XmlException), () => { SyndicationItem.Load(reader); }); + } + } + + private static void ReadWriteSyndicationItem(string file, Func itemFormatter) + { + string serializeFilePath = Path.GetTempFileName(); + bool toDeletedFile = true; + + try + { + SyndicationItem itemObjct = null; + using (FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read)) + { + using (XmlReader reader = XmlDictionaryReader.CreateTextReader(fileStream, XmlDictionaryReaderQuotas.Max)) + { + itemObjct = SyndicationItem.Load(reader); + } + } + + using (FileStream fileStream = new FileStream(serializeFilePath, FileMode.OpenOrCreate)) + { + using (XmlWriter writer = XmlDictionaryWriter.CreateTextWriter(fileStream)) + { + SyndicationItemFormatter formatter = itemFormatter(itemObjct); + formatter.WriteTo(writer); + } + } + + // compare file filePath and serializeFilePath + CompareHelper ch = new CompareHelper + { + Diff = new XmlDiff() + { + Option = XmlDiffOption.IgnoreComments | XmlDiffOption.IgnorePrefix | XmlDiffOption.IgnoreWhitespace | XmlDiffOption.IgnoreChildOrder | XmlDiffOption.IgnoreAttributeOrder + } + }; + + string diffNode = string.Empty; + if (!ch.Compare(file, serializeFilePath, out diffNode)) + { + toDeletedFile = false; + string errorMessage = $"The generated file was different from the baseline file:{Environment.NewLine}Baseline: {file}{Environment.NewLine}Actual: {serializeFilePath}{Environment.NewLine}Different Nodes:{Environment.NewLine}{diffNode}"; + Assert.True(false, errorMessage); + } + } + catch (Exception e) + { + Exception newEx = new Exception($"Failed File Name: {file}", e); + throw newEx; + } + finally + { + if (toDeletedFile) + { + File.Delete(serializeFilePath); + } + } + } + + private static void ReadWriteSyndicationFeed(string file, Func feedFormatter, List allowableDifferences = null) + { + string serializeFilePath = Path.GetTempFileName(); + bool toDeletedFile = true; + + try + { + SyndicationFeed feedObjct; + using (FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read)) + { + using (XmlReader reader = XmlDictionaryReader.CreateTextReader(fileStream, XmlDictionaryReaderQuotas.Max)) + { + feedObjct = SyndicationFeed.Load(reader); + } + } + + using (FileStream fileStream = new FileStream(serializeFilePath, FileMode.OpenOrCreate)) + { + using (XmlWriter writer = XmlDictionaryWriter.CreateTextWriter(fileStream)) + { + SyndicationFeedFormatter formatter = feedFormatter(feedObjct); + formatter.WriteTo(writer); + } + } + + CompareHelper ch = new CompareHelper + { + Diff = new XmlDiff() + { + Option = XmlDiffOption.IgnoreComments | XmlDiffOption.IgnorePrefix | XmlDiffOption.IgnoreWhitespace | XmlDiffOption.IgnoreChildOrder | XmlDiffOption.IgnoreAttributeOrder + }, + AllowableDifferences = allowableDifferences + }; + + string diffNode = string.Empty; + if (!ch.Compare(file, serializeFilePath, out diffNode)) + { + toDeletedFile = false; + string errorMessage = $"The generated file was different from the baseline file:{Environment.NewLine}Baseline: {file}{Environment.NewLine}Actual: {serializeFilePath}{Environment.NewLine}Different Nodes:{Environment.NewLine}{diffNode}"; + Assert.True(false, errorMessage); + } + } + catch (Exception e) + { + Exception newEx = new Exception($"Failed File Name: {file}", e); + throw newEx; + } + finally + { + if (toDeletedFile) + { + File.Delete(serializeFilePath); + } + } + } + + private static List GetAtomFeedPositiveTestAllowableDifferences() + { + return new List(new AllowableDifference[] + { + new AllowableDifference("",""), + new AllowableDifference("",""), + new AllowableDifference("",""), + new AllowableDifference("",""), + new AllowableDifference("","<title type=\"text\">"), + new AllowableDifference("<subtitle>","<subtitle type=\"text\">"), + new AllowableDifference("<subtitle xmlns=\"http://www.w3.org/2005/Atom\" />","<subtitle type=\"text\" xmlns=\"http://www.w3.org/2005/Atom\" />"), + new AllowableDifference("<title xmlns=\"http://www.w3.org/2005/Atom\" />","<title type=\"text\" xmlns=\"http://www.w3.org/2005/Atom\" />"), + new AllowableDifference("<atom:title>","<title type=\"text\">"), + new AllowableDifference("<summary>","<summary type=\"text\">"), + new AllowableDifference("<atom:summary>","<summary type=\"text\">"), + new AllowableDifference("<generator uri=\"http://www.contoso.com/\" version=\"1.0\">", "<generator>"), + new AllowableDifference("<generator uri=\"/generator\" xmlns=\"http://www.w3.org/2005/Atom\" />", "<generator xmlns=\"http://www.w3.org/2005/Atom\" />"), + new AllowableDifference("<generator uri=\"/generator\">", "<generator>"), + new AllowableDifference("<generator uri=\"misc/Colophon\">", "<generator>"), + new AllowableDifference("<generator uri=\"http://www.contoso.com/ \" version=\"1.0\">", "<generator>"), + new AllowableDifference("<rights>","<rights type=\"text\">"), + new AllowableDifference("<link href=\"http://contoso.com\" xmlns=\"http://www.w3.org/2005/Atom\" />","<link href=\"http://contoso.com/\" xmlns=\"http://www.w3.org/2005/Atom\" />"), + new AllowableDifference("<link href=\" http://contoso.com/ \" xmlns=\"http://www.w3.org/2005/Atom\" />","<link href=\"http://contoso.com/\" xmlns=\"http://www.w3.org/2005/Atom\" />"), + new AllowableDifference("<feed xml:lang=\"\" xmlns=\"http://www.w3.org/2005/Atom\">","<feed xmlns=\"http://www.w3.org/2005/Atom\">"), + new AllowableDifference("<entry xmlns:xh=\"http://www.w3.org/1999/xhtml\">","<entry>"), + new AllowableDifference("<xh:div>","<xh:div xmlns:xh=\"http://www.w3.org/1999/xhtml\">"), + new AllowableDifference("<summary type=\"xhtml\" xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">","<summary type=\"xhtml\">"), + new AllowableDifference("<xhtml:a href=\"http://contoso.com/\">","<xhtml:a href=\"http://contoso.com/\" xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">"), + new AllowableDifference("<feed xmlns:trackback=\"http://contoso.com/public/xml/rss/module/trackback/\" xmlns=\"http://www.w3.org/2005/Atom\">","<feed xmlns=\"http://www.w3.org/2005/Atom\">"), + new AllowableDifference("<trackback:ping>", "<trackback:ping xmlns:trackback=\"http://contoso.com/public/xml/rss/module/trackback/\">"), + new AllowableDifference("<feed xmlns:dc=\"http://contoso.com/dc/elements/1.1/\" xmlns:foaf=\"http://xmlns.com/foaf/0.1/\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns=\"http://www.w3.org/2005/Atom\">", "<feed xmlns=\"http://www.w3.org/2005/Atom\">"), + new AllowableDifference("<author rdf:parseType=\"Resource\">", "<author xmlns:a=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" a:parseType=\"Resource\">"), + new AllowableDifference("<foaf:homepage rdf:resource=\"http://contoso.com/\">", "<foaf:homepage xmlns:foaf=\"http://xmlns.com/foaf/0.1/\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" rdf:resource=\"http://contoso.com/\">"), + new AllowableDifference("<foaf:weblog rdf:resource=\"http://contoso.com/blog/\">", "<foaf:weblog xmlns:foaf=\"http://xmlns.com/foaf/0.1/\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" rdf:resource=\"http://contoso.com/blog/\">"), + new AllowableDifference("<foaf:workplaceHomepage rdf:resource=\"http://DoeCorp.contoso.com/\">", "<foaf:workplaceHomepage xmlns:foaf=\"http://xmlns.com/foaf/0.1/\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" rdf:resource=\"http://DoeCorp.contoso.com/\">"), + new AllowableDifference("<dc:description>", "<dc:description xmlns:dc=\"http://contoso.com/dc/elements/1.1/\">"), + new AllowableDifference("<entry rdf:parseType=\"Resource\">", "<entry xmlns:a=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" a:parseType=\"Resource\">"), + new AllowableDifference("<foaf:primaryTopic rdf:parseType=\"Resource\">", "<foaf:primaryTopic xmlns:foaf=\"http://xmlns.com/foaf/0.1/\" rdf:parseType=\"Resource\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">"), + new AllowableDifference("<link href=\"http://contoso.com/\" rdf:resource=\"http://contoso.com/\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns=\"http://www.w3.org/2005/Atom\" />", "<link xmlns:a=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" href=\"http://contoso.com/\" a:resource=\"http://contoso.com/\" xmlns=\"http://www.w3.org/2005/Atom\" />"), + new AllowableDifference("<feed xmlns:creativeCommons=\"http://contoso.com/creativeCommonsRssModule\" xmlns=\"http://www.w3.org/2005/Atom\">", "<feed xmlns=\"http://www.w3.org/2005/Atom\">"), + new AllowableDifference("<creativeCommons:license>", "<creativeCommons:license xmlns:creativeCommons=\"http://contoso.com/creativeCommonsRssModule\">"), + new AllowableDifference("<feed xmlns:a=\"http://www.contoso.com/extension-a\" xmlns:dc=\"http://contoso.com/dc/elements/1.1/\" xmlns=\"http://www.w3.org/2005/Atom\">", "<feed xmlns=\"http://www.w3.org/2005/Atom\">"), + new AllowableDifference("<a:simple-value>", "<a:simple-value xmlns:a=\"http://www.contoso.com/extension-a\">"), + new AllowableDifference("<a:structured-xml>", "<a:structured-xml xmlns:a=\"http://www.contoso.com/extension-a\">"), + new AllowableDifference("<dc:title>", "<dc:title xmlns:dc=\"http://contoso.com/dc/elements/1.1/\">"), + new AllowableDifference("<simple-value>", "<simple-value xmlns=\"\">"), + new AllowableDifference("<feed xmlns:xhtml=\"http://www.w3.org/1999/xhtml\" xmlns=\"http://www.w3.org/2005/Atom\">", "<feed xmlns=\"http://www.w3.org/2005/Atom\">"), + new AllowableDifference("<trackback:about>", "<trackback:about xmlns:trackback=\"http://contoso.com/public/xml/rss/module/trackback/\">"), + new AllowableDifference("<xhtml:img src=\"http://contoso.com/image.jpg\">", "<xhtml:img src=\"http://contoso.com/image.jpg\" xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">"), + new AllowableDifference("<feed xml:base=\"http://contoso.com\" xmlns=\"http://www.w3.org/2005/Atom\">", "<feed xml:base=\"http://contoso.com/\" xmlns=\"http://www.w3.org/2005/Atom\">"), + new AllowableDifference("<link href=\"http://contoso.com/licenses/by-nc/2.5/\" xmlns:lic=\"http://web.resource.org/cc/\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" rel=\"http://www.contoso.com/atom/extensions/proposed/license\" rdf:resource=\"http://contoso.com/licenses/by-nc/2.5/\" type=\"text/html\" rdf:type=\"http://web.resource.org/cc/license\">", "<link xmlns:a=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" href=\"http://contoso.com/licenses/by-nc/2.5/\" rel=\"http://www.contoso.com/atom/extensions/proposed/license\" a:resource=\"http://contoso.com/licenses/by-nc/2.5/\" type=\"text/html\" a:type=\"http://web.resource.org/cc/license\">"), + }); + } + + private static List<AllowableDifference> GetRssFeedPositiveTestAllowableDifferences() + { + return new List<AllowableDifference>(new AllowableDifference[] + { + new AllowableDifference("<rss version=\"2.0\">","<rss xmlns:a10=\"http://www.w3.org/2005/Atom\" version=\"2.0\">"), + new AllowableDifference("<content:encoded>", "<content:encoded xmlns:content=\"http://contoso.com/rss/1.0/modules/content/\">"), + new AllowableDifference("Tue, 31 Dec 2002 14:20:20 GMT", "Tue, 31 Dec 2002 14:20:20 Z"), + }); + } + + private static List<string> GetTestFilesForFeedTest(string dataFile) + { + List<string> fileList = new List<string>(); + + string file; + using (StreamReader sr = new StreamReader(dataFile)) + { + while (!string.IsNullOrEmpty(file = sr.ReadLine())) + { + if (!file.StartsWith("#")) + { + file = file.Trim(); + if (File.Exists(file)) + { + fileList.Add(Path.GetFullPath(file)); + } + else + { + throw new FileNotFoundException($"File `{file}` was not found!"); + } + } + } + } + return fileList; + } + } } diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/Configurations.props b/external/corefx/src/System.ServiceModel.Syndication/tests/Configurations.props index c398e42e89..8b803e0772 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/tests/Configurations.props +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/Configurations.props @@ -2,6 +2,7 @@ <Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <BuildConfigurations> + netcoreapp; netstandard; </BuildConfigurations> </PropertyGroup> diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/System.ServiceModel.Syndication.Tests.csproj b/external/corefx/src/System.ServiceModel.Syndication/tests/System.ServiceModel.Syndication.Tests.csproj index 66230d199f..449cfd5d6b 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/tests/System.ServiceModel.Syndication.Tests.csproj +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/System.ServiceModel.Syndication.Tests.csproj @@ -4,22 +4,33 @@ <PropertyGroup> <ProjectGuid>{A622B2C0-DD74-4218-9CF0-F9B2E52F4E91}</ProjectGuid> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='netcoreapp-Debug|AnyCPU'" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='netcoreapp-Release|AnyCPU'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='netstandard-Debug|AnyCPU'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='netstandard-Release|AnyCPU'" /> <ItemGroup> - <Compile Include="$(MsBuildThisFileDirectory)\**\*.cs" /> + <Compile Include="$(MsBuildThisFileDirectory)**\*.cs" Exclude="$(MsBuildThisFileDirectory)netcoreapp\**\*.cs" /> </ItemGroup> - <ItemGroup> - <Content Include="$(MsBuildThisFileDirectory)\TestFeeds\*.xml"> - <CopyToOutputDirectory>Always</CopyToOutputDirectory> - </Content> + <ItemGroup Condition="'$(TargetGroup)' == 'netcoreapp'"> + <Compile Include="$(MsBuildThisFileDirectory)netcoreapp\**\*.cs" /> </ItemGroup> - <ItemGroup> + <ItemGroup Condition="'$(TargetGroup)' == 'netcoreapp'"> <ReferenceFromRuntime Include="System.ServiceModel.Syndication"> - <!-- Copy this to our test output directory and run from there. - This is required so that we can stage the application with a custom runtimeconfig that lets it run on the test shared framework. --> <Private>true</Private> </ReferenceFromRuntime> </ItemGroup> + <ItemGroup> + <Content Include="$(MsBuildThisFileDirectory)\TestFeeds\**\*.xml"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + </ItemGroup> + <ItemGroup> + <None Include="$(MsBuildThisFileDirectory)\TestFeeds\atom_feeds.dat"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </None> + <None Include="$(MsBuildThisFileDirectory)\TestFeeds\rss_feeds.dat"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </None> + </ItemGroup> <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> -</Project> +</Project> \ No newline at end of file diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomEntryTest.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomEntryTest.xml new file mode 100644 index 0000000000..281c039ad8 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomEntryTest.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<entry xmlns="http://www.w3.org/2005/Atom"> + <id>uuid:43481a10-d881-40d1-adf2-99b438c57e21;id=1</id> + <title type="text">SyndicationFeed released for .net Core + 2017-10-11T11:25:55Z + + A lot of text describing the release of .net core feature + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/absolute_rel.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/absolute_rel.xml new file mode 100644 index 0000000000..6e4fb5c2d2 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/absolute_rel.xml @@ -0,0 +1,23 @@ + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/alternate-no-content.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/alternate-no-content.xml new file mode 100644 index 0000000000..ebe57bcb98 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/alternate-no-content.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/author-at-entry-only.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/author-at-entry-only.xml new file mode 100644 index 0000000000..11a7493a68 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/author-at-entry-only.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + Author Name + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/author-at-feed-and-entry.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/author-at-feed-and-entry.xml new file mode 100644 index 0000000000..cfc67ea2bf --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/author-at-feed-and-entry.xml @@ -0,0 +1,28 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + input name + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/author-at-feed-only.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/author-at-feed-only.xml new file mode 100644 index 0000000000..3382644539 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/author-at-feed-only.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/authorless-with-no-entries.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/authorless-with-no-entries.xml new file mode 100644 index 0000000000..cb9a0b1a5f --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/authorless-with-no-entries.xml @@ -0,0 +1,14 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/authorless-with-one-entry.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/authorless-with-one-entry.xml new file mode 100644 index 0000000000..507b09f237 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/authorless-with-one-entry.xml @@ -0,0 +1,22 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-entry-ext.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-entry-ext.xml new file mode 100644 index 0000000000..de4adb8bcd --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-entry-ext.xml @@ -0,0 +1,29 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-feed-ext.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-feed-ext.xml new file mode 100644 index 0000000000..ef6dc70660 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-feed-ext.xml @@ -0,0 +1,29 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-feed.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-feed.xml new file mode 100644 index 0000000000..3a254fde24 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-feed.xml @@ -0,0 +1,27 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-label-escaped-html.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-label-escaped-html.xml new file mode 100644 index 0000000000..630af3a29e --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-label-escaped-html.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-no-label.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-no-label.xml new file mode 100644 index 0000000000..a709510160 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-no-label.xml @@ -0,0 +1,27 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-no-scheme.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-no-scheme.xml new file mode 100644 index 0000000000..fa83f85919 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-no-scheme.xml @@ -0,0 +1,27 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-scheme-invalid-iri.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-scheme-invalid-iri.xml new file mode 100644 index 0000000000..7984809eee --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-scheme-invalid-iri.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-scheme-rel-iri.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-scheme-rel-iri.xml new file mode 100644 index 0000000000..bd4c8af1f8 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/category-scheme-rel-iri.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/contains-email.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/contains-email.xml new file mode 100644 index 0000000000..e8baf55b0a --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/contains-email.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name <test@contoso.com> + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-application-xthml.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-application-xthml.xml new file mode 100644 index 0000000000..6c05e5383c --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-application-xthml.xml @@ -0,0 +1,30 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + +
+ Some bold text. +
+
+
+ +
diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-base64-no-summary.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-base64-no-summary.xml new file mode 100644 index 0000000000..d1668e34cd --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-base64-no-summary.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + U29tZSBtb3JlIHRleHQu + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-jpeg-invalid-base64.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-jpeg-invalid-base64.xml new file mode 100644 index 0000000000..1c8251455b --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-jpeg-invalid-base64.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + insert image here + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-jpeg-valid-base64.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-jpeg-valid-base64.xml new file mode 100644 index 0000000000..b2be061515 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-jpeg-valid-base64.xml @@ -0,0 +1,54 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + /9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0a + HBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIy + MjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABYAGoDAREA + AhEBAxEB/8QAGwAAAgIDAQAAAAAAAAAAAAAAAAIBAwQGBwX/xAA4EAABAwIDAwgJAwUAAAAAAAAB + AAIDBBEFEmEUIaEGBzFBUYGR0RMiM1RxkpPh8DJTwSNDRFJi/8QAGgEBAQEBAQEBAAAAAAAAAAAA + AAECAwUEBv/EACIRAQACAgEEAwEBAAAAAAAAAAABAgMREwQSIVEFIzFBMv/aAAwDAQACEQMRAD8A + 7+gi6AzIDMgMyAzIDMgMyAzIDMgMyAzIJugECkoEJOqoi51TQLnVNAudU0C51TQLnVNAudU0C51T + QLnVNAudU0JBKBgoHQI5BUe5URfUcFQpe0dLgPBNCs1dM02M8YOrgtxjvP5AjbaX3iL5wnFf0m4G + 20vvEXzhOK/o3A22l94i+cK8V/RuEitpT/kRfMFOK/o3CwSMcLtc0/AhYmJjwprjtHBBI/OhQWNU + FiBHIKzfXiqMSvro8Po5Kmd+WONpJJJXTFjtktFapM68uGY3zj4lyjx+LC8NnNJSSSZDIOm3WV7+ + HpceLxEblym222UlFgrIWskbLUvA3yTTOLnHxUyRn348ETDJFFgnug+o/wA1z+/2u4NseCe6j6j/ + ADU+/wBm4QaLBLb6UfUf5p9/s3CuSjwLIQKYsuP1NleCO+61WM/s8NKquV+J8i+UTYIa2Wrw6QB7 + WzOzOaL2sSu98NMkRGSuk3Mfjs3JvlBTcocMjrKZ25w9ZtzcFeH1XTzgv2y6VmJe2L68V8rSxt1A + 9kCOQVO7uCo5lzuYlJBgjKSJ3tj61l7fxWKJmbuWSXIcHwh75GyAEOvcEL3MWKKx3S5zLdKajqwB + eeTxWrXommcykqQPbv8AFcZyUXRxR1P7z/FTkoaK6kqh/ef4qxkoaYs1HV23TvXSL0TTU8awiWRx + kkLnut0net3pW8bPxtnM/Xy0mJz0Dj/TcLgdhXk/IYd4Nz+w6Uny7gw3F93BfnHZc1ZFiBHIKXk6 + 8VqBzLnQo3VEED7EgG3Wv0Hw1o81ccjWcCogI23C9bPfthiIbTBSCw3LzL5pb0ym0Y7FwtmXR9iH + Ys8y6Q6i0VjMmmNLSCx3LtXLJp4GLUTTE7d1L0OnyTLEwTm9oCzlC6UDc0X3Ll8pMVwTHtafrs7L + 26+K/KS7r2rIdQK5BS8A9nBaGtcpMOixGikgksL9DgBdp6j0r7+jzTivFoYtG4c/poZ8LnMNUzKA + fVeP0u71+hteueu6uWtNjpamIgG4Xm5cdoaiXpx1FPbeQvjtSzW4Wiopv9gs9l18EkqacdBCtcdj + bz6mphsfWC+nHjsky1zEJHVb/QU0ZlkPU3+V6mKIxx3WlzltHJLBRhkJMhDp5N7yLW+AXkdf1PLb + UfjpSNN2jbuH2Xky6L2rIsUCOQVOuVoYFdDnZuvxXXHKS16en3kObdvYR9l9tL+mdMbYoAbiFg+D + fsu3Lef6moNs0YHsm+CzyW9mgII/2x4J329mhs8ZG+IeCd9va6K6jgI3wsPxCsZbx/U0tgp2t9WO + MMH/ACLfwsXyTP8AqV1EPew+BzbE34r48ttrD1m3H4V87S1t1A6gRyCs2VFb2NeLbvzvWolHmVFG + HE2A/O9dqX0mmG6gP5bzXSMppGwnTh5q8pobDoOHmnKDYTpw805TQ2E6cPNOU0yqeiAPQOHmudsm + zT1Iowxthbh5rhM7VaLdqirGrIsQI5BWQT2qgsdUClt+lXYgxA9qbRHohqrtR6Iap3A9CNU7gehG + qbDBgHRdTYbKe0qAAOqCxqgdBBF0ClvwQRlCoLBAZQgMoQFggMoQGUIDKEBlHYgMqgYCyBkH/9k= + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-no-alternate.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-no-alternate.xml new file mode 100644 index 0000000000..22b306dd31 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-no-alternate.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + Danger, Will Robinson! + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-no-type-escaped-html.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-no-type-escaped-html.xml new file mode 100644 index 0000000000..6aaa6c1488 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-no-type-escaped-html.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + Some <b>bold</b> text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-plain-with-children.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-plain-with-children.xml new file mode 100644 index 0000000000..aad16e3081 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-plain-with-children.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + Some bold text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-no-summary.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-no-summary.xml new file mode 100644 index 0000000000..d2779b16a0 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-no-summary.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-no-type-no-error.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-no-type-no-error.xml new file mode 100644 index 0000000000..fdf8dda37e --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-no-type-no-error.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-no-type.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-no-type.xml new file mode 100644 index 0000000000..c4613d7a22 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-no-type.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-relative-ref.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-relative-ref.xml new file mode 100644 index 0000000000..e083833efe --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-relative-ref.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-type-html.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-type-html.xml new file mode 100644 index 0000000000..5f8e9fa39d --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-type-html.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-type-text-html.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-type-text-html.xml new file mode 100644 index 0000000000..8f7db7ae6b --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-type-text-html.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-type-text.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-type-text.xml new file mode 100644 index 0000000000..6cf3bedaac --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-type-text.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-type-xhtml.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-type-xhtml.xml new file mode 100644 index 0000000000..525e3c9959 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src-type-xhtml.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src.xml new file mode 100644 index 0000000000..d828eedc62 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-src.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-text-html.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-text-html.xml new file mode 100644 index 0000000000..8a61043bf6 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-text-html.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + Some <b>bold</b> text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-xhtml-escaped.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-xhtml-escaped.xml new file mode 100644 index 0000000000..a4c659b377 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-xhtml-escaped.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. +
Some <b>bold</b> text.
+
+ +
diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-xhtml-mixed.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-xhtml-mixed.xml new file mode 100644 index 0000000000..edfa708327 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-xhtml-mixed.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. +
Example: Some <b>bold</b> text.
+
+ +
diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-xhtml-no-xhtml-div.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-xhtml-no-xhtml-div.xml new file mode 100644 index 0000000000..34839e8361 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-xhtml-no-xhtml-div.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + text + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-xhtml-notmarkup.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-xhtml-notmarkup.xml new file mode 100644 index 0000000000..3d2d399756 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-xhtml-notmarkup.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. +
Some <x>bold</x> text.
+
+ +
diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-xhtml-text-children.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-xhtml-text-children.xml new file mode 100644 index 0000000000..de1f4f4bfe --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/content-xhtml-text-children.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + text & more + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/duplicate-entries.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/duplicate-entries.xml new file mode 100644 index 0000000000..46bb9dbe96 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/duplicate-entries.xml @@ -0,0 +1,33 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Different text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/email-rss20-style.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/email-rss20-style.xml new file mode 100644 index 0000000000..5dfaacf1d4 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/email-rss20-style.xml @@ -0,0 +1,29 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + + input name + me@contoso.com (input name) + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/email-with-name.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/email-with-name.xml new file mode 100644 index 0000000000..9ae68293e3 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/email-with-name.xml @@ -0,0 +1,29 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + + input name + input name <test@contoso.com> + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/email-with-plus.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/email-with-plus.xml new file mode 100644 index 0000000000..2e2fa2be7f --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/email-with-plus.xml @@ -0,0 +1,29 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + + input name + test@contoso.com + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/empty-content.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/empty-content.xml new file mode 100644 index 0000000000..c5dcf3187d --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/empty-content.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/empty-title.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/empty-title.xml new file mode 100644 index 0000000000..a4a4b42f07 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/empty-title.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + <link href="http://contoso.com/2003/12/13/atom03"/> + <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id> + <updated>2003-12-13T18:30:02Z</updated> + <summary>Some text.</summary> + </entry> + +</feed> diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/empty_author.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/empty_author.xml new file mode 100644 index 0000000000..c006c0ea15 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/empty_author.xml @@ -0,0 +1,9 @@ +<feed xmlns="http://www.w3.org/2005/Atom"> +<title type="text"> +16ba9aff-eb42-4907-a86e-dcdc6068b20a +2007-04-11T20:04:16Z + + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/escaped_text.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/escaped_text.xml new file mode 100644 index 0000000000..6eba437d28 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/escaped_text.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some&nbsp;escaped&nbsp;html + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/example_html_title.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/example_html_title.xml new file mode 100644 index 0000000000..07e3ce3a19 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/example_html_title.xml @@ -0,0 +1,27 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + Less: <em> &lt; </em> + + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/example_text_title.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/example_text_title.xml new file mode 100644 index 0000000000..e36ee87a77 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/example_text_title.xml @@ -0,0 +1,27 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + Less: < + + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/example_xhtml_summary1.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/example_xhtml_summary1.xml new file mode 100644 index 0000000000..96843a9a38 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/example_xhtml_summary1.xml @@ -0,0 +1,29 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + +
+ This is XHTML content. +
+
+
+ +
diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/example_xhtml_summary2.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/example_xhtml_summary2.xml new file mode 100644 index 0000000000..aed3b201f3 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/example_xhtml_summary2.xml @@ -0,0 +1,29 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + + + This is XHTML content. + + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/example_xhtml_summary3.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/example_xhtml_summary3.xml new file mode 100644 index 0000000000..7a818f90f0 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/example_xhtml_summary3.xml @@ -0,0 +1,29 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + + + This is XHTML content. + + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/extension-unknown-noerror.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/extension-unknown-noerror.xml new file mode 100644 index 0000000000..2a594bd073 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/extension-unknown-noerror.xml @@ -0,0 +1,31 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + Test + A structured extension element + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + Test + A structured extension element + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/extensive-noerror.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/extensive-noerror.xml new file mode 100644 index 0000000000..f9f90d983c --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/extensive-noerror.xml @@ -0,0 +1,50 @@ + + + + + dive into mark + + A <em>lot</em> of effort + went into making this effortless + + 2005-07-11T12:29:29Z + tag:contoso.com,2003:3 + + + Contoso Rights. + + Example Toolkit + + + Atom draft-07 snapshot + + + tag:contoso.com,2003:3.2397 + 2005-07-11T12:29:29Z + 2003-12-13T08:29:29-04:00 + + Author Name + http://contoso.com/ + author@contoso.com + + + input name + + + Name + + +
+

[Update: The Atom draft is finished.]

+
+
+
+
diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/extensive-nowarn.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/extensive-nowarn.xml new file mode 100644 index 0000000000..012703232f --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/extensive-nowarn.xml @@ -0,0 +1,50 @@ + + + + + dive into mark + + A <em>lot</em> of effort + went into making this effortless + + 2005-07-11T12:29:29Z + tag:contoso.com,2003:3 + + + Copyright (c) 2003, Author Name + + Example Toolkit + + + Atom draft-07 snapshot + + + tag:contoso.com,2003:3.2397 + 2005-07-11T12:29:29Z + 2003-12-13T08:29:29-04:00 + + Author Name + http://contoso.com/ + author@contoso.com + + + input name + + + Name + + +
+

[Update: The Atom draft is finished.]

+
+
+
+
diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/generator-escaped-html.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/generator-escaped-html.xml new file mode 100644 index 0000000000..1aff7e9d12 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/generator-escaped-html.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + <b>The</b> generator + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/generator-no-text.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/generator-no-text.xml new file mode 100644 index 0000000000..3c7a8186db --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/generator-no-text.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/generator_relative_ref.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/generator_relative_ref.xml new file mode 100644 index 0000000000..7fb3bdaa50 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/generator_relative_ref.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + Generated from XML source code using Perl, Expat, XML::Parser, Emacs, Mysql, and ImageMagick. Industrial strength technology, baby. + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/icon_invalid_uri.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/icon_invalid_uri.xml new file mode 100644 index 0000000000..5d2b50fdd4 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/icon_invalid_uri.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + insert uri here + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/icon_relative_ref.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/icon_relative_ref.xml new file mode 100644 index 0000000000..505e886b64 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/icon_relative_ref.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + favicon.ico + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-dot-segments.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-dot-segments.xml new file mode 100644 index 0000000000..9110900c26 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-dot-segments.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + http://contoso.com/./id/1234 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-empty-fragment-id.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-empty-fragment-id.xml new file mode 100644 index 0000000000..d50b6ce020 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-empty-fragment-id.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + http://contoso.com/id/1234# + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-empty-path.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-empty-path.xml new file mode 100644 index 0000000000..495591fb4a --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-empty-path.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + http://contoso.com + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-empty-query.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-empty-query.xml new file mode 100644 index 0000000000..0aaaa042ba --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-empty-query.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + http://contoso.com/id/1234? + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-explicit-authority.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-explicit-authority.xml new file mode 100644 index 0000000000..71832dbbc2 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-explicit-authority.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + http://contoso.com/id/1234 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-explicit-default-port.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-explicit-default-port.xml new file mode 100644 index 0000000000..9ecf986f2f --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-explicit-default-port.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + http://contoso.com:80/id/1234 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-host-uppercase.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-host-uppercase.xml new file mode 100644 index 0000000000..883326cd3c --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-host-uppercase.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + http://contoso.com/id/1234 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-noniana.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-noniana.xml new file mode 100644 index 0000000000..041c903827 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-noniana.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + doi:10.1038/nature05582 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-not-uri.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-not-uri.xml new file mode 100644 index 0000000000..590dcec456 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-not-uri.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + insert id here + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-percent-encoded-lower.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-percent-encoded-lower.xml new file mode 100644 index 0000000000..4069fc301e --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-percent-encoded-lower.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + http://contoso.com/id/1234?q=%5c + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-percent-encoded.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-percent-encoded.xml new file mode 100644 index 0000000000..fcbcd8094a --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-percent-encoded.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + http://contoso.com/%69%64/1234 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-relative-uri.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-relative-uri.xml new file mode 100644 index 0000000000..1f293dcb6f --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-relative-uri.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + /id/1234 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-uppercase-scheme.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-uppercase-scheme.xml new file mode 100644 index 0000000000..5e80c8d500 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-uppercase-scheme.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + Http://contoso.com/id/1234 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-valid-tag-uris.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-valid-tag-uris.xml new file mode 100644 index 0000000000..994708349d --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/id-valid-tag-uris.xml @@ -0,0 +1,31 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + tag:contoso.com,2000: + + + Atom-Powered Robots Run Amok + + tag:contoso.com,2000:#anchor + 2005-12-18T13:32:18Z + + + + Atom-Powered Robots Test Empty Fragments + + tag:contoso.com,2000:# + 2005-12-18T13:32:33Z + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/infoset-attr-order.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/infoset-attr-order.xml new file mode 100644 index 0000000000..48b7120a15 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/infoset-attr-order.xml @@ -0,0 +1,28 @@ + + + + + + Example Feed + + + + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/infoset-char-ref.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/infoset-char-ref.xml new file mode 100644 index 0000000000..78ad80cf00 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/infoset-char-ref.xml @@ -0,0 +1,27 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + + 2003-12-13T18:30:02Z + + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/infoset-element-whitespace.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/infoset-element-whitespace.xml new file mode 100644 index 0000000000..9a05e46ccf --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/infoset-element-whitespace.xml @@ -0,0 +1,28 @@ + + + + + + Example Feed + + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/infoset-empty1.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/infoset-empty1.xml new file mode 100644 index 0000000000..f552c6a1c5 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/infoset-empty1.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + <link href="http://contoso.com/2003/12/13/atom03"/> + <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id> + <updated>2003-12-13T18:30:02Z</updated> + <summary>Some text.</summary> + </entry> + +</feed> diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/infoset-empty2.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/infoset-empty2.xml new file mode 100644 index 0000000000..a5687bf326 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/infoset-empty2.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Description: Empty title element +Expect: NotBlank{element:title,parent:entry} +--> + +<feed xmlns="http://www.w3.org/2005/Atom"> + + <title>Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + <link href="http://contoso.com/2003/12/13/atom03"/> + <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id> + <updated>2003-12-13T18:30:02Z</updated> + <summary>Some text.</summary> + </entry> + +</feed> diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/infoset-quote-single.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/infoset-quote-single.xml new file mode 100644 index 0000000000..8b9044ace5 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/infoset-quote-single.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Description: The kind of quotation marks (single or double) used to quote attribute values is not significant. +Expect: !Error +--> + +<feed xmlns='http://www.w3.org/2005/Atom'> + + <title>Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/invalid-email.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/invalid-email.xml new file mode 100644 index 0000000000..3c658560dd --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/invalid-email.xml @@ -0,0 +1,29 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + + input name + enter email address here + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/invalid-uri.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/invalid-uri.xml new file mode 100644 index 0000000000..4d0fcc699b --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/invalid-uri.xml @@ -0,0 +1,29 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + + input name + enter homepage here + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/invalid-xml-base.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/invalid-xml-base.xml new file mode 100644 index 0000000000..e89d653c32 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/invalid-xml-base.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/invalid-xml-lang.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/invalid-xml-lang.xml new file mode 100644 index 0000000000..70c065b5d1 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/invalid-xml-lang.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/invalid_html.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/invalid_html.xml new file mode 100644 index 0000000000..9b6aa55b86 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/invalid_html.xml @@ -0,0 +1,26 @@ + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + + <a + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/license-noerror.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/license-noerror.xml new file mode 100644 index 0000000000..9a0a1968dc --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/license-noerror.xml @@ -0,0 +1,28 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + http://contoso.com/licenses/by/2.0/ + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + http://contoso.com/licenses/by/2.0/ + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-extensions.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-extensions.xml new file mode 100644 index 0000000000..2e87e38ad7 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-extensions.xml @@ -0,0 +1,42 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + + + + + + + + + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-full-uri.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-full-uri.xml new file mode 100644 index 0000000000..16c4b6db4e --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-full-uri.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-href-relative.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-href-relative.xml new file mode 100644 index 0000000000..1405bbbe3e --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-href-relative.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-hreflang-invalid-language.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-hreflang-invalid-language.xml new file mode 100644 index 0000000000..a84b6cb537 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-hreflang-invalid-language.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-rel-full.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-rel-full.xml new file mode 100644 index 0000000000..c3bfc5a4fc --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-rel-full.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-rel-iana.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-rel-iana.xml new file mode 100644 index 0000000000..f7f9ea1f23 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-rel-iana.xml @@ -0,0 +1,34 @@ + + + + + urn:linkreltest:feed + Testing Link Rels + + + James + 2006-04-25T12:12:12Z + + urn:linkreltest:1 + Does your reader support http://www.contoso.com/assignments/relation/alternate properly? + 2006-04-25T12:12:12Z + + This entry uses link/@rel="http://www.contoso.com/assignments/relation/alternate". + + + urn:linkreltest:2 + How does your reader handle uppercase variances of standard rel values? + 2006-04-25T12:12:13Z + + + This entry uses an uppercase "ALTERNATE" and a lowercase "alternate", your reader should show the lowercase link to http://www.contoso.com/public/alternate. + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-rel-isegment-nz-nc.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-rel-isegment-nz-nc.xml new file mode 100644 index 0000000000..7ef2c9f41b --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-rel-isegment-nz-nc.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-rel-relative.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-rel-relative.xml new file mode 100644 index 0000000000..1b9614c028 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-rel-relative.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-same-rel-different-types.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-same-rel-different-types.xml new file mode 100644 index 0000000000..10029ca8bd --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-same-rel-different-types.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-same-rel-type-different-hreflang.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-same-rel-type-different-hreflang.xml new file mode 100644 index 0000000000..9743a8e3b0 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-same-rel-type-different-hreflang.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-same-rel-type-hreflang.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-same-rel-type-hreflang.xml new file mode 100644 index 0000000000..f3885ec9b2 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-same-rel-type-hreflang.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-same-rel-type-no-hreflang.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-same-rel-type-no-hreflang.xml new file mode 100644 index 0000000000..c7a30d8639 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-same-rel-type-no-hreflang.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-title-with-badchars.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-title-with-badchars.xml new file mode 100644 index 0000000000..d029a61e5d --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-title-with-badchars.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-title-with-html.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-title-with-html.xml new file mode 100644 index 0000000000..53e9f2342a --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-title-with-html.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-type-invalid-mime.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-type-invalid-mime.xml new file mode 100644 index 0000000000..dfdbab0be1 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-type-invalid-mime.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-type-parameters.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-type-parameters.xml new file mode 100644 index 0000000000..a0a0e606dd --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/link-type-parameters.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/logo_relative_ref.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/logo_relative_ref.xml new file mode 100644 index 0000000000..47549cbab9 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/logo_relative_ref.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + atomlogo.png + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/missing-id-source.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/missing-id-source.xml new file mode 100644 index 0000000000..2f8a4fe177 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/missing-id-source.xml @@ -0,0 +1,29 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + Source of all knowledge + 2003-12-13T17:46:27Z + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/missing-self.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/missing-self.xml new file mode 100644 index 0000000000..afe7d4abe6 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/missing-self.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/missing-title-source.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/missing-title-source.xml new file mode 100644 index 0000000000..b9f25b13be --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/missing-title-source.xml @@ -0,0 +1,29 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + urn:uuid:28213c50-f84c-11d9-8cd6-0800200c9a66 + 2003-12-13T17:46:27Z + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/missing-updated-source.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/missing-updated-source.xml new file mode 100644 index 0000000000..d517408111 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/missing-updated-source.xml @@ -0,0 +1,29 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + Source of all knowledge + urn:uuid:28213c50-f84c-11d9-8cd6-0800200c9a66 + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/missing_xhtml_div.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/missing_xhtml_div.xml new file mode 100644 index 0000000000..3ec4cab676 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/missing_xhtml_div.xml @@ -0,0 +1,26 @@ + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + + So I was reading contoso.com the other day, it's really interesting. + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/missing_xhtml_ns.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/missing_xhtml_ns.xml new file mode 100644 index 0000000000..244e898ecc --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/missing_xhtml_ns.xml @@ -0,0 +1,29 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + +
+ This is XHTML content. +
+
+
+ +
diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multi-enclosure-test.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multi-enclosure-test.xml new file mode 100644 index 0000000000..6bbeb6b1c1 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multi-enclosure-test.xml @@ -0,0 +1,25 @@ + + + + Enclosure Test + + http://www.contoso.com/workbench/ + Testing multiple enclosure elements + Wordzilla/0.101 + 2004-12-27T11:12:01-05:00 + + input name + + + Kirk Cameron + + Here's a two-enclosure test + 2004-12-27T11:12:01-05:00 + tag:contoso.com,2004-09-30:test.2423 + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-alternates-differing-source.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-alternates-differing-source.xml new file mode 100644 index 0000000000..0c2a37f8cd --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-alternates-differing-source.xml @@ -0,0 +1,32 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + Source of all knowledge + urn:uuid:28213c50-f84c-11d9-8cd6-0800200c9a66 + 2003-12-13T17:46:27Z + + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-alternates-differing.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-alternates-differing.xml new file mode 100644 index 0000000000..a23a76797b --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-alternates-differing.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-alternates-matching-source.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-alternates-matching-source.xml new file mode 100644 index 0000000000..0f4c0a88df --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-alternates-matching-source.xml @@ -0,0 +1,32 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + Source of all knowledge + urn:uuid:28213c50-f84c-11d9-8cd6-0800200c9a66 + 2003-12-13T17:46:27Z + + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-alternates-matching.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-alternates-matching.xml new file mode 100644 index 0000000000..a8628cdead --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-alternates-matching.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-authors-source.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-authors-source.xml new file mode 100644 index 0000000000..dc2acc8497 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-authors-source.xml @@ -0,0 +1,36 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + Source of all knowledge + urn:uuid:28213c50-f84c-11d9-8cd6-0800200c9a66 + 2003-12-13T17:46:27Z + + Author Name + + + input name + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-authors.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-authors.xml new file mode 100644 index 0000000000..74a49abfbc --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-authors.xml @@ -0,0 +1,28 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + + input name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-categories-entry.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-categories-entry.xml new file mode 100644 index 0000000000..4765c92eeb --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-categories-entry.xml @@ -0,0 +1,27 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-categories-source.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-categories-source.xml new file mode 100644 index 0000000000..e4a289db4b --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-categories-source.xml @@ -0,0 +1,32 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + Source of all knowledge + urn:uuid:28213c50-f84c-11d9-8cd6-0800200c9a66 + 2003-12-13T17:46:27Z + + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-categories.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-categories.xml new file mode 100644 index 0000000000..3c65c44886 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-categories.xml @@ -0,0 +1,27 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-contributors-entry.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-contributors-entry.xml new file mode 100644 index 0000000000..449e9cf746 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-contributors-entry.xml @@ -0,0 +1,32 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + John Smith + + + input name + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-contributors-source.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-contributors-source.xml new file mode 100644 index 0000000000..28cf9c4bad --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-contributors-source.xml @@ -0,0 +1,36 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + Source of all knowledge + urn:uuid:28213c50-f84c-11d9-8cd6-0800200c9a66 + 2003-12-13T17:46:27Z + + John Smith + + + input name + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-contributors.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-contributors.xml new file mode 100644 index 0000000000..4c3fed8e6e --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-contributors.xml @@ -0,0 +1,31 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + + John Smith + + + input name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-icons-source.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-icons-source.xml new file mode 100644 index 0000000000..dc3f9f6368 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-icons-source.xml @@ -0,0 +1,32 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + Source of all knowledge + urn:uuid:28213c50-f84c-11d9-8cd6-0800200c9a66 + 2003-12-13T17:46:27Z + /big.icon + /small.icon + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-icons.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-icons.xml new file mode 100644 index 0000000000..9af1cb5728 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-icons.xml @@ -0,0 +1,27 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + /big.icon + /small.icon + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-related-matching.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-related-matching.xml new file mode 100644 index 0000000000..fb35f20275 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/multiple-related-matching.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/no-content-or-alternate.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/no-content-or-alternate.xml new file mode 100644 index 0000000000..ef64321822 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/no-content-or-alternate.xml @@ -0,0 +1,24 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/no-content-or-summary.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/no-content-or-summary.xml new file mode 100644 index 0000000000..d5023ec5d3 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/no-content-or-summary.xml @@ -0,0 +1,24 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/no-name.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/no-name.xml new file mode 100644 index 0000000000..3b640d152e --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/no-name.xml @@ -0,0 +1,28 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + + test@contoso.com + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/prefixed-namespace.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/prefixed-namespace.xml new file mode 100644 index 0000000000..242e385c44 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/prefixed-namespace.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/rdf-extensions.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/rdf-extensions.xml new file mode 100644 index 0000000000..796ac6373d --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/rdf-extensions.xml @@ -0,0 +1,39 @@ + + + + + Example Feed + + 2003-12-13T18:30:02Z + A feed whose description here tries to be + legal Atom 1.0 but also use some foreign markup to have it + also count as legal RDF. Are the rdf:resource attributes OK? + + Author Name + + + + + + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/rel-colon.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/rel-colon.xml new file mode 100644 index 0000000000..86c213bdc9 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/rel-colon.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/related-same-rel-type-hreflang.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/related-same-rel-type-hreflang.xml new file mode 100644 index 0000000000..865c77e638 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/related-same-rel-type-hreflang.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/relative-ref.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/relative-ref.xml new file mode 100644 index 0000000000..7e2a973201 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/relative-ref.xml @@ -0,0 +1,29 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + + input name + http://contoso.com/name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/rights-text-with-escaped-html.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/rights-text-with-escaped-html.xml new file mode 100644 index 0000000000..07a81cb019 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/rights-text-with-escaped-html.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + Copyright &copy; 2005 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/rights-xhtml-no-xmldiv.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/rights-xhtml-no-xmldiv.xml new file mode 100644 index 0000000000..3ec7421c77 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/rights-xhtml-no-xmldiv.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + all rights reserved + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/self-vs-alternate.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/self-vs-alternate.xml new file mode 100644 index 0000000000..fd729f6165 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/self-vs-alternate.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/subtitle-blank.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/subtitle-blank.xml new file mode 100644 index 0000000000..68c3683515 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/subtitle-blank.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/summary-content-base64.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/summary-content-base64.xml new file mode 100644 index 0000000000..f673ea35c0 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/summary-content-base64.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + U29tZSBtb3JlIHRleHQu + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/summary-content-src.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/summary-content-src.xml new file mode 100644 index 0000000000..dfee56a852 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/summary-content-src.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/text_with_escaped_html.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/text_with_escaped_html.xml new file mode 100644 index 0000000000..cf7eba74ee --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/text_with_escaped_html.xml @@ -0,0 +1,26 @@ + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + + So I was reading <a href="http://contoso.com/">contoso.com</a> the other day, it's really interesting. + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/title-blank.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/title-blank.xml new file mode 100644 index 0000000000..d2860515aa --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/title-blank.xml @@ -0,0 +1,25 @@ + + + + + + + <link href="http://contoso.com/"/> + <updated>2003-12-13T18:30:02Z</updated> + <author> + <name>Author Name</name> + </author> + <id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id> + + <entry> + <title>Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/trackback-about-entry.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/trackback-about-entry.xml new file mode 100644 index 0000000000..0feb736f71 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/trackback-about-entry.xml @@ -0,0 +1,27 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + http://www.example.net/article/01 + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/trackback-ping-entry.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/trackback-ping-entry.xml new file mode 100644 index 0000000000..989a0247c5 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/trackback-ping-entry.xml @@ -0,0 +1,27 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + http://www.contoso.com/trackback.cgi?id=1225c695-cfb8-4ebb-aaaa-80da344efa6a + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/trackback-ping-outside-entry.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/trackback-ping-outside-entry.xml new file mode 100644 index 0000000000..e1b5b8ba2b --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/trackback-ping-outside-entry.xml @@ -0,0 +1,27 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + http://www.contoso.com/trackback.cgi + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/type-html.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/type-html.xml new file mode 100644 index 0000000000..380c78907d --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/type-html.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + Some content. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/type-multipart-alternative.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/type-multipart-alternative.xml new file mode 100644 index 0000000000..69fd026e35 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/type-multipart-alternative.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + Some content. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/type-text-html.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/type-text-html.xml new file mode 100644 index 0000000000..d9af21d758 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/type-text-html.xml @@ -0,0 +1,35 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + <html> + <head> + <title>Some title.</title> + </head> + <body> + Some content. + </body> + </html> + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/type-text.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/type-text.xml new file mode 100644 index 0000000000..ef1fc79996 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/type-text.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + Some content. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/type-xhtml.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/type-xhtml.xml new file mode 100644 index 0000000000..559cfffa66 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/type-xhtml.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. +
Some content.
+
+ +
diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/type-xml.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/type-xml.xml new file mode 100644 index 0000000000..ac685c42e0 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/type-xml.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + Some content. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/undeterminable-vocabulary.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/undeterminable-vocabulary.xml new file mode 100644 index 0000000000..8cddf8303d --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/undeterminable-vocabulary.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + Test + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/undeterminable-vocabulary2.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/undeterminable-vocabulary2.xml new file mode 100644 index 0000000000..b73e7107d0 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/undeterminable-vocabulary2.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + Test + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/undeterminable-vocabulary3.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/undeterminable-vocabulary3.xml new file mode 100644 index 0000000000..f3c2921937 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/undeterminable-vocabulary3.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + Test + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/unregistered-rel.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/unregistered-rel.xml new file mode 100644 index 0000000000..26d7a3d049 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/unregistered-rel.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/updated-example3.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/updated-example3.xml new file mode 100644 index 0000000000..8a9b3da2d6 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/updated-example3.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02+01:00 + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/valid-xml-base-iri.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/valid-xml-base-iri.xml new file mode 100644 index 0000000000..942a4f97b1 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/valid-xml-base-iri.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-author-uri.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-author-uri.xml new file mode 100644 index 0000000000..6b48c6f44b --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-author-uri.xml @@ -0,0 +1,28 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + http://contoso.com/ + + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-category-scheme.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-category-scheme.xml new file mode 100644 index 0000000000..3112e31746 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-category-scheme.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-content-src.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-content-src.xml new file mode 100644 index 0000000000..bd03af8848 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-content-src.xml @@ -0,0 +1,26 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-entry-id.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-entry-id.xml new file mode 100644 index 0000000000..cbcda6160e --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-entry-id.xml @@ -0,0 +1,27 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-entry-published.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-entry-published.xml new file mode 100644 index 0000000000..efb613d724 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-entry-published.xml @@ -0,0 +1,28 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + + 2003-12-13T12:17:23Z + + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-entry-updated.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-entry-updated.xml new file mode 100644 index 0000000000..f11a8565e0 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-entry-updated.xml @@ -0,0 +1,27 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + + 2003-12-13T18:30:02Z + + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-feed-icon.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-feed-icon.xml new file mode 100644 index 0000000000..607153f930 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-feed-icon.xml @@ -0,0 +1,28 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + + http://contoso.com/icon.jpg + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-feed-id.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-feed-id.xml new file mode 100644 index 0000000000..c6b8189d5c --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-feed-id.xml @@ -0,0 +1,27 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-feed-logo.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-feed-logo.xml new file mode 100644 index 0000000000..755ce9992d --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-feed-logo.xml @@ -0,0 +1,28 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + http://www.contoso.com/logo.gif + + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-feed-updated.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-feed-updated.xml new file mode 100644 index 0000000000..6f2f122c19 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-feed-updated.xml @@ -0,0 +1,27 @@ + + + + + + Example Feed + + + 2003-12-13T18:30:02Z + + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-generator-uri.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-generator-uri.xml new file mode 100644 index 0000000000..8bf0d4b4a1 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-generator-uri.xml @@ -0,0 +1,29 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + Example Toolkit + + + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-link-href.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-link-href.xml new file mode 100644 index 0000000000..7e040ba37e --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-link-href.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-link-rel.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-link-rel.xml new file mode 100644 index 0000000000..832b93de9c --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/ws-link-rel.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/xhtml-extensions.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/xhtml-extensions.xml new file mode 100644 index 0000000000..aa3e669498 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/xhtml-extensions.xml @@ -0,0 +1,23 @@ + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/xml-base-ambiguous.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/xml-base-ambiguous.xml new file mode 100644 index 0000000000..a876db673c --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/xml-base-ambiguous.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/xml-base-elem-ne-doc.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/xml-base-elem-ne-doc.xml new file mode 100644 index 0000000000..7b8e968396 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/xml-base-elem-ne-doc.xml @@ -0,0 +1,27 @@ + + + + + + Example Feed + + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/xml-base.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/xml-base.xml new file mode 100644 index 0000000000..186b2ec098 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/xml-base.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/xml-lang-blank.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/xml-lang-blank.xml new file mode 100644 index 0000000000..5b65cf2153 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/xml-lang-blank.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/xml-lang.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/xml-lang.xml new file mode 100644 index 0000000000..91433d981a --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/xml-lang.xml @@ -0,0 +1,25 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/zero-entries.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/zero-entries.xml new file mode 100644 index 0000000000..40bd3c6fc9 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/zero-entries.xml @@ -0,0 +1,17 @@ + + + + + + Example Feed + + 2003-12-13T18:30:02Z + + Author Name + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/FailureFeeds/diff_atom_ns.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/FailureFeeds/diff_atom_ns.xml new file mode 100644 index 0000000000..fe09aa6fb2 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/FailureFeeds/diff_atom_ns.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/FailureFeeds/diff_rss_ns.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/FailureFeeds/diff_rss_ns.xml new file mode 100644 index 0000000000..b64e23bd28 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/FailureFeeds/diff_rss_ns.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/FailureFeeds/diff_rss_version.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/FailureFeeds/diff_rss_version.xml new file mode 100644 index 0000000000..40046d7798 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/FailureFeeds/diff_rss_version.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/FailureFeeds/no_rss_version.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/FailureFeeds/no_rss_version.xml new file mode 100644 index 0000000000..83315feb74 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/FailureFeeds/no_rss_version.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssEntry.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssEntry.xml new file mode 100644 index 0000000000..817f34307a --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssEntry.xml @@ -0,0 +1,13 @@ + + WCF and eBay + http://contoso.com/rss + Thu, 12 Oct 2006 16:20:00 Z + 91d46819-8472-40ad-a661-2c78acb4018c:820210 + http://contoso.com/test + + <P>eBay provides a very rich, though very complicated, <A class="" href="http://contoso.com/developercenter/soap" mce_href="http://contoso.com/developercenter/soap">SOAP API</A>.&nbsp; It is possible to use WCF with eBay, but it takes a few tricks.&nbsp; First read <A class="" href="http://contoso.com/2006/04/18/21943.aspx" mce_href="http://contoso.com/2006/04/18/21943.aspx">this</A> article.&nbsp; I'll wait.&nbsp; Ok, since that didn't scare you away I'll continue.&nbsp; Everything there is still necessary except the custom message encoder for removing the quotes around the content type.&nbsp; In fact the basicHttpBinding produced by svcutil.exe is just fine, though bumping up some of the quotas (<FONT size=2>maxNameTableCharCount, <FONT size=2>maxBufferSize, <FONT size=2>maxReceivedMessageSize)</FONT></FONT></FONT>&nbsp;might not hurt.&nbsp; Now calling GeteBayOfficialTime just works, but graduating to say GetItem, there's another problem.&nbsp; GetItem returns a GetItemResponseType which derives from AbstractResponseType.&nbsp; After successfully calling GetItemResponseType, all the fields it inherits are correctly populated but the fields it defines haven't been.&nbsp; Looking at the response on the wire, those fields are there, for some reason they weren't deserialized.&nbsp; The problem is eBay defined the schema for AbstractResponseType with an xs:any, so the code generated for this type includes an array of XmlElements with the XmlAny attribute:</P><FONT size=2> + <P>[System.Xml.Serialization.XmlAnyAttribute(Order=12)] </P> + <P></FONT><FONT color=#0000ff size=2>public</FONT><FONT size=2> System.Xml.XmlElement[] Any</FONT></P> + <P><FONT size=2>When the response from eBay arrives the XmlSerializer happily begins populating all the fields in AbstractResponseType and anything it doesn't recognize gets put into Any.&nbsp; Now when it's time to populate the fields defined in GetItemResponseType there's no xml left.&nbsp; Given this knowledge it's an easy fix, but unfortunately it involves manually editing the generated proxy.&nbsp; All that's necessary is to change the XmlAnyAttribute to an XmlIgnoreAttribute.&nbsp; Now upon receiving the response, the XmlSerializer "saves" any elements that don't map to AbstractResponseType and uses them to populate GetItemResponseType.</P></FONT><img src="http://contoso.com/images" width="1" height="1"> + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/blank_title.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/blank_title.xml new file mode 100644 index 0000000000..aa8a5ed64b --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/blank_title.xml @@ -0,0 +1,12 @@ + + + + + +http://contoso.com/rss/2.0/ +channel title must not be blank + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/cloud_domain.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/cloud_domain.xml new file mode 100644 index 0000000000..0cf876333b --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/cloud_domain.xml @@ -0,0 +1,13 @@ + + + + +Invalid cloud +http://contoso.com/rss/2.0/ +foo + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/cloud_path.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/cloud_path.xml new file mode 100644 index 0000000000..2e562801fc --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/cloud_path.xml @@ -0,0 +1,13 @@ + + + + +Invalid cloud +http://contoso.com/rss/2.0/ +foo + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/cloud_port.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/cloud_port.xml new file mode 100644 index 0000000000..2ffa20ab9f --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/cloud_port.xml @@ -0,0 +1,13 @@ + + + + +Invalid cloud +http://contoso.com/rss/2.0/ +foo + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/cloud_port_integer.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/cloud_port_integer.xml new file mode 100644 index 0000000000..7a9ce6d604 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/cloud_port_integer.xml @@ -0,0 +1,13 @@ + + + + +Invalid cloud +http://contoso.com/rss/2.0/ +foo + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/cloud_protocol.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/cloud_protocol.xml new file mode 100644 index 0000000000..3127348a54 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/cloud_protocol.xml @@ -0,0 +1,13 @@ + + + + +Invalid cloud +http://contoso.com/rss/2.0/ +foo + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/cloud_registerprocedure.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/cloud_registerprocedure.xml new file mode 100644 index 0000000000..afe49db5af --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/cloud_registerprocedure.xml @@ -0,0 +1,13 @@ + + + + +Invalid cloud +http://contoso.com/rss/2.0/ +foo + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/contains_language.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/contains_language.xml new file mode 100644 index 0000000000..f487c05ad4 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/contains_language.xml @@ -0,0 +1,13 @@ + + + + +Missing dc:language +http://contoso.com/rss/2.0/ +channel should include dc:language +en-us + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/docs.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/docs.xml new file mode 100644 index 0000000000..e6c825d7ff --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/docs.xml @@ -0,0 +1,13 @@ + + + + + Valid docs + http://contoso.com/rss/2.0/ + foo + http://contoso.com/rss/doc + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/empty_author_rss.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/empty_author_rss.xml new file mode 100644 index 0000000000..a880eac716 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/empty_author_rss.xml @@ -0,0 +1,8 @@ + + + +<description /> + +<a10:author /> + +</channel></rss> \ No newline at end of file diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/empty_category.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/empty_category.xml new file mode 100644 index 0000000000..aa8e99a98b --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/empty_category.xml @@ -0,0 +1,7 @@ +<rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0"> +<channel> +<title /> +<description /> +<category /> + +</channel></rss> \ No newline at end of file diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/funky_content_encoded.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/funky_content_encoded.xml new file mode 100644 index 0000000000..f683325668 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/funky_content_encoded.xml @@ -0,0 +1,19 @@ +<!-- + Description: Funky content + Expect: !DuplicateDescriptionSemantics{element:content:encoded} +--> + +<rss version="2.0" xmlns:content="http://contoso.com/rss/1.0/modules/content/"> +<channel> +<title>Duplicate semantics +http://contoso.com/rss/2.0/ +Funky content + +Duplicate semantics +http://contoso.com/rss/2.0/?item +uuid:43481a10-d881-40d1-adf2-99b438c57e21;id=1 +Foo +Bar + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/language.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/language.xml new file mode 100644 index 0000000000..7a5548e315 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/language.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid language +en + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/language_country_code.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/language_country_code.xml new file mode 100644 index 0000000000..16ff646362 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/language_country_code.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid language +en-us + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/language_iso6392.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/language_iso6392.xml new file mode 100644 index 0000000000..04b640088e --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/language_iso6392.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid language +UND + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/link.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/link.xml new file mode 100644 index 0000000000..e7f0fe0d30 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/link.xml @@ -0,0 +1,12 @@ + + + + +Valid link +http://contoso.com/rss/2.0/ +valid channel link (http) + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/no_guid.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/no_guid.xml new file mode 100644 index 0000000000..03d0912959 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/no_guid.xml @@ -0,0 +1,16 @@ + + + + +Valid link +valid item link (http) +http://contoso.com/rss/2.0/ + +Valid link +http://contoso.com/rss/2.0/#item + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate.xml new file mode 100644 index 0000000000..e9a384e94d --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate +Tue, 31 Dec 2002 14:20:20 GMT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_a.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_a.xml new file mode 100644 index 0000000000..7cd665191f --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_a.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +problematical pubDate (A) +31 Dec 2002 14:20:20 A + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_april.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_april.xml new file mode 100644 index 0000000000..7bf57dd99a --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_april.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (April) +10 Apr 2002 14:20:20 GMT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_august.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_august.xml new file mode 100644 index 0000000000..dd53a82b36 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_august.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (August) +10 Aug 2002 14:20:20 GMT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_cst.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_cst.xml new file mode 100644 index 0000000000..c1c709ecbc --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_cst.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (CST) +31 Dec 2002 14:20:20 CST + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_edt.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_edt.xml new file mode 100644 index 0000000000..b5b91271d3 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_edt.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (EDT) +31 Dec 2002 14:20:20 EDT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_est.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_est.xml new file mode 100644 index 0000000000..e701342395 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_est.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (EST) +31 Dec 2002 14:20:20 EST + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_february.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_february.xml new file mode 100644 index 0000000000..d959a84a75 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_february.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (February) +10 Feb 2002 14:20:20 GMT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_friday.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_friday.xml new file mode 100644 index 0000000000..7ebdff98fb --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_friday.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (Friday) +Fri, 27 Dec 2002 14:20:20 GMT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_january.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_january.xml new file mode 100644 index 0000000000..1acf842d0e --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_january.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (January) +10 Jan 2002 14:20:20 GMT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_july.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_july.xml new file mode 100644 index 0000000000..962eaf9046 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_july.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (July) +10 Jul 2002 14:20:20 GMT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_june.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_june.xml new file mode 100644 index 0000000000..cc15d4a045 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_june.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (June) +10 Jun 2002 14:20:20 GMT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_march.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_march.xml new file mode 100644 index 0000000000..2b510bf7cb --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_march.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (March) +10 Mar 2002 14:20:20 GMT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_may.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_may.xml new file mode 100644 index 0000000000..2329b07f3e --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_may.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (May) +10 May 2002 14:20:20 GMT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_mdt.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_mdt.xml new file mode 100644 index 0000000000..3bb4c90f5f --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_mdt.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (MDT) +31 Dec 2002 14:20:20 MDT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_mst.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_mst.xml new file mode 100644 index 0000000000..8999f422bf --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_mst.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (MST) +31 Dec 2002 14:20:20 MST + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_no_seconds.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_no_seconds.xml new file mode 100644 index 0000000000..1642abb872 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_no_seconds.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate +21 Oct 2002 06:45 EDT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_no_weekday.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_no_weekday.xml new file mode 100644 index 0000000000..93fdecd1e3 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_no_weekday.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (no weekday) +31 Dec 2002 14:20:20 GMT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_november.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_november.xml new file mode 100644 index 0000000000..4a7830618c --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_november.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (November) +10 Nov 2002 14:20:20 GMT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_october.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_october.xml new file mode 100644 index 0000000000..aa85ab2177 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_october.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (October) +10 Oct 2002 14:20:20 GMT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_one_digit_day.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_one_digit_day.xml new file mode 100644 index 0000000000..3d04db9bf2 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_one_digit_day.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (1-digit day) +1 Dec 2002 14:20:20 +0000 + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_pdt.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_pdt.xml new file mode 100644 index 0000000000..86c8c00671 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_pdt.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (PDT) +31 Dec 2002 14:20:20 PDT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_pre_y2k.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_pre_y2k.xml new file mode 100644 index 0000000000..6183de1498 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_pre_y2k.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +problematical pubDate (2-digit year) +31 Dec 99 23:59:59 +0000 + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_pst.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_pst.xml new file mode 100644 index 0000000000..4e2a00b90c --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_pst.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (PST) +31 Dec 2002 14:20:20 PST + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_september.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_september.xml new file mode 100644 index 0000000000..951e3dba7e --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_september.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (September) +10 Sep 2002 14:20:20 GMT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_thursday.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_thursday.xml new file mode 100644 index 0000000000..63ef1121a8 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_thursday.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (Thursday) +Thu, 26 Dec 2002 14:20:20 GMT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_tuesday.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_tuesday.xml new file mode 100644 index 0000000000..ae8f1752e4 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_tuesday.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (Tuesday) +Tue, 31 Dec 2002 14:20:20 GMT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_ut.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_ut.xml new file mode 100644 index 0000000000..b586aca761 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_ut.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (UT) +31 Dec 2002 14:20:20 UT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_wednesday.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_wednesday.xml new file mode 100644 index 0000000000..53ff61dcf1 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_wednesday.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (Wednesday) +Wed, 25 Dec 2002 14:20:20 GMT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_with_diff_min.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_with_diff_min.xml new file mode 100644 index 0000000000..d5e7a6dbdc --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_with_diff_min.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +problematic pubDate (with minutes in differential) +31 Dec 2002 14:20:20 -0234 + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_with_differential.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_with_differential.xml new file mode 100644 index 0000000000..281f4758d3 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_with_differential.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (with differential) +31 Dec 2002 14:20:20 +0000 + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_with_differential2.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_with_differential2.xml new file mode 100644 index 0000000000..dfa665d51c --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_with_differential2.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (with differential) +Mon, 21 Oct 2002 14:04:23 -0500 + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_z.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_z.xml new file mode 100644 index 0000000000..4926a66249 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/pubdate_z.xml @@ -0,0 +1,13 @@ + + + + +Validity test +http://contoso.com/rss/2.0/ +valid pubDate (Z) +31 Dec 2002 14:20:20 Z + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/skipDays.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/skipDays.xml new file mode 100644 index 0000000000..9bf2667135 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/skipDays.xml @@ -0,0 +1,15 @@ + + + + +RSS 2.0 test case +http://www.contoso.com/testcases/rss20/ +skipDays contains 1 valid day + +Monday + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/skipDays_7_days.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/skipDays_7_days.xml new file mode 100644 index 0000000000..339c057022 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/skipDays_7_days.xml @@ -0,0 +1,21 @@ + + + + +RSS 2.0 test case +http://www.contoso.com/testcases/rss20/ +skipDays contains 7 valid days + +Friday +Monday +Wednesday +Thursday +Sunday +Saturday +Tuesday + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/skipHours.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/skipHours.xml new file mode 100644 index 0000000000..471e9e6f15 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/skipHours.xml @@ -0,0 +1,15 @@ + + + + +RSS 2.0 test case +http://www.contoso.com/testcases/rss20/ +skipHours contains 1 valid hour + +12 + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/textInput_description.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/textInput_description.xml new file mode 100644 index 0000000000..d438dc6142 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/textInput_description.xml @@ -0,0 +1,18 @@ + + + + +RSS 2.0 test case +http://www.contoso.com/testcases/rss20/ +textInput contains description + +Search Slashdot +query +http://contoso.com/test +Foo + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/textInput_name.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/textInput_name.xml new file mode 100644 index 0000000000..0c433b4ad7 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/textInput_name.xml @@ -0,0 +1,18 @@ + + + + +RSS 2.0 test case +http://www.contoso.com/testcases/rss20/ +textInput contains name + +Search Slashdot +Search Slashdot stories +http://contoso.com/test +query + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/textInput_title.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/textInput_title.xml new file mode 100644 index 0000000000..59b218d5e6 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/textInput_title.xml @@ -0,0 +1,18 @@ + + + + +RSS 2.0 test case +http://www.contoso.com/testcases/rss20/ +textInput contains title + +Search Slashdot +Search Slashdot stories +query +http://contoso.com/test + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/textInput_valid_link.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/textInput_valid_link.xml new file mode 100644 index 0000000000..ccd108332a --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/textInput_valid_link.xml @@ -0,0 +1,18 @@ + + + + +RSS 2.0 test case +http://www.contoso.com/testcases/rss20/ +textInput contains fully qualified http:// link + +Search Slashdot +Search Slashdot stories +query +http://contoso.com/test + + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/ttl.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/ttl.xml new file mode 100644 index 0000000000..2f77482e01 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/ttl.xml @@ -0,0 +1,13 @@ + + + + +RSS 2.0 test case +http://www.contoso.com/testcases/rss20/ +ttl is positive integer +60 + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/valid_lastBuildDate.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/valid_lastBuildDate.xml new file mode 100644 index 0000000000..8cdb2f6106 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/valid_lastBuildDate.xml @@ -0,0 +1,13 @@ + + + + +Invalid date format +http://contoso.com/rss/2.0/ +lastBuildDate must be RFC 2822 date format +Tue, 31 Dec 2002 14:20:20 GMT + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/valid_managingEditor.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/valid_managingEditor.xml new file mode 100644 index 0000000000..1b81c8f309 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/valid_managingEditor.xml @@ -0,0 +1,13 @@ + + + + +Invalid webMaster +http://contoso.com/rss/2.0/ +managingEditor must include email address +editor@contoso.com + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/valid_title.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/valid_title.xml new file mode 100644 index 0000000000..5efc1ceecc --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/valid_title.xml @@ -0,0 +1,12 @@ + + + + +Valid title +http://contoso.com/rss/2.0/ +channel title must not include HTML + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/valid_webMaster.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/valid_webMaster.xml new file mode 100644 index 0000000000..9a0e9aaae2 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/valid_webMaster.xml @@ -0,0 +1,13 @@ + + + + +Invalid webMaster +http://contoso.com/rss/2.0/ +webMaster must include email address +webmastere@contoso.com + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/webMaster_name_and_email.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/webMaster_name_and_email.xml new file mode 100644 index 0000000000..d2db50bc33 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssFeeds/webMaster_name_and_email.xml @@ -0,0 +1,13 @@ + + + + +Invalid webMaster +http://contoso.com/rss/2.0/ +webMaster must include email address +webmastere@contoso.com + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssSpecCustomParser.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssSpecCustomParser.xml new file mode 100644 index 0000000000..c90436a4f7 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/RssSpecCustomParser.xml @@ -0,0 +1,26 @@ + + + + FeedTitle + FeedLink + FeedDescription + FeedLanguage + Tue, 23 August 2016 16:08:00 EDT + http://blogs.law.harvard.edu/tech/rss + FeedGenerator + FeedEditorEmail + FeedCategory + FeedCopyright + 60 + + ImageUrl + + + ItemTitle + ItemLink + ItemDescription + Tue, 03 Jun 2003 09:39:21 GMT + ItemGuid + + + \ No newline at end of file diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/SimpleAtomFeedCustomParser.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/SimpleAtomFeedCustomParser.xml new file mode 100644 index 0000000000..e6237940dc --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/SimpleAtomFeedCustomParser.xml @@ -0,0 +1,23 @@ + + + FeedTitle + FeedSubtitle + FeedID + 2017-06-26T14:41:43-07:00 + FeedLogo + FeedGenerator + + AuthorName + author@Contoso.com + AuthorUri + + + asd + + EntryId + SyndicationFeed released for .net Core + 2017-06-26T21:41:43Z + + + + \ No newline at end of file diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/atom_feeds.dat b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/atom_feeds.dat new file mode 100644 index 0000000000..05f07efdc7 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/atom_feeds.dat @@ -0,0 +1,175 @@ +absolute_rel.xml +alternate-no-content.xml +author-at-entry-only.xml +author-at-feed-and-entry.xml +author-at-feed-only.xml +authorless-with-no-entries.xml +authorless-with-one-entry.xml +category-label-escaped-html.xml +category-no-label.xml +category-no-scheme.xml +category-scheme-invalid-iri.xml +category-scheme-rel-iri.xml +category-entry-ext.xml +category-feed-ext.xml +category-feed.xml +contains-email.xml +content-application-xthml.xml +content-base64-no-summary.xml +content-jpeg-invalid-base64.xml +content-jpeg-valid-base64.xml +content-no-alternate.xml +content-no-type-escaped-html.xml +content-plain-with-children.xml +content-src-no-summary.xml +content-src-no-type-no-error.xml +content-src-no-type.xml +content-src-relative-ref.xml +content-src-type-html.xml +content-src-type-text-html.xml +content-src-type-text.xml +content-src-type-xhtml.xml +content-src.xml +content-text-html.xml +content-xhtml-escaped.xml +content-xhtml-mixed.xml +content-xhtml-no-xhtml-div.xml +content-xhtml-notmarkup.xml +content-xhtml-text-children.xml +duplicate-entries.xml +email-rss20-style.xml +email-with-name.xml +email-with-plus.xml +empty-content.xml +empty-title.xml +empty_author.xml +escaped_text.xml +example_html_title.xml +example_text_title.xml +example_xhtml_summary1.xml +example_xhtml_summary2.xml +example_xhtml_summary3.xml +extension-unknown-noerror.xml +extensive-noerror.xml +extensive-nowarn.xml +generator-escaped-html.xml +generator-no-text.xml +generator_relative_ref.xml +icon_invalid_uri.xml +icon_relative_ref.xml +id-dot-segments.xml +id-empty-fragment-id.xml +id-empty-path.xml +id-empty-query.xml +id-explicit-authority.xml +id-explicit-default-port.xml +id-host-uppercase.xml +id-noniana.xml +id-not-uri.xml +id-percent-encoded-lower.xml +id-percent-encoded.xml +id-relative-uri.xml +id-uppercase-scheme.xml +id-valid-tag-uris.xml +infoset-attr-order.xml +infoset-char-ref.xml +infoset-element-whitespace.xml +infoset-empty1.xml +infoset-empty2.xml +infoset-quote-single.xml +invalid-email.xml +invalid-uri.xml +invalid-xml-base.xml +invalid-xml-lang.xml +invalid_html.xml +license-noerror.xml +link-extensions.xml +link-full-uri.xml +link-href-relative.xml +link-hreflang-invalid-language.xml +link-rel-full.xml +link-rel-iana.xml +link-rel-isegment-nz-nc.xml +link-rel-relative.xml +link-same-rel-different-types.xml +link-same-rel-type-different-hreflang.xml +link-same-rel-type-hreflang.xml +link-same-rel-type-no-hreflang.xml +link-title-with-badchars.xml +link-title-with-html.xml +link-type-invalid-mime.xml +link-type-parameters.xml +logo_relative_ref.xml +missing-id-source.xml +missing-self.xml +missing-title-source.xml +missing-updated-source.xml +missing_xhtml_div.xml +missing_xhtml_ns.xml +multi-enclosure-test.xml +multiple-alternates-differing-source.xml +multiple-alternates-differing.xml +multiple-alternates-matching-source.xml +multiple-alternates-matching.xml +multiple-authors-source.xml +multiple-authors.xml +multiple-categories-entry.xml +multiple-categories-source.xml +multiple-categories.xml +multiple-contributors-entry.xml +multiple-contributors-source.xml +multiple-contributors.xml +multiple-icons-source.xml +multiple-icons.xml +multiple-related-matching.xml +no-content-or-alternate.xml +no-content-or-summary.xml +no-name.xml +prefixed-namespace.xml +rdf-extensions.xml +rel-colon.xml +related-same-rel-type-hreflang.xml +relative-ref.xml +rights-text-with-escaped-html.xml +rights-xhtml-no-xmldiv.xml +self-vs-alternate.xml +subtitle-blank.xml +summary-content-base64.xml +summary-content-src.xml +text_with_escaped_html.xml +title-blank.xml +trackback-about-entry.xml +trackback-ping-entry.xml +trackback-ping-outside-entry.xml +type-html.xml +type-multipart-alternative.xml +type-text-html.xml +type-text.xml +type-xhtml.xml +type-xml.xml +undeterminable-vocabulary.xml +undeterminable-vocabulary2.xml +undeterminable-vocabulary3.xml +unregistered-rel.xml +updated-example3.xml +valid-xml-base-iri.xml +ws-author-uri.xml +ws-category-scheme.xml +ws-content-src.xml +ws-entry-id.xml +ws-entry-published.xml +ws-entry-updated.xml +ws-feed-icon.xml +ws-feed-id.xml +ws-feed-logo.xml +ws-feed-updated.xml +ws-generator-uri.xml +ws-link-href.xml +ws-link-rel.xml +xhtml-extensions.xml +xml-base-ambiguous.xml +xml-base-elem-ne-doc.xml +xml-base.xml +xml-lang-blank.xml +xml-lang.xml +zero-entries.xml diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/brief-entry-noerror.xml b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/brief-entry-noerror.xml new file mode 100644 index 0000000000..954af164b1 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/brief-entry-noerror.xml @@ -0,0 +1,17 @@ + + + + + Atom-Powered Robots Run Amok + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + John Doe + + diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/rss_feeds.dat b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/rss_feeds.dat new file mode 100644 index 0000000000..cd3d483ca4 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/TestFeeds/rss_feeds.dat @@ -0,0 +1,63 @@ +blank_title.xml +cloud_domain.xml +cloud_path.xml +cloud_port.xml +cloud_port_integer.xml +cloud_protocol.xml +cloud_registerprocedure.xml +contains_language.xml +docs.xml +empty_author_rss.xml +empty_category.xml +funky_content_encoded.xml +language.xml +language_country_code.xml +language_iso6392.xml +link.xml +no_guid.xml +pubdate.xml +pubdate_a.xml +pubdate_april.xml +pubdate_august.xml +pubdate_cst.xml +pubdate_edt.xml +pubdate_est.xml +pubdate_february.xml +pubdate_friday.xml +pubdate_january.xml +pubdate_july.xml +pubdate_june.xml +pubdate_march.xml +pubdate_may.xml +pubdate_mdt.xml +pubdate_mst.xml +pubdate_november.xml +pubdate_no_seconds.xml +pubdate_no_weekday.xml +pubdate_october.xml +pubdate_one_digit_day.xml +pubdate_pdt.xml +pubdate_pre_y2k.xml +pubdate_pst.xml +pubdate_september.xml +pubdate_thursday.xml +pubdate_tuesday.xml +pubdate_ut.xml +pubdate_wednesday.xml +pubdate_with_differential.xml +pubdate_with_differential2.xml +pubdate_with_diff_min.xml +pubdate_z.xml +skipDays.xml +skipDays_7_days.xml +skipHours.xml +textInput_description.xml +textInput_name.xml +textInput_title.xml +textInput_valid_link.xml +ttl.xml +valid_lastBuildDate.xml +valid_managingEditor.xml +valid_title.xml +valid_webMaster.xml +webMaster_name_and_email.xml diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/Utils/CompareHelper.cs b/external/corefx/src/System.ServiceModel.Syndication/tests/Utils/CompareHelper.cs new file mode 100644 index 0000000000..eb53804731 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/Utils/CompareHelper.cs @@ -0,0 +1,130 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; + +namespace System.ServiceModel.Syndication.Tests +{ + public class AllowableDifference + { + public AllowableDifference(string file1Value, string file2Value) + { + File1Value = file1Value; + File2Value = file2Value; + } + + public string File1Value; + public string File2Value; + } + + public class CompareHelper + { + private List _allowableDifferences = null; + + private XmlDiff _diff; + + public List AllowableDifferences + { + get + { + return _allowableDifferences; + } + set + { + _allowableDifferences = value; + } + } + + public XmlDiff Diff + { + get + { + return _diff; + } + set + { + _diff = value; + } + } + public bool Compare(string source, string target, out string diffNode) + { + diffNode = string.Empty; + StringBuilder stringBuilder = new StringBuilder(); + if (_diff.Compare(source, target)) + { + return true; + } + else + { + XmlDocument diffDoc = new XmlDocument(); + diffDoc.LoadXml(_diff.ToXml()); + XmlNodeList totalFailures = diffDoc.SelectNodes("/Root/Node/Diff[@DiffType]"); + XmlNodeList attrFailures = diffDoc.SelectNodes("/Root/Node/Diff[@DiffType=6]|/Root/Node/Diff[@DiffType=5]|/Root/Node/Diff[@DiffType=1]"); + + if (attrFailures.Count == totalFailures.Count) + { + //Check all different Nodes except allowable Nodes + bool allFailuresAllowed = true; + foreach (XmlNode node in attrFailures) + { + if (!IsAllowableFailure(node)) + { + allFailuresAllowed = false; + stringBuilder.AppendLine(node.InnerText); + break; + } + } + diffNode = stringBuilder.ToString(); + return allFailuresAllowed; + } + else + { + //get all different Nodes + foreach (XmlNode node in totalFailures) + { + stringBuilder.AppendLine(node.InnerText); + } + diffNode = stringBuilder.ToString(); + return false; + } + } + } + + private bool IsAllowableFailure(XmlNode failedNode) + { + //DiffType="1" is vs. , ignore + XmlAttribute diffType; + if ((diffType = failedNode.Attributes["DiffType"]) != null) + { + if (diffType.Value == "1") return true; + } + if (failedNode.ChildNodes.Count < 2) throw new ArgumentException("Unexpected node structure", "failedNode"); + if (AllowableDifferences == null) return false; + + // XmlDiff flags closing tags as different when the open tags were different + // this flags those since they aren't interesting failures + if (failedNode.ChildNodes[0].InnerText.StartsWith(" each in advOptions.AddedNamespaces) + { + xnm.AddNamespace(each.Key, each.Value); + } + + expr.SetContext(xnm); + } + + return expr; + } + + + private bool Diff() + { + bool flag = false; + _Writer = new XmlTextWriter(new StringWriter(_Output)); + _Writer.WriteStartElement(String.Empty, "Root", String.Empty); + + flag = CompareChildren(_SourceDoc, _TargetDoc); + + _Writer.WriteEndElement(); + _Writer.Close(); + return flag; + } + + // This function is being called recursively to compare the children of a certain node. + // When calling this function the navigator should be pointing at the parent node whose children + // we wants to compare and return the navigator back to the same node before exiting from it. + private bool CompareChildren(XmlDiffNode sourceNode, XmlDiffNode targetNode) + { + XmlDiffNode sourceChild = sourceNode.FirstChild; + XmlDiffNode targetChild = targetNode.FirstChild; + + bool flag = true; + bool tempFlag = true; + + DiffType result; + + // Loop to compare all the child elements of the parent node + while (sourceChild != null || targetChild != null) + { + //Console.WriteLine( (sourceChild!=null)?(sourceChild.NodeType.ToString()):"null" ); + //Console.WriteLine( (targetChild!=null)?(targetChild.NodeType.ToString()):"null" ); + if (sourceChild != null) + { + if (targetChild != null) + { + // Both Source and Target Read successful + if ((result = CompareNodes(sourceChild, targetChild)) == DiffType.Success) + { + // Child nodes compared successfully, write out the result + WriteResult(sourceChild, targetChild, DiffType.Success); + // Check whether this Node has Children, if it does call CompareChildren recursively + if (sourceChild.FirstChild != null) + { + tempFlag = CompareChildren(sourceChild, targetChild); + } + else if (targetChild.FirstChild != null) + { + WriteResult(null, targetChild, DiffType.TargetExtra); + tempFlag = false; + } + // set the compare flag + flag = (flag && tempFlag); + + // Writing out End Element to the result + if (sourceChild.NodeType == XmlDiffNodeType.Element && !(sourceChild is XmlDiffEmptyElement)) + { + XmlDiffElement sourceElem = sourceChild as XmlDiffElement; + XmlDiffElement targetElem = targetChild as XmlDiffElement; + Debug.Assert(sourceElem != null); + Debug.Assert(targetElem != null); + if (!DontWriteMatchingNodesToOutput && !DontWriteAnythingToOutput) + { + _Writer.WriteStartElement(String.Empty, "Node", String.Empty); + _Writer.WriteAttributeString(String.Empty, "SourceLineNum", String.Empty, sourceElem.EndLineNumber.ToString()); + _Writer.WriteAttributeString(String.Empty, "SourceLinePos", String.Empty, sourceElem.EndLinePosition.ToString()); + _Writer.WriteAttributeString(String.Empty, "TargetLineNum", String.Empty, targetElem.EndLineNumber.ToString()); + _Writer.WriteAttributeString(String.Empty, "TargetLinePos", String.Empty, targetElem.EndLinePosition.ToString()); + _Writer.WriteStartElement(String.Empty, "Diff", String.Empty); + _Writer.WriteEndElement(); + + _Writer.WriteStartElement(String.Empty, "Lexical-equal", String.Empty); + _Writer.WriteCData(""); + _Writer.WriteEndElement(); + _Writer.WriteEndElement(); + } + } + + // Move to Next child + sourceChild = sourceChild.NextSibling; + targetChild = targetChild.NextSibling; + } + else + { + // Child nodes not matched, start the recovery process + bool recoveryFlag = false; + + // Try to match the source node with the target nodes at the same level + XmlDiffNode backupTargetChild = targetChild.NextSibling; + while (!recoveryFlag && backupTargetChild != null) + { + if (CompareNodes(sourceChild, backupTargetChild) == DiffType.Success) + { + recoveryFlag = true; + do + { + WriteResult(null, targetChild, DiffType.TargetExtra); + targetChild = targetChild.NextSibling; + } while (targetChild != backupTargetChild); + break; + } + backupTargetChild = backupTargetChild.NextSibling; + } + + // If not recovered, try to match the target node with the source nodes at the same level + if (!recoveryFlag) + { + XmlDiffNode backupSourceChild = sourceChild.NextSibling; + while (!recoveryFlag && backupSourceChild != null) + { + if (CompareNodes(backupSourceChild, targetChild) == DiffType.Success) + { + recoveryFlag = true; + do + { + WriteResult(sourceChild, null, DiffType.SourceExtra); + sourceChild = sourceChild.NextSibling; + } while (sourceChild != backupSourceChild); + break; + } + backupSourceChild = backupSourceChild.NextSibling; + } + } + + // If still not recovered, write both of them as different nodes and move on + if (!recoveryFlag) + { + WriteResult(sourceChild, targetChild, result); + + // Check whether this Node has Children, if it does call CompareChildren recursively + if (sourceChild.FirstChild != null) + { + tempFlag = CompareChildren(sourceChild, targetChild); + } + else if (targetChild.FirstChild != null) + { + WriteResult(null, targetChild, DiffType.TargetExtra); + tempFlag = false; + } + + // Writing out End Element to the result + bool bSourceNonEmpElemEnd = (sourceChild.NodeType == XmlDiffNodeType.Element && !(sourceChild is XmlDiffEmptyElement)); + bool bTargetNonEmpElemEnd = (targetChild.NodeType == XmlDiffNodeType.Element && !(targetChild is XmlDiffEmptyElement)); + if (bSourceNonEmpElemEnd || bTargetNonEmpElemEnd) + { + XmlDiffElement sourceElem = sourceChild as XmlDiffElement; + XmlDiffElement targetElem = targetChild as XmlDiffElement; + if (!DontWriteAnythingToOutput) + { + _Writer.WriteStartElement(String.Empty, "Node", String.Empty); + _Writer.WriteAttributeString(String.Empty, "SourceLineNum", String.Empty, (sourceElem != null) ? sourceElem.EndLineNumber.ToString() : "-1"); + _Writer.WriteAttributeString(String.Empty, "SourceLinePos", String.Empty, (sourceElem != null) ? sourceElem.EndLinePosition.ToString() : "-1"); + _Writer.WriteAttributeString(String.Empty, "TargetLineNum", String.Empty, (targetElem != null) ? targetElem.EndLineNumber.ToString() : "-1"); + _Writer.WriteAttributeString(String.Empty, "TargetLinePos", String.Empty, (targetElem != null) ? targetElem.EndLineNumber.ToString() : "-1"); + _Writer.WriteStartElement(String.Empty, "Diff", String.Empty); + _Writer.WriteAttributeString(String.Empty, "DiffType", String.Empty, GetDiffType(result)); + if (bSourceNonEmpElemEnd) + { + _Writer.WriteStartElement(String.Empty, "File1", String.Empty); + _Writer.WriteCData(""); + _Writer.WriteEndElement(); + } + + if (bTargetNonEmpElemEnd) + { + _Writer.WriteStartElement(String.Empty, "File2", String.Empty); + _Writer.WriteCData(""); + _Writer.WriteEndElement(); + } + + _Writer.WriteEndElement(); + + _Writer.WriteStartElement(String.Empty, "Lexical-equal", String.Empty); + _Writer.WriteEndElement(); + _Writer.WriteEndElement(); + } + } + + sourceChild = sourceChild.NextSibling; + targetChild = targetChild.NextSibling; + } + + flag = false; + } + } + else + { + // SourceRead NOT NULL targetRead is NULL + WriteResult(sourceChild, null, DiffType.SourceExtra); + flag = false; + sourceChild = sourceChild.NextSibling; + } + } + else if (targetChild != null) + { + // SourceRead is NULL and targetRead is NOT NULL + WriteResult(null, targetChild, DiffType.TargetExtra); + flag = false; + targetChild = targetChild.NextSibling; + } + else + { + //Both SourceRead and TargetRead is NULL + Debug.Assert(false, "Impossible Situation for comparison"); + } + } + return flag; + } + + /// + /// Combines multiple adjacent text nodes into a single node. + /// Sometimes the binary reader/writer will break text nodes in multiple + /// nodes; i.e., if the user calls + /// writer.WriteString("foo"); writer.WriteString("bar") + /// on the writer and then iterate calling reader.Read() on the document, the binary + /// reader will report two text nodes ("foo", "bar"), while the + /// other readers will report a single text node ("foobar") – notice that + /// this is not a problem; calling ReadString, or Read[Element]ContentAsString in + /// all readers will return "foobar". + /// + /// the node to be normalized. Any siblings of + /// type XmlDiffCharacterData will be removed, with their values appended + /// to the value of this node + private void DoConcatenateAdjacentTextNodes(XmlDiffCharacterData node) + { + if (node.NextSibling == null) return; + if (!(node.NextSibling is XmlDiffCharacterData)) return; + StringBuilder sb = new StringBuilder(); + sb.Append(node.Value); + XmlDiffCharacterData nextNode = (node.NextSibling as XmlDiffCharacterData); + while (nextNode != null) + { + sb.Append(nextNode.Value); + XmlDiffCharacterData curNextNode = nextNode; + nextNode = (nextNode.NextSibling as XmlDiffCharacterData); + curNextNode.ParentNode.DeleteChild(curNextNode); + } + node.Value = sb.ToString(); + } + + // This function compares the two nodes passed to it, depending upon the options set by the user. + private DiffType CompareNodes(XmlDiffNode sourceNode, XmlDiffNode targetNode) + { + if (sourceNode.NodeType != targetNode.NodeType) + { + return DiffType.NodeType; + } + switch (sourceNode.NodeType) + { + case XmlDiffNodeType.Element: + XmlDiffElement sourceElem = sourceNode as XmlDiffElement; + XmlDiffElement targetElem = targetNode as XmlDiffElement; + Debug.Assert(sourceElem != null); + Debug.Assert(targetElem != null); + if (!IgnoreNS) + { + if (sourceElem.NamespaceURI != targetElem.NamespaceURI) + { + return DiffType.NS; + } + } + if (!IgnorePrefix) + { + if (sourceElem.Prefix != targetElem.Prefix) + { + return DiffType.Prefix; + } + } + + if (sourceElem.LocalName != targetElem.LocalName) + { + return DiffType.Element; + } + if (!IgnoreEmptyElement) + { + if ((sourceElem is XmlDiffEmptyElement) != (targetElem is XmlDiffEmptyElement)) + { + return DiffType.Element; + } + } + if (!CompareAttributes(sourceElem, targetElem)) + { + return DiffType.Attribute; + } + break; + case XmlDiffNodeType.Text: + case XmlDiffNodeType.Comment: + case XmlDiffNodeType.WS: + XmlDiffCharacterData sourceText = sourceNode as XmlDiffCharacterData; + XmlDiffCharacterData targetText = targetNode as XmlDiffCharacterData; + Debug.Assert(sourceText != null); + Debug.Assert(targetText != null); + + if (ConcatenateAdjacentTextNodes) + { + DoConcatenateAdjacentTextNodes(sourceText); + DoConcatenateAdjacentTextNodes(targetText); + } + + if (IgnoreWhitespace) + { + if (sourceText.Value.Trim() == targetText.Value.Trim()) + { + return DiffType.Success; + } + } + else + { + if (sourceText.Value == targetText.Value) + { + return DiffType.Success; + } + } + if (sourceText.NodeType == XmlDiffNodeType.Text || sourceText.NodeType == XmlDiffNodeType.WS) //should ws nodes also as text nodes??? + { + return DiffType.Text; + } + else if (sourceText.NodeType == XmlDiffNodeType.Comment) + { + return DiffType.Comment; + } + else if (sourceText.NodeType == XmlDiffNodeType.CData) + { + return DiffType.CData; + } + else + { + return DiffType.None; + } + case XmlDiffNodeType.PI: + XmlDiffProcessingInstruction sourcePI = sourceNode as XmlDiffProcessingInstruction; + XmlDiffProcessingInstruction targetPI = targetNode as XmlDiffProcessingInstruction; + Debug.Assert(sourcePI != null); + Debug.Assert(targetPI != null); + if (sourcePI.Name != targetPI.Name || sourcePI.Value != targetPI.Value) + { + return DiffType.PI; + } + break; + default: + break; + } + + return DiffType.Success; + } + + // This function writes the result in XML format so that it can be used by other applications to display the diff + private void WriteResult(XmlDiffNode sourceNode, XmlDiffNode targetNode, DiffType result) + { + if (DontWriteAnythingToOutput) return; + + if (result != DiffType.Success || !DontWriteMatchingNodesToOutput) + { + _Writer.WriteStartElement(String.Empty, "Node", String.Empty); + _Writer.WriteAttributeString(String.Empty, "SourceLineNum", String.Empty, (sourceNode != null) ? sourceNode.LineNumber.ToString() : "-1"); + _Writer.WriteAttributeString(String.Empty, "SourceLinePos", String.Empty, (sourceNode != null) ? sourceNode.LinePosition.ToString() : "-1"); + _Writer.WriteAttributeString(String.Empty, "TargetLineNum", String.Empty, (targetNode != null) ? targetNode.LineNumber.ToString() : "-1"); + _Writer.WriteAttributeString(String.Empty, "TargetLinePos", String.Empty, (targetNode != null) ? targetNode.LinePosition.ToString() : "-1"); + } + if (result == DiffType.Success) + { + if (!DontWriteMatchingNodesToOutput) + { + _Writer.WriteStartElement(String.Empty, "Diff", String.Empty); + _Writer.WriteEndElement(); + + _Writer.WriteStartElement(String.Empty, "Lexical-equal", String.Empty); + if (sourceNode.NodeType == XmlDiffNodeType.CData) + { + _Writer.WriteString(""); + } + else + { + _Writer.WriteCData(GetNodeText(sourceNode, result)); + } + _Writer.WriteEndElement(); + } + } + else + { + _Writer.WriteStartElement(String.Empty, "Diff", String.Empty); + _Writer.WriteAttributeString(String.Empty, "DiffType", String.Empty, GetDiffType(result)); + + if (sourceNode != null) + { + _Writer.WriteStartElement(String.Empty, "File1", String.Empty); + if (sourceNode.NodeType == XmlDiffNodeType.CData) + { + _Writer.WriteString(""); + } + else + { + _Writer.WriteString(GetNodeText(sourceNode, result)); + } + _Writer.WriteEndElement(); + } + + if (targetNode != null) + { + _Writer.WriteStartElement(String.Empty, "File2", String.Empty); + if (targetNode.NodeType == XmlDiffNodeType.CData) + { + _Writer.WriteString(""); + } + else + { + _Writer.WriteString(GetNodeText(targetNode, result)); + } + _Writer.WriteEndElement(); + } + + _Writer.WriteEndElement(); + + _Writer.WriteStartElement(String.Empty, "Lexical-equal", String.Empty); + _Writer.WriteEndElement(); + } + if (result != DiffType.Success || !DontWriteMatchingNodesToOutput) + { + _Writer.WriteEndElement(); + } + } + + // This is a helper function for WriteResult. It gets the Xml representation of the different node we wants + // to write out and all it's children. + private String GetNodeText(XmlDiffNode diffNode, DiffType result) + { + string text = string.Empty; + switch (diffNode.NodeType) + { + case XmlDiffNodeType.Element: + if (result == DiffType.SourceExtra || result == DiffType.TargetExtra) + return diffNode.OuterXml; + StringWriter str = new StringWriter(); + XmlTextWriter writer = new XmlTextWriter(str); + XmlDiffElement diffElem = diffNode as XmlDiffElement; + Debug.Assert(diffNode != null); + writer.WriteStartElement(diffElem.Prefix, diffElem.LocalName, diffElem.NamespaceURI); + XmlDiffAttribute diffAttr = diffElem.FirstAttribute; + while (diffAttr != null) + { + writer.WriteAttributeString(diffAttr.Prefix, diffAttr.LocalName, diffAttr.NamespaceURI, diffAttr.Value); + diffAttr = (XmlDiffAttribute)diffAttr.NextSibling; + } + if (diffElem is XmlDiffEmptyElement) + { + writer.WriteEndElement(); + text = str.ToString(); + } + else + { + text = str.ToString(); + text += ">"; + } + writer.Close(); + break; + case XmlDiffNodeType.CData: + text = ((XmlDiffCharacterData)diffNode).Value; + break; + default: + text = diffNode.OuterXml; + break; + } + return text; + } + + // This function is used to compare the attributes of an element node according to the options set by the user. + private bool CompareAttributes(XmlDiffElement sourceElem, XmlDiffElement targetElem) + { + Debug.Assert(sourceElem != null); + Debug.Assert(targetElem != null); + if (sourceElem.AttributeCount != targetElem.AttributeCount) + return false; + + if (sourceElem.AttributeCount == 0) + return true; + + XmlDiffAttribute sourceAttr = sourceElem.FirstAttribute; + XmlDiffAttribute targetAttr = targetElem.FirstAttribute; + + const string xmlnsNamespace = "http://www.w3.org/2000/xmlns/"; + + if (!IgnoreAttributeOrder) + { + while (sourceAttr != null && targetAttr != null) + { + if (!IgnoreNS) + { + if (sourceAttr.NamespaceURI != targetAttr.NamespaceURI) + { + return false; + } + } + + if (!IgnorePrefix) + { + if (sourceAttr.Prefix != targetAttr.Prefix) + { + return false; + } + } + + if (sourceAttr.NamespaceURI == xmlnsNamespace && targetAttr.NamespaceURI == xmlnsNamespace) + { + if (!IgnorePrefix && (sourceAttr.LocalName != targetAttr.LocalName)) + { + return false; + } + if (!IgnoreNS && (sourceAttr.Value != targetAttr.Value)) + { + return false; + } + } + else + { + if (sourceAttr.LocalName != targetAttr.LocalName) + { + return false; + } + if (sourceAttr.Value != targetAttr.Value) + { + if (ParseAttributeValuesAsQName) + { + if (sourceAttr.ValueAsQName != null) + { + if (!sourceAttr.ValueAsQName.Equals(targetAttr.ValueAsQName)) return false; + } + else + { + if (targetAttr.ValueAsQName != null) return false; + } + } + else + { + return false; + } + } + } + sourceAttr = (XmlDiffAttribute)(sourceAttr.NextSibling); + targetAttr = (XmlDiffAttribute)(targetAttr.NextSibling); + } + } + else + { + Hashtable sourceAttributeMap = new Hashtable(); + Hashtable targetAttributeMap = new Hashtable(); + + while (sourceAttr != null && targetAttr != null) + { + if (IgnorePrefix && sourceAttr.NamespaceURI == xmlnsNamespace) + { + // do nothing + } + else + { + string localNameAndNamespace = sourceAttr.LocalName + "<&&>" + sourceAttr.NamespaceURI; + sourceAttributeMap.Add(localNameAndNamespace, sourceAttr); + } + if (IgnorePrefix && targetAttr.NamespaceURI == xmlnsNamespace) + { + // do nothing + } + else + { + string localNameAndNamespace = targetAttr.LocalName + "<&&>" + targetAttr.NamespaceURI; + targetAttributeMap.Add(localNameAndNamespace, targetAttr); + } + + sourceAttr = (XmlDiffAttribute)(sourceAttr.NextSibling); + targetAttr = (XmlDiffAttribute)(targetAttr.NextSibling); + } + + if (sourceAttributeMap.Count != targetAttributeMap.Count) + { + return false; + } + + foreach (string sourceKey in sourceAttributeMap.Keys) + { + if (!targetAttributeMap.ContainsKey(sourceKey)) + { + return false; + } + sourceAttr = (XmlDiffAttribute)sourceAttributeMap[sourceKey]; + targetAttr = (XmlDiffAttribute)targetAttributeMap[sourceKey]; + + if (!IgnorePrefix) + { + if (sourceAttr.Prefix != targetAttr.Prefix) + { + return false; + } + } + + if (sourceAttr.Value != targetAttr.Value) + { + if (ParseAttributeValuesAsQName) + { + if (sourceAttr.ValueAsQName != null) + { + if (!sourceAttr.ValueAsQName.Equals(targetAttr.ValueAsQName)) return false; + } + else + { + if (targetAttr.ValueAsQName != null) return false; + } + } + else + { + return false; + } + } + } + } + return true; + } + + public string ToXml() + { + if (_Output != null) + return _Output.ToString(); + return string.Empty; + } + + public void ToXml(string fileName) + { + FileStream file; + file = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite); + StreamWriter writer = new StreamWriter(file); + writer.Write(""); + writer.Write(ToXml()); + writer.Close(); + file.Close(); + } + + public void ToXml(Stream stream) + { + StreamWriter writer = new StreamWriter(stream); + writer.Write(""); + writer.Write(ToXml()); + writer.Close(); + } + + private String GetDiffType(DiffType type) + { + switch (type) + { + case DiffType.None: + return String.Empty; + case DiffType.Element: + return "1"; + case DiffType.Whitespace: + return "2"; + case DiffType.Comment: + return "3"; + case DiffType.PI: + return "4"; + case DiffType.Text: + return "5"; + case DiffType.Attribute: + return "6"; + case DiffType.NS: + return "7"; + case DiffType.Prefix: + return "8"; + case DiffType.SourceExtra: + return "9"; + case DiffType.TargetExtra: + return "10"; + case DiffType.NodeType: + return "11"; + case DiffType.CData: + return "12"; + default: + return String.Empty; + } + } + } +} diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/Utils/XmlDiffDocument.cs b/external/corefx/src/System.ServiceModel.Syndication/tests/Utils/XmlDiffDocument.cs new file mode 100644 index 0000000000..288403c294 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/Utils/XmlDiffDocument.cs @@ -0,0 +1,1831 @@ +// 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.Collections; +using System.Diagnostics; +using System.IO; +using System.Text; +using System.Xml; +using System.Xml.XPath; + +namespace System.ServiceModel.Syndication.Tests +{ + public enum NodePosition + { + Before = 0, + After = 1, + Unknown = 2 + } + + public enum XmlDiffNodeType + { + Element = 0, + Attribute = 1, + ER = 2, + Text = 3, + CData = 4, + Comment = 5, + PI = 6, + WS = 7, + Document = 8 + } + + internal class PositionInfo : IXmlLineInfo + { + public virtual bool HasLineInfo() { return false; } + public virtual int LineNumber { get { return 0; } } + public virtual int LinePosition { get { return 0; } } + + public static PositionInfo GetPositionInfo(Object o) + { + IXmlLineInfo lineInfo = o as IXmlLineInfo; + if (lineInfo != null && lineInfo.HasLineInfo()) + { + return new ReaderPositionInfo(lineInfo); + } + else + { + return new PositionInfo(); + } + } + } + + internal class ReaderPositionInfo : PositionInfo + { + private IXmlLineInfo _mlineInfo; + + public ReaderPositionInfo(IXmlLineInfo lineInfo) + { + _mlineInfo = lineInfo; + } + + public override bool HasLineInfo() { return true; } + public override int LineNumber { get { return _mlineInfo.LineNumber; } } + public override int LinePosition { get { return _mlineInfo.LinePosition; } } + } + + //nametable could be added for next "release" + public class XmlDiffNameTable + { + } + + + public class XmlDiffDocument : XmlDiffNode, IXPathNavigable + { + //XmlDiffNameTable _nt; + private bool _bLoaded; + private bool _bIgnoreAttributeOrder; + private bool _bIgnoreChildOrder; + private bool _bIgnoreComments; + private bool _bIgnoreWhitespace; + private bool _bIgnoreDTD; + private bool _bIgnoreNS; + private bool _bIgnorePrefix; + private bool _bCDataAsText; + private bool _bNormalizeNewline; + private bool _bTreatWhitespaceTextAsWSNode = false; + private bool _bIgnoreEmptyTextNodes = false; + private bool _bWhitespaceAsText = false; + public XmlNameTable nameTable; + + public XmlDiffDocument() + : base() + { + _bLoaded = false; + _bIgnoreAttributeOrder = false; + _bIgnoreChildOrder = false; + _bIgnoreComments = false; + _bIgnoreWhitespace = false; + _bIgnoreDTD = false; + _bCDataAsText = false; + _bWhitespaceAsText = false; + } + + public XmlDiffOption Option + { + set + { + this.IgnoreAttributeOrder = (((int)value & (int)(XmlDiffOption.IgnoreAttributeOrder)) > 0); + this.IgnoreChildOrder = (((int)value & (int)(XmlDiffOption.IgnoreChildOrder)) > 0); + this.IgnoreComments = (((int)value & (int)(XmlDiffOption.IgnoreComments)) > 0); + this.IgnoreWhitespace = (((int)value & (int)(XmlDiffOption.IgnoreWhitespace)) > 0); + this.IgnoreDTD = (((int)value & (int)(XmlDiffOption.IgnoreDTD)) > 0); + this.IgnoreNS = (((int)value & (int)(XmlDiffOption.IgnoreNS)) > 0); + this.IgnorePrefix = (((int)value & (int)(XmlDiffOption.IgnorePrefix)) > 0); + this.CDataAsText = (((int)value & (int)(XmlDiffOption.CDataAsText)) > 0); + this.NormalizeNewline = (((int)value & (int)(XmlDiffOption.NormalizeNewline)) > 0); + this.TreatWhitespaceTextAsWSNode = (((int)value & (int)(XmlDiffOption.TreatWhitespaceTextAsWSNode)) > 0); + this.IgnoreEmptyTextNodes = (((int)value & (int)(XmlDiffOption.IgnoreEmptyTextNodes)) > 0); + this.WhitespaceAsText = (((int)value & (int)(XmlDiffOption.WhitespaceAsText)) > 0); + } + } + public override XmlDiffNodeType NodeType { get { return XmlDiffNodeType.Document; } } + + public bool IgnoreAttributeOrder + { + get { return _bIgnoreAttributeOrder; } + set { _bIgnoreAttributeOrder = value; } + } + + public bool IgnoreChildOrder + { + get { return _bIgnoreChildOrder; } + set { _bIgnoreChildOrder = value; } + } + + public bool IgnoreComments + { + get { return _bIgnoreComments; } + set { _bIgnoreComments = value; } + } + + public bool IgnoreWhitespace + { + get { return _bIgnoreWhitespace; } + set { _bIgnoreWhitespace = value; } + } + + public bool IgnoreDTD + { + get { return _bIgnoreDTD; } + set { _bIgnoreDTD = value; } + } + + public bool IgnoreNS + { + get { return _bIgnoreNS; } + set { _bIgnoreNS = value; } + } + + public bool IgnorePrefix + { + get { return _bIgnorePrefix; } + set { _bIgnorePrefix = value; } + } + + public bool CDataAsText + { + get { return _bCDataAsText; } + set { _bCDataAsText = value; } + } + + public bool NormalizeNewline + { + get { return _bNormalizeNewline; } + set { _bNormalizeNewline = value; } + } + + public bool TreatWhitespaceTextAsWSNode + { + get { return _bTreatWhitespaceTextAsWSNode; } + set { _bTreatWhitespaceTextAsWSNode = value; } + } + + public bool IgnoreEmptyTextNodes + { + get { return _bIgnoreEmptyTextNodes; } + set { _bIgnoreEmptyTextNodes = value; } + } + + public bool WhitespaceAsText + { + get { return _bWhitespaceAsText; } + set { _bWhitespaceAsText = value; } + } + + //NodePosition.Before is returned if node2 should be before node1; + //NodePosition.After is returned if node2 should be after node1; + //In any case, NodePosition.Unknown should never be returned. + internal NodePosition ComparePosition(XmlDiffNode node1, XmlDiffNode node2) + { + int nt1 = (int)(node1.NodeType); + int nt2 = (int)(node2.NodeType); + if (nt2 > nt1) + return NodePosition.After; + if (nt2 < nt1) + return NodePosition.Before; + //now nt1 == nt2 + if (nt1 == (int)XmlDiffNodeType.Element) + { + return CompareElements(node1 as XmlDiffElement, node2 as XmlDiffElement); + } + else if (nt1 == (int)XmlDiffNodeType.Attribute) + { + return CompareAttributes(node1 as XmlDiffAttribute, node2 as XmlDiffAttribute); + } + else if (nt1 == (int)XmlDiffNodeType.ER) + { + return CompareERs(node1 as XmlDiffEntityReference, node2 as XmlDiffEntityReference); + } + else if (nt1 == (int)XmlDiffNodeType.PI) + { + return ComparePIs(node1 as XmlDiffProcessingInstruction, node2 as XmlDiffProcessingInstruction); + } + else if (node1 is XmlDiffCharacterData) + { + return CompareTextLikeNodes(node1 as XmlDiffCharacterData, node2 as XmlDiffCharacterData); + } + else + { + //something really wrong here, what should we do??? + Debug.Assert(false, "ComparePosition meets an undecision situation."); + return NodePosition.Unknown; + } + } + + private NodePosition CompareElements(XmlDiffElement elem1, XmlDiffElement elem2) + { + Debug.Assert(elem1 != null); + Debug.Assert(elem2 != null); + int nCompare = 0; + if ((nCompare = CompareText(elem2.LocalName, elem1.LocalName)) == 0) + { + if (IgnoreNS || (nCompare = CompareText(elem2.NamespaceURI, elem1.NamespaceURI)) == 0) + { + if (IgnorePrefix || (nCompare = CompareText(elem2.Prefix, elem1.Prefix)) == 0) + { + if ((nCompare = CompareText(elem2.Value, elem1.Value)) == 0) + { + if ((nCompare = CompareAttributes(elem2, elem1)) == 0) + { + return NodePosition.After; + } + } + } + } + } + if (nCompare > 0) + //elem2 > elem1 + return NodePosition.After; + else + //elem2 < elem1 + return NodePosition.Before; + } + + private int CompareAttributes(XmlDiffElement elem1, XmlDiffElement elem2) + { + int count1 = elem1.AttributeCount; + int count2 = elem2.AttributeCount; + if (count1 > count2) + return 1; + else if (count1 < count2) + return -1; + else + { + XmlDiffAttribute current1 = elem1.FirstAttribute; + XmlDiffAttribute current2 = elem2.FirstAttribute; + // NodePosition result = 0; + int nCompare = 0; + while (current1 != null && current2 != null && nCompare == 0) + { + if ((nCompare = CompareText(current2.LocalName, current1.LocalName)) == 0) + { + if (IgnoreNS || (nCompare = CompareText(current2.NamespaceURI, current1.NamespaceURI)) == 0) + { + if (IgnorePrefix || (nCompare = CompareText(current2.Prefix, current1.Prefix)) == 0) + { + if ((nCompare = CompareText(current2.Value, current1.Value)) == 0) + { + //do nothing! + } + } + } + } + current1 = (XmlDiffAttribute)current1._next; + current2 = (XmlDiffAttribute)current2._next; + } + if (nCompare > 0) + //elem1 > attr2 + return 1; + else + //elem1 < elem2 + return -1; + } + } + + private NodePosition CompareAttributes(XmlDiffAttribute attr1, XmlDiffAttribute attr2) + { + Debug.Assert(attr1 != null); + Debug.Assert(attr2 != null); + + int nCompare = 0; + if ((nCompare = CompareText(attr2.LocalName, attr1.LocalName)) == 0) + { + if (IgnoreNS || (nCompare = CompareText(attr2.NamespaceURI, attr1.NamespaceURI)) == 0) + { + if (IgnorePrefix || (nCompare = CompareText(attr2.Prefix, attr1.Prefix)) == 0) + { + if ((nCompare = CompareText(attr2.Value, attr1.Value)) == 0) + { + return NodePosition.After; + } + } + } + } + if (nCompare > 0) + //attr2 > attr1 + return NodePosition.After; + else + //attr2 < attr1 + return NodePosition.Before; + } + + private NodePosition CompareERs(XmlDiffEntityReference er1, XmlDiffEntityReference er2) + { + Debug.Assert(er1 != null); + Debug.Assert(er2 != null); + + int nCompare = CompareText(er2.Name, er1.Name); + if (nCompare >= 0) + return NodePosition.After; + else + return NodePosition.Before; + } + + private NodePosition ComparePIs(XmlDiffProcessingInstruction pi1, XmlDiffProcessingInstruction pi2) + { + Debug.Assert(pi1 != null); + Debug.Assert(pi2 != null); + + int nCompare = 0; + if ((nCompare = CompareText(pi2.Name, pi1.Name)) == 0) + { + if ((nCompare = CompareText(pi2.Value, pi1.Value)) == 0) + { + return NodePosition.After; + } + } + if (nCompare > 0) + { + //pi2 > pi1 + return NodePosition.After; + } + else + { + //pi2 < pi1 + return NodePosition.Before; + } + } + + private NodePosition CompareTextLikeNodes(XmlDiffCharacterData t1, XmlDiffCharacterData t2) + { + Debug.Assert(t1 != null); + Debug.Assert(t2 != null); + + int nCompare = CompareText(t2.Value, t1.Value); + if (nCompare >= 0) + return NodePosition.After; + else + return NodePosition.Before; + } + + //returns 0 if the same string; 1 if s1 > s1 and -1 if s1 < s2 + private int CompareText(string s1, string s2) + { + return Math.Sign(String.Compare(s1, s2, StringComparison.Ordinal)); + } + + public virtual void Load(string xmlFileName) + { + XmlReaderSettings readerSettings = new XmlReaderSettings(); + if (IgnoreDTD) + { + readerSettings.ValidationType = ValidationType.None; + } + else + { + readerSettings.ValidationType = ValidationType.DTD; + } + + using (XmlReader reader = XmlReader.Create(xmlFileName)) + { + Load(reader); + reader.Close(); + } + } + + public virtual void Load(XmlReader reader) + { + if (_bLoaded) + throw new InvalidOperationException("The document already contains data and should not be used again."); + if (reader.ReadState == ReadState.Initial) + { + if (!reader.Read()) + { + return; + } + } + PositionInfo pInfo = PositionInfo.GetPositionInfo(reader); + ReadChildNodes(this, reader, pInfo); + _bLoaded = true; + this.nameTable = reader.NameTable; + } + + internal void ReadChildNodes(XmlDiffNode parent, XmlReader reader, PositionInfo pInfo) + { + bool lookAhead = false; + do + { + lookAhead = false; + switch (reader.NodeType) + { + case XmlNodeType.Element: + LoadElement(parent, reader, pInfo); + break; + case XmlNodeType.Comment: + if (!IgnoreComments) + LoadTextNode(parent, reader, pInfo, XmlDiffNodeType.Comment); + break; + case XmlNodeType.ProcessingInstruction: + LoadPI(parent, reader, pInfo); + break; + case XmlNodeType.SignificantWhitespace: + case XmlNodeType.Whitespace: + if (!IgnoreWhitespace) + { + if (this.WhitespaceAsText) + { + LoadTextNode(parent, reader, pInfo, XmlDiffNodeType.Text); + } + else + { + LoadTextNode(parent, reader, pInfo, XmlDiffNodeType.WS); + } + } + break; + case XmlNodeType.CDATA: + if (!CDataAsText) + { + LoadTextNode(parent, reader, pInfo, XmlDiffNodeType.CData); + } + else //merge with adjacent text/CDATA nodes + { + StringBuilder text = new StringBuilder(); + text.Append(reader.Value); + while ((lookAhead = reader.Read()) && (reader.NodeType == XmlNodeType.Text || reader.NodeType == XmlNodeType.CDATA)) + { + text.Append(reader.Value); + } + LoadTextNode(parent, text.ToString(), pInfo, XmlDiffNodeType.Text); + } + break; + case XmlNodeType.Text: + if (!CDataAsText) + { + LoadTextNode(parent, reader, pInfo, TextNodeIsWhitespace(reader.Value) ? XmlDiffNodeType.WS : XmlDiffNodeType.Text); + } + else //merge with adjacent text/CDATA nodes + { + StringBuilder text = new StringBuilder(); + text.Append(reader.Value); + while ((lookAhead = reader.Read()) && (reader.NodeType == XmlNodeType.Text || reader.NodeType == XmlNodeType.CDATA)) + { + text.Append(reader.Value); + } + string txt = text.ToString(); + LoadTextNode(parent, txt, pInfo, TextNodeIsWhitespace(txt) ? XmlDiffNodeType.WS : XmlDiffNodeType.Text); + } + break; + case XmlNodeType.EntityReference: + LoadEntityReference(parent, reader, pInfo); + break; + case XmlNodeType.EndElement: + SetElementEndPosition(parent as XmlDiffElement, pInfo); + return; + case XmlNodeType.Attribute: //attribute at top level + string attrVal = reader.Name + "=\"" + reader.Value + "\""; + LoadTopLevelAttribute(parent, attrVal, pInfo, XmlDiffNodeType.Text); + break; + default: + break; + } + } while (lookAhead || reader.Read()); + } + + private bool TextNodeIsWhitespace(string p) + { + if (!this.TreatWhitespaceTextAsWSNode) return false; + for (int i = 0; i < p.Length; i++) + { + if (!Char.IsWhiteSpace(p[i])) return false; + } + return true; + } + + private void LoadElement(XmlDiffNode parent, XmlReader reader, PositionInfo pInfo) + { + XmlDiffElement elem = null; + bool bEmptyElement = reader.IsEmptyElement; + if (bEmptyElement) + elem = new XmlDiffEmptyElement(reader.LocalName, reader.Prefix, reader.NamespaceURI); + else + elem = new XmlDiffElement(reader.LocalName, reader.Prefix, reader.NamespaceURI); + elem.LineNumber = pInfo.LineNumber; + elem.LinePosition = pInfo.LinePosition; + ReadAttributes(elem, reader, pInfo); + if (!bEmptyElement) + { + // bool rtn = reader.Read(); + // rtn = reader.Read(); + reader.Read(); //move to child + ReadChildNodes(elem, reader, pInfo); + } + InsertChild(parent, elem); + } + + private void ReadAttributes(XmlDiffElement parent, XmlReader reader, PositionInfo pInfo) + { + if (reader.MoveToFirstAttribute()) + { + do + { + XmlDiffAttribute attr = new XmlDiffAttribute(reader.LocalName, reader.Prefix, reader.NamespaceURI, reader.Value); + attr.SetValueAsQName(reader, reader.Value); + attr.LineNumber = pInfo.LineNumber; + attr.LinePosition = pInfo.LinePosition; + InsertAttribute(parent, attr); + } + while (reader.MoveToNextAttribute()); + } + } + + private void LoadTextNode(XmlDiffNode parent, XmlReader reader, PositionInfo pInfo, XmlDiffNodeType nt) + { + LoadTextNode(parent, reader.Value, pInfo, nt); + } + + private void LoadTextNode(XmlDiffNode parent, string text, PositionInfo pInfo, XmlDiffNodeType nt) + { + if (!this.IgnoreEmptyTextNodes || !String.IsNullOrEmpty(text)) + { + XmlDiffCharacterData textNode = new XmlDiffCharacterData(text, nt, this.NormalizeNewline); + textNode.LineNumber = pInfo.LineNumber; + textNode.LinePosition = pInfo.LinePosition; + InsertChild(parent, textNode); + } + } + + private void LoadTopLevelAttribute(XmlDiffNode parent, string text, PositionInfo pInfo, XmlDiffNodeType nt) + { + XmlDiffCharacterData textNode = new XmlDiffCharacterData(text, nt, this.NormalizeNewline); + textNode.LineNumber = pInfo.LineNumber; + textNode.LinePosition = pInfo.LinePosition; + InsertTopLevelAttributeAsText(parent, textNode); + } + + private void LoadPI(XmlDiffNode parent, XmlReader reader, PositionInfo pInfo) + { + XmlDiffProcessingInstruction pi = new XmlDiffProcessingInstruction(reader.Name, reader.Value); + pi.LineNumber = pInfo.LineNumber; + pi.LinePosition = pInfo.LinePosition; + InsertChild(parent, pi); + } + + private void LoadEntityReference(XmlDiffNode parent, XmlReader reader, PositionInfo pInfo) + { + XmlDiffEntityReference er = new XmlDiffEntityReference(reader.Name); + er.LineNumber = pInfo.LineNumber; + er.LinePosition = pInfo.LinePosition; + InsertChild(parent, er); + } + + private void SetElementEndPosition(XmlDiffElement elem, PositionInfo pInfo) + { + Debug.Assert(elem != null); + elem.EndLineNumber = pInfo.LineNumber; + elem.EndLinePosition = pInfo.LinePosition; + } + + + private void InsertChild(XmlDiffNode parent, XmlDiffNode newChild) + { + if (IgnoreChildOrder) + { + XmlDiffNode child = parent.FirstChild; + XmlDiffNode prevChild = null; + while (child != null && (ComparePosition(child, newChild) == NodePosition.After)) + { + prevChild = child; + child = child.NextSibling; + } + parent.InsertChildAfter(prevChild, newChild); + } + else + parent.InsertChildAfter(parent.LastChild, newChild); + } + + private void InsertTopLevelAttributeAsText(XmlDiffNode parent, XmlDiffCharacterData newChild) + { + if (parent.LastChild != null && (parent.LastChild.NodeType == XmlDiffNodeType.Text || parent.LastChild.NodeType == XmlDiffNodeType.WS)) + { + ((XmlDiffCharacterData)parent.LastChild).Value = ((XmlDiffCharacterData)parent.LastChild).Value + " " + newChild.Value; + } + else + { + parent.InsertChildAfter(parent.LastChild, newChild); + } + } + + private void InsertAttribute(XmlDiffElement parent, XmlDiffAttribute newAttr) + { + Debug.Assert(parent != null); + Debug.Assert(newAttr != null); + newAttr._parent = parent; + if (IgnoreAttributeOrder) + { + XmlDiffAttribute attr = parent.FirstAttribute; + XmlDiffAttribute prevAttr = null; + while (attr != null && (CompareAttributes(attr, newAttr) == NodePosition.After)) + { + prevAttr = attr; + attr = (XmlDiffAttribute)(attr.NextSibling); + } + parent.InsertAttributeAfter(prevAttr, newAttr); + } + else + parent.InsertAttributeAfter(parent.LastAttribute, newAttr); + } + + public override void WriteTo(XmlWriter w) + { + WriteContentTo(w); + } + + public override void WriteContentTo(XmlWriter w) + { + XmlDiffNode child = FirstChild; + while (child != null) + { + child.WriteTo(w); + child = child.NextSibling; + } + } + + //IXPathNavigable override + public XPathNavigator CreateNavigator() + { + return new XmlDiffNavigator(this); + } + + /* + public void SortChildren(string expr, XmlNamespaceManager mngr) + { + XPathNavigator _nav = this.CreateNavigator(); + XPathExpression _expr = _nav.Compile(expr); + if(mngr != null) + { + _expr.SetContext(mngr); + } + XPathNodeIterator _iter = _nav.Select(_expr); + while(_iter.MoveNext()) + { + if(((XmlDiffNavigator)_iter.Current).CurrentNode is XmlDiffElement) + { + SortChildren((XmlDiffElement)((XmlDiffNavigator)_iter.Current).CurrentNode); + } + } + } + */ + + public void SortChildren(XPathExpression expr) + { + if (expr == null) + { + return; + } + XPathNavigator _nav = this.CreateNavigator(); + XPathNodeIterator _iter = _nav.Select(expr); + while (_iter.MoveNext()) + { + if (((XmlDiffNavigator)_iter.Current).CurrentNode is XmlDiffElement) + { + SortChildren((XmlDiffElement)((XmlDiffNavigator)_iter.Current).CurrentNode); + } + } + } + + public void IgnoreNodes(XPathExpression expr) + { + if (expr == null) + { + return; + } + XPathNavigator _nav = this.CreateNavigator(); + XPathNodeIterator _iter = _nav.Select(expr); + while (_iter.MoveNext()) + { + if (((XmlDiffNavigator)_iter.Current).CurrentNode is XmlDiffAttribute) + { + ((XmlDiffElement)((XmlDiffNavigator)_iter.Current).CurrentNode.ParentNode).DeleteAttribute((XmlDiffAttribute)((XmlDiffNavigator)_iter.Current).CurrentNode); + } + else + { + ((XmlDiffNavigator)_iter.Current).CurrentNode.ParentNode.DeleteChild(((XmlDiffNavigator)_iter.Current).CurrentNode); + } + } + } + + public void IgnoreValues(XPathExpression expr) + { + if (expr == null) + { + return; + } + XPathNavigator _nav = this.CreateNavigator(); + XPathNodeIterator _iter = _nav.Select(expr); + while (_iter.MoveNext()) + { + ((XmlDiffNavigator)_iter.Current).CurrentNode.IgnoreValue = true; ; + } + } + + private void SortChildren(XmlDiffElement elem) + { + if (elem.FirstChild != null) + { + XmlDiffNode _first = elem.FirstChild; + XmlDiffNode _current = elem.FirstChild; + XmlDiffNode _last = elem.LastChild; + elem._firstChild = null; + elem._lastChild = null; + //set flag to ignore child order + bool temp = IgnoreChildOrder; + IgnoreChildOrder = true; + XmlDiffNode _next = null; + do + { + if (_current is XmlDiffElement) + _next = _current._next; + _current._next = null; + InsertChild(elem, _current); + if (_current == _last) + break; + _current = _next; + } + while (true); + //restore flag for ignoring child order + IgnoreChildOrder = temp; + } + } + } + + //navgator over the xmldiffdocument + public class XmlDiffNavigator : XPathNavigator + { + private XmlDiffDocument _document; + private XmlDiffNode _currentNode; + + public XmlDiffNavigator(XmlDiffDocument doc) + { + _document = doc; + _currentNode = _document; + } + public override XPathNavigator Clone() + { + XmlDiffNavigator _clone = new XmlDiffNavigator(_document); + if (!_clone.MoveTo(this)) + throw new Exception("Cannot clone"); + return _clone; + } + public override XmlNodeOrder ComparePosition(XPathNavigator nav) + { + XmlDiffNode targetNode = ((XmlDiffNavigator)nav).CurrentNode; + // Debug.Assert(false, "ComparePosition is NYI"); + if (!(nav is XmlDiffNavigator)) + { + return XmlNodeOrder.Unknown; + } + if (targetNode == this.CurrentNode) + { + return XmlNodeOrder.Same; + } + else + { + if (this.CurrentNode.ParentNode == null) //this is root + { + return XmlNodeOrder.After; + } + else if (targetNode.ParentNode == null) //this is root + { + return XmlNodeOrder.Before; + } + else //look in the following nodes + { + if (targetNode.LineNumber + targetNode.LinePosition > this.CurrentNode.LinePosition + this.CurrentNode.LineNumber) + { + return XmlNodeOrder.After; + } + return XmlNodeOrder.Before; + } + } + } + public override String GetAttribute(String localName, String namespaceURI) + { + if (_currentNode is XmlDiffElement) + { + return ((XmlDiffElement)_currentNode).GetAttributeValue(localName, namespaceURI); + } + return ""; + } + + public override String GetNamespace(String name) + { + Debug.Assert(false, "GetNamespace is NYI"); + return ""; + } + + /*public override bool IsDescendant (XPathNavigator nav) + { + Debug.Assert(false, "IsDescendant is NYI"); + return false; + } + */ + + public override bool IsSamePosition(XPathNavigator other) + { + if (other is XmlDiffNavigator) + { + if (_currentNode == ((XmlDiffNavigator)other).CurrentNode) + return true; + } + return false; + } + + public override bool MoveTo(XPathNavigator other) + { + if (other is XmlDiffNavigator) + { + _currentNode = ((XmlDiffNavigator)other).CurrentNode; + return true; + } + return false; + } + + public override bool MoveToAttribute(String localName, String namespaceURI) + { + if (_currentNode is XmlDiffElement) + { + XmlDiffAttribute _attr = ((XmlDiffElement)_currentNode).GetAttribute(localName, namespaceURI); + if (_attr != null) + { + _currentNode = _attr; + return true; + } + } + return false; + } + + public override bool MoveToFirst() + { + if (!(_currentNode is XmlDiffAttribute)) + { + if (_currentNode.ParentNode.FirstChild == _currentNode) + { + if (_currentNode.ParentNode.FirstChild._next != null) + { + _currentNode = _currentNode.ParentNode.FirstChild._next; + return true; + } + } + else + { + _currentNode = _currentNode.ParentNode.FirstChild; + return true; + } + } + return false; + } + + public override bool MoveToFirstAttribute() + { + if (_currentNode is XmlDiffElement) + { + if (((XmlDiffElement)_currentNode).FirstAttribute != null) + { + XmlDiffAttribute _attr = ((XmlDiffElement)_currentNode).FirstAttribute; + while (_attr != null && IsNamespaceNode(_attr)) + { + _attr = (XmlDiffAttribute)_attr._next; + } + if (_attr != null) + { + _currentNode = _attr; + return true; + } + } + } + return false; + } + + public override bool MoveToFirstChild() + { + if ((_currentNode is XmlDiffDocument || _currentNode is XmlDiffElement) && _currentNode.FirstChild != null) + { + _currentNode = _currentNode.FirstChild; + return true; + } + return false; + } + + //sunghonhack + public new bool MoveToFirstNamespace() + { + if (_currentNode is XmlDiffElement) + { + if (((XmlDiffElement)_currentNode).FirstAttribute != null) + { + XmlDiffAttribute _attr = ((XmlDiffElement)_currentNode).FirstAttribute; + while (_attr != null && !IsNamespaceNode(_attr)) + { + _attr = (XmlDiffAttribute)_attr._next; + } + if (_attr != null) + { + _currentNode = _attr; + return true; + } + } + } + return false; + } + public override bool MoveToFirstNamespace(XPathNamespaceScope namespaceScope) + { + return this.MoveToFirstNamespace(); + } + public override bool MoveToId(String id) + { + Debug.Assert(false, "MoveToId is NYI"); + return false; + } + public override bool MoveToNamespace(String name) + { + Debug.Assert(false, "MoveToNamespace is NYI"); + return false; + } + public override bool MoveToNext() + { + if (!(_currentNode is XmlDiffAttribute) && _currentNode._next != null) + { + _currentNode = _currentNode._next; + return true; + } + return false; + } + public override bool MoveToNextAttribute() + { + if (_currentNode is XmlDiffAttribute) + { + XmlDiffAttribute _attr = (XmlDiffAttribute)_currentNode._next; + while (_attr != null && IsNamespaceNode(_attr)) + { + _attr = (XmlDiffAttribute)_attr._next; + } + if (_attr != null) + { + _currentNode = _attr; + return true; + } + } + return false; + } + + //sunghonhack + public new bool MoveToNextNamespace() + { + if (_currentNode is XmlDiffAttribute) + { + XmlDiffAttribute _attr = (XmlDiffAttribute)_currentNode._next; + while (_attr != null && !IsNamespaceNode(_attr)) + { + _attr = (XmlDiffAttribute)_attr._next; + } + if (_attr != null) + { + _currentNode = _attr; + return true; + } + } + return false; + } + private bool IsNamespaceNode(XmlDiffAttribute attr) + { + return attr.LocalName.ToLower(System.Globalization.CultureInfo.InvariantCulture) == "xmlns" || + attr.Prefix.ToLower(System.Globalization.CultureInfo.InvariantCulture) == "xmlns"; + } + public override bool MoveToNextNamespace(XPathNamespaceScope namespaceScope) + { + return this.MoveToNextNamespace(); + } + public override bool MoveToParent() + { + if (!(_currentNode is XmlDiffDocument)) + { + _currentNode = _currentNode.ParentNode; + return true; + } + return false; + } + public override bool MoveToPrevious() + { + if (_currentNode != _currentNode.ParentNode.FirstChild) + { + XmlDiffNode _current = _currentNode.ParentNode.FirstChild; + XmlDiffNode _prev = _currentNode.ParentNode.FirstChild; + while (_current != _currentNode) + { + _prev = _current; + _current = _current._next; + } + _currentNode = _prev; + return true; + } + return false; + } + public override void MoveToRoot() + { + _currentNode = _document; + } + + //properties + + public override XPathNodeType NodeType + { + get + { + //namespace, comment and whitespace node types are not supported + switch (_currentNode.NodeType) + { + case XmlDiffNodeType.Element: + return XPathNodeType.Element; + case XmlDiffNodeType.Attribute: + return XPathNodeType.Attribute; + case XmlDiffNodeType.ER: + return XPathNodeType.Text; + case XmlDiffNodeType.Text: + return XPathNodeType.Text; + case XmlDiffNodeType.CData: + return XPathNodeType.Text; + case XmlDiffNodeType.Comment: + return XPathNodeType.Comment; + case XmlDiffNodeType.PI: + return XPathNodeType.ProcessingInstruction; + case XmlDiffNodeType.WS: + return XPathNodeType.SignificantWhitespace; + case XmlDiffNodeType.Document: + return XPathNodeType.Root; + default: + return XPathNodeType.All; + } + } + } + + public override string LocalName + { + get + { + if (_currentNode.NodeType == XmlDiffNodeType.Element) + { + return ((XmlDiffElement)_currentNode).LocalName; + } + else if (_currentNode.NodeType == XmlDiffNodeType.Attribute) + { + return ((XmlDiffAttribute)_currentNode).LocalName; + } + else if (_currentNode.NodeType == XmlDiffNodeType.PI) + { + return ((XmlDiffProcessingInstruction)_currentNode).Name; + } + return ""; + } + } + + public override string Name + { + get + { + if (_currentNode.NodeType == XmlDiffNodeType.Element) + { + //return ((XmlDiffElement)m_currentNode).Name; + return _document.nameTable.Get(((XmlDiffElement)_currentNode).Name); + } + else if (_currentNode.NodeType == XmlDiffNodeType.Attribute) + { + return ((XmlDiffAttribute)_currentNode).Name; + } + else if (_currentNode.NodeType == XmlDiffNodeType.PI) + { + return ((XmlDiffProcessingInstruction)_currentNode).Name; + } + return ""; + } + } + + public override string NamespaceURI + { + get + { + if (_currentNode is XmlDiffElement) + { + return ((XmlDiffElement)_currentNode).NamespaceURI; + } + else if (_currentNode is XmlDiffAttribute) + { + return ((XmlDiffAttribute)_currentNode).NamespaceURI; + } + return ""; + } + } + + public override string Value + { + get + { + if (_currentNode is XmlDiffAttribute) + { + return ((XmlDiffAttribute)_currentNode).Value; + } + else if (_currentNode is XmlDiffCharacterData) + { + return ((XmlDiffCharacterData)_currentNode).Value; + } + else if (_currentNode is XmlDiffElement) + { + return ((XmlDiffElement)_currentNode).Value; + } + return ""; + } + } + + public override string Prefix + { + get + { + if (_currentNode is XmlDiffElement) + { + return ((XmlDiffElement)_currentNode).Prefix; + } + else if (_currentNode is XmlDiffAttribute) + { + return ((XmlDiffAttribute)_currentNode).Prefix; + } + return ""; + } + } + + public override string BaseURI + { + get + { + Debug.Assert(false, "BaseURI is NYI"); + return ""; + } + } + public override string XmlLang + { + get + { + Debug.Assert(false, "XmlLang not supported"); + return ""; + } + } + public override bool HasAttributes + { + get + { + return (_currentNode is XmlDiffElement && ((XmlDiffElement)_currentNode).FirstAttribute != null) ? true : false; + } + } + public override bool HasChildren + { + get + { + return _currentNode._next != null ? true : false; + } + } + public override bool IsEmptyElement + { + get + { + return _currentNode is XmlDiffEmptyElement ? true : false; + } + } + public override XmlNameTable NameTable + { + get + { + return _document.nameTable; + //return new NameTable(); + } + } + public XmlDiffNode CurrentNode + { + get + { + return _currentNode; + } + } + public bool IsOnRoot() + { + return _currentNode == null ? true : false; + } + } + + + + public class PropertyCollection : Hashtable { } + + public abstract class XmlDiffNode + { + internal XmlDiffNode _next; + internal XmlDiffNode _firstChild; + internal XmlDiffNode _lastChild; + internal XmlDiffNode _parent; + internal int _lineNumber, _linePosition; + internal bool _bIgnoreValue; + private PropertyCollection _extendedProperties; + + public XmlDiffNode() + { + this._next = null; + this._firstChild = null; + this._lastChild = null; + this._parent = null; + _lineNumber = 0; + _linePosition = 0; + } + + public XmlDiffNode FirstChild + { + get + { + return this._firstChild; + } + } + public XmlDiffNode LastChild + { + get + { + return this._lastChild; + } + } + public XmlDiffNode NextSibling + { + get + { + return this._next; + } + } + public XmlDiffNode ParentNode + { + get + { + return this._parent; + } + } + + public virtual bool IgnoreValue + { + get + { + return _bIgnoreValue; + } + set + { + _bIgnoreValue = value; + XmlDiffNode current = this._firstChild; + while (current != null) + { + current.IgnoreValue = value; + current = current._next; + } + } + } + + + public abstract XmlDiffNodeType NodeType { get; } + + public virtual string OuterXml + { + get + { + StringWriter sw = new StringWriter(); + XmlTextWriter xw = new XmlTextWriter(sw); + + WriteTo(xw); + xw.Close(); + + return sw.ToString(); + } + } + public virtual string InnerXml + { + get + { + StringWriter sw = new StringWriter(); + XmlTextWriter xw = new XmlTextWriter(sw); + + WriteContentTo(xw); + xw.Close(); + + return sw.ToString(); + } + } + + public abstract void WriteTo(XmlWriter w); + public abstract void WriteContentTo(XmlWriter w); + + public PropertyCollection ExtendedProperties + { + get + { + if (_extendedProperties == null) + _extendedProperties = new PropertyCollection(); + return _extendedProperties; + } + } + public virtual void InsertChildAfter(XmlDiffNode child, XmlDiffNode newChild) + { + Debug.Assert(newChild != null); + newChild._parent = this; + if (child == null) + { + newChild._next = this._firstChild; + this._firstChild = newChild; + } + else + { + Debug.Assert(child._parent == this); + newChild._next = child._next; + child._next = newChild; + } + if (newChild._next == null) + this._lastChild = newChild; + } + + public virtual void DeleteChild(XmlDiffNode child) + { + if (child == this.FirstChild)//delete head + { + _firstChild = this.FirstChild.NextSibling; + } + else + { + XmlDiffNode current = this.FirstChild; + XmlDiffNode previous = null; + while (current != child) + { + previous = current; + current = current.NextSibling; + } + Debug.Assert(current != null); + if (current == this.LastChild) //tail being deleted + { + this._lastChild = current.NextSibling; + } + previous._next = current.NextSibling; + } + } + + public int LineNumber + { + get { return this._lineNumber; } + set { this._lineNumber = value; } + } + + public int LinePosition + { + get { return this._linePosition; } + set { this._linePosition = value; } + } + } + + public class XmlDiffElement : XmlDiffNode + { + private string _lName; + private string _prefix; + private string _ns; + private XmlDiffAttribute _firstAttribute; + private XmlDiffAttribute _lastAttribute; + private int _attrC; + private int _endLineNumber, _endLinePosition; + + public XmlDiffElement(string localName, string prefix, string ns) + : base() + { + _lName = localName; + _prefix = prefix; + _ns = ns; + _firstAttribute = null; + _lastAttribute = null; + _attrC = -1; + } + + public override XmlDiffNodeType NodeType { get { return XmlDiffNodeType.Element; } } + public string LocalName { get { return _lName; } } + public string NamespaceURI { get { return _ns; } } + public string Prefix { get { return _prefix; } } + + public string Name + { + get + { + if (_prefix.Length > 0) + return Prefix + ":" + LocalName; + else + return LocalName; + } + } + + public XmlDiffAttribute FirstAttribute + { + get + { + return _firstAttribute; + } + } + public XmlDiffAttribute LastAttribute + { + get + { + return _lastAttribute; + } + } + public string GetAttributeValue(string LocalName, string NamespaceUri) + { + if (_firstAttribute != null) + { + XmlDiffAttribute _current = _firstAttribute; + do + { + if (_current.LocalName == LocalName && _current.NamespaceURI == NamespaceURI) + { + return _current.Value; + } + _current = (XmlDiffAttribute)_current._next; + } + while (_current != _lastAttribute); + } + return ""; + } + + public XmlDiffAttribute GetAttribute(string LocalName, string NamespaceUri) + { + if (_firstAttribute != null) + { + XmlDiffAttribute _current = _firstAttribute; + do + { + if (_current.LocalName == LocalName && _current.NamespaceURI == NamespaceURI) + { + return _current; + } + _current = (XmlDiffAttribute)_current._next; + } + while (_current != _lastAttribute); + } + return null; + } + + internal void InsertAttributeAfter(XmlDiffAttribute attr, XmlDiffAttribute newAttr) + { + Debug.Assert(newAttr != null); + newAttr._ownerElement = this; + if (attr == null) + { + newAttr._next = _firstAttribute; + _firstAttribute = newAttr; + } + else + { + Debug.Assert(attr._ownerElement == this); + newAttr._next = attr._next; + attr._next = newAttr; + } + if (newAttr._next == null) + _lastAttribute = newAttr; + } + + internal void DeleteAttribute(XmlDiffAttribute attr) + { + if (attr == this.FirstAttribute)//delete head + { + if (attr == this.LastAttribute) //tail being deleted + { + _lastAttribute = (XmlDiffAttribute)attr.NextSibling; + } + _firstAttribute = (XmlDiffAttribute)this.FirstAttribute.NextSibling; + } + else + { + XmlDiffAttribute current = this.FirstAttribute; + XmlDiffAttribute previous = null; + while (current != attr) + { + previous = current; + current = (XmlDiffAttribute)current.NextSibling; + } + Debug.Assert(current != null); + if (current == this.LastAttribute) //tail being deleted + { + _lastAttribute = (XmlDiffAttribute)current.NextSibling; + } + previous._next = current.NextSibling; + } + } + + public int AttributeCount + { + get + { + if (_attrC != -1) + return _attrC; + XmlDiffAttribute attr = _firstAttribute; + _attrC = 0; + while (attr != null) + { + _attrC++; + attr = (XmlDiffAttribute)attr.NextSibling; + } + return _attrC; + } + } + public override bool IgnoreValue + { + set + { + base.IgnoreValue = value; + XmlDiffAttribute current = _firstAttribute; + while (current != null) + { + current.IgnoreValue = value; + current = (XmlDiffAttribute)current._next; + } + } + } + + public int EndLineNumber + { + get { return _endLineNumber; } + set { _endLineNumber = value; } + } + + public int EndLinePosition + { + get { return _endLinePosition; } + set { _endLinePosition = value; } + } + + public override void WriteTo(XmlWriter w) + { + w.WriteStartElement(Prefix, LocalName, NamespaceURI); + XmlDiffAttribute attr = _firstAttribute; + while (attr != null) + { + attr.WriteTo(w); + attr = (XmlDiffAttribute)(attr.NextSibling); + } + WriteContentTo(w); + w.WriteFullEndElement(); + } + + public override void WriteContentTo(XmlWriter w) + { + XmlDiffNode child = FirstChild; + while (child != null) + { + child.WriteTo(w); + child = child.NextSibling; + } + } + + public string Value + { + get + { + if (this.IgnoreValue) + { + return ""; + } + if (_firstChild != null) + { + StringBuilder _bldr = new StringBuilder(); + XmlDiffNode _current = _firstChild; + do + { + if (_current is XmlDiffCharacterData && _current.NodeType != XmlDiffNodeType.Comment && _current.NodeType != XmlDiffNodeType.PI) + { + _bldr.Append(((XmlDiffCharacterData)_current).Value); + } + else if (_current is XmlDiffElement) + { + _bldr.Append(((XmlDiffElement)_current).Value); + } + _current = _current._next; + } + while (_current != null); + return _bldr.ToString(); + } + return ""; + } + } + } + + public class XmlDiffEmptyElement : XmlDiffElement + { + public XmlDiffEmptyElement(string localName, string prefix, string ns) : base(localName, prefix, ns) { } + } + + public class XmlDiffAttribute : XmlDiffNode + { + internal XmlDiffElement _ownerElement; + private string _lName; + private string _prefix; + private string _ns; + private string _value; + private XmlQualifiedName _valueAsQName; + + public XmlDiffAttribute(string localName, string prefix, string ns, string value) + : base() + { + _lName = localName; + _prefix = prefix; + _ns = ns; + _value = value; + } + + public string Value + { + get + { + if (this.IgnoreValue) + { + return ""; + } + return _value; + } + } + public XmlQualifiedName ValueAsQName + { + get { return _valueAsQName; } + } + + internal void SetValueAsQName(XmlReader reader, string value) + { + int indexOfColon = value.IndexOf(':'); + if (indexOfColon == -1) + { + _valueAsQName = new XmlQualifiedName(value); + } + else + { + string prefix = value.Substring(0, indexOfColon); + string ns = reader.LookupNamespace(prefix); + if (ns == null) + { + _valueAsQName = null; + } + else + { + try + { + string localName = XmlConvert.VerifyNCName(value.Substring(indexOfColon + 1)); + _valueAsQName = new XmlQualifiedName(localName, ns); + } + catch (XmlException) + { + _valueAsQName = null; + } + } + } + } + + public string LocalName { get { return _lName; } } + public string NamespaceURI { get { return _ns; } } + public string Prefix { get { return _prefix; } } + + public string Name + { + get + { + if (_prefix.Length > 0) + return _prefix + ":" + _lName; + else + return _lName; + } + } + public override XmlDiffNodeType NodeType { get { return XmlDiffNodeType.Attribute; } } + + public override void WriteTo(XmlWriter w) + { + w.WriteStartAttribute(Prefix, LocalName, NamespaceURI); + WriteContentTo(w); + w.WriteEndAttribute(); + } + + public override void WriteContentTo(XmlWriter w) + { + w.WriteString(Value); + } + } + + public class XmlDiffEntityReference : XmlDiffNode + { + private string _name; + public XmlDiffEntityReference(string name) + : base() + { + _name = name; + } + public override XmlDiffNodeType NodeType { get { return XmlDiffNodeType.ER; } } + public string Name { get { return _name; } } + + public override void WriteTo(XmlWriter w) + { + w.WriteEntityRef(_name); + } + + public override void WriteContentTo(XmlWriter w) + { + XmlDiffNode child = this.FirstChild; + while (child != null) + { + child.WriteTo(w); + child = child.NextSibling; + } + } + } + + public class XmlDiffCharacterData : XmlDiffNode + { + private string _value; + private XmlDiffNodeType _nodetype; + public XmlDiffCharacterData(string value, XmlDiffNodeType nt, bool NormalizeNewline) + : base() + { + _value = value; + if (NormalizeNewline) + { + _value = _value.Replace("\n", ""); + _value = _value.Replace("\r", ""); + } + _nodetype = nt; + } + + public string Value + { + get + { + if (this.IgnoreValue) + { + return ""; + } + return _value; + } + set + { + _value = value; + } + } + public override XmlDiffNodeType NodeType { get { return _nodetype; } } + + public override void WriteTo(XmlWriter w) + { + switch (_nodetype) + { + case XmlDiffNodeType.Comment: + w.WriteComment(Value); + break; + case XmlDiffNodeType.CData: + w.WriteCData(Value); + break; + case XmlDiffNodeType.WS: + case XmlDiffNodeType.Text: + w.WriteString(Value); + break; + default: + Debug.Assert(false, "Wrong type for text-like node : " + _nodetype.ToString()); + break; + } + } + + public override void WriteContentTo(XmlWriter w) { } + } + + public class XmlDiffProcessingInstruction : XmlDiffCharacterData + { + private string _name; + public XmlDiffProcessingInstruction(string name, string value) + : base(value, XmlDiffNodeType.PI, false) + { + _name = name; + } + public string Name { get { return _name; } } + + public override void WriteTo(XmlWriter w) + { + w.WriteProcessingInstruction(_name, Value); + } + public override void WriteContentTo(XmlWriter w) { } + } +} diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/Utils/XmlDiffOption.cs b/external/corefx/src/System.ServiceModel.Syndication/tests/Utils/XmlDiffOption.cs new file mode 100644 index 0000000000..ddcbd6e08d --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/Utils/XmlDiffOption.cs @@ -0,0 +1,96 @@ +// 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.Collections.Generic; + +namespace System.ServiceModel.Syndication.Tests +{ + [Flags] + public enum XmlDiffOption + { + None = 0x0, + IgnoreEmptyElement = 0x1, + IgnoreWhitespace = 0x2, + IgnoreComments = 0x4, + IgnoreAttributeOrder = 0x8, + IgnoreNS = 0x10, + IgnorePrefix = 0x20, + IgnoreDTD = 0x40, + IgnoreChildOrder = 0x80, + InfosetComparison = 0xB, //sets IgnoreEmptyElement, IgnoreWhitespace and IgnoreAttributeOrder + CDataAsText = 0x100, + NormalizeNewline = 0x200, // ignores newlines in text nodes only + ConcatenateAdjacentTextNodes = 0x400, // treats adjacent text nodes as a single node + TreatWhitespaceTextAsWSNode = 0x800, // if a text node contains only whitespaces, it will be considered a whitespace node + ParseAttributeValuesAsQName = 0x1000, // will be treated the same as + DontWriteMatchingNodesToOutput = 0x2000, // output will only contain different nodes + DontWriteAnythingToOutput = 0x4000, // output will not contain anything (needed for very large XML docs, which could trigger OOM) + IgnoreEmptyTextNodes = 0x8000, // empty text nodes (sometimes produced by the binary reader) are ignored + WhitespaceAsText = 0x10000, // consider whitespace nodes as text nodes + } + + public class XmlDiffAdvancedOptions + { + private string _ignoreNodesExpr; + private string _ignoreValuesExpr; + private string _ignoreChildOrderExpr; + private IDictionary _addedNamespaces; + + public XmlDiffAdvancedOptions() + { + _addedNamespaces = new Dictionary(); + } + + public string IgnoreNodesExpr + { + get + { + return _ignoreNodesExpr; + } + set + { + _ignoreNodesExpr = value; + } + } + + public string IgnoreValuesExpr + { + get + { + return _ignoreValuesExpr; + } + set + { + _ignoreValuesExpr = value; + } + } + + public string IgnoreChildOrderExpr + { + get + { + return _ignoreChildOrderExpr; + } + set + { + _ignoreChildOrderExpr = value; + } + } + + public void AddNamespace(string prefix, string uri) + { + _addedNamespaces[prefix] = uri; + } + + public bool HadAddedNamespace() + { + return 0 != _addedNamespaces.Count; + } + + public IDictionary AddedNamespaces + { + get { return _addedNamespaces; } + } + } +} diff --git a/external/corefx/src/System.ServiceModel.Syndication/tests/netcoreapp/BasicScenarioTests.netcoreapp.cs b/external/corefx/src/System.ServiceModel.Syndication/tests/netcoreapp/BasicScenarioTests.netcoreapp.cs new file mode 100644 index 0000000000..78a25b6868 --- /dev/null +++ b/external/corefx/src/System.ServiceModel.Syndication/tests/netcoreapp/BasicScenarioTests.netcoreapp.cs @@ -0,0 +1,122 @@ +// 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.Collections.Generic; +using System.Text; +using System.ServiceModel.Syndication; +using System.Xml; +using System.IO; +using Xunit; +using System.Linq; + +namespace System.ServiceModel.Syndication.Tests +{ + public static partial class BasicScenarioTests + { + [Fact] + public static void SyndicationFeed_Rss_DateTimeParser() + { + // *** SETUP *** \\ + // *** EXECUTE *** \\ + SyndicationFeed feed; + DateTimeOffset dto = new DateTimeOffset(2017, 1, 2, 3, 4, 5, new TimeSpan(0)); + using (XmlReader reader = XmlReader.Create(@"RssSpecCustomParser.xml")) + { + var formatter = new Rss20FeedFormatter(); + formatter.DateTimeParser = (value, localName, ns) => dto; + formatter.ReadFrom(reader); + feed = formatter.Feed; + } + + // *** ASSERT *** \\ + Assert.True(feed != null, "res was null."); + Assert.Equal(dto, feed.LastUpdatedTime); + } + + [Fact] + public static void SyndicationFeed_Rss_UriParser() + { + // *** SETUP *** \\ + // *** EXECUTE *** \\ + SyndicationFeed feed; + using (XmlReader reader = XmlReader.Create(@"RssSpecCustomParser.xml")) + { + var formatter = new Rss20FeedFormatter + { + UriParser = (value, kind, localName, ns) => new Uri($"http://value-{value}-kind-{kind}-localName-{localName}-ns-{ns}-end") + }; + formatter.ReadFrom(reader); + feed = formatter.Feed; + } + + // *** ASSERT *** \\ + Assert.True(feed != null, "res was null."); + Assert.Equal(new Uri("http://value-ChannelBase-kind-relativeorabsolute-localName-channel-ns--end"), feed.BaseUri); + Assert.Equal(new Uri("http://value-ImageUrl-kind-relativeorabsolute-localName-url-ns--end"), feed.ImageUrl); + Assert.NotNull(feed.Links); + Assert.Equal(1, feed.Links.Count); + Assert.Equal(new Uri("http://value-FeedLink-kind-relativeorabsolute-localName-link-ns--end"), feed.Links.First().Uri); + + Assert.True(feed.Items != null, "res.Items was null."); + Assert.Equal(1, feed.Items.Count()); + Assert.Equal(1, feed.Items.First().Links.Count); + Assert.Equal(new Uri("http://value-itemlink-kind-relativeorabsolute-localName-link-ns--end"), feed.Items.First().Links.First().Uri); + } + + [Fact] + public static void SyndicationFeed_Atom_DateTimeParser() + { + // *** SETUP *** \\ + // *** EXECUTE *** \\ + SyndicationFeed feed; + DateTimeOffset dto = new DateTimeOffset(2017, 1, 2, 3, 4, 5, new TimeSpan(0)); + using (XmlReader reader = XmlReader.Create(@"SimpleAtomFeedCustomParser.xml")) + { + var formatter = new Atom10FeedFormatter + { + DateTimeParser = (value, localName, ns) => dto + }; + formatter.ReadFrom(reader); + feed = formatter.Feed; + } + + // *** ASSERT *** \\ + Assert.True(feed != null, "res was null."); + Assert.Equal(dto, feed.LastUpdatedTime); + + Assert.True(feed.Items != null, "res.Items was null."); + Assert.Equal(1, feed.Items.Count()); + Assert.Equal(dto, feed.Items.First().LastUpdatedTime); + } + + [Fact] + public static void SyndicationFeed_Atom_UriParser() + { + // *** SETUP *** \\ + // *** EXECUTE *** \\ + SyndicationFeed feed; + using (XmlReader reader = XmlReader.Create(@"SimpleAtomFeedCustomParser.xml")) + { + var formatter = new Atom10FeedFormatter + { + UriParser = (value, kind, localName, ns) => new Uri($"http://value-{value}-kind-{kind}-localName-{localName}-ns-{ns}-end") + }; + formatter.ReadFrom(reader); + feed = formatter.Feed; + } + + // *** ASSERT *** \\ + Assert.True(feed != null, "res was null."); + Assert.Equal(new Uri("http://value-FeedLogo-kind-relativeorabsolute-localName-logo-ns-http//www.w3.org/2005/Atom-end"), feed.ImageUrl); + + Assert.True(feed.Items != null, "res.Items was null."); + Assert.Equal(1, feed.Items.Count()); + Assert.NotNull(feed.Items.First().Links); + Assert.Equal(1, feed.Items.First().Links.Count); + Assert.Equal(new Uri("http://value-EntryLinkHref-kind-relativeorabsolute-localName-link-ns-http//www.w3.org/2005/Atom-end"), feed.Items.First().Links.First().Uri); + Assert.Equal(new Uri("http://value-EntryContentSrc-kind-relativeorabsolute-localName-content-ns-http://www.w3.org/2005/Atom-end"), ((UrlSyndicationContent)feed.Items.First().Content).Url); + } + } +} diff --git a/external/corefx/src/System.ServiceProcess.ServiceController/System.ServiceProcess.ServiceController.sln b/external/corefx/src/System.ServiceProcess.ServiceController/System.ServiceProcess.ServiceController.sln index a5261436bb..8b57833f8c 100644 --- a/external/corefx/src/System.ServiceProcess.ServiceController/System.ServiceProcess.ServiceController.sln +++ b/external/corefx/src/System.ServiceProcess.ServiceController/System.ServiceProcess.ServiceController.sln @@ -39,10 +39,10 @@ Global {F7D9984B-02EB-4573-84EF-00FFFBFB872C}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU {F7D9984B-02EB-4573-84EF-00FFFBFB872C}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU {F7D9984B-02EB-4573-84EF-00FFFBFB872C}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU - {F4821CB6-91A3-4546-BC4F-E00DBFBDAA05}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU - {F4821CB6-91A3-4546-BC4F-E00DBFBDAA05}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU - {F4821CB6-91A3-4546-BC4F-E00DBFBDAA05}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU - {F4821CB6-91A3-4546-BC4F-E00DBFBDAA05}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {F4821CB6-91A3-4546-BC4F-E00DBFBDAA05}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU + {F4821CB6-91A3-4546-BC4F-E00DBFBDAA05}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU + {F4821CB6-91A3-4546-BC4F-E00DBFBDAA05}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU + {F4821CB6-91A3-4546-BC4F-E00DBFBDAA05}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU {8479566D-6FA5-4241-9D66-524BEC4C19BD}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU {8479566D-6FA5-4241-9D66-524BEC4C19BD}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU {8479566D-6FA5-4241-9D66-524BEC4C19BD}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU diff --git a/external/corefx/src/System.ServiceProcess.ServiceController/ref/System.ServiceProcess.ServiceController.cs b/external/corefx/src/System.ServiceProcess.ServiceController/ref/System.ServiceProcess.ServiceController.cs index e10f43db51..948bf0f33d 100644 --- a/external/corefx/src/System.ServiceProcess.ServiceController/ref/System.ServiceProcess.ServiceController.cs +++ b/external/corefx/src/System.ServiceProcess.ServiceController/ref/System.ServiceProcess.ServiceController.cs @@ -23,6 +23,7 @@ namespace System.ServiceProcess { public const int MaxNameLength = 80; public ServiceBase() { } + public bool AutoLog { get { throw null; } set { } } [System.ComponentModel.DefaultValueAttribute(false)] public bool CanHandlePowerEvent { get { throw null; } set { } } [System.ComponentModel.DefaultValueAttribute(false)] @@ -33,6 +34,7 @@ namespace System.ServiceProcess public bool CanShutdown { get { throw null; } set { } } [System.ComponentModel.DefaultValueAttribute(true)] public bool CanStop { get { throw null; } set { } } + public virtual System.Diagnostics.EventLog EventLog { get { throw null; } } public int ExitCode { get { throw null; } set { } } [System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(2))] protected System.IntPtr ServiceHandle { get { throw null; } } @@ -115,9 +117,9 @@ namespace System.ServiceProcess Win32OwnProcess = 16, Win32ShareProcess = 32, } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] - public partial struct SessionChangeDescription + public readonly partial struct SessionChangeDescription { + private readonly int _dummy; public System.ServiceProcess.SessionChangeReason Reason { get { throw null; } } public int SessionId { get { throw null; } } public override bool Equals(object obj) { throw null; } diff --git a/external/corefx/src/System.ServiceProcess.ServiceController/ref/System.ServiceProcess.ServiceController.csproj b/external/corefx/src/System.ServiceProcess.ServiceController/ref/System.ServiceProcess.ServiceController.csproj index dc2cae1910..744752fa0d 100644 --- a/external/corefx/src/System.ServiceProcess.ServiceController/ref/System.ServiceProcess.ServiceController.csproj +++ b/external/corefx/src/System.ServiceProcess.ServiceController/ref/System.ServiceProcess.ServiceController.csproj @@ -17,18 +17,8 @@ - - - - - - - - - - - - + + \ No newline at end of file diff --git a/external/corefx/src/System.ServiceProcess.ServiceController/src/Configurations.props b/external/corefx/src/System.ServiceProcess.ServiceController/src/Configurations.props index 02a949d632..ad80ed9c3b 100644 --- a/external/corefx/src/System.ServiceProcess.ServiceController/src/Configurations.props +++ b/external/corefx/src/System.ServiceProcess.ServiceController/src/Configurations.props @@ -1,10 +1,14 @@  - - netcoreapp2.0-Windows_NT; + netstandard; + netcoreapp2.0-Windows_NT; netfx-Windows_NT; + + + $(PackageConfigurations); + netcoreapp-Windows_NT; \ No newline at end of file diff --git a/external/corefx/src/System.ServiceProcess.ServiceController/src/System.ServiceProcess.ServiceController.csproj b/external/corefx/src/System.ServiceProcess.ServiceController/src/System.ServiceProcess.ServiceController.csproj index fca6f12460..617fbc9101 100644 --- a/external/corefx/src/System.ServiceProcess.ServiceController/src/System.ServiceProcess.ServiceController.csproj +++ b/external/corefx/src/System.ServiceProcess.ServiceController/src/System.ServiceProcess.ServiceController.csproj @@ -14,11 +14,13 @@ + + - + Common\Interop\Windows\Interop.Libraries.cs @@ -103,7 +105,7 @@ - + @@ -114,7 +116,11 @@ + + + + \ No newline at end of file diff --git a/external/corefx/src/System.ServiceProcess.ServiceController/src/System/ServiceProcess/ServiceBase.cs b/external/corefx/src/System.ServiceProcess.ServiceController/src/System/ServiceProcess/ServiceBase.cs index d4be605cd8..0286919d61 100644 --- a/external/corefx/src/System.ServiceProcess.ServiceController/src/System/ServiceProcess/ServiceBase.cs +++ b/external/corefx/src/System.ServiceProcess.ServiceController/src/System/ServiceProcess/ServiceBase.cs @@ -35,6 +35,7 @@ namespace System.ServiceProcess private bool _commandPropsFrozen; // set to true once we've use the Can... properties. private bool _disposed; private bool _initialized; + private EventLog _eventLog; /// /// @@ -50,6 +51,7 @@ namespace System.ServiceProcess { _acceptedCommands = AcceptOptions.ACCEPT_STOP; ServiceName = ""; + AutoLog = true; } /// @@ -75,6 +77,12 @@ namespace System.ServiceProcess } } + /// + /// Indicates whether to report Start, Stop, Pause, and Continue commands in the event + /// + [DefaultValue(true)] + public bool AutoLog { get; set; } + /// /// The termination code for the service. Set this to a non-zero value before /// stopping to indicate an error to the Service Control Manager. @@ -225,6 +233,24 @@ namespace System.ServiceProcess } } + /// + /// can be used to write notification of service command calls, such as Start and Stop, to the Application event log. This property is read-only. + /// + [Browsable (false), DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] + public virtual EventLog EventLog + { + get + { + if (_eventLog == null) + { + _eventLog = new EventLog("Application"); + _eventLog.Source = ServiceName; + } + + return _eventLog; + } + } + [EditorBrowsable(EditorBrowsableState.Advanced)] protected IntPtr ServiceHandle { @@ -889,10 +915,18 @@ namespace System.ServiceProcess private void WriteLogEntry(string message, bool error = false) { - // Used to write to EventLog but for now just logging to debug output stream - // might want to plumb other logging in the future. - - Debug.WriteLine((error ? "Error: " : "") + message); + // EventLog failures shouldn't affect the service operation + try + { + if (AutoLog) + { + EventLog.WriteEntry(message); + } + } + catch + { + // Do nothing. Not having the event log is bad, but not starting the service as a result is worse. + } } } } diff --git a/external/corefx/src/System.ServiceProcess.ServiceController/src/System/ServiceProcess/SessionChangeDescription.cs b/external/corefx/src/System.ServiceProcess.ServiceController/src/System/ServiceProcess/SessionChangeDescription.cs index 8889f18031..1e5ecd1266 100644 --- a/external/corefx/src/System.ServiceProcess.ServiceController/src/System/ServiceProcess/SessionChangeDescription.cs +++ b/external/corefx/src/System.ServiceProcess.ServiceController/src/System/ServiceProcess/SessionChangeDescription.cs @@ -4,7 +4,7 @@ namespace System.ServiceProcess { - public struct SessionChangeDescription + public readonly struct SessionChangeDescription { private readonly SessionChangeReason _reason; private readonly int _id; diff --git a/external/corefx/src/System.ServiceProcess.ServiceController/src/System/ServiceProcess/TimeoutException.cs b/external/corefx/src/System.ServiceProcess.ServiceController/src/System/ServiceProcess/TimeoutException.cs index e64d5606cd..895f726db2 100644 --- a/external/corefx/src/System.ServiceProcess.ServiceController/src/System/ServiceProcess/TimeoutException.cs +++ b/external/corefx/src/System.ServiceProcess.ServiceController/src/System/ServiceProcess/TimeoutException.cs @@ -8,6 +8,9 @@ using System.Runtime.Serialization; namespace System.ServiceProcess { [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.ServiceProcess, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] +#endif public class TimeoutException : SystemException { private const int ServiceControllerTimeout = unchecked((int)0x80131906); @@ -31,7 +34,6 @@ namespace System.ServiceProcess protected TimeoutException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corefx/src/System.ServiceProcess.ServiceController/tests/ServiceBaseTests.cs b/external/corefx/src/System.ServiceProcess.ServiceController/tests/ServiceBaseTests.cs index 96fd003295..e09e915ac5 100644 --- a/external/corefx/src/System.ServiceProcess.ServiceController/tests/ServiceBaseTests.cs +++ b/external/corefx/src/System.ServiceProcess.ServiceController/tests/ServiceBaseTests.cs @@ -20,6 +20,7 @@ namespace System.ServiceProcess.Tests private static readonly Lazy s_isElevated = new Lazy(() => AdminHelpers.IsProcessElevated()); protected static bool IsProcessElevated => s_isElevated.Value; + protected static bool IsElevatedAndSupportsEventLogs => IsProcessElevated && PlatformDetection.IsNotWindowsNanoServer; private bool _disposed; @@ -174,6 +175,46 @@ OnStop Assert.Equal(expected, _testService.GetServiceOutput()); } + [ConditionalFact(nameof(IsElevatedAndSupportsEventLogs))] + public void LogWritten() + { + using (EventLog eventLog = new EventLog("Application")) + { + ServiceBase sb = new ServiceBase() { ServiceName = nameof(LogWritten) + Guid.NewGuid().ToString() }; + Assert.False(EventLog.SourceExists(sb.ServiceName)); + try + { + ServiceBase.Run(sb); + eventLog.Source = sb.ServiceName; + Assert.True(EventLog.SourceExists(sb.ServiceName)); + } + finally + { + sb.Stop(); + EventLog.DeleteEventSource(sb.ServiceName); + } + } + } + + [ConditionalFact(nameof(IsElevatedAndSupportsEventLogs))] + public void LogWritten_AutoLog_False() + { + using (EventLog eventLog = new EventLog("Application")) + { + ServiceBase sb = new ServiceBase() { ServiceName = nameof(LogWritten) + Guid.NewGuid().ToString(), AutoLog = false }; + Assert.False(EventLog.SourceExists(sb.ServiceName)); + try + { + ServiceBase.Run(sb); + Assert.False(EventLog.SourceExists(sb.ServiceName)); + } + finally + { + sb.Stop(); + } + } + } + public void Dispose() { if (!_disposed) diff --git a/external/corefx/src/System.ServiceProcess.ServiceController/tests/System.ServiceProcess.ServiceController.TestService/Program.cs b/external/corefx/src/System.ServiceProcess.ServiceController/tests/System.ServiceProcess.ServiceController.TestService/Program.cs index e085b6a0c5..e812baca1f 100644 --- a/external/corefx/src/System.ServiceProcess.ServiceController/tests/System.ServiceProcess.ServiceController.TestService/Program.cs +++ b/external/corefx/src/System.ServiceProcess.ServiceController/tests/System.ServiceProcess.ServiceController.TestService/Program.cs @@ -1,4 +1,8 @@ -using System; +// 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.Collections.Generic; using System.IO; using System.Text; diff --git a/external/corefx/src/System.ServiceProcess.ServiceController/tests/System.ServiceProcess.ServiceController.TestService/TestService.cs b/external/corefx/src/System.ServiceProcess.ServiceController/tests/System.ServiceProcess.ServiceController.TestService/TestService.cs index 24a8467e3b..93445c4b09 100644 --- a/external/corefx/src/System.ServiceProcess.ServiceController/tests/System.ServiceProcess.ServiceController.TestService/TestService.cs +++ b/external/corefx/src/System.ServiceProcess.ServiceController/tests/System.ServiceProcess.ServiceController.TestService/TestService.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System; using System.Collections; using System.IO; +using System.Reflection; using System.Threading; using System.Text; using System.Runtime.InteropServices; @@ -30,6 +31,11 @@ namespace System.ServiceProcess.Tests this.CanHandlePowerEvent = false; } + public static string GetLogPath(string serviceName) + { + return typeof(TestService).Assembly.Location + "." + serviceName + ".log"; + } + protected override void OnContinue() { WriteLog(nameof(OnContinue)); @@ -68,6 +74,8 @@ namespace System.ServiceProcess.Tests protected override void OnStart(string[] args) { + File.Delete(GetLogPath(ServiceName)); + WriteLog(nameof(OnStart) + " args=" + string.Join(",", args)); base.OnStart(args); } @@ -76,26 +84,11 @@ namespace System.ServiceProcess.Tests { WriteLog(nameof(OnStop)); base.OnStop(); - - if (_log != null) - { - _log.Dispose(); - _log = null; - } } - private StreamWriter _log; - private void WriteLog(string msg) { - if (_log == null) - { - string path = System.Reflection.Assembly.GetEntryAssembly().Location + "." + ServiceName + ".log"; - _log = new StreamWriter(path); - _log.AutoFlush = true; -; } - - _log.WriteLine(msg); + File.AppendAllText(GetLogPath(ServiceName), msg + Environment.NewLine); } } } diff --git a/external/corefx/src/System.ServiceProcess.ServiceController/tests/System.ServiceProcess.ServiceController.TestService/TestServiceInstaller.cs b/external/corefx/src/System.ServiceProcess.ServiceController/tests/System.ServiceProcess.ServiceController.TestService/TestServiceInstaller.cs index 878f560386..0f68314816 100644 --- a/external/corefx/src/System.ServiceProcess.ServiceController/tests/System.ServiceProcess.ServiceController.TestService/TestServiceInstaller.cs +++ b/external/corefx/src/System.ServiceProcess.ServiceController/tests/System.ServiceProcess.ServiceController.TestService/TestServiceInstaller.cs @@ -146,9 +146,20 @@ namespace System.ServiceProcess.Tests // Stop the service using (ServiceController svc = new ServiceController(ServiceName)) { + // The Service exists at this point, but OpenService is failing, possibly because its being invoked concurrently for another service. + // https://github.com/dotnet/corefx/issues/23388 if (svc.Status != ServiceControllerStatus.Stopped) { - svc.Stop(); + try + { + svc.Stop(); + } + catch (InvalidOperationException) + { + ServiceName = null; + return; + } + svc.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(30)); } } diff --git a/external/corefx/src/System.ServiceProcess.ServiceController/tests/TestServiceProvider.cs b/external/corefx/src/System.ServiceProcess.ServiceController/tests/TestServiceProvider.cs index 30b499a324..47800befb6 100644 --- a/external/corefx/src/System.ServiceProcess.ServiceController/tests/TestServiceProvider.cs +++ b/external/corefx/src/System.ServiceProcess.ServiceController/tests/TestServiceProvider.cs @@ -96,7 +96,15 @@ namespace System.ServiceProcess.Tests if (File.Exists(LogPath)) { - File.Delete(LogPath); + try + { + File.Delete(LogPath); + } + catch (IOException) + { + // Don't fail simply because the service was not fully cleaned up + // and is still holding a handle to the log file + } } } finally @@ -110,7 +118,7 @@ namespace System.ServiceProcess.Tests } } - private string LogPath => typeof(TestService).Assembly.Location + "." + TestServiceName + ".log"; + private string LogPath => TestService.GetLogPath(TestServiceName); public string GetServiceOutput() { diff --git a/external/corefx/src/System.Text.Encoding.CodePages/ref/System.Text.Encoding.CodePages.cs b/external/corefx/src/System.Text.Encoding.CodePages/ref/System.Text.Encoding.CodePages.cs index b3fcb078a6..71c444cab2 100644 --- a/external/corefx/src/System.Text.Encoding.CodePages/ref/System.Text.Encoding.CodePages.cs +++ b/external/corefx/src/System.Text.Encoding.CodePages/ref/System.Text.Encoding.CodePages.cs @@ -8,7 +8,6 @@ namespace System.Text { - [System.Security.SecurityCriticalAttribute] public sealed partial class CodePagesEncodingProvider { internal CodePagesEncodingProvider() { } diff --git a/external/corefx/src/System.Text.Encoding.CodePages/src/Configurations.props b/external/corefx/src/System.Text.Encoding.CodePages/src/Configurations.props index 8dcacd78b1..5a32b85e4d 100644 --- a/external/corefx/src/System.Text.Encoding.CodePages/src/Configurations.props +++ b/external/corefx/src/System.Text.Encoding.CodePages/src/Configurations.props @@ -3,11 +3,12 @@ netstandard; - netcoreapp-Windows_NT; + netcoreapp2.0-Windows_NT; netstandard-Windows_NT; $(PackageConfigurations); + netcoreapp-Windows_NT; uap-Windows_NT; diff --git a/external/corefx/src/System.Text.Encoding.CodePages/src/Microsoft/Win32/SafeHandles/SafeAllocHHandle.cs b/external/corefx/src/System.Text.Encoding.CodePages/src/Microsoft/Win32/SafeHandles/SafeAllocHHandle.cs index b0ef5f23e8..f1290f399a 100644 --- a/external/corefx/src/System.Text.Encoding.CodePages/src/Microsoft/Win32/SafeHandles/SafeAllocHHandle.cs +++ b/external/corefx/src/System.Text.Encoding.CodePages/src/Microsoft/Win32/SafeHandles/SafeAllocHHandle.cs @@ -8,7 +8,6 @@ using System.Security; namespace Microsoft.Win32.SafeHandles { - [SecurityCritical] internal sealed class SafeAllocHHandle : SafeBuffer { private SafeAllocHHandle() : base(true) { } @@ -23,7 +22,6 @@ namespace Microsoft.Win32.SafeHandles get { return new SafeAllocHHandle(IntPtr.Zero); } } - [System.Security.SecurityCritical] override protected bool ReleaseHandle() { if (handle != IntPtr.Zero) diff --git a/external/corefx/src/System.Text.Encoding.CodePages/src/System.Text.Encoding.CodePages.csproj b/external/corefx/src/System.Text.Encoding.CodePages/src/System.Text.Encoding.CodePages.csproj index 77023d590f..fdc22bcfb6 100644 --- a/external/corefx/src/System.Text.Encoding.CodePages/src/System.Text.Encoding.CodePages.csproj +++ b/external/corefx/src/System.Text.Encoding.CodePages/src/System.Text.Encoding.CodePages.csproj @@ -12,6 +12,8 @@ + + @@ -64,7 +66,6 @@ - diff --git a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/BaseCodePageEncoding.cs b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/BaseCodePageEncoding.cs index 406f8a51f4..4954da4d77 100644 --- a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/BaseCodePageEncoding.cs +++ b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/BaseCodePageEncoding.cs @@ -55,13 +55,11 @@ namespace System.Text protected char[] arrayUnicodeBestFit = null; protected char[] arrayBytesBestFit = null; - [System.Security.SecurityCritical] // auto-generated internal BaseCodePageEncoding(int codepage) : this(codepage, codepage) { } - [System.Security.SecurityCritical] // auto-generated internal BaseCodePageEncoding(int codepage, int dataCodePage) : base(codepage, new InternalEncoderBestFitFallback(null), new InternalDecoderBestFitFallback(null)) { @@ -157,7 +155,6 @@ namespace System.Text protected int m_dataSize; // Safe handle wrapper around section map view - [System.Security.SecurityCritical] // auto-generated protected SafeAllocHHandle safeNativeMemoryHandle = null; internal static Stream GetEncodingDataStream(String tableName) @@ -182,7 +179,6 @@ namespace System.Text } // We need to load tables for our code page - [System.Security.SecurityCritical] // auto-generated private unsafe void LoadCodePageTables() { if (!FindCodePage(dataTableCodePage)) @@ -196,7 +192,6 @@ namespace System.Text } // Look up the code page pointer - [System.Security.SecurityCritical] // auto-generated private unsafe bool FindCodePage(int codePage) { Debug.Assert(m_codePageHeader != null && m_codePageHeader.Length == CODEPAGE_HEADER_SIZE, "m_codePageHeader expected to match in size the struct CodePageHeader"); @@ -255,7 +250,6 @@ namespace System.Text } // Get our code page byte count - [System.Security.SecurityCritical] // auto-generated internal static unsafe int GetCodePageByteSize(int codePage) { // Loop through all of the m_pCodePageIndex[] items to find our code page @@ -296,11 +290,9 @@ namespace System.Text } // We have a managed code page entry, so load our tables - [System.Security.SecurityCritical] protected abstract unsafe void LoadManagedCodePage(); // Allocate memory to load our code page - [System.Security.SecurityCritical] // auto-generated protected unsafe byte* GetNativeMemory(int iSize) { if (safeNativeMemoryHandle == null) @@ -314,10 +306,8 @@ namespace System.Text return (byte*)safeNativeMemoryHandle.DangerousGetHandle(); } - [System.Security.SecurityCritical] protected abstract unsafe void ReadBestFitTable(); - [System.Security.SecuritySafeCritical] internal char[] GetBestFitUnicodeToBytesData() { // Read in our best fit table if necessary @@ -329,7 +319,6 @@ namespace System.Text return arrayUnicodeBestFit; } - [System.Security.SecuritySafeCritical] internal char[] GetBestFitBytesToUnicodeData() { // Read in our best fit table if necessary @@ -345,7 +334,6 @@ namespace System.Text // invalid. We detect that by validating the memory section handle then re-initializing the memory // section by calling LoadManagedCodePage() method and eventually the mapped file handle and // the memory section pointer will get finalized one more time. - [System.Security.SecurityCritical] // auto-generated internal unsafe void CheckMemorySection() { if (safeNativeMemoryHandle != null && safeNativeMemoryHandle.DangerousGetHandle() == IntPtr.Zero) diff --git a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/CodePagesEncodingProvider.cs b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/CodePagesEncodingProvider.cs index 3f884bb8f1..99f0284a3b 100644 --- a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/CodePagesEncodingProvider.cs +++ b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/CodePagesEncodingProvider.cs @@ -2,7 +2,6 @@ // 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.Diagnostics.Contracts; using System.Collections.Generic; using System.Threading; diff --git a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/DBCSCodePageEncoding.cs b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/DBCSCodePageEncoding.cs index ee183276bf..03a37da0f0 100644 --- a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/DBCSCodePageEncoding.cs +++ b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/DBCSCodePageEncoding.cs @@ -5,7 +5,6 @@ using System; using System.IO; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Text; using System.Threading; using System.Security; @@ -18,9 +17,7 @@ namespace System.Text internal class DBCSCodePageEncoding : BaseCodePageEncoding { // Pointers to our memory section parts - [SecurityCritical] protected unsafe char* mapBytesToUnicode = null; // char 65536 - [SecurityCritical] protected unsafe ushort* mapUnicodeToBytes = null; // byte 65536 protected const char UNKNOWN_CHAR_FLAG = (char)0x0; @@ -33,17 +30,14 @@ namespace System.Text private int _byteCountUnknown; protected char charUnknown = (char)0; - [System.Security.SecurityCritical] // auto-generated public DBCSCodePageEncoding(int codePage) : this(codePage, codePage) { } - [System.Security.SecurityCritical] // auto-generated internal DBCSCodePageEncoding(int codePage, int dataCodePage) : base(codePage, dataCodePage) { } - [System.Security.SecurityCritical] // auto-generated internal DBCSCodePageEncoding(int codePage, int dataCodePage, EncoderFallback enc, DecoderFallback dec) : base(codePage, dataCodePage, enc, dec) { } @@ -76,7 +70,6 @@ namespace System.Text // correspond to those unicode code points. // We have a managed code page entry, so load our tables // - [System.Security.SecurityCritical] // auto-generated protected override unsafe void LoadManagedCodePage() { Debug.Assert(m_codePageHeader?.Length > 0); @@ -212,7 +205,6 @@ namespace System.Text } // Any special processing for this code page - [System.Security.SecurityCritical] // auto-generated protected virtual unsafe void CleanUpEndBytes(char* chars) { } @@ -233,7 +225,6 @@ namespace System.Text } // Read in our best fit table - [System.Security.SecurityCritical] // auto-generated protected unsafe override void ReadBestFitTable() { // Lock so we don't confuse ourselves. @@ -512,7 +503,6 @@ namespace System.Text // GetByteCount // Note: We start by assuming that the output will be the same as count. Having // an encoder or fallback may change that assumption - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetByteCount(char* chars, int count, EncoderNLS encoder) { // Just need to ASSERT, this is called by something else internal that checked parameters already @@ -608,7 +598,6 @@ namespace System.Text return (int)byteCount; } - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount, EncoderNLS encoder) { @@ -759,7 +748,6 @@ namespace System.Text } // This is internal and called by something else, - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder) { // Just assert, we're called internally so these should be safe, checked already @@ -910,7 +898,6 @@ namespace System.Text return charCount; } - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetChars(byte* bytes, int byteCount, char* chars, int charCount, DecoderNLS baseDecoder) { @@ -1120,7 +1107,6 @@ namespace System.Text { if (charCount < 0) throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Characters would be # of characters + 1 in case high surrogate is ? * max fallback long byteCount = (long)charCount + 1; @@ -1141,7 +1127,6 @@ namespace System.Text { if (byteCount < 0) throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // DBCS is pretty much the same, but could have hanging high byte making extra ? and fallback for unknown long charCount = ((long)byteCount + 1); diff --git a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/DecoderBestFitFallback.cs b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/DecoderBestFitFallback.cs index 1c9c4af6ae..2e640856f6 100644 --- a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/DecoderBestFitFallback.cs +++ b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/DecoderBestFitFallback.cs @@ -155,14 +155,12 @@ namespace System.Text } // Clear the buffer - [System.Security.SecuritySafeCritical] // overrides public transparent member public override unsafe void Reset() { iCount = -1; } // This version just counts the fallback and doesn't actually copy anything. - [System.Security.SecurityCritical] // auto-generated internal unsafe int InternalFallback(byte[] bytes, byte* pBytes) // Right now this has both bytes and bytes[], since we might have extra bytes, hence the // array, and we might need the index, hence the byte* diff --git a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/DecoderFallbackBufferHelper.cs b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/DecoderFallbackBufferHelper.cs index 91b8c371b6..e4e82b47b2 100644 --- a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/DecoderFallbackBufferHelper.cs +++ b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/DecoderFallbackBufferHelper.cs @@ -94,7 +94,6 @@ namespace System.Text } // This version just counts the fallback and doesn't actually copy anything. - [System.Security.SecurityCritical] // auto-generated internal unsafe int InternalFallback(byte[] bytes, byte* pBytes) // Right now this has both bytes and bytes[], since we might have extra bytes, hence the // array, and we might need the index, hence the byte* diff --git a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/DecoderNLS.cs b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/DecoderNLS.cs index 0f17a0242c..cc702bde56 100644 --- a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/DecoderNLS.cs +++ b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/DecoderNLS.cs @@ -5,7 +5,6 @@ using System.Text; using System; using System.Globalization; -using System.Diagnostics.Contracts; using System.Runtime.Serialization; namespace System.Text @@ -91,7 +90,6 @@ namespace System.Text return GetCharCount(bytes, index, count, false); } - [System.Security.SecuritySafeCritical] // auto-generated public override unsafe int GetCharCount(byte[] bytes, int index, int count, bool flush) { // Validate Parameters @@ -104,8 +102,6 @@ namespace System.Text if (bytes.Length - index < count) throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); - // Avoid null fixed problem if (bytes.Length == 0) bytes = new byte[1]; @@ -115,7 +111,6 @@ namespace System.Text return GetCharCount(pBytes + index, count, flush); } - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetCharCount(byte* bytes, int count, bool flush) { // Validate parameters @@ -124,7 +119,6 @@ namespace System.Text if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Remember the flush m_mustFlush = flush; @@ -140,7 +134,6 @@ namespace System.Text return GetChars(bytes, byteIndex, byteCount, chars, charIndex, false); } - [System.Security.SecuritySafeCritical] // auto-generated public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex, bool flush) { @@ -157,8 +150,6 @@ namespace System.Text if (charIndex < 0 || charIndex > chars.Length) throw new ArgumentOutOfRangeException(nameof(charIndex), SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); - // Avoid empty input fixed problem if (bytes.Length == 0) bytes = new byte[1]; @@ -175,7 +166,6 @@ namespace System.Text pChars + charIndex, charCount, flush); } - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetChars(byte* bytes, int byteCount, char* chars, int charCount, bool flush) { @@ -185,7 +175,6 @@ namespace System.Text if (byteCount < 0 || charCount < 0) throw new ArgumentOutOfRangeException((byteCount < 0 ? nameof(byteCount): nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Remember our flush m_mustFlush = flush; @@ -197,7 +186,6 @@ namespace System.Text // This method is used when the output buffer might not be big enough. // Just call the pointer version. (This gets chars) - [System.Security.SecuritySafeCritical] // auto-generated public override unsafe void Convert(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex, int charCount, bool flush, out int bytesUsed, out int charsUsed, out bool completed) @@ -218,8 +206,6 @@ namespace System.Text if (chars.Length - charIndex < charCount) throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); - // Avoid empty input problem if (bytes.Length == 0) bytes = new byte[1]; @@ -239,7 +225,6 @@ namespace System.Text // This is the version that used pointers. We call the base encoding worker function // after setting our appropriate internal variables. This is getting chars - [System.Security.SecurityCritical] // auto-generated public override unsafe void Convert(byte* bytes, int byteCount, char* chars, int charCount, bool flush, out int bytesUsed, out int charsUsed, out bool completed) @@ -250,7 +235,6 @@ namespace System.Text if (byteCount < 0 || charCount < 0) throw new ArgumentOutOfRangeException((byteCount < 0 ? nameof(byteCount): nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // We don't want to throw m_mustFlush = flush; diff --git a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EUCJPEncoding.cs b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EUCJPEncoding.cs index d438d1093d..0f73aa0feb 100644 --- a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EUCJPEncoding.cs +++ b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EUCJPEncoding.cs @@ -49,7 +49,6 @@ namespace System.Text internal class EUCJPEncoding : DBCSCodePageEncoding { // This pretends to be CP 932 as far as memory tables are concerned. - [System.Security.SecurityCritical] // auto-generated public EUCJPEncoding() : base(51932, 932) { } @@ -146,7 +145,6 @@ namespace System.Text return true; } - [System.Security.SecurityCritical] // auto-generated protected override unsafe void CleanUpEndBytes(char* chars) { // Need to special case CP 51932 diff --git a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EncoderBestFitFallback.cs b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EncoderBestFitFallback.cs index a755d3ae21..be816767f8 100644 --- a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EncoderBestFitFallback.cs +++ b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EncoderBestFitFallback.cs @@ -8,7 +8,6 @@ using System; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Globalization; using System.Text; using System.Threading; @@ -122,7 +121,6 @@ namespace System.Text if (!Char.IsLowSurrogate(charUnknownLow)) throw new ArgumentOutOfRangeException(nameof(charUnknownLow), SR.Format(SR.ArgumentOutOfRange_Range, 0xDC00, 0xDFFF)); - Contract.EndContractBlock(); // If we had a buffer already we're being recursive, throw, it's probably at the suspect // character in our array. 0 is processing last character, < 0 is not falling back @@ -180,7 +178,6 @@ namespace System.Text } // Clear the buffer - [System.Security.SecuritySafeCritical] // overrides public transparent member public override unsafe void Reset() { _iCount = -1; diff --git a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EncoderFallbackBufferHelper.cs b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EncoderFallbackBufferHelper.cs index e786f631a2..60d31cd788 100644 --- a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EncoderFallbackBufferHelper.cs +++ b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EncoderFallbackBufferHelper.cs @@ -30,7 +30,6 @@ namespace System.Text // Internal Reset // For example, what if someone fails a conversion and wants to reset one of our fallback buffers? - [System.Security.SecurityCritical] // auto-generated internal unsafe void InternalReset() { charStart = null; @@ -41,7 +40,6 @@ namespace System.Text // Set the above values // This can't be part of the constructor because EncoderFallbacks would have to know how to implement these. - [System.Security.SecurityCritical] // auto-generated internal unsafe void InternalInitialize(char* _charStart, char* _charEnd, EncoderNLS _encoder, bool _setEncoder) { charStart = _charStart; @@ -69,7 +67,6 @@ namespace System.Text // Note that this could also change the contents of encoder, which is the same // object that the caller is using, so the caller could mess up the encoder for us // if they aren't careful. - [System.Security.SecurityCritical] // auto-generated internal unsafe bool InternalFallback(char ch, ref char* chars) { // Shouldn't have null charStart diff --git a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EncoderNLS.cs b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EncoderNLS.cs index f29d5bd81d..36dbaa2bfc 100644 --- a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EncoderNLS.cs +++ b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EncoderNLS.cs @@ -4,7 +4,6 @@ using System.Text; using System; -using System.Diagnostics.Contracts; using System.Runtime.Serialization; namespace System.Text @@ -89,7 +88,6 @@ namespace System.Text m_fallbackBuffer.Reset(); } - [System.Security.SecuritySafeCritical] // auto-generated public override unsafe int GetByteCount(char[] chars, int index, int count, bool flush) { // Validate input parameters @@ -101,7 +99,6 @@ namespace System.Text if (chars.Length - index < count) throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); // Avoid empty input problem if (chars.Length == 0) @@ -116,7 +113,6 @@ namespace System.Text return result; } - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetByteCount(char* chars, int count, bool flush) { // Validate input parameters @@ -125,14 +121,12 @@ namespace System.Text if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); m_mustFlush = flush; m_throwOnOverflow = true; return m_encoding.GetByteCount(chars, count, this); } - [System.Security.SecuritySafeCritical] // auto-generated public override unsafe int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex, bool flush) { @@ -148,7 +142,6 @@ namespace System.Text if (byteIndex < 0 || byteIndex > bytes.Length) throw new ArgumentOutOfRangeException(nameof(byteIndex), SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); if (chars.Length == 0) chars = new char[1]; @@ -166,7 +159,6 @@ namespace System.Text pBytes + byteIndex, byteCount, flush); } - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount, bool flush) { // Validate parameters @@ -175,7 +167,6 @@ namespace System.Text if (byteCount < 0 || charCount < 0) throw new ArgumentOutOfRangeException((byteCount < 0 ? nameof(byteCount): nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); m_mustFlush = flush; m_throwOnOverflow = true; @@ -184,7 +175,6 @@ namespace System.Text // This method is used when your output buffer might not be large enough for the entire result. // Just call the pointer version. (This gets bytes) - [System.Security.SecuritySafeCritical] // auto-generated public override unsafe void Convert(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex, int byteCount, bool flush, out int charsUsed, out int bytesUsed, out bool completed) @@ -205,8 +195,6 @@ namespace System.Text if (bytes.Length - byteIndex < byteCount) throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); - // Avoid empty input problem if (chars.Length == 0) chars = new char[1]; @@ -226,7 +214,6 @@ namespace System.Text // This is the version that uses pointers. We call the base encoding worker function // after setting our appropriate internal variables. This is getting bytes - [System.Security.SecurityCritical] // auto-generated public override unsafe void Convert(char* chars, int charCount, byte* bytes, int byteCount, bool flush, out int charsUsed, out int bytesUsed, out bool completed) @@ -237,7 +224,6 @@ namespace System.Text if (charCount < 0 || byteCount < 0) throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount): nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // We don't want to throw m_mustFlush = flush; diff --git a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EncodingCharBuffer.cs b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EncodingCharBuffer.cs index bdb19d5205..b7ada0d17f 100644 --- a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EncodingCharBuffer.cs +++ b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EncodingCharBuffer.cs @@ -20,7 +20,6 @@ namespace System.Text private DecoderFallbackBuffer _fallbackBuffer; private DecoderFallbackBufferHelper _fallbackBufferHelper; - [System.Security.SecurityCritical] // auto-generated internal unsafe EncodingCharBuffer(EncodingNLS enc, DecoderNLS decoder, char* charStart, int charCount, byte* byteStart, int byteCount) { _enc = enc; @@ -47,7 +46,6 @@ namespace System.Text _fallbackBufferHelper.InternalInitialize(_bytes, _charEnd); } - [System.Security.SecurityCritical] // auto-generated internal unsafe bool AddChar(char ch, int numBytes) { if (_chars != null) @@ -66,14 +64,12 @@ namespace System.Text return true; } - [System.Security.SecurityCritical] // auto-generated internal unsafe bool AddChar(char ch) { return AddChar(ch, 1); } - [System.Security.SecurityCritical] // auto-generated internal unsafe bool AddChar(char ch1, char ch2, int numBytes) { // Need room for 2 chars @@ -87,7 +83,6 @@ namespace System.Text return AddChar(ch1, numBytes) && AddChar(ch2, numBytes); } - [System.Security.SecurityCritical] // auto-generated internal unsafe void AdjustBytes(int count) { _bytes = unchecked(_bytes + count); @@ -95,7 +90,6 @@ namespace System.Text internal unsafe bool MoreData { - [System.Security.SecurityCritical] // auto-generated get { return _bytes < _byteEnd; @@ -103,7 +97,6 @@ namespace System.Text } // Do we have count more bytes? - [System.Security.SecurityCritical] // auto-generated internal unsafe bool EvenMoreData(int count) { return (_bytes <= _byteEnd - count); @@ -111,7 +104,6 @@ namespace System.Text // GetNextByte shouldn't be called unless the caller's already checked more data or even more data, // but we'll double check just to make sure. - [System.Security.SecurityCritical] // auto-generated internal unsafe byte GetNextByte() { Debug.Assert(_bytes < _byteEnd, "[EncodingCharBuffer.GetNextByte]Expected more date"); @@ -122,14 +114,12 @@ namespace System.Text internal unsafe int BytesUsed { - [System.Security.SecurityCritical] // auto-generated get { return unchecked((int)(_bytes - _byteStart)); } } - [System.Security.SecurityCritical] // auto-generated internal unsafe bool Fallback(byte fallbackByte) { // Build our buffer @@ -139,7 +129,6 @@ namespace System.Text return Fallback(byteBuffer); } - [System.Security.SecurityCritical] // auto-generated internal unsafe bool Fallback(byte byte1, byte byte2) { // Build our buffer @@ -149,7 +138,6 @@ namespace System.Text return Fallback(byteBuffer); } - [System.Security.SecurityCritical] // auto-generated internal unsafe bool Fallback(byte byte1, byte byte2, byte byte3, byte byte4) { // Build our buffer @@ -159,7 +147,6 @@ namespace System.Text return Fallback(byteBuffer); } - [System.Security.SecurityCritical] // auto-generated internal unsafe bool Fallback(byte[] byteBuffer) { // Do the fallback and add the data. diff --git a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EncodingNLS.cs b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EncodingNLS.cs index 87b246d2c4..71c1f51db7 100644 --- a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EncodingNLS.cs +++ b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/EncodingNLS.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics.Contracts; using System.Collections; using System.Globalization; using System.Resources; @@ -47,7 +46,6 @@ namespace System.Text // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) // So if you fix this, fix the others. // parent method is safe - [System.Security.SecuritySafeCritical] // overrides public transparent member public override unsafe int GetByteCount(char[] chars, int index, int count) { // Validate input parameters @@ -59,7 +57,6 @@ namespace System.Text if (chars.Length - index < count) throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); // If no input, return 0, avoid fixed empty array problem if (chars.Length == 0) @@ -73,13 +70,11 @@ namespace System.Text // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) // So if you fix this, fix the others. // parent method is safe - [System.Security.SecuritySafeCritical] // overrides public transparent member public override unsafe int GetByteCount(String s) { // Validate input if (s == null) throw new ArgumentNullException(nameof(s)); - Contract.EndContractBlock(); fixed (char* pChars = s) return GetByteCount(pChars, s.Length, null); @@ -87,7 +82,6 @@ namespace System.Text // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) // So if you fix this, fix the others. - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetByteCount(char* chars, int count) { // Validate Parameters @@ -96,7 +90,6 @@ namespace System.Text if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Call it with empty encoder return GetByteCount(chars, count, null); @@ -106,7 +99,6 @@ namespace System.Text // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) // So if you fix this, fix the others. - [System.Security.SecuritySafeCritical] // overrides public transparent member public override unsafe int GetBytes(String s, int charIndex, int charCount, byte[] bytes, int byteIndex) { @@ -121,7 +113,6 @@ namespace System.Text if (byteIndex < 0 || byteIndex > bytes.Length) throw new ArgumentOutOfRangeException(nameof(byteIndex), SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); int byteCount = bytes.Length - byteIndex; @@ -147,7 +138,6 @@ namespace System.Text // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) // So if you fix this, fix the others. // parent method is safe - [System.Security.SecuritySafeCritical] // overrides public transparent member public override unsafe int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) { @@ -163,7 +153,6 @@ namespace System.Text if (byteIndex < 0 || byteIndex > bytes.Length) throw new ArgumentOutOfRangeException(nameof(byteIndex), SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); // If nothing to encode return 0, avoid fixed problem if (chars.Length == 0) @@ -185,7 +174,6 @@ namespace System.Text // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) // So if you fix this, fix the others. - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount) { // Validate Parameters @@ -194,7 +182,6 @@ namespace System.Text if (charCount < 0 || byteCount < 0) throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount): nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); return GetBytes(chars, charCount, bytes, byteCount, null); } @@ -205,7 +192,6 @@ namespace System.Text // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) // So if you fix this, fix the others. // parent method is safe - [System.Security.SecuritySafeCritical] // overrides public transparent member public override unsafe int GetCharCount(byte[] bytes, int index, int count) { // Validate Parameters @@ -217,7 +203,6 @@ namespace System.Text if (bytes.Length - index < count) throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); // If no input just return 0, fixed doesn't like 0 length arrays if (bytes.Length == 0) @@ -230,7 +215,6 @@ namespace System.Text // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) // So if you fix this, fix the others. - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetCharCount(byte* bytes, int count) { // Validate Parameters @@ -239,7 +223,6 @@ namespace System.Text if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); return GetCharCount(bytes, count, null); } @@ -247,7 +230,6 @@ namespace System.Text // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) // So if you fix this, fix the others. // parent method is safe - [System.Security.SecuritySafeCritical] // overrides public transparent member public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) { @@ -263,7 +245,6 @@ namespace System.Text if (charIndex < 0 || charIndex > chars.Length) throw new ArgumentOutOfRangeException(nameof(charIndex), SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); // If no input, return 0 & avoid fixed problem if (bytes.Length == 0) @@ -285,7 +266,6 @@ namespace System.Text // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) // So if you fix this, fix the others. - [System.Security.SecurityCritical] // auto-generated public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount) { // Validate Parameters @@ -294,7 +274,6 @@ namespace System.Text if (charCount < 0 || byteCount < 0) throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount): nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); return GetChars(bytes, byteCount, chars, charCount, null); } @@ -305,7 +284,6 @@ namespace System.Text // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) // So if you fix this, fix the others. // parent method is safe - [System.Security.SecuritySafeCritical] // overrides public transparent member public override unsafe String GetString(byte[] bytes, int index, int count) { // Validate Parameters @@ -317,7 +295,6 @@ namespace System.Text if (bytes.Length - index < count) throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); // Avoid problems with empty input buffer if (bytes.Length == 0) return String.Empty; diff --git a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/GB18030Encoding.cs b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/GB18030Encoding.cs index 522886819e..fab6988add 100644 --- a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/GB18030Encoding.cs +++ b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/GB18030Encoding.cs @@ -85,7 +85,6 @@ using System; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Text; using System.Runtime.InteropServices; using System.Security; @@ -116,7 +115,6 @@ namespace System.Text private const int GBLastSurrogateOffset = 0x12E247; // GBE3329A35 // We have to load the 936 code page tables, so impersonate 936 as our base - [System.Security.SecurityCritical] // auto-generated internal GB18030Encoding() // For GB18030Encoding just use default replacement fallbacks because its only for bad surrogates : base(GB18030, 936, EncoderFallback.ReplacementFallback, DecoderFallback.ReplacementFallback) @@ -125,7 +123,6 @@ namespace System.Text // This loads our base 936 code page and then applies the changes from the tableUnicodeToGBDiffs table. // See table comments for table format. - [System.Security.SecurityCritical] // auto-generated protected override unsafe void LoadManagedCodePage() { // Use base code page loading algorithm. @@ -199,7 +196,6 @@ namespace System.Text // Is4Byte // Checks the 4 byte table and returns true if this is a 4 byte code. // Its a 4 byte code if the flag is set in mapUnicodeTo4BytesFlags - [System.Security.SecurityCritical] // auto-generated internal unsafe bool Is4Byte(char charTest) { // See what kind it is @@ -208,14 +204,12 @@ namespace System.Text } // GetByteCount - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetByteCount(char* chars, int count, EncoderNLS encoder) { // Just call GetBytes() with null bytes return GetBytes(chars, count, null, 0, encoder); } - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount, EncoderNLS encoder) { @@ -396,14 +390,12 @@ namespace System.Text } // This is internal and called by something else, - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder) { // Just call GetChars() with null chars to count return GetChars(bytes, count, null, 0, baseDecoder); } - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetChars(byte* bytes, int byteCount, char* chars, int charCount, DecoderNLS baseDecoder) { @@ -753,7 +745,6 @@ namespace System.Text { if (charCount < 0) throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Characters would be # of characters + 1 in case high surrogate is ? * max fallback long byteCount = (long)charCount + 1; @@ -774,7 +765,6 @@ namespace System.Text { if (byteCount < 0) throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Just return length, we could have a single char for each byte + whatever extra our decoder could do to us. // If decoder is messed up it could spit out 3 ?s. diff --git a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/ISCIIEncoding.cs.REMOVED.git-id b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/ISCIIEncoding.cs.REMOVED.git-id index 2e20635469..487291f9b8 100644 --- a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/ISCIIEncoding.cs.REMOVED.git-id +++ b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/ISCIIEncoding.cs.REMOVED.git-id @@ -1 +1 @@ -779fa4d26cd1990b1117f33b3e7ca94bbdd820dc \ No newline at end of file +65577abf17686484e6183eac153faf7fdef3cfdc \ No newline at end of file diff --git a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/ISO2022Encoding.cs b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/ISO2022Encoding.cs index 6a74ed7442..e3a875b305 100644 --- a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/ISO2022Encoding.cs +++ b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/ISO2022Encoding.cs @@ -34,7 +34,6 @@ using System.Globalization; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Text; using System.Runtime.InteropServices; using System; @@ -59,7 +58,6 @@ namespace System.Text // We have to load the 936 code page tables, so impersonate 936 as our base // This pretends to be other code pages as far as memory sections are concerned. - [System.Security.SecurityCritical] // auto-generated internal ISO2022Encoding(int codePage) : base(codePage, s_tableBaseCodePages[codePage % 10]) { } @@ -224,7 +222,6 @@ namespace System.Text } // GetByteCount - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetByteCount(char* chars, int count, EncoderNLS baseEncoder) { // Just need to ASSERT, this is called by something else internal that checked parameters already @@ -235,7 +232,6 @@ namespace System.Text return GetBytes(chars, count, null, 0, baseEncoder); } - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount, EncoderNLS baseEncoder) { @@ -277,7 +273,6 @@ namespace System.Text } // This is internal and called by something else, - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder) { // Just assert, we're called internally so these should be safe, checked already @@ -288,7 +283,6 @@ namespace System.Text return GetChars(bytes, count, null, 0, baseDecoder); } - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetChars(byte* bytes, int byteCount, char* chars, int charCount, DecoderNLS baseDecoder) { @@ -360,7 +354,6 @@ namespace System.Text // undefined, so we maintain that behavior when decoding. We will never generate characters using // that technique, but the decoder will process them. // - [System.Security.SecurityCritical] // auto-generated private unsafe int GetBytesCP5022xJP(char* chars, int charCount, byte* bytes, int byteCount, ISO2022Encoder encoder) { @@ -602,7 +595,6 @@ namespace System.Text // Also Mlang always assumed KR mode, even if the designator wasn't found yet, so we do that as // well. So basically we just ignore $)C when decoding. // - [System.Security.SecurityCritical] // auto-generated private unsafe int GetBytesCP50225KR(char* chars, int charCount, byte* bytes, int byteCount, ISO2022Encoder encoder) { @@ -752,7 +744,6 @@ namespace System.Text // // This encoding is designed for transmission by e-mail and news. No bytes should have high bit set. // (all bytes <= 0x7f) - [System.Security.SecurityCritical] // auto-generated private unsafe int GetBytesCP52936(char* chars, int charCount, byte* bytes, int byteCount, ISO2022Encoder encoder) { @@ -886,7 +877,6 @@ namespace System.Text return buffer.Count; } - [System.Security.SecurityCritical] // auto-generated private unsafe int GetCharsCP5022xJP(byte* bytes, int byteCount, char* chars, int charCount, ISO2022Decoder decoder) { @@ -1212,7 +1202,6 @@ namespace System.Text // Note that in DBCS mode mlang passed through ' ', '\t' and '\n' as SBCS characters // probably to allow mailer formatting without too much extra work. - [System.Security.SecurityCritical] // auto-generated private unsafe int GetCharsCP50225KR(byte* bytes, int byteCount, char* chars, int charCount, ISO2022Decoder decoder) { @@ -1450,7 +1439,6 @@ namespace System.Text // // This encoding is designed for transmission by e-mail and news. No bytes should have high bit set. // (all bytes <= 0x7f) - [System.Security.SecurityCritical] // auto-generated private unsafe int GetCharsCP52936(byte* bytes, int byteCount, char* chars, int charCount, ISO2022Decoder decoder) { @@ -1708,7 +1696,6 @@ namespace System.Text { if (charCount < 0) throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Characters would be # of characters + 1 in case high surrogate is ? * max fallback long byteCount = (long)charCount + 1; @@ -1762,7 +1749,6 @@ namespace System.Text { if (byteCount < 0) throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); int perChar = 1; int extraDecoder = 1; diff --git a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/SBCSCodePageEncoding.cs b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/SBCSCodePageEncoding.cs index 188c74133d..981782fbbb 100644 --- a/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/SBCSCodePageEncoding.cs +++ b/external/corefx/src/System.Text.Encoding.CodePages/src/System/Text/SBCSCodePageEncoding.cs @@ -5,7 +5,6 @@ using System; using System.IO; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Text; using System.Threading; using System.Globalization; @@ -17,9 +16,7 @@ namespace System.Text internal class SBCSCodePageEncoding : BaseCodePageEncoding { // Pointers to our memory section parts - [SecurityCritical] private unsafe char* _mapBytesToUnicode = null; // char 256 - [SecurityCritical] private unsafe byte* _mapUnicodeToBytes = null; // byte 65536 private const char UNKNOWN_CHAR = (char)0xFFFD; @@ -28,12 +25,10 @@ namespace System.Text private byte _byteUnknown; private char _charUnknown; - [System.Security.SecurityCritical] // auto-generated public SBCSCodePageEncoding(int codePage) : this(codePage, codePage) { } - [System.Security.SecurityCritical] // auto-generated public SBCSCodePageEncoding(int codePage, int dataCodePage) : base(codePage, dataCodePage) { } @@ -72,7 +67,6 @@ namespace System.Text // byte < 0x20 means skip the next n positions. (Where n is the byte #) // byte == 1 means that next word is another unicode code point # // byte == 0 is unknown. (doesn't override initial WCHAR[256] table! - [System.Security.SecurityCritical] // auto-generated protected override unsafe void LoadManagedCodePage() { Debug.Assert(m_codePageHeader?.Length > 0); @@ -159,7 +153,6 @@ namespace System.Text } // Read in our best fit table - [System.Security.SecurityCritical] // auto-generated protected unsafe override void ReadBestFitTable() { // Lock so we don't confuse ourselves. @@ -314,7 +307,6 @@ namespace System.Text // GetByteCount // Note: We start by assuming that the output will be the same as count. Having // an encoder or fallback may change that assumption - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetByteCount(char* chars, int count, EncoderNLS encoder) { // Just need to ASSERT, this is called by something else internal that checked parameters already @@ -439,7 +431,6 @@ namespace System.Text return (int)byteCount; } - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount, EncoderNLS encoder) { @@ -675,7 +666,6 @@ namespace System.Text } // This is internal and called by something else, - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS decoder) { // Just assert, we're called internally so these should be safe, checked already @@ -763,7 +753,6 @@ namespace System.Text return charCount; } - [System.Security.SecurityCritical] // auto-generated public override unsafe int GetChars(byte* bytes, int byteCount, char* chars, int charCount, DecoderNLS decoder) { @@ -926,7 +915,6 @@ namespace System.Text { if (charCount < 0) throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Characters would be # of characters + 1 in case high surrogate is ? * max fallback long byteCount = (long)charCount + 1; @@ -945,7 +933,6 @@ namespace System.Text { if (byteCount < 0) throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Just return length, SBCS stay the same length because they don't map to surrogate long charCount = (long)byteCount; diff --git a/external/corefx/src/System.Text.Encoding.CodePages/tests/System.Text.Encoding.CodePages.Tests.csproj b/external/corefx/src/System.Text.Encoding.CodePages/tests/System.Text.Encoding.CodePages.Tests.csproj index 4624105c12..4520bf9505 100644 --- a/external/corefx/src/System.Text.Encoding.CodePages/tests/System.Text.Encoding.CodePages.Tests.csproj +++ b/external/corefx/src/System.Text.Encoding.CodePages/tests/System.Text.Encoding.CodePages.Tests.csproj @@ -4,7 +4,6 @@ {835AD07B-7C9A-406F-B16F-59B3B0D017A4} - @@ -14,4 +13,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Text.Encoding.Extensions/ref/System.Text.Encoding.Extensions.cs b/external/corefx/src/System.Text.Encoding.Extensions/ref/System.Text.Encoding.Extensions.cs index 6095f5888f..807e95450e 100644 --- a/external/corefx/src/System.Text.Encoding.Extensions/ref/System.Text.Encoding.Extensions.cs +++ b/external/corefx/src/System.Text.Encoding.Extensions/ref/System.Text.Encoding.Extensions.cs @@ -13,21 +13,17 @@ namespace System.Text public ASCIIEncoding() { } public override bool IsSingleByte { get { throw null; } } [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] public unsafe override int GetByteCount(char* chars, int count) { throw null; } public override int GetByteCount(char[] chars, int index, int count) { throw null; } public override int GetByteCount(string chars) { throw null; } [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] public unsafe override int GetBytes(char* chars, int charCount, byte* bytes, int byteCount) { throw null; } public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) { throw null; } public override int GetBytes(string chars, int charIndex, int charCount, byte[] bytes, int byteIndex) { throw null; } [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] public unsafe override int GetCharCount(byte* bytes, int count) { throw null; } public override int GetCharCount(byte[] bytes, int index, int count) { throw null; } [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount) { throw null; } public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) { throw null; } public override System.Text.Decoder GetDecoder() { throw null; } @@ -45,20 +41,16 @@ namespace System.Text public override bool Equals(object value) { throw null; } public override int GetByteCount(char[] chars, int index, int count) { throw null; } [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] public unsafe override int GetByteCount(char* chars, int count) { throw null; } public override int GetByteCount(string s) { throw null; } [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] public unsafe override int GetBytes(char* chars, int charCount, byte* bytes, int byteCount) { throw null; } public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) { throw null; } public override int GetBytes(string s, int charIndex, int charCount, byte[] bytes, int byteIndex) { throw null; } [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] public unsafe override int GetCharCount(byte* bytes, int count) { throw null; } public override int GetCharCount(byte[] bytes, int index, int count) { throw null; } [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount) { throw null; } public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) { throw null; } public override System.Text.Decoder GetDecoder() { throw null; } @@ -76,21 +68,17 @@ namespace System.Text public UTF32Encoding(bool bigEndian, bool byteOrderMark, bool throwOnInvalidCharacters) { } public override bool Equals(object value) { throw null; } [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] public unsafe override int GetByteCount(char* chars, int count) { throw null; } public override int GetByteCount(char[] chars, int index, int count) { throw null; } public override int GetByteCount(string s) { throw null; } [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] public unsafe override int GetBytes(char* chars, int charCount, byte* bytes, int byteCount) { throw null; } public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) { throw null; } public override int GetBytes(string s, int charIndex, int charCount, byte[] bytes, int byteIndex) { throw null; } [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] public unsafe override int GetCharCount(byte* bytes, int count) { throw null; } public override int GetCharCount(byte[] bytes, int index, int count) { throw null; } [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount) { throw null; } public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) { throw null; } public override System.Text.Decoder GetDecoder() { throw null; } @@ -107,21 +95,17 @@ namespace System.Text public UTF7Encoding(bool allowOptionals) { } public override bool Equals(object value) { throw null; } [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] public unsafe override int GetByteCount(char* chars, int count) { throw null; } public override int GetByteCount(char[] chars, int index, int count) { throw null; } public override int GetByteCount(string s) { throw null; } [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] public unsafe override int GetBytes(char* chars, int charCount, byte* bytes, int byteCount) { throw null; } public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) { throw null; } public override int GetBytes(string s, int charIndex, int charCount, byte[] bytes, int byteIndex) { throw null; } [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] public unsafe override int GetCharCount(byte* bytes, int count) { throw null; } public override int GetCharCount(byte[] bytes, int index, int count) { throw null; } [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount) { throw null; } public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) { throw null; } public override System.Text.Decoder GetDecoder() { throw null; } @@ -138,21 +122,17 @@ namespace System.Text public UTF8Encoding(bool encoderShouldEmitUTF8Identifier, bool throwOnInvalidBytes) { } public override bool Equals(object value) { throw null; } [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] public unsafe override int GetByteCount(char* chars, int count) { throw null; } public override int GetByteCount(char[] chars, int index, int count) { throw null; } public override int GetByteCount(string chars) { throw null; } [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] public unsafe override int GetBytes(char* chars, int charCount, byte* bytes, int byteCount) { throw null; } public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) { throw null; } public override int GetBytes(string s, int charIndex, int charCount, byte[] bytes, int byteIndex) { throw null; } [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] public unsafe override int GetCharCount(byte* bytes, int count) { throw null; } public override int GetCharCount(byte[] bytes, int index, int count) { throw null; } [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount) { throw null; } public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) { throw null; } public override System.Text.Decoder GetDecoder() { throw null; } diff --git a/external/corefx/src/System.Text.Encoding.Extensions/tests/System.Text.Encoding.Extensions.Tests.csproj b/external/corefx/src/System.Text.Encoding.Extensions/tests/System.Text.Encoding.Extensions.Tests.csproj index 79b3bbc80a..2b997def56 100644 --- a/external/corefx/src/System.Text.Encoding.Extensions/tests/System.Text.Encoding.Extensions.Tests.csproj +++ b/external/corefx/src/System.Text.Encoding.Extensions/tests/System.Text.Encoding.Extensions.Tests.csproj @@ -5,7 +5,6 @@ {037D5B14-EEE1-43C4-8AA8-9F276C0C10CF} true - diff --git a/external/corefx/src/System.Text.Encoding/System.Text.Encoding.sln b/external/corefx/src/System.Text.Encoding/System.Text.Encoding.sln index 8b820a3a56..890a739352 100644 --- a/external/corefx/src/System.Text.Encoding/System.Text.Encoding.sln +++ b/external/corefx/src/System.Text.Encoding/System.Text.Encoding.sln @@ -35,10 +35,10 @@ Global {3BB28F2F-51DF-49A3-A0BF-E1C5C0D7E3E0}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {3BB28F2F-51DF-49A3-A0BF-E1C5C0D7E3E0}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU {3BB28F2F-51DF-49A3-A0BF-E1C5C0D7E3E0}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU - {859C92CB-72FB-453C-A363-7CD025E29B1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {859C92CB-72FB-453C-A363-7CD025E29B1D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {859C92CB-72FB-453C-A363-7CD025E29B1D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {859C92CB-72FB-453C-A363-7CD025E29B1D}.Release|Any CPU.Build.0 = Release|Any CPU + {859C92CB-72FB-453C-A363-7CD025E29B1D}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {859C92CB-72FB-453C-A363-7CD025E29B1D}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {859C92CB-72FB-453C-A363-7CD025E29B1D}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {859C92CB-72FB-453C-A363-7CD025E29B1D}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU {635F30B9-5566-4096-B772-68FAA9B00DF4}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU {635F30B9-5566-4096-B772-68FAA9B00DF4}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU {635F30B9-5566-4096-B772-68FAA9B00DF4}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU diff --git a/external/corefx/src/System.Text.Encoding/src/ApiCompatBaseline.uapaot.txt b/external/corefx/src/System.Text.Encoding/src/ApiCompatBaseline.uapaot.txt deleted file mode 100644 index 92acc2a6e5..0000000000 --- a/external/corefx/src/System.Text.Encoding/src/ApiCompatBaseline.uapaot.txt +++ /dev/null @@ -1,10 +0,0 @@ -Compat issues with assembly System.Text.Encoding: -MembersMustExist : Member 'System.Text.Encoding.BodyName.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Text.Encoding.GetEncodings()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Text.Encoding.HeaderName.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Text.Encoding.IsBrowserDisplay.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Text.Encoding.IsBrowserSave.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Text.Encoding.IsMailNewsDisplay.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Text.Encoding.IsMailNewsSave.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Text.Encoding.WindowsCodePage.get()' does not exist in the implementation but it does exist in the contract. -Total Issues: 8 diff --git a/external/corefx/src/System.Text.Encoding/tests/Performance/System.Text.Encoding.Performance.Tests.csproj b/external/corefx/src/System.Text.Encoding/tests/Performance/System.Text.Encoding.Performance.Tests.csproj index cd468e427c..02cb446865 100644 --- a/external/corefx/src/System.Text.Encoding/tests/Performance/System.Text.Encoding.Performance.Tests.csproj +++ b/external/corefx/src/System.Text.Encoding/tests/Performance/System.Text.Encoding.Performance.Tests.csproj @@ -6,9 +6,8 @@ true {859C92CB-72FB-453C-A363-7CD025E29B1D} - - - + + diff --git a/external/corefx/src/System.Text.Encoding/tests/System.Text.Encoding.Tests.csproj b/external/corefx/src/System.Text.Encoding/tests/System.Text.Encoding.Tests.csproj index d016ca2f63..f9f3aed6ae 100644 --- a/external/corefx/src/System.Text.Encoding/tests/System.Text.Encoding.Tests.csproj +++ b/external/corefx/src/System.Text.Encoding/tests/System.Text.Encoding.Tests.csproj @@ -6,7 +6,6 @@ true $(DefineConstants);netcoreapp - @@ -83,4 +82,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Text.Encodings.Web/src/System.Text.Encodings.Web.csproj b/external/corefx/src/System.Text.Encodings.Web/src/System.Text.Encodings.Web.csproj index 97dd539417..ebe050c6b3 100644 --- a/external/corefx/src/System.Text.Encodings.Web/src/System.Text.Encodings.Web.csproj +++ b/external/corefx/src/System.Text.Encodings.Web/src/System.Text.Encodings.Web.csproj @@ -7,7 +7,6 @@ true $(OutputPath)$(MSBuildProjectName).xml - @@ -32,7 +31,6 @@ - @@ -43,4 +41,4 @@ - \ No newline at end of file + diff --git a/external/corefx/src/System.Text.Encodings.Web/src/System/Text/Encodings/Web/AllowedCharactersBitmap.cs b/external/corefx/src/System.Text.Encodings.Web/src/System/Text/Encodings/Web/AllowedCharactersBitmap.cs index cf2d154ae9..a5878cfb32 100644 --- a/external/corefx/src/System.Text.Encodings.Web/src/System/Text/Encodings/Web/AllowedCharactersBitmap.cs +++ b/external/corefx/src/System.Text.Encodings.Web/src/System/Text/Encodings/Web/AllowedCharactersBitmap.cs @@ -8,7 +8,7 @@ using System.Text.Unicode; namespace System.Text.Internal { - internal struct AllowedCharactersBitmap + internal readonly struct AllowedCharactersBitmap { private const int ALLOWED_CHARS_BITMAP_LENGTH = 0x10000 / (8 * sizeof(uint)); private readonly uint[] _allowedCharacters; diff --git a/external/corefx/src/System.Text.Encodings.Web/src/System/Text/Encodings/Web/BufferInternal.cs b/external/corefx/src/System.Text.Encodings.Web/src/System/Text/Encodings/Web/BufferInternal.cs index a47ea868eb..dd2aa1ca80 100644 --- a/external/corefx/src/System.Text.Encodings.Web/src/System/Text/Encodings/Web/BufferInternal.cs +++ b/external/corefx/src/System.Text.Encodings.Web/src/System/Text/Encodings/Web/BufferInternal.cs @@ -14,7 +14,6 @@ namespace System { static class BufferInternal { // This method has different signature for x64 and other platforms and is done for performance reasons. - [System.Security.SecurityCritical] private static unsafe void Memmove(byte* dest, byte* src, uint len) { if (AreOverlapping(dest, src, len)) @@ -187,7 +186,6 @@ namespace System { // The attributes on this method are chosen for best JIT performance. // Please do not edit unless intentional. - [System.Security.SecurityCritical] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void MemoryCopy(void* source, void* destination, int destinationSizeInBytes, int sourceBytesToCopy) { diff --git a/external/corefx/src/System.Text.Encodings.Web/tests/System.Text.Encodings.Web.Tests.csproj b/external/corefx/src/System.Text.Encodings.Web/tests/System.Text.Encodings.Web.Tests.csproj index 09ef643e7b..6f2cc61d9e 100644 --- a/external/corefx/src/System.Text.Encodings.Web/tests/System.Text.Encodings.Web.Tests.csproj +++ b/external/corefx/src/System.Text.Encodings.Web/tests/System.Text.Encodings.Web.Tests.csproj @@ -10,7 +10,6 @@ ..\..\ true - diff --git a/external/corefx/src/System.Text.RegularExpressions/src/System.Text.RegularExpressions.csproj b/external/corefx/src/System.Text.RegularExpressions/src/System.Text.RegularExpressions.csproj index 7ccb1589a4..8b3d02dd91 100644 --- a/external/corefx/src/System.Text.RegularExpressions/src/System.Text.RegularExpressions.csproj +++ b/external/corefx/src/System.Text.RegularExpressions/src/System.Text.RegularExpressions.csproj @@ -6,7 +6,6 @@ System.Text.RegularExpressions $(DefineConstants);FEATURE_COMPILED - @@ -67,4 +66,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs b/external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs index ce8c5dd81e..d078cda53a 100644 --- a/external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs +++ b/external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs @@ -1120,7 +1120,7 @@ namespace System.Text.RegularExpressions /* * Used as a key for CacheCodeEntry */ - internal struct CachedCodeEntryKey : IEquatable + internal readonly struct CachedCodeEntryKey : IEquatable { private readonly RegexOptions _options; private readonly string _cultureKey; diff --git a/external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs b/external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs index 320ccdde79..d4ab864958 100644 --- a/external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs +++ b/external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs @@ -1323,7 +1323,7 @@ namespace System.Text.RegularExpressions /// /// Lower case mapping descriptor. /// - private struct LowerCaseMapping + private readonly struct LowerCaseMapping { internal LowerCaseMapping(char chMin, char chMax, int lcOp, int data) { @@ -1359,7 +1359,7 @@ namespace System.Text.RegularExpressions /// /// A first/last pair representing a single range of characters. /// - private struct SingleRange + private readonly struct SingleRange { internal SingleRange(char first, char last) { diff --git a/external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexInterpreter.cs b/external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexInterpreter.cs index 04d4cf2f90..4a1f0d8814 100644 --- a/external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexInterpreter.cs +++ b/external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexInterpreter.cs @@ -34,11 +34,6 @@ namespace System.Text.RegularExpressions runtrackcount = _code._trackcount; } - private void Advance() - { - Advance(0); - } - private void Advance(int i) { _codepos += (i + 1); @@ -457,8 +452,16 @@ namespace System.Text.RegularExpressions { Goto(0); + int advance = -1; for (; ;) { + if (advance >= 0) + { + // https://github.com/dotnet/coreclr/pull/14850#issuecomment-342256447 + // Single common Advance call to reduce method size; and single method inline point + Advance(advance); + advance = -1; + } #if DEBUG if (runmatch.Debug) { @@ -483,12 +486,12 @@ namespace System.Text.RegularExpressions case RegexCode.Testref: if (!IsMatched(Operand(0))) break; - Advance(1); + advance = 1; continue; case RegexCode.Lazybranch: TrackPush(Textpos()); - Advance(1); + advance = 1; continue; case RegexCode.Lazybranch | RegexCode.Back: @@ -500,13 +503,13 @@ namespace System.Text.RegularExpressions case RegexCode.Setmark: StackPush(Textpos()); TrackPush(); - Advance(); + advance = 0; continue; case RegexCode.Nullmark: StackPush(-1); TrackPush(); - Advance(); + advance = 0; continue; case RegexCode.Setmark | RegexCode.Back: @@ -518,7 +521,7 @@ namespace System.Text.RegularExpressions StackPop(); TrackPush(StackPeek()); Textto(StackPeek()); - Advance(); + advance = 0; continue; case RegexCode.Getmark | RegexCode.Back: @@ -536,7 +539,7 @@ namespace System.Text.RegularExpressions Capture(Operand(0), StackPeek(), Textpos()); TrackPush(StackPeek()); - Advance(2); + advance = 2; continue; @@ -565,7 +568,7 @@ namespace System.Text.RegularExpressions else { // Empty match -> straight now TrackPush2(StackPeek()); // Save old mark - Advance(1); // Straight + advance = 1; // Straight } continue; } @@ -575,7 +578,7 @@ namespace System.Text.RegularExpressions StackPop(); Textto(TrackPeek(1)); // Recall position TrackPush2(TrackPeek()); // Save old mark - Advance(1); // Straight + advance = 1; // Straight continue; case RegexCode.Branchmark | RegexCode.Back2: @@ -609,7 +612,7 @@ namespace System.Text.RegularExpressions TrackPush2(StackPeek()); // Save old mark } - Advance(1); + advance = 1; continue; } @@ -642,13 +645,13 @@ namespace System.Text.RegularExpressions case RegexCode.Setcount: StackPush(Textpos(), Operand(0)); TrackPush(); - Advance(1); + advance = 1; continue; case RegexCode.Nullcount: StackPush(-1, Operand(0)); TrackPush(); - Advance(1); + advance = 1; continue; case RegexCode.Setcount | RegexCode.Back: @@ -672,7 +675,7 @@ namespace System.Text.RegularExpressions if (count >= Operand(1) || (matched == 0 && count >= 0)) { // Max loops or empty match -> straight now TrackPush2(mark, count); // Save old mark, count - Advance(2); // Straight + advance = 2; // Straight } else { // Nonempty match -> count+loop now @@ -695,7 +698,7 @@ namespace System.Text.RegularExpressions { // Positive -> can go straight Textto(StackPeek()); // Zap to mark TrackPush2(TrackPeek(), StackPeek(1) - 1); // Save old mark, old count - Advance(2); // Straight + advance = 2; // Straight continue; } StackPush(TrackPeek(), StackPeek(1) - 1); // recall old mark, old count @@ -728,7 +731,7 @@ namespace System.Text.RegularExpressions else { // Nonneg count -> straight now TrackPush(mark, count, Textpos()); // Save mark, count, position - Advance(2); // Straight + advance = 2; // Straight } continue; } @@ -772,7 +775,7 @@ namespace System.Text.RegularExpressions case RegexCode.Setjump: StackPush(Trackpos(), Crawlpos()); TrackPush(); - Advance(); + advance = 0; continue; case RegexCode.Setjump | RegexCode.Back: @@ -798,7 +801,7 @@ namespace System.Text.RegularExpressions StackPop(2); Trackto(StackPeek()); TrackPush(StackPeek(1)); - Advance(); + advance = 0; continue; case RegexCode.Forejump | RegexCode.Back: @@ -814,82 +817,82 @@ namespace System.Text.RegularExpressions case RegexCode.Bol: if (Leftchars() > 0 && CharAt(Textpos() - 1) != '\n') break; - Advance(); + advance = 0; continue; case RegexCode.Eol: if (Rightchars() > 0 && CharAt(Textpos()) != '\n') break; - Advance(); + advance = 0; continue; case RegexCode.Boundary: if (!IsBoundary(Textpos(), runtextbeg, runtextend)) break; - Advance(); + advance = 0; continue; case RegexCode.Nonboundary: if (IsBoundary(Textpos(), runtextbeg, runtextend)) break; - Advance(); + advance = 0; continue; case RegexCode.ECMABoundary: if (!IsECMABoundary(Textpos(), runtextbeg, runtextend)) break; - Advance(); + advance = 0; continue; case RegexCode.NonECMABoundary: if (IsECMABoundary(Textpos(), runtextbeg, runtextend)) break; - Advance(); + advance = 0; continue; case RegexCode.Beginning: if (Leftchars() > 0) break; - Advance(); + advance = 0; continue; case RegexCode.Start: if (Textpos() != Textstart()) break; - Advance(); + advance = 0; continue; case RegexCode.EndZ: if (Rightchars() > 1 || Rightchars() == 1 && CharAt(Textpos()) != '\n') break; - Advance(); + advance = 0; continue; case RegexCode.End: if (Rightchars() > 0) break; - Advance(); + advance = 0; continue; case RegexCode.One: if (Forwardchars() < 1 || Forwardcharnext() != (char)Operand(0)) break; - Advance(1); + advance = 1; continue; case RegexCode.Notone: if (Forwardchars() < 1 || Forwardcharnext() == (char)Operand(0)) break; - Advance(1); + advance = 1; continue; case RegexCode.Set: if (Forwardchars() < 1 || !RegexCharClass.CharInClass(Forwardcharnext(), _code._strings[Operand(0)])) break; - Advance(1); + advance = 1; continue; case RegexCode.Multi: @@ -897,7 +900,7 @@ namespace System.Text.RegularExpressions if (!Stringmatch(_code._strings[Operand(0)])) break; - Advance(1); + advance = 1; continue; } @@ -916,7 +919,7 @@ namespace System.Text.RegularExpressions break; } - Advance(1); + advance = 1; continue; } @@ -933,7 +936,7 @@ namespace System.Text.RegularExpressions if (Forwardcharnext() != ch) goto BreakBackward; - Advance(2); + advance = 2; continue; } @@ -950,7 +953,7 @@ namespace System.Text.RegularExpressions if (Forwardcharnext() == ch) goto BreakBackward; - Advance(2); + advance = 2; continue; } @@ -967,7 +970,7 @@ namespace System.Text.RegularExpressions if (!RegexCharClass.CharInClass(Forwardcharnext(), set)) goto BreakBackward; - Advance(2); + advance = 2; continue; } @@ -993,7 +996,7 @@ namespace System.Text.RegularExpressions if (c > i) TrackPush(c - i - 1, Textpos() - Bump()); - Advance(2); + advance = 2; continue; } @@ -1019,7 +1022,7 @@ namespace System.Text.RegularExpressions if (c > i) TrackPush(c - i - 1, Textpos() - Bump()); - Advance(2); + advance = 2; continue; } @@ -1045,7 +1048,7 @@ namespace System.Text.RegularExpressions if (c > i) TrackPush(c - i - 1, Textpos() - Bump()); - Advance(2); + advance = 2; continue; } @@ -1061,7 +1064,7 @@ namespace System.Text.RegularExpressions if (i > 0) TrackPush(i - 1, pos - Bump()); - Advance(2); + advance = 2; continue; } @@ -1076,7 +1079,7 @@ namespace System.Text.RegularExpressions if (i > 0) TrackPush(i - 1, pos - Bump()); - Advance(2); + advance = 2; continue; } @@ -1091,7 +1094,7 @@ namespace System.Text.RegularExpressions if (c > 0) TrackPush(c - 1, Textpos()); - Advance(2); + advance = 2; continue; } @@ -1105,7 +1108,7 @@ namespace System.Text.RegularExpressions if (c > 0) TrackPush(c - 1, Textpos()); - Advance(2); + advance = 2; continue; } @@ -1123,7 +1126,7 @@ namespace System.Text.RegularExpressions if (i > 0) TrackPush(i - 1, pos + Bump()); - Advance(2); + advance = 2; continue; } @@ -1141,7 +1144,7 @@ namespace System.Text.RegularExpressions if (i > 0) TrackPush(i - 1, pos + Bump()); - Advance(2); + advance = 2; continue; } @@ -1159,7 +1162,7 @@ namespace System.Text.RegularExpressions if (i > 0) TrackPush(i - 1, pos + Bump()); - Advance(2); + advance = 2; continue; } diff --git a/external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexMatchTimeoutException.cs b/external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexMatchTimeoutException.cs index 107282d88e..ae2c1fc095 100644 --- a/external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexMatchTimeoutException.cs +++ b/external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexMatchTimeoutException.cs @@ -10,6 +10,9 @@ namespace System.Text.RegularExpressions /// This is the exception that is thrown when a RegEx matching timeout occurs. ///
[Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class RegexMatchTimeoutException : TimeoutException, ISerializable { /// @@ -53,12 +56,17 @@ namespace System.Text.RegularExpressions protected RegexMatchTimeoutException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); + Input = info.GetString("regexInput"); + Pattern = info.GetString("regexPattern"); + MatchTimeout = new TimeSpan(info.GetInt64("timeoutTicks")); } void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + info.AddValue("regexInput", Input); + info.AddValue("regexPattern", Pattern); + info.AddValue("timeoutTicks", MatchTimeout.Ticks); } public string Input { get; } = string.Empty; diff --git a/external/corefx/src/System.Threading.AccessControl/src/System/Threading/ThreadingAclExtensions.cs b/external/corefx/src/System.Threading.AccessControl/src/System/Threading/ThreadingAclExtensions.cs index e7946daaac..fd80232cd9 100644 --- a/external/corefx/src/System.Threading.AccessControl/src/System/Threading/ThreadingAclExtensions.cs +++ b/external/corefx/src/System.Threading.AccessControl/src/System/Threading/ThreadingAclExtensions.cs @@ -7,7 +7,6 @@ using System; using System.Security.AccessControl; -using System.Diagnostics.Contracts; namespace System.Threading { @@ -22,7 +21,6 @@ namespace System.Threading { if (eventSecurity == null) throw new ArgumentNullException(nameof(eventSecurity)); - Contract.EndContractBlock(); eventSecurity.Persist(handle.GetSafeWaitHandle()); } @@ -36,7 +34,6 @@ namespace System.Threading { if (mutexSecurity == null) throw new ArgumentNullException(nameof(mutexSecurity)); - Contract.EndContractBlock(); mutexSecurity.Persist(mutex.GetSafeWaitHandle()); } diff --git a/external/corefx/src/System.Threading.Channels/System.Threading.Channels.sln b/external/corefx/src/System.Threading.Channels/System.Threading.Channels.sln new file mode 100644 index 0000000000..0baa0b1633 --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/System.Threading.Channels.sln @@ -0,0 +1,60 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Threading.Channels.Tests", "tests\System.Threading.Channels.Tests.csproj", "{9E984EB2-827E-4029-9647-FB5F8B67C553}" + ProjectSection(ProjectDependencies) = postProject + {1032D5F6-5AE7-4002-A0E4-FEBEADFEA977} = {1032D5F6-5AE7-4002-A0E4-FEBEADFEA977} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Threading.Channels.Performance.Tests", "tests\Performance\System.Threading.Channels.Performance.Tests.csproj", "{11ABE2F8-4FB9-48AC-91AA-D04503059550}" + ProjectSection(ProjectDependencies) = postProject + {1032D5F6-5AE7-4002-A0E4-FEBEADFEA977} = {1032D5F6-5AE7-4002-A0E4-FEBEADFEA977} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Threading.Channels", "src\System.Threading.Channels.csproj", "{1032D5F6-5AE7-4002-A0E4-FEBEADFEA977}" + ProjectSection(ProjectDependencies) = postProject + {9C524CA0-92FF-437B-B568-BCE8A794A69A} = {9C524CA0-92FF-437B-B568-BCE8A794A69A} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Threading.Channels", "ref\System.Threading.Channels.csproj", "{9C524CA0-92FF-437B-B568-BCE8A794A69A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{1A2F9F4A-A032-433E-B914-ADD5992BB178}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E107E9C1-E893-4E87-987E-04EF0DCEAEFD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{2E666815-2EDB-464B-9DF6-380BF4789AD4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9E984EB2-827E-4029-9647-FB5F8B67C553}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {9E984EB2-827E-4029-9647-FB5F8B67C553}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {9E984EB2-827E-4029-9647-FB5F8B67C553}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {9E984EB2-827E-4029-9647-FB5F8B67C553}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {11ABE2F8-4FB9-48AC-91AA-D04503059550}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {11ABE2F8-4FB9-48AC-91AA-D04503059550}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {11ABE2F8-4FB9-48AC-91AA-D04503059550}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {11ABE2F8-4FB9-48AC-91AA-D04503059550}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU + {1032D5F6-5AE7-4002-A0E4-FEBEADFEA977}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {1032D5F6-5AE7-4002-A0E4-FEBEADFEA977}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {1032D5F6-5AE7-4002-A0E4-FEBEADFEA977}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {1032D5F6-5AE7-4002-A0E4-FEBEADFEA977}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {9C524CA0-92FF-437B-B568-BCE8A794A69A}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {9C524CA0-92FF-437B-B568-BCE8A794A69A}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {9C524CA0-92FF-437B-B568-BCE8A794A69A}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {9C524CA0-92FF-437B-B568-BCE8A794A69A}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {9E984EB2-827E-4029-9647-FB5F8B67C553} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} + {11ABE2F8-4FB9-48AC-91AA-D04503059550} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} + {1032D5F6-5AE7-4002-A0E4-FEBEADFEA977} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD} + {9C524CA0-92FF-437B-B568-BCE8A794A69A} = {2E666815-2EDB-464B-9DF6-380BF4789AD4} + EndGlobalSection +EndGlobal diff --git a/external/corefx/src/System.Threading.Channels/dir.props b/external/corefx/src/System.Threading.Channels/dir.props new file mode 100644 index 0000000000..4356decc45 --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/dir.props @@ -0,0 +1,8 @@ + + + + + 4.0.0.0 + Open + + \ No newline at end of file diff --git a/external/corefx/src/System.Threading.Channels/pkg/System.Threading.Channels.pkgproj b/external/corefx/src/System.Threading.Channels/pkg/System.Threading.Channels.pkgproj new file mode 100644 index 0000000000..0ee61eec68 --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/pkg/System.Threading.Channels.pkgproj @@ -0,0 +1,10 @@ + + + + + + net46;netcore50;netcoreapp1.0;$(UAPvNextTFM);$(AllXamarinFrameworks) + + + + diff --git a/external/corefx/src/System.Threading.Channels/ref/Configurations.props b/external/corefx/src/System.Threading.Channels/ref/Configurations.props new file mode 100644 index 0000000000..5f3b2623ed --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/ref/Configurations.props @@ -0,0 +1,9 @@ + + + + + netstandard1.3; + netstandard; + + + diff --git a/external/corefx/src/System.Threading.Channels/ref/System.Threading.Channels.cs b/external/corefx/src/System.Threading.Channels/ref/System.Threading.Channels.cs new file mode 100644 index 0000000000..7ac0268176 --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/ref/System.Threading.Channels.cs @@ -0,0 +1,83 @@ +// 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. +// ------------------------------------------------------------------------------ +// Changes to this file must follow the http://aka.ms/api-review process. +// ------------------------------------------------------------------------------ + +namespace System.Threading.Channels +{ + public enum BoundedChannelFullMode + { + DropNewest = 1, + DropOldest = 2, + DropWrite = 3, + Wait = 0, + } + public sealed partial class BoundedChannelOptions : System.Threading.Channels.ChannelOptions + { + public BoundedChannelOptions(int capacity) { } + public int Capacity { get { throw null; } set { } } + public System.Threading.Channels.BoundedChannelFullMode FullMode { get { throw null; } set { } } + } + public static partial class Channel + { + public static System.Threading.Channels.Channel CreateBounded(int capacity) { throw null; } + public static System.Threading.Channels.Channel CreateBounded(System.Threading.Channels.BoundedChannelOptions options) { throw null; } + public static System.Threading.Channels.Channel CreateUnbounded() { throw null; } + public static System.Threading.Channels.Channel CreateUnbounded(System.Threading.Channels.UnboundedChannelOptions options) { throw null; } + public static System.Threading.Channels.Channel CreateUnbuffered() { throw null; } + public static System.Threading.Channels.Channel CreateUnbuffered(System.Threading.Channels.UnbufferedChannelOptions options) { throw null; } + } + public partial class ChannelClosedException : System.InvalidOperationException + { + public ChannelClosedException() { } + public ChannelClosedException(System.Exception innerException) { } + public ChannelClosedException(string message) { } + public ChannelClosedException(string message, System.Exception innerException) { } + } + public abstract partial class ChannelOptions + { + protected ChannelOptions() { } + public bool AllowSynchronousContinuations { get { throw null; } set { } } + public bool SingleReader { get { throw null; } set { } } + public bool SingleWriter { get { throw null; } set { } } + } + public abstract partial class ChannelReader + { + protected ChannelReader() { } + public virtual System.Threading.Tasks.Task Completion { get { throw null; } } + public virtual System.Threading.Tasks.ValueTask ReadAsync(CancellationToken cancellationToken = default) { throw null; } + public abstract bool TryRead(out T item); + public abstract System.Threading.Tasks.Task WaitToReadAsync(System.Threading.CancellationToken cancellationToken=default); + } + public abstract partial class ChannelWriter + { + protected ChannelWriter() { } + public void Complete(System.Exception error=null) { } + public virtual bool TryComplete(System.Exception error=null) { throw null; } + public abstract bool TryWrite(T item); + public abstract System.Threading.Tasks.Task WaitToWriteAsync(System.Threading.CancellationToken cancellationToken=default); + public virtual System.Threading.Tasks.Task WriteAsync(T item, System.Threading.CancellationToken cancellationToken=default) { throw null; } + } + public abstract partial class Channel : System.Threading.Channels.Channel + { + protected Channel() { } + } + public abstract partial class Channel + { + protected Channel() { } + public System.Threading.Channels.ChannelReader Reader { get { throw null; } protected set { } } + public System.Threading.Channels.ChannelWriter Writer { get { throw null; } protected set { } } + public static implicit operator System.Threading.Channels.ChannelReader (System.Threading.Channels.Channel channel) { throw null; } + public static implicit operator System.Threading.Channels.ChannelWriter (System.Threading.Channels.Channel channel) { throw null; } + } + public sealed partial class UnboundedChannelOptions : System.Threading.Channels.ChannelOptions + { + public UnboundedChannelOptions() { } + } + public sealed partial class UnbufferedChannelOptions : System.Threading.Channels.ChannelOptions + { + public UnbufferedChannelOptions() { } + } +} diff --git a/external/corefx/src/System.Threading.Channels/ref/System.Threading.Channels.csproj b/external/corefx/src/System.Threading.Channels/ref/System.Threading.Channels.csproj new file mode 100644 index 0000000000..6f9782d4ff --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/ref/System.Threading.Channels.csproj @@ -0,0 +1,23 @@ + + + + + {9C524CA0-92FF-437B-B568-BCE8A794A69A} + + + + + + + + + + + + + + + + + + diff --git a/external/corefx/src/System.Threading.Channels/src/Configurations.props b/external/corefx/src/System.Threading.Channels/src/Configurations.props new file mode 100644 index 0000000000..5f3b2623ed --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/src/Configurations.props @@ -0,0 +1,9 @@ + + + + + netstandard1.3; + netstandard; + + + diff --git a/external/corefx/src/System.Threading.Channels/src/Resources/Strings.resx b/external/corefx/src/System.Threading.Channels/src/Resources/Strings.resx new file mode 100644 index 0000000000..2beea8a357 --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/src/Resources/Strings.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The channel has been closed. + + \ No newline at end of file diff --git a/external/corefx/src/System.Threading.Channels/src/System.Threading.Channels.csproj b/external/corefx/src/System.Threading.Channels/src/System.Threading.Channels.csproj new file mode 100644 index 0000000000..9dc268ec36 --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/src/System.Threading.Channels.csproj @@ -0,0 +1,45 @@ + + + + + {1032D5F6-5AE7-4002-A0E4-FEBEADFEA977} + System.Threading.Channels + $(OutputPath)$(MSBuildProjectName).xml + + + + + + + + + + + + + + + + + + + + + + + Common\System\Collections\Concurrent\SingleProducerConsumerQueue.cs + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Threading.Channels/src/System/Collections/Generic/Dequeue.cs b/external/corefx/src/System.Threading.Channels/src/System/Collections/Generic/Dequeue.cs new file mode 100644 index 0000000000..6c44b73043 --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/src/System/Collections/Generic/Dequeue.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. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; + +namespace System.Collections.Generic +{ + /// Provides a double-ended queue data structure. + /// Type of the data stored in the dequeue. + [DebuggerDisplay("Count = {_size}")] + internal sealed class Dequeue + { + private T[] _array = Array.Empty(); + private int _head; // First valid element in the queue + private int _tail; // First open slot in the dequeue, unless the dequeue is full + private int _size; // Number of elements. + + public int Count => _size; + + public bool IsEmpty => _size == 0; + + public void EnqueueTail(T item) + { + if (_size == _array.Length) + { + Grow(); + } + + _array[_tail] = item; + if (++_tail == _array.Length) + { + _tail = 0; + } + _size++; + } + + //// Uncomment if/when enqueueing at the head is needed + //public void EnqueueHead(T item) + //{ + // if (_size == _array.Length) + // { + // Grow(); + // } + // + // _head = (_head == 0 ? _array.Length : _head) - 1; + // _array[_head] = item; + // _size++; + //} + + public T DequeueHead() + { + Debug.Assert(!IsEmpty); // caller's responsibility to make sure there are elements remaining + + T item = _array[_head]; + _array[_head] = default; + + if (++_head == _array.Length) + { + _head = 0; + } + _size--; + + return item; + } + + public T DequeueTail() + { + Debug.Assert(!IsEmpty); // caller's responsibility to make sure there are elements remaining + + if (--_tail == -1) + { + _tail = _array.Length - 1; + } + + T item = _array[_tail]; + _array[_tail] = default; + + _size--; + return item; + } + + public IEnumerator GetEnumerator() // meant for debug purposes only + { + int pos = _head; + int count = _size; + while (count-- > 0) + { + yield return _array[pos]; + pos = (pos + 1) % _array.Length; + } + } + + private void Grow() + { + Debug.Assert(_size == _array.Length); + Debug.Assert(_head == _tail); + + const int MinimumGrow = 4; + + int capacity = (int)(_array.Length * 2L); + if (capacity < _array.Length + MinimumGrow) + { + capacity = _array.Length + MinimumGrow; + } + + T[] newArray = new T[capacity]; + + if (_head == 0) + { + Array.Copy(_array, 0, newArray, 0, _size); + } + else + { + Array.Copy(_array, _head, newArray, 0, _array.Length - _head); + Array.Copy(_array, 0, newArray, _array.Length - _head, _tail); + } + + _array = newArray; + _head = 0; + _tail = _size; + } + } +} diff --git a/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/BoundedChannel.cs b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/BoundedChannel.cs new file mode 100644 index 0000000000..23047aba28 --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/BoundedChannel.cs @@ -0,0 +1,411 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Threading.Tasks; + +namespace System.Threading.Channels +{ + /// Provides a channel with a bounded capacity. + [DebuggerDisplay("Items={ItemsCountForDebugger}, Capacity={_bufferedCapacity}")] + [DebuggerTypeProxy(typeof(DebugEnumeratorDebugView<>))] + internal sealed class BoundedChannel : Channel, IDebugEnumerable + { + /// The mode used when the channel hits its bound. + private readonly BoundedChannelFullMode _mode; + /// Task signaled when the channel has completed. + private readonly TaskCompletionSource _completion; + /// The maximum capacity of the channel. + private readonly int _bufferedCapacity; + /// Items currently stored in the channel waiting to be read. + private readonly Dequeue _items = new Dequeue(); + /// Writers waiting to write to the channel. + private readonly Dequeue> _blockedWriters = new Dequeue>(); + /// Task signaled when any WaitToReadAsync waiters should be woken up. + private ReaderInteractor _waitingReaders; + /// Task signaled when any WaitToWriteAsync waiters should be woken up. + private ReaderInteractor _waitingWriters; + /// Whether to force continuations to be executed asynchronously from producer writes. + private readonly bool _runContinuationsAsynchronously; + /// Set to non-null once Complete has been called. + private Exception _doneWriting; + /// Gets an object used to synchronize all state on the instance. + private object SyncObj => _items; + + /// Initializes the . + /// The positive bounded capacity for the channel. + /// The mode used when writing to a full channel. + /// Whether to force continuations to be executed asynchronously. + internal BoundedChannel(int bufferedCapacity, BoundedChannelFullMode mode, bool runContinuationsAsynchronously) + { + Debug.Assert(bufferedCapacity > 0); + _bufferedCapacity = bufferedCapacity; + _mode = mode; + _runContinuationsAsynchronously = runContinuationsAsynchronously; + _completion = new TaskCompletionSource(runContinuationsAsynchronously ? TaskCreationOptions.RunContinuationsAsynchronously : TaskCreationOptions.None); + Reader = new BoundedChannelReader(this); + Writer = new BoundedChannelWriter(this); + } + + private sealed class BoundedChannelReader : ChannelReader + { + internal readonly BoundedChannel _parent; + internal BoundedChannelReader(BoundedChannel parent) => _parent = parent; + + public override Task Completion => _parent._completion.Task; + + public override bool TryRead(out T item) + { + BoundedChannel parent = _parent; + lock (parent.SyncObj) + { + parent.AssertInvariants(); + + // Get an item if there is one. + if (!parent._items.IsEmpty) + { + item = DequeueItemAndPostProcess(); + return true; + } + } + + item = default; + return false; + } + + public override Task WaitToReadAsync(CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + BoundedChannel parent = _parent; + lock (parent.SyncObj) + { + parent.AssertInvariants(); + + // If there are any items available, a read is possible. + if (!parent._items.IsEmpty) + { + return ChannelUtilities.s_trueTask; + } + + // There were no items available, so if we're done writing, a read will never be possible. + if (parent._doneWriting != null) + { + return parent._doneWriting != ChannelUtilities.s_doneWritingSentinel ? + Task.FromException(parent._doneWriting) : + ChannelUtilities.s_falseTask; + } + + // There were no items available, but there could be in the future, so ensure + // there's a blocked reader task and return it. + return ChannelUtilities.GetOrCreateWaiter(ref parent._waitingReaders, parent._runContinuationsAsynchronously, cancellationToken); + } + } + + /// Dequeues an item, and then fixes up our state around writers and completion. + /// The dequeued item. + private T DequeueItemAndPostProcess() + { + BoundedChannel parent = _parent; + Debug.Assert(Monitor.IsEntered(parent.SyncObj)); + + // Dequeue an item. + T item = parent._items.DequeueHead(); + + // If we're now empty and we're done writing, complete the channel. + if (parent._doneWriting != null && parent._items.IsEmpty) + { + ChannelUtilities.Complete(parent._completion, parent._doneWriting); + } + + // If there are any writers blocked, there's now room for at least one + // to be promoted to have its item moved into the items queue. We need + // to loop while trying to complete the writer in order to find one that + // hasn't yet been canceled (canceled writers transition to canceled but + // remain in the physical queue). + while (!parent._blockedWriters.IsEmpty) + { + WriterInteractor w = parent._blockedWriters.DequeueHead(); + if (w.Success(default)) + { + parent._items.EnqueueTail(w.Item); + return item; + } + } + + // There was no blocked writer, so see if there's a WaitToWriteAsync + // we should wake up. + ChannelUtilities.WakeUpWaiters(ref parent._waitingWriters, result: true); + + // Return the item + return item; + } + } + + private sealed class BoundedChannelWriter : ChannelWriter + { + internal readonly BoundedChannel _parent; + internal BoundedChannelWriter(BoundedChannel parent) => _parent = parent; + + public override bool TryComplete(Exception error) + { + BoundedChannel parent = _parent; + bool completeTask; + lock (parent.SyncObj) + { + parent.AssertInvariants(); + + // If we've already marked the channel as completed, bail. + if (parent._doneWriting != null) + { + return false; + } + + // Mark that we're done writing. + parent._doneWriting = error ?? ChannelUtilities.s_doneWritingSentinel; + completeTask = parent._items.IsEmpty; + } + + // If there are no items in the queue, complete the channel's task, + // as no more data can possibly arrive at this point. We do this outside + // of the lock in case we'll be running synchronous completions, and we + // do it before completing blocked/waiting readers, so that when they + // wake up they'll see the task as being completed. + if (completeTask) + { + ChannelUtilities.Complete(parent._completion, error); + } + + // At this point, _blockedWriters and _waitingReaders/Writers will not be mutated: + // they're only mutated by readers/writers while holding the lock, and only if _doneWriting is null. + // We also know that only one thread (this one) will ever get here, as only that thread + // will be the one to transition from _doneWriting false to true. As such, we can + // freely manipulate them without any concurrency concerns. + ChannelUtilities.FailInteractors, VoidResult>(parent._blockedWriters, ChannelUtilities.CreateInvalidCompletionException(error)); + ChannelUtilities.WakeUpWaiters(ref parent._waitingReaders, result: false, error: error); + ChannelUtilities.WakeUpWaiters(ref parent._waitingWriters, result: false, error: error); + + // Successfully transitioned to completed. + return true; + } + + public override bool TryWrite(T item) + { + ReaderInteractor waitingReaders = null; + + BoundedChannel parent = _parent; + lock (parent.SyncObj) + { + parent.AssertInvariants(); + + // If we're done writing, nothing more to do. + if (parent._doneWriting != null) + { + return false; + } + + // Get the number of items in the channel currently. + int count = parent._items.Count; + + if (count == 0) + { + // There are no items in the channel, which means we may have waiting readers. + // Store the item. + parent._items.EnqueueTail(item); + waitingReaders = parent._waitingReaders; + if (waitingReaders == null) + { + // If no one's waiting to be notified about a 0-to-1 transition, we're done. + return true; + } + parent._waitingReaders = null; + } + else if (count < parent._bufferedCapacity) + { + // There's room in the channel. Since we're not transitioning from 0-to-1 and + // since there's room, we can simply store the item and exit without having to + // worry about blocked/waiting readers. + parent._items.EnqueueTail(item); + return true; + } + else if (parent._mode == BoundedChannelFullMode.Wait) + { + // The channel is full and we're in a wait mode. + // Simply exit and let the caller know we didn't write the data. + return false; + } + else if (parent._mode == BoundedChannelFullMode.DropWrite) + { + // The channel is full. Just ignore the item being added + // but say we added it. + return true; + } + else + { + // The channel is full, and we're in a dropping mode. + // Drop either the oldest or the newest and write the new item. + T droppedItem = parent._mode == BoundedChannelFullMode.DropNewest ? + parent._items.DequeueTail() : + parent._items.DequeueHead(); + parent._items.EnqueueTail(item); + return true; + } + } + + // We stored an item bringing the count up from 0 to 1. Alert + // any waiting readers that there may be something for them to consume. + // Since we're no longer holding the lock, it's possible we'll end up + // waking readers that have since come in. + waitingReaders.Success(item: true); + return true; + } + + public override Task WaitToWriteAsync(CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + BoundedChannel parent = _parent; + lock (parent.SyncObj) + { + parent.AssertInvariants(); + + // If we're done writing, no writes will ever succeed. + if (parent._doneWriting != null) + { + return parent._doneWriting != ChannelUtilities.s_doneWritingSentinel ? + Task.FromException(parent._doneWriting) : + ChannelUtilities.s_falseTask; + } + + // If there's space to write, a write is possible. + // And if the mode involves dropping/ignoring, we can always write, as even if it's + // full we'll just drop an element to make room. + if (parent._items.Count < parent._bufferedCapacity || parent._mode != BoundedChannelFullMode.Wait) + { + return ChannelUtilities.s_trueTask; + } + + // We're still allowed to write, but there's no space, so ensure a waiter is queued and return it. + return ChannelUtilities.GetOrCreateWaiter(ref parent._waitingWriters, runContinuationsAsynchronously: true, cancellationToken); + } + } + + public override Task WriteAsync(T item, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + ReaderInteractor waitingReaders = null; + + BoundedChannel parent = _parent; + lock (parent.SyncObj) + { + parent.AssertInvariants(); + + // If we're done writing, trying to write is an error. + if (parent._doneWriting != null) + { + return Task.FromException(ChannelUtilities.CreateInvalidCompletionException(parent._doneWriting)); + } + + // Get the number of items in the channel currently. + int count = parent._items.Count; + + if (count == 0) + { + // There are no items in the channel, which means we may have waiting readers. + // Store the item. + parent._items.EnqueueTail(item); + waitingReaders = parent._waitingReaders; + if (waitingReaders == null) + { + // If no one's waiting to be notified about a 0-to-1 transition, we're done. + return ChannelUtilities.s_trueTask; + } + parent._waitingReaders = null; + } + else if (count < parent._bufferedCapacity) + { + // There's room in the channel. Since we're not transitioning from 0-to-1 and + // since there's room, we can simply store the item and exit without having to + // worry about blocked/waiting readers. + parent._items.EnqueueTail(item); + return ChannelUtilities.s_trueTask; + } + else if (parent._mode == BoundedChannelFullMode.Wait) + { + // The channel is full and we're in a wait mode. + // Queue the writer. + var writer = WriterInteractor.Create(runContinuationsAsynchronously: true, item, cancellationToken); + parent._blockedWriters.EnqueueTail(writer); + return writer.Task; + } + else if (parent._mode == BoundedChannelFullMode.DropWrite) + { + // The channel is full and we're in ignore mode. + // Ignore the item but say we accepted it. + return ChannelUtilities.s_trueTask; + } + else + { + // The channel is full, and we're in a dropping mode. + // Drop either the oldest or the newest and write the new item. + T droppedItem = parent._mode == BoundedChannelFullMode.DropNewest ? + parent._items.DequeueTail() : + parent._items.DequeueHead(); + parent._items.EnqueueTail(item); + return ChannelUtilities.s_trueTask; + } + } + + // We stored an item bringing the count up from 0 to 1. Alert + // any waiting readers that there may be something for them to consume. + // Since we're no longer holding the lock, it's possible we'll end up + // waking readers that have since come in. + waitingReaders.Success(item: true); + return ChannelUtilities.s_trueTask; + } + } + + [Conditional("DEBUG")] + private void AssertInvariants() + { + Debug.Assert(SyncObj != null, "The sync obj must not be null."); + Debug.Assert(Monitor.IsEntered(SyncObj), "Invariants can only be validated while holding the lock."); + + if (!_items.IsEmpty) + { + Debug.Assert(_waitingReaders == null, "There are items available, so there shouldn't be any waiting readers."); + } + if (_items.Count < _bufferedCapacity) + { + Debug.Assert(_blockedWriters.IsEmpty, "There's space available, so there shouldn't be any blocked writers."); + Debug.Assert(_waitingWriters == null, "There's space available, so there shouldn't be any waiting writers."); + } + if (!_blockedWriters.IsEmpty) + { + Debug.Assert(_items.Count == _bufferedCapacity, "We should have a full buffer if there's a blocked writer."); + } + if (_completion.Task.IsCompleted) + { + Debug.Assert(_doneWriting != null, "We can only complete if we're done writing."); + } + } + + /// Gets the number of items in the channel. This should only be used by the debugger. + private int ItemsCountForDebugger => _items.Count; + + /// Gets an enumerator the debugger can use to show the contents of the channel. + IEnumerator IDebugEnumerable.GetEnumerator() => _items.GetEnumerator(); + } +} diff --git a/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/BoundedChannelFullMode.cs b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/BoundedChannelFullMode.cs new file mode 100644 index 0000000000..385256541e --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/BoundedChannelFullMode.cs @@ -0,0 +1,19 @@ +// 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. + +namespace System.Threading.Channels +{ + /// Specifies the behavior to use when writing to a bounded channel that is already full. + public enum BoundedChannelFullMode + { + /// Wait for space to be available in order to complete the write operation. + Wait, + /// Remove and ignore the newest item in the channel in order to make room for the item being written. + DropNewest, + /// Remove and ignore the oldest item in the channel in order to make room for the item being written. + DropOldest, + /// Drop the item being written. + DropWrite + } +} diff --git a/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/Channel.cs b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/Channel.cs new file mode 100644 index 0000000000..ed1b3f34ef --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/Channel.cs @@ -0,0 +1,76 @@ +// 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. + +namespace System.Threading.Channels +{ + /// Provides static methods for creating channels. + public static class Channel + { + /// Creates an unbounded channel usable by any number of readers and writers concurrently. + /// The created channel. + public static Channel CreateUnbounded() => + new UnboundedChannel(runContinuationsAsynchronously: true); + + /// Creates an unbounded channel subject to the provided options. + /// Specifies the type of data in the channel. + /// Options that guide the behavior of the channel. + /// The created channel. + public static Channel CreateUnbounded(UnboundedChannelOptions options) => + options == null ? throw new ArgumentNullException(nameof(options)) : + options.SingleReader ? new SingleConsumerUnboundedChannel(!options.AllowSynchronousContinuations) : + (Channel)new UnboundedChannel(!options.AllowSynchronousContinuations); + + /// Creates a channel with the specified maximum capacity. + /// Specifies the type of data in the channel. + /// The maximum number of items the channel may store. + /// The created channel. + /// + /// Channels created with this method apply the + /// behavior and prohibit continuations from running synchronously. + /// + public static Channel CreateBounded(int capacity) + { + if (capacity < 1) + { + throw new ArgumentOutOfRangeException(nameof(capacity)); + } + + return new BoundedChannel(capacity, BoundedChannelFullMode.Wait, runContinuationsAsynchronously: true); + } + + /// Creates a channel with the specified maximum capacity. + /// Specifies the type of data in the channel. + /// Options that guide the behavior of the channel. + /// The created channel. + public static Channel CreateBounded(BoundedChannelOptions options) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + return new BoundedChannel(options.Capacity, options.FullMode, !options.AllowSynchronousContinuations); + } + + /// Creates a channel that doesn't buffer any items. + /// Specifies the type of data in the channel. + /// The created channel. + public static Channel CreateUnbuffered() => + new UnbufferedChannel(); + + /// Creates a channel that doesn't buffer any items. + /// Specifies the type of data in the channel. + /// Options that guide the behavior of the channel. + /// The created channel. + public static Channel CreateUnbuffered(UnbufferedChannelOptions options) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + return new UnbufferedChannel(); + } + } +} diff --git a/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/ChannelClosedException.cs b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/ChannelClosedException.cs new file mode 100644 index 0000000000..fe9efc615e --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/ChannelClosedException.cs @@ -0,0 +1,28 @@ +// 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. + +namespace System.Threading.Channels +{ + /// Exception thrown when a channel is used after it's been closed. + public class ChannelClosedException : InvalidOperationException + { + /// Initializes a new instance of the class. + public ChannelClosedException() : + base(SR.ChannelClosedException_DefaultMessage) { } + + /// Initializes a new instance of the class. + /// The message that describes the error. + public ChannelClosedException(string message) : base(message) { } + + /// Initializes a new instance of the class. + /// The exception that is the cause of this exception. + public ChannelClosedException(Exception innerException) : + base(SR.ChannelClosedException_DefaultMessage, innerException) { } + + /// Initializes a new instance of the class. + /// The message that describes the error. + /// The exception that is the cause of this exception. + public ChannelClosedException(string message, Exception innerException) : base(message, innerException) { } + } +} diff --git a/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/ChannelOptions.cs b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/ChannelOptions.cs new file mode 100644 index 0000000000..9172889de8 --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/ChannelOptions.cs @@ -0,0 +1,107 @@ +// 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. + +namespace System.Threading.Channels +{ + /// Provides options that control the behavior of channel instances. + public abstract class ChannelOptions + { + /// + /// true if writers to the channel guarantee that there will only ever be at most one write operation + /// at a time; false if no such constraint is guaranteed. + /// + /// + /// If true, the channel may be able to optimize certain operations based on knowing about the single-writer guarantee. + /// The default is false. + /// + public bool SingleWriter { get; set; } + + /// + /// true readers from the channel guarantee that there will only ever be at most one read operation at a time; + /// false if no such constraint is guaranteed. + /// + /// + /// If true, the channel may be able to optimize certain operations based on knowing about the single-reader guarantee. + /// The default is false. + /// + public bool SingleReader { get; set; } + + /// + /// true if operations performed on a channel may synchronously invoke continuations subscribed to + /// notifications of pending async operations; false if all continuations should be invoked asynchronously. + /// + /// + /// Setting this option to true can provide measurable throughput improvements by avoiding + /// scheduling additional work items. However, it may come at the cost of reduced parallelism, as for example a producer + /// may then be the one to execute work associated with a consumer, and if not done thoughtfully, this can lead + /// to unexpected interactions. The default is false. + /// + public bool AllowSynchronousContinuations { get; set; } + } + + /// Provides options that control the behavior of instances. + public sealed class BoundedChannelOptions : ChannelOptions + { + /// The maximum number of items the bounded channel may store. + private int _capacity; + /// The behavior incurred by write operations when the channel is full. + private BoundedChannelFullMode _mode = BoundedChannelFullMode.Wait; + + /// Initializes the options. + /// The maximum number of items the bounded channel may store. + public BoundedChannelOptions(int capacity) + { + if (capacity < 1) + { + throw new ArgumentOutOfRangeException(nameof(capacity)); + } + + Capacity = capacity; + } + + /// Gets or sets the maximum number of items the bounded channel may store. + public int Capacity + { + get => _capacity; + set + { + if (value < 1) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + _capacity = value; + } + } + + /// Gets or sets the behavior incurred by write operations when the channel is full. + public BoundedChannelFullMode FullMode + { + get => _mode; + set + { + switch (value) + { + case BoundedChannelFullMode.Wait: + case BoundedChannelFullMode.DropNewest: + case BoundedChannelFullMode.DropOldest: + case BoundedChannelFullMode.DropWrite: + _mode = value; + break; + default: + throw new ArgumentOutOfRangeException(nameof(value)); + } + } + } + } + + /// Provides options that control the behavior of instances. + public sealed class UnboundedChannelOptions : ChannelOptions + { + } + + /// Provides options that control the behavior of instances. + public sealed class UnbufferedChannelOptions : ChannelOptions + { + } +} diff --git a/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/ChannelReader.cs b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/ChannelReader.cs new file mode 100644 index 0000000000..8b2469ed1d --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/ChannelReader.cs @@ -0,0 +1,82 @@ +// 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.Threading.Tasks; + +namespace System.Threading.Channels +{ + /// + /// Provides a base class for reading from a channel. + /// + /// Specifies the type of data that may be read from the channel. + public abstract class ChannelReader + { + /// + /// Gets a that completes when no more data will ever + /// be available to be read from this channel. + /// + public virtual Task Completion => ChannelUtilities.s_neverCompletingTask; + + /// Attempts to read an item to the channel. + /// The read item, or a default value if no item could be read. + /// true if an item was read; otherwise, false if no item was read. + public abstract bool TryRead(out T item); + + /// Returns a that will complete when data is available to read. + /// A used to cancel the wait operation. + /// + /// A that will complete with a true result when data is available to read + /// or with a false result when no further data will ever be available to be read. + /// + public abstract Task WaitToReadAsync(CancellationToken cancellationToken = default); + + /// Asynchronously reads an item from the channel. + /// A used to cancel the read operation. + /// A that represents the asynchronous read operation. + public virtual ValueTask ReadAsync(CancellationToken cancellationToken = default) + { + if (cancellationToken.IsCancellationRequested) + { + return new ValueTask(Task.FromCanceled(cancellationToken)); + } + + try + { + if (TryRead(out T fastItem)) + { + return new ValueTask(fastItem); + } + } + catch (Exception exc) when (!(exc is ChannelClosedException || exc is OperationCanceledException)) + { + return new ValueTask(Task.FromException(exc)); + } + + return ReadAsyncCore(cancellationToken); + + async ValueTask ReadAsyncCore(CancellationToken ct) + { + try + { + while (true) + { + if (!await WaitToReadAsync(ct)) + { + throw new ChannelClosedException(); + } + + if (TryRead(out T item)) + { + return item; + } + } + } + catch (Exception exc) when (!(exc is ChannelClosedException || exc is OperationCanceledException)) + { + throw new ChannelClosedException(exc); + } + } + } + } +} diff --git a/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/ChannelUtilities.cs b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/ChannelUtilities.cs new file mode 100644 index 0000000000..17d412f04b --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/ChannelUtilities.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. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading.Tasks; + +namespace System.Threading.Channels +{ + /// Provides internal helper methods for implementing channels. + internal static class ChannelUtilities + { + /// Sentinel object used to indicate being done writing. + internal static readonly Exception s_doneWritingSentinel = new Exception(nameof(s_doneWritingSentinel)); + /// A cached task with a Boolean true result. + internal static readonly Task s_trueTask = Task.FromResult(result: true); + /// A cached task with a Boolean false result. + internal static readonly Task s_falseTask = Task.FromResult(result: false); + /// A cached task that never completes. + internal static readonly Task s_neverCompletingTask = new TaskCompletionSource().Task; + + /// Completes the specified TaskCompletionSource. + /// The source to complete. + /// + /// The optional exception with which to complete. + /// If this is null or the DoneWritingSentinel, the source will be completed successfully. + /// If this is an OperationCanceledException, it'll be completed with the exception's token. + /// Otherwise, it'll be completed as faulted with the exception. + /// + internal static void Complete(TaskCompletionSource tcs, Exception error = null) + { + if (error is OperationCanceledException oce) + { + tcs.TrySetCanceled(oce.CancellationToken); + } + else if (error != null && error != s_doneWritingSentinel) + { + tcs.TrySetException(error); + } + else + { + tcs.TrySetResult(default); + } + } + + /// Wake up all of the waiters and null out the field. + /// The waiters. + /// The value with which to complete each waiter. + internal static void WakeUpWaiters(ref ReaderInteractor waiters, bool result) + { + ReaderInteractor w = waiters; + if (w != null) + { + w.Success(result); + waiters = null; + } + } + + /// Wake up all of the waiters and null out the field. + /// The waiters. + /// The success value with which to complete each waiter if error is null. + /// The failure with which to cmplete each waiter, if non-null. + internal static void WakeUpWaiters(ref ReaderInteractor waiters, bool result, Exception error = null) + { + ReaderInteractor w = waiters; + if (w != null) + { + if (error != null) + { + w.Fail(error); + } + else + { + w.Success(result); + } + waiters = null; + } + } + + /// Removes all interactors from the queue, failing each. + /// The queue of interactors to complete. + /// The error with which to complete each interactor. + internal static void FailInteractors(Dequeue interactors, Exception error) where T : Interactor + { + Debug.Assert(error != null); + while (!interactors.IsEmpty) + { + interactors.DequeueHead().Fail(error); + } + } + + /// Gets or creates a "waiter" (e.g. WaitForRead/WriteAsync) interactor. + /// The field storing the waiter interactor. + /// true to force continuations to run asynchronously; otherwise, false. + /// The token to use to cancel the wait. + internal static Task GetOrCreateWaiter(ref ReaderInteractor waiter, bool runContinuationsAsynchronously, CancellationToken cancellationToken) + { + // Get the existing waiters interactor. + ReaderInteractor w = waiter; + + // If there isn't one, create one. This explicitly does not include the cancellation token, + // as we reuse it for any number of waiters that overlap. + if (w == null) + { + waiter = w = ReaderInteractor.Create(runContinuationsAsynchronously); + } + + // If the cancellation token can't be canceled, then just return the waiter task. + // If it can, we need to return a task that will complete when the waiter task does but that can also be canceled. + // Easiest way to do that is with a cancelable continuation. + return cancellationToken.CanBeCanceled ? + w.Task.ContinueWith(t => t.Result, cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default) : + w.Task; + } + + /// Creates and returns an exception object to indicate that a channel has been closed. + internal static Exception CreateInvalidCompletionException(Exception inner = null) => + inner is OperationCanceledException ? inner : + inner != null && inner != s_doneWritingSentinel ? new ChannelClosedException(inner) : + new ChannelClosedException(); + } +} diff --git a/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/ChannelWriter.cs b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/ChannelWriter.cs new file mode 100644 index 0000000000..d09fa1b0d0 --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/ChannelWriter.cs @@ -0,0 +1,79 @@ +// 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.Threading.Tasks; + +namespace System.Threading.Channels +{ + /// + /// Provides a base class for writing to a channel. + /// + /// Specifies the type of data that may be written to the channel. + public abstract class ChannelWriter + { + /// Attempts to mark the channel as being completed, meaning no more data will be written to it. + /// An indicating the failure causing no more data to be written, or null for success. + /// + /// true if this operation successfully completes the channel; otherwise, false if the channel could not be marked for completion, + /// for example due to having already been marked as such, or due to not supporting completion. + /// + public virtual bool TryComplete(Exception error = null) => false; + + /// Attempts to write the specified item to the channel. + /// The item to write. + /// true if the item was written; otherwise, false if it wasn't written. + public abstract bool TryWrite(T item); + + /// Returns a that will complete when space is available to write an item. + /// A used to cancel the wait operation. + /// + /// A that will complete with a true result when space is available to write an item + /// or with a false result when no further writing will be permitted. + /// + public abstract Task WaitToWriteAsync(CancellationToken cancellationToken = default); + + /// Asynchronously writes an item to the channel. + /// The value to write to the channel. + /// A used to cancel the write operation. + /// A that represents the asynchronous write operation. + public virtual Task WriteAsync(T item, CancellationToken cancellationToken = default) + { + try + { + return + cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) : + TryWrite(item) ? Task.CompletedTask : + WriteAsyncCore(item, cancellationToken); + } + catch (Exception e) + { + return Task.FromException(e); + } + + async Task WriteAsyncCore(T innerItem, CancellationToken ct) + { + while (await WaitToWriteAsync(ct).ConfigureAwait(false)) + { + if (TryWrite(innerItem)) + { + return; + } + } + + throw ChannelUtilities.CreateInvalidCompletionException(); + } + } + + /// Mark the channel as being complete, meaning no more items will be written to it. + /// Optional Exception indicating a failure that's causing the channel to complete. + /// The channel has already been marked as complete. + public void Complete(Exception error = null) + { + if (!TryComplete(error)) + { + throw ChannelUtilities.CreateInvalidCompletionException(); + } + } + } +} diff --git a/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/Channel_1.cs b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/Channel_1.cs new file mode 100644 index 0000000000..c10dea341d --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/Channel_1.cs @@ -0,0 +1,10 @@ +// 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. + +namespace System.Threading.Channels +{ + /// Provides a base class for channels that support reading and writing elements of type . + /// Specifies the type of data readable and writable in the channel. + public abstract class Channel : Channel { } +} diff --git a/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/Channel_2.cs b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/Channel_2.cs new file mode 100644 index 0000000000..d8e2b28848 --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/Channel_2.cs @@ -0,0 +1,29 @@ +// 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. + +namespace System.Threading.Channels +{ + /// + /// Provides a base class for channels that support reading elements of type + /// and writing elements of type . + /// + /// Specifies the type of data that may be written to the channel. + /// Specifies the type of data that may be read from the channel. + public abstract class Channel + { + /// Gets the readable half of this channel. + public ChannelReader Reader { get; protected set; } + + /// Gets the writable half of this channel. + public ChannelWriter Writer { get; protected set; } + + /// Implicit cast from a channel to its readable half. + /// The channel being cast. + public static implicit operator ChannelReader(Channel channel) => channel.Reader; + + /// Implicit cast from a channel to its writable half. + /// The channel being cast. + public static implicit operator ChannelWriter(Channel channel) => channel.Writer; + } +} diff --git a/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/IDebugEnumerator.cs b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/IDebugEnumerator.cs new file mode 100644 index 0000000000..d83eb78a23 --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/IDebugEnumerator.cs @@ -0,0 +1,30 @@ +// 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.Collections.Generic; +using System.Diagnostics; + +namespace System.Threading.Channels +{ + interface IDebugEnumerable + { + IEnumerator GetEnumerator(); + } + + internal sealed class DebugEnumeratorDebugView + { + public DebugEnumeratorDebugView(IDebugEnumerable enumerable) + { + var list = new List(); + foreach (T item in enumerable) + { + list.Add(item); + } + Items = list.ToArray(); + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public T[] Items { get; } + } +} diff --git a/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/Interactor.cs b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/Interactor.cs new file mode 100644 index 0000000000..f4e0c74767 --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/Interactor.cs @@ -0,0 +1,149 @@ +// 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.Threading.Tasks; + +namespace System.Threading.Channels +{ + /// A base class for a blocked or waiting reader or writer. + /// Specifies the type of data passed to the reader or writer. + internal abstract class Interactor : TaskCompletionSource + { + /// Initializes the interactor. + /// true if continuations should be forced to run asynchronously; otherwise, false. + protected Interactor(bool runContinuationsAsynchronously) : + base(runContinuationsAsynchronously ? TaskCreationOptions.RunContinuationsAsynchronously : TaskCreationOptions.None) { } + + /// Completes the interactor with a success state and the specified result. + /// The result value. + /// true if the interactor could be successfully transitioned to a completed state; false if it was already completed. + internal bool Success(T item) + { + UnregisterCancellation(); + return TrySetResult(item); + } + + /// Completes the interactor with a failed state and the specified error. + /// The error. + /// true if the interactor could be successfully transitioned to a completed state; false if it was already completed. + internal bool Fail(Exception exception) + { + UnregisterCancellation(); + return TrySetException(exception); + } + + /// Unregister cancellation in case cancellation was registered. + internal virtual void UnregisterCancellation() { } + } + + /// A blocked or waiting reader. + /// Specifies the type of data being read. + internal class ReaderInteractor : Interactor + { + /// Initializes the reader. + /// true if continuations should be forced to run asynchronously; otherwise, false. + protected ReaderInteractor(bool runContinuationsAsynchronously) : base(runContinuationsAsynchronously) { } + + /// Creates a reader. + /// true if continuations should be forced to run asynchronously; otherwise, false. + /// The reader. + public static ReaderInteractor Create(bool runContinuationsAsynchronously) => + new ReaderInteractor(runContinuationsAsynchronously); + + /// Creates a reader. + /// true if continuations should be forced to run asynchronously; otherwise, false. + /// A that can be used to cancel the read operation. + /// The reader. + public static ReaderInteractor Create(bool runContinuationsAsynchronously, CancellationToken cancellationToken) => + cancellationToken.CanBeCanceled ? + new CancelableReaderInteractor(runContinuationsAsynchronously, cancellationToken) : + new ReaderInteractor(runContinuationsAsynchronously); + } + + /// A blocked or waiting writer. + /// Specifies the type of data being written. + internal class WriterInteractor : Interactor + { + /// Initializes the writer. + /// true if continuations should be forced to run asynchronously; otherwise, false. + protected WriterInteractor(bool runContinuationsAsynchronously) : base(runContinuationsAsynchronously) { } + + /// The item being written. + internal T Item { get; private set; } + + /// Creates a writer. + /// true if continuations should be forced to run asynchronously; otherwise, false. + /// The item being written. + /// A that can be used to cancel the read operation. + /// The reader. + public static WriterInteractor Create(bool runContinuationsAsynchronously, T item, CancellationToken cancellationToken) + { + WriterInteractor w = cancellationToken.CanBeCanceled ? + new CancelableWriter(runContinuationsAsynchronously, cancellationToken) : + new WriterInteractor(runContinuationsAsynchronously); + w.Item = item; + return w; + } + } + + /// A blocked or waiting reader where the read can be canceled. + /// Specifies the type of data being read. + internal sealed class CancelableReaderInteractor : ReaderInteractor + { + /// The token used for cancellation. + private readonly CancellationToken _token; + /// Registration in that should be disposed of when the operation has completed. + private CancellationTokenRegistration _registration; + + /// Initializes the cancelable reader. + /// true if continuations should be forced to run asynchronously; otherwise, false. + /// A that can be used to cancel the read operation. + internal CancelableReaderInteractor(bool runContinuationsAsynchronously, CancellationToken cancellationToken) : base(runContinuationsAsynchronously) + { + _token = cancellationToken; + _registration = cancellationToken.Register(s => + { + var thisRef = (CancelableReaderInteractor)s; + thisRef.TrySetCanceled(thisRef._token); + }, this); + } + + /// Unregister cancellation in case cancellation was registered. + internal override void UnregisterCancellation() + { + _registration.Dispose(); + _registration = default; + } + } + + /// A blocked or waiting reader where the read can be canceled. + /// Specifies the type of data being read. + internal sealed class CancelableWriter : WriterInteractor + { + /// The token used for cancellation. + private CancellationToken _token; + /// Registration in that should be disposed of when the operation has completed. + private CancellationTokenRegistration _registration; + + /// Initializes the cancelable writer. + /// true if continuations should be forced to run asynchronously; otherwise, false. + /// A that can be used to cancel the read operation. + internal CancelableWriter(bool runContinuationsAsynchronously, CancellationToken cancellationToken) : base(runContinuationsAsynchronously) + { + _token = cancellationToken; + _registration = cancellationToken.Register(s => + { + var thisRef = (CancelableWriter)s; + thisRef.TrySetCanceled(thisRef._token); + }, this); + } + + /// Unregister cancellation in case cancellation was registered. + internal override void UnregisterCancellation() + { + _registration.Dispose(); + _registration = default; + } + } +} diff --git a/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/SingleConsumerUnboundedChannel.cs b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/SingleConsumerUnboundedChannel.cs new file mode 100644 index 0000000000..b3435b88ae --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/SingleConsumerUnboundedChannel.cs @@ -0,0 +1,231 @@ +// 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.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading.Tasks; + +namespace System.Threading.Channels +{ + /// + /// Provides a buffered channel of unbounded capacity for use by any number + /// of writers but at most a single reader at a time. + /// + [DebuggerDisplay("Items={ItemsCountForDebugger}")] + [DebuggerTypeProxy(typeof(DebugEnumeratorDebugView<>))] + internal sealed class SingleConsumerUnboundedChannel : Channel, IDebugEnumerable + { + /// Task that indicates the channel has completed. + private readonly TaskCompletionSource _completion; + /// + /// A concurrent queue to hold the items for this channel. The queue itself supports at most + /// one writer and one reader at a time; as a result, since this channel supports multiple writers, + /// all write access to the queue must be synchronized by the channel. + /// + private readonly SingleProducerSingleConsumerQueue _items = new SingleProducerSingleConsumerQueue(); + /// Whether to force continuations to be executed asynchronously from producer writes. + private readonly bool _runContinuationsAsynchronously; + + /// non-null if the channel has been marked as complete for writing. + private volatile Exception _doneWriting; + + /// A waiting reader (e.g. WaitForReadAsync) if there is one. + private ReaderInteractor _waitingReader; + + /// Initialize the channel. + /// Whether to force continuations to be executed asynchronously. + internal SingleConsumerUnboundedChannel(bool runContinuationsAsynchronously) + { + _runContinuationsAsynchronously = runContinuationsAsynchronously; + _completion = new TaskCompletionSource(runContinuationsAsynchronously ? TaskCreationOptions.RunContinuationsAsynchronously : TaskCreationOptions.None); + + Reader = new UnboundedChannelReader(this); + Writer = new UnboundedChannelWriter(this); + } + + private sealed class UnboundedChannelReader : ChannelReader + { + internal readonly SingleConsumerUnboundedChannel _parent; + internal UnboundedChannelReader(SingleConsumerUnboundedChannel parent) => _parent = parent; + + public override Task Completion => _parent._completion.Task; + + public override bool TryRead(out T item) + { + SingleConsumerUnboundedChannel parent = _parent; + if (parent._items.TryDequeue(out item)) + { + if (parent._doneWriting != null && parent._items.IsEmpty) + { + ChannelUtilities.Complete(parent._completion, parent._doneWriting); + } + return true; + } + return false; + } + + public override Task WaitToReadAsync(CancellationToken cancellationToken) + { + // Outside of the lock, check if there are any items waiting to be read. If there are, we're done. + return + cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) : + !_parent._items.IsEmpty ? ChannelUtilities.s_trueTask : + WaitToReadAsyncCore(cancellationToken); + + Task WaitToReadAsyncCore(CancellationToken ct) + { + SingleConsumerUnboundedChannel parent = _parent; + ReaderInteractor oldWaiter = null, newWaiter; + lock (parent.SyncObj) + { + // Again while holding the lock, check to see if there are any items available. + if (!parent._items.IsEmpty) + { + return ChannelUtilities.s_trueTask; + } + + // There aren't any items; if we're done writing, there never will be more items. + if (parent._doneWriting != null) + { + return parent._doneWriting != ChannelUtilities.s_doneWritingSentinel ? + Task.FromException(parent._doneWriting) : + ChannelUtilities.s_falseTask; + } + + // Create the new waiter. We're a bit more tolerant of a stray waiting reader + // than we are of a blocked reader, as with usage patterns it's easier to leave one + // behind, so we just cancel any that may have been waiting around. + oldWaiter = parent._waitingReader; + parent._waitingReader = newWaiter = ReaderInteractor.Create(parent._runContinuationsAsynchronously, ct); + } + + oldWaiter?.TrySetCanceled(); + return newWaiter.Task; + } + } + } + + private sealed class UnboundedChannelWriter : ChannelWriter + { + internal readonly SingleConsumerUnboundedChannel _parent; + internal UnboundedChannelWriter(SingleConsumerUnboundedChannel parent) => _parent = parent; + + public override bool TryComplete(Exception error) + { + ReaderInteractor waitingReader = null; + bool completeTask = false; + + SingleConsumerUnboundedChannel parent = _parent; + lock (parent.SyncObj) + { + // If we're already marked as complete, there's nothing more to do. + if (parent._doneWriting != null) + { + return false; + } + + // Mark as complete for writing. + parent._doneWriting = error ?? ChannelUtilities.s_doneWritingSentinel; + + // If we have no more items remaining, then the channel needs to be marked as completed + // and readers need to be informed they'll never get another item. All of that needs + // to happen outside of the lock to avoid invoking continuations under the lock. + if (parent._items.IsEmpty) + { + completeTask = true; + + if (parent._waitingReader != null) + { + waitingReader = parent._waitingReader; + parent._waitingReader = null; + } + } + } + + // Complete the channel task if necessary + if (completeTask) + { + ChannelUtilities.Complete(parent._completion, error); + } + + // Complete a waiting reader if necessary. + if (waitingReader != null) + { + if (error != null) + { + waitingReader.Fail(error); + } + else + { + waitingReader.Success(item: false); + } + } + + // Successfully completed the channel + return true; + } + + public override bool TryWrite(T item) + { + SingleConsumerUnboundedChannel parent = _parent; + while (true) // in case a reader was canceled and we need to try again + { + ReaderInteractor waitingReader = null; + + lock (parent.SyncObj) + { + // If writing is completed, exit out without writing. + if (parent._doneWriting != null) + { + return false; + } + + // Queue the item being written; then if there's a waiting + // reader, store it for notification outside of the lock. + parent._items.Enqueue(item); + + waitingReader = parent._waitingReader; + if (waitingReader == null) + { + return true; + } + parent._waitingReader = null; + } + + // If we get here, we grabbed a waiting reader. + // Notify it that an item was written and exit. + Debug.Assert(waitingReader != null, "Expected a waiting reader"); + waitingReader.Success(item: true); + return true; + } + } + + public override Task WaitToWriteAsync(CancellationToken cancellationToken) + { + Exception doneWriting = _parent._doneWriting; + return + cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) : + doneWriting == null ? ChannelUtilities.s_trueTask : + doneWriting != ChannelUtilities.s_doneWritingSentinel ? Task.FromException(doneWriting) : + ChannelUtilities.s_falseTask; + } + + public override Task WriteAsync(T item, CancellationToken cancellationToken) => + // Writing always succeeds (unless we've already completed writing or cancellation has been requested), + // so just TryWrite and return a completed task. + cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) : + TryWrite(item) ? Task.CompletedTask : + Task.FromException(ChannelUtilities.CreateInvalidCompletionException(_parent._doneWriting)); + } + + private object SyncObj => _items; + + /// Gets the number of items in the channel. This should only be used by the debugger. + private int ItemsCountForDebugger => _items.Count; + + /// Gets an enumerator the debugger can use to show the contents of the channel. + IEnumerator IDebugEnumerable.GetEnumerator() => _items.GetEnumerator(); + } +} diff --git a/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/UnboundedChannel.cs b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/UnboundedChannel.cs new file mode 100644 index 0000000000..33ca00c6af --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/UnboundedChannel.cs @@ -0,0 +1,232 @@ +// 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.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading.Tasks; + +namespace System.Threading.Channels +{ + /// Provides a buffered channel of unbounded capacity. + [DebuggerDisplay("Items={ItemsCountForDebugger}")] + [DebuggerTypeProxy(typeof(DebugEnumeratorDebugView<>))] + internal sealed class UnboundedChannel : Channel, IDebugEnumerable + { + /// Task that indicates the channel has completed. + private readonly TaskCompletionSource _completion; + /// The items in the channel. + private readonly ConcurrentQueue _items = new ConcurrentQueue(); + /// Whether to force continuations to be executed asynchronously from producer writes. + private readonly bool _runContinuationsAsynchronously; + + /// Readers waiting for a notification that data is available. + private ReaderInteractor _waitingReaders; + /// Set to non-null once Complete has been called. + private Exception _doneWriting; + + /// Initialize the channel. + internal UnboundedChannel(bool runContinuationsAsynchronously) + { + _runContinuationsAsynchronously = runContinuationsAsynchronously; + _completion = new TaskCompletionSource(runContinuationsAsynchronously ? TaskCreationOptions.RunContinuationsAsynchronously : TaskCreationOptions.None); + base.Reader = new UnboundedChannelReader(this); + Writer = new UnboundedChannelWriter(this); + } + + private sealed class UnboundedChannelReader : ChannelReader + { + internal readonly UnboundedChannel _parent; + internal UnboundedChannelReader(UnboundedChannel parent) => _parent = parent; + + public override Task Completion => _parent._completion.Task; + + public override bool TryRead(out T item) + { + UnboundedChannel parent = _parent; + + // Dequeue an item if we can + if (parent._items.TryDequeue(out item)) + { + if (parent._doneWriting != null && parent._items.IsEmpty) + { + // If we've now emptied the items queue and we're not getting any more, complete. + ChannelUtilities.Complete(parent._completion, parent._doneWriting); + } + return true; + } + + item = default; + return false; + } + + public override Task WaitToReadAsync(CancellationToken cancellationToken) + { + // If there are any items, readers can try to get them. + return !_parent._items.IsEmpty ? + ChannelUtilities.s_trueTask : + WaitToReadAsyncCore(cancellationToken); + + Task WaitToReadAsyncCore(CancellationToken ct) + { + UnboundedChannel parent = _parent; + + lock (parent.SyncObj) + { + parent.AssertInvariants(); + + // Try again to read now that we're synchronized with writers. + if (!parent._items.IsEmpty) + { + return ChannelUtilities.s_trueTask; + } + + // There are no items, so if we're done writing, there's never going to be data available. + if (parent._doneWriting != null) + { + return parent._doneWriting != ChannelUtilities.s_doneWritingSentinel ? + Task.FromException(parent._doneWriting) : + ChannelUtilities.s_falseTask; + } + + // Queue the waiter + return ChannelUtilities.GetOrCreateWaiter(ref parent._waitingReaders, parent._runContinuationsAsynchronously, ct); + } + } + } + } + + private sealed class UnboundedChannelWriter : ChannelWriter + { + internal readonly UnboundedChannel _parent; + internal UnboundedChannelWriter(UnboundedChannel parent) => _parent = parent; + + public override bool TryComplete(Exception error) + { + UnboundedChannel parent = _parent; + bool completeTask; + + lock (parent.SyncObj) + { + parent.AssertInvariants(); + + // If we've already marked the channel as completed, bail. + if (parent._doneWriting != null) + { + return false; + } + + // Mark that we're done writing. + parent._doneWriting = error ?? ChannelUtilities.s_doneWritingSentinel; + completeTask = parent._items.IsEmpty; + } + + // If there are no items in the queue, complete the channel's task, + // as no more data can possibly arrive at this point. We do this outside + // of the lock in case we'll be running synchronous completions, and we + // do it before completing blocked/waiting readers, so that when they + // wake up they'll see the task as being completed. + if (completeTask) + { + ChannelUtilities.Complete(parent._completion, error); + } + + // At this point, _waitingReaders will not be mutated: + // it's only mutated by readers while holding the lock, and only if _doneWriting is null. + // We also know that only one thread (this one) will ever get here, as only that thread + // will be the one to transition from _doneWriting false to true. As such, we can + // freely manipulate _waitingReaders without any concurrency concerns. + ChannelUtilities.WakeUpWaiters(ref parent._waitingReaders, result: false, error: error); + + // Successfully transitioned to completed. + return true; + } + + public override bool TryWrite(T item) + { + UnboundedChannel parent = _parent; + while (true) + { + ReaderInteractor waitingReaders = null; + lock (parent.SyncObj) + { + // If writing has already been marked as done, fail the write. + parent.AssertInvariants(); + if (parent._doneWriting != null) + { + return false; + } + + // Add the data to the queue, and let any waiting readers know that they should try to read it. + // We can only complete such waiters here under the lock if they run continuations asynchronously + // (otherwise the synchronous continuations could be invoked under the lock). If we don't complete + // them here, we need to do so outside of the lock. + parent._items.Enqueue(item); + waitingReaders = parent._waitingReaders; + if (waitingReaders == null) + { + return true; + } + parent._waitingReaders = null; + } + + // Wake up all of the waiters. Since we've released the lock, it's possible + // we could cause some spurious wake-ups here, if we tell a waiter there's + // something available but all data has already been removed. It's a benign + // race condition, though, as consumers already need to account for such things. + waitingReaders.Success(item: true); + return true; + } + } + + public override Task WaitToWriteAsync(CancellationToken cancellationToken) + { + Exception doneWriting = _parent._doneWriting; + return + cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) : + doneWriting == null ? ChannelUtilities.s_trueTask : // unbounded writing can always be done if we haven't completed + doneWriting != ChannelUtilities.s_doneWritingSentinel ? Task.FromException(doneWriting) : + ChannelUtilities.s_falseTask; + } + + public override Task WriteAsync(T item, CancellationToken cancellationToken) => + cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) : + TryWrite(item) ? ChannelUtilities.s_trueTask : + Task.FromException(ChannelUtilities.CreateInvalidCompletionException(_parent._doneWriting)); + } + + /// Gets the object used to synchronize access to all state on this instance. + private object SyncObj => _items; + + [Conditional("DEBUG")] + private void AssertInvariants() + { + Debug.Assert(SyncObj != null, "The sync obj must not be null."); + Debug.Assert(Monitor.IsEntered(SyncObj), "Invariants can only be validated while holding the lock."); + + if (!_items.IsEmpty) + { + if (_runContinuationsAsynchronously) + { + Debug.Assert(_waitingReaders == null, "There's data available, so there shouldn't be any waiting readers."); + } + Debug.Assert(!_completion.Task.IsCompleted, "We still have data available, so shouldn't be completed."); + } + if (_waitingReaders != null && _runContinuationsAsynchronously) + { + Debug.Assert(_items.IsEmpty, "There are blocked/waiting readers, so there shouldn't be any data available."); + } + if (_completion.Task.IsCompleted) + { + Debug.Assert(_doneWriting != null, "We're completed, so we must be done writing."); + } + } + + /// Gets the number of items in the channel. This should only be used by the debugger. + private int ItemsCountForDebugger => _items.Count; + + /// Gets an enumerator the debugger can use to show the contents of the channel. + IEnumerator IDebugEnumerable.GetEnumerator() => _items.GetEnumerator(); + } +} diff --git a/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/UnbufferedChannel.cs b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/UnbufferedChannel.cs new file mode 100644 index 0000000000..0cb75b67a5 --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/src/System/Threading/Channels/UnbufferedChannel.cs @@ -0,0 +1,324 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Threading.Tasks; + +namespace System.Threading.Channels +{ + /// Provides an unbuffered channel, such that a reader and a writer must rendezvous to succeed. + [DebuggerDisplay("Writers Waiting/Blocked: {WaitingWritersForDebugger}/{BlockedWritersCountForDebugger}, Readers Waiting/Blocked: {WaitingReadersForDebugger}/{BlockedReadersCountForDebugger}")] + [DebuggerTypeProxy(typeof(UnbufferedChannel<>.DebugView))] + internal sealed class UnbufferedChannel : Channel + { + /// Task that represents the completion of the channel. + private readonly TaskCompletionSource _completion = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + /// A queue of readers blocked waiting to be matched with a writer. + private readonly Dequeue> _blockedReaders = new Dequeue>(); + /// A queue of writers blocked waiting to be matched with a reader. + private readonly Dequeue> _blockedWriters = new Dequeue>(); + + /// Task signaled when any WaitToReadAsync waiters should be woken up. + private ReaderInteractor _waitingReaders; + /// Task signaled when any WaitToReadAsync waiters should be woken up. + private ReaderInteractor _waitingWriters; + + private sealed class UnbufferedChannelReader : ChannelReader + { + internal readonly UnbufferedChannel _parent; + internal UnbufferedChannelReader(UnbufferedChannel parent) => _parent = parent; + + public override Task Completion => _parent._completion.Task; + + public override bool TryRead(out T item) + { + UnbufferedChannel parent = _parent; + lock (parent.SyncObj) + { + parent.AssertInvariants(); + + // Try to find a writer to pair with + while (!parent._blockedWriters.IsEmpty) + { + WriterInteractor w = parent._blockedWriters.DequeueHead(); + if (w.Success(default)) + { + item = w.Item; + return true; + } + } + } + + // None found + item = default; + return false; + } + + public override ValueTask ReadAsync(CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return new ValueTask(Task.FromCanceled(cancellationToken)); + } + + UnbufferedChannel parent = _parent; + lock (parent.SyncObj) + { + parent.AssertInvariants(); + + // If we're already completed, nothing to read. + if (parent._completion.Task.IsCompleted) + { + return new ValueTask( + parent._completion.Task.IsCanceled ? Task.FromCanceled(new CancellationToken(true)) : + Task.FromException( + parent._completion.Task.IsFaulted ? + ChannelUtilities.CreateInvalidCompletionException(parent._completion.Task.Exception.InnerException) : + ChannelUtilities.CreateInvalidCompletionException())); + } + + // If there are any blocked writers, find one to pair up with + // and get its data. Writers that got canceled will remain in the queue, + // so we need to loop to skip past them. + while (!parent._blockedWriters.IsEmpty) + { + WriterInteractor w = parent._blockedWriters.DequeueHead(); + if (w.Success(default(VoidResult))) + { + return new ValueTask(w.Item); + } + } + + // No writer found to pair with. Queue the reader. + var r = ReaderInteractor.Create(true, cancellationToken); + parent._blockedReaders.EnqueueTail(r); + + // And let any waiting writers know it's their lucky day. + ChannelUtilities.WakeUpWaiters(ref parent._waitingWriters, result: true); + + return new ValueTask(r.Task); + } + } + + public override Task WaitToReadAsync(CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + UnbufferedChannel parent = _parent; + lock (parent.SyncObj) + { + // If we're done writing, fail. + if (parent._completion.Task.IsCompleted) + { + return parent._completion.Task.IsFaulted ? + Task.FromException(parent._completion.Task.Exception.InnerException) : + ChannelUtilities.s_falseTask; + } + + // If there's a blocked writer, we can read. + if (!parent._blockedWriters.IsEmpty) + { + return ChannelUtilities.s_trueTask; + } + + // Otherwise, queue the waiter. + return ChannelUtilities.GetOrCreateWaiter(ref parent._waitingReaders, runContinuationsAsynchronously: true, cancellationToken); + } + } + } + + private sealed class UnbufferedChannelWriter : ChannelWriter + { + internal readonly UnbufferedChannel _parent; + internal UnbufferedChannelWriter(UnbufferedChannel parent) => _parent = parent; + + public override bool TryComplete(Exception error) + { + UnbufferedChannel parent = _parent; + lock (parent.SyncObj) + { + parent.AssertInvariants(); + + // Mark the channel as being done. Since there's no buffered data, we can complete immediately. + if (parent._completion.Task.IsCompleted) + { + return false; + } + ChannelUtilities.Complete(parent._completion, error); + + // Fail any blocked readers/writers, as there will be no writers/readers to pair them with. + ChannelUtilities.FailInteractors, T>(parent._blockedReaders, ChannelUtilities.CreateInvalidCompletionException(error)); + ChannelUtilities.FailInteractors, VoidResult>(parent._blockedWriters, ChannelUtilities.CreateInvalidCompletionException(error)); + + // Let any waiting readers and writers know there won't be any more data + ChannelUtilities.WakeUpWaiters(ref parent._waitingReaders, result: false, error: error); + ChannelUtilities.WakeUpWaiters(ref parent._waitingWriters, result: false, error: error); + } + + return true; + } + + public override bool TryWrite(T item) + { + UnbufferedChannel parent = _parent; + lock (parent.SyncObj) + { + parent.AssertInvariants(); + + // Try to find a reader to pair with + while (!parent._blockedReaders.IsEmpty) + { + ReaderInteractor r = parent._blockedReaders.DequeueHead(); + if (r.Success(item)) + { + return true; + } + } + } + + // None found + return false; + } + + public override Task WaitToWriteAsync(CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + UnbufferedChannel parent = _parent; + lock (parent.SyncObj) + { + // If we're done writing, fail. + if (parent._completion.Task.IsCompleted) + { + return parent._completion.Task.IsFaulted ? + Task.FromException(parent._completion.Task.Exception.InnerException) : + ChannelUtilities.s_falseTask; + } + + // If there's a blocked reader, we can write + if (!parent._blockedReaders.IsEmpty) + { + return ChannelUtilities.s_trueTask; + } + + // Otherwise, queue the writer + return ChannelUtilities.GetOrCreateWaiter(ref parent._waitingWriters, true, cancellationToken); + } + } + + public override Task WriteAsync(T item, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + UnbufferedChannel parent = _parent; + lock (parent.SyncObj) + { + // Fail if we've already completed + if (parent._completion.Task.IsCompleted) + { + return + parent._completion.Task.IsCanceled ? Task.FromCanceled(new CancellationToken(true)) : + Task.FromException( + parent._completion.Task.IsFaulted ? + ChannelUtilities.CreateInvalidCompletionException(parent._completion.Task.Exception.InnerException) : + ChannelUtilities.CreateInvalidCompletionException()); + } + + // Try to find a reader to pair with. Canceled readers remain in the queue, + // so we need to loop until we find one. + while (!parent._blockedReaders.IsEmpty) + { + ReaderInteractor r = parent._blockedReaders.DequeueHead(); + if (r.Success(item)) + { + return Task.CompletedTask; + } + } + + // No reader was available. Queue the writer. + var w = WriterInteractor.Create(true, item, cancellationToken); + parent._blockedWriters.EnqueueTail(w); + + // And let any waiting readers know it's their lucky day. + ChannelUtilities.WakeUpWaiters(ref parent._waitingReaders, result: true); + + return w.Task; + } + } + } + + /// Initialize the channel. + internal UnbufferedChannel() + { + base.Reader = new UnbufferedChannelReader(this); + Writer = new UnbufferedChannelWriter(this); + } + + /// Gets an object used to synchronize all state on the instance. + private object SyncObj => _completion; + + [Conditional("DEBUG")] + private void AssertInvariants() + { + Debug.Assert(SyncObj != null, "The sync obj must not be null."); + Debug.Assert(Monitor.IsEntered(SyncObj), "Invariants can only be validated while holding the lock."); + + if (!_blockedReaders.IsEmpty) + { + Debug.Assert(_blockedWriters.IsEmpty, "If there are blocked readers, there can't be blocked writers."); + } + if (!_blockedWriters.IsEmpty) + { + Debug.Assert(_blockedReaders.IsEmpty, "If there are blocked writers, there can't be blocked readers."); + } + if (_completion.Task.IsCompleted) + { + Debug.Assert(_blockedReaders.IsEmpty, "No readers can be blocked after we've completed."); + Debug.Assert(_blockedWriters.IsEmpty, "No writers can be blocked after we've completed."); + } + } + + /// Gets whether there are any waiting writers. This should only be used by the debugger. + private bool WaitingWritersForDebugger => _waitingWriters != null; + /// Gets whether there are any waiting readers. This should only be used by the debugger. + private bool WaitingReadersForDebugger => _waitingReaders != null; + /// Gets the number of blocked writers. This should only be used by the debugger. + private int BlockedWritersCountForDebugger => _blockedWriters.Count; + /// Gets the number of blocked readers. This should only be used by the debugger. + private int BlockedReadersCountForDebugger => _blockedReaders.Count; + + private sealed class DebugView + { + private readonly UnbufferedChannel _channel; + + public DebugView(UnbufferedChannel channel) => _channel = channel; + + public bool WaitingReaders => _channel._waitingReaders != null; + public bool WaitingWriters => _channel._waitingWriters != null; + public int BlockedReaders => _channel._blockedReaders.Count; + public T[] BlockedWriters + { + get + { + var items = new List(); + foreach (WriterInteractor blockedWriter in _channel._blockedWriters) + { + items.Add(blockedWriter.Item); + } + return items.ToArray(); + } + } + } + } +} diff --git a/external/corefx/src/System.Data.Odbc/src/Common/System/Data/Common/ExternDll.Osx.cs b/external/corefx/src/System.Threading.Channels/src/System/VoidResult.cs similarity index 60% rename from external/corefx/src/System.Data.Odbc/src/Common/System/Data/Common/ExternDll.Osx.cs rename to external/corefx/src/System.Threading.Channels/src/System/VoidResult.cs index 5e6c68c5f6..43e8f44d98 100644 --- a/external/corefx/src/System.Data.Odbc/src/Common/System/Data/Common/ExternDll.Osx.cs +++ b/external/corefx/src/System.Threading.Channels/src/System/VoidResult.cs @@ -2,11 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace System.Data.Common +namespace System { - internal static class ExternDll - { - public const string Odbc32 = "libodbc.2.dylib"; - } + /// An empty struct, used to represent void in generic types. + internal struct VoidResult { } } - diff --git a/external/corefx/src/System.Threading.Channels/tests/BoundedChannelTests.cs b/external/corefx/src/System.Threading.Channels/tests/BoundedChannelTests.cs new file mode 100644 index 0000000000..b19bc8605a --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/tests/BoundedChannelTests.cs @@ -0,0 +1,402 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Threading.Tasks; +using Xunit; + +namespace System.Threading.Channels.Tests +{ + public class BoundedChannelTests : ChannelTestBase + { + protected override Channel CreateChannel() => Channel.CreateBounded(1); + protected override Channel CreateFullChannel() + { + var c = Channel.CreateBounded(1); + c.Writer.WriteAsync(42).Wait(); + return c; + } + + [Theory] + [InlineData(1)] + [InlineData(10)] + [InlineData(10000)] + public void TryWrite_TryRead_Many_Wait(int bufferedCapacity) + { + var c = Channel.CreateBounded(bufferedCapacity); + + for (int i = 0; i < bufferedCapacity; i++) + { + Assert.True(c.Writer.TryWrite(i)); + } + Assert.False(c.Writer.TryWrite(bufferedCapacity)); + + int result; + for (int i = 0; i < bufferedCapacity; i++) + { + Assert.True(c.Reader.TryRead(out result)); + Assert.Equal(i, result); + } + + Assert.False(c.Reader.TryRead(out result)); + Assert.Equal(0, result); + } + + [Theory] + [InlineData(1)] + [InlineData(10)] + [InlineData(10000)] + public void TryWrite_TryRead_Many_DropOldest(int bufferedCapacity) + { + var c = Channel.CreateBounded(new BoundedChannelOptions(bufferedCapacity) { FullMode = BoundedChannelFullMode.DropOldest }); + + for (int i = 0; i < bufferedCapacity * 2; i++) + { + Assert.True(c.Writer.TryWrite(i)); + } + + int result; + for (int i = bufferedCapacity; i < bufferedCapacity * 2; i++) + { + Assert.True(c.Reader.TryRead(out result)); + Assert.Equal(i, result); + } + + Assert.False(c.Reader.TryRead(out result)); + Assert.Equal(0, result); + } + + [Theory] + [InlineData(1)] + [InlineData(10)] + [InlineData(10000)] + public void WriteAsync_TryRead_Many_DropOldest(int bufferedCapacity) + { + var c = Channel.CreateBounded(new BoundedChannelOptions(bufferedCapacity) { FullMode = BoundedChannelFullMode.DropOldest }); + + for (int i = 0; i < bufferedCapacity * 2; i++) + { + AssertSynchronousSuccess(c.Writer.WriteAsync(i)); + } + + int result; + for (int i = bufferedCapacity; i < bufferedCapacity * 2; i++) + { + Assert.True(c.Reader.TryRead(out result)); + Assert.Equal(i, result); + } + + Assert.False(c.Reader.TryRead(out result)); + Assert.Equal(0, result); + } + + [Theory] + [InlineData(1)] + [InlineData(10)] + [InlineData(10000)] + public void TryWrite_TryRead_Many_DropNewest(int bufferedCapacity) + { + var c = Channel.CreateBounded(new BoundedChannelOptions(bufferedCapacity) { FullMode = BoundedChannelFullMode.DropNewest }); + + for (int i = 0; i < bufferedCapacity * 2; i++) + { + Assert.True(c.Writer.TryWrite(i)); + } + + int result; + for (int i = 0; i < bufferedCapacity - 1; i++) + { + Assert.True(c.Reader.TryRead(out result)); + Assert.Equal(i, result); + } + Assert.True(c.Reader.TryRead(out result)); + Assert.Equal(bufferedCapacity * 2 - 1, result); + + Assert.False(c.Reader.TryRead(out result)); + Assert.Equal(0, result); + } + + [Theory] + [InlineData(1)] + [InlineData(10)] + [InlineData(10000)] + public void WriteAsync_TryRead_Many_DropNewest(int bufferedCapacity) + { + var c = Channel.CreateBounded(new BoundedChannelOptions(bufferedCapacity) { FullMode = BoundedChannelFullMode.DropNewest }); + + for (int i = 0; i < bufferedCapacity * 2; i++) + { + AssertSynchronousSuccess(c.Writer.WriteAsync(i)); + } + + int result; + for (int i = 0; i < bufferedCapacity - 1; i++) + { + Assert.True(c.Reader.TryRead(out result)); + Assert.Equal(i, result); + } + Assert.True(c.Reader.TryRead(out result)); + Assert.Equal(bufferedCapacity * 2 - 1, result); + + Assert.False(c.Reader.TryRead(out result)); + Assert.Equal(0, result); + } + + [Fact] + public async Task TryWrite_DropNewest_WrappedAroundInternalQueue() + { + var c = Channel.CreateBounded(new BoundedChannelOptions(3) { FullMode = BoundedChannelFullMode.DropNewest }); + + // Move head of dequeue beyond the beginning + Assert.True(c.Writer.TryWrite(1)); + Assert.True(c.Reader.TryRead(out int item)); + Assert.Equal(1, item); + + // Add items to fill the capacity and put the tail at 0 + Assert.True(c.Writer.TryWrite(2)); + Assert.True(c.Writer.TryWrite(3)); + Assert.True(c.Writer.TryWrite(4)); + + // Add an item to overwrite the newest + Assert.True(c.Writer.TryWrite(5)); + + // Verify current contents + Assert.Equal(2, await c.Reader.ReadAsync()); + Assert.Equal(3, await c.Reader.ReadAsync()); + Assert.Equal(5, await c.Reader.ReadAsync()); + } + + [Theory] + [InlineData(1)] + [InlineData(10)] + [InlineData(10000)] + public void TryWrite_TryRead_Many_Ignore(int bufferedCapacity) + { + var c = Channel.CreateBounded(new BoundedChannelOptions(bufferedCapacity) { FullMode = BoundedChannelFullMode.DropWrite }); + + for (int i = 0; i < bufferedCapacity * 2; i++) + { + Assert.True(c.Writer.TryWrite(i)); + } + + int result; + for (int i = 0; i < bufferedCapacity; i++) + { + Assert.True(c.Reader.TryRead(out result)); + Assert.Equal(i, result); + } + + Assert.False(c.Reader.TryRead(out result)); + Assert.Equal(0, result); + } + + [Theory] + [InlineData(1)] + [InlineData(10)] + [InlineData(10000)] + public void WriteAsync_TryRead_Many_Ignore(int bufferedCapacity) + { + var c = Channel.CreateBounded(new BoundedChannelOptions(bufferedCapacity) { FullMode = BoundedChannelFullMode.DropWrite }); + + for (int i = 0; i < bufferedCapacity * 2; i++) + { + AssertSynchronousSuccess(c.Writer.WriteAsync(i)); + } + + int result; + for (int i = 0; i < bufferedCapacity; i++) + { + Assert.True(c.Reader.TryRead(out result)); + Assert.Equal(i, result); + } + + Assert.False(c.Reader.TryRead(out result)); + Assert.Equal(0, result); + } + + [Fact] + public async Task CancelPendingWrite_Reading_DataTransferredFromCorrectWriter() + { + var c = Channel.CreateBounded(1); + Assert.Equal(TaskStatus.RanToCompletion, c.Writer.WriteAsync(42).Status); + + var cts = new CancellationTokenSource(); + + Task write1 = c.Writer.WriteAsync(43, cts.Token); + Assert.Equal(TaskStatus.WaitingForActivation, write1.Status); + + cts.Cancel(); + + Task write2 = c.Writer.WriteAsync(44); + + Assert.Equal(42, await c.Reader.ReadAsync()); + Assert.Equal(44, await c.Reader.ReadAsync()); + + await AssertCanceled(write1, cts.Token); + await write2; + } + + [Theory] + [InlineData(1)] + [InlineData(10)] + [InlineData(10000)] + public void TryWrite_TryRead_OneAtATime(int bufferedCapacity) + { + var c = Channel.CreateBounded(bufferedCapacity); + + const int NumItems = 100000; + for (int i = 0; i < NumItems; i++) + { + Assert.True(c.Writer.TryWrite(i)); + Assert.True(c.Reader.TryRead(out int result)); + Assert.Equal(i, result); + } + } + + [Theory] + [InlineData(1)] + [InlineData(10)] + [InlineData(10000)] + public void SingleProducerConsumer_ConcurrentReadWrite_WithBufferedCapacity_Success(int bufferedCapacity) + { + var c = Channel.CreateBounded(bufferedCapacity); + + const int NumItems = 10000; + Task.WaitAll( + Task.Run(async () => + { + for (int i = 0; i < NumItems; i++) + { + await c.Writer.WriteAsync(i); + } + }), + Task.Run(async () => + { + for (int i = 0; i < NumItems; i++) + { + Assert.Equal(i, await c.Reader.ReadAsync()); + } + })); + } + + [Theory] + [InlineData(1)] + [InlineData(10)] + [InlineData(10000)] + public void ManyProducerConsumer_ConcurrentReadWrite_WithBufferedCapacity_Success(int bufferedCapacity) + { + var c = Channel.CreateBounded(bufferedCapacity); + + const int NumWriters = 10; + const int NumReaders = 10; + const int NumItems = 10000; + + long readTotal = 0; + int remainingWriters = NumWriters; + int remainingItems = NumItems; + + Task[] tasks = new Task[NumWriters + NumReaders]; + + for (int i = 0; i < NumReaders; i++) + { + tasks[i] = Task.Run(async () => + { + try + { + while (true) + { + Interlocked.Add(ref readTotal, await c.Reader.ReadAsync()); + } + } + catch (ChannelClosedException) { } + }); + } + + for (int i = 0; i < NumWriters; i++) + { + tasks[NumReaders + i] = Task.Run(async () => + { + while (true) + { + int value = Interlocked.Decrement(ref remainingItems); + if (value < 0) + { + break; + } + await c.Writer.WriteAsync(value + 1); + } + if (Interlocked.Decrement(ref remainingWriters) == 0) + { + c.Writer.Complete(); + } + }); + } + + Task.WaitAll(tasks); + Assert.Equal((NumItems * (NumItems + 1L)) / 2, readTotal); + } + + [Fact] + public async Task WaitToWriteAsync_AfterFullThenRead_ReturnsTrue() + { + var c = Channel.CreateBounded(1); + Assert.True(c.Writer.TryWrite(1)); + + Task write1 = c.Writer.WaitToWriteAsync(); + Assert.False(write1.IsCompleted); + + Task write2 = c.Writer.WaitToWriteAsync(); + Assert.False(write2.IsCompleted); + + Assert.Equal(1, await c.Reader.ReadAsync()); + + Assert.True(await write1); + Assert.True(await write2); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void AllowSynchronousContinuations_WaitToReadAsync_ContinuationsInvokedAccordingToSetting(bool allowSynchronousContinuations) + { + var c = Channel.CreateBounded(new BoundedChannelOptions(1) { AllowSynchronousContinuations = allowSynchronousContinuations }); + + int expectedId = Environment.CurrentManagedThreadId; + Task r = c.Reader.WaitToReadAsync().ContinueWith(_ => + { + Assert.Equal(allowSynchronousContinuations, expectedId == Environment.CurrentManagedThreadId); + }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); + + Assert.Equal(TaskStatus.RanToCompletion, c.Writer.WriteAsync(42).Status); + ((IAsyncResult)r).AsyncWaitHandle.WaitOne(); // avoid inlining the continuation + r.GetAwaiter().GetResult(); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void AllowSynchronousContinuations_CompletionTask_ContinuationsInvokedAccordingToSetting(bool allowSynchronousContinuations) + { + var c = Channel.CreateBounded(new BoundedChannelOptions(1) { AllowSynchronousContinuations = allowSynchronousContinuations }); + + int expectedId = Environment.CurrentManagedThreadId; + Task r = c.Reader.Completion.ContinueWith(_ => + { + Assert.Equal(allowSynchronousContinuations, expectedId == Environment.CurrentManagedThreadId); + }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); + + Assert.True(c.Writer.TryComplete()); + ((IAsyncResult)r).AsyncWaitHandle.WaitOne(); // avoid inlining the continuation + r.GetAwaiter().GetResult(); + } + + [Fact] + public void TryWrite_NoBlockedReaders_WaitingReader_WaiterNotifified() + { + Channel c = CreateChannel(); + + Task r = c.Reader.WaitToReadAsync(); + Assert.True(c.Writer.TryWrite(42)); + AssertSynchronousTrue(r); + } + } +} diff --git a/external/corefx/src/System.Threading.Channels/tests/ChannelClosedExceptionTests.cs b/external/corefx/src/System.Threading.Channels/tests/ChannelClosedExceptionTests.cs new file mode 100644 index 0000000000..38a1e9bb50 --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/tests/ChannelClosedExceptionTests.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Xunit; + +namespace System.Threading.Channels.Tests +{ + public class ChannelClosedExceptionTests + { + [Fact] + public void Ctors() + { + var e = new ChannelClosedException(); + Assert.NotEmpty(e.Message); + Assert.Null(e.InnerException); + + e = new ChannelClosedException("hello"); + Assert.Equal("hello", e.Message); + Assert.Null(e.InnerException); + + var inner = new FormatException(); + e = new ChannelClosedException("hello", inner); + Assert.Equal("hello", e.Message); + Assert.Same(inner, e.InnerException); + } + } +} diff --git a/external/corefx/src/System.Threading.Channels/tests/ChannelTestBase.cs b/external/corefx/src/System.Threading.Channels/tests/ChannelTestBase.cs new file mode 100644 index 0000000000..3260dbf2f9 --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/tests/ChannelTestBase.cs @@ -0,0 +1,694 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace System.Threading.Channels.Tests +{ + public abstract class ChannelTestBase : TestBase + { + protected abstract Channel CreateChannel(); + protected abstract Channel CreateFullChannel(); + + protected virtual bool RequiresSingleReader => false; + protected virtual bool RequiresSingleWriter => false; + protected virtual bool BuffersItems => true; + + [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot, "Requires internal reflection on framework types.")] + [Fact] + public void ValidateDebuggerAttributes() + { + Channel c = CreateChannel(); + for (int i = 1; i <= 10; i++) + { + c.Writer.WriteAsync(i); + } + DebuggerAttributes.ValidateDebuggerDisplayReferences(c); + DebuggerAttributes.InvokeDebuggerTypeProxyProperties(c); + } + + [Fact] + public void Cast_MatchesInOut() + { + Channel c = CreateChannel(); + ChannelReader rc = c; + ChannelWriter wc = c; + Assert.Same(rc, c.Reader); + Assert.Same(wc, c.Writer); + } + + [Fact] + public void Completion_Idempotent() + { + Channel c = CreateChannel(); + + Task completion = c.Reader.Completion; + Assert.Equal(TaskStatus.WaitingForActivation, completion.Status); + + Assert.Same(completion, c.Reader.Completion); + c.Writer.Complete(); + Assert.Same(completion, c.Reader.Completion); + + Assert.Equal(TaskStatus.RanToCompletion, completion.Status); + } + + [Fact] + public async Task Complete_AfterEmpty_NoWaiters_TriggersCompletion() + { + Channel c = CreateChannel(); + c.Writer.Complete(); + await c.Reader.Completion; + } + + [Fact] + public async Task Complete_AfterEmpty_WaitingReader_TriggersCompletion() + { + Channel c = CreateChannel(); + Task r = c.Reader.ReadAsync().AsTask(); + c.Writer.Complete(); + await c.Reader.Completion; + await Assert.ThrowsAnyAsync(() => r); + } + + [Fact] + public async Task Complete_BeforeEmpty_WaitingReaders_TriggersCompletion() + { + Channel c = CreateChannel(); + Task read = c.Reader.ReadAsync().AsTask(); + c.Writer.Complete(); + await c.Reader.Completion; + await Assert.ThrowsAnyAsync(() => read); + } + + [Fact] + public void Complete_Twice_ThrowsInvalidOperationException() + { + Channel c = CreateChannel(); + c.Writer.Complete(); + Assert.ThrowsAny(() => c.Writer.Complete()); + } + + [Fact] + public void TryComplete_Twice_ReturnsTrueThenFalse() + { + Channel c = CreateChannel(); + Assert.True(c.Writer.TryComplete()); + Assert.False(c.Writer.TryComplete()); + Assert.False(c.Writer.TryComplete()); + } + + [Fact] + public async Task TryComplete_ErrorsPropage() + { + Channel c; + + // Success + c = CreateChannel(); + Assert.True(c.Writer.TryComplete()); + await c.Reader.Completion; + + // Error + c = CreateChannel(); + Assert.True(c.Writer.TryComplete(new FormatException())); + await Assert.ThrowsAsync(() => c.Reader.Completion); + + // Canceled + c = CreateChannel(); + var cts = new CancellationTokenSource(); + cts.Cancel(); + Assert.True(c.Writer.TryComplete(new OperationCanceledException(cts.Token))); + await AssertCanceled(c.Reader.Completion, cts.Token); + } + + [Fact] + public void SingleProducerConsumer_ConcurrentReadWrite_Success() + { + Channel c = CreateChannel(); + + const int NumItems = 100000; + Task.WaitAll( + Task.Run(async () => + { + for (int i = 0; i < NumItems; i++) + { + await c.Writer.WriteAsync(i); + } + }), + Task.Run(async () => + { + for (int i = 0; i < NumItems; i++) + { + Assert.Equal(i, await c.Reader.ReadAsync()); + } + })); + } + + [Fact] + public void SingleProducerConsumer_PingPong_Success() + { + Channel c1 = CreateChannel(); + Channel c2 = CreateChannel(); + + const int NumItems = 100000; + Task.WaitAll( + Task.Run(async () => + { + for (int i = 0; i < NumItems; i++) + { + Assert.Equal(i, await c1.Reader.ReadAsync()); + await c2.Writer.WriteAsync(i); + } + }), + Task.Run(async () => + { + for (int i = 0; i < NumItems; i++) + { + await c1.Writer.WriteAsync(i); + Assert.Equal(i, await c2.Reader.ReadAsync()); + } + })); + } + + [Theory] + [InlineData(1, 1)] + [InlineData(1, 10)] + [InlineData(10, 1)] + [InlineData(10, 10)] + public void ManyProducerConsumer_ConcurrentReadWrite_Success(int numReaders, int numWriters) + { + if (RequiresSingleReader && numReaders > 1) + { + return; + } + + if (RequiresSingleWriter && numWriters > 1) + { + return; + } + + Channel c = CreateChannel(); + + const int NumItems = 10000; + + long readTotal = 0; + int remainingWriters = numWriters; + int remainingItems = NumItems; + + Task[] tasks = new Task[numWriters + numReaders]; + + for (int i = 0; i < numReaders; i++) + { + tasks[i] = Task.Run(async () => + { + try + { + while (await c.Reader.WaitToReadAsync()) + { + if (c.Reader.TryRead(out int value)) + { + Interlocked.Add(ref readTotal, value); + } + } + } + catch (ChannelClosedException) { } + }); + } + + for (int i = 0; i < numWriters; i++) + { + tasks[numReaders + i] = Task.Run(async () => + { + while (true) + { + int value = Interlocked.Decrement(ref remainingItems); + if (value < 0) + { + break; + } + await c.Writer.WriteAsync(value + 1); + } + if (Interlocked.Decrement(ref remainingWriters) == 0) + { + c.Writer.Complete(); + } + }); + } + + Task.WaitAll(tasks); + Assert.Equal((NumItems * (NumItems + 1L)) / 2, readTotal); + } + + [Fact] + public void WaitToReadAsync_DataAvailableBefore_CompletesSynchronously() + { + Channel c = CreateChannel(); + Task write = c.Writer.WriteAsync(42); + Task read = c.Reader.WaitToReadAsync(); + Assert.Equal(TaskStatus.RanToCompletion, read.Status); + } + + [Fact] + public void WaitToReadAsync_DataAvailableAfter_CompletesAsynchronously() + { + Channel c = CreateChannel(); + Task read = c.Reader.WaitToReadAsync(); + Assert.False(read.IsCompleted); + Task write = c.Writer.WriteAsync(42); + Assert.True(read.Result); + } + + [Fact] + public void WaitToReadAsync_AfterComplete_SynchronouslyCompletes() + { + Channel c = CreateChannel(); + c.Writer.Complete(); + Task read = c.Reader.WaitToReadAsync(); + Assert.Equal(TaskStatus.RanToCompletion, read.Status); + Assert.False(read.Result); + } + + [Fact] + public void WaitToReadAsync_BeforeComplete_AsynchronouslyCompletes() + { + Channel c = CreateChannel(); + Task read = c.Reader.WaitToReadAsync(); + Assert.False(read.IsCompleted); + c.Writer.Complete(); + Assert.False(read.Result); + } + + [Fact] + public void WaitToWriteAsync_AfterComplete_SynchronouslyCompletes() + { + Channel c = CreateChannel(); + c.Writer.Complete(); + Task write = c.Writer.WaitToWriteAsync(); + Assert.Equal(TaskStatus.RanToCompletion, write.Status); + Assert.False(write.Result); + } + + [Fact] + public void WaitToWriteAsync_EmptyChannel_SynchronouslyCompletes() + { + if (!BuffersItems) + { + return; + } + + Channel c = CreateChannel(); + Task write = c.Writer.WaitToWriteAsync(); + Assert.Equal(TaskStatus.RanToCompletion, write.Status); + Assert.True(write.Result); + } + + [Fact] + public async Task WaitToWriteAsync_ManyConcurrent_SatisifedByReaders() + { + if (RequiresSingleReader || RequiresSingleWriter) + { + return; + } + + Channel c = CreateChannel(); + + Task[] writers = Enumerable.Range(0, 100).Select(_ => c.Writer.WaitToWriteAsync()).ToArray(); + Task[] readers = Enumerable.Range(0, 100).Select(_ => c.Reader.ReadAsync().AsTask()).ToArray(); + + await Task.WhenAll(writers); + } + + [Fact] + public void WaitToWriteAsync_BlockedReader_ReturnsTrue() + { + Channel c = CreateChannel(); + ValueTask reader = c.Reader.ReadAsync(); + AssertSynchronousSuccess(c.Writer.WaitToWriteAsync()); + } + + [Fact] + public void TryRead_DataAvailable_Success() + { + Channel c = CreateChannel(); + Task write = c.Writer.WriteAsync(42); + Assert.True(c.Reader.TryRead(out int result)); + Assert.Equal(42, result); + } + + [Fact] + public void TryRead_AfterComplete_ReturnsFalse() + { + Channel c = CreateChannel(); + c.Writer.Complete(); + Assert.False(c.Reader.TryRead(out int result)); + } + + [Fact] + public void TryWrite_AfterComplete_ReturnsFalse() + { + Channel c = CreateChannel(); + c.Writer.Complete(); + Assert.False(c.Writer.TryWrite(42)); + } + + [Fact] + public async Task WriteAsync_AfterComplete_ThrowsException() + { + Channel c = CreateChannel(); + c.Writer.Complete(); + await Assert.ThrowsAnyAsync(() => c.Writer.WriteAsync(42)); + } + + [Fact] + public async Task Complete_WithException_PropagatesToCompletion() + { + Channel c = CreateChannel(); + var exc = new FormatException(); + c.Writer.Complete(exc); + Assert.Same(exc, await Assert.ThrowsAsync(() => c.Reader.Completion)); + } + + [Fact] + public async Task Complete_WithCancellationException_PropagatesToCompletion() + { + Channel c = CreateChannel(); + var cts = new CancellationTokenSource(); + cts.Cancel(); + + Exception exc = null; + try { cts.Token.ThrowIfCancellationRequested(); } + catch (Exception e) { exc = e; } + + c.Writer.Complete(exc); + await AssertCanceled(c.Reader.Completion, cts.Token); + } + + [Fact] + public async Task Complete_WithException_PropagatesToExistingWriter() + { + Channel c = CreateFullChannel(); + if (c != null) + { + Task write = c.Writer.WriteAsync(42); + var exc = new FormatException(); + c.Writer.Complete(exc); + Assert.Same(exc, (await Assert.ThrowsAsync(() => write)).InnerException); + } + } + + [Fact] + public async Task Complete_WithException_PropagatesToNewWriter() + { + Channel c = CreateChannel(); + var exc = new FormatException(); + c.Writer.Complete(exc); + Task write = c.Writer.WriteAsync(42); + Assert.Same(exc, (await Assert.ThrowsAsync(() => write)).InnerException); + } + + [Fact] + public async Task Complete_WithException_PropagatesToExistingWaitingReader() + { + Channel c = CreateChannel(); + Task read = c.Reader.WaitToReadAsync(); + var exc = new FormatException(); + c.Writer.Complete(exc); + await Assert.ThrowsAsync(() => read); + } + + [Fact] + public async Task Complete_WithException_PropagatesToNewWaitingReader() + { + Channel c = CreateChannel(); + var exc = new FormatException(); + c.Writer.Complete(exc); + Task read = c.Reader.WaitToReadAsync(); + await Assert.ThrowsAsync(() => read); + } + + [Fact] + public async Task Complete_WithException_PropagatesToNewWaitingWriter() + { + Channel c = CreateChannel(); + var exc = new FormatException(); + c.Writer.Complete(exc); + Task write = c.Writer.WaitToWriteAsync(); + await Assert.ThrowsAsync(() => write); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + public void ManyWriteAsync_ThenManyTryRead_Success(int readMode) + { + if (RequiresSingleReader || RequiresSingleWriter) + { + return; + } + + Channel c = CreateChannel(); + + const int NumItems = 2000; + + Task[] writers = new Task[NumItems]; + for (int i = 0; i < writers.Length; i++) + { + writers[i] = c.Writer.WriteAsync(i); + } + + Task[] readers = new Task[NumItems]; + for (int i = 0; i < readers.Length; i++) + { + int result; + Assert.True(c.Reader.TryRead(out result)); + Assert.Equal(i, result); + } + + Assert.All(writers, w => Assert.True(w.IsCompleted)); + } + + [Fact] + public void Precancellation_Writing_ReturnsImmediately() + { + Channel c = CreateChannel(); + + Task writeTask = c.Writer.WriteAsync(42, new CancellationToken(true)); + Assert.Equal(TaskStatus.Canceled, writeTask.Status); + + Task waitTask = c.Writer.WaitToWriteAsync(new CancellationToken(true)); + Assert.Equal(TaskStatus.Canceled, waitTask.Status); + } + + [Fact] + public void Write_WaitToReadAsync_CompletesSynchronously() + { + Channel c = CreateChannel(); + c.Writer.WriteAsync(42); + AssertSynchronousTrue(c.Reader.WaitToReadAsync()); + } + + [Fact] + public void Precancellation_WaitToReadAsync_ReturnsImmediately() + { + Channel c = CreateChannel(); + + Task writeTask = c.Reader.WaitToReadAsync(new CancellationToken(true)); + Assert.Equal(TaskStatus.Canceled, writeTask.Status); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task WaitToReadAsync_DataWritten_CompletesSuccessfully(bool cancelable) + { + Channel c = CreateChannel(); + CancellationToken token = cancelable ? new CancellationTokenSource().Token : default; + + Task read = c.Reader.WaitToReadAsync(token); + Assert.False(read.IsCompleted); + + Task write = c.Writer.WriteAsync(42, token); + + Assert.True(await read); + } + + [Fact] + public async Task WaitToReadAsync_NoDataWritten_Canceled_CompletesAsCanceled() + { + Channel c = CreateChannel(); + var cts = new CancellationTokenSource(); + + Task read = c.Reader.WaitToReadAsync(cts.Token); + Assert.False(read.IsCompleted); + cts.Cancel(); + await Assert.ThrowsAnyAsync(() => read); + } + + [Fact] + public async Task ReadAsync_ThenWriteAsync_Succeeds() + { + Channel c = CreateChannel(); + + ValueTask r = c.Reader.ReadAsync(); + Assert.False(r.IsCompleted); + + Task w = c.Writer.WriteAsync(42); + AssertSynchronousSuccess(w); + + Assert.Equal(42, await r); + } + + [Fact] + public async Task WriteAsync_ReadAsync_Succeeds() + { + Channel c = CreateChannel(); + + Task w = c.Writer.WriteAsync(42); + ValueTask r = c.Reader.ReadAsync(); + + await Task.WhenAll(w, r.AsTask()); + + Assert.Equal(42, await r); + } + + [Fact] + public void ReadAsync_Precanceled_CanceledSynchronously() + { + Channel c = CreateChannel(); + var cts = new CancellationTokenSource(); + cts.Cancel(); + AssertSynchronouslyCanceled(c.Reader.ReadAsync(cts.Token).AsTask(), cts.Token); + } + + [Fact] + public async Task ReadAsync_Canceled_CanceledAsynchronously() + { + Channel c = CreateChannel(); + var cts = new CancellationTokenSource(); + + ValueTask r = c.Reader.ReadAsync(cts.Token); + Assert.False(r.IsCompleted); + + cts.Cancel(); + + await AssertCanceled(r.AsTask(), cts.Token); + + if (c.Writer.TryWrite(42)) + { + Assert.Equal(42, await c.Reader.ReadAsync()); + } + } + + [Fact] + public async Task ReadAsync_WriteAsync_ManyConcurrentReaders_SerializedWriters_Success() + { + if (RequiresSingleReader) + { + return; + } + + Channel c = CreateChannel(); + const int Items = 100; + + ValueTask[] readers = (from i in Enumerable.Range(0, Items) select c.Reader.ReadAsync()).ToArray(); + for (int i = 0; i < Items; i++) + { + await c.Writer.WriteAsync(i); + } + + Assert.Equal((Items * (Items - 1)) / 2, Enumerable.Sum(await Task.WhenAll(readers.Select(r => r.AsTask())))); + } + + [Fact] + public async Task ReadAsync_TryWrite_ManyConcurrentReaders_SerializedWriters_Success() + { + if (RequiresSingleReader) + { + return; + } + + Channel c = CreateChannel(); + const int Items = 100; + + ValueTask[] readers = (from i in Enumerable.Range(0, Items) select c.Reader.ReadAsync()).ToArray(); + var remainingReaders = new List>(readers.Select(r => r.AsTask())); + + for (int i = 0; i < Items; i++) + { + Assert.True(c.Writer.TryWrite(i), $"Failed to write at {i}"); + Task r = await Task.WhenAny(remainingReaders); + await r; + remainingReaders.Remove(r); + } + + Assert.Equal((Items * (Items - 1)) / 2, Enumerable.Sum(await Task.WhenAll(readers.Select(r => r.AsTask())))); + } + + [Fact] + public async Task ReadAsync_AlreadyCompleted_Throws() + { + Channel c = CreateChannel(); + c.Writer.Complete(); + await Assert.ThrowsAsync(() => c.Reader.ReadAsync().AsTask()); + } + + [Fact] + public async Task ReadAsync_SubsequentlyCompleted_Throws() + { + Channel c = CreateChannel(); + Task r = c.Reader.ReadAsync().AsTask(); + Assert.False(r.IsCompleted); + c.Writer.Complete(); + await Assert.ThrowsAsync(() => r); + } + + [Fact] + public async Task ReadAsync_AfterFaultedChannel_Throws() + { + Channel c = CreateChannel(); + + var e = new FormatException(); + c.Writer.Complete(e); + Assert.True(c.Reader.Completion.IsFaulted); + + ChannelClosedException cce = await Assert.ThrowsAsync(() => c.Reader.ReadAsync().AsTask()); + Assert.Same(e, cce.InnerException); + } + + [Fact] + public async Task ReadAsync_AfterCanceledChannel_Throws() + { + Channel c = CreateChannel(); + + var e = new OperationCanceledException(); + c.Writer.Complete(e); + Assert.True(c.Reader.Completion.IsCanceled); + + await Assert.ThrowsAnyAsync(() => c.Reader.ReadAsync().AsTask()); + } + + [Fact] + public async Task ReadAsync_Canceled_WriteAsyncCompletesNextReader() + { + Channel c = CreateChannel(); + + for (int i = 0; i < 5; i++) + { + var cts = new CancellationTokenSource(); + ValueTask r = c.Reader.ReadAsync(cts.Token); + cts.Cancel(); + await AssertCanceled(r.AsTask(), cts.Token); + } + + for (int i = 0; i < 7; i++) + { + ValueTask r = c.Reader.ReadAsync(); + await c.Writer.WriteAsync(i); + Assert.Equal(i, await r); + } + } + } +} diff --git a/external/corefx/src/System.Threading.Channels/tests/ChannelTests.cs b/external/corefx/src/System.Threading.Channels/tests/ChannelTests.cs new file mode 100644 index 0000000000..26cf71448d --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/tests/ChannelTests.cs @@ -0,0 +1,210 @@ +// 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.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace System.Threading.Channels.Tests +{ + public class ChannelTests + { + [Fact] + public void ChannelOptimizations_Properties_Roundtrip() + { + var co = new UnboundedChannelOptions(); + + Assert.False(co.SingleReader); + Assert.False(co.SingleWriter); + + co.SingleReader = true; + Assert.True(co.SingleReader); + Assert.False(co.SingleWriter); + co.SingleReader = false; + Assert.False(co.SingleReader); + + co.SingleWriter = true; + Assert.False(co.SingleReader); + Assert.True(co.SingleWriter); + co.SingleWriter = false; + Assert.False(co.SingleWriter); + + co.SingleReader = true; + co.SingleWriter = true; + Assert.True(co.SingleReader); + Assert.True(co.SingleWriter); + + Assert.False(co.AllowSynchronousContinuations); + co.AllowSynchronousContinuations = true; + Assert.True(co.AllowSynchronousContinuations); + co.AllowSynchronousContinuations = false; + Assert.False(co.AllowSynchronousContinuations); + } + + [Fact] + public void Create_ValidInputs_ProducesValidChannels() + { + Assert.NotNull(Channel.CreateBounded(1)); + Assert.NotNull(Channel.CreateBounded(new BoundedChannelOptions(1))); + + Assert.NotNull(Channel.CreateUnbuffered()); + Assert.NotNull(Channel.CreateUnbuffered(new UnbufferedChannelOptions())); + + Assert.NotNull(Channel.CreateUnbounded()); + Assert.NotNull(Channel.CreateUnbounded(new UnboundedChannelOptions())); + } + + [Fact] + public void Create_NullOptions_ThrowsArgumentException() + { + AssertExtensions.Throws("options", () => Channel.CreateUnbounded(null)); + AssertExtensions.Throws("options", () => Channel.CreateUnbuffered(null)); + AssertExtensions.Throws("options", () => Channel.CreateBounded(null)); + } + + [Theory] + [InlineData(0)] + [InlineData(-2)] + public void CreateBounded_InvalidBufferSizes_ThrowArgumentExceptions(int capacity) + { + AssertExtensions.Throws("capacity", () => Channel.CreateBounded(capacity)); + AssertExtensions.Throws("capacity", () => new BoundedChannelOptions(capacity)); + } + + [Theory] + [InlineData((BoundedChannelFullMode)(-1))] + [InlineData((BoundedChannelFullMode)(4))] + public void BoundedChannelOptions_InvalidModes_ThrowArgumentExceptions(BoundedChannelFullMode mode) => + AssertExtensions.Throws("value", () => new BoundedChannelOptions(1) { FullMode = mode }); + + [Theory] + [InlineData(0)] + [InlineData(-2)] + public void BoundedChannelOptions_InvalidCapacity_ThrowArgumentExceptions(int capacity) => + AssertExtensions.Throws("value", () => new BoundedChannelOptions(1) { Capacity = capacity }); + + [Theory] + [InlineData(1)] + public void CreateBounded_ValidBufferSizes_Success(int bufferedCapacity) => + Assert.NotNull(Channel.CreateBounded(bufferedCapacity)); + + [Fact] + public async Task DefaultWriteAsync_UsesWaitToWriteAsyncAndTryWrite() + { + var c = new TestChannelWriter(10); + Assert.False(c.TryComplete()); + Assert.Equal(TaskStatus.Canceled, c.WriteAsync(42, new CancellationToken(true)).Status); + + int count = 0; + try + { + while (true) + { + await c.WriteAsync(count++); + } + } + catch (ChannelClosedException) { } + Assert.Equal(11, count); + } + + [Fact] + public void DefaultCompletion_NeverCompletes() + { + Task t = new TestChannelReader(Enumerable.Empty()).Completion; + Assert.False(t.IsCompleted); + } + + [Fact] + public async Task DefaultWriteAsync_CatchesTryWriteExceptions() + { + var w = new TryWriteThrowingWriter(); + Task t = w.WriteAsync(42); + Assert.Equal(TaskStatus.Faulted, t.Status); + await Assert.ThrowsAsync(() => t); + } + + [Fact] + public async Task DefaultReadAsync_CatchesTryWriteExceptions() + { + var r = new TryReadThrowingReader(); + Task t = r.ReadAsync().AsTask(); + Assert.Equal(TaskStatus.Faulted, t.Status); + await Assert.ThrowsAsync(() => t); + } + + private sealed class TestChannelWriter : ChannelWriter + { + private readonly Random _rand = new Random(42); + private readonly int _max; + private int _count; + + public TestChannelWriter(int max) => _max = max; + + public override bool TryWrite(T item) => _rand.Next(0, 2) == 0 && _count++ < _max; // succeed if we're under our limit, and add random failures + + public override Task WaitToWriteAsync(CancellationToken cancellationToken) => + _count >= _max ? Task.FromResult(false) : + _rand.Next(0, 2) == 0 ? Task.Delay(1).ContinueWith(_ => true) : // randomly introduce delays + Task.FromResult(true); + } + + private sealed class TestChannelReader : ChannelReader + { + private Random _rand = new Random(42); + private IEnumerator _enumerator; + private int _count; + private bool _closed; + + public TestChannelReader(IEnumerable enumerable) => _enumerator = enumerable.GetEnumerator(); + + public override bool TryRead(out T item) + { + // Randomly fail to read + if (_rand.Next(0, 2) == 0) + { + item = default; + return false; + } + + // If the enumerable is closed, fail the read. + if (!_enumerator.MoveNext()) + { + _enumerator.Dispose(); + _closed = true; + item = default; + return false; + } + + // Otherwise return the next item. + _count++; + item = _enumerator.Current; + return true; + } + + public override Task WaitToReadAsync(CancellationToken cancellationToken) => + _closed ? Task.FromResult(false) : + _rand.Next(0, 2) == 0 ? Task.Delay(1).ContinueWith(_ => true) : // randomly introduce delays + Task.FromResult(true); + } + + private sealed class TryWriteThrowingWriter : ChannelWriter + { + public override bool TryWrite(T item) => throw new FormatException(); + public override Task WaitToWriteAsync(CancellationToken cancellationToken = default) => throw new InvalidDataException(); + } + + private sealed class TryReadThrowingReader : ChannelReader + { + public override bool TryRead(out T item) => throw new FieldAccessException(); + public override Task WaitToReadAsync(CancellationToken cancellationToken = default) => throw new DriveNotFoundException(); + } + + private sealed class CanReadFalseStream : MemoryStream + { + public override bool CanRead => false; + } + } +} diff --git a/external/corefx/src/System.Threading.Channels/tests/Configurations.props b/external/corefx/src/System.Threading.Channels/tests/Configurations.props new file mode 100644 index 0000000000..78953dfc88 --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/tests/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + diff --git a/external/corefx/src/System.Threading.Channels/tests/Performance/Configurations.props b/external/corefx/src/System.Threading.Channels/tests/Performance/Configurations.props new file mode 100644 index 0000000000..2845c11c54 --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/tests/Performance/Configurations.props @@ -0,0 +1,8 @@ + + + + + netcoreapp; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Threading.Channels/tests/Performance/Perf.Channel.cs b/external/corefx/src/System.Threading.Channels/tests/Performance/Perf.Channel.cs new file mode 100644 index 0000000000..913054a244 --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/tests/Performance/Perf.Channel.cs @@ -0,0 +1,132 @@ +// 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.Threading.Tasks; +using Microsoft.Xunit.Performance; + +namespace System.Threading.Channels.Tests +{ + public sealed class Perf_UnboundedChannelTests : Perf_BufferingTests + { + public override Channel CreateChannel() => Channel.CreateUnbounded(); + } + + public sealed class Perf_UnboundedSpscChannelTests : Perf_BufferingTests + { + public override Channel CreateChannel() => Channel.CreateUnbounded(new UnboundedChannelOptions { SingleReader = true, SingleWriter = true }); + } + + public sealed class Perf_BoundedChannelTests : Perf_BufferingTests + { + public override Channel CreateChannel() => Channel.CreateBounded(10); + } + + public sealed class Perf_UnbufferedChannelTests : Perf_Tests + { + public override Channel CreateChannel() => Channel.CreateUnbuffered(); + } + + public abstract class Perf_BufferingTests : Perf_Tests + { + [Benchmark(InnerIterationCount = 1_000_000), MeasureGCAllocations] + public void TryWriteThenTryRead() + { + Channel channel = CreateChannel(); + ChannelReader reader = channel.Reader; + ChannelWriter writer = channel.Writer; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long iters = Benchmark.InnerIterationCount; + using (iteration.StartMeasurement()) + { + for (int i = 0; i < iters; i++) + { + writer.TryWrite(i); + reader.TryRead(out _); + } + } + } + } + + [Benchmark(InnerIterationCount = 1_000_000), MeasureGCAllocations] + public async Task WriteAsyncThenReadAsync() + { + Channel channel = CreateChannel(); + ChannelReader reader = channel.Reader; + ChannelWriter writer = channel.Writer; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long iters = Benchmark.InnerIterationCount; + using (iteration.StartMeasurement()) + { + for (int i = 0; i < iters; i++) + { + await writer.WriteAsync(i); + await reader.ReadAsync(); + } + } + } + } + + [Benchmark(InnerIterationCount = 1_000_000), MeasureGCAllocations] + public async Task ReadAsyncThenWriteAsync() + { + Channel channel = CreateChannel(); + ChannelReader reader = channel.Reader; + ChannelWriter writer = channel.Writer; + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long iters = Benchmark.InnerIterationCount; + using (iteration.StartMeasurement()) + { + for (int i = 0; i < iters; i++) + { + ValueTask r = reader.ReadAsync(); + await writer.WriteAsync(42); + await r; + } + } + } + } + } + + public abstract class Perf_Tests + { + public abstract Channel CreateChannel(); + + [Benchmark(InnerIterationCount = 1_000_000), MeasureGCAllocations] + public async Task ConcurrentReadAsyncWriteAsync() + { + Channel channel = CreateChannel(); + + foreach (BenchmarkIteration iteration in Benchmark.Iterations) + { + long iters = Benchmark.InnerIterationCount; + using (iteration.StartMeasurement()) + { + await Task.WhenAll( + Task.Run(async () => + { + ChannelReader reader = channel.Reader; + for (int i = 0; i < iters; i++) + { + await reader.ReadAsync(); + } + }), + Task.Run(async () => + { + ChannelWriter writer = channel.Writer; + for (int i = 0; i < iters; i++) + { + await writer.WriteAsync(i); + } + })); + } + } + } + } +} diff --git a/external/corefx/src/System.Threading.Channels/tests/Performance/System.Threading.Channels.Performance.Tests.csproj b/external/corefx/src/System.Threading.Channels/tests/Performance/System.Threading.Channels.Performance.Tests.csproj new file mode 100644 index 0000000000..28c6338fa5 --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/tests/Performance/System.Threading.Channels.Performance.Tests.csproj @@ -0,0 +1,24 @@ + + + + + true + {11ABE2F8-4FB9-48AC-91AA-D04503059550} + + + + + + + + Common\System\PerfUtils.cs + + + + + {69e46a6f-9966-45a5-8945-2559fe337827} + PerfRunner + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Threading.Channels/tests/System.Threading.Channels.Tests.csproj b/external/corefx/src/System.Threading.Channels/tests/System.Threading.Channels.Tests.csproj new file mode 100644 index 0000000000..33068d1346 --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/tests/System.Threading.Channels.Tests.csproj @@ -0,0 +1,22 @@ + + + + + {9E984EB2-827E-4029-9647-FB5F8B67C553} + + + + + + + + + + + + + Common\System\Diagnostics\DebuggerAttributes.cs + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Threading.Channels/tests/TestBase.cs b/external/corefx/src/System.Threading.Channels/tests/TestBase.cs new file mode 100644 index 0000000000..d3af1ba7eb --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/tests/TestBase.cs @@ -0,0 +1,48 @@ +// 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.Threading.Tasks; +using Xunit; + +#pragma warning disable 0649 // unused fields there for future testing needs + +namespace System.Threading.Channels.Tests +{ + public abstract class TestBase + { + protected void AssertSynchronouslyCanceled(Task task, CancellationToken token) + { + Assert.Equal(TaskStatus.Canceled, task.Status); + OperationCanceledException oce = Assert.ThrowsAny(() => task.GetAwaiter().GetResult()); + Assert.Equal(token, oce.CancellationToken); + } + + protected async Task AssertCanceled(Task task, CancellationToken token) + { + await Assert.ThrowsAnyAsync(() => task); + AssertSynchronouslyCanceled(task, token); + } + + protected void AssertSynchronousSuccess(Task task) => Assert.Equal(TaskStatus.RanToCompletion, task.Status); + + protected void AssertSynchronousTrue(Task task) + { + AssertSynchronousSuccess(task); + Assert.True(task.Result); + } + + internal sealed class DelegateObserver : IObserver + { + public Action OnNextDelegate = null; + public Action OnErrorDelegate = null; + public Action OnCompletedDelegate = null; + + void IObserver.OnNext(T value) => OnNextDelegate?.Invoke(value); + + void IObserver.OnError(Exception error) => OnErrorDelegate?.Invoke(error); + + void IObserver.OnCompleted() => OnCompletedDelegate?.Invoke(); + } + } +} diff --git a/external/corefx/src/System.Threading.Channels/tests/UnboundedChannelTests.cs b/external/corefx/src/System.Threading.Channels/tests/UnboundedChannelTests.cs new file mode 100644 index 0000000000..71d8e91f43 --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/tests/UnboundedChannelTests.cs @@ -0,0 +1,214 @@ +// 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.Diagnostics; +using System.Threading.Tasks; +using Xunit; + +namespace System.Threading.Channels.Tests +{ + public abstract class UnboundedChannelTests : ChannelTestBase + { + protected abstract bool AllowSynchronousContinuations { get; } + protected override Channel CreateChannel() => Channel.CreateUnbounded( + new UnboundedChannelOptions + { + SingleReader = RequiresSingleReader, + AllowSynchronousContinuations = AllowSynchronousContinuations + }); + protected override Channel CreateFullChannel() => null; + + [Fact] + public async Task Complete_BeforeEmpty_NoWaiters_TriggersCompletion() + { + Channel c = CreateChannel(); + Assert.True(c.Writer.TryWrite(42)); + c.Writer.Complete(); + Assert.False(c.Reader.Completion.IsCompleted); + Assert.Equal(42, await c.Reader.ReadAsync()); + await c.Reader.Completion; + } + + [Fact] + public void TryWrite_TryRead_Many() + { + Channel c = CreateChannel(); + + const int NumItems = 100000; + for (int i = 0; i < NumItems; i++) + { + Assert.True(c.Writer.TryWrite(i)); + } + for (int i = 0; i < NumItems; i++) + { + Assert.True(c.Reader.TryRead(out int result)); + Assert.Equal(i, result); + } + } + + [Fact] + public void TryWrite_TryRead_OneAtATime() + { + Channel c = CreateChannel(); + + for (int i = 0; i < 10; i++) + { + Assert.True(c.Writer.TryWrite(i)); + Assert.True(c.Reader.TryRead(out int result)); + Assert.Equal(i, result); + } + } + + [Fact] + public void WaitForReadAsync_DataAvailable_CompletesSynchronously() + { + Channel c = CreateChannel(); + Assert.True(c.Writer.TryWrite(42)); + AssertSynchronousTrue(c.Reader.WaitToReadAsync()); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + public async Task WriteMany_ThenComplete_SuccessfullyReadAll(int readMode) + { + Channel c = CreateChannel(); + for (int i = 0; i < 10; i++) + { + Assert.True(c.Writer.TryWrite(i)); + } + + c.Writer.Complete(); + Assert.False(c.Reader.Completion.IsCompleted); + + for (int i = 0; i < 10; i++) + { + Assert.False(c.Reader.Completion.IsCompleted); + switch (readMode) + { + case 0: + int result; + Assert.True(c.Reader.TryRead(out result)); + Assert.Equal(i, result); + break; + case 1: + Assert.Equal(i, await c.Reader.ReadAsync()); + break; + } + } + + await c.Reader.Completion; + } + + [Fact] + public void AllowSynchronousContinuations_WaitToReadAsync_ContinuationsInvokedAccordingToSetting() + { + Channel c = CreateChannel(); + + int expectedId = Environment.CurrentManagedThreadId; + Task r = c.Reader.WaitToReadAsync().ContinueWith(_ => + { + Assert.Equal(AllowSynchronousContinuations, expectedId == Environment.CurrentManagedThreadId); + }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); + + Assert.Equal(TaskStatus.RanToCompletion, c.Writer.WriteAsync(42).Status); + ((IAsyncResult)r).AsyncWaitHandle.WaitOne(); // avoid inlining the continuation + r.GetAwaiter().GetResult(); + } + + [Fact] + public void AllowSynchronousContinuations_CompletionTask_ContinuationsInvokedAccordingToSetting() + { + Channel c = CreateChannel(); + + int expectedId = Environment.CurrentManagedThreadId; + Task r = c.Reader.Completion.ContinueWith(_ => + { + Assert.Equal(AllowSynchronousContinuations, expectedId == Environment.CurrentManagedThreadId); + }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); + + Assert.True(c.Writer.TryComplete()); + ((IAsyncResult)r).AsyncWaitHandle.WaitOne(); // avoid inlining the continuation + r.GetAwaiter().GetResult(); + } + } + + public abstract class SingleReaderUnboundedChannelTests : UnboundedChannelTests + { + protected override bool RequiresSingleReader => true; + + [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot, "Requires internal reflection on framework types.")] + [Fact] + public void ValidateInternalDebuggerAttributes() + { + Channel c = CreateChannel(); + Assert.True(c.Writer.TryWrite(1)); + Assert.True(c.Writer.TryWrite(2)); + + object queue = DebuggerAttributes.GetFieldValue(c, "_items"); + DebuggerAttributes.ValidateDebuggerDisplayReferences(queue); + DebuggerAttributes.InvokeDebuggerTypeProxyProperties(queue); + } + + [Fact] + public async Task MultipleWaiters_CancelsPreviousWaiter() + { + Channel c = CreateChannel(); + Task t1 = c.Reader.WaitToReadAsync(); + Task t2 = c.Reader.WaitToReadAsync(); + await Assert.ThrowsAnyAsync(() => t1); + Assert.True(c.Writer.TryWrite(42)); + Assert.True(await t2); + } + + [Fact] + public void Stress_TryWrite_TryRead() + { + const int NumItems = 3000000; + Channel c = CreateChannel(); + + Task.WaitAll( + Task.Run(async () => + { + int received = 0; + while (await c.Reader.WaitToReadAsync()) + { + while (c.Reader.TryRead(out int i)) + { + Assert.Equal(received, i); + received++; + } + } + }), + Task.Run(() => + { + for (int i = 0; i < NumItems; i++) + { + Assert.True(c.Writer.TryWrite(i)); + } + c.Writer.Complete(); + })); + } + } + + public sealed class SyncMultiReaderUnboundedChannelTests : UnboundedChannelTests + { + protected override bool AllowSynchronousContinuations => true; + } + + public sealed class AsyncMultiReaderUnboundedChannelTests : UnboundedChannelTests + { + protected override bool AllowSynchronousContinuations => false; + } + + public sealed class SyncSingleReaderUnboundedChannelTests : SingleReaderUnboundedChannelTests + { + protected override bool AllowSynchronousContinuations => true; + } + + public sealed class AsyncSingleReaderUnboundedChannelTests : SingleReaderUnboundedChannelTests + { + protected override bool AllowSynchronousContinuations => false; + } +} diff --git a/external/corefx/src/System.Threading.Channels/tests/UnbufferedChannelTests.cs b/external/corefx/src/System.Threading.Channels/tests/UnbufferedChannelTests.cs new file mode 100644 index 0000000000..7ef42056ac --- /dev/null +++ b/external/corefx/src/System.Threading.Channels/tests/UnbufferedChannelTests.cs @@ -0,0 +1,95 @@ +// 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.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace System.Threading.Channels.Tests +{ + public class UnbufferedChannelTests : ChannelTestBase + { + protected override Channel CreateChannel() => Channel.CreateUnbuffered(); + protected override Channel CreateFullChannel() => CreateChannel(); + protected override bool BuffersItems => false; + + [Fact] + public async Task Complete_BeforeEmpty_WaitingWriters_TriggersCompletion() + { + Channel c = CreateChannel(); + Task write1 = c.Writer.WriteAsync(42); + Task write2 = c.Writer.WriteAsync(43); + c.Writer.Complete(); + await c.Reader.Completion; + await Assert.ThrowsAnyAsync(() => write1); + await Assert.ThrowsAnyAsync(() => write2); + } + + [Fact] + public void TryReadWrite_NoPartner_Fail() + { + Channel c = CreateChannel(); + Assert.False(c.Writer.TryWrite(42)); + Assert.False(c.Reader.TryRead(out int result)); + Assert.Equal(result, 0); + } + + [Fact] + public void TryRead_WriteAsync_Success() + { + Channel c = CreateChannel(); + Task w = c.Writer.WriteAsync(42); + Assert.False(w.IsCompleted); + Assert.True(c.Reader.TryRead(out int result)); + Assert.Equal(42, result); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task Read_MultipleUnpartneredWrites_CancelSome_ReadSucceeds(bool useReadAsync) + { + Channel c = CreateChannel(); + var cts = new CancellationTokenSource(); + + Task[] cancelableWrites = (from i in Enumerable.Range(0, 10) select c.Writer.WriteAsync(42, cts.Token)).ToArray(); + Assert.All(cancelableWrites, cw => Assert.Equal(TaskStatus.WaitingForActivation, cw.Status)); + + Task w = c.Writer.WriteAsync(84); + + cts.Cancel(); + foreach (Task t in cancelableWrites) + { + await AssertCanceled(t, cts.Token); + } + + if (useReadAsync) + { + Assert.True(c.Reader.TryRead(out int result)); + Assert.Equal(84, result); + } + else + { + Assert.Equal(84, await c.Reader.ReadAsync()); + } + } + + [Fact] + public async Task Cancel_PartneredWrite_Success() + { + Channel c = CreateChannel(); + var cts = new CancellationTokenSource(); + + Task w = c.Writer.WriteAsync(42, cts.Token); + Assert.False(w.IsCompleted); + + ValueTask r = c.Reader.ReadAsync(); + Assert.True(r.IsCompletedSuccessfully); + + cts.Cancel(); + await w; // no throw + } + + } +} diff --git a/external/corefx/src/System.Threading.Overlapped/ref/System.Threading.Overlapped.cs b/external/corefx/src/System.Threading.Overlapped/ref/System.Threading.Overlapped.cs index 37728114f1..2db5dcc9dd 100644 --- a/external/corefx/src/System.Threading.Overlapped/ref/System.Threading.Overlapped.cs +++ b/external/corefx/src/System.Threading.Overlapped/ref/System.Threading.Overlapped.cs @@ -9,9 +9,7 @@ namespace System.Threading { [System.CLSCompliantAttribute(false)] - [System.Security.SecurityCriticalAttribute] public unsafe delegate void IOCompletionCallback(uint errorCode, uint numBytes, System.Threading.NativeOverlapped* pOVERLAP); - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct NativeOverlapped { public System.IntPtr EventHandle; diff --git a/external/corefx/src/System.Threading.Overlapped/src/ApiCompatBaseline.uapaot.txt b/external/corefx/src/System.Threading.Overlapped/src/ApiCompatBaseline.uapaot.txt deleted file mode 100644 index aaa695df4f..0000000000 --- a/external/corefx/src/System.Threading.Overlapped/src/ApiCompatBaseline.uapaot.txt +++ /dev/null @@ -1,3 +0,0 @@ -Compat issues with assembly System.Threading.Overlapped: -TypesMustExist : Type 'System.Threading.Overlapped' does not exist in the implementation but it does exist in the contract. -Total Issues: 1 diff --git a/external/corefx/src/System.Threading.Overlapped/tests/System.Threading.Overlapped.Tests.csproj b/external/corefx/src/System.Threading.Overlapped/tests/System.Threading.Overlapped.Tests.csproj index 419323ec2a..38ac885f00 100644 --- a/external/corefx/src/System.Threading.Overlapped/tests/System.Threading.Overlapped.Tests.csproj +++ b/external/corefx/src/System.Threading.Overlapped/tests/System.Threading.Overlapped.Tests.csproj @@ -6,7 +6,6 @@ true $(DefineConstants);uap - @@ -28,4 +27,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Threading.Tasks.Dataflow/pkg/System.Threading.Tasks.Dataflow.pkgproj b/external/corefx/src/System.Threading.Tasks.Dataflow/pkg/System.Threading.Tasks.Dataflow.pkgproj index 2c1ebf5fd4..e62f1b9c18 100644 --- a/external/corefx/src/System.Threading.Tasks.Dataflow/pkg/System.Threading.Tasks.Dataflow.pkgproj +++ b/external/corefx/src/System.Threading.Tasks.Dataflow/pkg/System.Threading.Tasks.Dataflow.pkgproj @@ -12,12 +12,20 @@ .NETCoreApp;UAP + + + + net45 + \ No newline at end of file diff --git a/external/corefx/src/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.cs b/external/corefx/src/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.cs index 9e4e020e1e..244dddf0d8 100644 --- a/external/corefx/src/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.cs +++ b/external/corefx/src/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.cs @@ -31,14 +31,14 @@ namespace System.Threading.Tasks.Dataflow public void Complete() { } public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock target, System.Threading.Tasks.Dataflow.DataflowLinkOptions linkOptions) { throw null; } void System.Threading.Tasks.Dataflow.IDataflowBlock.Fault(System.Exception exception) { } - T[] System.Threading.Tasks.Dataflow.ISourceBlock.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target, out bool messageConsumed) { messageConsumed = default(bool); throw null; } + T[] System.Threading.Tasks.Dataflow.ISourceBlock.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target, out bool messageConsumed) { throw null; } void System.Threading.Tasks.Dataflow.ISourceBlock.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { } bool System.Threading.Tasks.Dataflow.ISourceBlock.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { throw null; } System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, T messageValue, System.Threading.Tasks.Dataflow.ISourceBlock source, bool consumeToAccept) { throw null; } public override string ToString() { throw null; } public void TriggerBatch() { } - public bool TryReceive(System.Predicate filter, out T[] item) { item = default(T[]); throw null; } - public bool TryReceiveAll(out System.Collections.Generic.IList items) { items = default(System.Collections.Generic.IList); throw null; } + public bool TryReceive(System.Predicate filter, out T[] item) { throw null; } + public bool TryReceiveAll(out System.Collections.Generic.IList items) { throw null; } } public sealed partial class BatchedJoinBlock : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock, System.Collections.Generic.IList>>, System.Threading.Tasks.Dataflow.ISourceBlock, System.Collections.Generic.IList>> { @@ -52,12 +52,12 @@ namespace System.Threading.Tasks.Dataflow public void Complete() { } public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock, System.Collections.Generic.IList>> target, System.Threading.Tasks.Dataflow.DataflowLinkOptions linkOptions) { throw null; } void System.Threading.Tasks.Dataflow.IDataflowBlock.Fault(System.Exception exception) { } - System.Tuple, System.Collections.Generic.IList> System.Threading.Tasks.Dataflow.ISourceBlock,System.Collections.Generic.IList>>.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock, System.Collections.Generic.IList>> target, out bool messageConsumed) { messageConsumed = default(bool); throw null; } + System.Tuple, System.Collections.Generic.IList> System.Threading.Tasks.Dataflow.ISourceBlock,System.Collections.Generic.IList>>.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock, System.Collections.Generic.IList>> target, out bool messageConsumed) { throw null; } void System.Threading.Tasks.Dataflow.ISourceBlock,System.Collections.Generic.IList>>.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock, System.Collections.Generic.IList>> target) { } bool System.Threading.Tasks.Dataflow.ISourceBlock,System.Collections.Generic.IList>>.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock, System.Collections.Generic.IList>> target) { throw null; } public override string ToString() { throw null; } - public bool TryReceive(System.Predicate, System.Collections.Generic.IList>> filter, out System.Tuple, System.Collections.Generic.IList> item) { item = default(System.Tuple, System.Collections.Generic.IList>); throw null; } - public bool TryReceiveAll(out System.Collections.Generic.IList, System.Collections.Generic.IList>> items) { items = default(System.Collections.Generic.IList, System.Collections.Generic.IList>>); throw null; } + public bool TryReceive(System.Predicate, System.Collections.Generic.IList>> filter, out System.Tuple, System.Collections.Generic.IList> item) { throw null; } + public bool TryReceiveAll(out System.Collections.Generic.IList, System.Collections.Generic.IList>> items) { throw null; } } public sealed partial class BatchedJoinBlock : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock, System.Collections.Generic.IList, System.Collections.Generic.IList>>, System.Threading.Tasks.Dataflow.ISourceBlock, System.Collections.Generic.IList, System.Collections.Generic.IList>> { @@ -72,12 +72,12 @@ namespace System.Threading.Tasks.Dataflow public void Complete() { } public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock, System.Collections.Generic.IList, System.Collections.Generic.IList>> target, System.Threading.Tasks.Dataflow.DataflowLinkOptions linkOptions) { throw null; } void System.Threading.Tasks.Dataflow.IDataflowBlock.Fault(System.Exception exception) { } - System.Tuple, System.Collections.Generic.IList, System.Collections.Generic.IList> System.Threading.Tasks.Dataflow.ISourceBlock,System.Collections.Generic.IList,System.Collections.Generic.IList>>.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock, System.Collections.Generic.IList, System.Collections.Generic.IList>> target, out bool messageConsumed) { messageConsumed = default(bool); throw null; } + System.Tuple, System.Collections.Generic.IList, System.Collections.Generic.IList> System.Threading.Tasks.Dataflow.ISourceBlock,System.Collections.Generic.IList,System.Collections.Generic.IList>>.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock, System.Collections.Generic.IList, System.Collections.Generic.IList>> target, out bool messageConsumed) { throw null; } void System.Threading.Tasks.Dataflow.ISourceBlock,System.Collections.Generic.IList,System.Collections.Generic.IList>>.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock, System.Collections.Generic.IList, System.Collections.Generic.IList>> target) { } bool System.Threading.Tasks.Dataflow.ISourceBlock,System.Collections.Generic.IList,System.Collections.Generic.IList>>.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock, System.Collections.Generic.IList, System.Collections.Generic.IList>> target) { throw null; } public override string ToString() { throw null; } - public bool TryReceive(System.Predicate, System.Collections.Generic.IList, System.Collections.Generic.IList>> filter, out System.Tuple, System.Collections.Generic.IList, System.Collections.Generic.IList> item) { item = default(System.Tuple, System.Collections.Generic.IList, System.Collections.Generic.IList>); throw null; } - public bool TryReceiveAll(out System.Collections.Generic.IList, System.Collections.Generic.IList, System.Collections.Generic.IList>> items) { items = default(System.Collections.Generic.IList, System.Collections.Generic.IList, System.Collections.Generic.IList>>); throw null; } + public bool TryReceive(System.Predicate, System.Collections.Generic.IList, System.Collections.Generic.IList>> filter, out System.Tuple, System.Collections.Generic.IList, System.Collections.Generic.IList> item) { throw null; } + public bool TryReceiveAll(out System.Collections.Generic.IList, System.Collections.Generic.IList, System.Collections.Generic.IList>> items) { throw null; } } public sealed partial class BroadcastBlock : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IPropagatorBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock, System.Threading.Tasks.Dataflow.ISourceBlock, System.Threading.Tasks.Dataflow.ITargetBlock { @@ -87,13 +87,13 @@ namespace System.Threading.Tasks.Dataflow public void Complete() { } public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock target, System.Threading.Tasks.Dataflow.DataflowLinkOptions linkOptions) { throw null; } void System.Threading.Tasks.Dataflow.IDataflowBlock.Fault(System.Exception exception) { } - bool System.Threading.Tasks.Dataflow.IReceivableSourceBlock.TryReceiveAll(out System.Collections.Generic.IList items) { items = default(System.Collections.Generic.IList); throw null; } - T System.Threading.Tasks.Dataflow.ISourceBlock.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target, out bool messageConsumed) { messageConsumed = default(bool); throw null; } + bool System.Threading.Tasks.Dataflow.IReceivableSourceBlock.TryReceiveAll(out System.Collections.Generic.IList items) { throw null; } + T System.Threading.Tasks.Dataflow.ISourceBlock.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target, out bool messageConsumed) { throw null; } void System.Threading.Tasks.Dataflow.ISourceBlock.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { } bool System.Threading.Tasks.Dataflow.ISourceBlock.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { throw null; } System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, T messageValue, System.Threading.Tasks.Dataflow.ISourceBlock source, bool consumeToAccept) { throw null; } public override string ToString() { throw null; } - public bool TryReceive(System.Predicate filter, out T item) { item = default(T); throw null; } + public bool TryReceive(System.Predicate filter, out T item) { throw null; } } public sealed partial class BufferBlock : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IPropagatorBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock, System.Threading.Tasks.Dataflow.ISourceBlock, System.Threading.Tasks.Dataflow.ITargetBlock { @@ -104,13 +104,13 @@ namespace System.Threading.Tasks.Dataflow public void Complete() { } public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock target, System.Threading.Tasks.Dataflow.DataflowLinkOptions linkOptions) { throw null; } void System.Threading.Tasks.Dataflow.IDataflowBlock.Fault(System.Exception exception) { } - T System.Threading.Tasks.Dataflow.ISourceBlock.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target, out bool messageConsumed) { messageConsumed = default(bool); throw null; } + T System.Threading.Tasks.Dataflow.ISourceBlock.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target, out bool messageConsumed) { throw null; } void System.Threading.Tasks.Dataflow.ISourceBlock.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { } bool System.Threading.Tasks.Dataflow.ISourceBlock.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { throw null; } System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, T messageValue, System.Threading.Tasks.Dataflow.ISourceBlock source, bool consumeToAccept) { throw null; } public override string ToString() { throw null; } - public bool TryReceive(System.Predicate filter, out T item) { item = default(T); throw null; } - public bool TryReceiveAll(out System.Collections.Generic.IList items) { items = default(System.Collections.Generic.IList); throw null; } + public bool TryReceive(System.Predicate filter, out T item) { throw null; } + public bool TryReceiveAll(out System.Collections.Generic.IList items) { throw null; } } public static partial class DataflowBlock { @@ -138,7 +138,7 @@ namespace System.Threading.Tasks.Dataflow public static TOutput Receive(this System.Threading.Tasks.Dataflow.ISourceBlock source, System.TimeSpan timeout, System.Threading.CancellationToken cancellationToken) { throw null; } public static System.Threading.Tasks.Task SendAsync(this System.Threading.Tasks.Dataflow.ITargetBlock target, TInput item) { throw null; } public static System.Threading.Tasks.Task SendAsync(this System.Threading.Tasks.Dataflow.ITargetBlock target, TInput item, System.Threading.CancellationToken cancellationToken) { throw null; } - public static bool TryReceive(this System.Threading.Tasks.Dataflow.IReceivableSourceBlock source, out TOutput item) { item = default(TOutput); throw null; } + public static bool TryReceive(this System.Threading.Tasks.Dataflow.IReceivableSourceBlock source, out TOutput item) { throw null; } } public partial class DataflowBlockOptions { @@ -158,10 +158,10 @@ namespace System.Threading.Tasks.Dataflow public int MaxMessages { get { throw null; } set { } } public bool PropagateCompletion { get { throw null; } set { } } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] - public partial struct DataflowMessageHeader : System.IEquatable + public readonly partial struct DataflowMessageHeader : System.IEquatable { - public DataflowMessageHeader(long id) { throw null;} + private readonly int _dummy; + public DataflowMessageHeader(long id) { throw null; } public long Id { get { throw null; } } public bool IsValid { get { throw null; } } public override bool Equals(object obj) { throw null; } @@ -226,12 +226,12 @@ namespace System.Threading.Tasks.Dataflow public void Complete() { } public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock> target, System.Threading.Tasks.Dataflow.DataflowLinkOptions linkOptions) { throw null; } void System.Threading.Tasks.Dataflow.IDataflowBlock.Fault(System.Exception exception) { } - System.Tuple System.Threading.Tasks.Dataflow.ISourceBlock>.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock> target, out bool messageConsumed) { messageConsumed = default(bool); throw null; } + System.Tuple System.Threading.Tasks.Dataflow.ISourceBlock>.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock> target, out bool messageConsumed) { throw null; } void System.Threading.Tasks.Dataflow.ISourceBlock>.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock> target) { } bool System.Threading.Tasks.Dataflow.ISourceBlock>.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock> target) { throw null; } public override string ToString() { throw null; } - public bool TryReceive(System.Predicate> filter, out System.Tuple item) { item = default(System.Tuple); throw null; } - public bool TryReceiveAll(out System.Collections.Generic.IList> items) { items = default(System.Collections.Generic.IList>); throw null; } + public bool TryReceive(System.Predicate> filter, out System.Tuple item) { throw null; } + public bool TryReceiveAll(out System.Collections.Generic.IList> items) { throw null; } } public sealed partial class JoinBlock : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock>, System.Threading.Tasks.Dataflow.ISourceBlock> { @@ -245,12 +245,12 @@ namespace System.Threading.Tasks.Dataflow public void Complete() { } public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock> target, System.Threading.Tasks.Dataflow.DataflowLinkOptions linkOptions) { throw null; } void System.Threading.Tasks.Dataflow.IDataflowBlock.Fault(System.Exception exception) { } - System.Tuple System.Threading.Tasks.Dataflow.ISourceBlock>.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock> target, out bool messageConsumed) { messageConsumed = default(bool); throw null; } + System.Tuple System.Threading.Tasks.Dataflow.ISourceBlock>.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock> target, out bool messageConsumed) { throw null; } void System.Threading.Tasks.Dataflow.ISourceBlock>.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock> target) { } bool System.Threading.Tasks.Dataflow.ISourceBlock>.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock> target) { throw null; } public override string ToString() { throw null; } - public bool TryReceive(System.Predicate> filter, out System.Tuple item) { item = default(System.Tuple); throw null; } - public bool TryReceiveAll(out System.Collections.Generic.IList> items) { items = default(System.Collections.Generic.IList>); throw null; } + public bool TryReceive(System.Predicate> filter, out System.Tuple item) { throw null; } + public bool TryReceiveAll(out System.Collections.Generic.IList> items) { throw null; } } public sealed partial class TransformBlock : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IPropagatorBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock, System.Threading.Tasks.Dataflow.ISourceBlock, System.Threading.Tasks.Dataflow.ITargetBlock { @@ -264,13 +264,13 @@ namespace System.Threading.Tasks.Dataflow public void Complete() { } public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock target, System.Threading.Tasks.Dataflow.DataflowLinkOptions linkOptions) { throw null; } void System.Threading.Tasks.Dataflow.IDataflowBlock.Fault(System.Exception exception) { } - TOutput System.Threading.Tasks.Dataflow.ISourceBlock.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target, out bool messageConsumed) { messageConsumed = default(bool); throw null; } + TOutput System.Threading.Tasks.Dataflow.ISourceBlock.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target, out bool messageConsumed) { throw null; } void System.Threading.Tasks.Dataflow.ISourceBlock.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { } bool System.Threading.Tasks.Dataflow.ISourceBlock.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { throw null; } System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, TInput messageValue, System.Threading.Tasks.Dataflow.ISourceBlock source, bool consumeToAccept) { throw null; } public override string ToString() { throw null; } - public bool TryReceive(System.Predicate filter, out TOutput item) { item = default(TOutput); throw null; } - public bool TryReceiveAll(out System.Collections.Generic.IList items) { items = default(System.Collections.Generic.IList); throw null; } + public bool TryReceive(System.Predicate filter, out TOutput item) { throw null; } + public bool TryReceiveAll(out System.Collections.Generic.IList items) { throw null; } } public sealed partial class TransformManyBlock : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IPropagatorBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock, System.Threading.Tasks.Dataflow.ISourceBlock, System.Threading.Tasks.Dataflow.ITargetBlock { @@ -284,13 +284,13 @@ namespace System.Threading.Tasks.Dataflow public void Complete() { } public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock target, System.Threading.Tasks.Dataflow.DataflowLinkOptions linkOptions) { throw null; } void System.Threading.Tasks.Dataflow.IDataflowBlock.Fault(System.Exception exception) { } - TOutput System.Threading.Tasks.Dataflow.ISourceBlock.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target, out bool messageConsumed) { messageConsumed = default(bool); throw null; } + TOutput System.Threading.Tasks.Dataflow.ISourceBlock.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target, out bool messageConsumed) { throw null; } void System.Threading.Tasks.Dataflow.ISourceBlock.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { } bool System.Threading.Tasks.Dataflow.ISourceBlock.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { throw null; } System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, TInput messageValue, System.Threading.Tasks.Dataflow.ISourceBlock source, bool consumeToAccept) { throw null; } public override string ToString() { throw null; } - public bool TryReceive(System.Predicate filter, out TOutput item) { item = default(TOutput); throw null; } - public bool TryReceiveAll(out System.Collections.Generic.IList items) { items = default(System.Collections.Generic.IList); throw null; } + public bool TryReceive(System.Predicate filter, out TOutput item) { throw null; } + public bool TryReceiveAll(out System.Collections.Generic.IList items) { throw null; } } public sealed partial class WriteOnceBlock : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IPropagatorBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock, System.Threading.Tasks.Dataflow.ISourceBlock, System.Threading.Tasks.Dataflow.ITargetBlock { @@ -300,12 +300,12 @@ namespace System.Threading.Tasks.Dataflow public void Complete() { } public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock target, System.Threading.Tasks.Dataflow.DataflowLinkOptions linkOptions) { throw null; } void System.Threading.Tasks.Dataflow.IDataflowBlock.Fault(System.Exception exception) { } - bool System.Threading.Tasks.Dataflow.IReceivableSourceBlock.TryReceiveAll(out System.Collections.Generic.IList items) { items = default(System.Collections.Generic.IList); throw null; } - T System.Threading.Tasks.Dataflow.ISourceBlock.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target, out bool messageConsumed) { messageConsumed = default(bool); throw null; } + bool System.Threading.Tasks.Dataflow.IReceivableSourceBlock.TryReceiveAll(out System.Collections.Generic.IList items) { throw null; } + T System.Threading.Tasks.Dataflow.ISourceBlock.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target, out bool messageConsumed) { throw null; } void System.Threading.Tasks.Dataflow.ISourceBlock.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { } bool System.Threading.Tasks.Dataflow.ISourceBlock.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { throw null; } System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, T messageValue, System.Threading.Tasks.Dataflow.ISourceBlock source, bool consumeToAccept) { throw null; } public override string ToString() { throw null; } - public bool TryReceive(System.Predicate filter, out T item) { item = default(T); throw null; } + public bool TryReceive(System.Predicate filter, out T item) { throw null; } } } diff --git a/external/corefx/src/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.csproj b/external/corefx/src/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.csproj index 2460e198fc..022c6afb5c 100644 --- a/external/corefx/src/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.csproj +++ b/external/corefx/src/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.csproj @@ -4,7 +4,6 @@ {01F36E3F-7664-4A71-AE6E-B8B6BF724FD1} - @@ -19,4 +18,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Threading.Tasks.Dataflow/src/Base/DataflowBlock.cs.REMOVED.git-id b/external/corefx/src/System.Threading.Tasks.Dataflow/src/Base/DataflowBlock.cs.REMOVED.git-id index 0b19655f54..3f7e63b633 100644 --- a/external/corefx/src/System.Threading.Tasks.Dataflow/src/Base/DataflowBlock.cs.REMOVED.git-id +++ b/external/corefx/src/System.Threading.Tasks.Dataflow/src/Base/DataflowBlock.cs.REMOVED.git-id @@ -1 +1 @@ -447df5f92108c4ddaad48588502be4ecaa29256c \ No newline at end of file +4ccd0eb7052b2beff9c98716e86c4227f60e6446 \ No newline at end of file diff --git a/external/corefx/src/System.Threading.Tasks.Dataflow/src/Base/DataflowMessageHeader.cs b/external/corefx/src/System.Threading.Tasks.Dataflow/src/Base/DataflowMessageHeader.cs index 983816dde8..8b2c3be09e 100644 --- a/external/corefx/src/System.Threading.Tasks.Dataflow/src/Base/DataflowMessageHeader.cs +++ b/external/corefx/src/System.Threading.Tasks.Dataflow/src/Base/DataflowMessageHeader.cs @@ -19,7 +19,7 @@ namespace System.Threading.Tasks.Dataflow { /// Provides a container of data attributes for passing between dataflow blocks. [DebuggerDisplay("Id = {Id}")] - public struct DataflowMessageHeader : IEquatable + public readonly struct DataflowMessageHeader : IEquatable { /// The message ID. Needs to be unique within the source. private readonly long _id; diff --git a/external/corefx/src/System.Threading.Tasks.Dataflow/src/Internal/ImmutableArray.cs b/external/corefx/src/System.Threading.Tasks.Dataflow/src/Internal/ImmutableArray.cs index f9213e5b88..96336e55ab 100644 --- a/external/corefx/src/System.Threading.Tasks.Dataflow/src/Internal/ImmutableArray.cs +++ b/external/corefx/src/System.Threading.Tasks.Dataflow/src/Internal/ImmutableArray.cs @@ -23,7 +23,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// Provides a simple, immutable array. /// Specifies the type of the data stored in the array. [DebuggerDisplay("Count={Count}")] - internal struct ImmutableArray + internal readonly struct ImmutableArray { /// An empty array. private static readonly ImmutableArray s_empty = new ImmutableArray(new T[0]); diff --git a/external/corefx/src/System.Threading.Tasks.Dataflow/src/Resources/Strings.resx b/external/corefx/src/System.Threading.Tasks.Dataflow/src/Resources/Strings.resx index 102c5a1376..934b767297 100644 --- a/external/corefx/src/System.Threading.Tasks.Dataflow/src/Resources/Strings.resx +++ b/external/corefx/src/System.Threading.Tasks.Dataflow/src/Resources/Strings.resx @@ -97,4 +97,7 @@ The SyncRoot property may not be used for the synchronization of concurrent collections. + + An error occurred while linking up the target or during the cleanup after completion. + \ No newline at end of file diff --git a/external/corefx/src/System.Threading.Tasks.Dataflow/src/System.Threading.Tasks.Dataflow.csproj b/external/corefx/src/System.Threading.Tasks.Dataflow/src/System.Threading.Tasks.Dataflow.csproj index 7bea7b1efe..8ed026f455 100644 --- a/external/corefx/src/System.Threading.Tasks.Dataflow/src/System.Threading.Tasks.Dataflow.csproj +++ b/external/corefx/src/System.Threading.Tasks.Dataflow/src/System.Threading.Tasks.Dataflow.csproj @@ -12,7 +12,6 @@ false - @@ -86,4 +85,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Threading.Tasks.Extensions/dir.props b/external/corefx/src/System.Threading.Tasks.Extensions/dir.props index 5ad1614920..69547aae1d 100644 --- a/external/corefx/src/System.Threading.Tasks.Extensions/dir.props +++ b/external/corefx/src/System.Threading.Tasks.Extensions/dir.props @@ -6,6 +6,5 @@ Open true true - false \ No newline at end of file diff --git a/external/corefx/src/System.Threading.Tasks.Extensions/pkg/System.Threading.Tasks.Extensions.pkgproj b/external/corefx/src/System.Threading.Tasks.Extensions/pkg/System.Threading.Tasks.Extensions.pkgproj index 1020a4bb8b..be069b8134 100644 --- a/external/corefx/src/System.Threading.Tasks.Extensions/pkg/System.Threading.Tasks.Extensions.pkgproj +++ b/external/corefx/src/System.Threading.Tasks.Extensions/pkg/System.Threading.Tasks.Extensions.pkgproj @@ -6,18 +6,13 @@ 2.8.6 - + net45;netcore45;netcoreapp1.0;wpa81;wp8;$(AllXamarinFrameworks) + - - - .NETCoreApp;UAP - + + diff --git a/external/corefx/src/System.Threading.Tasks.Extensions/ref/System.Threading.Tasks.Extensions.cs b/external/corefx/src/System.Threading.Tasks.Extensions/ref/System.Threading.Tasks.Extensions.cs index c461cbe952..2b8bd30e91 100644 --- a/external/corefx/src/System.Threading.Tasks.Extensions/ref/System.Threading.Tasks.Extensions.cs +++ b/external/corefx/src/System.Threading.Tasks.Extensions/ref/System.Threading.Tasks.Extensions.cs @@ -15,9 +15,9 @@ namespace System.Runtime.CompilerServices } public partial struct AsyncValueTaskMethodBuilder { + private TResult _result; public System.Threading.Tasks.ValueTask Task { get { throw null; } } public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.INotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } - [System.Security.SecuritySafeCriticalAttribute] public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } public static System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder Create() { throw null; } public void SetException(System.Exception exception) { } @@ -25,20 +25,22 @@ namespace System.Runtime.CompilerServices public void SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine stateMachine) { } public void Start(ref TStateMachine stateMachine) where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } } - public partial struct ConfiguredValueTaskAwaitable + public readonly partial struct ConfiguredValueTaskAwaitable { + private readonly object _dummy; public System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable.ConfiguredValueTaskAwaiter GetAwaiter() { throw null; } public partial struct ConfiguredValueTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion { + private object _dummy; public bool IsCompleted { get { throw null; } } public TResult GetResult() { throw null; } public void OnCompleted(System.Action continuation) { } public void UnsafeOnCompleted(System.Action continuation) { } } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct ValueTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion { + private object _dummy; public bool IsCompleted { get { throw null; } } public TResult GetResult() { throw null; } public void OnCompleted(System.Action continuation) { } @@ -48,10 +50,11 @@ namespace System.Runtime.CompilerServices namespace System.Threading.Tasks { [System.Runtime.CompilerServices.AsyncMethodBuilderAttribute(typeof(System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder<>))] - public partial struct ValueTask : System.IEquatable> + public readonly partial struct ValueTask : System.IEquatable> { - public ValueTask(System.Threading.Tasks.Task task) { throw null;} - public ValueTask(TResult result) { throw null;} + internal readonly TResult _result; + public ValueTask(System.Threading.Tasks.Task task) { throw null; } + public ValueTask(TResult result) { throw null; } public bool IsCanceled { get { throw null; } } public bool IsCompleted { get { throw null; } } public bool IsCompletedSuccessfully { get { throw null; } } diff --git a/external/corefx/src/System.Threading.Tasks.Extensions/ref/System.Threading.Tasks.Extensions.csproj b/external/corefx/src/System.Threading.Tasks.Extensions/ref/System.Threading.Tasks.Extensions.csproj index 7c691fde62..063e479478 100644 --- a/external/corefx/src/System.Threading.Tasks.Extensions/ref/System.Threading.Tasks.Extensions.csproj +++ b/external/corefx/src/System.Threading.Tasks.Extensions/ref/System.Threading.Tasks.Extensions.csproj @@ -4,8 +4,13 @@ {0DF7FA9A-E7D3-4CEF-862B-A37F5BBBB54C} true + + 4.1.1.0 - @@ -26,4 +31,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Threading.Tasks.Extensions/src/System.Threading.Tasks.Extensions.csproj b/external/corefx/src/System.Threading.Tasks.Extensions/src/System.Threading.Tasks.Extensions.csproj index 28a03703df..0bc93927d5 100644 --- a/external/corefx/src/System.Threading.Tasks.Extensions/src/System.Threading.Tasks.Extensions.csproj +++ b/external/corefx/src/System.Threading.Tasks.Extensions/src/System.Threading.Tasks.Extensions.csproj @@ -7,8 +7,8 @@ netstandard1.0;portable-net45+win8+wp8+wpa81 true true + 4.1.1.0 - diff --git a/external/corefx/src/System.Threading.Tasks.Extensions/src/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs b/external/corefx/src/System.Threading.Tasks.Extensions/src/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs index e4c40a2abb..8cbcdc562f 100644 --- a/external/corefx/src/System.Threading.Tasks.Extensions/src/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs +++ b/external/corefx/src/System.Threading.Tasks.Extensions/src/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs @@ -91,7 +91,6 @@ namespace System.Runtime.CompilerServices /// The type of the state machine. /// the awaiter /// The state machine. - [SecuritySafeCritical] public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine diff --git a/external/corefx/src/System.Threading.Tasks.Extensions/src/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs b/external/corefx/src/System.Threading.Tasks.Extensions/src/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs index 0c2d61db31..e0b92e5de1 100644 --- a/external/corefx/src/System.Threading.Tasks.Extensions/src/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs +++ b/external/corefx/src/System.Threading.Tasks.Extensions/src/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs @@ -10,7 +10,7 @@ namespace System.Runtime.CompilerServices /// Provides an awaitable type that enables configured awaits on a . /// The type of the result produced. [StructLayout(LayoutKind.Auto)] - public struct ConfiguredValueTaskAwaitable + public readonly struct ConfiguredValueTaskAwaitable { /// The wrapped . private readonly ValueTask _value; @@ -34,7 +34,7 @@ namespace System.Runtime.CompilerServices /// Provides an awaiter for a . [StructLayout(LayoutKind.Auto)] - public struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion + public readonly struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion { /// The value being awaited. private readonly ValueTask _value; diff --git a/external/corefx/src/System.Threading.Tasks.Extensions/src/System/Runtime/CompilerServices/ValueTaskAwaiter.cs b/external/corefx/src/System.Threading.Tasks.Extensions/src/System/Runtime/CompilerServices/ValueTaskAwaiter.cs index ee9578c7cc..2774ba6ad3 100644 --- a/external/corefx/src/System.Threading.Tasks.Extensions/src/System/Runtime/CompilerServices/ValueTaskAwaiter.cs +++ b/external/corefx/src/System.Threading.Tasks.Extensions/src/System/Runtime/CompilerServices/ValueTaskAwaiter.cs @@ -8,7 +8,7 @@ using System.Threading.Tasks; namespace System.Runtime.CompilerServices { /// Provides an awaiter for a . - public struct ValueTaskAwaiter : ICriticalNotifyCompletion + public readonly struct ValueTaskAwaiter : ICriticalNotifyCompletion { /// The value being awaited. private readonly ValueTask _value; diff --git a/external/corefx/src/System.Threading.Tasks.Extensions/src/System/Threading/Tasks/ValueTask.cs b/external/corefx/src/System.Threading.Tasks.Extensions/src/System/Threading/Tasks/ValueTask.cs index d2d4997124..f91e6da4cb 100644 --- a/external/corefx/src/System.Threading.Tasks.Extensions/src/System/Threading/Tasks/ValueTask.cs +++ b/external/corefx/src/System.Threading.Tasks.Extensions/src/System/Threading/Tasks/ValueTask.cs @@ -52,7 +52,7 @@ namespace System.Threading.Tasks /// [AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder<>))] [StructLayout(LayoutKind.Auto)] - public struct ValueTask : IEquatable> + public readonly struct ValueTask : IEquatable> { /// The task to be used if the operation completed asynchronously or if it completed synchronously but non-successfully. internal readonly Task _task; diff --git a/external/corefx/src/System.Threading.Tasks.Parallel/ref/System.Threading.Tasks.Parallel.cs b/external/corefx/src/System.Threading.Tasks.Parallel/ref/System.Threading.Tasks.Parallel.cs index 9ede5d209f..1bec429b48 100644 --- a/external/corefx/src/System.Threading.Tasks.Parallel/ref/System.Threading.Tasks.Parallel.cs +++ b/external/corefx/src/System.Threading.Tasks.Parallel/ref/System.Threading.Tasks.Parallel.cs @@ -5,7 +5,6 @@ // Changes to this file must follow the http://aka.ms/api-review process. // ------------------------------------------------------------------------------ - namespace System.Threading.Tasks { public static partial class Parallel @@ -18,10 +17,6 @@ namespace System.Threading.Tasks public static System.Threading.Tasks.ParallelLoopResult For(long fromInclusive, long toExclusive, System.Action body) { throw null; } public static System.Threading.Tasks.ParallelLoopResult For(long fromInclusive, long toExclusive, System.Threading.Tasks.ParallelOptions parallelOptions, System.Action body) { throw null; } public static System.Threading.Tasks.ParallelLoopResult For(long fromInclusive, long toExclusive, System.Threading.Tasks.ParallelOptions parallelOptions, System.Action body) { throw null; } - public static System.Threading.Tasks.ParallelLoopResult For(int fromInclusive, int toExclusive, System.Func localInit, System.Func body, System.Action localFinally) { throw null; } - public static System.Threading.Tasks.ParallelLoopResult For(int fromInclusive, int toExclusive, System.Threading.Tasks.ParallelOptions parallelOptions, System.Func localInit, System.Func body, System.Action localFinally) { throw null; } - public static System.Threading.Tasks.ParallelLoopResult For(long fromInclusive, long toExclusive, System.Func localInit, System.Func body, System.Action localFinally) { throw null; } - public static System.Threading.Tasks.ParallelLoopResult For(long fromInclusive, long toExclusive, System.Threading.Tasks.ParallelOptions parallelOptions, System.Func localInit, System.Func body, System.Action localFinally) { throw null; } public static System.Threading.Tasks.ParallelLoopResult ForEach(System.Collections.Concurrent.OrderablePartitioner source, System.Action body) { throw null; } public static System.Threading.Tasks.ParallelLoopResult ForEach(System.Collections.Concurrent.OrderablePartitioner source, System.Threading.Tasks.ParallelOptions parallelOptions, System.Action body) { throw null; } public static System.Threading.Tasks.ParallelLoopResult ForEach(System.Collections.Concurrent.Partitioner source, System.Action body) { throw null; } @@ -38,16 +33,20 @@ namespace System.Threading.Tasks public static System.Threading.Tasks.ParallelLoopResult ForEach(System.Collections.Concurrent.OrderablePartitioner source, System.Threading.Tasks.ParallelOptions parallelOptions, System.Func localInit, System.Func body, System.Action localFinally) { throw null; } public static System.Threading.Tasks.ParallelLoopResult ForEach(System.Collections.Concurrent.Partitioner source, System.Func localInit, System.Func body, System.Action localFinally) { throw null; } public static System.Threading.Tasks.ParallelLoopResult ForEach(System.Collections.Concurrent.Partitioner source, System.Threading.Tasks.ParallelOptions parallelOptions, System.Func localInit, System.Func body, System.Action localFinally) { throw null; } - public static System.Threading.Tasks.ParallelLoopResult ForEach(System.Collections.Generic.IEnumerable source, System.Func localInit, System.Func body, System.Action localFinally) { throw null; } public static System.Threading.Tasks.ParallelLoopResult ForEach(System.Collections.Generic.IEnumerable source, System.Func localInit, System.Func body, System.Action localFinally) { throw null; } - public static System.Threading.Tasks.ParallelLoopResult ForEach(System.Collections.Generic.IEnumerable source, System.Threading.Tasks.ParallelOptions parallelOptions, System.Func localInit, System.Func body, System.Action localFinally) { throw null; } + public static System.Threading.Tasks.ParallelLoopResult ForEach(System.Collections.Generic.IEnumerable source, System.Func localInit, System.Func body, System.Action localFinally) { throw null; } public static System.Threading.Tasks.ParallelLoopResult ForEach(System.Collections.Generic.IEnumerable source, System.Threading.Tasks.ParallelOptions parallelOptions, System.Func localInit, System.Func body, System.Action localFinally) { throw null; } + public static System.Threading.Tasks.ParallelLoopResult ForEach(System.Collections.Generic.IEnumerable source, System.Threading.Tasks.ParallelOptions parallelOptions, System.Func localInit, System.Func body, System.Action localFinally) { throw null; } + public static System.Threading.Tasks.ParallelLoopResult For(int fromInclusive, int toExclusive, System.Func localInit, System.Func body, System.Action localFinally) { throw null; } + public static System.Threading.Tasks.ParallelLoopResult For(int fromInclusive, int toExclusive, System.Threading.Tasks.ParallelOptions parallelOptions, System.Func localInit, System.Func body, System.Action localFinally) { throw null; } + public static System.Threading.Tasks.ParallelLoopResult For(long fromInclusive, long toExclusive, System.Func localInit, System.Func body, System.Action localFinally) { throw null; } + public static System.Threading.Tasks.ParallelLoopResult For(long fromInclusive, long toExclusive, System.Threading.Tasks.ParallelOptions parallelOptions, System.Func localInit, System.Func body, System.Action localFinally) { throw null; } public static void Invoke(params System.Action[] actions) { } public static void Invoke(System.Threading.Tasks.ParallelOptions parallelOptions, params System.Action[] actions) { } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct ParallelLoopResult { + private int _dummy; public bool IsCompleted { get { throw null; } } public System.Nullable LowestBreakIteration { get { throw null; } } } diff --git a/external/corefx/src/System.Threading.Tasks.Parallel/src/System.Threading.Tasks.Parallel.csproj b/external/corefx/src/System.Threading.Tasks.Parallel/src/System.Threading.Tasks.Parallel.csproj index 24f8f675f9..bd961b8c47 100644 --- a/external/corefx/src/System.Threading.Tasks.Parallel/src/System.Threading.Tasks.Parallel.csproj +++ b/external/corefx/src/System.Threading.Tasks.Parallel/src/System.Threading.Tasks.Parallel.csproj @@ -24,7 +24,6 @@ - diff --git a/external/corefx/src/System.Threading.Tasks.Parallel/src/System/Threading/Tasks/ParallelETWProvider.cs b/external/corefx/src/System.Threading.Tasks.Parallel/src/System/Threading/Tasks/ParallelETWProvider.cs index b426b890a1..66869e80e4 100644 --- a/external/corefx/src/System.Threading.Tasks.Parallel/src/System/Threading/Tasks/ParallelETWProvider.cs +++ b/external/corefx/src/System.Threading.Tasks.Parallel/src/System/Threading/Tasks/ParallelETWProvider.cs @@ -98,7 +98,6 @@ namespace System.Threading.Tasks /// The kind of fork/join operation. /// The lower bound of the loop. /// The upper bound of the loop. - [SecuritySafeCritical] [Event(PARALLELLOOPBEGIN_ID, Level = EventLevel.Informational, Task = ParallelEtwProvider.Tasks.Loop, Opcode = EventOpcode.Start)] public void ParallelLoopBegin(Int32 OriginatingTaskSchedulerID, Int32 OriginatingTaskID, // PFX_COMMON_EVENT_HEADER Int32 ForkJoinContextID, ForkJoinOperationType OperationType, // PFX_FORKJOIN_COMMON_EVENT_HEADER @@ -141,7 +140,6 @@ namespace System.Threading.Tasks /// The task ID. /// The loop ID. /// the total number of iterations processed. - [SecuritySafeCritical] [Event(PARALLELLOOPEND_ID, Level = EventLevel.Informational, Task = ParallelEtwProvider.Tasks.Loop, Opcode = EventOpcode.Stop)] public void ParallelLoopEnd(Int32 OriginatingTaskSchedulerID, Int32 OriginatingTaskID, // PFX_COMMON_EVENT_HEADER Int32 ForkJoinContextID, Int64 TotalIterations) @@ -177,7 +175,6 @@ namespace System.Threading.Tasks /// The invoke ID. /// The kind of fork/join operation. /// The number of actions being invoked. - [SecuritySafeCritical] [Event(PARALLELINVOKEBEGIN_ID, Level = EventLevel.Informational, Task = ParallelEtwProvider.Tasks.Invoke, Opcode = EventOpcode.Start)] public void ParallelInvokeBegin(Int32 OriginatingTaskSchedulerID, Int32 OriginatingTaskID, // PFX_COMMON_EVENT_HEADER Int32 ForkJoinContextID, ForkJoinOperationType OperationType, // PFX_FORKJOIN_COMMON_EVENT_HEADER diff --git a/external/corefx/src/System.Threading.Tasks.Parallel/tests/ParallelForTests.cs b/external/corefx/src/System.Threading.Tasks.Parallel/tests/ParallelForTests.cs index 32a4353857..b25d5b1fea 100644 --- a/external/corefx/src/System.Threading.Tasks.Parallel/tests/ParallelForTests.cs +++ b/external/corefx/src/System.Threading.Tasks.Parallel/tests/ParallelForTests.cs @@ -1384,25 +1384,21 @@ namespace System.Threading.Tasks.Tests // Used for parallel scheduler tests public class ParallelTestsScheduler : TaskScheduler { - [SecurityCritical] protected override void QueueTask(Task task) { Task.Run(() => TryExecuteTaskWrapper(task)); } - [SecuritySafeCritical] private void TryExecuteTaskWrapper(Task task) { TryExecuteTask(task); } - [SecurityCritical] protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { return TryExecuteTask(task); } - [SecurityCritical] protected override IEnumerable GetScheduledTasks() { return null; diff --git a/external/corefx/src/System.Threading.Tasks/ref/System.Threading.Tasks.cs b/external/corefx/src/System.Threading.Tasks/ref/System.Threading.Tasks.cs index 48cf5dbff7..91dda148e3 100644 --- a/external/corefx/src/System.Threading.Tasks/ref/System.Threading.Tasks.cs +++ b/external/corefx/src/System.Threading.Tasks/ref/System.Threading.Tasks.cs @@ -43,9 +43,9 @@ namespace System } namespace System.Runtime.CompilerServices { - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct AsyncTaskMethodBuilder { + private object _dummy; public System.Threading.Tasks.Task Task { get { throw null; } } public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.INotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } @@ -55,9 +55,9 @@ namespace System.Runtime.CompilerServices public void SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine stateMachine) { } public void Start(ref TStateMachine stateMachine) where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct AsyncTaskMethodBuilder { + private System.Threading.Tasks.Task _dummy; public System.Threading.Tasks.Task Task { get { throw null; } } public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.INotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } @@ -67,9 +67,9 @@ namespace System.Runtime.CompilerServices public void SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine stateMachine) { } public void Start(ref TStateMachine stateMachine) where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct AsyncVoidMethodBuilder { + private object _dummy; public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.INotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } public static System.Runtime.CompilerServices.AsyncVoidMethodBuilder Create() { throw null; } diff --git a/external/corefx/src/System.Threading.Tasks/src/System.Threading.Tasks.csproj b/external/corefx/src/System.Threading.Tasks/src/System.Threading.Tasks.csproj index a708ded2f8..1bee4d20fb 100644 --- a/external/corefx/src/System.Threading.Tasks/src/System.Threading.Tasks.csproj +++ b/external/corefx/src/System.Threading.Tasks/src/System.Threading.Tasks.csproj @@ -6,7 +6,6 @@ System.Threading.Tasks true - diff --git a/external/corefx/src/System.Threading.Tasks/tests/CESchedulerPairTests.cs b/external/corefx/src/System.Threading.Tasks/tests/CESchedulerPairTests.cs index ef8ec6a94f..ff2c6995ff 100644 --- a/external/corefx/src/System.Threading.Tasks/tests/CESchedulerPairTests.cs +++ b/external/corefx/src/System.Threading.Tasks/tests/CESchedulerPairTests.cs @@ -36,7 +36,6 @@ namespace System.Threading.Tasks.Tests } - [SecurityCritical] protected override void QueueTask(Task task) { if (task == null) throw new ArgumentNullException("When requesting to QueueTask, the input task can not be null"); @@ -52,13 +51,11 @@ namespace System.Threading.Tasks.Tests }, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); } - [SecuritySafeCritical] //This has to be SecuritySafeCritical since its accesses TaskScheduler.TryExecuteTask (which is safecritical) private void ExecuteTask(Task task) { base.TryExecuteTask(task); } - [SecurityCritical] protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { if (taskWasPreviouslyQueued) return false; @@ -72,7 +69,6 @@ namespace System.Threading.Tasks.Tests // set; //} - [SecurityCritical] protected override IEnumerable GetScheduledTasks() { return null; } private Object _lockObj = new Object(); private int _counter = 1; //This is used to keep track of how many scheduler tasks were created diff --git a/external/corefx/src/System.Threading.Tasks/tests/System.Threading.Tasks.Tests.csproj b/external/corefx/src/System.Threading.Tasks/tests/System.Threading.Tasks.Tests.csproj index be16c23396..b0745a5e54 100644 --- a/external/corefx/src/System.Threading.Tasks/tests/System.Threading.Tasks.Tests.csproj +++ b/external/corefx/src/System.Threading.Tasks/tests/System.Threading.Tasks.Tests.csproj @@ -4,7 +4,6 @@ {B6C09633-D161-499A-8FE1-46B2D53A16E7} - @@ -58,4 +57,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Threading.Tasks/tests/Task/TaskRunSyncTests.cs b/external/corefx/src/System.Threading.Tasks/tests/Task/TaskRunSyncTests.cs index 37e3d023bd..e2f7e4ca20 100644 --- a/external/corefx/src/System.Threading.Tasks/tests/Task/TaskRunSyncTests.cs +++ b/external/corefx/src/System.Threading.Tasks/tests/Task/TaskRunSyncTests.cs @@ -89,13 +89,11 @@ namespace System.Threading.Tasks.Tests } } - [SecuritySafeCritical] private bool ExecuteTask(Task task) { return TryExecuteTask(task); } - [SecurityCritical] protected override void QueueTask(Task task) { _tasks.Add(task); @@ -109,7 +107,6 @@ namespace System.Threading.Tasks.Tests } } - [SecurityCritical] protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { RunSyncCalledCount++; @@ -128,7 +125,6 @@ namespace System.Threading.Tasks.Tests } } - [SecurityCritical] protected override IEnumerable GetScheduledTasks() { return _tasks; diff --git a/external/corefx/src/System.Threading.Tasks/tests/TaskScheduler/TaskSchedulerTests.cs b/external/corefx/src/System.Threading.Tasks/tests/TaskScheduler/TaskSchedulerTests.cs index 2c4f75a42c..aebd9305f2 100644 --- a/external/corefx/src/System.Threading.Tasks/tests/TaskScheduler/TaskSchedulerTests.cs +++ b/external/corefx/src/System.Threading.Tasks/tests/TaskScheduler/TaskSchedulerTests.cs @@ -339,13 +339,11 @@ namespace System.Threading.Tasks.Tests // Buggy task scheduler to make sure that we handle QueueTask()/TryExecuteTaskInline() // exceptions correctly. Used in RunBuggySchedulerTests() below. - [SecuritySafeCritical] public class BuggyTaskScheduler : TaskScheduler { private readonly ConcurrentQueue _tasks = new ConcurrentQueue(); private bool _faultQueues; - [SecurityCritical] protected override void QueueTask(Task task) { if (_faultQueues) @@ -354,13 +352,11 @@ namespace System.Threading.Tasks.Tests _tasks.Enqueue(task); } - [SecurityCritical] protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { throw new ArgumentException("I am your worst nightmare!"); } - [SecurityCritical] protected override IEnumerable GetScheduledTasks() { return _tasks; diff --git a/external/corefx/src/System.Threading.Thread/tests/MTAMain/Configurations.props b/external/corefx/src/System.Threading.Thread/tests/MTAMain/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Threading.Thread/tests/MTAMain/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Threading.Thread/tests/MTAMain/MTAMain.cs b/external/corefx/src/System.Threading.Thread/tests/MTAMain/MTAMain.cs new file mode 100644 index 0000000000..fa25b067fd --- /dev/null +++ b/external/corefx/src/System.Threading.Thread/tests/MTAMain/MTAMain.cs @@ -0,0 +1,54 @@ +// 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.Threading; + +namespace MTAMain +{ + internal static class MTAMain + { + [MTAThread] + static int Main(string[] args) + { + const int Success = 0; + const int SuccessOnUnix = 2; + const int Failure = 1; + + string mode = args[0]; + int retValue = Failure; + Thread curThread = Thread.CurrentThread; + + if (mode == "GetApartmentState") + { + if (curThread.GetApartmentState() == ApartmentState.MTA) + { + curThread.SetApartmentState(ApartmentState.MTA); + retValue = Success; + } + else + { + retValue = SuccessOnUnix; + } + } + else + { + try + { + curThread.SetApartmentState(ApartmentState.STA); + } + catch (InvalidOperationException) + { + retValue = Success; + } + catch (PlatformNotSupportedException) + { + retValue = SuccessOnUnix; + } + } + + return retValue; + } + } +} diff --git a/external/corefx/src/System.Threading.Thread/tests/MTAMain/MTAMain.csproj b/external/corefx/src/System.Threading.Thread/tests/MTAMain/MTAMain.csproj new file mode 100644 index 0000000000..ef916918e4 --- /dev/null +++ b/external/corefx/src/System.Threading.Thread/tests/MTAMain/MTAMain.csproj @@ -0,0 +1,23 @@ + + + + + Exe + true + {06B19C7D-9EBE-420F-BD33-137DB18A1FEB} + + + + + + + + + PreserveNewest + + + PreserveNewest + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Threading.Thread/tests/MTAMain/MTAMain.exe.config b/external/corefx/src/System.Threading.Thread/tests/MTAMain/MTAMain.exe.config new file mode 100644 index 0000000000..b955a1b9b2 --- /dev/null +++ b/external/corefx/src/System.Threading.Thread/tests/MTAMain/MTAMain.exe.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/external/corefx/src/System.Threading.Thread/tests/MTAMain/MTAMain.runtimeconfig.json b/external/corefx/src/System.Threading.Thread/tests/MTAMain/MTAMain.runtimeconfig.json new file mode 100644 index 0000000000..0e7c179be9 --- /dev/null +++ b/external/corefx/src/System.Threading.Thread/tests/MTAMain/MTAMain.runtimeconfig.json @@ -0,0 +1,8 @@ +{ + "runtimeOptions": { + "framework": { + "name": "Microsoft.NETCore.App", + "version": "9.9.9" + } + } +} \ No newline at end of file diff --git a/external/corefx/src/System.Threading.Thread/tests/STAMain/Configurations.props b/external/corefx/src/System.Threading.Thread/tests/STAMain/Configurations.props new file mode 100644 index 0000000000..c398e42e89 --- /dev/null +++ b/external/corefx/src/System.Threading.Thread/tests/STAMain/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + \ No newline at end of file diff --git a/external/corefx/src/System.Threading.Thread/tests/STAMain/STAMain.cs b/external/corefx/src/System.Threading.Thread/tests/STAMain/STAMain.cs new file mode 100644 index 0000000000..410211175b --- /dev/null +++ b/external/corefx/src/System.Threading.Thread/tests/STAMain/STAMain.cs @@ -0,0 +1,54 @@ +// 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.Threading; + +namespace STAMain +{ + internal static class STAMain + { + [STAThread] + static int Main(string[] args) + { + const int Success = 0; + const int SuccessOnUnix = 2; + const int Failure = 1; + + string mode = args[0]; + int retValue = Failure; + Thread curThread = Thread.CurrentThread; + + if (mode == "GetApartmentState") + { + if (curThread.GetApartmentState() == ApartmentState.STA) + { + curThread.SetApartmentState(ApartmentState.STA); + retValue = Success; + } + else + { + retValue = SuccessOnUnix; + } + } + else + { + try + { + curThread.SetApartmentState(ApartmentState.MTA); + } + catch (InvalidOperationException) + { + retValue = Success; + } + catch (PlatformNotSupportedException) + { + retValue = SuccessOnUnix; + } + } + + return retValue; + } + } +} diff --git a/external/corefx/src/System.Threading.Thread/tests/STAMain/STAMain.csproj b/external/corefx/src/System.Threading.Thread/tests/STAMain/STAMain.csproj new file mode 100644 index 0000000000..1aa40ebf17 --- /dev/null +++ b/external/corefx/src/System.Threading.Thread/tests/STAMain/STAMain.csproj @@ -0,0 +1,21 @@ + + + + + Exe + true + {8045E634-C181-4C6C-AE48-71AC18D1C637} + + + + + + + PreserveNewest + + + PreserveNewest + + + + \ No newline at end of file diff --git a/external/corefx/src/System.Threading.Thread/tests/STAMain/STAMain.exe.config b/external/corefx/src/System.Threading.Thread/tests/STAMain/STAMain.exe.config new file mode 100644 index 0000000000..b955a1b9b2 --- /dev/null +++ b/external/corefx/src/System.Threading.Thread/tests/STAMain/STAMain.exe.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/external/corefx/src/System.Threading.Thread/tests/STAMain/STAMain.runtimeconfig.json b/external/corefx/src/System.Threading.Thread/tests/STAMain/STAMain.runtimeconfig.json new file mode 100644 index 0000000000..0e7c179be9 --- /dev/null +++ b/external/corefx/src/System.Threading.Thread/tests/STAMain/STAMain.runtimeconfig.json @@ -0,0 +1,8 @@ +{ + "runtimeOptions": { + "framework": { + "name": "Microsoft.NETCore.App", + "version": "9.9.9" + } + } +} \ No newline at end of file diff --git a/external/corefx/src/System.Threading.Thread/tests/System.Threading.Thread.Tests.csproj b/external/corefx/src/System.Threading.Thread/tests/System.Threading.Thread.Tests.csproj index 273901b62c..2402a73d29 100644 --- a/external/corefx/src/System.Threading.Thread/tests/System.Threading.Thread.Tests.csproj +++ b/external/corefx/src/System.Threading.Thread/tests/System.Threading.Thread.Tests.csproj @@ -1,11 +1,10 @@ - + {33F5A50E-B823-4FDD-8571-365C909ACEAE} true - @@ -18,6 +17,16 @@ CommonTest\System\Threading\ThreadPoolHelpers.cs + + {69e46a6f-9966-45a5-8945-2559fe337827} + RemoteExecutorConsoleApp + + + STAMain + + + MTAMain + - + \ No newline at end of file diff --git a/external/corefx/src/System.Threading.Thread/tests/ThreadTests.cs b/external/corefx/src/System.Threading.Thread/tests/ThreadTests.cs index ba15b4708e..22f0f88e50 100644 --- a/external/corefx/src/System.Threading.Thread/tests/ThreadTests.cs +++ b/external/corefx/src/System.Threading.Thread/tests/ThreadTests.cs @@ -13,7 +13,12 @@ using Xunit; namespace System.Threading.Threads.Tests { - public static class ThreadTests + public class DummyClass : RemoteExecutorTestBase + { + public static string HostRunnerTest = HostRunner; + } + + public static class ThreadTests { private const int UnexpectedTimeoutMilliseconds = ThreadTestHelpers.UnexpectedTimeoutMilliseconds; private const int ExpectedTimeoutMilliseconds = ThreadTestHelpers.ExpectedTimeoutMilliseconds; @@ -139,6 +144,105 @@ namespace System.Threading.Threads.Tests }; } + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + [InlineData("STAMain.exe", "GetApartmentState")] + [InlineData("STAMain.exe", "SetApartmentState")] + [InlineData("MTAMain.exe", "GetApartmentState")] + [InlineData("MTAMain.exe", "SetApartmentState")] + [ActiveIssue(20766, TargetFrameworkMonikers.Uap)] + public static void ApartmentState_AttributePresent(string AppName, string mode) + { + var psi = new ProcessStartInfo(); + if (PlatformDetection.IsFullFramework || PlatformDetection.IsNetNative) + { + psi.FileName = AppName; + psi.Arguments = $"{mode}"; + } + else + { + psi.FileName = DummyClass.HostRunnerTest; + psi.Arguments = $"{AppName} {mode}"; + } + using (Process p = Process.Start(psi)) + { + p.WaitForExit(); + Assert.Equal(PlatformDetection.IsWindows ? 0 : 2, p.ExitCode); + } + } + + [Fact] + [ActiveIssue(20766,TargetFrameworkMonikers.UapAot)] + [PlatformSpecific(TestPlatforms.Windows)] + public static void ApartmentState_NoAttributePresent_DefaultState_Windows() + { + DummyClass.RemoteInvoke(() => + { + Assert.Equal(ApartmentState.MTA, Thread.CurrentThread.GetApartmentState()); + Assert.Throws(() => Thread.CurrentThread.SetApartmentState(ApartmentState.STA)); + Thread.CurrentThread.SetApartmentState(ApartmentState.MTA); + }).Dispose(); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + [ActiveIssue(20766,TargetFrameworkMonikers.UapAot)] + [PlatformSpecific(TestPlatforms.Windows)] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + public static void ApartmentState_NoAttributePresent_STA_Windows_Core() + { + DummyClass.RemoteInvoke(() => + { + Thread.CurrentThread.SetApartmentState(ApartmentState.STA); + Assert.Equal(ApartmentState.STA, Thread.CurrentThread.GetApartmentState()); + Assert.Throws(() => Thread.CurrentThread.SetApartmentState(ApartmentState.MTA)); + }).Dispose(); + } + + // The Thread Apartment State is set to MTA if attribute is not specified on main function + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] + public static void ApartmentState_NoAttributePresent_STA_Windows_Desktop() + { + DummyClass.RemoteInvoke(() => + { + Assert.Throws(() => Thread.CurrentThread.SetApartmentState(ApartmentState.STA)); + Thread.CurrentThread.SetApartmentState(ApartmentState.MTA); + Assert.Equal(ApartmentState.MTA, Thread.CurrentThread.GetApartmentState()); + }).Dispose(); + } + + [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix)] + public static void ApartmentState_NoAttributePresent_DefaultState_Unix() + { + DummyClass.RemoteInvoke(() => + { + Assert.Equal(ApartmentState.Unknown, Thread.CurrentThread.GetApartmentState()); + Assert.Throws(() => Thread.CurrentThread.SetApartmentState(ApartmentState.MTA)); + }).Dispose(); + } + + // Thread is always initialized as MTA irrespective of the attribute present. + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsWindowsNanoServer))] + public static void ApartmentState_NoAttributePresent_DefaultState_Nano() + { + DummyClass.RemoteInvoke(() => + { + Assert.Throws(() => Thread.CurrentThread.SetApartmentState(ApartmentState.STA)); + Assert.Equal(ApartmentState.MTA, Thread.CurrentThread.GetApartmentState()); + }).Dispose(); + } + + [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix)] + public static void ApartmentState_NoAttributePresent_STA_Unix() + { + DummyClass.RemoteInvoke(() => + { + Assert.Throws(() => Thread.CurrentThread.SetApartmentState(ApartmentState.STA)); + }).Dispose(); + } + [Theory] [MemberData(nameof(ApartmentStateTest_MemberData))] [PlatformSpecific(TestPlatforms.Windows)] // Expected behavior differs on Unix and Windows @@ -225,6 +329,7 @@ namespace System.Threading.Threads.Tests [Fact] [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + [SkipOnTargetFramework(TargetFrameworkMonikers.Mono)] public static void CurrentCultureTest_SkipOnDesktopFramework() { // Cannot access culture properties on a thread object from a different thread @@ -277,6 +382,7 @@ namespace System.Threading.Threads.Tests [Fact] [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + [SkipOnTargetFramework(TargetFrameworkMonikers.Mono)] public static void CurrentPrincipalTest_SkipOnDesktopFramework() { ThreadTestHelpers.RunTestInBackgroundThread(() => Assert.Null(Thread.CurrentPrincipal)); @@ -317,6 +423,7 @@ namespace System.Threading.Threads.Tests [Fact] [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + [SkipOnTargetFramework(TargetFrameworkMonikers.Mono)] public static void ExecutionContextTest() { ThreadTestHelpers.RunTestInBackgroundThread( @@ -465,6 +572,7 @@ namespace System.Threading.Threads.Tests [Fact] [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + [SkipOnTargetFramework(TargetFrameworkMonikers.Mono)] public static void AbortSuspendTest() { var e = new ManualResetEvent(false); @@ -825,6 +933,7 @@ namespace System.Threading.Threads.Tests [Fact] [ActiveIssue(20766,TargetFrameworkMonikers.UapAot)] + [SkipOnTargetFramework(TargetFrameworkMonikers.Mono)] public static void MiscellaneousTest() { Thread.BeginCriticalRegion(); diff --git a/external/corefx/src/System.Threading.ThreadPool/System.Threading.ThreadPool.sln b/external/corefx/src/System.Threading.ThreadPool/System.Threading.ThreadPool.sln index 2f35ab555d..35b81da414 100644 --- a/external/corefx/src/System.Threading.ThreadPool/System.Threading.ThreadPool.sln +++ b/external/corefx/src/System.Threading.ThreadPool/System.Threading.ThreadPool.sln @@ -26,10 +26,10 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {403AD1B8-6F95-4A2E-92A2-727606ABD866}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU - {403AD1B8-6F95-4A2E-92A2-727606ABD866}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU - {403AD1B8-6F95-4A2E-92A2-727606ABD866}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU - {403AD1B8-6F95-4A2E-92A2-727606ABD866}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU + {403AD1B8-6F95-4A2E-92A2-727606ABD866}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {403AD1B8-6F95-4A2E-92A2-727606ABD866}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {403AD1B8-6F95-4A2E-92A2-727606ABD866}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {403AD1B8-6F95-4A2E-92A2-727606ABD866}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU {EE797598-BA64-4150-A3AA-8FB97DA63697}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU {EE797598-BA64-4150-A3AA-8FB97DA63697}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU {EE797598-BA64-4150-A3AA-8FB97DA63697}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU diff --git a/external/corefx/src/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.cs b/external/corefx/src/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.cs index b22f1c0f1f..bb4b87c06c 100644 --- a/external/corefx/src/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.cs +++ b/external/corefx/src/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.cs @@ -17,7 +17,6 @@ namespace System.Threading { [System.ObsoleteAttribute("ThreadPool.BindHandle(IntPtr) has been deprecated. Please use ThreadPool.BindHandle(SafeHandle) instead.", false)] public static bool BindHandle(System.IntPtr osHandle) { throw null; } - [System.Security.SecurityCriticalAttribute] public static bool BindHandle(System.Runtime.InteropServices.SafeHandle osHandle) { throw null; } public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads) { throw null; } public static void GetMaxThreads(out int workerThreads, out int completionPortThreads) { throw null; } diff --git a/external/corefx/src/System.Threading.ThreadPool/tests/System.Threading.ThreadPool.Tests.csproj b/external/corefx/src/System.Threading.ThreadPool/tests/System.Threading.ThreadPool.Tests.csproj index 1d781c04d9..90a7a2706c 100644 --- a/external/corefx/src/System.Threading.ThreadPool/tests/System.Threading.ThreadPool.Tests.csproj +++ b/external/corefx/src/System.Threading.ThreadPool/tests/System.Threading.ThreadPool.Tests.csproj @@ -1,10 +1,9 @@ - + {403AD1B8-6F95-4A2E-92A2-727606ABD866} - @@ -19,4 +18,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Threading.ThreadPool/tests/ThreadPoolTests.cs b/external/corefx/src/System.Threading.ThreadPool/tests/ThreadPoolTests.cs index e86ff390cf..bc2a0fb172 100644 --- a/external/corefx/src/System.Threading.ThreadPool/tests/ThreadPoolTests.cs +++ b/external/corefx/src/System.Threading.ThreadPool/tests/ThreadPoolTests.cs @@ -87,14 +87,6 @@ namespace System.Threading.ThreadPools.Tests int minw, minc, maxw, maxc; ThreadPool.GetMinThreads(out minw, out minc); ThreadPool.GetMaxThreads(out maxw, out maxc); - Action resetThreadCounts = - () => - { - Assert.True(ThreadPool.SetMaxThreads(maxw, maxc)); - VerifyMaxThreads(maxw, maxc); - Assert.True(ThreadPool.SetMinThreads(minw, minc)); - VerifyMinThreads(minw, minc); - }; try { @@ -135,7 +127,6 @@ namespace System.Threading.ThreadPools.Tests VerifyMinThreads(MaxPossibleThreadCount, MaxPossibleThreadCount); Assert.True(ThreadPool.SetMinThreads(0, 0)); - VerifyMinThreads(0, 0); Assert.True(ThreadPool.SetMaxThreads(1, 1)); VerifyMaxThreads(1, 1); Assert.True(ThreadPool.SetMinThreads(1, 1)); @@ -143,38 +134,38 @@ namespace System.Threading.ThreadPools.Tests } finally { - resetThreadCounts(); + Assert.True(ThreadPool.SetMaxThreads(maxw, maxc)); + VerifyMaxThreads(maxw, maxc); + Assert.True(ThreadPool.SetMinThreads(minw, minc)); + VerifyMinThreads(minw, minc); } } [Fact] // Desktop framework doesn't check for this and instead, hits an assertion failure [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + [SkipOnTargetFramework(TargetFrameworkMonikers.Mono)] [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot, "ThreadPool.SetMinThreads and SetMaxThreads are not supported on UapAot.")] public static void SetMinMaxThreadsTest_ChangedInDotNetCore() { int minw, minc, maxw, maxc; ThreadPool.GetMinThreads(out minw, out minc); ThreadPool.GetMaxThreads(out maxw, out maxc); - Action resetThreadCounts = - () => - { - Assert.True(ThreadPool.SetMaxThreads(maxw, maxc)); - VerifyMaxThreads(maxw, maxc); - Assert.True(ThreadPool.SetMinThreads(minw, minc)); - VerifyMinThreads(minw, minc); - }; try { Assert.True(ThreadPool.SetMinThreads(0, 0)); - VerifyMinThreads(0, 0); - Assert.False(ThreadPool.SetMaxThreads(0, 0)); + VerifyMinThreads(1, 1); + Assert.False(ThreadPool.SetMaxThreads(0, 1)); + Assert.False(ThreadPool.SetMaxThreads(1, 0)); VerifyMaxThreads(maxw, maxc); } finally { - resetThreadCounts(); + Assert.True(ThreadPool.SetMaxThreads(maxw, maxc)); + VerifyMaxThreads(maxw, maxc); + Assert.True(ThreadPool.SetMinThreads(minw, minc)); + VerifyMinThreads(minw, minc); } } @@ -194,6 +185,44 @@ namespace System.Threading.ThreadPools.Tests Assert.Equal(expectedMaxc, maxc); } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Triggers an assertion failure.")] + private static void SetMinThreadsTo0Test() + { + int minw, minc, maxw, maxc; + ThreadPool.GetMinThreads(out minw, out minc); + ThreadPool.GetMaxThreads(out maxw, out maxc); + + try + { + Assert.True(ThreadPool.SetMinThreads(0, minc)); + Assert.True(ThreadPool.SetMaxThreads(1, maxc)); + + int count = 0; + var done = new ManualResetEvent(false); + WaitCallback callback = null; + callback = state => + { + ++count; + if (count > 100) + { + done.Set(); + } + else + { + ThreadPool.QueueUserWorkItem(callback); + } + }; + ThreadPool.QueueUserWorkItem(callback); + done.WaitOne(ThreadTestHelpers.UnexpectedTimeoutMilliseconds); + } + finally + { + Assert.True(ThreadPool.SetMaxThreads(maxw, maxc)); + Assert.True(ThreadPool.SetMinThreads(minw, minc)); + } + } + [Fact] public static void QueueRegisterPositiveAndFlowTest() { diff --git a/external/corefx/src/System.Threading.Timer/tests/System.Threading.Timer.Tests.csproj b/external/corefx/src/System.Threading.Timer/tests/System.Threading.Timer.Tests.csproj index f164bb4906..134d7d07bb 100644 --- a/external/corefx/src/System.Threading.Timer/tests/System.Threading.Timer.Tests.csproj +++ b/external/corefx/src/System.Threading.Timer/tests/System.Threading.Timer.Tests.csproj @@ -4,7 +4,6 @@ {ac20a28f-fda8-45e8-8728-058ead16e44c} - @@ -13,4 +12,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Threading/System.Threading.sln b/external/corefx/src/System.Threading/System.Threading.sln index 8f6ddebfae..dbfcd7a669 100644 --- a/external/corefx/src/System.Threading/System.Threading.sln +++ b/external/corefx/src/System.Threading/System.Threading.sln @@ -35,10 +35,10 @@ Global {18EF66B3-51EE-46D8-B283-1CB6A1197813}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {18EF66B3-51EE-46D8-B283-1CB6A1197813}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU {18EF66B3-51EE-46D8-B283-1CB6A1197813}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU - {7AB03FFA-7D4D-4248-9702-3D2785859C18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7AB03FFA-7D4D-4248-9702-3D2785859C18}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7AB03FFA-7D4D-4248-9702-3D2785859C18}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7AB03FFA-7D4D-4248-9702-3D2785859C18}.Release|Any CPU.Build.0 = Release|Any CPU + {7AB03FFA-7D4D-4248-9702-3D2785859C18}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU + {7AB03FFA-7D4D-4248-9702-3D2785859C18}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU + {7AB03FFA-7D4D-4248-9702-3D2785859C18}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU + {7AB03FFA-7D4D-4248-9702-3D2785859C18}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU {604027F5-1DFC-42F4-B4FE-61F8789BA647}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU {604027F5-1DFC-42F4-B4FE-61F8789BA647}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU {604027F5-1DFC-42F4-B4FE-61F8789BA647}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU diff --git a/external/corefx/src/System.Threading/ref/System.Threading.cs b/external/corefx/src/System.Threading/ref/System.Threading.cs index a564bfde4c..0a9263b007 100644 --- a/external/corefx/src/System.Threading/ref/System.Threading.cs +++ b/external/corefx/src/System.Threading/ref/System.Threading.cs @@ -22,6 +22,7 @@ namespace System.Threading } public partial struct AsyncFlowControl : System.IDisposable { + private object _dummy; public void Dispose() { } public override bool Equals(object obj) { throw null; } public bool Equals(System.Threading.AsyncFlowControl obj) { throw null; } @@ -33,13 +34,12 @@ namespace System.Threading public sealed partial class AsyncLocal { public AsyncLocal() { } - [System.Security.SecurityCriticalAttribute] public AsyncLocal(System.Action> valueChangedHandler) { } public T Value { get { throw null; } set { } } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct AsyncLocalValueChangedArgs { + private T _dummy; public T CurrentValue { get { throw null; } } public T PreviousValue { get { throw null; } } public bool ThreadContextChanged { get { throw null; } } @@ -109,15 +109,11 @@ namespace System.Threading public partial class EventWaitHandle : System.Threading.WaitHandle { public EventWaitHandle(bool initialState, System.Threading.EventResetMode mode) { } - [System.Security.SecurityCriticalAttribute] public EventWaitHandle(bool initialState, System.Threading.EventResetMode mode, string name) { } - [System.Security.SecurityCriticalAttribute] public EventWaitHandle(bool initialState, System.Threading.EventResetMode mode, string name, out bool createdNew) { throw null; } - [System.Security.SecurityCriticalAttribute] public static System.Threading.EventWaitHandle OpenExisting(string name) { throw null; } public bool Reset() { throw null; } public bool Set() { throw null; } - [System.Security.SecurityCriticalAttribute] public static bool TryOpenExisting(string name, out System.Threading.EventWaitHandle result) { throw null; } } public sealed partial class ExecutionContext : System.IDisposable, System.Runtime.Serialization.ISerializable @@ -184,6 +180,7 @@ namespace System.Threading } public partial struct LockCookie { + private int _dummy; public override bool Equals(object obj) { throw null; } public bool Equals(System.Threading.LockCookie obj) { throw null; } public override int GetHashCode() { throw null; } @@ -249,14 +246,10 @@ namespace System.Threading { public Mutex() { } public Mutex(bool initiallyOwned) { } - [System.Security.SecurityCriticalAttribute] public Mutex(bool initiallyOwned, string name) { } - [System.Security.SecurityCriticalAttribute] public Mutex(bool initiallyOwned, string name, out bool createdNew) { throw null; } - [System.Security.SecurityCriticalAttribute] public static System.Threading.Mutex OpenExisting(string name) { throw null; } public void ReleaseMutex() { } - [System.Security.SecurityCriticalAttribute] public static bool TryOpenExisting(string name, out System.Threading.Mutex result) { throw null; } } public sealed partial class ReaderWriterLock : System.Runtime.ConstrainedExecution.CriticalFinalizerObject @@ -349,9 +342,9 @@ namespace System.Threading public System.Threading.Tasks.Task WaitAsync(System.TimeSpan timeout, System.Threading.CancellationToken cancellationToken) { throw null; } } public delegate void SendOrPostCallback(object state); - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct SpinLock { + private int _dummy; public SpinLock(bool enableThreadOwnerTracking) { throw null; } public bool IsHeld { get { throw null; } } public bool IsHeldByCurrentThread { get { throw null; } } @@ -363,9 +356,9 @@ namespace System.Threading public void TryEnter(int millisecondsTimeout, ref bool lockTaken) { } public void TryEnter(System.TimeSpan timeout, ref bool lockTaken) { } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct SpinWait { + private int _dummy; public int Count { get { throw null; } } public bool NextSpinWillYield { get { throw null; } } public void Reset() { } diff --git a/external/corefx/src/System.Threading/src/ApiCompatBaseline.uapaot.txt b/external/corefx/src/System.Threading/src/ApiCompatBaseline.uapaot.txt deleted file mode 100644 index 6117d7e920..0000000000 --- a/external/corefx/src/System.Threading/src/ApiCompatBaseline.uapaot.txt +++ /dev/null @@ -1,6 +0,0 @@ -Compat issues with assembly System.Threading: -MembersMustExist : Member 'System.Threading.SynchronizationContext.IsWaitNotificationRequired()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Threading.SynchronizationContext.SetWaitNotificationRequired()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Threading.SynchronizationContext.Wait(System.IntPtr[], System.Boolean, System.Int32)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Threading.SynchronizationContext.WaitHelper(System.IntPtr[], System.Boolean, System.Int32)' does not exist in the implementation but it does exist in the contract. -Total Issues: 4 diff --git a/external/corefx/src/System.Threading/src/System.Threading.csproj b/external/corefx/src/System.Threading/src/System.Threading.csproj index 4ee6403b27..23059cc38e 100644 --- a/external/corefx/src/System.Threading/src/System.Threading.csproj +++ b/external/corefx/src/System.Threading/src/System.Threading.csproj @@ -7,7 +7,6 @@ true true - @@ -36,4 +35,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Threading/src/System/Threading/Barrier.cs b/external/corefx/src/System.Threading/src/System/Threading/Barrier.cs index 13bcad0480..95f76295fc 100644 --- a/external/corefx/src/System.Threading/src/System/Threading/Barrier.cs +++ b/external/corefx/src/System.Threading/src/System/Threading/Barrier.cs @@ -21,6 +21,9 @@ namespace System.Threading /// The exception that is thrown when the post-phase action of a fails. /// [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class BarrierPostPhaseException : Exception { /// @@ -67,7 +70,6 @@ namespace System.Threading protected BarrierPostPhaseException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } @@ -133,7 +135,6 @@ namespace System.Threading private ExecutionContext _ownerThreadContext; // The EC callback that invokes the post phase action - [SecurityCritical] private static ContextCallback s_invokePostPhaseAction; // Post phase action after each phase @@ -756,7 +757,6 @@ namespace System.Threading /// last arrival thread /// /// The current phase sense - [SecuritySafeCritical] private void FinishPhase(bool observedSense) { // Execute the PHA in try/finally block to reset the variables back in case of it threw an exception @@ -806,7 +806,6 @@ namespace System.Threading /// Helper method to call the post phase action /// /// - [SecurityCritical] private static void InvokePostPhaseAction(object obj) { var thisBarrier = (Barrier)obj; diff --git a/external/corefx/src/System.Threading/src/System/Threading/CDSsyncETWBCLProvider.cs b/external/corefx/src/System.Threading/src/System/Threading/CDSsyncETWBCLProvider.cs index ddb9de23cd..53600ee0ab 100644 --- a/external/corefx/src/System.Threading/src/System/Threading/CDSsyncETWBCLProvider.cs +++ b/external/corefx/src/System.Threading/src/System/Threading/CDSsyncETWBCLProvider.cs @@ -59,7 +59,6 @@ namespace System.Threading // Barrier Events // - [SecuritySafeCritical] [Event(BARRIER_PHASEFINISHED_ID, Level = EventLevel.Verbose, Version = 1)] public void Barrier_PhaseFinished(bool currentSense, long phaseNum) { diff --git a/external/corefx/src/System.Threading/tests/ExecutionContextTests.cs b/external/corefx/src/System.Threading/tests/ExecutionContextTests.cs index 654de1302a..b339c56e3e 100644 --- a/external/corefx/src/System.Threading/tests/ExecutionContextTests.cs +++ b/external/corefx/src/System.Threading/tests/ExecutionContextTests.cs @@ -148,6 +148,7 @@ namespace System.Threading.Tests [Fact] [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] // desktop framework has a bug + [SkipOnTargetFramework(TargetFrameworkMonikers.Mono)] public static void CaptureThenSuppressThenRunFlowTest() { ThreadTestHelpers.RunTestInBackgroundThread(() => diff --git a/external/corefx/src/System.Threading/tests/Performance/System.Threading.Performance.Tests.csproj b/external/corefx/src/System.Threading/tests/Performance/System.Threading.Performance.Tests.csproj index d6a4792e82..d58dbfce23 100644 --- a/external/corefx/src/System.Threading/tests/Performance/System.Threading.Performance.Tests.csproj +++ b/external/corefx/src/System.Threading/tests/Performance/System.Threading.Performance.Tests.csproj @@ -5,9 +5,8 @@ true {7AB03FFA-7D4D-4248-9702-3D2785859C18}
- - - + + diff --git a/external/corefx/src/System.Threading/tests/SynchronizationContextTests.cs b/external/corefx/src/System.Threading/tests/SynchronizationContextTests.cs index 4c38b33991..36f1ceb981 100644 --- a/external/corefx/src/System.Threading/tests/SynchronizationContextTests.cs +++ b/external/corefx/src/System.Threading/tests/SynchronizationContextTests.cs @@ -10,6 +10,7 @@ namespace System.Threading.Tests public static class SynchronizationContextTests { [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.Mono, "SynchronizationContext.Wait(IntPtr[], bool, int) is not implemented on Mono")] public static void WaitTest() { var tsc = new TestSynchronizationContext(); @@ -37,12 +38,14 @@ namespace System.Threading.Tests [Fact] [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] // desktop framework does not check for null and crashes + [SkipOnTargetFramework(TargetFrameworkMonikers.Mono)] public static void WaitTest_ChangedInDotNetCore() { Assert.Throws(() => TestSynchronizationContext.WaitHelper(null, false, 0)); } [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.Mono, "https://bugzilla.xamarin.com/show_bug.cgi?id=60568")] public static void WaitNotificationTest() { ThreadTestHelpers.RunTestInBackgroundThread(() => diff --git a/external/corefx/src/System.Transactions.Local/ref/System.Transactions.cs b/external/corefx/src/System.Transactions.Local/ref/System.Transactions.Local.cs similarity index 99% rename from external/corefx/src/System.Transactions.Local/ref/System.Transactions.cs rename to external/corefx/src/System.Transactions.Local/ref/System.Transactions.Local.cs index 5493389df8..b20f619368 100644 --- a/external/corefx/src/System.Transactions.Local/ref/System.Transactions.cs +++ b/external/corefx/src/System.Transactions.Local/ref/System.Transactions.Local.cs @@ -200,9 +200,9 @@ namespace System.Transactions public TransactionManagerCommunicationException(string message) { } public TransactionManagerCommunicationException(string message, System.Exception innerException) { } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct TransactionOptions { + private int _dummy; public System.Transactions.IsolationLevel IsolationLevel { get { throw null; } set { } } public System.TimeSpan Timeout { get { throw null; } set { } } public override bool Equals(object obj) { throw null; } diff --git a/external/corefx/src/System.Transactions.Local/ref/System.Transactions.Local.csproj b/external/corefx/src/System.Transactions.Local/ref/System.Transactions.Local.csproj index 806d00bca0..9dc52333d8 100644 --- a/external/corefx/src/System.Transactions.Local/ref/System.Transactions.Local.csproj +++ b/external/corefx/src/System.Transactions.Local/ref/System.Transactions.Local.csproj @@ -10,7 +10,7 @@ - + diff --git a/external/corefx/src/System.Transactions.Local/src/System/Transactions/EnlistmentTraceIdentifier.cs b/external/corefx/src/System.Transactions.Local/src/System/Transactions/EnlistmentTraceIdentifier.cs index 612214bcf8..ac07bcb02b 100644 --- a/external/corefx/src/System.Transactions.Local/src/System/Transactions/EnlistmentTraceIdentifier.cs +++ b/external/corefx/src/System.Transactions.Local/src/System/Transactions/EnlistmentTraceIdentifier.cs @@ -9,7 +9,7 @@ namespace System.Transactions /// enlistments. This identifier is only unique within /// a given AppDomain. /// - internal struct EnlistmentTraceIdentifier : IEquatable + internal readonly struct EnlistmentTraceIdentifier : IEquatable { public static readonly EnlistmentTraceIdentifier Empty = new EnlistmentTraceIdentifier(); diff --git a/external/corefx/src/System.Transactions.Local/src/System/Transactions/TransactionException.cs b/external/corefx/src/System.Transactions.Local/src/System/Transactions/TransactionException.cs index 7cd2795043..261a4ef057 100644 --- a/external/corefx/src/System.Transactions.Local/src/System/Transactions/TransactionException.cs +++ b/external/corefx/src/System.Transactions.Local/src/System/Transactions/TransactionException.cs @@ -11,6 +11,9 @@ namespace System.Transactions /// Summary description for TransactionException. /// [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Transactions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class TransactionException : SystemException { internal static bool IncludeDistributedTxId(Guid distributedTxId) @@ -100,7 +103,6 @@ namespace System.Transactions /// protected TransactionException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } internal static TransactionException Create(string message, Guid distributedTxId) @@ -174,6 +176,9 @@ namespace System.Transactions /// Summary description for TransactionAbortedException. /// [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Transactions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class TransactionAbortedException : TransactionException { internal static new TransactionAbortedException Create(string message, Exception innerException, Guid distributedTxId) @@ -242,7 +247,6 @@ namespace System.Transactions /// protected TransactionAbortedException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } @@ -250,6 +254,9 @@ namespace System.Transactions /// Summary description for TransactionInDoubtException. /// [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Transactions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class TransactionInDoubtException : TransactionException { internal static new TransactionInDoubtException Create(TraceSourceType traceSource, string message, Exception innerException, Guid distributedTxId) @@ -303,7 +310,6 @@ namespace System.Transactions /// protected TransactionInDoubtException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } @@ -311,6 +317,9 @@ namespace System.Transactions /// Summary description for TransactionManagerCommunicationException. /// [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Transactions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class TransactionManagerCommunicationException : TransactionException { internal static new TransactionManagerCommunicationException Create(string message, Exception innerException) @@ -363,12 +372,13 @@ namespace System.Transactions /// protected TransactionManagerCommunicationException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } - [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Transactions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class TransactionPromotionException : TransactionException { /// @@ -402,7 +412,6 @@ namespace System.Transactions /// protected TransactionPromotionException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corefx/src/System.Transactions.Local/src/System/Transactions/TransactionTraceIdentifier.cs b/external/corefx/src/System.Transactions.Local/src/System/Transactions/TransactionTraceIdentifier.cs index ccc1456356..130e381ca2 100644 --- a/external/corefx/src/System.Transactions.Local/src/System/Transactions/TransactionTraceIdentifier.cs +++ b/external/corefx/src/System.Transactions.Local/src/System/Transactions/TransactionTraceIdentifier.cs @@ -9,7 +9,7 @@ namespace System.Transactions /// of transaction objects. This identifier is only unique within /// a given AppDomain. /// - internal struct TransactionTraceIdentifier : IEquatable + internal readonly struct TransactionTraceIdentifier : IEquatable { public static readonly TransactionTraceIdentifier Empty = new TransactionTraceIdentifier(); diff --git a/external/corefx/src/System.Transactions.Local/tests/TransactionTracingEventListener.cs b/external/corefx/src/System.Transactions.Local/tests/TransactionTracingEventListener.cs index 84a2f56b59..cc9132430a 100644 --- a/external/corefx/src/System.Transactions.Local/tests/TransactionTracingEventListener.cs +++ b/external/corefx/src/System.Transactions.Local/tests/TransactionTracingEventListener.cs @@ -1,4 +1,8 @@ -using System; +// 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.Collections.Generic; using System.Linq; using System.Text; diff --git a/external/corefx/src/System.ValueTuple/src/System.ValueTuple.csproj b/external/corefx/src/System.ValueTuple/src/System.ValueTuple.csproj index 85de8ed7b1..df0bb72a53 100644 --- a/external/corefx/src/System.ValueTuple/src/System.ValueTuple.csproj +++ b/external/corefx/src/System.ValueTuple/src/System.ValueTuple.csproj @@ -55,4 +55,4 @@ - + \ No newline at end of file diff --git a/external/corefx/src/System.Web.HttpUtility/src/System/Web/HttpUtility.cs b/external/corefx/src/System.Web.HttpUtility/src/System/Web/HttpUtility.cs index ea585a1cd6..a588021ad6 100644 --- a/external/corefx/src/System.Web.HttpUtility/src/System/Web/HttpUtility.cs +++ b/external/corefx/src/System.Web.HttpUtility/src/System/Web/HttpUtility.cs @@ -51,23 +51,22 @@ namespace System.Web { int count = Count; if (count == 0) + { return ""; + } + StringBuilder sb = new StringBuilder(); string[] keys = AllKeys; for (int i = 0; i < count; i++) { sb.AppendFormat("{0}={1}&", keys[i], UrlEncode(this[keys[i]])); } - if (sb.Length > 0) - sb.Length--; - return sb.ToString(); + + return sb.ToString(0, sb.Length - 1); } } - public static NameValueCollection ParseQueryString(string query) - { - return ParseQueryString(query, Encoding.UTF8); - } + public static NameValueCollection ParseQueryString(string query) => ParseQueryString(query, Encoding.UTF8); public static NameValueCollection ParseQueryString(string query, Encoding encoding) { @@ -129,187 +128,81 @@ namespace System.Web return result; } - public static string HtmlDecode(string s) - { - return HttpEncoder.HtmlDecode(s); - } + public static string HtmlDecode(string s) => HttpEncoder.HtmlDecode(s); + public static void HtmlDecode(string s, TextWriter output) => HttpEncoder.HtmlDecode(s, output); - public static void HtmlDecode(string s, TextWriter output) - { - HttpEncoder.HtmlDecode(s, output); - } + public static string HtmlEncode(string s) => HttpEncoder.HtmlEncode(s); + public static string HtmlEncode(object value) => + value == null ? null : HtmlEncode(Convert.ToString(value, CultureInfo.CurrentCulture)); - public static string HtmlEncode(string s) - { - return HttpEncoder.HtmlEncode(s); - } + public static void HtmlEncode(string s, TextWriter output) => HttpEncoder.HtmlEncode(s, output); + public static string HtmlAttributeEncode(string s) => HttpEncoder.HtmlAttributeEncode(s); - public static string HtmlEncode(object value) - { - if (value == null) - return null; + public static void HtmlAttributeEncode(string s, TextWriter output) => HttpEncoder.HtmlAttributeEncode(s, output); - return HtmlEncode(Convert.ToString(value, CultureInfo.CurrentCulture)); - } + public static string UrlEncode(string str) => str == null ? null : UrlEncode(str, Encoding.UTF8); + public static string UrlPathEncode(string str) => HttpEncoder.UrlPathEncode(str); - public static void HtmlEncode(string s, TextWriter output) - { - HttpEncoder.HtmlEncode(s, output); - } + public static string UrlEncode(string str, Encoding e) => + str == null ? null : Encoding.ASCII.GetString(UrlEncodeToBytes(str, e)); + public static string UrlEncode(byte[] bytes) => bytes == null ? null : Encoding.ASCII.GetString(UrlEncodeToBytes(bytes)); - public static string HtmlAttributeEncode(string s) - { - return HttpEncoder.HtmlAttributeEncode(s); - } + public static string UrlEncode(byte[] bytes, int offset, int count) => bytes == null ? null : Encoding.ASCII.GetString(UrlEncodeToBytes(bytes, offset, count)); + public static byte[] UrlEncodeToBytes(string str) => str == null ? null : UrlEncodeToBytes(str, Encoding.UTF8); - public static void HtmlAttributeEncode(string s, TextWriter output) - { - HttpEncoder.HtmlAttributeEncode(s, output); - } - - public static string UrlEncode(string str) - { - if (str == null) - return null; - return UrlEncode(str, Encoding.UTF8); - } - - - public static string UrlPathEncode(string str) - { - return HttpEncoder.UrlPathEncode(str); - } - - public static string UrlEncode(string str, Encoding e) - { - if (str == null) - return null; - return Encoding.ASCII.GetString(UrlEncodeToBytes(str, e)); - } - - public static string UrlEncode(byte[] bytes) - { - if (bytes == null) - return null; - return Encoding.ASCII.GetString(UrlEncodeToBytes(bytes)); - } - - public static string UrlEncode(byte[] bytes, int offset, int count) - { - if (bytes == null) - return null; - return Encoding.ASCII.GetString(UrlEncodeToBytes(bytes, offset, count)); - } - - public static byte[] UrlEncodeToBytes(string str) - { - if (str == null) - return null; - return UrlEncodeToBytes(str, Encoding.UTF8); - } - - public static byte[] UrlEncodeToBytes(byte[] bytes) - { - if (bytes == null) - return null; - return UrlEncodeToBytes(bytes, 0, bytes.Length); - } + public static byte[] UrlEncodeToBytes(byte[] bytes) => bytes == null ? null : UrlEncodeToBytes(bytes, 0, bytes.Length); [Obsolete( "This method produces non-standards-compliant output and has interoperability issues. The preferred alternative is UrlEncodeToBytes(String)." )] - public static byte[] UrlEncodeUnicodeToBytes(string str) - { - if (str == null) - return null; - return Encoding.ASCII.GetBytes(UrlEncodeUnicode(str)); - } + public static byte[] UrlEncodeUnicodeToBytes(string str) => str == null ? null : Encoding.ASCII.GetBytes(UrlEncodeUnicode(str)); - public static string UrlDecode(string str) - { - if (str == null) - return null; - return UrlDecode(str, Encoding.UTF8); - } + public static string UrlDecode(string str) => str == null ? null : UrlDecode(str, Encoding.UTF8); - public static string UrlDecode(byte[] bytes, Encoding e) - { - if (bytes == null) - return null; - return UrlDecode(bytes, 0, bytes.Length, e); - } + public static string UrlDecode(byte[] bytes, Encoding e) => bytes == null ? null : UrlDecode(bytes, 0, bytes.Length, e); - public static byte[] UrlDecodeToBytes(string str) - { - if (str == null) - return null; - return UrlDecodeToBytes(str, Encoding.UTF8); - } + public static byte[] UrlDecodeToBytes(string str) => str == null ? null : UrlDecodeToBytes(str, Encoding.UTF8); - public static byte[] UrlDecodeToBytes(string str, Encoding e) - { - if (str == null) - return null; - return UrlDecodeToBytes(e.GetBytes(str)); - } + public static byte[] UrlDecodeToBytes(string str, Encoding e) => str == null ? null : UrlDecodeToBytes(e.GetBytes(str)); - public static byte[] UrlDecodeToBytes(byte[] bytes) - { - if (bytes == null) - return null; - return UrlDecodeToBytes(bytes, 0, bytes != null ? bytes.Length : 0); - } + public static byte[] UrlDecodeToBytes(byte[] bytes) => bytes == null ? null : UrlDecodeToBytes(bytes, 0, bytes.Length); public static byte[] UrlEncodeToBytes(string str, Encoding e) { if (str == null) + { return null; - var bytes = e.GetBytes(str); - return HttpEncoder.UrlEncode(bytes, 0, bytes.Length, false /* alwaysCreateNewReturnValue */); + } + + byte[] bytes = e.GetBytes(str); + return HttpEncoder.UrlEncode(bytes, 0, bytes.Length, alwaysCreateNewReturnValue: false); } - public static byte[] UrlEncodeToBytes(byte[] bytes, int offset, int count) - { - return HttpEncoder.UrlEncode(bytes, offset, count, true /* alwaysCreateNewReturnValue */); - } + public static byte[] UrlEncodeToBytes(byte[] bytes, int offset, int count) => HttpEncoder.UrlEncode(bytes, offset, count, alwaysCreateNewReturnValue: true); [Obsolete( "This method produces non-standards-compliant output and has interoperability issues. The preferred alternative is UrlEncode(String)." )] - public static string UrlEncodeUnicode(string str) - { - return HttpEncoder.UrlEncodeUnicode(str, false /* ignoreAscii */); - } + public static string UrlEncodeUnicode(string str) => HttpEncoder.UrlEncodeUnicode(str); - public static string UrlDecode(string str, Encoding e) - { - return HttpEncoder.UrlDecode(str, e); - } + public static string UrlDecode(string str, Encoding e) => HttpEncoder.UrlDecode(str, e); - public static string UrlDecode(byte[] bytes, int offset, int count, Encoding e) - { - return HttpEncoder.UrlDecode(bytes, offset, count, e); - } + public static string UrlDecode(byte[] bytes, int offset, int count, Encoding e) => + HttpEncoder.UrlDecode(bytes, offset, count, e); - public static byte[] UrlDecodeToBytes(byte[] bytes, int offset, int count) - { - return HttpEncoder.UrlDecode(bytes, offset, count); - } + public static byte[] UrlDecodeToBytes(byte[] bytes, int offset, int count) => HttpEncoder.UrlDecode(bytes, offset, count); - public static string JavaScriptStringEncode(string value) - { - return JavaScriptStringEncode(value, false); - } + public static string JavaScriptStringEncode(string value) => HttpEncoder.JavaScriptStringEncode(value); public static string JavaScriptStringEncode(string value, bool addDoubleQuotes) { - var encoded = HttpEncoder.JavaScriptStringEncode(value); + string encoded = HttpEncoder.JavaScriptStringEncode(value); return addDoubleQuotes ? "\"" + encoded + "\"" : encoded; } } diff --git a/external/corefx/src/System.Web.HttpUtility/src/System/Web/Util/HttpEncoder.cs b/external/corefx/src/System.Web.HttpUtility/src/System/Web/Util/HttpEncoder.cs index 8458b6b51f..550bbe07e1 100644 --- a/external/corefx/src/System.Web.HttpUtility/src/System/Web/Util/HttpEncoder.cs +++ b/external/corefx/src/System.Web.HttpUtility/src/System/Web/Util/HttpEncoder.cs @@ -19,9 +19,8 @@ namespace System.Web.Util builder.Append(((int)c).ToString("x4", CultureInfo.InvariantCulture)); } - private static bool CharRequiresJavaScriptEncoding(char c) - { - return c < 0x20 // control chars always have to be encoded + private static bool CharRequiresJavaScriptEncoding(char c) => + c < 0x20 // control chars always have to be encoded || c == '\"' // chars which must be encoded per JSON spec || c == '\\' || c == '\'' // HTML-sensitive chars encoded for safety @@ -31,7 +30,6 @@ namespace System.Web.Util || c == '\u0085' // newline chars (see Unicode 6.2, Table 5-1 [http://www.unicode.org/versions/Unicode6.2.0/ch05.pdf]) have to be encoded || c == '\u2028' || c == '\u2029'; - } internal static string HtmlAttributeEncode(string value) { @@ -117,36 +115,27 @@ namespace System.Web.Util } } - internal static string HtmlDecode(string value) - { - if (string.IsNullOrEmpty(value)) - { - return value; - } - - return WebUtility.HtmlDecode(value); - } + internal static string HtmlDecode(string value) => string.IsNullOrEmpty(value) ? value : WebUtility.HtmlDecode(value); internal static void HtmlDecode(string value, TextWriter output) { if (output == null) + { throw new ArgumentNullException(nameof(output)); + } + output.Write(WebUtility.HtmlDecode(value)); } - internal static string HtmlEncode(string value) - { - if (string.IsNullOrEmpty(value)) - { - return value; - } - return WebUtility.HtmlEncode(value); - } + internal static string HtmlEncode(string value) => string.IsNullOrEmpty(value) ? value : WebUtility.HtmlEncode(value); internal static void HtmlEncode(string value, TextWriter output) { if (output == null) + { throw new ArgumentNullException(nameof(output)); + } + output.Write(WebUtility.HtmlEncode(value)); } @@ -176,10 +165,7 @@ namespace System.Web.Util return -1; } - private static bool IsNonAsciiByte(byte b) - { - return (b >= 0x7F || b < 0x20); - } + private static bool IsNonAsciiByte(byte b) => b >= 0x7F || b < 0x20; internal static string JavaScriptStringEncode(string value) { @@ -211,41 +197,38 @@ namespace System.Web.Util startIndex = i + 1; count = 0; - } - switch (c) - { - case '\r': - b.Append("\\r"); - break; - case '\t': - b.Append("\\t"); - break; - case '\"': - b.Append("\\\""); - break; - case '\\': - b.Append("\\\\"); - break; - case '\n': - b.Append("\\n"); - break; - case '\b': - b.Append("\\b"); - break; - case '\f': - b.Append("\\f"); - break; - default: - if (CharRequiresJavaScriptEncoding(c)) - { + switch (c) + { + case '\r': + b.Append("\\r"); + break; + case '\t': + b.Append("\\t"); + break; + case '\"': + b.Append("\\\""); + break; + case '\\': + b.Append("\\\\"); + break; + case '\n': + b.Append("\\n"); + break; + case '\b': + b.Append("\\b"); + break; + case '\f': + b.Append("\\f"); + break; + default: AppendCharAsUnicodeJavaScript(b, c); - } - else - { - count++; - } - break; + break; + } + } + else + { + count++; } } @@ -465,9 +448,13 @@ namespace System.Web.Util char ch = (char)bytes[offset + i]; if (ch == ' ') + { cSpaces++; + } else if (!HttpEncoderUtility.IsUrlSafeChar(ch)) + { cUnsafe++; + } } // nothing to expand? @@ -480,7 +467,7 @@ namespace System.Web.Util } else { - var subarray = new byte[count]; + byte[] subarray = new byte[count]; Buffer.BlockCopy(bytes, offset, subarray, 0, count); return subarray; } @@ -517,34 +504,31 @@ namespace System.Web.Util // Helper to encode the non-ASCII url characters only private static string UrlEncodeNonAscii(string str, Encoding e) { - if (string.IsNullOrEmpty(str)) - return str; - if (e == null) - e = Encoding.UTF8; + Debug.Assert(!string.IsNullOrEmpty(str)); + Debug.Assert(e != null); byte[] bytes = e.GetBytes(str); - byte[] encodedBytes = UrlEncodeNonAscii(bytes, 0, bytes.Length, false /* alwaysCreateNewReturnValue */); + byte[] encodedBytes = UrlEncodeNonAscii(bytes, 0, bytes.Length); return Encoding.ASCII.GetString(encodedBytes); } - private static byte[] UrlEncodeNonAscii(byte[] bytes, int offset, int count, bool alwaysCreateNewReturnValue) + private static byte[] UrlEncodeNonAscii(byte[] bytes, int offset, int count) { - if (!ValidateUrlEncodingParameters(bytes, offset, count)) - { - return null; - } - int cNonAscii = 0; // count them first for (int i = 0; i < count; i++) { if (IsNonAsciiByte(bytes[offset + i])) + { cNonAscii++; + } } // nothing to expand? - if (!alwaysCreateNewReturnValue && cNonAscii == 0) + if (cNonAscii == 0) + { return bytes; + } // expand not 'safe' characters into %XX, spaces to +s byte[] expandedBytes = new byte[count + cNonAscii * 2]; @@ -570,7 +554,7 @@ namespace System.Web.Util } [Obsolete("This method produces non-standards-compliant output and has interoperability issues. The preferred alternative is UrlEncode(*).")] - internal static string UrlEncodeUnicode(string value, bool ignoreAscii) + internal static string UrlEncodeUnicode(string value) { if (value == null) { @@ -586,7 +570,7 @@ namespace System.Web.Util if ((ch & 0xff80) == 0) { // 7 bit? - if (ignoreAscii || HttpEncoderUtility.IsUrlSafeChar(ch)) + if (HttpEncoderUtility.IsUrlSafeChar(ch)) { sb.Append(ch); } @@ -654,7 +638,9 @@ namespace System.Web.Util // recurse in case there is a query string int i = value.IndexOf('?'); if (i >= 0) + { return UrlPathEncodeImpl(value.Substring(0, i)) + value.Substring(i); + } // encode DBCS characters and spaces only return HttpEncoderUtility.UrlEncodeSpaces(UrlEncodeNonAscii(value, Encoding.UTF8)); @@ -663,7 +649,10 @@ namespace System.Web.Util private static bool ValidateUrlEncodingParameters(byte[] bytes, int offset, int count) { if (bytes == null && count == 0) + { return false; + } + if (bytes == null) { throw new ArgumentNullException(nameof(bytes)); @@ -717,7 +706,9 @@ namespace System.Web.Util internal void AddChar(char ch) { if (_numBytes > 0) + { FlushBytes(); + } _charBuffer[_numChars++] = ch; } @@ -734,7 +725,9 @@ namespace System.Web.Util */ { if (_byteBuffer == null) + { _byteBuffer = new byte[_bufferSize]; + } _byteBuffer[_numBytes++] = b; } @@ -743,12 +736,11 @@ namespace System.Web.Util internal string GetString() { if (_numBytes > 0) + { FlushBytes(); + } - if (_numChars > 0) - return new string(_charBuffer, 0, _numChars); - else - return string.Empty; + return _numChars > 0 ? new string(_charBuffer, 0, _numChars) : ""; } } } diff --git a/external/corefx/src/System.Web.HttpUtility/src/System/Web/Util/HttpEncoderUtility.cs b/external/corefx/src/System.Web.HttpUtility/src/System/Web/Util/HttpEncoderUtility.cs index e2e1ad70a1..5582b1a286 100644 --- a/external/corefx/src/System.Web.HttpUtility/src/System/Web/Util/HttpEncoderUtility.cs +++ b/external/corefx/src/System.Web.HttpUtility/src/System/Web/Util/HttpEncoderUtility.cs @@ -8,29 +8,29 @@ namespace System.Web.Util { internal static class HttpEncoderUtility { - public static int HexToInt(char h) - { - return (h >= '0' && h <= '9') ? h - '0' : - (h >= 'a' && h <= 'f') ? h - 'a' + 10 : - (h >= 'A' && h <= 'F') ? h - 'A' + 10 : - -1; - } + public static int HexToInt(char h) => + h >= '0' && h <= '9' + ? h - '0' + : h >= 'a' && h <= 'f' + ? h - 'a' + 10 + : h >= 'A' && h <= 'F' + ? h - 'A' + 10 + : -1; public static char IntToHex(int n) { Debug.Assert(n < 0x10); - if (n <= 9) - return (char)(n + (int)'0'); - else - return (char)(n - 10 + (int)'a'); + return n <= 9 ? (char)(n + '0') : (char)(n - 10 + 'a'); } // Set of safe chars, from RFC 1738.4 minus '+' public static bool IsUrlSafeChar(char ch) { if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) + { return true; + } switch (ch) { @@ -48,11 +48,6 @@ namespace System.Web.Util } // Helper to encode spaces only - internal static string UrlEncodeSpaces(string str) - { - if (str != null && str.IndexOf(' ') >= 0) - str = str.Replace(" ", "%20"); - return str; - } + internal static string UrlEncodeSpaces(string str) => str != null && str.IndexOf(' ') >= 0 ? str.Replace(" ", "%20") : str; } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Web.HttpUtility/src/System/Web/Util/Utf16StringValidator.cs b/external/corefx/src/System.Web.HttpUtility/src/System/Web/Util/Utf16StringValidator.cs index 0544fcfaa2..a852972a01 100644 --- a/external/corefx/src/System.Web.HttpUtility/src/System/Web/Util/Utf16StringValidator.cs +++ b/external/corefx/src/System.Web.HttpUtility/src/System/Web/Util/Utf16StringValidator.cs @@ -6,7 +6,7 @@ namespace System.Web.Util { internal static class Utf16StringValidator { - private const char UNICODE_REPLACEMENT_CHAR = '\uFFFD'; + private const char UnicodeReplacementChar = '\uFFFD'; internal static string ValidateString(string input) { @@ -42,7 +42,7 @@ namespace System.Web.Util // a high surrogate, so we'll replace it. if (char.IsLowSurrogate(thisChar)) { - chars[i] = UNICODE_REPLACEMENT_CHAR; + chars[i] = UnicodeReplacementChar; continue; } @@ -58,7 +58,7 @@ namespace System.Web.Util // If this character is a high surrogate and it is not followed // by a low surrogate, replace it. - chars[i] = UNICODE_REPLACEMENT_CHAR; + chars[i] = UnicodeReplacementChar; continue; } @@ -68,4 +68,4 @@ namespace System.Web.Util return new string(chars); } } -} \ No newline at end of file +} diff --git a/external/corefx/src/System.Web.HttpUtility/tests/HttpUtility/HttpUtilityTest.cs b/external/corefx/src/System.Web.HttpUtility/tests/HttpUtility/HttpUtilityTest.cs index aabfd4fd9b..0a5bb18d24 100644 --- a/external/corefx/src/System.Web.HttpUtility/tests/HttpUtility/HttpUtilityTest.cs +++ b/external/corefx/src/System.Web.HttpUtility/tests/HttpUtility/HttpUtilityTest.cs @@ -349,6 +349,14 @@ namespace System.Web.Tests Assert.Equal("\"" + encoded + "\"", HttpUtility.JavaScriptStringEncode(decoded, true)); } + + [Theory] + [MemberData(nameof(JavaScriptStringEncodeData))] + public void JavaScriptStringEncode_ExplicitDontAddQuotes(string decoded, string encoded) + { + Assert.Equal(encoded, HttpUtility.JavaScriptStringEncode(decoded, false)); + } + #endregion JavaScriptStringEncode #region ParseQueryString @@ -740,6 +748,7 @@ namespace System.Web.Tests [InlineData("http://EXAMPLE.NET/défault.xxx?sdsd=sds", "http://EXAMPLE.NET/d%c3%a9fault.xxx?sdsd=sds")] [InlineData("file:///C/Users", "file:///C/Users")] [InlineData("mailto:user@example.net", "mailto:user@example.net")] + [InlineData("http://example\u200E.net/", "http://example%e2%80%8e.net/")] public void UrlPathEncode(string decoded, string encoded) { Assert.Equal(encoded, HttpUtility.UrlPathEncode(decoded)); diff --git a/external/corefx/src/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs.REMOVED.git-id b/external/corefx/src/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs.REMOVED.git-id index a192423f64..f096d14695 100644 --- a/external/corefx/src/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs.REMOVED.git-id +++ b/external/corefx/src/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs.REMOVED.git-id @@ -1 +1 @@ -c909f9406c47791731c7273d49bba9f0bedd51b2 \ No newline at end of file +1665c22d6ad97d0752f1f3a96df235ef146aec3a \ No newline at end of file diff --git a/external/corefx/src/System.Xml.ReaderWriter/src/System.Xml.ReaderWriter.csproj b/external/corefx/src/System.Xml.ReaderWriter/src/System.Xml.ReaderWriter.csproj index 0ab5657068..25fdb8ef2e 100644 --- a/external/corefx/src/System.Xml.ReaderWriter/src/System.Xml.ReaderWriter.csproj +++ b/external/corefx/src/System.Xml.ReaderWriter/src/System.Xml.ReaderWriter.csproj @@ -8,7 +8,6 @@ true None - diff --git a/external/corefx/src/System.Xml.XDocument/src/System.Xml.XDocument.csproj b/external/corefx/src/System.Xml.XDocument/src/System.Xml.XDocument.csproj index f1ff780381..33e397e2e0 100644 --- a/external/corefx/src/System.Xml.XDocument/src/System.Xml.XDocument.csproj +++ b/external/corefx/src/System.Xml.XDocument/src/System.Xml.XDocument.csproj @@ -8,7 +8,6 @@ true None - diff --git a/external/corefx/src/System.Xml.XPath.XDocument/src/System.Xml.XPath.XDocument.csproj b/external/corefx/src/System.Xml.XPath.XDocument/src/System.Xml.XPath.XDocument.csproj index 56cf122a1d..c5ddabd0ab 100644 --- a/external/corefx/src/System.Xml.XPath.XDocument/src/System.Xml.XPath.XDocument.csproj +++ b/external/corefx/src/System.Xml.XPath.XDocument/src/System.Xml.XPath.XDocument.csproj @@ -7,7 +7,6 @@ true true - diff --git a/external/corefx/src/System.Xml.XPath/src/System.Xml.XPath.csproj b/external/corefx/src/System.Xml.XPath/src/System.Xml.XPath.csproj index 7791d424bc..53d76aa538 100644 --- a/external/corefx/src/System.Xml.XPath/src/System.Xml.XPath.csproj +++ b/external/corefx/src/System.Xml.XPath/src/System.Xml.XPath.csproj @@ -7,7 +7,6 @@ true None - diff --git a/external/corefx/src/System.Xml.XmlDocument/src/System.Xml.XmlDocument.csproj b/external/corefx/src/System.Xml.XmlDocument/src/System.Xml.XmlDocument.csproj index 8cb93a54e2..1ed354e293 100644 --- a/external/corefx/src/System.Xml.XmlDocument/src/System.Xml.XmlDocument.csproj +++ b/external/corefx/src/System.Xml.XmlDocument/src/System.Xml.XmlDocument.csproj @@ -9,7 +9,6 @@ true None - diff --git a/external/corefx/src/System.Xml.XmlSerializer/ref/System.Xml.XmlSerializer.cs b/external/corefx/src/System.Xml.XmlSerializer/ref/System.Xml.XmlSerializer.cs index cf5b8e49c2..104b78a53d 100644 --- a/external/corefx/src/System.Xml.XmlSerializer/ref/System.Xml.XmlSerializer.cs +++ b/external/corefx/src/System.Xml.XmlSerializer/ref/System.Xml.XmlSerializer.cs @@ -251,9 +251,9 @@ namespace System.Xml.Serialization public XmlChoiceIdentifierAttribute(string name) { } public string MemberName { get { throw null; } set { } } } - [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct XmlDeserializationEvents { + private object _dummy; public System.Xml.Serialization.XmlAttributeEventHandler OnUnknownAttribute { get { throw null; } set { } } public System.Xml.Serialization.XmlElementEventHandler OnUnknownElement { get { throw null; } set { } } public System.Xml.Serialization.XmlNodeEventHandler OnUnknownNode { get { throw null; } set { } } diff --git a/external/corefx/src/dirs.proj b/external/corefx/src/dirs.proj index efccf72b28..e98055b274 100644 --- a/external/corefx/src/dirs.proj +++ b/external/corefx/src/dirs.proj @@ -15,6 +15,30 @@ + + + + + + + + + + <_sharedFrameworkDepsJson Include="$(ToolsDir)dotnetcli\shared\Microsoft.NETCore.App\*\Microsoft.NETCore.App.deps.json" /> + + + + <_OriginalDepsJsonPath>%(_sharedFrameworkDepsJson.FullPath) + <_OutputTestSharedFrameworkDepsPath>$(NETCoreAppTestSharedFrameworkPath)\Microsoft.NETCore.App.deps.json + + + + + diff --git a/external/corefx/src/microsoft.csharp/src/microsoft/csharp/runtimebinder/semantics/types/typeparametertype.cs b/external/corefx/src/microsoft.csharp/src/microsoft/csharp/runtimebinder/semantics/types/typeparametertype.cs deleted file mode 100644 index 8d684daca4..0000000000 --- a/external/corefx/src/microsoft.csharp/src/microsoft/csharp/runtimebinder/semantics/types/typeparametertype.cs +++ /dev/null @@ -1,35 +0,0 @@ -// 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.Diagnostics; - -namespace Microsoft.CSharp.RuntimeBinder.Semantics -{ - ///////////////////////////////////////////////////////////////////////////////// - - internal sealed class TypeParameterType : CType - { - public TypeParameterSymbol GetTypeParameterSymbol() { return _pTypeParameterSymbol; } - public void SetTypeParameterSymbol(TypeParameterSymbol pTypePArameterSymbol) { _pTypeParameterSymbol = pTypePArameterSymbol; } - - public ParentSymbol GetOwningSymbol() { return _pTypeParameterSymbol.parent; } - - // Forward calls into the symbol. - public bool Covariant { get { return _pTypeParameterSymbol.Covariant; } } - public bool Invariant { get { return _pTypeParameterSymbol.Invariant; } } - public bool Contravariant { get { return _pTypeParameterSymbol.Contravariant; } } - public bool IsValueType() { return _pTypeParameterSymbol.IsValueType(); } - public bool IsReferenceType() { return _pTypeParameterSymbol.IsReferenceType(); } - public bool IsNonNullableValueType() { return _pTypeParameterSymbol.IsNonNullableValueType(); } - public bool HasNewConstraint() { return _pTypeParameterSymbol.HasNewConstraint(); } - public bool HasRefConstraint() { return _pTypeParameterSymbol.HasRefConstraint(); } - public bool HasValConstraint() { return _pTypeParameterSymbol.HasValConstraint(); } - public bool IsMethodTypeParameter() { return _pTypeParameterSymbol.IsMethodTypeParameter(); } - public int GetIndexInOwnParameters() { return _pTypeParameterSymbol.GetIndexInOwnParameters(); } - public int GetIndexInTotalParameters() { return _pTypeParameterSymbol.GetIndexInTotalParameters(); } - public TypeArray GetBounds() { return _pTypeParameterSymbol.GetBounds(); } - - private TypeParameterSymbol _pTypeParameterSymbol; - } -} diff --git a/external/corefx/src/publish.proj b/external/corefx/src/publish.proj index 8bcf4c83d9..90c42841de 100644 --- a/external/corefx/src/publish.proj +++ b/external/corefx/src/publish.proj @@ -3,9 +3,10 @@ + - $(PackageOutputRoot)**\*.nupkg + $(PackageOutputRoot)\**\*.nupkg @@ -23,5 +24,45 @@ - - + + + + $(PackagesDir)AzureTransfer\$(ConfigurationGroup) + $(PackageDownloadDirectory)\**\*.nupkg + $(PackageDownloadDirectory)\**\*Private*.nupkg + $(PackageDownloadDirectory)\**\*.symbols.nupkg + + + + + + NonShipping=true + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/shims/ApiCompatBaseline.netcoreapp.netfx461.ignore.txt.REMOVED.git-id b/external/corefx/src/shims/ApiCompatBaseline.netcoreapp.netfx461.ignore.txt.REMOVED.git-id index e5c4c364de..6fa5fb9910 100644 --- a/external/corefx/src/shims/ApiCompatBaseline.netcoreapp.netfx461.ignore.txt.REMOVED.git-id +++ b/external/corefx/src/shims/ApiCompatBaseline.netcoreapp.netfx461.ignore.txt.REMOVED.git-id @@ -1 +1 @@ -6ded52988e80345d9d592503be2226eff0ef978f \ No newline at end of file +aac056d31f899a6b3eb69723a149250214905d87 \ No newline at end of file diff --git a/external/corefx/src/shims/ApiCompatBaseline.netcoreapp.netfx461.txt b/external/corefx/src/shims/ApiCompatBaseline.netcoreapp.netfx461.txt index 7ac14f39f1..93fe848308 100644 --- a/external/corefx/src/shims/ApiCompatBaseline.netcoreapp.netfx461.txt +++ b/external/corefx/src/shims/ApiCompatBaseline.netcoreapp.netfx461.txt @@ -4,8 +4,8 @@ ApiCompat Error: 0 : Unable to resolve assembly 'Assembly(Name=System.ServiceMod ApiCompat Error: 0 : Unable to resolve assembly 'Assembly(Name=System.Xaml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)' referenced by the contract assembly 'Assembly(Name=WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35)'. ApiCompat Error: 0 : Unable to resolve assembly 'Assembly(Name=netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51)' referenced by the contract assembly 'Assembly(Name=System.DirectoryServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a)'. ApiCompat Error: 0 : Unable to resolve assembly 'Assembly(Name=netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51)' referenced by the contract assembly 'Assembly(Name=System.DirectoryServices.AccountManagement, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)'. -ApiCompat Error: 0 : Unable to resolve assembly 'Assembly(Name=System.Security.AccessControl, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a)' referenced by the contract assembly 'Assembly(Name=System.DirectoryServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a)'. ApiCompat Error: 0 : Unable to resolve assembly 'Assembly(Name=System.IO.FileSystem.AccessControl, Version=4.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a)' referenced by the contract assembly 'Assembly(Name=System.DirectoryServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a)'. +ApiCompat Error: 0 : Unable to resolve assembly 'Assembly(Name=System.Security.AccessControl, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a)' referenced by the contract assembly 'Assembly(Name=System.DirectoryServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a)'. ApiCompat Error: 0 : Unable to resolve assembly 'Assembly(Name=netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51)' referenced by the contract assembly 'Assembly(Name=System.DirectoryServices.Protocols, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a)'. Compat issues with assembly System: MembersMustExist : Member 'System.CodeDom.Compiler.CompilerParameters.Evidence.get()' does not exist in the implementation but it does exist in the contract. @@ -69,8 +69,6 @@ MembersMustExist : Member 'System.Data.SqlClient.SqlConnectionStringBuilder.Tran MembersMustExist : Member 'System.Data.SqlClient.SqlDataReader.IsCommandBehavior(System.Data.CommandBehavior)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'System.Data.SqlClient.SqlParameter.ForceColumnEncryption.get()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'System.Data.SqlClient.SqlParameter.ForceColumnEncryption.set(System.Boolean)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Data.SqlClient.SqlParameter.UdtTypeName.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.Data.SqlClient.SqlParameter.UdtTypeName.set(System.String)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'System.Data.SqlClient.SqlParameterCollection.Add(System.String, System.Object)' does not exist in the implementation but it does exist in the contract. Compat issues with assembly System.Security: MembersMustExist : Member 'System.Security.Cryptography.CryptographicAttributeObjectCollection.IsSynchronized.get()' does not exist in the implementation but it does exist in the contract. @@ -131,7 +129,6 @@ TypesMustExist : Type 'System.ComponentModel.SortDescription' does not exist in TypesMustExist : Type 'System.ComponentModel.SortDescriptionCollection' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'System.Diagnostics.PresentationTraceLevel' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'System.Diagnostics.PresentationTraceSources' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'System.IO.FileFormatException..ctor(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'System.IO.Packaging.CertificateEmbeddingOption' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'System.IO.Packaging.EncryptedPackageEnvelope' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'System.IO.Packaging.InvalidSignatureEventHandler' does not exist in the implementation but it does exist in the contract. @@ -256,4 +253,4 @@ TypesMustExist : Type 'System.Windows.Threading.DispatcherUnhandledExceptionEven TypesMustExist : Type 'System.Windows.Threading.DispatcherUnhandledExceptionEventHandler' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'System.Windows.Threading.DispatcherUnhandledExceptionFilterEventArgs' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'System.Windows.Threading.DispatcherUnhandledExceptionFilterEventHandler' does not exist in the implementation but it does exist in the contract. -Total Issues: 242 +Total Issues: 239 diff --git a/external/corefx/src/shims/ApiCompatBaseline.netcoreapp.netstandard20.txt b/external/corefx/src/shims/ApiCompatBaseline.netcoreapp.netstandard20.txt index ea5a272ae0..b68ab3b6eb 100644 --- a/external/corefx/src/shims/ApiCompatBaseline.netcoreapp.netstandard20.txt +++ b/external/corefx/src/shims/ApiCompatBaseline.netcoreapp.netstandard20.txt @@ -1,5 +1,7 @@ Compat issues with assembly mscorlib: +TypeCannotChangeClassification : Type 'System.RuntimeArgumentHandle' is a 'ref struct' in the implementation but is a 'struct' in the contract. TypesMustExist : Type 'System.TupleExtensions' does not exist in the implementation but it does exist in the contract. +TypeCannotChangeClassification : Type 'System.TypedReference' is a 'ref struct' in the implementation but is a 'struct' in the contract. TypesMustExist : Type 'System.ValueTuple' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'System.ValueTuple' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'System.ValueTuple' does not exist in the implementation but it does exist in the contract. @@ -10,8 +12,11 @@ TypesMustExist : Type 'System.ValueTuple' does not exist TypesMustExist : Type 'System.ValueTuple' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'System.ValueTuple' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'System.Runtime.CompilerServices.TupleElementNamesAttribute' does not exist in the implementation but it does exist in the contract. +Compat issues with assembly netstandard: +TypeCannotChangeClassification : Type 'System.RuntimeArgumentHandle' is a 'ref struct' in the implementation but is a 'struct' in the contract. +TypeCannotChangeClassification : Type 'System.TypedReference' is a 'ref struct' in the implementation but is a 'struct' in the contract. Compat issues with assembly System.Core: TypesMustExist : Type 'System.Security.Cryptography.ECCurve' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'System.Security.Cryptography.ECParameters' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'System.Security.Cryptography.ECPoint' does not exist in the implementation but it does exist in the contract. -Total Issues: 14 +Total Issues: 18 diff --git a/external/corefx/src/shims/ApiCompatBaseline.netcoreapp.netstandard20Only.txt b/external/corefx/src/shims/ApiCompatBaseline.netcoreapp.netstandard20Only.txt index fcc74cf864..b5b3f20d60 100644 --- a/external/corefx/src/shims/ApiCompatBaseline.netcoreapp.netstandard20Only.txt +++ b/external/corefx/src/shims/ApiCompatBaseline.netcoreapp.netstandard20Only.txt @@ -1 +1,4 @@ -Total Issues: 0 +Compat issues with assembly netstandard: +TypeCannotChangeClassification : Type 'System.RuntimeArgumentHandle' is a 'ref struct' in the implementation but is a 'struct' in the contract. +TypeCannotChangeClassification : Type 'System.TypedReference' is a 'ref struct' in the implementation but is a 'struct' in the contract. +Total Issues: 2 diff --git a/external/corefx/src/shims/ApiCompatBaseline.uap.netstandard20.txt b/external/corefx/src/shims/ApiCompatBaseline.uap.netstandard20.txt index 55db3229c1..b6d9449567 100644 --- a/external/corefx/src/shims/ApiCompatBaseline.uap.netstandard20.txt +++ b/external/corefx/src/shims/ApiCompatBaseline.uap.netstandard20.txt @@ -1,8 +1,13 @@ Compat issues with assembly mscorlib: +TypeCannotChangeClassification : Type 'System.RuntimeArgumentHandle' is a 'ref struct' in the implementation but is a 'struct' in the contract. TypesMustExist : Type 'System.TupleExtensions' does not exist in the implementation but it does exist in the contract. +TypeCannotChangeClassification : Type 'System.TypedReference' is a 'ref struct' in the implementation but is a 'struct' in the contract. TypesMustExist : Type 'System.Runtime.CompilerServices.TupleElementNamesAttribute' does not exist in the implementation but it does exist in the contract. +Compat issues with assembly netstandard: +TypeCannotChangeClassification : Type 'System.RuntimeArgumentHandle' is a 'ref struct' in the implementation but is a 'struct' in the contract. +TypeCannotChangeClassification : Type 'System.TypedReference' is a 'ref struct' in the implementation but is a 'struct' in the contract. Compat issues with assembly System.Core: TypesMustExist : Type 'System.Security.Cryptography.ECCurve' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'System.Security.Cryptography.ECParameters' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'System.Security.Cryptography.ECPoint' does not exist in the implementation but it does exist in the contract. -Total Issues: 5 +Total Issues: 9 diff --git a/external/corefx/src/shims/ApiCompatBaseline.uap.netstandard20Only.txt b/external/corefx/src/shims/ApiCompatBaseline.uap.netstandard20Only.txt index fcc74cf864..b5b3f20d60 100644 --- a/external/corefx/src/shims/ApiCompatBaseline.uap.netstandard20Only.txt +++ b/external/corefx/src/shims/ApiCompatBaseline.uap.netstandard20Only.txt @@ -1 +1,4 @@ -Total Issues: 0 +Compat issues with assembly netstandard: +TypeCannotChangeClassification : Type 'System.RuntimeArgumentHandle' is a 'ref struct' in the implementation but is a 'struct' in the contract. +TypeCannotChangeClassification : Type 'System.TypedReference' is a 'ref struct' in the implementation but is a 'struct' in the contract. +Total Issues: 2 diff --git a/external/corefx/src/shims/ApiCompatBaseline.uapaot.netstandard20.txt b/external/corefx/src/shims/ApiCompatBaseline.uapaot.netstandard20.txt index 55db3229c1..b6d9449567 100644 --- a/external/corefx/src/shims/ApiCompatBaseline.uapaot.netstandard20.txt +++ b/external/corefx/src/shims/ApiCompatBaseline.uapaot.netstandard20.txt @@ -1,8 +1,13 @@ Compat issues with assembly mscorlib: +TypeCannotChangeClassification : Type 'System.RuntimeArgumentHandle' is a 'ref struct' in the implementation but is a 'struct' in the contract. TypesMustExist : Type 'System.TupleExtensions' does not exist in the implementation but it does exist in the contract. +TypeCannotChangeClassification : Type 'System.TypedReference' is a 'ref struct' in the implementation but is a 'struct' in the contract. TypesMustExist : Type 'System.Runtime.CompilerServices.TupleElementNamesAttribute' does not exist in the implementation but it does exist in the contract. +Compat issues with assembly netstandard: +TypeCannotChangeClassification : Type 'System.RuntimeArgumentHandle' is a 'ref struct' in the implementation but is a 'struct' in the contract. +TypeCannotChangeClassification : Type 'System.TypedReference' is a 'ref struct' in the implementation but is a 'struct' in the contract. Compat issues with assembly System.Core: TypesMustExist : Type 'System.Security.Cryptography.ECCurve' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'System.Security.Cryptography.ECParameters' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'System.Security.Cryptography.ECPoint' does not exist in the implementation but it does exist in the contract. -Total Issues: 5 +Total Issues: 9 diff --git a/external/corefx/src/shims/ApiCompatBaseline.uapaot.netstandard20Only.txt b/external/corefx/src/shims/ApiCompatBaseline.uapaot.netstandard20Only.txt index fcc74cf864..b5b3f20d60 100644 --- a/external/corefx/src/shims/ApiCompatBaseline.uapaot.netstandard20Only.txt +++ b/external/corefx/src/shims/ApiCompatBaseline.uapaot.netstandard20Only.txt @@ -1 +1,4 @@ -Total Issues: 0 +Compat issues with assembly netstandard: +TypeCannotChangeClassification : Type 'System.RuntimeArgumentHandle' is a 'ref struct' in the implementation but is a 'struct' in the contract. +TypeCannotChangeClassification : Type 'System.TypedReference' is a 'ref struct' in the implementation but is a 'struct' in the contract. +Total Issues: 2 diff --git a/external/corefx/src/shims/manual/System.Data.csproj b/external/corefx/src/shims/manual/System.Data.csproj new file mode 100644 index 0000000000..3807fda917 --- /dev/null +++ b/external/corefx/src/shims/manual/System.Data.csproj @@ -0,0 +1,31 @@ + + + + + netcoreapp; + uap; + + true + true + + + + + 4.0.0.0 + ECMA + true + true + $(NetFxRefPath)System.Data.dll + true + true + $(DefineConstants);netcoreapp + {5E51460E-C9DC-4B6B-B87E-0ED742FC6733} + + + + + + + + + \ No newline at end of file diff --git a/external/corefx/src/shims/manual/System.Data.forwards.cs b/external/corefx/src/shims/manual/System.Data.forwards.cs new file mode 100644 index 0000000000..81aaf80209 --- /dev/null +++ b/external/corefx/src/shims/manual/System.Data.forwards.cs @@ -0,0 +1,11 @@ +// 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. + +// Add any internal types that we need to forward from System.Data. + +// These types are required for Desktop to Core serialization as they are not covered by GenAPI because they are not exposed in the ref assembly. +#if netcoreapp +// System.Data.Odbc is only supported on netcoreapp +[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Data.Odbc.ODBC32))] +#endif \ No newline at end of file diff --git a/external/corefx/src/shims/manual/System.forwards.cs b/external/corefx/src/shims/manual/System.forwards.cs index caf47dac74..328a6ce0e2 100644 --- a/external/corefx/src/shims/manual/System.forwards.cs +++ b/external/corefx/src/shims/manual/System.forwards.cs @@ -4,7 +4,8 @@ // Add any internal types that we need to forward from System. -// These types are required for Desktop <--> Core serialization as they are not covered by GenAPI because they are not exposed. +// These types are required for Desktop to Core serialization as they are not covered by GenAPI because they are marked as internal. [assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Collections.Generic.TreeSet<>))] +[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.IO.Compression.ZLibException))] [assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Net.CookieVariant))] [assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Net.PathList))] diff --git a/external/corefx/src/shims/manual/mscorlib.forwards.cs b/external/corefx/src/shims/manual/mscorlib.forwards.cs index 1aa08e3505..2e31dca518 100644 --- a/external/corefx/src/shims/manual/mscorlib.forwards.cs +++ b/external/corefx/src/shims/manual/mscorlib.forwards.cs @@ -5,22 +5,21 @@ // Add any internal types that we need to forward from mscorlib. // These types are required for Desktop to Core serialization as they are not covered by GenAPI because they are marked as internal. -[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.CultureAwareComparer))] -[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.OrdinalComparer))] [assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Collections.Generic.GenericComparer<>))] [assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Collections.Generic.NullableComparer<>))] [assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Collections.Generic.ObjectComparer<>))] [assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Collections.Generic.GenericEqualityComparer<>))] [assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Collections.Generic.NullableEqualityComparer<>))] [assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Collections.Generic.ObjectEqualityComparer<>))] +// This is required for back-compatibility with .NET Core 2.0 as we exposed the NonRandomizedStringEqualityComparer inside the serialization blob [assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Collections.Generic.NonRandomizedStringEqualityComparer))] [assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Collections.Generic.ByteEqualityComparer))] [assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Collections.Generic.EnumEqualityComparer<>))] -[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Collections.Generic.SByteEnumEqualityComparer<>))] -[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Collections.Generic.ShortEnumEqualityComparer<>))] -[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Collections.Generic.LongEnumEqualityComparer<>))] [assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Collections.ListDictionaryInternal))] +[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.CultureAwareComparer))] +[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.OrdinalComparer))] [assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.UnitySerializationHolder))] +[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Diagnostics.Contracts.ContractException))] // This is temporary as we are building the mscorlib shim against netfx461 which doesn't contain ValueTuples. [assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueTuple))] diff --git a/external/corefx/src/shims/netfxreference.props b/external/corefx/src/shims/netfxreference.props index c7cb3ea2ab..d04168575d 100644 --- a/external/corefx/src/shims/netfxreference.props +++ b/external/corefx/src/shims/netfxreference.props @@ -6,13 +6,14 @@ true - MSSharedLib - + + true + diff --git a/external/corefx/src/shims/shims.proj b/external/corefx/src/shims/shims.proj index fa06e8dfa8..216e429a6c 100644 --- a/external/corefx/src/shims/shims.proj +++ b/external/corefx/src/shims/shims.proj @@ -52,13 +52,13 @@ WorkingDirectory="$(ToolRuntimePath)" /> diff --git a/external/corefx/src/sign.builds b/external/corefx/src/sign.builds index f0cef964e3..d22978a41d 100644 --- a/external/corefx/src/sign.builds +++ b/external/corefx/src/sign.builds @@ -36,7 +36,7 @@ diff --git a/external/corefx/src/src.builds b/external/corefx/src/src.builds index 40124b0f79..ba21b62904 100644 --- a/external/corefx/src/src.builds +++ b/external/corefx/src/src.builds @@ -7,30 +7,4 @@ - - - - - - - - - - - - - <_sharedFrameworkDepsJson Include="$(ToolsDir)dotnetcli\shared\Microsoft.NETCore.App\*\Microsoft.NETCore.App.deps.json" /> - - - - <_OriginalDepsJsonPath>%(_sharedFrameworkDepsJson.FullPath) - <_OutputTestSharedFrameworkDepsPath>$(NETCoreAppTestSharedFrameworkPath)\Microsoft.NETCore.App.deps.json - - - - diff --git a/external/corefx/sync.sh b/external/corefx/sync.sh index 835b3c9ed9..42b52a3a3a 100755 --- a/external/corefx/sync.sh +++ b/external/corefx/sync.sh @@ -6,5 +6,5 @@ fi working_tree_root="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -$working_tree_root/run.sh sync $__args $* +$working_tree_root/run.sh sync $__args "$@" exit $? diff --git a/external/corefx/tools-local/DefaultGenApiDocIds.txt b/external/corefx/tools-local/DefaultGenApiDocIds.txt index e2c93f8733..8432a7b546 100644 --- a/external/corefx/tools-local/DefaultGenApiDocIds.txt +++ b/external/corefx/tools-local/DefaultGenApiDocIds.txt @@ -6,6 +6,7 @@ T:System.ComponentModel.Design.Serialization.RootDesignerSerializerAttribute T:System.ComponentModel.EditorAttribute T:System.ComponentModel.TypeConverterAttribute T:System.Configuration.ConfigurationPropertyAttribute +T:System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute T:System.Diagnostics.CodeAnalysis.SuppressMessageAttribute T:System.Diagnostics.DebuggerBrowsableAttribute T:System.Diagnostics.DebuggerDisplayAttribute @@ -26,6 +27,7 @@ T:System.Runtime.InteropServices.ComDefaultInterfaceAttribute T:System.Runtime.InteropServices.ComVisibleAttribute T:System.Runtime.InteropServices.GuidAttribute T:System.Runtime.InteropServices.InterfaceTypeAttribute +T:System.Runtime.InteropServices.StructLayoutAttribute T:System.Runtime.Serialization.KnownTypeAttribute T:System.Security.Permissions.EnvironmentPermissionAttribute T:System.Security.Permissions.FileIOPermissionAttribute diff --git a/external/corefx/tools-local/ILAsmVersion.txt b/external/corefx/tools-local/ILAsmVersion.txt new file mode 100644 index 0000000000..bab4a159d0 --- /dev/null +++ b/external/corefx/tools-local/ILAsmVersion.txt @@ -0,0 +1 @@ +2.1.0-preview1-26122-04 \ No newline at end of file diff --git a/external/corert/.gitignore b/external/corert/.gitignore index a08b8cea72..078e129dee 100644 --- a/external/corert/.gitignore +++ b/external/corert/.gitignore @@ -13,6 +13,7 @@ tests_downloaded/ *.user *.userosscache *.sln.docstates +launchSettings.json # Build results [Dd]ebug/ diff --git a/external/corert/.vscode/launch.json b/external/corert/.vscode/launch.json index 46d40ef7b9..e5ac2c52e8 100644 --- a/external/corert/.vscode/launch.json +++ b/external/corert/.vscode/launch.json @@ -6,11 +6,14 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "", + "windows": { + "program": "${workspaceRoot}/bin/Windows_NT.x64.Debug/tools/ilc.dll" + }, "osx": { - "program": "${workspaceRoot}/bin/Product/OSX.x64.Debug/packaging/publish1/ilc.dll" + "program": "${workspaceRoot}/bin/OSX.x64.Debug/tools/ilc.dll" }, "linux": { - "program": "${workspaceRoot}/bin/Product/Linux.x64.Debug/packaging/publish1/ilc.dll" + "program": "${workspaceRoot}/bin/Linux.x64.Debug/tools/ilc.dll" }, "args": [], "cwd": "${workspaceRoot}", @@ -21,7 +24,7 @@ "name": ".NET Core Attach", "type": "coreclr", "request": "attach", - "processId": "${command.pickProcess}" + "processId": "${command:pickProcess}" } ] } \ No newline at end of file diff --git a/external/corert/BuildToolsVersion.txt b/external/corert/BuildToolsVersion.txt index 0cd317f7fd..8cc3da5def 100644 --- a/external/corert/BuildToolsVersion.txt +++ b/external/corert/BuildToolsVersion.txt @@ -1 +1 @@ -1.0.27-prerelease-01322-01 +2.1.0-prerelease-02403-01 diff --git a/external/corert/Documentation/README.md b/external/corert/Documentation/README.md index 881d7b7f9c..a41b66bdba 100644 --- a/external/corert/Documentation/README.md +++ b/external/corert/Documentation/README.md @@ -15,3 +15,4 @@ This is the repo for CoreRT, the .NET Core runtime optimized for AOT (Ahead of T - [How to build and run from VSCode](how-to-build-and-run-ilcompiler-in-vscode.md) - [How to run tests](how-to-run-tests.md) - [Cross Compilation for ARM on Linux](cross-building.md) +- [How to Build WebAssembly](how-to-build-WebAssembly.md) diff --git a/external/corert/Documentation/cross-building.md b/external/corert/Documentation/cross-building.md index afc0f07c9b..c95fd15dd5 100644 --- a/external/corert/Documentation/cross-building.md +++ b/external/corert/Documentation/cross-building.md @@ -1,7 +1,7 @@ Cross Compilation for ARM on Linux ================================== -Through cross compilation, on Linux it is possible to build CoreCLR for arm or arm64. +Through cross compilation, on Linux it is possible to build CoreRT for arm or arm64. Requirements ------------ @@ -21,7 +21,7 @@ and conversely for arm64: Generating the rootfs --------------------- -The `cross\build-rootfs.sh` script can be used to download the files needed for cross compilation. It will generate an Ubuntu 14.04 rootfs as this is what CoreCLR targets. +The `cross\build-rootfs.sh` script can be used to download the files needed for cross compilation. It will generate an Ubuntu 14.04 rootfs as this is what CoreRT targets. Usage: build-rootfs.sh [BuildArch] BuildArch can be: arm, arm64 @@ -38,7 +38,7 @@ and if you wanted to generate the rootfs elsewhere: Cross compiling CoreCLR ----------------------- -Once the rootfs has been generated, it will be possible to cross compile CoreCLR. If `ROOTFS_DIR` was set when generating the rootfs, then it must also be set when running `build.sh`. +Once the rootfs has been generated, it will be possible to cross compile CoreRT. If `ROOTFS_DIR` was set when generating the rootfs, then it must also be set when running `build.sh`. So, without `ROOTFS_DIR`: @@ -49,3 +49,57 @@ And with: $ ROOTFS_DIR=~/coreclr-cross/arm ./build.sh arm debug verbose clean cross As usual the resulting binaries will be found in `bin/Product/BuildOS.BuildArch.BuildType/` + +Using CoreRT for cross compiling under arm on x86 host +----------------------- +It is possible to use CoreRT for compiling under arm/armel on x86 host (or on x64 machine using roots). + +1. Build CoreCLR for x86 (`checked` version) +``` +sudo ./cross/build-rootfs.sh x86 xenial +./build.sh clang3.9 x86 checked verbose cross skiptests +``` + +2. Build CoreFX (`Debug` version) +3. Build CoreRT for armel, x64, x86 +``` +sudo ./cross/build-rootfs.sh armel tizen cross +./build.sh clang3.9 armel debug verbose cross +./build.sh debug verbose skiptests +./build.sh clang3.9 x86 debug verbose cross skiptests +``` + +4. Copy necessary binaries to working directory (in x86 rootfs) +``` +cp ${CORECLR}/bin/Product/Linux.x86.Checked ${WORKING_DIR} +cp ${CORERT}/bin/Linux.x86.Debug/tools/ilc.dll ${WORKING_DIR} +cp ${CORERT}/bin/Linux.x86.Debug/tools/ILCompiler.* ${WORKING_DIR} +cp ${CORERT}/bin/Linux.x86.Debug/tools/System.CommandLine.dll ${WORKING_DIR} +cp ${CORERT}/bin/Linux.x86.Debug/tools/Microsoft.DiaSymReader.dll ${WORKING_DIR} +cp ${CORERT}/bin/Linux.x86.Debug/tools/jitinterface.so ${WORKING_DIR} +cp -r ${CORERT}/bin/Linux.x86.Debug/framework ${WORKING_DIR} + +# Copy CoreRT sdk binaries from target (armel) output folder +cp -r ${CORERT}/bin/Linux.armel.Debug/sdk ${WORKING_DIR} +``` + +5. Rename RyuJIT compiler library +``` +# Use cross-compiler library as default for ILC +cp ${WORKING_DIR}/libarmelnonjit.so ${WORKING_DIR}/libclrjitilc.so + +# ... or ARM version instead if it's needed +# cp ${WORKING_DIR}/libprotojit.so ${WORKING_DIR}/libclrjitilc.so +``` + +6. [Build ObjectWriter library](how-to-build-ObjectWriter.md). You have to compile it on x86 chroot. + +7. And to execute use: +``` +./corerun ilc.dll --codegenopt "AltJitNgen=*" --verbose @Hello.ilc.rsp +# Any other options to RyuJIT could be passed via --codegenopt argument, e.g.: +#./corerun ilc.dll --codegenopt "AltJitNgen=*" --codegenopt "NgenDisasm=*" --verbose @Hello.ilc.rsp + +# For linking +clang-3.9 -target arm-linux-gnueabi --sysroot=corert/cross/rootfs/armel -Bcorert/cross/rootfs/armel/usr/lib/gcc/armv7l-tizen-linux-gnueabi/6.2.1 -Lcorert/cross/rootfs/armel/usr/lib/gcc/armv7l-tizen-linux-gnueabi/6.2.1 Hello.o -o Hello corert/bin/Linux.armel.Debug/sdk/libbootstrapper.a corert/bin/Linux.armel.Debug/sdk/libRuntime.a corert/bin/Linux.armel.Debug/sdk/libSystem.Private.CoreLib.Native.a corert/bin/Linux.armel.Debug/framework/System.Native.a corert/bin/Linux.armel.Debug/framework/libSystem.Globalization.Native.a -g -Wl,-rpath,'$ORIGIN' -pthread -lstdc++ -ldl -lm -luuid -lrt -fPIC +``` diff --git a/external/corert/Documentation/design-docs/WebAssembly-design.md b/external/corert/Documentation/design-docs/WebAssembly-design.md new file mode 100644 index 0000000000..8113f69de5 --- /dev/null +++ b/external/corert/Documentation/design-docs/WebAssembly-design.md @@ -0,0 +1,51 @@ +# WebAssembly Design + +WebAssembly (WASM) is a rapidly evolving platform and significantly different than the ones that .NET already supports. CoreRT is in a good position to target new and different architectures due to its pluggable compiler design and because most of it is written in architecture-independent C#. Many high-level .NET features will hopefully work without modification. The important WASM-specific areas we've considered so far are below. + +## Code Generation +For WASM code generation, CoreRT plugs into LLVM's APIs to generate LLVM bitcode. That bitcode can be linked by [Emscripten](http://kripken.github.io/emscripten-site/) into WebAssembly, as well as ASM.js. The reasons for this design are: +1. While it may be possible to create a WASM RyuJIT target architecture, that's likely fairly expensive to get working and even more expensive to build WASM-specific optimizations for. If WASM becomes a major .NET usage scenario, we'd likely revisit this. +2. Emscripten includes a combination of general LLVM optimizations (like eliminating redundant assignments) and WASM-specific optimizations (like controlling the JavaScript optimizer). +3. Compiling with LLVM gives managed code the ability to call into C code, which is valuable for calls into the .NET or C runtimes. +4. An LLVM bitcode generator in CoreRT might be usable for quick ports to future platforms before RyuJIT is ready for them. + +### Details +Working via LLVM bitcode implies that some IL concepts need to be translated to C-like concepts. In particular: +1. .NET requires visibility into its stack layout for garbage collection and exception handling. WebAssembly does not allow pointers to the stack at all and LLVM has some limitations on what's available. To get sufficient control over the stack, CoreRT manages its own "shadow" stack that is allocated on the C heap. That also means methods have a custom calling convention. Every method call pushes managed arguments onto the shadow stack, followed by a space for the return value. The method signature fed to LLVM is always the same: void MethodName(int8* ArgumentPointer, int8* ReturnArgumentAddress). This will also allow delegates and reflection to call arbitrary methods without special codegen per-signature. +2. IL includes implicit conversion between various integer and pointer types. For example, the C# code: +``` +int b = 0; +fixed(int* a = &b) +{ + int* c = a + 1; +} +``` +Translates into IL something like: +``` +ldloc.a b // Loads an int& +stloc a // stores into a native int +ldloc a // loads a native int +ldc 1 // loads an int32 +add // produces a native int +``` +Since those implicit conversions are illegal in C, every load/push operation must convert to canonical types and every store/pop operation must convert to the target type. + +3. LLVM's internal representation is strongly typed, but because the C++ rules are different than .NET rules, we only want minimal integration with LLVM's strong typing. To handle that, all .NET types are canonicalized to a small set of LLVM types. Numeric types map directly while structs are mapped to Int8 arrays matching the struct's size. Classes are represented as Int8 pointers. + +## Framework Libraries +Many framework libraries should work without modification. However, some APIs (such as files and networking) are platform-dependent. CoreRT and CoreFX include a Platform Abstraction Layer (PAL) built for each supported platform. We will need a WebAssembly PAL that calls into the right C or JavaScript APIs to support functionality. JavaScript sandboxing will limit some operations (such as which network calls are permitted). + +## Garbage Collection +The WebAssembly working group is discussing proposals to include garbage collector object reporting, but that's not yet complete and it's not entirely clear if it will work for .NET. While work has not started on enabling the garbage collector, the intent is that the "shadow stack" will provide enough control for stackwalks that perform precise GC reporting. + +## Exception Handling +This will need further investigation. The C++ compiler does allow exception handling, although it requires special checks in methods with catch blocks. We may be able to build something similar. The WebAssembly working group has also proposed "zero-cost" exceptions (ones that don't need special checks), but it's not yet clear what that will look like or if it will work for the .NET two-pass exception handling model. + +## Interoperability +Interop with C code should work similarly to P/Invokes today with the possible exception of needing to statially link C libraries instead of calling them dynamically. We likely will need to define a JavaScript interop model as well to provide interaction with the rest of the web page. + +## Debugging +There is currently very little debugging support for WebAssembly built into browsers. We'll probably need to watch as this expands to understand what's available for CoreRT. + +## Threading +WebAssembly does not currently support threading, although there are some proposals under consideration. If WebAssembly does not adopt threading in a timely manner, CoreRT may be able to inject context-switching code into compiled code. diff --git a/external/corert/Documentation/engineering/updating-ryujit.md b/external/corert/Documentation/engineering/updating-ryujit.md new file mode 100644 index 0000000000..67664b0493 --- /dev/null +++ b/external/corert/Documentation/engineering/updating-ryujit.md @@ -0,0 +1,12 @@ +# Updating RyuJIT + +Following steps are necessary to pick up a new version of RyuJIT code generation backend from CoreCLR: + +1. From the master branch of the CoreCLR repo, copy header files that are part of the contract with RyuJIT from `src\inc` on the CoreCLR side, to `src\JitInterface\src\ThunkGenerator` on the CoreRT side. +2. Inspect the diffs + 1. If an enum was modified, port the change to the managed version of the enum manually. + 2. If a JitInterface method was added or changed, update `src\JitInterface\src\ThunkGenerator\ThunkInput.txt` and run the generation script next to the file to regenerate `CorInfoBase.cs` and `jitinterface.h`. Update the managed implementation of the method in `CorInfoImpl.cs` manually. + 3. If the JitInterface GUID was updated, update it in `src\Native\jitinterface\jitwrapper.cpp` +3. Go to https://dotnet.myget.org/feed/dotnet-core/package/nuget/Microsoft.NETCore.Jit and find the latest version of the RyuJIT package. Update the version number in dependencies.props at the root of the repo. +4. Rebuild everything and run tests to validate the change. +5. Create a pull request with title "Update RyuJIT". diff --git a/external/corert/Documentation/how-to-build-ObjectWriter.md b/external/corert/Documentation/how-to-build-ObjectWriter.md new file mode 100644 index 0000000000..3f20e177cb --- /dev/null +++ b/external/corert/Documentation/how-to-build-ObjectWriter.md @@ -0,0 +1,44 @@ +# Build ObjectWriter library # + +ObjWriter is based on LLVM, so it requires recent CMake and GCC/Clang to build LLVM. +See [LLVM requirements](http://llvm.org/docs/GettingStarted.html#requirements) for more details. + +1. Clone LLVM from official LLVM mirror github git repository: + + ``` + git clone -b release_50 https://github.com/llvm-mirror/llvm.git + ``` + +2. Copy ObjWriter directory from CoreRT into LLVM tree + + ``` + cp -r CoreRT/src/Native/ObjWriter llvm/tools/ + ``` + +3. Apply the patch to LLVM: + + ``` + cd llvm + git apply tools/ObjWriter/llvm.patch + ``` + +4. Configure and build LLVM with ObjWriter: + + ``` + mkdir build + cd build + cmake ../ -DCMAKE_BUILD_TYPE=Release -DLLVM_OPTIMIZED_TABLEGEN=1 -DHAVE_POSIX_SPAWN=0 -DLLVM_ENABLE_PIC=1 -DLLVM_BUILD_TESTS=0 -DLLVM_ENABLE_DOXYGEN=0 -DLLVM_INCLUDE_DOCS=0 -DLLVM_INCLUDE_TESTS=0 + make -j10 objwriter + cd .. + ``` + +* For ARM(cross/non-cross) please specify Triple for LLVM as Cmake configuration option: + ``` + -DLLVM_DEFAULT_TARGET_TRIPLE=thumbv7-linux-gnueabi + ``` +* You can change the building type(CMAKE_BUILD_TYPE) to the debugging type(Debug), if necessary to debug ObjWriter. +* Also, you can do this under chroot to building ObjWriter for other platforms. + +5. Get ObjWriter: + + If all goes well, the build will complete in the previous step and you will get ObjWriter library as llvm/build/lib/libobjwriter.so diff --git a/external/corert/Documentation/how-to-build-WebAssembly.md b/external/corert/Documentation/how-to-build-WebAssembly.md new file mode 100644 index 0000000000..72fb034b25 --- /dev/null +++ b/external/corert/Documentation/how-to-build-WebAssembly.md @@ -0,0 +1,22 @@ +# Build WebAssembly # +Currently, building WebAssembly is only possible on Windows. + +1. Install Emscripten by following the instructions [here](https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html). +2. Follow the instructions [here](https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html#updating-the-sdk) to update Emscripten to the latest version. +3. Get CoreRT set up by following the [Visual Studio instructions](how-to-build-and-run-ilcompiler-in-visual-studio.md). +4. Build the WebAssembly runtime by running ```build.cmd wasm``` from the repo root. +5. Run the WebAssembly "Hello World" test by running ```C:\corert\tests\runtest.cmd wasm```. + +To debug compiling WebAssembly: +1. Set the ILCompiler startup command line to ```@C:\corert\tests\src\Simple\HelloWasm\obj\Debug\wasm\native\HelloWasm.ilc.rsp```. That will generate HelloWasm.bc, an LLVM bitcode file. +2. To compile that to WebAssembly, run ```emcc HelloWasm.bc -s ALLOW_MEMORY_GROWTH=1 C:\corert\bin\WebAssembly.wasm.Debug\sdk\libPortableRuntime.bc C:\corert\bin\WebAssembly.wasm.Debug\sdk\libbootstrappercpp.bc -s WASM=1 -o HelloWasm.html``` (if emcc isn't on your path, you'll need to launch an Emscripten command prompt to do this). That will generate a .wasm file with your code as well as html and js files to run it. + +To run a WebAssembly application +1. Ensure you have Edge 41 or above or [Firefox](https://www.getfirefox.com). +2. Open the generated html file in Edge or Firefox and look at the on-screen console for output. + +Useful tips: +* To manually make ILC compile to WebAssembly, add ```--wasm``` to the command line. +* Add ```-g3``` to the emcc command line to generate more debuggable output and a .wast file with the text form of the WebAssembly. +* Omit ```-s WASM=1``` from the emcc command line to generate asm.js. Browser debuggers currently work better with asm.js and it's often a bit more readable than wast. +* Add ```-O2 --llvm-lto 2``` to the emcc command line to enable optimizations. This makes the generated WebAssembly as much as 75% smaller as well as more efficient. diff --git a/external/corert/Documentation/how-to-build-and-run-ilcompiler-in-console-shell-prompt.md b/external/corert/Documentation/how-to-build-and-run-ilcompiler-in-console-shell-prompt.md index 2b920384ef..48b1f3b479 100644 --- a/external/corert/Documentation/how-to-build-and-run-ilcompiler-in-console-shell-prompt.md +++ b/external/corert/Documentation/how-to-build-and-run-ilcompiler-in-console-shell-prompt.md @@ -8,18 +8,16 @@ Build your repo by issuing the following command at repo root: build[.cmd|.sh] clean [Debug|Release] ``` -If you're using Visual Studio 2017, you need to run the above command from the "Developer Command Prompt for VS 2017". Visual Studio setup puts a shortcut to this in the Start menu. - This will result in the following: - Restore nuget packages required for building -- Build native and managed components of ILCompiler. The final binaries are placed to `\bin\Product\..\packaging\publish1`. +- Build native and managed components of ILCompiler. The final binaries are placed to `\bin\..\tools`. - Build and run tests -# Install latest CLI tools +# Install .NET Core 2.0 SDK -* Download latest CLI tools from [https://github.com/dotnet/cli/](https://github.com/dotnet/cli/) and add them to the path. The latest CLI tools include MSBuild support that the native compilation build integration depends on. These instructions have been tested with build `1.0.0-rc4-004812`. -* On windows ensure you are using the 'x64 Native Tools Command Prompt for VS 2017' or 'VS2015 x64 Native Tools Command Prompt' +* Download .NET Core 2.0 SDK from [https://www.microsoft.com/net/download/core](https://www.microsoft.com/net/download/core) +* On windows ensure you are using the 'x64 Native Tools Command Prompt for VS 2017' (This is distinct from the 'Developer Command Prompt for VS 2017') You should now be able to use the `dotnet` commands of the CLI tools. @@ -28,7 +26,7 @@ You should now be able to use the `dotnet` commands of the CLI tools. * Ensure that you have done a repo build per the instructions above. * Create a new folder and switch into it. -* Run `dotnet new` on the command/shell prompt. This will add a project template. If you get an error, please ensure the [pre-requisites](prerequisites-for-building.md) are installed. +* Run `dotnet new console` on the command/shell prompt. This will add a project template. If you get an error, please ensure the [pre-requisites](prerequisites-for-building.md) are installed. * Modify `.csproj` file that is part of your project. A few lines at the top and at the bottom are different from the default template. ``` @@ -37,23 +35,21 @@ You should now be able to use the `dotnet` commands of the CLI tools. Exe - netcoreapp1.0 + netcoreapp2.0 - + ``` -* Set IlcPath environment variable to point to the built binaries. Alternatively, pass an extra `/p:IlcPath=\bin\Product\Windows_NT.x64.Debug\packaging\publish1` argument to all dotnet commands below. +* Set IlcPath environment variable to point to the built binaries. Alternatively, pass an extra `/p:IlcPath=\bin\Windows_NT.x64.Debug` argument to all dotnet commands below. - * Unix: `export IlcPath=/bin/Product/Linux.x64.Debug/packaging/publish1` + * Unix: `export IlcPath=/bin/Linux.x64.Debug` - * Windows: `set IlcPath=\bin\Product\Windows_NT.x64.Debug\packaging\publish1` + * Windows: `set IlcPath=\bin\Windows_NT.x64.Debug` -* Run `dotnet restore`. This will download nuget packages required for compilation. - -* Please [open an issue](https://github.com/dotnet/corert/issues) if these instructions do not work anymore. .NET Core integration with MSBuild is work in progress and these instructions need updating accordingly from time to time. +* Please [open an issue](https://github.com/dotnet/corert/issues) if these instructions do not work anymore. ## Using RyuJIT ## @@ -62,23 +58,27 @@ This approach uses the same code-generator (RyuJIT), as [CoreCLR](https://github From the shell/command prompt, issue the following commands, from the folder containing your project, to generate the native executable ``` - dotnet build /t:LinkNative + dotnet publish -r win-x64|linux-x64|osx-x64 ``` -Native executable will be dropped in `./bin/[configuration]/native/` folder and will have the same name as the folder in which your source file is present. +Native executable will be dropped in `./bin/x64/[configuration]/netcoreapp2.0/publish/` folder and will have the same name as the folder in which your source file is present. ## Using CPP Code Generator ## -This approach uses platform specific C++ compiler and linker for compiling/linking the application. +This approach uses [transpiler](https://en.wikipedia.org/wiki/Source-to-source_compiler) to convert IL to C++, and then uses platform specific C++ compiler and linker for compiling/linking the application. The transpiler is a lot less mature than the RyuJIT path. If you came here to give CoreRT a try on your .NET Core program, use the RyuJIT option above. From the shell/command prompt, issue the following commands to generate the native executable: ``` - dotnet build /t:LinkNative /p:NativeCodeGen=cpp + dotnet publish /p:NativeCodeGen=cpp -r win-x64|linux-x64|osx-x64 ``` For CoreRT debug build on Windows, add an extra `/p:AdditionalCppCompilerFlags=/MTd` argument. +## Disabling Native Compilation + +Native compilation can be disabled during publishing by adding an extra `/p:NativeCompilationDuringPublish=false` argument. + ## Workarounds for build errors on Windows ## If you are seeing errors such as: @@ -93,7 +93,19 @@ If you are seeing errors such as: ``` libcpmtd.lib(nothrow.obj) : fatal error LNK1112: module machine type 'X86' conflicts with target machine type 'x64' [C:\Users\[omitted]\nativetest\app\app.csproj] -C:\Users\[omitted]\nativetest\bin\Product\Windows_NT.x64.Debug\packaging\publish1\Microsoft.NETCore.Native.targets(151,5): error MSB3073: The command "link @"obj\Debug\netcoreapp1.0\native\link.rsp"" exited with code 1112. [C:\Users\[omitted]\nativetest\app\app.csproj] +C:\Users\[omitted]\nativetest\bin\Windows_NT.x64.Debug\build\Microsoft.NETCore.Native.targets(151,5): error MSB3073: The command "link @"obj\Debug\netcoreapp1.0\native\link.rsp"" exited with code 1112. [C:\Users\[omitted]\nativetest\app\app.csproj] +``` + +or + +``` +Microsoft.NETCore.Native.targets(150,5): error MSB3073: The command "link @"native\link.rsp"" exited with code 1. +``` + +or + +``` +Microsoft.NETCore.Native.targets(132,5): error MSB3073: The command "cl @"native\cl.rsp"" exited with code 9009. ``` Make sure you run these commands from the `x64 Native Tools Command Prompt for VS 2017` instead of a vanilla command prompt diff --git a/external/corert/Documentation/how-to-build-and-run-ilcompiler-in-visual-studio.md b/external/corert/Documentation/how-to-build-and-run-ilcompiler-in-visual-studio.md index e5d430f5b8..90eb39586d 100644 --- a/external/corert/Documentation/how-to-build-and-run-ilcompiler-in-visual-studio.md +++ b/external/corert/Documentation/how-to-build-and-run-ilcompiler-in-visual-studio.md @@ -4,15 +4,19 @@ _Note_: * Instructions below assume `c:\corert` is the repo root. -# Build ILCompiler # -Build your repo by issuing the following command at repo root: +# Building ILCompiler in Visual Studio 2017 # + +First, build your repo by issuing the following command at repo root, by default this builds Debug x64: ``` build.cmd ``` +Then when building ILCompiler.sln in Visual Studio, remember to select the appropriate configuration that you built. By default, `build.cmd` builds Debug x64 and so `Debug` and `x64` must be selected in the solution build configuration drop downs. -If you changed `c:\corert\src\ILCompiler\repro\project.json` +--- + +If you changed `c:\corert\src\ILCompiler\repro\repro.csproj` ``` build.cmd clean @@ -24,7 +28,7 @@ _Note: The size of NuGet packages is approximately 2.75 GB, so download might ta 1. Open c:\corert\src\ILCompiler\ILCompiler.sln in VS - - Set "desktop" project in solution explorer as your startup project + - Set "ILCompiler" project in solution explorer as your startup project - Set startup command line to: `@c:\corert\bin\obj\Windows_NT.x64.Debug\ryujit.rsp` @@ -47,7 +51,7 @@ _Note: The size of NuGet packages is approximately 2.75 GB, so download might ta 1. Open `c:\corert\src\ILCompiler\ILCompiler.sln` in VS - - Set "desktop" project in solution explorer as your startup project + - Set "ILCompiler" project in solution explorer as your startup project - Set startup command line to: `@c:\corert\bin\obj\Windows_NT.x64.Debug\cpp.rsp` @@ -63,3 +67,7 @@ _Note: The size of NuGet packages is approximately 2.75 GB, so download might ta - Set breakpoint at repro::Program::Main in main.cpp - Build, run & step through as with any other C++ program + +## Writing your repro code in IL ## + +If you want to write your repro code using IL (instead of C#), you can search/replace `"repro\repro.csproj"` to `"repro\repro.ilproj"` in ILCompiler.sln which switches to repro.ilproj instead of repro.csproj. Just write your IL code in `repro.il` and build/debug as usual. All the instructions above still would work as expected. Just make sure don't check-in that change. diff --git a/external/corert/Documentation/how-to-build-and-run-ilcompiler-in-vscode.md b/external/corert/Documentation/how-to-build-and-run-ilcompiler-in-vscode.md index df1f3fe9b3..e17ca9bbc1 100644 --- a/external/corert/Documentation/how-to-build-and-run-ilcompiler-in-vscode.md +++ b/external/corert/Documentation/how-to-build-and-run-ilcompiler-in-vscode.md @@ -25,11 +25,14 @@ And then press SHIFT+COMMAND+B to start the build. Go to the debug pane and click Debug, choose .NET Core as the environment. If needed, you can change program property in launch.json (the gear button) to point to a different flavor of ilc: ```json + "windows": { + "program": "${workspaceRoot}/bin/Windows_NT.x64.Debug/tools/ilc.dll" + }, "osx": { - "program": "${workspaceRoot}/bin/Product/OSX.x64.Debug/packaging/publish1/ilc.dll" + "program": "${workspaceRoot}/bin/OSX.x64.Debug/tools/ilc.dll" }, "linux": { - "program": "${workspaceRoot}/bin/Product/Linux.x64.Debug/packaging/publish1/ilc.dll" + "program": "${workspaceRoot}/bin/Linux.x64.Debug/tools/ilc.dll" }, ``` diff --git a/external/corert/Documentation/how-to-debug-compiler-dependency-analysis.md b/external/corert/Documentation/how-to-debug-compiler-dependency-analysis.md new file mode 100644 index 0000000000..99fb959bd4 --- /dev/null +++ b/external/corert/Documentation/how-to-debug-compiler-dependency-analysis.md @@ -0,0 +1,37 @@ +Debugging Compiler Dependency Analysis +============================ + +The general technique is to identify what node is missing from the graph or is erroneously present in the graph, and change the dependency analysis logic to adjust the graph. This document describes the various ways of debugging to identify what's happening. + +Analysis techniques for the dependency graph. +1. Use the ILCompiler-DependencyGraph-Viewer tool (if running on Windows). This tool is located in src\ILCompiler.DependencyAnalysisFramework\ILCompiler-DependencyGraph-Viewer + - This is the only convenient way to examine the graph while also simultaneously debugging the compiler. + - While this is currently Windows only due to use of WinForms, it would be fairly straightforward to make a command line based tool. +2. Pass command line switches to the compiler to generate a dependency graph dgml file. This will produce the same data as is viewable in the viewer tool, but in a textual xml format. + - Future efforts may make the xml file loadable by the viewer tool. +3. Instrument the compiler dependency analysis. (This may be necessary in cases where the viewer is unable to provide sufficient information about why the graph is structured as it is.) + +ILCompiler-DependencyGraph-Viewer +==================================== + +This application allows viewing the dependency graph produced by the CoreRT compilation. + +Usage instructions: +1. Launch the process as an administrator +2. Run the compiler +- The compiler can be run to completion, or stopped. +3. Explore through the graph + +# Graphs View # +- Choose one of the graphs that appears in the Dependency Graphs view to explore. As compilers execute, new graphs will automatically appear here. +- The set of graphs loaded into the process is limited by available memory space. To clear the used memory, close all windows of the application. + +# Graph View # +- In the Dependency Graph view, enter a regular expression in the text box, and then press ""Filter"". This will display a list of the nodes in the graph which have names which match the regular expression. +- Commonly, if there is a object file symbol associated with the node it should be used as part of the regular expression. See the various implementations of GetName in the compiler for naming behavior. +- Additionally, the event source marking mode assigns an Id to each node, and that is found as the mark object on the node, so if a specific id is known, just type that in, and it will appear in the window. (This is for use when using this tool in parallel with debugging the compiler. + +# Single Node Exploration # +Once the interesting node(s) have been identified in the dependency graph window, select one of them, and then press Explore. + - In the Node Explorer window, the Dependent nodes (the ones which dependend on the current node are the nodes displayed above, and the Dependee nodes (the nodes that this node depends on) are displayed below. Each node in the list is paired with a textual reason as to why that edge in the graph exists. + - Select a node to explore further and press the corresponding button to make it happen. diff --git a/external/corert/Documentation/intro-to-corert.md b/external/corert/Documentation/intro-to-corert.md index da9bbd7c6c..775d6fd77e 100644 --- a/external/corert/Documentation/intro-to-corert.md +++ b/external/corert/Documentation/intro-to-corert.md @@ -6,7 +6,7 @@ Native compilation is a great scenario addition to .NET Core apps on Windows, OS Architecture ============ -[.NET Native](https://msdn.microsoft.com/library/dn584397.aspx) is a native toolchain that compiles [IL byte code](https://en.wikipedia.org/wiki/Common_Intermediate_Language) to machine code (e.g. X64 instructions). By default, .NET Native (for .NET Core, as opposed to UWP) uses RyuJIT as an ahead-of-time (AOT) compiler, the same one that CoreCLR uses as a just-in-time (JIT) compiler. It can also be used with other compilers, such as [LLILC](https://github.com/dotnet/llilc), UTC for UWP apps and [IL to CPP](https://github.com/dotnet/corert/tree/master/src/ILCompiler.CppCodeGen/src/CppCodeGen) (an IL to textual C++ compiler we have built as a reference prototype). +[.NET Native](https://msdn.microsoft.com/library/dn584397.aspx) is a native toolchain that compiles [CIL byte code](https://en.wikipedia.org/wiki/Common_Intermediate_Language) to machine code (e.g. X64 instructions). By default, .NET Native (for .NET Core, as opposed to UWP) uses RyuJIT as an ahead-of-time (AOT) compiler, the same one that CoreCLR uses as a just-in-time (JIT) compiler. It can also be used with other compilers, such as [LLILC](https://github.com/dotnet/llilc), UTC for UWP apps and [IL to CPP](https://github.com/dotnet/corert/tree/master/src/ILCompiler.CppCodeGen/src/CppCodeGen) (an IL to textual C++ compiler we have built as a reference prototype). [CoreRT](https://github.com/dotnet/corert) is the .NET Core runtime that is optimized for AOT scenarios, which .NET Native targets. This is a refactored and layered runtime. The base is a small native execution engine that provides services such as garbage collection(GC). This is the same GC used in CoreCLR. Many other parts of the traditional .NET runtime, such as the [type system](https://github.com/dotnet/corert/tree/master/src/Common/src/TypeSystem), are implemented in C#. We've always wanted to implement runtime functionality in C#. We now have the infrastructure to do that. In addition, library implementations that were built deep into CoreCLR, have also been cleanly refactored and implemented as C# libraries. @@ -27,4 +27,4 @@ These benefits open up some new scenarios for .NET developers Roadmap ======= -To start, we are targeting native executables (AKA "console apps"). Over time, we'll extend that to include ASP.NET 5 apps. You can continue to use CoreCLR for your .NET Core apps. It remains a great option if native compilation isn't critical for your needs. CoreCLR will also provide a superior debugging experience until we add debugging support to CoreRT. +To start, we are targeting native executables (AKA "console apps"). Over time, we'll extend that to include ASP.NET Core apps. You can continue to use CoreCLR for your .NET Core apps. It remains a great option if native compilation isn't critical for your needs. CoreCLR will also provide a superior debugging experience until we add debugging support to CoreRT. diff --git a/external/corert/Documentation/prerequisites-for-building.md b/external/corert/Documentation/prerequisites-for-building.md index ada124ca15..b8360e4281 100644 --- a/external/corert/Documentation/prerequisites-for-building.md +++ b/external/corert/Documentation/prerequisites-for-building.md @@ -2,13 +2,13 @@ The following pre-requisites need to be installed for building the repo: # Windows (Windows 7+) -1. Install [Visual Studio 2017](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx), including Visual C++ support. Visual Studio 2015 also works. +1. Install [Visual Studio 2017](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx), including Visual C++ support. 2. Install [CMake](http://www.cmake.org/download/) 3.8.0 or later. Make sure you add it to the PATH in the setup wizard. 3. (Windows 7 only) Install PowerShell 3.0. It's part of [Windows Management Framework 3.0](http://go.microsoft.com/fwlink/?LinkID=240290). Windows 8 or later comes with the right version inbox. PowerShell also needs to be available from the PATH environment variable (it's the default). Typically it should be %SYSTEMROOT%\System32\WindowsPowerShell\v1.0\. -# Ubuntu (14.04) +# Ubuntu (14.04+) Install basic dependency packages: @@ -23,12 +23,10 @@ sudo apt-get update sudo apt-get install cmake clang-3.9 libicu52 libunwind8 uuid-dev ``` -# Mac OSX (10.11.5+) +# macOS (10.12+) 1. Install [Command Line Tools for XCode 8](https://developer.apple.com/xcode/download/) or higher. -2. Install [CMake](https://cmake.org/download/). Launch `/Applications/CMake.app/Contents/MacOS/CMake` GUI. Goto "OSX App Menu -> Tools -> Install For Command Line Use" and follow the steps. CMake < 3.6 has [a bug](https://cmake.org/Bug/view.php?id=16064) that can make `--install` fail. Do `sudo mkdir /usr/local/bin` to work around. -3. Install OpenSSL. Build tools have a dependency on OpenSSL. You can use [Homebrew](http://brew.sh/) to install it: with Homebrew installed, run `brew install openssl` followed by `ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/`, followed by `ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/`. -4. Install ICU (International Components for Unicode). It can be obtained via [Homebrew](http://brew.sh/): run `brew install icu4c` followed by `brew link --force icu4c` +2. Install [CMake](https://cmake.org/download/) 3.8.0 or later. Launch `/Applications/CMake.app/Contents/MacOS/CMake` GUI. Goto "OSX App Menu -> Tools -> Install For Command Line Use" and follow the steps. # Bash on Ubuntu on Windows (Windows 10 Creators Update or later) diff --git a/external/corert/DotnetCLIVersion.txt b/external/corert/DotnetCLIVersion.txt index 7a77ed32ab..227cea2156 100644 --- a/external/corert/DotnetCLIVersion.txt +++ b/external/corert/DotnetCLIVersion.txt @@ -1 +1 @@ -1.0.0-preview3-003223 +2.0.0 diff --git a/external/corert/src/NuGet.config b/external/corert/NuGet.config similarity index 70% rename from external/corert/src/NuGet.config rename to external/corert/NuGet.config index 935e6edd88..57feb087be 100644 --- a/external/corert/src/NuGet.config +++ b/external/corert/NuGet.config @@ -1,21 +1,23 @@ - - - - + + + + + diff --git a/external/corert/Packaging.props b/external/corert/Packaging.props index 83444e4490..88abaa0194 100644 --- a/external/corert/Packaging.props +++ b/external/corert/Packaging.props @@ -1,4 +1,4 @@ - + alpha $(ProjectDir)pkg/ diff --git a/external/corert/README.md b/external/corert/README.md index 2be492753e..12181ef2eb 100644 --- a/external/corert/README.md +++ b/external/corert/README.md @@ -1,10 +1,14 @@ # .NET Core Runtime (CoreRT) This repo contains the .NET Core runtime optimized for AOT compilation -| |Ubuntu 14.04 |Windows |Mac OS X | -|---------|:------:|:------:|:------:| -|**Debug**|[![Build status](https://ci.dot.net/job/dotnet_corert/job/master/job/debug_ubuntu/badge/icon)](https://ci.dot.net/job/dotnet_corert/job/master/job/debug_ubuntu/)|[![Build status](https://ci.dot.net/job/dotnet_corert/job/master/job/debug_windows_nt/badge/icon)](https://ci.dot.net/job/dotnet_corert/job/master/job/debug_windows_nt/)|[![Build Status](https://ci.dot.net/job/dotnet_corert/job/master/job/debug_osx/badge/icon)](https://ci.dot.net/job/dotnet_corert/job/master/job/debug_osx/)| -|**Release**|[![Build status](https://ci.dot.net/job/dotnet_corert/job/master/job/release_ubuntu/badge/icon)](https://ci.dot.net/job/dotnet_corert/job/master/job/release_ubuntu/)|[![Build status](https://ci.dot.net/job/dotnet_corert/job/master/job/release_windows_nt/badge/icon)](https://ci.dot.net/job/dotnet_corert/job/master/job/release_windows_nt/)|[![Build Status](https://ci.dot.net/job/dotnet_corert/job/master/job/release_osx/badge/icon)](https://ci.dot.net/job/dotnet_corert/job/master/job/release_osx/)| +## Platform Support + +This is a work in progress. The current state of platform support: +- Windows x64 w/ RyuJIT codegen: Simple ASP.NET apps [compile and run](https://github.com/dotnet/corert/tree/master/samples/WebApi) +- MacOS and Linux x64 w/ RyuJIT codegen: Same as Windows, the libraries are less complete. +- Linux ARM w/ RyuJIT codegen: ElmSharp Hello Tizen application ([detailed status](https://github.com/dotnet/corert/issues/4856)) +- CppCodeGen (targets all platforms that support C++): Simple C# programs. The big missing features are [reflection](https://github.com/dotnet/corert/issues/2035), [garbage collection](https://github.com/dotnet/corert/issues/2033) and [exception handling](https://github.com/dotnet/corert/issues/910). +- WebAssembly: Early prototype that compiles and runs very trivial programs only. Many features are [not yet implemented](https://github.com/dotnet/corert/issues?q=is%3Aissue+is%3Aopen+label%3Aarch-wasm). ## How to Engage, Contribute and Provide Feedback Some of the best ways to contribute are to try things out, file bugs, and join in design conversations. @@ -35,6 +39,11 @@ This project has adopted the code of conduct defined by the [Contributor Covenan ## Related Projects There are many .NET related projects on GitHub. - The [.NET home repo](https://github.com/Microsoft/dotnet) links to 100s of .NET projects, from Microsoft and the community. -- The [.NET Core repo](https://github.com/dotnet/core) links to .NET Core related projects from Microsoft. - The [ASP.NET home repo](https://github.com/aspnet/home) is the best place to start learning about [ASP.NET Core](http://www.asp.net). -- dotnet.github.io is a good place to discover .NET Foundation projects. + +## Build Status + +| |Ubuntu 14.04 |Windows |Mac OS X | +|---------|:------:|:------:|:------:| +|**Debug**|[![Build status](https://ci.dot.net/job/dotnet_corert/job/master/job/debug_ubuntu/badge/icon)](https://ci.dot.net/job/dotnet_corert/job/master/job/debug_ubuntu/)|[![Build status](https://ci.dot.net/job/dotnet_corert/job/master/job/debug_windows_nt/badge/icon)](https://ci.dot.net/job/dotnet_corert/job/master/job/debug_windows_nt/)|[![Build Status](https://ci.dot.net/job/dotnet_corert/job/master/job/debug_osx10.12/badge/icon)](https://ci.dot.net/job/dotnet_corert/job/master/job/debug_osx10.12/)| +|**Release**|[![Build status](https://ci.dot.net/job/dotnet_corert/job/master/job/release_ubuntu/badge/icon)](https://ci.dot.net/job/dotnet_corert/job/master/job/release_ubuntu/)|[![Build status](https://ci.dot.net/job/dotnet_corert/job/master/job/release_windows_nt/badge/icon)](https://ci.dot.net/job/dotnet_corert/job/master/job/release_windows_nt/)|[![Build Status](https://ci.dot.net/job/dotnet_corert/job/master/job/release_osx10.12/badge/icon)](https://ci.dot.net/job/dotnet_corert/job/master/job/release_osx10.12/)| diff --git a/external/corert/build.proj b/external/corert/build.proj index 20fe452544..7a7a11a32e 100644 --- a/external/corert/build.proj +++ b/external/corert/build.proj @@ -1,21 +1,15 @@ - - + - + - - true + true - - true - - - + @@ -23,32 +17,6 @@ - - - BatchRestorePackages; - $(TraversalBuildDependsOn); - - + - - - - - - - - - - - - - - - - - - - \ No newline at end of file + diff --git a/external/corert/buildpipeline/DotNet-CoreRT-Linux.json b/external/corert/buildpipeline/DotNet-CoreRT-Linux.json index 2066489951..58ac951974 100644 --- a/external/corert/buildpipeline/DotNet-CoreRT-Linux.json +++ b/external/corert/buildpipeline/DotNet-CoreRT-Linux.json @@ -741,7 +741,7 @@ "value": "microsoft/dotnet-buildtools-prereqs" }, "DockerTag": { - "value": "debian-8.2-corert-395a49e-20170403100424" + "value": "ubuntu-14.04-198573d-20170319080304" }, "DockerCopyDest": { "value": "$(Build.BinariesDirectory)/docker_$(SourceFolder)" @@ -807,7 +807,7 @@ "fetchDepth": "0", "gitLfsSupport": "false", "skipSyncSource": "false", - "cleanOptions": "0" + "cleanOptions": "3" }, "id": "0a2b2664-c1be-429c-9b40-8a24dee27a4a", "type": "TfsGit", @@ -840,4 +840,4 @@ "state": "wellFormed", "revision": 418097423 } -} \ No newline at end of file +} diff --git a/external/corert/buildpipeline/DotNet-CoreRT-Mac.json b/external/corert/buildpipeline/DotNet-CoreRT-Mac.json index 2abbbb7606..401c55b7d9 100644 --- a/external/corert/buildpipeline/DotNet-CoreRT-Mac.json +++ b/external/corert/buildpipeline/DotNet-CoreRT-Mac.json @@ -340,7 +340,7 @@ "fetchDepth": "0", "gitLfsSupport": "false", "skipSyncSource": "false", - "cleanOptions": "0" + "cleanOptions": "3" }, "id": "0a2b2664-c1be-429c-9b40-8a24dee27a4a", "type": "TfsGit", @@ -353,12 +353,12 @@ "quality": "definition", "defaultBranch": "refs/heads/master", "queue": { + "id": 330, + "name": "DotNetCore-Build", "pool": { - "id": 39, - "name": "DotNet-Build" - }, - "id": 36, - "name": "DotNet-Build" + "id": 97, + "name": "DotNetCore-Build" + } }, "path": "\\", "type": "build", @@ -373,4 +373,4 @@ "state": "wellFormed", "revision": 418097423 } -} \ No newline at end of file +} diff --git a/external/corert/buildpipeline/DotNet-CoreRT-Publish.json b/external/corert/buildpipeline/DotNet-CoreRT-Publish.json index c0b3486bc1..3589bf04e9 100644 --- a/external/corert/buildpipeline/DotNet-CoreRT-Publish.json +++ b/external/corert/buildpipeline/DotNet-CoreRT-Publish.json @@ -94,7 +94,7 @@ "scriptType": "inlineScript", "scriptName": "", "arguments": "", - "inlineScript": "if ($env:Configuration -ne \"Release\") { exit }\n\n& $env:Build_SourcesDirectory\\scripts\\DotNet-Trusted-Publish\\Embed-Index.ps1 `\n $env:Pipeline_SourcesDirectory\\packages\\AzureTransfer\\Windows_NT.x64.$env:Configuration\\Microsoft.TargetingPack.Private.CoreRT\\$env:AzureContainerSymbolPackageGlob `\n $env:Build_StagingDirectory\\IndexedSymbolPackages", + "inlineScript": "if ($env:Configuration -ne \"Release\") { exit }\n\n& $env:Build_SourcesDirectory\\scripts\\DotNet-Trusted-Publish\\Embed-Index.ps1 `\n $env:Pipeline_SourcesDirectory\\packages\\AzureTransfer\\$env:Configuration\\$env:AzureContainerSymbolPackageGlob `\n $env:Build_StagingDirectory\\IndexedSymbolPackages", "workingFolder": "", "failOnStandardError": "true" } @@ -152,7 +152,7 @@ "scriptType": "inlineScript", "scriptName": "", "arguments": "$(MyGetApiKey)", - "inlineScript": "param($ApiKey)\nif ($env:Configuration -ne \"Release\") { exit }\n& $env:CustomNuGetPath push $env:Pipeline_SourcesDirectory\\packages\\AzureTransfer\\Windows_NT.x64.$env:Configuration\\Microsoft.TargetingPack.Private.CoreRT\\$env:AzureContainerPackageGlob $ApiKey -Source $env:MyGetFeedUrl -Timeout 3600", + "inlineScript": "param($ApiKey)\nif ($env:Configuration -ne \"Release\") { exit }\n& $env:CustomNuGetPath push $env:Pipeline_SourcesDirectory\\packages\\AzureTransfer\\$env:Configuration\\$env:AzureContainerPackageGlob $ApiKey -Source $env:MyGetFeedUrl -Timeout 3600", "workingFolder": "", "failOnStandardError": "true" } @@ -192,7 +192,7 @@ "scriptType": "inlineScript", "scriptName": "", "arguments": "-gitHubAuthToken $(UpdatePublishedVersions.AuthToken) -root $(Pipeline.SourcesDirectory)", - "inlineScript": "param($gitHubAuthToken, $root)\nif ($env:Configuration -ne \"Release\") { exit }\ncd $root\n. $root\\buildscripts\\UpdatePublishedVersions.ps1 `\n -gitHubUser dotnet-build-bot -gitHubEmail dotnet-build-bot@microsoft.com `\n -gitHubAuthToken $gitHubAuthToken `\n -versionsRepoOwner $env:VersionsRepoOwner -versionsRepo $env:VersionsRepo `\n -versionsRepoPath build-info/dotnet/$env:GitHubRepositoryName/$env:SourceBranch `\n -nupkgPath $root\\packages\\AzureTransfer\\Windows_NT.x64.$env:Configuration\\Microsoft.TargetingPack.Private.CoreRT\\$env:AzureContainerPackageGlob", + "inlineScript": "param($gitHubAuthToken, $root)\nif ($env:Configuration -ne \"Release\") { exit }\ncd $root\n. $root\\buildscripts\\UpdatePublishedVersions.ps1 `\n -gitHubUser dotnet-build-bot -gitHubEmail dotnet-build-bot@microsoft.com `\n -gitHubAuthToken $gitHubAuthToken `\n -versionsRepoOwner $env:VersionsRepoOwner -versionsRepo $env:VersionsRepo `\n -versionsRepoPath build-info/dotnet/$env:GitHubRepositoryName/$env:SourceBranch `\n -nupkgPath $root\\packages\\AzureTransfer\\$env:Configuration\\$env:AzureContainerPackageGlob", "workingFolder": "", "failOnStandardError": "true" } diff --git a/external/corert/buildpipeline/DotNet-CoreRT-Windows.json b/external/corert/buildpipeline/DotNet-CoreRT-Windows.json index f67a230710..2c56a71a0e 100644 --- a/external/corert/buildpipeline/DotNet-CoreRT-Windows.json +++ b/external/corert/buildpipeline/DotNet-CoreRT-Windows.json @@ -176,7 +176,7 @@ "inputs": { "SymbolsPath": "\\\\cpvsbuild\\drops\\DotNetCore\\$(Build.DefinitionName)\\$(Build.BuildNumber)\\symbols", "SearchPattern": "**\\*.pdb", - "SymbolsFolder": "$(Build.SourcesDirectory)\\$(SourceFolder)\\bin\\Product", + "SymbolsFolder": "$(Build.SourcesDirectory)\\$(SourceFolder)\\bin\\Windows_NT.$(Platform).$(Configuration)", "SkipIndexing": "false", "TreatNotIndexedAsWarning": "false", "SymbolsMaximumWaitTime": "", @@ -198,7 +198,7 @@ }, "inputs": { "symbolStore": "\\\\cpvsbuild\\drops\\DotNetCore\\$(Build.DefinitionName)\\$(Build.BuildNumber)\\symbols", - "contacts": "crummel;sedarg", + "contacts": "crummel;simonn;michals", "project": "DDE" } }, @@ -477,7 +477,7 @@ "fetchDepth": "0", "gitLfsSupport": "false", "skipSyncSource": "false", - "cleanOptions": "0" + "cleanOptions": "3" }, "id": "0a2b2664-c1be-429c-9b40-8a24dee27a4a", "type": "TfsGit", @@ -491,11 +491,11 @@ "defaultBranch": "refs/heads/master", "queue": { "pool": { - "id": 39, - "name": "DotNet-Build" + "id": 97, + "name": "DotNetCore-Build" }, - "id": 36, - "name": "DotNet-Build" + "id": 330, + "name": "DotNetCore-Build" }, "path": "\\", "type": "build", @@ -510,4 +510,4 @@ "state": "wellFormed", "revision": 418097423 } -} \ No newline at end of file +} diff --git a/external/corert/buildscripts/build-managed.cmd b/external/corert/buildscripts/build-managed.cmd index 4a43073829..e6258748ac 100644 --- a/external/corert/buildscripts/build-managed.cmd +++ b/external/corert/buildscripts/build-managed.cmd @@ -33,19 +33,24 @@ set Platform= :: Restore the Tools directory call "%__ProjectDir%\init-tools.cmd" -rem Tell nuget to always use repo-local nuget package cache. The "dotnet restore" invocations use the --packages -rem argument, but there are a few commands in publish and tests that do not. -set "NUGET_PACKAGES=%__PackagesDir%" - echo Using CLI tools version: dir /b "%__DotNetCliPath%\sdk" +"%__DotNetCliPath%\dotnet.exe" msbuild "%__ProjectDir%\build.proj" /nologo /t:Restore /flp:v=normal;LogFile=build-restore.log /p:NuPkgRid=%__NugetRuntimeId% /maxcpucount /p:OSGroup=%__BuildOS% /p:Configuration=%__BuildType% /p:Platform=%__BuildArch% %__ExtraMsBuildParams% +IF ERRORLEVEL 1 exit /b %ERRORLEVEL% + +rem Buildtools tooling is not capable of publishing netcoreapp currently. Use helper projects to publish skeleton of +rem the standalone app that the build injects actual binaries into later. +"%__DotNetCliPath%\dotnet.exe" restore "%__SourceDir%\ILCompiler\netcoreapp\ilc.csproj" -r %__NugetRuntimeId% +IF ERRORLEVEL 1 exit /b %ERRORLEVEL% +"%__DotNetCliPath%\dotnet.exe" publish "%__SourceDir%\ILCompiler\netcoreapp\ilc.csproj" -r %__NugetRuntimeId% -o "%__RootBinDir%\%__BuildOS%.%__BuildArch%.%__BuildType%\tools" +IF ERRORLEVEL 1 exit /b %ERRORLEVEL% + :: Set the environment for the managed build -:SetupManagedBuild call "!VS%__VSProductVersion%COMNTOOLS!\VsDevCmd.bat" echo Commencing build of managed components for %__BuildOS%.%__BuildArch%.%__BuildType% echo. -%_msbuildexe% /ConsoleLoggerParameters:ForceNoAlign "%__ProjectDir%\build.proj" %__MSBCleanBuildArgs% %__ExtraMsBuildParams% /p:RepoPath="%__ProjectDir%" /p:RepoLocalBuild="true" /p:RelativeProductBinDir="%__RelativeProductBinDir%" /p:NuPkgRid=win7-x64 /p:ToolchainMilestone=%__ToolchainMilestone% /nologo /maxcpucount /verbosity:minimal /nodeReuse:false /fileloggerparameters:Verbosity=normal;LogFile="%__BuildLog%" +%_msbuildexe% /ConsoleLoggerParameters:ForceNoAlign "%__ProjectDir%\build.proj" %__ExtraMsBuildParams% /p:RepoPath="%__ProjectDir%" /p:RepoLocalBuild="true" /p:NuPkgRid=%__NugetRuntimeId% /nologo /maxcpucount /verbosity:minimal /nodeReuse:false /fileloggerparameters:Verbosity=normal;LogFile="%__BuildLog%" IF NOT ERRORLEVEL 1 ( findstr /ir /c:".*Warning(s)" /c:".*Error(s)" /c:"Time Elapsed.*" "%__BuildLog%" goto AfterILCompilerBuild @@ -61,21 +66,15 @@ set __GenRespFiles=0 if not exist "%__ObjDir%\ryujit.rsp" set __GenRespFiles=1 if not exist "%__ObjDir%\cpp.rsp" set __GenRespFiles=1 if "%__GenRespFiles%"=="1" ( - if exist "%__ReproProjectBinDir%" rd /s /q "%__ReproProjectBinDir%" - if exist "%__ReproProjectObjDir%" rd /s /q "%__ReproProjectObjDir%" - - %_msbuildexe% /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%__BinDir%\packaging\publish1" /p:Configuration=%__BuildType% /t:IlcCompile "%__ReproProjectDir%\repro.csproj" - call :CopyResponseFile "%__ReproProjectObjDir%\native\repro.ilc.rsp" "%__ObjDir%\ryujit.rsp" - - if exist "%__ReproProjectBinDir%" rd /s /q "%__ReproProjectBinDir%" - if exist "%__ReproProjectObjDir%" rd /s /q "%__ReproProjectObjDir%" + %_msbuildexe% /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%__BinDir%" /p:Configuration=%__BuildType% /t:Clean,IlcCompile "%__ProjectDir%\src\ILCompiler\repro\repro.csproj" + call :CopyResponseFile "%__ObjDir%\repro\native\repro.ilc.rsp" "%__ObjDir%\ryujit.rsp" set __ExtraArgs=/p:NativeCodeGen=cpp if /i "%__BuildType%"=="debug" ( set __ExtraArgs=!__ExtraArgs! "/p:AdditionalCppCompilerFlags=/MTd" ) - %_msbuildexe% /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%__BinDir%\packaging\publish1" /p:Configuration=%__BuildType% /t:IlcCompile "%__ReproProjectDir%\repro.csproj" !__ExtraArgs! - call :CopyResponseFile "%__ReproProjectObjDir%\native\repro.ilc.rsp" "%__ObjDir%\cpp.rsp" + %_msbuildexe% /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%__BinDir%" /p:Configuration=%__BuildType% /t:Clean,IlcCompile "%__ProjectDir%\src\ILCompiler\repro\repro.csproj" !__ExtraArgs! + call :CopyResponseFile "%__ObjDir%\repro\native\repro.ilc.rsp" "%__ObjDir%\cpp.rsp" ) :AfterVsDevGenerateRespFiles exit /b %ERRORLEVEL% @@ -108,4 +107,4 @@ rem %1 Full path to the file, %2 Variable to receive the file name setlocal for %%i in ("%1") DO set fileName=%%~ni endlocal & set "%2=%fileName%" -goto:eof \ No newline at end of file +goto:eof diff --git a/external/corert/buildscripts/build-managed.sh b/external/corert/buildscripts/build-managed.sh index 5a0970eb25..5c0addc6f5 100755 --- a/external/corert/buildscripts/build-managed.sh +++ b/external/corert/buildscripts/build-managed.sh @@ -6,6 +6,8 @@ if [ "$BUILDVARS_DONE" != 1 ]; then . $scriptRoot/buildvars-setup.sh $* fi +export __BuildArch + # Prepare the system for building prepare_managed_build() @@ -13,10 +15,6 @@ prepare_managed_build() # Run Init-Tools to restore BuildTools and ToolRuntime $__ProjectRoot/init-tools.sh - # Tell nuget to always use repo-local nuget package cache. The "dotnet restore" invocations use the --packages - # argument, but there are a few commands in publish and tests that do not. - export NUGET_PACKAGES=$__packageroot - echo "Using CLI tools version:" ls "$__dotnetclipath/sdk" } @@ -26,17 +24,39 @@ build_managed_corert() __buildproj=$__ProjectRoot/build.proj __buildlog=$__ProjectRoot/msbuild.$__BuildArch.log - if [ -z "${ToolchainMilestone}" ]; then - ToolchainMilestone=testing - fi - __buildarch="$__BuildArch" if [ "$__buildarch" = "armel" ]; then __buildarch=arm __ExtraMsBuildArgs="$__ExtraMsBuildArgs /p:BinDirPlatform=armel" fi - $__ProjectRoot/Tools/msbuild.sh "$__buildproj" /m /nologo /verbosity:minimal "/fileloggerparameters:Verbosity=normal;LogFile=$__buildlog" /t:Build /p:RepoPath=$__ProjectRoot /p:RepoLocalBuild="true" /p:RelativeProductBinDir=$__RelativeProductBinDir /p:CleanedTheBuild=$__CleanBuild /p:NuPkgRid=$__NugetRuntimeId /p:TestNugetRuntimeId=$__NugetRuntimeId /p:OSGroup=$__BuildOS /p:Configuration=$__BuildType /p:Platform=$__buildarch /p:COMPUTERNAME=$(hostname) /p:USERNAME=$(id -un) /p:ToolchainMilestone=${ToolchainMilestone} $__UnprocessedBuildArgs $__ExtraMsBuildArgs + $__dotnetclipath/dotnet msbuild "$__buildproj" /m /nologo /verbosity:minimal "/fileloggerparameters:Verbosity=normal;LogFile=$__buildlog" /t:Restore /p:RepoPath=$__ProjectRoot /p:RepoLocalBuild="true" /p:NuPkgRid=$__NugetRuntimeId /p:OSGroup=$__BuildOS /p:Configuration=$__BuildType /p:Platform=$__buildarch /p:COMPUTERNAME=$(hostname) /p:USERNAME=$(id -un) $__UnprocessedBuildArgs $__ExtraMsBuildArgs + export BUILDERRORLEVEL=$? + + echo + + # Pull the build summary from the log file + tail -n 4 "$__buildlog" + echo Build Exit Code = $BUILDERRORLEVEL + if [ $BUILDERRORLEVEL != 0 ]; then + exit $BUILDERRORLEVEL + fi + + # Buildtools tooling is not capable of publishing netcoreapp currently. Use helper projects to publish skeleton of + # the standalone app that the build injects actual binaries into later. + $__dotnetclipath/dotnet restore $__sourceroot/ILCompiler/netcoreapp/ilc.csproj -r $__NugetRuntimeId + export BUILDERRORLEVEL=$? + if [ $BUILDERRORLEVEL != 0 ]; then + exit $BUILDERRORLEVEL + fi + $__dotnetclipath/dotnet publish $__sourceroot/ILCompiler/netcoreapp/ilc.csproj -r $__NugetRuntimeId -o $__ProductBinDir/tools + export BUILDERRORLEVEL=$? + if [ $BUILDERRORLEVEL != 0 ]; then + exit $BUILDERRORLEVEL + fi + chmod +x $__ProductBinDir/tools/ilc + + $__ProjectRoot/Tools/msbuild.sh "$__buildproj" /m /nologo /verbosity:minimal "/fileloggerparameters:Verbosity=normal;LogFile=$__buildlog" /t:Build /p:RepoPath=$__ProjectRoot /p:RepoLocalBuild="true" /p:NuPkgRid=$__NugetRuntimeId /p:OSGroup=$__BuildOS /p:Configuration=$__BuildType /p:Platform=$__buildarch /p:COMPUTERNAME=$(hostname) /p:USERNAME=$(id -un) $__UnprocessedBuildArgs $__ExtraMsBuildArgs export BUILDERRORLEVEL=$? echo @@ -54,50 +74,52 @@ build_managed_corert() get_official_cross_builds() { if [ $__CrossBuild == 1 ]; then - __corefxsite="https://ci.dot.net/job/dotnet_corefx/job/master/view/Official%20Builds/job/" - __coreclrsite="https://ci.dot.net/job/dotnet_coreclr/job/master/view/Official%20Builds/job/" - __corefxsource= - __coreclrsource= - __buildtype= - if [ $__BuildType = "Debug" ]; then - __buildtype="debug" - else - __buildtype="release" - fi + ID= case $__BuildArch in arm) ;; arm64) ;; armel) - ID= if [ -e $ROOTFS_DIR/etc/os-release ]; then source $ROOTFS_DIR/etc/os-release fi - if [ "$ID" = "tizen" ]; then - __corefxsource="tizen_armel_cross_${__buildtype}/lastSuccessfulBuild/artifact/bin/build.tar.gz" - __coreclrsource="armel_cross_${__buildtype}_tizen/lastSuccessfulBuild/artifact/bin/Product/Linux.armel.${__BuildType}/libSystem.Globalization.Native.a" - fi ;; esac - if [ -n "${__corefxsource}" ]; then - wget "${__corefxsite}${__corefxsource}" - export BUILDERRORLEVEL=$? - if [ $BUILDERRORLEVEL != 0 ]; then - exit $BUILDERRORLEVEL - fi - tar xvf ./build.tar.gz ./System.Native.a - mv ./System.Native.a $__ProjectRoot/bin/Product/Linux.${__BuildArch}.${__BuildType}/packaging/publish1/framework - rm -rf ./build.tar.gz + + # only tizen case now + if [ "$ID" != "tizen" ]; then + return 0 fi - if [ -n ${__coreclrsource} ]; then - wget "${__coreclrsite}${__coreclrsource}" - export BUILDERRORLEVEL=$? - if [ $BUILDERRORLEVEL != 0 ]; then - exit $BUILDERRORLEVEL - fi - mv ./libSystem.Globalization.Native.a $__ProjectRoot/bin/Product/Linux.${__BuildArch}.${__BuildType}/packaging/publish1/framework + __tizenToolsRoot=${__ProjectRoot}/Tools/tizen + __corefxsite="https://ci.dot.net/job/dotnet_corefx/job/master/view/Official%20Builds/job/" + __coreclrsite="https://ci.dot.net/job/dotnet_coreclr/job/master/view/Official%20Builds/job/" + __buildArchiveName="build.tar.gz" + __systemNativeLibName="System.Native.a" + __systemGlobNativeLibName="libSystem.Globalization.Native.a" + if [ $__BuildType = "Debug" ]; then + __buildtype="debug" + else + __buildtype="release" fi + __corefxsource="tizen_armel_cross_${__buildtype}/lastSuccessfulBuild/artifact/bin/${__buildArchiveName}" + __coreclrsource="armel_cross_${__buildtype}_tizen/lastSuccessfulBuild/artifact/bin/Product/Linux.armel.${__BuildType}/${__systemGlobNativeLibName}" + mkdir -p $__tizenToolsRoot + + (cd ${__tizenToolsRoot} && wget -N "${__corefxsite}${__corefxsource}") + export BUILDERRORLEVEL=$? + if [ $BUILDERRORLEVEL != 0 ]; then + exit $BUILDERRORLEVEL + fi + tar xvf ${__tizenToolsRoot}/${__buildArchiveName} -C ${__tizenToolsRoot} ./${__systemNativeLibName} + cp ${__tizenToolsRoot}/${__systemNativeLibName} $__ProjectRoot/bin/Linux.${__BuildArch}.${__BuildType}/framework + + (cd ${__tizenToolsRoot} && wget -N "${__coreclrsite}${__coreclrsource}") + export BUILDERRORLEVEL=$? + if [ $BUILDERRORLEVEL != 0 ]; then + exit $BUILDERRORLEVEL + fi + cp ${__tizenToolsRoot}/${__systemGlobNativeLibName} $__ProjectRoot/bin/Linux.${__BuildArch}.${__BuildType}/framework fi } diff --git a/external/corert/buildscripts/build-native.cmd b/external/corert/buildscripts/build-native.cmd index 793c520e87..e19bcca6d1 100644 --- a/external/corert/buildscripts/build-native.cmd +++ b/external/corert/buildscripts/build-native.cmd @@ -30,31 +30,36 @@ exit /b %ERRORLEVEL% echo Commencing build of native components for %__BuildOS%.%__BuildArch%.%__BuildType% echo. +:PrepareVs :: Set the environment for the native build set __VCBuildArch=x86_amd64 if /i "%__BuildArch%" == "x86" (set __VCBuildArch=x86) -:: VS2017 changed the location of vcvarsall.bat. -if /i "%__VSVersion%" == "vs2017" ( - call "!VS%__VSProductVersion%COMNTOOLS!\..\..\VC\Auxiliary\Build\vcvarsall.bat" %__VCBuildArch% -) else ( - call "!VS%__VSProductVersion%COMNTOOLS!\..\..\VC\vcvarsall.bat" %__VCBuildArch% -) +call "!VS%__VSProductVersion%COMNTOOLS!\..\..\VC\Auxiliary\Build\vcvarsall.bat" %__VCBuildArch% -:: Regenerate the VS solution +:: Regenerate the build files +:RegenerateBuildFiles pushd "%__IntermediatesDir%" -call "%__SourceDir%\Native\gen-buildsys-win.bat" "%__ProjectDir%\src\Native" %__VSVersion% %__BuildArch% +call "%__SourceDir%\Native\gen-buildsys-win.bat" "%__ProjectDir%\src\Native" %__VSVersion% %__BuildArch% %__BuildType% popd -if exist "%__IntermediatesDir%\install.vcxproj" goto BuildNative +if exist "%__IntermediatesDir%\install.vcxproj" goto BuildNativeVs +if exist "%__IntermediatesDir%\Makefile" goto BuildNativeEmscripten echo Failed to generate native component build project! exit /b 1 -:BuildNative -%_msbuildexe% /ConsoleLoggerParameters:ForceNoAlign "%__IntermediatesDir%\install.vcxproj" %__MSBCleanBuildArgs% %__ExtraMsBuildParams% /nologo /maxcpucount /nodeReuse:false /p:Configuration=%__BuildType% /p:Platform=%__BuildArch% /fileloggerparameters:Verbosity=normal;LogFile="%__NativeBuildLog%" +:BuildNativeVs +%_msbuildexe% /ConsoleLoggerParameters:ForceNoAlign "%__IntermediatesDir%\install.vcxproj" %__ExtraMsBuildParams% /nologo /maxcpucount /nodeReuse:false /p:Configuration=%__BuildType% /p:Platform=%__BuildArch% /fileloggerparameters:Verbosity=normal;LogFile="%__NativeBuildLog%" IF NOT ERRORLEVEL 1 goto AfterNativeBuild echo Native component build failed. Refer !__NativeBuildLog! for details. exit /b 1 +:BuildNativeEmscripten +pushd "%__IntermediatesDir%" +nmake install +popd +IF NOT ERRORLEVEL 1 goto AfterNativeBuild +exit /b 1 + :AfterNativeBuild -endlocal \ No newline at end of file +endlocal diff --git a/external/corert/buildscripts/build-native.sh b/external/corert/buildscripts/build-native.sh index 3cfa086ddb..95037e6d41 100755 --- a/external/corert/buildscripts/build-native.sh +++ b/external/corert/buildscripts/build-native.sh @@ -16,6 +16,12 @@ check_native_prereqs() # Check for clang hash clang-$__ClangMajorVersion.$__ClangMinorVersion 2>/dev/null || hash clang$__ClangMajorVersion$__ClangMinorVersion 2>/dev/null || hash clang 2>/dev/null || { echo >&2 "Please install clang before running this script"; exit 1; } + + # Check for additional prereqs for wasm build + if [ $__BuildArch == "wasm" ]; then + hash emcmake 2>/dev/null || { echo >&2 "Please install Emscripten before running this script. See https://github.com/dotnet/corert/blob/master/Documentation/how-to-build-WebAssembly.md for more information."; exit 1; } + if [ -z ${EMSCRIPTEN+x} ]; then echo "EMSCRIPTEN is not set. Ensure your have set up the Emscripten environment using \"source /emsdk_env.sh\""; exit 1; fi + fi } prepare_native_build() @@ -65,7 +71,11 @@ build_native_corert() echo "Executing make install -j $NumProc $__UnprocessedBuildArgs" - make install -j $NumProc $__UnprocessedBuildArgs + if [ $__BuildArch == "wasm" ]; then + emmake make install -j $NumProc $__UnprocessedBuildArgs + else + make install -j $NumProc $__UnprocessedBuildArgs + fi if [ $? != 0 ]; then echo "Failed to build corert native components." popd @@ -119,7 +129,6 @@ build_host_native_corert() build_native_corert cp ${__ProductHostBinDir}/jitinterface.so ${__ProductBinDir} - cp ${__ProductHostBinDir}/jitinterface.so ${__ProductBinDir}/packaging/publish1 export __BuildArch=$__SavedBuildArch export __IntermediatesDir=$__SavedIntermediatesDir diff --git a/external/corert/buildscripts/build-packages.cmd b/external/corert/buildscripts/build-packages.cmd index 6f2cbe6e6f..f62bfc9d2f 100644 --- a/external/corert/buildscripts/build-packages.cmd +++ b/external/corert/buildscripts/build-packages.cmd @@ -27,5 +27,5 @@ exit /b %ERRORLEVEL% :AfterVarSetup -%_msbuildexe% "%__ProjectDir%\pkg\packages.proj" /m /nologo /flp:v=diag;LogFile=build-packages.log /p:NuPkgRid=win7-x64 /p:OSGroup=%__BuildOS% /p:Configuration=%__BuildType% /p:Platform=%__BuildArch% %__ExtraMsBuildParams% -exit /b %ERRORLEVEL% \ No newline at end of file +%_msbuildexe% "%__ProjectDir%\pkg\packages.proj" /m /nologo /flp:v=diag;LogFile=build-packages.log /p:NuPkgRid=%__NugetRuntimeId% /p:OSGroup=%__BuildOS% /p:Configuration=%__BuildType% /p:Platform=%__BuildArch% %__ExtraMsBuildParams% +exit /b %ERRORLEVEL% diff --git a/external/corert/buildscripts/build-tests.cmd b/external/corert/buildscripts/build-tests.cmd index a1133c95d5..8dedcf1827 100644 --- a/external/corert/buildscripts/build-tests.cmd +++ b/external/corert/buildscripts/build-tests.cmd @@ -33,4 +33,4 @@ pushd "%__ProjectDir%\tests" call "runtest.cmd" %__BuildType% %__BuildArch% /dotnetclipath %__DotNetCliPath% set TEST_EXIT_CODE=%ERRORLEVEL% popd -exit /b %TEST_EXIT_CODE% \ No newline at end of file +exit /b %TEST_EXIT_CODE% diff --git a/external/corert/buildscripts/buildvars-setup.cmd b/external/corert/buildscripts/buildvars-setup.cmd index b52e6b7bed..62beab9bab 100644 --- a/external/corert/buildscripts/buildvars-setup.cmd +++ b/external/corert/buildscripts/buildvars-setup.cmd @@ -3,21 +3,14 @@ set __BuildArch=x64 set __BuildType=Debug set __BuildOS=Windows_NT -:: Default to highest Visual Studio version available -set __VSVersion=vs2015 -if defined VS150COMNTOOLS set __VSVersion=vs2017 - :: Set the various build properties here so that CMake and MSBuild can pick them up set "__ProjectDir=%~dp0.." :: remove trailing slash if %__ProjectDir:~-1%==\ set "__ProjectDir=%__ProjectDir:~0,-1%" set "__SourceDir=%__ProjectDir%\src" -set "__PackagesDir=%__ProjectDir%\packages" set "__RootBinDir=%__ProjectDir%\bin" set "__LogsDir=%__RootBinDir%\Logs" -set __MSBCleanBuildArgs= set __SkipTestBuild= -set __ToolchainMilestone=testing set "__DotNetCliPath=%__ProjectDir%\Tools\dotnetcli" :Arg_Loop @@ -33,18 +26,15 @@ if /i "%1" == "-help" goto Usage if /i "%1" == "x64" (set __BuildArch=x64&&shift&goto Arg_Loop) if /i "%1" == "x86" (set __BuildArch=x86&&shift&goto Arg_Loop) if /i "%1" == "arm" (set __BuildArch=arm&&shift&goto Arg_Loop) +if /i "%1" == "wasm" (set __BuildOS=WebAssembly&&set __BuildArch=wasm&&shift&goto Arg_Loop) if /i "%1" == "debug" (set __BuildType=Debug&shift&goto Arg_Loop) if /i "%1" == "release" (set __BuildType=Release&shift&goto Arg_Loop) -if /i "%1" == "vs2017" (set __VSVersion=vs2017&shift&goto Arg_Loop) -if /i "%1" == "vs2015" (set __VSVersion=vs2015&shift&goto Arg_Loop) - if /i "%1" == "clean" (set __CleanBuild=1&shift&goto Arg_Loop) if /i "%1" == "skiptests" (set __SkipTests=1&shift&goto Arg_Loop) if /i "%1" == "skipvsdev" (set __SkipVsDev=1&shift&goto Arg_Loop) -if /i "%1" == "/milestone" (set __ToolchainMilestone=%2&shift&shift&goto Arg_Loop) if /i "%1" == "/dotnetclipath" (set __DotNetCliPath=%2&shift&shift&goto Arg_Loop) if /i "%1" == "/officialbuildid" (set "__ExtraMsBuildParams=/p:OfficialBuildId=%2"&shift&shift&goto Arg_Loop) @@ -54,15 +44,11 @@ exit /b 1 :ArgsDone :: Set the remaining variables based upon the determined build configuration -set "__BinDir=%__RootBinDir%\Product\%__BuildOS%.%__BuildArch%.%__BuildType%" +set "__BinDir=%__RootBinDir%\%__BuildOS%.%__BuildArch%.%__BuildType%" set "__ObjDir=%__RootBinDir%\obj\%__BuildOS%.%__BuildArch%.%__BuildType%" set "__IntermediatesDir=%__RootBinDir%\obj\Native\%__BuildOS%.%__BuildArch%.%__BuildType%\" -set "__RelativeProductBinDir=bin\Product\%__BuildOS%.%__BuildArch%.%__BuildType%" set "__NativeBuildLog=%__LogsDir%\Native_%__BuildOS%__%__BuildArch%__%__BuildType%.log" set "__BuildLog=%__LogsDir%\msbuild_%__BuildOS%__%__BuildArch%__%__BuildType%.log" -set "__ReproProjectDir=%__ProjectDir%\src\ILCompiler\repro" -set "__ReproProjectBinDir=%__BinDir%\repro" -set "__ReproProjectObjDir=%__ObjDir%\repro" :: Generate path to be set for CMAKE_INSTALL_PREFIX to contain forward slash set "__CMakeBinDir=%__BinDir%" @@ -73,9 +59,6 @@ if not defined __CleanBuild goto MakeDirs echo Doing a clean build echo. -:: MSBuild projects would need a rebuild -set __MSBCleanBuildArgs=/t:rebuild /p:CleanedTheBuild=1 - :: Cleanup the previous output for the selected configuration if exist "%__BinDir%" rd /s /q "%__BinDir%" if exist "%__ObjDir%" rd /s /q "%__ObjDir%" @@ -89,11 +72,25 @@ if not exist "%__ObjDir%" md "%__ObjDir%" if not exist "%__IntermediatesDir%" md "%__IntermediatesDir%" if not exist "%__LogsDir%" md "%__LogsDir%" -:CheckPrereqs :: Check prerequisites echo Checking pre-requisites... echo. +if "%__BuildArch%"=="wasm" ( + goto :CheckPrereqsEmscripten +) else ( + goto :CheckPrereqsVs +) + +:CheckPrereqsEmscripten +if not defined EMSCRIPTEN ( + echo Emscripten is a prerequisite to build for WebAssembly. + echo See: https://github.com/dotnet/corert/blob/master/Documentation/how-to-build-WebAssembly.md + exit /b 1 +) +goto CheckPrereqsVs + +:CheckPrereqsVs :: Validate that PowerShell is accessibile. for %%X in (powershell.exe) do (set __PSDir=%%~$PATH:X) if defined __PSDir goto EvaluatePS @@ -105,45 +102,58 @@ exit /b 1 :: Eval the output from probe-win1.ps1 for /f "delims=" %%a in ('powershell -NoProfile -ExecutionPolicy ByPass "& ""%__SourceDir%\Native\probe-win.ps1"""') do %%a +:: Default to highest Visual Studio version available +:: +:: For VS2017, multiple instances can be installed on the same box SxS and VS150COMNTOOLS +:: is no longer set as a global environment variable and is instead only set if the user +:: has launched the VS2017 Developer Command Prompt. +:: +:: Following this logic, we will default to the VS2017 toolset if VS150COMNTOOLS tools is +:: set, as this indicates the user is running from the VS2017 Developer Command Prompt and +:: is already configured to use that toolset. Otherwise, we will fallback to using the latest +:: VS2017 toolset if it is installed. Finally, we will fail the script if no supported VS instance +:: can be found. -set __VSProductVersion= -if /i "%__VSVersion%" == "vs2015" set __VSProductVersion=140 -if /i "%__VSVersion%" == "vs2017" set __VSProductVersion=150 +if defined VisualStudioVersion goto :RunVCVars -:: Check presence of VS -if defined VS%__VSProductVersion%COMNTOOLS goto CheckVSExistence -echo Visual Studio 2015 or 2017 (Community is free) is a pre-requisite to build this repository. -echo If you're using Visual Studio 2017, make sure to run build.cmd from the "Developer Command Prompt -echo for VS 2017" (find it in the Start menu). +set _VSWHERE="%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" +if exist %_VSWHERE% ( + for /f "usebackq tokens=*" %%i in (`%_VSWHERE% -latest -prerelease -property installationPath`) do set _VSCOMNTOOLS=%%i\Common7\Tools +) +if not exist "%_VSCOMNTOOLS%" goto :MissingVersion + +call "%_VSCOMNTOOLS%\VsDevCmd.bat" + +:RunVCVars +if "%VisualStudioVersion%"=="15.0" ( + goto :VS2017 +) + +:MissingVersion +:: Can't find VS 2017 +echo Visual Studio 2017 is a pre-requisite to build this repository. echo See: https://github.com/dotnet/corert/blob/master/Documentation/prerequisites-for-building.md exit /b 1 -:CheckVSExistence -:: Does VS VS 2015 really exist? -if exist "!VS%__VSProductVersion%COMNTOOLS!\..\IDE\devenv.exe" goto CheckMSBuild -echo Visual Studio not installed in !VS%__VSProductVersion%COMNTOOLS!. +:VS2017 +:: Setup vars for VS2017 +set __VSVersion=vs2017 +set __VSProductVersion=150 +if not exist "!VS%__VSProductVersion%COMNTOOLS!\..\..\VC\Auxiliary\Build\vcvarsall.bat" goto :MissingVisualC +goto :CheckMSBuild + +:MissingVisualC +echo Could not find Visual C++ under !VS%__VSProductVersion%COMNTOOLS!. Visual C++ is a pre-requisite to build this repository. echo See: https://github.com/dotnet/corert/blob/master/Documentation/prerequisites-for-building.md exit /b 1 - :CheckMSBuild -:: Note: We've disabled node reuse because it causes file locking issues. -:: The issue is that we extend the build with our own targets which -:: means that that rebuilding cannot successfully delete the task -:: assembly. -if /i "%__VSVersion%" == "vs2017" ( - rem The MSBuild that is installed in the shared location is not compatible - rem with VS2017 C++ projects. I must use the MSBuild located in - rem C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe - rem which is compatible. However, I don't know a good way to specify this - rem path in a way that isn't specific to my system, so I am relying on the - rem system PATH to locate this tool. - set _msbuildexe=msbuild -) else ( - set _msbuildexe="%ProgramFiles(x86)%\MSBuild\14.0\Bin\MSBuild.exe" - if not exist !_msbuildexe! (set _msbuildexe="%ProgramFiles%\MSBuild\14.0\Bin\MSBuild.exe") - if not exist !_msbuildexe! (echo Error: Could not find MSBuild.exe. Please see https://github.com/dotnet/corert/blob/master/Documentation/prerequisites-for-building.md for build instructions. && exit /b 1) -) +rem The MSBuild that is installed in the shared location is not compatible +rem with VS2017 C++ projects. I must use the MSBuild located in +rem C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe +set _msbuildexe="%VSINSTALLDIR%\MSBuild\15.0\Bin\MSBuild.exe" + +if not exist !_msbuildexe! (echo Error: Could not find MSBuild.exe. Please see https://github.com/dotnet/corert/blob/master/Documentation/prerequisites-for-building.md for build instructions. && exit /b 1) rem Explicitly set Platform causes conflicts in managed project files. Clear it to allow building from VS x64 Native Tools Command Prompt set Platform= @@ -152,10 +162,10 @@ set Platform= set __VCBuildArch=x86_amd64 if /i "%__BuildArch%" == "x86" (set __VCBuildArch=x86) -rem Tell nuget to always use repo-local nuget package cache. The "dotnet restore" invocations use the --packages -rem argument, but there are a few commands in publish and tests that do not. -set "NUGET_PACKAGES=%__PackagesDir%" +set __NugetRuntimeId=win7-x64 +if /i "%__BuildArch%" == "x86" (set __NugetRuntimeId=win7-x86) +:Done set BUILDVARS_DONE=1 exit /b 0 @@ -169,9 +179,8 @@ echo. echo All arguments are optional. The options are: echo. echo./? -? /h -h /help -help: view this message. -echo Build architecture: one of x64, x86, arm ^(default: x64^). +echo Build architecture: one of x64, x86, arm, wasm ^(default: x64^). echo Build type: one of Debug, Checked, Release ^(default: Debug^). -echo Visual Studio version: vs2015, vs2017 ^(defaults to highest detected^). echo clean: force a clean build ^(default is to perform an incremental build^). echo skiptests: skip building tests ^(default: tests are built^). -exit /b 1 \ No newline at end of file +exit /b 1 diff --git a/external/corert/buildscripts/buildvars-setup.sh b/external/corert/buildscripts/buildvars-setup.sh index 8bf8c5833a..1615565652 100755 --- a/external/corert/buildscripts/buildvars-setup.sh +++ b/external/corert/buildscripts/buildvars-setup.sh @@ -2,14 +2,15 @@ usage() { - echo "Usage: $0 [managed] [native] [BuildArch] [BuildType] [clean] [cross] [verbose] [clangx.y]" + echo "Usage: $0 [managed] [native] [BuildArch] [BuildType] [clean] [cross] [verbose] [objwriter] [clangx.y]" echo "managed - optional argument to build the managed code" echo "native - optional argument to build the native code" echo "The following arguments affect native builds only:" - echo "BuildArch can be: x64, x86, arm, arm64, armel" + echo "BuildArch can be: x64, x86, arm, arm64, armel, wasm" echo "BuildType can be: Debug, Release" echo "clean - optional argument to force a clean build." echo "verbose - optional argument to enable verbose build output." + echo "objwriter - optional argument to enable build ObjWriter library" echo "clangx.y - optional argument to build using clang version x.y." echo "cross - optional argument to signify cross compilation," echo " - will use ROOTFS_DIR environment variable if set." @@ -56,99 +57,43 @@ check_native_prereqs() hash clang-$__ClangMajorVersion.$__ClangMinorVersion 2>/dev/null || hash clang$__ClangMajorVersion$__ClangMinorVersion 2>/dev/null || hash clang 2>/dev/null || { echo >&2 "Please install clang before running this script"; exit 1; } } +get_current_linux_rid() { + # Construct RID for current distro -get_current_linux_distro() { - # Detect Distro - if [ "$(cat /etc/*-release | grep -cim1 ubuntu)" -eq 1 ]; then - if [ "$(cat /etc/*-release | grep -cim1 16.04)" -eq 1 ]; then - echo "ubuntu.16.04" - return 0 + rid=linux + + if [ -e /etc/os-release ]; then + source /etc/os-release + if [[ $ID == "alpine" ]]; then + # remove the last version digit + VERSION_ID=${VERSION_ID%.*} + rid=alpine.$VERSION_ID + elif [[ $ID == "ubuntu" ]]; then + rid=$ID.$VERSION_ID fi - echo "ubuntu.14.04" - return 0 + elif [ -e /etc/redhat-release ]; then + redhatRelease=$( - + @@ -17,4 +16,4 @@ - \ No newline at end of file + diff --git a/external/corert/buildscripts/syncAzure.proj b/external/corert/buildscripts/syncAzure.proj index 5e5c3bcc8c..00f13f62d5 100644 --- a/external/corert/buildscripts/syncAzure.proj +++ b/external/corert/buildscripts/syncAzure.proj @@ -1,5 +1,4 @@ - - + @@ -16,4 +15,4 @@ - \ No newline at end of file + diff --git a/external/corert/cross/arm/sources.list.jessie b/external/corert/cross/arm/sources.list.jessie new file mode 100644 index 0000000000..4d142ac9b1 --- /dev/null +++ b/external/corert/cross/arm/sources.list.jessie @@ -0,0 +1,3 @@ +# Debian (sid) # UNSTABLE +deb http://ftp.debian.org/debian/ sid main contrib non-free +deb-src http://ftp.debian.org/debian/ sid main contrib non-free diff --git a/external/corert/cross/arm/sources.list b/external/corert/cross/arm/sources.list.trusty similarity index 100% rename from external/corert/cross/arm/sources.list rename to external/corert/cross/arm/sources.list.trusty diff --git a/external/corert/cross/arm/sources.list.vivid b/external/corert/cross/arm/sources.list.vivid new file mode 100644 index 0000000000..0b1215e475 --- /dev/null +++ b/external/corert/cross/arm/sources.list.vivid @@ -0,0 +1,11 @@ +deb http://ports.ubuntu.com/ubuntu-ports/ vivid main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ vivid main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ vivid-updates main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ vivid-updates main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ vivid-backports main restricted +deb-src http://ports.ubuntu.com/ubuntu-ports/ vivid-backports main restricted + +deb http://ports.ubuntu.com/ubuntu-ports/ vivid-security main restricted universe multiverse +deb-src http://ports.ubuntu.com/ubuntu-ports/ vivid-security main restricted universe multiverse \ No newline at end of file diff --git a/external/corert/cross/arm/sources.list.wily b/external/corert/cross/arm/sources.list.wily new file mode 100644 index 0000000000..e23d1e02a0 --- /dev/null +++ b/external/corert/cross/arm/sources.list.wily @@ -0,0 +1,11 @@ +deb http://ports.ubuntu.com/ubuntu-ports/ wily main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ wily main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ wily-updates main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ wily-updates main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ wily-backports main restricted +deb-src http://ports.ubuntu.com/ubuntu-ports/ wily-backports main restricted + +deb http://ports.ubuntu.com/ubuntu-ports/ wily-security main restricted universe multiverse +deb-src http://ports.ubuntu.com/ubuntu-ports/ wily-security main restricted universe multiverse \ No newline at end of file diff --git a/external/corert/cross/arm/sources.list.xenial b/external/corert/cross/arm/sources.list.xenial new file mode 100644 index 0000000000..eacd86b7df --- /dev/null +++ b/external/corert/cross/arm/sources.list.xenial @@ -0,0 +1,11 @@ +deb http://ports.ubuntu.com/ubuntu-ports/ xenial main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ xenial main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ xenial-updates main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ xenial-updates main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ xenial-backports main restricted +deb-src http://ports.ubuntu.com/ubuntu-ports/ xenial-backports main restricted + +deb http://ports.ubuntu.com/ubuntu-ports/ xenial-security main restricted universe multiverse +deb-src http://ports.ubuntu.com/ubuntu-ports/ xenial-security main restricted universe multiverse \ No newline at end of file diff --git a/external/corert/cross/armel/tizen-fetch.sh b/external/corert/cross/armel/tizen-fetch.sh index 1252199481..58e4b5a347 100755 --- a/external/corert/cross/armel/tizen-fetch.sh +++ b/external/corert/cross/armel/tizen-fetch.sh @@ -51,7 +51,7 @@ if [ ! -d $TMPDIR ]; then mkdir -p $TMPDIR fi -TIZEN_URL=http://download.tizen.org/releases/weekly/tizen +TIZEN_URL=http://download.tizen.org/releases/daily/tizen BUILD_XML=build.xml REPOMD_XML=repomd.xml PRIMARY_XML=primary.xml @@ -120,19 +120,21 @@ fetch_tizen_pkgs_init() fetch_tizen_pkgs() { - PROFILE=$1 - PACKAGE_XPATH_TPL='string(//*[local-name()="metadata"]/*[local-name()="package"][*[local-name()="name"][text()="_PKG_"]]/*[local-name()="location"]/@href)' + ARCH=$1 + PACKAGE_XPATH_TPL='string(//*[local-name()="metadata"]/*[local-name()="package"][*[local-name()="name"][text()="_PKG_"]][*[local-name()="arch"][text()="_ARCH_"]]/*[local-name()="location"]/@href)' - PACKAGE_CHECKSUM_XPATH_TPL='string(//*[local-name()="metadata"]/*[local-name()="package"][*[local-name()="name"][text()="_PKG_"]]/*[local-name()="checksum"]/text())' + PACKAGE_CHECKSUM_XPATH_TPL='string(//*[local-name()="metadata"]/*[local-name()="package"][*[local-name()="name"][text()="_PKG_"]][*[local-name()="arch"][text()="_ARCH_"]]/*[local-name()="checksum"]/text())' for pkg in ${@:2} do Inform "Fetching... $pkg" XPATH=${PACKAGE_XPATH_TPL/_PKG_/$pkg} + XPATH=${XPATH/_ARCH_/$ARCH} Xpath_get $XPATH $TMP_PRIMARY PKG_PATH=$XPATH_RESULT XPATH=${PACKAGE_CHECKSUM_XPATH_TPL/_PKG_/$pkg} + XPATH=${XPATH/_ARCH_/$ARCH} Xpath_get $XPATH $TMP_PRIMARY CHECKSUM=$XPATH_RESULT @@ -142,33 +144,33 @@ fetch_tizen_pkgs() Debug "Download $PKG_URL to $PKG_PATH" Fetch $PKG_URL $PKG_PATH true - + echo "$CHECKSUM $PKG_PATH" | sha256sum -c - > /dev/null if [ $? -ne 0 ]; then Error "Fail to fetch $PKG_URL to $PKG_PATH" Debug "Checksum = $CHECKSUM" exit 1 fi - done } -Inform "Initialize arm base" -fetch_tizen_pkgs_init arm base -Inform "fetch base common packages" -fetch_tizen_pkgs base gcc glibc glibc-devel linux-glibc-devel -Inform "fetch base coreclr packages" -fetch_tizen_pkgs base lldb lldb-devel libuuid libuuid-devel libgcc libstdc++ libstdc++-devel -Inform "fetch base corefx packages" -fetch_tizen_pkgs base libcom_err libcom_err-devel zlib zlib-devel libopenssl libopenssl-devel +Inform "Initialize base" +fetch_tizen_pkgs_init standard base +Inform "fetch common packages" +fetch_tizen_pkgs armv7l gcc glibc glibc-devel +fetch_tizen_pkgs noarch linux-glibc-devel +Inform "fetch coreclr packages" +fetch_tizen_pkgs armv7l lldb lldb-devel libuuid libuuid-devel libgcc libstdc++ libstdc++-devel libunwind libunwind-devel +Inform "fetch corefx packages" +fetch_tizen_pkgs armv7l libcom_err libcom_err-devel zlib zlib-devel libopenssl libopenssl-devel -Inform "initialize arm mobile" -fetch_tizen_pkgs_init arm-wayland mobile -Inform "fetch mobile common packages" -fetch_tizen_pkgs mobile libicu-devel -Inform "fetch mobile coreclr packages" -fetch_tizen_pkgs mobile libunwind libunwind-devel tizen-release -Inform "fetch mobile corefx packages" -fetch_tizen_pkgs mobile gssdp gssdp-devel krb5 krb5-devel libcurl libcurl-devel +Inform "Initialize unified" +fetch_tizen_pkgs_init standard unified +Inform "fetch common packages" +fetch_tizen_pkgs armv7l libicu icu libicu-devel +Inform "fetch coreclr packages" +fetch_tizen_pkgs armv7l tizen-release +Inform "fetch corefx packages" +fetch_tizen_pkgs armv7l gssdp gssdp-devel krb5 krb5-devel libcurl libcurl-devel diff --git a/external/corert/cross/armel/tizen/tizen.patch b/external/corert/cross/armel/tizen/tizen.patch index c92dc02809..d223427c97 100644 --- a/external/corert/cross/armel/tizen/tizen.patch +++ b/external/corert/cross/armel/tizen/tizen.patch @@ -16,18 +16,3 @@ diff -u -r a/usr/lib/libpthread.so b/usr/lib/libpthread.so OUTPUT_FORMAT(elf32-littlearm) -GROUP ( /lib/libpthread.so.0 /usr/lib/libpthread_nonshared.a ) +GROUP ( libpthread.so.0 libpthread_nonshared.a ) -diff -u -r a/usr/lib/libpthread.so b/usr/lib/libpthread.so ---- a/etc/os-release 2016-10-17 23:39:36.000000000 +0900 -+++ b/etc/os-release 2017-01-05 14:34:39.099867682 +0900 -@@ -1,7 +1,7 @@ - NAME=Tizen --VERSION="3.0.0 (Tizen3/Mobile)" -+VERSION="4.0.0 (Tizen4/Mobile)" - ID=tizen --VERSION_ID=3.0.0 --PRETTY_NAME="Tizen 3.0.0 (Tizen3/Mobile)" -+VERSION_ID=4.0.0 -+PRETTY_NAME="Tizen 4.0.0 (Tizen4/Mobile)" - ANSI_COLOR="0;36" --CPE_NAME="cpe:/o:tizen:tizen:3.0.0" -+CPE_NAME="cpe:/o:tizen:tizen:4.0.0" diff --git a/external/corert/cross/armel/toolchain.cmake b/external/corert/cross/armel/toolchain.cmake index 95bff0878b..1dc6ec5d88 100644 --- a/external/corert/cross/armel/toolchain.cmake +++ b/external/corert/cross/armel/toolchain.cmake @@ -23,11 +23,11 @@ set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -target ${TOOLCHAIN}") set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} --sysroot=${CROSS_ROOTFS}") if("$ENV{__DistroRid}" MATCHES "tizen.*") - add_compile_options(-I$ENV{ROOTFS_DIR}/usr/lib/gcc/armv7l-tizen-linux-gnueabi/4.9.2/include/c++/) - add_compile_options(-I$ENV{ROOTFS_DIR}/usr/lib/gcc/armv7l-tizen-linux-gnueabi/4.9.2/include/c++/armv7l-tizen-linux-gnueabi) + set(TIZEN_TOOLCHAIN "armv7l-tizen-linux-gnueabi/6.2.1") + add_compile_options(-I$ENV{ROOTFS_DIR}/usr/lib/gcc/${TIZEN_TOOLCHAIN}/include/c++/) + add_compile_options(-I$ENV{ROOTFS_DIR}/usr/lib/gcc/${TIZEN_TOOLCHAIN}/include/c++/armv7l-tizen-linux-gnueabi) add_compile_options(-Wno-deprecated-declarations) # compile-time option add_compile_options(-D__extern_always_inline=inline) - set(TIZEN_TOOLCHAIN "armv7l-tizen-linux-gnueabi/4.9.2") set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -B${CROSS_ROOTFS}/usr/lib/gcc/${TIZEN_TOOLCHAIN}") set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -L${CROSS_ROOTFS}/usr/lib/gcc/${TIZEN_TOOLCHAIN}") else() diff --git a/external/corert/cross/build-rootfs.sh b/external/corert/cross/build-rootfs.sh index 16c67b116d..07d6c3be76 100755 --- a/external/corert/cross/build-rootfs.sh +++ b/external/corert/cross/build-rootfs.sh @@ -2,12 +2,14 @@ usage() { - echo "Usage: $0 [BuildArch]" - echo "BuildArch can be: arm, arm64" - + echo "Usage: $0 [BuildArch] [LinuxCodeName] [cross]" + echo "BuildArch can be: arm(default), armel, arm64, x86" + echo "LinuxCodeName - optional, Code name for Linux, can be: trusty(default), vivid, wily, jessie, xenial. If BuildArch is armel, LinuxCodeName is jessie(default) or tizen." + echo "cross - optional, it initializes rootfs for cross building, works only for armel tizen now" exit 1 } +__LinuxCodeName=trusty __CrossDir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) __InitialDir=$PWD __BuildArch=arm @@ -30,14 +32,10 @@ for i in "$@" arm) __BuildArch=arm __UbuntuArch=armhf - __UbuntuRepo="http://ports.ubuntu.com/" - __UbuntuPackages="build-essential lldb-3.6-dev libunwind8-dev gettext symlinks liblttng-ust-dev libicu-dev" - __MachineTriple=arm-linux-gnueabihf ;; arm64) __BuildArch=arm64 __UbuntuArch=arm64 - __UbuntuRepo="http://ports.ubuntu.com/" __UbuntuPackages="build-essential libunwind8-dev gettext symlinks liblttng-ust-dev libicu-dev" __MachineTriple=aarch64-linux-gnu ;; @@ -47,6 +45,11 @@ for i in "$@" __UbuntuRepo="http://ftp.debian.org/debian/" __LinuxCodeName=jessie ;; + x86) + __BuildArch=x86 + __UbuntuArch=i386 + __UbuntuRepo="http://archive.ubuntu.com/ubuntu/" + ;; lldb3.6) __LLDB_Package="lldb-3.6-dev" ;; @@ -82,6 +85,17 @@ for i in "$@" __UbuntuRepo= __Tizen=tizen ;; + cross) + if [ "$__Tizen" != "tizen" ]; then + echo "Cross building rootfs is available only for armel tizen." + usage; + exit 1; + fi + # Cross building is available for armel tizen only with x86 rootfs + echo Building x86 xenial rootfs for armel tizen cross build... + $0 x86 xenial + echo Building armel rootfs... + ;; --skipunmount) __SkipUnmount=1 ;; @@ -95,7 +109,7 @@ if [ "$__BuildArch" == "armel" ]; then fi __RootfsDir="$__CrossDir/rootfs/$__BuildArch" -__UbuntuPackages="$__UbuntuPackagesBase $__LLDB_Package" +__UbuntuPackages+=" ${__LLDB_Package:-}" if [[ -n "$ROOTFS_DIR" ]]; then @@ -109,16 +123,22 @@ if [ -d "$__RootfsDir" ]; then rm -rf $__RootfsDir fi -if [ "$__Tizen" == "tizen" ]; then - ROOTFS_DIR=$__RootfsDir $__CrossDir/$__BuildArch/tizen-build-rootfs.sh -else - qemu-debootstrap --arch $__UbuntuArch trusty $__RootfsDir $__UbuntuRepo - cp $__CrossDir/$__BuildArch/sources.list $__RootfsDir/etc/apt/sources.list + +if [[ -n $__LinuxCodeName ]]; then + qemu-debootstrap --arch $__UbuntuArch $__LinuxCodeName $__RootfsDir $__UbuntuRepo + cp $__CrossDir/$__BuildArch/sources.list.$__LinuxCodeName $__RootfsDir/etc/apt/sources.list chroot $__RootfsDir apt-get update + chroot $__RootfsDir apt-get -f -y install chroot $__RootfsDir apt-get -y install $__UbuntuPackages chroot $__RootfsDir symlinks -cr /usr if [ $__SkipUnmount == 0 ]; then umount $__RootfsDir/* fi +elif [ "$__Tizen" == "tizen" ]; then + ROOTFS_DIR=$__RootfsDir $__CrossDir/$__BuildArch/tizen-build-rootfs.sh +else + echo "Unsupported target platform." + usage; + exit 1 fi diff --git a/external/corert/cross/x86/sources.list.trusty b/external/corert/cross/x86/sources.list.trusty new file mode 100644 index 0000000000..9b3085436e --- /dev/null +++ b/external/corert/cross/x86/sources.list.trusty @@ -0,0 +1,11 @@ +deb http://archive.ubuntu.com/ubuntu/ trusty main restricted universe +deb-src http://archive.ubuntu.com/ubuntu/ trusty main restricted universe + +deb http://archive.ubuntu.com/ubuntu/ trusty-updates main restricted universe +deb-src http://archive.ubuntu.com/ubuntu/ trusty-updates main restricted universe + +deb http://archive.ubuntu.com/ubuntu/ trusty-backports main restricted +deb-src http://archive.ubuntu.com/ubuntu/ trusty-backports main restricted + +deb http://archive.ubuntu.com/ubuntu/ trusty-security main restricted universe multiverse +deb-src http://archive.ubuntu.com/ubuntu/ trusty-security main restricted universe multiverse diff --git a/external/corert/cross/x86/sources.list.vivid b/external/corert/cross/x86/sources.list.vivid new file mode 100644 index 0000000000..26d37b20fc --- /dev/null +++ b/external/corert/cross/x86/sources.list.vivid @@ -0,0 +1,11 @@ +deb http://archive.ubuntu.com/ubuntu/ vivid main restricted universe +deb-src http://archive.ubuntu.com/ubuntu/ vivid main restricted universe + +deb http://archive.ubuntu.com/ubuntu/ vivid-updates main restricted universe +deb-src http://archive.ubuntu.com/ubuntu/ vivid-updates main restricted universe + +deb http://archive.ubuntu.com/ubuntu/ vivid-backports main restricted +deb-src http://archive.ubuntu.com/ubuntu/ vivid-backports main restricted + +deb http://archive.ubuntu.com/ubuntu/ vivid-security main restricted universe multiverse +deb-src http://archive.ubuntu.com/ubuntu/ vivid-security main restricted universe multiverse diff --git a/external/corert/cross/x86/sources.list.wily b/external/corert/cross/x86/sources.list.wily new file mode 100644 index 0000000000..c4b0b442ab --- /dev/null +++ b/external/corert/cross/x86/sources.list.wily @@ -0,0 +1,11 @@ +deb http://archive.ubuntu.com/ubuntu/ wily main restricted universe +deb-src http://archive.ubuntu.com/ubuntu/ wily main restricted universe + +deb http://archive.ubuntu.com/ubuntu/ wily-updates main restricted universe +deb-src http://archive.ubuntu.com/ubuntu/ wily-updates main restricted universe + +deb http://archive.ubuntu.com/ubuntu/ wily-backports main restricted +deb-src http://archive.ubuntu.com/ubuntu/ wily-backports main restricted + +deb http://archive.ubuntu.com/ubuntu/ wily-security main restricted universe multiverse +deb-src http://archive.ubuntu.com/ubuntu/ wily-security main restricted universe multiverse diff --git a/external/corert/cross/x86/sources.list.xenial b/external/corert/cross/x86/sources.list.xenial new file mode 100644 index 0000000000..ad9c5a0144 --- /dev/null +++ b/external/corert/cross/x86/sources.list.xenial @@ -0,0 +1,11 @@ +deb http://archive.ubuntu.com/ubuntu/ xenial main restricted universe +deb-src http://archive.ubuntu.com/ubuntu/ xenial main restricted universe + +deb http://archive.ubuntu.com/ubuntu/ xenial-updates main restricted universe +deb-src http://archive.ubuntu.com/ubuntu/ xenial-updates main restricted universe + +deb http://archive.ubuntu.com/ubuntu/ xenial-backports main restricted +deb-src http://archive.ubuntu.com/ubuntu/ xenial-backports main restricted + +deb http://archive.ubuntu.com/ubuntu/ xenial-security main restricted universe multiverse +deb-src http://archive.ubuntu.com/ubuntu/ xenial-security main restricted universe multiverse diff --git a/external/corert/cross/x86/toolchain.cmake b/external/corert/cross/x86/toolchain.cmake new file mode 100644 index 0000000000..1389a768fb --- /dev/null +++ b/external/corert/cross/x86/toolchain.cmake @@ -0,0 +1,39 @@ +set(CROSS_ROOTFS $ENV{ROOTFS_DIR}) + +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR i686) + +add_compile_options("-m32") +add_compile_options("--sysroot=${CROSS_ROOTFS}") +add_compile_options("-Wno-error=unused-command-line-argument") + +set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} --sysroot=${CROSS_ROOTFS}") +set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -B ${CROSS_ROOTFS}/usr/lib/gcc/i686-linux-gnu") +set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -L${CROSS_ROOTFS}/lib/i386-linux-gnu") +set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -m32") + +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${CROSS_LINK_FLAGS}" CACHE STRING "" FORCE) +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${CROSS_LINK_FLAGS}" CACHE STRING "" FORCE) +set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${CROSS_LINK_FLAGS}" CACHE STRING "" FORCE) + +set(CMAKE_FIND_ROOT_PATH "${CROSS_ROOTFS}") +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +set(LLVM_CROSS_DIR "$ENV{LLVM_CROSS_HOME}") +if(LLVM_CROSS_DIR) + set(WITH_LLDB_LIBS "${LLVM_CROSS_DIR}/lib/" CACHE STRING "") + set(WITH_LLDB_INCLUDES "${LLVM_CROSS_DIR}/include" CACHE STRING "") + set(LLDB_H "${WITH_LLDB_INCLUDES}" CACHE STRING "") + set(LLDB "${LLVM_CROSS_DIR}/lib/liblldb.so" CACHE STRING "") +else() + set(WITH_LLDB_LIBS "${CROSS_ROOTFS}/usr/lib/i386-linux-gnu" CACHE STRING "") + set(CHECK_LLVM_DIR "${CROSS_ROOTFS}/usr/lib/llvm-3.8/include") + if(EXISTS "${CHECK_LLVM_DIR}" AND IS_DIRECTORY "${CHECK_LLVM_DIR}") + set(WITH_LLDB_INCLUDES "${CHECK_LLVM_DIR}") + else() + set(WITH_LLDB_INCLUDES "${CROSS_ROOTFS}/usr/lib/llvm-3.6/include") + endif() +endif() diff --git a/external/corert/cross/x86/tryrun.cmake b/external/corert/cross/x86/tryrun.cmake new file mode 100644 index 0000000000..1bf3da935c --- /dev/null +++ b/external/corert/cross/x86/tryrun.cmake @@ -0,0 +1,127 @@ +SET( REALPATH_SUPPORTS_NONEXISTENT_FILES_EXITCODE + 1 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( SSCANF_SUPPORT_ll_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( SSCANF_CANNOT_HANDLE_MISSING_EXPONENT_EXITCODE + 1 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAVE_LARGE_SNPRINTF_SUPPORT_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAVE_SCHED_GET_PRIORITY_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAVE_WORKING_GETTIMEOFDAY_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAVE_WORKING_CLOCK_GETTIME_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAVE_CLOCK_MONOTONIC_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAVE_MMAP_DEV_ZERO_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS_EXITCODE + 1 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( PTHREAD_CREATE_MODIFIES_ERRNO_EXITCODE + 1 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( SEM_INIT_MODIFIES_ERRNO_EXITCODE + 1 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAVE_COMPATIBLE_ACOS_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAVE_COMPATIBLE_ASIN_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAVE_COMPATIBLE_POW_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAVE_VALID_NEGATIVE_INF_POW_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAVE_VALID_POSITIVE_INF_POW_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAVE_COMPATIBLE_ATAN2_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAVE_COMPATIBLE_LOG_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAVE_COMPATIBLE_LOG10_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( UNGETC_NOT_RETURN_EOF_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAS_POSIX_SEMAPHORES_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( GETPWUID_R_SETS_ERRNO_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL_EXITCODE + 1 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAVE_CLOCK_THREAD_CPUTIME_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAVE_SCHED_GETCPU_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAVE_CLOCK_MONOTONIC_COARSE_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAVE_PROCFS_CTL_EXITCODE + 1 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAVE_PROCFS_MAPS_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAVE_PROCFS_STAT_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAVE_PROCFS_STATUS_EXITCODE + 0 + CACHE STRING "Result from TRY_RUN" FORCE) + +SET( HAVE_FUNCTIONAL_PTHREAD_ROBUST_MUTEXES + 0 + CACHE STRING "Result from TRY_RUN" FORCE) diff --git a/external/corert/dependencies.props b/external/corert/dependencies.props new file mode 100644 index 0000000000..dd8d755ceb --- /dev/null +++ b/external/corert/dependencies.props @@ -0,0 +1,11 @@ + + + 2.1.0-preview1-26030-01 + 1.0.19-prerelease-00001 + 4.5.0-preview1-26029-02 + 4.6.0-preview1-26029-02 + 2.1.0-preview1-26030-01 + 2.0.0 + 1.0.1-prerelease-02104-02 + + diff --git a/external/corert/dir.props b/external/corert/dir.props index bd405c8b7e..b3b81a0dc1 100644 --- a/external/corert/dir.props +++ b/external/corert/dir.props @@ -1,4 +1,4 @@ - + - - true - - $(__BuildOS) @@ -66,95 +58,31 @@ $(ProjectDir)bin/ $(BinDir)obj/ - $(BinDir)Product/ - $(BinDir)tests/ + $(ObjDir) $(BinDir)packages_noship/ - $(ProductBinDir)pkg/ - - - $(BinDir)packages/ + $(BinDir)packages/ $(ProjectDir)packages/ - $(ProjectDir)Tools/ - $(ToolRuntimePath) + $(PackagesDir) + $(ProjectDir)Tools/ - $(ToolRuntimePath)dotnetcli/ - $(DotnetCliPath)dotnet - $(ToolsDir)net46/ - $(ToolsDir) - $(BuildToolsTaskDir) + + + + + + + + + + + Open - - - $(BinDirOSGroup).$(BinDirPlatform).$(BinDirConfiguration) - - $(ProductBinDir) - $(PackageOutputRoot)$(OSPlatformConfig)/$(MSBuildProjectName)/ - $(PackageOutputPath)symbols/ - $(BaseOutputPath)$(OSPlatformConfig)/$(MSBuildProjectName) - - - $(BaseOutputPath)$(OSPlatformConfig)/packaging/ - - $(ObjDir) - $(BaseIntermediateOutputPath)$(OSPlatformConfig)\ - $(IntermediateOutputRootPath)$(MSBuildProjectName)\ - $(IntermediateOutputRootPath)\CoreRTRef - $(PackagesDir)/runtime.win10-x64-aot.Microsoft.Private.CoreFx.UAP/4.4.0-preview1-25218-01/runtimes/win10-x64-aot/lib/uap10.1 - - $(TestWorkingDir)$(OSPlatformConfig)\$(MSBuildProjectName)\ - - $(BinDir)$(OSPlatformConfig) - - - - - - - - - - - - - - - - - - - - - $(DotnetCliPath)dotnet.exe - $(DotnetCliPath)dotnet - $(DotnetToolCommand) - - @(DnuSourceList -> '--source %(Identity)', ' ') - - "$(DnuToolPath)" - $(DnuRestoreCommand) restore - $(DnuRestoreCommand) --parallel - $(DnuRestoreCommand) --packages "$(PackagesDir.TrimEnd('/\'.ToCharArray()))" $(DnuRestoreSource) - $(DnuRestoreCommand) --lock - - - - - false - - - - - true - - - - true @@ -175,7 +103,8 @@ true false false - 7 + latest + portable @@ -184,13 +113,45 @@ true - $(SourceDir)Common\src $(SourceDir)Common\tests + + + $(BinDirOSGroup).$(BinDirPlatform).$(BinDirConfiguration) + + $(BinDir) + + $(BaseOutputPath)$(OSPlatformConfig)/$(MSBuildProjectName) + + $(BaseIntermediateOutputPath)$(OSPlatformConfig)/ + $(IntermediateOutputRootPath)$(MSBuildProjectName) + + $(IntermediateOutputRootPath)test-runtime/ + + + $(DotnetCliPath) + + $(PackageOutputRoot)$(BinDirConfiguration)/ + $(PackageOutputPath)symbols/ + + + $(BaseOutputPath)$(OSPlatformConfig)/packaging/ + + $(IntermediateOutputPath) + + $(IntermediateOutputRootPath)\AotPackageReference + + $(BinDir)$(OSPlatformConfig) + + + + + + false @@ -213,6 +174,13 @@ win-corert + + + true + false + unix-corert + + true @@ -253,7 +221,7 @@ - + CORERT;$(DefineConstants) @@ -280,9 +248,12 @@ ARM64;BIT64;$(DefineConstants) + + WASM;BIT32;$(DefineConstants) + - netcoreapp2.0 + true .NETCoreApp,Version=v2.0 false @@ -292,7 +263,6 @@ true - - - + + diff --git a/external/corert/dir.targets b/external/corert/dir.targets index f0d140e64a..ba5405fa2b 100644 --- a/external/corert/dir.targets +++ b/external/corert/dir.targets @@ -1,5 +1,4 @@ - - + false - - - true + + + + .NETCoreApp + v2.0 - + + + .NETStandard,Version=v1.3 + .NETStandard + v1.3 + + + + 1.6.1 + + + + + + .NETStandard + + + + $(RuntimeIdentifiers) + + + - - - - https://github.com/dotnet/corert/blob/master/LICENSE.TXT - - - - - - <_TargetFrameworkDirectories>$(MSBuildThisFileDirectory)/Documentation - <_FullFrameworkReferenceAssemblyPaths>$(MSBuildThisFileDirectory)/Documentation - - - - true - diff --git a/external/corert/dir.traversal.targets b/external/corert/dir.traversal.targets index 6980585777..dc4e3da102 100644 --- a/external/corert/dir.traversal.targets +++ b/external/corert/dir.traversal.targets @@ -1,108 +1,6 @@ - - + - - - $(OSGroup) - - - - - - TargetGroup=%(Project.TargetGroup);%(Project.AdditionalProperties) - - - $(FilterToTargetGroup) - - - $(FilterToOSGroup) - - - OSGroup=%(Project.OSGroup);%(Project.AdditionalProperties) - - - Platform=%(Project.Platform);%(Project.AdditionalProperties) - - - FilterToOSGroup=%(Project.FilterToOSGroup);%(Project.AdditionalProperties) - - - FilterToTargetGroup=%(Project.FilterToTargetGroup);%(Project.AdditionalProperties) - - - InputOSGroup=%(Project.InputOSGroup);%(Project.AdditionalProperties) - - - BuildAllOSGroups=%(Project.BuildAllOSGroups);%(Project.AdditionalProperties) - - - - %(Project.UndefineProperties);OSGroup - - - %(Project.UndefineProperties);TestTFMs;FilterToOSGroup;FilterToTestTFM;DefaultBuildAllTarget;SerializeProjects;BuildAllOSGroups - - - - - - - SerializeProjects=true;%(Project.AdditionalProperties) - - - - - AnyOS;$(FilterToOSGroup); - $(OSGroupList);Unix; - $(OSGroupList);Unix; - $(OSGroupList);Unix; - $(OSGroupList);Unix; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + $(MSBuildProjectDefaultTargets) @@ -126,50 +24,17 @@ - - - Clean - - - - - - - - - - - - + - RestorePackages + Restore - - + - - @@ -182,23 +47,14 @@ $(TraversalBuildDependsOn); - - CleanAllProjects; - $(TraversalCleanDependsOn); - - - - RestoreAllProjectPackages; - $(TraversalRestorePackagesDependsOn) - + + RestoreAllProjects; + $(TraversalRestoreDependsOn) + - - - - - + diff --git a/external/corert/global.json b/external/corert/global.json deleted file mode 100644 index ea0c3af8a3..0000000000 --- a/external/corert/global.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "projects": [ "./src/Common" ] -} diff --git a/external/corert/init-tools.cmd b/external/corert/init-tools.cmd index 3db46fc0e7..2818750203 100644 --- a/external/corert/init-tools.cmd +++ b/external/corert/init-tools.cmd @@ -9,10 +9,8 @@ if [%DOTNET_CMD%]==[] set DOTNET_CMD=%DOTNET_PATH%dotnet.exe if [%BUILDTOOLS_SOURCE%]==[] set BUILDTOOLS_SOURCE=https://dotnet.myget.org/F/dotnet-buildtools/api/v3/index.json set /P BUILDTOOLS_VERSION=< "%~dp0BuildToolsVersion.txt" set BUILD_TOOLS_PATH=%PACKAGES_DIR%Microsoft.DotNet.BuildTools\%BUILDTOOLS_VERSION%\lib\ -set PROJECT_JSON_PATH=%TOOLRUNTIME_DIR%\%BUILDTOOLS_VERSION% -set PROJECT_JSON_FILE=%PROJECT_JSON_PATH%\project.json -set PROJECT_JSON_CONTENTS={ "dependencies": { "Microsoft.DotNet.BuildTools": "%BUILDTOOLS_VERSION%" }, "frameworks": { "netcoreapp1.0": { } } } -set BUILD_TOOLS_SEMAPHORE=%PROJECT_JSON_PATH%\init-tools.completed +set INIT_TOOLS_RESTORE_PROJECT=%~dp0init-tools.msbuild +set BUILD_TOOLS_SEMAPHORE=%TOOLRUNTIME_DIR%\%BUILDTOOLS_VERSION%\init-tools.completed :: if force option is specified then clean the tool runtime and build tools package directory to force it to get recreated if [%1]==[force] ( @@ -20,7 +18,7 @@ if [%1]==[force] ( if exist "%PACKAGES_DIR%Microsoft.DotNet.BuildTools" rmdir /S /Q "%PACKAGES_DIR%Microsoft.DotNet.BuildTools" ) -:: If sempahore exists do nothing +:: If semaphore exists do nothing if exist "%BUILD_TOOLS_SEMAPHORE%" ( echo Tools are already initialized. goto :EOF @@ -28,34 +26,32 @@ if exist "%BUILD_TOOLS_SEMAPHORE%" ( if exist "%TOOLRUNTIME_DIR%" rmdir /S /Q "%TOOLRUNTIME_DIR%" -if NOT exist "%PROJECT_JSON_PATH%" mkdir "%PROJECT_JSON_PATH%" -echo %PROJECT_JSON_CONTENTS% > "%PROJECT_JSON_FILE%" echo Running %0 > "%INIT_TOOLS_LOG%" +set /p DOTNET_VERSION=< "%~dp0DotnetCLIVersion.txt" if exist "%DOTNET_CMD%" goto :afterdotnetrestore echo Installing dotnet cli... if NOT exist "%DOTNET_PATH%" mkdir "%DOTNET_PATH%" -set /p DOTNET_VERSION=< "%~dp0DotnetCLIVersion.txt" -set DOTNET_ZIP_NAME=dotnet-dev-win-x64.%DOTNET_VERSION%.zip -set DOTNET_REMOTE_PATH=https://dotnetcli.blob.core.windows.net/dotnet/Sdk/%DOTNET_VERSION%/%DOTNET_ZIP_NAME% +set DOTNET_ZIP_NAME=dotnet-sdk-%DOTNET_VERSION%-win-x64.zip +set DOTNET_REMOTE_PATH=https://dotnetcli.azureedge.net/dotnet/Sdk/%DOTNET_VERSION%/%DOTNET_ZIP_NAME% set DOTNET_LOCAL_PATH=%DOTNET_PATH%%DOTNET_ZIP_NAME% echo Installing '%DOTNET_REMOTE_PATH%' to '%DOTNET_LOCAL_PATH%' >> "%INIT_TOOLS_LOG%" powershell -NoProfile -ExecutionPolicy unrestricted -Command "$retryCount = 0; $success = $false; do { try { (New-Object Net.WebClient).DownloadFile('%DOTNET_REMOTE_PATH%', '%DOTNET_LOCAL_PATH%'); $success = $true; } catch { if ($retryCount -ge 6) { throw; } else { $retryCount++; Start-Sleep -Seconds (5 * $retryCount); } } } while ($success -eq $false); Add-Type -Assembly 'System.IO.Compression.FileSystem' -ErrorVariable AddTypeErrors; if ($AddTypeErrors.Count -eq 0) { [System.IO.Compression.ZipFile]::ExtractToDirectory('%DOTNET_LOCAL_PATH%', '%DOTNET_PATH%') } else { (New-Object -com shell.application).namespace('%DOTNET_PATH%').CopyHere((new-object -com shell.application).namespace('%DOTNET_LOCAL_PATH%').Items(),16) }" >> "%INIT_TOOLS_LOG%" if NOT exist "%DOTNET_LOCAL_PATH%" ( - echo ERROR: Could not install dotnet cli correctly. See '%INIT_TOOLS_LOG%' for more details. 1>&2 - exit /b 1 + echo ERROR: Could not install dotnet cli correctly. 1>&2 + goto :error ) :afterdotnetrestore if exist "%BUILD_TOOLS_PATH%" goto :afterbuildtoolsrestore echo Restoring BuildTools version %BUILDTOOLS_VERSION%... -echo Running: "%DOTNET_CMD%" restore "%PROJECT_JSON_FILE%" --no-cache --packages %PACKAGES_DIR% --source "%BUILDTOOLS_SOURCE%" >> "%INIT_TOOLS_LOG%" -call "%DOTNET_CMD%" restore "%PROJECT_JSON_FILE%" --no-cache --packages %PACKAGES_DIR% --source "%BUILDTOOLS_SOURCE%" >> "%INIT_TOOLS_LOG%" +echo Running: "%DOTNET_CMD%" restore "%INIT_TOOLS_RESTORE_PROJECT%" --no-cache --packages %PACKAGES_DIR% --source "%BUILDTOOLS_SOURCE%" /p:BuildToolsPackageVersion=%BUILDTOOLS_VERSION% >> "%INIT_TOOLS_LOG%" +call "%DOTNET_CMD%" restore "%INIT_TOOLS_RESTORE_PROJECT%" --no-cache --packages %PACKAGES_DIR% --source "%BUILDTOOLS_SOURCE%" /p:BuildToolsPackageVersion=%BUILDTOOLS_VERSION% >> "%INIT_TOOLS_LOG%" if NOT exist "%BUILD_TOOLS_PATH%init-tools.cmd" ( - echo ERROR: Could not restore build tools correctly. See '%INIT_TOOLS_LOG%' for more details. 1>&2 - exit /b 1 + echo ERROR: Could not restore build tools correctly. 1>&2 + goto :error ) :afterbuildtoolsrestore @@ -65,11 +61,19 @@ echo Running: "%BUILD_TOOLS_PATH%init-tools.cmd" "%~dp0" "%DOTNET_CMD%" "%TOOLRU call "%BUILD_TOOLS_PATH%init-tools.cmd" "%~dp0" "%DOTNET_CMD%" "%TOOLRUNTIME_DIR%" >> "%INIT_TOOLS_LOG%" set INIT_TOOLS_ERRORLEVEL=%ERRORLEVEL% if not [%INIT_TOOLS_ERRORLEVEL%]==[0] ( - echo ERROR: An error occured when trying to initialize the tools. Please check '%INIT_TOOLS_LOG%' for more details. 1>&2 - exit /b %INIT_TOOLS_ERRORLEVEL% + echo ERROR: An error occured when trying to initialize the tools. 1>&2 + goto :error ) +rem CoreRT does not use special copy of the shared runtime for testing +copy /Y %TOOLRUNTIME_DIR%\csc.runtimeconfig.json %TOOLRUNTIME_DIR%\xunit.console.netcore.runtimeconfig.json + :: Create semaphore file echo Done initializing tools. echo Init-Tools.cmd completed for BuildTools Version: %BUILDTOOLS_VERSION% > "%BUILD_TOOLS_SEMAPHORE%" exit /b 0 + +:error +echo Please check the detailed log that follows. 1>&2 +type "%INIT_TOOLS_LOG%" 1>&2 +exit /b 1 diff --git a/external/corert/init-tools.msbuild b/external/corert/init-tools.msbuild new file mode 100644 index 0000000000..1ec33f6782 --- /dev/null +++ b/external/corert/init-tools.msbuild @@ -0,0 +1,11 @@ + + + netcoreapp1.0 + false + true + $(MSBuildThisFileDirectory)Tools/$(BuildToolsPackageVersion) + + + + + \ No newline at end of file diff --git a/external/corert/init-tools.sh b/external/corert/init-tools.sh index a2a94c8017..ed2cffd5d0 100755 --- a/external/corert/init-tools.sh +++ b/external/corert/init-tools.sh @@ -1,157 +1,160 @@ #!/usr/bin/env bash __scriptpath=$(cd "$(dirname "$0")"; pwd -P) + +if [ "$BUILDVARS_DONE" != 1 ]; then + . $__scriptpath/buildscripts/hostvars-setup.sh +fi + __init_tools_log=$__scriptpath/init-tools.log __PACKAGES_DIR=$__scriptpath/packages __TOOLRUNTIME_DIR=$__scriptpath/Tools __DOTNET_PATH=$__TOOLRUNTIME_DIR/dotnetcli __DOTNET_CMD=$__DOTNET_PATH/dotnet if [ -z "$__BUILDTOOLS_SOURCE" ]; then __BUILDTOOLS_SOURCE=https://dotnet.myget.org/F/dotnet-buildtools/api/v3/index.json; fi +export __BUILDTOOLS_USE_CSPROJ=true __BUILD_TOOLS_PACKAGE_VERSION=$(cat $__scriptpath/BuildToolsVersion.txt) __DOTNET_TOOLS_VERSION=$(cat $__scriptpath/DotnetCLIVersion.txt) -__BUILD_TOOLS_PATH=$__PACKAGES_DIR/Microsoft.DotNet.BuildTools/$__BUILD_TOOLS_PACKAGE_VERSION/lib -__PROJECT_JSON_PATH=$__TOOLRUNTIME_DIR/$__BUILD_TOOLS_PACKAGE_VERSION -__PROJECT_JSON_FILE=$__PROJECT_JSON_PATH/project.json -__PROJECT_JSON_CONTENTS="{ \"dependencies\": { \"Microsoft.DotNet.BuildTools\": \"$__BUILD_TOOLS_PACKAGE_VERSION\" }, \"frameworks\": { \"netcoreapp1.0\": { } } }" -__INIT_TOOLS_DONE_MARKER=$__PROJECT_JSON_PATH/done - -# Extended version of platform detection logic from dotnet/cli/scripts/obtain/dotnet-install.sh 16692fc -get_current_linux_name() { - # Detect Distro - if [ "$(cat /etc/*-release | grep -cim1 ubuntu)" -eq 1 ]; then - if [ "$(cat /etc/*-release | grep -cim1 16.04)" -eq 1 ]; then - echo "ubuntu.16.04" - return 0 - fi - if [ "$(cat /etc/*-release | grep -cim1 16.10)" -eq 1 ]; then - echo "ubuntu.16.10" - return 0 - fi - - echo "ubuntu" - return 0 - elif [ "$(cat /etc/*-release | grep -cim1 centos)" -eq 1 ]; then - echo "centos" - return 0 - elif [ "$(cat /etc/*-release | grep -cim1 rhel)" -eq 1 ]; then - echo "rhel" - return 0 - elif [ "$(cat /etc/*-release | grep -cim1 debian)" -eq 1 ]; then - echo "debian" - return 0 - elif [ "$(cat /etc/*-release | grep -cim1 fedora)" -eq 1 ]; then - if [ "$(cat /etc/*-release | grep -cim1 23)" -eq 1 ]; then - echo "fedora.23" - return 0 - fi - if [ "$(cat /etc/*-release | grep -cim1 24)" -eq 1 ]; then - echo "fedora.24" - return 0 - fi - elif [ "$(cat /etc/*-release | grep -cim1 opensuse)" -eq 1 ]; then - if [ "$(cat /etc/*-release | grep -cim1 13.2)" -eq 1 ]; then - echo "opensuse.13.2" - return 0 - fi - if [ "$(cat /etc/*-release | grep -cim1 42.1)" -eq 1 ]; then - echo "opensuse.42.1" - return 0 - fi - fi - - # Cannot determine Linux distribution, assuming Ubuntu 14.04. - echo "ubuntu" - return 0 -} +__BUILD_TOOLS_PATH=$__PACKAGES_DIR/microsoft.dotnet.buildtools/$__BUILD_TOOLS_PACKAGE_VERSION/lib +__INIT_TOOLS_RESTORE_PROJECT=$__scriptpath/init-tools.msbuild +__INIT_TOOLS_DONE_MARKER_DIR=$__TOOLRUNTIME_DIR/$__BUILD_TOOLS_PACKAGE_VERSION +__INIT_TOOLS_DONE_MARKER=$__INIT_TOOLS_DONE_MARKER_DIR/done if [ -z "$__DOTNET_PKG" ]; then -OSName=$(uname -s) + OSName=$(uname -s) case $OSName in Darwin) OS=OSX - __DOTNET_PKG=dotnet-dev-osx-x64 - __DOTNET_RUNTIME_PKG=dotnet-osx-x64 + __PKG_RID=osx ulimit -n 2048 + # Format x.y.z as single integer with three digits for each part + VERSION=`sw_vers -productVersion| sed -e 's/\./ /g' | xargs printf "%03d%03d%03d"` + if [ "$VERSION" -lt 010012000 ]; then + echo error: macOS version `sw_vers -productVersion` is too old. 10.12 is needed as minimum. + exit 1 + fi ;; Linux) - __DOTNET_PKG="dotnet-dev-$(get_current_linux_name)-x64" - __DOTNET_RUNTIME_PKG="dotnet-$(get_current_linux_name)-x64" OS=Linux + __PKG_RID=linux + + if [ -e /etc/os-release ]; then + source /etc/os-release + if [[ $ID == "alpine" ]]; then + # remove the last version digit + VERSION_ID=${VERSION_ID%.*} + __PKG_RID=alpine.$VERSION_ID + fi + + elif [ -e /etc/redhat-release ]; then + redhatRelease=$(&2 + cat "$__init_tools_log" 1>&2 +} + if [ ! -e $__INIT_TOOLS_DONE_MARKER ]; then + __PATCH_CLI_NUGET_FRAMEWORKS=0 + if [ -e $__TOOLRUNTIME_DIR ]; then rm -rf -- $__TOOLRUNTIME_DIR; fi echo "Running: $__scriptpath/init-tools.sh" > $__init_tools_log + if [ ! -e $__DOTNET_PATH ]; then mkdir -p "$__DOTNET_PATH" - - echo "Installing dotnet cli..." - __DOTNET_LOCATION="https://dotnetcli.blob.core.windows.net/dotnet/Sdk/${__DOTNET_TOOLS_VERSION}/${__DOTNET_PKG}.${__DOTNET_TOOLS_VERSION}.tar.gz" - # curl has HTTPS CA trust-issues less often than wget, so lets try that first. - echo "Installing '${__DOTNET_LOCATION}' to '$__DOTNET_PATH/dotnet.tar'" >> $__init_tools_log - if command -v curl > /dev/null; then - curl --retry 10 -sSL --create-dirs -o $__DOTNET_PATH/dotnet.tar ${__DOTNET_LOCATION} + + if [ -n "$DOTNET_TOOLSET_DIR" ] && [ -d "$DOTNET_TOOLSET_DIR/$__DOTNET_TOOLS_VERSION" ]; then + echo "Copying $DOTNET_TOOLSET_DIR/$__DOTNET_TOOLS_VERSION to $__DOTNET_PATH" >> $__init_tools_log + cp -r $DOTNET_TOOLSET_DIR/$__DOTNET_TOOLS_VERSION/* $__DOTNET_PATH + elif [ -n "$DOTNET_TOOL_DIR" ] && [ -d "$DOTNET_TOOL_DIR" ]; then + echo "Copying $DOTNET_TOOL_DIR to $__DOTNET_PATH" >> $__init_tools_log + cp -r $DOTNET_TOOL_DIR/* $__DOTNET_PATH else - wget -q -O $__DOTNET_PATH/dotnet.tar ${__DOTNET_LOCATION} + echo "Installing dotnet cli..." + __DOTNET_LOCATION="https://dotnetcli.azureedge.net/dotnet/Sdk/${__DOTNET_TOOLS_VERSION}/${__DOTNET_PKG}.tar.gz" + # curl has HTTPS CA trust-issues less often than wget, so lets try that first. + echo "Installing '${__DOTNET_LOCATION}' to '$__DOTNET_PATH/dotnet.tar'" >> $__init_tools_log + if command -v curl > /dev/null; then + curl --retry 10 -sSL --create-dirs -o $__DOTNET_PATH/dotnet.tar ${__DOTNET_LOCATION} + else + wget -q -O $__DOTNET_PATH/dotnet.tar ${__DOTNET_LOCATION} + fi + cd $__DOTNET_PATH + tar -xf $__DOTNET_PATH/dotnet.tar + if [ "$?" != "0" ]; then + echo "ERROR: Could not download dotnet cli." 1>&2 + display_error_message + exit 1 + fi + + cd $__scriptpath + + __PATCH_CLI_NUGET_FRAMEWORKS=1 fi - cd $__DOTNET_PATH - tar -xf $__DOTNET_PATH/dotnet.tar - - # Delete the bundled in runtime. - rm -rf $__DOTNET_PATH/shared - - # Install .NET Core 1.0.4 until we get CLI updated to a version that has it bundled in - echo "Installing .NETCore 1.0.4..." - __NETCORE10_LOCATION="https://dotnetcli.blob.core.windows.net/dotnet/preview/Binaries/1.0.4/${__DOTNET_RUNTIME_PKG}.1.0.4.tar.gz" - if command -v curl > /dev/null; then - curl --retry 10 -sSL --create-dirs -o $__DOTNET_PATH/netcore10.tar ${__NETCORE10_LOCATION} - else - wget -q -O $__DOTNET_PATH/netcore10.tar ${__NETCORE10_LOCATION} - fi - tar -xf $__DOTNET_PATH/netcore10.tar - - # Install .NET Core 1.1.0 until we get CLI updated to a version that has it bundled in - echo "Installing .NETCore 1.1.0..." - __NETCORE11_LOCATION="https://dotnetcli.blob.core.windows.net/dotnet/release/1.1.0/Binaries/1.1.0/${__DOTNET_RUNTIME_PKG}.1.1.0.tar.gz" - if command -v curl > /dev/null; then - curl --retry 10 -sSL --create-dirs -o $__DOTNET_PATH/netcore11.tar ${__NETCORE11_LOCATION} - else - wget -q -O $__DOTNET_PATH/netcore11.tar ${__NETCORE11_LOCATION} - fi - tar -xf $__DOTNET_PATH/netcore11.tar - - cd $__scriptpath fi - if [ ! -d "$__PROJECT_JSON_PATH" ]; then mkdir "$__PROJECT_JSON_PATH"; fi - echo $__PROJECT_JSON_CONTENTS > "$__PROJECT_JSON_FILE" - if [ ! -e $__BUILD_TOOLS_PATH ]; then - echo "Restoring BuildTools version $__BUILD_TOOLS_PACKAGE_VERSION..." - echo "Running: $__DOTNET_CMD restore \"$__PROJECT_JSON_FILE\" --no-cache --packages $__PACKAGES_DIR --source $__BUILDTOOLS_SOURCE" >> $__init_tools_log - $__DOTNET_CMD restore "$__PROJECT_JSON_FILE" --no-cache --packages $__PACKAGES_DIR --source $__BUILDTOOLS_SOURCE >> $__init_tools_log - if [ ! -e "$__BUILD_TOOLS_PATH/init-tools.sh" ]; then echo "ERROR: Could not restore build tools correctly. See '$__init_tools_log' for more details."1>&2; fi + if [ -n "$BUILD_TOOLS_TOOLSET_DIR" ] && [ -d "$BUILD_TOOLS_TOOLSET_DIR/$__BUILD_TOOLS_PACKAGE_VERSION" ]; then + echo "Copying $BUILD_TOOLS_TOOLSET_DIR/$__BUILD_TOOLS_PACKAGE_VERSION to $__TOOLRUNTIME_DIR" >> $__init_tools_log + cp -r $BUILD_TOOLS_TOOLSET_DIR/$__BUILD_TOOLS_PACKAGE_VERSION/* $__TOOLRUNTIME_DIR + elif [ -n "$BUILD_TOOLS_TOOL_DIR" ] && [ -d "$BUILD_TOOLS_TOOL_DIR" ]; then + echo "Copying $BUILD_TOOLS_TOOL_DIR to $__TOOLRUNTIME_DIR" >> $__init_tools_log + cp -r $BUILD_TOOLS_TOOL_DIR/* $__TOOLRUNTIME_DIR + else + if [ ! -e $__BUILD_TOOLS_PATH ]; then + echo "Restoring BuildTools version $__BUILD_TOOLS_PACKAGE_VERSION..." + echo "Running: $__DOTNET_CMD restore \"$__INIT_TOOLS_RESTORE_PROJECT\" --no-cache --packages $__PACKAGES_DIR --source $__BUILDTOOLS_SOURCE /p:BuildToolsPackageVersion=$__BUILD_TOOLS_PACKAGE_VERSION" >> $__init_tools_log + $__DOTNET_CMD restore "$__INIT_TOOLS_RESTORE_PROJECT" --no-cache --packages $__PACKAGES_DIR --source $__BUILDTOOLS_SOURCE /p:BuildToolsPackageVersion=$__BUILD_TOOLS_PACKAGE_VERSION >> $__init_tools_log + if [ ! -e "$__BUILD_TOOLS_PATH/init-tools.sh" ]; then + echo "ERROR: Could not restore build tools correctly." 1>&2 + display_error_message + fi + fi + + echo "Initializing BuildTools..." + echo "Running: $__BUILD_TOOLS_PATH/init-tools.sh $__scriptpath $__DOTNET_CMD $__TOOLRUNTIME_DIR" >> $__init_tools_log + + # Executables restored with .NET Core 2.0 do not have executable permission flags. https://github.com/NuGet/Home/issues/4424 + chmod +x $__BUILD_TOOLS_PATH/init-tools.sh + $__BUILD_TOOLS_PATH/init-tools.sh $__scriptpath $__DOTNET_CMD $__TOOLRUNTIME_DIR >> $__init_tools_log + if [ "$?" != "0" ]; then + echo "ERROR: An error occurred when trying to initialize the tools." 1>&2 + display_error_message + exit 1 + fi fi - echo "Initializing BuildTools..." - echo "Running: $__BUILD_TOOLS_PATH/init-tools.sh $__scriptpath $__DOTNET_CMD $__TOOLRUNTIME_DIR" >> $__init_tools_log - $__BUILD_TOOLS_PATH/init-tools.sh $__scriptpath $__DOTNET_CMD $__TOOLRUNTIME_DIR >> $__init_tools_log - if [ "$?" != "0" ]; then - echo "ERROR: An error occured when trying to initialize the tools. Please check '$__init_tools_log' for more details."1>&2 - exit 1 - fi + echo "Making all .sh files executable under Tools." + # Executables restored with .NET Core 2.0 do not have executable permission flags. https://github.com/NuGet/Home/issues/4424 + ls $__scriptpath/Tools/*.sh | xargs chmod +x + ls $__scriptpath/Tools/scripts/docker/*.sh | xargs chmod +x + Tools/crossgen.sh $__scriptpath/Tools + + # CoreRT does not use special copy of the shared runtime for testing + cp $__TOOLRUNTIME_DIR/csc.runtimeconfig.json $__TOOLRUNTIME_DIR/xunit.console.netcore.runtimeconfig.json + + mkdir -p $__INIT_TOOLS_DONE_MARKER_DIR touch $__INIT_TOOLS_DONE_MARKER + echo "Done initializing tools." else echo "Tools are already initialized" diff --git a/external/corert/netci.groovy b/external/corert/netci.groovy index 4f0bf87930..d98779822c 100644 --- a/external/corert/netci.groovy +++ b/external/corert/netci.groovy @@ -9,11 +9,11 @@ def project = GithubProject def branch = GithubBranchName def imageVersionMap = ['Windows_NT':'latest-or-auto', - 'OSX':'latest-or-auto', + 'OSX10.12':'latest-or-auto', 'Ubuntu':'20170118'] // Innerloop build OS's -def osList = ['Ubuntu', 'OSX', 'Windows_NT'] +def osList = ['Ubuntu', 'OSX10.12', 'Windows_NT'] // Generate the builds for debug and release, commit and PRJob [true, false].each { isPR -> // Defines a closure over true and false, value assigned to isPR diff --git a/external/corert/pkg/Microsoft.DotNet.ILCompiler/Microsoft.DotNet.ILCompiler.builds b/external/corert/pkg/Microsoft.DotNet.ILCompiler/Microsoft.DotNet.ILCompiler.builds new file mode 100644 index 0000000000..0a0cff6048 --- /dev/null +++ b/external/corert/pkg/Microsoft.DotNet.ILCompiler/Microsoft.DotNet.ILCompiler.builds @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/external/corert/pkg/Microsoft.DotNet.ILCompiler/Microsoft.DotNet.ILCompiler.pkgproj b/external/corert/pkg/Microsoft.DotNet.ILCompiler/Microsoft.DotNet.ILCompiler.pkgproj index 1e6c7aab48..b7b2f5cddc 100644 --- a/external/corert/pkg/Microsoft.DotNet.ILCompiler/Microsoft.DotNet.ILCompiler.pkgproj +++ b/external/corert/pkg/Microsoft.DotNet.ILCompiler/Microsoft.DotNet.ILCompiler.pkgproj @@ -1,38 +1,41 @@ - - + - - - - true false x64; true + true true - + true + + true + + + + + + + %(ProjectReference.AdditionalProperties);PackageTargetRuntime=win-x64 + + + %(ProjectReference.AdditionalProperties);PackageTargetRuntime=linux-x64 + + + %(ProjectReference.AdditionalProperties);PackageTargetRuntime=osx-x64 + + - - - - - - - - - 1.0.13-prerelease-00001 - - - 1.2.0-beta-24815-03 - - - 1.2.0 - - + + build + + + + tools + diff --git a/external/corert/pkg/Microsoft.DotNet.ILCompiler/TargetSpecific/Microsoft.DotNet.ILCompiler.pkgproj b/external/corert/pkg/Microsoft.DotNet.ILCompiler/TargetSpecific/Microsoft.DotNet.ILCompiler.pkgproj new file mode 100644 index 0000000000..df63fb391a --- /dev/null +++ b/external/corert/pkg/Microsoft.DotNet.ILCompiler/TargetSpecific/Microsoft.DotNet.ILCompiler.pkgproj @@ -0,0 +1,53 @@ + + + + + win-x64 + linux-x64 + osx-x64 + + + + + true + false + x64; + true + true + true + + + + + targets + + + tools + + + sdk + + + framework + + + + sdk + + + sdk + + + sdk + + + sdk + + + sdk + + + + + + diff --git a/external/corert/pkg/Microsoft.TargetingPack.Private.CoreRT/Microsoft.TargetingPack.Private.CoreRT.pkgproj b/external/corert/pkg/Microsoft.TargetingPack.Private.CoreRT/Microsoft.TargetingPack.Private.CoreRT.pkgproj index 25a75752ae..2598926b49 100644 --- a/external/corert/pkg/Microsoft.TargetingPack.Private.CoreRT/Microsoft.TargetingPack.Private.CoreRT.pkgproj +++ b/external/corert/pkg/Microsoft.TargetingPack.Private.CoreRT/Microsoft.TargetingPack.Private.CoreRT.pkgproj @@ -1,5 +1,4 @@ - - + true diff --git a/external/corert/pkg/packageIndex.json b/external/corert/pkg/packageIndex.json index 1b47ad4688..f1839ab664 100644 --- a/external/corert/pkg/packageIndex.json +++ b/external/corert/pkg/packageIndex.json @@ -4,6 +4,10 @@ "StableVersions": [], "BaselineVersion": "1.0.0", }, + "Microsoft.DotNet.ILCompiler" : { + "StableVersions": [], + "BaselineVersion": "1.0.0", + }, "Microsoft.TargetingPack.Private.CoreRT" : { "StableVersions": [], "BaselineVersion": "1.0.0", diff --git a/external/corert/pkg/packages.proj b/external/corert/pkg/packages.proj index e4c8b0a861..51a4d79a1c 100644 --- a/external/corert/pkg/packages.proj +++ b/external/corert/pkg/packages.proj @@ -1,19 +1,31 @@ - - + - + $(BinDir)pkg/reports/ false - - - AnyOS + + + false + true + + + + + + Windows_NT - + + + AnyOS + $(AdditionalProperties);BuildIdentityPackage=$(BuildIdentityPackage) + + + diff --git a/external/corert/samples/WebApi/Controllers/ValuesController.cs b/external/corert/samples/WebApi/Controllers/ValuesController.cs new file mode 100644 index 0000000000..75b9ee0f0e --- /dev/null +++ b/external/corert/samples/WebApi/Controllers/ValuesController.cs @@ -0,0 +1,32 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace SampleWebApi.Controllers +{ + public class ValuesController + { + [HttpGet("/")] + public string Hello() => "Hello World!"; + + // GET api/values + [HttpGet("/api/values")] + public IEnumerable Get() + { + return new string[] { "value1", "value2" }; + } + + // GET api/values/5 + [HttpGet("/api/values/{id}")] + public string Get(int id) + { + return "value is " + id; + } + } +} diff --git a/external/corert/samples/WebApi/Program.cs b/external/corert/samples/WebApi/Program.cs new file mode 100644 index 0000000000..1cc126ce4c --- /dev/null +++ b/external/corert/samples/WebApi/Program.cs @@ -0,0 +1,29 @@ +// 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.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace SampleWebApi +{ + public class Program + { + public static void Main(string[] args) + { + BuildWebHost(args).Run(); + } + + public static IWebHost BuildWebHost(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseStartup() + .Build(); + } +} diff --git a/external/corert/samples/WebApi/README.md b/external/corert/samples/WebApi/README.md new file mode 100644 index 0000000000..a47f25e124 --- /dev/null +++ b/external/corert/samples/WebApi/README.md @@ -0,0 +1,141 @@ +# Building a WebAPI app with CoreRT + +This document will guide you through compiling a .NET Core Web API application with CoreRT. + +## Install the .NET Core SDK +CoreRT is an AOT-optimized .NET Core runtime. If you're new to .NET Core make sure to visit the [official starting page](http://dotnet.github.io). It will guide you through installing pre-requisites and building your first app. +If you're already familiar with .NET Core make sure you've [downloaded and installed the .NET Core 2 SDK](https://www.microsoft.com/net/download/core). + +## Create your app + +Open a new shell/command prompt window and run the following commands. +```bash +> dotnet new webapi -o myApp +> cd myApp +``` + +## Add CoreRT to your project +Using CoreRT to compile your application is done via the ILCompiler NuGet package, which is [published to MyGet with the CoreRT daily builds](https://dotnet.myget.org/feed/dotnet-core/package/nuget/Microsoft.DotNet.ILCompiler). +For the compiler to work, it first needs to be added to your project. + +In your shell/command prompt navigate to the root directory of your project and run the command: + +```bash +> dotnet new nuget +``` + +This will add a nuget.config file to your application. Open the file and in the `` `` element under ```` add the following: + +```xml + + +``` + +Once you've added the package source, add a reference to the compiler by running the following command: + +```bash +> dotnet add package Microsoft.DotNet.ILCompiler -v 1.0.0-alpha-* +``` + +## Add Core MVC services +With the package successfully added to your project, your project's default registered MVC services must be modified. + +The default template's `AddMvc()` call registers a large set of middleware services by default, even if they are not needed by your application. This is not AOT-compilation friendly because it leads to large binaries and creates the risk of adding unsupported features. + +Open the file called `Startup.cs` and in the `ConfigureServices()` method and modify the line: + +```csharp +services.AddMvc(); +``` + +to + +```csharp +services.AddMvcCore().AddJsonFormatters(); +``` + +Replacing `AddMvc()` with `AddMvcCore()` adds only the basic MVC functionality. It's followed by explicit registration of services, which are required by the application - in this case the `JsonFormatter`. For more details see [Fabian Gosebrink's blog post comparing AddMvc and AddMvcCore](https://dzone.com/articles/the-difference-between-addmvc-and-addmvccore). + +## Using reflection +Runtime directives are XML configuration files, which specify which elements of your program are available for reflection. They are used at compile-time to enable AOT compilation in applications at runtime. + +In this sample a basic rd.xml file has been added for a simple Web API application under the root project folder. Copy its contents to your application directory and modify the element +```xml + + ``` + to use your app's name. + +If your application makes use of reflection, you will need to create a rd.xml file specifying explicitly which assemblies and types should be made available. For example, in your .NET Core Web API application, reflection is required to determine the correct namespace, from which to load the ``Startup`` type. Both are defined respectively via the `` and `` attributes. For example, in the case of our specific application: + +```xml + + + +``` + +At runtime, if a method or type is not found or cannot be loaded, an exception will be thrown. The exception message will contain information on the missing type reference, which you can then add to the rd.xml of your program. + +Once you've created a rd.xml file, navigate to the root directory of your project and open its `.csproj` file and in the first `` element add the following: + +```xml +path_to_rdxml_file\rd.xml +``` + +where path_to_rdxml_file is the location of the file on your disk. + +Under the second `` remove the line containing a reference to `Microsoft.AspNetCore.All` and substitute it with: + +```xml + + + +``` + +This substitution removes unnecessary package references added by AspNetCore.All, which will remove them from your application's published files and avoid encountering unsupported features, as described in [the section above](#add-core-mvc-services) + +After you've modified your project's `.csproj` file, open your application's controller file (in the default template this should be called `ValuesController.cs`) and substitute the ValuesController class with the following: + +```csharp +public class ValuesController +{ + [HttpGet("/")] + public string Hello() => "Hello World!"; + // GET api/values + [HttpGet("/api/values")] + public IEnumerable Get() + { + return new string[] { "value1", "value2" }; + } + // GET api/values/5 + [HttpGet("/api/values/{id}")] + public string Get(int id) + { + return "Your value is " + id; + } +} +``` + +(note the removed inheritance and [Route] directive). Also note that URL request paths are explicitly defined on each method. + + +## Restore and Publish your app + +Once the package has been successfully added it's time to compile and publish your app! If you're using Windows, make sure you're using `x64 Native Tools Command Prompt for VS 2017` instead of the standard Windows command prompt. In the shell/command prompt window, run the following command: + +```bash +> dotnet publish -r -c +``` + +where `` is your project configuration (such as Debug or Release) and `` is the runtime identifier, which you specified in the csproj file (one of win-x64, linux-x64, osx-x64). For example, if you want to publish a release configuration of your app for a 64-bit version of Windows the command would look like: + +```bash +> dotnet publish -r win-x64 -c release +``` + +Once completed, you can find the native executable in the root folder of your project under `/bin/x64//netcoreapp2.0/publish/` + +## Try it out! + +Navigate to `/bin/x64//netcoreapp2.0/publish/` in your project folder and run the produced executable. It should display "Now listening on: http://localhost:XXXX" with XXXX being a port on your machine. Open your browser and navigate to that URL. You should see "Hello World!" displayed in your browser. + +Feel free to modify the sample application and experiment. However, keep in mind some functionality might not yet be supported in CoreRT. Let us know on the [Issues page](https://github.com/dotnet/corert/issues/). diff --git a/external/corert/samples/WebApi/SampleWebApi.csproj b/external/corert/samples/WebApi/SampleWebApi.csproj new file mode 100644 index 0000000000..d485b393d8 --- /dev/null +++ b/external/corert/samples/WebApi/SampleWebApi.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp2.0 + rd.xml + + + + + + + + + + + + + + diff --git a/external/corert/samples/WebApi/Startup.cs b/external/corert/samples/WebApi/Startup.cs new file mode 100644 index 0000000000..c432519f5b --- /dev/null +++ b/external/corert/samples/WebApi/Startup.cs @@ -0,0 +1,44 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace SampleWebApi +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddMvcCore().AddJsonFormatters(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseMvc(); + } + } +} diff --git a/external/corert/samples/WebApi/appsettings.Development.json b/external/corert/samples/WebApi/appsettings.Development.json new file mode 100644 index 0000000000..fa8ce71a97 --- /dev/null +++ b/external/corert/samples/WebApi/appsettings.Development.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/external/corert/samples/WebApi/appsettings.json b/external/corert/samples/WebApi/appsettings.json new file mode 100644 index 0000000000..26bb0ac7ac --- /dev/null +++ b/external/corert/samples/WebApi/appsettings.json @@ -0,0 +1,15 @@ +{ + "Logging": { + "IncludeScopes": false, + "Debug": { + "LogLevel": { + "Default": "Warning" + } + }, + "Console": { + "LogLevel": { + "Default": "Warning" + } + } + } +} diff --git a/external/corert/samples/WebApi/nuget.config b/external/corert/samples/WebApi/nuget.config new file mode 100644 index 0000000000..d8d3f2b015 --- /dev/null +++ b/external/corert/samples/WebApi/nuget.config @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/external/corert/samples/WebApi/rd.xml b/external/corert/samples/WebApi/rd.xml new file mode 100644 index 0000000000..c491837b16 --- /dev/null +++ b/external/corert/samples/WebApi/rd.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/corert/src/AotPackageReference/AotPackageReference.depproj b/external/corert/src/AotPackageReference/AotPackageReference.depproj index 9d54644552..883ebef826 100644 --- a/external/corert/src/AotPackageReference/AotPackageReference.depproj +++ b/external/corert/src/AotPackageReference/AotPackageReference.depproj @@ -1,25 +1,36 @@ - - + - 4.0.0.0 - Library $(AotPackageReferencePath) - - - - - UAP,Version=v10.1 - win8-aot + uap10.1 + win10-x64-aot + true - \ No newline at end of file + + + + $(CoreFxUapVersion) + + + + + + + + + + + + + + diff --git a/external/corert/src/AotPackageReference/project.json b/external/corert/src/AotPackageReference/project.json deleted file mode 100644 index 6a2550f239..0000000000 --- a/external/corert/src/AotPackageReference/project.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "dependencies": { - "Microsoft.NETCore.Platforms": "1.2.0-beta-24906-01", - "System.Runtime": "4.4.0-beta-24906-01", - "System.Reflection.Primitives":"4.4.0-beta-24906-01", - "System.Runtime.Extensions": "4.4.0-beta-24906-01", - "System.Collections": "4.4.0-beta-24906-01", - "System.Resources.ResourceManager": "4.4.0-beta-24906-01", - "System.Console": "4.4.0-beta-24906-01", - - "System.Threading.Tasks": "4.4.0-beta-24906-01", - "System.Text.Encoding.Extensions": "4.4.0-beta-24906-01", - "System.Reflection.TypeExtensions": "4.4.0-beta-24906-01", - "System.Runtime.InteropServices": "4.4.0-beta-24906-01", - "System.Private.Uri": "4.4.0-beta-24906-01", - "System.IO": "4.4.0-beta-24906-01", - "System.Globalization": "4.4.0-beta-24906-01", - "System.ObjectModel": "4.4.0-beta-24906-01", - "System.Collections.Concurrent": "4.4.0-beta-24906-01", - "System.Reflection.Metadata": "1.4.2", - "System.Collections.Immutable": "1.3.1" - }, - "frameworks": { - "uap10.1": { } - }, - "runtimes" : { - "win8-aot": { } - } -} diff --git a/external/corert/src/BuildIntegration/BuildFrameworkNativeObjects.proj b/external/corert/src/BuildIntegration/BuildFrameworkNativeObjects.proj index 62ae1ca1ff..6affd6d3ef 100644 --- a/external/corert/src/BuildIntegration/BuildFrameworkNativeObjects.proj +++ b/external/corert/src/BuildIntegration/BuildFrameworkNativeObjects.proj @@ -1,22 +1,24 @@ - + - BuildOneFrameworkLibrary + ComputeIlcCompileInputs;BuildOneFrameworkLibrary BuildAllFrameworkLibrariesAsSingleLib true $(FrameworkObjPath)\ true - + + + + Inputs="@(DefaultFrameworkAssemblies)" + Outputs="@(DefaultFrameworkAssemblies->'$(NativeIntermediateOutputPath)\%(Filename)$(NativeObjectExt)')"> - LibraryToCompile=%(IlcReference.Identity) + LibraryToCompile=%(DefaultFrameworkAssemblies.Identity) diff --git a/external/corert/src/BuildIntegration/BuildIntegration.proj b/external/corert/src/BuildIntegration/BuildIntegration.proj new file mode 100644 index 0000000000..5340405aa8 --- /dev/null +++ b/external/corert/src/BuildIntegration/BuildIntegration.proj @@ -0,0 +1,18 @@ + + + + $(BaseOutputPath)$(OSPlatformConfig)/build + + + + + + + + + + + + + + diff --git a/external/corert/src/BuildIntegration/Microsoft.DotNet.ILCompiler.targets b/external/corert/src/BuildIntegration/Microsoft.DotNet.ILCompiler.targets new file mode 100644 index 0000000000..0009363a3e --- /dev/null +++ b/external/corert/src/BuildIntegration/Microsoft.DotNet.ILCompiler.targets @@ -0,0 +1,32 @@ + + + false + + + + runtime.$(RuntimeIdentifier).Microsoft.DotNet.ILCompiler + true + + + RunResolvePackageDependencies + ImportRuntimeIlcPackageTarget + SetupProperties + + + + + + + + + + + + + + + + + + diff --git a/external/corert/src/BuildIntegration/Microsoft.NETCore.Native.Publish.targets b/external/corert/src/BuildIntegration/Microsoft.NETCore.Native.Publish.targets new file mode 100644 index 0000000000..12743b911b --- /dev/null +++ b/external/corert/src/BuildIntegration/Microsoft.NETCore.Native.Publish.targets @@ -0,0 +1,66 @@ + + + + $(MSBuildThisFileDirectory)..\tools\ILCompiler.Build.Tasks.dll + + + false + + + + + + + + + + + + + + + + + + <_NativeIntermediateAssembly Include="@(IntermediateAssembly->'$(NativeOutputPath)%(Filename)$(NativeBinaryExt)')" /> + + + + + + + + + + + + + + + + + + + + + diff --git a/external/corert/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props b/external/corert/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props index fa4d8859b1..69539c5e13 100644 --- a/external/corert/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props +++ b/external/corert/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props @@ -1,4 +1,3 @@ - - + clang clang-3.9 @@ -22,40 +21,70 @@ See the LICENSE file in the project root for more information. $(CppCompilerAndLinker) ar + + + + + clang + clang-3.9 + $(CppCompilerAndLinker) + $(CppCompilerAndLinker) + ar + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + + + + - - - - - + + + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/corert/src/BuildIntegration/Microsoft.NETCore.Native.Windows.props b/external/corert/src/BuildIntegration/Microsoft.NETCore.Native.Windows.props index 633031bba8..0182d55187 100644 --- a/external/corert/src/BuildIntegration/Microsoft.NETCore.Native.Windows.props +++ b/external/corert/src/BuildIntegration/Microsoft.NETCore.Native.Windows.props @@ -1,4 +1,3 @@ - - - + cl link lib + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/corert/src/BuildIntegration/Microsoft.NETCore.Native.targets b/external/corert/src/BuildIntegration/Microsoft.NETCore.Native.targets index 41a2ea231a..6e03efac7a 100644 --- a/external/corert/src/BuildIntegration/Microsoft.NETCore.Native.targets +++ b/external/corert/src/BuildIntegration/Microsoft.NETCore.Native.targets @@ -1,4 +1,3 @@ - - + $(IntermediateOutputPath)native\ $(OutputPath)native\ + true OSX + + $(OS) .obj .o + .bc + .lib .a $(NativeObjectExt) .cpp + .bc .exe - .dll - .dylib - .so + .dll + .dylib + .so + .lib + .a + .html + + .def + .exports $(NativeIntermediateOutputPath)$(TargetName)$(NativeObjectExt) $(NativeOutputPath)$(TargetName)$(NativeBinaryExt) + $(NativeIntermediateOutputPath)$(TargetName)$(ExportsFileExt) $(NativeObject) $(NativeIntermediateOutputPath)$(TargetName).cpp + IlcCompile CppCompile + IlcCompile $(NativeOutputPath) $(NativeIntermediateOutputPath) $(FrameworkLibPath)\Framework$(LibFileExt) $(FrameworkLibPath)\libframework$(LibFileExt) + SetupProperties - - + + Compile;ComputeIlcCompileInputs + $(IlcCompileDependsOn);BuildFrameworkLib + $(IlcCompileDependsOn);SetupOSSpecificProps + + + + + + + - - - + + - - Compile - $(IlcCompileDependsOn);BuildFrameworkLib - + + + + + + $(RuntimePackagePath) + $(RuntimePackagePath)\tools\ILCompiler.Build.Tasks.dll + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + + IntermediateOutputPath=$(IntermediateOutputPath); FrameworkLibPath=$(FrameworkLibPath); - FrameworkObjPath=$(FrameworkObjPath) + FrameworkObjPath=$(FrameworkObjPath); + RuntimePackagePath=$(RuntimePackagePath) @@ -102,6 +154,11 @@ See the LICENSE file in the project root for more information. + + + + + @@ -109,18 +166,13 @@ See the LICENSE file in the project root for more information. - - CoreRun.exe - corerun - - - - + + - - + + - + + + + + + + + + - - - + + + + + + + + + + + "$(NativeObject)" -o "$(NativeBinary)" -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 + $(EmccArgs) "$(IlcPath)\sdk\libPortableRuntime.bc" "$(IlcPath)\sdk\libbootstrappercpp.bc" + $(EmccArgs) -O2 --llvm-lto 2 + $(EmccArgs) -g3 + + + + + DependsOnTargets="$(CreateLibDependsOn);$(IlcDynamicBuildPropertyDependencies)" + > - + + + + + diff --git a/external/corert/src/Common/src/Internal/Metadata/NativeFormat/Generator/NativeFormatGen.csproj b/external/corert/src/Common/src/Internal/Metadata/NativeFormat/Generator/NativeFormatGen.csproj index 23b7508abb..f9927e4661 100644 --- a/external/corert/src/Common/src/Internal/Metadata/NativeFormat/Generator/NativeFormatGen.csproj +++ b/external/corert/src/Common/src/Internal/Metadata/NativeFormat/Generator/NativeFormatGen.csproj @@ -1,10 +1,6 @@ - - + - Debug - AnyCPU - {EE99759F-C776-4F0E-8B06-E65E833B2059} Exe Properties NativeFormatGen diff --git a/external/corert/src/Common/src/Internal/Metadata/NativeFormat/Generator/ReaderGen.cs b/external/corert/src/Common/src/Internal/Metadata/NativeFormat/Generator/ReaderGen.cs index 41d489d73d..d85b0d93b6 100644 --- a/external/corert/src/Common/src/Internal/Metadata/NativeFormat/Generator/ReaderGen.cs +++ b/external/corert/src/Common/src/Internal/Metadata/NativeFormat/Generator/ReaderGen.cs @@ -174,7 +174,7 @@ class ReaderGen : CsWriter private void EmitCollection(string collectionTypeName, string elementTypeName) { - OpenScope($"public partial struct {collectionTypeName} : IReadOnlyCollection<{elementTypeName}>"); + OpenScope($"public partial struct {collectionTypeName}"); WriteLine("private NativeReader _reader;"); WriteLine("private uint _offset;"); @@ -196,15 +196,7 @@ class ReaderGen : CsWriter WriteLine($"return new Enumerator(_reader, _offset);"); CloseScope("GetEnumerator"); - OpenScope($"IEnumerator<{elementTypeName}> IEnumerable<{elementTypeName}>.GetEnumerator()"); - WriteLine($"return new Enumerator(_reader, _offset);"); - CloseScope("GetEnumerator"); - - OpenScope($"System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()"); - WriteLine("return new Enumerator(_reader, _offset);"); - CloseScope("GetEnumerator"); - - OpenScope($"public struct Enumerator : IEnumerator<{elementTypeName}>"); + OpenScope($"public struct Enumerator"); WriteLine("private NativeReader _reader;"); WriteLine("private uint _offset;"); @@ -223,12 +215,6 @@ class ReaderGen : CsWriter CloseScope(); CloseScope("Current"); - OpenScope("object System.Collections.IEnumerator.Current"); - OpenScope("get"); - WriteLine("return _current;"); - CloseScope(); - CloseScope("Current"); - OpenScope("public bool MoveNext()"); WriteLine("if (_remaining == 0)"); WriteLine(" return false;"); @@ -237,10 +223,6 @@ class ReaderGen : CsWriter WriteLine("return true;"); CloseScope("MoveNext"); - OpenScope("void System.Collections.IEnumerator.Reset()"); - WriteLine("throw new NotSupportedException();"); - CloseScope("Reset"); - OpenScope("public void Dispose()"); CloseScope("Dispose"); diff --git a/external/corert/src/Common/src/Internal/Metadata/NativeFormat/Generator/SchemaDef.cs b/external/corert/src/Common/src/Internal/Metadata/NativeFormat/Generator/SchemaDef.cs index 54d049aa60..d5da54db8e 100644 --- a/external/corert/src/Common/src/Internal/Metadata/NativeFormat/Generator/SchemaDef.cs +++ b/external/corert/src/Common/src/Internal/Metadata/NativeFormat/Generator/SchemaDef.cs @@ -26,25 +26,23 @@ public enum RecordDefFlags [Flags] public enum MemberDefFlags { - Map = 0x0001, // => Dictionary for MetadataWriter - // => List for MetadataReader - List = 0x0002, // => List - Array = 0x0004, // => RecordType[] + Map = 0x0001, // => List for writer + List = 0x0002, // => List for writer + Array = 0x0004, // => RecordType[] for writer Collection = MemberDefFlags.Map | MemberDefFlags.List | MemberDefFlags.Array, Sequence = MemberDefFlags.List | MemberDefFlags.Array, - RecordRef = 0x0008, // => RecordTypeHandle - - Ref = MemberDefFlags.RecordRef, + RecordRef = 0x0008, Child = 0x0010, // Member instance is logically defined and owned by record; // otherwise instance may be shared (such as a TypeRef). - Name = 0x0020, // May be used as the member"s simple name for diagnostics. + Name = 0x0020, // May be used as the member's simple name for diagnostics. NotPersisted = 0x0040, // Indicates member is not written to or read from metadata. Compare = 0x0080, // Indicates member should be used for equality functionality. EnumerateForHashCode = 0x0100, // Indicates that the collection is safe to be enumerated in GetHashCode // without causing reentrancy + CustomCompare = 0x0200, // Indicates that this member uses a custom comparer } public enum MemberTypeKind @@ -86,14 +84,14 @@ public class MemberDef else { typeName = (kind == MemberTypeKind.WriterField) ? - (TypeName != null ? (string)TypeName : "MetadataRecord"): $"{ TypeName}Handle"; + (TypeName != null ? (string)TypeName : "MetadataRecord"): $"{TypeName}Handle"; } } else { typeName = (string)TypeName; } - if ((Flags & (MemberDefFlags.Array | MemberDefFlags.List | MemberDefFlags.Map)) != 0) + if ((Flags & MemberDefFlags.Collection) != 0) { if (kind == MemberTypeKind.WriterField) { @@ -119,10 +117,7 @@ public class MemberDef if (typeSet == null) return null; - string result = "One of: " + typeSet[0]; - for (int i = 1; i < typeSet.Length; i++) - result += ", " + typeSet[i]; - return result; + return "One of: " + String.Join(", ", typeSet); } } @@ -161,14 +156,16 @@ public class EnumType public class PrimitiveType { - public PrimitiveType(string name, string typeName) + public PrimitiveType(string name, string typeName, bool customCompare = false) { Name = name; TypeName = typeName; + CustomCompare = customCompare; } readonly public string Name; readonly public string TypeName; + readonly public bool CustomCompare; } /// @@ -208,8 +205,8 @@ class SchemaDef new PrimitiveType("uint", "UInt32"), new PrimitiveType("long", "Int64"), new PrimitiveType("ulong", "UInt64"), - new PrimitiveType("float", "Single"), - new PrimitiveType("double", "Double"), + new PrimitiveType("float", "Single", customCompare: true), + new PrimitiveType("double", "Double", customCompare: true), }; // These enums supplement those defined by System.Reflection.Primitives. @@ -292,7 +289,8 @@ class SchemaDef new RecordDef( name: "Constant" + primitiveType.TypeName + "Value", members: new MemberDef[] { - new MemberDef(name: "Value", typeName: primitiveType.Name) + new MemberDef(name: "Value", typeName: primitiveType.Name, + flags: primitiveType.CustomCompare ? MemberDefFlags.CustomCompare : 0) } ) ) @@ -326,7 +324,8 @@ class SchemaDef new RecordDef( name: "Constant" + primitiveType.TypeName + "Array", members: new MemberDef[] { - new MemberDef(name: "Value", flags: MemberDefFlags.Array, typeName: primitiveType.TypeName) + new MemberDef(name: "Value", typeName: primitiveType.TypeName, + flags: MemberDefFlags.Array | (primitiveType.CustomCompare ? MemberDefFlags.CustomCompare : 0)) } ) ) @@ -782,9 +781,7 @@ class SchemaDef from member in r.Members let memberTypeName = member.TypeName as string where memberTypeName != null && - ((member.Flags & MemberDefFlags.Array) != 0 || - (member.Flags & MemberDefFlags.List) != 0 || - (member.Flags & MemberDefFlags.Map) != 0) && + (member.Flags & MemberDefFlags.Collection) != 0 && !PrimitiveTypes.Any(pt => pt.TypeName == memberTypeName) select memberTypeName ).Concat(new[] { "ScopeDefinition" }).Distinct().ToArray(); diff --git a/external/corert/src/Common/src/Internal/Metadata/NativeFormat/Generator/WriterGen.cs b/external/corert/src/Common/src/Internal/Metadata/NativeFormat/Generator/WriterGen.cs index bb9e9da82b..fe1daca06f 100644 --- a/external/corert/src/Common/src/Internal/Metadata/NativeFormat/Generator/WriterGen.cs +++ b/external/corert/src/Common/src/Internal/Metadata/NativeFormat/Generator/WriterGen.cs @@ -61,14 +61,7 @@ class WriterGen : CsWriter if ((member.Flags & MemberDefFlags.RecordRef) == 0) continue; - if ((member.Flags & (MemberDefFlags.Map | MemberDefFlags.Sequence)) != 0) - { - WriteLine($"{member.Name} = visitor.Visit(this, {member.Name});"); - } - else - { - WriteLine($"{member.Name} = visitor.Visit(this, {member.Name});"); - } + WriteLine($"{member.Name} = visitor.Visit(this, {member.Name});"); } CloseScope("Visit"); @@ -94,17 +87,20 @@ class WriterGen : CsWriter if ((member.Flags & MemberDefFlags.Sequence) != 0) { - WriteLine($"if (!{member.Name}.SequenceEqual(other.{member.Name})) return false;"); + if ((member.Flags & MemberDefFlags.CustomCompare) != 0) + WriteLine($"if (!{member.Name}.SequenceEqual(other.{member.Name}, {member.TypeName}Comparer.Instance)) return false;"); + else + WriteLine($"if (!{member.Name}.SequenceEqual(other.{member.Name})) return false;"); } else - if ((member.Flags & (MemberDefFlags.List | MemberDefFlags.Map)) != 0) + if ((member.Flags & (MemberDefFlags.Map | MemberDefFlags.RecordRef)) != 0) { WriteLine($"if (!Object.Equals({member.Name}, other.{member.Name})) return false;"); } else - if ((member.Flags & MemberDefFlags.RecordRef) != 0) + if ((member.Flags & MemberDefFlags.CustomCompare) != 0) { - WriteLine($"if (!Object.Equals({member.Name}, other.{member.Name})) return false;"); + WriteLine($"if (!CustomComparer.Equals({member.Name}, other.{member.Name})) return false;"); } else { @@ -133,7 +129,7 @@ class WriterGen : CsWriter // Compute hash seed using stable hashcode byte[] nameBytes = System.Text.Encoding.UTF8.GetBytes(record.Name); - byte[] hashBytes = new System.Security.Cryptography.SHA1Managed().ComputeHash(nameBytes); + byte[] hashBytes = System.Security.Cryptography.SHA256.Create().ComputeHash(nameBytes); int hashSeed = System.BitConverter.ToInt32(hashBytes, 0); WriteLine($"int hash = {hashSeed};"); diff --git a/external/corert/src/Common/src/Internal/Metadata/NativeFormat/NativeFormatReaderGen.cs.REMOVED.git-id b/external/corert/src/Common/src/Internal/Metadata/NativeFormat/NativeFormatReaderGen.cs.REMOVED.git-id index d9e033b75b..3ed09f7a3e 100644 --- a/external/corert/src/Common/src/Internal/Metadata/NativeFormat/NativeFormatReaderGen.cs.REMOVED.git-id +++ b/external/corert/src/Common/src/Internal/Metadata/NativeFormat/NativeFormatReaderGen.cs.REMOVED.git-id @@ -1 +1 @@ -edbe004dc24c8e36fd4c4e3d8a77a35239b98821 \ No newline at end of file +f068f31f9511fd44c80c628a2a5951841d2da89c \ No newline at end of file diff --git a/external/corert/src/Common/src/Internal/Metadata/NativeFormat/NativeMetadataReader.cs b/external/corert/src/Common/src/Internal/Metadata/NativeFormat/NativeMetadataReader.cs index fdbda9ceec..d5334ddf08 100644 --- a/external/corert/src/Common/src/Internal/Metadata/NativeFormat/NativeMetadataReader.cs +++ b/external/corert/src/Common/src/Internal/Metadata/NativeFormat/NativeMetadataReader.cs @@ -144,10 +144,12 @@ namespace Internal.Metadata.NativeFormat internal int _value; +#if DEBUG public override string ToString() { return String.Format("{1} : {0,8:X8}", _value, Enum.GetName(typeof(HandleType), this.HandleType)); } +#endif } public static class NativeFormatReaderExtensions @@ -196,7 +198,7 @@ namespace Internal.Metadata.NativeFormat /// Used as the root entrypoint for metadata, this is where all top-down /// structural walks of metadata must start. /// - public IEnumerable ScopeDefinitions + public ScopeDefinitionHandleCollection ScopeDefinitions { get { diff --git a/external/corert/src/Common/src/Internal/NativeFormat/NativeFormatReader.cs b/external/corert/src/Common/src/Internal/NativeFormat/NativeFormatReader.cs index f76d6e06ce..beb49d9321 100644 --- a/external/corert/src/Common/src/Internal/NativeFormat/NativeFormatReader.cs +++ b/external/corert/src/Common/src/Internal/NativeFormat/NativeFormatReader.cs @@ -390,7 +390,7 @@ namespace Internal.NativeFormat } set { - Debug.Assert(value >= 0 && value < _reader.Size); + Debug.Assert(value < _reader.Size); _offset = value; } } @@ -414,6 +414,13 @@ namespace Internal.NativeFormat return value; } + public ulong GetUnsignedLong() + { + ulong value; + _offset = _reader.DecodeUnsignedLong(_offset, out value); + return value; + } + public int GetSigned() { int value; diff --git a/external/corert/src/Common/src/Internal/Runtime/CanonTypeKind.cs b/external/corert/src/Common/src/Internal/Runtime/CanonTypeKind.cs new file mode 100644 index 0000000000..6efe7341ac --- /dev/null +++ b/external/corert/src/Common/src/Internal/Runtime/CanonTypeKind.cs @@ -0,0 +1,12 @@ +// 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. + +namespace Internal.Runtime +{ + public enum CanonTypeKind + { + NormalCanon, + UniversalCanon, + } +} diff --git a/external/corert/src/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs b/external/corert/src/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs index af73c636a9..e5fba47ad5 100644 --- a/external/corert/src/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs +++ b/external/corert/src/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs @@ -13,15 +13,20 @@ namespace Internal.Runtime.CompilerHelpers [McgIntrinsics] public static partial class StartupCodeHelpers { - public static IntPtr[] OSModules - { - get; private set; - } + /// + /// Initial module array allocation used when adding modules dynamically. + /// + private const int InitialModuleCount = 8; - public static TypeManagerHandle[] Modules - { - get; private set; - } + /// + /// Table of logical modules. Only the first s_moduleCount elements of the array are in use. + /// + private static TypeManagerHandle[] s_modules; + + /// + /// Number of valid elements in the logical module table. + /// + private static int s_moduleCount; [NativeCallable(EntryPoint = "InitializeModules", CallingConvention = CallingConvention.Cdecl)] internal static unsafe void InitializeModules(IntPtr osModule, IntPtr* pModuleHeaders, int count, IntPtr* pClasslibFunctions, int nClasslibFunctions) @@ -36,8 +41,18 @@ namespace Internal.Runtime.CompilerHelpers // We are now at a stage where we can use GC statics - publish the list of modules // so that the eager constructors can access it. - Modules = modules; - OSModules = new IntPtr[] { osModule }; + if (s_modules != null) + { + for (int i = 0; i < modules.Length; i++) + { + AddModule(modules[i]); + } + } + else + { + s_modules = modules; + s_moduleCount = modules.Length; + } // These two loops look funny but it's important to initialize the global tables before running // the first class constructor to prevent them calling into another uninitialized module @@ -47,6 +62,46 @@ namespace Internal.Runtime.CompilerHelpers } } + /// + /// Return the number of registered logical modules; optionally copy them into an array. + /// + /// Array to copy logical modules to, null = only return logical module count + internal static int GetLoadedModules(TypeManagerHandle[] outputModules) + { + if (outputModules != null) + { + int copyLimit = (s_moduleCount < outputModules.Length ? s_moduleCount : outputModules.Length); + for (int copyIndex = 0; copyIndex < copyLimit; copyIndex++) + { + outputModules[copyIndex] = s_modules[copyIndex]; + } + } + return s_moduleCount; + } + + private static void AddModule(TypeManagerHandle newModuleHandle) + { + if (s_modules == null || s_moduleCount >= s_modules.Length) + { + // Reallocate logical module array + int newModuleLength = 2 * s_moduleCount; + if (newModuleLength < InitialModuleCount) + { + newModuleLength = InitialModuleCount; + } + + TypeManagerHandle[] newModules = new TypeManagerHandle[newModuleLength]; + for (int copyIndex = 0; copyIndex < s_moduleCount; copyIndex++) + { + newModules[copyIndex] = s_modules[copyIndex]; + } + s_modules = newModules; + } + + s_modules[s_moduleCount] = newModuleHandle; + s_moduleCount++; + } + private static unsafe TypeManagerHandle[] CreateTypeManagers(IntPtr osModule, IntPtr* pModuleHeaders, int count, IntPtr* pClasslibFunctions, int nClasslibFunctions) { // Count the number of modules so we can allocate an array to hold the TypeManager objects. @@ -86,7 +141,15 @@ namespace Internal.Runtime.CompilerHelpers section->TypeManager = typeManager; section->ModuleIndex = moduleIndex; -#if CORERT + // Initialize Mrt import address tables + IntPtr mrtImportSection = RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.ImportAddressTables, out length); + if (mrtImportSection != IntPtr.Zero) + { + Debug.Assert(length % IntPtr.Size == 0); + InitializeImports(mrtImportSection, length); + } + +#if !PROJECTN // Initialize statics if any are present IntPtr staticsSection = RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.GCStaticRegion, out length); if (staticsSection != IntPtr.Zero) @@ -141,7 +204,66 @@ namespace Internal.Runtime.CompilerHelpers } } -#if CORERT + [StructLayout(LayoutKind.Sequential)] + unsafe struct MrtExportsV1 + { + public int ExportsVersion; // Currently only version 1 is supported + public int SymbolsCount; + public int FirstDataItemAsRelativePointer; // Index 1 + } + + [StructLayout(LayoutKind.Sequential)] + unsafe struct MrtImportsV1 + { + public int ImportVersion; // Currently only version 1 is supported + public int ImportCount; // Count of imports + public MrtExportsV1** ExportTable; // Pointer to pointer to Export table + public IntPtr FirstImportEntry; + } + + private static unsafe void InitializeImports(IntPtr importsRegionStart, int length) + { + IntPtr importsRegionEnd = (IntPtr)((byte*)importsRegionStart + length); + + for (MrtImportsV1** importTablePtr = (MrtImportsV1**)importsRegionStart; importTablePtr < (MrtImportsV1**)importsRegionEnd; importTablePtr++) + { + MrtImportsV1* importTable = *importTablePtr; + if (importTable->ImportVersion != 1) + RuntimeExceptionHelpers.FailFast("Mrt Import table version"); + + MrtExportsV1* exportTable = *importTable->ExportTable; + if (exportTable->ExportsVersion != 1) + RuntimeExceptionHelpers.FailFast("Mrt Export table version"); + + if (importTable->ImportCount < 0) + { + RuntimeExceptionHelpers.FailFast("Mrt Import Count"); + } + + int* firstExport = &exportTable->FirstDataItemAsRelativePointer; + IntPtr* firstImport = &importTable->FirstImportEntry; + for (int import = 0; import < importTable->ImportCount; import++) + { + // Get 1 based ordinal from import table + int importOrdinal = (int)firstImport[import]; + + if ((importOrdinal < 1) || (importOrdinal > exportTable->SymbolsCount)) + RuntimeExceptionHelpers.FailFast("Mrt import ordinal"); + + // Get entry in export table + int* exportTableEntry = &firstExport[importOrdinal - 1]; + + // Get pointer from export table + int relativeOffsetFromExportTableEntry = *exportTableEntry; + byte* actualPointer = ((byte*)exportTableEntry) + relativeOffsetFromExportTableEntry + sizeof(int); + + // Update import table with imported value + firstImport[import] = new IntPtr(actualPointer); + } + } + } + +#if !PROJECTN private static unsafe void InitializeStatics(IntPtr gcStaticRegionStart, int length) { IntPtr gcStaticRegionEnd = (IntPtr)((byte*)gcStaticRegionStart + length); @@ -172,7 +294,7 @@ namespace Internal.Runtime.CompilerHelpers } } } -#endif // CORERT +#endif // !PROJECTN } [StructLayout(LayoutKind.Sequential)] diff --git a/external/corert/src/Common/src/Internal/Runtime/EEType.Constants.cs b/external/corert/src/Common/src/Internal/Runtime/EEType.Constants.cs index 63839949be..fb314d47ca 100644 --- a/external/corert/src/Common/src/Internal/Runtime/EEType.Constants.cs +++ b/external/corert/src/Common/src/Internal/Runtime/EEType.Constants.cs @@ -43,9 +43,9 @@ namespace Internal.Runtime HasPointersFlag = 0x0020, /// - /// This type instance was allocated at runtime (rather than being embedded in a module image). + /// Type implements ICastable to allow dynamic resolution of interface casts. /// - RuntimeAllocatedFlag = 0x0040, + ICastableFlag = 0x0040, /// /// This type is generic and one or more of its type parameters is co- or contra-variant. This @@ -118,7 +118,7 @@ namespace Internal.Runtime /// /// Type implements ICastable to allow dynamic resolution of interface casts. /// - ICastableFlag = 0x00000002, + UNUSED1 = 0x00000002, /// /// Type is an instantiation of Nullable. @@ -141,11 +141,9 @@ namespace Internal.Runtime HasCctorFlag = 0x0000020, /// - /// This EEType has sealed vtable entries (note that this flag is only used for - /// dynamically created types because they always have an optional field (hence the - /// very explicit flag name). + /// Old unused flag /// - IsDynamicTypeWithSealedVTableEntriesFlag = 0x00000040, + UNUSED2 = 0x00000040, /// /// This EEType was constructed from a universal canonical template, and has @@ -160,8 +158,6 @@ namespace Internal.Runtime /// /// This EEType has sealed vtable entries - /// This is for statically generated types - we need two different flags because - /// the sealed vtable entries are reached in different ways in the static and dynamic case /// HasSealedVTableEntriesFlag = 0x00000200, diff --git a/external/corert/src/Common/src/Internal/Runtime/EEType.cs b/external/corert/src/Common/src/Internal/Runtime/EEType.cs index 8fa5cd5020..c9852dd80a 100644 --- a/external/corert/src/Common/src/Internal/Runtime/EEType.cs +++ b/external/corert/src/Common/src/Internal/Runtime/EEType.cs @@ -595,7 +595,7 @@ namespace Internal.Runtime { get { - return (RareFlags & EETypeRareFlags.ICastableFlag) != 0; + return ((_usFlags & (UInt16)EETypeFlags.ICastableFlag) != 0); } } @@ -609,16 +609,17 @@ namespace Internal.Runtime Debug.Assert(IsICastable); byte* optionalFields = OptionalFieldsPtr; - Debug.Assert(optionalFields != null); - - const UInt16 NoSlot = 0xFFFF; - UInt16 uiSlot = (UInt16)OptionalFieldsReader.GetInlineField(optionalFields, EETypeOptionalFieldTag.ICastableIsInstSlot, NoSlot); - if (uiSlot != NoSlot) + if(optionalFields != null) { - if (uiSlot < NumVtableSlots) - return GetVTableStartAddress()[uiSlot]; - else - return GetSealedVirtualSlot((UInt16)(uiSlot - NumVtableSlots)); + const UInt16 NoSlot = 0xFFFF; + UInt16 uiSlot = (UInt16)OptionalFieldsReader.GetInlineField(optionalFields, EETypeOptionalFieldTag.ICastableIsInstSlot, NoSlot); + if (uiSlot != NoSlot) + { + if (uiSlot < NumVtableSlots) + return GetVTableStartAddress()[uiSlot]; + else + return GetSealedVirtualSlot((UInt16)(uiSlot - NumVtableSlots)); + } } EEType* baseType = BaseType; @@ -640,16 +641,17 @@ namespace Internal.Runtime Debug.Assert(IsICastable); byte* optionalFields = OptionalFieldsPtr; - Debug.Assert(optionalFields != null); - - const UInt16 NoSlot = 0xFFFF; - UInt16 uiSlot = (UInt16)OptionalFieldsReader.GetInlineField(optionalFields, EETypeOptionalFieldTag.ICastableGetImplTypeSlot, NoSlot); - if (uiSlot != NoSlot) + if(optionalFields != null) { - if (uiSlot < NumVtableSlots) - return GetVTableStartAddress()[uiSlot]; - else - return GetSealedVirtualSlot((UInt16)(uiSlot - NumVtableSlots)); + const UInt16 NoSlot = 0xFFFF; + UInt16 uiSlot = (UInt16)OptionalFieldsReader.GetInlineField(optionalFields, EETypeOptionalFieldTag.ICastableGetImplTypeSlot, NoSlot); + if (uiSlot != NoSlot) + { + if (uiSlot < NumVtableSlots) + return GetVTableStartAddress()[uiSlot]; + else + return GetSealedVirtualSlot((UInt16)(uiSlot - NumVtableSlots)); + } } EEType* baseType = BaseType; @@ -746,16 +748,6 @@ namespace Internal.Runtime } } - // Mark or determine that a type instance was allocated at runtime (currently only used for unification of - // generic instantiations). This is sometimes important for memory management or debugging purposes. - internal bool IsRuntimeAllocated - { - get - { - return ((_usFlags & (UInt16)EETypeFlags.RuntimeAllocatedFlag) != 0); - } - } - internal EEInterfaceInfo* InterfaceMap { get @@ -1005,6 +997,7 @@ namespace Internal.Runtime internal IntPtr GetSealedVirtualSlot(UInt16 slotNumber) { Debug.Assert(!IsNullable); + Debug.Assert((RareFlags & EETypeRareFlags.HasSealedVTableEntriesFlag) != 0); fixed (EEType* pThis = &this) { @@ -1045,10 +1038,6 @@ namespace Internal.Runtime if (!HasOptionalFields) return null; - // Runtime allocated EETypes don't copy over optional fields. We should be careful to avoid operations - // that require them on paths that can handle such cases. - Debug.Assert(!IsRuntimeAllocated); - UInt32 cbOptionalFieldsOffset = GetFieldOffset(EETypeField.ETF_OptionalFieldsPtr); fixed (EEType* pThis = &this) { @@ -1195,7 +1184,7 @@ namespace Internal.Runtime } } #endif -#endif // CORERT +#endif // EETYPE_TYPE_MANAGER internal unsafe EETypeRareFlags RareFlags { @@ -1230,33 +1219,6 @@ namespace Internal.Runtime } } - internal static UInt32 ComputeValueTypeFieldPaddingFieldValue(UInt32 padding, UInt32 alignment) - { - // For the default case, return 0 - if ((padding == 0) && (alignment == IntPtr.Size)) - return 0; - - UInt32 alignmentLog2 = 0; - Debug.Assert(alignment != 0); - - while ((alignment & 1) == 0) - { - alignmentLog2++; - alignment = alignment >> 1; - } - Debug.Assert(alignment == 1); - - Debug.Assert(ValueTypePaddingMax >= padding); - - alignmentLog2++; // Our alignment values here are adjusted by one to allow for a default of 0 - - UInt32 paddingLowBits = padding & ValueTypePaddingLowMask; - UInt32 paddingHighBits = ((padding & ~ValueTypePaddingLowMask) >> ValueTypePaddingAlignmentShift) << ValueTypePaddingHighShift; - UInt32 alignmentLog2Bits = alignmentLog2 << ValueTypePaddingAlignmentShift; - Debug.Assert((alignmentLog2Bits & ~ValueTypePaddingAlignmentMask) == 0); - return paddingLowBits | paddingHighBits | alignmentLog2Bits; - } - internal CorElementType CorElementType { get @@ -1315,19 +1277,21 @@ namespace Internal.Runtime if (eField == EETypeField.ETF_SealedVirtualSlots) return cbOffset; - if (IsNullable || (RareFlags & EETypeRareFlags.IsDynamicTypeWithSealedVTableEntriesFlag) != 0) + if (IsNullable) cbOffset += (UInt32)IntPtr.Size; + EETypeRareFlags rareFlags = RareFlags; + // in the case of sealed vtable entries on static types, we have a UInt sized relative pointer - if ((RareFlags & EETypeRareFlags.HasSealedVTableEntriesFlag) != 0) - cbOffset += 4; + if ((rareFlags & EETypeRareFlags.HasSealedVTableEntriesFlag) != 0) + cbOffset += (IsDynamicType ? (UInt32)IntPtr.Size : 4); if (eField == EETypeField.ETF_DynamicDispatchMap) { Debug.Assert(IsDynamicType); return cbOffset; } - if ((RareFlags & EETypeRareFlags.HasDynamicallyAllocatedDispatchMapFlag) != 0) + if ((rareFlags & EETypeRareFlags.HasDynamicallyAllocatedDispatchMapFlag) != 0) cbOffset += (UInt32)IntPtr.Size; if (eField == EETypeField.ETF_GenericDefinition) @@ -1351,7 +1315,7 @@ namespace Internal.Runtime return cbOffset; } - if ((RareFlags & EETypeRareFlags.HasDynamicModuleFlag) != 0) + if ((rareFlags & EETypeRareFlags.HasDynamicModuleFlag) != 0) cbOffset += (UInt32)IntPtr.Size; if (eField == EETypeField.ETF_DynamicTemplateType) @@ -1364,26 +1328,26 @@ namespace Internal.Runtime if (eField == EETypeField.ETF_DynamicGcStatics) { - Debug.Assert((RareFlags & EETypeRareFlags.IsDynamicTypeWithGcStatics) != 0); + Debug.Assert((rareFlags & EETypeRareFlags.IsDynamicTypeWithGcStatics) != 0); return cbOffset; } - if ((RareFlags & EETypeRareFlags.IsDynamicTypeWithGcStatics) != 0) + if ((rareFlags & EETypeRareFlags.IsDynamicTypeWithGcStatics) != 0) cbOffset += (UInt32)IntPtr.Size; if (eField == EETypeField.ETF_DynamicNonGcStatics) { - Debug.Assert((RareFlags & EETypeRareFlags.IsDynamicTypeWithNonGcStatics) != 0); + Debug.Assert((rareFlags & EETypeRareFlags.IsDynamicTypeWithNonGcStatics) != 0); return cbOffset; } - if ((RareFlags & EETypeRareFlags.IsDynamicTypeWithNonGcStatics) != 0) + if ((rareFlags & EETypeRareFlags.IsDynamicTypeWithNonGcStatics) != 0) cbOffset += (UInt32)IntPtr.Size; if (eField == EETypeField.ETF_DynamicThreadStaticOffset) { - Debug.Assert((RareFlags & EETypeRareFlags.IsDynamicTypeWithThreadStatics) != 0); + Debug.Assert((rareFlags & EETypeRareFlags.IsDynamicTypeWithThreadStatics) != 0); return cbOffset; } - if ((RareFlags & EETypeRareFlags.IsDynamicTypeWithThreadStatics) != 0) + if ((rareFlags & EETypeRareFlags.IsDynamicTypeWithThreadStatics) != 0) cbOffset += 4; Debug.Assert(false, "Unknown EEType field type"); diff --git a/external/corert/src/Common/src/Internal/Runtime/EETypeBuilderHelpers.cs b/external/corert/src/Common/src/Internal/Runtime/EETypeBuilderHelpers.cs index 571c260e92..9f59b074e7 100644 --- a/external/corert/src/Common/src/Internal/Runtime/EETypeBuilderHelpers.cs +++ b/external/corert/src/Common/src/Internal/Runtime/EETypeBuilderHelpers.cs @@ -52,7 +52,7 @@ namespace Internal.Runtime break; } - Debug.Assert(false, "Primitive type value expected."); + Debug.Fail("Primitive type value expected."); return 0; } @@ -181,10 +181,10 @@ namespace Internal.Runtime /// of objects on the GCHeap. The amount of padding is recorded to allow unboxing to locals / /// arrays of value types which don't need it. /// - internal static UInt32 ComputeValueTypeFieldPaddingFieldValue(UInt32 padding, UInt32 alignment) + internal static UInt32 ComputeValueTypeFieldPaddingFieldValue(UInt32 padding, UInt32 alignment, int targetPointerSize) { // For the default case, return 0 - if ((padding == 0) && (alignment == IntPtr.Size)) + if ((padding == 0) && (alignment == targetPointerSize)) return 0; UInt32 alignmentLog2 = 0; diff --git a/external/corert/src/Common/src/Internal/Runtime/GCDescEncoder.cs b/external/corert/src/Common/src/Internal/Runtime/GCDescEncoder.cs index 97d1a3cede..3ee9079a30 100644 --- a/external/corert/src/Common/src/Internal/Runtime/GCDescEncoder.cs +++ b/external/corert/src/Common/src/Internal/Runtime/GCDescEncoder.cs @@ -154,9 +154,14 @@ namespace Internal.Runtime private static void EncodeAllGCPointersArrayGCDesc(ref T builder, int baseSize) where T : struct, ITargetBinaryWriter { - builder.EmitNaturalInt(-3 * builder.TargetPointerSize); - builder.EmitNaturalInt(baseSize); + // Construct the gc info as if this array contains exactly one pointer + // - the encoding trick where the size of the series is measured as a difference from + // total object size will make this work for arbitrary array lengths + // Series size + builder.EmitNaturalInt(-(baseSize + builder.TargetPointerSize)); + // Series offset + builder.EmitNaturalInt(baseSize); // NumSeries builder.EmitNaturalInt(1); } @@ -212,4 +217,4 @@ namespace Internal.Runtime builder.EmitNaturalInt(-numSeries); } } -} \ No newline at end of file +} diff --git a/external/corert/src/Common/src/Internal/Runtime/MappingTableFlags.cs b/external/corert/src/Common/src/Internal/Runtime/MappingTableFlags.cs index 552f1d1c98..10fe194669 100644 --- a/external/corert/src/Common/src/Internal/Runtime/MappingTableFlags.cs +++ b/external/corert/src/Common/src/Internal/Runtime/MappingTableFlags.cs @@ -50,6 +50,7 @@ namespace Internal.Runtime IsUniversalCanonicalEntry = 0x04, HasMetadataHandle = 0x08, IsGcSection = 0x10, - FieldOffsetEncodedDirectly = 0x20 + FieldOffsetEncodedDirectly = 0x20, + IsAnyCanonicalEntry = 0x40 } } diff --git a/external/corert/src/Common/src/Internal/Runtime/MetadataBlob.cs b/external/corert/src/Common/src/Internal/Runtime/MetadataBlob.cs index c58a950879..42fedd14e3 100644 --- a/external/corert/src/Common/src/Internal/Runtime/MetadataBlob.cs +++ b/external/corert/src/Common/src/Internal/Runtime/MetadataBlob.cs @@ -21,7 +21,7 @@ namespace Internal.Runtime EmbeddedMetadata = 13, DefaultConstructorMap = 14, UnboxingAndInstantiatingStubMap = 15, - InvokeInstantiations = 16, // unused + StructMarshallingStubMap = 16, DelegateMarshallingStubMap = 17, GenericVirtualMethodTable = 18, InterfaceGenericVirtualMethodTable = 19, @@ -32,6 +32,8 @@ namespace Internal.Runtime DynamicInvokeTemplateData = 23, BlobIdResourceIndex = 24, BlobIdResourceData = 25, + BlobIdStackTraceEmbeddedMetadata = 26, + BlobIdStackTraceMethodRvaToTokenMapping = 27, //Native layout blobs: NativeLayoutInfo = 30, diff --git a/external/corert/src/Common/src/Internal/Runtime/ModuleHeaders.cs b/external/corert/src/Common/src/Internal/Runtime/ModuleHeaders.cs index 4472d515c7..20195b7cc6 100644 --- a/external/corert/src/Common/src/Internal/Runtime/ModuleHeaders.cs +++ b/external/corert/src/Common/src/Internal/Runtime/ModuleHeaders.cs @@ -57,6 +57,8 @@ namespace Internal.Runtime ThreadStaticOffsetRegion = 208, ThreadStaticGCDescRegion = 209, ThreadStaticIndex = 210, + LoopHijackFlag = 211, + ImportAddressTables = 212, // Sections 300 - 399 are reserved for RhFindBlob backwards compatibility ReadonlyBlobRegionStart = 300, diff --git a/external/corert/src/Common/src/Internal/Runtime/RuntimeConstants.cs b/external/corert/src/Common/src/Internal/Runtime/RuntimeConstants.cs index 800f438839..c219ea2a5a 100644 --- a/external/corert/src/Common/src/Internal/Runtime/RuntimeConstants.cs +++ b/external/corert/src/Common/src/Internal/Runtime/RuntimeConstants.cs @@ -45,4 +45,12 @@ namespace Internal.Runtime public const int Mask = Uninitialized | HasPreInitializedData; } + + internal static class ArrayTypesConstants + { + /// + /// Maximum allowable size for array element types. + /// + public const int MaxSizeForValueClassInArray = 0xFFFF; + } } diff --git a/external/corert/src/Common/src/Internal/Runtime/TransitionBlock.cs b/external/corert/src/Common/src/Internal/Runtime/TransitionBlock.cs index 0de2ea9a5c..70d89dd9bc 100644 --- a/external/corert/src/Common/src/Internal/Runtime/TransitionBlock.cs +++ b/external/corert/src/Common/src/Internal/Runtime/TransitionBlock.cs @@ -39,6 +39,8 @@ #define ENREGISTERED_RETURNTYPE_MAXSIZE #define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE #define ENREGISTERED_PARAMTYPE_MAXSIZE +#elif WASM +#define _TARGET_WASM_ #else #error Unknown architecture! #endif @@ -132,26 +134,6 @@ namespace Internal.Runtime IntPtr returnValue2; IntPtr returnValue3; IntPtr returnValue4; - IntPtr returnValue5; - IntPtr returnValue6; - IntPtr returnValue7; - IntPtr returnValue8; - } - - struct CalleeSavedRegisters - { - IntPtr x29; - public IntPtr m_ReturnAddress; // Also known as x30 - IntPtr x19; - IntPtr x20; - IntPtr x21; - IntPtr x22; - IntPtr x23; - IntPtr x24; - IntPtr x25; - IntPtr x26; - IntPtr x27; - IntPtr x28; } struct ArgumentRegisters @@ -164,6 +146,11 @@ namespace Internal.Runtime IntPtr x5; IntPtr x6; IntPtr x7; + IntPtr x8; + public static unsafe int GetOffsetOfx8() + { + return sizeof(IntPtr) * 8; + } } #pragma warning restore 0169 @@ -188,10 +175,10 @@ namespace Internal.Runtime public const int NUM_ARGUMENT_REGISTERS = 8; public const int ARGUMENTREGISTERS_SIZE = NUM_ARGUMENT_REGISTERS * 8; - public const int ENREGISTERED_RETURNTYPE_MAXSIZE = 64; //(maximum HFA size is 8 doubles) - public const int ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE = 8; + public const int ENREGISTERED_RETURNTYPE_MAXSIZE = 32; // bytes (four FP registers: d0,d1,d2 and d3) + public const int ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE = 16; // bytes (two int registers: x0 and x1) public const int ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE_PRIMITIVE = 8; - public const int ENREGISTERED_PARAMTYPE_MAXSIZE = 16; //(max value type size than can be passed by value) + public const int ENREGISTERED_PARAMTYPE_MAXSIZE = 16; // bytes (max value type size that can be passed by value) public const int STACK_ELEM_SIZE = 8; public static int StackElemSize(int size) { return (((size) + STACK_ELEM_SIZE - 1) & ~(STACK_ELEM_SIZE - 1)); } } @@ -283,6 +270,38 @@ namespace Internal.Runtime public const int STACK_ELEM_SIZE = 4; public static int StackElemSize(int size) { return (((size) + STACK_ELEM_SIZE - 1) & ~(STACK_ELEM_SIZE - 1)); } } + +#elif _TARGET_WASM_ +#pragma warning disable 0169 + struct ReturnBlock + { + IntPtr returnValue; + } + + struct ArgumentRegisters + { + // No registers on WASM + } + + struct FloatArgumentRegisters + { + // No registers on WASM + } +#pragma warning restore 0169 + + struct ArchitectureConstants + { + // To avoid corner case bugs, limit maximum size of the arguments with sufficient margin + public const int MAX_ARG_SIZE = 0xFFFFFF; + + public const int NUM_ARGUMENT_REGISTERS = 0; + public const int ARGUMENTREGISTERS_SIZE = NUM_ARGUMENT_REGISTERS * 4; + public const int ENREGISTERED_RETURNTYPE_MAXSIZE = 32; + public const int ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE = 4; + public const int ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE_PRIMITIVE = 8; + public const int STACK_ELEM_SIZE = 4; + public static int StackElemSize(int size) { return (((size) + STACK_ELEM_SIZE - 1) & ~(STACK_ELEM_SIZE - 1)); } + } #endif // @@ -362,10 +381,24 @@ namespace Internal.Runtime { return sizeof(ReturnBlock); } + + public IntPtr m_alignmentPad; +#elif _TARGET_WASM_ + public ReturnBlock m_returnBlock; + public static unsafe int GetOffsetOfReturnValuesBlock() + { + return 0; + } + + public ArgumentRegisters m_argumentRegisters; + public static unsafe int GetOffsetOfArgumentRegisters() + { + return sizeof(ReturnBlock); + } #else #error Portability problem #endif -#pragma warning restore 0169,0649 +#pragma warning restore 0169, 0649 // The transition block should define everything pushed by callee. The code assumes in number of places that // end of the transition block is caller's stack pointer. @@ -395,6 +428,11 @@ namespace Internal.Runtime { return ((offset - GetOffsetOfArgumentRegisters()) / IntPtr.Size); } + + public static int GetStackArgumentIndexFromOffset(int offset) + { + return (offset - GetOffsetOfArgs()) / ArchitectureConstants.STACK_ELEM_SIZE; + } #endif #if CALLDESCR_FPARGREGS diff --git a/external/corert/src/Common/src/Internal/Runtime/TypeManagerHandle.RVA.cs b/external/corert/src/Common/src/Internal/Runtime/TypeManagerHandle.RVA.cs index 4131084822..b3fed2ac5c 100644 --- a/external/corert/src/Common/src/Internal/Runtime/TypeManagerHandle.RVA.cs +++ b/external/corert/src/Common/src/Internal/Runtime/TypeManagerHandle.RVA.cs @@ -51,6 +51,17 @@ namespace System.Runtime } } + public unsafe IntPtr OsModuleBase + { + get + { + if (IsTypeManager) + return AsTypeManagerPtr->OsHandle; + else + return AsOsModuleIntPtr; + } + } + [CLSCompliant(false)] public unsafe byte* ConvertRVAToPointer(int rva) { @@ -60,18 +71,11 @@ namespace System.Runtime [CLSCompliant(false)] public unsafe byte* ConvertRVAToPointer(uint rva) { -#if CORERT - Environment.FailFast("RVA fixups not supported in CoreRT"); - return null; +#if PROJECTN + return ((byte*)OsModuleBase) + rva; #else - IntPtr osModuleBase; - - if (IsTypeManager) - osModuleBase = AsTypeManagerPtr->OsHandle; - else - osModuleBase = AsOsModuleIntPtr; - - return ((byte*)osModuleBase) + rva; + Environment.FailFast("RVA fixups not supported"); + return null; #endif } diff --git a/external/corert/src/Common/src/Internal/Runtime/UniversalGenericParameterLayout.cs b/external/corert/src/Common/src/Internal/Runtime/UniversalGenericParameterLayout.cs index dcc5f5ca6d..4cfb43d0a7 100644 --- a/external/corert/src/Common/src/Internal/Runtime/UniversalGenericParameterLayout.cs +++ b/external/corert/src/Common/src/Internal/Runtime/UniversalGenericParameterLayout.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics; using Internal.TypeSystem; @@ -96,5 +97,14 @@ namespace Internal.Runtime return false; } + + public static bool VTableMethodRequiresCallingConventionConverter(MethodDesc method) + { + if (!MethodSignatureHasVarsNeedingCallingConventionConverter(method.GetTypicalMethodDefinition().Signature)) + return false; + + MethodDesc slotDecl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method).GetCanonMethodTarget(CanonicalFormKind.Specific); + return slotDecl.IsCanonicalMethod(CanonicalFormKind.Universal); + } } -} \ No newline at end of file +} diff --git a/external/corert/src/Common/src/Internal/Text/Utf8String.cs b/external/corert/src/Common/src/Internal/Text/Utf8String.cs index 3587a2c92d..47e7013507 100644 --- a/external/corert/src/Common/src/Internal/Text/Utf8String.cs +++ b/external/corert/src/Common/src/Internal/Text/Utf8String.cs @@ -8,7 +8,7 @@ using System.Text; namespace Internal.Text { - public struct Utf8String : IEquatable + public struct Utf8String : IEquatable, IComparable { private byte[] _value; @@ -112,5 +112,40 @@ namespace Internal.Text } } } + + private static int Compare(Utf8String strA, Utf8String strB) + { + int length = Math.Min(strA.Length, strB.Length); + + unsafe + { + fixed (byte* ap = strA._value) + fixed (byte* bp = strB._value) + { + byte* a = ap; + byte* b = bp; + + while (length > 0) + { + if (*a != *b) + return *a - *b; + a += 1; + b += 1; + length -= 1; + } + + // At this point, we have compared all the characters in at least one string. + // The longer string will be larger. + // We could optimize and compare lengths before iterating strings, but we want + // Foo and Foo1 to be sorted adjacent to eachother. + return strA.Length - strB.Length; + } + } + } + + public int CompareTo(Utf8String other) + { + return Compare(this, other); + } } } diff --git a/external/corert/src/Common/src/Internal/Text/Utf8StringBuilder.cs b/external/corert/src/Common/src/Internal/Text/Utf8StringBuilder.cs index 61a124303f..1fcca94507 100644 --- a/external/corert/src/Common/src/Internal/Text/Utf8StringBuilder.cs +++ b/external/corert/src/Common/src/Internal/Text/Utf8StringBuilder.cs @@ -4,6 +4,7 @@ using System; using System.Text; +using System.Diagnostics; namespace Internal.Text { @@ -26,6 +27,13 @@ namespace Internal.Text return this; } + public Utf8StringBuilder Truncate(int newLength) + { + Debug.Assert(newLength <= _length); + _length = newLength; + return this; + } + public Utf8StringBuilder Append(Utf8String value) { return Append(value.UnderlyingArray); diff --git a/external/corert/src/Common/src/Interop/Unix/System.Native/Interop.SysConf.cs b/external/corert/src/Common/src/Interop/Unix/System.Native/Interop.SysConf.cs deleted file mode 100644 index 5becba3d28..0000000000 --- a/external/corert/src/Common/src/Interop/Unix/System.Native/Interop.SysConf.cs +++ /dev/null @@ -1,21 +0,0 @@ -// 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.Runtime.InteropServices; - -internal static partial class Interop -{ - internal static unsafe partial class Sys - { - internal enum SysConfName - { - _SC_CLK_TCK = 1, - _SC_PAGESIZE = 2, - _SC_NPROCESSORS_ONLN = 3, - } - - [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_SysConf", SetLastError = true)] - internal static extern long SysConf(SysConfName name); - } -} diff --git a/external/corert/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.Exit.cs b/external/corert/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.Exit.cs index 469566805f..a8fe22792f 100644 --- a/external/corert/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.Exit.cs +++ b/external/corert/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.Exit.cs @@ -13,9 +13,4 @@ internal static partial class Interop [DllImport(Interop.Libraries.CoreLibNative, EntryPoint = "CoreLibNative_Exit")] internal static extern void Exit(int exitCode); } - - internal static void ExitProcess(int exitCode) - { - Sys.Exit(exitCode); - } } diff --git a/external/corert/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.GetCpuUtilization.cs b/external/corert/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.GetCpuUtilization.cs new file mode 100644 index 0000000000..1f01d97a86 --- /dev/null +++ b/external/corert/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.GetCpuUtilization.cs @@ -0,0 +1,24 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal unsafe partial class Sys + { + [StructLayout(LayoutKind.Sequential)] + internal struct ProcessCpuInformation + { + ulong lastRecordedCurrentTime; + ulong lastRecordedKernelTime; + ulong lastRecordedUserTime; + } + + [DllImport(Libraries.CoreLibNative, EntryPoint = "CoreLibNative_GetCpuUtilization")] + internal static extern unsafe int GetCpuUtilization(ref ProcessCpuInformation previousCpuInfo); + } +} diff --git a/external/corert/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.HighPrecisionCounter.cs b/external/corert/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.HighPrecisionCounter.cs new file mode 100644 index 0000000000..42e5db8a89 --- /dev/null +++ b/external/corert/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.HighPrecisionCounter.cs @@ -0,0 +1,19 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal unsafe partial class Sys + { + [DllImport(Interop.Libraries.CoreLibNative, EntryPoint = "CoreLibNative_GetHighPrecisionCount")] + internal static extern ulong GetHighPrecisionCount(); + + [DllImport(Interop.Libraries.CoreLibNative, EntryPoint = "CoreLibNative_GetHighPrecisionCounterFrequency")] + internal static extern ulong GetHighPrecisionCounterFrequency(); + } +} diff --git a/external/corert/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.FormatProvider.FormatAndParse.cs b/external/corert/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.Number.cs similarity index 100% rename from external/corert/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.FormatProvider.FormatAndParse.cs rename to external/corert/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.Number.cs diff --git a/external/corert/src/Common/src/Interop/Windows/advapi32/Interop.RegDeleteValue.cs b/external/corert/src/Common/src/Interop/Windows/advapi32/Interop.RegDeleteValue.cs new file mode 100644 index 0000000000..91369a01fc --- /dev/null +++ b/external/corert/src/Common/src/Interop/Windows/advapi32/Interop.RegDeleteValue.cs @@ -0,0 +1,16 @@ +// 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.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +internal partial class Interop +{ + internal partial class Advapi32 + { + [DllImport("advapi32.dll", CharSet = CharSet.Unicode, BestFitMapping = false, EntryPoint = "RegDeleteValueW")] + internal static extern int RegDeleteValue(SafeRegistryHandle hKey, String lpValueName); + } +} diff --git a/external/corert/src/Common/src/Interop/Windows/advapi32/Interop.RegSetValueEx.cs b/external/corert/src/Common/src/Interop/Windows/advapi32/Interop.RegSetValueEx.cs new file mode 100644 index 0000000000..59e038d959 --- /dev/null +++ b/external/corert/src/Common/src/Interop/Windows/advapi32/Interop.RegSetValueEx.cs @@ -0,0 +1,17 @@ +// 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.Runtime.InteropServices; +using Microsoft.Win32; +using Microsoft.Win32.SafeHandles; + +internal partial class Interop +{ + internal partial class Advapi32 + { + [DllImport("advapi32.dll", CharSet = CharSet.Unicode, BestFitMapping = false, EntryPoint = "RegSetValueExW")] + internal static extern int RegSetValueEx(SafeRegistryHandle hKey, string lpValueName, int Reserved, RegistryValueKind dwType, string lpData, int cbData); + } +} diff --git a/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.CompletionPort.cs b/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.CompletionPort.cs new file mode 100644 index 0000000000..326f0e9df0 --- /dev/null +++ b/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.CompletionPort.cs @@ -0,0 +1,23 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal static extern IntPtr CreateIoCompletionPort(IntPtr FileHandle, IntPtr ExistingCompletionPort, UIntPtr CompletionKey, int NumberOfConcurrentThreads); + + [DllImport(Libraries.Kernel32, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool PostQueuedCompletionStatus(IntPtr CompletionPort, int dwNumberOfBytesTransferred, UIntPtr CompletionKey, IntPtr lpOverlapped); + + [DllImport(Libraries.Kernel32, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool GetQueuedCompletionStatus(IntPtr CompletionPort, out int lpNumberOfBytes, out UIntPtr CompletionKey, out IntPtr lpOverlapped, int dwMilliseconds); + } +} diff --git a/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.ConditionVariable.cs b/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.ConditionVariable.cs new file mode 100644 index 0000000000..0dcb7c0946 --- /dev/null +++ b/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.ConditionVariable.cs @@ -0,0 +1,28 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [StructLayout(LayoutKind.Sequential)] + internal struct CONDITION_VARIABLE + { + private IntPtr Ptr; + } + + [DllImport(Libraries.Kernel32)] + internal static extern void InitializeConditionVariable(out CONDITION_VARIABLE ConditionVariable); + + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal static extern void WakeConditionVariable(ref CONDITION_VARIABLE ConditionVariable); + + [DllImport(Libraries.Kernel32, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool SleepConditionVariableCS(ref CONDITION_VARIABLE ConditionVariable, ref CRITICAL_SECTION CriticalSection, int dwMilliseconds); + } +} diff --git a/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.CriticalSection.cs b/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.CriticalSection.cs new file mode 100644 index 0000000000..56278fba1e --- /dev/null +++ b/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.CriticalSection.cs @@ -0,0 +1,35 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [StructLayout(LayoutKind.Sequential)] + internal struct CRITICAL_SECTION + { + private IntPtr DebugInfo; + private int LockCount; + private int RecursionCount; + private IntPtr OwningThread; + private IntPtr LockSemaphore; + private UIntPtr SpinCount; + } + + [DllImport(Libraries.Kernel32)] + internal static extern void InitializeCriticalSection(out CRITICAL_SECTION lpCriticalSection); + + [DllImport(Libraries.Kernel32)] + internal static extern void EnterCriticalSection(ref CRITICAL_SECTION lpCriticalSection); + + [DllImport(Libraries.Kernel32)] + internal static extern void LeaveCriticalSection(ref CRITICAL_SECTION lpCriticalSection); + + [DllImport(Libraries.Kernel32)] + internal static extern void DeleteCriticalSection(ref CRITICAL_SECTION lpCriticalSection); + } +} diff --git a/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.ExitProcess.cs b/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.ExitProcess.cs new file mode 100644 index 0000000000..6a0d408cd1 --- /dev/null +++ b/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.ExitProcess.cs @@ -0,0 +1,16 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static unsafe partial class Kernel32 + { + [DllImport(Libraries.Kernel32, EntryPoint = "ExitProcess")] + internal static extern void ExitProcess(int exitCode); + } +} diff --git a/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.ExpandEnvironmentStrings.cs b/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.ExpandEnvironmentStrings.cs new file mode 100644 index 0000000000..438de060cd --- /dev/null +++ b/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.ExpandEnvironmentStrings.cs @@ -0,0 +1,14 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, EntryPoint = "ExpandEnvironmentStringsW", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern int ExpandEnvironmentStrings(string lpSrc, [Out] char[] lpDst, int nSize); + } +} diff --git a/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.GetSystemTimes.cs b/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.GetSystemTimes.cs new file mode 100644 index 0000000000..892948dd17 --- /dev/null +++ b/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.GetSystemTimes.cs @@ -0,0 +1,16 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool GetSystemTimes(out ulong idleTime, out ulong kernelTime, out ulong userTime); + } +} diff --git a/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.Memory.cs b/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.Memory.cs index 96e6ef643f..2af6d953da 100644 --- a/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.Memory.cs +++ b/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.Memory.cs @@ -9,9 +9,6 @@ internal partial class Interop { internal partial class Kernel32 { - [DllImport(Libraries.Kernel32)] - unsafe internal static extern void* VirtualAlloc(void* address, UIntPtr numBytes, int commitOrReserve, int pageProtectionMode); - [DllImport(Libraries.Kernel32)] unsafe internal static extern bool VirtualFree(void* address, UIntPtr numBytes, int pageFreeMode); diff --git a/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.QueryPerformance.cs b/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.QueryPerformance.cs new file mode 100644 index 0000000000..8486c65fb6 --- /dev/null +++ b/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.QueryPerformance.cs @@ -0,0 +1,17 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32)] + internal static extern bool QueryPerformanceCounter(out ulong value); + + [DllImport(Libraries.Kernel32)] + internal static extern bool QueryPerformanceFrequency(out ulong value); + } +} \ No newline at end of file diff --git a/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.VirtualAlloc.cs b/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.VirtualAlloc.cs new file mode 100644 index 0000000000..627184ca39 --- /dev/null +++ b/external/corert/src/Common/src/Interop/Windows/kernel32/Interop.VirtualAlloc.cs @@ -0,0 +1,15 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32)] + internal static extern unsafe void* VirtualAlloc(void* address, UIntPtr numBytes, int commitOrReserve, int pageProtectionMode); + } +} diff --git a/external/corert/src/Common/src/Interop/Windows/mincore/Interop.DynamicLoad.cs b/external/corert/src/Common/src/Interop/Windows/mincore/Interop.DynamicLoad.cs index 2eed131c6a..d839b6db85 100644 --- a/external/corert/src/Common/src/Interop/Windows/mincore/Interop.DynamicLoad.cs +++ b/external/corert/src/Common/src/Interop/Windows/mincore/Interop.DynamicLoad.cs @@ -1,11 +1,11 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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.Runtime.InteropServices; using System.Text; -using Microsoft.Win32; +using Microsoft.Win32.SafeHandles; internal static partial class Interop { @@ -17,16 +17,7 @@ internal static partial class Interop [DllImport("api-ms-win-core-libraryloader-l1-2-0.dll", EntryPoint = "LoadLibraryExW", CharSet = CharSet.Unicode)] internal static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, int dwFlags); - [DllImport("api-ms-win-core-libraryloader-l1-2-0.dll", EntryPoint = "LoadLibraryExW", CharSet = CharSet.Unicode)] - internal static extern SafeLibraryHandle LoadLibraryEx_SafeHandle(string lpFileName, IntPtr hFile, int dwFlags); - [DllImport("api-ms-win-core-libraryloader-l1-2-0.dll")] internal static extern bool FreeLibrary(IntPtr hModule); - - [DllImport("api-ms-win-core-libraryloader-l1-2-0.dll", EntryPoint = "LoadStringW", CharSet = CharSet.Unicode)] - internal static extern int LoadString(SafeLibraryHandle handle, int id, StringBuilder buffer, int bufferLength); - - internal const int LOAD_LIBRARY_AS_DATAFILE = 0x00000002; - internal const int LOAD_STRING_MAX_LENGTH = 500; } } diff --git a/external/corert/src/Common/src/Interop/Windows/mincore/Interop.Environment.cs b/external/corert/src/Common/src/Interop/Windows/mincore/Interop.Environment.cs index e2b4a87ebc..10d0c9cef4 100644 --- a/external/corert/src/Common/src/Interop/Windows/mincore/Interop.Environment.cs +++ b/external/corert/src/Common/src/Interop/Windows/mincore/Interop.Environment.cs @@ -12,13 +12,5 @@ internal static partial class Interop { [DllImport(Libraries.ProcessEnvironment, CharSet = CharSet.Unicode, EntryPoint = "GetEnvironmentVariableW")] internal static extern unsafe int GetEnvironmentVariable(string lpName, [Out] char[] lpValue, int size); - - [DllImport(Libraries.Kernel32, EntryPoint = "ExitProcess")] - internal static extern void ExitProcess(int exitCode); - } - - internal static void ExitProcess(int exitCode) - { - mincore.ExitProcess(exitCode); } } diff --git a/external/corert/src/Common/src/Interop/Windows/mincore/Interop.SYSTEM_INFO.cs b/external/corert/src/Common/src/Interop/Windows/mincore/Interop.SYSTEM_INFO.cs deleted file mode 100644 index ec283cab9e..0000000000 --- a/external/corert/src/Common/src/Interop/Windows/mincore/Interop.SYSTEM_INFO.cs +++ /dev/null @@ -1,27 +0,0 @@ -// 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.Runtime.InteropServices; - -internal static partial class Interop -{ - internal static partial class mincore - { - [StructLayout(LayoutKind.Sequential)] - internal struct SYSTEM_INFO - { - internal uint dwOemId; - internal uint dwPageSize; - internal IntPtr lpMinimumApplicationAddress; - internal IntPtr lpMaximumApplicationAddress; - internal UIntPtr dwActiveProcessorMask; - internal uint dwNumberOfProcessors; - internal uint dwProcessorType; - internal uint dwAllocationGranularity; - internal ushort wProcessorLevel; - internal ushort wProcessorRevision; - } - } -} diff --git a/external/corert/src/Common/src/Interop/Windows/mincore/Interop.ThreadPool.cs b/external/corert/src/Common/src/Interop/Windows/mincore/Interop.ThreadPool.cs index 13142f650c..202440c946 100644 --- a/external/corert/src/Common/src/Interop/Windows/mincore/Interop.ThreadPool.cs +++ b/external/corert/src/Common/src/Interop/Windows/mincore/Interop.ThreadPool.cs @@ -19,5 +19,19 @@ internal static partial class Interop [DllImport("api-ms-win-core-threadpool-l1-2-0.dll")] internal extern static void CloseThreadpoolWork(IntPtr pwk); + + internal delegate void WaitCallback(IntPtr Instance, IntPtr Context, IntPtr Wait, uint WaitResult); + + [DllImport("api-ms-win-core-threadpool-l1-2-0.dll")] + internal extern static IntPtr CreateThreadpoolWait(IntPtr pfnwa, IntPtr pv, IntPtr pcbe); + + [DllImport("api-ms-win-core-threadpool-l1-2-0.dll")] + internal extern static void SetThreadpoolWait(IntPtr pwa, IntPtr h, IntPtr pftTimeout); + + [DllImport("api-ms-win-core-threadpool-l1-2-0.dll")] + internal extern static void WaitForThreadpoolWaitCallbacks(IntPtr pwa, bool fCancelPendingCallbacks); + + [DllImport("api-ms-win-core-threadpool-l1-2-0.dll")] + internal extern static void CloseThreadpoolWait(IntPtr pwa); } } diff --git a/external/corert/src/Common/src/Interop/Windows/mincore/Interop.GetNativeSystemInfo.cs b/external/corert/src/Common/src/Interop/Windows/mincore/Interop.VirtualAllocFromApp.cs similarity index 63% rename from external/corert/src/Common/src/Interop/Windows/mincore/Interop.GetNativeSystemInfo.cs rename to external/corert/src/Common/src/Interop/Windows/mincore/Interop.VirtualAllocFromApp.cs index ff63edf1bb..9d645b926b 100644 --- a/external/corert/src/Common/src/Interop/Windows/mincore/Interop.GetNativeSystemInfo.cs +++ b/external/corert/src/Common/src/Interop/Windows/mincore/Interop.VirtualAllocFromApp.cs @@ -9,7 +9,7 @@ internal static partial class Interop { internal static partial class mincore { - [DllImport(Interop.Libraries.SysInfo)] - internal extern static void GetNativeSystemInfo(out SYSTEM_INFO lpSystemInfo); + [DllImport("api-ms-win-core-memory-l1-1-3.dll")] + internal static extern unsafe void* VirtualAllocFromApp(void* address, UIntPtr numBytes, int commitOrReserve, int pageProtectionMode); } } diff --git a/external/corert/src/Common/src/System/Collections/Concurrent/ConcurrentUnifier.cs b/external/corert/src/Common/src/System/Collections/Concurrent/ConcurrentUnifier.cs index 0be787567d..cc7edb0a40 100644 --- a/external/corert/src/Common/src/System/Collections/Concurrent/ConcurrentUnifier.cs +++ b/external/corert/src/Common/src/System/Collections/Concurrent/ConcurrentUnifier.cs @@ -282,7 +282,7 @@ namespace System.Collections.Concurrent if (walk2 != -1) walk2 = _entries[walk2]._next; if (walk1 == walk2 && walk2 != -1) - Debug.Assert(false, "Bucket " + bucket + " has a cycle in its linked list."); + Debug.Fail("Bucket " + bucket + " has a cycle in its linked list."); } } // The assertion is "<=" rather than "==" because we allow an entry to "leak" until the next resize if diff --git a/external/corert/src/Common/src/System/Collections/Concurrent/ConcurrentUnifierW.cs b/external/corert/src/Common/src/System/Collections/Concurrent/ConcurrentUnifierW.cs index 916a1276c2..8324b6c398 100644 --- a/external/corert/src/Common/src/System/Collections/Concurrent/ConcurrentUnifierW.cs +++ b/external/corert/src/Common/src/System/Collections/Concurrent/ConcurrentUnifierW.cs @@ -220,7 +220,7 @@ namespace System.Collections.Concurrent { V heyYoureSupposedToBeDead; if (_entries[idx]._weakValue.TryGetTarget(out heyYoureSupposedToBeDead)) - Debug.Assert(false, "Add: You were supposed to verify inside the lock that this entry's weak reference had already expired!"); + Debug.Fail("Add: You were supposed to verify inside the lock that this entry's weak reference had already expired!"); } #endif //DEBUG _entries[idx]._weakValue = new WeakReference(value, trackResurrection: false); @@ -367,7 +367,7 @@ namespace System.Collections.Concurrent if (walk2 != -1) walk2 = _entries[walk2]._next; if (walk1 == walk2 && walk2 != -1) - Debug.Assert(false, "Bucket " + bucket + " has a cycle in its linked list."); + Debug.Fail("Bucket " + bucket + " has a cycle in its linked list."); } } // The assertion is "<=" rather than "==" because we allow an entry to "leak" until the next resize if diff --git a/external/corert/src/Common/src/System/Collections/Concurrent/ConcurrentUnifierWKeyed.cs b/external/corert/src/Common/src/System/Collections/Concurrent/ConcurrentUnifierWKeyed.cs index 6ebfef65ee..8aa21984db 100644 --- a/external/corert/src/Common/src/System/Collections/Concurrent/ConcurrentUnifierWKeyed.cs +++ b/external/corert/src/Common/src/System/Collections/Concurrent/ConcurrentUnifierWKeyed.cs @@ -362,7 +362,7 @@ namespace System.Collections.Concurrent if (walk2 != -1) walk2 = _entries[walk2]._next; if (walk1 == walk2 && walk2 != -1) - Debug.Assert(false, "Bucket " + bucket + " has a cycle in its linked list."); + Debug.Fail("Bucket " + bucket + " has a cycle in its linked list."); } } // The assertion is "<=" rather than "==" because we allow an entry to "leak" until the next resize if diff --git a/external/corert/src/Common/src/System/Collections/Generic/ArrayBuilder.cs b/external/corert/src/Common/src/System/Collections/Generic/ArrayBuilder.cs index 6b2df18b2f..07548eff7f 100644 --- a/external/corert/src/Common/src/System/Collections/Generic/ArrayBuilder.cs +++ b/external/corert/src/Common/src/System/Collections/Generic/ArrayBuilder.cs @@ -32,11 +32,29 @@ namespace System.Collections.Generic public void Append(T[] newItems) { - if (newItems.Length == 0) + Append(newItems, 0, newItems.Length); + } + + public void Append(T[] newItems, int offset, int length) + { + if (length == 0) return; - EnsureCapacity(_count + newItems.Length); - Array.Copy(newItems, 0, _items, _count, newItems.Length); - _count += newItems.Length; + + Debug.Assert(length > 0); + Debug.Assert(newItems.Length >= offset + length); + + EnsureCapacity(_count + length); + Array.Copy(newItems, offset, _items, _count, length); + _count += length; + } + + public void Append(ArrayBuilder newItems) + { + if (newItems.Count == 0) + return; + EnsureCapacity(_count + newItems.Count); + Array.Copy(newItems._items, 0, _items, _count, newItems.Count); + _count += newItems.Count; } public void ZeroExtend(int numItems) diff --git a/external/corert/src/Common/src/System/Collections/Generic/LowLevelList.cs b/external/corert/src/Common/src/System/Collections/Generic/LowLevelList.cs index bf06fe3c8a..12bbbdaea5 100644 --- a/external/corert/src/Common/src/System/Collections/Generic/LowLevelList.cs +++ b/external/corert/src/Common/src/System/Collections/Generic/LowLevelList.cs @@ -20,7 +20,6 @@ using System; using System.Diagnostics; -using System.Diagnostics.Contracts; namespace System.Collections.Generic { @@ -45,7 +44,6 @@ namespace System.Collections.Generic private const int _defaultCapacity = 4; protected T[] _items; - [ContractPublicPropertyName("Count")] protected int _size; protected int _version; @@ -66,7 +64,6 @@ namespace System.Collections.Generic public LowLevelList(int capacity) { if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity)); - Contract.EndContractBlock(); if (capacity == 0) _items = s_emptyArray; @@ -82,7 +79,6 @@ namespace System.Collections.Generic { if (collection == null) throw new ArgumentNullException(nameof(collection)); - Contract.EndContractBlock(); ICollection c = collection as ICollection; if (c != null) @@ -124,7 +120,6 @@ namespace System.Collections.Generic { get { - Contract.Ensures(Contract.Result() >= 0); return _items.Length; } set @@ -133,7 +128,6 @@ namespace System.Collections.Generic { throw new ArgumentOutOfRangeException(nameof(value)); } - Contract.EndContractBlock(); if (value != _items.Length) { @@ -156,7 +150,6 @@ namespace System.Collections.Generic { get { - Contract.Ensures(Contract.Result() >= 0); return _size; } } @@ -172,7 +165,6 @@ namespace System.Collections.Generic { throw new ArgumentOutOfRangeException(); } - Contract.EndContractBlock(); return _items[index]; } @@ -182,7 +174,6 @@ namespace System.Collections.Generic { throw new ArgumentOutOfRangeException(); } - Contract.EndContractBlock(); _items[index] = value; _version++; } @@ -224,7 +215,6 @@ namespace System.Collections.Generic // public void AddRange(IEnumerable collection) { - Contract.Ensures(Count >= Contract.OldValue(Count)); InsertRange(_size, collection); } @@ -273,7 +263,6 @@ namespace System.Collections.Generic { throw new ArgumentException(); } - Contract.EndContractBlock(); // Delegate rest of error checking to Array.Copy. Array.Copy(_items, index, array, arrayIndex, count); @@ -295,8 +284,6 @@ namespace System.Collections.Generic // public int IndexOf(T item) { - Contract.Ensures(Contract.Result() >= -1); - Contract.Ensures(Contract.Result() < Count); return Array.IndexOf(_items, item, 0, _size); } @@ -314,9 +301,6 @@ namespace System.Collections.Generic { if (index > _size) throw new ArgumentOutOfRangeException(nameof(index)); - Contract.Ensures(Contract.Result() >= -1); - Contract.Ensures(Contract.Result() < Count); - Contract.EndContractBlock(); return Array.IndexOf(_items, item, index, _size - index); } @@ -331,7 +315,6 @@ namespace System.Collections.Generic { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); if (_size == _items.Length) EnsureCapacity(_size + 1); if (index < _size) { @@ -358,7 +341,6 @@ namespace System.Collections.Generic { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); ICollection c = collection as ICollection; if (c != null) @@ -425,9 +407,6 @@ namespace System.Collections.Generic { throw new ArgumentNullException(nameof(match)); } - Contract.Ensures(Contract.Result() >= 0); - Contract.Ensures(Contract.Result() <= Contract.OldValue(Count)); - Contract.EndContractBlock(); int freeIndex = 0; // the first free slot in items array @@ -464,7 +443,6 @@ namespace System.Collections.Generic { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); _size--; if (index < _size) { @@ -478,9 +456,6 @@ namespace System.Collections.Generic // This requires copying the List, which is an O(n) operation. public T[] ToArray() { - Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == Count); - T[] array = new T[_size]; Array.Copy(_items, 0, array, 0, _size); return array; diff --git a/external/corert/src/Common/src/System/Collections/HashHelpers.cs b/external/corert/src/Common/src/System/Collections/HashHelpers.cs index 073c88cb9d..464572c1cc 100644 --- a/external/corert/src/Common/src/System/Collections/HashHelpers.cs +++ b/external/corert/src/Common/src/System/Collections/HashHelpers.cs @@ -13,7 +13,6 @@ ===========================================================*/ using System.Diagnostics; -using System.Diagnostics.Contracts; namespace System.Collections { @@ -60,7 +59,6 @@ namespace System.Collections { if (min < 0) throw new ArgumentException(SR.Arg_HTCapacityOverflow); - Contract.EndContractBlock(); for (int i = 0; i < primes.Length; i++) { diff --git a/external/corert/src/Common/src/System/Globalization/FormatProvider.Number.cs b/external/corert/src/Common/src/System/Globalization/FormatProvider.Number.cs deleted file mode 100644 index ebb98a33f6..0000000000 --- a/external/corert/src/Common/src/System/Globalization/FormatProvider.Number.cs +++ /dev/null @@ -1,1515 +0,0 @@ -// 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.Diagnostics; -using System.Text; - -namespace System.Globalization -{ - internal partial class FormatProvider - { - // The Number class implements methods for formatting and parsing - // numeric values. To format and parse numeric values, applications should - // use the Format and Parse methods provided by the numeric - // classes (Byte, Int16, Int32, Int64, - // Single, Double, Currency, and Decimal). Those - // Format and Parse methods share a common implementation - // provided by this class, and are thus documented in detail here. - // - // Formatting - // - // The Format methods provided by the numeric classes are all of the - // form - // - // public static String Format(XXX value, String format); - // public static String Format(XXX value, String format, NumberFormatInfo info); - // - // where XXX is the name of the particular numeric class. The methods convert - // the numeric value to a string using the format string given by the - // format parameter. If the format parameter is null or - // an empty string, the number is formatted as if the string "G" (general - // format) was specified. The info parameter specifies the - // NumberFormatInfo instance to use when formatting the number. If the - // info parameter is null or omitted, the numeric formatting information - // is obtained from the current culture. The NumberFormatInfo supplies - // such information as the characters to use for decimal and thousand - // separators, and the spelling and placement of currency symbols in monetary - // values. - // - // Format strings fall into two categories: Standard format strings and - // user-defined format strings. A format string consisting of a single - // alphabetic character (A-Z or a-z), optionally followed by a sequence of - // digits (0-9), is a standard format string. All other format strings are - // used-defined format strings. - // - // A standard format string takes the form Axx, where A is an - // alphabetic character called the format specifier and xx is a - // sequence of digits called the precision specifier. The format - // specifier controls the type of formatting applied to the number and the - // precision specifier controls the number of significant digits or decimal - // places of the formatting operation. The following table describes the - // supported standard formats. - // - // C c - Currency format. The number is - // converted to a string that represents a currency amount. The conversion is - // controlled by the currency format information of the NumberFormatInfo - // used to format the number. The precision specifier indicates the desired - // number of decimal places. If the precision specifier is omitted, the default - // currency precision given by the NumberFormatInfo is used. - // - // D d - Decimal format. This format is - // supported for integral types only. The number is converted to a string of - // decimal digits, prefixed by a minus sign if the number is negative. The - // precision specifier indicates the minimum number of digits desired in the - // resulting string. If required, the number will be left-padded with zeros to - // produce the number of digits given by the precision specifier. - // - // E e Engineering (scientific) format. - // The number is converted to a string of the form - // "-d.ddd...E+ddd" or "-d.ddd...e+ddd", where each - // 'd' indicates a digit (0-9). The string starts with a minus sign if the - // number is negative, and one digit always precedes the decimal point. The - // precision specifier indicates the desired number of digits after the decimal - // point. If the precision specifier is omitted, a default of 6 digits after - // the decimal point is used. The format specifier indicates whether to prefix - // the exponent with an 'E' or an 'e'. The exponent is always consists of a - // plus or minus sign and three digits. - // - // F f Fixed point format. The number is - // converted to a string of the form "-ddd.ddd....", where each - // 'd' indicates a digit (0-9). The string starts with a minus sign if the - // number is negative. The precision specifier indicates the desired number of - // decimal places. If the precision specifier is omitted, the default numeric - // precision given by the NumberFormatInfo is used. - // - // G g - General format. The number is - // converted to the shortest possible decimal representation using fixed point - // or scientific format. The precision specifier determines the number of - // significant digits in the resulting string. If the precision specifier is - // omitted, the number of significant digits is determined by the type of the - // number being converted (10 for int, 19 for long, 7 for - // float, 15 for double, 19 for Currency, and 29 for - // Decimal). Trailing zeros after the decimal point are removed, and the - // resulting string contains a decimal point only if required. The resulting - // string uses fixed point format if the exponent of the number is less than - // the number of significant digits and greater than or equal to -4. Otherwise, - // the resulting string uses scientific format, and the case of the format - // specifier controls whether the exponent is prefixed with an 'E' or an - // 'e'. - // - // N n Number format. The number is - // converted to a string of the form "-d,ddd,ddd.ddd....", where - // each 'd' indicates a digit (0-9). The string starts with a minus sign if the - // number is negative. Thousand separators are inserted between each group of - // three digits to the left of the decimal point. The precision specifier - // indicates the desired number of decimal places. If the precision specifier - // is omitted, the default numeric precision given by the - // NumberFormatInfo is used. - // - // X x - Hexadecimal format. This format is - // supported for integral types only. The number is converted to a string of - // hexadecimal digits. The format specifier indicates whether to use upper or - // lower case characters for the hexadecimal digits above 9 ('X' for 'ABCDEF', - // and 'x' for 'abcdef'). The precision specifier indicates the minimum number - // of digits desired in the resulting string. If required, the number will be - // left-padded with zeros to produce the number of digits given by the - // precision specifier. - // - // Some examples of standard format strings and their results are shown in the - // table below. (The examples all assume a default NumberFormatInfo.) - // - // Value Format Result - // 12345.6789 C $12,345.68 - // -12345.6789 C ($12,345.68) - // 12345 D 12345 - // 12345 D8 00012345 - // 12345.6789 E 1.234568E+004 - // 12345.6789 E10 1.2345678900E+004 - // 12345.6789 e4 1.2346e+004 - // 12345.6789 F 12345.68 - // 12345.6789 F0 12346 - // 12345.6789 F6 12345.678900 - // 12345.6789 G 12345.6789 - // 12345.6789 G7 12345.68 - // 123456789 G7 1.234568E8 - // 12345.6789 N 12,345.68 - // 123456789 N4 123,456,789.0000 - // 0x2c45e x 2c45e - // 0x2c45e X 2C45E - // 0x2c45e X8 0002C45E - // - // Format strings that do not start with an alphabetic character, or that start - // with an alphabetic character followed by a non-digit, are called - // user-defined format strings. The following table describes the formatting - // characters that are supported in user defined format strings. - // - // - // 0 - Digit placeholder. If the value being - // formatted has a digit in the position where the '0' appears in the format - // string, then that digit is copied to the output string. Otherwise, a '0' is - // stored in that position in the output string. The position of the leftmost - // '0' before the decimal point and the rightmost '0' after the decimal point - // determines the range of digits that are always present in the output - // string. - // - // # - Digit placeholder. If the value being - // formatted has a digit in the position where the '#' appears in the format - // string, then that digit is copied to the output string. Otherwise, nothing - // is stored in that position in the output string. - // - // . - Decimal point. The first '.' character - // in the format string determines the location of the decimal separator in the - // formatted value; any additional '.' characters are ignored. The actual - // character used as a the decimal separator in the output string is given by - // the NumberFormatInfo used to format the number. - // - // , - Thousand separator and number scaling. - // The ',' character serves two purposes. First, if the format string contains - // a ',' character between two digit placeholders (0 or #) and to the left of - // the decimal point if one is present, then the output will have thousand - // separators inserted between each group of three digits to the left of the - // decimal separator. The actual character used as a the decimal separator in - // the output string is given by the NumberFormatInfo used to format the - // number. Second, if the format string contains one or more ',' characters - // immediately to the left of the decimal point, or after the last digit - // placeholder if there is no decimal point, then the number will be divided by - // 1000 times the number of ',' characters before it is formatted. For example, - // the format string '0,,' will represent 100 million as just 100. Use of the - // ',' character to indicate scaling does not also cause the formatted number - // to have thousand separators. Thus, to scale a number by 1 million and insert - // thousand separators you would use the format string '#,##0,,'. - // - // % - Percentage placeholder. The presence of - // a '%' character in the format string causes the number to be multiplied by - // 100 before it is formatted. The '%' character itself is inserted in the - // output string where it appears in the format string. - // - // E+ E- e+ e- - Scientific notation. - // If any of the strings 'E+', 'E-', 'e+', or 'e-' are present in the format - // string and are immediately followed by at least one '0' character, then the - // number is formatted using scientific notation with an 'E' or 'e' inserted - // between the number and the exponent. The number of '0' characters following - // the scientific notation indicator determines the minimum number of digits to - // output for the exponent. The 'E+' and 'e+' formats indicate that a sign - // character (plus or minus) should always precede the exponent. The 'E-' and - // 'e-' formats indicate that a sign character should only precede negative - // exponents. - // - // \ - Literal character. A backslash character - // causes the next character in the format string to be copied to the output - // string as-is. The backslash itself isn't copied, so to place a backslash - // character in the output string, use two backslashes (\\) in the format - // string. - // - // 'ABC' "ABC" - Literal string. Characters - // enclosed in single or double quotation marks are copied to the output string - // as-is and do not affect formatting. - // - // ; - Section separator. The ';' character is - // used to separate sections for positive, negative, and zero numbers in the - // format string. - // - // Other - All other characters are copied to - // the output string in the position they appear. - // - // For fixed point formats (formats not containing an 'E+', 'E-', 'e+', or - // 'e-'), the number is rounded to as many decimal places as there are digit - // placeholders to the right of the decimal point. If the format string does - // not contain a decimal point, the number is rounded to the nearest - // integer. If the number has more digits than there are digit placeholders to - // the left of the decimal point, the extra digits are copied to the output - // string immediately before the first digit placeholder. - // - // For scientific formats, the number is rounded to as many significant digits - // as there are digit placeholders in the format string. - // - // To allow for different formatting of positive, negative, and zero values, a - // user-defined format string may contain up to three sections separated by - // semicolons. The results of having one, two, or three sections in the format - // string are described in the table below. - // - // Sections: - // - // One - The format string applies to all values. - // - // Two - The first section applies to positive values - // and zeros, and the second section applies to negative values. If the number - // to be formatted is negative, but becomes zero after rounding according to - // the format in the second section, then the resulting zero is formatted - // according to the first section. - // - // Three - The first section applies to positive - // values, the second section applies to negative values, and the third section - // applies to zeros. The second section may be left empty (by having no - // characters between the semicolons), in which case the first section applies - // to all non-zero values. If the number to be formatted is non-zero, but - // becomes zero after rounding according to the format in the first or second - // section, then the resulting zero is formatted according to the third - // section. - // - // For both standard and user-defined formatting operations on values of type - // float and double, if the value being formatted is a NaN (Not - // a Number) or a positive or negative infinity, then regardless of the format - // string, the resulting string is given by the NaNSymbol, - // PositiveInfinitySymbol, or NegativeInfinitySymbol property of - // the NumberFormatInfo used to format the number. - // - // Parsing - // - // The Parse methods provided by the numeric classes are all of the form - // - // public static XXX Parse(String s); - // public static XXX Parse(String s, int style); - // public static XXX Parse(String s, int style, NumberFormatInfo info); - // - // where XXX is the name of the particular numeric class. The methods convert a - // string to a numeric value. The optional style parameter specifies the - // permitted style of the numeric string. It must be a combination of bit flags - // from the NumberStyles enumeration. The optional info parameter - // specifies the NumberFormatInfo instance to use when parsing the - // string. If the info parameter is null or omitted, the numeric - // formatting information is obtained from the current culture. - // - // Numeric strings produced by the Format methods using the Currency, - // Decimal, Engineering, Fixed point, General, or Number standard formats - // (the C, D, E, F, G, and N format specifiers) are guaranteed to be parseable - // by the Parse methods if the NumberStyles.Any style is - // specified. Note, however, that the Parse methods do not accept - // NaNs or Infinities. - // - // This class contains only static members and does not need to be serializable - - private partial class Number - { - private Number() { } - - // Constants used by number parsing - private const int NumberMaxDigits = 32; - - internal const int DECIMAL_PRECISION = 29; // Decimal.DecCalc also uses this value - - private const int MIN_SB_BUFFER_SIZE = 105; - - private static bool IsWhite(char ch) - { - return (((ch) == 0x20) || ((ch) >= 0x09 && (ch) <= 0x0D)); - } - - private static unsafe char* MatchChars(char* p, string str) - { - fixed (char* stringPointer = str) - { - return MatchChars(p, stringPointer); - } - } - - private static unsafe char* MatchChars(char* p, char* str) - { - Debug.Assert(p != null && str != null); - - if (*str == '\0') - { - return null; - } - - // We only hurt the failure case - // This fix is for French or Kazakh cultures. Since a user cannot type 0xA0 as a - // space character we use 0x20 space character instead to mean the same. - while (*p == *str || (*str == '\u00a0' && *p == '\u0020')) - { - p++; - str++; - if (*str == '\0') return p; - } - return null; - } - - private static unsafe bool ParseNumber(ref char* str, NumberStyles options, ref NumberBuffer number, StringBuilder sb, NumberFormatInfo numfmt, bool parseDecimal) - { - const int StateSign = 0x0001; - const int StateParens = 0x0002; - const int StateDigits = 0x0004; - const int StateNonZero = 0x0008; - const int StateDecimal = 0x0010; - const int StateCurrency = 0x0020; - - number.scale = 0; - number.sign = false; - string decSep; // Decimal separator from NumberFormatInfo. - string groupSep; // Group separator from NumberFormatInfo. - string currSymbol = null; // Currency symbol from NumberFormatInfo. - - bool parsingCurrency = false; - if ((options & NumberStyles.AllowCurrencySymbol) != 0) - { - currSymbol = numfmt.CurrencySymbol; - // The idea here is to match the currency separators and on failure match the number separators to keep the perf of VB's IsNumeric fast. - // The values of decSep are setup to use the correct relevant separator (currency in the if part and decimal in the else part). - decSep = numfmt.CurrencyDecimalSeparator; - groupSep = numfmt.CurrencyGroupSeparator; - parsingCurrency = true; - } - else - { - decSep = numfmt.NumberDecimalSeparator; - groupSep = numfmt.NumberGroupSeparator; - } - - int state = 0; - bool bigNumber = (sb != null); // When a StringBuilder is provided then we use it in place of the number.digits char[50] - int maxParseDigits = bigNumber ? int.MaxValue : NumberMaxDigits; - - char* p = str; - char ch = *p; - char* next; - - char* dig = number.digits; - - while (true) - { - // Eat whitespace unless we've found a sign which isn't followed by a currency symbol. - // "-Kr 1231.47" is legal but "- 1231.47" is not. - if (!IsWhite(ch) || (options & NumberStyles.AllowLeadingWhite) == 0 || ((state & StateSign) != 0 && ((state & StateCurrency) == 0 && numfmt.NumberNegativePattern != 2))) - { - if ((((options & NumberStyles.AllowLeadingSign) != 0) && (state & StateSign) == 0) && ((next = MatchChars(p, numfmt.PositiveSign)) != null || ((next = MatchChars(p, numfmt.NegativeSign)) != null && (number.sign = true)))) - { - state |= StateSign; - p = next - 1; - } - else if (ch == '(' && ((options & NumberStyles.AllowParentheses) != 0) && ((state & StateSign) == 0)) - { - state |= StateSign | StateParens; - number.sign = true; - } - else if (currSymbol != null && (next = MatchChars(p, currSymbol)) != null) - { - state |= StateCurrency; - currSymbol = null; - - // We already found the currency symbol. There should not be more currency symbols. Set - // currSymbol to NULL so that we won't search it again in the later code path. - p = next - 1; - } - else - { - break; - } - } - ch = *++p; - } - - int digCount = 0; - int digEnd = 0; - while (true) - { - if ((ch >= '0' && ch <= '9') || (((options & NumberStyles.AllowHexSpecifier) != 0) && ((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')))) - { - state |= StateDigits; - - if (ch != '0' || (state & StateNonZero) != 0 || (bigNumber && ((options & NumberStyles.AllowHexSpecifier) != 0))) - { - if (digCount < maxParseDigits) - { - if (bigNumber) - sb.Append(ch); - else - dig[digCount++] = ch; - if (ch != '0' || parseDecimal) - { - digEnd = digCount; - } - } - if ((state & StateDecimal) == 0) - { - number.scale++; - } - state |= StateNonZero; - } - else if ((state & StateDecimal) != 0) - { - number.scale--; - } - } - else if (((options & NumberStyles.AllowDecimalPoint) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, decSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, numfmt.NumberDecimalSeparator)) != null)) - { - state |= StateDecimal; - p = next - 1; - } - else if (((options & NumberStyles.AllowThousands) != 0) && ((state & StateDigits) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, groupSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, numfmt.NumberGroupSeparator)) != null)) - { - p = next - 1; - } - else - { - break; - } - ch = *++p; - } - - bool negExp = false; - number.precision = digEnd; - if (bigNumber) - sb.Append('\0'); - else - dig[digEnd] = '\0'; - if ((state & StateDigits) != 0) - { - if ((ch == 'E' || ch == 'e') && ((options & NumberStyles.AllowExponent) != 0)) - { - char* temp = p; - ch = *++p; - if ((next = MatchChars(p, numfmt.PositiveSign)) != null) - { - ch = *(p = next); - } - else if ((next = MatchChars(p, numfmt.NegativeSign)) != null) - { - ch = *(p = next); - negExp = true; - } - if (ch >= '0' && ch <= '9') - { - int exp = 0; - do - { - exp = exp * 10 + (ch - '0'); - ch = *++p; - if (exp > 1000) - { - exp = 9999; - while (ch >= '0' && ch <= '9') - { - ch = *++p; - } - } - } while (ch >= '0' && ch <= '9'); - if (negExp) - { - exp = -exp; - } - number.scale += exp; - } - else - { - p = temp; - ch = *p; - } - } - while (true) - { - if (!IsWhite(ch) || (options & NumberStyles.AllowTrailingWhite) == 0) - { - if (((options & NumberStyles.AllowTrailingSign) != 0 && ((state & StateSign) == 0)) && ((next = MatchChars(p, numfmt.PositiveSign)) != null || (((next = MatchChars(p, numfmt.NegativeSign)) != null) && (number.sign = true)))) - { - state |= StateSign; - p = next - 1; - } - else if (ch == ')' && ((state & StateParens) != 0)) - { - state &= ~StateParens; - } - else if (currSymbol != null && (next = MatchChars(p, currSymbol)) != null) - { - currSymbol = null; - p = next - 1; - } - else - { - break; - } - } - ch = *++p; - } - if ((state & StateParens) == 0) - { - if ((state & StateNonZero) == 0) - { - if (!parseDecimal) - { - number.scale = 0; - } - if ((state & StateDecimal) == 0) - { - number.sign = false; - } - } - str = p; - return true; - } - } - str = p; - return false; - } - - private static bool TrailingZeros(string s, int index) - { - // For compatibility, we need to allow trailing zeros at the end of a number string - for (int i = index; i < s.Length; i++) - { - if (s[i] != '\0') - { - return false; - } - } - return true; - } - - internal static unsafe bool TryStringToNumber(string str, NumberStyles options, ref NumberBuffer number, StringBuilder sb, NumberFormatInfo numfmt, bool parseDecimal) - { - if (str == null) - { - return false; - } - Debug.Assert(numfmt != null); - - fixed (char* stringPointer = str) - { - char* p = stringPointer; - if (!ParseNumber(ref p, options, ref number, sb, numfmt, parseDecimal) - || (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer)))) - { - return false; - } - } - - return true; - } - - // ********************************************************************************************************** - // - // The remaining code in this module is an almost direct translation from the original unmanaged version in - // the CLR. The code uses NumberBuffer directly instead of an analog of the NUMBER unmanaged data structure - // but this causes next to no differences since we've modified NumberBuffer to take account of the changes (it - // has an inline array of digits and no need of a pack operation to prepare for use by the "unmanaged" code). - // - // Some minor cleanup has been done (e.g. taking advantage of StringBuilder instead of having to precompute - // string buffer sizes) but there's still plenty of opportunity to further C#'ize this code and potentially - // better unify it with the code above. - // - - private static string[] s_posCurrencyFormats = - { - "$#", "#$", "$ #", "# $" - }; - - private static string[] s_negCurrencyFormats = - { - "($#)", "-$#", "$-#", "$#-", - "(#$)", "-#$", "#-$", "#$-", - "-# $", "-$ #", "# $-", "$ #-", - "$ -#", "#- $", "($ #)", "(# $)" - }; - - private static string[] s_posPercentFormats = - { - "# %", "#%", "%#", "% #" - }; - - private static string[] s_negPercentFormats = - { - "-# %", "-#%", "-%#", - "%-#", "%#-", - "#-%", "#%-", - "-% #", "# %-", "% #-", - "% -#", "#- %" - }; - - private static string[] s_negNumberFormats = - { - "(#)", "-#", "- #", "#-", "# -", - }; - - private static string s_posNumberFormat = "#"; - - internal static unsafe void Int32ToDecChars(char* buffer, ref int index, uint value, int digits) - { - while (--digits >= 0 || value != 0) - { - buffer[--index] = (char)(value % 10 + '0'); - value /= 10; - } - } - - internal static unsafe char ParseFormatSpecifier(string format, out int digits) - { - if (format != null) - { - fixed (char* pFormat = format) - { - int i = 0; - char ch = pFormat[i]; - if (ch != 0) - { - if (((ch >= 'A') && (ch <= 'Z')) || ((ch >= 'a') && (ch <= 'z'))) - { - i++; - int n = -1; - if ((pFormat[i] >= '0') && (pFormat[i] <= '9')) - { - n = pFormat[i++] - '0'; - while ((pFormat[i] >= '0') && (pFormat[i] <= '9')) - { - n = (n * 10) + pFormat[i++] - '0'; - if (n >= 10) - break; - } - } - if (pFormat[i] == 0) - { - digits = n; - return ch; - } - } - - digits = -1; - return '\0'; - } - } - } - - digits = -1; - return 'G'; - } - - internal static unsafe string NumberToString(NumberBuffer number, char format, int nMaxDigits, NumberFormatInfo info, bool isDecimal) - { - int nMinDigits = -1; - - StringBuilder sb = new StringBuilder(MIN_SB_BUFFER_SIZE); - - switch (format) - { - case 'C': - case 'c': - { - nMinDigits = nMaxDigits >= 0 ? nMaxDigits : info.CurrencyDecimalDigits; - if (nMaxDigits < 0) - nMaxDigits = info.CurrencyDecimalDigits; - - RoundNumber(ref number, number.scale + nMaxDigits); // Don't change this line to use digPos since digCount could have its sign changed. - - FormatCurrency(sb, number, nMinDigits, nMaxDigits, info); - - break; - } - - case 'F': - case 'f': - { - if (nMaxDigits < 0) - nMaxDigits = nMinDigits = info.NumberDecimalDigits; - else - nMinDigits = nMaxDigits; - - RoundNumber(ref number, number.scale + nMaxDigits); - - if (number.sign) - sb.Append(info.NegativeSign); - - FormatFixed(sb, number, nMinDigits, nMaxDigits, info, null, info.NumberDecimalSeparator, null); - - break; - } - - case 'N': - case 'n': - { - if (nMaxDigits < 0) - nMaxDigits = nMinDigits = info.NumberDecimalDigits; // Since we are using digits in our calculation - else - nMinDigits = nMaxDigits; - - RoundNumber(ref number, number.scale + nMaxDigits); - - FormatNumber(sb, number, nMinDigits, nMaxDigits, info); - - break; - } - - case 'E': - case 'e': - { - if (nMaxDigits < 0) - nMaxDigits = nMinDigits = 6; - else - nMinDigits = nMaxDigits; - nMaxDigits++; - - RoundNumber(ref number, nMaxDigits); - - if (number.sign) - sb.Append(info.NegativeSign); - - FormatScientific(sb, number, nMinDigits, nMaxDigits, info, format); - - break; - } - - case 'G': - case 'g': - { - bool enableRounding = true; - if (nMaxDigits < 1) - { - if (isDecimal && (nMaxDigits == -1)) - { - // Default to 29 digits precision only for G formatting without a precision specifier - // This ensures that the PAL code pads out to the correct place even when we use the default precision - nMaxDigits = nMinDigits = DECIMAL_PRECISION; - enableRounding = false; // Turn off rounding for ECMA compliance to output trailing 0's after decimal as significant - } - else - { - // This ensures that the PAL code pads out to the correct place even when we use the default precision - nMaxDigits = nMinDigits = number.precision; - } - } - else - nMinDigits = nMaxDigits; - - if (enableRounding) // Don't round for G formatting without precision - RoundNumber(ref number, nMaxDigits); // This also fixes up the minus zero case - else - { - if (isDecimal && (number.digits[0] == 0)) - { - // Minus zero should be formatted as 0 - number.sign = false; - } - } - - if (number.sign) - sb.Append(info.NegativeSign); - - FormatGeneral(sb, number, nMinDigits, nMaxDigits, info, (char)(format - ('G' - 'E')), !enableRounding); - - break; - } - - case 'P': - case 'p': - { - if (nMaxDigits < 0) - nMaxDigits = nMinDigits = info.PercentDecimalDigits; - else - nMinDigits = nMaxDigits; - number.scale += 2; - - RoundNumber(ref number, number.scale + nMaxDigits); - - FormatPercent(sb, number, nMinDigits, nMaxDigits, info); - - break; - } - - default: - throw new FormatException(SR.Argument_BadFormatSpecifier); - } - - return sb.ToString(); - } - - private static void FormatCurrency(StringBuilder sb, NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info) - { - string fmt = number.sign ? - s_negCurrencyFormats[info.CurrencyNegativePattern] : - s_posCurrencyFormats[info.CurrencyPositivePattern]; - - foreach (char ch in fmt) - { - switch (ch) - { - case '#': - FormatFixed(sb, number, nMinDigits, nMaxDigits, info, info.CurrencyGroupSizes, info.CurrencyDecimalSeparator, info.CurrencyGroupSeparator); - break; - case '-': - sb.Append(info.NegativeSign); - break; - case '$': - sb.Append(info.CurrencySymbol); - break; - default: - sb.Append(ch); - break; - } - } - } - - private static unsafe int wcslen(char* s) - { - int result = 0; - while (*s++ != '\0') - result++; - return result; - } - - private static unsafe void FormatFixed(StringBuilder sb, NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, int[] groupDigits, string sDecimal, string sGroup) - { - int digPos = number.scale; - char* dig = number.digits; - int digLength = wcslen(dig); - - if (digPos > 0) - { - if (groupDigits != null) - { - int groupSizeIndex = 0; // Index into the groupDigits array. - int groupSizeCount = groupDigits[groupSizeIndex]; // The current total of group size. - int groupSizeLen = groupDigits.Length; // The length of groupDigits array. - int bufferSize = digPos; // The length of the result buffer string. - int groupSeparatorLen = sGroup.Length; // The length of the group separator string. - int groupSize = 0; // The current group size. - - // Find out the size of the string buffer for the result. - if (groupSizeLen != 0) // You can pass in 0 length arrays - { - while (digPos > groupSizeCount) - { - groupSize = groupDigits[groupSizeIndex]; - if (groupSize == 0) - break; - - bufferSize += groupSeparatorLen; - if (groupSizeIndex < groupSizeLen - 1) - groupSizeIndex++; - - groupSizeCount += groupDigits[groupSizeIndex]; - if (groupSizeCount < 0 || bufferSize < 0) - throw new ArgumentOutOfRangeException(); // If we overflow - } - if (groupSizeCount == 0) // If you passed in an array with one entry as 0, groupSizeCount == 0 - groupSize = 0; - else - groupSize = groupDigits[0]; - } - - char* tmpBuffer = stackalloc char[bufferSize]; - groupSizeIndex = 0; - int digitCount = 0; - int digStart; - digStart = (digPos < digLength) ? digPos : digLength; - char* p = tmpBuffer + bufferSize - 1; - for (int i = digPos - 1; i >= 0; i--) - { - *(p--) = (i < digStart) ? dig[i] : '0'; - - if (groupSize > 0) - { - digitCount++; - if ((digitCount == groupSize) && (i != 0)) - { - for (int j = groupSeparatorLen - 1; j >= 0; j--) - *(p--) = sGroup[j]; - - if (groupSizeIndex < groupSizeLen - 1) - { - groupSizeIndex++; - groupSize = groupDigits[groupSizeIndex]; - } - digitCount = 0; - } - } - } - - sb.Append(tmpBuffer, bufferSize); - dig += digStart; - } - else - { - int digits = Math.Min(digLength, digPos); - sb.Append(dig, digits); - dig += digits; - if (digPos > digLength) - sb.Append('0', digPos - digLength); - } - } - else - { - sb.Append('0'); - } - - if (nMaxDigits > 0) - { - sb.Append(sDecimal); - if ((digPos < 0) && (nMaxDigits > 0)) - { - int zeroes = Math.Min(-digPos, nMaxDigits); - sb.Append('0', zeroes); - digPos += zeroes; - nMaxDigits -= zeroes; - } - - while (nMaxDigits > 0) - { - sb.Append((*dig != 0) ? *dig++ : '0'); - nMaxDigits--; - } - } - } - - private static void FormatNumber(StringBuilder sb, NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info) - { - string fmt = number.sign ? - s_negNumberFormats[info.NumberNegativePattern] : - s_posNumberFormat; - - foreach (char ch in fmt) - { - switch (ch) - { - case '#': - FormatFixed(sb, number, nMinDigits, nMaxDigits, info, info.NumberGroupSizes, info.NumberDecimalSeparator, info.NumberGroupSeparator); - break; - case '-': - sb.Append(info.NegativeSign); - break; - default: - sb.Append(ch); - break; - } - } - } - - private static unsafe void FormatScientific(StringBuilder sb, NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, char expChar) - { - char* dig = number.digits; - - sb.Append((*dig != 0) ? *dig++ : '0'); - - if (nMaxDigits != 1) // For E0 we would like to suppress the decimal point - sb.Append(info.NumberDecimalSeparator); - - while (--nMaxDigits > 0) - sb.Append((*dig != 0) ? *dig++ : '0'); - - int e = number.digits[0] == 0 ? 0 : number.scale - 1; - FormatExponent(sb, info, e, expChar, 3, true); - } - - private static unsafe void FormatExponent(StringBuilder sb, NumberFormatInfo info, int value, char expChar, int minDigits, bool positiveSign) - { - sb.Append(expChar); - - if (value < 0) - { - sb.Append(info.NegativeSign); - value = -value; - } - else - { - if (positiveSign) - sb.Append(info.PositiveSign); - } - - char* digits = stackalloc char[11]; - int index = 10; - Int32ToDecChars(digits, ref index, (uint)value, minDigits); - int i = 10 - index; - while (--i >= 0) - sb.Append(digits[index++]); - } - - private static unsafe void FormatGeneral(StringBuilder sb, NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, char expChar, bool bSuppressScientific) - { - int digPos = number.scale; - bool scientific = false; - - if (!bSuppressScientific) - { - // Don't switch to scientific notation - if (digPos > nMaxDigits || digPos < -3) - { - digPos = 1; - scientific = true; - } - } - - char* dig = number.digits; - - if (digPos > 0) - { - do - { - sb.Append((*dig != 0) ? *dig++ : '0'); - } while (--digPos > 0); - } - else - { - sb.Append('0'); - } - - if (*dig != 0 || digPos < 0) - { - sb.Append(info.NumberDecimalSeparator); - - while (digPos < 0) - { - sb.Append('0'); - digPos++; - } - - while (*dig != 0) - sb.Append(*dig++); - } - - if (scientific) - FormatExponent(sb, info, number.scale - 1, expChar, 2, true); - } - - private static void FormatPercent(StringBuilder sb, NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info) - { - string fmt = number.sign ? - s_negPercentFormats[info.PercentNegativePattern] : - s_posPercentFormats[info.PercentPositivePattern]; - - foreach (char ch in fmt) - { - switch (ch) - { - case '#': - FormatFixed(sb, number, nMinDigits, nMaxDigits, info, info.PercentGroupSizes, info.PercentDecimalSeparator, info.PercentGroupSeparator); - break; - case '-': - sb.Append(info.NegativeSign); - break; - case '%': - sb.Append(info.PercentSymbol); - break; - default: - sb.Append(ch); - break; - } - } - } - - private static unsafe void RoundNumber(ref NumberBuffer number, int pos) - { - char* dig = number.digits; - - int i = 0; - while (i < pos && dig[i] != 0) - i++; - - if (i == pos && dig[i] >= '5') - { - while (i > 0 && dig[i - 1] == '9') - i--; - - if (i > 0) - { - dig[i - 1]++; - } - else - { - number.scale++; - dig[0] = '1'; - i = 1; - } - } - else - { - while (i > 0 && dig[i - 1] == '0') - i--; - } - if (i == 0) - { - number.scale = 0; - number.sign = false; - } - dig[i] = '\0'; - } - - private static unsafe int FindSection(string format, int section) - { - int src; - char ch; - - if (section == 0) - return 0; - - fixed (char* pFormat = format) - { - src = 0; - for (;;) - { - switch (ch = pFormat[src++]) - { - case '\'': - case '"': - while (pFormat[src] != 0 && pFormat[src++] != ch) - ; - break; - case '\\': - if (pFormat[src] != 0) - src++; - break; - case ';': - if (--section != 0) - break; - if (pFormat[src] != 0 && pFormat[src] != ';') - return src; - goto case '\0'; - case '\0': - return 0; - } - } - } - } - - internal static unsafe string NumberToStringFormat(NumberBuffer number, string format, NumberFormatInfo info) - { - int digitCount; - int decimalPos; - int firstDigit; - int lastDigit; - int digPos; - bool scientific; - int thousandPos; - int thousandCount = 0; - bool thousandSeps; - int scaleAdjust; - int adjust; - - int section; - int src; - char* dig = number.digits; - char ch; - - section = FindSection(format, dig[0] == 0 ? 2 : number.sign ? 1 : 0); - - while (true) - { - digitCount = 0; - decimalPos = -1; - firstDigit = 0x7FFFFFFF; - lastDigit = 0; - scientific = false; - thousandPos = -1; - thousandSeps = false; - scaleAdjust = 0; - src = section; - - fixed (char* pFormat = format) - { - while ((ch = pFormat[src++]) != 0 && ch != ';') - { - switch (ch) - { - case '#': - digitCount++; - break; - case '0': - if (firstDigit == 0x7FFFFFFF) - firstDigit = digitCount; - digitCount++; - lastDigit = digitCount; - break; - case '.': - if (decimalPos < 0) - decimalPos = digitCount; - break; - case ',': - if (digitCount > 0 && decimalPos < 0) - { - if (thousandPos >= 0) - { - if (thousandPos == digitCount) - { - thousandCount++; - break; - } - thousandSeps = true; - } - thousandPos = digitCount; - thousandCount = 1; - } - break; - case '%': - scaleAdjust += 2; - break; - case '\x2030': - scaleAdjust += 3; - break; - case '\'': - case '"': - while (pFormat[src] != 0 && pFormat[src++] != ch) - ; - break; - case '\\': - if (pFormat[src] != 0) - src++; - break; - case 'E': - case 'e': - if (pFormat[src] == '0' || ((pFormat[src] == '+' || pFormat[src] == '-') && pFormat[src + 1] == '0')) - { - while (pFormat[++src] == '0') - ; - scientific = true; - } - break; - } - } - } - - if (decimalPos < 0) - decimalPos = digitCount; - - if (thousandPos >= 0) - { - if (thousandPos == decimalPos) - scaleAdjust -= thousandCount * 3; - else - thousandSeps = true; - } - - if (dig[0] != 0) - { - number.scale += scaleAdjust; - int pos = scientific ? digitCount : number.scale + digitCount - decimalPos; - RoundNumber(ref number, pos); - if (dig[0] == 0) - { - src = FindSection(format, 2); - if (src != section) - { - section = src; - continue; - } - } - } - else - { - number.sign = false; // We need to format -0 without the sign set. - number.scale = 0; // Decimals with scale ('0.00') should be rounded. - } - - break; - } - - firstDigit = firstDigit < decimalPos ? decimalPos - firstDigit : 0; - lastDigit = lastDigit > decimalPos ? decimalPos - lastDigit : 0; - if (scientific) - { - digPos = decimalPos; - adjust = 0; - } - else - { - digPos = number.scale > decimalPos ? number.scale : decimalPos; - adjust = number.scale - decimalPos; - } - src = section; - - // Adjust can be negative, so we make this an int instead of an unsigned int. - // Adjust represents the number of characters over the formatting e.g. format string is "0000" and you are trying to - // format 100000 (6 digits). Means adjust will be 2. On the other hand if you are trying to format 10 adjust will be - // -2 and we'll need to fixup these digits with 0 padding if we have 0 formatting as in this example. - int[] thousandsSepPos = new int[4]; - int thousandsSepCtr = -1; - - if (thousandSeps) - { - // We need to precompute this outside the number formatting loop - if (info.NumberGroupSeparator.Length > 0) - { - // We need this array to figure out where to insert the thousands separator. We would have to traverse the string - // backwards. PIC formatting always traverses forwards. These indices are precomputed to tell us where to insert - // the thousands separator so we can get away with traversing forwards. Note we only have to compute up to digPos. - // The max is not bound since you can have formatting strings of the form "000,000..", and this - // should handle that case too. - - int[] groupDigits = info.NumberGroupSizes; - - int groupSizeIndex = 0; // Index into the groupDigits array. - int groupTotalSizeCount = 0; - int groupSizeLen = groupDigits.Length; // The length of groupDigits array. - if (groupSizeLen != 0) - groupTotalSizeCount = groupDigits[groupSizeIndex]; // The current running total of group size. - int groupSize = groupTotalSizeCount; - - int totalDigits = digPos + ((adjust < 0) ? adjust : 0); // Actual number of digits in o/p - int numDigits = (firstDigit > totalDigits) ? firstDigit : totalDigits; - while (numDigits > groupTotalSizeCount) - { - if (groupSize == 0) - break; - ++thousandsSepCtr; - if (thousandsSepCtr >= thousandsSepPos.Length) - Array.Resize(ref thousandsSepPos, thousandsSepPos.Length * 2); - - thousandsSepPos[thousandsSepCtr] = groupTotalSizeCount; - if (groupSizeIndex < groupSizeLen - 1) - { - groupSizeIndex++; - groupSize = groupDigits[groupSizeIndex]; - } - groupTotalSizeCount += groupSize; - } - } - } - - StringBuilder sb = new StringBuilder(MIN_SB_BUFFER_SIZE); - - if (number.sign && section == 0) - sb.Append(info.NegativeSign); - - bool decimalWritten = false; - - fixed (char* pFormat = format) - { - char* cur = dig; - - while ((ch = pFormat[src++]) != 0 && ch != ';') - { - if (adjust > 0) - { - switch (ch) - { - case '#': - case '0': - case '.': - while (adjust > 0) - { - // digPos will be one greater than thousandsSepPos[thousandsSepCtr] since we are at - // the character after which the groupSeparator needs to be appended. - sb.Append(*cur != 0 ? *cur++ : '0'); - if (thousandSeps && digPos > 1 && thousandsSepCtr >= 0) - { - if (digPos == thousandsSepPos[thousandsSepCtr] + 1) - { - sb.Append(info.NumberGroupSeparator); - thousandsSepCtr--; - } - } - digPos--; - adjust--; - } - break; - } - } - - switch (ch) - { - case '#': - case '0': - { - if (adjust < 0) - { - adjust++; - ch = digPos <= firstDigit ? '0' : '\0'; - } - else - { - ch = *cur != 0 ? *cur++ : digPos > lastDigit ? '0' : '\0'; - } - if (ch != 0) - { - sb.Append(ch); - if (thousandSeps && digPos > 1 && thousandsSepCtr >= 0) - { - if (digPos == thousandsSepPos[thousandsSepCtr] + 1) - { - sb.Append(info.NumberGroupSeparator); - thousandsSepCtr--; - } - } - } - - digPos--; - break; - } - case '.': - { - if (digPos != 0 || decimalWritten) - { - // For compatibility, don't echo repeated decimals - break; - } - // If the format has trailing zeros or the format has a decimal and digits remain - if (lastDigit < 0 || (decimalPos < digitCount && *cur != 0)) - { - sb.Append(info.NumberDecimalSeparator); - decimalWritten = true; - } - break; - } - case '\x2030': - sb.Append(info.PerMilleSymbol); - break; - case '%': - sb.Append(info.PercentSymbol); - break; - case ',': - break; - case '\'': - case '"': - while (pFormat[src] != 0 && pFormat[src] != ch) - sb.Append(pFormat[src++]); - if (pFormat[src] != 0) - src++; - break; - case '\\': - if (pFormat[src] != 0) - sb.Append(pFormat[src++]); - break; - case 'E': - case 'e': - { - bool positiveSign = false; - int i = 0; - if (scientific) - { - if (pFormat[src] == '0') - { - // Handles E0, which should format the same as E-0 - i++; - } - else if (pFormat[src] == '+' && pFormat[src + 1] == '0') - { - // Handles E+0 - positiveSign = true; - } - else if (pFormat[src] == '-' && pFormat[src + 1] == '0') - { - // Handles E-0 - // Do nothing, this is just a place holder s.t. we don't break out of the loop. - } - else - { - sb.Append(ch); - break; - } - - while (pFormat[++src] == '0') - i++; - if (i > 10) - i = 10; - - int exp = dig[0] == 0 ? 0 : number.scale - decimalPos; - FormatExponent(sb, info, exp, ch, i, positiveSign); - scientific = false; - } - else - { - sb.Append(ch); // Copy E or e to output - if (pFormat[src] == '+' || pFormat[src] == '-') - sb.Append(pFormat[src++]); - while (pFormat[src] == '0') - sb.Append(pFormat[src++]); - } - break; - } - default: - sb.Append(ch); - break; - } - } - } - - return sb.ToString(); - } - } - } -} diff --git a/external/corert/src/Common/src/System/NotImplemented.cs b/external/corert/src/Common/src/System/NotImplemented.cs index d401a11baa..32771643e2 100644 --- a/external/corert/src/Common/src/System/NotImplemented.cs +++ b/external/corert/src/Common/src/System/NotImplemented.cs @@ -5,18 +5,13 @@ namespace System { // - // This simple class enables one to throw a NotImplementedException using the following - // idiom: - // - // throw NotImplemented.ByDesign; - // - // Used by methods whose intended implementation is to throw a NotImplementedException (typically - // virtual methods in public abstract classes that intended to be subclassed by third parties.) - // - // This makes it distinguishable both from human eyes and CCI from NYI's that truly represent undone work. + // Support for tooling-friendly NotImplementedExceptions. // internal static class NotImplemented { + /// + /// Permanent NotImplementedException with no message shown to user. + /// internal static Exception ByDesign { get @@ -25,10 +20,22 @@ namespace System } } - internal static Exception ByDesignWithMessage(String message) + /// + /// Permanent NotImplementedException with localized message shown to user. + /// + internal static Exception ByDesignWithMessage(string message) { return new NotImplementedException(message); } + + /// + /// Temporary NotImplementedException with no message shown to user. + /// Example: Exception.ActiveIssue("https://github.com/dotnet/corert/issues/xxxx") or Exception.ActiveIssue("TFS xxxxxx"). + /// + internal static Exception ActiveIssue(string issue) + { + return new NotImplementedException(); + } } } diff --git a/external/corert/src/Common/src/System/Runtime/CompilerServices/__BlockAllReflectionAttribute.cs b/external/corert/src/Common/src/System/Runtime/CompilerServices/__BlockAllReflectionAttribute.cs new file mode 100644 index 0000000000..41a5305fec --- /dev/null +++ b/external/corert/src/Common/src/System/Runtime/CompilerServices/__BlockAllReflectionAttribute.cs @@ -0,0 +1,17 @@ +// 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. + +/* + Providing a definition for __BlockAllReflectionAttribute in an assembly is a signal to the .NET Native toolchain + to remove the metadata for all APIs. This both reduces size and disables all reflection on those + APIs in libraries that include this. +*/ + +using System; + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.All)] + internal class __BlockAllReflectionAttribute : Attribute { } +} diff --git a/external/corert/src/Common/src/System/SR.Core.cs b/external/corert/src/Common/src/System/SR.Core.cs deleted file mode 100644 index ab042a5cf2..0000000000 --- a/external/corert/src/Common/src/System/SR.Core.cs +++ /dev/null @@ -1,137 +0,0 @@ -// 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. -// -// This is a standin for the SR class used throughout FX. -// - -#if ENABLE_WINRT -using Internal.Runtime.Augments; -#endif // ENABLE_WINRT -using System.Runtime.CompilerServices; -using System.Text; - -namespace System -{ - internal static partial class SR - { - // - // System.Private.CoreLib and Reflection assemblies cannot depend on System.Resources so we'll call Windows Runtime ResourceManager - // directly using Internal.Runtime.Augments. - // For other assemblies, we use System.Resources.ResourceManager to do the resources lookup. it is important to - // not have such assemblies depend on internal contratcs as we can decide to make these assemblies portable. - // - - private static Object s_resourceMap; - private const string MoreInfoLink = @". For more information, visit http://go.microsoft.com/fwlink/?LinkId=623485"; - - private static Object ResourceMap - { - get - { - if (SR.s_resourceMap == null) - { -#if ENABLE_WINRT - SR.s_resourceMap = WinRTInterop.Callbacks.GetResourceMap(SR.s_resourcesName); -#else - SR.s_resourceMap = new object(); -#endif // ENABLE_WINRT - } - return SR.s_resourceMap; - } - } - - // This method is used to decide if we need to append the exception message parameters to the message when calling SR.Format. - // by default it returns false. We overwrite the implementation of this method to return true through IL transformer - // when compiling ProjectN app as retail build. - [MethodImpl(MethodImplOptions.NoInlining)] - private static bool UsingResourceKeys() - { - return false; - } - - // TODO: Resouce generation tool should be modified to call this version in release build - [MethodImpl(MethodImplOptions.NoInlining)] - internal static string GetResourceString(string resourceKey) - { -#if ENABLE_WINRT - return WinRTInterop.Callbacks.GetResourceString(ResourceMap, resourceKey, null); -#else - return resourceKey; -#endif // ENABLE_WINRT - } - - internal static string GetResourceString(string resourceKey, string defaultString) - { - string resourceString = GetResourceString(resourceKey); - - // if we are running on desktop, GetResourceString will just return resourceKey. so - // in this case we'll return defaultString (if it is not null). - if (defaultString != null && resourceKey.Equals(resourceString)) - { - return defaultString; - } - - if (resourceString == null) - { - // It is not expected to have resourceString is null at this point. - // this means our framework resources is missing while it is expected to be there. - // we have to throw on that or otherwise we’ll eventually get stack overflow exception. - // we have to use hardcode the exception message here as we cannot lookup the resources for other keys. - // We cannot throw MissingManifestResourceException as we cannot depend on the System.Resources here. - - throw new InvalidProgramException("Unable to find resource for the key " + resourceKey + "."); - } - - return resourceString; - } - - internal static string Format(string resourceFormat, params object[] args) - { - if (args != null) - { - if (UsingResourceKeys()) - { - return resourceFormat + String.Join(", ", args) + MoreInfoLink; - } - - return String.Format(resourceFormat, args); - } - - return resourceFormat; - } - - - [MethodImpl(MethodImplOptions.NoInlining)] - internal static string Format(string resourceFormat, object p1) - { - if (UsingResourceKeys()) - { - return String.Join(", ", resourceFormat, p1) + MoreInfoLink; - } - - return String.Format(resourceFormat, p1); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - internal static string Format(string resourceFormat, object p1, object p2) - { - if (UsingResourceKeys()) - { - return String.Join(", ", resourceFormat, p1, p2) + MoreInfoLink; - } - - return String.Format(resourceFormat, p1, p2); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - internal static string Format(string resourceFormat, object p1, object p2, object p3) - { - if (UsingResourceKeys()) - { - return String.Join(", ", resourceFormat, p1, p2, p3) + MoreInfoLink; - } - return String.Format(resourceFormat, p1, p2, p3); - } - } -} diff --git a/external/corert/src/Common/src/System/SR.cs b/external/corert/src/Common/src/System/SR.cs index 9f5dfc636f..30462614f7 100644 --- a/external/corert/src/Common/src/System/SR.cs +++ b/external/corert/src/Common/src/System/SR.cs @@ -2,8 +2,12 @@ // 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.Collections.Generic; +using System.Diagnostics; +using System.IO; using System.Resources; using System.Runtime.CompilerServices; +using System.Threading; namespace System { @@ -40,7 +44,7 @@ namespace System internal static string GetResourceString(string resourceKey, string defaultString) { string resourceString = null; - try { resourceString = ResourceManager.GetString(resourceKey); } + try { resourceString = InternalGetResourceString(resourceKey); } catch (MissingManifestResourceException) { } if (defaultString != null && resourceKey.Equals(resourceString, StringComparison.Ordinal)) @@ -51,6 +55,84 @@ namespace System return resourceString; } + private static object _lock = new object(); + private static List _currentlyLoading; + private static int _infinitelyRecursingCount; + + private static string InternalGetResourceString(string key) + { + if (key == null || key.Length == 0) + { + Debug.Fail("SR::GetResourceString with null or empty key. Bug in caller, or weird recursive loading problem?"); + return key; + } + + // We have a somewhat common potential for infinite + // loops with mscorlib's ResourceManager. If "potentially dangerous" + // code throws an exception, we will get into an infinite loop + // inside the ResourceManager and this "potentially dangerous" code. + // Potentially dangerous code includes the IO package, CultureInfo, + // parts of the loader, some parts of Reflection, Security (including + // custom user-written permissions that may parse an XML file at + // class load time), assembly load event handlers, etc. Essentially, + // this is not a bounded set of code, and we need to fix the problem. + // Fortunately, this is limited to mscorlib's error lookups and is NOT + // a general problem for all user code using the ResourceManager. + + // The solution is to make sure only one thread at a time can call + // GetResourceString. Also, since resource lookups can be + // reentrant, if the same thread comes into GetResourceString + // twice looking for the exact same resource name before + // returning, we're going into an infinite loop and we should + // return a bogus string. + + bool lockTaken = false; + try + { + Monitor.Enter(_lock, ref lockTaken); + + // Are we recursively looking up the same resource? Note - our backout code will set + // the ResourceHelper's currentlyLoading stack to null if an exception occurs. + if (_currentlyLoading != null && _currentlyLoading.Count > 0 && _currentlyLoading.LastIndexOf(key) != -1) + { + // We can start infinitely recursing for one resource lookup, + // then during our failure reporting, start infinitely recursing again. + // avoid that. + if (_infinitelyRecursingCount > 0) + { + return key; + } + _infinitelyRecursingCount++; + } + if (_currentlyLoading == null) + _currentlyLoading = new List(); + + _currentlyLoading.Add(key); // Push + + string s = ResourceManager.GetString(key, null); + _currentlyLoading.RemoveAt(_currentlyLoading.Count - 1); // Pop + + Debug.Assert(s != null, "Managed resource string lookup failed. Was your resource name misspelled? Did you rebuild mscorlib after adding a resource to resources.txt? Debug this w/ cordbg and bug whoever owns the code that called SR.GetResourceString. Resource name was: \"" + key + "\""); + return s ?? key; + } + catch + { + if (lockTaken) + { + // Backout code - throw away potentially corrupt state + _currentlyLoading = null; + } + throw; + } + finally + { + if (lockTaken) + { + Monitor.Exit(_lock); + } + } + } + internal static string Format(string resourceFormat, params object[] args) { if (args != null) diff --git a/external/corert/src/Common/src/System/__HResults.cs b/external/corert/src/Common/src/System/__HResults.cs deleted file mode 100644 index 259c4dcb9a..0000000000 --- a/external/corert/src/Common/src/System/__HResults.cs +++ /dev/null @@ -1,237 +0,0 @@ -// 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. - -//============================================================================= -// -// -// Purpose: Define HResult constants. Every exception has one of these. -// -// -//===========================================================================*/ - -using System; - -namespace System -{ - // Note: FACILITY_URT is defined as 0x13 (0x8013xxxx). Within that - // range, 0x1yyy is for Runtime errors (used for Security, Metadata, etc). - // In that subrange, 0x15zz and 0x16zz have been allocated for classlib-type - // HResults. Also note that some of our HResults have to map to certain - // COM HR's, etc. - - // Another arbitrary decision... Feel free to change this, as long as you - // renumber the HResults yourself (and update rexcep.h). - // Reflection will use 0x1600 -> 0x161f. IO will use 0x1620 -> 0x163f. - // Security will use 0x1640 -> 0x165f - - // There are __HResults files in the IO, Remoting, Reflection & - // Security/Util directories as well, so choose your HResults carefully. - internal static class __HResults - { - internal const int APPMODEL_ERROR_NO_PACKAGE = unchecked((int)0x80073D54); - internal const int CLDB_E_FILE_CORRUPT = unchecked((int)0x8013110e); - internal const int CLDB_E_FILE_OLDVER = unchecked((int)0x80131107); - internal const int CLDB_E_INDEX_NOTFOUND = unchecked((int)0x80131124); - internal const int CLR_E_BIND_ASSEMBLY_NOT_FOUND = unchecked((int)0x80132004); - internal const int CLR_E_BIND_ASSEMBLY_PUBLIC_KEY_MISMATCH = unchecked((int)0x80132001); - internal const int CLR_E_BIND_ASSEMBLY_VERSION_TOO_LOW = unchecked((int)0x80132000); - internal const int CLR_E_BIND_TYPE_NOT_FOUND = unchecked((int)0x80132005); - internal const int CLR_E_BIND_UNRECOGNIZED_IDENTITY_FORMAT = unchecked((int)0x80132003); - internal const int COR_E_ABANDONEDMUTEX = unchecked((int)0x8013152D); - internal const int COR_E_AMBIGUOUSMATCH = unchecked((int)0x8000211D); - internal const int COR_E_APPDOMAINUNLOADED = unchecked((int)0x80131014); - internal const int COR_E_APPLICATION = unchecked((int)0x80131600); - internal const int COR_E_ARGUMENT = unchecked((int)0x80070057); - internal const int COR_E_ARGUMENTOUTOFRANGE = unchecked((int)0x80131502); - internal const int COR_E_ARITHMETIC = unchecked((int)0x80070216); - internal const int COR_E_ARRAYTYPEMISMATCH = unchecked((int)0x80131503); - internal const int COR_E_ASSEMBLYEXPECTED = unchecked((int)0x80131018); - internal const int COR_E_BADIMAGEFORMAT = unchecked((int)0x8007000B); - internal const int COR_E_CANNOTUNLOADAPPDOMAIN = unchecked((int)0x80131015); - internal const int COR_E_CODECONTRACTFAILED = unchecked((int)0x80131542); - internal const int COR_E_CONTEXTMARSHAL = unchecked((int)0x80131504); - internal const int COR_E_CUSTOMATTRIBUTEFORMAT = unchecked((int)0x80131605); - internal const int COR_E_DATAMISALIGNED = unchecked((int)0x80131541); - internal const int COR_E_DIVIDEBYZERO = unchecked((int)0x80020012); // DISP_E_DIVBYZERO - internal const int COR_E_DLLNOTFOUND = unchecked((int)0x80131524); - internal const int COR_E_DUPLICATEWAITOBJECT = unchecked((int)0x80131529); - internal const int COR_E_ENTRYPOINTNOTFOUND = unchecked((int)0x80131523); - internal const int COR_E_EXCEPTION = unchecked((int)0x80131500); - internal const int COR_E_EXECUTIONENGINE = unchecked((int)0x80131506); - internal const int COR_E_FIELDACCESS = unchecked((int)0x80131507); - internal const int COR_E_FIXUPSINEXE = unchecked((int)0x80131019); - internal const int COR_E_FORMAT = unchecked((int)0x80131537); - internal const int COR_E_INDEXOUTOFRANGE = unchecked((int)0x80131508); - internal const int COR_E_INSUFFICIENTEXECUTIONSTACK = unchecked((int)0x80131578); - internal const int COR_E_INSUFFICIENTMEMORY = unchecked((int)0x8013153d); - internal const int COR_E_INVALIDCAST = unchecked((int)0x80004002); - internal const int COR_E_INVALIDCOMOBJECT = unchecked((int)0x80131527); - internal const int COR_E_INVALIDFILTERCRITERIA = unchecked((int)0x80131601); - internal const int COR_E_INVALIDOLEVARIANTTYPE = unchecked((int)0x80131531); - internal const int COR_E_INVALIDOPERATION = unchecked((int)0x80131509); - internal const int COR_E_INVALIDPROGRAM = unchecked((int)0x8013153a); - internal const int COR_E_KEYNOTFOUND = unchecked((int)0x80131577); - internal const int COR_E_LOADING_REFERENCE_ASSEMBLY = unchecked((int)0x80131058); - internal const int COR_E_MARSHALDIRECTIVE = unchecked((int)0x80131535); - internal const int COR_E_MEMBERACCESS = unchecked((int)0x8013151A); - internal const int COR_E_METHODACCESS = unchecked((int)0x80131510); - internal const int COR_E_MISSINGFIELD = unchecked((int)0x80131511); - internal const int COR_E_MISSINGMANIFESTRESOURCE = unchecked((int)0x80131532); - internal const int COR_E_MISSINGMEMBER = unchecked((int)0x80131512); - internal const int COR_E_MISSINGMETHOD = unchecked((int)0x80131513); - internal const int COR_E_MISSINGSATELLITEASSEMBLY = unchecked((int)0x80131536); - internal const int COR_E_MODULE_HASH_CHECK_FAILED = unchecked((int)0x80131039); - internal const int COR_E_MULTICASTNOTSUPPORTED = unchecked((int)0x80131514); - internal const int COR_E_NEWER_RUNTIME = unchecked((int)0x8013101b); - internal const int COR_E_NOTFINITENUMBER = unchecked((int)0x80131528); - internal const int COR_E_NOTSUPPORTED = unchecked((int)0x80131515); - internal const int COR_E_NULLREFERENCE = unchecked((int)0x80004003); - internal const int COR_E_OBJECTDISPOSED = unchecked((int)0x80131622); - internal const int COR_E_OPERATIONCANCELED = unchecked((int)0x8013153B); - internal const int COR_E_OUTOFMEMORY = unchecked((int)0x8007000E); - internal const int COR_E_OVERFLOW = unchecked((int)0x80131516); - internal const int COR_E_PLATFORMNOTSUPPORTED = unchecked((int)0x80131539); - internal const int COR_E_RANK = unchecked((int)0x80131517); - internal const int COR_E_REFLECTIONTYPELOAD = unchecked((int)0x80131602); - internal const int COR_E_REMOTING = unchecked((int)0x8013150b); - internal const int COR_E_RUNTIMEWRAPPED = unchecked((int)0x8013153e); - internal const int COR_E_SAFEARRAYRANKMISMATCH = unchecked((int)0x80131538); - internal const int COR_E_SAFEARRAYTYPEMISMATCH = unchecked((int)0x80131533); - internal const int COR_E_SECURITY = unchecked((int)0x8013150A); - internal const int COR_E_SERIALIZATION = unchecked((int)0x8013150C); - internal const int COR_E_SERVER = unchecked((int)0x8013150e); - internal const int COR_E_STACKOVERFLOW = unchecked((int)0x800703E9); - internal const int COR_E_SYNCHRONIZATIONLOCK = unchecked((int)0x80131518); - internal const int COR_E_SYSTEM = unchecked((int)0x80131501); - internal const int COR_E_TARGET = unchecked((int)0x80131603); - internal const int COR_E_TARGETINVOCATION = unchecked((int)0x80131604); - internal const int COR_E_TARGETPARAMCOUNT = unchecked((int)0x8002000e); - internal const int COR_E_THREADABORTED = unchecked((int)0x80131530); - internal const int COR_E_THREADINTERRUPTED = unchecked((int)0x80131519); - internal const int COR_E_THREADSTART = unchecked((int)0x80131525); - internal const int COR_E_THREADSTATE = unchecked((int)0x80131520); - internal const int COR_E_TIMEOUT = unchecked((int)0x80131505); - internal const int COR_E_TYPEACCESS = unchecked((int)0x80131543); - internal const int COR_E_TYPEINITIALIZATION = unchecked((int)0x80131534); - internal const int COR_E_TYPELOAD = unchecked((int)0x80131522); - internal const int COR_E_TYPEUNLOADED = unchecked((int)0x80131013); - internal const int COR_E_UNAUTHORIZEDACCESS = unchecked((int)0x80070005); - internal const int COR_E_VERIFICATION = unchecked((int)0x8013150D); - internal const int COR_E_WAITHANDLECANNOTBEOPENED = unchecked((int)0x8013152C); - internal const int CORSEC_E_CRYPTO = unchecked((int)0x80131430); - internal const int CORSEC_E_CRYPTO_UNEX_OPER = unchecked((int)0x80131431); - internal const int CORSEC_E_INVALID_IMAGE_FORMAT = unchecked((int)0x8013141d); - internal const int CORSEC_E_INVALID_PUBLICKEY = unchecked((int)0x8013141e); - internal const int CORSEC_E_INVALID_STRONGNAME = unchecked((int)0x8013141a); - internal const int CORSEC_E_MIN_GRANT_FAIL = unchecked((int)0x80131417); - internal const int CORSEC_E_MISSING_STRONGNAME = unchecked((int)0x8013141b); - internal const int CORSEC_E_NO_EXEC_PERM = unchecked((int)0x80131418); - internal const int CORSEC_E_POLICY_EXCEPTION = unchecked((int)0x80131416); - internal const int CORSEC_E_SIGNATURE_MISMATCH = unchecked((int)0x80131420); - internal const int CORSEC_E_XMLSYNTAX = unchecked((int)0x80131419); - internal const int CTL_E_DEVICEIOERROR = unchecked((int)0x800A0039); - internal const int CTL_E_DIVISIONBYZERO = unchecked((int)0x800A000B); - internal const int CTL_E_FILENOTFOUND = unchecked((int)0x800A0035); - internal const int CTL_E_OUTOFMEMORY = unchecked((int)0x800A0007); - internal const int CTL_E_OUTOFSTACKSPACE = unchecked((int)0x800A001C); - internal const int CTL_E_OVERFLOW = unchecked((int)0x800A0006); - internal const int CTL_E_PATHFILEACCESSERROR = unchecked((int)0x800A004B); - internal const int CTL_E_PATHNOTFOUND = unchecked((int)0x800A004C); - internal const int CTL_E_PERMISSIONDENIED = unchecked((int)0x800A0046); - internal const int E_ELEMENTNOTAVAILABLE = unchecked((int)0x802B001F); - internal const int E_ELEMENTNOTENABLED = unchecked((int)0x802B001E); - internal const int E_FAIL = unchecked((int)0x80004005); - internal const int E_ILLEGAL_DELEGATE_ASSIGNMENT = unchecked((int)0x80000018); - internal const int E_ILLEGAL_METHOD_CALL = unchecked((int)0x8000000E); - internal const int E_ILLEGAL_STATE_CHANGE = unchecked((int)0x8000000D); - internal const int E_LAYOUTCYCLE = unchecked((int)0x802B0014); - internal const int E_NOTIMPL = unchecked((int)0x80004001); - internal const int E_OUTOFMEMORY = unchecked((int)0x8007000E); - internal const int E_POINTER = unchecked((int)0x80004003L); - internal const int E_XAMLPARSEFAILED = unchecked((int)0x802B000A); - internal const int ERROR_BAD_EXE_FORMAT = unchecked((int)0x800700C1); - internal const int ERROR_BAD_NET_NAME = unchecked((int)0x80070043); - internal const int ERROR_BAD_NETPATH = unchecked((int)0x80070035); - internal const int ERROR_DISK_CORRUPT = unchecked((int)0x80070571); - internal const int ERROR_DLL_INIT_FAILED = unchecked((int)0x8007045A); - internal const int ERROR_DLL_NOT_FOUND = unchecked((int)0x80070485); - internal const int ERROR_EXE_MARKED_INVALID = unchecked((int)0x800700C0); - internal const int ERROR_FILE_CORRUPT = unchecked((int)0x80070570); - internal const int ERROR_FILE_INVALID = unchecked((int)0x800703EE); - internal const int ERROR_FILE_NOT_FOUND = unchecked((int)0x80070002); - internal const int ERROR_INVALID_DLL = unchecked((int)0x80070482); - internal const int ERROR_INVALID_NAME = unchecked((int)0x8007007B); - internal const int ERROR_INVALID_ORDINAL = unchecked((int)0x800700B6); - internal const int ERROR_INVALID_PARAMETER = unchecked((int)0x80070057); - internal const int ERROR_LOCK_VIOLATION = unchecked((int)0x80070021); - internal const int ERROR_MOD_NOT_FOUND = unchecked((int)0x8007007E); - internal const int ERROR_MRM_MAP_NOT_FOUND = unchecked((int)0x80073b1f); - internal const int ERROR_NO_UNICODE_TRANSLATION = unchecked((int)0x80070459); - internal const int ERROR_NOACCESS = unchecked((int)0x800703E6); - internal const int ERROR_NOT_READY = unchecked((int)0x80070015); - internal const int ERROR_OPEN_FAILED = unchecked((int)0x8007006E); - internal const int ERROR_PATH_NOT_FOUND = unchecked((int)0x80070003); - internal const int ERROR_SHARING_VIOLATION = unchecked((int)0x80070020); - internal const int ERROR_TOO_MANY_OPEN_FILES = unchecked((int)0x80070004); - internal const int ERROR_INVALID_HANDLE = unchecked((int)0x80070006); - internal const int ERROR_UNRECOGNIZED_VOLUME = unchecked((int)0x800703ED); - internal const int ERROR_WRONG_TARGET_NAME = unchecked((int)0x80070574); - internal const int FUSION_E_ASM_MODULE_MISSING = unchecked((int)0x80131042); - internal const int FUSION_E_CACHEFILE_FAILED = unchecked((int)0x80131052); - internal const int FUSION_E_CODE_DOWNLOAD_DISABLED = unchecked((int)0x80131048); - internal const int FUSION_E_HOST_GAC_ASM_MISMATCH = unchecked((int)0x80131050); - internal const int FUSION_E_INVALID_NAME = unchecked((int)0x80131047); - internal const int FUSION_E_INVALID_PRIVATE_ASM_LOCATION = unchecked((int)0x80131041); - internal const int FUSION_E_LOADFROM_BLOCKED = unchecked((int)0x80131051); - internal const int FUSION_E_PRIVATE_ASM_DISALLOWED = unchecked((int)0x80131044); - internal const int FUSION_E_REF_DEF_MISMATCH = unchecked((int)0x80131040); - internal const int FUSION_E_SIGNATURE_CHECK_FAILED = unchecked((int)0x80131045); - internal const int INET_E_CANNOT_CONNECT = unchecked((int)0x800C0004); - internal const int INET_E_CONNECTION_TIMEOUT = unchecked((int)0x800C000B); - internal const int INET_E_DATA_NOT_AVAILABLE = unchecked((int)0x800C0007); - internal const int INET_E_DOWNLOAD_FAILURE = unchecked((int)0x800C0008); - internal const int INET_E_OBJECT_NOT_FOUND = unchecked((int)0x800C0006); - internal const int INET_E_RESOURCE_NOT_FOUND = unchecked((int)0x800C0005); - internal const int INET_E_UNKNOWN_PROTOCOL = unchecked((int)0x800C000D); - internal const int ISS_E_ALLOC_TOO_LARGE = unchecked((int)0x80131484); - internal const int ISS_E_BLOCK_SIZE_TOO_SMALL = unchecked((int)0x80131483); - internal const int ISS_E_CALLER = unchecked((int)0x801314A1); - internal const int ISS_E_CORRUPTED_STORE_FILE = unchecked((int)0x80131480); - internal const int ISS_E_CREATE_DIR = unchecked((int)0x80131468); - internal const int ISS_E_CREATE_MUTEX = unchecked((int)0x80131464); - internal const int ISS_E_DEPRECATE = unchecked((int)0x801314A0); - internal const int ISS_E_FILE_NOT_MAPPED = unchecked((int)0x80131482); - internal const int ISS_E_FILE_WRITE = unchecked((int)0x80131466); - internal const int ISS_E_GET_FILE_SIZE = unchecked((int)0x80131463); - internal const int ISS_E_ISOSTORE = unchecked((int)0x80131450); - internal const int ISS_E_LOCK_FAILED = unchecked((int)0x80131465); - internal const int ISS_E_MACHINE = unchecked((int)0x801314A3); - internal const int ISS_E_MACHINE_DACL = unchecked((int)0x801314A4); - internal const int ISS_E_MAP_VIEW_OF_FILE = unchecked((int)0x80131462); - internal const int ISS_E_OPEN_FILE_MAPPING = unchecked((int)0x80131461); - internal const int ISS_E_OPEN_STORE_FILE = unchecked((int)0x80131460); - internal const int ISS_E_PATH_LENGTH = unchecked((int)0x801314A2); - internal const int ISS_E_SET_FILE_POINTER = unchecked((int)0x80131467); - internal const int ISS_E_STORE_NOT_OPEN = unchecked((int)0x80131469); - internal const int ISS_E_STORE_VERSION = unchecked((int)0x80131481); - internal const int ISS_E_TABLE_ROW_NOT_FOUND = unchecked((int)0x80131486); - internal const int ISS_E_USAGE_WILL_EXCEED_QUOTA = unchecked((int)0x80131485); - internal const int META_E_BAD_SIGNATURE = unchecked((int)0x80131192); - internal const int META_E_CA_FRIENDS_SN_REQUIRED = unchecked((int)0x801311e6); - internal const int MSEE_E_ASSEMBLYLOADINPROGRESS = unchecked((int)0x80131016); - internal const int RO_E_CLOSED = unchecked((int)0x80000013); - internal const int E_BOUNDS = unchecked((int)0x8000000B); - internal const int RO_E_METADATA_NAME_NOT_FOUND = unchecked((int)0x8000000F); - internal const int SECURITY_E_INCOMPATIBLE_EVIDENCE = unchecked((int)0x80131403); - internal const int SECURITY_E_INCOMPATIBLE_SHARE = unchecked((int)0x80131401); - internal const int SECURITY_E_UNVERIFIABLE = unchecked((int)0x80131402); - internal const int STG_E_PATHNOTFOUND = unchecked((int)0x80030003); - public const int COR_E_DIRECTORYNOTFOUND = unchecked((int)0x80070003); - public const int COR_E_ENDOFSTREAM = unchecked((int)0x80070026); // OS defined - public const int COR_E_FILELOAD = unchecked((int)0x80131621); - public const int COR_E_FILENOTFOUND = unchecked((int)0x80070002); - public const int COR_E_IO = unchecked((int)0x80131620); - public const int COR_E_PATHTOOLONG = unchecked((int)0x800700CE); - } -} diff --git a/external/corert/src/Common/src/TypeSystem/Canon/CanonTypes.Interop.cs b/external/corert/src/Common/src/TypeSystem/Canon/CanonTypes.Interop.cs new file mode 100644 index 0000000000..cca6403d0c --- /dev/null +++ b/external/corert/src/Common/src/TypeSystem/Canon/CanonTypes.Interop.cs @@ -0,0 +1,11 @@ +// 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. + +namespace Internal.TypeSystem +{ + partial class CanonBaseType + { + public override PInvokeStringFormat PInvokeStringFormat => default(PInvokeStringFormat); + } +} diff --git a/external/corert/src/Common/src/TypeSystem/Canon/CanonTypes.cs b/external/corert/src/Common/src/TypeSystem/Canon/CanonTypes.cs index 79963e0e04..ef8b601909 100644 --- a/external/corert/src/Common/src/TypeSystem/Canon/CanonTypes.cs +++ b/external/corert/src/Common/src/TypeSystem/Canon/CanonTypes.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using Internal.NativeFormat; @@ -34,7 +35,7 @@ namespace Internal.TypeSystem /// /// Base class for specialized and universal canon types /// - public abstract partial class CanonBaseType : DefType + public abstract partial class CanonBaseType : MetadataType { private TypeSystemContext _context; @@ -50,6 +51,54 @@ namespace Internal.TypeSystem return _context; } } + + protected override MethodImplRecord[] ComputeVirtualMethodImplsForType() + { + return Array.Empty(); + } + + public override MetadataType MetadataBaseType => (MetadataType)BaseType; + + public override DefType ContainingType => null; + + public override DefType[] ExplicitlyImplementedInterfaces => Array.Empty(); + + public override bool IsAbstract => false; + + public override bool IsBeforeFieldInit => false; + + public override bool IsSequentialLayout => false; + + public override bool IsExplicitLayout => false; + + public override ModuleDesc Module => _context.CanonTypesModule; + + public override bool IsModuleType => false; + + public override MethodImplRecord[] FindMethodsImplWithMatchingDeclName(string name) + { + return null; + } + + public override ClassLayoutMetadata GetClassLayout() + { + return default(ClassLayoutMetadata); + } + + public override MetadataType GetNestedType(string name) + { + return null; + } + + public override IEnumerable GetNestedTypes() + { + return Array.Empty(); + } + + public override bool HasCustomAttribute(string attributeNamespace, string attributeName) + { + return false; + } } /// @@ -79,6 +128,8 @@ namespace Internal.TypeSystem } } + public override bool IsSealed => false; + public CanonType(TypeSystemContext context) : base(context) { @@ -124,6 +175,7 @@ namespace Internal.TypeSystem } flags |= TypeFlags.HasFinalizerComputed; + flags |= TypeFlags.IsByRefLikeComputed; return flags; } @@ -137,11 +189,6 @@ namespace Internal.TypeSystem return _hashcode; } - - public override string ToString() - { - return FullName; - } } /// @@ -171,6 +218,8 @@ namespace Internal.TypeSystem } } + public override bool IsSealed => true; + public UniversalCanonType(TypeSystemContext context) : base(context) { @@ -198,7 +247,6 @@ namespace Internal.TypeSystem protected override TypeDesc ConvertToCanonFormImpl(CanonicalFormKind kind) { - Debug.Assert(kind == CanonicalFormKind.Universal); return this; } @@ -214,6 +262,7 @@ namespace Internal.TypeSystem } flags |= TypeFlags.HasFinalizerComputed; + flags |= TypeFlags.IsByRefLikeComputed; return flags; } @@ -227,10 +276,5 @@ namespace Internal.TypeSystem return _hashcode; } - - public override string ToString() - { - return FullName; - } } } diff --git a/external/corert/src/Common/src/TypeSystem/Canon/GenericParameterDesc.Canon.cs b/external/corert/src/Common/src/TypeSystem/Canon/GenericParameterDesc.Canon.cs index 76cca4b992..59f1c73114 100644 --- a/external/corert/src/Common/src/TypeSystem/Canon/GenericParameterDesc.Canon.cs +++ b/external/corert/src/Common/src/TypeSystem/Canon/GenericParameterDesc.Canon.cs @@ -11,13 +11,13 @@ namespace Internal.TypeSystem { public sealed override bool IsCanonicalSubtype(CanonicalFormKind policy) { - Debug.Assert(false, "IsCanonicalSubtype of an indefinite type"); + Debug.Fail("IsCanonicalSubtype of an indefinite type"); return false; } protected sealed override TypeDesc ConvertToCanonFormImpl(CanonicalFormKind kind) { - Debug.Assert(false, "ConvertToCanonFormImpl for an indefinite type"); + Debug.Fail("ConvertToCanonFormImpl for an indefinite type"); return this; } } diff --git a/external/corert/src/Common/src/TypeSystem/Canon/InstantiatedMethod.Canon.cs b/external/corert/src/Common/src/TypeSystem/Canon/InstantiatedMethod.Canon.cs index 1be635af9c..4e3e0204e7 100644 --- a/external/corert/src/Common/src/TypeSystem/Canon/InstantiatedMethod.Canon.cs +++ b/external/corert/src/Common/src/TypeSystem/Canon/InstantiatedMethod.Canon.cs @@ -37,6 +37,16 @@ namespace Internal.TypeSystem canonicalMethodResult = this; } + // If the method instantiation is universal, we use a __UniversalCanon for all instantiation arguments for simplicity. + // This is to not end up having method instantiations like Foo<__UniversalCanon>.Method or Foo<__UniversalCanon>.Method + // or Foo<__UniversalCanon>.Method<__Canon> or Foo.Method<__UniversalCanon> + // It should just be Foo<__UniversalCanon>.Method<__UniversalCanon> + if ((kind == CanonicalFormKind.Specific) && + canonicalMethodResult.IsCanonicalMethod(CanonicalFormKind.Universal)) + { + canonicalMethodResult = (InstantiatedMethod)canonicalMethodResult.GetCanonMethodTarget(CanonicalFormKind.Universal); + } + SetCachedCanonValue(kind, canonicalMethodResult); } @@ -101,4 +111,4 @@ namespace Internal.TypeSystem return false; } } -} \ No newline at end of file +} diff --git a/external/corert/src/Common/src/TypeSystem/Canon/SignatureVariable.Canon.cs b/external/corert/src/Common/src/TypeSystem/Canon/SignatureVariable.Canon.cs index bb90a62af0..a7da04a857 100644 --- a/external/corert/src/Common/src/TypeSystem/Canon/SignatureVariable.Canon.cs +++ b/external/corert/src/Common/src/TypeSystem/Canon/SignatureVariable.Canon.cs @@ -11,13 +11,13 @@ namespace Internal.TypeSystem { public override bool IsCanonicalSubtype(CanonicalFormKind policy) { - Debug.Assert(false, "IsCanonicalSubtype of an indefinite type"); + Debug.Fail("IsCanonicalSubtype of an indefinite type"); return false; } protected override TypeDesc ConvertToCanonFormImpl(CanonicalFormKind kind) { - Debug.Assert(false, "ConvertToCanonFormImpl for an indefinite type"); + Debug.Fail("ConvertToCanonFormImpl for an indefinite type"); return this; } } @@ -27,13 +27,13 @@ namespace Internal.TypeSystem { public override bool IsCanonicalSubtype(CanonicalFormKind policy) { - Debug.Assert(false, "IsCanonicalSubtype of an indefinite type"); + Debug.Fail("IsCanonicalSubtype of an indefinite type"); return false; } protected override TypeDesc ConvertToCanonFormImpl(CanonicalFormKind kind) { - Debug.Assert(false, "ConvertToCanonFormImpl for an indefinite type"); + Debug.Fail("ConvertToCanonFormImpl for an indefinite type"); return this; } } diff --git a/external/corert/src/Common/src/TypeSystem/Canon/TypeSystemContext.Canon.cs b/external/corert/src/Common/src/TypeSystem/Canon/TypeSystemContext.Canon.cs index 381ea2f82b..444f24b67a 100644 --- a/external/corert/src/Common/src/TypeSystem/Canon/TypeSystemContext.Canon.cs +++ b/external/corert/src/Common/src/TypeSystem/Canon/TypeSystemContext.Canon.cs @@ -44,6 +44,11 @@ namespace Internal.TypeSystem } } + protected internal virtual ModuleDesc CanonTypesModule + { + get { return SystemModule; } + } + /// /// Returns true if and only if the '' is __Canon or __UniversalCanon /// that matches the parameter. @@ -95,5 +100,18 @@ namespace Internal.TypeSystem public abstract bool SupportsCanon { get; } public abstract bool SupportsUniversalCanon { get; } + + public MetadataType GetCanonType(string name) + { + switch (name) + { + case TypeSystem.CanonType.FullName: + return CanonType; + case TypeSystem.UniversalCanonType.FullName: + return UniversalCanonType; + } + + return null; + } } -} \ No newline at end of file +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/INonEmittableType.cs b/external/corert/src/Common/src/TypeSystem/CodeGen/INonEmittableType.cs similarity index 93% rename from external/corert/src/ILCompiler.Compiler/src/Compiler/INonEmittableType.cs rename to external/corert/src/Common/src/TypeSystem/CodeGen/INonEmittableType.cs index 942fc93dde..24b1d2eeec 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/INonEmittableType.cs +++ b/external/corert/src/Common/src/TypeSystem/CodeGen/INonEmittableType.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace ILCompiler +namespace Internal.TypeSystem { /// /// Used to mark TypeDesc types that are not part of the core type system diff --git a/external/corert/src/Common/src/TypeSystem/CodeGen/NativeStructType.CodeGen.cs b/external/corert/src/Common/src/TypeSystem/CodeGen/NativeStructType.CodeGen.cs new file mode 100644 index 0000000000..8e25499135 --- /dev/null +++ b/external/corert/src/Common/src/TypeSystem/CodeGen/NativeStructType.CodeGen.cs @@ -0,0 +1,11 @@ +// 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. + +namespace Internal.TypeSystem.Interop +{ + // Implements INonEmittableType + partial class NativeStructType : INonEmittableType + { + } +} diff --git a/external/corert/src/Common/src/TypeSystem/CodeGen/TargetDetails.CodeGen.cs b/external/corert/src/Common/src/TypeSystem/CodeGen/TargetDetails.CodeGen.cs new file mode 100644 index 0000000000..311f7488dd --- /dev/null +++ b/external/corert/src/Common/src/TypeSystem/CodeGen/TargetDetails.CodeGen.cs @@ -0,0 +1,45 @@ +// 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. + +namespace Internal.TypeSystem +{ + // Extension to TargetDetails related to code generation + partial class TargetDetails + { + public TargetDetails(TargetArchitecture architecture, TargetOS targetOS, TargetAbi abi, SimdVectorLength simdVectorLength) + : this(architecture, targetOS, abi) + { + MaximumSimdVectorLength = simdVectorLength; + } + + /// + /// Specifies the maximum size of native vectors on the target architecture. + /// + public SimdVectorLength MaximumSimdVectorLength + { + get; + } + } + + /// + /// Specifies the size of native vectors. + /// + public enum SimdVectorLength + { + /// + /// Specifies that native vectors are not supported. + /// + None, + + /// + /// Specifies that native vectors are 128 bit (e.g. SSE on x86). + /// + Vector128Bit, + + /// + /// Specifies that native vectors are 256 bit (e.g. AVX on x86). + /// + Vector256Bit, + } +} diff --git a/external/corert/src/Common/src/TypeSystem/Common/ArrayType.cs b/external/corert/src/Common/src/TypeSystem/Common/ArrayType.cs index ea3e2ea7b5..22a9850123 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/ArrayType.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/ArrayType.cs @@ -145,12 +145,9 @@ namespace Internal.TypeSystem flags |= TypeFlags.HasFinalizerComputed; - return flags; - } + flags |= TypeFlags.IsByRefLikeComputed; - public override string ToString() - { - return this.ElementType.ToString() + "[" + new String(',', Rank - 1) + "]"; + return flags; } } @@ -324,10 +321,5 @@ namespace Internal.TypeSystem else return this; } - - public override string ToString() - { - return _owningType.ToString() + "." + Name; - } } } diff --git a/external/corert/src/Common/src/TypeSystem/Common/ByRefType.cs b/external/corert/src/Common/src/TypeSystem/Common/ByRefType.cs index 5042c2373d..164925f9df 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/ByRefType.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/ByRefType.cs @@ -37,12 +37,9 @@ namespace Internal.TypeSystem flags |= TypeFlags.HasFinalizerComputed; - return flags; - } + flags |= TypeFlags.IsByRefLikeComputed; - public override string ToString() - { - return this.ParameterType.ToString() + "&"; + return flags; } } } diff --git a/external/corert/src/Common/src/TypeSystem/Common/CastingHelper.cs b/external/corert/src/Common/src/TypeSystem/Common/CastingHelper.cs index cf1dd0cc29..d850cc6b33 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/CastingHelper.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/CastingHelper.cs @@ -317,6 +317,11 @@ namespace Internal.TypeSystem { TypeDesc curType = thisType; + if (curType.IsInterface && otherType.IsObject) + { + return true; + } + // If the target type has variant type parameters, we take a slower path if (curType.HasVariance) { @@ -352,11 +357,6 @@ namespace Internal.TypeSystem return thisType.CanCastTo(otherType.Instantiation[0]); } - if (curType.IsInterface) - { - return otherType.IsObject; - } - do { if (curType == otherType) diff --git a/external/corert/src/Common/src/TypeSystem/Common/DefType.FieldLayout.cs b/external/corert/src/Common/src/TypeSystem/Common/DefType.FieldLayout.cs index 289c79760a..4977018128 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/DefType.FieldLayout.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/DefType.FieldLayout.cs @@ -49,16 +49,6 @@ namespace Internal.TypeSystem /// True if information about the shape of value type has been computed. /// public const int ComputedValueTypeShapeCharacteristics = 0x40; - - /// - /// True if has been computed. - /// - public const int ComputedIsByRefLike = 0x80; - - /// - /// True if this is a byref-like type. - /// - public const int IsByRefLike = 0x100; } private class StaticBlockInfo @@ -297,22 +287,6 @@ namespace Internal.TypeSystem } } - /// - /// Gets a value indicating whether this is a byref-like type - /// (a TypedReference, Span<T>, etc.). - /// - public bool IsByRefLike - { - get - { - if (!_fieldLayoutFlags.HasFlags(FieldLayoutFlags.ComputedIsByRefLike)) - { - ComputeIsByRefLike(); - } - return _fieldLayoutFlags.HasFlags(FieldLayoutFlags.IsByRefLike); - } - } - private void ComputeValueTypeShapeCharacteristics() { _valueTypeShapeCharacteristics = this.Context.GetLayoutAlgorithmForType(this).ComputeValueTypeShapeCharacteristics(this); @@ -398,21 +372,5 @@ namespace Internal.TypeSystem _fieldLayoutFlags.AddFlags(flagsToAdd); } - - public void ComputeIsByRefLike() - { - if (_fieldLayoutFlags.HasFlags(FieldLayoutFlags.ComputedIsByRefLike)) - return; - - int flagsToAdd = FieldLayoutFlags.ComputedIsByRefLike; - - if (this.Context.GetLayoutAlgorithmForType(this).ComputeIsByRefLike(this)) - { - flagsToAdd |= FieldLayoutFlags.IsByRefLike; - } - - _fieldLayoutFlags.AddFlags(flagsToAdd); - } } - } diff --git a/external/corert/src/Common/src/TypeSystem/Common/DefType.cs b/external/corert/src/Common/src/TypeSystem/Common/DefType.cs index 80ca79838b..fb1cf075c9 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/DefType.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/DefType.cs @@ -13,34 +13,16 @@ namespace Internal.TypeSystem /// /// Gets the namespace of the type. /// - public virtual string Namespace - { - get - { - return null; - } - } + public virtual string Namespace => null; /// /// Gets the name of the type as represented in the metadata. /// - public virtual string Name - { - get - { - return null; - } - } + public virtual string Name => null; /// /// Gets the containing type of this type or null if the type is not nested. /// - public virtual DefType ContainingType - { - get - { - return null; - } - } + public virtual DefType ContainingType => null; } } diff --git a/external/corert/src/Common/src/TypeSystem/Common/ExceptionStringID.cs b/external/corert/src/Common/src/TypeSystem/Common/ExceptionStringID.cs index 73d355b921..14800f0e73 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/ExceptionStringID.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/ExceptionStringID.cs @@ -27,9 +27,11 @@ namespace Internal.TypeSystem FileLoadErrorGeneric, // InvalidProgramException + InvalidProgramDefault, InvalidProgramSpecific, InvalidProgramVararg, InvalidProgramCallVirtFinalize, + InvalidProgramNativeCallable, // BadImageFormatException BadImageFormatGeneric, diff --git a/external/corert/src/Common/src/TypeSystem/Common/FieldDesc.ToString.cs b/external/corert/src/Common/src/TypeSystem/Common/FieldDesc.ToString.cs new file mode 100644 index 0000000000..29cea11d53 --- /dev/null +++ b/external/corert/src/Common/src/TypeSystem/Common/FieldDesc.ToString.cs @@ -0,0 +1,14 @@ +// 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. + +namespace Internal.TypeSystem +{ + partial class FieldDesc + { + public override string ToString() + { + return $"{OwningType}.{Name}"; + } + } +} diff --git a/external/corert/src/Common/src/TypeSystem/Common/FieldLayoutAlgorithm.cs b/external/corert/src/Common/src/TypeSystem/Common/FieldLayoutAlgorithm.cs index 79f6627711..eee09f1b38 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/FieldLayoutAlgorithm.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/FieldLayoutAlgorithm.cs @@ -43,11 +43,6 @@ namespace Internal.TypeSystem /// the element type of the homogenous float aggregate. This will either be System.Double or System.Float. /// public abstract DefType ComputeHomogeneousFloatAggregateElementType(DefType type); - - /// - /// Compute whether '' is a ByRef-like value type (TypedReference, Span<T>, etc.). - /// - public abstract bool ComputeIsByRefLike(DefType type); } /// diff --git a/external/corert/src/Common/src/TypeSystem/Common/FunctionPointerType.cs b/external/corert/src/Common/src/TypeSystem/Common/FunctionPointerType.cs index b523bc2bca..5d50620d75 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/FunctionPointerType.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/FunctionPointerType.cs @@ -67,24 +67,9 @@ namespace Internal.TypeSystem flags |= TypeFlags.HasFinalizerComputed; + flags |= TypeFlags.IsByRefLikeComputed; + return flags; } - - public override string ToString() - { - StringBuilder sb = new StringBuilder(); - sb.Append(_signature.ReturnType); - - sb.Append(" *("); - for (int i = 0; i < _signature.Length; i++) - { - if (i > 0) - sb.Append(", "); - sb.Append(_signature[i]); - } - sb.Append(')'); - - return sb.ToString(); - } } } diff --git a/external/corert/src/Common/src/TypeSystem/Common/GenericParameterDesc.cs b/external/corert/src/Common/src/TypeSystem/Common/GenericParameterDesc.cs index 0eeff79b54..d98497921d 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/GenericParameterDesc.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/GenericParameterDesc.cs @@ -164,6 +164,8 @@ namespace Internal.TypeSystem flags |= TypeFlags.HasGenericVarianceComputed; + flags |= TypeFlags.IsByRefLikeComputed; + return flags; } diff --git a/external/corert/src/Common/src/TypeSystem/Common/InstantiatedMethod.cs b/external/corert/src/Common/src/TypeSystem/Common/InstantiatedMethod.cs index 4c457ca067..f73366c0a5 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/InstantiatedMethod.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/InstantiatedMethod.cs @@ -150,14 +150,5 @@ namespace Internal.TypeSystem return _methodDef.Name; } } - - public override string ToString() - { - var sb = new StringBuilder(_methodDef.ToString()); - sb.Append('<'); - sb.Append(_instantiation.ToString()); - sb.Append('>'); - return sb.ToString(); - } } } diff --git a/external/corert/src/Common/src/TypeSystem/Common/InstantiatedType.cs b/external/corert/src/Common/src/TypeSystem/Common/InstantiatedType.cs index 9df2c6ae17..c5ae6776e4 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/InstantiatedType.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/InstantiatedType.cs @@ -104,6 +104,14 @@ namespace Internal.TypeSystem flags |= TypeFlags.HasFinalizer; } + if ((mask & TypeFlags.IsByRefLikeComputed) != 0) + { + flags |= TypeFlags.IsByRefLikeComputed; + + if (_typeDef.IsByRefLike) + flags |= TypeFlags.IsByRefLike; + } + return flags; } @@ -261,15 +269,6 @@ namespace Internal.TypeSystem return _typeDef; } - public override string ToString() - { - var sb = new StringBuilder(_typeDef.ToString()); - sb.Append('<'); - sb.Append(_instantiation.ToString()); - sb.Append('>'); - return sb.ToString(); - } - // Properties that are passed through from the type definition public override ClassLayoutMetadata GetClassLayout() { diff --git a/external/corert/src/Common/src/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs b/external/corert/src/Common/src/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs index 7953499205..ab35f05485 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs @@ -21,7 +21,7 @@ namespace Internal.TypeSystem // CLI - Partition 1, section 9.5 - Generic types shall not be marked explicitlayout. if (type.HasInstantiation && type.IsExplicitLayout) { - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadExplicitGeneric, type.GetTypeDefinition()); + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadExplicitGeneric, type.GetTypeDefinition()); } // Count the number of instance fields in advance for convenience @@ -35,14 +35,11 @@ namespace Internal.TypeSystem // ByRef instance fields are not allowed. if (fieldType.IsByRef) - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); - // ByRef-like instance fields on reference types are not allowed. - if (fieldType.IsValueType && !type.IsValueType) - { - if (((DefType)fieldType).IsByRefLike) - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); - } + // ByRef-like instance fields on non-byref-like types are not allowed. + if (fieldType.IsByRefLike && !type.IsByRefLike) + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); numInstanceFields++; } @@ -52,7 +49,7 @@ namespace Internal.TypeSystem // This is a global type, it must not have instance fields. if (numInstanceFields > 0) { - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } // Global types do not do the rest of instance field layout. @@ -77,14 +74,14 @@ namespace Internal.TypeSystem if (type.IsSequentialLayout != baseType.IsSequentialLayout || type.IsExplicitLayout != baseType.IsExplicitLayout) { - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadBadFormat, type); + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadBadFormat, type); } } // Enum types must have a single instance field if (type.IsEnum && numInstanceFields != 1) { - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } if (type.IsPrimitive) @@ -93,7 +90,7 @@ namespace Internal.TypeSystem // as the type itself. They do not do the rest of instance field layout. if (numInstanceFields > 1) { - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } SizeAndAlignment instanceByteSizeAndAlignment; @@ -144,7 +141,7 @@ namespace Internal.TypeSystem { if (type.IsEnum) { - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadBadFormat, type); + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadBadFormat, type); } var layoutMetadata = type.GetClassLayout(); @@ -153,7 +150,7 @@ namespace Internal.TypeSystem int packing = layoutMetadata.PackingSize; if (packing < 0 || packing > 128 || ((packing & (packing - 1)) != 0)) { - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadBadFormat, type); + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadBadFormat, type); } Debug.Assert(layoutMetadata.Offsets == null || layoutMetadata.Offsets.Length == numInstanceFields); @@ -211,7 +208,7 @@ namespace Internal.TypeSystem TypeDesc fieldType = field.FieldType; if (fieldType.IsByRef || (fieldType.IsValueType && ((DefType)fieldType).IsByRefLike)) { - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } ref StaticsBlock block = ref GetStaticsBlockForField(ref result, field); @@ -309,7 +306,7 @@ namespace Internal.TypeSystem largestAlignmentRequired = LayoutInt.Max(fieldSizeAndAlignment.Alignment, largestAlignmentRequired); if (fieldAndOffset.Offset == FieldAndOffset.InvalidOffset) - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadBadFormat, type); + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadBadFormat, type); LayoutInt computedOffset = fieldAndOffset.Offset + cumulativeInstanceFieldPos; @@ -319,7 +316,7 @@ namespace Internal.TypeSystem if (offsetModulo != 0) { // GC pointers MUST be aligned. - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadExplicitLayout, type, fieldAndOffset.Offset.ToStringInvariant()); + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadExplicitLayout, type, fieldAndOffset.Offset.ToStringInvariant()); } } @@ -617,40 +614,12 @@ namespace Internal.TypeSystem break; default: - Debug.Assert(false, "Why is IsHfa true on this type?"); + Debug.Fail("Why is IsHfa true on this type?"); return null; } } } - public override bool ComputeIsByRefLike(DefType type) - { - // Reference types can never be ByRef-like. - if (!type.IsValueType) - return false; - - if (type.IsByReferenceOfT) - return true; - - foreach (FieldDesc field in type.GetFields()) - { - if (field.IsStatic) - continue; - - TypeDesc fieldType = field.FieldType; - if (fieldType.IsValueType && !fieldType.IsPrimitive) - { - DefType fieldDefType = (DefType)fieldType; - if (fieldDefType.IsByRefLike) - { - return true; - } - } - } - - return false; - } - private struct SizeAndAlignment { public LayoutInt Size; diff --git a/external/corert/src/Common/src/TypeSystem/Common/MetadataTypeSystemContext.cs b/external/corert/src/Common/src/TypeSystem/Common/MetadataTypeSystemContext.cs index 7b82dc3610..2d8a9378cc 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/MetadataTypeSystemContext.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/MetadataTypeSystemContext.cs @@ -87,7 +87,7 @@ namespace Internal.TypeSystem int typeIndex = (int)wellKnownType - 1; DefType type = _wellKnownTypes[typeIndex]; if (type == null && throwIfNotFound) - throw new TypeSystemException.TypeLoadException("System", s_wellKnownTypeNames[typeIndex], SystemModule); + ThrowHelper.ThrowTypeLoadException("System", s_wellKnownTypeNames[typeIndex], SystemModule); return type; } diff --git a/external/corert/src/Common/src/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs b/external/corert/src/Common/src/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs index be57c09175..c635b01342 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs @@ -209,6 +209,8 @@ namespace Internal.TypeSystem // Step 4, name/sig match virtual function resolve MethodDesc resolutionTarget = FindNameSigOverrideForVirtualMethod(group.DefiningMethod, uninstantiatedType); + if (resolutionTarget == null) + return null; // Step 5, convert resolution target from uninstantiated form target to objecttype target, // and instantiate as appropriate @@ -293,7 +295,7 @@ namespace Internal.TypeSystem { if (implMethod != null) { - throw new NotImplementedException("NYI: differentiating between overloads on instantiations when the instantiated signatures match."); + throw NotImplemented.ActiveIssue("https://github.com/dotnet/corert/issues/190"); } implMethod = candidate; } @@ -466,6 +468,11 @@ namespace Internal.TypeSystem return ResolveInterfaceMethodToVirtualMethodOnType(interfaceMethod, (MetadataType)currentType); } + public override MethodDesc ResolveVariantInterfaceMethodToVirtualMethodOnType(MethodDesc interfaceMethod, TypeDesc currentType) + { + return ResolveVariantInterfaceMethodToVirtualMethodOnType(interfaceMethod, (MetadataType)currentType); + } + //////////////////////// INTERFACE RESOLUTION //Interface function resolution // Interface function resolution follows the following rules @@ -538,11 +545,39 @@ namespace Internal.TypeSystem } } + public static MethodDesc ResolveVariantInterfaceMethodToVirtualMethodOnType(MethodDesc interfaceMethod, MetadataType currentType) + { + MetadataType interfaceType = (MetadataType)interfaceMethod.OwningType; + bool foundInterface = IsInterfaceImplementedOnType(currentType, interfaceType); + MethodDesc implMethod; + + if (foundInterface) + { + implMethod = ResolveInterfaceMethodToVirtualMethodOnType(interfaceMethod, currentType); + if (implMethod != null) + return implMethod; + } + + foreach (TypeDesc iface in currentType.RuntimeInterfaces) + { + if (iface.CanCastTo(interfaceType)) + { + implMethod = iface.FindMethodOnTypeWithMatchingTypicalMethod(interfaceMethod); + Debug.Assert(implMethod != null); + implMethod = ResolveInterfaceMethodToVirtualMethodOnType(implMethod, currentType); + if (implMethod != null) + return implMethod; + } + } + + return null; + } + // Helper routine used during implicit interface implementation discovery private static MethodDesc ResolveInterfaceMethodToVirtualMethodOnTypeRecursive(MethodDesc interfaceMethod, MetadataType currentType) { while (true) - { + { if (currentType == null) return null; diff --git a/external/corert/src/Common/src/TypeSystem/Common/MethodDesc.ToString.cs b/external/corert/src/Common/src/TypeSystem/Common/MethodDesc.ToString.cs new file mode 100644 index 0000000000..2c87071fc8 --- /dev/null +++ b/external/corert/src/Common/src/TypeSystem/Common/MethodDesc.ToString.cs @@ -0,0 +1,78 @@ +// 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.Text; + +namespace Internal.TypeSystem +{ + partial class MethodSignature + { + public override string ToString() + { + return ToString(includeReturnType: true); + } + + public string ToString(bool includeReturnType) + { + var sb = new StringBuilder(); + + if (includeReturnType) + { + DebugNameFormatter.Instance.AppendName(sb, ReturnType, DebugNameFormatter.FormatOptions.None); + sb.Append('('); + } + + bool first = true; + foreach (TypeDesc param in _parameters) + { + if (first) + first = false; + else + sb.Append(','); + DebugNameFormatter.Instance.AppendName(sb, param, DebugNameFormatter.FormatOptions.None); + } + + if (includeReturnType) + sb.Append(')'); + + return sb.ToString(); + } + } + + partial class MethodDesc + { + public override string ToString() + { + var sb = new StringBuilder(); + + // (Skipping return type to keep things short) + sb.Append(OwningType); + sb.Append('.'); + sb.Append(Name); + + bool first = true; + for (int i = 0; i < Instantiation.Length; i++) + { + if (first) + { + sb.Append('<'); + first = false; + } + else + { + sb.Append(','); + } + DebugNameFormatter.Instance.AppendName(sb, Instantiation[i], DebugNameFormatter.FormatOptions.None); + } + if (!first) + sb.Append('>'); + + sb.Append('('); + sb.Append(Signature.ToString(includeReturnType: false)); + sb.Append(')'); + + return sb.ToString(); + } + } +} diff --git a/external/corert/src/Common/src/TypeSystem/Common/MethodDesc.cs b/external/corert/src/Common/src/TypeSystem/Common/MethodDesc.cs index 89706ff7f9..74c48d8eba 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/MethodDesc.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/MethodDesc.cs @@ -19,6 +19,7 @@ namespace Internal.TypeSystem UnmanagedCallingConventionCdecl = 0x0001, UnmanagedCallingConventionStdCall = 0x0002, UnmanagedCallingConventionThisCall = 0x0003, + CallingConventionVarargs = 0x0005, Static = 0x0010, } @@ -26,7 +27,7 @@ namespace Internal.TypeSystem /// /// Represents the parameter types, the return type, and flags of a method. /// - public sealed partial class MethodSignature + public sealed partial class MethodSignature : TypeSystemEntity { internal MethodSignatureFlags _flags; internal int _genericParameterCount; @@ -131,6 +132,8 @@ namespace Internal.TypeSystem { return TypeHashingAlgorithms.ComputeMethodSignatureHashCode(_returnType.GetHashCode(), _parameters); } + + public override TypeSystemContext Context => _returnType.Context; } /// @@ -466,8 +469,7 @@ namespace Internal.TypeSystem get { TypeDesc owningType = OwningType; - return owningType.HasFinalizer && - (owningType.GetFinalizer() == this || owningType.IsObject && Name == "Finalize"); + return (owningType.IsObject && Name == "Finalize") || (owningType.HasFinalizer && owningType.GetFinalizer() == this); } } diff --git a/external/corert/src/Common/src/TypeSystem/Common/MethodForInstantiatedType.cs b/external/corert/src/Common/src/TypeSystem/Common/MethodForInstantiatedType.cs index e65783b661..f8ac6f8c40 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/MethodForInstantiatedType.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/MethodForInstantiatedType.cs @@ -134,10 +134,5 @@ namespace Internal.TypeSystem return _typicalMethodDef.Name; } } - - public override string ToString() - { - return OwningType.ToString() + "." + Name; - } } } diff --git a/external/corert/src/Common/src/TypeSystem/Common/PointerType.cs b/external/corert/src/Common/src/TypeSystem/Common/PointerType.cs index c7e5304bb7..e0cd681b2d 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/PointerType.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/PointerType.cs @@ -35,13 +35,9 @@ namespace Internal.TypeSystem flags |= TypeFlags.HasGenericVarianceComputed; flags |= TypeFlags.HasFinalizerComputed; + flags |= TypeFlags.IsByRefLikeComputed; return flags; } - - public override string ToString() - { - return this.ParameterType.ToString() + "*"; - } } } diff --git a/external/corert/src/Common/src/TypeSystem/Common/SignatureVariable.cs b/external/corert/src/Common/src/TypeSystem/Common/SignatureVariable.cs index 3650f61d74..2fde3c5389 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/SignatureVariable.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/SignatureVariable.cs @@ -78,11 +78,6 @@ namespace Internal.TypeSystem { return typeInstantiation.IsNull ? this : typeInstantiation[Index]; } - - public override string ToString() - { - return "!" + Index.ToStringInvariant(); - } } public sealed partial class SignatureMethodVariable : SignatureVariable @@ -120,10 +115,5 @@ namespace Internal.TypeSystem { return methodInstantiation.IsNull ? this : methodInstantiation[Index]; } - - public override string ToString() - { - return "!!" + Index.ToStringInvariant(); - } } } diff --git a/external/corert/src/Common/src/TypeSystem/Common/TargetDetails.cs b/external/corert/src/Common/src/TypeSystem/Common/TargetDetails.cs index 627e773e17..31323e59b0 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/TargetDetails.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/TargetDetails.cs @@ -18,6 +18,7 @@ namespace Internal.TypeSystem ARM64, X64, X86, + Wasm32 } /// @@ -31,6 +32,7 @@ namespace Internal.TypeSystem OSX, FreeBSD, NetBSD, + WebAssembly, } public enum TargetAbi @@ -54,7 +56,7 @@ namespace Internal.TypeSystem /// Represents various details about the compilation target that affect /// layout, padding, allocations, or ABI. /// - public class TargetDetails + public partial class TargetDetails { /// /// Gets the target CPU architecture. @@ -92,9 +94,10 @@ namespace Internal.TypeSystem case TargetArchitecture.ARM: case TargetArchitecture.ARMEL: case TargetArchitecture.X86: + case TargetArchitecture.Wasm32: return 4; default: - throw new NotImplementedException(); + throw new NotSupportedException(); } } } @@ -201,6 +204,7 @@ namespace Internal.TypeSystem { case TargetArchitecture.ARM: case TargetArchitecture.ARMEL: + case TargetArchitecture.Wasm32: // ARM supports two alignments for objects on the GC heap (4 byte and 8 byte) if (fieldAlignment.IsIndeterminate) return LayoutInt.Indeterminate; @@ -210,11 +214,12 @@ namespace Internal.TypeSystem else return new LayoutInt(8); case TargetArchitecture.X64: + case TargetArchitecture.ARM64: return new LayoutInt(8); case TargetArchitecture.X86: return new LayoutInt(4); default: - throw new NotImplementedException(); + throw new NotSupportedException(); } } diff --git a/external/corert/src/Common/src/TypeSystem/Common/ThrowHelper.Common.cs b/external/corert/src/Common/src/TypeSystem/Common/ThrowHelper.Common.cs new file mode 100644 index 0000000000..40b5928d98 --- /dev/null +++ b/external/corert/src/Common/src/TypeSystem/Common/ThrowHelper.Common.cs @@ -0,0 +1,127 @@ +// 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.Text; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.TypeSystem +{ + public static partial class ThrowHelper + { + [System.Diagnostics.DebuggerHidden] + public static void ThrowTypeLoadException(string nestedTypeName, ModuleDesc module) + { + ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, nestedTypeName, Format.Module(module)); + } + + [System.Diagnostics.DebuggerHidden] + public static void ThrowTypeLoadException(string @namespace, string name, ModuleDesc module) + { + ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, Format.Type(@namespace, name), Format.Module(module)); + } + + [System.Diagnostics.DebuggerHidden] + public static void ThrowTypeLoadException(TypeDesc type) + { + ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, Format.Type(type), Format.OwningModule(type)); + } + + [System.Diagnostics.DebuggerHidden] + public static void ThrowTypeLoadException(ExceptionStringID id, MethodDesc method) + { + ThrowTypeLoadException(id, Format.Type(method.OwningType), Format.OwningModule(method), Format.Method(method)); + } + + [System.Diagnostics.DebuggerHidden] + public static void ThrowTypeLoadException(ExceptionStringID id, TypeDesc type, string messageArg) + { + ThrowTypeLoadException(id, Format.Type(type), Format.OwningModule(type), messageArg); + } + + [System.Diagnostics.DebuggerHidden] + public static void ThrowTypeLoadException(ExceptionStringID id, TypeDesc type) + { + ThrowTypeLoadException(id, Format.Type(type), Format.OwningModule(type)); + } + + private static partial class Format + { + public static string OwningModule(MethodDesc method) + { + return OwningModule(method.OwningType); + } + + public static string Module(ModuleDesc module) + { + if (module == null) + return "?"; + + IAssemblyDesc assembly = module as IAssemblyDesc; + if (assembly != null) + { + return assembly.GetName().FullName; + } + else + { + Debug.Fail("Multi-module assemblies"); + return module.ToString(); + } + } + + public static string Type(TypeDesc type) + { + return ExceptionTypeNameFormatter.Instance.FormatName(type); + } + + public static string Type(string @namespace, string name) + { + return String.IsNullOrEmpty(@namespace) ? name : @namespace + "." + name; + } + + public static string Field(TypeDesc owningType, string fieldName) + { + return Type(owningType) + "." + fieldName; + } + + public static string Method(MethodDesc method) + { + return Method(method.OwningType, method.Name, method.Signature); + } + + public static string Method(TypeDesc owningType, string methodName, MethodSignature signature) + { + StringBuilder sb = new StringBuilder(); + + if (signature != null) + { + sb.Append(ExceptionTypeNameFormatter.Instance.FormatName(signature.ReturnType)); + sb.Append(' '); + } + + sb.Append(ExceptionTypeNameFormatter.Instance.FormatName(owningType)); + sb.Append('.'); + sb.Append(methodName); + + if (signature != null) + { + sb.Append('('); + for (int i = 0; i < signature.Length; i++) + { + if (i > 0) + { + sb.Append(", "); + } + + sb.Append(ExceptionTypeNameFormatter.Instance.FormatName(signature[i])); + } + sb.Append(')'); + } + + return sb.ToString(); + } + } + } +} diff --git a/external/corert/src/Common/src/TypeSystem/Common/ThrowHelper.cs b/external/corert/src/Common/src/TypeSystem/Common/ThrowHelper.cs new file mode 100644 index 0000000000..46ff4cee5a --- /dev/null +++ b/external/corert/src/Common/src/TypeSystem/Common/ThrowHelper.cs @@ -0,0 +1,65 @@ +// 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. + +namespace Internal.TypeSystem +{ + public static partial class ThrowHelper + { + [System.Diagnostics.DebuggerHidden] + private static void ThrowTypeLoadException(ExceptionStringID id, string typeName, string assemblyName, string messageArg) + { + throw new TypeSystemException.TypeLoadException(id, typeName, assemblyName, messageArg); + } + + [System.Diagnostics.DebuggerHidden] + private static void ThrowTypeLoadException(ExceptionStringID id, string typeName, string assemblyName) + { + throw new TypeSystemException.TypeLoadException(id, typeName, assemblyName); + } + + [System.Diagnostics.DebuggerHidden] + public static void ThrowMissingMethodException(TypeDesc owningType, string methodName, MethodSignature signature) + { + throw new TypeSystemException.MissingMethodException(ExceptionStringID.MissingMethod, Format.Method(owningType, methodName, signature)); + } + + [System.Diagnostics.DebuggerHidden] + public static void ThrowMissingFieldException(TypeDesc owningType, string fieldName) + { + throw new TypeSystemException.MissingFieldException(ExceptionStringID.MissingField, Format.Field(owningType, fieldName)); + } + + [System.Diagnostics.DebuggerHidden] + public static void ThrowFileNotFoundException(ExceptionStringID id, string fileName) + { + throw new TypeSystemException.FileNotFoundException(id, fileName); + } + + [System.Diagnostics.DebuggerHidden] + public static void ThrowInvalidProgramException() + { + throw new TypeSystemException.InvalidProgramException(); + } + + [System.Diagnostics.DebuggerHidden] + public static void ThrowInvalidProgramException(ExceptionStringID id, MethodDesc method) + { + throw new TypeSystemException.InvalidProgramException(id, Format.Method(method)); + } + + [System.Diagnostics.DebuggerHidden] + public static void ThrowBadImageFormatException() + { + throw new TypeSystemException.BadImageFormatException(); + } + + private static partial class Format + { + public static string OwningModule(TypeDesc type) + { + return Module((type as MetadataType)?.Module); + } + } + } +} diff --git a/external/corert/src/Common/src/TypeSystem/Common/TypeDesc.ToString.cs b/external/corert/src/Common/src/TypeSystem/Common/TypeDesc.ToString.cs new file mode 100644 index 0000000000..34e616fae4 --- /dev/null +++ b/external/corert/src/Common/src/TypeSystem/Common/TypeDesc.ToString.cs @@ -0,0 +1,14 @@ +// 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. + +namespace Internal.TypeSystem +{ + partial class TypeDesc + { + public override string ToString() + { + return DebugNameFormatter.Instance.FormatName(this, DebugNameFormatter.FormatOptions.Default); + } + } +} diff --git a/external/corert/src/Common/src/TypeSystem/Common/TypeDesc.cs b/external/corert/src/Common/src/TypeSystem/Common/TypeDesc.cs index 0e25eb56a9..a3bc755f22 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/TypeDesc.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/TypeDesc.cs @@ -453,7 +453,8 @@ namespace Internal.TypeSystem return field.FieldType; } - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, this); + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, this); + return null; // Unreachable } } @@ -616,5 +617,17 @@ namespace Internal.TypeSystem return HasInstantiation && IsTypeDefinition; } } + + /// + /// Gets a value indicating whether this is a byref-like type + /// (a TypedReference, Span<T>, etc.). + /// + public bool IsByRefLike + { + get + { + return (GetTypeFlags(TypeFlags.IsByRefLike | TypeFlags.IsByRefLikeComputed) & TypeFlags.IsByRefLike) != 0; + } + } } } diff --git a/external/corert/src/Common/src/TypeSystem/Common/TypeFlags.cs b/external/corert/src/Common/src/TypeSystem/Common/TypeFlags.cs index 09e57c19a2..c177cbe591 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/TypeFlags.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/TypeFlags.cs @@ -56,5 +56,8 @@ namespace Internal.TypeSystem HasFinalizerComputed = 0x1000, HasFinalizer = 0x2000, + + IsByRefLike = 0x4000, + IsByRefLikeComputed = 0x8000, } } diff --git a/external/corert/src/Common/src/TypeSystem/Common/TypeSystemConstaintsHelpers.cs b/external/corert/src/Common/src/TypeSystem/Common/TypeSystemConstaintsHelpers.cs deleted file mode 100644 index f48708aa70..0000000000 --- a/external/corert/src/Common/src/TypeSystem/Common/TypeSystemConstaintsHelpers.cs +++ /dev/null @@ -1,84 +0,0 @@ -// 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.Diagnostics; - -namespace Internal.TypeSystem -{ - public static class TypeSystemConstraintsHelpers - { - private static bool VerifyGenericParamConstraint(Instantiation typeInstantiation, Instantiation methodInstantiation, GenericParameterDesc genericParam, TypeDesc instantiationParam) - { - // Check class constraint - if (genericParam.HasReferenceTypeConstraint) - { - if (!instantiationParam.IsGCPointer) - return false; - } - - // Check default constructor constraint - if (genericParam.HasDefaultConstructorConstraint) - { - if (!instantiationParam.HasExplicitOrImplicitDefaultConstructor()) - return false; - } - - // Check struct constraint - if (genericParam.HasNotNullableValueTypeConstraint) - { - if (!instantiationParam.IsValueType) - return false; - - if (instantiationParam.IsNullable) - return false; - } - - foreach (var constraintType in genericParam.TypeConstraints) - { - var instantiatedType = constraintType.InstantiateSignature(typeInstantiation, methodInstantiation); - if (!instantiationParam.CanCastTo(instantiatedType)) - return false; - } - - return true; - } - - public static bool CheckConstraints(this TypeDesc type) - { - TypeDesc uninstantiatedType = type.GetTypeDefinition(); - - // Non-generic types always pass constraints check - if (uninstantiatedType == type) - return true; - - for (int i = 0; i < uninstantiatedType.Instantiation.Length; i++) - { - if (!VerifyGenericParamConstraint(type.Instantiation, default(Instantiation), (GenericParameterDesc)uninstantiatedType.Instantiation[i], type.Instantiation[i])) - return false; - } - - return true; - } - - public static bool CheckConstraints(this MethodDesc method) - { - if (!method.OwningType.CheckConstraints()) - return false; - - // Non-generic methods always pass constraints check - if (!method.HasInstantiation) - return true; - - MethodDesc uninstantiatedMethod = method.GetMethodDefinition(); - for (int i = 0; i < uninstantiatedMethod.Instantiation.Length; i++) - { - if (!VerifyGenericParamConstraint(method.OwningType.Instantiation, method.Instantiation, (GenericParameterDesc)uninstantiatedMethod.Instantiation[i], method.Instantiation[i])) - return false; - } - - return true; - } - } -} diff --git a/external/corert/src/Common/src/TypeSystem/Common/TypeSystemConstraintsHelpers.cs b/external/corert/src/Common/src/TypeSystem/Common/TypeSystemConstraintsHelpers.cs new file mode 100644 index 0000000000..d428fcfe9b --- /dev/null +++ b/external/corert/src/Common/src/TypeSystem/Common/TypeSystemConstraintsHelpers.cs @@ -0,0 +1,214 @@ +// 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.Collections.Generic; +using System.Diagnostics; + +namespace Internal.TypeSystem +{ + public class InstantiationContext + { + public readonly Instantiation TypeInstantiation; + public readonly Instantiation MethodInstantiation; + + public InstantiationContext(Instantiation typeInstantiation, Instantiation methodInstantiation) + { + TypeInstantiation = typeInstantiation; + MethodInstantiation = methodInstantiation; + } + } + + public static class TypeSystemConstraintsHelpers + { + private static bool VerifyGenericParamConstraint(InstantiationContext genericParamContext, GenericParameterDesc genericParam, + InstantiationContext instantiationParamContext, TypeDesc instantiationParam) + { + GenericConstraints constraints = genericParam.Constraints; + + // Check class constraint + if ((constraints & GenericConstraints.ReferenceTypeConstraint) != 0) + { + if (!instantiationParam.IsGCPointer + && !CheckGenericSpecialConstraint(instantiationParam, GenericConstraints.ReferenceTypeConstraint)) + return false; + } + + // Check default constructor constraint + if ((constraints & GenericConstraints.DefaultConstructorConstraint) != 0) + { + if (!instantiationParam.HasExplicitOrImplicitDefaultConstructor() + && !CheckGenericSpecialConstraint(instantiationParam, GenericConstraints.DefaultConstructorConstraint)) + return false; + } + + // Check struct constraint + if ((constraints & GenericConstraints.NotNullableValueTypeConstraint) != 0) + { + if ((!instantiationParam.IsValueType || instantiationParam.IsNullable) + && !CheckGenericSpecialConstraint(instantiationParam, GenericConstraints.NotNullableValueTypeConstraint)) + return false; + } + + var instantiatedConstraints = new ArrayBuilder(); + GetInstantiatedConstraintsRecursive(instantiationParamContext, instantiationParam, ref instantiatedConstraints); + + foreach (var constraintType in genericParam.TypeConstraints) + { + var instantiatedType = constraintType.InstantiateSignature(genericParamContext.TypeInstantiation, genericParamContext.MethodInstantiation); + if (CanCastConstraint(ref instantiatedConstraints, instantiatedType)) + continue; + + if (!instantiationParam.CanCastTo(instantiatedType)) + return false; + } + + return true; + } + + // Used to determine whether a type parameter used to instantiate another type parameter with a specific special + // constraint satisfies that constraint. + private static bool CheckGenericSpecialConstraint(TypeDesc type, GenericConstraints specialConstraint) + { + if (!type.IsGenericParameter) + return false; + + var genericType = (GenericParameterDesc)type; + + GenericConstraints constraints = genericType.Constraints; + + // Check if type has specialConstraint on its own + if ((constraints & specialConstraint) != 0) + return true; + + // Value type always has default constructor + if (specialConstraint == GenericConstraints.DefaultConstructorConstraint && (constraints & GenericConstraints.NotNullableValueTypeConstraint) != 0) + return true; + + // The special constraints did not match, check if there is a primary type constraint, + // that would always satisfy the special constraint + foreach (var constraint in genericType.TypeConstraints) + { + if (constraint.IsGenericParameter || constraint.IsInterface) + continue; + + switch (specialConstraint) + { + case GenericConstraints.NotNullableValueTypeConstraint: + if (constraint.IsValueType && !constraint.IsNullable) + return true; + break; + case GenericConstraints.ReferenceTypeConstraint: + if (!constraint.IsValueType) + return true; + break; + case GenericConstraints.DefaultConstructorConstraint: + // As constraint is only ancestor, can only be sure whether type has public default constructor if it is a value type + if (constraint.IsValueType) + return true; + break; + default: + Debug.Assert(false); + break; + } + } + + // type did not satisfy special constraint in any way + return false; + } + + private static void GetInstantiatedConstraintsRecursive(InstantiationContext typeContext, TypeDesc type, ref ArrayBuilder instantiatedConstraints) + { + if (!type.IsGenericParameter || typeContext == null) + return; + + GenericParameterDesc genericParam = (GenericParameterDesc)type; + + foreach (var constraint in genericParam.TypeConstraints) + { + var instantiatedType = constraint.InstantiateSignature(typeContext.TypeInstantiation, typeContext.MethodInstantiation); + + if (instantiatedType.IsGenericParameter) + { + // Make sure it is save to call this method recursively + if (!instantiatedConstraints.Contains(instantiatedType)) + { + instantiatedConstraints.Add(instantiatedType); + + // Constraints of this constraint apply to 'genericParam' too + GetInstantiatedConstraintsRecursive(typeContext, instantiatedType, ref instantiatedConstraints); + } + } + else + { + instantiatedConstraints.Add(instantiatedType); + } + } + } + + private static bool CanCastConstraint(ref ArrayBuilder instantiatedConstraints, TypeDesc instantiatedType) + { + for (int i = 0; i < instantiatedConstraints.Count; ++i) + { + if (instantiatedConstraints[i].CanCastTo(instantiatedType)) + return true; + } + + return false; + } + + public static bool CheckValidInstantiationArguments(this Instantiation instantiation) + { + foreach(var arg in instantiation) + { + if (arg.IsPointer || arg.IsByRef || arg.IsGenericParameter || arg.IsVoid) + return false; + + if (arg.HasInstantiation) + { + if (!CheckValidInstantiationArguments(arg.Instantiation)) + return false; + } + } + return true; + } + + public static bool CheckConstraints(this TypeDesc type, InstantiationContext context = null) + { + TypeDesc uninstantiatedType = type.GetTypeDefinition(); + + // Non-generic types always pass constraints check + if (uninstantiatedType == type) + return true; + + var paramContext = new InstantiationContext(type.Instantiation, default(Instantiation)); + for (int i = 0; i < uninstantiatedType.Instantiation.Length; i++) + { + if (!VerifyGenericParamConstraint(paramContext, (GenericParameterDesc)uninstantiatedType.Instantiation[i], context, type.Instantiation[i])) + return false; + } + + return true; + } + + public static bool CheckConstraints(this MethodDesc method, InstantiationContext context = null) + { + if (!method.OwningType.CheckConstraints(context)) + return false; + + // Non-generic methods always pass constraints check + if (!method.HasInstantiation) + return true; + + var paramContext = new InstantiationContext(method.OwningType.Instantiation, method.Instantiation); + MethodDesc uninstantiatedMethod = method.GetMethodDefinition(); + for (int i = 0; i < uninstantiatedMethod.Instantiation.Length; i++) + { + if (!VerifyGenericParamConstraint(paramContext, (GenericParameterDesc)uninstantiatedMethod.Instantiation[i], context, method.Instantiation[i])) + return false; + } + + return true; + } + } +} diff --git a/external/corert/src/Common/src/TypeSystem/Common/TypeSystemException.cs b/external/corert/src/Common/src/TypeSystem/Common/TypeSystemException.cs index 972c8aed3c..afe5ebf7bb 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/TypeSystemException.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/TypeSystemException.cs @@ -4,9 +4,6 @@ using System; using System.Collections.Generic; -using System.Text; - -using Debug = System.Diagnostics.Debug; namespace Internal.TypeSystem { @@ -41,7 +38,7 @@ namespace Internal.TypeSystem } } - public TypeSystemException(ExceptionStringID id, params string[] args) + internal TypeSystemException(ExceptionStringID id, params string[] args) { StringID = id; _arguments = args; @@ -62,44 +59,19 @@ namespace Internal.TypeSystem public string AssemblyName { get; } - private TypeLoadException(ExceptionStringID id, string typeName, string assemblyName, string messageArg) + internal TypeLoadException(ExceptionStringID id, string typeName, string assemblyName, string messageArg) : base(id, new string[] { typeName, assemblyName, messageArg }) { TypeName = typeName; AssemblyName = assemblyName; } - private TypeLoadException(ExceptionStringID id, string typeName, string assemblyName) + internal TypeLoadException(ExceptionStringID id, string typeName, string assemblyName) : base(id, new string[] { typeName, assemblyName }) { TypeName = typeName; AssemblyName = assemblyName; } - - public TypeLoadException(string nestedTypeName, ModuleDesc module) - : this(ExceptionStringID.ClassLoadGeneral, nestedTypeName, Format.Module(module)) - { - } - - public TypeLoadException(string @namespace, string name, ModuleDesc module) - : this(ExceptionStringID.ClassLoadGeneral, Format.Type(@namespace, name), Format.Module(module)) - { - } - - public TypeLoadException(ExceptionStringID id, MethodDesc method) - : this(id, Format.Type(method.OwningType), Format.OwningModule(method), Format.Method(method)) - { - } - - public TypeLoadException(ExceptionStringID id, TypeDesc type, string messageArg) - : this(id, Format.Type(type), Format.OwningModule(type), messageArg) - { - } - - public TypeLoadException(ExceptionStringID id, TypeDesc type) - : this(id, Format.Type(type), Format.OwningModule(type)) - { - } } /// @@ -119,15 +91,10 @@ namespace Internal.TypeSystem /// public class MissingMethodException : MissingMemberException { - public MissingMethodException(ExceptionStringID id, params string[] args) + internal MissingMethodException(ExceptionStringID id, params string[] args) : base(id, args) { } - - public MissingMethodException(TypeDesc owningType, string methodName, MethodSignature signature) - : this(ExceptionStringID.MissingMethod, Format.Method(owningType, methodName, signature)) - { - } } /// @@ -135,15 +102,10 @@ namespace Internal.TypeSystem /// public class MissingFieldException : MissingMemberException { - public MissingFieldException(ExceptionStringID id, params string[] args) + internal MissingFieldException(ExceptionStringID id, params string[] args) : base(id, args) { } - - public MissingFieldException(TypeDesc owningType, string fieldName) - : this(ExceptionStringID.MissingField, Format.Field(owningType, fieldName)) - { - } } /// @@ -151,7 +113,7 @@ namespace Internal.TypeSystem /// public class FileNotFoundException : TypeSystemException { - public FileNotFoundException(ExceptionStringID id, string fileName) + internal FileNotFoundException(ExceptionStringID id, string fileName) : base(id, fileName) { } @@ -163,212 +125,23 @@ namespace Internal.TypeSystem /// public class InvalidProgramException : TypeSystemException { - public InvalidProgramException(ExceptionStringID id, MethodDesc method) - : base(id, Format.Method(method)) + internal InvalidProgramException(ExceptionStringID id, string method) + : base(id, method) + { + } + + internal InvalidProgramException() + : base(ExceptionStringID.InvalidProgramDefault) { } } public class BadImageFormatException : TypeSystemException { - public BadImageFormatException() + internal BadImageFormatException() : base(ExceptionStringID.BadImageFormatGeneric) { } } - - #region Formatting helpers - - private static class Format - { - public static string OwningModule(MethodDesc method) - { - return OwningModule(method.OwningType); - } - - public static string OwningModule(TypeDesc type) - { - return Module((type as MetadataType)?.Module); - } - - public static string Module(ModuleDesc module) - { - if (module == null) - return "?"; - - IAssemblyDesc assembly = module as IAssemblyDesc; - if (assembly != null) - { - return assembly.GetName().FullName; - } - else - { - Debug.Assert(false, "Multi-module assemblies"); - return module.ToString(); - } - } - - public static string Type(TypeDesc type) - { - return ExceptionTypeNameFormatter.Instance.FormatName(type); - } - - public static string Type(string @namespace, string name) - { - return String.IsNullOrEmpty(@namespace) ? name : @namespace + "." + name; - } - - public static string Field(TypeDesc owningType, string fieldName) - { - return Type(owningType) + "." + fieldName; - } - - public static string Method(MethodDesc method) - { - return Method(method.OwningType, method.Name, method.Signature); - } - - public static string Method(TypeDesc owningType, string methodName, MethodSignature signature) - { - StringBuilder sb = new StringBuilder(); - - if (signature != null) - { - sb.Append(ExceptionTypeNameFormatter.Instance.FormatName(signature.ReturnType)); - sb.Append(' '); - } - - sb.Append(ExceptionTypeNameFormatter.Instance.FormatName(owningType)); - sb.Append('.'); - sb.Append(methodName); - - if (signature != null) - { - sb.Append('('); - for (int i = 0; i < signature.Length; i++) - { - if (i > 0) - { - sb.Append(", "); - } - - sb.Append(ExceptionTypeNameFormatter.Instance.FormatName(signature[i])); - } - sb.Append(')'); - } - - return sb.ToString(); - } - - /// - /// Provides a name formatter that is compatible with SigFormat.cpp in the CLR. - /// - private class ExceptionTypeNameFormatter : TypeNameFormatter - { - public static ExceptionTypeNameFormatter Instance { get; } = new ExceptionTypeNameFormatter(); - - public override void AppendName(StringBuilder sb, PointerType type) - { - AppendName(sb, type.ParameterType); - sb.Append('*'); - } - - public override void AppendName(StringBuilder sb, GenericParameterDesc type) - { - string prefix = type.Kind == GenericParameterKind.Type ? "!" : "!!"; - sb.Append(prefix); - sb.Append(type.Name); - } - - public override void AppendName(StringBuilder sb, SignatureTypeVariable type) - { - sb.Append("!"); - sb.Append(type.Index.ToStringInvariant()); - } - - public override void AppendName(StringBuilder sb, SignatureMethodVariable type) - { - sb.Append("!!"); - sb.Append(type.Index.ToStringInvariant()); - } - - public override void AppendName(StringBuilder sb, FunctionPointerType type) - { - MethodSignature signature = type.Signature; - - AppendName(sb, signature.ReturnType); - - sb.Append(" ("); - for (int i = 0; i < signature.Length; i++) - { - if (i > 0) - sb.Append(", "); - AppendName(sb, signature[i]); - } - - // TODO: Append '...' for vararg methods - - sb.Append(')'); - } - - public override void AppendName(StringBuilder sb, ByRefType type) - { - AppendName(sb, type.ParameterType); - sb.Append(" ByRef"); - } - - public override void AppendName(StringBuilder sb, ArrayType type) - { - AppendName(sb, type.ElementType); - sb.Append('['); - - // NOTE: We're ignoring difference between SzArray and MdArray rank 1 for SigFormat.cpp compat. - sb.Append(',', type.Rank - 1); - - sb.Append(']'); - } - - protected override void AppendNameForInstantiatedType(StringBuilder sb, DefType type) - { - AppendName(sb, type.GetTypeDefinition()); - sb.Append('<'); - - for (int i = 0; i < type.Instantiation.Length; i++) - { - if (i > 0) - sb.Append(", "); - AppendName(sb, type.Instantiation[i]); - } - - sb.Append('>'); - } - - protected override void AppendNameForNamespaceType(StringBuilder sb, DefType type) - { - if (type.IsPrimitive) - { - sb.Append(type.Name); - } - else - { - string ns = type.Namespace; - if (ns.Length > 0) - { - sb.Append(ns); - sb.Append('.'); - } - sb.Append(type.Name); - } - } - - protected override void AppendNameForNestedType(StringBuilder sb, DefType nestedType, DefType containingType) - { - // NOTE: We're ignoring the containing type for compatiblity with SigFormat.cpp - sb.Append(nestedType.Name); - } - } - } - - #endregion } } diff --git a/external/corert/src/Common/src/TypeSystem/Common/TypeSystemHelpers.cs b/external/corert/src/Common/src/TypeSystem/Common/TypeSystemHelpers.cs index 42ef2c7365..028c2955d6 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/TypeSystemHelpers.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/TypeSystemHelpers.cs @@ -153,7 +153,7 @@ namespace Internal.TypeSystem targetOrBase = targetOrBase.BaseType; } while (targetOrBase != null); - Debug.Assert(false, "method has no related type in the type hierarchy of type"); + Debug.Fail("method has no related type in the type hierarchy of type"); return null; } @@ -271,6 +271,11 @@ namespace Internal.TypeSystem return type.Context.GetVirtualMethodAlgorithmForType(type).ResolveInterfaceMethodToVirtualMethodOnType(interfaceMethod, type); } + public static MethodDesc ResolveVariantInterfaceMethodToVirtualMethodOnType(this TypeDesc type, MethodDesc interfaceMethod) + { + return type.Context.GetVirtualMethodAlgorithmForType(type).ResolveVariantInterfaceMethodToVirtualMethodOnType(interfaceMethod, type); + } + /// /// Resolves a virtual method call. /// diff --git a/external/corert/src/Common/src/TypeSystem/Common/UniversalCanonLayoutAlgorithm.cs b/external/corert/src/Common/src/TypeSystem/Common/UniversalCanonLayoutAlgorithm.cs index d8e8b834ed..f6c475abd5 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/UniversalCanonLayoutAlgorithm.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/UniversalCanonLayoutAlgorithm.cs @@ -22,11 +22,6 @@ namespace Internal.TypeSystem throw new NotSupportedException(); } - public override bool ComputeIsByRefLike(DefType type) - { - return false; - } - public override DefType ComputeHomogeneousFloatAggregateElementType(DefType type) { return null; diff --git a/external/corert/src/Common/src/TypeSystem/Common/Utilities/CustomAttributeTypeNameFormatter.cs b/external/corert/src/Common/src/TypeSystem/Common/Utilities/CustomAttributeTypeNameFormatter.cs new file mode 100644 index 0000000000..864a7b3220 --- /dev/null +++ b/external/corert/src/Common/src/TypeSystem/Common/Utilities/CustomAttributeTypeNameFormatter.cs @@ -0,0 +1,187 @@ +// 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.Text; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.TypeSystem +{ + /// + /// Formats type names in the 'SerString' format as defined by the ECMA-335 standard. + /// This is the inverse of what does. + /// + public sealed class CustomAttributeTypeNameFormatter : TypeNameFormatter + { + private readonly IAssemblyDesc _relativeHomeAssembly; + + public CustomAttributeTypeNameFormatter() + { + } + + public CustomAttributeTypeNameFormatter(IAssemblyDesc relativeHomeAssembly) + { + _relativeHomeAssembly = relativeHomeAssembly; + } + + private void AppendAssemblyName(StringBuilder sb, IAssemblyDesc assembly) + { + if (assembly == _relativeHomeAssembly || assembly == null) + return; + + sb.Append(','); + AppendEscapedIdentifier(sb, assembly.GetName().Name); + } + + public override IAssemblyDesc AppendName(StringBuilder sb, ArrayType type, bool assemblyQualify) + { + IAssemblyDesc homeAssembly = AppendName(sb, type.ElementType, false); + + if (type.IsSzArray) + { + sb.Append("[]"); + } + else if (type.Rank == 1) + { + sb.Append("[*]"); + } + else + { + sb.Append('['); + sb.Append(',', type.Rank - 1); + sb.Append(']'); + } + + if (assemblyQualify) + AppendAssemblyName(sb, homeAssembly); + + return homeAssembly; + } + + public override IAssemblyDesc AppendName(StringBuilder sb, ByRefType type, bool assemblyQualify) + { + IAssemblyDesc homeAssembly = AppendName(sb, type.ParameterType, false); + + sb.Append('&'); + + if (assemblyQualify) + AppendAssemblyName(sb, homeAssembly); + + return homeAssembly; + } + + public override IAssemblyDesc AppendName(StringBuilder sb, PointerType type, bool assemblyQualify) + { + IAssemblyDesc homeAssembly = AppendName(sb, type.ParameterType, false); + + sb.Append('*'); + + if (assemblyQualify) + AppendAssemblyName(sb, homeAssembly); + + return homeAssembly; + } + + public override IAssemblyDesc AppendName(StringBuilder sb, FunctionPointerType type, bool assemblyQualify) + { + throw new NotSupportedException(); + } + + public override IAssemblyDesc AppendName(StringBuilder sb, GenericParameterDesc type, bool assemblyQualify) + { + throw new NotSupportedException(); + } + + public override IAssemblyDesc AppendName(StringBuilder sb, SignatureMethodVariable type, bool assemblyQualify) + { + throw new NotSupportedException(); + } + + public override IAssemblyDesc AppendName(StringBuilder sb, SignatureTypeVariable type, bool assemblyQualify) + { + throw new NotSupportedException(); + } + + protected override IAssemblyDesc AppendNameForInstantiatedType(StringBuilder sb, DefType type, bool assemblyQualify) + { + IAssemblyDesc homeAssembly = AppendName(sb, type.GetTypeDefinition(), false); + + sb.Append('['); + + for (int i = 0; i < type.Instantiation.Length; i++) + { + if (i != 0) + sb.Append(','); + + sb.Append('['); + AppendName(sb, type.Instantiation[i], true); + sb.Append(']'); + } + + sb.Append(']'); + + if (assemblyQualify) + AppendAssemblyName(sb, homeAssembly); + + return homeAssembly; + } + + protected override IAssemblyDesc AppendNameForNamespaceType(StringBuilder sb, DefType type, bool assemblyQualify) + { + string ns = type.Namespace; + if (ns.Length > 0) + { + AppendEscapedIdentifier(sb, ns); + sb.Append('.'); + } + AppendEscapedIdentifier(sb, type.Name); + + if (type is MetadataType mdType) + { + Debug.Assert(mdType.Module is IAssemblyDesc, "Multi-module?"); + + if (assemblyQualify) + AppendAssemblyName(sb, (IAssemblyDesc)mdType.Module); + + return (IAssemblyDesc)mdType.Module; + } + + return null; + } + + protected override IAssemblyDesc AppendNameForNestedType(StringBuilder sb, DefType nestedType, DefType containingType, bool assemblyQualify) + { + IAssemblyDesc homeAssembly = AppendName(sb, containingType, false); + + sb.Append('+'); + + AppendEscapedIdentifier(sb, nestedType.Name); + + if (assemblyQualify) + AppendAssemblyName(sb, homeAssembly); + + return homeAssembly; + } + + private static char[] s_escapedChars = new char[] { ',', '=', '"', ']', '[', '*', '&', '+', '\\' }; + private void AppendEscapedIdentifier(StringBuilder sb, string identifier) + { + if (identifier.IndexOfAny(s_escapedChars) < 0) + { + string escapedIdentifier = identifier; + foreach (char escapedChar in s_escapedChars) + { + string escapedCharString = new string(escapedChar, 1); + escapedIdentifier = escapedIdentifier.Replace(escapedCharString, "\\" + escapedCharString); + } + sb.Append(escapedIdentifier); + } + else + { + sb.Append(identifier); + } + } + } +} diff --git a/external/corert/src/Common/src/TypeSystem/Common/Utilities/CustomAttributeTypeNameParser.cs b/external/corert/src/Common/src/TypeSystem/Common/Utilities/CustomAttributeTypeNameParser.cs index 092397226e..1bea19a91a 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/Utilities/CustomAttributeTypeNameParser.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/Utilities/CustomAttributeTypeNameParser.cs @@ -20,8 +20,9 @@ namespace Internal.TypeSystem /// /// Parses the string '' and returns the type corresponding to the parsed type name. /// The type name string should be in the 'SerString' format as defined by the ECMA-335 standard. + /// This is the inverse of what does. /// - public static TypeDesc GetTypeByCustomAttributeTypeName(this ModuleDesc module, string name, bool throwIfNotFound = true) + public static TypeDesc GetTypeByCustomAttributeTypeName(this ModuleDesc module, string name, bool throwIfNotFound = true, Func resolver = null) { TypeDesc loadedType; @@ -71,7 +72,8 @@ namespace Internal.TypeSystem { homeModule = module.Context.ResolveAssembly(homeAssembly); } - MetadataType typeDef = ResolveCustomAttributeTypeNameToTypeDesc(genericTypeDefName.ToString(), homeModule, throwIfNotFound); + MetadataType typeDef = resolver != null ? resolver(genericTypeDefName.ToString(), homeModule, throwIfNotFound) : + ResolveCustomAttributeTypeDefinitionName(genericTypeDefName.ToString(), homeModule, throwIfNotFound); if (typeDef == null) return null; @@ -103,7 +105,7 @@ namespace Internal.TypeSystem ch += argLen; } - TypeDesc argType = module.GetTypeByCustomAttributeTypeName(typeArgName, throwIfNotFound); + TypeDesc argType = module.GetTypeByCustomAttributeTypeName(typeArgName, throwIfNotFound, resolver); if (argType == null) return null; genericArgs.Add(argType); @@ -189,7 +191,7 @@ namespace Internal.TypeSystem } - private static MetadataType ResolveCustomAttributeTypeNameToTypeDesc(string name, ModuleDesc module, bool throwIfNotFound) + public static MetadataType ResolveCustomAttributeTypeDefinitionName(string name, ModuleDesc module, bool throwIfNotFound) { MetadataType containingType = null; StringBuilder typeName = new StringBuilder(name.Length); @@ -228,9 +230,9 @@ namespace Internal.TypeSystem if (containingType == null) { if (throwIfNotFound) - throw new TypeSystemException.TypeLoadException(typeName.ToString(), outerType.Module); - else - return null; + ThrowHelper.ThrowTypeLoadException(typeName.ToString(), outerType.Module); + + return null; } } else @@ -250,7 +252,7 @@ namespace Internal.TypeSystem { MetadataType type = containingType.GetNestedType(typeName.ToString()); if ((type == null) && throwIfNotFound) - throw new TypeSystemException.TypeLoadException(typeName.ToString(), containingType.Module); + ThrowHelper.ThrowTypeLoadException(typeName.ToString(), containingType.Module); return type; } diff --git a/external/corert/src/Common/src/TypeSystem/Common/Utilities/DebugNameFormatter.cs b/external/corert/src/Common/src/TypeSystem/Common/Utilities/DebugNameFormatter.cs new file mode 100644 index 0000000000..c510525435 --- /dev/null +++ b/external/corert/src/Common/src/TypeSystem/Common/Utilities/DebugNameFormatter.cs @@ -0,0 +1,234 @@ +// 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.Text; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.TypeSystem +{ + public class DebugNameFormatter : TypeNameFormatter + { + public static readonly DebugNameFormatter Instance = new DebugNameFormatter(); + + public override Void AppendName(StringBuilder sb, ArrayType type, FormatOptions options) + { + AppendName(sb, type.ElementType, options); + + if (!type.IsSzArray && type.Rank == 1) + { + sb.Append("[*]"); + } + else + { + sb.Append('['); + sb.Append(',', type.Rank - 1); + sb.Append(']'); + } + + return Void.Value; + } + + public override Void AppendName(StringBuilder sb, ByRefType type, FormatOptions options) + { + AppendName(sb, type.ParameterType, options); + sb.Append('&'); + + return Void.Value; + } + + public override Void AppendName(StringBuilder sb, PointerType type, FormatOptions options) + { + AppendName(sb, type.ParameterType, options); + sb.Append('*'); + + return Void.Value; + } + + public override Void AppendName(StringBuilder sb, FunctionPointerType type, FormatOptions options) + { + MethodSignature signature = type.Signature; + + sb.Append("(*"); + AppendName(sb, signature.ReturnType, options); + sb.Append(")("); + for (int i = 0; i < signature.Length; i++) + { + if (i > 0) + sb.Append(','); + AppendName(sb, signature[i], options); + } + sb.Append(')'); + + return Void.Value; + } + + public override Void AppendName(StringBuilder sb, GenericParameterDesc type, FormatOptions options) + { + sb.Append(type.Name); + + return Void.Value; + } + + public override Void AppendName(StringBuilder sb, SignatureMethodVariable type, FormatOptions options) + { + sb.Append("!!"); + sb.Append(type.Index); + + return Void.Value; + } + + public override Void AppendName(StringBuilder sb, SignatureTypeVariable type, FormatOptions options) + { + sb.Append("!"); + sb.Append(type.Index); + + return Void.Value; + } + + protected override Void AppendNameForNestedType(StringBuilder sb, DefType nestedType, DefType containingType, FormatOptions options) + { + if ((options & FormatOptions.NamespaceQualify) != 0) + { + AppendName(sb, containingType, options); + sb.Append('+'); + } + + sb.Append(nestedType.Name); + + return Void.Value; + } + + protected override Void AppendNameForNamespaceType(StringBuilder sb, DefType type, FormatOptions options) + { + // Shortcut some of the well known types + switch (type.Category) + { + case TypeFlags.Void: + sb.Append("void"); + return Void.Value; + case TypeFlags.Boolean: + sb.Append("bool"); + return Void.Value; + case TypeFlags.Char: + sb.Append("char"); + return Void.Value; + case TypeFlags.SByte: + sb.Append("int8"); + return Void.Value; + case TypeFlags.Byte: + sb.Append("uint8"); + return Void.Value; + case TypeFlags.Int16: + sb.Append("int16"); + return Void.Value; + case TypeFlags.UInt16: + sb.Append("uint16"); + return Void.Value; + case TypeFlags.Int32: + sb.Append("int32"); + return Void.Value; + case TypeFlags.UInt32: + sb.Append("uint32"); + return Void.Value; + case TypeFlags.Int64: + sb.Append("int64"); + return Void.Value; + case TypeFlags.UInt64: + sb.Append("uint64"); + return Void.Value; + case TypeFlags.IntPtr: + sb.Append("native int"); + return Void.Value; + case TypeFlags.UIntPtr: + sb.Append("native uint"); + return Void.Value; + case TypeFlags.Single: + sb.Append("float32"); + return Void.Value; + case TypeFlags.Double: + sb.Append("float64"); + return Void.Value; + } + + if (type.IsString) + { + sb.Append("string"); + return Void.Value; + } + + if (type.IsObject) + { + sb.Append("object"); + return Void.Value; + } + + if (((options & FormatOptions.AssemblyQualify) != 0) + && type is MetadataType mdType + && mdType.Module is IAssemblyDesc) + { + sb.Append('['); + + // Trim the "System.Private." prefix + string assemblyName = ((IAssemblyDesc)mdType.Module).GetName().Name; + if (assemblyName.StartsWith("System.Private")) + assemblyName = "S.P" + assemblyName.Substring(14); + + sb.Append(assemblyName); + sb.Append(']'); + } + + if ((options & FormatOptions.NamespaceQualify) != 0) + { + string ns = type.Namespace; + if (!string.IsNullOrEmpty(ns)) + { + sb.Append(ns); + sb.Append('.'); + } + } + + sb.Append(type.Name); + + return Void.Value; + } + + protected override Void AppendNameForInstantiatedType(StringBuilder sb, DefType type, FormatOptions options) + { + AppendName(sb, type.GetTypeDefinition(), options); + + FormatOptions parameterOptions = options & ~FormatOptions.AssemblyQualify; + + sb.Append('<'); + + for (int i = 0; i < type.Instantiation.Length; i++) + { + if (i != 0) + sb.Append(','); + + AppendName(sb, type.Instantiation[i], parameterOptions); + } + + sb.Append('>'); + + return Void.Value; + } + + public struct Void + { + public static Void Value => default(Void); + } + + [Flags] + public enum FormatOptions + { + None = 0, + AssemblyQualify = 0x1, + NamespaceQualify = 0x2, + + Default = AssemblyQualify | NamespaceQualify, + } + } +} diff --git a/external/corert/src/Common/src/TypeSystem/Common/Utilities/ExceptionTypeNameFormatter.Metadata.cs b/external/corert/src/Common/src/TypeSystem/Common/Utilities/ExceptionTypeNameFormatter.Metadata.cs new file mode 100644 index 0000000000..f79364bfd1 --- /dev/null +++ b/external/corert/src/Common/src/TypeSystem/Common/Utilities/ExceptionTypeNameFormatter.Metadata.cs @@ -0,0 +1,20 @@ +// 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. + +namespace Internal.TypeSystem +{ + // Type name formatting functionality that relies on metadata. + partial class ExceptionTypeNameFormatter + { + private string GetTypeName(DefType type) + { + return type.Name; + } + + private string GetTypeNamespace(DefType type) + { + return type.Namespace; + } + } +} diff --git a/external/corert/src/Common/src/TypeSystem/Common/Utilities/ExceptionTypeNameFormatter.cs b/external/corert/src/Common/src/TypeSystem/Common/Utilities/ExceptionTypeNameFormatter.cs new file mode 100644 index 0000000000..4e7447cc4a --- /dev/null +++ b/external/corert/src/Common/src/TypeSystem/Common/Utilities/ExceptionTypeNameFormatter.cs @@ -0,0 +1,118 @@ +// 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.Text; + +namespace Internal.TypeSystem +{ + /// + /// Provides a name formatter that is compatible with SigFormat.cpp in the CLR. + /// + public partial class ExceptionTypeNameFormatter : TypeNameFormatter + { + public static ExceptionTypeNameFormatter Instance { get; } = new ExceptionTypeNameFormatter(); + + public override void AppendName(StringBuilder sb, PointerType type) + { + AppendName(sb, type.ParameterType); + sb.Append('*'); + } + + public override void AppendName(StringBuilder sb, GenericParameterDesc type) + { + string prefix = type.Kind == GenericParameterKind.Type ? "!" : "!!"; + sb.Append(prefix); + sb.Append(type.Name); + } + + public override void AppendName(StringBuilder sb, SignatureTypeVariable type) + { + sb.Append("!"); + sb.Append(type.Index.ToStringInvariant()); + } + + public override void AppendName(StringBuilder sb, SignatureMethodVariable type) + { + sb.Append("!!"); + sb.Append(type.Index.ToStringInvariant()); + } + + public override void AppendName(StringBuilder sb, FunctionPointerType type) + { + MethodSignature signature = type.Signature; + + AppendName(sb, signature.ReturnType); + + sb.Append(" ("); + for (int i = 0; i < signature.Length; i++) + { + if (i > 0) + sb.Append(", "); + AppendName(sb, signature[i]); + } + + // TODO: Append '...' for vararg methods + + sb.Append(')'); + } + + public override void AppendName(StringBuilder sb, ByRefType type) + { + AppendName(sb, type.ParameterType); + sb.Append(" ByRef"); + } + + public override void AppendName(StringBuilder sb, ArrayType type) + { + AppendName(sb, type.ElementType); + sb.Append('['); + + // NOTE: We're ignoring difference between SzArray and MdArray rank 1 for SigFormat.cpp compat. + sb.Append(',', type.Rank - 1); + + sb.Append(']'); + } + + protected override void AppendNameForInstantiatedType(StringBuilder sb, DefType type) + { + AppendName(sb, type.GetTypeDefinition()); + sb.Append('<'); + + for (int i = 0; i < type.Instantiation.Length; i++) + { + if (i > 0) + sb.Append(", "); + AppendName(sb, type.Instantiation[i]); + } + + sb.Append('>'); + } + + protected override void AppendNameForNamespaceType(StringBuilder sb, DefType type) + { + if (type.IsPrimitive) + { + sb.Append(GetTypeName(type)); + } + else + { + string ns = GetTypeNamespace(type); + if (ns.Length > 0) + { + sb.Append(ns); + sb.Append('.'); + } + sb.Append(GetTypeName(type)); + } + } + + protected override void AppendNameForNestedType(StringBuilder sb, DefType nestedType, DefType containingType) + { + // NOTE: We're ignoring the containing type for compatiblity with SigFormat.cpp + sb.Append(GetTypeName(nestedType)); + } + } +} + diff --git a/external/corert/src/Common/src/TypeSystem/Common/Utilities/GCPointerMap.cs b/external/corert/src/Common/src/TypeSystem/Common/Utilities/GCPointerMap.cs index 58d7c8c691..342e1f834d 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/Utilities/GCPointerMap.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/Utilities/GCPointerMap.cs @@ -13,7 +13,7 @@ namespace Internal.TypeSystem /// Represents a bitmap of GC pointers within a memory region divided into /// pointer-sized cells. /// - public partial struct GCPointerMap : IEquatable + public partial struct GCPointerMap : IEquatable, IComparable { // Each bit in this array represents a pointer-sized cell. private int[] _gcFlags; @@ -130,6 +130,21 @@ namespace Internal.TypeSystem sb.Append(bit ? '1' : '0'); return sb.ToString(); } + + public int CompareTo(GCPointerMap other) + { + if (_numCells != other._numCells) + return _numCells - other._numCells; + + for (int i = 0; i < _gcFlags.Length; i++) + { + if (_gcFlags[i] != other._gcFlags[i]) + return _gcFlags[i] - other._gcFlags[i]; + } + + Debug.Assert(Equals(other)); + return 0; + } } /// diff --git a/external/corert/src/Common/src/TypeSystem/Common/Utilities/LockFreeReaderHashtable.cs b/external/corert/src/Common/src/TypeSystem/Common/Utilities/LockFreeReaderHashtable.cs index 17a0cf4ffd..f9b929546c 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/Utilities/LockFreeReaderHashtable.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/Utilities/LockFreeReaderHashtable.cs @@ -547,7 +547,7 @@ namespace Internal.TypeSystem public void Reset() { - throw new NotImplementedException(); + throw new NotSupportedException(); } public TValue Current @@ -562,7 +562,7 @@ namespace Internal.TypeSystem { get { - throw new NotImplementedException(); + throw new NotSupportedException(); } } } diff --git a/external/corert/src/Common/src/TypeSystem/Common/Utilities/LockFreeReaderHashtableOfPointers.cs b/external/corert/src/Common/src/TypeSystem/Common/Utilities/LockFreeReaderHashtableOfPointers.cs index d3746c3487..45394b63b6 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/Utilities/LockFreeReaderHashtableOfPointers.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/Utilities/LockFreeReaderHashtableOfPointers.cs @@ -569,7 +569,7 @@ namespace Internal.TypeSystem public void Reset() { - throw new NotImplementedException(); + throw new NotSupportedException(); } public TValue Current @@ -584,7 +584,7 @@ namespace Internal.TypeSystem { get { - throw new NotImplementedException(); + throw new NotSupportedException(); } } } diff --git a/external/corert/src/Common/src/TypeSystem/Common/Utilities/TypeNameFormatter.cs b/external/corert/src/Common/src/TypeSystem/Common/Utilities/TypeNameFormatter.cs index ae11174e97..46533317b6 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/Utilities/TypeNameFormatter.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/Utilities/TypeNameFormatter.cs @@ -75,71 +75,74 @@ namespace Internal.TypeSystem protected abstract void AppendNameForNamespaceType(StringBuilder sb, DefType type); protected abstract void AppendNameForInstantiatedType(StringBuilder sb, DefType type); - #region Convenience methods - public string FormatName(TypeDesc type) { StringBuilder sb = new StringBuilder(); AppendName(sb, type); return sb.ToString(); } + } - public string FormatName(DefType type) + public abstract class TypeNameFormatter + { + public TState AppendName(StringBuilder sb, TypeDesc type, TOptions options) { - StringBuilder sb = new StringBuilder(); - AppendName(sb, type); - return sb.ToString(); + switch (type.Category) + { + case TypeFlags.Array: + case TypeFlags.SzArray: + return AppendName(sb, (ArrayType)type, options); + case TypeFlags.ByRef: + return AppendName(sb, (ByRefType)type, options); + case TypeFlags.Pointer: + return AppendName(sb, (PointerType)type, options); + case TypeFlags.FunctionPointer: + return AppendName(sb, (FunctionPointerType)type, options); + case TypeFlags.GenericParameter: + return AppendName(sb, (GenericParameterDesc)type, options); + case TypeFlags.SignatureTypeVariable: + return AppendName(sb, (SignatureTypeVariable)type, options); + case TypeFlags.SignatureMethodVariable: + return AppendName(sb, (SignatureMethodVariable)type, options); + default: + Debug.Assert(type.IsDefType); + return AppendName(sb, (DefType)type, options); + } } - public string FormatName(ArrayType type) + public TState AppendName(StringBuilder sb, DefType type, TOptions options) { - StringBuilder sb = new StringBuilder(); - AppendName(sb, type); - return sb.ToString(); + if (!type.IsTypeDefinition) + { + return AppendNameForInstantiatedType(sb, type, options); + } + else + { + DefType containingType = type.ContainingType; + if (containingType != null) + return AppendNameForNestedType(sb, type, containingType, options); + else + return AppendNameForNamespaceType(sb, type, options); + } } - public string FormatName(ByRefType type) + public abstract TState AppendName(StringBuilder sb, ArrayType type, TOptions options); + public abstract TState AppendName(StringBuilder sb, ByRefType type, TOptions options); + public abstract TState AppendName(StringBuilder sb, PointerType type, TOptions options); + public abstract TState AppendName(StringBuilder sb, FunctionPointerType type, TOptions options); + public abstract TState AppendName(StringBuilder sb, GenericParameterDesc type, TOptions options); + public abstract TState AppendName(StringBuilder sb, SignatureMethodVariable type, TOptions options); + public abstract TState AppendName(StringBuilder sb, SignatureTypeVariable type, TOptions options); + + protected abstract TState AppendNameForNestedType(StringBuilder sb, DefType nestedType, DefType containingType, TOptions options); + protected abstract TState AppendNameForNamespaceType(StringBuilder sb, DefType type, TOptions options); + protected abstract TState AppendNameForInstantiatedType(StringBuilder sb, DefType type, TOptions options); + + public string FormatName(TypeDesc type, TOptions options) { StringBuilder sb = new StringBuilder(); - AppendName(sb, type); + AppendName(sb, type, options); return sb.ToString(); } - - public string FormatName(PointerType type) - { - StringBuilder sb = new StringBuilder(); - AppendName(sb, type); - return sb.ToString(); - } - - public string FormatName(InstantiatedType type) - { - StringBuilder sb = new StringBuilder(); - AppendNameForInstantiatedType(sb, type); - return sb.ToString(); - } - - public string FormatName(GenericParameterDesc type) - { - StringBuilder sb = new StringBuilder(); - AppendName(sb, type); - return sb.ToString(); - } - - public string FormatName(SignatureMethodVariable type) - { - StringBuilder sb = new StringBuilder(); - AppendName(sb, type); - return sb.ToString(); - } - - public string FormatName(SignatureTypeVariable type) - { - StringBuilder sb = new StringBuilder(); - AppendName(sb, type); - return sb.ToString(); - } - - #endregion } } diff --git a/external/corert/src/Common/src/TypeSystem/Common/VirtualMethodAlgorithm.cs b/external/corert/src/Common/src/TypeSystem/Common/VirtualMethodAlgorithm.cs index 3b87037800..4f22881d4a 100644 --- a/external/corert/src/Common/src/TypeSystem/Common/VirtualMethodAlgorithm.cs +++ b/external/corert/src/Common/src/TypeSystem/Common/VirtualMethodAlgorithm.cs @@ -23,6 +23,8 @@ namespace Internal.TypeSystem /// public abstract MethodDesc ResolveInterfaceMethodToVirtualMethodOnType(MethodDesc interfaceMethod, TypeDesc currentType); + public abstract MethodDesc ResolveVariantInterfaceMethodToVirtualMethodOnType(MethodDesc interfaceMethod, TypeDesc currentType); + /// /// Resolves a virtual method call. /// diff --git a/external/corert/src/Common/src/TypeSystem/Ecma/EcmaField.cs b/external/corert/src/Common/src/TypeSystem/Ecma/EcmaField.cs index 7deeaac492..1c8ebe4dc5 100644 --- a/external/corert/src/Common/src/TypeSystem/Ecma/EcmaField.cs +++ b/external/corert/src/Common/src/TypeSystem/Ecma/EcmaField.cs @@ -41,7 +41,7 @@ namespace Internal.TypeSystem.Ecma #if DEBUG // Initialize name eagerly in debug builds for convenience - this.ToString(); + InitializeName(); #endif } @@ -251,11 +251,6 @@ namespace Internal.TypeSystem.Ecma return !MetadataReader.GetCustomAttributeHandle(MetadataReader.GetFieldDefinition(_handle).GetCustomAttributes(), attributeNamespace, attributeName).IsNil; } - - public override string ToString() - { - return _type.ToString() + "." + Name; - } } public static class EcmaFieldExtensions diff --git a/external/corert/src/Common/src/TypeSystem/Ecma/EcmaGenericParameter.cs b/external/corert/src/Common/src/TypeSystem/Ecma/EcmaGenericParameter.cs index b69bf873ee..28a43f0424 100644 --- a/external/corert/src/Common/src/TypeSystem/Ecma/EcmaGenericParameter.cs +++ b/external/corert/src/Common/src/TypeSystem/Ecma/EcmaGenericParameter.cs @@ -133,11 +133,5 @@ namespace Internal.TypeSystem.Ecma return constraintTypes; } } - - public override string ToString() - { - MetadataReader reader = _module.MetadataReader; - return reader.GetString(reader.GetGenericParameter(_handle).Name); - } } } diff --git a/external/corert/src/Common/src/TypeSystem/Ecma/EcmaMethod.cs b/external/corert/src/Common/src/TypeSystem/Ecma/EcmaMethod.cs index 7c40ea5579..214f09c077 100644 --- a/external/corert/src/Common/src/TypeSystem/Ecma/EcmaMethod.cs +++ b/external/corert/src/Common/src/TypeSystem/Ecma/EcmaMethod.cs @@ -51,7 +51,7 @@ namespace Internal.TypeSystem.Ecma #if DEBUG // Initialize name eagerly in debug builds for convenience - this.ToString(); + InitializeName(); #endif } @@ -397,11 +397,6 @@ namespace Internal.TypeSystem.Ecma attributeNamespace, attributeName).IsNil; } - public override string ToString() - { - return _type.ToString() + "." + Name; - } - public override bool IsPInvoke { get diff --git a/external/corert/src/Common/src/TypeSystem/Ecma/EcmaModule.cs b/external/corert/src/Common/src/TypeSystem/Ecma/EcmaModule.cs index 049fe1757c..14b3d51b49 100644 --- a/external/corert/src/Common/src/TypeSystem/Ecma/EcmaModule.cs +++ b/external/corert/src/Common/src/TypeSystem/Ecma/EcmaModule.cs @@ -189,7 +189,7 @@ namespace Internal.TypeSystem.Ecma { if (!peReader.HasMetadata) { - throw new TypeSystemException.BadImageFormatException(); + ThrowHelper.ThrowBadImageFormatException(); } var stringDecoderProvider = context as IMetadataStringDecoderProvider; @@ -293,7 +293,7 @@ namespace Internal.TypeSystem.Ecma } if (throwIfNotFound) - throw new TypeSystemException.TypeLoadException(nameSpace, name, this); + ThrowHelper.ThrowTypeLoadException(nameSpace, name, this); return null; } @@ -389,7 +389,7 @@ namespace Internal.TypeSystem.Ecma if (field != null) return field; - throw new TypeSystemException.MissingFieldException(parentTypeDesc, name); + ThrowHelper.ThrowMissingFieldException(parentTypeDesc, name); } else { @@ -413,12 +413,12 @@ namespace Internal.TypeSystem.Ecma typeDescToInspect = typeDescToInspect.BaseType; } while (typeDescToInspect != null); - throw new TypeSystemException.MissingMethodException(parentTypeDesc, name, sig); + ThrowHelper.ThrowMissingMethodException(parentTypeDesc, name, sig); } } else if (parent is MethodDesc) { - throw new TypeSystemException.InvalidProgramException(ExceptionStringID.InvalidProgramVararg, (MethodDesc)parent); + ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramVararg, (MethodDesc)parent); } else if (parent is ModuleDesc) { @@ -442,11 +442,13 @@ namespace Internal.TypeSystem.Ecma if (resolutionScope is MetadataType) { string typeName = _metadataReader.GetString(typeReference.Name); + if (!typeReference.Namespace.IsNil) + typeName = _metadataReader.GetString(typeReference.Namespace) + "." + typeName; MetadataType result = ((MetadataType)(resolutionScope)).GetNestedType(typeName); if (result != null) return result; - throw new TypeSystemException.TypeLoadException(typeName, ((MetadataType)resolutionScope).Module); + ThrowHelper.ThrowTypeLoadException(typeName, ((MetadataType)resolutionScope).Module); } // TODO @@ -496,7 +498,7 @@ namespace Internal.TypeSystem.Ecma string name = _metadataReader.GetString(exportedType.Name); var nestedType = type.GetNestedType(name); if (nestedType == null) - throw new TypeSystemException.TypeLoadException(name, this); + ThrowHelper.ThrowTypeLoadException(name, this); return nestedType; } else diff --git a/external/corert/src/Common/src/TypeSystem/Ecma/EcmaSignatureEncoder.cs b/external/corert/src/Common/src/TypeSystem/Ecma/EcmaSignatureEncoder.cs new file mode 100644 index 0000000000..3a1d2ac677 --- /dev/null +++ b/external/corert/src/Common/src/TypeSystem/Ecma/EcmaSignatureEncoder.cs @@ -0,0 +1,184 @@ +// 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.Collections.Immutable; +using System.Reflection; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.Metadata; + +using Internal.TypeSystem; + +namespace Internal.TypeSystem.Ecma +{ + public interface IEntityHandleProvider + { + /// Implement to allow EcmaSignatureEncoder to encode types that need metadata references to be resolved. + /// only used to express non-generic references + EntityHandle GetTypeDefOrRefHandleForTypeDesc(TypeDesc type); + } + + public class EcmaSignatureEncoder where TEntityHandleProvider : IEntityHandleProvider + { + TEntityHandleProvider _entityHandleProvider; + + public EcmaSignatureEncoder(TEntityHandleProvider entityHandleProvider) + { + _entityHandleProvider = entityHandleProvider; + } + + public void EncodeMethodSignature(BlobBuilder methodSignatureBlob, MethodSignature signature) + { + BlobEncoder encoder = new BlobEncoder(methodSignatureBlob); + + MethodSignatureEncoder methodSigEncoder = encoder.MethodSignature( + SignatureCallingConvention.Default, signature.GenericParameterCount, !signature.IsStatic); + + ReturnTypeEncoder returnTypeEncoder; + ParametersEncoder parametersEncoder; + methodSigEncoder.Parameters(signature.Length, out returnTypeEncoder, out parametersEncoder); + + // Return Type Sig + EncodeTypeSignature(returnTypeEncoder.Type(), signature.ReturnType); + + // Parameter Types Sig + for (int i = 0; i < signature.Length; i++) + EncodeTypeSignature(parametersEncoder.AddParameter().Type(), signature[i]); + } + + public void EncodeTypeSignature(SignatureTypeEncoder encoder, TypeDesc type) + { + if (type is RuntimeDeterminedType) + { + EncodeTypeSignature(encoder, ((RuntimeDeterminedType)type).RuntimeDeterminedDetailsType); + return; + } + + switch (type.Category) + { + case TypeFlags.Boolean: + encoder.Boolean(); break; + case TypeFlags.Byte: + encoder.Byte(); break; + case TypeFlags.SByte: + encoder.SByte(); break; + case TypeFlags.Char: + encoder.Char(); break; + case TypeFlags.Int16: + encoder.Int16(); break; + case TypeFlags.UInt16: + encoder.UInt16(); break; + case TypeFlags.Int32: + encoder.Int32(); break; + case TypeFlags.UInt32: + encoder.UInt32(); break; + case TypeFlags.Int64: + encoder.Int64(); break; + case TypeFlags.UInt64: + encoder.UInt64(); break; + case TypeFlags.Single: + encoder.Single(); break; + case TypeFlags.Double: + encoder.Double(); break; + case TypeFlags.IntPtr: + encoder.IntPtr(); break; + case TypeFlags.UIntPtr: + encoder.UIntPtr(); break; + case TypeFlags.Void: + encoder.Builder.WriteByte((byte)PrimitiveTypeCode.Void); + break; + + case TypeFlags.SignatureTypeVariable: + encoder.GenericTypeParameter(((SignatureVariable)type).Index); + break; + + case TypeFlags.SignatureMethodVariable: + encoder.GenericMethodTypeParameter(((SignatureMethodVariable)type).Index); + break; + + case TypeFlags.GenericParameter: + { + var genericTypeDesc = (GenericParameterDesc)type; + if (genericTypeDesc.Kind == GenericParameterKind.Type) + encoder.GenericTypeParameter(genericTypeDesc.Index); + else + encoder.GenericMethodTypeParameter(genericTypeDesc.Index); + } + break; + + case TypeFlags.FunctionPointer: + { + FunctionPointerType fptrType = (FunctionPointerType)type; + encoder.FunctionPointer( + SignatureCallingConvention.Default, + fptrType.Signature.IsStatic ? default(FunctionPointerAttributes) : FunctionPointerAttributes.HasThis, + fptrType.Signature.GenericParameterCount); + + // Return Type Sig + EncodeTypeSignature(encoder, fptrType.Signature.ReturnType); + + // Parameter Types Sig + for (int i = 0; i < fptrType.Signature.Length; i++) + EncodeTypeSignature(encoder, fptrType.Signature[i]); + } + break; + + case TypeFlags.Array: + { + // Skip bounds and lobounds (TODO) + ImmutableArray bounds = ImmutableArray.Create(); + ImmutableArray lowerBounds = ImmutableArray.Create(); + encoder.Array( + elementType => EncodeTypeSignature(elementType, ((ArrayType)type).ElementType), + arrayShape => arrayShape.Shape(((ArrayType)type).Rank, bounds, lowerBounds)); + } + break; + + case TypeFlags.SzArray: + encoder.SZArray(); + EncodeTypeSignature(encoder, ((ParameterizedType)type).ParameterType); + break; + + case TypeFlags.ByRef: + encoder.Builder.WriteByte((byte)SignatureTypeCode.ByReference); + EncodeTypeSignature(encoder, ((ParameterizedType)type).ParameterType); + break; + + case TypeFlags.Pointer: + encoder.Builder.WriteByte((byte)SignatureTypeCode.Pointer); + EncodeTypeSignature(encoder, ((ParameterizedType)type).ParameterType); + break; + + case TypeFlags.Enum: + case TypeFlags.Class: + case TypeFlags.ValueType: + case TypeFlags.Interface: + case TypeFlags.Nullable: + { + if (type == type.Context.GetWellKnownType(WellKnownType.TypedReference)) + encoder.Builder.WriteByte((byte)PrimitiveTypeCode.TypedReference); + else if (type == type.Context.GetWellKnownType(WellKnownType.Object)) + encoder.PrimitiveType(PrimitiveTypeCode.Object); + else if (type == type.Context.GetWellKnownType(WellKnownType.String)) + encoder.PrimitiveType(PrimitiveTypeCode.String); + else if (type.HasInstantiation && !type.IsGenericDefinition) + { + encoder.GenericInstantiation(_entityHandleProvider.GetTypeDefOrRefHandleForTypeDesc(type.GetTypeDefinition()), type.Instantiation.Length, type.IsValueType); + + for (int i = 0; i < type.Instantiation.Length; i++) + EncodeTypeSignature(encoder, type.Instantiation[i]); + } + else + { + encoder.Type(_entityHandleProvider.GetTypeDefOrRefHandleForTypeDesc(type), type.IsValueType); + } + } + break; + + default: + throw new InvalidOperationException("Attempting to encode an invalid type signature."); + } + } + } +} diff --git a/external/corert/src/Common/src/TypeSystem/Ecma/EcmaSignatureParser.cs b/external/corert/src/Common/src/TypeSystem/Ecma/EcmaSignatureParser.cs index dc7987c65b..74d6043479 100644 --- a/external/corert/src/Common/src/TypeSystem/Ecma/EcmaSignatureParser.cs +++ b/external/corert/src/Common/src/TypeSystem/Ecma/EcmaSignatureParser.cs @@ -169,6 +169,7 @@ namespace Internal.TypeSystem.Ecma Debug.Assert((int)MethodSignatureFlags.UnmanagedCallingConventionCdecl == (int)SignatureCallingConvention.CDecl); Debug.Assert((int)MethodSignatureFlags.UnmanagedCallingConventionStdCall == (int)SignatureCallingConvention.StdCall); Debug.Assert((int)MethodSignatureFlags.UnmanagedCallingConventionThisCall == (int)SignatureCallingConvention.ThisCall); + Debug.Assert((int)MethodSignatureFlags.CallingConventionVarargs == (int)SignatureCallingConvention.VarArgs); flags = (MethodSignatureFlags)signatureCallConv; } diff --git a/external/corert/src/Common/src/TypeSystem/Ecma/EcmaType.Interfaces.cs b/external/corert/src/Common/src/TypeSystem/Ecma/EcmaType.Interfaces.cs index 41b46bb4ba..c118df190f 100644 --- a/external/corert/src/Common/src/TypeSystem/Ecma/EcmaType.Interfaces.cs +++ b/external/corert/src/Common/src/TypeSystem/Ecma/EcmaType.Interfaces.cs @@ -43,7 +43,7 @@ namespace Internal.TypeSystem.Ecma var interfaceImplementation = this.MetadataReader.GetInterfaceImplementation(interfaceHandle); DefType interfaceType = _module.GetType(interfaceImplementation.Interface) as DefType; if (interfaceType == null) - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadBadFormat, this); + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadBadFormat, this); implementedInterfaces[i++] = interfaceType; } diff --git a/external/corert/src/Common/src/TypeSystem/Ecma/EcmaType.MethodImpls.cs b/external/corert/src/Common/src/TypeSystem/Ecma/EcmaType.MethodImpls.cs index fa893448d2..4348f6c8ed 100644 --- a/external/corert/src/Common/src/TypeSystem/Ecma/EcmaType.MethodImpls.cs +++ b/external/corert/src/Common/src/TypeSystem/Ecma/EcmaType.MethodImpls.cs @@ -58,7 +58,7 @@ namespace Internal.TypeSystem.Ecma break; default: - Debug.Assert(false, "unexpected methodDeclHandleKind"); + Debug.Fail("unexpected methodDeclHandleKind"); break; } @@ -113,7 +113,7 @@ namespace Internal.TypeSystem.Ecma break; default: - Debug.Assert(false, "unexpected methodDeclHandleKind"); + Debug.Fail("unexpected methodDeclHandleKind"); break; } diff --git a/external/corert/src/Common/src/TypeSystem/Ecma/EcmaType.cs b/external/corert/src/Common/src/TypeSystem/Ecma/EcmaType.cs index af8cf1334d..e2e840f8a6 100644 --- a/external/corert/src/Common/src/TypeSystem/Ecma/EcmaType.cs +++ b/external/corert/src/Common/src/TypeSystem/Ecma/EcmaType.cs @@ -42,7 +42,8 @@ namespace Internal.TypeSystem.Ecma #if DEBUG // Initialize name eagerly in debug builds for convenience - this.ToString(); + InitializeName(); + InitializeNamespace(); #endif } @@ -165,7 +166,7 @@ namespace Internal.TypeSystem.Ecma if (type == null) { // PREFER: "new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadBadFormat, this)" but the metadata is too broken - throw new TypeSystemException.TypeLoadException(Namespace, Name, Module); + ThrowHelper.ThrowTypeLoadException(Namespace, Name, Module); } _baseType = type; return type; @@ -241,6 +242,14 @@ namespace Internal.TypeSystem.Ecma flags |= TypeFlags.HasFinalizer; } + if ((mask & TypeFlags.IsByRefLikeComputed) != 0) + { + flags |= TypeFlags.IsByRefLikeComputed; + + if (IsValueType && HasCustomAttribute("System.Runtime.CompilerServices", "IsByRefLikeAttribute")) + flags |= TypeFlags.IsByRefLike; + } + return flags; } @@ -422,7 +431,20 @@ namespace Internal.TypeSystem.Ecma foreach (var handle in _typeDefinition.GetNestedTypes()) { - if (stringComparer.Equals(metadataReader.GetTypeDefinition(handle).Name, name)) + bool nameMatched; + TypeDefinition type = metadataReader.GetTypeDefinition(handle); + if (type.Namespace.IsNil) + { + nameMatched = stringComparer.Equals(type.Name, name); + } + else + { + string typeName = metadataReader.GetString(type.Name); + typeName = metadataReader.GetString(type.Namespace) + "." + typeName; + nameMatched = typeName == name; + } + + if (nameMatched) return (MetadataType)_module.GetObject(handle); } @@ -455,11 +477,6 @@ namespace Internal.TypeSystem.Ecma attributeNamespace, attributeName).IsNil; } - public override string ToString() - { - return "[" + _module.ToString() + "]" + this.GetFullName(); - } - public override ClassLayoutMetadata GetClassLayout() { TypeLayout layout = _typeDefinition.GetLayout(); diff --git a/external/corert/src/Common/src/TypeSystem/Ecma/MetadataExtensions.cs b/external/corert/src/Common/src/TypeSystem/Ecma/MetadataExtensions.cs index c9ac80519b..db5912bfa9 100644 --- a/external/corert/src/Common/src/TypeSystem/Ecma/MetadataExtensions.cs +++ b/external/corert/src/Common/src/TypeSystem/Ecma/MetadataExtensions.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Reflection; using System.Reflection.Metadata; using System.Runtime.InteropServices; @@ -39,6 +40,20 @@ namespace Internal.TypeSystem.Ecma return metadataReader.GetCustomAttribute(attributeHandle).DecodeValue(new CustomAttributeTypeProvider(This.Module)); } + public static IEnumerable> GetDecodedCustomAttributes(this EcmaMethod This, + string attributeNamespace, string attributeName) + { + var metadataReader = This.MetadataReader; + var attributeHandles = metadataReader.GetMethodDefinition(This.Handle).GetCustomAttributes(); + foreach (var attributeHandle in attributeHandles) + { + if (IsEqualCustomAttributeName(attributeHandle, metadataReader, attributeNamespace, attributeName)) + { + yield return metadataReader.GetCustomAttribute(attributeHandle).DecodeValue(new CustomAttributeTypeProvider(This.Module)); + } + } + } + public static CustomAttributeValue? GetDecodedCustomAttribute(this EcmaField This, string attributeNamespace, string attributeName) { @@ -53,17 +68,40 @@ namespace Internal.TypeSystem.Ecma return metadataReader.GetCustomAttribute(attributeHandle).DecodeValue(new CustomAttributeTypeProvider(This.Module)); } + public static IEnumerable> GetDecodedCustomAttributes(this EcmaField This, + string attributeNamespace, string attributeName) + { + var metadataReader = This.MetadataReader; + var attributeHandles = metadataReader.GetFieldDefinition(This.Handle).GetCustomAttributes(); + foreach (var attributeHandle in attributeHandles) + { + if (IsEqualCustomAttributeName(attributeHandle, metadataReader, attributeNamespace, attributeName)) + { + yield return metadataReader.GetCustomAttribute(attributeHandle).DecodeValue(new CustomAttributeTypeProvider(This.Module)); + } + } + } + + public static IEnumerable> GetDecodedCustomAttributes(this EcmaAssembly This, + string attributeNamespace, string attributeName) + { + var metadataReader = This.MetadataReader; + var attributeHandles = metadataReader.GetAssemblyDefinition().GetCustomAttributes(); + foreach (var attributeHandle in attributeHandles) + { + if (IsEqualCustomAttributeName(attributeHandle, metadataReader, attributeNamespace, attributeName)) + { + yield return metadataReader.GetCustomAttribute(attributeHandle).DecodeValue(new CustomAttributeTypeProvider(This)); + } + } + } + public static CustomAttributeHandle GetCustomAttributeHandle(this MetadataReader metadataReader, CustomAttributeHandleCollection customAttributes, string attributeNamespace, string attributeName) { foreach (var attributeHandle in customAttributes) { - StringHandle namespaceHandle, nameHandle; - if (!metadataReader.GetAttributeNamespaceAndName(attributeHandle, out namespaceHandle, out nameHandle)) - continue; - - if (metadataReader.StringComparer.Equals(namespaceHandle, attributeNamespace) - && metadataReader.StringComparer.Equals(nameHandle, attributeName)) + if (IsEqualCustomAttributeName(attributeHandle, metadataReader, attributeNamespace, attributeName)) { return attributeHandle; } @@ -72,6 +110,17 @@ namespace Internal.TypeSystem.Ecma return default(CustomAttributeHandle); } + private static bool IsEqualCustomAttributeName(CustomAttributeHandle attributeHandle, MetadataReader metadataReader, + string attributeNamespace, string attributeName) + { + StringHandle namespaceHandle, nameHandle; + if (!metadataReader.GetAttributeNamespaceAndName(attributeHandle, out namespaceHandle, out nameHandle)) + return false; + + return metadataReader.StringComparer.Equals(namespaceHandle, attributeNamespace) + && metadataReader.StringComparer.Equals(nameHandle, attributeName); + } + public static bool GetAttributeNamespaceAndName(this MetadataReader metadataReader, CustomAttributeHandle attributeHandle, out StringHandle namespaceHandle, out StringHandle nameHandle) { diff --git a/external/corert/src/Common/src/TypeSystem/Ecma/SymbolReader/UnmanagedPdbSymbolReader.cs b/external/corert/src/Common/src/TypeSystem/Ecma/SymbolReader/UnmanagedPdbSymbolReader.cs index ac4c8d9a2b..5dc82b12ad 100644 --- a/external/corert/src/Common/src/TypeSystem/Ecma/SymbolReader/UnmanagedPdbSymbolReader.cs +++ b/external/corert/src/Common/src/TypeSystem/Ecma/SymbolReader/UnmanagedPdbSymbolReader.cs @@ -82,44 +82,47 @@ namespace Internal.TypeSystem.Ecma static UnmanagedPdbSymbolReader() { - try + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - Guid IID_IUnknown = new Guid(0x00000000, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); + try + { + Guid IID_IUnknown = new Guid(0x00000000, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); - ICLRMetaHost objMetaHost; - Guid CLSID_CLRMetaHost = new Guid(0x9280188d, 0x0e8e, 0x4867, 0xb3, 0x0c, 0x7f, 0xa8, 0x38, 0x84, 0xe8, 0xde); - Guid IID_CLRMetaHost = new Guid(0xd332db9e, 0xb9b3, 0x4125, 0x82, 0x07, 0xa1, 0x48, 0x84, 0xf5, 0x32, 0x16); - if (CLRCreateInstance(ref CLSID_CLRMetaHost, ref IID_CLRMetaHost, out objMetaHost) < 0) - return; + ICLRMetaHost objMetaHost; + Guid CLSID_CLRMetaHost = new Guid(0x9280188d, 0x0e8e, 0x4867, 0xb3, 0x0c, 0x7f, 0xa8, 0x38, 0x84, 0xe8, 0xde); + Guid IID_CLRMetaHost = new Guid(0xd332db9e, 0xb9b3, 0x4125, 0x82, 0x07, 0xa1, 0x48, 0x84, 0xf5, 0x32, 0x16); + if (CLRCreateInstance(ref CLSID_CLRMetaHost, ref IID_CLRMetaHost, out objMetaHost) < 0) + return; - ICLRRuntimeInfo objRuntime; - Guid IID_CLRRuntimeInfo = new Guid(0xbd39d1d2, 0xba2f, 0x486a, 0x89, 0xb0, 0xb4, 0xb0, 0xcb, 0x46, 0x68, 0x91); - if (objMetaHost.GetRuntime("v4.0.30319", ref IID_CLRRuntimeInfo, out objRuntime) < 0) - return; + ICLRRuntimeInfo objRuntime; + Guid IID_CLRRuntimeInfo = new Guid(0xbd39d1d2, 0xba2f, 0x486a, 0x89, 0xb0, 0xb4, 0xb0, 0xcb, 0x46, 0x68, 0x91); + if (objMetaHost.GetRuntime("v4.0.30319", ref IID_CLRRuntimeInfo, out objRuntime) < 0) + return; - // To get everything from the v4 runtime - objRuntime.BindAsLegacyV2Runtime(); + // To get everything from the v4 runtime + objRuntime.BindAsLegacyV2Runtime(); - // Create a COM Metadata dispenser - object objDispenser; - Guid CLSID_CorMetaDataDispenser = new Guid(0xe5cb7a31, 0x7512, 0x11d2, 0x89, 0xce, 0x00, 0x80, 0xc7, 0x92, 0xe5, 0xd8); - if (objRuntime.GetInterface(ref CLSID_CorMetaDataDispenser, ref IID_IUnknown, out objDispenser) < 0) - return; - s_metadataDispenser = (IMetaDataDispenser)objDispenser; + // Create a COM Metadata dispenser + object objDispenser; + Guid CLSID_CorMetaDataDispenser = new Guid(0xe5cb7a31, 0x7512, 0x11d2, 0x89, 0xce, 0x00, 0x80, 0xc7, 0x92, 0xe5, 0xd8); + if (objRuntime.GetInterface(ref CLSID_CorMetaDataDispenser, ref IID_IUnknown, out objDispenser) < 0) + return; + s_metadataDispenser = (IMetaDataDispenser)objDispenser; - // Create a SymBinder - object objBinder; - Guid CLSID_CorSymBinder = new Guid(0x0a29ff9e, 0x7f9c, 0x4437, 0x8b, 0x11, 0xf4, 0x24, 0x49, 0x1e, 0x39, 0x31); - if (CoCreateInstance(ref CLSID_CorSymBinder, - IntPtr.Zero, // pUnkOuter - 1, // CLSCTX_INPROC_SERVER - ref IID_IUnknown, - out objBinder) < 0) - return; - s_symBinder = (ISymUnmanagedBinder)objBinder; - } - catch - { + // Create a SymBinder + object objBinder; + Guid CLSID_CorSymBinder = new Guid(0x0a29ff9e, 0x7f9c, 0x4437, 0x8b, 0x11, 0xf4, 0x24, 0x49, 0x1e, 0x39, 0x31); + if (CoCreateInstance(ref CLSID_CorSymBinder, + IntPtr.Zero, // pUnkOuter + 1, // CLSCTX_INPROC_SERVER + ref IID_IUnknown, + out objBinder) < 0) + return; + s_symBinder = (ISymUnmanagedBinder)objBinder; + } + catch + { + } } } diff --git a/external/corert/src/Common/src/TypeSystem/IL/DelegateInfo.cs b/external/corert/src/Common/src/TypeSystem/IL/DelegateInfo.cs index fd3227729a..66267bf451 100644 --- a/external/corert/src/Common/src/TypeSystem/IL/DelegateInfo.cs +++ b/external/corert/src/Common/src/TypeSystem/IL/DelegateInfo.cs @@ -2,6 +2,8 @@ // 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.Collections.Generic; + using Internal.IL.Stubs; using Internal.TypeSystem; @@ -23,9 +25,9 @@ namespace Internal.IL private DelegateThunkCollection _thunks; /// - /// Gets the Delegate.GetThunk override implementation for this delegate type. + /// Gets the synthetic methods that support this delegate type. /// - public MethodDesc GetThunkMethod + public IEnumerable Methods { get { @@ -34,7 +36,15 @@ namespace Internal.IL Interlocked.CompareExchange(ref _getThunkMethod, new DelegateGetThunkMethodOverride(this), null); } - return _getThunkMethod; + yield return _getThunkMethod; + + DelegateThunkCollection thunks = Thunks; + for (DelegateThunkKind kind = 0; kind < DelegateThunkCollection.MaxThunkKind; kind++) + { + MethodDesc thunk = thunks[kind]; + if (thunk != null) + yield return thunk; + } } } @@ -93,6 +103,8 @@ namespace Internal.IL /// public class DelegateThunkCollection { + public const DelegateThunkKind MaxThunkKind = DelegateThunkKind.ObjectArrayThunk + 1; + private MethodDesc _openStaticThunk; private MethodDesc _multicastThunk; private MethodDesc _closedStaticThunk; @@ -109,22 +121,62 @@ namespace Internal.IL _closedStaticThunk = new DelegateInvokeClosedStaticThunk(owningDelegate); _invokeThunk = new DelegateDynamicInvokeThunk(owningDelegate); _closedInstanceOverGeneric = new DelegateInvokeInstanceClosedOverGenericMethodThunk(owningDelegate); - _invokeObjectArrayThunk = new DelegateInvokeObjectArrayThunk(owningDelegate); - - if (!owningDelegate.Type.HasInstantiation && IsNativeCallingConventionCompatible(owningDelegate.Signature)) - _reversePInvokeThunk = new DelegateReversePInvokeThunk(owningDelegate); + // Methods that have a byref-like type in the signature cannot be invoked with the object array thunk. + // We would need to box the parameter and these can't be boxed. MethodSignature delegateSignature = owningDelegate.Signature; + bool generateObjectArrayThunk = true; + for (int i = 0; i < delegateSignature.Length; i++) + { + TypeDesc paramType = delegateSignature[i]; + if (paramType.IsByRef) + paramType = ((ByRefType)paramType).ParameterType; + if (!paramType.IsSignatureVariable && paramType.IsByRefLike) + { + generateObjectArrayThunk = false; + break; + } + } + TypeDesc normalizedReturnType = delegateSignature.ReturnType; + if (normalizedReturnType.IsByRef) + normalizedReturnType = ((ByRefType)normalizedReturnType).ParameterType; + if (!normalizedReturnType.IsSignatureVariable && normalizedReturnType.IsByRefLike) + generateObjectArrayThunk = false; + + if (generateObjectArrayThunk) + _invokeObjectArrayThunk = new DelegateInvokeObjectArrayThunk(owningDelegate); + + if (!owningDelegate.Type.HasInstantiation && IsNativeCallingConventionCompatible(delegateSignature)) + _reversePInvokeThunk = new DelegateReversePInvokeThunk(owningDelegate); + if (delegateSignature.Length > 0) { TypeDesc firstParam = delegateSignature[0]; - bool generateOpenInstanceMethod = true; + bool generateOpenInstanceMethod; - if (firstParam.IsValueType || - (!firstParam.IsDefType && !firstParam.IsSignatureVariable) /* no arrays, pointers, byrefs, etc. */) + switch (firstParam.Category) { - generateOpenInstanceMethod = false; + case TypeFlags.Pointer: + case TypeFlags.FunctionPointer: + generateOpenInstanceMethod = false; + break; + + case TypeFlags.ByRef: + firstParam = ((ByRefType)firstParam).ParameterType; + generateOpenInstanceMethod = firstParam.IsSignatureVariable || firstParam.IsValueType; + break; + + case TypeFlags.Array: + case TypeFlags.SzArray: + case TypeFlags.SignatureTypeVariable: + generateOpenInstanceMethod = true; + break; + + default: + Debug.Assert(firstParam.IsDefType); + generateOpenInstanceMethod = !firstParam.IsValueType; + break; } if (generateOpenInstanceMethod) @@ -222,4 +274,4 @@ namespace Internal.IL ReversePinvokeThunk = 6, // This may not exist ObjectArrayThunk = 7, // This may not exist } -} \ No newline at end of file +} diff --git a/external/corert/src/Common/src/TypeSystem/IL/HelperExtensions.cs b/external/corert/src/Common/src/TypeSystem/IL/HelperExtensions.cs index 694430c8c1..682dab4f04 100644 --- a/external/corert/src/Common/src/TypeSystem/IL/HelperExtensions.cs +++ b/external/corert/src/Common/src/TypeSystem/IL/HelperExtensions.cs @@ -76,14 +76,29 @@ namespace Internal.IL return field; } + /// + /// Retrieves a nested type on that is well known to the compiler. + /// Throws an exception if the nested type doesn't exist. + /// + public static MetadataType GetKnownNestedType(this MetadataType type, string name) + { + MetadataType nestedType = type.GetNestedType(name); + if (nestedType == null) + { + throw new InvalidOperationException(String.Format("Expected type '{0}' not found on type '{1}'", name, type)); + } + + return nestedType; + } + /// /// Retrieves a namespace type in that is well known to the compiler. /// Throws an exception if the type doesn't exist. /// - public static MetadataType GetKnownType(this ModuleDesc module, string @namespace, string name, bool throwIfNotFound = true) + public static MetadataType GetKnownType(this ModuleDesc module, string @namespace, string name) { MetadataType type = module.GetType(@namespace, name, false); - if (type == null && throwIfNotFound) + if (type == null) { throw new InvalidOperationException( String.Format("Expected type '{0}' not found in module '{1}'", diff --git a/external/corert/src/Common/src/TypeSystem/IL/ILDisassember.cs b/external/corert/src/Common/src/TypeSystem/IL/ILDisassembler.cs similarity index 98% rename from external/corert/src/Common/src/TypeSystem/IL/ILDisassember.cs rename to external/corert/src/Common/src/TypeSystem/IL/ILDisassembler.cs index 20f290278b..c7fb70f6e8 100644 --- a/external/corert/src/Common/src/TypeSystem/IL/ILDisassember.cs +++ b/external/corert/src/Common/src/TypeSystem/IL/ILDisassembler.cs @@ -22,14 +22,14 @@ namespace Internal.IL /// /// Helper struct to disassemble IL instructions into a textual representation. /// - public struct ILDisassember + public struct ILDisassembler { private byte[] _ilBytes; private MethodIL _methodIL; private ILTypeNameFormatter _typeNameFormatter; private int _currentOffset; - public ILDisassember(MethodIL methodIL) + public ILDisassembler(MethodIL methodIL) { _methodIL = methodIL; _ilBytes = methodIL.GetILBytes(); @@ -248,6 +248,14 @@ namespace Internal.IL } } + public int Offset + { + get + { + return _currentOffset; + } + } + public int CodeSize { get @@ -423,7 +431,7 @@ namespace Internal.IL #endregion #region Helpers - private class ILTypeNameFormatter : TypeNameFormatter + public class ILTypeNameFormatter : TypeNameFormatter { private ModuleDesc _thisModule; diff --git a/external/corert/src/Common/src/TypeSystem/IL/ILImporter.cs b/external/corert/src/Common/src/TypeSystem/IL/ILImporter.cs index 46491a71b7..fbfdac0723 100644 --- a/external/corert/src/Common/src/TypeSystem/IL/ILImporter.cs +++ b/external/corert/src/Common/src/TypeSystem/IL/ILImporter.cs @@ -47,11 +47,17 @@ namespace Internal.IL private byte ReadILByte() { + if (_currentOffset >= _ilBytes.Length) + ReportMethodEndInsideInstruction(); + return _ilBytes[_currentOffset++]; } private UInt16 ReadILUInt16() { + if (_currentOffset + 1 >= _ilBytes.Length) + ReportMethodEndInsideInstruction(); + UInt16 val = (UInt16)(_ilBytes[_currentOffset] + (_ilBytes[_currentOffset + 1] << 8)); _currentOffset += 2; return val; @@ -59,6 +65,9 @@ namespace Internal.IL private UInt32 ReadILUInt32() { + if (_currentOffset + 3 >= _ilBytes.Length) + ReportMethodEndInsideInstruction(); + UInt32 val = (UInt32)(_ilBytes[_currentOffset] + (_ilBytes[_currentOffset + 1] << 8) + (_ilBytes[_currentOffset + 2] << 16) + (_ilBytes[_currentOffset + 3] << 24)); _currentOffset += 4; return val; @@ -90,6 +99,9 @@ namespace Internal.IL private void SkipIL(int bytes) { + if (_currentOffset + (bytes - 1) >= _ilBytes.Length) + ReportMethodEndInsideInstruction(); + _currentOffset += bytes; } @@ -141,6 +153,7 @@ namespace Internal.IL case ILOpcode.stloc_s: case ILOpcode.ldc_i4_s: case ILOpcode.unaligned: + case ILOpcode.no: SkipIL(1); break; case ILOpcode.ldarg: @@ -200,7 +213,11 @@ namespace Internal.IL case ILOpcode.leave_s: { int delta = (sbyte)ReadILByte(); - CreateBasicBlock(_currentOffset + delta); + int target = _currentOffset + delta; + if ((uint)target < (uint)_basicBlocks.Length) + CreateBasicBlock(target); + else + ReportInvalidBranchTarget(target); } break; case ILOpcode.brfalse_s: @@ -217,7 +234,11 @@ namespace Internal.IL case ILOpcode.blt_un_s: { int delta = (sbyte)ReadILByte(); - CreateBasicBlock(_currentOffset + delta); + int target = _currentOffset + delta; + if ((uint)target < (uint)_basicBlocks.Length) + CreateBasicBlock(target); + else + ReportInvalidBranchTarget(target); CreateBasicBlock(_currentOffset); } break; @@ -225,7 +246,11 @@ namespace Internal.IL case ILOpcode.leave: { int delta = (int)ReadILUInt32(); - CreateBasicBlock(_currentOffset + delta); + int target = _currentOffset + delta; + if ((uint)target < (uint)_basicBlocks.Length) + CreateBasicBlock(target); + else + ReportInvalidBranchTarget(target); } break; case ILOpcode.brfalse: @@ -242,7 +267,11 @@ namespace Internal.IL case ILOpcode.blt_un: { int delta = (int)ReadILUInt32(); - CreateBasicBlock(_currentOffset + delta); + int target = _currentOffset + delta; + if ((uint)target < (uint)_basicBlocks.Length) + CreateBasicBlock(target); + else + ReportInvalidBranchTarget(target); CreateBasicBlock(_currentOffset); } break; @@ -253,7 +282,11 @@ namespace Internal.IL for (uint i = 0; i < count; i++) { int delta = (int)ReadILUInt32(); - CreateBasicBlock(jmpBase + delta); + int target = jmpBase + delta; + if ((uint)target < (uint)_basicBlocks.Length) + CreateBasicBlock(target); + else + ReportInvalidBranchTarget(target); } CreateBasicBlock(_currentOffset); } @@ -297,13 +330,13 @@ namespace Internal.IL private void MarkBasicBlock(BasicBlock basicBlock) { - if (basicBlock.EndOffset == 0) + if (basicBlock.State == BasicBlock.ImportState.Unmarked) { // Link basicBlock.Next = _pendingBasicBlocks; _pendingBasicBlocks = basicBlock; - basicBlock.EndOffset = -1; + basicBlock.State = BasicBlock.ImportState.IsPending; } } @@ -403,7 +436,8 @@ namespace Internal.IL break; case ILOpcode.jmp: ImportJmp(ReadILToken()); - break; + EndImportingInstruction(); + return; case ILOpcode.call: ImportCall(opCode, ReadILToken()); break; @@ -412,6 +446,7 @@ namespace Internal.IL break; case ILOpcode.ret: ImportReturn(); + EndImportingInstruction(); return; case ILOpcode.br_s: case ILOpcode.brfalse_s: @@ -431,6 +466,7 @@ namespace Internal.IL ImportBranch(opCode + (ILOpcode.br - ILOpcode.br_s), _basicBlocks[_currentOffset + delta], (opCode != ILOpcode.br_s) ? _basicBlocks[_currentOffset] : null); } + EndImportingInstruction(); return; case ILOpcode.br: case ILOpcode.brfalse: @@ -450,6 +486,7 @@ namespace Internal.IL ImportBranch(opCode, _basicBlocks[_currentOffset + delta], (opCode != ILOpcode.br) ? _basicBlocks[_currentOffset] : null); } + EndImportingInstruction(); return; case ILOpcode.switch_: { @@ -461,6 +498,7 @@ namespace Internal.IL ImportSwitchJump(jmpBase, jmpDelta, _basicBlocks[_currentOffset]); } + EndImportingInstruction(); return; case ILOpcode.ldind_i1: ImportLoadIndirect(WellKnownType.SByte); @@ -588,6 +626,7 @@ namespace Internal.IL break; case ILOpcode.throw_: ImportThrow(); + EndImportingInstruction(); return; case ILOpcode.ldfld: ImportLoadField(ReadILToken(), false); @@ -698,7 +737,7 @@ namespace Internal.IL ImportStoreElement(WellKnownType.Int32); break; case ILOpcode.stelem_i8: - ImportStoreElement(WellKnownType.Int32); + ImportStoreElement(WellKnownType.Int64); break; case ILOpcode.stelem_r4: ImportStoreElement(WellKnownType.Single); @@ -779,18 +818,21 @@ namespace Internal.IL break; case ILOpcode.endfinally: //both endfinally and endfault ImportEndFinally(); + EndImportingInstruction(); return; case ILOpcode.leave: { int delta = (int)ReadILUInt32(); ImportLeave(_basicBlocks[_currentOffset + delta]); } + EndImportingInstruction(); return; case ILOpcode.leave_s: { int delta = (sbyte)ReadILByte(); ImportLeave(_basicBlocks[_currentOffset + delta]); } + EndImportingInstruction(); return; case ILOpcode.stind_i: ImportStoreIndirect(WellKnownType.IntPtr); @@ -838,6 +880,7 @@ namespace Internal.IL break; case ILOpcode.endfilter: ImportEndFilter(); + EndImportingInstruction(); return; case ILOpcode.unaligned: ImportUnalignedPrefix(ReadILByte()); @@ -865,6 +908,7 @@ namespace Internal.IL continue; case ILOpcode.rethrow: ImportRethrow(); + EndImportingInstruction(); return; case ILOpcode.sizeof_: ImportSizeOf(ReadILToken()); @@ -876,7 +920,18 @@ namespace Internal.IL ImportReadOnlyPrefix(); continue; default: - throw new BadImageFormatException("Invalid opcode"); + ReportInvalidInstruction(opCode); + EndImportingInstruction(); + return; + } + + EndImportingInstruction(); + + // Check if control falls through the end of method. + if (_currentOffset == _basicBlocks.Length) + { + ReportFallthroughAtEndOfMethod(); + return; } BasicBlock nextBasicBlock = _basicBlocks[_currentOffset]; @@ -885,8 +940,6 @@ namespace Internal.IL ImportFallthrough(nextBasicBlock); return; } - - EndImportingInstruction(); } } diff --git a/external/corert/src/Common/src/TypeSystem/IL/ILOpcodeHelper.cs b/external/corert/src/Common/src/TypeSystem/IL/ILOpcodeHelper.cs new file mode 100644 index 0000000000..b051e205f0 --- /dev/null +++ b/external/corert/src/Common/src/TypeSystem/IL/ILOpcodeHelper.cs @@ -0,0 +1,328 @@ +// 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 Debug = System.Diagnostics.Debug; + +namespace Internal.IL +{ + public static class ILOpcodeHelper + { + private const byte VariableSize = 0xFF; + private const byte Invalid = 0xFE; + + /// + /// Gets the size, in bytes, of the opcode and its arguments. + /// Note: if '' is , + /// the caller must handle it differently as that opcode is variable sized. + /// + public static int GetSize(this ILOpcode opcode) + { + Debug.Assert((uint)opcode < (uint)s_opcodeSizes.Length); + byte result = s_opcodeSizes[(int)opcode]; + Debug.Assert(result != VariableSize); + Debug.Assert(result != Invalid); + return result; + } + + /// + /// Returns true if '' is a valid opcode. + /// + public static bool IsValid(this ILOpcode opcode) + { + return (uint)opcode < (uint)s_opcodeSizes.Length + && s_opcodeSizes[(int)opcode] != Invalid; + } + + private static readonly byte[] s_opcodeSizes = new byte[] + { + 1, // nop = 0x00, + 1, // break_ = 0x01, + 1, // ldarg_0 = 0x02, + 1, // ldarg_1 = 0x03, + 1, // ldarg_2 = 0x04, + 1, // ldarg_3 = 0x05, + 1, // ldloc_0 = 0x06, + 1, // ldloc_1 = 0x07, + 1, // ldloc_2 = 0x08, + 1, // ldloc_3 = 0x09, + 1, // stloc_0 = 0x0a, + 1, // stloc_1 = 0x0b, + 1, // stloc_2 = 0x0c, + 1, // stloc_3 = 0x0d, + 2, // ldarg_s = 0x0e, + 2, // ldarga_s = 0x0f, + 2, // starg_s = 0x10, + 2, // ldloc_s = 0x11, + 2, // ldloca_s = 0x12, + 2, // stloc_s = 0x13, + 1, // ldnull = 0x14, + 1, // ldc_i4_m1 = 0x15, + 1, // ldc_i4_0 = 0x16, + 1, // ldc_i4_1 = 0x17, + 1, // ldc_i4_2 = 0x18, + 1, // ldc_i4_3 = 0x19, + 1, // ldc_i4_4 = 0x1a, + 1, // ldc_i4_5 = 0x1b, + 1, // ldc_i4_6 = 0x1c, + 1, // ldc_i4_7 = 0x1d, + 1, // ldc_i4_8 = 0x1e, + 2, // ldc_i4_s = 0x1f, + 5, // ldc_i4 = 0x20, + 9, // ldc_i8 = 0x21, + 5, // ldc_r4 = 0x22, + 9, // ldc_r8 = 0x23, + Invalid, // = 0x24, + 1, // dup = 0x25, + 1, // pop = 0x26, + 5, // jmp = 0x27, + 5, // call = 0x28, + 5, // calli = 0x29, + 1, // ret = 0x2a, + 2, // br_s = 0x2b, + 2, // brfalse_s = 0x2c, + 2, // brtrue_s = 0x2d, + 2, // beq_s = 0x2e, + 2, // bge_s = 0x2f, + 2, // bgt_s = 0x30, + 2, // ble_s = 0x31, + 2, // blt_s = 0x32, + 2, // bne_un_s = 0x33, + 2, // bge_un_s = 0x34, + 2, // bgt_un_s = 0x35, + 2, // ble_un_s = 0x36, + 2, // blt_un_s = 0x37, + 5, // br = 0x38, + 5, // brfalse = 0x39, + 5, // brtrue = 0x3a, + 5, // beq = 0x3b, + 5, // bge = 0x3c, + 5, // bgt = 0x3d, + 5, // ble = 0x3e, + 5, // blt = 0x3f, + 5, // bne_un = 0x40, + 5, // bge_un = 0x41, + 5, // bgt_un = 0x42, + 5, // ble_un = 0x43, + 5, // blt_un = 0x44, + VariableSize, // switch_ = 0x45, + 1, // ldind_i1 = 0x46, + 1, // ldind_u1 = 0x47, + 1, // ldind_i2 = 0x48, + 1, // ldind_u2 = 0x49, + 1, // ldind_i4 = 0x4a, + 1, // ldind_u4 = 0x4b, + 1, // ldind_i8 = 0x4c, + 1, // ldind_i = 0x4d, + 1, // ldind_r4 = 0x4e, + 1, // ldind_r8 = 0x4f, + 1, // ldind_ref = 0x50, + 1, // stind_ref = 0x51, + 1, // stind_i1 = 0x52, + 1, // stind_i2 = 0x53, + 1, // stind_i4 = 0x54, + 1, // stind_i8 = 0x55, + 1, // stind_r4 = 0x56, + 1, // stind_r8 = 0x57, + 1, // add = 0x58, + 1, // sub = 0x59, + 1, // mul = 0x5a, + 1, // div = 0x5b, + 1, // div_un = 0x5c, + 1, // rem = 0x5d, + 1, // rem_un = 0x5e, + 1, // and = 0x5f, + 1, // or = 0x60, + 1, // xor = 0x61, + 1, // shl = 0x62, + 1, // shr = 0x63, + 1, // shr_un = 0x64, + 1, // neg = 0x65, + 1, // not = 0x66, + 1, // conv_i1 = 0x67, + 1, // conv_i2 = 0x68, + 1, // conv_i4 = 0x69, + 1, // conv_i8 = 0x6a, + 1, // conv_r4 = 0x6b, + 1, // conv_r8 = 0x6c, + 1, // conv_u4 = 0x6d, + 1, // conv_u8 = 0x6e, + 5, // callvirt = 0x6f, + 5, // cpobj = 0x70, + 5, // ldobj = 0x71, + 5, // ldstr = 0x72, + 5, // newobj = 0x73, + 5, // castclass = 0x74, + 5, // isinst = 0x75, + 1, // conv_r_un = 0x76, + Invalid, // = 0x77, + Invalid, // = 0x78, + 5, // unbox = 0x79, + 1, // throw_ = 0x7a, + 5, // ldfld = 0x7b, + 5, // ldflda = 0x7c, + 5, // stfld = 0x7d, + 5, // ldsfld = 0x7e, + 5, // ldsflda = 0x7f, + 5, // stsfld = 0x80, + 5, // stobj = 0x81, + 1, // conv_ovf_i1_un = 0x82, + 1, // conv_ovf_i2_un = 0x83, + 1, // conv_ovf_i4_un = 0x84, + 1, // conv_ovf_i8_un = 0x85, + 1, // conv_ovf_u1_un = 0x86, + 1, // conv_ovf_u2_un = 0x87, + 1, // conv_ovf_u4_un = 0x88, + 1, // conv_ovf_u8_un = 0x89, + 1, // conv_ovf_i_un = 0x8a, + 1, // conv_ovf_u_un = 0x8b, + 5, // box = 0x8c, + 5, // newarr = 0x8d, + 1, // ldlen = 0x8e, + 5, // ldelema = 0x8f, + 1, // ldelem_i1 = 0x90, + 1, // ldelem_u1 = 0x91, + 1, // ldelem_i2 = 0x92, + 1, // ldelem_u2 = 0x93, + 1, // ldelem_i4 = 0x94, + 1, // ldelem_u4 = 0x95, + 1, // ldelem_i8 = 0x96, + 1, // ldelem_i = 0x97, + 1, // ldelem_r4 = 0x98, + 1, // ldelem_r8 = 0x99, + 1, // ldelem_ref = 0x9a, + 1, // stelem_i = 0x9b, + 1, // stelem_i1 = 0x9c, + 1, // stelem_i2 = 0x9d, + 1, // stelem_i4 = 0x9e, + 1, // stelem_i8 = 0x9f, + 1, // stelem_r4 = 0xa0, + 1, // stelem_r8 = 0xa1, + 1, // stelem_ref = 0xa2, + 5, // ldelem = 0xa3, + 5, // stelem = 0xa4, + 5, // unbox_any = 0xa5, + Invalid, // = 0xa6, + Invalid, // = 0xa7, + Invalid, // = 0xa8, + Invalid, // = 0xa9, + Invalid, // = 0xaa, + Invalid, // = 0xab, + Invalid, // = 0xac, + Invalid, // = 0xad, + Invalid, // = 0xae, + Invalid, // = 0xaf, + Invalid, // = 0xb0, + Invalid, // = 0xb1, + Invalid, // = 0xb2, + 1, // conv_ovf_i1 = 0xb3, + 1, // conv_ovf_u1 = 0xb4, + 1, // conv_ovf_i2 = 0xb5, + 1, // conv_ovf_u2 = 0xb6, + 1, // conv_ovf_i4 = 0xb7, + 1, // conv_ovf_u4 = 0xb8, + 1, // conv_ovf_i8 = 0xb9, + 1, // conv_ovf_u8 = 0xba, + Invalid, // = 0xbb, + Invalid, // = 0xbc, + Invalid, // = 0xbd, + Invalid, // = 0xbe, + Invalid, // = 0xbf, + Invalid, // = 0xc0, + Invalid, // = 0xc1, + 5, // refanyval = 0xc2, + 1, // ckfinite = 0xc3, + Invalid, // = 0xc4, + Invalid, // = 0xc5, + 5, // mkrefany = 0xc6, + Invalid, // = 0xc7, + Invalid, // = 0xc8, + Invalid, // = 0xc9, + Invalid, // = 0xca, + Invalid, // = 0xcb, + Invalid, // = 0xcc, + Invalid, // = 0xcd, + Invalid, // = 0xce, + Invalid, // = 0xcf, + 5, // ldtoken = 0xd0, + 1, // conv_u2 = 0xd1, + 1, // conv_u1 = 0xd2, + 1, // conv_i = 0xd3, + 1, // conv_ovf_i = 0xd4, + 1, // conv_ovf_u = 0xd5, + 1, // add_ovf = 0xd6, + 1, // add_ovf_un = 0xd7, + 1, // mul_ovf = 0xd8, + 1, // mul_ovf_un = 0xd9, + 1, // sub_ovf = 0xda, + 1, // sub_ovf_un = 0xdb, + 1, // endfinally = 0xdc, + 5, // leave = 0xdd, + 2, // leave_s = 0xde, + 1, // stind_i = 0xdf, + 1, // conv_u = 0xe0, + Invalid, // = 0xe1, + Invalid, // = 0xe2, + Invalid, // = 0xe3, + Invalid, // = 0xe4, + Invalid, // = 0xe5, + Invalid, // = 0xe6, + Invalid, // = 0xe7, + Invalid, // = 0xe8, + Invalid, // = 0xe9, + Invalid, // = 0xea, + Invalid, // = 0xeb, + Invalid, // = 0xec, + Invalid, // = 0xed, + Invalid, // = 0xee, + Invalid, // = 0xef, + Invalid, // = 0xf0, + Invalid, // = 0xf1, + Invalid, // = 0xf2, + Invalid, // = 0xf3, + Invalid, // = 0xf4, + Invalid, // = 0xf5, + Invalid, // = 0xf6, + Invalid, // = 0xf7, + Invalid, // = 0xf8, + Invalid, // = 0xf9, + Invalid, // = 0xfa, + Invalid, // = 0xfb, + Invalid, // = 0xfc, + Invalid, // = 0xfd, + 1, // prefix1 = 0xfe, + Invalid, // = 0xff, + 2, // arglist = 0x100, + 2, // ceq = 0x101, + 2, // cgt = 0x102, + 2, // cgt_un = 0x103, + 2, // clt = 0x104, + 2, // clt_un = 0x105, + 6, // ldftn = 0x106, + 6, // ldvirtftn = 0x107, + Invalid, // = 0x108, + 4, // ldarg = 0x109, + 4, // ldarga = 0x10a, + 4, // starg = 0x10b, + 4, // ldloc = 0x10c, + 4, // ldloca = 0x10d, + 4, // stloc = 0x10e, + 2, // localloc = 0x10f, + Invalid, // = 0x110, + 2, // endfilter = 0x111, + 3, // unaligned = 0x112, + 2, // volatile_ = 0x113, + 2, // tail = 0x114, + 6, // initobj = 0x115, + 6, // constrained = 0x116, + 2, // cpblk = 0x117, + 2, // initblk = 0x118, + 3, // no = 0x119, + 2, // rethrow = 0x11a, + Invalid, // = 0x11b, + 6, // sizeof_ = 0x11c, + 2, // refanytype = 0x11d, + 2, // readonly_ = 0x11e, + }; + } +} diff --git a/external/corert/src/Common/src/TypeSystem/IL/ILProvider.cs b/external/corert/src/Common/src/TypeSystem/IL/ILProvider.cs index c1bcf486d7..d12904f383 100644 --- a/external/corert/src/Common/src/TypeSystem/IL/ILProvider.cs +++ b/external/corert/src/Common/src/TypeSystem/IL/ILProvider.cs @@ -56,7 +56,7 @@ namespace Internal.IL { case "Unsafe": { - if (owningType.Namespace == "System.Runtime.CompilerServices") + if (owningType.Namespace == "Internal.Runtime.CompilerServices") return UnsafeIntrinsics.EmitIL(method); } break; @@ -72,6 +72,12 @@ namespace Internal.IL return EETypePtrOfIntrinsic.EmitIL(method); } break; + case "RuntimeAugments": + { + if (owningType.Namespace == "Internal.Runtime.Augments" && method.Name == "GetCanonType") + return GetCanonTypeIntrinsic.EmitIL(method); + } + break; } return null; @@ -90,11 +96,14 @@ namespace Internal.IL if (owningType == null) return null; + string methodName = method.Name; + switch (owningType.Name) { case "RuntimeHelpers": { - if (owningType.Namespace == "System.Runtime.CompilerServices" && method.Name == "IsReferenceOrContainsReferences") + if ((methodName == "IsReferenceOrContainsReferences" || methodName == "IsReference") + && owningType.Namespace == "System.Runtime.CompilerServices") { TypeDesc elementType = method.Instantiation[0]; @@ -102,8 +111,11 @@ namespace Internal.IL if (elementType.IsCanonicalSubtype(CanonicalFormKind.Universal)) return null; - bool result = elementType.IsGCPointer || - (elementType.IsDefType ? ((DefType)elementType).ContainsGCPointers : false); + bool result = elementType.IsGCPointer; + if (methodName == "IsReferenceOrContainsReferences") + { + result |= (elementType.IsDefType ? ((DefType)elementType).ContainsGCPointers : false); + } return new ILStubMethodIL(method, new byte[] { result ? (byte)ILOpcode.ldc_i4_1 : (byte)ILOpcode.ldc_i4_0, @@ -112,6 +124,113 @@ namespace Internal.IL } } break; + case "Comparer`1": + { + if (methodName == "Create" && owningType.Namespace == "System.Collections.Generic") + return ComparerIntrinsics.EmitComparerCreate(method); + } + break; + case "EqualityComparer`1": + { + if (methodName == "Create" && owningType.Namespace == "System.Collections.Generic") + return ComparerIntrinsics.EmitEqualityComparerCreate(method); + } + break; + case "EqualityComparerHelpers": + { + if (owningType.Namespace != "Internal.IntrinsicSupport") + return null; + + if (methodName == "EnumOnlyEquals") + { + // EnumOnlyEquals would basically like to do this: + // static bool EnumOnlyEquals(T x, T y) where T: struct => x == y; + // This is not legal though. + // We don't want to do this: + // static bool EnumOnlyEquals(T x, T y) where T: struct => x.Equals(y); + // Because it would box y. + // So we resort to some per-instantiation magic. + + TypeDesc elementType = method.Instantiation[0]; + if (!elementType.IsEnum) + return null; + + ILOpcode convInstruction; + if (((DefType)elementType).InstanceFieldSize.AsInt <= 4) + { + convInstruction = ILOpcode.conv_i4; + } + else + { + Debug.Assert(((DefType)elementType).InstanceFieldSize.AsInt == 8); + convInstruction = ILOpcode.conv_i8; + } + + return new ILStubMethodIL(method, new byte[] { + (byte)ILOpcode.ldarg_0, + (byte)convInstruction, + (byte)ILOpcode.ldarg_1, + (byte)convInstruction, + (byte)ILOpcode.prefix1, unchecked((byte)ILOpcode.ceq), + (byte)ILOpcode.ret, + }, + Array.Empty(), null); + } + else if (methodName == "GetComparerForReferenceTypesOnly") + { + TypeDesc elementType = method.Instantiation[0]; + if (!elementType.IsRuntimeDeterminedSubtype + && !elementType.IsCanonicalSubtype(CanonicalFormKind.Any) + && !elementType.IsGCPointer) + { + return new ILStubMethodIL(method, new byte[] { + (byte)ILOpcode.ldnull, + (byte)ILOpcode.ret + }, + Array.Empty(), null); + } + } + else if (methodName == "StructOnlyEquals") + { + TypeDesc elementType = method.Instantiation[0]; + if (!elementType.IsRuntimeDeterminedSubtype + && !elementType.IsCanonicalSubtype(CanonicalFormKind.Any) + && !elementType.IsGCPointer) + { + Debug.Assert(elementType.IsValueType); + + TypeSystemContext context = elementType.Context; + MetadataType helperType = context.SystemModule.GetKnownType("Internal.IntrinsicSupport", "EqualityComparerHelpers"); + + MethodDesc methodToCall = null; + if (elementType.IsEnum) + { + methodToCall = helperType.GetKnownMethod("EnumOnlyEquals", null).MakeInstantiatedMethod(elementType); + } + else if (elementType.IsNullable && ComparerIntrinsics.ImplementsIEquatable(elementType.Instantiation[0])) + { + methodToCall = helperType.GetKnownMethod("StructOnlyEqualsNullable", null).MakeInstantiatedMethod(elementType.Instantiation[0]); + } + else if (ComparerIntrinsics.ImplementsIEquatable(elementType)) + { + methodToCall = helperType.GetKnownMethod("StructOnlyEqualsIEquatable", null).MakeInstantiatedMethod(elementType); + } + + if (methodToCall != null) + { + return new ILStubMethodIL(method, new byte[] + { + (byte)ILOpcode.ldarg_0, + (byte)ILOpcode.ldarg_1, + (byte)ILOpcode.call, 1, 0, 0, 0, + (byte)ILOpcode.ret + }, + Array.Empty(), new object[] { methodToCall }); + } + } + } + } + break; } return null; diff --git a/external/corert/src/Common/src/TypeSystem/IL/ILStackHelper.cs b/external/corert/src/Common/src/TypeSystem/IL/ILStackHelper.cs new file mode 100644 index 0000000000..d30532e137 --- /dev/null +++ b/external/corert/src/Common/src/TypeSystem/IL/ILStackHelper.cs @@ -0,0 +1,474 @@ +// 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.Diagnostics; + +using Internal.TypeSystem; + +namespace Internal.IL +{ + public static class ILStackHelper + { + /// + /// Validates that the CIL evaluation stack is properly balanced. + /// + [Conditional("DEBUG")] + public static void CheckStackBalance(this MethodIL methodIL) + { + methodIL.ComputeMaxStack(); + } + + /// + /// Computes the maximum number of items that can be pushed onto the CIL evaluation stack. + /// + public static int ComputeMaxStack(this MethodIL methodIL) + { + const int StackHeightNotSet = Int32.MinValue; + + byte[] ilbytes = methodIL.GetILBytes(); + int currentOffset = 0; + int stackHeight = 0; + int maxStack = 0; + + // TODO: Use Span for this and stackalloc the array if reasonably sized + int[] stackHeights = new int[ilbytes.Length]; + for (int i = 0; i < stackHeights.Length; i++) + stackHeights[i] = StackHeightNotSet; + + // Catch and filter clauses have a known non-zero stack height. + foreach (ILExceptionRegion region in methodIL.GetExceptionRegions()) + { + if (region.Kind == ILExceptionRegionKind.Catch) + { + stackHeights[region.HandlerOffset] = 1; + } + else if (region.Kind == ILExceptionRegionKind.Filter) + { + stackHeights[region.FilterOffset] = 1; + stackHeights[region.HandlerOffset] = 1; + } + } + + while (currentOffset < ilbytes.Length) + { + ILOpcode opcode = (ILOpcode)ilbytes[currentOffset]; + if (opcode == ILOpcode.prefix1) + opcode = 0x100 + (ILOpcode)ilbytes[currentOffset + 1]; + + // The stack height could be unknown if the previous instruction + // was an unconditional control transfer. + // In that case we check if we have a known stack height due to + // this instruction being a target of a previous branch or an EH block. + if (stackHeight == StackHeightNotSet) + stackHeight = stackHeights[currentOffset]; + + // If we still don't know the stack height, ECMA-335 III.1.7.5 + // "Backward branch constraint" demands the evaluation stack be empty. + if (stackHeight == StackHeightNotSet) + stackHeight = 0; + + // Remeber the stack height at this offset. + Debug.Assert(stackHeights[currentOffset] == StackHeightNotSet + || stackHeights[currentOffset] == stackHeight); + stackHeights[currentOffset] = stackHeight; + + bool isVariableSize = false; + switch (opcode) + { + case ILOpcode.arglist: + case ILOpcode.dup: + case ILOpcode.ldc_i4: + case ILOpcode.ldc_i4_0: + case ILOpcode.ldc_i4_1: + case ILOpcode.ldc_i4_2: + case ILOpcode.ldc_i4_3: + case ILOpcode.ldc_i4_4: + case ILOpcode.ldc_i4_5: + case ILOpcode.ldc_i4_6: + case ILOpcode.ldc_i4_7: + case ILOpcode.ldc_i4_8: + case ILOpcode.ldc_i4_m1: + case ILOpcode.ldc_i4_s: + case ILOpcode.ldc_i8: + case ILOpcode.ldc_r4: + case ILOpcode.ldc_r8: + case ILOpcode.ldftn: + case ILOpcode.ldnull: + case ILOpcode.ldsfld: + case ILOpcode.ldsflda: + case ILOpcode.ldstr: + case ILOpcode.ldtoken: + case ILOpcode.ldarg: + case ILOpcode.ldarg_0: + case ILOpcode.ldarg_1: + case ILOpcode.ldarg_2: + case ILOpcode.ldarg_3: + case ILOpcode.ldarg_s: + case ILOpcode.ldarga: + case ILOpcode.ldarga_s: + case ILOpcode.ldloc: + case ILOpcode.ldloc_0: + case ILOpcode.ldloc_1: + case ILOpcode.ldloc_2: + case ILOpcode.ldloc_3: + case ILOpcode.ldloc_s: + case ILOpcode.ldloca: + case ILOpcode.ldloca_s: + case ILOpcode.sizeof_: + stackHeight += 1; + break; + + case ILOpcode.add: + case ILOpcode.add_ovf: + case ILOpcode.add_ovf_un: + case ILOpcode.and: + case ILOpcode.ceq: + case ILOpcode.cgt: + case ILOpcode.cgt_un: + case ILOpcode.clt: + case ILOpcode.clt_un: + case ILOpcode.div: + case ILOpcode.div_un: + case ILOpcode.initobj: + case ILOpcode.ldelem: + case ILOpcode.ldelem_i: + case ILOpcode.ldelem_i1: + case ILOpcode.ldelem_i2: + case ILOpcode.ldelem_i4: + case ILOpcode.ldelem_i8: + case ILOpcode.ldelem_r4: + case ILOpcode.ldelem_r8: + case ILOpcode.ldelem_ref: + case ILOpcode.ldelem_u1: + case ILOpcode.ldelem_u2: + case ILOpcode.ldelem_u4: + case ILOpcode.ldelema: + case ILOpcode.mkrefany: + case ILOpcode.mul: + case ILOpcode.mul_ovf: + case ILOpcode.mul_ovf_un: + case ILOpcode.or: + case ILOpcode.pop: + case ILOpcode.rem: + case ILOpcode.rem_un: + case ILOpcode.shl: + case ILOpcode.shr: + case ILOpcode.shr_un: + case ILOpcode.stsfld: + case ILOpcode.sub: + case ILOpcode.sub_ovf: + case ILOpcode.sub_ovf_un: + case ILOpcode.xor: + case ILOpcode.starg: + case ILOpcode.starg_s: + case ILOpcode.stloc: + case ILOpcode.stloc_0: + case ILOpcode.stloc_1: + case ILOpcode.stloc_2: + case ILOpcode.stloc_3: + case ILOpcode.stloc_s: + Debug.Assert(stackHeight > 0); + stackHeight -= 1; + break; + + case ILOpcode.throw_: + Debug.Assert(stackHeight > 0); + stackHeight = StackHeightNotSet; + break; + + case ILOpcode.br: + case ILOpcode.leave: + case ILOpcode.brfalse: + case ILOpcode.brtrue: + case ILOpcode.beq: + case ILOpcode.bge: + case ILOpcode.bge_un: + case ILOpcode.bgt: + case ILOpcode.bgt_un: + case ILOpcode.ble: + case ILOpcode.ble_un: + case ILOpcode.blt: + case ILOpcode.blt_un: + case ILOpcode.bne_un: + { + int target = currentOffset + ReadInt32(ilbytes, currentOffset + 1) + 5; + + int adjustment; + bool isConditional; + if (opcode == ILOpcode.br || opcode == ILOpcode.leave) + { + isConditional = false; + adjustment = 0; + } + else if (opcode == ILOpcode.brfalse || opcode == ILOpcode.brtrue) + { + isConditional = true; + adjustment = 1; + } + else + { + isConditional = true; + adjustment = 2; + } + + Debug.Assert(stackHeight >= adjustment); + stackHeight -= adjustment; + + Debug.Assert(stackHeights[target] == StackHeightNotSet + || stackHeights[target] == stackHeight); + + // Forward branch carries information about stack height at a future + // offset. We need to remember it. + if (target > currentOffset) + stackHeights[target] = stackHeight; + + if (!isConditional) + stackHeight = StackHeightNotSet; + } + break; + + case ILOpcode.br_s: + case ILOpcode.leave_s: + case ILOpcode.brfalse_s: + case ILOpcode.brtrue_s: + case ILOpcode.beq_s: + case ILOpcode.bge_s: + case ILOpcode.bge_un_s: + case ILOpcode.bgt_s: + case ILOpcode.bgt_un_s: + case ILOpcode.ble_s: + case ILOpcode.ble_un_s: + case ILOpcode.blt_s: + case ILOpcode.blt_un_s: + case ILOpcode.bne_un_s: + { + int target = currentOffset + (sbyte)ilbytes[currentOffset + 1] + 2; + + int adjustment; + bool isConditional; + if (opcode == ILOpcode.br_s || opcode == ILOpcode.leave_s) + { + isConditional = false; + adjustment = 0; + } + else if (opcode == ILOpcode.brfalse_s || opcode == ILOpcode.brtrue_s) + { + isConditional = true; + adjustment = 1; + } + else + { + isConditional = true; + adjustment = 2; + } + + Debug.Assert(stackHeight >= adjustment); + stackHeight -= adjustment; + + Debug.Assert(stackHeights[target] == StackHeightNotSet + || stackHeights[target] == stackHeight); + + // Forward branch carries information about stack height at a future + // offset. We need to remember it. + if (target > currentOffset) + stackHeights[target] = stackHeight; + + if (!isConditional) + stackHeight = StackHeightNotSet; + } + break; + + case ILOpcode.call: + case ILOpcode.calli: + case ILOpcode.callvirt: + case ILOpcode.newobj: + { + int token = ReadILToken(ilbytes, currentOffset + 1); + object obj = methodIL.GetObject(token); + MethodSignature sig = obj is MethodSignature ? + (MethodSignature)obj : + ((MethodDesc)obj).Signature; + int adjustment = sig.Length; + if (opcode == ILOpcode.newobj) + { + adjustment--; + } + else + { + if (opcode == ILOpcode.calli) + adjustment++; + if (!sig.IsStatic) + adjustment++; + if (!sig.ReturnType.IsVoid) + adjustment--; + } + + Debug.Assert(stackHeight >= adjustment); + stackHeight -= adjustment; + } + break; + + case ILOpcode.ret: + { + bool hasReturnValue = !methodIL.OwningMethod.Signature.ReturnType.IsVoid; + if (hasReturnValue) + stackHeight -= 1; + + Debug.Assert(stackHeight == 0); + + stackHeight = StackHeightNotSet; + } + break; + + case ILOpcode.cpobj: + case ILOpcode.stfld: + case ILOpcode.stind_i: + case ILOpcode.stind_i1: + case ILOpcode.stind_i2: + case ILOpcode.stind_i4: + case ILOpcode.stind_i8: + case ILOpcode.stind_r4: + case ILOpcode.stind_r8: + case ILOpcode.stind_ref: + case ILOpcode.stobj: + Debug.Assert(stackHeight > 1); + stackHeight -= 2; + break; + + case ILOpcode.cpblk: + case ILOpcode.initblk: + case ILOpcode.stelem: + case ILOpcode.stelem_i: + case ILOpcode.stelem_i1: + case ILOpcode.stelem_i2: + case ILOpcode.stelem_i4: + case ILOpcode.stelem_i8: + case ILOpcode.stelem_r4: + case ILOpcode.stelem_r8: + case ILOpcode.stelem_ref: + Debug.Assert(stackHeight > 2); + stackHeight -= 3; + break; + + case ILOpcode.break_: + case ILOpcode.constrained: + case ILOpcode.no: + case ILOpcode.nop: + case ILOpcode.readonly_: + case ILOpcode.tail: + case ILOpcode.unaligned: + case ILOpcode.volatile_: + break; + + case ILOpcode.endfilter: + Debug.Assert(stackHeight > 0); + stackHeight = StackHeightNotSet; + break; + + case ILOpcode.jmp: + case ILOpcode.rethrow: + case ILOpcode.endfinally: + stackHeight = StackHeightNotSet; + break; + + case ILOpcode.box: + case ILOpcode.castclass: + case ILOpcode.ckfinite: + case ILOpcode.conv_i: + case ILOpcode.conv_i1: + case ILOpcode.conv_i2: + case ILOpcode.conv_i4: + case ILOpcode.conv_i8: + case ILOpcode.conv_ovf_i: + case ILOpcode.conv_ovf_i_un: + case ILOpcode.conv_ovf_i1: + case ILOpcode.conv_ovf_i1_un: + case ILOpcode.conv_ovf_i2: + case ILOpcode.conv_ovf_i2_un: + case ILOpcode.conv_ovf_i4: + case ILOpcode.conv_ovf_i4_un: + case ILOpcode.conv_ovf_i8: + case ILOpcode.conv_ovf_i8_un: + case ILOpcode.conv_ovf_u: + case ILOpcode.conv_ovf_u_un: + case ILOpcode.conv_ovf_u1: + case ILOpcode.conv_ovf_u1_un: + case ILOpcode.conv_ovf_u2: + case ILOpcode.conv_ovf_u2_un: + case ILOpcode.conv_ovf_u4: + case ILOpcode.conv_ovf_u4_un: + case ILOpcode.conv_ovf_u8: + case ILOpcode.conv_ovf_u8_un: + case ILOpcode.conv_r_un: + case ILOpcode.conv_r4: + case ILOpcode.conv_r8: + case ILOpcode.conv_u: + case ILOpcode.conv_u1: + case ILOpcode.conv_u2: + case ILOpcode.conv_u4: + case ILOpcode.conv_u8: + case ILOpcode.isinst: + case ILOpcode.ldfld: + case ILOpcode.ldflda: + case ILOpcode.ldind_i: + case ILOpcode.ldind_i1: + case ILOpcode.ldind_i2: + case ILOpcode.ldind_i4: + case ILOpcode.ldind_i8: + case ILOpcode.ldind_r4: + case ILOpcode.ldind_r8: + case ILOpcode.ldind_ref: + case ILOpcode.ldind_u1: + case ILOpcode.ldind_u2: + case ILOpcode.ldind_u4: + case ILOpcode.ldlen: + case ILOpcode.ldobj: + case ILOpcode.ldvirtftn: + case ILOpcode.localloc: + case ILOpcode.neg: + case ILOpcode.newarr: + case ILOpcode.not: + case ILOpcode.refanytype: + case ILOpcode.refanyval: + case ILOpcode.unbox: + case ILOpcode.unbox_any: + Debug.Assert(stackHeight > 0); + break; + + case ILOpcode.switch_: + Debug.Assert(stackHeight > 0); + isVariableSize = true; + stackHeight -= 1; + currentOffset += 1 + (ReadInt32(ilbytes, currentOffset + 1) * 4) + 4; + break; + + default: + Debug.Fail("Unknown instruction"); + break; + } + + if (!isVariableSize) + currentOffset += opcode.GetSize(); + + maxStack = Math.Max(maxStack, stackHeight); + } + + return maxStack; + } + + private static int ReadInt32(byte[] ilBytes, int offset) + { + return ilBytes[offset] + + (ilBytes[offset + 1] << 8) + + (ilBytes[offset + 2] << 16) + + (ilBytes[offset + 3] << 24); + } + + private static int ReadILToken(byte[] ilBytes, int offset) + { + return ReadInt32(ilBytes, offset); + } + } +} diff --git a/external/corert/src/Common/src/TypeSystem/IL/MethodILDebugView.cs b/external/corert/src/Common/src/TypeSystem/IL/MethodILDebugView.cs index dc2c1078bb..1f5fbcb4a0 100644 --- a/external/corert/src/Common/src/TypeSystem/IL/MethodILDebugView.cs +++ b/external/corert/src/Common/src/TypeSystem/IL/MethodILDebugView.cs @@ -21,7 +21,7 @@ namespace Internal.IL { get { - ILDisassember disasm = new ILDisassember(_methodIL); + ILDisassembler disasm = new ILDisassembler(_methodIL); StringBuilder sb = new StringBuilder(); diff --git a/external/corert/src/Common/src/TypeSystem/IL/Stubs/ComparerIntrinsics.cs b/external/corert/src/Common/src/TypeSystem/IL/Stubs/ComparerIntrinsics.cs new file mode 100644 index 0000000000..9919805e28 --- /dev/null +++ b/external/corert/src/Common/src/TypeSystem/IL/Stubs/ComparerIntrinsics.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. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL.Stubs +{ + /// + /// Intrinsic support arround EqualityComparer<T> and Comparer<T>. + /// + public static class ComparerIntrinsics + { + /// + /// Generates a specialized method body for Comparer`1.Create or returns null if no specialized body can be generated. + /// + public static MethodIL EmitComparerCreate(MethodDesc target) + { + return EmitComparerAndEqualityComparerCreateCommon(target, "Comparer", "IComparable`1"); + } + + /// + /// Generates a specialized method body for EqualityComparer`1.Create or returns null if no specialized body can be generated. + /// + public static MethodIL EmitEqualityComparerCreate(MethodDesc target) + { + return EmitComparerAndEqualityComparerCreateCommon(target, "EqualityComparer", "IEquatable`1"); + } + + /// + /// Gets the concrete type EqualityComparer`1.Create returns or null if it's not known at compile time. + /// + public static TypeDesc GetEqualityComparerForType(TypeDesc comparand) + { + return GetComparerForType(comparand, "EqualityComparer", "IEquatable`1"); + } + + private static MethodIL EmitComparerAndEqualityComparerCreateCommon(MethodDesc methodBeingGenerated, string flavor, string interfaceName) + { + // We expect the method to be fully instantiated + Debug.Assert(!methodBeingGenerated.IsTypicalMethodDefinition); + + TypeDesc owningType = methodBeingGenerated.OwningType; + TypeDesc comparedType = owningType.Instantiation[0]; + + // If the type is canonical, we use the default implementation provided by the class library. + // This will rely on the type loader to load the proper type at runtime. + if (comparedType.IsCanonicalSubtype(CanonicalFormKind.Any)) + return null; + + TypeDesc comparerType = GetComparerForType(comparedType, flavor, interfaceName); + Debug.Assert(comparerType != null); + + ILEmitter emitter = new ILEmitter(); + var codeStream = emitter.NewCodeStream(); + + codeStream.Emit(ILOpcode.newobj, emitter.NewToken(comparerType.GetParameterlessConstructor())); + codeStream.Emit(ILOpcode.dup); + codeStream.Emit(ILOpcode.stsfld, emitter.NewToken(owningType.GetKnownField("_default"))); + codeStream.Emit(ILOpcode.ret); + + return emitter.Link(methodBeingGenerated); + } + + /// + /// Gets the comparer type that is suitable to compare instances of + /// or null if such comparer cannot be determined at compile time. + /// + private static TypeDesc GetComparerForType(TypeDesc type, string flavor, string interfaceName) + { + TypeSystemContext context = type.Context; + + if (context.IsCanonicalDefinitionType(type, CanonicalFormKind.Any) || + (type.IsRuntimeDeterminedSubtype && !type.HasInstantiation)) + { + // The comparer will be determined at runtime. We can't tell the exact type at compile time. + return null; + } + else if (type.IsNullable) + { + TypeDesc nullableType = type.Instantiation[0]; + + if (context.IsCanonicalDefinitionType(nullableType, CanonicalFormKind.Universal)) + { + // We can't tell at compile time either. + return null; + } + else if (ImplementsInterfaceOfSelf(nullableType, interfaceName)) + { + return context.SystemModule.GetKnownType("System.Collections.Generic", $"Nullable{flavor}`1") + .MakeInstantiatedType(nullableType); + } + } + else if (flavor == "EqualityComparer" && type.IsEnum) + { + // Enums have a specialized comparer that avoids boxing + return context.SystemModule.GetKnownType("System.Collections.Generic", $"Enum{flavor}`1") + .MakeInstantiatedType(type); + } + else if (ImplementsInterfaceOfSelf(type, interfaceName)) + { + return context.SystemModule.GetKnownType("System.Collections.Generic", $"Generic{flavor}`1") + .MakeInstantiatedType(type); + } + + return context.SystemModule.GetKnownType("System.Collections.Generic", $"Object{flavor}`1") + .MakeInstantiatedType(type); + } + + public static TypeDesc[] GetPotentialComparersForType(TypeDesc type) + { + return GetPotentialComparersForTypeCommon(type, "Comparer", "IComparable`1"); + } + + public static TypeDesc[] GetPotentialEqualityComparersForType(TypeDesc type) + { + return GetPotentialComparersForTypeCommon(type, "EqualityComparer", "IEquatable`1"); + } + + /// + /// Gets the set of template types needed to support loading comparers for the give canonical type at runtime. + /// + private static TypeDesc[] GetPotentialComparersForTypeCommon(TypeDesc type, string flavor, string interfaceName) + { + Debug.Assert(type.IsCanonicalSubtype(CanonicalFormKind.Any)); + + TypeDesc exactComparer = GetComparerForType(type, flavor, interfaceName); + + if (exactComparer != null) + { + // If we have a comparer that is exactly known at runtime, we're done. + // This will typically be if type is a generic struct, generic enum, or a nullable. + return new TypeDesc[] { exactComparer }; + } + + TypeSystemContext context = type.Context; + + if (context.IsCanonicalDefinitionType(type, CanonicalFormKind.Universal)) + { + // This can be any of the comparers we have. + + ArrayBuilder universalComparers = new ArrayBuilder(); + + universalComparers.Add(context.SystemModule.GetKnownType("System.Collections.Generic", $"Nullable{flavor}`1") + .MakeInstantiatedType(type)); + + if (flavor == "EqualityComparer") + universalComparers.Add(context.SystemModule.GetKnownType("System.Collections.Generic", $"Enum{flavor}`1") + .MakeInstantiatedType(type)); + + universalComparers.Add(context.SystemModule.GetKnownType("System.Collections.Generic", $"Generic{flavor}`1") + .MakeInstantiatedType(type)); + + universalComparers.Add(context.SystemModule.GetKnownType("System.Collections.Generic", $"Object{flavor}`1") + .MakeInstantiatedType(type)); + + return universalComparers.ToArray(); + } + + // This mirrors exactly what GetUnknownEquatableComparer and GetUnknownComparer (in the class library) + // will need at runtime. This is the general purpose code path that can be used to compare + // anything. + + if (type.IsNullable) + { + TypeDesc nullableType = type.Instantiation[0]; + + // This should only be reachabe for universal canon code. + // For specific canon, this should have been an exact match above. + Debug.Assert(context.IsCanonicalDefinitionType(nullableType, CanonicalFormKind.Universal)); + + return new TypeDesc[] + { + context.SystemModule.GetKnownType("System.Collections.Generic", $"Nullable{flavor}`1") + .MakeInstantiatedType(nullableType), + context.SystemModule.GetKnownType("System.Collections.Generic", $"Object{flavor}`1") + .MakeInstantiatedType(type), + }; + } + + return new TypeDesc[] + { + context.SystemModule.GetKnownType("System.Collections.Generic", $"Generic{flavor}`1") + .MakeInstantiatedType(type), + context.SystemModule.GetKnownType("System.Collections.Generic", $"Object{flavor}`1") + .MakeInstantiatedType(type), + }; + } + + public static bool ImplementsIEquatable(TypeDesc type) + => ImplementsInterfaceOfSelf(type, "IEquatable`1"); + + private static bool ImplementsInterfaceOfSelf(TypeDesc type, string interfaceName) + { + MetadataType interfaceType = null; + + foreach (TypeDesc implementedInterface in type.RuntimeInterfaces) + { + Instantiation interfaceInstantiation = implementedInterface.Instantiation; + if (interfaceInstantiation.Length == 1 && + interfaceInstantiation[0] == type) + { + if (interfaceType == null) + interfaceType = type.Context.SystemModule.GetKnownType("System", interfaceName); + + if (implementedInterface.GetTypeDefinition() == interfaceType) + return true; + } + } + + return false; + } + } +} diff --git a/external/corert/src/Common/src/TypeSystem/IL/Stubs/DebuggerSteppingHelpers.cs b/external/corert/src/Common/src/TypeSystem/IL/Stubs/DebuggerSteppingHelpers.cs new file mode 100644 index 0000000000..5fb1f470ce --- /dev/null +++ b/external/corert/src/Common/src/TypeSystem/IL/Stubs/DebuggerSteppingHelpers.cs @@ -0,0 +1,19 @@ +// 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. + +namespace Internal.IL.Stubs +{ + public static class DebuggerSteppingHelpers + { + public static void MarkDebuggerStepThroughPoint(this ILCodeStream codeStream) + { + codeStream.DefineSequencePoint("", 0xF00F00); + } + + public static void MarkDebuggerStepInPoint(this ILCodeStream codeStream) + { + codeStream.DefineSequencePoint("", 0xFEEFEE); + } + } +} diff --git a/external/corert/src/Common/src/TypeSystem/IL/Stubs/DelegateMarshallingMethodThunk.Sorting.cs b/external/corert/src/Common/src/TypeSystem/IL/Stubs/DelegateMarshallingMethodThunk.Sorting.cs index 61b3615ac2..2f5195188f 100644 --- a/external/corert/src/Common/src/TypeSystem/IL/Stubs/DelegateMarshallingMethodThunk.Sorting.cs +++ b/external/corert/src/Common/src/TypeSystem/IL/Stubs/DelegateMarshallingMethodThunk.Sorting.cs @@ -14,7 +14,7 @@ namespace Internal.IL.Stubs protected internal override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) { var otherMethod = (DelegateMarshallingMethodThunk)other; - int result = Kind - otherMethod.Kind; + int result = (int)Kind - (int)otherMethod.Kind; if (result != 0) return result; diff --git a/external/corert/src/Common/src/TypeSystem/IL/Stubs/DelegateMarshallingMethodThunk.cs b/external/corert/src/Common/src/TypeSystem/IL/Stubs/DelegateMarshallingMethodThunk.cs index 34a89d3aa5..05fbbdd372 100644 --- a/external/corert/src/Common/src/TypeSystem/IL/Stubs/DelegateMarshallingMethodThunk.cs +++ b/external/corert/src/Common/src/TypeSystem/IL/Stubs/DelegateMarshallingMethodThunk.cs @@ -93,6 +93,22 @@ namespace Internal.IL.Stubs } } + private TypeDesc GetNativeMethodParameterType(TypeDesc managedType, MarshalAsDescriptor marshalAs, InteropStateManager interopStateManager, bool isReturn, bool isAnsi) + { + TypeDesc nativeType; + try + { + nativeType = MarshalHelpers.GetNativeMethodParameterType(managedType, marshalAs, interopStateManager, isReturn, isAnsi); + } + catch (NotSupportedException) + { + // if marshalling is not supported for this type the generated stubs will emit appropriate + // error message. We just set native type to be same as managedtype + nativeType = managedType; + } + return nativeType; + } + public override MethodSignature Signature { get @@ -123,7 +139,12 @@ namespace Internal.IL.Stubs marshalAs = parameterMetadataArray[parameterIndex++].MarshalAsDescriptor; } - TypeDesc nativeReturnType = MarshalHelpers.GetNativeMethodParameterType(delegateSignature.ReturnType, null, _interopStateManager, true, isAnsi); + TypeDesc nativeReturnType = GetNativeMethodParameterType(delegateSignature.ReturnType, + marshalAs, + _interopStateManager, + isReturn:true, + isAnsi:isAnsi); + for (int i = 0; i < delegateSignature.Length; i++) { int sequence = i + 1; @@ -142,7 +163,11 @@ namespace Internal.IL.Stubs var managedType = isByRefType ? delegateSignature[i].GetParameterType() : delegateSignature[i]; - var nativeType = MarshalHelpers.GetNativeMethodParameterType(managedType, marshalAs, _interopStateManager, false, isAnsi); + var nativeType = GetNativeMethodParameterType(managedType, + marshalAs, + _interopStateManager, + isReturn:false, + isAnsi:isAnsi); nativeParameterTypes[i] = isByRefType ? nativeType.MakePointerType() : nativeType; } @@ -184,7 +209,7 @@ namespace Internal.IL.Stubs case DelegateMarshallingMethodThunkKind.ForwardNativeFunctionWrapper: return "ForwardNativeFunctionWrapper"; default: - System.Diagnostics.Debug.Assert(false, "Unexpected DelegateMarshallingMethodThunkKind."); + System.Diagnostics.Debug.Fail("Unexpected DelegateMarshallingMethodThunkKind."); return String.Empty; } } diff --git a/external/corert/src/Common/src/TypeSystem/IL/Stubs/DelegateMethodILEmitter.cs b/external/corert/src/Common/src/TypeSystem/IL/Stubs/DelegateMethodILEmitter.cs index 32fa232e91..ef798375df 100644 --- a/external/corert/src/Common/src/TypeSystem/IL/Stubs/DelegateMethodILEmitter.cs +++ b/external/corert/src/Common/src/TypeSystem/IL/Stubs/DelegateMethodILEmitter.cs @@ -29,7 +29,7 @@ namespace Internal.IL.Stubs if (method.Name == ".ctor") { // We only support delegate creation if the IL follows the delegate creation verifiability requirements - // described in ECMA-335 III.4.21 (newobj – create a new object). The codegen is expected to + // described in ECMA-335 III.4.21 (newobj - create a new object). The codegen is expected to // intrinsically expand the pattern. // If the delegate creation doesn't follow the pattern, we generate code that throws at runtime. // We could potentially implement this (unreliably) through the use of reflection metadata, diff --git a/external/corert/src/Common/src/TypeSystem/IL/Stubs/DelegateThunks.cs b/external/corert/src/Common/src/TypeSystem/IL/Stubs/DelegateThunks.cs index abb60e283e..af8cb9a057 100644 --- a/external/corert/src/Common/src/TypeSystem/IL/Stubs/DelegateThunks.cs +++ b/external/corert/src/Common/src/TypeSystem/IL/Stubs/DelegateThunks.cs @@ -911,16 +911,14 @@ namespace Internal.IL.Stubs public override MethodIL EmitIL() { - const DelegateThunkKind maxThunkKind = DelegateThunkKind.ObjectArrayThunk + 1; - ILEmitter emitter = new ILEmitter(); var codeStream = emitter.NewCodeStream(); ILCodeLabel returnNullLabel = emitter.NewCodeLabel(); - ILCodeLabel[] labels = new ILCodeLabel[(int)maxThunkKind]; - for (DelegateThunkKind i = 0; i < maxThunkKind; i++) + ILCodeLabel[] labels = new ILCodeLabel[(int)DelegateThunkCollection.MaxThunkKind]; + for (DelegateThunkKind i = 0; i < DelegateThunkCollection.MaxThunkKind; i++) { MethodDesc thunk = _delegateInfo.Thunks[i]; if (thunk != null) @@ -934,7 +932,7 @@ namespace Internal.IL.Stubs codeStream.Emit(ILOpcode.br, returnNullLabel); - for (DelegateThunkKind i = 0; i < maxThunkKind; i++) + for (DelegateThunkKind i = 0; i < DelegateThunkCollection.MaxThunkKind; i++) { MethodDesc thunk = _delegateInfo.Thunks[i]; if (thunk != null) diff --git a/external/corert/src/Common/src/TypeSystem/IL/Stubs/GetCanonTypeIntrinsic.cs b/external/corert/src/Common/src/TypeSystem/IL/Stubs/GetCanonTypeIntrinsic.cs new file mode 100644 index 0000000000..ca8fd38c10 --- /dev/null +++ b/external/corert/src/Common/src/TypeSystem/IL/Stubs/GetCanonTypeIntrinsic.cs @@ -0,0 +1,64 @@ +// 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 Internal.Runtime; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL.Stubs +{ + /// + /// Provides method body for the GetCanonType intrinsic. + /// + public static class GetCanonTypeIntrinsic + { + public static MethodIL EmitIL(MethodDesc target) + { + Debug.Assert(target.Signature.Length == 1); + + ILEmitter emitter = new ILEmitter(); + var codeStream = emitter.NewCodeStream(); + + TypeSystemContext context = target.Context; + TypeDesc runtimeTypeHandleType = context.GetWellKnownType(WellKnownType.RuntimeTypeHandle); + Debug.Assert(target.Signature.ReturnType == runtimeTypeHandleType); + + if (context.SupportsCanon) + { + ILCodeLabel lNotCanon = emitter.NewCodeLabel(); + codeStream.Emit(ILOpcode.ldarg_0); + codeStream.EmitLdc((int)CanonTypeKind.NormalCanon); + codeStream.Emit(ILOpcode.bne_un, lNotCanon); + codeStream.Emit(ILOpcode.ldtoken, emitter.NewToken(context.CanonType)); + codeStream.Emit(ILOpcode.ret); + codeStream.EmitLabel(lNotCanon); + + // We're not conditioning this on SupportsUniversalCanon because the runtime type loader + // does a lot of comparisons against UniversalCanon and not having a RuntimeTypeHandle + // for it makes these checks awkward. + // Would be nice if we didn't have to emit the EEType if universal canonical code wasn't enabled + // at the time of compilation. + ILCodeLabel lNotUniversalCanon = emitter.NewCodeLabel(); + codeStream.Emit(ILOpcode.ldarg_0); + codeStream.EmitLdc((int)CanonTypeKind.UniversalCanon); + codeStream.Emit(ILOpcode.bne_un, lNotUniversalCanon); + codeStream.Emit(ILOpcode.ldtoken, emitter.NewToken(context.UniversalCanonType)); + codeStream.Emit(ILOpcode.ret); + codeStream.EmitLabel(lNotUniversalCanon); + } + + ILLocalVariable vNullTypeHandle = emitter.NewLocal(runtimeTypeHandleType); + codeStream.EmitLdLoca(vNullTypeHandle); + codeStream.Emit(ILOpcode.initobj, emitter.NewToken(runtimeTypeHandleType)); + codeStream.EmitLdLoc(vNullTypeHandle); + + codeStream.Emit(ILOpcode.ret); + + return emitter.Link(target); + } + } +} diff --git a/external/corert/src/Common/src/TypeSystem/IL/Stubs/ILEmitter.cs b/external/corert/src/Common/src/TypeSystem/IL/Stubs/ILEmitter.cs index b5af1ba34c..596a9b4bf0 100644 --- a/external/corert/src/Common/src/TypeSystem/IL/Stubs/ILEmitter.cs +++ b/external/corert/src/Common/src/TypeSystem/IL/Stubs/ILEmitter.cs @@ -28,6 +28,7 @@ namespace Internal.IL.Stubs internal byte[] _instructions; internal int _length; internal int _startOffsetForLinking; + internal ArrayBuilder _sequencePoints; private ArrayBuilder _offsetsNeedingPatching; @@ -204,6 +205,12 @@ namespace Internal.IL.Stubs } } + public void EmitUnaligned() + { + Emit(ILOpcode.unaligned); + EmitByte(1); + } + public void EmitLdInd(TypeDesc type) { switch (type.UnderlyingType.Category) @@ -249,7 +256,7 @@ namespace Internal.IL.Stubs Emit(ILOpcode.ldobj, _emitter.NewToken(type)); break; default: - Debug.Assert(false, "Unexpected TypeDesc category"); + Debug.Fail("Unexpected TypeDesc category"); break; } } @@ -298,7 +305,7 @@ namespace Internal.IL.Stubs Emit(ILOpcode.stobj, _emitter.NewToken(type)); break; default: - Debug.Assert(false, "Unexpected TypeDesc category"); + Debug.Fail("Unexpected TypeDesc category"); break; } } @@ -348,7 +355,7 @@ namespace Internal.IL.Stubs Emit(ILOpcode.stelem, _emitter.NewToken(type)); break; default: - Debug.Assert(false, "Unexpected TypeDesc category"); + Debug.Fail("Unexpected TypeDesc category"); break; } } @@ -398,7 +405,7 @@ namespace Internal.IL.Stubs Emit(ILOpcode.ldelem, _emitter.NewToken(type)); break; default: - Debug.Assert(false, "Unexpected TypeDesc category"); + Debug.Fail("Unexpected TypeDesc category"); break; } } @@ -432,6 +439,19 @@ namespace Internal.IL.Stubs _instructions[offset + 3] = (byte)(value >> 24); } } + + public void DefineSequencePoint(string document, int lineNumber) + { + // Last sequence point defined for this offset wins. + if (_sequencePoints.Count > 0 && _sequencePoints[_sequencePoints.Count - 1].Offset == _length) + { + _sequencePoints[_sequencePoints.Count - 1] = new ILSequencePoint(_length, document, lineNumber); + } + else + { + _sequencePoints.Add(new ILSequencePoint(_length, document, lineNumber)); + } + } } /// @@ -447,17 +467,26 @@ namespace Internal.IL.Stubs public class ILStubMethodIL : MethodIL { - private byte[] _ilBytes; - private LocalVariableDefinition[] _locals; - private Object[] _tokens; - private MethodDesc _method; + private readonly byte[] _ilBytes; + private readonly LocalVariableDefinition[] _locals; + private readonly Object[] _tokens; + private readonly MethodDesc _method; + private readonly MethodDebugInformation _debugInformation; - public ILStubMethodIL(MethodDesc owningMethod, byte[] ilBytes, LocalVariableDefinition[] locals, Object[] tokens) + private const int MaxStackNotSet = -1; + private int _maxStack; + + public ILStubMethodIL(MethodDesc owningMethod, byte[] ilBytes, LocalVariableDefinition[] locals, Object[] tokens, MethodDebugInformation debugInfo = null) { _ilBytes = ilBytes; _locals = locals; _tokens = tokens; _method = owningMethod; + _maxStack = MaxStackNotSet; + + if (debugInfo == null) + debugInfo = MethodDebugInformation.None; + _debugInformation = debugInfo; } public ILStubMethodIL(ILStubMethodIL methodIL) @@ -466,6 +495,8 @@ namespace Internal.IL.Stubs _locals = methodIL._locals; _tokens = methodIL._tokens; _method = methodIL._method; + _debugInformation = methodIL._debugInformation; + _maxStack = methodIL._maxStack; } public override MethodDesc OwningMethod @@ -480,12 +511,19 @@ namespace Internal.IL.Stubs { return _ilBytes; } + + public override MethodDebugInformation GetDebugInfo() + { + return _debugInformation; + } + public override int MaxStack { get { - // Conservative estimate... - return _ilBytes.Length; + if (_maxStack == MaxStackNotSet) + _maxStack = this.ComputeMaxStack(); + return _maxStack; } } @@ -511,6 +549,25 @@ namespace Internal.IL.Stubs } } + // Workaround for places that emit IL that doesn't conform to the ECMA-335 CIL stack requirements. + // https://github.com/dotnet/corert/issues/5152 + // This class and all references to it should be deleted when the issue is fixed. + // Do not add new references to this. + public class ILStubMethodILWithNonConformingStack : ILStubMethodIL + { + public ILStubMethodILWithNonConformingStack(MethodDesc owningMethod, byte[] ilBytes, LocalVariableDefinition[] locals, Object[] tokens, MethodDebugInformation debugInfo) + : base(owningMethod, ilBytes, locals, tokens, debugInfo) + { + } + + public ILStubMethodILWithNonConformingStack(ILStubMethodIL methodIL) + : base(methodIL) + { + } + + public override int MaxStack => GetILBytes().Length; + } + public class ILCodeLabel { private ILCodeStream _codeStream; @@ -608,14 +665,17 @@ namespace Internal.IL.Stubs return newLabel; } - public MethodIL Link(MethodDesc owningMethod) + public MethodIL Link(MethodDesc owningMethod, bool nonConformingStackWorkaround = false) { int totalLength = 0; + int numSequencePoints = 0; + for (int i = 0; i < _codeStreams.Count; i++) { ILCodeStream ilCodeStream = _codeStreams[i]; ilCodeStream._startOffsetForLinking = totalLength; totalLength += ilCodeStream._length; + numSequencePoints += ilCodeStream._sequencePoints.Count; } byte[] ilInstructions = new byte[totalLength]; @@ -628,7 +688,56 @@ namespace Internal.IL.Stubs copiedLength += ilCodeStream._length; } - return new ILStubMethodIL(owningMethod, ilInstructions, _locals.ToArray(), _tokens.ToArray()); + MethodDebugInformation debugInfo = null; + if (numSequencePoints > 0) + { + ILSequencePoint[] sequencePoints = new ILSequencePoint[numSequencePoints]; + int copiedSequencePointLength = 0; + for (int codeStreamIndex = 0; codeStreamIndex < _codeStreams.Count; codeStreamIndex++) + { + ILCodeStream ilCodeStream = _codeStreams[codeStreamIndex]; + + for (int sequencePointIndex = 0; sequencePointIndex < ilCodeStream._sequencePoints.Count; sequencePointIndex++) + { + ILSequencePoint sequencePoint = ilCodeStream._sequencePoints[sequencePointIndex]; + sequencePoints[copiedSequencePointLength] = new ILSequencePoint( + ilCodeStream._startOffsetForLinking + sequencePoint.Offset, + sequencePoint.Document, + sequencePoint.LineNumber); + copiedSequencePointLength++; + } + } + + debugInfo = new EmittedMethodDebugInformation(sequencePoints); + } + + ILStubMethodIL result; + if (nonConformingStackWorkaround) + { + // nonConformingStackWorkaround is a workaround for https://github.com/dotnet/corert/issues/5152 + result = new ILStubMethodILWithNonConformingStack(owningMethod, ilInstructions, _locals.ToArray(), _tokens.ToArray(), debugInfo); + } + else + { + result = new ILStubMethodIL(owningMethod, ilInstructions, _locals.ToArray(), _tokens.ToArray(), debugInfo); + result.CheckStackBalance(); + } + return result; + } + + private class EmittedMethodDebugInformation : MethodDebugInformation + { + private readonly ILSequencePoint[] _sequencePoints; + + public EmittedMethodDebugInformation(ILSequencePoint[] sequencePoints) + { + _sequencePoints = sequencePoints; + } + + public override IEnumerable GetSequencePoints() + { + return _sequencePoints; + } } } diff --git a/external/corert/src/Common/src/TypeSystem/IL/Stubs/PInvokeILEmitter.cs b/external/corert/src/Common/src/TypeSystem/IL/Stubs/PInvokeILEmitter.cs index 8e23eb77a8..6a0669b8c3 100644 --- a/external/corert/src/Common/src/TypeSystem/IL/Stubs/PInvokeILEmitter.cs +++ b/external/corert/src/Common/src/TypeSystem/IL/Stubs/PInvokeILEmitter.cs @@ -25,6 +25,7 @@ namespace Internal.IL.Stubs private readonly Marshaller[] _marshallers; private readonly PInvokeILEmitterConfiguration _pInvokeILEmitterConfiguration; private readonly PInvokeMetadata _importMetadata; + private readonly PInvokeFlags _flags; private readonly InteropStateManager _interopStateManager; private PInvokeILEmitter(MethodDesc targetMethod, PInvokeILEmitterConfiguration pinvokeILEmitterConfiguration, InteropStateManager interopStateManager) @@ -35,21 +36,23 @@ namespace Internal.IL.Stubs _importMetadata = targetMethod.GetPInvokeMethodMetadata(); _interopStateManager = interopStateManager; - PInvokeFlags flags = new PInvokeFlags(); - if (targetMethod.IsPInvoke) + // + // targetMethod could be either a PInvoke or a DelegateMarshallingMethodThunk + // ForwardNativeFunctionWrapper method thunks are marked as PInvokes, so it is + // important to check them first here so that we get the right flags. + // + DelegateMarshallingMethodThunk delegateThunk = _targetMethod as DelegateMarshallingMethodThunk; + + if (delegateThunk != null) { - flags = _importMetadata.Flags; + _flags = ((EcmaType)delegateThunk.DelegateType).GetDelegatePInvokeFlags(); } - else + else { - var delegateType = ((DelegateMarshallingMethodThunk)_targetMethod).DelegateType as EcmaType; - if (delegateType != null) - { - flags = delegateType.GetDelegatePInvokeFlags(); - } + Debug.Assert(_targetMethod.IsPInvoke); + _flags = _importMetadata.Flags; } - - _marshallers = InitializeMarshallers(targetMethod, interopStateManager, flags); + _marshallers = InitializeMarshallers(targetMethod, interopStateManager, _flags); } private static Marshaller[] InitializeMarshallers(MethodDesc targetMethod, InteropStateManager interopStateManager, PInvokeFlags flags) @@ -159,6 +162,13 @@ namespace Internal.IL.Stubs else if (delegateMethod.Kind == DelegateMarshallingMethodThunkKind .ForwardNativeFunctionWrapper) { + // if the SetLastError flag is set in UnmanagedFunctionPointerAttribute, clear the error code before doing P/Invoke + if (_flags.SetLastError) + { + callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken( + InteropTypes.GetPInvokeMarshal(context).GetKnownMethod("ClearLastWin32Error", null))); + } + // // For NativeFunctionWrapper we need to load the native function and call it // @@ -181,21 +191,93 @@ namespace Internal.IL.Stubs nativeParameterTypes[i - 1] = _marshallers[i].NativeParameterType; } - MethodSignatureFlags flags = MethodSignatureFlags.Static; - var delegateType = ((DelegateMarshallingMethodThunk)_targetMethod).DelegateType as EcmaType; - if (delegateType != null) + MethodSignature nativeSig = new MethodSignature( + MethodSignatureFlags.Static | _flags.UnmanagedCallingConvention, 0, nativeReturnType, nativeParameterTypes); + + callsiteSetupCodeStream.Emit(ILOpcode.calli, emitter.NewToken(nativeSig)); + + // if the SetLastError flag is set in UnmanagedFunctionPointerAttribute, call the PInvokeMarshal. + // SaveLastWin32Error so that last error can be used later by calling + // PInvokeMarshal.GetLastWin32Error + if (_flags.SetLastError) { - flags |= delegateType.GetDelegatePInvokeFlags().UnmanagedCallingConvention; + callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken( + InteropTypes.GetPInvokeMarshal(context) + .GetKnownMethod("SaveLastWin32Error", null))); } + } + else + { + Debug.Fail("Unexpected DelegateMarshallingMethodThunkKind"); + } + } + + private void EmitPInvokeCall(PInvokeILCodeStreams ilCodeStreams) + { + ILEmitter emitter = ilCodeStreams.Emitter; + ILCodeStream fnptrLoadStream = ilCodeStreams.FunctionPointerLoadStream; + ILCodeStream callsiteSetupCodeStream = ilCodeStreams.CallsiteSetupCodeStream; + TypeSystemContext context = _targetMethod.Context; + + TypeDesc nativeReturnType = _marshallers[0].NativeParameterType; + TypeDesc[] nativeParameterTypes = new TypeDesc[_marshallers.Length - 1]; + + // if the SetLastError flag is set in DllImport, clear the error code before doing P/Invoke + if (_flags.SetLastError) + { + callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken( + InteropTypes.GetPInvokeMarshal(context).GetKnownMethod("ClearLastWin32Error", null))); + } + + for (int i = 1; i < _marshallers.Length; i++) + { + nativeParameterTypes[i - 1] = _marshallers[i].NativeParameterType; + } + + if (MarshalHelpers.UseLazyResolution(_targetMethod, + _importMetadata.Module, + _pInvokeILEmitterConfiguration)) + { + MetadataType lazyHelperType = context.GetHelperType("InteropHelpers"); + FieldDesc lazyDispatchCell = _interopStateManager.GetPInvokeLazyFixupField(_targetMethod); + + fnptrLoadStream.Emit(ILOpcode.ldsflda, emitter.NewToken(lazyDispatchCell)); + fnptrLoadStream.Emit(ILOpcode.call, emitter.NewToken(lazyHelperType + .GetKnownMethod("ResolvePInvoke", null))); + + MethodSignatureFlags unmanagedCallConv = _flags.UnmanagedCallingConvention; MethodSignature nativeSig = new MethodSignature( - flags, 0, nativeReturnType, nativeParameterTypes); + _targetMethod.Signature.Flags | unmanagedCallConv, 0, nativeReturnType, + nativeParameterTypes); + ILLocalVariable vNativeFunctionPointer = emitter.NewLocal(context + .GetWellKnownType(WellKnownType.IntPtr)); + + fnptrLoadStream.EmitStLoc(vNativeFunctionPointer); + callsiteSetupCodeStream.EmitLdLoc(vNativeFunctionPointer); callsiteSetupCodeStream.Emit(ILOpcode.calli, emitter.NewToken(nativeSig)); } else { - Debug.Assert(false, "Unexpected DelegateMarshallingMethodThunkKind"); + // Eager call + MethodSignature nativeSig = new MethodSignature( + _targetMethod.Signature.Flags, 0, nativeReturnType, nativeParameterTypes); + + MethodDesc nativeMethod = + new PInvokeTargetNativeMethod(_targetMethod, nativeSig); + + callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken(nativeMethod)); + } + + // if the SetLastError flag is set in DllImport, call the PInvokeMarshal. + // SaveLastWin32Error so that last error can be used later by calling + // PInvokeMarshal.GetLastWin32Error + if (_flags.SetLastError) + { + callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken( + InteropTypes.GetPInvokeMarshal(context) + .GetKnownMethod("SaveLastWin32Error", null))); } } @@ -203,10 +285,7 @@ namespace Internal.IL.Stubs { PInvokeILCodeStreams pInvokeILCodeStreams = new PInvokeILCodeStreams(); ILEmitter emitter = pInvokeILCodeStreams.Emitter; - ILCodeStream fnptrLoadStream = pInvokeILCodeStreams.FunctionPointerLoadStream; - ILCodeStream callsiteSetupCodeStream = pInvokeILCodeStreams.CallsiteSetupCodeStream; ILCodeStream unmarshallingCodestream = pInvokeILCodeStreams.UnmarshallingCodestream; - TypeSystemContext context = _targetMethod.Context; // Marshal the arguments for (int i = 0; i < _marshallers.Length; i++) @@ -214,16 +293,7 @@ namespace Internal.IL.Stubs _marshallers[i].EmitMarshallingIL(pInvokeILCodeStreams); } - - // if the SetLastError flag is set in DllImport, clear the error code before doing P/Invoke - if (_importMetadata.Flags.SetLastError) - { - callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken( - InteropTypes.GetPInvokeMarshal(context).GetKnownMethod("ClearLastWin32Error", null))); - } - // make the call - DelegateMarshallingMethodThunk delegateMethod = _targetMethod as DelegateMarshallingMethodThunk; if (delegateMethod != null) { @@ -231,64 +301,12 @@ namespace Internal.IL.Stubs } else { - TypeDesc nativeReturnType = _marshallers[0].NativeParameterType; - TypeDesc[] nativeParameterTypes = new TypeDesc[_marshallers.Length - 1]; - - for (int i = 1; i < _marshallers.Length; i++) - { - nativeParameterTypes[i - 1] = _marshallers[i].NativeParameterType; - } - - if (MarshalHelpers.UseLazyResolution(_targetMethod, - _importMetadata.Module, - _pInvokeILEmitterConfiguration)) - { - MetadataType lazyHelperType = _targetMethod.Context.GetHelperType("InteropHelpers"); - FieldDesc lazyDispatchCell = new PInvokeLazyFixupField(_targetMethod); - - fnptrLoadStream.Emit(ILOpcode.ldsflda, emitter.NewToken(lazyDispatchCell)); - fnptrLoadStream.Emit(ILOpcode.call, emitter.NewToken(lazyHelperType - .GetKnownMethod("ResolvePInvoke", null))); - - MethodSignatureFlags unmanagedCallConv = _importMetadata.Flags.UnmanagedCallingConvention; - - MethodSignature nativeSig = new MethodSignature( - _targetMethod.Signature.Flags | unmanagedCallConv, 0, nativeReturnType, - nativeParameterTypes); - - ILLocalVariable vNativeFunctionPointer = emitter.NewLocal(_targetMethod.Context - .GetWellKnownType(WellKnownType.IntPtr)); - - fnptrLoadStream.EmitStLoc(vNativeFunctionPointer); - callsiteSetupCodeStream.EmitLdLoc(vNativeFunctionPointer); - callsiteSetupCodeStream.Emit(ILOpcode.calli, emitter.NewToken(nativeSig)); - } - else - { - // Eager call - MethodSignature nativeSig = new MethodSignature( - _targetMethod.Signature.Flags, 0, nativeReturnType, nativeParameterTypes); - - MethodDesc nativeMethod = - new PInvokeTargetNativeMethod(_targetMethod, nativeSig); - - callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken(nativeMethod)); - } - } - - // if the SetLastError flag is set in DllImport, call the PInvokeMarshal. - // SaveLastWin32Error so that last error can be used later by calling - // PInvokeMarshal.GetLastWin32Error - if (_importMetadata.Flags.SetLastError) - { - callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken( - InteropTypes.GetPInvokeMarshal(context) - .GetKnownMethod("SaveLastWin32Error", null))); + EmitPInvokeCall(pInvokeILCodeStreams); } unmarshallingCodestream.Emit(ILOpcode.ret); - return new PInvokeILStubMethodIL((ILStubMethodIL)emitter.Link(_targetMethod), + return new PInvokeILStubMethodIL((ILStubMethodIL)emitter.Link(_targetMethod, nonConformingStackWorkaround: true), IsStubRequired()); } @@ -328,7 +346,7 @@ namespace Internal.IL.Stubs { return true; } - if (_importMetadata.Flags.SetLastError) + if (_flags.SetLastError) { return true; } @@ -377,7 +395,7 @@ namespace Internal.IL.Stubs } } - public sealed class PInvokeILStubMethodIL : ILStubMethodIL + public sealed class PInvokeILStubMethodIL : ILStubMethodILWithNonConformingStack { public bool IsStubRequired { get; } public PInvokeILStubMethodIL(ILStubMethodIL methodIL, bool isStubRequired) : base(methodIL) diff --git a/external/corert/src/Common/src/TypeSystem/IL/Stubs/PInvokeLazyFixupField.cs b/external/corert/src/Common/src/TypeSystem/IL/Stubs/PInvokeLazyFixupField.cs index 72eedf1827..650b244c13 100644 --- a/external/corert/src/Common/src/TypeSystem/IL/Stubs/PInvokeLazyFixupField.cs +++ b/external/corert/src/Common/src/TypeSystem/IL/Stubs/PInvokeLazyFixupField.cs @@ -15,14 +15,24 @@ namespace Internal.IL.Stubs /// public sealed partial class PInvokeLazyFixupField : FieldDesc { - private MethodDesc _targetMethod; + private readonly DefType _owningType; + private readonly MethodDesc _targetMethod; - public PInvokeLazyFixupField(MethodDesc targetMethod) + public PInvokeLazyFixupField(DefType owningType, MethodDesc targetMethod) { Debug.Assert(targetMethod.IsPInvoke); + _owningType = owningType; _targetMethod = targetMethod; } + public MethodDesc TargetMethod + { + get + { + return _targetMethod; + } + } + public PInvokeMetadata PInvokeMetadata { get @@ -91,7 +101,7 @@ namespace Internal.IL.Stubs { get { - return (DefType)_targetMethod.OwningType; + return _owningType; } } diff --git a/external/corert/src/Common/src/TypeSystem/IL/Stubs/PInvokeTargetNativeMethod.cs b/external/corert/src/Common/src/TypeSystem/IL/Stubs/PInvokeTargetNativeMethod.cs index 6602299921..2a2cae4e82 100644 --- a/external/corert/src/Common/src/TypeSystem/IL/Stubs/PInvokeTargetNativeMethod.cs +++ b/external/corert/src/Common/src/TypeSystem/IL/Stubs/PInvokeTargetNativeMethod.cs @@ -76,10 +76,5 @@ namespace Internal.IL.Stubs { return _declMethod.GetPInvokeMethodMetadata(); } - - public override string ToString() - { - return "[EXTERNAL]" + Name; - } } } diff --git a/external/corert/src/Common/src/TypeSystem/IL/Stubs/StructMarshallingThunk.cs b/external/corert/src/Common/src/TypeSystem/IL/Stubs/StructMarshallingThunk.cs index cd52a17437..6625e3b9a0 100644 --- a/external/corert/src/Common/src/TypeSystem/IL/Stubs/StructMarshallingThunk.cs +++ b/external/corert/src/Common/src/TypeSystem/IL/Stubs/StructMarshallingThunk.cs @@ -105,7 +105,7 @@ namespace Internal.IL.Stubs case StructMarshallingThunkType.Cleanup: return "Cleanup"; default: - System.Diagnostics.Debug.Assert(false, "Unexpected Struct marshalling thunk type"); + System.Diagnostics.Debug.Fail("Unexpected Struct marshalling thunk type"); return string.Empty; } } @@ -119,38 +119,6 @@ namespace Internal.IL.Stubs } } - public IEnumerable GetInlineArrayCandidates() - { - int index = 0; - MarshalAsDescriptor[] marshalAsDescriptors = ((MetadataType)ManagedType).GetFieldMarshalAsDescriptors(); - foreach (FieldDesc field in ManagedType.GetFields()) - { - if (field.IsStatic) - { - continue; - } - - Marshaller marshaller = _marshallers[index]; - - if (marshaller.MarshallerKind == MarshallerKind.ByValAnsiString - || marshaller.MarshallerKind == MarshallerKind.ByValUnicodeString) - { - yield return MarshalHelpers.GetInlineArrayCandidate(marshaller.ManagedType.Context.GetWellKnownType(WellKnownType.Char), marshaller.ElementMarshallerKind, _interopStateManager, marshalAsDescriptors[index]); - } - else if (marshaller.MarshallerKind == MarshallerKind.ByValArray - || marshaller.MarshallerKind == MarshallerKind.ByValAnsiCharArray) - { - var arrayType = marshaller.ManagedType as ArrayType; - - Debug.Assert(arrayType != null); - - yield return MarshalHelpers.GetInlineArrayCandidate(arrayType.ElementType, marshaller.ElementMarshallerKind, _interopStateManager, marshalAsDescriptors[index]); - } - - index++; - } - } - private Marshaller[] InitializeMarshallers() { Debug.Assert(_interopStateManager != null); diff --git a/external/corert/src/Common/src/TypeSystem/IL/Stubs/TypeSystemThrowingILEmitter.cs b/external/corert/src/Common/src/TypeSystem/IL/Stubs/TypeSystemThrowingILEmitter.cs index 0f57bcb73c..90d5937398 100644 --- a/external/corert/src/Common/src/TypeSystem/IL/Stubs/TypeSystemThrowingILEmitter.cs +++ b/external/corert/src/Common/src/TypeSystem/IL/Stubs/TypeSystemThrowingILEmitter.cs @@ -51,7 +51,17 @@ namespace Internal.IL.Stubs } else if (exceptionType == typeof(TypeSystemException.InvalidProgramException)) { + // + // There are two ThrowInvalidProgramException helpers. Find the one which matches the number of + // arguments "exception" was initialized with. + // + helper = context.GetHelperEntryPoint("ThrowHelpers", "ThrowInvalidProgramException"); + + if (helper.Signature.Length != exception.Arguments.Count + 1) + { + helper = context.GetHelperEntryPoint("ThrowHelpers", "ThrowInvalidProgramExceptionWithArgument"); + } } else if (exceptionType == typeof(TypeSystemException.BadImageFormatException)) { diff --git a/external/corert/src/Common/src/TypeSystem/IL/Stubs/UnsafeIntrinsics.cs b/external/corert/src/Common/src/TypeSystem/IL/Stubs/UnsafeIntrinsics.cs index 6a42f7bffd..86ce8c7f92 100644 --- a/external/corert/src/Common/src/TypeSystem/IL/Stubs/UnsafeIntrinsics.cs +++ b/external/corert/src/Common/src/TypeSystem/IL/Stubs/UnsafeIntrinsics.cs @@ -26,7 +26,10 @@ namespace Internal.IL.Stubs case "SizeOf": return EmitSizeOf(method); case "As": + case "AsRef": return new ILStubMethodIL(method, new byte[] { (byte)ILOpcode.ldarg_0, (byte)ILOpcode.ret }, Array.Empty(), null); + case "Add": + return EmitAdd(method); case "AddByteOffset": return new ILStubMethodIL(method, new byte[] { (byte)ILOpcode.ldarg_0, (byte)ILOpcode.ldarg_1, (byte)ILOpcode.add, (byte)ILOpcode.ret }, Array.Empty(), null); case "InitBlockUnaligned": @@ -35,6 +38,26 @@ namespace Internal.IL.Stubs (byte)ILOpcode.prefix1, unchecked((byte)ILOpcode.unaligned), 0x01, (byte)ILOpcode.prefix1, unchecked((byte)ILOpcode.initblk), (byte)ILOpcode.ret }, Array.Empty(), null); + case "Read": + return EmitReadWrite(method, write: false); + case "Write": + return EmitReadWrite(method, write: true); + case "ReadUnaligned": + return EmitReadWrite(method, write: false, unaligned: true); + case "WriteUnaligned": + return EmitReadWrite(method, write: true, unaligned: true); + case "AreSame": + return new ILStubMethodIL(method, new byte[] + { + (byte)ILOpcode.ldarg_0, (byte)ILOpcode.ldarg_1, + (byte)ILOpcode.prefix1, unchecked((byte)ILOpcode.ceq), + (byte)ILOpcode.ret }, Array.Empty(), null); + case "ByteOffset": + return new ILStubMethodIL(method, new byte[] + { + (byte)ILOpcode.ldarg_1, (byte)ILOpcode.ldarg_0, + (byte)ILOpcode.sub, + (byte)ILOpcode.ret }, Array.Empty(), null); } return null; @@ -52,5 +75,41 @@ namespace Internal.IL.Stubs codeStream.Emit(ILOpcode.ret); return emit.Link(method); } + + private static MethodIL EmitAdd(MethodDesc method) + { + Debug.Assert(method.Signature.IsStatic && method.Signature.Length == 2); + + TypeSystemContext context = method.Context; + + ILEmitter emit = new ILEmitter(); + ILCodeStream codeStream = emit.NewCodeStream(); + codeStream.Emit(ILOpcode.ldarg_1); + codeStream.Emit(ILOpcode.sizeof_, emit.NewToken(context.GetSignatureVariable(0, method: true))); + codeStream.Emit(ILOpcode.conv_i); + codeStream.Emit(ILOpcode.mul); + codeStream.Emit(ILOpcode.ldarg_0); + codeStream.Emit(ILOpcode.add); + codeStream.Emit(ILOpcode.ret); + return emit.Link(method); + } + + private static MethodIL EmitReadWrite(MethodDesc method, bool write, bool unaligned = false) + { + Debug.Assert(method.Signature.IsStatic && method.Signature.Length == (write ? 2 : 1)); + + TypeSystemContext context = method.Context; + + ILEmitter emit = new ILEmitter(); + ILCodeStream codeStream = emit.NewCodeStream(); + + codeStream.EmitLdArg(0); + if (write) codeStream.EmitLdArg(1); + if (unaligned) codeStream.EmitUnaligned(); + codeStream.Emit(write ? ILOpcode.stobj : ILOpcode.ldobj, + emit.NewToken(context.GetSignatureVariable(0, method: true))); + codeStream.Emit(ILOpcode.ret); + return emit.Link(method); + } } } diff --git a/external/corert/src/Common/src/TypeSystem/IL/TypeSystemContext.EnumMethods.cs b/external/corert/src/Common/src/TypeSystem/IL/TypeSystemContext.EnumMethods.cs index 26d89ad252..5736c50c31 100644 --- a/external/corert/src/Common/src/TypeSystem/IL/TypeSystemContext.EnumMethods.cs +++ b/external/corert/src/Common/src/TypeSystem/IL/TypeSystemContext.EnumMethods.cs @@ -69,7 +69,7 @@ namespace Internal.TypeSystem return resolvedMethod; } - protected IEnumerable GetAllMethodsForEnum(TypeDesc enumType) + protected virtual IEnumerable GetAllMethodsForEnum(TypeDesc enumType) { TypeDesc enumTypeDefinition = enumType.GetTypeDefinition(); EnumInfo info = _enumInfoHashtable.GetOrCreateValue(enumTypeDefinition); diff --git a/external/corert/src/Common/src/TypeSystem/Interop/IL/InlineArrayType.cs b/external/corert/src/Common/src/TypeSystem/Interop/IL/InlineArrayType.cs index eca69969a7..d7412081f2 100644 --- a/external/corert/src/Common/src/TypeSystem/Interop/IL/InlineArrayType.cs +++ b/external/corert/src/Common/src/TypeSystem/Interop/IL/InlineArrayType.cs @@ -219,6 +219,7 @@ namespace Internal.TypeSystem.Interop } flags |= TypeFlags.HasFinalizerComputed; + flags |= TypeFlags.IsByRefLikeComputed; return flags; } @@ -385,4 +386,4 @@ namespace Internal.TypeSystem.Interop Setter = 1 } -} \ No newline at end of file +} diff --git a/external/corert/src/Common/src/TypeSystem/Interop/IL/MarshalHelpers.cs b/external/corert/src/Common/src/TypeSystem/Interop/IL/MarshalHelpers.cs index 08458b8998..78fc4d24ae 100644 --- a/external/corert/src/Common/src/TypeSystem/Interop/IL/MarshalHelpers.cs +++ b/external/corert/src/Common/src/TypeSystem/Interop/IL/MarshalHelpers.cs @@ -96,15 +96,6 @@ namespace Internal.TypeSystem.Interop if (forceLazyResolution.HasValue) return forceLazyResolution.Value; - // In multi-module library mode, the WinRT p/invokes in System.Private.Interop cause linker failures - // since we don't link against the OS libraries containing those APIs. Force them to be lazy. - // See https://github.com/dotnet/corert/issues/2601 - string assemblySimpleName = ((IAssemblyDesc)((MetadataType)method.OwningType).Module).GetName().Name; - if (assemblySimpleName == "System.Private.Interop") - { - return true; - } - // Determine whether this call should be made through a lazy resolution or a static reference // Eventually, this should be controlled by a custom attribute (or an extension to the metadata format). if (importModule == "[MRT]" || importModule == "*") @@ -305,7 +296,7 @@ namespace Internal.TypeSystem.Interop var elementNativeType = nativeType as MetadataType; if (elementNativeType == null) { - Debug.Assert(nativeType is PointerType); + Debug.Assert(nativeType.IsPointer || nativeType.IsFunctionPointer); // If it is a pointer type we will create InlineArray for IntPtr elementNativeType = (MetadataType)managedElementType.Context.GetWellKnownType(WellKnownType.IntPtr); @@ -456,18 +447,13 @@ namespace Internal.TypeSystem.Interop else return MarshallerKind.Invalid; } - /* - TODO: Bring HandleRef to CoreLib - https://github.com/dotnet/corert/issues/2570 - - else if (context.IsHandleRef(type)) - { - if (nativeType == NativeType.Invalid) - return MarshallerKind.HandleRef; - else - return MarshallerKind.Invalid; - } - */ + else if (InteropTypes.IsHandleRef(context, type)) + { + if (nativeType == NativeTypeKind.Invalid) + return MarshallerKind.HandleRef; + else + return MarshallerKind.Invalid; + } switch (nativeType) { @@ -512,181 +498,158 @@ namespace Internal.TypeSystem.Interop return MarshallerKind.Struct; } } - else // !ValueType + else if (type.IsSzArray) { - if (type.Category == TypeFlags.Class) + if (nativeType == NativeTypeKind.Invalid) + nativeType = NativeTypeKind.Array; + + switch (nativeType) { - if (type.IsString) - { - switch (nativeType) + case NativeTypeKind.Array: { - case NativeTypeKind.LPWStr: - return MarshallerKind.UnicodeString; - - case NativeTypeKind.LPStr: - return MarshallerKind.AnsiString; - - case NativeTypeKind.LPTStr: - return MarshallerKind.UnicodeString; - - case NativeTypeKind.ByValTStr: - if (isAnsi) - { - elementMarshallerKind = MarshallerKind.AnsiChar; - return MarshallerKind.ByValAnsiString; - } - else - { - elementMarshallerKind = MarshallerKind.UnicodeChar; - return MarshallerKind.ByValUnicodeString; - } - - case NativeTypeKind.Invalid: - if (isAnsi) - return MarshallerKind.AnsiString; - else - return MarshallerKind.UnicodeString; - - default: + if (isField || isReturn) return MarshallerKind.Invalid; + + var arrayType = (ArrayType)type; + + elementMarshallerKind = GetArrayElementMarshallerKind( + arrayType, + marshalAs, + isAnsi); + + // If element is invalid type, the array itself is invalid + if (elementMarshallerKind == MarshallerKind.Invalid) + return MarshallerKind.Invalid; + + if (elementMarshallerKind == MarshallerKind.AnsiChar) + return MarshallerKind.AnsiCharArray; + else if (elementMarshallerKind == MarshallerKind.UnicodeChar // Arrays of unicode char should be marshalled as blittable arrays + || elementMarshallerKind == MarshallerKind.Enum + || elementMarshallerKind == MarshallerKind.BlittableValue) + return MarshallerKind.BlittableArray; + else + return MarshallerKind.Array; } - } - else if (type.IsDelegate) - { - if (nativeType == NativeTypeKind.Invalid || nativeType == NativeTypeKind.Func) - return MarshallerKind.FunctionPointer; - else - return MarshallerKind.Invalid; - } - else if (type.IsObject) - { - if (nativeType == NativeTypeKind.Invalid) - return MarshallerKind.Variant; - else - return MarshallerKind.Invalid; - } - else if (InteropTypes.IsStringBuilder(context, type)) - { - switch (nativeType) + + case NativeTypeKind.ByValArray: // fix sized array { - case NativeTypeKind.Invalid: - if (isAnsi) - { - return MarshallerKind.AnsiStringBuilder; - } - else - { - return MarshallerKind.UnicodeStringBuilder; - } + var arrayType = (ArrayType)type; + elementMarshallerKind = GetArrayElementMarshallerKind( + arrayType, + marshalAs, + isAnsi); - case NativeTypeKind.LPStr: - return MarshallerKind.AnsiStringBuilder; - - case NativeTypeKind.LPWStr: - return MarshallerKind.UnicodeStringBuilder; - default: + // If element is invalid type, the array itself is invalid + if (elementMarshallerKind == MarshallerKind.Invalid) return MarshallerKind.Invalid; + + if (elementMarshallerKind == MarshallerKind.AnsiChar) + return MarshallerKind.ByValAnsiCharArray; + else + return MarshallerKind.ByValArray; } - } - else if (InteropTypes.IsSafeHandle(context, type)) - { - if (nativeType == NativeTypeKind.Invalid) - return MarshallerKind.SafeHandle; - else - return MarshallerKind.Invalid; - } - /* - TODO: Bring CriticalHandle to CoreLib - https://github.com/dotnet/corert/issues/2570 - else if (InteropTypes.IsCriticalHandle(context, type)) - { - if (nativeType != NativeType.Invalid || isField) - { - return MarshallerKind.Invalid; - } - else - { - return MarshallerKind.CriticalHandle; - } - } - */ - return MarshallerKind.Invalid; - } - else if (InteropTypes.IsSystemArray(context, type)) - { - return MarshallerKind.Invalid; - } - else if (type.IsSzArray) - { - if (nativeType == NativeTypeKind.Invalid) - nativeType = NativeTypeKind.Array; - - switch (nativeType) - { - case NativeTypeKind.Array: - { - if (isField || isReturn) - return MarshallerKind.Invalid; - - var arrayType = (ArrayType)type; - - elementMarshallerKind = GetArrayElementMarshallerKind( - arrayType, - marshalAs, - isAnsi); - - // If element is invalid type, the array itself is invalid - if (elementMarshallerKind == MarshallerKind.Invalid) - return MarshallerKind.Invalid; - - if (elementMarshallerKind == MarshallerKind.AnsiChar) - return MarshallerKind.AnsiCharArray; - else if (elementMarshallerKind == MarshallerKind.UnicodeChar // Arrays of unicode char should be marshalled as blittable arrays - || elementMarshallerKind == MarshallerKind.Enum - || elementMarshallerKind == MarshallerKind.BlittableValue) - return MarshallerKind.BlittableArray; - else - return MarshallerKind.Array; - } - - case NativeTypeKind.ByValArray: // fix sized array - { - var arrayType = (ArrayType)type; - elementMarshallerKind = GetArrayElementMarshallerKind( - arrayType, - marshalAs, - isAnsi); - - // If element is invalid type, the array itself is invalid - if (elementMarshallerKind == MarshallerKind.Invalid) - return MarshallerKind.Invalid; - - if (elementMarshallerKind == MarshallerKind.AnsiChar) - return MarshallerKind.ByValAnsiCharArray; - else - return MarshallerKind.ByValArray; - } - - default: - return MarshallerKind.Invalid; - } - } - else if (type.Category == TypeFlags.Pointer) - { - // - // @TODO - add checks for the pointee type in case the pointee type is not blittable - // C# already does this and will emit compilation errors (can't declare pointers to - // managed type). - // - if (nativeType == NativeTypeKind.Invalid) - return MarshallerKind.BlittableValue; - else + default: return MarshallerKind.Invalid; } } + else if (type.IsPointer || type.IsFunctionPointer) + { + if (nativeType == NativeTypeKind.Invalid) + return MarshallerKind.BlittableValue; + else + return MarshallerKind.Invalid; + } + else if (type.IsDelegate) + { + if (nativeType == NativeTypeKind.Invalid || nativeType == NativeTypeKind.Func) + return MarshallerKind.FunctionPointer; + else + return MarshallerKind.Invalid; + } + else if (type.IsString) + { + switch (nativeType) + { + case NativeTypeKind.LPWStr: + return MarshallerKind.UnicodeString; - return MarshallerKind.Invalid; + case NativeTypeKind.LPStr: + return MarshallerKind.AnsiString; + + case NativeTypeKind.LPTStr: + return MarshallerKind.UnicodeString; + + case NativeTypeKind.ByValTStr: + if (isAnsi) + { + elementMarshallerKind = MarshallerKind.AnsiChar; + return MarshallerKind.ByValAnsiString; + } + else + { + elementMarshallerKind = MarshallerKind.UnicodeChar; + return MarshallerKind.ByValUnicodeString; + } + + case NativeTypeKind.Invalid: + if (isAnsi) + return MarshallerKind.AnsiString; + else + return MarshallerKind.UnicodeString; + + default: + return MarshallerKind.Invalid; + } + } + // else if (type.IsObject) + // { + // if (nativeType == NativeTypeKind.Invalid) + // return MarshallerKind.Variant; + // else + // return MarshallerKind.Invalid; + // } + else if (InteropTypes.IsStringBuilder(context, type)) + { + switch (nativeType) + { + case NativeTypeKind.Invalid: + if (isAnsi) + { + return MarshallerKind.AnsiStringBuilder; + } + else + { + return MarshallerKind.UnicodeStringBuilder; + } + + case NativeTypeKind.LPStr: + return MarshallerKind.AnsiStringBuilder; + + case NativeTypeKind.LPWStr: + return MarshallerKind.UnicodeStringBuilder; + default: + return MarshallerKind.Invalid; + } + } + else if (InteropTypes.IsSafeHandle(context, type)) + { + if (nativeType == NativeTypeKind.Invalid) + return MarshallerKind.SafeHandle; + else + return MarshallerKind.Invalid; + } + else if (InteropTypes.IsCriticalHandle(context, type)) + { + if (nativeType == NativeTypeKind.Invalid) + return MarshallerKind.CriticalHandle; + else + return MarshallerKind.Invalid; + } + else + { + return MarshallerKind.Invalid; + } } private static MarshallerKind GetArrayElementMarshallerKind( @@ -802,15 +765,13 @@ namespace Internal.TypeSystem.Interop return MarshallerKind.Invalid; } } - /* - TODO: Bring HandleRef to CoreLib - https://github.com/dotnet/corert/issues/2570 - - else if (InteropTypes.IsHandleRef(context, elementType)) - { - return MarshallerKind.HandleRef; - } - */ + else if (InteropTypes.IsHandleRef(context, elementType)) + { + if (nativeType == NativeTypeKind.Invalid) + return MarshallerKind.HandleRef; + else + return MarshallerKind.Invalid; + } else { @@ -833,60 +794,41 @@ namespace Internal.TypeSystem.Interop } } } - else // !valueType + else if (elementType.IsPointer || elementType.IsFunctionPointer) { - if (elementType.IsString) + if (nativeType == NativeTypeKind.Invalid) + return MarshallerKind.BlittableValue; + else + return MarshallerKind.Invalid; + } + else if (elementType.IsString) + { + switch (nativeType) { - switch (nativeType) - { - case NativeTypeKind.Invalid: - if (isAnsi) - return MarshallerKind.AnsiString; - else - return MarshallerKind.UnicodeString; - case NativeTypeKind.LPStr: + case NativeTypeKind.Invalid: + if (isAnsi) return MarshallerKind.AnsiString; - case NativeTypeKind.LPWStr: + else return MarshallerKind.UnicodeString; - default: - return MarshallerKind.Invalid; - } - } - - if (elementType.IsObject) - { - if (nativeType == NativeTypeKind.Invalid) - return MarshallerKind.Variant; - else + case NativeTypeKind.LPStr: + return MarshallerKind.AnsiString; + case NativeTypeKind.LPWStr: + return MarshallerKind.UnicodeString; + default: return MarshallerKind.Invalid; } - - if (elementType.IsSzArray) - { - return MarshallerKind.Invalid; - } - - if (elementType.IsPointer) - { - return MarshallerKind.Invalid; - } - - if (InteropTypes.IsSafeHandle(context, elementType)) - { - return MarshallerKind.Invalid; - } - /* - TODO: Bring CriticalHandle to CoreLib - https://github.com/dotnet/corert/issues/2570 - - if (pInvokeData.IsCriticalHandle(elementType)) - { - return MarshallerKind.Invalid; - } - */ } - - return MarshallerKind.Invalid; + // else if (elementType.IsObject) + // { + // if (nativeType == NativeTypeKind.Invalid) + // return MarshallerKind.Variant; + // else + // return MarshallerKind.Invalid; + // } + else + { + return MarshallerKind.Invalid; + } } //TODO: https://github.com/dotnet/corert/issues/2675 @@ -907,8 +849,8 @@ namespace Internal.TypeSystem.Interop codeStream.Emit(ILOpcode.throw_); codeStream.Emit(ILOpcode.ret); - return new PInvokeILStubMethodIL((ILStubMethodIL)emitter.Link(method), true); + return new PInvokeILStubMethodIL((ILStubMethodIL)emitter.Link(method, nonConformingStackWorkaround: true), true); } } -} \ No newline at end of file +} diff --git a/external/corert/src/Common/src/TypeSystem/Interop/IL/Marshaller.cs b/external/corert/src/Common/src/TypeSystem/Interop/IL/Marshaller.cs index 40b5f47283..a8f5078a2a 100644 --- a/external/corert/src/Common/src/TypeSystem/Interop/IL/Marshaller.cs +++ b/external/corert/src/Common/src/TypeSystem/Interop/IL/Marshaller.cs @@ -225,7 +225,7 @@ namespace Internal.TypeSystem.Interop switch (_homeType) { case HomeType.Arg: - Debug.Assert(false, "Unexpectting setting value on non-byref arg"); + Debug.Fail("Unexpectting setting value on non-byref arg"); break; case HomeType.Local: stream.EmitStLoc(_var); @@ -301,20 +301,21 @@ namespace Internal.TypeSystem.Interop marshaller.PInvokeFlags = flags; // - // Desktop ignores [Out] on marshaling scenarios where they don't make sense (such as passing - // value types and string as [out] without byref). + // Desktop ignores [Out] on marshaling scenarios where they don't make sense // - if (marshaller.IsManagedByRef) + if (isOut) { - // Passing as [Out] by ref is valid - marshaller.Out = isOut; - } - else - { - // Passing as [Out] is valid only if it is not ValueType nor string - if (!parameterType.IsValueType && !parameterType.IsString) - marshaller.Out = isOut; + // Passing as [Out] by ref is always valid. + if (!marshaller.IsManagedByRef) + { + // Ignore [Out] for ValueType, string and pointers + if (parameterType.IsValueType || parameterType.IsString || parameterType.IsPointer || parameterType.IsFunctionPointer) + { + isOut = false; + } + } } + marshaller.Out = isOut; if (!marshaller.In && !marshaller.Out) { @@ -359,6 +360,8 @@ namespace Internal.TypeSystem.Interop case MarshallerKind.BlittableStruct: case MarshallerKind.UnicodeChar: return new BlittableValueMarshaller(); + case MarshallerKind.BlittableStructPtr: + return new BlittableStructPtrMarshaller(); case MarshallerKind.AnsiChar: return new AnsiCharMarshaller(); case MarshallerKind.Array: @@ -938,6 +941,29 @@ namespace Internal.TypeSystem.Interop } } + class BlittableStructPtrMarshaller : Marshaller + { + protected override void TransformManagedToNative(ILCodeStream codeStream) + { + if (Out) + { + // TODO: https://github.com/dotnet/corert/issues/4466 + throw new NotSupportedException("Marshalling an LPStruct argument not yet implemented"); + } + else + { + LoadManagedAddr(codeStream); + StoreNativeValue(codeStream); + } + } + + protected override void TransformNativeToManaged(ILCodeStream codeStream) + { + // TODO: https://github.com/dotnet/corert/issues/4466 + throw new NotSupportedException("Marshalling an LPStruct argument not yet implemented"); + } + } + class ArrayMarshaller : Marshaller { private Marshaller _elementMarshaller; @@ -1610,6 +1636,28 @@ namespace Internal.TypeSystem.Interop class SafeHandleMarshaller : ReferenceMarshaller { + protected override void AllocNativeToManaged(ILCodeStream codeStream) + { + var ctor = ManagedType.GetParameterlessConstructor(); + if (ctor == null) + { + var emitter = _ilCodeStreams.Emitter; + + MethodSignature ctorSignature = new MethodSignature(0, 0, Context.GetWellKnownType(WellKnownType.Void), + new TypeDesc[] { Context.GetWellKnownType(WellKnownType.String) }); + MethodDesc exceptionCtor = InteropTypes.GetMissingMemberException(Context).GetKnownMethod(".ctor", ctorSignature); + + string name = ((MetadataType)ManagedType).Name; + codeStream.Emit(ILOpcode.ldstr, emitter.NewToken(String.Format("'{0}' does not have a default constructor. Subclasses of SafeHandle must have a default constructor to support marshaling a Windows HANDLE into managed code.", name))); + codeStream.Emit(ILOpcode.newobj, emitter.NewToken(exceptionCtor)); + codeStream.Emit(ILOpcode.throw_); + } + else + { + base.AllocNativeToManaged(codeStream); + } + } + protected override void EmitMarshalArgumentManagedToNative() { ILEmitter emitter = _ilCodeStreams.Emitter; @@ -1631,7 +1679,7 @@ namespace Internal.TypeSystem.Interop throw new NotSupportedException("Marshalling an argument as both in and out not yet implemented"); } - var safeHandleType = InteropTypes.GetSafeHandleType(Context); + var safeHandleType = InteropTypes.GetSafeHandle(Context); if (Out && IsManagedByRef) { @@ -1686,7 +1734,7 @@ namespace Internal.TypeSystem.Interop LoadManagedValue(codeStream); LoadNativeValue(codeStream); codeStream.Emit(ILOpcode.call, _ilCodeStreams.Emitter.NewToken( - InteropTypes.GetSafeHandleType(Context).GetKnownMethod("SetHandle", null))); + InteropTypes.GetSafeHandle(Context).GetKnownMethod("SetHandle", null))); } } @@ -1880,11 +1928,18 @@ namespace Internal.TypeSystem.Interop codeStream.EmitLdArg(0); codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(_managedField)); + var lNullCheck = emitter.NewCodeLabel(); + codeStream.Emit(ILOpcode.brfalse, lNullCheck); + + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(_managedField)); + codeStream.Emit(ILOpcode.ldlen); codeStream.Emit(ILOpcode.conv_i4); codeStream.EmitStLoc(vLength); - + codeStream.EmitLabel(lNullCheck); + Debug.Assert(MarshalAsDescriptor.SizeConst.HasValue); int sizeConst = (int)MarshalAsDescriptor.SizeConst.Value; @@ -1947,8 +2002,6 @@ namespace Internal.TypeSystem.Interop // check if ManagedType == null, then return codeStream.EmitLdArg(0); codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(_managedField)); - codeStream.Emit(ILOpcode.ldnull); - codeStream.Emit(ILOpcode.cgt_un); codeStream.Emit(ILOpcode.brfalse, lDone); codeStream.EmitLdArg(1); diff --git a/external/corert/src/Common/src/TypeSystem/Interop/IL/NativeStructType.cs b/external/corert/src/Common/src/TypeSystem/Interop/IL/NativeStructType.cs index c23c43555a..87fb167a68 100644 --- a/external/corert/src/Common/src/TypeSystem/Interop/IL/NativeStructType.cs +++ b/external/corert/src/Common/src/TypeSystem/Interop/IL/NativeStructType.cs @@ -127,7 +127,23 @@ namespace Internal.TypeSystem.Interop private NativeStructField[] _fields; private InteropStateManager _interopStateManager; + private bool _hasInvalidLayout; + public bool HasInvalidLayout + { + get + { + return _hasInvalidLayout; + } + } + + public FieldDesc[] Fields + { + get + { + return _fields; + } + } public NativeStructType(ModuleDesc owningModule, MetadataType managedStructType, InteropStateManager interopStateManager) { @@ -138,6 +154,7 @@ namespace Internal.TypeSystem.Interop Module = owningModule; ManagedStructType = managedStructType; _interopStateManager = interopStateManager; + _hasInvalidLayout = false; CalculateFields(); } @@ -179,6 +196,7 @@ namespace Internal.TypeSystem.Interop // if marshalling is not supported for this type the generated stubs will emit appropriate // error message. We just set native type to be same as managedtype nativeType = managedType; + _hasInvalidLayout = true; } _fields[index++] = new NativeStructField(nativeType, this, field); @@ -283,6 +301,7 @@ namespace Internal.TypeSystem.Interop } flags |= TypeFlags.HasFinalizerComputed; + flags |= TypeFlags.IsByRefLikeComputed; return flags; } @@ -388,4 +407,4 @@ namespace Internal.TypeSystem.Interop } } -} \ No newline at end of file +} diff --git a/external/corert/src/Common/src/TypeSystem/Interop/IL/PInvokeDelegateWrapper.cs b/external/corert/src/Common/src/TypeSystem/Interop/IL/PInvokeDelegateWrapper.cs index 3931f3aefe..3ff100bf47 100644 --- a/external/corert/src/Common/src/TypeSystem/Interop/IL/PInvokeDelegateWrapper.cs +++ b/external/corert/src/Common/src/TypeSystem/Interop/IL/PInvokeDelegateWrapper.cs @@ -204,6 +204,7 @@ namespace Internal.TypeSystem.Interop } flags |= TypeFlags.HasFinalizerComputed; + flags |= TypeFlags.IsByRefLikeComputed; return flags; } @@ -249,4 +250,4 @@ namespace Internal.TypeSystem.Interop Invoke = 1 } -} \ No newline at end of file +} diff --git a/external/corert/src/Common/src/TypeSystem/Interop/InteropStateManager.cs b/external/corert/src/Common/src/TypeSystem/Interop/InteropStateManager.cs index fac3f5f9df..aeed8f19a9 100644 --- a/external/corert/src/Common/src/TypeSystem/Interop/InteropStateManager.cs +++ b/external/corert/src/Common/src/TypeSystem/Interop/InteropStateManager.cs @@ -21,6 +21,7 @@ namespace Internal.TypeSystem private readonly ForwardDelegateCreationStubHashtable _forwardDelegateCreationStubHashtable; private readonly PInvokeDelegateWrapperHashtable _pInvokeDelegateWrapperHashtable; private readonly InlineArrayHashTable _inlineArrayHashtable; + private readonly PInvokeLazyFixupFieldHashtable _pInvokeLazyFixupFieldHashtable; public InteropStateManager(ModuleDesc generatedAssembly) { @@ -31,6 +32,7 @@ namespace Internal.TypeSystem _forwardDelegateCreationStubHashtable = new ForwardDelegateCreationStubHashtable(this, _generatedAssembly.GetGlobalModuleType()); _pInvokeDelegateWrapperHashtable = new PInvokeDelegateWrapperHashtable(this, _generatedAssembly); _inlineArrayHashtable = new InlineArrayHashTable(this, _generatedAssembly); + _pInvokeLazyFixupFieldHashtable = new PInvokeLazyFixupFieldHashtable(_generatedAssembly.GetGlobalModuleType()); } // // Delegate Marshalling Stubs @@ -180,6 +182,10 @@ namespace Internal.TypeSystem return _inlineArrayHashtable.GetOrCreateValue(candidate); } + public FieldDesc GetPInvokeLazyFixupField(MethodDesc method) + { + return _pInvokeLazyFixupFieldHashtable.GetOrCreateValue(method); + } private class NativeStructTypeHashtable : LockFreeReaderHashtable { @@ -433,6 +439,39 @@ namespace Internal.TypeSystem } } + private class PInvokeLazyFixupFieldHashtable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(MethodDesc key) + { + return key.GetHashCode(); + } + protected override int GetValueHashCode(PInvokeLazyFixupField value) + { + return value.TargetMethod.GetHashCode(); + } + + protected override bool CompareKeyToValue(MethodDesc key, PInvokeLazyFixupField value) + { + return key == value.TargetMethod; + } + + protected override bool CompareValueToValue(PInvokeLazyFixupField value1, PInvokeLazyFixupField value2) + { + return value1.TargetMethod == value2.TargetMethod; + } + + protected override PInvokeLazyFixupField CreateValueFromKey(MethodDesc key) + { + return new PInvokeLazyFixupField(_owningType, key); + } + + private readonly DefType _owningType; + + public PInvokeLazyFixupFieldHashtable(DefType owningType) + { + _owningType = owningType; + } + } } -} \ No newline at end of file +} diff --git a/external/corert/src/Common/src/TypeSystem/Interop/InteropTypes.cs b/external/corert/src/Common/src/TypeSystem/Interop/InteropTypes.cs index 14f0d01a85..23bff20226 100644 --- a/external/corert/src/Common/src/TypeSystem/Interop/InteropTypes.cs +++ b/external/corert/src/Common/src/TypeSystem/Interop/InteropTypes.cs @@ -15,112 +15,72 @@ namespace Internal.TypeSystem.Interop return context.SystemModule.GetKnownType("System", "GC"); } - public static MetadataType GetSafeHandleType(TypeSystemContext context) + public static MetadataType GetSafeHandle(TypeSystemContext context) { return context.SystemModule.GetKnownType("System.Runtime.InteropServices", "SafeHandle"); } + public static MetadataType GetCriticalHandle(TypeSystemContext context) + { + return context.SystemModule.GetKnownType("System.Runtime.InteropServices", "CriticalHandle"); + } + + public static MetadataType GetMissingMemberException(TypeSystemContext context) + { + return context.SystemModule.GetKnownType("System", "MissingMemberException"); + } + public static MetadataType GetPInvokeMarshal(TypeSystemContext context) { return context.SystemModule.GetKnownType("System.Runtime.InteropServices", "PInvokeMarshal"); } - /* - TODO: Bring CriticalHandle to CoreLib - https://github.com/dotnet/corert/issues/2570 - - public static MetadataType GetCriticalHandle(TypeSystemContext context) - { - return context.SystemModule.GetKnownType("System.Runtime.InteropServices", "CriticalHandle"); - } - - - TODO: Bring HandleRef to CoreLib - https://github.com/dotnet/corert/issues/2570 - - public static MetadataType GetHandleRef(TypeSystemContext context) - { - get - { - return context.SystemModule.GetKnownType("System.Runtime.InteropServices", "HandleRef"); - } - } - */ - public static MetadataType GetNativeFunctionPointerWrapper(TypeSystemContext context) { return context.SystemModule.GetKnownType("System.Runtime.InteropServices", "NativeFunctionPointerWrapper"); } - public static MetadataType GetStringBuilder(TypeSystemContext context, bool throwIfNotFound = true) - { - return context.SystemModule.GetKnownType("System.Text", "StringBuilder", throwIfNotFound); - } - - public static MetadataType GetSystemArray(TypeSystemContext context) - { - return context.SystemModule.GetKnownType("System", "Array"); - } - - public static MetadataType GetSystemDateTime(TypeSystemContext context) - { - return context.SystemModule.GetKnownType("System", "DateTime"); - } - - public static MetadataType GetSystemDecimal(TypeSystemContext context) - { - return context.SystemModule.GetKnownType("System", "Decimal"); - } - - public static MetadataType GetSystemGuid(TypeSystemContext context) - { - return context.SystemModule.GetKnownType("System", "Guid"); - } - public static bool IsSafeHandle(TypeSystemContext context, TypeDesc type) { - return IsOrDerivesFromType(type, GetSafeHandleType(context)); + return IsOrDerivesFromType(type, GetSafeHandle(context)); } - /* - TODO: Bring CriticalHandle to CoreLib - https://github.com/dotnet/corert/issues/2570 - public static bool IsCriticalHandle(TypeSystemContext context, TypeDesc type) - { - return IsOrDerivesFromType(type, context.GetCriticalHandle()); - } - - TODO: Bring HandleRef to CoreLib - public static bool IsHandleRef(TypeSystemContext context, TypeDesc type) - { - return IsOrDerivesFromType(type, this.HandleRef); - } - */ - - public static bool IsSystemArray(TypeSystemContext context, TypeDesc type) + public static bool IsCriticalHandle(TypeSystemContext context, TypeDesc type) { - return type == GetSystemArray(context); + return IsOrDerivesFromType(type, GetCriticalHandle(context)); + } + + private static bool IsCoreNamedType(TypeSystemContext context, TypeDesc type, string @namespace, string name) + { + return type is MetadataType mdType && + mdType.Name == name && + mdType.Namespace == @namespace && + mdType.Module == context.SystemModule; + } + + public static bool IsHandleRef(TypeSystemContext context, TypeDesc type) + { + return IsCoreNamedType(context, type, "System.Runtime.InteropServices", "HandleRef"); } public static bool IsSystemDateTime(TypeSystemContext context, TypeDesc type) { - return type == GetSystemDateTime(context); + return IsCoreNamedType(context, type, "System", "DateTime"); } public static bool IsStringBuilder(TypeSystemContext context, TypeDesc type) { - Debug.Assert(type != null); - return type == GetStringBuilder(context, throwIfNotFound: false); + return IsCoreNamedType(context, type, "System.Text", "StringBuilder"); } public static bool IsSystemDecimal(TypeSystemContext context, TypeDesc type) { - return type == GetSystemDecimal(context); + return IsCoreNamedType(context, type, "System", "Decimal"); } public static bool IsSystemGuid(TypeSystemContext context, TypeDesc type) { - return type == GetSystemGuid(context); + return IsCoreNamedType(context, type, "System", "Guid"); } private static bool IsOrDerivesFromType(TypeDesc type, MetadataType targetType) @@ -134,4 +94,4 @@ namespace Internal.TypeSystem.Interop return false; } } -} \ No newline at end of file +} diff --git a/external/corert/src/Common/src/TypeSystem/Interop/MethodDesc.Interop.cs b/external/corert/src/Common/src/TypeSystem/Interop/MethodDesc.Interop.cs index ac6ef58bbc..78e29e25e7 100644 --- a/external/corert/src/Common/src/TypeSystem/Interop/MethodDesc.Interop.cs +++ b/external/corert/src/Common/src/TypeSystem/Interop/MethodDesc.Interop.cs @@ -184,7 +184,7 @@ namespace Internal.TypeSystem _attributes |= PInvokeAttributes.CallingConventionThisCall; break; default: - System.Diagnostics.Debug.Assert(false, "Unexpected Unmanaged Calling Convention."); + System.Diagnostics.Debug.Fail("Unexpected Unmanaged Calling Convention."); break; } } diff --git a/external/corert/src/Common/src/TypeSystem/NativeFormat/MetadataExtensions.cs b/external/corert/src/Common/src/TypeSystem/NativeFormat/MetadataExtensions.cs index c0c117a38a..7cb0db67c2 100644 --- a/external/corert/src/Common/src/TypeSystem/NativeFormat/MetadataExtensions.cs +++ b/external/corert/src/Common/src/TypeSystem/NativeFormat/MetadataExtensions.cs @@ -14,7 +14,7 @@ namespace Internal.TypeSystem.NativeFormat { public static class MetadataExtensions { - public static bool HasCustomAttribute(this MetadataReader metadataReader, IEnumerable customAttributes, + public static bool HasCustomAttribute(this MetadataReader metadataReader, CustomAttributeHandleCollection customAttributes, string attributeNamespace, string attributeName) { foreach (var attributeHandle in customAttributes) diff --git a/external/corert/src/Common/src/TypeSystem/NativeFormat/NativeFormatField.cs b/external/corert/src/Common/src/TypeSystem/NativeFormat/NativeFormatField.cs index b110da7037..c5c08b63b1 100644 --- a/external/corert/src/Common/src/TypeSystem/NativeFormat/NativeFormatField.cs +++ b/external/corert/src/Common/src/TypeSystem/NativeFormat/NativeFormatField.cs @@ -42,7 +42,7 @@ namespace Internal.TypeSystem.NativeFormat #if DEBUG // Initialize name eagerly in debug builds for convenience - this.ToString(); + InitializeName(); #endif } @@ -267,10 +267,5 @@ namespace Internal.TypeSystem.NativeFormat return MetadataReader.HasCustomAttribute(MetadataReader.GetField(_handle).CustomAttributes, attributeNamespace, attributeName); } - - public override string ToString() - { - return _type.ToString() + "." + Name; - } } } \ No newline at end of file diff --git a/external/corert/src/Common/src/TypeSystem/NativeFormat/NativeFormatGenericParameter.cs b/external/corert/src/Common/src/TypeSystem/NativeFormat/NativeFormatGenericParameter.cs index e552356a30..b21b0696a1 100644 --- a/external/corert/src/Common/src/TypeSystem/NativeFormat/NativeFormatGenericParameter.cs +++ b/external/corert/src/Common/src/TypeSystem/NativeFormat/NativeFormatGenericParameter.cs @@ -121,12 +121,5 @@ namespace Internal.TypeSystem.NativeFormat return constraintTypes; } } - - public override string ToString() - { - MetadataReader reader = MetadataReader; - GenericParameter parameter = reader.GetGenericParameter(_handle); - return parameter.Name.GetConstantStringValue(reader).Value; - } } } diff --git a/external/corert/src/Common/src/TypeSystem/NativeFormat/NativeFormatMethod.cs b/external/corert/src/Common/src/TypeSystem/NativeFormat/NativeFormatMethod.cs index f5cb1f1fcd..461b141768 100644 --- a/external/corert/src/Common/src/TypeSystem/NativeFormat/NativeFormatMethod.cs +++ b/external/corert/src/Common/src/TypeSystem/NativeFormat/NativeFormatMethod.cs @@ -53,7 +53,7 @@ namespace Internal.TypeSystem.NativeFormat #if DEBUG // Initialize name eagerly in debug builds for convenience - this.ToString(); + InitializeName(); #endif } @@ -416,11 +416,6 @@ namespace Internal.TypeSystem.NativeFormat attributeNamespace, attributeName); } - public override string ToString() - { - return _type.ToString() + "." + Name; - } - public override MethodNameAndSignature NameAndSignature { get diff --git a/external/corert/src/Common/src/TypeSystem/NativeFormat/NativeFormatType.MethodImpls.cs b/external/corert/src/Common/src/TypeSystem/NativeFormat/NativeFormatType.MethodImpls.cs index e5ac8734ec..8fab7167cd 100644 --- a/external/corert/src/Common/src/TypeSystem/NativeFormat/NativeFormatType.MethodImpls.cs +++ b/external/corert/src/Common/src/TypeSystem/NativeFormat/NativeFormatType.MethodImpls.cs @@ -98,7 +98,7 @@ namespace Internal.TypeSystem.NativeFormat break; default: - Debug.Assert(false, "unexpected methodDeclHandleType"); + Debug.Fail("unexpected methodDeclHandleType"); break; } diff --git a/external/corert/src/Common/src/TypeSystem/NativeFormat/NativeFormatType.cs b/external/corert/src/Common/src/TypeSystem/NativeFormat/NativeFormatType.cs index d1d7e46500..6b4cb59023 100644 --- a/external/corert/src/Common/src/TypeSystem/NativeFormat/NativeFormatType.cs +++ b/external/corert/src/Common/src/TypeSystem/NativeFormat/NativeFormatType.cs @@ -68,7 +68,7 @@ namespace Internal.TypeSystem.NativeFormat #if DEBUG // Initialize name eagerly in debug builds for convenience - this.ToString(); + InitializeName(); #endif } @@ -287,6 +287,14 @@ namespace Internal.TypeSystem.NativeFormat flags |= TypeFlags.HasFinalizer; } + if ((mask & TypeFlags.IsByRefLikeComputed) != 0) + { + flags |= TypeFlags.IsByRefLikeComputed; + + if (IsValueType && HasCustomAttribute("System.Runtime.CompilerServices", "IsByRefLikeAttribute")) + flags |= TypeFlags.IsByRefLike; + } + return flags; } @@ -447,7 +455,7 @@ namespace Internal.TypeSystem.NativeFormat if (impl == null) { // TODO: invalid input: the type doesn't derive from our System.Object - throw new TypeLoadException(this.GetFullName()); + ThrowHelper.ThrowTypeLoadException(this); } if (impl.OwningType != objectType) @@ -458,8 +466,8 @@ namespace Internal.TypeSystem.NativeFormat return null; } - // TODO: Better exception type. Should be: "CoreLib doesn't have a required thing in it". - throw new NotImplementedException(); + ThrowHelper.ThrowTypeLoadException(objectType); + return null; } public override IEnumerable GetFields() @@ -534,11 +542,6 @@ namespace Internal.TypeSystem.NativeFormat attributeNamespace, attributeName); } - public override string ToString() - { - return "[" + NativeFormatModule.GetName().Name + "]" + this.GetFullName(); - } - public override ClassLayoutMetadata GetClassLayout() { ClassLayoutMetadata result; diff --git a/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/GenericParameterDesc.RuntimeDetermined.cs b/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/GenericParameterDesc.RuntimeDetermined.cs index ca524078ca..79b2b7b429 100644 --- a/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/GenericParameterDesc.RuntimeDetermined.cs +++ b/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/GenericParameterDesc.RuntimeDetermined.cs @@ -12,7 +12,7 @@ namespace Internal.TypeSystem { get { - Debug.Assert(false, "IsRuntimeDeterminedSubtype of an indefinite type"); + Debug.Fail("IsRuntimeDeterminedSubtype of an indefinite type"); return false; } } diff --git a/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/MethodForRuntimeDeterminedType.Sorting.cs b/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/MethodForRuntimeDeterminedType.Sorting.cs new file mode 100644 index 0000000000..dd318b30ce --- /dev/null +++ b/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/MethodForRuntimeDeterminedType.Sorting.cs @@ -0,0 +1,23 @@ +// 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. + +namespace Internal.TypeSystem +{ + // Functionality related to deterministic ordering of types + partial class MethodForRuntimeDeterminedType + { + protected internal override int ClassCode => 719937490; + + protected internal override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + var otherMethod = (MethodForRuntimeDeterminedType)other; + + int result = comparer.CompareWithinClass(_rdType, otherMethod._rdType); + if (result != 0) + return result; + + return comparer.Compare(_typicalMethodDef, otherMethod._typicalMethodDef); + } + } +} diff --git a/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/MethodForRuntimeDeterminedType.cs b/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/MethodForRuntimeDeterminedType.cs new file mode 100644 index 0000000000..2ae3380ad1 --- /dev/null +++ b/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/MethodForRuntimeDeterminedType.cs @@ -0,0 +1,62 @@ +// 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.Diagnostics; + +namespace Internal.TypeSystem +{ + public sealed partial class MethodForRuntimeDeterminedType : MethodDesc + { + private MethodDesc _typicalMethodDef; + private RuntimeDeterminedType _rdType; + + internal MethodForRuntimeDeterminedType(MethodDesc typicalMethodDef, RuntimeDeterminedType rdType) + { + Debug.Assert(typicalMethodDef.IsTypicalMethodDefinition); + + _typicalMethodDef = typicalMethodDef; + _rdType = rdType; + } + + // This constructor is a performance optimization - it allows supplying the hash code if it has already + // been computed prior to the allocation of this type. The supplied hash code still has to match the + // hash code this type would compute on it's own (and we assert to enforce that). + internal MethodForRuntimeDeterminedType(MethodDesc typicalMethodDef, RuntimeDeterminedType rdType, int hashcode) + : this(typicalMethodDef, rdType) + { + SetHashCode(hashcode); + } + + public override TypeSystemContext Context => _typicalMethodDef.Context; + public override TypeDesc OwningType => _rdType; + public override MethodSignature Signature => _typicalMethodDef.Signature; + public override bool IsVirtual => _typicalMethodDef.IsVirtual; + public override bool IsNewSlot => _typicalMethodDef.IsNewSlot; + public override bool IsAbstract => _typicalMethodDef.IsAbstract; + public override bool IsFinal => _typicalMethodDef.IsFinal; + public override bool IsDefaultConstructor => _typicalMethodDef.IsDefaultConstructor; + public override string Name => _typicalMethodDef.Name; + public override MethodDesc GetTypicalMethodDefinition() => _typicalMethodDef; + public override Instantiation Instantiation => _typicalMethodDef.Instantiation; + + public override bool HasCustomAttribute(string attributeNamespace, string attributeName) + { + return _typicalMethodDef.HasCustomAttribute(attributeNamespace, attributeName); + } + + public override bool IsCanonicalMethod(CanonicalFormKind policy) + { + // Owning type is a RuntimeDeterminedType, so it can never be canonical. + // Instantiation for the method can also never be canonical since it's a typical method definition. + return false; + } + + public override MethodDesc GetCanonMethodTarget(CanonicalFormKind kind) + { + TypeDesc canonicalizedTypeOfTargetMethod = _rdType.CanonicalType.ConvertToCanonForm(kind); + return Context.GetMethodForInstantiatedType(_typicalMethodDef, (InstantiatedType)canonicalizedTypeOfTargetMethod); + } + } +} diff --git a/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/RuntimeDeterminedFieldLayoutAlgorithm.cs b/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/RuntimeDeterminedFieldLayoutAlgorithm.cs index 416fe863ec..75039d735b 100644 --- a/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/RuntimeDeterminedFieldLayoutAlgorithm.cs +++ b/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/RuntimeDeterminedFieldLayoutAlgorithm.cs @@ -51,14 +51,6 @@ namespace Internal.TypeSystem return canonicalType.ContainsGCPointers; } - public override bool ComputeIsByRefLike(DefType type) - { - RuntimeDeterminedType runtimeDeterminedType = (RuntimeDeterminedType)type; - DefType canonicalType = runtimeDeterminedType.CanonicalType; - - return canonicalType.IsByRefLike; - } - public override ValueTypeShapeCharacteristics ComputeValueTypeShapeCharacteristics(DefType type) { RuntimeDeterminedType runtimeDeterminedType = (RuntimeDeterminedType)type; diff --git a/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/RuntimeDeterminedType.cs b/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/RuntimeDeterminedType.cs index ccbd0acf17..096fb1c0b6 100644 --- a/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/RuntimeDeterminedType.cs +++ b/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/RuntimeDeterminedType.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using Debug = System.Diagnostics.Debug; @@ -106,6 +107,22 @@ namespace Internal.TypeSystem } } + public override IEnumerable GetMethods() + { + foreach (var method in _rawCanonType.GetMethods()) + { + yield return Context.GetMethodForRuntimeDeterminedType(method.GetTypicalMethodDefinition(), this); + } + } + + public override MethodDesc GetMethod(string name, MethodSignature signature) + { + MethodDesc method = _rawCanonType.GetMethod(name, signature); + if (method == null) + return null; + return Context.GetMethodForRuntimeDeterminedType(method.GetTypicalMethodDefinition(), this); + } + protected override TypeFlags ComputeTypeFlags(TypeFlags mask) { TypeFlags flags = 0; @@ -120,6 +137,11 @@ namespace Internal.TypeSystem flags |= _rawCanonType.GetTypeFlags(mask); } + if ((mask & TypeFlags.IsByRefLikeComputed) != 0) + { + flags |= _rawCanonType.GetTypeFlags(mask); + } + // Might need to define the behavior if we ever hit this. Debug.Assert((flags & mask) != 0); return flags; @@ -151,11 +173,6 @@ namespace Internal.TypeSystem return false; } - public override string ToString() - { - return String.Concat(_runtimeDeterminedDetailsType.ToString(), "_", _rawCanonType.ToString()); - } - public override TypeDesc GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(Instantiation typeInstantiation, Instantiation methodInstantiation) { if (_runtimeDeterminedDetailsType.Kind == GenericParameterKind.Type) diff --git a/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/SignatureVariable.RuntimeDetermined.cs b/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/SignatureVariable.RuntimeDetermined.cs index 82d7685c56..416cc19d1c 100644 --- a/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/SignatureVariable.RuntimeDetermined.cs +++ b/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/SignatureVariable.RuntimeDetermined.cs @@ -12,7 +12,7 @@ namespace Internal.TypeSystem { get { - Debug.Assert(false, "IsRuntimeDeterminedSubtype of an indefinite type"); + Debug.Fail("IsRuntimeDeterminedSubtype of an indefinite type"); return false; } } diff --git a/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/TypeSystemContext.RuntimeDetermined.cs b/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/TypeSystemContext.RuntimeDetermined.cs index 270b9aab73..e84d875242 100644 --- a/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/TypeSystemContext.RuntimeDetermined.cs +++ b/external/corert/src/Common/src/TypeSystem/RuntimeDetermined/TypeSystemContext.RuntimeDetermined.cs @@ -3,6 +3,9 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics; + +using Internal.NativeFormat; namespace Internal.TypeSystem { @@ -60,5 +63,80 @@ namespace Internal.TypeSystem { throw new NotSupportedException(); } + + // + // Methods for runtime determined type + // + + private struct MethodForRuntimeDeterminedTypeKey + { + private MethodDesc _typicalMethodDef; + private RuntimeDeterminedType _rdType; + private int _hashcode; + + public MethodForRuntimeDeterminedTypeKey(MethodDesc typicalMethodDef, RuntimeDeterminedType rdType) + { + _typicalMethodDef = typicalMethodDef; + _rdType = rdType; + _hashcode = TypeHashingAlgorithms.ComputeMethodHashCode(rdType.CanonicalType.GetHashCode(), TypeHashingAlgorithms.ComputeNameHashCode(typicalMethodDef.Name)); + } + + public MethodDesc TypicalMethodDef + { + get + { + return _typicalMethodDef; + } + } + + public RuntimeDeterminedType RDType + { + get + { + return _rdType; + } + } + + public class MethodForRuntimeDeterminedTypeKeyHashtable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(MethodForRuntimeDeterminedTypeKey key) + { + return key._hashcode; + } + + protected override int GetValueHashCode(MethodForRuntimeDeterminedType value) + { + return value.GetHashCode(); + } + + protected override bool CompareKeyToValue(MethodForRuntimeDeterminedTypeKey key, MethodForRuntimeDeterminedType value) + { + if (key._typicalMethodDef != value.GetTypicalMethodDefinition()) + return false; + + return key._rdType == value.OwningType; + } + + protected override bool CompareValueToValue(MethodForRuntimeDeterminedType value1, MethodForRuntimeDeterminedType value2) + { + return (value1.GetTypicalMethodDefinition() == value2.GetTypicalMethodDefinition()) && (value1.OwningType == value2.OwningType); + } + + protected override MethodForRuntimeDeterminedType CreateValueFromKey(MethodForRuntimeDeterminedTypeKey key) + { + return new MethodForRuntimeDeterminedType(key.TypicalMethodDef, key.RDType, key._hashcode); + } + } + } + + private MethodForRuntimeDeterminedTypeKey.MethodForRuntimeDeterminedTypeKeyHashtable _methodForRDTypes = new MethodForRuntimeDeterminedTypeKey.MethodForRuntimeDeterminedTypeKeyHashtable(); + + public MethodDesc GetMethodForRuntimeDeterminedType(MethodDesc typicalMethodDef, RuntimeDeterminedType rdType) + { + Debug.Assert(!(typicalMethodDef is MethodForRuntimeDeterminedType)); + Debug.Assert(typicalMethodDef.IsTypicalMethodDefinition); + + return _methodForRDTypes.GetOrCreateValue(new MethodForRuntimeDeterminedTypeKey(typicalMethodDef, rdType)); + } } } diff --git a/external/corert/src/Common/src/TypeSystem/Sorting/SignatureVariable.Sorting.cs b/external/corert/src/Common/src/TypeSystem/Sorting/SignatureVariable.Sorting.cs index b5fcbbd037..cb5cae9399 100644 --- a/external/corert/src/Common/src/TypeSystem/Sorting/SignatureVariable.Sorting.cs +++ b/external/corert/src/Common/src/TypeSystem/Sorting/SignatureVariable.Sorting.cs @@ -8,19 +8,33 @@ namespace Internal.TypeSystem { // Functionality related to determinstic ordering of types partial class SignatureVariable + { + protected internal sealed override int CompareToImpl(TypeDesc other, TypeSystemComparer comparer) + { + return ((SignatureVariable)other).Index - Index; + } + } + + partial class SignatureTypeVariable { protected internal override int ClassCode { get { - throw new NotSupportedException(); + return 1042124696; + } + } + } + + partial class SignatureMethodVariable + { + protected internal override int ClassCode + { + get + { + return 144542889; } } - protected internal sealed override int CompareToImpl(TypeDesc other, TypeSystemComparer comparer) - { - // Who needs this and why? - throw new NotSupportedException(); - } } } diff --git a/external/corert/src/Common/src/TypeSystem/TypesDebugInfoWriter/DebugInfoWriter.cs b/external/corert/src/Common/src/TypeSystem/TypesDebugInfoWriter/DebugInfoWriter.cs new file mode 100644 index 0000000000..a2640d7572 --- /dev/null +++ b/external/corert/src/Common/src/TypeSystem/TypesDebugInfoWriter/DebugInfoWriter.cs @@ -0,0 +1,778 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using Internal.Text; + +using BlobBuilder = System.Reflection.Metadata.BlobBuilder; +using Blob = System.Reflection.Metadata.Blob; + +namespace Internal.TypeSystem.TypesDebugInfo +{ + class DebugInfoBlob + { + ArrayBuilder _data; + + public byte[] ToArray() => _data.ToArray(); + + public uint Size() { return (uint)_data.Count; } + + public uint DWORDAlignedSize(uint size) + { + checked + { + if ((size + Size()) % 4 != 0) + { + size = size + 4 - ((size + Size()) % 4); + } + } + return size; + } + + public void AlignToDWORD() + { + if ((Size() % 4) != 0) + { + uint pad = 4 - (Size() % 4); + for (uint i = pad; i > 0; i--) + { + WriteBYTE((byte)(0xF0 | i)); // Insert LF_PADX entries. + } + } + } + + public void SetWORDAtBlobIndex(uint blobIndex, ushort src) + { + _data[(int)blobIndex] = (byte)src; + _data[(int)blobIndex + 1] = (byte)(src >> 8); + } + + public void SetDWORDAtBlobIndex(uint blobIndex, uint src) + { + _data[(int)blobIndex] = (byte)src; + _data[(int)blobIndex + 1] = (byte)(src >> 8); + _data[(int)blobIndex + 2] = (byte)(src >> 16); + _data[(int)blobIndex + 3] = (byte)(src >> 24); + } + + public void WriteBYTE(byte src) + { + _data.Add(src); + } + + public void WriteWORD(ushort src) + { + _data.Add((byte)src); + _data.Add((byte)(src >> 8)); + } + + public void WriteDWORD(uint src) + { + _data.Add((byte)src); + _data.Add((byte)(src >> 8)); + _data.Add((byte)(src >> 16)); + _data.Add((byte)(src >> 24)); + } + + public void WriteString(Utf8String utf8String) + { + _data.Append(utf8String.UnderlyingArray); + _data.Add(0); + } + + public void WriteString(string str) + { + WriteString(new Utf8String(str)); + } + + public void WriteBuffer(byte[] data) + { + _data.Append(data); + } + + public void WriteBuffer(DebugInfoBlob debugInfoBlob) + { + _data.Append(debugInfoBlob._data); + } + + public void WriteBuffer(BlobBuilder blobBuilder) + { + foreach (Blob blob in blobBuilder.GetBlobs()) + { + ArraySegment byteChunk = blob.GetBytes(); + this.WriteBuffer(byteChunk.Array, byteChunk.Offset, byteChunk.Count); + } + } + + public void WriteBuffer(byte[] data, int index, int length) + { + _data.Append(data, index, length); + } + + public void WriteQWORD(ulong src) + { + _data.Add((byte)src); + _data.Add((byte)(src >> 8)); + _data.Add((byte)(src >> 16)); + _data.Add((byte)(src >> 24)); + _data.Add((byte)(src >> 32)); + _data.Add((byte)(src >> 40)); + _data.Add((byte)(src >> 48)); + _data.Add((byte)(src >> 56)); + } + + public static uint StringLengthEncoded(Utf8String str) + { + return checked((uint)str.Length + 1); + } + } + class DebugInfoWriter + { + enum LeafKind : ushort + { + // values used for type records + LF_VTSHAPE = 0x000a, + LF_POINTER = 0x1002, + LF_PROCEDURE = 0x1008, + LF_MFUNCTION = 0x1009, + LF_ARGLIST = 0x1201, + LF_FIELDLIST = 0x1203, + LF_BCLASS = 0x1400, + LF_INDEX = 0x1404, + LF_VFUNCTAB = 0x1409, + LF_ENUMERATE = 0x1502, + LF_ARRAY = 0x1503, + LF_CLASS = 0x1504, + LF_STRUCTURE = 0x1505, + LF_ENUM = 0x1507, + LF_MEMBER = 0x150d, + LF_STATICMEMBER = 0x150e, + LF_MFUNC_ID = 0x1602, // member func ID + + // values used for numeric leafs + LF_CHAR = 0x8000, + LF_SHORT = 0x8001, + LF_LONG = 0x8003, + LF_ULONG = 0x8004, + LF_QUADWORD = 0x8009 + }; + + enum LF_CLASS_Properties : ushort + { + None = 0x0000, + ForwardReference = 0x0080 + } + + enum CV_Visibility : ushort + { + Private = 0x1, + Protected = 0x2, + Public = 0x3 + } + + class TypeRecordsBlob : DebugInfoBlob + { + uint _currentTypeIndex = 0x1000; + + public uint GetNextTypeIndex() + { + Debug.Assert((Size() % 4) == 0); + return _currentTypeIndex++; + } + + public void WriteCV_Visibility(CV_Visibility src) + { + WriteWORD((ushort)src); + } + + public void WriteLeafKind(LeafKind src) + { + WriteWORD((ushort)src); + } + + public void WriteLF_CLASS_Properties(LF_CLASS_Properties src) + { + WriteWORD((ushort)src); + } + + public void WriteNumericLeaf(ulong value) + { + long signedValue = (long)value; + if (signedValue < 0) + { + if (signedValue >= -0x80) + { + WriteLeafKind(LeafKind.LF_CHAR); + WriteBYTE((byte)value); + } + else if (signedValue >= -0x8000) + { + WriteLeafKind(LeafKind.LF_SHORT); + WriteWORD((ushort)value); + } + else if (signedValue >= -0x80000000) + { + WriteLeafKind(LeafKind.LF_LONG); + WriteDWORD((uint)value); + } + else + { + WriteLeafKind(LeafKind.LF_QUADWORD); + WriteQWORD(value); + } + } + else + { + if (value < 0x8000) + { + WriteWORD((ushort)value); + } + else if (value <= 0x7FFFFFFF) + { + WriteLeafKind(LeafKind.LF_LONG); + WriteDWORD((uint)value); + } + else if (value <= 0xFFFFFFFF) + { + WriteLeafKind(LeafKind.LF_ULONG); + WriteDWORD((uint)value); + } + else + { + WriteLeafKind(LeafKind.LF_QUADWORD); + WriteQWORD(value); + } + } + } + + public void WriteNumericLeaf(long value) + { + if (value < 0) + { + if (value >= -0x80) + { + WriteLeafKind(LeafKind.LF_CHAR); + WriteBYTE((byte)value); + } + else if (value >= -0x8000) + { + WriteLeafKind(LeafKind.LF_SHORT); + WriteWORD((ushort)value); + } + else if (value >= -0x80000000) + { + WriteLeafKind(LeafKind.LF_LONG); + WriteDWORD((uint)value); + } + else + { + WriteLeafKind(LeafKind.LF_QUADWORD); + WriteQWORD((ulong)value); + } + } + else + { + if (value < 0x8000) + { + WriteWORD((ushort)value); + } + else if (value <= 0x7FFFFFFF) + { + WriteLeafKind(LeafKind.LF_LONG); + WriteDWORD((uint)value); + } + else if (value <= 0xFFFFFFFF) + { + WriteLeafKind(LeafKind.LF_ULONG); + WriteDWORD((uint)value); + } + else + { + WriteLeafKind(LeafKind.LF_QUADWORD); + WriteQWORD((ulong)value); + } + } + } + public static uint NumericLeafSize(long value) + { + if (value < 0) + { + if (value >= -0x80) + return 2 + 1; + else if (value >= -0x8000) + return 2 + 2; + else if (value >= -0x80000000L) + return 2 + 4; + else + return 2 + 8; + } + else + { + if (value < 0x8000) + return 2; + else if (value <= 0x7FFFFFFF || value <= 0xFFFFFFFF) + return 2 + 4; + else + return 2 + 8; + } + } + + public static uint NumericLeafSize(ulong value) + { + long signedValue = (long)value; + if (signedValue < 0) + { + if (signedValue >= -0x80) + return 2 + 1; + else if (signedValue >= -0x8000) + return 2 + 2; + else if (signedValue >= -0x80000000L) + return 2 + 4; + else + return 2 + 8; + } + else + { + if (value < 0x8000) + return 2; + else if (value <= 0x7FFFFFFF || value <= 0xFFFFFFFF) + return 2 + 4; + else + return 2 + 8; + } + } + } + + + TypeRecordsBlob _blob = new TypeRecordsBlob(); + uint _tiVTShapePointer; + + public DebugInfoWriter() + { + // Write header + _blob.WriteBYTE(0x04); + _blob.WriteBYTE(0x00); + _blob.WriteBYTE(0x00); + _blob.WriteBYTE(0x00); + + // Write out vtable shape pointer. Various other contents of this file will refer to it. + _tiVTShapePointer = EmitVFuncTableShapeDebugType(); + } + + private struct FieldListInProgress + { + public uint TypeIndexOfFieldList; + public uint BlobOffsetCurrentFieldListChunk; + public ushort FieldsCount; + } + + public void VerifyBlobEligibleToBeBetweenRecords() + { + Debug.Assert((_blob.Size() % 4) == 0); + } + + private FieldListInProgress StartFieldList() + { + Debug.Assert((_blob.Size() % 4) == 0); + FieldListInProgress fieldListInProgress = new FieldListInProgress(); + fieldListInProgress.BlobOffsetCurrentFieldListChunk = _blob.Size(); + fieldListInProgress.TypeIndexOfFieldList = _blob.GetNextTypeIndex(); + fieldListInProgress.FieldsCount = 0; + _blob.WriteWORD(0); + _blob.WriteLeafKind(LeafKind.LF_FIELDLIST); + + return fieldListInProgress; + } + + private void FinalizeFieldList(FieldListInProgress fieldListInProgress) + { + ushort length = checked((ushort)(_blob.Size() - fieldListInProgress.BlobOffsetCurrentFieldListChunk - 2)); + _blob.SetWORDAtBlobIndex(fieldListInProgress.BlobOffsetCurrentFieldListChunk, length); + } + + private void ExtendFieldList(ref FieldListInProgress fieldListInProgress, uint newDataLength, out bool mustSkipEmission) + { + checked + { + if (fieldListInProgress.FieldsCount == 0xFFFF) + { + mustSkipEmission = true; + return; + } + + mustSkipEmission = false; + + fieldListInProgress.FieldsCount++; + if ((_blob.Size() + newDataLength + 11/* size of LF_INDEX + maximum possible padding*/ - fieldListInProgress.BlobOffsetCurrentFieldListChunk) > 0xFF00) + { + Debug.Assert((_blob.Size() % 4) == 0); + + // Add LF_INDEX record to push forward + _blob.WriteLeafKind(LeafKind.LF_INDEX); + _blob.WriteWORD(0); // pad0 + uint newFieldListTypeIndex = _blob.GetNextTypeIndex(); + _blob.WriteDWORD(newFieldListTypeIndex); + FinalizeFieldList(fieldListInProgress); + + Debug.Assert((_blob.Size() % 4) == 0); + fieldListInProgress.BlobOffsetCurrentFieldListChunk = _blob.Size(); + _blob.WriteWORD(0); + _blob.WriteLeafKind(LeafKind.LF_FIELDLIST); + } + } + } + + private void EmitBaseClass(ref FieldListInProgress fieldListInProgress, uint baseClassIndex) + { + Debug.Assert((_blob.Size() % 4) == 0); + bool mustSkipEmission; + ExtendFieldList(ref fieldListInProgress, 8 + TypeRecordsBlob.NumericLeafSize(0), out mustSkipEmission); + if (mustSkipEmission) + return; + + _blob.WriteLeafKind(LeafKind.LF_BCLASS); + _blob.WriteCV_Visibility(CV_Visibility.Public); + _blob.WriteDWORD(baseClassIndex); + _blob.WriteNumericLeaf(0); + _blob.AlignToDWORD(); + VerifyBlobEligibleToBeBetweenRecords(); + } + + private void EmitDataMember(ref FieldListInProgress fieldListInProgress, uint type, int offset, Utf8String name) + { + Debug.Assert((_blob.Size() % 4) == 0); + + bool isStaticField = (uint)offset == 0xFFFFFFFF; + + bool mustSkipEmission; + uint recordSize = 8 + (isStaticField ? 0 : TypeRecordsBlob.NumericLeafSize(offset)) + DebugInfoBlob.StringLengthEncoded(name); + ExtendFieldList(ref fieldListInProgress, recordSize, out mustSkipEmission); + if (mustSkipEmission) + return; + + _blob.WriteLeafKind(isStaticField ? LeafKind.LF_STATICMEMBER : LeafKind.LF_MEMBER); + _blob.WriteCV_Visibility(CV_Visibility.Public); + _blob.WriteDWORD(type); + + if (!isStaticField) + _blob.WriteNumericLeaf(offset); + + _blob.WriteString(name); + _blob.AlignToDWORD(); + VerifyBlobEligibleToBeBetweenRecords(); + } + + + private void EmitEnumerate(ref FieldListInProgress fieldListInProgress, ulong value, Utf8String name) + { + Debug.Assert((_blob.Size() % 4) == 0); + bool mustSkipEmission; + uint recordSize = 4 + TypeRecordsBlob.NumericLeafSize(value) + DebugInfoBlob.StringLengthEncoded(name); + ExtendFieldList(ref fieldListInProgress, recordSize, out mustSkipEmission); + if (mustSkipEmission) + return; + + _blob.WriteLeafKind(LeafKind.LF_ENUMERATE); + _blob.WriteCV_Visibility(CV_Visibility.Public); + _blob.WriteNumericLeaf(value); + _blob.WriteString(name); + _blob.AlignToDWORD(); + VerifyBlobEligibleToBeBetweenRecords(); + } + + private uint EmitVFuncTableShapeDebugType() + { + Debug.Assert((_blob.Size() % 4) == 0); + uint vFuncTableShapeTypeIndex = _blob.GetNextTypeIndex(); + uint recordSize = 10; + _blob.WriteWORD((ushort)(_blob.DWORDAlignedSize(recordSize) - 2)); + _blob.WriteLeafKind(LeafKind.LF_VTSHAPE); + _blob.WriteWORD(0); + _blob.WriteDWORD(0); + _blob.AlignToDWORD(); + VerifyBlobEligibleToBeBetweenRecords(); + return vFuncTableShapeTypeIndex; + } + + private void EmitVFuncTab(ref FieldListInProgress fieldListInProgress) + { + Debug.Assert((_blob.Size() % 4) == 0); + bool mustSkipEmission; + uint recordSize = 8; + ExtendFieldList(ref fieldListInProgress, recordSize, out mustSkipEmission); + if (mustSkipEmission) + return; + + _blob.WriteLeafKind(LeafKind.LF_VFUNCTAB); + _blob.WriteWORD(0); + _blob.WriteDWORD(_tiVTShapePointer); + VerifyBlobEligibleToBeBetweenRecords(); + } + + public uint GetClassTypeIndex(ClassTypeDescriptor classTypeDescriptor) + { + FieldListInProgress fieldList = default(FieldListInProgress); + if (classTypeDescriptor.BaseClassId != 0) + { + fieldList = StartFieldList(); + EmitBaseClass(ref fieldList, classTypeDescriptor.BaseClassId); + FinalizeFieldList(fieldList); + } + + uint classTypeIndex = _blob.GetNextTypeIndex(); + Utf8String name = new Utf8String(classTypeDescriptor.Name); + uint recordSize = 20 + DebugInfoBlob.StringLengthEncoded(name) + TypeRecordsBlob.NumericLeafSize(0) /*size of length */; + _blob.WriteWORD(checked((ushort)(_blob.DWORDAlignedSize(recordSize) - 2))); // don't include size of 'length' in 'length' + _blob.WriteLeafKind(classTypeDescriptor.IsStruct != 0 ? LeafKind.LF_STRUCTURE : LeafKind.LF_CLASS); + _blob.WriteWORD(fieldList.FieldsCount); + _blob.WriteLF_CLASS_Properties(LF_CLASS_Properties.ForwardReference); + _blob.WriteDWORD(fieldList.TypeIndexOfFieldList); + _blob.WriteDWORD(0); // Derivation list is not filled in here + _blob.WriteDWORD(0); // No vtable shape + _blob.WriteNumericLeaf(0); + _blob.WriteString(name); + _blob.AlignToDWORD(); + VerifyBlobEligibleToBeBetweenRecords(); + + return classTypeIndex; + } + + public uint GetCompleteClassTypeIndex(ClassTypeDescriptor classTypeDescriptor, ClassFieldsTypeDescriptor classFieldsTypeDescriptior, DataFieldDescriptor[] fields) + { + FieldListInProgress fieldList = default(FieldListInProgress); + if ((classTypeDescriptor.BaseClassId != 0) || (fields != null && fields.Length > 0) || (classTypeDescriptor.IsStruct == 0)) + { + fieldList = StartFieldList(); + if (classTypeDescriptor.BaseClassId != 0) + EmitBaseClass(ref fieldList, classTypeDescriptor.BaseClassId); + + if (classTypeDescriptor.IsStruct == 0) + EmitVFuncTab(ref fieldList); + + if (fields != null) + { + foreach (DataFieldDescriptor field in fields) + { + EmitDataMember(ref fieldList, field.FieldTypeIndex, (int)field.Offset, new Utf8String(field.Name)); + } + } + FinalizeFieldList(fieldList); + } + + uint classTypeIndex = _blob.GetNextTypeIndex(); + Utf8String name = new Utf8String(classTypeDescriptor.Name); + uint recordSize = 20 + DebugInfoBlob.StringLengthEncoded(name) + TypeRecordsBlob.NumericLeafSize(classFieldsTypeDescriptior.Size) /*size of length */; + _blob.WriteWORD(checked((ushort)(_blob.DWORDAlignedSize(recordSize) - 2))); // don't include size of 'length' in 'length' + _blob.WriteLeafKind(classTypeDescriptor.IsStruct != 0 ? LeafKind.LF_STRUCTURE : LeafKind.LF_CLASS); + _blob.WriteWORD(fieldList.FieldsCount); + _blob.WriteLF_CLASS_Properties(LF_CLASS_Properties.None); + _blob.WriteDWORD(fieldList.TypeIndexOfFieldList); + _blob.WriteDWORD(0); // Derivation list is not filled in here + _blob.WriteDWORD(_tiVTShapePointer); // No vtable shape + _blob.WriteNumericLeaf(classFieldsTypeDescriptior.Size); + _blob.WriteString(name); + _blob.AlignToDWORD(); + VerifyBlobEligibleToBeBetweenRecords(); + return classTypeIndex; + } + + public uint GetEnumTypeIndex(EnumTypeDescriptor enumTypeDescriptor, EnumRecordTypeDescriptor[] enumerates) + { + checked + { + FieldListInProgress fieldList = default(FieldListInProgress); + if ((enumerates != null && enumerates.Length > 0)) + { + fieldList = StartFieldList(); + foreach (EnumRecordTypeDescriptor enumerate in enumerates) + { + EmitEnumerate(ref fieldList, enumerate.Value, new Utf8String(enumerate.Name)); + } + FinalizeFieldList(fieldList); + } + + if (enumerates != null) + Debug.Assert(checked((int)enumTypeDescriptor.ElementCount == enumerates.Length)); + if (enumerates == null) + Debug.Assert(enumTypeDescriptor.ElementCount == 0); + + uint enumTypeIndex = _blob.GetNextTypeIndex(); + Utf8String name = new Utf8String(enumTypeDescriptor.Name); + uint recordSize = 16 + DebugInfoBlob.StringLengthEncoded(name); + _blob.WriteWORD(checked((ushort)(_blob.DWORDAlignedSize(recordSize) - 2))); // don't include size of 'length' in 'length' + _blob.WriteLeafKind(LeafKind.LF_ENUM); + _blob.WriteWORD(fieldList.FieldsCount); + _blob.WriteWORD(0); + _blob.WriteDWORD((uint)enumTypeDescriptor.ElementType); + _blob.WriteDWORD(fieldList.TypeIndexOfFieldList); + _blob.WriteString(name); + _blob.AlignToDWORD(); + VerifyBlobEligibleToBeBetweenRecords(); + return enumTypeIndex; + } + } + + public uint GetArgListTypeDescriptor(uint[] arguments) + { + uint argListTypeIndex = _blob.GetNextTypeIndex(); + ushort recordSizeEmit; + uint argumentListEmit; + + try + { + checked + { + uint recordSize = (ushort)(8 + (4 * arguments.Length)); + recordSizeEmit = checked((ushort)(_blob.DWORDAlignedSize(recordSize) - 2)); + argumentListEmit = (uint)arguments.Length; + } + } + catch (OverflowException) + { + return 0; + } + + _blob.WriteWORD(recordSizeEmit); // don't include size of 'length' in 'length' + _blob.WriteLeafKind(LeafKind.LF_ARGLIST); + _blob.WriteDWORD(argumentListEmit); + foreach (uint argType in arguments) + { + _blob.WriteDWORD(argType); + } + VerifyBlobEligibleToBeBetweenRecords(); + return argListTypeIndex; + } + + public uint GetMemberFunctionTypeIndex(MemberFunctionTypeDescriptor memberDescriptor, uint[] arguments) + { + uint argumentList = GetArgListTypeDescriptor(arguments); + if (argumentList == 0) + return 0; + + uint memberFunctionTypeIndex = _blob.GetNextTypeIndex(); + uint recordSize = 28; + _blob.WriteWORD(checked((ushort)(_blob.DWORDAlignedSize(recordSize) - 2))); // don't include size of 'length' in 'length' + _blob.WriteLeafKind(LeafKind.LF_MFUNCTION); + _blob.WriteDWORD(memberDescriptor.ReturnType); + _blob.WriteDWORD(memberDescriptor.ContainingClass); + _blob.WriteDWORD(memberDescriptor.TypeIndexOfThisPointer); + _blob.WriteBYTE(checked((byte)memberDescriptor.CallingConvention)); + _blob.WriteBYTE(0); + _blob.WriteWORD(checked((ushort)arguments.Length)); + _blob.WriteDWORD(argumentList); + _blob.WriteDWORD(memberDescriptor.ThisAdjust); + VerifyBlobEligibleToBeBetweenRecords(); + return memberFunctionTypeIndex; + } + + public uint GetMemberFunctionId(MemberFunctionIdTypeDescriptor memberIdDescriptor) + { + uint memberFunctionIdTypeIndex = _blob.GetNextTypeIndex(); + + Utf8String name = new Utf8String(memberIdDescriptor.Name); + uint recordSize = 12 + DebugInfoBlob.StringLengthEncoded(name); + _blob.WriteWORD(checked((ushort)(_blob.DWORDAlignedSize(recordSize) - 2))); // don't include size of 'length' in 'length' + _blob.WriteLeafKind(LeafKind.LF_MFUNC_ID); + _blob.WriteDWORD(memberIdDescriptor.MemberFunction); + _blob.WriteDWORD(memberIdDescriptor.ParentClass); + _blob.WriteString(name); + _blob.AlignToDWORD(); + VerifyBlobEligibleToBeBetweenRecords(); + return memberFunctionIdTypeIndex; + } + + public uint GetPointerTypeIndex(PointerTypeDescriptor pointerTypeDescriptor) + { + uint pointerTypeIndex = _blob.GetNextTypeIndex(); + uint recordSize = 12; + uint attr = 0; + if (pointerTypeDescriptor.IsReference != 0) + attr |= 0x20; + if (pointerTypeDescriptor.IsConst != 0) + attr |= 0x400; + if (pointerTypeDescriptor.Is64Bit != 0) + attr |= 12; + else + attr |= 10; + + _blob.WriteWORD(checked((ushort)(_blob.DWORDAlignedSize(recordSize) - 2))); // don't include size of 'length' in 'length' + _blob.WriteLeafKind(LeafKind.LF_POINTER); + _blob.WriteDWORD(pointerTypeDescriptor.ElementType); + _blob.WriteDWORD(attr); + VerifyBlobEligibleToBeBetweenRecords(); + return pointerTypeIndex; + } + + public uint GetSimpleArrayTypeIndex(uint elementType, uint elementSize) + { + uint simpleArrayTypeIndex = _blob.GetNextTypeIndex(); + + TypeRecordsBlob simpleArrayDataBlob = new TypeRecordsBlob(); + simpleArrayDataBlob.WriteLeafKind(LeafKind.LF_ARRAY); + simpleArrayDataBlob.WriteDWORD(elementType); + simpleArrayDataBlob.WriteDWORD((uint)PrimitiveTypeDescriptor.TYPE_ENUM.T_INT4); + simpleArrayDataBlob.WriteNumericLeaf(elementSize); + simpleArrayDataBlob.WriteString(""); + + uint recordSize = simpleArrayDataBlob.Size() + 2; + _blob.WriteWORD(checked((ushort)(_blob.DWORDAlignedSize(recordSize) - 2))); // don't include size of 'length' in 'length' + _blob.WriteBuffer(simpleArrayDataBlob); + _blob.AlignToDWORD(); + VerifyBlobEligibleToBeBetweenRecords(); + return simpleArrayTypeIndex; + } + + public uint GetArrayTypeIndex(ClassTypeDescriptor classDescriptor, ArrayTypeDescriptor arrayTypeDescriptor, int targetPointerSize) + { + uint simpleArrayDebugType = GetSimpleArrayTypeIndex(arrayTypeDescriptor.ElementType, arrayTypeDescriptor.Size); + + FieldListInProgress fieldList = default(FieldListInProgress); + + fieldList = StartFieldList(); + EmitBaseClass(ref fieldList, classDescriptor.BaseClassId); + EmitDataMember(ref fieldList, (uint)PrimitiveTypeDescriptor.TYPE_ENUM.T_INT4, targetPointerSize, new Utf8String("count")); + int nextOffset = targetPointerSize * 2; + if (arrayTypeDescriptor.IsMultiDimensional != 0) + { + for (uint i = 0; i < arrayTypeDescriptor.Rank; i++) + { + EmitDataMember(ref fieldList, (uint)PrimitiveTypeDescriptor.TYPE_ENUM.T_INT4, nextOffset, new Utf8String("length" + i.ToStringInvariant())); + nextOffset += 4; + } + for (uint i = 0; i < arrayTypeDescriptor.Rank; i++) + { + EmitDataMember(ref fieldList, (uint)PrimitiveTypeDescriptor.TYPE_ENUM.T_INT4, nextOffset, new Utf8String("bounds" + i.ToStringInvariant())); + nextOffset += 4; + } + } + + EmitDataMember(ref fieldList, simpleArrayDebugType, nextOffset, new Utf8String("values")); + + FinalizeFieldList(fieldList); + + uint classTypeIndex = _blob.GetNextTypeIndex(); + Utf8String name = new Utf8String(classDescriptor.Name); + uint recordSize = 20 + DebugInfoBlob.StringLengthEncoded(name) + TypeRecordsBlob.NumericLeafSize(targetPointerSize) /*size of length */; + _blob.WriteWORD(checked((ushort)(_blob.DWORDAlignedSize(recordSize) - 2))); // don't include size of 'length' in 'length' + _blob.WriteLeafKind(classDescriptor.IsStruct != 0 ? LeafKind.LF_STRUCTURE : LeafKind.LF_CLASS); + _blob.WriteWORD(fieldList.FieldsCount); + _blob.WriteLF_CLASS_Properties(LF_CLASS_Properties.None); + _blob.WriteDWORD(fieldList.TypeIndexOfFieldList); + _blob.WriteDWORD(0); // Derivation list is not filled in here + _blob.WriteDWORD(_tiVTShapePointer); // No vtable shape + _blob.WriteNumericLeaf(targetPointerSize); + _blob.WriteString(name); + _blob.AlignToDWORD(); + VerifyBlobEligibleToBeBetweenRecords(); + return classTypeIndex; + } + + public DebugInfoBlob GetRawBlob() + { + return _blob; + } + } +} diff --git a/external/corert/src/Common/src/TypeSystem/TypesDebugInfoWriter/TypesDebugInfoWriter.cs b/external/corert/src/Common/src/TypeSystem/TypesDebugInfoWriter/TypesDebugInfoWriter.cs index e51df2b6e3..dd57f19244 100644 --- a/external/corert/src/Common/src/TypeSystem/TypesDebugInfoWriter/TypesDebugInfoWriter.cs +++ b/external/corert/src/Common/src/TypeSystem/TypesDebugInfoWriter/TypesDebugInfoWriter.cs @@ -16,35 +16,39 @@ namespace Internal.TypeSystem.TypesDebugInfo uint GetArrayTypeIndex(ClassTypeDescriptor classDescriptor, ArrayTypeDescriptor arrayTypeDescriprtor); + uint GetPointerTypeIndex(PointerTypeDescriptor pointerDescriptor); + + uint GetMemberFunctionTypeIndex(MemberFunctionTypeDescriptor memberDescriptor, uint[] argumentTypes); + + uint GetMemberFunctionId(MemberFunctionIdTypeDescriptor memberIdDescriptor); + string GetMangledName(TypeDesc type); } - [StructLayout(LayoutKind.Sequential, Pack = 1)] + [StructLayout(LayoutKind.Sequential)] public struct EnumRecordTypeDescriptor { public ulong Value; public string Name; } - [StructLayout(LayoutKind.Sequential, Pack = 1)] + [StructLayout(LayoutKind.Sequential)] public struct EnumTypeDescriptor { public uint ElementType; public ulong ElementCount; public string Name; - public string UniqueName; } - [StructLayout(LayoutKind.Sequential, Pack = 1)] + [StructLayout(LayoutKind.Sequential)] public struct ClassTypeDescriptor { public int IsStruct; public string Name; - public string UniqueName; public uint BaseClassId; } - [StructLayout(LayoutKind.Sequential, Pack = 1)] + [StructLayout(LayoutKind.Sequential)] public struct DataFieldDescriptor { public uint FieldTypeIndex; @@ -52,14 +56,14 @@ namespace Internal.TypeSystem.TypesDebugInfo public string Name; } - [StructLayout(LayoutKind.Sequential, Pack = 1)] + [StructLayout(LayoutKind.Sequential)] public struct ClassFieldsTypeDescriptor { public ulong Size; public int FieldsCount; } - [StructLayout(LayoutKind.Sequential, Pack = 1)] + [StructLayout(LayoutKind.Sequential)] public struct ArrayTypeDescriptor { public uint Rank; @@ -67,4 +71,32 @@ namespace Internal.TypeSystem.TypesDebugInfo public uint Size; public int IsMultiDimensional; } + + [StructLayout(LayoutKind.Sequential)] + public struct PointerTypeDescriptor + { + public uint ElementType; + public int IsReference; + public int IsConst; + public int Is64Bit; // Otherwise, 32 bit + } + + [StructLayout(LayoutKind.Sequential)] + public struct MemberFunctionTypeDescriptor + { + public uint ReturnType; + public uint ContainingClass; + public uint TypeIndexOfThisPointer; + public uint ThisAdjust; + public uint CallingConvention; + public ushort NumberOfArguments; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MemberFunctionIdTypeDescriptor + { + public uint MemberFunction; + public uint ParentClass; + public string Name; + } } diff --git a/external/corert/src/Common/src/TypeSystem/TypesDebugInfoWriter/UserDefinedTypeDescriptor.cs b/external/corert/src/Common/src/TypeSystem/TypesDebugInfoWriter/UserDefinedTypeDescriptor.cs deleted file mode 100644 index d79434d46d..0000000000 --- a/external/corert/src/Common/src/TypeSystem/TypesDebugInfoWriter/UserDefinedTypeDescriptor.cs +++ /dev/null @@ -1,227 +0,0 @@ -// 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.Collections.Generic; -using System.Reflection.Metadata; -using Internal.TypeSystem.Ecma; - -namespace Internal.TypeSystem.TypesDebugInfo -{ - public class UserDefinedTypeDescriptor - { - public UserDefinedTypeDescriptor(ITypesDebugInfoWriter objectWriter) - { - _objectWriter = objectWriter; - } - - public uint GetVariableTypeIndex(TypeDesc type, bool needsCompleteIndex) - { - uint typeIndex = 0; - if (type.IsPrimitive) - { - typeIndex = PrimitiveTypeDescriptor.GetPrimitiveTypeIndex(type); - } - else - { - typeIndex = GetTypeIndex(type, needsCompleteIndex); - } - return typeIndex; - } - - public uint GetTypeIndex(TypeDesc type, bool needsCompleteType) - { - uint variableTypeIndex = 0; - if (needsCompleteType ? - _completeKnownTypes.TryGetValue(type, out variableTypeIndex) - : _knownTypes.TryGetValue(type, out variableTypeIndex)) - { - return variableTypeIndex; - } - else - { - return GetNewTypeIndex(type, needsCompleteType); - } - } - - private uint GetNewTypeIndex(TypeDesc type, bool needsCompleteType) - { - - if (type.IsEnum) - { - return GetEnumTypeIndex(type); - } - else if (type.IsArray) - { - return GetArrayTypeIndex(type); - } - else if (type.IsDefType) - { - return GetClassTypeIndex(type, needsCompleteType); - } - return 0; - } - - public uint GetEnumTypeIndex(TypeDesc type) - { - System.Diagnostics.Debug.Assert(type.IsEnum, "GetEnumTypeIndex was called with wrong type"); - DefType defType = type as DefType; - System.Diagnostics.Debug.Assert(defType != null, "GetEnumTypeIndex was called with non def type"); - List fieldsDescriptors = new List(); - foreach (var field in defType.GetFields()) - { - if (field.IsLiteral) - { - fieldsDescriptors.Add(field); - } - } - EnumTypeDescriptor enumTypeDescriptor = new EnumTypeDescriptor - { - ElementCount = (ulong)fieldsDescriptors.Count, - ElementType = PrimitiveTypeDescriptor.GetPrimitiveTypeIndex(defType.UnderlyingType), - Name = _objectWriter.GetMangledName(type), - UniqueName = defType.GetFullName() - }; - EnumRecordTypeDescriptor[] typeRecords = new EnumRecordTypeDescriptor[enumTypeDescriptor.ElementCount]; - for (int i = 0; i < fieldsDescriptors.Count; ++i) - { - FieldDesc field = fieldsDescriptors[i]; - EnumRecordTypeDescriptor recordTypeDescriptor; - recordTypeDescriptor.Value = GetEnumRecordValue(field); - recordTypeDescriptor.Name = field.Name; - typeRecords[i] = recordTypeDescriptor; - } - uint typeIndex = _objectWriter.GetEnumTypeIndex(enumTypeDescriptor, typeRecords); - _knownTypes[type] = typeIndex; - _completeKnownTypes[type] = typeIndex; - return typeIndex; - } - - public uint GetArrayTypeIndex(TypeDesc type) - { - System.Diagnostics.Debug.Assert(type.IsArray, "GetArrayTypeIndex was called with wrong type"); - ArrayType arrayType = (ArrayType)type; - ArrayTypeDescriptor arrayTypeDescriptor = new ArrayTypeDescriptor - { - Rank = (uint)arrayType.Rank, - ElementType = GetVariableTypeIndex(arrayType.ElementType, false), - Size = (uint)arrayType.GetElementSize().AsInt, - IsMultiDimensional = arrayType.IsMdArray ? 1 : 0 - }; - - ClassTypeDescriptor classDescriptor = new ClassTypeDescriptor - { - IsStruct = 0, - Name = _objectWriter.GetMangledName(type), - BaseClassId = GetVariableTypeIndex(arrayType.BaseType, false) - }; - - uint typeIndex = _objectWriter.GetArrayTypeIndex(classDescriptor, arrayTypeDescriptor); - _knownTypes[type] = typeIndex; - _completeKnownTypes[type] = typeIndex; - return typeIndex; - } - - public ulong GetEnumRecordValue(FieldDesc field) - { - var ecmaField = field as EcmaField; - if (ecmaField != null) - { - MetadataReader reader = ecmaField.MetadataReader; - FieldDefinition fieldDef = reader.GetFieldDefinition(ecmaField.Handle); - ConstantHandle defaultValueHandle = fieldDef.GetDefaultValue(); - if (!defaultValueHandle.IsNil) - { - return HandleConstant(ecmaField.Module, defaultValueHandle); - } - } - return 0; - } - - private ulong HandleConstant(EcmaModule module, ConstantHandle constantHandle) - { - MetadataReader reader = module.MetadataReader; - Constant constant = reader.GetConstant(constantHandle); - BlobReader blob = reader.GetBlobReader(constant.Value); - switch (constant.TypeCode) - { - case ConstantTypeCode.Byte: - return (ulong)blob.ReadByte(); - case ConstantTypeCode.Int16: - return (ulong)blob.ReadInt16(); - case ConstantTypeCode.Int32: - return (ulong)blob.ReadInt32(); - case ConstantTypeCode.Int64: - return (ulong)blob.ReadInt64(); - case ConstantTypeCode.SByte: - return (ulong)blob.ReadSByte(); - case ConstantTypeCode.UInt16: - return (ulong)blob.ReadUInt16(); - case ConstantTypeCode.UInt32: - return (ulong)blob.ReadUInt32(); - case ConstantTypeCode.UInt64: - return (ulong)blob.ReadUInt64(); - } - System.Diagnostics.Debug.Assert(false); - return 0; - } - - public uint GetClassTypeIndex(TypeDesc type, bool needsCompleteType) - { - DefType defType = type as DefType; - System.Diagnostics.Debug.Assert(defType != null, "GetClassTypeIndex was called with non def type"); - ClassTypeDescriptor classTypeDescriptor = new ClassTypeDescriptor - { - IsStruct = type.IsValueType ? 1 : 0, - Name = _objectWriter.GetMangledName(type), - UniqueName = defType.GetFullName(), - BaseClassId = 0 - }; - - uint typeIndex = _objectWriter.GetClassTypeIndex(classTypeDescriptor); - _knownTypes[type] = typeIndex; - - if (type.HasBaseType && !type.IsValueType) - { - classTypeDescriptor.BaseClassId = GetVariableTypeIndex(defType.BaseType, false); - } - - List fieldsDescs = new List(); - foreach (var fieldDesc in defType.GetFields()) - { - if (fieldDesc.HasRva || fieldDesc.IsLiteral) - continue; - DataFieldDescriptor field = new DataFieldDescriptor - { - FieldTypeIndex = GetVariableTypeIndex(fieldDesc.FieldType, false), - Offset = (ulong)fieldDesc.Offset.AsInt, - Name = fieldDesc.Name - }; - fieldsDescs.Add(field); - } - - DataFieldDescriptor[] fields = new DataFieldDescriptor[fieldsDescs.Count]; - for (int i = 0; i < fieldsDescs.Count; ++i) - { - fields[i] = fieldsDescs[i]; - } - ClassFieldsTypeDescriptor fieldsDescriptor = new ClassFieldsTypeDescriptor - { - Size = (ulong)defType.GetElementSize().AsInt, - FieldsCount = fieldsDescs.Count - }; - - uint completeTypeIndex = _objectWriter.GetCompleteClassTypeIndex(classTypeDescriptor, fieldsDescriptor, fields); - _completeKnownTypes[type] = completeTypeIndex; - - if (needsCompleteType) - return completeTypeIndex; - else - return typeIndex; - } - - private ITypesDebugInfoWriter _objectWriter; - private Dictionary _knownTypes = new Dictionary(); - private Dictionary _completeKnownTypes = new Dictionary(); - } -} diff --git a/external/corert/src/Common/test-runtime/XUnit.Runtime.depproj b/external/corert/src/Common/test-runtime/XUnit.Runtime.depproj new file mode 100644 index 0000000000..dae724a68a --- /dev/null +++ b/external/corert/src/Common/test-runtime/XUnit.Runtime.depproj @@ -0,0 +1,36 @@ + + + + + win7-x64 + netcoreapp2.0 + true + $(RuntimePath) + + $(NoWarn);NU1603 + + + + 1.0.3-prerelease-00921-01 + + + $(XunitNetcoreExtensionsVersion) + + + $(XunitNetcoreExtensionsVersion) + + + + + + + + + + + + + + diff --git a/external/corert/src/Common/test-runtime/project.json b/external/corert/src/Common/test-runtime/project.json deleted file mode 100644 index 8d7ad6dcb9..0000000000 --- a/external/corert/src/Common/test-runtime/project.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "dependencies": { - "NETStandard.Library": "1.6.1", - "Microsoft.NETCore.Runtime.CoreCLR": "1.1.0", - "Microsoft.NETCore.TestHost": "1.1.0", - - "System.Reflection.Metadata": "1.4.2", - "System.Collections.Immutable": "1.3.1", - - "coveralls.io": "1.4", - "OpenCover": "4.6.519", - "ReportGenerator": "2.4.3", - - "Microsoft.DotNet.xunit.performance.analysis": "1.0.0-alpha-build0040", - "Microsoft.DotNet.xunit.performance.runner.Windows": "1.0.0-alpha-build0040", - - "Microsoft.DotNet.BuildTools.TestSuite": "1.0.0-prerelease-00807-03", - "xunit.console.netcore": "1.0.3-prerelease-00607-01" - }, - "frameworks": { - "netstandard1.3": { } - } -} diff --git a/external/corert/src/Framework/Framework-native.depproj b/external/corert/src/Framework/Framework-native.depproj new file mode 100644 index 0000000000..8719e1c856 --- /dev/null +++ b/external/corert/src/Framework/Framework-native.depproj @@ -0,0 +1,24 @@ + + + + $(BaseOutputPath)$(OSPlatformConfig)/framework + true + + + + .NETCoreApp,Version=v2.0 + netcoreapp2.0 + $(NuPkgRid) + osx-x64 + linux-x64 + true + + + + + $(MicrosoftNETCoreNativeVersion) + + + + + diff --git a/external/corert/src/Framework/Framework-uapaot.depproj b/external/corert/src/Framework/Framework-uapaot.depproj new file mode 100644 index 0000000000..5ad5937ad5 --- /dev/null +++ b/external/corert/src/Framework/Framework-uapaot.depproj @@ -0,0 +1,32 @@ + + + + $(BaseOutputPath)$(OSPlatformConfig)/framework + true + + + + UAP,Version=v10.1 + uap10.1 + win10-x64-aot + true + + + + + $(CoreFxUapVersion) + + + + + + + + + + + + + + + diff --git a/external/corert/src/Framework/Framework.depproj b/external/corert/src/Framework/Framework.depproj new file mode 100644 index 0000000000..23f206ea6b --- /dev/null +++ b/external/corert/src/Framework/Framework.depproj @@ -0,0 +1,53 @@ + + + + $(BaseOutputPath)$(OSPlatformConfig)/framework + + + + .NETCoreApp,Version=v2.1 + netcoreapp2.1 + $(NuPkgRid) + osx-x64 + linux-x64 + true + + + + + false + true + + + false + true + + + + + + $(CoreFxVersion) + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/corert/src/ILCompiler.Build.Tasks/src/ComputeManagedAssemblies.cs b/external/corert/src/ILCompiler.Build.Tasks/src/ComputeManagedAssemblies.cs new file mode 100644 index 0000000000..0e4998ecee --- /dev/null +++ b/external/corert/src/ILCompiler.Build.Tasks/src/ComputeManagedAssemblies.cs @@ -0,0 +1,168 @@ +// 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 Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; + + + +namespace Build.Tasks +{ + public class ComputeManagedAssemblies : Task + { + [Required] + public ITaskItem[] Assemblies + { + get; + set; + } + + /// + /// The CoreRT-specific System.Private.* assemblies that must be used instead of the netcoreapp2.0 versions. + /// + [Required] + public ITaskItem[] SdkAssemblies + { + get; + set; + } + + /// + /// The set of AOT-specific framework assemblies we currently need to use which will replace the same-named ones + /// in the app's closure. + /// + [Required] + public ITaskItem[] FrameworkAssemblies + { + get; + set; + } + + /// + /// The native apphost (whose name ends up colliding with the CoreRT output binary) + /// + [Required] + public string DotNetAppHostExecutableName + { + get; + set; + } + + /// + /// The CoreCLR dotnet host fixer library that can be skipped during publish + /// + [Required] + public string DotNetHostFxrLibraryName + { + get; + set; + } + + /// + /// The CoreCLR dotnet host policy library that can be skipped during publish + /// + [Required] + public string DotNetHostPolicyLibraryName + { + get; + set; + } + + [Output] + public ITaskItem[] ManagedAssemblies + { + get; + set; + } + + [Output] + public ITaskItem[] AssembliesToSkipPublish + { + get; + set; + } + + public override bool Execute() + { + var list = new List(); + var assembliesToSkipPublish = new List(); + + var coreRTFrameworkAssembliesToUse = new HashSet(); + + foreach (ITaskItem taskItem in SdkAssemblies) + { + coreRTFrameworkAssembliesToUse.Add(Path.GetFileName(taskItem.ItemSpec)); + } + + foreach (ITaskItem taskItem in FrameworkAssemblies) + { + coreRTFrameworkAssembliesToUse.Add(Path.GetFileName(taskItem.ItemSpec)); + } + + foreach (ITaskItem taskItem in Assemblies) + { + // In the case of disk-based assemblies, this holds the file path + string itemSpec = taskItem.ItemSpec; + + // Skip the native apphost (whose name ends up colliding with the CoreRT output binary) and supporting libraries + if (itemSpec.EndsWith(DotNetAppHostExecutableName, StringComparison.OrdinalIgnoreCase) || itemSpec.Contains(DotNetHostFxrLibraryName) || itemSpec.Contains(DotNetHostPolicyLibraryName)) + { + assembliesToSkipPublish.Add(taskItem); + continue; + } + + // Prototype aid - remove the native CoreCLR runtime pieces from the publish folder + if (itemSpec.Contains("microsoft.netcore.app") && (itemSpec.Contains("\\native\\") || itemSpec.Contains("/native/"))) + { + assembliesToSkipPublish.Add(taskItem); + continue; + } + + // Remove any assemblies whose implementation we want to come from CoreRT's package. + // Currently that's System.Private.* SDK assemblies and a bunch of framework assemblies. + if (coreRTFrameworkAssembliesToUse.Contains(Path.GetFileName(itemSpec))) + { + assembliesToSkipPublish.Add(taskItem); + continue; + } + + try + { + using (FileStream moduleStream = File.OpenRead(itemSpec)) + using (var module = new PEReader(moduleStream)) + { + if (module.HasMetadata) + { + MetadataReader moduleMetadataReader = module.GetMetadataReader(); + if (moduleMetadataReader.IsAssembly) + { + string culture = moduleMetadataReader.GetString(moduleMetadataReader.GetAssemblyDefinition().Culture); + + if (culture == "" || culture.Equals("neutral", StringComparison.OrdinalIgnoreCase)) + { + // CoreRT doesn't consume resource assemblies yet so skip them + assembliesToSkipPublish.Add(taskItem); + list.Add(taskItem); + } + } + } + } + } + catch (BadImageFormatException) + { + } + } + + ManagedAssemblies = list.ToArray(); + AssembliesToSkipPublish = assembliesToSkipPublish.ToArray(); + + return true; + } + } +} diff --git a/external/corert/src/ILCompiler.Build.Tasks/src/ILCompiler.Build.Tasks.csproj b/external/corert/src/ILCompiler.Build.Tasks/src/ILCompiler.Build.Tasks.csproj new file mode 100644 index 0000000000..808a4de4cf --- /dev/null +++ b/external/corert/src/ILCompiler.Build.Tasks/src/ILCompiler.Build.Tasks.csproj @@ -0,0 +1,25 @@ + + + + Library + ILCompiler + ILCompiler.Build.Tasks + netstandard1.3 + $(BaseOutputPath)$(OSPlatformConfig)/tools + + + + + + + 15.3.409 + + + 15.3.409 + + + 1.4.2 + + + + diff --git a/external/corert/src/ILCompiler.Compiler/ILCompiler.Compiler.sln b/external/corert/src/ILCompiler.Compiler/ILCompiler.Compiler.sln new file mode 100644 index 0000000000..5ccae92902 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/ILCompiler.Compiler.sln @@ -0,0 +1,149 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26430.16 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.Compiler.Tests", "tests\ILCompiler.Compiler.Tests.csproj", "{A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.Compiler", "src\ILCompiler.Compiler.csproj", "{395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.TypeSystem", "..\ILCompiler.TypeSystem\src\ILCompiler.TypeSystem.csproj", "{846B4638-43AE-452F-B171-D8DB600F1284}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILCompiler.Compiler.Tests.Assets", "tests\ILCompiler.Compiler.Tests.Assets\ILCompiler.Compiler.Tests.Assets.csproj", "{94D3F829-6ACA-4399-B6CF-EE0486E1E36A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test.CoreLib", "..\Test.CoreLib\src\Test.CoreLib.csproj", "{22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|arm = Debug|arm + Debug|arm64 = Debug|arm64 + Debug|armel = Debug|armel + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|arm = Release|arm + Release|arm64 = Release|arm64 + Release|armel = Release|armel + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}.Debug|Any CPU.ActiveCfg = Debug|x86 + {A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}.Debug|arm.ActiveCfg = Debug|arm + {A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}.Debug|arm.Build.0 = Debug|arm + {A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}.Debug|arm64.ActiveCfg = Debug|arm64 + {A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}.Debug|arm64.Build.0 = Debug|arm64 + {A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}.Debug|armel.ActiveCfg = Debug|armel + {A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}.Debug|armel.Build.0 = Debug|armel + {A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}.Debug|x64.ActiveCfg = Debug|x64 + {A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}.Debug|x64.Build.0 = Debug|x64 + {A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}.Debug|x86.ActiveCfg = Debug|x86 + {A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}.Debug|x86.Build.0 = Debug|x86 + {A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}.Release|Any CPU.ActiveCfg = Release|x86 + {A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}.Release|arm.ActiveCfg = Release|arm + {A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}.Release|arm.Build.0 = Release|arm + {A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}.Release|arm64.ActiveCfg = Release|arm64 + {A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}.Release|arm64.Build.0 = Release|arm64 + {A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}.Release|armel.ActiveCfg = Release|armel + {A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}.Release|armel.Build.0 = Release|armel + {A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}.Release|x64.ActiveCfg = Release|x64 + {A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}.Release|x64.Build.0 = Release|x64 + {A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}.Release|x86.ActiveCfg = Release|x86 + {A0BB7B15-1E33-4F22-9A2E-6899E6FC669B}.Release|x86.Build.0 = Release|x86 + {395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}.Debug|Any CPU.ActiveCfg = Debug|x86 + {395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}.Debug|arm.ActiveCfg = Debug|arm + {395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}.Debug|arm.Build.0 = Debug|arm + {395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}.Debug|arm64.ActiveCfg = Debug|arm64 + {395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}.Debug|arm64.Build.0 = Debug|arm64 + {395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}.Debug|armel.ActiveCfg = Debug|armel + {395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}.Debug|armel.Build.0 = Debug|armel + {395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}.Debug|x64.ActiveCfg = Debug|x64 + {395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}.Debug|x64.Build.0 = Debug|x64 + {395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}.Debug|x86.ActiveCfg = Debug|x86 + {395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}.Debug|x86.Build.0 = Debug|x86 + {395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}.Release|Any CPU.ActiveCfg = Release|x86 + {395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}.Release|arm.ActiveCfg = Release|arm + {395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}.Release|arm.Build.0 = Release|arm + {395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}.Release|arm64.ActiveCfg = Release|arm64 + {395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}.Release|arm64.Build.0 = Release|arm64 + {395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}.Release|armel.ActiveCfg = Release|armel + {395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}.Release|armel.Build.0 = Release|armel + {395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}.Release|x64.ActiveCfg = Release|x64 + {395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}.Release|x64.Build.0 = Release|x64 + {395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}.Release|x86.ActiveCfg = Release|x86 + {395E8F7D-52A1-4E69-A4F5-40D94FB6A81B}.Release|x86.Build.0 = Release|x86 + {846B4638-43AE-452F-B171-D8DB600F1284}.Debug|Any CPU.ActiveCfg = Debug|x86 + {846B4638-43AE-452F-B171-D8DB600F1284}.Debug|arm.ActiveCfg = Debug|arm + {846B4638-43AE-452F-B171-D8DB600F1284}.Debug|arm.Build.0 = Debug|arm + {846B4638-43AE-452F-B171-D8DB600F1284}.Debug|arm64.ActiveCfg = Debug|arm64 + {846B4638-43AE-452F-B171-D8DB600F1284}.Debug|arm64.Build.0 = Debug|arm64 + {846B4638-43AE-452F-B171-D8DB600F1284}.Debug|armel.ActiveCfg = Debug|armel + {846B4638-43AE-452F-B171-D8DB600F1284}.Debug|armel.Build.0 = Debug|armel + {846B4638-43AE-452F-B171-D8DB600F1284}.Debug|x64.ActiveCfg = Debug|x64 + {846B4638-43AE-452F-B171-D8DB600F1284}.Debug|x64.Build.0 = Debug|x64 + {846B4638-43AE-452F-B171-D8DB600F1284}.Debug|x86.ActiveCfg = Debug|x86 + {846B4638-43AE-452F-B171-D8DB600F1284}.Debug|x86.Build.0 = Debug|x86 + {846B4638-43AE-452F-B171-D8DB600F1284}.Release|Any CPU.ActiveCfg = Release|x86 + {846B4638-43AE-452F-B171-D8DB600F1284}.Release|arm.ActiveCfg = Release|arm + {846B4638-43AE-452F-B171-D8DB600F1284}.Release|arm.Build.0 = Release|arm + {846B4638-43AE-452F-B171-D8DB600F1284}.Release|arm64.ActiveCfg = Release|arm64 + {846B4638-43AE-452F-B171-D8DB600F1284}.Release|arm64.Build.0 = Release|arm64 + {846B4638-43AE-452F-B171-D8DB600F1284}.Release|armel.ActiveCfg = Release|armel + {846B4638-43AE-452F-B171-D8DB600F1284}.Release|armel.Build.0 = Release|armel + {846B4638-43AE-452F-B171-D8DB600F1284}.Release|x64.ActiveCfg = Release|x64 + {846B4638-43AE-452F-B171-D8DB600F1284}.Release|x64.Build.0 = Release|x64 + {846B4638-43AE-452F-B171-D8DB600F1284}.Release|x86.ActiveCfg = Release|x86 + {846B4638-43AE-452F-B171-D8DB600F1284}.Release|x86.Build.0 = Release|x86 + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Debug|arm.ActiveCfg = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Debug|arm.Build.0 = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Debug|arm64.ActiveCfg = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Debug|arm64.Build.0 = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Debug|armel.ActiveCfg = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Debug|armel.Build.0 = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Debug|x64.ActiveCfg = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Debug|x64.Build.0 = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Debug|x86.ActiveCfg = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Debug|x86.Build.0 = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Release|Any CPU.Build.0 = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Release|arm.ActiveCfg = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Release|arm.Build.0 = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Release|arm64.ActiveCfg = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Release|arm64.Build.0 = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Release|armel.ActiveCfg = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Release|armel.Build.0 = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Release|x64.ActiveCfg = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Release|x64.Build.0 = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Release|x86.ActiveCfg = Debug|Any CPU + {94D3F829-6ACA-4399-B6CF-EE0486E1E36A}.Release|x86.Build.0 = Debug|Any CPU + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Debug|Any CPU.ActiveCfg = Debug|x86 + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Debug|arm.ActiveCfg = Debug|arm + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Debug|arm.Build.0 = Debug|arm + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Debug|arm64.ActiveCfg = Debug|arm64 + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Debug|arm64.Build.0 = Debug|arm64 + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Debug|armel.ActiveCfg = Debug|armel + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Debug|armel.Build.0 = Debug|armel + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Debug|x64.ActiveCfg = Debug|x64 + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Debug|x64.Build.0 = Debug|x64 + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Debug|x86.ActiveCfg = Debug|x86 + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Debug|x86.Build.0 = Debug|x86 + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Release|Any CPU.ActiveCfg = Debug|arm64 + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Release|Any CPU.Build.0 = Debug|arm64 + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Release|arm.ActiveCfg = Debug|arm + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Release|arm.Build.0 = Debug|arm + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Release|arm64.ActiveCfg = Debug|arm64 + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Release|arm64.Build.0 = Debug|arm64 + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Release|armel.ActiveCfg = Debug|armel + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Release|armel.Build.0 = Debug|armel + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Release|x64.ActiveCfg = Debug|x64 + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Release|x64.Build.0 = Debug|x64 + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Release|x86.ActiveCfg = Debug|x86 + {22C898F5-CE3A-4BE4-87B0-1E2F29ECB5D6}.Release|x86.Build.0 = Debug|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/AnalysisBasedMetadataManager.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/AnalysisBasedMetadataManager.cs new file mode 100644 index 0000000000..ccbeb5afb5 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/AnalysisBasedMetadataManager.cs @@ -0,0 +1,263 @@ +// 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.Collections.Generic; + +using Internal.TypeSystem; + +using ILCompiler.Metadata; +using ILCompiler.DependencyAnalysis; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + /// + /// A metadata manager that knows the full set of metadata ahead of time. + /// + public sealed class AnalysisBasedMetadataManager : GeneratingMetadataManager, ICompilationRootProvider + { + private readonly List _modulesWithMetadata; + + private readonly Dictionary _reflectableTypes = new Dictionary(); + private readonly Dictionary _reflectableMethods = new Dictionary(); + private readonly Dictionary _reflectableFields = new Dictionary(); + + public AnalysisBasedMetadataManager( + ModuleDesc generatedAssembly, + CompilerTypeSystemContext typeSystemContext, + MetadataBlockingPolicy blockingPolicy, + string logFile, + StackTraceEmissionPolicy stackTracePolicy, + IEnumerable modulesWithMetadata, + IEnumerable> reflectableTypes, + IEnumerable> reflectableMethods, + IEnumerable> reflectableFields) + : base(generatedAssembly, typeSystemContext, blockingPolicy, logFile, stackTracePolicy) + { + _modulesWithMetadata = new List(modulesWithMetadata); + + foreach (var refType in reflectableTypes) + { + _reflectableTypes.Add(refType.Entity, refType.Category); + } + + foreach (var refMethod in reflectableMethods) + { + // Asking for description or runtime mapping for a member without asking + // for the owning type would mean we can't actually satisfy the request. + Debug.Assert((refMethod.Category & MetadataCategory.Description) == 0 + || (_reflectableTypes[refMethod.Entity.OwningType] & MetadataCategory.Description) != 0); + Debug.Assert((refMethod.Category & MetadataCategory.RuntimeMapping) == 0 + || (_reflectableTypes[refMethod.Entity.OwningType] & MetadataCategory.RuntimeMapping) != 0); + _reflectableMethods.Add(refMethod.Entity, refMethod.Category); + + MethodDesc canonMethod = refMethod.Entity.GetCanonMethodTarget(CanonicalFormKind.Specific); + if (refMethod.Entity != canonMethod) + { + if (!_reflectableMethods.TryGetValue(canonMethod, out MetadataCategory category)) + category = 0; + _reflectableMethods[canonMethod] = category | refMethod.Category; + } + } + + foreach (var refField in reflectableFields) + { + // Asking for description or runtime mapping for a member without asking + // for the owning type would mean we can't actually satisfy the request. + Debug.Assert((refField.Category & MetadataCategory.Description) == 0 + || (_reflectableTypes[refField.Entity.OwningType] & MetadataCategory.Description) != 0); + Debug.Assert((refField.Category & MetadataCategory.RuntimeMapping) == 0 + || (_reflectableTypes[refField.Entity.OwningType] & MetadataCategory.RuntimeMapping) != 0); + _reflectableFields.Add(refField.Entity, refField.Category); + } + +#if DEBUG + HashSet moduleHash = new HashSet(_modulesWithMetadata); + foreach (var refType in reflectableTypes) + { + // The instantiated types need to agree on the Description bit with the definition. + // GetMetadataCategory relies on that. + Debug.Assert((GetMetadataCategory(refType.Entity.GetTypeDefinition()) & MetadataCategory.Description) + == (GetMetadataCategory(refType.Entity) & MetadataCategory.Description)); + + Debug.Assert(!(refType.Entity is MetadataType) || moduleHash.Contains(((MetadataType)refType.Entity).Module)); + } + + foreach (var refMethod in reflectableMethods) + { + // The instantiated methods need to agree on the Description bit with the definition. + // GetMetadataCategory relies on that. + Debug.Assert((GetMetadataCategory(refMethod.Entity.GetTypicalMethodDefinition()) & MetadataCategory.Description) + == (GetMetadataCategory(refMethod.Entity) & MetadataCategory.Description)); + } + + foreach (var refField in reflectableFields) + { + // The instantiated fields need to agree on the Description bit with the definition. + // GetMetadataCategory relies on that. + Debug.Assert((GetMetadataCategory(refField.Entity.GetTypicalFieldDefinition()) & MetadataCategory.Description) + == (GetMetadataCategory(refField.Entity) & MetadataCategory.Description)); + } +#endif + } + + public override IEnumerable GetCompilationModulesWithMetadata() + { + return _modulesWithMetadata; + } + + protected override void ComputeMetadata(NodeFactory factory, + out byte[] metadataBlob, + out List> typeMappings, + out List> methodMappings, + out List> fieldMappings, + out List> stackTraceMapping) + { + ComputeMetadata(new Policy(_blockingPolicy, this), factory, + out metadataBlob, + out typeMappings, + out methodMappings, + out fieldMappings, + out stackTraceMapping); + } + + protected sealed override MetadataCategory GetMetadataCategory(MethodDesc method) + { + if (_reflectableMethods.TryGetValue(method, out MetadataCategory value)) + return value; + return 0; + } + + protected sealed override MetadataCategory GetMetadataCategory(TypeDesc type) + { + if (_reflectableTypes.TryGetValue(type, out MetadataCategory value)) + return value; + return 0; + } + + protected sealed override MetadataCategory GetMetadataCategory(FieldDesc field) + { + if (_reflectableFields.TryGetValue(field, out MetadataCategory value)) + return value; + return 0; + } + + protected override IEnumerable GetFieldsWithRuntimeMapping() + { + foreach (var pair in _reflectableFields) + { + if ((pair.Value & MetadataCategory.RuntimeMapping) != 0) + yield return pair.Key; + } + } + + void ICompilationRootProvider.AddCompilationRoots(IRootingServiceProvider rootProvider) + { + // We go over all the types and members that need a runtime artiface present in the + // compiled executable and root it. + + const string reason = "Reflection"; + + foreach (var pair in _reflectableTypes) + { + if ((pair.Value & MetadataCategory.RuntimeMapping) != 0) + rootProvider.AddCompilationRoot(pair.Key, reason); + } + + foreach (var pair in _reflectableMethods) + { + if ((pair.Value & MetadataCategory.RuntimeMapping) != 0) + { + MethodDesc method = pair.Key; + + // We need to root virtual methods as if they were called virtually. + // This will possibly trigger the generation of other overrides too. + if (method.IsVirtual) + rootProvider.RootVirtualMethodForReflection(method, reason); + + if (!method.IsAbstract) + rootProvider.AddCompilationRoot(pair.Key, reason); + } + } + + foreach (var pair in _reflectableFields) + { + if ((pair.Value & MetadataCategory.RuntimeMapping) != 0) + { + FieldDesc field = pair.Key; + + // We only care about static fields at this point. Instance fields don't need + // runtime artifacts generated in the image. + if (field.IsStatic && !field.IsLiteral) + { + if (field.IsThreadStatic) + rootProvider.RootThreadStaticBaseForType(field.OwningType, reason); + else if (field.HasGCStaticBase) + rootProvider.RootGCStaticBaseForType(field.OwningType, reason); + else + rootProvider.RootNonGCStaticBaseForType(field.OwningType, reason); + } + } + } + } + + private struct Policy : IMetadataPolicy + { + private readonly MetadataBlockingPolicy _blockingPolicy; + private readonly ExplicitScopeAssemblyPolicyMixin _explicitScopeMixin; + private readonly AnalysisBasedMetadataManager _parent; + + public Policy(MetadataBlockingPolicy blockingPolicy, + AnalysisBasedMetadataManager parent) + { + _blockingPolicy = blockingPolicy; + _parent = parent; + _explicitScopeMixin = new ExplicitScopeAssemblyPolicyMixin(); + } + + public bool GeneratesMetadata(FieldDesc fieldDef) + { + return (_parent.GetMetadataCategory(fieldDef) & MetadataCategory.Description) != 0; + } + + public bool GeneratesMetadata(MethodDesc methodDef) + { + return (_parent.GetMetadataCategory(methodDef) & MetadataCategory.Description) != 0; + } + + public bool GeneratesMetadata(MetadataType typeDef) + { + return (_parent.GetMetadataCategory(typeDef) & MetadataCategory.Description) != 0; + } + + public bool IsBlocked(MetadataType typeDef) + { + return _blockingPolicy.IsBlocked(typeDef); + } + + public bool IsBlocked(MethodDesc methodDef) + { + return _blockingPolicy.IsBlocked(methodDef); + } + + public ModuleDesc GetModuleOfType(MetadataType typeDef) + { + return _explicitScopeMixin.GetModuleOfType(typeDef); + } + } + } + + public struct ReflectableEntity + { + public readonly TEntity Entity; + public readonly MetadataCategory Category; + + public ReflectableEntity(TEntity entity, MetadataCategory category) + { + Entity = entity; + Category = category; + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/BlockedInternalsBlockingPolicy.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/BlockedInternalsBlockingPolicy.cs index da6759197d..c0ac54a272 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/BlockedInternalsBlockingPolicy.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/BlockedInternalsBlockingPolicy.cs @@ -19,14 +19,21 @@ namespace ILCompiler /// public sealed class BlockedInternalsBlockingPolicy : MetadataBlockingPolicy { + private enum ModuleBlockingMode + { + None, + BlockedInternals, + FullyBlocked, + } + private class ModuleBlockingState { public ModuleDesc Module { get; } - public bool HasBlockedInternals { get; } - public ModuleBlockingState(ModuleDesc module, bool hasBlockedInternals) + public ModuleBlockingMode BlockingMode { get; } + public ModuleBlockingState(ModuleDesc module, ModuleBlockingMode mode) { Module = module; - HasBlockedInternals = hasBlockedInternals; + BlockingMode = mode; } } @@ -38,8 +45,18 @@ namespace ILCompiler protected override bool CompareValueToValue(ModuleBlockingState value1, ModuleBlockingState value2) => Object.ReferenceEquals(value1.Module, value2.Module); protected override ModuleBlockingState CreateValueFromKey(ModuleDesc module) { - bool moduleHasBlockingPolicy = module.GetType("System.Runtime.CompilerServices", "__BlockReflectionAttribute", false) != null; - return new ModuleBlockingState(module, moduleHasBlockingPolicy); + ModuleBlockingMode blockingMode = ModuleBlockingMode.None; + + if (module.GetType("System.Runtime.CompilerServices", "__BlockAllReflectionAttribute", false) != null) + { + blockingMode = ModuleBlockingMode.FullyBlocked; + } + else if (module.GetType("System.Runtime.CompilerServices", "__BlockReflectionAttribute", false) != null) + { + blockingMode = ModuleBlockingMode.BlockedInternals; + } + + return new ModuleBlockingState(module, blockingMode); } } private BlockedModulesHashtable _blockedModules = new BlockedModulesHashtable(); @@ -70,17 +87,17 @@ namespace ILCompiler protected override bool CompareValueToValue(BlockingState value1, BlockingState value2) => Object.ReferenceEquals(value1.Type, value2.Type); protected override BlockingState CreateValueFromKey(EcmaType type) { - bool isBlocked = false; - if (_blockedModules.GetOrCreateValue(type.EcmaModule).HasBlockedInternals) - { - isBlocked = ComputeIsBlocked(type); - } - + ModuleBlockingMode moduleBlockingMode = _blockedModules.GetOrCreateValue(type.EcmaModule).BlockingMode; + bool isBlocked = ComputeIsBlocked(type, moduleBlockingMode); return new BlockingState(type, isBlocked); } - private bool ComputeIsBlocked(EcmaType type) + private bool ComputeIsBlocked(EcmaType type, ModuleBlockingMode blockingMode) { + // If no blocking is applied to the module, the type is not blocked + if (blockingMode == ModuleBlockingMode.None) + return false; + // type always gets metadata if (type.IsModuleType) return false; @@ -89,6 +106,10 @@ namespace ILCompiler if (type.Name == "SR") return false; + // We block everything else if the module is blocked + if (blockingMode == ModuleBlockingMode.FullyBlocked) + return true; + var typeDefinition = type.MetadataReader.GetTypeDefinition(type.Handle); DefType containingType = type.ContainingType; if (containingType == null) @@ -102,7 +123,7 @@ namespace ILCompiler { if ((typeDefinition.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic) { - return ComputeIsBlocked((EcmaType)containingType); + return ComputeIsBlocked((EcmaType)containingType, blockingMode); } else { @@ -115,6 +136,21 @@ namespace ILCompiler } private BlockedTypeHashtable _blockedTypes; + private MetadataType _arrayOfTType; + private MetadataType InitializeArrayOfTType(TypeSystemEntity contextEntity) + { + _arrayOfTType = contextEntity.Context.SystemModule.GetType("System", "Array`1"); + return _arrayOfTType; + } + private MetadataType GetArrayOfTType(TypeSystemEntity contextEntity) + { + if (_arrayOfTType != null) + { + return _arrayOfTType; + } + return InitializeArrayOfTType(contextEntity); + } + public BlockedInternalsBlockingPolicy() { _blockedTypes = new BlockedTypeHashtable(_blockedModules); @@ -139,14 +175,28 @@ namespace ILCompiler if (ecmaMethod == null) return true; + ModuleBlockingMode moduleBlockingMode = _blockedModules.GetOrCreateValue(ecmaMethod.Module).BlockingMode; + if (moduleBlockingMode == ModuleBlockingMode.None) + return false; + else if (moduleBlockingMode == ModuleBlockingMode.FullyBlocked) + return true; + + // We are blocking internal implementation details + Debug.Assert(moduleBlockingMode == ModuleBlockingMode.BlockedInternals); + if (_blockedTypes.GetOrCreateValue((EcmaType)ecmaMethod.OwningType).IsBlocked) return true; - if (_blockedModules.GetOrCreateValue(ecmaMethod.Module).HasBlockedInternals) - { - if ((ecmaMethod.Attributes & MethodAttributes.Public) != MethodAttributes.Public) - return true; - } + if ((ecmaMethod.Attributes & MethodAttributes.Public) != MethodAttributes.Public) + return true; + + // Methods on Array`1 are implementation details that implement the generic interfaces on + // arrays. They should not generate metadata or be reflection invokable. + // We could get rid of this special casing two ways: + // * Make these method stop being regular EcmaMethods with Array as their owning type, or + // * Make these methods implement the interfaces explicitly (they would become private and naturally blocked) + if (ecmaMethod.OwningType == GetArrayOfTType(ecmaMethod)) + return true; return false; } @@ -159,14 +209,20 @@ namespace ILCompiler if (ecmaField == null) return true; + ModuleBlockingMode moduleBlockingMode = _blockedModules.GetOrCreateValue(ecmaField.Module).BlockingMode; + if (moduleBlockingMode == ModuleBlockingMode.None) + return false; + else if (moduleBlockingMode == ModuleBlockingMode.FullyBlocked) + return true; + + // We are blocking internal implementation details + Debug.Assert(moduleBlockingMode == ModuleBlockingMode.BlockedInternals); + if (_blockedTypes.GetOrCreateValue((EcmaType)ecmaField.OwningType).IsBlocked) return true; - if (_blockedModules.GetOrCreateValue(ecmaField.Module).HasBlockedInternals) - { - if ((ecmaField.Attributes & FieldAttributes.Public) != FieldAttributes.Public) - return true; - } + if ((ecmaField.Attributes & FieldAttributes.Public) != FieldAttributes.Public) + return true; return false; } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/CodeGenerationFailedException.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/CodeGenerationFailedException.cs new file mode 100644 index 0000000000..4be840a5cf --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/CodeGenerationFailedException.cs @@ -0,0 +1,28 @@ +// 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 Internal.TypeSystem; + +namespace ILCompiler +{ + public class CodeGenerationFailedException : InternalCompilerErrorException + { + private const string MessageText = "Code generation failed"; + + public MethodDesc Method { get; } + + public CodeGenerationFailedException(MethodDesc method) + : this(method, null) + { + } + + public CodeGenerationFailedException(MethodDesc method, Exception inner) + : base(MessageText, inner) + { + Method = method; + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/ComparerCompilationRootProvider.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/ComparerCompilationRootProvider.cs new file mode 100644 index 0000000000..58310783b7 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/ComparerCompilationRootProvider.cs @@ -0,0 +1,95 @@ +// 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 Internal.TypeSystem; + +namespace ILCompiler +{ + /// + /// Compilation roots necessary to enable use of the jit intrinsic based Comparer.Default and EqualityComparer.Default implementations + /// + public class ComparerCompilationRootProvider : ICompilationRootProvider + { + TypeSystemContext _context; + + public ComparerCompilationRootProvider(TypeSystemContext context) + { + _context = context; + } + + private Instantiation GetUniformInstantiation(int numArgs, TypeDesc uniformInstanitationType) + { + TypeDesc[] args = new TypeDesc[numArgs]; + for (int i = 0; i < numArgs; i++) + args[i] = uniformInstanitationType; + return new Instantiation(args); + } + + private MethodDesc InstantiateMethodOverUniformType(MethodDesc method, TypeDesc uniformInstanitationType) + { + method = method.GetTypicalMethodDefinition(); + + Instantiation typeUniformInstantiation = GetUniformInstantiation(method.OwningType.Instantiation.Length, uniformInstanitationType); + + DefType owningType; + + if (typeUniformInstantiation.Length == 0) + owningType = (DefType)method.OwningType; + else + owningType = ((MetadataType)method.OwningType).MakeInstantiatedType(typeUniformInstantiation); + + Instantiation methodUniformInstantiation = GetUniformInstantiation(method.Instantiation.Length, uniformInstanitationType); + + MethodDesc uninstantiatedMethod = owningType.FindMethodOnTypeWithMatchingTypicalMethod(method); + + if (methodUniformInstantiation.Length == 0) + return uninstantiatedMethod; + else + return uninstantiatedMethod.MakeInstantiatedMethod(methodUniformInstantiation); + } + + private void AddUniformInstantiationForMethod(IRootingServiceProvider rootProvider, MethodDesc method, TypeDesc uniformInstanitationType) + { + MethodDesc instantiatedMethod = InstantiateMethodOverUniformType(method, uniformInstanitationType); + rootProvider.AddCompilationRoot(instantiatedMethod, "Adding uniform instantiation"); + } + + private void AddCanonInstantiationsForMethod(IRootingServiceProvider rootProvider, MethodDesc method, bool normalCanonSupported) + { + if (_context.SupportsCanon && normalCanonSupported) + AddUniformInstantiationForMethod(rootProvider, method, _context.CanonType); + + if (_context.SupportsUniversalCanon) + AddUniformInstantiationForMethod(rootProvider, method, _context.UniversalCanonType); + } + + private void AddCanonInstantiationsForMethod(IRootingServiceProvider rootProvider, MetadataType type, string methodName, bool normalCanonSupported) + { + MethodDesc method = type.GetMethod(methodName, null); + AddCanonInstantiationsForMethod(rootProvider, method, normalCanonSupported); + } + + public void AddCompilationRoots(IRootingServiceProvider rootProvider) + { + ModuleDesc systemModule = ((MetadataType)_context.GetWellKnownType(WellKnownType.Object)).Module; + + MetadataType equalityComparerType = systemModule.GetType("Internal.IntrinsicSupport", "EqualityComparerHelpers", false) as MetadataType; + if (equalityComparerType != null) + { + AddCanonInstantiationsForMethod(rootProvider, equalityComparerType, "GetKnownGenericEquatableComparer", true); + AddCanonInstantiationsForMethod(rootProvider, equalityComparerType, "GetKnownObjectEquatableComparer", true); + AddCanonInstantiationsForMethod(rootProvider, equalityComparerType, "GetKnownNullableEquatableComparer", false); + AddCanonInstantiationsForMethod(rootProvider, equalityComparerType, "GetKnownEnumEquatableComparer", false); + } + + MetadataType comparerType = systemModule.GetType("Internal.IntrinsicSupport", "ComparerHelpers", false) as MetadataType; + if (comparerType != null) + { + AddCanonInstantiationsForMethod(rootProvider, comparerType, "GetKnownGenericComparer", true); + AddCanonInstantiationsForMethod(rootProvider, comparerType, "GetKnownObjectComparer", true); + AddCanonInstantiationsForMethod(rootProvider, comparerType, "GetKnownNullableComparer", false); + } + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/Compilation.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/Compilation.cs index f476ef2e5d..d686a0d7b4 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/Compilation.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/Compilation.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using ILCompiler.DependencyAnalysis; @@ -23,6 +24,8 @@ namespace ILCompiler protected readonly DependencyAnalyzerBase _dependencyGraph; protected readonly NodeFactory _nodeFactory; protected readonly Logger _logger; + private readonly DebugInformationProvider _debugInformationProvider; + private readonly DevirtualizationManager _devirtualizationManager; public NameMangler NameMangler => _nodeFactory.NameMangler; public NodeFactory NodeFactory => _nodeFactory; @@ -30,8 +33,6 @@ namespace ILCompiler public Logger Logger => _logger; internal PInvokeILProvider PInvokeILProvider { get; } - protected abstract bool GenerateDebugInfo { get; } - private readonly TypeGetTypeMethodThunkCache _typeGetTypeMethodThunks; private readonly AssemblyGetExecutingAssemblyMethodThunkCache _assemblyGetExecutingAssemblyMethodThunks; private readonly MethodBaseGetCurrentMethodThunkCache _methodBaseGetCurrentMethodThunks; @@ -40,11 +41,15 @@ namespace ILCompiler DependencyAnalyzerBase dependencyGraph, NodeFactory nodeFactory, IEnumerable compilationRoots, + DebugInformationProvider debugInformationProvider, + DevirtualizationManager devirtualizationManager, Logger logger) { _dependencyGraph = dependencyGraph; _nodeFactory = nodeFactory; _logger = logger; + _debugInformationProvider = debugInformationProvider; + _devirtualizationManager = devirtualizationManager; _dependencyGraph.ComputeDependencyRoutine += ComputeDependencyNodeDependencies; NodeFactory.AttachToDependencyGraph(_dependencyGraph); @@ -115,12 +120,7 @@ namespace ILCompiler public MethodDebugInformation GetDebugInfo(MethodIL methodIL) { - if (!GenerateDebugInfo) - return MethodDebugInformation.None; - - // This method looks odd right now, but it's an extensibility point that lets us generate - // fake debugging information for things that don't have physical symbols. - return methodIL.GetDebugInfo(); + return _debugInformationProvider.GetDebugInfo(methodIL); } /// @@ -176,7 +176,142 @@ namespace ILCompiler return intrinsicMethod; } - void ICompilation.Compile(string outputFile, ObjectDumper dumper) + public bool HasFixedSlotVTable(TypeDesc type) + { + return NodeFactory.VTable(type).HasFixedSlots; + } + + public bool IsEffectivelySealed(TypeDesc type) + { + return _devirtualizationManager.IsEffectivelySealed(type); + } + + public bool IsEffectivelySealed(MethodDesc method) + { + return _devirtualizationManager.IsEffectivelySealed(method); + } + + public MethodDesc ResolveVirtualMethod(MethodDesc declMethod, TypeDesc implType) + { + return _devirtualizationManager.ResolveVirtualMethod(declMethod, implType); + } + + public bool NeedsRuntimeLookup(ReadyToRunHelperId lookupKind, object targetOfLookup) + { + switch (lookupKind) + { + case ReadyToRunHelperId.TypeHandle: + case ReadyToRunHelperId.NecessaryTypeHandle: + case ReadyToRunHelperId.DefaultConstructor: + return ((TypeDesc)targetOfLookup).IsRuntimeDeterminedSubtype; + + case ReadyToRunHelperId.MethodDictionary: + case ReadyToRunHelperId.MethodEntry: + case ReadyToRunHelperId.VirtualDispatchCell: + case ReadyToRunHelperId.MethodHandle: + return ((MethodDesc)targetOfLookup).IsRuntimeDeterminedExactMethod; + + case ReadyToRunHelperId.FieldHandle: + return ((FieldDesc)targetOfLookup).OwningType.IsRuntimeDeterminedSubtype; + + default: + throw new NotImplementedException(); + } + } + + public ISymbolNode ComputeConstantLookup(ReadyToRunHelperId lookupKind, object targetOfLookup) + { + switch (lookupKind) + { + case ReadyToRunHelperId.TypeHandle: + return NodeFactory.ConstructedTypeSymbol((TypeDesc)targetOfLookup); + case ReadyToRunHelperId.NecessaryTypeHandle: + return NodeFactory.NecessaryTypeSymbol((TypeDesc)targetOfLookup); + case ReadyToRunHelperId.MethodDictionary: + return NodeFactory.MethodGenericDictionary((MethodDesc)targetOfLookup); + case ReadyToRunHelperId.MethodEntry: + return NodeFactory.FatFunctionPointer((MethodDesc)targetOfLookup); + case ReadyToRunHelperId.MethodHandle: + return NodeFactory.RuntimeMethodHandle((MethodDesc)targetOfLookup); + case ReadyToRunHelperId.FieldHandle: + return NodeFactory.RuntimeFieldHandle((FieldDesc)targetOfLookup); + case ReadyToRunHelperId.DefaultConstructor: + { + var type = (TypeDesc)targetOfLookup; + MethodDesc ctor = type.GetDefaultConstructor(); + if (ctor == null) + { + MetadataType activatorType = TypeSystemContext.SystemModule.GetKnownType("System", "Activator"); + MetadataType classWithMissingCtor = activatorType.GetKnownNestedType("ClassWithMissingConstructor"); + ctor = classWithMissingCtor.GetParameterlessConstructor(); + } + return NodeFactory.CanonicalEntrypoint(ctor); + } + + default: + throw new NotImplementedException(); + } + } + + public GenericDictionaryLookup ComputeGenericLookup(MethodDesc contextMethod, ReadyToRunHelperId lookupKind, object targetOfLookup) + { + GenericContextSource contextSource; + + if (contextMethod.RequiresInstMethodDescArg()) + { + contextSource = GenericContextSource.MethodParameter; + } + else if (contextMethod.RequiresInstMethodTableArg()) + { + contextSource = GenericContextSource.TypeParameter; + } + else + { + Debug.Assert(contextMethod.AcquiresInstMethodTableFromThis()); + contextSource = GenericContextSource.ThisObject; + } + + // Can we do a fixed lookup? Start by checking if we can get to the dictionary. + // Context source having a vtable with fixed slots is a prerequisite. + if (contextSource == GenericContextSource.MethodParameter + || HasFixedSlotVTable(contextMethod.OwningType)) + { + DictionaryLayoutNode dictionaryLayout; + if (contextSource == GenericContextSource.MethodParameter) + dictionaryLayout = _nodeFactory.GenericDictionaryLayout(contextMethod); + else + dictionaryLayout = _nodeFactory.GenericDictionaryLayout(contextMethod.OwningType); + + // If the dictionary layout has fixed slots, we can compute the lookup now. Otherwise defer to helper. + if (dictionaryLayout.HasFixedSlots) + { + int pointerSize = _nodeFactory.Target.PointerSize; + + GenericLookupResult lookup = ReadyToRunGenericHelperNode.GetLookupSignature(_nodeFactory, lookupKind, targetOfLookup); + int dictionarySlot = dictionaryLayout.GetSlotForFixedEntry(lookup); + if (dictionarySlot != -1) + { + int dictionaryOffset = dictionarySlot * pointerSize; + + if (contextSource == GenericContextSource.MethodParameter) + { + return GenericDictionaryLookup.CreateFixedLookup(contextSource, dictionaryOffset); + } + else + { + int vtableSlot = VirtualMethodSlotHelper.GetGenericDictionarySlot(_nodeFactory, contextMethod.OwningType); + int vtableOffset = EETypeNode.GetVTableOffset(pointerSize) + vtableSlot * pointerSize; + return GenericDictionaryLookup.CreateFixedLookup(contextSource, vtableOffset, dictionaryOffset); + } + } + } + } + + // Fixed lookup not possible - use helper. + return GenericDictionaryLookup.CreateHelperLookup(contextSource); + } + + CompilationResults ICompilation.Compile(string outputFile, ObjectDumper dumper) { if (dumper != null) { @@ -191,15 +326,8 @@ namespace ILCompiler { dumper.End(); } - } - void ICompilation.WriteDependencyLog(string fileName) - { - using (FileStream dgmlOutput = new FileStream(fileName, FileMode.Create)) - { - DgmlWriter.WriteDependencyGraphToStream(dgmlOutput, _dependencyGraph, _nodeFactory); - dgmlOutput.Flush(); - } + return new CompilationResults(_dependencyGraph, _nodeFactory); } private class RootingServiceProvider : IRootingServiceProvider @@ -224,59 +352,130 @@ namespace ILCompiler public void AddCompilationRoot(TypeDesc type, string reason) { - if (!ConstructedEETypeNode.CreationAllowed(type)) - { - _graph.AddRoot(_factory.NecessaryTypeSymbol(type), reason); - } - else - { - _graph.AddRoot(_factory.ConstructedTypeSymbol(type), reason); - } + _graph.AddRoot(_factory.MaximallyConstructableType(type), reason); } - public void RootStaticBasesForType(TypeDesc type, string reason) + public void RootThreadStaticBaseForType(TypeDesc type, string reason) { Debug.Assert(!type.IsGenericDefinition); MetadataType metadataType = type as MetadataType; - if (metadataType != null) + if (metadataType != null && metadataType.ThreadStaticFieldSize.AsInt > 0) { - if (metadataType.ThreadStaticFieldSize.AsInt > 0) - { - _graph.AddRoot(_factory.TypeThreadStaticIndex(metadataType), reason); - } + _graph.AddRoot(_factory.TypeThreadStaticIndex(metadataType), reason); - if (metadataType.GCStaticFieldSize.AsInt > 0) - { - _graph.AddRoot(_factory.TypeGCStaticsSymbol(metadataType), reason); - } - - if (metadataType.NonGCStaticFieldSize.AsInt > 0) - { + // Also explicitly root the non-gc base if we have a lazy cctor + if(_factory.TypeSystemContext.HasLazyStaticConstructor(type)) _graph.AddRoot(_factory.TypeNonGCStaticsSymbol(metadataType), reason); - } } } - + + public void RootGCStaticBaseForType(TypeDesc type, string reason) + { + Debug.Assert(!type.IsGenericDefinition); + + MetadataType metadataType = type as MetadataType; + if (metadataType != null && metadataType.GCStaticFieldSize.AsInt > 0) + { + _graph.AddRoot(_factory.TypeGCStaticsSymbol(metadataType), reason); + + // Also explicitly root the non-gc base if we have a lazy cctor + if (_factory.TypeSystemContext.HasLazyStaticConstructor(type)) + _graph.AddRoot(_factory.TypeNonGCStaticsSymbol(metadataType), reason); + } + } + + public void RootNonGCStaticBaseForType(TypeDesc type, string reason) + { + Debug.Assert(!type.IsGenericDefinition); + + MetadataType metadataType = type as MetadataType; + if (metadataType != null && (metadataType.NonGCStaticFieldSize.AsInt > 0 || _factory.TypeSystemContext.HasLazyStaticConstructor(type))) + { + _graph.AddRoot(_factory.TypeNonGCStaticsSymbol(metadataType), reason); + } + } + public void RootVirtualMethodForReflection(MethodDesc method, string reason) { Debug.Assert(method.IsVirtual); - if (!_factory.CompilationModuleGroup.ShouldProduceFullVTable(method.OwningType)) - _graph.AddRoot(_factory.VirtualMethodUse(method), reason); + // Virtual method use is tracked on the slot defining method only. + MethodDesc slotDefiningMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method); + if (!_factory.VTable(slotDefiningMethod.OwningType).HasFixedSlots) + _graph.AddRoot(_factory.VirtualMethodUse(slotDefiningMethod), reason); if (method.IsAbstract) { _graph.AddRoot(_factory.ReflectableMethod(method), reason); } } + + public void RootModuleMetadata(ModuleDesc module, string reason) + { + _graph.AddRoot(_factory.ModuleMetadata(module), reason); + } } } // Interface under which Compilation is exposed externally. public interface ICompilation { - void Compile(string outputFileName, ObjectDumper dumper); - void WriteDependencyLog(string outputFileName); + CompilationResults Compile(string outputFileName, ObjectDumper dumper); + } + + public class CompilationResults + { + private readonly DependencyAnalyzerBase _graph; + private readonly NodeFactory _factory; + + protected ImmutableArray> MarkedNodes + { + get + { + return _graph.MarkedNodeList; + } + } + + internal CompilationResults(DependencyAnalyzerBase graph, NodeFactory factory) + { + _graph = graph; + _factory = factory; + } + + public void WriteDependencyLog(string fileName) + { + using (FileStream dgmlOutput = new FileStream(fileName, FileMode.Create)) + { + DgmlWriter.WriteDependencyGraphToStream(dgmlOutput, _graph, _factory); + dgmlOutput.Flush(); + } + } + + public IEnumerable CompiledMethodBodies + { + get + { + foreach (var node in MarkedNodes) + { + if (node is IMethodBodyNode) + yield return ((IMethodBodyNode)node).Method; + } + } + } + + public IEnumerable ConstructedEETypes + { + get + { + foreach (var node in MarkedNodes) + { + if (node is ConstructedEETypeNode || node is CanonicalEETypeNode) + { + yield return ((IEETypeNode)node).Type; + } + } + } + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs index 838606a9d1..fc20b34321 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs @@ -22,15 +22,18 @@ namespace ILCompiler private DependencyTrackingLevel _dependencyTrackingLevel = DependencyTrackingLevel.None; protected IEnumerable _compilationRoots = Array.Empty(); protected OptimizationMode _optimizationMode = OptimizationMode.None; - protected bool _generateDebugInfo = false; protected MetadataManager _metadataManager; + protected VTableSliceProvider _vtableSliceProvider = new LazyVTableSliceProvider(); + protected DictionaryLayoutProvider _dictionaryLayoutProvider = new LazyDictionaryLayoutProvider(); + protected DebugInformationProvider _debugInformationProvider = new DebugInformationProvider(); + protected DevirtualizationManager _devirtualizationManager = new DevirtualizationManager(); public CompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup compilationGroup, NameMangler nameMangler) { _context = context; _compilationGroup = compilationGroup; _nameMangler = nameMangler; - _metadataManager = new EmptyMetadataManager(compilationGroup, context); + _metadataManager = new EmptyMetadataManager(context); } public CompilationBuilder UseLogger(Logger logger) @@ -63,18 +66,35 @@ namespace ILCompiler return this; } - public CompilationBuilder UseDebugInfo(bool generateDebugInfo) + public CompilationBuilder UseVTableSliceProvider(VTableSliceProvider provider) { - _generateDebugInfo = generateDebugInfo; + _vtableSliceProvider = provider; + return this; + } + + public CompilationBuilder UseGenericDictionaryLayoutProvider(DictionaryLayoutProvider provider) + { + _dictionaryLayoutProvider = provider; + return this; + } + + public CompilationBuilder UseDevirtualizationManager(DevirtualizationManager manager) + { + _devirtualizationManager = manager; + return this; + } + + public CompilationBuilder UseDebugInfoProvider(DebugInformationProvider provider) + { + _debugInformationProvider = provider; return this; } public abstract CompilationBuilder UseBackendOptions(IEnumerable options); - protected DependencyAnalyzerBase CreateDependencyGraph(NodeFactory factory) + protected DependencyAnalyzerBase CreateDependencyGraph(NodeFactory factory, IComparer> comparer = null) { - // TODO: add graph sorter when we go multi-threaded - return _dependencyTrackingLevel.CreateDependencyGraph(factory); + return _dependencyTrackingLevel.CreateDependencyGraph(factory, comparer); } public ILScannerBuilder GetILScannerBuilder(CompilationModuleGroup compilationGroup = null) diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilationModuleGroup.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilationModuleGroup.cs index 3a2de5bff1..8fa73ff20b 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilationModuleGroup.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilationModuleGroup.cs @@ -12,6 +12,13 @@ using Debug = System.Diagnostics.Debug; namespace ILCompiler { + public enum ExportForm + { + None = 0, + ByName = 1, + ByOrdinal = 2, + } + public abstract class CompilationModuleGroup { /// @@ -30,9 +37,13 @@ namespace ILCompiler /// public abstract bool ContainsType(TypeDesc type); /// + /// If true, type dictionary of "type" is in the module to be compiled + /// + public abstract bool ContainsTypeDictionary(TypeDesc type); + /// /// If true, "method" is in the set of input assemblies being compiled /// - public abstract bool ContainsMethodBody(MethodDesc method); + public abstract bool ContainsMethodBody(MethodDesc method, bool unboxingStub); /// /// If true, the generic dictionary of "method" is in the set of input assemblies being compiled /// @@ -40,15 +51,19 @@ namespace ILCompiler /// /// If true, "type" is exported by the set of input assemblies being compiled /// - public abstract bool ExportsType(TypeDesc type); + public abstract ExportForm GetExportTypeForm(TypeDesc type); + /// + /// If true, generic dictionary of "type" is exported by the set of input assemblies being compiled + /// + public abstract ExportForm GetExportTypeFormDictionary(TypeDesc type); /// /// If true, "method" is exported by the set of input assemblies being compiled /// - public abstract bool ExportsMethod(MethodDesc method); + public abstract ExportForm GetExportMethodForm(MethodDesc method, bool unboxingStub); /// /// If true, the generic dictionary of "method" is exported by the set of input assemblies being compiled /// - public abstract bool ExportsMethodDictionary(MethodDesc method); + public abstract ExportForm GetExportMethodDictionaryForm(MethodDesc method); /// /// If true, all code is compiled into a single module /// @@ -64,6 +79,11 @@ namespace ILCompiler /// public abstract bool ShouldPromoteToFullType(TypeDesc type); /// + /// If true, if a type is in the dependency graph, its non-generic methods that can be transformed + /// into code must be. + /// + public abstract bool PresenceOfEETypeImpliesAllMethodsOnType(TypeDesc type); + /// /// If true, the type will not be linked into the same module as the current compilation and therefore /// accessed through the target platform's import mechanism (ie, Import Address Table on Windows) /// @@ -102,10 +122,10 @@ namespace ILCompiler public override MetadataType GetType(string nameSpace, string name, bool throwIfNotFound = true) { - Debug.Assert(false, "Resolving a TypeRef in the compiler generated assembly?"); + Debug.Fail("Resolving a TypeRef in the compiler generated assembly?"); if (throwIfNotFound) - throw new TypeSystemException.TypeLoadException(nameSpace, name, this); + ThrowHelper.ThrowTypeLoadException(nameSpace, name, this); return null; } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilerGeneratedInteropStubManager.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilerGeneratedInteropStubManager.cs new file mode 100644 index 0000000000..d63f1b92a7 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilerGeneratedInteropStubManager.cs @@ -0,0 +1,282 @@ +// 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.Collections.Generic; + +using Internal.IL.Stubs; +using Internal.TypeSystem; +using Internal.TypeSystem.Interop; +using ILCompiler.DependencyAnalysis; + +using Debug = System.Diagnostics.Debug; +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; +using ReflectionMapBlob = Internal.Runtime.ReflectionMapBlob; + +namespace ILCompiler +{ + /// + /// This class is responsible for managing stub methods for interop + /// + public sealed class CompilerGeneratedInteropStubManager : InteropStubManager + { + internal HashSet _delegateMarshalingTypes = new HashSet(); + private HashSet _structMarshallingTypes = new HashSet(); + + public CompilerGeneratedInteropStubManager(CompilationModuleGroup compilationModuleGroup, CompilerTypeSystemContext typeSystemContext, InteropStateManager interopStateManager) : + base(compilationModuleGroup, typeSystemContext, interopStateManager) + { + } + + private MethodDesc GetOpenStaticDelegateMarshallingStub(TypeDesc delegateType) + { + var stub = InteropStateManager.GetOpenStaticDelegateMarshallingThunk(delegateType); + Debug.Assert(stub != null); + + _delegateMarshalingTypes.Add(delegateType); + return stub; + } + + private MethodDesc GetClosedDelegateMarshallingStub(TypeDesc delegateType) + { + var stub = InteropStateManager.GetClosedDelegateMarshallingThunk(delegateType); + Debug.Assert(stub != null); + + _delegateMarshalingTypes.Add(delegateType); + return stub; + } + private MethodDesc GetForwardDelegateCreationStub(TypeDesc delegateType) + { + var stub = InteropStateManager.GetForwardDelegateCreationThunk(delegateType); + Debug.Assert(stub != null); + + _delegateMarshalingTypes.Add(delegateType); + return stub; + } + + private MethodDesc GetStructMarshallingManagedToNativeStub(TypeDesc structType) + { + MethodDesc stub = InteropStateManager.GetStructMarshallingManagedToNativeThunk(structType); + Debug.Assert(stub != null); + + _structMarshallingTypes.Add(structType); + return stub; + } + + private MethodDesc GetStructMarshallingNativeToManagedStub(TypeDesc structType) + { + MethodDesc stub = InteropStateManager.GetStructMarshallingNativeToManagedThunk(structType); + Debug.Assert(stub != null); + + _structMarshallingTypes.Add(structType); + return stub; + } + + private MethodDesc GetStructMarshallingCleanupStub(TypeDesc structType) + { + MethodDesc stub = InteropStateManager.GetStructMarshallingCleanupThunk(structType); + Debug.Assert(stub != null); + + _structMarshallingTypes.Add(structType); + return stub; + } + + private TypeDesc GetInlineArrayType(InlineArrayCandidate candidate) + { + TypeDesc inlineArrayType = InteropStateManager.GetInlineArrayType(candidate); + Debug.Assert(inlineArrayType != null); + return inlineArrayType; + } + + internal struct DelegateMarshallingThunks + { + public TypeDesc DelegateType; + public MethodDesc OpenStaticDelegateMarshallingThunk; + public MethodDesc ClosedDelegateMarshallingThunk; + public MethodDesc DelegateCreationThunk; + } + + internal IEnumerable GetDelegateMarshallingThunks() + { + foreach (var delegateType in _delegateMarshalingTypes) + { + yield return + new DelegateMarshallingThunks() + { + DelegateType = delegateType, + OpenStaticDelegateMarshallingThunk = InteropStateManager.GetOpenStaticDelegateMarshallingThunk(delegateType), + ClosedDelegateMarshallingThunk = InteropStateManager.GetClosedDelegateMarshallingThunk(delegateType), + DelegateCreationThunk = InteropStateManager.GetForwardDelegateCreationThunk(delegateType) + }; + } + } + + internal struct StructMarshallingThunks + { + public TypeDesc StructType; + public NativeStructType NativeStructType; + public MethodDesc MarshallingThunk; + public MethodDesc UnmarshallingThunk; + public MethodDesc CleanupThunk; + } + + internal IEnumerable GetStructMarshallingTypes() + { + foreach (var structType in _structMarshallingTypes) + { + yield return + new StructMarshallingThunks() + { + StructType = structType, + NativeStructType = InteropStateManager.GetStructMarshallingNativeType(structType), + MarshallingThunk = InteropStateManager.GetStructMarshallingManagedToNativeThunk(structType), + UnmarshallingThunk = InteropStateManager.GetStructMarshallingNativeToManagedThunk(structType), + CleanupThunk = InteropStateManager.GetStructMarshallingCleanupThunk(structType) + }; + } + } + + private void AddDependenciesDueToPInvokeStructDelegateField(ref DependencyList dependencies, NodeFactory factory, TypeDesc typeDesc) + { + if (typeDesc is ByRefType) + { + typeDesc = typeDesc.GetParameterType(); + } + + MetadataType metadataType = typeDesc as MetadataType; + if (metadataType != null) + { + foreach (FieldDesc field in metadataType.GetFields()) + { + if (field.IsStatic) + { + continue; + } + TypeDesc fieldType = field.FieldType; + + if (fieldType.IsDelegate) + { + AddDependenciesDueToPInvokeDelegate(ref dependencies, factory, fieldType); + } + else if (MarshalHelpers.IsStructMarshallingRequired(fieldType)) + { + AddDependenciesDueToPInvokeStructDelegateField(ref dependencies, factory, fieldType); + } + } + } + } + + public override void AddDependeciesDueToPInvoke(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + if (method.IsPInvoke) + { + dependencies = dependencies ?? new DependencyList(); + + MethodSignature methodSig = method.Signature; + AddDependenciesDueToPInvokeDelegate(ref dependencies, factory, methodSig.ReturnType); + + // struct may contain delegate fields, hence we need to add dependencies for it + if (MarshalHelpers.IsStructMarshallingRequired(methodSig.ReturnType)) + { + AddDependenciesDueToPInvokeStructDelegateField(ref dependencies, factory, methodSig.ReturnType); + } + + for (int i = 0; i < methodSig.Length; i++) + { + AddDependenciesDueToPInvokeDelegate(ref dependencies, factory, methodSig[i]); + if (MarshalHelpers.IsStructMarshallingRequired(methodSig[i])) + { + AddDependenciesDueToPInvokeStructDelegateField(ref dependencies, factory, methodSig[i]); + } + } + } + + if (method.HasInstantiation) + { + dependencies = dependencies ?? new DependencyList(); + AddMarshalAPIsGenericDependencies(ref dependencies, factory, method); + } + } + + public override void AddInterestingInteropConstructedTypeDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + if (type.IsDelegate) + { + var delegateType = (MetadataType)type; + if (delegateType.HasCustomAttribute("System.Runtime.InteropServices", "UnmanagedFunctionPointerAttribute")) + { + AddDependenciesDueToPInvokeDelegate(ref dependencies, factory, delegateType); + } + } + } + + /// + /// For Marshal generic APIs(eg. Marshal.StructureToPtr, GetFunctionPointerForDelegate) we add + /// the generic parameter as dependencies so that we can generate runtime data for them + /// + public override void AddMarshalAPIsGenericDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + Debug.Assert(method.HasInstantiation); + + TypeDesc owningType = method.OwningType; + MetadataType metadataType = owningType as MetadataType; + if (metadataType != null && metadataType.Module == _interopModule) + { + if (metadataType.Name == "Marshal" && metadataType.Namespace == "System.Runtime.InteropServices") + { + string methodName = method.Name; + if (methodName == "GetFunctionPointerForDelegate" || + methodName == "GetDelegateForFunctionPointer" || + methodName == "PtrToStructure" || + methodName == "StructureToPtr" || + methodName == "SizeOf" || + methodName == "OffsetOf") + { + foreach (TypeDesc type in method.Instantiation) + { + AddDependenciesDueToPInvokeDelegate(ref dependencies, factory, type); + AddDependenciesDueToPInvokeStruct(ref dependencies, factory, type); + } + } + } + } + } + + private void AddDependenciesDueToPInvokeDelegate(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + if (type.IsDelegate) + { + dependencies.Add(factory.NecessaryTypeSymbol(type), "Delegate Marshalling Stub"); + + dependencies.Add(factory.MethodEntrypoint(GetOpenStaticDelegateMarshallingStub(type)), "Delegate Marshalling Stub"); + dependencies.Add(factory.MethodEntrypoint(GetClosedDelegateMarshallingStub(type)), "Delegate Marshalling Stub"); + dependencies.Add(factory.MethodEntrypoint(GetForwardDelegateCreationStub(type)), "Delegate Marshalling Stub"); + } + } + + private void AddDependenciesDueToPInvokeStruct(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + if (MarshalHelpers.IsStructMarshallingRequired(type)) + { + dependencies.Add(factory.NecessaryTypeSymbol(type), "Struct Marshalling Stub"); + + var stub = (StructMarshallingThunk)GetStructMarshallingManagedToNativeStub(type); + dependencies.Add(factory.MethodEntrypoint(stub), "Struct Marshalling stub"); + dependencies.Add(factory.MethodEntrypoint(GetStructMarshallingNativeToManagedStub(type)), "Struct Marshalling stub"); + dependencies.Add(factory.MethodEntrypoint(GetStructMarshallingCleanupStub(type)), "Struct Marshalling stub"); + + AddDependenciesDueToPInvokeStructDelegateField(ref dependencies, factory, type); + } + } + + public override void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode) + { + var delegateMapNode = new DelegateMarshallingStubMapNode(commonFixupsTableNode); + header.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.DelegateMarshallingStubMap), delegateMapNode, delegateMapNode, delegateMapNode.EndSymbol); + + var structMapNode = new StructMarshallingStubMapNode(commonFixupsTableNode); + header.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.StructMarshallingStubMap), structMapNode, structMapNode, structMapNode.EndSymbol); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilerGeneratedMetadataManager.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilerGeneratedMetadataManager.cs deleted file mode 100644 index 3c9fc89f35..0000000000 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilerGeneratedMetadataManager.cs +++ /dev/null @@ -1,284 +0,0 @@ -// 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.Collections.Generic; -using System.Text; - -using Internal.IL.Stubs; -using Internal.TypeSystem; -using Internal.Metadata.NativeFormat.Writer; - -using ILCompiler.Metadata; -using ILCompiler.DependencyAnalysis; - -using Debug = System.Diagnostics.Debug; -using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; - -namespace ILCompiler -{ - /// - /// This class is responsible for managing native metadata to be emitted into the compiled - /// module. It applies a policy that every type/method emitted shall be reflectable. - /// - public sealed class CompilerGeneratedMetadataManager : MetadataManager - { - private readonly string _metadataLogFile; - private Dictionary _dynamicInvokeThunks; - - public CompilerGeneratedMetadataManager(CompilationModuleGroup group, CompilerTypeSystemContext typeSystemContext, string logFile) - : base(group, typeSystemContext, new BlockedInternalsBlockingPolicy()) - { - _metadataLogFile = logFile; - - if (DynamicInvokeMethodThunk.SupportsThunks(typeSystemContext)) - { - _dynamicInvokeThunks = new Dictionary(); - } - } - - public override bool WillUseMetadataTokenToReferenceMethod(MethodDesc method) - { - return (GetMetadataCategory(method) & MetadataCategory.Description) != 0; - } - - public override bool WillUseMetadataTokenToReferenceField(FieldDesc field) - { - return (GetMetadataCategory(field) & MetadataCategory.Description) != 0; - } - - protected override MetadataCategory GetMetadataCategory(FieldDesc field) - { - MetadataCategory category = 0; - - if (!IsReflectionBlocked(field)) - { - category = MetadataCategory.RuntimeMapping; - - if (_compilationModuleGroup.ContainsType(field.GetTypicalFieldDefinition().OwningType)) - category |= MetadataCategory.Description; - } - - return category; - } - - protected override MetadataCategory GetMetadataCategory(MethodDesc method) - { - MetadataCategory category = 0; - - if (!IsReflectionBlocked(method)) - { - category = MetadataCategory.RuntimeMapping; - - if (_compilationModuleGroup.ContainsType(method.GetTypicalMethodDefinition().OwningType)) - category |= MetadataCategory.Description; - } - - return category; - } - - protected override MetadataCategory GetMetadataCategory(TypeDesc type) - { - MetadataCategory category = 0; - - if (!IsReflectionBlocked(type)) - { - category = MetadataCategory.RuntimeMapping; - - if (_compilationModuleGroup.ContainsType(type.GetTypeDefinition())) - category |= MetadataCategory.Description; - } - - return category; - } - - protected override void ComputeMetadata(NodeFactory factory, - out byte[] metadataBlob, - out List> typeMappings, - out List> methodMappings, - out List> fieldMappings) - { - var transformed = MetadataTransform.Run(new GeneratedTypesAndCodeMetadataPolicy(_blockingPolicy, factory), GetCompilationModulesWithMetadata()); - - // TODO: DeveloperExperienceMode: Use transformed.Transform.HandleType() to generate - // TypeReference records for _typeDefinitionsGenerated that don't have metadata. - // (To be used in MissingMetadataException messages) - - // Generate metadata blob - var writer = new MetadataWriter(); - writer.ScopeDefinitions.AddRange(transformed.Scopes); - var ms = new MemoryStream(); - - // .NET metadata is UTF-16 and UTF-16 contains code points that don't translate to UTF-8. - var noThrowUtf8Encoding = new UTF8Encoding(false, false); - - using (var logWriter = _metadataLogFile != null ? new StreamWriter(File.Open(_metadataLogFile, FileMode.Create, FileAccess.Write, FileShare.Read), noThrowUtf8Encoding) : null) - { - writer.LogWriter = logWriter; - writer.Write(ms); - } - - metadataBlob = ms.ToArray(); - - typeMappings = new List>(); - methodMappings = new List>(); - fieldMappings = new List>(); - - // Generate type definition mappings - foreach (var type in factory.MetadataManager.GetTypesWithEETypes()) - { - MetadataType definition = type.IsTypeDefinition ? type as MetadataType : null; - if (definition == null) - continue; - - MetadataRecord record = transformed.GetTransformedTypeDefinition(definition); - - // Reflection requires that we maintain type identity. Even if we only generated a TypeReference record, - // if there is an EEType for it, we also need a mapping table entry for it. - if (record == null) - record = transformed.GetTransformedTypeReference(definition); - - if (record != null) - typeMappings.Add(new MetadataMapping(definition, writer.GetRecordHandle(record))); - } - - foreach (var method in GetCompiledMethods()) - { - if (method.IsCanonicalMethod(CanonicalFormKind.Specific)) - { - // Canonical methods are not interesting. - continue; - } - - if (IsReflectionBlocked(method.Instantiation) || IsReflectionBlocked(method.OwningType.Instantiation)) - continue; - - MetadataRecord record = transformed.GetTransformedMethodDefinition(method.GetTypicalMethodDefinition()); - - if (record != null) - methodMappings.Add(new MetadataMapping(method, writer.GetRecordHandle(record))); - } - - foreach (var eetypeGenerated in GetTypesWithEETypes()) - { - if (eetypeGenerated.IsGenericDefinition) - continue; - - if (eetypeGenerated.HasInstantiation) - { - // Collapsing of field map entries based on canonicalization, to avoid redundant equivalent entries - - TypeDesc canonicalType = eetypeGenerated.ConvertToCanonForm(CanonicalFormKind.Specific); - if (canonicalType != eetypeGenerated && TypeGeneratesEEType(canonicalType)) - continue; - } - - foreach (FieldDesc field in eetypeGenerated.GetFields()) - { - if (IsReflectionBlocked(field.OwningType.Instantiation)) - continue; - - Field record = transformed.GetTransformedFieldDefinition(field.GetTypicalFieldDefinition()); - if (record != null) - fieldMappings.Add(new MetadataMapping(field, writer.GetRecordHandle(record))); - } - } - } - - /// - /// Is there a reflection invoke stub for a method that is invokable? - /// - public override bool HasReflectionInvokeStubForInvokableMethod(MethodDesc method) - { - Debug.Assert(IsReflectionInvokable(method)); - - if (_dynamicInvokeThunks == null) - return false; - - // Place an upper limit on how many parameters a method can have to still get a static stub. - // From the past experience, methods taking 8000+ parameters get a stub that can hit various limitations - // in the codegen. On Project N, we were limited to 256 parameters because of MDIL limitations. - // We don't have such limitations here, but it's a nice round number. - // Reflection invoke will still work, but will go through the calling convention converter. - - return method.Signature.Length <= 256; - } - - /// - /// Gets a stub that can be used to reflection-invoke a method with a given signature. - /// - public override MethodDesc GetCanonicalReflectionInvokeStub(MethodDesc method) - { - TypeSystemContext context = method.Context; - var sig = method.Signature; - - // Get a generic method that can be used to invoke method with this shape. - MethodDesc thunk; - var lookupSig = new DynamicInvokeMethodSignature(sig); - if (!_dynamicInvokeThunks.TryGetValue(lookupSig, out thunk)) - { - thunk = new DynamicInvokeMethodThunk(_compilationModuleGroup.GeneratedAssembly.GetGlobalModuleType(), lookupSig); - _dynamicInvokeThunks.Add(lookupSig, thunk); - } - - return InstantiateCanonicalDynamicInvokeMethodForMethod(thunk, method); - } - - protected override void GetMetadataDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) - { - dependencies = dependencies ?? new DependencyList(); - dependencies.Add(factory.MethodMetadata(method.GetTypicalMethodDefinition()), "Reflectable method"); - } - - protected override void GetMetadataDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) - { - TypeMetadataNode.GetMetadataDependencies(ref dependencies, factory, type, "Reflectable type"); - } - - private struct GeneratedTypesAndCodeMetadataPolicy : IMetadataPolicy - { - private readonly MetadataBlockingPolicy _blockingPolicy; - private readonly NodeFactory _factory; - private readonly ExplicitScopeAssemblyPolicyMixin _explicitScopeMixin; - - public GeneratedTypesAndCodeMetadataPolicy(MetadataBlockingPolicy blockingPolicy, NodeFactory factory) - { - _blockingPolicy = blockingPolicy; - _factory = factory; - _explicitScopeMixin = new ExplicitScopeAssemblyPolicyMixin(); - } - - public bool GeneratesMetadata(FieldDesc fieldDef) - { - return _factory.FieldMetadata(fieldDef).Marked; - } - - public bool GeneratesMetadata(MethodDesc methodDef) - { - return _factory.MethodMetadata(methodDef).Marked; - } - - public bool GeneratesMetadata(MetadataType typeDef) - { - return _factory.TypeMetadata(typeDef).Marked; - } - - public bool IsBlocked(MetadataType typeDef) - { - return _blockingPolicy.IsBlocked(typeDef); - } - - public bool IsBlocked(MethodDesc methodDef) - { - return _blockingPolicy.IsBlocked(methodDef); - } - - public ModuleDesc GetModuleOfType(MetadataType typeDef) - { - return _explicitScopeMixin.GetModuleOfType(typeDef); - } - } - } -} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilerGeneratedType.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilerGeneratedType.cs index d00064826a..7bbd0aa454 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilerGeneratedType.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilerGeneratedType.cs @@ -77,7 +77,8 @@ namespace ILCompiler return TypeFlags.Class | TypeFlags.HasGenericVarianceComputed | TypeFlags.HasStaticConstructorComputed | - TypeFlags.HasFinalizerComputed; + TypeFlags.HasFinalizerComputed | + TypeFlags.IsByRefLikeComputed; } public override ClassLayoutMetadata GetClassLayout() diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.BoxedTypes.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.BoxedTypes.cs index 70f806bde5..9b8bd363c8 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.BoxedTypes.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.BoxedTypes.cs @@ -89,6 +89,40 @@ namespace ILCompiler return thunk; } + public MethodDesc GetUnboxingThunk(MethodDesc targetMethod, ModuleDesc ownerModuleOfThunk) + { + TypeDesc owningType = targetMethod.OwningType; + Debug.Assert(owningType.IsValueType); + + var owningTypeDefinition = (MetadataType)owningType.GetTypeDefinition(); + + // Get a reference type that has the same layout as the boxed valuetype. + var typeKey = new BoxedValuetypeHashtableKey(owningTypeDefinition, ownerModuleOfThunk); + BoxedValueType boxedTypeDefinition = _boxedValuetypeHashtable.GetOrCreateValue(typeKey); + + // Get a method on the reference type with the same signature as the target method (but different + // calling convention, since 'this' will be a reference type). + var targetMethodDefinition = targetMethod.GetTypicalMethodDefinition(); + var methodKey = new UnboxingThunkHashtableKey(targetMethodDefinition, boxedTypeDefinition); + UnboxingThunk thunkDefinition = _nonGenericUnboxingThunkHashtable.GetOrCreateValue(methodKey); + + // Find the thunk on the instantiated version of the reference type. + if (owningType != owningTypeDefinition) + { + InstantiatedType boxedType = boxedTypeDefinition.MakeInstantiatedType(owningType.Instantiation); + MethodDesc thunk = GetMethodForInstantiatedType(thunkDefinition, boxedType); + //TODO: this might be triggered by a struct that implements an interface with a generic method + Debug.Assert(!thunk.HasInstantiation); + return thunk; + } + else + { + //TODO: this might be triggered by a struct that implements an interface with a generic method + Debug.Assert(!thunkDefinition.HasInstantiation); + return thunkDefinition; + } + } + /// /// Returns true of is a standin method for unboxing thunk target. /// @@ -184,7 +218,34 @@ namespace ILCompiler } } private UnboxingThunkHashtable _unboxingThunkHashtable = new UnboxingThunkHashtable(); - + + private class NonGenericUnboxingThunkHashtable : LockFreeReaderHashtable + { + protected override int GetKeyHashCode(UnboxingThunkHashtableKey key) + { + return key.TargetMethod.GetHashCode(); + } + protected override int GetValueHashCode(UnboxingThunk value) + { + return value.TargetMethod.GetHashCode(); + } + protected override bool CompareKeyToValue(UnboxingThunkHashtableKey key, UnboxingThunk value) + { + return Object.ReferenceEquals(key.TargetMethod, value.TargetMethod) && + Object.ReferenceEquals(key.OwningType, value.OwningType); + } + protected override bool CompareValueToValue(UnboxingThunk value1, UnboxingThunk value2) + { + return Object.ReferenceEquals(value1.TargetMethod, value2.TargetMethod) && + Object.ReferenceEquals(value1.OwningType, value2.OwningType); + } + protected override UnboxingThunk CreateValueFromKey(UnboxingThunkHashtableKey key) + { + return new UnboxingThunk(key.OwningType, key.TargetMethod); + } + } + + private NonGenericUnboxingThunkHashtable _nonGenericUnboxingThunkHashtable = new NonGenericUnboxingThunkHashtable(); /// /// A type with an identical layout to the layout of a boxed value type. @@ -210,6 +271,7 @@ namespace ILCompiler public override bool IsSequentialLayout => true; public override bool IsBeforeFieldInit => false; public override MetadataType MetadataBaseType => (MetadataType)Context.GetWellKnownType(WellKnownType.Object); + public override DefType BaseType => MetadataBaseType; public override bool IsSealed => true; public override bool IsAbstract => false; public override DefType ContainingType => null; @@ -248,11 +310,6 @@ namespace ILCompiler return hashCodeBuilder.ToHashCode(); } - public override string ToString() - { - return "Boxed " + Module.ToString() + ValueTypeRepresented.ToString(); - } - protected override TypeFlags ComputeTypeFlags(TypeFlags mask) { TypeFlags flags = 0; @@ -268,6 +325,7 @@ namespace ILCompiler } flags |= TypeFlags.HasFinalizerComputed; + flags |= TypeFlags.IsByRefLikeComputed; return flags; } @@ -414,6 +472,67 @@ namespace ILCompiler } } + /// + /// Represents a thunk to call instance method on boxed valuetypes. + /// + private partial class UnboxingThunk : ILStubMethod + { + private MethodDesc _targetMethod; + private BoxedValueType _owningType; + + public UnboxingThunk(BoxedValueType owningType, MethodDesc targetMethod) + { + Debug.Assert(targetMethod.OwningType.IsValueType); + Debug.Assert(!targetMethod.Signature.IsStatic); + + _owningType = owningType; + _targetMethod = targetMethod; + } + + public override TypeSystemContext Context => _targetMethod.Context; + + public override TypeDesc OwningType => _owningType; + + public override MethodSignature Signature => _targetMethod.Signature; + + public MethodDesc TargetMethod => _targetMethod; + + public override string Name + { + get + { + return _targetMethod.Name + "_Unbox"; + } + } + + public override MethodIL EmitIL() + { + // Generate the unboxing stub. This loosely corresponds to following C#: + // return BoxedValue.InstanceMethod([rest of parameters]) + + ILEmitter emit = new ILEmitter(); + ILCodeStream codeStream = emit.NewCodeStream(); + + FieldDesc boxedValueField = _owningType.BoxedValue.InstantiateAsOpen(); + + // unbox to get a pointer to the value type + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.ldflda, emit.NewToken(boxedValueField)); + + // Load rest of the arguments + for (int i = 0; i < _targetMethod.Signature.Length; i++) + { + codeStream.EmitLdArg(i + 1); + } + + // Call an instance method on the target valuetype + codeStream.Emit(ILOpcode.call, emit.NewToken(_targetMethod.InstantiateAsOpen())); + codeStream.Emit(ILOpcode.ret); + + return emit.Link(this); + } + } + /// /// Represents an instance method on a generic valuetype with an explicit instantiation parameter in the /// signature. This is so that we can refer to the parameter from IL. References to this method will diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.Sorting.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.Sorting.cs index a81797ef33..46aa35989c 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.Sorting.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.Sorting.cs @@ -40,6 +40,17 @@ namespace ILCompiler } } + partial class UnboxingThunk + { + protected override int ClassCode => 446545583; + + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + var otherMethod = (UnboxingThunk)other; + return comparer.Compare(_targetMethod, otherMethod._targetMethod); + } + } + partial class ValueTypeInstanceMethodWithHiddenParameter { protected override int ClassCode => 2131875345; diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.cs index 7b46dd77ca..b224f558fd 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.cs @@ -22,10 +22,13 @@ namespace ILCompiler { private MetadataFieldLayoutAlgorithm _metadataFieldLayoutAlgorithm = new CompilerMetadataFieldLayoutAlgorithm(); private RuntimeDeterminedFieldLayoutAlgorithm _runtimeDeterminedFieldLayoutAlgorithm = new RuntimeDeterminedFieldLayoutAlgorithm(); + private VectorOfTFieldLayoutAlgorithm _vectorOfTFieldLayoutAlgorithm; private MetadataRuntimeInterfacesAlgorithm _metadataRuntimeInterfacesAlgorithm = new MetadataRuntimeInterfacesAlgorithm(); private ArrayOfTRuntimeInterfacesAlgorithm _arrayOfTRuntimeInterfacesAlgorithm; private MetadataVirtualMethodAlgorithm _virtualMethodAlgorithm = new MetadataVirtualMethodAlgorithm(); + private SimdHelper _simdHelper; + private TypeDesc[] _arrayOfTInterfaces; private MetadataStringDecoder _metadataStringDecoder; @@ -59,7 +62,7 @@ namespace ILCompiler } protected override ModuleData CreateValueFromKey(EcmaModule key) { - Debug.Assert(false, "CreateValueFromKey not supported"); + Debug.Fail("CreateValueFromKey not supported"); return null; } } @@ -87,7 +90,7 @@ namespace ILCompiler } protected override ModuleData CreateValueFromKey(string key) { - Debug.Assert(false, "CreateValueFromKey not supported"); + Debug.Fail("CreateValueFromKey not supported"); return null; } } @@ -99,6 +102,8 @@ namespace ILCompiler : base(details) { _genericsMode = genericsMode; + + _vectorOfTFieldLayoutAlgorithm = new VectorOfTFieldLayoutAlgorithm(_metadataFieldLayoutAlgorithm); } public IReadOnlyDictionary InputFilePaths @@ -128,7 +133,7 @@ namespace ILCompiler return GetModuleForSimpleName(name.Name, throwIfNotFound); } - public EcmaModule GetModuleForSimpleName(string simpleName, bool throwIfNotFound = true) + public ModuleDesc GetModuleForSimpleName(string simpleName, bool throwIfNotFound = true) { ModuleData existing; if (_simpleNameHashtable.TryGetValue(simpleName, out existing)) @@ -139,19 +144,33 @@ namespace ILCompiler { if (!ReferenceFilePaths.TryGetValue(simpleName, out filePath)) { + // We allow the CanonTypesModule to not be an EcmaModule. + if (((IAssemblyDesc)CanonTypesModule).GetName().Name == simpleName) + return CanonTypesModule; + // TODO: the exception is wrong for two reasons: for one, this should be assembly full name, not simple name. // The other reason is that on CoreCLR, the exception also captures the reason. We should be passing two // string IDs. This makes this rather annoying. if (throwIfNotFound) - throw new TypeSystemException.FileNotFoundException(ExceptionStringID.FileLoadErrorGeneric, simpleName); + ThrowHelper.ThrowFileNotFoundException(ExceptionStringID.FileLoadErrorGeneric, simpleName); return null; } } - return AddModule(filePath, simpleName); + return AddModule(filePath, simpleName, true); } public EcmaModule GetModuleFromPath(string filePath) + { + return GetOrAddModuleFromPath(filePath, true); + } + + public EcmaModule GetMetadataOnlyModuleFromPath(string filePath) + { + return GetOrAddModuleFromPath(filePath, false); + } + + private EcmaModule GetOrAddModuleFromPath(string filePath, bool useForBinding) { // This method is not expected to be called frequently. Linear search is acceptable. foreach (var entry in ModuleHashtable.Enumerator.Get(_moduleHashtable)) @@ -160,10 +179,10 @@ namespace ILCompiler return entry.Module; } - return AddModule(filePath, null); + return AddModule(filePath, null, useForBinding); } - private static unsafe PEReader OpenPEFile(string filePath, out MemoryMappedViewAccessor mappedViewAccessor) + public static unsafe PEReader OpenPEFile(string filePath, out MemoryMappedViewAccessor mappedViewAccessor) { // System.Reflection.Metadata has heuristic that tries to save virtual address space. This heuristic does not work // well for us since it can make IL access very slow (call to OS for each method IL query). We will map the file @@ -201,7 +220,7 @@ namespace ILCompiler } } - private EcmaModule AddModule(string filePath, string expectedSimpleName) + private EcmaModule AddModule(string filePath, string expectedSimpleName, bool useForBinding) { MemoryMappedViewAccessor mappedViewAccessor = null; PdbSymbolReader pdbReader = null; @@ -228,12 +247,15 @@ namespace ILCompiler lock (this) { - ModuleData actualModuleData = _simpleNameHashtable.AddOrGetExisting(moduleData); - if (actualModuleData != moduleData) + if (useForBinding) { - if (actualModuleData.FilePath != filePath) - throw new FileNotFoundException("Module with same simple name already exists " + filePath); - return actualModuleData.Module; + ModuleData actualModuleData = _simpleNameHashtable.AddOrGetExisting(moduleData); + if (actualModuleData != moduleData) + { + if (actualModuleData.FilePath != filePath) + throw new FileNotFoundException("Module with same simple name already exists " + filePath); + return actualModuleData.Module; + } } mappedViewAccessor = null; // Ownership has been transfered pdbReader = null; // Ownership has been transferred @@ -258,6 +280,8 @@ namespace ILCompiler return UniversalCanonLayoutAlgorithm.Instance; else if (type.IsRuntimeDeterminedType) return _runtimeDeterminedFieldLayoutAlgorithm; + else if (_simdHelper.IsVectorOfT(type)) + return _vectorOfTFieldLayoutAlgorithm; else return _metadataFieldLayoutAlgorithm; } @@ -325,19 +349,21 @@ namespace ILCompiler return type.GetMethods(); } - private IEnumerable GetAllMethodsForDelegate(TypeDesc type) + protected virtual IEnumerable GetAllMethodsForDelegate(TypeDesc type) { - // Inject the synthetic GetThunk virtual override + // Inject the synthetic methods that support the implementation of the delegate. InstantiatedType instantiatedType = type as InstantiatedType; if (instantiatedType != null) { DelegateInfo info = GetDelegateInfo(type.GetTypeDefinition()); - yield return GetMethodForInstantiatedType(info.GetThunkMethod, instantiatedType); + foreach (MethodDesc syntheticMethod in info.Methods) + yield return GetMethodForInstantiatedType(syntheticMethod, instantiatedType); } else { DelegateInfo info = GetDelegateInfo(type); - yield return info.GetThunkMethod; + foreach (MethodDesc syntheticMethod in info.Methods) + yield return syntheticMethod; } // Append all the methods defined in metadata diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/CoreRTNameMangler.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/CoreRTNameMangler.cs index e2bda0e03c..33a13fc2ed 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/CoreRTNameMangler.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/CoreRTNameMangler.cs @@ -165,6 +165,7 @@ namespace ILCompiler private string EnterNameScopeSequence => _mangleForCplusPlus ? "_A_" : "<"; private string ExitNameScopeSequence => _mangleForCplusPlus ? "_V_" : ">"; + private string DelimitNameScopeSequence => _mangleForCplusPlus? "_C_" : ","; protected string NestMangledName(string name) { @@ -275,17 +276,15 @@ namespace ILCompiler switch (type.Category) { case TypeFlags.Array: + mangledName = "__MDArray" + + EnterNameScopeSequence + + GetMangledTypeName(((ArrayType)type).ElementType) + + DelimitNameScopeSequence + + ((ArrayType)type).Rank.ToStringInvariant() + + ExitNameScopeSequence; + break; case TypeFlags.SzArray: - mangledName = GetMangledTypeName(((ArrayType)type).ElementType) + "__"; - - if (type.IsMdArray) - { - mangledName += NestMangledName("ArrayRank" + ((ArrayType)type).Rank.ToStringInvariant()); - } - else - { - mangledName += NestMangledName("Array"); - } + mangledName = "__Array" + NestMangledName(GetMangledTypeName(((ArrayType)type).ElementType)); break; case TypeFlags.ByRef: mangledName = GetMangledTypeName(((ByRefType)type).ParameterType) + NestMangledName("ByRef"); diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DebugInformationProvider.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DebugInformationProvider.cs new file mode 100644 index 0000000000..9f40963f91 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DebugInformationProvider.cs @@ -0,0 +1,30 @@ +// 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 Internal.IL; + +namespace ILCompiler +{ + /// + /// Provides debug information by delegating to the . + /// + public class DebugInformationProvider + { + public virtual MethodDebugInformation GetDebugInfo(MethodIL methodIL) + { + return methodIL.GetDebugInfo(); + } + } + + /// + /// Provides empty debug information. + /// + public sealed class NullDebugInformationProvider : DebugInformationProvider + { + public override MethodDebugInformation GetDebugInfo(MethodIL methodIL) + { + return MethodDebugInformation.None; + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DelegateCreationInfo.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DelegateCreationInfo.cs index eb69b11465..6fc5afe9f9 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DelegateCreationInfo.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DelegateCreationInfo.cs @@ -96,7 +96,7 @@ namespace ILCompiler return factory.GenericLookup.MethodEntry(TargetMethod, TargetMethodIsUnboxingThunk); case TargetKind.InterfaceDispatch: - return factory.GenericLookup.VirtualMethodAddress(TargetMethod); + return factory.GenericLookup.VirtualDispatchCell(TargetMethod); case TargetKind.MethodHandle: return factory.GenericLookup.MethodHandle(TargetMethod); @@ -129,7 +129,7 @@ namespace ILCompiler return factory.RuntimeMethodHandle(TargetMethod); case TargetKind.VTableLookup: - Debug.Assert(false, "Need to do runtime lookup"); + Debug.Fail("Need to do runtime lookup"); return null; default: @@ -313,5 +313,33 @@ namespace ILCompiler { return Constructor.GetHashCode() ^ TargetMethod.GetHashCode(); } + +#if !SUPPORT_JIT + internal int CompareTo(DelegateCreationInfo other, TypeSystemComparer comparer) + { + var compare = _targetKind - other._targetKind; + if (compare != 0) + return compare; + + compare = comparer.Compare(TargetMethod, other.TargetMethod); + if (compare != 0) + return compare; + + compare = comparer.Compare(Constructor.Method, other.Constructor.Method); + if (compare != 0) + return compare; + + if (Thunk == other.Thunk) + return 0; + + if (Thunk == null) + return -1; + + if (other.Thunk == null) + return 1; + + return comparer.Compare(Thunk.Method, other.Thunk.Method); + } +#endif } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ArrayMapNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ArrayMapNode.cs index 16aaa733db..6b298e7e92 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ArrayMapNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ArrayMapNode.cs @@ -36,6 +36,7 @@ namespace ILCompiler.DependencyAnalysis public override ObjectNodeSection Section => _externalReferences.Section; public override bool StaticDependenciesAreComputed => true; + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection; protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); @@ -51,7 +52,7 @@ namespace ILCompiler.DependencyAnalysis Section hashTableSection = writer.NewSection(); hashTableSection.Place(typeMapHashTable); - foreach (var type in factory.MetadataManager.GetTypesWithEETypes()) + foreach (var type in factory.MetadataManager.GetTypesWithConstructedEETypes()) { if (!type.IsSzArray) continue; @@ -61,8 +62,8 @@ namespace ILCompiler.DependencyAnalysis if (!arrayType.ElementType.IsValueType) continue; - // Go with a necessary type symbol. It will be upgraded to a constructed one if a constructed was emitted. - IEETypeNode arrayTypeSymbol = factory.NecessaryTypeSymbol(arrayType); + // Look at the constructed type symbol. If a constructed type wasn't emitted, then the array map entry isn't valid for use + IEETypeNode arrayTypeSymbol = factory.ConstructedTypeSymbol(arrayType); Vertex vertex = writer.GetUnsignedConstant(_externalReferences.GetIndex(arrayTypeSymbol)); @@ -76,5 +77,8 @@ namespace ILCompiler.DependencyAnalysis return new ObjectData(hashTableBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.ArrayMapNode; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ArrayOfEmbeddedDataNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ArrayOfEmbeddedDataNode.cs index a18c7c6a2d..927710fad0 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ArrayOfEmbeddedDataNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ArrayOfEmbeddedDataNode.cs @@ -11,30 +11,28 @@ using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis { + public interface IHasStartSymbol + { + ObjectAndOffsetSymbolNode StartSymbol { get; } + } + /// /// Represents an array of nodes. The contents of this node will be emitted /// by placing a starting symbol, followed by contents of nodes (optionally /// sorted using provided comparer), followed by ending symbol. /// - public class ArrayOfEmbeddedDataNode : ObjectNode + public class ArrayOfEmbeddedDataNode : EmbeddedDataContainerNode, IHasStartSymbol where TEmbedded : EmbeddedObjectNode { private HashSet _nestedNodes = new HashSet(); private List _nestedNodesList = new List(); - private ObjectAndOffsetSymbolNode _startSymbol; - private ObjectAndOffsetSymbolNode _endSymbol; private IComparer _sorter; - public ArrayOfEmbeddedDataNode(string startSymbolMangledName, string endSymbolMangledName, IComparer nodeSorter) + public ArrayOfEmbeddedDataNode(string startSymbolMangledName, string endSymbolMangledName, IComparer nodeSorter) : base(startSymbolMangledName, endSymbolMangledName) { - _startSymbol = new ObjectAndOffsetSymbolNode(this, 0, startSymbolMangledName, true); - _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, endSymbolMangledName, true); _sorter = nodeSorter; } - - public ObjectAndOffsetSymbolNode StartSymbol => _startSymbol; - public ObjectAndOffsetSymbolNode EndSymbol => _endSymbol; - + public void AddEmbeddedObject(TEmbedded symbol) { lock (_nestedNodes) @@ -43,16 +41,11 @@ namespace ILCompiler.DependencyAnalysis { _nestedNodesList.Add(symbol); } + symbol.ContainingNode = this; } } - public int IndexOfEmbeddedObject(TEmbedded symbol) - { - Debug.Assert(_sorter == null); - return _nestedNodesList.IndexOf(symbol); - } - - protected override string GetName(NodeFactory factory) => $"Region {_startSymbol.GetMangledName(factory.NameMangler)}"; + protected override string GetName(NodeFactory factory) => $"Region {StartSymbol.GetMangledName(factory.NameMangler)}"; public override ObjectNodeSection Section => ObjectNodeSection.DataSection; public override bool IsShareable => false; @@ -63,10 +56,14 @@ namespace ILCompiler.DependencyAnalysis protected virtual void GetElementDataForNodes(ref ObjectDataBuilder builder, NodeFactory factory, bool relocsOnly) { + int index = 0; foreach (TEmbedded node in NodesList) { if (!relocsOnly) + { node.InitializeOffsetFromBeginningOfArray(builder.CountBytes); + node.InitializeIndexFromBeginningOfArray(index++); + } node.EncodeData(ref builder, factory, relocsOnly); if (node is ISymbolDefinitionNode) @@ -84,12 +81,12 @@ namespace ILCompiler.DependencyAnalysis if (_sorter != null) _nestedNodesList.Sort(_sorter); - builder.AddSymbol(_startSymbol); + builder.AddSymbol(StartSymbol); GetElementDataForNodes(ref builder, factory, relocsOnly); - _endSymbol.SetSymbolOffset(builder.CountBytes); - builder.AddSymbol(_endSymbol); + EndSymbol.SetSymbolOffset(builder.CountBytes); + builder.AddSymbol(EndSymbol); ObjectData objData = builder.ToObjectData(); return objData; @@ -108,15 +105,9 @@ namespace ILCompiler.DependencyAnalysis return dependencies; } - } - // TODO: delete this once we review each use of this and put it on the generic plan with the - // right element type - public class ArrayOfEmbeddedDataNode : ArrayOfEmbeddedDataNode - { - public ArrayOfEmbeddedDataNode(string startSymbolMangledName, string endSymbolMangledName, IComparer nodeSorter) - : base(startSymbolMangledName, endSymbolMangledName, nodeSorter) - { - } + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + + protected internal override int ClassCode => (int)ObjectNodeOrder.ArrayOfEmbeddedDataNode; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ArrayOfEmbeddedPointersNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ArrayOfEmbeddedPointersNode.cs index d6d4f38857..45271af23b 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ArrayOfEmbeddedPointersNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ArrayOfEmbeddedPointersNode.cs @@ -14,7 +14,7 @@ namespace ILCompiler.DependencyAnalysis /// of node each pointer within the vector points to. /// public sealed class ArrayOfEmbeddedPointersNode : ArrayOfEmbeddedDataNode> - where TTarget : ISymbolNode + where TTarget : ISortableSymbolNode { private int _nextId; private string _startSymbolMangledName; @@ -44,16 +44,15 @@ namespace ILCompiler.DependencyAnalysis return new EmbeddedPointerIndirectionWithSymbolNode(this, target, GetNextId()); } - public EmbeddedObjectNode NewNodeWithSymbol(TTarget target, OnMarkedDelegate callback) - { - return new EmbeddedPointerIndirectionWithSymbolAndOnMarkedCallbackNode(this, target, GetNextId(), callback); - } - int GetNextId() { return System.Threading.Interlocked.Increment(ref _nextId); } + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + + protected internal override int ClassCode => (int)ObjectNodeOrder.ArrayOfEmbeddedPointersNode; + private class PointerIndirectionNodeComparer : IComparer> { private IComparer _innerComparer; @@ -96,6 +95,8 @@ namespace ILCompiler.DependencyAnalysis new DependencyListEntry(_parentNode, "Pointer region") }; } + + protected internal override int ClassCode => -66002498; } private class EmbeddedPointerIndirectionWithSymbolNode : SimpleEmbeddedPointerIndirectionNode, ISymbolDefinitionNode @@ -113,27 +114,10 @@ namespace ILCompiler.DependencyAnalysis int ISymbolDefinitionNode.Offset => OffsetFromBeginningOfArray; - public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append(nameMangler.CompilationUnitPrefix).Append(_parentNode._startSymbolMangledName).Append("_").Append(_id.ToStringInvariant()); } } - - private class EmbeddedPointerIndirectionWithSymbolAndOnMarkedCallbackNode : EmbeddedPointerIndirectionWithSymbolNode - { - private OnMarkedDelegate _onMarkedCallback; - - public EmbeddedPointerIndirectionWithSymbolAndOnMarkedCallbackNode(ArrayOfEmbeddedPointersNode futureParent, TTarget target, int id, OnMarkedDelegate onMarkedCallback) - : base(futureParent, target, id) - { - _onMarkedCallback = onMarkedCallback; - } - - protected override void OnMarked(NodeFactory factory) - { - base.OnMarked(factory); - _onMarkedCallback(this); - } - } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ArrayOfFrozenObjectsNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ArrayOfFrozenObjectsNode.cs index 705dc4705a..70349bdbf5 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ArrayOfFrozenObjectsNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ArrayOfFrozenObjectsNode.cs @@ -41,5 +41,7 @@ namespace ILCompiler.DependencyAnalysis AlignNextObject(ref builder, factory); builder.EmitZeroPointer(); } + + protected internal override int ClassCode => -1771336339; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/AssemblyStubNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/AssemblyStubNode.cs index 8eb6347f33..db568a3e8c 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/AssemblyStubNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/AssemblyStubNode.cs @@ -50,6 +50,13 @@ namespace ILCompiler.DependencyAnalysis armEmitter.Builder.AddSymbol(this); return armEmitter.Builder.ToObjectData(); + case TargetArchitecture.ARM64: + ARM64.ARM64Emitter arm64Emitter = new ARM64.ARM64Emitter(factory, relocsOnly); + EmitCode(factory, ref arm64Emitter, relocsOnly); + arm64Emitter.Builder.RequireInitialAlignment(factory.Target.MinimumFunctionAlignment); + arm64Emitter.Builder.AddSymbol(this); + return arm64Emitter.Builder.ToObjectData(); + default: throw new NotImplementedException(); } @@ -58,5 +65,6 @@ namespace ILCompiler.DependencyAnalysis protected abstract void EmitCode(NodeFactory factory, ref X64.X64Emitter instructionEncoder, bool relocsOnly); protected abstract void EmitCode(NodeFactory factory, ref X86.X86Emitter instructionEncoder, bool relocsOnly); protected abstract void EmitCode(NodeFactory factory, ref ARM.ARMEmitter instructionEncoder, bool relocsOnly); + protected abstract void EmitCode(NodeFactory factory, ref ARM64.ARM64Emitter instructionEncoder, bool relocsOnly); } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/BlobNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/BlobNode.cs index 1d7afc2e42..5f2b27f5c4 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/BlobNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/BlobNode.cs @@ -5,6 +5,7 @@ using System; using Internal.Text; +using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis { @@ -39,5 +40,14 @@ namespace ILCompiler.DependencyAnalysis } protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + +#if !SUPPORT_JIT + protected internal override int ClassCode => -470351029; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return _name.CompareTo(((BlobNode)other)._name); + } +#endif } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/BlockReflectionTypeMapNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/BlockReflectionTypeMapNode.cs index cb4e63f96f..2955b3d0ed 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/BlockReflectionTypeMapNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/BlockReflectionTypeMapNode.cs @@ -37,6 +37,8 @@ namespace ILCompiler.DependencyAnalysis public override bool StaticDependenciesAreComputed => true; + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection; + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) @@ -81,5 +83,8 @@ namespace ILCompiler.DependencyAnalysis return new ObjectData(hashTableBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.BlockReflectionTypeMapNode; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CallingConventionConverterKey.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CallingConventionConverterKey.cs index 529bb0ddb0..7233edfa7a 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CallingConventionConverterKey.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CallingConventionConverterKey.cs @@ -30,7 +30,7 @@ namespace ILCompiler.DependencyAnalysis if (ConverterKind != other.ConverterKind) return false; - if (Signature.Equals(other.Signature)) + if (!Signature.Equals(other.Signature)) return false; return true; @@ -49,17 +49,33 @@ namespace ILCompiler.DependencyAnalysis public static class MethodSignatureExtensions { + public static void AppendName(this MethodSignature signature, StringBuilder nameBuilder, UniqueTypeNameFormatter typeNameFormatter) + { + if (signature.GenericParameterCount > 0) + { + nameBuilder.Append("GenParams:"); + nameBuilder.Append(signature.GenericParameterCount); + nameBuilder.Append(' '); + } + + if (signature.IsStatic) + nameBuilder.Append("Static "); + + typeNameFormatter.AppendName(nameBuilder, signature.ReturnType); + nameBuilder.Append('('); + for (int i = 0; i < signature.Length; i++) + { + if (i != 0) + nameBuilder.Append(','); + typeNameFormatter.AppendName(nameBuilder, signature[i]); + } + nameBuilder.Append(')'); + } + public static string GetName(this MethodSignature signature) { StringBuilder nameBuilder = new StringBuilder(); - if (signature.GenericParameterCount > 0) - nameBuilder.Append("GenParams:" + signature.GenericParameterCount); - if (signature.IsStatic) - nameBuilder.Append("Static"); - nameBuilder.Append(signature.ReturnType.ToString()); - for (int i = 0; i < signature.Length; i++) - nameBuilder.Append(signature[i].ToString()); - + signature.AppendName(nameBuilder, UniqueTypeNameFormatter.Instance); return nameBuilder.ToString(); } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalDefinitionEETypeNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalDefinitionEETypeNode.cs index 7ebee934c9..520781f538 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalDefinitionEETypeNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalDefinitionEETypeNode.cs @@ -33,17 +33,16 @@ namespace ILCompiler.DependencyAnalysis uint numInstanceFieldBytes = 1 + (uint)factory.Target.PointerSize; uint valueTypeFieldPadding = (uint)(MinimumObjectSize - factory.Target.PointerSize) - numInstanceFieldBytes; - uint valueTypeFieldPaddingEncoded = EETypeBuilderHelpers.ComputeValueTypeFieldPaddingFieldValue(valueTypeFieldPadding, 1); + uint valueTypeFieldPaddingEncoded = EETypeBuilderHelpers.ComputeValueTypeFieldPaddingFieldValue(valueTypeFieldPadding, 1, _type.Context.Target.PointerSize); Debug.Assert(valueTypeFieldPaddingEncoded != 0); _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.ValueTypeFieldPadding, valueTypeFieldPaddingEncoded); } } - protected override void OutputBaseSize(ref ObjectDataBuilder objData) - { - // Canonical definition types will have their base size set to the minimum - objData.EmitInt(MinimumObjectSize); - } + // Canonical definition types will have their base size set to the minimum + protected override int BaseSize => MinimumObjectSize; + + protected internal override int ClassCode => -1851030036; } -} \ No newline at end of file +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs index ae890f6cf8..498e5d36d7 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs @@ -27,11 +27,11 @@ namespace ILCompiler.DependencyAnalysis Debug.Assert(type.IsCanonicalSubtype(CanonicalFormKind.Any)); Debug.Assert(type == type.ConvertToCanonForm(CanonicalFormKind.Specific)); Debug.Assert(!type.IsMdArray); + Debug.Assert(!type.IsByRefLike); } public override bool StaticDependenciesAreComputed => true; public override bool IsShareable => IsTypeNodeShareable(_type); - public override bool HasConditionalStaticDependencies => false; protected override bool EmitVirtualSlotsAndInterfaces => true; public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => false; @@ -49,17 +49,25 @@ namespace ILCompiler.DependencyAnalysis if (_type.RuntimeInterfaces.Length > 0) dependencyList.Add(factory.InterfaceDispatchMap(_type), "Canonical interface dispatch map"); - dependencyList.Add(factory.VTable(_type), "VTable"); + dependencyList.Add(factory.VTable(closestDefType), "VTable"); if (_type.IsCanonicalSubtype(CanonicalFormKind.Universal)) dependencyList.Add(factory.NativeLayout.TemplateTypeLayout(_type), "Universal generic types always have template layout"); + // Track generic virtual methods that will get added to the GVM tables + if (TypeGVMEntriesNode.TypeNeedsGVMTableEntries(_type)) + { + dependencyList.Add(new DependencyListEntry(factory.TypeGVMEntries(_type), "Type with generic virtual methods")); + + AddDependenciesForUniversalGVMSupport(factory, _type, ref dependencyList); + } + return dependencyList; } protected override ISymbolNode GetBaseTypeNode(NodeFactory factory) { - return _type.BaseType != null ? factory.NecessaryTypeSymbol(GetFullCanonicalTypeForCanonicalType(_type.BaseType)) : null; + return _type.BaseType != null ? factory.NecessaryTypeSymbol(_type.NormalizedBaseType()) : null; } protected override int GCDescSize @@ -94,26 +102,24 @@ namespace ILCompiler.DependencyAnalysis } } - protected override void OutputBaseSize(ref ObjectDataBuilder objData) + protected override int BaseSize { - bool emitMinimumObjectSize = false; - - if (_type.IsCanonicalSubtype(CanonicalFormKind.Universal) && _type.IsDefType) + get { - LayoutInt instanceByteCount = ((DefType)_type).InstanceByteCount; - - if (instanceByteCount.IsIndeterminate) + if (_type.IsCanonicalSubtype(CanonicalFormKind.Universal) && _type.IsDefType) { - // For USG types, they may be of indeterminate size, and the size of the type may be meaningless. - // In that case emit a fixed constant. - emitMinimumObjectSize = true; - } - } + LayoutInt instanceByteCount = ((DefType)_type).InstanceByteCount; - if (emitMinimumObjectSize) - objData.EmitInt(MinimumObjectSize); - else - base.OutputBaseSize(ref objData); + if (instanceByteCount.IsIndeterminate) + { + // For USG types, they may be of indeterminate size, and the size of the type may be meaningless. + // In that case emit a fixed constant. + return MinimumObjectSize; + } + } + + return base.BaseSize; + } } protected override void ComputeValueTypeFieldPadding() @@ -129,5 +135,7 @@ namespace ILCompiler.DependencyAnalysis base.ComputeValueTypeFieldPadding(); } + + protected internal override int ClassCode => -1798018602; } -} \ No newline at end of file +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ClassConstructorContextMap.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ClassConstructorContextMap.cs index 0d0ab22066..2ee6b9cf9c 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ClassConstructorContextMap.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ClassConstructorContextMap.cs @@ -36,6 +36,8 @@ namespace ILCompiler.DependencyAnalysis public override bool StaticDependenciesAreComputed => true; + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection; + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) @@ -79,5 +81,8 @@ namespace ILCompiler.DependencyAnalysis return new ObjectData(hashTableBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.ClassConstructorContextMap; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ClonedConstructedEETypeNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ClonedConstructedEETypeNode.cs index 1297fe7ebc..d64319e907 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ClonedConstructedEETypeNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ClonedConstructedEETypeNode.cs @@ -17,10 +17,6 @@ namespace ILCompiler.DependencyAnalysis public override ObjectNode NodeForLinkage(NodeFactory factory) => this; - // Cloned EEType does not have its own interface dispatch map. The runtime dispatching - // routine will use its primary type's dispatch map instead. - protected override bool TrackInterfaceDispatchMapDepenendency => false; - // // A cloned type must be named differently than the type it is a clone of so the linker // will have an unambiguous symbol to resolve @@ -38,5 +34,7 @@ namespace ILCompiler.DependencyAnalysis // objData.EmitPointerReloc(factory.NecessaryTypeSymbol(_type)); } + + protected internal override int ClassCode => -288888778; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CodeBasedDependencyAlgorithm.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CodeBasedDependencyAlgorithm.cs index 48fcdea9e9..be47cef80d 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CodeBasedDependencyAlgorithm.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CodeBasedDependencyAlgorithm.cs @@ -18,18 +18,15 @@ namespace ILCompiler.DependencyAnalysis { public static void AddDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) { - // TODO: https://github.com/dotnet/corert/issues/3224 - // Reflection invoke stub handling is here because in the current reflection model we reflection-enable - // all methods that are compiled. Ideally the list of reflection enabled methods should be known before - // we even start the compilation process (with the invocation stubs being compilation roots like any other). - // The existing model has it's problems: e.g. the invocability of the method depends on inliner decisions. if (factory.MetadataManager.IsReflectionInvokable(method)) { if (dependencies == null) dependencies = new DependencyList(); + dependencies.Add(factory.MaximallyConstructableType(method.OwningType), "Reflection invoke"); + if (factory.MetadataManager.HasReflectionInvokeStubForInvokableMethod(method) - && ((factory.Target.Abi != TargetAbi.ProjectN) || !method.IsCanonicalMethod(CanonicalFormKind.Any))) + && ((factory.Target.Abi != TargetAbi.ProjectN) || ProjectNDependencyBehavior.EnableFullAnalysis || !method.IsCanonicalMethod(CanonicalFormKind.Any))) { MethodDesc canonInvokeStub = factory.MetadataManager.GetCanonicalReflectionInvokeStub(method); if (canonInvokeStub.IsSharedByGenericInstantiations) @@ -42,7 +39,7 @@ namespace ILCompiler.DependencyAnalysis } bool skipUnboxingStubDependency = false; - if (factory.Target.Abi == TargetAbi.ProjectN) + if ((factory.Target.Abi == TargetAbi.ProjectN) && !ProjectNDependencyBehavior.EnableFullAnalysis) { // ProjectN compilation currently computes the presence of these stubs independent from dependency analysis here // TODO: fix that issue and remove this odd treatment of unboxing stubs @@ -51,7 +48,7 @@ namespace ILCompiler.DependencyAnalysis } if (method.OwningType.IsValueType && !method.Signature.IsStatic && !skipUnboxingStubDependency) - dependencies.Add(new DependencyListEntry(factory.MethodEntrypoint(method, unboxingStub: true), "Reflection unboxing stub")); + dependencies.Add(new DependencyListEntry(factory.ExactCallableAddress(method, isUnboxingStub: true), "Reflection unboxing stub")); // If the method is defined in a different module than this one, a metadata token isn't known for performing the reference // Use a name/sig reference instead. @@ -99,49 +96,46 @@ namespace ILCompiler.DependencyAnalysis GenericTypesTemplateMap.GetTemplateTypeDependencies(ref dependencies, factory, owningTemplateType); } - // On Project N, the compiler doesn't generate the interop code on the fly - if (method.Context.Target.Abi != TargetAbi.ProjectN) + factory.InteropStubManager.AddDependeciesDueToPInvoke(ref dependencies, factory, method); + + if (method.IsIntrinsic && factory.Target.Abi != TargetAbi.ProjectN) { - if (method.IsPInvoke) + if (method.OwningType is MetadataType owningType) { - if (dependencies == null) - dependencies = new DependencyList(); + string name = method.Name; - MethodSignature methodSig = method.Signature; - AddPInvokeParameterDependencies(ref dependencies, factory, methodSig.ReturnType); - - for (int i = 0; i < methodSig.Length; i++) + switch (name) { - AddPInvokeParameterDependencies(ref dependencies, factory, methodSig[i]); - } - } - } - } + // The general purpose code in Comparer/EqualityComparer Create method depends on the template + // type loader being able to load the necessary types at runtime. + case "Create": + if (method.IsSharedByGenericInstantiations + && owningType.Module == factory.TypeSystemContext.SystemModule + && owningType.Namespace == "System.Collections.Generic") + { + TypeDesc[] templateDependencies = null; - private static void AddPInvokeParameterDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc parameter) - { - if (parameter.IsDelegate) - { - dependencies.Add(factory.NecessaryTypeSymbol(parameter), "Delegate Marshalling Stub"); + if (owningType.Name == "Comparer`1") + { + templateDependencies = Internal.IL.Stubs.ComparerIntrinsics.GetPotentialComparersForType( + owningType.Instantiation[0]); + } + else if (owningType.Name == "EqualityComparer`1") + { + templateDependencies = Internal.IL.Stubs.ComparerIntrinsics.GetPotentialEqualityComparersForType( + owningType.Instantiation[0]); + } - dependencies.Add(factory.MethodEntrypoint(factory.InteropStubManager.GetOpenStaticDelegateMarshallingStub(parameter)), "Delegate Marshalling Stub"); - dependencies.Add(factory.MethodEntrypoint(factory.InteropStubManager.GetClosedDelegateMarshallingStub(parameter)), "Delegate Marshalling Stub"); - dependencies.Add(factory.MethodEntrypoint(factory.InteropStubManager.GetForwardDelegateCreationStub(parameter)), "Delegate Marshalling Stub"); - } - else if (Internal.TypeSystem.Interop.MarshalHelpers.IsStructMarshallingRequired(parameter)) - { - var stub = (Internal.IL.Stubs.StructMarshallingThunk)factory.InteropStubManager.GetStructMarshallingManagedToNativeStub(parameter); - dependencies.Add(factory.ConstructedTypeSymbol(factory.InteropStubManager.GetStructMarshallingType(parameter)), "Struct Marshalling Type"); - dependencies.Add(factory.MethodEntrypoint(stub), "Struct Marshalling stub"); - dependencies.Add(factory.MethodEntrypoint(factory.InteropStubManager.GetStructMarshallingNativeToManagedStub(parameter)), "Struct Marshalling stub"); - dependencies.Add(factory.MethodEntrypoint(factory.InteropStubManager.GetStructMarshallingCleanupStub(parameter)), "Struct Marshalling stub"); - - foreach (var inlineArrayCandidate in stub.GetInlineArrayCandidates()) - { - dependencies.Add(factory.ConstructedTypeSymbol(factory.InteropStubManager.GetInlineArrayType(inlineArrayCandidate)), "Struct Marshalling Type"); - foreach (var method in inlineArrayCandidate.ElementType.GetMethods()) - { - dependencies.Add(factory.MethodEntrypoint(method), "inline array marshalling stub"); + if (templateDependencies != null) + { + dependencies = dependencies ?? new DependencyList(); + foreach (TypeDesc templateType in templateDependencies) + { + dependencies.Add(factory.NativeLayout.TemplateTypeLayout(templateType), "Generic comparer"); + } + } + } + break; } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CompilerComparer.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CompilerComparer.cs new file mode 100644 index 0000000000..2a564335e1 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CompilerComparer.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. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using System.Collections.Generic; + +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public class CompilerComparer : TypeSystemComparer, IComparer + { + public int Compare(ISortableSymbolNode x, ISortableSymbolNode y) + { + if (x == y) + { + return 0; + } + + int codeX = x.ClassCode; + int codeY = y.ClassCode; + if (codeX == codeY) + { + Debug.Assert(x.GetType() == y.GetType()); + + int result = x.CompareToImpl(y, this); + + // We did a reference equality check above so an "Equal" result is not expected + Debug.Assert(result != 0); + + return result; + } + else + { + Debug.Assert(x.GetType() != y.GetType()); + return codeX > codeY ? -1 : 1; + } + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs index c308ff5264..4bfd1ea7f7 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs @@ -2,12 +2,10 @@ // 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.Collections.Generic; using System.Diagnostics; using Internal.Runtime; -using Internal.Text; using Internal.TypeSystem; using Internal.IL; @@ -31,12 +29,46 @@ namespace ILCompiler.DependencyAnalysis { get { - return _type.IsDefType && _type.HasGenericVirtualMethod(); + if (_type.IsInterface) + return _type.HasGenericVirtualMethods(); + + if (_type.IsDefType) + { + // First, check if this type has any GVM that overrides a GVM on a parent type. If that's the case, this makes + // the current type interesting for GVM analysis (i.e. instantiate its overriding GVMs for existing GVMDependenciesNodes + // of the instantiated GVM on the parent types). + foreach (var method in _type.GetAllMethods()) + { + if (method.HasInstantiation && method.IsVirtual) + { + MethodDesc slotDecl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method); + if (slotDecl != method) + return true; + } + } + + // Second, check if this type has any GVMs that implement any GVM on any of the implemented interfaces. This would + // make the current type interesting for dynamic dependency analysis to that we can instantiate its GVMs. + foreach (DefType interfaceImpl in _type.RuntimeInterfaces) + { + foreach (var method in interfaceImpl.GetAllMethods()) + { + if (method.HasInstantiation && method.IsVirtual) + { + // We found a GVM on one of the implemented interfaces. Find if the type implements this method. + // (Note, do this comparision against the generic definition of the method, not the specific method instantiation + MethodDesc genericDefinition = method.GetMethodDefinition(); + MethodDesc slotDecl = _type.ResolveInterfaceMethodTarget(genericDefinition); + if (slotDecl != null) + return true; + } + } + } + } + return false; } } - protected virtual bool TrackInterfaceDispatchMapDepenendency => true; - protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { DependencyList dependencyList = base.ComputeNonRelocationBasedDependencies(factory); @@ -48,84 +80,11 @@ namespace ILCompiler.DependencyAnalysis DefType closestDefType = _type.GetClosestDefType(); - if (TrackInterfaceDispatchMapDepenendency && _type.RuntimeInterfaces.Length > 0) + if (_type.RuntimeInterfaces.Length > 0) { dependencyList.Add(factory.InterfaceDispatchMap(_type), "Interface dispatch map"); } - if (_type.RuntimeInterfaces.Length > 0 && !factory.CompilationModuleGroup.ShouldProduceFullVTable(_type)) - { - foreach (var implementedInterface in _type.RuntimeInterfaces) - { - // If the type implements ICastable, the methods are implicitly necessary - if (implementedInterface == factory.ICastableInterface) - { - MethodDesc isInstDecl = implementedInterface.GetKnownMethod("IsInstanceOfInterface", null); - MethodDesc getImplTypeDecl = implementedInterface.GetKnownMethod("GetImplType", null); - - MethodDesc isInstMethodImpl = _type.ResolveInterfaceMethodTarget(isInstDecl); - MethodDesc getImplTypeMethodImpl = _type.ResolveInterfaceMethodTarget(getImplTypeDecl); - - if (isInstMethodImpl != null) - dependencyList.Add(factory.VirtualMethodUse(isInstMethodImpl), "ICastable IsInst"); - if (getImplTypeMethodImpl != null) - dependencyList.Add(factory.VirtualMethodUse(getImplTypeMethodImpl), "ICastable GetImplType"); - } - - // If any of the implemented interfaces have variance, calls against compatible interface methods - // could result in interface methods of this type being used (e.g. IEnumberable.GetEnumerator() - // can dispatch to an implementation of IEnumerable.GetEnumerator()). - // For now, we will not try to optimize this and we will pretend all interface methods are necessary. - bool allInterfaceMethodsAreImplicitlyUsed = false; - if (implementedInterface.HasVariance) - { - TypeDesc interfaceDefinition = implementedInterface.GetTypeDefinition(); - for (int i = 0; i < interfaceDefinition.Instantiation.Length; i++) - { - if (((GenericParameterDesc)interfaceDefinition.Instantiation[i]).Variance != 0 && - !implementedInterface.Instantiation[i].IsValueType) - { - allInterfaceMethodsAreImplicitlyUsed = true; - break; - } - } - } - if (!allInterfaceMethodsAreImplicitlyUsed && - (_type.IsArray || _type.GetTypeDefinition() == factory.ArrayOfTEnumeratorType) && - implementedInterface.HasInstantiation) - { - // NOTE: we need to also do this for generic interfaces on arrays because they have a weird casting rule - // that doesn't require the implemented interface to be variant to consider it castable. - // For value types, we only need this when the array is castable by size (int[] and ICollection), - // or it's a reference type (Derived[] and ICollection). - TypeDesc elementType = _type.IsArray ? ((ArrayType)_type).ElementType : _type.Instantiation[0]; - allInterfaceMethodsAreImplicitlyUsed = - CastingHelper.IsArrayElementTypeCastableBySize(elementType) || - (elementType.IsDefType && !elementType.IsValueType); - } - - if (allInterfaceMethodsAreImplicitlyUsed) - { - foreach (var interfaceMethod in implementedInterface.GetAllMethods()) - { - if (interfaceMethod.Signature.IsStatic) - continue; - - // Generic virtual methods are tracked by an orthogonal mechanism. - if (interfaceMethod.HasInstantiation) - continue; - - MethodDesc implMethod = closestDefType.ResolveInterfaceMethodToVirtualMethodOnType(interfaceMethod); - if (implMethod != null) - { - dependencyList.Add(factory.VirtualMethodUse(interfaceMethod), "Variant interface method"); - dependencyList.Add(factory.VirtualMethodUse(implMethod), "Variant interface method"); - } - } - } - } - } - if (_type.IsArray) { // Array EEType depends on System.Array's virtuals. Array EETypes don't point to @@ -141,14 +100,18 @@ namespace ILCompiler.DependencyAnalysis } } - dependencyList.Add(factory.VTable(_type), "VTable"); + dependencyList.Add(factory.VTable(closestDefType), "VTable"); if (closestDefType.HasInstantiation) { TypeDesc canonType = _type.ConvertToCanonForm(CanonicalFormKind.Specific); + TypeDesc canonClosestDefType = closestDefType.ConvertToCanonForm(CanonicalFormKind.Specific); // Add a dependency on the template for this type, if the canonical type should be generated into this binary. - if (canonType.IsCanonicalSubtype(CanonicalFormKind.Any) && !factory.NecessaryTypeSymbol(canonType).RepresentsIndirectionCell) + // If the type is an array type, the check should be on its underlying Array type. This is because a copy of + // an array type gets placed into each module but the Array type only exists in the defining module and only + // one template is needed for the Array type by the dynamic type loader. + if (canonType.IsCanonicalSubtype(CanonicalFormKind.Any) && !factory.NecessaryTypeSymbol(canonClosestDefType).RepresentsIndirectionCell) dependencyList.Add(factory.NativeLayout.TemplateTypeLayout(canonType), "Template Type Layout"); } @@ -156,6 +119,8 @@ namespace ILCompiler.DependencyAnalysis if (TypeGVMEntriesNode.TypeNeedsGVMTableEntries(_type)) { dependencyList.Add(new DependencyListEntry(factory.TypeGVMEntries(_type), "Type with generic virtual methods")); + + AddDependenciesForUniversalGVMSupport(factory, _type, ref dependencyList); } if (factory.TypeSystemContext.HasLazyStaticConstructor(_type)) @@ -168,82 +133,11 @@ namespace ILCompiler.DependencyAnalysis // Ask the metadata manager if we have any dependencies due to reflectability. factory.MetadataManager.GetDependenciesDueToReflectability(ref dependencyList, factory, _type); + factory.InteropStubManager.AddInterestingInteropConstructedTypeDependencies(ref dependencyList, factory, _type); + return dependencyList; } - public override bool HasConditionalStaticDependencies - { - get - { - // Since the vtable is dependency driven, generate conditional static dependencies for - // all possible vtable entries - foreach (var method in _type.GetClosestDefType().GetAllMethods()) - { - if (method.IsVirtual) - return true; - } - - // If the type implements at least one interface, calls against that interface could result in this type's - // implementation being used. - if (_type.RuntimeInterfaces.Length > 0) - return true; - - return false; - } - } - - public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) - { - DefType defType = _type.GetClosestDefType(); - - // If we're producing a full vtable, none of the dependencies are conditional. - if (!factory.CompilationModuleGroup.ShouldProduceFullVTable(defType)) - { - foreach (MethodDesc decl in defType.EnumAllVirtualSlots()) - { - // Generic virtual methods are tracked by an orthogonal mechanism. - if (decl.HasInstantiation) - continue; - - MethodDesc impl = defType.FindVirtualFunctionTargetMethodOnObjectType(decl); - if (impl.OwningType == defType && !impl.IsAbstract) - { - MethodDesc canonImpl = impl.GetCanonMethodTarget(CanonicalFormKind.Specific); - yield return new CombinedDependencyListEntry(factory.MethodEntrypoint(canonImpl, _type.IsValueType), factory.VirtualMethodUse(decl), "Virtual method"); - } - } - - Debug.Assert( - _type == defType || - ((System.Collections.IStructuralEquatable)defType.RuntimeInterfaces).Equals(_type.RuntimeInterfaces, - EqualityComparer.Default)); - - // Add conditional dependencies for interface methods the type implements. For example, if the type T implements - // interface IFoo which has a method M1, add a dependency on T.M1 dependent on IFoo.M1 being called, since it's - // possible for any IFoo object to actually be an instance of T. - foreach (DefType interfaceType in defType.RuntimeInterfaces) - { - Debug.Assert(interfaceType.IsInterface); - - foreach (MethodDesc interfaceMethod in interfaceType.GetAllMethods()) - { - if (interfaceMethod.Signature.IsStatic) - continue; - - // Generic virtual methods are tracked by an orthogonal mechanism. - if (interfaceMethod.HasInstantiation) - continue; - - MethodDesc implMethod = defType.ResolveInterfaceMethodToVirtualMethodOnType(interfaceMethod); - if (implMethod != null) - { - yield return new CombinedDependencyListEntry(factory.VirtualMethodUse(implMethod), factory.VirtualMethodUse(interfaceMethod), "Interface method"); - } - } - } - } - } - protected override ISymbolNode GetBaseTypeNode(NodeFactory factory) { return _type.BaseType != null ? factory.ConstructedTypeSymbol(_type.BaseType) : null; @@ -281,12 +175,12 @@ namespace ILCompiler.DependencyAnalysis if (type.IsGenericDefinition) return false; - // Full EEtype of System.Canon should never be used. + // Full EEType of System.Canon should never be used. if (type.IsCanonicalDefinitionType(CanonicalFormKind.Any)) return false; // Byref-like types have interior pointers and cannot be heap allocated. - if (type.IsValueType && ((DefType)type).IsByRefLike) + if (type.IsByRefLike) return false; // The global "" type can never be allocated. @@ -302,7 +196,9 @@ namespace ILCompiler.DependencyAnalysis public static void CheckCanGenerateConstructedEEType(NodeFactory factory, TypeDesc type) { if (!CreationAllowed(type)) - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } + + protected internal override int ClassCode => 590142654; } -} \ No newline at end of file +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs index 3495f9e7b1..9f2cae5608 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs @@ -23,7 +23,16 @@ namespace ILCompiler.DependencyAnalysis { MetadataReader reader = method.MetadataReader; MethodDefinition methodDef = reader.GetMethodDefinition(method.Handle); + + // Handle custom attributes on the method AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, methodDef.GetCustomAttributes()); + + // Handle custom attributes on method parameters + foreach (ParameterHandle parameterHandle in methodDef.GetParameters()) + { + Parameter parameter = reader.GetParameter(parameterHandle); + AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, parameter.GetCustomAttributes()); + } } public static void AddDependenciesDueToCustomAttributes(ref DependencyList dependencies, NodeFactory factory, EcmaType type) @@ -174,6 +183,14 @@ namespace ILCompiler.DependencyAnalysis private static bool AddDependenciesFromCustomAttributeArgument(DependencyList dependencies, NodeFactory factory, TypeDesc type, object value) { + // If this is an initializer that refers to e.g. a blocked enum, we can't encode this attribute. + if (factory.MetadataManager.IsReflectionBlocked(type)) + return false; + + // Reflection will need to be able to allocate this type at runtime + // (e.g. this could be an array that needs to be allocated, or an enum that needs to be boxed). + dependencies.Add(factory.ConstructedTypeSymbol(type), "Custom attribute blob"); + if (type.UnderlyingType.IsPrimitive || type.IsString || value == null) return true; @@ -201,7 +218,11 @@ namespace ILCompiler.DependencyAnalysis if (factory.MetadataManager.IsReflectionBlocked(typeofType)) return false; - TypeMetadataNode.GetMetadataDependencies(ref dependencies, factory, typeofType, "Custom attribute blob"); + // We go for the entire EEType because the reflection stack at runtime might need an EEType anyway + // (e.g. if this is a `typeof(SomeType[,,])` just having the metadata for SomeType is not enough). + // Theoretically, we only need the EEType if this is a constructed type, but let's go for the full + // EEType for the sake of consistency. + dependencies.Add(factory.MaximallyConstructableType(typeofType), "Custom attribute blob"); return true; } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DefaultConstructorMapNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DefaultConstructorMapNode.cs new file mode 100644 index 0000000000..9f6735b880 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DefaultConstructorMapNode.cs @@ -0,0 +1,122 @@ +// 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.Diagnostics; +using System.Collections.Generic; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.NativeFormat; + +using ILCompiler.DependencyAnalysisFramework; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Dependency analysis node used to keep track of types needing an entry in the default + /// constructor map hashtable + /// + internal sealed class DefaultConstructorFromLazyNode : DependencyNodeCore + { + public TypeDesc TypeNeedingDefaultCtor { get; } + + public DefaultConstructorFromLazyNode(TypeDesc type) + { + Debug.Assert(!type.IsRuntimeDeterminedSubtype); + Debug.Assert(type == type.ConvertToCanonForm(CanonicalFormKind.Specific)); + Debug.Assert(type.GetDefaultConstructor() != null && !type.IsValueType); + + TypeNeedingDefaultCtor = type; + } + + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool StaticDependenciesAreComputed => true; + protected override string GetName(NodeFactory factory) => "__DefaultConstructorFromLazyNode_" + factory.NameMangler.GetMangledTypeName(TypeNeedingDefaultCtor); + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + yield return new DependencyListEntry( + context.MaximallyConstructableType(TypeNeedingDefaultCtor), + "DefaultConstructorNode type"); + + yield return new DependencyListEntry( + context.MethodEntrypoint(TypeNeedingDefaultCtor.GetDefaultConstructor(), TypeNeedingDefaultCtor.IsValueType), + "DefaultConstructorNode"); + } + } + + /// + /// DefaultConstructorMap blob, containing information on default constructor entrypoints of all types used + /// by lazy generic instantiations. + /// + internal class DefaultConstructorMapNode : ObjectNode, ISymbolDefinitionNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + + public DefaultConstructorMapNode(ExternalReferencesTableNode externalReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__DefaultConstructor_Map_End", true); + _externalReferences = externalReferences; + } + + public ISymbolNode EndSymbol => _endSymbol; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__DefaultConstructor_Map"); + } + + public int Offset => 0; + public override bool IsShareable => false; + public override ObjectNodeSection Section => _externalReferences.Section; + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => false; + public override bool StaticDependenciesAreComputed => true; + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.DefaultConstructorMapNode; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + var writer = new NativeWriter(); + var defaultConstructorHashtable = new VertexHashtable(); + + Section defaultConstructorHashtableSection = writer.NewSection(); + defaultConstructorHashtableSection.Place(defaultConstructorHashtable); + + foreach (var ctorNeeded in factory.MetadataManager.GetDefaultConstructorsNeeded()) + { + MethodDesc defaultCtor = ctorNeeded.TypeNeedingDefaultCtor.GetDefaultConstructor(); + Debug.Assert(defaultCtor != null); + + ISymbolNode typeNode = factory.NecessaryTypeSymbol(ctorNeeded.TypeNeedingDefaultCtor); + ISymbolNode defaultCtorNode = factory.MethodEntrypoint(defaultCtor, false); + + Vertex vertex = writer.GetTuple( + writer.GetUnsignedConstant(_externalReferences.GetIndex(typeNode)), + writer.GetUnsignedConstant(_externalReferences.GetIndex(defaultCtorNode))); + + int hashCode = ctorNeeded.TypeNeedingDefaultCtor.GetHashCode(); + defaultConstructorHashtable.Append((uint)hashCode, defaultConstructorHashtableSection.Place(vertex)); + } + + byte[] hashTableBytes = writer.Save(); + + _endSymbol.SetSymbolOffset(hashTableBytes.Length); + + return new ObjectData(hashTableBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DelegateMarshallingStubMapNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DelegateMarshallingStubMapNode.cs index 86143aca86..11318ad2f7 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DelegateMarshallingStubMapNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DelegateMarshallingStubMapNode.cs @@ -6,6 +6,7 @@ using System; using Internal.NativeFormat; using Internal.Text; +using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis { @@ -50,7 +51,7 @@ namespace ILCompiler.DependencyAnalysis Section hashTableSection = writer.NewSection(); hashTableSection.Place(typeMapHashTable); - foreach (var delegateEntry in factory.InteropStubManager.GetDelegateMarshallingThunks()) + foreach (var delegateEntry in ((CompilerGeneratedInteropStubManager)factory.InteropStubManager).GetDelegateMarshallingThunks()) { var delegateType = delegateEntry.DelegateType; Vertex thunks= writer.GetTuple( @@ -74,5 +75,8 @@ namespace ILCompiler.DependencyAnalysis return new ObjectData(hashTableBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.DelegateMarshallingStubMapNode; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DictionaryLayoutNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DictionaryLayoutNode.cs index fc46936636..5dbf7af844 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DictionaryLayoutNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DictionaryLayoutNode.cs @@ -22,7 +22,231 @@ namespace ILCompiler.DependencyAnalysis /// are runtime-determined - the concrete dependency depends on the generic context the canonical /// entity is instantiated with. /// - public class DictionaryLayoutNode : DependencyNodeCore + public abstract class DictionaryLayoutNode : DependencyNodeCore + { + private readonly TypeSystemEntity _owningMethodOrType; + + public DictionaryLayoutNode(TypeSystemEntity owningMethodOrType) + { + _owningMethodOrType = owningMethodOrType; + Validate(); + } + + [Conditional("DEBUG")] + private void Validate() + { + TypeDesc type = _owningMethodOrType as TypeDesc; + if (type != null) + { + Debug.Assert(type.IsCanonicalSubtype(CanonicalFormKind.Any)); + Debug.Assert(type.IsDefType); + } + else + { + MethodDesc method = _owningMethodOrType as MethodDesc; + Debug.Assert(method != null && method.IsSharedByGenericInstantiations); + } + } + + public virtual ObjectNodeSection DictionarySection(NodeFactory factory) + { + if (factory.Target.IsWindows) + { + if (_owningMethodOrType is TypeDesc) + { + return ObjectNodeSection.FoldableReadOnlyDataSection; + } + else + { + // Method dictionary serves as an identity at runtime which means they are not foldable. + Debug.Assert(_owningMethodOrType is MethodDesc); + return ObjectNodeSection.ReadOnlyDataSection; + } + } + else + { + return ObjectNodeSection.DataSection; + } + } + + /// + /// Ensure that a generic lookup result can be resolved. Used to add new lookups to a dictionary which HasUnfixedSlots + /// Must not be used after any calls to GetSlotForEntry + /// + public abstract void EnsureEntry(GenericLookupResult entry); + + /// + /// Get a slot index for a given entry. Slot indices are never expected to change once given out. + /// + public abstract int GetSlotForEntry(GenericLookupResult entry); + + /// + /// Get the slot for an entry which is fixed already. Otherwise return -1 + /// + /// + /// + public virtual int GetSlotForFixedEntry(GenericLookupResult entry) + { + return GetSlotForEntry(entry); + } + + public abstract IEnumerable Entries + { + get; + } + + public virtual IEnumerable FixedEntries => Entries; + + public TypeSystemEntity OwningMethodOrType => _owningMethodOrType; + + /// + /// Gets a value indicating whether the slot assignment is determined at the node creation time. + /// + public abstract bool HasFixedSlots + { + get; + } + + /// + /// Gets a value indicating if this dictionary may have non fixed slots + /// + public virtual bool HasUnfixedSlots => !HasFixedSlots; + + public virtual ICollection GetTemplateEntries(NodeFactory factory) + { + ArrayBuilder templateEntries = new ArrayBuilder(); + foreach (var entry in Entries) + { + templateEntries.Add(entry.TemplateDictionaryNode(factory)); + } + + return templateEntries.ToArray(); + } + + public virtual void EmitDictionaryData(ref ObjectDataBuilder builder, NodeFactory factory, GenericDictionaryNode dictionary, bool fixedLayoutOnly) + { + var context = new GenericLookupResultContext(dictionary.OwningEntity, dictionary.TypeInstantiation, dictionary.MethodInstantiation); + + IEnumerable entriesToEmit = fixedLayoutOnly ? FixedEntries : Entries; + + foreach (GenericLookupResult lookupResult in entriesToEmit) + { +#if DEBUG + int offsetBefore = builder.CountBytes; +#endif + + lookupResult.EmitDictionaryEntry(ref builder, factory, context, dictionary); + +#if DEBUG + Debug.Assert(builder.CountBytes - offsetBefore == factory.Target.PointerSize); +#endif + } + } + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + // Root the template for the type. In the future, we may want to control this via type reflectability instead. + if (_owningMethodOrType is MethodDesc) + { + yield return new DependencyListEntry(factory.NativeLayout.TemplateMethodLayout((MethodDesc)_owningMethodOrType), "Type loader template"); + } + else + { + yield return new DependencyListEntry(factory.NativeLayout.TemplateTypeLayout((TypeDesc) _owningMethodOrType), "Type loader template"); + } + + if (HasFixedSlots) + { + foreach (GenericLookupResult lookupResult in FixedEntries) + { + foreach (DependencyNodeCore dependency in lookupResult.NonRelocDependenciesFromUsage(factory)) + { + yield return new DependencyListEntry(dependency, "GenericLookupResultDependency"); + } + } + } + } + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) + { + Debug.Assert(HasFixedSlots); + + NativeLayoutSavedVertexNode templateLayout; + if (_owningMethodOrType is MethodDesc) + { + templateLayout = factory.NativeLayout.TemplateMethodLayout((MethodDesc)_owningMethodOrType); + } + else + { + templateLayout = factory.NativeLayout.TemplateTypeLayout((TypeDesc)_owningMethodOrType); + } + + List conditionalDependencies = new List(); + + foreach (var lookupSignature in FixedEntries) + { + conditionalDependencies.Add(new CombinedDependencyListEntry(lookupSignature.TemplateDictionaryNode(factory), + templateLayout, + "Type loader template")); + } + + return conditionalDependencies; + } + + protected override string GetName(NodeFactory factory) => $"Dictionary layout for {_owningMethodOrType.ToString()}"; + + public override bool HasConditionalStaticDependencies => HasFixedSlots; + public override bool HasDynamicDependencies => false; + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool StaticDependenciesAreComputed => true; + + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + } + + public class PrecomputedDictionaryLayoutNode : DictionaryLayoutNode + { + private readonly GenericLookupResult[] _layout; + + public override bool HasFixedSlots => true; + + public PrecomputedDictionaryLayoutNode(TypeSystemEntity owningMethodOrType, IEnumerable layout) + : base(owningMethodOrType) + { + ArrayBuilder l = new ArrayBuilder(); + foreach (var entry in layout) + l.Add(entry); + + _layout = l.ToArray(); + } + + public override void EnsureEntry(GenericLookupResult entry) + { + int index = Array.IndexOf(_layout, entry); + + if (index == -1) + { + // Using EnsureEntry to add a slot to a PrecomputedDictionaryLayoutNode is not supported + throw new NotSupportedException(); + } + } + + public override int GetSlotForEntry(GenericLookupResult entry) + { + int index = Array.IndexOf(_layout, entry); + Debug.Assert(index >= 0); + return index; + } + + public override IEnumerable Entries + { + get + { + return _layout; + } + } + } + + public sealed class LazilyBuiltDictionaryLayoutNode : DictionaryLayoutNode { class EntryHashTable : LockFreeReaderHashtable { @@ -33,17 +257,17 @@ namespace ILCompiler.DependencyAnalysis protected override int GetValueHashCode(GenericLookupResult value) => value.GetHashCode(); } - private TypeSystemEntity _owningMethodOrType; private EntryHashTable _entries = new EntryHashTable(); private volatile GenericLookupResult[] _layout; - public DictionaryLayoutNode(TypeSystemEntity owningMethodOrType) + public override bool HasFixedSlots => false; + + public LazilyBuiltDictionaryLayoutNode(TypeSystemEntity owningMethodOrType) + : base(owningMethodOrType) { - _owningMethodOrType = owningMethodOrType; - Validate(); } - public void EnsureEntry(GenericLookupResult entry) + public override void EnsureEntry(GenericLookupResult entry) { Debug.Assert(_layout == null, "Trying to add entry but layout already computed"); _entries.AddOrGetExisting(entry); @@ -65,7 +289,7 @@ namespace ILCompiler.DependencyAnalysis _layout = layout; } - public virtual int GetSlotForEntry(GenericLookupResult entry) + public override int GetSlotForEntry(GenericLookupResult entry) { if (_layout == null) ComputeLayout(); @@ -75,7 +299,7 @@ namespace ILCompiler.DependencyAnalysis return index; } - public virtual IEnumerable Entries + public override IEnumerable Entries { get { @@ -85,62 +309,5 @@ namespace ILCompiler.DependencyAnalysis return _layout; } } - - public virtual ICollection GetTemplateEntries(NodeFactory factory) - { - if (_layout == null) - ComputeLayout(); - - ArrayBuilder templateEntries = new ArrayBuilder(); - for (int i = 0; i < _layout.Length; i++) - { - templateEntries.Add(_layout[i].TemplateDictionaryNode(factory)); - } - - return templateEntries.ToArray(); - } - - [Conditional("DEBUG")] - private void Validate() - { - TypeDesc type = _owningMethodOrType as TypeDesc; - if (type != null) - { - Debug.Assert(type.IsCanonicalSubtype(CanonicalFormKind.Any)); - Debug.Assert(type.IsDefType); - } - else - { - MethodDesc method = _owningMethodOrType as MethodDesc; - Debug.Assert(method != null && method.IsSharedByGenericInstantiations); - } - } - - public virtual void EmitDictionaryData(ref ObjectDataBuilder builder, NodeFactory factory, GenericDictionaryNode dictionary) - { - foreach (GenericLookupResult lookupResult in Entries) - { -#if DEBUG - int offsetBefore = builder.CountBytes; -#endif - - lookupResult.EmitDictionaryEntry(ref builder, factory, dictionary.TypeInstantiation, dictionary.MethodInstantiation, dictionary); - -#if DEBUG - Debug.Assert(builder.CountBytes - offsetBefore == factory.Target.PointerSize); -#endif - } - } - - protected override string GetName(NodeFactory factory) => $"Dictionary layout for {_owningMethodOrType.ToString()}"; - - public override bool HasConditionalStaticDependencies => false; - public override bool HasDynamicDependencies => false; - public override bool InterestingForDynamicDependencyAnalysis => false; - public override bool StaticDependenciesAreComputed => true; - - public override IEnumerable GetStaticDependencies(NodeFactory factory) => null; - public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; - public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DynamicInvokeTemplateDataNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DynamicInvokeTemplateDataNode.cs index f914be78b6..d18e2eb590 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DynamicInvokeTemplateDataNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DynamicInvokeTemplateDataNode.cs @@ -122,5 +122,8 @@ namespace ILCompiler.DependencyAnalysis return objData.ToObjectData(); } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.DynamicInvokeTemplateDataNode; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs index bab3051cb8..53eec543b6 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs @@ -95,7 +95,7 @@ namespace ILCompiler.DependencyAnalysis return (ObjectNode)factory.NecessaryTypeSymbol(_type); } - public virtual bool IsExported(NodeFactory factory) => factory.CompilationModuleGroup.ExportsType(Type); + public ExportForm GetExportForm(NodeFactory factory) => factory.CompilationModuleGroup.GetExportTypeForm(Type); public TypeDesc Type => _type; @@ -119,18 +119,13 @@ namespace ILCompiler.DependencyAnalysis get { return _optionalFieldsBuilder.IsAtLeastOneFieldUsed(); } } - internal byte[] GetOptionalFieldsData(NodeFactory factory) + internal byte[] GetOptionalFieldsData() { return _optionalFieldsBuilder.GetBytes(); } public override bool StaticDependenciesAreComputed => true; - - public void SetDispatchMapIndex(int index) - { - _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.DispatchMap, checked((uint)index)); - } - + public static string GetMangledName(TypeDesc type, NameMangler nameMangler) { return nameMangler.NodeMangler.EEType(type); @@ -146,11 +141,236 @@ namespace ILCompiler.DependencyAnalysis public override bool IsShareable => IsTypeNodeShareable(_type); + private bool CanonFormTypeMayExist + { + get + { + if (!_type.HasInstantiation) + return false; + + if (!_type.Context.SupportsCanon) + return false; + + // If type is already in canon form, a canonically equivalent type cannot exist + if (_type.IsCanonicalSubtype(CanonicalFormKind.Any)) + return false; + + // If we reach here, a universal canon variant can exist (if universal canon is supported) + if (_type.Context.SupportsUniversalCanon) + return true; + + // Attempt to convert to canon. If the type changes, then the CanonForm exists + return (_type.ConvertToCanonForm(CanonicalFormKind.Specific) != _type); + } + } + + public sealed override bool HasConditionalStaticDependencies + { + get + { + // If the type is can be converted to some interesting canon type, and this is the non-constructed variant of an EEType + // we may need to trigger the fully constructed type to exist to make the behavior of the type consistent + // in reflection and generic template expansion scenarios + if (CanonFormTypeMayExist && ProjectNDependencyBehavior.EnableFullAnalysis) + { + return true; + } + + if (!EmitVirtualSlotsAndInterfaces) + return false; + + // Since the vtable is dependency driven, generate conditional static dependencies for + // all possible vtable entries + foreach (var method in _type.GetClosestDefType().GetAllMethods()) + { + if (method.IsVirtual) + return true; + } + + // If the type implements at least one interface, calls against that interface could result in this type's + // implementation being used. + if (_type.RuntimeInterfaces.Length > 0) + return true; + + return false; + } + } + + public sealed override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) + { + IEETypeNode maximallyConstructableType = factory.MaximallyConstructableType(_type); + + if (maximallyConstructableType != this) + { + // EEType upgrading from necessary to constructed if some template instantation exists that matches up + if (CanonFormTypeMayExist) + { + yield return new CombinedDependencyListEntry(maximallyConstructableType, factory.MaximallyConstructableType(_type.ConvertToCanonForm(CanonicalFormKind.Specific)), "Trigger full type generation if canonical form exists"); + + if (_type.Context.SupportsUniversalCanon) + yield return new CombinedDependencyListEntry(maximallyConstructableType, factory.MaximallyConstructableType(_type.ConvertToCanonForm(CanonicalFormKind.Universal)), "Trigger full type generation if universal canonical form exists"); + } + yield break; + } + + if (!EmitVirtualSlotsAndInterfaces) + yield break; + + DefType defType = _type.GetClosestDefType(); + + // If we're producing a full vtable, none of the dependencies are conditional. + if (!factory.VTable(defType).HasFixedSlots) + { + foreach (MethodDesc decl in defType.EnumAllVirtualSlots()) + { + // Generic virtual methods are tracked by an orthogonal mechanism. + if (decl.HasInstantiation) + continue; + + MethodDesc impl = defType.FindVirtualFunctionTargetMethodOnObjectType(decl); + if (impl.OwningType == defType && !impl.IsAbstract) + { + MethodDesc canonImpl = impl.GetCanonMethodTarget(CanonicalFormKind.Specific); + yield return new CombinedDependencyListEntry(factory.MethodEntrypoint(canonImpl, _type.IsValueType), factory.VirtualMethodUse(decl.Normalize()), "Virtual method"); + } + } + + Debug.Assert( + _type == defType || + ((System.Collections.IStructuralEquatable)defType.RuntimeInterfaces).Equals(_type.RuntimeInterfaces, + EqualityComparer.Default)); + + // Add conditional dependencies for interface methods the type implements. For example, if the type T implements + // interface IFoo which has a method M1, add a dependency on T.M1 dependent on IFoo.M1 being called, since it's + // possible for any IFoo object to actually be an instance of T. + foreach (DefType interfaceType in defType.NormalizedRuntimeInterfaces()) + { + Debug.Assert(interfaceType.IsInterface); + + foreach (MethodDesc interfaceMethod in interfaceType.GetAllMethods()) + { + if (interfaceMethod.Signature.IsStatic) + continue; + + // Generic virtual methods are tracked by an orthogonal mechanism. + if (interfaceMethod.HasInstantiation) + continue; + + MethodDesc implMethod = defType.ResolveInterfaceMethodToVirtualMethodOnType(interfaceMethod); + if (implMethod != null) + { + yield return new CombinedDependencyListEntry(factory.VirtualMethodUse(implMethod), factory.VirtualMethodUse(interfaceMethod), "Interface method"); + } + } + } + } + } + public static bool IsTypeNodeShareable(TypeDesc type) { return type.IsParameterizedType || type.IsFunctionPointer || type is InstantiatedType; } + private void AddVirtualMethodUseDependencies(DependencyList dependencyList, NodeFactory factory) + { + DefType closestDefType = _type.GetClosestDefType(); + + if (_type.RuntimeInterfaces.Length > 0 && !factory.VTable(closestDefType).HasFixedSlots) + { + foreach (var implementedInterface in _type.NormalizedRuntimeInterfaces()) + { + // If the type implements ICastable, the methods are implicitly necessary + if (implementedInterface == factory.ICastableInterface) + { + MethodDesc isInstDecl = implementedInterface.GetKnownMethod("IsInstanceOfInterface", null); + MethodDesc getImplTypeDecl = implementedInterface.GetKnownMethod("GetImplType", null); + + MethodDesc isInstMethodImpl = _type.ResolveInterfaceMethodTarget(isInstDecl); + MethodDesc getImplTypeMethodImpl = _type.ResolveInterfaceMethodTarget(getImplTypeDecl); + + if (isInstMethodImpl != null) + dependencyList.Add(factory.VirtualMethodUse(isInstMethodImpl), "ICastable IsInst"); + if (getImplTypeMethodImpl != null) + dependencyList.Add(factory.VirtualMethodUse(getImplTypeMethodImpl), "ICastable GetImplType"); + } + + // If any of the implemented interfaces have variance, calls against compatible interface methods + // could result in interface methods of this type being used (e.g. IEnumberable.GetEnumerator() + // can dispatch to an implementation of IEnumerable.GetEnumerator()). + // For now, we will not try to optimize this and we will pretend all interface methods are necessary. + bool allInterfaceMethodsAreImplicitlyUsed = false; + if (implementedInterface.HasVariance) + { + TypeDesc interfaceDefinition = implementedInterface.GetTypeDefinition(); + for (int i = 0; i < interfaceDefinition.Instantiation.Length; i++) + { + if (((GenericParameterDesc)interfaceDefinition.Instantiation[i]).Variance != 0 && + !implementedInterface.Instantiation[i].IsValueType) + { + allInterfaceMethodsAreImplicitlyUsed = true; + break; + } + } + } + if (!allInterfaceMethodsAreImplicitlyUsed && + (_type.IsArray || _type.GetTypeDefinition() == factory.ArrayOfTEnumeratorType) && + implementedInterface.HasInstantiation) + { + // NOTE: we need to also do this for generic interfaces on arrays because they have a weird casting rule + // that doesn't require the implemented interface to be variant to consider it castable. + // For value types, we only need this when the array is castable by size (int[] and ICollection), + // or it's a reference type (Derived[] and ICollection). + TypeDesc elementType = _type.IsArray ? ((ArrayType)_type).ElementType : _type.Instantiation[0]; + allInterfaceMethodsAreImplicitlyUsed = + CastingHelper.IsArrayElementTypeCastableBySize(elementType) || + (elementType.IsDefType && !elementType.IsValueType); + } + + if (allInterfaceMethodsAreImplicitlyUsed) + { + foreach (var interfaceMethod in implementedInterface.GetAllMethods()) + { + if (interfaceMethod.Signature.IsStatic) + continue; + + // Generic virtual methods are tracked by an orthogonal mechanism. + if (interfaceMethod.HasInstantiation) + continue; + + MethodDesc implMethod = closestDefType.ResolveInterfaceMethodToVirtualMethodOnType(interfaceMethod); + if (implMethod != null) + { + dependencyList.Add(factory.VirtualMethodUse(interfaceMethod), "Variant interface method"); + dependencyList.Add(factory.VirtualMethodUse(implMethod), "Variant interface method"); + } + } + } + } + } + } + + internal static bool MethodHasNonGenericILMethodBody(MethodDesc method) + { + // Generic methods have their own generic dictionaries + if (method.HasInstantiation) + return false; + + // Abstract methods don't have a body + if (method.IsAbstract) + return false; + + // PInvoke methods, runtime imports, etc. are not permitted on generic types, + // but let's not crash the compilation because of that. + if (method.IsPInvoke || method.IsRuntimeImplemented) + return false; + + // InternalCall functions do not really have entrypoints that need to be handled here + if (method.IsInternalCall) + return false; + + return true; + } + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { DependencyList dependencies = new DependencyList(); @@ -162,7 +382,37 @@ namespace ILCompiler.DependencyAnalysis dependencies.Add(new DependencyListEntry(_optionalFieldsNode, "Optional fields")); StaticsInfoHashtableNode.AddStaticsInfoDependencies(ref dependencies, factory, _type); - ReflectionFieldMapNode.AddReflectionFieldMapEntryDependencies(ref dependencies, factory, _type); + + if (EmitVirtualSlotsAndInterfaces) + { + AddVirtualMethodUseDependencies(dependencies, factory); + } + + if (factory.CompilationModuleGroup.PresenceOfEETypeImpliesAllMethodsOnType(_type)) + { + if (_type.IsArray || _type.IsDefType) + { + // If the compilation group wants this type to be fully promoted, ensure that all non-generic methods of the + // type are generated. + // This may be done for several reasons: + // - The EEType may be going to be COMDAT folded with other EETypes generated in a different object file + // This means their generic dictionaries need to have identical contents. The only way to achieve that is + // by generating the entries for all methods that contribute to the dictionary, and sorting the dictionaries. + // - The generic type may be imported into another module, in which case the generic dictionary imported + // must represent all of the methods, as the set of used methods cannot be known at compile time + // - As a matter of policy, the type and its methods may be exported for use in another module. The policy + // may wish to specify that if a type is to be placed into a shared module, all of the methods associated with + // it should be also be exported. + foreach (var method in _type.GetClosestDefType().ConvertToCanonForm(CanonicalFormKind.Specific).GetAllMethods()) + { + if (!MethodHasNonGenericILMethodBody(method)) + continue; + + dependencies.Add(factory.MethodEntrypoint(method.GetCanonMethodTarget(CanonicalFormKind.Specific)), + "Ensure all methods on type due to CompilationModuleGroup policy"); + } + } + } return dependencies; } @@ -178,7 +428,7 @@ namespace ILCompiler.DependencyAnalysis OutputGCDesc(ref objData); OutputComponentSize(ref objData); OutputFlags(factory, ref objData); - OutputBaseSize(ref objData); + objData.EmitInt(BaseSize); OutputRelatedType(factory, ref objData); // Number of vtable slots will be only known later. Reseve the bytes for it. @@ -195,7 +445,7 @@ namespace ILCompiler.DependencyAnalysis // Emit VTable Debug.Assert(objData.CountBytes - ((ISymbolDefinitionNode)this).Offset == GetVTableOffset(objData.TargetPointerSize)); SlotCounter virtualSlotCounter = SlotCounter.BeginCounting(ref /* readonly */ objData); - OutputVirtualSlots(factory, ref objData, _type, _type, relocsOnly); + OutputVirtualSlots(factory, ref objData, _type, _type, _type, relocsOnly); // Update slot count int numberOfVtableSlots = virtualSlotCounter.CountSlots(ref /* readonly */ objData); @@ -286,6 +536,18 @@ namespace ILCompiler.DependencyAnalysis flags |= (UInt16)EETypeFlags.GenericVarianceFlag; } + if (!(this is CanonicalDefinitionEETypeNode)) + { + foreach (DefType itf in _type.RuntimeInterfaces) + { + if (itf == factory.ICastableInterface) + { + flags |= (UInt16)EETypeFlags.ICastableFlag; + break; + } + } + } + ISymbolNode relatedTypeNode = GetRelatedTypeNode(factory); // If the related type (base type / array element type / pointee type) is not part of this compilation group, and @@ -301,84 +563,74 @@ namespace ILCompiler.DependencyAnalysis flags |= (UInt16)EETypeFlags.OptionalFieldsFlag; } + if (this is ClonedConstructedEETypeNode) + { + flags |= (UInt16)EETypeKind.ClonedEEType; + } + objData.EmitShort((short)flags); } - protected virtual void OutputBaseSize(ref ObjectDataBuilder objData) + protected virtual int BaseSize { - int pointerSize = _type.Context.Target.PointerSize; - int objectSize; - - if (_type.IsDefType) + get { - LayoutInt instanceByteCount = ((DefType)_type).InstanceByteCount; + int pointerSize = _type.Context.Target.PointerSize; + int objectSize; - if (instanceByteCount.IsIndeterminate) + if (_type.IsDefType) { - // Some value must be put in, but the specific value doesn't matter as it - // isn't used for specific instantiations, and the universal canon eetype - // is never associated with an allocated object. - objectSize = pointerSize; + LayoutInt instanceByteCount = ((DefType)_type).InstanceByteCount; + + if (instanceByteCount.IsIndeterminate) + { + // Some value must be put in, but the specific value doesn't matter as it + // isn't used for specific instantiations, and the universal canon eetype + // is never associated with an allocated object. + objectSize = pointerSize; + } + else + { + objectSize = pointerSize + + ((DefType)_type).InstanceByteCount.AsInt; // +pointerSize for SyncBlock + } + + if (_type.IsValueType) + objectSize += pointerSize; // + EETypePtr field inherited from System.Object + } + else if (_type.IsArray) + { + objectSize = 3 * pointerSize; // SyncBlock + EETypePtr + Length + if (_type.IsMdArray) + objectSize += + 2 * sizeof(int) * ((ArrayType)_type).Rank; + } + else if (_type.IsPointer) + { + // These never get boxed and don't have a base size. Use a sentinel value recognized by the runtime. + return ParameterizedTypeShapeConstants.Pointer; + } + else if (_type.IsByRef) + { + // These never get boxed and don't have a base size. Use a sentinel value recognized by the runtime. + return ParameterizedTypeShapeConstants.ByRef; } else + throw new NotImplementedException(); + + objectSize = AlignmentHelper.AlignUp(objectSize, pointerSize); + objectSize = Math.Max(MinimumObjectSize, objectSize); + + if (_type.IsString) { - objectSize = pointerSize + - ((DefType)_type).InstanceByteCount.AsInt; // +pointerSize for SyncBlock + // If this is a string, throw away objectSize we computed so far. Strings are special. + // SyncBlock + EETypePtr + length + firstChar + objectSize = 2 * pointerSize + + sizeof(int) + + StringComponentSize.Value; } - if (_type.IsValueType) - objectSize += pointerSize; // + EETypePtr field inherited from System.Object - } - else if (_type.IsArray) - { - objectSize = 3 * pointerSize; // SyncBlock + EETypePtr + Length - if (_type.IsMdArray) - objectSize += - 2 * sizeof(int) * ((ArrayType)_type).Rank; - } - else if (_type.IsPointer) - { - // These never get boxed and don't have a base size. Use a sentinel value recognized by the runtime. - objData.EmitInt(ParameterizedTypeShapeConstants.Pointer); - return; - } - else if (_type.IsByRef) - { - // These never get boxed and don't have a base size. Use a sentinel value recognized by the runtime. - objData.EmitInt(ParameterizedTypeShapeConstants.ByRef); - return; - } - else - throw new NotImplementedException(); - - objectSize = AlignmentHelper.AlignUp(objectSize, pointerSize); - objectSize = Math.Max(MinimumObjectSize, objectSize); - - if (_type.IsString) - { - // If this is a string, throw away objectSize we computed so far. Strings are special. - // SyncBlock + EETypePtr + length + firstChar - objectSize = 2 * pointerSize + - sizeof(int) + - sizeof(char); - } - - objData.EmitInt(objectSize); - } - - protected static TypeDesc GetFullCanonicalTypeForCanonicalType(TypeDesc type) - { - if (type.IsCanonicalSubtype(CanonicalFormKind.Specific)) - { - return type.ConvertToCanonForm(CanonicalFormKind.Specific); - } - else if (type.IsCanonicalSubtype(CanonicalFormKind.Universal)) - { - return type.ConvertToCanonForm(CanonicalFormKind.Universal); - } - else - { - return type; + return objectSize; } } @@ -422,25 +674,51 @@ namespace ILCompiler.DependencyAnalysis } } - protected virtual void OutputVirtualSlots(NodeFactory factory, ref ObjectDataBuilder objData, TypeDesc implType, TypeDesc declType, bool relocsOnly) + protected virtual void OutputVirtualSlots(NodeFactory factory, ref ObjectDataBuilder objData, TypeDesc implType, TypeDesc declType, TypeDesc templateType, bool relocsOnly) { Debug.Assert(EmitVirtualSlotsAndInterfaces); declType = declType.GetClosestDefType(); + templateType = templateType.ConvertToCanonForm(CanonicalFormKind.Specific); - var baseType = declType.BaseType; + var baseType = declType.NormalizedBaseType(); if (baseType != null) - OutputVirtualSlots(factory, ref objData, implType, baseType, relocsOnly); + { + Debug.Assert(templateType.BaseType != null); + OutputVirtualSlots(factory, ref objData, implType, baseType, templateType.BaseType, relocsOnly); + } + + // + // In the universal canonical types case, we could have base types in the hierarchy that are partial universal canonical types. + // The presence of these types could cause incorrect vtable layouts, so we need to fully canonicalize them and walk the + // hierarchy of the template type of the original input type to detect these cases. + // + // Exmaple: we begin with Derived<__UniversalCanon> and walk the template hierarchy: + // + // class Derived : Middle { } // -> Template is Derived<__UniversalCanon> and needs a dictionary slot + // // -> Basetype tempalte is Middle<__UniversalCanon, MyStruct>. It's a partial + // Universal canonical type, so we need to fully canonicalize it. + // + // class Middle : Base { } // -> Template is Middle<__UniversalCanon, __UniversalCanon> and needs a dictionary slot + // // -> Basetype template is Base<__UniversalCanon> + // + // class Base { } // -> Template is Base<__UniversalCanon> and needs a dictionary slot. + // + // If we had not fully canonicalized the Middle class template, we would have ended up with Base, which does not need + // a dictionary slot, meaning we would have created a vtable layout that the runtime does not expect. + // // The generic dictionary pointer occupies the first slot of each type vtable slice - if (declType.HasGenericDictionarySlot()) + if (declType.HasGenericDictionarySlot() || templateType.HasGenericDictionarySlot()) { // All generic interface types have a dictionary slot, but only some of them have an actual dictionary. bool isInterfaceWithAnEmptySlot = declType.IsInterface && declType.ConvertToCanonForm(CanonicalFormKind.Specific) == declType; // Note: Canonical type instantiations always have a generic dictionary vtable slot, but it's empty + // Note: If the current EETypeNode represents a universal canonical type, any dictionary slot must be empty if (declType.IsCanonicalSubtype(CanonicalFormKind.Any) + || implType.IsCanonicalSubtype(CanonicalFormKind.Universal) || factory.LazyGenericsPolicy.UsesLazyGenerics(declType) || isInterfaceWithAnEmptySlot) objData.EmitZeroPointer(); @@ -450,7 +728,7 @@ namespace ILCompiler.DependencyAnalysis // It's only okay to touch the actual list of slots if we're in the final emission phase // or the vtable is not built lazily. - if (relocsOnly && !factory.CompilationModuleGroup.ShouldProduceFullVTable(declType)) + if (relocsOnly && !factory.VTable(declType).HasFixedSlots) return; // Actual vtable slots follow @@ -549,6 +827,11 @@ namespace ILCompiler.DependencyAnalysis /// protected internal virtual void ComputeOptionalEETypeFields(NodeFactory factory, bool relocsOnly) { + if (!relocsOnly && _type.RuntimeInterfaces.Length > 0 && factory.InterfaceDispatchMap(_type).Marked) + { + _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.DispatchMap, checked((uint)factory.InterfaceDispatchMapIndirection(Type).IndexFromBeginningOfArray)); + } + ComputeRareFlags(factory); ComputeNullableValueOffset(); if (!relocsOnly) @@ -588,21 +871,12 @@ namespace ILCompiler.DependencyAnalysis flags |= (uint)EETypeRareFlags.IsHFAFlag; } - foreach (DefType itf in _type.RuntimeInterfaces) - { - if (itf == factory.ICastableInterface) - { - flags |= (uint)EETypeRareFlags.ICastableFlag; - break; - } - } - if (metadataType != null && !_type.IsInterface && metadataType.IsAbstract) { flags |= (uint)EETypeRareFlags.IsAbstractClassFlag; } - if (metadataType != null && metadataType.IsByRefLike) + if (_type.IsByRefLike) { flags |= (uint)EETypeRareFlags.IsByRefLikeFlag; } @@ -682,12 +956,33 @@ namespace ILCompiler.DependencyAnalysis if (defType.InstanceByteCount.IsIndeterminate) { - valueTypeFieldPaddingEncoded = EETypeBuilderHelpers.ComputeValueTypeFieldPaddingFieldValue(0, 1); + valueTypeFieldPaddingEncoded = EETypeBuilderHelpers.ComputeValueTypeFieldPaddingFieldValue(0, 1, _type.Context.Target.PointerSize); } else { - uint valueTypeFieldPadding = checked((uint)(defType.InstanceByteCount.AsInt - defType.InstanceByteCountUnaligned.AsInt)); - valueTypeFieldPaddingEncoded = EETypeBuilderHelpers.ComputeValueTypeFieldPaddingFieldValue(valueTypeFieldPadding, (uint)defType.InstanceFieldAlignment.AsInt); + int numInstanceFieldBytes = defType.InstanceByteCountUnaligned.AsInt; + + // Check if we have a type derived from System.ValueType or System.Enum, but not System.Enum itself + if (defType.IsValueType) + { + // Value types should have at least 1 byte of size + Debug.Assert(numInstanceFieldBytes >= 1); + + // The size doesn't currently include the EEType pointer size. We need to add this so that + // the number of instance field bytes consistently represents the boxed size. + numInstanceFieldBytes += _type.Context.Target.PointerSize; + } + + // For unboxing to work correctly and for supporting dynamic type loading for derived types we need + // to record the actual size of the fields of a type without any padding for GC heap allocation (since + // we can unbox into locals or arrays where this padding is not used, and because field layout for derived + // types is effected by the unaligned base size). We don't want to store this information for all EETypes + // since it's only relevant for value types, and derivable types so it's added as an optional field. It's + // also enough to simply store the size of the padding (between 0 and 4 or 8 bytes for 32-bit and 0 and 8 or 16 bytes + // for 64-bit) which cuts down our storage requirements. + + uint valueTypeFieldPadding = checked((uint)((BaseSize - _type.Context.Target.PointerSize) - numInstanceFieldBytes)); + valueTypeFieldPaddingEncoded = EETypeBuilderHelpers.ComputeValueTypeFieldPaddingFieldValue(valueTypeFieldPadding, (uint)defType.InstanceFieldAlignment.AsInt, _type.Context.Target.PointerSize); } if (valueTypeFieldPaddingEncoded != 0) @@ -722,18 +1017,18 @@ namespace ILCompiler.DependencyAnalysis } // It must be possible to create an EEType for the base type of this type - TypeDesc baseType = type.BaseType; + TypeDesc baseType = type.NormalizedBaseType(); if (baseType != null) { // Make sure EEType can be created for this. - factory.NecessaryTypeSymbol(GetFullCanonicalTypeForCanonicalType(baseType)); + factory.NecessaryTypeSymbol(baseType); } // We need EETypes for interfaces - foreach (var intf in type.RuntimeInterfaces) + foreach (var intf in type.NormalizedRuntimeInterfaces()) { // Make sure EEType can be created for this. - factory.NecessaryTypeSymbol(GetFullCanonicalTypeForCanonicalType(intf)); + factory.NecessaryTypeSymbol(intf); } // Validate classes, structs, enums, interfaces, and delegates @@ -756,7 +1051,7 @@ namespace ILCompiler.DependencyAnalysis // TODO: it might be more resonable for the type system to enforce this (also for methods) if (defType.Instantiation.Length != defType.GetTypeDefinition().Instantiation.Length) { - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } foreach (TypeDesc typeArg in defType.Instantiation) @@ -766,9 +1061,9 @@ namespace ILCompiler.DependencyAnalysis || typeArg.IsPointer || typeArg.IsFunctionPointer || typeArg.IsVoid - || (typeArg.IsValueType && ((DefType)typeArg).IsByRefLike)) + || typeArg.IsByRefLike) { - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } // TODO: validate constraints @@ -792,25 +1087,25 @@ namespace ILCompiler.DependencyAnalysis if (parameterType.IsFunctionPointer) { // Arrays of function pointers are not currently supported - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } LayoutInt elementSize = parameterType.GetElementSize(); if (!elementSize.IsIndeterminate && elementSize.AsInt >= ushort.MaxValue) { // Element size over 64k can't be encoded in the GCDesc - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadValueClassTooLarge, parameterType); + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadValueClassTooLarge, parameterType); } if (((ArrayType)parameterizedType).Rank > 32) { - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadRankTooLarge, type); + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadRankTooLarge, type); } - if ((parameterType.IsDefType) && ((DefType)parameterType).IsByRefLike) + if (parameterType.IsByRefLike) { // Arrays of byref-like types are not allowed - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } } @@ -819,7 +1114,7 @@ namespace ILCompiler.DependencyAnalysis { // CLR compat note: "ldtoken int32&&" will actually fail with a message about int32&; "ldtoken int32&[]" // will fail with a message about being unable to create an array of int32&. This is a middle ground. - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } // It might seem reasonable to disallow array of void, but the CLR doesn't prevent that too hard. @@ -829,10 +1124,80 @@ namespace ILCompiler.DependencyAnalysis // Function pointer EETypes are not currently supported if (type.IsFunctionPointer) { - throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } } + public static void AddDependenciesForStaticsNode(NodeFactory factory, TypeDesc type, ref DependencyList dependencies) + { + if ((factory.Target.Abi == TargetAbi.ProjectN) && !ProjectNDependencyBehavior.EnableFullAnalysis) + return; + + // To ensure that the behvior of FieldInfo.GetValue/SetValue remains correct, + // if a type may be reflectable, and it is generic, if a canonical instantiation of reflection + // can exist which can refer to the associated type of this static base, ensure that type + // has an EEType. (Which will allow the static field lookup logic to find the right type) + if (type.HasInstantiation && factory.MetadataManager.SupportsReflection && !factory.MetadataManager.IsReflectionBlocked(type)) + { + // This current implementation is slightly generous, as it does not attempt to restrict + // the created types to the maximum extent by investigating reflection data and such. Here we just + // check if we support use of a canonically equivalent type to perform reflection. + // We don't check to see if reflection is enabled on the type. + if (factory.TypeSystemContext.SupportsUniversalCanon + || (factory.TypeSystemContext.SupportsCanon && (type != type.ConvertToCanonForm(CanonicalFormKind.Specific)))) + { + if (dependencies == null) + dependencies = new DependencyList(); + + dependencies.Add(factory.NecessaryTypeSymbol(type), "Static block owning type is necessary for canonically equivalent reflection"); + } + } + } + + protected static void AddDependenciesForUniversalGVMSupport(NodeFactory factory, TypeDesc type, ref DependencyList dependencies) + { + if (factory.TypeSystemContext.SupportsUniversalCanon) + { + if ((factory.Target.Abi == TargetAbi.ProjectN) && !ProjectNDependencyBehavior.EnableFullAnalysis) + return; + + foreach (MethodDesc method in type.GetMethods()) + { + if (!method.IsVirtual || !method.HasInstantiation) + continue; + + if (method.IsAbstract) + continue; + + TypeDesc[] universalCanonArray = new TypeDesc[method.Instantiation.Length]; + for (int i = 0; i < universalCanonArray.Length; i++) + universalCanonArray[i] = factory.TypeSystemContext.UniversalCanonType; + + MethodDesc universalCanonMethodNonCanonicalized = method.MakeInstantiatedMethod(new Instantiation(universalCanonArray)); + MethodDesc universalCanonGVMMethod = universalCanonMethodNonCanonicalized.GetCanonMethodTarget(CanonicalFormKind.Universal); + + if (dependencies == null) + dependencies = new DependencyList(); + + dependencies.Add(new DependencyListEntry(factory.MethodEntrypoint(universalCanonGVMMethod), "USG GVM Method")); + } + } + } + + protected internal override int ClassCode => 1521789141; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return comparer.Compare(_type, ((EETypeNode)other)._type); + } + + int ISortableSymbolNode.ClassCode => ClassCode; + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + return CompareToImpl((ObjectNode)other, comparer); + } + private struct SlotCounter { private int _startBytes; diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeOptionalFieldsNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeOptionalFieldsNode.cs index 1cf45d9143..0cc9ef2056 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeOptionalFieldsNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeOptionalFieldsNode.cs @@ -2,7 +2,11 @@ // 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.Diagnostics; + using Internal.Text; +using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis { @@ -53,10 +57,17 @@ namespace ILCompiler.DependencyAnalysis if (!relocsOnly) { _owner.ComputeOptionalEETypeFields(factory, relocsOnly: false); - objData.EmitBytes(_owner.GetOptionalFieldsData(factory)); + objData.EmitBytes(_owner.GetOptionalFieldsData()); } return objData.ToObjectData(); } + + protected internal override int ClassCode => 821718028; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return SortableDependencyNode.CompareImpl(_owner, ((EETypeOptionalFieldsNode)other)._owner, comparer); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EmbeddedDataContainerNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EmbeddedDataContainerNode.cs new file mode 100644 index 0000000000..bc9d9975de --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EmbeddedDataContainerNode.cs @@ -0,0 +1,36 @@ +// 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 Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a node that contains a set of embedded objects. The main function is + /// to serve as a base class, providing symbol name boundaries and node ordering. + /// + public abstract class EmbeddedDataContainerNode : ObjectNode + { + private ObjectAndOffsetSymbolNode _startSymbol; + private ObjectAndOffsetSymbolNode _endSymbol; + private string _startSymbolMangledName; + + public ObjectAndOffsetSymbolNode StartSymbol => _startSymbol; + public ObjectAndOffsetSymbolNode EndSymbol => _endSymbol; + + protected EmbeddedDataContainerNode(string startSymbolMangledName, string endSymbolMangledName) + { + _startSymbolMangledName = startSymbolMangledName; + _startSymbol = new ObjectAndOffsetSymbolNode(this, 0, startSymbolMangledName, true); + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, endSymbolMangledName, true); + } + + protected internal override int ClassCode => -1410622237; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return _startSymbolMangledName.CompareTo(((EmbeddedDataContainerNode)other)._startSymbolMangledName); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EmbeddedObjectNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EmbeddedObjectNode.cs index 220ae23eb4..3ea5ebf833 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EmbeddedObjectNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EmbeddedObjectNode.cs @@ -10,18 +10,22 @@ using Debug = System.Diagnostics.Debug; namespace ILCompiler.DependencyAnalysis { - public abstract class EmbeddedObjectNode : DependencyNodeCore + public abstract class EmbeddedObjectNode : SortableDependencyNode { private const int InvalidOffset = int.MinValue; private int _offset; + private int _index; + + public IHasStartSymbol ContainingNode { get; set; } public EmbeddedObjectNode() { _offset = InvalidOffset; + _index = InvalidOffset; } - protected int OffsetFromBeginningOfArray + public int OffsetFromBeginningOfArray { get { @@ -30,12 +34,27 @@ namespace ILCompiler.DependencyAnalysis } } + public int IndexFromBeginningOfArray + { + get + { + Debug.Assert(_index != InvalidOffset); + return _index; + } + } + internal void InitializeOffsetFromBeginningOfArray(int offset) { Debug.Assert(_offset == InvalidOffset || _offset == offset); _offset = offset; } + internal void InitializeIndexFromBeginningOfArray(int index) + { + Debug.Assert(_index == InvalidOffset || _index == index); + _index = index; + } + public virtual bool IsShareable => false; public virtual bool RepresentsIndirectionCell => false; diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EmbeddedPointerIndirectionNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EmbeddedPointerIndirectionNode.cs index 64af3f2441..766a737068 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EmbeddedPointerIndirectionNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EmbeddedPointerIndirectionNode.cs @@ -5,14 +5,16 @@ using System; using System.Collections.Generic; +using Internal.Text; + namespace ILCompiler.DependencyAnalysis { /// /// An whose sole value is a pointer to a different . /// represents the node type this pointer points to. /// - public abstract class EmbeddedPointerIndirectionNode : EmbeddedObjectNode - where TTarget : ISymbolNode + public abstract class EmbeddedPointerIndirectionNode : EmbeddedObjectNode, ISortableSymbolNode + where TTarget : ISortableSymbolNode { private TTarget _targetNode; @@ -36,5 +38,20 @@ namespace ILCompiler.DependencyAnalysis // At minimum, Target needs to be reported as a static dependency by inheritors. public abstract override IEnumerable GetStaticDependencies(NodeFactory factory); + + int ISymbolNode.Offset => 0; + + public virtual void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("_embedded_ptr_"); + Target.AppendMangledName(nameMangler, sb); + } + + int ISortableSymbolNode.ClassCode => -2055384490; + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + return comparer.Compare(_targetNode, ((EmbeddedPointerIndirectionNode)other)._targetNode); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs index ddfe2e5693..57313c7a98 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs @@ -36,6 +36,7 @@ namespace ILCompiler.DependencyAnalysis public override ObjectNodeSection Section => _externalReferences.Section; public override bool StaticDependenciesAreComputed => true; protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection; public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { @@ -110,6 +111,9 @@ namespace ILCompiler.DependencyAnalysis if (!IsMethodEligibleForTracking(method)) return; + if (!factory.MetadataManager.SupportsReflection) + return; + dependencies = dependencies ?? new DependencyList(); // Method entry point dependency @@ -147,5 +151,8 @@ namespace ILCompiler.DependencyAnalysis return true; } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.ExactMethodInstantiationsNode; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExternSymbolNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExternSymbolNode.cs index 2e3de600df..f35a57d1c4 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExternSymbolNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExternSymbolNode.cs @@ -2,6 +2,7 @@ // 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.Collections.Generic; using ILCompiler.DependencyAnalysisFramework; @@ -13,7 +14,7 @@ namespace ILCompiler.DependencyAnalysis /// /// Represents a symbol that is defined externally and statically linked to the output obj file. /// - public class ExternSymbolNode : DependencyNodeCore, ISymbolNode + public class ExternSymbolNode : DependencyNodeCore, ISortableSymbolNode { private Utf8String _name; @@ -39,5 +40,14 @@ namespace ILCompiler.DependencyAnalysis public override IEnumerable GetStaticDependencies(NodeFactory factory) => null; public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + +#if !SUPPORT_JIT + int ISortableSymbolNode.ClassCode => 1092559304; + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + return _name.CompareTo(((ExternSymbolNode)other)._name); + } +#endif } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExternSymbolsImportedNodeProvider.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExternSymbolsImportedNodeProvider.cs new file mode 100644 index 0000000000..6da656edcb --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExternSymbolsImportedNodeProvider.cs @@ -0,0 +1,55 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + public class ExternSymbolsImportedNodeProvider : ImportedNodeProvider + { + public override IEETypeNode ImportedEETypeNode(NodeFactory factory, TypeDesc type) + { + return new ExternEETypeSymbolNode(factory, type); + } + + public override ISortableSymbolNode ImportedGCStaticNode(NodeFactory factory, MetadataType type) + { + return new ExternSymbolNode(GCStaticsNode.GetMangledName(type, factory.NameMangler)); + } + + public override ISortableSymbolNode ImportedNonGCStaticNode(NodeFactory factory, MetadataType type) + { + return new ExternSymbolNode(NonGCStaticsNode.GetMangledName(type, factory.NameMangler)); + } + + public override ISortableSymbolNode ImportedThreadStaticOffsetNode(NodeFactory factory, MetadataType type) + { + return new ExternSymbolNode(ThreadStaticsOffsetNode.GetMangledName(factory.NameMangler, type)); + } + + public override ISortableSymbolNode ImportedThreadStaticIndexNode(NodeFactory factory, MetadataType type) + { + return factory.ExternSymbol(ThreadStaticsIndexNode.GetMangledName((factory.NameMangler as UTCNameMangler).GetImportedTlsIndexPrefix())); + } + + public override ISortableSymbolNode ImportedTypeDictionaryNode(NodeFactory factory, TypeDesc type) + { + return new ImportedTypeGenericDictionaryNode(factory, type); + } + + public override ISortableSymbolNode ImportedMethodDictionaryNode(NodeFactory factory, MethodDesc method) + { + return new ImportedMethodGenericDictionaryNode(factory, method); + } + + public override IMethodNode ImportedMethodCodeNode(NodeFactory factory, MethodDesc method, bool unboxingStub) + { + return new ExternMethodSymbolNode(factory, method, unboxingStub); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExternSymbolsWithIndirectionImportedNodeProvider.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExternSymbolsWithIndirectionImportedNodeProvider.cs new file mode 100644 index 0000000000..497563ac7f --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExternSymbolsWithIndirectionImportedNodeProvider.cs @@ -0,0 +1,55 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + public class ExternSymbolsWithIndirectionImportedNodeProvider : ImportedNodeProvider + { + public override IEETypeNode ImportedEETypeNode(NodeFactory factory, TypeDesc type) + { + return new ImportedEETypeSymbolNode(factory, type); + } + + public override ISortableSymbolNode ImportedGCStaticNode(NodeFactory factory, MetadataType type) + { + return new ImportedGCStaticsNode(factory, type); + } + + public override ISortableSymbolNode ImportedNonGCStaticNode(NodeFactory factory, MetadataType type) + { + return new ImportedNonGCStaticsNode(factory, type); + } + + public override ISortableSymbolNode ImportedThreadStaticOffsetNode(NodeFactory factory, MetadataType type) + { + return new ImportedThreadStaticsOffsetNode(type, factory); + } + + public override ISortableSymbolNode ImportedThreadStaticIndexNode(NodeFactory factory, MetadataType type) + { + return new ImportedThreadStaticsIndexNode(factory); + } + + public override ISortableSymbolNode ImportedTypeDictionaryNode(NodeFactory factory, TypeDesc type) + { + return new ImportedTypeGenericDictionaryNode(factory, type); + } + + public override ISortableSymbolNode ImportedMethodDictionaryNode(NodeFactory factory, MethodDesc method) + { + return new ImportedMethodGenericDictionaryNode(factory, method); + } + + public override IMethodNode ImportedMethodCodeNode(NodeFactory factory, MethodDesc method, bool unboxingStub) + { + return new ExternMethodSymbolNode(factory, method, unboxingStub); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExternalReferencesTableNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExternalReferencesTableNode.cs index 038f643b42..84c17aab5a 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExternalReferencesTableNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExternalReferencesTableNode.cs @@ -122,6 +122,14 @@ namespace ILCompiler.DependencyAnalysis return builder.ToObjectData(); } + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.ExternalReferencesTableNode; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return string.Compare(_blobName, ((ExternalReferencesTableNode)other)._blobName); + } + struct SymbolAndDelta : IEquatable { public readonly ISymbolNode Symbol; diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FatFunctionPointerNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FatFunctionPointerNode.cs index 77d71b6b74..a4127e63d3 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FatFunctionPointerNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FatFunctionPointerNode.cs @@ -78,7 +78,7 @@ namespace ILCompiler.DependencyAnalysis builder.EmitPointerReloc(factory.MethodEntrypoint(canonMethod, _isUnboxingStub)); // Find out what's the context to use - ISymbolNode contextParameter; + ISortableSymbolNode contextParameter; if (canonMethod.RequiresInstMethodDescArg()) { contextParameter = factory.MethodGenericDictionary(Method); @@ -96,5 +96,23 @@ namespace ILCompiler.DependencyAnalysis return builder.ToObjectData(); } + + protected internal override int ClassCode => 190463489; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + var compare = _isUnboxingStub.CompareTo(((FatFunctionPointerNode)other)._isUnboxingStub); + if (compare != 0) + return compare; + + return comparer.Compare(Method, ((FatFunctionPointerNode)other).Method); + } + + int ISortableSymbolNode.ClassCode => ClassCode; + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + return CompareToImpl((ObjectNode)other, comparer); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenArrayNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenArrayNode.cs index d25273599a..23b0992bde 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenArrayNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenArrayNode.cs @@ -45,14 +45,14 @@ namespace ILCompiler.DependencyAnalysis private IEETypeNode GetEETypeNode(NodeFactory factory) { - var fieldType = _preInitFieldInfo.Field.FieldType; + var fieldType = _preInitFieldInfo.Type; var node = factory.ConstructedTypeSymbol(fieldType); Debug.Assert(!node.RepresentsIndirectionCell); // Array are always local return node; } public override void EncodeData(ref ObjectDataBuilder dataBuilder, NodeFactory factory, bool relocsOnly) - { + { // Sync Block dataBuilder.EmitZeroPointer(); @@ -72,22 +72,40 @@ namespace ILCompiler.DependencyAnalysis } // byte contents - dataBuilder.EmitBytes(_preInitFieldInfo.Data); + _preInitFieldInfo.WriteData(ref dataBuilder, factory, relocsOnly); } protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); public override IEnumerable GetStaticDependencies(NodeFactory factory) { - return new DependencyListEntry[] + ObjectDataBuilder builder = new ObjectDataBuilder(factory, true); + EncodeData(ref builder, factory, true); + Relocation[] relocs = builder.ToObjectData().Relocs; + DependencyList dependencies = null; + + if (relocs != null) { - new DependencyListEntry(GetEETypeNode(factory), "Frozen preinitialized array"), - }; + dependencies = new DependencyList(); + foreach (Relocation reloc in relocs) + { + dependencies.Add(reloc.Target, "reloc"); + } + } + + return dependencies; } protected override void OnMarked(NodeFactory factory) { factory.FrozenSegmentRegion.AddEmbeddedObject(this); } + + protected internal override int ClassCode => 1789429316; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return _preInitFieldInfo.CompareTo(((FrozenArrayNode)other)._preInitFieldInfo, comparer); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenStringNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenStringNode.cs index e58879302e..1fa63fc515 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenStringNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenStringNode.cs @@ -90,5 +90,12 @@ namespace ILCompiler.DependencyAnalysis { factory.FrozenSegmentRegion.AddEmbeddedObject(this); } + + protected internal override int ClassCode => -1733946122; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return string.CompareOrdinal(_data, ((FrozenStringNode)other)._data); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticDescNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticDescNode.cs index f38bb7d2cb..07d482d3e4 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticDescNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticDescNode.cs @@ -170,6 +170,19 @@ namespace ILCompiler.DependencyAnalysis Debug.Assert(numSeries == NumSeries); } + + internal int CompareTo(GCStaticDescNode other, TypeSystemComparer comparer) + { + var compare = _isThreadStatic.CompareTo(other._isThreadStatic); + return compare != 0 ? compare : comparer.Compare(_type, other._type); + } + + protected internal override int ClassCode => 2142332918; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return comparer.Compare(_type, ((GCStaticDescNode)other)._type); + } } public class GCStaticDescRegionNode : ArrayOfEmbeddedDataNode @@ -232,5 +245,12 @@ namespace ILCompiler.DependencyAnalysis { return "Standalone" + _standaloneGCStaticDesc.GetMangledName(context.NameMangler); } + + protected internal override int ClassCode => 2091208431; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return _standaloneGCStaticDesc.CompareTo(((StandaloneGCStaticDescRegionNode)other)._standaloneGCStaticDesc, comparer); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs index 7df1ca1276..bea0665cd2 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs @@ -100,5 +100,12 @@ namespace ILCompiler.DependencyAnalysis return dataBuilder.ToObjectData(); } + + protected internal override int ClassCode => 1304929125; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return _gcMap.CompareTo(((GCStaticEETypeNode)other)._gcMap); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsNode.cs index 62209b420e..814ea97568 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsNode.cs @@ -12,7 +12,7 @@ using GCStaticRegionConstants = Internal.Runtime.GCStaticRegionConstants; namespace ILCompiler.DependencyAnalysis { - public class GCStaticsNode : ObjectNode, IExportableSymbolNode + public class GCStaticsNode : ObjectNode, IExportableSymbolNode, ISortableSymbolNode { private MetadataType _type; private List _preInitFieldInfos; @@ -21,7 +21,7 @@ namespace ILCompiler.DependencyAnalysis { Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Specific)); _type = type; - _preInitFieldInfos = PreInitFieldInfo.GetPreInitFieldInfos(_type); + _preInitFieldInfos = PreInitFieldInfo.GetPreInitFieldInfos(_type, hasGCStaticBase: true); } protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); @@ -39,7 +39,7 @@ namespace ILCompiler.DependencyAnalysis return nameMangler.NodeMangler.GCStatics(type); } - public virtual bool IsExported(NodeFactory factory) => factory.CompilationModuleGroup.ExportsType(Type); + public virtual ExportForm GetExportForm(NodeFactory factory) => factory.CompilationModuleGroup.GetExportTypeForm(Type); private ISymbolNode GetGCStaticEETypeNode(NodeFactory factory) { @@ -69,8 +69,14 @@ namespace ILCompiler.DependencyAnalysis if (_preInitFieldInfos != null) dependencyList.Add(factory.GCStaticsPreInitDataNode(_type), "PreInitData node"); } + else + { + dependencyList.Add(((UtcNodeFactory)factory).TypeGCStaticDescSymbol(_type), "GC Desc"); + } dependencyList.Add(factory.GCStaticIndirection(_type), "GC statics indirection"); + EETypeNode.AddDependenciesForStaticsNode(factory, _type, ref dependencyList); + return dependencyList; } @@ -81,12 +87,12 @@ namespace ILCompiler.DependencyAnalysis public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { - ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); - - builder.RequireInitialPointerAlignment(); - if (factory.Target.Abi == TargetAbi.CoreRT) { + ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); + + builder.RequireInitialPointerAlignment(); + int delta = GCStaticRegionConstants.Uninitialized; // Set the flag that indicates next pointer following EEType is the preinit data @@ -97,18 +103,45 @@ namespace ILCompiler.DependencyAnalysis if (_preInitFieldInfos != null) builder.EmitPointerReloc(factory.GCStaticsPreInitDataNode(_type)); + + builder.AddSymbol(this); + + return builder.ToObjectData(); } - else + else { - builder.RequireInitialAlignment(_type.GCStaticFieldAlignment.AsInt); + if (_preInitFieldInfos == null) + { + ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); - // @TODO - emit the frozen array node reloc - builder.EmitZeros(_type.GCStaticFieldSize.AsInt); + builder.RequireInitialPointerAlignment(); + + builder.EmitZeros(_type.GCStaticFieldSize.AsInt); + + builder.AddSymbol(this); + + return builder.ToObjectData(); + } + else + { + _preInitFieldInfos.Sort(PreInitFieldInfo.FieldDescCompare); + return GCStaticsPreInitDataNode.GetDataForPreInitDataField(this, _type, _preInitFieldInfos, 0, factory, relocsOnly); + } } + } - builder.AddSymbol(this); + protected internal override int ClassCode => -522346696; - return builder.ToObjectData(); + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return comparer.Compare(_type, ((GCStaticsNode)other)._type); + } + + int ISortableSymbolNode.ClassCode => ClassCode; + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + return CompareToImpl((ObjectNode)other, comparer); } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsPreInitDataNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsPreInitDataNode.cs index b547bca6ff..653a18fa0e 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsPreInitDataNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsPreInitDataNode.cs @@ -29,12 +29,7 @@ namespace ILCompiler.DependencyAnalysis // sort the PreInitFieldInfo to appear in increasing offset order for easier emitting _sortedPreInitFields = new List(preInitFields); - _sortedPreInitFields.Sort(FieldDescCompare); - } - - static int FieldDescCompare(PreInitFieldInfo fieldInfo1, PreInitFieldInfo fieldInfo2) - { - return fieldInfo1.Field.Offset.AsInt - fieldInfo2.Field.Offset.AsInt; + _sortedPreInitFields.Sort(PreInitFieldInfo.FieldDescCompare); } protected override string GetName(NodeFactory factory) => GetMangledName(_type, factory.NameMangler); @@ -84,20 +79,32 @@ namespace ILCompiler.DependencyAnalysis while (staticOffset < staticOffsetEnd) { + PreInitFieldInfo fieldInfo = idx < sortedPreInitFields.Count ? sortedPreInitFields[idx] : null; int writeTo = staticOffsetEnd; - if (idx < sortedPreInitFields.Count) - writeTo = sortedPreInitFields[idx].Field.Offset.AsInt; + if (fieldInfo != null) + writeTo = fieldInfo.Field.Offset.AsInt; // Emit the zero before the next preinitField builder.EmitZeros(writeTo - staticOffset); staticOffset = writeTo; - // Emit a pointer reloc to the frozen data - if (idx < sortedPreInitFields.Count) + if (fieldInfo != null) { - builder.EmitPointerReloc(factory.SerializedFrozenArray(sortedPreInitFields[idx])); + int count = builder.CountBytes; + + if (fieldInfo.Field.FieldType.IsValueType) + { + // Emit inlined data for value types + fieldInfo.WriteData(ref builder, factory); + } + else + { + // Emit a pointer reloc to the frozen data for reference types + builder.EmitPointerReloc(factory.SerializedFrozenArray(fieldInfo)); + } + + staticOffset += builder.CountBytes - count; idx++; - staticOffset += factory.Target.PointerSize; } } @@ -105,5 +112,12 @@ namespace ILCompiler.DependencyAnalysis return builder.ToObjectData(); } + + protected internal override int ClassCode => 1148300665; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return comparer.Compare(_type, ((GCStaticsPreInitDataNode)other)._type); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GVMDependenciesNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GVMDependenciesNode.cs index 85ef528484..a76728bd5f 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GVMDependenciesNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GVMDependenciesNode.cs @@ -18,9 +18,11 @@ namespace ILCompiler.DependencyAnalysis /// 2) Variant-interfaces GVMs /// This analysis node will ensure that the proper GVM instantiations are compiled on types. /// - internal class GVMDependenciesNode : DependencyNodeCore + public class GVMDependenciesNode : DependencyNodeCore { - private MethodDesc _method; + private const int UniversalCanonGVMDepthHeuristic_NonCanonDepth = 2; + private const int UniversalCanonGVMDepthHeuristic_CanonDepth = 2; + private readonly MethodDesc _method; public MethodDesc Method => _method; @@ -38,17 +40,58 @@ namespace ILCompiler.DependencyAnalysis public override IEnumerable GetStaticDependencies(NodeFactory context) { - // TODO: https://github.com/dotnet/corert/issues/3224 - if (_method.IsAbstract) + DependencyList dependencies = null; + + context.MetadataManager.GetDependenciesDueToVirtualMethodReflectability(ref dependencies, context, _method); + + if (!_method.IsAbstract) { - return new DependencyListEntry[] + MethodDesc instantiatedMethod = _method; + + // Universal canonical instantiations should be entirely universal canon + if (instantiatedMethod.IsCanonicalMethod(CanonicalFormKind.Universal)) + instantiatedMethod = instantiatedMethod.GetCanonMethodTarget(CanonicalFormKind.Universal); + + bool validInstantiation = + instantiatedMethod.IsSharedByGenericInstantiations || ( // Non-exact methods are always valid instantiations (always pass constraints check) + instantiatedMethod.Instantiation.CheckValidInstantiationArguments() && + instantiatedMethod.OwningType.Instantiation.CheckValidInstantiationArguments() && + instantiatedMethod.CheckConstraints()); + + if (validInstantiation) { - new DependencyListEntry(context.ReflectableMethod(_method), "Abstract reflectable method"), - }; + MethodDesc canonMethodTarget = instantiatedMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + + if (context.TypeSystemContext.SupportsUniversalCanon && canonMethodTarget.IsGenericDepthGreaterThan(UniversalCanonGVMDepthHeuristic_CanonDepth)) + { + // fall back to using the universal generic variant of the generic method + return dependencies; + } + + bool getUnboxingStub = instantiatedMethod.OwningType.IsValueType; + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(context.MethodEntrypoint(canonMethodTarget, getUnboxingStub), "GVM Dependency - Canon method"); + + if (canonMethodTarget != instantiatedMethod) + { + // Dependency includes the generic method dictionary of the instantiation, and all its dependencies. This is done by adding the + // ShadowConcreteMethod to the list of dynamic dependencies. The generic dictionary will be reported as a dependency of the ShadowConcreteMethod + Debug.Assert(!instantiatedMethod.IsCanonicalMethod(CanonicalFormKind.Any)); + + if (context.TypeSystemContext.SupportsUniversalCanon && instantiatedMethod.IsGenericDepthGreaterThan(UniversalCanonGVMDepthHeuristic_NonCanonDepth)) + { + // fall back to using the universal generic variant of the generic method + return dependencies; + } + + dependencies.Add(context.ShadowConcreteMethod(instantiatedMethod), "GVM Dependency - Dictionary"); + } + } } - return Array.Empty(); + return dependencies; } + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) { return Array.Empty(); @@ -76,7 +119,7 @@ namespace ILCompiler.DependencyAnalysis List dynamicDependencies = new List(); // Disable dependence tracking for ProjectN - if (factory.Target.Abi == TargetAbi.ProjectN) + if ((factory.Target.Abi == TargetAbi.ProjectN) && !ProjectNDependencyBehavior.EnableFullAnalysis) { return dynamicDependencies; } @@ -90,20 +133,25 @@ namespace ILCompiler.DependencyAnalysis continue; TypeDesc potentialOverrideType = entryAsEETypeNode.Type; - if (!(potentialOverrideType is DefType)) + if (!potentialOverrideType.IsDefType) continue; Debug.Assert(!potentialOverrideType.IsRuntimeDeterminedSubtype); - if (_method.OwningType.HasSameTypeDefinition(potentialOverrideType) && potentialOverrideType.IsInterface && (potentialOverrideType != _method.OwningType)) + if (potentialOverrideType.IsInterface) { - if (_method.OwningType.CanCastTo(potentialOverrideType)) + if (_method.OwningType.HasSameTypeDefinition(potentialOverrideType) && (potentialOverrideType != _method.OwningType)) { - // Variance expansion - MethodDesc matchingMethodOnRelatedVariantMethod = potentialOverrideType.GetMethod(_method.Name, _method.GetTypicalMethodDefinition().Signature); - matchingMethodOnRelatedVariantMethod = _method.Context.GetInstantiatedMethod(matchingMethodOnRelatedVariantMethod, _method.Instantiation); - dynamicDependencies.Add(new CombinedDependencyListEntry(factory.GVMDependencies(matchingMethodOnRelatedVariantMethod), null, "GVM Variant Interface dependency")); + if (potentialOverrideType.CanCastTo(_method.OwningType)) + { + // Variance expansion + MethodDesc matchingMethodOnRelatedVariantMethod = potentialOverrideType.GetMethod(_method.Name, _method.GetTypicalMethodDefinition().Signature); + matchingMethodOnRelatedVariantMethod = _method.Context.GetInstantiatedMethod(matchingMethodOnRelatedVariantMethod, _method.Instantiation); + dynamicDependencies.Add(new CombinedDependencyListEntry(factory.GVMDependencies(matchingMethodOnRelatedVariantMethod), null, "GVM Variant Interface dependency")); + } } + + continue; } // If this is an interface gvm, look for types that implement the interface @@ -112,89 +160,59 @@ namespace ILCompiler.DependencyAnalysis // relationship in the vtable. if (_method.OwningType.IsInterface) { - if (potentialOverrideType.IsInterface) - continue; - foreach (DefType interfaceImpl in potentialOverrideType.RuntimeInterfaces) { - if (interfaceImpl.ConvertToCanonForm(CanonicalFormKind.Specific) == _method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific)) + if (interfaceImpl == _method.OwningType) { - // Find if the type implements this method. (Note, do this comparision against the generic definition of the method, not the - // specific method instantiation that is "method" - MethodDesc genericDefinition = interfaceImpl.GetMethod(_method.Name, _method.GetTypicalMethodDefinition().Signature); - MethodDesc slotDecl = potentialOverrideType.ResolveInterfaceMethodTarget(genericDefinition); + MethodDesc slotDecl = potentialOverrideType.ResolveInterfaceMethodTarget(_method.GetMethodDefinition()); if (slotDecl != null) - CreateDependencyForMethodSlotAndInstantiation(slotDecl, dynamicDependencies, factory); + { + MethodDesc implementingMethodInstantiation = _method.Context.GetInstantiatedMethod(slotDecl, _method.Instantiation); + dynamicDependencies.Add(new CombinedDependencyListEntry(factory.GVMDependencies(implementingMethodInstantiation), null, "ImplementingMethodInstantiation")); + } } } } else { - // TODO: Ensure GVM Canon Target - - TypeDesc overrideTypeCanonCur = potentialOverrideType; - TypeDesc methodCanonContainingType = _method.OwningType; - while (overrideTypeCanonCur != null) + // Quickly check if the potential overriding type is at all related to the GVM's owning type (there is no need + // to do any processing for a type that is not at all related to the GVM's owning type. Resolving virtuals is expensive). + TypeDesc overrideTypeCur = potentialOverrideType; { - if (overrideTypeCanonCur.ConvertToCanonForm(CanonicalFormKind.Specific) == methodCanonContainingType.ConvertToCanonForm(CanonicalFormKind.Specific)) + do { - MethodDesc methodDefInDerivedType = potentialOverrideType.GetMethod(_method.Name, _method.GetTypicalMethodDefinition().Signature); - if(methodDefInDerivedType != null) - CreateDependencyForMethodSlotAndInstantiation(methodDefInDerivedType, dynamicDependencies, factory); + if (overrideTypeCur == _method.OwningType) + break; - MethodDesc slotDecl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(_method); - if (slotDecl != null) - CreateDependencyForMethodSlotAndInstantiation(slotDecl.GetMethodDefinition(), dynamicDependencies, factory); + overrideTypeCur = overrideTypeCur.BaseType; + } + while (overrideTypeCur != null); + + if (overrideTypeCur == null) + continue; + } + + overrideTypeCur = potentialOverrideType; + while (overrideTypeCur != null) + { + if (overrideTypeCur == _method.OwningType) + { + // The GVMDependencyNode already declares the entrypoint/dictionary dependencies of the current method + // as static dependencies, therefore we can break the loop as soon we hit the current method's owning type + // while we're traversing the hierarchy of the potential derived types. + break; } - overrideTypeCanonCur = overrideTypeCanonCur.BaseType; + MethodDesc instantiatedTargetMethod = overrideTypeCur.FindVirtualFunctionTargetMethodOnObjectType(_method); + if (instantiatedTargetMethod != null) + dynamicDependencies.Add(new CombinedDependencyListEntry(factory.GVMDependencies(instantiatedTargetMethod), null, "DerivedMethodInstantiation")); + + overrideTypeCur = overrideTypeCur.BaseType; } } } + return dynamicDependencies; } - - private void CreateDependencyForMethodSlotAndInstantiation(MethodDesc methodDef, List dynamicDependencies, NodeFactory factory) - { - Debug.Assert(methodDef != null); - Debug.Assert(!methodDef.Signature.IsStatic); - - if (methodDef.IsAbstract) - return; - - MethodDesc derivedMethodInstantiation = _method.Context.GetInstantiatedMethod(methodDef, _method.Instantiation); - - // Universal canonical instantiations should be entirely universal canon - if (derivedMethodInstantiation.IsCanonicalMethod(CanonicalFormKind.Universal)) - { - derivedMethodInstantiation = derivedMethodInstantiation.GetCanonMethodTarget(CanonicalFormKind.Universal); - } - - // TODO: verify for invalid instantiations, like List? - bool validInstantiation = - derivedMethodInstantiation.IsSharedByGenericInstantiations || // Non-exact methods are always valid instantiations (always pass constraints check) - derivedMethodInstantiation.CheckConstraints(); // Verify that the instantiation does not violate constraints - - if (validInstantiation) - { - MethodDesc canonMethodTarget = derivedMethodInstantiation.GetCanonMethodTarget(CanonicalFormKind.Specific); - - bool getUnboxingStub = (derivedMethodInstantiation.OwningType.IsValueType || derivedMethodInstantiation.OwningType.IsEnum); - dynamicDependencies.Add(new CombinedDependencyListEntry(factory.MethodEntrypoint(canonMethodTarget, getUnboxingStub), null, "DerivedMethodInstantiation")); - - if (canonMethodTarget != derivedMethodInstantiation) - { - // Dependency includes the generic method dictionary of the instantiation, and all its dependencies. This is done by adding the - // ShadowConcreteMethod to the list of dynamic dependencies. The generic dictionary will be reported as a dependency of the ShadowConcreteMethod - // TODO: detect large recursive generics and fallback to USG templates - Debug.Assert(!derivedMethodInstantiation.IsCanonicalMethod(CanonicalFormKind.Any)); - dynamicDependencies.Add(new CombinedDependencyListEntry(factory.ShadowConcreteMethod(derivedMethodInstantiation), null, "DerivedMethodInstantiation dictionary")); - } - } - else - { - // TODO: universal generics - } - } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericCompositionNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericCompositionNode.cs index 5594f71e99..cff9f37463 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericCompositionNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericCompositionNode.cs @@ -99,6 +99,12 @@ namespace ILCompiler.DependencyAnalysis } protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + protected internal override int ClassCode => -762680703; + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return _details.CompareToImpl(((GenericCompositionNode)other)._details, comparer); + } } internal struct GenericCompositionDetails : IEquatable @@ -174,6 +180,36 @@ namespace ILCompiler.DependencyAnalysis return true; } + public int CompareToImpl(GenericCompositionDetails other, TypeSystemComparer comparer) + { + var compare = Instantiation.Length.CompareTo(other.Instantiation.Length); + if (compare != 0) + return compare; + + if (Variance == null && other.Variance != null) + return -1; + + if (Variance != null && other.Variance == null) + return 1; + + for (int i = 0; i < Instantiation.Length; i++) + { + compare = comparer.Compare(Instantiation[i], other.Instantiation[i]); + if (compare != 0) + return compare; + + if (Variance != null) + { + compare = Variance[i].CompareTo(other.Variance[i]); + if (compare != 0) + return compare; + } + } + + Debug.Assert(Equals(other)); + return 0; + } + public override bool Equals(object obj) { return obj is GenericCompositionDetails && Equals((GenericCompositionDetails)obj); diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs index bca4d64746..d40972210d 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs @@ -51,6 +51,8 @@ namespace ILCompiler.DependencyAnalysis flags |= (short)EETypeFlags.IsInterfaceFlag; if (factory.TypeSystemContext.HasLazyStaticConstructor(_type)) rareFlags = rareFlags | EETypeRareFlags.HasCctorFlag; + if (_type.IsByRefLike) + rareFlags |= EETypeRareFlags.IsByRefLikeFlag; if (rareFlags != 0) _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.RareFlags, (uint)rareFlags); @@ -73,5 +75,7 @@ namespace ILCompiler.DependencyAnalysis return dataBuilder.ToObjectData(); } + + protected internal override int ClassCode => -160325006; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericDictionaryNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericDictionaryNode.cs index c54b870e75..8ebdfa051a 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericDictionaryNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericDictionaryNode.cs @@ -16,18 +16,19 @@ namespace ILCompiler.DependencyAnalysis /// at runtime to look up runtime artifacts that depend on the concrete /// context the generic type or method was instantiated with. /// - public abstract class GenericDictionaryNode : ObjectNode, IExportableSymbolNode + public abstract class GenericDictionaryNode : ObjectNode, IExportableSymbolNode, ISortableSymbolNode { + private readonly NodeFactory _factory; + protected abstract TypeSystemContext Context { get; } + public abstract TypeSystemEntity OwningEntity { get; } + public abstract Instantiation TypeInstantiation { get; } public abstract Instantiation MethodInstantiation { get; } public abstract DictionaryLayoutNode GetDictionaryLayout(NodeFactory factory); - - public override ObjectNodeSection Section => - Context.Target.IsWindows ? ObjectNodeSection.FoldableReadOnlyDataSection : ObjectNodeSection.DataSection; public sealed override bool StaticDependenciesAreComputed => true; @@ -35,15 +36,7 @@ namespace ILCompiler.DependencyAnalysis int ISymbolNode.Offset => 0; - protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) - { - return new DependencyList - { - new DependencyListEntry(GetDictionaryLayout(factory), "Dictionary layout"), - }; - } - - public abstract bool IsExported(NodeFactory factory); + public abstract ExportForm GetExportForm(NodeFactory factory); public abstract void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb); @@ -51,34 +44,54 @@ namespace ILCompiler.DependencyAnalysis int ISymbolDefinitionNode.Offset => HeaderSize; + public override ObjectNodeSection Section => GetDictionaryLayout(_factory).DictionarySection(_factory); + + public GenericDictionaryNode(NodeFactory factory) + { + _factory = factory; + } + public sealed override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); builder.AddSymbol(this); builder.RequireInitialPointerAlignment(); - // Node representing the generic dictionary doesn't have any dependencies for - // dependency analysis purposes. The dependencies are tracked as dependencies of the - // concrete method bodies. When we reach the object data emission phase, the dependencies - // should all already have been marked. - if (!relocsOnly) + DictionaryLayoutNode layout = GetDictionaryLayout(factory); + + // Node representing the generic dictionary layout might be one of two kinds: + // With fixed slots, or where slots are added as we're expanding the graph. + // If it's the latter, we can't touch the collection of slots before the graph expansion + // is complete (relocsOnly == false). It's someone else's responsibility + // to make sure the dependencies are properly generated. + // If this is a dictionary layout with fixed slots, it's the responsibility of + // each dictionary to ensure the targets are marked. + if (layout.HasFixedSlots || !relocsOnly) { - EmitDataInternal(ref builder, factory); + // TODO: pass the layout we already have to EmitDataInternal + EmitDataInternal(ref builder, factory, relocsOnly); } return builder.ToObjectData(); } - protected virtual void EmitDataInternal(ref ObjectDataBuilder builder, NodeFactory factory) + protected virtual void EmitDataInternal(ref ObjectDataBuilder builder, NodeFactory factory, bool fixedLayoutOnly) { DictionaryLayoutNode layout = GetDictionaryLayout(factory); - layout.EmitDictionaryData(ref builder, factory, this); + layout.EmitDictionaryData(ref builder, factory, this, fixedLayoutOnly: fixedLayoutOnly); } protected sealed override string GetName(NodeFactory factory) { return this.GetMangledName(factory.NameMangler); } + + int ISortableSymbolNode.ClassCode => ClassCode; + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + return CompareToImpl((ObjectNode)other, comparer); + } } public sealed class TypeGenericDictionaryNode : GenericDictionaryNode @@ -94,14 +107,10 @@ namespace ILCompiler.DependencyAnalysis public override Instantiation TypeInstantiation => _owningType.Instantiation; public override Instantiation MethodInstantiation => new Instantiation(); protected override TypeSystemContext Context => _owningType.Context; - - public override bool IsExported(NodeFactory factory) => factory.CompilationModuleGroup.ExportsType(OwningType); - + public override TypeSystemEntity OwningEntity => _owningType; + public override ExportForm GetExportForm(NodeFactory factory) => factory.CompilationModuleGroup.GetExportTypeFormDictionary(OwningType); public TypeDesc OwningType => _owningType; - public override ObjectNodeSection Section => - Context.Target.IsWindows ? ObjectNodeSection.FoldableReadOnlyDataSection : base.Section; - public override DictionaryLayoutNode GetDictionaryLayout(NodeFactory factory) { return factory.GenericDictionaryLayout(_owningType.ConvertToCanonForm(CanonicalFormKind.Specific)); @@ -109,43 +118,28 @@ namespace ILCompiler.DependencyAnalysis public override bool HasConditionalStaticDependencies => true; - private static bool ContributesToDictionaryLayout(MethodDesc method) - { - // Generic methods have their own generic dictionaries - if (method.HasInstantiation) - return false; - - // Abstract methods don't have a body - if (method.IsAbstract) - return false; - - // PInvoke methods, runtime imports, etc. are not permitted on generic types, - // but let's not crash the compilation because of that. - if (method.IsPInvoke || method.IsRuntimeImplemented) - return false; - - return true; - } protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { - DependencyList result = null; + DependencyList result = new DependencyList(); - if (factory.CompilationModuleGroup.ShouldPromoteToFullType(_owningType)) + // Include the layout as a dependency if the canonical type isn't imported + TypeDesc canonicalOwningType = _owningType.ConvertToCanonForm(CanonicalFormKind.Specific); + if (factory.CompilationModuleGroup.ContainsType(canonicalOwningType) || !factory.CompilationModuleGroup.ShouldReferenceThroughImportTable(canonicalOwningType)) + result.Add(GetDictionaryLayout(factory), "Layout"); + + // Lazy generic use of the Activator.CreateInstance heuristic requires tracking type parameters that are used in lazy generics. + if (factory.LazyGenericsPolicy.UsesLazyGenerics(_owningType)) { - result = new DependencyList(); - - // If the compilation group wants this type to be fully promoted, it means the EEType is going to be - // COMDAT folded with other EETypes generated in a different object file. This means their generic - // dictionaries need to have identical contents. The only way to achieve that is by generating - // the entries for all methods that contribute to the dictionary, and sorting the dictionaries. - foreach (var method in _owningType.GetAllMethods()) + foreach (var arg in _owningType.Instantiation) { - if (!ContributesToDictionaryLayout(method)) + // Skip types that do not have a default constructor (not interesting). + if (arg.IsValueType || arg.GetDefaultConstructor() == null) continue; - result.Add(factory.MethodEntrypoint(method.GetCanonMethodTarget(CanonicalFormKind.Specific)), - "Cross-objectfile equivalent dictionary"); + result.Add(new DependencyListEntry( + factory.DefaultConstructorFromLazy(arg.ConvertToCanonForm(CanonicalFormKind.Specific)), + "Default constructor for lazy generics")); } } @@ -159,7 +153,7 @@ namespace ILCompiler.DependencyAnalysis // that use the same dictionary layout. foreach (var method in _owningType.GetAllMethods()) { - if (!ContributesToDictionaryLayout(method)) + if (!EETypeNode.MethodHasNonGenericILMethodBody(method)) continue; // If a canonical method body was compiled, we need to track the dictionary @@ -171,7 +165,8 @@ namespace ILCompiler.DependencyAnalysis } } - public TypeGenericDictionaryNode(TypeDesc owningType) + public TypeGenericDictionaryNode(TypeDesc owningType, NodeFactory factory) + : base(factory) { Debug.Assert(!owningType.IsCanonicalSubtype(CanonicalFormKind.Any)); Debug.Assert(!owningType.IsRuntimeDeterminedSubtype); @@ -180,6 +175,13 @@ namespace ILCompiler.DependencyAnalysis _owningType = owningType; } + + protected internal override int ClassCode => 889700584; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return comparer.Compare(_owningType, ((TypeGenericDictionaryNode)other)._owningType); + } } public sealed class MethodGenericDictionaryNode : GenericDictionaryNode @@ -194,15 +196,50 @@ namespace ILCompiler.DependencyAnalysis public override Instantiation TypeInstantiation => _owningMethod.OwningType.Instantiation; public override Instantiation MethodInstantiation => _owningMethod.Instantiation; protected override TypeSystemContext Context => _owningMethod.Context; - - public override bool IsExported(NodeFactory factory) => factory.CompilationModuleGroup.ExportsMethodDictionary(OwningMethod); - + public override TypeSystemEntity OwningEntity => _owningMethod; + public override ExportForm GetExportForm(NodeFactory factory) => factory.CompilationModuleGroup.GetExportMethodDictionaryForm(OwningMethod); public MethodDesc OwningMethod => _owningMethod; protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { DependencyList dependencies = new DependencyList(); + + MethodDesc canonicalTarget = _owningMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + if (factory.CompilationModuleGroup.ContainsMethodBody(canonicalTarget, false)) + dependencies.Add(GetDictionaryLayout(factory), "Layout"); + GenericMethodsHashtableNode.GetGenericMethodsHashtableDependenciesForMethod(ref dependencies, factory, _owningMethod); + + factory.InteropStubManager.AddMarshalAPIsGenericDependencies(ref dependencies, factory, _owningMethod); + + // Lazy generic use of the Activator.CreateInstance heuristic requires tracking type parameters that are used in lazy generics. + if (factory.LazyGenericsPolicy.UsesLazyGenerics(_owningMethod)) + { + foreach (var arg in _owningMethod.OwningType.Instantiation) + { + // Skip types that do not have a default constructor (not interesting). + if (arg.IsValueType || arg.GetDefaultConstructor() == null) + continue; + + dependencies.Add(new DependencyListEntry( + factory.DefaultConstructorFromLazy(arg.ConvertToCanonForm(CanonicalFormKind.Specific)), + "Default constructor for lazy generics")); + } + foreach (var arg in _owningMethod.Instantiation) + { + // Skip types that do not have a default constructor (not interesting). + if (arg.IsValueType || arg.GetDefaultConstructor() == null) + continue; + + dependencies.Add(new DependencyListEntry( + factory.DefaultConstructorFromLazy(arg.ConvertToCanonForm(CanonicalFormKind.Specific)), + "Default constructor for lazy generics")); + } + } + + // Make sure the dictionary can also be populated + dependencies.Add(factory.ShadowConcreteMethod(_owningMethod), "Dictionary contents"); + return dependencies; } @@ -211,7 +248,7 @@ namespace ILCompiler.DependencyAnalysis return factory.GenericDictionaryLayout(_owningMethod.GetCanonMethodTarget(CanonicalFormKind.Specific)); } - protected override void EmitDataInternal(ref ObjectDataBuilder builder, NodeFactory factory) + protected override void EmitDataInternal(ref ObjectDataBuilder builder, NodeFactory factory, bool fixedLayoutOnly) { // Method generic dictionaries get prefixed by the hash code of the owning method // to allow quick lookups of additional details by the type loader. @@ -227,10 +264,11 @@ namespace ILCompiler.DependencyAnalysis if (factory.LazyGenericsPolicy.UsesLazyGenerics(OwningMethod)) return; - base.EmitDataInternal(ref builder, factory); + base.EmitDataInternal(ref builder, factory, fixedLayoutOnly); } - public MethodGenericDictionaryNode(MethodDesc owningMethod) + public MethodGenericDictionaryNode(MethodDesc owningMethod, NodeFactory factory) + : base(factory) { Debug.Assert(!owningMethod.IsSharedByGenericInstantiations); Debug.Assert(owningMethod.HasInstantiation); @@ -238,5 +276,12 @@ namespace ILCompiler.DependencyAnalysis _owningMethod = owningMethod; } + + protected internal override int ClassCode => -1245704203; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return comparer.Compare(_owningMethod, ((MethodGenericDictionaryNode)other)._owningMethod); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericLookupResult.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericLookupResult.cs index fc63d1a43e..59d2ac7629 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericLookupResult.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericLookupResult.cs @@ -50,14 +50,70 @@ namespace ILCompiler.DependencyAnalysis CheckArrayElementType, // check the array element type TypeSize, // size of the type FieldOffset, // field offset - CallingConvention, // CallingConventionConverterThunk + CallingConvention_NoInstParam, // CallingConventionConverterThunk NO_INSTANTIATING_PARAM + CallingConvention_HasInstParam, // CallingConventionConverterThunk HAS_INSTANTIATING_PARAM + CallingConvention_MaybeInstParam, // CallingConventionConverterThunk MAYBE_INSTANTIATING_PARAM VtableOffset, // Offset of a virtual method into the type's vtable Constrained, // ConstrainedCallDesc + ConstrainedDirect, // Direct ConstrainedCallDesc + Integer, // Integer + UnboxingMethod, // UnboxingMethod } public interface IGenericLookupResultTocWriter { void WriteData(GenericLookupResultReferenceType referenceType, LookupResultType slotType, TypeSystemEntity context); + void WriteIntegerSlot(int value); + } + + public struct GenericLookupResultContext + { + private readonly TypeSystemEntity _canonicalOwner; + + public readonly Instantiation TypeInstantiation; + + public readonly Instantiation MethodInstantiation; + + public TypeSystemEntity Context + { + get + { + if (_canonicalOwner is TypeDesc) + { + var owningTypeDefinition = (MetadataType)((TypeDesc)_canonicalOwner).GetTypeDefinition(); + Debug.Assert(owningTypeDefinition.Instantiation.Length == TypeInstantiation.Length); + Debug.Assert(MethodInstantiation.IsNull || MethodInstantiation.Length == 0); + + return owningTypeDefinition.MakeInstantiatedType(TypeInstantiation); + } + + Debug.Assert(_canonicalOwner is MethodDesc); + MethodDesc owningMethodDefinition = ((MethodDesc)_canonicalOwner).GetTypicalMethodDefinition(); + Debug.Assert(owningMethodDefinition.Instantiation.Length == MethodInstantiation.Length); + + MethodDesc concreteMethod = owningMethodDefinition; + if (!TypeInstantiation.IsNull && TypeInstantiation.Length > 0) + { + TypeDesc owningType = owningMethodDefinition.OwningType; + Debug.Assert(owningType.Instantiation.Length == TypeInstantiation.Length); + concreteMethod = owningType.Context.GetMethodForInstantiatedType(owningMethodDefinition, ((MetadataType)owningType).MakeInstantiatedType(TypeInstantiation)); + } + else + { + Debug.Assert(owningMethodDefinition.OwningType.Instantiation.IsNull + || owningMethodDefinition.OwningType.Instantiation.Length == 0); + } + + return concreteMethod.MakeInstantiatedMethod(MethodInstantiation); + } + } + + public GenericLookupResultContext(TypeSystemEntity canonicalOwner, Instantiation typeInst, Instantiation methodInst) + { + _canonicalOwner = canonicalOwner; + TypeInstantiation = typeInst; + MethodInstantiation = methodInst; + } } /// @@ -70,14 +126,30 @@ namespace ILCompiler.DependencyAnalysis public abstract class GenericLookupResult { protected abstract int ClassCode { get; } - public abstract ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary); + public abstract ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary); public abstract void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb); public abstract override string ToString(); protected abstract int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer); + protected abstract bool EqualsImpl(GenericLookupResult obj); + protected abstract int GetHashCodeImpl(); - public virtual void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public sealed override bool Equals(object obj) { - ISymbolNode target = GetTarget(factory, typeInstantiation, methodInstantiation, dictionary); + GenericLookupResult other = obj as GenericLookupResult; + if (obj == null) + return false; + + return ClassCode == other.ClassCode && EqualsImpl(other); + } + + public sealed override int GetHashCode() + { + return ClassCode * 31 + GetHashCodeImpl(); + } + + public virtual void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, GenericLookupResultContext dictionary, GenericDictionaryNode dictionaryNode) + { + ISymbolNode target = GetTarget(factory, dictionary); if (LookupResultReferenceType(factory) == GenericLookupResultReferenceType.ConditionalIndirect) { builder.EmitPointerRelocOrIndirectionReference(target); @@ -103,7 +175,7 @@ namespace ILCompiler.DependencyAnalysis return Array.Empty>(); } - public class Comparer + public class Comparer : IComparer { private TypeSystemComparer _comparer; @@ -144,7 +216,7 @@ namespace ILCompiler.DependencyAnalysis /// /// Generic lookup result that points to an EEType. /// - internal sealed class TypeHandleGenericLookupResult : GenericLookupResult + public sealed class TypeHandleGenericLookupResult : GenericLookupResult { private TypeDesc _type; @@ -156,10 +228,10 @@ namespace ILCompiler.DependencyAnalysis _type = type; } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) { // We are getting a constructed type symbol because this might be something passed to newobj. - TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(typeInstantiation, methodInstantiation); + TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); return factory.ConstructedTypeSymbol(instantiatedType); } @@ -169,6 +241,7 @@ namespace ILCompiler.DependencyAnalysis sb.Append(nameMangler.GetMangledTypeName(_type)); } + public TypeDesc Type => _type; public override string ToString() => $"TypeHandle: {_type}"; public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) @@ -197,13 +270,23 @@ namespace ILCompiler.DependencyAnalysis { return comparer.Compare(_type, ((TypeHandleGenericLookupResult)other)._type); } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((TypeHandleGenericLookupResult)obj)._type == _type; + } } /// /// Generic lookup result that points to an EEType where if the type is Nullable<X> the EEType is X /// - internal sealed class UnwrapNullableTypeHandleGenericLookupResult : GenericLookupResult + public sealed class UnwrapNullableTypeHandleGenericLookupResult : GenericLookupResult { private TypeDesc _type; @@ -215,9 +298,9 @@ namespace ILCompiler.DependencyAnalysis _type = type; } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) { - TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(typeInstantiation, methodInstantiation); + TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); // Unwrap the nullable type if necessary if (instantiatedType.IsNullable) @@ -233,6 +316,7 @@ namespace ILCompiler.DependencyAnalysis sb.Append(nameMangler.GetMangledTypeName(_type)); } + public TypeDesc Type => _type; public override string ToString() => $"UnwrapNullable: {_type}"; public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) @@ -261,6 +345,16 @@ namespace ILCompiler.DependencyAnalysis { return comparer.Compare(_type, ((UnwrapNullableTypeHandleGenericLookupResult)other)._type); } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((UnwrapNullableTypeHandleGenericLookupResult)obj)._type == _type; + } } /// @@ -278,15 +372,15 @@ namespace ILCompiler.DependencyAnalysis _field = field; } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) { - Debug.Assert(false, "GetTarget for a FieldOffsetGenericLookupResult doesn't make sense. It isn't a pointer being emitted"); + Debug.Fail("GetTarget for a FieldOffsetGenericLookupResult doesn't make sense. It isn't a pointer being emitted"); return null; } - public override void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, GenericLookupResultContext dictionary, GenericDictionaryNode dictionaryNode) { - FieldDesc instantiatedField = _field.GetNonRuntimeDeterminedFieldFromRuntimeDeterminedFieldViaSubstitution(typeInstantiation, methodInstantiation); + FieldDesc instantiatedField = _field.GetNonRuntimeDeterminedFieldFromRuntimeDeterminedFieldViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); int offset = instantiatedField.Offset.AsInt; builder.EmitNaturalInt(offset); } @@ -314,6 +408,16 @@ namespace ILCompiler.DependencyAnalysis { return comparer.Compare(_field, ((FieldOffsetGenericLookupResult)other)._field); } + + protected override int GetHashCodeImpl() + { + return _field.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((FieldOffsetGenericLookupResult)obj)._field == _field; + } } /// @@ -331,15 +435,15 @@ namespace ILCompiler.DependencyAnalysis _method = method; } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) { - Debug.Assert(false, "GetTarget for a VTableOffsetGenericLookupResult doesn't make sense. It isn't a pointer being emitted"); + Debug.Fail("GetTarget for a VTableOffsetGenericLookupResult doesn't make sense. It isn't a pointer being emitted"); return null; } - public override void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, GenericLookupResultContext dictionary, GenericDictionaryNode dictionaryNode) { - Debug.Assert(false, "VTableOffset contents should only be generated into generic dictionaries at runtime"); + Debug.Fail("VTableOffset contents should only be generated into generic dictionaries at runtime"); builder.EmitNaturalInt(0); } @@ -362,7 +466,7 @@ namespace ILCompiler.DependencyAnalysis MethodDesc canonMethod = _method.GetCanonMethodTarget(CanonicalFormKind.Universal); // If we're producing a full vtable for the type, we don't need to report virtual method use. - if (factory.CompilationModuleGroup.ShouldProduceFullVTable(canonMethod.OwningType)) + if (factory.VTable(canonMethod.OwningType).HasFixedSlots) return Array.Empty>(); return new DependencyNodeCore[] { @@ -379,6 +483,16 @@ namespace ILCompiler.DependencyAnalysis { return comparer.Compare(_method, ((VTableOffsetGenericLookupResult)other)._method); } + + protected override int GetHashCodeImpl() + { + return _method.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((VTableOffsetGenericLookupResult)obj)._method == _method; + } } /// @@ -396,9 +510,9 @@ namespace ILCompiler.DependencyAnalysis _method = method; } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) { - MethodDesc instantiatedMethod = _method.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(typeInstantiation, methodInstantiation); + MethodDesc instantiatedMethod = _method.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); return factory.RuntimeMethodHandle(instantiatedMethod); } @@ -424,6 +538,16 @@ namespace ILCompiler.DependencyAnalysis { return comparer.Compare(_method, ((MethodHandleGenericLookupResult)other)._method); } + + protected override int GetHashCodeImpl() + { + return _method.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((MethodHandleGenericLookupResult)obj)._method == _method; + } } /// @@ -441,9 +565,9 @@ namespace ILCompiler.DependencyAnalysis _field = field; } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) { - FieldDesc instantiatedField = _field.GetNonRuntimeDeterminedFieldFromRuntimeDeterminedFieldViaSubstitution(typeInstantiation, methodInstantiation); + FieldDesc instantiatedField = _field.GetNonRuntimeDeterminedFieldFromRuntimeDeterminedFieldViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); return factory.RuntimeFieldHandle(instantiatedField); } @@ -469,12 +593,22 @@ namespace ILCompiler.DependencyAnalysis { return comparer.Compare(_field, ((FieldHandleGenericLookupResult)other)._field); } + + protected override int GetHashCodeImpl() + { + return _field.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((FieldHandleGenericLookupResult)obj)._field == _field; + } } /// /// Generic lookup result that points to a method dictionary. /// - internal sealed class MethodDictionaryGenericLookupResult : GenericLookupResult + public sealed class MethodDictionaryGenericLookupResult : GenericLookupResult { private MethodDesc _method; @@ -486,9 +620,9 @@ namespace ILCompiler.DependencyAnalysis _method = method; } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) { - MethodDesc instantiatedMethod = _method.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(typeInstantiation, methodInstantiation); + MethodDesc instantiatedMethod = _method.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); return factory.MethodGenericDictionary(instantiatedMethod); } @@ -510,6 +644,7 @@ namespace ILCompiler.DependencyAnalysis sb.Append(nameMangler.GetMangledMethodName(_method)); } + public MethodDesc Method => _method; public override string ToString() => $"MethodDictionary: {_method}"; public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) @@ -526,6 +661,16 @@ namespace ILCompiler.DependencyAnalysis { return comparer.Compare(_method, ((MethodDictionaryGenericLookupResult)other)._method); } + + protected override int GetHashCodeImpl() + { + return _method.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((MethodDictionaryGenericLookupResult)obj)._method == _method; + } } /// @@ -545,9 +690,9 @@ namespace ILCompiler.DependencyAnalysis _isUnboxingThunk = isUnboxingThunk; } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) { - MethodDesc instantiatedMethod = _method.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(typeInstantiation, methodInstantiation); + MethodDesc instantiatedMethod = _method.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); return factory.FatFunctionPointer(instantiatedMethod, _isUnboxingThunk); } @@ -565,13 +710,28 @@ namespace ILCompiler.DependencyAnalysis public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) { - return factory.NativeLayout.MethodEntrypointDictionarySlot - (_method, unboxing: true, functionPointerTarget: factory.MethodEntrypoint(_method.GetCanonMethodTarget(CanonicalFormKind.Specific))); + MethodDesc canonMethod = _method.GetCanonMethodTarget(CanonicalFormKind.Specific); + + // + // For universal canonical methods, we don't need the unboxing stub really, because + // the calling convention translation thunk will handle the unboxing (and we can avoid having a double thunk here) + // We just need the flag in the native layout info signature indicating that we needed an unboxing stub + // + bool getUnboxingStubNode = _isUnboxingThunk && !canonMethod.IsCanonicalMethod(CanonicalFormKind.Universal); + + return factory.NativeLayout.MethodEntrypointDictionarySlot( + _method, + _isUnboxingThunk, + factory.MethodEntrypoint(canonMethod, getUnboxingStubNode)); } public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) { - writer.WriteData(LookupResultReferenceType(factory), LookupResultType.Method, _method); + LookupResultType lookupResult = LookupResultType.Method; + if (_isUnboxingThunk && ProjectNDependencyBehavior.EnableFullAnalysis) + lookupResult = LookupResultType.UnboxingMethod; + + writer.WriteData(LookupResultReferenceType(factory), lookupResult, _method); } protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) @@ -583,153 +743,81 @@ namespace ILCompiler.DependencyAnalysis return comparer.Compare(_method, otherEntry._method); } + + protected override int GetHashCodeImpl() + { + return _method.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((MethodEntryGenericLookupResult)obj)._method == _method && + ((MethodEntryGenericLookupResult)obj)._isUnboxingThunk == _isUnboxingThunk; + } } /// - /// Generic lookup result that points to a virtual dispatch stub. + /// Generic lookup result that points to a dispatch cell. /// - internal sealed class VirtualDispatchGenericLookupResult : GenericLookupResult + internal sealed class VirtualDispatchCellGenericLookupResult : GenericLookupResult { private MethodDesc _method; protected override int ClassCode => 643566930; - public VirtualDispatchGenericLookupResult(MethodDesc method) + public VirtualDispatchCellGenericLookupResult(MethodDesc method) { Debug.Assert(method.IsRuntimeDeterminedExactMethod); Debug.Assert(method.IsVirtual); - - // Normal virtual methods don't need a generic lookup. - Debug.Assert(method.OwningType.IsInterface || method.HasInstantiation); + Debug.Assert(method.OwningType.IsInterface); _method = method; } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext context) { - if (factory.Target.Abi == TargetAbi.CoreRT) - { - MethodDesc instantiatedMethod = _method.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(typeInstantiation, methodInstantiation); - return factory.ReadyToRunHelper(ReadyToRunHelperId.VirtualCall, instantiatedMethod); - } - else - { - MethodDesc instantiatedMethod = _method.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); - return factory.InterfaceDispatchCell(instantiatedMethod, dictionary.GetMangledName(factory.NameMangler)); - } + MethodDesc instantiatedMethod = _method.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(context.TypeInstantiation, context.MethodInstantiation); + + TypeSystemEntity contextOwner = context.Context; + GenericDictionaryNode dictionary = + contextOwner is TypeDesc ? + (GenericDictionaryNode)factory.TypeGenericDictionary((TypeDesc)contextOwner) : + (GenericDictionaryNode)factory.MethodGenericDictionary((MethodDesc)contextOwner); + + return factory.InterfaceDispatchCell(instantiatedMethod, dictionary.GetMangledName(factory.NameMangler)); } public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { - sb.Append("VirtualCall_"); + sb.Append("DispatchCell_"); sb.Append(nameMangler.GetMangledMethodName(_method)); } - public override string ToString() => $"VirtualCall: {_method}"; + public override string ToString() => $"DispatchCell: {_method}"; public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) { - if (factory.Target.Abi == TargetAbi.CoreRT) - { - return factory.NativeLayout.NotSupportedDictionarySlot; - } - else - { - return factory.NativeLayout.InterfaceCellDictionarySlot(_method); - } + return factory.NativeLayout.InterfaceCellDictionarySlot(_method); } public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) { - if (factory.Target.Abi == TargetAbi.CoreRT) - { - // TODO - throw new NotImplementedException(); - } - else - { - writer.WriteData(LookupResultReferenceType(factory), LookupResultType.InterfaceDispatchCell, _method); - } + writer.WriteData(LookupResultReferenceType(factory), LookupResultType.InterfaceDispatchCell, _method); } protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { - return comparer.Compare(_method, ((VirtualDispatchGenericLookupResult)other)._method); - } - } - - /// - /// Generic lookup result that points to a virtual function address load stub. - /// - internal sealed class VirtualResolveGenericLookupResult : GenericLookupResult - { - private MethodDesc _method; - - protected override int ClassCode => -12619218; - - public VirtualResolveGenericLookupResult(MethodDesc method) - { - Debug.Assert(method.IsRuntimeDeterminedExactMethod); - Debug.Assert(method.IsVirtual); - - // Normal virtual methods don't need a generic lookup. - Debug.Assert(method.OwningType.IsInterface || method.HasInstantiation); - - _method = method; + return comparer.Compare(_method, ((VirtualDispatchCellGenericLookupResult)other)._method); } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + protected override int GetHashCodeImpl() { - if (factory.Target.Abi == TargetAbi.CoreRT) - { - MethodDesc instantiatedMethod = _method.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(typeInstantiation, methodInstantiation); - return factory.InterfaceDispatchCell(instantiatedMethod); - } - else - { - MethodDesc instantiatedMethod = _method.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); - return factory.InterfaceDispatchCell(instantiatedMethod, dictionary.GetMangledName(factory.NameMangler)); - } + return _method.GetHashCode(); } - public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + protected override bool EqualsImpl(GenericLookupResult obj) { - sb.Append("VirtualResolve_"); - sb.Append(nameMangler.GetMangledMethodName(_method)); - } - - public override string ToString() => $"VirtualResolve: {_method}"; - - public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) - { - if (factory.Target.Abi == TargetAbi.CoreRT) - { - // We should be able to get rid of this custom ABI handling - // once https://github.com/dotnet/corert/issues/3248 is fixed. - return factory.NativeLayout.NotSupportedDictionarySlot; - } - else - { - return factory.NativeLayout.InterfaceCellDictionarySlot(_method); - } - } - - public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) - { - if (factory.Target.Abi == TargetAbi.CoreRT) - { - // TODO - throw new NotImplementedException(); - } - else - { - writer.WriteData(LookupResultReferenceType(factory), LookupResultType.InterfaceDispatchCell, _method); - } - } - - protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) - { - return comparer.Compare(_method, ((VirtualResolveGenericLookupResult)other)._method); + return ((VirtualDispatchCellGenericLookupResult)obj)._method == _method; } } @@ -749,9 +837,9 @@ namespace ILCompiler.DependencyAnalysis _type = (MetadataType)type; } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) { - var instantiatedType = (MetadataType)_type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(typeInstantiation, methodInstantiation); + var instantiatedType = (MetadataType)_type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); return factory.Indirection(factory.TypeNonGCStaticsSymbol(instantiatedType)); } @@ -782,6 +870,16 @@ namespace ILCompiler.DependencyAnalysis { return comparer.Compare(_type, ((TypeNonGCStaticBaseGenericLookupResult)other)._type); } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((TypeNonGCStaticBaseGenericLookupResult)obj)._type == _type; + } } /// @@ -800,9 +898,9 @@ namespace ILCompiler.DependencyAnalysis _type = (MetadataType)type; } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) { - var instantiatedType = (MetadataType)_type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(typeInstantiation, methodInstantiation); + var instantiatedType = (MetadataType)_type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); return factory.TypeThreadStaticIndex(instantiatedType); } @@ -829,12 +927,22 @@ namespace ILCompiler.DependencyAnalysis { return comparer.Compare(_type, ((TypeThreadStaticBaseIndexGenericLookupResult)other)._type); } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((TypeThreadStaticBaseIndexGenericLookupResult)obj)._type == _type; + } } /// /// Generic lookup result that points to the GC static base of a type. /// - internal sealed class TypeGCStaticBaseGenericLookupResult : GenericLookupResult + public sealed class TypeGCStaticBaseGenericLookupResult : GenericLookupResult { private MetadataType _type; @@ -847,9 +955,9 @@ namespace ILCompiler.DependencyAnalysis _type = (MetadataType)type; } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) { - var instantiatedType = (MetadataType)_type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(typeInstantiation, methodInstantiation); + var instantiatedType = (MetadataType)_type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); return factory.Indirection(factory.TypeGCStaticsSymbol(instantiatedType)); } @@ -859,6 +967,7 @@ namespace ILCompiler.DependencyAnalysis sb.Append(nameMangler.GetMangledTypeName(_type)); } + public MetadataType Type => _type; public override string ToString() => $"GCStaticBase: {_type}"; public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) @@ -880,6 +989,16 @@ namespace ILCompiler.DependencyAnalysis { return comparer.Compare(_type, ((TypeGCStaticBaseGenericLookupResult)other)._type); } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((TypeGCStaticBaseGenericLookupResult)obj)._type == _type; + } } /// @@ -897,9 +1016,9 @@ namespace ILCompiler.DependencyAnalysis _type = type; } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) { - TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(typeInstantiation, methodInstantiation); + TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); return factory.Indirection(factory.ExternSymbol(JitHelper.GetNewObjectHelperForType(instantiatedType))); } @@ -930,6 +1049,16 @@ namespace ILCompiler.DependencyAnalysis { return comparer.Compare(_type, ((ObjectAllocatorGenericLookupResult)other)._type); } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((ObjectAllocatorGenericLookupResult)obj)._type == _type; + } } /// @@ -947,9 +1076,9 @@ namespace ILCompiler.DependencyAnalysis _type = type; } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) { - TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(typeInstantiation, methodInstantiation); + TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); Debug.Assert(instantiatedType.IsArray); return factory.Indirection(factory.ExternSymbol(JitHelper.GetNewArrayHelperForType(instantiatedType))); } @@ -981,6 +1110,16 @@ namespace ILCompiler.DependencyAnalysis { return comparer.Compare(_type, ((ArrayAllocatorGenericLookupResult)other)._type); } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((ArrayAllocatorGenericLookupResult)obj)._type == _type; + } } /// @@ -998,9 +1137,9 @@ namespace ILCompiler.DependencyAnalysis _type = type; } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) { - TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(typeInstantiation, methodInstantiation); + TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); return factory.Indirection(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(instantiatedType, true))); } @@ -1031,8 +1170,18 @@ namespace ILCompiler.DependencyAnalysis { return comparer.Compare(_type, ((CastClassGenericLookupResult)other)._type); } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((CastClassGenericLookupResult)obj)._type == _type; + } } - + /// /// Generic lookup result that points to an isInst helper. /// @@ -1048,9 +1197,9 @@ namespace ILCompiler.DependencyAnalysis _type = type; } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) { - TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(typeInstantiation, methodInstantiation); + TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); return factory.Indirection(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(instantiatedType, false))); } @@ -1081,6 +1230,16 @@ namespace ILCompiler.DependencyAnalysis { return comparer.Compare(_type, ((IsInstGenericLookupResult)other)._type); } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((IsInstGenericLookupResult)obj)._type == _type; + } } internal sealed class ThreadStaticIndexLookupResult : GenericLookupResult @@ -1095,12 +1254,12 @@ namespace ILCompiler.DependencyAnalysis _type = type; } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) { UtcNodeFactory utcNodeFactory = factory as UtcNodeFactory; Debug.Assert(utcNodeFactory != null); - TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(typeInstantiation, methodInstantiation); - return factory.Indirection(utcNodeFactory.TypeThreadStaticsIndexSymbol(instantiatedType)); + TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); + return utcNodeFactory.TypeThreadStaticsIndexSymbol((MetadataType)instantiatedType); } public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) @@ -1118,7 +1277,7 @@ namespace ILCompiler.DependencyAnalysis public override GenericLookupResultReferenceType LookupResultReferenceType(NodeFactory factory) { - return GenericLookupResultReferenceType.Indirect; + return GenericLookupResultReferenceType.ConditionalIndirect; } public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) @@ -1130,9 +1289,19 @@ namespace ILCompiler.DependencyAnalysis { return comparer.Compare(_type, ((ThreadStaticIndexLookupResult)other)._type); } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((ThreadStaticIndexLookupResult)obj)._type == _type; + } } - internal sealed class ThreadStaticOffsetLookupResult : GenericLookupResult + public sealed class ThreadStaticOffsetLookupResult : GenericLookupResult { private TypeDesc _type; @@ -1144,13 +1313,13 @@ namespace ILCompiler.DependencyAnalysis _type = type; } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) { UtcNodeFactory utcNodeFactory = factory as UtcNodeFactory; Debug.Assert(utcNodeFactory != null); - TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(typeInstantiation, methodInstantiation); + TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); Debug.Assert(instantiatedType is MetadataType); - return factory.Indirection(utcNodeFactory.TypeThreadStaticsOffsetSymbol((MetadataType)instantiatedType)); + return utcNodeFactory.TypeThreadStaticsOffsetSymbol((MetadataType)instantiatedType); } public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) @@ -1159,6 +1328,7 @@ namespace ILCompiler.DependencyAnalysis sb.Append(nameMangler.GetMangledTypeName(_type)); } + public TypeDesc Type => _type; public override string ToString() => $"ThreadStaticOffset: {_type}"; public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) @@ -1168,7 +1338,7 @@ namespace ILCompiler.DependencyAnalysis public override GenericLookupResultReferenceType LookupResultReferenceType(NodeFactory factory) { - return GenericLookupResultReferenceType.Indirect; + return GenericLookupResultReferenceType.ConditionalIndirect; } public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) @@ -1180,6 +1350,16 @@ namespace ILCompiler.DependencyAnalysis { return comparer.Compare(_type, ((ThreadStaticOffsetLookupResult)other)._type); } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((ThreadStaticOffsetLookupResult)obj)._type == _type; + } } internal sealed class DefaultConstructorLookupResult : GenericLookupResult @@ -1194,17 +1374,16 @@ namespace ILCompiler.DependencyAnalysis _type = type; } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) { - TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(typeInstantiation, methodInstantiation); + TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); MethodDesc defaultCtor = instantiatedType.GetDefaultConstructor(); if (defaultCtor == null) { // If there isn't a default constructor, use the fallback one. MetadataType missingCtorType = factory.TypeSystemContext.SystemModule.GetKnownType("System", "Activator"); - missingCtorType = missingCtorType.GetNestedType("ClassWithMissingConstructor"); - Debug.Assert(missingCtorType != null); + missingCtorType = missingCtorType.GetKnownNestedType("ClassWithMissingConstructor"); defaultCtor = missingCtorType.GetParameterlessConstructor(); } else @@ -1237,6 +1416,16 @@ namespace ILCompiler.DependencyAnalysis { return comparer.Compare(_type, ((DefaultConstructorLookupResult)other)._type); } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((DefaultConstructorLookupResult)obj)._type == _type; + } } internal sealed class CallingConventionConverterLookupResult : GenericLookupResult @@ -1251,15 +1440,15 @@ namespace ILCompiler.DependencyAnalysis Debug.Assert(Internal.Runtime.UniversalGenericParameterLayout.MethodSignatureHasVarsNeedingCallingConventionConverter(callingConventionConverter.Signature)); } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) { - Debug.Assert(false, "GetTarget for a CallingConventionConverterLookupResult doesn't make sense. It isn't a pointer being emitted"); + Debug.Fail("GetTarget for a CallingConventionConverterLookupResult doesn't make sense. It isn't a pointer being emitted"); return null; } - public override void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, GenericLookupResultContext dictionary, GenericDictionaryNode dictionaryNode) { - Debug.Assert(false, "CallingConventionConverterLookupResult contents should only be generated into generic dictionaries at runtime"); + Debug.Fail("CallingConventionConverterLookupResult contents should only be generated into generic dictionaries at runtime"); builder.EmitNaturalInt(0); } @@ -1291,6 +1480,16 @@ namespace ILCompiler.DependencyAnalysis return comparer.Compare(_callingConventionConverter.Signature, otherEntry._callingConventionConverter.Signature); } + + protected override int GetHashCodeImpl() + { + return _callingConventionConverter.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((CallingConventionConverterLookupResult)obj)._callingConventionConverter.Equals(_callingConventionConverter); + } } internal sealed class TypeSizeLookupResult : GenericLookupResult @@ -1304,15 +1503,15 @@ namespace ILCompiler.DependencyAnalysis _type = type; Debug.Assert(type.IsRuntimeDeterminedSubtype, "Concrete type in a generic dictionary?"); } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) { - Debug.Assert(false, "GetTarget for a TypeSizeLookupResult doesn't make sense. It isn't a pointer being emitted"); + Debug.Fail("GetTarget for a TypeSizeLookupResult doesn't make sense. It isn't a pointer being emitted"); return null; } - public override void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, GenericLookupResultContext dictionary, GenericDictionaryNode dictionaryNode) { - TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(typeInstantiation, methodInstantiation); + TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); int typeSize; if (_type.IsDefType) @@ -1349,6 +1548,16 @@ namespace ILCompiler.DependencyAnalysis { return comparer.Compare(_type, ((TypeSizeLookupResult)other)._type); } + + protected override int GetHashCodeImpl() + { + return _type.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + return ((TypeSizeLookupResult)obj)._type == _type; + } } internal sealed class ConstrainedMethodUseLookupResult : GenericLookupResult @@ -1369,12 +1578,18 @@ namespace ILCompiler.DependencyAnalysis Debug.Assert(!_constrainedMethod.HasInstantiation || !_directCall, "Direct call to constrained generic method isn't supported"); } - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, GenericDictionaryNode dictionary) + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) { - MethodDesc instantiatedConstrainedMethod = _constrainedMethod.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(typeInstantiation, methodInstantiation); - TypeDesc instantiatedConstraintType = _constraintType.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(typeInstantiation, methodInstantiation); + MethodDesc instantiatedConstrainedMethod = _constrainedMethod.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); + TypeDesc instantiatedConstraintType = _constraintType.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); + MethodDesc implMethod = instantiatedConstrainedMethod; - MethodDesc implMethod = instantiatedConstraintType.GetClosestDefType().ResolveInterfaceMethodToVirtualMethodOnType(instantiatedConstrainedMethod); + if (implMethod.OwningType.IsInterface) + { + implMethod = instantiatedConstraintType.GetClosestDefType().ResolveVariantInterfaceMethodToVirtualMethodOnType(implMethod); + } + + implMethod = instantiatedConstraintType.GetClosestDefType().FindVirtualFunctionTargetMethodOnObjectType(implMethod); // AOT use of this generic lookup is restricted to finding methods on valuetypes (runtime usage of this slot in universal generics is more flexible) Debug.Assert(instantiatedConstraintType.IsValueType); @@ -1386,7 +1601,7 @@ namespace ILCompiler.DependencyAnalysis } else { - return factory.MethodEntrypoint(implMethod); + return factory.CanonicalEntrypoint(implMethod); } } @@ -1425,5 +1640,147 @@ namespace ILCompiler.DependencyAnalysis return comparer.Compare(_constrainedMethod, otherResult._constrainedMethod); } + + protected override int GetHashCodeImpl() + { + return _constrainedMethod.GetHashCode() * 13 + _constraintType.GetHashCode(); + } + + protected override bool EqualsImpl(GenericLookupResult obj) + { + var other = (ConstrainedMethodUseLookupResult)obj; + return _constrainedMethod == other._constrainedMethod && + _constraintType == other._constraintType && + _directCall == other._directCall; + } + } + + public sealed class IntegerLookupResult : GenericLookupResult + { + int _integerValue; + + public IntegerLookupResult(int integer) + { + _integerValue = integer; + } + + public int IntegerValue => _integerValue; + + protected override int ClassCode => 385752509; + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + return null; + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("IntegerLookupResult_").Append(_integerValue.ToString("x")); + } + + public override string ToString() + { + return "IntegerLookupResult_" + _integerValue.ToString("x"); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + IntegerLookupResult lookupResultOther = (IntegerLookupResult)other; + if (lookupResultOther._integerValue == _integerValue) + return 0; + + return _integerValue > lookupResultOther._integerValue ? 1 : -1; + } + + protected override bool EqualsImpl(GenericLookupResult other) + { + IntegerLookupResult lookupResultOther = (IntegerLookupResult)other; + return lookupResultOther._integerValue == _integerValue; + } + + protected override int GetHashCodeImpl() + { + return _integerValue; + } + + public override void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, GenericLookupResultContext dictionary, GenericDictionaryNode dictionaryNode) + { + builder.EmitNaturalInt(_integerValue); + } + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.IntegerSlot(_integerValue); + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + writer.WriteIntegerSlot(_integerValue); + } + } + + public sealed class PointerToSlotLookupResult : GenericLookupResult + { + int _slotIndex; + + public PointerToSlotLookupResult(int slotIndex) + { + _slotIndex = slotIndex; + } + + public int SlotIndex => _slotIndex; + + protected override int ClassCode => 551050755; + + public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary) + { + return null; + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("PointerToSlotLookupResult_").Append(_slotIndex.ToString("x")); + } + + public override string ToString() + { + return "PointerToSlotLookupResult_" + _slotIndex.ToString("x"); + } + + protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) + { + PointerToSlotLookupResult pointerToSlotResultOther = (PointerToSlotLookupResult)other; + if (pointerToSlotResultOther._slotIndex == _slotIndex) + return 0; + + return _slotIndex > pointerToSlotResultOther._slotIndex ? 1 : -1; + } + + protected override bool EqualsImpl(GenericLookupResult other) + { + PointerToSlotLookupResult pointerToSlotResultOther = (PointerToSlotLookupResult)other; + return pointerToSlotResultOther._slotIndex == _slotIndex; + } + + protected override int GetHashCodeImpl() + { + return _slotIndex; + } + + public override void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, GenericLookupResultContext dictionary, GenericDictionaryNode dictionaryNode) + { + builder.EmitPointerReloc(dictionaryNode, _slotIndex * factory.Target.PointerSize); + } + + public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) + { + return factory.NativeLayout.PointerToOtherSlot(_slotIndex); + } + + public override void WriteDictionaryTocData(NodeFactory factory, IGenericLookupResultTocWriter writer) + { + // Under no circumstance should we attempt to write out a pointer to slot result + throw new InvalidProgramException(); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericMethodsHashtableNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericMethodsHashtableNode.cs index f5ca8ce690..870f489982 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericMethodsHashtableNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericMethodsHashtableNode.cs @@ -35,6 +35,7 @@ namespace ILCompiler.DependencyAnalysis public override bool IsShareable => false; public override ObjectNodeSection Section => _externalReferences.Section; public override bool StaticDependenciesAreComputed => true; + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection; protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) @@ -100,6 +101,9 @@ namespace ILCompiler.DependencyAnalysis public static void GetGenericMethodsHashtableDependenciesForMethod(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) { + if (!factory.MetadataManager.SupportsReflection) + return; + Debug.Assert(method.HasInstantiation && !method.IsCanonicalMethod(CanonicalFormKind.Any)); // Method's containing type @@ -121,5 +125,8 @@ namespace ILCompiler.DependencyAnalysis ISymbolNode dictionaryNode = factory.MethodGenericDictionary(method); dependencies.Add(new DependencyListEntry(dictionaryNode, "GenericMethodsHashtable entry dictionary")); } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.GenericMethodsHashtableNode; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericMethodsTemplateMap.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericMethodsTemplateMap.cs index 4144f908b7..7a92d5dce5 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericMethodsTemplateMap.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericMethodsTemplateMap.cs @@ -35,6 +35,7 @@ namespace ILCompiler.DependencyAnalysis public override bool IsShareable => false; public override ObjectNodeSection Section => _externalReferences.Section; public override bool StaticDependenciesAreComputed => true; + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection; protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) @@ -85,6 +86,9 @@ namespace ILCompiler.DependencyAnalysis if (!IsEligibleToBeATemplate(method)) return; + if (!factory.MetadataManager.SupportsReflection) + return; + dependencies = dependencies ?? new DependencyList(); dependencies.Add(new DependencyListEntry(factory.NativeLayout.TemplateMethodEntry(method), "Template Method Entry")); dependencies.Add(new DependencyListEntry(factory.NativeLayout.TemplateMethodLayout(method), "Template Method Layout")); @@ -95,6 +99,9 @@ namespace ILCompiler.DependencyAnalysis if (!method.HasInstantiation) return false; + if (method.IsAbstract) + return false; + if (method.IsCanonicalMethod(CanonicalFormKind.Specific)) { // Must be fully canonical @@ -110,5 +117,8 @@ namespace ILCompiler.DependencyAnalysis return false; } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.GenericMethodsTemplateMap; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericTypesHashtableNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericTypesHashtableNode.cs index 9b6ed2288f..f48ec5dd79 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericTypesHashtableNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericTypesHashtableNode.cs @@ -34,6 +34,7 @@ namespace ILCompiler.DependencyAnalysis public override bool IsShareable => false; public override ObjectNodeSection Section => _externalReferences.Section; public override bool StaticDependenciesAreComputed => true; + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection; protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) @@ -66,5 +67,8 @@ namespace ILCompiler.DependencyAnalysis return new ObjectData(streamBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.GenericTypesHashtableNode; } } \ No newline at end of file diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericTypesTemplateMap.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericTypesTemplateMap.cs index 35bc2afa6d..2be294a156 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericTypesTemplateMap.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericTypesTemplateMap.cs @@ -6,6 +6,7 @@ using System; using System.Diagnostics; using Internal.Text; +using ILCompiler.DependencyAnalysisFramework; using Internal.TypeSystem; using Internal.NativeFormat; @@ -35,6 +36,7 @@ namespace ILCompiler.DependencyAnalysis public override bool IsShareable => false; public override ObjectNodeSection Section => _externalReferences.Section; public override bool StaticDependenciesAreComputed => true; + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection; protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) @@ -56,11 +58,13 @@ namespace ILCompiler.DependencyAnalysis if (!IsEligibleToHaveATemplate(type)) continue; - if (factory.Target.Abi == TargetAbi.ProjectN) + if ((factory.Target.Abi == TargetAbi.ProjectN) && !ProjectNDependencyBehavior.EnableFullAnalysis) { - // If the type does not have fully constructed type, don't emit it. + // If the type does not have fully constructed type, don't track its dependencies. // TODO: Remove the workaround once we stop using the STS dependency analysis. - if (!factory.ConstructedTypeSymbol(type).Marked) + IDependencyNode node = factory.MaximallyConstructableType(type); + + if (!node.Marked) continue; } @@ -91,16 +95,21 @@ namespace ILCompiler.DependencyAnalysis public static void GetTemplateTypeDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) { + if (!factory.MetadataManager.SupportsReflection) + return; + TypeDesc templateType = ConvertArrayOfTToRegularArray(factory, type); if (!IsEligibleToHaveATemplate(templateType)) return; - if (factory.Target.Abi == TargetAbi.ProjectN) + if ((factory.Target.Abi == TargetAbi.ProjectN) && !ProjectNDependencyBehavior.EnableFullAnalysis) { // If the type does not have fully constructed type, don't track its dependencies. // TODO: Remove the workaround once we stop using the STS dependency analysis. - if (!factory.ConstructedTypeSymbol(templateType).Marked) + IDependencyNode node = factory.MaximallyConstructableType(templateType); + + if (!node.Marked) return; } @@ -150,5 +159,8 @@ namespace ILCompiler.DependencyAnalysis return false; } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.GenericTypesTemplateMap; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericVirtualMethodTableNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericVirtualMethodTableNode.cs index 51187a2131..7e7e582a48 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericVirtualMethodTableNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericVirtualMethodTableNode.cs @@ -38,6 +38,7 @@ namespace ILCompiler.DependencyAnalysis public override bool IsShareable => false; public override ObjectNodeSection Section => _externalReferences.Section; public override bool StaticDependenciesAreComputed => true; + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection; protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); /// @@ -50,7 +51,10 @@ namespace ILCompiler.DependencyAnalysis public static void GetGenericVirtualMethodImplementationDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc callingMethod, MethodDesc implementationMethod) { Debug.Assert(!callingMethod.OwningType.IsInterface); - + + if (!factory.MetadataManager.SupportsReflection) + return; + // Compute the open method signatures MethodDesc openCallingMethod = callingMethod.GetTypicalMethodDefinition(); MethodDesc openImplementationMethod = implementationMethod.GetTypicalMethodDefinition(); @@ -140,5 +144,8 @@ namespace ILCompiler.DependencyAnalysis return new ObjectData(streamBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.GenericVirtualMethodTableNode; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IEETypeNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IEETypeNode.cs index 6da67b63a4..4aee5b975d 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IEETypeNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IEETypeNode.cs @@ -9,7 +9,7 @@ namespace ILCompiler.DependencyAnalysis /// /// A dependency analysis node that represents a runtime type data structure. /// - public interface IEETypeNode : ISymbolNode + public interface IEETypeNode : ISortableSymbolNode { TypeDesc Type { get; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IExportableSymbolNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IExportableSymbolNode.cs index 65613dbb3c..7358eb4b9b 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IExportableSymbolNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IExportableSymbolNode.cs @@ -11,9 +11,10 @@ namespace ILCompiler.DependencyAnalysis public interface IExportableSymbolNode : ISymbolDefinitionNode { /// - /// Set the return value of this property to true to indicate that this symbol - /// is exported and will be referenced by external modules. + /// Set the return value of this property to non-ExportForm.None to indicate that this symbol + /// is exported and will be referenced by external modules. The values of the enum indicate what form + /// of export is to be used. /// - bool IsExported(NodeFactory factory); + ExportForm GetExportForm(NodeFactory factory); } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ILScanNodeFactory.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ILScanNodeFactory.cs index a920c67321..6f061c02f6 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ILScanNodeFactory.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ILScanNodeFactory.cs @@ -15,8 +15,8 @@ namespace ILCompiler.DependencyAnalysis /// public sealed class ILScanNodeFactory : NodeFactory { - public ILScanNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager, NameMangler nameMangler) - : base(context, compilationModuleGroup, metadataManager, nameMangler, new LazyGenericsDisabledPolicy()) + public ILScanNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager, InteropStubManager interopStubManager, NameMangler nameMangler) + : base(context, compilationModuleGroup, metadataManager, interopStubManager, nameMangler, new LazyGenericsDisabledPolicy(), new LazyVTableSliceProvider(), new LazyDictionaryLayoutProvider(), new ExternSymbolsImportedNodeProvider()) { } @@ -40,10 +40,10 @@ namespace ILCompiler.DependencyAnalysis // On CLR this would throw a SecurityException with "ECall methods must be packaged into a system module." // This is a corner case that nobody is likely to care about. - throw new TypeSystemException.InvalidProgramException(ExceptionStringID.InvalidProgramSpecific, method); + ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramSpecific, method); } - if (CompilationModuleGroup.ContainsMethodBody(method)) + if (CompilationModuleGroup.ContainsMethodBody(method, false)) { return new ScannedMethodNode(method); } @@ -68,7 +68,7 @@ namespace ILCompiler.DependencyAnalysis else { // Otherwise we just unbox 'this' and don't touch anything else. - return new UnboxingStubNode(method); + return new UnboxingStubNode(method, Target); } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IMethodBodyNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IMethodBodyNode.cs new file mode 100644 index 0000000000..b862f59b49 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IMethodBodyNode.cs @@ -0,0 +1,13 @@ +// 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. + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Marker interface that identifies the node representing a compiled method body. + /// + public interface IMethodBodyNode : IMethodNode + { + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IMethodBodyNodeWithFuncletSymbols.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IMethodBodyNodeWithFuncletSymbols.cs new file mode 100644 index 0000000000..6537ff9ec6 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IMethodBodyNodeWithFuncletSymbols.cs @@ -0,0 +1,14 @@ +// 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. + +namespace ILCompiler.DependencyAnalysis +{ + public interface IMethodBodyNodeWithFuncletSymbols : IMethodBodyNode + { + /// + /// Symbols of any funclets associated with this method. + /// + ISymbolNode[] FuncletSymbols { get; } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IMethodNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IMethodNode.cs index be4edd7938..393ee4adf5 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IMethodNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IMethodNode.cs @@ -9,7 +9,7 @@ namespace ILCompiler.DependencyAnalysis /// /// A dependency analysis node that represents a method. /// - public interface IMethodNode : ISymbolNode + public interface IMethodNode : ISortableSymbolNode { MethodDesc Method { get; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/INodeWithCodeInfo.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/INodeWithCodeInfo.cs index ffa254e76a..48b48e656f 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/INodeWithCodeInfo.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/INodeWithCodeInfo.cs @@ -9,11 +9,12 @@ namespace ILCompiler.DependencyAnalysis [Flags] public enum FrameInfoFlags { - Handler = 0x01, - Filter = 0x02, + Handler = 0x01, + Filter = 0x02, - HasEHInfo = 0x04, - ReversePInvoke = 0x08, + HasEHInfo = 0x04, + ReversePInvoke = 0x08, + HasAssociatedData = 0x10, } public struct FrameInfo @@ -48,5 +49,7 @@ namespace ILCompiler.DependencyAnalysis { get; } + + ISymbolNode GetAssociatedDataNode(NodeFactory factory); } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/INodeWithDebugInfo.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/INodeWithDebugInfo.cs index 1fd29063bc..7a379e8812 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/INodeWithDebugInfo.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/INodeWithDebugInfo.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Runtime.InteropServices; + using Internal.JitInterface; using Internal.TypeSystem; @@ -53,4 +53,17 @@ namespace ILCompiler.DependencyAnalysis this.Ranges = new List(); } } + + public static class WellKnownLineNumber + { + /// + /// Informs the debugger that it should step through the annotated sequence point. + /// + public const int DebuggerStepThrough = 0xF00F00; + + /// + /// Informs the debugger that it should step into the annotated sequence point. + /// + public const int DebuggerStepIn = 0xFEEFEE; + } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IObjectDumper.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IObjectDumper.cs index 0822d7f5bc..e694acf527 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IObjectDumper.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IObjectDumper.cs @@ -6,7 +6,7 @@ using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData; namespace ILCompiler.DependencyAnalysis { - internal interface IObjectDumper + public interface IObjectDumper { void DumpObjectNode(NameMangler mangler, ObjectNode node, ObjectData objectData); } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ISpecialUnboxThunkNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ISpecialUnboxThunkNode.cs new file mode 100644 index 0000000000..3c8b62513c --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ISpecialUnboxThunkNode.cs @@ -0,0 +1,17 @@ +// 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 Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// A dependency analysis node that represents a special instantiating unboxing stub. + /// + public interface ISpecialUnboxThunkNode : IMethodNode + { + bool IsSpecialUnboxingThunk { get; } + ISymbolNode GetUnboxingThunkTarget(NodeFactory factory); + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ISymbolNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ISymbolNode.cs index 7cfaf23448..af38e11d66 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ISymbolNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ISymbolNode.cs @@ -32,6 +32,16 @@ namespace ILCompiler.DependencyAnalysis bool RepresentsIndirectionCell { get; } } + + public interface ISortableSymbolNode : ISymbolNode + { +#if !SUPPORT_JIT + int ClassCode { get; } + int CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer); +#endif + } + + /// /// Represents a definition of a symbol within an . The symbol will be defined /// at the specified offset from the beginning of the that reports this as one of diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ISymbolNodeWithFuncletId.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ISymbolNodeWithFuncletId.cs new file mode 100644 index 0000000000..54c816faa0 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ISymbolNodeWithFuncletId.cs @@ -0,0 +1,19 @@ +// 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 ILCompiler.DependencyAnalysisFramework; +using Internal.Text; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a reference to a symbol attributed with a funclet id. + /// + public interface ISymbolNodeWithFuncletId : ISymbolNode + { + ISymbolNode AssociatedMethodSymbol { get; } + int FuncletId { get; } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ImportedNodeProvider.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ImportedNodeProvider.cs new file mode 100644 index 0000000000..bbf057f865 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ImportedNodeProvider.cs @@ -0,0 +1,70 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Abstract api to allow creation of various different types of import nodes as might be exposed through the NodeFactory + /// + public abstract class ImportedNodeProvider + { + public abstract IEETypeNode ImportedEETypeNode(NodeFactory factory, TypeDesc type); + public abstract ISortableSymbolNode ImportedGCStaticNode(NodeFactory factory, MetadataType type); + public abstract ISortableSymbolNode ImportedNonGCStaticNode(NodeFactory factory, MetadataType type); + public abstract ISortableSymbolNode ImportedThreadStaticOffsetNode(NodeFactory factory, MetadataType type); + public abstract ISortableSymbolNode ImportedThreadStaticIndexNode(NodeFactory factory, MetadataType type); + public abstract ISortableSymbolNode ImportedTypeDictionaryNode(NodeFactory factory, TypeDesc type); + public abstract ISortableSymbolNode ImportedMethodDictionaryNode(NodeFactory factory, MethodDesc method); + public abstract IMethodNode ImportedMethodCodeNode(NodeFactory factory, MethodDesc method, bool unboxingStub); + } + + public class ImportedNodeProviderThrowing : ImportedNodeProvider + { + public override IEETypeNode ImportedEETypeNode(NodeFactory factory, TypeDesc type) + { + throw new NotSupportedException(); + } + + public override ISortableSymbolNode ImportedGCStaticNode(NodeFactory factory, MetadataType type) + { + throw new NotSupportedException(); + } + + public override ISortableSymbolNode ImportedNonGCStaticNode(NodeFactory factory, MetadataType type) + { + throw new NotSupportedException(); + } + + public override ISortableSymbolNode ImportedThreadStaticOffsetNode(NodeFactory factory, MetadataType type) + { + throw new NotSupportedException(); + } + + public override ISortableSymbolNode ImportedThreadStaticIndexNode(NodeFactory factory, MetadataType type) + { + throw new NotSupportedException(); + } + + public override ISortableSymbolNode ImportedTypeDictionaryNode(NodeFactory factory, TypeDesc type) + { + throw new NotSupportedException(); + } + + public override ISortableSymbolNode ImportedMethodDictionaryNode(NodeFactory factory, MethodDesc method) + { + throw new NotSupportedException(); + } + + public override IMethodNode ImportedMethodCodeNode(NodeFactory factory, MethodDesc method, bool unboxingStub) + { + throw new NotSupportedException(); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IndirectionNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IndirectionNode.cs index b62b4a5113..77bd897eac 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IndirectionNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/IndirectionNode.cs @@ -14,11 +14,11 @@ namespace ILCompiler.DependencyAnalysis /// public class IndirectionNode : ObjectNode, ISymbolDefinitionNode { - private ISymbolNode _indirectedNode; + private ISortableSymbolNode _indirectedNode; private int _offsetDelta; private TargetDetails _target; - public IndirectionNode(TargetDetails target, ISymbolNode indirectedNode, int offsetDelta) + public IndirectionNode(TargetDetails target, ISortableSymbolNode indirectedNode, int offsetDelta) { _indirectedNode = indirectedNode; _offsetDelta = offsetDelta; @@ -60,5 +60,12 @@ namespace ILCompiler.DependencyAnalysis return builder.ToObjectData(); } + + protected internal override int ClassCode => -1401349230; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return comparer.Compare(this._indirectedNode, ((IndirectionNode)other)._indirectedNode); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs index 007ee65ed1..a5c49fc66c 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs @@ -2,7 +2,6 @@ // 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.Diagnostics; using Internal.Runtime; @@ -13,8 +12,8 @@ namespace ILCompiler.DependencyAnalysis { public class InterfaceDispatchCellNode : ObjectNode, ISymbolDefinitionNode { - MethodDesc _targetMethod; - string _callSiteIdentifier; + private readonly MethodDesc _targetMethod; + private readonly string _callSiteIdentifier; public InterfaceDispatchCellNode(MethodDesc targetMethod, string callSiteIdentifier) { @@ -49,7 +48,21 @@ namespace ILCompiler.DependencyAnalysis public override ObjectNodeSection Section => ObjectNodeSection.DataSection; public override bool StaticDependenciesAreComputed => true; - + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList result = new DependencyList(); + + if (!factory.VTable(_targetMethod.OwningType).HasFixedSlots) + { + result.Add(factory.VirtualMethodUse(_targetMethod), "Interface method use"); + } + + factory.MetadataManager.GetDependenciesDueToVirtualMethodReflectability(ref result, factory, _targetMethod); + + return result; + } + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); @@ -58,7 +71,9 @@ namespace ILCompiler.DependencyAnalysis objData.RequireInitialAlignment(_targetMethod.Context.Target.PointerSize * 2); objData.AddSymbol(this); - if (factory.Target.Architecture == TargetArchitecture.ARM) + TargetArchitecture targetArchitecture = factory.Target.Architecture; + if (targetArchitecture == TargetArchitecture.ARM || + targetArchitecture == TargetArchitecture.ARMEL) { objData.EmitPointerReloc(factory.InitialInterfaceDispatchStub); } @@ -98,5 +113,13 @@ namespace ILCompiler.DependencyAnalysis return objData.ToObjectData(); } + + protected internal override int ClassCode => -2023802120; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + var compare = comparer.Compare(_targetMethod, ((InterfaceDispatchCellNode)other)._targetMethod); + return compare != 0 ? compare : string.Compare(_callSiteIdentifier, ((InterfaceDispatchCellNode)other)._callSiteIdentifier); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs index 089d59418b..a406b1be5f 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs @@ -11,29 +11,20 @@ using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis { - public class InterfaceDispatchMapNode : ObjectNode, ISymbolDefinitionNode + public class InterfaceDispatchMapNode : ObjectNode, ISymbolDefinitionNode, ISortableSymbolNode { - const int IndexNotSet = int.MaxValue; - - int _dispatchMapTableIndex; TypeDesc _type; public InterfaceDispatchMapNode(TypeDesc type) { _type = type; - _dispatchMapTableIndex = IndexNotSet; } protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { - if (_dispatchMapTableIndex == IndexNotSet) - { - throw new InvalidOperationException("MangledName called before InterfaceDispatchMap index was initialized."); - } - - sb.Append(nameMangler.CompilationUnitPrefix).Append("__InterfaceDispatchMap_").Append(_dispatchMapTableIndex.ToStringInvariant()); + sb.Append(nameMangler.CompilationUnitPrefix).Append("__InterfaceDispatchMap_").Append(nameMangler.SanitizeName(nameMangler.GetMangledTypeName(_type))); } public int Offset => 0; @@ -51,17 +42,18 @@ namespace ILCompiler.DependencyAnalysis return ObjectNodeSection.DataSection; } } - - public void SetDispatchMapIndex(NodeFactory factory, int index) - { - _dispatchMapTableIndex = index; - ((EETypeNode)factory.ConstructedTypeSymbol(_type)).SetDispatchMapIndex(_dispatchMapTableIndex); - } - + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { var result = new DependencyList(); result.Add(factory.InterfaceDispatchMapIndirection(_type), "Interface dispatch map indirection node"); + + // VTable slots of implemented interfaces are consulted during emission + foreach (TypeDesc runtimeInterface in _type.NormalizedRuntimeInterfaces()) + { + result.Add(factory.VTable(runtimeInterface), "Interface for a dispatch map"); + } + return result; } @@ -69,10 +61,10 @@ namespace ILCompiler.DependencyAnalysis { var entryCountReservation = builder.ReserveInt(); int entryCount = 0; + int interfaceIndex = 0; - for (int interfaceIndex = 0; interfaceIndex < _type.RuntimeInterfaces.Length; interfaceIndex++) + foreach (var interfaceType in _type.NormalizedRuntimeInterfaces()) { - var interfaceType = _type.RuntimeInterfaces[interfaceIndex]; Debug.Assert(interfaceType.IsInterface); IReadOnlyList virtualSlots = factory.VTable(interfaceType).Slots; @@ -88,10 +80,12 @@ namespace ILCompiler.DependencyAnalysis { builder.EmitShort(checked((short)interfaceIndex)); builder.EmitShort(checked((short)(interfaceMethodSlot + (interfaceType.HasGenericDictionarySlot() ? 1 : 0)))); - builder.EmitShort(checked((short)VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, implMethod))); + builder.EmitShort(checked((short)VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, implMethod.Normalize()))); entryCount++; } } + + interfaceIndex++; } builder.EmitInt(entryCountReservation, entryCount); @@ -110,5 +104,19 @@ namespace ILCompiler.DependencyAnalysis return objData.ToObjectData(); } + + protected internal override int ClassCode => 848664602; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return comparer.Compare(_type, ((InterfaceDispatchMapNode)other)._type); + } + + int ISortableSymbolNode.ClassCode => ClassCode; + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + return CompareToImpl((ObjectNode)other, comparer); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs index 26a743e13c..3fd60f7bda 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs @@ -39,6 +39,7 @@ namespace ILCompiler.DependencyAnalysis public override bool IsShareable => false; public override ObjectNodeSection Section => _externalReferences.Section; public override bool StaticDependenciesAreComputed => true; + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection; protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); /// @@ -51,7 +52,10 @@ namespace ILCompiler.DependencyAnalysis public static void GetGenericVirtualMethodImplementationDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc callingMethod, TypeDesc implementationType, MethodDesc implementationMethod) { Debug.Assert(callingMethod.OwningType.IsInterface); - + + if (!factory.MetadataManager.SupportsReflection) + return; + // Compute the open method signatures MethodDesc openCallingMethod = callingMethod.GetTypicalMethodDefinition(); MethodDesc openImplementationMethod = implementationMethod.GetTypicalMethodDefinition(); @@ -209,5 +213,8 @@ namespace ILCompiler.DependencyAnalysis return new ObjectData(streamBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.InterfaceGenericVirtualMethodTableNode; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/JumpStubNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/JumpStubNode.cs new file mode 100644 index 0000000000..abc13496c7 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/JumpStubNode.cs @@ -0,0 +1,28 @@ +// 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. + +namespace ILCompiler.DependencyAnalysis +{ + public abstract partial class JumpStubNode : AssemblyStubNode + { + private ISymbolNode _target; + + public ISymbolNode Target + { + get + { + return _target; + } + } + + public JumpStubNode(ISymbolNode target) + { + _target = target; + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + protected internal override int ClassCode => 737788182; + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/LoopHijackFlagNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/LoopHijackFlagNode.cs new file mode 100644 index 0000000000..69b1bd7649 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/LoopHijackFlagNode.cs @@ -0,0 +1,51 @@ +// 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 Internal.Text; + +namespace ILCompiler.DependencyAnalysis +{ + public class LoopHijackFlagNode : ObjectNode, ISymbolDefinitionNode + { + public LoopHijackFlagNode() + { + } + + public int Offset => 0; + + protected override string GetName(NodeFactory factory) => "LoopHijackFlag"; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("LoopHijackFlag"); + } + + public override ObjectNodeSection Section + { + get + { + return ObjectNodeSection.DataSection; + } + } + + public override bool IsShareable => true; + + public override bool StaticDependenciesAreComputed => true; + + protected internal override int ClassCode => -266743363; + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // Emit a 4-byte integer flag with initial value of 0. + // TODO: define it as "comdat select any" when multiple object files present. + ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); + objData.RequireInitialAlignment(4); + objData.AddSymbol(this); + objData.EmitInt(0); + return objData.ToObjectData(); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MetadataNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MetadataNode.cs index 97d23f17e9..46bb90e094 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MetadataNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MetadataNode.cs @@ -5,6 +5,7 @@ using System; using Internal.Text; +using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis { @@ -34,6 +35,8 @@ namespace ILCompiler.DependencyAnalysis public override bool StaticDependenciesAreComputed => true; + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection; + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) @@ -55,5 +58,8 @@ namespace ILCompiler.DependencyAnalysis _endSymbol }); } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.MetadataNode; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodAssociatedDataNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodAssociatedDataNode.cs new file mode 100644 index 0000000000..46dacad9e5 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodAssociatedDataNode.cs @@ -0,0 +1,90 @@ +// 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.Diagnostics; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.TypeSystem.Interop; + +namespace ILCompiler.DependencyAnalysis +{ + [Flags] + public enum AssociatedDataFlags : byte + { + None = 0, + HasUnboxingStubTarget = 1, + } + + /// + /// This node contains any custom data that we'd like to associated with a method. The unwind info of the method + /// will have a reloc to this custom data if it exists. Not all methods need custom data to be emitted. + /// This custom data excludes gcinfo and ehinfo (they are written by ObjectWriter during obj file emission). + /// + public class MethodAssociatedDataNode : ObjectNode, ISymbolDefinitionNode + { + private IMethodNode _methodNode; + + public MethodAssociatedDataNode(IMethodNode methodNode) + { + Debug.Assert(!methodNode.Method.IsAbstract); + Debug.Assert(methodNode.Method.GetCanonMethodTarget(CanonicalFormKind.Specific) == methodNode.Method); + _methodNode = methodNode; + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectNodeSection Section => ObjectNodeSection.ReadOnlyDataSection; + public override bool StaticDependenciesAreComputed => true; + public int Offset => 0; + public override bool IsShareable => _methodNode.Method is InstantiatedMethod || EETypeNode.IsTypeNodeShareable(_methodNode.Method.OwningType); + + protected internal override int ClassCode => 1055183914; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return comparer.Compare(_methodNode, ((MethodAssociatedDataNode)other)._methodNode); + } + + public virtual void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("_associatedData_").Append(nameMangler.GetMangledMethodName(_methodNode.Method)); + } + + public static bool MethodHasAssociatedData(NodeFactory factory, IMethodNode methodNode) + { + // Instantiating unboxing stubs. We need to store their non-unboxing target pointer (looked up by runtime) + ISpecialUnboxThunkNode unboxThunk = methodNode as ISpecialUnboxThunkNode; + if(unboxThunk != null && unboxThunk.IsSpecialUnboxingThunk) + return true; + + return false; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly) + { + Debug.Assert(MethodHasAssociatedData(factory, _methodNode)); + + ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); + objData.RequireInitialAlignment(1); + objData.AddSymbol(this); + + AssociatedDataFlags flags = AssociatedDataFlags.None; + + var flagsReservation = objData.ReserveByte(); + + ISpecialUnboxThunkNode unboxThunkNode = _methodNode as ISpecialUnboxThunkNode; + if (unboxThunkNode != null && unboxThunkNode.IsSpecialUnboxingThunk) + { + flags |= AssociatedDataFlags.HasUnboxingStubTarget; + objData.EmitReloc(unboxThunkNode.GetUnboxingThunkTarget(factory), RelocType.IMAGE_REL_BASED_RELPTR32); + } + + objData.EmitByte(flagsReservation, (byte)flags); + + return objData.ToObjectData(); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodCodeNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodCodeNode.cs index 4cc1f850f7..69832904e2 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodCodeNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodCodeNode.cs @@ -10,7 +10,7 @@ using Internal.TypeSystem.Interop; namespace ILCompiler.DependencyAnalysis { - public class MethodCodeNode : ObjectNode, IMethodNode, INodeWithCodeInfo, INodeWithDebugInfo, IMethodCodeNode + public class MethodCodeNode : ObjectNode, IMethodBodyNode, INodeWithCodeInfo, INodeWithDebugInfo, IMethodCodeNode, ISpecialUnboxThunkNode { public static readonly ObjectNodeSection StartSection = new ObjectNodeSection(".managedcode$A", SectionType.Executable); public static readonly ObjectNodeSection WindowsContentSection = new ObjectNodeSection(".managedcode$I", SectionType.Executable); @@ -82,6 +82,12 @@ namespace ILCompiler.DependencyAnalysis } } + if (MethodAssociatedDataNode.MethodHasAssociatedData(factory, this)) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(new DependencyListEntry(factory.MethodAssociatedData(this), "Method associated data")); + } + CodeBasedDependencyAlgorithm.AddDependenciesDueToMethodCodePresence(ref dependencies, factory, _method); return dependencies; @@ -92,10 +98,28 @@ namespace ILCompiler.DependencyAnalysis return _methodCode; } + public bool IsSpecialUnboxingThunk => ((CompilerTypeSystemContext)Method.Context).IsSpecialUnboxingThunk(_method); + + public ISymbolNode GetUnboxingThunkTarget(NodeFactory factory) + { + Debug.Assert(IsSpecialUnboxingThunk); + + MethodDesc nonUnboxingMethod = ((CompilerTypeSystemContext)Method.Context).GetTargetOfSpecialUnboxingThunk(_method); + return factory.MethodEntrypoint(nonUnboxingMethod, false); + } + public FrameInfo[] FrameInfos => _frameInfos; public byte[] GCInfo => _gcInfo; public ObjectData EHInfo => _ehInfo; + public ISymbolNode GetAssociatedDataNode(NodeFactory factory) + { + if (MethodAssociatedDataNode.MethodHasAssociatedData(factory, this)) + return factory.MethodAssociatedData(this); + + return null; + } + public void InitializeFrameInfos(FrameInfo[] frameInfos) { Debug.Assert(_frameInfos == null); @@ -128,5 +152,19 @@ namespace ILCompiler.DependencyAnalysis Debug.Assert(_debugVarInfos == null); _debugVarInfos = debugVarInfos; } + + protected internal override int ClassCode => 788492407; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return comparer.Compare(_method, ((MethodCodeNode)other)._method); + } + + int ISortableSymbolNode.ClassCode => ClassCode; + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + return CompareToImpl((ObjectNode)other, comparer); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ModulesSectionNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ModulesSectionNode.cs index 544bcc2ec8..ed629af325 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ModulesSectionNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ModulesSectionNode.cs @@ -2,6 +2,8 @@ // 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 Internal.Text; using Internal.TypeSystem; @@ -52,5 +54,7 @@ namespace ILCompiler.DependencyAnalysis return objData.ToObjectData(); } + + protected internal override int ClassCode => -1225116970; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MrtImportImportedNodeProvider.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MrtImportImportedNodeProvider.cs new file mode 100644 index 0000000000..2c44ddc3cc --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MrtImportImportedNodeProvider.cs @@ -0,0 +1,126 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + public class MrtImportImportedNodeProvider : ImportedNodeProvider + { + private readonly KeyValuePair[] _importOrdinals; + + public MrtImportImportedNodeProvider(TypeSystemContext context, KeyValuePair[] ordinals) + { + Dictionary importAddressTables = new Dictionary(); + _importOrdinals = new KeyValuePair[ordinals.Length]; + + for (int i = 0; i < ordinals.Length; i++) + { + string symbolName = "__imp_" + ordinals[i].Key + "ExportAddressTable"; + MrtProcessedImportAddressTableNode importTable = null; + if (!importAddressTables.TryGetValue(symbolName, out importTable)) + { + importTable = new MrtProcessedImportAddressTableNode(symbolName, context); + importAddressTables.Add(symbolName, importTable); + } + + _importOrdinals[i] = new KeyValuePair(importTable, ordinals[i].Value); + } + } + + private bool LookupInImportExportOrdinals( + Func> getOrdinalDictionary, + TType lookup, + MrtImportNode node) + { + uint ordinal = 0; + MrtProcessedImportAddressTableNode importTable = null; + foreach (KeyValuePair ordinalGroup in _importOrdinals) + { + if (getOrdinalDictionary(ordinalGroup.Value).TryGetValue(lookup, out ordinal)) + { + importTable = ordinalGroup.Key; + break; + } + } + + if (importTable == null) + throw new ArgumentException(); + + node.InitializeImport(importTable, (int)ordinal); + return true; + } + + public override IEETypeNode ImportedEETypeNode(NodeFactory factory, TypeDesc type) + { + var node = new MrtImportedEETypeSymbolNode(type); + LookupInImportExportOrdinals((ImportExportOrdinals importOrdinals) => importOrdinals.typeOrdinals, type, node); + return node; + } + + public override ISortableSymbolNode ImportedGCStaticNode(NodeFactory factory, MetadataType type) + { + MrtImportNode node = new MrtImportedGCStaticSymbolNode(type); + LookupInImportExportOrdinals((ImportExportOrdinals importOrdinals) => importOrdinals.gcStaticOrdinals, type, node); + return node; + } + + public override ISortableSymbolNode ImportedNonGCStaticNode(NodeFactory factory, MetadataType type) + { + MrtImportNode node = new MrtImportedNonGCStaticSymbolNode(type); + LookupInImportExportOrdinals((ImportExportOrdinals importOrdinals) => importOrdinals.nonGcStaticOrdinals, type, node); + return node; + } + + public override ISortableSymbolNode ImportedThreadStaticOffsetNode(NodeFactory factory, MetadataType type) + { + MrtImportNode node = new MrtImportedThreadStaticOffsetSymbolNode(type); + LookupInImportExportOrdinals((ImportExportOrdinals importOrdinals) => importOrdinals.tlsStaticOrdinals, type, node); + return node; + } + + public override ISortableSymbolNode ImportedThreadStaticIndexNode(NodeFactory factory, MetadataType type) + { + return factory.ExternSymbol("__imp__tls_index_SharedLibrary"); + } + + public override ISortableSymbolNode ImportedTypeDictionaryNode(NodeFactory factory, TypeDesc type) + { + // When using this style of imported symbol, this symbol should never be imported + throw new NotSupportedException(); + } + + public override ISortableSymbolNode ImportedMethodDictionaryNode(NodeFactory factory, MethodDesc method) + { + MrtImportNode node = new MrtImportedMethodDictionarySymbolNode(method); + + LookupInImportExportOrdinals((ImportExportOrdinals importOrdinals) => importOrdinals.methodDictionaryOrdinals, method, node); + return node; + } + + public override IMethodNode ImportedMethodCodeNode(NodeFactory factory, MethodDesc method, bool unboxingStub) + { + IMethodNode node; + if (unboxingStub) + { + var newUnboxingNode = new MrtImportedUnboxingMethodCodeSymbolNode(method); + LookupInImportExportOrdinals((ImportExportOrdinals importOrdinals) => importOrdinals.unboxingStubMethodOrdinals, method, newUnboxingNode); + node = newUnboxingNode; + } + else + { + var newCodeNode = new MrtImportedMethodCodeSymbolNode(method); + LookupInImportExportOrdinals((ImportExportOrdinals importOrdinals) => importOrdinals.methodOrdinals, method, newCodeNode); + node = newCodeNode; + } + + // return jump stub to code instead of code directly. Logic that is aware, and can itself do an indirect jump is responsible for looking through the method entrypoint + return new RuntimeDecodableJumpStubNode(node); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MrtImports.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MrtImports.cs new file mode 100644 index 0000000000..8289c2f51b --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MrtImports.cs @@ -0,0 +1,196 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.Text; +using ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; +using Internal.Text; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a symbol that is defined externally but modelled as a type in the + /// DependencyAnalysis infrastructure during compilation. An "ImportedEETypeSymbolNode" + /// will not be present in the final linked binary and instead referenced through + /// an import table mechanism. + /// + public sealed class MrtImportedEETypeSymbolNode : MrtImportWithTypeSymbol, IEETypeNode + { + public MrtImportedEETypeSymbolNode(TypeDesc type) : base(type) { } + protected override sealed string GetNonImportedName(NameMangler nameMangler) => nameMangler.NodeMangler.EEType(Type); + } + + public sealed class MrtImportedGCStaticSymbolNode : MrtImportWithTypeSymbol + { + public MrtImportedGCStaticSymbolNode(TypeDesc type) : base(type) { } + protected override sealed string GetNonImportedName(NameMangler nameMangler) => GCStaticsNode.GetMangledName(Type, nameMangler); + } + + public sealed class MrtImportedNonGCStaticSymbolNode : MrtImportWithTypeSymbol + { + public MrtImportedNonGCStaticSymbolNode(TypeDesc type) : base(type) { } + protected override sealed string GetNonImportedName(NameMangler nameMangler) => NonGCStaticsNode.GetMangledName(Type, nameMangler); + } + + public sealed class MrtImportedThreadStaticOffsetSymbolNode : MrtImportWithTypeSymbol + { + public MrtImportedThreadStaticOffsetSymbolNode(TypeDesc type) : base(type) { } + protected override sealed string GetNonImportedName(NameMangler nameMangler) => ThreadStaticsOffsetNode.GetMangledName(nameMangler, Type); + } + + public sealed class MrtImportedMethodDictionarySymbolNode : MrtImportWithMethodSymbol + { + public MrtImportedMethodDictionarySymbolNode(MethodDesc method) : base(method) { } + protected override sealed string GetNonImportedName(NameMangler nameMangler) => nameMangler.NodeMangler.MethodGenericDictionary(Method); + } + + public sealed class MrtImportedMethodCodeSymbolNode : MrtImportWithMethodSymbol, IMethodNode + { + public MrtImportedMethodCodeSymbolNode(MethodDesc method) : base(method) { } + protected override sealed string GetNonImportedName(NameMangler nameMangler) => nameMangler.GetMangledMethodName(Method).ToString(); + } + + public sealed class MrtImportedUnboxingMethodCodeSymbolNode : MrtImportWithMethodSymbol, IMethodNode + { + public MrtImportedUnboxingMethodCodeSymbolNode(MethodDesc method) : base(method) { } + protected override sealed string GetNonImportedName(NameMangler nameMangler) => UnboxingStubNode.GetMangledName(nameMangler, Method); + } + + public abstract class MrtImportWithTypeSymbol : MrtImportNode + { + private TypeDesc _type; + + public MrtImportWithTypeSymbol(TypeDesc type) + { + _type = type; + } + + public TypeDesc Type => _type; + } + + public abstract class MrtImportWithMethodSymbol : MrtImportNode + { + private MethodDesc _method; + + public MrtImportWithMethodSymbol(MethodDesc method) + { + _method = method; + } + + public MethodDesc Method => _method; + + } + + public abstract class MrtImportNode : SortableDependencyNode, ISymbolDefinitionNode, ISortableSymbolNode + { + private const int InvalidOffset = int.MinValue; + + private int _offset; + private MrtProcessedImportAddressTableNode _importTable; + public int Ordinal { get; private set; } + + public IHasStartSymbol ContainingNode => _importTable; + + public MrtImportNode() + { + _offset = InvalidOffset; + } + + public void InitializeImport(MrtProcessedImportAddressTableNode importTable, int ordinal) + { + Debug.Assert(_importTable == null); + Debug.Assert(importTable != null); + Ordinal = ordinal; + _importTable = importTable; + } + + int ISymbolNode.Offset => 0; + int ISymbolDefinitionNode.Offset => OffsetFromBeginningOfArray; + + protected override sealed string GetName(NodeFactory factory) + { + string prefix = "MrtImport " + Ordinal.ToStringInvariant() + " __mrt_"; + return prefix + GetNonImportedName(factory.NameMangler); + } + + protected abstract string GetNonImportedName(NameMangler nameMangler); + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("__mrt_").Append(nameMangler.CompilationUnitPrefix).Append(GetNonImportedName(nameMangler)); + } + + public bool RepresentsIndirectionCell => true; + + public int OffsetFromBeginningOfArray + { + get + { + if (_offset == InvalidOffset) + throw new InvalidOperationException(); + + Debug.Assert(_offset != InvalidOffset); + return _offset; + } + } + + internal void InitializeOffsetFromBeginningOfArray(int offset) + { + Debug.Assert(_offset == InvalidOffset || _offset == offset); + _offset = offset; + } + + public bool IsShareable => false; + public sealed override bool InterestingForDynamicDependencyAnalysis => false; + public sealed override bool HasDynamicDependencies => false; + public sealed override bool HasConditionalStaticDependencies => false; + + public sealed override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + public sealed override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + MrtImportNode otherImportNode = (MrtImportNode)other; + + int result = string.CompareOrdinal(_importTable.ExportTableToImportSymbol, otherImportNode._importTable.ExportTableToImportSymbol); + if (result != 0) + return result; + + return Ordinal - otherImportNode.Ordinal; + } + + protected internal override int ClassCode => 2017985192; + + public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) + { + return new DependencyListEntry[] { new DependencyListEntry(_importTable, "Import table") }; + } + + public override bool StaticDependenciesAreComputed => true; + + protected override void OnMarked(NodeFactory factory) + { + // We don't want the child in the parent collection unless it's necessary. + // Only when this node gets marked, the parent node becomes the actual parent. + _importTable.AddNode(this); + } + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + return CompareToImpl((SortableDependencyNode)other, comparer); + } + + int ISortableSymbolNode.ClassCode => this.ClassCode; + + void ISymbolNode.AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + AppendMangledName(nameMangler, sb); + } + } + +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MrtProcessedExportAddressTableNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MrtProcessedExportAddressTableNode.cs new file mode 100644 index 0000000000..50c3e3a8f1 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MrtProcessedExportAddressTableNode.cs @@ -0,0 +1,104 @@ +// 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.Collections.Generic; + +using Internal.Text; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + public class MrtProcessedExportAddressTableNode : ObjectNode, IExportableSymbolNode, ISortableSymbolNode + { + private readonly HashSet _exportableSymbols = new HashSet(); + private readonly string _symbolName; + private readonly NodeFactory _factory; + + public MrtProcessedExportAddressTableNode(string symbolName, NodeFactory factory) + { + _symbolName = symbolName; + _factory = factory; + } + + public event Action ReportExportedItem; + + public void AddExportableSymbol(IExportableSymbolNode exportableSymbol) + { + if (exportableSymbol.GetExportForm(_factory) == ExportForm.ByOrdinal) + { + if (exportableSymbol is EETypeNode) + { + exportableSymbol = (IExportableSymbolNode)((EETypeNode)exportableSymbol).NodeForLinkage(_factory); + } + + lock (_exportableSymbols) + { + _exportableSymbols.Add((ISortableSymbolNode)exportableSymbol); + } + } + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(_symbolName); + } + + public int Offset => 0; + + public virtual ExportForm GetExportForm(NodeFactory factory) => ExportForm.ByName; + + public override bool StaticDependenciesAreComputed => true; + + public override ObjectNodeSection Section => ObjectNodeSection.ReadOnlyDataSection; + public override bool IsShareable => true; + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + var builder = new ObjectDataBuilder(factory, relocsOnly); + builder.RequireInitialPointerAlignment(); + builder.AddSymbol(this); + + ISortableSymbolNode[] symbolNodes = new ISortableSymbolNode[_exportableSymbols.Count]; + _exportableSymbols.CopyTo(symbolNodes); + Array.Sort(symbolNodes, new CompilerComparer()); + + builder.EmitInt(1); // Export table version 1 + builder.EmitInt(symbolNodes.Length); // Count of exported symbols in this table + + int index = 1; + foreach (ISortableSymbolNode symbol in symbolNodes) + { + builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_REL32); + ReportExportedItem?.Invoke(index, (IExportableSymbolNode)symbol); + index++; + } + + return builder.ToObjectData(); + } + + protected internal override int ClassCode => 40423846; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + Debug.Assert(Object.ReferenceEquals(other, this)); + return 0; // There should only ever be one of these per dependency graph + } + + int ISortableSymbolNode.ClassCode => ClassCode; + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + return CompareToImpl((ObjectNode)other, comparer); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MrtProcessedImportAddressTableNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MrtProcessedImportAddressTableNode.cs new file mode 100644 index 0000000000..e72126588c --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MrtProcessedImportAddressTableNode.cs @@ -0,0 +1,134 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.Text; +using ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; +using Internal.Text; + +namespace ILCompiler.DependencyAnalysis +{ + public class MrtProcessedImportAddressTableNode : EmbeddedDataContainerNode, IHasStartSymbol, ISortableSymbolNode, ISymbolDefinitionNode + { + private List _importNodes = new List(); + private bool _nodeListComplete; + private int _pointerSize; + private EmbeddedObjectNode _pointerFromImportTablesTable; + + public MrtProcessedImportAddressTableNode(string exportTableToImportSymbol, TypeSystemContext context) : base("_ImportTable_" + exportTableToImportSymbol, "_ImportTable_end_" + exportTableToImportSymbol) + { + ExportTableToImportSymbol = exportTableToImportSymbol; + _pointerSize = context.Target.PointerSize; + } + + public readonly string ExportTableToImportSymbol; + + protected override string GetName(NodeFactory factory) => $"Region {StartSymbol.GetMangledName(factory.NameMangler)}"; + + public override ObjectNodeSection Section => ObjectNodeSection.DataSection; + public override bool IsShareable => false; + + public override bool StaticDependenciesAreComputed => true; + + public void AddNode(MrtImportNode node) + { + Debug.Assert(!_nodeListComplete); + _importNodes.Add(node); + } + + public void FinalizeOffsets() + { + if (_nodeListComplete) + return; + + _nodeListComplete = true; + _importNodes.Sort((import1, import2) => import1.Ordinal - import2.Ordinal); + + // Layout of importtable node + // + // Version Number - Always 1 (32bit int) + // Count of nodes (32bit int) + // Symbol that points to imported EAT (this is done via traditional linker import/export tables) (Pointer sized) + // Pointer sized indirection cell (1 per node, initial value is that of the index into the EAT) + int offset = 4 + 4 + _pointerSize; + foreach (MrtImportNode node in _importNodes) + { + node.InitializeOffsetFromBeginningOfArray(offset); + offset += _pointerSize; + } + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly) + { + ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); + builder.RequireInitialPointerAlignment(); + + builder.AddSymbol(this); + builder.AddSymbol(StartSymbol); + + builder.EmitInt(1); + builder.EmitInt(_importNodes.Count); + builder.EmitPointerReloc(factory.ExternSymbol(ExportTableToImportSymbol)); + + + if (!relocsOnly) + { + FinalizeOffsets(); + + foreach (MrtImportNode node in _importNodes) + { + Debug.Assert(((ISymbolDefinitionNode)node).Offset == builder.CountBytes); + builder.AddSymbol(node); + builder.EmitNaturalInt(node.Ordinal); + } + } + + EndSymbol.SetSymbolOffset(builder.CountBytes); + builder.AddSymbol(EndSymbol); + + ObjectData objData = builder.ToObjectData(); + return objData; + } + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList dependencies = new DependencyList(); + dependencies.Add(StartSymbol, "StartSymbol"); + dependencies.Add(EndSymbol, "EndSymbol"); + + lock(this) + { + if (_pointerFromImportTablesTable == null) + { + _pointerFromImportTablesTable = factory.ImportAddressTablesTable.NewNode(this); + } + } + dependencies.Add(_pointerFromImportTablesTable, "Pointer from ImportTablesTableNode"); + + return dependencies; + } + + protected internal override int ClassCode => -1145565068; + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + return CompareToImpl((SortableDependencyNode)other, comparer); + } + + int ISortableSymbolNode.ClassCode => this.ClassCode; + + void ISymbolNode.AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("__"); + StartSymbol.AppendMangledName(nameMangler, sb); + } + + int ISymbolNode.Offset => 0; + int ISymbolDefinitionNode.Offset => 0; + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NamedJumpStubNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NamedJumpStubNode.cs new file mode 100644 index 0000000000..e3d4afaa49 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NamedJumpStubNode.cs @@ -0,0 +1,23 @@ +// 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 Internal.Text; + +namespace ILCompiler.DependencyAnalysis +{ + class NamedJumpStubNode : JumpStubNode + { + Utf8String _name; + + public NamedJumpStubNode(string name, ISymbolNode target) : base(target) + { + _name = new Utf8String(name); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(_name); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutInfoNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutInfoNode.cs index 6d2d8e1d9e..bed4f3fe42 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutInfoNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutInfoNode.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using Internal.Text; using Internal.NativeFormat; +using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis { @@ -51,6 +52,7 @@ namespace ILCompiler.DependencyAnalysis public override bool IsShareable => false; public override ObjectNodeSection Section => _externalReferences.Section; public override bool StaticDependenciesAreComputed => true; + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection; protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); public Section LdTokenInfoSection => _ldTokenInfoSection; @@ -92,5 +94,8 @@ namespace ILCompiler.DependencyAnalysis return new ObjectData(_writerSavedBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.NativeLayoutInfoNode; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutSignatureNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutSignatureNode.cs index 6c593d929a..2c1dd5f4dd 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutSignatureNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutSignatureNode.cs @@ -84,5 +84,38 @@ namespace ILCompiler.DependencyAnalysis return objData.ToObjectData(); } + + protected internal override int ClassCode => 1887049331; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + NativeLayoutSignatureNode otherSignature = (NativeLayoutSignatureNode)other; + if (_identity is MethodDesc) + { + if (otherSignature._identity is TypeDesc || otherSignature._identity is FieldDesc) + return -1; + return comparer.Compare((MethodDesc)_identity, (MethodDesc)((NativeLayoutSignatureNode)other)._identity); + } + else if (_identity is TypeDesc) + { + if (otherSignature._identity is MethodDesc) + return 1; + + if (otherSignature._identity is FieldDesc) + return -1; + + return comparer.Compare((TypeDesc)_identity, (TypeDesc)((NativeLayoutSignatureNode)other)._identity); + } + else if (_identity is FieldDesc) + { + if (otherSignature._identity is MethodDesc || otherSignature._identity is TypeDesc) + return 1; + return comparer.Compare((FieldDesc)_identity, (FieldDesc)((NativeLayoutSignatureNode)other)._identity); + } + else + { + throw new NotSupportedException("New type system entity needs a comparison"); + } + } } } \ No newline at end of file diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs deleted file mode 100644 index a4425efee6..0000000000 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs +++ /dev/null @@ -1,1980 +0,0 @@ -// 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.Collections.Generic; -using System.Diagnostics; - -using Internal.NativeFormat; -using Internal.Runtime; -using Internal.TypeSystem; -using ILCompiler.DependencyAnalysisFramework; - -namespace ILCompiler.DependencyAnalysis -{ - /// - /// Wrapper nodes for native layout vertex structures. These wrapper nodes are "abstract" as they do not - /// generate any data. They are used to keep track of the dependency nodes required by a Vertex structure. - /// - /// Any node in the graph that references data in the native layout blob needs to create one of these - /// NativeLayoutVertexNode nodes, and track it as a dependency of itself. - /// Example: MethodCodeNodes that are saved to the table in the ExactMethodInstantiationsNode reference - /// signatures stored in the native layout blob, so a NativeLayoutPlacedSignatureVertexNode node is created - /// and returned as a static dependency of the associated MethodCodeNode (in the GetStaticDependencies API). - /// - /// Each NativeLayoutVertexNode that gets marked in the graph will register itself with the NativeLayoutInfoNode, - /// so that the NativeLayoutInfoNode can write it later to the native layout blob during the call to its GetData API. - /// - public abstract class NativeLayoutVertexNode : DependencyNodeCore - { - public override bool HasConditionalStaticDependencies => false; - public override bool HasDynamicDependencies => false; - public override bool InterestingForDynamicDependencyAnalysis => false; - public override bool StaticDependenciesAreComputed => true; - - - [Conditional("DEBUG")] - public virtual void CheckIfMarkedEnoughToWrite() - { - Debug.Assert(Marked); - } - - public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) - { - return Array.Empty(); - } - - public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) - { - return Array.Empty(); - } - - protected override void OnMarked(NodeFactory context) - { - context.MetadataManager.NativeLayoutInfo.AddVertexNodeToNativeLayout(this); - } - - public abstract Vertex WriteVertex(NodeFactory factory); - - protected NativeWriter GetNativeWriter(NodeFactory factory) - { - // There is only one native layout info blob, so only one writer for now... - return factory.MetadataManager.NativeLayoutInfo.Writer; - } - } - - /// - /// Any NativeLayoutVertexNode that needs to expose the native layout Vertex after it has been saved - /// needs to derive from this NativeLayoutSavedVertexNode class. - /// - /// A nativelayout Vertex should typically only be exposed for Vertex offset fetching purposes, after the native - /// writer is saved (Vertex offsets get generated when the native writer gets saved). - /// - /// It is important for whoever derives from this class to produce unified Vertices. Calling the WriteVertex method - /// multiple times should always produce the same exact unified Vertex each time (hence the assert in SetSavedVertex). - /// All nativewriter.Getxyz methods return unified Vertices. - /// - /// When exposing a saved Vertex that is a result of a section placement operation (Section.Place(...)), always make - /// sure a unified Vertex is being placed in the section (Section.Place creates a PlacedVertex structure that wraps the - /// Vertex to be placed, so if the Vertex to be placed is unified, there will only be a single unified PlacedVertex - /// structure created for that placed Vertex). - /// - public abstract class NativeLayoutSavedVertexNode : NativeLayoutVertexNode - { - public Vertex SavedVertex { get; private set; } - protected Vertex SetSavedVertex(Vertex value) - { - Debug.Assert(SavedVertex == null || Object.ReferenceEquals(SavedVertex, value)); - SavedVertex = value; - return value; - } - } - - internal abstract class NativeLayoutMethodEntryVertexNode : NativeLayoutSavedVertexNode - { - [Flags] - public enum MethodEntryFlags - { - CreateInstantiatedSignature = 1, - SaveEntryPoint = 2, - /// - /// IsUnboxingStub is not set for template methods (all template lookups performed at runtime are done with this flag not set, - /// since it can't always be conveniently computed for a concrete method before looking up its template). - /// - DisableUnboxingStub = 4 - } - - protected readonly MethodDesc _method; - private MethodEntryFlags _flags; - private NativeLayoutTypeSignatureVertexNode _containingTypeSig; - private NativeLayoutMethodSignatureVertexNode _methodSig; - private NativeLayoutTypeSignatureVertexNode[] _instantiationArgsSig; - - public NativeLayoutMethodEntryVertexNode(NodeFactory factory, MethodDesc method, MethodEntryFlags flags) - { - _method = method; - _flags = flags; - _methodSig = factory.NativeLayout.MethodSignatureVertex(method.GetTypicalMethodDefinition().Signature); - - if ((_flags & MethodEntryFlags.CreateInstantiatedSignature) == 0) - { - _containingTypeSig = factory.NativeLayout.TypeSignatureVertex(method.OwningType); - if (method.HasInstantiation && !method.IsMethodDefinition) - { - _instantiationArgsSig = new NativeLayoutTypeSignatureVertexNode[method.Instantiation.Length]; - for (int i = 0; i < _instantiationArgsSig.Length; i++) - _instantiationArgsSig[i] = factory.NativeLayout.TypeSignatureVertex(method.Instantiation[i]); - } - } - } - - public override IEnumerable GetStaticDependencies(NodeFactory context) - { - DependencyList dependencies = new DependencyList(); - - dependencies.Add(new DependencyListEntry(_methodSig, "NativeLayoutMethodEntryVertexNode method signature")); - if ((_flags & MethodEntryFlags.CreateInstantiatedSignature) != 0) - { - dependencies.Add(new DependencyListEntry(context.NecessaryTypeSymbol(_method.OwningType), "NativeLayoutMethodEntryVertexNode containing type")); - foreach (var arg in _method.Instantiation) - dependencies.Add(new DependencyListEntry(context.NecessaryTypeSymbol(arg), "NativeLayoutMethodEntryVertexNode instantiation argument type")); - } - else - { - dependencies.Add(new DependencyListEntry(_containingTypeSig, "NativeLayoutMethodEntryVertexNode containing type signature")); - if (_method.HasInstantiation && !_method.IsMethodDefinition) - { - foreach (var arg in _instantiationArgsSig) - dependencies.Add(new DependencyListEntry(arg, "NativeLayoutMethodEntryVertexNode instantiation argument signature")); - } - } - - if ((_flags & MethodEntryFlags.SaveEntryPoint) != 0) - { - bool unboxingStub; - IMethodNode methodEntryPointNode = GetMethodEntrypointNode(context, out unboxingStub); - dependencies.Add(new DependencyListEntry(methodEntryPointNode, "NativeLayoutMethodEntryVertexNode entrypoint")); - } - - return dependencies; - } - - public override Vertex WriteVertex(NodeFactory factory) - { - Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); - - Vertex containingType = GetContainingTypeVertex(factory); - Vertex methodSig = _methodSig.WriteVertex(factory); - Vertex methodNameAndSig = GetNativeWriter(factory).GetMethodNameAndSigSignature(_method.Name, methodSig); - - Vertex[] args = null; - MethodFlags flags = 0; - if (_method.HasInstantiation && !_method.IsMethodDefinition) - { - Debug.Assert(_instantiationArgsSig == null || (_instantiationArgsSig != null && _method.Instantiation.Length == _instantiationArgsSig.Length)); - - flags |= MethodFlags.HasInstantiation; - args = new Vertex[_method.Instantiation.Length]; - - for (int i = 0; i < args.Length; i++) - { - if ((_flags & MethodEntryFlags.CreateInstantiatedSignature) != 0) - { - IEETypeNode eetypeNode = factory.NecessaryTypeSymbol(_method.Instantiation[i]); - uint typeIndex = factory.MetadataManager.NativeLayoutInfo.ExternalReferences.GetIndex(eetypeNode); - args[i] = GetNativeWriter(factory).GetExternalTypeSignature(typeIndex); - } - else - { - args[i] = _instantiationArgsSig[i].WriteVertex(factory); - } - } - } - - uint fptrReferenceId = 0; - if ((_flags & MethodEntryFlags.SaveEntryPoint) != 0) - { - flags |= MethodFlags.HasFunctionPointer; - - bool unboxingStub; - IMethodNode methodEntryPointNode = GetMethodEntrypointNode(factory, out unboxingStub); - fptrReferenceId = factory.MetadataManager.NativeLayoutInfo.ExternalReferences.GetIndex(methodEntryPointNode); - - if (unboxingStub) - flags |= MethodFlags.IsUnboxingStub; - if (_method.IsCanonicalMethod(CanonicalFormKind.Universal)) - flags |= MethodFlags.FunctionPointerIsUSG; - } - - return GetNativeWriter(factory).GetMethodSignature((uint)flags, fptrReferenceId, containingType, methodNameAndSig, args); - } - - private Vertex GetContainingTypeVertex(NodeFactory factory) - { - if ((_flags & MethodEntryFlags.CreateInstantiatedSignature) != 0) - { - IEETypeNode eetypeNode = factory.NecessaryTypeSymbol(_method.OwningType); - uint typeIndex = factory.MetadataManager.NativeLayoutInfo.ExternalReferences.GetIndex(eetypeNode); - return GetNativeWriter(factory).GetExternalTypeSignature(typeIndex); - } - else - { - return _containingTypeSig.WriteVertex(factory); - } - } - - protected virtual IMethodNode GetMethodEntrypointNode(NodeFactory factory, out bool unboxingStub) - { - unboxingStub = (_flags & MethodEntryFlags.DisableUnboxingStub) != 0 ? false : _method.OwningType.IsValueType && !_method.Signature.IsStatic; - IMethodNode methodEntryPointNode = factory.MethodEntrypoint(_method, unboxingStub); - - return methodEntryPointNode; - } - } - - internal sealed class NativeLayoutMethodLdTokenVertexNode : NativeLayoutMethodEntryVertexNode - { - protected override string GetName(NodeFactory factory) => "NativeLayoutMethodLdTokenVertexNode_" + factory.NameMangler.GetMangledMethodName(_method); - - public NativeLayoutMethodLdTokenVertexNode(NodeFactory factory, MethodDesc method) - : base(factory, method, 0) - { - } - - public override Vertex WriteVertex(NodeFactory factory) - { - Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); - - Vertex methodEntryVertex = base.WriteVertex(factory); - return SetSavedVertex(factory.MetadataManager.NativeLayoutInfo.LdTokenInfoSection.Place(methodEntryVertex)); - } - } - - internal sealed class NativeLayoutFieldLdTokenVertexNode : NativeLayoutSavedVertexNode - { - private readonly FieldDesc _field; - private readonly NativeLayoutTypeSignatureVertexNode _containingTypeSig; - - public NativeLayoutFieldLdTokenVertexNode(NodeFactory factory, FieldDesc field) - { - _field = field; - _containingTypeSig = factory.NativeLayout.TypeSignatureVertex(field.OwningType); - } - - protected override string GetName(NodeFactory factory) => "NativeLayoutFieldLdTokenVertexNode_" + factory.NameMangler.GetMangledFieldName(_field); - - public override IEnumerable GetStaticDependencies(NodeFactory context) - { - return new DependencyListEntry[] - { - new DependencyListEntry(_containingTypeSig, "NativeLayoutFieldLdTokenVertexNode containing type signature"), - }; - } - - public override Vertex WriteVertex(NodeFactory factory) - { - Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); - - Vertex containingType = _containingTypeSig.WriteVertex(factory); - - Vertex unplacedVertex = GetNativeWriter(factory).GetFieldSignature(containingType, _field.Name); - - return SetSavedVertex(factory.MetadataManager.NativeLayoutInfo.LdTokenInfoSection.Place(unplacedVertex)); - } - } - - internal sealed class NativeLayoutMethodSignatureVertexNode : NativeLayoutVertexNode - { - private Internal.TypeSystem.MethodSignature _signature; - private NativeLayoutTypeSignatureVertexNode _returnTypeSig; - private NativeLayoutTypeSignatureVertexNode[] _parametersSig; - - protected override string GetName(NodeFactory factory) => "NativeLayoutMethodSignatureVertexNode " + _signature.GetName(); - - public NativeLayoutMethodSignatureVertexNode(NodeFactory factory, Internal.TypeSystem.MethodSignature signature) - { - _signature = signature; - _returnTypeSig = factory.NativeLayout.TypeSignatureVertex(signature.ReturnType); - _parametersSig = new NativeLayoutTypeSignatureVertexNode[signature.Length]; - for (int i = 0; i < _parametersSig.Length; i++) - _parametersSig[i] = factory.NativeLayout.TypeSignatureVertex(signature[i]); - } - - public override IEnumerable GetStaticDependencies(NodeFactory context) - { - DependencyList dependencies = new DependencyList(); - - dependencies.Add(new DependencyListEntry(_returnTypeSig, "NativeLayoutMethodSignatureVertexNode return type signature")); - foreach (var arg in _parametersSig) - dependencies.Add(new DependencyListEntry(arg, "NativeLayoutMethodSignatureVertexNode parameter signature")); - - return dependencies; - } - - public override Vertex WriteVertex(NodeFactory factory) - { - Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); - - MethodCallingConvention methodCallingConvention = default(MethodCallingConvention); - - if (_signature.GenericParameterCount > 0) - methodCallingConvention |= MethodCallingConvention.Generic; - if (_signature.IsStatic) - methodCallingConvention |= MethodCallingConvention.Static; - - Debug.Assert(_signature.Length == _parametersSig.Length); - - Vertex returnType = _returnTypeSig.WriteVertex(factory); - Vertex[] parameters = new Vertex[_parametersSig.Length]; - for (int i = 0; i < _parametersSig.Length; i++) - parameters[i] = _parametersSig[i].WriteVertex(factory); - - Vertex signature = GetNativeWriter(factory).GetMethodSigSignature((uint)methodCallingConvention, (uint)_signature.GenericParameterCount, returnType, parameters); - return factory.MetadataManager.NativeLayoutInfo.SignaturesSection.Place(signature); - } - } - - internal sealed class NativeLayoutMethodNameAndSignatureVertexNode : NativeLayoutVertexNode - { - private MethodDesc _method; - private NativeLayoutMethodSignatureVertexNode _methodSig; - - protected override string GetName(NodeFactory factory) => "NativeLayoutMethodNameAndSignatureVertexNode" + factory.NameMangler.GetMangledMethodName(_method); - - public NativeLayoutMethodNameAndSignatureVertexNode(NodeFactory factory, MethodDesc method) - { - _method = method; - _methodSig = factory.NativeLayout.MethodSignatureVertex(method.Signature); - } - public override IEnumerable GetStaticDependencies(NodeFactory context) - { - return new DependencyListEntry[] { new DependencyListEntry(_methodSig, "NativeLayoutMethodNameAndSignatureVertexNode signature vertex") }; - } - public override Vertex WriteVertex(NodeFactory factory) - { - Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); - - Vertex methodSig = _methodSig.WriteVertex(factory); - return GetNativeWriter(factory).GetMethodNameAndSigSignature(_method.Name, methodSig); - } - } - - internal abstract class NativeLayoutTypeSignatureVertexNode : NativeLayoutVertexNode - { - protected readonly TypeDesc _type; - - protected NativeLayoutTypeSignatureVertexNode(TypeDesc type) - { - _type = type; - } - - protected override string GetName(NodeFactory factory) => "NativeLayoutTypeSignatureVertexNode: " + _type.ToString(); - - public static NativeLayoutTypeSignatureVertexNode NewTypeSignatureVertexNode(NodeFactory factory, TypeDesc type) - { - switch (type.Category) - { - case Internal.TypeSystem.TypeFlags.Array: - case Internal.TypeSystem.TypeFlags.SzArray: - case Internal.TypeSystem.TypeFlags.Pointer: - case Internal.TypeSystem.TypeFlags.ByRef: - return new NativeLayoutParameterizedTypeSignatureVertexNode(factory, type); - - case Internal.TypeSystem.TypeFlags.SignatureTypeVariable: - case Internal.TypeSystem.TypeFlags.SignatureMethodVariable: - return new NativeLayoutGenericVarSignatureVertexNode(factory, type); - - // TODO Internal.TypeSystem.TypeFlags.FunctionPointer (Runtime parsing also not yet implemented) - case Internal.TypeSystem.TypeFlags.FunctionPointer: - throw new NotImplementedException("FunctionPointer signature"); - - default: - { - Debug.Assert(type.IsDefType); - - if (type.HasInstantiation && !type.IsGenericDefinition) - return new NativeLayoutInstantiatedTypeSignatureVertexNode(factory, type); - else - return new NativeLayoutEETypeSignatureVertexNode(factory, type); - } - } - } - - sealed class NativeLayoutParameterizedTypeSignatureVertexNode : NativeLayoutTypeSignatureVertexNode - { - private NativeLayoutVertexNode _parameterTypeSig; - - public NativeLayoutParameterizedTypeSignatureVertexNode(NodeFactory factory, TypeDesc type) : base(type) - { - _parameterTypeSig = factory.NativeLayout.TypeSignatureVertex(((ParameterizedType)type).ParameterType); - } - public override IEnumerable GetStaticDependencies(NodeFactory context) - { - return new DependencyListEntry[] { new DependencyListEntry(_parameterTypeSig, "NativeLayoutParameterizedTypeSignatureVertexNode parameter type signature") }; - } - public override Vertex WriteVertex(NodeFactory factory) - { - Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); - - switch (_type.Category) - { - case Internal.TypeSystem.TypeFlags.SzArray: - return GetNativeWriter(factory).GetModifierTypeSignature(TypeModifierKind.Array, _parameterTypeSig.WriteVertex(factory)); - - case Internal.TypeSystem.TypeFlags.Pointer: - return GetNativeWriter(factory).GetModifierTypeSignature(TypeModifierKind.Pointer, _parameterTypeSig.WriteVertex(factory)); - - case Internal.TypeSystem.TypeFlags.ByRef: - return GetNativeWriter(factory).GetModifierTypeSignature(TypeModifierKind.ByRef, _parameterTypeSig.WriteVertex(factory)); - - case Internal.TypeSystem.TypeFlags.Array: - { - Vertex elementType = _parameterTypeSig.WriteVertex(factory); - - // Skip bounds and lobounds (TODO) - var bounds = Array.Empty(); - var lobounds = Array.Empty(); - - return GetNativeWriter(factory).GetMDArrayTypeSignature(elementType, (uint)((ArrayType)_type).Rank, bounds, lobounds); - } - } - - Debug.Assert(false, "UNREACHABLE"); - return null; - } - } - - sealed class NativeLayoutGenericVarSignatureVertexNode : NativeLayoutTypeSignatureVertexNode - { - public NativeLayoutGenericVarSignatureVertexNode(NodeFactory factory, TypeDesc type) : base(type) - { - } - public override IEnumerable GetStaticDependencies(NodeFactory context) - { - return Array.Empty(); - } - public override Vertex WriteVertex(NodeFactory factory) - { - Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); - - switch (_type.Category) - { - case Internal.TypeSystem.TypeFlags.SignatureTypeVariable: - return GetNativeWriter(factory).GetVariableTypeSignature((uint)((SignatureVariable)_type).Index, false); - - case Internal.TypeSystem.TypeFlags.SignatureMethodVariable: - return GetNativeWriter(factory).GetVariableTypeSignature((uint)((SignatureMethodVariable)_type).Index, true); - } - - Debug.Assert(false, "UNREACHABLE"); - return null; - } - } - - sealed class NativeLayoutInstantiatedTypeSignatureVertexNode : NativeLayoutTypeSignatureVertexNode - { - private NativeLayoutTypeSignatureVertexNode _genericTypeDefSig; - private NativeLayoutTypeSignatureVertexNode[] _instantiationArgs; - - public NativeLayoutInstantiatedTypeSignatureVertexNode(NodeFactory factory, TypeDesc type) : base(type) - { - Debug.Assert(type.HasInstantiation && !type.IsGenericDefinition); - - _genericTypeDefSig = factory.NativeLayout.TypeSignatureVertex(type.GetTypeDefinition()); - _instantiationArgs = new NativeLayoutTypeSignatureVertexNode[type.Instantiation.Length]; - for (int i = 0; i < _instantiationArgs.Length; i++) - _instantiationArgs[i] = factory.NativeLayout.TypeSignatureVertex(type.Instantiation[i]); - - } - public override IEnumerable GetStaticDependencies(NodeFactory context) - { - DependencyList dependencies = new DependencyList(); - - dependencies.Add(new DependencyListEntry(_genericTypeDefSig, "NativeLayoutInstantiatedTypeSignatureVertexNode generic definition signature")); - foreach (var arg in _instantiationArgs) - dependencies.Add(new DependencyListEntry(arg, "NativeLayoutInstantiatedTypeSignatureVertexNode instantiation argument signature")); - - return dependencies; - } - public override Vertex WriteVertex(NodeFactory factory) - { - Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); - - Vertex genericDefVertex = _genericTypeDefSig.WriteVertex(factory); - Vertex[] args = new Vertex[_instantiationArgs.Length]; - for (int i = 0; i < args.Length; i++) - args[i] = _instantiationArgs[i].WriteVertex(factory); - - return GetNativeWriter(factory).GetInstantiationTypeSignature(genericDefVertex, args); - } - } - - sealed class NativeLayoutEETypeSignatureVertexNode : NativeLayoutTypeSignatureVertexNode - { - public NativeLayoutEETypeSignatureVertexNode(NodeFactory factory, TypeDesc type) : base(type) - { - Debug.Assert(!type.IsRuntimeDeterminedSubtype); - Debug.Assert(!type.HasInstantiation || type.IsGenericDefinition); - } - public override IEnumerable GetStaticDependencies(NodeFactory context) - { - return new DependencyListEntry[] - { - new DependencyListEntry(context.NecessaryTypeSymbol(_type), "NativeLayoutEETypeVertexNode containing type signature") - }; - } - public override Vertex WriteVertex(NodeFactory factory) - { - Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); - - IEETypeNode eetypeNode = factory.NecessaryTypeSymbol(_type); - uint typeIndex = factory.MetadataManager.NativeLayoutInfo.ExternalReferences.GetIndex(eetypeNode); - return GetNativeWriter(factory).GetExternalTypeSignature(typeIndex); - } - } - } - - public sealed class NativeLayoutExternalReferenceVertexNode : NativeLayoutVertexNode - { - private ISymbolNode _symbol; - - public NativeLayoutExternalReferenceVertexNode(NodeFactory factory, ISymbolNode symbol) - { - _symbol = symbol; - } - - protected override string GetName(NodeFactory factory) => "NativeLayoutISymbolNodeReferenceVertexNode " + _symbol.GetMangledName(factory.NameMangler); - - public override IEnumerable GetStaticDependencies(NodeFactory context) - { - return new DependencyListEntry[] - { - new DependencyListEntry(_symbol, "NativeLayoutISymbolNodeReferenceVertexNode containing symbol") - }; - } - - public override Vertex WriteVertex(NodeFactory factory) - { - Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); - - uint symbolIndex = factory.MetadataManager.NativeLayoutInfo.ExternalReferences.GetIndex(_symbol); - return GetNativeWriter(factory).GetUnsignedConstant(symbolIndex); - } - } - - internal sealed class NativeLayoutPlacedSignatureVertexNode : NativeLayoutSavedVertexNode - { - private NativeLayoutVertexNode _signatureToBePlaced; - - protected override string GetName(NodeFactory factory) => "NativeLayoutPlacedSignatureVertexNode"; - - public NativeLayoutPlacedSignatureVertexNode(NativeLayoutVertexNode signatureToBePlaced) - { - _signatureToBePlaced = signatureToBePlaced; - } - public override IEnumerable GetStaticDependencies(NodeFactory context) - { - return new DependencyListEntry[] { new DependencyListEntry(_signatureToBePlaced, "NativeLayoutPlacedSignatureVertexNode placed signature") }; - } - public override Vertex WriteVertex(NodeFactory factory) - { - // This vertex doesn't need to assert as marked, as it simply represents the concept of an existing vertex which has been placed. - - // Always use the NativeLayoutInfo blob for names and sigs, even if the associated types/methods are written elsewhere. - // This saves space, since we can Unify more signatures, allows optimizations in comparing sigs in the same module, and - // prevents the dynamic type loader having to know about other native layout sections (since sigs contain types). If we are - // using a non-native layout info writer, write the sig to the native layout info, and refer to it by offset in its own - // section. At runtime, we will assume all names and sigs are in the native layout and find it. - - Vertex signature = _signatureToBePlaced.WriteVertex(factory); - return SetSavedVertex(factory.MetadataManager.NativeLayoutInfo.SignaturesSection.Place(signature)); - } - } - - internal sealed class NativeLayoutPlacedVertexSequenceOfUIntVertexNode : NativeLayoutSavedVertexNode - { - private List _uints; - - protected override string GetName(NodeFactory factory) => "NativeLayoutPlacedVertexSequenceVertexNode"; - public NativeLayoutPlacedVertexSequenceOfUIntVertexNode(List uints) - { - _uints = uints; - } - - public override IEnumerable GetStaticDependencies(NodeFactory context) - { - // There are no interesting dependencies - return null; - } - - public override Vertex WriteVertex(NodeFactory factory) - { - // Eagerly return the SavedVertex so that we can unify the VertexSequence - if (SavedVertex != null) - return SavedVertex; - - // This vertex doesn't need to assert as marked, as it simply represents the concept of an existing vertex which has been placed. - - NativeWriter writer = GetNativeWriter(factory); - - VertexSequence sequence = new VertexSequence(); - foreach (uint value in _uints) - { - sequence.Append(writer.GetUnsignedConstant(value)); - } - - return SetSavedVertex(factory.MetadataManager.NativeLayoutInfo.SignaturesSection.Place(sequence)); - } - } - - internal sealed class NativeLayoutPlacedVertexSequenceVertexNode : NativeLayoutSavedVertexNode - { - private List _vertices; - - protected override string GetName(NodeFactory factory) => "NativeLayoutPlacedVertexSequenceVertexNode"; - public NativeLayoutPlacedVertexSequenceVertexNode(List vertices) - { - _vertices = vertices; - } - - public override IEnumerable GetStaticDependencies(NodeFactory context) - { - DependencyListEntry[] dependencies = new DependencyListEntry[_vertices.Count]; - for (int i = 0; i < _vertices.Count; i++) - { - dependencies[i] = new DependencyListEntry(_vertices[i], "NativeLayoutPlacedVertexSequenceVertexNode element"); - } - - return dependencies; - } - - public override Vertex WriteVertex(NodeFactory factory) - { - // Eagerly return the SavedVertex so that we can unify the VertexSequence - if (SavedVertex != null) - return SavedVertex; - - // This vertex doesn't need to assert as marked, as it simply represents the concept of an existing vertex which has been placed. - - VertexSequence sequence = new VertexSequence(); - foreach (NativeLayoutVertexNode vertex in _vertices) - { - sequence.Append(vertex.WriteVertex(factory)); - } - - return SetSavedVertex(factory.MetadataManager.NativeLayoutInfo.SignaturesSection.Place(sequence)); - } - } - - internal sealed class NativeLayoutTemplateMethodSignatureVertexNode : NativeLayoutMethodEntryVertexNode - { - protected override string GetName(NodeFactory factory) => "NativeLayoutTemplateMethodSignatureVertexNode_" + factory.NameMangler.GetMangledMethodName(_method); - - public NativeLayoutTemplateMethodSignatureVertexNode(NodeFactory factory, MethodDesc method) - : base(factory, method, MethodEntryFlags.CreateInstantiatedSignature | MethodEntryFlags.SaveEntryPoint | MethodEntryFlags.DisableUnboxingStub) - { - } - - public override Vertex WriteVertex(NodeFactory factory) - { - Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); - - Vertex methodEntryVertex = base.WriteVertex(factory); - return SetSavedVertex(factory.MetadataManager.NativeLayoutInfo.TemplatesSection.Place(methodEntryVertex)); - } - } - - - public sealed class NativeLayoutDictionarySignatureNode : NativeLayoutSavedVertexNode - { - private TypeSystemEntity _owningMethodOrType; - public NativeLayoutDictionarySignatureNode(NodeFactory nodeFactory, TypeSystemEntity owningMethodOrType) - { - if (owningMethodOrType is MethodDesc) - { - MethodDesc owningMethod = (MethodDesc)owningMethodOrType; - Debug.Assert(owningMethod.IsCanonicalMethod(CanonicalFormKind.Universal) || nodeFactory.LazyGenericsPolicy.UsesLazyGenerics(owningMethod)); - Debug.Assert(owningMethod.IsCanonicalMethod(CanonicalFormKind.Any)); - Debug.Assert(owningMethod.HasInstantiation); - } - else - { - TypeDesc owningType = (TypeDesc)owningMethodOrType; - Debug.Assert(owningType.IsCanonicalSubtype(CanonicalFormKind.Universal) || nodeFactory.LazyGenericsPolicy.UsesLazyGenerics(owningType)); - Debug.Assert(owningType.IsCanonicalSubtype(CanonicalFormKind.Any)); - } - - _owningMethodOrType = owningMethodOrType; - } - - private GenericContextKind ContextKind - { - get - { - if (_owningMethodOrType is MethodDesc) - { - MethodDesc owningMethod = (MethodDesc)_owningMethodOrType; - Debug.Assert(owningMethod.HasInstantiation); - return GenericContextKind.FromMethodHiddenArg | GenericContextKind.NeedsUSGContext; - } - else - { - TypeDesc owningType = (TypeDesc)_owningMethodOrType; - if (owningType.IsSzArray || owningType.IsValueType || owningType.IsSealed()) - { - return GenericContextKind.FromHiddenArg | GenericContextKind.NeedsUSGContext; - } - else - { - return GenericContextKind.FromHiddenArg | GenericContextKind.NeedsUSGContext | GenericContextKind.HasDeclaringType; - } - } - } - } - - public override IEnumerable GetStaticDependencies(NodeFactory context) - { - if ((ContextKind & GenericContextKind.HasDeclaringType) != 0) - { - return new DependencyListEntry[] { new DependencyListEntry(context.NativeLayout.TypeSignatureVertex((TypeDesc)_owningMethodOrType), "DeclaringType signature") }; - } - else - { - return Array.Empty(); - } - } - - public override Vertex WriteVertex(NodeFactory factory) - { - Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); - - VertexSequence sequence = new VertexSequence(); - - DictionaryLayoutNode associatedLayout = factory.GenericDictionaryLayout(_owningMethodOrType); - ICollection templateLayout = associatedLayout.GetTemplateEntries(factory); - - foreach (NativeLayoutVertexNode dictionaryEntry in templateLayout) - { - dictionaryEntry.CheckIfMarkedEnoughToWrite(); - sequence.Append(dictionaryEntry.WriteVertex(factory)); - } - - Vertex signature; - - GenericContextKind contextKind = ContextKind; - NativeWriter nativeWriter = GetNativeWriter(factory); - - if ((ContextKind & GenericContextKind.HasDeclaringType) != 0) - { - signature = nativeWriter.GetTuple(factory.NativeLayout.TypeSignatureVertex((TypeDesc)_owningMethodOrType).WriteVertex(factory), sequence); - } - else - { - signature = sequence; - } - - Vertex signatureWithContextKind = nativeWriter.GetTuple(nativeWriter.GetUnsignedConstant((uint)contextKind), signature); - return SetSavedVertex(factory.MetadataManager.NativeLayoutInfo.SignaturesSection.Place(signatureWithContextKind)); - } - - protected override string GetName(NodeFactory factory) => $"Dictionary layout signature for {_owningMethodOrType.ToString()}"; - } - - public sealed class NativeLayoutTemplateMethodLayoutVertexNode : NativeLayoutSavedVertexNode - { - private MethodDesc _method; - - protected override string GetName(NodeFactory factory) => "NativeLayoutTemplateMethodLayoutVertexNode" + factory.NameMangler.GetMangledMethodName(_method); - - public NativeLayoutTemplateMethodLayoutVertexNode(NodeFactory factory, MethodDesc method) - { - _method = method; - Debug.Assert(method.HasInstantiation); - Debug.Assert(method.IsCanonicalMethod(CanonicalFormKind.Any)); - Debug.Assert(method.GetCanonMethodTarget(CanonicalFormKind.Specific) == method, "Assert that the canonical method passed in is in standard canonical form"); - } - - public override IEnumerable GetStaticDependencies(NodeFactory context) - { - return Array.Empty(); - } - - private int CompareDictionaryEntries(KeyValuePair left, KeyValuePair right) - { - return left.Key - right.Key; - } - - public override Vertex WriteVertex(NodeFactory factory) - { - Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); - - VertexBag layoutInfo = new VertexBag(); - - DictionaryLayoutNode associatedLayout = factory.GenericDictionaryLayout(_method); - ICollection templateLayout = associatedLayout.GetTemplateEntries(factory); - - if (!(_method.IsCanonicalMethod(CanonicalFormKind.Universal) || (factory.LazyGenericsPolicy.UsesLazyGenerics(_method))) && (templateLayout.Count > 0)) - { - List dictionaryVertices = new List(); - - foreach (NativeLayoutVertexNode dictionaryEntry in templateLayout) - { - dictionaryEntry.CheckIfMarkedEnoughToWrite(); - dictionaryVertices.Add(dictionaryEntry); - } - NativeLayoutVertexNode dictionaryLayout = factory.NativeLayout.PlacedVertexSequence(dictionaryVertices); - - layoutInfo.Append(BagElementKind.DictionaryLayout, dictionaryLayout.WriteVertex(factory)); - } - - factory.MetadataManager.NativeLayoutInfo.TemplatesSection.Place(layoutInfo); - - return SetSavedVertex(layoutInfo); - } - } - - public sealed class NativeLayoutTemplateTypeLayoutVertexNode : NativeLayoutSavedVertexNode - { - private TypeDesc _type; - private bool _isUniversalCanon; - - protected override string GetName(NodeFactory factory) => "NativeLayoutTemplateTypeLayoutVertexNode_" + factory.NameMangler.GetMangledTypeName(_type); - - public NativeLayoutTemplateTypeLayoutVertexNode(NodeFactory factory, TypeDesc type) - { - Debug.Assert(type.IsCanonicalSubtype(CanonicalFormKind.Any)); - Debug.Assert(type.ConvertToCanonForm(CanonicalFormKind.Specific) == type, "Assert that the canonical type passed in is in standard canonical form"); - _isUniversalCanon = type.IsCanonicalSubtype(CanonicalFormKind.Universal); - - _type = GetActualTemplateTypeForType(factory, type); - } - - private static TypeDesc GetActualTemplateTypeForType(NodeFactory factory, TypeDesc type) - { - DefType defType = type as DefType; - if (defType == null) - { - Debug.Assert(GenericTypesTemplateMap.IsArrayTypeEligibleForTemplate(type)); - defType = type.GetClosestDefType().ConvertToSharedRuntimeDeterminedForm(); - Debug.Assert(defType.Instantiation.Length == 1); - return factory.TypeSystemContext.GetArrayType(defType.Instantiation[0]); - } - else - { - return defType.ConvertToSharedRuntimeDeterminedForm(); - } - } - - private ISymbolNode GetStaticsNode(NodeFactory context, out BagElementKind staticsBagKind) - { - ISymbolNode symbol; - - if (context is UtcNodeFactory) - { - symbol = ((UtcNodeFactory)context).TypeGCStaticDescSymbol((MetadataType)_type.GetClosestDefType().ConvertToCanonForm(CanonicalFormKind.Specific)); - staticsBagKind = BagElementKind.GcStaticDesc; - } - else - { - symbol = context.GCStaticEEType(GCPointerMap.FromStaticLayout(_type.GetClosestDefType())); - staticsBagKind = BagElementKind.GcStaticEEType; - } - - return symbol; - } - - private ISymbolNode GetThreadStaticsNode(NodeFactory context, out BagElementKind staticsBagKind) - { - ISymbolNode symbol; - - if (context is UtcNodeFactory) - { - symbol = ((UtcNodeFactory)context).TypeThreadStaticGCDescNode((MetadataType)_type.GetClosestDefType().ConvertToCanonForm(CanonicalFormKind.Specific)); - staticsBagKind = BagElementKind.ThreadStaticDesc; - } - else - { - symbol = context.GCStaticEEType(GCPointerMap.FromThreadStaticLayout(_type.GetClosestDefType())); - staticsBagKind = BagElementKind.End; // GC static EETypes not yet implemented in type loader - } - - return symbol; - } - - public override IEnumerable GetStaticDependencies(NodeFactory context) - { - yield return new DependencyListEntry(context.ConstructedTypeSymbol(_type.ConvertToCanonForm(CanonicalFormKind.Specific)), "Template EEType"); - - foreach (TypeDesc iface in _type.RuntimeInterfaces) - { - yield return new DependencyListEntry(context.NativeLayout.TypeSignatureVertex(iface), "template interface list"); - } - - if (context.TypeSystemContext.HasLazyStaticConstructor(_type)) - { - yield return new DependencyListEntry(context.MethodEntrypoint(_type.GetStaticConstructor().GetCanonMethodTarget(CanonicalFormKind.Specific)), "cctor for template"); - } - - if (!_isUniversalCanon) - { - if (_type.GetClosestDefType().GCStaticFieldSize.AsInt > 0) - { - BagElementKind ignored; - yield return new DependencyListEntry(GetStaticsNode(context, out ignored), "type gc static info"); - } - - if (_type.GetClosestDefType().ThreadStaticFieldSize.AsInt > 0) - { - BagElementKind ignored; - yield return new DependencyListEntry(GetThreadStaticsNode(context, out ignored), "type thread static info"); - } - } - - if (_type.BaseType != null && _type.BaseType.IsRuntimeDeterminedSubtype) - { - yield return new DependencyListEntry(context.NativeLayout.PlacedSignatureVertex(context.NativeLayout.TypeSignatureVertex(_type.BaseType)), "template base type"); - } - else if (_type.IsDelegate && _isUniversalCanon) - { - // For USG delegate, we need to write the signature of the Invoke method to the native layout. - // This signature is used by the calling convention converter to marshal parameters during delegate calls. - yield return new DependencyListEntry(context.NativeLayout.MethodSignatureVertex(_type.GetMethod("Invoke", null).GetTypicalMethodDefinition().Signature), "invoke method signature"); - } - - if (_isUniversalCanon) - { - // For universal canonical template types, we need to write out field layout information so that we - // can correctly compute the type sizes for dynamically created types at runtime, and construct - // their GCDesc info - foreach (FieldDesc field in _type.GetFields()) - { - // If this field does not contribute to layout, skip - if (field.HasRva || field.IsLiteral) - { - continue; - } - - DependencyListEntry typeForFieldLayout; - - if (field.FieldType.IsGCPointer) - { - typeForFieldLayout = new DependencyListEntry(context.NativeLayout.PlacedSignatureVertex(context.NativeLayout.TypeSignatureVertex(field.Context.GetWellKnownType(WellKnownType.Object))), "universal field layout type object sized"); - } - else if (field.FieldType.IsPointer || field.FieldType.IsFunctionPointer) - { - typeForFieldLayout = new DependencyListEntry(context.NativeLayout.PlacedSignatureVertex(context.NativeLayout.TypeSignatureVertex(field.Context.GetWellKnownType(WellKnownType.IntPtr))), "universal field layout type IntPtr sized"); - } - else - { - typeForFieldLayout = new DependencyListEntry(context.NativeLayout.PlacedSignatureVertex(context.NativeLayout.TypeSignatureVertex(field.FieldType)), "universal field layout type"); - } - - yield return typeForFieldLayout; - } - - // We also need to write out the signatures of interesting methods in the type's vtable, which - // will be needed by the calling convention translation logic at runtime, when the type's methods - // get invoked. This logic gathers nodes for entries *unconditionally* present. (entries may be conditionally - // present if a type has a vtable which has a size computed by usage not by IL contents) - List vtableSignatureNodeEntries = null; - int currentVTableIndexUnused = 0; - ProcessVTableEntriesForCallingConventionSignatureGeneration(context, VTableEntriesToProcess.AllOnTypesThatShouldProduceFullVTables, ref currentVTableIndexUnused, - (int vtableIndex, MethodDesc declMethod, MethodDesc implMethod) => - { - if (implMethod.IsAbstract) - return; - - if (UniversalGenericParameterLayout.MethodSignatureHasVarsNeedingCallingConventionConverter(implMethod.Signature)) - { - if (vtableSignatureNodeEntries == null) - vtableSignatureNodeEntries = new List(); - - vtableSignatureNodeEntries.Add(context.NativeLayout.MethodSignatureVertex(implMethod.GetTypicalMethodDefinition().Signature)); - } - } - , _type, _type); - - if (vtableSignatureNodeEntries != null) - { - foreach (NativeLayoutVertexNode node in vtableSignatureNodeEntries) - yield return new DependencyListEntry(node, "vtable cctor sig"); - } - } - } - - public override bool HasConditionalStaticDependencies => _isUniversalCanon; - public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) - { - List conditionalDependencies = null; - - if (_isUniversalCanon) - { - // We also need to write out the signatures of interesting methods in the type's vtable, which - // will be needed by the calling convention translation logic at runtime, when the type's methods - // get invoked. This logic gathers nodes for entries *conditionally* present. (entries may be conditionally - // present if a type has a vtable which has a size computed by usage not by IL contents) - - int currentVTableIndexUnused = 0; - ProcessVTableEntriesForCallingConventionSignatureGeneration(context, VTableEntriesToProcess.AllOnTypesThatProducePartialVTables, ref currentVTableIndexUnused, - (int vtableIndex, MethodDesc declMethod, MethodDesc implMethod) => - { - if (implMethod.IsAbstract) - return; - - if (UniversalGenericParameterLayout.MethodSignatureHasVarsNeedingCallingConventionConverter(implMethod.Signature)) - { - if (conditionalDependencies == null) - conditionalDependencies = new List(); - - conditionalDependencies.Add( - new CombinedDependencyListEntry(context.NativeLayout.MethodSignatureVertex(implMethod.GetTypicalMethodDefinition().Signature), - context.VirtualMethodUse(declMethod), - "conditional vtable cctor sig")); - } - } - , _type, _type); - } - - if (conditionalDependencies != null) - return conditionalDependencies; - else - return Array.Empty(); - } - - private int CompareDictionaryEntries(KeyValuePair left, KeyValuePair right) - { - return left.Key - right.Key; - } - - private bool HasInstantiationDeterminedSize() - { - Debug.Assert(_isUniversalCanon); - return _type.GetClosestDefType().InstanceFieldSize.IsIndeterminate; - } - - public override Vertex WriteVertex(NodeFactory factory) - { - Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); - - VertexBag layoutInfo = new VertexBag(); - - DictionaryLayoutNode associatedLayout = factory.GenericDictionaryLayout(_type.ConvertToCanonForm(CanonicalFormKind.Specific).GetClosestDefType()); - ICollection templateLayout = associatedLayout.GetTemplateEntries(factory); - - NativeWriter writer = GetNativeWriter(factory); - - // Interfaces - if (_type.RuntimeInterfaces.Length > 0) - { - List implementedInterfacesList = new List(); - - foreach (TypeDesc iface in _type.RuntimeInterfaces) - { - implementedInterfacesList.Add(factory.NativeLayout.TypeSignatureVertex(iface)); - } - NativeLayoutVertexNode implementedInterfaces = factory.NativeLayout.PlacedVertexSequence(implementedInterfacesList); - - layoutInfo.Append(BagElementKind.ImplementedInterfaces, implementedInterfaces.WriteVertex(factory)); - } - - if (!(_isUniversalCanon || (factory.LazyGenericsPolicy.UsesLazyGenerics(_type)) )&& (templateLayout.Count > 0)) - { - List dictionaryVertices = new List(); - - foreach (NativeLayoutVertexNode dictionaryEntry in templateLayout) - { - dictionaryEntry.CheckIfMarkedEnoughToWrite(); - dictionaryVertices.Add(dictionaryEntry); - } - NativeLayoutVertexNode dictionaryLayout = factory.NativeLayout.PlacedVertexSequence(dictionaryVertices); - - layoutInfo.Append(BagElementKind.DictionaryLayout, dictionaryLayout.WriteVertex(factory)); - } - - Internal.NativeFormat.TypeFlags typeFlags = default(Internal.NativeFormat.TypeFlags); - - if (factory.TypeSystemContext.HasLazyStaticConstructor(_type)) - { - MethodDesc cctorMethod = _type.GetStaticConstructor(); - MethodDesc canonCctorMethod = cctorMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); - ISymbolNode cctorSymbol = factory.MethodEntrypoint(canonCctorMethod); - uint cctorStaticsIndex = factory.MetadataManager.NativeLayoutInfo.StaticsReferences.GetIndex(cctorSymbol); - layoutInfo.AppendUnsigned(BagElementKind.ClassConstructorPointer, cctorStaticsIndex); - - typeFlags = typeFlags | Internal.NativeFormat.TypeFlags.HasClassConstructor; - } - - if (!_isUniversalCanon) - { - DefType closestDefType = _type.GetClosestDefType(); - if (closestDefType.NonGCStaticFieldSize.AsInt != 0) - { - layoutInfo.AppendUnsigned(BagElementKind.NonGcStaticDataSize, checked((uint)closestDefType.NonGCStaticFieldSize.AsInt)); - } - - if (closestDefType.GCStaticFieldSize.AsInt != 0) - { - layoutInfo.AppendUnsigned(BagElementKind.GcStaticDataSize, checked((uint)closestDefType.GCStaticFieldSize.AsInt)); - BagElementKind staticDescBagType; - ISymbolNode staticsDescSymbol = GetStaticsNode(factory, out staticDescBagType); - uint gcStaticsSymbolIndex = factory.MetadataManager.NativeLayoutInfo.StaticsReferences.GetIndex(staticsDescSymbol); - layoutInfo.AppendUnsigned(staticDescBagType, gcStaticsSymbolIndex); - } - - if (closestDefType.ThreadStaticFieldSize.AsInt != 0) - { - layoutInfo.AppendUnsigned(BagElementKind.ThreadStaticDataSize, checked((uint)closestDefType.ThreadStaticFieldSize.AsInt)); - BagElementKind threadStaticDescBagType; - ISymbolNode threadStaticsDescSymbol = GetThreadStaticsNode(factory, out threadStaticDescBagType); - uint threadStaticsSymbolIndex = factory.MetadataManager.NativeLayoutInfo.StaticsReferences.GetIndex(threadStaticsDescSymbol); - layoutInfo.AppendUnsigned(threadStaticDescBagType, threadStaticsSymbolIndex); - } - } - else - { - Debug.Assert(_isUniversalCanon); - // Determine if type has instantiation determined size - if (!_type.IsInterface && HasInstantiationDeterminedSize()) - { - typeFlags = typeFlags | Internal.NativeFormat.TypeFlags.HasInstantiationDeterminedSize; - } - } - - if (_type.BaseType != null && _type.BaseType.IsRuntimeDeterminedSubtype) - { - layoutInfo.Append(BagElementKind.BaseType, factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.TypeSignatureVertex(_type.BaseType)).WriteVertex(factory)); - } - else if (_type.IsDelegate && _isUniversalCanon) - { - // For USG delegate, we need to write the signature of the Invoke method to the native layout. - // This signature is used by the calling convention converter to marshal parameters during delegate calls. - MethodDesc delegateInvokeMethod = _type.GetMethod("Invoke", null).GetTypicalMethodDefinition(); - NativeLayoutMethodSignatureVertexNode invokeSignatureVertexNode = factory.NativeLayout.MethodSignatureVertex(delegateInvokeMethod.Signature); - layoutInfo.Append(BagElementKind.DelegateInvokeSignature, invokeSignatureVertexNode.WriteVertex(factory)); - } - - if (typeFlags != default(Internal.NativeFormat.TypeFlags)) - layoutInfo.AppendUnsigned(BagElementKind.TypeFlags, (uint)typeFlags); - - if (_type.GetTypeDefinition().HasVariance || factory.TypeSystemContext.IsGenericArrayInterfaceType(_type)) - { - // Runtime casting logic relies on all interface types implemented on arrays - // to have the variant flag set (even if all the arguments are non-variant). - // This supports e.g. casting uint[] to ICollection - List varianceFlags = new List(); - foreach (GenericParameterDesc param in _type.GetTypeDefinition().Instantiation) - { - varianceFlags.Add((uint)param.Variance); - } - - layoutInfo.Append(BagElementKind.GenericVarianceInfo, factory.NativeLayout.PlacedUIntVertexSequence(varianceFlags).WriteVertex(factory)); - } - else if (_type.GetTypeDefinition() == factory.ArrayOfTEnumeratorType) - { - // Generic array enumerators use special variance rules recognized by the runtime - List varianceFlag = new List(); - varianceFlag.Add((uint)Internal.Runtime.GenericVariance.ArrayCovariant); - layoutInfo.Append(BagElementKind.GenericVarianceInfo, factory.NativeLayout.PlacedUIntVertexSequence(varianceFlag).WriteVertex(factory)); - } - - if (_isUniversalCanon) - { - // For universal canonical template types, we need to write out field layout information so that we - // can correctly compute the type sizes for dynamically created types at runtime, and construct - // their GCDesc info - VertexSequence fieldsSequence = null; - - foreach (FieldDesc field in _type.GetFields()) - { - // If this field does contribute to layout, skip - if (field.HasRva || field.IsLiteral) - continue; - - // NOTE: The order and contents of the signature vertices emitted here is what we consider a field ordinal for the - // purpose of NativeLayoutFieldOffsetGenericDictionarySlotNode. - - FieldStorage fieldStorage = FieldStorage.Instance; - if (field.IsStatic) - { - if (field.IsThreadStatic) - fieldStorage = FieldStorage.TLSStatic; - else if (field.HasGCStaticBase) - fieldStorage = FieldStorage.GCStatic; - else - fieldStorage = FieldStorage.NonGCStatic; - } - - - NativeLayoutVertexNode fieldTypeSignature; - if (field.FieldType.IsGCPointer) - { - fieldTypeSignature = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.TypeSignatureVertex(field.Context.GetWellKnownType(WellKnownType.Object))); - } - else if (field.FieldType.IsPointer || field.FieldType.IsFunctionPointer) - { - fieldTypeSignature = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.TypeSignatureVertex(field.Context.GetWellKnownType(WellKnownType.IntPtr))); - } - else - { - fieldTypeSignature = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.TypeSignatureVertex(field.FieldType)); - } - - Vertex staticFieldVertexData = writer.GetTuple(fieldTypeSignature.WriteVertex(factory), writer.GetUnsignedConstant((uint)fieldStorage)); - - if (fieldsSequence == null) - fieldsSequence = new VertexSequence(); - fieldsSequence.Append(staticFieldVertexData); - } - - if (fieldsSequence != null) - { - Vertex placedFieldsLayout = factory.MetadataManager.NativeLayoutInfo.SignaturesSection.Place(fieldsSequence); - layoutInfo.Append(BagElementKind.FieldLayout, placedFieldsLayout); - } - - // We also need to write out the signatures of interesting methods in the type's vtable, which - // will be needed by the calling convention translation logic at runtime, when the type's methods - // get invoked. - int currentVTableIndexUnused = 0; - VertexSequence vtableSignaturesSequence = null; - - ProcessVTableEntriesForCallingConventionSignatureGeneration(factory, VTableEntriesToProcess.AllInVTable, ref currentVTableIndexUnused, - (int vtableIndex, MethodDesc declMethod, MethodDesc implMethod) => - { - if (implMethod.IsAbstract) - return; - - if (UniversalGenericParameterLayout.MethodSignatureHasVarsNeedingCallingConventionConverter(implMethod.Signature)) - { - if (vtableSignaturesSequence == null) - vtableSignaturesSequence = new VertexSequence(); - - NativeLayoutVertexNode signatureVertex = factory.NativeLayout.MethodSignatureVertex(implMethod.GetTypicalMethodDefinition().Signature); - NativeLayoutVertexNode placedSignatureVertex = factory.NativeLayout.PlacedSignatureVertex(signatureVertex); - - Vertex vtableSignatureEntry = writer.GetTuple(writer.GetUnsignedConstant(((uint)vtableIndex) << 1), // We currently do not use sealed vtable entries yet. Update when that happens - placedSignatureVertex.WriteVertex(factory)); - - vtableSignaturesSequence.Append(vtableSignatureEntry); - } - } - , _type, _type); - - if (vtableSignaturesSequence != null) - { - Vertex placedVtableSigs = factory.MetadataManager.NativeLayoutInfo.SignaturesSection.Place(vtableSignaturesSequence); - layoutInfo.Append(BagElementKind.VTableMethodSignatures, placedVtableSigs); - } - } - - factory.MetadataManager.NativeLayoutInfo.TemplatesSection.Place(layoutInfo); - - return SetSavedVertex(layoutInfo); - } - - private enum VTableEntriesToProcess - { - AllInVTable, - AllOnTypesThatShouldProduceFullVTables, - AllOnTypesThatProducePartialVTables - } - - private static IEnumerable EnumVirtualSlotsDeclaredOnType(TypeDesc declType) - { - // VirtualMethodUse of Foo.Method will bring in VirtualMethodUse - // of Foo<__Canon>.Method. This in turn should bring in Foo.Method. - DefType defType = declType.GetClosestDefType(); - - Debug.Assert(!declType.IsInterface); - - IEnumerable allSlots = defType.EnumAllVirtualSlots(); - - foreach (var method in allSlots) - { - // Generic virtual methods are tracked by an orthogonal mechanism. - if (method.HasInstantiation) - continue; - - // Current type doesn't define this slot. Another VTableSlice will take care of this. - if (method.OwningType != defType) - continue; - - yield return method; - } - } - - /// - /// Process the vtable entries of a type by calling operation with the vtable index, declaring method, and implementing method - /// Process them in order from 0th entry to last. - /// Skip generic virtual methods, as they are not present in the vtable itself - /// Do not adjust vtable index for generic dictionary slot - /// The vtable index is only actually valid if whichEntries is set to VTableEntriesToProcess.AllInVTable - /// - private void ProcessVTableEntriesForCallingConventionSignatureGeneration(NodeFactory factory, VTableEntriesToProcess whichEntries, ref int currentVTableIndex, Action operation, TypeDesc implType, TypeDesc declType) - { - if (implType.IsInterface) - return; - - declType = declType.GetClosestDefType(); - - var baseType = declType.BaseType; - if (baseType != null) - ProcessVTableEntriesForCallingConventionSignatureGeneration(factory, whichEntries, ref currentVTableIndex, operation, implType, baseType); - - IEnumerable vtableEntriesToProcess; - - switch (whichEntries) - { - case VTableEntriesToProcess.AllInVTable: - vtableEntriesToProcess = factory.VTable(declType).Slots; - break; - - case VTableEntriesToProcess.AllOnTypesThatShouldProduceFullVTables: - if (factory.CompilationModuleGroup.ShouldProduceFullVTable(declType)) - { - vtableEntriesToProcess = factory.VTable(declType).Slots; - } - else - { - vtableEntriesToProcess = Array.Empty(); - } - break; - - case VTableEntriesToProcess.AllOnTypesThatProducePartialVTables: - if (factory.CompilationModuleGroup.ShouldProduceFullVTable(declType)) - { - vtableEntriesToProcess = Array.Empty(); - } - else - { - vtableEntriesToProcess = EnumVirtualSlotsDeclaredOnType(declType); - } - break; - - default: - throw new Exception(); - } - - // Actual vtable slots follow - foreach (MethodDesc declMethod in vtableEntriesToProcess) - { - // No generic virtual methods can appear in the vtable! - Debug.Assert(!declMethod.HasInstantiation); - - MethodDesc implMethod = implType.GetClosestDefType().FindVirtualFunctionTargetMethodOnObjectType(declMethod); - - operation(currentVTableIndex, declMethod, implMethod); - currentVTableIndex++; - } - } - } - - public abstract class NativeLayoutGenericDictionarySlotNode : NativeLayoutVertexNode - { - public abstract override IEnumerable GetStaticDependencies(NodeFactory context); - protected abstract Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory); - protected abstract FixupSignatureKind SignatureKind { get; } - - public override Vertex WriteVertex(NodeFactory factory) - { - CheckIfMarkedEnoughToWrite(); - - NativeWriter writer = GetNativeWriter(factory); - return writer.GetFixupSignature(SignatureKind, WriteSignatureVertex(writer, factory)); - } - } - - public abstract class NativeLayoutTypeSignatureBasedGenericDictionarySlotNode : NativeLayoutGenericDictionarySlotNode - { - NativeLayoutTypeSignatureVertexNode _signature; - TypeDesc _type; - - public NativeLayoutTypeSignatureBasedGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) - { - _signature = factory.NativeLayout.TypeSignatureVertex(type); - _type = type; - } - - protected abstract string NodeTypeName { get; } - protected sealed override string GetName(NodeFactory factory) => NodeTypeName + factory.NameMangler.GetMangledTypeName(_type); - - public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) - { - return new DependencyListEntry[1] { new DependencyListEntry(_signature, "TypeSignature") }; - } - - protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) - { - return _signature.WriteVertex(factory); - } - } - - public sealed class NativeLayoutTypeHandleGenericDictionarySlotNode : NativeLayoutTypeSignatureBasedGenericDictionarySlotNode - { - public NativeLayoutTypeHandleGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) - { - } - - protected override string NodeTypeName => "NativeLayoutTypeHandleGenericDictionarySlotNode_"; - - protected override FixupSignatureKind SignatureKind => FixupSignatureKind.TypeHandle; - } - - public sealed class NativeLayoutUnwrapNullableGenericDictionarySlotNode : NativeLayoutTypeSignatureBasedGenericDictionarySlotNode - { - public NativeLayoutUnwrapNullableGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) - { - } - - protected override string NodeTypeName => "NativeLayoutUnwrapNullableGenericDictionarySlotNode_"; - - protected override FixupSignatureKind SignatureKind => FixupSignatureKind.UnwrapNullableType; - } - - public sealed class NativeLayoutTypeSizeGenericDictionarySlotNode : NativeLayoutTypeSignatureBasedGenericDictionarySlotNode - { - public NativeLayoutTypeSizeGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) - { - } - - protected override string NodeTypeName => "NativeLayoutTypeSizeGenericDictionarySlotNode_"; - - protected override FixupSignatureKind SignatureKind => FixupSignatureKind.TypeSize; - } - - public sealed class NativeLayoutAllocateObjectGenericDictionarySlotNode : NativeLayoutTypeSignatureBasedGenericDictionarySlotNode - { - public NativeLayoutAllocateObjectGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) - { - } - - protected override string NodeTypeName => "NativeLayoutAllocateObjectGenericDictionarySlotNode_"; - - protected override FixupSignatureKind SignatureKind => FixupSignatureKind.AllocateObject; - } - - public sealed class NativeLayoutCastClassGenericDictionarySlotNode : NativeLayoutTypeSignatureBasedGenericDictionarySlotNode - { - public NativeLayoutCastClassGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) - { - } - - protected override string NodeTypeName => "NativeLayoutAllocateCastClassDictionarySlotNode_"; - - protected override FixupSignatureKind SignatureKind => FixupSignatureKind.CastClass; - } - - public sealed class NativeLayoutIsInstGenericDictionarySlotNode : NativeLayoutTypeSignatureBasedGenericDictionarySlotNode - { - public NativeLayoutIsInstGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) - { - } - - protected override string NodeTypeName => "NativeLayoutIsInstGenericDictionarySlotNode_"; - - protected override FixupSignatureKind SignatureKind => FixupSignatureKind.IsInst; - } - - public sealed class NativeLayoutTlsIndexGenericDictionarySlotNode : NativeLayoutTypeSignatureBasedGenericDictionarySlotNode - { - public NativeLayoutTlsIndexGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) - { - } - - protected override string NodeTypeName => "NativeLayoutTlsIndexGenericDictionarySlotNode_"; - - protected override FixupSignatureKind SignatureKind => FixupSignatureKind.TlsIndex; - } - - public sealed class NativeLayoutTlsOffsetGenericDictionarySlotNode : NativeLayoutTypeSignatureBasedGenericDictionarySlotNode - { - public NativeLayoutTlsOffsetGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) - { - } - - protected override string NodeTypeName => "NativeLayoutTlsOffsetGenericDictionarySlotNode_"; - - protected override FixupSignatureKind SignatureKind => FixupSignatureKind.TlsOffset; - } - - public sealed class NativeLayoutDefaultConstructorGenericDictionarySlotNode : NativeLayoutTypeSignatureBasedGenericDictionarySlotNode - { - public NativeLayoutDefaultConstructorGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) - { - } - - protected override string NodeTypeName => "NativeLayoutDefaultConstructorGenericDictionarySlotNode_"; - - protected override FixupSignatureKind SignatureKind => FixupSignatureKind.DefaultConstructor; - } - - public sealed class NativeLayoutAllocateArrayGenericDictionarySlotNode : NativeLayoutTypeSignatureBasedGenericDictionarySlotNode - { - public NativeLayoutAllocateArrayGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) - { - Debug.Assert(type.IsArray); // TODO! Verify that the passed in type is the array type and not the element type of the array. - } - - protected override string NodeTypeName => "NativeLayoutAllocateArrayGenericDictionarySlotNode_"; - - protected override FixupSignatureKind SignatureKind => FixupSignatureKind.AllocateArray; - } - - public abstract class NativeLayoutStaticsGenericDictionarySlotNode : NativeLayoutGenericDictionarySlotNode - { - NativeLayoutTypeSignatureVertexNode _signature; - TypeDesc _type; - - public NativeLayoutStaticsGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) - { - _signature = factory.NativeLayout.TypeSignatureVertex(type); - _type = type; - } - - protected abstract StaticDataKind StaticDataKindFlag { get; } - protected abstract string NodeTypeName { get; } - - protected sealed override string GetName(NodeFactory factory) => NodeTypeName + factory.NameMangler.GetMangledTypeName(_type); - - protected sealed override FixupSignatureKind SignatureKind => FixupSignatureKind.StaticData; - public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) - { - return new DependencyListEntry[1] { new DependencyListEntry(_signature, "TypeSignature") }; - } - - protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) - { - return writer.GetStaticDataSignature(_signature.WriteVertex(factory), StaticDataKindFlag); - } - } - - public sealed class NativeLayoutGcStaticsGenericDictionarySlotNode : NativeLayoutStaticsGenericDictionarySlotNode - { - public NativeLayoutGcStaticsGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) - { } - - protected override StaticDataKind StaticDataKindFlag => StaticDataKind.Gc; - protected override string NodeTypeName => "NativeLayoutGcStaticsGenericDictionarySlotNode_"; - } - - public sealed class NativeLayoutNonGcStaticsGenericDictionarySlotNode : NativeLayoutStaticsGenericDictionarySlotNode - { - public NativeLayoutNonGcStaticsGenericDictionarySlotNode(NodeFactory factory, TypeDesc type) : base(factory, type) - { } - - protected override StaticDataKind StaticDataKindFlag => StaticDataKind.NonGc; - protected override string NodeTypeName => "NativeLayoutNonGcStaticsGenericDictionarySlotNode_"; - } - - public sealed class NativeLayoutInterfaceDispatchGenericDictionarySlotNode : NativeLayoutGenericDictionarySlotNode - { - NativeLayoutTypeSignatureVertexNode _signature; - MethodDesc _method; - - public NativeLayoutInterfaceDispatchGenericDictionarySlotNode(NodeFactory factory, MethodDesc method) - { - _signature = factory.NativeLayout.TypeSignatureVertex(method.OwningType); - _method = method; - } - - protected sealed override string GetName(NodeFactory factory) => "NativeLayoutInterfaceDispatchGenericDictionarySlotNode_" + factory.NameMangler.GetMangledMethodName(_method); - - protected sealed override FixupSignatureKind SignatureKind => FixupSignatureKind.InterfaceCall; - public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) - { - return new DependencyListEntry[1] { new DependencyListEntry(_signature, "TypeSignature") }; - } - - protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) - { - int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, _method); - - return writer.GetMethodSlotSignature(_signature.WriteVertex(factory), checked((uint)slot)); - } - } - - public sealed class NativeLayoutMethodDictionaryGenericDictionarySlotNode : NativeLayoutGenericDictionarySlotNode - { - MethodDesc _method; - WrappedMethodDictionaryVertexNode _wrappedNode; - - private class WrappedMethodDictionaryVertexNode : NativeLayoutMethodEntryVertexNode - { - public WrappedMethodDictionaryVertexNode(NodeFactory factory, MethodDesc method) : - base(factory, method, default(MethodEntryFlags)) - { - } - - protected override IMethodNode GetMethodEntrypointNode(NodeFactory factory, out bool unboxingStub) - { - throw new NotSupportedException(); - } - - protected sealed override string GetName(NodeFactory factory) => "WrappedMethodEntryVertexNodeForDictionarySlot_" + factory.NameMangler.GetMangledMethodName(_method); - } - - - public NativeLayoutMethodDictionaryGenericDictionarySlotNode(NodeFactory factory, MethodDesc method) - { - Debug.Assert(method.HasInstantiation); - _method = method; - _wrappedNode = new WrappedMethodDictionaryVertexNode(factory, method); - } - - protected sealed override string GetName(NodeFactory factory) => "NativeLayoutMethodDictionaryGenericDictionarySlotNode_" + factory.NameMangler.GetMangledMethodName(_method); - protected sealed override FixupSignatureKind SignatureKind => FixupSignatureKind.MethodDictionary; - public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) - { - return new DependencyListEntry[] { new DependencyListEntry(_wrappedNode, "wrappednode") }; - } - - protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) - { - return _wrappedNode.WriteVertex(factory); - } - } - - public sealed class NativeLayoutFieldOffsetGenericDictionarySlotNode : NativeLayoutGenericDictionarySlotNode - { - FieldDesc _field; - - public NativeLayoutFieldOffsetGenericDictionarySlotNode(FieldDesc field) - { - _field = field; - } - - protected sealed override string GetName(NodeFactory factory) => "NativeLayoutFieldOffsetGenericDictionarySlotNode_" + factory.NameMangler.GetMangledFieldName(_field); - - protected sealed override FixupSignatureKind SignatureKind => FixupSignatureKind.FieldOffset; - - public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) - { - return new DependencyListEntry[1] { new DependencyListEntry(factory.NativeLayout.TypeSignatureVertex(_field.OwningType), "Field Containing Type Signature") }; - } - - protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) - { - NativeWriter nativeWriter = GetNativeWriter(factory); - - uint fieldOrdinal = 0; - - foreach (FieldDesc field in _field.OwningType.GetFields()) - { - // If this field does contribute to layout, skip - if (field.HasRva || field.IsLiteral) - continue; - - // NOTE: The order and contents of the field ordinal emitted here is based on the order of emission for fields - // in the USG template generation. - - if (field == _field) - { - Vertex typeVertex = factory.NativeLayout.TypeSignatureVertex(_field.OwningType).WriteVertex(factory); - return nativeWriter.GetTuple(typeVertex, nativeWriter.GetUnsignedConstant(fieldOrdinal)); - } - fieldOrdinal++; - } - - // If we reach here, we were unable to calculate field ordinal. - Debug.Assert(false, "This should be unreachable, as we should have found a field ordinal above"); - throw new Exception("Internal Compiler Error"); - } - } - - public sealed class NativeLayoutFieldLdTokenGenericDictionarySlotNode : NativeLayoutGenericDictionarySlotNode - { - FieldDesc _field; - - public NativeLayoutFieldLdTokenGenericDictionarySlotNode(FieldDesc field) - { - _field = field; - } - - protected sealed override string GetName(NodeFactory factory) => "NativeLayoutFieldLdTokenGenericDictionarySlotNode_" + factory.NameMangler.GetMangledFieldName(_field); - - protected sealed override FixupSignatureKind SignatureKind => FixupSignatureKind.FieldLdToken; - - public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) - { - return new DependencyListEntry[1] { new DependencyListEntry(factory.NativeLayout.FieldLdTokenVertex(_field), "Field Signature") }; - } - - protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) - { - return factory.NativeLayout.FieldLdTokenVertex(_field).WriteVertex(factory); - } - } - - public sealed class NativeLayoutVTableOffsetGenericDictionarySlotNode : NativeLayoutGenericDictionarySlotNode - { - MethodDesc _method; - MethodDesc _slotDefiningMethod; - - public NativeLayoutVTableOffsetGenericDictionarySlotNode(MethodDesc method) - { - _method = method; - MethodDesc typicalSlotDefiningMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method.GetTypicalMethodDefinition()); - _slotDefiningMethod = _method.OwningType.FindMethodOnTypeWithMatchingTypicalMethod(typicalSlotDefiningMethod).GetCanonMethodTarget(CanonicalFormKind.Specific); - Debug.Assert(!method.HasInstantiation); - Debug.Assert(!method.OwningType.IsInterface); - Debug.Assert(method.OwningType.IsDefType); - Debug.Assert(method.IsVirtual); - } - - protected sealed override string GetName(NodeFactory factory) => "NativeLayoutVTableOffsetGenericDictionarySlotNode_" + factory.NameMangler.GetMangledMethodName(_method); - - protected sealed override FixupSignatureKind SignatureKind => FixupSignatureKind.VTableOffset; - - public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) - { - return new DependencyListEntry[1] { new DependencyListEntry(factory.NativeLayout.TypeSignatureVertex(_slotDefiningMethod.OwningType), "Method VTableOffset Containing Type Signature") }; - } - - protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) - { - NativeWriter nativeWriter = GetNativeWriter(factory); - - int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, _slotDefiningMethod); - Vertex typeVertex = factory.NativeLayout.TypeSignatureVertex(_slotDefiningMethod.OwningType).WriteVertex(factory); - return nativeWriter.GetTuple(typeVertex, nativeWriter.GetUnsignedConstant((uint)slot)); - } - } - - public sealed class NativeLayoutMethodLdTokenGenericDictionarySlotNode : NativeLayoutGenericDictionarySlotNode - { - MethodDesc _method; - - public NativeLayoutMethodLdTokenGenericDictionarySlotNode(MethodDesc method) - { - _method = method; - } - - protected sealed override string GetName(NodeFactory factory) => "NativeLayoutMethodLdTokenGenericDictionarySlotNode_" + factory.NameMangler.GetMangledMethodName(_method); - - protected sealed override FixupSignatureKind SignatureKind => FixupSignatureKind.MethodLdToken; - - public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) - { - return new DependencyListEntry[1] { new DependencyListEntry(factory.NativeLayout.MethodLdTokenVertex(_method), "Method Signature") }; - } - - protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) - { - return factory.NativeLayout.MethodLdTokenVertex(_method).WriteVertex(factory); - } - } - - public sealed class NativeLayoutCallingConventionConverterGenericDictionarySlotNode : NativeLayoutGenericDictionarySlotNode - { - Internal.TypeSystem.MethodSignature _signature; - CallingConventionConverterKind _converterKind; - - public NativeLayoutCallingConventionConverterGenericDictionarySlotNode(Internal.TypeSystem.MethodSignature signature, CallingConventionConverterKind converterKind) - { - _signature = signature; - _converterKind = converterKind; - } - - protected sealed override string GetName(NodeFactory factory) => - "NativeLayoutCallingConventionConverterGenericDictionarySlotNode" + _converterKind.ToString() + - _signature.GetName(); - - protected sealed override FixupSignatureKind SignatureKind => FixupSignatureKind.MethodLdToken; - - public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) - { - return new DependencyListEntry[1] { new DependencyListEntry(factory.NativeLayout.MethodSignatureVertex(_signature), "Method Signature") }; - } - - protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) - { - Vertex signatureStream = factory.NativeLayout.MethodSignatureVertex(_signature).WriteVertex(factory); - return GetNativeWriter(factory).GetCallingConventionConverterSignature((uint)_converterKind, signatureStream); - } - } - - public sealed class NativeLayoutConstrainedMethodDictionarySlotNode : NativeLayoutGenericDictionarySlotNode - { - MethodDesc _constrainedMethod; - TypeDesc _constraintType; - bool _directCall; - - public NativeLayoutConstrainedMethodDictionarySlotNode(MethodDesc constrainedMethod, TypeDesc constraintType, bool directCall) - { - _constrainedMethod = constrainedMethod; - _constraintType = constraintType; - _directCall = directCall; - Debug.Assert(_constrainedMethod.OwningType.IsInterface); - Debug.Assert(!_constrainedMethod.HasInstantiation || !directCall); - } - - protected sealed override string GetName(NodeFactory factory) => - "NativeLayoutConstrainedMethodDictionarySlotNode_" - + (_directCall ? "Direct" : "") - + factory.NameMangler.GetMangledMethodName(_constrainedMethod) - + "," - + factory.NameMangler.GetMangledTypeName(_constraintType); - - protected sealed override FixupSignatureKind SignatureKind => _constrainedMethod.HasInstantiation ? FixupSignatureKind.GenericConstrainedMethod : - (_directCall ? FixupSignatureKind.NonGenericDirectConstrainedMethod : FixupSignatureKind.NonGenericConstrainedMethod); - - public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) - { - DependencyNodeCore constrainedMethodDescriptorNode; - if (_constrainedMethod.HasInstantiation) - { - constrainedMethodDescriptorNode = factory.NativeLayout.MethodLdTokenVertex(_constrainedMethod); - } - else - { - constrainedMethodDescriptorNode = factory.NativeLayout.TypeSignatureVertex(_constrainedMethod.OwningType); - } - - return new DependencyListEntry[] { - new DependencyListEntry(factory.NativeLayout.TypeSignatureVertex(_constraintType), "ConstraintType"), - new DependencyListEntry(constrainedMethodDescriptorNode, "ConstrainedMethodType"), - }; - } - - protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) - { - Vertex constraintType = factory.NativeLayout.TypeSignatureVertex(_constraintType).WriteVertex(factory); - if (_constrainedMethod.HasInstantiation) - { - Debug.Assert(SignatureKind == FixupSignatureKind.GenericConstrainedMethod); - Vertex constrainedMethodVertex = factory.NativeLayout.MethodLdTokenVertex(_constrainedMethod).WriteVertex(factory); - return writer.GetTuple(constraintType, constrainedMethodVertex); - } - else - { - Debug.Assert((SignatureKind == FixupSignatureKind.NonGenericConstrainedMethod) || (SignatureKind == FixupSignatureKind.NonGenericDirectConstrainedMethod)); - Vertex methodType = factory.NativeLayout.TypeSignatureVertex(_constrainedMethod.OwningType).WriteVertex(factory); - int interfaceSlot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, _constrainedMethod.GetCanonMethodTarget(CanonicalFormKind.Specific)); - Vertex interfaceSlotVertex = writer.GetUnsignedConstant(checked((uint)interfaceSlot)); - return writer.GetTuple(constraintType, methodType, interfaceSlotVertex); - } - } - } - - public sealed class NativeLayoutMethodEntrypointGenericDictionarySlotNode : NativeLayoutGenericDictionarySlotNode - { - MethodDesc _method; - WrappedMethodEntryVertexNode _wrappedNode; - - private class WrappedMethodEntryVertexNode : NativeLayoutMethodEntryVertexNode - { - public bool _unboxingStub; - public IMethodNode _functionPointerNode; - - public WrappedMethodEntryVertexNode(NodeFactory factory, MethodDesc method, bool unboxingStub, IMethodNode functionPointerNode) : - base(factory, method, functionPointerNode != null ? MethodEntryFlags.SaveEntryPoint : default(MethodEntryFlags)) - { - _unboxingStub = unboxingStub; - _functionPointerNode = functionPointerNode; - } - - protected override IMethodNode GetMethodEntrypointNode(NodeFactory factory, out bool unboxingStub) - { - unboxingStub = _unboxingStub; - return _functionPointerNode; - } - - protected sealed override string GetName(NodeFactory factory) => "WrappedMethodEntryVertexNodeForDictionarySlot_" + (_unboxingStub ? "Unboxing_" : "") + factory.NameMangler.GetMangledMethodName(_method); - } - - - public NativeLayoutMethodEntrypointGenericDictionarySlotNode(NodeFactory factory, MethodDesc method, IMethodNode functionPointerNode, bool unboxingStub) - { - _method = method; - _wrappedNode = new WrappedMethodEntryVertexNode(factory, method, unboxingStub, functionPointerNode); - } - - protected sealed override string GetName(NodeFactory factory) => "NativeLayoutMethodEntrypointGenericDictionarySlotNode_" + (_wrappedNode._unboxingStub ? "Unboxing_" : "") + factory.NameMangler.GetMangledMethodName(_method); - protected sealed override FixupSignatureKind SignatureKind => FixupSignatureKind.Method; - public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) - { - return new DependencyListEntry[] { new DependencyListEntry(_wrappedNode, "wrappednode") }; - } - - protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) - { - return _wrappedNode.WriteVertex(factory); - } - } - - public sealed class NativeLayoutIntegerDictionarySlotNode : NativeLayoutGenericDictionarySlotNode - { - int _value; - - public NativeLayoutIntegerDictionarySlotNode(int value) - { - _value = value; - } - - protected override FixupSignatureKind SignatureKind => FixupSignatureKind.IntValue; - - public override IEnumerable GetStaticDependencies(NodeFactory context) - { - return null; - } - - protected override string GetName(NodeFactory context) => "NativeLayoutIntegerDictionarySlotNode_" + _value.ToStringInvariant(); - - protected override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) - { - return writer.GetUnsignedConstant((uint)_value); - } - - public override void CheckIfMarkedEnoughToWrite() - { - // Do nothing, this node does not need marking - } - } - - public sealed class NativeLayoutPointerToOtherSlotDictionarySlotNode : NativeLayoutGenericDictionarySlotNode - { - int _otherSlotIndex; - - public NativeLayoutPointerToOtherSlotDictionarySlotNode(int otherSlotIndex) - { - _otherSlotIndex = otherSlotIndex; - } - - protected override FixupSignatureKind SignatureKind => FixupSignatureKind.PointerToOtherSlot; - - public override IEnumerable GetStaticDependencies(NodeFactory context) - { - return null; - } - - protected override string GetName(NodeFactory context) => "NativeLayoutPointerToOtherSlotDictionarySlotNode_" + _otherSlotIndex.ToStringInvariant(); - - protected override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) - { - return writer.GetUnsignedConstant((uint)_otherSlotIndex); - } - - public override void CheckIfMarkedEnoughToWrite() - { - // Do nothing, this node does not need marking - } - } - - public sealed class NativeLayoutNotSupportedDictionarySlotNode : NativeLayoutGenericDictionarySlotNode - { - protected override FixupSignatureKind SignatureKind => FixupSignatureKind.NotYetSupported; - - public override IEnumerable GetStaticDependencies(NodeFactory context) - { - return null; - } - - protected override string GetName(NodeFactory context) => "NativeLayoutNotSupportedDictionarySlotNode"; - - protected override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) - { - return writer.GetUnsignedConstant(0xDEADBEEF); - } - } -} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs.REMOVED.git-id b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs.REMOVED.git-id new file mode 100644 index 0000000000..aa698a15c8 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs.REMOVED.git-id @@ -0,0 +1 @@ +6c1be4884e98026a251a0ea6f4bd4f3d823dfddc \ No newline at end of file diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NecessaryCanonicalEETypeNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NecessaryCanonicalEETypeNode.cs index 0222257777..775c607576 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NecessaryCanonicalEETypeNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NecessaryCanonicalEETypeNode.cs @@ -9,7 +9,7 @@ using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis { - /// + /// /// The node is used in ProjectX to represent a canonical type that does not have a vtable. /// internal sealed class NecessaryCanonicalEETypeNode : EETypeNode @@ -24,7 +24,9 @@ namespace ILCompiler.DependencyAnalysis protected override ISymbolNode GetBaseTypeNode(NodeFactory factory) { - return _type.BaseType != null ? factory.NecessaryTypeSymbol(GetFullCanonicalTypeForCanonicalType(_type.BaseType)) : null; + return _type.BaseType != null ? factory.NecessaryTypeSymbol(_type.NormalizedBaseType()) : null; } + + protected internal override int ClassCode => 1505000724; } -} \ No newline at end of file +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.GenericLookups.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.GenericLookups.cs index 73bb229d8e..a7649e88a7 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.GenericLookups.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.GenericLookups.cs @@ -53,14 +53,9 @@ namespace ILCompiler.DependencyAnalysis return new MethodEntryGenericLookupResult(key.Method, key.IsUnboxingStub); }); - _virtualCallHelpers = new NodeCache(method => + _virtualDispatchCells = new NodeCache(method => { - return new VirtualDispatchGenericLookupResult(method); - }); - - _virtualResolveHelpers = new NodeCache(method => - { - return new VirtualResolveGenericLookupResult(method); + return new VirtualDispatchCellGenericLookupResult(method); }); _typeThreadStaticBaseIndexSymbols = new NodeCache(type => @@ -206,18 +201,11 @@ namespace ILCompiler.DependencyAnalysis return _methodDictionaries.GetOrAdd(method); } - private NodeCache _virtualCallHelpers; + private NodeCache _virtualDispatchCells; - public GenericLookupResult VirtualCall(MethodDesc method) + public GenericLookupResult VirtualDispatchCell(MethodDesc method) { - return _virtualCallHelpers.GetOrAdd(method); - } - - private NodeCache _virtualResolveHelpers; - - public GenericLookupResult VirtualMethodAddress(MethodDesc method) - { - return _virtualResolveHelpers.GetOrAdd(method); + return _virtualDispatchCells.GetOrAdd(method); } private NodeCache _methodEntrypoints; @@ -309,6 +297,26 @@ namespace ILCompiler.DependencyAnalysis { return _constrainedMethodUses.GetOrAdd(new ConstrainedMethodUseKey(constrainedMethod, constraintType, directCall)); } + + private static NodeCache s_integers = new NodeCache(slotIndex => + { + return new IntegerLookupResult(slotIndex); + }); + + public static GenericLookupResult Integer(int integer) + { + return s_integers.GetOrAdd(integer); + } + + private static NodeCache s_pointersToSlots = new NodeCache(slotIndex => + { + return new PointerToSlotLookupResult(slotIndex); + }); + + public static GenericLookupResult PointerToSlot(int slotIndex) + { + return s_pointersToSlots.GetOrAdd(slotIndex); + } } public GenericLookupResults GenericLookup = new GenericLookupResults(); diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs index 36e05bb042..5392ad1cde 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using Internal.Text; using Internal.TypeSystem; +using ILCompiler.DependencyAnalysisFramework; namespace ILCompiler.DependencyAnalysis { @@ -210,6 +211,68 @@ namespace ILCompiler.DependencyAnalysis }); } + // Produce a set of dependencies that is necessary such that if this type + // needs to be used referenced from a NativeLayout template, that the template + // will be properly constructable. (This is done by ensuring that all + // canonical types in the deconstruction of the type are ConstructedEEType instead + // of just necessary. (Which is what the actual templates signatures will ensure) + public IEnumerable TemplateConstructableTypes(TypeDesc type) + { + if ((_factory.Target.Abi == TargetAbi.ProjectN) && !ProjectNDependencyBehavior.EnableFullAnalysis) + yield break; + + while (type.IsParameterizedType) + { + type = ((ParameterizedType)type).ParameterType; + } + + TypeDesc canonicalType = type.ConvertToCanonForm(CanonicalFormKind.Specific); + yield return _factory.MaximallyConstructableType(canonicalType); + + // Add a dependency on the template for this type, if the canonical type should be generated into this binary. + if (canonicalType.IsCanonicalSubtype(CanonicalFormKind.Any) && !_factory.NecessaryTypeSymbol(canonicalType).RepresentsIndirectionCell) + { + if (!_factory.TypeSystemContext.IsCanonicalDefinitionType(canonicalType, CanonicalFormKind.Any)) + yield return _factory.NativeLayout.TemplateTypeLayout(canonicalType); + } + + foreach (TypeDesc instantiationType in type.Instantiation) + { + foreach (var dependency in TemplateConstructableTypes(instantiationType)) + yield return dependency; + } + } + + // Produce a set of dependencies that is necessary such that if this type + // needs to be used referenced from a NativeLayout template and any Universal Shared + // instantiation is all that is needed, that the template + // will be properly constructable. (This is done by ensuring that all + // canonical types in the deconstruction of the type are ConstructedEEType instead + // of just necessary, and that the USG variant of the template is created + // (Which is what the actual templates signatures will ensure) + public IEnumerable UniversalTemplateConstructableTypes(TypeDesc type) + { + if ((_factory.Target.Abi == TargetAbi.ProjectN) && !ProjectNDependencyBehavior.EnableFullAnalysis) + yield break; + + while (type.IsParameterizedType) + { + type = ((ParameterizedType)type).ParameterType; + } + + if (type.IsSignatureVariable) + yield break; + + TypeDesc canonicalType = type.ConvertToCanonForm(CanonicalFormKind.Universal); + yield return _factory.MaximallyConstructableType(canonicalType); + + foreach (TypeDesc instantiationType in type.Instantiation) + { + foreach (var dependency in UniversalTemplateConstructableTypes(instantiationType)) + yield return dependency; + } + } + private NodeCache _typeSignatures; internal NativeLayoutTypeSignatureVertexNode TypeSignatureVertex(TypeDesc type) { diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.WindowsDebugData.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.WindowsDebugData.cs new file mode 100644 index 0000000000..75af21b3d6 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.WindowsDebugData.cs @@ -0,0 +1,108 @@ +// 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.Diagnostics; +using Internal.TypeSystem.TypesDebugInfo; +using ILCompiler.DependencyAnalysisFramework; + +namespace ILCompiler.DependencyAnalysis +{ + // Part of Node factory that deals with nodes describing windows debug data + partial class NodeFactory + { + /// + /// Helper class that provides a level of grouping for all the windows debug data nodes + /// + public class WindowsDebugDataHelper + { + NodeFactory _nodeFactory; + + public WindowsDebugDataHelper(NodeFactory nodeFactory) + { + _nodeFactory = nodeFactory; + } + + /// + /// Initialize the WindowsDebugData emission pipeline. + /// Cannot be called twice + /// + /// debug$T section generation interface. If null, use managed implementation that generates a object file section. + /// record of how assemblies are merged. If null, do not genearate extended debug information + /// Graph to attach WindowsDebugData to + public void Init(ITypesDebugInfoWriter nonSectionBasedDebugInfoWriter, MergedAssemblyRecords mergedAssemblyRecords, DependencyAnalyzerBase graph) + { + Debug.Assert(_userDefinedTypeDescriptor == null); // Cannot be called twice + Debug.Assert(graph != null); + + _debugNeedTypeIndicesStore = new WindowsDebugNeedTypeIndicesStoreNode(); + + if (mergedAssemblyRecords != null) + { + _debugPseudoAssemblySection = new WindowsDebugPseudoAssemblySection(_nodeFactory.TypeSystemContext); + _debugMergedAssembliesSection = new WindowsDebugMergedAssembliesSection(mergedAssemblyRecords); + _debugILImagesSection = new WindowsDebugILImagesSection(mergedAssemblyRecords); + _debugManagedNativeDictionaryInfoSection = new WindowsDebugManagedNativeDictionaryInfoSection(); + + _debugTypeSignatureMapSection = new WindowsDebugTypeSignatureMapSection(_userDefinedTypeDescriptor); + _debugMethodSignatureMapSection = new WindowsDebugMethodSignatureMapSection(); + _debugVirtualMethodInfoSection = new WindowsDebugMethodInfoSection(mergedAssemblyRecords); + } + + if (nonSectionBasedDebugInfoWriter != null) + { + _userDefinedTypeDescriptor = new UserDefinedTypeDescriptor(nonSectionBasedDebugInfoWriter, _nodeFactory); + } + else + { + _debugTypeRecordsSection = new WindowsDebugTypeRecordsSection(new DebugInfoWriter(), _nodeFactory); + _userDefinedTypeDescriptor = new UserDefinedTypeDescriptor(_debugTypeRecordsSection, _nodeFactory); + } + + graph.AddRoot(_debugNeedTypeIndicesStore, "Debug Force All EETypes to have type indices"); + + if (_debugManagedNativeDictionaryInfoSection != null) + graph.AddRoot(_debugManagedNativeDictionaryInfoSection, "Debug Method MDToken map"); + if (_debugMethodSignatureMapSection != null) + graph.AddRoot(_debugMethodSignatureMapSection, "Debug Method MDToken map"); + if (_debugTypeSignatureMapSection != null) + graph.AddRoot(_debugTypeSignatureMapSection, "Debug Type MDToken map"); + if (_debugILImagesSection != null) + graph.AddRoot(_debugILImagesSection, "Debug Merged ILImages"); + if (_debugMergedAssembliesSection != null) + graph.AddRoot(_debugMergedAssembliesSection, "Debug MergedAssemblyRecords"); + if (_debugPseudoAssemblySection != null) + graph.AddRoot(_debugPseudoAssemblySection, "Debug PseudoAssembly"); + if (_debugTypeRecordsSection != null) + graph.AddRoot(_debugTypeRecordsSection, "Debug Type Records"); + if (_debugVirtualMethodInfoSection != null) + graph.AddRoot(_debugVirtualMethodInfoSection, "Debug Virtual Method map"); + } + + private WindowsDebugILImagesSection _debugILImagesSection; + private WindowsDebugTypeSignatureMapSection _debugTypeSignatureMapSection; + private WindowsDebugTypeRecordsSection _debugTypeRecordsSection; + private WindowsDebugManagedNativeDictionaryInfoSection _debugManagedNativeDictionaryInfoSection; + private WindowsDebugMethodSignatureMapSection _debugMethodSignatureMapSection; + private WindowsDebugMergedAssembliesSection _debugMergedAssembliesSection; + private WindowsDebugPseudoAssemblySection _debugPseudoAssemblySection; + private UserDefinedTypeDescriptor _userDefinedTypeDescriptor; + private WindowsDebugNeedTypeIndicesStoreNode _debugNeedTypeIndicesStore; + private WindowsDebugMethodInfoSection _debugVirtualMethodInfoSection; + + internal WindowsDebugILImagesSection DebugILImagesSection => _debugILImagesSection; + internal WindowsDebugTypeSignatureMapSection DebugTypeSignatureMapSection => _debugTypeSignatureMapSection; + internal WindowsDebugMethodSignatureMapSection DebugMethodSignatureMapSection => _debugMethodSignatureMapSection; + internal WindowsDebugTypeRecordsSection DebugTypeRecordsSection => _debugTypeRecordsSection; + internal WindowsDebugMergedAssembliesSection DebugMergedAssembliesSection => _debugMergedAssembliesSection; + internal WindowsDebugPseudoAssemblySection DebugPseudoAssemblySection => _debugPseudoAssemblySection; + internal WindowsDebugManagedNativeDictionaryInfoSection DebugManagedNativeDictionaryInfoSection => _debugManagedNativeDictionaryInfoSection; + internal WindowsDebugNeedTypeIndicesStoreNode DebugNeedTypeIndicesStore => _debugNeedTypeIndicesStore; + internal WindowsDebugMethodInfoSection DebugVirtualMethodInfoSection => _debugVirtualMethodInfoSection; + + public UserDefinedTypeDescriptor UserDefinedTypeDescriptor => _userDefinedTypeDescriptor; + } + + public WindowsDebugDataHelper WindowsDebugData { get; private set; } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs index 37bea13c0f..2a25d967bc 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs @@ -23,19 +23,33 @@ namespace ILCompiler.DependencyAnalysis private TargetDetails _target; private CompilerTypeSystemContext _context; private CompilationModuleGroup _compilationModuleGroup; + private VTableSliceProvider _vtableSliceProvider; + private DictionaryLayoutProvider _dictionaryLayoutProvider; + protected readonly ImportedNodeProvider _importedNodeProvider; private bool _markingComplete; - public NodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, - MetadataManager metadataManager, NameMangler nameMangler, LazyGenericsPolicy lazyGenericsPolicy) + public NodeFactory( + CompilerTypeSystemContext context, + CompilationModuleGroup compilationModuleGroup, + MetadataManager metadataManager, + InteropStubManager interoptStubManager, + NameMangler nameMangler, + LazyGenericsPolicy lazyGenericsPolicy, + VTableSliceProvider vtableSliceProvider, + DictionaryLayoutProvider dictionaryLayoutProvider, + ImportedNodeProvider importedNodeProvider) { _target = context.Target; _context = context; _compilationModuleGroup = compilationModuleGroup; + _vtableSliceProvider = vtableSliceProvider; + _dictionaryLayoutProvider = dictionaryLayoutProvider; NameMangler = nameMangler; - InteropStubManager = new InteropStubManager(compilationModuleGroup, context, new InteropStateManager(compilationModuleGroup.GeneratedAssembly)); + InteropStubManager = interoptStubManager; CreateNodeCaches(); MetadataManager = metadataManager; LazyGenericsPolicy = lazyGenericsPolicy; + _importedNodeProvider = importedNodeProvider; } public void SetMarkingComplete() @@ -200,38 +214,30 @@ namespace ILCompiler.DependencyAnalysis _importedTypeSymbols = new NodeCache((TypeDesc type) => { Debug.Assert(_compilationModuleGroup.ShouldReferenceThroughImportTable(type)); - return new ImportedEETypeSymbolNode(this, type); + return _importedNodeProvider.ImportedEETypeNode(this, type); }); - _nonGCStatics = new NodeCache((MetadataType type) => + _nonGCStatics = new NodeCache((MetadataType type) => { - if (_compilationModuleGroup.ContainsType(type)) + if (_compilationModuleGroup.ContainsType(type) && !_compilationModuleGroup.ShouldReferenceThroughImportTable(type)) { return new NonGCStaticsNode(type, this); } - else if (_compilationModuleGroup.ShouldReferenceThroughImportTable(type)) - { - return new ImportedNonGCStaticsNode(this, type); - } else { - return new ExternSymbolNode(NonGCStaticsNode.GetMangledName(type, NameMangler)); + return _importedNodeProvider.ImportedNonGCStaticNode(this, type); } }); - _GCStatics = new NodeCache((MetadataType type) => + _GCStatics = new NodeCache((MetadataType type) => { - if (_compilationModuleGroup.ContainsType(type)) + if (_compilationModuleGroup.ContainsType(type) && !_compilationModuleGroup.ShouldReferenceThroughImportTable(type)) { return new GCStaticsNode(type); } - else if (_compilationModuleGroup.ShouldReferenceThroughImportTable(type)) - { - return new ImportedGCStaticsNode(this, type); - } else { - return new ExternSymbolNode(GCStaticsNode.GetMangledName(type, NameMangler)); + return _importedNodeProvider.ImportedGCStaticNode(this, type); } }); @@ -285,6 +291,11 @@ namespace ILCompiler.DependencyAnalysis _unboxingStubs = new NodeCache(CreateUnboxingStubNode); + _methodAssociatedData = new NodeCache(methodNode => + { + return new MethodAssociatedDataNode(methodNode); + }); + _fatFunctionPointers = new NodeCache(method => { return new FatFunctionPointerNode(method.Method, method.IsUnboxingStub); @@ -327,9 +338,9 @@ namespace ILCompiler.DependencyAnalysis _virtMethods = new NodeCache((MethodDesc method) => { - // We don't need to track virtual method uses for types that are producing full vtables. + // We don't need to track virtual method uses for types that have a vtable with a known layout. // It's a waste of CPU time and memory. - Debug.Assert(!CompilationModuleGroup.ShouldProduceFullVTable(method.OwningType)); + Debug.Assert(!VTable(method.OwningType).HasFixedSlots); return new VirtualMethodUseNode(method); }); @@ -346,7 +357,7 @@ namespace ILCompiler.DependencyAnalysis return new ReadyToRunGenericLookupFromTypeNode(this, data.HelperId, data.Target, data.DictionaryOwner); }); - _indirectionNodes = new NodeCache(indirectedNode => + _indirectionNodes = new NodeCache(indirectedNode => { return new IndirectionNode(Target, indirectedNode, 0); }); @@ -383,11 +394,7 @@ namespace ILCompiler.DependencyAnalysis _interfaceDispatchMapIndirectionNodes = new NodeCache((TypeDesc type) => { - var dispatchMap = InterfaceDispatchMap(type); - return DispatchMapTable.NewNodeWithSymbol(dispatchMap, (indirectionNode) => - { - dispatchMap.SetDispatchMapIndex(this, DispatchMapTable.IndexOfEmbeddedObject(indirectionNode)); - }); + return DispatchMapTable.NewNodeWithSymbol(InterfaceDispatchMap(type)); }); _genericCompositions = new NodeCache((GenericCompositionDetails details) => @@ -401,37 +408,42 @@ namespace ILCompiler.DependencyAnalysis Debug.Assert(TypeSystemContext.HasEagerStaticConstructor((MetadataType)method.OwningType)); return EagerCctorTable.NewNode(MethodEntrypoint(method)); }); - + + _namedJumpStubNodes = new NodeCache, NamedJumpStubNode>((Tuple id) => + { + return new NamedJumpStubNode(id.Item1, id.Item2); + }); + _vTableNodes = new NodeCache((TypeDesc type ) => { if (CompilationModuleGroup.ShouldProduceFullVTable(type)) return new EagerlyBuiltVTableSliceNode(type); else - return new LazilyBuiltVTableSliceNode(type); + return _vtableSliceProvider.GetSlice(type); }); - _methodGenericDictionaries = new NodeCache(method => + _methodGenericDictionaries = new NodeCache(method => { if (CompilationModuleGroup.ContainsMethodDictionary(method)) { - return new MethodGenericDictionaryNode(method); + return new MethodGenericDictionaryNode(method, this); } else { - return new ImportedMethodGenericDictionaryNode(this, method); + return _importedNodeProvider.ImportedMethodDictionaryNode(this, method); } }); - _typeGenericDictionaries = new NodeCache(type => + _typeGenericDictionaries = new NodeCache(type => { - if (CompilationModuleGroup.ContainsType(type)) + if (CompilationModuleGroup.ContainsTypeDictionary(type)) { Debug.Assert(!this.LazyGenericsPolicy.UsesLazyGenerics(type)); - return new TypeGenericDictionaryNode(type); + return new TypeGenericDictionaryNode(type, this); } else { - return new ImportedTypeGenericDictionaryNode(this, type); + return _importedNodeProvider.ImportedTypeDictionaryNode(this, type); } }); @@ -455,17 +467,20 @@ namespace ILCompiler.DependencyAnalysis return new ModuleMetadataNode(module); }); - _genericDictionaryLayouts = new NodeCache(methodOrType => - { - return new DictionaryLayoutNode(methodOrType); - }); + _genericDictionaryLayouts = new NodeCache(_dictionaryLayoutProvider.GetLayout); _stringAllocators = new NodeCache(constructor => { return new StringAllocatorMethodNode(constructor); }); + _defaultConstructorFromLazyNodes = new NodeCache(type => + { + return new DefaultConstructorFromLazyNode(type); + }); + NativeLayout = new NativeLayoutHelper(this); + WindowsDebugData = new WindowsDebugDataHelper(this); } protected abstract IMethodNode CreateMethodEntrypointNode(MethodDesc method); @@ -514,6 +529,14 @@ namespace ILCompiler.DependencyAnalysis private NodeCache _clonedTypeSymbols; + public IEETypeNode MaximallyConstructableType(TypeDesc type) + { + if (ConstructedEETypeNode.CreationAllowed(type)) + return ConstructedTypeSymbol(type); + else + return NecessaryTypeSymbol(type); + } + public IEETypeNode ConstructedClonedTypeSymbol(TypeDesc type) { Debug.Assert(!TypeCannotHaveEEType(type)); @@ -528,17 +551,17 @@ namespace ILCompiler.DependencyAnalysis return _importedTypeSymbols.GetOrAdd(type); } - private NodeCache _nonGCStatics; + private NodeCache _nonGCStatics; - public ISymbolNode TypeNonGCStaticsSymbol(MetadataType type) + public ISortableSymbolNode TypeNonGCStaticsSymbol(MetadataType type) { Debug.Assert(!TypeCannotHaveEEType(type)); return _nonGCStatics.GetOrAdd(type); } - private NodeCache _GCStatics; + private NodeCache _GCStatics; - public ISymbolNode TypeGCStaticsSymbol(MetadataType type) + public ISortableSymbolNode TypeGCStaticsSymbol(MetadataType type) { Debug.Assert(!TypeCannotHaveEEType(type)); return _GCStatics.GetOrAdd(type); @@ -641,7 +664,7 @@ namespace ILCompiler.DependencyAnalysis private NodeCache _externSymbols; - public ISymbolNode ExternSymbol(string name) + public ISortableSymbolNode ExternSymbol(string name) { return _externSymbols.GetOrAdd(name); } @@ -667,20 +690,20 @@ namespace ILCompiler.DependencyAnalysis return _vTableNodes.GetOrAdd(type); } - private NodeCache _methodGenericDictionaries; - public ISymbolNode MethodGenericDictionary(MethodDesc method) + private NodeCache _methodGenericDictionaries; + public ISortableSymbolNode MethodGenericDictionary(MethodDesc method) { return _methodGenericDictionaries.GetOrAdd(method); } - private NodeCache _typeGenericDictionaries; - public ISymbolNode TypeGenericDictionary(TypeDesc type) + private NodeCache _typeGenericDictionaries; + public ISortableSymbolNode TypeGenericDictionary(TypeDesc type) { return _typeGenericDictionaries.GetOrAdd(type); } private NodeCache _genericDictionaryLayouts; - public virtual DictionaryLayoutNode GenericDictionaryLayout(TypeSystemEntity methodOrType) + public DictionaryLayoutNode GenericDictionaryLayout(TypeSystemEntity methodOrType) { return _genericDictionaryLayouts.GetOrAdd(methodOrType); } @@ -693,6 +716,7 @@ namespace ILCompiler.DependencyAnalysis private NodeCache _methodEntrypoints; private NodeCache _unboxingStubs; + private NodeCache _methodAssociatedData; public IMethodNode MethodEntrypoint(MethodDesc method, bool unboxingStub = false) { @@ -704,6 +728,11 @@ namespace ILCompiler.DependencyAnalysis return _methodEntrypoints.GetOrAdd(method); } + public MethodAssociatedDataNode MethodAssociatedData(IMethodNode methodNode) + { + return _methodAssociatedData.GetOrAdd(methodNode); + } + private NodeCache _fatFunctionPointers; public IMethodNode FatFunctionPointer(MethodDesc method, bool isUnboxingStub = false) @@ -730,7 +759,7 @@ namespace ILCompiler.DependencyAnalysis } private NodeCache _gvmDependenciesNode; - internal GVMDependenciesNode GVMDependencies(MethodDesc method) + public GVMDependenciesNode GVMDependencies(MethodDesc method) { return _gvmDependenciesNode.GetOrAdd(method); } @@ -742,25 +771,29 @@ namespace ILCompiler.DependencyAnalysis } private NodeCache _reflectableMethods; - internal ReflectableMethodNode ReflectableMethod(MethodDesc method) + public ReflectableMethodNode ReflectableMethod(MethodDesc method) { return _reflectableMethods.GetOrAdd(method); } private NodeCache _shadowConcreteMethods; - public IMethodNode ShadowConcreteMethod(MethodDesc method, bool isUnboxingStub = false) { return _shadowConcreteMethods.GetOrAdd(new MethodKey(method, isUnboxingStub)); } private NodeCache _runtimeDeterminedMethods; - public IMethodNode RuntimeDeterminedMethod(MethodDesc method) { return _runtimeDeterminedMethods.GetOrAdd(method); } + private NodeCache _defaultConstructorFromLazyNodes; + internal DefaultConstructorFromLazyNode DefaultConstructorFromLazy(TypeDesc type) + { + return _defaultConstructorFromLazyNodes.GetOrAdd(type); + } + private static readonly string[][] s_helperEntrypointNames = new string[][] { new string[] { "System.Runtime.CompilerServices", "ClassConstructorRunner", "CheckStaticClassConstructionReturnGCStaticBase" }, new string[] { "System.Runtime.CompilerServices", "ClassConstructorRunner", "CheckStaticClassConstructionReturnNonGCStaticBase" }, @@ -860,9 +893,9 @@ namespace ILCompiler.DependencyAnalysis return _genericReadyToRunHelpersFromType.GetOrAdd(new ReadyToRunGenericHelperKey(id, target, dictionaryOwner)); } - private NodeCache _indirectionNodes; + private NodeCache _indirectionNodes; - public ISymbolNode Indirection(ISymbolNode symbol) + public ISymbolNode Indirection(ISortableSymbolNode symbol) { if (symbol.RepresentsIndirectionCell) { @@ -934,6 +967,13 @@ namespace ILCompiler.DependencyAnalysis return ReadOnlyDataBlob(symbolName, stringBytes, 1); } + private NodeCache, NamedJumpStubNode> _namedJumpStubNodes; + + public ISymbolNode NamedJumpStub(string name, ISymbolNode target) + { + return _namedJumpStubNodes.GetOrAdd(new Tuple(name, target)); + } + /// /// Returns alternative symbol name that object writer should produce for given symbols /// in addition to the regular one. @@ -948,13 +988,13 @@ namespace ILCompiler.DependencyAnalysis public ArrayOfEmbeddedPointersNode GCStaticsRegion = new ArrayOfEmbeddedPointersNode( "__GCStaticRegionStart", - "__GCStaticRegionEnd", - null); + "__GCStaticRegionEnd", + new SortableDependencyNode.ObjectNodeComparer(new CompilerComparer())); - public ArrayOfEmbeddedDataNode ThreadStaticsRegion = new ArrayOfEmbeddedDataNode( + public ArrayOfEmbeddedDataNode ThreadStaticsRegion = new ArrayOfEmbeddedDataNode( "__ThreadStaticRegionStart", "__ThreadStaticRegionEnd", - null); + new SortableDependencyNode.EmbeddedObjectNodeComparer(new CompilerComparer())); public ArrayOfEmbeddedPointersNode EagerCctorTable = new ArrayOfEmbeddedPointersNode( "__EagerCctorStart", @@ -964,12 +1004,17 @@ namespace ILCompiler.DependencyAnalysis public ArrayOfEmbeddedPointersNode DispatchMapTable = new ArrayOfEmbeddedPointersNode( "__DispatchMapTableStart", "__DispatchMapTableEnd", - null); + new SortableDependencyNode.ObjectNodeComparer(new CompilerComparer())); public ArrayOfEmbeddedDataNode FrozenSegmentRegion = new ArrayOfFrozenObjectsNode( "__FrozenSegmentRegionStart", "__FrozenSegmentRegionEnd", - null); + new SortableDependencyNode.EmbeddedObjectNodeComparer(new CompilerComparer())); + + public ArrayOfEmbeddedPointersNode ImportAddressTablesTable = new ArrayOfEmbeddedPointersNode( + "__ImportTablesTableStart", + "__ImportTablesTableEnd", + new SortableDependencyNode.ObjectNodeComparer(new CompilerComparer())); public ReadyToRunHeaderNode ReadyToRunHeader; @@ -990,6 +1035,15 @@ namespace ILCompiler.DependencyAnalysis graph.AddRoot(TypeManagerIndirection, "TypeManagerIndirection is always generated"); graph.AddRoot(DispatchMapTable, "DispatchMapTable is always generated"); graph.AddRoot(FrozenSegmentRegion, "FrozenSegmentRegion is always generated"); + if (Target.IsWindows) + { + // We need 2 delimiter symbols to bound the unboxing stubs region on Windows platforms (these symbols are + // accessed using extern "C" variables in the bootstrapper) + // On non-Windows platforms, the linker emits special symbols with special names at the begining/end of a section + // so we do not need to emit them ourselves. + graph.AddRoot(new WindowsUnboxingStubsRegionNode(false), "UnboxingStubsRegion delimiter for Windows platform"); + graph.AddRoot(new WindowsUnboxingStubsRegionNode(true), "UnboxingStubsRegion delimiter for Windows platform"); + } ReadyToRunHeader.Add(ReadyToRunSectionType.GCStaticRegion, GCStaticsRegion, GCStaticsRegion.StartSymbol, GCStaticsRegion.EndSymbol); ReadyToRunHeader.Add(ReadyToRunSectionType.ThreadStaticRegion, ThreadStaticsRegion, ThreadStaticsRegion.StartSymbol, ThreadStaticsRegion.EndSymbol); @@ -998,7 +1052,9 @@ namespace ILCompiler.DependencyAnalysis ReadyToRunHeader.Add(ReadyToRunSectionType.InterfaceDispatchTable, DispatchMapTable, DispatchMapTable.StartSymbol); ReadyToRunHeader.Add(ReadyToRunSectionType.FrozenObjectRegion, FrozenSegmentRegion, FrozenSegmentRegion.StartSymbol, FrozenSegmentRegion.EndSymbol); - MetadataManager.AddToReadyToRunHeader(ReadyToRunHeader, this); + var commonFixupsTableNode = new ExternalReferencesTableNode("CommonFixupsTable", this); + InteropStubManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode); + MetadataManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode); MetadataManager.AttachToDependencyGraph(graph); } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonExternMethodSymbolNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonExternMethodSymbolNode.cs index d097d512f9..1b09590cac 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonExternMethodSymbolNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonExternMethodSymbolNode.cs @@ -2,23 +2,34 @@ // 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.Collections.Generic; using System.Diagnostics; using ILCompiler.DependencyAnalysisFramework; +using Internal.Text; using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis { + public static class ProjectNDependencyBehavior + { + // Temporary static variable to enable full analysis when using the ProjectN abi + // When full analysis is fully supported, remove this class and field forever. + public static bool EnableFullAnalysis = false; + } + /// /// Represents a symbol that is defined externally but modeled as a method /// in the DependencyAnalysis infrastructure during compilation that is compiled /// in the current compilation process /// - public class NonExternMethodSymbolNode : ExternSymbolNode, IMethodNode + public class NonExternMethodSymbolNode : ExternSymbolNode, IMethodBodyNodeWithFuncletSymbols, ISpecialUnboxThunkNode, IExportableSymbolNode { private MethodDesc _method; + private bool _isUnboxing; private List _compilationDiscoveredDependencies; + ISymbolNode[] _funcletSymbols = Array.Empty(); bool _dependenciesQueried; bool _hasCompiledBody; @@ -26,7 +37,22 @@ namespace ILCompiler.DependencyAnalysis : base(isUnboxing ? UnboxingStubNode.GetMangledName(factory.NameMangler, method) : factory.NameMangler.GetMangledMethodName(method)) { + _isUnboxing = isUnboxing; _method = method; + + // Ensure all method bodies are fully canonicalized or not at all. + Debug.Assert(!method.IsCanonicalMethod(CanonicalFormKind.Any) || (method.GetCanonMethodTarget(CanonicalFormKind.Specific) == method)); + Debug.Assert(!method.IsCanonicalMethod(CanonicalFormKind.Universal) || (method.GetCanonMethodTarget(CanonicalFormKind.Universal) == method)); + } + + protected override string GetName(NodeFactory factory) => "Non" + base.GetName(factory); + + public ExportForm GetExportForm(NodeFactory factory) + { + ExportForm exportForm = factory.CompilationModuleGroup.GetExportMethodForm(_method, IsSpecialUnboxingThunk); + if (exportForm == ExportForm.ByName) + return ExportForm.None; // Non-extern symbols exported by name are naturally handled by the linker + return exportForm; } public MethodDesc Method @@ -37,6 +63,26 @@ namespace ILCompiler.DependencyAnalysis } } + public bool IsSpecialUnboxingThunk + { + get + { + if (_isUnboxing) + { + if (!_method.HasInstantiation && _method.OwningType.IsValueType && !_method.Signature.IsStatic) + return _method.IsCanonicalMethod(CanonicalFormKind.Any); + } + + return false; + } + } + public ISymbolNode GetUnboxingThunkTarget(NodeFactory factory) + { + Debug.Assert(IsSpecialUnboxingThunk); + + return factory.MethodEntrypoint(_method.GetCanonMethodTarget(CanonicalFormKind.Specific), false); + } + public bool HasCompiledBody => _hasCompiledBody; public void SetHasCompiledBody() { @@ -45,6 +91,16 @@ namespace ILCompiler.DependencyAnalysis _hasCompiledBody = true; } + public void SetFuncletCount(int funcletCount) + { + Debug.Assert(funcletCount > 0); + Debug.Assert(_funcletSymbols.Length == 0); + ISymbolNode[] funclets = new ISymbolNode[funcletCount]; + for (int funcletId = 1; funcletId <= funcletCount; funcletId++) + funclets[funcletId - 1] = new FuncletSymbol(this, funcletId); + _funcletSymbols = funclets; + } + public void AddCompilationDiscoveredDependency(IDependencyNode node, string reason) { Debug.Assert(!_dependenciesQueried); @@ -57,13 +113,21 @@ namespace ILCompiler.DependencyAnalysis { get { - // TODO Change this to - // return HasCompiledBody; - // when we fix up creation of NonExternMethodSymbolNode to be correctly handled - return true; + if (ProjectNDependencyBehavior.EnableFullAnalysis) + return HasCompiledBody; + else + return true; } } + ISymbolNode[] IMethodBodyNodeWithFuncletSymbols.FuncletSymbols + { + get + { + return _funcletSymbols; + } + } + public override IEnumerable GetStaticDependencies(NodeFactory factory) { _dependenciesQueried = true; @@ -76,7 +140,57 @@ namespace ILCompiler.DependencyAnalysis dependencies.AddRange(_compilationDiscoveredDependencies); } + if (MethodAssociatedDataNode.MethodHasAssociatedData(factory, this)) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(new DependencyListEntry(factory.MethodAssociatedData(this), "Method associated data")); + } + return dependencies; } + + private class FuncletSymbol : ISymbolNodeWithFuncletId + { + public FuncletSymbol(NonExternMethodSymbolNode methodSymbol, int funcletId) + { + _funcletId = funcletId; + _methodSymbol = methodSymbol; + } + + private int _funcletId; + private NonExternMethodSymbolNode _methodSymbol; + + public ISymbolNode AssociatedMethodSymbol => _methodSymbol; + + public int FuncletId => _funcletId; + + public int Offset => 0; + + public bool RepresentsIndirectionCell => false; + public bool InterestingForDynamicDependencyAnalysis => false; + public bool HasDynamicDependencies => false; + public bool HasConditionalStaticDependencies => false; + public bool StaticDependenciesAreComputed => true; + public bool Marked => _methodSymbol.Marked; + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + _methodSymbol.AppendMangledName(nameMangler, sb); + } + + public IEnumerable GetConditionalStaticDependencies(NodeFactory context) + { + return null; + } + + public IEnumerable GetStaticDependencies(NodeFactory context) + { + return null; + } + + public IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) + { + return null; + } + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonGCStaticsNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonGCStaticsNode.cs index dbe02b7e00..dc6ade07cb 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonGCStaticsNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonGCStaticsNode.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using Internal.Text; using Internal.TypeSystem; @@ -17,16 +18,23 @@ namespace ILCompiler.DependencyAnalysis /// with the class constructor context if the type has a class constructor that /// needs to be triggered before the type members can be accessed. /// - public class NonGCStaticsNode : ObjectNode, IExportableSymbolNode + public class NonGCStaticsNode : ObjectNode, IExportableSymbolNode, ISortableSymbolNode { private MetadataType _type; private NodeFactory _factory; + private List _sortedPreInitFields; public NonGCStaticsNode(MetadataType type, NodeFactory factory) { Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Specific)); _type = type; _factory = factory; + var preInitFieldInfos = PreInitFieldInfo.GetPreInitFieldInfos(_type, hasGCStaticBase: false); + if (preInitFieldInfos != null) + { + _sortedPreInitFields = new List(preInitFieldInfos); + _sortedPreInitFields.Sort(PreInitFieldInfo.FieldDescCompare); + } } protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); @@ -65,9 +73,9 @@ namespace ILCompiler.DependencyAnalysis public MetadataType Type => _type; - public virtual bool IsExported(NodeFactory factory) + public virtual ExportForm GetExportForm(NodeFactory factory) { - return factory.CompilationModuleGroup.ExportsType(Type); + return factory.CompilationModuleGroup.GetExportTypeForm(Type); } private static int GetClassConstructorContextSize(TargetDetails target) @@ -94,14 +102,17 @@ namespace ILCompiler.DependencyAnalysis protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { + DependencyList dependencyList = null; + if (factory.TypeSystemContext.HasEagerStaticConstructor(_type)) { - var result = new DependencyList(); - result.Add(factory.EagerCctorIndirection(_type.GetStaticConstructor()), "Eager .cctor"); - return result; + dependencyList = new DependencyList(); + dependencyList.Add(factory.EagerCctorIndirection(_type.GetStaticConstructor()), "Eager .cctor"); } - return null; + EETypeNode.AddDependenciesForStaticsNode(factory, _type, ref dependencyList); + + return dependencyList; } public override ObjectData GetData(NodeFactory factory, bool relocsOnly) @@ -131,10 +142,54 @@ namespace ILCompiler.DependencyAnalysis builder.RequireInitialAlignment(_type.NonGCStaticFieldAlignment.AsInt); } - builder.EmitZeros(_type.NonGCStaticFieldSize.AsInt); + if (_sortedPreInitFields != null) + { + int staticOffsetBegin = builder.CountBytes; + int staticOffsetEnd = builder.CountBytes + _type.NonGCStaticFieldSize.AsInt; + int staticOffset = staticOffsetBegin; + int idx = 0; + + while (staticOffset < staticOffsetEnd) + { + int writeTo = staticOffsetEnd; + if (idx < _sortedPreInitFields.Count) + writeTo = staticOffsetBegin + _sortedPreInitFields[idx].Field.Offset.AsInt; + + // Emit the zeros before the next preinitField + builder.EmitZeros(writeTo - staticOffset); + staticOffset = writeTo; + + // Emit the data + if (idx < _sortedPreInitFields.Count) + { + _sortedPreInitFields[idx].WriteData(ref builder, factory); + idx++; + staticOffset = builder.CountBytes; + } + } + } + else + { + builder.EmitZeros(_type.NonGCStaticFieldSize.AsInt); + } + builder.AddSymbol(this); return builder.ToObjectData(); } + + protected internal override int ClassCode => -1173104872; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return comparer.Compare(_type, ((NonGCStaticsNode)other)._type); + } + + int ISortableSymbolNode.ClassCode => ClassCode; + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + return CompareToImpl((ObjectNode)other, comparer); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectDataBuilder.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectDataBuilder.cs index b0ad1b6e27..034d3afe23 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectDataBuilder.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectDataBuilder.cs @@ -90,6 +90,14 @@ namespace ILCompiler.DependencyAnalysis EmitByte((byte)((emit >> 24) & 0xFF)); } + public void EmitUInt(uint emit) + { + EmitByte((byte)(emit & 0xFF)); + EmitByte((byte)((emit >> 8) & 0xFF)); + EmitByte((byte)((emit >> 16) & 0xFF)); + EmitByte((byte)((emit >> 24) & 0xFF)); + } + public void EmitLong(long emit) { EmitByte((byte)(emit & 0xFF)); @@ -164,6 +172,16 @@ namespace ILCompiler.DependencyAnalysis _data.Append(bytes); } + public void EmitBytes(byte[] bytes, int offset, int length) + { + _data.Append(bytes, offset, length); + } + + internal void EmitBytes(ArrayBuilder bytes) + { + _data.Append(bytes); + } + public void EmitZeroPointer() { _data.ZeroExtend(_target.PointerSize); @@ -258,6 +276,7 @@ namespace ILCompiler.DependencyAnalysis EmitLong(delta); break; case RelocType.IMAGE_REL_BASED_THUMB_BRANCH24: + case RelocType.IMAGE_REL_BASED_ARM64_BRANCH26: case RelocType.IMAGE_REL_BASED_THUMB_MOV32: // Do not vacate space for this kind of relocation, because // the space is embedded in the instruction. diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectNode.cs index 09610d9d38..6dcf96ae89 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectNode.cs @@ -4,11 +4,13 @@ using System; using System.Collections.Generic; + using ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis { - public abstract class ObjectNode : DependencyNodeCore + public abstract partial class ObjectNode : SortableDependencyNode { public class ObjectData { diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs index afeb432e24..dde97273e1 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs @@ -304,7 +304,17 @@ namespace ILCompiler.DependencyAnalysis public void EmitDebugVar(DebugVarInfo debugVar) { int rangeCount = debugVar.Ranges.Count; - uint typeIndex = _userDefinedTypeDescriptor.GetVariableTypeIndex(debugVar.Type, true); + uint typeIndex; + + try + { + typeIndex = _userDefinedTypeDescriptor.GetVariableTypeIndex(debugVar.Type); + } + catch (TypeSystemException) + { + typeIndex = 0; // T_NOTYPE + } + EmitDebugVar(_nativeObjectWriter, debugVar.Name, typeIndex, debugVar.IsParam, rangeCount, debugVar.Ranges.ToArray()); } @@ -422,11 +432,15 @@ namespace ILCompiler.DependencyAnalysis FrameInfo[] frameInfos = nodeWithCodeInfo.FrameInfos; if (frameInfos == null) { + // Data should only be present if the method has unwind info + Debug.Assert(nodeWithCodeInfo.GetAssociatedDataNode(_nodeFactory) == null); + return; } byte[] gcInfo = nodeWithCodeInfo.GCInfo; ObjectData ehInfo = nodeWithCodeInfo.EHInfo; + ISymbolNode associatedDataNode = nodeWithCodeInfo.GetAssociatedDataNode(_nodeFactory); for (int i = 0; i < frameInfos.Length; i++) { @@ -448,15 +462,19 @@ namespace ILCompiler.DependencyAnalysis EmitSymbolDef(blobSymbolName); FrameInfoFlags flags = frameInfo.Flags; - if (ehInfo != null) - { - flags |= FrameInfoFlags.HasEHInfo; - } + flags |= ehInfo != null ? FrameInfoFlags.HasEHInfo : 0; + flags |= associatedDataNode != null ? FrameInfoFlags.HasAssociatedData : 0; EmitBlob(blob); EmitIntValue((byte)flags, 1); + if (associatedDataNode != null) + { + EmitSymbolRef(_sb.Clear().Append(associatedDataNode.GetMangledName(_nodeFactory.NameMangler)), RelocType.IMAGE_REL_BASED_ABSOLUTE); + associatedDataNode = null; + } + if (ehInfo != null) { EmitSymbolRef(_sb.Clear().Append(_nodeFactory.NameMangler.CompilationUnitPrefix).Append("_ehInfo").Append(_currentNodeZeroTerminatedName), RelocType.IMAGE_REL_BASED_ABSOLUTE); @@ -502,11 +520,15 @@ namespace ILCompiler.DependencyAnalysis FrameInfo[] frameInfos = nodeWithCodeInfo.FrameInfos; if (frameInfos == null) { + // Data should only be present if the method has unwind info + Debug.Assert(nodeWithCodeInfo.GetAssociatedDataNode(_nodeFactory) == null); + return; } byte[] gcInfo = nodeWithCodeInfo.GCInfo; ObjectData ehInfo = nodeWithCodeInfo.EHInfo; + ISymbolNode associatedDataNode = nodeWithCodeInfo.GetAssociatedDataNode(_nodeFactory); for (int i = 0; i < frameInfos.Length; i++) { @@ -529,10 +551,9 @@ namespace ILCompiler.DependencyAnalysis EmitSymbolDef(blobSymbolName); FrameInfoFlags flags = frameInfo.Flags; - if (ehInfo != null) - { - flags |= FrameInfoFlags.HasEHInfo; - } + flags |= ehInfo != null ? FrameInfoFlags.HasEHInfo : 0; + flags |= associatedDataNode != null ? FrameInfoFlags.HasAssociatedData : 0; + EmitIntValue((byte)flags, 1); if (i != 0) @@ -543,6 +564,14 @@ namespace ILCompiler.DependencyAnalysis EmitIntValue((ulong)(start - frameInfos[0].StartOffset), 4); } + if (associatedDataNode != null) + { + _sb.Clear(); + AppendExternCPrefix(_sb); + EmitSymbolRef(_sb.Append(associatedDataNode.GetMangledName(_nodeFactory.NameMangler)), RelocType.IMAGE_REL_BASED_RELPTR32); + associatedDataNode = null; + } + if (ehInfo != null) { EmitSymbolRef(_sb.Clear().Append("_ehInfo").Append(_currentNodeZeroTerminatedName), RelocType.IMAGE_REL_BASED_RELPTR32); @@ -769,7 +798,7 @@ namespace ILCompiler.DependencyAnalysis } _nodeFactory = factory; _targetPlatform = _nodeFactory.Target; - _userDefinedTypeDescriptor = new UserDefinedTypeDescriptor(this); + _userDefinedTypeDescriptor = new UserDefinedTypeDescriptor(this, factory); } public void Dispose() @@ -899,7 +928,7 @@ namespace ILCompiler.DependencyAnalysis catch (ArgumentException) { ISymbolNode alreadyWrittenSymbol = _previouslyWrittenNodeNames[definedSymbol.GetMangledName(factory.NameMangler)]; - Debug.Assert(false, "Duplicate node name emitted to file", + Debug.Fail("Duplicate node name emitted to file", $"Symbol {definedSymbol.GetMangledName(factory.NameMangler)} has already been written to the output object file {objectFilePath} with symbol {alreadyWrittenSymbol}"); } } @@ -921,7 +950,10 @@ namespace ILCompiler.DependencyAnalysis // Build symbol definition map. objectWriter.BuildSymbolDefinitionMap(node, nodeContents.DefinedSymbols); - if (!factory.Target.IsWindows) + // The DWARF CFI unwind is implemented for AMD64 & ARM32 only. + TargetArchitecture tarch = factory.Target.Architecture; + if (!factory.Target.IsWindows && + (tarch == TargetArchitecture.X64 || tarch == TargetArchitecture.ARMEL || tarch == TargetArchitecture.ARM)) objectWriter.BuildCFIMap(factory, node); // Build debug location map @@ -967,6 +999,19 @@ namespace ILCompiler.DependencyAnalysis } int size = objectWriter.EmitSymbolReference(reloc.Target, (int)delta, reloc.RelocType); + // Emit a copy of original Thumb2 instruction that came from RyuJIT + if (reloc.RelocType == RelocType.IMAGE_REL_BASED_THUMB_MOV32 || + reloc.RelocType == RelocType.IMAGE_REL_BASED_THUMB_BRANCH24) + { + unsafe + { + fixed (void* location = &nodeContents.Data[i]) + { + objectWriter.EmitBytes((IntPtr)location, size); + } + } + } + // Update nextRelocIndex/Offset if (++nextRelocIndex < relocs.Length) { @@ -1049,5 +1094,29 @@ namespace ILCompiler.DependencyAnalysis } } } + + [DllImport(NativeObjectWriterFileName)] + private static extern uint GetPointerTypeIndex(IntPtr objWriter, PointerTypeDescriptor pointerDescriptor); + + uint ITypesDebugInfoWriter.GetPointerTypeIndex(PointerTypeDescriptor pointerDescriptor) + { + return GetPointerTypeIndex(_nativeObjectWriter, pointerDescriptor); + } + + [DllImport(NativeObjectWriterFileName)] + private static extern uint GetMemberFunctionTypeIndex(IntPtr objWriter, MemberFunctionTypeDescriptor memberDescriptor, uint[] argumentTypes); + + uint ITypesDebugInfoWriter.GetMemberFunctionTypeIndex(MemberFunctionTypeDescriptor memberDescriptor, uint[] argumentTypes) + { + return GetMemberFunctionTypeIndex(_nativeObjectWriter, memberDescriptor, argumentTypes); + } + + [DllImport(NativeObjectWriterFileName)] + private static extern uint GetMemberFunctionIdTypeIndex(IntPtr objWriter, MemberFunctionIdTypeDescriptor memberIdDescriptor); + + uint ITypesDebugInfoWriter.GetMemberFunctionId(MemberFunctionIdTypeDescriptor memberIdDescriptor) + { + return GetMemberFunctionIdTypeIndex(_nativeObjectWriter, memberIdDescriptor); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/PInvokeMethodFixupNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/PInvokeMethodFixupNode.cs index ef1dc0aa87..797032e3eb 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/PInvokeMethodFixupNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/PInvokeMethodFixupNode.cs @@ -5,6 +5,7 @@ using System; using Internal.Text; +using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis { @@ -73,5 +74,16 @@ namespace ILCompiler.DependencyAnalysis return builder.ToObjectData(); } + + protected internal override int ClassCode => -1592006940; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + var compare = string.Compare(_moduleName, ((PInvokeMethodFixupNode)other)._moduleName); + if (compare != 0) + return compare; + + return string.Compare(_entryPointName, ((PInvokeMethodFixupNode)other)._entryPointName); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/PInvokeModuleFixupNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/PInvokeModuleFixupNode.cs index 939e2b037b..bf83f13b1b 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/PInvokeModuleFixupNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/PInvokeModuleFixupNode.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using Internal.Text; +using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis { @@ -48,5 +49,12 @@ namespace ILCompiler.DependencyAnalysis return builder.ToObjectData(); } + + protected internal override int ClassCode => 159930099; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return string.Compare(_moduleName, ((PInvokeModuleFixupNode)other)._moduleName); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs index a3a3fb44dc..090fdf7bb6 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs @@ -29,8 +29,11 @@ namespace ILCompiler.DependencyAnalysis _lookupSignature = GetLookupSignature(factory, helperId, target); } - private static GenericLookupResult GetLookupSignature(NodeFactory factory, ReadyToRunHelperId id, object target) + public static GenericLookupResult GetLookupSignature(NodeFactory factory, ReadyToRunHelperId id, object target) { + // Necessary type handle is not something you can put in a dictionary - someone should have normalized to TypeHandle + Debug.Assert(id != ReadyToRunHelperId.NecessaryTypeHandle); + switch (id) { case ReadyToRunHelperId.TypeHandle: @@ -47,14 +50,14 @@ namespace ILCompiler.DependencyAnalysis return factory.GenericLookup.TypeThreadStaticBaseIndex((TypeDesc)target); case ReadyToRunHelperId.MethodDictionary: return factory.GenericLookup.MethodDictionary((MethodDesc)target); - case ReadyToRunHelperId.VirtualCall: - return factory.GenericLookup.VirtualCall((MethodDesc)target); - case ReadyToRunHelperId.ResolveVirtualFunction: - return factory.GenericLookup.VirtualMethodAddress((MethodDesc)target); + case ReadyToRunHelperId.VirtualDispatchCell: + return factory.GenericLookup.VirtualDispatchCell((MethodDesc)target); case ReadyToRunHelperId.MethodEntry: return factory.GenericLookup.MethodEntry((MethodDesc)target); case ReadyToRunHelperId.DelegateCtor: return ((DelegateCreationInfo)target).GetLookupKind(factory); + case ReadyToRunHelperId.DefaultConstructor: + return factory.GenericLookup.DefaultCtorLookupResult((TypeDesc)target); default: throw new NotImplementedException(); } @@ -65,22 +68,29 @@ namespace ILCompiler.DependencyAnalysis protected sealed override void OnMarked(NodeFactory factory) { - // When the helper call gets marked, ensure the generic layout for the associated dictionaries - // includes the signature. - factory.GenericDictionaryLayout(_dictionaryOwner).EnsureEntry(_lookupSignature); + DictionaryLayoutNode layout = factory.GenericDictionaryLayout(_dictionaryOwner); - if ((_id == ReadyToRunHelperId.GetGCStaticBase || _id == ReadyToRunHelperId.GetThreadStaticBase) && - factory.TypeSystemContext.HasLazyStaticConstructor((TypeDesc)_target)) + if (layout.HasUnfixedSlots) { - // If the type has a lazy static constructor, we also need the non-GC static base - // because that's where the class constructor context is. - factory.GenericDictionaryLayout(_dictionaryOwner).EnsureEntry(factory.GenericLookup.TypeNonGCStaticBase((TypeDesc)_target)); + // When the helper call gets marked, ensure the generic layout for the associated dictionaries + // includes the signature. + layout.EnsureEntry(_lookupSignature); + + if ((_id == ReadyToRunHelperId.GetGCStaticBase || _id == ReadyToRunHelperId.GetThreadStaticBase) && + factory.TypeSystemContext.HasLazyStaticConstructor((TypeDesc)_target)) + { + // If the type has a lazy static constructor, we also need the non-GC static base + // because that's where the class constructor context is. + layout.EnsureEntry(factory.GenericLookup.TypeNonGCStaticBase((TypeDesc)_target)); + } } } public IEnumerable InstantiateDependencies(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation) { - ArrayBuilder result = new ArrayBuilder(); + DependencyList result = new DependencyList(); + + var lookupContext = new GenericLookupResultContext(_dictionaryOwner, typeInstantiation, methodInstantiation); switch (_id) { @@ -93,13 +103,9 @@ namespace ILCompiler.DependencyAnalysis if (factory.TypeSystemContext.HasLazyStaticConstructor(type)) { - // TODO: Make _dictionaryOwner a RuntimeDetermined type/method and make a substitution with - // typeInstantiation/methodInstantiation to get a concrete type. Then pass the generic dictionary - // node of the concrete type to the GetTarget call. Also change the signature of GetTarget to - // take only the factory and dictionary as input. result.Add( new DependencyListEntry( - factory.GenericLookup.TypeNonGCStaticBase(type).GetTarget(factory, typeInstantiation, methodInstantiation, null), + factory.GenericLookup.TypeNonGCStaticBase(type).GetTarget(factory, lookupContext), "Dictionary dependency")); } } @@ -111,7 +117,7 @@ namespace ILCompiler.DependencyAnalysis if (createInfo.NeedsVirtualMethodUseTracking) { MethodDesc instantiatedTargetMethod = createInfo.TargetMethod.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(typeInstantiation, methodInstantiation); - if (!factory.CompilationModuleGroup.ShouldProduceFullVTable(instantiatedTargetMethod.OwningType)) + if (!factory.VTable(instantiatedTargetMethod.OwningType).HasFixedSlots) { result.Add( new DependencyListEntry( @@ -119,30 +125,7 @@ namespace ILCompiler.DependencyAnalysis "Dictionary dependency")); } - // TODO: https://github.com/dotnet/corert/issues/3224 - if (instantiatedTargetMethod.IsAbstract) - { - result.Add(new DependencyListEntry(factory.ReflectableMethod(instantiatedTargetMethod), "Abstract reflectable method")); - } - } - } - break; - - case ReadyToRunHelperId.ResolveVirtualFunction: - { - MethodDesc instantiatedTarget = ((MethodDesc)_target).GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(typeInstantiation, methodInstantiation); - if (!factory.CompilationModuleGroup.ShouldProduceFullVTable(instantiatedTarget.OwningType)) - { - result.Add( - new DependencyListEntry( - factory.VirtualMethodUse(instantiatedTarget), - "Dictionary dependency")); - } - - // TODO: https://github.com/dotnet/corert/issues/3224 - if (instantiatedTarget.IsAbstract) - { - result.Add(new DependencyListEntry(factory.ReflectableMethod(instantiatedTarget), "Abstract reflectable method")); + factory.MetadataManager.GetDependenciesDueToVirtualMethodReflectability(ref result, factory, instantiatedTargetMethod); } } break; @@ -150,7 +133,7 @@ namespace ILCompiler.DependencyAnalysis // All generic lookups depend on the thing they point to result.Add(new DependencyListEntry( - _lookupSignature.GetTarget(factory, typeInstantiation, methodInstantiation, null), + _lookupSignature.GetTarget(factory, lookupContext), "Dictionary dependency")); return result.ToArray(); @@ -171,22 +154,14 @@ namespace ILCompiler.DependencyAnalysis protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { DependencyList dependencies = new DependencyList(); + + dependencies.Add(factory.GenericDictionaryLayout(_dictionaryOwner), "Layout"); + foreach (DependencyNodeCore dependency in _lookupSignature.NonRelocDependenciesFromUsage(factory)) { dependencies.Add(new DependencyListEntry(dependency, "GenericLookupResultDependency")); } - // Root the template for the type while we're hitting its dictionary cells. In the future, we may - // want to control this via type reflectability instead. - if (_dictionaryOwner is MethodDesc) - { - dependencies.Add(factory.NativeLayout.TemplateMethodLayout((MethodDesc)_dictionaryOwner), "Type loader template"); - } - else - { - dependencies.Add(factory.NativeLayout.TemplateTypeLayout((TypeDesc)_dictionaryOwner), "Type loader template"); - } - return dependencies; } @@ -227,6 +202,52 @@ namespace ILCompiler.DependencyAnalysis return conditionalDependencies; } + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + var compare = _id.CompareTo(((ReadyToRunGenericHelperNode)other)._id); + if (compare != 0) + return compare; + + if (_dictionaryOwner is MethodDesc) + { + if (((ReadyToRunGenericHelperNode)other)._dictionaryOwner is TypeDesc) + return -1; + + compare = comparer.Compare((MethodDesc)_dictionaryOwner, (MethodDesc)((ReadyToRunGenericHelperNode)other)._dictionaryOwner); + } + else + { + if (((ReadyToRunGenericHelperNode)other)._dictionaryOwner is MethodDesc) + return 1; + + compare = comparer.Compare((TypeDesc)_dictionaryOwner, (TypeDesc)((ReadyToRunGenericHelperNode)other)._dictionaryOwner); + } + + if (compare != 0) + return compare; + + switch (_id) + { + case ReadyToRunHelperId.TypeHandle: + case ReadyToRunHelperId.GetGCStaticBase: + case ReadyToRunHelperId.GetNonGCStaticBase: + case ReadyToRunHelperId.GetThreadStaticBase: + case ReadyToRunHelperId.DefaultConstructor: + return comparer.Compare((TypeDesc)_target, (TypeDesc)((ReadyToRunGenericHelperNode)other)._target); + case ReadyToRunHelperId.MethodHandle: + case ReadyToRunHelperId.MethodDictionary: + case ReadyToRunHelperId.VirtualDispatchCell: + case ReadyToRunHelperId.MethodEntry: + return comparer.Compare((MethodDesc)_target, (MethodDesc)((ReadyToRunGenericHelperNode)other)._target); + case ReadyToRunHelperId.FieldHandle: + return comparer.Compare((FieldDesc)_target, (FieldDesc)((ReadyToRunGenericHelperNode)other)._target); + case ReadyToRunHelperId.DelegateCtor: + return ((DelegateCreationInfo)_target).CompareTo((DelegateCreationInfo)((ReadyToRunGenericHelperNode)other)._target, comparer); + default: + throw new NotImplementedException(); + } + } } public partial class ReadyToRunGenericLookupFromDictionaryNode : ReadyToRunGenericHelperNode @@ -247,6 +268,8 @@ namespace ILCompiler.DependencyAnalysis sb.Append("__GenericLookupFromDict_").Append(mangledContextName).Append("_"); AppendLookupSignatureMangledName(nameMangler, sb); } + + protected internal override int ClassCode => 1055354299; } public partial class ReadyToRunGenericLookupFromTypeNode : ReadyToRunGenericHelperNode @@ -267,5 +290,7 @@ namespace ILCompiler.DependencyAnalysis sb.Append("__GenericLookupFromType_").Append(mangledContextName).Append("_"); AppendLookupSignatureMangledName(nameMangler, sb); } + + protected internal override int ClassCode => 913214059; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHeaderNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHeaderNode.cs index f54e71e700..6c2fd6d735 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHeaderNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHeaderNode.cs @@ -2,6 +2,7 @@ // 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.Collections.Generic; using Internal.Runtime; @@ -126,5 +127,7 @@ namespace ILCompiler.DependencyAnalysis return builder.ToObjectData(); } + + protected internal override int ClassCode => -534800244; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs index c4dc1fac9e..7b941ec850 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs @@ -27,13 +27,16 @@ namespace ILCompiler.DependencyAnalysis // The following helpers are used for generic lookups only TypeHandle, + NecessaryTypeHandle, MethodHandle, FieldHandle, MethodDictionary, - MethodEntry + MethodEntry, + VirtualDispatchCell, + DefaultConstructor, } - public partial class ReadyToRunHelperNode : AssemblyStubNode + public partial class ReadyToRunHelperNode : AssemblyStubNode, INodeWithDebugInfo { private ReadyToRunHelperId _id; private Object _target; @@ -81,7 +84,7 @@ namespace ILCompiler.DependencyAnalysis // Make sure we aren't trying to callvirt Object.Finalize MethodDesc method = (MethodDesc)target; if (method.IsFinalizer) - throw new TypeSystemException.InvalidProgramException(ExceptionStringID.InvalidProgramCallVirtFinalize, method); + ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramCallVirtFinalize, method); } break; } @@ -143,13 +146,9 @@ namespace ILCompiler.DependencyAnalysis DependencyList dependencyList = new DependencyList(); #if !SUPPORT_JIT - // TODO: https://github.com/dotnet/corert/issues/3224 - if (targetMethod.IsAbstract) - { - dependencyList.Add(factory.ReflectableMethod(targetMethod), "Abstract reflectable method"); - } + factory.MetadataManager.GetDependenciesDueToVirtualMethodReflectability(ref dependencyList, factory, targetMethod); - if (!factory.CompilationModuleGroup.ShouldProduceFullVTable(targetMethod.OwningType)) + if (!factory.VTable(targetMethod.OwningType).HasFixedSlots) { dependencyList.Add(factory.VirtualMethodUse((MethodDesc)_target), "ReadyToRun Virtual Method Call"); @@ -167,13 +166,9 @@ namespace ILCompiler.DependencyAnalysis DependencyList dependencyList = new DependencyList(); #if !SUPPORT_JIT - // TODO: https://github.com/dotnet/corert/issues/3224 - if (targetMethod.IsAbstract) - { - dependencyList.Add(factory.ReflectableMethod(targetMethod), "Abstract reflectable method"); - } + factory.MetadataManager.GetDependenciesDueToVirtualMethodReflectability(ref dependencyList, factory, targetMethod); - if (!factory.CompilationModuleGroup.ShouldProduceFullVTable(info.TargetMethod.OwningType)) + if (!factory.VTable(info.TargetMethod.OwningType).HasFixedSlots) { dependencyList.Add(factory.VirtualMethodUse(info.TargetMethod), "ReadyToRun Delegate to virtual method"); } @@ -185,5 +180,75 @@ namespace ILCompiler.DependencyAnalysis return null; } + + DebugLocInfo[] INodeWithDebugInfo.DebugLocInfos + { + get + { + if (_id == ReadyToRunHelperId.VirtualCall) + { + // Generate debug information that lets debuggers step into the virtual calls. + // We generate a step into sequence point at the point where the helper jumps to + // the target of the virtual call. + TargetDetails target = ((MethodDesc)_target).Context.Target; + int debuggerStepInOffset = -1; + switch (target.Architecture) + { + case TargetArchitecture.X64: + debuggerStepInOffset = 3; + break; + } + if (debuggerStepInOffset != -1) + { + return new DebugLocInfo[] + { + new DebugLocInfo(0, String.Empty, WellKnownLineNumber.DebuggerStepThrough), + new DebugLocInfo(debuggerStepInOffset, String.Empty, WellKnownLineNumber.DebuggerStepIn) + }; + } + } + + return Array.Empty(); + } + } + + DebugVarInfo[] INodeWithDebugInfo.DebugVarInfos + { + get + { + return Array.Empty(); + } + } + +#if !SUPPORT_JIT + protected internal override int ClassCode => -911637948; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + var compare = _id.CompareTo(((ReadyToRunHelperNode)other)._id); + if (compare != 0) + return compare; + + switch (_id) + { + case ReadyToRunHelperId.NewHelper: + case ReadyToRunHelperId.NewArr1: + case ReadyToRunHelperId.IsInstanceOf: + case ReadyToRunHelperId.CastClass: + case ReadyToRunHelperId.GetNonGCStaticBase: + case ReadyToRunHelperId.GetGCStaticBase: + case ReadyToRunHelperId.GetThreadStaticBase: + return comparer.Compare((TypeDesc)_target, (TypeDesc)((ReadyToRunHelperNode)other)._target); + case ReadyToRunHelperId.VirtualCall: + case ReadyToRunHelperId.ResolveVirtualFunction: + return comparer.Compare((MethodDesc)_target, (MethodDesc)((ReadyToRunHelperNode)other)._target); + case ReadyToRunHelperId.DelegateCtor: + return ((DelegateCreationInfo)_target).CompareTo((DelegateCreationInfo)((ReadyToRunHelperNode)other)._target, comparer); + default: + throw new NotImplementedException(); + } + + } +#endif } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectableMethodNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectableMethodNode.cs index ff4bd731d6..2664308a82 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectableMethodNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectableMethodNode.cs @@ -15,7 +15,7 @@ namespace ILCompiler.DependencyAnalysis /// /// Represents a method that doesn't have a body, but we need to track it because it's reflectable. /// - internal class ReflectableMethodNode : DependencyNodeCore + public class ReflectableMethodNode : DependencyNodeCore { private MethodDesc _method; diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionFieldMapNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionFieldMapNode.cs index c063aecaa7..ddae41d987 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionFieldMapNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionFieldMapNode.cs @@ -39,37 +39,12 @@ namespace ILCompiler.DependencyAnalysis public override ObjectNodeSection Section => _externalReferences.Section; + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection; + public override bool StaticDependenciesAreComputed => true; protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); - /// - /// Helper method to compute the dependencies that would be needed for reflection field lookup. - /// - public static void AddReflectionFieldMapEntryDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) - { - // TODO: https://github.com/dotnet/corert/issues/3224 - // Reflection static field bases handling is here because in the current reflection model we reflection-enable - // all fields of types that are compiled. Ideally the list of reflection enabled fields should be known before - // we even start the compilation process (with the static bases being compilation roots like any other). - if (type is MetadataType && !type.HasInstantiation && !type.IsCanonicalSubtype(CanonicalFormKind.Any)) - { - MetadataType metadataType = (MetadataType)type; - - if (metadataType.GCStaticFieldSize.AsInt > 0) - { - dependencies.Add(factory.TypeGCStaticsSymbol(metadataType), "GC statics for ReflectionFieldMap entry"); - } - - if (metadataType.NonGCStaticFieldSize.AsInt > 0) - { - dependencies.Add(factory.TypeNonGCStaticsSymbol(metadataType), "Non-GC statics for ReflectionFieldMap entry"); - } - - // TODO: TLS dependencies - } - } - public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { // This node does not trigger generation of other nodes. @@ -92,7 +67,7 @@ namespace ILCompiler.DependencyAnalysis FieldTableFlags flags; if (field.IsThreadStatic) { - flags = FieldTableFlags.ThreadStatic; + flags = FieldTableFlags.ThreadStatic | FieldTableFlags.FieldOffsetEncodedDirectly; } else if (field.IsStatic) { @@ -112,6 +87,9 @@ namespace ILCompiler.DependencyAnalysis if (fieldMapping.MetadataHandle != 0) flags |= FieldTableFlags.HasMetadataHandle; + if (field.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) + flags |= FieldTableFlags.IsAnyCanonicalEntry; + if (field.OwningType.IsCanonicalSubtype(CanonicalFormKind.Universal)) flags |= FieldTableFlags.IsUniversalCanonicalEntry; @@ -139,16 +117,28 @@ namespace ILCompiler.DependencyAnalysis if ((flags & FieldTableFlags.IsUniversalCanonicalEntry) != 0) { - // TODO: USG - continue; + vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant(checked((uint)field.GetFieldOrdinal()))); } else { switch (flags & FieldTableFlags.StorageClass) { case FieldTableFlags.ThreadStatic: - // TODO: thread statics - continue; + if (factory.Target.Abi == TargetAbi.ProjectN) + { + if (!field.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) + { + ISymbolNode tlsOffsetForType = ((UtcNodeFactory)factory).TypeThreadStaticsOffsetSymbol((MetadataType)field.OwningType); + vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant(_externalReferences.GetIndex(tlsOffsetForType))); + } + vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant((uint)(field.Offset.AsInt))); + } + else + { + // TODO: CoreRT + continue; + } + break; case FieldTableFlags.Static: { @@ -197,5 +187,8 @@ namespace ILCompiler.DependencyAnalysis return new ObjectData(hashTableBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.ReflectionFieldMapNode; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionInvokeMapNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionInvokeMapNode.cs index bb58818a04..b41717a869 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionInvokeMapNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionInvokeMapNode.cs @@ -44,10 +44,18 @@ namespace ILCompiler.DependencyAnalysis public override ObjectNodeSection Section => _externalReferences.Section; + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection; + public override bool StaticDependenciesAreComputed => true; protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + private bool MethodRequiresInstArg(MethodDesc method, bool isUnboxingStub) + { + // Similar to RequiresInstArg, but will do the right thing for instantiating unboxing stubs (canonical non-generic instance methods on valuetypes) + return method.IsSharedByGenericInstantiations && (method.HasInstantiation || method.Signature.IsStatic || (method.ImplementationType.IsValueType && !isUnboxingStub)); + } + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { // This node does not trigger generation of other nodes. @@ -68,20 +76,17 @@ namespace ILCompiler.DependencyAnalysis { MethodDesc method = mappingEntry.Entity; - // The current format requires us to have an EEType for the owning type. We might want to lift this. - if (!factory.MetadataManager.TypeGeneratesEEType(method.OwningType)) + if (!factory.MetadataManager.ShouldMethodBeInInvokeMap(method)) continue; - // We have a method body, we have a metadata token, but we can't get an invoke stub. Bail. - if (!factory.MetadataManager.IsReflectionInvokable(method)) - continue; + bool useUnboxingStub = method.OwningType.IsValueType && !method.Signature.IsStatic; InvokeTableFlags flags = 0; if (method.HasInstantiation) flags |= InvokeTableFlags.IsGenericMethod; - if (method.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstArg()) + if (MethodRequiresInstArg(method.GetCanonMethodTarget(CanonicalFormKind.Specific), useUnboxingStub)) flags |= InvokeTableFlags.RequiresInstArg; if (method.IsDefaultConstructor) @@ -130,7 +135,6 @@ namespace ILCompiler.DependencyAnalysis if ((flags & InvokeTableFlags.HasEntrypoint) != 0) { - bool useUnboxingStub = method.OwningType.IsValueType && !method.Signature.IsStatic; vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant(_externalReferences.GetIndex( factory.MethodEntrypoint(method.GetCanonMethodTarget(CanonicalFormKind.Specific), useUnboxingStub)))); @@ -185,5 +189,8 @@ namespace ILCompiler.DependencyAnalysis return new ObjectData(hashTableBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.ReflectionInvokeMapNode; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionVirtualInvokeMapNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionVirtualInvokeMapNode.cs index 664a0148b1..a6b2cf4b93 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionVirtualInvokeMapNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionVirtualInvokeMapNode.cs @@ -39,6 +39,7 @@ namespace ILCompiler.DependencyAnalysis public override bool IsShareable => false; public override ObjectNodeSection Section => _externalReferences.Section; public override bool StaticDependenciesAreComputed => true; + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection; protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); public static bool NeedsVirtualInvokeInfo(MethodDesc method) @@ -88,6 +89,9 @@ namespace ILCompiler.DependencyAnalysis public static void GetVirtualInvokeMapDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) { + if (!factory.MetadataManager.SupportsReflection) + return; + if (NeedsVirtualInvokeInfo(method)) { dependencies = dependencies ?? new DependencyList(); @@ -108,6 +112,15 @@ namespace ILCompiler.DependencyAnalysis NativeLayoutMethodNameAndSignatureVertexNode nameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(method.GetTypicalMethodDefinition()); NativeLayoutPlacedSignatureVertexNode placedNameAndSig = factory.NativeLayout.PlacedSignatureVertex(nameAndSig); dependencies.Add(placedNameAndSig, "Reflection virtual invoke method signature"); + + if (!method.HasInstantiation) + { + MethodDesc slotDefiningMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method).Normalize(); + if (!factory.VTable(slotDefiningMethod.OwningType).HasFixedSlots) + { + dependencies.Add(factory.VirtualMethodUse(slotDefiningMethod), "Reflection virtual invoke method"); + } + } } } @@ -207,13 +220,7 @@ namespace ILCompiler.DependencyAnalysis { // Get the declaring method for slot on the instantiated declaring type int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, declaringMethodForSlot, factory.Target.Abi != TargetAbi.ProjectN); - - if (slot == -1) - { - // This method doesn't have a slot. (At this time, this is only done for the Object.Finalize method) - Debug.Assert(declaringMethodForSlot.Name == "Finalize"); - continue; - } + Debug.Assert(slot != -1); vertex = writer.GetTuple( writer.GetUnsignedConstant(_externalReferences.GetIndex(containingTypeKeyNode)), @@ -234,5 +241,8 @@ namespace ILCompiler.DependencyAnalysis return new ObjectData(hashTableBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.ReflectionVirtualInvokeMapNode; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Relocation.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Relocation.cs index e0ee402c2e..941ba5e0a8 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Relocation.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Relocation.cs @@ -15,11 +15,16 @@ namespace ILCompiler.DependencyAnalysis IMAGE_REL_BASED_DIR64 = 0x0A, // 64 bit address base IMAGE_REL_BASED_REL32 = 0x10, // 32-bit relative address from byte following reloc IMAGE_REL_BASED_THUMB_BRANCH24 = 0x13, // Thumb2: based B, BL + IMAGE_REL_BASED_ARM64_BRANCH26 = 0x14, // Arm64: B, BL IMAGE_REL_BASED_RELPTR32 = 0x7C, // 32-bit relative address from byte starting reloc // This is a special NGEN-specific relocation type // for relative pointer (used to make NGen relocation // section smaller) IMAGE_REL_SECREL = 0x80, // 32 bit offset from base of section containing target + + IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 = 0x81, // ADRP + IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A = 0x82, // ADD/ADDS (immediate) with zero shift, for page offset + IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L = 0x83, // LDR (indexed, unsigned immediate), for page offset } public struct Relocation diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceDataNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceDataNode.cs index c73ef63be2..9ff7a7243f 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceDataNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceDataNode.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Internal.Text; -using Internal.TypeSystem.Ecma; using System; using System.Collections.Generic; using System.Diagnostics; @@ -11,6 +9,10 @@ using System.Linq; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; +using Internal.Text; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + namespace ILCompiler.DependencyAnalysis { /// @@ -40,6 +42,8 @@ namespace ILCompiler.DependencyAnalysis public override bool StaticDependenciesAreComputed => true; + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection; + public int Offset => 0; public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) @@ -139,6 +143,9 @@ namespace ILCompiler.DependencyAnalysis _endSymbol.SetSymbolOffset(resourceBlob.Length); return resourceBlob; } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.ResourceDataNode; } /// diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceIndexNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceIndexNode.cs index 37853ae1f2..053a3112b6 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceIndexNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceIndexNode.cs @@ -6,6 +6,7 @@ using System; using Internal.NativeFormat; using Internal.Text; +using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis { @@ -32,6 +33,8 @@ namespace ILCompiler.DependencyAnalysis public override bool StaticDependenciesAreComputed => true; + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection; + public int Offset => 0; public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) @@ -94,5 +97,8 @@ namespace ILCompiler.DependencyAnalysis _endSymbol.SetSymbolOffset(blob.Length); return blob; } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.ResourceIndexNode; } } \ No newline at end of file diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeDecodableJumpStub.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeDecodableJumpStub.cs new file mode 100644 index 0000000000..1d4f00f6b3 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeDecodableJumpStub.cs @@ -0,0 +1,65 @@ +// 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 Internal.Text; +using Internal.TypeSystem; + +using ILCompiler.DependencyAnalysisFramework; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents an unboxing stub that supports calling instance methods on boxed valuetypes. + /// + public partial class RuntimeDecodableJumpStubNode : JumpStubNode, IMethodNode + { + private IMethodNode WrappedMethodIndirectionCellNode => (IMethodNode)Target; + + public MethodDesc Method => WrappedMethodIndirectionCellNode.Method; + + public override ObjectNodeSection Section + { + get + { + // Use the unboxing stub node section. This allows the logic in RhGetCodeTarget to identify that it should be able to decode this stub + // TODO rename these sections to make it obvious these are jump stubs as well as unboxings stubs + TargetDetails targetDetails = WrappedMethodIndirectionCellNode.Method.Context.Target; + string sectionName = targetDetails.IsWindows ? UnboxingStubNode.WindowsSectionName : UnboxingStubNode.UnixSectionName; + return new ObjectNodeSection(sectionName, SectionType.Executable); + } + } + public override bool IsShareable => true; + + public RuntimeDecodableJumpStubNode(IMethodNode target) : base(target) + { + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + string name = WrappedMethodIndirectionCellNode.GetMangledName(nameMangler); + Debug.Assert(name.StartsWith("__mrt_")); + sb.Append(name.Substring(6)); + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + protected internal override int ClassCode => 532434339; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return comparer.Compare(WrappedMethodIndirectionCellNode, ((RuntimeDecodableJumpStubNode)other).WrappedMethodIndirectionCellNode); + } + + int ISortableSymbolNode.ClassCode => ClassCode; + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + return CompareToImpl((ObjectNode)other, comparer); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeDeterminedMethodNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeDeterminedMethodNode.cs index 83bd6b0b76..d3a3672eaf 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeDeterminedMethodNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeDeterminedMethodNode.cs @@ -61,5 +61,12 @@ namespace ILCompiler.DependencyAnalysis public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + + int ISortableSymbolNode.ClassCode => 258139501; + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + return comparer.Compare(_canonicalMethodNode, ((RuntimeDeterminedMethodNode)other)._canonicalMethodNode); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeFieldHandleNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeFieldHandleNode.cs index 7d47a0afaf..57a4018fd8 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeFieldHandleNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeFieldHandleNode.cs @@ -37,18 +37,9 @@ namespace ILCompiler.DependencyAnalysis protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { - // TODO: https://github.com/dotnet/corert/issues/3224 - // We should figure out reflectable fields when scanning for reflection - FieldDesc fieldDefinition = _targetField.GetTypicalFieldDefinition(); - if (factory.MetadataManager.CanGenerateMetadata(fieldDefinition)) - { - return new DependencyList - { - new DependencyListEntry(factory.FieldMetadata(fieldDefinition), "LDTOKEN") - }; - } - - return null; + DependencyList result = null; + factory.MetadataManager.GetDependenciesDueToLdToken(ref result, factory, _targetField); + return result; } public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) @@ -63,5 +54,12 @@ namespace ILCompiler.DependencyAnalysis return objData.ToObjectData(); } + + protected internal override int ClassCode => -1326215725; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return comparer.Compare(_targetField, ((RuntimeFieldHandleNode)other)._targetField); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeMethodHandleNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeMethodHandleNode.cs index 920947abaa..c40987f3cc 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeMethodHandleNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeMethodHandleNode.cs @@ -50,14 +50,7 @@ namespace ILCompiler.DependencyAnalysis dependencies.Add(factory.GVMDependencies(_targetMethod), "GVM dependencies for runtime method handle"); } - // TODO: https://github.com/dotnet/corert/issues/3224 - // We should figure out reflectable methods when scanning for reflection - MethodDesc methodDefinition = _targetMethod.GetTypicalMethodDefinition(); - if (factory.MetadataManager.CanGenerateMetadata(methodDefinition)) - { - dependencies = dependencies ?? new DependencyList(); - dependencies.Add(factory.MethodMetadata(methodDefinition), "LDTOKEN"); - } + factory.MetadataManager.GetDependenciesDueToLdToken(ref dependencies, factory, _targetMethod); return dependencies; } @@ -76,5 +69,12 @@ namespace ILCompiler.DependencyAnalysis return objData.ToObjectData(); } + + protected internal override int ClassCode => -274400625; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return comparer.Compare(_targetMethod, ((RuntimeMethodHandleNode)other)._targetMethod); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs index 6e22b73da4..cce88f8108 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs @@ -12,8 +12,9 @@ namespace ILCompiler.DependencyAnalysis { public sealed class RyuJitNodeFactory : NodeFactory { - public RyuJitNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager, NameMangler nameMangler) - : base(context, compilationModuleGroup, metadataManager, nameMangler, new LazyGenericsDisabledPolicy()) + public RyuJitNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager, + InteropStubManager interopStubManager, NameMangler nameMangler, VTableSliceProvider vtableSliceProvider, DictionaryLayoutProvider dictionaryLayoutProvider) + : base(context, compilationModuleGroup, metadataManager, interopStubManager, nameMangler, new LazyGenericsDisabledPolicy(), vtableSliceProvider, dictionaryLayoutProvider, new ExternSymbolsImportedNodeProvider()) { } @@ -36,16 +37,16 @@ namespace ILCompiler.DependencyAnalysis // On CLR this would throw a SecurityException with "ECall methods must be packaged into a system module." // This is a corner case that nobody is likely to care about. - throw new TypeSystemException.InvalidProgramException(ExceptionStringID.InvalidProgramSpecific, method); + ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramSpecific, method); } - if (CompilationModuleGroup.ContainsMethodBody(method)) + if (CompilationModuleGroup.ContainsMethodBody(method, false)) { return new MethodCodeNode(method); } else { - return new ExternMethodSymbolNode(this, method); + return _importedNodeProvider.ImportedMethodCodeNode(this, method, false); } } @@ -64,7 +65,7 @@ namespace ILCompiler.DependencyAnalysis else { // Otherwise we just unbox 'this' and don't touch anything else. - return new UnboxingStubNode(method); + return new UnboxingStubNode(method, Target); } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ScannedMethodNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ScannedMethodNode.cs index d5fb77f08b..43d27c3443 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ScannedMethodNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ScannedMethodNode.cs @@ -18,7 +18,7 @@ namespace ILCompiler.DependencyAnalysis /// Represents a method that should be scanned by an IL scanner and its dependencies /// analyzed. /// - public class ScannedMethodNode : DependencyNodeCore, IMethodNode + public class ScannedMethodNode : DependencyNodeCore, IMethodBodyNode { private readonly MethodDesc _method; private DependencyList _dependencies; @@ -62,5 +62,12 @@ namespace ILCompiler.DependencyAnalysis public override bool InterestingForDynamicDependencyAnalysis => false; public override bool HasDynamicDependencies => false; public override bool HasConditionalStaticDependencies => false; + + int ISortableSymbolNode.ClassCode => -1381809560; + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + return comparer.Compare(Method, ((ScannedMethodNode)other).Method); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ShadowConcreteMethodNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ShadowConcreteMethodNode.cs index cb5e24e649..40d7cc978a 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ShadowConcreteMethodNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ShadowConcreteMethodNode.cs @@ -20,7 +20,7 @@ namespace ILCompiler.DependencyAnalysis /// method body, as if it was generated. The node acts as a symbol for the canonical /// method for convenience. /// - internal class ShadowConcreteMethodNode : DependencyNodeCore, IMethodNode + public class ShadowConcreteMethodNode : DependencyNodeCore, IMethodNode { /// /// Gets the canonical method body that defines the dependencies of this node. @@ -98,5 +98,16 @@ namespace ILCompiler.DependencyAnalysis public sealed override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; public sealed override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + + int ISortableSymbolNode.ClassCode => -1440570971; + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + var compare = comparer.Compare(Method, ((ShadowConcreteMethodNode)other).Method); + if (compare != 0) + return compare; + + return comparer.Compare(CanonicalMethodNode, ((ShadowConcreteMethodNode)other).CanonicalMethodNode); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ShadowConcreteUnboxingThunkNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ShadowConcreteUnboxingThunkNode.cs index ae9f233d60..fe973fdb3e 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ShadowConcreteUnboxingThunkNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ShadowConcreteUnboxingThunkNode.cs @@ -69,5 +69,16 @@ namespace ILCompiler.DependencyAnalysis public sealed override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; public sealed override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + + int ISortableSymbolNode.ClassCode => -501699818; + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + var compare = comparer.Compare(Method, ((ShadowConcreteUnboxingThunkNode)other).Method); + if (compare != 0) + return compare; + + return comparer.Compare(_canonicalThunk, ((ShadowConcreteUnboxingThunkNode)other)._canonicalThunk); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/SortableDependencyNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/SortableDependencyNode.cs new file mode 100644 index 0000000000..521a5bf459 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/SortableDependencyNode.cs @@ -0,0 +1,172 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +using ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public abstract class SortableDependencyNode : DependencyNodeCore + { +#if !SUPPORT_JIT + /// + /// Allows grouping of instances such that all nodes in a lower phase + /// will be ordered before nodes in a later phase. + /// + protected internal virtual int Phase => (int)ObjectNodePhase.Unordered; + + /// + /// Gets an identifier that is the same for all instances of this + /// descendant, but different from the of any other descendant. + /// + /// + /// This is really just a number, ideally produced by "new Random().Next(int.MinValue, int.MaxValue)". + /// If two manage to conflict (which is pretty unlikely), just make a new one... + /// + protected internal abstract int ClassCode { get; } + + // Note to implementers: the type of `other` is actually the same as the type of `this`. + protected internal virtual int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + throw new NotImplementedException("Multiple nodes of this type are not supported"); + } + + protected enum ObjectNodePhase + { + /// + /// Nodes should only be placed in this phase if they have strict output ordering requirements that + /// affect compiler correctness. Today that includes native layout tables. + /// + Ordered, + Unordered + } + + protected enum ObjectNodeOrder + { + // + // The ordering of this sequence of nodes is deliberate and currently required for + // compiler correctness. + // + MetadataNode, + ResourceDataNode, + ResourceIndexNode, + TypeMetadataMapNode, + ClassConstructorContextMap, + DynamicInvokeTemplateDataNode, + ReflectionInvokeMapNode, + DelegateMarshallingStubMapNode, + StructMarshallingStubMapNode, + ArrayMapNode, + ReflectionFieldMapNode, + NativeLayoutInfoNode, + ExactMethodInstantiationsNode, + GenericTypesHashtableNode, + GenericMethodsHashtableNode, + GenericVirtualMethodTableNode, + InterfaceGenericVirtualMethodTableNode, + GenericMethodsTemplateMap, + GenericTypesTemplateMap, + BlockReflectionTypeMapNode, + StaticsInfoHashtableNode, + ReflectionVirtualInvokeMapNode, + ExternalReferencesTableNode, + ArrayOfEmbeddedPointersNode, + DefaultConstructorMapNode, + StackTraceEmbeddedMetadataNode, + StackTraceMethodMappingNode, + ArrayOfEmbeddedDataNode + } + + public class EmbeddedObjectNodeComparer : IComparer + { + private CompilerComparer _comparer; + + public EmbeddedObjectNodeComparer(CompilerComparer comparer) + { + _comparer = comparer; + } + + public int Compare(EmbeddedObjectNode x, EmbeddedObjectNode y) + { + return CompareImpl(x, y, _comparer); + } + } + + /// + /// This comparer is used to sort the marked node list. We only care about ordering ObjectNodes + /// for emission into the binary, so any EmbeddedObjectNode or DependencyNodeCore objects are + /// skipped for efficiency. + /// + public class ObjectNodeComparer : IComparer> + { + private CompilerComparer _comparer; + + public ObjectNodeComparer(CompilerComparer comparer) + { + _comparer = comparer; + } + + public int Compare(DependencyNodeCore x1, DependencyNodeCore y1) + { + ObjectNode x = x1 as ObjectNode; + ObjectNode y = y1 as ObjectNode; + + if (x == y) + { + return 0; + } + + // Sort non-object nodes after ObjectNodes + if (x == null) + return 1; + + if (y == null) + return -1; + + return CompareImpl(x, y, _comparer); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CompareImpl(SortableDependencyNode x, SortableDependencyNode y, CompilerComparer comparer) + { + int phaseX = x.Phase; + int phaseY = y.Phase; + + if (phaseX == phaseY) + { + int codeX = x.ClassCode; + int codeY = y.ClassCode; + if (codeX == codeY) + { + Debug.Assert(x.GetType() == y.GetType() || + (x.GetType().IsConstructedGenericType && y.GetType().IsConstructedGenericType + && x.GetType().GetGenericTypeDefinition() == y.GetType().GetGenericTypeDefinition())); + + int result = x.CompareToImpl(y, comparer); + + // We did a reference equality check above so an "Equal" result is not expected + Debug.Assert(result != 0 || x == y); + + return result; + } + else + { + Debug.Assert(x.GetType() != y.GetType()); + return codeX - codeY; + } + } + else + { + return phaseX - phaseY; + } + } +#endif + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StackTraceEmbeddedMetadataNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StackTraceEmbeddedMetadataNode.cs new file mode 100644 index 0000000000..728bad0ecc --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StackTraceEmbeddedMetadataNode.cs @@ -0,0 +1,72 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; +using System.Text; + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// BlobIdStackTraceEmbeddedMetadata - holds the binary metadata graph + /// + public class StackTraceEmbeddedMetadataNode : ObjectNode, ISymbolDefinitionNode + { + public StackTraceEmbeddedMetadataNode() + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "_stacktrace_embedded_metadata_End", true); + } + + private ObjectAndOffsetSymbolNode _endSymbol; + public ISymbolDefinitionNode EndSymbol => _endSymbol; + + public override bool IsShareable => false; + + public override ObjectNodeSection Section => ObjectNodeSection.ReadOnlyDataSection; + + public override bool StaticDependenciesAreComputed => true; + + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection; + + public int Offset => 0; + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.StackTraceEmbeddedMetadataNode; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("_stacktrace_embedded_metadata"); + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node has no relocations. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + byte[] blob = ((PrecomputedMetadataManager)factory.MetadataManager).GetStackTraceBlob(factory); + _endSymbol.SetSymbolOffset(blob.Length); + + return new ObjectData( + blob, + Array.Empty(), + 1, + new ISymbolDefinitionNode[] + { + this, + EndSymbol + }); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StackTraceMethodMappingNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StackTraceMethodMappingNode.cs new file mode 100644 index 0000000000..0bfc65a28a --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StackTraceMethodMappingNode.cs @@ -0,0 +1,72 @@ +// 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 Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// BlobIdStackTraceMethodRvaToTokenMapping - list of 8-byte pairs (method RVA-method token) + /// + public sealed class StackTraceMethodMappingNode : ObjectNode, ISymbolDefinitionNode + { + public StackTraceMethodMappingNode() + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "_stacktrace_methodRVA_to_token_mapping_End", true); + } + + private ObjectAndOffsetSymbolNode _endSymbol; + public ISymbolDefinitionNode EndSymbol => _endSymbol; + + public override bool IsShareable => false; + + public override ObjectNodeSection Section => ObjectNodeSection.ReadOnlyDataSection; + + public override bool StaticDependenciesAreComputed => true; + + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection; + + public int Offset => 0; + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.StackTraceMethodMappingNode; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("_stacktrace_methodRVA_to_token_mapping"); + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // The dependency tracking of this node currently does nothing because the data emission relies + // the set of compiled methods which has an incomplete state during dependency tracking. + if (relocsOnly) + { + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + } + + ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); + objData.RequireInitialPointerAlignment(); + objData.AddSymbol(this); + objData.AddSymbol(_endSymbol); + + RelocType reloc = factory.Target.Abi == TargetAbi.CoreRT ? + RelocType.IMAGE_REL_BASED_RELPTR32 : RelocType.IMAGE_REL_BASED_ADDR32NB; + + foreach (var mappingEntry in factory.MetadataManager.GetStackTraceMapping(factory)) + { + objData.EmitReloc(factory.MethodEntrypoint(mappingEntry.Entity), reloc); + objData.EmitInt(mappingEntry.MetadataHandle); + } + + _endSymbol.SetSymbolOffset(objData.CountBytes); + return objData.ToObjectData(); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StaticsInfoHashtableNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StaticsInfoHashtableNode.cs index 21268b7a9e..22122f7ede 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StaticsInfoHashtableNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StaticsInfoHashtableNode.cs @@ -38,6 +38,7 @@ namespace ILCompiler.DependencyAnalysis public override bool IsShareable => false; public override ObjectNodeSection Section => _externalReferences.Section; public override bool StaticDependenciesAreComputed => true; + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection; protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); @@ -48,6 +49,9 @@ namespace ILCompiler.DependencyAnalysis /// public static void AddStaticsInfoDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) { + if (!factory.MetadataManager.SupportsReflection) + return; + if (type is MetadataType && type.HasInstantiation && !type.IsCanonicalSubtype(CanonicalFormKind.Any)) { MetadataType metadataType = (MetadataType)type; @@ -67,7 +71,16 @@ namespace ILCompiler.DependencyAnalysis dependencies.Add(factory.Indirection(factory.TypeNonGCStaticsSymbol(metadataType)), "Non-GC statics indirection for StaticsInfoHashtable"); } - // TODO: TLS dependencies + if (metadataType.ThreadStaticFieldSize.AsInt > 0) + { + if (factory.Target.Abi == TargetAbi.ProjectN) + { + UtcNodeFactory utcFactory = (UtcNodeFactory)factory; + dependencies.Add(utcFactory.TypeThreadStaticsIndexSymbol(metadataType), "Thread statics index indirection for StaticsInfoHashtable"); + dependencies.Add(utcFactory.TypeThreadStaticsOffsetSymbol(metadataType), "Thread statics offset indirection for StaticsInfoHashtable"); + } + // TODO: TLS for CoreRT + } } } @@ -104,8 +117,20 @@ namespace ILCompiler.DependencyAnalysis ISymbolNode nonGCStaticIndirection = factory.Indirection(factory.TypeNonGCStaticsSymbol(metadataType)); bag.AppendUnsigned(BagElementKind.NonGcStaticData, _nativeStaticsReferences.GetIndex(nonGCStaticIndirection)); } + if (metadataType.ThreadStaticFieldSize.AsInt > 0) + { + if (factory.Target.Abi == TargetAbi.ProjectN) + { + UtcNodeFactory utcFactory = (UtcNodeFactory)factory; - // TODO: TLS + ISymbolNode threadStaticIndexIndirection = utcFactory.TypeThreadStaticsIndexSymbol(metadataType); + bag.AppendUnsigned(BagElementKind.ThreadStaticIndex, _nativeStaticsReferences.GetIndex(threadStaticIndexIndirection)); + + ISymbolNode threadStaticOffsetIndirection = utcFactory.TypeThreadStaticsOffsetSymbol(metadataType); + bag.AppendUnsigned(BagElementKind.ThreadStaticOffset, _nativeStaticsReferences.GetIndex(threadStaticOffsetIndirection)); + } + // TODO: TLS for CoreRT + } if (bag.ElementsCount > 0) { @@ -122,5 +147,8 @@ namespace ILCompiler.DependencyAnalysis return new ObjectData(hashTableBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.StaticsInfoHashtableNode; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StringAllocatorMethodNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StringAllocatorMethodNode.cs index 6318cdcb17..fe9528040a 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StringAllocatorMethodNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StringAllocatorMethodNode.cs @@ -22,7 +22,8 @@ namespace ILCompiler.DependencyAnalysis /// class StringAllocatorMethodNode : DependencyNodeCore, IMethodNode { - private MethodDesc _allocationMethod; + private readonly MethodDesc _allocationMethod; + private readonly MethodDesc _constructorMethod; public MethodDesc Method => _allocationMethod; @@ -43,17 +44,23 @@ namespace ILCompiler.DependencyAnalysis signatureBuilder.ReturnType = constructorMethod.OwningType; _allocationMethod = constructorMethod.OwningType.GetKnownMethod("Ctor", signatureBuilder.ToSignature()); + _constructorMethod = constructorMethod; } public override IEnumerable GetStaticDependencies(NodeFactory factory) { - return new[] { - new DependencyListEntry( - factory.ConstructedTypeSymbol(factory.TypeSystemContext.GetWellKnownType(WellKnownType.String)), - "String constructor call"), - new DependencyListEntry( - factory.MethodEntrypoint(_allocationMethod), - "String constructor call") }; + DependencyList result = new DependencyList(); + + result.Add( + factory.ConstructedTypeSymbol(factory.TypeSystemContext.GetWellKnownType(WellKnownType.String)), + "String constructor call"); + result.Add( + factory.MethodEntrypoint(_allocationMethod), + "String constructor call"); + + factory.MetadataManager.GetDependenciesDueToReflectability(ref result, factory, _constructorMethod); + + return result; } public override bool HasConditionalStaticDependencies => false; @@ -65,5 +72,12 @@ namespace ILCompiler.DependencyAnalysis public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => null; protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + int ISortableSymbolNode.ClassCode => 1991750873; + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + return comparer.Compare(_allocationMethod, ((StringAllocatorMethodNode)other)._allocationMethod); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StructMarshallingStubMapNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StructMarshallingStubMapNode.cs new file mode 100644 index 0000000000..604e250760 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/StructMarshallingStubMapNode.cs @@ -0,0 +1,111 @@ +// 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 Internal.NativeFormat; +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a hash table of struct marshalling stub types generated into the image. + /// + internal sealed class StructMarshallingStubMapNode : ObjectNode, ISymbolDefinitionNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + + public StructMarshallingStubMapNode(ExternalReferencesTableNode externalReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__struct_marshalling_stub_map_End", true); + _externalReferences = externalReferences; + } + + public ISymbolDefinitionNode EndSymbol => _endSymbol; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__struct_marshalling_stub_map"); + } + public int Offset => 0; + public override bool IsShareable => false; + + public override ObjectNodeSection Section => _externalReferences.Section; + + public override bool StaticDependenciesAreComputed => true; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + var writer = new NativeWriter(); + var typeMapHashTable = new VertexHashtable(); + + Section hashTableSection = writer.NewSection(); + hashTableSection.Place(typeMapHashTable); + + foreach (var structEntry in ((CompilerGeneratedInteropStubManager)factory.InteropStubManager).GetStructMarshallingTypes()) + { + // the order of data written is as follows: + // 0. managed struct type + // 1. struct marshalling thunk + // 2. struct unmarshalling thunk + // 3. struct cleanup thunk + // 4. size + // 5. NumFields<< 1 | HasInvalidLayout + // 6 for each field + // a. name + // b. offset + + var structType = structEntry.StructType; + var nativeType = structEntry.NativeStructType; + Vertex thunks= writer.GetTuple( + writer.GetUnsignedConstant(_externalReferences.GetIndex(factory.MethodEntrypoint(structEntry.MarshallingThunk))), + writer.GetUnsignedConstant(_externalReferences.GetIndex(factory.MethodEntrypoint(structEntry.UnmarshallingThunk))), + writer.GetUnsignedConstant(_externalReferences.GetIndex(factory.MethodEntrypoint(structEntry.CleanupThunk)))); + + uint size = (uint)nativeType.InstanceByteCount.AsInt; + uint mask = (uint)(nativeType.Fields.Length << 1) | (uint)(nativeType.HasInvalidLayout ? 1 : 0); + + Vertex data = writer.GetTuple( + thunks, + writer.GetUnsignedConstant(size), + writer.GetUnsignedConstant(mask) + ); + + for (int i = 0; i < nativeType.Fields.Length; i++) + { + data = writer.GetTuple( + data, + writer.GetStringConstant(nativeType.Fields[i].Name), + writer.GetUnsignedConstant((uint)nativeType.Fields[i].Offset.AsInt) + ); + } + + Vertex vertex = writer.GetTuple( + writer.GetUnsignedConstant(_externalReferences.GetIndex(factory.NecessaryTypeSymbol(structType))), + data + ); + + int hashCode = structType.GetHashCode(); + typeMapHashTable.Append((uint)hashCode, hashTableSection.Place(vertex)); + } + + byte[] hashTableBytes = writer.Save(); + + _endSymbol.SetSymbolOffset(hashTableBytes.Length); + + return new ObjectData(hashTableBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + protected internal override int ClassCode => (int)ObjectNodeOrder.StructMarshallingStubMapNode; + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMDebug.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMDebug.cs new file mode 100644 index 0000000000..0a1cd06391 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMDebug.cs @@ -0,0 +1,32 @@ +// 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 ILCompiler.DependencyAnalysis.ARM; + +namespace ILCompiler.DependencyAnalysis +{ + public static class ARMDebug + { + public static void EmitNYIAssert(NodeFactory factory, ref ARMEmitter encoder, string message, + [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = null, + [System.Runtime.CompilerServices.CallerMemberName] string memberName = null, + [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0) + { + ISymbolNode NYI_Assert = factory.ExternSymbol("NYI_Assert"); + String CallInfoPrefix = " " + sourceFilePath + "(" + sourceLineNumber.ToString() + "): method " + memberName + ": "; + ISymbolNode messageSymbol = factory.ConstantUtf8String(CallInfoPrefix + message); + encoder.EmitMOV(encoder.TargetRegister.Arg0, messageSymbol); + encoder.EmitJMP(NYI_Assert); + } + + public static void EmitHelperNYIAssert(NodeFactory factory, ref ARMEmitter encoder, ReadyToRunHelperId hId, + [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = null, + [System.Runtime.CompilerServices.CallerMemberName] string memberName = null, + [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0) + { + EmitNYIAssert(factory, ref encoder, hId.ToString() + " is not implemented", sourceFilePath, memberName, sourceLineNumber); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMEmitter.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMEmitter.cs index df063a9f8d..535f13fce4 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMEmitter.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMEmitter.cs @@ -18,30 +18,133 @@ namespace ILCompiler.DependencyAnalysis.ARM public ObjectDataBuilder Builder; public TargetRegisterMap TargetRegister; - // add reg, immediate - public void EmitADD(Register reg, byte immediate) + // check the value length + private static bool IsBitNumOverflow(int value, byte numBits) { + return (value >> numBits) != 0; + } + + private static bool IsLowReg(Register reg) + { + return !IsBitNumOverflow((int)reg, 3); + } + + private static bool IsValidReg(Register reg) + { + return !IsBitNumOverflow((int)reg, 4); + } + + // mov reg, immediate + // reg range: [0..7] + // immediage range: [0..255] + public void EmitMOV(Register reg, int immediate) + { + Debug.Assert(IsLowReg(reg) && !IsBitNumOverflow(immediate, 8)); + Builder.EmitShort((short)(0x2000 + ((byte)reg << 8) + immediate)); + } + + // cmp reg, immediate + // reg range: [0..7] + // immediage range: [0..255] + public void EmitCMP(Register reg, int immediate) + { + Debug.Assert(IsLowReg(reg) && !IsBitNumOverflow(immediate, 8)); + Builder.EmitShort((short)(0x2800 + ((byte)reg << 8) + immediate)); + } + + // add reg, immediate + // reg range: [0..7] + // immediage range: [0..255] + public void EmitADD(Register reg, int immediate) + { + Debug.Assert(IsLowReg(reg) && !IsBitNumOverflow(immediate, 8)); Builder.EmitShort((short)(0x3000 + ((byte)reg << 8) + immediate)); } + // sub reg, immediate + // reg range: [0..7] + // immediage range: [0..255] + public void EmitSUB(Register reg, int immediate) + { + Debug.Assert(IsLowReg(reg) && !IsBitNumOverflow(immediate, 8)); + Builder.EmitShort((short)(0x3800 + ((byte)reg << 8) + immediate)); + } + + // nop + public void EmitNOP() + { + Builder.EmitByte(0x00); + Builder.EmitByte(0xbf); + } + + // __debugbreak + public void EmitDebugBreak() + { + Builder.EmitByte(0xde); + Builder.EmitByte(0xfe); + } + // push reg + // reg range: [0..12, LR] public void EmitPUSH(Register reg) { + Debug.Assert(reg >= Register.R0 && (reg <= Register.R12 || reg == TargetRegister.LR)); Builder.EmitByte(0x4d); Builder.EmitByte(0xf8); Builder.EmitShort((short)(0x0d04 + ((byte)reg << 12))); } + // pop reg + // reg range: [0..12, LR, PC] + public void EmitPOP(Register reg) + { + Debug.Assert(IsValidReg(reg) && reg != TargetRegister.SP); + Builder.EmitByte(0x5d); + Builder.EmitByte(0xf8); + Builder.EmitShort((short)(0x0b04 + ((byte)reg << 12))); + } + // mov reg, reg + // reg range: [0..PC] public void EmitMOV(Register destination, Register source) { + Debug.Assert(IsValidReg(destination) && IsValidReg(source)); Builder.EmitShort((short)(0x4600 + (((byte)destination & 0x8) << 4) + (((byte)source & 0x8) << 3) + (((byte)source & 0x7) << 3) + ((byte)destination & 0x7))); } - // mov reg, [reloc] & 0x0000FFFF + // ldr reg, [reg] + // reg range: [0..7] + public void EmitLDR(Register destination, Register source) + { + Debug.Assert(IsLowReg(destination) && IsLowReg(source)); + Builder.EmitShort((short)(0x6800 + (((byte)source & 0x7) << 3) + ((byte)destination & 0x7))); + } + + // ldr.w reg, [reg, #offset] + // reg range: [0..PC] + // offset range: [-255..4095] + public void EmitLDR(Register destination, Register source, int offset) + { + Debug.Assert(IsValidReg(destination) && IsValidReg(source)); + Debug.Assert(offset >= -255 && offset <= 4095); + if (offset >= 0) + { + Builder.EmitShort((short)(0xf8d0 + ((byte)(source)))); + Builder.EmitShort((short)(offset + (((byte)destination) << 12))); + } + else + { + Builder.EmitShort((short)(0xf850 + ((byte)(source)))); + Builder.EmitShort((short)(-offset + (((byte)destination) << 12) + (((byte)12) << 8))); + } + } + + // movw reg, [reloc] & 0x0000FFFF // movt reg, [reloc] & 0xFFFF0000 + // reg range: [0..12, LR] public void EmitMOV(Register destination, ISymbolNode symbol) { + Debug.Assert(destination >= Register.R0 && (destination <= Register.R12 || destination == TargetRegister.LR)); Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_THUMB_MOV32); Builder.EmitShort(unchecked((short)0xf240)); Builder.EmitShort((short)((byte)destination << 8)); @@ -49,14 +152,62 @@ namespace ILCompiler.DependencyAnalysis.ARM Builder.EmitShort((short)((byte)destination << 8)); } - // b symbol + // b.w symbol public void EmitJMP(ISymbolNode symbol) { + Debug.Assert(!symbol.RepresentsIndirectionCell); Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_THUMB_BRANCH24); Builder.EmitByte(0); Builder.EmitByte(0xF0); Builder.EmitByte(0); Builder.EmitByte(0xB8); } + + // bx reg + // reg range: [0..PC] + public void EmitJMP(Register destination) + { + Debug.Assert(IsValidReg(destination)); + Builder.EmitShort((short)(0x4700 + ((byte)destination << 3))); + } + + // bx lr + public void EmitRET() + { + EmitJMP(TargetRegister.LR); + } + + // bne #offset + // offset range: [-256..254] + public void EmitBNE(int immediate) + { + Debug.Assert(immediate >= -256 && immediate <= 254); + // considering the pipeline with PC + immediate -= 4; + + Builder.EmitByte((byte)(immediate >> 1)); + Builder.EmitByte(0xD1); + } + + // beq #offset + // offset range: [-256..254] + public void EmitBEQ(int immediate) + { + Debug.Assert(immediate >= -256 && immediate <= 254); + // considering the pipeline with PC + immediate -= 4; + + Builder.EmitByte((byte)(immediate >> 1)); + Builder.EmitByte(0xD0); + } + + // bne label(+4): ret(2) + next(2) + // bx lr + // label: ... + public void EmitRETIfEqual() + { + EmitBNE(4); + EmitRET(); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMInitialInterfaceDispatchStubNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMInitialInterfaceDispatchStubNode.cs index 82dc0e475e..1c201ebe35 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMInitialInterfaceDispatchStubNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMInitialInterfaceDispatchStubNode.cs @@ -8,6 +8,7 @@ using System; using ILCompiler.DependencyAnalysis.ARM; using ILCompiler.DependencyAnalysis.X64; using ILCompiler.DependencyAnalysis.X86; +using ILCompiler.DependencyAnalysis.ARM64; namespace ILCompiler.DependencyAnalysis { @@ -45,5 +46,12 @@ namespace ILCompiler.DependencyAnalysis { throw new NotImplementedException(); } + + protected override void EmitCode(NodeFactory factory, ref ARM64Emitter instructionEncoder, bool relocsOnly) + { + throw new NotImplementedException(); + } + + protected internal override int ClassCode => 588185132; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMJumpStubNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMJumpStubNode.cs new file mode 100644 index 0000000000..afbc1a662b --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMJumpStubNode.cs @@ -0,0 +1,24 @@ +// 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 ILCompiler.DependencyAnalysis.ARM; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class JumpStubNode + { + protected override void EmitCode(NodeFactory factory, ref ARMEmitter encoder, bool relocsOnly) + { + if (!_target.RepresentsIndirectionCell) + { + encoder.EmitJMP(_target); // b methodEntryPoint + } + else + { + encoder.EmitMOV(encoder.TargetRegister.InterproceduralScratch, _target); + encoder.EmitJMP(encoder.TargetRegister.InterproceduralScratch); + } + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunGenericHelperNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunGenericHelperNode.cs index df0916b44d..b760ddd7ce 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunGenericHelperNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunGenericHelperNode.cs @@ -13,10 +13,232 @@ using Debug = System.Diagnostics.Debug; namespace ILCompiler.DependencyAnalysis { partial class ReadyToRunGenericHelperNode - { + { + protected Register GetContextRegister(ref /* readonly */ ARMEmitter encoder) + { + if (_id == ReadyToRunHelperId.DelegateCtor) + return encoder.TargetRegister.Arg2; + else + return encoder.TargetRegister.Arg0; + } + + protected void EmitDictionaryLookup(NodeFactory factory, ref ARMEmitter encoder, Register context, Register result, GenericLookupResult lookup, bool relocsOnly) + { + // INVARIANT: must not trash context register + + // Find the generic dictionary slot + int dictionarySlot = 0; + if (!relocsOnly) + { + // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly. + dictionarySlot = factory.GenericDictionaryLayout(_dictionaryOwner).GetSlotForEntry(lookup); + } + + // Load the generic dictionary cell + encoder.EmitLDR(result, context, dictionarySlot * factory.Target.PointerSize); + + switch (lookup.LookupResultReferenceType(factory)) + { + case GenericLookupResultReferenceType.Indirect: + // Do another indirection + encoder.EmitLDR(result, result); + break; + + case GenericLookupResultReferenceType.ConditionalIndirect: + // Test result, 0x1 + // JEQ L1 + // mov result, [result-1] + // L1: + throw new NotImplementedException(); + + default: + break; + } + } + protected sealed override void EmitCode(NodeFactory factory, ref ARMEmitter encoder, bool relocsOnly) { - throw new NotImplementedException(); + // First load the generic context into the context register. + EmitLoadGenericContext(factory, ref encoder, relocsOnly); + + Register contextRegister = GetContextRegister(ref encoder); + + switch (_id) + { + case ReadyToRunHelperId.GetNonGCStaticBase: + { + Debug.Assert(contextRegister == encoder.TargetRegister.Arg0); + + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); + + MetadataType target = (MetadataType)_target; + if (!factory.TypeSystemContext.HasLazyStaticConstructor(target)) + { + encoder.EmitRET(); + } + else + { + // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. + int cctorContextSize = NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target); + encoder.EmitLDR(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg0, ((short)(factory.Target.PointerSize - cctorContextSize))); + encoder.EmitCMP(encoder.TargetRegister.Arg1, ((byte)1)); + encoder.EmitRETIfEqual(); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); + encoder.EmitSUB(encoder.TargetRegister.Arg0, ((byte)(cctorContextSize))); + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); + } + } + break; + + case ReadyToRunHelperId.GetGCStaticBase: + { + Debug.Assert(contextRegister == encoder.TargetRegister.Arg0); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg0); + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); + encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Result); + encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Result); + + MetadataType target = (MetadataType)_target; + if (!factory.TypeSystemContext.HasLazyStaticConstructor(target)) + { + encoder.EmitRET(); + } + else + { + // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. + GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg2, nonGcRegionLookup, relocsOnly); + + int cctorContextSize = NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target); + encoder.EmitLDR(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, ((short)(factory.Target.PointerSize - cctorContextSize))); + encoder.EmitCMP(encoder.TargetRegister.Arg3, 1); + encoder.EmitRETIfEqual(); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); + encoder.EmitMOV(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2); + encoder.EmitSUB(encoder.TargetRegister.Arg0, cctorContextSize); + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); + } + } + break; + + case ReadyToRunHelperId.GetThreadStaticBase: + { + Debug.Assert(contextRegister == encoder.TargetRegister.Arg0); + + MetadataType target = (MetadataType)_target; + + // Look up the index cell + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1, _lookupSignature, relocsOnly); + + ISymbolNode helperEntrypoint; + if (factory.TypeSystemContext.HasLazyStaticConstructor(target)) + { + // There is a lazy class constructor. We need the non-GC static base because that's where the + // class constructor context lives. + GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2, nonGcRegionLookup, relocsOnly); + int cctorContextSize = NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target); + encoder.EmitSUB(encoder.TargetRegister.Arg2, cctorContextSize); + + helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase); + } + else + { + helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType); + } + + // First arg: address of the TypeManager slot that provides the helper with + // information about module index and the type manager instance (which is used + // for initialization on first access). + encoder.EmitLDR(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1); + + // Second arg: index of the type in the ThreadStatic section of the modules + encoder.EmitLDR(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg1, factory.Target.PointerSize); + + encoder.EmitJMP(helperEntrypoint); + } + break; + + case ReadyToRunHelperId.DelegateCtor: + { + ARMDebug.EmitNYIAssert(factory, ref encoder, "DelegateCtor EmitCode is not implemented"); + /* + *** + NOT TESTED!!! + *** + // This is a weird helper. Codegen populated Arg0 and Arg1 with the values that the constructor + // method expects. Codegen also passed us the generic context in Arg2. + // We now need to load the delegate target method into Arg2 (using a dictionary lookup) + // and the optional 4th parameter, and call the ctor. + + Debug.Assert(contextRegister == encoder.TargetRegister.Arg2); + + var target = (DelegateCreationInfo)_target; + + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, _lookupSignature, relocsOnly); + + if (target.Thunk != null) + { + Debug.Assert(target.Constructor.Method.Signature.Length == 3); + encoder.EmitMOV(encoder.TargetRegister.Arg3, target.Thunk); + } + else + { + Debug.Assert(target.Constructor.Method.Signature.Length == 2); + } + + encoder.EmitJMP(target.Constructor); + */ + } + break; + + // These are all simple: just get the thing from the dictionary and we're done + case ReadyToRunHelperId.TypeHandle: + case ReadyToRunHelperId.MethodHandle: + case ReadyToRunHelperId.FieldHandle: + case ReadyToRunHelperId.MethodDictionary: + case ReadyToRunHelperId.MethodEntry: + case ReadyToRunHelperId.VirtualDispatchCell: + case ReadyToRunHelperId.DefaultConstructor: + { + EmitDictionaryLookup(factory, ref encoder, contextRegister, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); + encoder.EmitRET(); + } + break; + default: + throw new NotImplementedException(); + } + } + + protected virtual void EmitLoadGenericContext(NodeFactory factory, ref ARMEmitter encoder, bool relocsOnly) + { + // Assume generic context is already loaded in the context register. + } + } + + partial class ReadyToRunGenericLookupFromTypeNode + { + protected override void EmitLoadGenericContext(NodeFactory factory, ref ARMEmitter encoder, bool relocsOnly) + { + // We start with context register pointing to the EEType + Register contextRegister = GetContextRegister(ref encoder); + + // Locate the VTable slot that points to the dictionary + int vtableSlot = 0; + if (!relocsOnly) + { + // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly. + vtableSlot = VirtualMethodSlotHelper.GetGenericDictionarySlot(factory, (TypeDesc)_dictionaryOwner); + } + + int pointerSize = factory.Target.PointerSize; + int slotOffset = EETypeNode.GetVTableOffset(pointerSize) + (vtableSlot * pointerSize); + + // Load the dictionary pointer from the VTable + encoder.EmitLDR(contextRegister, contextRegister, slotOffset); } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs index c8a21f2fe6..d4bf28b316 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System; - +using System.Diagnostics; using Internal.TypeSystem; using ILCompiler; @@ -16,80 +16,209 @@ namespace ILCompiler.DependencyAnalysis /// partial class ReadyToRunHelperNode { - private ExternSymbolNode NYI_Assert; protected override void EmitCode(NodeFactory factory, ref ARMEmitter encoder, bool relocsOnly) { - NYI_Assert = new ExternSymbolNode("NYI_Assert"); - switch (Id) { case ReadyToRunHelperId.NewHelper: { TypeDesc target = (TypeDesc)Target; - encoder.EmitJMP(NYI_Assert); + encoder.EmitMOV(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target)); + encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewObjectHelperForType(target))); } break; case ReadyToRunHelperId.VirtualCall: { MethodDesc targetMethod = (MethodDesc)Target; - encoder.EmitJMP(NYI_Assert); + + Debug.Assert(!targetMethod.OwningType.IsInterface); + + int pointerSize = factory.Target.PointerSize; + + int slot = 0; + if (!relocsOnly) + { + slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod); + Debug.Assert(slot != -1); + } + + encoder.EmitLDR(encoder.TargetRegister.InterproceduralScratch, encoder.TargetRegister.Arg0, 0); + encoder.EmitLDR(encoder.TargetRegister.InterproceduralScratch, encoder.TargetRegister.InterproceduralScratch, + EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize)); + encoder.EmitJMP(encoder.TargetRegister.InterproceduralScratch); } break; case ReadyToRunHelperId.IsInstanceOf: { TypeDesc target = (TypeDesc)Target; - encoder.EmitJMP(NYI_Assert); + encoder.EmitMOV(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target)); + encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, false))); } break; case ReadyToRunHelperId.CastClass: { TypeDesc target = (TypeDesc)Target; - encoder.EmitJMP(NYI_Assert); + encoder.EmitMOV(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target)); + encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, true))); } break; case ReadyToRunHelperId.NewArr1: { TypeDesc target = (TypeDesc)Target; - encoder.EmitJMP(NYI_Assert); + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg0); + encoder.EmitMOV(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target)); + encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewArrayHelperForType(target))); } break; case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)Target; - encoder.EmitJMP(NYI_Assert); + bool hasLazyStaticConstructor = factory.TypeSystemContext.HasLazyStaticConstructor(target); + encoder.EmitMOV(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target)); + + if (!hasLazyStaticConstructor) + { + encoder.EmitRET(); + } + else + { + // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. + encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target)); + encoder.EmitLDR(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, + ((short)(factory.Target.PointerSize - NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target)))); + encoder.EmitCMP(encoder.TargetRegister.Arg3, 1); + encoder.EmitRETIfEqual(); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); + encoder.EmitMOV(encoder.TargetRegister.Arg0/*Result*/, encoder.TargetRegister.Arg2); + encoder.EmitSUB(encoder.TargetRegister.Arg0, NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target)); + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); + } } break; case ReadyToRunHelperId.GetThreadStaticBase: { MetadataType target = (MetadataType)Target; - encoder.EmitJMP(NYI_Assert); + encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeThreadStaticIndex(target)); + + // First arg: address of the TypeManager slot that provides the helper with + // information about module index and the type manager instance (which is used + // for initialization on first access). + encoder.EmitLDR(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2); + + // Second arg: index of the type in the ThreadStatic section of the modules + encoder.EmitLDR(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg2, factory.Target.PointerSize); + + if (!factory.TypeSystemContext.HasLazyStaticConstructor(target)) + { + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType)); + } + else + { + encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target)); + encoder.EmitSUB(encoder.TargetRegister.Arg2, NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target)); + // TODO: performance optimization - inline the check verifying whether we need to trigger the cctor + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase)); + } } break; case ReadyToRunHelperId.GetGCStaticBase: { MetadataType target = (MetadataType)Target; - encoder.EmitJMP(NYI_Assert); + encoder.EmitMOV(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target)); + encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Result); + encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Result); + if (!factory.TypeSystemContext.HasLazyStaticConstructor(target)) + { + encoder.EmitRET(); + } + else + { + // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. + encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target)); + encoder.EmitLDR(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, + ((short)(factory.Target.PointerSize - NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target)))); + encoder.EmitCMP(encoder.TargetRegister.Arg3, 1); + encoder.EmitRETIfEqual(); + + encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); + encoder.EmitMOV(encoder.TargetRegister.Arg0/*Result*/, encoder.TargetRegister.Arg2); + encoder.EmitSUB(encoder.TargetRegister.Arg0, NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target)); + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); + } } break; case ReadyToRunHelperId.DelegateCtor: { DelegateCreationInfo target = (DelegateCreationInfo)Target; - encoder.EmitJMP(NYI_Assert); + + if (target.TargetNeedsVTableLookup) + { + encoder.EmitLDR(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg1); + + int slot = 0; + if (!relocsOnly) + slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, target.TargetMethod); + + Debug.Assert(slot != -1); + encoder.EmitLDR(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, + EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize)); + } + else + { + ISymbolNode targetMethodNode = target.GetTargetNode(factory); + encoder.EmitMOV(encoder.TargetRegister.Arg2, target.GetTargetNode(factory)); + } + + if (target.Thunk != null) + { + Debug.Assert(target.Constructor.Method.Signature.Length == 3); + encoder.EmitMOV(encoder.TargetRegister.Arg3, target.Thunk); + } + else + { + Debug.Assert(target.Constructor.Method.Signature.Length == 2); + } + + encoder.EmitJMP(target.Constructor); } break; case ReadyToRunHelperId.ResolveVirtualFunction: { + ARMDebug.EmitHelperNYIAssert(factory, ref encoder, ReadyToRunHelperId.ResolveVirtualFunction); + /* + *** + NOT TESTED!!! + *** MethodDesc targetMethod = (MethodDesc)Target; - encoder.EmitJMP(NYI_Assert); + if (targetMethod.OwningType.IsInterface) + { + encoder.EmitMOV(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod)); + encoder.EmitJMP(factory.ExternSymbol("RhpResolveInterfaceMethod")); + } + else + { + if (relocsOnly) + break; + + encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Arg0); + + int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod); + Debug.Assert(slot != -1); + encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Result, + ((short)(EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize)))); + encoder.EmitRET(); + } + */ } break; diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMUnboxingStubNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMUnboxingStubNode.cs index 99fd0dec3a..d61802e4c3 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMUnboxingStubNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMUnboxingStubNode.cs @@ -11,7 +11,7 @@ namespace ILCompiler.DependencyAnalysis protected override void EmitCode(NodeFactory factory, ref ARMEmitter encoder, bool relocsOnly) { encoder.EmitADD(encoder.TargetRegister.Arg0, (byte)factory.Target.PointerSize); // add r0, sizeof(void*); - encoder.EmitJMP(factory.MethodEntrypoint(_target)); // b methodEntryPoint + encoder.EmitJMP(GetUnderlyingMethodEntrypoint(factory)); // b methodEntryPoint } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/TargetRegisterMap.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/TargetRegisterMap.cs index 5e0fde4fa2..d3940ceaaa 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/TargetRegisterMap.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/TargetRegisterMap.cs @@ -15,13 +15,25 @@ namespace ILCompiler.DependencyAnalysis.ARM { public readonly Register Arg0; public readonly Register Arg1; + public readonly Register Arg2; + public readonly Register Arg3; public readonly Register Result; + public readonly Register InterproceduralScratch; + public readonly Register SP; + public readonly Register LR; + public readonly Register PC; public TargetRegisterMap(TargetOS os) { Arg0 = Register.R0; Arg1 = Register.R1; + Arg2 = Register.R2; + Arg3 = Register.R3; Result = Register.R0; + InterproceduralScratch = Register.R12; + SP = Register.R13; + LR = Register.R14; + PC = Register.R15; } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/ARM64Emitter.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/ARM64Emitter.cs new file mode 100644 index 0000000000..abeb8ee7f2 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/ARM64Emitter.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. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; + +namespace ILCompiler.DependencyAnalysis.ARM64 +{ + public struct ARM64Emitter + { + public ARM64Emitter(NodeFactory factory, bool relocsOnly) + { + Builder = new ObjectDataBuilder(factory, relocsOnly); + TargetRegister = new TargetRegisterMap(factory.Target.OperatingSystem); + } + + public ObjectDataBuilder Builder; + public TargetRegisterMap TargetRegister; + + // Assembly stub creation api. TBD, actually make this general purpose + public void EmitMOV(Register regDst, ref AddrMode memory) + { + throw new NotImplementedException(); + } + + public void EmitMOV(Register regDst, Register regSrc) + { + throw new NotImplementedException(); + } + + public void EmitMOV(Register regDst, int imm32) + { + throw new NotImplementedException(); + } + + public void EmitLEAQ(Register reg, ISymbolNode symbol, int delta = 0) + { + throw new NotImplementedException(); + } + + public void EmitLEA(Register reg, ref AddrMode addrMode) + { + throw new NotImplementedException(); + } + + public void EmitCMP(ref AddrMode addrMode, sbyte immediate) + { + throw new NotImplementedException(); + } + + // add reg, immediate + public void EmitADD(Register reg, byte immediate) + { + Builder.EmitInt((int)(0x91 << 24) | (immediate << 10) | ((byte)reg << 5) | (byte) reg); + } + + public void EmitJMP(ISymbolNode symbol) + { + if (symbol.RepresentsIndirectionCell) + { + Debug.Assert(false, "The following code to emit an jump stub to an indirection cell is untested. When testing on ARM64 please remove this assert and verify it is correct"); + // xip0 register num is 0x10 + + // ADRP xip0, [symbol (21bit ADRP thing)] + // 0x90000000 + (xip regnum) + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21); + Builder.EmitByte(0x10); + Builder.EmitByte(0x00); + Builder.EmitByte(0x00); + Builder.EmitByte(0x90); + + // LDR xip0, [xip0 + 12bit LDR page offset reloc)] + // 0xF9400000 + ((xip0 regnum) << 5) + (xip regnum) + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L); + Builder.EmitByte(0x10); + Builder.EmitByte(0x02); + Builder.EmitByte(0x40); + Builder.EmitByte(0xF9); + + // BR xip0 + // 0xD61F0000 + (xip0 regnum) << 5) + Builder.EmitByte(0x00); + Builder.EmitByte(0x02); + Builder.EmitByte(0x1F); + Builder.EmitByte(0xD6); + } + else + { + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_ARM64_BRANCH26); + Builder.EmitByte(0); + Builder.EmitByte(0); + Builder.EmitByte(0); + Builder.EmitByte(0x14); + } + } + + public void EmitINT3() + { + throw new NotImplementedException(); + } + + public void EmitJmpToAddrMode(ref AddrMode addrMode) + { + throw new NotImplementedException(); + } + + public void EmitRET() + { + throw new NotImplementedException(); + } + + public void EmitRETIfEqual() + { + throw new NotImplementedException(); + } + + private bool InSignedByteRange(int i) + { + return i == (int)(sbyte)i; + } + + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/ARM64JumpStubNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/ARM64JumpStubNode.cs new file mode 100644 index 0000000000..afff14c101 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/ARM64JumpStubNode.cs @@ -0,0 +1,16 @@ +// 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 ILCompiler.DependencyAnalysis.ARM64; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class JumpStubNode + { + protected override void EmitCode(NodeFactory factory, ref ARM64Emitter encoder, bool relocsOnly) + { + encoder.EmitJMP(_target); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunGenericHelperNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunGenericHelperNode.cs new file mode 100644 index 0000000000..ea938206e8 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunGenericHelperNode.cs @@ -0,0 +1,45 @@ +// 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 ILCompiler.DependencyAnalysis.ARM64; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + partial class ReadyToRunGenericHelperNode + { + protected Register GetContextRegister(ref /* readonly */ ARM64Emitter encoder) + { + throw new NotImplementedException(); + } + + protected void EmitDictionaryLookup(NodeFactory factory, ref ARM64Emitter encoder, Register context, Register result, GenericLookupResult lookup, bool relocsOnly) + { + throw new NotImplementedException(); + } + + protected sealed override void EmitCode(NodeFactory factory, ref ARM64Emitter encoder, bool relocsOnly) + { + throw new NotImplementedException(); + } + + protected virtual void EmitLoadGenericContext(NodeFactory factory, ref ARM64Emitter encoder, bool relocsOnly) + { + throw new NotImplementedException(); + } + } + + partial class ReadyToRunGenericLookupFromTypeNode + { + protected override void EmitLoadGenericContext(NodeFactory factory, ref ARM64Emitter encoder, bool relocsOnly) + { + throw new NotImplementedException(); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs new file mode 100644 index 0000000000..816c206014 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs @@ -0,0 +1,23 @@ +// 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.Diagnostics; + +using ILCompiler.DependencyAnalysis.ARM64; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// ARM64 specific portions of ReadyToRunHelperNode + /// + public partial class ReadyToRunHelperNode + { + protected override void EmitCode(NodeFactory factory, ref ARM64Emitter encoder, bool relocsOnly) + { + throw new NotImplementedException(); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/ARM64UnboxingStubNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/ARM64UnboxingStubNode.cs new file mode 100644 index 0000000000..3560aeeaa8 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/ARM64UnboxingStubNode.cs @@ -0,0 +1,18 @@ +// 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 ILCompiler.DependencyAnalysis.ARM64; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class UnboxingStubNode + { + protected override void EmitCode(NodeFactory factory, ref ARM64Emitter encoder, bool relocsOnly) + { + encoder.EmitADD(encoder.TargetRegister.Arg0, (byte)factory.Target.PointerSize); // add r0, sizeof(void*); + encoder.EmitJMP(GetUnderlyingMethodEntrypoint(factory)); // b methodEntryPoint + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/AddrMode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/AddrMode.cs new file mode 100644 index 0000000000..f8d757359d --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/AddrMode.cs @@ -0,0 +1,35 @@ +// 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; + +namespace ILCompiler.DependencyAnalysis.ARM64 +{ + public enum AddrModeSize + { + Int8 = 1, + Int16 = 2, + Int32 = 4, + Int64 = 8, + Int128 = 16 + } + + public struct AddrMode + { + public readonly Register BaseReg; + public readonly Register? IndexReg; + public readonly int Offset; + public readonly byte Scale; + public readonly AddrModeSize Size; + + public AddrMode(Register baseRegister, Register? indexRegister, int offset, byte scale, AddrModeSize size) + { + BaseReg = baseRegister; + IndexReg = indexRegister; + Offset = offset; + Scale = scale; + Size = size; + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/Register.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/Register.cs new file mode 100644 index 0000000000..b69177e30a --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/Register.cs @@ -0,0 +1,52 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ILCompiler.DependencyAnalysis.ARM64 +{ + public enum Register + { + X0 = 0, + X1 = 1, + X2 = 2, + X3 = 3, + X4 = 4, + X5 = 5, + X6 = 6, + X7 = 7, + X8 = 8, + X9 = 9, + X10 = 10, + X11 = 11, + X12 = 12, + X13 = 13, + X14 = 14, + X15 = 15, + X16 = 16, + X17 = 17, + X18 = 18, + X19 = 19, + X20 = 20, + X21 = 21, + X22 = 22, + X23 = 23, + X24 = 24, + X25 = 25, + X26 = 26, + X27 = 27, + X28 = 28, + X29 = 29, + X30 = 30, + + X31 = 31, + + None = 32, + NoIndex = 128, + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/TargetRegisterMap.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/TargetRegisterMap.cs new file mode 100644 index 0000000000..dff6e4a837 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM64/TargetRegisterMap.cs @@ -0,0 +1,39 @@ +// 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 Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis.ARM64 +{ + /// + /// Maps logical registers to physical registers on a specified OS. + /// + public struct TargetRegisterMap + { + public readonly Register Arg0; + public readonly Register Arg1; + public readonly Register Arg2; + public readonly Register Arg3; + public readonly Register Arg4; + public readonly Register Arg5; + public readonly Register Arg6; + public readonly Register Arg7; + public readonly Register Result; + + public TargetRegisterMap(TargetOS os) + { + Arg0 = Register.X0; + Arg1 = Register.X1; + Arg2 = Register.X2; + Arg3 = Register.X3; + Arg4 = Register.X4; + Arg5 = Register.X5; + Arg6 = Register.X6; + Arg7 = Register.X7; + Result = Register.X0; + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64Emitter.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64Emitter.cs index 3ca448c3f6..09ebadeaad 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64Emitter.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64Emitter.cs @@ -74,8 +74,18 @@ namespace ILCompiler.DependencyAnalysis.X64 public void EmitJMP(ISymbolNode symbol) { - Builder.EmitByte(0xE9); - Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_REL32); + if (symbol.RepresentsIndirectionCell) + { + Builder.EmitByte(0xff); + Builder.EmitByte(0x25); + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_REL32); + } + else + { + Builder.EmitByte(0xE9); + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_REL32); + + } } public void EmitINT3() diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64JumpStubNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64JumpStubNode.cs new file mode 100644 index 0000000000..ceaa371502 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64JumpStubNode.cs @@ -0,0 +1,16 @@ +// 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 ILCompiler.DependencyAnalysis.X64; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class JumpStubNode + { + protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) + { + encoder.EmitJMP(_target); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunGenericHelperNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunGenericHelperNode.cs index 93241ff8da..fca8037df7 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunGenericHelperNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunGenericHelperNode.cs @@ -207,9 +207,9 @@ namespace ILCompiler.DependencyAnalysis case ReadyToRunHelperId.MethodHandle: case ReadyToRunHelperId.FieldHandle: case ReadyToRunHelperId.MethodDictionary: - case ReadyToRunHelperId.VirtualCall: - case ReadyToRunHelperId.ResolveVirtualFunction: case ReadyToRunHelperId.MethodEntry: + case ReadyToRunHelperId.VirtualDispatchCell: + case ReadyToRunHelperId.DefaultConstructor: { EmitDictionaryLookup(factory, ref encoder, contextRegister, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); encoder.EmitRET(); diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs index 82247a54bd..614c171e62 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs @@ -31,27 +31,23 @@ namespace ILCompiler.DependencyAnalysis { MethodDesc targetMethod = (MethodDesc)Target; - if (targetMethod.OwningType.IsInterface) + Debug.Assert(!targetMethod.OwningType.IsInterface); + + AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); + encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); + + int pointerSize = factory.Target.PointerSize; + + int slot = 0; + if (!relocsOnly) { - encoder.EmitLEAQ(Register.R10, factory.InterfaceDispatchCell((MethodDesc)Target)); - AddrMode jmpAddrMode = new AddrMode(Register.R10, null, 0, 0, AddrModeSize.Int64); - encoder.EmitJmpToAddrMode(ref jmpAddrMode); - } - else - { - if (relocsOnly) - break; - - AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); - encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); - - int pointerSize = factory.Target.PointerSize; - - int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod); + slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod); Debug.Assert(slot != -1); - AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize), 0, AddrModeSize.Int64); - encoder.EmitJmpToAddrMode(ref jmpAddrMode); } + Debug.Assert(((INodeWithDebugInfo)this).DebugLocInfos[1].NativeOffset == encoder.Builder.CountBytes); + + AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize), 0, AddrModeSize.Int64); + encoder.EmitJmpToAddrMode(ref jmpAddrMode); } break; diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64UnboxingStubNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64UnboxingStubNode.cs index 326559d6a4..6239ec3371 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64UnboxingStubNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64UnboxingStubNode.cs @@ -13,7 +13,7 @@ namespace ILCompiler.DependencyAnalysis AddrMode thisPtr = new AddrMode( Register.RegDirect | encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitADD(ref thisPtr, (sbyte)factory.Target.PointerSize); - encoder.EmitJMP(factory.MethodEntrypoint(_target)); + encoder.EmitJMP(GetUnderlyingMethodEntrypoint(factory)); } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86Emitter.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86Emitter.cs index 72068ff227..ddf08d5007 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86Emitter.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86Emitter.cs @@ -28,10 +28,24 @@ namespace ILCompiler.DependencyAnalysis.X86 public void EmitJMP(ISymbolNode symbol) { - Builder.EmitByte(0xE9); - Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_REL32); + if (symbol.RepresentsIndirectionCell) + { + Builder.EmitByte(0xff); + Builder.EmitByte(0x25); + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_REL32); + } + else + { + Builder.EmitByte(0xE9); + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_REL32); + } } - + + public void EmitINT3() + { + Builder.EmitByte(0xCC); + } + private bool InSignedByteRange(int i) { return i == (int)(sbyte)i; diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86JumpStubNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86JumpStubNode.cs new file mode 100644 index 0000000000..b95bd1a545 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86JumpStubNode.cs @@ -0,0 +1,16 @@ +// 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 ILCompiler.DependencyAnalysis.X86; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class JumpStubNode + { + protected override void EmitCode(NodeFactory factory, ref X86Emitter encoder, bool relocsOnly) + { + encoder.EmitJMP(_target); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86UnboxingStubNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86UnboxingStubNode.cs index ea6009ce74..23bbe7e46e 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86UnboxingStubNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86UnboxingStubNode.cs @@ -13,7 +13,7 @@ namespace ILCompiler.DependencyAnalysis AddrMode thisPtr = new AddrMode( Register.RegDirect | encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int32); encoder.EmitADD(ref thisPtr, (sbyte)factory.Target.PointerSize); - encoder.EmitJMP(factory.MethodEntrypoint(_target)); + encoder.EmitJMP(GetUnderlyingMethodEntrypoint(factory)); } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ThreadStaticsIndexNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ThreadStaticsIndexNode.cs index 469c1473bd..f350c2cfba 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ThreadStaticsIndexNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ThreadStaticsIndexNode.cs @@ -5,6 +5,7 @@ using System; using Internal.Text; +using Internal.TypeSystem; using Debug = System.Diagnostics.Debug; @@ -17,7 +18,7 @@ namespace ILCompiler.DependencyAnalysis // The TLS slot index allocated for this module by the OS loader. We keep a pointer to this // value in the module header. - public class ThreadStaticsIndexNode : ObjectNode, IExportableSymbolNode + public class ThreadStaticsIndexNode : ObjectNode, IExportableSymbolNode, ISortableSymbolNode { string _prefix; @@ -56,7 +57,7 @@ namespace ILCompiler.DependencyAnalysis } } - public bool IsExported(NodeFactory factory) => true; + public ExportForm GetExportForm(NodeFactory factory) => ExportForm.ByName; public override bool IsShareable => false; @@ -80,6 +81,20 @@ namespace ILCompiler.DependencyAnalysis return objData.ToObjectData(); } + + protected internal override int ClassCode => -968500265; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return string.Compare(_prefix, ((ThreadStaticsIndexNode)other)._prefix); + } + + int ISortableSymbolNode.ClassCode => ClassCode; + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + return CompareToImpl((ObjectNode)other, comparer); + } } // The data structure used by the OS loader to load TLS chunks. @@ -166,5 +181,12 @@ namespace ILCompiler.DependencyAnalysis return objData.ToObjectData(); } + + protected internal override int ClassCode => -754150753; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return string.Compare(_prefix, ((ThreadStaticsDirectoryNode)other)._prefix); + } } -} \ No newline at end of file +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ThreadStaticsNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ThreadStaticsNode.cs index 9a66b0e436..357dde0fe9 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ThreadStaticsNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ThreadStaticsNode.cs @@ -53,7 +53,7 @@ namespace ILCompiler.DependencyAnalysis public override IEnumerable GetStaticDependencies(NodeFactory factory) { - List result = new List(); + DependencyList result = new DependencyList(); result.Add(new DependencyListEntry(GetGCStaticEETypeNode(factory), "ThreadStatic EEType")); @@ -62,6 +62,7 @@ namespace ILCompiler.DependencyAnalysis result.Add(new DependencyListEntry(factory.EagerCctorIndirection(_type.GetStaticConstructor()), "Eager .cctor")); } + EETypeNode.AddDependenciesForStaticsNode(factory, _type, ref result); return result; } @@ -74,5 +75,12 @@ namespace ILCompiler.DependencyAnalysis builder.RequireInitialPointerAlignment(); builder.EmitPointerReloc(GetGCStaticEETypeNode(factory)); } + + protected internal override int ClassCode => 2091208431; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return comparer.Compare(_type, ((ThreadStaticsNode)other)._type); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ThreadStaticsOffsetNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ThreadStaticsOffsetNode.cs index 8dd47f6427..096ebe98aa 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ThreadStaticsOffsetNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ThreadStaticsOffsetNode.cs @@ -14,7 +14,7 @@ namespace ILCompiler.DependencyAnalysis /// Represents the offset of the thread static region of a given type from the TLS section start. /// The node is used for cross-module thread statics reference /// - public class ThreadStaticsOffsetNode : EmbeddedObjectNode, IExportableSymbolNode + public class ThreadStaticsOffsetNode : EmbeddedObjectNode, IExportableSymbolNode, ISortableSymbolNode { private MetadataType _type; @@ -50,9 +50,9 @@ namespace ILCompiler.DependencyAnalysis return result; } - public bool IsExported(NodeFactory factory) + public ExportForm GetExportForm(NodeFactory factory) { - return factory.CompilationModuleGroup.ExportsType(Type); + return factory.CompilationModuleGroup.GetExportTypeForm(Type); } public MetadataType Type => _type; @@ -73,5 +73,19 @@ namespace ILCompiler.DependencyAnalysis { builder.EmitReloc(factory.TypeThreadStaticsSymbol(_type), RelocType.IMAGE_REL_SECREL); } + + protected internal override int ClassCode => 419394032; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return comparer.Compare(_type, ((ThreadStaticsOffsetNode)other)._type); + } + + int ISortableSymbolNode.ClassCode => 419394032; + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + return CompareToImpl((SortableDependencyNode)other, comparer); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs index 91481ec01a..2932a50e2a 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs @@ -77,7 +77,36 @@ namespace ILCompiler.DependencyAnalysis if(!type.IsDefType || type.IsInterface) return false; - return type.HasGenericVirtualMethod(); + // Type declares GVMs + if (type.HasGenericVirtualMethods()) + return true; + + // + // Check if the type implements any interface with GVM methods, where the method implementations could be on + // base types. + // Example: + // interface IFace { + // void IFaceGVMethod(); + // } + // class BaseClass { + // public virtual void IFaceGVMethod() { ... } + // } + // public class DerivedClass : BaseClass, IFace { } + // + foreach (var iface in type.RuntimeInterfaces) + { + foreach (var method in iface.GetMethods()) + { + if (!method.HasInstantiation || method.Signature.IsStatic) + continue; + + MethodDesc slotDecl = type.ResolveInterfaceMethodTarget(method); + if (slotDecl != null) + return true; + } + } + + return false; } public IEnumerable ScanForGenericVirtualMethodEntries() diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeManagerIndirectionNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeManagerIndirectionNode.cs index 7819750873..4199c29fbe 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeManagerIndirectionNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeManagerIndirectionNode.cs @@ -2,7 +2,10 @@ // 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 Internal.Text; +using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis { @@ -30,5 +33,7 @@ namespace ILCompiler.DependencyAnalysis objData.EmitZeroPointer(); return objData.ToObjectData(); } + + protected internal override int ClassCode => -2028598574; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeMetadataMapNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeMetadataMapNode.cs index c007657a5e..ea58dd3508 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeMetadataMapNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeMetadataMapNode.cs @@ -6,6 +6,7 @@ using System; using Internal.NativeFormat; using Internal.Text; +using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis { @@ -36,6 +37,8 @@ namespace ILCompiler.DependencyAnalysis public override bool StaticDependenciesAreComputed => true; + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.SupportsReflection; + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) @@ -78,5 +81,9 @@ namespace ILCompiler.DependencyAnalysis return new ObjectData(hashTableBytes, Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + + protected internal override int ClassCode => (int)ObjectNodeOrder.TypeMetadataMapNode; } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeMetadataNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeMetadataNode.cs index 4e06cf40f2..a8416afad0 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeMetadataNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeMetadataNode.cs @@ -44,12 +44,11 @@ namespace ILCompiler.DependencyAnalysis else dependencies.Add(factory.ModuleMetadata(_type.Module), "Containing module of a reflectable type"); - // TODO: https://github.com/dotnet/corert/issues/3224 - // We don't currently track the exact list of fields used - assume all are used - foreach (FieldDesc field in _type.GetFields()) + if (_type.IsDelegate) { - if (factory.MetadataManager.CanGenerateMetadata(field)) - dependencies.Add(factory.FieldMetadata(field), "Field of a reflectable type"); + // A delegate type metadata is rather useless without the Invoke method. + // If someone reflects on a delegate, chances are they're going to look at the signature. + dependencies.Add(factory.MethodMetadata(_type.GetMethod("Invoke", null)), "Delegate invoke method metadata"); } return dependencies; diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeThreadStaticIndexNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeThreadStaticIndexNode.cs index 6586d1ce83..11ac06f2c5 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeThreadStaticIndexNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeThreadStaticIndexNode.cs @@ -58,7 +58,7 @@ namespace ILCompiler.DependencyAnalysis if (!relocsOnly) { var node = factory.TypeThreadStaticsSymbol(_type); - typeTlsIndex = factory.ThreadStaticsRegion.IndexOfEmbeddedObject((ThreadStaticsNode)node); + typeTlsIndex = ((ThreadStaticsNode)node).IndexFromBeginningOfArray; } objData.EmitPointerReloc(factory.TypeManagerIndirection); @@ -66,5 +66,12 @@ namespace ILCompiler.DependencyAnalysis return objData.ToObjectData(); } + + protected internal override int ClassCode => -149601250; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return comparer.Compare(_type, ((TypeThreadStaticIndexNode)other)._type); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UnboxingStubNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UnboxingStubNode.cs index e63087f885..d0cb80ba38 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UnboxingStubNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UnboxingStubNode.cs @@ -2,9 +2,13 @@ // 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 Internal.Text; using Internal.TypeSystem; +using ILCompiler.DependencyAnalysisFramework; + using Debug = System.Diagnostics.Debug; namespace ILCompiler.DependencyAnalysis @@ -14,28 +18,49 @@ namespace ILCompiler.DependencyAnalysis /// public partial class UnboxingStubNode : AssemblyStubNode, IMethodNode, IExportableSymbolNode { - private MethodDesc _target; + // Section name on Windows has to be alphabetically less than the ending WindowsUnboxingStubsRegionNode node, and larger than + // the begining WindowsUnboxingStubsRegionNode node, in order to have proper delimiters to the begining/ending of the + // stubs region, in order for the runtime to know where the region starts and ends. + internal static readonly string WindowsSectionName = ".unbox$M"; + internal static readonly string UnixSectionName = "__unbox"; - public MethodDesc Method + private readonly TargetDetails _targetDetails; + + public MethodDesc Method { get; } + + public override ObjectNodeSection Section { get { - return _target; + string sectionName = _targetDetails.IsWindows ? WindowsSectionName : UnixSectionName; + return new ObjectNodeSection(sectionName, SectionType.Executable); } } + public override bool IsShareable => true; - public bool IsExported(NodeFactory factory) => factory.CompilationModuleGroup.ExportsMethod(Method); + public ExportForm GetExportForm(NodeFactory factory) => factory.CompilationModuleGroup.GetExportMethodForm(Method, true); - public UnboxingStubNode(MethodDesc target) + public UnboxingStubNode(MethodDesc target, TargetDetails targetDetails) { Debug.Assert(target.GetCanonMethodTarget(CanonicalFormKind.Specific) == target); Debug.Assert(target.OwningType.IsValueType); - _target = target; + Method = target; + _targetDetails = targetDetails; + } + + private ISymbolNode GetUnderlyingMethodEntrypoint(NodeFactory factory) + { + ISymbolNode node = factory.MethodEntrypoint(Method); + if (node is RuntimeDecodableJumpStubNode) + { + return ((RuntimeDecodableJumpStubNode)node).Target; + } + return node; } public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { - sb.Append("unbox_").Append(nameMangler.GetMangledMethodName(_target)); + sb.Append("unbox_").Append(nameMangler.GetMangledMethodName(Method)); } public static string GetMangledName(NameMangler nameMangler, MethodDesc method) @@ -43,8 +68,65 @@ namespace ILCompiler.DependencyAnalysis return "unbox_" + nameMangler.GetMangledMethodName(method); } + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + protected internal override int ClassCode => -1846923013; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return comparer.Compare(Method, ((UnboxingStubNode)other).Method); + } + + int ISortableSymbolNode.ClassCode => ClassCode; + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + return CompareToImpl((ObjectNode)other, comparer); + } + } + + // + // On Windows, we need to create special start/stop sections, in order to group all the unboxing stubs and + // have delimiters accessible through extern "C" variables in the bootstrapper. On Linux/Apple, the linker provides + // special names to the begining and end of sections already. + // + public class WindowsUnboxingStubsRegionNode : ObjectNode, ISymbolDefinitionNode + { + private readonly bool _isEndSymbol; + + public override ObjectNodeSection Section => new ObjectNodeSection(".unbox$" + (_isEndSymbol? "Z" : "A"), SectionType.Executable); public override bool IsShareable => true; + public override bool StaticDependenciesAreComputed => true; + public int Offset => 0; + + public WindowsUnboxingStubsRegionNode(bool isEndSymbol) + { + _isEndSymbol = isEndSymbol; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("__unbox_" + (_isEndSymbol ? "z" : "a")); + } protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + Debug.Assert(factory.Target.IsWindows); + + ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); + objData.RequireInitialAlignment(factory.Target.MinimumFunctionAlignment); + objData.AddSymbol(this); + + return objData.ToObjectData(); + } + + protected internal override int ClassCode => 1102274050; + + protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return _isEndSymbol.CompareTo(((WindowsUnboxingStubsRegionNode)other)._isEndSymbol); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcDictionaryLayoutNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcDictionaryLayoutNode.cs index aaf9250cce..d268fa17bd 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcDictionaryLayoutNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcDictionaryLayoutNode.cs @@ -25,9 +25,25 @@ namespace ILCompiler.DependencyAnalysis /// public partial class UtcDictionaryLayoutNode : DictionaryLayoutNode { + public override bool HasFixedSlots + { + get + { + return false; + } + } + public UtcDictionaryLayoutNode(TypeSystemEntity owningMethodOrType) : base(owningMethodOrType) { } + +#if !PROJECTN + public override void EnsureEntry(GenericLookupResult lookupResult) => throw new NotImplementedException(); + public override int GetSlotForEntry(GenericLookupResult entry) => throw new NotImplementedException(); + public override IEnumerable Entries => throw new NotImplementedException(); + public override ICollection GetTemplateEntries(NodeFactory factory) => throw new NotImplementedException(); + public override void EmitDictionaryData(ref ObjectDataBuilder builder, NodeFactory factory, GenericDictionaryNode dictionary, bool fixedLayoutOnly) => throw new NotImplementedException(); +#endif } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcNodeFactory.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcNodeFactory.cs index 1301565ea9..fbeb03680d 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcNodeFactory.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcNodeFactory.cs @@ -11,6 +11,7 @@ using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; using Internal.Runtime; using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; namespace ILCompiler { @@ -18,6 +19,7 @@ namespace ILCompiler { public static string CompilationUnitPrefix = ""; public string targetPrefix; + private bool buildMRT; private static byte[] ReadBytesFromFile(string filename) { @@ -57,20 +59,44 @@ namespace ILCompiler return null; } - private static MetadataManager PickMetadataManager(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, IEnumerable inputModules, string metadataFile) + private static MetadataManager PickMetadataManager(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, IEnumerable inputModules, IEnumerable inputMetadataOnlyAssemblies, string metadataFile) { if (metadataFile == null) { - return new EmptyMetadataManager(compilationModuleGroup, context); + return new EmptyMetadataManager(context); } else { - return new PrecomputedMetadataManager(compilationModuleGroup, context, FindMetadataDescribingModuleInInputSet(inputModules), inputModules, ReadBytesFromFile(metadataFile)); + return new PrecomputedMetadataManager(compilationModuleGroup, context, FindMetadataDescribingModuleInInputSet(inputModules), inputModules, inputMetadataOnlyAssemblies, ReadBytesFromFile(metadataFile), new UtcStackTraceEmissionPolicy()); } } - public UtcNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, IEnumerable inputModules, string metadataFile, string outputFile, UTCNameMangler nameMangler) - : base(context, compilationModuleGroup, PickMetadataManager(context, compilationModuleGroup, inputModules, metadataFile), nameMangler, new AttributeDrivenLazyGenericsPolicy()) + private static InteropStubManager NewEmptyInteropStubManager(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup) + { + // On Project N, the compiler doesn't generate the interop code on the fly + return new EmptyInteropStubManager(compilationModuleGroup, context, null); + } + + public UtcNodeFactory( + CompilerTypeSystemContext context, + CompilationModuleGroup compilationModuleGroup, + IEnumerable inputModules, + IEnumerable inputMetadataOnlyAssemblies, + string metadataFile, + string outputFile, + UTCNameMangler nameMangler, + bool buildMRT, + DictionaryLayoutProvider dictionaryLayoutProvider, + ImportedNodeProvider importedNodeProvider) + : base(context, + compilationModuleGroup, + PickMetadataManager(context, compilationModuleGroup, inputModules, inputMetadataOnlyAssemblies, metadataFile), + NewEmptyInteropStubManager(context, compilationModuleGroup), + nameMangler, + new AttributeDrivenLazyGenericsPolicy(), + null, + dictionaryLayoutProvider, + importedNodeProvider) { CreateHostedNodeCaches(); CompilationUnitPrefix = nameMangler.CompilationUnitPrefix; @@ -79,6 +105,8 @@ namespace ILCompiler TLSDirectory = new ThreadStaticsDirectoryNode(targetPrefix); TlsStart = new ExternSymbolNode(targetPrefix + "_tls_start"); TlsEnd = new ExternSymbolNode(targetPrefix + "_tls_end"); + LoopHijackFlag = new LoopHijackFlagNode(); + this.buildMRT = buildMRT; } private void CreateHostedNodeCaches() @@ -93,19 +121,15 @@ namespace ILCompiler return new GCStaticDescNode(type, true); }); - _threadStaticsOffset = new NodeCache((MetadataType type) => + _threadStaticsOffset = new NodeCache((MetadataType type) => { - if (CompilationModuleGroup.ContainsType(type)) + if (CompilationModuleGroup.ContainsType(type) && !(CompilationModuleGroup.ShouldReferenceThroughImportTable(type))) { return new ThreadStaticsOffsetNode(type, this); } - else if (CompilationModuleGroup.ShouldReferenceThroughImportTable(type)) - { - return new ImportedThreadStaticsOffsetNode(type, this); - } else { - return new ExternSymbolNode(ThreadStaticsOffsetNode.GetMangledName(NameMangler, type)); + return _importedNodeProvider.ImportedThreadStaticOffsetNode(this, type); } }); @@ -114,11 +138,6 @@ namespace ILCompiler return new ImportedThreadStaticsIndexNode(this); }); - _hostedGenericDictionaryLayouts = new NodeCache((TypeSystemEntity methodOrType) => - { - return new UtcDictionaryLayoutNode(methodOrType); - }); - _nonExternMethodSymbols = new NodeCache((MethodKey method) => { return new NonExternMethodSymbolNode(this, method.Method, method.IsUnboxingStub); @@ -145,8 +164,25 @@ namespace ILCompiler graph.AddRoot(GCStaticDescRegion, "GC Static Desc is always generated"); graph.AddRoot(ThreadStaticsOffsetRegion, "Thread Statics Offset Region is always generated"); graph.AddRoot(ThreadStaticGCDescRegion, "Thread Statics GC Desc Region is always generated"); - graph.AddRoot(ThreadStaticsIndex, "Thread statics index is always generated"); - graph.AddRoot(TLSDirectory, "TLS Directory is always generated"); + graph.AddRoot(ImportAddressTablesTable, "Import address tables region"); + + + if (Target.IsWindows) + { + // We need 2 delimiter symbols to bound the unboxing stubs region on Windows platforms (these symbols are + // accessed using extern "C" variables in the bootstrapper) + // On non-Windows platforms, the linker emits special symbols with special names at the begining/end of a section + // so we do not need to emit them ourselves. + graph.AddRoot(new WindowsUnboxingStubsRegionNode(false), "UnboxingStubsRegion delimiter for Windows platform"); + graph.AddRoot(new WindowsUnboxingStubsRegionNode(true), "UnboxingStubsRegion delimiter for Windows platform"); + } + + // The native part of the MRT library links against CRT which defines _tls_index and _tls_used. + if (!buildMRT) + { + graph.AddRoot(ThreadStaticsIndex, "Thread statics index is always generated"); + graph.AddRoot(TLSDirectory, "TLS Directory is always generated"); + } ReadyToRunHeader.Add(ReadyToRunSectionType.EagerCctor, EagerCctorTable, EagerCctorTable.StartSymbol, EagerCctorTable.EndSymbol); ReadyToRunHeader.Add(ReadyToRunSectionType.InterfaceDispatchTable, DispatchMapTable, DispatchMapTable.StartSymbol); @@ -156,9 +192,18 @@ namespace ILCompiler ReadyToRunHeader.Add(ReadyToRunSectionType.GCStaticDesc, GCStaticDescRegion, GCStaticDescRegion.StartSymbol, GCStaticDescRegion.EndSymbol); ReadyToRunHeader.Add(ReadyToRunSectionType.ThreadStaticOffsetRegion, ThreadStaticsOffsetRegion, ThreadStaticsOffsetRegion.StartSymbol, ThreadStaticsOffsetRegion.EndSymbol); ReadyToRunHeader.Add(ReadyToRunSectionType.ThreadStaticGCDescRegion, ThreadStaticGCDescRegion, ThreadStaticGCDescRegion.StartSymbol, ThreadStaticGCDescRegion.EndSymbol); - ReadyToRunHeader.Add(ReadyToRunSectionType.ThreadStaticIndex, ThreadStaticsIndex, ThreadStaticsIndex); + ReadyToRunHeader.Add(ReadyToRunSectionType.LoopHijackFlag, LoopHijackFlag, LoopHijackFlag); + ReadyToRunHeader.Add(ReadyToRunSectionType.ImportAddressTables, ImportAddressTablesTable, ImportAddressTablesTable.StartSymbol, ImportAddressTablesTable.EndSymbol); - MetadataManager.AddToReadyToRunHeader(ReadyToRunHeader, this); + if (!buildMRT) + { + ReadyToRunHeader.Add(ReadyToRunSectionType.ThreadStaticIndex, ThreadStaticsIndex, ThreadStaticsIndex); + } + + + var commonFixupsTableNode = new ExternalReferencesTableNode("CommonFixupsTable", this); + InteropStubManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode); + MetadataManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode); MetadataManager.AttachToDependencyGraph(graph); } @@ -169,12 +214,12 @@ namespace ILCompiler return new RuntimeImportMethodNode(method); } - if (CompilationModuleGroup.ContainsMethodBody(method)) + if (CompilationModuleGroup.ContainsMethodBody(method, false)) { return NonExternMethodSymbol(method, false); } - return new ExternMethodSymbolNode(this, method); + return _importedNodeProvider.ImportedMethodCodeNode(this, method, false); } protected override IMethodNode CreateUnboxingStubNode(MethodDesc method) @@ -184,17 +229,17 @@ namespace ILCompiler // Unboxing stubs to canonical instance methods need a special unboxing instantiating stub that unboxes // 'this' and also provides an instantiation argument (we do a calling convention conversion). // The unboxing instantiating stub is emitted by UTC. - if (CompilationModuleGroup.ContainsMethodBody(method)) + if (CompilationModuleGroup.ContainsMethodBody(method, true)) { return NonExternMethodSymbol(method, true); } - return new ExternMethodSymbolNode(this, method, true); + return _importedNodeProvider.ImportedMethodCodeNode(this, method, true); } else { // Otherwise we just unbox 'this' and don't touch anything else. - return new UnboxingStubNode(method); + return new UnboxingStubNode(method, Target); } } @@ -211,7 +256,7 @@ namespace ILCompiler CompilationUnitPrefix + "__ThreadStaticGCDescStart", CompilationUnitPrefix + "__ThreadStaticGCDescEnd"); - public ArrayOfEmbeddedDataNode ThreadStaticsOffsetRegion = new ArrayOfEmbeddedDataNode( + public ArrayOfEmbeddedDataNode ThreadStaticsOffsetRegion = new ArrayOfEmbeddedDataNode( CompilationUnitPrefix + "__ThreadStaticOffsetRegionStart", CompilationUnitPrefix + "__ThreadStaticOffsetRegionEnd", null); @@ -225,6 +270,8 @@ namespace ILCompiler public ExternSymbolNode TlsStart; public ExternSymbolNode TlsEnd; + public LoopHijackFlagNode LoopHijackFlag; + protected override ISymbolDefinitionNode CreateThreadStaticsNode(MetadataType type) { return new UtcThreadStaticsNode(type); @@ -258,38 +305,27 @@ namespace ILCompiler } } - private NodeCache _threadStaticsOffset; + private NodeCache _threadStaticsOffset; - public ISymbolNode TypeThreadStaticsOffsetSymbol(MetadataType type) + public ISortableSymbolNode TypeThreadStaticsOffsetSymbol(MetadataType type) { return _threadStaticsOffset.GetOrAdd(type); } private NodeCache _importedThreadStaticsIndices; - public ISymbolNode TypeThreadStaticsIndexSymbol(TypeDesc type) + public ISortableSymbolNode TypeThreadStaticsIndexSymbol(MetadataType type) { - if (CompilationModuleGroup.ContainsType(type)) + if (CompilationModuleGroup.ContainsType(type) && !CompilationModuleGroup.ShouldReferenceThroughImportTable(type)) { return ThreadStaticsIndex; } - else if (CompilationModuleGroup.ShouldReferenceThroughImportTable(type)) - { - return _importedThreadStaticsIndices.GetOrAdd((MetadataType)type); - } else { - return ExternSymbol(ThreadStaticsIndexNode.GetMangledName((NameMangler as UTCNameMangler).GetImportedTlsIndexPrefix())); + return _importedNodeProvider.ImportedThreadStaticIndexNode(this, type); } } - private NodeCache _hostedGenericDictionaryLayouts; - - public override DictionaryLayoutNode GenericDictionaryLayout(TypeSystemEntity methodOrType) - { - return _hostedGenericDictionaryLayouts.GetOrAdd(methodOrType); - } - private NodeCache _nonExternMethodSymbols; public NonExternMethodSymbolNode NonExternMethodSymbol(MethodDesc method, bool isUnboxingStub) @@ -303,5 +339,25 @@ namespace ILCompiler { return _standaloneGCStaticDescs.GetOrAdd(staticDesc); } + + public BlobNode FieldRvaDataBlob(FieldDesc field) + { + // Use the typical field definition in case this is an instantiated generic type + field = field.GetTypicalFieldDefinition(); + return ReadOnlyDataBlob(NameMangler.GetMangledFieldName(field), ((EcmaField)field).GetFieldRvaData(), Target.PointerSize); + } + + public class UtcDictionaryLayoutProvider : DictionaryLayoutProvider + { + public override DictionaryLayoutNode GetLayout(TypeSystemEntity methodOrType) + { + return new UtcDictionaryLayoutNode(methodOrType); + } + } + + public ISymbolNode LoopHijackFlagSymbol() + { + return LoopHijackFlag; + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcThreadStaticsNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcThreadStaticsNode.cs index 895084104b..3aeadde412 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcThreadStaticsNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcThreadStaticsNode.cs @@ -31,7 +31,7 @@ namespace ILCompiler.DependencyAnalysis return nameMangler.NodeMangler.ThreadStatics(type); } - public virtual bool IsExported(NodeFactory factory) => factory.CompilationModuleGroup.ExportsType(Type); + public virtual ExportForm GetExportForm(NodeFactory factory) => factory.CompilationModuleGroup.GetExportTypeForm(Type); protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { @@ -42,6 +42,8 @@ namespace ILCompiler.DependencyAnalysis dependencyList.Add(factory.EagerCctorIndirection(_type.GetStaticConstructor()), "Eager .cctor"); } + dependencyList.Add(((UtcNodeFactory)factory).TypeThreadStaticGCDescNode(_type), "GC Desc"); + EETypeNode.AddDependenciesForStaticsNode(factory, _type, ref dependencyList); return dependencyList; } @@ -58,5 +60,7 @@ namespace ILCompiler.DependencyAnalysis builder.AddSymbol(this); return builder.ToObjectData(); } + + protected internal override int ClassCode => -1421136129; } -} \ No newline at end of file +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VTableSliceNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VTableSliceNode.cs index bdb4d5cfda..956afc8794 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VTableSliceNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VTableSliceNode.cs @@ -21,6 +21,9 @@ namespace ILCompiler.DependencyAnalysis public VTableSliceNode(TypeDesc type) { + Debug.Assert(!type.IsArray, "Wanted to call GetClosestDefType?"); + Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Any) || + type.ConvertToCanonForm(CanonicalFormKind.Specific) == type); _type = type; } @@ -29,10 +32,31 @@ namespace ILCompiler.DependencyAnalysis get; } + public TypeDesc Type => _type; + + /// + /// Gets a value indicating whether the slots are assigned at the beginning of the compilation. + /// + public abstract bool HasFixedSlots + { + get; + } + protected override string GetName(NodeFactory factory) => $"__vtable_{factory.NameMangler.GetMangledTypeName(_type).ToString()}"; public override bool StaticDependenciesAreComputed => true; + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + DefType baseType = _type.NormalizedBaseType(); + if (baseType != null) + { + return new[] { new DependencyListEntry(factory.VTable(baseType), "Base type VTable") }; + } + + return null; + } + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; @@ -40,9 +64,9 @@ namespace ILCompiler.DependencyAnalysis public override bool HasDynamicDependencies => false; public override bool HasConditionalStaticDependencies => false; - protected IEnumerable GetAllVirtualMethods() + protected static IEnumerable GetAllVirtualMethods(TypeDesc type) { - foreach (MethodDesc method in _type.GetAllMethods()) + foreach (MethodDesc method in type.GetAllMethods()) { if (method.IsVirtual) yield return method; @@ -50,24 +74,56 @@ namespace ILCompiler.DependencyAnalysis } } + /// + /// Represents a VTable slice with fixed slots whose assignment was determined at the time the slice was allocated. + /// + internal class PrecomputedVTableSliceNode : VTableSliceNode + { + private readonly IReadOnlyList _slots; + + public PrecomputedVTableSliceNode(TypeDesc type, IReadOnlyList slots) + : base(type) + { + _slots = slots; + } + + public override IReadOnlyList Slots + { + get + { + return _slots; + } + } + + public override bool HasFixedSlots + { + get + { + return true; + } + } + } + /// /// Represents a VTable slice for a complete type - a type with all virtual method slots generated, /// irrespective of whether they are used. /// - internal sealed class EagerlyBuiltVTableSliceNode : VTableSliceNode + internal sealed class EagerlyBuiltVTableSliceNode : PrecomputedVTableSliceNode { - private MethodDesc[] _slots; - public EagerlyBuiltVTableSliceNode(TypeDesc type) - : base(type) + : base(type, ComputeSlots(type)) + { + } + + private static IReadOnlyList ComputeSlots(TypeDesc type) { var slots = new ArrayBuilder(); bool isObjectType = type.IsObject; - DefType defType = _type.GetClosestDefType(); + DefType defType = type.GetClosestDefType(); - IEnumerable allSlots = _type.IsInterface ? - GetAllVirtualMethods() : defType.EnumAllVirtualSlots(); + IEnumerable allSlots = type.IsInterface ? + GetAllVirtualMethods(type) : defType.EnumAllVirtualSlots(); foreach (var method in allSlots) { @@ -86,28 +142,7 @@ namespace ILCompiler.DependencyAnalysis slots.Add(method); } - _slots = slots.ToArray(); - } - - public override IReadOnlyList Slots - { - get - { - return _slots; - } - } - - public override IEnumerable GetStaticDependencies(NodeFactory factory) - { - if (_type.HasBaseType) - { - return new DependencyListEntry[] - { - new DependencyListEntry(factory.VTable(_type.BaseType), "Base type VTable") - }; - } - - return null; + return slots.ToArray(); } } @@ -153,6 +188,14 @@ namespace ILCompiler.DependencyAnalysis } } + public override bool HasFixedSlots + { + get + { + return false; + } + } + public void AddEntry(NodeFactory factory, MethodDesc virtualMethod) { // GVMs are not emitted in the type's vtable. @@ -163,16 +206,6 @@ namespace ILCompiler.DependencyAnalysis _usedMethods.Add(virtualMethod); } - public override IEnumerable GetStaticDependencies(NodeFactory factory) - { - if (_type.HasBaseType) - { - return new[] { new DependencyListEntry(factory.VTable(_type.BaseType), "Base type VTable") }; - } - - return null; - } - public override bool HasConditionalStaticDependencies { get @@ -188,7 +221,7 @@ namespace ILCompiler.DependencyAnalysis DefType defType = _type.GetClosestDefType(); IEnumerable allSlots = _type.IsInterface ? - GetAllVirtualMethods() : defType.EnumAllVirtualSlots(); + GetAllVirtualMethods(_type) : defType.EnumAllVirtualSlots(); foreach (var method in allSlots) { diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs index 66075291e1..9f29f53ce2 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs @@ -27,8 +27,12 @@ namespace ILCompiler.DependencyAnalysis public VirtualMethodUseNode(MethodDesc decl) { + Debug.Assert(!decl.IsRuntimeDeterminedExactMethod); Debug.Assert(decl.IsVirtual); + Debug.Assert(!decl.IsCanonicalMethod(CanonicalFormKind.Any) || + decl.GetCanonMethodTarget(CanonicalFormKind.Specific) == decl); + // Virtual method use always represents the slot defining method of the virtual. // Places that might see virtual methods being used through an override need to normalize // to the slot defining method. @@ -67,6 +71,8 @@ namespace ILCompiler.DependencyAnalysis dependencies.Add(new DependencyListEntry(factory.VTable(_decl.OwningType), "VTable of a VirtualMethodUse")); + factory.MetadataManager.GetDependenciesDueToVirtualMethodReflectability(ref dependencies, factory, _decl); + return dependencies; } @@ -79,7 +85,7 @@ namespace ILCompiler.DependencyAnalysis DefType universalCanonicalOwningType = (DefType)_decl.OwningType.ConvertToCanonForm(CanonicalFormKind.Universal); Debug.Assert(universalCanonicalOwningType.IsCanonicalSubtype(CanonicalFormKind.Universal)); - if (!factory.CompilationModuleGroup.ShouldProduceFullVTable(universalCanonicalOwningType)) + if (!factory.VTable(universalCanonicalOwningType).HasFixedSlots) { // This code ensures that in cases where we don't structurally force all universal canonical instantiations // to have full vtables, that we ensure that all vtables are equivalently shaped between universal and non-universal types diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugILImagesSection.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugILImagesSection.cs new file mode 100644 index 0000000000..5ac16bbb38 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugILImagesSection.cs @@ -0,0 +1,100 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; +using System.IO; +using System.Text; + +using Internal.Text; + +namespace ILCompiler.DependencyAnalysis +{ + internal class WindowsDebugILImagesSection : ObjectNode, ISymbolDefinitionNode + { + private MergedAssemblyRecords _mergedAssemblies; + + public WindowsDebugILImagesSection(MergedAssemblyRecords mergedAssemblies) + { + _mergedAssemblies = mergedAssemblies; + } + + private ObjectNodeSection _section = new ObjectNodeSection(".ilimges", SectionType.ReadOnly); + public override ObjectNodeSection Section => _section; + + public override bool IsShareable => false; + + public override bool StaticDependenciesAreComputed => true; + + public int Offset => 0; + + protected internal override int ClassCode => 2051656903; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(GetName(null)); + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + MemoryStream memoryStream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(memoryStream, Encoding.Unicode, true); + writer.Write(1); // magic format version number + writer.Write(_mergedAssemblies.MergedAssemblies.Count); // number of il images that will follow + + int totalSizeOfActualMergedAssemblies = 0; + checked + { + const int ILIMAGES_HEADER_ELEMENTS = 2; + const int ILIMAGES_PERMODULE_ELEMENTS = 3; + int endOfILAssemblyListHeader = ((_mergedAssemblies.MergedAssemblies.Count * ILIMAGES_PERMODULE_ELEMENTS) + ILIMAGES_HEADER_ELEMENTS) * sizeof(int); + + int offsetOfNextAssembly = endOfILAssemblyListHeader; + + foreach (MergedAssemblyRecord mergedAssembly in _mergedAssemblies.MergedAssemblies) + { + int assemblyFileLen = mergedAssembly.Assembly.PEReader.GetEntireImage().Length; + writer.Write(mergedAssembly.AssemblyIndex); + writer.Write(offsetOfNextAssembly); + writer.Write(assemblyFileLen); + offsetOfNextAssembly += assemblyFileLen; + totalSizeOfActualMergedAssemblies += assemblyFileLen; + } + + writer.Flush(); + writer.Dispose(); + + byte[] mergedAssemblyHeader = memoryStream.ToArray(); + Debug.Assert(mergedAssemblyHeader.Length == endOfILAssemblyListHeader); + + byte[] mergedAssemblyBlob = new byte[mergedAssemblyHeader.Length + totalSizeOfActualMergedAssemblies]; + Array.Copy(mergedAssemblyHeader, mergedAssemblyBlob, mergedAssemblyHeader.Length); + + offsetOfNextAssembly = endOfILAssemblyListHeader; + foreach (MergedAssemblyRecord mergedAssembly in _mergedAssemblies.MergedAssemblies) + { + var memoryBlock = mergedAssembly.Assembly.PEReader.GetEntireImage(); + int assemblyFileLen = memoryBlock.Length; + unsafe + { + Marshal.Copy(new IntPtr(memoryBlock.Pointer), mergedAssemblyBlob, offsetOfNextAssembly, assemblyFileLen); + } + offsetOfNextAssembly += assemblyFileLen; + } + + return new ObjectData(mergedAssemblyBlob, Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + } + } + + protected override string GetName(NodeFactory context) + { + return "___DebugILImagesSection"; + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugManagedNativeDictionaryInfoSection.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugManagedNativeDictionaryInfoSection.cs new file mode 100644 index 0000000000..5d3ebd6e7b --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugManagedNativeDictionaryInfoSection.cs @@ -0,0 +1,107 @@ +// 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.Collections.Generic; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.TypeSystem.TypesDebugInfo; + +namespace ILCompiler.DependencyAnalysis +{ + internal class WindowsDebugManagedNativeDictionaryInfoSection : ObjectNode, ISymbolDefinitionNode + { + public WindowsDebugManagedNativeDictionaryInfoSection() + { + } + + private ObjectNodeSection _section = new ObjectNodeSection(".dbgmanagednativedictionaryinfo", SectionType.ReadOnly); + public override ObjectNodeSection Section => _section; + + public override bool IsShareable => false; + + public override bool StaticDependenciesAreComputed => true; + + public int Offset => 0; + + protected internal override int ClassCode => 1502860768; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(GetName(null)); + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + ObjectDataBuilder objDataBuilder = new ObjectDataBuilder(factory, relocsOnly); + + // Emit number of dictionaries in table + objDataBuilder.AddSymbol(this); + IReadOnlyCollection dictionariesEmitted = factory.MetadataManager.GetCompiledGenericDictionaries(); + objDataBuilder.EmitInt(dictionariesEmitted.Count); + DebugInfoBlob signatureData = new DebugInfoBlob(); + + BlobBuilder signatureBlobBuilder = new BlobBuilder(); + BlobBuilder signatureLenBuilder = new BlobBuilder(); + ManagedBinaryEmitter pseudoAssembly = factory.WindowsDebugData.DebugPseudoAssemblySection.PseudoAssembly; + + foreach (GenericDictionaryNode dictionary in dictionariesEmitted) + { + objDataBuilder.EmitReloc(dictionary, RelocType.IMAGE_REL_BASED_ADDR32NB); + objDataBuilder.EmitUInt(signatureData.Size()); + + signatureBlobBuilder.Clear(); + + int typeDictLen = dictionary.TypeInstantiation.IsNull ? 0 : dictionary.TypeInstantiation.Length; + int methodDictLen = dictionary.MethodInstantiation.IsNull ? 0 : dictionary.MethodInstantiation.Length; + signatureBlobBuilder.WriteCompressedInteger(typeDictLen + methodDictLen); + + if (typeDictLen != 0) + { + foreach (TypeDesc type in dictionary.TypeInstantiation) + { + pseudoAssembly.EncodeSignatureForType(type, signatureBlobBuilder); + } + } + + if (methodDictLen != 0) + { + foreach (TypeDesc type in dictionary.MethodInstantiation) + { + pseudoAssembly.EncodeSignatureForType(type, signatureBlobBuilder); + } + } + + int blobSize = signatureBlobBuilder.Count; + + signatureLenBuilder.Clear(); + signatureLenBuilder.WriteCompressedInteger(blobSize); + + // Prepend the signature data with a length + signatureData.WriteBuffer(signatureLenBuilder); + // And then attach the actual signature data + signatureData.WriteBuffer(signatureBlobBuilder); + } + + // Attach signature information to end after all of the rva/offset pairs + objDataBuilder.EmitBytes(signatureData.ToArray()); + + return objDataBuilder.ToObjectData(); + } + + protected override string GetName(NodeFactory context) + { + return "___DebugManagedNativeDictionaryInfoSection"; + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugMergedAssemblyRecordsSection.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugMergedAssemblyRecordsSection.cs new file mode 100644 index 0000000000..49f75a1006 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugMergedAssemblyRecordsSection.cs @@ -0,0 +1,62 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; +using System.IO; +using System.Text; + +using Internal.Text; +using Internal.TypeSystem.TypesDebugInfo; + +namespace ILCompiler.DependencyAnalysis +{ + internal class WindowsDebugMergedAssembliesSection : ObjectNode, ISymbolDefinitionNode + { + private MergedAssemblyRecords _mergedAssemblies; + + public WindowsDebugMergedAssembliesSection(MergedAssemblyRecords mergedAssemblies) + { + _mergedAssemblies = mergedAssemblies; + } + + private ObjectNodeSection _section = new ObjectNodeSection(".dbgmergedassemblyrecords", SectionType.ReadOnly); + public override ObjectNodeSection Section => _section; + + public override bool IsShareable => false; + + public override bool StaticDependenciesAreComputed => true; + + public int Offset => 0; + + protected internal override int ClassCode => -1250136545; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(GetName(null)); + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + DebugInfoBlob debugBlob = new DebugInfoBlob(); + foreach (MergedAssemblyRecord record in _mergedAssemblies.MergedAssemblies) + record.Encode(debugBlob); + + byte [] _pdbBlob = debugBlob.ToArray(); + Debug.Assert(_pdbBlob.Length > 0); + + return new ObjectData(_pdbBlob, Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + } + + protected override string GetName(NodeFactory context) + { + return "___DebugMergedAssemblyRecordsSection"; + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugMethodInfoSection.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugMethodInfoSection.cs new file mode 100644 index 0000000000..7d4396fc72 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugMethodInfoSection.cs @@ -0,0 +1,155 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using Internal.TypeSystem.Ecma; +using Internal.Text; +using Internal.TypeSystem; +using Internal.TypeSystem.TypesDebugInfo; + +namespace ILCompiler.DependencyAnalysis +{ + internal class WindowsDebugMethodInfoSection : ObjectNode, ISymbolDefinitionNode + { + private MergedAssemblyRecords _mergedAssemblies; + + public WindowsDebugMethodInfoSection(MergedAssemblyRecords mergedAssemblies) + { + _mergedAssemblies = mergedAssemblies; + } + + private ObjectNodeSection _section = new ObjectNodeSection(".mdinfo", SectionType.ReadOnly); + public override ObjectNodeSection Section => _section; + + public override bool IsShareable => false; + + public override bool StaticDependenciesAreComputed => true; + + public int Offset => 0; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(GetName(null)); + } + + private uint AdjustIndex(uint assemblyIndex, uint corLibIndex) + { + if (assemblyIndex == 0x7FFFFFFF) + return corLibIndex; + if (assemblyIndex < corLibIndex) + return assemblyIndex; + return assemblyIndex + 1; + } + + // + // returns the DebugInfoBlob containing the method token to virtual method slot mapping + + // .mdinfo format + // offset 0, 4 bytes: version number + // offset 4, 4 bytes: count of assemblies + // + // for each assembly + // offset 0, 4 bytes: count of methods with a virtual method slot + // + // for each method + // offset 0, 4 bytes: method def token (as they are in the input assembly) + // offset 4, 2 bytes: length of the per method information [ currently always 4 ] + // offset 6, 4 bytes: virtual slot number + // methods are sorted by their method def token + + internal DebugInfoBlob GetDebugMethodInfoMap(NodeFactory factory) + { + Dictionary originalAssemblyOrder = new Dictionary(); + List> moduleMethods = new List>(); + + // re-construct orginal assembly input order + foreach (MergedAssemblyRecord mergedAssembly in _mergedAssemblies.MergedAssemblies) + { + uint assemblyIndex = AdjustIndex(mergedAssembly.AssemblyIndex, _mergedAssemblies.CorLibIndex); + originalAssemblyOrder.Add(mergedAssembly.Assembly, assemblyIndex); + moduleMethods.Add(new SortedDictionary()); + } + + foreach (TypeDesc type in factory.MetadataManager.GetTypesWithConstructedEETypes()) + { + // skip if sealed + if (type.IsSealed()) + continue; + + // no generic support yet + if (type is EcmaType) + { + EcmaType ecmaType = (EcmaType)type; + EcmaAssembly ecmaAssembly = (EcmaAssembly)ecmaType.EcmaModule; + int assemblyIndex = (int)originalAssemblyOrder[ecmaAssembly]; + SortedDictionary methodList = moduleMethods[assemblyIndex]; + foreach (MethodDesc md in type.GetAllMethods()) + { + // skip non-virtual and final methods + if (!md.IsVirtual || md.IsFinal) + continue; + // skip generic + if (md.HasInstantiation) + continue; + // method token. + EntityHandle methodHandle = ((EcmaMethod)md).Handle; + uint methodToken = (uint)MetadataTokens.GetToken(methodHandle); + // find virtual method slot. + MethodDesc declaringMethodForSlot = + MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(md.GetTypicalMethodDefinition()); + int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, declaringMethodForSlot); + if (slot != -1 && !methodList.ContainsKey(methodToken)) + methodList.Add(methodToken, slot); + } + } + } + return ConvertToDebugInfoBlob(moduleMethods); + } + + private DebugInfoBlob ConvertToDebugInfoBlob(List> assemblyMethods) + { + DebugInfoBlob debugInfoBlob = new DebugInfoBlob(); + // version + debugInfoBlob.WriteDWORD(0); + // number of assemblies + debugInfoBlob.WriteDWORD((uint)assemblyMethods.Count); + foreach (var methods in assemblyMethods) + { + debugInfoBlob.WriteDWORD((uint)methods.Count); + foreach (var method in methods) + { + // method token + debugInfoBlob.WriteDWORD(method.Key); + // method info length , now it's 4 + debugInfoBlob.WriteWORD(4); + // method slot + debugInfoBlob.WriteDWORD((uint)method.Value); + } + } + return debugInfoBlob; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + { + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + } + DebugInfoBlob debugData = GetDebugMethodInfoMap(factory); + return new ObjectData(debugData.ToArray(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + } + + protected override string GetName(NodeFactory context) + { + return "___DebugMethodInfoSection"; + } + + protected internal override int ClassCode => 513099721; + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugMethodMapSection.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugMethodMapSection.cs new file mode 100644 index 0000000000..c351d54e87 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugMethodMapSection.cs @@ -0,0 +1,195 @@ +// 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.Collections.Generic; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.TypeSystem.TypesDebugInfo; + +namespace ILCompiler.DependencyAnalysis +{ + internal class WindowsDebugMethodSignatureMapSection : ObjectNode, ISymbolDefinitionNode + { + public WindowsDebugMethodSignatureMapSection() + { + } + + private ObjectNodeSection _section = new ObjectNodeSection(".dbgmethodsignaturemap", SectionType.ReadOnly); + public override ObjectNodeSection Section => _section; + + public override bool IsShareable => false; + + public override bool StaticDependenciesAreComputed => true; + + public int Offset => 0; + + protected internal override int ClassCode => -2063194124; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(GetName(null)); + } + + struct EmittedMethodWithILToken : IComparable + { + public EmittedMethodWithILToken(IMethodBodyNode emittedMethod, uint ilTokenRid) + { + EmittedMethod = emittedMethod; + IlTokenRid = ilTokenRid; + } + + public IMethodBodyNode EmittedMethod; + public uint IlTokenRid; + + int IComparable.CompareTo(EmittedMethodWithILToken other) + { + if (IlTokenRid == other.IlTokenRid) + return 0; + if (IlTokenRid < other.IlTokenRid) + return -1; + return 1; + } + } + + // + // returns the DEBUG_S_FUNC_MDTOKEN_MAP subsection as a byte array + // DEBUG_S_FUNC_MDTOKEN_MAP subsection contains method RVA to mdToken mapping + // + // contents of subsection: + // offset 0, 4 bytes: count of entries in the map + // offset 4, 8*N bytes: 4 byte RVA + 4 byte 'offset' relative to the start of 'method data' + // offset 4+8*N, * bytes: all method data packed sequentially with no padding. method data consists of + // 1 byte 'count' of generic parameters, 3 bytes of method's rid and 'count' + // variable sized ECMA formatted TypeSpec signatures for each generic parameter + // + // Compiler places the CTLToken (for a method) or the lexical funclet order (if a method has 1 or more funclets), + // which binder uses to compute the RVA. + // + // all entries are sorted by 'offset' field. + // + // 'offset' optimization: if the method has no generic parameters, we don't need to pass in a signature + // and can encode the mdToken of method in 'offset' + // We do this by setting the high bit of 'offset' and then storing rid part of + // token in last 3 bytes of 'offset' + // + internal DebugInfoBlob GetDebugMethodRVAToTokenMap(ManagedBinaryEmitter pseudoAssembly, IEnumerable emittedMethods, out List debugRelocations) + { + DebugInfoBlob methodRVAToTokenMap = new DebugInfoBlob(); + DebugInfoBlob methodDataBlob = new DebugInfoBlob(); + debugRelocations = new List(); + BlobBuilder blobBuilder = new BlobBuilder(); + + uint entryCount = 0; + methodRVAToTokenMap.WriteDWORD(0); // Placeholder for count of entries in map. Will be udpated later. + + List tokenInOffsetEntries = new List(); + + foreach (IMethodBodyNode emitted in emittedMethods) + { + NonExternMethodSymbolNode methodNode = emitted as NonExternMethodSymbolNode; + if (methodNode != null && !methodNode.HasCompiledBody) + { + continue; + } + + if (!(emitted.Method.GetTypicalMethodDefinition() is Internal.TypeSystem.Ecma.EcmaMethod)) + { + continue; + } + + EntityHandle methodHandle = pseudoAssembly.EmitMetadataHandleForTypeSystemEntity(emitted.Method.GetTypicalMethodDefinition()); + Debug.Assert(methodHandle.Kind == HandleKind.MemberReference); + uint methodToken = (uint)MetadataTokens.GetToken(methodHandle); + uint methodTokenRid = methodToken & 0xFFFFFF; + + if (!(emitted.Method.HasInstantiation || emitted.Method.OwningType.HasInstantiation)) + { + tokenInOffsetEntries.Add(new EmittedMethodWithILToken(emitted, methodTokenRid)); + continue; + } + + uint cGenericArguments = checked((uint)emitted.Method.Instantiation.Length + (uint)emitted.Method.OwningType.Instantiation.Length); + + // Debugger format does not allow the debugging of methods that have more than 255 generic parameters (spread between the type and method instantiation) + if (cGenericArguments > 0xFF) + continue; + + blobBuilder.Clear(); + + // write the signature for each generic parameter of class + foreach (TypeDesc instantiationType in emitted.Method.OwningType.Instantiation) + pseudoAssembly.EncodeSignatureForType(instantiationType, blobBuilder); + + // write the signature for each generic parameter of the method + foreach (TypeDesc instantiationType in emitted.Method.Instantiation) + pseudoAssembly.EncodeSignatureForType(instantiationType, blobBuilder); + + Add_DEBUG_S_FUNC_MDTOKEN_MAP_Entry(methodRVAToTokenMap, debugRelocations, emitted, methodDataBlob.Size(), ref entryCount); + + methodDataBlob.WriteDWORD(cGenericArguments << 24 | methodTokenRid); + methodDataBlob.WriteBuffer(blobBuilder); + } + + // sort tokenInOffsetEntries based on tokenInOffset + tokenInOffsetEntries.Sort(); + + foreach (EmittedMethodWithILToken emitted in tokenInOffsetEntries) + { + Add_DEBUG_S_FUNC_MDTOKEN_MAP_Entry(methodRVAToTokenMap, debugRelocations, emitted.EmittedMethod, emitted.IlTokenRid | 0x80000000, ref entryCount); + } + + methodRVAToTokenMap.SetDWORDAtBlobIndex(0, entryCount); // // Update placeholder for count of entries in map + methodRVAToTokenMap.WriteBuffer(methodDataBlob); + + return methodRVAToTokenMap; + } + + private void Add_DEBUG_S_FUNC_MDTOKEN_MAP_Entry(DebugInfoBlob methodRVAToTokenMap, List debugRelocations, IMethodBodyNode method, uint methodDataOrOffsetToMethodData, ref uint entryCount) + { + debugRelocations.Add(new Relocation(RelocType.IMAGE_REL_BASED_ADDR32NB, checked((int)methodRVAToTokenMap.Size()), method)); + methodRVAToTokenMap.WriteDWORD(0); + methodRVAToTokenMap.WriteDWORD(methodDataOrOffsetToMethodData); + entryCount++; + + IMethodBodyNodeWithFuncletSymbols funcletSymbolsNode = method as IMethodBodyNodeWithFuncletSymbols; + + if (funcletSymbolsNode != null) + { + foreach (ISymbolNode funclet in funcletSymbolsNode.FuncletSymbols) + { + debugRelocations.Add(new Relocation(RelocType.IMAGE_REL_BASED_ADDR32NB, checked((int)methodRVAToTokenMap.Size()), funclet)); + methodRVAToTokenMap.WriteDWORD(0); + methodRVAToTokenMap.WriteDWORD(methodDataOrOffsetToMethodData); + entryCount++; + } + } + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + if (factory.WindowsDebugData.DebugTypeRecordsSection != null) + factory.WindowsDebugData.DebugTypeRecordsSection.Neuter(); + + List relocations = new List(); + DebugInfoBlob debugData = GetDebugMethodRVAToTokenMap(factory.WindowsDebugData.DebugPseudoAssemblySection.PseudoAssembly, factory.MetadataManager.GetCompiledMethodBodies(), out relocations); + + return new ObjectData(debugData.ToArray(), relocations.ToArray(), 1, new ISymbolDefinitionNode[] { this }); + } + + protected override string GetName(NodeFactory context) + { + return "___DebugMethodSignatureMapSection"; + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugNeedTypeIndicesStoreNode.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugNeedTypeIndicesStoreNode.cs new file mode 100644 index 0000000000..71435021ca --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugNeedTypeIndicesStoreNode.cs @@ -0,0 +1,70 @@ +// 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.Linq; +using System.Collections.Generic; +using Internal.Text; +using Internal.TypeSystem; +using Internal.TypeSystem.TypesDebugInfo; + +namespace ILCompiler.DependencyAnalysis +{ + public class WindowsDebugNeedTypeIndicesStoreNode : ObjectNode, ISymbolDefinitionNode + { + public override ObjectNodeSection Section => ObjectNodeSection.DataSection; + + public override bool IsShareable => true; + + public override bool StaticDependenciesAreComputed => true; + + public int Offset => 0; + + protected internal override int ClassCode => 1275723356; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(GetName(null)); + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + if (!relocsOnly) + { + UserDefinedTypeDescriptor userDefinedTypeDescriptor = factory.WindowsDebugData.UserDefinedTypeDescriptor; + if (userDefinedTypeDescriptor != null) + { + List typesThatNeedIndices = new List(); + foreach (TypeDesc type in factory.MetadataManager.GetTypesWithEETypes()) + { + if (!type.IsGenericDefinition) + { + typesThatNeedIndices.Add(type); + } + } + + typesThatNeedIndices.Sort(new TypeSystemComparer().Compare); + + foreach (TypeDesc type in typesThatNeedIndices) + { + // Force creation of type descriptors for _ALL_ EETypes + userDefinedTypeDescriptor.GetVariableTypeIndex(type); + } + } + } + + // There isn't actually any data in this node. Its simply an ObjectNode as that allows this + // function to be executed in a defined time during object emission. This does imply embedding a bit of data + // into the object file, but the linker should be able to strip it out of the final file, and even if its in the final file + // it won't cost significant size. + + return new ObjectData(new byte[1], Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + } + + protected override string GetName(NodeFactory context) + { + return "___DebugNeedTypeIndicesStore"; + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugPseudoAssemblySection.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugPseudoAssemblySection.cs new file mode 100644 index 0000000000..7f393b2d40 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugPseudoAssemblySection.cs @@ -0,0 +1,56 @@ +// 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 Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public class WindowsDebugPseudoAssemblySection : ObjectNode, ISymbolDefinitionNode + { + private ManagedBinaryEmitter _pseudoAssembly; + + public WindowsDebugPseudoAssemblySection(TypeSystemContext typeSystemContext) + { + _pseudoAssembly = new ManagedBinaryEmitter(typeSystemContext, "PseudoAssembly"); + } + + private ObjectNodeSection _section = new ObjectNodeSection(".psdo-il", SectionType.ReadOnly); + public override ObjectNodeSection Section => _section; + + public ManagedBinaryEmitter PseudoAssembly => _pseudoAssembly; + public override bool IsShareable => false; + + public override bool StaticDependenciesAreComputed => true; + + public int Offset => 0; + + protected internal override int ClassCode => 920778380; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(GetName(null)); + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + MemoryStream memoryStream = new MemoryStream(1000000); + _pseudoAssembly.EmitToStream(memoryStream); + _pseudoAssembly = null; + return new ObjectData(memoryStream.ToArray(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + } + + protected override string GetName(NodeFactory context) + { + return "___DebugPseudoAssemblySection"; + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugTypeRecordsSection.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugTypeRecordsSection.cs new file mode 100644 index 0000000000..660559ccb4 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugTypeRecordsSection.cs @@ -0,0 +1,106 @@ +// 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.Collections.Concurrent; +using System.Diagnostics; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.TypeSystem.TypesDebugInfo; + +namespace ILCompiler.DependencyAnalysis +{ + internal class WindowsDebugTypeRecordsSection : ObjectNode, ISymbolDefinitionNode, ITypesDebugInfoWriter + { + DebugInfoWriter _dbgInfo; // Pointer to DebugInfoWriter used to write data + DebugInfoWriter _dbgInfoWriter; // Pointer to DebugInfoWriter used to generate new entries + NodeFactory _nodeFactory; + + public WindowsDebugTypeRecordsSection(DebugInfoWriter dbgInfo, NodeFactory factory) + { + _dbgInfoWriter = _dbgInfo = dbgInfo; + _nodeFactory = factory; + } + + private ObjectNodeSection _section = new ObjectNodeSection(".debug$T", SectionType.ReadOnly); + public override ObjectNodeSection Section => _section; + + public override bool IsShareable => false; + + public override bool StaticDependenciesAreComputed => true; + + public int Offset => 0; + + protected internal override int ClassCode => -2081034825; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(GetName(null)); + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + byte[] typeRecords = _dbgInfo.GetRawBlob().ToArray(); + _dbgInfo = null; // Neuter the section so that it cannot grow any larger + Neuter(); // Neuter the writer so that nothing else can attempt to add new types + + return new ObjectData(typeRecords, Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + } + + protected override string GetName(NodeFactory context) + { + return "___DebugTypeRecordsSection"; + } + + public void Neuter() + { + _dbgInfoWriter = null; + } + + uint ITypesDebugInfoWriter.GetEnumTypeIndex(EnumTypeDescriptor enumTypeDescriptor, EnumRecordTypeDescriptor[] typeRecords) + { + return _dbgInfoWriter.GetEnumTypeIndex(enumTypeDescriptor, typeRecords); + } + + uint ITypesDebugInfoWriter.GetClassTypeIndex(ClassTypeDescriptor classTypeDescriptor) + { + return _dbgInfoWriter.GetClassTypeIndex(classTypeDescriptor); + } + + uint ITypesDebugInfoWriter.GetCompleteClassTypeIndex(ClassTypeDescriptor classTypeDescriptor, ClassFieldsTypeDescriptor classFieldsTypeDescriptior, DataFieldDescriptor[] fields) + { + return _dbgInfoWriter.GetCompleteClassTypeIndex(classTypeDescriptor, classFieldsTypeDescriptior, fields); + } + + uint ITypesDebugInfoWriter.GetArrayTypeIndex(ClassTypeDescriptor classDescriptor, ArrayTypeDescriptor arrayTypeDescriptor) + { + return _dbgInfoWriter.GetArrayTypeIndex(classDescriptor, arrayTypeDescriptor, _nodeFactory.Target.PointerSize); + } + + uint ITypesDebugInfoWriter.GetPointerTypeIndex(PointerTypeDescriptor pointerDescriptor) + { + return _dbgInfoWriter.GetPointerTypeIndex(pointerDescriptor); + } + + uint ITypesDebugInfoWriter.GetMemberFunctionTypeIndex(MemberFunctionTypeDescriptor memberDescriptor, uint[] argumentTypes) + { + return _dbgInfoWriter.GetMemberFunctionTypeIndex(memberDescriptor, argumentTypes); + } + + uint ITypesDebugInfoWriter.GetMemberFunctionId(MemberFunctionIdTypeDescriptor memberIdDescriptor) + { + return _dbgInfoWriter.GetMemberFunctionId(memberIdDescriptor); + } + + string ITypesDebugInfoWriter.GetMangledName(TypeDesc type) + { + return _nodeFactory.NameMangler.GetMangledTypeName(type); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugTypeSignatureMapSection.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugTypeSignatureMapSection.cs new file mode 100644 index 0000000000..0ec387d9cf --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugTypeSignatureMapSection.cs @@ -0,0 +1,145 @@ +// 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.Collections.Generic; +using System.IO; +using System.Reflection.Metadata; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.TypeSystem.TypesDebugInfo; + +namespace ILCompiler.DependencyAnalysis +{ + internal class WindowsDebugTypeSignatureMapSection : ObjectNode, ISymbolDefinitionNode + { + UserDefinedTypeDescriptor _userDefinedTypeDescriptor; + + public WindowsDebugTypeSignatureMapSection(UserDefinedTypeDescriptor userDefinedTypeDescriptor) + { + _userDefinedTypeDescriptor = userDefinedTypeDescriptor; + } + + private ObjectNodeSection _section = new ObjectNodeSection(".dbgtypesignaturemap", SectionType.ReadOnly); + public override ObjectNodeSection Section => _section; + + public override bool IsShareable => false; + + public override bool StaticDependenciesAreComputed => true; + + public int Offset => 0; + + protected internal override int ClassCode => 1029840999; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(GetName(null)); + } + + + // returns the DEBUG_S_TYPE_MDTOKEN_MAP subsection as a byte array + // DEBUG_S_TYPE_MDTOKEN_MAP subsection contains type-index to mdToken mapping + // + // contents of subsection: + // offset 0, 4 bytes: count of entries in the map + // offset 4, 8*N bytes: 4 byte type-index + 4 byte 'offset' relative to the start of 'type data' + // offset 4+8*N, * bytes: ECMA formatted type signature packed sequentially with no padding + // + // 'offset' optimization: for type signatures with size<= 4-bytes + // we can store the signature in offset field such that + // offset = (1 << 31) | (sig[0] << 24 | sig[1] << 16 | sig[2] << 8 | sig[3]) + // We chose this bit encoding because sig[0] holds the CorElementType whose + // highest bit is always 0 and the highest bit of offset can be used as a flag + // to indicate that it is not an offset but the signature itself. + // + // all entries are sorted by 'offset' field and so offset-based entries are arranged before other + // (raw-signature) entries (since raw-signature entries are of the form 0x80000000 | signature, and will always be + // numerically bigger than the offset) + // + private DebugInfoBlob GetDebugTypeIndexToTokenMap(ManagedBinaryEmitter pseudoAssembly, ICollection> completeKnownTypes) + { + DebugInfoBlob typeDataBlob = new DebugInfoBlob(); + DebugInfoBlob typeIndexToTokenMapBlob = new DebugInfoBlob(); + List> sigInOffsetEntries = new List>(); + + typeIndexToTokenMapBlob.WriteDWORD(checked((uint)completeKnownTypes.Count)); + BlobBuilder blobBuilder = new BlobBuilder(); + foreach (var entry in completeKnownTypes) + { + uint typeIndex = entry.Value; + blobBuilder.Clear(); + pseudoAssembly.EncodeSignatureForType(entry.Key, blobBuilder); + + // if signature fits in 4-bytes, store it in sigInOffsetEntries + // otherwise store it in the type-data blob + if (blobBuilder.Count <= 4) + { + uint sigInOffset = 0x80000000; + int i = 0; + + // This is a slightly confusing approach, but this is how one iterates through the bytes in a blobBuilder without flattening it to a byte[] + foreach (Blob blob in blobBuilder.GetBlobs()) + { + foreach (byte b in blob.GetBytes()) + { + sigInOffset |= ((uint)b) << (8 * (3 - i)); + i++; + } + } + + // sigInOffsetEntries will be later sorted and appended to typeIndexToTokenMapBlob + sigInOffsetEntries.Add(new KeyValuePair(typeIndex, sigInOffset)); + } + else + { + typeIndexToTokenMapBlob.WriteDWORD(typeIndex); + typeIndexToTokenMapBlob.WriteDWORD(typeDataBlob.Size()); + typeDataBlob.WriteBuffer(blobBuilder); + } + } + + // sort sigInOffsetEntries based on sigInOffset + sigInOffsetEntries.Sort((KeyValuePair left, KeyValuePair right) => + { + if (left.Value < right.Value) + return -1; + if (left.Value == right.Value) + return 0; + return 1; + }); + + // write the sorted sigInOffsetEntries + foreach (KeyValuePair sigInOffsetEntry in sigInOffsetEntries) + { + typeIndexToTokenMapBlob.WriteDWORD(sigInOffsetEntry.Key); + typeIndexToTokenMapBlob.WriteDWORD(sigInOffsetEntry.Value); + } + + // add typeDataBlob to the end of m_typeIndexToTokenMapBlob + typeIndexToTokenMapBlob.WriteBuffer(typeDataBlob.ToArray()); + + return typeIndexToTokenMapBlob; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + if (factory.WindowsDebugData.DebugTypeRecordsSection != null) + factory.WindowsDebugData.DebugTypeRecordsSection.Neuter(); + + DebugInfoBlob debugData = GetDebugTypeIndexToTokenMap(factory.WindowsDebugData.DebugPseudoAssemblySection.PseudoAssembly, factory.WindowsDebugData.UserDefinedTypeDescriptor.CompleteKnownTypes); + + return new ObjectData(debugData.ToArray(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + } + + protected override string GetName(NodeFactory context) + { + return "___DebugTypeSignatureMapSection"; + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyTrackingLevel.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyTrackingLevel.cs index 6a1079e50c..9322b9d302 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyTrackingLevel.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DependencyTrackingLevel.cs @@ -39,7 +39,10 @@ namespace ILCompiler switch (trackingLevel) { case DependencyTrackingLevel.None: - return new DependencyAnalyzer, NodeFactory>(factory, comparer); + if (EventSourceLogStrategy.IsEventSourceEnabled) + return new DependencyAnalyzer, NodeFactory>(factory, comparer); + else + return new DependencyAnalyzer, NodeFactory>(factory, comparer); case DependencyTrackingLevel.First: return new DependencyAnalyzer, NodeFactory>(factory, comparer); diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DevirtualizationManager.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DevirtualizationManager.cs new file mode 100644 index 0000000000..8a6227ee4d --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DevirtualizationManager.cs @@ -0,0 +1,90 @@ +// 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 Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + /// + /// Manages devirtualization behaviors. Devirtualization is the process of converting + /// virtual calls to direct calls in cases where we can compute the result of a virtual + /// lookup at compile time. + /// + public class DevirtualizationManager + { + /// + /// Returns true if cannot be the base class of any other + /// type. + /// + public virtual bool IsEffectivelySealed(TypeDesc type) + { + switch (type.Category) + { + case TypeFlags.Array: + case TypeFlags.SzArray: + case TypeFlags.ByRef: + case TypeFlags.Pointer: + case TypeFlags.FunctionPointer: + return true; + + default: + Debug.Assert(type.IsDefType); + var metadataType = (MetadataType)type; + return metadataType.IsSealed || metadataType.IsModuleType; + } + } + + /// + /// Returns true if cannot be overriden by any other method. + /// + public virtual bool IsEffectivelySealed(MethodDesc method) + { + return method.IsFinal || IsEffectivelySealed(method.OwningType); + } + + /// + /// Attempts to resolve the virtual method into + /// a method on that implements the declaring method. + /// Returns null if this is not possible. + /// + /// + /// Note that if is a value type, the result of the resolution + /// might have to be treated as an unboxing thunk by the caller. + /// + public MethodDesc ResolveVirtualMethod(MethodDesc declMethod, TypeDesc implType) + { + Debug.Assert(declMethod.IsVirtual); + + // Quick check: if decl matches impl, we're done. + if (declMethod.OwningType == implType) + return declMethod; + + // We're operating on virtual methods. This means that if implType is an array, we need + // to get the type that has all the virtual methods provided by the class library. + return ResolveVirtualMethod(declMethod, implType.GetClosestDefType()); + } + + protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType implType) + { + MethodDesc impl; + + if (declMethod.OwningType.IsInterface) + { + impl = implType.ResolveInterfaceMethodTarget(declMethod); + if (impl != null) + { + impl = implType.FindVirtualFunctionTargetMethodOnObjectType(impl); + } + } + else + { + impl = implType.FindVirtualFunctionTargetMethodOnObjectType(declMethod); + } + + return impl; + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/DictionaryLayoutProvider.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/DictionaryLayoutProvider.cs new file mode 100644 index 0000000000..193fffb44f --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/DictionaryLayoutProvider.cs @@ -0,0 +1,29 @@ +// 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 Internal.TypeSystem; +using ILCompiler.DependencyAnalysis; + +namespace ILCompiler +{ + /// + /// Provides generic dictionary layout information for a specific type or method. + /// + public abstract class DictionaryLayoutProvider + { + public abstract DictionaryLayoutNode GetLayout(TypeSystemEntity methodOrType); + } + + /// + /// Provides dictionary layout information that collects data during the compilation to build a dictionary layout + /// for a type or method on demand. + /// + public sealed class LazyDictionaryLayoutProvider : DictionaryLayoutProvider + { + public override DictionaryLayoutNode GetLayout(TypeSystemEntity methodOrType) + { + return new LazilyBuiltDictionaryLayoutNode(methodOrType); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/EmptyInteropStubManager.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/EmptyInteropStubManager.cs new file mode 100644 index 0000000000..084301ebe1 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/EmptyInteropStubManager.cs @@ -0,0 +1,40 @@ +// 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 Internal.TypeSystem; +using ILCompiler.DependencyAnalysis; + +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; + +namespace ILCompiler +{ + /// + /// This class is responsible for managing stub methods for interop + /// + public sealed class EmptyInteropStubManager : InteropStubManager + { + public EmptyInteropStubManager(CompilationModuleGroup compilationModuleGroup, CompilerTypeSystemContext typeSystemContext, InteropStateManager interopStateManager) : + base(compilationModuleGroup, typeSystemContext, interopStateManager) + { + } + + public override void AddDependeciesDueToPInvoke(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + } + + public override void AddInterestingInteropConstructedTypeDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + } + + public override void AddMarshalAPIsGenericDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + } + + public override void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode) + { + } + } +} \ No newline at end of file diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/EmptyMetadataManager.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/EmptyMetadataManager.cs index 082268a5f5..97eed867a3 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/EmptyMetadataManager.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/EmptyMetadataManager.cs @@ -12,10 +12,12 @@ using Debug = System.Diagnostics.Debug; namespace ILCompiler { - class EmptyMetadataManager : MetadataManager + public class EmptyMetadataManager : MetadataManager { - public EmptyMetadataManager(CompilationModuleGroup group, CompilerTypeSystemContext typeSystemContext) - : base(group, typeSystemContext, new FullyBlockedMetadataPolicy()) + public override bool SupportsReflection => false; + + public EmptyMetadataManager(CompilerTypeSystemContext typeSystemContext) + : base(typeSystemContext, new FullyBlockedMetadataPolicy()) { } @@ -39,13 +41,19 @@ namespace ILCompiler return MetadataCategory.None; } - protected override void ComputeMetadata(NodeFactory factory, out byte[] metadataBlob, out List> typeMappings, out List> methodMappings, out List> fieldMappings) + protected override void ComputeMetadata(NodeFactory factory, + out byte[] metadataBlob, + out List> typeMappings, + out List> methodMappings, + out List> fieldMappings, + out List> stackTraceMapping) { metadataBlob = Array.Empty(); typeMappings = new List>(); methodMappings = new List>(); fieldMappings = new List>(); + stackTraceMapping = new List>(); } /// diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/ExportedMethodsRootProvider.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/ExportedMethodsRootProvider.cs index 11fa9c01f1..fcedbe2553 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/ExportedMethodsRootProvider.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/ExportedMethodsRootProvider.cs @@ -2,6 +2,9 @@ // 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.Collections.Generic; + +using Internal.TypeSystem; using Internal.TypeSystem.Ecma; namespace ILCompiler @@ -18,27 +21,37 @@ namespace ILCompiler _module = module; } + public IEnumerable ExportedMethods + { + get + { + foreach (var type in _module.GetAllTypes()) + { + foreach (var method in type.GetMethods()) + { + EcmaMethod ecmaMethod = (EcmaMethod)method; + if (ecmaMethod.IsRuntimeExport || ecmaMethod.IsNativeCallable) + yield return ecmaMethod; + } + } + } + } + public void AddCompilationRoots(IRootingServiceProvider rootProvider) { - foreach (var type in _module.GetAllTypes()) + foreach (var ecmaMethod in ExportedMethods) { - foreach (var method in type.GetMethods()) + if (ecmaMethod.IsRuntimeExport) { - EcmaMethod ecmaMethod = (EcmaMethod)method; - - if (ecmaMethod.IsRuntimeExport) - { - string runtimeExportName = ecmaMethod.GetRuntimeExportName(); - if (runtimeExportName != null) - rootProvider.AddCompilationRoot(method, "Runtime export", runtimeExportName); - } - - if (ecmaMethod.IsNativeCallable) - { - string nativeCallableExportName = ecmaMethod.GetNativeCallableExportName(); - if (nativeCallableExportName != null) - rootProvider.AddCompilationRoot(method, "Native callable", nativeCallableExportName); - } + string runtimeExportName = ecmaMethod.GetRuntimeExportName(); + if (runtimeExportName != null) + rootProvider.AddCompilationRoot((MethodDesc)ecmaMethod, "Runtime export", runtimeExportName); + } + else if (ecmaMethod.IsNativeCallable) + { + string nativeCallableExportName = ecmaMethod.GetNativeCallableExportName(); + if (nativeCallableExportName != null) + rootProvider.AddCompilationRoot((MethodDesc)ecmaMethod, "Native callable", nativeCallableExportName); } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/ExportsFileWriter.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/ExportsFileWriter.cs new file mode 100644 index 0000000000..9935a4dbea --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/ExportsFileWriter.cs @@ -0,0 +1,49 @@ +// 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.Collections.Generic; +using System.IO; +using System.Linq; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler +{ + public class ExportsFileWriter + { + private string _exportsFile; + private List _methods; + private TypeSystemContext _context; + + public ExportsFileWriter(TypeSystemContext context, string exportsFile) + { + _exportsFile = exportsFile; + _context = context; + _methods = new List(); + } + + public void AddExportedMethods(IEnumerable methods) + => _methods.AddRange(methods.Where(m => m.Module != _context.SystemModule)); + + public void EmitExportedMethods() + { + FileStream fileStream = new FileStream(_exportsFile, FileMode.Create); + using (StreamWriter streamWriter = new StreamWriter(fileStream)) + { + if (_context.Target.IsWindows) + { + streamWriter.WriteLine("EXPORTS"); + foreach (var method in _methods) + streamWriter.WriteLine($" {method.GetNativeCallableExportName()}"); + } + else + { + foreach (var method in _methods) + streamWriter.WriteLine($"_{method.GetNativeCallableExportName()}"); + } + } + } + } +} \ No newline at end of file diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/GeneratingMetadataManager.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/GeneratingMetadataManager.cs new file mode 100644 index 0000000000..27af8432ce --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/GeneratingMetadataManager.cs @@ -0,0 +1,236 @@ +// 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.Collections.Generic; +using System.Text; + +using Internal.IL.Stubs; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; +using Internal.Metadata.NativeFormat.Writer; + +using ILCompiler.Metadata; +using ILCompiler.DependencyAnalysis; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + /// + /// Base class for metadata managers that generate metadata blobs. + /// + public abstract class GeneratingMetadataManager : MetadataManager + { + protected readonly string _metadataLogFile; + protected readonly StackTraceEmissionPolicy _stackTraceEmissionPolicy; + private readonly Dictionary _dynamicInvokeThunks; + private readonly ModuleDesc _generatedAssembly; + + public GeneratingMetadataManager(ModuleDesc generatedAssembly, CompilerTypeSystemContext typeSystemContext, MetadataBlockingPolicy blockingPolicy, string logFile, StackTraceEmissionPolicy stackTracePolicy) + : base(typeSystemContext, blockingPolicy) + { + _metadataLogFile = logFile; + _stackTraceEmissionPolicy = stackTracePolicy; + _generatedAssembly = generatedAssembly; + + if (DynamicInvokeMethodThunk.SupportsThunks(typeSystemContext)) + { + _dynamicInvokeThunks = new Dictionary(); + } + } + + public sealed override bool WillUseMetadataTokenToReferenceMethod(MethodDesc method) + { + return (GetMetadataCategory(method) & MetadataCategory.Description) != 0; + } + + public sealed override bool WillUseMetadataTokenToReferenceField(FieldDesc field) + { + return (GetMetadataCategory(field) & MetadataCategory.Description) != 0; + } + + protected void ComputeMetadata( + TPolicy policy, + NodeFactory factory, + out byte[] metadataBlob, + out List> typeMappings, + out List> methodMappings, + out List> fieldMappings, + out List> stackTraceMapping) where TPolicy : struct, IMetadataPolicy + { + var transformed = MetadataTransform.Run(policy, GetCompilationModulesWithMetadata()); + MetadataTransform transform = transformed.Transform; + + // TODO: DeveloperExperienceMode: Use transformed.Transform.HandleType() to generate + // TypeReference records for _typeDefinitionsGenerated that don't have metadata. + // (To be used in MissingMetadataException messages) + + // Generate metadata blob + var writer = new MetadataWriter(); + writer.ScopeDefinitions.AddRange(transformed.Scopes); + + // Generate entries in the blob for methods that will be necessary for stack trace purposes. + var stackTraceRecords = new List>(); + foreach (var methodBody in GetCompiledMethodBodies()) + { + MethodDesc method = methodBody.Method; + + MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); + + // Methods that will end up in the reflection invoke table should not have an entry in stack trace table + // We'll try looking them up in reflection data at runtime. + if (transformed.GetTransformedMethodDefinition(typicalMethod) != null && + ShouldMethodBeInInvokeMap(method) && + (GetMetadataCategory(method) & MetadataCategory.RuntimeMapping) != 0) + continue; + + if (!_stackTraceEmissionPolicy.ShouldIncludeMethod(method)) + continue; + + MetadataRecord record = transform.HandleQualifiedMethod(typicalMethod); + + // As a twist, instantiated generic methods appear as if instantiated over their formals. + if (typicalMethod.HasInstantiation) + { + var methodInst = new MethodInstantiation + { + Method = record, + }; + methodInst.GenericTypeArguments.Capacity = typicalMethod.Instantiation.Length; + foreach (EcmaGenericParameter typeArgument in typicalMethod.Instantiation) + { + var genericParam = new TypeReference + { + TypeName = (ConstantStringValue)typeArgument.Name, + }; + methodInst.GenericTypeArguments.Add(genericParam); + } + record = methodInst; + } + + stackTraceRecords.Add(new KeyValuePair( + method, + record)); + + writer.AdditionalRootRecords.Add(record); + } + + var ms = new MemoryStream(); + + // .NET metadata is UTF-16 and UTF-16 contains code points that don't translate to UTF-8. + var noThrowUtf8Encoding = new UTF8Encoding(false, false); + + using (var logWriter = _metadataLogFile != null ? new StreamWriter(File.Open(_metadataLogFile, FileMode.Create, FileAccess.Write, FileShare.Read), noThrowUtf8Encoding) : null) + { + writer.LogWriter = logWriter; + writer.Write(ms); + } + + metadataBlob = ms.ToArray(); + + typeMappings = new List>(); + methodMappings = new List>(); + fieldMappings = new List>(); + stackTraceMapping = new List>(); + + // Generate type definition mappings + foreach (var type in factory.MetadataManager.GetTypesWithEETypes()) + { + MetadataType definition = type.IsTypeDefinition ? type as MetadataType : null; + if (definition == null) + continue; + + MetadataRecord record = transformed.GetTransformedTypeDefinition(definition); + + // Reflection requires that we maintain type identity. Even if we only generated a TypeReference record, + // if there is an EEType for it, we also need a mapping table entry for it. + if (record == null) + record = transformed.GetTransformedTypeReference(definition); + + if (record != null) + typeMappings.Add(new MetadataMapping(definition, writer.GetRecordHandle(record))); + } + + foreach (var method in GetCompiledMethods()) + { + if (method.IsCanonicalMethod(CanonicalFormKind.Specific)) + { + // Canonical methods are not interesting. + continue; + } + + if (IsReflectionBlocked(method.Instantiation) || IsReflectionBlocked(method.OwningType.Instantiation)) + continue; + + if ((GetMetadataCategory(method) & MetadataCategory.RuntimeMapping) == 0) + continue; + + MetadataRecord record = transformed.GetTransformedMethodDefinition(method.GetTypicalMethodDefinition()); + + if (record != null) + methodMappings.Add(new MetadataMapping(method, writer.GetRecordHandle(record))); + } + + foreach (var field in GetFieldsWithRuntimeMapping()) + { + Field record = transformed.GetTransformedFieldDefinition(field.GetTypicalFieldDefinition()); + if (record != null) + fieldMappings.Add(new MetadataMapping(field, writer.GetRecordHandle(record))); + } + + // Generate stack trace metadata mapping + foreach (var stackTraceRecord in stackTraceRecords) + { + stackTraceMapping.Add(new MetadataMapping(stackTraceRecord.Key, writer.GetRecordHandle(stackTraceRecord.Value))); + } + } + + /// + /// Gets a list of fields that got "compiled" and are eligible for a runtime mapping. + /// + /// + protected abstract IEnumerable GetFieldsWithRuntimeMapping(); + + /// + /// Is there a reflection invoke stub for a method that is invokable? + /// + public sealed override bool HasReflectionInvokeStubForInvokableMethod(MethodDesc method) + { + Debug.Assert(IsReflectionInvokable(method)); + + if (_dynamicInvokeThunks == null) + return false; + + // Place an upper limit on how many parameters a method can have to still get a static stub. + // From the past experience, methods taking 8000+ parameters get a stub that can hit various limitations + // in the codegen. On Project N, we were limited to 256 parameters because of MDIL limitations. + // We don't have such limitations here, but it's a nice round number. + // Reflection invoke will still work, but will go through the calling convention converter. + + return method.Signature.Length <= 256; + } + + /// + /// Gets a stub that can be used to reflection-invoke a method with a given signature. + /// + public sealed override MethodDesc GetCanonicalReflectionInvokeStub(MethodDesc method) + { + TypeSystemContext context = method.Context; + var sig = method.Signature; + + // Get a generic method that can be used to invoke method with this shape. + MethodDesc thunk; + var lookupSig = new DynamicInvokeMethodSignature(sig); + if (!_dynamicInvokeThunks.TryGetValue(lookupSig, out thunk)) + { + thunk = new DynamicInvokeMethodThunk(_generatedAssembly.GetGlobalModuleType(), lookupSig); + _dynamicInvokeThunks.Add(lookupSig, thunk); + } + + return InstantiateCanonicalDynamicInvokeMethodForMethod(thunk, method); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/GenericDictionaryLookup.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/GenericDictionaryLookup.cs new file mode 100644 index 0000000000..42c7deee73 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/GenericDictionaryLookup.cs @@ -0,0 +1,98 @@ +// 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 Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + /// + /// Structure that specifies how a generic dictionary lookup should be performed. + /// + public struct GenericDictionaryLookup + { + private const short UseHelperOffset = -1; + + private short _offset1; + private short _offset2; + + public readonly GenericContextSource ContextSource; + + public bool UseHelper + { + get + { + return _offset1 == UseHelperOffset; + } + } + + public int NumberOfIndirections + { + get + { + Debug.Assert(!UseHelper); + return ContextSource == GenericContextSource.MethodParameter ? 1 : 2; + } + } + + public int this[int index] + { + get + { + Debug.Assert(!UseHelper); + Debug.Assert(index < NumberOfIndirections); + switch (index) + { + case 0: + return _offset1; + case 1: + return _offset2; + } + + // Should be unreachable. + throw new NotSupportedException(); + } + } + + private GenericDictionaryLookup(GenericContextSource contextSource, int offset1, int offset2) + { + ContextSource = contextSource; + _offset1 = checked((short)offset1); + _offset2 = checked((short)offset2); + } + + public static GenericDictionaryLookup CreateFixedLookup(GenericContextSource contextSource, int offset1, int offset2 = UseHelperOffset) + { + Debug.Assert(offset1 != UseHelperOffset); + return new GenericDictionaryLookup(contextSource, offset1, offset2); + } + + public static GenericDictionaryLookup CreateHelperLookup(GenericContextSource contextSource) + { + return new GenericDictionaryLookup(contextSource, UseHelperOffset, UseHelperOffset); + } + } + + /// + /// Specifies to source of the generic context. + /// + public enum GenericContextSource + { + /// + /// Generic context is specified by a hidden parameter that has a method dictionary. + /// + MethodParameter, + + /// + /// Generic context is specified by a hidden parameter that has a type. + /// + TypeParameter, + + /// + /// Generic context is specified implicitly by the `this` object. + /// + ThisObject, + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/ILAssemblyGeneratingMethodDebugInfoProvider.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/ILAssemblyGeneratingMethodDebugInfoProvider.cs new file mode 100644 index 0000000000..ec2abe962d --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/ILAssemblyGeneratingMethodDebugInfoProvider.cs @@ -0,0 +1,165 @@ +// 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.Collections.Generic; +using System.IO; + +using Internal.IL; +using Internal.TypeSystem; + +namespace ILCompiler +{ + /// + /// Debug information provider that wraps another provider and generates IL assembly listing + /// (and maps debug information to it) for all methods the wrapped provider doesn't provide + /// debug information for. + /// + public class ILAssemblyGeneratingMethodDebugInfoProvider : DebugInformationProvider, IDisposable + { + private readonly DebugInformationProvider _wrappedProvider; + private readonly string _fileName; + private readonly TextWriter _tw; + private readonly Dictionary _generatedInfos = new Dictionary(); + + private int _currentLine; + + public ILAssemblyGeneratingMethodDebugInfoProvider(string fileName, DebugInformationProvider wrappedProvider = null) + { + _wrappedProvider = wrappedProvider; + _fileName = fileName; + _tw = new StreamWriter(File.OpenWrite(fileName)); + } + + public override MethodDebugInformation GetDebugInfo(MethodIL methodIL) + { + MethodDebugInformation debugInfo = _wrappedProvider?.GetDebugInfo(methodIL); + if (debugInfo != null && debugInfo != MethodDebugInformation.None) + return debugInfo; + + MethodIL definitionIL = methodIL.GetMethodILDefinition(); + + // One file per compilation model has obvious stability problems in multithreaded + // compilation. This would be fixable by e.g. generating multiple files, or preparing + // the file after the IL scanning phase. Since this is a non-shipping feature, + // we're going to ignore the stability concern here. + lock (_generatedInfos) + { + if (!_generatedInfos.TryGetValue(definitionIL.OwningMethod, out debugInfo)) + { + debugInfo = GetDebugInformation(definitionIL); + _generatedInfos.Add(definitionIL.OwningMethod, debugInfo); + } + + return debugInfo; + } + } + + private MethodDebugInformation GetDebugInformation(MethodIL methodIL) + { + MethodDesc owningMethod = methodIL.OwningMethod; + var disasm = new ILDisassembler(methodIL); + var fmt = new ILDisassembler.ILTypeNameFormatter(null); + + ArrayBuilder sequencePoints = new ArrayBuilder(); + + _tw.Write(".method "); + // TODO: accessibility, specialname, calling conventions etc. + if (!owningMethod.Signature.IsStatic) + _tw.Write("instance "); + _tw.Write(fmt.FormatName(owningMethod.Signature.ReturnType)); + _tw.Write(" "); + _tw.Write(owningMethod.Name); + if (owningMethod.HasInstantiation) + { + _tw.Write("<"); + for (int i = 0; i < owningMethod.Instantiation.Length; i++) + { + if (i != 0) + _tw.Write(", "); + _tw.Write(fmt.FormatName(owningMethod.Instantiation[i])); + } + _tw.Write(">"); + } + _tw.Write("("); + for (int i = 0; i < owningMethod.Signature.Length; i++) + { + if (i != 0) + _tw.Write(", "); + _tw.Write(fmt.FormatName(owningMethod.Signature[i])); + } + _tw.WriteLine(") cil managed"); _currentLine++; + + _tw.WriteLine("{"); _currentLine++; + + _tw.Write(" // Code size: "); + _tw.Write(disasm.CodeSize); + _tw.WriteLine(); _currentLine++; + _tw.Write(" .maxstack "); + _tw.Write(methodIL.MaxStack); + _tw.WriteLine(); _currentLine++; + + LocalVariableDefinition[] locals = methodIL.GetLocals(); + if (locals != null && locals.Length > 0) + { + _tw.Write(" .locals "); + if (methodIL.IsInitLocals) + _tw.Write("init "); + + _tw.Write("("); + + for (int i = 0; i < locals.Length; i++) + { + if (i != 0) + { + _tw.WriteLine(","); _currentLine++; + _tw.Write(" "); + } + _tw.Write(fmt.FormatName(locals[i].Type)); + _tw.Write(" "); + if (locals[i].IsPinned) + _tw.Write("pinned "); + _tw.Write("V_"); + _tw.Write(i); + } + _tw.WriteLine(")"); _currentLine++; + } + _tw.WriteLine(); _currentLine++; + + while (disasm.HasNextInstruction) + { + _currentLine++; + + int offset = disasm.Offset; + _tw.WriteLine(disasm.GetNextInstruction()); + sequencePoints.Add(new ILSequencePoint(offset, _fileName, _currentLine)); + } + + _tw.WriteLine("}"); _currentLine++; + _tw.WriteLine(); _currentLine++; + + return new SyntheticMethodDebugInformation(sequencePoints.ToArray()); + } + + public void Dispose() + { + _tw.Dispose(); + } + + private class SyntheticMethodDebugInformation : MethodDebugInformation + { + private readonly ILSequencePoint[] _sequencePoints; + + public SyntheticMethodDebugInformation(ILSequencePoint[] sequencePoints) + { + _sequencePoints = sequencePoints; + } + + public override IEnumerable GetSequencePoints() + { + return _sequencePoints; + } + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/ILScanner.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/ILScanner.cs index d93aba60cc..fd5b47c30f 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/ILScanner.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/ILScanner.cs @@ -13,6 +13,8 @@ using Internal.IL; using Internal.IL.Stubs; using Internal.TypeSystem; +using Debug = System.Diagnostics.Debug; + namespace ILCompiler { /// @@ -26,13 +28,12 @@ namespace ILCompiler DependencyAnalyzerBase dependencyGraph, ILScanNodeFactory nodeFactory, IEnumerable roots, + DebugInformationProvider debugInformationProvider, Logger logger) - : base(dependencyGraph, nodeFactory, roots, logger) + : base(dependencyGraph, nodeFactory, roots, debugInformationProvider, null, logger) { } - protected override bool GenerateDebugInfo => false; - protected override void CompileInternal(string outputFile, ObjectDumper dumper) { // TODO: We should have a base class for compilation that doesn't implement ICompilation so that @@ -74,9 +75,12 @@ namespace ILCompiler } } - public ILScanResults Scan() + ILScanResults IILScanner.Scan() { - return new ILScanResults(_dependencyGraph.MarkedNodeList); + _nodeFactory.NameMangler.CompilationUnitPrefix = ""; + _dependencyGraph.ComputeMarkedNodes(); + + return new ILScanResults(_dependencyGraph, _nodeFactory); } } @@ -85,26 +89,225 @@ namespace ILCompiler ILScanResults Scan(); } - public class ILScanResults + internal class ScannerFailedException : InternalCompilerErrorException { - private readonly ImmutableArray> _markedNodes; - - internal ILScanResults(ImmutableArray> markedNodes) + public ScannerFailedException(string message) + : base(message + " " + "You can work around by running the compilation with scanner disabled.") + { + } + } + + public class ILScanResults : CompilationResults + { + internal ILScanResults(DependencyAnalyzerBase graph, NodeFactory factory) + : base(graph, factory) { - _markedNodes = markedNodes; } - public IEnumerable CompiledMethods + public VTableSliceProvider GetVTableLayoutInfo() { - get + return new ScannedVTableProvider(MarkedNodes); + } + + public DictionaryLayoutProvider GetDictionaryLayoutInfo() + { + return new ScannedDictionaryLayoutProvider(MarkedNodes); + } + + public DevirtualizationManager GetDevirtualizationManager() + { + return new ScannedDevirtualizationManager(MarkedNodes); + } + + private class ScannedVTableProvider : VTableSliceProvider + { + private Dictionary> _vtableSlices = new Dictionary>(); + + public ScannedVTableProvider(ImmutableArray> markedNodes) { - foreach (var node in _markedNodes) + foreach (var node in markedNodes) { - var methodNode = node as ScannedMethodNode; - if (methodNode != null) - yield return methodNode.Method; + var vtableSliceNode = node as VTableSliceNode; + if (vtableSliceNode != null) + { + _vtableSlices.Add(vtableSliceNode.Type, vtableSliceNode.Slots); + } } } + + internal override VTableSliceNode GetSlice(TypeDesc type) + { + // TODO: move ownership of compiler-generated entities to CompilerTypeSystemContext. + // https://github.com/dotnet/corert/issues/3873 + if (type.GetTypeDefinition() is Internal.TypeSystem.Ecma.EcmaType) + { + if (!_vtableSlices.TryGetValue(type, out IReadOnlyList slots)) + { + // If we couln't find the vtable slice information for this type, it's because the scanner + // didn't correctly predict what will be needed. + // To troubleshoot, compare the dependency graph of the scanner and the compiler. + // Follow the path from the node that requested this node to the root. + // On the path, you'll find a node that exists in both graphs, but it's predecessor + // only exists in the compiler's graph. That's the place to focus the investigation on. + // Use the ILCompiler-DependencyGraph-Viewer tool to investigate. + Debug.Assert(false); + string typeName = ExceptionTypeNameFormatter.Instance.FormatName(type); + throw new ScannerFailedException($"VTable of type '{typeName}' not computed by the IL scanner."); + } + return new PrecomputedVTableSliceNode(type, slots); + } + else + return new LazilyBuiltVTableSliceNode(type); + } + } + + private class ScannedDictionaryLayoutProvider : DictionaryLayoutProvider + { + private Dictionary> _layouts = new Dictionary>(); + + public ScannedDictionaryLayoutProvider(ImmutableArray> markedNodes) + { + foreach (var node in markedNodes) + { + var layoutNode = node as DictionaryLayoutNode; + if (layoutNode != null) + { + TypeSystemEntity owningMethodOrType = layoutNode.OwningMethodOrType; + _layouts.Add(owningMethodOrType, layoutNode.Entries); + } + } + } + + private DictionaryLayoutNode GetPrecomputedLayout(TypeSystemEntity methodOrType) + { + if (!_layouts.TryGetValue(methodOrType, out IEnumerable layout)) + { + // If we couln't find the dictionary layout information for this, it's because the scanner + // didn't correctly predict what will be needed. + // To troubleshoot, compare the dependency graph of the scanner and the compiler. + // Follow the path from the node that requested this node to the root. + // On the path, you'll find a node that exists in both graphs, but it's predecessor + // only exists in the compiler's graph. That's the place to focus the investigation on. + // Use the ILCompiler-DependencyGraph-Viewer tool to investigate. + Debug.Assert(false); + throw new ScannerFailedException($"A dictionary layout was not computed by the IL scanner."); + } + return new PrecomputedDictionaryLayoutNode(methodOrType, layout); + } + + public override DictionaryLayoutNode GetLayout(TypeSystemEntity methodOrType) + { + if (methodOrType is TypeDesc type) + { + // TODO: move ownership of compiler-generated entities to CompilerTypeSystemContext. + // https://github.com/dotnet/corert/issues/3873 + if (type.GetTypeDefinition() is Internal.TypeSystem.Ecma.EcmaType) + return GetPrecomputedLayout(type); + else + return new LazilyBuiltDictionaryLayoutNode(type); + } + else + { + Debug.Assert(methodOrType is MethodDesc); + MethodDesc method = (MethodDesc)methodOrType; + + // TODO: move ownership of compiler-generated entities to CompilerTypeSystemContext. + // https://github.com/dotnet/corert/issues/3873 + if (method.GetTypicalMethodDefinition() is Internal.TypeSystem.Ecma.EcmaMethod) + return GetPrecomputedLayout(method); + else + return new LazilyBuiltDictionaryLayoutNode(method); + } + } + } + + private class ScannedDevirtualizationManager : DevirtualizationManager + { + private HashSet _constructedTypes = new HashSet(); + private HashSet _unsealedTypes = new HashSet(); + + public ScannedDevirtualizationManager(ImmutableArray> markedNodes) + { + foreach (var node in markedNodes) + { + if (node is ConstructedEETypeNode eetypeNode) + { + TypeDesc type = eetypeNode.Type; + + if (!type.IsInterface) + { + // + // We collect this information: + // + // 1. What types got allocated + // This is needed for optimizing codegens that might attempt to devirtualize + // calls to sealed types. The devirtualization is not allowed to succeed + // for types that never got allocated because the scanner likely didn't scan + // the target of the virtual call. + // 2. What types are the base types of other types + // This is needed for optimizations. We use this information to effectively + // seal types that are not base types for any other type. + // + + TypeDesc canonType = type.ConvertToCanonForm(CanonicalFormKind.Specific); + + _constructedTypes.Add(canonType); + + // Since this is used for the purposes of devirtualization, it's really convenient + // to also have Array for each T[]. + if (canonType.IsArray) + _constructedTypes.Add(canonType.GetClosestDefType()); + + TypeDesc baseType = canonType.BaseType; + bool added = true; + while (baseType != null && added) + { + baseType = baseType.ConvertToCanonForm(CanonicalFormKind.Specific); + added = _unsealedTypes.Add(baseType); + baseType = baseType.BaseType; + } + } + + } + } + } + + public override bool IsEffectivelySealed(TypeDesc type) + { + // If we know we scanned a type that derives from this one, this for sure can't be reported as sealed. + TypeDesc canonType = type.ConvertToCanonForm(CanonicalFormKind.Specific); + if (_unsealedTypes.Contains(canonType)) + return false; + + // We don't want to report types that never got allocated as sealed because that would allow + // the codegen to do direct calls to the type's methods. That can potentially lead to codegen + // generating calls to methods we never scanned (consider a sealed type that never got allocated + // with a virtual method that can be devirtualized because the type is sealed). + // Codegen looking at code we didn't scan is never okay. + if (!_constructedTypes.Contains(canonType)) + return false; + + if (type is MetadataType metadataType) + { + // Due to how the compiler is structured, we might see "constructed" EETypes for things + // that never got allocated (doing a typeof() on a class that is otherwise never used is + // a good example of when that happens). This can put us into a position where we could + // report `sealed` on an `abstract` class, but that doesn't lead to anything good. + return !metadataType.IsAbstract; + } + + // Everything else can be considered sealed. + return true; + } + + public override bool IsEffectivelySealed(MethodDesc method) + { + // For the same reason as above, don't report methods on unallocated types as sealed. + if (!_constructedTypes.Contains(method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific))) + return false; + + return base.IsEffectivelySealed(method); + } } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/ILScannerBuilder.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/ILScannerBuilder.cs index aea5978d22..340c5603a9 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/ILScannerBuilder.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/ILScannerBuilder.cs @@ -8,6 +8,8 @@ using System.Collections.Generic; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; + namespace ILCompiler { public sealed class ILScannerBuilder @@ -21,12 +23,14 @@ namespace ILCompiler private Logger _logger = Logger.Null; private DependencyTrackingLevel _dependencyTrackingLevel = DependencyTrackingLevel.None; private IEnumerable _compilationRoots = Array.Empty(); + private MetadataManager _metadataManager; internal ILScannerBuilder(CompilerTypeSystemContext context, CompilationModuleGroup compilationGroup, NameMangler mangler) { _context = context; _compilationGroup = compilationGroup; _nameMangler = mangler; + _metadataManager = new EmptyMetadataManager(context); } public ILScannerBuilder UseDependencyTracking(DependencyTrackingLevel trackingLevel) @@ -41,15 +45,19 @@ namespace ILCompiler return this; } + public ILScannerBuilder UseMetadataManager(MetadataManager metadataManager) + { + _metadataManager = metadataManager; + return this; + } + public IILScanner ToILScanner() { - // TODO: we will want different metadata managers depending on whether we're doing reflection analysis - var metadataManager = new CompilerGeneratedMetadataManager(_compilationGroup, _context, null); - - var nodeFactory = new ILScanNodeFactory(_context, _compilationGroup, metadataManager, _nameMangler); + var interopStubManager = new CompilerGeneratedInteropStubManager(_compilationGroup, _context, new InteropStateManager(_compilationGroup.GeneratedAssembly)); + var nodeFactory = new ILScanNodeFactory(_context, _compilationGroup, _metadataManager, interopStubManager, _nameMangler); DependencyAnalyzerBase graph = _dependencyTrackingLevel.CreateDependencyGraph(nodeFactory); - return new ILScanner(graph, nodeFactory, _compilationRoots, _logger); + return new ILScanner(graph, nodeFactory, _compilationRoots, new NullDebugInformationProvider(), _logger); } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/ILStreamReader.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/ILStreamReader.cs index 9d30c702cf..8facc05f74 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/ILStreamReader.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/ILStreamReader.cs @@ -19,7 +19,7 @@ namespace Internal.Compiler /// Used by logic which is designed to encode information in il structure, but not used /// to support general compilation of IL. /// - struct ILStreamReader + public struct ILStreamReader { private byte[] _ilBytes; private MethodIL _methodIL; @@ -175,14 +175,19 @@ namespace Internal.Compiler public bool TryReadLdtokenAsTypeSystemEntity(out TypeSystemEntity entity) { int token; - if (!TryReadLdtoken(out token)) + bool tokenResolved; + try { + tokenResolved = TryReadLdtoken(out token); + entity = tokenResolved ?(TypeSystemEntity)_methodIL.GetObject(token) : null; + } + catch (TypeSystemException.TypeLoadException) + { + tokenResolved = false; entity = null; - return false; } - entity = (TypeSystemEntity)_methodIL.GetObject(token); - return true; + return tokenResolved; } public TypeSystemEntity ReadLdtokenAsTypeSystemEntity() @@ -194,7 +199,7 @@ namespace Internal.Compiler return result; } - bool TryReadLdcI4(out int value) + public bool TryReadLdcI4(out int value) { ILOpcode opcode = PeekILOpcode(); diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/IRootingServiceProvider.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/IRootingServiceProvider.cs index 01a9281772..0895464ccd 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/IRootingServiceProvider.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/IRootingServiceProvider.cs @@ -13,7 +13,10 @@ namespace ILCompiler { void AddCompilationRoot(MethodDesc method, string reason, string exportName = null); void AddCompilationRoot(TypeDesc type, string reason); - void RootStaticBasesForType(TypeDesc type, string reason); + void RootThreadStaticBaseForType(TypeDesc type, string reason); + void RootGCStaticBaseForType(TypeDesc type, string reason); + void RootNonGCStaticBaseForType(TypeDesc type, string reason); void RootVirtualMethodForReflection(MethodDesc method, string reason); + void RootModuleMetadata(ModuleDesc module, string reason); } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/InternalCompilerErrorException.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/InternalCompilerErrorException.cs new file mode 100644 index 0000000000..06d0cf3e5d --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/InternalCompilerErrorException.cs @@ -0,0 +1,21 @@ +// 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; + +namespace ILCompiler +{ + public class InternalCompilerErrorException : Exception + { + public InternalCompilerErrorException(string message) + : this(message, innerException: null) + { + } + + public InternalCompilerErrorException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/InteropStubManager.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/InteropStubManager.cs index 0c5850b1c7..8c021e94f0 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/InteropStubManager.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/InteropStubManager.cs @@ -3,26 +3,23 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; -using Internal.IL.Stubs; using Internal.TypeSystem; -using Internal.TypeSystem.Interop; +using ILCompiler.DependencyAnalysis; - -using Debug = System.Diagnostics.Debug; +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; namespace ILCompiler { /// /// This class is responsible for managing stub methods for interop /// - public sealed class InteropStubManager + public abstract class InteropStubManager { private readonly CompilationModuleGroup _compilationModuleGroup; private readonly CompilerTypeSystemContext _typeSystemContext; - internal HashSet _delegateMarshalingTypes = new HashSet(); - private HashSet _structMarshallingTypes = new HashSet(); + protected ModuleDesc _interopModule; + private const string _interopModuleName = "System.Private.Interop"; public InteropStateManager InteropStateManager { @@ -34,99 +31,23 @@ namespace ILCompiler _compilationModuleGroup = compilationModuleGroup; _typeSystemContext = typeSystemContext; InteropStateManager = interopStateManager; - } - - internal MethodDesc GetOpenStaticDelegateMarshallingStub(TypeDesc delegateType) - { - var stub = InteropStateManager.GetOpenStaticDelegateMarshallingThunk(delegateType); - Debug.Assert(stub != null); - _delegateMarshalingTypes.Add(delegateType); - return stub; + // Note: interopModule might be null if we're building with a class library that doesn't support rich interop + _interopModule = typeSystemContext.GetModuleForSimpleName(_interopModuleName, false); } - internal MethodDesc GetClosedDelegateMarshallingStub(TypeDesc delegateType) - { - var stub = InteropStateManager.GetClosedDelegateMarshallingThunk(delegateType); - Debug.Assert(stub != null); - _delegateMarshalingTypes.Add(delegateType); - return stub; - } - internal MethodDesc GetForwardDelegateCreationStub(TypeDesc delegateType) - { - var stub = InteropStateManager.GetForwardDelegateCreationThunk(delegateType); - Debug.Assert(stub != null); + public abstract void AddDependeciesDueToPInvoke(ref DependencyList dependencies, NodeFactory factory, MethodDesc method); + + public abstract void AddInterestingInteropConstructedTypeDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type); + - _delegateMarshalingTypes.Add(delegateType); - return stub; - } + /// + /// For Marshal generic APIs(eg. Marshal.StructureToPtr, GetFunctionPointerForDelegate) we add + /// the generic parameter as dependencies so that we can generate runtime data for them + /// + public abstract void AddMarshalAPIsGenericDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method); - internal TypeDesc GetStructMarshallingType(TypeDesc structType) - { - NativeStructType nativeType = InteropStateManager.GetStructMarshallingNativeType(structType); - Debug.Assert(nativeType != null); - _structMarshallingTypes.Add(nativeType); - return nativeType; - } - - internal MethodDesc GetStructMarshallingManagedToNativeStub(TypeDesc structType) - { - MethodDesc stub = InteropStateManager.GetStructMarshallingManagedToNativeThunk(structType); - Debug.Assert(stub != null); - return stub; - } - - internal MethodDesc GetStructMarshallingNativeToManagedStub(TypeDesc structType) - { - MethodDesc stub = InteropStateManager.GetStructMarshallingNativeToManagedThunk(structType); - Debug.Assert(stub != null); - return stub; - } - - internal MethodDesc GetStructMarshallingCleanupStub(TypeDesc structType) - { - MethodDesc stub = InteropStateManager.GetStructMarshallingCleanupThunk(structType); - Debug.Assert(stub != null); - return stub; - } - - internal TypeDesc GetInlineArrayType(InlineArrayCandidate candidate) - { - TypeDesc inlineArrayType = InteropStateManager.GetInlineArrayType(candidate); - Debug.Assert(inlineArrayType != null); - return inlineArrayType; - } - - internal struct DelegateMarshallingThunks - { - public TypeDesc DelegateType; - public DelegateMarshallingMethodThunk OpenStaticDelegateMarshallingThunk; - public DelegateMarshallingMethodThunk ClosedDelegateMarshallingThunk; - public ForwardDelegateCreationThunk DelegateCreationThunk; - } - - internal IEnumerable GetDelegateMarshallingThunks() - { - foreach (var delegateType in _delegateMarshalingTypes) - { - var openStub = InteropStateManager.GetOpenStaticDelegateMarshallingThunk(delegateType); - var closedStub = InteropStateManager.GetClosedDelegateMarshallingThunk(delegateType); - var delegateCreationStub = InteropStateManager.GetForwardDelegateCreationThunk(delegateType); - yield return - new DelegateMarshallingThunks() - { - DelegateType = delegateType, - OpenStaticDelegateMarshallingThunk = openStub, - ClosedDelegateMarshallingThunk = closedStub, - DelegateCreationThunk = delegateCreationStub - }; - } - } - - internal HashSet GetStructMarshallingTypes() - { - return _structMarshallingTypes; - } + public abstract void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode); } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/JitHelper.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/JitHelper.cs index 59b675487f..e7c15c87bd 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/JitHelper.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/JitHelper.cs @@ -45,6 +45,15 @@ namespace ILCompiler case ReadyToRunHelper.ThrowDivZero: methodDesc = context.GetHelperEntryPoint("ThrowHelpers", "ThrowDivideByZeroException"); break; + case ReadyToRunHelper.ThrowArgumentOutOfRange: + methodDesc = context.GetHelperEntryPoint("ThrowHelpers", "ThrowArgumentOutOfRangeException"); + break; + case ReadyToRunHelper.ThrowArgument: + methodDesc = context.GetHelperEntryPoint("ThrowHelpers", "ThrowArgumentException"); + break; + case ReadyToRunHelper.ThrowPlatformNotSupported: + methodDesc = context.GetHelperEntryPoint("ThrowHelpers", "ThrowPlatformNotSupportedException"); + break; case ReadyToRunHelper.DebugBreak: mangledName = "RhDebugBreak"; @@ -119,6 +128,12 @@ namespace ILCompiler case ReadyToRunHelper.Dbl2ULng: mangledName = "RhpDbl2ULng"; break; + case ReadyToRunHelper.Dbl2Int: + mangledName = "RhpDbl2Int"; + break; + case ReadyToRunHelper.Dbl2UInt: + mangledName = "RhpDbl2UInt"; + break; case ReadyToRunHelper.Dbl2IntOvf: methodDesc = context.GetHelperEntryPoint("MathHelpers", "Dbl2IntOvf"); @@ -139,6 +154,58 @@ namespace ILCompiler case ReadyToRunHelper.FltRem: mangledName = "RhpFltRem"; break; + case ReadyToRunHelper.DblRound: + mangledName = "RhpDblRound"; + break; + case ReadyToRunHelper.FltRound: + mangledName = "RhpFltRound"; + break; + + case ReadyToRunHelper.LMul: + mangledName = "RhpLMul"; + break; + case ReadyToRunHelper.LMulOfv: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "LMulOvf"); + break; + case ReadyToRunHelper.ULMulOvf: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "ULMulOvf"); + break; + + case ReadyToRunHelper.Mod: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "IMod"); + break; + case ReadyToRunHelper.UMod: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "UMod"); + break; + case ReadyToRunHelper.ULMod: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "ULMod"); + break; + case ReadyToRunHelper.LMod: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "LMod"); + break; + + case ReadyToRunHelper.Div: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "IDiv"); + break; + case ReadyToRunHelper.UDiv: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "UDiv"); + break; + case ReadyToRunHelper.ULDiv: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "ULDiv"); + break; + case ReadyToRunHelper.LDiv: + methodDesc = context.GetHelperEntryPoint("MathHelpers", "LDiv"); + break; + + case ReadyToRunHelper.LRsz: + mangledName = "RhpLRsz"; + break; + case ReadyToRunHelper.LRsh: + mangledName = "RhpLRsh"; + break; + case ReadyToRunHelper.LLsh: + mangledName = "RhpLLsh"; + break; case ReadyToRunHelper.PInvokeBegin: mangledName = "RhpPInvoke"; diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/LazyGenericsPolicy.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/LazyGenericsPolicy.cs index 738fd18e6f..5890d260e6 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/LazyGenericsPolicy.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/LazyGenericsPolicy.cs @@ -17,10 +17,13 @@ namespace ILCompiler { public sealed override bool UsesLazyGenerics(MethodDesc method) { + if (UsesLazyGenerics(method.OwningType)) + return true; + if (method.HasInstantiation) return method.IsVirtual || method.HasCustomAttribute("System.Runtime.CompilerServices", "ForceLazyDictionaryAttribute"); - else - return UsesLazyGenerics(method.OwningType); + + return false; } public sealed override bool UsesLazyGenerics(TypeDesc type) diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/LibraryInitializers.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/LibraryInitializers.cs index 69c9a1477a..cf9222cb85 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/LibraryInitializers.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/LibraryInitializers.cs @@ -29,6 +29,7 @@ namespace ILCompiler new LibraryInitializerInfo("System.Private.Reflection.Execution"), new LibraryInitializerInfo("System.Private.DeveloperExperience.Console"), new LibraryInitializerInfo("System.Private.Interop"), + new LibraryInitializerInfo("System.Private.StackTraceMetadata"), }; private List _libraryInitializerMethods; diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/LibraryRootProvider.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/LibraryRootProvider.cs index c4a3805cb2..24f05e00df 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/LibraryRootProvider.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/LibraryRootProvider.cs @@ -41,7 +41,9 @@ namespace ILCompiler if (!type.HasInstantiation) { RootMethods(type, "Library module method", rootProvider); - rootProvider.RootStaticBasesForType(type, "Library module type statics"); + rootProvider.RootThreadStaticBaseForType(type, "Library module type statics"); + rootProvider.RootGCStaticBaseForType(type, "Library module type statics"); + rootProvider.RootNonGCStaticBaseForType(type, "Library module type statics"); } } } @@ -84,6 +86,10 @@ namespace ILCompiler { MethodSignature signature = method.Signature; + // Vararg methods are not supported in .NET Core + if ((signature.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask) == MethodSignatureFlags.CallingConventionVarargs) + ThrowHelper.ThrowBadImageFormatException(); + CheckTypeCanBeUsedInSignature(signature.ReturnType); for (int i = 0; i < signature.Length; i++) diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/ManagedBinaryEmitter.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/ManagedBinaryEmitter.cs new file mode 100644 index 0000000000..bfb57b69c2 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/ManagedBinaryEmitter.cs @@ -0,0 +1,387 @@ +// 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.Linq; +using System.Reflection; +using System.Collections.Generic; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.Metadata; +using System.Collections.Immutable; +using System.Reflection.PortableExecutable; +using System.IO; +using System.Diagnostics; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; +using ILCompiler.DependencyAnalysis; + +namespace ILCompiler +{ + public class ManagedBinaryEmitter + { + public class EmittedTypeDefinition + { + private readonly ManagedBinaryEmitter _managedEmitter; + private List _methods = new List(); + + public string Name { get; private set; } + public bool IsValueType { get; private set; } + public IReadOnlyList Methods => _methods; + + internal EmittedTypeDefinition(string name, bool isValueType, ManagedBinaryEmitter managedEmitter) + { + Name = name; + IsValueType = isValueType; + _managedEmitter = managedEmitter; + } + + public EmittedMethodDefinition EmitMethodDefinition(string name, MethodSignature signature) + { + var newMethodDef = new EmittedMethodDefinition(name, signature, _managedEmitter); + _methods.Add(newMethodDef); + return newMethodDef; + } + } + public class EmittedMethodDefinition + { + private readonly ManagedBinaryEmitter _managedEmitter; + + public string Name { get; private set; } + public MethodSignature Signature { get; private set; } + public InstructionEncoder Code { get; private set; } + + internal EmittedMethodDefinition(string name, MethodSignature signature, ManagedBinaryEmitter managedEmitter) + { + Name = name; + Signature = signature; + _managedEmitter = managedEmitter; + Code = new InstructionEncoder(new BlobBuilder()); + } + } + + private readonly TypeSystemContext _typeSystemContext; + + private MetadataBuilder _metadataBuilder; + private MethodBodyStreamEncoder _methodBodyStream; + private List _emittedTypes; + + protected MetadataBuilder Builder => _metadataBuilder; + + public ManagedBinaryEmitter(TypeSystemContext typeSystemContext, string assemblyName) + { + _typeSystemContext = typeSystemContext; + + _metadataBuilder = new MetadataBuilder(); + _methodBodyStream = new MethodBodyStreamEncoder(new BlobBuilder()); + _emittedTypes = new List(); + + _metadataBuilder.AddAssembly( + _metadataBuilder.GetOrAddString(assemblyName), + new Version(0, 0, 0, 0), + culture: default(StringHandle), + publicKey: default(BlobHandle), + flags: default(AssemblyFlags), + hashAlgorithm: AssemblyHashAlgorithm.None); + + _metadataBuilder.AddModule( + 0, + _metadataBuilder.GetOrAddString(assemblyName), + default(GuidHandle), default(GuidHandle), default(GuidHandle)); + + // Module type + _metadataBuilder.AddTypeDefinition( + default(TypeAttributes), + default(StringHandle), + _metadataBuilder.GetOrAddString(""), + baseType: default(EntityHandle), + fieldList: MetadataTokens.FieldDefinitionHandle(1), + methodList: MetadataTokens.MethodDefinitionHandle(1)); + + _signatureEmitter = new EcmaSignatureEncoder(new EntityProviderForEcmaSignature(this)); + } + + public EmittedTypeDefinition EmitTypeDefinition(string typeName, bool isValueType) + { + EmittedTypeDefinition newTypeDef = new EmittedTypeDefinition(typeName, isValueType, this); + _emittedTypes.Add(newTypeDef); + return newTypeDef; + } + + public EntityHandle EmitMetadataHandleForTypeSystemEntity(TypeSystemEntity entity) + { + switch (entity) + { + case FieldDesc field: return MakeMemberReferenceHandle(field); + case MethodDesc method: return MakeMemberReferenceHandle(method); + case TypeDesc type: return MakeTypeRefOrSpecHandle(type); + + default: + throw new NotSupportedException(); + } + } + + public BlobHandle EmitSignatureBlobForMethodSignature(MethodSignature signature) + { + return MakeSignatureHandle(signature); + } + + /// + /// Encode a type signature into a specified blob. + /// + /// Blob to encode type signature into. Must not be null + public void EncodeSignatureForType(TypeDesc type, BlobBuilder blobBuilder) + { + SignatureTypeEncoder sigEncoder = new SignatureTypeEncoder(blobBuilder); + _signatureEmitter.EncodeTypeSignature(sigEncoder, type); + } + + public void EmitOutputFile(string outputPath) + { + using (FileStream sw = new FileStream(outputPath, FileMode.Create, FileAccess.ReadWrite)) + { + EmitToStream(sw); + } + } + + public void EmitToStream(Stream stream) + { + foreach (var typeDef in _emittedTypes) + { + MethodDefinitionHandle? firstMethodHandle = null; + + foreach (var methodDef in typeDef.Methods) + { + int bodyOffset = _methodBodyStream.AddMethodBody(methodDef.Code); + + BlobHandle signature = MakeSignatureHandle(methodDef.Signature); + + MethodDefinitionHandle methodHandle = _metadataBuilder.AddMethodDefinition( + MethodAttributes.PrivateScope | MethodAttributes.Static, + MethodImplAttributes.IL | MethodImplAttributes.Managed, + _metadataBuilder.GetOrAddString(methodDef.Name), + signature, + bodyOffset, + parameterList: default(ParameterHandle)); + + if (firstMethodHandle == null) + firstMethodHandle = methodHandle; + } + + _metadataBuilder.AddTypeDefinition( + default(TypeAttributes), + default(StringHandle), + _metadataBuilder.GetOrAddString(typeDef.Name), + typeDef.IsValueType ? + MakeTypeRefHandle(_typeSystemContext.GetWellKnownType(WellKnownType.ValueType)) : + MakeTypeRefHandle(_typeSystemContext.GetWellKnownType(WellKnownType.Object)), + fieldList: MetadataTokens.FieldDefinitionHandle(1), + methodList: firstMethodHandle.Value); + } + + BlobBuilder peBlob = new BlobBuilder(); + new ManagedPEBuilder(PEHeaderBuilder.CreateLibraryHeader(), new MetadataRootBuilder(_metadataBuilder), _methodBodyStream.Builder).Serialize(peBlob); + + peBlob.WriteContentTo(stream); + + // Clear some variables to catch any caller trying to emit data after writing the output file + _emittedTypes = null; + _metadataBuilder = null; + _methodBodyStream = default(MethodBodyStreamEncoder); + } + + #region TypeSystem Entities To Handle Encoders + private Dictionary _assemblyRefHandles = new Dictionary(); + private Dictionary _typeRefOrSpecHandles = new Dictionary(); + private Dictionary _memberRefOrSpecHandles = new Dictionary(); + private Dictionary _methodSignatureHandles = new Dictionary(); + private Dictionary _fieldSignatureHandles = new Dictionary(); + + private struct EntityProviderForEcmaSignature : IEntityHandleProvider + { + private ManagedBinaryEmitter _emitter; + + public EntityProviderForEcmaSignature(ManagedBinaryEmitter emitter) + { + _emitter = emitter; + } + + public EntityHandle GetTypeDefOrRefHandleForTypeDesc(TypeDesc type) + { + return _emitter.MakeTypeRefHandle(type); + } + } + + private EcmaSignatureEncoder _signatureEmitter; + + private BlobHandle MakeSignatureHandle(MethodSignature signature) + { + BlobHandle handle; + + if (!_methodSignatureHandles.TryGetValue(signature, out handle)) + { + BlobBuilder metadataSignature = new BlobBuilder(); + + _signatureEmitter.EncodeMethodSignature(metadataSignature, signature); + + _methodSignatureHandles[signature] = handle = _metadataBuilder.GetOrAddBlob(metadataSignature); + } + + return handle; + } + + private BlobHandle MakeSignatureHandle(TypeSystemEntity methodOrField) + { + if (methodOrField is MethodDesc) + { + return MakeSignatureHandle(((MethodDesc)methodOrField).Signature); + } + else + { + BlobHandle handle; + FieldDesc field = (FieldDesc)methodOrField; + + if (!_fieldSignatureHandles.TryGetValue(field, out handle)) + { + BlobBuilder metadataSignature = new BlobBuilder(); + + SignatureTypeEncoder fieldSigEncoder = new BlobEncoder(metadataSignature).FieldSignature(); + _signatureEmitter.EncodeTypeSignature(fieldSigEncoder, field.FieldType); + + _fieldSignatureHandles[field] = handle = _metadataBuilder.GetOrAddBlob(metadataSignature); + } + + return handle; + } + } + + private AssemblyReferenceHandle MakeAssemblyReferenceHandle(IAssemblyDesc assemblyRef) + { + AssemblyReferenceHandle handle; + + if (!_assemblyRefHandles.TryGetValue(assemblyRef, out handle)) + { + AssemblyName assemblyName = assemblyRef.GetName(); + + handle = _metadataBuilder.AddAssemblyReference( + _metadataBuilder.GetOrAddString(assemblyName.Name), + assemblyName.Version, + default(StringHandle), + _metadataBuilder.GetOrAddBlob(ImmutableArray.Create(assemblyName.GetPublicKeyToken())), + default(AssemblyFlags), + default(BlobHandle)); + + _assemblyRefHandles[assemblyRef] = handle; + } + + return handle; + } + + private EntityHandle MakeTypeRefHandle(TypeDesc type) + { + Debug.Assert(type.IsTypeDefinition); + Debug.Assert(type is MetadataType); + + EntityHandle handle; + + if (!_typeRefOrSpecHandles.TryGetValue(type, out handle)) + { + EntityHandle scope; + MetadataType typeAsMetadataType = (MetadataType)type; + + if (typeAsMetadataType.ContainingType != null) + scope = MakeTypeRefHandle(typeAsMetadataType.ContainingType); + else + scope = MakeAssemblyReferenceHandle((IAssemblyDesc)typeAsMetadataType.Module); + + handle = _metadataBuilder.AddTypeReference( + scope, + _metadataBuilder.GetOrAddString(typeAsMetadataType.Namespace), + _metadataBuilder.GetOrAddString(typeAsMetadataType.Name)); + + _typeRefOrSpecHandles[type] = handle; + } + + return handle; + } + + private EntityHandle MakeTypeRefOrSpecHandle(TypeDesc type) + { + EntityHandle handle; + + if (!_typeRefOrSpecHandles.TryGetValue(type, out handle)) + { + if(!type.IsDefType || !type.IsTypeDefinition || type is RuntimeDeterminedType) + { + SignatureTypeEncoder sigEncoder = new SignatureTypeEncoder(new BlobBuilder()); + _signatureEmitter.EncodeTypeSignature(sigEncoder, type); + handle = _metadataBuilder.AddTypeSpecification(_metadataBuilder.GetOrAddBlob(sigEncoder.Builder)); + } + else + { + handle = MakeTypeRefHandle(type); + } + + _typeRefOrSpecHandles[type] = handle; + } + + return handle; + } + + private EntityHandle MakeMemberReferenceHandle(TypeSystemEntity methodOrField) + { + EntityHandle handle; + + if (!_memberRefOrSpecHandles.TryGetValue(methodOrField, out handle)) + { + MethodDesc method = methodOrField as MethodDesc; + FieldDesc field = methodOrField as FieldDesc; + TypeDesc owningType = (method != null ? method.OwningType : field.OwningType); + string name = (method != null ? method.Name : field.Name); + + BlobHandle signature = method != null ? + MakeSignatureHandle(method.GetTypicalMethodDefinition()) : + MakeSignatureHandle(field); + + handle = _metadataBuilder.AddMemberReference( + MakeTypeRefOrSpecHandle(owningType), + _metadataBuilder.GetOrAddString(name), + signature); + + if (method != null && method.HasInstantiation && !method.IsTypicalMethodDefinition) + { + BlobEncoder methodSpecEncoder = new BlobEncoder(new BlobBuilder()); + + GenericTypeArgumentsEncoder argEncoder = methodSpecEncoder.MethodSpecificationSignature(method.Instantiation.Length); + for (int i = 0; i < method.Instantiation.Length; i++) + { + SignatureTypeEncoder argTypeEncoder = argEncoder.AddArgument(); + _signatureEmitter.EncodeTypeSignature(argTypeEncoder, method.Instantiation[i]); + } + + handle = _metadataBuilder.AddMethodSpecification(handle, _metadataBuilder.GetOrAddBlob(methodSpecEncoder.Builder)); + } + + _memberRefOrSpecHandles[methodOrField] = handle; + } + + return handle; + } + #endregion + } + + public static class InstructionEncoderExtensions + { + public static void EmitLdToken(this InstructionEncoder code, TypeSystemEntity typeSystemEntity, ManagedBinaryEmitter emitter) + { + code.OpCode(ILOpCode.Ldtoken); + code.Token(emitter.EmitMetadataHandleForTypeSystemEntity(typeSystemEntity)); + } + public static void EmitI4Constant(this InstructionEncoder code, int value) + { + code.OpCode(ILOpCode.Ldc_i4); + code.CodeBuilder.WriteInt32(value); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/MergedAssemblyRecords.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/MergedAssemblyRecords.cs new file mode 100644 index 0000000000..bfff48ca6c --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/MergedAssemblyRecords.cs @@ -0,0 +1,95 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.IO; +using System.Reflection; + +using Internal.TypeSystem.Ecma; +using Internal.TypeSystem.TypesDebugInfo; + +namespace ILCompiler +{ + public class MergedAssemblyRecord + { + public EcmaAssembly Assembly { get; } + public string Name { get; } + public uint AssemblyIndex { get; } + public uint Timestamp { get; } + public bool HasPDB { get; } + public byte[] PublicKey { get; } + public byte[] VersionInfo { get; } + public int VersionInfoLength + { + get + { + return BitConverter.ToUInt16(VersionInfo, 0); + } + } + + public MergedAssemblyRecord(EcmaAssembly assembly, string name, uint assemblyIndex, uint timestamp, bool hasPDB, byte[] publicKey, byte[] versionInfo) + { + Assembly = assembly; + Name = name; + AssemblyIndex = assemblyIndex; + Timestamp = timestamp; + HasPDB = hasPDB; + PublicKey = publicKey; + VersionInfo = versionInfo; + + if (versionInfo.Length < sizeof(ushort)) + throw new ArgumentException("versionInfo"); + + int versionInfoLength = VersionInfoLength; + if (versionInfoLength == 0) + { + VersionInfo = BitConverter.GetBytes((ushort)sizeof(ushort)); + Debug.Assert(VersionInfoLength == sizeof(ushort)); + } + else + { + // Validate that a non-empty version info contains a VS_VERSION_INFO structure + string vsVersionInfoString = "VS_VERSION_INFO"; + + if (VersionInfoLength < (6 + vsVersionInfoString.Length * sizeof(char))) + throw new ArgumentException("versionInfo"); + + string encodedString = Encoding.Unicode.GetString(versionInfo, 6, vsVersionInfoString.Length * sizeof(char)); + if (encodedString != vsVersionInfoString) + throw new ArgumentException("versionInfo"); + } + + } + + internal void Encode(DebugInfoBlob blob) + { + blob.WriteDWORD(Timestamp); + blob.WriteDWORD(AssemblyIndex & 0x7FFFFFFF | (HasPDB ? 0x80000000 : 0)); + blob.WriteBuffer(VersionInfo, 0, VersionInfoLength); + + string nameWithPublicKey = Name; + if (PublicKey != null && PublicKey.Length > 0) + { + nameWithPublicKey += ", PublicKey="; + nameWithPublicKey += BitConverter.ToString(PublicKey).Replace("-", ""); + } + blob.WriteString(nameWithPublicKey); + blob.AlignToDWORD(); + } + } + + public class MergedAssemblyRecords + { + public IReadOnlyCollection MergedAssemblies { get; } + public uint CorLibIndex { get; } + public MergedAssemblyRecords(IReadOnlyCollection mergedAssemblies, uint corLibIndex) + { + MergedAssemblies = mergedAssemblies; + CorLibIndex = corLibIndex; + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs index 23bce180d0..ab86d40e28 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs @@ -3,14 +3,10 @@ // See the LICENSE file in the project root for more information. using System; -using System.IO; using System.Collections.Generic; -using Internal.IL.Stubs; using Internal.TypeSystem; -using Internal.TypeSystem.Interop; -using ILCompiler.Metadata; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; @@ -26,7 +22,7 @@ namespace ILCompiler /// module. It also helps facilitate mappings between generated runtime structures or code, /// and the native metadata. /// - public abstract class MetadataManager + public abstract class MetadataManager : ICompilationRootProvider { internal const int MetadataOffsetMask = 0xFFFFFF; @@ -34,8 +30,8 @@ namespace ILCompiler private List> _typeMappings; private List> _fieldMappings; private List> _methodMappings; + private List> _stackTraceMappings; - protected readonly CompilationModuleGroup _compilationModuleGroup; protected readonly CompilerTypeSystemContext _typeSystemContext; protected readonly MetadataBlockingPolicy _blockingPolicy; @@ -44,15 +40,16 @@ namespace ILCompiler private HashSet _typesWithConstructedEETypesGenerated = new HashSet(); private HashSet _methodsGenerated = new HashSet(); private HashSet _genericDictionariesGenerated = new HashSet(); - private List _modulesWithMetadata = new List(); + private HashSet _methodBodiesGenerated = new HashSet(); private List _typeGVMEntries = new List(); + private HashSet _defaultConstructorsNeeded = new HashSet(); internal NativeLayoutInfoNode NativeLayoutInfo { get; private set; } internal DynamicInvokeTemplateDataNode DynamicInvokeTemplateData { get; private set; } + public virtual bool SupportsReflection => true; - public MetadataManager(CompilationModuleGroup compilationModuleGroup, CompilerTypeSystemContext typeSystemContext, MetadataBlockingPolicy blockingPolicy) + public MetadataManager(CompilerTypeSystemContext typeSystemContext, MetadataBlockingPolicy blockingPolicy) { - _compilationModuleGroup = compilationModuleGroup; _typeSystemContext = typeSystemContext; _blockingPolicy = blockingPolicy; } @@ -62,19 +59,18 @@ namespace ILCompiler graph.NewMarkedNode += Graph_NewMarkedNode; } - private static ReadyToRunSectionType BlobIdToReadyToRunSection(ReflectionMapBlob blobId) + internal static ReadyToRunSectionType BlobIdToReadyToRunSection(ReflectionMapBlob blobId) { var result = (ReadyToRunSectionType)((int)blobId + (int)ReadyToRunSectionType.ReadonlyBlobRegionStart); Debug.Assert(result <= ReadyToRunSectionType.ReadonlyBlobRegionEnd); return result; } - public void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory) + public void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode) { var metadataNode = new MetadataNode(); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.EmbeddedMetadata), metadataNode, metadataNode, metadataNode.EndSymbol); - var commonFixupsTableNode = new ExternalReferencesTableNode("CommonFixupsTable", nodeFactory); var nativeReferencesTableNode = new ExternalReferencesTableNode("NativeReferences", nodeFactory); var nativeStaticsTableNode = new ExternalReferencesTableNode("NativeStatics", nodeFactory); @@ -83,7 +79,7 @@ namespace ILCompiler var resourceIndexNode = new ResourceIndexNode(resourceDataNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.BlobIdResourceIndex), resourceIndexNode, resourceIndexNode, resourceIndexNode.EndSymbol); - + var typeMapNode = new TypeMetadataMapNode(commonFixupsTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.TypeMap), typeMapNode, typeMapNode, typeMapNode.EndSymbol); @@ -97,9 +93,6 @@ namespace ILCompiler var invokeMapNode = new ReflectionInvokeMapNode(commonFixupsTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.InvokeMap), invokeMapNode, invokeMapNode, invokeMapNode.EndSymbol); - var delegateMapNode = new DelegateMarshallingStubMapNode(commonFixupsTableNode); - header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.DelegateMarshallingStubMap), delegateMapNode, delegateMapNode, delegateMapNode.EndSymbol); - var arrayMapNode = new ArrayMapNode(commonFixupsTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.ArrayMap), arrayMapNode, arrayMapNode, arrayMapNode.EndSymbol); @@ -139,13 +132,24 @@ namespace ILCompiler var virtualInvokeMapNode = new ReflectionVirtualInvokeMapNode(commonFixupsTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.VirtualInvokeMap), virtualInvokeMapNode, virtualInvokeMapNode, virtualInvokeMapNode.EndSymbol); + var defaultConstructorMapNode = new DefaultConstructorMapNode(commonFixupsTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.DefaultConstructorMap), defaultConstructorMapNode, defaultConstructorMapNode, defaultConstructorMapNode.EndSymbol); + +#if !CORERT + var stackTraceEmbeddedMetadataNode = new StackTraceEmbeddedMetadataNode(); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.BlobIdStackTraceEmbeddedMetadata), stackTraceEmbeddedMetadataNode, stackTraceEmbeddedMetadataNode, stackTraceEmbeddedMetadataNode.EndSymbol); +#endif + + var stackTraceMethodMappingNode = new StackTraceMethodMappingNode(); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.BlobIdStackTraceMethodRvaToTokenMapping), stackTraceMethodMappingNode, stackTraceMethodMappingNode, stackTraceMethodMappingNode.EndSymbol); + // The external references tables should go last header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.CommonFixupsTable), commonFixupsTableNode, commonFixupsTableNode, commonFixupsTableNode.EndSymbol); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeReferences), nativeReferencesTableNode, nativeReferencesTableNode, nativeReferencesTableNode.EndSymbol); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeStatics), nativeStaticsTableNode, nativeStaticsTableNode, nativeStaticsTableNode.EndSymbol); } - private void Graph_NewMarkedNode(DependencyNodeCore obj) + protected virtual void Graph_NewMarkedNode(DependencyNodeCore obj) { var eetypeNode = obj as EETypeNode; if (eetypeNode != null) @@ -160,13 +164,16 @@ namespace ILCompiler return; } - IMethodNode methodNode = obj as MethodCodeNode; + IMethodBodyNode methodBodyNode = obj as IMethodBodyNode; + if (methodBodyNode != null) + { + _methodBodiesGenerated.Add(methodBodyNode); + } + + IMethodNode methodNode = methodBodyNode; if (methodNode == null) methodNode = obj as ShadowConcreteMethodNode; - if (methodNode == null) - methodNode = obj as NonExternMethodSymbolNode; - if (methodNode != null) { _methodsGenerated.Add(methodNode.Method); @@ -197,11 +204,10 @@ namespace ILCompiler _genericDictionariesGenerated.Add(dictionaryNode); } - // TODO: temporary until we have an IL scanning Metadata Manager. We shouldn't have to keep track of these. - var moduleMetadataNode = obj as ModuleMetadataNode; - if (moduleMetadataNode != null) + var ctorFromLazyGenericsNode = obj as DefaultConstructorFromLazyNode; + if (ctorFromLazyGenericsNode != null) { - _modulesWithMetadata.Add(moduleMetadataNode.Module); + _defaultConstructorsNeeded.Add(ctorFromLazyGenericsNode); } } @@ -251,13 +257,13 @@ namespace ILCompiler // Methods that return ByRef-like types or take them by reference can't be reflection invoked // ---------------------------------------------------------------- - if (signature.ReturnType.IsDefType && ((DefType)signature.ReturnType).IsByRefLike) + if (signature.ReturnType.IsByRefLike) return false; for (int i = 0; i < signature.Length; i++) { ByRefType paramType = signature[i] as ByRefType; - if (paramType != null && paramType.ParameterType.IsDefType && ((DefType)paramType.ParameterType).IsByRefLike) + if (paramType != null && paramType.ParameterType.IsByRefLike) return false; } @@ -289,10 +295,6 @@ namespace ILCompiler // String constructors are intrinsic and special cased in runtime reflection if (owningType.IsString) return false; - - // IntPtr/UIntPtr constructors are intrinsic and special cased in runtime reflection - if (owningType.IsWellKnownType(WellKnownType.IntPtr) || owningType.IsWellKnownType(WellKnownType.UIntPtr)) - return false; } // Everything else can go in the mapping table. @@ -310,6 +312,22 @@ namespace ILCompiler return HasReflectionInvokeStubForInvokableMethod(method); } + /// + /// Is there a reflection invoke stub for a method that is invokable? + /// + public bool ShouldMethodBeInInvokeMap(MethodDesc method) + { + // The current format requires us to have an EEType for the owning type. We might want to lift this. + if (!TypeGeneratesEEType(method.OwningType)) + return false; + + // We have a method body, we have a metadata token, but we can't get an invoke stub. Bail. + if (!IsReflectionInvokable(method)) + return false; + + return true; + } + /// /// This method is an extension point that can provide additional metadata-based dependencies to compiled method bodies. /// @@ -332,6 +350,21 @@ namespace ILCompiler } } + /// + /// This method is an extension point that can provide additional metadata-based dependencies on a virtual method. + /// + public void GetDependenciesDueToVirtualMethodReflectability(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + // If we have a use of an abstract method, GetDependenciesDueToReflectability is not going to see the method + // as being used since there's no body. We inject a dependency on a new node that serves as a logical method body + // for the metadata manager. Metadata manager treats that node the same as a body. + if (method.IsAbstract && GetMetadataCategory(method) != 0) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(factory.ReflectableMethod(method), "Abstract reflectable method"); + } + } + protected virtual void GetMetadataDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) { // MetadataManagers can override this to provide additional dependencies caused by the emission of metadata @@ -355,8 +388,9 @@ namespace ILCompiler { // We're going to generate a mapping table entry for this. Collect dependencies. - // Nothing for now - the mapping table only refers to the EEType and we already generated one because - // we got the callback. + // Nothing special is needed for the mapping table (we only emit the EEType and we already + // have one, since we got this callback). But check if a child wants to do something extra. + GetRuntimeMappingDependenciesDueToReflectability(ref dependencies, factory, type); } } @@ -367,6 +401,30 @@ namespace ILCompiler // and property setters) } + protected virtual void GetRuntimeMappingDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + // MetadataManagers can override this to provide additional dependencies caused by the emission of a runtime + // mapping for a type. + } + + /// + /// This method is an extension point that can provide additional metadata-based dependencies to generated RuntimeMethodHandles. + /// + public virtual void GetDependenciesDueToLdToken(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + // MetadataManagers can override this to provide additional dependencies caused by the presence of a + // RuntimeMethodHandle data structure. + } + + /// + /// This method is an extension point that can provide additional metadata-based dependencies to generated RuntimeFieldHandles. + /// + public virtual void GetDependenciesDueToLdToken(ref DependencyList dependencies, NodeFactory factory, FieldDesc field) + { + // MetadataManagers can override this to provide additional dependencies caused by the presence of a + // RuntimeFieldHandle data structure. + } + /// /// Given that a method is invokable, does there exist a reflection invoke stub? /// @@ -488,30 +546,33 @@ namespace ILCompiler return instantiatedDynamicInvokeMethod; } - private void EnsureMetadataGenerated(NodeFactory factory) + protected void EnsureMetadataGenerated(NodeFactory factory) { if (_metadataBlob != null) return; - ComputeMetadata(factory, out _metadataBlob, out _typeMappings, out _methodMappings, out _fieldMappings); + ComputeMetadata(factory, out _metadataBlob, out _typeMappings, out _methodMappings, out _fieldMappings, out _stackTraceMappings); + } + + void ICompilationRootProvider.AddCompilationRoots(IRootingServiceProvider rootProvider) + { + // MetadataManagers can override this to provide metadata compilation roots that need to be added to the graph ahead of time. + // (E.g. reflection roots computed by IL analyzers, or non-compilation-based roots) } protected abstract void ComputeMetadata(NodeFactory factory, out byte[] metadataBlob, out List> typeMappings, out List> methodMappings, - out List> fieldMappings); + out List> fieldMappings, + out List> stackTraceMapping); /// /// Returns a set of modules that will get some metadata emitted into the output module /// - public virtual IEnumerable GetCompilationModulesWithMetadata() - { - // TODO: this is temporary until we have a metadata manager for IL scanner. This method should be abstract. - return _modulesWithMetadata; - } + public abstract IEnumerable GetCompilationModulesWithMetadata(); public byte[] GetMetadataBlob(NodeFactory factory) { @@ -537,6 +598,12 @@ namespace ILCompiler return _fieldMappings; } + public IEnumerable> GetStackTraceMapping(NodeFactory factory) + { + EnsureMetadataGenerated(factory); + return _stackTraceMappings; + } + internal IEnumerable GetCctorContextMapping() { return _cctorContextsGenerated; @@ -547,7 +614,7 @@ namespace ILCompiler return _typeGVMEntries; } - internal IEnumerable GetCompiledGenericDictionaries() + internal IReadOnlyCollection GetCompiledGenericDictionaries() { return _genericDictionariesGenerated; } @@ -557,6 +624,16 @@ namespace ILCompiler return _methodsGenerated; } + internal IEnumerable GetCompiledMethodBodies() + { + return _methodBodiesGenerated; + } + + internal IEnumerable GetDefaultConstructorsNeeded() + { + return _defaultConstructorsNeeded; + } + internal bool TypeGeneratesEEType(TypeDesc type) { return _typesWithEETypesGenerated.Contains(type); diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/MethodExtensions.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/MethodExtensions.cs index 57cfa1786e..6b259d5a12 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/MethodExtensions.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/MethodExtensions.cs @@ -2,6 +2,7 @@ // 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 Internal.TypeSystem; using Internal.TypeSystem.Ecma; @@ -76,5 +77,52 @@ namespace ILCompiler { return method.IsPInvoke && ((method is Internal.IL.Stubs.PInvokeTargetNativeMethod) || Internal.IL.McgInteropSupport.IsPregeneratedInterop(method)); } + + /// + /// What is the maximum number of steps that need to be taken from this type to its most contained generic type. + /// i.e. + /// SomeGenericType<System.Int32>.Method<System.Int32> => 1 + /// SomeType.Method<System.Int32> => 0 + /// SomeType.Method<List<System.Int32>> => 1 + /// + public static int GetGenericDepth(this MethodDesc method) + { + int genericDepth = method.OwningType.GetGenericDepth(); + foreach (TypeDesc type in method.Instantiation) + { + genericDepth = Math.Max(genericDepth, type.GetGenericDepth()); + } + return genericDepth; + } + + /// + /// Determine if a type has a generic depth greater than a given value + /// + /// + /// + public static bool IsGenericDepthGreaterThan(this MethodDesc method, int depth) + { + if (method.OwningType.IsGenericDepthGreaterThan(depth)) + return true; + + foreach (TypeDesc type in method.Instantiation) + { + if (type.IsGenericDepthGreaterThan(depth)) + return true; + } + + return false; + } + + /// + /// Gets fully canonicalized method if method is canonical. + /// + public static MethodDesc Normalize(this MethodDesc method) + { + // If method is Foo<__Canon,object>::Method, we get Foo<__Canon,__Canon>::Method. + if (method.IsCanonicalMethod(CanonicalFormKind.Any)) + return method.GetCanonMethodTarget(CanonicalFormKind.Specific); + return method; + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/MultiFileCompilationModuleGroup.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/MultiFileCompilationModuleGroup.cs index 727d61d7fb..9541e4285e 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/MultiFileCompilationModuleGroup.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/MultiFileCompilationModuleGroup.cs @@ -40,7 +40,12 @@ namespace ILCompiler return true; } - public sealed override bool ContainsMethodBody(MethodDesc method) + public sealed override bool ContainsTypeDictionary(TypeDesc type) + { + return ContainsType(type); + } + + public sealed override bool ContainsMethodBody(MethodDesc method, bool unboxingStub) { if (method.HasInstantiation) return true; @@ -51,22 +56,27 @@ namespace ILCompiler public sealed override bool ContainsMethodDictionary(MethodDesc method) { Debug.Assert(method.GetCanonMethodTarget(CanonicalFormKind.Specific) != method); - return ContainsMethodBody(method); + return ContainsMethodBody(method, false); } - public sealed override bool ExportsType(TypeDesc type) + public sealed override ExportForm GetExportTypeForm(TypeDesc type) { - return false; + return ExportForm.None; } - public sealed override bool ExportsMethod(MethodDesc method) + public sealed override ExportForm GetExportTypeFormDictionary(TypeDesc type) { - return false; + return ExportForm.None; } - public override bool ExportsMethodDictionary(MethodDesc method) + public sealed override ExportForm GetExportMethodForm(MethodDesc method, bool unboxingStub) { - return false; + return ExportForm.None; + } + + public override ExportForm GetExportMethodDictionaryForm(MethodDesc method) + { + return ExportForm.None; } private bool IsModuleInCompilationGroup(EcmaModule module) @@ -115,5 +125,11 @@ namespace ILCompiler { return ShouldProduceFullVTable(type); } + + public override bool PresenceOfEETypeImpliesAllMethodsOnType(TypeDesc type) + { + return (type.HasInstantiation || type.IsArray) && ShouldProduceFullVTable(type) && + type.ConvertToCanonForm(CanonicalFormKind.Specific).IsCanonicalSubtype(CanonicalFormKind.Any); + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/NativeLibraryInitializerRootProvider.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/NativeLibraryInitializerRootProvider.cs new file mode 100644 index 0000000000..d7460d45ac --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/NativeLibraryInitializerRootProvider.cs @@ -0,0 +1,39 @@ +// 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.Collections.Generic; + +using Internal.TypeSystem.Ecma; +using Internal.TypeSystem; +using Internal.IL.Stubs.StartupCode; + +namespace ILCompiler +{ + /// + /// Provides compilation group for a native library that compiles the initialize method. + /// + public class NativeLibraryInitializerRootProvider : ICompilationRootProvider + { + /// + /// Symbolic name under which the managed initializer is exported. + /// + public const string ManagedEntryPointMethodName = "__managed__Startup"; + + private EcmaModule _module; + private IList _libraryInitializers; + + public NativeLibraryInitializerRootProvider(EcmaModule module, IList libraryInitializers) + { + _module = module; + _libraryInitializers = libraryInitializers; + } + + public void AddCompilationRoots(IRootingServiceProvider rootProvider) + { + TypeDesc owningType = _module.GetGlobalModuleType(); + NativeLibraryStartupMethod nativeLibStartupCode = new NativeLibraryStartupMethod(owningType, _libraryInitializers); + rootProvider.AddCompilationRoot(nativeLibStartupCode, "Startup Code Main Method", ManagedEntryPointMethodName); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/ObjectDumper.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/ObjectDumper.cs index 2dc9b64a24..194d926934 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/ObjectDumper.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/ObjectDumper.cs @@ -4,6 +4,7 @@ using System; using System.IO; +using System.Security.Cryptography; using System.Xml; using Internal.Text; @@ -17,7 +18,7 @@ namespace ILCompiler public class ObjectDumper : IObjectDumper { private readonly string _fileName; - + private SHA256 _sha256; private XmlWriter _writer; public ObjectDumper(string fileName) @@ -33,6 +34,7 @@ namespace ILCompiler Indent = true, }; + _sha256 = SHA256.Create(); _writer = XmlWriter.Create(File.CreateText(_fileName), settings); _writer.WriteStartElement("ObjectNodes"); } @@ -69,7 +71,7 @@ namespace ILCompiler } _writer.WriteAttributeString("Length", objectData.Data.Length.ToStringInvariant()); - + _writer.WriteAttributeString("Hash", HashData(objectData.Data)); _writer.WriteEndElement(); var nodeWithCodeInfo = node as INodeWithCodeInfo; @@ -78,6 +80,7 @@ namespace ILCompiler _writer.WriteStartElement("GCInfo"); _writer.WriteAttributeString("Name", name); _writer.WriteAttributeString("Length", nodeWithCodeInfo.GCInfo.Length.ToStringInvariant()); + _writer.WriteAttributeString("Hash", HashData(nodeWithCodeInfo.GCInfo)); _writer.WriteEndElement(); if (nodeWithCodeInfo.EHInfo != null) @@ -85,11 +88,17 @@ namespace ILCompiler _writer.WriteStartElement("EHInfo"); _writer.WriteAttributeString("Name", name); _writer.WriteAttributeString("Length", nodeWithCodeInfo.EHInfo.Data.Length.ToStringInvariant()); + _writer.WriteAttributeString("Hash", HashData(nodeWithCodeInfo.EHInfo.Data)); _writer.WriteEndElement(); } } } + private string HashData(byte[] data) + { + return BitConverter.ToString(_sha256.ComputeHash(data)).Replace("-", "").ToLower(); + } + internal void End() { _writer.WriteEndElement(); diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/PreInitFieldInfo.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/PreInitFieldInfo.cs index db5f67dcce..2875d0e5f6 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/PreInitFieldInfo.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/PreInitFieldInfo.cs @@ -8,37 +8,182 @@ using Debug = System.Diagnostics.Debug; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; +using ILCompiler.DependencyAnalysis; namespace ILCompiler { + public abstract class PreInitFixupInfo : IComparable + { + /// + /// Offset into the blob + /// + public int Offset { get; } + + public PreInitFixupInfo(int offset) + { + Offset = offset; + } + + int IComparable.CompareTo(PreInitFixupInfo other) + { + return this.Offset - other.Offset; + } + + /// + /// Writes fixup data into current ObjectDataBuilder. Caller needs to make sure ObjectDataBuilder is + /// at correct offset before writing. + /// + public abstract void WriteData(ref ObjectDataBuilder builder, NodeFactory factory); + } + + public class PreInitTypeFixupInfo : PreInitFixupInfo + { + public TypeDesc TypeFixup { get; } + + public PreInitTypeFixupInfo(int offset, TypeDesc type) + :base(offset) + { + TypeFixup = type; + } + + public override void WriteData(ref ObjectDataBuilder builder, NodeFactory factory) + { + builder.EmitPointerRelocOrIndirectionReference(factory.MaximallyConstructableType(TypeFixup)); + } + } + + public class PreInitMethodFixupInfo : PreInitFixupInfo + { + public MethodDesc MethodFixup { get; } + + public PreInitMethodFixupInfo(int offset, MethodDesc method) + : base(offset) + { + MethodFixup = method; + } + + public override void WriteData(ref ObjectDataBuilder builder, NodeFactory factory) + { + builder.EmitPointerReloc(factory.ExactCallableAddress(MethodFixup)); + } + } + + public class PreInitFieldFixupInfo : PreInitFixupInfo + { + public FieldDesc FieldFixup { get; } + + public PreInitFieldFixupInfo(int offset, FieldDesc field) + : base(offset) + { + FieldFixup = field; + } + + public override void WriteData(ref ObjectDataBuilder builder, NodeFactory factory) + { + MetadataType type = (MetadataType)FieldFixup.OwningType; + + // Do not support fixing up fields from external modules + if (!factory.CompilationModuleGroup.ContainsType(type)) + throw new BadImageFormatException(); + + ISymbolNode staticBase = FieldFixup.HasGCStaticBase ? factory.TypeGCStaticsSymbol(type) : factory.TypeNonGCStaticsSymbol(type); + builder.EmitPointerReloc(staticBase, FieldFixup.Offset.AsInt); + } + } + public class PreInitFieldInfo { public FieldDesc Field { get; } + /// + /// The type of the real field data. This could be a subtype of the field type. + /// + public TypeDesc Type { get; } + /// /// Points to the underlying contents of the data. /// public byte[] Data { get; } + /// + /// Start offset of the real contents in the data. + /// + public int Offset { get; } + /// /// Number of elements, if this is a frozen array. /// public int Length { get; } - public PreInitFieldInfo(FieldDesc field, byte[] data, int length) + /// + /// List of fixup to be apply to the data blob + /// This is needed for information that can't be encoded into blob ahead of time before codegen + /// + private List FixupInfos; + + public PreInitFieldInfo(FieldDesc field, TypeDesc type, byte[] data, int offset, int length, List fixups) { Field = field; + Type = type; Data = data; + Offset = offset; Length = length; + FixupInfos = fixups; + + if (FixupInfos != null) + FixupInfos.Sort(); } - public static List GetPreInitFieldInfos(TypeDesc type) + public void WriteData(ref ObjectDataBuilder builder, NodeFactory factory, bool relocsOnly = false) + { + int offset = Offset; + + if (FixupInfos != null) + { + int startOffset = builder.CountBytes; + + for (int i = 0; i < FixupInfos.Count; ++i) + { + var fixupInfo = FixupInfos[i]; + + // do we have overlapping fixups? + if (fixupInfo.Offset < offset) + throw new BadImageFormatException(); + + if (!relocsOnly) + { + // emit bytes before fixup + builder.EmitBytes(Data, offset, fixupInfo.Offset - offset); + } + + // write the fixup + FixupInfos[i].WriteData(ref builder, factory); + + // move pointer past the fixup + offset = Offset + builder.CountBytes - startOffset; + } + } + + if (offset > Data.Length) + throw new BadImageFormatException(); + + if (!relocsOnly) + { + // Emit remaining bytes + builder.EmitBytes(Data, offset, Data.Length - offset); + } + } + + public static List GetPreInitFieldInfos(TypeDesc type, bool hasGCStaticBase) { List list = null; foreach (var field in type.GetFields()) { - if (!field.IsStatic) + if (!field.IsStatic || field.IsThreadStatic) + continue; + + if (field.HasGCStaticBase != hasGCStaticBase) continue; var dataField = GetPreInitDataField(field); @@ -64,26 +209,27 @@ namespace ILCompiler if (field == null) return null; - if (!field.HasCustomAttribute("System.Runtime.CompilerServices", "PreInitializedAttribute")) - return null; - var decoded = field.GetDecodedCustomAttribute("System.Runtime.CompilerServices", "InitDataBlobAttribute"); if (decoded == null) return null; var decodedValue = decoded.Value; if (decodedValue.FixedArguments.Length != 2) - return null; + throw new BadImageFormatException(); var typeDesc = decodedValue.FixedArguments[0].Value as TypeDesc; if (typeDesc == null) - return null; + throw new BadImageFormatException(); if (decodedValue.FixedArguments[1].Type != field.Context.GetWellKnownType(WellKnownType.String)) - return null; + throw new BadImageFormatException(); var fieldName = (string)decodedValue.FixedArguments[1].Value; - return typeDesc.GetField(fieldName); + var dataField = typeDesc.GetField(fieldName); + if (dataField== null) + throw new BadImageFormatException(); + + return dataField; } /// @@ -91,26 +237,156 @@ namespace ILCompiler /// private static PreInitFieldInfo ConstructPreInitFieldInfo(FieldDesc field, FieldDesc dataField) { - var arrType = field.FieldType as ArrayType; - if (arrType == null || !arrType.IsSzArray) - { - // We only support single dimensional arrays - throw new NotSupportedException(); - } - if (!dataField.HasRva) throw new BadImageFormatException(); - + var ecmaDataField = dataField as EcmaField; if (ecmaDataField == null) throw new NotSupportedException(); - - var rvaData = ecmaDataField.GetFieldRvaData(); - int elementSize = arrType.ElementType.GetElementSize().AsInt; - if (rvaData.Length % elementSize != 0) - throw new BadImageFormatException(); - return new PreInitFieldInfo(field, rvaData, rvaData.Length / elementSize); + var rvaData = ecmaDataField.GetFieldRvaData(); + var fieldType = field.FieldType; + int elementCount; + int realDataOffset; + TypeDesc realDataType = null; + + // + // Construct fixups + // + List fixups = null; + + var typeFixupAttrs = ecmaDataField.GetDecodedCustomAttributes("System.Runtime.CompilerServices", "TypeHandleFixupAttribute"); + foreach (var typeFixupAttr in typeFixupAttrs) + { + if (typeFixupAttr.FixedArguments[0].Type != field.Context.GetWellKnownType(WellKnownType.Int32)) + throw new BadImageFormatException(); + + int offset = (int)typeFixupAttr.FixedArguments[0].Value; + var typeArg = typeFixupAttr.FixedArguments[1].Value; + var fixupType = typeArg as TypeDesc; + if (fixupType == null) + { + if (typeArg is string fixupTypeName) + { + fixupType = CustomAttributeTypeNameParser.GetTypeByCustomAttributeTypeName(ecmaDataField.Module, fixupTypeName, throwIfNotFound: true); + } + else + { + throw new BadImageFormatException(); + } + } + + fixups = fixups ?? new List(); + + if (offset == 0 && fieldType.IsSzArray) + { + // For array field, offset 0 is the element type handle followed by the element count + realDataType = fixupType; + } + else + { + fixups.Add(new PreInitTypeFixupInfo(offset, fixupType)); + } + } + + var methodFixupAttrs = ecmaDataField.GetDecodedCustomAttributes("System.Runtime.CompilerServices", "MethodAddrFixupAttribute"); + foreach (var methodFixupAttr in methodFixupAttrs) + { + if (methodFixupAttr.FixedArguments[0].Type != field.Context.GetWellKnownType(WellKnownType.Int32)) + throw new BadImageFormatException(); + + int offset = (int)methodFixupAttr.FixedArguments[0].Value; + TypeDesc fixupType = methodFixupAttr.FixedArguments[1].Value as TypeDesc; + if (fixupType == null) + throw new BadImageFormatException(); + + string methodName = methodFixupAttr.FixedArguments[2].Value as string; + if (methodName == null) + throw new BadImageFormatException(); + + var method = fixupType.GetMethod(methodName, signature : null); + if (method == null) + throw new BadImageFormatException(); + + fixups = fixups ?? new List(); + + fixups.Add(new PreInitMethodFixupInfo(offset, method)); + } + + var fieldFixupAttrs = ecmaDataField.GetDecodedCustomAttributes("System.Runtime.CompilerServices", "FieldAddrFixupAttribute"); + foreach (var fieldFixupAttr in fieldFixupAttrs) + { + if (fieldFixupAttr.FixedArguments[0].Type != field.Context.GetWellKnownType(WellKnownType.Int32)) + throw new BadImageFormatException(); + + int offset = (int)fieldFixupAttr.FixedArguments[0].Value; + TypeDesc fixupType = fieldFixupAttr.FixedArguments[1].Value as TypeDesc; + if (fixupType == null) + throw new BadImageFormatException(); + + string fieldName = fieldFixupAttr.FixedArguments[2].Value as string; + if (fieldName == null) + throw new BadImageFormatException(); + + var fixupField = fixupType.GetField(fieldName); + if (fixupField == null) + throw new BadImageFormatException(); + + if (!fixupField.IsStatic) + throw new BadImageFormatException(); + + fixups = fixups ?? new List(); + + fixups.Add(new PreInitFieldFixupInfo(offset, fixupField)); + } + + if (fieldType.IsValueType || fieldType.IsPointer) + { + elementCount = -1; + realDataOffset = 0; + realDataType = fieldType; + } + else if (fieldType.IsSzArray) + { + // Offset 0 is the element type handle fixup followed by the element count + if (realDataType == null) + throw new BadImageFormatException(); + + int ptrSize = fieldType.Context.Target.PointerSize; + elementCount = rvaData[ptrSize] | rvaData[ptrSize + 1] << 8 | rvaData[ptrSize + 2] << 16 | rvaData[ptrSize + 3] << 24; + realDataOffset = ptrSize * 2; + realDataType = realDataType.MakeArrayType(); + } + else + { + throw new NotSupportedException(); + } + + return new PreInitFieldInfo(field, realDataType, rvaData, realDataOffset, elementCount, fixups); + } + + public static int FieldDescCompare(PreInitFieldInfo fieldInfo1, PreInitFieldInfo fieldInfo2) + { + return fieldInfo1.Field.Offset.AsInt - fieldInfo2.Field.Offset.AsInt; + } + + public int CompareTo(PreInitFieldInfo other, TypeSystemComparer comparer) + { + if (Length != other.Length) + return Length - other.Length; + + var compare = comparer.Compare(Field, other.Field); + if (compare != 0) + return compare; + + Debug.Assert(Data.Length == other.Data.Length); + for (int i = 0; i < Length; i++) + { + if (Data[i] != other.Data[i]) + return Data[i] - other.Data[i]; + } + + return 0; } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/PrecomputedMetadataManager.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/PrecomputedMetadataManager.cs index 9678996dd7..7e0cad821c 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/PrecomputedMetadataManager.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/PrecomputedMetadataManager.cs @@ -4,6 +4,7 @@ using System; using System.IO; +using System.Linq; using System.Collections.Generic; using System.Collections.Immutable; @@ -11,6 +12,7 @@ using Internal.Compiler; using Internal.IL; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; +using Internal.Metadata.NativeFormat.Writer; using ILCompiler.DependencyAnalysisFramework; using ILCompiler.Metadata; @@ -20,7 +22,7 @@ using Debug = System.Diagnostics.Debug; namespace ILCompiler { - class PrecomputedMetadataManager : MetadataManager + class PrecomputedMetadataManager : MetadataManager, ICompilationRootProvider { private const string MetadataMappingTypeName = "_$ILCT$+$ILT$ReflectionMapping$"; @@ -34,28 +36,48 @@ namespace ILCompiler public Dictionary MethodMappings = new Dictionary(); public Dictionary FieldMappings = new Dictionary(); public HashSet DynamicInvokeCompiledMethods = new HashSet(); + public HashSet RequiredGenericTypes = new HashSet(); + public HashSet RequiredGenericMethods = new HashSet(); + public HashSet RequiredGenericFields = new HashSet(); + public HashSet RequiredTemplateTypes = new HashSet(); + public HashSet RequiredTemplateMethods = new HashSet(); + public HashSet RequiredTemplateFields = new HashSet(); } private readonly ModuleDesc _metadataDescribingModule; private readonly HashSet _compilationModules; + private readonly HashSet _metadataOnlyAssemblies; private readonly Lazy _loadedMetadata; private Lazy> _dynamicInvokeStubs; private readonly byte[] _metadataBlob; + private readonly StackTraceEmissionPolicy _stackTraceEmissionPolicy; + private byte[] _stackTraceBlob; + private readonly CompilationModuleGroup _compilationModuleGroup; - public PrecomputedMetadataManager(CompilationModuleGroup group, CompilerTypeSystemContext typeSystemContext, ModuleDesc metadataDescribingModule, IEnumerable compilationModules, byte[] metadataBlob) - : base(group, typeSystemContext, new AttributeSpecifiedBlockingPolicy()) + public PrecomputedMetadataManager( + CompilationModuleGroup group, + CompilerTypeSystemContext typeSystemContext, + ModuleDesc metadataDescribingModule, + IEnumerable compilationModules, + IEnumerable inputMetadataOnlyAssemblies, + byte[] metadataBlob, + StackTraceEmissionPolicy stackTraceEmissionPolicy) + : base(typeSystemContext, new AttributeSpecifiedBlockingPolicy()) { + _compilationModuleGroup = group; _metadataDescribingModule = metadataDescribingModule; _compilationModules = new HashSet(compilationModules); + _metadataOnlyAssemblies = new HashSet(inputMetadataOnlyAssemblies); _loadedMetadata = new Lazy(LoadMetadata); _dynamicInvokeStubs = new Lazy>(LoadDynamicInvokeStubs); _metadataBlob = metadataBlob; + _stackTraceEmissionPolicy = stackTraceEmissionPolicy; } /// /// Read a method that describes the type system to metadata mappings. /// - private void ReadMetadataMethod(MethodIL methodOfMappings, ref Dictionary typeMappings, ref Dictionary methodMappings, ref Dictionary fieldMappings, ref HashSet metadataModules) + private void ReadMetadataMethod(MethodIL methodOfMappings, Dictionary typeMappings, Dictionary methodMappings, Dictionary fieldMappings, HashSet metadataModules) { ILStreamReader il = new ILStreamReader(methodOfMappings); // structure is @@ -85,8 +107,12 @@ namespace ILCompiler else if (tse is MethodDesc) { MethodDesc method = (MethodDesc)tse; - metadataModules.Add(((MetadataType)method.OwningType).Module); - methodMappings.Add(method, metadataTokenValue); + // Finalizers are called via a field on the EEType. They should not be reflectable. + if (!method.IsFinalizer) + { + metadataModules.Add(((MetadataType)method.OwningType).Module); + methodMappings.Add(method, metadataTokenValue); + } } else if (tse is FieldDesc) { @@ -106,6 +132,149 @@ namespace ILCompiler } } + private IEnumerable ReadRequiredGenericsEntities(MethodIL method) + { + ILStreamReader il = new ILStreamReader(method); + // structure is + // REPEAT N TIMES + //ldtoken generic type/method/field + //pop + while (true) + { + if (il.TryReadRet()) // ret + yield break; + + TypeSystemEntity tse; + il.TryReadLdtokenAsTypeSystemEntity(out tse); + il.ReadPop(); + + if (tse == null) + throw new BadImageFormatException(); + + if (tse is TypeDesc) + { + if (tse is DefType) + { + if (((TypeDesc)tse).Instantiation.CheckValidInstantiationArguments() && ((TypeDesc)tse).CheckConstraints()) + yield return tse; + } + else if (tse is ByRefType) + { + // Skip by ref types. + } + else + throw new BadImageFormatException(); + } + else if (tse is FieldDesc) + { + TypeDesc owningType = ((FieldDesc)tse).OwningType; + + if (owningType.Instantiation.CheckValidInstantiationArguments() && owningType.CheckConstraints()) + yield return tse; + } + else if (tse is MethodDesc) + { + MethodDesc genericMethod = (MethodDesc)tse; + + if (genericMethod.Instantiation.CheckValidInstantiationArguments() && + genericMethod.OwningType.Instantiation.CheckValidInstantiationArguments() && + genericMethod.CheckConstraints()) + { + // TODO: Detect large number of instantiations of the same method and collapse to using dynamic + // USG instantiations at runtime, to avoid infinite generic expansion and large compilation times. + yield return tse; + } + } + } + } + + private Instantiation GetUniversalCanonicalInstantiation(int numArgs) + { + TypeDesc[] args = new TypeDesc[numArgs]; + for (int i = 0; i < numArgs; i++) + args[i] = _typeSystemContext.UniversalCanonType; + return new Instantiation(args); + } + + private void ReadRequiredTemplates(MethodIL methodIL, HashSet typeTemplates, HashSet methodTemplates, HashSet fieldTemplates) + { + ILStreamReader il = new ILStreamReader(methodIL); + + if (!_typeSystemContext.SupportsUniversalCanon) + return; + + // + // Types, methods and field tokens listed here are *open* generic definitions determined by the reducer as needing reflection. + // Type tokens listed are tokens of generic type definitions that will need reflection support. + // Method tokens listed are either tokens of generic method definitions, or non-generic methods on generic containing type definitions, and need reflection support. + // Field tokens listed are for fields on generic type definitions that will need reflection support + // The canonical form supported by the typesystem context will determine how to instantiate the types/methods/fields listed here, and templatize them for + // reflection support at runtime (templates used by the dynamic TypeLoader component of the runtime). + // + + // structure is + // REPEAT N TIMES + //ldtoken type + //pop + while (true) + { + if (il.TryReadRet()) // ret + break; + + TypeSystemEntity tse; + il.TryReadLdtokenAsTypeSystemEntity(out tse); + il.ReadPop(); + + if (tse == null) + throw new BadImageFormatException(); + + if (tse is MetadataType) + { + MetadataType type = (MetadataType)tse; + Debug.Assert(type.IsGenericDefinition && type.HasInstantiation); + + type = _typeSystemContext.GetInstantiatedType(type, GetUniversalCanonicalInstantiation(type.Instantiation.Length)); + + typeTemplates.Add(type); + } + else if (tse is MethodDesc) + { + MethodDesc method = (MethodDesc)tse; + TypeDesc containingType = method.OwningType; + + Debug.Assert(method.IsTypicalMethodDefinition); + Debug.Assert(method.HasInstantiation || method.OwningType.HasInstantiation); + + if (containingType.HasInstantiation) + { + containingType = _typeSystemContext.GetInstantiatedType((MetadataType)containingType, GetUniversalCanonicalInstantiation(containingType.Instantiation.Length)); + method = containingType.GetMethod(method.Name, method.GetTypicalMethodDefinition().Signature); + typeTemplates.Add(containingType); + } + + if (method.HasInstantiation) + { + method = _typeSystemContext.GetInstantiatedMethod(method, GetUniversalCanonicalInstantiation(method.Instantiation.Length)); + } + + methodTemplates.Add(method); + } + else if (tse is FieldDesc) + { + FieldDesc field = (FieldDesc)tse; + TypeDesc containingType = field.OwningType; + + Debug.Assert(containingType.HasInstantiation && containingType.IsGenericDefinition); + + containingType = _typeSystemContext.GetInstantiatedType((MetadataType)containingType, GetUniversalCanonicalInstantiation(containingType.Instantiation.Length)); + field = containingType.GetField(field.Name); + + typeTemplates.Add(containingType); + fieldTemplates.Add(field); + } + } + } + public static bool ModuleHasMetadataMappings(ModuleDesc metadataDescribingModule) { return metadataDescribingModule.GetTypeByCustomAttributeTypeName(MetadataMappingTypeName, throwIfNotFound: false) != null; @@ -118,6 +287,10 @@ namespace ILCompiler MethodDesc fullMetadataMethod = typeWithMetadataMappings.GetMethod("Metadata", null); MethodDesc weakMetadataMethod = typeWithMetadataMappings.GetMethod("WeakMetadata", null); + MethodDesc requiredGenericTypesMethod = typeWithMetadataMappings.GetMethod("RequiredGenericTypes", null); + MethodDesc requiredGenericMethodsMethod = typeWithMetadataMappings.GetMethod("RequiredGenericMethods", null); + MethodDesc requiredGenericFieldsMethod = typeWithMetadataMappings.GetMethod("RequiredGenericFields", null); + MethodDesc requiredTemplatesMethod = typeWithMetadataMappings.GetMethod("CompilerDeterminedInstantiations", null); ILProvider ilProvider = new ILProvider(null); @@ -126,7 +299,7 @@ namespace ILCompiler if (fullMetadataMethod != null) { MethodIL fullMethodIL = ilProvider.GetMethodIL(fullMetadataMethod); - ReadMetadataMethod(fullMethodIL, ref result.AllTypeMappings, ref result.MethodMappings, ref result.FieldMappings, ref metadataModules); + ReadMetadataMethod(fullMethodIL, result.AllTypeMappings, result.MethodMappings, result.FieldMappings, metadataModules); foreach (var mapping in result.AllTypeMappings) { result.TypesWithStrongMetadataMappings.Add(mapping.Key); @@ -138,7 +311,7 @@ namespace ILCompiler MethodIL weakMethodIL = ilProvider.GetMethodIL(weakMetadataMethod); Dictionary weakMethodMappings = new Dictionary(); Dictionary weakFieldMappings = new Dictionary(); - ReadMetadataMethod(weakMethodIL, ref result.AllTypeMappings, ref weakMethodMappings, ref weakFieldMappings, ref metadataModules); + ReadMetadataMethod(weakMethodIL, result.AllTypeMappings, weakMethodMappings, weakFieldMappings, metadataModules); if ((weakMethodMappings.Count > 0) || (weakFieldMappings.Count > 0)) { // the format does not permit weak field/method mappings @@ -146,6 +319,35 @@ namespace ILCompiler } } + if (requiredGenericTypesMethod != null) + { + foreach (var type in ReadRequiredGenericsEntities(ilProvider.GetMethodIL(requiredGenericTypesMethod))) + { + Debug.Assert(type is DefType); + result.RequiredGenericTypes.Add((TypeDesc)type); + } + } + + if (requiredGenericMethodsMethod != null) + { + foreach (var method in ReadRequiredGenericsEntities(ilProvider.GetMethodIL(requiredGenericMethodsMethod))) + result.RequiredGenericMethods.Add((MethodDesc)method); + } + + if (requiredGenericFieldsMethod != null) + { + foreach (var field in ReadRequiredGenericsEntities(ilProvider.GetMethodIL(requiredGenericFieldsMethod))) + result.RequiredGenericFields.Add((FieldDesc)field); + } + + if (requiredTemplatesMethod != null) + { + ReadRequiredTemplates(ilProvider.GetMethodIL(requiredTemplatesMethod), + result.RequiredTemplateTypes, + result.RequiredTemplateMethods, + result.RequiredTemplateFields); + } + result.MetadataModules = ImmutableArray.CreateRange(metadataModules); ImmutableArray.Builder externalMetadataModulesBuilder = ImmutableArray.CreateBuilder(); @@ -160,8 +362,6 @@ namespace ILCompiler result.ExternalMetadataModules = externalMetadataModulesBuilder.ToImmutable(); result.LocalMetadataModules = localMetadataModulesBuilder.ToImmutable(); - // TODO! Replace with something more complete that capture the generic instantiations that the pre-analysis - // indicates should have been present foreach (var pair in result.MethodMappings) { MethodDesc reflectableMethod = pair.Key; @@ -180,12 +380,22 @@ namespace ILCompiler result.DynamicInvokeCompiledMethods.Add(instantiatiatedDynamicInvokeStub); } + foreach (var reflectableMethod in result.RequiredGenericMethods) + { + MethodDesc typicalDynamicInvokeStub; + if (!_dynamicInvokeStubs.Value.TryGetValue(reflectableMethod.GetTypicalMethodDefinition(), out typicalDynamicInvokeStub)) + continue; + + MethodDesc instantiatiatedDynamicInvokeStub = InstantiateCanonicalDynamicInvokeMethodForMethod(typicalDynamicInvokeStub, reflectableMethod); + result.DynamicInvokeCompiledMethods.Add(instantiatiatedDynamicInvokeStub); + } + return result; } public override IEnumerable GetCompilationModulesWithMetadata() { - return _loadedMetadata.Value.LocalMetadataModules; + return _loadedMetadata.Value.LocalMetadataModules.Union(_metadataOnlyAssemblies); } public override bool WillUseMetadataTokenToReferenceMethod(MethodDesc method) @@ -216,7 +426,154 @@ namespace ILCompiler return MetadataCategory.RuntimeMapping; } - protected override void ComputeMetadata(NodeFactory factory, out byte[] metadataBlob, out List> typeMappings, out List> methodMappings, out List> fieldMappings) + protected override void GetRuntimeMappingDependenciesDueToReflectability(ref DependencyNodeCore.DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + // Backwards compatible behavior with when this code was indiscriminately injected into all EETypes. + // We might want to tweak this. + + if (type is MetadataType metadataType && !type.IsGenericDefinition) + { + Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Any)); + + // For instantiated types, we write the static fields offsets directly into the table, and we do not reference the gc/non-gc statics nodes + if (!type.HasInstantiation) + { + if (metadataType.GCStaticFieldSize.AsInt > 0) + { + dependencies.Add(factory.TypeGCStaticsSymbol(metadataType), "GC statics for ReflectionFieldMap entry"); + } + + if (metadataType.NonGCStaticFieldSize.AsInt > 0) + { + dependencies.Add(factory.TypeNonGCStaticsSymbol(metadataType), "Non-GC statics for ReflectionFieldMap entry"); + } + } + + if (metadataType.ThreadStaticFieldSize.AsInt > 0) + { + dependencies.Add(((UtcNodeFactory)factory).TypeThreadStaticsOffsetSymbol(metadataType), "Thread statics for ReflectionFieldMap entry"); + } + } + } + + private bool IsMethodSupportedInPrecomputedReflection(MethodDesc method) + { + if (!IsMethodSupportedInReflectionInvoke(method)) + return false; + + MethodDesc typicalInvokeTarget = method.GetTypicalMethodDefinition(); + MethodDesc typicalDynamicInvokeStub; + + return _dynamicInvokeStubs.Value.TryGetValue(typicalInvokeTarget, out typicalDynamicInvokeStub); + } + + void ICompilationRootProvider.AddCompilationRoots(IRootingServiceProvider rootProvider) + { + MetadataLoadedInfo loadedMetadata = _loadedMetadata.Value; + + // Add all non-generic reflectable types as roots. + foreach (var type in loadedMetadata.TypesWithStrongMetadataMappings) + { + rootProvider.AddCompilationRoot(type, "Required non-generic type"); + } + + // Add all non-generic reflectable methods as roots. + // Virtual methods need special handling (e.g. with dependency tracking) since they can be abstract. + foreach (var method in loadedMetadata.MethodMappings.Keys) + { + if (method.HasInstantiation || method.OwningType.HasInstantiation) + continue; + + if (!IsMethodSupportedInPrecomputedReflection(method)) + continue; + + if (method.IsVirtual) + rootProvider.RootVirtualMethodForReflection(method, "Reflection root"); + else + { + if (method.IsConstructor) + { + rootProvider.AddCompilationRoot(method.OwningType, "Type for method reflection root"); + } + + rootProvider.AddCompilationRoot(method, "Reflection root"); + } + } + + // Root all the generic type instantiations from the pre-computed metadata + foreach (var type in loadedMetadata.RequiredGenericTypes) + { + rootProvider.AddCompilationRoot(type, "Required generic type"); + } + + // Root all the generic methods (either non-generic methods on generic types, or generic methods) from + // the pre-computed metadata. + // Virtual methods need special handling (e.g. with dependency tracking) since they can be abstract. + foreach (var method in loadedMetadata.RequiredGenericMethods) + { + if (!IsMethodSupportedInPrecomputedReflection(method)) + continue; + + if (method.IsVirtual) + rootProvider.RootVirtualMethodForReflection(method, "Required generic method"); + + if (!method.IsAbstract) + { + if (method.IsConstructor) + { + rootProvider.AddCompilationRoot(method.OwningType, "Type for method required generic method"); + } + + rootProvider.AddCompilationRoot(method, "Required generic method"); + } + } + + foreach (var field in loadedMetadata.RequiredGenericFields) + { + rootProvider.AddCompilationRoot(field.OwningType, "Required generic field's owning type"); + if (field.IsThreadStatic) + { + rootProvider.RootThreadStaticBaseForType(field.OwningType, "Required generic field"); + } + else if (field.IsStatic) + { + if (field.HasGCStaticBase) + rootProvider.RootGCStaticBaseForType(field.OwningType, "Required generic field"); + else + rootProvider.RootNonGCStaticBaseForType(field.OwningType, "Required generic field"); + } + } + + foreach (var type in loadedMetadata.RequiredTemplateTypes) + { + Debug.Assert(type.IsCanonicalSubtype(CanonicalFormKind.Any)); + rootProvider.AddCompilationRoot(type, "Compiler determined template"); + } + + foreach (var method in loadedMetadata.RequiredTemplateMethods) + { + Debug.Assert(method.IsCanonicalMethod(CanonicalFormKind.Any)); + if (method.IsVirtual) + rootProvider.RootVirtualMethodForReflection(method, "Compiler determined template"); + + if (!method.IsAbstract) + { + if (method.IsConstructor) + { + rootProvider.AddCompilationRoot(method.OwningType, "Type for method compiler determined template method"); + } + + rootProvider.AddCompilationRoot(method, "Compiler determined template"); + } + } + } + + protected override void ComputeMetadata(NodeFactory factory, + out byte[] metadataBlob, + out List> typeMappings, + out List> methodMappings, + out List> fieldMappings, + out List> stackTraceMapping) { MetadataLoadedInfo loadedMetadata = _loadedMetadata.Value; metadataBlob = _metadataBlob; @@ -226,6 +583,10 @@ namespace ILCompiler fieldMappings = new List>(); Dictionary canonicalToSpecificMethods = new Dictionary(); + + HashSet canonicalFieldsAddedToMap = new HashSet(); + HashSet canonicalMethodsAddedToMap = new HashSet(); + // The handling of generic methods which are implemented by canonical code is interesting, the invoke map // needs to have a specific instantiation for each canonical bit of code. foreach (GenericDictionaryNode dictionaryNode in GetCompiledGenericDictionaries()) @@ -249,7 +610,7 @@ namespace ILCompiler } // Generate type definition mappings - foreach (var type in factory.MetadataManager.GetTypesWithEETypes()) + foreach (var type in GetTypesWithEETypes()) { MetadataType definition = type.IsTypeDefinition ? type as MetadataType : null; if (definition == null) @@ -262,35 +623,22 @@ namespace ILCompiler } } + // Mappings for all compiled methods foreach (var method in GetCompiledMethods()) { - if (!MethodCanBeInvokedViaReflection(factory, method)) + AddMethodMapping(factory, method, canonicalMethodsAddedToMap, canonicalToSpecificMethods, methodMappings); + } + + // Mappings for reflectable abstract non-generic methods (methods with compiled bodies are handled above) + foreach (var method in loadedMetadata.MethodMappings.Keys) + { + if (!method.IsAbstract) continue; - // If there is a possible canonical method, use that instead of a specific method (folds canonically equivalent methods away) - if (method.GetCanonMethodTarget(CanonicalFormKind.Specific) != method) + if (method.HasInstantiation || method.OwningType.HasInstantiation) continue; - int token; - if (loadedMetadata.MethodMappings.TryGetValue(method.GetTypicalMethodDefinition(), out token)) - { - MethodDesc invokeMapMethod = GetInvokeMapMethodForMethod(canonicalToSpecificMethods, method); - - if (invokeMapMethod != null) - methodMappings.Add(new MetadataMapping(invokeMapMethod, token)); - } - else if (!WillUseMetadataTokenToReferenceMethod(method) && - _compilationModuleGroup.ContainsMethodBody(method.GetCanonMethodTarget(CanonicalFormKind.Specific))) - { - MethodDesc invokeMapMethod = GetInvokeMapMethodForMethod(canonicalToSpecificMethods, method); - - // For methods on types that are not in the current module, assume they must be reflectable - // and generate a non-metadata backed invoke table entry - // TODO, the above computation is overly generous with the set of methods that are placed into the method invoke map - // It includes methods which are not reflectable at all. - if (invokeMapMethod != null) - methodMappings.Add(new MetadataMapping(invokeMapMethod, 0)); - } + AddMethodMapping(factory, method, canonicalMethodsAddedToMap, canonicalToSpecificMethods, methodMappings); } foreach (var eetypeGenerated in GetTypesWithEETypes()) @@ -298,31 +646,84 @@ namespace ILCompiler if (eetypeGenerated.IsGenericDefinition) continue; - if (eetypeGenerated.HasInstantiation) - { - // Collapsing of field map entries based on canonicalization, to avoid redundant equivalent entries - - TypeDesc canonicalType = eetypeGenerated.ConvertToCanonForm(CanonicalFormKind.Specific); - if (canonicalType != eetypeGenerated && TypeGeneratesEEType(canonicalType)) - continue; - } - foreach (FieldDesc field in eetypeGenerated.GetFields()) - { - int token; - if (loadedMetadata.FieldMappings.TryGetValue(field.GetTypicalFieldDefinition(), out token)) - { - fieldMappings.Add(new MetadataMapping(field, token)); - } - else if (!WillUseMetadataTokenToReferenceField(field)) - { - // TODO, the above computation is overly generous with the set of fields that are placed into the field invoke map - // It includes fields which are not reflectable at all, and collapses static fields across generics + AddFieldMapping(field, canonicalFieldsAddedToMap, fieldMappings); + } - // TODO! enable this. Disabled due to cross module import of statics is not yet implemented - // fieldMappings.Add(new MetadataMapping(field, 0)); - } - } + stackTraceMapping = GenerateStackTraceMetadata(factory); + } + + private void AddFieldMapping(FieldDesc field, HashSet canonicalFieldsAddedToMap, List> fieldMappings) + { + if (field.OwningType.HasInstantiation) + { + // Not all fields of generic types are reflectable. + if (!_loadedMetadata.Value.RequiredGenericFields.Contains(field) && !_loadedMetadata.Value.RequiredTemplateFields.Contains(field)) + return; + + // Collapsing of field map entries based on canonicalization, to avoid redundant equivalent entries + FieldDesc canonicalField = field.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific).GetField(field.Name); + if (!canonicalFieldsAddedToMap.Add(canonicalField)) + return; + } + + int token; + if (_loadedMetadata.Value.FieldMappings.TryGetValue(field.GetTypicalFieldDefinition(), out token)) + { + fieldMappings.Add(new MetadataMapping(field, token)); + } + else if (!WillUseMetadataTokenToReferenceField(field)) + { + // TODO, the above computation is overly generous with the set of fields that are placed into the field invoke map + // It includes fields which are not reflectable at all, and collapses static fields across generics + + // TODO! enable this. Disabled due to cross module import of statics is not yet implemented + // fieldMappings.Add(new MetadataMapping(field, 0)); + } + } + + private void AddMethodMapping(NodeFactory factory, MethodDesc method, HashSet canonicalMethodsAddedToMap, Dictionary canonicalToSpecificMethods, List> methodMappings) + { + if (!MethodCanBeInvokedViaReflection(factory, method)) + return; + + if (!IsReflectionInvokable(method)) + return; + + MethodDesc canonicalMethod = method.GetCanonMethodTarget(CanonicalFormKind.Specific); + + if (method.HasInstantiation || method.OwningType.HasInstantiation) + { + // Not all generic methods or methods on generic types are reflectable. + // TODO: This might cause issues with delegate reverse lookups, especially in incremental compilation mode. + // Delegate reverse lookups depend on the existance of entries in the InvokeMap table, for methods that were not necessarily + // considered to be reflectable by the DR. + if (!_loadedMetadata.Value.RequiredGenericMethods.Contains(method) && !_loadedMetadata.Value.RequiredTemplateMethods.Contains(method)) + return; + + // Collapsing of invoke map entries based on canonicalization + if (!canonicalMethodsAddedToMap.Add(canonicalMethod)) + return; + } + + int token; + if (_loadedMetadata.Value.MethodMappings.TryGetValue(method.GetTypicalMethodDefinition(), out token)) + { + MethodDesc invokeMapMethod = GetInvokeMapMethodForMethod(canonicalToSpecificMethods, method); + + if (invokeMapMethod != null) + methodMappings.Add(new MetadataMapping(invokeMapMethod, token)); + } + else if (!WillUseMetadataTokenToReferenceMethod(method) && _compilationModuleGroup.ContainsMethodBody(canonicalMethod, false)) + { + MethodDesc invokeMapMethod = GetInvokeMapMethodForMethod(canonicalToSpecificMethods, method); + + // For methods on types that are not in the current module, assume they must be reflectable + // and generate a non-metadata backed invoke table entry + // TODO, the above computation is overly generous with the set of methods that are placed into the method invoke map + // It includes methods which are not reflectable at all. + if (invokeMapMethod != null) + methodMappings.Add(new MetadataMapping(invokeMapMethod, 0)); } } @@ -410,8 +811,16 @@ namespace ILCompiler { Debug.Assert(IsReflectionInvokable(method)); - if (method.IsCanonicalMethod(CanonicalFormKind.Any)) - return false; + if (!ProjectNDependencyBehavior.EnableFullAnalysis) + { + if (method.IsCanonicalMethod(CanonicalFormKind.Any)) + return false; + } + else + { + if (method.IsCanonicalMethod(CanonicalFormKind.Universal)) + return false; + } MethodDesc reflectionInvokeStub = GetCanonicalReflectionInvokeStub(method); @@ -446,6 +855,91 @@ namespace ILCompiler return dynamicInvokeStubCanonicalized; } + private List> GenerateStackTraceMetadata(NodeFactory factory) + { + var transformed = MetadataTransform.Run(new NoDefinitionMetadataPolicy(), Array.Empty()); + MetadataTransform transform = transformed.Transform; + + // Generate metadata blob + var writer = new MetadataWriter(); + + // Only emit stack trace metadata for those methods which don't have reflection metadata + HashSet methodInvokeMap = new HashSet(); + foreach (var mappingEntry in GetMethodMapping(factory)) + { + var method = mappingEntry.Entity; + if (ShouldMethodBeInInvokeMap(method)) + methodInvokeMap.Add(method); + } + + // Generate entries in the blob for methods that will be necessary for stack trace purposes. + var stackTraceRecords = new List>(); + foreach (var methodBody in GetCompiledMethodBodies()) + { + NonExternMethodSymbolNode methodNode = methodBody as NonExternMethodSymbolNode; + if (methodNode != null && !methodNode.HasCompiledBody) + continue; + + MethodDesc method = methodBody.Method; + + if (methodInvokeMap.Contains(method)) + continue; + + if (!_stackTraceEmissionPolicy.ShouldIncludeMethod(method)) + continue; + + // In the metadata, we only represent the generic definition + MethodDesc methodToGenerateMetadataFor = method.GetTypicalMethodDefinition(); + MetadataRecord record = transform.HandleQualifiedMethod(methodToGenerateMetadataFor); + + // As a twist, instantiated generic methods appear as if instantiated over their formals. + if (methodToGenerateMetadataFor.HasInstantiation) + { + var methodInst = new MethodInstantiation + { + Method = record, + }; + methodInst.GenericTypeArguments.Capacity = methodToGenerateMetadataFor.Instantiation.Length; + foreach (EcmaGenericParameter typeArgument in methodToGenerateMetadataFor.Instantiation) + { + var genericParam = new TypeReference + { + TypeName = (ConstantStringValue)typeArgument.Name, + }; + methodInst.GenericTypeArguments.Add(genericParam); + } + record = methodInst; + } + + stackTraceRecords.Add(new KeyValuePair( + method, + record)); + + writer.AdditionalRootRecords.Add(record); + } + + var ms = new MemoryStream(); + writer.Write(ms); + + _stackTraceBlob = ms.ToArray(); + + var result = new List>(); + + // Generate stack trace metadata mapping + foreach (var stackTraceRecord in stackTraceRecords) + { + result.Add(new MetadataMapping(stackTraceRecord.Key, writer.GetRecordHandle(stackTraceRecord.Value))); + } + + return result; + } + + public byte[] GetStackTraceBlob(NodeFactory factory) + { + EnsureMetadataGenerated(factory); + return _stackTraceBlob; + } + private sealed class AttributeSpecifiedBlockingPolicy : MetadataBlockingPolicy { public override bool IsBlocked(MetadataType type) @@ -468,5 +962,15 @@ namespace ILCompiler return false; } } + + private struct NoDefinitionMetadataPolicy : IMetadataPolicy + { + public bool GeneratesMetadata(FieldDesc fieldDef) => false; + public bool GeneratesMetadata(MethodDesc methodDef) => false; + public bool GeneratesMetadata(MetadataType typeDef) => false; + public bool IsBlocked(MetadataType typeDef) => false; + public bool IsBlocked(MethodDesc methodDef) => false; + public ModuleDesc GetModuleOfType(MetadataType typeDef) => typeDef.Module; + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/ReadyToRun.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/ReadyToRun.cs index 462255ace1..30302ee6a9 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/ReadyToRun.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/ReadyToRun.cs @@ -22,6 +22,9 @@ namespace ILCompiler FailFast = 0x24, ThrowNullRef = 0x25, ThrowDivZero = 0x26, + ThrowArgumentOutOfRange = 0x27, + ThrowArgument = 0x28, + ThrowPlatformNotSupported = 0x29, DebugBreak = 0x2F, diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilation.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilation.cs index 0e4b2ce83d..97b6ce7232 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilation.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilation.cs @@ -23,25 +23,20 @@ namespace ILCompiler DependencyAnalyzerBase dependencyGraph, NodeFactory nodeFactory, IEnumerable roots, + DebugInformationProvider debugInformationProvider, Logger logger, + DevirtualizationManager devirtualizationManager, JitConfigProvider configProvider) - : base(dependencyGraph, nodeFactory, roots, logger) + : base(dependencyGraph, nodeFactory, roots, debugInformationProvider, devirtualizationManager, logger) { _jitConfigProvider = configProvider; } - protected override bool GenerateDebugInfo - { - get - { - return _jitConfigProvider.HasFlag(CorJitFlag.CORJIT_FLAG_DEBUG_INFO); - } - } - protected override void CompileInternal(string outputFile, ObjectDumper dumper) { _corInfo = new CorInfoImpl(this, _jitConfigProvider); + _dependencyGraph.ComputeMarkedNodes(); var nodes = _dependencyGraph.MarkedNodeList; NodeFactory.SetMarkingComplete(); @@ -85,7 +80,10 @@ namespace ILCompiler MethodIL throwingIL = TypeSystemThrowingILEmitter.EmitIL(method, ex); _corInfo.CompileMethod(methodCodeNodeNeedingCode, throwingIL); - // TODO: Log as a warning + // TODO: Log as a warning. For now, just log to the logger; but this needs to + // have an error code, be supressible, the method name/sig needs to be properly formatted, etc. + // https://github.com/dotnet/corert/issues/72 + Logger.Writer.WriteLine($"Warning: Method `{method}` will always throw because: {ex.Message}"); } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilationBuilder.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilationBuilder.cs index 20d9b497f2..773560798c 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilationBuilder.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilationBuilder.cs @@ -7,7 +7,11 @@ using System.Collections.Generic; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; + using Internal.JitInterface; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; namespace ILCompiler { @@ -71,14 +75,23 @@ namespace ILCompiler break; } - if (_generateDebugInfo) + // Do not bother with debug information if the debug info provider never gives anything. + if (!(_debugInformationProvider is NullDebugInformationProvider)) jitFlagBuilder.Add(CorJitFlag.CORJIT_FLAG_DEBUG_INFO); - var factory = new RyuJitNodeFactory(_context, _compilationGroup, _metadataManager, _nameMangler); + if (_context.Target.MaximumSimdVectorLength != SimdVectorLength.None) + { + // TODO: AVX + Debug.Assert(_context.Target.MaximumSimdVectorLength == SimdVectorLength.Vector128Bit); + jitFlagBuilder.Add(CorJitFlag.CORJIT_FLAG_FEATURE_SIMD); + } + + var interopStubManager = new CompilerGeneratedInteropStubManager(_compilationGroup, _context, new InteropStateManager(_compilationGroup.GeneratedAssembly)); + var factory = new RyuJitNodeFactory(_context, _compilationGroup, _metadataManager, interopStubManager, _nameMangler, _vtableSliceProvider, _dictionaryLayoutProvider); var jitConfig = new JitConfigProvider(jitFlagBuilder.ToArray(), _ryujitOptions); - DependencyAnalyzerBase graph = CreateDependencyGraph(factory); - return new RyuJitCompilation(graph, factory, _compilationRoots, _logger, jitConfig); + DependencyAnalyzerBase graph = CreateDependencyGraph(factory, new ObjectNode.ObjectNodeComparer(new CompilerComparer())); + return new RyuJitCompilation(graph, factory, _compilationRoots, _debugInformationProvider, _logger, _devirtualizationManager, jitConfig); } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/SimdHelper.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/SimdHelper.cs new file mode 100644 index 0000000000..3f523b18da --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/SimdHelper.cs @@ -0,0 +1,62 @@ +// 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.Collections.Generic; + +using Internal.TypeSystem; + +using AssemblyName = System.Reflection.AssemblyName; + +namespace ILCompiler +{ + /// + /// Helper type that deals with System.Numerics.Vectors intrinsics. + /// + public struct SimdHelper + { + private ModuleDesc[] _simdModulesCached; + + public bool IsInSimdModule(TypeDesc type) + { + if (type is MetadataType) + { + if (_simdModulesCached == null) + { + InitializeSimdModules(type); + } + + ModuleDesc typeModule = ((MetadataType)type).Module; + foreach (ModuleDesc simdModule in _simdModulesCached) + if (typeModule == simdModule) + return true; + } + + return false; + } + + private void InitializeSimdModules(TypeDesc type) + { + TypeSystemContext context = type.Context; + + ArrayBuilder simdModules = new ArrayBuilder(); + + ModuleDesc module = context.ResolveAssembly(new AssemblyName("System.Numerics"), false); + if (module != null) + simdModules.Add(module); + + module = context.ResolveAssembly(new AssemblyName("System.Numerics.Vectors"), false); + if (module != null) + simdModules.Add(module); + + _simdModulesCached = simdModules.ToArray(); + } + + public bool IsVectorOfT(TypeDesc type) + { + return IsInSimdModule(type) + && ((MetadataType)type).Name == "Vector`1" + && ((MetadataType)type).Namespace == "System.Numerics"; + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/SingleFileCompilationModuleGroup.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/SingleFileCompilationModuleGroup.cs index 5bfa8a8639..ba4c23d79b 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/SingleFileCompilationModuleGroup.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/SingleFileCompilationModuleGroup.cs @@ -20,7 +20,12 @@ namespace ILCompiler return true; } - public override bool ContainsMethodBody(MethodDesc method) + public override bool ContainsTypeDictionary(TypeDesc type) + { + return true; + } + + public override bool ContainsMethodBody(MethodDesc method, bool unboxingStub) { return true; } @@ -28,22 +33,27 @@ namespace ILCompiler public override bool ContainsMethodDictionary(MethodDesc method) { Debug.Assert(method.GetCanonMethodTarget(CanonicalFormKind.Specific) != method); - return ContainsMethodBody(method); + return ContainsMethodBody(method, false); } - public override bool ExportsType(TypeDesc type) + public override ExportForm GetExportTypeForm(TypeDesc type) { - return false; + return ExportForm.None; } - public override bool ExportsMethod(MethodDesc method) + public override ExportForm GetExportTypeFormDictionary(TypeDesc type) { - return false; + return ExportForm.None; } - public override bool ExportsMethodDictionary(MethodDesc method) + public override ExportForm GetExportMethodForm(MethodDesc method, bool unboxingStub) { - return false; + return ExportForm.None; + } + + public override ExportForm GetExportMethodDictionaryForm(MethodDesc method) + { + return ExportForm.None; } public override bool IsSingleFileCompilation @@ -64,6 +74,11 @@ namespace ILCompiler return false; } + public override bool PresenceOfEETypeImpliesAllMethodsOnType(TypeDesc type) + { + return false; + } + public override bool ShouldReferenceThroughImportTable(TypeDesc type) { return false; diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/SingleMethodCompilationModuleGroup.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/SingleMethodCompilationModuleGroup.cs index 3e7578bb47..61f220e094 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/SingleMethodCompilationModuleGroup.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/SingleMethodCompilationModuleGroup.cs @@ -30,7 +30,7 @@ namespace ILCompiler } } - public override bool ContainsMethodBody(MethodDesc method) + public override bool ContainsMethodBody(MethodDesc method, bool unboxingStub) { return method == _method; } @@ -38,7 +38,7 @@ namespace ILCompiler public sealed override bool ContainsMethodDictionary(MethodDesc method) { Debug.Assert(method.GetCanonMethodTarget(CanonicalFormKind.Specific) != method); - return ContainsMethodBody(method); + return ContainsMethodBody(method, false); } public override bool ContainsType(TypeDesc type) @@ -46,19 +46,29 @@ namespace ILCompiler return false; } - public override bool ExportsType(TypeDesc type) + public override bool ContainsTypeDictionary(TypeDesc type) { return false; } - public override bool ExportsMethod(MethodDesc method) + public override ExportForm GetExportTypeForm(TypeDesc type) { - return false; + return ExportForm.None; } - public override bool ExportsMethodDictionary(MethodDesc method) + public override ExportForm GetExportTypeFormDictionary(TypeDesc type) { - return false; + return ExportForm.None; + } + + public override ExportForm GetExportMethodForm(MethodDesc method, bool unboxingStub) + { + return ExportForm.None; + } + + public override ExportForm GetExportMethodDictionaryForm(MethodDesc method) + { + return ExportForm.None; } public override bool ShouldProduceFullVTable(TypeDesc type) @@ -71,6 +81,11 @@ namespace ILCompiler return false; } + public override bool PresenceOfEETypeImpliesAllMethodsOnType(TypeDesc type) + { + return false; + } + public override bool ShouldReferenceThroughImportTable(TypeDesc type) { return false; diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/StackTraceEmissionPolicy.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/StackTraceEmissionPolicy.cs new file mode 100644 index 0000000000..05b3d1cffd --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/StackTraceEmissionPolicy.cs @@ -0,0 +1,36 @@ +// 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 Internal.TypeSystem; + +namespace ILCompiler +{ + /// + /// Represents a stack trace emission policy. + /// + public abstract class StackTraceEmissionPolicy + { + public abstract bool ShouldIncludeMethod(MethodDesc method); + } + + public class NoStackTraceEmissionPolicy : StackTraceEmissionPolicy + { + public override bool ShouldIncludeMethod(MethodDesc method) + { + return false; + } + } + + /// + /// Stack trace emission policy that ensures presence of stack trace metadata for all + /// -based methods. + /// + public class EcmaMethodStackTraceEmissionPolicy : StackTraceEmissionPolicy + { + public override bool ShouldIncludeMethod(MethodDesc method) + { + return method.GetTypicalMethodDefinition() is Internal.TypeSystem.Ecma.EcmaMethod; + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs index b641f375cf..35f8d8503b 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs @@ -2,6 +2,7 @@ // 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 Internal.IL; using Internal.TypeSystem; @@ -97,7 +98,7 @@ namespace ILCompiler /// /// Gets a value indicating whether this type has any generic virtual methods. /// - public static bool HasGenericVirtualMethod(this TypeDesc type) + public static bool HasGenericVirtualMethods(this TypeDesc type) { foreach (var method in type.GetAllMethods()) { @@ -115,5 +116,141 @@ namespace ILCompiler { return type.Context.IsCanonicalDefinitionType(type, kind); } + + /// + /// Gets the value of the field ordinal. Ordinals are computed by also including static fields, but excluding + /// literal fields and fields with RVAs. + /// + public static int GetFieldOrdinal(this FieldDesc inputField) + { + // Make sure we are asking the question for a valid instance or static field + Debug.Assert(!inputField.HasRva && !inputField.IsLiteral); + + int fieldOrdinal = 0; + foreach (FieldDesc field in inputField.OwningType.GetFields()) + { + // If this field does not contribute to layout, skip + if (field.HasRva || field.IsLiteral) + continue; + + if (field == inputField) + return fieldOrdinal; + + fieldOrdinal++; + } + + Debug.Assert(false); + return -1; + } + + /// + /// What is the maximum number of steps that need to be taken from this type to its most contained generic type. + /// i.e. + /// System.Int32 => 0 + /// List<System.Int32> => 1 + /// Dictionary<System.Int32,System.Int32> => 1 + /// Dictionary<List<System.Int32>,<System.Int32> => 2 + /// + public static int GetGenericDepth(this TypeDesc type) + { + if (type.HasInstantiation) + { + int maxGenericDepthInInstantiation = 0; + foreach (TypeDesc instantiationType in type.Instantiation) + { + maxGenericDepthInInstantiation = Math.Max(instantiationType.GetGenericDepth(), maxGenericDepthInInstantiation); + } + + return maxGenericDepthInInstantiation + 1; + } + + return 0; + } + + /// + /// Determine if a type has a generic depth greater than a given value + /// + public static bool IsGenericDepthGreaterThan(this TypeDesc type, int depth) + { + if (depth < 0) + return true; + + foreach (TypeDesc instantiationType in type.Instantiation) + { + if (instantiationType.IsGenericDepthGreaterThan(depth - 1)) + return true; + } + + return false; + } + + /// + /// Gets a fully canonicalized base type if base type is canonical, or unmodified base type otherwise. + /// + public static DefType NormalizedBaseType(this TypeDesc type) + { + // Base type for Foo<__Canon> where Foo is defined as + // class Foo : Bar { } + // is Bar<__Canon, string>. This method normalizes it to Bar<__Canon, __Canon>. + DefType baseType = type.BaseType; + if (baseType != null && baseType.IsCanonicalSubtype(CanonicalFormKind.Any)) + baseType = (DefType)baseType.ConvertToCanonForm(CanonicalFormKind.Specific); + return baseType; + } + + /// + /// Gets an interface list that is fully canonicalized if the interfaces are canonical or the unmodified + /// interface types otherwise. + /// + public static NormalizedInterfaceList NormalizedRuntimeInterfaces(this TypeDesc type) + { + // Interface list for Foo<__Canon> where Foo is defined as + // class Foo : IFooer + // is IFooer<__Canon, object>. This method normalizes it to IFooer<__Canon, __Canon>. + return new NormalizedInterfaceList(type); + } + + public struct NormalizedInterfaceList + { + private readonly TypeDesc _type; + + public NormalizedInterfaceList(TypeDesc type) + { + _type = type; + } + + public Enumerator GetEnumerator() + { + return new Enumerator(_type); + } + + public struct Enumerator + { + private readonly DefType[] _interfaces; + private int _index; + + public Enumerator(TypeDesc type) + { + _interfaces = type.RuntimeInterfaces; + _index = -1; + } + + public DefType Current + { + get + { + DefType intface = _interfaces[_index]; + if (intface.IsCanonicalSubtype(CanonicalFormKind.Any)) + intface = (DefType)intface.ConvertToCanonForm(CanonicalFormKind.Specific); + return intface; + } + } + + public bool MoveNext() + { + return ++_index < _interfaces.Length; + } + } + } } } diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/UniversalGenericsRootProvider.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/UniversalGenericsRootProvider.cs new file mode 100644 index 0000000000..b585a33662 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/UniversalGenericsRootProvider.cs @@ -0,0 +1,28 @@ +// 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 Internal.TypeSystem; + +namespace ILCompiler +{ + /// + /// Compilation roots necessary to enable universal shared generics thats + /// are not encompassed in other root providers + /// + public class UniversalGenericsRootProvider : ICompilationRootProvider + { + TypeSystemContext _context; + + public UniversalGenericsRootProvider(TypeSystemContext context) + { + _context = context; + } + + public void AddCompilationRoots(IRootingServiceProvider rootProvider) + { + if (_context.SupportsUniversalCanon) + rootProvider.AddCompilationRoot(_context.UniversalCanonType.MakeArrayType(), "Universal generic array support"); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/UsageBasedMetadataManager.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/UsageBasedMetadataManager.cs new file mode 100644 index 0000000000..b5845786c8 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/UsageBasedMetadataManager.cs @@ -0,0 +1,441 @@ +// 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.Collections.Generic; + +using Internal.TypeSystem; + +using ILCompiler.Metadata; +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; + +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + /// + /// This class is responsible for managing native metadata to be emitted into the compiled + /// module. It applies a policy that every type/method that is statically used shall be reflectable. + /// + public sealed class UsageBasedMetadataManager : GeneratingMetadataManager + { + private readonly CompilationModuleGroup _compilationModuleGroup; + + private readonly bool _hasPreciseFieldUsageInformation; + + private readonly List _modulesWithMetadata = new List(); + private readonly List _fieldsWithMetadata = new List(); + private readonly List _methodsWithMetadata = new List(); + private readonly List _typesWithMetadata = new List(); + + public UsageBasedMetadataManager( + CompilationModuleGroup group, + CompilerTypeSystemContext typeSystemContext, + MetadataBlockingPolicy blockingPolicy, + string logFile, + StackTraceEmissionPolicy stackTracePolicy) + : base(group.GeneratedAssembly, typeSystemContext, blockingPolicy, logFile, stackTracePolicy) + { + // We use this to mark places that would behave differently if we tracked exact fields used. + _hasPreciseFieldUsageInformation = false; + _compilationModuleGroup = group; + } + + protected override void Graph_NewMarkedNode(DependencyNodeCore obj) + { + base.Graph_NewMarkedNode(obj); + + var moduleMetadataNode = obj as ModuleMetadataNode; + if (moduleMetadataNode != null) + { + _modulesWithMetadata.Add(moduleMetadataNode.Module); + } + + var fieldMetadataNode = obj as FieldMetadataNode; + if (fieldMetadataNode != null) + { + _fieldsWithMetadata.Add(fieldMetadataNode.Field); + } + + var methodMetadataNode = obj as MethodMetadataNode; + if (methodMetadataNode != null) + { + _methodsWithMetadata.Add(methodMetadataNode.Method); + } + + var typeMetadataNode = obj as TypeMetadataNode; + if (typeMetadataNode != null) + { + _typesWithMetadata.Add(typeMetadataNode.Type); + } + } + + protected override MetadataCategory GetMetadataCategory(FieldDesc field) + { + MetadataCategory category = 0; + + if (!IsReflectionBlocked(field)) + { + category = MetadataCategory.RuntimeMapping; + + if (_compilationModuleGroup.ContainsType(field.GetTypicalFieldDefinition().OwningType)) + category |= MetadataCategory.Description; + } + + return category; + } + + protected override MetadataCategory GetMetadataCategory(MethodDesc method) + { + MetadataCategory category = 0; + + if (!IsReflectionBlocked(method)) + { + category = MetadataCategory.RuntimeMapping; + + if (_compilationModuleGroup.ContainsType(method.GetTypicalMethodDefinition().OwningType)) + category |= MetadataCategory.Description; + } + + return category; + } + + protected override MetadataCategory GetMetadataCategory(TypeDesc type) + { + MetadataCategory category = 0; + + if (!IsReflectionBlocked(type)) + { + category = MetadataCategory.RuntimeMapping; + + if (_compilationModuleGroup.ContainsType(type.GetTypeDefinition())) + category |= MetadataCategory.Description; + } + + return category; + } + + protected override void ComputeMetadata(NodeFactory factory, + out byte[] metadataBlob, + out List> typeMappings, + out List> methodMappings, + out List> fieldMappings, + out List> stackTraceMapping) + { + ComputeMetadata(new GeneratedTypesAndCodeMetadataPolicy(_blockingPolicy, factory), + factory, out metadataBlob, out typeMappings, out methodMappings, out fieldMappings, out stackTraceMapping); + } + + protected override void GetMetadataDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(factory.MethodMetadata(method.GetTypicalMethodDefinition()), "Reflectable method"); + } + + protected override void GetMetadataDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + TypeMetadataNode.GetMetadataDependencies(ref dependencies, factory, type, "Reflectable type"); + + // If we don't have precise field usage information, apply policy that all fields that + // are eligible to have metadata get metadata. + if (!_hasPreciseFieldUsageInformation) + { + TypeDesc typeDefinition = type.GetTypeDefinition(); + + foreach (FieldDesc field in typeDefinition.GetFields()) + { + if ((GetMetadataCategory(field) & MetadataCategory.Description) != 0) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(factory.FieldMetadata(field), "Field of a reflectable type"); + } + } + } + } + + protected override void GetRuntimeMappingDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + // If we precisely track field usage, we don't need the logic below. + if (_hasPreciseFieldUsageInformation) + return; + + const string reason = "Reflection"; + + // This logic is applying policy: if a type is reflectable (has a runtime mapping), all of it's fields + // are reflectable (with a runtime mapping) as well. + // This is potentially overly broad (we don't know if any of the fields will actually be eligile + // for metadata - e.g. they could all be reflection blocked). This is fine since lack of + // precise field usage information is already not ideal from a size on disk perspective. + // The more precise way to do this would be to go over each field, check that it's eligible for RuntimeMapping + // according to the policy (e.g. it's not blocked), and only then root the base of the field. + if (type is MetadataType metadataType && !type.IsGenericDefinition) + { + Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Any)); + + if (metadataType.GCStaticFieldSize.AsInt > 0) + { + dependencies.Add(factory.TypeGCStaticsSymbol(metadataType), reason); + } + + if (metadataType.NonGCStaticFieldSize.AsInt > 0 || _typeSystemContext.HasLazyStaticConstructor(metadataType)) + { + dependencies.Add(factory.TypeNonGCStaticsSymbol(metadataType), reason); + } + + // TODO: tread static fields + } + } + + public override void GetDependenciesDueToLdToken(ref DependencyList dependencies, NodeFactory factory, FieldDesc field) + { + // In order for the RuntimeFieldHandle data structure to be usable at runtime, ensure the field + // is generating metadata. + if ((GetMetadataCategory(field) & MetadataCategory.Description) == MetadataCategory.Description) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(factory.FieldMetadata(field.GetTypicalFieldDefinition()), "LDTOKEN field"); + } + } + + public override void GetDependenciesDueToLdToken(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + // In order for the RuntimeMethodHandle data structure to be usable at runtime, ensure the method + // is generating metadata. + if ((GetMetadataCategory(method) & MetadataCategory.Description) == MetadataCategory.Description) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(factory.MethodMetadata(method.GetTypicalMethodDefinition()), "LDTOKEN method"); + } + } + + protected override IEnumerable GetFieldsWithRuntimeMapping() + { + if (_hasPreciseFieldUsageInformation) + { + // TODO + } + else + { + // This applies a policy that fields inherit runtime mapping from their owning type, + // unless they are blocked. + foreach (var type in GetTypesWithRuntimeMapping()) + { + if (type.IsGenericDefinition) + continue; + + foreach (var field in type.GetFields()) + { + if ((GetMetadataCategory(field) & MetadataCategory.RuntimeMapping) != 0) + yield return field; + } + } + } + } + + public override IEnumerable GetCompilationModulesWithMetadata() + { + return _modulesWithMetadata; + } + + private IEnumerable GetTypesWithRuntimeMapping() + { + // All constructed types that are not blocked get runtime mapping + foreach (var constructedType in GetTypesWithConstructedEETypes()) + { + if (!IsReflectionBlocked(constructedType)) + yield return constructedType; + } + + // All necessary types for which this is the highest load level that are not blocked + // get runtime mapping. + foreach (var necessaryType in GetTypesWithEETypes()) + { + if (!ConstructedEETypeNode.CreationAllowed(necessaryType) && + !IsReflectionBlocked(necessaryType)) + yield return necessaryType; + } + } + + public MetadataManager ToAnalysisBasedMetadataManager() + { + var reflectableTypes = ReflectableEntityBuilder.Create(); + + // Collect the list of types that are generating reflection metadata + foreach (var typeWithMetadata in _typesWithMetadata) + { + reflectableTypes[typeWithMetadata] = MetadataCategory.Description; + } + + foreach (var constructedType in GetTypesWithRuntimeMapping()) + { + if (!IsReflectionBlocked(constructedType)) + { + reflectableTypes[constructedType] |= MetadataCategory.RuntimeMapping; + + // Also set the description bit if the definition is getting metadata. + TypeDesc constructedTypeDefinition = constructedType.GetTypeDefinition(); + if (constructedType != constructedTypeDefinition && + (reflectableTypes[constructedTypeDefinition] & MetadataCategory.Description) != 0) + { + reflectableTypes[constructedType] |= MetadataCategory.Description; + } + } + } + + var reflectableMethods = ReflectableEntityBuilder.Create(); + foreach (var methodWithMetadata in _methodsWithMetadata) + { + reflectableMethods[methodWithMetadata] = MetadataCategory.Description; + } + + foreach (var method in GetCompiledMethods()) + { + if (!method.IsCanonicalMethod(CanonicalFormKind.Specific) && + !IsReflectionBlocked(method)) + { + if ((reflectableTypes[method.OwningType] & MetadataCategory.RuntimeMapping) != 0) + reflectableMethods[method] |= MetadataCategory.RuntimeMapping; + + // Also set the description bit if the definition is getting metadata. + MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); + if (method != typicalMethod && + (reflectableMethods[typicalMethod] & MetadataCategory.Description) != 0) + { + reflectableMethods[method] |= MetadataCategory.Description; + reflectableTypes[method.OwningType] |= MetadataCategory.Description; + } + } + } + + var reflectableFields = ReflectableEntityBuilder.Create(); + foreach (var fieldWithMetadata in _fieldsWithMetadata) + { + reflectableFields[fieldWithMetadata] = MetadataCategory.Description; + } + + if (_hasPreciseFieldUsageInformation) + { + // TODO + } + else + { + // If we don't have precise field usage information we apply a policy that + // says the fields inherit the setting from the type, potentially restricted by blocking. + // (I.e. if a type has RuntimeMapping metadata, the field has RuntimeMapping too, unless blocked.) + foreach (var reflectableType in reflectableTypes.ToEnumerable()) + { + if (reflectableType.Entity.IsGenericDefinition) + continue; + + if (reflectableType.Entity.IsCanonicalSubtype(CanonicalFormKind.Specific)) + continue; + + if ((reflectableType.Category & MetadataCategory.RuntimeMapping) == 0) + continue; + + foreach (var field in reflectableType.Entity.GetFields()) + { + if (!IsReflectionBlocked(field)) + { + reflectableFields[field] |= MetadataCategory.RuntimeMapping; + + // Also set the description bit if the definition is getting metadata. + FieldDesc typicalField = field.GetTypicalFieldDefinition(); + if (field != typicalField && + (reflectableFields[typicalField] & MetadataCategory.Description) != 0) + { + reflectableFields[field] |= MetadataCategory.Description; + } + } + } + } + } + + return new AnalysisBasedMetadataManager(_compilationModuleGroup.GeneratedAssembly, + _typeSystemContext, _blockingPolicy, _metadataLogFile, _stackTraceEmissionPolicy, + _modulesWithMetadata, reflectableTypes.ToEnumerable(), reflectableMethods.ToEnumerable(), + reflectableFields.ToEnumerable()); + } + + private struct ReflectableEntityBuilder + { + private Dictionary _dictionary; + + public static ReflectableEntityBuilder Create() + { + return new ReflectableEntityBuilder + { + _dictionary = new Dictionary(), + }; + } + + public MetadataCategory this[T key] + { + get + { + if (_dictionary.TryGetValue(key, out MetadataCategory category)) + return category; + return 0; + } + set + { + _dictionary[key] = value; + } + } + + public IEnumerable> ToEnumerable() + { + foreach (var entry in _dictionary) + { + yield return new ReflectableEntity(entry.Key, entry.Value); + } + } + } + + private struct GeneratedTypesAndCodeMetadataPolicy : IMetadataPolicy + { + private readonly MetadataBlockingPolicy _blockingPolicy; + private readonly NodeFactory _factory; + private readonly ExplicitScopeAssemblyPolicyMixin _explicitScopeMixin; + + public GeneratedTypesAndCodeMetadataPolicy(MetadataBlockingPolicy blockingPolicy, NodeFactory factory) + { + _blockingPolicy = blockingPolicy; + _factory = factory; + _explicitScopeMixin = new ExplicitScopeAssemblyPolicyMixin(); + } + + public bool GeneratesMetadata(FieldDesc fieldDef) + { + return _factory.FieldMetadata(fieldDef).Marked; + } + + public bool GeneratesMetadata(MethodDesc methodDef) + { + return _factory.MethodMetadata(methodDef).Marked; + } + + public bool GeneratesMetadata(MetadataType typeDef) + { + return _factory.TypeMetadata(typeDef).Marked; + } + + public bool IsBlocked(MetadataType typeDef) + { + return _blockingPolicy.IsBlocked(typeDef); + } + + public bool IsBlocked(MethodDesc methodDef) + { + return _blockingPolicy.IsBlocked(methodDef); + } + + public ModuleDesc GetModuleOfType(MetadataType typeDef) + { + return _explicitScopeMixin.GetModuleOfType(typeDef); + } + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/UserDefinedTypeDescriptor.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/UserDefinedTypeDescriptor.cs new file mode 100644 index 0000000000..c91a1f08ed --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/UserDefinedTypeDescriptor.cs @@ -0,0 +1,546 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; +using Internal.TypeSystem.TypesDebugInfo; + +using ILCompiler.DependencyAnalysis; + +namespace ILCompiler +{ + public class UserDefinedTypeDescriptor + { + object _lock = new object(); + NodeFactory _nodeFactory; + + NodeFactory NodeFactory => _nodeFactory; + + bool Is64Bit => NodeFactory.Target.PointerSize == 8; + + TargetAbi Abi => NodeFactory.Target.Abi; + + public UserDefinedTypeDescriptor(ITypesDebugInfoWriter objectWriter, NodeFactory nodeFactory) + { + _objectWriter = objectWriter; + _nodeFactory = nodeFactory; + } + + // Get type index for use as a variable/parameter + public uint GetVariableTypeIndex(TypeDesc type) + { + lock (_lock) + { + return GetVariableTypeIndex(type, true); + } + } + + // Get Type index for this pointer of specified type + public uint GetThisTypeIndex(TypeDesc type) + { + lock (_lock) + { + uint typeIndex; + + if (_thisTypes.TryGetValue(type, out typeIndex)) + return typeIndex; + + PointerTypeDescriptor descriptor = new PointerTypeDescriptor(); + // Note the use of GetTypeIndex here instead of GetVariableTypeIndex (We need the type exactly, not a reference to the type (as would happen for arrays/classes), and not a primitive value (as would happen for primitives)) + descriptor.ElementType = GetTypeIndex(type, true); + descriptor.Is64Bit = Is64Bit ? 1 : 0; + descriptor.IsConst = 1; + descriptor.IsReference = 0; + + typeIndex = _objectWriter.GetPointerTypeIndex(descriptor); + _thisTypes.Add(type, typeIndex); + return typeIndex; + } + } + + // Get type index for method + public uint GetMethodTypeIndex(MethodDesc method) + { + lock (_lock) + { + uint typeIndex; + + if (_methodIndices.TryGetValue(method, out typeIndex)) + return typeIndex; + + MemberFunctionTypeDescriptor descriptor = new MemberFunctionTypeDescriptor(); + MethodSignature signature = method.Signature; + + descriptor.ReturnType = GetVariableTypeIndex(DebuggerCanonicalize(signature.ReturnType)); + descriptor.ThisAdjust = 0; + descriptor.CallingConvention = 0x4; // Near fastcall + descriptor.TypeIndexOfThisPointer = signature.IsStatic ? (uint)PrimitiveTypeDescriptor.TYPE_ENUM.T_VOID : GetThisTypeIndex(method.OwningType); + descriptor.ContainingClass = GetTypeIndex(method.OwningType, true); + + try + { + descriptor.NumberOfArguments = checked((ushort)signature.Length); + } + catch (OverflowException) + { + return 0; + } + + uint[] args = new uint[signature.Length]; + for (int i = 0; i < args.Length; i++) + args[i] = GetVariableTypeIndex(DebuggerCanonicalize(signature[i])); + + + typeIndex = _objectWriter.GetMemberFunctionTypeIndex(descriptor, args); + _methodIndices.Add(method, typeIndex); + return typeIndex; + } + } + + // Get type index for specific method by name + public uint GetMethodFunctionIdTypeIndex(MethodDesc method) + { + lock (_lock) + { + uint typeIndex; + + if (_methodIdIndices.TryGetValue(method, out typeIndex)) + return typeIndex; + + MemberFunctionIdTypeDescriptor descriptor = new MemberFunctionIdTypeDescriptor(); + + descriptor.MemberFunction = GetMethodTypeIndex(method); + descriptor.ParentClass = GetTypeIndex(method.OwningType, true); + descriptor.Name = method.Name; + + typeIndex = _objectWriter.GetMemberFunctionId(descriptor); + _methodIdIndices.Add(method, typeIndex); + return typeIndex; + } + } + + private TypeDesc DebuggerCanonicalize(TypeDesc type) + { + if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) + return type.ConvertToCanonForm(CanonicalFormKind.Specific); + + return type; + } + + private uint GetVariableTypeIndex(TypeDesc type, bool needsCompleteIndex) + { + uint variableTypeIndex = 0; + if (type.IsPrimitive) + { + variableTypeIndex = PrimitiveTypeDescriptor.GetPrimitiveTypeIndex(type); + } + else + { + type = DebuggerCanonicalize(type); + + if ((type.IsDefType && !type.IsValueType) || type.IsArray) + { + // The type index of a variable/field of a reference type is wrapped + // in a pointer, as these fields are really pointer fields, and the data is on the heap + variableTypeIndex = 0; + if (_knownReferenceWrappedTypes.TryGetValue(type, out variableTypeIndex)) + { + return variableTypeIndex; + } + else + { + uint typeindex = GetTypeIndex(type, false); + + PointerTypeDescriptor descriptor = new PointerTypeDescriptor(); + descriptor.ElementType = typeindex; + descriptor.Is64Bit = Is64Bit ? 1 : 0; + descriptor.IsConst = 0; + descriptor.IsReference = 1; + + variableTypeIndex = _objectWriter.GetPointerTypeIndex(descriptor); + _knownReferenceWrappedTypes[type] = variableTypeIndex; + + return variableTypeIndex; + } + } + else if (type.IsEnum) + { + // Enum's use the LF_ENUM record as the variable type index, but it is required to also emit a regular structure record for them. + + if (_enumTypes.TryGetValue(type, out variableTypeIndex)) + return variableTypeIndex; + + variableTypeIndex = GetEnumTypeIndex(type); + + GetTypeIndex(type, false); // Ensure regular structure record created + } + + variableTypeIndex = GetTypeIndex(type, needsCompleteIndex); + } + return variableTypeIndex; + } + + /// + /// Get type index for type without the type being wrapped as a reference (as a variable or field must be) + /// + /// + /// + /// + private uint GetTypeIndex(TypeDesc type, bool needsCompleteType) + { + uint typeIndex = 0; + if (needsCompleteType ? + _completeKnownTypes.TryGetValue(type, out typeIndex) + : _knownTypes.TryGetValue(type, out typeIndex)) + { + return typeIndex; + } + else + { + return GetNewTypeIndex(type, needsCompleteType); + } + } + + private uint GetNewTypeIndex(TypeDesc type, bool needsCompleteType) + { + if (type.IsArray) + { + return GetArrayTypeIndex(type); + } + else if (type.IsDefType) + { + return GetClassTypeIndex(type, needsCompleteType); + } + else if (type.IsPointer) + { + return GetPointerTypeIndex(((ParameterizedType)type).ParameterType); + } + else if (type.IsByRef) + { + return GetByRefTypeIndex(((ParameterizedType)type).ParameterType); + } + + return 0; + } + + private uint GetPointerTypeIndex(TypeDesc pointeeType) + { + uint typeIndex; + + if (_pointerTypes.TryGetValue(pointeeType, out typeIndex)) + return typeIndex; + + PointerTypeDescriptor descriptor = new PointerTypeDescriptor(); + descriptor.ElementType = GetVariableTypeIndex(pointeeType, false); + descriptor.Is64Bit = Is64Bit ? 1 : 0; + descriptor.IsConst = 0; + descriptor.IsReference = 0; + + // Calling GetVariableTypeIndex may have filled in _pointerTypes + if (_pointerTypes.TryGetValue(pointeeType, out typeIndex)) + return typeIndex; + + typeIndex = _objectWriter.GetPointerTypeIndex(descriptor); + _pointerTypes.Add(pointeeType, typeIndex); + return typeIndex; + } + + private uint GetByRefTypeIndex(TypeDesc pointeeType) + { + uint typeIndex; + + if (_byRefTypes.TryGetValue(pointeeType, out typeIndex)) + return typeIndex; + + PointerTypeDescriptor descriptor = new PointerTypeDescriptor(); + descriptor.ElementType = GetVariableTypeIndex(pointeeType, false); + descriptor.Is64Bit = Is64Bit ? 1 : 0; + descriptor.IsConst = 0; + descriptor.IsReference = 1; + + // Calling GetVariableTypeIndex may have filled in _byRefTypes + if (_byRefTypes.TryGetValue(pointeeType, out typeIndex)) + return typeIndex; + + typeIndex = _objectWriter.GetPointerTypeIndex(descriptor); + _byRefTypes.Add(pointeeType, typeIndex); + return typeIndex; + } + + private uint GetEnumTypeIndex(TypeDesc type) + { + System.Diagnostics.Debug.Assert(type.IsEnum, "GetEnumTypeIndex was called with wrong type"); + DefType defType = type as DefType; + System.Diagnostics.Debug.Assert(defType != null, "GetEnumTypeIndex was called with non def type"); + List fieldsDescriptors = new List(); + foreach (var field in defType.GetFields()) + { + if (field.IsLiteral) + { + fieldsDescriptors.Add(field); + } + } + EnumTypeDescriptor enumTypeDescriptor = new EnumTypeDescriptor + { + ElementCount = (ulong)fieldsDescriptors.Count, + ElementType = PrimitiveTypeDescriptor.GetPrimitiveTypeIndex(defType.UnderlyingType), + Name = _objectWriter.GetMangledName(type), + }; + EnumRecordTypeDescriptor[] typeRecords = new EnumRecordTypeDescriptor[enumTypeDescriptor.ElementCount]; + for (int i = 0; i < fieldsDescriptors.Count; ++i) + { + FieldDesc field = fieldsDescriptors[i]; + EnumRecordTypeDescriptor recordTypeDescriptor; + recordTypeDescriptor.Value = GetEnumRecordValue(field); + recordTypeDescriptor.Name = field.Name; + typeRecords[i] = recordTypeDescriptor; + } + uint typeIndex = _objectWriter.GetEnumTypeIndex(enumTypeDescriptor, typeRecords); + return typeIndex; + } + + private uint GetArrayTypeIndex(TypeDesc type) + { + System.Diagnostics.Debug.Assert(type.IsArray, "GetArrayTypeIndex was called with wrong type"); + ArrayType arrayType = (ArrayType)type; + + uint elementSize = (uint)type.Context.Target.PointerSize; + LayoutInt layoutElementSize = arrayType.GetElementSize(); + if (!layoutElementSize.IsIndeterminate) + elementSize = (uint)layoutElementSize.AsInt; + + ArrayTypeDescriptor arrayTypeDescriptor = new ArrayTypeDescriptor + { + Rank = (uint)arrayType.Rank, + ElementType = GetVariableTypeIndex(arrayType.ElementType, false), + Size = elementSize, + IsMultiDimensional = arrayType.IsMdArray ? 1 : 0 + }; + + ClassTypeDescriptor classDescriptor = new ClassTypeDescriptor + { + IsStruct = 0, + Name = _objectWriter.GetMangledName(type), + BaseClassId = GetTypeIndex(arrayType.BaseType, false) + }; + + uint typeIndex = _objectWriter.GetArrayTypeIndex(classDescriptor, arrayTypeDescriptor); + _knownTypes[type] = typeIndex; + _completeKnownTypes[type] = typeIndex; + return typeIndex; + } + + private ulong GetEnumRecordValue(FieldDesc field) + { + var ecmaField = field as EcmaField; + if (ecmaField != null) + { + MetadataReader reader = ecmaField.MetadataReader; + FieldDefinition fieldDef = reader.GetFieldDefinition(ecmaField.Handle); + ConstantHandle defaultValueHandle = fieldDef.GetDefaultValue(); + if (!defaultValueHandle.IsNil) + { + return HandleConstant(ecmaField.Module, defaultValueHandle); + } + } + return 0; + } + + private ulong HandleConstant(EcmaModule module, ConstantHandle constantHandle) + { + MetadataReader reader = module.MetadataReader; + Constant constant = reader.GetConstant(constantHandle); + BlobReader blob = reader.GetBlobReader(constant.Value); + switch (constant.TypeCode) + { + case ConstantTypeCode.Byte: + return (ulong)blob.ReadByte(); + case ConstantTypeCode.Int16: + return (ulong)blob.ReadInt16(); + case ConstantTypeCode.Int32: + return (ulong)blob.ReadInt32(); + case ConstantTypeCode.Int64: + return (ulong)blob.ReadInt64(); + case ConstantTypeCode.SByte: + return (ulong)blob.ReadSByte(); + case ConstantTypeCode.UInt16: + return (ulong)blob.ReadUInt16(); + case ConstantTypeCode.UInt32: + return (ulong)blob.ReadUInt32(); + case ConstantTypeCode.UInt64: + return (ulong)blob.ReadUInt64(); + } + System.Diagnostics.Debug.Assert(false); + return 0; + } + + TypeDesc GetFieldDebugType(FieldDesc field) + { + TypeDesc type = field.FieldType; + + // TODO: check the type's generic complexity + if (NodeFactory.LazyGenericsPolicy.UsesLazyGenerics(type)) + { + type = type.ConvertToCanonForm(CanonicalFormKind.Specific); + } + + return type; + } + + private uint GetClassTypeIndex(TypeDesc type, bool needsCompleteType) + { + DefType defType = type as DefType; + System.Diagnostics.Debug.Assert(defType != null, "GetClassTypeIndex was called with non def type"); + ClassTypeDescriptor classTypeDescriptor = new ClassTypeDescriptor + { + IsStruct = type.IsValueType ? 1 : 0, + Name = _objectWriter.GetMangledName(type), + BaseClassId = 0 + }; + + uint typeIndex = _objectWriter.GetClassTypeIndex(classTypeDescriptor); + _knownTypes[type] = typeIndex; + + if (type.HasBaseType && !type.IsValueType) + { + classTypeDescriptor.BaseClassId = GetTypeIndex(defType.BaseType, true); + } + + List fieldsDescs = new List(); + List nonGcStaticFields = new List(); + List gcStaticFields = new List(); + List threadStaticFields = new List(); + + bool isCanonical = defType.IsCanonicalSubtype(CanonicalFormKind.Any); + + foreach (var fieldDesc in defType.GetFields()) + { + if (fieldDesc.HasRva || fieldDesc.IsLiteral) + continue; + + if (isCanonical && fieldDesc.IsStatic) + continue; + + LayoutInt fieldOffset = fieldDesc.Offset; + int fieldOffsetEmit = fieldOffset.IsIndeterminate ? 0xBAAD : fieldOffset.AsInt; + DataFieldDescriptor field = new DataFieldDescriptor + { + FieldTypeIndex = GetVariableTypeIndex(GetFieldDebugType(fieldDesc), false), + Offset = (ulong)fieldOffsetEmit, + Name = fieldDesc.Name + }; + + if (fieldDesc.IsStatic) + { + if (fieldDesc.IsThreadStatic) + threadStaticFields.Add(field); + else if (fieldDesc.HasGCStaticBase) + gcStaticFields.Add(field); + else + nonGcStaticFields.Add(field); + } + else + { + fieldsDescs.Add(field); + } + } + + InsertStaticFieldRegionMember(fieldsDescs, defType, nonGcStaticFields, WindowsNodeMangler.NonGCStaticMemberName, "__type_" + WindowsNodeMangler.NonGCStaticMemberName, false); + InsertStaticFieldRegionMember(fieldsDescs, defType, gcStaticFields, WindowsNodeMangler.GCStaticMemberName, "__type_" + WindowsNodeMangler.GCStaticMemberName, Abi == TargetAbi.CoreRT); + InsertStaticFieldRegionMember(fieldsDescs, defType, threadStaticFields, WindowsNodeMangler.ThreadStaticMemberName, "__type_" + WindowsNodeMangler.ThreadStaticMemberName, Abi == TargetAbi.CoreRT); + + DataFieldDescriptor[] fields = new DataFieldDescriptor[fieldsDescs.Count]; + for (int i = 0; i < fieldsDescs.Count; ++i) + { + fields[i] = fieldsDescs[i]; + } + + LayoutInt elementSize = defType.GetElementSize(); + int elementSizeEmit = elementSize.IsIndeterminate ? 0xBAAD : elementSize.AsInt; + ClassFieldsTypeDescriptor fieldsDescriptor = new ClassFieldsTypeDescriptor + { + Size = (ulong)elementSizeEmit, + FieldsCount = fieldsDescs.Count + }; + + uint completeTypeIndex = _objectWriter.GetCompleteClassTypeIndex(classTypeDescriptor, fieldsDescriptor, fields); + _completeKnownTypes[type] = completeTypeIndex; + + if (needsCompleteType) + return completeTypeIndex; + else + return typeIndex; + } + + private void InsertStaticFieldRegionMember(List fieldDescs, DefType defType, List staticFields, string staticFieldForm, string staticFieldFormTypePrefix, bool staticDataInObject) + { + if (staticFields != null && (staticFields.Count > 0)) + { + // Generate struct symbol for type describing individual fields of the statics region + ClassFieldsTypeDescriptor fieldsDescriptor = new ClassFieldsTypeDescriptor + { + Size = (ulong)0, + FieldsCount = staticFields.Count + }; + + ClassTypeDescriptor classTypeDescriptor = new ClassTypeDescriptor + { + IsStruct = !staticDataInObject ? 1 : 0, + Name = staticFieldFormTypePrefix + _objectWriter.GetMangledName(defType), + BaseClassId = 0 + }; + + if (staticDataInObject) + { + classTypeDescriptor.BaseClassId = GetTypeIndex(defType.Context.GetWellKnownType(WellKnownType.Object), true); + } + + uint staticFieldRegionTypeIndex = _objectWriter.GetCompleteClassTypeIndex(classTypeDescriptor, fieldsDescriptor, staticFields.ToArray()); + uint staticFieldRegionSymbolTypeIndex = staticFieldRegionTypeIndex; + + // This means that access to this static region is done via a double indirection + if (staticDataInObject) + { + PointerTypeDescriptor pointerTypeDescriptor = new PointerTypeDescriptor(); + pointerTypeDescriptor.Is64Bit = Is64Bit ? 1 : 0; + pointerTypeDescriptor.IsConst = 0; + pointerTypeDescriptor.IsReference = 0; + pointerTypeDescriptor.ElementType = staticFieldRegionTypeIndex; + + uint intermediatePointerDescriptor = _objectWriter.GetPointerTypeIndex(pointerTypeDescriptor); + pointerTypeDescriptor.ElementType = intermediatePointerDescriptor; + staticFieldRegionSymbolTypeIndex = _objectWriter.GetPointerTypeIndex(pointerTypeDescriptor); + } + + DataFieldDescriptor staticRegionField = new DataFieldDescriptor + { + FieldTypeIndex = staticFieldRegionSymbolTypeIndex, + Offset = 0xFFFFFFFF, + Name = staticFieldForm + }; + + fieldDescs.Add(staticRegionField); + } + } + + private ITypesDebugInfoWriter _objectWriter; + private Dictionary _knownTypes = new Dictionary(); + private Dictionary _completeKnownTypes = new Dictionary(); + private Dictionary _knownReferenceWrappedTypes = new Dictionary(); + private Dictionary _pointerTypes = new Dictionary(); + private Dictionary _enumTypes = new Dictionary(); + private Dictionary _byRefTypes = new Dictionary(); + private Dictionary _thisTypes = new Dictionary(); + private Dictionary _methodIndices = new Dictionary(); + private Dictionary _methodIdIndices = new Dictionary(); + + public ICollection> CompleteKnownTypes => _completeKnownTypes; + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/UtcNameMangler.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/UtcNameMangler.cs index f2db7201fa..5943a5ed61 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/UtcNameMangler.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/UtcNameMangler.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Collections.ObjectModel; @@ -21,7 +22,11 @@ namespace ILCompiler public bool isImport; public uint tlsIndexOrdinal; public ReadOnlyDictionary typeOrdinals; + public ReadOnlyDictionary nonGcStaticOrdinals; + public ReadOnlyDictionary gcStaticOrdinals; + public ReadOnlyDictionary tlsStaticOrdinals; public ReadOnlyDictionary methodOrdinals; + public ReadOnlyDictionary unboxingStubMethodOrdinals; public ReadOnlyDictionary methodDictionaryOrdinals; } @@ -53,6 +58,12 @@ namespace ILCompiler { _exportOrdinals = ordinals; } + + // Use SHA256 hash here to provide a high degree of uniqueness to symbol names without requiring them to be long + // This hash function provides an exceedingly high likelihood that no two strings will be given equal symbol names + // This is not considered used for security purpose; however collisions would be highly unfortunate as they will cause compilation + // failure. + _sha256 = SHA256.Create(); } private bool GetMethodOrdinal(MethodDesc method, out uint ordinal) @@ -191,15 +202,6 @@ namespace ILCompiler if (mangledName != literal) { - if (_sha256 == null) - { - // Use SHA256 hash here to provide a high degree of uniqueness to symbol names without requiring them to be long - // This hash function provides an exceedingly high likelihood that no two strings will be given equal symbol names - // This is not considered used for security purpose; however collisions would be highly unfortunate as they will cause compilation - // failure. - _sha256 = SHA256.Create(); - } - var hash = _sha256.ComputeHash(GetBytesFromString(literal)); mangledName += "_" + BitConverter.ToString(hash).Replace("-", ""); } @@ -228,7 +230,7 @@ namespace ILCompiler result = string.Concat(origName, deDuplicatePrefix, (iter++).ToStringInvariant()); } return result; - } + } public override string GetMangledTypeName(TypeDesc type) { @@ -386,6 +388,23 @@ namespace ILCompiler return name; } + public string GetLinkageNameForPInvokeMethod(MethodDesc method, out int ordinal) + { + string methodName = method.GetPInvokeMethodMetadata().Name; + + if (methodName.StartsWith("#")) + { + if (int.TryParse(methodName.Substring(1), out ordinal)) + { + string moduleName = method.GetPInvokeMethodMetadata().Module; + return moduleName + methodName; + } + } + + ordinal = -1; + return methodName; + } + private Utf8String ComputeMangledNameMethodWithoutInstantiation(MethodDesc method) { Debug.Assert(method == method.GetMethodDefinition()); @@ -401,25 +420,35 @@ namespace ILCompiler { foreach (var m in method.OwningType.GetMethods()) { - string name = SanitizeName(m.Name); - uint ordinal; + string name; - // Ensure that name is unique and update our tables accordingly. - if (GetMethodOrdinal(m, out ordinal)) + if (m.IsPInvoke) { - name += OrdinalPrefix + ordinal; + int ordinal; + name = GetLinkageNameForPInvokeMethod(m, out ordinal); } else { - name = DisambiguateName(name, deduplicator); + name = SanitizeName(m.Name); + uint ordinal; + + // Ensure that name is unique and update our tables accordingly. + if (GetMethodOrdinal(m, out ordinal)) + { + name += OrdinalPrefix + ordinal; + } + else + { + name = DisambiguateName(name, deduplicator); + } + + Debug.Assert(!deduplicator.Contains(name)); + deduplicator.Add(name); + + if (prependTypeName != null) + name = prependTypeName + "__" + name; } - Debug.Assert(!deduplicator.Contains(name)); - deduplicator.Add(name); - - if (prependTypeName != null) - name = prependTypeName + "__" + name; - _mangledMethodNames = _mangledMethodNames.Add(m, name); } } @@ -584,6 +613,11 @@ namespace ILCompiler return mangledName; } + public string GetMangledDataBlobName(FieldDesc field) + { + return "__Data_" + ComputeMangledFieldName(field); + } + public string GetImportedTlsIndexPrefix() { uint ordinal; @@ -628,4 +662,4 @@ namespace ILCompiler } } } -} \ No newline at end of file +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/UtcStackTraceEmissionPolicy.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/UtcStackTraceEmissionPolicy.cs new file mode 100644 index 0000000000..c17a8fb317 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/UtcStackTraceEmissionPolicy.cs @@ -0,0 +1,195 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.IO; + +using Internal.TypeSystem; + +namespace ILCompiler +{ + public class UtcStackTraceEmissionPolicy : StackTraceEmissionPolicy + { + /// + /// List of exception files to load. + /// + List _stackTraceExceptionFiles = new List(); + + /// + /// Explicitly blacklisted namespaces. + /// + HashSet _namespaceBlacklist = new HashSet(); + + /// + /// Explicitly whitelisted namespaces. + /// + HashSet _namespaceWhitelist = new HashSet(); + + /// + /// Explicitly blacklisted types. + /// + HashSet _typeBlacklist = new HashSet(); + + /// + /// Explicitly whitelisted types. + /// + HashSet _typeWhitelist = new HashSet(); + + /// + /// Cache of explicitly enabled / disabled types after their eligibility has been resolved. + /// + Dictionary _cachedTypeEligibility = new Dictionary(); + + public UtcStackTraceEmissionPolicy() + { + // load the default exception file next to the NUTC app + LoadExceptionFile(Path.Combine(GetAppExeDirectory(), "StackTraceExceptions.txt")); + } + + public override bool ShouldIncludeMethod(MethodDesc method) + { + DefType type = method.GetTypicalMethodDefinition().OwningType as DefType; + if (type != null) + return !IsTypeExplicitlyDisabled(type); + return false; + } + + /// + /// Check explicit type / namespace blacklist and update the cache. + /// + bool IsTypeExplicitlyDisabled(DefType type) + { + bool result; + if (_cachedTypeEligibility.TryGetValue(type, out result)) + { + return result; + } + + string typeName; + result = IsTypeExplicitlyDisabledInner(type, out typeName); + _cachedTypeEligibility.Add(type, result); + return result; + } + + /// + /// Check explicit type / namespace blacklist ignoring cache management. Optionally output the type name. + /// + bool IsTypeExplicitlyDisabledInner(DefType type, out string outputTypeName) + { + string typeName; + bool isTypeDisabled = false; + MetadataType metaDataType = type as MetadataType; + + if (metaDataType != null && metaDataType.ContainingType != null) + { + // Nested type + isTypeDisabled = IsTypeExplicitlyDisabledInner(metaDataType.ContainingType, out typeName); + typeName = typeName + '+' + metaDataType.Name; + } + else + { + // Namespace type + typeName = type.Name; + int lastPeriod = typeName.LastIndexOf('.'); + string namespaceName = null; + + if (lastPeriod != -1) + { + namespaceName = typeName.Substring(0, lastPeriod); + } + + isTypeDisabled = _namespaceBlacklist.Contains(namespaceName) && _namespaceWhitelist.Contains(namespaceName); + } + + if (_typeBlacklist.Contains(typeName)) + { + isTypeDisabled = true; + } + + if (_typeWhitelist.Contains(typeName)) + { + isTypeDisabled = false; + } + + outputTypeName = typeName; + return isTypeDisabled; + } + + /// + /// Identify the directory where the main executable resides. + /// + string GetAppExeDirectory() + { +#if PROJECTN + var process = Process.GetCurrentProcess(); + string fullPath = process.MainModule.FileName; + return Path.GetDirectoryName(fullPath); +#else + Debug.Assert(false); + return null; +#endif + } + + /// + /// Load the default exception file and possibly additional custom stack trace exception files. + /// + void LoadExceptionFile(string exceptionFileName) + { +#if PROJECTN + if (!File.Exists(exceptionFileName)) + return; + + const int LeaderCharacterCount = 2; + + using (TextReader tr = File.OpenText(exceptionFileName)) + { + string line = tr.ReadLine(); + while (line != null) + { + // Currently supported leader character sequences are: + // N+nnn .. explicitly whitelist namespace nnn + // N-nnn .. explicitly blacklist namespace nnn + // T+ttt .. explicitly whitelist namespace-qualified type ttt (nested types separated by +) + // T-ttt .. explicitly blacklist namespace-qualified type ttt + // Type specifications override namespace specifications. + // Whenever both whitelist and blacklist is specified for an element, whitelist wins. + + // Reserve empty lines and lines starting with # for comments + if (line.Length > LeaderCharacterCount && line[0] != '#') + { + char char1 = line[0]; + char char2 = line[1]; + string arg = line.Substring(2); + + if (char1 == 'N' && char2 == '+') + { + _namespaceWhitelist.Add(arg); + } + else if (char1 == 'N' && char2 == '-') + { + _namespaceBlacklist.Add(arg); + } + else if (char1 == 'T' && char2 == '+') + { + _typeWhitelist.Add(arg); + } + else if (char1 == 'T' && char2 == '-') + { + _typeBlacklist.Add(arg); + } + else + { + // unexpected pattern + Debug.Assert(false); + } + } + + line = tr.ReadLine(); + } + } +#endif + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/VTableSliceProvider.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/VTableSliceProvider.cs new file mode 100644 index 0000000000..18455f2b49 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/VTableSliceProvider.cs @@ -0,0 +1,28 @@ +// 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 Internal.TypeSystem; +using ILCompiler.DependencyAnalysis; + +namespace ILCompiler +{ + /// + /// Provides VTable information for a specific type. + /// + public abstract class VTableSliceProvider + { + internal abstract VTableSliceNode GetSlice(TypeDesc type); + } + + /// + /// Provides VTable information that collects data during the compilation to build a VTable for a type. + /// + public sealed class LazyVTableSliceProvider : VTableSliceProvider + { + internal override VTableSliceNode GetSlice(TypeDesc type) + { + return new LazilyBuiltVTableSliceNode(type); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/VectorOfTFieldLayoutAlgorithm.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/VectorOfTFieldLayoutAlgorithm.cs new file mode 100644 index 0000000000..f2fb5af3ee --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/VectorOfTFieldLayoutAlgorithm.cs @@ -0,0 +1,77 @@ +// 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 Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + /// + /// Represents an algorithm that computes field layout for the SIMD Vector<T> type + /// depending on the target details. + /// + internal class VectorOfTFieldLayoutAlgorithm : FieldLayoutAlgorithm + { + private readonly FieldLayoutAlgorithm _fallbackAlgorithm; + + public VectorOfTFieldLayoutAlgorithm(FieldLayoutAlgorithm fallbackAlgorithm) + { + _fallbackAlgorithm = fallbackAlgorithm; + } + + public override ComputedInstanceFieldLayout ComputeInstanceLayout(DefType defType, InstanceLayoutKind layoutKind) + { + TargetDetails targetDetails = defType.Context.Target; + + ComputedInstanceFieldLayout layoutFromMetadata = _fallbackAlgorithm.ComputeInstanceLayout(defType, layoutKind); + + LayoutInt instanceFieldSize; + + if (targetDetails.MaximumSimdVectorLength == SimdVectorLength.Vector128Bit) + { + instanceFieldSize = new LayoutInt(16); + } + else if (targetDetails.MaximumSimdVectorLength == SimdVectorLength.Vector256Bit) + { + instanceFieldSize = new LayoutInt(32); + } + else + { + Debug.Assert(targetDetails.MaximumSimdVectorLength == SimdVectorLength.None); + return layoutFromMetadata; + } + + return new ComputedInstanceFieldLayout + { + ByteCountUnaligned = instanceFieldSize, + ByteCountAlignment = layoutFromMetadata.ByteCountAlignment, + FieldAlignment = layoutFromMetadata.FieldAlignment, + FieldSize = instanceFieldSize, + Offsets = layoutFromMetadata.Offsets, + }; + } + + public unsafe override ComputedStaticFieldLayout ComputeStaticFieldLayout(DefType defType, StaticLayoutKind layoutKind) + { + return _fallbackAlgorithm.ComputeStaticFieldLayout(defType, layoutKind); + } + + public override bool ComputeContainsGCPointers(DefType type) + { + return false; + } + + public override ValueTypeShapeCharacteristics ComputeValueTypeShapeCharacteristics(DefType type) + { + return _fallbackAlgorithm.ComputeValueTypeShapeCharacteristics(type); + } + + public override DefType ComputeHomogeneousFloatAggregateElementType(DefType type) + { + return _fallbackAlgorithm.ComputeHomogeneousFloatAggregateElementType(type); + } + + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/VirtualMethodCallHelper.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/VirtualMethodCallHelper.cs index 8949525bcc..27b1198c6e 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/VirtualMethodCallHelper.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/VirtualMethodCallHelper.cs @@ -44,7 +44,9 @@ namespace ILCompiler private static int GetNumberOfBaseSlots(NodeFactory factory, TypeDesc owningType, bool countDictionarySlots) { int baseSlots = 0; + TypeDesc baseType = owningType.BaseType; + TypeDesc templateBaseType = owningType.ConvertToCanonForm(CanonicalFormKind.Specific).BaseType; while (baseType != null) { @@ -53,16 +55,38 @@ namespace ILCompiler // something like Base<__Canon, string>. We would get "0 slots used" for weird // base types like this. baseType = baseType.ConvertToCanonForm(CanonicalFormKind.Specific); + templateBaseType = templateBaseType.ConvertToCanonForm(CanonicalFormKind.Specific); + + // + // In the universal canonical types case, we could have base types in the hierarchy that are partial universal canonical types. + // The presence of these types could cause incorrect vtable layouts, so we need to fully canonicalize them and walk the + // hierarchy of the template type of the original input type to detect these cases. + // + // Exmaple: we begin with Derived<__UniversalCanon> and walk the template hierarchy: + // + // class Derived : Middle { } // -> Template is Derived<__UniversalCanon> and needs a dictionary slot + // // -> Basetype tempalte is Middle<__UniversalCanon, MyStruct>. It's a partial + // Universal canonical type, so we need to fully canonicalize it. + // + // class Middle : Base { } // -> Template is Middle<__UniversalCanon, __UniversalCanon> and needs a dictionary slot + // // -> Basetype template is Base<__UniversalCanon> + // + // class Base { } // -> Template is Base<__UniversalCanon> and needs a dictionary slot. + // + // If we had not fully canonicalized the Middle class template, we would have ended up with Base, which does not need + // a dictionary slot, meaning we would have created a vtable layout that the runtime does not expect. + // // For types that have a generic dictionary, the introduced virtual method slots are // prefixed with a pointer to the generic dictionary. - if (baseType.HasGenericDictionarySlot() && countDictionarySlots) + if ((baseType.HasGenericDictionarySlot() || templateBaseType.HasGenericDictionarySlot()) && countDictionarySlots) baseSlots++; IReadOnlyList baseVirtualSlots = factory.VTable(baseType).Slots; baseSlots += baseVirtualSlots.Count; baseType = baseType.BaseType; + templateBaseType = templateBaseType.BaseType; } return baseSlots; diff --git a/external/corert/src/ILCompiler.Compiler/src/Compiler/WindowsNodeMangler.cs b/external/corert/src/ILCompiler.Compiler/src/Compiler/WindowsNodeMangler.cs index 82c0a92090..81f5e1592a 100644 --- a/external/corert/src/ILCompiler.Compiler/src/Compiler/WindowsNodeMangler.cs +++ b/external/corert/src/ILCompiler.Compiler/src/Compiler/WindowsNodeMangler.cs @@ -13,6 +13,10 @@ namespace ILCompiler // public class WindowsNodeMangler : NodeMangler { + public const string NonGCStaticMemberName = "__NONGCSTATICS"; + public const string GCStaticMemberName = "__GCSTATICS"; + public const string ThreadStaticMemberName = "__THREADSTATICS"; + // Mangled name of boxed version of a type public sealed override string MangledBoxedTypeName(TypeDesc type) { @@ -28,22 +32,22 @@ namespace ILCompiler mangledJustTypeName = MangledBoxedTypeName(type); else mangledJustTypeName = NameMangler.GetMangledTypeName(type); - return mangledJustTypeName + "::`vftable'"; + return "const " + mangledJustTypeName + "::`vftable'"; } public sealed override string GCStatics(TypeDesc type) { - return NameMangler.GetMangledTypeName(type) + "::__GCSTATICS"; + return NameMangler.GetMangledTypeName(type) + "::" + GCStaticMemberName; } public sealed override string NonGCStatics(TypeDesc type) { - return NameMangler.GetMangledTypeName(type) + "::__NONGCSTATICS"; + return NameMangler.GetMangledTypeName(type) + "::" + NonGCStaticMemberName; } public sealed override string ThreadStatics(TypeDesc type) { - return NameMangler.CompilationUnitPrefix + NameMangler.GetMangledTypeName(type) + "::__THREADSTATICS"; + return NameMangler.CompilationUnitPrefix + NameMangler.GetMangledTypeName(type) + "::" + ThreadStaticMemberName; } public sealed override string TypeGenericDictionary(TypeDesc type) diff --git a/external/corert/src/ILCompiler.Compiler/src/IL/ILImporter.Scanner.cs b/external/corert/src/ILCompiler.Compiler/src/IL/ILImporter.Scanner.cs index 4011819b1d..009dc068bb 100644 --- a/external/corert/src/ILCompiler.Compiler/src/IL/ILImporter.Scanner.cs +++ b/external/corert/src/ILCompiler.Compiler/src/IL/ILImporter.Scanner.cs @@ -34,16 +34,23 @@ namespace Internal.IL private class BasicBlock { // Common fields + public enum ImportState : byte + { + Unmarked, + IsPending + } + public BasicBlock Next; public int StartOffset; - public int EndOffset; + public ImportState State = ImportState.Unmarked; public bool TryStart; public bool FilterStart; public bool HandlerStart; } + private bool _isReadOnly; private TypeDesc _constrained; private int _currentInstructionOffset; @@ -69,7 +76,7 @@ namespace Internal.IL // This is e.g. an "extern" method in C# without a DllImport or InternalCall. if (methodIL == null) { - throw new TypeSystemException.InvalidProgramException(ExceptionStringID.InvalidProgramSpecific, method); + ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramSpecific, method); } _compilation = compilation; @@ -102,6 +109,46 @@ namespace Internal.IL public DependencyList Import() { + TypeDesc owningType = _canonMethod.OwningType; + if (_factory.TypeSystemContext.HasLazyStaticConstructor(owningType)) + { + // Don't trigger cctor if this is a fallback compilation (bad cctor could have been the reason for fallback). + // Otherwise follow the rules from ECMA-335 I.8.9.5. + if (!_isFallbackBodyCompilation && + (_canonMethod.Signature.IsStatic || _canonMethod.IsConstructor || owningType.IsValueType)) + { + // For beforefieldinit, we can wait for field access. + if (!((MetadataType)owningType).IsBeforeFieldInit) + { + MethodDesc method = _methodIL.OwningMethod; + if (method.OwningType.IsRuntimeDeterminedSubtype) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.GetNonGCStaticBase, method.OwningType), "Owning type cctor"); + } + else + { + _dependencies.Add(_factory.ReadyToRunHelper(ReadyToRunHelperId.GetNonGCStaticBase, method.OwningType), "Owning type cctor"); + } + } + } + } + + if (_canonMethod.IsSynchronized) + { + const string reason = "Synchronized method"; + if (_canonMethod.Signature.IsStatic) + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.MonitorEnterStatic), reason); + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.MonitorExitStatic), reason); + } + else + { + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.MonitorEnter), reason); + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.MonitorExit), reason); + } + + } + FindBasicBlocks(); ImportBasicBlocks(); @@ -139,7 +186,6 @@ namespace Internal.IL private void MarkInstructionBoundary() { } private void EndImportingBasicBlock(BasicBlock basicBlock) { } - private void EndImportingInstruction() { } private void StartImportingBasicBlock(BasicBlock basicBlock) { @@ -152,6 +198,17 @@ namespace Internal.IL MarkBasicBlock(_basicBlocks[region.HandlerOffset]); if (region.Kind == ILExceptionRegionKind.Filter) MarkBasicBlock(_basicBlocks[region.FilterOffset]); + + // Once https://github.com/dotnet/corert/issues/3460 is done, this should be deleted. + // Throwing InvalidProgram is not great, but we want to do *something* if this happens + // because doing nothing means problems at runtime. This is not worth piping a + // a new exception with a fancy message for. + if (region.Kind == ILExceptionRegionKind.Catch) + { + TypeDesc catchType = (TypeDesc)_methodIL.GetObject(region.ClassToken); + if (catchType.IsRuntimeDeterminedSubtype) + ThrowHelper.ThrowInvalidProgramException(); + } } } @@ -165,9 +222,17 @@ namespace Internal.IL _currentInstructionOffset = _currentOffset; } + private void EndImportingInstruction() + { + // The instruction should have consumed any prefixes. + _constrained = null; + _isReadOnly = false; + } + private void ImportJmp(int token) { - // TODO + // JMP is kind of like a tail call (with no arguments pushed on the stack). + ImportCall(ILOpcode.call, token); } private void ImportCasting(ILOpcode opcode, int token) @@ -230,30 +295,6 @@ namespace Internal.IL Debug.Assert(false); break; } - // If we're scanning the fallback body because scanning the real body failed, don't trigger cctor. - // Accessing the cctor could have been a reason why we failed. - if (!_isFallbackBodyCompilation) - { - // Do we need to run the cctor? - TypeDesc owningType = runtimeDeterminedMethod.OwningType; - if (_factory.TypeSystemContext.HasLazyStaticConstructor(owningType)) - { - // For beforefieldinit, we can wait for field access. - if (!((MetadataType)owningType).IsBeforeFieldInit) - { - // Accessing the static base will trigger the cctor. - if (owningType.IsRuntimeDeterminedSubtype) - { - _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.GetNonGCStaticBase, owningType), reason); - } - else - { - _dependencies.Add(_factory.ReadyToRunHelper(ReadyToRunHelperId.GetNonGCStaticBase, owningType), reason); - } - } - } - } - if (opcode == ILOpcode.newobj) { TypeDesc owningType = runtimeDeterminedMethod.OwningType; @@ -261,12 +302,8 @@ namespace Internal.IL { // String .ctor handled specially below } - else + else if (owningType.IsGCPointer) { - // Nullable needs to be unwrapped. - if (owningType.IsNullable) - owningType = owningType.Instantiation[0]; - if (owningType.IsRuntimeDeterminedSubtype) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, owningType), reason); @@ -337,10 +374,54 @@ namespace Internal.IL if (_previousInstructionOffset >= 0 && _ilBytes[_previousInstructionOffset] == (byte)ILOpcode.ldtoken) return; } + + if (IsActivatorDefaultConstructorOf(method)) + { + if (runtimeDeterminedMethod.IsRuntimeDeterminedExactMethod) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.DefaultConstructor, runtimeDeterminedMethod.Instantiation[0]), reason); + } + else + { + MethodDesc ctor = method.Instantiation[0].GetDefaultConstructor(); + if (ctor == null) + { + MetadataType activatorType = _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "Activator"); + MetadataType classWithMissingCtor = activatorType.GetKnownNestedType("ClassWithMissingConstructor"); + ctor = classWithMissingCtor.GetParameterlessConstructor(); + } + _dependencies.Add(_factory.CanonicalEntrypoint(ctor), reason); + } + + return; + } + + if (method.OwningType.IsByReferenceOfT && (method.IsConstructor || method.Name == "get_Value")) + { + return; + } + + if (IsEETypePtrOf(method)) + { + if (runtimeDeterminedMethod.IsRuntimeDeterminedExactMethod) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedMethod.Instantiation[0]), reason); + } + else + { + _dependencies.Add(_factory.ConstructedTypeSymbol(method.Instantiation[0]), reason); + } + return; + } } TypeDesc exactType = method.OwningType; + if (method.IsNativeCallable && (opcode != ILOpcode.ldftn && opcode != ILOpcode.ldvirtftn)) + { + ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramNativeCallable, method); + } + bool resolvedConstraint = false; bool forceUseRuntimeLookup = false; @@ -353,12 +434,16 @@ namespace Internal.IL // JIT compilation, and require a runtime lookup for the actual code pointer // to call. - MethodDesc directMethod = _constrained.GetClosestDefType().TryResolveConstraintMethodApprox(method.OwningType, method, out forceUseRuntimeLookup); - if (directMethod == null && _constrained.IsEnum) + TypeDesc constrained = _constrained; + if (constrained.IsRuntimeDeterminedSubtype) + constrained = constrained.ConvertToCanonForm(CanonicalFormKind.Specific); + + MethodDesc directMethod = constrained.GetClosestDefType().TryResolveConstraintMethodApprox(method.OwningType, method, out forceUseRuntimeLookup); + if (directMethod == null && constrained.IsEnum) { // Constrained calls to methods on enum methods resolve to System.Enum's methods. System.Enum is a reference // type though, so we would fail to resolve and box. We have a special path for those to avoid boxing. - directMethod = _compilation.TypeSystemContext.TryResolveConstrainedEnumMethod(_constrained, method); + directMethod = _compilation.TypeSystemContext.TryResolveConstrainedEnumMethod(constrained, method); } if (directMethod != null) @@ -373,15 +458,13 @@ namespace Internal.IL Debug.Assert(!methodAfterConstraintResolution.OwningType.IsInterface); resolvedConstraint = true; - exactType = _constrained; + exactType = constrained; } - else if (_constrained.IsValueType) + else if (constrained.IsValueType) { - // We'll need to box `this`. + // We'll need to box `this`. Note we use _constrained here, because the other one is canonical. AddBoxingDependencies(_constrained, reason); } - - _constrained = null; } MethodDesc targetMethod = methodAfterConstraintResolution; @@ -466,12 +549,51 @@ namespace Internal.IL { if (targetMethod.IsSharedByGenericInstantiations && !resolvedConstraint && !referencingArrayAddressMethod) { - _dependencies.Add(_factory.RuntimeDeterminedMethod(runtimeDeterminedMethod), reason); + ISymbolNode instParam = null; + + if (targetMethod.RequiresInstMethodDescArg()) + { + instParam = GetGenericLookupHelper(ReadyToRunHelperId.MethodDictionary, runtimeDeterminedMethod); + } + else if (targetMethod.RequiresInstMethodTableArg()) + { + bool hasHiddenParameter = true; + + if (targetMethod.IsIntrinsic) + { + if (_factory.TypeSystemContext.IsSpecialUnboxingThunkTargetMethod(targetMethod)) + hasHiddenParameter = false; + } + + if (hasHiddenParameter) + instParam = GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedMethod.OwningType); + } + + if (instParam != null) + { + _dependencies.Add(instParam, reason); + } + + _dependencies.Add(_factory.CanonicalEntrypoint(targetMethod), reason); } else { Debug.Assert(!forceUseRuntimeLookup); _dependencies.Add(_factory.MethodEntrypoint(targetMethod), reason); + + if (targetMethod.RequiresInstMethodTableArg() && resolvedConstraint) + { + if (_constrained.IsRuntimeDeterminedSubtype) + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, _constrained), reason); + else + _dependencies.Add(_factory.ConstructedTypeSymbol(_constrained), reason); + } + + if (referencingArrayAddressMethod && !_isReadOnly) + { + // Address method is special - it expects an instantiation argument, unless a readonly prefix was applied. + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedMethod.OwningType), reason); + } } } else @@ -482,7 +604,7 @@ namespace Internal.IL { instParam = _compilation.NodeFactory.MethodGenericDictionary(concreteMethod); } - else if (targetMethod.RequiresInstMethodTableArg() || referencingArrayAddressMethod) + else if (targetMethod.RequiresInstMethodTableArg() || (referencingArrayAddressMethod && !_isReadOnly)) { // Ask for a constructed type symbol because we need the vtable to get to the dictionary instParam = _compilation.NodeFactory.ConstructedTypeSymbol(concreteMethod.OwningType); @@ -529,32 +651,26 @@ namespace Internal.IL _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.GVMLookupForSlot), reason); } + else if (method.OwningType.IsInterface) + { + if (exactContextNeedsRuntimeLookup) + { + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.VirtualDispatchCell, runtimeDeterminedMethod), reason); + } + else + { + _dependencies.Add(_factory.InterfaceDispatchCell(method), reason); + } + } + else if (_compilation.HasFixedSlotVTable(method.OwningType)) + { + // No dependencies: virtual call through the vtable + } else { - ReadyToRunHelperId helper; - if (opcode == ILOpcode.ldvirtftn) - { - helper = ReadyToRunHelperId.ResolveVirtualFunction; - } - else - { - Debug.Assert(opcode == ILOpcode.callvirt); - helper = ReadyToRunHelperId.VirtualCall; - } - - if (exactContextNeedsRuntimeLookup && targetMethod.OwningType.IsInterface) - { - _dependencies.Add(GetGenericLookupHelper(helper, runtimeDeterminedMethod), reason); - } - else - { - // Get the slot defining method to make sure our virtual method use tracking gets this right. - // For normal C# code the targetMethod will always be newslot. - MethodDesc slotDefiningMethod = targetMethod.IsNewSlot ? + MethodDesc slotDefiningMethod = targetMethod.IsNewSlot ? targetMethod : MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(targetMethod); - - _dependencies.Add(_factory.ReadyToRunHelper(helper, slotDefiningMethod), reason); - } + _dependencies.Add(_factory.VirtualMethodUse(slotDefiningMethod), reason); } } @@ -597,7 +713,14 @@ namespace Internal.IL TypeDesc type = (TypeDesc)_methodIL.GetObject(token); if (!type.IsValueType) + { + if (opCode == ILOpcode.unbox_any) + { + // When applied to a reference type, unbox_any has the same effect as castclass. + ImportCasting(ILOpcode.castclass, token); + } return; + } if (type.IsRuntimeDeterminedSubtype) { @@ -624,12 +747,12 @@ namespace Internal.IL private void ImportRefAnyVal(int token) { - // TODO + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.GetRefAny), "refanyval"); } private void ImportMkRefAny(int token) { - // TODO + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.TypeHandleToRuntimeType), "mkrefany"); } private void ImportLdToken(int token) @@ -639,16 +762,30 @@ namespace Internal.IL if (obj is TypeDesc) { var type = (TypeDesc)obj; + if (type.IsRuntimeDeterminedSubtype) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, type), "ldtoken"); } else { - if (ConstructedEETypeNode.CreationAllowed(type)) - _dependencies.Add(_factory.ConstructedTypeSymbol(type), "ldtoken"); - else - _dependencies.Add(_factory.NecessaryTypeSymbol(type), "ldtoken"); + _dependencies.Add(_factory.MaximallyConstructableType(type), "ldtoken"); + } + + // If this is a ldtoken Type / GetValueInternal sequence, we're done. + BasicBlock nextBasicBlock = _basicBlocks[_currentOffset]; + if (nextBasicBlock == null) + { + if ((ILOpcode)_ilBytes[_currentOffset] == ILOpcode.call) + { + int methodToken = ReadILTokenAt(_currentOffset + 1); + var method = (MethodDesc)_methodIL.GetObject(methodToken); + if (IsRuntimeTypeHandleGetValueInternal(method)) + { + // Codegen expands this and doesn't do the normal ldtoken. + return; + } + } } _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.GetRuntimeTypeHandle), "ldtoken"); @@ -712,17 +849,36 @@ namespace Internal.IL private void ImportConstrainedPrefix(int token) { - // We convert to canon, because that's what ryujit would see. _constrained = (TypeDesc)_methodIL.GetObject(token); - if (_constrained.IsRuntimeDeterminedSubtype) - _constrained = _constrained.ConvertToCanonForm(CanonicalFormKind.Specific); + } + + private void ImportReadOnlyPrefix() + { + _isReadOnly = true; } private void ImportFieldAccess(int token, bool isStatic, string reason) { - if (isStatic) + var field = (FieldDesc)_methodIL.GetObject(token); + + // Covers both ldsfld/ldsflda and ldfld/ldflda with a static field + if (isStatic || field.IsStatic) { - var field = (FieldDesc)_methodIL.GetObject(token); + // ldsfld/ldsflda with an instance field is invalid IL + if (isStatic && !field.IsStatic) + ThrowHelper.ThrowInvalidProgramException(); + + // References to literal fields from IL body should never resolve. + // The CLR would throw a MissingFieldException while jitting and so should we. + if (field.IsLiteral) + ThrowHelper.ThrowMissingFieldException(field.OwningType, field.Name); + + if (field.HasRva) + { + // We could add a dependency to the data node, but we don't really need it. + // TODO: lazy cctor dependency + return; + } ReadyToRunHelperId helperId; if (field.IsThreadStatic) @@ -735,7 +891,6 @@ namespace Internal.IL } else { - Debug.Assert(field.IsStatic); helperId = ReadyToRunHelperId.GetNonGCStaticBase; } @@ -769,6 +924,7 @@ namespace Internal.IL private void ImportLoadString(int token) { // If we care, this can include allocating the frozen string node. + _dependencies.Add(_factory.SerializedStringObject(""), "ldstr"); } private void ImportBox(int token) @@ -778,6 +934,13 @@ namespace Internal.IL private void AddBoxingDependencies(TypeDesc type, string reason) { + Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Any)); + + // Generic code will have BOX instructions when referring to T - the instruction is a no-op + // if the substitution wasn't a value type. + if (!type.IsValueType) + return; + if (type.IsRuntimeDeterminedSubtype) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, type), reason); @@ -838,9 +1001,33 @@ namespace Internal.IL private void ImportAddressOfElement(int token) { + TypeDesc elementType = (TypeDesc)_methodIL.GetObject(token); + if (elementType.IsGCPointer && !_isReadOnly) + { + if (elementType.IsRuntimeDeterminedSubtype) + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, elementType), "ldelema"); + else + _dependencies.Add(_factory.NecessaryTypeSymbol(elementType), "ldelema"); + } + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.RngChkFail), "ldelema"); } + private void ImportBinaryOperation(ILOpcode opcode) + { + switch (opcode) + { + case ILOpcode.add_ovf: + case ILOpcode.add_ovf_un: + case ILOpcode.mul_ovf: + case ILOpcode.mul_ovf_un: + case ILOpcode.sub_ovf: + case ILOpcode.sub_ovf_un: + _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.Overflow), "_ovf"); + break; + } + } + private void ImportFallthrough(BasicBlock next) { MarkBasicBlock(next); @@ -854,6 +1041,26 @@ namespace Internal.IL + (_ilBytes[ilOffset + 3] << 24)); } + private void ReportInvalidBranchTarget(int targetOffset) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + private void ReportFallthroughAtEndOfMethod() + { + ThrowHelper.ThrowInvalidProgramException(); + } + + private void ReportMethodEndInsideInstruction() + { + ThrowHelper.ThrowInvalidProgramException(); + } + + private void ReportInvalidInstruction(ILOpcode opcode) + { + ThrowHelper.ThrowInvalidProgramException(); + } + private bool IsRuntimeHelpersInitializeArray(MethodDesc method) { if (method.IsIntrinsic && method.Name == "InitializeArray") @@ -882,6 +1089,34 @@ namespace Internal.IL return false; } + private bool IsActivatorDefaultConstructorOf(MethodDesc method) + { + if (method.IsIntrinsic && method.Name == "DefaultConstructorOf" && method.Instantiation.Length == 1) + { + MetadataType owningType = method.OwningType as MetadataType; + if (owningType != null) + { + return owningType.Name == "Activator" && owningType.Namespace == "System"; + } + } + + return false; + } + + private bool IsEETypePtrOf(MethodDesc method) + { + if (method.IsIntrinsic && method.Name == "EETypePtrOf" && method.Instantiation.Length == 1) + { + MetadataType owningType = method.OwningType as MetadataType; + if (owningType != null) + { + return owningType.Name == "EETypePtr" && owningType.Namespace == "System"; + } + } + + return false; + } + private TypeDesc GetWellKnownType(WellKnownType wellKnownType) { return _compilation.TypeSystemContext.GetWellKnownType(wellKnownType); @@ -903,7 +1138,6 @@ namespace Internal.IL private void ImportLoadIndirect(TypeDesc type) { } private void ImportStoreIndirect(int token) { } private void ImportStoreIndirect(TypeDesc type) { } - private void ImportBinaryOperation(ILOpcode opcode) { } private void ImportShiftOperation(ILOpcode opcode) { } private void ImportCompareOperation(ILOpcode opcode) { } private void ImportConvert(WellKnownType wellKnownType, bool checkOverflow, bool unsigned) { } @@ -920,7 +1154,6 @@ namespace Internal.IL private void ImportVolatilePrefix() { } private void ImportTailPrefix() { } private void ImportNoPrefix(byte mask) { } - private void ImportReadOnlyPrefix() { } private void ImportThrow() { } private void ImportInitObj(int token) { } private void ImportLoadLength() { } diff --git a/external/corert/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/NativeLibraryStartupMethod.Sorting.cs b/external/corert/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/NativeLibraryStartupMethod.Sorting.cs new file mode 100644 index 0000000000..d8bf6ac036 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/NativeLibraryStartupMethod.Sorting.cs @@ -0,0 +1,25 @@ +// 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.Collections.Generic; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL.Stubs.StartupCode +{ + partial class NativeLibraryStartupMethod + { + protected override int ClassCode => -304225482; + + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + // Should be a singleton + Debug.Assert(this == other); + return 0; + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/NativeLibraryStartupMethod.cs b/external/corert/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/NativeLibraryStartupMethod.cs new file mode 100644 index 0000000000..80bd691a01 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/NativeLibraryStartupMethod.cs @@ -0,0 +1,93 @@ +// 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.Collections.Generic; + +using Internal.TypeSystem; + +namespace Internal.IL.Stubs.StartupCode +{ + /// + /// Startup code that does initialization, Main invocation + /// and shutdown of the runtime. + /// + public sealed partial class NativeLibraryStartupMethod : ILStubMethod + { + private TypeDesc _owningType; + private MethodSignature _signature; + private IList _libraryInitializers; + + public NativeLibraryStartupMethod(TypeDesc owningType, IList libraryInitializers) + { + _owningType = owningType; + _libraryInitializers = libraryInitializers; + } + + public override TypeSystemContext Context + { + get + { + return _owningType.Context; + } + } + + public override TypeDesc OwningType + { + get + { + return _owningType; + } + } + + public override string Name + { + get + { + return "NativeLibraryStartup"; + } + } + + public override MethodIL EmitIL() + { + ILEmitter emitter = new ILEmitter(); + ILCodeStream codeStream = emitter.NewCodeStream(); + + // Allow the class library to run explicitly ordered class constructors first thing in start-up. + if (_libraryInitializers != null) + { + foreach (MethodDesc method in _libraryInitializers) + { + codeStream.Emit(ILOpcode.call, emitter.NewToken(method)); + } + } + + codeStream.Emit(ILOpcode.ret); + return emitter.Link(this); + } + + public override MethodSignature Signature + { + get + { + if (_signature == null) + { + _signature = new MethodSignature(MethodSignatureFlags.Static, 0, + Context.GetWellKnownType(WellKnownType.Void), + new TypeDesc[0]); + } + + return _signature; + } + } + + public override bool IsNativeCallable + { + get + { + return true; + } + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/StartupCodeMainMethod.cs b/external/corert/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/StartupCodeMainMethod.cs index 7619f9ba52..f994a1d0aa 100644 --- a/external/corert/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/StartupCodeMainMethod.cs +++ b/external/corert/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/StartupCodeMainMethod.cs @@ -59,6 +59,8 @@ namespace Internal.IL.Stubs.StartupCode ILEmitter emitter = new ILEmitter(); ILCodeStream codeStream = emitter.NewCodeStream(); + codeStream.MarkDebuggerStepThroughPoint(); + // Allow the class library to run explicitly ordered class constructors first thing in start-up. if (_libraryInitializers != null) { @@ -100,6 +102,8 @@ namespace Internal.IL.Stubs.StartupCode codeStream.Emit(ILOpcode.call, emitter.NewToken(startup.GetKnownMethod("GetMainMethodArguments", null))); } + + codeStream.MarkDebuggerStepInPoint(); codeStream.Emit(ILOpcode.call, emitter.NewToken(_mainMethod)); MethodDesc setLatchedExitCode = startup.GetMethod("SetLatchedExitCode", null); @@ -214,9 +218,13 @@ namespace Internal.IL.Stubs.StartupCode ILEmitter emit = new ILEmitter(); ILCodeStream codeStream = emit.NewCodeStream(); + codeStream.MarkDebuggerStepThroughPoint(); + for (int i = 0; i < Signature.Length; i++) codeStream.EmitLdArg(i); + codeStream.MarkDebuggerStepInPoint(); + codeStream.Emit(ILOpcode.call, emit.NewToken(WrappedMethod)); codeStream.Emit(ILOpcode.ret); diff --git a/external/corert/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/external/corert/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj index 7803f90e24..a29a5cd10f 100644 --- a/external/corert/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj +++ b/external/corert/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj @@ -1,44 +1,22 @@ - - + - Debug - AnyCPU - {13BB3788-C3EB-4046-8105-A95F8AE49404} Library ILCompiler.Compiler ILCompiler.Compiler true - true - .NETStandard,Version=v1.3 - - - - + netstandard1.3 - - false - .NetCoreApp,Version=2.0 - + + 4.3.0 + - - {dac23e9f-f826-4577-ae7a-0849ff83280c} - ILCompiler.DependencyAnalysisFramework - - - {a965ea82-219d-48f7-ad51-bc030c16cc6f} - ILCompiler.MetadataTransform - - - {d66338d4-f9e4-4051-b302-232c6bfb6ef6} - ILCompiler.MetadataWriter - - - {1a9df196-43a9-44bb-b2c6-d62aa56b0e49} - ILCompiler.TypeSystem - + + + + @@ -65,6 +43,9 @@ Common\Utf8StringBuilder.cs + + Ecma\EcmaSignatureEncoder.cs + IL\ILImporter.cs @@ -74,9 +55,15 @@ IL\Stubs\AssemblyGetExecutingAssemblyMethodThunk.Sorting.cs + + IL\Stubs\ComparerIntrinsics.cs + IL\Stubs\DynamicInvokeMethodThunk.Sorting.cs + + IL\Stubs\GetCanonTypeIntrinsic.cs + IL\Stubs\MethodBaseGetCurrentMethodThunk.cs @@ -107,15 +94,52 @@ JitInterface\JitConfigProvider.cs + + JitInterface\TypeString.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -133,9 +157,9 @@ - + @@ -148,6 +172,7 @@ + @@ -189,8 +214,8 @@ - - + + @@ -207,9 +232,11 @@ + + @@ -231,9 +258,12 @@ + + + @@ -253,8 +283,11 @@ + + + @@ -262,29 +295,44 @@ + + + + + + + + + + + + + - + + + @@ -292,18 +340,29 @@ + + + + + + + + + + + @@ -313,6 +372,9 @@ Common\ArrayBuilder.cs + + Common\CanonTypeKind.cs + Common\EEType.Constants.cs @@ -360,8 +422,8 @@ TypeSystem\TypesDebugInfoWriter\TypesDebugInfoWriter.cs - - TypeSystem\TypesDebugInfoWriter\UserDefinedTypeDescriptor.cs + + TypeSystem\TypesDebugInfoWriter\DebugInfoWriter.cs @@ -381,8 +443,5 @@ JitInterface\CorInfoTypes.cs - - - diff --git a/external/corert/src/ILCompiler.Compiler/src/project.json b/external/corert/src/ILCompiler.Compiler/src/project.json deleted file mode 100644 index 43580f1ced..0000000000 --- a/external/corert/src/ILCompiler.Compiler/src/project.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "dependencies": { - "NETStandard.Library": "1.6.1", - "System.IO.MemoryMappedFiles": "4.3.0", - "System.Reflection.Metadata": "1.4.2", - "System.Collections.Immutable": "1.3.1", - "System.Runtime.CompilerServices.Unsafe": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0" - }, - "frameworks": { - "netstandard1.3": {} - } -} diff --git a/external/corert/src/ILCompiler.Compiler/tests/DependencyGraphTests.cs b/external/corert/src/ILCompiler.Compiler/tests/DependencyGraphTests.cs new file mode 100644 index 0000000000..c4ace726b8 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/tests/DependencyGraphTests.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. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using Xunit; + +using CustomAttributeValue = System.Reflection.Metadata.CustomAttributeValue; + +namespace ILCompiler.Compiler.Tests +{ + // + // This test uses IL scanner to scan a dependency graph, starting with a + // single method from the test assembly. + // It then checks various invariants about the resulting dependency graph. + // The test method declares these invariants using custom attributes. + // + // The invariants to check for are: + // * Whether an EEType was/was not generated + // * Whether a method body was/was not generated + // * Etc. + // + // The most valuable tests are the ones that check that something was not + // generated. These let us create unit tests for size on disk regressions. + // + + public class DependencyGraphTests + { + public static IEnumerable GetTestMethods() + { + var target = new TargetDetails(TargetArchitecture.X64, TargetOS.Windows, TargetAbi.CoreRT); + var context = new CompilerTypeSystemContext(target, SharedGenericsMode.CanonicalReferenceTypes); + + context.InputFilePaths = new Dictionary { + { "Test.CoreLib", @"Test.CoreLib.dll" }, + { "ILCompiler.Compiler.Tests.Assets", @"ILCompiler.Compiler.Tests.Assets.dll" }, + }; + context.ReferenceFilePaths = new Dictionary(); + + context.SetSystemModule(context.GetModuleForSimpleName("Test.CoreLib")); + var testModule = context.GetModuleForSimpleName("ILCompiler.Compiler.Tests.Assets"); + + bool foundSomethingToCheck = false; + foreach (var type in testModule.GetType("ILCompiler.Compiler.Tests.Assets", "DependencyGraph").GetNestedTypes()) + { + foundSomethingToCheck = true; + yield return new object[] { type.GetMethod("Entrypoint", null) }; + } + + Assert.True(foundSomethingToCheck, "No methods to check?"); + } + + [Theory] + [MemberData(nameof(GetTestMethods))] + public void TestDependencyGraphInvariants(EcmaMethod method) + { + // + // Scan the input method + // + + var context = (CompilerTypeSystemContext)method.Context; + CompilationModuleGroup compilationGroup = new SingleFileCompilationModuleGroup(context); + + CompilationBuilder builder = new RyuJitCompilationBuilder(context, compilationGroup); + IILScanner scanner = builder.GetILScannerBuilder() + .UseCompilationRoots(new ICompilationRootProvider[] { new SingleMethodRootProvider(method) }) + .ToILScanner(); + + ILScanResults results = scanner.Scan(); + + // + // Check invariants + // + + const string assetsNamespace = "ILCompiler.Compiler.Tests.Assets"; + bool foundSomethingToCheck = false; + + foreach (var attr in method.GetDecodedCustomAttributes(assetsNamespace, "GeneratesConstructedEETypeAttribute")) + { + foundSomethingToCheck = true; + Assert.Contains((TypeDesc)attr.FixedArguments[0].Value, results.ConstructedEETypes); + } + + foreach (var attr in method.GetDecodedCustomAttributes(assetsNamespace, "NoConstructedEETypeAttribute")) + { + foundSomethingToCheck = true; + Assert.DoesNotContain((TypeDesc)attr.FixedArguments[0].Value, results.ConstructedEETypes); + } + + foreach (var attr in method.GetDecodedCustomAttributes(assetsNamespace, "GeneratesMethodBodyAttribute")) + { + foundSomethingToCheck = true; + MethodDesc methodToCheck = GetMethodFromAttribute(attr); + Assert.Contains(methodToCheck.GetCanonMethodTarget(CanonicalFormKind.Specific), results.CompiledMethodBodies); + } + + foreach (var attr in method.GetDecodedCustomAttributes(assetsNamespace, "NoMethodBodyAttribute")) + { + foundSomethingToCheck = true; + MethodDesc methodToCheck = GetMethodFromAttribute(attr); + Assert.DoesNotContain(methodToCheck.GetCanonMethodTarget(CanonicalFormKind.Specific), results.CompiledMethodBodies); + } + + // + // Make sure we checked something + // + + Assert.True(foundSomethingToCheck, "No invariants to check?"); + } + + private static MethodDesc GetMethodFromAttribute(CustomAttributeValue attr) + { + if (attr.NamedArguments.Length > 0) + throw new NotImplementedException(); // TODO: parse sig and instantiation + + return ((TypeDesc)attr.FixedArguments[0].Value).GetMethod((string)attr.FixedArguments[1].Value, null); + } + } +} \ No newline at end of file diff --git a/external/corert/src/ILCompiler.Compiler/tests/DevirtualizationTests.cs b/external/corert/src/ILCompiler.Compiler/tests/DevirtualizationTests.cs new file mode 100644 index 0000000000..6b320fb171 --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/tests/DevirtualizationTests.cs @@ -0,0 +1,103 @@ +// 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.Collections.Generic; + +using Internal.TypeSystem; + +using Xunit; + +namespace ILCompiler.Compiler.Tests +{ + public class DevirtualizationTests + { + private readonly CompilerTypeSystemContext _context; + private readonly ModuleDesc _testModule; + + public DevirtualizationTests() + { + var target = new TargetDetails(TargetArchitecture.X64, TargetOS.Windows, TargetAbi.CoreRT); + _context = new CompilerTypeSystemContext(target, SharedGenericsMode.CanonicalReferenceTypes); + + _context.InputFilePaths = new Dictionary { + { "Test.CoreLib", @"Test.CoreLib.dll" }, + { "ILCompiler.Compiler.Tests.Assets", @"ILCompiler.Compiler.Tests.Assets.dll" }, + }; + _context.ReferenceFilePaths = new Dictionary(); + + _context.SetSystemModule(_context.GetModuleForSimpleName("Test.CoreLib")); + _testModule = _context.GetModuleForSimpleName("ILCompiler.Compiler.Tests.Assets"); + } + + private DevirtualizationManager GetDevirtualizationManagerFromScan(MethodDesc method) + { + CompilationModuleGroup compilationGroup = new SingleFileCompilationModuleGroup(_context); + + CompilationBuilder builder = new RyuJitCompilationBuilder(_context, compilationGroup); + IILScanner scanner = builder.GetILScannerBuilder() + .UseCompilationRoots(new ICompilationRootProvider[] { new SingleMethodRootProvider(method) }) + .ToILScanner(); + + return scanner.Scan().GetDevirtualizationManager(); + } + + [Fact] + public void TestDevirtualizeWithUnallocatedType() + { + MetadataType testType = _testModule.GetType("Devirtualization", "DevirtualizeWithUnallocatedType"); + DevirtualizationManager scanDevirt = GetDevirtualizationManagerFromScan(testType.GetMethod("Run", null)); + + MethodDesc decl = testType.GetNestedType("Base").GetMethod("Unreachable", null); + MetadataType impl = testType.GetNestedType("Derived"); + + // Base::Unreachable should resolve into Derived::Unreachable on Derived. + MethodDesc resolvedMethod = scanDevirt.ResolveVirtualMethod(decl, impl); + Assert.Same(impl.GetMethod("Unreachable", null), resolvedMethod); + + // The resolved method should not be treated as sealed + Assert.False(scanDevirt.IsEffectivelySealed(resolvedMethod)); + + // Even though the metadata based algorithm would say it's sealed + var devirt = new DevirtualizationManager(); + Assert.True(devirt.IsEffectivelySealed(resolvedMethod)); + } + + [Fact] + public void TestDevirtualizeWithOtherUnallocatedType() + { + MetadataType testType = _testModule.GetType("Devirtualization", "DevirtualizeWithOtherUnallocatedType"); + DevirtualizationManager scanDevirt = GetDevirtualizationManagerFromScan(testType.GetMethod("Run", null)); + + MetadataType impl = testType.GetNestedType("Derived"); + + // The resolved method should not be treated as sealed + Assert.False(scanDevirt.IsEffectivelySealed(impl.GetMethod("Unreachable", null))); + } + + [Fact] + public void TestDevirtualizeSimple() + { + MetadataType testType = _testModule.GetType("Devirtualization", "DevirtualizeSimple"); + DevirtualizationManager scanDevirt = GetDevirtualizationManagerFromScan(testType.GetMethod("Run", null)); + + MethodDesc implMethod = testType.GetNestedType("Derived").GetMethod("Virtual", null); + + // The impl method should be treated as sealed + Assert.True(scanDevirt.IsEffectivelySealed(implMethod)); + + // Even though the metadata based algorithm would say it isn't + var devirt = new DevirtualizationManager(); + Assert.False(devirt.IsEffectivelySealed(implMethod)); + } + + [Fact] + public void TestDevirtualizeAbstract() + { + MetadataType testType = _testModule.GetType("Devirtualization", "DevirtualizeAbstract"); + DevirtualizationManager scanDevirt = GetDevirtualizationManagerFromScan(testType.GetMethod("Run", null)); + + Assert.False(scanDevirt.IsEffectivelySealed(testType.GetNestedType("Abstract"))); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/tests/ILCompiler.Compiler.Tests.Assets/DependencyGraph.cs b/external/corert/src/ILCompiler.Compiler/tests/ILCompiler.Compiler.Tests.Assets/DependencyGraph.cs new file mode 100644 index 0000000000..e5b551a29a --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/tests/ILCompiler.Compiler.Tests.Assets/DependencyGraph.cs @@ -0,0 +1,63 @@ +// 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.Runtime.InteropServices; + +namespace ILCompiler.Compiler.Tests.Assets +{ + // + // Classes nested under this class gets automatically discovered by the unit test. + // The unit test will locate the Entrypoint method, run the IL scanner on it, + // and validate the invariants declared by the Entrypoint method with custom attributes. + // + + class DependencyGraph + { + /// + /// Validates a cast doesn't force a constructed EEType. + /// + class PInvokeCctorDependencyTest + { + class TypeThatWasNeverAllocated + { + public static object O = null; + } + + [NoConstructedEEType(typeof(TypeThatWasNeverAllocated))] + public static void Entrypoint() + { + ((TypeThatWasNeverAllocated)TypeThatWasNeverAllocated.O).GetHashCode(); + } + } + } + + #region Custom attributes that define invariants to check + public class GeneratesConstructedEETypeAttribute : Attribute + { + public GeneratesConstructedEETypeAttribute(Type type) { } + } + + public class NoConstructedEETypeAttribute : Attribute + { + public NoConstructedEETypeAttribute(Type type) { } + } + + public class GeneratesMethodBodyAttribute : Attribute + { + public GeneratesMethodBodyAttribute(Type owningType, string methodName) { } + + public Type[] GenericArguments; + public Type[] Signature; + } + + public class NoMethodBodyAttribute : Attribute + { + public NoMethodBodyAttribute(Type owningType, string methodName) { } + + public Type[] GenericArguments; + public Type[] Signature; + } + #endregion +} diff --git a/external/corert/src/ILCompiler.Compiler/tests/ILCompiler.Compiler.Tests.Assets/Devirtualization.cs b/external/corert/src/ILCompiler.Compiler/tests/ILCompiler.Compiler.Tests.Assets/Devirtualization.cs new file mode 100644 index 0000000000..c0f18c0abd --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/tests/ILCompiler.Compiler.Tests.Assets/Devirtualization.cs @@ -0,0 +1,84 @@ +// 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. + +namespace Devirtualization +{ + class DevirtualizeWithUnallocatedType + { + abstract class Base + { + public abstract void Unreachable(); + } + + sealed class Derived : Base + { + public override void Unreachable() + { + new Derived(); + } + } + + static void Run() + { + Derived p = null; + if (new object() == null) + p.Unreachable(); + } + } + + class DevirtualizeWithOtherUnallocatedType + { + abstract class Base + { + public abstract void Unreachable(); + } + + class Derived : Base + { + public sealed override void Unreachable() + { + new Derived(); + } + } + + static void Run() + { + Derived p = null; + if (new object() == null) + p.Unreachable(); + } + } + + class DevirtualizeSimple + { + abstract class Base + { + public abstract void Virtual(); + } + + class Derived : Base + { + public override void Virtual() + { + new Derived(); + } + } + + static void Run() + { + Base p = new Derived(); + p.Virtual(); + } + } + + class DevirtualizeAbstract + { + abstract class Abstract { } + + static void Run() + { + typeof(Abstract).GetHashCode(); + } + } +} diff --git a/external/corert/src/ILCompiler.Compiler/tests/ILCompiler.Compiler.Tests.Assets/ILCompiler.Compiler.Tests.Assets.csproj b/external/corert/src/ILCompiler.Compiler/tests/ILCompiler.Compiler.Tests.Assets/ILCompiler.Compiler.Tests.Assets.csproj new file mode 100644 index 0000000000..44d852d96c --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/tests/ILCompiler.Compiler.Tests.Assets/ILCompiler.Compiler.Tests.Assets.csproj @@ -0,0 +1,19 @@ + + + + Library + ILCompiler.Compiler.Tests.Assets + false + false + + + + + + + + + + + + \ No newline at end of file diff --git a/external/corert/src/ILCompiler.Compiler/tests/ILCompiler.Compiler.Tests.csproj b/external/corert/src/ILCompiler.Compiler/tests/ILCompiler.Compiler.Tests.csproj new file mode 100644 index 0000000000..6e2a7f7f4e --- /dev/null +++ b/external/corert/src/ILCompiler.Compiler/tests/ILCompiler.Compiler.Tests.csproj @@ -0,0 +1,47 @@ + + + + Library + ILCompiler.Compiler.Tests + ILCompiler.Compiler.Tests + netstandard1.3 + + + + $(XunitNetcoreExtensionsVersion) + + + + + + + + + + + false + Content + PreserveNewest + Build;DebugSymbolsProjectOutputGroup + + + false + Content + PreserveNewest + Build;DebugSymbolsProjectOutputGroup + + + + + 1.0.8 + + + + + + + + + + + diff --git a/external/corert/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilation.cs b/external/corert/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilation.cs index 6abae23926..92a26eb425 100644 --- a/external/corert/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilation.cs +++ b/external/corert/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilation.cs @@ -22,22 +22,14 @@ namespace ILCompiler DependencyAnalyzerBase dependencyGraph, NodeFactory nodeFactory, IEnumerable roots, + DebugInformationProvider debugInformationProvider, Logger logger, CppCodegenConfigProvider options) - : base(dependencyGraph, nodeFactory, GetCompilationRoots(roots, nodeFactory), logger) + : base(dependencyGraph, nodeFactory, GetCompilationRoots(roots, nodeFactory), debugInformationProvider, null, logger) { Options = options; } - protected override bool GenerateDebugInfo - { - get - { - /// Some degree of control exposed by . - return true; - } - } - private static IEnumerable GetCompilationRoots(IEnumerable existingRoots, NodeFactory factory) { yield return new CppCodegenCompilationRootProvider(factory.TypeSystemContext); @@ -50,6 +42,8 @@ namespace ILCompiler { _cppWriter = new CppWriter(this, outputFile); + _dependencyGraph.ComputeMarkedNodes(); + var nodes = _dependencyGraph.MarkedNodeList; _cppWriter.OutputCode(nodes, NodeFactory); diff --git a/external/corert/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilationBuilder.cs b/external/corert/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilationBuilder.cs index 7ea68e2120..8b4fe63601 100644 --- a/external/corert/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilationBuilder.cs +++ b/external/corert/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilationBuilder.cs @@ -8,6 +8,8 @@ using System.Collections.Generic; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; + namespace ILCompiler { public sealed class CppCodegenCompilationBuilder : CompilationBuilder @@ -29,10 +31,11 @@ namespace ILCompiler public override ICompilation ToCompilation() { - CppCodegenNodeFactory factory = new CppCodegenNodeFactory(_context, _compilationGroup, _metadataManager, _nameMangler); + var interopStubManager = new CompilerGeneratedInteropStubManager(_compilationGroup, _context, new InteropStateManager(_compilationGroup.GeneratedAssembly)); + CppCodegenNodeFactory factory = new CppCodegenNodeFactory(_context, _compilationGroup, _metadataManager, interopStubManager, _nameMangler, _vtableSliceProvider, _dictionaryLayoutProvider); DependencyAnalyzerBase graph = CreateDependencyGraph(factory); - return new CppCodegenCompilation(graph, factory, _compilationRoots, _logger, _config); + return new CppCodegenCompilation(graph, factory, _compilationRoots, _debugInformationProvider, _logger, _config); } } diff --git a/external/corert/src/ILCompiler.CppCodeGen/src/Compiler/DependencyAnalysis/CppCodegenNodeFactory.cs b/external/corert/src/ILCompiler.CppCodeGen/src/Compiler/DependencyAnalysis/CppCodegenNodeFactory.cs index 3eaf2ae2ae..baf0339aa5 100644 --- a/external/corert/src/ILCompiler.CppCodeGen/src/Compiler/DependencyAnalysis/CppCodegenNodeFactory.cs +++ b/external/corert/src/ILCompiler.CppCodeGen/src/Compiler/DependencyAnalysis/CppCodegenNodeFactory.cs @@ -10,8 +10,17 @@ namespace ILCompiler.DependencyAnalysis { public sealed class CppCodegenNodeFactory : NodeFactory { - public CppCodegenNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager, NameMangler nameMangler) - : base(context, compilationModuleGroup, metadataManager, nameMangler, new LazyGenericsDisabledPolicy()) + public CppCodegenNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager, + InteropStubManager interopStubManager, NameMangler nameMangler, VTableSliceProvider vtableSliceProvider, DictionaryLayoutProvider dictionaryLayoutProvider) + : base(context, + compilationModuleGroup, + metadataManager, + interopStubManager, + nameMangler, + new LazyGenericsDisabledPolicy(), + vtableSliceProvider, + dictionaryLayoutProvider, + new ImportedNodeProviderThrowing()) { } @@ -19,7 +28,7 @@ namespace ILCompiler.DependencyAnalysis protected override IMethodNode CreateMethodEntrypointNode(MethodDesc method) { - if (CompilationModuleGroup.ContainsMethodBody(method)) + if (CompilationModuleGroup.ContainsMethodBody(method, false)) { return new CppMethodCodeNode(method); } @@ -32,7 +41,7 @@ namespace ILCompiler.DependencyAnalysis protected override IMethodNode CreateUnboxingStubNode(MethodDesc method) { // TODO: this is wrong: this returns an assembly stub node - return new UnboxingStubNode(method); + return new UnboxingStubNode(method, Target); } protected override ISymbolNode CreateReadyToRunHelperNode(ReadyToRunHelperKey helperCall) diff --git a/external/corert/src/ILCompiler.CppCodeGen/src/Compiler/DependencyAnalysis/CppMethodCodeNode.cs b/external/corert/src/ILCompiler.CppCodeGen/src/Compiler/DependencyAnalysis/CppMethodCodeNode.cs index aa29a89b92..70a138649e 100644 --- a/external/corert/src/ILCompiler.CppCodeGen/src/Compiler/DependencyAnalysis/CppMethodCodeNode.cs +++ b/external/corert/src/ILCompiler.CppCodeGen/src/Compiler/DependencyAnalysis/CppMethodCodeNode.cs @@ -13,7 +13,7 @@ using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis { - internal class CppMethodCodeNode : DependencyNodeCore, IMethodNode + internal class CppMethodCodeNode : DependencyNodeCore, IMethodBodyNode { private MethodDesc _method; private string _methodCode; @@ -75,5 +75,12 @@ namespace ILCompiler.DependencyAnalysis public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + + int ISortableSymbolNode.ClassCode => 1643555522; + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + return comparer.Compare(_method, ((CppMethodCodeNode)other)._method); + } } } diff --git a/external/corert/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs b/external/corert/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs index 53e9e892df..be43013037 100644 --- a/external/corert/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs +++ b/external/corert/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs @@ -56,6 +56,22 @@ namespace ILCompiler.CppCodeGen BuildExternCSignatureMap(); } + // Mangled type names referenced by the generated code + private Dictionary _mangledNames = new Dictionary(); + + private string GetMangledTypeName(TypeDesc type) + { + string mangledName; + if (_mangledNames.TryGetValue(type, out mangledName)) + return mangledName; + + mangledName = _compilation.NameMangler.GetMangledTypeName(type); + + _mangledNames.Add(type, mangledName); + + return mangledName; + } + private Dictionary _cppSignatureNames = new Dictionary(); public string GetCppSignatureTypeName(TypeDesc type) @@ -270,7 +286,7 @@ namespace ILCompiler.CppCodeGen case TypeFlags.Pointer: return GetCppSignatureTypeName(((ParameterizedType)type).ParameterType) + "*"; default: - return _compilation.NameMangler.GetMangledTypeName(type).ToString(); + return GetMangledTypeName(type); } } @@ -284,7 +300,7 @@ namespace ILCompiler.CppCodeGen /// C++ declaration name for . public string GetCppMethodDeclarationName(TypeDesc owningType, string methodName, bool isDeclaration = true) { - var s = _compilation.NameMangler.GetMangledTypeName(owningType); + var s = GetMangledTypeName(owningType); if (isDeclaration && s.StartsWith("::")) { // For a Method declaration we do not need the starting :: @@ -377,7 +393,12 @@ namespace ILCompiler.CppCodeGen { MethodDesc method = methodCodeNodeNeedingCode.Method; - _compilation.Logger.Writer.WriteLine("Compiling " + method.ToString()); + if (_compilation.Logger.IsVerbose) + { + string methodName = method.ToString(); + _compilation.Logger.Writer.WriteLine("Compiling " + methodName); + } + if (method.HasCustomAttribute("System.Runtime", "RuntimeImportAttribute")) { CompileExternMethod(methodCodeNodeNeedingCode, ((EcmaMethod)method).GetRuntimeImportName()); @@ -479,25 +500,33 @@ namespace ILCompiler.CppCodeGen private void OutputTypeFields(CppGenerationBuffer sb, TypeDesc t) { bool explicitLayout = false; + bool hasSize = false; ClassLayoutMetadata classLayoutMetadata = default(ClassLayoutMetadata); if (t.IsValueType) { MetadataType metadataType = (MetadataType)t; + classLayoutMetadata = metadataType.GetClassLayout(); + hasSize = classLayoutMetadata.Size > 0; if (metadataType.IsExplicitLayout) { explicitLayout = true; - classLayoutMetadata = metadataType.GetClassLayout(); } } int instanceFieldIndex = 0; - if (explicitLayout) + if (explicitLayout || hasSize) { sb.AppendLine(); sb.Append("union {"); sb.Indent(); + if (!explicitLayout) + { + sb.Append("struct {"); + sb.Indent(); + } + } foreach (var field in t.GetFields()) @@ -549,13 +578,21 @@ namespace ILCompiler.CppCodeGen } } - if (explicitLayout) + if (explicitLayout || hasSize) { if (classLayoutMetadata.Size > 0) { sb.AppendLine(); sb.Append("struct { char __sizePadding[" + classLayoutMetadata.Size + "]; };"); } + + if (!explicitLayout) + { + sb.Exdent(); + sb.AppendLine(); + sb.Append("};"); + } + sb.Exdent(); sb.AppendLine(); sb.Append("};"); @@ -958,7 +995,7 @@ namespace ILCompiler.CppCodeGen CppGenerationBuffer typeDefinitions = new CppGenerationBuffer(); CppGenerationBuffer methodTables = new CppGenerationBuffer(); CppGenerationBuffer additionalNodes = new CppGenerationBuffer(); - DependencyNodeIterator nodeIterator = new DependencyNodeIterator(nodes); + DependencyNodeIterator nodeIterator = new DependencyNodeIterator(nodes, factory); // Number of InterfaceDispatchMapNodes needs to be declared explicitly for Ubuntu and OSX int dispatchMapCount = 0; @@ -968,11 +1005,15 @@ namespace ILCompiler.CppCodeGen //RTR header needs to be declared after all modules have already been output string rtrHeader = string.Empty; + // GetData stabilizes the indices of the embedded objects. This must be done manually + // for C++ codegen since we don't currently emit the DispatchMapTable node directly. + factory.DispatchMapTable.GetData(factory, false); + // Iterate through nodes foreach (var node in nodeIterator.GetNodes()) { if (node is EETypeNode) - OutputTypeNode(node as EETypeNode, factory, forwardDefinitions, typeDefinitions, methodTables); + OutputTypeNode(node as EETypeNode, factory, typeDefinitions, methodTables); else if ((node is EETypeOptionalFieldsNode || node is TypeManagerIndirectionNode || node is GenericCompositionNode) && !(node as ObjectNode).ShouldSkipEmittingObjectNode(factory)) additionalNodes.Append(GetCodeForObjectNode(node as ObjectNode, factory)); else if (node is InterfaceDispatchMapNode) @@ -992,7 +1033,7 @@ namespace ILCompiler.CppCodeGen dispatchPointers.AppendLine(); dispatchPointers.Exdent(); - Out.Write(forwardDefinitions.ToString()); + WriteForwardDefinitions(); Out.Write(typeDefinitions.ToString()); @@ -1045,7 +1086,54 @@ namespace ILCompiler.CppCodeGen Out.Write(sb.ToString()); } } - private void OutputTypeNode(IEETypeNode typeNode, NodeFactory factory, CppGenerationBuffer forwardDefinitions, CppGenerationBuffer typeDefinitions, CppGenerationBuffer methodTable) + + /// + /// Write forward definitions for all mangled type names referenced by the generated C++ code. This set is tracked separately from + /// the types that need EEType because of the type mangled names are often needed to just get the code to compile but type node is not + /// actually required for it. + /// + private void WriteForwardDefinitions() + { + CppGenerationBuffer forwardDefinitions = new CppGenerationBuffer(); + + string[] mangledNames = _mangledNames.Values.ToArray(); + Array.Sort(mangledNames); + foreach (string mangledName in mangledNames) + { + int nesting = 0; + int current = 0; + + for (; ; ) + { + int sep = mangledName.IndexOf("::", current); + + if (sep < 0) + break; + + if (sep != 0) + { + // Case of a name not starting with :: + forwardDefinitions.Append("namespace " + mangledName.Substring(current, sep - current) + " { "); + nesting++; + } + current = sep + 2; + } + + forwardDefinitions.Append("class " + mangledName.Substring(current) + ";"); + + while (nesting > 0) + { + forwardDefinitions.Append(" }"); + nesting--; + } + + forwardDefinitions.AppendLine(); + } + + Out.Write(forwardDefinitions.ToString()); + } + + private void OutputTypeNode(IEETypeNode typeNode, NodeFactory factory, CppGenerationBuffer typeDefinitions, CppGenerationBuffer methodTable) { if (_emittedTypes == null) { @@ -1058,12 +1146,11 @@ namespace ILCompiler.CppCodeGen _emittedTypes.Add(nodeType); // Create Namespaces - string mangledName = _compilation.NameMangler.GetMangledTypeName(nodeType); + string mangledName = GetMangledTypeName(nodeType); int nesting = 0; int current = 0; - forwardDefinitions.AppendLine(); for (;;) { int sep = mangledName.IndexOf("::", current); @@ -1074,7 +1161,6 @@ namespace ILCompiler.CppCodeGen if (sep != 0) { // Case of a name not starting with :: - forwardDefinitions.Append("namespace " + mangledName.Substring(current, sep - current) + " { "); typeDefinitions.Append("namespace " + mangledName.Substring(current, sep - current) + " { "); typeDefinitions.Indent(); nesting++; @@ -1082,8 +1168,6 @@ namespace ILCompiler.CppCodeGen current = sep + 2; } - forwardDefinitions.Append("class " + mangledName.Substring(current) + ";"); - // type definition typeDefinitions.Append("class " + mangledName.Substring(current)); if (!nodeType.IsValueType) @@ -1099,17 +1183,20 @@ namespace ILCompiler.CppCodeGen typeDefinitions.Append("public:"); typeDefinitions.Indent(); - // TODO: Enable once the dependencies are tracked for arrays - // if (((DependencyNode)_compilation.NodeFactory.ConstructedTypeSymbol(t)).Marked) + if (typeNode.Marked) { typeDefinitions.AppendLine(); typeDefinitions.Append("static MethodTable * __getMethodTable();"); } - if (typeNode is ConstructedEETypeNode) + + if (nodeType.IsDefType && !nodeType.IsGenericDefinition) { OutputTypeFields(typeDefinitions, nodeType); + } - IReadOnlyList virtualSlots = _compilation.NodeFactory.VTable(nodeType).Slots; + if (typeNode is ConstructedEETypeNode) + { + IReadOnlyList virtualSlots = _compilation.NodeFactory.VTable(nodeType.GetClosestDefType()).Slots; int baseSlots = 0; var baseType = nodeType.BaseType; @@ -1160,7 +1247,6 @@ namespace ILCompiler.CppCodeGen while (nesting > 0) { - forwardDefinitions.Append("};"); typeDefinitions.Append("};"); typeDefinitions.Exdent(); nesting--; @@ -1168,7 +1254,10 @@ namespace ILCompiler.CppCodeGen typeDefinitions.AppendEmptyLine(); // declare method table - methodTable.Append(GetCodeForObjectNode(typeNode as ObjectNode, factory)); + if (typeNode.Marked) + { + methodTable.Append(GetCodeForObjectNode(typeNode as ObjectNode, factory)); + } methodTable.AppendEmptyLine(); } diff --git a/external/corert/src/ILCompiler.CppCodeGen/src/CppCodeGen/DependencyNodeIterator.cs b/external/corert/src/ILCompiler.CppCodeGen/src/CppCodeGen/DependencyNodeIterator.cs index 9503982958..7b46483e21 100644 --- a/external/corert/src/ILCompiler.CppCodeGen/src/CppCodeGen/DependencyNodeIterator.cs +++ b/external/corert/src/ILCompiler.CppCodeGen/src/CppCodeGen/DependencyNodeIterator.cs @@ -17,12 +17,14 @@ namespace ILCompiler.Compiler.CppCodeGen private List _nodes; private HashSet _visited; private Dictionary _typeToNodeMap; + private NodeFactory _factory; - public DependencyNodeIterator(IEnumerable nodes) + public DependencyNodeIterator(IEnumerable nodes, NodeFactory factory) { _nodes = new List(); _typeToNodeMap = new Dictionary(); _visited = new HashSet(); + _factory = factory; foreach (var node in nodes) { if (node is EETypeNode) @@ -56,17 +58,26 @@ namespace ILCompiler.Compiler.CppCodeGen AddTypeNode(baseTypeNode); else if (!_nodes.Contains(baseTypeNode)) _nodes.Add(baseTypeNode); } - foreach (var field in node.Type.GetFields()) + if (!node.Type.IsGenericDefinition) { - EETypeNode fieldNode; - _typeToNodeMap.TryGetValue(field.FieldType, out fieldNode); - if (fieldNode != null) + foreach (var field in node.Type.GetFields()) { - if (fieldNode.Type.IsValueType) + EETypeNode fieldNode; + _typeToNodeMap.TryGetValue(field.FieldType, out fieldNode); + if (fieldNode == null) { - if (!fieldNode.Type.IsPrimitive) - AddTypeNode(fieldNode); - else if (!_nodes.Contains(fieldNode)) _nodes.Add(fieldNode); + if (field.FieldType.IsValueType) + AddTypeNode((EETypeNode)_factory.NecessaryTypeSymbol(field.FieldType)); + } + else + { + if (fieldNode.Type.IsValueType) + { + if (!fieldNode.Type.IsPrimitive) + AddTypeNode(fieldNode); + else if (!_nodes.Contains(fieldNode)) + _nodes.Add(fieldNode); + } } } } @@ -78,4 +89,4 @@ namespace ILCompiler.Compiler.CppCodeGen return _nodes; } } -} \ No newline at end of file +} diff --git a/external/corert/src/ILCompiler.CppCodeGen/src/CppCodeGen/EvaluationStack.cs b/external/corert/src/ILCompiler.CppCodeGen/src/CppCodeGen/EvaluationStack.cs index 20ed6831a5..feaf2ed2dc 100644 --- a/external/corert/src/ILCompiler.CppCodeGen/src/CppCodeGen/EvaluationStack.cs +++ b/external/corert/src/ILCompiler.CppCodeGen/src/CppCodeGen/EvaluationStack.cs @@ -503,7 +503,7 @@ namespace Internal.IL public override void Append(CppGenerationBuffer _builder) { _builder.Append("// FIXME: An invalid value was pushed onto the evaluation stack."); - Debug.Assert(false, "Invalid stack values shouldn't be appended."); + Debug.Fail("Invalid stack values shouldn't be appended."); } public override StackEntry Duplicate() diff --git a/external/corert/src/ILCompiler.CppCodeGen/src/CppCodeGen/ILToCppImporter.cs b/external/corert/src/ILCompiler.CppCodeGen/src/CppCodeGen/ILToCppImporter.cs index ac3016c38d..1b88ec3eab 100644 --- a/external/corert/src/ILCompiler.CppCodeGen/src/CppCodeGen/ILToCppImporter.cs +++ b/external/corert/src/ILCompiler.CppCodeGen/src/CppCodeGen/ILToCppImporter.cs @@ -82,10 +82,16 @@ namespace Internal.IL private class BasicBlock { // Common fields + public enum ImportState : byte + { + Unmarked, + IsPending + } + public BasicBlock Next; public int StartOffset; - public int EndOffset; + public ImportState State = ImportState.Unmarked; public EvaluationStack EntryStack; @@ -315,6 +321,145 @@ namespace Internal.IL } } + private void AppendComparison(ILOpcode opcode, StackEntry op1, StackEntry op2) + { + // StackValueKind is carefully ordered to make this work (assuming the IL is valid) + StackValueKind kind = (op1.Kind > op2.Kind) ? op1.Kind : op2.Kind; + + string op = null; + bool unsigned = false; + bool inverted = false; + switch (opcode) + { + case ILOpcode.beq: + case ILOpcode.ceq: + op = "=="; + break; + case ILOpcode.bge: + op = ">="; + break; + case ILOpcode.bgt: + case ILOpcode.cgt: + op = ">"; + break; + case ILOpcode.ble: + op = "<="; + break; + case ILOpcode.blt: + case ILOpcode.clt: + op = "<"; + break; + case ILOpcode.bne_un: + op = "!="; + break; + case ILOpcode.bge_un: + if (kind == StackValueKind.Float) + { + op = "<"; + inverted = true; + } + else + { + op = ">="; + unsigned = true; + } + break; + case ILOpcode.bgt_un: + case ILOpcode.cgt_un: + if (kind == StackValueKind.Float) + { + op = "<="; + inverted = true; + } + else + if (op1.Kind == StackValueKind.ObjRef && op1.Type == null) + { + // ECMA-335 III.1.5 Operand type table, P. 303: + // cgt.un is commonly used when comparing an ObjectRef with null (there is no "compare - not - equal" instruction) + // Turn into more natural compare not equal. + op = "!="; + } + else + { + op = ">"; + unsigned = true; + } + break; + case ILOpcode.ble_un: + if (kind == StackValueKind.Float) + { + op = ">"; + inverted = true; + } + else + { + op = "<="; + unsigned = true; + } + break; + case ILOpcode.blt_un: + case ILOpcode.clt_un: + if (kind == StackValueKind.Float) + { + op = ">="; + inverted = true; + } + else + { + op = "<"; + unsigned = true; + } + break; + default: + Debug.Fail("Unexpected opcode"); + break; + } + + if (inverted) + { + Append("!("); + } + if (unsigned) + { + if (kind < StackValueKind.ByRef) + { + Append("(u"); + Append(GetStackValueKindCPPTypeName(kind)); + Append(")"); + } + else + { + if (op2.Kind < StackValueKind.ByRef + || (op2.Kind == StackValueKind.ObjRef && op2.Type == null)) + Append("(void*)"); + } + } + Append(op2); + Append(" "); + Append(op); + Append(" "); + if (unsigned) + { + if (kind < StackValueKind.ByRef) + { + Append("(u"); + Append(GetStackValueKindCPPTypeName(kind)); + Append(")"); + } + else + { + if (op1.Kind < StackValueKind.ByRef + || (op1.Kind == StackValueKind.ObjRef && op1.Type == null)) + Append("(void*)"); + } + } + Append(op1); + if (inverted) + { + Append(")"); + } + } + private StackEntry NewSpillSlot(StackEntry entry) { if (_spillSlots == null) @@ -523,6 +668,17 @@ namespace Internal.IL Append("{"); Indent(); + + if (_method.IsNativeCallable) + { + AppendLine(); + Append("ReversePInvokeFrame __frame"); + AppendSemicolon(); + AppendLine(); + Append("__reverse_pinvoke(&__frame)"); + AppendSemicolon(); + } + bool initLocals = _methodIL.IsInitLocals; for (int i = 0; i < _locals.Length; i++) { @@ -819,6 +975,37 @@ namespace Internal.IL return true; } break; + case ".ctor": + if (IsTypeName(method, "System", "ByReference`1")) + { + var value = _stack.Pop(); + var byReferenceType = method.OwningType; + + string tempName = NewTempName(); + + Append(GetStackValueKindCPPTypeName(StackValueKind.ValueType, byReferenceType)); + Append(" "); + Append(tempName); + AppendSemicolon(); + + Append(tempName); + Append("._value = (intptr_t)"); + Append(value); + AppendSemicolon(); + + PushExpression(StackValueKind.ValueType, tempName, byReferenceType); + return true; + } + break; + case "get_Value": + if (IsTypeName(method, "System", "ByReference`1")) + { + var thisRef = _stack.Pop(); + + PushExpression(StackValueKind.ValueType, ((ExpressionEntry)thisRef).Name + "->_value", method.Signature.ReturnType); + return true; + } + break; default: break; } @@ -875,6 +1062,17 @@ namespace Internal.IL return; } + //this assumes that there will only ever be at most one RawPInvoke call in a given method + if (method.IsRawPInvoke()) + { + AppendLine(); + Append("PInvokeTransitionFrame __piframe"); + AppendSemicolon(); + AppendLine(); + Append("__pinvoke(&__piframe)"); + AppendSemicolon(); + } + TypeDesc constrained = null; if (opcode != ILOpcode.newobj) { @@ -882,9 +1080,9 @@ namespace Internal.IL { _pendingPrefix &= ~Prefix.Constrained; constrained = _constrained; - bool forceUseRuntimeLookup; - MethodDesc directMethod = constrained.GetClosestDefType().TryResolveConstraintMethodApprox(method.OwningType, method, out forceUseRuntimeLookup); + var constrainedType = constrained.GetClosestDefType(); + MethodDesc directMethod = constrainedType.TryResolveConstraintMethodApprox(method.OwningType, method, out forceUseRuntimeLookup); if (forceUseRuntimeLookup) throw new NotImplementedException(); @@ -894,22 +1092,16 @@ namespace Internal.IL method = directMethod; opcode = ILOpcode.call; } + //If constrainedType is a value type and constrainedType does not implement method (directMethod == null) then ptr is + //dereferenced, boxed, and passed as the 'this' pointer to the callvirt method instruction. + else if (constrainedType.IsValueType) + { + int thisPosition = _stack.Top - (method.Signature.Length + 1); + _stack[thisPosition] = BoxValue(constrainedType, DereferenceThisPtr(method, constrainedType)); + } else { - // Dereference "this" - int thisPosition = _stack.Top - (method.Signature.Length + 1); - string tempName = NewTempName(); - - Append(GetStackValueKindCPPTypeName(StackValueKind.ObjRef)); - Append(" "); - Append(tempName); - Append(" = *("); - Append(GetStackValueKindCPPTypeName(StackValueKind.ObjRef)); - Append("*)"); - Append(_stack[thisPosition]); - AppendSemicolon(); - - _stack[thisPosition] = new ExpressionEntry(StackValueKind.ObjRef, tempName); + DereferenceThisPtr(method); } } } @@ -968,7 +1160,7 @@ namespace Internal.IL else callViaSlot = true; - if (!_nodeFactory.CompilationModuleGroup.ShouldProduceFullVTable(method.OwningType)) + if (!_nodeFactory.VTable(method.OwningType).HasFixedSlots) _dependencies.Add(_nodeFactory.VirtualMethodUse(method)); } } @@ -990,48 +1182,18 @@ namespace Internal.IL if (callViaInterfaceDispatch) { - _dependencies.Add(_nodeFactory.ReadyToRunHelper(ReadyToRunHelperId.VirtualCall, method)); ExpressionEntry v = (ExpressionEntry)_stack[_stack.Top - (methodSignature.Length + 1)]; string typeDefName = _writer.GetCppMethodName(method); - _writer.AppendSignatureTypeDef(_builder, typeDefName, method.Signature, method.OwningType); + _writer.AppendSignatureTypeDef(_builder, typeDefName, method.Signature, method.OwningType); string functionPtr = NewTempName(); AppendEmptyLine(); Append("void*"); Append(functionPtr); - Append(" = (void*) (("); - Append(typeDefName); - // Call method to find implementation address - Append(") System_Private_CoreLib::System::Runtime::DispatchResolve::FindInterfaceMethodImplementationTarget("); - - // Get EEType of current object (interface implementation) - Append("::System_Private_CoreLib::System::Object::get_EEType((::System_Private_CoreLib::System::Object*)"); - Append(v.Name); - Append(")"); - - Append(", "); - - // Get EEType of interface - Append("((::System_Private_CoreLib::Internal::Runtime::EEType *)("); - Append(_writer.GetCppTypeName(method.OwningType)); - Append("::__getMethodTable()))"); - - Append(", "); - - // Get slot of implementation - Append("(uint16_t)"); - Append("("); - Append(_writer.GetCppTypeName(method.OwningType)); - Append("::"); - Append("__getslot__"); - Append(_writer.GetCppMethodName(method)); - Append("("); - Append(v.Name); - Append("))"); - - Append("));"); + Append(" = (void*) "); + GetFunctionPointerForInterfaceMethod(method, v, typeDefName); PushExpression(StackValueKind.ByRef, functionPtr); } @@ -1175,6 +1337,71 @@ namespace Internal.IL PushExpression(retKind, temp, retType); } AppendSemicolon(); + + if (method.IsRawPInvoke()) + { + AppendLine(); + Append("__pinvoke_return(&__piframe)"); + AppendSemicolon(); + } + } + + private ExpressionEntry DereferenceThisPtr(MethodDesc method, TypeDesc type = null) + { + // Dereference "this" + int thisPosition = _stack.Top - (method.Signature.Length + 1); + string tempName = NewTempName(); + + StackValueKind valueKind = StackValueKind.ObjRef; + if (type != null && type.IsValueType) + valueKind = StackValueKind.ValueType; + + Append(GetStackValueKindCPPTypeName(valueKind, type)); + Append(" "); + Append(tempName); + Append(" = *("); + Append(GetStackValueKindCPPTypeName(valueKind, type)); + Append("*)"); + Append(_stack[thisPosition]); + AppendSemicolon(); + var result = new ExpressionEntry(valueKind, tempName, type); + _stack[thisPosition] = result; + return result; + } + + private void GetFunctionPointerForInterfaceMethod(MethodDesc method, ExpressionEntry v, string typeDefName) + { + Append("(("); + Append(typeDefName); + // Call method to find implementation address + Append(") System_Private_CoreLib::System::Runtime::DispatchResolve::FindInterfaceMethodImplementationTarget("); + + // Get EEType of current object (interface implementation) + Append("::System_Private_CoreLib::System::Object::get_EEType((::System_Private_CoreLib::System::Object*)"); + Append(v.Name); + Append(")"); + + Append(", "); + + // Get EEType of interface + Append("((::System_Private_CoreLib::Internal::Runtime::EEType *)("); + Append(_writer.GetCppTypeName(method.OwningType)); + Append("::__getMethodTable()))"); + + Append(", "); + + // Get slot of implementation + Append("(uint16_t)"); + Append("("); + Append(_writer.GetCppTypeName(method.OwningType)); + Append("::"); + Append("__getslot__"); + Append(_writer.GetCppMethodName(method)); + Append("("); + Append(v.Name); + Append("))"); + + Append("));"); } private void PassCallArguments(MethodSignature methodSignature, TypeDesc thisArgument) @@ -1266,22 +1493,51 @@ namespace Internal.IL { MethodDesc method = (MethodDesc)_methodIL.GetObject(token); - if (opCode == ILOpcode.ldvirtftn) + if (opCode == ILOpcode.ldvirtftn && method.IsVirtual && method.OwningType.IsInterface) { - if (method.IsVirtual) - throw new NotImplementedException(); + AddVirtualMethodReference(method); + var entry = new LdTokenEntry(StackValueKind.NativeInt, NewTempName(), method); + ExpressionEntry v = (ExpressionEntry)_stack.Pop(); + string typeDefName = _writer.GetCppMethodName(method); + _writer.AppendSignatureTypeDef(_builder, typeDefName, method.Signature, method.OwningType); + + AppendEmptyLine(); + + PushTemp(entry); + Append("(intptr_t) "); + GetFunctionPointerForInterfaceMethod(method, v, typeDefName); } + else + { + AddMethodReference(method); + var entry = new LdTokenEntry(StackValueKind.NativeInt, NewTempName(), method); + + if (opCode == ILOpcode.ldvirtftn && method.IsVirtual) + { + //ldvirtftn requires an object instance, we have to pop one off the stack + //then call the associated getslot method passing in the object instance to get the real function pointer + ExpressionEntry v = (ExpressionEntry)_stack.Pop(); + PushTemp(entry); + Append("(intptr_t)"); + Append(_writer.GetCppTypeName(method.OwningType)); + Append("::__getslot__"); + Append(_writer.GetCppMethodName(method)); + Append("("); + Append(v.Name); + Append(")"); + AppendSemicolon(); + } + else + { + PushTemp(entry); + Append("(intptr_t)&"); + Append(_writer.GetCppTypeName(method.OwningType)); + Append("::"); + Append(_writer.GetCppMethodName(method)); - AddMethodReference(method); - - var entry = new LdTokenEntry(StackValueKind.NativeInt, NewTempName(), method); - PushTemp(entry); - Append("(intptr_t)&"); - Append(_writer.GetCppTypeName(method.OwningType)); - Append("::"); - Append(_writer.GetCppMethodName(method)); - - AppendSemicolon(); + AppendSemicolon(); + } + } } private void ImportLoadInt(long value, StackValueKind kind) @@ -1309,6 +1565,13 @@ namespace Internal.IL private void ImportReturn() { + if (_method.IsNativeCallable) + { + AppendLine(); + Append("__reverse_pinvoke_return(&__frame)"); + AppendSemicolon(); + } + var returnType = _methodSignature.ReturnType; AppendLine(); if (returnType.IsVoid) @@ -1427,107 +1690,7 @@ namespace Internal.IL var op1 = _stack.Pop(); var op2 = _stack.Pop(); - // StackValueKind is carefully ordered to make this work (assuming the IL is valid) - StackValueKind kind; - - if (op1.Kind > op2.Kind) - { - kind = op1.Kind; - } - else - { - kind = op2.Kind; - } - - string op = null; - bool unsigned = false; - bool inverted = false; - switch (opcode) - { - case ILOpcode.beq: op = "=="; break; - case ILOpcode.bge: op = ">="; break; - case ILOpcode.bgt: op = ">"; break; - case ILOpcode.ble: op = "<="; break; - case ILOpcode.blt: op = "<"; break; - case ILOpcode.bne_un: op = "!="; break; - case ILOpcode.bge_un: - if (kind == StackValueKind.Float) - { - op = "<"; inverted = true; - } - else - { - op = ">="; - } - if (kind == StackValueKind.Int32 || kind == StackValueKind.Int64) - unsigned = true; - break; - case ILOpcode.bgt_un: - if (kind == StackValueKind.Float) - { - op = "<="; inverted = true; - } - else - { - op = ">"; - } - if (kind == StackValueKind.Int32 || kind == StackValueKind.Int64) - unsigned = true; - break; - case ILOpcode.ble_un: - if (kind == StackValueKind.Float) - { - op = ">"; inverted = true; - } - else - { - op = "<="; - } - if (kind == StackValueKind.Int32 || kind == StackValueKind.Int64) - unsigned = true; - break; - case ILOpcode.blt_un: - if (kind == StackValueKind.Float) - { - op = ">="; inverted = true; - } - else - { - op = "<"; - } - if (kind == StackValueKind.Int32 || kind == StackValueKind.Int64) - unsigned = true; - break; - } - - if (kind == StackValueKind.ByRef) - unsigned = false; - - if (inverted) - { - Append("!("); - } - if (unsigned) - { - Append("(u"); - Append(GetStackValueKindCPPTypeName(kind)); - Append(")"); - } - Append(op2); - Append(" "); - Append(op); - Append(" "); - if (unsigned) - { - Append("(u"); - Append(GetStackValueKindCPPTypeName(kind)); - Append(")"); - } - Append(op1); - if (inverted) - { - Append(")"); - } + AppendComparison(opcode, op1, op2); } Append(") "); } @@ -1599,7 +1762,7 @@ namespace Internal.IL case ILOpcode.mul_ovf: op = "*"; break; case ILOpcode.mul_ovf_un: op = "*"; unsigned = true; break; - default: Debug.Assert(false, "Unexpected opcode"); break; + default: Debug.Fail("Unexpected opcode"); break; } if (kind == StackValueKind.ByRef) @@ -1675,82 +1838,9 @@ namespace Internal.IL var op1 = _stack.Pop(); var op2 = _stack.Pop(); - // StackValueKind is carefully ordered to make this work (assuming the IL is valid) - StackValueKind kind; - - if (op1.Kind > op2.Kind) - { - kind = op1.Kind; - } - else - { - kind = op2.Kind; - } - PushTemp(StackValueKind.Int32); - string op = null; - bool unsigned = false; - bool inverted = false; - switch (opcode) - { - case ILOpcode.ceq: op = "=="; break; - case ILOpcode.cgt: op = ">"; break; - case ILOpcode.clt: op = "<"; break; - case ILOpcode.cgt_un: - if (kind == StackValueKind.Float) - { - op = "<="; inverted = true; - } - else - { - op = ">"; - if (kind == StackValueKind.Int32 || kind == StackValueKind.Int64) - unsigned = true; - } - break; - case ILOpcode.clt_un: - if (kind == StackValueKind.Float) - { - op = ">="; inverted = true; - } - else - { - op = "<"; - if (kind == StackValueKind.Int32 || kind == StackValueKind.Int64) - unsigned = true; - } - break; - } - - if (kind == StackValueKind.ByRef) - unsigned = false; - - if (inverted) - { - Append("!("); - } - if (unsigned) - { - Append("(u"); - Append(GetStackValueKindCPPTypeName(kind)); - Append(")"); - } - Append(op2); - Append(" "); - Append(op); - Append(" "); - if (unsigned) - { - Append("(u"); - Append(GetStackValueKindCPPTypeName(kind)); - Append(")"); - } - Append(op1); - if (inverted) - { - Append(")"); - } + AppendComparison(opcode, op1, op2); AppendSemicolon(); } @@ -2055,28 +2145,35 @@ namespace Internal.IL throw new NotImplementedException(); var value = _stack.Pop(); - - PushTemp(StackValueKind.ObjRef, type); - - AddTypeReference(type, true); - - Append("__allocate_object("); - Append(_writer.GetCppTypeName(type)); - Append("::__getMethodTable())"); - AppendSemicolon(); - - string typeName = GetStackValueKindCPPTypeName(GetStackValueKind(type), type); - - // TODO: Write barrier as necessary - AppendLine(); - Append("*(" + typeName + " *)((void **)"); - Append(_stack.Peek()); - Append(" + 1) = "); - Append(value); - AppendSemicolon(); + _stack.Push(BoxValue(type, value)); } } + private ExpressionEntry BoxValue(TypeDesc type, StackEntry value) + { + string tempName = NewTempName(); + + AddTypeReference(type, true); + Append(GetStackValueKindCPPTypeName(StackValueKind.ObjRef, type)); + Append(" "); + Append(tempName); + Append(" = __allocate_object("); + Append(_writer.GetCppTypeName(type)); + Append("::__getMethodTable())"); + AppendSemicolon(); + + string typeName = GetStackValueKindCPPTypeName(GetStackValueKind(type), type); + + // TODO: Write barrier as necessary + AppendLine(); + Append("*(" + typeName + " *)((void **)"); + Append(tempName); + Append(" + 1) = "); + Append(value); + AppendSemicolon(); + return new ExpressionEntry(StackValueKind.ObjRef, tempName, type); + } + private static bool IsOffsetContained(int offset, int start, int length) { return start <= offset && offset < start + length; @@ -2609,6 +2706,11 @@ namespace Internal.IL _dependencies.Add(_nodeFactory.MethodEntrypoint(method)); } + private void AddVirtualMethodReference(MethodDesc method) + { + _dependencies.Add(_nodeFactory.VirtualMethodUse(method)); + } + private void AddFieldReference(FieldDesc field) { if (field.IsStatic) @@ -2628,7 +2730,7 @@ namespace Internal.IL node = _nodeFactory.TypeNonGCStaticsSymbol(owningType); } - // TODO: Remove once the depedencies for static fields are tracked properly + // TODO: Remove once the dependencies for static fields are tracked properly GetSignatureTypeNameAndAddReference(owningType, true); _dependencies.Add(node); } @@ -2640,6 +2742,26 @@ namespace Internal.IL AddTypeReference(type, constructed); return _writer.GetCppSignatureTypeName(type); } + + private void ReportInvalidBranchTarget(int targetOffset) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + private void ReportFallthroughAtEndOfMethod() + { + ThrowHelper.ThrowInvalidProgramException(); + } + + private void ReportMethodEndInsideInstruction() + { + ThrowHelper.ThrowInvalidProgramException(); + } + + private void ReportInvalidInstruction(ILOpcode opcode) + { + ThrowHelper.ThrowInvalidProgramException(); + } } } diff --git a/external/corert/src/ILCompiler.CppCodeGen/src/ILCompiler.CppCodeGen.csproj b/external/corert/src/ILCompiler.CppCodeGen/src/ILCompiler.CppCodeGen.csproj index 57b613157d..1ef83f8bdd 100644 --- a/external/corert/src/ILCompiler.CppCodeGen/src/ILCompiler.CppCodeGen.csproj +++ b/external/corert/src/ILCompiler.CppCodeGen/src/ILCompiler.CppCodeGen.csproj @@ -1,40 +1,18 @@ - - + - Debug - AnyCPU - {971AE7E7-08C6-48B4-902A-63851E6DAC66} Library ILCompiler ILCompiler.CppCodeGen true - true - .NETStandard,Version=v1.3 + netstandard1.3 - - - - - - - false - .NetCoreApp,Version=2.0 - - - - {dac23e9f-f826-4577-ae7a-0849ff83280c} - ILCompiler.DependencyAnalysisFramework - - - {1a9df196-43a9-44bb-b2c6-d62aa56b0e49} - ILCompiler.TypeSystem - - - {13BB3788-C3EB-4046-8105-A95F8AE49404} - ILCompiler.Compiler - + + + + + @@ -61,8 +39,5 @@ - - - diff --git a/external/corert/src/ILCompiler.CppCodeGen/src/project.json b/external/corert/src/ILCompiler.CppCodeGen/src/project.json deleted file mode 100644 index 43580f1ced..0000000000 --- a/external/corert/src/ILCompiler.CppCodeGen/src/project.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "dependencies": { - "NETStandard.Library": "1.6.1", - "System.IO.MemoryMappedFiles": "4.3.0", - "System.Reflection.Metadata": "1.4.2", - "System.Collections.Immutable": "1.3.1", - "System.Runtime.CompilerServices.Unsafe": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0" - }, - "frameworks": { - "netstandard1.3": {} - } -} diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/App.config b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/App.config new file mode 100644 index 0000000000..731f6de6c2 --- /dev/null +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/DependencyGraphsForm.Designer.cs b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/DependencyGraphsForm.Designer.cs new file mode 100644 index 0000000000..7500e8fcb4 --- /dev/null +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/DependencyGraphsForm.Designer.cs @@ -0,0 +1,120 @@ +// 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. + +namespace DependencyLogViewer +{ + partial class DependencyGraphs + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.help = new System.Windows.Forms.Button(); + this.explore = new System.Windows.Forms.Button(); + this.listBox1 = new System.Windows.Forms.ListBox(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.SuspendLayout(); + // + // help + // + this.help.Dock = System.Windows.Forms.DockStyle.Fill; + this.help.Location = new System.Drawing.Point(0, 0); + this.help.Name = "help"; + this.help.Size = new System.Drawing.Size(281, 100); + this.help.TabIndex = 0; + this.help.Text = "Help"; + this.help.UseVisualStyleBackColor = true; + this.help.Click += new System.EventHandler(this.help_Click); + // + // explore + // + this.explore.Dock = System.Windows.Forms.DockStyle.Fill; + this.explore.Location = new System.Drawing.Point(0, 0); + this.explore.Name = "explore"; + this.explore.Size = new System.Drawing.Size(559, 100); + this.explore.TabIndex = 1; + this.explore.Text = "Explore"; + this.explore.UseVisualStyleBackColor = true; + this.explore.Click += new System.EventHandler(this.explore_Click); + // + // listBox1 + // + this.listBox1.Dock = System.Windows.Forms.DockStyle.Fill; + this.listBox1.FormattingEnabled = true; + this.listBox1.ItemHeight = 20; + this.listBox1.Location = new System.Drawing.Point(0, 0); + this.listBox1.Name = "listBox1"; + this.listBox1.Size = new System.Drawing.Size(844, 565); + this.listBox1.TabIndex = 2; + this.listBox1.DoubleClick += new System.EventHandler(this.explore_Click); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Bottom; + this.splitContainer1.Location = new System.Drawing.Point(0, 465); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.Controls.Add(this.help); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.explore); + this.splitContainer1.Size = new System.Drawing.Size(844, 100); + this.splitContainer1.SplitterDistance = 281; + this.splitContainer1.TabIndex = 3; + // + // DependencyGraphs + // + this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(844, 565); + this.Controls.Add(this.splitContainer1); + this.Controls.Add(this.listBox1); + this.Name = "DependencyGraphs"; + this.Text = "DependencyGraphs"; + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button help; + private System.Windows.Forms.Button explore; + private System.Windows.Forms.ListBox listBox1; + private System.Windows.Forms.SplitContainer splitContainer1; + } +} + diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/DependencyGraphsForm.cs b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/DependencyGraphsForm.cs new file mode 100644 index 0000000000..9912fc91ce --- /dev/null +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/DependencyGraphsForm.cs @@ -0,0 +1,82 @@ +// 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.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace DependencyLogViewer +{ + public partial class DependencyGraphs : Form + { + public DependencyGraphs() + { + InitializeComponent(); + } + + private void explore_Click(object sender, EventArgs e) + { + Graph g = this.listBox1.SelectedItem as Graph; + if (g != null) + { + SingleDependencyGraphForm singleGraphForm = new SingleDependencyGraphForm(g); + singleGraphForm.Show(); + } + } + + public void ForceRefresh() + { + Action refreshAction = () => + { + lock (GraphCollection.Singleton) + { + GraphCollection singleton = GraphCollection.Singleton; + this.listBox1.DataSource = singleton.Graphs.ToArray(); + } + }; + + this.BeginInvoke(refreshAction); + } + + protected override void OnHandleDestroyed(EventArgs e) + { + GraphProcessing.Singleton.Stop(); + Application.Exit(); + } + + private void help_Click(object sender, EventArgs e) + { + string helpMessage = @" +Dependency Graph Viewer +This application allows viewing the dependency graph produced by the CoreRT compilation. + +Usage instructions: +1. Launch the process as an administrator +2. Run the compiler +3. Explore through the graph + +Graphs View +- Choose one of the graphs that appears in the Dependency Graphs view to explore. As compilers execute, new graphs will automatically appear here. +- The set of graphs loaded into the process is limited by available memory space. To clear the used memory, close all windows of the application. + +Graph View +- In the Dependency Graph view, enter a regular expression in the text box, and then press ""Filter"". This will display a list of the nodes in the graph which have names which match the regular expression. +- Commonly, if there is a object file symbol associated with the node it should be used as part of the regular expression. See the various implementations of GetName in the compiler for naming behavior. +- Additionally, the event source marking mode assigns an Id to each node, and that is found as the mark object on the node, so if a specific id is known, just type that in, and it will appear in the window. (This is for use when using this tool in parallel with debugging the compiler. + +Single Node Exploration +Once the interesting node(s) have been identified in the dependency graph window, select one of them, and then press Explore. + - In the Node Explorer window, the Dependent nodes (the ones which dependend on the current node are the nodes displayed above, and the Dependee nodes (the nodes that this node depends on) are displayed below. Each node in the list is paired with a textual reason as to why that edge in the graph exists. + - Select a node to explore further and press the corresponding button to make it happen. +"; + MessageBox.Show(helpMessage); + } + } +} diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/DependencyGraphsForm.resx b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/DependencyGraphsForm.resx new file mode 100644 index 0000000000..1af7de150c --- /dev/null +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/DependencyGraphsForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/ILCompiler-DependencyGraph-Viewer.csproj b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/ILCompiler-DependencyGraph-Viewer.csproj new file mode 100644 index 0000000000..3a512a9ca3 --- /dev/null +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/ILCompiler-DependencyGraph-Viewer.csproj @@ -0,0 +1,112 @@ + + + + + Debug + AnyCPU + {03F15361-EAF4-4A06-8A2B-BD0304618CEA} + WinExe + DependencyLogViewer + ILCompiler-DependencyGraph-Viewer + v4.6.1 + 512 + true + + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + app.manifest + + + + + + + + + + + + + + + + + Form + + + DependencyGraphsForm.cs + + + Form + + + NodeForm.cs + + + + + Form + + + SingleDependencyGraphForm.cs + + + DependencyGraphsForm.cs + + + NodeForm.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + SingleDependencyGraphForm.cs + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + 1.0.41 + + + + \ No newline at end of file diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/ILCompiler-DependencyGraph-Viewer.sln b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/ILCompiler-DependencyGraph-Viewer.sln new file mode 100644 index 0000000000..bad93af4c6 --- /dev/null +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/ILCompiler-DependencyGraph-Viewer.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27016.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILCompiler-DependencyGraph-Viewer", "ILCompiler-DependencyGraph-Viewer.csproj", "{03F15361-EAF4-4A06-8A2B-BD0304618CEA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {03F15361-EAF4-4A06-8A2B-BD0304618CEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {03F15361-EAF4-4A06-8A2B-BD0304618CEA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {03F15361-EAF4-4A06-8A2B-BD0304618CEA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {03F15361-EAF4-4A06-8A2B-BD0304618CEA}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E06F254B-B3D1-456A-8707-75953B49D7C6} + EndGlobalSection +EndGlobal diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/NodeForm.Designer.cs b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/NodeForm.Designer.cs new file mode 100644 index 0000000000..ad9a3f6d21 --- /dev/null +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/NodeForm.Designer.cs @@ -0,0 +1,148 @@ +// 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. + +namespace DependencyLogViewer +{ + partial class NodeForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.nodeTitle = new System.Windows.Forms.Label(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.exploreDependee = new System.Windows.Forms.Button(); + this.dependeesListBox = new System.Windows.Forms.ListBox(); + this.exploreDependent = new System.Windows.Forms.Button(); + this.dependentsListBox = new System.Windows.Forms.ListBox(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.SuspendLayout(); + // + // nodeTitle + // + this.nodeTitle.AutoSize = true; + this.nodeTitle.Dock = System.Windows.Forms.DockStyle.Top; + this.nodeTitle.Location = new System.Drawing.Point(0, 0); + this.nodeTitle.Name = "nodeTitle"; + this.nodeTitle.Size = new System.Drawing.Size(51, 20); + this.nodeTitle.TabIndex = 0; + this.nodeTitle.Text = "label1"; + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 20); + this.splitContainer1.Name = "splitContainer1"; + this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.Controls.Add(this.dependentsListBox); + this.splitContainer1.Panel1.Controls.Add(this.exploreDependent); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.dependeesListBox); + this.splitContainer1.Panel2.Controls.Add(this.exploreDependee); + this.splitContainer1.Size = new System.Drawing.Size(1497, 892); + this.splitContainer1.SplitterDistance = 464; + this.splitContainer1.TabIndex = 1; + // + // exploreDependee + // + this.exploreDependee.Dock = System.Windows.Forms.DockStyle.Bottom; + this.exploreDependee.Location = new System.Drawing.Point(0, 389); + this.exploreDependee.Name = "exploreDependee"; + this.exploreDependee.Size = new System.Drawing.Size(1497, 35); + this.exploreDependee.TabIndex = 1; + this.exploreDependee.Text = "ExploreDependee"; + this.exploreDependee.UseVisualStyleBackColor = true; + this.exploreDependee.Click += new System.EventHandler(this.exploreDependee_Click); + // + // dependeesListBox + // + this.dependeesListBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.dependeesListBox.FormattingEnabled = true; + this.dependeesListBox.ItemHeight = 20; + this.dependeesListBox.Location = new System.Drawing.Point(0, 0); + this.dependeesListBox.Name = "dependeesListBox"; + this.dependeesListBox.Size = new System.Drawing.Size(1497, 424); + this.dependeesListBox.TabIndex = 0; + this.dependeesListBox.DoubleClick += new System.EventHandler(this.exploreDependee_Click); + // + // exploreDependent + // + this.exploreDependent.Dock = System.Windows.Forms.DockStyle.Bottom; + this.exploreDependent.Location = new System.Drawing.Point(0, 425); + this.exploreDependent.Name = "exploreDependent"; + this.exploreDependent.Size = new System.Drawing.Size(1497, 39); + this.exploreDependent.TabIndex = 1; + this.exploreDependent.Text = "Explore Dependent"; + this.exploreDependent.UseVisualStyleBackColor = true; + this.exploreDependent.Click += new System.EventHandler(this.exploreDependent_Click); + // + // dependentsListBox + // + this.dependentsListBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.dependentsListBox.FormattingEnabled = true; + this.dependentsListBox.ItemHeight = 20; + this.dependentsListBox.Location = new System.Drawing.Point(0, 0); + this.dependentsListBox.Name = "dependentsListBox"; + this.dependentsListBox.Size = new System.Drawing.Size(1497, 425); + this.dependentsListBox.TabIndex = 0; + this.dependentsListBox.DoubleClick += new System.EventHandler(this.exploreDependent_Click); + // + // NodeForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1497, 912); + this.Controls.Add(this.splitContainer1); + this.Controls.Add(this.nodeTitle); + this.Name = "NodeForm"; + this.Text = "NodeForm"; + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label nodeTitle; + private System.Windows.Forms.SplitContainer splitContainer1; + private System.Windows.Forms.Button exploreDependee; + private System.Windows.Forms.ListBox dependeesListBox; + private System.Windows.Forms.Button exploreDependent; + private System.Windows.Forms.ListBox dependentsListBox; + } +} diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/NodeForm.cs b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/NodeForm.cs new file mode 100644 index 0000000000..20f8d0dd89 --- /dev/null +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/NodeForm.cs @@ -0,0 +1,60 @@ +// 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.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace DependencyLogViewer +{ + public partial class NodeForm : Form + { + Graph _graph; + Node _node; + + public NodeForm(Graph g, Node n) + { + _graph = g; + _node = n; + + InitializeComponent(); + + this.Text = "Graph Pid:" + _graph.PID + " Id:" + _graph.ID + " Node:" + _node.ToString(); + this.nodeTitle.Text = _node.ToString(); + + lock (GraphCollection.Singleton) + { + this.dependentsListBox.DataSource = _node.Dependents.ToArray(); + this.dependeesListBox.DataSource = _node.Dependencies.ToArray(); + } + } + + private static void ExploreSelectedItem(Graph graph, ListBox listbox) + { + if (listbox.SelectedItem == null) + return; + + KeyValuePair pair = (KeyValuePair)listbox.SelectedItem; + + NodeForm nodeForm = new NodeForm(graph, pair.Key); + nodeForm.Show(); + } + + private void exploreDependee_Click(object sender, EventArgs e) + { + ExploreSelectedItem(_graph, dependeesListBox); + } + + private void exploreDependent_Click(object sender, EventArgs e) + { + ExploreSelectedItem(_graph, dependentsListBox); + } + } +} diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/NodeForm.resx b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/NodeForm.resx new file mode 100644 index 0000000000..1af7de150c --- /dev/null +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/NodeForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/Program.cs b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/Program.cs new file mode 100644 index 0000000000..16367610bf --- /dev/null +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/Program.cs @@ -0,0 +1,296 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; +using Microsoft.Diagnostics.Tracing; +using Microsoft.Diagnostics.Tracing.Session; +using System.Collections.Concurrent; +using System.Threading; + +namespace DependencyLogViewer +{ + public class Node + { + public Node(int index, string name) + { + Index = index; + Name = name; + } + + public override string ToString() + { + return "Index:" + Index.ToString() + " Name:" + Name; + } + + public readonly int Index; + public readonly string Name; + public readonly List> Dependencies = new List>(); + public readonly List> Dependents = new List>(); + } + + public class Graph + { + public override string ToString() + { + return "PID:" + PID.ToString() + " Id:" + ID.ToString() + " Name: " + Name; + } + + public int NextConditionalNodeIndex = Int32.MaxValue; + + public int PID; + public int ID; + public string Name; + public Dictionary Nodes = new Dictionary(); + } + + class GraphCollection + { + public static readonly GraphCollection Singleton = new GraphCollection(); + public static DependencyGraphs DependencyGraphsUI; + + public List Graphs = new List(); + + public void AddGraph(int pid, int id, string name) + { + Graph g = new Graph(); + g.ID = id; + g.PID = pid; + g.Name = name; + Graphs.Add(g); + + DependencyGraphsUI.ForceRefresh(); + } + + public Graph GetGraph(int pid, int id) + { + foreach (Graph g in Graphs) + { + if ((g.PID == pid) && (g.ID == id)) + return g; + } + + return null; + } + + public void AddNodeToGraph(int pid, int id, int index, string name) + { + Graph g = GetGraph(pid, id); + if (g == null) + return; + + Node n = new Node(index, name); + g.Nodes.Add(index, n); + } + + public void AddEdgeToGraph(int pid, int id, int edge1, int edge2, string reason) + { + Graph g = GetGraph(pid, id); + if (g == null) + return; + + Node dependent = g.Nodes[edge1]; + Node dependee = g.Nodes[edge2]; + + dependent.Dependencies.Add(new KeyValuePair(dependee, reason)); + dependee.Dependents.Add(new KeyValuePair(dependent, reason)); + } + + public void AddConditionalEdgeToGraph(int pid, int id, int reason1, int reason2, int target, string reason) + { + Graph g = GetGraph(pid, id); + if (g == null) + return; + + Node reason1Node = g.Nodes[reason1]; + Node reason2Node = g.Nodes[reason2]; + Node dependee = g.Nodes[target]; + + int conditionalNodeIndex = g.NextConditionalNodeIndex--; + Node conditionalNode = new Node(conditionalNodeIndex, String.Format("Conditional({0} - {1})", reason1Node.ToString(), reason2Node.ToString())); + g.Nodes.Add(conditionalNodeIndex, conditionalNode); + + conditionalNode.Dependencies.Add(new KeyValuePair(dependee, reason)); + dependee.Dependents.Add(new KeyValuePair(conditionalNode, reason)); + + reason1Node.Dependencies.Add(new KeyValuePair(conditionalNode, "Reason1Conditional - " + reason)); + conditionalNode.Dependents.Add(new KeyValuePair(reason1Node, "Reason1Conditional - " + reason)); + + reason2Node.Dependencies.Add(new KeyValuePair(conditionalNode, "Reason2Conditional - " + reason)); + conditionalNode.Dependents.Add(new KeyValuePair(reason2Node, "Reason2Conditional - " + reason)); + } + } + + enum GraphEventType + { + NewGraph, + NewNode, + NewEdge, + NewConditionalEdge, + } + + struct GraphEvent + { + public int Pid; + public int Id; + public GraphEventType EventType; + public int Num1; + public int Num2; + public int Num3; + public string Str; + } + + class GraphProcessing + { + public static GraphProcessing Singleton; + + ConcurrentQueue events = new ConcurrentQueue(); + TraceEventSession session; + volatile bool stopped; + + public GraphProcessing() + { + var sessionName = "GraphETWEventProcessingSession"; + + session = new TraceEventSession(sessionName); + + Thread t = new Thread(EventProcessingThread); + t.Start(); + + Thread t2 = new Thread(ETWImportingThread); + t2.Start(); + } + + private void EventProcessingThread() + { + GraphCollection collection = GraphCollection.Singleton; + + while (!stopped) + { + Thread.Sleep(1); + + lock (collection) + { + GraphEvent eventRead; + while (events.TryDequeue(out eventRead)) + { + try + { + switch (eventRead.EventType) + { + case GraphEventType.NewEdge: + collection.AddEdgeToGraph(eventRead.Pid, eventRead.Id, eventRead.Num1, eventRead.Num2, eventRead.Str); + break; + case GraphEventType.NewNode: + collection.AddNodeToGraph(eventRead.Pid, eventRead.Id, eventRead.Num1, eventRead.Str); + break; + case GraphEventType.NewGraph: + collection.AddGraph(eventRead.Pid, eventRead.Id, eventRead.Str); + break; + case GraphEventType.NewConditionalEdge: + collection.AddConditionalEdgeToGraph(eventRead.Pid, eventRead.Id, eventRead.Num1, eventRead.Num2, eventRead.Num3, eventRead.Str); + break; + } + } + catch + { + // Ignore bad input + } + } + } + } + } + + private void ETWImportingThread() + { + + using (session) + { + session.BufferSizeMB = 1024; + session.Source.Dynamic.AddCallbackForProviderEvent("Microsoft-ILCompiler-DependencyGraph", "Graph", delegate (TraceEvent data) + { + GraphEvent ge = new GraphEvent(); + ge.EventType = GraphEventType.NewGraph; + ge.Pid = data.ProcessID; + ge.Id = (int)data.PayloadValue(0); + ge.Str = (string)data.PayloadValue(1); + events.Enqueue(ge); + }); + session.Source.Dynamic.AddCallbackForProviderEvent("Microsoft-ILCompiler-DependencyGraph", "Node", delegate (TraceEvent data) + { + GraphEvent ge = new GraphEvent(); + ge.EventType = GraphEventType.NewNode; + ge.Pid = data.ProcessID; + ge.Id = (int)data.PayloadValue(0); + ge.Num1 = (int)data.PayloadValue(1); + ge.Str = (string)data.PayloadValue(2); + events.Enqueue(ge); + }); + session.Source.Dynamic.AddCallbackForProviderEvent("Microsoft-ILCompiler-DependencyGraph", "Edge", delegate (TraceEvent data) + { + GraphEvent ge = new GraphEvent(); + ge.EventType = GraphEventType.NewEdge; + ge.Pid = data.ProcessID; + ge.Id = (int)data.PayloadValue(0); + ge.Num1 = (int)data.PayloadValue(1); + ge.Num2 = (int)data.PayloadValue(2); + ge.Str = (string)data.PayloadValue(3); + events.Enqueue(ge); + }); + session.Source.Dynamic.AddCallbackForProviderEvent("Microsoft-ILCompiler-DependencyGraph", "ConditionalEdge", delegate (TraceEvent data) + { + GraphEvent ge = new GraphEvent(); + ge.EventType = GraphEventType.NewConditionalEdge; + ge.Pid = data.ProcessID; + ge.Id = (int)data.PayloadValue(0); + ge.Num1 = (int)data.PayloadValue(1); + ge.Num2 = (int)data.PayloadValue(2); + ge.Num3 = (int)data.PayloadValue(3); + ge.Str = (string)data.PayloadValue(4); + events.Enqueue(ge); + }); + + var restarted = session.EnableProvider("Microsoft-ILCompiler-DependencyGraph"); + session.Source.Process(); + } + } + + public void Stop() + { + session.Source.Dispose(); + stopped = true; + } + } + + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static int Main() + { + // Today you have to be Admin to turn on ETW events (anyone can write ETW events). + if (!(TraceEventSession.IsElevated() ?? false)) + { + MessageBox.Show("To turn on ETW events you need to be Administrator, please run from an Admin process."); + return -1; + } + + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + + GraphCollection.DependencyGraphsUI = new DependencyGraphs(); + GraphProcessing.Singleton = new GraphProcessing(); + + Application.Run(GraphCollection.DependencyGraphsUI); + + return 0; + } + } +} diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/Properties/AssemblyInfo.cs b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..5499050ec6 --- /dev/null +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("DependencyLogViewer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("DependencyLogViewer")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("03f15361-eaf4-4a06-8a2b-bd0304618cea")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/Properties/Resources.Designer.cs b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/Properties/Resources.Designer.cs new file mode 100644 index 0000000000..651fecb56b --- /dev/null +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace DependencyLogViewer.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DependencyLogViewer.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/Properties/Resources.resx b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/Properties/Resources.resx new file mode 100644 index 0000000000..af7dbebbac --- /dev/null +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/Properties/Settings.Designer.cs b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/Properties/Settings.Designer.cs new file mode 100644 index 0000000000..4ae4821590 --- /dev/null +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace DependencyLogViewer.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/Properties/Settings.settings b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/Properties/Settings.settings new file mode 100644 index 0000000000..39645652af --- /dev/null +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/SingleDependencyGraphForm.Designer.cs b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/SingleDependencyGraphForm.Designer.cs new file mode 100644 index 0000000000..69436f5978 --- /dev/null +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/SingleDependencyGraphForm.Designer.cs @@ -0,0 +1,151 @@ +// 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. + +namespace DependencyLogViewer +{ + partial class SingleDependencyGraphForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.filteredNodes = new System.Windows.Forms.ListBox(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.filterButton = new System.Windows.Forms.Button(); + this.filterTextBox = new System.Windows.Forms.TextBox(); + this.exploreNode = new System.Windows.Forms.Button(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.Controls.Add(this.filteredNodes); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Size = new System.Drawing.Size(920, 581); + this.splitContainer1.SplitterDistance = 359; + this.splitContainer1.TabIndex = 0; + // + // filteredNodes + // + this.filteredNodes.Dock = System.Windows.Forms.DockStyle.Fill; + this.filteredNodes.FormattingEnabled = true; + this.filteredNodes.ItemHeight = 20; + this.filteredNodes.Location = new System.Drawing.Point(0, 0); + this.filteredNodes.Name = "filteredNodes"; + this.filteredNodes.Size = new System.Drawing.Size(920, 359); + this.filteredNodes.TabIndex = 0; + this.filteredNodes.DoubleClick += new System.EventHandler(this.exploreNode_Click); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 2; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.filterButton, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.filterTextBox, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.exploreNode, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(920, 218); + this.tableLayoutPanel1.TabIndex = 0; + // + // filterButton + // + this.filterButton.Dock = System.Windows.Forms.DockStyle.Fill; + this.filterButton.Location = new System.Drawing.Point(3, 3); + this.filterButton.Name = "filterButton"; + this.filterButton.Size = new System.Drawing.Size(454, 103); + this.filterButton.TabIndex = 0; + this.filterButton.Text = "Filter"; + this.filterButton.UseVisualStyleBackColor = true; + this.filterButton.Click += new System.EventHandler(this.filterButton_Click); + // + // filterTextBox + // + this.filterTextBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.filterTextBox.Location = new System.Drawing.Point(463, 3); + this.filterTextBox.Name = "filterTextBox"; + this.filterTextBox.Size = new System.Drawing.Size(454, 26); + this.filterTextBox.TabIndex = 1; + // + // exploreNode + // + this.exploreNode.Dock = System.Windows.Forms.DockStyle.Fill; + this.exploreNode.Location = new System.Drawing.Point(3, 112); + this.exploreNode.Name = "exploreNode"; + this.exploreNode.Size = new System.Drawing.Size(454, 103); + this.exploreNode.TabIndex = 2; + this.exploreNode.Text = "Explore Node"; + this.exploreNode.UseVisualStyleBackColor = true; + this.exploreNode.Click += new System.EventHandler(this.exploreNode_Click); + // + // SingleDependencyGraphForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(920, 581); + this.Controls.Add(this.splitContainer1); + this.Name = "SingleDependencyGraphForm"; + this.Text = "SingleDependencyGraphForm"; + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.SplitContainer splitContainer1; + private System.Windows.Forms.ListBox filteredNodes; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Button filterButton; + private System.Windows.Forms.TextBox filterTextBox; + private System.Windows.Forms.Button exploreNode; + } +} diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/SingleDependencyGraphForm.cs b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/SingleDependencyGraphForm.cs new file mode 100644 index 0000000000..9c4776080d --- /dev/null +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/SingleDependencyGraphForm.cs @@ -0,0 +1,57 @@ +// 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.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using System.Text.RegularExpressions; + +namespace DependencyLogViewer +{ + public partial class SingleDependencyGraphForm : Form + { + private Graph _graph; + + public SingleDependencyGraphForm(Graph graph) + { + _graph = graph; + InitializeComponent(); + this.Text = this.Text + graph.ToString(); + } + + private void filterButton_Click(object sender, EventArgs e) + { + string filterString = this.filterTextBox.Text; + lock (GraphCollection.Singleton) + { + Regex regex = new Regex(filterString, RegexOptions.Compiled); + + List filteredNodes = new List(); + foreach (var entry in _graph.Nodes) + { + if (regex.IsMatch(entry.Value.ToString())) + filteredNodes.Add(entry.Value); + } + + this.filteredNodes.DataSource = filteredNodes; + } + } + + private void exploreNode_Click(object sender, EventArgs e) + { + Node n = filteredNodes.SelectedItem as Node; + if (n != null) + { + NodeForm nodeForm = new NodeForm(_graph, n); + nodeForm.Show(); + } + } + } +} diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/SingleDependencyGraphForm.resx b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/SingleDependencyGraphForm.resx new file mode 100644 index 0000000000..1af7de150c --- /dev/null +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/SingleDependencyGraphForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/app.manifest b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/app.manifest new file mode 100644 index 0000000000..467015914e --- /dev/null +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/ILCompiler-DependencyGraph-Viewer/app.manifest @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/DependencyAnalyzer.cs b/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/DependencyAnalyzer.cs index 446b51ff1b..52f25984be 100644 --- a/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/DependencyAnalyzer.cs +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/DependencyAnalyzer.cs @@ -40,6 +40,7 @@ namespace ILCompiler.DependencyAnalysisFramework private Dictionary, HashSet.CombinedDependencyListEntry>> _conditional_dependency_store = new Dictionary, HashSet.CombinedDependencyListEntry>>(); private bool _markingCompleted = false; + private Random _stackPopRandomizer = null; private struct DynamicDependencyNode { @@ -68,6 +69,12 @@ namespace ILCompiler.DependencyAnalysisFramework { _dependencyContext = dependencyContext; _resultSorter = resultSorter; + _marker.AttachContext(dependencyContext); + + if (int.TryParse(Environment.GetEnvironmentVariable("CoreRT_DeterminismSeed"), out int seed)) + { + _stackPopRandomizer = new Random(seed); + } } /// @@ -87,8 +94,7 @@ namespace ILCompiler.DependencyAnalysisFramework { if (!_markingCompleted) { - _markingCompleted = true; - ComputeMarkedNodes(); + throw new InvalidOperationException(); } return _markedNodesFinal; @@ -242,8 +248,11 @@ namespace ILCompiler.DependencyAnalysisFramework } while (_markStack.Count != 0); } - private void ComputeMarkedNodes() + public override void ComputeMarkedNodes() { + if (_markingCompleted) + return; + do { // Run mark stack algorithm as much as possible @@ -265,13 +274,40 @@ namespace ILCompiler.DependencyAnalysisFramework _markedNodesFinal = _markedNodes.ToImmutableArray(); _markedNodes = null; + _markingCompleted = true; } private bool AddToMarkStack(DependencyNodeCore node, string reason, DependencyNodeCore reason1, DependencyNodeCore reason2) { if (_marker.MarkNode(node, reason1, reason2, reason)) { - _markStack.Push(node); + // Pop the top node of the mark stack + if (_stackPopRandomizer == null) + { + _markStack.Push(node); + } + else + { + // + // Expose output file determinism bugs in our system by randomizing the order nodes are pushed + // on to the mark stack. + // + int randomNodeIndex = _stackPopRandomizer.Next(_markStack.Count); + var tempStack = new Stack>(); + + for (int i = 0; i < randomNodeIndex; i++) + { + tempStack.Push(_markStack.Pop()); + } + + _markStack.Push(node); + + while (tempStack.Count > 0) + { + _markStack.Push(tempStack.Pop()); + } + } + _markedNodes.Add(node); node.CallOnMarked(_dependencyContext); diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/DependencyAnalyzerBase.cs b/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/DependencyAnalyzerBase.cs index 809db8713d..56878094dc 100644 --- a/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/DependencyAnalyzerBase.cs +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/DependencyAnalyzerBase.cs @@ -40,12 +40,19 @@ namespace ILCompiler.DependencyAnalysisFramework /// /// Return the marked node list. Do not modify this list, as it will cause unexpected behavior. + /// Call to compute the list first. /// public abstract ImmutableArray> MarkedNodeList { get; } + /// + /// Computes the list of marked nodes. This is a no-op if the marked nodes are already computed. + /// The list is available as . + /// + public abstract void ComputeMarkedNodes(); + /// /// This event is triggered when a node is added to the graph. /// diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/EventSourceLogStrategy.cs b/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/EventSourceLogStrategy.cs new file mode 100644 index 0000000000..82e2c5b25a --- /dev/null +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/EventSourceLogStrategy.cs @@ -0,0 +1,137 @@ +// 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.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Threading; +using System.Diagnostics.Tracing; +using System.Runtime.InteropServices; + +namespace ILCompiler.DependencyAnalysisFramework +{ + [EventSource(Name = "Microsoft-ILCompiler-DependencyGraph")] + class GraphEventSource : EventSource + { + public class Keywords + { + public const EventKeywords Graph = (EventKeywords)1; + } + + // Notice that the bodies of the events follow a pattern: WriteEvent(ID, ) where + // ID is a unique ID starting at 1 and incrementing for each new event method. and + // is every argument for the method. + // WriteEvent then takes care of all the details of actually writing out the values complete + // with the name of the event (method name) as well as the names and types of all the parameters. + [Event(1, Keywords = Keywords.Graph, Level = EventLevel.Informational)] + public void Graph(int id, string name) { WriteEvent(1, id, name); } + [Event(2, Keywords = Keywords.Graph, Level = EventLevel.Informational)] + public void Node(int id, int index, string name) { WriteEvent(2, id, index, name); } + [Event(3, Keywords = Keywords.Graph, Level = EventLevel.Informational)] + public void Edge(int id, int dependentIndex, int dependencyIndex, string reason) { WriteEvent(3, id, dependentIndex, dependencyIndex, reason); } + [Event(4, Keywords = Keywords.Graph, Level = EventLevel.Informational)] + public void ConditionalEdge(int id, int dependentIndex1, int dependentIndex2, int dependencyIndex, string reason) { WriteEvent(4, id, dependentIndex1, dependentIndex2, dependencyIndex, reason); } + + // Typically you only create one EventSource and use it throughout your program. Thus a static field makes sense. + public static GraphEventSource Log = new GraphEventSource(); + } + + public struct EventSourceLogStrategy : IDependencyAnalysisMarkStrategy + { + private static int s_GraphIds = 0; + + private int GraphId; + private int RootIndex; + private int ObjectIndex; + private DependencyContextType _context; + + public static bool IsEventSourceEnabled + { + get + { + return +#if !ALWAYS_SUPPORT_EVENTSOURCE_LOG + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && // Processing these event source events is only implemented on Windows +#endif + GraphEventSource.Log.IsEnabled(); + } + } + + bool IDependencyAnalysisMarkStrategy.MarkNode( + DependencyNodeCore node, + DependencyNodeCore reasonNode, + DependencyNodeCore reasonNode2, + string reason) + { + bool retVal = false; + + int nodeIndex; + + if (!node.Marked) + { + nodeIndex = Interlocked.Increment(ref ObjectIndex); + node.SetMark(nodeIndex); + + if (GraphId == 0) + { + lock (GraphEventSource.Log) + { + if (GraphId == 0) + { + GraphId = Interlocked.Increment(ref s_GraphIds); + GraphEventSource.Log.Graph(GraphId, ""); + RootIndex = Interlocked.Increment(ref ObjectIndex); + GraphEventSource.Log.Node(GraphId, RootIndex, "roots"); + } + } + } + + retVal = true; + + GraphEventSource.Log.Node(GraphId, nodeIndex, node.GetNameInternal(_context)); + } + else + { + nodeIndex = (int)node.GetMark(); + } + + if (reasonNode != null) + { + if (reasonNode2 != null) + { + GraphEventSource.Log.ConditionalEdge(GraphId, (int)reasonNode.GetMark(), (int)reasonNode2.GetMark(), nodeIndex, reason); + } + else + { + GraphEventSource.Log.Edge(GraphId, (int)reasonNode.GetMark(), nodeIndex, reason); + } + } + else + { + GraphEventSource.Log.Edge(GraphId, RootIndex, nodeIndex, reason); + } + return retVal; + } + + void IDependencyAnalysisMarkStrategy.VisitLogEdges(IEnumerable> nodeList, IDependencyAnalyzerLogEdgeVisitor logEdgeVisitor) + { + // This marker does not permit logging. + return; + } + + void IDependencyAnalysisMarkStrategy.VisitLogNodes(IEnumerable> nodeList, IDependencyAnalyzerLogNodeVisitor logNodeVisitor) + { + // This marker does not permit logging. + return; + } + + void IDependencyAnalysisMarkStrategy.AttachContext(DependencyContextType context) + { + _context = context; + } + } +} diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/FirstMarkLogStrategy.cs b/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/FirstMarkLogStrategy.cs index 55dd769c5f..ddab08988f 100644 --- a/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/FirstMarkLogStrategy.cs +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/FirstMarkLogStrategy.cs @@ -121,5 +121,10 @@ namespace ILCompiler.DependencyAnalysisFramework } } } + + void IDependencyAnalysisMarkStrategy.AttachContext(DependencyContextType context) + { + // This logger does not need to use the context + } } } diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/FullGraphLogStrategy.cs b/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/FullGraphLogStrategy.cs index b85842dc29..32e171c8d7 100644 --- a/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/FullGraphLogStrategy.cs +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/FullGraphLogStrategy.cs @@ -195,5 +195,9 @@ namespace ILCompiler.DependencyAnalysisFramework } } } + void IDependencyAnalysisMarkStrategy.AttachContext(DependencyContextType context) + { + // This logger does not need to use the context + } } } diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/IDependencyAnalysisMarkStrategy.cs b/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/IDependencyAnalysisMarkStrategy.cs index 02b1ca2f5d..ccb5afb23c 100644 --- a/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/IDependencyAnalysisMarkStrategy.cs +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/IDependencyAnalysisMarkStrategy.cs @@ -22,5 +22,7 @@ namespace ILCompiler.DependencyAnalysisFramework void VisitLogNodes(IEnumerable> nodeList, IDependencyAnalyzerLogNodeVisitor logNodeVisitor); void VisitLogEdges(IEnumerable> nodeList, IDependencyAnalyzerLogEdgeVisitor logEdgeVisitor); + + void AttachContext(DependencyContextType context); } } diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/ILCompiler.DependencyAnalysisFramework.csproj b/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/ILCompiler.DependencyAnalysisFramework.csproj index 300c271a38..b9ce700079 100644 --- a/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/ILCompiler.DependencyAnalysisFramework.csproj +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/ILCompiler.DependencyAnalysisFramework.csproj @@ -1,26 +1,18 @@ - - + - Debug - AnyCPU - {DAC23E9F-F826-4577-AE7A-0849FF83280C} Library - Properties ILCompiler.DependencyAnalysisFramework ILCompiler.DependencyAnalysisFramework - true - .NETStandard,Version=v1.3 - - - - + netstandard1.3 - - false - .NetCoreApp,Version=2.0 - + + 4.3.0 + + + 1.3.1 + @@ -29,6 +21,7 @@ + @@ -37,8 +30,5 @@ - - - - \ No newline at end of file + diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/NoLogStrategy.cs b/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/NoLogStrategy.cs index 530a1db4f8..bd2b9d3a80 100644 --- a/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/NoLogStrategy.cs +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/NoLogStrategy.cs @@ -43,5 +43,10 @@ namespace ILCompiler.DependencyAnalysisFramework // This marker does not permit logging. return; } + + void IDependencyAnalysisMarkStrategy.AttachContext(DependencyContextType context) + { + // This logger does not need to use the context + } } } diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/project.json b/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/project.json deleted file mode 100644 index e2b39c4024..0000000000 --- a/external/corert/src/ILCompiler.DependencyAnalysisFramework/src/project.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "dependencies": { - "NETStandard.Library": "1.6.1", - "System.Xml.ReaderWriter": "4.3.0", - "System.Collections.Immutable": "1.3.1" - }, - "frameworks": { - "netstandard1.3": {} - } -} diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/tests/ILCompiler.DependencyAnalysisFramework.Tests.csproj b/external/corert/src/ILCompiler.DependencyAnalysisFramework/tests/ILCompiler.DependencyAnalysisFramework.Tests.csproj index 54d2e0ced4..37f6e2a402 100644 --- a/external/corert/src/ILCompiler.DependencyAnalysisFramework/tests/ILCompiler.DependencyAnalysisFramework.Tests.csproj +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/tests/ILCompiler.DependencyAnalysisFramework.Tests.csproj @@ -1,33 +1,25 @@ - - + - Debug - AnyCPU - {90076B9B-918B-49DD-8ADE-E76426D60B4D} Library ILCompiler.DependencyAnalysisFramework.Tests ILCompiler.DependencyAnalysisFramework.Tests - .NETStandard,Version=v1.3 - netcoreapp1.1 - - - - - + netstandard1.3 - - {DAC23E9F-F826-4577-AE7A-0849FF83280C} - ILCompiler.DependencyAnalysisFramework - + + + + + 1.3.1 + + + $(XunitNetcoreExtensionsVersion) + - - - - \ No newline at end of file + diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/tests/TestGraph.cs b/external/corert/src/ILCompiler.DependencyAnalysisFramework/tests/TestGraph.cs index 53a0f7690d..ebf9000dbc 100644 --- a/external/corert/src/ILCompiler.DependencyAnalysisFramework/tests/TestGraph.cs +++ b/external/corert/src/ILCompiler.DependencyAnalysisFramework/tests/TestGraph.cs @@ -163,6 +163,9 @@ namespace ILCompiler.DependencyAnalysisFramework.Tests get { List liveNodes = new List(); + + _analyzer.ComputeMarkedNodes(); + foreach (var node in _analyzer.MarkedNodeList) { liveNodes.Add(((TestNode)node).Data); diff --git a/external/corert/src/ILCompiler.DependencyAnalysisFramework/tests/project.json b/external/corert/src/ILCompiler.DependencyAnalysisFramework/tests/project.json deleted file mode 100644 index be28cf2c66..0000000000 --- a/external/corert/src/ILCompiler.DependencyAnalysisFramework/tests/project.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "dependencies": { - "NETStandard.Library": "1.6.1", - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections.Immutable": "1.3.1", - "Microsoft.DotNet.BuildTools.TestSuite": "1.0.0-prerelease-00807-03", - "test-runtime": { - "target": "project", - "exclude": "compile" - } - }, - "frameworks": { - "netstandard1.3": { } - }, - "supports": { - "coreFx.Test.netcoreapp1.1": { } - } -} diff --git a/external/corert/src/ILCompiler.MetadataTransform/src/ILCompiler.MetadataTransform.csproj b/external/corert/src/ILCompiler.MetadataTransform/src/ILCompiler.MetadataTransform.csproj index 71389b2369..2ecadbac2a 100644 --- a/external/corert/src/ILCompiler.MetadataTransform/src/ILCompiler.MetadataTransform.csproj +++ b/external/corert/src/ILCompiler.MetadataTransform/src/ILCompiler.MetadataTransform.csproj @@ -1,34 +1,18 @@ - - + - {A965EA82-219D-48F7-AD51-BC030C16CC6F} Library - Properties ILCompiler.MetadataTransform - true - .NETStandard,Version=v1.3 + netstandard1.3 - - - - - - - - false - .NetCoreApp,Version=2.0 - - - - {D66338D4-F9E4-4051-B302-232C6BFB6EF6} - ILCompiler.MetadataWriter - - - {1a9df196-43a9-44bb-b2c6-d62aa56b0e49} - ILCompiler.TypeSystem - + + + + + + 1.4.2 + @@ -48,9 +32,7 @@ - - - + diff --git a/external/corert/src/ILCompiler.MetadataTransform/src/ILCompiler/Metadata/Transform.Namespace.cs b/external/corert/src/ILCompiler.MetadataTransform/src/ILCompiler/Metadata/Transform.Namespace.cs index 495ef7b78f..6573c3b8d9 100644 --- a/external/corert/src/ILCompiler.MetadataTransform/src/ILCompiler/Metadata/Transform.Namespace.cs +++ b/external/corert/src/ILCompiler.MetadataTransform/src/ILCompiler/Metadata/Transform.Namespace.cs @@ -72,6 +72,10 @@ namespace ILCompiler.Metadata private NamespaceReference HandleNamespaceReference(Cts.ModuleDesc parentScope, string namespaceString) { + // The format represents root namespace as a namespace with null name, in contrast with ECMA-335 + if (namespaceString.Length == 0) + namespaceString = null; + NamespaceReference result; NamespaceKey key = new NamespaceKey(parentScope, namespaceString); if (_namespaceRefs.TryGetValue(key, out result)) @@ -92,6 +96,9 @@ namespace ILCompiler.Metadata _namespaceRefs.Add(key, rootNamespace); } + if (namespaceString == null) + return rootNamespace; + NamespaceReference currentNamespace = rootNamespace; string currentNamespaceName = String.Empty; foreach (var segment in namespaceString.Split('.')) diff --git a/external/corert/src/ILCompiler.MetadataTransform/src/ILCompiler/Metadata/Transform.Scope.cs b/external/corert/src/ILCompiler.MetadataTransform/src/ILCompiler/Metadata/Transform.Scope.cs index a518dcb85f..406a61511f 100644 --- a/external/corert/src/ILCompiler.MetadataTransform/src/ILCompiler/Metadata/Transform.Scope.cs +++ b/external/corert/src/ILCompiler.MetadataTransform/src/ILCompiler/Metadata/Transform.Scope.cs @@ -13,6 +13,7 @@ using Debug = System.Diagnostics.Debug; using AssemblyFlags = Internal.Metadata.NativeFormat.AssemblyFlags; using AssemblyNameFlags = System.Reflection.AssemblyNameFlags; using AssemblyContentType = System.Reflection.AssemblyContentType; +using AssemblyName = System.Reflection.AssemblyName; namespace ILCompiler.Metadata { @@ -97,6 +98,8 @@ namespace ILCompiler.Metadata { scopeDefinition.ModuleCustomAttributes = HandleCustomAttributes(ecmaAssembly, moduleAttributes); } + + HandleTypeForwarders(ecmaAssembly); } } else @@ -105,45 +108,57 @@ namespace ILCompiler.Metadata } } - private EntityMap _scopeRefs - = new EntityMap(EqualityComparer.Default); - private Action _initScopeRef; + private EntityMap _scopeRefs + = new EntityMap(new SimpleAssemblyNameComparer()); + private Action _initScopeRef; private ScopeReference HandleScopeReference(Cts.ModuleDesc module) { - return _scopeRefs.GetOrCreate(module, _initScopeRef ?? (_initScopeRef = InitializeScopeReference)); + var assembly = module as Cts.IAssemblyDesc; + if (assembly != null) + return HandleScopeReference(assembly.GetName()); + else + throw new NotSupportedException("Multi-module assemblies"); } - private void InitializeScopeReference(Cts.ModuleDesc module, ScopeReference scopeReference) + private ScopeReference HandleScopeReference(AssemblyName assemblyName) { - var assemblyDesc = module as Cts.IAssemblyDesc; - if (assemblyDesc != null) + return _scopeRefs.GetOrCreate(assemblyName, _initScopeRef ?? (_initScopeRef = InitializeScopeReference)); + } + + private void InitializeScopeReference(AssemblyName assemblyName, ScopeReference scopeReference) + { + scopeReference.Name = HandleString(assemblyName.Name); + scopeReference.Culture = HandleString(assemblyName.CultureName); + scopeReference.MajorVersion = checked((ushort)assemblyName.Version.Major); + scopeReference.MinorVersion = checked((ushort)assemblyName.Version.Minor); + scopeReference.BuildNumber = checked((ushort)assemblyName.Version.Build); + scopeReference.RevisionNumber = checked((ushort)assemblyName.Version.Revision); + + Debug.Assert((int)AssemblyFlags.PublicKey == (int)AssemblyNameFlags.PublicKey); + Debug.Assert((int)AssemblyFlags.Retargetable == (int)AssemblyNameFlags.Retargetable); + + // References use a public key token instead of full public key. + scopeReference.Flags = (AssemblyFlags)(assemblyName.Flags & ~AssemblyNameFlags.PublicKey); + + if (assemblyName.ContentType == AssemblyContentType.WindowsRuntime) { - var assemblyName = assemblyDesc.GetName(); - - scopeReference.Name = HandleString(assemblyName.Name); - scopeReference.Culture = HandleString(assemblyName.CultureName); - scopeReference.MajorVersion = checked((ushort)assemblyName.Version.Major); - scopeReference.MinorVersion = checked((ushort)assemblyName.Version.Minor); - scopeReference.BuildNumber = checked((ushort)assemblyName.Version.Build); - scopeReference.RevisionNumber = checked((ushort)assemblyName.Version.Revision); - - Debug.Assert((int)AssemblyFlags.PublicKey == (int)AssemblyNameFlags.PublicKey); - Debug.Assert((int)AssemblyFlags.Retargetable == (int)AssemblyNameFlags.Retargetable); - - // References use a public key token instead of full public key. - scopeReference.Flags = (AssemblyFlags)(assemblyName.Flags & ~AssemblyNameFlags.PublicKey); - - if (assemblyName.ContentType == AssemblyContentType.WindowsRuntime) - { - scopeReference.Flags |= (AssemblyFlags)((int)AssemblyContentType.WindowsRuntime << 9); - } - - scopeReference.PublicKeyOrToken = assemblyName.GetPublicKeyToken(); + scopeReference.Flags |= (AssemblyFlags)((int)AssemblyContentType.WindowsRuntime << 9); } - else + + scopeReference.PublicKeyOrToken = assemblyName.GetPublicKeyToken(); + } + + private class SimpleAssemblyNameComparer : IEqualityComparer + { + public bool Equals(AssemblyName x, AssemblyName y) { - throw new NotSupportedException("Multi-module assemblies"); + return Object.Equals(x.Name, y.Name); + } + + public int GetHashCode(AssemblyName obj) + { + return obj.Name?.GetHashCode() ?? 0; } } } diff --git a/external/corert/src/ILCompiler.MetadataTransform/src/ILCompiler/Metadata/Transform.Type.cs b/external/corert/src/ILCompiler.MetadataTransform/src/ILCompiler/Metadata/Transform.Type.cs index ea95f9c506..97727a22ff 100644 --- a/external/corert/src/ILCompiler.MetadataTransform/src/ILCompiler/Metadata/Transform.Type.cs +++ b/external/corert/src/ILCompiler.MetadataTransform/src/ILCompiler/Metadata/Transform.Type.cs @@ -32,8 +32,6 @@ namespace ILCompiler.Metadata public override MetadataRecord HandleType(Cts.TypeDesc type) { - Debug.Assert(!IsBlocked(type)); - MetadataRecord rec; if (_types.TryGet(type, out rec)) { @@ -78,6 +76,7 @@ namespace ILCompiler.Metadata var metadataType = (Cts.MetadataType)type; if (_policy.GeneratesMetadata(metadataType)) { + Debug.Assert(!_policy.IsBlocked(metadataType)); rec = _types.Create(metadataType, _initTypeDef ?? (_initTypeDef = InitializeTypeDef)); } else diff --git a/external/corert/src/ILCompiler.MetadataTransform/src/ILCompiler/Metadata/Transform.TypeForwarders.cs b/external/corert/src/ILCompiler.MetadataTransform/src/ILCompiler/Metadata/Transform.TypeForwarders.cs new file mode 100644 index 0000000000..be03c1d730 --- /dev/null +++ b/external/corert/src/ILCompiler.MetadataTransform/src/ILCompiler/Metadata/Transform.TypeForwarders.cs @@ -0,0 +1,92 @@ +// 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 Internal.Metadata.NativeFormat.Writer; + +using Cts = Internal.TypeSystem; +using Ecma = System.Reflection.Metadata; + +using Debug = System.Diagnostics.Debug; +using AssemblyName = System.Reflection.AssemblyName; +using AssemblyContentType = System.Reflection.AssemblyContentType; +using AssemblyNameFlags = System.Reflection.AssemblyNameFlags; +using AssemblyFlags = System.Reflection.AssemblyFlags; + +namespace ILCompiler.Metadata +{ + partial class Transform + { + private void HandleTypeForwarders(Cts.Ecma.EcmaModule module) + { + foreach (var exportedTypeHandle in module.MetadataReader.ExportedTypes) + { + Ecma.ExportedType exportedType = module.MetadataReader.GetExportedType(exportedTypeHandle); + if (exportedType.IsForwarder || exportedType.Implementation.Kind == Ecma.HandleKind.ExportedType) + { + HandleTypeForwarder(module, exportedType); + } + else + { + Debug.Assert(false, "Multi-module assemblies"); + } + } + } + + private TypeForwarder HandleTypeForwarder(Cts.Ecma.EcmaModule module, Ecma.ExportedType exportedType) + { + Ecma.MetadataReader reader = module.MetadataReader; + string name = reader.GetString(exportedType.Name); + TypeForwarder result; + + switch (exportedType.Implementation.Kind) + { + case Ecma.HandleKind.AssemblyReference: + { + string ns = reader.GetString(exportedType.Namespace); + NamespaceDefinition namespaceDefinition = HandleNamespaceDefinition(module, ns); + + Ecma.AssemblyReference assemblyRef = reader.GetAssemblyReference((Ecma.AssemblyReferenceHandle)exportedType.Implementation); + AssemblyName refName = new AssemblyName + { + ContentType = (AssemblyContentType)((int)(assemblyRef.Flags & AssemblyFlags.ContentTypeMask) >> 9), + Flags = (AssemblyNameFlags)(assemblyRef.Flags & ~AssemblyFlags.ContentTypeMask), + CultureName = reader.GetString(assemblyRef.Culture), + Name = reader.GetString(assemblyRef.Name), + Version = assemblyRef.Version, + }; + + result = new TypeForwarder + { + Name = HandleString(name), + Scope = HandleScopeReference(refName), + }; + + namespaceDefinition.TypeForwarders.Add(result); + } + break; + + case Ecma.HandleKind.ExportedType: + { + TypeForwarder scope = HandleTypeForwarder(module, reader.GetExportedType((Ecma.ExportedTypeHandle)exportedType.Implementation)); + + result = new TypeForwarder + { + Name = HandleString(name), + Scope = scope.Scope, + }; + + scope.NestedTypes.Add(result); + } + break; + + default: + throw new BadImageFormatException(); + } + + return result; + } + } +} diff --git a/external/corert/src/ILCompiler.MetadataTransform/src/project.json b/external/corert/src/ILCompiler.MetadataTransform/src/project.json deleted file mode 100644 index 75ee7c0bd7..0000000000 --- a/external/corert/src/ILCompiler.MetadataTransform/src/project.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "dependencies": { - "NETStandard.Library": "1.6.1", - "System.Reflection.Metadata": "1.4.2" - }, - "frameworks": { - "netstandard1.3": { } - } -} diff --git a/external/corert/src/ILCompiler.MetadataTransform/tests/ILCompiler.MetadataTransform.Tests.csproj b/external/corert/src/ILCompiler.MetadataTransform/tests/ILCompiler.MetadataTransform.Tests.csproj index e118897679..9de4e1df79 100644 --- a/external/corert/src/ILCompiler.MetadataTransform/tests/ILCompiler.MetadataTransform.Tests.csproj +++ b/external/corert/src/ILCompiler.MetadataTransform/tests/ILCompiler.MetadataTransform.Tests.csproj @@ -1,34 +1,20 @@ - - + - Debug - AnyCPU - {B4B713D9-68A1-4EB3-8164-4DC8BE69BCBC} Library ILCompiler.MetadataTransform.Tests MetadataTransformTests - .NETStandard,Version=v1.3 - netcoreapp1.1 - - - - - + netstandard1.3 - - {1a9df196-43a9-44bb-b2c6-d62aa56b0e49} - ILCompiler.TypeSystem - - - {a965ea82-219d-48f7-ad51-bc030c16cc6f} - ILCompiler.MetadataTransform - - - {D66338D4-F9E4-4051-B302-232C6BFB6EF6} - ILCompiler.MetadataWriter - + + $(XunitNetcoreExtensionsVersion) + + + + + + false Content @@ -60,9 +46,6 @@ Build;DebugSymbolsProjectOutputGroup - - - @@ -73,4 +56,4 @@ - \ No newline at end of file + diff --git a/external/corert/src/ILCompiler.MetadataTransform/tests/ILMetadataAssembly/ILMetadataAssembly.ilproj b/external/corert/src/ILCompiler.MetadataTransform/tests/ILMetadataAssembly/ILMetadataAssembly.ilproj index f1001ee6ad..8e383e0670 100644 --- a/external/corert/src/ILCompiler.MetadataTransform/tests/ILMetadataAssembly/ILMetadataAssembly.ilproj +++ b/external/corert/src/ILCompiler.MetadataTransform/tests/ILMetadataAssembly/ILMetadataAssembly.ilproj @@ -3,17 +3,9 @@ - Debug - AnyCPU Library ILMetadataAssembly - {347E2A5A-54E5-4482-9723-ED460DE79CE8} - - - - - - + false diff --git a/external/corert/src/ILCompiler.MetadataTransform/tests/PrimaryMetadataAssembly/PrimaryMetadataAssembly.csproj b/external/corert/src/ILCompiler.MetadataTransform/tests/PrimaryMetadataAssembly/PrimaryMetadataAssembly.csproj index e9ccb27f8e..82b325adfc 100644 --- a/external/corert/src/ILCompiler.MetadataTransform/tests/PrimaryMetadataAssembly/PrimaryMetadataAssembly.csproj +++ b/external/corert/src/ILCompiler.MetadataTransform/tests/PrimaryMetadataAssembly/PrimaryMetadataAssembly.csproj @@ -1,29 +1,11 @@ - - + - Debug - AnyCPU Library PrimaryMetadataAssembly false true - - Portable - .NETPortable - v4.5 - Profile7 - .NET Portable Subset - false - {C29B7395-F925-4B0E-972D-187D2D4BFEC7} - - - - - + false diff --git a/external/corert/src/ILCompiler.MetadataTransform/tests/SampleMetadataAssembly/SampleMetadataAssembly.csproj b/external/corert/src/ILCompiler.MetadataTransform/tests/SampleMetadataAssembly/SampleMetadataAssembly.csproj index 5178033200..04ff2298d9 100644 --- a/external/corert/src/ILCompiler.MetadataTransform/tests/SampleMetadataAssembly/SampleMetadataAssembly.csproj +++ b/external/corert/src/ILCompiler.MetadataTransform/tests/SampleMetadataAssembly/SampleMetadataAssembly.csproj @@ -1,18 +1,10 @@ - - + - Debug - AnyCPU Library SampleMetadataAssembly false - {D29B7395-A925-5B0E-972D-387D2D4BFEC8} - - - - - + false diff --git a/external/corert/src/ILCompiler.MetadataTransform/tests/SampleWinRTMetadataAssembly/SampleWinRTMetadataAssembly.csproj b/external/corert/src/ILCompiler.MetadataTransform/tests/SampleWinRTMetadataAssembly/SampleWinRTMetadataAssembly.csproj index 28d819e739..5149824077 100644 --- a/external/corert/src/ILCompiler.MetadataTransform/tests/SampleWinRTMetadataAssembly/SampleWinRTMetadataAssembly.csproj +++ b/external/corert/src/ILCompiler.MetadataTransform/tests/SampleWinRTMetadataAssembly/SampleWinRTMetadataAssembly.csproj @@ -1,18 +1,10 @@ - - + - Debug - AnyCPU Library SampleWinRTMetadataAssembly false - {46CDD663-FCCC-4E74-901F-3D9D5A36A0D9} - - - - - + false diff --git a/external/corert/src/ILCompiler.MetadataTransform/tests/WindowsWinRTMetadataAssembly/WindowsWinRTMetadataAssembly.csproj b/external/corert/src/ILCompiler.MetadataTransform/tests/WindowsWinRTMetadataAssembly/WindowsWinRTMetadataAssembly.csproj index 765d5761f4..d6f36ace18 100644 --- a/external/corert/src/ILCompiler.MetadataTransform/tests/WindowsWinRTMetadataAssembly/WindowsWinRTMetadataAssembly.csproj +++ b/external/corert/src/ILCompiler.MetadataTransform/tests/WindowsWinRTMetadataAssembly/WindowsWinRTMetadataAssembly.csproj @@ -1,18 +1,10 @@ - - + - Debug - AnyCPU Library WindowsWinRTMetadataAssembly false - {19D0BAA8-8762-4D64-80AF-53D7A2BBC4AE} - - - - - + false diff --git a/external/corert/src/ILCompiler.MetadataTransform/tests/project.json b/external/corert/src/ILCompiler.MetadataTransform/tests/project.json deleted file mode 100644 index 0764ff3a58..0000000000 --- a/external/corert/src/ILCompiler.MetadataTransform/tests/project.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "dependencies": { - "NETStandard.Library": "1.6.1", - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Reflection.Metadata": "1.4.2", - "Microsoft.DotNet.BuildTools.TestSuite": "1.0.0-prerelease-00807-03", - "test-runtime": { - "target": "project", - "exclude": "compile" - } - }, - "frameworks": { - "netstandard1.3": { } - }, - "supports": { - "coreFx.Test.netcoreapp1.1": { } - } -} diff --git a/external/corert/src/ILCompiler.MetadataWriter/src/ILCompiler.MetadataWriter.csproj b/external/corert/src/ILCompiler.MetadataWriter/src/ILCompiler.MetadataWriter.csproj index 8450a683e9..9d6864fc95 100644 --- a/external/corert/src/ILCompiler.MetadataWriter/src/ILCompiler.MetadataWriter.csproj +++ b/external/corert/src/ILCompiler.MetadataWriter/src/ILCompiler.MetadataWriter.csproj @@ -1,32 +1,12 @@ - - + - {D66338D4-F9E4-4051-B302-232C6BFB6EF6} Library - Properties ILCompiler.MetadataWriter - true true $(DefineConstants);NATIVEFORMAT_PUBLICWRITER;NETFX_45 - .NETStandard,Version=v1.3 + netstandard1.3 - - - - - - - - - false - .NetCoreApp,Version=2.0 - - - - - - diff --git a/external/corert/src/ILCompiler.MetadataWriter/src/ILCompiler.MetadataWriter.targets b/external/corert/src/ILCompiler.MetadataWriter/src/ILCompiler.MetadataWriter.targets index 05ef3d97cd..85ec1a1f4f 100644 --- a/external/corert/src/ILCompiler.MetadataWriter/src/ILCompiler.MetadataWriter.targets +++ b/external/corert/src/ILCompiler.MetadataWriter/src/ILCompiler.MetadataWriter.targets @@ -1,5 +1,4 @@ - - + $(MSBuildThisFileDirectory)..\..\Common\src\ $(CommonSourcePath)Internal\NativeFormat @@ -33,4 +32,4 @@ - \ No newline at end of file + diff --git a/external/corert/src/ILCompiler.MetadataWriter/src/Internal/Metadata/NativeFormat/Writer/NativeFormatWriterGen.cs.REMOVED.git-id b/external/corert/src/ILCompiler.MetadataWriter/src/Internal/Metadata/NativeFormat/Writer/NativeFormatWriterGen.cs.REMOVED.git-id index 1f989323f0..8fd73e4b2e 100644 --- a/external/corert/src/ILCompiler.MetadataWriter/src/Internal/Metadata/NativeFormat/Writer/NativeFormatWriterGen.cs.REMOVED.git-id +++ b/external/corert/src/ILCompiler.MetadataWriter/src/Internal/Metadata/NativeFormat/Writer/NativeFormatWriterGen.cs.REMOVED.git-id @@ -1 +1 @@ -a1c8e11c7ee67d55f384f3f51e96f9017f096a24 \ No newline at end of file +d503e858e2d9a50147fcbccf8e82790bb64c2dfc \ No newline at end of file diff --git a/external/corert/src/ILCompiler.MetadataWriter/src/Internal/Metadata/NativeFormat/Writer/NativeMetadataWriter.cs b/external/corert/src/ILCompiler.MetadataWriter/src/Internal/Metadata/NativeFormat/Writer/NativeMetadataWriter.cs index 0dec3aad33..5f486ab6de 100644 --- a/external/corert/src/ILCompiler.MetadataWriter/src/Internal/Metadata/NativeFormat/Writer/NativeMetadataWriter.cs +++ b/external/corert/src/ILCompiler.MetadataWriter/src/Internal/Metadata/NativeFormat/Writer/NativeMetadataWriter.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#pragma warning disable 649 - using System; using System.IO; using System.Collections.Generic; @@ -567,7 +565,7 @@ namespace Internal.Metadata.NativeFormat.Writer /// public partial class ConstantStringValue { - public static explicit operator string (ConstantStringValue value) + public static explicit operator string(ConstantStringValue value) { if (value == null) return null; @@ -929,7 +927,7 @@ namespace Internal.Metadata.NativeFormat.Writer ReturnType.ToString(false), name + (GenericParameterCount == 0 ? "" : "`" + GenericParameterCount.ToString()) - + "(" + String.Join(", ", Parameters.Select(p => p.ToString(false))) + + + "(" + String.Join(", ", Parameters.Select(p => p.ToString(false))) + String.Join(", ", VarArgParameters.Select(p => p.ToString(false))) + ")"}.Where(e => !String.IsNullOrWhiteSpace(e))); } } @@ -1120,5 +1118,34 @@ namespace Internal.Metadata.NativeFormat.Writer return true; } } -} + // Distinguishes positive and negative zeros for float and double values + public static class CustomComparer + { + public static unsafe bool Equals(float x, float y) + { + return *(int*)&x == *(int*)&y; + } + + public static bool Equals(double x, double y) + { + return BitConverter.DoubleToInt64Bits(x) == BitConverter.DoubleToInt64Bits(y); + } + } + + public sealed class SingleComparer : IEqualityComparer + { + public static readonly SingleComparer Instance = new SingleComparer(); + + public bool Equals(float x, float y) => CustomComparer.Equals(x, y); + public int GetHashCode(float obj) => obj.GetHashCode(); + } + + public sealed class DoubleComparer : IEqualityComparer + { + public static readonly DoubleComparer Instance = new DoubleComparer(); + + public bool Equals(double x, double y) => CustomComparer.Equals(x, y); + public int GetHashCode(double obj) => obj.GetHashCode(); + } +} diff --git a/external/corert/src/ILCompiler.MetadataWriter/src/project.json b/external/corert/src/ILCompiler.MetadataWriter/src/project.json deleted file mode 100644 index 36a099f6b9..0000000000 --- a/external/corert/src/ILCompiler.MetadataWriter/src/project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "dependencies": { - "NETStandard.Library": "1.6.1" - }, - "frameworks": { - "netstandard1.3": {} - } -} diff --git a/external/corert/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj b/external/corert/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj index c842cd4b3f..d9de5b3c90 100644 --- a/external/corert/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj +++ b/external/corert/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj @@ -1,26 +1,22 @@ - - + - Debug - AnyCPU - {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49} Library Internal.TypeSystem ILCompiler.TypeSystem true - true - .NETStandard,Version=v1.3 - - - - + netstandard1.3 - - false - .NetCoreApp,Version=2.0 - + + 4.3.0 + + + 1.4.2 + + + 1.0.8 + @@ -32,6 +28,9 @@ TypeSystem\Canon\CanonTypes.cs + + TypeSystem\Canon\CanonTypes.Interop.cs + TypeSystem\Canon\CanonTypes.Sorting.cs @@ -80,6 +79,9 @@ TypeSystem\CodeGen\MethodDesc.CodeGen.cs + + TypeSystem\CodeGen\TargetDetails.CodeGen.cs + Utilities\AlignmentHelper.cs @@ -107,9 +109,21 @@ TypeSystem\Common\TypeSystemException.cs + + TypeSystem\Common\ThrowHelper.cs + + + TypeSystem\Common\ThrowHelper.Common.cs + + + Utilities\CustomAttributeTypeNameFormatter.cs + Utilities\CustomAttributeTypeNameParser.cs + + Utilities\DebugNameFormatter.cs + Utilities\GCPointerMap.Algorithm.cs @@ -143,6 +157,9 @@ TypeSystem\Common\FieldDesc.cs + + TypeSystem\Common\FieldDesc.ToString.cs + TypeSystem\Common\FieldDesc.FieldLayout.cs @@ -215,12 +232,18 @@ TypeSystem\Common\TypeSystemHelpers.cs - - TypeSystem\Common\TypeSystemConstaintsHelpers.cs + + TypeSystem\Common\TypeSystemConstraintsHelpers.cs TypeSystem\Common\UniversalCanonLayoutAlgorithm.cs + + Utilities\ExceptionTypeNameFormatter.cs + + + Utilities\ExceptionTypeNameFormatter.Metadata.cs + Utilities\TypeNameFormatter.cs @@ -233,12 +256,18 @@ TypeSystem\Common\MethodDesc.cs + + TypeSystem\Common\MethodDesc.ToString.cs + TypeSystem\Common\StandardVirtualMethodAlgorithm.cs TypeSystem\Common\TypeDesc.cs + + TypeSystem\Common\TypeDesc.ToString.cs + TypeSystem\Common\TypeDesc.Interfaces.cs @@ -371,15 +400,21 @@ IL\MethodILDebugView.cs - - IL\ILDisassember.cs + + IL\ILDisassembler.cs IL\InstantiatedMethodIL.cs + + IL\ILStackHelper.cs + IL\ILOpcode.cs + + IL\ILOpcodeHelper.cs + IL\TypeSystemContext.EnumMethods.cs @@ -410,6 +445,9 @@ IL\Stubs\ILEmitter.cs + + IL\Stubs\DebuggerSteppingHelpers.cs + IL\Stubs\DelegateMarshallingMethodThunk.cs @@ -503,6 +541,12 @@ TypeSystem\Common\LocalVariableDefinition.cs + + TypeSystem\CodeGen\INonEmittableType.cs + + + TypeSystem\CodeGen\NativeStructType.CodeGen.cs + TypeSystem\RuntimeDetermined\ArrayType.RuntimeDetermined.cs @@ -521,6 +565,12 @@ TypeSystem\RuntimeDetermined\PointerType.RuntimeDetermined.cs + + TypeSystem\RuntimeDetermined\MethodForRuntimeDeterminedType.cs + + + TypeSystem\RuntimeDetermined\MethodForRuntimeDeterminedType.Sorting.cs + TypeSystem\RuntimeDetermined\RuntimeDeterminedCanonicalizationAlgorithm.cs @@ -557,6 +607,9 @@ Common\System\FormattingHelpers.cs + + System\NotImplemented.cs + TypeSystem\Sorting\ArrayType.Sorting.cs @@ -584,9 +637,7 @@ TypeSystem\Sorting\TypeSystemComparer.cs - - - + - \ No newline at end of file + diff --git a/external/corert/src/ILCompiler.TypeSystem/src/Utilities/UniqueTypeNameFormatter.cs b/external/corert/src/ILCompiler.TypeSystem/src/Utilities/UniqueTypeNameFormatter.cs new file mode 100644 index 0000000000..62d4cc4112 --- /dev/null +++ b/external/corert/src/ILCompiler.TypeSystem/src/Utilities/UniqueTypeNameFormatter.cs @@ -0,0 +1,165 @@ +// 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.Text; + +namespace Internal.TypeSystem +{ + /// + /// Provides a name formatter attempts to produce unique names given a type. + /// The exact format of this type name provider is not considered stable or suited for parsing at this time. + /// + public class UniqueTypeNameFormatter : TypeNameFormatter + { + public static UniqueTypeNameFormatter Instance { get; } = new UniqueTypeNameFormatter(); + + public override void AppendName(StringBuilder sb, PointerType type) + { + AppendName(sb, type.ParameterType); + sb.Append('*'); + } + + public override void AppendName(StringBuilder sb, GenericParameterDesc type) + { + string prefix = type.Kind == GenericParameterKind.Type ? "!" : "!!"; + sb.Append(prefix); + sb.Append(type.Name); + } + + public override void AppendName(StringBuilder sb, SignatureTypeVariable type) + { + sb.Append("!"); + sb.Append(type.Index.ToStringInvariant()); + } + + public override void AppendName(StringBuilder sb, SignatureMethodVariable type) + { + sb.Append("!!"); + sb.Append(type.Index.ToStringInvariant()); + } + + public override void AppendName(StringBuilder sb, FunctionPointerType type) + { + MethodSignature signature = type.Signature; + + AppendName(sb, signature.ReturnType); + + sb.Append(" ("); + for (int i = 0; i < signature.Length; i++) + { + if (i > 0) + sb.Append(", "); + AppendName(sb, signature[i]); + } + + // TODO: Append '...' for vararg methods + + sb.Append(')'); + } + + public override void AppendName(StringBuilder sb, ByRefType type) + { + AppendName(sb, type.ParameterType); + sb.Append(" ByRef"); + } + + public override void AppendName(StringBuilder sb, ArrayType type) + { + AppendName(sb, type.ElementType); + sb.Append('['); + + if (type.Rank == 1 && type.IsMdArray) + sb.Append('*'); + sb.Append(',', type.Rank - 1); + + sb.Append(']'); + } + + protected override void AppendNameForInstantiatedType(StringBuilder sb, DefType type) + { + AppendName(sb, type.GetTypeDefinition()); + sb.Append('<'); + + for (int i = 0; i < type.Instantiation.Length; i++) + { + if (i > 0) + sb.Append(", "); + AppendName(sb, type.Instantiation[i]); + } + + sb.Append('>'); + } + + protected override void AppendNameForNamespaceType(StringBuilder sb, DefType type) + { + string ns = GetTypeNamespace(type); + if (ns.Length > 0) + { + AppendEscapedIdentifier(sb, ns); + sb.Append('.'); + } + AppendEscapedIdentifier(sb, GetTypeName(type)); + + if (type is MetadataType) + { + IAssemblyDesc homeAssembly = ((MetadataType)type).Module as IAssemblyDesc; + AppendAssemblyName(sb, homeAssembly); + } + } + + private void AppendAssemblyName(StringBuilder sb, IAssemblyDesc assembly) + { + if (assembly == null) + return; + + sb.Append(','); + AppendEscapedIdentifier(sb, assembly.GetName().Name); + } + + protected override void AppendNameForNestedType(StringBuilder sb, DefType nestedType, DefType containingType) + { + AppendNameForNamespaceType(sb, containingType); + + sb.Append('+'); + + string ns = GetTypeNamespace(nestedType); + if (ns.Length > 0) + { + AppendEscapedIdentifier(sb, ns); + sb.Append('.'); + } + AppendEscapedIdentifier(sb, GetTypeName(nestedType)); + } + + private string GetTypeName(DefType type) + { + return type.Name; + } + + private string GetTypeNamespace(DefType type) + { + return type.Namespace; + } + + private static char[] s_escapedChars = new char[] { ',', '=', '"', ']', '[', '*', '&', '+', '\\' }; + private void AppendEscapedIdentifier(StringBuilder sb, string identifier) + { + if (identifier.IndexOfAny(s_escapedChars) < 0) + { + string escapedIdentifier = identifier; + foreach (char escapedChar in s_escapedChars) + { + string escapedCharString = new string(escapedChar, 1); + escapedIdentifier = escapedIdentifier.Replace(escapedCharString, "\\" + escapedCharString); + } + sb.Append(escapedIdentifier); + } + else + { + sb.Append(identifier); + } + } + } +} diff --git a/external/corert/src/ILCompiler.TypeSystem/src/project.json b/external/corert/src/ILCompiler.TypeSystem/src/project.json deleted file mode 100644 index 1f6a16ec98..0000000000 --- a/external/corert/src/ILCompiler.TypeSystem/src/project.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "dependencies": { - "NETStandard.Library": "1.6.1", - "System.IO.MemoryMappedFiles": "4.3.0", - "System.Reflection.Metadata": "1.4.2", - "Microsoft.DiaSymReader": "1.0.8" - }, - "frameworks": { - "netstandard1.3": {} - } -} diff --git a/external/corert/src/ILCompiler.TypeSystem/tests/CastingTests.cs b/external/corert/src/ILCompiler.TypeSystem/tests/CastingTests.cs index 6e439984c3..1d81c81964 100644 --- a/external/corert/src/ILCompiler.TypeSystem/tests/CastingTests.cs +++ b/external/corert/src/ILCompiler.TypeSystem/tests/CastingTests.cs @@ -184,6 +184,13 @@ namespace TypeSystemTests Assert.True(stringSzArrayType.CanCastTo(iEnumerableOfObjectType)); Assert.False(stringSzArrayType.CanCastTo(iEnumerableOfExceptionType)); + + MetadataType iContravariantOfTType = _testModule.GetType("Casting", "IContravariant`1"); + InstantiatedType iContravariantOfObjectType = iContravariantOfTType.MakeInstantiatedType(objectType); + InstantiatedType iEnumerableOfStringType = iEnumerableOfTType.MakeInstantiatedType(stringType); + + Assert.True(iContravariantOfObjectType.CanCastTo(objectType)); + Assert.True(iEnumerableOfStringType.CanCastTo(objectType)); } [Fact] diff --git a/external/corert/src/ILCompiler.TypeSystem/tests/ConstraintsValidationTest.cs b/external/corert/src/ILCompiler.TypeSystem/tests/ConstraintsValidationTest.cs index 4691be685d..bcb985634b 100644 --- a/external/corert/src/ILCompiler.TypeSystem/tests/ConstraintsValidationTest.cs +++ b/external/corert/src/ILCompiler.TypeSystem/tests/ConstraintsValidationTest.cs @@ -33,8 +33,13 @@ namespace TypeSystemTests private MetadataType _complexGenericConstraint1Type; private MetadataType _complexGenericConstraint2Type; private MetadataType _complexGenericConstraint3Type; + private MetadataType _complexGenericConstraint4Type; private MetadataType _multipleConstraintsType; + private MetadataType _genericMethodsType; + private MethodDesc _simpleGenericConstraintMethod; + private MethodDesc _complexGenericConstraintMethod; + public ConstraintsValidationTest() { _context = new TestTypeSystemContext(TargetArchitecture.Unknown); @@ -64,13 +69,19 @@ namespace TypeSystemTests _complexGenericConstraint1Type = _testModule.GetType("GenericConstraints", "ComplexGenericConstraint1`2"); _complexGenericConstraint2Type = _testModule.GetType("GenericConstraints", "ComplexGenericConstraint2`2"); _complexGenericConstraint3Type = _testModule.GetType("GenericConstraints", "ComplexGenericConstraint3`2"); + _complexGenericConstraint4Type = _testModule.GetType("GenericConstraints", "ComplexGenericConstraint4`2"); _multipleConstraintsType = _testModule.GetType("GenericConstraints", "MultipleConstraints`2"); + + _genericMethodsType = _testModule.GetType("GenericConstraints", "GenericMethods"); + _simpleGenericConstraintMethod = _genericMethodsType.GetMethod("SimpleGenericConstraintMethod", null); + _complexGenericConstraintMethod = _genericMethodsType.GetMethod("ComplexGenericConstraintMethod", null); } [Fact] public void TestTypeConstraints() { - MetadataType instantiatedType; + TypeDesc instantiatedType; + MethodDesc instantiatedMethod; MetadataType arg2OfInt = _arg2Type.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Int32)); MetadataType arg2OfBool = _arg2Type.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Boolean)); @@ -134,6 +145,39 @@ namespace TypeSystemTests Assert.False(instantiatedType.CheckConstraints()); } + // Special constraints instantiated with generic parameter + { + instantiatedType = _referenceTypeConstraintType.MakeInstantiatedType(_referenceTypeConstraintType.Instantiation[0]); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _defaultConstructorConstraintType.MakeInstantiatedType(_defaultConstructorConstraintType.Instantiation[0]); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _notNullableValueTypeConstraintType.MakeInstantiatedType(_notNullableValueTypeConstraintType.Instantiation[0]); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _defaultConstructorConstraintType.MakeInstantiatedType(_notNullableValueTypeConstraintType.Instantiation[0]); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _referenceTypeConstraintType.MakeInstantiatedType(_arg2Type.Instantiation[0]); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _defaultConstructorConstraintType.MakeInstantiatedType(_arg2Type.Instantiation[0]); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _notNullableValueTypeConstraintType.MakeInstantiatedType(_arg2Type.Instantiation[0]); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _referenceTypeConstraintType.MakeInstantiatedType(_simpleTypeConstraintType.Instantiation[0]); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _defaultConstructorConstraintType.MakeInstantiatedType(_simpleTypeConstraintType.Instantiation[0]); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _notNullableValueTypeConstraintType.MakeInstantiatedType(_simpleTypeConstraintType.Instantiation[0]); + Assert.False(instantiatedType.CheckConstraints()); + } + // SimpleTypeConstraint and DoubleSimpleTypeConstraint foreach(var genType in new MetadataType[] { _simpleTypeConstraintType , _doubleSimpleTypeConstraintType }) { @@ -241,6 +285,36 @@ namespace TypeSystemTests Assert.True(instantiatedType.CheckConstraints()); } + // Constraints requiring InstantiationContext + { + // Instantiate type / method with own generic parameters + instantiatedType = _complexGenericConstraint3Type.MakeInstantiatedType(_complexGenericConstraint3Type.Instantiation[0], _complexGenericConstraint3Type.Instantiation[1]); + Assert.True(instantiatedType.CheckConstraints(new InstantiationContext(instantiatedType.Instantiation, default(Instantiation)))); + + instantiatedType = _complexGenericConstraint4Type.MakeInstantiatedType(_complexGenericConstraint4Type.Instantiation[0], _complexGenericConstraint4Type.Instantiation[1]); + Assert.True(instantiatedType.CheckConstraints(new InstantiationContext(instantiatedType.Instantiation, default(Instantiation)))); + + instantiatedMethod = _simpleGenericConstraintMethod.MakeInstantiatedMethod(_simpleGenericConstraintMethod.Instantiation); + Assert.True(instantiatedMethod.CheckConstraints(new InstantiationContext(default(Instantiation), instantiatedMethod.Instantiation))); + + instantiatedMethod = _complexGenericConstraintMethod.MakeInstantiatedMethod(_complexGenericConstraintMethod.Instantiation); + Assert.True(instantiatedMethod.CheckConstraints(new InstantiationContext(default(Instantiation), instantiatedMethod.Instantiation))); + + // Instantiate type with generic parameters of method + instantiatedType = _simpleGenericConstraintType.MakeInstantiatedType(_simpleGenericConstraintMethod.Instantiation); + Assert.True(instantiatedType.CheckConstraints(new InstantiationContext(default(Instantiation), _simpleGenericConstraintMethod.Instantiation))); + + instantiatedType = _complexGenericConstraint4Type.MakeInstantiatedType(_complexGenericConstraintMethod.Instantiation); + Assert.True(instantiatedType.CheckConstraints(new InstantiationContext(default(Instantiation), _complexGenericConstraintMethod.Instantiation))); + + // Instantiate method with generic parameters of type + instantiatedMethod = _simpleGenericConstraintMethod.MakeInstantiatedMethod(_simpleGenericConstraintType.Instantiation); + Assert.True(instantiatedMethod.CheckConstraints(new InstantiationContext(_simpleGenericConstraintType.Instantiation, default(Instantiation)))); + + instantiatedMethod = _complexGenericConstraintMethod.MakeInstantiatedMethod(_complexGenericConstraint4Type.Instantiation); + Assert.True(instantiatedMethod.CheckConstraints(new InstantiationContext(_complexGenericConstraint4Type.Instantiation, default(Instantiation)))); + } + // MultipleConstraints { // Violate the class constraint @@ -259,6 +333,27 @@ namespace TypeSystemTests instantiatedType = _multipleConstraintsType.MakeInstantiatedType(_classArgWithDefaultCtorType, _context.GetWellKnownType(WellKnownType.Object)); Assert.True(instantiatedType.CheckConstraints()); } + + // InvalidInstantiationArgs + { + var pointer = _context.GetWellKnownType(WellKnownType.Int16).MakePointerType(); + var byref = _context.GetWellKnownType(WellKnownType.Int16).MakeByRefType(); + + Assert.False(_iGenType.Instantiation.CheckValidInstantiationArguments()); + + instantiatedType = _iGenType.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Void)); + Assert.False(instantiatedType.Instantiation.CheckValidInstantiationArguments()); + + instantiatedType = _iGenType.MakeInstantiatedType(pointer); + Assert.False(instantiatedType.Instantiation.CheckValidInstantiationArguments()); + + instantiatedType = _iGenType.MakeInstantiatedType(byref); + Assert.False(instantiatedType.Instantiation.CheckValidInstantiationArguments()); + + instantiatedType = _iGenType.MakeInstantiatedType(byref); + instantiatedType = _iGenType.MakeInstantiatedType(instantiatedType); + Assert.False(instantiatedType.Instantiation.CheckValidInstantiationArguments()); + } } } } diff --git a/external/corert/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/CoreTestAssembly.csproj b/external/corert/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/CoreTestAssembly.csproj index 74380612af..447bf566f6 100644 --- a/external/corert/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/CoreTestAssembly.csproj +++ b/external/corert/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/CoreTestAssembly.csproj @@ -1,29 +1,11 @@ - - + - Debug - AnyCPU Library CoreTestAssembly false true - - Portable - .NETPortable - v4.5 - Profile7 - .NET Portable Subset - false - {5813B7DC-6588-4553-B04D-387EC9630AC8} - - - - - + false diff --git a/external/corert/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/GenericConstraints.cs b/external/corert/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/GenericConstraints.cs index 841a7873f0..8af3228393 100644 --- a/external/corert/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/GenericConstraints.cs +++ b/external/corert/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/GenericConstraints.cs @@ -59,5 +59,14 @@ namespace GenericConstraints public class ComplexGenericConstraint3 where T : IGen { } + public class ComplexGenericConstraint4 where T : U where U : IGen { } + public class MultipleConstraints where T : class, IGen, new() { } + + public class GenericMethods + { + public static void SimpleGenericConstraintMethod() where T : U { } + + public static void ComplexGenericConstraintMethod() where T : U where U : IGen { } + } } diff --git a/external/corert/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/InstanceFieldLayout.cs b/external/corert/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/InstanceFieldLayout.cs index 912d7e778c..64367e2332 100644 --- a/external/corert/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/InstanceFieldLayout.cs +++ b/external/corert/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/InstanceFieldLayout.cs @@ -148,29 +148,13 @@ namespace Sequential namespace IsByRefLike { - public struct ByRefLikeStruct + public ref struct ByRefLikeStruct { ByReference ByRef; } - public struct ComposedStruct - { - ByRefLikeStruct ByRefLike; - } - public struct NotByRefLike { int X; } - - public class Invalid - { - ByReference ByRef; - } - - public class ComposedInvalid - { - ByRefLikeStruct ByRefLike; - } } - diff --git a/external/corert/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/Platform.cs b/external/corert/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/Platform.cs index b15af240e1..e61a165cd6 100644 --- a/external/corert/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/Platform.cs +++ b/external/corert/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/Platform.cs @@ -67,13 +67,13 @@ namespace System public class Exception { } - public struct TypedReference + public ref struct TypedReference { private readonly ByReference _value; private readonly RuntimeTypeHandle _typeHandle; } - public struct ByReference { } + public ref struct ByReference { } } namespace System.Collections @@ -128,3 +128,9 @@ namespace System.Runtime.InteropServices } } +namespace System.Runtime.CompilerServices +{ + public sealed class IsByRefLikeAttribute : Attribute + { + } +} diff --git a/external/corert/src/ILCompiler.TypeSystem/tests/ILDisassemblerTests.cs b/external/corert/src/ILCompiler.TypeSystem/tests/ILDisassemblerTests.cs index f763fe8130..fc55923ae0 100644 --- a/external/corert/src/ILCompiler.TypeSystem/tests/ILDisassemblerTests.cs +++ b/external/corert/src/ILCompiler.TypeSystem/tests/ILDisassemblerTests.cs @@ -54,7 +54,7 @@ namespace TypeSystemTests { 50, "IL_0084: initobj ILDisassembler.TestStruct" } }; - ILDisassember disasm = new ILDisassember(methodIL); + ILDisassembler disasm = new ILDisassembler(methodIL); int numLines = 1; while (disasm.HasNextInstruction) diff --git a/external/corert/src/ILCompiler.TypeSystem/tests/ILTestAssembly/ILTestAssembly.ilproj b/external/corert/src/ILCompiler.TypeSystem/tests/ILTestAssembly/ILTestAssembly.ilproj index 8ce6d7d2ad..5d65099c49 100644 --- a/external/corert/src/ILCompiler.TypeSystem/tests/ILTestAssembly/ILTestAssembly.ilproj +++ b/external/corert/src/ILCompiler.TypeSystem/tests/ILTestAssembly/ILTestAssembly.ilproj @@ -1,19 +1,10 @@ - - + - Debug - AnyCPU Library ILTestAssembly - {ED3BECBB-EABE-4717-875D-C91E15FC260E} - - - - - - + false @@ -21,6 +12,7 @@ + diff --git a/external/corert/src/ILCompiler.TypeSystem/tests/ILTestAssembly/InstanceFieldLayout.il b/external/corert/src/ILCompiler.TypeSystem/tests/ILTestAssembly/InstanceFieldLayout.il new file mode 100644 index 0000000000..2a07e9e575 --- /dev/null +++ b/external/corert/src/ILCompiler.TypeSystem/tests/ILTestAssembly/InstanceFieldLayout.il @@ -0,0 +1,22 @@ +// 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. + +.class public sequential ansi sealed beforefieldinit IsByRefLike.InvalidStruct + extends [CoreTestAssembly]System.ValueType +{ + .field private valuetype [CoreTestAssembly]System.ByReference`1 ByRef +} + +.class public auto ansi beforefieldinit IsByRefLike.InvalidClass1 + extends [CoreTestAssembly]System.Object +{ + .field private valuetype [CoreTestAssembly]System.ByReference`1 ByRef +} + +.class public auto ansi beforefieldinit IsByRefLike.InvalidClass2 + extends [CoreTestAssembly]System.Object +{ + .custom instance void [CoreTestAssembly]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( 01 00 00 00 ) + .field private valuetype [CoreTestAssembly]System.ByReference`1 ByRef +} diff --git a/external/corert/src/ILCompiler.TypeSystem/tests/InstanceFieldLayoutTests.cs b/external/corert/src/ILCompiler.TypeSystem/tests/InstanceFieldLayoutTests.cs index ef65ceb5d7..d5945c0ced 100644 --- a/external/corert/src/ILCompiler.TypeSystem/tests/InstanceFieldLayoutTests.cs +++ b/external/corert/src/ILCompiler.TypeSystem/tests/InstanceFieldLayoutTests.cs @@ -2,11 +2,6 @@ // 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.Collections.Generic; -using System.Linq; -using System.Text; - using Internal.TypeSystem; using Xunit; @@ -17,6 +12,7 @@ namespace TypeSystemTests { TestTypeSystemContext _context; ModuleDesc _testModule; + ModuleDesc _ilTestModule; public InstanceFieldLayoutTests() { @@ -25,6 +21,7 @@ namespace TypeSystemTests _context.SetSystemModule(systemModule); _testModule = systemModule; + _ilTestModule = _context.CreateModuleForSimpleName("ILTestAssembly"); } [Fact] @@ -318,11 +315,6 @@ namespace TypeSystemTests Assert.True(type.IsByRefLike); } - { - DefType type = _testModule.GetType("IsByRefLike", "ComposedStruct"); - Assert.True(type.IsByRefLike); - } - { DefType type = _testModule.GetType("IsByRefLike", "NotByRefLike"); Assert.False(type.IsByRefLike); @@ -333,12 +325,17 @@ namespace TypeSystemTests public void TestInvalidByRefLikeTypes() { { - DefType type = _testModule.GetType("IsByRefLike", "Invalid"); + DefType type = _ilTestModule.GetType("IsByRefLike", "InvalidClass1"); Assert.Throws(() => type.ComputeInstanceLayout(InstanceLayoutKind.TypeAndFields)); } { - DefType type = _testModule.GetType("IsByRefLike", "ComposedInvalid"); + DefType type = _ilTestModule.GetType("IsByRefLike", "InvalidClass2"); + Assert.Throws(() => type.ComputeInstanceLayout(InstanceLayoutKind.TypeAndFields)); + } + + { + DefType type = _ilTestModule.GetType("IsByRefLike", "InvalidStruct"); Assert.Throws(() => type.ComputeInstanceLayout(InstanceLayoutKind.TypeAndFields)); } } diff --git a/external/corert/src/ILCompiler.TypeSystem/tests/SyntheticVirtualOverrideTests.cs b/external/corert/src/ILCompiler.TypeSystem/tests/SyntheticVirtualOverrideTests.cs index 66c387bf33..0058db8a60 100644 --- a/external/corert/src/ILCompiler.TypeSystem/tests/SyntheticVirtualOverrideTests.cs +++ b/external/corert/src/ILCompiler.TypeSystem/tests/SyntheticVirtualOverrideTests.cs @@ -226,11 +226,6 @@ namespace TypeSystemTests { return false; } - - public override string ToString() - { - return "[::Synthetic]" + _owningType.ToString() + "." + _name; - } } } } diff --git a/external/corert/src/ILCompiler.TypeSystem/tests/TypeNameParsingTests.cs b/external/corert/src/ILCompiler.TypeSystem/tests/TypeNameParsingTests.cs index b6cd357b45..392eb0bceb 100644 --- a/external/corert/src/ILCompiler.TypeSystem/tests/TypeNameParsingTests.cs +++ b/external/corert/src/ILCompiler.TypeSystem/tests/TypeNameParsingTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using Internal.TypeSystem; @@ -240,5 +241,59 @@ namespace TypeSystemTests Assert.Null(_testModule.GetTypeByCustomAttributeTypeName("TypeNameParsing.Simple+Nested+NonNamespaceQualifiedType", throwIfNotFound: false)); Assert.Null(_testModule.GetTypeByCustomAttributeTypeName("TypeNameParsing.Generic`1[TypeNameParsing.SimpleButNotThere]", throwIfNotFound: false)); } + + public IEnumerable GetTypesForRoundtripTest() + { + yield return _simpleType; + yield return _nestedType; + yield return _nestedTwiceType; + yield return _context.GetWellKnownType(WellKnownType.Int32); + yield return _veryGenericType; + yield return _simpleType.MakeArrayType(); + yield return _simpleType.MakeArrayType().MakeArrayType(); + yield return _simpleType.MakeArrayType(2).MakeArrayType(3); + yield return _context.GetWellKnownType(WellKnownType.Int32).MakeArrayType(); + yield return _structType.MakePointerType(); + yield return _context.GetWellKnownType(WellKnownType.Int32).MakePointerType().MakePointerType(); + yield return _genericType.MakeInstantiatedType(_simpleType); + yield return _veryGenericType.MakeInstantiatedType( + _simpleType, + _genericType.MakeInstantiatedType(_simpleType), + _structType + ); + yield return _genericType.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Object)); + yield return _veryGenericType.MakeInstantiatedType( + _context.GetWellKnownType(WellKnownType.Object), + _simpleType, + _context.GetWellKnownType(WellKnownType.Int32) + ); + yield return ((MetadataType)_context.GetWellKnownType(WellKnownType.Nullable)).MakeInstantiatedType(_structType); + yield return _genericType.MakeInstantiatedType(_structType.MakePointerType().MakeArrayType()); + yield return _nestedGenericType.MakeInstantiatedType( + ((MetadataType)_context.GetWellKnownType(WellKnownType.Nullable)).MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Int32)), + _nestedType.MakeArrayType() + ); + } + + [Fact] + public void TestRoundtripping() + { + foreach (TypeDesc type in GetTypesForRoundtripTest()) + { + { + var fmt = new CustomAttributeTypeNameFormatter((IAssemblyDesc)_testModule); + string formatted = fmt.FormatName(type, true); + TypeDesc roundTripped = _testModule.GetTypeByCustomAttributeTypeName(formatted); + Assert.Equal(type, roundTripped); + } + + { + var fmt = new CustomAttributeTypeNameFormatter(); + string formatted = fmt.FormatName(type, true); + TypeDesc roundTripped = _testModule.GetTypeByCustomAttributeTypeName(formatted); + Assert.Equal(type, roundTripped); + } + } + } } } diff --git a/external/corert/src/ILCompiler.TypeSystem/tests/TypeSystem.Tests.csproj b/external/corert/src/ILCompiler.TypeSystem/tests/TypeSystem.Tests.csproj index 52d227d49b..3c4e785164 100644 --- a/external/corert/src/ILCompiler.TypeSystem/tests/TypeSystem.Tests.csproj +++ b/external/corert/src/ILCompiler.TypeSystem/tests/TypeSystem.Tests.csproj @@ -1,26 +1,26 @@ - - + - Debug - AnyCPU - {8874CEEC-5594-511B-A44C-5CA9EC1CEB11} Library TypeSystem.Tests TypeSystem.Tests - .NETStandard,Version=v1.3 - netcoreapp1.1 - - - - - + netstandard1.3 + + $(NoWarn);NU1603 - - {dd5b6baa-d41a-4a6e-9e7d-83060f394b10} - ILCompiler.TypeSystem - + + $(XunitNetcoreExtensionsVersion) + + + $(XunitNetcoreExtensionsVersion) + + + 1.4.2 + + + + false @@ -57,8 +57,5 @@ - - - - \ No newline at end of file + diff --git a/external/corert/src/ILCompiler.TypeSystem/tests/project.json b/external/corert/src/ILCompiler.TypeSystem/tests/project.json deleted file mode 100644 index ea8222708f..0000000000 --- a/external/corert/src/ILCompiler.TypeSystem/tests/project.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "dependencies": { - "NETStandard.Library": "1.6.1", - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Reflection.Metadata": "1.4.2", - "Microsoft.xunit.netcore.extensions": "1.0.0-prerelease-00807-03", - "Microsoft.DotNet.BuildTools.TestSuite": "1.0.0-prerelease-00807-03", - "test-runtime": { - "target": "project", - "exclude": "compile" - } - }, - "frameworks": { - "netstandard1.3": { } - }, - "supports": { - "coreFx.Test.netcoreapp1.1": { } - } -} diff --git a/external/corert/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs b/external/corert/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs new file mode 100644 index 0000000000..c218dd4407 --- /dev/null +++ b/external/corert/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs @@ -0,0 +1,557 @@ +// 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.Diagnostics; +using System.Text; +using ILCompiler.Compiler.CppCodeGen; +using Internal.TypeSystem; +using LLVMSharp; +using ILCompiler.CodeGen; +using System.Collections.Generic; + +namespace Internal.IL +{ + /// + /// Abstraction of a variable size last-in-first-out (LIFO) collection of instances of the same specified type + /// implemented via an array. + /// + /// Type of elements in the stack. + internal class EvaluationStack + { + /// + /// Initializes a new instance of the stack that is empty and has the specified initial capacity . + /// + /// Initial number of elements that the stack can contain. + public EvaluationStack(int n) + { + Debug.Assert(n >= 0, "Count should be non-negative"); + + _stack = n > 0 ? new T[n] : s_emptyStack; + _top = 0; + + Debug.Assert(n == _stack.Length, "Stack length does not match requested capacity"); + Debug.Assert(_top == 0, "Top of stack is at bottom"); + } + + /// + /// Value for all stacks of length 0. + /// + private static readonly T[] s_emptyStack = new T[0]; + + /// + /// Storage for current stack. + /// + private T[] _stack; + + /// + /// Position in where next element will be pushed. + /// + private int _top; + + /// + /// Position in stack where next element will be pushed. + /// + public int Top + { + get { return _top; } + } + + /// + /// Number of elements contained in the stack. + /// + public int Length + { + get { return _top; } + } + + /// + /// Push at the top of the stack. + /// + /// Element to push onto the stack. + public void Push(T value) + { + if (_top >= _stack.Length) + { + Array.Resize(ref _stack, 2 * _top + 3); + } + _stack[_top++] = value; + } + + /// + /// Insert at position in current stack, shifting all + /// elements after or at by one. + /// + /// Element to insert + /// Position where to insert + public void InsertAt(T v, int pos) + { + Debug.Assert(pos <= _top, "Invalid insertion point"); + + if (_top >= _stack.Length) + { + Array.Resize(ref _stack, 2 * _top + 3); + } + for (int i = _top - 1; i >= pos; i--) + { + _stack[i + 1] = _stack[i]; + } + _top++; + _stack[pos] = v; + } + + /// + /// Access and set + /// + /// + /// + public T this[int index] + { + get + { + Debug.Assert(index >= 0 && index < _top, "Index not in range"); + return _stack[index]; + } + set + { + Debug.Assert(index >= 0 && index < _top, "Index not in range"); + _stack[index] = value; + } + } + + /// + /// Return element of the top of the stack. + /// + /// Element at the top of the stack + public T Peek() + { + if (_top <= 0) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + return _stack[_top - 1]; + } + + /// + /// Remove top element from stack and return it. + /// + /// Element formerly at the top of the stack + public T Pop() + { + if (_top <= 0) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + return _stack[--_top]; + } + + /// + /// Remove elements from the stack. + /// + /// Number of elements to remove from the stack + public void PopN(int n) + { + Debug.Assert(n <= _top, "Too many elements to remove"); + _top -= n; + } + + /// + /// Remove all elements from the stack. + /// + public void Clear() + { + _top = 0; + } + } + + /// + /// Abstract representation of a stack entry + /// + internal abstract class StackEntry + { + /// + /// Evaluation stack kind of the entry. + /// + public StackValueKind Kind { get; } + + /// + /// Managed type if any of the entry. + /// + public TypeDesc Type { get; } + + public LLVMValueRef ValueAsType(LLVMTypeRef type, LLVMBuilderRef builder) + { + return ValueAsTypeInternal(type, builder, false); + } + + public LLVMValueRef ValueAsType(TypeDesc type, LLVMBuilderRef builder) + { + return ValueAsType(ILImporter.GetLLVMTypeForTypeDesc(type), builder); + } + + public LLVMValueRef ValueForStackKind(StackValueKind kind, LLVMBuilderRef builder, bool signExtend) + { + if (kind == StackValueKind.Int32) + return ValueAsInt32(builder, signExtend); + else if (kind == StackValueKind.Int64) + return ValueAsInt64(builder, signExtend); + else if (kind == StackValueKind.Float) + return ValueAsType(LLVM.FloatType(), builder); + else if (kind == StackValueKind.NativeInt || kind == StackValueKind.ByRef || kind == StackValueKind.ObjRef) + return ValueAsInt32(builder, false); + else + throw new NotImplementedException(); + } + + public LLVMValueRef ValueAsInt32(LLVMBuilderRef builder, bool signExtend) + { + return ValueAsTypeInternal(LLVM.Int32Type(), builder, signExtend); + } + + public LLVMValueRef ValueAsInt64(LLVMBuilderRef builder, bool signExtend) + { + return ValueAsTypeInternal(LLVM.Int32Type(), builder, signExtend); + } + + protected abstract LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend); + + /// + /// Initializes a new instance of StackEntry. + /// + /// Kind of entry. + /// Type if any of entry. + protected StackEntry(StackValueKind kind, TypeDesc type = null) + { + Kind = kind; + Type = type; + } + + /// + /// Add representation of current entry in . + /// + /// Generation buffer used for appending new content. + //public abstract void Append(CppGenerationBuffer builder); + + /// + /// Create a new copy of current entry. + /// + /// A new instance of the same type as the current entry. + public abstract StackEntry Duplicate(); + } + + /// + /// Abstract entry for all constant values. + /// + internal abstract class ConstantEntry : StackEntry + { + protected ConstantEntry(StackValueKind kind, TypeDesc type = null) : base(kind, type) + { + } + + /// + /// Does current entry require a cast to be assigned to ? + /// + /// Type of destination + /// True if a cast is required + public virtual bool IsCastNecessary(TypeDesc destType) + { + return false; + } + } + + internal abstract class ConstantEntry : ConstantEntry where T : IConvertible + { + public T Value { get; } + + protected ConstantEntry(StackValueKind kind, T value, TypeDesc type = null) : base(kind, type) + { + Value = value; + } + } + + internal class Int32ConstantEntry : ConstantEntry + { + public Int32ConstantEntry(int value, TypeDesc type = null) : base(StackValueKind.Int32, value, type) + { + } + + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + if (type.TypeKind == LLVMTypeKind.LLVMPointerTypeKind && Value == 0) + { + return LLVM.ConstPointerNull(type); + } + else if (type.TypeKind == LLVMTypeKind.LLVMPointerTypeKind && Value != 0) + { + return LLVM.ConstIntToPtr(LLVM.ConstInt(LLVM.Int32Type(), (ulong)Value, LLVMMisc.False), type); + } + else if (type.TypeKind != LLVMTypeKind.LLVMIntegerTypeKind) + { + throw new NotImplementedException(); + } + else + { + return LLVM.ConstInt(type, (ulong)Value, LLVMMisc.False); + } + } + + public override StackEntry Duplicate() + { + return new Int32ConstantEntry(Value, Type); + } + + public override bool IsCastNecessary(TypeDesc destType) + { + switch (destType.UnderlyingType.Category) + { + case TypeFlags.SByte: + return Value >= sbyte.MaxValue || Value <= sbyte.MinValue; + case TypeFlags.Byte: + case TypeFlags.Boolean: + return Value >= byte.MaxValue || Value < 0; + case TypeFlags.Int16: + return Value >= short.MaxValue || Value <= short.MinValue; + case TypeFlags.UInt16: + case TypeFlags.Char: + return Value >= ushort.MaxValue || Value < 0; + case TypeFlags.Int32: + return false; + case TypeFlags.UInt32: + return Value < 0; + default: + return true; + } + } + } + + internal class Int64ConstantEntry : ConstantEntry + { + public Int64ConstantEntry(long value, TypeDesc type = null) : base(StackValueKind.Int64, value, type) + { + } + + public override StackEntry Duplicate() + { + return new Int64ConstantEntry(Value, Type); + } + + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + if (type.TypeKind == LLVMTypeKind.LLVMPointerTypeKind && Value == 0) + { + return LLVM.ConstPointerNull(type); + } + else if (type.TypeKind == LLVMTypeKind.LLVMPointerTypeKind && Value != 0) + { + return LLVM.ConstIntToPtr(LLVM.ConstInt(LLVM.Int64Type(), (ulong)Value, LLVMMisc.False), type); + } + else if (type.TypeKind != LLVMTypeKind.LLVMIntegerTypeKind) + { + throw new NotImplementedException(); + } + else + { + return LLVM.ConstInt(type, (ulong)Value, LLVMMisc.False); + } + } + + public override bool IsCastNecessary(TypeDesc destType) + { + switch (destType.UnderlyingType.Category) + { + case TypeFlags.SByte: + return Value >= sbyte.MaxValue || Value <= sbyte.MinValue; + case TypeFlags.Byte: + case TypeFlags.Boolean: + return Value >= byte.MaxValue || Value < 0; + case TypeFlags.Int16: + return Value >= short.MaxValue || Value <= short.MinValue; + case TypeFlags.UInt16: + case TypeFlags.Char: + return Value >= ushort.MaxValue || Value < 0; + case TypeFlags.Int32: + return Value >= int.MaxValue || Value <= int.MinValue; + case TypeFlags.UInt32: + return Value >= uint.MaxValue || Value < 0; + case TypeFlags.Int64: + return false; + case TypeFlags.UInt64: + return Value < 0; + default: + return true; + } + } + } + + internal class FloatConstantEntry : ConstantEntry + { + public FloatConstantEntry(double value, TypeDesc type = null) : base(StackValueKind.Float, value, type) + { + } + + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + return LLVM.ConstReal(type, Value); + } + + public override StackEntry Duplicate() + { + return new FloatConstantEntry(Value, Type); + } + } + + /// + /// Entry representing some expression + /// + internal class ExpressionEntry : StackEntry + { + /// + /// String representation of current expression + /// + public string Name { get; set; } + public LLVMValueRef RawLLVMValue { get; set; } + /// + /// Initializes new instance of ExpressionEntry + /// + /// Kind of entry + /// String representation of entry + /// Type if any of entry + public ExpressionEntry(StackValueKind kind, string name, LLVMValueRef llvmValue, TypeDesc type = null) : base(kind, type) + { + Name = name; + RawLLVMValue = llvmValue; + } + + public override StackEntry Duplicate() + { + return new ExpressionEntry(Kind, Name, RawLLVMValue, Type); + } + + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + //TODO: deal with sign extension here + return ILImporter.CastIfNecessary(builder, RawLLVMValue, type); + } + } + + internal class LoadExpressionEntry : ExpressionEntry + { + /// + /// Initializes new instance of ExpressionEntry + /// + /// Kind of entry + /// String representation of entry + /// Type if any of entry + public LoadExpressionEntry(StackValueKind kind, string name, LLVMValueRef llvmValue, TypeDesc type = null) : base(kind, name, llvmValue, type) + { + } + + public override StackEntry Duplicate() + { + return new LoadExpressionEntry(Kind, Name, RawLLVMValue, Type); + } + + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + return ILImporter.LoadValue(builder, RawLLVMValue, Type, type, signExtend); + } + } + + internal class AddressExpressionEntry : ExpressionEntry + { + /// + /// Initializes new instance of ExpressionEntry + /// + /// Kind of entry + /// String representation of entry + /// Type if any of entry + public AddressExpressionEntry(StackValueKind kind, string name, LLVMValueRef llvmValue, TypeDesc type = null) : base(kind, name, llvmValue, type) + { + } + + public override StackEntry Duplicate() + { + return new LoadExpressionEntry(Kind, Name, RawLLVMValue, Type); + } + + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + return ILImporter.CastIfNecessary(builder, RawLLVMValue, type); + } + } + + /// + /// Entry representing some token (either of TypeDesc, MethodDesc or FieldDesc) along with its string representation + /// + internal class LdTokenEntry : ExpressionEntry + { + public T LdToken { get; } + + public LdTokenEntry(StackValueKind kind, string name, T token, TypeDesc type = null) : base(kind, name, default(LLVMValueRef), type) + { + LdToken = token; + } + + public override StackEntry Duplicate() + { + return new LdTokenEntry(Kind, Name, LdToken, Type); + } + + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + if (RawLLVMValue.Pointer == IntPtr.Zero) + throw new NullReferenceException(); + + return ILImporter.CastIfNecessary(builder, RawLLVMValue, type); + } + } + + internal class InvalidEntry : StackEntry + { + /// + /// Entry to use to get an instance of InvalidEntry. + /// + public static InvalidEntry Entry = new InvalidEntry(); + + protected InvalidEntry() : base(StackValueKind.Unknown, null) + { + } + + public override StackEntry Duplicate() + { + return this; + } + + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + throw new InvalidOperationException(); + } + } + + /// + /// Entry representing a writable sharable stack entry that can survive from one basic block to another + /// + internal class SpilledExpressionEntry : ExpressionEntry + { + public int LocalIndex; + private ILImporter _importer; + public SpilledExpressionEntry(StackValueKind kind, string name, TypeDesc type, int localIndex, ILImporter importer) : base(kind, name, new LLVMValueRef(IntPtr.Zero), type) + { + LocalIndex = localIndex; + _importer = importer; + } + + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + return _importer.LoadTemp(LocalIndex, type); + } + + public override StackEntry Duplicate() + { + return new SpilledExpressionEntry(Kind, Name, Type, LocalIndex, _importer); + } + } +} diff --git a/external/corert/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs b/external/corert/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs new file mode 100644 index 0000000000..71da8d50a3 --- /dev/null +++ b/external/corert/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs @@ -0,0 +1,2134 @@ +// 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.Collections.Generic; +using System.Diagnostics; + +using Internal.TypeSystem; +using ILCompiler; +using LLVMSharp; +using ILCompiler.CodeGen; +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem.Ecma; + +namespace Internal.IL +{ + // Implements an IL scanner that scans method bodies to be compiled by the code generation + // backend before the actual compilation happens to gain insights into the code. + partial class ILImporter + { + public enum LocalVarKind + { + Argument, + Local, + Temp + } + + ArrayBuilder _dependencies = new ArrayBuilder(); + public IEnumerable GetDependencies() + { + return _dependencies.ToArray(); + } + + public LLVMModuleRef Module { get; } + private readonly MethodDesc _method; + private readonly MethodIL _methodIL; + private readonly MethodSignature _signature; + private readonly TypeDesc _thisType; + private readonly WebAssemblyCodegenCompilation _compilation; + private LLVMValueRef _llvmFunction; + private LLVMBasicBlockRef _curBasicBlock; + private LLVMBuilderRef _builder; + private readonly LocalVariableDefinition[] _locals; + private List _spilledExpressions = new List(); + + private readonly byte[] _ilBytes; + + /// + /// Stack of values pushed onto the IL stack: locals, arguments, values, function pointer, ... + /// + private EvaluationStack _stack = new EvaluationStack(0); + + private class BasicBlock + { + // Common fields + public enum ImportState : byte + { + Unmarked, + IsPending + } + + public BasicBlock Next; + + public int StartOffset; + public ImportState State = ImportState.Unmarked; + + public EvaluationStack EntryStack; + + public bool TryStart; + public bool FilterStart; + public bool HandlerStart; + + public LLVMBasicBlockRef Block; + } + + private class ExceptionRegion + { + public ILExceptionRegion ILRegion; + } + private ExceptionRegion[] _exceptionRegions; + + public ILImporter(WebAssemblyCodegenCompilation compilation, MethodDesc method, MethodIL methodIL, string mangledName) + { + Module = compilation.Module; + _compilation = compilation; + _method = method; + _methodIL = methodIL; + _ilBytes = methodIL.GetILBytes(); + _locals = methodIL.GetLocals(); + _signature = method.Signature; + _thisType = method.OwningType; + + var ilExceptionRegions = methodIL.GetExceptionRegions(); + _exceptionRegions = new ExceptionRegion[ilExceptionRegions.Length]; + for (int i = 0; i < ilExceptionRegions.Length; i++) + { + _exceptionRegions[i] = new ExceptionRegion() { ILRegion = ilExceptionRegions[i] }; + } + _llvmFunction = GetOrCreateLLVMFunction(mangledName); + _builder = LLVM.CreateBuilder(); + } + + public void Import() + { + FindBasicBlocks(); + + try + { + ImportBasicBlocks(); + } + catch + { + // Change the function body to trap + foreach (BasicBlock block in _basicBlocks) + { + if (block != null && block.Block.Pointer != IntPtr.Zero) + { + LLVM.DeleteBasicBlock(block.Block); + } + } + LLVMBasicBlockRef trapBlock = LLVM.AppendBasicBlock(_llvmFunction, "Trap"); + LLVM.PositionBuilderAtEnd(_builder, trapBlock); + EmitTrapCall(); + LLVM.BuildRetVoid(_builder); + throw; + } + finally + { + // Generate thunk for runtime exports + if (_method.IsRuntimeExport) + { + EmitNativeToManagedThunk(_compilation, _method, ((EcmaMethod)_method).GetRuntimeExportName(), _llvmFunction); + } + } + } + + private void GenerateProlog() + { + int totalLocalSize = 0; + foreach(LocalVariableDefinition local in _locals) + { + int localSize = local.Type.GetElementSize().AsInt; + totalLocalSize += localSize; + } + + var sp = LLVM.GetFirstParam(_llvmFunction); + int paramOffset = GetTotalParameterOffset(); + for (int i = 0; i < totalLocalSize; i++) + { + var stackOffset = LLVM.BuildGEP(_builder, sp, new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (ulong)(paramOffset + i), LLVMMisc.False) }, String.Empty); + LLVM.BuildStore(_builder, LLVM.ConstInt(LLVM.Int8Type(), 0, LLVMMisc.False), stackOffset); + } + } + + private LLVMValueRef CreateLLVMFunction(string mangledName) + { + LLVMTypeRef universalSignature = LLVM.FunctionType(LLVM.VoidType(), new LLVMTypeRef[] { LLVM.PointerType(LLVM.Int8Type(), 0), LLVM.PointerType(LLVM.Int8Type(), 0) }, false); + return LLVM.AddFunction(Module, mangledName , universalSignature); + } + + private LLVMValueRef GetOrCreateLLVMFunction(string mangledName) + { + LLVMValueRef llvmFunction = LLVM.GetNamedFunction(Module, mangledName); + + if(llvmFunction.Pointer == IntPtr.Zero) + { + return CreateLLVMFunction(mangledName); + } + return llvmFunction; + } + + private LLVMValueRef GetOrCreateLLVMFunction(string mangledName, LLVMTypeRef functionType) + { + LLVMValueRef llvmFunction = LLVM.GetNamedFunction(Module, mangledName); + + if (llvmFunction.Pointer == IntPtr.Zero) + { + return LLVM.AddFunction(Module, mangledName, functionType); + } + return llvmFunction; + } + + private void ImportCallMemset(LLVMValueRef targetPointer, byte value, int length) + { + LLVMValueRef objectSizeValue = BuildConstInt32(length); + var memsetSignature = LLVM.FunctionType(LLVM.VoidType(), new LLVMTypeRef[] { LLVM.PointerType(LLVM.Int8Type(), 0), LLVM.Int8Type(), LLVM.Int32Type(), LLVM.Int32Type(), LLVM.Int1Type() }, false); + LLVM.BuildCall(_builder, GetOrCreateLLVMFunction("llvm.memset.p0i8.i32", memsetSignature), new LLVMValueRef[] { targetPointer, BuildConstInt8(value), objectSizeValue, BuildConstInt32(1), BuildConstInt1(0) }, String.Empty); + } + + private void PushLoadExpression(StackValueKind kind, string name, LLVMValueRef rawLLVMValue, TypeDesc type) + { + Debug.Assert(kind != StackValueKind.Unknown, "Unknown stack kind"); + _stack.Push(new LoadExpressionEntry(kind, name, rawLLVMValue, type)); + } + + /// + /// Push an expression named of kind . + /// + /// Kind of entry in stack + /// Variable to be pushed + /// Type if any of + private void PushExpression(StackValueKind kind, string name, LLVMValueRef llvmValue, TypeDesc type = null) + { + Debug.Assert(kind != StackValueKind.Unknown, "Unknown stack kind"); + + switch (kind) + { + case StackValueKind.Int32: + { + if (!type.IsWellKnownType(WellKnownType.Int32) + && !type.IsWellKnownType(WellKnownType.IntPtr) + && !type.IsWellKnownType(WellKnownType.UInt32) + && !type.IsWellKnownType(WellKnownType.UIntPtr)) + { + llvmValue = LLVM.BuildIntCast(_builder, llvmValue, LLVM.Int32Type(), ""); + } + } + break; + + case StackValueKind.Int64: + { + if (!type.IsWellKnownType(WellKnownType.Int64) + && !(type.IsWellKnownType(WellKnownType.UInt64))) + { + llvmValue = LLVM.BuildIntCast(_builder, llvmValue, LLVM.Int64Type(), ""); + } + } + break; + + case StackValueKind.NativeInt: + break; + } + + _stack.Push(new ExpressionEntry(kind, name, llvmValue, type)); + } + + private void MarkInstructionBoundary() + { + } + + private LLVMBasicBlockRef GetLLVMBasicBlockForBlock(BasicBlock block) + { + if (block.Block.Pointer == IntPtr.Zero) + { + block.Block = LLVM.AppendBasicBlock(_llvmFunction, "Block" + block.StartOffset); + } + return block.Block; + } + + private void StartImportingBasicBlock(BasicBlock basicBlock) + { + _stack.Clear(); + + EvaluationStack entryStack = basicBlock.EntryStack; + if (entryStack != null) + { + int n = entryStack.Length; + for (int i = 0; i < n; i++) + { + _stack.Push(entryStack[i].Duplicate()); + } + } + + bool isFirstBlock = false; + if(_curBasicBlock.Equals(default(LLVMBasicBlockRef))) + { + isFirstBlock = true; + } + _curBasicBlock = GetLLVMBasicBlockForBlock(basicBlock); + + LLVM.PositionBuilderAtEnd(_builder, _curBasicBlock); + + if(isFirstBlock) + { + GenerateProlog(); + } + } + + private void EndImportingBasicBlock(BasicBlock basicBlock) + { + var terminator = basicBlock.Block.GetBasicBlockTerminator(); + if (terminator.Pointer == IntPtr.Zero) + { + if (_basicBlocks.Length > _currentOffset) + { + if (_basicBlocks[_currentOffset].StartOffset == 0) + throw new InvalidProgramException(); + + LLVM.BuildBr(_builder, GetLLVMBasicBlockForBlock(_basicBlocks[_currentOffset])); + } + else + { + LLVM.BuildRet(_builder, default(LLVMValueRef)); + } + } + } + + private void StartImportingInstruction() + { + } + + private void EndImportingInstruction() + { + } + + private void ImportNop() + { + EmitDoNothingCall(); + } + + private void ImportBreak() + { + } + + private void ImportLoadVar(int index, bool argument) + { + LLVMValueRef typedLoadLocation = LoadVarAddress(index, argument ? LocalVarKind.Argument : LocalVarKind.Local, out TypeDesc type); + PushLoadExpression(GetStackValueKind(type), "ld" + (argument ? "arg" : "loc") + index + "_", typedLoadLocation, type); + } + + private LLVMValueRef LoadTemp(int index) + { + LLVMValueRef address = LoadVarAddress(index, LocalVarKind.Temp, out TypeDesc type); + return LLVM.BuildLoad(_builder, CastToPointerToTypeDesc(address, type), "ldtemp"); + } + + internal LLVMValueRef LoadTemp(int index, LLVMTypeRef asType) + { + LLVMValueRef address = LoadVarAddress(index, LocalVarKind.Temp, out TypeDesc type); + return LLVM.BuildLoad(_builder, CastIfNecessary(address, LLVM.PointerType(asType, 0)), "ldtemp"); + } + + private void StoreTemp(int index, LLVMValueRef value) + { + LLVMValueRef address = LoadVarAddress(index, LocalVarKind.Temp, out TypeDesc type); + LLVM.BuildStore(_builder, CastToTypeDesc(value, type), CastToPointerToTypeDesc(address, type)); + } + + internal static LLVMValueRef LoadValue(LLVMBuilderRef builder, LLVMValueRef address, TypeDesc sourceType, LLVMTypeRef targetType, bool signExtend) + { + if (targetType.TypeKind == LLVMTypeKind.LLVMIntegerTypeKind && sourceType.IsPrimitive && !sourceType.IsPointer) + { + var sourceLLVMType = ILImporter.GetLLVMTypeForTypeDesc(sourceType); + var typedAddress = CastIfNecessary(builder, address, LLVM.PointerType(sourceLLVMType, 0)); + return CastIntValue(builder, LLVM.BuildLoad(builder, typedAddress, "ldvalue"), targetType, signExtend); + } + else + { + var typedAddress = CastIfNecessary(builder, address, LLVM.PointerType(targetType, 0)); + return LLVM.BuildLoad(builder, typedAddress, "ldvalue"); + } + } + + private static LLVMValueRef CastIntValue(LLVMBuilderRef builder, LLVMValueRef value, LLVMTypeRef type, bool signExtend) + { + LLVMTypeKind typeKind = LLVM.TypeOf(value).TypeKind; + if (LLVM.TypeOf(value).Pointer == type.Pointer) + { + return value; + } + else if (typeKind == LLVMTypeKind.LLVMPointerTypeKind) + { + return LLVM.BuildPtrToInt(builder, value, type, "intcast"); + } + else if (typeKind == LLVMTypeKind.LLVMFloatTypeKind || typeKind == LLVMTypeKind.LLVMDoubleTypeKind) + { + if (signExtend) + { + return LLVM.BuildFPToSI(builder, value, type, "fptosi"); + } + else + { + return LLVM.BuildFPToUI(builder, value, type, "fptoui"); + } + } + else if (signExtend && type.GetIntTypeWidth() > LLVM.TypeOf(value).GetIntTypeWidth()) + { + return LLVM.BuildSExtOrBitCast(builder, value, type, "SExtOrBitCast"); + } + else + { + Debug.Assert(typeKind == LLVMTypeKind.LLVMIntegerTypeKind); + return LLVM.BuildIntCast(builder, value, type, "intcast"); + } + } + + private LLVMValueRef LoadVarAddress(int index, LocalVarKind kind, out TypeDesc type) + { + int varBase; + int varCountBase; + int varOffset; + LLVMTypeRef valueType; + + if (kind == LocalVarKind.Argument) + { + varCountBase = 0; + varBase = 0; + if (!_signature.IsStatic) + { + varCountBase = 1; + } + + GetArgSizeAndOffsetAtIndex(index, out int argSize, out varOffset); + + if (!_signature.IsStatic && index == 0) + { + type = _thisType; + if (type.IsValueType) + { + type = type.MakeByRefType(); + } + } + else + { + type = _signature[index - varCountBase]; + } + valueType = GetLLVMTypeForTypeDesc(type); + } + else if (kind == LocalVarKind.Local) + { + varBase = GetTotalParameterOffset(); + GetLocalSizeAndOffsetAtIndex(index, out int localSize, out varOffset); + valueType = GetLLVMTypeForTypeDesc(_locals[index].Type); + type = _locals[index].Type; + } + else + { + varBase = GetTotalRealLocalOffset(); + GetSpillSizeAndOffsetAtIndex(index, out int localSize, out varOffset); + valueType = GetLLVMTypeForTypeDesc(_spilledExpressions[index].Type); + type = _spilledExpressions[index].Type; + } + + return LLVM.BuildGEP(_builder, LLVM.GetFirstParam(_llvmFunction), + new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (uint)(varBase + varOffset), LLVMMisc.False) }, + String.Empty); + + } + + private StackValueKind GetStackValueKind(TypeDesc type) + { + switch (type.Category) + { + case TypeFlags.Boolean: + case TypeFlags.Char: + case TypeFlags.SByte: + case TypeFlags.Byte: + case TypeFlags.Int16: + case TypeFlags.UInt16: + case TypeFlags.Int32: + case TypeFlags.UInt32: + return StackValueKind.Int32; + case TypeFlags.Int64: + case TypeFlags.UInt64: + return StackValueKind.Int64; + case TypeFlags.Single: + case TypeFlags.Double: + return StackValueKind.Float; + case TypeFlags.IntPtr: + case TypeFlags.UIntPtr: + return StackValueKind.NativeInt; + case TypeFlags.ValueType: + case TypeFlags.Nullable: + return StackValueKind.ValueType; + case TypeFlags.Enum: + return GetStackValueKind(type.UnderlyingType); + case TypeFlags.Class: + case TypeFlags.Interface: + case TypeFlags.Array: + case TypeFlags.SzArray: + return StackValueKind.ObjRef; + case TypeFlags.ByRef: + return StackValueKind.ByRef; + case TypeFlags.Pointer: + return StackValueKind.NativeInt; + default: + return StackValueKind.Unknown; + } + } + + private void ImportStoreVar(int index, bool argument) + { + TypeDesc varType; + StackEntry toStore = _stack.Pop(); + LLVMValueRef varAddress = LoadVarAddress(index, argument ? LocalVarKind.Argument : LocalVarKind.Local, out varType); + CastingStore(varAddress, toStore, varType); + } + + private void ImportStoreHelper(LLVMValueRef toStore, LLVMTypeRef valueType, LLVMValueRef basePtr, uint offset) + { + LLVMValueRef typedToStore = CastIfNecessary(toStore, valueType); + + var storeLocation = LLVM.BuildGEP(_builder, basePtr, + new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), offset, LLVMMisc.False) }, + String.Empty); + var typedStoreLocation = CastIfNecessary(storeLocation, LLVM.PointerType(valueType, 0)); + LLVM.BuildStore(_builder, typedToStore, typedStoreLocation); + } + + private LLVMValueRef CastToRawPointer(LLVMValueRef source) + { + return CastIfNecessary(source, LLVM.PointerType(LLVM.Int8Type(), 0)); + } + + private LLVMValueRef CastToTypeDesc(LLVMValueRef source, TypeDesc type) + { + return CastIfNecessary(source, GetLLVMTypeForTypeDesc(type)); + } + + private LLVMValueRef CastToPointerToTypeDesc(LLVMValueRef source, TypeDesc type) + { + return CastIfNecessary(source, LLVM.PointerType(GetLLVMTypeForTypeDesc(type), 0)); + } + + private void CastingStore(LLVMValueRef address, StackEntry value, TypeDesc targetType) + { + var typedStoreLocation = CastToPointerToTypeDesc(address, targetType); + LLVM.BuildStore(_builder, value.ValueAsType(targetType, _builder), typedStoreLocation); + } + + private LLVMValueRef CastIfNecessary(LLVMValueRef source, LLVMTypeRef valueType) + { + return CastIfNecessary(_builder, source, valueType); + } + + internal static LLVMValueRef CastIfNecessary(LLVMBuilderRef builder, LLVMValueRef source, LLVMTypeRef valueType) + { + LLVMTypeRef sourceType = LLVM.TypeOf(source); + if (sourceType.Pointer == valueType.Pointer) + return source; + + LLVMTypeKind toStoreKind = LLVM.GetTypeKind(LLVM.TypeOf(source)); + LLVMTypeKind valueTypeKind = LLVM.GetTypeKind(valueType); + + LLVMValueRef typedToStore = source; + if (toStoreKind == LLVMTypeKind.LLVMPointerTypeKind && valueTypeKind == LLVMTypeKind.LLVMPointerTypeKind) + { + typedToStore = LLVM.BuildPointerCast(builder, source, valueType, "CastIfNecessaryPtr"); + } + else if (toStoreKind == LLVMTypeKind.LLVMPointerTypeKind && valueTypeKind == LLVMTypeKind.LLVMIntegerTypeKind) + { + typedToStore = LLVM.BuildPtrToInt(builder, source, valueType, "CastIfNecessaryInt"); + } + else if (toStoreKind == LLVMTypeKind.LLVMIntegerTypeKind && valueTypeKind == LLVMTypeKind.LLVMArrayTypeKind) + { + typedToStore = LLVM.BuildLoad(builder, CastIfNecessary(builder, source, LLVM.PointerType(valueType, 0)), "CastIfNecessaryArrayLoad"); + } + else if (toStoreKind == LLVMTypeKind.LLVMPointerTypeKind && valueTypeKind == LLVMTypeKind.LLVMArrayTypeKind) + { + typedToStore = LLVM.BuildLoad(builder, CastIfNecessary(builder, source, LLVM.PointerType(valueType, 0)), "CastIfNecessaryArrayLoad"); + } + else if (toStoreKind == LLVMTypeKind.LLVMPointerTypeKind && valueTypeKind != LLVMTypeKind.LLVMIntegerTypeKind) + { + throw new NotImplementedException($"trying to cast {toStoreKind} to {valueTypeKind}"); + } + else if (toStoreKind == LLVMTypeKind.LLVMIntegerTypeKind && valueTypeKind == LLVMTypeKind.LLVMPointerTypeKind) + { + typedToStore = LLVM.BuildIntToPtr(builder, source, valueType, "CastIfNecessaryPtr"); + } + else if (toStoreKind != LLVMTypeKind.LLVMIntegerTypeKind && valueTypeKind == LLVMTypeKind.LLVMPointerTypeKind) + { + throw new NotImplementedException($"trying to cast {toStoreKind} to {valueTypeKind}"); + } + else if (toStoreKind != valueTypeKind && toStoreKind != LLVMTypeKind.LLVMIntegerTypeKind && valueTypeKind != LLVMTypeKind.LLVMIntegerTypeKind) + { + throw new NotImplementedException($"trying to cast {toStoreKind} to {valueTypeKind}"); + } + else if (toStoreKind == valueTypeKind && toStoreKind == LLVMTypeKind.LLVMIntegerTypeKind) + { + Debug.Assert(toStoreKind != LLVMTypeKind.LLVMPointerTypeKind && valueTypeKind != LLVMTypeKind.LLVMPointerTypeKind); + typedToStore = LLVM.BuildIntCast(builder, source, valueType, "CastIfNecessaryInt"); + } + else if (toStoreKind != LLVMTypeKind.LLVMFloatTypeKind && valueTypeKind == LLVMTypeKind.LLVMFloatTypeKind) + { + typedToStore = LLVM.BuildFPCast(builder, source, valueType, "CastIfNecessaryFloat"); + } + else if ((toStoreKind == LLVMTypeKind.LLVMDoubleTypeKind || toStoreKind == LLVMTypeKind.LLVMFloatTypeKind) && + valueTypeKind == LLVMTypeKind.LLVMIntegerTypeKind) + { + //TODO: keep track of the TypeDesc so we can call BuildFPToUI when the integer is unsigned + typedToStore = LLVM.BuildFPToSI(builder, source, valueType, "CastIfNecessaryFloat"); + } + else if (toStoreKind == LLVMTypeKind.LLVMIntegerTypeKind && valueTypeKind == LLVMTypeKind.LLVMDoubleTypeKind) + { + throw new NotImplementedException(); + } + + return typedToStore; + } + + internal static LLVMTypeRef GetLLVMTypeForTypeDesc(TypeDesc type) + { + switch (type.Category) + { + case TypeFlags.Boolean: + return LLVM.Int1Type(); + + case TypeFlags.SByte: + case TypeFlags.Byte: + return LLVM.Int8Type(); + + case TypeFlags.Int16: + case TypeFlags.UInt16: + case TypeFlags.Char: + return LLVM.Int16Type(); + + case TypeFlags.Int32: + case TypeFlags.UInt32: + return LLVM.Int32Type(); + case TypeFlags.IntPtr: + case TypeFlags.UIntPtr: + case TypeFlags.Array: + case TypeFlags.SzArray: + case TypeFlags.ByRef: + case TypeFlags.Class: + case TypeFlags.Interface: + return LLVM.PointerType(LLVM.Int8Type(), 0); + + case TypeFlags.Pointer: + return LLVM.PointerType(type.GetParameterType().IsVoid ? LLVM.Int8Type() : GetLLVMTypeForTypeDesc(type.GetParameterType()), 0); + + case TypeFlags.Int64: + case TypeFlags.UInt64: + return LLVM.Int64Type(); + + case TypeFlags.Single: + return LLVM.FloatType(); + + case TypeFlags.Double: + return LLVM.DoubleType(); + + case TypeFlags.ValueType: + case TypeFlags.Nullable: + return LLVM.ArrayType(LLVM.Int8Type(), (uint)type.GetElementSize().AsInt); + + case TypeFlags.Enum: + return GetLLVMTypeForTypeDesc(type.UnderlyingType); + + case TypeFlags.Void: + return LLVM.VoidType(); + + default: + throw new NotImplementedException(type.Category.ToString()); + } + } + + private int GetTotalLocalOffset() + { + int offset = GetTotalRealLocalOffset(); + for (int i = 0; i < _spilledExpressions.Count; i++) + { + offset += _spilledExpressions[i].Type.GetElementSize().AsInt; + } + return offset; + } + + private int GetTotalRealLocalOffset() + { + int offset = 0; + for (int i = 0; i < _locals.Length; i++) + { + offset += _locals[i].Type.GetElementSize().AsInt; + } + return offset; + } + + private int GetTotalParameterOffset() + { + int offset = 0; + for (int i = 0; i < _signature.Length; i++) + { + offset += _signature[i].GetElementSize().AsInt; + } + if (!_signature.IsStatic) + { + // If this is a struct, then it's a pointer on the stack + if (_thisType.IsValueType) + { + offset += _thisType.Context.Target.PointerSize; + } + else + { + offset += _thisType.GetElementSize().AsInt; + } + } + + return offset; + } + + private void GetArgSizeAndOffsetAtIndex(int index, out int size, out int offset) + { + int thisSize = 0; + if (!_signature.IsStatic) + { + thisSize = _thisType.IsValueType ? _thisType.Context.Target.PointerSize : _thisType.GetElementSize().AsInt; + if (index == 0) + { + size = thisSize; + offset = 0; + return; + } + else + { + index--; + } + } + + var argType = _signature[index]; + size = argType.GetElementSize().AsInt; + + offset = thisSize; + for (int i = 0; i < index; i++) + { + offset += _signature[i].GetElementSize().AsInt; + } + } + + private void GetLocalSizeAndOffsetAtIndex(int index, out int size, out int offset) + { + LocalVariableDefinition local = _locals[index]; + size = local.Type.GetElementSize().AsInt; + + offset = 0; + for (int i = 0; i < index; i++) + { + offset += _locals[i].Type.GetElementSize().AsInt; + } + } + + private void GetSpillSizeAndOffsetAtIndex(int index, out int size, out int offset) + { + SpilledExpressionEntry spill = _spilledExpressions[index]; + size = spill.Type.GetElementSize().AsInt; + + offset = 0; + for (int i = 0; i < index; i++) + { + offset += _spilledExpressions[i].Type.GetElementSize().AsInt; + } + } + + private void ImportAddressOfVar(int index, bool argument) + { + TypeDesc type; + LLVMValueRef typedLoadLocation = LoadVarAddress(index, argument ? LocalVarKind.Argument : LocalVarKind.Local, out type); + _stack.Push(new AddressExpressionEntry(StackValueKind.ByRef, "ldloca", typedLoadLocation, type.MakePointerType())); + } + + private void ImportDup() + { + _stack.Push(_stack.Peek().Duplicate()); + } + + private void ImportPop() + { + _stack.Pop(); + } + + private void ImportJmp(int token) + { + throw new NotImplementedException("jmp"); + } + + private void ImportCasting(ILOpcode opcode, int token) + { + } + + private void ImportLoadNull() + { + _stack.Push(new ExpressionEntry(StackValueKind.ObjRef, "null", LLVM.ConstInt(LLVM.Int32Type(), 0, LLVMMisc.False))); + } + + private void ImportReturn() + { + if (_signature.ReturnType != GetWellKnownType(WellKnownType.Void)) + { + StackEntry retVal = _stack.Pop(); + LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(_signature.ReturnType); + ImportStoreHelper(retVal.ValueAsType(valueType, _builder), valueType, LLVM.GetNextParam(LLVM.GetFirstParam(_llvmFunction)), 0); + } + + LLVM.BuildRetVoid(_builder); + } + + private void ImportCall(ILOpcode opcode, int token) + { + MethodDesc callee = (MethodDesc)_methodIL.GetObject(token); + if (callee.IsIntrinsic) + { + if (ImportIntrinsicCall(callee)) + { + return; + } + } + + if (callee.IsPInvoke) + { + ImportRawPInvoke(callee); + return; + } + + if (opcode == ILOpcode.newobj) + { + if (callee.OwningType.IsString) + { + // String constructors actually look like regular method calls + IMethodNode node = _compilation.NodeFactory.StringAllocator(callee); + _dependencies.Add(node); + callee = node.Method; + opcode = ILOpcode.call; + } + else + { + StackEntry newObjResult = AllocateObject(callee.OwningType); + //one for the real result and one to be consumed by ctor + if (callee.Signature.Length > _stack.Length) //System.Reflection.MemberFilter.ctor + throw new InvalidProgramException(); + _stack.InsertAt(newObjResult, _stack.Top - callee.Signature.Length); + _stack.InsertAt(newObjResult, _stack.Top - callee.Signature.Length); + } + } + + // we don't really have virtual call support, but we'll treat it as direct for now + if (opcode != ILOpcode.call && opcode != ILOpcode.callvirt && opcode != ILOpcode.newobj) + { + throw new NotImplementedException(); + } + if (opcode == ILOpcode.callvirt && callee.IsAbstract) + { + throw new NotImplementedException(); + } + + if (callee.OwningType.IsDelegate) + { + throw new NotImplementedException(); + } + + HandleCall(callee, opcode); + } + + private LLVMValueRef LLVMFunctionForMethod(MethodDesc callee, StackEntry thisPointer, bool isCallVirt) + { + string calleeName = _compilation.NameMangler.GetMangledMethodName(callee).ToString(); + if (thisPointer != null && callee.IsVirtual && isCallVirt) + { + // TODO: Full resolution of virtual methods + if (!callee.IsNewSlot) + throw new NotImplementedException(); + + if (!_compilation.HasFixedSlotVTable(callee.OwningType)) + _dependencies.Add(_compilation.NodeFactory.VirtualMethodUse(callee)); + + //TODO: needs runtime support for DispatchByInterface + if (callee.OwningType.IsInterface) + throw new NotImplementedException(); + + return GetCallableVirtualMethod(thisPointer.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder), callee); + } + else + { + return GetOrCreateLLVMFunction(calleeName); + } + } + + private LLVMValueRef GetOrCreateMethodSlot(MethodDesc method) + { + var vtableSlotSymbol = _compilation.NodeFactory.VTableSlot(method); + _dependencies.Add(vtableSlotSymbol); + LLVMValueRef slot = LoadAddressOfSymbolNode(vtableSlotSymbol); + return LLVM.BuildLoad(_builder, slot, string.Empty); + } + + private LLVMValueRef GetCallableVirtualMethod(LLVMValueRef objectPtr, MethodDesc method) + { + Debug.Assert(method.IsVirtual); + if (method.OwningType.IsInterface) + { + throw new NotImplementedException(); + } + else + { + LLVMValueRef slot = GetOrCreateMethodSlot(method); + var pointerSize = method.Context.Target.PointerSize; + LLVMTypeRef universalSignature = LLVM.FunctionType(LLVM.VoidType(), new LLVMTypeRef[] { LLVM.PointerType(LLVM.Int8Type(), 0), LLVM.PointerType(LLVM.Int8Type(), 0) }, false); + var rawObjectPtr = CastIfNecessary(objectPtr, LLVM.PointerType(LLVM.PointerType(LLVM.PointerType(universalSignature, 0), 0), 0)); + var eeType = LLVM.BuildLoad(_builder, rawObjectPtr, "ldEEType"); + var slotPtr = LLVM.BuildGEP(_builder, eeType, new LLVMValueRef[] { slot }, "__getslot__"); + var functionPtr = LLVM.BuildLoad(_builder, slotPtr, "ld__getslot__"); + return functionPtr; + } + } + + private ExpressionEntry AllocateObject(TypeDesc type) + { + MetadataType metadataType = (MetadataType)type; + int objectSize = metadataType.InstanceByteCount.AsInt; + if (metadataType.IsValueType) + { + objectSize += type.Context.Target.PointerSize; + } + + LLVMValueRef eeTypePointer = GetEETypeForTypeDesc(type, true); + var rhpNewFastSig = LLVM.FunctionType(LLVMTypeRef.PointerType(LLVMTypeRef.Int8Type(), 0), new LLVMTypeRef[] { LLVM.TypeOf(eeTypePointer) }, false); + var rhpNewFast = GetOrCreateLLVMFunction("RhpNewFast", rhpNewFastSig); + LLVMValueRef allocatedMemory = LLVM.BuildCall(_builder, rhpNewFast, new LLVMValueRef[] { eeTypePointer }, "newobj"); + return new ExpressionEntry(StackValueKind.ObjRef, "newobj", allocatedMemory, type); + } + + private static LLVMValueRef BuildConstInt1(int number) + { + Debug.Assert(number == 0 || number == 1, "Non-boolean int1"); + return LLVM.ConstInt(LLVM.Int1Type(), (ulong)number, LLVMMisc.False); + } + + private static LLVMValueRef BuildConstInt8(byte number) + { + return LLVM.ConstInt(LLVM.Int8Type(), number, LLVMMisc.False); + } + + private static LLVMValueRef BuildConstInt32(int number) + { + return LLVM.ConstInt(LLVM.Int32Type(), (ulong)number, LLVMMisc.False); + } + + private LLVMValueRef GetEETypeForTypeDesc(TypeDesc target, bool constructed) + { + ISymbolNode node; + if (constructed) + { + node = _compilation.NodeFactory.ConstructedTypeSymbol(target); + } + else + { + node = _compilation.NodeFactory.NecessaryTypeSymbol(target); + } + LLVMValueRef eeTypePointer = LoadAddressOfSymbolNode(node); + _dependencies.Add(node); + + return eeTypePointer; + } + + /// + /// Implements intrinsic methods instread of calling them + /// + /// True if the method was implemented + private bool ImportIntrinsicCall(MethodDesc method) + { + Debug.Assert(method.IsIntrinsic); + + if (!(method.OwningType is MetadataType metadataType)) + { + return false; + } + + switch (method.Name) + { + // Workaround for not being able to build a WASM version of CoreLib. This method + // would return the x64 size, which is too large for WASM + case "get_OffsetToStringData": + if (metadataType.Name == "RuntimeHelpers" && metadataType.Namespace == "System.Runtime.CompilerServices") + { + _stack.Push(new Int32ConstantEntry(8, _method.Context.GetWellKnownType(WellKnownType.Int32))); + return true; + } + break; + } + + return false; + } + + private void HandleCall(MethodDesc callee, ILOpcode opcode = ILOpcode.call) + { + AddMethodReference(callee); + int offset = GetTotalParameterOffset() + GetTotalLocalOffset() + callee.Signature.ReturnType.GetElementSize().AsInt; + + LLVMValueRef shadowStack = LLVM.BuildGEP(_builder, LLVM.GetFirstParam(_llvmFunction), + new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (uint)offset, LLVMMisc.False) }, + String.Empty); + var castShadowStack = LLVM.BuildPointerCast(_builder, shadowStack, LLVM.PointerType(LLVM.Int8Type(), 0), "castshadowstack"); + + int returnOffset = GetTotalParameterOffset() + GetTotalLocalOffset(); + var returnAddress = LLVM.BuildGEP(_builder, LLVM.GetFirstParam(_llvmFunction), + new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (uint)returnOffset, LLVMMisc.False) }, + String.Empty); + var castReturnAddress = LLVM.BuildPointerCast(_builder, returnAddress, LLVM.PointerType(LLVM.Int8Type(), 0), "castreturnaddress"); + + // argument offset + uint argOffset = 0; + int instanceAdjustment = 0; + if (!callee.Signature.IsStatic) + { + instanceAdjustment = 1; + } + + // The last argument is the top of the stack. We need to reverse them and store starting at the first argument + StackEntry[] argumentValues = new StackEntry[callee.Signature.Length + instanceAdjustment]; + + for(int i = 0; i < argumentValues.Length; i++) + { + argumentValues[argumentValues.Length - i - 1] = _stack.Pop(); + } + + for (int index = 0; index < argumentValues.Length; index++) + { + StackEntry toStore = argumentValues[index]; + + TypeDesc argType; + if (index == 0 && !callee.Signature.IsStatic) + { + if(callee.OwningType.IsValueType) + argType = callee.OwningType.MakeByRefType(); + else + argType = callee.OwningType; + } + else + { + argType = callee.Signature[index - instanceAdjustment]; + } + + LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(argType); + + ImportStoreHelper(toStore.ValueAsType(valueType, _builder), valueType, castShadowStack, argOffset); + + argOffset += (uint)argType.GetElementSize().AsInt; + } + + LLVMValueRef fn = LLVMFunctionForMethod(callee, callee.Signature.IsStatic ? null : argumentValues[0], opcode == ILOpcode.callvirt); + LLVM.BuildCall(_builder, fn, new LLVMValueRef[] { + castShadowStack, + castReturnAddress}, string.Empty); + + + if (!callee.Signature.ReturnType.IsVoid) + { + LLVMTypeRef returnLLVMType = GetLLVMTypeForTypeDesc(callee.Signature.ReturnType); + LLVMValueRef returnLLVMPointer = LLVM.BuildPointerCast(_builder, returnAddress, LLVM.PointerType(returnLLVMType, 0), "castreturnpointer"); + PushLoadExpression(GetStackValueKind(callee.Signature.ReturnType), String.Empty, returnLLVMPointer, callee.Signature.ReturnType); + } + } + + private void AddMethodReference(MethodDesc method) + { + _dependencies.Add(_compilation.NodeFactory.MethodEntrypoint(method)); + } + + private void ImportRawPInvoke(MethodDesc method) + { + LLVMValueRef nativeFunc = LLVM.GetNamedFunction(Module, method.Name); + + //emscripten dies if this is output because its expected to have i32, i32, i64. But the runtime has defined it as i8*, i8*, i64 + if (method.Name == "memmove") + throw new NotImplementedException(); + + + // Create an import if we haven't already + if (nativeFunc.Pointer == IntPtr.Zero) + { + // Set up native parameter types + LLVMTypeRef[] paramTypes = new LLVMTypeRef[method.Signature.Length]; + for (int i = 0; i < paramTypes.Length; i++) + { + paramTypes[i] = GetLLVMTypeForTypeDesc(method.Signature[i]); + } + + // Define the full signature + LLVMTypeRef nativeFuncType = LLVM.FunctionType(GetLLVMTypeForTypeDesc(method.Signature.ReturnType), paramTypes, LLVMMisc.False); + + nativeFunc = LLVM.AddFunction(Module, method.Name, nativeFuncType); + LLVM.SetLinkage(nativeFunc, LLVMLinkage.LLVMDLLImportLinkage); + } + + LLVMValueRef[] arguments = new LLVMValueRef[method.Signature.Length]; + for(int i = 0; i < arguments.Length; i++) + { + // Arguments are reversed on the stack + // Coerce pointers to the native type + TypeDesc signatureType = method.Signature[arguments.Length - i - 1]; + arguments[arguments.Length - i - 1] = _stack.Pop().ValueAsType(GetLLVMTypeForTypeDesc(signatureType), _builder); + } + + // Save the top of the shadow stack in case the callee reverse P/Invokes + LLVMValueRef stackFrameSize = BuildConstInt32(GetTotalParameterOffset() + GetTotalLocalOffset()); + LLVM.BuildStore(_builder, LLVM.BuildGEP(_builder, LLVM.GetFirstParam(_llvmFunction), new LLVMValueRef[] { stackFrameSize }, "shadowStackTop"), + LLVM.GetNamedGlobal(Module, "t_pShadowStackTop")); + + // Don't name the return value if the function returns void, it's invalid + var returnValue = LLVM.BuildCall(_builder, nativeFunc, arguments, !method.Signature.ReturnType.IsVoid ? "call" : string.Empty); + + if(!method.Signature.ReturnType.IsVoid) + PushExpression(GetStackValueKind(method.Signature.ReturnType), "retval", returnValue, method.Signature.ReturnType); + } + + static LLVMValueRef s_shadowStackTop = default(LLVMValueRef); + LLVMValueRef ShadowStackTop + { + get + { + if (s_shadowStackTop.Pointer.Equals(IntPtr.Zero)) + { + s_shadowStackTop = LLVM.AddGlobal(Module, LLVM.PointerType(LLVM.Int8Type(), 0), "t_pShadowStackTop"); + LLVM.SetLinkage(s_shadowStackTop, LLVMLinkage.LLVMInternalLinkage); + LLVM.SetInitializer(s_shadowStackTop, LLVM.ConstPointerNull(LLVM.PointerType(LLVM.Int8Type(), 0))); + LLVM.SetThreadLocal(s_shadowStackTop, LLVMMisc.True); + } + return s_shadowStackTop; + } + } + + private void EmitNativeToManagedThunk(WebAssemblyCodegenCompilation compilation, MethodDesc method, string nativeName, LLVMValueRef managedFunction) + { + LLVMTypeRef[] llvmParams = new LLVMTypeRef[method.Signature.Length]; + for (int i = 0; i < llvmParams.Length; i++) + { + llvmParams[i] = GetLLVMTypeForTypeDesc(method.Signature[i]); + } + + LLVMTypeRef thunkSig = LLVM.FunctionType(GetLLVMTypeForTypeDesc(method.Signature.ReturnType), llvmParams, false); + LLVMValueRef thunkFunc = LLVM.AddFunction(compilation.Module, nativeName, thunkSig); + LLVMBasicBlockRef block = LLVM.AppendBasicBlock(thunkFunc, "Block0"); + LLVMBuilderRef builder = LLVM.CreateBuilder(); + LLVM.PositionBuilderAtEnd(builder, block); + + LLVMValueRef shadowStack = LLVM.BuildLoad(builder, ShadowStackTop, ""); + + int curOffset = 0; + for (int i = 0; i < llvmParams.Length; i++) + { + LLVMValueRef argAddr = LLVM.BuildGEP(builder, shadowStack, new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (ulong)curOffset, LLVMMisc.False) }, "arg" + i); + LLVM.BuildStore(builder, LLVM.GetParam(thunkFunc, (uint)i), CastIfNecessary(builder, argAddr, LLVM.PointerType(llvmParams[i], 0))); + curOffset += method.Signature[i].GetElementSize().AsInt; + } + + LLVMValueRef retAddr = LLVM.BuildGEP(builder, shadowStack, new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (ulong)curOffset, LLVMMisc.False) }, "retAddr"); + LLVM.BuildCall(builder, managedFunction, new LLVMValueRef[] { shadowStack, retAddr }, ""); + if (method.Signature.ReturnType != compilation.TypeSystemContext.GetWellKnownType(WellKnownType.Void)) + { + LLVM.BuildRet(builder, LLVM.BuildLoad(builder, CastIfNecessary(builder, retAddr, LLVM.PointerType(GetLLVMTypeForTypeDesc(method.Signature.ReturnType), 0)), "")); + } + else + { + LLVM.BuildRetVoid(builder); + } + } + + private void ImportCalli(int token) + { + } + + private void ImportLdFtn(int token, ILOpcode opCode) + { + } + + private void ImportLoadInt(long value, StackValueKind kind) + { + switch (kind) + { + case StackValueKind.Int32: + case StackValueKind.NativeInt: + _stack.Push(new Int32ConstantEntry((int)value, _method.Context.GetWellKnownType(WellKnownType.Int32))); + break; + + case StackValueKind.Int64: + _stack.Push(new Int64ConstantEntry(value, _method.Context.GetWellKnownType(WellKnownType.Int64))); + break; + + default: + throw new InvalidOperationException(kind.ToString()); + } + + } + + private void ImportLoadFloat(double value) + { + _stack.Push(new FloatConstantEntry(value, _method.Context.GetWellKnownType(WellKnownType.Double))); + } + + private void ImportBranch(ILOpcode opcode, BasicBlock target, BasicBlock fallthrough) + { + if (opcode == ILOpcode.br) + { + ImportFallthrough(target); + //TODO: why does this illegal branch happen in System.Reflection.MemberFilter.ctor + if (target.StartOffset == 0) + throw new InvalidProgramException(); + LLVM.BuildBr(_builder, GetLLVMBasicBlockForBlock(target)); + } + else + { + LLVMValueRef condition; + + if (opcode == ILOpcode.brfalse || opcode == ILOpcode.brtrue) + { + var op = _stack.Pop(); + LLVMValueRef value = op.ValueAsInt32(_builder, false); + + if (LLVM.TypeOf(value).TypeKind != LLVMTypeKind.LLVMIntegerTypeKind) + throw new InvalidProgramException("branch on non integer"); + + if (opcode == ILOpcode.brfalse) + { + condition = LLVM.BuildICmp(_builder, LLVMIntPredicate.LLVMIntEQ, value, LLVM.ConstInt(LLVM.TypeOf(value), 0, LLVMMisc.False), "brfalse"); + } + else + { + condition = LLVM.BuildICmp(_builder, LLVMIntPredicate.LLVMIntNE, value, LLVM.ConstInt(LLVM.TypeOf(value), 0, LLVMMisc.False), "brtrue"); + } + } + else + { + var op1 = _stack.Pop(); + var op2 = _stack.Pop(); + + // StackValueKind is carefully ordered to make this work (assuming the IL is valid) + StackValueKind kind; + + if (op1.Kind > op2.Kind) + { + kind = op1.Kind; + } + else + { + kind = op2.Kind; + } + + LLVMValueRef right = op1.ValueForStackKind(kind, _builder, false); + LLVMValueRef left = op2.ValueForStackKind(kind, _builder, false); + + if (kind != StackValueKind.Float) + { + switch (opcode) + { + case ILOpcode.beq: + condition = LLVM.BuildICmp(_builder, LLVMIntPredicate.LLVMIntEQ, left, right, "beq"); + break; + case ILOpcode.bge: + condition = LLVM.BuildICmp(_builder, LLVMIntPredicate.LLVMIntSGE, left, right, "bge"); + break; + case ILOpcode.bgt: + condition = LLVM.BuildICmp(_builder, LLVMIntPredicate.LLVMIntSGT, left, right, "bgt"); + break; + case ILOpcode.ble: + condition = LLVM.BuildICmp(_builder, LLVMIntPredicate.LLVMIntSLE, left, right, "ble"); + break; + case ILOpcode.blt: + condition = LLVM.BuildICmp(_builder, LLVMIntPredicate.LLVMIntSLT, left, right, "blt"); + break; + case ILOpcode.bne_un: + condition = LLVM.BuildICmp(_builder, LLVMIntPredicate.LLVMIntNE, left, right, "bne_un"); + break; + case ILOpcode.bge_un: + condition = LLVM.BuildICmp(_builder, LLVMIntPredicate.LLVMIntUGE, left, right, "bge_un"); + break; + case ILOpcode.bgt_un: + condition = LLVM.BuildICmp(_builder, LLVMIntPredicate.LLVMIntUGT, left, right, "bgt_un"); + break; + case ILOpcode.ble_un: + condition = LLVM.BuildICmp(_builder, LLVMIntPredicate.LLVMIntULE, left, right, "ble_un"); + break; + case ILOpcode.blt_un: + condition = LLVM.BuildICmp(_builder, LLVMIntPredicate.LLVMIntULT, left, right, "blt_un"); + break; + default: + throw new NotSupportedException(); // unreachable + } + } + else + { + switch (opcode) + { + case ILOpcode.beq: + condition = LLVM.BuildFCmp(_builder, LLVMRealPredicate.LLVMRealOEQ, left, right, "beq"); + break; + case ILOpcode.bge: + condition = LLVM.BuildFCmp(_builder, LLVMRealPredicate.LLVMRealOGE, left, right, "bge"); + break; + case ILOpcode.bgt: + condition = LLVM.BuildFCmp(_builder, LLVMRealPredicate.LLVMRealOGT, left, right, "bgt"); + break; + case ILOpcode.ble: + condition = LLVM.BuildFCmp(_builder, LLVMRealPredicate.LLVMRealOLE, left, right, "ble"); + break; + case ILOpcode.blt: + condition = LLVM.BuildFCmp(_builder, LLVMRealPredicate.LLVMRealOLT, left, right, "blt"); + break; + case ILOpcode.bne_un: + condition = LLVM.BuildFCmp(_builder, LLVMRealPredicate.LLVMRealONE, left, right, "bne_un"); + break; + case ILOpcode.bge_un: + condition = LLVM.BuildFCmp(_builder, LLVMRealPredicate.LLVMRealUGE, left, right, "bge_un"); + break; + case ILOpcode.bgt_un: + condition = LLVM.BuildFCmp(_builder, LLVMRealPredicate.LLVMRealUGT, left, right, "bgt_un"); + break; + case ILOpcode.ble_un: + condition = LLVM.BuildFCmp(_builder, LLVMRealPredicate.LLVMRealULE, left, right, "ble_un"); + break; + case ILOpcode.blt_un: + condition = LLVM.BuildFCmp(_builder, LLVMRealPredicate.LLVMRealULT, left, right, "blt_un"); + break; + default: + throw new NotSupportedException(); // unreachable + } + } + } + //TODO: why did this happen only during an optimized build of [System.Private.CoreLib]System.Threading.Lock.ReleaseContended + if (target.StartOffset == 0) + throw new NotImplementedException("cant branch to entry basic block"); + + ImportFallthrough(target); + ImportFallthrough(fallthrough); + LLVM.BuildCondBr(_builder, condition, GetLLVMBasicBlockForBlock(target), GetLLVMBasicBlockForBlock(fallthrough)); + } + } + + private void ImportSwitchJump(int jmpBase, int[] jmpDelta, BasicBlock fallthrough) + { + var operand = _stack.Pop(); + + var @switch = LLVM.BuildSwitch(_builder, operand.ValueAsInt32(_builder, false), GetLLVMBasicBlockForBlock(fallthrough), (uint)jmpDelta.Length); + for (var i = 0; i < jmpDelta.Length; i++) + { + var target = _basicBlocks[_currentOffset + jmpDelta[i]]; + LLVM.AddCase(@switch, LLVM.ConstInt(LLVM.Int32Type(), (ulong)i, false), GetLLVMBasicBlockForBlock(target)); + ImportFallthrough(target); + } + + ImportFallthrough(fallthrough); + } + + private void ImportLoadIndirect(int token) + { + ImportLoadIndirect(ResolveTypeToken(token)); + } + + private void ImportLoadIndirect(TypeDesc type) + { + var pointer = _stack.Pop(); + Debug.Assert(pointer is ExpressionEntry || pointer is ConstantEntry); + var expressionPointer = pointer as ExpressionEntry; + TypeDesc pointerElementType = pointer.Type.GetParameterType(); + LLVMValueRef rawValue = expressionPointer?.RawLLVMValue ?? LLVM.ConstNull(GetLLVMTypeForTypeDesc(pointerElementType)); + _stack.Push(new LoadExpressionEntry(type != null ? GetStackValueKind(type) : StackValueKind.ByRef, "ldind", + rawValue, pointer.Type.GetParameterType())); + } + + private void ImportStoreIndirect(int token) + { + ImportStoreIndirect(ResolveTypeToken(token)); + } + + private void ImportStoreIndirect(TypeDesc type) + { + StackEntry value = _stack.Pop(); + StackEntry destinationPointer = _stack.Pop(); + LLVMValueRef typedValue; + LLVMValueRef typedPointer; + + if (type != null) + { + typedValue = value.ValueAsType(type, _builder); + typedPointer = destinationPointer.ValueAsType(type.MakePointerType(), _builder); + } + else + { + typedPointer = destinationPointer.ValueAsType(LLVM.PointerType(LLVM.Int32Type(), 0), _builder); + typedValue = value.ValueAsInt32(_builder, false); + } + + LLVM.BuildStore(_builder, typedValue, typedPointer); + } + + private void ImportBinaryOperation(ILOpcode opcode) + { + StackEntry op1 = _stack.Pop(); + StackEntry op2 = _stack.Pop(); + + // StackValueKind is carefully ordered to make this work (assuming the IL is valid) + StackValueKind kind; + TypeDesc type; + + if (op1.Kind > op2.Kind) + { + kind = op1.Kind; + type = op1.Type; + } + else + { + kind = op2.Kind; + type = op2.Type; + } + + // The one exception from the above rule + if (kind == StackValueKind.ByRef) + { + kind = StackValueKind.NativeInt; + type = type.MakePointerType(); + } + + LLVMValueRef result; + LLVMValueRef left = op1.ValueForStackKind(kind, _builder, false); + LLVMValueRef right = op2.ValueForStackKind(kind, _builder, false); + if (kind == StackValueKind.Float) + { + switch (opcode) + { + case ILOpcode.add: + result = LLVM.BuildFAdd(_builder, left, right, "fadd"); + break; + case ILOpcode.sub: + result = LLVM.BuildFSub(_builder, left, right, "fsub"); + break; + case ILOpcode.mul: + result = LLVM.BuildFMul(_builder, left, right, "fmul"); + break; + case ILOpcode.div: + result = LLVM.BuildFDiv(_builder, left, right, "fdiv"); + break; + case ILOpcode.rem: + result = LLVM.BuildFRem(_builder, left, right, "frem"); + break; + + // TODO: Overflow checks + case ILOpcode.add_ovf: + case ILOpcode.add_ovf_un: + result = LLVM.BuildFAdd(_builder, left, right, "fadd"); + break; + case ILOpcode.sub_ovf: + case ILOpcode.sub_ovf_un: + result = LLVM.BuildFSub(_builder, left, right, "fsub"); + break; + case ILOpcode.mul_ovf: + case ILOpcode.mul_ovf_un: + result = LLVM.BuildFMul(_builder, left, right, "fmul"); + break; + + default: + throw new InvalidOperationException(); // Should be unreachable + } + } + else + { + switch (opcode) + { + case ILOpcode.add: + result = LLVM.BuildAdd(_builder, left, right, "add"); + break; + case ILOpcode.sub: + result = LLVM.BuildSub(_builder, left, right, "sub"); + break; + case ILOpcode.mul: + result = LLVM.BuildMul(_builder, left, right, "mul"); + break; + case ILOpcode.div: + result = LLVM.BuildSDiv(_builder, left, right, "sdiv"); + break; + case ILOpcode.div_un: + result = LLVM.BuildUDiv(_builder, left, right, "udiv"); + break; + case ILOpcode.rem: + result = LLVM.BuildSRem(_builder, left, right, "srem"); + break; + case ILOpcode.rem_un: + result = LLVM.BuildURem(_builder, left, right, "urem"); + break; + case ILOpcode.and: + result = LLVM.BuildAnd(_builder, left, right, "and"); + break; + case ILOpcode.or: + result = LLVM.BuildOr(_builder, left, right, "or"); + break; + case ILOpcode.xor: + result = LLVM.BuildXor(_builder, left, right, "xor"); + break; + + // TODO: Overflow checks + case ILOpcode.add_ovf: + case ILOpcode.add_ovf_un: + result = LLVM.BuildAdd(_builder, left, right, "add"); + break; + case ILOpcode.sub_ovf: + case ILOpcode.sub_ovf_un: + result = LLVM.BuildSub(_builder, left, right, "sub"); + break; + case ILOpcode.mul_ovf: + case ILOpcode.mul_ovf_un: + result = LLVM.BuildMul(_builder, left, right, "mul"); + break; + + default: + throw new InvalidOperationException(); // Should be unreachable + } + } + + + if (kind == StackValueKind.NativeInt || kind == StackValueKind.ByRef || kind == StackValueKind.ObjRef) + { + //we need to put the type back if we changed it because it started out a pointer + result = CastToTypeDesc(result, type); + } + PushExpression(kind, "binop", result, type); + } + + private void ImportShiftOperation(ILOpcode opcode) + { + LLVMValueRef result; + StackEntry numBitsToShift = _stack.Pop(); + StackEntry valueToShift = _stack.Pop(); + + LLVMValueRef valueToShiftValue = valueToShift.ValueForStackKind(valueToShift.Kind, _builder, false); + + switch (opcode) + { + case ILOpcode.shl: + result = LLVM.BuildShl(_builder, valueToShiftValue, numBitsToShift.ValueAsInt32(_builder, false), "shl"); + break; + case ILOpcode.shr: + result = LLVM.BuildAShr(_builder, valueToShiftValue, numBitsToShift.ValueAsInt32(_builder, false), "shr"); + break; + case ILOpcode.shr_un: + result = LLVM.BuildLShr(_builder, valueToShiftValue, numBitsToShift.ValueAsInt32(_builder, false), "shr"); + break; + default: + throw new InvalidOperationException(); // Should be unreachable + } + + PushExpression(valueToShift.Kind, "shiftop", result, valueToShift.Type); + } + + private void ImportCompareOperation(ILOpcode opcode) + { + var op1 = _stack.Pop(); + var op2 = _stack.Pop(); + + // StackValueKind is carefully ordered to make this work (assuming the IL is valid) + StackValueKind kind; + + if (op1.Kind > op2.Kind) + { + kind = op1.Kind; + } + else + { + kind = op2.Kind; + } + + LLVMValueRef result; + LLVMValueRef typeSaneOp1 = op1.ValueForStackKind(kind, _builder, true); + LLVMValueRef typeSaneOp2 = op2.ValueForStackKind(kind, _builder, true); + + if (kind != StackValueKind.Float) + { + switch (opcode) + { + case ILOpcode.ceq: + result = LLVM.BuildICmp(_builder, LLVMIntPredicate.LLVMIntEQ, typeSaneOp2, typeSaneOp1, "ceq"); + break; + case ILOpcode.cgt: + result = LLVM.BuildICmp(_builder, LLVMIntPredicate.LLVMIntSGT, typeSaneOp2, typeSaneOp1, "cgt"); + break; + case ILOpcode.clt: + result = LLVM.BuildICmp(_builder, LLVMIntPredicate.LLVMIntSLT, typeSaneOp2, typeSaneOp1, "clt"); + break; + case ILOpcode.cgt_un: + result = LLVM.BuildICmp(_builder, LLVMIntPredicate.LLVMIntUGT, typeSaneOp2, typeSaneOp1, "cgt_un"); + break; + case ILOpcode.clt_un: + result = LLVM.BuildICmp(_builder, LLVMIntPredicate.LLVMIntULT, typeSaneOp2, typeSaneOp1, "clt_un"); + break; + default: + throw new NotSupportedException(); // unreachable + } + } + else + { + switch (opcode) + { + case ILOpcode.ceq: + result = LLVM.BuildFCmp(_builder, LLVMRealPredicate.LLVMRealOEQ, typeSaneOp2, typeSaneOp1, "ceq"); + break; + case ILOpcode.cgt: + result = LLVM.BuildFCmp(_builder, LLVMRealPredicate.LLVMRealOGT, typeSaneOp2, typeSaneOp1, "cgt"); + break; + case ILOpcode.clt: + result = LLVM.BuildFCmp(_builder, LLVMRealPredicate.LLVMRealOLT, typeSaneOp2, typeSaneOp1, "clt"); + break; + case ILOpcode.cgt_un: + result = LLVM.BuildFCmp(_builder, LLVMRealPredicate.LLVMRealUGT, typeSaneOp2, typeSaneOp1, "cgt_un"); + break; + case ILOpcode.clt_un: + result = LLVM.BuildFCmp(_builder, LLVMRealPredicate.LLVMRealULT, typeSaneOp2, typeSaneOp1, "clt_un"); + break; + default: + throw new NotSupportedException(); // unreachable + } + } + + PushExpression(StackValueKind.Int32, "cmpop", result, GetWellKnownType(WellKnownType.SByte)); + } + + private void ImportConvert(WellKnownType wellKnownType, bool checkOverflow, bool unsigned) + { + StackEntry value = _stack.Pop(); + LLVMValueRef convertedValue; + //conv.u for a pointer should change to a int8* + if (wellKnownType == WellKnownType.UIntPtr) + { + if (value.Kind == StackValueKind.Int32) + { + convertedValue = LLVM.BuildIntToPtr(_builder, value.ValueAsInt32(_builder, false), LLVM.PointerType(LLVM.Int8Type(), 0), "conv.u"); + } + else + { + convertedValue = value.ValueAsType(GetWellKnownType(wellKnownType), _builder); + } + } + else + { + convertedValue = value.ValueAsType(GetWellKnownType(wellKnownType), _builder); + } + PushExpression(value.Kind, "conv", convertedValue, value.Type); + } + + private void ImportUnaryOperation(ILOpcode opCode) + { + var argument = _stack.Pop(); + + LLVMValueRef result; + switch (opCode) + { + case ILOpcode.neg: + if (argument.Kind == StackValueKind.Float) + { + result = LLVM.BuildFNeg(_builder, argument.ValueForStackKind(argument.Kind, _builder, false), "neg"); + } + else + { + result = LLVM.BuildNeg(_builder, argument.ValueForStackKind(argument.Kind, _builder, true), "neg"); + } + break; + case ILOpcode.not: + result = LLVM.BuildNot(_builder, argument.ValueForStackKind(argument.Kind, _builder, true), "not"); + break; + default: + throw new NotSupportedException(); // unreachable + } + + PushExpression(argument.Kind, "unaryop", result, argument.Type); + } + + private void ImportCpOpj(int token) + { + var type = ResolveTypeToken(token); + + if (!type.IsValueType) + { + throw new InvalidOperationException(); + } + + var src = _stack.Pop(); + + if (src.Kind != StackValueKind.NativeInt && src.Kind != StackValueKind.ByRef && src.Kind != StackValueKind.ObjRef) + { + throw new InvalidOperationException(); + } + + var dest = _stack.Pop(); + + if (dest.Kind != StackValueKind.NativeInt && dest.Kind != StackValueKind.ByRef && dest.Kind != StackValueKind.ObjRef) + { + throw new InvalidOperationException(); + } + + var pointerType = GetLLVMTypeForTypeDesc(type.MakePointerType()); + + var value = LLVM.BuildLoad(_builder, src.ValueAsType(pointerType, _builder), "cpobj.load"); + + LLVM.BuildStore(_builder, value, dest.ValueAsType(pointerType, _builder)); + } + + private void ImportUnbox(int token, ILOpcode opCode) + { + TypeDesc type = ResolveTypeToken(token); + if (type.IsNullable) + throw new NotImplementedException(); + + if (opCode == ILOpcode.unbox) + { + var unboxResult = _stack.Pop().ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder); + LLVMValueRef unboxData = LLVM.BuildGEP(_builder, unboxResult, new LLVMValueRef[] { BuildConstInt32(type.Context.Target.PointerSize) }, "unboxData"); + var expressionType = type.MakeByRefType(); + //push the pointer to the data, but it shouldnt be implicitly dereferenced + PushExpression(GetStackValueKind(expressionType), "unboxed", unboxData, expressionType); + } + else //unbox_any + { + Debug.Assert(opCode == ILOpcode.unbox_any); + + //TODO: when the runtime is ready switch this to calling the real RhUnboxAny + //LLVMValueRef eeType = GetEETypeForTypeDesc(type); + //var eeTypeDesc = _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr"); + //LLVMValueRef untypedObjectValue = LLVM.BuildAlloca(_builder, GetLLVMTypeForTypeDesc(type), "objptr"); + //PushExpression(StackValueKind.ByRef, "objPtr", untypedObjectValue, type.MakePointerType()); + //PushExpression(StackValueKind.ByRef, "eeType", eeType, eeTypeDesc); + //CallRuntimeExport(_compilation.TypeSystemContext, "RhUnboxAny"); + //PushLoadExpression(GetStackValueKind(type), "unboxed", untypedObjectValue, type); + //this can be removed once we can call RhUnboxAny + if (!type.IsValueType) + throw new NotImplementedException(); + + var unboxResult = _stack.Pop().ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder); + LLVMValueRef unboxData = LLVM.BuildGEP(_builder, unboxResult, new LLVMValueRef[] { BuildConstInt32(type.Context.Target.PointerSize) }, "unboxData"); + PushLoadExpression(GetStackValueKind(type), "unboxed", unboxData, type); + } + } + + private void ImportRefAnyVal(int token) + { + } + + private void ImportCkFinite() + { + } + + private void ImportMkRefAny(int token) + { + } + + private void ImportLdToken(int token) + { + var ldtokenValue = _methodIL.GetObject(token); + WellKnownType ldtokenKind; + string name; + StackEntry value; + if (ldtokenValue is TypeDesc) + { + ldtokenKind = WellKnownType.RuntimeTypeHandle; + PushExpression(StackValueKind.ByRef, "ldtoken", GetEETypeForTypeDesc(ldtokenValue as TypeDesc, false), _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr")); + MethodDesc helper = _compilation.TypeSystemContext.GetHelperEntryPoint("LdTokenHelpers", "GetRuntimeTypeHandle"); + AddMethodReference(helper); + HandleCall(helper); + name = ldtokenValue.ToString(); + } + else if (ldtokenValue is FieldDesc) + { + ldtokenKind = WellKnownType.RuntimeFieldHandle; + value = new LdTokenEntry(StackValueKind.ValueType, null, (FieldDesc)ldtokenValue, GetWellKnownType(ldtokenKind)); + _stack.Push(value); + } + else if (ldtokenValue is MethodDesc) + { + throw new NotImplementedException(); + } + else + { + throw new InvalidOperationException(); + } + } + + private void ImportLocalAlloc() + { + } + + private void ImportEndFilter() + { + } + + private void ImportCpBlk() + { + } + + private void ImportInitBlk() + { + } + + private void ImportRethrow() + { + EmitTrapCall(); + } + + private void ImportSizeOf(int token) + { + } + + private void ImportRefAnyType() + { + } + + private void ImportArgList() + { + } + + private void ImportUnalignedPrefix(byte alignment) + { + } + + private void ImportVolatilePrefix() + { + } + + private void ImportTailPrefix() + { + } + + private void ImportConstrainedPrefix(int token) + { + } + + private void ImportNoPrefix(byte mask) + { + } + + private void ImportReadOnlyPrefix() + { + } + + private void ImportThrow() + { + var exceptionObject = _stack.Pop(); + + EmitTrapCall(); + } + + private LLVMValueRef GetInstanceFieldAddress(StackEntry objectEntry, FieldDesc field) + { + var objectType = objectEntry.Type ?? field.OwningType; + LLVMValueRef untypedObjectValue; + LLVMTypeRef llvmObjectType = GetLLVMTypeForTypeDesc(objectType); + if (objectType.IsValueType && !objectType.IsPointer && objectEntry.Kind != StackValueKind.NativeInt && objectEntry.Kind != StackValueKind.ByRef) + { + if (objectEntry is LoadExpressionEntry) + { + untypedObjectValue = CastToRawPointer(((LoadExpressionEntry)objectEntry).RawLLVMValue); + } + else + { + untypedObjectValue = LLVM.BuildAlloca(_builder, llvmObjectType, "objptr"); + LLVM.BuildStore(_builder, objectEntry.ValueAsType(llvmObjectType, _builder), untypedObjectValue); + untypedObjectValue = LLVM.BuildPointerCast(_builder, untypedObjectValue, LLVM.PointerType(LLVMTypeRef.Int8Type(), 0), "objptrcast"); + } + } + else + { + untypedObjectValue = objectEntry.ValueAsType(LLVM.PointerType(LLVMTypeRef.Int8Type(), 0), _builder); + } + if (field.Offset.AsInt == 0) + { + return untypedObjectValue; + } + else + { + var loadLocation = LLVM.BuildGEP(_builder, untypedObjectValue, + new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (ulong)field.Offset.AsInt, LLVMMisc.False) }, String.Empty); + return loadLocation; + } + } + + private LLVMValueRef GetFieldAddress(FieldDesc field, bool isStatic) + { + if (field.IsStatic) + { + //pop unused value + if (!isStatic) + _stack.Pop(); + + return WebAssemblyObjectWriter.EmitGlobal(Module, field, _compilation.NameMangler); + } + else + { + return GetInstanceFieldAddress(_stack.Pop(), field); + } + } + + private void ImportLoadField(int token, bool isStatic) + { + FieldDesc field = (FieldDesc)_methodIL.GetObject(token); + LLVMValueRef fieldAddress = GetFieldAddress(field, isStatic); + PushLoadExpression(GetStackValueKind(field.FieldType), "ldfld_" + field.Name, fieldAddress, field.FieldType); + } + + private void ImportAddressOfField(int token, bool isStatic) + { + FieldDesc field = (FieldDesc)_methodIL.GetObject(token); + LLVMValueRef fieldAddress = GetFieldAddress(field, isStatic); + _stack.Push(new AddressExpressionEntry(StackValueKind.ByRef, "ldflda", fieldAddress, field.FieldType.MakePointerType())); + } + + private void ImportStoreField(int token, bool isStatic) + { + FieldDesc field = (FieldDesc)_methodIL.GetObject(token); + StackEntry valueEntry = _stack.Pop(); + LLVMValueRef fieldAddress = GetFieldAddress(field, isStatic); + CastingStore(fieldAddress, valueEntry, field.FieldType); + } + + // Loads symbol address. Address is represented as a i32* + private LLVMValueRef LoadAddressOfSymbolNode(ISymbolNode node) + { + LLVMValueRef addressOfAddress = WebAssemblyObjectWriter.GetSymbolValuePointer(Module, node, _compilation.NameMangler, false); + //return addressOfAddress; + return LLVM.BuildLoad(_builder, addressOfAddress, "LoadAddressOfSymbolNode"); + } + + private void ImportLoadString(int token) + { + TypeDesc stringType = this._compilation.TypeSystemContext.GetWellKnownType(WellKnownType.String); + + string str = (string)_methodIL.GetObject(token); + ISymbolNode node = _compilation.NodeFactory.SerializedStringObject(str); + LLVMValueRef stringDataPointer = LoadAddressOfSymbolNode(node); + _dependencies.Add(node); + _stack.Push(new ExpressionEntry(GetStackValueKind(stringType), String.Empty, stringDataPointer, stringType)); + } + + private void ImportInitObj(int token) + { + TypeDesc type = ResolveTypeToken(token); + var valueEntry = _stack.Pop(); + var llvmType = GetLLVMTypeForTypeDesc(type); + if (llvmType.TypeKind == LLVMTypeKind.LLVMArrayTypeKind) + ImportCallMemset(valueEntry.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder), 0, type.GetElementSize().AsInt); + else if (llvmType.TypeKind == LLVMTypeKind.LLVMIntegerTypeKind) + LLVM.BuildStore(_builder, LLVM.ConstInt(llvmType, 0, LLVMMisc.False), valueEntry.ValueAsType(LLVM.PointerType(llvmType, 0), _builder)); + else if (llvmType.TypeKind == LLVMTypeKind.LLVMPointerTypeKind) + LLVM.BuildStore(_builder, LLVM.ConstNull(llvmType), valueEntry.ValueAsType(LLVM.PointerType(llvmType, 0), _builder)); + else if (llvmType.TypeKind == LLVMTypeKind.LLVMFloatTypeKind) + LLVM.BuildStore(_builder, LLVM.ConstReal(llvmType, 0.0), valueEntry.ValueAsType(LLVM.PointerType(llvmType, 0), _builder)); + else + throw new NotImplementedException(); + } + + private void ImportBox(int token) + { + TypeDesc type = ResolveTypeToken(token); + if (type.IsValueType) + { + if (type.IsNullable) + throw new NotImplementedException(); + + var value = _stack.Pop(); + ExpressionEntry boxTarget = AllocateObject(type); + LLVMValueRef boxData = LLVM.BuildGEP(_builder, boxTarget.RawLLVMValue, new LLVMValueRef[] { BuildConstInt32(type.Context.Target.PointerSize) }, "boxData"); + LLVMValueRef typedBoxData = LLVM.BuildPointerCast(_builder, boxData, LLVM.PointerType(GetLLVMTypeForTypeDesc(type), 0), "typedBoxData"); + LLVM.BuildStore(_builder, value.ValueAsType(type, _builder), typedBoxData); + _stack.Push(boxTarget); + } + } + + private void ImportLeave(BasicBlock target) + { + for (int i = 0; i < _exceptionRegions.Length; i++) + { + var r = _exceptionRegions[i]; + + if (r.ILRegion.Kind == ILExceptionRegionKind.Finally && + IsOffsetContained(_currentOffset - 1, r.ILRegion.TryOffset, r.ILRegion.TryLength) && + !IsOffsetContained(target.StartOffset, r.ILRegion.TryOffset, r.ILRegion.TryLength)) + { + MarkBasicBlock(_basicBlocks[r.ILRegion.HandlerOffset]); + } + } + + MarkBasicBlock(target); + LLVM.BuildBr(_builder, GetLLVMBasicBlockForBlock(target)); + } + + private static bool IsOffsetContained(int offset, int start, int length) + { + return start <= offset && offset < start + length; + } + + private void ImportNewArray(int token) + { + } + + private void ImportLoadElement(int token) + { + } + + private void ImportLoadElement(TypeDesc elementType) + { + } + + private void ImportStoreElement(int token) + { + } + + private void ImportStoreElement(TypeDesc elementType) + { + } + + private void ImportLoadLength() + { + } + + private void ImportAddressOfElement(int token) + { + } + + private void ImportEndFinally() + { + } + + private void ImportFallthrough(BasicBlock next) + { + EvaluationStack entryStack = next.EntryStack; + + if (entryStack != null) + { + if (entryStack.Length != _stack.Length) + throw new InvalidProgramException(); + + for (int i = 0; i < entryStack.Length; i++) + { + // TODO: Do we need to allow conversions? + if (entryStack[i].Kind != _stack[i].Kind) + throw new InvalidProgramException(); + + if (entryStack[i].Kind == StackValueKind.ValueType) + { + if (entryStack[i].Type != _stack[i].Type) + throw new InvalidProgramException(); + } + } + } + else + { + if (_stack.Length > 0) + { + entryStack = new EvaluationStack(_stack.Length); + for (int i = 0; i < _stack.Length; i++) + { + entryStack.Push(NewSpillSlot(_stack[i])); + } + } + next.EntryStack = entryStack; + } + + if (entryStack != null) + { + for (int i = 0; i < entryStack.Length; i++) + { + var currentEntry = _stack[i]; + var entry = entryStack[i] as SpilledExpressionEntry; + if (entry == null) + throw new InvalidProgramException(); + + if (currentEntry is SpilledExpressionEntry) + continue; //this is already a sharable value + + StoreTemp(entry.LocalIndex, currentEntry.ValueAsType(entry.Type, _builder)); + } + } + + MarkBasicBlock(next); + + } + + private void CallRuntimeExport(TypeSystemContext context, string methodName) + { + MetadataType helperType = context.SystemModule.GetKnownType("System.Runtime", "RuntimeExports"); + MethodDesc helperMethod = helperType.GetKnownMethod(methodName, null); + HandleCall(helperMethod); + } + + private StackEntry NewSpillSlot(StackEntry entry) + { + if (entry is SpilledExpressionEntry) + return entry; + else + { + var entryType = entry.Type ?? GetWellKnownType(WellKnownType.Object); //type is required here, currently the only time entry.Type is null is if someone has pushed a null literal + var entryIndex = _spilledExpressions.Count; + var newEntry = new SpilledExpressionEntry(entry.Kind, entry is ExpressionEntry ? ((ExpressionEntry)entry).Name : "spilled" + entryIndex, entryType, entryIndex, this); + _spilledExpressions.Add(newEntry); + return newEntry; + } + } + + private TypeDesc ResolveTypeToken(int token) + { + return (TypeDesc)_methodIL.GetObject(token); + } + + private TypeDesc GetWellKnownType(WellKnownType wellKnownType) + { + return _compilation.TypeSystemContext.GetWellKnownType(wellKnownType); + } + + private void ReportInvalidBranchTarget(int targetOffset) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + private void ReportFallthroughAtEndOfMethod() + { + ThrowHelper.ThrowInvalidProgramException(); + } + + private void ReportMethodEndInsideInstruction() + { + ThrowHelper.ThrowInvalidProgramException(); + } + + private void ReportInvalidInstruction(ILOpcode opcode) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + private void EmitTrapCall() + { + if (TrapFunction.Pointer == IntPtr.Zero) + { + TrapFunction = LLVM.AddFunction(Module, "llvm.trap", LLVM.FunctionType(LLVM.VoidType(), Array.Empty(), false)); + } + LLVM.BuildCall(_builder, TrapFunction, Array.Empty(), string.Empty); + } + + private void EmitDoNothingCall() + { + if (DoNothingFunction.Pointer == IntPtr.Zero) + { + DoNothingFunction = LLVM.AddFunction(Module, "llvm.donothing", LLVM.FunctionType(LLVM.VoidType(), Array.Empty(), false)); + } + LLVM.BuildCall(_builder, DoNothingFunction, Array.Empty(), string.Empty); + } + + public override string ToString() + { + return _method.ToString(); + } + } +} diff --git a/external/corert/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter_Statics.cs b/external/corert/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter_Statics.cs new file mode 100644 index 0000000000..002e5e916e --- /dev/null +++ b/external/corert/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter_Statics.cs @@ -0,0 +1,122 @@ +// 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.Collections.Generic; +using System.Diagnostics; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using ILCompiler; +using ILCompiler.CodeGen; + +using ILCompiler.DependencyAnalysis; +using LLVMSharp; + +namespace Internal.IL +{ + internal partial class ILImporter + { + public static void CompileMethod(WebAssemblyCodegenCompilation compilation, WebAssemblyMethodCodeNode methodCodeNodeNeedingCode) + { + MethodDesc method = methodCodeNodeNeedingCode.Method; + + if (compilation.Logger.IsVerbose) + { + string methodName = method.ToString(); + compilation.Logger.Writer.WriteLine("Compiling " + methodName); + } + + if (method.HasCustomAttribute("System.Runtime", "RuntimeImportAttribute")) + { + methodCodeNodeNeedingCode.CompilationCompleted = true; + //throw new NotImplementedException(); + //CompileExternMethod(methodCodeNodeNeedingCode, ((EcmaMethod)method).GetRuntimeImportName()); + //return; + } + + if (method.IsRawPInvoke()) + { + //CompileExternMethod(methodCodeNodeNeedingCode, method.GetPInvokeMethodMetadata().Name ?? method.Name); + //return; + } + + var methodIL = compilation.GetMethodIL(method); + if (methodIL == null) + return; + + ILImporter ilImporter = null; + try + { + string mangledName; + // TODO: We should use the startup node to generate StartupCodeMain and avoid special casing here + if (methodCodeNodeNeedingCode.Method.Signature.IsStatic && methodCodeNodeNeedingCode.Method.Name == "Main") + { + mangledName = "StartupCodeMain"; + } + else + { + mangledName = compilation.NameMangler.GetMangledMethodName(methodCodeNodeNeedingCode.Method).ToString(); + } + + ilImporter = new ILImporter(compilation, method, methodIL, mangledName); + + CompilerTypeSystemContext typeSystemContext = compilation.TypeSystemContext; + + //MethodDebugInformation debugInfo = compilation.GetDebugInfo(methodIL); + + /* if (!compilation.Options.HasOption(CppCodegenConfigProvider.NoLineNumbersString))*/ + { + //IEnumerable sequencePoints = debugInfo.GetSequencePoints(); + /*if (sequencePoints != null) + ilImporter.SetSequencePoints(sequencePoints);*/ + } + + //IEnumerable localVariables = debugInfo.GetLocalVariables(); + /*if (localVariables != null) + ilImporter.SetLocalVariables(localVariables);*/ + + IEnumerable parameters = GetParameterNamesForMethod(method); + /*if (parameters != null) + ilImporter.SetParameterNames(parameters);*/ + + ilImporter.Import(); + + methodCodeNodeNeedingCode.CompilationCompleted = true; + } + catch (Exception e) + { + compilation.Logger.Writer.WriteLine(e.Message + " (" + method + ")"); + + methodCodeNodeNeedingCode.CompilationCompleted = true; +// methodCodeNodeNeedingCode.SetDependencies(ilImporter.GetDependencies()); + //throw new NotImplementedException(); + //methodCodeNodeNeedingCode.SetCode(sb.ToString(), Array.Empty()); + } + + // Ensure dependencies show up regardless of exceptions to avoid breaking LLVM + methodCodeNodeNeedingCode.SetDependencies(ilImporter.GetDependencies()); + } + + static LLVMValueRef TrapFunction = default(LLVMValueRef); + static LLVMValueRef DoNothingFunction = default(LLVMValueRef); + + private static IEnumerable GetParameterNamesForMethod(MethodDesc method) + { + // TODO: The uses of this method need revision. The right way to get to this info is from + // a MethodIL. For declarations, we don't need names. + + method = method.GetTypicalMethodDefinition(); + var ecmaMethod = method as EcmaMethod; + if (ecmaMethod != null && ecmaMethod.Module.PdbReader != null) + { + return (new EcmaMethodDebugInformation(ecmaMethod)).GetParameterNames(); + } + + return null; + } + + } +} diff --git a/external/corert/src/ILCompiler.WebAssembly/src/CodeGen/LLVMMisc.cs b/external/corert/src/ILCompiler.WebAssembly/src/CodeGen/LLVMMisc.cs new file mode 100644 index 0000000000..0139acdbfe --- /dev/null +++ b/external/corert/src/ILCompiler.WebAssembly/src/CodeGen/LLVMMisc.cs @@ -0,0 +1,16 @@ +// 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 LLVMSharp; + +namespace ILCompiler.CodeGen +{ + class LLVMMisc + { + public static LLVMBool False { get; } = new LLVMBool(0); + + public static LLVMBool True { get; } = new LLVMBool(1); + } +} diff --git a/external/corert/src/ILCompiler.WebAssembly/src/CodeGen/NodeDataSection.cs b/external/corert/src/ILCompiler.WebAssembly/src/CodeGen/NodeDataSection.cs new file mode 100644 index 0000000000..e1c4438fda --- /dev/null +++ b/external/corert/src/ILCompiler.WebAssembly/src/CodeGen/NodeDataSection.cs @@ -0,0 +1,30 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ILCompiler.Compiler.CppCodeGen +{ + internal enum NodeDataSectionType + { + Relocation, + ByteData + } + + internal struct NodeDataSection + { + public readonly NodeDataSectionType SectionType; + public readonly int SectionSize; + + public NodeDataSection(NodeDataSectionType sectionType, int sectionSize) + { + SectionType = sectionType; + SectionSize = sectionSize; + } + } +} diff --git a/external/corert/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs b/external/corert/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs new file mode 100644 index 0000000000..9cb9b26d50 --- /dev/null +++ b/external/corert/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs @@ -0,0 +1,864 @@ +// 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.Collections.Generic; +using System.IO; +using System.Diagnostics; +using System.Runtime.InteropServices; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.TypeSystem.TypesDebugInfo; +using Internal.JitInterface; +using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData; + +using LLVMSharp; +using ILCompiler.CodeGen; +using System.Linq; +using Internal.IL; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Object writer using https://github.com/dotnet/llilc + /// + internal class WebAssemblyObjectWriter : IDisposable + { + public static string GetBaseSymbolName(ISymbolNode symbol, NameMangler nameMangler, bool objectWriterUse = false) + { + if (symbol is WebAssemblyMethodCodeNode) + { + return symbol.GetMangledName(nameMangler); + } + + if (symbol is ObjectNode) + { + ObjectNode objNode = (ObjectNode)symbol; + ISymbolDefinitionNode symbolDefNode = (ISymbolDefinitionNode)symbol; + if (symbolDefNode.Offset == 0) + { + return symbol.GetMangledName(nameMangler); + } + else + { + return symbol.GetMangledName(nameMangler) + "___REALBASE"; + } + } + else if (symbol is ObjectAndOffsetSymbolNode) + { + ObjectAndOffsetSymbolNode objAndOffset = (ObjectAndOffsetSymbolNode)symbol; + if (objAndOffset.Target is IHasStartSymbol) + { + ISymbolNode startSymbol = ((IHasStartSymbol)objAndOffset.Target).StartSymbol; + if (startSymbol == symbol) + { + Debug.Assert(startSymbol.Offset == 0); + return symbol.GetMangledName(nameMangler); + } + return GetBaseSymbolName(startSymbol, nameMangler, objectWriterUse); + } + return GetBaseSymbolName((ISymbolNode)objAndOffset.Target, nameMangler, objectWriterUse); + } + else if (symbol is EmbeddedObjectNode) + { + EmbeddedObjectNode embeddedNode = (EmbeddedObjectNode)symbol; + return GetBaseSymbolName(embeddedNode.ContainingNode.StartSymbol, nameMangler, objectWriterUse); + } + else + { + return null; + } + } + + private static Dictionary s_symbolValues = new Dictionary(); + private static Dictionary s_staticFieldMapping = new Dictionary(); + + public static LLVMValueRef GetSymbolValuePointer(LLVMModuleRef module, ISymbolNode symbol, NameMangler nameMangler, bool objectWriterUse = false) + { + if (symbol is WebAssemblyMethodCodeNode) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + string symbolAddressGlobalName = symbol.GetMangledName(nameMangler) + "___SYMBOL"; + LLVMValueRef symbolAddress; + if (s_symbolValues.TryGetValue(symbolAddressGlobalName, out symbolAddress)) + { + return symbolAddress; + } + var intPtrType = LLVM.PointerType(LLVM.Int32Type(), 0); + var myGlobal = LLVM.AddGlobalInAddressSpace(module, intPtrType, symbolAddressGlobalName, 0); + LLVM.SetGlobalConstant(myGlobal, (LLVMBool)true); + LLVM.SetLinkage(myGlobal, LLVMLinkage.LLVMInternalLinkage); + s_symbolValues.Add(symbolAddressGlobalName, myGlobal); + return myGlobal; + } + + private static int GetNumericOffsetFromBaseSymbolValue(ISymbolNode symbol) + { + if (symbol is WebAssemblyMethodCodeNode) + { + return 0; + } + + if (symbol is ObjectNode) + { + ISymbolDefinitionNode symbolDefNode = (ISymbolDefinitionNode)symbol; + return symbolDefNode.Offset; + } + else if (symbol is ObjectAndOffsetSymbolNode) + { + ObjectAndOffsetSymbolNode objAndOffset = (ObjectAndOffsetSymbolNode)symbol; + ISymbolDefinitionNode symbolDefNode = (ISymbolDefinitionNode)symbol; + if (objAndOffset.Target is IHasStartSymbol) + { + ISymbolNode startSymbol = ((IHasStartSymbol)objAndOffset.Target).StartSymbol; + + if (startSymbol == symbol) + { + Debug.Assert(symbolDefNode.Offset == 0); + return symbolDefNode.Offset; + } + return symbolDefNode.Offset; + } + int baseOffset = GetNumericOffsetFromBaseSymbolValue((ISymbolNode)objAndOffset.Target); + return baseOffset + symbolDefNode.Offset; + } + else if (symbol is EmbeddedObjectNode) + { + EmbeddedObjectNode embeddedNode = (EmbeddedObjectNode)symbol; + int baseOffset = GetNumericOffsetFromBaseSymbolValue(embeddedNode.ContainingNode.StartSymbol); + return baseOffset + ((ISymbolDefinitionNode)embeddedNode).Offset; + } + else + { + ThrowHelper.ThrowInvalidProgramException(); + return 0; + } + } + + // this is the llvm instance. + public LLVMModuleRef Module { get; } + + // This is used to build mangled names + private Utf8StringBuilder _sb = new Utf8StringBuilder(); + + // Track offsets in node data that prevent writing all bytes in one single blob. This includes + // relocs, symbol definitions, debug data that must be streamed out using the existing LLVM API + private SortedSet _byteInterruptionOffsets = new SortedSet(); + + // Code offset to defined names + private Dictionary> _offsetToDefName = new Dictionary>(); + + // The section for the current node being processed. + private ObjectNodeSection _currentSection; + + // The first defined symbol name of the current node being processed. + private Utf8String _currentNodeZeroTerminatedName; + + // Nodefactory for which ObjectWriter is instantiated for. + private NodeFactory _nodeFactory; + +#if DEBUG + static Dictionary _previouslyWrittenNodeNames = new Dictionary(); +#endif + + public void SetSection(ObjectNodeSection section) + { + _currentSection = section; + } + + public void FinishObjWriter() + { + // Since emission to llvm is delayed until after all nodes are emitted... emit now. + foreach (var nodeData in _dataToFill) + { + nodeData.Fill(Module, _nodeFactory); + } + + EmitNativeMain(); + LLVM.WriteBitcodeToFile(Module, _objectFilePath); +#if DEBUG + LLVM.PrintModuleToFile(Module, Path.ChangeExtension(_objectFilePath, ".txt"), out IntPtr unused2); +#endif //DEBUG + LLVM.VerifyModule(Module, LLVMVerifierFailureAction.LLVMAbortProcessAction, out IntPtr unused); + + //throw new NotImplementedException(); // This function isn't complete + } + + public static LLVMValueRef GetConstZeroArray(int length) + { + var int8Type = LLVM.Int8Type(); + var result = new LLVMValueRef[length]; + for (int i = 0; i < length; i++) + { + result[i] = LLVM.ConstInt(int8Type, 0, LLVMMisc.False); + } + return LLVM.ConstArray(int8Type, result); + } + + public static LLVMValueRef EmitGlobal(LLVMModuleRef module, FieldDesc field, NameMangler nameMangler) + { + if (field.IsStatic) + { + if (s_staticFieldMapping.TryGetValue(field, out LLVMValueRef existingValue)) + return existingValue; + else + { + var valueType = LLVM.ArrayType(LLVM.Int8Type(), (uint)field.FieldType.GetElementSize().AsInt); + var llvmValue = LLVM.AddGlobal(module, valueType, nameMangler.GetMangledFieldName(field).ToString()); + LLVM.SetLinkage(llvmValue, LLVMLinkage.LLVMInternalLinkage); + LLVM.SetInitializer(llvmValue, GetConstZeroArray(field.FieldType.GetElementSize().AsInt)); + if (field.IsThreadStatic) + { + LLVM.SetThreadLocal(llvmValue, LLVMMisc.True); + } + s_staticFieldMapping.Add(field, llvmValue); + return llvmValue; + } + } + else + throw new NotImplementedException(); + } + + + private void EmitNativeMain() + { + LLVMValueRef shadowStackTop = LLVM.GetNamedGlobal(Module, "t_pShadowStackTop"); + + LLVMBuilderRef builder = LLVM.CreateBuilder(); + var mainSignature = LLVM.FunctionType(LLVM.Int32Type(), new LLVMTypeRef[] { LLVM.Int32Type(), LLVM.PointerType(LLVM.Int8Type(), 0) }, false); + var mainFunc = LLVM.AddFunction(Module, "__managed__Main", mainSignature); + var mainEntryBlock = LLVM.AppendBasicBlock(mainFunc, "entry"); + LLVM.PositionBuilderAtEnd(builder, mainEntryBlock); + LLVMValueRef managedMain = LLVM.GetNamedFunction(Module, "StartupCodeMain"); + if (managedMain.Pointer == IntPtr.Zero) + { + throw new Exception("Main not found"); + } + + LLVMTypeRef reversePInvokeFrameType = LLVM.StructType(new LLVMTypeRef[] { LLVM.PointerType(LLVM.Int8Type(), 0), LLVM.PointerType(LLVM.Int8Type(), 0) }, false); + LLVMValueRef reversePinvokeFrame = LLVM.BuildAlloca(builder, reversePInvokeFrameType, "ReversePInvokeFrame"); + LLVMValueRef RhpReversePInvoke2 = LLVM.AddFunction(Module, "RhpReversePInvoke2", LLVM.FunctionType(LLVM.VoidType(), new LLVMTypeRef[] { LLVM.PointerType(reversePInvokeFrameType, 0) }, false)); + LLVM.BuildCall(builder, RhpReversePInvoke2, new LLVMValueRef[] { reversePinvokeFrame }, ""); + + var shadowStack = LLVM.BuildMalloc(builder, LLVM.ArrayType(LLVM.Int8Type(), 1000000), String.Empty); + var castShadowStack = LLVM.BuildPointerCast(builder, shadowStack, LLVM.PointerType(LLVM.Int8Type(), 0), String.Empty); + LLVM.BuildStore(builder, castShadowStack, shadowStackTop); + LLVM.BuildCall(builder, managedMain, new LLVMValueRef[] + { + castShadowStack, + LLVM.ConstPointerNull(LLVM.PointerType(LLVM.Int8Type(), 0)) + }, + String.Empty); + + LLVM.BuildRet(builder, LLVM.ConstInt(LLVM.Int32Type(), 42, LLVMMisc.False)); + LLVM.SetLinkage(mainFunc, LLVMLinkage.LLVMExternalLinkage); + } + + public void SetCodeSectionAttribute(ObjectNodeSection section) + { + //throw new NotImplementedException(); // This function isn't complete + } + + public void EnsureCurrentSection() + { + } + + ArrayBuilder _currentObjectData = new ArrayBuilder(); + struct SymbolRefData + { + public SymbolRefData(bool isFunction, string symbolName, int offset) + { + IsFunction = isFunction; + SymbolName = symbolName; + Offset = offset; + } + + readonly bool IsFunction; + readonly string SymbolName; + readonly int Offset; + + public LLVMValueRef ToLLVMValueRef(LLVMModuleRef module) + { + LLVMValueRef valRef = IsFunction ? LLVM.GetNamedFunction(module, SymbolName) : LLVM.GetNamedGlobal(module, SymbolName); + + if (Offset != 0 && valRef.Pointer != IntPtr.Zero) + { + var pointerType = LLVM.PointerType(LLVM.Int8Type(), 0); + var bitCast = LLVM.ConstBitCast(valRef, pointerType); + LLVMValueRef[] index = new LLVMValueRef[] {LLVM.ConstInt(LLVM.Int32Type(), (uint)Offset, (LLVMBool)false)}; + valRef = LLVM.ConstGEP(bitCast, index); + } + + return valRef; + } + } + + Dictionary _currentObjectSymbolRefs = new Dictionary(); + ObjectNode _currentObjectNode; + + List _dataToFill = new List(); + + List> _symbolDefs = new List>(); + + struct ObjectNodeDataEmission + { + public ObjectNodeDataEmission(LLVMValueRef node, byte[] data, Dictionary objectSymbolRefs) + { + Node = node; + Data = data; + ObjectSymbolRefs = objectSymbolRefs; + } + LLVMValueRef Node; + readonly byte[] Data; + readonly Dictionary ObjectSymbolRefs; + + public void Fill(LLVMModuleRef module, NodeFactory nodeFactory) + { + List entries = new List(); + int pointerSize = nodeFactory.Target.PointerSize; + + int countOfPointerSizedElements = Data.Length / pointerSize; + + byte[] currentObjectData = Data; + var intPtrType = LLVM.PointerType(LLVM.Int32Type(), 0); + var intType = LLVM.Int32Type(); + + var int8PtrType = LLVM.PointerType(LLVM.Int8Type(), 0); + + for (int i = 0; i < countOfPointerSizedElements; i++) + { + int curOffset = (i * pointerSize); + SymbolRefData symbolRef; + if (ObjectSymbolRefs.TryGetValue(curOffset, out symbolRef)) + { + LLVMValueRef pointedAtValue = symbolRef.ToLLVMValueRef(module); + //TODO: why did this come back null + if (pointedAtValue.Pointer != IntPtr.Zero) + { + var ptrValue = LLVM.ConstBitCast(pointedAtValue, intPtrType); + entries.Add(ptrValue); + } + else + { + entries.Add(LLVM.ConstPointerNull(intPtrType)); + } + } + else + { + int value = BitConverter.ToInt32(currentObjectData, curOffset); + var nullptr = LLVM.ConstPointerNull(int8PtrType); + var dataVal = LLVM.ConstInt(intType, (uint)value, (LLVMBool)false); + var ptrValAsInt8Ptr = LLVM.ConstGEP(nullptr, new LLVMValueRef[] { dataVal }); + + var ptrValue = LLVM.ConstBitCast(ptrValAsInt8Ptr, intPtrType); + entries.Add(ptrValue); + } + } + + var funcptrarray = LLVM.ConstArray(intPtrType, entries.ToArray()); + LLVM.SetInitializer(Node, funcptrarray); + } + } + + public void StartObjectNode(ObjectNode node) + { + Debug.Assert(_currentObjectNode == null); + _currentObjectNode = node; + Debug.Assert(_currentObjectData.Count == 0); + } + + public void DoneObjectNode() + { + int pointerSize = _nodeFactory.Target.PointerSize; + EmitAlignment(_nodeFactory.Target.PointerSize); + Debug.Assert(_nodeFactory.Target.PointerSize == 4); + int countOfPointerSizedElements = _currentObjectData.Count / _nodeFactory.Target.PointerSize; + + ISymbolNode symNode = _currentObjectNode as ISymbolNode; + if (symNode == null) + symNode = ((IHasStartSymbol)_currentObjectNode).StartSymbol; + string realName = GetBaseSymbolName(symNode, _nodeFactory.NameMangler, true); + + var intPtrType = LLVM.PointerType(LLVM.Int32Type(), 0); + var arrayglobal = LLVM.AddGlobalInAddressSpace(Module, LLVM.ArrayType(intPtrType, (uint)countOfPointerSizedElements), realName, 0); + LLVM.SetLinkage(arrayglobal, LLVMLinkage.LLVMExternalLinkage); + + _dataToFill.Add(new ObjectNodeDataEmission(arrayglobal, _currentObjectData.ToArray(), _currentObjectSymbolRefs)); + + foreach (var symbolIdInfo in _symbolDefs) + { + EmitSymbolDef(arrayglobal, symbolIdInfo.Key, symbolIdInfo.Value); + } + + _currentObjectNode = null; + _currentObjectSymbolRefs = new Dictionary(); + _currentObjectData = new ArrayBuilder(); + _symbolDefs.Clear(); + } + + public void EmitAlignment(int byteAlignment) + { + while ((_currentObjectData.Count % byteAlignment) != 0) + _currentObjectData.Add(0); + } + + public void EmitBlob(byte[] blob) + { + _currentObjectData.Append(blob); + } + + public void EmitIntValue(ulong value, int size) + { + switch (size) + { + case 1: + _currentObjectData.Append(BitConverter.GetBytes((byte)value)); + break; + case 2: + _currentObjectData.Append(BitConverter.GetBytes((ushort)value)); + break; + case 4: + _currentObjectData.Append(BitConverter.GetBytes((uint)value)); + break; + case 8: + _currentObjectData.Append(BitConverter.GetBytes(value)); + break; + default: + ThrowHelper.ThrowInvalidProgramException(); + break; + } + } + + public void EmitBytes(IntPtr pArray, int length) + { + unsafe + { + byte* pBytes = (byte*)pArray; + for (int i = 0; i < length; i++) + _currentObjectData.Add(pBytes[i]); + } + } + + public void EmitSymbolDef(LLVMValueRef realSymbol, string symbolIdentifier, int offsetFromSymbolName) + { + string symbolAddressGlobalName = symbolIdentifier + "___SYMBOL"; + LLVMValueRef symbolAddress; + var intType = LLVM.Int32Type(); + if (s_symbolValues.TryGetValue(symbolAddressGlobalName, out symbolAddress)) + { + var int8PtrType = LLVM.PointerType(LLVM.Int8Type(), 0); + var intPtrType = LLVM.PointerType(LLVM.Int32Type(), 0); + var pointerToRealSymbol = LLVM.ConstBitCast(realSymbol, int8PtrType); + var offsetValue = LLVM.ConstInt(intType, (uint)offsetFromSymbolName, (LLVMBool)false); + var symbolPointerData = LLVM.ConstGEP(pointerToRealSymbol, new LLVMValueRef[] { offsetValue }); + var symbolPointerDataAsInt32Ptr = LLVM.ConstBitCast(symbolPointerData, intPtrType); + LLVM.SetInitializer(symbolAddress, symbolPointerDataAsInt32Ptr); + } + } + + public int EmitSymbolRef(string realSymbolName, int offsetFromSymbolName, bool isFunction, RelocType relocType, int delta = 0) + { + int symbolStartOffset = _currentObjectData.Count; + + // Workaround for ObjectWriter's lack of support for IMAGE_REL_BASED_RELPTR32 + // https://github.com/dotnet/corert/issues/3278 + if (relocType == RelocType.IMAGE_REL_BASED_RELPTR32) + { + relocType = RelocType.IMAGE_REL_BASED_REL32; + delta = checked(delta + sizeof(int)); + } + + EmitBlob(new byte[this._nodeFactory.Target.PointerSize]); + if (relocType == RelocType.IMAGE_REL_BASED_REL32) + { + return this._nodeFactory.Target.PointerSize; + } + + _currentObjectSymbolRefs.Add(symbolStartOffset, new SymbolRefData(isFunction, realSymbolName, delta)); + return _nodeFactory.Target.PointerSize; + } + + public string GetMangledName(TypeDesc type) + { + return _nodeFactory.NameMangler.GetMangledTypeName(type); + } + + public void BuildSymbolDefinitionMap(ObjectNode node, ISymbolDefinitionNode[] definedSymbols) + { + _offsetToDefName.Clear(); + foreach (ISymbolDefinitionNode n in definedSymbols) + { + if (!_offsetToDefName.ContainsKey(n.Offset)) + { + _offsetToDefName[n.Offset] = new List(); + } + + _offsetToDefName[n.Offset].Add(n); + _byteInterruptionOffsets.Add(n.Offset); + } + + var symbolNode = node as ISymbolDefinitionNode; + if (symbolNode != null) + { + _sb.Clear(); + AppendExternCPrefix(_sb); + symbolNode.AppendMangledName(_nodeFactory.NameMangler, _sb); + _currentNodeZeroTerminatedName = _sb.Append('\0').ToUtf8String(); + } + else + { + _currentNodeZeroTerminatedName = default(Utf8String); + } + } + + private void AppendExternCPrefix(Utf8StringBuilder sb) + { + } + + // Returns size of the emitted symbol reference + public int EmitSymbolReference(ISymbolNode target, int delta, RelocType relocType) + { + string realSymbolName = GetBaseSymbolName(target, _nodeFactory.NameMangler, true); + + if (realSymbolName == null) + { + Console.WriteLine("Unable to generate symbolRef to " + target.GetMangledName(_nodeFactory.NameMangler)); + + int pointerSize = _nodeFactory.Target.PointerSize; + EmitBlob(new byte[pointerSize]); + return pointerSize; + } + int offsetFromBase = GetNumericOffsetFromBaseSymbolValue(target); + return EmitSymbolRef(realSymbolName, offsetFromBase, target is WebAssemblyMethodCodeNode, relocType, delta); + } + + public void EmitBlobWithRelocs(byte[] blob, Relocation[] relocs) + { + int nextRelocOffset = -1; + int nextRelocIndex = -1; + if (relocs.Length > 0) + { + nextRelocOffset = relocs[0].Offset; + nextRelocIndex = 0; + } + + int i = 0; + while (i < blob.Length) + { + if (i == nextRelocOffset) + { + Relocation reloc = relocs[nextRelocIndex]; + + long delta; + unsafe + { + fixed (void* location = &blob[i]) + { + delta = Relocation.ReadValue(reloc.RelocType, location); + } + } + int size = EmitSymbolReference(reloc.Target, (int)delta, reloc.RelocType); + + // Update nextRelocIndex/Offset + if (++nextRelocIndex < relocs.Length) + { + nextRelocOffset = relocs[nextRelocIndex].Offset; + } + i += size; + } + else + { + EmitIntValue(blob[i], 1); + i++; + } + } + } + + public void EmitSymbolDefinition(int currentOffset) + { + List nodes; + if (_offsetToDefName.TryGetValue(currentOffset, out nodes)) + { + foreach (var name in nodes) + { + _sb.Clear(); + AppendExternCPrefix(_sb); + name.AppendMangledName(_nodeFactory.NameMangler, _sb); + + string symbolId = name.GetMangledName(_nodeFactory.NameMangler); + int offsetFromBase = GetNumericOffsetFromBaseSymbolValue(name); + Debug.Assert(offsetFromBase == currentOffset); + + _symbolDefs.Add(new KeyValuePair(symbolId, offsetFromBase)); + /* + string alternateName = _nodeFactory.GetSymbolAlternateName(name); + if (alternateName != null) + { + _sb.Clear(); + //AppendExternCPrefix(_sb); + _sb.Append(alternateName); + + EmitSymbolDef(_sb); + }*/ + } + } + } + + //System.IO.FileStream _file; + string _objectFilePath; + + public WebAssemblyObjectWriter(string objectFilePath, NodeFactory factory, WebAssemblyCodegenCompilation compilation) + { + _nodeFactory = factory; + _objectFilePath = objectFilePath; + Module = compilation.Module; + } + + public void Dispose() + { + Dispose(true); + } + + public virtual void Dispose(bool bDisposing) + { + FinishObjWriter(); + //if (_file != null) + //{ + // // Finalize object emission. + // FinishObjWriter(); + // _file.Flush(); + // _file.Dispose(); + // _file = null; + //} + + _nodeFactory = null; + + if (bDisposing) + { + GC.SuppressFinalize(this); + } + } + + ~WebAssemblyObjectWriter() + { + Dispose(false); + } + + private bool ShouldShareSymbol(ObjectNode node) + { + if (_nodeFactory.CompilationModuleGroup.IsSingleFileCompilation) + return false; + + if (!(node is ISymbolNode)) + return false; + + // These intentionally clash with one another, but are merged with linker directives so should not be Comdat folded + if (node is ModulesSectionNode) + return false; + + return true; + } + + private ObjectNodeSection GetSharedSection(ObjectNodeSection section, string key) + { + string standardSectionPrefix = ""; + if (section.IsStandardSection) + standardSectionPrefix = "."; + + return new ObjectNodeSection(standardSectionPrefix + section.Name, section.Type, key); + } + + public void ResetByteRunInterruptionOffsets(Relocation[] relocs) + { + _byteInterruptionOffsets.Clear(); + + for (int i = 0; i < relocs.Length; ++i) + { + _byteInterruptionOffsets.Add(relocs[i].Offset); + } + } + + private static int GetVTableSlotsCount(NodeFactory factory, TypeDesc type) + { + if (type == null) + return 0; + int slotsOnCurrentType = factory.VTable(type).Slots.Count; + return slotsOnCurrentType + GetVTableSlotsCount(factory, type.BaseType); + } + + public static void EmitObject(string objectFilePath, IEnumerable nodes, NodeFactory factory, WebAssemblyCodegenCompilation compilation, IObjectDumper dumper) + { + WebAssemblyObjectWriter objectWriter = new WebAssemblyObjectWriter(objectFilePath, factory, compilation); + bool succeeded = false; + + try + { + //ObjectNodeSection managedCodeSection = null; + + var listOfOffsets = new List(); + foreach (DependencyNode depNode in nodes) + { + ObjectNode node = depNode as ObjectNode; + if (node == null) + continue; + + if (node.ShouldSkipEmittingObjectNode(factory)) + continue; + + objectWriter.StartObjectNode(node); + ObjectData nodeContents = node.GetData(factory); + + if (dumper != null) + dumper.DumpObjectNode(factory.NameMangler, node, nodeContents); + +#if DEBUG + foreach (ISymbolNode definedSymbol in nodeContents.DefinedSymbols) + { + try + { + _previouslyWrittenNodeNames.Add(definedSymbol.GetMangledName(factory.NameMangler), definedSymbol); + } + catch (ArgumentException) + { + ISymbolNode alreadyWrittenSymbol = _previouslyWrittenNodeNames[definedSymbol.GetMangledName(factory.NameMangler)]; + Debug.Fail("Duplicate node name emitted to file", + $"Symbol {definedSymbol.GetMangledName(factory.NameMangler)} has already been written to the output object file {objectFilePath} with symbol {alreadyWrittenSymbol}"); + } + } +#endif + + ObjectNodeSection section = node.Section; + if (objectWriter.ShouldShareSymbol(node)) + { + section = objectWriter.GetSharedSection(section, ((ISymbolNode)node).GetMangledName(factory.NameMangler)); + } + + // Ensure section and alignment for the node. + objectWriter.SetSection(section); + objectWriter.EmitAlignment(nodeContents.Alignment); + + objectWriter.ResetByteRunInterruptionOffsets(nodeContents.Relocs); + + // Build symbol definition map. + objectWriter.BuildSymbolDefinitionMap(node, nodeContents.DefinedSymbols); + + Relocation[] relocs = nodeContents.Relocs; + int nextRelocOffset = -1; + int nextRelocIndex = -1; + if (relocs.Length > 0) + { + nextRelocOffset = relocs[0].Offset; + nextRelocIndex = 0; + } + + int i = 0; + + listOfOffsets.Clear(); + listOfOffsets.AddRange(objectWriter._byteInterruptionOffsets); + + int offsetIndex = 0; + while (i < nodeContents.Data.Length) + { + // Emit symbol definitions if necessary + objectWriter.EmitSymbolDefinition(i); + + if (i == nextRelocOffset) + { + Relocation reloc = relocs[nextRelocIndex]; + + long delta; + unsafe + { + fixed (void* location = &nodeContents.Data[i]) + { + delta = Relocation.ReadValue(reloc.RelocType, location); + } + } + int size = objectWriter.EmitSymbolReference(reloc.Target, (int)delta, reloc.RelocType); + + /* + WebAssembly has no thumb + // Emit a copy of original Thumb2 instruction that came from RyuJIT + if (reloc.RelocType == RelocType.IMAGE_REL_BASED_THUMB_MOV32 || + reloc.RelocType == RelocType.IMAGE_REL_BASED_THUMB_BRANCH24) + { + unsafe + { + fixed (void* location = &nodeContents.Data[i]) + { + objectWriter.EmitBytes((IntPtr)location, size); + } + } + }*/ + + // Update nextRelocIndex/Offset + if (++nextRelocIndex < relocs.Length) + { + nextRelocOffset = relocs[nextRelocIndex].Offset; + } + else + { + // This is the last reloc. Set the next reloc offset to -1 in case the last reloc has a zero size, + // which means the reloc does not have vacant bytes corresponding to in the data buffer. E.g, + // IMAGE_REL_THUMB_BRANCH24 is a kind of 24-bit reloc whose bits scatte over the instruction that + // references it. We do not vacate extra bytes in the data buffer for this kind of reloc. + nextRelocOffset = -1; + } + i += size; + } + else + { + while (offsetIndex < listOfOffsets.Count && listOfOffsets[offsetIndex] <= i) + { + offsetIndex++; + } + + int nextOffset = offsetIndex == listOfOffsets.Count ? nodeContents.Data.Length : listOfOffsets[offsetIndex]; + + unsafe + { + // Todo: Use Span instead once it's available to us in this repo + fixed (byte* pContents = &nodeContents.Data[i]) + { + objectWriter.EmitBytes((IntPtr)(pContents), nextOffset - i); + i += nextOffset - i; + } + } + + } + } + Debug.Assert(i == nodeContents.Data.Length); + + // It is possible to have a symbol just after all of the data. + objectWriter.EmitSymbolDefinition(nodeContents.Data.Length); + objectWriter.DoneObjectNode(); + } + + succeeded = true; + } + finally + { + objectWriter.Dispose(); + + if (!succeeded) + { + // If there was an exception while generating the OBJ file, make sure we don't leave the unfinished + // object file around. + try + { + File.Delete(objectFilePath); + } + catch + { + } + } + } + } + } +} diff --git a/external/corert/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/RawMainMethodRootProvider.cs b/external/corert/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/RawMainMethodRootProvider.cs new file mode 100644 index 0000000000..b1055d8ee7 --- /dev/null +++ b/external/corert/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/RawMainMethodRootProvider.cs @@ -0,0 +1,29 @@ +// 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 Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler +{ + public class RawMainMethodRootProvider : ICompilationRootProvider + { + private EcmaModule _module; + + public RawMainMethodRootProvider(EcmaModule module) + { + _module = module; + } + + public void AddCompilationRoots(IRootingServiceProvider rootProvider) + { + MethodDesc mainMethod = _module.EntryPoint; + if (mainMethod == null) + throw new Exception("No managed entrypoint defined for executable module"); + + rootProvider.AddCompilationRoot(mainMethod, "Managed Main Method"); + } + } +} diff --git a/external/corert/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyCodegenNodeFactory.cs b/external/corert/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyCodegenNodeFactory.cs new file mode 100644 index 0000000000..a43a1e5729 --- /dev/null +++ b/external/corert/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyCodegenNodeFactory.cs @@ -0,0 +1,63 @@ +// 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 ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public sealed class WebAssemblyCodegenNodeFactory : NodeFactory + { + private NodeCache _vTableSlotNodes; + + public WebAssemblyCodegenNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager, + InteropStubManager interopStubManager, NameMangler nameMangler, VTableSliceProvider vtableSliceProvider, DictionaryLayoutProvider dictionaryLayoutProvider) + : base(context, + compilationModuleGroup, + metadataManager, + interopStubManager, + nameMangler, + new LazyGenericsDisabledPolicy(), + vtableSliceProvider, + dictionaryLayoutProvider, + new ImportedNodeProviderThrowing()) + { + _vTableSlotNodes = new NodeCache(methodKey => + { + return new WebAssemblyVTableSlotNode(methodKey); + }); + } + + public override bool IsCppCodegenTemporaryWorkaround => true; + + protected override IMethodNode CreateMethodEntrypointNode(MethodDesc method) + { + if (CompilationModuleGroup.ContainsMethodBody(method, false)) + { + return new WebAssemblyMethodCodeNode(method); + } + else + { + return new ExternMethodSymbolNode(this, method); + } + } + + public WebAssemblyVTableSlotNode VTableSlot(MethodDesc method) + { + return _vTableSlotNodes.GetOrAdd(method); + } + + protected override IMethodNode CreateUnboxingStubNode(MethodDesc method) + { + return new WebAssemblyMethodCodeNode(TypeSystemContext.GetUnboxingThunk(method, CompilationModuleGroup.GeneratedAssembly)); + } + + protected override ISymbolNode CreateReadyToRunHelperNode(ReadyToRunHelperKey helperCall) + { + // TODO: this is wrong: this returns an assembly stub node + return new ReadyToRunHelperNode(this, helperCall.HelperId, helperCall.Target); + } + } +} diff --git a/external/corert/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyMethodCodeNode.cs b/external/corert/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyMethodCodeNode.cs new file mode 100644 index 0000000000..0cfd4382c2 --- /dev/null +++ b/external/corert/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyMethodCodeNode.cs @@ -0,0 +1,78 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using ILCompiler.DependencyAnalysisFramework; + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + internal class WebAssemblyMethodCodeNode : DependencyNodeCore, IMethodBodyNode + { + private MethodDesc _method; + private IEnumerable _dependencies = Enumerable.Empty(); + + public WebAssemblyMethodCodeNode(MethodDesc method) + { + Debug.Assert(!method.IsAbstract); + _method = method; + } + + public void SetDependencies(IEnumerable dependencies) + { + Debug.Assert(dependencies != null); + _dependencies = dependencies; + } + + public MethodDesc Method + { + get + { + return _method; + } + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override bool StaticDependenciesAreComputed => CompilationCompleted; + + public bool CompilationCompleted { get; set; } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.GetMangledMethodName(_method)); + } + public int Offset => 0; + public bool RepresentsIndirectionCell => false; + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + var dependencies = new DependencyList(); + + foreach (Object node in _dependencies) + dependencies.Add(node, "Wasm code "); + + return dependencies; + } + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + + int ISortableSymbolNode.ClassCode => -1502960727; + + int ISortableSymbolNode.CompareToImpl(ISortableSymbolNode other, CompilerComparer comparer) + { + return comparer.Compare(_method, ((WebAssemblyMethodCodeNode)other)._method); + } + } +} diff --git a/external/corert/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyVTableSlotNode.cs b/external/corert/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyVTableSlotNode.cs new file mode 100644 index 0000000000..6f7897a8f0 --- /dev/null +++ b/external/corert/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyVTableSlotNode.cs @@ -0,0 +1,77 @@ +// 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.Diagnostics; + +using Internal.Runtime; +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public class WebAssemblyVTableSlotNode : ObjectNode, ISymbolDefinitionNode + { + private readonly MethodDesc _targetMethod; + + public WebAssemblyVTableSlotNode(MethodDesc targetMethod) + { + Debug.Assert(targetMethod.IsVirtual); + Debug.Assert(!targetMethod.IsSharedByGenericInstantiations); + _targetMethod = targetMethod; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(GetMangledName(nameMangler, _targetMethod)); + } + public int Offset => 0; + + public override bool IsShareable => false; + + public static string GetMangledName(NameMangler nameMangler, MethodDesc method) + { + return "__getslot__" + nameMangler.GetMangledMethodName(method); + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectNodeSection Section => ObjectNodeSection.DataSection; + + public override bool StaticDependenciesAreComputed => true; + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList result = new DependencyList(); + + if (!factory.VTable(_targetMethod.OwningType).HasFixedSlots) + { + result.Add(factory.VirtualMethodUse(_targetMethod), "VTable method use"); + } + + return result; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + Debug.Assert((EETypeNode.GetVTableOffset(factory.Target.PointerSize) % factory.Target.PointerSize) == 0, "vtable offset must be aligned"); + ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); + + objData.AddSymbol(this); + + if (!relocsOnly) + { + var tableOffset = EETypeNode.GetVTableOffset(factory.Target.PointerSize) / factory.Target.PointerSize; + objData.EmitInt(tableOffset + VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, _targetMethod)); + } + return objData.ToObjectData(); + } + + protected override int ClassCode => 0; + + protected override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer) + { + return comparer.Compare(_targetMethod, ((WebAssemblyVTableSlotNode)other)._targetMethod); + } + } +} diff --git a/external/corert/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs b/external/corert/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs new file mode 100644 index 0000000000..94b35a3f47 --- /dev/null +++ b/external/corert/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs @@ -0,0 +1,58 @@ +// 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.Collections.Generic; + +using Internal.TypeSystem; + +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; +using LLVMSharp; + +namespace ILCompiler +{ + public sealed class WebAssemblyCodegenCompilation : Compilation + { + internal WebAssemblyCodegenConfigProvider Options { get; } + internal LLVMModuleRef Module { get; } + public new WebAssemblyCodegenNodeFactory NodeFactory { get; } + internal WebAssemblyCodegenCompilation( + DependencyAnalyzerBase dependencyGraph, + WebAssemblyCodegenNodeFactory nodeFactory, + IEnumerable roots, + Logger logger, + WebAssemblyCodegenConfigProvider options) + : base(dependencyGraph, nodeFactory, GetCompilationRoots(roots, nodeFactory), null, null, logger) + { + NodeFactory = nodeFactory; + LLVM.LoadLibrary_libLLVM("./libLLVM-x64.dll"); + Module = LLVM.ModuleCreateWithName("netscripten"); + LLVM.SetTarget(Module, "asmjs-unknown-emscripten"); + Options = options; + } + + private static IEnumerable GetCompilationRoots(IEnumerable existingRoots, NodeFactory factory) + { + foreach (var existingRoot in existingRoots) + yield return existingRoot; + } + + protected override void CompileInternal(string outputFile, ObjectDumper dumper) + { + _dependencyGraph.ComputeMarkedNodes(); + + var nodes = _dependencyGraph.MarkedNodeList; + + WebAssemblyObjectWriter.EmitObject(outputFile, nodes, NodeFactory, this, dumper); + } + + protected override void ComputeDependencyNodeDependencies(List> obj) + { + foreach (WebAssemblyMethodCodeNode methodCodeNodeNeedingCode in obj) + { + Internal.IL.ILImporter.CompileMethod(this, methodCodeNodeNeedingCode); + } + } + } +} diff --git a/external/corert/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilationBuilder.cs b/external/corert/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilationBuilder.cs new file mode 100644 index 0000000000..48bfaa15ba --- /dev/null +++ b/external/corert/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilationBuilder.cs @@ -0,0 +1,58 @@ +// 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.Collections.Generic; + +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; + +using Internal.TypeSystem; + +namespace ILCompiler +{ + public sealed class WebAssemblyCodegenCompilationBuilder : CompilationBuilder + { + // These need to provide reasonable defaults so that the user can optionally skip + // calling the Use/Configure methods and still get something reasonable back. + WebAssemblyCodegenConfigProvider _config = new WebAssemblyCodegenConfigProvider(Array.Empty()); + + public WebAssemblyCodegenCompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup group) + : base(context, group, new CoreRTNameMangler(new WebAssemblyNodeMangler(), false)) + { + } + + public override CompilationBuilder UseBackendOptions(IEnumerable options) + { + _config = new WebAssemblyCodegenConfigProvider(options); + return this; + } + + public override ICompilation ToCompilation() + { + var interopStubManager = new CompilerGeneratedInteropStubManager(_compilationGroup, _context, new InteropStateManager(_compilationGroup.GeneratedAssembly)); + WebAssemblyCodegenNodeFactory factory = new WebAssemblyCodegenNodeFactory(_context, _compilationGroup, _metadataManager, interopStubManager, _nameMangler, _vtableSliceProvider, _dictionaryLayoutProvider); + DependencyAnalyzerBase graph = CreateDependencyGraph(factory); + + return new WebAssemblyCodegenCompilation(graph, factory, _compilationRoots, _logger, _config); + } + } + + internal class WebAssemblyCodegenConfigProvider + { + private readonly HashSet _options; + + public const string NoLineNumbersString = "NoLineNumbers"; + + public WebAssemblyCodegenConfigProvider(IEnumerable options) + { + _options = new HashSet(options, StringComparer.OrdinalIgnoreCase); + } + + public bool HasOption(string optionName) + { + return _options.Contains(optionName); + } + } +} diff --git a/external/corert/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyNodeMangler.cs b/external/corert/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyNodeMangler.cs new file mode 100644 index 0000000000..e0b3e80aa6 --- /dev/null +++ b/external/corert/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyNodeMangler.cs @@ -0,0 +1,53 @@ +// 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 Internal.Text; +using Internal.TypeSystem; +using System.Diagnostics; + +namespace ILCompiler +{ + // + // The naming format of these names is known to the debugger + // + public sealed class WebAssemblyNodeMangler : NodeMangler + { + // Mangled name of boxed version of a type + public sealed override string MangledBoxedTypeName(TypeDesc type) + { + Debug.Assert(type.IsValueType); + return "Boxed_" + NameMangler.GetMangledTypeName(type); + } + + public sealed override string EEType(TypeDesc type) + { + return "__EEType_" + NameMangler.GetMangledTypeName(type); + } + + public sealed override string GCStatics(TypeDesc type) + { + return "__GCStaticBase_" + NameMangler.GetMangledTypeName(type); + } + + public sealed override string NonGCStatics(TypeDesc type) + { + return "__NonGCStaticBase_" + NameMangler.GetMangledTypeName(type); + } + + public sealed override string ThreadStatics(TypeDesc type) + { + return "__ThreadStaticBase_" + NameMangler.GetMangledTypeName(type); + } + + public sealed override string TypeGenericDictionary(TypeDesc type) + { + return GenericDictionaryNamePrefix + NameMangler.GetMangledTypeName(type); + } + + public sealed override string MethodGenericDictionary(MethodDesc method) + { + return GenericDictionaryNamePrefix + NameMangler.GetMangledMethodName(method); + } + } +} diff --git a/external/corert/src/ILCompiler.WebAssembly/src/ILCompiler.WebAssembly.csproj b/external/corert/src/ILCompiler.WebAssembly/src/ILCompiler.WebAssembly.csproj new file mode 100644 index 0000000000..cd88923c71 --- /dev/null +++ b/external/corert/src/ILCompiler.WebAssembly/src/ILCompiler.WebAssembly.csproj @@ -0,0 +1,64 @@ + + + + + Library + ILCompiler + ILCompiler.WebAssembly + true + netstandard1.3 + 8002 + $(BaseOutputPath)$(OSPlatformConfig)/tools + + + + 3.9.1-rc3 + + + 3.9.1 + + + + + + + + + + + + IL\ILImporter.cs + + + Common\ArrayBuilder.cs + + + Common\FormattingHelpers.cs + + + IL\HelperExtensions.cs + + + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + + diff --git a/external/corert/src/ILCompiler/ILCompiler.sln b/external/corert/src/ILCompiler/ILCompiler.sln index 50edff4b53..c210088aa8 100644 --- a/external/corert/src/ILCompiler/ILCompiler.sln +++ b/external/corert/src/ILCompiler/ILCompiler.sln @@ -1,16 +1,14 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26507.0 +VisualStudioVersion = 15.0.26828.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "desktop", "desktop\desktop.csproj", "{B2C35178-5E42-4010-A72A-938711D7F8F0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler", "src\ILCompiler.csproj", "{DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}" ProjectSection(ProjectDependencies) = postProject {FBA029C3-B184-4457-AEEC-38D0C2523067} = {FBA029C3-B184-4457-AEEC-38D0C2523067} EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler", "src\ILCompiler.csproj", "{DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "repro", "repro\repro.csproj", "{FBA029C3-B184-4457-AEEC-38D0C2523067}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "repro", "repro\repro.csproj", "{FBA029C3-B184-4457-AEEC-38D0C2523067}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.TypeSystem", "..\ILCompiler.TypeSystem\src\ILCompiler.TypeSystem.csproj", "{1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}" EndProject @@ -26,6 +24,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.MetadataTransfor EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.CppCodegen", "..\ILCompiler.CppCodegen\src\ILCompiler.CppCodegen.csproj", "{971AE7E7-08C6-48B4-902A-63851E6DAC66}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.WebAssembly", "..\ILCompiler.WebAssembly\src\ILCompiler.WebAssembly.csproj", "{5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "desktop", "desktop\desktop.csproj", "{CE9781B1-0028-4039-A48C-8193F0D28467}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.Build.Tasks", "..\ILCompiler.Build.Tasks\src\ILCompiler.Build.Tasks.csproj", "{44DE7F7B-AD73-40EA-87CC-554F2F57C38A}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution ..\System.Private.CoreLib\shared\System.Private.CoreLib.Shared.projitems*{be95c560-b508-4588-8907-f9fc5bc1a0cf}*SharedItemsImports = 4 @@ -34,255 +38,248 @@ Global Debug|Any CPU = Debug|Any CPU Debug|arm = Debug|arm Debug|arm64 = Debug|arm64 - Debug|armel = Debug|armel Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|arm = Release|arm Release|arm64 = Release|arm64 - Release|armel = Release|armel Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B2C35178-5E42-4010-A72A-938711D7F8F0}.Debug|Any CPU.ActiveCfg = Debug|x86 - {B2C35178-5E42-4010-A72A-938711D7F8F0}.Debug|arm.ActiveCfg = Debug|arm - {B2C35178-5E42-4010-A72A-938711D7F8F0}.Debug|arm.Build.0 = Debug|arm - {B2C35178-5E42-4010-A72A-938711D7F8F0}.Debug|arm64.ActiveCfg = Debug|arm64 - {B2C35178-5E42-4010-A72A-938711D7F8F0}.Debug|arm64.Build.0 = Debug|arm64 - {B2C35178-5E42-4010-A72A-938711D7F8F0}.Debug|armel.ActiveCfg = Debug|armel - {B2C35178-5E42-4010-A72A-938711D7F8F0}.Debug|armel.Build.0 = Debug|armel - {B2C35178-5E42-4010-A72A-938711D7F8F0}.Debug|x64.ActiveCfg = Debug|x64 - {B2C35178-5E42-4010-A72A-938711D7F8F0}.Debug|x64.Build.0 = Debug|x64 - {B2C35178-5E42-4010-A72A-938711D7F8F0}.Debug|x86.ActiveCfg = Debug|x86 - {B2C35178-5E42-4010-A72A-938711D7F8F0}.Debug|x86.Build.0 = Debug|x86 - {B2C35178-5E42-4010-A72A-938711D7F8F0}.Release|Any CPU.ActiveCfg = Release|x86 - {B2C35178-5E42-4010-A72A-938711D7F8F0}.Release|arm.ActiveCfg = Release|arm - {B2C35178-5E42-4010-A72A-938711D7F8F0}.Release|arm.Build.0 = Release|arm - {B2C35178-5E42-4010-A72A-938711D7F8F0}.Release|arm64.ActiveCfg = Release|arm64 - {B2C35178-5E42-4010-A72A-938711D7F8F0}.Release|arm64.Build.0 = Release|arm64 - {B2C35178-5E42-4010-A72A-938711D7F8F0}.Release|armel.ActiveCfg = Release|armel - {B2C35178-5E42-4010-A72A-938711D7F8F0}.Release|armel.Build.0 = Release|armel - {B2C35178-5E42-4010-A72A-938711D7F8F0}.Release|x64.ActiveCfg = Release|x64 - {B2C35178-5E42-4010-A72A-938711D7F8F0}.Release|x64.Build.0 = Release|x64 - {B2C35178-5E42-4010-A72A-938711D7F8F0}.Release|x86.ActiveCfg = Release|x86 - {B2C35178-5E42-4010-A72A-938711D7F8F0}.Release|x86.Build.0 = Release|x86 - {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|Any CPU.ActiveCfg = Debug|x86 {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|arm.ActiveCfg = Debug|arm {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|arm.Build.0 = Debug|arm {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|arm64.ActiveCfg = Debug|arm64 {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|arm64.Build.0 = Debug|arm64 - {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|armel.ActiveCfg = Debug|armel - {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|armel.Build.0 = Debug|armel - {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|x64.ActiveCfg = Debug|Any CPU - {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|x64.Build.0 = Debug|Any CPU + {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|x64.ActiveCfg = Debug|x64 + {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|x64.Build.0 = Debug|x64 {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|x86.ActiveCfg = Debug|x86 {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Debug|x86.Build.0 = Debug|x86 {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|Any CPU.ActiveCfg = Release|Any CPU {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|Any CPU.Build.0 = Release|Any CPU - {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|arm.ActiveCfg = Release|arm - {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|arm.Build.0 = Release|arm - {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|arm64.ActiveCfg = Release|arm64 - {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|arm64.Build.0 = Release|arm64 - {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|armel.ActiveCfg = Release|armel - {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|armel.Build.0 = Release|armel - {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|x64.ActiveCfg = Release|Any CPU - {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|x64.Build.0 = Release|Any CPU - {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|x86.ActiveCfg = Release|x86 - {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|x86.Build.0 = Release|x86 - {FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|arm.ActiveCfg = Debug|arm + {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|arm.Build.0 = Debug|arm + {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|arm64.ActiveCfg = Debug|arm64 + {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|arm64.Build.0 = Debug|arm64 + {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|x64.ActiveCfg = Debug|x64 + {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|x64.Build.0 = Debug|x64 + {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|x86.ActiveCfg = Debug|x86 + {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10}.Release|x86.Build.0 = Debug|x86 + {FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|Any CPU.ActiveCfg = Debug|x86 {FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|arm.ActiveCfg = Debug|arm {FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|arm.Build.0 = Debug|arm {FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|arm64.ActiveCfg = Debug|arm64 {FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|arm64.Build.0 = Debug|arm64 - {FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|armel.ActiveCfg = Debug|armel - {FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|armel.Build.0 = Debug|armel - {FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|x64.ActiveCfg = Debug|Any CPU - {FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|x64.Build.0 = Debug|Any CPU + {FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|x64.ActiveCfg = Debug|x64 + {FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|x64.Build.0 = Debug|x64 {FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|x86.ActiveCfg = Debug|x86 {FBA029C3-B184-4457-AEEC-38D0C2523067}.Debug|x86.Build.0 = Debug|x86 - {FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|Any CPU.Build.0 = Release|Any CPU + {FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|Any CPU.ActiveCfg = Release|x86 {FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|arm.ActiveCfg = Release|arm {FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|arm.Build.0 = Release|arm {FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|arm64.ActiveCfg = Release|arm64 {FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|arm64.Build.0 = Release|arm64 - {FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|armel.ActiveCfg = Release|armel - {FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|armel.Build.0 = Release|armel - {FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|x64.ActiveCfg = Release|Any CPU - {FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|x64.Build.0 = Release|Any CPU + {FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|x64.ActiveCfg = Release|x64 + {FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|x64.Build.0 = Release|x64 {FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|x86.ActiveCfg = Release|x86 {FBA029C3-B184-4457-AEEC-38D0C2523067}.Release|x86.Build.0 = Release|x86 - {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|Any CPU.ActiveCfg = Debug|x86 {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|arm.ActiveCfg = Debug|arm {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|arm.Build.0 = Debug|arm {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|arm64.ActiveCfg = Debug|arm64 {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|arm64.Build.0 = Debug|arm64 - {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|armel.ActiveCfg = Debug|armel - {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|armel.Build.0 = Debug|armel - {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|x64.ActiveCfg = Debug|Any CPU - {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|x64.Build.0 = Debug|Any CPU + {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|x64.ActiveCfg = Debug|x64 + {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|x64.Build.0 = Debug|x64 {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|x86.ActiveCfg = Debug|x86 {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Debug|x86.Build.0 = Debug|x86 - {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|Any CPU.Build.0 = Release|Any CPU + {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|Any CPU.ActiveCfg = Release|x86 {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|arm.ActiveCfg = Release|arm {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|arm.Build.0 = Release|arm {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|arm64.ActiveCfg = Release|arm64 {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|arm64.Build.0 = Release|arm64 - {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|armel.ActiveCfg = Release|armel - {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|armel.Build.0 = Release|armel - {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|x64.ActiveCfg = Release|Any CPU - {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|x64.Build.0 = Release|Any CPU + {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|x64.ActiveCfg = Release|x64 + {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|x64.Build.0 = Release|x64 {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|x86.ActiveCfg = Release|x86 {1A9DF196-43A9-44BB-B2C6-D62AA56B0E49}.Release|x86.Build.0 = Release|x86 - {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|Any CPU.ActiveCfg = Debug|x86 {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|arm.ActiveCfg = Debug|arm {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|arm.Build.0 = Debug|arm {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|arm64.ActiveCfg = Debug|arm64 {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|arm64.Build.0 = Debug|arm64 - {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|armel.ActiveCfg = Debug|armel - {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|armel.Build.0 = Debug|armel - {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|x64.ActiveCfg = Debug|Any CPU - {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|x64.Build.0 = Debug|Any CPU + {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|x64.ActiveCfg = Debug|x64 + {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|x64.Build.0 = Debug|x64 {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|x86.ActiveCfg = Debug|x86 {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Debug|x86.Build.0 = Debug|x86 - {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|Any CPU.Build.0 = Release|Any CPU + {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|Any CPU.ActiveCfg = Release|x86 {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|arm.ActiveCfg = Release|arm {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|arm.Build.0 = Release|arm {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|arm64.ActiveCfg = Release|arm64 {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|arm64.Build.0 = Release|arm64 - {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|armel.ActiveCfg = Release|armel - {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|armel.Build.0 = Release|armel - {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|x64.ActiveCfg = Release|Any CPU - {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|x64.Build.0 = Release|Any CPU + {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|x64.ActiveCfg = Release|x64 + {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|x64.Build.0 = Release|x64 {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|x86.ActiveCfg = Release|x86 {DAC23E9F-F826-4577-AE7A-0849FF83280C}.Release|x86.Build.0 = Release|x86 - {13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|Any CPU.Build.0 = Debug|Any CPU + {13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|Any CPU.ActiveCfg = Debug|x86 {13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|arm.ActiveCfg = Debug|arm {13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|arm.Build.0 = Debug|arm {13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|arm64.ActiveCfg = Debug|arm64 {13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|arm64.Build.0 = Debug|arm64 - {13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|armel.ActiveCfg = Debug|armel - {13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|armel.Build.0 = Debug|armel - {13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|x64.ActiveCfg = Debug|Any CPU - {13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|x64.Build.0 = Debug|Any CPU + {13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|x64.ActiveCfg = Debug|x64 + {13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|x64.Build.0 = Debug|x64 {13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|x86.ActiveCfg = Debug|x86 {13BB3788-C3EB-4046-8105-A95F8AE49404}.Debug|x86.Build.0 = Debug|x86 - {13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|Any CPU.ActiveCfg = Release|Any CPU - {13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|Any CPU.Build.0 = Release|Any CPU + {13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|Any CPU.ActiveCfg = Release|x86 {13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|arm.ActiveCfg = Release|arm {13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|arm.Build.0 = Release|arm {13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|arm64.ActiveCfg = Release|arm64 {13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|arm64.Build.0 = Release|arm64 - {13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|armel.ActiveCfg = Release|armel - {13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|armel.Build.0 = Release|armel - {13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|x64.ActiveCfg = Release|Any CPU - {13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|x64.Build.0 = Release|Any CPU + {13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|x64.ActiveCfg = Release|x64 + {13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|x64.Build.0 = Release|x64 {13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|x86.ActiveCfg = Release|x86 {13BB3788-C3EB-4046-8105-A95F8AE49404}.Release|x86.Build.0 = Release|x86 - {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Debug|Any CPU.ActiveCfg = Debug|x86 + {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Debug|Any CPU.ActiveCfg = Debug|x64 {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Debug|arm.ActiveCfg = Debug|arm {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Debug|arm.Build.0 = Debug|arm {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Debug|arm64.ActiveCfg = Debug|arm64 {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Debug|arm64.Build.0 = Debug|arm64 - {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Debug|armel.ActiveCfg = Debug|armel - {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Debug|armel.Build.0 = Debug|armel {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Debug|x64.ActiveCfg = Debug|x64 - {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Debug|x64.Build.0 = Debug|x64 {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Debug|x86.ActiveCfg = Debug|x86 {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Debug|x86.Build.0 = Debug|x86 - {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Release|Any CPU.ActiveCfg = Release|x86 + {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Release|Any CPU.ActiveCfg = Release|x64 {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Release|arm.ActiveCfg = Release|arm {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Release|arm.Build.0 = Release|arm {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Release|arm64.ActiveCfg = Release|arm64 {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Release|arm64.Build.0 = Release|arm64 - {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Release|armel.ActiveCfg = Release|armel - {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Release|armel.Build.0 = Release|armel {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Release|x64.ActiveCfg = Release|x64 {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Release|x64.Build.0 = Release|x64 {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Release|x86.ActiveCfg = Release|x86 {BE95C560-B508-4588-8907-F9FC5BC1A0CF}.Release|x86.Build.0 = Release|x86 {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|arm.ActiveCfg = Debug|arm - {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|arm.Build.0 = Debug|arm - {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|arm64.ActiveCfg = Debug|arm64 - {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|arm64.Build.0 = Debug|arm64 - {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|armel.ActiveCfg = Debug|armel - {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|armel.Build.0 = Debug|armel + {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|arm.ActiveCfg = Debug|Any CPU + {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|arm.Build.0 = Debug|Any CPU + {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|arm64.ActiveCfg = Debug|Any CPU + {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|arm64.Build.0 = Debug|Any CPU {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|x64.ActiveCfg = Debug|Any CPU {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|x64.Build.0 = Debug|Any CPU - {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|x86.ActiveCfg = Debug|x86 - {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|x86.Build.0 = Debug|x86 + {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|x86.ActiveCfg = Debug|Any CPU + {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Debug|x86.Build.0 = Debug|Any CPU {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|Any CPU.ActiveCfg = Release|Any CPU {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|Any CPU.Build.0 = Release|Any CPU - {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|arm.ActiveCfg = Release|arm - {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|arm.Build.0 = Release|arm - {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|arm64.ActiveCfg = Release|arm64 - {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|arm64.Build.0 = Release|arm64 - {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|armel.ActiveCfg = Release|armel - {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|armel.Build.0 = Release|armel + {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|arm.ActiveCfg = Release|Any CPU + {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|arm.Build.0 = Release|Any CPU + {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|arm64.ActiveCfg = Release|Any CPU + {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|arm64.Build.0 = Release|Any CPU {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|x64.ActiveCfg = Release|Any CPU {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|x64.Build.0 = Release|Any CPU - {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|x86.ActiveCfg = Release|x86 - {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|x86.Build.0 = Release|x86 + {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|x86.ActiveCfg = Release|Any CPU + {D66338D4-F9E4-4051-B302-232C6BFB6EF6}.Release|x86.Build.0 = Release|Any CPU {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|arm.ActiveCfg = Debug|arm - {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|arm.Build.0 = Debug|arm - {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|arm64.ActiveCfg = Debug|arm64 - {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|arm64.Build.0 = Debug|arm64 - {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|armel.ActiveCfg = Debug|armel - {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|armel.Build.0 = Debug|armel + {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|arm.ActiveCfg = Debug|Any CPU + {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|arm.Build.0 = Debug|Any CPU + {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|arm64.ActiveCfg = Debug|Any CPU + {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|arm64.Build.0 = Debug|Any CPU {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|x64.ActiveCfg = Debug|Any CPU {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|x64.Build.0 = Debug|Any CPU - {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|x86.ActiveCfg = Debug|x86 - {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|x86.Build.0 = Debug|x86 + {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|x86.ActiveCfg = Debug|Any CPU + {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Debug|x86.Build.0 = Debug|Any CPU {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|Any CPU.ActiveCfg = Release|Any CPU {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|Any CPU.Build.0 = Release|Any CPU - {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|arm.ActiveCfg = Release|arm - {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|arm.Build.0 = Release|arm - {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|arm64.ActiveCfg = Release|arm64 - {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|arm64.Build.0 = Release|arm64 - {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|armel.ActiveCfg = Release|armel - {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|armel.Build.0 = Release|armel + {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|arm.ActiveCfg = Release|Any CPU + {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|arm.Build.0 = Release|Any CPU + {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|arm64.ActiveCfg = Release|Any CPU + {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|arm64.Build.0 = Release|Any CPU {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|x64.ActiveCfg = Release|Any CPU {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|x64.Build.0 = Release|Any CPU - {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|x86.ActiveCfg = Release|x86 - {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|x86.Build.0 = Release|x86 + {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|x86.ActiveCfg = Release|Any CPU + {A965EA82-219D-48F7-AD51-BC030C16CC6F}.Release|x86.Build.0 = Release|Any CPU {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|Any CPU.Build.0 = Debug|Any CPU - {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|arm.ActiveCfg = Debug|arm - {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|arm.Build.0 = Debug|arm - {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|arm64.ActiveCfg = Debug|arm64 - {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|arm64.Build.0 = Debug|arm64 - {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|armel.ActiveCfg = Debug|armel - {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|armel.Build.0 = Debug|armel - {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|x64.ActiveCfg = Debug|x64 - {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|x64.Build.0 = Debug|x64 - {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|x86.ActiveCfg = Debug|x86 - {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|x86.Build.0 = Debug|x86 + {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|arm.ActiveCfg = Debug|Any CPU + {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|arm.Build.0 = Debug|Any CPU + {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|arm64.ActiveCfg = Debug|Any CPU + {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|arm64.Build.0 = Debug|Any CPU + {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|x64.ActiveCfg = Debug|Any CPU + {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|x64.Build.0 = Debug|Any CPU + {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|x86.ActiveCfg = Debug|Any CPU + {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Debug|x86.Build.0 = Debug|Any CPU {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|Any CPU.ActiveCfg = Release|Any CPU {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|Any CPU.Build.0 = Release|Any CPU - {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|arm.ActiveCfg = Release|arm - {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|arm.Build.0 = Release|arm - {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|arm64.ActiveCfg = Release|arm64 - {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|arm64.Build.0 = Release|arm64 - {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|armel.ActiveCfg = Release|armel - {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|armel.Build.0 = Release|armel - {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|x64.ActiveCfg = Release|x64 - {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|x64.Build.0 = Release|x64 - {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|x86.ActiveCfg = Release|x86 - {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|x86.Build.0 = Release|x86 + {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|arm.ActiveCfg = Release|Any CPU + {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|arm.Build.0 = Release|Any CPU + {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|arm64.ActiveCfg = Release|Any CPU + {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|arm64.Build.0 = Release|Any CPU + {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|x64.ActiveCfg = Release|Any CPU + {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|x64.Build.0 = Release|Any CPU + {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|x86.ActiveCfg = Release|Any CPU + {971AE7E7-08C6-48B4-902A-63851E6DAC66}.Release|x86.Build.0 = Release|Any CPU + {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Debug|arm.ActiveCfg = Debug|arm + {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Debug|arm.Build.0 = Debug|arm + {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Debug|arm64.ActiveCfg = Debug|arm64 + {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Debug|arm64.Build.0 = Debug|arm64 + {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Debug|x64.ActiveCfg = Debug|x64 + {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Debug|x64.Build.0 = Debug|x64 + {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Debug|x86.ActiveCfg = Debug|x86 + {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Debug|x86.Build.0 = Debug|x86 + {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Release|Any CPU.Build.0 = Release|Any CPU + {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Release|arm.ActiveCfg = Release|arm + {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Release|arm.Build.0 = Release|arm + {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Release|arm64.ActiveCfg = Release|arm64 + {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Release|arm64.Build.0 = Release|arm64 + {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Release|x64.ActiveCfg = Release|x64 + {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Release|x64.Build.0 = Release|x64 + {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Release|x86.ActiveCfg = Release|x86 + {5D796936-CCC1-4E9D-B1A9-36A5ABA9B499}.Release|x86.Build.0 = Release|x86 + {CE9781B1-0028-4039-A48C-8193F0D28467}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CE9781B1-0028-4039-A48C-8193F0D28467}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE9781B1-0028-4039-A48C-8193F0D28467}.Debug|arm.ActiveCfg = Debug|Any CPU + {CE9781B1-0028-4039-A48C-8193F0D28467}.Debug|arm.Build.0 = Debug|Any CPU + {CE9781B1-0028-4039-A48C-8193F0D28467}.Debug|arm64.ActiveCfg = Debug|Any CPU + {CE9781B1-0028-4039-A48C-8193F0D28467}.Debug|arm64.Build.0 = Debug|Any CPU + {CE9781B1-0028-4039-A48C-8193F0D28467}.Debug|x64.ActiveCfg = Debug|x64 + {CE9781B1-0028-4039-A48C-8193F0D28467}.Debug|x64.Build.0 = Debug|x64 + {CE9781B1-0028-4039-A48C-8193F0D28467}.Debug|x86.ActiveCfg = Debug|Any CPU + {CE9781B1-0028-4039-A48C-8193F0D28467}.Debug|x86.Build.0 = Debug|Any CPU + {CE9781B1-0028-4039-A48C-8193F0D28467}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CE9781B1-0028-4039-A48C-8193F0D28467}.Release|Any CPU.Build.0 = Release|Any CPU + {CE9781B1-0028-4039-A48C-8193F0D28467}.Release|arm.ActiveCfg = Release|Any CPU + {CE9781B1-0028-4039-A48C-8193F0D28467}.Release|arm.Build.0 = Release|Any CPU + {CE9781B1-0028-4039-A48C-8193F0D28467}.Release|arm64.ActiveCfg = Release|Any CPU + {CE9781B1-0028-4039-A48C-8193F0D28467}.Release|arm64.Build.0 = Release|Any CPU + {CE9781B1-0028-4039-A48C-8193F0D28467}.Release|x64.ActiveCfg = Release|x64 + {CE9781B1-0028-4039-A48C-8193F0D28467}.Release|x64.Build.0 = Release|x64 + {CE9781B1-0028-4039-A48C-8193F0D28467}.Release|x86.ActiveCfg = Release|Any CPU + {CE9781B1-0028-4039-A48C-8193F0D28467}.Release|x86.Build.0 = Release|Any CPU + {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Debug|arm.ActiveCfg = Debug|arm + {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Debug|arm.Build.0 = Debug|arm + {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Debug|arm64.ActiveCfg = Debug|arm64 + {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Debug|arm64.Build.0 = Debug|arm64 + {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Debug|x64.ActiveCfg = Debug|x64 + {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Debug|x64.Build.0 = Debug|x64 + {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Debug|x86.ActiveCfg = Debug|x86 + {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Debug|x86.Build.0 = Debug|x86 + {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Release|Any CPU.Build.0 = Release|Any CPU + {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Release|arm.ActiveCfg = Release|arm + {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Release|arm.Build.0 = Release|arm + {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Release|arm64.ActiveCfg = Release|arm64 + {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Release|arm64.Build.0 = Release|arm64 + {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Release|x64.ActiveCfg = Release|x64 + {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Release|x64.Build.0 = Release|x64 + {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Release|x86.ActiveCfg = Release|x86 + {44DE7F7B-AD73-40EA-87CC-554F2F57C38A}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D823809D-1D28-46C0-8810-B5C7E2E59E94} + EndGlobalSection EndGlobal diff --git a/external/corert/src/ILCompiler/ObjectWriter/ObjectWriter.depproj b/external/corert/src/ILCompiler/ObjectWriter/ObjectWriter.depproj new file mode 100644 index 0000000000..95fed28328 --- /dev/null +++ b/external/corert/src/ILCompiler/ObjectWriter/ObjectWriter.depproj @@ -0,0 +1,22 @@ + + + + $(BaseOutputPath)$(OSPlatformConfig)/tools + + + + .NETCoreApp,Version=v2.0 + netcoreapp2.0 + $(NuPkgRid) + ubuntu.14.04-x64 + true + + + + + $(ObjectWriterVersion) + + + + + diff --git a/external/corert/src/ILCompiler/RyuJIT/RyuJIT.depproj b/external/corert/src/ILCompiler/RyuJIT/RyuJIT.depproj new file mode 100644 index 0000000000..5e543ee5b9 --- /dev/null +++ b/external/corert/src/ILCompiler/RyuJIT/RyuJIT.depproj @@ -0,0 +1,39 @@ + + + + $(BaseOutputPath)$(OSPlatformConfig)/tools + + + + .NETCoreApp,Version=v2.0 + netcoreapp2.0 + $(NuPkgRid) + win-x64 + osx-x64 + linux-x64 + true + + + + + $(RyuJITVersion) + + + + + + + %(Filename)ilc%(Extension) + + + + + + + + + $(GetCopyToOutputDirectoryItemsDependsOn); + RenameToJitIlc + + + diff --git a/external/corert/src/ILCompiler/desktop/desktop.csproj b/external/corert/src/ILCompiler/desktop/desktop.csproj index b34dff91b0..d564e8ecf6 100644 --- a/external/corert/src/ILCompiler/desktop/desktop.csproj +++ b/external/corert/src/ILCompiler/desktop/desktop.csproj @@ -1,35 +1,74 @@  - - + + Debug AnyCPU - {B2C35178-5E42-4010-A72A-938711D7F8F0} + {CE9781B1-0028-4039-A48C-8193F0D28467} Exe - Properties - ILCompiler - ilc - .NETFramework + desktop + desktop v4.6 - win7 + 512 true - - x64 + + true + full + false bin\Debug\ + DEBUG;TRACE prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + prompt + MinimumRecommendedRules.ruleset + true + bin\x64\Release\ + TRACE + true + pdbonly x64 - bin\Release\ prompt + MinimumRecommendedRules.ruleset + true + + + 0.1.0-e160909-1 + + + + + + + + - src\Program.cs + Program.cs - src\RdXmlRootProvider.cs + RdXmlRootProvider.cs + + + EcmaOnlyDebugInformationProvider.cs @@ -45,42 +84,32 @@ {13bb3788-c3eb-4046-8105-a95f8ae49404} ILCompiler.Compiler - - {971AE7E7-08C6-48B4-902A-63851E6DAC66} - ILCompiler.Compiler.Cpp + + {971ae7e7-08c6-48b4-902a-63851e6dac66} + ILCompiler.CppCodegen {1a9df196-43a9-44bb-b2c6-d62aa56b0e49} ILCompiler.TypeSystem + + {5d796936-ccc1-4e9d-b1a9-36a5aba9b499} + ILCompiler.WebAssembly + - - - - - - - - - - - + PreserveNewest - + PreserveNewest - - Always + + PreserveNewest - - + + + + \ No newline at end of file diff --git a/external/corert/src/ILCompiler/desktop/project.json b/external/corert/src/ILCompiler/desktop/project.json deleted file mode 100644 index 24010620d5..0000000000 --- a/external/corert/src/ILCompiler/desktop/project.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "dependencies": { - "NETStandard.Library": "1.6.1", - "System.IO.MemoryMappedFiles": "4.3.0", - "System.Reflection.Metadata": "1.4.2", - "System.Runtime.CompilerServices.Unsafe": "4.3.0", - "Microsoft.DiaSymReader": "1.1.0", - "System.CommandLine": "0.1.0-e160909-1" - }, - "frameworks": { - "net46": {} - }, - "runtimes": { - "win7-x64": {} - } -} diff --git a/external/corert/src/ILCompiler/netcoreapp/ilc.cs b/external/corert/src/ILCompiler/netcoreapp/ilc.cs new file mode 100644 index 0000000000..6158bb2541 --- /dev/null +++ b/external/corert/src/ILCompiler/netcoreapp/ilc.cs @@ -0,0 +1,9 @@ +using System; + +class Program +{ + static void Main() + { + Console.WriteLine("Hello world!"); + } +} diff --git a/external/corert/src/ILCompiler/netcoreapp/ilc.csproj b/external/corert/src/ILCompiler/netcoreapp/ilc.csproj new file mode 100644 index 0000000000..1feb02316e --- /dev/null +++ b/external/corert/src/ILCompiler/netcoreapp/ilc.csproj @@ -0,0 +1,32 @@ + + + + Exe + netcoreapp2.0 + + + + + 1.3.1 + + + 4.3.0 + + + 1.4.2 + + + 4.3.0 + + + 4.3.0 + + + 1.0.8 + + + 0.1.0-e160909-1 + + + + diff --git a/external/corert/src/ILCompiler/repro/project.json b/external/corert/src/ILCompiler/repro/project.json deleted file mode 100644 index 7dc020c70f..0000000000 --- a/external/corert/src/ILCompiler/repro/project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "dependencies": { - "NETStandard.Library": "1.6.1" - }, - "frameworks": { - "netstandard1.6": {} - } -} diff --git a/external/corert/src/ILCompiler/repro/repro.csproj b/external/corert/src/ILCompiler/repro/repro.csproj index 12aeea405c..15ed06b830 100644 --- a/external/corert/src/ILCompiler/repro/repro.csproj +++ b/external/corert/src/ILCompiler/repro/repro.csproj @@ -1,33 +1,20 @@ - - + - Debug - AnyCPU - {FBA029C3-B184-4457-AEEC-38D0C2523067} Exe - repro - true - .NETStandard,Version=v1.6 + netcoreapp2.0 true - - - bin\Debug - prompt - - - bin\Release - prompt - - - + true + false - + + $(MicrosoftNETCoreAppPackageVersion) + - + diff --git a/external/corert/src/ILCompiler/repro/repro.il b/external/corert/src/ILCompiler/repro/repro.il new file mode 100644 index 0000000000..94f9aac84e --- /dev/null +++ b/external/corert/src/ILCompiler/repro/repro.il @@ -0,0 +1,50 @@ +// 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. + +.assembly extern System.Runtime +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly extern System.Console +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} + +.assembly repro +{ + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} + +.module repro.exe +.imagebase 0x00400000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY + +.class private auto ansi beforefieldinit Program + extends [System.Runtime]System.Object +{ + .method private hidebysig static void Main(string[] args) cil managed + { + .entrypoint + .maxstack 8 + + ldstr "Hello world from MSIL!" + call void [System.Console]System.Console::WriteLine(string) + ret + } + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + .maxstack 8 + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } +} diff --git a/external/corert/src/ILCompiler/repro/repro.ilproj b/external/corert/src/ILCompiler/repro/repro.ilproj new file mode 100644 index 0000000000..2c49426955 --- /dev/null +++ b/external/corert/src/ILCompiler/repro/repro.ilproj @@ -0,0 +1,14 @@ + + + + Exe + repro + netcoreapp2.0 + true + + + + + + + diff --git a/external/corert/src/ILCompiler/reproNative/CoreRTNatVis.natvis b/external/corert/src/ILCompiler/reproNative/CoreRTNatVis.natvis new file mode 100644 index 0000000000..c0dfbfec5b --- /dev/null +++ b/external/corert/src/ILCompiler/reproNative/CoreRTNatVis.natvis @@ -0,0 +1,63 @@ + + + + {&(_firstChar),su} + &(_firstChar),su + + + {{count = {_numComponents}}} + + + _numComponents + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1**)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + ($T1*)(((char*)this)+sizeof(void*)+sizeof(void*)) + + + + \ No newline at end of file diff --git a/external/corert/src/ILCompiler/reproNative/reproNative.vcxproj b/external/corert/src/ILCompiler/reproNative/reproNative.vcxproj index efe578a14b..654438da27 100644 --- a/external/corert/src/ILCompiler/reproNative/reproNative.vcxproj +++ b/external/corert/src/ILCompiler/reproNative/reproNative.vcxproj @@ -1,5 +1,4 @@ - - + Debug @@ -63,7 +62,7 @@ Console true - ..\..\..\bin\obj\Windows_NT.x64.Debug\repro\native\repro.obj;kernel32.lib;user32.lib;gdi32.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;bcrypt.lib;%(AdditionalDependencies);..\..\..\bin\Product\Windows_NT.x64.Debug\lib\Runtime.lib + ..\..\..\bin\obj\Windows_NT.x64.Debug\repro\native\repro.obj;kernel32.lib;user32.lib;gdi32.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;bcrypt.lib;%(AdditionalDependencies);..\..\..\bin\Windows_NT.x64.Debug\sdk\Runtime.lib @@ -85,7 +84,7 @@ true true true - ..\..\..\bin\obj\Windows_NT.x64.Release\repro\native\repro.obj;kernel32.lib;user32.lib;gdi32.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;bcrypt.lib;%(AdditionalDependencies);..\..\..\bin\Product\Windows_NT.x64.Release\lib\Runtime.lib + ..\..\..\bin\obj\Windows_NT.x64.Release\repro\native\repro.obj;kernel32.lib;user32.lib;gdi32.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;bcrypt.lib;%(AdditionalDependencies);..\..\..\bin\Windows_NT.x64.Release\sdk\Runtime.lib @@ -99,7 +98,10 @@ + + + - \ No newline at end of file + diff --git a/external/corert/src/ILCompiler/reproNative/reproNativeCpp.vcxproj b/external/corert/src/ILCompiler/reproNative/reproNativeCpp.vcxproj index 2d7bbf5ef9..70081ce85b 100644 --- a/external/corert/src/ILCompiler/reproNative/reproNativeCpp.vcxproj +++ b/external/corert/src/ILCompiler/reproNative/reproNativeCpp.vcxproj @@ -1,5 +1,4 @@ - - + Debug @@ -64,7 +63,7 @@ Console true - kernel32.lib;user32.lib;gdi32.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;bcrypt.lib;%(AdditionalDependencies);..\..\..\bin\Product\Windows_NT.x64.Debug\lib\PortableRuntime.lib + kernel32.lib;user32.lib;gdi32.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;bcrypt.lib;%(AdditionalDependencies);..\..\..\bin\Windows_NT.x64.Debug\sdk\PortableRuntime.lib @@ -87,7 +86,7 @@ true true true - kernel32.lib;user32.lib;gdi32.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;bcrypt.lib;%(AdditionalDependencies);..\..\..\bin\Product\Windows_NT.x64.Release\lib\PortableRuntime.lib + kernel32.lib;user32.lib;gdi32.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;bcrypt.lib;%(AdditionalDependencies);..\..\..\bin\Windows_NT.x64.Release\sdk\PortableRuntime.lib @@ -104,4 +103,4 @@ - \ No newline at end of file + diff --git a/external/corert/src/ILCompiler/src/EcmaOnlyDebugInformationProvider.cs b/external/corert/src/ILCompiler/src/EcmaOnlyDebugInformationProvider.cs new file mode 100644 index 0000000000..7131ea123a --- /dev/null +++ b/external/corert/src/ILCompiler/src/EcmaOnlyDebugInformationProvider.cs @@ -0,0 +1,23 @@ +// 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 Internal.IL; + +namespace ILCompiler +{ + /// + /// Provides debug information for ECMA-based only. + /// + public class EcmaOnlyDebugInformationProvider : DebugInformationProvider + { + public override MethodDebugInformation GetDebugInfo(MethodIL methodIL) + { + MethodIL definitionIL = methodIL.GetMethodILDefinition(); + if (definitionIL is EcmaMethodIL) + return methodIL.GetDebugInfo(); + + return MethodDebugInformation.None; + } + } +} diff --git a/external/corert/src/ILCompiler/src/ILCompiler.csproj b/external/corert/src/ILCompiler/src/ILCompiler.csproj index 3e1e67342b..75d6ba4e2c 100644 --- a/external/corert/src/ILCompiler/src/ILCompiler.csproj +++ b/external/corert/src/ILCompiler/src/ILCompiler.csproj @@ -1,39 +1,36 @@ - - + - Debug - AnyCPU - {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10} - Exe - ILCompiler - - - - .dll - ilc - true - true - $(DefineConstants);FXCORE - .NETStandard,Version=v1.6 - false - - - - + $(BaseOutputPath)$(OSPlatformConfig)/tools - + Exe + ILCompiler + ilc + true + netcoreapp2.0 + false + + .dll - - false - .NetCoreApp,Version=2.0 - + + + + + + + + + + + 0.1.0-e160909-1 + + @@ -44,25 +41,14 @@ - - - PreserveNewest + Always - - - {13bb3788-c3eb-4046-8105-a95f8ae49404} - ILCompiler.Compiler - - - {971AE7E7-08C6-48B4-902A-63851E6DAC66} - ILCompiler.Compiler.Cpp - - - {1a9df196-43a9-44bb-b2c6-d62aa56b0e49} - ILCompiler.TypeSystem - - + + $(TargetDir)$(AssemblyName).exe + $(TargetDir)$(AssemblyName) + $(StartArguments) + diff --git a/external/corert/src/ILCompiler/src/Program.cs b/external/corert/src/ILCompiler/src/Program.cs index 28d02f8bed..e869f7540d 100644 --- a/external/corert/src/ILCompiler/src/Program.cs +++ b/external/corert/src/ILCompiler/src/Program.cs @@ -22,10 +22,13 @@ namespace ILCompiler private string _outputFilePath; private bool _isCppCodegen; + private bool _isWasmCodegen; private bool _isVerbose; private string _dgmlLogFileName; private bool _generateFullDgmlLog; + private string _scanDgmlLogFileName; + private bool _generateFullScanDgmlLog; private TargetArchitecture _targetArchitecture; private string _targetArchitectureStr; @@ -33,9 +36,15 @@ namespace ILCompiler private string _targetOSStr; private OptimizationMode _optimizationMode; private bool _enableDebugInfo; + private string _ilDump; private string _systemModuleName = "System.Private.CoreLib"; private bool _multiFile; + private bool _nativeLib; + private string _exportsFile; private bool _useSharedGenerics; + private bool _useScanner; + private bool _noScanner; + private bool _emitStackTraceData; private string _mapFileName; private string _metadataLogFileName; @@ -66,7 +75,6 @@ namespace ILCompiler private void InitializeDefaultOptions() { -#if FXCORE // We could offer this as a command line option, but then we also need to // load a different RyuJIT, so this is a future nice to have... if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -95,10 +103,11 @@ namespace ILCompiler default: throw new NotImplementedException(); } -#else - _targetOS = TargetOS.Windows; - _targetArchitecture = TargetArchitecture.X64; -#endif + + // Workaround for https://github.com/dotnet/corefx/issues/25267 + // If pointer size is 8, we're obviously not an X86 process... + if (_targetArchitecture == TargetArchitecture.X86 && IntPtr.Size == 8) + _targetArchitecture = TargetArchitecture.X64; } private ArgumentSyntax ParseCommandLine(string[] args) @@ -124,8 +133,13 @@ namespace ILCompiler syntax.DefineOption("O", ref optimize, "Enable optimizations"); syntax.DefineOption("g", ref _enableDebugInfo, "Emit debugging information"); syntax.DefineOption("cpp", ref _isCppCodegen, "Compile for C++ code-generation"); + syntax.DefineOption("wasm", ref _isWasmCodegen, "Compile for WebAssembly code-generation"); + syntax.DefineOption("nativelib", ref _nativeLib, "Compile as static or shared library"); + syntax.DefineOption("exportsfile", ref _exportsFile, "File to write exported method definitions"); syntax.DefineOption("dgmllog", ref _dgmlLogFileName, "Save result of dependency analysis as DGML"); syntax.DefineOption("fulllog", ref _generateFullDgmlLog, "Save detailed log of dependency analysis"); + syntax.DefineOption("scandgmllog", ref _scanDgmlLogFileName, "Save result of scanner dependency analysis as DGML"); + syntax.DefineOption("scanfulllog", ref _generateFullScanDgmlLog, "Save detailed log of scanner dependency analysis"); syntax.DefineOption("verbose", ref _isVerbose, "Enable verbose logging"); syntax.DefineOption("systemmodule", ref _systemModuleName, "System module name (default: System.Private.CoreLib)"); syntax.DefineOption("multifile", ref _multiFile, "Compile only input files (do not compile referenced assemblies)"); @@ -135,6 +149,10 @@ namespace ILCompiler syntax.DefineOptionList("rdxml", ref _rdXmlFilePaths, "RD.XML file(s) for compilation"); syntax.DefineOption("map", ref _mapFileName, "Generate a map file"); syntax.DefineOption("metadatalog", ref _metadataLogFileName, "Generate a metadata log file"); + syntax.DefineOption("scan", ref _useScanner, "Use IL scanner to generate optimized code (implied by -O)"); + syntax.DefineOption("noscan", ref _noScanner, "Do not use IL scanner to generate optimized code"); + syntax.DefineOption("ildump", ref _ilDump, "Dump IL assembly listing for compiler-generated IL"); + syntax.DefineOption("stacktracedata", ref _emitStackTraceData, "Emit data to support generating stack trace strings at runtime"); syntax.DefineOption("targetarch", ref _targetArchitectureStr, "Target architecture for cross compilation"); syntax.DefineOption("targetos", ref _targetOSStr, "Target OS for cross compilation"); @@ -191,6 +209,11 @@ namespace ILCompiler _targetArchitecture = TargetArchitecture.ARMEL; else if (_targetArchitectureStr.Equals("arm64", StringComparison.OrdinalIgnoreCase)) _targetArchitecture = TargetArchitecture.ARM64; + else if (_targetArchitectureStr.Equals("wasm", StringComparison.OrdinalIgnoreCase)) + { + _targetArchitecture = TargetArchitecture.Wasm32; + _isWasmCodegen = true; + } else throw new CommandLineException("Target architecture is not supported"); } @@ -206,14 +229,19 @@ namespace ILCompiler throw new CommandLineException("Target OS is not supported"); } + if (_isWasmCodegen) + _targetArchitecture = TargetArchitecture.Wasm32; // // Initialize type system context // - SharedGenericsMode genericsMode = _useSharedGenerics || !_isCppCodegen ? + SharedGenericsMode genericsMode = _useSharedGenerics || (!_isCppCodegen && !_isWasmCodegen) ? SharedGenericsMode.CanonicalReferenceTypes : SharedGenericsMode.Disabled; - var typeSystemContext = new CompilerTypeSystemContext(new TargetDetails(_targetArchitecture, _targetOS, TargetAbi.CoreRT), genericsMode); + // TODO: compiler switch for SIMD support? + var simdVectorLength = (_isCppCodegen || _isWasmCodegen) ? SimdVectorLength.None : SimdVectorLength.Vector128Bit; + var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, TargetAbi.CoreRT, simdVectorLength); + var typeSystemContext = new CompilerTypeSystemContext(targetDetails, genericsMode); // // TODO: To support our pre-compiled test tree, allow input files that aren't managed assemblies since @@ -285,6 +313,12 @@ namespace ILCompiler new LibraryInitializers(typeSystemContext, _isCppCodegen); compilationRoots.Add(new MainMethodRootProvider(entrypointModule, libraryInitializers.LibraryInitializerMethods)); } + else if (_nativeLib) + { + EcmaModule module = (EcmaModule)typeSystemContext.SystemModule; + LibraryInitializers libraryInitializers = new LibraryInitializers(typeSystemContext, _isCppCodegen); + compilationRoots.Add(new NativeLibraryInitializerRootProvider(module, libraryInitializers.LibraryInitializerMethods)); + } if (_multiFile) { @@ -306,7 +340,7 @@ namespace ILCompiler } else { - if (entrypointModule == null) + if (entrypointModule == null && !_nativeLib) throw new Exception("No entrypoint module"); compilationRoots.Add(new ExportedMethodsRootProvider((EcmaModule)typeSystemContext.SystemModule)); @@ -314,6 +348,8 @@ namespace ILCompiler compilationGroup = new SingleFileCompilationModuleGroup(typeSystemContext); } + if (_rdXmlFilePaths.Count > 0) + Console.WriteLine("Warning: RD.XML processing will change before release (https://github.com/dotnet/corert/issues/5001)"); foreach (var rdXmlFilePath in _rdXmlFilePaths) { compilationRoots.Add(new RdXmlRootProvider(typeSystemContext, rdXmlFilePath)); @@ -325,52 +361,191 @@ namespace ILCompiler // CompilationBuilder builder; - if (_isCppCodegen) + if (_isWasmCodegen) + builder = new WebAssemblyCodegenCompilationBuilder(typeSystemContext, compilationGroup); + else if (_isCppCodegen) builder = new CppCodegenCompilationBuilder(typeSystemContext, compilationGroup); else builder = new RyuJitCompilationBuilder(typeSystemContext, compilationGroup); - var logger = _isVerbose ? new Logger(Console.Out, true) : Logger.Null; + var stackTracePolicy = _emitStackTraceData ? + (StackTraceEmissionPolicy)new EcmaMethodStackTraceEmissionPolicy() : new NoStackTraceEmissionPolicy(); + + UsageBasedMetadataManager metadataManager = new UsageBasedMetadataManager( + compilationGroup, + typeSystemContext, + new BlockedInternalsBlockingPolicy(), + _metadataLogFileName, + stackTracePolicy); + + // Unless explicitly opted in at the command line, we enable scanner for retail builds by default. + // We don't do this for CppCodegen and Wasm, because those codegens are behind. + // We also don't do this for multifile because scanner doesn't simulate inlining (this would be + // fixable by using a CompilationGroup for the scanner that has a bigger worldview, but + // let's cross that bridge when we get there). + bool useScanner = _useScanner || + (_optimizationMode != OptimizationMode.None && !_isCppCodegen && !_isWasmCodegen && !_multiFile); + + useScanner &= !_noScanner; + + MetadataManager compilationMetadataManager = _isWasmCodegen ? (MetadataManager)new EmptyMetadataManager(typeSystemContext) : metadataManager; + ILScanResults scanResults = null; + if (useScanner) + { + ILScannerBuilder scannerBuilder = builder.GetILScannerBuilder() + .UseCompilationRoots(compilationRoots) + .UseMetadataManager(metadataManager); + + if (_scanDgmlLogFileName != null) + scannerBuilder.UseDependencyTracking(_generateFullScanDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); + + IILScanner scanner = scannerBuilder.ToILScanner(); + + scanResults = scanner.Scan(); + + compilationMetadataManager = metadataManager.ToAnalysisBasedMetadataManager(); + } + + var logger = new Logger(Console.Out, _isVerbose); + + DebugInformationProvider debugInfoProvider = _enableDebugInfo ? + (_ilDump == null ? new DebugInformationProvider() : new ILAssemblyGeneratingMethodDebugInfoProvider(_ilDump, new EcmaOnlyDebugInformationProvider())) : + new NullDebugInformationProvider(); DependencyTrackingLevel trackingLevel = _dgmlLogFileName == null ? DependencyTrackingLevel.None : (_generateFullDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); - CompilerGeneratedMetadataManager metadataManager = new CompilerGeneratedMetadataManager(compilationGroup, typeSystemContext, _metadataLogFileName); + compilationRoots.Add(compilationMetadataManager); - ICompilation compilation = builder + builder .UseBackendOptions(_codegenOptions) - .UseMetadataManager(metadataManager) + .UseMetadataManager(compilationMetadataManager) .UseLogger(logger) .UseDependencyTracking(trackingLevel) .UseCompilationRoots(compilationRoots) .UseOptimizationMode(_optimizationMode) - .UseDebugInfo(_enableDebugInfo) - .ToCompilation(); + .UseDebugInfoProvider(debugInfoProvider); + + if (scanResults != null) + { + // If we have a scanner, feed the vtable analysis results to the compilation. + // This could be a command line switch if we really wanted to. + builder.UseVTableSliceProvider(scanResults.GetVTableLayoutInfo()); + + // If we have a scanner, feed the generic dictionary results to the compilation. + // This could be a command line switch if we really wanted to. + builder.UseGenericDictionaryLayoutProvider(scanResults.GetDictionaryLayoutInfo()); + + // If we feed any outputs of the scanner into the compilation, it's essential + // we use scanner's devirtualization manager. It prevents optimizing codegens + // from accidentally devirtualizing cases that can never happen at runtime + // (e.g. devirtualizing a method on a type that never gets allocated). + builder.UseDevirtualizationManager(scanResults.GetDevirtualizationManager()); + } + + ICompilation compilation = builder.ToCompilation(); ObjectDumper dumper = _mapFileName != null ? new ObjectDumper(_mapFileName) : null; - compilation.Compile(_outputFilePath, dumper); + CompilationResults compilationResults = compilation.Compile(_outputFilePath, dumper); + if (_exportsFile != null) + { + ExportsFileWriter defFileWriter = new ExportsFileWriter(typeSystemContext, _exportsFile); + foreach (var compilationRoot in compilationRoots) + { + if (compilationRoot is ExportedMethodsRootProvider provider) + defFileWriter.AddExportedMethods(provider.ExportedMethods); + } + + defFileWriter.EmitExportedMethods(); + } if (_dgmlLogFileName != null) - compilation.WriteDependencyLog(_dgmlLogFileName); + compilationResults.WriteDependencyLog(_dgmlLogFileName); + + if (scanResults != null) + { + SimdHelper simdHelper = new SimdHelper(); + + if (_scanDgmlLogFileName != null) + scanResults.WriteDependencyLog(_scanDgmlLogFileName); + + // If the scanner and compiler don't agree on what to compile, the outputs of the scanner might not actually be usable. + // We are going to check this two ways: + // 1. The methods and types generated during compilation are a subset of method and types scanned + // 2. The methods and types scanned are a subset of methods and types compiled (this has a chance to hold for unoptimized builds only). + + // Check that methods and types generated during compilation are a subset of method and types scanned + bool scanningFail = false; + DiffCompilationResults(ref scanningFail, compilationResults.CompiledMethodBodies, scanResults.CompiledMethodBodies, + "Methods", "compiled", "scanned", method => !(method.GetTypicalMethodDefinition() is EcmaMethod)); + DiffCompilationResults(ref scanningFail, compilationResults.ConstructedEETypes, scanResults.ConstructedEETypes, + "EETypes", "compiled", "scanned", type => !(type.GetTypeDefinition() is EcmaType)); + + // If optimizations are enabled, the results will for sure not match in the other direction due to inlining, etc. + // But there's at least some value in checking the scanner doesn't expand the universe too much in debug. + if (_optimizationMode == OptimizationMode.None) + { + // Check that methods and types scanned are a subset of methods and types compiled + + // If we find diffs here, they're not critical, but still might be causing a Size on Disk regression. + bool dummy = false; + + // We additionally skip methods in SIMD module because there's just too many intrisics to handle and IL scanner + // doesn't expand them. They would show up as noisy diffs. + DiffCompilationResults(ref dummy, scanResults.CompiledMethodBodies, compilationResults.CompiledMethodBodies, + "Methods", "scanned", "compiled", method => !(method.GetTypicalMethodDefinition() is EcmaMethod) || simdHelper.IsInSimdModule(method.OwningType)); + DiffCompilationResults(ref dummy, scanResults.ConstructedEETypes, compilationResults.ConstructedEETypes, + "EETypes", "scanned", "compiled", type => !(type.GetTypeDefinition() is EcmaType)); + } + + if (scanningFail) + throw new Exception("Scanning failure"); + } + + if (debugInfoProvider is IDisposable) + ((IDisposable)debugInfoProvider).Dispose(); return 0; } + [System.Diagnostics.Conditional("DEBUG")] + private void DiffCompilationResults(ref bool result, IEnumerable set1, IEnumerable set2, string prefix, + string set1name, string set2name, Predicate filter) + { + HashSet diff = new HashSet(set1); + diff.ExceptWith(set2); + + // TODO: move ownership of compiler-generated entities to CompilerTypeSystemContext. + // https://github.com/dotnet/corert/issues/3873 + diff.RemoveWhere(filter); + + if (diff.Count > 0) + { + result = true; + + Console.WriteLine($"*** {prefix} {set1name} but not {set2name}:"); + + foreach (var d in diff) + { + Console.WriteLine(d.ToString()); + } + } + } + private TypeDesc FindType(CompilerTypeSystemContext context, string typeName) { ModuleDesc systemModule = context.SystemModule; - TypeDesc foundType = systemModule.GetTypeByCustomAttributeTypeName(typeName); + TypeDesc foundType = systemModule.GetTypeByCustomAttributeTypeName(typeName, false, (typeDefName, module, throwIfNotFound) => + { + return (MetadataType)context.GetCanonType(typeDefName) + ?? CustomAttributeTypeNameParser.ResolveCustomAttributeTypeDefinitionName(typeDefName, module, throwIfNotFound); + }); if (foundType == null) throw new CommandLineException($"Type '{typeName}' not found"); - TypeDesc classLibCanon = systemModule.GetType("System", "__Canon", false); - TypeDesc classLibUniCanon = systemModule.GetType("System", "__UniversalCanon", false); - - return foundType.ReplaceTypesInConstructionOfType( - new TypeDesc[] { classLibCanon, classLibUniCanon }, - new TypeDesc[] { context.CanonType, context.UniversalCanonType }); + return foundType; } private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemContext context) @@ -406,10 +581,34 @@ namespace ILCompiler return method; } + private static bool DumpReproArguments(CodeGenerationFailedException ex) + { + Console.WriteLine("To repro, add following arguments to the command line:"); + + MethodDesc failingMethod = ex.Method; + + var formatter = new CustomAttributeTypeNameFormatter((IAssemblyDesc)failingMethod.Context.SystemModule); + + Console.Write($"--singlemethodtypename \"{formatter.FormatName(failingMethod.OwningType, true)}\""); + Console.Write($" --singlemethodname {failingMethod.Name}"); + + for (int i = 0; i < failingMethod.Instantiation.Length; i++) + Console.Write($" --singlemethodgenericarg \"{formatter.FormatName(failingMethod.Instantiation[i], true)}\""); + + return false; + } + private static int Main(string[] args) { #if DEBUG - return new Program().Run(args); + try + { + return new Program().Run(args); + } + catch (CodeGenerationFailedException ex) when (DumpReproArguments(ex)) + { + throw new NotSupportedException(); // Unreachable + } #else try { diff --git a/external/corert/src/ILCompiler/src/RdXmlRootProvider.cs b/external/corert/src/ILCompiler/src/RdXmlRootProvider.cs index 5093709bd3..d55f877207 100644 --- a/external/corert/src/ILCompiler/src/RdXmlRootProvider.cs +++ b/external/corert/src/ILCompiler/src/RdXmlRootProvider.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Linq; using System.Xml.Linq; @@ -61,6 +62,8 @@ namespace ILCompiler ModuleDesc assembly = _context.ResolveAssembly(new AssemblyName(assemblyNameAttribute.Value)); + rootProvider.RootModuleMetadata(assembly, "RD.XML root"); + var dynamicDegreeAttribute = assemblyElement.Attribute("Dynamic"); if (dynamicDegreeAttribute != null) { @@ -91,16 +94,63 @@ namespace ILCompiler var typeNameAttribute = typeElement.Attribute("Name"); if (typeNameAttribute == null) throw new Exception(); + string typeName = typeNameAttribute.Value; + TypeDesc type = containingModule.GetTypeByCustomAttributeTypeName(typeName); var dynamicDegreeAttribute = typeElement.Attribute("Dynamic"); if (dynamicDegreeAttribute != null) { if (dynamicDegreeAttribute.Value != "Required All") throw new NotSupportedException(); + + RootType(rootProvider, type); } - string typeName = typeNameAttribute.Value; - RootType(rootProvider, containingModule.GetTypeByCustomAttributeTypeName(typeName)); + foreach (var element in typeElement.Elements()) + { + switch (element.Name.LocalName) + { + case "Method": + ProcessMethodDirective(rootProvider, containingModule, type, element); + break; + default: + throw new NotSupportedException(); + } + } + } + + private void ProcessMethodDirective(IRootingServiceProvider rootProvider, ModuleDesc containingModule, TypeDesc containingType, XElement methodElement) + { + var methodNameAttribute = methodElement.Attribute("Name"); + if (methodNameAttribute == null) + throw new Exception(); + string methodName = methodNameAttribute.Value; + MethodDesc method = containingType.GetMethod(methodName, null); + + var instArgs = new List(); + foreach (var element in methodElement.Elements()) + { + switch (element.Name.LocalName) + { + case "GenericArgument": + string instArgName = element.Attribute("Name").Value; + instArgs.Add(containingModule.GetTypeByCustomAttributeTypeName(instArgName)); + break; + default: + throw new NotSupportedException(); + } + } + + if (instArgs.Count != method.Instantiation.Length) + throw new Exception(); + + if (instArgs.Count > 0) + { + var methodInst = new Instantiation(instArgs.ToArray()); + method = method.MakeInstantiatedMethod(methodInst); + } + + RootMethod(rootProvider, method); } private void RootType(IRootingServiceProvider rootProvider, TypeDesc type) @@ -118,32 +168,34 @@ namespace ILCompiler if (method.HasInstantiation) continue; - try - { - LibraryRootProvider.CheckCanGenerateMethod(method); - - // Virtual methods should be rooted as if they were called virtually - if (method.IsVirtual) - { - MethodDesc slotMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method); - rootProvider.RootVirtualMethodForReflection(slotMethod, "RD.XML root"); - } - - if (!method.IsAbstract) - rootProvider.AddCompilationRoot(method, "RD.XML root"); - } - catch (TypeSystemException) - { - // TODO: fail compilation if a switch was passed - - // Individual methods can fail to load types referenced in their signatures. - // Skip them in library mode since they're not going to be callable. - continue; - - // TODO: Log as a warning - } + RootMethod(rootProvider, method); } } } + + private void RootMethod(IRootingServiceProvider rootProvider, MethodDesc method) + { + try + { + LibraryRootProvider.CheckCanGenerateMethod(method); + + // Virtual methods should be rooted as if they were called virtually + if (method.IsVirtual) + rootProvider.RootVirtualMethodForReflection(method, "RD.XML root"); + + if (!method.IsAbstract) + rootProvider.AddCompilationRoot(method, "RD.XML root"); + } + catch (TypeSystemException) + { + // TODO: fail compilation if a switch was passed + + // Individual methods can fail to load types referenced in their signatures. + // Skip them in library mode since they're not going to be callable. + return; + + // TODO: Log as a warning + } + } } } diff --git a/external/corert/src/ILCompiler/src/ilc.runtimeconfig.json b/external/corert/src/ILCompiler/src/ilc.runtimeconfig.json index d0437745b1..a1dfd923b1 100755 --- a/external/corert/src/ILCompiler/src/ilc.runtimeconfig.json +++ b/external/corert/src/ILCompiler/src/ilc.runtimeconfig.json @@ -1,8 +1,7 @@ { "runtimeOptions": { - "framework": { - "name": "Microsoft.NETCore.App", - "version": "1.0.1" + "configProperties": { + "Microsoft.NETCore.DotNetHostPolicy.SetAppPaths": true } } -} \ No newline at end of file +} diff --git a/external/corert/src/ILCompiler/src/project.json b/external/corert/src/ILCompiler/src/project.json deleted file mode 100644 index 699cbbf77c..0000000000 --- a/external/corert/src/ILCompiler/src/project.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "dependencies": { - "NETStandard.Library": "1.6.1", - "System.Reflection.Metadata": "1.4.2", - "System.CommandLine": "0.1.0-e160909-1" - }, - "frameworks": { - "netstandard1.6": {} - } -} diff --git a/external/corert/src/ILVerify/ILVerify.sln b/external/corert/src/ILVerify/ILVerify.sln new file mode 100644 index 0000000000..a26a9be5c1 --- /dev/null +++ b/external/corert/src/ILVerify/ILVerify.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26510.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILVerify", "src\ILVerify.csproj", "{56AA4730-39A4-4B48-95E9-89E8A29F0A06}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILVerify.Tests", "tests\ILVerify.Tests.csproj", "{1228E4B6-E5E5-414A-94EC-69B792984FAB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {56AA4730-39A4-4B48-95E9-89E8A29F0A06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {56AA4730-39A4-4B48-95E9-89E8A29F0A06}.Debug|Any CPU.Build.0 = Debug|Any CPU + {56AA4730-39A4-4B48-95E9-89E8A29F0A06}.Release|Any CPU.ActiveCfg = Release|Any CPU + {56AA4730-39A4-4B48-95E9-89E8A29F0A06}.Release|Any CPU.Build.0 = Release|Any CPU + {1228E4B6-E5E5-414A-94EC-69B792984FAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1228E4B6-E5E5-414A-94EC-69B792984FAB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1228E4B6-E5E5-414A-94EC-69B792984FAB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1228E4B6-E5E5-414A-94EC-69B792984FAB}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/external/corert/src/ILVerify/README.md b/external/corert/src/ILVerify/README.md index c2ebe27ec4..662bf8ca2e 100644 --- a/external/corert/src/ILVerify/README.md +++ b/external/corert/src/ILVerify/README.md @@ -16,6 +16,73 @@ Historically on Full Framework IL generators used PEVerify to make sure that the - It should be easy to add new verification rules - Fast spin up/tear down. +## The codebase +The project targets netcoreapp2.0 and uses the new .csproj based project format. If you want to open and compile it with Visual Studio then you need a version, which supports .NET Core 2.0 tooling. This is supported in Visual Studio 2017 Update 3 (Version 15.3) or later. The other option is to use command (with .NET Core 2.0 tooling). + +## Tests + +To test ILVerify we have small methods checked in as .il files testing specific verification scenarios. These tests live under [src/ILVerify/tests/ILTests](https://github.com/dotnet/corert/tree/master/src/ILVerify/tests/ILTests). Tests are grouped into .il files based on functionalities they test. There is no strict policy here, the goal is to have a few dozen .il files instead of thousands containing each only a single method. + +Currently the IL files are NOT compiled automatically. You have to compile manually (We want to automatize this step later): + +``` +ilasm [filename.il] /dll /ERROR +``` + +Note: if you run the tests and get an error similar to this then it means that the .il files were not compiled, or none of them contained methods that follow the naming convention described below: + +``` +Result Message: System.InvalidOperationException : No data found for ILVerify.Tests.ILMethodTester.TestMethodsWithInvalidIL +``` + +The test project itself is under [src/ILVerify/tests](https://github.com/dotnet/corert/tree/master/src/ILVerify/tests) + + Method names in the .il files must follow the following naming convention: + +### Methods with Valid IL: + +``` +[FriendlyName]_Valid +``` +The method must contain 1 '`_`'. + - The part before the `_` is a friendly name describing what the method does. + - The word after the `_` must be 'Valid' (Case sensitive) + +E.g.: ```SimpleAdd_Valid``` + +### Methods with Invalid IL: +``` +[FriendlyName]_Invalid_[ExpectedVerifierError1].[ExpectedVerifierError2]....[ExpectedVerifierErrorN] +``` + +The method name must contain 2 '`_`' characters. + 1. part: a friendly name + 2. part: must be the word 'Invalid' (Case sensitive) + 3. part: the expected [VerifierErrors](https://github.com/dotnet/corert/blob/master/src/ILVerify/src/VerifierError.cs) as string separated by '.'. We assert on these errors; the test fails if ILVerify does not report these errors. + + E.g.: ```SimpleAdd_Invalid_ExpectedNumericType``` + +### Methods with special names: + +In order to test methods with special names (e.g. '.ctor'), the specialname method is defined as usual and a separate empty method is added to the type: +``` +special.[FriendlyName].[SpecialName]_[Valid | Invalid]_[ExpectedVerifierError1].[ExpectedVerifierError2]....[ExpectedVerifierErrorN] +``` + +The format of the special test method is equal to normal valid or invalid tests, except that the first part must contain 3 sub-parts separated by '`.`': + 1. part: the '`special`' prefix + 2. part: a friendly name + 3. part: the name of the specialname method to actually test + +Additionally the method signature of the special test method must be equal to the signature of the method that shall be tested. + + E.g.: In order to test a specific invalid constructor method the specialname `.ctor` method is defined as usual, while an additional method ```'special.SimpleAdd..ctor_Invalid_StackUnexpected'``` is defined. + + +The methods are automatically fed into appropriate XUnit theories based on the naming convention. Methods not following this naming conventions are ignored by the test scaffolding system. + +You can run the tests either in Visual Studio (in Test Explorer) or with the ```dotnet test ``` command from the command line. + ## How to contribute All ILVerify issues are labeled with [area-ILVerification](https://github.com/search?utf8=%E2%9C%93&q=label%3Aarea-ILVerification&type=). @@ -27,8 +94,10 @@ Currently every IL command falls into one of these categories: - Partially implemented: These are typically methods with TODOs in it. As the first phase we want to make sure that for every command the stack is correctly maintained, therefore for some commands we either have no verification or we have only a not complete verification. You can also pick one of these and finish it. - Implemented: find and fix bugs ;) . +Another option to contribute is to write tests (see Tests section). + Useful sources: - [PEVerify source code](https://github.com/lewischeng-ms/sscli/blob/master/clr/src/jit64/newverify.cpp) - [RyuJIT source code](https://github.com/dotnet/coreclr/blob/master/src/jit), specifically: [exception handling specific part](https://github.com/dotnet/coreclr/blob/master/src/jit/jiteh.cpp), [importer.cpp](https://github.com/dotnet/coreclr/blob/master/src/jit/importer.cpp) (look for `Compiler::ver`, `Verify`, `VerifyOrReturn`, and `VerifyOrReturnSpeculative`), [_typeinfo.h](https://github.com/dotnet/coreclr/blob/master/src/jit/_typeinfo.h), [typeinfo.cpp](https://github.com/dotnet/coreclr/blob/master/src/jit/typeinfo.cpp) - [ECMA-335 standard](https://www.ecma-international.org/publications/standards/Ecma-335.htm) - - [Expert .NET 2.0 IL Assembler book](http://www.apress.com/us/book/9781590596463) by Serge Lidin \ No newline at end of file + - [Expert .NET 2.0 IL Assembler book](http://www.apress.com/us/book/9781590596463) by Serge Lidin diff --git a/external/corert/src/ILVerify/src/AccessVerificationHelpers.cs b/external/corert/src/ILVerify/src/AccessVerificationHelpers.cs new file mode 100644 index 0000000000..8966432731 --- /dev/null +++ b/external/corert/src/ILVerify/src/AccessVerificationHelpers.cs @@ -0,0 +1,336 @@ +// 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.Diagnostics; +using System.Reflection; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILVerify +{ + internal static class AccessVerificationHelpers + { + /// + /// Returns whether the class can access the class . + /// + internal static bool CanAccess(this TypeDesc currentClass, TypeDesc targetClass) + { + if (targetClass.IsGenericParameter || targetClass.IsSignatureVariable) + return true; // Generic parameters are always accessible + + if (targetClass.IsParameterizedType) + return currentClass.CanAccess(((ParameterizedType)targetClass).ParameterType); + +#if false + // perform transparency check on the type, if the caller is transparent + if ((NULL != pCurrentMD) && Security::IsTransparentMethod(pCurrentMD)) + { + // check if type is visible outside the assembly + if (!IsTypeVisibleOutsideAssembly(pTargetClass)) + { + // check transparent/critical on type + if (!Security::CheckNonPublicCriticalAccess(pCurrentMD, NULL, NULL, pTargetClass)) + return FALSE; + } + } +#endif + + // Check access to class instantiations if generic class + if (targetClass.HasInstantiation && !currentClass.CanAccessInstantiation(targetClass.Instantiation)) + return false; + + var currentTypeDef = (MetadataType)currentClass.GetTypeDefinition(); + var targetTypeDef = (EcmaType)targetClass.GetTypeDefinition(); + + var targetContainingType = targetTypeDef.ContainingType; + if (targetContainingType == null) + { + // a non-nested class can be either all public or accessible only from its own assembly (and friends) + if ((targetTypeDef.Attributes & TypeAttributes.Public) != 0) + return true; + else + return currentTypeDef.Module == targetTypeDef.Module || targetTypeDef.Module.GrantsFriendAccessTo(currentTypeDef.Module); + } + + // Target class is nested + MethodAttributes visibility = NestedToMethodAccessAttribute(targetTypeDef.Attributes); + + // Translate access check into member access check, i.e. check whether the current class can access + // a member of the enclosing class with the visibility of target class + return currentTypeDef.CanAccessMember(targetContainingType, visibility, null); + } + + /// + /// Returns whether the class '' can access the method '' through + /// the instance ''. The instance can be null, if the method to be accessed is static. + /// + internal static bool CanAccess(this TypeDesc currentType, MethodDesc targetMethod, TypeDesc instance = null) + { + // If generic method, check instantiation access + if (targetMethod.HasInstantiation && !currentType.CanAccessInstantiation(targetMethod.Instantiation)) + return false; + + var targetMethodDef = targetMethod.GetTypicalMethodDefinition() as EcmaMethod; + var currentTypeDef = (MetadataType)currentType.GetTypeDefinition(); + + if (targetMethodDef != null) // Non metadata methods, such as ArrayMethods, may be null at this point + { + if (!currentTypeDef.CanAccessMember(targetMethod.OwningType, targetMethodDef.Attributes & MethodAttributes.MemberAccessMask, instance)) + return false; + } + + return currentTypeDef.CanAccessMethodSignature(targetMethod); + } + + /// + /// Returns whether the class '' can access the field '' through + /// the instance ''. The instance can be null, if the field to be accessed is static. + /// + internal static bool CanAccess(this TypeDesc currentType, FieldDesc targetField, TypeDesc instance = null) + { + // Check access to field owning type + var targetFieldDef = (EcmaField)targetField.GetTypicalFieldDefinition(); + var currentTypeDef = (MetadataType)currentType.GetTypeDefinition(); + + var targetFieldAccess = FieldToMethodAccessAttribute(targetFieldDef.Attributes); + + if (!currentTypeDef.CanAccessMember(targetField.OwningType, targetFieldAccess, instance)) + return false; + + // Check access to field type itself + return currentType.CanAccess(targetField.FieldType); + } + + private static bool CanAccessMember(this MetadataType currentType, TypeDesc targetType, MethodAttributes memberVisibility, TypeDesc instance) + { + if (instance == null) + instance = currentType; + + // Check access to class defining member + if (!currentType.CanAccess(targetType)) + return false; + + var targetTypeDef = (MetadataType)targetType.GetTypeDefinition(); + + if (memberVisibility == MethodAttributes.Public) + return true; + + // This is module-scope checking, to support C++ file & function statics. + if (memberVisibility == MethodAttributes.PrivateScope) + return currentType.Module == targetTypeDef.Module; + + if (memberVisibility == MethodAttributes.Assembly) + return currentType.Module == targetTypeDef.Module || targetTypeDef.Module.GrantsFriendAccessTo(currentType.Module); + + if (memberVisibility == MethodAttributes.FamANDAssem) + { + if (currentType.Module != targetTypeDef.Module && !targetTypeDef.Module.GrantsFriendAccessTo(currentType.Module)) + return false; + } + + // Nested classes can access all members of their parent class. + do + { + // Classes have access to all of their own members + if (currentType == targetTypeDef) + return true; + + switch (memberVisibility) + { + case MethodAttributes.FamORAssem: + if (currentType.Module == targetTypeDef.Module || targetTypeDef.Module.GrantsFriendAccessTo(currentType.Module)) + return true; + + // Check if current class is subclass of target + if (CanAccessFamily(currentType, targetTypeDef, instance)) + return true; + break; + case MethodAttributes.Family: + case MethodAttributes.FamANDAssem: + // Assembly acces was already checked earlier, so only need to check family access + if (CanAccessFamily(currentType, targetTypeDef, instance)) + return true; + break; + case MethodAttributes.Private: + break; // Already handled by loop + default: + Debug.Assert(false); + break; + } + + var containingType = currentType.ContainingType; + if (containingType != null) + currentType = (MetadataType)containingType.GetTypeDefinition(); + else + currentType = null; + } while (currentType != null); + + return false; + } + + private static bool CanAccessInstantiation(this TypeDesc currentType, Instantiation instantiation) + { + foreach (var inst in instantiation) + { + if (!currentType.CanAccess(inst)) + return false; + } + + return true; + } + + private static bool CanAccessMethodSignature(this TypeDesc currentType, MethodDesc targetMethod) + { + var methodSig = targetMethod.Signature; + + // Check return type + var returnType = methodSig.ReturnType; + if (returnType.IsParameterizedType) + returnType = ((ParameterizedType)returnType).ParameterType; + + if (!returnType.IsGenericParameter && !returnType.IsSignatureVariable // Generic parameters are always accessible + && !returnType.IsVoid) + { + if (!currentType.CanAccess(returnType)) + return false; + } + + // Check arguments + for (int i = 0; i < methodSig.Length; ++i) + { + var param = methodSig[i]; + if (param.IsByRef) + param = ((ByRefType)param).ParameterType; + + if (param.IsGenericParameter || param.IsSignatureVariable) + continue; // Generic parameters are always accessible + + if (!currentType.CanAccess(param)) + return false; + } + + return true; + } + + private static bool CanAccessFamily(TypeDesc currentType, TypeDesc targetTypeDef, TypeDesc instanceType) + { + // Iterate through all containing types of instance + while (instanceType != null) + { + var curInstTypeDef = instanceType; + var currentTypeDef = currentType.GetTypeDefinition(); + // Iterate through all super types of current instance type + while (curInstTypeDef != null) + { + if (currentTypeDef == curInstTypeDef.GetTypeDefinition()) + { + // At this point we know that the instance type is able to access the same family fields as current type + // Now iterate through all super types of current type to see if current type can access family target type + while (currentTypeDef != null) + { + if (currentTypeDef == targetTypeDef) + return true; + + currentTypeDef = currentTypeDef.BaseType; + if (currentTypeDef != null) + currentTypeDef = currentTypeDef.GetTypeDefinition(); + } + + return false; + } + + curInstTypeDef = curInstTypeDef.BaseType; + } + + instanceType = ((MetadataType)instanceType.GetTypeDefinition()).ContainingType; + } + + return false; + } + + private static bool GrantsFriendAccessTo(this ModuleDesc module, ModuleDesc friendModule) + { + var assembly = (EcmaAssembly)module; + var friendName = ((IAssemblyDesc)friendModule).GetName(); + + foreach (var attribute in assembly.GetDecodedCustomAttributes("System.Runtime.CompilerServices", "InternalsVisibleToAttribute")) + { + AssemblyName friendAttributeName = new AssemblyName((string)attribute.FixedArguments[0].Value); + if (!friendName.Name.Equals(friendAttributeName.Name, StringComparison.OrdinalIgnoreCase)) + continue; + + // Comparing PublicKeyToken, since GetPublicKey returns null due to a bug + if (IsSamePublicKey(friendAttributeName.GetPublicKeyToken(), friendName.GetPublicKeyToken())) + return true; + } + return false; + } + + private static bool IsSamePublicKey(byte[] key1, byte[] key2) + { + if (key1 == null) + return key2 == null || key2.Length == 0; + if (key2 == null) + return key1 == null || key1.Length == 0; + + if (key1.Length != key2.Length) + return false; + + for (int i = 0; i < key1.Length; ++i) + { + if (key1[i] != key2[i]) + return false; + } + + return true; + } + + private static MethodAttributes NestedToMethodAccessAttribute(TypeAttributes nestedVisibility) + { + switch (nestedVisibility & TypeAttributes.VisibilityMask) + { + case TypeAttributes.NestedAssembly: + return MethodAttributes.Assembly; + case TypeAttributes.NestedFamANDAssem: + return MethodAttributes.FamANDAssem; + case TypeAttributes.NestedFamily: + return MethodAttributes.Family; + case TypeAttributes.NestedFamORAssem: + return MethodAttributes.FamORAssem; + case TypeAttributes.NestedPrivate: + return MethodAttributes.Private; + case TypeAttributes.NestedPublic: + return MethodAttributes.Public; + default: + Debug.Assert(false); + return MethodAttributes.Public; + } + } + + private static MethodAttributes FieldToMethodAccessAttribute(FieldAttributes attributes) + { + switch (attributes & FieldAttributes.FieldAccessMask) + { + case FieldAttributes.Assembly: + return MethodAttributes.Assembly; + case FieldAttributes.FamANDAssem: + return MethodAttributes.FamANDAssem; + case FieldAttributes.Family: + return MethodAttributes.Family; + case FieldAttributes.FamORAssem: + return MethodAttributes.FamORAssem; + case FieldAttributes.Private: + return MethodAttributes.Private; + case FieldAttributes.PrivateScope: + return MethodAttributes.PrivateScope; + case FieldAttributes.Public: + return MethodAttributes.Public; + default: + Debug.Assert(false); + return MethodAttributes.Public; + } + } + } +} diff --git a/external/corert/src/ILVerify/src/AssemblyInfo.cs b/external/corert/src/ILVerify/src/AssemblyInfo.cs new file mode 100644 index 0000000000..8e392ff7bc --- /dev/null +++ b/external/corert/src/ILVerify/src/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("ILVerify.Tests")] diff --git a/external/corert/src/ILVerify/src/ILImporter.StackValue.cs b/external/corert/src/ILVerify/src/ILImporter.StackValue.cs index 076e2e57a8..ad2497b1c0 100644 --- a/external/corert/src/ILVerify/src/ILImporter.StackValue.cs +++ b/external/corert/src/ILVerify/src/ILImporter.StackValue.cs @@ -6,18 +6,85 @@ using System; using System.Diagnostics; using Internal.TypeSystem; +using ILVerify; namespace Internal.IL { struct StackValue { + [Flags] + public enum StackValueFlags + { + None = 0, + ReadOnly = 1 << 1, + PermanentHome = 1 << 2, + ThisPtr = 1 << 3, + } + private StackValueFlags Flags; + public readonly StackValueKind Kind; public readonly TypeDesc Type; + public readonly MethodDesc Method; - private StackValue(StackValueKind kind, TypeDesc type = null) + private StackValue(StackValueKind kind, TypeDesc type = null, MethodDesc method = null, StackValueFlags flags = StackValueFlags.None) { this.Kind = kind; this.Type = type; + this.Method = method; + this.Flags = flags; + } + + public void SetIsReadOnly() + { + Debug.Assert(Kind == StackValueKind.ByRef); + Flags |= StackValueFlags.ReadOnly; + } + + public void SetIsPermanentHome() + { + Debug.Assert(Kind == StackValueKind.ByRef); + Flags |= StackValueFlags.PermanentHome; + } + + public void SetIsThisPtr() + { + Flags |= StackValueFlags.ThisPtr; + } + + public bool IsReadOnly + { + get { return (Flags & StackValueFlags.ReadOnly) == StackValueFlags.ReadOnly; } + } + + public bool IsPermanentHome + { + get { return (Flags & StackValueFlags.PermanentHome) == StackValueFlags.PermanentHome; } + } + + public bool IsThisPtr + { + get { return (Flags & StackValueFlags.ThisPtr) == StackValueFlags.ThisPtr; } + } + + public bool IsNullReference + { + get { return Kind == StackValueKind.ObjRef && Type == null; } + } + + public bool IsMethod + { + get { return Kind == StackValueKind.NativeInt && Method != null; } + } + + public bool IsBoxedValueType + { + get { return Kind == StackValueKind.ObjRef && Type.IsValueType; } + } + + public StackValue DereferenceByRef() + { + Debug.Assert(Kind == StackValueKind.ByRef && Type != null, "Cannot dereference"); + return CreateFromType(Type); } static public StackValue CreateUnknown() @@ -45,9 +112,16 @@ namespace Internal.IL return new StackValue(StackValueKind.ValueType, type); } - static public StackValue CreateByRef(TypeDesc type) + static public StackValue CreateByRef(TypeDesc type, bool readOnly = false, bool permanentHome = false) { - return new StackValue(StackValueKind.ByRef, type); + return new StackValue(StackValueKind.ByRef, type, null, + (readOnly ? StackValueFlags.ReadOnly : StackValueFlags.None) | + (permanentHome ? StackValueFlags.PermanentHome : StackValueFlags.None)); + } + + static public StackValue CreateMethod(MethodDesc method) + { + return new StackValue(StackValueKind.NativeInt, null, method); } static public StackValue CreateFromType(TypeDesc type) @@ -78,13 +152,45 @@ namespace Internal.IL case TypeFlags.ByRef: return CreateByRef(((ByRefType)type).ParameterType); default: - if (type.IsValueType) + if (type.IsValueType || type.IsGenericParameter) return CreateValueType(type); else return CreateObjRef(type); } } + public override bool Equals(object obj) + { + if (Object.ReferenceEquals(this, obj)) + return true; + + if (!(obj is StackValue)) + return false; + + var value = (StackValue)obj; + return this.Kind == value.Kind && this.Flags == value.Flags && this.Type == value.Type; + } + + public static bool operator ==(StackValue left, StackValue right) + { + return left.Equals(right); + } + + public static bool operator !=(StackValue left, StackValue right) + { + return !(left == right); + } + + public override int GetHashCode() + { + const int prime = 17; + int hash = 23; + hash = (hash * prime) ^ Type.GetHashCode(); + hash = (hash * prime) ^ Kind.GetHashCode(); + hash = (hash * prime) ^ Flags.GetHashCode(); + return hash; + } + // For now, match PEVerify type formating to make it easy to compare with baseline static string TypeToStringForByRef(TypeDesc type) { @@ -122,7 +228,7 @@ namespace Internal.IL case StackValueKind.Float: return "Double"; case StackValueKind.ByRef: - return "address of " + TypeToStringForByRef(Type); + return (IsReadOnly ? "readonly " : "") + "address of " + TypeToStringForByRef(Type); case StackValueKind.ObjRef: return (Type != null) ? "ref '" + Type.ToString() + "'" : "Nullobjref 'NullReference'"; case StackValueKind.ValueType: @@ -135,39 +241,232 @@ namespace Internal.IL partial class ILImporter { - static TypeFlags GetReducedTypeCategory(TypeDesc type) + /// + /// Merges two stack values to a common stack value as defined in the ECMA-335 + /// standard III.1.8.1.3 (Merging stack states). + /// + /// The value to be merged with . + /// The value to be merged with . + /// The resulting type of merging and . + /// True if merge operation was successful, false if the merge operation failed. + public static bool TryMergeStackValues(StackValue valueA, StackValue valueB, out StackValue merged) { - var category = type.Category; + merged = valueA; - switch (type.Category) + if (valueB.IsReadOnly) + merged.SetIsReadOnly(); + + // Same type + if (valueA.Kind == valueB.Kind && valueA.Type == valueB.Type) + return true; + + if (valueA.IsNullReference) { - case TypeFlags.Byte: return TypeFlags.SByte; - case TypeFlags.UInt16: return TypeFlags.Int16; - case TypeFlags.UInt32: return TypeFlags.Int32; - case TypeFlags.UInt64: return TypeFlags.Int64; - case TypeFlags.UIntPtr: return TypeFlags.IntPtr; + //Null can be any reference type + if (valueB.Kind == StackValueKind.ObjRef) + { + merged = valueB; + return true; + } + } + else if (valueA.Kind == StackValueKind.ObjRef) + { + if (valueB.Kind != StackValueKind.ObjRef) + return false; - case TypeFlags.SByte: - case TypeFlags.Int16: - case TypeFlags.Int32: - case TypeFlags.Int64: - case TypeFlags.IntPtr: - case TypeFlags.Boolean: - case TypeFlags.Char: - case TypeFlags.Single: - case TypeFlags.Double: - return category; + // Null can be any reference type + if (valueB.IsNullReference) + return true; + + // Merging classes always succeeds since System.Object always works + merged = StackValue.CreateFromType(MergeObjectReferences(valueA.Type, valueB.Type)); + return true; } - return TypeFlags.Unknown; + return false; + } + + // Used to merge stack states. + static TypeDesc MergeObjectReferences(TypeDesc classA, TypeDesc classB) + { + if (classA == classB) + return classA; + + // Array case + if (classA.IsArray) + { + if (classB.IsArray) + return MergeArrayTypes((ArrayType)classA, (ArrayType)classB); + } + + // Assumes generic parameters are boxed at this point. + // Return supertype, if related, otherwhise object + if (classA.IsGenericParameter || classB.IsGenericParameter) + { + if (classA.CanCastTo(classB)) + return classB; + if (classB.CanCastTo(classA)) + return classA; + + return classA.Context.GetWellKnownType(WellKnownType.Object); + } + + if (classB.IsInterface) + { + if (classA.IsInterface) + return MergeInterfaceWithInterface(classA, classB); + else + return MergeClassWithInterface(classA, classB); + } + else if (classA.IsInterface) + return MergeClassWithInterface(classB, classA); + + return MergeClassWithClass(classA, classB); + } + + static TypeDesc MergeInterfaceWithInterface(TypeDesc interfA, TypeDesc interfB) + { + foreach (var interf in interfA.RuntimeInterfaces) + { + if (interf == interfB) + return interfB; // Interface A extends interface B + } + + foreach (var interf in interfB.RuntimeInterfaces) + { + if (interf == interfA) + return interfA; // Interface B extends interface A + } + + // Get common supertype + foreach (var subInterfB in interfB.RuntimeInterfaces) + { + foreach (var subInterfA in interfA.RuntimeInterfaces) + { + if (subInterfA == subInterfB) + return subInterfA; + } + } + + // No compatible interface found, return Object + return interfA.Context.GetWellKnownType(WellKnownType.Object); + } + + static TypeDesc MergeClassWithClass(TypeDesc classA, TypeDesc classB) + { + // Find class hierarchy depth for both classes + int aDepth = 0; + int bDepth = 0; + TypeDesc curType; + + for (curType = classA; curType != null; curType = curType.BaseType) + aDepth++; + + for (curType = classB; curType != null; curType = curType.BaseType) + bDepth++; + + // Walk up superclass chain until both classes at same level + while (aDepth > bDepth) + { + classA = classA.BaseType; + aDepth--; + } + + while (bDepth > aDepth) + { + classB = classB.BaseType; + bDepth--; + } + + while (classA != classB) + { + classA = classA.BaseType; + classB = classB.BaseType; + } + + // At this point we should either have found a common supertype or end up at System.Object + Debug.Assert(classA != null); + + return classA; + } + + static TypeDesc MergeClassWithInterface(TypeDesc classType, TypeDesc interfaceType) + { + // Check if class implements interface + foreach (var interf in classType.RuntimeInterfaces) + { + if (interf == interfaceType) + return interfaceType; + } + + // Check if class and interface implement common interface + foreach (var iInterf in interfaceType.RuntimeInterfaces) + { + foreach (var cInterf in classType.RuntimeInterfaces) + { + if (iInterf == cInterf) + return iInterf; + } + } + + // No compatible merge, return Object + return classType.Context.GetWellKnownType(WellKnownType.Object); + } + + static TypeDesc MergeArrayTypes(ArrayType arrayTypeA, ArrayType arrayTypeB) + { + if (arrayTypeA == arrayTypeB) + return arrayTypeA; + + var basicArrayType = arrayTypeA.Context.GetWellKnownType(WellKnownType.Array); + + // If non matching rank, common ancestor = System.Array + var rank = arrayTypeA.Rank; + var mergeCategory = arrayTypeA.Category; + if (rank != arrayTypeB.Rank) + return basicArrayType; + + if (arrayTypeA.Category != arrayTypeB.Category) + { + if (rank == 1) + mergeCategory = TypeFlags.Array; + else + return basicArrayType; + } + + // Determine merged array element type + TypeDesc mergedElementType; + if (arrayTypeA.ElementType == arrayTypeB.ElementType) + mergedElementType = arrayTypeA.ElementType; + else if (arrayTypeA.ElementType.IsArray && arrayTypeB.ElementType.IsArray) + { + // Array of arrays -> find merged type + mergedElementType = MergeArrayTypes(arrayTypeA, arrayTypeB); + } + //Both array element types are ObjRefs + else if ((!arrayTypeA.ElementType.IsValueType && !arrayTypeA.ElementType.IsByRef) && + (!arrayTypeB.ElementType.IsValueType && !arrayTypeB.ElementType.IsByRef)) + { + // Find common ancestor of the element types + mergedElementType = MergeObjectReferences(arrayTypeA.ElementType, arrayTypeB.ElementType); + } + else + { + // Array element types have nothing in common + return basicArrayType; + } + + Debug.Assert(mergeCategory == TypeFlags.Array || mergeCategory == TypeFlags.SzArray); + + if (mergeCategory == TypeFlags.SzArray) + return arrayTypeA.Context.GetArrayType(mergedElementType); + else + return arrayTypeA.Context.GetArrayType(mergedElementType, rank); } static bool IsSameReducedType(TypeDesc src, TypeDesc dst) { - var srcCategory = GetReducedTypeCategory(src); - if (srcCategory == TypeFlags.Unknown) - return false; - return srcCategory == GetReducedTypeCategory(dst); + return src.GetReducedType() == dst.GetReducedType(); } bool IsAssignable(TypeDesc src, TypeDesc dst, bool allowSizeEquivalence = false) @@ -189,9 +488,12 @@ namespace Internal.IL bool IsAssignable(StackValue src, StackValue dst) { - if (src.Kind == dst.Kind && src.Type == dst.Type) + if (src.Kind == dst.Kind && src.Type == dst.Type && src.IsReadOnly == dst.IsReadOnly) return true; + if (dst.Type == null) + return false; + switch (src.Kind) { case StackValueKind.ObjRef: @@ -211,6 +513,8 @@ namespace Internal.IL return false; case StackValueKind.ByRef: + if (dst.Kind == StackValueKind.ByRef && dst.IsReadOnly) + return src.Type == dst.Type; // TODO: Other cases - variance, etc. @@ -315,7 +619,7 @@ namespace Internal.IL } return FALSE; -#endif +#endif } @@ -330,9 +634,13 @@ namespace Internal.IL switch (dst.Kind) { case StackValueKind.ObjRef: + // ECMA-335 III.1.5 Operand type table, P. 303: + // __cgt.un__ is allowed and verifiable on ObjectRefs (O). This is commonly used when + // comparing an ObjectRef with null(there is no "compare - not - equal" instruction, which + // would otherwise be a more obvious solution) return op == ILOpcode.beq || op == ILOpcode.beq_s || op == ILOpcode.bne_un || op == ILOpcode.bne_un_s || - op == ILOpcode.ceq; + op == ILOpcode.ceq || op == ILOpcode.cgt_un; default: return false; } diff --git a/external/corert/src/ILVerify/src/ILImporter.Verify.cs b/external/corert/src/ILVerify/src/ILImporter.Verify.cs index ca3e29055c..5dde3d6f39 100644 --- a/external/corert/src/ILVerify/src/ILImporter.Verify.cs +++ b/external/corert/src/ILVerify/src/ILImporter.Verify.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Diagnostics; using Internal.TypeSystem; @@ -38,18 +37,18 @@ namespace Internal.IL partial class ILImporter { - MethodDesc _method; - MethodSignature _methodSignature; - TypeSystemContext _typeSystemContext; + readonly MethodDesc _method; + readonly MethodSignature _methodSignature; + readonly TypeSystemContext _typeSystemContext; - TypeDesc _thisType; + readonly TypeDesc _thisType; - MethodIL _methodIL; - byte[] _ilBytes; - LocalVariableDefinition[] _locals; + readonly MethodIL _methodIL; + readonly byte[] _ilBytes; + readonly LocalVariableDefinition[] _locals; - bool _initLocals; - int _maxStack; + readonly bool _initLocals; + readonly int _maxStack; bool[] _instructionBoundaries; // For IL verification @@ -57,6 +56,14 @@ namespace Internal.IL StackValue[] _stack = s_emptyStack; int _stackTop = 0; + bool _isThisInitialized; + bool _modifiesThisPtr; + bool _trackObjCtorState; + + bool[] _validTargetOffsets; + + int? _delegateCreateStart; + class ExceptionRegion { public ILExceptionRegion ILRegion; @@ -81,12 +88,20 @@ namespace Internal.IL class BasicBlock { // Common fields + public enum ImportState : byte + { + Unmarked, + IsPending, + WasVerified + } + public BasicBlock Next; public int StartOffset; - public int EndOffset; + public ImportState State = ImportState.Unmarked; public StackValue[] EntryStack; + public bool IsThisInitialized = false; public bool TryStart; public bool FilterStart; @@ -96,6 +111,19 @@ namespace Internal.IL public int? TryIndex; public int? HandlerIndex; public int? FilterIndex; + + // Used for Backward Branch Constraint + public bool HasPredecessorWithLowerOffset = false; + + public int ErrorCount + { + get; + private set; + } + public void IncrementErrorCount() + { + ErrorCount++; + } }; void EmptyTheStack() => _stackTop = 0; @@ -109,28 +137,62 @@ namespace Internal.IL _stack[_stackTop++] = value; } - StackValue Pop() + StackValue Pop(bool allowUninitThis = false) { FatalCheck(_stackTop > 0, VerifierError.StackUnderflow); - return _stack[--_stackTop]; + var stackValue = _stack[--_stackTop]; + + if (!allowUninitThis) + Check(!_trackObjCtorState || !stackValue.IsThisPtr || _isThisInitialized, VerifierError.UninitStack, stackValue); + + return stackValue; } public ILImporter(MethodDesc method, MethodIL methodIL) { - _method = method; - _methodSignature = method.Signature; _typeSystemContext = method.Context; - if (!_methodSignature.IsStatic) - _thisType = method.OwningType; + // Instantiate method and its owning type + var instantiatedType = method.OwningType; + var instantiatedMethod = method; + if (instantiatedType.HasInstantiation) + { + Instantiation genericTypeInstantiation = InstantiatedGenericParameter.CreateGenericTypeInstantiaton(instantiatedType.Instantiation); + instantiatedType = _typeSystemContext.GetInstantiatedType((MetadataType)instantiatedType, genericTypeInstantiation); + instantiatedMethod = _typeSystemContext.GetMethodForInstantiatedType(instantiatedMethod.GetTypicalMethodDefinition(), (InstantiatedType)instantiatedType); + } - _methodIL = methodIL; + if (instantiatedMethod.HasInstantiation) + { + Instantiation genericMethodInstantiation = InstantiatedGenericParameter.CreateGenericMethodInstantiation( + instantiatedType.Instantiation, instantiatedMethod.Instantiation); + instantiatedMethod = _typeSystemContext.GetInstantiatedMethod(instantiatedMethod, genericMethodInstantiation); + } + _method = instantiatedMethod; + + _methodSignature = _method.Signature; + _methodIL = method == instantiatedMethod ? methodIL : new InstantiatedMethodIL(instantiatedMethod, methodIL); + + // Determine this type + if (!_method.Signature.IsStatic) + { + _thisType = instantiatedType; + + // ECMA-335 II.13.3 Methods of value types, P. 164: + // ... By contrast, instance and virtual methods of value types shall be coded to expect a + // managed pointer(see Partition I) to an unboxed instance of the value type. ... + if (_thisType.IsValueType) + _thisType = _thisType.MakeByRefType(); + } _initLocals = _methodIL.IsInitLocals; _maxStack = _methodIL.MaxStack; + _isThisInitialized = false; + _trackObjCtorState = !_methodSignature.IsStatic && _method.IsConstructor && !method.OwningType.IsValueType; + _ilBytes = _methodIL.GetILBytes(); _locals = _methodIL.GetLocals(); @@ -154,6 +216,7 @@ namespace Internal.IL FindBasicBlocks(); FindEnclosingExceptionRegions(); + InitialPass(); ImportBasicBlocks(); } @@ -170,8 +233,8 @@ namespace Internal.IL for (int j = 0; j < _exceptionRegions.Length; j++) { var r = _exceptionRegions[j].ILRegion; - - if (r.TryOffset <= offset && r.TryOffset + r.TryLength >= offset) + // Check if offset is within the range [TryOffset, TryOffset + TryLength[ + if (r.TryOffset <= offset && offset < r.TryOffset + r.TryLength) { if (!basicBlock.TryIndex.HasValue) { @@ -189,7 +252,8 @@ namespace Internal.IL } } } - if (r.HandlerOffset <= offset && r.HandlerOffset + r.HandlerLength >= offset) + // Check if offset is within the range [HandlerOffset, HandlerOffset + HandlerLength[ + if (r.HandlerOffset <= offset && offset < r.HandlerOffset + r.HandlerLength) { if (!basicBlock.HandlerIndex.HasValue) { @@ -207,7 +271,8 @@ namespace Internal.IL } } } - if(r.FilterOffset != -1 && r.FilterOffset <= offset && r.HandlerOffset - 1 >= offset) + // Check if offset is within the range [FilterOffset, HandlerOffset[ + if (r.FilterOffset != -1 && r.FilterOffset <= offset && offset < r.HandlerOffset ) { if(!basicBlock.FilterIndex.HasValue) { @@ -218,6 +283,198 @@ namespace Internal.IL } } + /// + /// Checks whether the metod's il modifies the this pointer and builds up the + /// array of valid target offsets. + /// + private void InitialPass() + { + FatalCheck(_ilBytes.Length > 0, VerifierError.CodeSizeZero); + + _modifiesThisPtr = false; + _validTargetOffsets = new bool[_ilBytes.Length]; + + bool previousWasPrefix = false; + + _currentOffset = 0; + + while (_currentOffset < _ilBytes.Length) + { + if (!previousWasPrefix) // The instruction following a prefix is not a valid branch target. + _validTargetOffsets[_currentOffset] = true; + + ILOpcode opCode = (ILOpcode)ReadILByte(); + + previousWasPrefix = false; +again: + switch (opCode) + { + // Check this pointer modification + case ILOpcode.starg_s: + case ILOpcode.ldarga_s: + if (ReadILByte() == 0) + { + _modifiesThisPtr = true; + break; + } + break; + case ILOpcode.starg: + case ILOpcode.ldarga: + if (ReadILUInt16() == 0) + { + _modifiesThisPtr = true; + break; + } + break; + + // Keep track of prefixes + case ILOpcode.unaligned: + case ILOpcode.no: + previousWasPrefix = true; + SkipIL(1); + continue; + case ILOpcode.constrained: + previousWasPrefix = true; + SkipIL(4); + continue; + case ILOpcode.tail: + case ILOpcode.volatile_: + case ILOpcode.readonly_: + previousWasPrefix = true; + continue; + + // Check for block predecessors with lower il offset + case ILOpcode.br: + case ILOpcode.leave: + MarkPredecessorWithLowerOffset((int)ReadILUInt32()); + continue; + case ILOpcode.brfalse: + case ILOpcode.brtrue: + case ILOpcode.beq: + case ILOpcode.bge: + case ILOpcode.bgt: + case ILOpcode.ble: + case ILOpcode.blt: + case ILOpcode.bne_un: + case ILOpcode.bge_un: + case ILOpcode.bgt_un: + case ILOpcode.ble_un: + case ILOpcode.blt_un: + MarkPredecessorWithLowerOffset((int)ReadILUInt32()); + break; + case ILOpcode.br_s: + case ILOpcode.leave_s: + MarkPredecessorWithLowerOffset((sbyte)ReadILByte()); + continue; + case ILOpcode.brfalse_s: + case ILOpcode.brtrue_s: + case ILOpcode.beq_s: + case ILOpcode.bge_s: + case ILOpcode.bgt_s: + case ILOpcode.ble_s: + case ILOpcode.blt_s: + case ILOpcode.bne_un_s: + case ILOpcode.bge_un_s: + case ILOpcode.bgt_un_s: + case ILOpcode.ble_un_s: + case ILOpcode.blt_un_s: + MarkPredecessorWithLowerOffset((sbyte)ReadILByte()); + break; + case ILOpcode.switch_: + { + uint count = ReadILUInt32(); + int[] jmpDeltas = new int[count]; + for (uint i = 0; i < count; i++) + jmpDeltas[i] = (int)ReadILUInt32(); + + foreach (int delta in jmpDeltas) + MarkPredecessorWithLowerOffset(delta); + } + break; + + // Skip all other Opcodes + case ILOpcode.ret: + case ILOpcode.throw_: + case ILOpcode.rethrow: + case ILOpcode.endfinally: + case ILOpcode.endfilter: + continue; + case ILOpcode.ldarg_s: + case ILOpcode.ldloc_s: + case ILOpcode.ldloca_s: + case ILOpcode.stloc_s: + case ILOpcode.ldc_i4_s: + SkipIL(1); + break; + case ILOpcode.ldarg: + case ILOpcode.ldloc: + case ILOpcode.ldloca: + case ILOpcode.stloc: + SkipIL(2); + break; + case ILOpcode.ldc_i4: + case ILOpcode.ldc_r4: + case ILOpcode.call: + case ILOpcode.calli: + case ILOpcode.callvirt: + case ILOpcode.cpobj: + case ILOpcode.ldobj: + case ILOpcode.ldstr: + case ILOpcode.newobj: + case ILOpcode.castclass: + case ILOpcode.isinst: + case ILOpcode.unbox: + case ILOpcode.ldfld: + case ILOpcode.ldflda: + case ILOpcode.stfld: + case ILOpcode.ldsfld: + case ILOpcode.ldsflda: + case ILOpcode.stsfld: + case ILOpcode.stobj: + case ILOpcode.box: + case ILOpcode.newarr: + case ILOpcode.ldelema: + case ILOpcode.ldelem: + case ILOpcode.stelem: + case ILOpcode.unbox_any: + case ILOpcode.refanyval: + case ILOpcode.mkrefany: + case ILOpcode.ldtoken: + case ILOpcode.ldftn: + case ILOpcode.ldvirtftn: + case ILOpcode.initobj: + case ILOpcode.sizeof_: + SkipIL(4); + break; + case ILOpcode.jmp: + SkipIL(4); + continue; + case ILOpcode.ldc_i8: + case ILOpcode.ldc_r8: + SkipIL(8); + break; + case ILOpcode.prefix1: + opCode = (ILOpcode)(0x100 + ReadILByte()); + goto again; + default: + break; + } + + if (_currentOffset < _basicBlocks.Length) + { + var fallthrough = _basicBlocks[_currentOffset]; + if (fallthrough != null) + MarkPredecessorWithLowerOffset(0); + } + } + } + + void MarkPredecessorWithLowerOffset(int delta) + { + if (delta >= 0) + _basicBlocks[_currentOffset + delta].HasPredecessorWithLowerOffset = true; + } + void AbortBasicBlockVerification() { throw new LocalVerificationException(); @@ -235,15 +492,35 @@ namespace Internal.IL AbortMethodVerification(); } + // Check whether the condition is true. If not, terminate the verification of current method. + void FatalCheck(bool cond, VerifierError error, StackValue found) + { + if (!Check(cond, error, found)) + AbortMethodVerification(); + } + + // Check whether the condition is true. If not, terminate the verification of current method. + void FatalCheck(bool cond, VerifierError error, StackValue found, StackValue expected) + { + if (!Check(cond, error, found, expected)) + AbortMethodVerification(); + } + // If not, report verification error and continue verification. void VerificationError(VerifierError error) { + if (_currentBasicBlock != null) + _currentBasicBlock.IncrementErrorCount(); + var args = new VerificationErrorArgs() { Code = error, Offset = _currentInstructionOffset }; ReportVerificationError(args); } void VerificationError(VerifierError error, object found) { + if (_currentBasicBlock != null) + _currentBasicBlock.IncrementErrorCount(); + var args = new VerificationErrorArgs() { Code = error, @@ -255,6 +532,9 @@ namespace Internal.IL void VerificationError(VerifierError error, object found, object expected) { + if (_currentBasicBlock != null) + _currentBasicBlock.IncrementErrorCount(); + var args = new VerificationErrorArgs() { Code = error, @@ -334,6 +614,394 @@ namespace Internal.IL VerificationError(VerifierError.StackUnexpected, src, dst); } + private void CheckIsValidLeaveTarget(BasicBlock src, BasicBlock target) + { + if (!_validTargetOffsets[target.StartOffset]) + { + VerificationError(VerifierError.BadJumpTarget); + return; + } + + // If the source is within filter, target shall be within the same + if (src.FilterIndex.HasValue && src.FilterIndex != target.FilterIndex) + { + VerificationError(VerifierError.LeaveOutOfFilter); + } + + // If the source is within fault handler or finally handler, target shall be within the same + if (src.HandlerIndex.HasValue && src.HandlerIndex != target.HandlerIndex) + { + var regionKind = _exceptionRegions[src.HandlerIndex.Value].ILRegion.Kind; + if (regionKind == ILExceptionRegionKind.Fault) + VerificationError(VerifierError.LeaveOutOfFault); + else if (regionKind == ILExceptionRegionKind.Finally) + VerificationError(VerifierError.LeaveOutOfFinally); + } + + // If the source is within a try block, target shall be within the same or an enclosing try block + // or the first instruction of a disjoint try block + // or not within any try block + bool invalidLeaveIntoTry = false; + if (src.TryIndex.HasValue && src.TryIndex != target.TryIndex) + { + if (target.TryIndex.HasValue) + { + ref var srcRegion = ref _exceptionRegions[src.TryIndex.Value].ILRegion; + ref var targetRegion = ref _exceptionRegions[target.TryIndex.Value].ILRegion; + + // Target is not enclosing source + if (targetRegion.TryOffset > srcRegion.TryOffset || + src.StartOffset >= targetRegion.TryOffset + targetRegion.TryLength) + { + // Target is not first instruction + if (target.StartOffset != targetRegion.TryOffset) + { + VerificationError(VerifierError.LeaveIntoTry); + invalidLeaveIntoTry = true; + } + else if (srcRegion.TryOffset <= targetRegion.TryOffset && + srcRegion.TryOffset + srcRegion.TryLength > targetRegion.TryOffset) // Source is enclosing target + { + if (!IsDirectChildRegion(src, target)) + { + VerificationError(VerifierError.LeaveIntoTry); + invalidLeaveIntoTry = true; + } + } + else if (!IsDisjointTryBlock(ref targetRegion, ref srcRegion)) + { + VerificationError(VerifierError.LeaveIntoTry); + invalidLeaveIntoTry = true; + } + } + } + } + + // If the source is within a catch or filtered handler, target shall be within same catch or filtered handler + // or within the associated try block + // or within a try block enclosing the catch / filtered handler + // or the first instruction of a disjoint try block + // or not within any try block + if (src.HandlerIndex.HasValue && src.HandlerIndex != target.HandlerIndex) + { + if (target.TryIndex.HasValue) + { + ref var srcRegion = ref _exceptionRegions[src.HandlerIndex.Value].ILRegion; + ref var targetRegion = ref _exceptionRegions[target.TryIndex.Value].ILRegion; + + // If target is not associated try block, and not enclosing srcRegion + if (target.TryIndex != src.HandlerIndex && + (targetRegion.TryOffset > srcRegion.HandlerOffset || + targetRegion.TryOffset + targetRegion.TryLength < srcRegion.HandlerOffset)) + { + // If target is not first instruction of try, or not a direct sibling + if (target.StartOffset != targetRegion.TryOffset || !IsDisjointTryBlock(ref targetRegion, ref srcRegion)) + VerificationError(VerifierError.LeaveIntoTry); + } + } + } + + // If the target is within a filter or handler, source shall be within same + if (target.HandlerIndex.HasValue && src.HandlerIndex != target.HandlerIndex) + { + ref var targetRegion = ref _exceptionRegions[target.HandlerIndex.Value].ILRegion; + // If target region is not enclosing source + if (targetRegion.HandlerOffset > src.StartOffset || targetRegion.HandlerOffset + targetRegion.HandlerLength < src.StartOffset) + VerificationError(VerifierError.LeaveIntoHandler); + } + if (target.FilterIndex.HasValue && src.FilterIndex != target.FilterIndex) + { + ref var targetRegion = ref _exceptionRegions[target.FilterIndex.Value].ILRegion; + var filterLength = targetRegion.HandlerOffset - targetRegion.FilterOffset; + + // If target region is not enclosing source + if (targetRegion.FilterOffset > src.StartOffset || targetRegion.FilterOffset + filterLength < src.StartOffset) + VerificationError(VerifierError.LeaveIntoFilter); + } + + // If the target is within a try block (except first instruction), source shall be within same + // or within associated handler + if (!invalidLeaveIntoTry && target.TryIndex.HasValue && src.TryIndex != target.TryIndex) + { + ref var targetRegion = ref _exceptionRegions[target.TryIndex.Value].ILRegion; + + if (target.StartOffset != targetRegion.TryOffset && // Not first instruction + (!src.HandlerIndex.HasValue || src.HandlerIndex != target.TryIndex) && // Not associated handler + (targetRegion.TryOffset > src.StartOffset || targetRegion.TryOffset + targetRegion.TryLength < src.StartOffset)) // Target region does not enclose source + VerificationError(VerifierError.LeaveIntoTry); + } + } + + bool IsValidBranchTarget(BasicBlock src, BasicBlock target, bool isFallthrough, bool reportErrors = true) + { + if (!_validTargetOffsets[target.StartOffset]) + { + if (reportErrors) + VerificationError(VerifierError.BadJumpTarget); + return false; + } + + bool isValid = true; + + if (src.TryIndex != target.TryIndex) + { + if (src.TryIndex == null) + { + // Branching to first instruction of try-block is valid + if (target.StartOffset != _exceptionRegions[target.TryIndex.Value].ILRegion.TryOffset || !IsDirectChildRegion(src, target)) + { + if (reportErrors) + { + Debug.Assert(!isFallthrough); // This should not be reachable by fallthrough + VerificationError(VerifierError.BranchIntoTry); + } + isValid = false; + } + } + else if (target.TryIndex == null) + { + if (reportErrors) + { + if (isFallthrough) + VerificationError(VerifierError.FallthroughException); + else + VerificationError(VerifierError.BranchOutOfTry); + } + isValid = false; + } + else + { + ref var srcRegion = ref _exceptionRegions[src.TryIndex.Value].ILRegion; + ref var targetRegion = ref _exceptionRegions[target.TryIndex.Value].ILRegion; + // If target is inside source region + if (srcRegion.TryOffset <= targetRegion.TryOffset && + target.StartOffset < srcRegion.TryOffset + srcRegion.TryLength) + { + // Only branching to first instruction of try-block is valid + if (target.StartOffset != targetRegion.TryOffset || !IsDirectChildRegion(src, target)) + { + if (reportErrors) + { + Debug.Assert(!isFallthrough); // This should not be reachable by fallthrough + VerificationError(VerifierError.BranchIntoTry); + } + isValid = false; + } + } + else + { + if (reportErrors) + { + if (isFallthrough) + VerificationError(VerifierError.FallthroughException); + else + VerificationError(VerifierError.BranchOutOfTry); + } + isValid = false; + } + } + } + + if (src.FilterIndex != target.FilterIndex) + { + if (src.FilterIndex == null) + { + if (reportErrors) + { + if (isFallthrough) + VerificationError(VerifierError.FallthroughIntoFilter); + else + VerificationError(VerifierError.BranchIntoFilter); + } + isValid = false; + } + else if (target.HandlerIndex == null) + { + if (reportErrors) + { + if (isFallthrough) + VerificationError(VerifierError.FallthroughException); + else + VerificationError(VerifierError.BranchOutOfFilter); + } + isValid = false; + } + else + { + ref var srcRegion = ref _exceptionRegions[src.FilterIndex.Value].ILRegion; + ref var targetRegion = ref _exceptionRegions[target.FilterIndex.Value].ILRegion; + if (srcRegion.FilterOffset <= targetRegion.FilterOffset) + { + if (reportErrors) + { + if (isFallthrough) + VerificationError(VerifierError.FallthroughIntoFilter); + else + VerificationError(VerifierError.BranchIntoFilter); + } + isValid = false; + } + else + { + if (reportErrors) + { + if (isFallthrough) + VerificationError(VerifierError.FallthroughException); + else + VerificationError(VerifierError.BranchOutOfFilter); + } + isValid = false; + } + } + } + + if (src.HandlerIndex != target.HandlerIndex) + { + if (src.HandlerIndex == null) + { + if (reportErrors) + { + if (isFallthrough) + VerificationError(VerifierError.FallthroughIntoHandler); + else + VerificationError(VerifierError.BranchIntoHandler); + } + isValid = false; + } + else if (target.HandlerIndex == null) + { + if (reportErrors) + { + if (isFallthrough) + VerificationError(VerifierError.FallthroughException); + else + { + if (_exceptionRegions[src.HandlerIndex.Value].ILRegion.Kind == ILExceptionRegionKind.Finally) + VerificationError(VerifierError.BranchOutOfFinally); + else + VerificationError(VerifierError.BranchOutOfHandler); + } + } + isValid = false; + } + else + { + ref var srcRegion = ref _exceptionRegions[src.HandlerIndex.Value].ILRegion; + ref var targetRegion = ref _exceptionRegions[target.HandlerIndex.Value].ILRegion; + if (srcRegion.HandlerOffset <= targetRegion.HandlerOffset) + { + if (reportErrors) + { + if (isFallthrough) + VerificationError(VerifierError.FallthroughIntoHandler); + else + VerificationError(VerifierError.BranchIntoHandler); + } + isValid = false; + } + else + { + if (reportErrors) + { + if (isFallthrough) + VerificationError(VerifierError.FallthroughException); + else + { + if (srcRegion.Kind == ILExceptionRegionKind.Finally) + VerificationError(VerifierError.BranchOutOfFinally); + else + VerificationError(VerifierError.BranchOutOfHandler); + } + } + isValid = false; + } + } + } + + return isValid; + } + + /// + /// Checks whether the given enclosed try block is a direct child try-region of + /// the given enclosing try block. + /// + /// The block enclosing the try block given by . + /// The block to check whether it is a direct child try-region of . + /// True if is a direct child try region of . + bool IsDirectChildRegion(BasicBlock enclosingBlock, BasicBlock enclosedBlock) + { + ref var enclosedRegion = ref _exceptionRegions[enclosedBlock.TryIndex.Value].ILRegion; + + // Walk from enclosed try start backwards and check each BasicBlock whether it is a try-start + for (int i = enclosedRegion.TryOffset - 1; i > enclosingBlock.StartOffset; --i) + { + var block = _basicBlocks[i]; + if (block == null) + continue; + + if (block.TryStart && block.TryIndex != enclosingBlock.TryIndex) + { + ref var blockRegion = ref _exceptionRegions[block.TryIndex.Value].ILRegion; + // blockRegion is actually enclosing enclosedRegion + if (blockRegion.TryOffset + blockRegion.TryLength > enclosedRegion.TryOffset) + return false; + } + } + + return true; + } + + /// + /// Checks whether the try block is a disjoint try block relative to . + /// + /// True if is a disjoint try block relative to . + bool IsDisjointTryBlock(ref ILExceptionRegion disjoint, ref ILExceptionRegion source) + { + if (source.TryOffset <= disjoint.TryOffset && source.TryOffset + source.TryLength >= disjoint.TryOffset + disjoint.TryLength) + { + // source is enclosing disjoint + return false; + } + + // Walk from disjoint region backwards and check for enclosing exception regions. + for (int i = disjoint.TryOffset - 1; i >= 0; --i) + { + var block = _basicBlocks[i]; + if (block == null) + continue; + + if (block.TryStart) + { + ref var blockRegion = ref _exceptionRegions[block.TryIndex.Value].ILRegion; + // blockRegion is enclosing disjoint, but not source + if (blockRegion.TryOffset + blockRegion.TryLength > disjoint.TryOffset && + (blockRegion.TryOffset > source.TryOffset || blockRegion.TryOffset + blockRegion.TryLength <= source.TryOffset)) + return false; + } + + if (block.HandlerStart) + { + ref var blockRegion = ref _exceptionRegions[block.HandlerIndex.Value].ILRegion; + // blockRegion is enclosing secondRegion, but not source + if (blockRegion.HandlerOffset + blockRegion.HandlerLength > disjoint.TryOffset && + (blockRegion.HandlerOffset > source.TryOffset || blockRegion.HandlerOffset + blockRegion.HandlerLength <= source.TryOffset)) + return false; + } + + if (block.FilterStart) + { + ref var blockRegion = ref _exceptionRegions[block.FilterIndex.Value].ILRegion; + // blockRegion is enclosing secondRegion, but not source + var filterLength = blockRegion.HandlerOffset - blockRegion.FilterOffset; + if (blockRegion.FilterOffset + filterLength > disjoint.TryOffset && + (blockRegion.FilterOffset > source.TryOffset || blockRegion.FilterOffset + filterLength <= source.TryOffset)) + return false; + } + } + + return true; + } + // For now, match PEVerify type formating to make it easy to compare with baseline static string TypeToStringForIsAssignable(TypeDesc type) { @@ -366,16 +1034,6 @@ namespace Internal.IL } } - void CheckIsAssignablePointer(TypeDesc src, TypeDesc dst) - { - if (!IsAssignable(src, dst)) - { - VerificationError(VerifierError.StackUnexpected, "address of " + TypeToStringForIsAssignable(src), "address of " + TypeToStringForIsAssignable(dst)); - VerificationError(VerifierError.StackUnexpected, "address of " + TypeToStringForIsAssignable(src)); - AbortBasicBlockVerification(); - } - } - void CheckIsArrayElementCompatibleWith(TypeDesc src, TypeDesc dst) { if (!IsAssignable(src, dst, true)) @@ -404,6 +1062,18 @@ namespace Internal.IL } } + void CheckIsObjRef(StackValue value) + { + if (value.Kind != StackValueKind.ObjRef) + VerificationError(VerifierError.StackObjRef, value); + } + + private void CheckIsNotPointer(TypeDesc type) + { + if (type.IsPointer) + VerificationError(VerifierError.UnmanagedPointer); + } + void CheckIsComparable(StackValue a, StackValue b, ILOpcode op) { if (!IsBinaryComparable(a, b, op)) @@ -412,6 +1082,89 @@ namespace Internal.IL } } + void CheckDelegateCreation(StackValue ftn, StackValue obj) + { + if (!_delegateCreateStart.HasValue) + { + VerificationError(VerifierError.DelegatePattern); + return; + } + + int delegateStart = _delegateCreateStart.Value; + + if (_currentInstructionOffset - delegateStart == 6) // ldftn takes 6 bytes + { + if (GetOpcodeAt(delegateStart) != ILOpcode.ldftn) + { + VerificationError(VerifierError.DelegatePattern); + return; + } + else + { + // See "Rules for non-virtual call to a non-final virtual method" in ImportCall + if (ftn.Method.IsVirtual && !ftn.Method.IsFinal && !obj.IsBoxedValueType) + { + var methodTypeDef = ftn.Method.OwningType.GetTypeDefinition() as MetadataType; // Method is always considered final if owning type is sealed + if (methodTypeDef == null || !methodTypeDef.IsSealed) + Check(obj.IsThisPtr && !_modifiesThisPtr, VerifierError.LdftnNonFinalVirtual); + } + } + } + else if (_currentInstructionOffset - _delegateCreateStart == 7) // dup, ldvirtftn takes 7 bytes + { + if (GetOpcodeAt(delegateStart) != ILOpcode.dup || + GetOpcodeAt(delegateStart + 1) != ILOpcode.ldvirtftn) + { + VerificationError(VerifierError.DelegatePattern); + return; + } + } + else + VerificationError(VerifierError.DelegatePattern); + } + + void CheckIsDelegateAssignable(MethodDesc ftn, TypeDesc delegateType) + { + if (!IsDelegateAssignable(ftn, delegateType)) + VerificationError(VerifierError.DelegateCtor); + } + + bool IsDelegateAssignable(MethodDesc ftn, TypeDesc delegateType) + { + var invokeMethod = delegateType.GetMethod("Invoke", null); + if (invokeMethod == null) + return false; + + var ftnSignature = ftn.Signature; + var delegateSignature = invokeMethod.Signature; + + // Compare calling convention ignoring distinction between static and instance + if ((ftnSignature.Flags & ~MethodSignatureFlags.Static) != (delegateSignature.Flags & ~MethodSignatureFlags.Static)) + return false; + + // Compare signature parameters + if (ftnSignature.Length != delegateSignature.Length) + return false; + + for (int i = 0; i < ftnSignature.Length; i++) + { + if (!IsAssignable(delegateSignature[i], ftnSignature[i])) + return false; + } + + // Compare return type + return IsAssignable(ftnSignature.ReturnType, delegateSignature.ReturnType); + } + + ILOpcode GetOpcodeAt(int instructionOffset) + { + var opCode = (ILOpcode)_ilBytes[instructionOffset]; + if (opCode == ILOpcode.prefix1) + opCode = (ILOpcode)(0x100 + _ilBytes[instructionOffset + 1]); + + return opCode; + } + void Unverifiable() { VerificationError(VerifierError.Unverifiable); @@ -485,10 +1238,15 @@ namespace Internal.IL void EndImportingInstruction() { + CheckPendingPrefix(_pendingPrefix); + ClearPendingPrefix(_pendingPrefix); // Make sure prefix is cleared } void StartImportingBasicBlock(BasicBlock basicBlock) { + _delegateCreateStart = null; + _isThisInitialized = basicBlock.IsThisInitialized; + if (basicBlock.TryStart) { Check(basicBlock.EntryStack == null || basicBlock.EntryStack.Length == 0, VerifierError.TryNonEmptyStack); @@ -501,16 +1259,20 @@ namespace Internal.IL continue; if (r.ILRegion.Kind == ILExceptionRegionKind.Filter) - MarkBasicBlock(_basicBlocks[r.ILRegion.FilterOffset]); - - MarkBasicBlock(_basicBlocks[r.ILRegion.HandlerOffset]); + { + var filterBlock = _basicBlocks[r.ILRegion.FilterOffset]; + PropagateThisState(basicBlock, filterBlock); + MarkBasicBlock(filterBlock); + } + + var handlerBlock = _basicBlocks[r.ILRegion.HandlerOffset]; + PropagateThisState(basicBlock, handlerBlock); + MarkBasicBlock(handlerBlock); } } if (basicBlock.FilterStart || basicBlock.HandlerStart) { - Debug.Assert(basicBlock.EntryStack == null); - ExceptionRegion r; if (basicBlock.HandlerIndex.HasValue) { @@ -522,26 +1284,52 @@ namespace Internal.IL } else { + Debug.Fail("Block marked as filter / handler start but no filter / handler index set."); return; } - if (r.ILRegion.Kind == ILExceptionRegionKind.Filter) + if (r.ILRegion.Kind == ILExceptionRegionKind.Filter || r.ILRegion.Kind == ILExceptionRegionKind.Catch) { - basicBlock.EntryStack = new StackValue[] { StackValue.CreateObjRef(GetWellKnownType(WellKnownType.Object)) }; - } - else - if (r.ILRegion.Kind == ILExceptionRegionKind.Catch) - { - basicBlock.EntryStack = new StackValue[] { StackValue.CreateObjRef(ResolveTypeToken(r.ILRegion.ClassToken)) }; + // stack must uninit or 1 (exception object) + Check(basicBlock.EntryStack == null || basicBlock.EntryStack.Length == 1, VerifierError.FilterOrCatchUnexpectedStack); + + if (basicBlock.EntryStack == null) + basicBlock.EntryStack = new StackValue[1]; + + if (r.ILRegion.Kind == ILExceptionRegionKind.Filter) + { + basicBlock.EntryStack[0] = StackValue.CreateObjRef(GetWellKnownType(WellKnownType.Object)); + } + else + if (r.ILRegion.Kind == ILExceptionRegionKind.Catch) + { + var exceptionType = ResolveTypeToken(r.ILRegion.ClassToken); + Check(!exceptionType.IsByRef, VerifierError.CatchByRef); + basicBlock.EntryStack[0] = StackValue.CreateObjRef(exceptionType); + } } else { - basicBlock.EntryStack = s_emptyStack; + // stack must be uninit or empty + Check(basicBlock.EntryStack == null || basicBlock.EntryStack.Length == 0, VerifierError.FinOrFaultNonEmptyStack); + if (basicBlock.EntryStack == null) + basicBlock.EntryStack = s_emptyStack; } } if (basicBlock.EntryStack?.Length > 0) { + if (!basicBlock.TryStart && !basicBlock.HandlerStart && !basicBlock.FilterStart) + { + // ECMA III 1.7.5 Backward Branch Constraints + // if stack is not empty at beginning of this block, + // there must exist a predecessor block with lower IL offset. + Check(basicBlock.HasPredecessorWithLowerOffset, VerifierError.BackwardBranch); + } + + // Copy stack state + if (_stack == null || _stack.Length < basicBlock.EntryStack.Length) + Array.Resize(ref _stack, basicBlock.EntryStack.Length); Array.Copy(basicBlock.EntryStack, _stack, basicBlock.EntryStack.Length); _stackTop = basicBlock.EntryStack.Length; } @@ -553,6 +1341,7 @@ namespace Internal.IL void EndImportingBasicBlock(BasicBlock basicBlock) { + basicBlock.State = BasicBlock.ImportState.WasVerified; } void ImportNop() @@ -592,7 +1381,16 @@ namespace Internal.IL if (!argument) Check(_initLocals, VerifierError.InitLocals); - Push(StackValue.CreateFromType(varType)); + CheckIsNotPointer(varType); + + var stackValue = StackValue.CreateFromType(varType); + if (index == 0 && argument && _thisType != null) + { + Debug.Assert(varType == _thisType); + stackValue.SetIsThisPtr(); + } + + Push(stackValue); } void ImportStoreVar(int index, bool argument) @@ -601,6 +1399,9 @@ namespace Internal.IL var value = Pop(); + if (_trackObjCtorState && !_isThisInitialized) + Check(index != 0 || !argument, VerifierError.ThisUninitStore); + CheckIsAssignable(value, StackValue.CreateFromType(varType)); } @@ -611,12 +1412,26 @@ namespace Internal.IL if (!argument) Check(_initLocals, VerifierError.InitLocals); - Push(StackValue.CreateByRef(varType)); + Check(!varType.IsByRef, VerifierError.ByrefOfByref); + + var stackValue = StackValue.CreateByRef(varType); + if (index == 0 && argument && _thisType != null) + { + Debug.Assert(varType == _thisType); + stackValue.SetIsThisPtr(); + + Check(!_trackObjCtorState || _isThisInitialized, VerifierError.ThisUninitStore); + } + + Push(stackValue); } void ImportDup() { - var value = Pop(); + var value = Pop(allowUninitThis: true); + + // this could be the beginning of a delegate create + _delegateCreateStart = _currentInstructionOffset; Push(value); Push(value); @@ -624,7 +1439,7 @@ namespace Internal.IL void ImportPop() { - Pop(); + Pop(allowUninitThis: true); } void ImportJmp(int token) @@ -640,11 +1455,9 @@ namespace Internal.IL var value = Pop(); - // TODO - // VerifyIsObjRef(tiVal); + CheckIsObjRef(value); - // TODO - // verCheckClassAccess(pResolvedToken); + Check(_method.OwningType.CanAccess(type), VerifierError.TypeAccess); Push(StackValue.CreateObjRef(type)); } @@ -658,15 +1471,15 @@ namespace Internal.IL if (opcode != ILOpcode.newobj) { - if ((_pendingPrefix & Prefix.Constrained) != 0 && opcode == ILOpcode.callvirt) + if (HasPendingPrefix(Prefix.Constrained) && opcode == ILOpcode.callvirt) { - _pendingPrefix &= ~Prefix.Constrained; + ClearPendingPrefix(Prefix.Constrained); constrained = _constrained; } - if ((_pendingPrefix & Prefix.Tail) != 0) + if (HasPendingPrefix(Prefix.Tail)) { - _pendingPrefix &= ~Prefix.Tail; + ClearPendingPrefix(Prefix.Tail); tailCall = true; } } @@ -686,62 +1499,93 @@ namespace Internal.IL Check(methodType != null, VerifierError.CallVirtOnStatic); Check(!methodType.IsValueType, VerifierError.CallVirtOnValueType); } - else + else if (opcode != ILOpcode.newobj) { EcmaMethod ecmaMethod = method.GetTypicalMethodDefinition() as EcmaMethod; if (ecmaMethod != null) Check(!ecmaMethod.IsAbstract, VerifierError.CallAbstract); } - for (int i = sig.Length - 1; i >= 0; i--) + if (opcode == ILOpcode.newobj && methodType.IsDelegate) { - var actual = Pop(); - var declared = StackValue.CreateFromType(sig[i]); + Check(sig.Length == 2, VerifierError.DelegateCtor); + var declaredObj = StackValue.CreateFromType(sig[0]); + var declaredFtn = StackValue.CreateFromType(sig[1]); - CheckIsAssignable(actual, declared); + Check(declaredFtn.Kind == StackValueKind.NativeInt, VerifierError.DelegateCtorSigI, declaredFtn); - // check that the argument is not a byref for tailcalls - if (tailCall) - Check(!IsByRefLike(declared), VerifierError.TailByRef, declared); + var actualFtn = Pop(); + var actualObj = Pop(); + + Check(actualFtn.IsMethod, VerifierError.StackMethod); + + CheckIsAssignable(actualObj, declaredObj); + Check(actualObj.Kind == StackValueKind.ObjRef, VerifierError.DelegateCtorSigO, actualObj); + + CheckDelegateCreation(actualFtn, actualObj); + + CheckIsDelegateAssignable(actualFtn.Method, methodType); } + else + { + for (int i = sig.Length - 1; i >= 0; i--) + { + var actual = Pop(allowUninitThis: true); + var declared = StackValue.CreateFromType(sig[i]); + + CheckIsAssignable(actual, declared); + + // check that the argument is not a byref for tailcalls + if (tailCall) + Check(!IsByRefLike(declared), VerifierError.TailByRef, declared); + } + } + + TypeDesc instance = null; if (opcode == ILOpcode.newobj) { - // TODO: + Check(method.IsConstructor, VerifierError.CtorExpected); + if (sig.IsStatic || methodType == null || method.IsAbstract) + { + VerificationError(VerifierError.CtorSig); + } + else + { + if (methodType.IsArray) + { + var arrayType = (ArrayType)methodType; + Check(!IsByRefLike(StackValue.CreateFromType(arrayType.ElementType)), VerifierError.ArrayByRef); + } + else + { + var metadataType = (MetadataType)methodType; + Check(!metadataType.IsAbstract, VerifierError.NewobjAbstractClass); + } + } } else if (methodType != null) { - var actualThis = Pop(); + var actualThis = Pop(allowUninitThis: true); + instance = actualThis.Type; var declaredThis = methodType.IsValueType ? StackValue.CreateByRef(methodType) : StackValue.CreateObjRef(methodType); -#if false - // If this is a call to the base class .ctor, set thisPtr Init for - // this block. - if (mflags & CORINFO_FLG_CONSTRUCTOR) + // If this is a call to the base class .ctor, set thisPtr Init for this block. + if (method.IsConstructor) { - if (m_verTrackObjCtorInitState && tiThis.IsThisPtr() - && verIsCallToInitThisPtr(getCurrentMethodClass(), methodClassHnd)) + if (_trackObjCtorState && actualThis.IsThisPtr && + (methodType == _thisType || methodType == _thisType.BaseType)) // Call to overloaded ctor or base ctor { - // do not allow double init - Verify(vstate->thisInitialized == THISUNINIT - || vstate->thisInitialized == THISEHREACHED, MVER_E_PATH_THIS); - - vstate->containsCtorCall = 1; - vstate->setThisInitialized(); - vstate->thisInitializedThisBlock = true; - tiThis.SetInitialisedObjRef(); + _isThisInitialized = true; } else { - // We allow direct calls to value type constructors - // NB: we have to check that the contents of tiThis is a value type, otherwise we could use a constrained - // callvirt to illegally re-enter a .ctor on a value of reference type. - VerifyAndReportFound(tiThis.IsByRef() && DereferenceByRef(tiThis).IsValueClass(), tiThis, MVER_E_CALL_CTOR); + // Allow direct calls to value type constructors + Check(actualThis.Kind == StackValueKind.ByRef && actualThis.Type.IsValueType, VerifierError.CallCtor); } } -#endif if (constrained != null) { @@ -755,50 +1599,44 @@ namespace Internal.IL actualThis = StackValue.CreateObjRef(constrained); } -#if false - // To support direct calls on readonly byrefs, just pretend tiDeclaredThis is readonly too - if(tiDeclaredThis.IsByRef() && tiThis.IsReadonlyByRef()) + // To support direct calls on readonly byrefs, just pretend declaredThis is readonly too + if(declaredThis.Kind == StackValueKind.ByRef && (actualThis.Kind == StackValueKind.ByRef && actualThis.IsReadOnly)) { - tiDeclaredThis.SetIsReadonlyByRef(); + declaredThis.SetIsReadOnly(); } -#endif - CheckIsAssignable(actualThis, declaredThis); -#if false - // Rules for non-virtual call to a non-final virtual method: - - // Define: - // The "this" pointer is considered to be "possibly written" if - // 1. Its address have been taken (LDARGA 0) anywhere in the method. - // (or) - // 2. It has been stored to (STARG.0) anywhere in the method. - - // A non-virtual call to a non-final virtual method is only allowed if - // 1. The this pointer passed to the callee is an instance of a boxed value type. - // (or) - // 2. The this pointer passed to the callee is the current method's this pointer. - // (and) The current method's this pointer is not "possibly written". - - // Thus the rule is that if you assign to this ANYWHERE you can't make "base" calls to - // virtual methods. (Luckily this does affect .ctors, since they are not virtual). - // This is stronger that is strictly needed, but implementing a laxer rule is significantly - // hard and more error prone. - - if (opcode == ReaderBaseNS::CEE_CALL - && (mflags & CORINFO_FLG_VIRTUAL) - && ((mflags & CORINFO_FLG_FINAL) == 0) - && (!verIsBoxedValueType(tiThis))) + if (opcode == ILOpcode.call) { - // always enforce for peverify - Verify(tiThis.IsThisPtr() && !thisPossiblyModified, - MVER_E_THIS_MISMATCH); + // Rules for non-virtual call to a non-final virtual method (ECMA III.3.19: Verifiability of 'call'): + + // Define: + // The "this" pointer is considered to be "possibly written" if + // 1. Its address have been taken (LDARGA 0) anywhere in the method. + // (or) + // 2. It has been stored to (STARG.0) anywhere in the method. + + // A non-virtual call to a non-final virtual method is only allowed if + // 1. The this pointer passed to the callee is an instance of a boxed value type. + // (or) + // 2. The this pointer passed to the callee is the current method's this pointer. + // (and) The current method's this pointer is not "possibly written". + + // Thus the rule is that if you assign to this ANYWHERE you can't make "base" calls to + // virtual methods. (Luckily this does not affect .ctors, since they are not virtual). + // This is stronger than is strictly needed, but implementing a laxer rule is significantly + // harder and more error prone. + if (method.IsVirtual && !method.IsFinal && !actualThis.IsBoxedValueType) + { + var methodTypeDef = methodType.GetTypeDefinition() as MetadataType; // Method is always considered final if owning type is sealed + if (methodTypeDef == null || !methodTypeDef.IsSealed) + Check(actualThis.IsThisPtr && !_modifiesThisPtr, VerifierError.ThisMismatch); + } } -#endif if (tailCall) { - // also check the specil tailcall rule + // also check the special tailcall rule Check(!IsByRefLike(declaredThis), VerifierError.TailByRef, declaredThis); // Tail calls on constrained calls should be illegal too: @@ -807,22 +1645,13 @@ namespace Internal.IL } } -#if false - // check any constraints on the callee's class and type parameters - Verify(m_jitInfo->satisfiesClassConstraints(methodClassHnd), - MVER_E_UNSATISFIED_METHOD_PARENT_INST); //"method has unsatisfied class constraints" - Verify(m_jitInfo->satisfiesMethodConstraints(methodClassHnd,methodHnd), - MVER_E_UNSATISFIED_METHOD_INST); //"method has unsatisfied method constraints" - + // Check any constraints on the callee's class and type parameters + if (!method.OwningType.CheckConstraints()) + VerificationError(VerifierError.UnsatisfiedMethodParentInst, method.OwningType); + else if (!method.CheckConstraints()) + VerificationError(VerifierError.UnsatisfiedMethodInst, method); - handleMemberAccessForVerification(callInfo.accessAllowed, callInfo.callsiteCalloutHelper, - MVER_E_METHOD_ACCESS); - - if (mflags & CORINFO_FLG_PROTECTED) - { - Verify(m_jitInfo->canAccessFamily(getCurrentMethodHandle(), instanceClassHnd), MVER_E_METHOD_ACCESS); - } -#endif + Check(_method.OwningType.CanAccess(method, instance), VerifierError.MethodAccess); TypeDesc returnType = sig.ReturnType; @@ -842,11 +1671,16 @@ namespace Internal.IL // } else { - CheckIsAssignable(StackValue.CreateFromType(returnType), StackValue.CreateFromType(callerReturnType)); + var retStackType = StackValue.CreateFromType(returnType); + var callerRetStackType = StackValue.CreateFromType(callerReturnType); + Check(IsAssignable(retStackType, callerRetStackType), VerifierError.TailRetType, retStackType, callerRetStackType); } // for tailcall, stack must be empty Check(_stackTop == 0, VerifierError.TailStackEmpty); + + // The instruction following a tail.call shall be a ret + Check(_currentOffset < _ilBytes.Length && (ILOpcode)_ilBytes[_currentOffset] == ILOpcode.ret, VerifierError.TailRet); } // now push on the result @@ -859,23 +1693,21 @@ namespace Internal.IL { var returnValue = StackValue.CreateFromType(returnType); -#if false // "readonly." prefixed calls only allowed for the Address operation on arrays. // The methods supported by array types are under the control of the EE // so we can trust that only the Address operation returns a byref. - if (readonlyCall) + if (HasPendingPrefix(Prefix.ReadOnly)) { - VerifyOrReturn ((methodClassFlgs & CORINFO_FLG_ARRAY) && tiRetVal.IsByRef(), - MVER_E_READONLY_UNEXPECTED_CALLEE);//"unexpected use of readonly prefix" - vstate->readonlyPrefix = false; - tiRetVal.SetIsReadonlyByRef(); + if (method.OwningType.IsArray && sig.ReturnType.IsByRef) + returnValue.SetIsReadOnly(); + else + VerificationError(VerifierError.ReadonlyUnexpectedCallee); + + ClearPendingPrefix(Prefix.ReadOnly); } - if (tiRetVal.IsByRef()) - { - tiRetVal.SetIsPermanentHomeByRef(); - } -#endif + if (returnValue.Kind == StackValueKind.ByRef) + returnValue.SetIsPermanentHome(); Push(returnValue); } @@ -888,16 +1720,56 @@ namespace Internal.IL void ImportLdFtn(int token, ILOpcode opCode) { - MethodDesc type = ResolveMethodToken(token); + MethodDesc method = ResolveMethodToken(token); + Check(!method.IsConstructor, VerifierError.LdftnCtor); - StackValue thisPtr = new StackValue(); +#if false + if (sig.hasTypeArg()) + NO_WAY("Currently do not support LDFTN of Parameterized functions"); +#endif - if (opCode == ILOpcode.ldvirtftn) - thisPtr = Pop(); + TypeDesc instance; - // TODO + if (opCode == ILOpcode.ldftn) + { + _delegateCreateStart = _currentInstructionOffset; - throw new NotImplementedException($"{nameof(ImportLdFtn)} not implemented"); + instance = null; + } + else if (opCode == ILOpcode.ldvirtftn) + { + Check(!method.Signature.IsStatic, VerifierError.LdvirtftnOnStatic); + + StackValue declaredType; + if (method.OwningType.IsValueType) + { + // Box value type for comparison + declaredType = StackValue.CreateObjRef(method.OwningType); + } + else + declaredType = StackValue.CreateFromType(method.OwningType); + + var thisPtr = Pop(); + instance = thisPtr.Type; + + CheckIsObjRef(thisPtr); + CheckIsAssignable(thisPtr, declaredType); + } + else + { + Debug.Fail("Unexpected ldftn opcode: " + opCode.ToString()); + return; + } + + // Check any constraints on the callee's class and type parameters + if (!method.OwningType.CheckConstraints()) + VerificationError(VerifierError.UnsatisfiedMethodParentInst, method.OwningType); + else if (!method.CheckConstraints()) + VerificationError(VerifierError.UnsatisfiedMethodInst, method); + + Check(_method.OwningType.CanAccess(method, instance), VerifierError.MethodAccess); + + Push(StackValue.CreateMethod(method)); } void ImportLoadInt(long value, StackValueKind kind) @@ -917,115 +1789,76 @@ namespace Internal.IL void ImportReturn() { + // 'this' must be init before return, unless System.Object + if (_trackObjCtorState) + Check(_isThisInitialized || _thisType.IsObject, VerifierError.ThisUninitReturn); + // Check current region type + Check(_currentBasicBlock.FilterIndex == null, VerifierError.ReturnFromFilter); + Check(_currentBasicBlock.TryIndex == null, VerifierError.ReturnFromTry); + Check(_currentBasicBlock.HandlerIndex == null, VerifierError.ReturnFromHandler); -#if false - // 'this' must be init before return - if (m_verTrackObjCtorInitState) - Verify(vstate->isThisPublishable(), MVER_E_THIS_UNINIT_RET); + var declaredReturnType = _method.Signature.ReturnType; - // review : already in verifyreturnflow - if (region) - { - switch (RgnGetRegionType(region)) - { - case ReaderBaseNS::RGN_FILTER: - BADCODE(MVER_E_RET_FROM_FIL); - break; - case ReaderBaseNS::RGN_TRY: - BADCODE(MVER_E_RET_FROM_TRY); - break; - case ReaderBaseNS::RGN_FAULT: - case ReaderBaseNS::RGN_FINALLY: - case ReaderBaseNS::RGN_MEXCEPT: - case ReaderBaseNS::RGN_MCATCH: - BADCODE(MVER_E_RET_FROM_HND); - break; - case ReaderBaseNS::RGN_ROOT: - break; - default: - VASSERT(UNREACHED); - break; - } - } + if (declaredReturnType.IsVoid) + { + Debug.Assert(_stackTop >= 0); - m_jitInfo->getMethodSig(getCurrentMethodHandle(), &sig); + if (_stackTop > 0) + VerificationError(VerifierError.ReturnVoid, _stack[_stackTop - 1]); + } + else + { + if (_stackTop <= 0) + VerificationError(VerifierError.ReturnMissing); + else + { + Check(_stackTop == 1, VerifierError.ReturnEmpty); - // Now get the return type and convert it to our format. - corType = sig.retType; + var actualReturnType = Pop(); + CheckIsAssignable(actualReturnType, StackValue.CreateFromType(declaredReturnType)); - expectedStack = 0; - if (corType != CORINFO_TYPE_VOID) - { - Verify(vstate->stackLevel() > 0, MVER_E_RET_MISSING); - Verify(vstate->stackLevel() == 1, MVER_E_RET_EMPTY); - - vertype tiVal = vstate->impStackTop(0); - vertype tiDeclared = verMakeTypeInfo(corType, sig.retTypeClass); - - VerifyCompatibleWith(tiVal, tiDeclared.NormaliseForStack()); - Verify((!verIsByRefLike(tiDeclared)) || verIsSafeToReturnByRef(tiVal), MVER_E_RET_PTR_TO_STACK); - expectedStack=1; - } - else - { - Verify(vstate->stackLevel() == 0, MVER_E_RET_VOID); - } - - if (expectedStack == 1) - vstate->pop(); - else - VASSERT(!expectedStack); -#endif - - // TODO + Check((!declaredReturnType.IsByRef && !declaredReturnType.IsByRefLike) || actualReturnType.IsPermanentHome, VerifierError.ReturnPtrToStack); + } + } } void ImportFallthrough(BasicBlock next) { - StackValue[] entryStack = next.EntryStack; + PropagateControlFlow(next, isFallthrough: true); + } - if (entryStack != null) - { - // TODO: Better error messages - if (entryStack.Length != _stackTop) - throw new InvalidProgramException(); - - for (int i = 0; i < entryStack.Length; i++) - { - // TODO: Do we need to allow conversions? - if (entryStack[i].Kind != _stack[i].Kind) - throw new InvalidProgramException(); - - if (entryStack[i].Type != _stack[i].Type) - throw new InvalidProgramException(); - } - } + void PropagateThisState(BasicBlock current, BasicBlock next) + { + if (next.State == BasicBlock.ImportState.Unmarked) + next.IsThisInitialized = _isThisInitialized; else { - entryStack = (_stackTop != 0) ? new StackValue[_stackTop] : s_emptyStack; + if (next.IsThisInitialized && !_isThisInitialized) + { + // Next block has 'this' initialized, but current state has not + // therefore next block must be reverified with 'this' uninitialized + if (next.State == BasicBlock.ImportState.WasVerified && next.ErrorCount == 0) + next.State = BasicBlock.ImportState.Unmarked; + } - for (int i = 0; i < entryStack.Length; i++) - entryStack[i] = _stack[i]; - - next.EntryStack = entryStack; + next.IsThisInitialized = next.IsThisInitialized && _isThisInitialized; } - - MarkBasicBlock(next); } void ImportSwitchJump(int jmpBase, int[] jmpDelta, BasicBlock fallthrough) { + var value = Pop(); + CheckIsAssignable(value, StackValue.CreatePrimitive(StackValueKind.Int32)); + for (int i = 0; i < jmpDelta.Length; i++) { BasicBlock target = _basicBlocks[jmpBase + jmpDelta[i]]; - ImportFallthrough(target); + PropagateControlFlow(target, isFallthrough: false); } if (fallthrough != null) ImportFallthrough(fallthrough); - - throw new NotImplementedException($"{nameof(ImportSwitchJump)} not implemented"); } void ImportBranch(ILOpcode opcode, BasicBlock target, BasicBlock fallthrough) @@ -1059,16 +1892,68 @@ namespace Internal.IL } break; default: - Debug.Assert(false, "Unexpected branch opcode"); + Debug.Fail("Unexpected branch opcode"); break; } - ImportFallthrough(target); + PropagateControlFlow(target, isFallthrough: false); if (fallthrough != null) ImportFallthrough(fallthrough); } + void PropagateControlFlow(BasicBlock next, bool isFallthrough) + { + if (!IsValidBranchTarget(_currentBasicBlock, next, isFallthrough) || _currentBasicBlock.ErrorCount > 0) + return; + + PropagateThisState(_currentBasicBlock, next); + + // Propagate stack across block bounds + StackValue[] entryStack = next.EntryStack; + + if (entryStack != null) + { + FatalCheck(entryStack.Length == _stackTop, VerifierError.PathStackDepth); + + for (int i = 0; i < entryStack.Length; i++) + { + // TODO: Do we need to allow conversions? + FatalCheck(entryStack[i].Kind == _stack[i].Kind, VerifierError.PathStackUnexpected, entryStack[i], _stack[i]); + + if (entryStack[i].Type != _stack[i].Type) + { + if (!IsAssignable(_stack[i], entryStack[i])) + { + StackValue mergedValue; + if (!TryMergeStackValues(entryStack[i], _stack[i], out mergedValue)) + FatalCheck(false, VerifierError.PathStackUnexpected, entryStack[i], _stack[i]); + + // If merge actually changed entry stack + if (mergedValue != entryStack[i]) + { + entryStack[i] = mergedValue; + + if (next.ErrorCount == 0 && next.State != BasicBlock.ImportState.IsPending) + next.State = BasicBlock.ImportState.Unmarked; // Make sure block is reverified + } + } + } + } + } + else + { + entryStack = (_stackTop != 0) ? new StackValue[_stackTop] : s_emptyStack; + + for (int i = 0; i < entryStack.Length; i++) + entryStack[i] = _stack[i]; + + next.EntryStack = entryStack; + } + + MarkBasicBlock(next); + } + void ImportBinaryOperation(ILOpcode opcode) { var op1 = Pop(); @@ -1120,7 +2005,13 @@ namespace Internal.IL void ImportShiftOperation(ILOpcode opcode) { - throw new NotImplementedException($"{nameof(ImportShiftOperation)} not implemented"); + var shiftBy = Pop(); + var toBeShifted = Pop(); + + Check(shiftBy.Kind == StackValueKind.Int32 || shiftBy.Kind == StackValueKind.NativeInt, VerifierError.StackUnexpected, shiftBy); + CheckIsInteger(toBeShifted); + + Push(StackValue.CreatePrimitive(toBeShifted.Kind)); } void ImportCompareOperation(ILOpcode opcode) @@ -1144,11 +2035,17 @@ namespace Internal.IL void ImportLoadField(int token, bool isStatic) { + ClearPendingPrefix(Prefix.Unaligned); + ClearPendingPrefix(Prefix.Volatile); + var field = ResolveFieldToken(token); + TypeDesc instance; if (isStatic) { Check(field.IsStatic, VerifierError.ExpectedStaticField); + + instance = null; } else { @@ -1157,26 +2054,38 @@ namespace Internal.IL // Note that even if the field is static, we require that the this pointer // satisfy the same constraints as a non-static field This happens to // be simpler and seems reasonable - var actualThis = Pop(); + var actualThis = Pop(allowUninitThis: true); if (actualThis.Kind == StackValueKind.ValueType) actualThis = StackValue.CreateByRef(actualThis.Type); var declaredThis = owningType.IsValueType ? StackValue.CreateByRef(owningType) : StackValue.CreateObjRef(owningType); - CheckIsAssignable(actualThis, declaredThis); + CheckIsAssignable(actualThis, declaredThis); + + instance = actualThis.Type; } + Check(_method.OwningType.CanAccess(field, instance), VerifierError.FieldAccess); + Push(StackValue.CreateFromType(field.FieldType)); } void ImportAddressOfField(int token, bool isStatic) { var field = ResolveFieldToken(token); + bool isPermanentHome = false; + TypeDesc instance; if (isStatic) { Check(field.IsStatic, VerifierError.ExpectedStaticField); + + isPermanentHome = true; + instance = null; + + if (field.IsInitOnly) + Check(_method.IsStaticConstructor && field.OwningType == _method.OwningType, VerifierError.InitOnly); } else { @@ -1185,7 +2094,7 @@ namespace Internal.IL // Note that even if the field is static, we require that the this pointer // satisfy the same constraints as a non-static field This happens to // be simpler and seems reasonable - var actualThis = Pop(); + var actualThis = Pop(allowUninitThis: true); if (actualThis.Kind == StackValueKind.ValueType) actualThis = StackValue.CreateByRef(actualThis.Type); @@ -1193,20 +2102,37 @@ namespace Internal.IL StackValue.CreateByRef(owningType) : StackValue.CreateObjRef(owningType); CheckIsAssignable(actualThis, declaredThis); + + isPermanentHome = actualThis.Kind == StackValueKind.ObjRef || actualThis.IsPermanentHome; + instance = actualThis.Type; + + if (field.IsInitOnly) + Check(_method.IsConstructor && field.OwningType == _method.OwningType && actualThis.IsThisPtr, VerifierError.InitOnly); } - Push(StackValue.CreateByRef(field.FieldType)); + Check(_method.OwningType.CanAccess(field, instance), VerifierError.FieldAccess); + + Push(StackValue.CreateByRef(field.FieldType, false, isPermanentHome)); } void ImportStoreField(int token, bool isStatic) { + ClearPendingPrefix(Prefix.Unaligned); + ClearPendingPrefix(Prefix.Volatile); + var value = Pop(); var field = ResolveFieldToken(token); + TypeDesc instance; if (isStatic) { Check(field.IsStatic, VerifierError.ExpectedStaticField); + + instance = null; + + if (field.IsInitOnly) + Check(_method.IsStaticConstructor && field.OwningType == _method.OwningType, VerifierError.InitOnly); } else { @@ -1215,7 +2141,7 @@ namespace Internal.IL // Note that even if the field is static, we require that the this pointer // satisfy the same constraints as a non-static field This happens to // be simpler and seems reasonable - var actualThis = Pop(); + var actualThis = Pop(allowUninitThis: true); if (actualThis.Kind == StackValueKind.ValueType) actualThis = StackValue.CreateByRef(actualThis.Type); @@ -1223,8 +2149,18 @@ namespace Internal.IL StackValue.CreateByRef(owningType) : StackValue.CreateObjRef(owningType); CheckIsAssignable(actualThis, declaredThis); + + instance = actualThis.Type; + + if (field.IsInitOnly) + Check(_method.IsConstructor && field.OwningType == _method.OwningType && actualThis.IsThisPtr, VerifierError.InitOnly); } + // Check any constraints on the fields' class --- accessing the field might cause a class constructor to run. + Check(field.OwningType.CheckConstraints(), VerifierError.UnsatisfiedFieldParentInst); + + Check(_method.OwningType.CanAccess(field, instance), VerifierError.FieldAccess); + CheckIsAssignable(value, StackValue.CreateFromType(field.FieldType)); } @@ -1235,37 +2171,21 @@ namespace Internal.IL void ImportLoadIndirect(TypeDesc type) { - var value = Pop(); + ClearPendingPrefix(Prefix.Unaligned); + ClearPendingPrefix(Prefix.Volatile); - CheckIsByRef(value); + var address = Pop(); + CheckIsByRef(address); - if (type != null) + if (type == null) { - CheckIsAssignablePointer(value.Type, type); + CheckIsObjRef(address.Type); + type = address.Type; } else { - type = value.Type; - CheckIsObjRef(type); + CheckIsAssignable(address.Type.GetVerificationType(), type.GetVerificationType()); } - -#if false - if (ptr.IsByRef()) - { - ptrVal = DereferenceByRef(ptr); - if (instrType == TI_REF) - { - VerifyIsObjRef(ptrVal); //@TODO: give better error: Expected Obref or Variable on stack - } - else - { - VerifyCompatibleWith(vertype(ptrVal).MakeByRef(), vertype(instrType).MakeByRef()); - VerifyAndReportFound(instrType == ptrVal.GetRawType(), ptr, - MVER_E_STACK_UNEXPECTED); - } - } -#endif - Push(StackValue.CreateFromType(type)); } @@ -1276,17 +2196,33 @@ namespace Internal.IL void ImportStoreIndirect(TypeDesc type) { - throw new NotImplementedException($"{nameof(ImportStoreIndirect)} not implemented"); + ClearPendingPrefix(Prefix.Unaligned); + ClearPendingPrefix(Prefix.Volatile); + + var value = Pop(); + var address = Pop(); + + Check(!address.IsReadOnly, VerifierError.ReadOnlyIllegalWrite); + + CheckIsByRef(address); + + if (type == null) + type = address.Type; + + var typeVal = StackValue.CreateFromType(type); + var addressVal = StackValue.CreateFromType(address.Type); + + CheckIsAssignable(typeVal, addressVal); + CheckIsAssignable(value, typeVal); } void ImportThrow() { var value = Pop(); - if (value.Kind != StackValueKind.ObjRef) - { - VerificationError(VerifierError.StackObjRef); - } + CheckIsObjRef(value); + + EmptyTheStack(); } void ImportLoadString(int token) @@ -1316,19 +2252,14 @@ namespace Internal.IL var targetType = StackValue.CreateFromType(type); - // TODO: Change the error message to say "cannot box byref" instead - Check(!IsByRefLike(targetType), VerifierError.ExpectedValClassObjRefVariable, targetType); + Check(!IsByRefLike(targetType), VerifierError.BoxByRef, targetType); -#if false - VerifyIsBoxable(tiBox); + Check(type.IsPrimitive || targetType.Kind == StackValueKind.ObjRef || + type.IsGenericParameter || type.IsValueType, VerifierError.ExpectedValClassObjRefVariable); - VerifyAndReportFound(m_jitInfo->satisfiesClassConstraints(clsHnd), - tiBox, - MVER_E_UNSATISFIED_BOX_OPERAND); //"boxed type has unsatisfied class constraints"); + Check(type.CheckConstraints(), VerifierError.UnsatisfiedBoxOperand); - //Check for access to the type. - verCheckClassAccess(pResolvedToken); -#endif + Check(_method.OwningType.CanAccess(type), VerifierError.TypeAccess); CheckIsAssignable(value, targetType); @@ -1348,8 +2279,10 @@ namespace Internal.IL { EmptyTheStack(); + PropagateThisState(_currentBasicBlock, target); MarkBasicBlock(target); - // TODO + + CheckIsValidLeaveTarget(_currentBasicBlock, target); } void ImportEndFinally() @@ -1391,7 +2324,7 @@ namespace Internal.IL if (elementType != null) { - CheckIsArrayElementCompatibleWith(actualElementType, elementType); + CheckIsArrayElementCompatibleWith(actualElementType.GetVerificationType(), elementType); } else { @@ -1423,7 +2356,7 @@ namespace Internal.IL if (elementType != null) { - CheckIsArrayElementCompatibleWith(actualElementType, elementType); + CheckIsArrayElementCompatibleWith(elementType, actualElementType.GetVerificationType()); } else { @@ -1434,6 +2367,7 @@ namespace Internal.IL if (elementType != null) { + // TODO: Change to CheckIsArrayElementCompatibleWith for two intermediate types CheckIsAssignable(value, StackValue.CreateFromType(elementType)); } } @@ -1455,7 +2389,9 @@ namespace Internal.IL CheckIsPointerElementCompatibleWith(actualElementType, elementType); } - Push(StackValue.CreateByRef(elementType)); + // an array interior pointer is always on the heap, hence permanentHome = true + Push(StackValue.CreateByRef(elementType, HasPendingPrefix(Prefix.ReadOnly), true)); + ClearPendingPrefix(Prefix.ReadOnly); } void ImportLoadLength() @@ -1480,7 +2416,7 @@ namespace Internal.IL CheckIsInteger(operand); break; default: - Debug.Assert(false, "Unexpected branch opcode"); + Debug.Fail("Unexpected branch opcode"); break; } @@ -1506,7 +2442,7 @@ namespace Internal.IL { var type = ResolveTypeToken(token); - var value = Pop(); + CheckIsObjRef(Pop()); if (opCode == ILOpcode.unbox_any) { @@ -1516,7 +2452,8 @@ namespace Internal.IL { Check(type.IsValueType, VerifierError.ValueTypeExpected); - Push(StackValue.CreateByRef(type)); + // We always come from an ObjRef, hence this is permanentHome + Push(StackValue.CreateByRef(type, true, true)); } } @@ -1575,13 +2512,16 @@ namespace Internal.IL Check(_currentBasicBlock.FilterIndex.HasValue, VerifierError.Endfilter); Check(_currentOffset == _exceptionRegions[_currentBasicBlock.FilterIndex.Value].ILRegion.HandlerOffset, VerifierError.Endfilter); - var result = Pop(); - Check(result.Kind == StackValueKind.Int32, VerifierError.StackUnexpected); + var result = Pop(allowUninitThis: true); + Check(result.Kind == StackValueKind.Int32, VerifierError.StackUnexpected, result); Check(_stackTop == 0, VerifierError.EndfilterStack); } void ImportCpBlk() { + ClearPendingPrefix(Prefix.Unaligned); + ClearPendingPrefix(Prefix.Volatile); + Unverifiable(); var size = Pop(); @@ -1595,6 +2535,9 @@ namespace Internal.IL void ImportInitBlk() { + ClearPendingPrefix(Prefix.Unaligned); + ClearPendingPrefix(Prefix.Volatile); + Unverifiable(); var size = Pop(); @@ -1658,6 +2601,9 @@ namespace Internal.IL { CheckPendingPrefix(_pendingPrefix); _pendingPrefix |= Prefix.Tail; + + Check(!_currentBasicBlock.TryIndex.HasValue && !_currentBasicBlock.FilterIndex.HasValue && + !_currentBasicBlock.HandlerIndex.HasValue, VerifierError.TailCallInsideER); } void ImportConstrainedPrefix(int token) @@ -1694,6 +2640,37 @@ namespace Internal.IL Check((mask & Prefix.Constrained) == 0, VerifierError.Constrained); } + bool HasPendingPrefix(Prefix prefix) + { + return (_pendingPrefix & prefix) != 0; + } + + void ClearPendingPrefix(Prefix prefix) + { + _pendingPrefix &= ~prefix; + } + + void ReportInvalidBranchTarget(int targetOffset) + { + VerificationError(VerifierError.BadBranch); + } + + void ReportFallthroughAtEndOfMethod() + { + VerificationError(VerifierError.MethodFallthrough); + } + + void ReportMethodEndInsideInstruction() + { + VerificationError(VerifierError.MethodEnd); + AbortMethodVerification(); + } + + void ReportInvalidInstruction(ILOpcode opcode) + { + VerificationError(VerifierError.UnknownOpcode); + } + // // Deprecated // diff --git a/external/corert/src/ILVerify/src/ILVerify.csproj b/external/corert/src/ILVerify/src/ILVerify.csproj index ddfb8279d5..27ce4fc9e9 100644 --- a/external/corert/src/ILVerify/src/ILVerify.csproj +++ b/external/corert/src/ILVerify/src/ILVerify.csproj @@ -1,39 +1,27 @@ - - - + + - Debug - AnyCPU - {B2C35178-5E42-4010-A72A-938711D7F8F0} Exe - ILVerify - ILVerify - en-US - .NETFramework - v4.6 - 512 + netcoreapp2.0;net46 + AnyCPU false true - win7 - - false - - - - - - - + false + + + + + + + TypeSystem\CodeGen\MethodDesc.CodeGen.cs @@ -194,6 +182,18 @@ TypeSystem\Common\RuntimeInterfacesAlgorithm.cs + + TypeSystem\Common\ThrowHelper.Common.cs + + + TypeSystem\Common\ThrowHelper.cs + + + TypeSystem\Common\Utilities\ExceptionTypeNameFormatter.cs + + + TypeSystem\Common\Utilities\ExceptionTypeNameFormatter.Metadata.cs + Ecma\CustomAttributeTypeProvider.cs @@ -245,8 +245,8 @@ IL\MethodILDebugView.cs - - IL\ILDisassember.cs + + IL\ILDisassembler.cs IL\InstantiatedMethodIL.cs @@ -278,6 +278,9 @@ Common\System\FormattingHelpers.cs + + TypeSystem\Common\TypeSystemConstraintsHelpers.cs + @@ -286,16 +289,14 @@ CommandLine\CommandLineHelpers.cs + + System\NotImplemented.cs + + - + + + - - - - - - - - \ No newline at end of file diff --git a/external/corert/src/ILVerify/src/InstantiatedGenericParameter.cs b/external/corert/src/ILVerify/src/InstantiatedGenericParameter.cs new file mode 100644 index 0000000000..a767de4fec --- /dev/null +++ b/external/corert/src/ILVerify/src/InstantiatedGenericParameter.cs @@ -0,0 +1,96 @@ +// 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.Collections.Generic; +using System.Diagnostics; + +namespace Internal.TypeSystem +{ + internal sealed partial class InstantiatedGenericParameter : GenericParameterDesc + { + private readonly GenericParameterDesc _genericParam; + private Instantiation _typeInstantiation; + private Instantiation _methodInstantiation; + + public GenericParameterDesc GenericParameter + { + get + { + return _genericParam; + } + } + + internal static Instantiation CreateGenericTypeInstantiaton(Instantiation instantiation) + { + if (instantiation.Length == 0) + return instantiation; + + var genericInstantiation = CreateGenericInstantiation(instantiation); + + foreach (var parameter in genericInstantiation) + ((InstantiatedGenericParameter)parameter)._typeInstantiation = genericInstantiation; + + return genericInstantiation; + } + + internal static Instantiation CreateGenericMethodInstantiation(Instantiation typeInstantiation, Instantiation methodInstantiation) + { + if (methodInstantiation.Length == 0) + return methodInstantiation; + + var genericInstantiation = CreateGenericInstantiation(methodInstantiation); + + foreach (var parameter in genericInstantiation) + { + var par = (InstantiatedGenericParameter)parameter; + par._typeInstantiation = typeInstantiation; + par._methodInstantiation = genericInstantiation; + } + + return genericInstantiation; + } + + private static Instantiation CreateGenericInstantiation(Instantiation fromInstantiation) + { + var parameters = new TypeDesc[fromInstantiation.Length]; + for (int i = 0; i < fromInstantiation.Length; ++i) + parameters[i] = new InstantiatedGenericParameter((GenericParameterDesc)fromInstantiation[i]); + + return new Instantiation(parameters); + } + + private InstantiatedGenericParameter(GenericParameterDesc genericParam) + { + Debug.Assert(!(genericParam is InstantiatedGenericParameter)); + _genericParam = genericParam; + } + + public override GenericParameterKind Kind => _genericParam.Kind; + + public override int Index => _genericParam.Index; + + public override TypeSystemContext Context => _genericParam.Context; + + public override GenericVariance Variance => _genericParam.Variance; + + public override GenericConstraints Constraints => _genericParam.Constraints; + + public override IEnumerable TypeConstraints + { + get + { + foreach (var constraint in _genericParam.TypeConstraints) + { + yield return constraint.InstantiateSignature(_typeInstantiation, _methodInstantiation); + } + } + } + + public override string ToString() + { + return _genericParam.ToString(); + } + } +} diff --git a/external/corert/src/ILVerify/src/Program.cs b/external/corert/src/ILVerify/src/Program.cs index a05ef51bfd..aec3b5c18a 100644 --- a/external/corert/src/ILVerify/src/Program.cs +++ b/external/corert/src/ILVerify/src/Program.cs @@ -19,20 +19,24 @@ using Internal.IL; using Internal.CommandLine; using System.Text.RegularExpressions; +using System.Globalization; +using System.Resources; namespace ILVerify { class Program { - private const string SystemModuleSimpleName = "mscorlib"; + private const string DefaultSystemModuleName = "mscorlib"; private bool _help; - private Dictionary _inputFilePaths = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - private Dictionary _referenceFilePaths = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + private string _systemModule = DefaultSystemModuleName; + private Dictionary _inputFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); + private Dictionary _referenceFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); private IReadOnlyList _includePatterns = Array.Empty(); private IReadOnlyList _excludePatterns = Array.Empty(); private SimpleTypeSystemContext _typeSystemContext; + private ResourceManager _stringResourceManager; private int _numErrors; @@ -42,14 +46,9 @@ namespace ILVerify private void Help(string helpText) { - Console.WriteLine("ILVerify version " + typeof(Program).Assembly.GetName().Version.ToString()); + Console.WriteLine("ILVerify version " + typeof(Program).GetTypeInfo().Assembly.GetName().Version.ToString()); Console.WriteLine(); - Console.WriteLine("--help Display this usage message (Short form: -?)"); - Console.WriteLine("--reference Reference metadata from the specified assembly (Short form: -r)"); - Console.WriteLine("--include Use only methods/types/namespaces, which match the given regular expression(s) (Short form: -i)"); - Console.WriteLine("--include-file Same as --include, but the regular expression(s) are declared line by line in the specified file."); - Console.WriteLine("--exclude Skip methods/types/namespaces, which match the given regular expression(s) (Short form: -e)"); - Console.WriteLine("--exclude-file Same as --exclude, but the regular expression(s) are declared line by line in the specified file."); + Console.WriteLine(helpText); } public static IReadOnlyList StringPatternsToRegexList(IReadOnlyList patterns) @@ -78,14 +77,15 @@ namespace ILVerify syntax.HandleHelp = false; syntax.HandleErrors = true; - syntax.DefineOption("h|help", ref _help, "Help message for ILC"); - syntax.DefineOptionList("r|reference", ref referenceFiles, "Reference file(s) for compilation"); + syntax.DefineOption("h|help", ref _help, "Display this usage message"); + syntax.DefineOption("s|system-module", ref _systemModule, "System module name (default: mscorlib)"); + syntax.DefineOptionList("r|reference", ref referenceFiles, "Reference metadata from the specified assembly"); syntax.DefineOptionList("i|include", ref includePatterns, "Use only methods/types/namespaces, which match the given regular expression(s)"); syntax.DefineOption("include-file", ref includeFile, "Same as --include, but the regular expression(s) are declared line by line in the specified file."); syntax.DefineOptionList("e|exclude", ref excludePatterns, "Skip methods/types/namespaces, which match the given regular expression(s)"); syntax.DefineOption("exclude-file", ref excludeFile, "Same as --exclude, but the regular expression(s) are declared line by line in the specified file."); - syntax.DefineParameterList("in", ref inputFiles, "Input file(s) to compile"); + syntax.DefineParameterList("in", ref inputFiles, "Input file(s)"); }); foreach (var input in inputFiles) @@ -126,13 +126,24 @@ namespace ILVerify var message = new StringBuilder(); message.Append("[IL]: Error: "); - + message.Append("["); message.Append(_typeSystemContext.GetModulePath(((EcmaMethod)method).Module)); message.Append(" : "); message.Append(((EcmaType)method.OwningType).Name); message.Append("::"); message.Append(method.Name); + message.Append("("); + if (method.Signature._parameters != null && method.Signature._parameters.Length > 0) + { + foreach (TypeDesc parameter in method.Signature._parameters) + { + message.Append(parameter.ToString()); + message.Append(", "); + } + message.Remove(message.Length - 2, 2); + } + message.Append(")"); message.Append("]"); message.Append("[offset 0x"); @@ -162,7 +173,13 @@ namespace ILVerify message.Append(" "); - message.Append(SR.GetResourceString(args.Code.ToString(), null) ?? args.Code.ToString()); + if (_stringResourceManager == null) + { + _stringResourceManager = new ResourceManager("ILVerify.Resources.Strings", Assembly.GetExecutingAssembly()); + } + + var str = _stringResourceManager.GetString(args.Code.ToString(), CultureInfo.InvariantCulture); + message.Append(string.IsNullOrEmpty(str) ? args.Code.ToString() : str); Console.WriteLine(message); @@ -228,7 +245,7 @@ namespace ILVerify _typeSystemContext.InputFilePaths = _inputFilePaths; _typeSystemContext.ReferenceFilePaths = _referenceFilePaths; - _typeSystemContext.SetSystemModule(_typeSystemContext.GetModuleForSimpleName(SystemModuleSimpleName)); + _typeSystemContext.SetSystemModule(_typeSystemContext.GetModuleForSimpleName(_systemModule)); foreach (var inputPath in _inputFilePaths.Values) { diff --git a/external/corert/src/ILVerify/src/Resources/Strings.resx b/external/corert/src/ILVerify/src/Resources/Strings.resx index eba02b4c2a..63ab4cb01e 100644 --- a/external/corert/src/ILVerify/src/Resources/Strings.resx +++ b/external/corert/src/ILVerify/src/Resources/Strings.resx @@ -120,21 +120,84 @@ Array of ELEMENT_TYPE_BYREF or ELEMENT_TYPE_TYPEDBYREF. + + Stack height at all points must be determinable in a single forward scan of IL. + + + Branch out of the method. + + + Branch / Leave into the middle of an instruction. + + + Cannot box byref. + + + Branch into exception filter block. + + + Branch into exception handler block. + + + Branch into try block. + + + Branch out of exception filter block. + + + Branch out of finally block. + + + Branch out of exception handler block. + + + Branch out of try block. + + + ByRef of ByRef. + Call not allowed on abstract methods. + + call to .ctor only allowed to initialize this pointer from within a .ctor. Try newobj. + - callvirt on static + callvirt on static. Callvirt on a value type method. + + ByRef not allowed as catch type. + + + Code size is zero. + Missing callvirt following constrained prefix. The 'this' argument to a constrained call must have ByRef type. + + .ctor expected. + + + newobj on static or abstract method. + + + Unrecognized arguments for delegate .ctor. + + + Unrecognized delegate .ctor signature; expected Native Int. + + + Unrecognized delegate .ctor signature; expected Object. + + + Dup, ldvirtftn, newobj delegate::.ctor() pattern expected (in the same basic block). + Endfilter from outside an exception filter block. @@ -171,18 +234,114 @@ Value type, ObjRef type or variable type expected. + + Fallthrough the end of an exception block. + + + Fallthrough into an exception filter. + + + Fallthrough into an exception handler. + + + Field is not visible. + + + Attempt to enter a filter or catch block with unexpected stack state. + + + Attempt to enter a finally or fault block with nonempty stack. + initlocals must be set for verifiable methods with one or more local variables. + + Cannot change initonly field outside its .ctor. + + + ldftn/ldvirtftn not allowed on .ctor. + + + Cannot LDFTN a non-final virtual method for delegate creation if target object is potentially not the same type as the method class. + + + ldvirtftn on static. + + + Leave into filter block. + + + Leave into exception handler block. + + + Leave into try block. + + + Leave out of fault block. + + + Leave out of filter block. + + + Leave out of finally block. + + + Method is not visible. + + + Method ends in the middle of an instruction. + + + Fall through end of the method without returning. + + + Cannot construct an instance of abstract class. + + + Stack depth differs depending on path. + + + Non-compatible types on stack depending on path. + Missing ldelema or call following readonly prefix. + + Illegal write to readonly ByRef. + + + The readonly prefix may only be applied to calls to array methods returning ByRefs. + Rethrow from outside a catch handler. + + Stack must contain only the return value. + + + Return out of exception filter block. + + + Return out of exception handler block. + + + Return out of try block. + + + Return value missing on the stack. + + + Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. + + + Stack must be empty on return from a void function. + Expected ByRef on the stack. + + Expected pointer to function on the stack. + Non-compatible types on the stack. @@ -210,24 +369,66 @@ Missing call/callvirt/calli. + + The tail.call (or calli or callvirt) instruction cannot be used to transfer control out of a try, filter, catch, or finally block. + + + tail.call may only be followed by ret. + + + Tail call return type not compatible. + Void ret type expected for tail call. + + The 'this' parameter to the call must be the calling method's 'this' parameter. + + + Return from .ctor when this is uninitialized. + + + Store into this when it is uninitialized. + Unable to resolve token. Attempt to enter a try block with nonempty stack. + + Type is not visible. + Missing ldind, stind, ldfld, stfld, ldobj, stobj, initblk, cpblk. + + Uninitialized item on stack. + + + Unknown opcode. + + + Unmanaged pointers are not a verifiable type. + Unrecognized argument number. Unrecognized local variable number. + + Type operand of box instruction has unsatisfied class type parameter constraints. + + + Field parent instantiation has unsatisfied class type parameter constraints. + + + Method instantiation has unsatisfied method type parameter constraints. + + + Method parent instantiation has unsatisfied class type parameter constraints. + Instruction cannot be verified. diff --git a/external/corert/src/ILVerify/src/SimpleArrayOfTRuntimeInterfacesAlgorithm.cs b/external/corert/src/ILVerify/src/SimpleArrayOfTRuntimeInterfacesAlgorithm.cs new file mode 100644 index 0000000000..5e62168f02 --- /dev/null +++ b/external/corert/src/ILVerify/src/SimpleArrayOfTRuntimeInterfacesAlgorithm.cs @@ -0,0 +1,64 @@ +// 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.Diagnostics; +using Internal.IL; +using Internal.TypeSystem; + +namespace ILVerify +{ + internal class SimpleArrayOfTRuntimeInterfacesAlgorithm : RuntimeInterfacesAlgorithm + { + private DefType[] _arrayRuntimeInterfaces; + private MetadataType[] _genericRuntimeInterfaces; + private ModuleDesc _systemModule; + + private static readonly string[] s_genericRuntimeInterfacesNames = + { + "IEnumerable`1", + "ICollection`1", + "IList`1", + "IReadOnlyList`1", + "IReadOnlyCollection`1", + }; + + public SimpleArrayOfTRuntimeInterfacesAlgorithm(ModuleDesc systemModule) + { + _systemModule = systemModule; + + // initialize interfaces + _arrayRuntimeInterfaces = _systemModule.GetType("System", "Array")?.RuntimeInterfaces + ?? Array.Empty(); + + _genericRuntimeInterfaces = new MetadataType[s_genericRuntimeInterfacesNames.Length]; + int count = 0; + for (int i = 0; i < s_genericRuntimeInterfacesNames.Length; ++i) + { + MetadataType runtimeInterface =_systemModule.GetType("System.Collections.Generic", s_genericRuntimeInterfacesNames[i], false); + if (runtimeInterface != null) + _genericRuntimeInterfaces[count++] = runtimeInterface; + }; + Array.Resize(ref _genericRuntimeInterfaces, count); + } + + public override DefType[] ComputeRuntimeInterfaces(TypeDesc type) + { + ArrayType arrayType = (ArrayType)type; + TypeDesc elementType = arrayType.ElementType; + Debug.Assert(arrayType.IsSzArray); + + // first copy runtime interfaces from System.Array + var result = new DefType[_arrayRuntimeInterfaces.Length + _genericRuntimeInterfaces.Length]; + Array.Copy(_arrayRuntimeInterfaces, result, _arrayRuntimeInterfaces.Length); + + // then copy instantiated generic interfaces + int offset = _arrayRuntimeInterfaces.Length; + for (int i = 0; i < _genericRuntimeInterfaces.Length; ++i) + result[i + offset] = _genericRuntimeInterfaces[i].MakeInstantiatedType(elementType); + + return result; + } + } +} diff --git a/external/corert/src/ILVerify/src/SimpleTypeSystemContext.cs b/external/corert/src/ILVerify/src/SimpleTypeSystemContext.cs index 7915302205..123e2e0d27 100644 --- a/external/corert/src/ILVerify/src/SimpleTypeSystemContext.cs +++ b/external/corert/src/ILVerify/src/SimpleTypeSystemContext.cs @@ -4,7 +4,6 @@ using System; using System.IO; -using System.Diagnostics; using System.Collections.Generic; using System.Reflection; using System.Reflection.Metadata; @@ -20,6 +19,9 @@ namespace ILVerify { class SimpleTypeSystemContext : MetadataTypeSystemContext { + private RuntimeInterfacesAlgorithm _arrayOfTRuntimeInterfacesAlgorithm; + private MetadataRuntimeInterfacesAlgorithm _metadataRuntimeInterfacesAlgorithm = new MetadataRuntimeInterfacesAlgorithm(); + Dictionary _modules = new Dictionary(StringComparer.OrdinalIgnoreCase); class ModuleData @@ -114,5 +116,19 @@ namespace ILVerify { return _moduleData[module].Path; } + + protected override RuntimeInterfacesAlgorithm GetRuntimeInterfacesAlgorithmForNonPointerArrayType(ArrayType type) + { + if (_arrayOfTRuntimeInterfacesAlgorithm == null) + { + _arrayOfTRuntimeInterfacesAlgorithm = new SimpleArrayOfTRuntimeInterfacesAlgorithm(SystemModule); + } + return _arrayOfTRuntimeInterfacesAlgorithm; + } + + protected override RuntimeInterfacesAlgorithm GetRuntimeInterfacesAlgorithmForDefType(DefType type) + { + return _metadataRuntimeInterfacesAlgorithm; + } } } diff --git a/external/corert/src/ILVerify/src/TypeSystemHelpers.cs b/external/corert/src/ILVerify/src/TypeSystemHelpers.cs new file mode 100644 index 0000000000..52c5fd987c --- /dev/null +++ b/external/corert/src/ILVerify/src/TypeSystemHelpers.cs @@ -0,0 +1,93 @@ +// 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 Internal.TypeSystem; + +namespace ILVerify +{ + internal static class TypeSystemHelpers + { + /// + /// Returns the "reduced type" based on the definition in the ECMA-335 standard (I.8.7). + /// + internal static TypeDesc GetReducedType(this TypeDesc type) + { + if (type == null) + return null; + + var category = type.UnderlyingType.Category; + + switch (category) + { + case TypeFlags.Byte: + return type.Context.GetWellKnownType(WellKnownType.SByte); + case TypeFlags.UInt16: + return type.Context.GetWellKnownType(WellKnownType.Int16); + case TypeFlags.UInt32: + return type.Context.GetWellKnownType(WellKnownType.Int32); + case TypeFlags.UInt64: + return type.Context.GetWellKnownType(WellKnownType.Int64); + case TypeFlags.UIntPtr: + return type.Context.GetWellKnownType(WellKnownType.IntPtr); + + default: + return type.UnderlyingType; //Reduced type is type itself + } + } + + /// + /// Returns the "verification type" based on the definition in the ECMA-335 standard (I.8.7). + /// + internal static TypeDesc GetVerificationType(this TypeDesc type) + { + if (type == null) + return null; + + if (type.IsByRef) + { + var parameterVerificationType = GetVerificationType(type.GetParameterType()); + return type.Context.GetByRefType(parameterVerificationType); + } + else + { + var reducedType = GetReducedType(type); + switch (reducedType.Category) + { + case TypeFlags.Boolean: + return type.Context.GetWellKnownType(WellKnownType.SByte); + + case TypeFlags.Char: + return type.Context.GetWellKnownType(WellKnownType.Int16); + + default: + return reducedType; // Verification type is reduced type + } + } + } + + /// + /// Returns the "intermediate type" based on the definition in the ECMA-335 standard (I.8.7). + /// + internal static TypeDesc GetIntermediateType(this TypeDesc type) + { + var verificationType = GetVerificationType(type); + + if (verificationType == null) + return null; + + switch (verificationType.Category) + { + case TypeFlags.SByte: + case TypeFlags.Int16: + case TypeFlags.Int32: + return type.Context.GetWellKnownType(WellKnownType.Int32); + case TypeFlags.Single: + case TypeFlags.Double: + return type.Context.GetWellKnownType(WellKnownType.Double); + default: + return verificationType; + } + } + } +} diff --git a/external/corert/src/ILVerify/src/VerifierError.cs b/external/corert/src/ILVerify/src/VerifierError.cs index 2714ca8055..8b2db48312 100644 --- a/external/corert/src/ILVerify/src/VerifierError.cs +++ b/external/corert/src/ILVerify/src/VerifierError.cs @@ -25,9 +25,9 @@ namespace ILVerify //E_FOUND "[found %s]" //E_EXPECTED "[expected %s]" - //E_UNKNOWN_OPCODE "Unknown opcode [0x%08X]." - //E_SIG_CALLCONV "Unknown calling convention [0x%08X]." - //E_SIG_ELEMTYPE "Unknown ELEMENT_TYPE [0x%08x]." + UnknownOpcode, // Unknown opcode. + //E_SIG_CALLCONV "Unknown calling convention [0x%08X]." + //E_SIG_ELEMTYPE "Unknown ELEMENT_TYPE [0x%08x]." //E_RET_SIG "[return sig]" //E_FIELD_SIG "[field sig]" @@ -36,207 +36,152 @@ namespace ILVerify //E_STACK_TOO_LARGE "Stack is too large." //E_ARRAY_NAME_LONG "Array name is too long." - //E_FALLTHRU "fall through end of the method without returning." - //E_TRY_GTEQ_END "try start >= try end." - //E_TRYEND_GT_CS "try end > code size." - //E_HND_GTEQ_END "handler start >= handler end." - //E_HNDEND_GT_CS "handler end > code size." - //E_TRY_START "Try starts in the middle of an instruction." - //E_HND_START "Handler starts in the middle of an instruction." - //E_TRY_OVERLAP "Try block overlap with another block." - //E_TRY_EQ_HND_FIL "Try and filter/handler blocks are equivalent." - //E_TRY_SHARE_FIN_FAL "Shared try has finally or fault handler." - //E_HND_OVERLAP "Handler block overlaps with another block." - //E_HND_EQ "Handler block is the same as another block." - //E_FIL_OVERLAP "Filter block overlaps with another block." - //E_FIL_EQ "Filter block is the same as another block." - //E_FIL_CONT_TRY "Filter contains try." - //E_FIL_CONT_HND "Filter contains handler." - //E_FIL_CONT_FIL "Nested filters." - //E_FIL_GTEQ_CS "filter >= code size." - //E_FIL_START "Filter starts in the middle of an instruction." - //E_FALLTHRU_EXCEP "fallthru the end of an exception block." - //E_FALLTHRU_INTO_HND "fallthru into an exception handler." - //E_FALLTHRU_INTO_FIL "fallthru into an exception filter." - //E_LEAVE "Leave from outside a try or catch block." - Rethrow, //"Rethrow from outside a catch handler." - Endfinally, //"Endfinally from outside a finally handler." - Endfilter, //"Endfilter from outside an exception filter block." - //E_ENDFILTER_MISSING "Missing Endfilter." - //E_BR_INTO_TRY "Branch into try block." - //E_BR_INTO_HND "Branch into exception handler block." - //E_BR_INTO_FIL "Branch into exception filter block." - //E_BR_OUTOF_TRY "Branch out of try block." - //E_BR_OUTOF_HND "Branch out of exception handler block." - //E_BR_OUTOF_FIL "Branch out of exception filter block." - //E_BR_OUTOF_FIN "Branch out of finally block." - //E_RET_FROM_TRY "Return out of try block." - //E_RET_FROM_HND "Return out of exception handler block." - //E_RET_FROM_FIL "Return out of exception filter block." - //E_BAD_JMP_TARGET "jmp / exception into the middle of an instruction." - //E_PATH_LOC "Non-compatible types depending on path." - //E_PATH_THIS "Init state for this differs depending on path." - //E_PATH_STACK "Non-compatible types on stack depending on path." - //E_PATH_STACK_DEPTH "Stack depth differs depending on path." - //E_THIS "Instance variable (this) missing." - //E_THIS_UNINIT_EXCEP "Uninitialized this on entering a try block." - //E_THIS_UNINIT_STORE "Store into this when it is uninitialized." - //E_THIS_UNINIT_RET "Return from .ctor when this is uninitialized." - //E_THIS_UNINIT_V_RET "Return from .ctor before all fields are initialized." - //E_THIS_UNINIT_BR "Branch back when this is uninitialized." - //E_LDFTN_CTOR "ldftn/ldvirtftn not allowed on .ctor." - //StackNotEq, // "Non-compatible types on the stack." + MethodFallthrough, // Fall through end of the method without returning. + //E_TRY_GTEQ_END "try start >= try end." + //E_TRYEND_GT_CS "try end > code size." + //E_HND_GTEQ_END "handler start >= handler end." + //E_HNDEND_GT_CS "handler end > code size." + //E_TRY_START "Try starts in the middle of an instruction." + //E_HND_START "Handler starts in the middle of an instruction." + //E_TRY_OVERLAP "Try block overlap with another block." + //E_TRY_EQ_HND_FIL "Try and filter/handler blocks are equivalent." + //E_TRY_SHARE_FIN_FAL "Shared try has finally or fault handler." + //E_HND_EQ "Handler block is the same as another block." + //E_FIL_CONT_TRY "Filter contains try." + //E_FIL_CONT_HND "Filter contains handler." + //E_FIL_CONT_FIL "Nested filters." + //E_FIL_GTEQ_CS "filter >= code size." + FallthroughException, // Fallthrough the end of an exception block. + FallthroughIntoHandler, // Fallthrough into an exception handler. + FallthroughIntoFilter, // Fallthrough into an exception filter. + LeaveIntoTry, // Leave into try block. + LeaveIntoHandler, // Leave into exception handler block. + LeaveIntoFilter, // Leave into filter block. + LeaveOutOfFilter, // Leave out of filter block. + LeaveOutOfFinally, // Leave out of finally block. + LeaveOutOfFault, // Leave out of fault block. + Rethrow, // Rethrow from outside a catch handler. + Endfinally, // Endfinally from outside a finally handler. + Endfilter, // Endfilter from outside an exception filter block. + BranchIntoTry, // Branch into try block. + BranchIntoHandler, // Branch into exception handler block. + BranchIntoFilter, // Branch into exception filter block. + BranchOutOfTry, // Branch out of try block. + BranchOutOfHandler, // Branch out of exception handler block. + BranchOutOfFilter, // Branch out of exception filter block. + BranchOutOfFinally, // Branch out of finally block. + ReturnFromTry, // Return out of try block. + ReturnFromHandler, // Return out of exception handler block. + ReturnFromFilter, // Return out of exception filter block. + BadJumpTarget, // Branch / Leave into the middle of an instruction. + PathStackUnexpected, // Non-compatible types on stack depending on path. + PathStackDepth, // Stack depth differs depending on path. + //E_THIS_UNINIT_EXCEP "Uninitialized this on entering a try block." + ThisUninitStore, // Store into this when it is uninitialized. + ThisUninitReturn, // Return from .ctor when this is uninitialized. + LdftnCtor, // ldftn/ldvirtftn not allowed on .ctor. + //StackNotEq, // Non-compatible types on the stack. StackUnexpected, // Unexpected type on the stack. StackUnexpectedArrayType, // Unexpected array type on the stack. - //E_STACK_EXCEPTION "Missing stack slot for exception." StackOverflow, // Stack overflow. StackUnderflow, // Stack underflow. - //E_STACK_EMPTY "Stack empty." - //E_STACK_UNINIT "Uninitialized item on stack." + UninitStack, // Uninitialized item on stack. ExpectedIntegerType, // Expected I, I4, or I8 on the stack. ExpectedFloatType, // Expected R, R4, or R8 on the stack. - //E_STACK_NO_R_I8 "unexpected R, R4, R8, or I8 on the stack." ExpectedNumericType, // Expected numeric type on the stack. - StackObjRef, // "Expected an ObjRef on the stack." - //E_STACK_P_OBJREF "Expected address of an ObjRef on the stack." + StackObjRef, // Expected an ObjRef on the stack. StackByRef, // Expected ByRef on the stack. - //E_STACK_METHOD "Expected pointer to function on the stack." - //E_STACK_ARRAY_SD "Expected single dimension array on the stack." - //E_STACK_VALCLASS "Expected value type instance on the stack." - //E_STACK_P_VALCLASS "Expected address of value type on the stack." - //E_STACK_NO_VALCLASS "Unexpected value type instance on the stack." - //E_LOC_DEAD "Local variable is unusable at this point." + StackMethod, // Expected pointer to function on the stack. UnrecognizedLocalNumber, // Unrecognized local variable number. UnrecognizedArgumentNumber, // Unrecognized argument number. ExpectedTypeToken, // Expected type token. TokenResolve, // Unable to resolve token. - //E_TOKEN_TYPE "Unable to resolve type of the token." + //E_TOKEN_TYPE "Unable to resolve type of the token." ExpectedMethodToken, // Expected memberRef, memberDef or methodSpec token. - //E_TOKEN_TYPE_FIELD "Expected memberRef or fieldDef token." - //E_TOKEN_TYPE_SIG "Expected signature token." ExpectedFieldToken, // Expected field token. - // E_TOKEN_TYPE_SIG "Expected signature token." Unverifiable, // Instruction can not be verified. StringOperand, // Operand does not point to a valid string ref. - //E_RET_PTR_TO_STACK "Return type is ByRef, TypedReference, ArgHandle, or ArgIterator." - //E_RET_VOID "Stack must be empty on return from a void function." - //E_RET_MISSING "Return value missing on the stack." - //E_RET_EMPTY "Stack must contain only the return value." - //E_RET_UNINIT "Return uninitialized data." - //E_ARRAY_ACCESS "Illegal array access." - //E_ARRAY_V_STORE "Store non Object type into Object array." + ReturnPtrToStack, // Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. + ReturnVoid, // Stack must be empty on return from a void function. + ReturnMissing, // Return value missing on the stack. + ReturnEmpty, // Stack must contain only the return value. ExpectedArray, // Expected single-dimension zero-based array. - //E_ARRAY_SD_PTR "Expected single dimension array of pointer types." - //E_ARRAY_FIELD "Array field access is denied." - //E_ARGLIST "Allowed only in vararg methods." + //E_ARRAY_SD_PTR "Expected single dimension array of pointer types." + //E_ARGLIST "Allowed only in vararg methods." ValueTypeExpected, // Value type expected. - //E_OPEN_DLGT_PROT_ACC "Protected method access through an open instance delegate is not verifiable." - //E_METHOD_ACCESS "Method is not visible." - //E_FIELD_ACCESS "Field is not visible." - //E_DEAD "Item is unusable at this point." + //E_OPEN_DLGT_PROT_ACC "Protected method access through an open instance delegate is not verifiable." + TypeAccess, // Type is not visible. + MethodAccess, // Method is not visible. + FieldAccess, // Field is not visible. ExpectedStaticField, // Expected static field. - //E_FIELD_NO_STATIC "Expected non-static field." - //E_ADDR "Address of not allowed for this item." - //E_ADDR_BYREF "Address of not allowed for ByRef." - //E_ADDR_LITERAL "Address of not allowed for literal field." - //E_INITONLY "Cannot change initonly field outside its .ctor." - //E_WRITE_RVA_STATIC "Cannot modify an imaged based (RVA) static" - //E_THROW "Cannot throw this object." - CallVirtOnValueType, // Callvirt on a value type method. - //E_CALL_SIG "Call signature mismatch." - //E_CALL_STATIC "Static function expected." - //E_CTOR ".ctor expected." - //E_CTOR_VIRT "Cannot use callvirt on .ctor." - //E_CTOR_OR_SUPER "Only super::ctor or typeof(this)::ctor allowed here." - //E_CTOR_MUL_INIT "Possible call to .ctor more than once." - //E_SIG "Unrecognized signature." - //E_SIG_ARRAY "Cannot resolve Array type." - //E_SIG_ARRAY_PTR "Array of ELEMENT_TYPE_PTR." + InitOnly, // Cannot change initonly field outside its .ctor. + //E_WRITE_RVA_STATIC "Cannot modify an imaged based (RVA) static" + CallVirtOnValueType, // Callvirt on a value type method. + CtorExpected, // .ctor expected. + CtorSig, // newobj on static or abstract method. + //E_SIG_ARRAY "Cannot resolve Array type." ArrayByRef, // Array of ELEMENT_TYPE_BYREF or ELEMENT_TYPE_TYPEDBYREF. - //E_SIG_ELEM_PTR "ELEMENT_TYPE_PTR cannot be verified." - //E_SIG_VARARG "Unexpected vararg." - //E_SIG_VOID "Unexpected Void." - //E_SIG_BYREF_BYREF "ByRef of ByRef" - //E_CODE_SIZE_ZERO "Code size is zero." - //E_BAD_VARARG "Unrecognized use of vararg." + ByrefOfByref, // ByRef of ByRef. + CodeSizeZero, // Code size is zero. TailCall, // Missing call/callvirt/calli. TailByRef, // Cannot pass ByRef to a tail call. - //E_TAIL_RET "Missing ret." + TailRet, // tail.call may only be followed by ret. TailRetVoid, // Void ret type expected for tail call. - //E_TAIL_RET_TYPE "Tail call return type not compatible." + TailRetType, // Tail call return type not compatible. TailStackEmpty, // Stack not empty after tail call. - //E_METHOD_END "Method ends in the middle of an instruction." - //E_BAD_BRANCH "Branch out of the method." - //E_FIN_OVERLAP "Finally handler blocks overlap." - //E_LEXICAL_NESTING "Lexical nesting." + MethodEnd, // Method ends in the middle of an instruction. + BadBranch, // Branch out of the method. + //E_LEXICAL_NESTING "Lexical nesting." Volatile, // Missing ldsfld, stsfld, ldind, stind, ldfld, stfld, ldobj, stobj, initblk, or cpblk. Unaligned, // Missing ldind, stind, ldfld, stfld, ldobj, stobj, initblk, cpblk. - //E_INNERMOST_FIRST "Innermost exception blocks should be declared first." - //E_CALLI_VIRTUAL "Calli not allowed on virtual methods." - CallAbstract, // Call not allowed on abstract methods. - //E_NOT_IN_GC_HEAP "Value type with NotInGCHeap attribute being created on the GC heap." - TryNonEmptyStack, // Attempt to enter a try block with nonempty stack. - //E_DLGT_CTOR "Unrecognized arguments for delegate .ctor." - //E_DLGT_BB "Delegate .ctor not allowed at the start of a basic block when the function pointer argument is a virtual method." - //E_DLGT_PATTERN "Dup, ldvirtftn, newobj delegate::.ctor() pattern expected (in the same basic block)." - //E_DLGT_LDFTN "Ldftn or ldvirtftn instruction required before call to a delegate .ctor." - //E_FTN_ABSTRACT "Attempt to load address of an abstract method." - //E_SIG_C_VC "ELEMENT_TYPE_CLASS ValueClass in signature." - //E_SIG_VC_C "ELEMENT_TYPE_VALUETYPE non-ValueClass in signature." - //E_BOX_PTR_TO_STACK "Box operation on TypedReference, ArgHandle, or ArgIterator." - //E_SIG_BYREF_TB_AH "ByRef of TypedReference, ArgHandle, or ArgIterator." - //E_SIG_ARRAY_TB_AH "Array of TypedReference, ArgHandle, or ArgIterator." - EndfilterStack, //"Stack not empty when leaving an exception filter." - //E_DLGT_SIG_I "Unrecognized delegate .ctor signature; expected I." - //E_DLGT_SIG_O "Unrecognized delegate .ctor signature; expected Object." - //E_RA_PTR_TO_STACK "Mkrefany on TypedReference, ArgHandle, or ArgIterator." - //E_CATCH_VALUE_TYPE "Value type not allowed as catch type." - //E_CATCH_BYREF "ByRef not allowed as catch type." - //E_FIL_PRECEED_HND "filter block should immediately precede handler block" - //E_LDVIRTFTN_STATIC "ldvirtftn on static" - CallVirtOnStatic, // callvirt on static + //E_INNERMOST_FIRST "Innermost exception blocks should be declared first." + CallAbstract, // Call not allowed on abstract methods. + TryNonEmptyStack, // Attempt to enter a try block with nonempty stack. + FilterOrCatchUnexpectedStack, // Attempt to enter a filter or catch block with unexpected stack state. + FinOrFaultNonEmptyStack, // Attempt to enter a finally or fault block with nonempty stack. + DelegateCtor, // Unrecognized arguments for delegate .ctor. + DelegatePattern, // Dup, ldvirtftn, newobj delegate::.ctor() pattern expected (in the same basic block). + //E_SIG_C_VC "ELEMENT_TYPE_CLASS ValueClass in signature." + //E_SIG_VC_C "ELEMENT_TYPE_VALUETYPE non-ValueClass in signature." + //E_BOX_PTR_TO_STACK "Box operation on TypedReference, ArgHandle, or ArgIterator." + BoxByRef, // Cannot box byref. + //E_SIG_BYREF_TB_AH "ByRef of TypedReference, ArgHandle, or ArgIterator." + EndfilterStack, // Stack not empty when leaving an exception filter. + DelegateCtorSigI, // Unrecognized delegate .ctor signature; expected Native Int. + DelegateCtorSigO, // Unrecognized delegate .ctor signature; expected Object. + //E_RA_PTR_TO_STACK "Mkrefany on TypedReference, ArgHandle, or ArgIterator." + CatchByRef, // ByRef not allowed as catch type. + LdvirtftnOnStatic, // ldvirtftn on static. + CallVirtOnStatic, // callvirt on static. InitLocals, // initlocals must be set for verifiable methods with one or more local variables. - //E_BR_TO_EXCEPTION "branch/leave to the beginning of a catch/filter handler" - //E_CALL_CTOR "call to .ctor only allowed to initialize this pointer from within a .ctor. Try newobj." - + CallCtor, // call to .ctor only allowed to initialize this pointer from within a .ctor. Try newobj. + ////@GENERICSVER: new generics related error messages ExpectedValClassObjRefVariable, // Value type, ObjRef type or variable type expected. - //E_STACK_P_VALCLASS_OBJREF_VAR "Expected address of value type, ObjRef type or variable type on the stack." - //E_SIG_VAR_PARAM "Unrecognized type parameter of enclosing class." - //E_SIG_MVAR_PARAM "Unrecognized type parameter of enclosing method." - //E_SIG_VAR_ARG "Unrecognized type argument of referenced class instantiation." - //E_SIG_MVAR_ARG "Unrecognized type argument of referenced method instantiation." - //E_SIG_GENERICINST "Cannot resolve generic type." - //E_SIG_METHOD_INST "Method instantiation contains non boxable type arguments." - //E_SIG_METHOD_PARENT_INST "Method parent instantiation contains non boxable type arguments." - //E_SIG_FIELD_PARENT_INST "Field parent instantiation contains non boxable type arguments." - //E_CALLCONV_NOT_GENERICINST "Unrecognized calling convention for an instantiated generic method." - //E_TOKEN_BAD_METHOD_SPEC "Unrecognized generic method in method instantiation." ReadOnly, // Missing ldelema or call following readonly prefix. Constrained, // Missing callvirt following constrained prefix. - //E_CIRCULAR_VAR_CONSTRAINTS "Method parent has circular class type parameter constraints." - //E_CIRCULAR_MVAR_CONSTRAINTS "Method has circular method type parameter constraints." + //E_CIRCULAR_VAR_CONSTRAINTS "Method parent has circular class type parameter constraints." + //E_CIRCULAR_MVAR_CONSTRAINTS "Method has circular method type parameter constraints." - //E_UNSATISFIED_METHOD_INST "Method instantiation has unsatisfied method type parameter constraints." - //E_UNSATISFIED_METHOD_PARENT_INST "Method parent instantiation has unsatisfied class type parameter constraints." - //E_UNSATISFIED_FIELD_PARENT_INST "Field parent instantiation has unsatisfied class type parameter constraints." - //E_UNSATISFIED_BOX_OPERAND "Type operand of box instruction has unsatisfied class type parameter constraints." - ConstrainedCallWithNonByRefThis, // The 'this' argument to a constrained call must have ByRef type. + UnsatisfiedMethodInst, // Method instantiation has unsatisfied method type parameter constraints. + UnsatisfiedMethodParentInst, // Method parent instantiation has unsatisfied class type parameter constraints. + UnsatisfiedFieldParentInst, // Field parent instantiation has unsatisfied class type parameter constraints. + UnsatisfiedBoxOperand, // Type operand of box instruction has unsatisfied class type parameter constraints. + ConstrainedCallWithNonByRefThis, // The 'this' argument to a constrained call must have ByRef type. //E_CONSTRAINED_OF_NON_VARIABLE_TYPE "The operand to a constrained prefix instruction must be a type parameter." - //E_READONLY_UNEXPECTED_CALLEE "The readonly prefix may only be applied to calls to array methods returning ByRefs." - //E_READONLY_ILLEGAL_WRITE "Illegal write to readonly ByRef." + ReadonlyUnexpectedCallee, // The readonly prefix may only be applied to calls to array methods returning ByRefs. + ReadOnlyIllegalWrite, // Illegal write to readonly ByRef. //E_READONLY_IN_MKREFANY "A readonly ByRef cannot be used with mkrefany." - //E_UNALIGNED_ALIGNMENT "Alignment specified for 'unaligned' prefix must be 1, 2, or 4." - //E_TAILCALL_INSIDE_EH "The tail.call (or calli or callvirt) instruction cannot be used to transfer control out of a try, filter, catch, or finally block." - //E_BACKWARD_BRANCH "Stack height at all points must be determinable in a single forward scan of IL." - //E_CALL_TO_VTYPE_BASE "Call to base type of valuetype." - //E_NEWOBJ_OF_ABSTRACT_CLASS "Cannot construct an instance of abstract class." - //E_UNMANAGED_POINTER "Unmanaged pointers are not a verifiable type." - //E_LDFTN_NON_FINAL_VIRTUAL "Cannot LDFTN a non-final virtual method for delegate creation if target object is potentially not the same type as the method class." - //E_FIELD_OVERLAP "Accessing type with overlapping fields." - //E_THIS_MISMATCH "The 'this' parameter to the call must be the calling method's 'this' parameter." - //E_STACK_I_I4 "Expected I4 on the stack." + //E_UNALIGNED_ALIGNMENT "Alignment specified for 'unaligned' prefix must be 1, 2, or 4." + TailCallInsideER, // The tail.call (or calli or callvirt) instruction cannot be used to transfer control out of a try, filter, catch, or finally block. + BackwardBranch, // Stack height at all points must be determinable in a single forward scan of IL. + //E_CALL_TO_VTYPE_BASE "Call to base type of valuetype." + NewobjAbstractClass, // Cannot construct an instance of abstract class. + UnmanagedPointer, // Unmanaged pointers are not a verifiable type. + LdftnNonFinalVirtual, // Cannot LDFTN a non-final virtual method for delegate creation if target object is potentially not the same type as the method class. + //E_FIELD_OVERLAP "Accessing type with overlapping fields." + ThisMismatch, // The 'this' parameter to the call must be the calling method's 'this' parameter. //E_BAD_PE "Unverifiable PE Header/native stub." //E_BAD_MD "Unrecognized metadata, unable to verify IL." diff --git a/external/corert/src/ILVerify/src/project.json b/external/corert/src/ILVerify/src/project.json deleted file mode 100644 index cfbfd8dd52..0000000000 --- a/external/corert/src/ILVerify/src/project.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "dependencies": { - "NETStandard.Library": "1.6.0", - "System.IO.MemoryMappedFiles": "4.0.0", - "System.Reflection.Metadata": "1.4.1-beta-24227-04", - "System.CommandLine": "0.1.0-e160909-1" - }, - "frameworks": { - "net46": {} - }, - "runtimes": { - "win7-x64": {} - } -} diff --git a/external/corert/src/ILVerify/tests/ILMethodTester.cs b/external/corert/src/ILVerify/tests/ILMethodTester.cs new file mode 100644 index 0000000000..671bd3f915 --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILMethodTester.cs @@ -0,0 +1,79 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Reflection.Metadata.Ecma335; +using Internal.IL; +using Internal.TypeSystem.Ecma; +using Xunit; + +namespace ILVerify.Tests +{ + public class ILMethodTester + { + [Theory(DisplayName = "")] + [MemberData(nameof(TestDataLoader.GetMethodsWithValidIL), MemberType = typeof(TestDataLoader))] + [Trait("", "Valid IL Tests")] + void TestMethodsWithValidIL(ValidILTestCase validIL) + { + ILImporter importer = ConstructILImporter(validIL); + + var verifierErrors = new List(); + importer.ReportVerificationError = new Action((err) => + { + verifierErrors.Add(err.Code); + }); + + importer.Verify(); + Assert.Equal(0, verifierErrors.Count); + } + + [Theory(DisplayName = "")] + [MemberData(nameof(TestDataLoader.GetMethodsWithInvalidIL), MemberType = typeof(TestDataLoader))] + [Trait("", "Invalid IL Tests")] + void TestMethodsWithInvalidIL(InvalidILTestCase invalidIL) + { + ILImporter importer = ConstructILImporter(invalidIL); + + var verifierErrors = new List(); + importer.ReportVerificationError = new Action((err) => + { + verifierErrors.Add(err.Code); + }); + + try + { + importer.Verify(); + } + catch + { + //in some cases ILVerify throws exceptions when things look too wrong to continue + //currently these are not caught. In tests we just catch these and do the asserts. + //Once these exceptions are better handled and ILVerify instead of crashing aborts the verification + //gracefully we can remove this empty catch block. + } + finally + { + Assert.Equal(invalidIL.ExpectedVerifierErrors.Count, verifierErrors.Count); + + foreach (var item in invalidIL.ExpectedVerifierErrors) + { + var actual = verifierErrors.Select(e => e.ToString()); + Assert.True(verifierErrors.Contains(item), $"Actual errors where: {string.Join(',', actual)}"); + } + } + } + + private ILImporter ConstructILImporter(TestCase testCase) + { + var module = TestDataLoader.GetModuleForTestAssembly(testCase.ModuleName); + var method = (EcmaMethod)module.GetMethod(MetadataTokens.EntityHandle(testCase.MetadataToken)); + var methodIL = EcmaMethodIL.Create(method); + + return new ILImporter(method, methodIL); + } + } +} diff --git a/external/corert/src/ILVerify/tests/ILTests/AccessTests.il b/external/corert/src/ILVerify/tests/ILTests/AccessTests.il new file mode 100644 index 0000000000..9de153c3c5 --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILTests/AccessTests.il @@ -0,0 +1,398 @@ +// 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. + +.assembly extern System.Runtime +{ +} + +.assembly extern AccessTestsFriend +{ +} + +.assembly AccessTests +{ +} + +.class private auto ansi beforefieldinit PrivateClass +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } +} + +.class public auto ansi beforefieldinit GenericClass + extends [System.Runtime]System.Object +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } +} + +.class public auto ansi beforefieldinit SimpleClass + extends [System.Runtime]System.Object +{ + .field public static int32 publicField + .field private static int32 privateField + .field family static int32 familyField + .field assembly static int32 assemblyField + .field famorassem static int32 familyOrAssemblyField + .field famandassem static int32 familyAndAssemblyField + + .field family int32 instanceFamilyField + + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } + + .method private hidebysig static void PrivateMethod() cil managed + { + ret + } + + .method family hidebysig static void FamilyMethod() cil managed + { + ret + } + + .method assembly hidebysig static void AssemblyMethod() cil managed + { + ret + } + + .method famorassem hidebysig static void FamilyOrAssemblyMethod() cil managed + { + ret + } + + .method famandassem hidebysig static void FamilyAndAssemblyMethod() cil managed + { + ret + } + + .method public hidebysig static void InaccessibleParamMethod(class SimpleClass/PrivateNestedClass c) cil managed + { + ret + } + + .method public hidebysig static void GenericMethod() cil managed + { + ret + } + + .method family hidebysig instance void FamilyMethod() cil managed + { + ret + } + + .method public hidebysig static class SimpleClass/PrivateNestedClass InaccessibleReturnMethod() cil managed + { + newobj instance void SimpleClass/PrivateNestedClass::.ctor() + ret + } + + .class nested public auto ansi beforefieldinit PublicNestedClass + extends [System.Runtime]System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } + + .method public hidebysig static void Instantiate.PrivateNestedClassFromNested_Valid() cil managed + { + newobj instance void SimpleClass/PrivateNestedClass::.ctor() + pop + ret + } + + .method public hidebysig static void Load.PrivateFieldFromNestedClass_Valid() cil managed + { + ldsfld int32 SimpleClass::privateField + pop + ret + } + } + + .class nested private auto ansi beforefieldinit PrivateNestedClass + extends [System.Runtime]System.Object + { + .field public static int32 publicField + + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } + } + + .class nested assembly auto ansi beforefieldinit AssemblyNestedClass + extends [System.Runtime]System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } + } + + .class nested famorassem auto ansi beforefieldinit FamilyOrAssemblyNestedClass + extends [System.Runtime]System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } + } + + .class nested famandassem auto ansi beforefieldinit FamilyAndAssemblyNestedClass + extends [System.Runtime]System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } + } + + .class nested family auto ansi beforefieldinit FamilyNestedClass + extends [System.Runtime]System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } + } + + .method private hidebysig static void Call.PrivateClassParamIntern_Valid(class SimpleClass/PrivateNestedClass c) cil managed + { + ldarg.0 + call void SimpleClass::InaccessibleParamMethod(class SimpleClass/PrivateNestedClass) + ret + } + + .method private hidebysig static void Call.PrivateReturnTypeIntern_Valid() cil managed + { + call class SimpleClass/PrivateNestedClass SimpleClass::InaccessibleReturnMethod() + pop + ret + } +} + +.class public auto ansi beforefieldinit AccessTestsType + extends [System.Runtime]System.Object +{ + .method public hidebysig instance void Instantiate.PublicNestedClass_Valid() cil managed + { + newobj instance void SimpleClass/PublicNestedClass::.ctor() + pop + ret + } + + .method public hidebysig instance void Instantiate.PrivateNestedClass_Invalid_MethodAccess() cil managed + { + newobj instance void SimpleClass/PrivateNestedClass::.ctor() + pop + ret + } + + .method public hidebysig instance void Instantiate.AssemNestedClass_Valid() cil managed + { + newobj instance void SimpleClass/AssemblyNestedClass::.ctor() + pop + ret + } + + .method public hidebysig instance void Instantiate.AssemOrFamNestedClass_Valid() cil managed + { + newobj instance void SimpleClass/FamilyOrAssemblyNestedClass::.ctor() + pop + ret + } + + .method public hidebysig instance void Instantiate.AssemAndFamNestedClass_Invalid_MethodAccess() cil managed + { + newobj instance void SimpleClass/FamilyAndAssemblyNestedClass::.ctor() + pop + ret + } + + .method public hidebysig instance void Instantiate.FamilyNestedClass_Invalid_MethodAccess() cil managed + { + newobj instance void SimpleClass/FamilyNestedClass::.ctor() + pop + ret + } + + .method public hidebysig instance void Instantiate.GenericWithPrivateNestedClass_Invalid_MethodAccess() cil managed + { + newobj instance void class GenericClass::.ctor() + pop + ret + } + + .method public hidebysig instance void Call.PrivateClassParamExtern_Invalid_MethodAccess(class SimpleClass/PrivateNestedClass c) cil managed + { + ldarg.1 + call void SimpleClass::InaccessibleParamMethod(class SimpleClass/PrivateNestedClass) + ret + } + + .method public hidebysig instance void Call.PrivateReturnTypeExtern_Invalid_MethodAccess() cil managed + { + call class SimpleClass/PrivateNestedClass SimpleClass::InaccessibleReturnMethod() + pop + ret + } + + .method public hidebysig instance void Call.GenericMethodWithPrivateNestedClass_Invalid_MethodAccess() cil managed + { + call void class SimpleClass::GenericMethod() + ret + } + + .method public hidebysig instance void Load.PrivateFieldExtern_Invalid_FieldAccess() cil managed + { + ldsfld int32 SimpleClass::privateField + pop + ret + } + + .method public hidebysig instance void Load.PublicFieldOfPrivateClass_Invalid_FieldAccess() cil managed + { + ldsfld int32 SimpleClass/PrivateNestedClass::publicField + pop + ret + } +} + +.class public auto ansi beforefieldinit DerivedType + extends SimpleClass +{ + .method public hidebysig instance void Call.DerivedFamAndAssemblyMethod_Valid() cil managed + { + call void SimpleClass::FamilyAndAssemblyMethod() + ret + } + + .method public hidebysig instance void Instantiate.FamOrAssemblyNestedClassFromDerived_Valid() cil managed + { + newobj instance void SimpleClass/FamilyOrAssemblyNestedClass::.ctor() + pop + ret + } + + .method public hidebysig instance void Instantiate.FamNestedClassFromDerived_Valid() cil managed + { + newobj instance void SimpleClass/FamilyNestedClass::.ctor() + pop + ret + } + + .method private hidebysig instance void Load.FamFieldOfBaseWithBaseInstance_Invalid_FieldAccess(class SimpleClass c) cil managed + { + ldarg.1 + ldfld int32 SimpleClass::instanceFamilyField + pop + ret + } + + .method private hidebysig instance void Load.FamFieldOfBaseWithDerivedInstance_Valid() cil managed + { + ldarg.0 + ldfld int32 SimpleClass::instanceFamilyField + pop + ret + } + + .method private hidebysig instance void Call.FamMethodOfBaseWithBaseInstance_Invalid_MethodAccess(class SimpleClass c) cil managed + { + ldarg.1 + call instance void SimpleClass::FamilyMethod() + ret + } + + .method private hidebysig instance void Call.FamMethodOfBaseWithDerivedInstance_Valid() cil managed + { + ldarg.0 + call instance void SimpleClass::FamilyMethod() + ret + } + + .method private hidebysig instance void Load.AssemFieldOfFriendAssembly_Valid() cil managed + { + ldsfld int32 [AccessTestsFriend]AccessTestsFriendType::assemblyField + pop + ret + } + + .method private hidebysig instance void Load.PrivateFieldOfFriendAssembly_Invalid_FieldAccess() cil managed + { + ldsfld int32 [AccessTestsFriend]AccessTestsFriendType::privateField + pop + ret + } +} + +// Special case: Nested class trying to access family field of superclass of containing class + +.class private auto ansi beforefieldinit SuperClass + extends [System.Runtime]System.Object +{ + .field family static int32 familyField + .field family int32 instanceFamilyField + + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } +} + +.class private auto ansi beforefieldinit NestedClass + extends SuperClass +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void SuperClass::.ctor() + ret + } + + .class nested private auto ansi beforefieldinit NestedClass + extends [System.Runtime]System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } + + .method private hidebysig instance void Load.FamFieldOfSuperOfContaining_Valid() cil managed + { + ldsfld int32 SuperClass::familyField + pop + ret + } + } +} diff --git a/external/corert/src/ILVerify/tests/ILTests/AccessTestsExtern.il b/external/corert/src/ILVerify/tests/ILTests/AccessTestsExtern.il new file mode 100644 index 0000000000..39939e1bfc --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILTests/AccessTestsExtern.il @@ -0,0 +1,163 @@ +// 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. + +.assembly extern System.Runtime +{ +} + +.assembly extern AccessTests +{ +} + +.assembly extern AccessTestsFriend +{ +} + +.assembly AccessTestsExtern +{ +} + +.class public auto ansi beforefieldinit AccessTestsType + extends [System.Runtime]System.Object +{ + .method public hidebysig instance void Instantiate.PublicClass_Valid() cil managed + { + newobj instance void [AccessTests]SimpleClass::.ctor() + pop + ret + } + + .method public hidebysig instance void Instantiate.PrivateClass_Invalid_MethodAccess() cil managed + { + newobj instance void [AccessTests]PrivateClass::.ctor() + pop + ret + } + + .method public hidebysig instance void Instantiate.PublicNested_Valid() cil managed + { + newobj instance void [AccessTests]SimpleClass/PublicNestedClass::.ctor() + pop + ret + } + + .method public hidebysig instance void Instantiate.PrivateNested_Invalid_MethodAccess() cil managed + { + newobj instance void [AccessTests]SimpleClass/PrivateNestedClass::.ctor() + pop + ret + } + + .method public hidebysig instance void Instantiate.AssemblyNested_Invalid_MethodAccess() cil managed + { + newobj instance void [AccessTests]SimpleClass/AssemblyNestedClass::.ctor() + pop + ret + } + + .method public hidebysig instance void Instantiate.FamOrAssemNested_Invalid_MethodAccess() cil managed + { + newobj instance void [AccessTests]SimpleClass/FamilyOrAssemblyNestedClass::.ctor() + pop + ret + } + + .method public hidebysig instance void Instantiate.FamAndAssemNested_Invalid_MethodAccess() cil managed + { + newobj instance void [AccessTests]SimpleClass/FamilyAndAssemblyNestedClass::.ctor() + pop + ret + } + + .method public hidebysig instance void Instantiate.FamilyNested_Invalid_MethodAccess() cil managed + { + newobj instance void [AccessTests]SimpleClass/FamilyNestedClass::.ctor() + pop + ret + } + + .method public hidebysig instance void Call.PrivateMethod_Invalid_MethodAccess() cil managed + { + call void [AccessTests]SimpleClass::PrivateMethod() + ret + } + + .method public hidebysig instance void Call.FamilyMethod_Invalid_MethodAccess() cil managed + { + call void [AccessTests]SimpleClass::FamilyMethod() + ret + } + + .method public hidebysig instance void Call.AssemblyMethod_Invalid_MethodAccess() cil managed + { + call void [AccessTests]SimpleClass::AssemblyMethod() + ret + } + + .method public hidebysig instance void Call.FamOrAssemMethod_Invalid_MethodAccess() cil managed + { + call void [AccessTests]SimpleClass::FamilyOrAssemblyMethod() + ret + } + + .method public hidebysig instance void Call.FamAndAssemMethod_Invalid_MethodAccess() cil managed + { + call void [AccessTests]SimpleClass::FamilyAndAssemblyMethod() + ret + } + + .method private hidebysig instance void Load.AssemFieldOfFriendAssemblyWithSpace_Valid() cil managed + { + ldsfld int32 [AccessTestsFriend]AccessTestsFriendType::assemblyField + pop + ret + } +} + +.class public auto ansi beforefieldinit DerivedType + extends [AccessTests]SimpleClass +{ + .method public hidebysig instance void Call.DerivedFamMethod_Valid() cil managed + { + call void [AccessTests]SimpleClass::FamilyMethod() + ret + } + + .method public hidebysig instance void Call.DerivedFamOrAssemMethod_Valid() cil managed + { + call void [AccessTests]SimpleClass::FamilyOrAssemblyMethod() + ret + } + + .method public hidebysig instance void Call.DerivedFamAndAssemMethod_Invalid_MethodAccess() cil managed + { + call void [AccessTests]SimpleClass::FamilyAndAssemblyMethod() + ret + } +} + +.class public auto ansi beforefieldinit DerivedClass + extends [AccessTests]SimpleClass +{ + .method public hidebysig instance void Instantiate.FamAndAssemNestedClassFromDerived_Invalid_MethodAccess() cil managed + { + newobj instance void [AccessTests]SimpleClass/FamilyAndAssemblyNestedClass::.ctor() + pop + ret + } + + .method public hidebysig instance void Instantiate.FamOrAssemNestedClassFromDerived_Valid() cil managed + { + newobj instance void [AccessTests]SimpleClass/FamilyOrAssemblyNestedClass::.ctor() + pop + ret + } + + .method public hidebysig instance void Instantiate.FamNestedClassFromDerived_Valid() cil managed + { + newobj instance void [AccessTests]SimpleClass/FamilyNestedClass::.ctor() + pop + ret + } +} diff --git a/external/corert/src/ILVerify/tests/ILTests/AccessTestsFriend.il b/external/corert/src/ILVerify/tests/ILTests/AccessTestsFriend.il new file mode 100644 index 0000000000..78db6d8ad8 --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILTests/AccessTestsFriend.il @@ -0,0 +1,33 @@ +// 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. + +.assembly extern System.Runtime +{ +} + +.assembly extern AccessTests +{ +} + +.assembly AccessTestsFriend +{ + // InternalsVisibleTo("AccessTests") + .custom instance void [System.Runtime]System.Runtime.CompilerServices.InternalsVisibleToAttribute::.ctor(string) = ( + 01 00 0b 41 63 63 65 73 73 54 65 73 74 73 00 00 + ) + + // InternalsVisibleTo("AccessTestsExtern ") + // ^ purposeful space at the end + .custom instance void [System.Runtime]System.Runtime.CompilerServices.InternalsVisibleToAttribute::.ctor(string) = ( + 01 00 12 41 63 63 65 73 73 54 65 73 74 73 45 78 + 74 65 72 6e 20 00 00 + ) +} + +.class private auto ansi beforefieldinit AccessTestsFriendType + extends [System.Runtime]System.Object +{ + .field private static int32 privateField + .field assembly static int32 assemblyField +} diff --git a/external/corert/src/ILVerify/tests/ILTests/ArrayTests.il b/external/corert/src/ILVerify/tests/ILTests/ArrayTests.il new file mode 100644 index 0000000000..7f4cb903a7 --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILTests/ArrayTests.il @@ -0,0 +1,92 @@ +// 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. + +.assembly extern System.Runtime +{ +} + +.assembly ArrayTests +{ +} + +.class public auto ansi beforefieldinit ArrayTestsType + extends [System.Runtime]System.Object +{ + + .method public hidebysig instance void StoreElement.Int8IntoBoolArray_Valid() cil managed + { + // bool[] a = new bool[]; + // a[0] = true; + + ldc.i4.1 + newarr [System.Runtime]System.Boolean + ldc.i4.0 + ldc.i4.1 + stelem.i1 + ret + } + + .method public hidebysig instance void StoreElement.Int16IntoCharArray_Valid() cil managed + { + // char[] a = new char[]; + // a[0] = 'a'; + + ldc.i4.1 + newarr [System.Runtime]System.Char + ldc.i4.0 + ldc.i4.s 97 + stelem.i2 + ret + } + + .method public hidebysig instance void LoadElement.UInt16FromCharArray_Valid() cil managed + { + .locals init ( + [0] char[], + [1] char + ) + + // char[] a = new char[]; + // a[0] = 'a'; + // char b = a[0]; + + ldc.i4.1 + newarr [System.Runtime]System.Char + stloc.0 + ldloc.0 + ldc.i4.0 + ldc.i4.s 97 + stelem.i2 + ldloc.0 + ldc.i4.0 + ldelem.u2 + stloc.1 + ret + } + + .method public hidebysig instance void LoadElement.UInt8FromBoolArray_Valid() cil managed + { + .locals init ( + [0] bool[], + [1] bool + ) + + // bool[] a = new bool[]; + // a[0] = true; + // bool b = a[0]; + + ldc.i4.1 + newarr [System.Runtime]System.Boolean + stloc.0 + ldloc.0 + ldc.i4.0 + ldc.i4.1 + stelem.i1 + ldloc.0 + ldc.i4.0 + ldelem.u1 + stloc.1 + ret + } +} diff --git a/external/corert/src/ILVerify/tests/ILTests/BasicArithmeticTests.il b/external/corert/src/ILVerify/tests/ILTests/BasicArithmeticTests.il new file mode 100644 index 0000000000..8ce554d862 --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILTests/BasicArithmeticTests.il @@ -0,0 +1,33 @@ +// 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. + +.assembly extern System.Runtime +{ +} + +.assembly BasicArithmeticTests +{ +} + +.class public auto ansi beforefieldinit BasicArithmeticTestsType + extends [System.Runtime]System.Object +{ + .method public hidebysig instance int32 + SimpleAdd_Valid(int32 a, + int32 b) cil managed + { + ldarg.1 + ldarg.2 + add + ret + } + + .method public hidebysig instance void SimpleAdd_Invalid_ExpectedNumericType() cil managed + { + ldstr "sfdsf" + ldc.i4.3 + add + ret + } +} diff --git a/external/corert/src/ILVerify/tests/ILTests/BranchingTests.il b/external/corert/src/ILVerify/tests/ILTests/BranchingTests.il new file mode 100644 index 0000000000..dd5258c224 --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILTests/BranchingTests.il @@ -0,0 +1,1017 @@ +// 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. + +.assembly extern System.Runtime +{ +} + +.assembly BranchingTests +{ +} + +// Custom Comparer class implementing IEqualityComparer for testing +.class private auto ansi beforefieldinit CustomComparer + extends [System.Runtime]System.Object + implements [System.Runtime]System.Collections.IEqualityComparer +{ + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } + + .method public final hidebysig newslot virtual + instance bool Equals ( + object x, + object y + ) cil managed + { + ldc.i4.0 + ret + } + + .method public final hidebysig newslot virtual + instance int32 GetHashCode ( + object obj + ) cil managed + { + ldarg.1 + callvirt instance int32 [System.Runtime]System.Object::GetHashCode() + ret + } +} + +.class public auto ansi beforefieldinit BranchingTestsType + extends [System.Runtime]System.Object +{ + // Volatile field for testing + .field private static int32 modreq([System.Runtime]System.Runtime.CompilerServices.IsVolatile) volatileField + + .method static public hidebysig void StaticMethod() cil managed + { + ret + } + + .method static public hidebysig void Branching.NullConditional_Valid() cil managed + { + //object o = null; + //Type t = o != null ? o.GetType() : null; + //Type.GetTypeCode(t); + + .maxstack 1 + .locals init ( + [0] object o + ) + + IL_0000: ldnull + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: brtrue.s IL_0008 + + IL_0005: ldnull + IL_0006: br.s IL_000E + + IL_0008: ldloc.0 + IL_0009: callvirt instance class [System.Runtime]System.Type [System.Runtime]System.Object::GetType() + + IL_000E: call valuetype [System.Runtime]System.TypeCode [System.Runtime]System.Type::GetTypeCode(class [System.Runtime]System.Type) + IL_0013: pop + IL_0014: ret + } + + .method static public hidebysig void Branching.NullConditional_Invalid_StackUnexpected() cil managed + { + //object o = null; + //Type t = o != null ? o.GetType() : o; + //Type.GetTypeCode(t); + + .maxstack 1 + .locals init ( + [0] object o + ) + + IL_0000: ldnull + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: brtrue.s IL_0008 + + IL_0005: ldloc.0 + IL_0006: br.s IL_000E + + IL_0008: ldloc.0 + IL_0009: callvirt instance class [System.Runtime]System.Type [System.Runtime]System.Object::GetType() + + IL_000E: call valuetype [System.Runtime]System.TypeCode [System.Runtime]System.Type::GetTypeCode(class [System.Runtime]System.Type) + IL_0013: pop + IL_0014: ret + } + + .method static public hidebysig void Branching.UnequalStackMerge_Valid(class [System.Runtime]System.Collections.IEqualityComparer equalityComparer, + class CustomComparer customComparer) cil managed + { + .maxstack 1 + .locals init( + [0] class [System.Runtime]System.Collections.IEqualityComparer + ) + + ldc.i4.0 + brfalse.s lbl_cComp + + ldarg.0 + br.s lbl_store + + lbl_cComp: + ldarg.1 + + lbl_store: + stloc.0 + ret + } + + .method static public hidebysig void Branching.UnequalStackMergeConstrained_Valid<([System.Runtime]System.Collections.IEqualityComparer) T> + (class [System.Runtime]System.Collections.IEqualityComparer equalityComparer, !!T customComparer) cil managed + { + .maxstack 1 + .locals init( + [0] class [System.Runtime]System.Collections.IEqualityComparer + ) + + ldc.i4.0 + brfalse.s lbl_cComp + + ldarg.0 + br.s lbl_store + + lbl_cComp: + ldarg.1 + box !!T + + lbl_store: + stloc.0 + ret + } + + .method static public hidebysig void Branching.UnequalStackMergeArrays_Valid(class CustomComparer[] comparers, + class [System.Runtime]System.Collections.IEqualityComparer[] customComparer) cil managed + { + .maxstack 1 + .locals init( + [0] class [System.Runtime]System.Collections.IEqualityComparer[] + ) + + ldarg.0 + brfalse.s lbl_cComp + + ldarg.0 + br.s lbl_store + + lbl_cComp: + ldarg.1 + + lbl_store: + stloc.0 + ret + } + + .method static public hidebysig void Branching.UnequalStackMergeArrays_Invalid_StackUnexpected(class CustomComparer[] comparers, + class [System.Runtime]System.Collections.IEqualityComparer[] customComparer) cil managed + { + .maxstack 1 + .locals init( + [0] class CustomComparer[] + ) + + ldarg.0 + brfalse.s lbl_cComp + + ldarg.0 + br.s lbl_store + + lbl_cComp: + ldarg.1 + + lbl_store: + stloc.0 + ret + } + + .method static public hidebysig void Branching.UnequalStackMergeGeneric_Invalid_PathStackUnexpected(!!T generic, + object obj) cil managed + { + .maxstack 1 + .locals init( + [0] object obj + ) + + ldarg.1 + brfalse.s lbl_gen + + ldarg.1 + br.s lbl_store + + lbl_gen: + ldarg.0 + + lbl_store: + stloc.0 + ret + } + + .method static public hidebysig void Branching.UnequalStackMergeGenericArrayConstrain_Valid<(int32[]) T>(!!T generic, + int32[] arr) cil managed + { + .maxstack 1 + .locals init( + [0] int32[] dest + ) + + ldarg.1 + brfalse.s lbl_arr + + ldarg.0 + box !!T + br.s lbl_store + + lbl_arr: + ldarg.1 + + lbl_store: + stloc.0 + ret + } + + .method static public hidebysig void Branching.InsideTry_Valid() cil managed + { + .maxstack 2 + + .try + { + ldc.i4.s 10 + ldc.i4.s 5 + bne.un.s lbl_true + + nop + br.s lbl_leave + + lbl_true: nop + + lbl_leave: leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Branching.OutOfTry_Invalid_BranchOutOfTry() cil managed + { + .maxstack 2 + + .try + { + ldc.i4.s 10 + ldc.i4.s 5 + bne.un.s lbl_true + + nop + br.s lbl_ret + + lbl_true: nop + + lbl_leave: leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Branching.IntoTryStart_Valid() cil managed + { + .maxstack 2 + + ldc.i4.s 10 + ldc.i4.s 5 + bne.un.s lbl_true + + nop + br.s lbl_ret + + .try + { + lbl_true: nop + + leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Branching.IntoTryMiddle_Invalid_BranchIntoTry() cil managed + { + .maxstack 2 + + ldc.i4.s 10 + ldc.i4.s 5 + bne.un.s lbl_true + + nop + br.s lbl_ret + + .try + { + nop + lbl_true: nop + + leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Branching.IntoNestedTryStart_Valid() cil managed + { + .maxstack 2 + + .try + { + ldc.i4.s 10 + ldc.i4.s 5 + bne.un.s lbl_true + + nop + br.s lbl_leave + .try + { + lbl_true: nop + + leave.s lbl_leave + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_leave + } + + lbl_leave: leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Branching.IntoDoubleNestedTryStart_Invalid_BranchIntoTry() cil managed + { + .try + { + nop + br Inner + + .try + { + nop + + .try + { + Inner: + leave.s A + } + catch [System.Runtime]System.Object + { + pop + leave.s A + } + A: + + leave.s B + } + catch [System.Runtime]System.Object + { + pop + leave.s B + } + B: + + leave.s C + } + catch [System.Runtime]System.Object + { + pop + leave.s C + } + C: + + ret + } + + .method static public hidebysig void Branching.IntoDoubleNestedTryStartStart_Valid() cil managed + { + .try + { + nop + br Inner + + .try + { + .try + { + Inner: + leave.s A + } + catch [System.Runtime]System.Object + { + pop + leave.s A + } + A: + + leave.s B + } + catch [System.Runtime]System.Object + { + pop + leave.s B + } + B: + + leave.s C + } + catch [System.Runtime]System.Object + { + pop + leave.s C + } + C: + + ret + } + + .method static public hidebysig void Branching.IntoDoubleNestedTryStartFromOutside_Invalid_BranchIntoTry() cil managed + { + br Inner + + .try + { + nop + + .try + { + Inner: + leave.s A + } + catch [System.Runtime]System.Object + { + pop + leave.s A + } + A: + + leave.s B + } + catch [System.Runtime]System.Object + { + pop + leave.s B + } + B: + + ret + } + + .method static public hidebysig void Branching.IntoTryStartWithPredecessor_Valid() cil managed + { + .try + { + nop + br Inner + + .try + { + leave.s A + } + catch [System.Runtime]System.Object + { + pop + leave.s A + } + + .try + { + Inner: + leave.s A + } + catch [System.Runtime]System.Object + { + pop + leave.s A + } + + A: + + leave.s C + } + catch [System.Runtime]System.Object + { + pop + leave.s C + } + C: + + ret + } + + .method static public hidebysig void Branching.IntoNestedTryMiddle_Invalid_BranchIntoTry() cil managed + { + .maxstack 2 + + .try + { + ldc.i4.s 10 + ldc.i4.s 5 + bne.un.s lbl_true + + nop + br.s lbl_leave + .try + { + nop + lbl_true: nop + + leave.s lbl_leave + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_leave + } + + lbl_leave: leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Branching.FromTryIntoCatch_Invalid_BranchOutOfTry.BranchIntoHandler() cil managed + { + .maxstack 2 + + .try + { + ldc.i4.s 10 + ldc.i4.s 5 + bne.un.s lbl_true + + nop + leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + lbl_true: nop + pop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Branching.FromTryIntoFilter_Invalid_BranchOutOfTry.BranchIntoFilter() cil managed + { + .try + { + br.s lbl_filter + } + filter + { + lbl_filter: pop + ldc.i4.0 + endfilter + } + { + pop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Branching.FromTryIntoFinally_Invalid_BranchOutOfTry.BranchIntoHandler() cil managed + { + .maxstack 2 + + .try + { + ldc.i4.s 10 + ldc.i4.s 5 + bne.un.s lbl_finally + + nop + leave.s lbl_ret + } + finally + { + lbl_finally: nop + endfinally + } + + lbl_ret: ret + } + + .method static public hidebysig void Branching.OutOfFinally_Invalid_BranchOutOfFinally() cil managed + { + .maxstack 2 + + .try + { + leave.s lbl_ret + } + finally + { + br lbl_ret + endfinally + } + + lbl_ret: ret + } + + .method static public hidebysig void Branching.FromTryIntoOtherTryStart_Invalid_BranchOutOfTry() cil managed + { + .maxstack 2 + + .try + { + ldc.i4.s 10 + ldc.i4.s 5 + bne.un.s lbl_try2 + + nop + leave.s lbl_try + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_try + } + + lbl_try: nop + .try + { + lbl_try2: nop + leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Branching.OutOfTrySameStart_Invalid_BranchOutOfTry() cil managed + { + .try + { + .try + { + br A + + leave.s A + } + catch [System.Runtime]System.Object + { + pop + leave.s A + } + A: + + leave.s B + } + catch [System.Runtime]System.Object + { + pop + leave.s B + } + B: + + ret + } + + .method static public hidebysig void Branching.FromTryIntoOtherTryMiddle_Invalid_BranchOutOfTry() cil managed + { + .maxstack 2 + + .try + { + ldc.i4.s 10 + ldc.i4.s 5 + bne.un.s lbl_try2 + + nop + leave.s lbl_try + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_try + } + + lbl_try: nop + .try + { + nop + lbl_try2: leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Fallthrough.OutOfTryIntoFinally_Invalid_FallthroughException.FallthroughIntoHandler() cil managed + { + .try + { + nop + } + finally + { + endfinally + } + } + + .method static public hidebysig void Fallthrough.OutOfTryIntoCatch_Invalid_FallthroughException.FallthroughIntoHandler() cil managed + { + .try + { + nop + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Fallthrough.OutOfCatch_Invalid_FallthroughException() cil managed + { + .try + { + nop + leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + pop + } + + lbl_ret: ret + } + + .method static public hidebysig void Fallthrough.OutOfTryIntoFilter_Invalid_FallthroughException.FallthroughIntoFilter() cil managed + { + .try + { + nop + } + filter + { + pop + ldc.i4.0 + endfilter + } + { + pop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Branch.BeforeReadonlyInstruction_Valid(object[] objectArray) cil managed + { + // objectArray[0]; + + ldarg.0 + ldc.i4.0 + br BeforeInstr + + BeforeInstr: + readonly. + ldelema [System.Runtime]System.Object + pop + ret + } + + .method static public hidebysig void Branch.IntoReadonlyInstruction_Invalid_BadJumpTarget(object[] objectArray) cil managed + { + // objectArray[0]; + + ldarg.0 + ldc.i4.0 + br MidInstr + + readonly. + MidInstr: + ldelema [System.Runtime]System.Object + pop + ret + } + + .method static public hidebysig void Branch.IntoConstrainedInstruction_Invalid_BadJumpTarget(!!T arg) cil managed + { + // arg.ToString(); + ldarga.s arg + br MidInstr + + constrained. !!T + MidInstr: + callvirt instance string [System.Runtime]System.Object::ToString() + pop + ret + } + + .method static public hidebysig void Branch.AfterConstrainedInstruction_Valid(!!T arg) cil managed + { + // arg.ToString(); + ldarga.s arg + br AfterInstr + + constrained. !!T + callvirt instance string [System.Runtime]System.Object::ToString() + AfterInstr: + pop + ret + } + + .method static public hidebysig void Branch.IntoVolatileInstruction_Invalid_BadJumpTarget() cil managed + { + // volatileField = 0; + + ldc.i4.0 + br MidInstr + + volatile. + MidInstr: + stsfld int32 modreq([System.Runtime]System.Runtime.CompilerServices.IsVolatile) BranchingTestsType::volatileField + ret + } + + .method static public hidebysig void Branch.IntoUnalignedInstruction_Invalid_BadJumpTarget() cil managed + { + .locals init ( + int32& + ) + + ldloc.0 + br MidInstr + + unaligned. 4 + MidInstr: + ldind.i4 + pop + ret + } + + .method static public hidebysig void Branch.IntoUnalignedVolatileInstruction_Invalid_BadJumpTarget() cil managed + { + .locals init ( + int32& + ) + + ldloc.0 + br MidInstr + + unaligned. 4 + volatile. + MidInstr: + ldind.i4 + pop + ret + } + + .method static public hidebysig void Branch.IntoTailInstruction_Invalid_BadJumpTarget() cil managed + { + br MidInstr + + tail. + MidInstr: + call void BranchingTestsType::StaticMethod() + ret + } + + .method static public hidebysig void Branch.BackwardWithPredecessor_Valid() cil managed + { + ldnull + ldc.i4.0 + brfalse MethodEnd + + BackwardBranch: + pop + + ldnull + ldc.i4.1 + brtrue BackwardBranch + + MethodEnd: + pop + ret + } + + .method static public hidebysig void Branch.BackwardWithoutPredecessor_Invalid_BackwardBranch() cil managed + { + br MethodEnd + + BackwardBranch: + pop + + MethodEnd: + ldnull + ldc.i4.1 + brtrue BackwardBranch + + pop + ret + } + + .method static public hidebysig void BrFalseS.Backward_Valid() cil managed + { + BackwardBranch: + // Note: additional nops to increase branch delta + nop + nop + ldc.i4.0 + brfalse.s BackwardBranch + + ret + } + + .method static public hidebysig void Branch.BackwardEmptyStack_Valid() cil managed + { + br MethodEnd + + BackwardBranch: + nop + + MethodEnd: + ldc.i4.1 + brtrue BackwardBranch + + ret + } + + .method static public hidebysig void Branch.BackwardWithComplexPredecessor_Valid() cil managed + { + ldnull + br MethodEnd + + Predecessor: + ldnull + + BackwardBranch: + pop + + ldnull + ldc.i4.1 + brtrue BackwardBranch + + MethodEnd: + pop + ldc.i4.1 + brtrue Predecessor + + ret + } +} diff --git a/external/corert/src/ILVerify/tests/ILTests/CallTests.il b/external/corert/src/ILVerify/tests/ILTests/CallTests.il new file mode 100644 index 0000000000..a0637a25c0 --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILTests/CallTests.il @@ -0,0 +1,150 @@ +// 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. + +.assembly extern System.Runtime +{ +} + +.assembly CallTests +{ +} + +.class public auto ansi beforefieldinit SimpleClass + extends [System.Runtime]System.Object +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } + + .method public hidebysig newslot virtual instance void VirtualMethod() cil managed + { + ret + } + + .method public hidebysig static void InObjectMethod([in] object&) cil managed + { + ret + } +} + +.class public auto ansi beforefieldinit DerivedClass + extends SimpleClass +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void SimpleClass::.ctor() + ret + } + + .method public hidebysig virtual instance void VirtualMethod() cil managed + { + ldarg.0 + call instance void SimpleClass::VirtualMethod() + ret + } + + .method public hidebysig instance void Call.BaseMethod_Valid() cil managed + { + ldarg.0 + call instance void SimpleClass::VirtualMethod() + ret + } + + .method public hidebysig instance void Call.BaseMethodModThisLdarga_Invalid_ThisMismatch() cil managed + { + ldarg.0 + call instance void SimpleClass::VirtualMethod() + ldarga 0 + pop + ret + } + + .method public hidebysig instance void Call.BaseMethodModThisStarg_Invalid_ThisMismatch() cil managed + { + ldarg.0 + call instance void SimpleClass::VirtualMethod() + ldarg.0 + starg 0 + ret + } +} + +.class public auto ansi beforefieldinit CallTestsType + extends [System.Runtime]System.Object +{ + .method public hidebysig instance void Call.IntToStringManagedPtr_Valid() cil managed + { + .locals init ( + [0] int32 + ) + + ldc.i4.0 + stloc.0 + ldloca.s 0 + call instance string [System.Runtime]System.Int32::ToString() + pop + ret + } + + .method public hidebysig instance void Call.IntToStringBoxed_Valid() cil managed + { + .locals init ( + [0] int32 + ) + + ldc.i4.0 + box [System.Runtime]System.Int32 + call instance string [System.Runtime]System.Object::ToString() + pop + ret + } + + .method public hidebysig instance void Callvirt.IntToStringBoxed_Valid() cil managed + { + .locals init ( + [0] int32 + ) + + ldc.i4.0 + box [System.Runtime]System.Int32 + callvirt instance string [System.Runtime]System.Object::ToString() + pop + ret + } + + .method public hidebysig instance void Call.IntToStringUnboxed_Invalid_StackUnexpected() cil managed + { + .locals init ( + [0] int32 + ) + + ldc.i4.0 + call instance string [System.Runtime]System.Int32::ToString() + pop + ret + } + + .method public hidebysig instance void Call.ExternBaseMethod_Invalid_ThisMismatch(class DerivedClass c) cil managed + { + ldarg.1 + call instance void SimpleClass::VirtualMethod() + ret + } + + .method public hidebysig instance void Call.ReadonlyByRefForInArg_Invalid_StackUnexpected(object[] objectArray) cil managed + { + // SimpleClass.InObjectMethod(objectArray[0]); + + ldarg.1 + ldc.i4.0 + readonly. + ldelema [System.Runtime]System.Object + call void SimpleClass::InObjectMethod(object&) + ret + } +} diff --git a/external/corert/src/ILVerify/tests/ILTests/CastingTests.il b/external/corert/src/ILVerify/tests/ILTests/CastingTests.il new file mode 100644 index 0000000000..bf754a53c6 --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILTests/CastingTests.il @@ -0,0 +1,370 @@ +// 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. + +.assembly extern System.Runtime +{ +} + +.assembly CastingTests +{ +} + +// Provides tests for casting arrays +.class public auto ansi beforefieldinit ArrayCastingTestsType + extends [System.Runtime]System.Object +{ + // Array tests + .method static public hidebysig void Casting.ArrayOfTToEnumerableOfT_Valid(int32[] test) cil managed + { + .locals init ( + class [System.Runtime]System.Collections.Generic.IEnumerable`1 V_0 + ) + ldarg.0 + stloc.0 + ret + } + + .method static public hidebysig void Casting.ArrayOfTToEnumerable_Valid(int32[] test) cil managed + { + .locals init ( + class [System.Runtime]System.Collections.IEnumerable V_0 + ) + ldarg.0 + stloc.0 + ret + } + + .method static public hidebysig void Casting.ArrayOfTToICollectionOfT_Valid(int32[] test) cil managed + { + .locals init ( + class [System.Runtime]System.Collections.Generic.ICollection`1 V_0 + ) + ldarg.0 + stloc.0 + ret + } + + .method static public hidebysig void Casting.ArrayOfTToICollection_Valid(int32[] test) cil managed + { + .locals init ( + class [System.Runtime]System.Collections.ICollection V_0 + ) + ldarg.0 + stloc.0 + ret + } + + .method static public hidebysig void Casting.ArrayOfTToIListOfT_Valid(int32[] test) cil managed + { + .locals init ( + class [System.Runtime]System.Collections.Generic.IList`1 V_0 + ) + ldarg.0 + stloc.0 + ret + } + + .method static public hidebysig void Casting.ArrayOfTToIList_Valid(int32[] test) cil managed + { + .locals init ( + class [System.Runtime]System.Collections.IList V_0 + ) + ldarg.0 + stloc.0 + ret + } + + .method static public hidebysig void Casting.ArrayOfTToIReadOnlyCollectionOfT_Valid(int32[] test) cil managed + { + .locals init ( + class [System.Runtime]System.Collections.Generic.IReadOnlyCollection`1 V_0 + ) + ldarg.0 + stloc.0 + ret + } + + .method static public hidebysig void Casting.ArrayOfTToIReadOnlyListOfT_Valid(int32[] test) cil managed + { + .locals init ( + class [System.Runtime]System.Collections.Generic.IReadOnlyList`1 V_0 + ) + ldarg.0 + stloc.0 + ret + } + + .method static public hidebysig void Casting.ArrayOfTToArray_Valid(int32[] test) cil managed + { + .locals init ( + class [System.Runtime]System.Array V_0 + ) + ldarg.0 + stloc.0 + ret + } + + .method static public hidebysig void Assign.ConstrainedGenericToComparable_Valid<(class [System.Runtime]System.IComparable`1) T>(!!T t) + { + .locals init ( + class [System.Runtime]System.IComparable`1 c + ) + + ldarg.0 + box !!T + stloc.0 + ret + } + + .method static public hidebysig void Assign.ComplexConstrainedGenericToComparable_Valid<(!!S) T, (class [System.Runtime]System.IComparable`1) S>(!!T t) + { + .locals init ( + class [System.Runtime]System.IComparable`1 c + ) + + ldarg.0 + box !!T + stloc.0 + ret + } +} + +.class public sequential ansi sealed beforefieldinit GenericOtherFieldsType`1 + extends [System.Runtime]System.ValueType +{ + .field public !T GenericField; + .field int32 IntField; +} + +.class public sequential ansi beforefieldinit GenericTypeWithConstrained + extends [System.Runtime]System.Object +{ + .property !TValueType Value() + { + .get instance !0 GenericTypeWithConstrained::get_Value() + } + .method public !TValueType get_Value() + { + ldc.i4 0x00000000 + ret + } +} + +// Provides casting logic tests for stfld, ldfld, call, callvirt +.class public auto ansi beforefieldinit GenericCastingTestsType`1 + extends [System.Runtime]System.Object +{ + .field private int32 ThisIntField + .method public hidebysig instance void Casting.StorePlainFieldInThisGenericType_Valid(int32 v) cil managed + { + ldarg.0 + ldarg.1 + stfld int32 class GenericCastingTestsType`1::ThisIntField + ret + } + + .method public hidebysig instance void Casting.StorePlainFieldInOtherGenericType_Valid(int32 v) cil managed + { + .locals init ( + class GenericOtherFieldsType`1 V_0 + ) + + ldloc.0 + ldarg.1 + stfld !0 class GenericOtherFieldsType`1::IntField + ret + } + + .field private !T ThisGenericField + .method public hidebysig instance void Casting.StoreGenericFieldInThisGenericType_Valid(!T v) cil managed + { + ldarg.0 + ldarg.1 + stfld !0 class GenericCastingTestsType`1::ThisGenericField + ret + } + + .method public hidebysig instance void Casting.StoreGenericFieldInOtherGenericType_Valid(!T v) cil managed + { + .locals init ( + class GenericOtherFieldsType`1 V_0 + ) + ldloc.0 + ldarg.1 + stfld !0 class GenericOtherFieldsType`1::GenericField + ret + } + + .method public hidebysig instance void Casting.CallPlainFunctionFromThisGenericType_Valid(int32 v) cil managed + { + ldarg.0 + ldarg.1 + callvirt instance void class GenericCastingTestsType`1::Casting.StorePlainFieldInThisGenericType_Valid(int32) + ret + } + + .method public hidebysig instance void Casting.CallGenericFunctionFromThisGenericType_Valid(!T v) cil managed + { + ldarg.0 + ldarg.1 + callvirt instance void class GenericCastingTestsType`1::Casting.CallGenericFunctionFromOtherGenericType_Valid(!0) + ret + } + + .method public hidebysig instance void Casting.CallGenericFunctionFromOtherGenericType_Valid(!T v) cil managed + { + .locals init ( + class [System.Runtime]System.Func`1 V_0 + ) + + ldloc.0 + callvirt instance !0 class [System.Runtime]System.Func`1::Invoke() + pop + ret + } + + .method public hidebysig instance void Casting.AssignVariantInterfaceToObject_Valid(class [System.Runtime]System.Collections.Generic.IEnumerable`1 e) cil managed + { + .locals init ( + object o + ) + + ldarg.1 + stloc.0 + ret + } + + .method public hidebysig instance void Casting.AssignThisToSameTypeWithOtherGenericArgs_Invalid_StackUnexpected() cil managed + { + .locals init ( + class GenericCastingTestsType`1 V_0 + ) + + ldarg.0 + stloc.0 + ret + } + + .method public hidebysig instance void Casting.AssignGenericToObject_Valid(!T v) cil managed + { + .locals init ( + object + ) + + ldarg.1 + box !T + stloc.0 + ret + } + + .method public hidebysig static void Casting.AssignToSameTypeWithOtherGenericArgs_Invalid_StackUnexpected() cil managed + { + .locals init ( + class GenericCastingTestsType`1 V_0, + class GenericCastingTestsType`1 V_1 + ) + + ldloc.0 + stloc.1 + ret + } + + .method public static void Casting.AssignReturnValueFromConstrainedProperty_Valid(class GenericTypeWithConstrained wrapper) cil managed + { + .locals init ( + class [System.Runtime]System.IComparable V_0 + ) + + ldarg.0 + callvirt instance !0 class GenericTypeWithConstrained::get_Value() + box !!TValueType + stloc.0 + ret + } +} + +// Class with private nested class +.class public auto ansi beforefieldinit ContainingClass + extends [System.Runtime]System.Object +{ + .class nested private auto ansi beforefieldinit PrivateNestedClass + extends [System.Runtime]System.Object + { + } +} + + +// Test enum +.class public auto ansi sealed Enum + extends [System.Runtime]System.Enum +{ + .field public static literal valuetype Enum EnumValue1 = int32(0) + .field public static literal valuetype Enum EnumValue2 = int32(1) + + .field public specialname rtspecialname int32 value__ +} + +.class public auto ansi beforefieldinit CastingTestsType + extends [System.Runtime]System.Object +{ + .method public static void Casting.AssignPrimitiveToIComparable_Valid(int32 arg) cil managed + { + .locals init ( + class [System.Runtime]System.IComparable + ) + + ldarg.0 + box [System.Runtime]System.Int32 + stloc.0 + ret + } + + .method public static void Casting.CastToInaccessibleType_Invalid_TypeAccess() cil managed + { + ldnull + castclass ContainingClass/PrivateNestedClass + pop + ret + } + + .method public static void Casting.CastClassInt_Invalid_StackObjRef() cil managed + { + ldc.i4.0 + castclass CastingTestsType + pop + ret + } + + .method public static void Casting.BoxInaccesibleType_Invalid_TypeAccess(class ContainingClass/PrivateNestedClass c) cil managed + { + ldarg.0 + box ContainingClass/PrivateNestedClass + pop + ret + } + + .method public static void Casting.BoxByRefInt_Invalid_BoxByRef.ExpectedValClassObjRefVariable(int32& i) cil managed + { + ldarg.0 + box int32& + pop + ret + } + + .method public static void Casting.BoxToUnsatisfiedConstraint_Invalid_UnsatisfiedBoxOperand(class GenericTypeWithConstrained c) cil managed + { + ldarg.0 + box class GenericTypeWithConstrained + pop + ret + } + + .method public static void Casting.BoxEnum_Valid(valuetype Enum e) cil managed + { + ldarg.0 + box Enum + pop + ret + } +} diff --git a/external/corert/src/ILVerify/tests/ILTests/ComparisonTests.il b/external/corert/src/ILVerify/tests/ILTests/ComparisonTests.il new file mode 100644 index 0000000000..28797f958a --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILTests/ComparisonTests.il @@ -0,0 +1,39 @@ +// 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. + +.assembly extern System.Runtime +{ +} + +.assembly ComparisonTests +{ +} + +.class public auto ansi beforefieldinit ComparisonTestsType + extends [System.Runtime]System.Object +{ + .method static public hidebysig void Comparison.NullWithObjRef_Valid() cil managed + { + .locals init ( + class [System.Runtime]System.Object V_0 + ) + ldnull + ldloc.0 + cgt.un + pop + ret + } + + .method static public hidebysig void Comparison.ObjRefWithNull_Valid() cil managed + { + .locals init ( + class [System.Runtime]System.Object V_0 + ) + ldloc.0 + ldnull + cgt.un + pop + ret + } +} diff --git a/external/corert/src/ILVerify/tests/ILTests/DelegateTests.il b/external/corert/src/ILVerify/tests/ILTests/DelegateTests.il new file mode 100644 index 0000000000..12ef7c57af --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILTests/DelegateTests.il @@ -0,0 +1,103 @@ +// 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. + +.assembly extern System.Runtime +{ +} + +.assembly DelegateTests +{ +} + +.class public auto ansi sealed ByteEnum + extends [System.Runtime]System.Enum +{ + .field public specialname rtspecialname uint8 value__ + .field public static literal valuetype ByteEnum A = uint8(0) + .field public static literal valuetype ByteEnum B = uint8(0) +} + +.class private auto ansi beforefieldinit DelegateTestsType + extends [System.Runtime]System.Object +{ + // assignment from Func to Func is valid + .method private hidebysig instance class [System.Runtime]System.Func`2 + DelegateAssignmentReturn_Valid(class [System.Runtime]System.Func`2 input) cil managed + { + ldarg.1 + ret + } + + // assignment from Func to Func is valid + .method private hidebysig instance class [System.Runtime]System.Func`2 + DelegateAssignmentParameter_Valid(class [System.Runtime]System.Func`2 input) cil managed + { + ldarg.1 + ret + } + + // assignment from Func to Func is invalid + .method private hidebysig instance class [System.Runtime]System.Func`2 + DelegateAssignmentParameter_Invalid_StackUnexpected(class [System.Runtime]System.Func`2 input) cil managed + { + ldarg.1 + ret + } + + // assignment from Func to Func is invalid + .method private hidebysig instance class [System.Runtime]System.Func`2 + DelegateAssignment_Invalid_StackUnexpected(class [System.Runtime]System.Func`2 input) cil managed + { + ldarg.1 + ret + } + + // assignment from Func to Func is invalid + .method private hidebysig instance class [System.Runtime]System.Func`1 + AssignIntFuncToByteFunc_Invalid_StackUnexpected(class [System.Runtime]System.Func`2 input) cil managed + { + ldarg.1 + ret + } + + // assignment from Func to Func is invalid + .method private hidebysig instance class [System.Runtime]System.Func`1 + AssignByteFuncToIntFunc_Invalid_StackUnexpected(class [System.Runtime]System.Func`1 input) cil managed + { + ldarg.1 + ret + } + + // assignment from Func to Func is invalid + .method private hidebysig instance class [System.Runtime]System.Func`1 + AssignByteActionToIntAction_Invalid_StackUnexpected(class [System.Runtime]System.Func`1 input) cil managed + { + ldarg.1 + ret + } + + // assignment from Action to Action is invalid + .method private hidebysig instance class [System.Runtime]System.Action`1 + AssignByteActionToIntAction_Invalid_StackUnexpected(class [System.Runtime]System.Action`1 input) cil managed + { + ldarg.1 + ret + } + + // assignment from Action to Action is invalid + .method private hidebysig instance class [System.Runtime]System.Action`1 + AssignByteActionToByteEnumAction_Invalid_StackUnexpected(class [System.Runtime]System.Action`1 input) cil managed + { + ldarg.1 + ret + } + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } +} diff --git a/external/corert/src/ILVerify/tests/ILTests/ExceptionRegionTests.il b/external/corert/src/ILVerify/tests/ILTests/ExceptionRegionTests.il new file mode 100644 index 0000000000..379d25ffc0 --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILTests/ExceptionRegionTests.il @@ -0,0 +1,737 @@ +// 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. + +.assembly extern System.Runtime +{ +} + +.assembly ExceptionRegionTests +{ +} + +.class public sequential ansi sealed beforefieldinit ExceptionRegionTests + extends [System.Runtime]System.Object +{ + .method public instance void ExceptionRegion.NestedTryFinally_Valid() cil managed + { + .try + { + nop + leave EndTryCatch + } + finally + { + .try + { + nop + leave EndFinally + } + catch [System.Runtime]System.Exception + { + nop + leave EndFinally + } + EndFinally: + endfinally + } + EndTryCatch: + ret + } + + .method public instance void Leave.ToSameFilter_Valid() cil managed + { + .try + { + leave MethodEnd + } + filter + { + pop + leave SameFilter + + SameFilter: + ldc.i4.0 + endfilter + } + { + pop + leave MethodEnd + } + MethodEnd: + ret + } + + .method public instance void Leave.ToOtherFilter_Invalid_LeaveOutOfFilter.LeaveIntoFilter() cil managed + { + .try + { + leave AfterTry + } + filter + { + pop + leave OtherFilter + + ldc.i4.0 + endfilter + } + { + pop + leave MethodEnd + } + AfterTry: + nop + + .try + { + leave MethodEnd + } + filter + { + pop + + OtherFilter: + ldc.i4.0 + endfilter + } + { + pop + leave MethodEnd + } + MethodEnd: + ret + } + + .method public instance void Leave.OutOfFilter_Invalid_LeaveOutOfFilter() cil managed + { + .try + { + leave MethodEnd + } + filter + { + pop + leave MethodEnd + + ldc.i4.0 + endfilter + } + { + pop + leave MethodEnd + } + MethodEnd: + ret + } + + .method public instance void Leave.ToSameFault_Valid() cil managed + { + .try + { + leave MethodEnd + } + fault + { + leave SameFault + + SameFault: + endfault + } + MethodEnd: + ret + } + + .method public instance void Leave.ToSameFinally_Valid() cil managed + { + .try + { + leave MethodEnd + } + finally + { + leave SameFinally + + SameFinally: + endfinally + } + MethodEnd: + ret + } + + .method public instance void Leave.ToOtherFinally_Invalid_LeaveOutOfFinally.LeaveIntoHandler() cil managed + { + .try + { + leave AfterTry + } + finally + { + leave OtherFinally + endfinally + } + AfterTry: + nop + + .try + { + leave MethodEnd + } + finally + { + OtherFinally: + endfinally + } + MethodEnd: + ret + } + + .method public instance void Leave.ToOtherFault_Invalid_LeaveOutOfFault.LeaveIntoFault.LeaveIntoHandler() cil managed + { + .try + { + leave AfterTry + } + fault + { + leave OtherFault + endfault + } + AfterTry: + nop + + .try + { + leave MethodEnd + } + fault + { + OtherFault: + endfault + } + MethodEnd: + ret + } + + .method public instance void Leave.OutOfFinally_Invalid_LeaveOutOfFinally() cil managed + { + .try + { + leave MethodEnd + } + finally + { + leave MethodEnd + endfinally + } + MethodEnd: + ret + } + + .method public instance void Leave.OutOfFault_Invalid_LeaveOutOfFault() cil managed + { + .try + { + leave MethodEnd + } + fault + { + leave MethodEnd + endfault + } + MethodEnd: + ret + } + + .method public instance void Leave.ToSameTry_Valid() cil managed + { + .try + { + leave SameTry + + SameTry: + leave MethodEnd + } + finally + { + endfinally + } + MethodEnd: + ret + } + + .method public instance void Leave.ToEnclosingTry_Valid() cil managed + { + .try + { + .try + { + leave EnclosingTry + } + finally + { + endfinally + } + EnclosingTry: + leave MethodEnd + } + finally + { + endfinally + } + MethodEnd: + ret + } + + .method public instance void Leave.ToFirstInstrOfDisjointTry_Valid() cil managed + { + .try + { + leave DisjointTry + } + finally + { + endfinally + } + + .try + { + DisjointTry: + leave MethodEnd + } + finally + { + endfinally + } + MethodEnd: + ret + } + + .method public instance void Leave.ToFirstInstrOfNestedDisjointTry_Valid() cil managed + { + .try + { + leave DisjointTry + } + finally + { + endfinally + } + + .try + { + .try + { + DisjointTry: + leave MethodEnd + } + finally + { + endfinally + } + } + finally + { + endfinally + } + MethodEnd: + ret + } + + .method public instance void Leave.IntoOtherTry_Invalid_LeaveIntoTry() cil managed + { + .try + { + leave OtherTry + } + finally + { + endfinally + } + + .try + { + nop + OtherTry: + leave MethodEnd + } + finally + { + endfinally + } + MethodEnd: + ret + } + + .method public instance void Leave.ToSecondInstrOfNestedDisjointTry_Invalid_LeaveIntoTry() cil managed + { + .try + { + leave DisjointTry + } + finally + { + endfinally + } + + .try + { + nop + .try + { + DisjointTry: + leave MethodEnd + } + finally + { + endfinally + } + } + finally + { + endfinally + } + MethodEnd: + ret + } + + .method public instance void Leave.ToFirstInstrOfEnclosedTry_Valid() cil managed + { + .try + { + leave EnclosedTry + + .try + { + EnclosedTry: + leave TryEnd + } + finally + { + endfinally + } + TryEnd: + leave MethodEnd + } + finally + { + endfinally + } + MethodEnd: + ret + } + + .method public instance void Leave.FromCatchIntoAssociatedTry_Valid() cil managed + { + .try + { + TryStart: + leave MethodEnd + } + catch [System.Runtime]System.Object + { + leave TryStart + } + MethodEnd: + ret + } + + .method public instance void Leave.ToSameCatch_Valid() cil managed + { + .try + { + leave MethodEnd + } + catch [System.Runtime]System.Object + { + leave SameCatch + + SameCatch: + leave MethodEnd + } + MethodEnd: + ret + } + + .method public instance void Leave.OutOfCatch_Valid() cil managed + { + .try + { + leave MethodEnd + } + catch [System.Runtime]System.Object + { + leave MethodEnd + } + MethodEnd: + ret + } + + .method public instance void Leave.CatchToFirstInstrOfDisjointTry_Valid() cil managed + { + .try + { + leave AfterTry + } + catch [System.Runtime]System.Object + { + leave DisjointTry + } + AfterTry: + nop + + .try + { + DisjointTry: + leave MethodEnd + } + finally + { + endfinally + } + MethodEnd: + ret + } + + .method public instance void Leave.CatchToFirstInstrOfNestedTry_Valid() cil managed + { + .try + { + leave MethodEnd + } + catch [System.Runtime]System.Object + { + leave NestedTry + + .try + { + NestedTry: + leave MethodEnd + } + finally + { + endfinally + } + + leave MethodEnd + } + + MethodEnd: + ret + } + + .method public instance void Leave.CatchToSecondInstrOfNestedTry_Invalid_LeaveIntoTry() cil managed + { + .try + { + leave MethodEnd + } + catch [System.Runtime]System.Object + { + leave NestedTry + + .try + { + nop + NestedTry: + leave MethodEnd + } + finally + { + endfinally + } + + leave MethodEnd + } + + MethodEnd: + ret + } + + .method public instance void Leave.TryToEnclosingCatch_Valid() cil managed + { + .try + { + leave MethodEnd + } + catch [System.Runtime]System.Object + { + .try + { + leave EnclosingCatch + } + finally + { + endfinally + } + + EnclosingCatch: + leave MethodEnd + } + + MethodEnd: + ret + } + + .method public instance void Leave.CatchToEnclosingTry_Valid() cil managed + { + .try + { + .try + { + leave MethodEnd + } + catch [System.Runtime]System.Object + { + leave EnclosingTry + } + + EnclosingTry: + leave MethodEnd + } + filter + { + pop + ldc.i4.0 + endfilter + } + { + leave MethodEnd + } + + MethodEnd: + ret + } + + .method public instance void Leave.FromOutsideIntoFilter_Invalid_LeaveIntoFilter() cil managed + { + leave IntoFilter + + .try + { + leave MethodEnd + } + filter + { + pop + IntoFilter: + ldc.i4.0 + endfilter + } + { + leave MethodEnd + } + + MethodEnd: + ret + } + + .method public instance void Leave.FromOutsideIntoHandler_Invalid_LeaveIntoHandler() cil managed + { + leave IntoHandler + + .try + { + leave MethodEnd + } + catch [System.Runtime]System.Object + { + pop + IntoHandler: + leave MethodEnd + } + + MethodEnd: + ret + } + + .method public instance void Leave.FromOutsideIntoTry_Invalid_LeaveIntoTry() cil managed + { + leave IntoTry + + .try + { + nop + IntoTry: + leave MethodEnd + } + finally + { + endfinally + } + + MethodEnd: + ret + } + + .method public instance void Leave.FromNestedTryToDisjointTry_Valid() cil managed + { + .try + { + .try + { + leave DisjointTry + } + finally + { + endfinally + } + leave AfterTry + } + finally + { + endfinally + } + AfterTry: + nop + + .try + { + DisjointTry: + leave MethodEnd + } + finally + { + endfinally + } + + MethodEnd: + ret + } + + .method public instance void Leave.FromNestedTryToDisjointTryBackward_Valid() cil managed + { + .try + { + DisjointTry: + leave AfterTry + } + finally + { + endfinally + } + AfterTry: + nop + + .try + { + .try + { + leave DisjointTry + } + finally + { + endfinally + } + leave MethodEnd + } + finally + { + endfinally + } + + MethodEnd: + ret + } +} + diff --git a/external/corert/src/ILVerify/tests/ILTests/FieldTests.il b/external/corert/src/ILVerify/tests/ILTests/FieldTests.il new file mode 100644 index 0000000000..816f6fee7f --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILTests/FieldTests.il @@ -0,0 +1,160 @@ +// 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. + +.assembly extern System.Runtime +{ +} + +.assembly FieldTests +{ +} + +.class private auto ansi beforefieldinit ConstrainedClass`1<([System.Runtime]System.Collections.IEnumerable) T> + extends [System.Runtime]System.Object +{ + .field public static int32 StaticField + + .method public hidebysig specialname rtspecialname instance void .ctor () cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } +} + +.class public auto ansi beforefieldinit FieldTestsType + extends [System.Runtime]System.Object +{ + .field public initonly int32 InstanceInitonlyField + .field public static initonly int32 StaticInitonlyField + + .method public instance void Stsfld.UnsatisfiedParentConstraints_Invalid_UnsatisfiedFieldParentInst() cil managed + { + ldc.i4.0 + stsfld int32 class ConstrainedClass`1::StaticField + ret + } + + .method public instance void Stfld.InitonlyFieldOutsideCtor_Invalid_InitOnly() cil managed + { + ldarg.0 + ldc.i4.0 + stfld int32 FieldTestsType::InstanceInitonlyField + ret + } + + .method public instance void Ldflda.InitonlyFieldOutsideCtor_Invalid_InitOnly() cil managed + { + ldarg.0 + ldflda int32 FieldTestsType::InstanceInitonlyField + pop + ret + } + + .method public hidebysig instance void 'special.StoreInitonlyField..ctor_Valid'() cil managed { ret } + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ldarg.0 + ldc.i4.0 + stfld int32 FieldTestsType::InstanceInitonlyField + ret + } + + .method public hidebysig instance void 'special.LoadAddrInitonlyField..ctor_Valid'(int32) cil managed { ret } + .method public hidebysig specialname rtspecialname instance void .ctor(int32) cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ldarg.0 + ldflda int32 FieldTestsType::InstanceInitonlyField + pop + ret + } + + .method public hidebysig instance void 'special.LoadAddrInitonlyField..ctor_Valid'(int64) cil managed { ret } + .method public hidebysig specialname rtspecialname instance void .ctor(int64) cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ldarg.0 + ldflda int32 FieldTestsType::InstanceInitonlyField + pop + ret + } + + .method public hidebysig instance void 'special.StoreInitonlyFieldOtherType..ctor_Invalid_InitOnly'(class OtherType c) cil managed { ret } + .method public hidebysig specialname rtspecialname instance void .ctor(class OtherType c) cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ldarg.1 + ldc.i4.0 + stfld int32 OtherType::InstanceInitonlyField + ret + } + + .method public hidebysig instance void 'special.StoreInitonlyFieldOtherInstance..ctor_Invalid_InitOnly'(class FieldTestsType c) cil managed { ret } + .method public hidebysig specialname rtspecialname instance void .ctor(class FieldTestsType c) cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ldarg.1 + ldc.i4.0 + stfld int32 FieldTestsType::InstanceInitonlyField + ret + } + + .method public hidebysig instance void 'special.StsfldInitonlyInCtor..ctor_Invalid_InitOnly'(bool) cil managed { ret } + .method public hidebysig specialname rtspecialname instance void .ctor(bool) cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ldc.i4.0 + stsfld int32 FieldTestsType::StaticInitonlyField + ret + } + + .method public hidebysig instance void 'special.LdsfldInitonlyInCtor..ctor_Invalid_InitOnly'(char) cil managed { ret } + .method public hidebysig specialname rtspecialname instance void .ctor(char) cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ldsflda int32 FieldTestsType::StaticInitonlyField + pop + ret + } + + .method public hidebysig instance void 'special.LdsfldStslfdInitonlyCctor..cctor_Valid'() cil managed { ret } + .method public hidebysig specialname rtspecialname instance void .cctor() cil managed + { + ldsflda int32 FieldTestsType::StaticInitonlyField + pop + ldc.i4.0 + stsfld int32 FieldTestsType::StaticInitonlyField + ret + } +} + +.class public auto ansi beforefieldinit OtherType + extends [System.Runtime]System.Object +{ + .field public static class OtherType Instance + + .field public initonly int32 InstanceInitonlyField + .field public static initonly int32 StaticInitonlyField + + .method public hidebysig instance void 'special.LdfldStlfdInitonlyCctor..cctor_Invalid_InitOnly.InitOnly'() cil managed { ret } + .method public hidebysig specialname rtspecialname instance void .cctor() cil managed + { + ldsfld class OtherType OtherType::Instance + ldflda int32 OtherType::InstanceInitonlyField + pop + ldsfld class OtherType OtherType::Instance + ldc.i4.0 + stfld int32 OtherType::InstanceInitonlyField + ret + } +} diff --git a/external/corert/src/ILVerify/tests/ILTests/FtnTests.il b/external/corert/src/ILVerify/tests/ILTests/FtnTests.il new file mode 100644 index 0000000000..c3a0aec451 --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILTests/FtnTests.il @@ -0,0 +1,498 @@ +// 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. + +.assembly extern System.Runtime +{ +} + +.assembly FtnTests +{ +} + +// Generic test class with class constraint +.class public auto ansi beforefieldinit GenericTestClass`1 + extends [System.Runtime]System.Object +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } + + .method public hidebysig instance void InstanceMethod() cil managed + { + ret + } + + .method public hidebysig static void StaticMethod() cil managed + { + ret + } +} + +// Generic test class with default ctor constraint +.class public auto ansi beforefieldinit GenericCtor`1<.ctor T> + extends [System.Runtime]System.Object +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } +} + +// Generic test class with default valuetype constraint +.class public auto ansi beforefieldinit GenericValueType`1 + extends [System.Runtime]System.Object +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } +} + +// Test class with generic methods with constraints and private default ctor +.class public auto ansi beforefieldinit TestClass + extends [System.Runtime]System.Object +{ + .method private hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } + + .method private hidebysig specialname rtspecialname instance void .ctor(int32 i) cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } + + .method public hidebysig instance void InstanceMethodRefConstr() cil managed + { + ret + } + + .method public hidebysig instance void InstanceMethod() cil managed + { + ret + } + + .method public hidebysig newslot virtual instance void VirtInstanceMethod() cil managed + { + ret + } + + .method public hidebysig static void StaticMethod() cil managed + { + ret + } + + .method public hidebysig static int32 StaticIntMethod() cil managed + { + ldc.i4.0 + ret + } + + .method public hidebysig static void StaticMethodRefConstr() cil managed + { + ret + } +} + +// Type containing test methods for delegate-assignment +.class public auto ansi beforefieldinit TestMethodsType + extends [System.Runtime]System.Object +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } + + .method public hidebysig static uint8 ByteReturnMethod() cil managed + { + ldc.i4.0 + ret + } + + .method public hidebysig static valuetype ByteEnum ByteEnumReturnMethod() cil managed + { + ldc.i4.0 + box ByteEnum + ret + } + + .method public hidebysig static int32 IntReturnMethod() cil managed + { + ldc.i4.0 + ret + } + + .method public hidebysig static string StringReturnMethod() cil managed + { + ldnull + ret + } + + .method public hidebysig static object ObjectReturnMethod() cil managed + { + ldnull + ret + } + + .method public hidebysig static void ByteParamMethod(uint8 param) cil managed + { + ret + } + + .method public hidebysig static void ByteEnumParamMethod(valuetype ByteEnum param) cil managed + { + ret + } +} + +.class public auto ansi sealed ByteEnum + extends [System.Runtime]System.Enum +{ + .field public specialname rtspecialname uint8 value__ + .field public static literal valuetype ByteEnum A = uint8(0) + .field public static literal valuetype ByteEnum B = uint8(0) +} + +.class public auto ansi beforefieldinit FtnTestsType + extends [System.Runtime]System.Object +{ + .method static public hidebysig void LdFtn.StaticClassRefConstraint_Valid() cil managed + { + // var a = new System.Action(GenericTestClass.StaticMethod); + // a(); + + ldnull + ldftn void class GenericTestClass`1::StaticMethod() + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + callvirt instance void [System.Runtime]System.Action::Invoke() + ret + } + + .method static public hidebysig void LdFtn.InstanceClassRefConstraint_Valid(class GenericTestClass`1 c) cil managed + { + // (GenericTestClass c) + // var a = new System.Action(c.InstanceMethod); + // a(); + + ldarg.0 + ldftn instance void class GenericTestClass`1::InstanceMethod() + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + callvirt instance void [System.Runtime]System.Action::Invoke() + ret + } + + .method static public hidebysig void LdFtn.IntForStaticClassRefConstraint_Invalid_UnsatisfiedMethodParentInst() cil managed + { + // var a = new System.Action(GenericTestClass.StaticMethod); + // a(); + + ldnull + ldftn void class GenericTestClass`1::StaticMethod() + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + callvirt instance void [System.Runtime]System.Action::Invoke() + ret + } + + .method static public hidebysig void LdFtn.IntForStaticMethodRefConstraint_Invalid_UnsatisfiedMethodInst() cil managed + { + // var a = new System.Action(TestClass.StaticMethod); + // a(); + + ldnull + ldftn void TestClass::StaticMethodRefConstr() + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + callvirt instance void [System.Runtime]System.Action::Invoke() + ret + } + + .method static public hidebysig void LdvirtFtn.InstanceMethod_Valid(class TestClass c) cil managed + { + // (TestClass c) + // var a = new System.Action(c.InstanceMethod); + // a(); + + ldarg.0 + dup + ldvirtftn instance void TestClass::InstanceMethod() + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + callvirt instance void [System.Runtime]System.Action::Invoke() + ret + } + + .method static public hidebysig void LdvirtFtn.VirtInstanceMethod_Valid(class TestClass c) cil managed + { + // (TestClass c) + // var a = new System.Action(c.InstanceMethod); + // a(); + + ldarg.0 + dup + ldvirtftn instance void TestClass::VirtInstanceMethod() + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + callvirt instance void [System.Runtime]System.Action::Invoke() + ret + } + + .method static public hidebysig void LdvirtFtn.ValueTypeInstanceMethod_Valid(int32 i) cil managed + { + // (int i) + // var f = new System.Func(i.CompareTo); + // f(0); + + ldarg.0 + box [System.Runtime]System.Int32 + dup + ldvirtftn instance int32 [System.Runtime]System.Int32::CompareTo(int32) + newobj instance void class [System.Runtime]System.Func`2::.ctor(object, native int) + ldc.i4.0 + callvirt instance !1 class [System.Runtime]System.Func`2::Invoke(!0) + pop + ret + } + + .method static public hidebysig void LdvirtFtn.StaticMethod_Invalid_LdvirtftnOnStatic() cil managed + { + ldnull + dup + ldvirtftn void TestClass::StaticMethod() + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + callvirt instance void [System.Runtime]System.Action::Invoke() + ret + } + + .method static public hidebysig void LdvirtFtn.ValueTypeNoBox_Invalid_StackObjRef.StackUnexpected.StackUnexpected.DelegateCtorSigO(int32 i) cil managed + { + // (int i) + // var f = new System.Func(i.CompareTo); + // f(0); + + ldarg.0 + dup + ldvirtftn instance int32 [System.Runtime]System.Int32::CompareTo(int32) + newobj instance void class [System.Runtime]System.Func`2::.ctor(object, native int) + ldc.i4.0 + callvirt instance !1 class [System.Runtime]System.Func`2::Invoke(!0) + pop + ret + } + + .method static public hidebysig void LdvirtFtn.ValueTypeWrongBox_Invalid_StackUnexpected.DelegateCtorSigO.DelegatePattern(int32 i) cil managed + { + // (int i) + // var f = new System.Func(i.CompareTo); + // f(0); + + ldarg.0 + dup + box [System.Runtime]System.Int32 + ldvirtftn instance int32 [System.Runtime]System.Int32::CompareTo(int32) + newobj instance void class [System.Runtime]System.Func`2::.ctor(object, native int) + ldc.i4.0 + callvirt instance !1 class [System.Runtime]System.Func`2::Invoke(!0) + pop + ret + } + + .method static public hidebysig void LdvirtFtn.ObjectForTestClassInstance_Invalid_StackUnexpected(object c) cil managed + { + // (object c) + // var a = new System.Action(c.InstanceMethod); + + ldarg.0 + dup + ldvirtftn instance void TestClass::InstanceMethod() + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + pop + ret + } + + .method static public hidebysig void NewObj.RefGenericForRefConstraint_Valid() cil managed + { + // var c = new GenericTestClass`1(); + + newobj instance void class GenericTestClass`1::.ctor() + pop + ret + } + + .method static public hidebysig void NewObj.IntForRefConstraint_Invalid_UnsatisfiedMethodParentInst() cil managed + { + // GenericTestClass c = new GenericTestClass(); + + newobj instance void class GenericTestClass`1::.ctor() + pop + ret + } + + .method static public hidebysig void LdFtn.NopInDelegatePattern_Invalid_DelegatePattern() cil managed + { + // var a = new System.Action(TestClass.StaticMethod); + // a(); + + ldnull + ldftn void TestClass::StaticMethod() + nop + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + callvirt instance void [System.Runtime]System.Action::Invoke() + ret + } + + .method static public hidebysig void LdvirtFtn.NopInDelegatePattern_Invalid_DelegatePattern(class TestClass c) cil managed + { + // (TestClass c) + // var a = new System.Action(c.InstanceMethod); + // a(); + + ldarg.0 + dup + nop + ldvirtftn instance void TestClass::InstanceMethod() + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + callvirt instance void [System.Runtime]System.Action::Invoke() + ret + } + + .method static public hidebysig void LdFtn.BranchIntoDelegatePattern_Invalid_DelegatePattern() cil managed + { + ldnull + ldftn void TestClass::StaticMethod() + br lbl_newobj + + ldnull + ldftn void TestClass::StaticMethod() + + lbl_newobj: + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + callvirt instance void [System.Runtime]System.Action::Invoke() + ret + } + + .method static public hidebysig void LdvirtFtn.BranchIntoDelegatePattern_Invalid_DelegatePattern(class TestClass t) cil managed + { + ldarg.0 + dup + br lbl_ldvirt + + ldarg.0 + dup + + lbl_ldvirt: + ldvirtftn instance void TestClass::InstanceMethod() + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + callvirt instance void [System.Runtime]System.Action::Invoke() + ret + } + + .method static public hidebysig void LdFtn.DelegateMissingArgument_Invalid_DelegateCtor() cil managed + { + ldnull + ldftn void TestClass::StaticMethod() + newobj instance void class [System.Runtime]System.Action`1::.ctor(object, native int) + ldc.i4.0 + callvirt instance void class [System.Runtime]System.Action`1::Invoke(!0) + ret + } + + .method static public hidebysig void LdFtn.DelegateWrongReturnType_Invalid_DelegateCtor() cil managed + { + ldnull + ldftn int32 TestClass::StaticIntMethod() + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + callvirt instance void [System.Runtime]System.Action::Invoke() + ret + } + + .method static public hidebysig void LdFtn.VirtualMethodNonThisPtr_Invalid_LdftnNonFinalVirtual(class TestClass c) cil managed + { + ldarg.0 + ldftn instance void TestClass::VirtInstanceMethod() + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + callvirt instance void [System.Runtime]System.Action::Invoke() + ret + } + + // creating a Func from a string return method is valid + .method private hidebysig instance void ObjectFuncFromStringMethod_Valid() cil managed + { + ldnull + ldftn string TestMethodsType::StringReturnMethod() + newobj instance void class [System.Runtime]System.Func`1::.ctor(object, native int) + pop + ret + } + + // creating a Func from a object return method is invalid + .method private hidebysig instance void StringFuncFromObjectMethod_Invalid_DelegateCtor() cil managed + { + ldnull + ldftn object TestMethodsType::ObjectReturnMethod() + newobj instance void class [System.Runtime]System.Func`1::.ctor(object, native int) + pop + ret + } + + // creating a Func from an int return method is invalid + .method private hidebysig instance void ByteFuncFromIntMethod_Invalid_DelegateCtor() cil managed + { + ldnull + ldftn int32 TestMethodsType::IntReturnMethod() + newobj instance void class [System.Runtime]System.Func`1::.ctor(object, native int) + pop + ret + } + + // creating a Func from a byte return method is invalid + .method private hidebysig instance void IntFuncFromByteMethod_Invalid_DelegateCtor() cil managed + { + ldnull + ldftn uint8 TestMethodsType::ByteReturnMethod() + newobj instance void class [System.Runtime]System.Func`1::.ctor(object, native int) + pop + ret + } + + // creating a Func from a ByteEnum return method is invalid + .method private hidebysig instance void IntFuncFromByteEnumMethod_Invalid_DelegateCtor() cil managed + { + ldnull + ldftn valuetype ByteEnum TestMethodsType::ByteEnumReturnMethod() + newobj instance void class [System.Runtime]System.Func`1::.ctor(object, native int) + pop + ret + } + + // creating an Action from a method with byte parameter is invalid + .method private hidebysig instance void IntActionFromByteMethod_Invalid_DelegateCtor() cil managed + { + ldnull + ldftn void TestMethodsType::ByteParamMethod(uint8) + newobj instance void class [System.Runtime]System.Action`1::.ctor(object, native int) + pop + ret + } + + // creating an Action from a method with an enum (with underlying type byte) as parameter is invalid + .method private hidebysig instance void ByteActionFromByteEnum_Invalid_DelegateCtor() cil managed + { + ldnull + ldftn void TestMethodsType::ByteEnumParamMethod(valuetype ByteEnum) + newobj instance void class [System.Runtime]System.Action`1::.ctor(object, native int) + pop + ret + } +} diff --git a/external/corert/src/ILVerify/tests/ILTests/LoadStoreIndirectTests.il b/external/corert/src/ILVerify/tests/ILTests/LoadStoreIndirectTests.il new file mode 100644 index 0000000000..ecf0545c55 --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILTests/LoadStoreIndirectTests.il @@ -0,0 +1,239 @@ +// 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. + +.assembly extern System.Runtime +{ +} + +.assembly LoadStoreIndirectTests +{ +} + +.class public auto ansi beforefieldinit LoadStoreIndirectTestsType + extends [System.Runtime]System.Object +{ + .method static public hidebysig void LoadIndirectRef.FromStringRef_Valid(string&) cil managed + { + ldarg.0 + ldind.ref + pop + ret + } + + .method static public hidebysig void LoadIndirect.FromInt32Ref_Valid(int32&) cil managed + { + ldarg.0 + ldind.i4 + pop + ret + } + + .method static public hidebysig void LoadIndirect.FromInt64Ref_Invalid_StackUnexpected(int64&) cil managed + { + ldarg.0 + ldind.i4 + pop + ret + } + + .method static public hidebysig void LoadIndirectRef.FromInt64Ref_Invalid_StackUnexpected(int64&) cil managed + { + ldarg.0 + ldind.ref + pop + ret + } + + .method static public hidebysig void LoadIndirectRef.AssignRefStringToString_Valid(string&) cil managed + { + .locals init ( + string V_0) + ldarg.0 + ldind.ref + stloc.0 + ret + } + + .method static public hidebysig void StoreIndirect.AssignToInt_Valid(int32&) cil managed + { + // arg = 0; + ldarg.0 + ldc.i4.0 + stind.i4 + ret + } + + .method static public hidebysig void StoreIndirect.AssignToRefBase_Valid(object&) cil managed + { + .locals init (string V_0) + + // ref object a; + // string b; + // a = b; + + ldarg.0 + ldloc.0 + stind.ref + ret + } + + .method static public hidebysig void StoreIndirect.AssignToRefString_Invalid_StackUnexpected(string&) cil managed + { + .locals init (object V_0) + + // ref string x; + // object y; + // x = y; + + ldarg.0 + ldloc.0 + stind.ref + ret + } + + .method static public hidebysig void StoreIndirect.AssignNullToRefString_Valid(string&) cil managed + { + // ref string x; + // x = null; + + ldarg.0 + ldnull + stind.ref + ret + } + + .method static public hidebysig void StoreIndirect.AssignStringToRefString_Valid(string&) cil managed + { + // ref string x; + // string y = "a"; + // x = y; + + ldarg.0 + ldstr "a" + stind.ref + ret + } + + .method static public hidebysig void StoreObject.AssignStringToRefStringAsObject_Invalid_StackUnexpected(string&) cil managed + { + // ref string x; + // string y = "a"; + // x = y; + + ldarg.0 + ldstr "a" + stobj [System.Runtime]System.Object + ret + } + + .method static public hidebysig void StoreIndirect.AssignByteToBoolRef_Valid(bool&) cil managed + { + ldarg.0 + ldc.i4.0 + stind.i1 + ret + } + + .method static public hidebysig void StoreObject.ValidTypeToken_Valid() cil managed + { + .locals init (object V_0) + + ldloca.s V_0 + ldstr "Hello" + stobj string + ret + } + + .method static public hidebysig void StoreObject.InvalidTypeToken_Invalid_StackUnexpected() cil managed + { + .locals init (object V_0) + + ldloca.s V_0 + ldstr "Hello" + stobj [System.Runtime]System.IO.Stream + ret + } + + .method static public hidebysig void LoadIndirect.AssignInt8ToBool_Valid(bool&) cil managed + { + .locals init (bool) + + // ref bool a; + // bool b; + // b = a; + + ldarg.0 + ldind.i1 + stloc.0 + ret + } + + .method static public hidebysig void LoadIndirect.AssignUInt8ToBool_Valid(bool&) cil managed + { + .locals init (bool) + + // ref bool a; + // bool b; + // b = a; + + ldarg.0 + ldind.u1 + stloc.0 + ret + } + + .method static public hidebysig void LoadIndirect.AssignInt16ToChar_Valid(char&) cil managed + { + .locals init (char) + + // ref char a; + // char b; + // b = a; + + ldarg.0 + ldind.i2 + stloc.0 + ret + } + + .method static public hidebysig void LoadIndirect.AssignUInt16ToChar_Valid(char&) cil managed + { + .locals init (char) + + // ref char a; + // char b; + // b = a; + + ldarg.0 + ldind.u2 + stloc.0 + ret + } + + .method static public hidebysig void StoreIndirect.AssignInt32ToCharRef_Valid(char&) cil managed + { + .locals init (char) + + // ref char a; + // a = 'a'; + + ldarg.0 + ldc.i4.s 97 + stind.i2 + ret + } + + .method static public hidebysig void StoreIndirect.AssignInt32ToBoolRef_Valid(bool&) cil managed + { + .locals init (bool) + + // ref bool a; + // a = true; + + ldarg.0 + ldc.i4.1 + stind.i1 + ret + } +} diff --git a/external/corert/src/ILVerify/tests/ILTests/NewobjTests.il b/external/corert/src/ILVerify/tests/ILTests/NewobjTests.il new file mode 100644 index 0000000000..38d121c722 --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILTests/NewobjTests.il @@ -0,0 +1,46 @@ +// 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. + +.assembly extern System.Runtime +{ +} + +.assembly NewobjTests +{ +} + +.class public auto ansi abstract beforefieldinit AbstractClass +{ + .method public hidebysig newslot abstract virtual instance void AbstractMethod() cil managed + { + } +} + +.class public auto ansi beforefieldinit NewobjTestsType + extends [System.Runtime]System.Object +{ + .method public hidebysig instance void InstanceMethod() cil managed + { + ret + } + + .method public hidebysig static void StaticMethod() cil managed + { + ret + } + + .method public hidebysig instance void Newobj.InstanceMethod_Invalid_CtorExpected() cil managed + { + newobj instance void NewobjTestsType::InstanceMethod() + pop + ret + } + + .method public hidebysig instance void Newobj.AbstractMethod_Invalid_CtorSig.CtorExpected() cil managed + { + newobj instance void AbstractClass::AbstractMethod() + pop + ret + } +} diff --git a/external/corert/src/ILVerify/tests/ILTests/PrefixTests.il b/external/corert/src/ILVerify/tests/ILTests/PrefixTests.il new file mode 100644 index 0000000000..f53782d769 --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILTests/PrefixTests.il @@ -0,0 +1,185 @@ +// 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. + +.assembly extern System.Runtime +{ +} + +.assembly PrefixTests +{ +} + +.class public auto ansi beforefieldinit PrefixTestsType + extends [System.Runtime]System.Object +{ + .method static public hidebysig void StaticMethod() cil managed + { + ret + } + + .method static public hidebysig void Readonly.Ldelema_Valid() cil managed + { + .locals init (int32[] V_0) + + //int[] array = new int[2]; + ldc.i4.2 + newarr [System.Runtime]System.Int32 + stloc.0 + ldloc.0 + // array[1].ToString(); + ldc.i4.1 + readonly. + ldelema [System.Runtime]System.Int32 + call instance string [System.Runtime]System.Int32::ToString() + pop + ret + } + + .method static public hidebysig void Readonly.Ldelem_Invalid_ReadOnly() cil managed + { + .locals init (int32[] V_0) + + //int[] array = new int[2]; + ldc.i4.2 + newarr [System.Runtime]System.Int32 + stloc.0 + ldloc.0 + ldc.i4.1 + + readonly. + ldelem [System.Runtime]System.Int32 + pop + ret + } + + .method static public hidebysig void Readonly.ArrayAddress_Valid() cil managed + { + //int[,] array = new int[1, 1]; + ldc.i4.1 + ldc.i4.1 + newobj instance void int32[0..., 0...]::.ctor(int32, int32) + ldc.i4.0 + ldc.i4.0 + readonly. + call instance int32& int32[0..., 0...]::Address(int32, int32) + pop + ret + } + + .method static public hidebysig void Readonly.ArrayGet_Invalid_ReadonlyUnexpectedCallee() cil managed + { + //int[,] array = new int[1, 1]; + ldc.i4.1 + ldc.i4.1 + newobj instance void int32[0..., 0...]::.ctor(int32, int32) + ldc.i4.0 + ldc.i4.0 + readonly. + call instance int32 int32[0..., 0...]::Get(int32, int32) + pop + ret + } + + .method static public hidebysig void Prefix.UnalignedAndVolatile_Valid() cil managed + { + .locals init (int32& V_0, int32 V_1) + // ref int x; + // int y; + // y = *x; + ldloc.0 + volatile. + unaligned. 4 + ldind.i4 + stloc.1 + ret + } + + .method static public hidebysig void Tail.FromTry_Invalid_TailCallInsideER.TailRet() cil managed + { + .try + { + tail. + call void PrefixTestsType::StaticMethod() + + leave MethodEnd + } + finally + { + endfinally + } + + MethodEnd: + ret + } + + .method static public hidebysig void Tail.FollowedByRet_Valid() cil managed + { + tail. + call void PrefixTestsType::StaticMethod() + ret + } + + .method static public hidebysig void Tail.NoRet_Invalid_TailRet.MethodFallthrough() cil managed + { + tail. + call void PrefixTestsType::StaticMethod() + } + + .method static public hidebysig void Tail.DoubleCall_Invalid_TailRet() cil managed + { + tail. + call void PrefixTestsType::StaticMethod() + call void PrefixTestsType::StaticMethod() + ret + } + + .method static public hidebysig void Tail.BranchToRet_Valid() cil managed + { + ldc.i4.0 + brfalse MethodEnd + + tail. + call void PrefixTestsType::StaticMethod() + + MethodEnd: + ret + } + + .method static public hidebysig void Tail.ComplexBranchToRet_Valid() cil managed + { + br AfterTail + + TailCall: + tail. + call void PrefixTestsType::StaticMethod() + + MethodEnd: + ret + + AfterTail: + ldc.i4.0 + brfalse TailCall + + br MethodEnd + } + + .method static public hidebysig void Tail.BranchToNopRet_Invalid_TailRet() cil managed + { + ldc.i4.0 + brfalse MethodEnd + + tail. + call void PrefixTestsType::StaticMethod() + + MethodEnd: + nop + ret + } + + .method static public hidebysig void Tail.Ret_Invalid_TailCall() cil managed + { + tail. + ret + } +} diff --git a/external/corert/src/ILVerify/tests/ILTests/ReturnTests.il b/external/corert/src/ILVerify/tests/ILTests/ReturnTests.il new file mode 100644 index 0000000000..4cc0b50814 --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILTests/ReturnTests.il @@ -0,0 +1,170 @@ +// 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. + +.assembly extern System.Runtime +{ +} + +.assembly ReturnTests +{ +} + +.class public sequential ansi sealed beforefieldinit Struct + extends [System.Runtime]System.ValueType +{ + .field public int32 instanceField +} + +.class public auto ansi beforefieldinit ReturnTestsType + extends [System.Runtime]System.Object +{ + .field private int32 instanceField + .field private static int32 staticField + + .field private valuetype Struct instanceStruct + + .method public hidebysig instance void Return.Void_Valid() cil managed + { + ldarg.0 + pop + ret + } + + .method public hidebysig instance void Return.VoidNonEmptyStack_Invalid_ReturnVoid() cil managed + { + ldarg.0 + ret + } + + .method public hidebysig instance int32 Return.Int32_Valid() cil managed + { + ldc.i4.0 + ret + } + + .method public hidebysig instance int32 Return.Int32EmptyStack_Invalid_ReturnMissing() cil managed + { + ret + } + + .method public hidebysig instance int32 Return.Int32StackLeft_Invalid_ReturnEmpty() cil managed + { + ldc.i4.0 + ldc.i4.0 + ret + } + + .method public hidebysig instance void Return.FromTry_Invalid_ReturnFromTry() cil managed + { + .try + { + ret + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_ret + } + + lbl_ret: + ret + } + + .method public hidebysig instance void Return.FromCatch_Invalid_ReturnFromHandler() cil managed + { + .try + { + leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + pop + ret + } + + lbl_ret: + ret + } + + .method public hidebysig instance void Return.FromFilter_Invalid_ReturnFromFilter() cil managed + { + .try + { + leave.s lbl_ret + } + filter + { + pop + ret + + endfilter + } + { + pop + leave.s lbl_ret + } + + lbl_ret: + ret + } + + .method public hidebysig instance int32 Return.ObjectForInt32_Invalid_StackUnexpected(object o) cil managed + { + ldarg.1 + ret + } + + .method public hidebysig instance int32& Return.LocalArgByRef_Invalid_ReturnPtrToStack(int32 i) cil managed + { + ldarga.s i + ret + } + + .method public hidebysig instance int32& Return.InstanceFieldByRef_Valid(int32 i) cil managed + { + ldarg.0 + ldflda int32 ReturnTestsType::instanceField + ret + } + + .method public hidebysig instance int32& Return.StaticFieldByRef_Valid(int32 i) cil managed + { + ldsflda int32 ReturnTestsType::staticField + ret + } + + .method public hidebysig instance int32& Return.LocalStructFieldByRef_Invalid_ReturnPtrToStack(valuetype Struct s) cil managed + { + ldarg.1 + ldflda int32 Struct::instanceField + ret + } + + .method public hidebysig instance int32& Return.FieldStructFieldByRef_Valid() cil managed + { + ldarg.0 + ldflda valuetype Struct ReturnTestsType::instanceStruct + ldflda int32 Struct::instanceField + ret + } + + .method public hidebysig instance void MissingReturn_Invalid_MethodFallthrough() cil managed + { + ldarg.0 + } + + .method public hidebysig instance void 'special.ReturnAfterBaseCtor..ctor_Valid'() cil managed { ret } + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } + + .method public hidebysig instance void 'special.ReturnBeforeBaseCtor..ctor_Invalid_ThisUninitReturn'(int32) cil managed { ret } + .method public hidebysig specialname rtspecialname instance void .ctor(int32) cil managed + { + ret + } +} diff --git a/external/corert/src/ILVerify/tests/ILTests/ShiftTests.il b/external/corert/src/ILVerify/tests/ILTests/ShiftTests.il new file mode 100644 index 0000000000..d0b7f84849 --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILTests/ShiftTests.il @@ -0,0 +1,199 @@ +// 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. + +.assembly extern System.Runtime +{ +} + +.assembly ShiftTests +{ +} + +.class public auto ansi beforefieldinit ShiftTestsType + extends [System.Runtime]System.Object +{ + .method public hidebysig instance int32 ShiftLeft.Int32ByInt32_Valid(int32 toBeShifted, int32 shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shl + ret + } + + .method public hidebysig instance int32 ShiftRight.Int32ByInt32_Valid(int32 toBeShifted, int32 shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shr + ret + } + + .method public hidebysig instance int32 ShiftRightUn.Int32ByInt32_Valid(int32 toBeShifted, int32 shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shr.un + ret + } + + .method public hidebysig instance int32 ShiftLeft.Int32ByNativeInt_Valid(int32 toBeShifted, native int shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shl + ret + } + + .method public hidebysig instance int64 ShiftLeft.Int64ByInt32_Valid(int64 toBeShifted, int32 shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shl + ret + } + + .method public hidebysig instance int64 ShiftLeft.Int64ByNativeInt_Valid(int64 toBeShifted, native int shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shl + ret + } + + .method public hidebysig instance native int ShiftLeft.NativeIntByInt32_Valid(native int toBeShifted, int32 shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shl + ret + } + + .method public hidebysig instance native int ShiftLeft.NativeIntByNativeInt_Valid(native int toBeShifted, native int shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shl + ret + } + + .method public hidebysig instance int32 ShiftLeft.Int32ByInt64_Invalid_StackUnexpected(int32 toBeShifted, int64 shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shl + ret + } + + .method public hidebysig instance int32 ShiftLeft.Int32ByFloat64_Invalid_StackUnexpected(int32 toBeShifted, float64 shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shl + ret + } + + .method public hidebysig instance int32 ShiftLeft.Int32ByInt32Ref_Invalid_StackUnexpected(int32 toBeShifted, int32& shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shl + ret + } + + .method public hidebysig instance int32 ShiftLeft.Int32ByObject_Invalid_StackUnexpected(int32 toBeShifted, object shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shl + ret + } + + .method public hidebysig instance int64 ShiftLeft.Int64ByInt64_Invalid_StackUnexpected(int64 toBeShifted, int64 shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shl + ret + } + + .method public hidebysig instance int64 ShiftLeft.Int64ByFloat64_Invalid_StackUnexpected(int64 toBeShifted, float64 shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shl + ret + } + + .method public hidebysig instance int64 ShiftLeft.Int64ByInt32Ref_Invalid_StackUnexpected(int64 toBeShifted, int32& shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shl + ret + } + + .method public hidebysig instance int64 ShiftLeft.Int64ByObject_Invalid_StackUnexpected(int64 toBeShifted, object shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shl + ret + } + + .method public hidebysig instance native int ShiftLeft.NativeIntByInt64_Invalid_StackUnexpected(native int toBeShifted, int64 shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shl + ret + } + + .method public hidebysig instance native int ShiftLeft.NativeIntByFloat64_Invalid_StackUnexpected(native int toBeShifted, float64 shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shl + ret + } + + .method public hidebysig instance native int ShiftLeft.NativeIntByInt32Ref_Invalid_StackUnexpected(native int toBeShifted, int32& shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shl + ret + } + + .method public hidebysig instance native int ShiftLeft.NativeIntByObject_Invalid_StackUnexpected(native int toBeShifted, object shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shl + ret + } + + .method public hidebysig instance float64 ShiftLeft.Float64ByInt32_Invalid_ExpectedIntegerType(float64 toBeShifted, int32 shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shl + ret + } + + .method public hidebysig instance int32& ShiftLeft.Int32RefByInt32_Invalid_ExpectedIntegerType(int32& toBeShifted, int32 shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shl + ret + } + + .method public hidebysig instance object ShiftLeft.ObjectByInt32_Invalid_ExpectedIntegerType(object toBeShifted, int32 shiftBy) cil managed + { + ldarg.1 + ldarg.2 + shl + ret + } +} diff --git a/external/corert/src/ILVerify/tests/ILTests/SwitchTests.il b/external/corert/src/ILVerify/tests/ILTests/SwitchTests.il new file mode 100644 index 0000000000..974af29b62 --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILTests/SwitchTests.il @@ -0,0 +1,275 @@ +// 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. + +.assembly extern System.Runtime +{ +} + +.assembly SwitchTests +{ +} + +.class public auto ansi beforefieldinit SwitchTestsType + extends [System.Runtime]System.Object +{ + .method static public hidebysig void Switch.Int32Value_Valid() cil managed + { + .maxstack 1 + + ldc.i4.s 10 + switch (lbl_ret, lbl_a, lbl_a, lbl_b, lbl_b) + + br.s lbl_ret + + lbl_a: nop + br.s lbl_ret + + lbl_b: nop + + lbl_ret: ret + } + + .method static public hidebysig void Switch.Int64Value_Invalid_StackUnexpected() cil managed + { + .maxstack 1 + + ldc.i8 10 + switch (lbl_ret, lbl_a, lbl_a, lbl_b, lbl_b) + + br.s lbl_ret + + lbl_a: nop + br.s lbl_ret + + lbl_b: nop + + lbl_ret: ret + } + + .method static public hidebysig void Switch.InsideTry_Valid() cil managed + { + .maxstack 1 + + .try + { + ldc.i4.s 10 + switch (lbl_leave, lbl_a, lbl_a, lbl_b, lbl_b) + + br.s lbl_leave + + lbl_a: nop + br.s lbl_leave + + lbl_b: nop + + lbl_leave: leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + nop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Switch.IntoTryStart_Valid() cil managed + { + .maxstack 1 + + ldc.i4.s 10 + switch (lbl_ret, lbl_a) + + br.s lbl_ret + + .try + { + lbl_a: nop + br.s lbl_leave + + lbl_leave: leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + nop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Switch.IntoTryMiddle_Invalid_BranchIntoTry() cil managed + { + .maxstack 1 + + ldc.i4.s 10 + switch (lbl_ret, lbl_a) + + br.s lbl_ret + + .try + { + nop + lbl_a: br.s lbl_leave + + lbl_leave: leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + nop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Switch.OutOfTry_Invalid_BranchOutOfTry() cil managed + { + .maxstack 1 + + .try + { + ldc.i4.s 10 + switch (lbl_ret, lbl_a) + + br.s lbl_leave + + lbl_a: nop + br.s lbl_leave + + lbl_leave: leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + nop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Switch.NestedOutOfTry_Invalid_BranchOutOfTry() cil managed + { + .maxstack 1 + + .try + { + .try + { + ldc.i4.s 10 + switch (lbl_leave, lbl_a, lbl_leave2) + + br.s lbl_leave + + lbl_a: nop + br.s lbl_leave + + lbl_leave: leave.s lbl_leave2 + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_leave2 + } + + lbl_leave2: leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Switch.NestedIntoTryStart_Valid() cil managed + { + .maxstack 1 + + .try + { + ldc.i4.s 10 + switch (lbl_leave, lbl_a, lbl_b) + + br.s lbl_leave + + lbl_a: nop + br.s lbl_leave + + .try + { + lbl_b: nop + leave.s lbl_leave + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_leave + } + + lbl_leave: leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Switch.NestedIntoTryMiddle_Invalid_BranchIntoTry() cil managed + { + .maxstack 1 + + .try + { + ldc.i4.s 10 + switch (lbl_leave, lbl_a, lbl_b) + + br.s lbl_leave + + lbl_a: nop + br.s lbl_leave + + .try + { + nop + lbl_b: leave.s lbl_leave + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_leave + } + + lbl_leave: leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Switch.ObjectValue_Invalid_StackUnexpected() cil managed + { + .maxstack 1 + + ldnull + switch (lbl_ret, lbl_a, lbl_a, lbl_b, lbl_b) + + br.s lbl_ret + + lbl_a: nop + br.s lbl_ret + + lbl_b: nop + + lbl_ret: ret + } +} diff --git a/external/corert/src/ILVerify/tests/ILTests/ThisStateTests.il b/external/corert/src/ILVerify/tests/ILTests/ThisStateTests.il new file mode 100644 index 0000000000..0c55f4451c --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILTests/ThisStateTests.il @@ -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. + +.assembly extern System.Runtime +{ +} + +.assembly ThisStateTests +{ +} + +.class public auto ansi beforefieldinit FieldTestsType + extends [System.Runtime]System.Object +{ + .field private int32 instanceField + .field private static int32 staticField + + .method public hidebysig instance void 'special.LoadFieldThisInit..ctor_Valid'() cil managed { ret } + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ldarg.0 + ldfld int32 FieldTestsType::instanceField + pop + ret + } + + .method public hidebysig instance void 'special.LoadFieldThisUninit..ctor_Valid'(int32) cil managed { ret } + .method public hidebysig specialname rtspecialname instance void .ctor(int32) cil managed + { + ldarg.0 + dup + ldfld int32 FieldTestsType::instanceField + pop + call instance void [System.Runtime]System.Object::.ctor() + ret + } + + .method public hidebysig instance void 'special.LoadSFieldThisUninit..ctor_Valid'(float64) cil managed { ret } + .method public hidebysig specialname rtspecialname instance void .ctor(float64) cil managed + { + ldarg.0 + ldsfld int32 FieldTestsType::staticField + pop + call instance void [System.Runtime]System.Object::.ctor() + ret + } + + .method public hidebysig instance void 'special.StoreFieldThisInit..ctor_Valid'(int64) cil managed { ret } + .method public hidebysig specialname rtspecialname instance void .ctor(int64) cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ldarg.0 + ldc.i4.0 + stfld int32 FieldTestsType::instanceField + ret + } + + .method public hidebysig instance void 'special.StoreFieldThisUninit..ctor_Valid'(int32, int32) cil managed { ret } + .method public hidebysig specialname rtspecialname instance void .ctor(int32, int32) cil managed + { + ldarg.0 + dup + ldc.i4.0 + stfld int32 FieldTestsType::instanceField + call instance void [System.Runtime]System.Object::.ctor() + ret + } + + .method public hidebysig instance void 'special.StoreSFieldThisUninit..ctor_Valid'(native int) cil managed { ret } + .method public hidebysig specialname rtspecialname instance void .ctor(native int) cil managed + { + ldc.i4.0 + stsfld int32 FieldTestsType::staticField + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } +} + +.class public auto ansi beforefieldinit ThisStateTestsType + extends [System.Runtime]System.Object +{ + .method public hidebysig instance void 'special.ThrowThisUninit..ctor_Invalid_UninitStack'() cil managed { ret } + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + throw + ret + } + + .method public hidebysig instance void 'special.CallCtorOverload..ctor_Valid'(int32) cil managed { ret } + .method public hidebysig specialname rtspecialname instance void .ctor(int32) cil managed + { + ldarg.0 + call instance void ThisStateTestsType::.ctor() + ret + } +} + +.class public auto ansi beforefieldinit LoadStoreVarTestsType + extends [System.Runtime]System.Object +{ + .method public hidebysig instance void 'special.StoreLocUninit..ctor_Invalid_StackUninit'() cil managed { ret } + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + .locals init ( + [0] int32 + ) + + ldc.i4.0 + stloc.0 + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } + + .method public hidebysig instance void 'special.StoreThisUninit..ctor_Invalid_ThisUninitStore'(int32) cil managed { ret } + .method public hidebysig specialname rtspecialname instance void .ctor(int32) cil managed + { + newobj instance void LoadStoreVarTestsType::.ctor() + starg 0 + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } + + .method public hidebysig instance void 'special.LoadAddrOfThisUninit..ctor_Invalid_ThisUninitStore'(int64) cil managed { ret } + .method public hidebysig specialname rtspecialname instance void .ctor(int64) cil managed + { + ldarga 0 + pop + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } +} diff --git a/external/corert/src/ILVerify/tests/ILTests/ValueTypeTests.il b/external/corert/src/ILVerify/tests/ILTests/ValueTypeTests.il new file mode 100644 index 0000000000..f381172039 --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILTests/ValueTypeTests.il @@ -0,0 +1,29 @@ +// 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. + +.assembly extern System.Runtime +{ +} + +.assembly ValueTypeTests +{ +} + +.class public sequential ansi sealed beforefieldinit ValueTypeTests + extends [System.Runtime]System.ValueType +{ + .size 1 + .method public instance void CallThis() cil managed + { + ret + } + + .method public instance void ValueType.CallMethod_Valid() cil managed + { + ldarg.0 + call instance void ValueTypeTests::CallThis() + ret + } +} + diff --git a/external/corert/src/ILVerify/tests/ILVerify.Tests.csproj b/external/corert/src/ILVerify/tests/ILVerify.Tests.csproj new file mode 100644 index 0000000000..94c21cd159 --- /dev/null +++ b/external/corert/src/ILVerify/tests/ILVerify.Tests.csproj @@ -0,0 +1,27 @@ + + + + netcoreapp2.0 + + false + + + + + + + + + + + + + + + + + + + + + diff --git a/external/corert/src/ILVerify/tests/TestDataLoader.cs b/external/corert/src/ILVerify/tests/TestDataLoader.cs new file mode 100644 index 0000000000..15dc8efde2 --- /dev/null +++ b/external/corert/src/ILVerify/tests/TestDataLoader.cs @@ -0,0 +1,261 @@ +// 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.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Text; +using Internal.IL; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; +using Newtonsoft.Json; +using Xunit; +using Xunit.Abstractions; + +namespace ILVerify.Tests +{ + /// + /// Parses the methods in the test assemblies. + /// It loads all assemblies from the test folder defined in TestDataLoader.TESTASSEMBLYPATH + /// This class feeds the xunit Theories + /// + class TestDataLoader + { + /// + /// The folder with the binaries which are compiled from the test driver IL Code + /// Currently the test .il code is built manually, but the plan is to have a ProjectReference and automatically build the .il files. + /// See: https://github.com/dotnet/corert/pull/3725#discussion_r118820770 + /// + public static string TESTASSEMBLYPATH = @"..\..\..\ILTests\"; + + private const string SPECIALTEST_PREFIX = "special."; + + /// + /// Returns all methods that contain valid IL code based on the following naming convention: + /// [FriendlyName]_Valid + /// The method must contain 1 '_'. The part before the '_' is a friendly name describing what the method does. + /// The word after the '_' has to be 'Valid' (Case sensitive) + /// E.g.: 'SimpleAdd_Valid' + /// + public static TheoryData GetMethodsWithValidIL() + { + var methodSelector = new Func((mparams, methodHandle) => + { + if (mparams.Length == 2 && mparams[1] == "Valid") + { + return new ValidILTestCase { MetadataToken = MetadataTokens.GetToken(methodHandle) }; + } + return null; + }); + return GetTestMethodsFromDll(methodSelector); + } + + /// + /// Returns all methods that contain valid IL code based on the following naming convention: + /// [FriendlyName]_Invalid_[ExpectedVerifierError1].[ExpectedVerifierError2]....[ExpectedVerifierErrorN] + /// The method name must contain 2 '_' characters. + /// 1. part: a friendly name + /// 2. part: must be the word 'Invalid' (Case sensitive) + /// 3. part: the expected VerifierErrors as string separated by '.'. + /// E.g.: SimpleAdd_Invalid_ExpectedNumericType + /// + public static TheoryData GetMethodsWithInvalidIL() + { + var methodSelector = new Func((mparams, methodHandle) => + { + if (mparams.Length == 3 && mparams[1] == "Invalid") + { + var expectedErrors = mparams[2].Split('.'); + var verificationErros = new List(); + + foreach (var item in expectedErrors) + { + if (Enum.TryParse(item, out VerifierError expectedError)) + { + verificationErros.Add(expectedError); + } + } + + var newItem = new InvalidILTestCase { MetadataToken = MetadataTokens.GetToken(methodHandle) }; + + if (expectedErrors.Length > 0) + { + newItem.ExpectedVerifierErrors = verificationErros; + } + + return newItem; + } + return null; + }); + return GetTestMethodsFromDll(methodSelector); + } + + private static TheoryData GetTestMethodsFromDll(Func methodSelector) + { + var retVal = new Xunit.TheoryData(); + + foreach (var testDllName in GetAllTestDlls()) + { + var testModule = GetModuleForTestAssembly(testDllName); + + foreach (var methodHandle in testModule.MetadataReader.MethodDefinitions) + { + var method = (EcmaMethod)testModule.GetMethod(methodHandle); + var methodName = method.Name; + + if (!String.IsNullOrEmpty(methodName) && methodName.Contains("_")) + { + var mparams = methodName.Split('_'); + var specialMethodHandle = HandleSpecialTests(mparams, method); + var newItem = methodSelector(mparams, specialMethodHandle); + + if (newItem != null) + { + newItem.TestName = mparams[0]; + newItem.MethodName = methodName; + newItem.ModuleName = testDllName; + + retVal.Add(newItem); + } + } + } + } + return retVal; + } + + private static MethodDefinitionHandle HandleSpecialTests(string[] methodParams, EcmaMethod method) + { + if (!methodParams[0].StartsWith(SPECIALTEST_PREFIX)) + return method.Handle; + + // Cut off special prefix + var specialParams = methodParams[0].Substring(SPECIALTEST_PREFIX.Length); + + // Get friendly name / special name + int delimiter = specialParams.IndexOf('.'); + if (delimiter < 0) + return method.Handle; + + var friendlyName = specialParams.Substring(0, delimiter); + var specialName = specialParams.Substring(delimiter + 1); + + // Substitute method parameters with friendly name + methodParams[0] = friendlyName; + + var specialMethodHandle = (EcmaMethod)method.OwningType.GetMethod(specialName, method.Signature); + return specialMethodHandle == null ? method.Handle : specialMethodHandle.Handle; + } + + private static IEnumerable GetAllTestDlls() + { + foreach (var item in System.IO.Directory.GetFiles(TESTASSEMBLYPATH)) + { + if (item.ToLower().EndsWith(".dll")) + { + yield return System.IO.Path.GetFileName(item); + } + } + } + + public static EcmaModule GetModuleForTestAssembly(string assemblyName) + { + var typeSystemContext = new SimpleTypeSystemContext(); + var coreAssembly = typeof(Object).Assembly; + var systemRuntime = Assembly.Load("System.Runtime"); + + typeSystemContext.InputFilePaths = new Dictionary + { + { coreAssembly.GetName().Name, coreAssembly.Location }, + { systemRuntime.GetName().Name, systemRuntime.Location } + }; + + typeSystemContext.ReferenceFilePaths = new Dictionary(); + foreach (var fileName in GetAllTestDlls()) + typeSystemContext.ReferenceFilePaths.Add(Path.GetFileNameWithoutExtension(fileName), TESTASSEMBLYPATH + fileName); + + typeSystemContext.SetSystemModule(typeSystemContext.GetModuleForSimpleName(coreAssembly.GetName().Name)); + return typeSystemContext.GetModuleFromPath(TESTASSEMBLYPATH + assemblyName); + } + } + + abstract class TestCase : IXunitSerializable + { + public string TestName { get; set; } + public string MethodName { get; set; } + public int MetadataToken { get; set; } + public string ModuleName { get; set; } + + public virtual void Deserialize(IXunitSerializationInfo info) + { + TestName = info.GetValue(nameof(TestName)); + MethodName = info.GetValue(nameof(MethodName)); + MetadataToken = info.GetValue(nameof(MetadataToken)); + ModuleName = info.GetValue(nameof(ModuleName)); + } + + public virtual void Serialize(IXunitSerializationInfo info) + { + info.AddValue(nameof(TestName), TestName); + info.AddValue(nameof(MethodName), MethodName); + info.AddValue(nameof(MetadataToken), MetadataToken); + info.AddValue(nameof(ModuleName), ModuleName); + } + + public override string ToString() + { + return $"[{Path.GetFileNameWithoutExtension(ModuleName)}] {TestName}"; + } + } + + /// + /// Describes a test case with a method that contains valid IL + /// + class ValidILTestCase : TestCase { } + + /// + /// Describes a test case with a method that contains invalid IL with the expected VerifierErrors + /// + class InvalidILTestCase : TestCase + { + public List ExpectedVerifierErrors { get; set; } + + public override void Serialize(IXunitSerializationInfo info) + { + base.Serialize(info); + var serializedExpectedErrors = JsonConvert.SerializeObject(ExpectedVerifierErrors); + info.AddValue(nameof(ExpectedVerifierErrors), serializedExpectedErrors); + } + + public override void Deserialize(IXunitSerializationInfo info) + { + base.Deserialize(info); + var serializedExpectedErrors = info.GetValue(nameof(ExpectedVerifierErrors)); + ExpectedVerifierErrors = JsonConvert.DeserializeObject>(serializedExpectedErrors); + } + + public override string ToString() + { + return base.ToString() + GetErrorsString(ExpectedVerifierErrors); + } + + private static string GetErrorsString(List errors) + { + if (errors == null || errors.Count <= 0) + return String.Empty; + + var errorsString = new StringBuilder(" ("); + + for (int i = 0; i < errors.Count - 1; ++i) + errorsString.Append(errors[i]).Append(", "); + + errorsString.Append(errors[errors.Count - 1]); + errorsString.Append(")"); + + return errorsString.ToString(); + } + } +} diff --git a/external/corert/src/JitInterface/src/CorInfoBase.cs.REMOVED.git-id b/external/corert/src/JitInterface/src/CorInfoBase.cs.REMOVED.git-id index 84d63a18a3..b680729bde 100644 --- a/external/corert/src/JitInterface/src/CorInfoBase.cs.REMOVED.git-id +++ b/external/corert/src/JitInterface/src/CorInfoBase.cs.REMOVED.git-id @@ -1 +1 @@ -a426b8bf90a3ea2758fa921667332be87aa69303 \ No newline at end of file +8ce1de30094ddd19b5efe44cc1711ecb871ce82e \ No newline at end of file diff --git a/external/corert/src/JitInterface/src/CorInfoHelpFunc.cs b/external/corert/src/JitInterface/src/CorInfoHelpFunc.cs index eeda3f9417..392f59f96f 100644 --- a/external/corert/src/JitInterface/src/CorInfoHelpFunc.cs +++ b/external/corert/src/JitInterface/src/CorInfoHelpFunc.cs @@ -58,6 +58,7 @@ namespace Internal.JitInterface CORINFO_HELP_NEW_MDARR, // multi-dim array helper (with or without lower bounds - dimensions passed in as vararg) CORINFO_HELP_NEW_MDARR_NONVARARG,// multi-dim array helper (with or without lower bounds - dimensions passed in as unmanaged array) CORINFO_HELP_NEWARR_1_DIRECT, // helper for any one dimensional array creation + CORINFO_HELP_NEWARR_1_R2R_DIRECT, // wrapper for R2R direct call, which extracts method table from ArrayTypeDesc CORINFO_HELP_NEWARR_1_OBJ, // optimized 1-D object arrays CORINFO_HELP_NEWARR_1_VC, // optimized 1-D value class arrays CORINFO_HELP_NEWARR_1_ALIGN8, // like VC, but aligns the array start @@ -298,6 +299,7 @@ namespace Internal.JitInterface CORINFO_HELP_THROW_ARGUMENTEXCEPTION, // throw ArgumentException CORINFO_HELP_THROW_ARGUMENTOUTOFRANGEEXCEPTION, // throw ArgumentOutOfRangeException + CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, // throw PlatformNotSupportedException CORINFO_HELP_JIT_PINVOKE_BEGIN, // Transition to preemptive mode before a P/Invoke, frame is the first argument CORINFO_HELP_JIT_PINVOKE_END, // Transition to cooperative mode after a P/Invoke, frame is the first argument diff --git a/external/corert/src/JitInterface/src/CorInfoImpl.Intrinsics.cs b/external/corert/src/JitInterface/src/CorInfoImpl.Intrinsics.cs index 10b345d41a..18bb742dd5 100644 --- a/external/corert/src/JitInterface/src/CorInfoImpl.Intrinsics.cs +++ b/external/corert/src/JitInterface/src/CorInfoImpl.Intrinsics.cs @@ -50,7 +50,7 @@ namespace Internal.JitInterface } protected override IntrinsicEntry CreateValueFromKey(IntrinsicKey key) { - Debug.Assert(false, "CreateValueFromKey not supported"); + Debug.Fail("CreateValueFromKey not supported"); return null; } protected override int GetKeyHashCode(IntrinsicKey key) @@ -79,6 +79,7 @@ namespace Internal.JitInterface table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Sin, "Sin", "System", "Math"); table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Cos, "Cos", "System", "Math"); + // table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Cbrt, "Cbrt", "System", "Math"); // not in CoreRT yet table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Sqrt, "Sqrt", "System", "Math"); table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Abs, "Abs", "System", "Math"); table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Round, "Round", "System", "Math"); @@ -87,9 +88,12 @@ namespace Internal.JitInterface table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Tan, "Tan", "System", "Math"); table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Tanh, "Tanh", "System", "Math"); table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Asin, "Asin", "System", "Math"); + // table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Asinh, "Asinh", "System", "Math"); // not in CoreRT yet table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Acos, "Acos", "System", "Math"); + // table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Acosh, "Acosh", "System", "Math"); // not in CoreRT yet table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Atan, "Atan", "System", "Math"); table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Atan2, "Atan2", "System", "Math"); + // table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Atanh, "Atanh", "System", "Math"); // not in CoreRT yet table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Log10, "Log10", "System", "Math"); table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Pow, "Pow", "System", "Math"); table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Exp, "Exp", "System", "Math"); @@ -124,22 +128,29 @@ namespace Internal.JitInterface // table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_GetManagedThreadId, "get_ManagedThreadId", "System", "Thread"); // not in .NET Core table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_ByReference_Ctor, ".ctor", "System", "ByReference`1"); table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_ByReference_Value, "get_Value", "System", "ByReference`1"); - table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Span_GetItem, "get_Item", "System", "Span`1"); // not handled - table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_ReadOnlySpan_GetItem, "get_Item", "System", "ReadOnlySpan`1"); // not handled + table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_Span_GetItem, "get_Item", "System", "Span`1"); + table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_ReadOnlySpan_GetItem, "get_Item", "System", "ReadOnlySpan`1"); + table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_GetRawHandle, "EETypePtrOf", "System", "EETypePtr"); + table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_GetRawHandle, "DefaultConstructorOf", "System", "Activator"); // If this assert fails, make sure to add the new intrinsics to the table above and update the expected count below. - Debug.Assert((int)CorInfoIntrinsics.CORINFO_INTRINSIC_Count == 49); + Debug.Assert((int)CorInfoIntrinsics.CORINFO_INTRINSIC_Count == 54); return table; } static IntrinsicHashtable s_IntrinsicHashtable = InitializeIntrinsicHashtable(); - private CorInfoIntrinsics getIntrinsicID(CORINFO_METHOD_STRUCT_* ftn, ref bool pMustExpand) + private CorInfoIntrinsics getIntrinsicID(CORINFO_METHOD_STRUCT_* ftn, byte* pMustExpand) { - pMustExpand = false; - var method = HandleToObject(ftn); + return getIntrinsicID(method, pMustExpand); + } + + private CorInfoIntrinsics getIntrinsicID(MethodDesc method, byte* pMustExpand) + { + if (pMustExpand != null) + *pMustExpand = 0; Debug.Assert(method.IsIntrinsic); @@ -200,7 +211,13 @@ namespace Internal.JitInterface case CorInfoIntrinsics.CORINFO_INTRINSIC_InitializeArray: case CorInfoIntrinsics.CORINFO_INTRINSIC_ByReference_Ctor: case CorInfoIntrinsics.CORINFO_INTRINSIC_ByReference_Value: - pMustExpand = true; + if (pMustExpand != null) + *pMustExpand = 1; + break; + + case CorInfoIntrinsics.CORINFO_INTRINSIC_GetRawHandle: + if (pMustExpand != null) + *pMustExpand = 1; break; default: diff --git a/external/corert/src/JitInterface/src/CorInfoImpl.cs.REMOVED.git-id b/external/corert/src/JitInterface/src/CorInfoImpl.cs.REMOVED.git-id index 619b1d409c..2e29f3d762 100644 --- a/external/corert/src/JitInterface/src/CorInfoImpl.cs.REMOVED.git-id +++ b/external/corert/src/JitInterface/src/CorInfoImpl.cs.REMOVED.git-id @@ -1 +1 @@ -f34bb3d07a6f1cbcc15e3407562b91ce85c4ad64 \ No newline at end of file +c06bd6b5ca3c766fa47c733d9c925a3946798fb6 \ No newline at end of file diff --git a/external/corert/src/JitInterface/src/CorInfoTypes.cs b/external/corert/src/JitInterface/src/CorInfoTypes.cs index 8da9d89980..b427f44761 100644 --- a/external/corert/src/JitInterface/src/CorInfoTypes.cs +++ b/external/corert/src/JitInterface/src/CorInfoTypes.cs @@ -137,10 +137,6 @@ namespace Internal.JitInterface mdtBaseType = 0x72000000, } - public enum SIZE_T : ulong { } // Really should be IntPtr - - public enum GSCookie : ulong { } // Really should be IntPtr - public enum HRESULT { } public unsafe struct CORINFO_SIG_INFO @@ -280,10 +276,17 @@ namespace Internal.JitInterface public byte _testForFixup; public bool testForFixup { get { return _testForFixup != 0; } set { _testForFixup = value ? (byte)1 : (byte)0; } } - public SIZE_T offset0; - public SIZE_T offset1; - public SIZE_T offset2; - public SIZE_T offset3; + public IntPtr offset0; + public IntPtr offset1; + public IntPtr offset2; + public IntPtr offset3; + + public byte _indirectFirstOffset; + public bool indirectFirstOffset { get { return _indirectFirstOffset != 0; } set { _indirectFirstOffset = value ? (byte)1 : (byte)0; } } + + public byte _indirectSecondOffset; + public bool indirectSecondOffset { get { return _indirectSecondOffset != 0; } set { _indirectSecondOffset = value ? (byte)1 : (byte)0; } } + } // Result of calling embedGenericHandle @@ -430,6 +433,7 @@ namespace Internal.JitInterface { CORINFO_INTRINSIC_Sin, CORINFO_INTRINSIC_Cos, + CORINFO_INTRINSIC_Cbrt, CORINFO_INTRINSIC_Sqrt, CORINFO_INTRINSIC_Abs, CORINFO_INTRINSIC_Round, @@ -438,9 +442,12 @@ namespace Internal.JitInterface CORINFO_INTRINSIC_Tan, CORINFO_INTRINSIC_Tanh, CORINFO_INTRINSIC_Asin, + CORINFO_INTRINSIC_Asinh, CORINFO_INTRINSIC_Acos, + CORINFO_INTRINSIC_Acosh, CORINFO_INTRINSIC_Atan, CORINFO_INTRINSIC_Atan2, + CORINFO_INTRINSIC_Atanh, CORINFO_INTRINSIC_Log10, CORINFO_INTRINSIC_Pow, CORINFO_INTRINSIC_Exp, @@ -477,6 +484,7 @@ namespace Internal.JitInterface CORINFO_INTRINSIC_ByReference_Value, CORINFO_INTRINSIC_Span_GetItem, CORINFO_INTRINSIC_ReadOnlySpan_GetItem, + CORINFO_INTRINSIC_GetRawHandle, CORINFO_INTRINSIC_Count, CORINFO_INTRINSIC_Illegal = -1, // Not a true intrinsic, @@ -597,7 +605,7 @@ namespace Internal.JitInterface CORINFO_FLG_VIRTUAL = 0x00000040, // CORINFO_FLG_UNUSED = 0x00000080, CORINFO_FLG_NATIVE = 0x00000100, - // CORINFO_FLG_UNUSED = 0x00000200, + CORINFO_FLG_INTRINSIC_TYPE = 0x00000200, // This type is marked by [Intrinsic] CORINFO_FLG_ABSTRACT = 0x00000400, CORINFO_FLG_EnC = 0x00000800, // member was added by Edit'n'Continue @@ -616,7 +624,7 @@ namespace Internal.JitInterface CORINFO_FLG_NOSECURITYWRAP = 0x04000000, // The method requires no security checks CORINFO_FLG_DONT_INLINE = 0x10000000, // The method should not be inlined CORINFO_FLG_DONT_INLINE_CALLER = 0x20000000, // The method should not be inlined, nor should its callers. It cannot be tail called. - // CORINFO_FLG_UNUSED = 0x40000000, + CORINFO_FLG_JIT_INTRINSIC = 0x40000000, // Method is a potential jit intrinsic; verify identity by name check // These are internal flags that can only be on Classes CORINFO_FLG_VALUECLASS = 0x00010000, // is the class a value class @@ -1021,6 +1029,10 @@ namespace Internal.JitInterface CORINFO_VIRTUALCALL_VTABLE }; + public enum CORINFO_VIRTUALCALL_NO_CHUNK : uint + { + Value = 0xFFFFFFFF, + } public unsafe struct CORINFO_CALL_INFO { @@ -1216,6 +1228,18 @@ namespace Internal.JitInterface // between offsets. }; + public enum ILNum + { + VARARGS_HND_ILNUM = -1, // Value for the CORINFO_VARARGS_HANDLE varNumber + RETBUF_ILNUM = -2, // Pointer to the return-buffer + TYPECTXT_ILNUM = -3, // ParamTypeArg for CORINFO_GENERICS_CTXT_FROM_PARAMTYPEARG + + UNKNOWN_ILNUM = -4, // Unknown variable + + MAX_ILNUM = -4 // Sentinal value. This should be set to the largest magnitude value in the enum + // so that the compression routines know the enum's range. + }; + public struct ILVarInfo { public uint startOffset; @@ -1232,13 +1256,13 @@ namespace Internal.JitInterface }; // The following 16 bytes come from coreclr types. See comment below. + [StructLayout(LayoutKind.Sequential)] public struct VarLoc { - public int vlType; - // The 64bit field is here to keep VarLoc 8byte aligned on Amd64. - // For x86, we need to change the VarLoc definition here. - public long A; - public int B; + IntPtr A; // vlType + padding + int B; + int C; + int D; /* Changes to the following types may require revisiting the above layout. @@ -1416,6 +1440,13 @@ namespace Internal.JitInterface CORJIT_RECOVERABLEERROR = unchecked((int)0x80000005)/*MAKE_HRESULT(SEVERITY_ERROR, FACILITY_NULL, 5)*/ }; + public enum TypeCompareState + { + MustNot = -1, // types are not equal + May = 0, // types may be equal (must test at runtime) + Must = 1, // type are equal + } + public enum CorJitFlag : uint { CORJIT_FLAG_CALL_GETJITFLAGS = 0xffffffff, // Indicates that the JIT should retrieve flags in the form of a @@ -1462,6 +1493,45 @@ namespace Internal.JitInterface CORJIT_FLAG_TIER0 = 39, // This is the initial tier for tiered compilation which should generate code as quickly as possible CORJIT_FLAG_TIER1 = 40, // This is the final tier (for now) for tiered compilation which should generate high quality code CORJIT_FLAG_RELATIVE_CODE_RELOCS = 41, // JIT should generate PC-relative address computations instead of EE relocation records + CORJIT_FLAG_NO_INLINING = 42, // JIT should not inline any called method into this method + +#region ARM64 + CORJIT_FLAG_HAS_ARM64_AES = 43, // ID_AA64ISAR0_EL1.AES is 1 or better + CORJIT_FLAG_HAS_ARM64_ATOMICS = 44, // ID_AA64ISAR0_EL1.Atomic is 2 or better + CORJIT_FLAG_HAS_ARM64_CRC32 = 45, // ID_AA64ISAR0_EL1.CRC32 is 1 or better + CORJIT_FLAG_HAS_ARM64_DCPOP = 46, // ID_AA64ISAR1_EL1.DPB is 1 or better + CORJIT_FLAG_HAS_ARM64_DP = 47, // ID_AA64ISAR0_EL1.DP is 1 or better + CORJIT_FLAG_HAS_ARM64_FCMA = 48, // ID_AA64ISAR1_EL1.FCMA is 1 or better + CORJIT_FLAG_HAS_ARM64_FP = 49, // ID_AA64PFR0_EL1.FP is 0 or better + CORJIT_FLAG_HAS_ARM64_FP16 = 50, // ID_AA64PFR0_EL1.FP is 1 or better + CORJIT_FLAG_HAS_ARM64_JSCVT = 51, // ID_AA64ISAR1_EL1.JSCVT is 1 or better + CORJIT_FLAG_HAS_ARM64_LRCPC = 52, // ID_AA64ISAR1_EL1.LRCPC is 1 or better + CORJIT_FLAG_HAS_ARM64_PMULL = 53, // ID_AA64ISAR0_EL1.AES is 2 or better + CORJIT_FLAG_HAS_ARM64_SHA1 = 54, // ID_AA64ISAR0_EL1.SHA1 is 1 or better + CORJIT_FLAG_HAS_ARM64_SHA2 = 55, // ID_AA64ISAR0_EL1.SHA2 is 1 or better + CORJIT_FLAG_HAS_ARM64_SHA512 = 56, // ID_AA64ISAR0_EL1.SHA2 is 2 or better + CORJIT_FLAG_HAS_ARM64_SHA3 = 57, // ID_AA64ISAR0_EL1.SHA3 is 1 or better + CORJIT_FLAG_HAS_ARM64_SIMD = 58, // ID_AA64PFR0_EL1.AdvSIMD is 0 or better + CORJIT_FLAG_HAS_ARM64_SIMD_V81 = 59, // ID_AA64ISAR0_EL1.RDM is 1 or better + CORJIT_FLAG_HAS_ARM64_SIMD_FP16 = 60, // ID_AA64PFR0_EL1.AdvSIMD is 1 or better + CORJIT_FLAG_HAS_ARM64_SM3 = 61, // ID_AA64ISAR0_EL1.SM3 is 1 or better + CORJIT_FLAG_HAS_ARM64_SM4 = 62, // ID_AA64ISAR0_EL1.SM4 is 1 or better + CORJIT_FLAG_HAS_ARM64_SVE = 63, // ID_AA64PFR0_EL1.SVE is 1 or better +#endregion + +#region x86/x64 + CORJIT_FLAG_USE_SSE3 = 43, + CORJIT_FLAG_USE_SSSE3 = 44, + CORJIT_FLAG_USE_SSE41 = 45, + CORJIT_FLAG_USE_SSE42 = 46, + CORJIT_FLAG_USE_AES = 47, + CORJIT_FLAG_USE_BMI1 = 48, + CORJIT_FLAG_USE_BMI2 = 49, + CORJIT_FLAG_USE_FMA = 50, + CORJIT_FLAG_USE_LZCNT = 51, + CORJIT_FLAG_USE_PCLMULQDQ = 52, + CORJIT_FLAG_USE_POPCNT = 53, +#endregion } public struct CORJIT_FLAGS diff --git a/external/corert/src/JitInterface/src/JitConfigProvider.cs b/external/corert/src/JitInterface/src/JitConfigProvider.cs index 78a596ff0e..6008528fba 100644 --- a/external/corert/src/JitInterface/src/JitConfigProvider.cs +++ b/external/corert/src/JitInterface/src/JitConfigProvider.cs @@ -105,14 +105,14 @@ namespace Internal.JitInterface return instance; } - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + [UnmanagedFunctionPointer(default(CallingConvention))] private unsafe delegate int __getIntConfigValue(IntPtr thisHandle, [MarshalAs(UnmanagedType.LPWStr)] string name, int defaultValue); private unsafe int getIntConfigValue(IntPtr thisHandle, string name, int defaultValue) { return GetIntConfigValue(name, defaultValue); } - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + [UnmanagedFunctionPointer(default(CallingConvention))] private unsafe delegate int __getStringConfigValue(IntPtr thisHandle, [MarshalAs(UnmanagedType.LPWStr)] string name, char* retBuffer, int retBufferLength); private unsafe int getStringConfigValue(IntPtr thisHandle, string name, char* retBuffer, int retBufferLength) { diff --git a/external/corert/src/JitInterface/src/ThunkGenerator/Program.cs b/external/corert/src/JitInterface/src/ThunkGenerator/Program.cs index 29656286ae..499014b233 100644 --- a/external/corert/src/JitInterface/src/ThunkGenerator/Program.cs +++ b/external/corert/src/JitInterface/src/ThunkGenerator/Program.cs @@ -272,7 +272,7 @@ namespace Internal.JitInterface foreach (FunctionDecl decl in functionData) { - tr.WriteLine(" [UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]"); + tr.WriteLine(" [UnmanagedFunctionPointerAttribute(default(CallingConvention))]"); string returnType = decl.ReturnAsParm ? "void" : decl.ReturnType.ManagedTypeName; int marshalAs = returnType.LastIndexOf(']'); @@ -409,7 +409,7 @@ struct JitInterfaceCallbacks foreach (FunctionDecl decl in functionData) { string returnType = decl.ReturnAsParm ? "void" : decl.ReturnType.NativeTypeName; - tw.Write(" " + returnType + " (__stdcall * " + decl.FunctionName + ")("); + tw.Write(" " + returnType + " (* " + decl.FunctionName + ")("); tw.Write("void * thisHandle"); tw.Write(", CorInfoException** ppException"); if (decl.ReturnAsParm) diff --git a/external/corert/src/JitInterface/src/ThunkGenerator/ThunkInput.txt b/external/corert/src/JitInterface/src/ThunkGenerator/ThunkInput.txt index fcbe38f6b0..4792e13d1f 100644 --- a/external/corert/src/JitInterface/src/ThunkGenerator/ThunkInput.txt +++ b/external/corert/src/JitInterface/src/ThunkGenerator/ThunkInput.txt @@ -39,7 +39,7 @@ LPVOID,void*,void* void* const void *,void* HRESULT,,int -SIZE_T*,,size_t* +SIZE_T*,void*,size_t* int INT,int,int INT32,int,int @@ -51,17 +51,18 @@ unsigned int, uint size_t,UIntPtr SIZE_T,UIntPtr,size_t WORD,ushort,unsigned short -BOOL,[MarshalAs(UnmanagedType.Bool)]bool,bool +BOOL,[MarshalAs(UnmanagedType.Bool)]bool,int bool,[MarshalAs(UnmanagedType.I1)]bool const char *,byte* mdMethodDef,mdToken,unsigned int mdToken,,unsigned int BYTE*,byte*,unsigned char* -GSCookie*,,void* -GSCookie**,,void** +GSCookie*,IntPtr*,void* +GSCookie**,IntPtr**,void** BOOL*,[MarshalAs(UnmanagedType.Bool)] ref bool,int* bool*,[MarshalAs(UnmanagedType.U1)] ref bool +BoolStar,byte*,bool* ULONG*,ref uint,unsigned long* void **,ref void* VOIDSTARSTAR,void **,void ** @@ -127,6 +128,7 @@ CORINFO_CALLINFO_FLAGS,,int CorJitAllocMemFlag,,int CorJitFuncKind,,int CorJitResult,,int +TypeCompareState,,int ; Handle types CORINFO_MODULE_HANDLE,CORINFO_MODULE_STRUCT_*,void* @@ -151,7 +153,7 @@ ICorDebugInfo::BoundaryTypes*,BoundaryTypes*,void* struct _EXCEPTION_POINTERS*,_EXCEPTION_POINTERS*,void* RETURNTYPES -BOOL,[return: MarshalAs(UnmanagedType.Bool)]bool,bool +BOOL,[return: MarshalAs(UnmanagedType.Bool)]bool,int bool,[return: MarshalAs(UnmanagedType.I1)]bool LPCWSTR,[return: MarshalAs(UnmanagedType.LPWStr)]string,const wchar_t* ; NOTE in managed SIZE_T is an enum that is 64bits in size, and returning one of those causing mcg to do the wrong thing. @@ -169,15 +171,17 @@ FUNCTIONS void getEHinfo( CORINFO_METHOD_HANDLE ftn, unsigned EHnumber, CORINFO_EH_CLAUSE* clause ); CORINFO_CLASS_HANDLE getMethodClass( CORINFO_METHOD_HANDLE method ); CORINFO_MODULE_HANDLE getMethodModule( CORINFO_METHOD_HANDLE method ); - void getMethodVTableOffset( CORINFO_METHOD_HANDLE method, unsigned* offsetOfIndirection, unsigned* offsetAfterIndirection ); + void getMethodVTableOffset( CORINFO_METHOD_HANDLE method, unsigned* offsetOfIndirection, unsigned* offsetAfterIndirection, bool* isRelative); CORINFO_METHOD_HANDLE resolveVirtualMethod( CORINFO_METHOD_HANDLE virtualMethod, CORINFO_CLASS_HANDLE implementingClass, CORINFO_CONTEXT_HANDLE ownerType); - CorInfoIntrinsics getIntrinsicID( CORINFO_METHOD_HANDLE method , bool * pMustExpand); + CORINFO_METHOD_HANDLE getUnboxedEntry(CORINFO_METHOD_HANDLE ftn, BoolStar requiresInstMethodTableArg); + CORINFO_CLASS_HANDLE getDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE elemType); + void expandRawHandleIntrinsic(CORINFO_RESOLVED_TOKEN * pResolvedToken, CORINFO_GENERICHANDLE_RESULT * pResult); + CorInfoIntrinsics getIntrinsicID( CORINFO_METHOD_HANDLE method , BoolStar pMustExpand); bool isInSIMDModule( CORINFO_CLASS_HANDLE classHnd ); CorInfoUnmanagedCallConv getUnmanagedCallConv( CORINFO_METHOD_HANDLE method ); BOOL pInvokeMarshalingRequired( CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* callSiteSig ); BOOL satisfiesMethodConstraints( CORINFO_CLASS_HANDLE parent, CORINFO_METHOD_HANDLE method ); BOOL isCompatibleDelegate( CORINFO_CLASS_HANDLE objCls, CORINFO_CLASS_HANDLE methodParentCls, CORINFO_METHOD_HANDLE method, CORINFO_CLASS_HANDLE delegateCls, BOOL *pfIsOpenDelegate ); - BOOL isDelegateCreationAllowed( CORINFO_CLASS_HANDLE delegateHnd, CORINFO_METHOD_HANDLE calleeHnd ); CorInfoInstantiationVerification isInstantiationOfVerifiedGeneric( CORINFO_METHOD_HANDLE method ); void initConstraintsForVerification( CORINFO_METHOD_HANDLE method, BOOL *pfHasCircularClassConstraints, BOOL *pfHasCircularMethodConstraint ); CorInfoCanSkipVerificationResult canSkipMethodVerification( CORINFO_METHOD_HANDLE ftnHandle ); @@ -195,6 +199,8 @@ FUNCTIONS BOOL shouldEnforceCallvirtRestriction(CORINFO_MODULE_HANDLE scope) CorInfoType asCorInfoType(CORINFO_CLASS_HANDLE cls) const char* getClassName(CORINFO_CLASS_HANDLE cls) + const char* getClassNameFromMetadata(CORINFO_CLASS_HANDLE cls, const char **namespaceName) + CORINFO_CLASS_HANDLE getTypeInstantiationArgument(CORINFO_CLASS_HANDLE cls, unsigned index) int appendClassName(WCHAR** ppBuf, int* pnBufLen, CORINFO_CLASS_HANDLE cls, BOOL fNamespace, BOOL fFullInst, BOOL fAssembly) BOOL isValueClass(CORINFO_CLASS_HANDLE cls) BOOL canInlineTypeCheckWithObjectVTable(CORINFO_CLASS_HANDLE cls) @@ -227,8 +233,11 @@ FUNCTIONS void classMustBeLoadedBeforeCodeIsRun(CORINFO_CLASS_HANDLE cls) CORINFO_CLASS_HANDLE getBuiltinClass(CorInfoClassId classId) CorInfoType getTypeForPrimitiveValueClass(CORINFO_CLASS_HANDLE cls) + CorInfoType getTypeForPrimitiveNumericClass(CORINFO_CLASS_HANDLE cls) BOOL canCast(CORINFO_CLASS_HANDLE child, CORINFO_CLASS_HANDLE parent) BOOL areTypesEquivalent(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2) + TypeCompareState compareTypesForCast(CORINFO_CLASS_HANDLE fromClass, CORINFO_CLASS_HANDLE toClass) + TypeCompareState compareTypesForEquality(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2) CORINFO_CLASS_HANDLE mergeClasses(CORINFO_CLASS_HANDLE cls1, CORINFO_CLASS_HANDLE cls2) CORINFO_CLASS_HANDLE getParentType(CORINFO_CLASS_HANDLE cls) CorInfoType getChildType(CORINFO_CLASS_HANDLE clsHnd, CORINFO_CLASS_HANDLE* clsRet) @@ -265,13 +274,13 @@ FUNCTIONS LPCWSTR getJitTimeLogFilename(); mdMethodDef getMethodDefFromMethod(CORINFO_METHOD_HANDLE hMethod); const char* getMethodName(CORINFO_METHOD_HANDLE ftn, const char **moduleName); + const char* getMethodNameFromMetadata(CORINFO_METHOD_HANDLE ftn, const char **className, const char **namespaceName); unsigned getMethodHash(CORINFO_METHOD_HANDLE ftn); size_t findNameOfToken(CORINFO_MODULE_HANDLE moduleHandle,mdToken token, char * szFQName,size_t FQNameCapacity); bool getSystemVAmd64PassStructInRegisterDescriptor(CORINFO_CLASS_HANDLE structHnd, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr); DWORD getThreadTLSIndex(void **ppIndirection); const void * getInlinedCallFrameVptr(void **ppIndirection); LONG * getAddrOfCaptureThreadGlobal(void **ppIndirection); - SIZE_T* getAddrModuleDomainID(CORINFO_MODULE_HANDLE module); void* getHelperFtn (CorInfoHelpFunc ftnNum, void **ppIndirection); void getFunctionEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP * pResult, CORINFO_ACCESS_FLAGS accessFlags); void getFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, CORINFO_CONST_LOOKUP * pResult); diff --git a/external/corert/src/JitInterface/src/ThunkGenerator/corinfo.h.REMOVED.git-id b/external/corert/src/JitInterface/src/ThunkGenerator/corinfo.h.REMOVED.git-id index 43968d01e8..61624f2c00 100644 --- a/external/corert/src/JitInterface/src/ThunkGenerator/corinfo.h.REMOVED.git-id +++ b/external/corert/src/JitInterface/src/ThunkGenerator/corinfo.h.REMOVED.git-id @@ -1 +1 @@ -2495de2516b8a9e7a7381d3cb9d63ca15dffbc92 \ No newline at end of file +c79dd3f4c65fd8c9e4bedce829279a67a086cc82 \ No newline at end of file diff --git a/external/corert/src/JitInterface/src/ThunkGenerator/corjit.h b/external/corert/src/JitInterface/src/ThunkGenerator/corjit.h index e6e8257afe..cadfdff387 100644 --- a/external/corert/src/JitInterface/src/ThunkGenerator/corjit.h +++ b/external/corert/src/JitInterface/src/ThunkGenerator/corjit.h @@ -32,6 +32,8 @@ #include +#include + #define CORINFO_STACKPROBE_DEPTH 256*sizeof(UINT_PTR) // Guaranteed stack until an fcall/unmanaged // code can set up a frame. Please make sure // this is less than a page. This is due to @@ -72,155 +74,6 @@ enum CorJitResult CORJIT_RECOVERABLEERROR = MAKE_HRESULT(SEVERITY_ERROR,FACILITY_NULL, 5), }; -class CORJIT_FLAGS -{ -public: - - enum CorJitFlag - { - CORJIT_FLAG_CALL_GETJITFLAGS = 0xffffffff, // Indicates that the JIT should retrieve flags in the form of a - // pointer to a CORJIT_FLAGS value via ICorJitInfo::getJitFlags(). - CORJIT_FLAG_SPEED_OPT = 0, - CORJIT_FLAG_SIZE_OPT = 1, - CORJIT_FLAG_DEBUG_CODE = 2, // generate "debuggable" code (no code-mangling optimizations) - CORJIT_FLAG_DEBUG_EnC = 3, // We are in Edit-n-Continue mode - CORJIT_FLAG_DEBUG_INFO = 4, // generate line and local-var info - CORJIT_FLAG_MIN_OPT = 5, // disable all jit optimizations (not necesarily debuggable code) - CORJIT_FLAG_GCPOLL_CALLS = 6, // Emit calls to JIT_POLLGC for thread suspension. - CORJIT_FLAG_MCJIT_BACKGROUND = 7, // Calling from multicore JIT background thread, do not call JitComplete - - #if defined(_TARGET_X86_) - - CORJIT_FLAG_PINVOKE_RESTORE_ESP = 8, // Restore ESP after returning from inlined PInvoke - CORJIT_FLAG_TARGET_P4 = 9, - CORJIT_FLAG_USE_FCOMI = 10, // Generated code may use fcomi(p) instruction - CORJIT_FLAG_USE_CMOV = 11, // Generated code may use cmov instruction - CORJIT_FLAG_USE_SSE2 = 12, // Generated code may use SSE-2 instructions - - #else // !defined(_TARGET_X86_) - - CORJIT_FLAG_UNUSED1 = 8, - CORJIT_FLAG_UNUSED2 = 9, - CORJIT_FLAG_UNUSED3 = 10, - CORJIT_FLAG_UNUSED4 = 11, - CORJIT_FLAG_UNUSED5 = 12, - - #endif // !defined(_TARGET_X86_) - - #if defined(_TARGET_X86_) || defined(_TARGET_AMD64_) - - CORJIT_FLAG_USE_SSE3_4 = 13, - CORJIT_FLAG_USE_AVX = 14, - CORJIT_FLAG_USE_AVX2 = 15, - CORJIT_FLAG_USE_AVX_512 = 16, - CORJIT_FLAG_FEATURE_SIMD = 17, - - #else // !defined(_TARGET_X86_) && !defined(_TARGET_AMD64_) - - CORJIT_FLAG_UNUSED6 = 13, - CORJIT_FLAG_UNUSED7 = 14, - CORJIT_FLAG_UNUSED8 = 15, - CORJIT_FLAG_UNUSED9 = 16, - CORJIT_FLAG_UNUSED10 = 17, - - #endif // !defined(_TARGET_X86_) && !defined(_TARGET_AMD64_) - - CORJIT_FLAG_MAKEFINALCODE = 18, // Use the final code generator, i.e., not the interpreter. - CORJIT_FLAG_READYTORUN = 19, // Use version-resilient code generation - CORJIT_FLAG_PROF_ENTERLEAVE = 20, // Instrument prologues/epilogues - CORJIT_FLAG_PROF_REJIT_NOPS = 21, // Insert NOPs to ensure code is re-jitable - CORJIT_FLAG_PROF_NO_PINVOKE_INLINE = 22, // Disables PInvoke inlining - CORJIT_FLAG_SKIP_VERIFICATION = 23, // (lazy) skip verification - determined without doing a full resolve. See comment below - CORJIT_FLAG_PREJIT = 24, // jit or prejit is the execution engine. - CORJIT_FLAG_RELOC = 25, // Generate relocatable code - CORJIT_FLAG_IMPORT_ONLY = 26, // Only import the function - CORJIT_FLAG_IL_STUB = 27, // method is an IL stub - CORJIT_FLAG_PROCSPLIT = 28, // JIT should separate code into hot and cold sections - CORJIT_FLAG_BBINSTR = 29, // Collect basic block profile information - CORJIT_FLAG_BBOPT = 30, // Optimize method based on profile information - CORJIT_FLAG_FRAMED = 31, // All methods have an EBP frame - CORJIT_FLAG_ALIGN_LOOPS = 32, // add NOPs before loops to align them at 16 byte boundaries - CORJIT_FLAG_PUBLISH_SECRET_PARAM = 33, // JIT must place stub secret param into local 0. (used by IL stubs) - CORJIT_FLAG_GCPOLL_INLINE = 34, // JIT must inline calls to GCPoll when possible - CORJIT_FLAG_SAMPLING_JIT_BACKGROUND = 35, // JIT is being invoked as a result of stack sampling for hot methods in the background - CORJIT_FLAG_USE_PINVOKE_HELPERS = 36, // The JIT should use the PINVOKE_{BEGIN,END} helpers instead of emitting inline transitions - CORJIT_FLAG_REVERSE_PINVOKE = 37, // The JIT should insert REVERSE_PINVOKE_{ENTER,EXIT} helpers into method prolog/epilog - CORJIT_FLAG_DESKTOP_QUIRKS = 38, // The JIT should generate desktop-quirk-compatible code - CORJIT_FLAG_TIER0 = 39, // This is the initial tier for tiered compilation which should generate code as quickly as possible - CORJIT_FLAG_TIER1 = 40, // This is the final tier (for now) for tiered compilation which should generate high quality code - -#if defined(_TARGET_ARM_) - CORJIT_FLAG_RELATIVE_CODE_RELOCS = 41, // JIT should generate PC-relative address computations instead of EE relocation records -#else // !defined(_TARGET_ARM_) - CORJIT_FLAG_UNUSED11 = 41 -#endif // !defined(_TARGET_ARM_) - }; - - CORJIT_FLAGS() - : corJitFlags(0) - { - // empty - } - - // Convenience constructor to set exactly one flag. - CORJIT_FLAGS(CorJitFlag flag) - : corJitFlags(0) - { - Set(flag); - } - - CORJIT_FLAGS(const CORJIT_FLAGS& other) - { - corJitFlags = other.corJitFlags; - } - - void Reset() - { - corJitFlags = 0; - } - - void Set(CorJitFlag flag) - { - corJitFlags |= 1ULL << (unsigned __int64)flag; - } - - void Clear(CorJitFlag flag) - { - corJitFlags &= ~(1ULL << (unsigned __int64)flag); - } - - bool IsSet(CorJitFlag flag) const - { - return (corJitFlags & (1ULL << (unsigned __int64)flag)) != 0; - } - - void Add(const CORJIT_FLAGS& other) - { - corJitFlags |= other.corJitFlags; - } - - void Remove(const CORJIT_FLAGS& other) - { - corJitFlags &= ~other.corJitFlags; - } - - bool IsEmpty() const - { - return corJitFlags == 0; - } - - // DO NOT USE THIS FUNCTION! (except in very restricted special cases) - unsigned __int64 GetFlagsRaw() - { - return corJitFlags; - } - -private: - - unsigned __int64 corJitFlags; -}; - - /***************************************************************************** Here is how CORJIT_FLAG_SKIP_VERIFICATION should be interepreted. Note that even if any method is inlined, it need not be verified. diff --git a/external/corert/src/JitInterface/src/ThunkGenerator/corjitflags.h b/external/corert/src/JitInterface/src/ThunkGenerator/corjitflags.h new file mode 100644 index 0000000000..2598c26acf --- /dev/null +++ b/external/corert/src/JitInterface/src/ThunkGenerator/corjitflags.h @@ -0,0 +1,250 @@ +// 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. + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE +// +// The JIT/EE interface is versioned. By "interface", we mean mean any and all communication between the +// JIT and the EE. Any time a change is made to the interface, the JIT/EE interface version identifier +// must be updated. See code:JITEEVersionIdentifier for more information. +// +// NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE +// +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef _COR_JIT_FLAGS_H_ +#define _COR_JIT_FLAGS_H_ + +class CORJIT_FLAGS +{ +public: + + enum CorJitFlag + { + CORJIT_FLAG_CALL_GETJITFLAGS = 0xffffffff, // Indicates that the JIT should retrieve flags in the form of a + // pointer to a CORJIT_FLAGS value via ICorJitInfo::getJitFlags(). + CORJIT_FLAG_SPEED_OPT = 0, + CORJIT_FLAG_SIZE_OPT = 1, + CORJIT_FLAG_DEBUG_CODE = 2, // generate "debuggable" code (no code-mangling optimizations) + CORJIT_FLAG_DEBUG_EnC = 3, // We are in Edit-n-Continue mode + CORJIT_FLAG_DEBUG_INFO = 4, // generate line and local-var info + CORJIT_FLAG_MIN_OPT = 5, // disable all jit optimizations (not necesarily debuggable code) + CORJIT_FLAG_GCPOLL_CALLS = 6, // Emit calls to JIT_POLLGC for thread suspension. + CORJIT_FLAG_MCJIT_BACKGROUND = 7, // Calling from multicore JIT background thread, do not call JitComplete + + #if defined(_TARGET_X86_) + + CORJIT_FLAG_PINVOKE_RESTORE_ESP = 8, // Restore ESP after returning from inlined PInvoke + CORJIT_FLAG_TARGET_P4 = 9, + CORJIT_FLAG_USE_FCOMI = 10, // Generated code may use fcomi(p) instruction + CORJIT_FLAG_USE_CMOV = 11, // Generated code may use cmov instruction + CORJIT_FLAG_USE_SSE2 = 12, // Generated code may use SSE-2 instructions + + #else // !defined(_TARGET_X86_) + + CORJIT_FLAG_UNUSED1 = 8, + CORJIT_FLAG_UNUSED2 = 9, + CORJIT_FLAG_UNUSED3 = 10, + CORJIT_FLAG_UNUSED4 = 11, + CORJIT_FLAG_UNUSED5 = 12, + + #endif // !defined(_TARGET_X86_) + + #if defined(_TARGET_X86_) || defined(_TARGET_AMD64_) + + CORJIT_FLAG_USE_SSE3_4 = 13, + CORJIT_FLAG_USE_AVX = 14, + CORJIT_FLAG_USE_AVX2 = 15, + CORJIT_FLAG_USE_AVX_512 = 16, + + #else // !defined(_TARGET_X86_) && !defined(_TARGET_AMD64_) + + CORJIT_FLAG_UNUSED6 = 13, + CORJIT_FLAG_UNUSED7 = 14, + CORJIT_FLAG_UNUSED8 = 15, + CORJIT_FLAG_UNUSED9 = 16, + + #endif // !defined(_TARGET_X86_) && !defined(_TARGET_AMD64_) + + #if defined(_TARGET_X86_) || defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) + CORJIT_FLAG_FEATURE_SIMD = 17, + #else + CORJIT_FLAG_UNUSED10 = 17, + #endif // !(defined(_TARGET_X86_) || defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)) + + CORJIT_FLAG_MAKEFINALCODE = 18, // Use the final code generator, i.e., not the interpreter. + CORJIT_FLAG_READYTORUN = 19, // Use version-resilient code generation + CORJIT_FLAG_PROF_ENTERLEAVE = 20, // Instrument prologues/epilogues + CORJIT_FLAG_PROF_REJIT_NOPS = 21, // Insert NOPs to ensure code is re-jitable + CORJIT_FLAG_PROF_NO_PINVOKE_INLINE = 22, // Disables PInvoke inlining + CORJIT_FLAG_SKIP_VERIFICATION = 23, // (lazy) skip verification - determined without doing a full resolve. See comment below + CORJIT_FLAG_PREJIT = 24, // jit or prejit is the execution engine. + CORJIT_FLAG_RELOC = 25, // Generate relocatable code + CORJIT_FLAG_IMPORT_ONLY = 26, // Only import the function + CORJIT_FLAG_IL_STUB = 27, // method is an IL stub + CORJIT_FLAG_PROCSPLIT = 28, // JIT should separate code into hot and cold sections + CORJIT_FLAG_BBINSTR = 29, // Collect basic block profile information + CORJIT_FLAG_BBOPT = 30, // Optimize method based on profile information + CORJIT_FLAG_FRAMED = 31, // All methods have an EBP frame + CORJIT_FLAG_ALIGN_LOOPS = 32, // add NOPs before loops to align them at 16 byte boundaries + CORJIT_FLAG_PUBLISH_SECRET_PARAM = 33, // JIT must place stub secret param into local 0. (used by IL stubs) + CORJIT_FLAG_GCPOLL_INLINE = 34, // JIT must inline calls to GCPoll when possible + CORJIT_FLAG_SAMPLING_JIT_BACKGROUND = 35, // JIT is being invoked as a result of stack sampling for hot methods in the background + CORJIT_FLAG_USE_PINVOKE_HELPERS = 36, // The JIT should use the PINVOKE_{BEGIN,END} helpers instead of emitting inline transitions + CORJIT_FLAG_REVERSE_PINVOKE = 37, // The JIT should insert REVERSE_PINVOKE_{ENTER,EXIT} helpers into method prolog/epilog + CORJIT_FLAG_DESKTOP_QUIRKS = 38, // The JIT should generate desktop-quirk-compatible code + CORJIT_FLAG_TIER0 = 39, // This is the initial tier for tiered compilation which should generate code as quickly as possible + CORJIT_FLAG_TIER1 = 40, // This is the final tier (for now) for tiered compilation which should generate high quality code + +#if defined(_TARGET_ARM_) + CORJIT_FLAG_RELATIVE_CODE_RELOCS = 41, // JIT should generate PC-relative address computations instead of EE relocation records +#else // !defined(_TARGET_ARM_) + CORJIT_FLAG_UNUSED11 = 41, +#endif // !defined(_TARGET_ARM_) + + CORJIT_FLAG_NO_INLINING = 42, // JIT should not inline any called method into this method + +#if defined(_TARGET_ARM64_) + + CORJIT_FLAG_HAS_ARM64_AES = 43, // ID_AA64ISAR0_EL1.AES is 1 or better + CORJIT_FLAG_HAS_ARM64_ATOMICS = 44, // ID_AA64ISAR0_EL1.Atomic is 2 or better + CORJIT_FLAG_HAS_ARM64_CRC32 = 45, // ID_AA64ISAR0_EL1.CRC32 is 1 or better + CORJIT_FLAG_HAS_ARM64_DCPOP = 46, // ID_AA64ISAR1_EL1.DPB is 1 or better + CORJIT_FLAG_HAS_ARM64_DP = 47, // ID_AA64ISAR0_EL1.DP is 1 or better + CORJIT_FLAG_HAS_ARM64_FCMA = 48, // ID_AA64ISAR1_EL1.FCMA is 1 or better + CORJIT_FLAG_HAS_ARM64_FP = 49, // ID_AA64PFR0_EL1.FP is 0 or better + CORJIT_FLAG_HAS_ARM64_FP16 = 50, // ID_AA64PFR0_EL1.FP is 1 or better + CORJIT_FLAG_HAS_ARM64_JSCVT = 51, // ID_AA64ISAR1_EL1.JSCVT is 1 or better + CORJIT_FLAG_HAS_ARM64_LRCPC = 52, // ID_AA64ISAR1_EL1.LRCPC is 1 or better + CORJIT_FLAG_HAS_ARM64_PMULL = 53, // ID_AA64ISAR0_EL1.AES is 2 or better + CORJIT_FLAG_HAS_ARM64_SHA1 = 54, // ID_AA64ISAR0_EL1.SHA1 is 1 or better + CORJIT_FLAG_HAS_ARM64_SHA2 = 55, // ID_AA64ISAR0_EL1.SHA2 is 1 or better + CORJIT_FLAG_HAS_ARM64_SHA512 = 56, // ID_AA64ISAR0_EL1.SHA2 is 2 or better + CORJIT_FLAG_HAS_ARM64_SHA3 = 57, // ID_AA64ISAR0_EL1.SHA3 is 1 or better + CORJIT_FLAG_HAS_ARM64_SIMD = 58, // ID_AA64PFR0_EL1.AdvSIMD is 0 or better + CORJIT_FLAG_HAS_ARM64_SIMD_V81 = 59, // ID_AA64ISAR0_EL1.RDM is 1 or better + CORJIT_FLAG_HAS_ARM64_SIMD_FP16 = 60, // ID_AA64PFR0_EL1.AdvSIMD is 1 or better + CORJIT_FLAG_HAS_ARM64_SM3 = 61, // ID_AA64ISAR0_EL1.SM3 is 1 or better + CORJIT_FLAG_HAS_ARM64_SM4 = 62, // ID_AA64ISAR0_EL1.SM4 is 1 or better + CORJIT_FLAG_HAS_ARM64_SVE = 63 // ID_AA64PFR0_EL1.SVE is 1 or better + +#elif defined(_TARGET_X86_) || defined(_TARGET_AMD64_) + + CORJIT_FLAG_USE_SSE3 = 43, + CORJIT_FLAG_USE_SSSE3 = 44, + CORJIT_FLAG_USE_SSE41 = 45, + CORJIT_FLAG_USE_SSE42 = 46, + CORJIT_FLAG_USE_AES = 47, + CORJIT_FLAG_USE_BMI1 = 48, + CORJIT_FLAG_USE_BMI2 = 49, + CORJIT_FLAG_USE_FMA = 50, + CORJIT_FLAG_USE_LZCNT = 51, + CORJIT_FLAG_USE_PCLMULQDQ = 52, + CORJIT_FLAG_USE_POPCNT = 53, + CORJIT_FLAG_UNUSED23 = 54, + CORJIT_FLAG_UNUSED24 = 55, + CORJIT_FLAG_UNUSED25 = 56, + CORJIT_FLAG_UNUSED26 = 57, + CORJIT_FLAG_UNUSED27 = 58, + CORJIT_FLAG_UNUSED28 = 59, + CORJIT_FLAG_UNUSED29 = 60, + CORJIT_FLAG_UNUSED30 = 61, + CORJIT_FLAG_UNUSED31 = 62, + CORJIT_FLAG_UNUSED32 = 63 + + +#else // !defined(_TARGET_ARM64_) &&!defined(_TARGET_X86_) && !defined(_TARGET_AMD64_) + + CORJIT_FLAG_UNUSED12 = 43, + CORJIT_FLAG_UNUSED13 = 44, + CORJIT_FLAG_UNUSED14 = 45, + CORJIT_FLAG_UNUSED15 = 46, + CORJIT_FLAG_UNUSED16 = 47, + CORJIT_FLAG_UNUSED17 = 48, + CORJIT_FLAG_UNUSED18 = 49, + CORJIT_FLAG_UNUSED19 = 50, + CORJIT_FLAG_UNUSED20 = 51, + CORJIT_FLAG_UNUSED21 = 52, + CORJIT_FLAG_UNUSED22 = 53, + CORJIT_FLAG_UNUSED23 = 54, + CORJIT_FLAG_UNUSED24 = 55, + CORJIT_FLAG_UNUSED25 = 56, + CORJIT_FLAG_UNUSED26 = 57, + CORJIT_FLAG_UNUSED27 = 58, + CORJIT_FLAG_UNUSED28 = 59, + CORJIT_FLAG_UNUSED29 = 60, + CORJIT_FLAG_UNUSED30 = 61, + CORJIT_FLAG_UNUSED31 = 62, + CORJIT_FLAG_UNUSED32 = 63 + +#endif // !defined(_TARGET_ARM64_) &&!defined(_TARGET_X86_) && !defined(_TARGET_AMD64_) + }; + + CORJIT_FLAGS() + : corJitFlags(0) + { + // empty + } + + // Convenience constructor to set exactly one flag. + CORJIT_FLAGS(CorJitFlag flag) + : corJitFlags(0) + { + Set(flag); + } + + CORJIT_FLAGS(const CORJIT_FLAGS& other) + { + corJitFlags = other.corJitFlags; + } + + void Reset() + { + corJitFlags = 0; + } + + void Set(CorJitFlag flag) + { + corJitFlags |= 1ULL << (unsigned __int64)flag; + } + + void Clear(CorJitFlag flag) + { + corJitFlags &= ~(1ULL << (unsigned __int64)flag); + } + + bool IsSet(CorJitFlag flag) const + { + return (corJitFlags & (1ULL << (unsigned __int64)flag)) != 0; + } + + void Add(const CORJIT_FLAGS& other) + { + corJitFlags |= other.corJitFlags; + } + + void Remove(const CORJIT_FLAGS& other) + { + corJitFlags &= ~other.corJitFlags; + } + + bool IsEmpty() const + { + return corJitFlags == 0; + } + + // DO NOT USE THIS FUNCTION! (except in very restricted special cases) + unsigned __int64 GetFlagsRaw() + { + return corJitFlags; + } + +private: + + unsigned __int64 corJitFlags; +}; + + +#endif // _COR_JIT_FLAGS_H_ diff --git a/external/corert/src/JitInterface/src/TypeString.cs b/external/corert/src/JitInterface/src/TypeString.cs new file mode 100644 index 0000000000..52a9dcfb95 --- /dev/null +++ b/external/corert/src/JitInterface/src/TypeString.cs @@ -0,0 +1,113 @@ +// 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.Text; + +using Internal.TypeSystem; + +namespace Internal.JitInterface +{ + // This is a very rough equivalent of typestring.cpp in the CLR. + // There's way more rules to capture. Hopefully, we'll only ever need this in the code to recognize SIMD intrisics + // and we won't need to replicate all the details around escaping and such. + internal class TypeString : TypeNameFormatter + { + public static TypeString Instance { get; } = new TypeString(); + + public override void AppendName(StringBuilder sb, PointerType type) + { + AppendName(sb, type.ParameterType); + sb.Append('*'); + } + + public override void AppendName(StringBuilder sb, GenericParameterDesc type) + { + string prefix = type.Kind == GenericParameterKind.Type ? "!" : "!!"; + sb.Append(prefix); + sb.Append(type.Name); + } + + public override void AppendName(StringBuilder sb, SignatureTypeVariable type) + { + sb.Append("!"); + sb.Append(type.Index); + } + + public override void AppendName(StringBuilder sb, SignatureMethodVariable type) + { + sb.Append("!!"); + sb.Append(type.Index); + } + + public override void AppendName(StringBuilder sb, FunctionPointerType type) + { + MethodSignature signature = type.Signature; + + AppendName(sb, signature.ReturnType); + + sb.Append(" ("); + for (int i = 0; i < signature.Length; i++) + { + if (i > 0) + sb.Append(", "); + AppendName(sb, signature[i]); + } + + sb.Append(')'); + } + + public override void AppendName(StringBuilder sb, ByRefType type) + { + AppendName(sb, type.ParameterType); + sb.Append("&"); + } + + public override void AppendName(StringBuilder sb, ArrayType type) + { + AppendName(sb, type.ElementType); + sb.Append('['); + + int rank = type.Rank; + if (rank == 1 && type.IsMdArray) + sb.Append('*'); + else + sb.Append(',', type.Rank - 1); + + sb.Append(']'); + } + + protected override void AppendNameForInstantiatedType(StringBuilder sb, DefType type) + { + AppendName(sb, type.GetTypeDefinition()); + sb.Append('['); + + for (int i = 0; i < type.Instantiation.Length; i++) + { + if (i > 0) + sb.Append(", "); + AppendName(sb, type.Instantiation[i]); + } + + sb.Append(']'); + } + + protected override void AppendNameForNamespaceType(StringBuilder sb, DefType type) + { + string ns = type.Namespace; + if (ns.Length > 0) + { + sb.Append(ns); + sb.Append('.'); + } + sb.Append(type.Name); + } + + protected override void AppendNameForNestedType(StringBuilder sb, DefType nestedType, DefType containingType) + { + AppendName(sb, containingType); + sb.Append('+'); + sb.Append(nestedType.Name); + } + } +} diff --git a/external/corert/src/Native/Bootstrap/CMakeLists.txt b/external/corert/src/Native/Bootstrap/CMakeLists.txt index db9acf1ba0..bb7b2280d8 100644 --- a/external/corert/src/Native/Bootstrap/CMakeLists.txt +++ b/external/corert/src/Native/Bootstrap/CMakeLists.txt @@ -9,5 +9,9 @@ add_compile_options(-Wno-unused-private-field) add_compile_options(-Wno-tautological-undefined-compare) endif() -add_subdirectory(base) +if(NOT CLR_CMAKE_PLATFORM_WASM) + add_subdirectory(base) +endif(NOT CLR_CMAKE_PLATFORM_WASM) + add_subdirectory(cpp) +add_subdirectory(dll) diff --git a/external/corert/src/Native/Bootstrap/CppCodeGen.h b/external/corert/src/Native/Bootstrap/CppCodeGen.h index 65ef9c0a5d..e5e41402f8 100644 --- a/external/corert/src/Native/Bootstrap/CppCodeGen.h +++ b/external/corert/src/Native/Bootstrap/CppCodeGen.h @@ -41,4 +41,17 @@ inline double __uint64_to_double(uint64_t v) return val.d; } -#endif // __CPP_CODE_GEN_H +struct ReversePInvokeFrame +{ + void* m_savedPInvokeTransitionFrame; + void* m_savedThread; +}; + +struct PInvokeTransitionFrame +{ + void* m_RIP; + void* m_pThread; // unused by stack crawler, this is so GetThread is only called once per method + // can be an invalid pointer in universal transition cases (which never need to call GetThread) + uint32_t m_Flags; // PInvokeTransitionFrameFlags +}; +#endif diff --git a/external/corert/src/Native/Bootstrap/base/CMakeLists.txt b/external/corert/src/Native/Bootstrap/base/CMakeLists.txt index 7e36b817c4..aba667e6eb 100644 --- a/external/corert/src/Native/Bootstrap/base/CMakeLists.txt +++ b/external/corert/src/Native/Bootstrap/base/CMakeLists.txt @@ -8,7 +8,7 @@ set(SOURCES add_library(bootstrapper STATIC ${SOURCES}) # Install the static bootstrapper library -install (TARGETS bootstrapper DESTINATION lib) +install (TARGETS bootstrapper DESTINATION sdk) if(WIN32) - install (FILES ${CMAKE_CURRENT_BINARY_DIR}/bootstrapper.dir/$/bootstrapper.pdb DESTINATION lib) + install (FILES ${CMAKE_CURRENT_BINARY_DIR}/bootstrapper.dir/$/bootstrapper.pdb DESTINATION sdk) endif() diff --git a/external/corert/src/Native/Bootstrap/common.h b/external/corert/src/Native/Bootstrap/common.h index 1aa9679e31..06a1ac5e6a 100644 --- a/external/corert/src/Native/Bootstrap/common.h +++ b/external/corert/src/Native/Bootstrap/common.h @@ -73,15 +73,16 @@ struct RawEEType void* m_pIndirectionModule; }; -struct ReversePInvokeFrame -{ - void* m_savedPInvokeTransitionFrame; - void* m_savedThread; -}; +struct ReversePInvokeFrame; void __reverse_pinvoke(ReversePInvokeFrame* pRevFrame); void __reverse_pinvoke_return(ReversePInvokeFrame* pRevFrame); +struct PInvokeTransitionFrame; + +void __pinvoke(PInvokeTransitionFrame* pFrame); +void __pinvoke_return(PInvokeTransitionFrame* pFrame); + typedef size_t UIntNative; inline bool IS_ALIGNED(UIntNative val, UIntNative alignment) diff --git a/external/corert/src/Native/Bootstrap/cpp/CMakeLists.txt b/external/corert/src/Native/Bootstrap/cpp/CMakeLists.txt index fd1ca6f585..48738ea1c0 100644 --- a/external/corert/src/Native/Bootstrap/cpp/CMakeLists.txt +++ b/external/corert/src/Native/Bootstrap/cpp/CMakeLists.txt @@ -10,7 +10,11 @@ set(SOURCES add_library(bootstrappercpp STATIC ${SOURCES}) # Install the static bootstrappercpp library -install (TARGETS bootstrappercpp DESTINATION lib) +install (TARGETS bootstrappercpp DESTINATION sdk) if(WIN32) - install (FILES ${CMAKE_CURRENT_BINARY_DIR}/bootstrappercpp.dir/$/bootstrappercpp.pdb DESTINATION lib) + install (FILES ${CMAKE_CURRENT_BINARY_DIR}/bootstrappercpp.dir/$/bootstrappercpp.pdb DESTINATION sdk) endif() + +# Install the CppCodeGen support headers +install (FILES ../common.h DESTINATION inc) +install (FILES ../CppCodeGen.h DESTINATION inc) diff --git a/external/corert/src/Native/Bootstrap/dll/CMakeLists.txt b/external/corert/src/Native/Bootstrap/dll/CMakeLists.txt new file mode 100644 index 0000000000..8357d0afb0 --- /dev/null +++ b/external/corert/src/Native/Bootstrap/dll/CMakeLists.txt @@ -0,0 +1,16 @@ +project(bootstrapperdll) + +add_definitions(-DCORERT_DLL) + +set(SOURCES + ../main.cpp + ../common.cpp +) + +add_library(bootstrapperdll STATIC ${SOURCES}) + +# Install the static bootstrapperdll library +install (TARGETS bootstrapperdll DESTINATION sdk) +if(WIN32) + install (FILES ${CMAKE_CURRENT_BINARY_DIR}/bootstrapperdll.dir/$/bootstrapperdll.pdb DESTINATION sdk) +endif() diff --git a/external/corert/src/Native/Bootstrap/main.cpp b/external/corert/src/Native/Bootstrap/main.cpp index cb63f67a1f..1f482795a1 100644 --- a/external/corert/src/Native/Bootstrap/main.cpp +++ b/external/corert/src/Native/Bootstrap/main.cpp @@ -8,7 +8,7 @@ #include "gcenv.structs.h" #include "gcenv.base.h" -#include +#include #ifndef CPPCODEGEN @@ -39,9 +39,18 @@ __declspec(allocate(".modules$Z")) void * __modules_z[] = { nullptr }; // #pragma comment(linker, "/merge:.modules=.rdata") +// +// Unboxing stubs need to be merged, folded and sorted. They are delimited by two special sections (.unbox$A +// and .unbox$Z). All unboxing stubs are in .unbox$M sections. +// +#pragma comment(linker, "/merge:.unbox=.text") + extern "C" void __managedcode_a(); extern "C" void __managedcode_z(); +extern "C" void __unbox_a(); +extern "C" void __unbox_z(); + #else // _MSC_VER #if defined(__APPLE__) @@ -50,6 +59,8 @@ extern void * __modules_a[] __asm("section$start$__DATA$__modules"); extern void * __modules_z[] __asm("section$end$__DATA$__modules"); extern char __managedcode_a __asm("section$start$__TEXT$__managedcode"); extern char __managedcode_z __asm("section$end$__TEXT$__managedcode"); +extern char __unbox_a __asm("section$start$__TEXT$__unbox"); +extern char __unbox_z __asm("section$end$__TEXT$__unbox"); #else // __APPLE__ @@ -63,17 +74,23 @@ extern "C" char __stop___managedcode; static char& __managedcode_a = __start___managedcode; static char& __managedcode_z = __stop___managedcode; +extern "C" char __start___unbox; +extern "C" char __stop___unbox; +static char& __unbox_a = __start___unbox; +static char& __unbox_z = __stop___unbox; + #endif // __APPLE__ #endif // _MSC_VER #endif // !CPPCODEGEN +// Do not warn that extern C methods throw exceptions. This is temporary +// as long as we have unimplemented/throwing APIs in this file. +#pragma warning(disable:4297) #ifdef CPPCODEGEN -#pragma warning(disable:4297) - extern "C" Object * RhNewObject(MethodTable * pMT); extern "C" Object * RhNewArray(MethodTable * pMT, int32_t elements); extern "C" void * RhTypeCast_IsInstanceOf(void * pObject, MethodTable * pMT); @@ -135,6 +152,19 @@ void __reverse_pinvoke_return(ReversePInvokeFrame* pRevFrame) RhpReversePInvokeReturn2(pRevFrame); } +extern "C" void RhpPInvoke2(PInvokeTransitionFrame* pFrame); +extern "C" void RhpPInvokeReturn2(PInvokeTransitionFrame* pFrame); + +void __pinvoke(PInvokeTransitionFrame* pFrame) +{ + RhpPInvoke2(pFrame); +} + +void __pinvoke_return(PInvokeTransitionFrame* pFrame) +{ + RhpPInvokeReturn2(pFrame); +} + namespace System_Private_CoreLib { namespace System { class Object { @@ -209,15 +239,18 @@ extern "C" void RhpUniversalTransition_DebugStepTailCall() { throw "RhpUniversalTransition_DebugStepTailCall"; } -extern "C" void CCWAddRef() -{ - throw "CCWAddRef"; -} void* RtRHeaderWrapper(); #endif // CPPCODEGEN +// This works around System.Private.Interop's references to Interop.Native. +// This won't be needed once we stop dragging in S.P.Interop for basic p/invoke support. +extern "C" void CCWAddRef() +{ + throw "CCWAddRef"; +} + extern "C" void __fail_fast() { // TODO: FailFast @@ -226,35 +259,25 @@ extern "C" void __fail_fast() exit(-1); } -extern "C" bool REDHAWK_PALAPI PalInit(); - -#define DLL_PROCESS_ATTACH 1 -extern "C" BOOL WINAPI RtuDllMain(HANDLE hPalInstance, DWORD dwReason, void* pvReserved); - +extern "C" bool RhInitialize(); +extern "C" void RhpEnableConservativeStackReporting(); extern "C" void RhpShutdown(); - -extern "C" int32_t RhpEnableConservativeStackReporting(); +extern "C" void RhSetRuntimeInitializationCallback(int (*fPtr)()); #ifndef CPPCODEGEN -extern "C" bool RhpRegisterCoffModule(void * pModule, - void * pvStartRange, uint32_t cbRange, +extern "C" bool RhRegisterOSModule(void * pModule, + void * pvManagedCodeStartRange, uint32_t cbManagedCodeRange, + void * pvUnboxingStubsStartRange, uint32_t cbUnboxingStubsRange, void ** pClasslibFunctions, uint32_t nClasslibFunctions); -extern "C" bool RhpRegisterUnixModule(void * pModule, - void * pvStartRange, uint32_t cbRange, - void ** pClasslibFunctions, uint32_t nClasslibFunctions); - -#ifdef _WIN32 -extern "C" void* WINAPI GetModuleHandleW(const wchar_t *); -#else -extern "C" void* WINAPI PalGetModuleHandleFromPointer(void* pointer); -#endif +extern "C" void* PalGetModuleHandleFromPointer(void* pointer); extern "C" void GetRuntimeException(); extern "C" void FailFast(); extern "C" void AppendExceptionStackFrame(); extern "C" void GetSystemArrayEEType(); +extern "C" void OnFirstChanceException(); typedef void(*pfn)(); @@ -265,59 +288,76 @@ static const pfn c_classlibFunctions[] = { &AppendExceptionStackFrame, nullptr, // &CheckStaticClassConstruction, &GetSystemArrayEEType, + &OnFirstChanceException }; #endif // !CPPCODEGEN extern "C" void InitializeModules(void* osModule, void ** modules, int count, void ** pClasslibFunctions, int nClasslibFunctions); +#ifndef CORERT_DLL +#define CORERT_ENTRYPOINT __managed__Main #if defined(_WIN32) extern "C" int __managed__Main(int argc, wchar_t* argv[]); -int wmain(int argc, wchar_t* argv[]) #else extern "C" int __managed__Main(int argc, char* argv[]); -int main(int argc, char* argv[]) #endif -{ - if (!PalInit()) - return -1; +#else +#define CORERT_ENTRYPOINT __managed__Startup +extern "C" void __managed__Startup(); +#endif // !CORERT_DLL - if (!RtuDllMain(NULL, DLL_PROCESS_ATTACH, NULL)) +static int InitializeRuntime() +{ + if (!RhInitialize()) return -1; #if defined(CPPCODEGEN) - if (!RhpEnableConservativeStackReporting()) - return -1; + RhpEnableConservativeStackReporting(); #endif // CPPCODEGEN #ifndef CPPCODEGEN - void *osModule; + void * osModule = PalGetModuleHandleFromPointer((void*)&CORERT_ENTRYPOINT); -#if defined(_WIN32) - osModule = GetModuleHandleW(NULL); - if (!RhpRegisterCoffModule(osModule, -#else // _WIN32 - osModule = PalGetModuleHandleFromPointer((void*)&main); - if (!RhpRegisterUnixModule(osModule, -#endif // _WIN32 + // TODO: pass struct with parameters instead of the large signature of RhRegisterOSModule + if (!RhRegisterOSModule( + osModule, (void*)&__managedcode_a, (uint32_t)((char *)&__managedcode_z - (char*)&__managedcode_a), + (void*)&__unbox_a, (uint32_t)((char *)&__unbox_z - (char*)&__unbox_a), (void **)&c_classlibFunctions, _countof(c_classlibFunctions))) { return -1; } #endif // !CPPCODEGEN -#ifdef CPPCODEGEN - ReversePInvokeFrame frame; - __reverse_pinvoke(&frame); -#endif - #ifndef CPPCODEGEN InitializeModules(osModule, __modules_a, (int)((__modules_z - __modules_a)), (void **)&c_classlibFunctions, _countof(c_classlibFunctions)); +#elif defined _WASM_ + // WASMTODO: Figure out what to do here. This is a NativeCallable method in the runtime + // and we also would have to figure out what to pass for pModuleHeaders #else // !CPPCODEGEN InitializeModules(nullptr, (void**)RtRHeaderWrapper(), 2, nullptr, 0); #endif // !CPPCODEGEN +#ifdef CORERT_DLL + // Run startup method immediately for a native library + __managed__Startup(); +#endif // CORERT_DLL + + return 0; +} + +#ifndef CORERT_DLL +#if defined(_WIN32) +int __cdecl wmain(int argc, wchar_t* argv[]) +#else +int main(int argc, char* argv[]) +#endif +{ + int initval = InitializeRuntime(); + if (initval != 0) + return initval; + int retval; #ifdef CPPCODEGEN try @@ -333,12 +373,18 @@ int main(int argc, char* argv[]) retval = -1; } #endif - -#ifdef CPPCODEGEN - __reverse_pinvoke_return(&frame); -#endif - RhpShutdown(); return retval; } +#endif // !CORERT_DLL + +#ifdef CORERT_DLL +static struct InitializeRuntimePointerHelper +{ + InitializeRuntimePointerHelper() + { + RhSetRuntimeInitializationCallback(&InitializeRuntime); + } +} initializeRuntimePointerHelper; +#endif // CORERT_DLL diff --git a/external/corert/src/Native/CMakeLists.txt b/external/corert/src/Native/CMakeLists.txt index 91f5d9a717..219d689364 100644 --- a/external/corert/src/Native/CMakeLists.txt +++ b/external/corert/src/Native/CMakeLists.txt @@ -4,9 +4,14 @@ project(CoreRT) # Include cmake functions include(functions.cmake) -string(FIND ${CMAKE_GENERATOR} "Visual Studio 15 2017" VS2017_POS) -if(VS2017_POS EQUAL 0 AND (CMAKE_VERSION VERSION_LESS 3.6.0)) - message(FATAL_ERROR "CMake version 3.6.0 or higher is required to compile targeting VS 2017") +if(CMAKE_SYSTEM_NAME STREQUAL Windows) + # CMake version 3.8.0 or higher is required to compile targeting VS 2017 + cmake_minimum_required(VERSION 3.8.0) +endif() + +if(CMAKE_SYSTEM_NAME STREQUAL Darwin) + # CMake version 3.4.0 or higher has bug fixes required to compile targeting Darwin + cmake_minimum_required(VERSION 3.4.0) endif() set(CMAKE_MACOSX_RPATH ON) @@ -19,73 +24,64 @@ endif() set(CMAKE_SHARED_LIBRARY_PREFIX "") function(clr_unknown_arch) - if (WIN32) - message(FATAL_ERROR "Only AMD64 and I386 are supported") - else() - message(FATAL_ERROR "Only AMD64, ARM64, ARM and ARMEL are supported") - endif() + message(FATAL_ERROR "Only AMD64, ARM64, ARM, ARMEL, I386 and WASM are supported") endfunction() -if (CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64 OR CMAKE_SYSTEM_PROCESSOR STREQUAL amd64) - set(IS_64BIT_BUILD 1) -elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL armv7l) +if(CLR_CMAKE_TARGET_ARCH STREQUAL x64) + set(CLR_CMAKE_PLATFORM_ARCH_AMD64 1) + add_definitions(-D_TARGET_AMD64_=1) + add_definitions(-D_AMD64_) + add_definitions(-DBIT64=1) +elseif(CLR_CMAKE_TARGET_ARCH MATCHES "^(arm|armel)$") + set(CLR_CMAKE_PLATFORM_ARCH_ARM 1) + add_definitions(-D_TARGET_ARM_=1) + add_definitions(-D_ARM_) add_definitions(-DBIT32=1) - # Because we don't use CMAKE_C_COMPILER/CMAKE_CXX_COMPILER to use clang - # we have to set the triple by adding a compiler argument - if(TOOLCHAIN STREQUAL arm-linux-gnueabi) - add_compile_options(-target armv7-linux-gnueabi) - add_compile_options(-mfloat-abi=softfp) - else () - add_compile_options(-target armv7-linux-gnueabihf) - endif () - add_compile_options(-mthumb) - add_compile_options(-mfpu=vfpv3) -elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64) - set(IS_64BIT_BUILD 1) - # Because we don't use CMAKE_C_COMPILER/CMAKE_CXX_COMPILER to use clang - # we have to set the triple by adding a compiler argument - add_compile_options(-target aarch64-linux-gnu) -endif () +elseif(CLR_CMAKE_TARGET_ARCH STREQUAL arm64) + set(CLR_CMAKE_PLATFORM_ARCH_ARM64 1) + add_definitions(-D_TARGET_ARM64_=1) + add_definitions(-D_ARM64_) + add_definitions(-DBIT64=1) +elseif(CLR_CMAKE_TARGET_ARCH STREQUAL x86) + set(CLR_CMAKE_PLATFORM_ARCH_I386 1) + add_definitions(-D_TARGET_X86_=1) + add_definitions(-D_X86_) + add_definitions(-DBIT32=1) +elseif(CLR_CMAKE_TARGET_ARCH STREQUAL wasm) + set(CLR_CMAKE_PLATFORM_ARCH_WASM 1) + add_definitions(-D_TARGET_WASM_=1) + add_definitions(-D_WASM_) + add_definitions(-DBIT32=1) +else() + clr_unknown_arch() +endif() if(CMAKE_SYSTEM_NAME STREQUAL Darwin) - set(CLR_CMAKE_PLATFORM_UNIX 1) - set(CLR_CMAKE_PLATFORM_UNIX_TARGET_AMD64 1) - set(CLR_CMAKE_PLATFORM_DARWIN 1) - if(CMAKE_VERSION VERSION_LESS "3.4.0") - set(CMAKE_ASM_COMPILE_OBJECT "${CMAKE_C_COMPILER} -o -c ") - else() + set(CLR_CMAKE_PLATFORM_UNIX 1) + set(CLR_CMAKE_PLATFORM_DARWIN 1) set(CMAKE_ASM_COMPILE_OBJECT "${CMAKE_C_COMPILER} -o -c ") - endif(CMAKE_VERSION VERSION_LESS "3.4.0") endif(CMAKE_SYSTEM_NAME STREQUAL Darwin) if(CMAKE_SYSTEM_NAME STREQUAL Linux) set(CLR_CMAKE_PLATFORM_UNIX 1) - if(CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64) - set(CLR_CMAKE_PLATFORM_UNIX_TARGET_AMD64 1) - elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL armv7l) - set(CLR_CMAKE_PLATFORM_UNIX_TARGET_ARM 1) - elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64) - set(CLR_CMAKE_PLATFORM_UNIX_TARGET_ARM64 1) - else() - clr_unknown_arch() - endif() set(CLR_CMAKE_PLATFORM_LINUX 1) endif(CMAKE_SYSTEM_NAME STREQUAL Linux) +if(CMAKE_SYSTEM_NAME STREQUAL FreeBSD) + set(CLR_CMAKE_PLATFORM_UNIX 1) + set(CLR_CMAKE_PLATFORM_FREEBSD 1) +endif(CMAKE_SYSTEM_NAME STREQUAL FreeBSD) + if(CMAKE_SYSTEM_NAME STREQUAL NetBSD) set(CLR_CMAKE_PLATFORM_UNIX 1) - if(CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64) - set(CLR_CMAKE_PLATFORM_UNIX_TARGET_AMD64 1) - elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL armv7l) - set(CLR_CMAKE_PLATFORM_UNIX_TARGET_ARM 1) - elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64) - set(CLR_CMAKE_PLATFORM_UNIX_TARGET_ARM64 1) - else() - clr_unknown_arch() - endif() set(CLR_CMAKE_PLATFORM_NETBSD 1) endif(CMAKE_SYSTEM_NAME STREQUAL NetBSD) +if(CMAKE_SYSTEM_NAME STREQUAL Emscripten) + set(CLR_CMAKE_PLATFORM_UNIX 1) + set(CLR_CMAKE_PLATFORM_WASM 1) +endif(CMAKE_SYSTEM_NAME STREQUAL Emscripten) + if (CLR_CMAKE_PLATFORM_UNIX) include_directories(inc/unix) @@ -99,10 +95,34 @@ if (CLR_CMAKE_PLATFORM_UNIX) add_compile_options(-Wno-null-arithmetic) add_compile_options(-Wno-null-conversion) - if (CLR_CMAKE_PLATFORM_UNIX_TARGET_AMD64) + if (CLR_CMAKE_PLATFORM_ARCH_AMD64 OR CLR_CMAKE_PLATFORM_ARCH_I386) # Allow 16 byte compare-exchange add_compile_options(-mcx16) + endif() + + if (CLR_CMAKE_PLATFORM_ARCH_ARM) + # Because we don't use CMAKE_C_COMPILER/CMAKE_CXX_COMPILER to use clang + # we have to set the triple by adding a compiler argument + if(TOOLCHAIN STREQUAL arm-linux-gnueabi) + add_compile_options(-target armv7-linux-gnueabi) + add_compile_options(-mfloat-abi=softfp) + else () + add_compile_options(-target armv7-linux-gnueabihf) + endif () + add_compile_options(-mthumb) + add_compile_options(-mfpu=vfpv3) + endif() + + if (CLR_CMAKE_PLATFORM_ARCH_ARM64) + # Because we don't use CMAKE_C_COMPILER/CMAKE_CXX_COMPILER to use clang + # we have to set the triple by adding a compiler argument + add_compile_options(-target aarch64-linux-gnu) + endif () + + if (CLR_CMAKE_PLATFORM_ARCH_AMD64) add_definitions(-DUNIX_AMD64_ABI) + elseif (CLR_CMAKE_PLATFORM_ARCH_I386) + add_definitions(-DUNIX_X86_ABI) endif() # Disable strict warning on unused functions. @@ -122,32 +142,10 @@ if (CLR_CMAKE_PLATFORM_UNIX) endif(CLR_CMAKE_PLATFORM_DARWIN) if(CLR_CMAKE_PLATFORM_LINUX) - set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -Wa,--noexecstack") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -Wa,--noexecstack") endif(CLR_CMAKE_PLATFORM_LINUX) endif(CLR_CMAKE_PLATFORM_UNIX) -if(CLR_CMAKE_PLATFORM_UNIX_TARGET_ARM) - set(CLR_CMAKE_PLATFORM_ARCH_ARM 1) -elseif(CLR_CMAKE_PLATFORM_UNIX_TARGET_ARM64) - set(CLR_CMAKE_PLATFORM_ARCH_ARM64 1) -elseif(CLR_CMAKE_PLATFORM_UNIX_TARGET_AMD64) - set(CLR_CMAKE_PLATFORM_ARCH_AMD64 1) -elseif(WIN32) - if (CLR_CMAKE_TARGET_ARCH STREQUAL x64) - set(CLR_CMAKE_PLATFORM_ARCH_AMD64 1) - set(IS_64BIT_BUILD 1) - elseif(CLR_CMAKE_TARGET_ARCH STREQUAL x86) - set(CLR_CMAKE_PLATFORM_ARCH_I386 1) - set(IS_64BIT_BUILD 0) - else() - clr_unknown_arch() - endif() -endif() - -if(IS_64BIT_BUILD) - add_definitions(-DBIT64=1) -endif(IS_64BIT_BUILD) - if(WIN32) enable_language(ASM_MASM) else() @@ -161,7 +159,7 @@ function(get_compile_definitions DefinitionName) foreach(DEFINITION IN LISTS COMPILE_DEFINITIONS_LIST) if (${DEFINITION} MATCHES "^\\$<\\$]+)>:([^>]+)>$") - # The entries that contain generator expressions must have the -D inside of the + # The entries that contain generator expressions must have the -D inside of the # expression. So we transform e.g. $<$:_DEBUG> to $<$:-D_DEBUG> set(DEFINITION "$<$:-D${CMAKE_MATCH_2}>") else() @@ -172,25 +170,9 @@ function(get_compile_definitions DefinitionName) set(${DefinitionName} ${DEFINITIONS} PARENT_SCOPE) endfunction(get_compile_definitions) -if(CLR_CMAKE_PLATFORM_ARCH_AMD64) - add_definitions(-D_TARGET_AMD64_=1) - add_definitions(-D_AMD64_) -elseif(CLR_CMAKE_PLATFORM_ARCH_I386) - add_definitions(-D_TARGET_X86_=1) - add_definitions(-D_X86_) -elseif(CLR_CMAKE_PLATFORM_ARCH_ARM) - add_definitions(-D_TARGET_ARM_=1) - add_definitions(-D_ARM_) -elseif(CLR_CMAKE_PLATFORM_ARCH_ARM64) - add_definitions(-D_TARGET_ARM64_=1) - add_definitions(-D_ARM64_) -else() - clr_unknown_arch() -endif() - # Set the passed in RetSources variable to the list of sources with added current source directory -# to form absolute paths. -# The parameters after the RetSources are the input files. +# to form absolute paths. +# The parameters after the RetSources are the input files. function(convert_to_absolute_path RetSources) set(Sources ${ARGN}) foreach(Source IN LISTS Sources) @@ -204,8 +186,13 @@ if(WIN32) add_compile_options($<$:-DDEBUG>) add_compile_options($<$:/MTd>) add_compile_options($<$:/MT>) + add_compile_options(/source-charset:utf-8) # Force MSVC to compile source as UTF-8. add_compile_options(/Zi) # enable debugging information set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEBUG") + + if (CLR_CMAKE_PLATFORM_ARCH_I386) + add_compile_options(/Gz) + endif (CLR_CMAKE_PLATFORM_ARCH_I386) else(WIN32) string(TOUPPER ${CMAKE_BUILD_TYPE} UPPERCASE_CMAKE_BUILD_TYPE) if (UPPERCASE_CMAKE_BUILD_TYPE STREQUAL DEBUG) @@ -213,23 +200,35 @@ if (UPPERCASE_CMAKE_BUILD_TYPE STREQUAL DEBUG) add_definitions(-DDEBUG) add_definitions(-D_DEBUG) elseif (UPPERCASE_CMAKE_BUILD_TYPE STREQUAL RELEASE) - add_compile_options (-O3) + add_compile_options (-g -O3) add_definitions(-DNDEBUG) else () message(FATAL_ERROR "Unknown build type. Set CMAKE_BUILD_TYPE to DEBUG or RELEASE.") endif () +if (CLR_CMAKE_PLATFORM_ARCH_I386) + add_compile_options(-m32) + link_libraries(-m32) +endif() endif (WIN32) include(configure.cmake) if(WIN32) - add_subdirectory(gc) + add_subdirectory(gc) endif() add_subdirectory(Runtime) add_subdirectory(Bootstrap) -add_subdirectory(jitinterface) + +if(NOT CLR_CMAKE_PLATFORM_WASM) + add_subdirectory(jitinterface) +endif(NOT CLR_CMAKE_PLATFORM_WASM) # We don't need the PAL on Windows. -if(CLR_CMAKE_PLATFORM_UNIX) - add_subdirectory(System.Private.CoreLib.Native) +if(NOT WIN32) + add_subdirectory(System.Private.CoreLib.Native) +endif(NOT WIN32) + +# Build ObjWriter on Linux only +if(CMAKE_SYSTEM_NAME STREQUAL Linux AND OBJWRITER_BUILD) + add_subdirectory(ObjWriter/llvmCap) endif() diff --git a/external/corert/src/Native/ObjWriter/.nuget/Microsoft.DotNet.ObjectWriter.nuspec b/external/corert/src/Native/ObjWriter/.nuget/Microsoft.DotNet.ObjectWriter.nuspec new file mode 100644 index 0000000000..f3421bb32a --- /dev/null +++ b/external/corert/src/Native/ObjWriter/.nuget/Microsoft.DotNet.ObjectWriter.nuspec @@ -0,0 +1,20 @@ + + + + Microsoft.DotNet.ObjectWriter + 1.0.19-prerelease-00001 + Microsoft .NET Object File Generator + Microsoft + Microsoft + http://go.microsoft.com/fwlink/?LinkId=329770 + https://github.com/dotnet/corert + http://go.microsoft.com/fwlink/?LinkID=288859 + true + Provides object writer to the managed to native code-generator. + Initial release + Copyright © Microsoft Corporation + + + + + diff --git a/external/corert/src/Native/ObjWriter/.nuget/runtime.json b/external/corert/src/Native/ObjWriter/.nuget/runtime.json new file mode 100644 index 0000000000..c9c5b8e6b1 --- /dev/null +++ b/external/corert/src/Native/ObjWriter/.nuget/runtime.json @@ -0,0 +1,19 @@ +{ + "runtimes": { + "win7-x64": { + "Microsoft.DotNet.ObjectWriter": { + "toolchain.win7-x64.Microsoft.DotNet.ObjectWriter": "1.0.19-prerelease-00001" + } + }, + "ubuntu.14.04-x64": { + "Microsoft.DotNet.ObjectWriter": { + "toolchain.ubuntu.14.04-x64.Microsoft.DotNet.ObjectWriter": "1.0.19-prerelease-00001" + } + }, + "osx.10.10-x64": { + "Microsoft.DotNet.ObjectWriter": { + "toolchain.osx.10.10-x64.Microsoft.DotNet.ObjectWriter": "1.0.19-prerelease-00001" + } + } + } +} diff --git a/external/corert/src/Native/ObjWriter/.nuget/toolchain.osx.10.10-x64.Microsoft.DotNet.ObjectWriter.nuspec b/external/corert/src/Native/ObjWriter/.nuget/toolchain.osx.10.10-x64.Microsoft.DotNet.ObjectWriter.nuspec new file mode 100644 index 0000000000..7a571beaf4 --- /dev/null +++ b/external/corert/src/Native/ObjWriter/.nuget/toolchain.osx.10.10-x64.Microsoft.DotNet.ObjectWriter.nuspec @@ -0,0 +1,20 @@ + + + + toolchain.osx.10.10-x64.Microsoft.DotNet.ObjectWriter + 1.0.19-prerelease-00001 + Microsoft .NET Object File Generator + Microsoft + Microsoft + http://go.microsoft.com/fwlink/?LinkId=329770 + https://github.com/dotnet/corert + http://go.microsoft.com/fwlink/?LinkID=288859 + true + Provides object writer to the managed to native code-generator. + Initial release + Copyright © Microsoft Corporation + + + + + diff --git a/external/corert/src/Native/ObjWriter/.nuget/toolchain.ubuntu.14.04-x64.Microsoft.DotNet.ObjectWriter.nuspec b/external/corert/src/Native/ObjWriter/.nuget/toolchain.ubuntu.14.04-x64.Microsoft.DotNet.ObjectWriter.nuspec new file mode 100644 index 0000000000..fd9c514c65 --- /dev/null +++ b/external/corert/src/Native/ObjWriter/.nuget/toolchain.ubuntu.14.04-x64.Microsoft.DotNet.ObjectWriter.nuspec @@ -0,0 +1,20 @@ + + + + toolchain.ubuntu.14.04-x64.Microsoft.DotNet.ObjectWriter + 1.0.19-prerelease-00001 + Microsoft .NET Object File Generator + Microsoft + Microsoft + http://go.microsoft.com/fwlink/?LinkId=329770 + https://github.com/dotnet/corert + http://go.microsoft.com/fwlink/?LinkID=288859 + true + Provides object writer to the managed to native code-generator. + Initial release + Copyright © Microsoft Corporation + + + + + diff --git a/external/corert/src/Native/ObjWriter/.nuget/toolchain.win7-x64.Microsoft.DotNet.ObjectWriter.nuspec b/external/corert/src/Native/ObjWriter/.nuget/toolchain.win7-x64.Microsoft.DotNet.ObjectWriter.nuspec new file mode 100644 index 0000000000..a54cdc2303 --- /dev/null +++ b/external/corert/src/Native/ObjWriter/.nuget/toolchain.win7-x64.Microsoft.DotNet.ObjectWriter.nuspec @@ -0,0 +1,20 @@ + + + + toolchain.win7-x64.Microsoft.DotNet.ObjectWriter + 1.0.19-prerelease-00001 + Microsoft .NET Object File Generator + Microsoft + Microsoft + http://go.microsoft.com/fwlink/?LinkId=329770 + https://github.com/dotnet/corert + http://go.microsoft.com/fwlink/?LinkID=288859 + true + Provides object writer to the managed to native code-generator. + Initial release + Copyright © Microsoft Corporation + + + + + diff --git a/external/corert/src/Native/ObjWriter/CMakeLists.txt b/external/corert/src/Native/ObjWriter/CMakeLists.txt new file mode 100644 index 0000000000..fae4ddb12b --- /dev/null +++ b/external/corert/src/Native/ObjWriter/CMakeLists.txt @@ -0,0 +1,46 @@ +project(objwriter) + +set(CMAKE_BUILD_TYPE "${OBJWRITER_BUILD_TYPE}") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OBJWRITER_C_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OBJWRITER_CXX_FLAGS} -std=c++11 -fno-rtti") + +message(STATUS "ObjWriter configuring with (${CMAKE_BUILD_TYPE}) build type and (${LLVM_DEFAULT_TARGET_TRIPLE}) default target triple") + +include_directories(${LLVM_INCLUDE_DIRS}) +include_directories(.) +add_definitions(${LLVM_DEFINITIONS}) + +if (WIN32) + # Create .def file containing a list of exports preceeded by + # 'EXPORTS'. The file "objwriter.exports" already contains the list, so we + # massage it into the correct format here to create "objwriter.exports.def". + set(OBJWRITER_EXPORTS_DEF ${CMAKE_CURRENT_BINARY_DIR}/objwriter.exports.def) + set(OBJWRITER_EXPORTS_DEF_TEMP ${OBJWRITER_EXPORTS_DEF}.txt) + file(READ "objwriter.exports" exports_list) + file(WRITE ${OBJWRITER_EXPORTS_DEF_TEMP} "LIBRARY OBJWRITER\n") + file(APPEND ${OBJWRITER_EXPORTS_DEF_TEMP} "EXPORTS\n") + file(APPEND ${OBJWRITER_EXPORTS_DEF_TEMP} ${exports_list}) + # Copy the file only if it has changed. + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${OBJWRITER_EXPORTS_DEF_TEMP} ${OBJWRITER_EXPORTS_DEF}) +endif() + +# Now build our tools +add_library(objwriter + SHARED + objwriter.cpp + typeBuilder.cpp + objwriter.h # Visual Studio generator doesn't include necessary header files into the project automatically + typeBuilder.h + ${OBJWRITER_EXPORTS_DEF} +) + +# Find the libraries that correspond to the LLVM components +# that we wish to use +llvm_map_components_to_libnames(llvm_libs + ${LLVM_TARGETS_TO_BUILD} +) + +# Link against LLVM libraries +target_link_libraries(objwriter +${llvm_libs}) diff --git a/external/corert/src/Native/ObjWriter/cfi.h b/external/corert/src/Native/ObjWriter/cfi.h new file mode 100644 index 0000000000..fc359acd30 --- /dev/null +++ b/external/corert/src/Native/ObjWriter/cfi.h @@ -0,0 +1,37 @@ +// 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. + +// +// Keep in sync with https://github.com/dotnet/coreclr/blob/master/src/inc/cfi.h +// + +#ifndef CFI_H_ +#define CFI_H_ + +#define DWARF_REG_ILLEGAL -1 +enum CFI_OPCODE +{ + CFI_ADJUST_CFA_OFFSET, // Offset is adjusted relative to the current one. + CFI_DEF_CFA_REGISTER, // New register is used to compute CFA + CFI_REL_OFFSET // Register is saved at offset from the current CFA +}; + +struct CFI_CODE +{ + unsigned char CodeOffset;// Offset from the start of code the frame covers. + unsigned char CfiOpCode; + short DwarfReg; // Dwarf register number. 0~32 for x64. + int Offset; + CFI_CODE(unsigned char codeOffset, unsigned char cfiOpcode, + short dwarfReg, int offset) + : CodeOffset(codeOffset) + , CfiOpCode(cfiOpcode) + , DwarfReg(dwarfReg) + , Offset(offset) + {} +}; +typedef CFI_CODE* PCFI_CODE; + +#endif // CFI_H + diff --git a/external/corert/src/Native/ObjWriter/cordebuginfo.h b/external/corert/src/Native/ObjWriter/cordebuginfo.h new file mode 100644 index 0000000000..1ed430a11d --- /dev/null +++ b/external/corert/src/Native/ObjWriter/cordebuginfo.h @@ -0,0 +1,327 @@ +// 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. + +// +// Keep in sync with https://github.com/dotnet/coreclr/blob/master/src/inc/cordebuginfo.h +// + +/**********************************************************************************/ +// DebugInfo types shared by JIT-EE interface and EE-Debugger interface + +class ICorDebugInfo +{ +public: + /*----------------------------- Boundary-info ---------------------------*/ + + enum MappingTypes + { + NO_MAPPING = -1, + PROLOG = -2, + EPILOG = -3, + MAX_MAPPING_VALUE = -3 // Sentinal value. This should be set to the largest magnitude value in the enum + // so that the compression routines know the enum's range. + }; + + enum BoundaryTypes + { + NO_BOUNDARIES = 0x00, // No implicit boundaries + STACK_EMPTY_BOUNDARIES = 0x01, // Boundary whenever the IL evaluation stack is empty + NOP_BOUNDARIES = 0x02, // Before every CEE_NOP instruction + CALL_SITE_BOUNDARIES = 0x04, // Before every CEE_CALL, CEE_CALLVIRT, etc instruction + + // Set of boundaries that debugger should always reasonably ask the JIT for. + DEFAULT_BOUNDARIES = STACK_EMPTY_BOUNDARIES | NOP_BOUNDARIES | CALL_SITE_BOUNDARIES + }; + + // Note that SourceTypes can be OR'd together - it's possible that + // a sequence point will also be a stack_empty point, and/or a call site. + // The debugger will check to see if a boundary offset's source field & + // SEQUENCE_POINT is true to determine if the boundary is a sequence point. + + enum SourceTypes + { + SOURCE_TYPE_INVALID = 0x00, // To indicate that nothing else applies + SEQUENCE_POINT = 0x01, // The debugger asked for it. + STACK_EMPTY = 0x02, // The stack is empty here + CALL_SITE = 0x04, // This is a call site. + NATIVE_END_OFFSET_UNKNOWN = 0x08, // Indicates a epilog endpoint + CALL_INSTRUCTION = 0x10 // The actual instruction of a call. + + }; + + struct OffsetMapping + { + DWORD nativeOffset; + DWORD ilOffset; + SourceTypes source; // The debugger needs this so that + // we don't put Edit and Continue breakpoints where + // the stack isn't empty. We can put regular breakpoints + // there, though, so we need a way to discriminate + // between offsets. + }; + + /*------------------------------ Var-info -------------------------------*/ + + // Note: The debugger needs to target register numbers on platforms other than which the debugger itself + // is running. To this end it maintains its own values for REGNUM_SP and REGNUM_AMBIENT_SP across multiple + // platforms. So any change here that may effect these values should be reflected in the definitions + // contained in debug/inc/DbgIPCEvents.h. + enum RegNum + { +#ifdef _TARGET_X86_ + REGNUM_EAX, + REGNUM_ECX, + REGNUM_EDX, + REGNUM_EBX, + REGNUM_ESP, + REGNUM_EBP, + REGNUM_ESI, + REGNUM_EDI, +#elif _TARGET_ARM_ + REGNUM_R0, + REGNUM_R1, + REGNUM_R2, + REGNUM_R3, + REGNUM_R4, + REGNUM_R5, + REGNUM_R6, + REGNUM_R7, + REGNUM_R8, + REGNUM_R9, + REGNUM_R10, + REGNUM_R11, + REGNUM_R12, + REGNUM_SP, + REGNUM_LR, + REGNUM_PC, +#elif _TARGET_ARM64_ + REGNUM_X0, + REGNUM_X1, + REGNUM_X2, + REGNUM_X3, + REGNUM_X4, + REGNUM_X5, + REGNUM_X6, + REGNUM_X7, + REGNUM_X8, + REGNUM_X9, + REGNUM_X10, + REGNUM_X11, + REGNUM_X12, + REGNUM_X13, + REGNUM_X14, + REGNUM_X15, + REGNUM_X16, + REGNUM_X17, + REGNUM_X18, + REGNUM_X19, + REGNUM_X20, + REGNUM_X21, + REGNUM_X22, + REGNUM_X23, + REGNUM_X24, + REGNUM_X25, + REGNUM_X26, + REGNUM_X27, + REGNUM_X28, + REGNUM_FP, + REGNUM_LR, + REGNUM_SP, + REGNUM_PC, +#elif _TARGET_AMD64_ + REGNUM_RAX, + REGNUM_RCX, + REGNUM_RDX, + REGNUM_RBX, + REGNUM_RSP, + REGNUM_RBP, + REGNUM_RSI, + REGNUM_RDI, + REGNUM_R8, + REGNUM_R9, + REGNUM_R10, + REGNUM_R11, + REGNUM_R12, + REGNUM_R13, + REGNUM_R14, + REGNUM_R15, +#else + PORTABILITY_WARNING("Register numbers not defined on this platform") +#endif + REGNUM_COUNT, + REGNUM_AMBIENT_SP, // ambient SP support. Ambient SP is the original SP in the non-BP based frame. + // Ambient SP should not change even if there are push/pop operations in the method. + +#ifdef _TARGET_X86_ + REGNUM_FP = REGNUM_EBP, + REGNUM_SP = REGNUM_ESP, +#elif _TARGET_AMD64_ + REGNUM_SP = REGNUM_RSP, +#elif _TARGET_ARM_ +#ifdef REDHAWK + REGNUM_FP = REGNUM_R7, +#else + REGNUM_FP = REGNUM_R11, +#endif //REDHAWK +#elif _TARGET_ARM64_ + //Nothing to do here. FP is already alloted. +#else + // RegNum values should be properly defined for this platform + REGNUM_FP = 0, + REGNUM_SP = 1, +#endif + + }; + + // VarLoc describes the location of a native variable. Note that currently, VLT_REG_BYREF and VLT_STK_BYREF + // are only used for value types on X64. + + enum VarLocType + { + VLT_REG, // variable is in a register + VLT_REG_BYREF, // address of the variable is in a register + VLT_REG_FP, // variable is in an fp register + VLT_STK, // variable is on the stack (memory addressed relative to the frame-pointer) + VLT_STK_BYREF, // address of the variable is on the stack (memory addressed relative to the frame-pointer) + VLT_REG_REG, // variable lives in two registers + VLT_REG_STK, // variable lives partly in a register and partly on the stack + VLT_STK_REG, // reverse of VLT_REG_STK + VLT_STK2, // variable lives in two slots on the stack + VLT_FPSTK, // variable lives on the floating-point stack + VLT_FIXED_VA, // variable is a fixed argument in a varargs function (relative to VARARGS_HANDLE) + + VLT_COUNT, + VLT_INVALID, + }; + + struct VarLoc + { + VarLocType vlType; + + union + { + // VLT_REG/VLT_REG_FP -- Any pointer-sized enregistered value (TYP_INT, TYP_REF, etc) + // eg. EAX + // VLT_REG_BYREF -- the specified register contains the address of the variable + // eg. [EAX] + + struct + { + RegNum vlrReg; + } vlReg; + + // VLT_STK -- Any 32 bit value which is on the stack + // eg. [ESP+0x20], or [EBP-0x28] + // VLT_STK_BYREF -- the specified stack location contains the address of the variable + // eg. mov EAX, [ESP+0x20]; [EAX] + + struct + { + RegNum vlsBaseReg; + signed vlsOffset; + } vlStk; + + // VLT_REG_REG -- TYP_LONG with both DWords enregistred + // eg. RBM_EAXEDX + + struct + { + RegNum vlrrReg1; + RegNum vlrrReg2; + } vlRegReg; + + // VLT_REG_STK -- Partly enregistered TYP_LONG + // eg { LowerDWord=EAX UpperDWord=[ESP+0x8] } + + struct + { + RegNum vlrsReg; + struct + { + RegNum vlrssBaseReg; + signed vlrssOffset; + } vlrsStk; + } vlRegStk; + + // VLT_STK_REG -- Partly enregistered TYP_LONG + // eg { LowerDWord=[ESP+0x8] UpperDWord=EAX } + + struct + { + struct + { + RegNum vlsrsBaseReg; + signed vlsrsOffset; + } vlsrStk; + RegNum vlsrReg; + } vlStkReg; + + // VLT_STK2 -- Any 64 bit value which is on the stack, + // in 2 successsive DWords. + // eg 2 DWords at [ESP+0x10] + + struct + { + RegNum vls2BaseReg; + signed vls2Offset; + } vlStk2; + + // VLT_FPSTK -- enregisterd TYP_DOUBLE (on the FP stack) + // eg. ST(3). Actually it is ST("FPstkHeigth - vpFpStk") + + struct + { + unsigned vlfReg; + } vlFPstk; + + // VLT_FIXED_VA -- fixed argument of a varargs function. + // The argument location depends on the size of the variable + // arguments (...). Inspecting the VARARGS_HANDLE indicates the + // location of the first arg. This argument can then be accessed + // relative to the position of the first arg + + struct + { + unsigned vlfvOffset; + } vlFixedVarArg; + + // VLT_MEMORY + + struct + { + void *rpValue; // pointer to the in-process + // location of the value. + } vlMemory; + }; + }; + + // This is used to report implicit/hidden arguments + + enum + { + VARARGS_HND_ILNUM = -1, // Value for the CORINFO_VARARGS_HANDLE varNumber + RETBUF_ILNUM = -2, // Pointer to the return-buffer + TYPECTXT_ILNUM = -3, // ParamTypeArg for CORINFO_GENERICS_CTXT_FROM_PARAMTYPEARG + + UNKNOWN_ILNUM = -4, // Unknown variable + + MAX_ILNUM = -4 // Sentinal value. This should be set to the largest magnitude value in th enum + // so that the compression routines know the enum's range. + }; + + struct ILVarInfo + { + DWORD startOffset; + DWORD endOffset; + DWORD varNumber; + }; + + struct NativeVarInfo + { + DWORD startOffset; + DWORD endOffset; + DWORD varNumber; + VarLoc loc; + }; +}; diff --git a/external/corert/src/Native/ObjWriter/cvconst.h b/external/corert/src/Native/ObjWriter/cvconst.h new file mode 100644 index 0000000000..5c4f2569e2 --- /dev/null +++ b/external/corert/src/Native/ObjWriter/cvconst.h @@ -0,0 +1,3716 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +// cvconst.h - codeview constant definitions +//----------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. +// +//--------------------------------------------------------------- +#ifndef _CVCONST_H_ +#define _CVCONST_H_ + +// Enumeration for function call type + +typedef enum CV_call_e { + CV_CALL_NEAR_C = 0x00, // near right to left push, caller pops stack + CV_CALL_FAR_C = 0x01, // far right to left push, caller pops stack + CV_CALL_NEAR_PASCAL = 0x02, // near left to right push, callee pops stack + CV_CALL_FAR_PASCAL = 0x03, // far left to right push, callee pops stack + CV_CALL_NEAR_FAST = + 0x04, // near left to right push with regs, callee pops stack + CV_CALL_FAR_FAST = + 0x05, // far left to right push with regs, callee pops stack + CV_CALL_SKIPPED = 0x06, // skipped (unused) call index + CV_CALL_NEAR_STD = 0x07, // near standard call + CV_CALL_FAR_STD = 0x08, // far standard call + CV_CALL_NEAR_SYS = 0x09, // near sys call + CV_CALL_FAR_SYS = 0x0a, // far sys call + CV_CALL_THISCALL = 0x0b, // this call (this passed in register) + CV_CALL_MIPSCALL = 0x0c, // Mips call + CV_CALL_GENERIC = 0x0d, // Generic call sequence + CV_CALL_ALPHACALL = 0x0e, // Alpha call + CV_CALL_PPCCALL = 0x0f, // PPC call + CV_CALL_SHCALL = 0x10, // Hitachi SuperH call + CV_CALL_ARMCALL = 0x11, // ARM call + CV_CALL_AM33CALL = 0x12, // AM33 call + CV_CALL_TRICALL = 0x13, // TriCore Call + CV_CALL_SH5CALL = 0x14, // Hitachi SuperH-5 call + CV_CALL_M32RCALL = 0x15, // M32R Call + CV_CALL_CLRCALL = 0x16, // clr call + CV_CALL_INLINE = + 0x17, // Marker for routines always inlined and thus lacking a convention + CV_CALL_NEAR_VECTOR = + 0x18, // near left to right push with regs, callee pops stack + CV_CALL_RESERVED = 0x19 // first unused call enumeration + + // Do NOT add any more machine specific conventions. This is to be used for + // calling conventions in the source only (e.g. __cdecl, __stdcall). +} CV_call_e; + +// Values for the access protection of class attributes + +typedef enum CV_access_e { + CV_private = 1, + CV_protected = 2, + CV_public = 3 +} CV_access_e; + +typedef enum THUNK_ORDINAL { + THUNK_ORDINAL_NOTYPE, // standard thunk + THUNK_ORDINAL_ADJUSTOR, // "this" adjustor thunk + THUNK_ORDINAL_VCALL, // virtual call thunk + THUNK_ORDINAL_PCODE, // pcode thunk + THUNK_ORDINAL_LOAD, // thunk which loads the address to jump to + // via unknown means... + + // trampoline thunk ordinals - only for use in Trampoline thunk symbols + THUNK_ORDINAL_TRAMP_INCREMENTAL, + THUNK_ORDINAL_TRAMP_BRANCHISLAND, + +} THUNK_ORDINAL; + +enum CV_SourceChksum_t { + CHKSUM_TYPE_NONE = 0, // indicates no checksum is available + CHKSUM_TYPE_MD5, + CHKSUM_TYPE_SHA1, + CHKSUM_TYPE_SHA_256, +}; + +// +// DIA enums +// + +enum SymTagEnum { + SymTagNull, + SymTagExe, + SymTagCompiland, + SymTagCompilandDetails, + SymTagCompilandEnv, + SymTagFunction, + SymTagBlock, + SymTagData, + SymTagAnnotation, + SymTagLabel, + SymTagPublicSymbol, + SymTagUDT, + SymTagEnum, + SymTagFunctionType, + SymTagPointerType, + SymTagArrayType, + SymTagBaseType, + SymTagTypedef, + SymTagBaseClass, + SymTagFriend, + SymTagFunctionArgType, + SymTagFuncDebugStart, + SymTagFuncDebugEnd, + SymTagUsingNamespace, + SymTagVTableShape, + SymTagVTable, + SymTagCustom, + SymTagThunk, + SymTagCustomType, + SymTagManagedType, + SymTagDimension, + SymTagCallSite, + SymTagInlineSite, + SymTagBaseInterface, + SymTagVectorType, + SymTagMatrixType, + SymTagHLSLType, + SymTagCaller, + SymTagCallee, + SymTagExport, + SymTagHeapAllocationSite, + SymTagCoffGroup, + SymTagMax +}; + +enum LocationType { + LocIsNull, + LocIsStatic, + LocIsTLS, + LocIsRegRel, + LocIsThisRel, + LocIsEnregistered, + LocIsBitField, + LocIsSlot, + LocIsIlRel, + LocInMetaData, + LocIsConstant, + LocTypeMax +}; + +enum DataKind { + DataIsUnknown, + DataIsLocal, + DataIsStaticLocal, + DataIsParam, + DataIsObjectPtr, + DataIsFileStatic, + DataIsGlobal, + DataIsMember, + DataIsStaticMember, + DataIsConstant +}; + +enum UdtKind { UdtStruct, UdtClass, UdtUnion, UdtInterface }; + +enum BasicType { + btNoType = 0, + btVoid = 1, + btChar = 2, + btWChar = 3, + btInt = 6, + btUInt = 7, + btFloat = 8, + btBCD = 9, + btBool = 10, + btLong = 13, + btULong = 14, + btCurrency = 25, + btDate = 26, + btVariant = 27, + btComplex = 28, + btBit = 29, + btBSTR = 30, + btHresult = 31, + btChar16 = 32, // char16_t + btChar32 = 33, // char32_t +}; + +// enumeration for type modifier values + +typedef enum CV_modifier_e { + // 0x0000 - 0x01ff - Reserved. + + CV_MOD_INVALID = 0x0000, + + // Standard modifiers. + + CV_MOD_CONST = 0x0001, + CV_MOD_VOLATILE = 0x0002, + CV_MOD_UNALIGNED = 0x0003, + + // 0x0200 - 0x03ff - HLSL modifiers. + + CV_MOD_HLSL_UNIFORM = 0x0200, + CV_MOD_HLSL_LINE = 0x0201, + CV_MOD_HLSL_TRIANGLE = 0x0202, + CV_MOD_HLSL_LINEADJ = 0x0203, + CV_MOD_HLSL_TRIANGLEADJ = 0x0204, + CV_MOD_HLSL_LINEAR = 0x0205, + CV_MOD_HLSL_CENTROID = 0x0206, + CV_MOD_HLSL_CONSTINTERP = 0x0207, + CV_MOD_HLSL_NOPERSPECTIVE = 0x0208, + CV_MOD_HLSL_SAMPLE = 0x0209, + CV_MOD_HLSL_CENTER = 0x020a, + CV_MOD_HLSL_SNORM = 0x020b, + CV_MOD_HLSL_UNORM = 0x020c, + CV_MOD_HLSL_PRECISE = 0x020d, + CV_MOD_HLSL_UAV_GLOBALLY_COHERENT = 0x020e, + + // 0x0400 - 0xffff - Unused. + +} CV_modifier_e; + +// built-in type kinds + +typedef enum CV_builtin_e { + + // 0x0000 - 0x01ff - Reserved. + CV_BI_INVALID = 0x0000, + + // 0x0200 - 0x03ff - HLSL types. + + CV_BI_HLSL_INTERFACE_POINTER = 0x0200, + CV_BI_HLSL_TEXTURE1D = 0x0201, + CV_BI_HLSL_TEXTURE1D_ARRAY = 0x0202, + CV_BI_HLSL_TEXTURE2D = 0x0203, + CV_BI_HLSL_TEXTURE2D_ARRAY = 0x0204, + CV_BI_HLSL_TEXTURE3D = 0x0205, + CV_BI_HLSL_TEXTURECUBE = 0x0206, + CV_BI_HLSL_TEXTURECUBE_ARRAY = 0x0207, + CV_BI_HLSL_TEXTURE2DMS = 0x0208, + CV_BI_HLSL_TEXTURE2DMS_ARRAY = 0x0209, + CV_BI_HLSL_SAMPLER = 0x020a, + CV_BI_HLSL_SAMPLERCOMPARISON = 0x020b, + CV_BI_HLSL_BUFFER = 0x020c, + CV_BI_HLSL_POINTSTREAM = 0x020d, + CV_BI_HLSL_LINESTREAM = 0x020e, + CV_BI_HLSL_TRIANGLESTREAM = 0x020f, + CV_BI_HLSL_INPUTPATCH = 0x0210, + CV_BI_HLSL_OUTPUTPATCH = 0x0211, + CV_BI_HLSL_RWTEXTURE1D = 0x0212, + CV_BI_HLSL_RWTEXTURE1D_ARRAY = 0x0213, + CV_BI_HLSL_RWTEXTURE2D = 0x0214, + CV_BI_HLSL_RWTEXTURE2D_ARRAY = 0x0215, + CV_BI_HLSL_RWTEXTURE3D = 0x0216, + CV_BI_HLSL_RWBUFFER = 0x0217, + CV_BI_HLSL_BYTEADDRESS_BUFFER = 0x0218, + CV_BI_HLSL_RWBYTEADDRESS_BUFFER = 0x0219, + CV_BI_HLSL_STRUCTURED_BUFFER = 0x021a, + CV_BI_HLSL_RWSTRUCTURED_BUFFER = 0x021b, + CV_BI_HLSL_APPEND_STRUCTURED_BUFFER = 0x021c, + CV_BI_HLSL_CONSUME_STRUCTURED_BUFFER = 0x021d, + CV_BI_HLSL_MIN8FLOAT = 0x021e, + CV_BI_HLSL_MIN10FLOAT = 0x021f, + CV_BI_HLSL_MIN16FLOAT = 0x0220, + CV_BI_HLSL_MIN12INT = 0x0221, + CV_BI_HLSL_MIN16INT = 0x0222, + CV_BI_HLSL_MIN16UINT = 0x0223, + + // 0x0400 - 0xffff - Unused. + +} CV_builtin_e; + +// enum describing the compile flag source language + +typedef enum CV_CFL_LANG { + CV_CFL_C = 0x00, + CV_CFL_CXX = 0x01, + CV_CFL_FORTRAN = 0x02, + CV_CFL_MASM = 0x03, + CV_CFL_PASCAL = 0x04, + CV_CFL_BASIC = 0x05, + CV_CFL_COBOL = 0x06, + CV_CFL_LINK = 0x07, + CV_CFL_CVTRES = 0x08, + CV_CFL_CVTPGD = 0x09, + CV_CFL_CSHARP = 0x0A, // C# + CV_CFL_VB = 0x0B, // Visual Basic + CV_CFL_ILASM = 0x0C, // IL (as in CLR) ASM + CV_CFL_JAVA = 0x0D, + CV_CFL_JSCRIPT = 0x0E, + CV_CFL_MSIL = 0x0F, // Unknown MSIL (LTCG of .NETMODULE) + CV_CFL_HLSL = 0x10, // High Level Shader Language +} CV_CFL_LANG; + +// enum describing target processor + +typedef enum CV_CPU_TYPE_e { + CV_CFL_8080 = 0x00, + CV_CFL_8086 = 0x01, + CV_CFL_80286 = 0x02, + CV_CFL_80386 = 0x03, + CV_CFL_80486 = 0x04, + CV_CFL_PENTIUM = 0x05, + CV_CFL_PENTIUMII = 0x06, + CV_CFL_PENTIUMPRO = CV_CFL_PENTIUMII, + CV_CFL_PENTIUMIII = 0x07, + CV_CFL_MIPS = 0x10, + CV_CFL_MIPSR4000 = CV_CFL_MIPS, // don't break current code + CV_CFL_MIPS16 = 0x11, + CV_CFL_MIPS32 = 0x12, + CV_CFL_MIPS64 = 0x13, + CV_CFL_MIPSI = 0x14, + CV_CFL_MIPSII = 0x15, + CV_CFL_MIPSIII = 0x16, + CV_CFL_MIPSIV = 0x17, + CV_CFL_MIPSV = 0x18, + CV_CFL_M68000 = 0x20, + CV_CFL_M68010 = 0x21, + CV_CFL_M68020 = 0x22, + CV_CFL_M68030 = 0x23, + CV_CFL_M68040 = 0x24, + CV_CFL_ALPHA = 0x30, + CV_CFL_ALPHA_21064 = 0x30, + CV_CFL_ALPHA_21164 = 0x31, + CV_CFL_ALPHA_21164A = 0x32, + CV_CFL_ALPHA_21264 = 0x33, + CV_CFL_ALPHA_21364 = 0x34, + CV_CFL_PPC601 = 0x40, + CV_CFL_PPC603 = 0x41, + CV_CFL_PPC604 = 0x42, + CV_CFL_PPC620 = 0x43, + CV_CFL_PPCFP = 0x44, + CV_CFL_PPCBE = 0x45, + CV_CFL_SH3 = 0x50, + CV_CFL_SH3E = 0x51, + CV_CFL_SH3DSP = 0x52, + CV_CFL_SH4 = 0x53, + CV_CFL_SHMEDIA = 0x54, + CV_CFL_ARM3 = 0x60, + CV_CFL_ARM4 = 0x61, + CV_CFL_ARM4T = 0x62, + CV_CFL_ARM5 = 0x63, + CV_CFL_ARM5T = 0x64, + CV_CFL_ARM6 = 0x65, + CV_CFL_ARM_XMAC = 0x66, + CV_CFL_ARM_WMMX = 0x67, + CV_CFL_ARM7 = 0x68, + CV_CFL_OMNI = 0x70, + CV_CFL_IA64 = 0x80, + CV_CFL_IA64_1 = 0x80, + CV_CFL_IA64_2 = 0x81, + CV_CFL_CEE = 0x90, + CV_CFL_AM33 = 0xA0, + CV_CFL_M32R = 0xB0, + CV_CFL_TRICORE = 0xC0, + CV_CFL_X64 = 0xD0, + CV_CFL_AMD64 = CV_CFL_X64, + CV_CFL_EBC = 0xE0, + CV_CFL_THUMB = 0xF0, + CV_CFL_ARMNT = 0xF4, + CV_CFL_ARM64 = 0xF6, + CV_CFL_D3D11_SHADER = 0x100, +} CV_CPU_TYPE_e; + +typedef enum CV_HREG_e { + // Register subset shared by all processor types, + // must not overlap with any of the ranges below, hence the high values + + CV_ALLREG_ERR = 30000, + CV_ALLREG_TEB = 30001, + CV_ALLREG_TIMER = 30002, + CV_ALLREG_EFAD1 = 30003, + CV_ALLREG_EFAD2 = 30004, + CV_ALLREG_EFAD3 = 30005, + CV_ALLREG_VFRAME = 30006, + CV_ALLREG_HANDLE = 30007, + CV_ALLREG_PARAMS = 30008, + CV_ALLREG_LOCALS = 30009, + CV_ALLREG_TID = 30010, + CV_ALLREG_ENV = 30011, + CV_ALLREG_CMDLN = 30012, + + // Register set for the Intel 80x86 and ix86 processor series + // (plus PCODE registers) + + CV_REG_NONE = 0, + CV_REG_AL = 1, + CV_REG_CL = 2, + CV_REG_DL = 3, + CV_REG_BL = 4, + CV_REG_AH = 5, + CV_REG_CH = 6, + CV_REG_DH = 7, + CV_REG_BH = 8, + CV_REG_AX = 9, + CV_REG_CX = 10, + CV_REG_DX = 11, + CV_REG_BX = 12, + CV_REG_SP = 13, + CV_REG_BP = 14, + CV_REG_SI = 15, + CV_REG_DI = 16, + CV_REG_EAX = 17, + CV_REG_ECX = 18, + CV_REG_EDX = 19, + CV_REG_EBX = 20, + CV_REG_ESP = 21, + CV_REG_EBP = 22, + CV_REG_ESI = 23, + CV_REG_EDI = 24, + CV_REG_ES = 25, + CV_REG_CS = 26, + CV_REG_SS = 27, + CV_REG_DS = 28, + CV_REG_FS = 29, + CV_REG_GS = 30, + CV_REG_IP = 31, + CV_REG_FLAGS = 32, + CV_REG_EIP = 33, + CV_REG_EFLAGS = 34, + CV_REG_TEMP = 40, // PCODE Temp + CV_REG_TEMPH = 41, // PCODE TempH + CV_REG_QUOTE = 42, // PCODE Quote + CV_REG_PCDR3 = 43, // PCODE reserved + CV_REG_PCDR4 = 44, // PCODE reserved + CV_REG_PCDR5 = 45, // PCODE reserved + CV_REG_PCDR6 = 46, // PCODE reserved + CV_REG_PCDR7 = 47, // PCODE reserved + CV_REG_CR0 = 80, // CR0 -- control registers + CV_REG_CR1 = 81, + CV_REG_CR2 = 82, + CV_REG_CR3 = 83, + CV_REG_CR4 = 84, // Pentium + CV_REG_DR0 = 90, // Debug register + CV_REG_DR1 = 91, + CV_REG_DR2 = 92, + CV_REG_DR3 = 93, + CV_REG_DR4 = 94, + CV_REG_DR5 = 95, + CV_REG_DR6 = 96, + CV_REG_DR7 = 97, + CV_REG_GDTR = 110, + CV_REG_GDTL = 111, + CV_REG_IDTR = 112, + CV_REG_IDTL = 113, + CV_REG_LDTR = 114, + CV_REG_TR = 115, + + CV_REG_PSEUDO1 = 116, + CV_REG_PSEUDO2 = 117, + CV_REG_PSEUDO3 = 118, + CV_REG_PSEUDO4 = 119, + CV_REG_PSEUDO5 = 120, + CV_REG_PSEUDO6 = 121, + CV_REG_PSEUDO7 = 122, + CV_REG_PSEUDO8 = 123, + CV_REG_PSEUDO9 = 124, + + CV_REG_ST0 = 128, + CV_REG_ST1 = 129, + CV_REG_ST2 = 130, + CV_REG_ST3 = 131, + CV_REG_ST4 = 132, + CV_REG_ST5 = 133, + CV_REG_ST6 = 134, + CV_REG_ST7 = 135, + CV_REG_CTRL = 136, + CV_REG_STAT = 137, + CV_REG_TAG = 138, + CV_REG_FPIP = 139, + CV_REG_FPCS = 140, + CV_REG_FPDO = 141, + CV_REG_FPDS = 142, + CV_REG_ISEM = 143, + CV_REG_FPEIP = 144, + CV_REG_FPEDO = 145, + + CV_REG_MM0 = 146, + CV_REG_MM1 = 147, + CV_REG_MM2 = 148, + CV_REG_MM3 = 149, + CV_REG_MM4 = 150, + CV_REG_MM5 = 151, + CV_REG_MM6 = 152, + CV_REG_MM7 = 153, + + CV_REG_XMM0 = 154, // KATMAI registers + CV_REG_XMM1 = 155, + CV_REG_XMM2 = 156, + CV_REG_XMM3 = 157, + CV_REG_XMM4 = 158, + CV_REG_XMM5 = 159, + CV_REG_XMM6 = 160, + CV_REG_XMM7 = 161, + + CV_REG_XMM00 = 162, // KATMAI sub-registers + CV_REG_XMM01 = 163, + CV_REG_XMM02 = 164, + CV_REG_XMM03 = 165, + CV_REG_XMM10 = 166, + CV_REG_XMM11 = 167, + CV_REG_XMM12 = 168, + CV_REG_XMM13 = 169, + CV_REG_XMM20 = 170, + CV_REG_XMM21 = 171, + CV_REG_XMM22 = 172, + CV_REG_XMM23 = 173, + CV_REG_XMM30 = 174, + CV_REG_XMM31 = 175, + CV_REG_XMM32 = 176, + CV_REG_XMM33 = 177, + CV_REG_XMM40 = 178, + CV_REG_XMM41 = 179, + CV_REG_XMM42 = 180, + CV_REG_XMM43 = 181, + CV_REG_XMM50 = 182, + CV_REG_XMM51 = 183, + CV_REG_XMM52 = 184, + CV_REG_XMM53 = 185, + CV_REG_XMM60 = 186, + CV_REG_XMM61 = 187, + CV_REG_XMM62 = 188, + CV_REG_XMM63 = 189, + CV_REG_XMM70 = 190, + CV_REG_XMM71 = 191, + CV_REG_XMM72 = 192, + CV_REG_XMM73 = 193, + + CV_REG_XMM0L = 194, + CV_REG_XMM1L = 195, + CV_REG_XMM2L = 196, + CV_REG_XMM3L = 197, + CV_REG_XMM4L = 198, + CV_REG_XMM5L = 199, + CV_REG_XMM6L = 200, + CV_REG_XMM7L = 201, + + CV_REG_XMM0H = 202, + CV_REG_XMM1H = 203, + CV_REG_XMM2H = 204, + CV_REG_XMM3H = 205, + CV_REG_XMM4H = 206, + CV_REG_XMM5H = 207, + CV_REG_XMM6H = 208, + CV_REG_XMM7H = 209, + + CV_REG_MXCSR = 211, // XMM status register + + CV_REG_EDXEAX = 212, // EDX:EAX pair + + CV_REG_EMM0L = 220, // XMM sub-registers (WNI integer) + CV_REG_EMM1L = 221, + CV_REG_EMM2L = 222, + CV_REG_EMM3L = 223, + CV_REG_EMM4L = 224, + CV_REG_EMM5L = 225, + CV_REG_EMM6L = 226, + CV_REG_EMM7L = 227, + + CV_REG_EMM0H = 228, + CV_REG_EMM1H = 229, + CV_REG_EMM2H = 230, + CV_REG_EMM3H = 231, + CV_REG_EMM4H = 232, + CV_REG_EMM5H = 233, + CV_REG_EMM6H = 234, + CV_REG_EMM7H = 235, + + // do not change the order of these regs, first one must be even too + CV_REG_MM00 = 236, + CV_REG_MM01 = 237, + CV_REG_MM10 = 238, + CV_REG_MM11 = 239, + CV_REG_MM20 = 240, + CV_REG_MM21 = 241, + CV_REG_MM30 = 242, + CV_REG_MM31 = 243, + CV_REG_MM40 = 244, + CV_REG_MM41 = 245, + CV_REG_MM50 = 246, + CV_REG_MM51 = 247, + CV_REG_MM60 = 248, + CV_REG_MM61 = 249, + CV_REG_MM70 = 250, + CV_REG_MM71 = 251, + + CV_REG_YMM0 = 252, // AVX registers + CV_REG_YMM1 = 253, + CV_REG_YMM2 = 254, + CV_REG_YMM3 = 255, + CV_REG_YMM4 = 256, + CV_REG_YMM5 = 257, + CV_REG_YMM6 = 258, + CV_REG_YMM7 = 259, + + CV_REG_YMM0H = 260, + CV_REG_YMM1H = 261, + CV_REG_YMM2H = 262, + CV_REG_YMM3H = 263, + CV_REG_YMM4H = 264, + CV_REG_YMM5H = 265, + CV_REG_YMM6H = 266, + CV_REG_YMM7H = 267, + + CV_REG_YMM0I0 = 268, // AVX integer registers + CV_REG_YMM0I1 = 269, + CV_REG_YMM0I2 = 270, + CV_REG_YMM0I3 = 271, + CV_REG_YMM1I0 = 272, + CV_REG_YMM1I1 = 273, + CV_REG_YMM1I2 = 274, + CV_REG_YMM1I3 = 275, + CV_REG_YMM2I0 = 276, + CV_REG_YMM2I1 = 277, + CV_REG_YMM2I2 = 278, + CV_REG_YMM2I3 = 279, + CV_REG_YMM3I0 = 280, + CV_REG_YMM3I1 = 281, + CV_REG_YMM3I2 = 282, + CV_REG_YMM3I3 = 283, + CV_REG_YMM4I0 = 284, + CV_REG_YMM4I1 = 285, + CV_REG_YMM4I2 = 286, + CV_REG_YMM4I3 = 287, + CV_REG_YMM5I0 = 288, + CV_REG_YMM5I1 = 289, + CV_REG_YMM5I2 = 290, + CV_REG_YMM5I3 = 291, + CV_REG_YMM6I0 = 292, + CV_REG_YMM6I1 = 293, + CV_REG_YMM6I2 = 294, + CV_REG_YMM6I3 = 295, + CV_REG_YMM7I0 = 296, + CV_REG_YMM7I1 = 297, + CV_REG_YMM7I2 = 298, + CV_REG_YMM7I3 = 299, + + CV_REG_YMM0F0 = 300, // AVX floating-point single precise registers + CV_REG_YMM0F1 = 301, + CV_REG_YMM0F2 = 302, + CV_REG_YMM0F3 = 303, + CV_REG_YMM0F4 = 304, + CV_REG_YMM0F5 = 305, + CV_REG_YMM0F6 = 306, + CV_REG_YMM0F7 = 307, + CV_REG_YMM1F0 = 308, + CV_REG_YMM1F1 = 309, + CV_REG_YMM1F2 = 310, + CV_REG_YMM1F3 = 311, + CV_REG_YMM1F4 = 312, + CV_REG_YMM1F5 = 313, + CV_REG_YMM1F6 = 314, + CV_REG_YMM1F7 = 315, + CV_REG_YMM2F0 = 316, + CV_REG_YMM2F1 = 317, + CV_REG_YMM2F2 = 318, + CV_REG_YMM2F3 = 319, + CV_REG_YMM2F4 = 320, + CV_REG_YMM2F5 = 321, + CV_REG_YMM2F6 = 322, + CV_REG_YMM2F7 = 323, + CV_REG_YMM3F0 = 324, + CV_REG_YMM3F1 = 325, + CV_REG_YMM3F2 = 326, + CV_REG_YMM3F3 = 327, + CV_REG_YMM3F4 = 328, + CV_REG_YMM3F5 = 329, + CV_REG_YMM3F6 = 330, + CV_REG_YMM3F7 = 331, + CV_REG_YMM4F0 = 332, + CV_REG_YMM4F1 = 333, + CV_REG_YMM4F2 = 334, + CV_REG_YMM4F3 = 335, + CV_REG_YMM4F4 = 336, + CV_REG_YMM4F5 = 337, + CV_REG_YMM4F6 = 338, + CV_REG_YMM4F7 = 339, + CV_REG_YMM5F0 = 340, + CV_REG_YMM5F1 = 341, + CV_REG_YMM5F2 = 342, + CV_REG_YMM5F3 = 343, + CV_REG_YMM5F4 = 344, + CV_REG_YMM5F5 = 345, + CV_REG_YMM5F6 = 346, + CV_REG_YMM5F7 = 347, + CV_REG_YMM6F0 = 348, + CV_REG_YMM6F1 = 349, + CV_REG_YMM6F2 = 350, + CV_REG_YMM6F3 = 351, + CV_REG_YMM6F4 = 352, + CV_REG_YMM6F5 = 353, + CV_REG_YMM6F6 = 354, + CV_REG_YMM6F7 = 355, + CV_REG_YMM7F0 = 356, + CV_REG_YMM7F1 = 357, + CV_REG_YMM7F2 = 358, + CV_REG_YMM7F3 = 359, + CV_REG_YMM7F4 = 360, + CV_REG_YMM7F5 = 361, + CV_REG_YMM7F6 = 362, + CV_REG_YMM7F7 = 363, + + CV_REG_YMM0D0 = 364, // AVX floating-point double precise registers + CV_REG_YMM0D1 = 365, + CV_REG_YMM0D2 = 366, + CV_REG_YMM0D3 = 367, + CV_REG_YMM1D0 = 368, + CV_REG_YMM1D1 = 369, + CV_REG_YMM1D2 = 370, + CV_REG_YMM1D3 = 371, + CV_REG_YMM2D0 = 372, + CV_REG_YMM2D1 = 373, + CV_REG_YMM2D2 = 374, + CV_REG_YMM2D3 = 375, + CV_REG_YMM3D0 = 376, + CV_REG_YMM3D1 = 377, + CV_REG_YMM3D2 = 378, + CV_REG_YMM3D3 = 379, + CV_REG_YMM4D0 = 380, + CV_REG_YMM4D1 = 381, + CV_REG_YMM4D2 = 382, + CV_REG_YMM4D3 = 383, + CV_REG_YMM5D0 = 384, + CV_REG_YMM5D1 = 385, + CV_REG_YMM5D2 = 386, + CV_REG_YMM5D3 = 387, + CV_REG_YMM6D0 = 388, + CV_REG_YMM6D1 = 389, + CV_REG_YMM6D2 = 390, + CV_REG_YMM6D3 = 391, + CV_REG_YMM7D0 = 392, + CV_REG_YMM7D1 = 393, + CV_REG_YMM7D2 = 394, + CV_REG_YMM7D3 = 395, + + CV_REG_BND0 = 396, + CV_REG_BND1 = 397, + CV_REG_BND2 = 398, + CV_REG_BND3 = 399, + + // registers for the 68K processors + + CV_R68_D0 = 0, + CV_R68_D1 = 1, + CV_R68_D2 = 2, + CV_R68_D3 = 3, + CV_R68_D4 = 4, + CV_R68_D5 = 5, + CV_R68_D6 = 6, + CV_R68_D7 = 7, + CV_R68_A0 = 8, + CV_R68_A1 = 9, + CV_R68_A2 = 10, + CV_R68_A3 = 11, + CV_R68_A4 = 12, + CV_R68_A5 = 13, + CV_R68_A6 = 14, + CV_R68_A7 = 15, + CV_R68_CCR = 16, + CV_R68_SR = 17, + CV_R68_USP = 18, + CV_R68_MSP = 19, + CV_R68_SFC = 20, + CV_R68_DFC = 21, + CV_R68_CACR = 22, + CV_R68_VBR = 23, + CV_R68_CAAR = 24, + CV_R68_ISP = 25, + CV_R68_PC = 26, + // reserved 27 + CV_R68_FPCR = 28, + CV_R68_FPSR = 29, + CV_R68_FPIAR = 30, + // reserved 31 + CV_R68_FP0 = 32, + CV_R68_FP1 = 33, + CV_R68_FP2 = 34, + CV_R68_FP3 = 35, + CV_R68_FP4 = 36, + CV_R68_FP5 = 37, + CV_R68_FP6 = 38, + CV_R68_FP7 = 39, + // reserved 40 + CV_R68_MMUSR030 = 41, + CV_R68_MMUSR = 42, + CV_R68_URP = 43, + CV_R68_DTT0 = 44, + CV_R68_DTT1 = 45, + CV_R68_ITT0 = 46, + CV_R68_ITT1 = 47, + // reserved 50 + CV_R68_PSR = 51, + CV_R68_PCSR = 52, + CV_R68_VAL = 53, + CV_R68_CRP = 54, + CV_R68_SRP = 55, + CV_R68_DRP = 56, + CV_R68_TC = 57, + CV_R68_AC = 58, + CV_R68_SCC = 59, + CV_R68_CAL = 60, + CV_R68_TT0 = 61, + CV_R68_TT1 = 62, + // reserved 63 + CV_R68_BAD0 = 64, + CV_R68_BAD1 = 65, + CV_R68_BAD2 = 66, + CV_R68_BAD3 = 67, + CV_R68_BAD4 = 68, + CV_R68_BAD5 = 69, + CV_R68_BAD6 = 70, + CV_R68_BAD7 = 71, + CV_R68_BAC0 = 72, + CV_R68_BAC1 = 73, + CV_R68_BAC2 = 74, + CV_R68_BAC3 = 75, + CV_R68_BAC4 = 76, + CV_R68_BAC5 = 77, + CV_R68_BAC6 = 78, + CV_R68_BAC7 = 79, + + // Register set for the MIPS 4000 + + CV_M4_NOREG = CV_REG_NONE, + + CV_M4_IntZERO = 10, /* CPU REGISTER */ + CV_M4_IntAT = 11, + CV_M4_IntV0 = 12, + CV_M4_IntV1 = 13, + CV_M4_IntA0 = 14, + CV_M4_IntA1 = 15, + CV_M4_IntA2 = 16, + CV_M4_IntA3 = 17, + CV_M4_IntT0 = 18, + CV_M4_IntT1 = 19, + CV_M4_IntT2 = 20, + CV_M4_IntT3 = 21, + CV_M4_IntT4 = 22, + CV_M4_IntT5 = 23, + CV_M4_IntT6 = 24, + CV_M4_IntT7 = 25, + CV_M4_IntS0 = 26, + CV_M4_IntS1 = 27, + CV_M4_IntS2 = 28, + CV_M4_IntS3 = 29, + CV_M4_IntS4 = 30, + CV_M4_IntS5 = 31, + CV_M4_IntS6 = 32, + CV_M4_IntS7 = 33, + CV_M4_IntT8 = 34, + CV_M4_IntT9 = 35, + CV_M4_IntKT0 = 36, + CV_M4_IntKT1 = 37, + CV_M4_IntGP = 38, + CV_M4_IntSP = 39, + CV_M4_IntS8 = 40, + CV_M4_IntRA = 41, + CV_M4_IntLO = 42, + CV_M4_IntHI = 43, + + CV_M4_Fir = 50, + CV_M4_Psr = 51, + + CV_M4_FltF0 = 60, /* Floating point registers */ + CV_M4_FltF1 = 61, + CV_M4_FltF2 = 62, + CV_M4_FltF3 = 63, + CV_M4_FltF4 = 64, + CV_M4_FltF5 = 65, + CV_M4_FltF6 = 66, + CV_M4_FltF7 = 67, + CV_M4_FltF8 = 68, + CV_M4_FltF9 = 69, + CV_M4_FltF10 = 70, + CV_M4_FltF11 = 71, + CV_M4_FltF12 = 72, + CV_M4_FltF13 = 73, + CV_M4_FltF14 = 74, + CV_M4_FltF15 = 75, + CV_M4_FltF16 = 76, + CV_M4_FltF17 = 77, + CV_M4_FltF18 = 78, + CV_M4_FltF19 = 79, + CV_M4_FltF20 = 80, + CV_M4_FltF21 = 81, + CV_M4_FltF22 = 82, + CV_M4_FltF23 = 83, + CV_M4_FltF24 = 84, + CV_M4_FltF25 = 85, + CV_M4_FltF26 = 86, + CV_M4_FltF27 = 87, + CV_M4_FltF28 = 88, + CV_M4_FltF29 = 89, + CV_M4_FltF30 = 90, + CV_M4_FltF31 = 91, + CV_M4_FltFsr = 92, + + // Register set for the ALPHA AXP + + CV_ALPHA_NOREG = CV_REG_NONE, + + CV_ALPHA_FltF0 = 10, // Floating point registers + CV_ALPHA_FltF1 = 11, + CV_ALPHA_FltF2 = 12, + CV_ALPHA_FltF3 = 13, + CV_ALPHA_FltF4 = 14, + CV_ALPHA_FltF5 = 15, + CV_ALPHA_FltF6 = 16, + CV_ALPHA_FltF7 = 17, + CV_ALPHA_FltF8 = 18, + CV_ALPHA_FltF9 = 19, + CV_ALPHA_FltF10 = 20, + CV_ALPHA_FltF11 = 21, + CV_ALPHA_FltF12 = 22, + CV_ALPHA_FltF13 = 23, + CV_ALPHA_FltF14 = 24, + CV_ALPHA_FltF15 = 25, + CV_ALPHA_FltF16 = 26, + CV_ALPHA_FltF17 = 27, + CV_ALPHA_FltF18 = 28, + CV_ALPHA_FltF19 = 29, + CV_ALPHA_FltF20 = 30, + CV_ALPHA_FltF21 = 31, + CV_ALPHA_FltF22 = 32, + CV_ALPHA_FltF23 = 33, + CV_ALPHA_FltF24 = 34, + CV_ALPHA_FltF25 = 35, + CV_ALPHA_FltF26 = 36, + CV_ALPHA_FltF27 = 37, + CV_ALPHA_FltF28 = 38, + CV_ALPHA_FltF29 = 39, + CV_ALPHA_FltF30 = 40, + CV_ALPHA_FltF31 = 41, + + CV_ALPHA_IntV0 = 42, // Integer registers + CV_ALPHA_IntT0 = 43, + CV_ALPHA_IntT1 = 44, + CV_ALPHA_IntT2 = 45, + CV_ALPHA_IntT3 = 46, + CV_ALPHA_IntT4 = 47, + CV_ALPHA_IntT5 = 48, + CV_ALPHA_IntT6 = 49, + CV_ALPHA_IntT7 = 50, + CV_ALPHA_IntS0 = 51, + CV_ALPHA_IntS1 = 52, + CV_ALPHA_IntS2 = 53, + CV_ALPHA_IntS3 = 54, + CV_ALPHA_IntS4 = 55, + CV_ALPHA_IntS5 = 56, + CV_ALPHA_IntFP = 57, + CV_ALPHA_IntA0 = 58, + CV_ALPHA_IntA1 = 59, + CV_ALPHA_IntA2 = 60, + CV_ALPHA_IntA3 = 61, + CV_ALPHA_IntA4 = 62, + CV_ALPHA_IntA5 = 63, + CV_ALPHA_IntT8 = 64, + CV_ALPHA_IntT9 = 65, + CV_ALPHA_IntT10 = 66, + CV_ALPHA_IntT11 = 67, + CV_ALPHA_IntRA = 68, + CV_ALPHA_IntT12 = 69, + CV_ALPHA_IntAT = 70, + CV_ALPHA_IntGP = 71, + CV_ALPHA_IntSP = 72, + CV_ALPHA_IntZERO = 73, + + CV_ALPHA_Fpcr = 74, // Control registers + CV_ALPHA_Fir = 75, + CV_ALPHA_Psr = 76, + CV_ALPHA_FltFsr = 77, + CV_ALPHA_SoftFpcr = 78, + + // Register Set for Motorola/IBM PowerPC + + /* + ** PowerPC General Registers ( User Level ) + */ + CV_PPC_GPR0 = 1, + CV_PPC_GPR1 = 2, + CV_PPC_GPR2 = 3, + CV_PPC_GPR3 = 4, + CV_PPC_GPR4 = 5, + CV_PPC_GPR5 = 6, + CV_PPC_GPR6 = 7, + CV_PPC_GPR7 = 8, + CV_PPC_GPR8 = 9, + CV_PPC_GPR9 = 10, + CV_PPC_GPR10 = 11, + CV_PPC_GPR11 = 12, + CV_PPC_GPR12 = 13, + CV_PPC_GPR13 = 14, + CV_PPC_GPR14 = 15, + CV_PPC_GPR15 = 16, + CV_PPC_GPR16 = 17, + CV_PPC_GPR17 = 18, + CV_PPC_GPR18 = 19, + CV_PPC_GPR19 = 20, + CV_PPC_GPR20 = 21, + CV_PPC_GPR21 = 22, + CV_PPC_GPR22 = 23, + CV_PPC_GPR23 = 24, + CV_PPC_GPR24 = 25, + CV_PPC_GPR25 = 26, + CV_PPC_GPR26 = 27, + CV_PPC_GPR27 = 28, + CV_PPC_GPR28 = 29, + CV_PPC_GPR29 = 30, + CV_PPC_GPR30 = 31, + CV_PPC_GPR31 = 32, + + /* + ** PowerPC Condition Register ( User Level ) + */ + CV_PPC_CR = 33, + CV_PPC_CR0 = 34, + CV_PPC_CR1 = 35, + CV_PPC_CR2 = 36, + CV_PPC_CR3 = 37, + CV_PPC_CR4 = 38, + CV_PPC_CR5 = 39, + CV_PPC_CR6 = 40, + CV_PPC_CR7 = 41, + + /* + ** PowerPC Floating Point Registers ( User Level ) + */ + CV_PPC_FPR0 = 42, + CV_PPC_FPR1 = 43, + CV_PPC_FPR2 = 44, + CV_PPC_FPR3 = 45, + CV_PPC_FPR4 = 46, + CV_PPC_FPR5 = 47, + CV_PPC_FPR6 = 48, + CV_PPC_FPR7 = 49, + CV_PPC_FPR8 = 50, + CV_PPC_FPR9 = 51, + CV_PPC_FPR10 = 52, + CV_PPC_FPR11 = 53, + CV_PPC_FPR12 = 54, + CV_PPC_FPR13 = 55, + CV_PPC_FPR14 = 56, + CV_PPC_FPR15 = 57, + CV_PPC_FPR16 = 58, + CV_PPC_FPR17 = 59, + CV_PPC_FPR18 = 60, + CV_PPC_FPR19 = 61, + CV_PPC_FPR20 = 62, + CV_PPC_FPR21 = 63, + CV_PPC_FPR22 = 64, + CV_PPC_FPR23 = 65, + CV_PPC_FPR24 = 66, + CV_PPC_FPR25 = 67, + CV_PPC_FPR26 = 68, + CV_PPC_FPR27 = 69, + CV_PPC_FPR28 = 70, + CV_PPC_FPR29 = 71, + CV_PPC_FPR30 = 72, + CV_PPC_FPR31 = 73, + + /* + ** PowerPC Floating Point Status and Control Register ( User Level ) + */ + CV_PPC_FPSCR = 74, + + /* + ** PowerPC Machine State Register ( Supervisor Level ) + */ + CV_PPC_MSR = 75, + + /* + ** PowerPC Segment Registers ( Supervisor Level ) + */ + CV_PPC_SR0 = 76, + CV_PPC_SR1 = 77, + CV_PPC_SR2 = 78, + CV_PPC_SR3 = 79, + CV_PPC_SR4 = 80, + CV_PPC_SR5 = 81, + CV_PPC_SR6 = 82, + CV_PPC_SR7 = 83, + CV_PPC_SR8 = 84, + CV_PPC_SR9 = 85, + CV_PPC_SR10 = 86, + CV_PPC_SR11 = 87, + CV_PPC_SR12 = 88, + CV_PPC_SR13 = 89, + CV_PPC_SR14 = 90, + CV_PPC_SR15 = 91, + + /* + ** For all of the special purpose registers add 100 to the SPR# that the + ** Motorola/IBM documentation gives with the exception of any imaginary + ** registers. + */ + + /* + ** PowerPC Special Purpose Registers ( User Level ) + */ + CV_PPC_PC = 99, // PC (imaginary register) + + CV_PPC_MQ = 100, // MPC601 + CV_PPC_XER = 101, + CV_PPC_RTCU = 104, // MPC601 + CV_PPC_RTCL = 105, // MPC601 + CV_PPC_LR = 108, + CV_PPC_CTR = 109, + + CV_PPC_COMPARE = 110, // part of XER (internal to the debugger only) + CV_PPC_COUNT = 111, // part of XER (internal to the debugger only) + + /* + ** PowerPC Special Purpose Registers ( Supervisor Level ) + */ + CV_PPC_DSISR = 118, + CV_PPC_DAR = 119, + CV_PPC_DEC = 122, + CV_PPC_SDR1 = 125, + CV_PPC_SRR0 = 126, + CV_PPC_SRR1 = 127, + CV_PPC_SPRG0 = 372, + CV_PPC_SPRG1 = 373, + CV_PPC_SPRG2 = 374, + CV_PPC_SPRG3 = 375, + CV_PPC_ASR = 280, // 64-bit implementations only + CV_PPC_EAR = 382, + CV_PPC_PVR = 287, + CV_PPC_BAT0U = 628, + CV_PPC_BAT0L = 629, + CV_PPC_BAT1U = 630, + CV_PPC_BAT1L = 631, + CV_PPC_BAT2U = 632, + CV_PPC_BAT2L = 633, + CV_PPC_BAT3U = 634, + CV_PPC_BAT3L = 635, + CV_PPC_DBAT0U = 636, + CV_PPC_DBAT0L = 637, + CV_PPC_DBAT1U = 638, + CV_PPC_DBAT1L = 639, + CV_PPC_DBAT2U = 640, + CV_PPC_DBAT2L = 641, + CV_PPC_DBAT3U = 642, + CV_PPC_DBAT3L = 643, + + /* + ** PowerPC Special Purpose Registers Implementation Dependent ( Supervisor + *Level ) + */ + + /* + ** Doesn't appear that IBM/Motorola has finished defining these. + */ + + CV_PPC_PMR0 = 1044, // MPC620, + CV_PPC_PMR1 = 1045, // MPC620, + CV_PPC_PMR2 = 1046, // MPC620, + CV_PPC_PMR3 = 1047, // MPC620, + CV_PPC_PMR4 = 1048, // MPC620, + CV_PPC_PMR5 = 1049, // MPC620, + CV_PPC_PMR6 = 1050, // MPC620, + CV_PPC_PMR7 = 1051, // MPC620, + CV_PPC_PMR8 = 1052, // MPC620, + CV_PPC_PMR9 = 1053, // MPC620, + CV_PPC_PMR10 = 1054, // MPC620, + CV_PPC_PMR11 = 1055, // MPC620, + CV_PPC_PMR12 = 1056, // MPC620, + CV_PPC_PMR13 = 1057, // MPC620, + CV_PPC_PMR14 = 1058, // MPC620, + CV_PPC_PMR15 = 1059, // MPC620, + + CV_PPC_DMISS = 1076, // MPC603 + CV_PPC_DCMP = 1077, // MPC603 + CV_PPC_HASH1 = 1078, // MPC603 + CV_PPC_HASH2 = 1079, // MPC603 + CV_PPC_IMISS = 1080, // MPC603 + CV_PPC_ICMP = 1081, // MPC603 + CV_PPC_RPA = 1082, // MPC603 + + CV_PPC_HID0 = 1108, // MPC601, MPC603, MPC620 + CV_PPC_HID1 = 1109, // MPC601 + CV_PPC_HID2 = 1110, // MPC601, MPC603, MPC620 ( IABR ) + CV_PPC_HID3 = 1111, // Not Defined + CV_PPC_HID4 = 1112, // Not Defined + CV_PPC_HID5 = 1113, // MPC601, MPC604, MPC620 ( DABR ) + CV_PPC_HID6 = 1114, // Not Defined + CV_PPC_HID7 = 1115, // Not Defined + CV_PPC_HID8 = 1116, // MPC620 ( BUSCSR ) + CV_PPC_HID9 = 1117, // MPC620 ( L2CSR ) + CV_PPC_HID10 = 1118, // Not Defined + CV_PPC_HID11 = 1119, // Not Defined + CV_PPC_HID12 = 1120, // Not Defined + CV_PPC_HID13 = 1121, // MPC604 ( HCR ) + CV_PPC_HID14 = 1122, // Not Defined + CV_PPC_HID15 = 1123, // MPC601, MPC604, MPC620 ( PIR ) + + // + // JAVA VM registers + // + + CV_JAVA_PC = 1, + + // + // Register set for the Hitachi SH3 + // + + CV_SH3_NOREG = CV_REG_NONE, + + CV_SH3_IntR0 = 10, // CPU REGISTER + CV_SH3_IntR1 = 11, + CV_SH3_IntR2 = 12, + CV_SH3_IntR3 = 13, + CV_SH3_IntR4 = 14, + CV_SH3_IntR5 = 15, + CV_SH3_IntR6 = 16, + CV_SH3_IntR7 = 17, + CV_SH3_IntR8 = 18, + CV_SH3_IntR9 = 19, + CV_SH3_IntR10 = 20, + CV_SH3_IntR11 = 21, + CV_SH3_IntR12 = 22, + CV_SH3_IntR13 = 23, + CV_SH3_IntFp = 24, + CV_SH3_IntSp = 25, + CV_SH3_Gbr = 38, + CV_SH3_Pr = 39, + CV_SH3_Mach = 40, + CV_SH3_Macl = 41, + + CV_SH3_Pc = 50, + CV_SH3_Sr = 51, + + CV_SH3_BarA = 60, + CV_SH3_BasrA = 61, + CV_SH3_BamrA = 62, + CV_SH3_BbrA = 63, + CV_SH3_BarB = 64, + CV_SH3_BasrB = 65, + CV_SH3_BamrB = 66, + CV_SH3_BbrB = 67, + CV_SH3_BdrB = 68, + CV_SH3_BdmrB = 69, + CV_SH3_Brcr = 70, + + // + // Additional registers for Hitachi SH processors + // + + CV_SH_Fpscr = 75, // floating point status/control register + CV_SH_Fpul = 76, // floating point communication register + + CV_SH_FpR0 = 80, // Floating point registers + CV_SH_FpR1 = 81, + CV_SH_FpR2 = 82, + CV_SH_FpR3 = 83, + CV_SH_FpR4 = 84, + CV_SH_FpR5 = 85, + CV_SH_FpR6 = 86, + CV_SH_FpR7 = 87, + CV_SH_FpR8 = 88, + CV_SH_FpR9 = 89, + CV_SH_FpR10 = 90, + CV_SH_FpR11 = 91, + CV_SH_FpR12 = 92, + CV_SH_FpR13 = 93, + CV_SH_FpR14 = 94, + CV_SH_FpR15 = 95, + + CV_SH_XFpR0 = 96, + CV_SH_XFpR1 = 97, + CV_SH_XFpR2 = 98, + CV_SH_XFpR3 = 99, + CV_SH_XFpR4 = 100, + CV_SH_XFpR5 = 101, + CV_SH_XFpR6 = 102, + CV_SH_XFpR7 = 103, + CV_SH_XFpR8 = 104, + CV_SH_XFpR9 = 105, + CV_SH_XFpR10 = 106, + CV_SH_XFpR11 = 107, + CV_SH_XFpR12 = 108, + CV_SH_XFpR13 = 109, + CV_SH_XFpR14 = 110, + CV_SH_XFpR15 = 111, + + // + // Register set for the ARM processor. + // + + CV_ARM_NOREG = CV_REG_NONE, + + CV_ARM_R0 = 10, + CV_ARM_R1 = 11, + CV_ARM_R2 = 12, + CV_ARM_R3 = 13, + CV_ARM_R4 = 14, + CV_ARM_R5 = 15, + CV_ARM_R6 = 16, + CV_ARM_R7 = 17, + CV_ARM_R8 = 18, + CV_ARM_R9 = 19, + CV_ARM_R10 = 20, + CV_ARM_R11 = 21, // Frame pointer, if allocated + CV_ARM_R12 = 22, + CV_ARM_SP = 23, // Stack pointer + CV_ARM_LR = 24, // Link Register + CV_ARM_PC = 25, // Program counter + CV_ARM_CPSR = 26, // Current program status register + + CV_ARM_ACC0 = 27, // DSP co-processor 0 40 bit accumulator + + // + // Registers for ARM VFP10 support + // + + CV_ARM_FPSCR = 40, + CV_ARM_FPEXC = 41, + + CV_ARM_FS0 = 50, + CV_ARM_FS1 = 51, + CV_ARM_FS2 = 52, + CV_ARM_FS3 = 53, + CV_ARM_FS4 = 54, + CV_ARM_FS5 = 55, + CV_ARM_FS6 = 56, + CV_ARM_FS7 = 57, + CV_ARM_FS8 = 58, + CV_ARM_FS9 = 59, + CV_ARM_FS10 = 60, + CV_ARM_FS11 = 61, + CV_ARM_FS12 = 62, + CV_ARM_FS13 = 63, + CV_ARM_FS14 = 64, + CV_ARM_FS15 = 65, + CV_ARM_FS16 = 66, + CV_ARM_FS17 = 67, + CV_ARM_FS18 = 68, + CV_ARM_FS19 = 69, + CV_ARM_FS20 = 70, + CV_ARM_FS21 = 71, + CV_ARM_FS22 = 72, + CV_ARM_FS23 = 73, + CV_ARM_FS24 = 74, + CV_ARM_FS25 = 75, + CV_ARM_FS26 = 76, + CV_ARM_FS27 = 77, + CV_ARM_FS28 = 78, + CV_ARM_FS29 = 79, + CV_ARM_FS30 = 80, + CV_ARM_FS31 = 81, + + // + // ARM VFP Floating Point Extra control registers + // + + CV_ARM_FPEXTRA0 = 90, + CV_ARM_FPEXTRA1 = 91, + CV_ARM_FPEXTRA2 = 92, + CV_ARM_FPEXTRA3 = 93, + CV_ARM_FPEXTRA4 = 94, + CV_ARM_FPEXTRA5 = 95, + CV_ARM_FPEXTRA6 = 96, + CV_ARM_FPEXTRA7 = 97, + + // XSCALE Concan co-processor registers + CV_ARM_WR0 = 128, + CV_ARM_WR1 = 129, + CV_ARM_WR2 = 130, + CV_ARM_WR3 = 131, + CV_ARM_WR4 = 132, + CV_ARM_WR5 = 133, + CV_ARM_WR6 = 134, + CV_ARM_WR7 = 135, + CV_ARM_WR8 = 136, + CV_ARM_WR9 = 137, + CV_ARM_WR10 = 138, + CV_ARM_WR11 = 139, + CV_ARM_WR12 = 140, + CV_ARM_WR13 = 141, + CV_ARM_WR14 = 142, + CV_ARM_WR15 = 143, + + // XSCALE Concan co-processor control registers + CV_ARM_WCID = 144, + CV_ARM_WCON = 145, + CV_ARM_WCSSF = 146, + CV_ARM_WCASF = 147, + CV_ARM_WC4 = 148, + CV_ARM_WC5 = 149, + CV_ARM_WC6 = 150, + CV_ARM_WC7 = 151, + CV_ARM_WCGR0 = 152, + CV_ARM_WCGR1 = 153, + CV_ARM_WCGR2 = 154, + CV_ARM_WCGR3 = 155, + CV_ARM_WC12 = 156, + CV_ARM_WC13 = 157, + CV_ARM_WC14 = 158, + CV_ARM_WC15 = 159, + + // + // ARM VFPv3/Neon extended floating Point + // + + CV_ARM_FS32 = 200, + CV_ARM_FS33 = 201, + CV_ARM_FS34 = 202, + CV_ARM_FS35 = 203, + CV_ARM_FS36 = 204, + CV_ARM_FS37 = 205, + CV_ARM_FS38 = 206, + CV_ARM_FS39 = 207, + CV_ARM_FS40 = 208, + CV_ARM_FS41 = 209, + CV_ARM_FS42 = 210, + CV_ARM_FS43 = 211, + CV_ARM_FS44 = 212, + CV_ARM_FS45 = 213, + CV_ARM_FS46 = 214, + CV_ARM_FS47 = 215, + CV_ARM_FS48 = 216, + CV_ARM_FS49 = 217, + CV_ARM_FS50 = 218, + CV_ARM_FS51 = 219, + CV_ARM_FS52 = 220, + CV_ARM_FS53 = 221, + CV_ARM_FS54 = 222, + CV_ARM_FS55 = 223, + CV_ARM_FS56 = 224, + CV_ARM_FS57 = 225, + CV_ARM_FS58 = 226, + CV_ARM_FS59 = 227, + CV_ARM_FS60 = 228, + CV_ARM_FS61 = 229, + CV_ARM_FS62 = 230, + CV_ARM_FS63 = 231, + + // ARM double-precision floating point + + CV_ARM_ND0 = 300, + CV_ARM_ND1 = 301, + CV_ARM_ND2 = 302, + CV_ARM_ND3 = 303, + CV_ARM_ND4 = 304, + CV_ARM_ND5 = 305, + CV_ARM_ND6 = 306, + CV_ARM_ND7 = 307, + CV_ARM_ND8 = 308, + CV_ARM_ND9 = 309, + CV_ARM_ND10 = 310, + CV_ARM_ND11 = 311, + CV_ARM_ND12 = 312, + CV_ARM_ND13 = 313, + CV_ARM_ND14 = 314, + CV_ARM_ND15 = 315, + CV_ARM_ND16 = 316, + CV_ARM_ND17 = 317, + CV_ARM_ND18 = 318, + CV_ARM_ND19 = 319, + CV_ARM_ND20 = 320, + CV_ARM_ND21 = 321, + CV_ARM_ND22 = 322, + CV_ARM_ND23 = 323, + CV_ARM_ND24 = 324, + CV_ARM_ND25 = 325, + CV_ARM_ND26 = 326, + CV_ARM_ND27 = 327, + CV_ARM_ND28 = 328, + CV_ARM_ND29 = 329, + CV_ARM_ND30 = 330, + CV_ARM_ND31 = 331, + + // ARM extended precision floating point + + CV_ARM_NQ0 = 400, + CV_ARM_NQ1 = 401, + CV_ARM_NQ2 = 402, + CV_ARM_NQ3 = 403, + CV_ARM_NQ4 = 404, + CV_ARM_NQ5 = 405, + CV_ARM_NQ6 = 406, + CV_ARM_NQ7 = 407, + CV_ARM_NQ8 = 408, + CV_ARM_NQ9 = 409, + CV_ARM_NQ10 = 410, + CV_ARM_NQ11 = 411, + CV_ARM_NQ12 = 412, + CV_ARM_NQ13 = 413, + CV_ARM_NQ14 = 414, + CV_ARM_NQ15 = 415, + + // + // Register set for ARM64 + // + + CV_ARM64_NOREG = CV_REG_NONE, + + // General purpose 32-bit integer registers + + CV_ARM64_W0 = 10, + CV_ARM64_W1 = 11, + CV_ARM64_W2 = 12, + CV_ARM64_W3 = 13, + CV_ARM64_W4 = 14, + CV_ARM64_W5 = 15, + CV_ARM64_W6 = 16, + CV_ARM64_W7 = 17, + CV_ARM64_W8 = 18, + CV_ARM64_W9 = 19, + CV_ARM64_W10 = 20, + CV_ARM64_W11 = 21, + CV_ARM64_W12 = 22, + CV_ARM64_W13 = 23, + CV_ARM64_W14 = 24, + CV_ARM64_W15 = 25, + CV_ARM64_W16 = 26, + CV_ARM64_W17 = 27, + CV_ARM64_W18 = 28, + CV_ARM64_W19 = 29, + CV_ARM64_W20 = 30, + CV_ARM64_W21 = 31, + CV_ARM64_W22 = 32, + CV_ARM64_W23 = 33, + CV_ARM64_W24 = 34, + CV_ARM64_W25 = 35, + CV_ARM64_W26 = 36, + CV_ARM64_W27 = 37, + CV_ARM64_W28 = 38, + CV_ARM64_W29 = 39, + CV_ARM64_W30 = 40, + CV_ARM64_WZR = 41, + + // General purpose 64-bit integer registers + + CV_ARM64_X0 = 50, + CV_ARM64_X1 = 51, + CV_ARM64_X2 = 52, + CV_ARM64_X3 = 53, + CV_ARM64_X4 = 54, + CV_ARM64_X5 = 55, + CV_ARM64_X6 = 56, + CV_ARM64_X7 = 57, + CV_ARM64_X8 = 58, + CV_ARM64_X9 = 59, + CV_ARM64_X10 = 60, + CV_ARM64_X11 = 61, + CV_ARM64_X12 = 62, + CV_ARM64_X13 = 63, + CV_ARM64_X14 = 64, + CV_ARM64_X15 = 65, + CV_ARM64_IP0 = 66, + CV_ARM64_IP1 = 67, + CV_ARM64_X18 = 68, + CV_ARM64_X19 = 69, + CV_ARM64_X20 = 70, + CV_ARM64_X21 = 71, + CV_ARM64_X22 = 72, + CV_ARM64_X23 = 73, + CV_ARM64_X24 = 74, + CV_ARM64_X25 = 75, + CV_ARM64_X26 = 76, + CV_ARM64_X27 = 77, + CV_ARM64_X28 = 78, + CV_ARM64_FP = 79, + CV_ARM64_LR = 80, + CV_ARM64_SP = 81, + CV_ARM64_ZR = 82, + + // statue register + + CV_ARM64_NZCV = 90, + + // 32-bit floating point registers + + CV_ARM64_S0 = 100, + CV_ARM64_S1 = 101, + CV_ARM64_S2 = 102, + CV_ARM64_S3 = 103, + CV_ARM64_S4 = 104, + CV_ARM64_S5 = 105, + CV_ARM64_S6 = 106, + CV_ARM64_S7 = 107, + CV_ARM64_S8 = 108, + CV_ARM64_S9 = 109, + CV_ARM64_S10 = 110, + CV_ARM64_S11 = 111, + CV_ARM64_S12 = 112, + CV_ARM64_S13 = 113, + CV_ARM64_S14 = 114, + CV_ARM64_S15 = 115, + CV_ARM64_S16 = 116, + CV_ARM64_S17 = 117, + CV_ARM64_S18 = 118, + CV_ARM64_S19 = 119, + CV_ARM64_S20 = 120, + CV_ARM64_S21 = 121, + CV_ARM64_S22 = 122, + CV_ARM64_S23 = 123, + CV_ARM64_S24 = 124, + CV_ARM64_S25 = 125, + CV_ARM64_S26 = 126, + CV_ARM64_S27 = 127, + CV_ARM64_S28 = 128, + CV_ARM64_S29 = 129, + CV_ARM64_S30 = 130, + CV_ARM64_S31 = 131, + + // 64-bit floating point registers + + CV_ARM64_D0 = 140, + CV_ARM64_D1 = 141, + CV_ARM64_D2 = 142, + CV_ARM64_D3 = 143, + CV_ARM64_D4 = 144, + CV_ARM64_D5 = 145, + CV_ARM64_D6 = 146, + CV_ARM64_D7 = 147, + CV_ARM64_D8 = 148, + CV_ARM64_D9 = 149, + CV_ARM64_D10 = 150, + CV_ARM64_D11 = 151, + CV_ARM64_D12 = 152, + CV_ARM64_D13 = 153, + CV_ARM64_D14 = 154, + CV_ARM64_D15 = 155, + CV_ARM64_D16 = 156, + CV_ARM64_D17 = 157, + CV_ARM64_D18 = 158, + CV_ARM64_D19 = 159, + CV_ARM64_D20 = 160, + CV_ARM64_D21 = 161, + CV_ARM64_D22 = 162, + CV_ARM64_D23 = 163, + CV_ARM64_D24 = 164, + CV_ARM64_D25 = 165, + CV_ARM64_D26 = 166, + CV_ARM64_D27 = 167, + CV_ARM64_D28 = 168, + CV_ARM64_D29 = 169, + CV_ARM64_D30 = 170, + CV_ARM64_D31 = 171, + + // 128-bit SIMD registers + + CV_ARM64_Q0 = 180, + CV_ARM64_Q1 = 181, + CV_ARM64_Q2 = 182, + CV_ARM64_Q3 = 183, + CV_ARM64_Q4 = 184, + CV_ARM64_Q5 = 185, + CV_ARM64_Q6 = 186, + CV_ARM64_Q7 = 187, + CV_ARM64_Q8 = 188, + CV_ARM64_Q9 = 189, + CV_ARM64_Q10 = 190, + CV_ARM64_Q11 = 191, + CV_ARM64_Q12 = 192, + CV_ARM64_Q13 = 193, + CV_ARM64_Q14 = 194, + CV_ARM64_Q15 = 195, + CV_ARM64_Q16 = 196, + CV_ARM64_Q17 = 197, + CV_ARM64_Q18 = 198, + CV_ARM64_Q19 = 199, + CV_ARM64_Q20 = 200, + CV_ARM64_Q21 = 201, + CV_ARM64_Q22 = 202, + CV_ARM64_Q23 = 203, + CV_ARM64_Q24 = 204, + CV_ARM64_Q25 = 205, + CV_ARM64_Q26 = 206, + CV_ARM64_Q27 = 207, + CV_ARM64_Q28 = 208, + CV_ARM64_Q29 = 209, + CV_ARM64_Q30 = 210, + CV_ARM64_Q31 = 211, + + // Floating point status register + + CV_ARM64_FPSR = 220, + + // + // Register set for Intel IA64 + // + + CV_IA64_NOREG = CV_REG_NONE, + + // Branch Registers + + CV_IA64_Br0 = 512, + CV_IA64_Br1 = 513, + CV_IA64_Br2 = 514, + CV_IA64_Br3 = 515, + CV_IA64_Br4 = 516, + CV_IA64_Br5 = 517, + CV_IA64_Br6 = 518, + CV_IA64_Br7 = 519, + + // Predicate Registers + + CV_IA64_P0 = 704, + CV_IA64_P1 = 705, + CV_IA64_P2 = 706, + CV_IA64_P3 = 707, + CV_IA64_P4 = 708, + CV_IA64_P5 = 709, + CV_IA64_P6 = 710, + CV_IA64_P7 = 711, + CV_IA64_P8 = 712, + CV_IA64_P9 = 713, + CV_IA64_P10 = 714, + CV_IA64_P11 = 715, + CV_IA64_P12 = 716, + CV_IA64_P13 = 717, + CV_IA64_P14 = 718, + CV_IA64_P15 = 719, + CV_IA64_P16 = 720, + CV_IA64_P17 = 721, + CV_IA64_P18 = 722, + CV_IA64_P19 = 723, + CV_IA64_P20 = 724, + CV_IA64_P21 = 725, + CV_IA64_P22 = 726, + CV_IA64_P23 = 727, + CV_IA64_P24 = 728, + CV_IA64_P25 = 729, + CV_IA64_P26 = 730, + CV_IA64_P27 = 731, + CV_IA64_P28 = 732, + CV_IA64_P29 = 733, + CV_IA64_P30 = 734, + CV_IA64_P31 = 735, + CV_IA64_P32 = 736, + CV_IA64_P33 = 737, + CV_IA64_P34 = 738, + CV_IA64_P35 = 739, + CV_IA64_P36 = 740, + CV_IA64_P37 = 741, + CV_IA64_P38 = 742, + CV_IA64_P39 = 743, + CV_IA64_P40 = 744, + CV_IA64_P41 = 745, + CV_IA64_P42 = 746, + CV_IA64_P43 = 747, + CV_IA64_P44 = 748, + CV_IA64_P45 = 749, + CV_IA64_P46 = 750, + CV_IA64_P47 = 751, + CV_IA64_P48 = 752, + CV_IA64_P49 = 753, + CV_IA64_P50 = 754, + CV_IA64_P51 = 755, + CV_IA64_P52 = 756, + CV_IA64_P53 = 757, + CV_IA64_P54 = 758, + CV_IA64_P55 = 759, + CV_IA64_P56 = 760, + CV_IA64_P57 = 761, + CV_IA64_P58 = 762, + CV_IA64_P59 = 763, + CV_IA64_P60 = 764, + CV_IA64_P61 = 765, + CV_IA64_P62 = 766, + CV_IA64_P63 = 767, + + CV_IA64_Preds = 768, + + // Banked General Registers + + CV_IA64_IntH0 = 832, + CV_IA64_IntH1 = 833, + CV_IA64_IntH2 = 834, + CV_IA64_IntH3 = 835, + CV_IA64_IntH4 = 836, + CV_IA64_IntH5 = 837, + CV_IA64_IntH6 = 838, + CV_IA64_IntH7 = 839, + CV_IA64_IntH8 = 840, + CV_IA64_IntH9 = 841, + CV_IA64_IntH10 = 842, + CV_IA64_IntH11 = 843, + CV_IA64_IntH12 = 844, + CV_IA64_IntH13 = 845, + CV_IA64_IntH14 = 846, + CV_IA64_IntH15 = 847, + + // Special Registers + + CV_IA64_Ip = 1016, + CV_IA64_Umask = 1017, + CV_IA64_Cfm = 1018, + CV_IA64_Psr = 1019, + + // Banked General Registers + + CV_IA64_Nats = 1020, + CV_IA64_Nats2 = 1021, + CV_IA64_Nats3 = 1022, + + // General-Purpose Registers + + // Integer registers + CV_IA64_IntR0 = 1024, + CV_IA64_IntR1 = 1025, + CV_IA64_IntR2 = 1026, + CV_IA64_IntR3 = 1027, + CV_IA64_IntR4 = 1028, + CV_IA64_IntR5 = 1029, + CV_IA64_IntR6 = 1030, + CV_IA64_IntR7 = 1031, + CV_IA64_IntR8 = 1032, + CV_IA64_IntR9 = 1033, + CV_IA64_IntR10 = 1034, + CV_IA64_IntR11 = 1035, + CV_IA64_IntR12 = 1036, + CV_IA64_IntR13 = 1037, + CV_IA64_IntR14 = 1038, + CV_IA64_IntR15 = 1039, + CV_IA64_IntR16 = 1040, + CV_IA64_IntR17 = 1041, + CV_IA64_IntR18 = 1042, + CV_IA64_IntR19 = 1043, + CV_IA64_IntR20 = 1044, + CV_IA64_IntR21 = 1045, + CV_IA64_IntR22 = 1046, + CV_IA64_IntR23 = 1047, + CV_IA64_IntR24 = 1048, + CV_IA64_IntR25 = 1049, + CV_IA64_IntR26 = 1050, + CV_IA64_IntR27 = 1051, + CV_IA64_IntR28 = 1052, + CV_IA64_IntR29 = 1053, + CV_IA64_IntR30 = 1054, + CV_IA64_IntR31 = 1055, + + // Register Stack + CV_IA64_IntR32 = 1056, + CV_IA64_IntR33 = 1057, + CV_IA64_IntR34 = 1058, + CV_IA64_IntR35 = 1059, + CV_IA64_IntR36 = 1060, + CV_IA64_IntR37 = 1061, + CV_IA64_IntR38 = 1062, + CV_IA64_IntR39 = 1063, + CV_IA64_IntR40 = 1064, + CV_IA64_IntR41 = 1065, + CV_IA64_IntR42 = 1066, + CV_IA64_IntR43 = 1067, + CV_IA64_IntR44 = 1068, + CV_IA64_IntR45 = 1069, + CV_IA64_IntR46 = 1070, + CV_IA64_IntR47 = 1071, + CV_IA64_IntR48 = 1072, + CV_IA64_IntR49 = 1073, + CV_IA64_IntR50 = 1074, + CV_IA64_IntR51 = 1075, + CV_IA64_IntR52 = 1076, + CV_IA64_IntR53 = 1077, + CV_IA64_IntR54 = 1078, + CV_IA64_IntR55 = 1079, + CV_IA64_IntR56 = 1080, + CV_IA64_IntR57 = 1081, + CV_IA64_IntR58 = 1082, + CV_IA64_IntR59 = 1083, + CV_IA64_IntR60 = 1084, + CV_IA64_IntR61 = 1085, + CV_IA64_IntR62 = 1086, + CV_IA64_IntR63 = 1087, + CV_IA64_IntR64 = 1088, + CV_IA64_IntR65 = 1089, + CV_IA64_IntR66 = 1090, + CV_IA64_IntR67 = 1091, + CV_IA64_IntR68 = 1092, + CV_IA64_IntR69 = 1093, + CV_IA64_IntR70 = 1094, + CV_IA64_IntR71 = 1095, + CV_IA64_IntR72 = 1096, + CV_IA64_IntR73 = 1097, + CV_IA64_IntR74 = 1098, + CV_IA64_IntR75 = 1099, + CV_IA64_IntR76 = 1100, + CV_IA64_IntR77 = 1101, + CV_IA64_IntR78 = 1102, + CV_IA64_IntR79 = 1103, + CV_IA64_IntR80 = 1104, + CV_IA64_IntR81 = 1105, + CV_IA64_IntR82 = 1106, + CV_IA64_IntR83 = 1107, + CV_IA64_IntR84 = 1108, + CV_IA64_IntR85 = 1109, + CV_IA64_IntR86 = 1110, + CV_IA64_IntR87 = 1111, + CV_IA64_IntR88 = 1112, + CV_IA64_IntR89 = 1113, + CV_IA64_IntR90 = 1114, + CV_IA64_IntR91 = 1115, + CV_IA64_IntR92 = 1116, + CV_IA64_IntR93 = 1117, + CV_IA64_IntR94 = 1118, + CV_IA64_IntR95 = 1119, + CV_IA64_IntR96 = 1120, + CV_IA64_IntR97 = 1121, + CV_IA64_IntR98 = 1122, + CV_IA64_IntR99 = 1123, + CV_IA64_IntR100 = 1124, + CV_IA64_IntR101 = 1125, + CV_IA64_IntR102 = 1126, + CV_IA64_IntR103 = 1127, + CV_IA64_IntR104 = 1128, + CV_IA64_IntR105 = 1129, + CV_IA64_IntR106 = 1130, + CV_IA64_IntR107 = 1131, + CV_IA64_IntR108 = 1132, + CV_IA64_IntR109 = 1133, + CV_IA64_IntR110 = 1134, + CV_IA64_IntR111 = 1135, + CV_IA64_IntR112 = 1136, + CV_IA64_IntR113 = 1137, + CV_IA64_IntR114 = 1138, + CV_IA64_IntR115 = 1139, + CV_IA64_IntR116 = 1140, + CV_IA64_IntR117 = 1141, + CV_IA64_IntR118 = 1142, + CV_IA64_IntR119 = 1143, + CV_IA64_IntR120 = 1144, + CV_IA64_IntR121 = 1145, + CV_IA64_IntR122 = 1146, + CV_IA64_IntR123 = 1147, + CV_IA64_IntR124 = 1148, + CV_IA64_IntR125 = 1149, + CV_IA64_IntR126 = 1150, + CV_IA64_IntR127 = 1151, + + // Floating-Point Registers + + // Low Floating Point Registers + CV_IA64_FltF0 = 2048, + CV_IA64_FltF1 = 2049, + CV_IA64_FltF2 = 2050, + CV_IA64_FltF3 = 2051, + CV_IA64_FltF4 = 2052, + CV_IA64_FltF5 = 2053, + CV_IA64_FltF6 = 2054, + CV_IA64_FltF7 = 2055, + CV_IA64_FltF8 = 2056, + CV_IA64_FltF9 = 2057, + CV_IA64_FltF10 = 2058, + CV_IA64_FltF11 = 2059, + CV_IA64_FltF12 = 2060, + CV_IA64_FltF13 = 2061, + CV_IA64_FltF14 = 2062, + CV_IA64_FltF15 = 2063, + CV_IA64_FltF16 = 2064, + CV_IA64_FltF17 = 2065, + CV_IA64_FltF18 = 2066, + CV_IA64_FltF19 = 2067, + CV_IA64_FltF20 = 2068, + CV_IA64_FltF21 = 2069, + CV_IA64_FltF22 = 2070, + CV_IA64_FltF23 = 2071, + CV_IA64_FltF24 = 2072, + CV_IA64_FltF25 = 2073, + CV_IA64_FltF26 = 2074, + CV_IA64_FltF27 = 2075, + CV_IA64_FltF28 = 2076, + CV_IA64_FltF29 = 2077, + CV_IA64_FltF30 = 2078, + CV_IA64_FltF31 = 2079, + + // High Floating Point Registers + CV_IA64_FltF32 = 2080, + CV_IA64_FltF33 = 2081, + CV_IA64_FltF34 = 2082, + CV_IA64_FltF35 = 2083, + CV_IA64_FltF36 = 2084, + CV_IA64_FltF37 = 2085, + CV_IA64_FltF38 = 2086, + CV_IA64_FltF39 = 2087, + CV_IA64_FltF40 = 2088, + CV_IA64_FltF41 = 2089, + CV_IA64_FltF42 = 2090, + CV_IA64_FltF43 = 2091, + CV_IA64_FltF44 = 2092, + CV_IA64_FltF45 = 2093, + CV_IA64_FltF46 = 2094, + CV_IA64_FltF47 = 2095, + CV_IA64_FltF48 = 2096, + CV_IA64_FltF49 = 2097, + CV_IA64_FltF50 = 2098, + CV_IA64_FltF51 = 2099, + CV_IA64_FltF52 = 2100, + CV_IA64_FltF53 = 2101, + CV_IA64_FltF54 = 2102, + CV_IA64_FltF55 = 2103, + CV_IA64_FltF56 = 2104, + CV_IA64_FltF57 = 2105, + CV_IA64_FltF58 = 2106, + CV_IA64_FltF59 = 2107, + CV_IA64_FltF60 = 2108, + CV_IA64_FltF61 = 2109, + CV_IA64_FltF62 = 2110, + CV_IA64_FltF63 = 2111, + CV_IA64_FltF64 = 2112, + CV_IA64_FltF65 = 2113, + CV_IA64_FltF66 = 2114, + CV_IA64_FltF67 = 2115, + CV_IA64_FltF68 = 2116, + CV_IA64_FltF69 = 2117, + CV_IA64_FltF70 = 2118, + CV_IA64_FltF71 = 2119, + CV_IA64_FltF72 = 2120, + CV_IA64_FltF73 = 2121, + CV_IA64_FltF74 = 2122, + CV_IA64_FltF75 = 2123, + CV_IA64_FltF76 = 2124, + CV_IA64_FltF77 = 2125, + CV_IA64_FltF78 = 2126, + CV_IA64_FltF79 = 2127, + CV_IA64_FltF80 = 2128, + CV_IA64_FltF81 = 2129, + CV_IA64_FltF82 = 2130, + CV_IA64_FltF83 = 2131, + CV_IA64_FltF84 = 2132, + CV_IA64_FltF85 = 2133, + CV_IA64_FltF86 = 2134, + CV_IA64_FltF87 = 2135, + CV_IA64_FltF88 = 2136, + CV_IA64_FltF89 = 2137, + CV_IA64_FltF90 = 2138, + CV_IA64_FltF91 = 2139, + CV_IA64_FltF92 = 2140, + CV_IA64_FltF93 = 2141, + CV_IA64_FltF94 = 2142, + CV_IA64_FltF95 = 2143, + CV_IA64_FltF96 = 2144, + CV_IA64_FltF97 = 2145, + CV_IA64_FltF98 = 2146, + CV_IA64_FltF99 = 2147, + CV_IA64_FltF100 = 2148, + CV_IA64_FltF101 = 2149, + CV_IA64_FltF102 = 2150, + CV_IA64_FltF103 = 2151, + CV_IA64_FltF104 = 2152, + CV_IA64_FltF105 = 2153, + CV_IA64_FltF106 = 2154, + CV_IA64_FltF107 = 2155, + CV_IA64_FltF108 = 2156, + CV_IA64_FltF109 = 2157, + CV_IA64_FltF110 = 2158, + CV_IA64_FltF111 = 2159, + CV_IA64_FltF112 = 2160, + CV_IA64_FltF113 = 2161, + CV_IA64_FltF114 = 2162, + CV_IA64_FltF115 = 2163, + CV_IA64_FltF116 = 2164, + CV_IA64_FltF117 = 2165, + CV_IA64_FltF118 = 2166, + CV_IA64_FltF119 = 2167, + CV_IA64_FltF120 = 2168, + CV_IA64_FltF121 = 2169, + CV_IA64_FltF122 = 2170, + CV_IA64_FltF123 = 2171, + CV_IA64_FltF124 = 2172, + CV_IA64_FltF125 = 2173, + CV_IA64_FltF126 = 2174, + CV_IA64_FltF127 = 2175, + + // Application Registers + + CV_IA64_ApKR0 = 3072, + CV_IA64_ApKR1 = 3073, + CV_IA64_ApKR2 = 3074, + CV_IA64_ApKR3 = 3075, + CV_IA64_ApKR4 = 3076, + CV_IA64_ApKR5 = 3077, + CV_IA64_ApKR6 = 3078, + CV_IA64_ApKR7 = 3079, + CV_IA64_AR8 = 3080, + CV_IA64_AR9 = 3081, + CV_IA64_AR10 = 3082, + CV_IA64_AR11 = 3083, + CV_IA64_AR12 = 3084, + CV_IA64_AR13 = 3085, + CV_IA64_AR14 = 3086, + CV_IA64_AR15 = 3087, + CV_IA64_RsRSC = 3088, + CV_IA64_RsBSP = 3089, + CV_IA64_RsBSPSTORE = 3090, + CV_IA64_RsRNAT = 3091, + CV_IA64_AR20 = 3092, + CV_IA64_StFCR = 3093, + CV_IA64_AR22 = 3094, + CV_IA64_AR23 = 3095, + CV_IA64_EFLAG = 3096, + CV_IA64_CSD = 3097, + CV_IA64_SSD = 3098, + CV_IA64_CFLG = 3099, + CV_IA64_StFSR = 3100, + CV_IA64_StFIR = 3101, + CV_IA64_StFDR = 3102, + CV_IA64_AR31 = 3103, + CV_IA64_ApCCV = 3104, + CV_IA64_AR33 = 3105, + CV_IA64_AR34 = 3106, + CV_IA64_AR35 = 3107, + CV_IA64_ApUNAT = 3108, + CV_IA64_AR37 = 3109, + CV_IA64_AR38 = 3110, + CV_IA64_AR39 = 3111, + CV_IA64_StFPSR = 3112, + CV_IA64_AR41 = 3113, + CV_IA64_AR42 = 3114, + CV_IA64_AR43 = 3115, + CV_IA64_ApITC = 3116, + CV_IA64_AR45 = 3117, + CV_IA64_AR46 = 3118, + CV_IA64_AR47 = 3119, + CV_IA64_AR48 = 3120, + CV_IA64_AR49 = 3121, + CV_IA64_AR50 = 3122, + CV_IA64_AR51 = 3123, + CV_IA64_AR52 = 3124, + CV_IA64_AR53 = 3125, + CV_IA64_AR54 = 3126, + CV_IA64_AR55 = 3127, + CV_IA64_AR56 = 3128, + CV_IA64_AR57 = 3129, + CV_IA64_AR58 = 3130, + CV_IA64_AR59 = 3131, + CV_IA64_AR60 = 3132, + CV_IA64_AR61 = 3133, + CV_IA64_AR62 = 3134, + CV_IA64_AR63 = 3135, + CV_IA64_RsPFS = 3136, + CV_IA64_ApLC = 3137, + CV_IA64_ApEC = 3138, + CV_IA64_AR67 = 3139, + CV_IA64_AR68 = 3140, + CV_IA64_AR69 = 3141, + CV_IA64_AR70 = 3142, + CV_IA64_AR71 = 3143, + CV_IA64_AR72 = 3144, + CV_IA64_AR73 = 3145, + CV_IA64_AR74 = 3146, + CV_IA64_AR75 = 3147, + CV_IA64_AR76 = 3148, + CV_IA64_AR77 = 3149, + CV_IA64_AR78 = 3150, + CV_IA64_AR79 = 3151, + CV_IA64_AR80 = 3152, + CV_IA64_AR81 = 3153, + CV_IA64_AR82 = 3154, + CV_IA64_AR83 = 3155, + CV_IA64_AR84 = 3156, + CV_IA64_AR85 = 3157, + CV_IA64_AR86 = 3158, + CV_IA64_AR87 = 3159, + CV_IA64_AR88 = 3160, + CV_IA64_AR89 = 3161, + CV_IA64_AR90 = 3162, + CV_IA64_AR91 = 3163, + CV_IA64_AR92 = 3164, + CV_IA64_AR93 = 3165, + CV_IA64_AR94 = 3166, + CV_IA64_AR95 = 3167, + CV_IA64_AR96 = 3168, + CV_IA64_AR97 = 3169, + CV_IA64_AR98 = 3170, + CV_IA64_AR99 = 3171, + CV_IA64_AR100 = 3172, + CV_IA64_AR101 = 3173, + CV_IA64_AR102 = 3174, + CV_IA64_AR103 = 3175, + CV_IA64_AR104 = 3176, + CV_IA64_AR105 = 3177, + CV_IA64_AR106 = 3178, + CV_IA64_AR107 = 3179, + CV_IA64_AR108 = 3180, + CV_IA64_AR109 = 3181, + CV_IA64_AR110 = 3182, + CV_IA64_AR111 = 3183, + CV_IA64_AR112 = 3184, + CV_IA64_AR113 = 3185, + CV_IA64_AR114 = 3186, + CV_IA64_AR115 = 3187, + CV_IA64_AR116 = 3188, + CV_IA64_AR117 = 3189, + CV_IA64_AR118 = 3190, + CV_IA64_AR119 = 3191, + CV_IA64_AR120 = 3192, + CV_IA64_AR121 = 3193, + CV_IA64_AR122 = 3194, + CV_IA64_AR123 = 3195, + CV_IA64_AR124 = 3196, + CV_IA64_AR125 = 3197, + CV_IA64_AR126 = 3198, + CV_IA64_AR127 = 3199, + + // CPUID Registers + + CV_IA64_CPUID0 = 3328, + CV_IA64_CPUID1 = 3329, + CV_IA64_CPUID2 = 3330, + CV_IA64_CPUID3 = 3331, + CV_IA64_CPUID4 = 3332, + + // Control Registers + + CV_IA64_ApDCR = 4096, + CV_IA64_ApITM = 4097, + CV_IA64_ApIVA = 4098, + CV_IA64_CR3 = 4099, + CV_IA64_CR4 = 4100, + CV_IA64_CR5 = 4101, + CV_IA64_CR6 = 4102, + CV_IA64_CR7 = 4103, + CV_IA64_ApPTA = 4104, + CV_IA64_ApGPTA = 4105, + CV_IA64_CR10 = 4106, + CV_IA64_CR11 = 4107, + CV_IA64_CR12 = 4108, + CV_IA64_CR13 = 4109, + CV_IA64_CR14 = 4110, + CV_IA64_CR15 = 4111, + CV_IA64_StIPSR = 4112, + CV_IA64_StISR = 4113, + CV_IA64_CR18 = 4114, + CV_IA64_StIIP = 4115, + CV_IA64_StIFA = 4116, + CV_IA64_StITIR = 4117, + CV_IA64_StIIPA = 4118, + CV_IA64_StIFS = 4119, + CV_IA64_StIIM = 4120, + CV_IA64_StIHA = 4121, + CV_IA64_CR26 = 4122, + CV_IA64_CR27 = 4123, + CV_IA64_CR28 = 4124, + CV_IA64_CR29 = 4125, + CV_IA64_CR30 = 4126, + CV_IA64_CR31 = 4127, + CV_IA64_CR32 = 4128, + CV_IA64_CR33 = 4129, + CV_IA64_CR34 = 4130, + CV_IA64_CR35 = 4131, + CV_IA64_CR36 = 4132, + CV_IA64_CR37 = 4133, + CV_IA64_CR38 = 4134, + CV_IA64_CR39 = 4135, + CV_IA64_CR40 = 4136, + CV_IA64_CR41 = 4137, + CV_IA64_CR42 = 4138, + CV_IA64_CR43 = 4139, + CV_IA64_CR44 = 4140, + CV_IA64_CR45 = 4141, + CV_IA64_CR46 = 4142, + CV_IA64_CR47 = 4143, + CV_IA64_CR48 = 4144, + CV_IA64_CR49 = 4145, + CV_IA64_CR50 = 4146, + CV_IA64_CR51 = 4147, + CV_IA64_CR52 = 4148, + CV_IA64_CR53 = 4149, + CV_IA64_CR54 = 4150, + CV_IA64_CR55 = 4151, + CV_IA64_CR56 = 4152, + CV_IA64_CR57 = 4153, + CV_IA64_CR58 = 4154, + CV_IA64_CR59 = 4155, + CV_IA64_CR60 = 4156, + CV_IA64_CR61 = 4157, + CV_IA64_CR62 = 4158, + CV_IA64_CR63 = 4159, + CV_IA64_SaLID = 4160, + CV_IA64_SaIVR = 4161, + CV_IA64_SaTPR = 4162, + CV_IA64_SaEOI = 4163, + CV_IA64_SaIRR0 = 4164, + CV_IA64_SaIRR1 = 4165, + CV_IA64_SaIRR2 = 4166, + CV_IA64_SaIRR3 = 4167, + CV_IA64_SaITV = 4168, + CV_IA64_SaPMV = 4169, + CV_IA64_SaCMCV = 4170, + CV_IA64_CR75 = 4171, + CV_IA64_CR76 = 4172, + CV_IA64_CR77 = 4173, + CV_IA64_CR78 = 4174, + CV_IA64_CR79 = 4175, + CV_IA64_SaLRR0 = 4176, + CV_IA64_SaLRR1 = 4177, + CV_IA64_CR82 = 4178, + CV_IA64_CR83 = 4179, + CV_IA64_CR84 = 4180, + CV_IA64_CR85 = 4181, + CV_IA64_CR86 = 4182, + CV_IA64_CR87 = 4183, + CV_IA64_CR88 = 4184, + CV_IA64_CR89 = 4185, + CV_IA64_CR90 = 4186, + CV_IA64_CR91 = 4187, + CV_IA64_CR92 = 4188, + CV_IA64_CR93 = 4189, + CV_IA64_CR94 = 4190, + CV_IA64_CR95 = 4191, + CV_IA64_CR96 = 4192, + CV_IA64_CR97 = 4193, + CV_IA64_CR98 = 4194, + CV_IA64_CR99 = 4195, + CV_IA64_CR100 = 4196, + CV_IA64_CR101 = 4197, + CV_IA64_CR102 = 4198, + CV_IA64_CR103 = 4199, + CV_IA64_CR104 = 4200, + CV_IA64_CR105 = 4201, + CV_IA64_CR106 = 4202, + CV_IA64_CR107 = 4203, + CV_IA64_CR108 = 4204, + CV_IA64_CR109 = 4205, + CV_IA64_CR110 = 4206, + CV_IA64_CR111 = 4207, + CV_IA64_CR112 = 4208, + CV_IA64_CR113 = 4209, + CV_IA64_CR114 = 4210, + CV_IA64_CR115 = 4211, + CV_IA64_CR116 = 4212, + CV_IA64_CR117 = 4213, + CV_IA64_CR118 = 4214, + CV_IA64_CR119 = 4215, + CV_IA64_CR120 = 4216, + CV_IA64_CR121 = 4217, + CV_IA64_CR122 = 4218, + CV_IA64_CR123 = 4219, + CV_IA64_CR124 = 4220, + CV_IA64_CR125 = 4221, + CV_IA64_CR126 = 4222, + CV_IA64_CR127 = 4223, + + // Protection Key Registers + + CV_IA64_Pkr0 = 5120, + CV_IA64_Pkr1 = 5121, + CV_IA64_Pkr2 = 5122, + CV_IA64_Pkr3 = 5123, + CV_IA64_Pkr4 = 5124, + CV_IA64_Pkr5 = 5125, + CV_IA64_Pkr6 = 5126, + CV_IA64_Pkr7 = 5127, + CV_IA64_Pkr8 = 5128, + CV_IA64_Pkr9 = 5129, + CV_IA64_Pkr10 = 5130, + CV_IA64_Pkr11 = 5131, + CV_IA64_Pkr12 = 5132, + CV_IA64_Pkr13 = 5133, + CV_IA64_Pkr14 = 5134, + CV_IA64_Pkr15 = 5135, + + // Region Registers + + CV_IA64_Rr0 = 6144, + CV_IA64_Rr1 = 6145, + CV_IA64_Rr2 = 6146, + CV_IA64_Rr3 = 6147, + CV_IA64_Rr4 = 6148, + CV_IA64_Rr5 = 6149, + CV_IA64_Rr6 = 6150, + CV_IA64_Rr7 = 6151, + + // Performance Monitor Data Registers + + CV_IA64_PFD0 = 7168, + CV_IA64_PFD1 = 7169, + CV_IA64_PFD2 = 7170, + CV_IA64_PFD3 = 7171, + CV_IA64_PFD4 = 7172, + CV_IA64_PFD5 = 7173, + CV_IA64_PFD6 = 7174, + CV_IA64_PFD7 = 7175, + CV_IA64_PFD8 = 7176, + CV_IA64_PFD9 = 7177, + CV_IA64_PFD10 = 7178, + CV_IA64_PFD11 = 7179, + CV_IA64_PFD12 = 7180, + CV_IA64_PFD13 = 7181, + CV_IA64_PFD14 = 7182, + CV_IA64_PFD15 = 7183, + CV_IA64_PFD16 = 7184, + CV_IA64_PFD17 = 7185, + + // Performance Monitor Config Registers + + CV_IA64_PFC0 = 7424, + CV_IA64_PFC1 = 7425, + CV_IA64_PFC2 = 7426, + CV_IA64_PFC3 = 7427, + CV_IA64_PFC4 = 7428, + CV_IA64_PFC5 = 7429, + CV_IA64_PFC6 = 7430, + CV_IA64_PFC7 = 7431, + CV_IA64_PFC8 = 7432, + CV_IA64_PFC9 = 7433, + CV_IA64_PFC10 = 7434, + CV_IA64_PFC11 = 7435, + CV_IA64_PFC12 = 7436, + CV_IA64_PFC13 = 7437, + CV_IA64_PFC14 = 7438, + CV_IA64_PFC15 = 7439, + + // Instruction Translation Registers + + CV_IA64_TrI0 = 8192, + CV_IA64_TrI1 = 8193, + CV_IA64_TrI2 = 8194, + CV_IA64_TrI3 = 8195, + CV_IA64_TrI4 = 8196, + CV_IA64_TrI5 = 8197, + CV_IA64_TrI6 = 8198, + CV_IA64_TrI7 = 8199, + + // Data Translation Registers + + CV_IA64_TrD0 = 8320, + CV_IA64_TrD1 = 8321, + CV_IA64_TrD2 = 8322, + CV_IA64_TrD3 = 8323, + CV_IA64_TrD4 = 8324, + CV_IA64_TrD5 = 8325, + CV_IA64_TrD6 = 8326, + CV_IA64_TrD7 = 8327, + + // Instruction Breakpoint Registers + + CV_IA64_DbI0 = 8448, + CV_IA64_DbI1 = 8449, + CV_IA64_DbI2 = 8450, + CV_IA64_DbI3 = 8451, + CV_IA64_DbI4 = 8452, + CV_IA64_DbI5 = 8453, + CV_IA64_DbI6 = 8454, + CV_IA64_DbI7 = 8455, + + // Data Breakpoint Registers + + CV_IA64_DbD0 = 8576, + CV_IA64_DbD1 = 8577, + CV_IA64_DbD2 = 8578, + CV_IA64_DbD3 = 8579, + CV_IA64_DbD4 = 8580, + CV_IA64_DbD5 = 8581, + CV_IA64_DbD6 = 8582, + CV_IA64_DbD7 = 8583, + + // + // Register set for the TriCore processor. + // + + CV_TRI_NOREG = CV_REG_NONE, + + // General Purpose Data Registers + + CV_TRI_D0 = 10, + CV_TRI_D1 = 11, + CV_TRI_D2 = 12, + CV_TRI_D3 = 13, + CV_TRI_D4 = 14, + CV_TRI_D5 = 15, + CV_TRI_D6 = 16, + CV_TRI_D7 = 17, + CV_TRI_D8 = 18, + CV_TRI_D9 = 19, + CV_TRI_D10 = 20, + CV_TRI_D11 = 21, + CV_TRI_D12 = 22, + CV_TRI_D13 = 23, + CV_TRI_D14 = 24, + CV_TRI_D15 = 25, + + // General Purpose Address Registers + + CV_TRI_A0 = 26, + CV_TRI_A1 = 27, + CV_TRI_A2 = 28, + CV_TRI_A3 = 29, + CV_TRI_A4 = 30, + CV_TRI_A5 = 31, + CV_TRI_A6 = 32, + CV_TRI_A7 = 33, + CV_TRI_A8 = 34, + CV_TRI_A9 = 35, + CV_TRI_A10 = 36, + CV_TRI_A11 = 37, + CV_TRI_A12 = 38, + CV_TRI_A13 = 39, + CV_TRI_A14 = 40, + CV_TRI_A15 = 41, + + // Extended (64-bit) data registers + + CV_TRI_E0 = 42, + CV_TRI_E2 = 43, + CV_TRI_E4 = 44, + CV_TRI_E6 = 45, + CV_TRI_E8 = 46, + CV_TRI_E10 = 47, + CV_TRI_E12 = 48, + CV_TRI_E14 = 49, + + // Extended (64-bit) address registers + + CV_TRI_EA0 = 50, + CV_TRI_EA2 = 51, + CV_TRI_EA4 = 52, + CV_TRI_EA6 = 53, + CV_TRI_EA8 = 54, + CV_TRI_EA10 = 55, + CV_TRI_EA12 = 56, + CV_TRI_EA14 = 57, + + CV_TRI_PSW = 58, + CV_TRI_PCXI = 59, + CV_TRI_PC = 60, + CV_TRI_FCX = 61, + CV_TRI_LCX = 62, + CV_TRI_ISP = 63, + CV_TRI_ICR = 64, + CV_TRI_BIV = 65, + CV_TRI_BTV = 66, + CV_TRI_SYSCON = 67, + CV_TRI_DPRx_0 = 68, + CV_TRI_DPRx_1 = 69, + CV_TRI_DPRx_2 = 70, + CV_TRI_DPRx_3 = 71, + CV_TRI_CPRx_0 = 68, + CV_TRI_CPRx_1 = 69, + CV_TRI_CPRx_2 = 70, + CV_TRI_CPRx_3 = 71, + CV_TRI_DPMx_0 = 68, + CV_TRI_DPMx_1 = 69, + CV_TRI_DPMx_2 = 70, + CV_TRI_DPMx_3 = 71, + CV_TRI_CPMx_0 = 68, + CV_TRI_CPMx_1 = 69, + CV_TRI_CPMx_2 = 70, + CV_TRI_CPMx_3 = 71, + CV_TRI_DBGSSR = 72, + CV_TRI_EXEVT = 73, + CV_TRI_SWEVT = 74, + CV_TRI_CREVT = 75, + CV_TRI_TRnEVT = 76, + CV_TRI_MMUCON = 77, + CV_TRI_ASI = 78, + CV_TRI_TVA = 79, + CV_TRI_TPA = 80, + CV_TRI_TPX = 81, + CV_TRI_TFA = 82, + + // + // Register set for the AM33 and related processors. + // + + CV_AM33_NOREG = CV_REG_NONE, + + // "Extended" (general purpose integer) registers + CV_AM33_E0 = 10, + CV_AM33_E1 = 11, + CV_AM33_E2 = 12, + CV_AM33_E3 = 13, + CV_AM33_E4 = 14, + CV_AM33_E5 = 15, + CV_AM33_E6 = 16, + CV_AM33_E7 = 17, + + // Address registers + CV_AM33_A0 = 20, + CV_AM33_A1 = 21, + CV_AM33_A2 = 22, + CV_AM33_A3 = 23, + + // Integer data registers + CV_AM33_D0 = 30, + CV_AM33_D1 = 31, + CV_AM33_D2 = 32, + CV_AM33_D3 = 33, + + // (Single-precision) floating-point registers + CV_AM33_FS0 = 40, + CV_AM33_FS1 = 41, + CV_AM33_FS2 = 42, + CV_AM33_FS3 = 43, + CV_AM33_FS4 = 44, + CV_AM33_FS5 = 45, + CV_AM33_FS6 = 46, + CV_AM33_FS7 = 47, + CV_AM33_FS8 = 48, + CV_AM33_FS9 = 49, + CV_AM33_FS10 = 50, + CV_AM33_FS11 = 51, + CV_AM33_FS12 = 52, + CV_AM33_FS13 = 53, + CV_AM33_FS14 = 54, + CV_AM33_FS15 = 55, + CV_AM33_FS16 = 56, + CV_AM33_FS17 = 57, + CV_AM33_FS18 = 58, + CV_AM33_FS19 = 59, + CV_AM33_FS20 = 60, + CV_AM33_FS21 = 61, + CV_AM33_FS22 = 62, + CV_AM33_FS23 = 63, + CV_AM33_FS24 = 64, + CV_AM33_FS25 = 65, + CV_AM33_FS26 = 66, + CV_AM33_FS27 = 67, + CV_AM33_FS28 = 68, + CV_AM33_FS29 = 69, + CV_AM33_FS30 = 70, + CV_AM33_FS31 = 71, + + // Special purpose registers + + // Stack pointer + CV_AM33_SP = 80, + + // Program counter + CV_AM33_PC = 81, + + // Multiply-divide/accumulate registers + CV_AM33_MDR = 82, + CV_AM33_MDRQ = 83, + CV_AM33_MCRH = 84, + CV_AM33_MCRL = 85, + CV_AM33_MCVF = 86, + + // CPU status words + CV_AM33_EPSW = 87, + CV_AM33_FPCR = 88, + + // Loop buffer registers + CV_AM33_LIR = 89, + CV_AM33_LAR = 90, + + // + // Register set for the Mitsubishi M32R + // + + CV_M32R_NOREG = CV_REG_NONE, + + CV_M32R_R0 = 10, + CV_M32R_R1 = 11, + CV_M32R_R2 = 12, + CV_M32R_R3 = 13, + CV_M32R_R4 = 14, + CV_M32R_R5 = 15, + CV_M32R_R6 = 16, + CV_M32R_R7 = 17, + CV_M32R_R8 = 18, + CV_M32R_R9 = 19, + CV_M32R_R10 = 20, + CV_M32R_R11 = 21, + CV_M32R_R12 = 22, // Gloabal Pointer, if used + CV_M32R_R13 = 23, // Frame Pointer, if allocated + CV_M32R_R14 = 24, // Link Register + CV_M32R_R15 = 25, // Stack Pointer + CV_M32R_PSW = 26, // Preocessor Status Register + CV_M32R_CBR = 27, // Condition Bit Register + CV_M32R_SPI = 28, // Interrupt Stack Pointer + CV_M32R_SPU = 29, // User Stack Pointer + CV_M32R_SPO = 30, // OS Stack Pointer + CV_M32R_BPC = 31, // Backup Program Counter + CV_M32R_ACHI = 32, // Accumulator High + CV_M32R_ACLO = 33, // Accumulator Low + CV_M32R_PC = 34, // Program Counter + + // + // Register set for the SuperH SHMedia processor including compact + // mode + // + + // Integer - 64 bit general registers + CV_SHMEDIA_NOREG = CV_REG_NONE, + CV_SHMEDIA_R0 = 10, + CV_SHMEDIA_R1 = 11, + CV_SHMEDIA_R2 = 12, + CV_SHMEDIA_R3 = 13, + CV_SHMEDIA_R4 = 14, + CV_SHMEDIA_R5 = 15, + CV_SHMEDIA_R6 = 16, + CV_SHMEDIA_R7 = 17, + CV_SHMEDIA_R8 = 18, + CV_SHMEDIA_R9 = 19, + CV_SHMEDIA_R10 = 20, + CV_SHMEDIA_R11 = 21, + CV_SHMEDIA_R12 = 22, + CV_SHMEDIA_R13 = 23, + CV_SHMEDIA_R14 = 24, + CV_SHMEDIA_R15 = 25, + CV_SHMEDIA_R16 = 26, + CV_SHMEDIA_R17 = 27, + CV_SHMEDIA_R18 = 28, + CV_SHMEDIA_R19 = 29, + CV_SHMEDIA_R20 = 30, + CV_SHMEDIA_R21 = 31, + CV_SHMEDIA_R22 = 32, + CV_SHMEDIA_R23 = 33, + CV_SHMEDIA_R24 = 34, + CV_SHMEDIA_R25 = 35, + CV_SHMEDIA_R26 = 36, + CV_SHMEDIA_R27 = 37, + CV_SHMEDIA_R28 = 38, + CV_SHMEDIA_R29 = 39, + CV_SHMEDIA_R30 = 40, + CV_SHMEDIA_R31 = 41, + CV_SHMEDIA_R32 = 42, + CV_SHMEDIA_R33 = 43, + CV_SHMEDIA_R34 = 44, + CV_SHMEDIA_R35 = 45, + CV_SHMEDIA_R36 = 46, + CV_SHMEDIA_R37 = 47, + CV_SHMEDIA_R38 = 48, + CV_SHMEDIA_R39 = 49, + CV_SHMEDIA_R40 = 50, + CV_SHMEDIA_R41 = 51, + CV_SHMEDIA_R42 = 52, + CV_SHMEDIA_R43 = 53, + CV_SHMEDIA_R44 = 54, + CV_SHMEDIA_R45 = 55, + CV_SHMEDIA_R46 = 56, + CV_SHMEDIA_R47 = 57, + CV_SHMEDIA_R48 = 58, + CV_SHMEDIA_R49 = 59, + CV_SHMEDIA_R50 = 60, + CV_SHMEDIA_R51 = 61, + CV_SHMEDIA_R52 = 62, + CV_SHMEDIA_R53 = 63, + CV_SHMEDIA_R54 = 64, + CV_SHMEDIA_R55 = 65, + CV_SHMEDIA_R56 = 66, + CV_SHMEDIA_R57 = 67, + CV_SHMEDIA_R58 = 68, + CV_SHMEDIA_R59 = 69, + CV_SHMEDIA_R60 = 70, + CV_SHMEDIA_R61 = 71, + CV_SHMEDIA_R62 = 72, + CV_SHMEDIA_R63 = 73, + + // Target Registers - 32 bit + CV_SHMEDIA_TR0 = 74, + CV_SHMEDIA_TR1 = 75, + CV_SHMEDIA_TR2 = 76, + CV_SHMEDIA_TR3 = 77, + CV_SHMEDIA_TR4 = 78, + CV_SHMEDIA_TR5 = 79, + CV_SHMEDIA_TR6 = 80, + CV_SHMEDIA_TR7 = 81, + CV_SHMEDIA_TR8 = 82, // future-proof + CV_SHMEDIA_TR9 = 83, // future-proof + CV_SHMEDIA_TR10 = 84, // future-proof + CV_SHMEDIA_TR11 = 85, // future-proof + CV_SHMEDIA_TR12 = 86, // future-proof + CV_SHMEDIA_TR13 = 87, // future-proof + CV_SHMEDIA_TR14 = 88, // future-proof + CV_SHMEDIA_TR15 = 89, // future-proof + + // Single - 32 bit fp registers + CV_SHMEDIA_FR0 = 128, + CV_SHMEDIA_FR1 = 129, + CV_SHMEDIA_FR2 = 130, + CV_SHMEDIA_FR3 = 131, + CV_SHMEDIA_FR4 = 132, + CV_SHMEDIA_FR5 = 133, + CV_SHMEDIA_FR6 = 134, + CV_SHMEDIA_FR7 = 135, + CV_SHMEDIA_FR8 = 136, + CV_SHMEDIA_FR9 = 137, + CV_SHMEDIA_FR10 = 138, + CV_SHMEDIA_FR11 = 139, + CV_SHMEDIA_FR12 = 140, + CV_SHMEDIA_FR13 = 141, + CV_SHMEDIA_FR14 = 142, + CV_SHMEDIA_FR15 = 143, + CV_SHMEDIA_FR16 = 144, + CV_SHMEDIA_FR17 = 145, + CV_SHMEDIA_FR18 = 146, + CV_SHMEDIA_FR19 = 147, + CV_SHMEDIA_FR20 = 148, + CV_SHMEDIA_FR21 = 149, + CV_SHMEDIA_FR22 = 150, + CV_SHMEDIA_FR23 = 151, + CV_SHMEDIA_FR24 = 152, + CV_SHMEDIA_FR25 = 153, + CV_SHMEDIA_FR26 = 154, + CV_SHMEDIA_FR27 = 155, + CV_SHMEDIA_FR28 = 156, + CV_SHMEDIA_FR29 = 157, + CV_SHMEDIA_FR30 = 158, + CV_SHMEDIA_FR31 = 159, + CV_SHMEDIA_FR32 = 160, + CV_SHMEDIA_FR33 = 161, + CV_SHMEDIA_FR34 = 162, + CV_SHMEDIA_FR35 = 163, + CV_SHMEDIA_FR36 = 164, + CV_SHMEDIA_FR37 = 165, + CV_SHMEDIA_FR38 = 166, + CV_SHMEDIA_FR39 = 167, + CV_SHMEDIA_FR40 = 168, + CV_SHMEDIA_FR41 = 169, + CV_SHMEDIA_FR42 = 170, + CV_SHMEDIA_FR43 = 171, + CV_SHMEDIA_FR44 = 172, + CV_SHMEDIA_FR45 = 173, + CV_SHMEDIA_FR46 = 174, + CV_SHMEDIA_FR47 = 175, + CV_SHMEDIA_FR48 = 176, + CV_SHMEDIA_FR49 = 177, + CV_SHMEDIA_FR50 = 178, + CV_SHMEDIA_FR51 = 179, + CV_SHMEDIA_FR52 = 180, + CV_SHMEDIA_FR53 = 181, + CV_SHMEDIA_FR54 = 182, + CV_SHMEDIA_FR55 = 183, + CV_SHMEDIA_FR56 = 184, + CV_SHMEDIA_FR57 = 185, + CV_SHMEDIA_FR58 = 186, + CV_SHMEDIA_FR59 = 187, + CV_SHMEDIA_FR60 = 188, + CV_SHMEDIA_FR61 = 189, + CV_SHMEDIA_FR62 = 190, + CV_SHMEDIA_FR63 = 191, + + // Double - 64 bit synonyms for 32bit fp register pairs + // subtract 128 to find first base single register + CV_SHMEDIA_DR0 = 256, + CV_SHMEDIA_DR2 = 258, + CV_SHMEDIA_DR4 = 260, + CV_SHMEDIA_DR6 = 262, + CV_SHMEDIA_DR8 = 264, + CV_SHMEDIA_DR10 = 266, + CV_SHMEDIA_DR12 = 268, + CV_SHMEDIA_DR14 = 270, + CV_SHMEDIA_DR16 = 272, + CV_SHMEDIA_DR18 = 274, + CV_SHMEDIA_DR20 = 276, + CV_SHMEDIA_DR22 = 278, + CV_SHMEDIA_DR24 = 280, + CV_SHMEDIA_DR26 = 282, + CV_SHMEDIA_DR28 = 284, + CV_SHMEDIA_DR30 = 286, + CV_SHMEDIA_DR32 = 288, + CV_SHMEDIA_DR34 = 290, + CV_SHMEDIA_DR36 = 292, + CV_SHMEDIA_DR38 = 294, + CV_SHMEDIA_DR40 = 296, + CV_SHMEDIA_DR42 = 298, + CV_SHMEDIA_DR44 = 300, + CV_SHMEDIA_DR46 = 302, + CV_SHMEDIA_DR48 = 304, + CV_SHMEDIA_DR50 = 306, + CV_SHMEDIA_DR52 = 308, + CV_SHMEDIA_DR54 = 310, + CV_SHMEDIA_DR56 = 312, + CV_SHMEDIA_DR58 = 314, + CV_SHMEDIA_DR60 = 316, + CV_SHMEDIA_DR62 = 318, + + // Vector - 128 bit synonyms for 32bit fp register quads + // subtract 384 to find first base single register + CV_SHMEDIA_FV0 = 512, + CV_SHMEDIA_FV4 = 516, + CV_SHMEDIA_FV8 = 520, + CV_SHMEDIA_FV12 = 524, + CV_SHMEDIA_FV16 = 528, + CV_SHMEDIA_FV20 = 532, + CV_SHMEDIA_FV24 = 536, + CV_SHMEDIA_FV28 = 540, + CV_SHMEDIA_FV32 = 544, + CV_SHMEDIA_FV36 = 548, + CV_SHMEDIA_FV40 = 552, + CV_SHMEDIA_FV44 = 556, + CV_SHMEDIA_FV48 = 560, + CV_SHMEDIA_FV52 = 564, + CV_SHMEDIA_FV56 = 568, + CV_SHMEDIA_FV60 = 572, + + // Matrix - 512 bit synonyms for 16 adjacent 32bit fp registers + // subtract 896 to find first base single register + CV_SHMEDIA_MTRX0 = 1024, + CV_SHMEDIA_MTRX16 = 1040, + CV_SHMEDIA_MTRX32 = 1056, + CV_SHMEDIA_MTRX48 = 1072, + + // Control - Implementation defined 64bit control registers + CV_SHMEDIA_CR0 = 2000, + CV_SHMEDIA_CR1 = 2001, + CV_SHMEDIA_CR2 = 2002, + CV_SHMEDIA_CR3 = 2003, + CV_SHMEDIA_CR4 = 2004, + CV_SHMEDIA_CR5 = 2005, + CV_SHMEDIA_CR6 = 2006, + CV_SHMEDIA_CR7 = 2007, + CV_SHMEDIA_CR8 = 2008, + CV_SHMEDIA_CR9 = 2009, + CV_SHMEDIA_CR10 = 2010, + CV_SHMEDIA_CR11 = 2011, + CV_SHMEDIA_CR12 = 2012, + CV_SHMEDIA_CR13 = 2013, + CV_SHMEDIA_CR14 = 2014, + CV_SHMEDIA_CR15 = 2015, + CV_SHMEDIA_CR16 = 2016, + CV_SHMEDIA_CR17 = 2017, + CV_SHMEDIA_CR18 = 2018, + CV_SHMEDIA_CR19 = 2019, + CV_SHMEDIA_CR20 = 2020, + CV_SHMEDIA_CR21 = 2021, + CV_SHMEDIA_CR22 = 2022, + CV_SHMEDIA_CR23 = 2023, + CV_SHMEDIA_CR24 = 2024, + CV_SHMEDIA_CR25 = 2025, + CV_SHMEDIA_CR26 = 2026, + CV_SHMEDIA_CR27 = 2027, + CV_SHMEDIA_CR28 = 2028, + CV_SHMEDIA_CR29 = 2029, + CV_SHMEDIA_CR30 = 2030, + CV_SHMEDIA_CR31 = 2031, + CV_SHMEDIA_CR32 = 2032, + CV_SHMEDIA_CR33 = 2033, + CV_SHMEDIA_CR34 = 2034, + CV_SHMEDIA_CR35 = 2035, + CV_SHMEDIA_CR36 = 2036, + CV_SHMEDIA_CR37 = 2037, + CV_SHMEDIA_CR38 = 2038, + CV_SHMEDIA_CR39 = 2039, + CV_SHMEDIA_CR40 = 2040, + CV_SHMEDIA_CR41 = 2041, + CV_SHMEDIA_CR42 = 2042, + CV_SHMEDIA_CR43 = 2043, + CV_SHMEDIA_CR44 = 2044, + CV_SHMEDIA_CR45 = 2045, + CV_SHMEDIA_CR46 = 2046, + CV_SHMEDIA_CR47 = 2047, + CV_SHMEDIA_CR48 = 2048, + CV_SHMEDIA_CR49 = 2049, + CV_SHMEDIA_CR50 = 2050, + CV_SHMEDIA_CR51 = 2051, + CV_SHMEDIA_CR52 = 2052, + CV_SHMEDIA_CR53 = 2053, + CV_SHMEDIA_CR54 = 2054, + CV_SHMEDIA_CR55 = 2055, + CV_SHMEDIA_CR56 = 2056, + CV_SHMEDIA_CR57 = 2057, + CV_SHMEDIA_CR58 = 2058, + CV_SHMEDIA_CR59 = 2059, + CV_SHMEDIA_CR60 = 2060, + CV_SHMEDIA_CR61 = 2061, + CV_SHMEDIA_CR62 = 2062, + CV_SHMEDIA_CR63 = 2063, + + CV_SHMEDIA_FPSCR = 2064, + + // Compact mode synonyms + CV_SHMEDIA_GBR = CV_SHMEDIA_R16, + CV_SHMEDIA_MACL = 90, // synonym for lower 32bits of media R17 + CV_SHMEDIA_MACH = 91, // synonym for upper 32bits of media R17 + CV_SHMEDIA_PR = CV_SHMEDIA_R18, + CV_SHMEDIA_T = 92, // synonym for lowest bit of media R19 + CV_SHMEDIA_FPUL = CV_SHMEDIA_FR32, + CV_SHMEDIA_PC = 93, + CV_SHMEDIA_SR = CV_SHMEDIA_CR0, + + // + // AMD64 registers + // + + CV_AMD64_AL = 1, + CV_AMD64_CL = 2, + CV_AMD64_DL = 3, + CV_AMD64_BL = 4, + CV_AMD64_AH = 5, + CV_AMD64_CH = 6, + CV_AMD64_DH = 7, + CV_AMD64_BH = 8, + CV_AMD64_AX = 9, + CV_AMD64_CX = 10, + CV_AMD64_DX = 11, + CV_AMD64_BX = 12, + CV_AMD64_SP = 13, + CV_AMD64_BP = 14, + CV_AMD64_SI = 15, + CV_AMD64_DI = 16, + CV_AMD64_EAX = 17, + CV_AMD64_ECX = 18, + CV_AMD64_EDX = 19, + CV_AMD64_EBX = 20, + CV_AMD64_ESP = 21, + CV_AMD64_EBP = 22, + CV_AMD64_ESI = 23, + CV_AMD64_EDI = 24, + CV_AMD64_ES = 25, + CV_AMD64_CS = 26, + CV_AMD64_SS = 27, + CV_AMD64_DS = 28, + CV_AMD64_FS = 29, + CV_AMD64_GS = 30, + CV_AMD64_FLAGS = 32, + CV_AMD64_RIP = 33, + CV_AMD64_EFLAGS = 34, + + // Control registers + CV_AMD64_CR0 = 80, + CV_AMD64_CR1 = 81, + CV_AMD64_CR2 = 82, + CV_AMD64_CR3 = 83, + CV_AMD64_CR4 = 84, + CV_AMD64_CR8 = 88, + + // Debug registers + CV_AMD64_DR0 = 90, + CV_AMD64_DR1 = 91, + CV_AMD64_DR2 = 92, + CV_AMD64_DR3 = 93, + CV_AMD64_DR4 = 94, + CV_AMD64_DR5 = 95, + CV_AMD64_DR6 = 96, + CV_AMD64_DR7 = 97, + CV_AMD64_DR8 = 98, + CV_AMD64_DR9 = 99, + CV_AMD64_DR10 = 100, + CV_AMD64_DR11 = 101, + CV_AMD64_DR12 = 102, + CV_AMD64_DR13 = 103, + CV_AMD64_DR14 = 104, + CV_AMD64_DR15 = 105, + + CV_AMD64_GDTR = 110, + CV_AMD64_GDTL = 111, + CV_AMD64_IDTR = 112, + CV_AMD64_IDTL = 113, + CV_AMD64_LDTR = 114, + CV_AMD64_TR = 115, + + CV_AMD64_ST0 = 128, + CV_AMD64_ST1 = 129, + CV_AMD64_ST2 = 130, + CV_AMD64_ST3 = 131, + CV_AMD64_ST4 = 132, + CV_AMD64_ST5 = 133, + CV_AMD64_ST6 = 134, + CV_AMD64_ST7 = 135, + CV_AMD64_CTRL = 136, + CV_AMD64_STAT = 137, + CV_AMD64_TAG = 138, + CV_AMD64_FPIP = 139, + CV_AMD64_FPCS = 140, + CV_AMD64_FPDO = 141, + CV_AMD64_FPDS = 142, + CV_AMD64_ISEM = 143, + CV_AMD64_FPEIP = 144, + CV_AMD64_FPEDO = 145, + + CV_AMD64_MM0 = 146, + CV_AMD64_MM1 = 147, + CV_AMD64_MM2 = 148, + CV_AMD64_MM3 = 149, + CV_AMD64_MM4 = 150, + CV_AMD64_MM5 = 151, + CV_AMD64_MM6 = 152, + CV_AMD64_MM7 = 153, + + CV_AMD64_XMM0 = 154, // KATMAI registers + CV_AMD64_XMM1 = 155, + CV_AMD64_XMM2 = 156, + CV_AMD64_XMM3 = 157, + CV_AMD64_XMM4 = 158, + CV_AMD64_XMM5 = 159, + CV_AMD64_XMM6 = 160, + CV_AMD64_XMM7 = 161, + + CV_AMD64_XMM0_0 = 162, // KATMAI sub-registers + CV_AMD64_XMM0_1 = 163, + CV_AMD64_XMM0_2 = 164, + CV_AMD64_XMM0_3 = 165, + CV_AMD64_XMM1_0 = 166, + CV_AMD64_XMM1_1 = 167, + CV_AMD64_XMM1_2 = 168, + CV_AMD64_XMM1_3 = 169, + CV_AMD64_XMM2_0 = 170, + CV_AMD64_XMM2_1 = 171, + CV_AMD64_XMM2_2 = 172, + CV_AMD64_XMM2_3 = 173, + CV_AMD64_XMM3_0 = 174, + CV_AMD64_XMM3_1 = 175, + CV_AMD64_XMM3_2 = 176, + CV_AMD64_XMM3_3 = 177, + CV_AMD64_XMM4_0 = 178, + CV_AMD64_XMM4_1 = 179, + CV_AMD64_XMM4_2 = 180, + CV_AMD64_XMM4_3 = 181, + CV_AMD64_XMM5_0 = 182, + CV_AMD64_XMM5_1 = 183, + CV_AMD64_XMM5_2 = 184, + CV_AMD64_XMM5_3 = 185, + CV_AMD64_XMM6_0 = 186, + CV_AMD64_XMM6_1 = 187, + CV_AMD64_XMM6_2 = 188, + CV_AMD64_XMM6_3 = 189, + CV_AMD64_XMM7_0 = 190, + CV_AMD64_XMM7_1 = 191, + CV_AMD64_XMM7_2 = 192, + CV_AMD64_XMM7_3 = 193, + + CV_AMD64_XMM0L = 194, + CV_AMD64_XMM1L = 195, + CV_AMD64_XMM2L = 196, + CV_AMD64_XMM3L = 197, + CV_AMD64_XMM4L = 198, + CV_AMD64_XMM5L = 199, + CV_AMD64_XMM6L = 200, + CV_AMD64_XMM7L = 201, + + CV_AMD64_XMM0H = 202, + CV_AMD64_XMM1H = 203, + CV_AMD64_XMM2H = 204, + CV_AMD64_XMM3H = 205, + CV_AMD64_XMM4H = 206, + CV_AMD64_XMM5H = 207, + CV_AMD64_XMM6H = 208, + CV_AMD64_XMM7H = 209, + + CV_AMD64_MXCSR = 211, // XMM status register + + CV_AMD64_EMM0L = 220, // XMM sub-registers (WNI integer) + CV_AMD64_EMM1L = 221, + CV_AMD64_EMM2L = 222, + CV_AMD64_EMM3L = 223, + CV_AMD64_EMM4L = 224, + CV_AMD64_EMM5L = 225, + CV_AMD64_EMM6L = 226, + CV_AMD64_EMM7L = 227, + + CV_AMD64_EMM0H = 228, + CV_AMD64_EMM1H = 229, + CV_AMD64_EMM2H = 230, + CV_AMD64_EMM3H = 231, + CV_AMD64_EMM4H = 232, + CV_AMD64_EMM5H = 233, + CV_AMD64_EMM6H = 234, + CV_AMD64_EMM7H = 235, + + // do not change the order of these regs, first one must be even too + CV_AMD64_MM00 = 236, + CV_AMD64_MM01 = 237, + CV_AMD64_MM10 = 238, + CV_AMD64_MM11 = 239, + CV_AMD64_MM20 = 240, + CV_AMD64_MM21 = 241, + CV_AMD64_MM30 = 242, + CV_AMD64_MM31 = 243, + CV_AMD64_MM40 = 244, + CV_AMD64_MM41 = 245, + CV_AMD64_MM50 = 246, + CV_AMD64_MM51 = 247, + CV_AMD64_MM60 = 248, + CV_AMD64_MM61 = 249, + CV_AMD64_MM70 = 250, + CV_AMD64_MM71 = 251, + + // Extended KATMAI registers + CV_AMD64_XMM8 = 252, // KATMAI registers + CV_AMD64_XMM9 = 253, + CV_AMD64_XMM10 = 254, + CV_AMD64_XMM11 = 255, + CV_AMD64_XMM12 = 256, + CV_AMD64_XMM13 = 257, + CV_AMD64_XMM14 = 258, + CV_AMD64_XMM15 = 259, + + CV_AMD64_XMM8_0 = 260, // KATMAI sub-registers + CV_AMD64_XMM8_1 = 261, + CV_AMD64_XMM8_2 = 262, + CV_AMD64_XMM8_3 = 263, + CV_AMD64_XMM9_0 = 264, + CV_AMD64_XMM9_1 = 265, + CV_AMD64_XMM9_2 = 266, + CV_AMD64_XMM9_3 = 267, + CV_AMD64_XMM10_0 = 268, + CV_AMD64_XMM10_1 = 269, + CV_AMD64_XMM10_2 = 270, + CV_AMD64_XMM10_3 = 271, + CV_AMD64_XMM11_0 = 272, + CV_AMD64_XMM11_1 = 273, + CV_AMD64_XMM11_2 = 274, + CV_AMD64_XMM11_3 = 275, + CV_AMD64_XMM12_0 = 276, + CV_AMD64_XMM12_1 = 277, + CV_AMD64_XMM12_2 = 278, + CV_AMD64_XMM12_3 = 279, + CV_AMD64_XMM13_0 = 280, + CV_AMD64_XMM13_1 = 281, + CV_AMD64_XMM13_2 = 282, + CV_AMD64_XMM13_3 = 283, + CV_AMD64_XMM14_0 = 284, + CV_AMD64_XMM14_1 = 285, + CV_AMD64_XMM14_2 = 286, + CV_AMD64_XMM14_3 = 287, + CV_AMD64_XMM15_0 = 288, + CV_AMD64_XMM15_1 = 289, + CV_AMD64_XMM15_2 = 290, + CV_AMD64_XMM15_3 = 291, + + CV_AMD64_XMM8L = 292, + CV_AMD64_XMM9L = 293, + CV_AMD64_XMM10L = 294, + CV_AMD64_XMM11L = 295, + CV_AMD64_XMM12L = 296, + CV_AMD64_XMM13L = 297, + CV_AMD64_XMM14L = 298, + CV_AMD64_XMM15L = 299, + + CV_AMD64_XMM8H = 300, + CV_AMD64_XMM9H = 301, + CV_AMD64_XMM10H = 302, + CV_AMD64_XMM11H = 303, + CV_AMD64_XMM12H = 304, + CV_AMD64_XMM13H = 305, + CV_AMD64_XMM14H = 306, + CV_AMD64_XMM15H = 307, + + CV_AMD64_EMM8L = 308, // XMM sub-registers (WNI integer) + CV_AMD64_EMM9L = 309, + CV_AMD64_EMM10L = 310, + CV_AMD64_EMM11L = 311, + CV_AMD64_EMM12L = 312, + CV_AMD64_EMM13L = 313, + CV_AMD64_EMM14L = 314, + CV_AMD64_EMM15L = 315, + + CV_AMD64_EMM8H = 316, + CV_AMD64_EMM9H = 317, + CV_AMD64_EMM10H = 318, + CV_AMD64_EMM11H = 319, + CV_AMD64_EMM12H = 320, + CV_AMD64_EMM13H = 321, + CV_AMD64_EMM14H = 322, + CV_AMD64_EMM15H = 323, + + // Low byte forms of some standard registers + CV_AMD64_SIL = 324, + CV_AMD64_DIL = 325, + CV_AMD64_BPL = 326, + CV_AMD64_SPL = 327, + + // 64-bit regular registers + CV_AMD64_RAX = 328, + CV_AMD64_RBX = 329, + CV_AMD64_RCX = 330, + CV_AMD64_RDX = 331, + CV_AMD64_RSI = 332, + CV_AMD64_RDI = 333, + CV_AMD64_RBP = 334, + CV_AMD64_RSP = 335, + + // 64-bit integer registers with 8-, 16-, and 32-bit forms (B, W, and D) + CV_AMD64_R8 = 336, + CV_AMD64_R9 = 337, + CV_AMD64_R10 = 338, + CV_AMD64_R11 = 339, + CV_AMD64_R12 = 340, + CV_AMD64_R13 = 341, + CV_AMD64_R14 = 342, + CV_AMD64_R15 = 343, + + CV_AMD64_R8B = 344, + CV_AMD64_R9B = 345, + CV_AMD64_R10B = 346, + CV_AMD64_R11B = 347, + CV_AMD64_R12B = 348, + CV_AMD64_R13B = 349, + CV_AMD64_R14B = 350, + CV_AMD64_R15B = 351, + + CV_AMD64_R8W = 352, + CV_AMD64_R9W = 353, + CV_AMD64_R10W = 354, + CV_AMD64_R11W = 355, + CV_AMD64_R12W = 356, + CV_AMD64_R13W = 357, + CV_AMD64_R14W = 358, + CV_AMD64_R15W = 359, + + CV_AMD64_R8D = 360, + CV_AMD64_R9D = 361, + CV_AMD64_R10D = 362, + CV_AMD64_R11D = 363, + CV_AMD64_R12D = 364, + CV_AMD64_R13D = 365, + CV_AMD64_R14D = 366, + CV_AMD64_R15D = 367, + + // AVX registers 256 bits + CV_AMD64_YMM0 = 368, + CV_AMD64_YMM1 = 369, + CV_AMD64_YMM2 = 370, + CV_AMD64_YMM3 = 371, + CV_AMD64_YMM4 = 372, + CV_AMD64_YMM5 = 373, + CV_AMD64_YMM6 = 374, + CV_AMD64_YMM7 = 375, + CV_AMD64_YMM8 = 376, + CV_AMD64_YMM9 = 377, + CV_AMD64_YMM10 = 378, + CV_AMD64_YMM11 = 379, + CV_AMD64_YMM12 = 380, + CV_AMD64_YMM13 = 381, + CV_AMD64_YMM14 = 382, + CV_AMD64_YMM15 = 383, + + // AVX registers upper 128 bits + CV_AMD64_YMM0H = 384, + CV_AMD64_YMM1H = 385, + CV_AMD64_YMM2H = 386, + CV_AMD64_YMM3H = 387, + CV_AMD64_YMM4H = 388, + CV_AMD64_YMM5H = 389, + CV_AMD64_YMM6H = 390, + CV_AMD64_YMM7H = 391, + CV_AMD64_YMM8H = 392, + CV_AMD64_YMM9H = 393, + CV_AMD64_YMM10H = 394, + CV_AMD64_YMM11H = 395, + CV_AMD64_YMM12H = 396, + CV_AMD64_YMM13H = 397, + CV_AMD64_YMM14H = 398, + CV_AMD64_YMM15H = 399, + + // Lower/upper 8 bytes of XMM registers. Unlike CV_AMD64_XMM, + // these + // values reprsesent the bit patterns of the registers as 64-bit integers, not + // the representation of these registers as a double. + CV_AMD64_XMM0IL = 400, + CV_AMD64_XMM1IL = 401, + CV_AMD64_XMM2IL = 402, + CV_AMD64_XMM3IL = 403, + CV_AMD64_XMM4IL = 404, + CV_AMD64_XMM5IL = 405, + CV_AMD64_XMM6IL = 406, + CV_AMD64_XMM7IL = 407, + CV_AMD64_XMM8IL = 408, + CV_AMD64_XMM9IL = 409, + CV_AMD64_XMM10IL = 410, + CV_AMD64_XMM11IL = 411, + CV_AMD64_XMM12IL = 412, + CV_AMD64_XMM13IL = 413, + CV_AMD64_XMM14IL = 414, + CV_AMD64_XMM15IL = 415, + + CV_AMD64_XMM0IH = 416, + CV_AMD64_XMM1IH = 417, + CV_AMD64_XMM2IH = 418, + CV_AMD64_XMM3IH = 419, + CV_AMD64_XMM4IH = 420, + CV_AMD64_XMM5IH = 421, + CV_AMD64_XMM6IH = 422, + CV_AMD64_XMM7IH = 423, + CV_AMD64_XMM8IH = 424, + CV_AMD64_XMM9IH = 425, + CV_AMD64_XMM10IH = 426, + CV_AMD64_XMM11IH = 427, + CV_AMD64_XMM12IH = 428, + CV_AMD64_XMM13IH = 429, + CV_AMD64_XMM14IH = 430, + CV_AMD64_XMM15IH = 431, + + CV_AMD64_YMM0I0 = 432, // AVX integer registers + CV_AMD64_YMM0I1 = 433, + CV_AMD64_YMM0I2 = 434, + CV_AMD64_YMM0I3 = 435, + CV_AMD64_YMM1I0 = 436, + CV_AMD64_YMM1I1 = 437, + CV_AMD64_YMM1I2 = 438, + CV_AMD64_YMM1I3 = 439, + CV_AMD64_YMM2I0 = 440, + CV_AMD64_YMM2I1 = 441, + CV_AMD64_YMM2I2 = 442, + CV_AMD64_YMM2I3 = 443, + CV_AMD64_YMM3I0 = 444, + CV_AMD64_YMM3I1 = 445, + CV_AMD64_YMM3I2 = 446, + CV_AMD64_YMM3I3 = 447, + CV_AMD64_YMM4I0 = 448, + CV_AMD64_YMM4I1 = 449, + CV_AMD64_YMM4I2 = 450, + CV_AMD64_YMM4I3 = 451, + CV_AMD64_YMM5I0 = 452, + CV_AMD64_YMM5I1 = 453, + CV_AMD64_YMM5I2 = 454, + CV_AMD64_YMM5I3 = 455, + CV_AMD64_YMM6I0 = 456, + CV_AMD64_YMM6I1 = 457, + CV_AMD64_YMM6I2 = 458, + CV_AMD64_YMM6I3 = 459, + CV_AMD64_YMM7I0 = 460, + CV_AMD64_YMM7I1 = 461, + CV_AMD64_YMM7I2 = 462, + CV_AMD64_YMM7I3 = 463, + CV_AMD64_YMM8I0 = 464, + CV_AMD64_YMM8I1 = 465, + CV_AMD64_YMM8I2 = 466, + CV_AMD64_YMM8I3 = 467, + CV_AMD64_YMM9I0 = 468, + CV_AMD64_YMM9I1 = 469, + CV_AMD64_YMM9I2 = 470, + CV_AMD64_YMM9I3 = 471, + CV_AMD64_YMM10I0 = 472, + CV_AMD64_YMM10I1 = 473, + CV_AMD64_YMM10I2 = 474, + CV_AMD64_YMM10I3 = 475, + CV_AMD64_YMM11I0 = 476, + CV_AMD64_YMM11I1 = 477, + CV_AMD64_YMM11I2 = 478, + CV_AMD64_YMM11I3 = 479, + CV_AMD64_YMM12I0 = 480, + CV_AMD64_YMM12I1 = 481, + CV_AMD64_YMM12I2 = 482, + CV_AMD64_YMM12I3 = 483, + CV_AMD64_YMM13I0 = 484, + CV_AMD64_YMM13I1 = 485, + CV_AMD64_YMM13I2 = 486, + CV_AMD64_YMM13I3 = 487, + CV_AMD64_YMM14I0 = 488, + CV_AMD64_YMM14I1 = 489, + CV_AMD64_YMM14I2 = 490, + CV_AMD64_YMM14I3 = 491, + CV_AMD64_YMM15I0 = 492, + CV_AMD64_YMM15I1 = 493, + CV_AMD64_YMM15I2 = 494, + CV_AMD64_YMM15I3 = 495, + + CV_AMD64_YMM0F0 = 496, // AVX floating-point single precise registers + CV_AMD64_YMM0F1 = 497, + CV_AMD64_YMM0F2 = 498, + CV_AMD64_YMM0F3 = 499, + CV_AMD64_YMM0F4 = 500, + CV_AMD64_YMM0F5 = 501, + CV_AMD64_YMM0F6 = 502, + CV_AMD64_YMM0F7 = 503, + CV_AMD64_YMM1F0 = 504, + CV_AMD64_YMM1F1 = 505, + CV_AMD64_YMM1F2 = 506, + CV_AMD64_YMM1F3 = 507, + CV_AMD64_YMM1F4 = 508, + CV_AMD64_YMM1F5 = 509, + CV_AMD64_YMM1F6 = 510, + CV_AMD64_YMM1F7 = 511, + CV_AMD64_YMM2F0 = 512, + CV_AMD64_YMM2F1 = 513, + CV_AMD64_YMM2F2 = 514, + CV_AMD64_YMM2F3 = 515, + CV_AMD64_YMM2F4 = 516, + CV_AMD64_YMM2F5 = 517, + CV_AMD64_YMM2F6 = 518, + CV_AMD64_YMM2F7 = 519, + CV_AMD64_YMM3F0 = 520, + CV_AMD64_YMM3F1 = 521, + CV_AMD64_YMM3F2 = 522, + CV_AMD64_YMM3F3 = 523, + CV_AMD64_YMM3F4 = 524, + CV_AMD64_YMM3F5 = 525, + CV_AMD64_YMM3F6 = 526, + CV_AMD64_YMM3F7 = 527, + CV_AMD64_YMM4F0 = 528, + CV_AMD64_YMM4F1 = 529, + CV_AMD64_YMM4F2 = 530, + CV_AMD64_YMM4F3 = 531, + CV_AMD64_YMM4F4 = 532, + CV_AMD64_YMM4F5 = 533, + CV_AMD64_YMM4F6 = 534, + CV_AMD64_YMM4F7 = 535, + CV_AMD64_YMM5F0 = 536, + CV_AMD64_YMM5F1 = 537, + CV_AMD64_YMM5F2 = 538, + CV_AMD64_YMM5F3 = 539, + CV_AMD64_YMM5F4 = 540, + CV_AMD64_YMM5F5 = 541, + CV_AMD64_YMM5F6 = 542, + CV_AMD64_YMM5F7 = 543, + CV_AMD64_YMM6F0 = 544, + CV_AMD64_YMM6F1 = 545, + CV_AMD64_YMM6F2 = 546, + CV_AMD64_YMM6F3 = 547, + CV_AMD64_YMM6F4 = 548, + CV_AMD64_YMM6F5 = 549, + CV_AMD64_YMM6F6 = 550, + CV_AMD64_YMM6F7 = 551, + CV_AMD64_YMM7F0 = 552, + CV_AMD64_YMM7F1 = 553, + CV_AMD64_YMM7F2 = 554, + CV_AMD64_YMM7F3 = 555, + CV_AMD64_YMM7F4 = 556, + CV_AMD64_YMM7F5 = 557, + CV_AMD64_YMM7F6 = 558, + CV_AMD64_YMM7F7 = 559, + CV_AMD64_YMM8F0 = 560, + CV_AMD64_YMM8F1 = 561, + CV_AMD64_YMM8F2 = 562, + CV_AMD64_YMM8F3 = 563, + CV_AMD64_YMM8F4 = 564, + CV_AMD64_YMM8F5 = 565, + CV_AMD64_YMM8F6 = 566, + CV_AMD64_YMM8F7 = 567, + CV_AMD64_YMM9F0 = 568, + CV_AMD64_YMM9F1 = 569, + CV_AMD64_YMM9F2 = 570, + CV_AMD64_YMM9F3 = 571, + CV_AMD64_YMM9F4 = 572, + CV_AMD64_YMM9F5 = 573, + CV_AMD64_YMM9F6 = 574, + CV_AMD64_YMM9F7 = 575, + CV_AMD64_YMM10F0 = 576, + CV_AMD64_YMM10F1 = 577, + CV_AMD64_YMM10F2 = 578, + CV_AMD64_YMM10F3 = 579, + CV_AMD64_YMM10F4 = 580, + CV_AMD64_YMM10F5 = 581, + CV_AMD64_YMM10F6 = 582, + CV_AMD64_YMM10F7 = 583, + CV_AMD64_YMM11F0 = 584, + CV_AMD64_YMM11F1 = 585, + CV_AMD64_YMM11F2 = 586, + CV_AMD64_YMM11F3 = 587, + CV_AMD64_YMM11F4 = 588, + CV_AMD64_YMM11F5 = 589, + CV_AMD64_YMM11F6 = 590, + CV_AMD64_YMM11F7 = 591, + CV_AMD64_YMM12F0 = 592, + CV_AMD64_YMM12F1 = 593, + CV_AMD64_YMM12F2 = 594, + CV_AMD64_YMM12F3 = 595, + CV_AMD64_YMM12F4 = 596, + CV_AMD64_YMM12F5 = 597, + CV_AMD64_YMM12F6 = 598, + CV_AMD64_YMM12F7 = 599, + CV_AMD64_YMM13F0 = 600, + CV_AMD64_YMM13F1 = 601, + CV_AMD64_YMM13F2 = 602, + CV_AMD64_YMM13F3 = 603, + CV_AMD64_YMM13F4 = 604, + CV_AMD64_YMM13F5 = 605, + CV_AMD64_YMM13F6 = 606, + CV_AMD64_YMM13F7 = 607, + CV_AMD64_YMM14F0 = 608, + CV_AMD64_YMM14F1 = 609, + CV_AMD64_YMM14F2 = 610, + CV_AMD64_YMM14F3 = 611, + CV_AMD64_YMM14F4 = 612, + CV_AMD64_YMM14F5 = 613, + CV_AMD64_YMM14F6 = 614, + CV_AMD64_YMM14F7 = 615, + CV_AMD64_YMM15F0 = 616, + CV_AMD64_YMM15F1 = 617, + CV_AMD64_YMM15F2 = 618, + CV_AMD64_YMM15F3 = 619, + CV_AMD64_YMM15F4 = 620, + CV_AMD64_YMM15F5 = 621, + CV_AMD64_YMM15F6 = 622, + CV_AMD64_YMM15F7 = 623, + + CV_AMD64_YMM0D0 = 624, // AVX floating-point double precise registers + CV_AMD64_YMM0D1 = 625, + CV_AMD64_YMM0D2 = 626, + CV_AMD64_YMM0D3 = 627, + CV_AMD64_YMM1D0 = 628, + CV_AMD64_YMM1D1 = 629, + CV_AMD64_YMM1D2 = 630, + CV_AMD64_YMM1D3 = 631, + CV_AMD64_YMM2D0 = 632, + CV_AMD64_YMM2D1 = 633, + CV_AMD64_YMM2D2 = 634, + CV_AMD64_YMM2D3 = 635, + CV_AMD64_YMM3D0 = 636, + CV_AMD64_YMM3D1 = 637, + CV_AMD64_YMM3D2 = 638, + CV_AMD64_YMM3D3 = 639, + CV_AMD64_YMM4D0 = 640, + CV_AMD64_YMM4D1 = 641, + CV_AMD64_YMM4D2 = 642, + CV_AMD64_YMM4D3 = 643, + CV_AMD64_YMM5D0 = 644, + CV_AMD64_YMM5D1 = 645, + CV_AMD64_YMM5D2 = 646, + CV_AMD64_YMM5D3 = 647, + CV_AMD64_YMM6D0 = 648, + CV_AMD64_YMM6D1 = 649, + CV_AMD64_YMM6D2 = 650, + CV_AMD64_YMM6D3 = 651, + CV_AMD64_YMM7D0 = 652, + CV_AMD64_YMM7D1 = 653, + CV_AMD64_YMM7D2 = 654, + CV_AMD64_YMM7D3 = 655, + CV_AMD64_YMM8D0 = 656, + CV_AMD64_YMM8D1 = 657, + CV_AMD64_YMM8D2 = 658, + CV_AMD64_YMM8D3 = 659, + CV_AMD64_YMM9D0 = 660, + CV_AMD64_YMM9D1 = 661, + CV_AMD64_YMM9D2 = 662, + CV_AMD64_YMM9D3 = 663, + CV_AMD64_YMM10D0 = 664, + CV_AMD64_YMM10D1 = 665, + CV_AMD64_YMM10D2 = 666, + CV_AMD64_YMM10D3 = 667, + CV_AMD64_YMM11D0 = 668, + CV_AMD64_YMM11D1 = 669, + CV_AMD64_YMM11D2 = 670, + CV_AMD64_YMM11D3 = 671, + CV_AMD64_YMM12D0 = 672, + CV_AMD64_YMM12D1 = 673, + CV_AMD64_YMM12D2 = 674, + CV_AMD64_YMM12D3 = 675, + CV_AMD64_YMM13D0 = 676, + CV_AMD64_YMM13D1 = 677, + CV_AMD64_YMM13D2 = 678, + CV_AMD64_YMM13D3 = 679, + CV_AMD64_YMM14D0 = 680, + CV_AMD64_YMM14D1 = 681, + CV_AMD64_YMM14D2 = 682, + CV_AMD64_YMM14D3 = 683, + CV_AMD64_YMM15D0 = 684, + CV_AMD64_YMM15D1 = 685, + CV_AMD64_YMM15D2 = 686, + CV_AMD64_YMM15D3 = 687 + + // Note: Next set of platform registers need to go into a new enum... + // this one is above 44K now. + +} CV_HREG_e; + +typedef enum CV_HLSLREG_e { + CV_HLSLREG_TEMP = 0, + CV_HLSLREG_INPUT = 1, + CV_HLSLREG_OUTPUT = 2, + CV_HLSLREG_INDEXABLE_TEMP = 3, + CV_HLSLREG_IMMEDIATE32 = 4, + CV_HLSLREG_IMMEDIATE64 = 5, + CV_HLSLREG_SAMPLER = 6, + CV_HLSLREG_RESOURCE = 7, + CV_HLSLREG_CONSTANT_BUFFER = 8, + CV_HLSLREG_IMMEDIATE_CONSTANT_BUFFER = 9, + CV_HLSLREG_LABEL = 10, + CV_HLSLREG_INPUT_PRIMITIVEID = 11, + CV_HLSLREG_OUTPUT_DEPTH = 12, + CV_HLSLREG_NULL = 13, + CV_HLSLREG_RASTERIZER = 14, + CV_HLSLREG_OUTPUT_COVERAGE_MASK = 15, + CV_HLSLREG_STREAM = 16, + CV_HLSLREG_FUNCTION_BODY = 17, + CV_HLSLREG_FUNCTION_TABLE = 18, + CV_HLSLREG_INTERFACE = 19, + CV_HLSLREG_FUNCTION_INPUT = 20, + CV_HLSLREG_FUNCTION_OUTPUT = 21, + CV_HLSLREG_OUTPUT_CONTROL_POINT_ID = 22, + CV_HLSLREG_INPUT_FORK_INSTANCE_ID = 23, + CV_HLSLREG_INPUT_JOIN_INSTANCE_ID = 24, + CV_HLSLREG_INPUT_CONTROL_POINT = 25, + CV_HLSLREG_OUTPUT_CONTROL_POINT = 26, + CV_HLSLREG_INPUT_PATCH_CONSTANT = 27, + CV_HLSLREG_INPUT_DOMAIN_POINT = 28, + CV_HLSLREG_THIS_POINTER = 29, + CV_HLSLREG_UNORDERED_ACCESS_VIEW = 30, + CV_HLSLREG_THREAD_GROUP_SHARED_MEMORY = 31, + CV_HLSLREG_INPUT_THREAD_ID = 32, + CV_HLSLREG_INPUT_THREAD_GROUP_ID = 33, + CV_HLSLREG_INPUT_THREAD_ID_IN_GROUP = 34, + CV_HLSLREG_INPUT_COVERAGE_MASK = 35, + CV_HLSLREG_INPUT_THREAD_ID_IN_GROUP_FLATTENED = 36, + CV_HLSLREG_INPUT_GS_INSTANCE_ID = 37, + CV_HLSLREG_OUTPUT_DEPTH_GREATER_EQUAL = 38, + CV_HLSLREG_OUTPUT_DEPTH_LESS_EQUAL = 39, + CV_HLSLREG_CYCLE_COUNTER = 40, +} CV_HLSLREG_e; + +enum StackFrameTypeEnum { + FrameTypeFPO, // Frame pointer omitted, FPO info available + FrameTypeTrap, // Kernel Trap frame + FrameTypeTSS, // Kernel Trap frame + FrameTypeStandard, // Standard EBP stackframe + FrameTypeFrameData, // Frame pointer omitted, FrameData info available + + FrameTypeUnknown = -1, // Frame which does not have any debug info +}; + +enum MemoryTypeEnum { + MemTypeCode, // Read only code memory + MemTypeData, // Read only data/stack memory + MemTypeStack, // Read only stack memory + MemTypeCodeOnHeap, // Read only memory for code generated on heap by runtime + + MemTypeAny = -1, +}; + +typedef enum CV_HLSLMemorySpace_e { + // HLSL specific memory spaces + + CV_HLSL_MEMSPACE_DATA = 0x00, + CV_HLSL_MEMSPACE_SAMPLER = 0x01, + CV_HLSL_MEMSPACE_RESOURCE = 0x02, + CV_HLSL_MEMSPACE_RWRESOURCE = 0x03, + + CV_HLSL_MEMSPACE_MAX = 0x0F, +} CV_HLSLMemorySpace_e; + +#endif diff --git a/external/corert/src/Native/ObjWriter/jitDebugInfo.h b/external/corert/src/Native/ObjWriter/jitDebugInfo.h new file mode 100644 index 0000000000..103dff8ad1 --- /dev/null +++ b/external/corert/src/Native/ObjWriter/jitDebugInfo.h @@ -0,0 +1,43 @@ +#ifndef JIT_DEBUG_INFO_H +#define JIT_DEBUG_INFO_H + +typedef unsigned int DWORD; +#define _TARGET_AMD64_ 1 + +#include "cordebuginfo.h" +#include "cvconst.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" + +struct DebugLocInfo { + int NativeOffset; + int FileId; + int LineNumber; + int ColNumber; +}; + +struct DebugVarInfo { + std::string Name; + int TypeIndex; + bool IsParam; + std::vector Ranges; + + DebugVarInfo() {} + DebugVarInfo(char *ArgName, int ArgTypeIndex, bool ArgIsParam) + : Name(ArgName), TypeIndex(ArgTypeIndex), IsParam(ArgIsParam) {} +}; + +typedef unsigned short CVRegMapping; + +#define CVREGDAT(p2, cv) cv + +const CVRegMapping cvRegMapAmd64[] = { + CVREGDAT(REGNUM_RAX, CV_AMD64_RAX), CVREGDAT(REGNUM_RCX, CV_AMD64_RCX), + CVREGDAT(REGNUM_RDX, CV_AMD64_RDX), CVREGDAT(REGNUM_RBX, CV_AMD64_RBX), + CVREGDAT(REGNUM_RSP, CV_AMD64_RSP), CVREGDAT(REGNUM_RBP, CV_AMD64_RBP), + CVREGDAT(REGNUM_RSI, CV_AMD64_RSI), CVREGDAT(REGNUM_RDI, CV_AMD64_RDI), + CVREGDAT(REGNUM_R8, CV_AMD64_R8), CVREGDAT(REGNUM_R9, CV_AMD64_R9), + CVREGDAT(REGNUM_R10, CV_AMD64_R10), CVREGDAT(REGNUM_R11, CV_AMD64_R11), + CVREGDAT(REGNUM_R12, CV_AMD64_R12), CVREGDAT(REGNUM_R13, CV_AMD64_R13), + CVREGDAT(REGNUM_R14, CV_AMD64_R14), CVREGDAT(REGNUM_R15, CV_AMD64_R15)}; + +#endif // JIT_DEBUG_INFO_H diff --git a/external/corert/src/Native/ObjWriter/llvm.patch b/external/corert/src/Native/ObjWriter/llvm.patch new file mode 100644 index 0000000000..2dde952d54 --- /dev/null +++ b/external/corert/src/Native/ObjWriter/llvm.patch @@ -0,0 +1,123 @@ +diff --git a/include/llvm/MC/MCObjectStreamer.h b/include/llvm/MC/MCObjectStreamer.h +index 7c1189e46ab..d1d77c97311 100644 +--- a/include/llvm/MC/MCObjectStreamer.h ++++ b/include/llvm/MC/MCObjectStreamer.h +@@ -101,6 +101,11 @@ public: + void EmitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI, + bool = false) override; + ++ /// \brief EmitValueImpl with additional param, that allows to emit PCRelative ++ /// MCFixup. ++ void EmitValueImpl(const MCExpr *Value, unsigned Size, SMLoc Loc, ++ bool isPCRelative); ++ + /// \brief Emit an instruction to a special fragment, because this instruction + /// can change its size during relaxation. + virtual void EmitInstToFragment(const MCInst &Inst, const MCSubtargetInfo &); +diff --git a/lib/MC/MCObjectStreamer.cpp b/lib/MC/MCObjectStreamer.cpp +index 174397e2739..ef7161fb56c 100644 +--- a/lib/MC/MCObjectStreamer.cpp ++++ b/lib/MC/MCObjectStreamer.cpp +@@ -122,7 +122,7 @@ void MCObjectStreamer::EmitCFISections(bool EH, bool Debug) { + } + + void MCObjectStreamer::EmitValueImpl(const MCExpr *Value, unsigned Size, +- SMLoc Loc) { ++ SMLoc Loc, bool isPCRelative) { + MCStreamer::EmitValueImpl(Value, Size, Loc); + MCDataFragment *DF = getOrCreateDataFragment(); + flushPendingLabels(DF, DF->getContents().size()); +@@ -143,10 +143,16 @@ void MCObjectStreamer::EmitValueImpl(const MCExpr *Value, unsigned Size, + } + DF->getFixups().push_back( + MCFixup::create(DF->getContents().size(), Value, +- MCFixup::getKindForSize(Size, false), Loc)); ++ MCFixup::getKindForSize(Size, isPCRelative), Loc)); + DF->getContents().resize(DF->getContents().size() + Size, 0); + } + ++ ++void MCObjectStreamer::EmitValueImpl(const MCExpr *Value, unsigned Size, ++ SMLoc Loc) { ++ EmitValueImpl(Value, Size, Loc, false); ++} ++ + void MCObjectStreamer::EmitCFIStartProcImpl(MCDwarfFrameInfo &Frame) { + // We need to create a local symbol to avoid relocations. + Frame.Begin = getContext().createTempSymbol(); +diff --git a/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp b/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp +index a77df7a2598..e1aa7526f9b 100644 +--- a/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp ++++ b/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp +@@ -48,6 +48,14 @@ public: + }; + } // end anonymous namespace + ++Optional ARMAsmBackend::getFixupKind(StringRef Name) const { ++ return StringSwitch>(Name) ++ .Case("R_ARM_THM_MOVW_ABS_NC", (MCFixupKind)ARM::fixup_t2_movw_lo16) ++ .Case("R_ARM_THM_MOVT_ABS", (MCFixupKind)ARM::fixup_t2_movt_hi16) ++ .Case("R_ARM_THM_JUMP24", (MCFixupKind)ARM::fixup_arm_thumb_blx) ++ .Default(MCAsmBackend::getFixupKind(Name)); ++} ++ + const MCFixupKindInfo &ARMAsmBackend::getFixupKindInfo(MCFixupKind Kind) const { + const static MCFixupKindInfo InfosLE[ARM::NumTargetFixupKinds] = { + // This table *must* be in the order that the fixup_* kinds are defined in +@@ -386,6 +394,8 @@ unsigned ARMAsmBackend::adjustFixupValue(const MCAssembler &Asm, + case FK_Data_2: + case FK_Data_4: + return Value; ++ case FK_PCRel_4: ++ return Value; + case FK_SecRel_2: + return Value; + case FK_SecRel_4: +@@ -825,6 +835,9 @@ static unsigned getFixupKindNumBytes(unsigned Kind) { + case ARM::fixup_t2_so_imm: + return 4; + ++ case FK_PCRel_4: ++ return 4; ++ + case FK_SecRel_2: + return 2; + case FK_SecRel_4: +diff --git a/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.h b/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.h +index 02374966daf..01676a01683 100644 +--- a/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.h ++++ b/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.h +@@ -36,6 +36,7 @@ public: + + bool hasNOP() const { return STI->getFeatureBits()[ARM::HasV6T2Ops]; } + ++ Optional getFixupKind(StringRef Name) const override; + const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override; + + bool shouldForceRelocation(const MCAssembler &Asm, const MCFixup &Fixup, +diff --git a/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp b/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp +index 59f31be69d5..9b95598f99f 100644 +--- a/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp ++++ b/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp +@@ -103,6 +103,9 @@ unsigned ARMELFObjectWriter::GetRelocTypeInner(const MCValue &Target, + break; + } + break; ++ case FK_PCRel_4: ++ Type = ELF::R_ARM_REL32; ++ break; + case ARM::fixup_arm_blx: + case ARM::fixup_arm_uncondbl: + switch (Modifier) { +diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt +index b654b8c5cb8..58d25159af8 100644 +--- a/tools/CMakeLists.txt ++++ b/tools/CMakeLists.txt +@@ -46,6 +46,7 @@ add_llvm_external_project(clang) + add_llvm_external_project(llgo) + add_llvm_external_project(lld) + add_llvm_external_project(lldb) ++add_llvm_external_project(ObjWriter) + + # Automatically add remaining sub-directories containing a 'CMakeLists.txt' + # file as external projects. diff --git a/external/corert/src/Native/ObjWriter/llvmCap/CMakeLists.txt b/external/corert/src/Native/ObjWriter/llvmCap/CMakeLists.txt new file mode 100644 index 0000000000..fd2e66ea16 --- /dev/null +++ b/external/corert/src/Native/ObjWriter/llvmCap/CMakeLists.txt @@ -0,0 +1,116 @@ +cmake_minimum_required(VERSION 3.6) +project(ObjWriter) +include(ExternalProject) + +set(OBJWRITER_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../) +set(OBJWRITER_LLVM_POINT tools/ObjWriter) + +if(NOT OBJWRITER_TARGET_ARCH) + if(CLR_CMAKE_TARGET_ARCH MATCHES "^(x64|x86)$") + if(NOT CROSS_BUILD) + set(OBJWRITER_TARGET_ARCH "X86") + else() + # If we really want a cross version for x86/x64 -> arm32/arm64 + # I think in the opposite direction it's not necessary for anyone ;-) + set(OBJWRITER_TARGET_ARCH "X86::ARM") + set(USE_ARM_TARGET_TRIPLE 1) + endif() + elseif(CLR_CMAKE_TARGET_ARCH MATCHES "^(arm|armel)$") + set(OBJWRITER_TARGET_ARCH "ARM") + set(USE_ARM_TARGET_TRIPLE 1) + elseif(CLR_CMAKE_TARGET_ARCH STREQUAL "arm64") + set(OBJWRITER_TARGET_ARCH "AArch64") + else() + clr_unknown_arch() + endif() +endif() + +get_target_property(CORERT_NATIVE_COMPILE_OPTIONS Runtime COMPILE_OPTIONS) + +# For armel RootFS compatibility +if(CLR_CMAKE_TARGET_ARCH STREQUAL "armel") + list(APPEND CORERT_NATIVE_COMPILE_OPTIONS "-Wno-gnu-include-next") +endif() + +if(USE_ARM_TARGET_TRIPLE) + list(APPEND LLVM_CMAKE_EXTRA_ARGS "-DLLVM_DEFAULT_TARGET_TRIPLE=thumbv7-linux-gnueabi") +endif() + +list(REMOVE_DUPLICATES CORERT_NATIVE_COMPILE_OPTIONS) + +# Make sure to remove debug flags from general build flags for LLVM +set(LLVM_COMPILE_OPTIONS "${CORERT_NATIVE_COMPILE_OPTIONS}") +list(REMOVE_ITEM LLVM_COMPILE_OPTIONS "-g") +list(REMOVE_ITEM LLVM_COMPILE_OPTIONS "-O0") + +string(REPLACE ";" "\ " CORERT_NATIVE_COMPILE_OPTIONS "${CORERT_NATIVE_COMPILE_OPTIONS}") +string(REPLACE ";" "\ " LLVM_COMPILE_OPTIONS "${LLVM_COMPILE_OPTIONS}") + +# If host and target are the same, we could use llvm-tblgen from LLVM itself. +# Otherwise we use host llvm-tblgen. It's universal way for cross-building. +if(NOT ${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL ${CMAKE_SYSTEM_PROCESSOR}) + execute_process ( + COMMAND bash -c "echo -n `which llvm-tblgen`" + OUTPUT_VARIABLE LLVM_TBLGEN_TOOL + ) + if(NOT LLVM_TBLGEN_TOOL) + message(FATAL_ERROR "Can't find llvm-tblgen. You need to make sure that you have installed LLVM") + endif() + list(APPEND LLVM_CMAKE_EXTRA_ARGS "-DLLVM_TABLEGEN=${LLVM_TBLGEN_TOOL}") +endif() + +ExternalProject_Add(LLVM + GIT_REPOSITORY https://github.com/llvm-mirror/llvm + GIT_TAG release_50 + GIT_SHALLOW 1 + GIT_PROGRESS 1 + PATCH_COMMAND "" + UPDATE_COMMAND "" + INSTALL_COMMAND "" + USES_TERMINAL_DOWNLOAD 1 + USES_TERMINAL_CONFIGURE 1 + USES_TERMINAL_BUILD 1 + USES_TERMINAL_INSTALL 1 + UPDATE_DISCONNECTED 1 + DEPENDS Runtime PortableRuntime + LIST_SEPARATOR :: + CMAKE_ARGS -DHAVE_POSIX_SPAWN=0 + -DLLVM_BUILD_DOCS=0 + -DLLVM_BUILD_TESTS=0 + -DLLVM_ENABLE_DOXYGEN=0 + -DLLVM_ENABLE_PEDANTIC=0 + -DLLVM_ENABLE_PIC=1 + -DLLVM_ENABLE_WERROR=0 + -DLLVM_INCLUDE_DOCS=0 + -DLLVM_INCLUDE_EXAMPLES=0 + -DLLVM_INCLUDE_TESTS=0 + -DLLVM_OPTIMIZED_TABLEGEN=1 + -DCMAKE_BUILD_TYPE=Release + -DLLVM_TARGET_ARCH=${OBJWRITER_TARGET_ARCH} + -DLLVM_TARGETS_TO_BUILD=${OBJWRITER_TARGET_ARCH} + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_C_FLAGS=${LLVM_COMPILE_OPTIONS} + -DCMAKE_CXX_FLAGS=${LLVM_COMPILE_OPTIONS} + -DCMAKE_EXE_LINKER_FLAGS=${CMAKE_EXE_LINKER_FLAGS} + -DCMAKE_MODULE_LINKER_FLAGS=${CMAKE_MODULE_LINKER_FLAGS} + -DCMAKE_SHARED_LINKER_FLAGS=${CMAKE_SHARED_LINKER_FLAGS} + -DOBJWRITER_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DOBJWRITER_C_FLAGS=${CORERT_NATIVE_COMPILE_OPTIONS} + -DOBJWRITER_CXX_FLAGS=${CORERT_NATIVE_COMPILE_OPTIONS} + ${LLVM_CMAKE_EXTRA_ARGS} + ) +ExternalProject_Get_Property(LLVM source_dir) +set(LLVM_SOURCE_DIR ${source_dir}) +ExternalProject_Add_Step(LLVM PatchingLLVM + COMMAND git apply --3way ${OBJWRITER_ROOT}/llvm.patch COMMENT "Applying LLVM patch with ObjWriter fixes" + WORKING_DIRECTORY ${LLVM_SOURCE_DIR} + DEPENDEES patch + USES_TERMINAL 1 + ) +ExternalProject_Add_Step(LLVM ObjWriterInjection + COMMAND ${CMAKE_COMMAND} -E create_symlink ${OBJWRITER_ROOT} ${OBJWRITER_LLVM_POINT} COMMENT "mklink ${OBJWRITER_ROOT} -> ${OBJWRITER_LLVM_POINT}" + WORKING_DIRECTORY ${LLVM_SOURCE_DIR} + DEPENDEES patch + USES_TERMINAL 1 + ) diff --git a/external/corert/src/Native/ObjWriter/objwriter.cpp b/external/corert/src/Native/ObjWriter/objwriter.cpp new file mode 100644 index 0000000000..7f81e070c3 --- /dev/null +++ b/external/corert/src/Native/ObjWriter/objwriter.cpp @@ -0,0 +1,839 @@ +//===---- objwriter.cpp --------------------------------*- C++ -*-===// +// +// object writer +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. +// See LICENSE file in the project root for full license information. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Implementation of object writer API for JIT/AOT +/// +//===----------------------------------------------------------------------===// + +#include "objwriter.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/Line.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDwarf.h" +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCParser/AsmLexer.h" +#include "llvm/MC/MCParser/MCTargetAsmParser.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSectionCOFF.h" +#include "llvm/MC/MCSectionELF.h" +#include "llvm/MC/MCSectionMachO.h" +#include "llvm/MC/MCObjectStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compression.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/Win64EH.h" +#include "llvm/Target/TargetMachine.h" + +using namespace llvm; +using namespace llvm::codeview; + +bool error(const Twine &Error) { + errs() << Twine("error: ") + Error + "\n"; + return false; +} + +void ObjectWriter::InitTripleName() { + TripleName = sys::getDefaultTargetTriple(); +} + +Triple ObjectWriter::GetTriple() { + Triple TheTriple(TripleName); + + if (TheTriple.getOS() == Triple::OSType::Darwin) { + TheTriple = Triple( + TheTriple.getArchName(), TheTriple.getVendorName(), "darwin", + TheTriple + .getEnvironmentName()); // it is workaround for llvm bug + // https://bugs.llvm.org//show_bug.cgi?id=24927. + } + return TheTriple; +} + +bool ObjectWriter::Init(llvm::StringRef ObjectFilePath) { + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + // Initialize targets + InitializeNativeTarget(); + InitializeNativeTargetAsmPrinter(); + + TargetMOptions = InitMCTargetOptionsFromFlags(); + + InitTripleName(); + Triple TheTriple = GetTriple(); + + // Get the target specific parser. + std::string TargetError; + const Target *TheTarget = + TargetRegistry::lookupTarget(TripleName, TargetError); + if (!TheTarget) { + return error("Unable to create target for " + ObjectFilePath + ": " + + TargetError); + } + + std::error_code EC; + OS.reset(new raw_fd_ostream(ObjectFilePath, EC, sys::fs::F_None)); + if (EC) + return error("Unable to create file for " + ObjectFilePath + ": " + + EC.message()); + + RegisterInfo.reset(TheTarget->createMCRegInfo(TripleName)); + if (!RegisterInfo) + return error("Unable to create target register info!"); + + AsmInfo.reset(TheTarget->createMCAsmInfo(*RegisterInfo, TripleName)); + if (!AsmInfo) + return error("Unable to create target asm info!"); + + ObjFileInfo.reset(new MCObjectFileInfo); + OutContext.reset( + new MCContext(AsmInfo.get(), RegisterInfo.get(), ObjFileInfo.get())); + ObjFileInfo->InitMCObjectFileInfo(TheTriple, false, CodeModel::Default, + *OutContext); + + InstrInfo.reset(TheTarget->createMCInstrInfo()); + if (!InstrInfo) + return error("no instr info info for target " + TripleName); + + std::string FeaturesStr; + std::string MCPU; + SubtargetInfo.reset( + TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr)); + if (!SubtargetInfo) + return error("no subtarget info for target " + TripleName); + + CodeEmitter = + TheTarget->createMCCodeEmitter(*InstrInfo, *RegisterInfo, *OutContext); + if (!CodeEmitter) + return error("no code emitter for target " + TripleName); + + AsmBackend = TheTarget->createMCAsmBackend(*RegisterInfo, TripleName, MCPU, + TargetMOptions); + if (!AsmBackend) + return error("no asm backend for target " + TripleName); + + Streamer = (MCObjectStreamer *)TheTarget->createMCObjectStreamer( + TheTriple, *OutContext, *AsmBackend, *OS, CodeEmitter, *SubtargetInfo, + RelaxAll, + /*IncrementalLinkerCompatible*/ true, + /*DWARFMustBeAtTheEnd*/ false); + if (!Streamer) + return error("no object streamer for target " + TripleName); + Assembler = &Streamer->getAssembler(); + + TMachine.reset(TheTarget->createTargetMachine(TripleName, MCPU, FeaturesStr, + TargetOptions(), None)); + if (!TMachine) + return error("no target machine for target " + TripleName); + + AssemblerPrinter.reset(TheTarget->createAsmPrinter( + *TMachine, std::unique_ptr(Streamer))); + if (!AssemblerPrinter) + return error("no asm printer for target " + TripleName); + + FrameOpened = false; + FuncId = 1; + + SetCodeSectionAttribute("text", CustomSectionAttributes_Executable, nullptr); + + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF) { + TypeBuilder.SetStreamer(Streamer); + unsigned TargetPointerSize = AssemblerPrinter->getPointerSize(); + TypeBuilder.SetTargetPointerSize(TargetPointerSize); + } + + return true; +} + +void ObjectWriter::Finish() { Streamer->Finish(); } + +void ObjectWriter::SwitchSection(const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName) { + MCSection *Section = GetSection(SectionName, attributes, ComdatName); + Streamer->SwitchSection(Section); + if (Sections.count(Section) == 0) { + Sections.insert(Section); + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsMachO) { + assert(!Section->getBeginSymbol()); + // Output a DWARF linker-local symbol. + // This symbol is used as a base for other symbols in a section. + MCSymbol *SectionStartSym = OutContext->createTempSymbol(); + Streamer->EmitLabel(SectionStartSym); + Section->setBeginSymbol(SectionStartSym); + } + } +} + +MCSection *ObjectWriter::GetSection(const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName) { + MCSection *Section = nullptr; + + if (strcmp(SectionName, "text") == 0) { + Section = ObjFileInfo->getTextSection(); + } else if (strcmp(SectionName, "data") == 0) { + Section = ObjFileInfo->getDataSection(); + } else if (strcmp(SectionName, "rdata") == 0) { + Section = ObjFileInfo->getReadOnlySection(); + } else if (strcmp(SectionName, "xdata") == 0) { + Section = ObjFileInfo->getXDataSection(); + } else { + Section = GetSpecificSection(SectionName, attributes, ComdatName); + } + assert(Section); + return Section; +} + +MCSection *ObjectWriter::GetSpecificSection(const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName) { + Triple TheTriple(TripleName); + MCSection *Section = nullptr; + SectionKind Kind = (attributes & CustomSectionAttributes_Executable) + ? SectionKind::getText() + : (attributes & CustomSectionAttributes_Writeable) + ? SectionKind::getData() + : SectionKind::getReadOnly(); + switch (TheTriple.getObjectFormat()) { + case Triple::MachO: { + unsigned typeAndAttributes = 0; + if (attributes & CustomSectionAttributes_MachO_Init_Func_Pointers) { + typeAndAttributes |= MachO::SectionType::S_MOD_INIT_FUNC_POINTERS; + } + Section = OutContext->getMachOSection( + (attributes & CustomSectionAttributes_Executable) ? "__TEXT" : "__DATA", + SectionName, typeAndAttributes, Kind); + break; + } + case Triple::COFF: { + unsigned Characteristics = COFF::IMAGE_SCN_MEM_READ; + + if (attributes & CustomSectionAttributes_Executable) { + Characteristics |= COFF::IMAGE_SCN_CNT_CODE | COFF::IMAGE_SCN_MEM_EXECUTE; + } else if (attributes & CustomSectionAttributes_Writeable) { + Characteristics |= + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_WRITE; + } else { + Characteristics |= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; + } + + if (ComdatName != nullptr) { + Section = OutContext->getCOFFSection( + SectionName, Characteristics | COFF::IMAGE_SCN_LNK_COMDAT, Kind, + ComdatName, COFF::COMDATType::IMAGE_COMDAT_SELECT_ANY); + } else { + Section = OutContext->getCOFFSection(SectionName, Characteristics, Kind); + } + break; + } + case Triple::ELF: { + unsigned Flags = ELF::SHF_ALLOC; + if (ComdatName != nullptr) { + MCSymbolELF *GroupSym = + cast(OutContext->getOrCreateSymbol(ComdatName)); + OutContext->createELFGroupSection(GroupSym); + Flags |= ELF::SHF_GROUP; + } + if (attributes & CustomSectionAttributes_Executable) { + Flags |= ELF::SHF_EXECINSTR; + } else if (attributes & CustomSectionAttributes_Writeable) { + Flags |= ELF::SHF_WRITE; + } + Section = + OutContext->getELFSection(SectionName, ELF::SHT_PROGBITS, Flags, 0, + ComdatName != nullptr ? ComdatName : ""); + break; + } + default: + error("Unknown output format for target " + TripleName); + break; + } + return Section; +} + +void ObjectWriter::SetCodeSectionAttribute(const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName) { + MCSection *Section = GetSection(SectionName, attributes, ComdatName); + + assert(!Section->hasInstructions()); + Section->setHasInstructions(true); + if (ObjFileInfo->getObjectFileType() != ObjFileInfo->IsCOFF) { + OutContext->addGenDwarfSection(Section); + } +} + +void ObjectWriter::EmitAlignment(int ByteAlignment) { + Streamer->EmitValueToAlignment(ByteAlignment, 0x90 /* Nop */); +} + +void ObjectWriter::EmitBlob(int BlobSize, const char *Blob) { + Streamer->EmitBytes(StringRef(Blob, BlobSize)); +} + +void ObjectWriter::EmitIntValue(uint64_t Value, unsigned Size) { + Streamer->EmitIntValue(Value, Size); +} + +void ObjectWriter::EmitSymbolDef(const char *SymbolName) { + MCSymbol *Sym = OutContext->getOrCreateSymbol(Twine(SymbolName)); + Streamer->EmitSymbolAttribute(Sym, MCSA_Global); + + // A Thumb2 function symbol should be marked with an appropriate ELF + // attribute to make later computation of a relocation address value correct + if (GetTriple().getArch() == Triple::thumb && + GetTriple().getObjectFormat() == Triple::ELF && + Streamer->getCurrentSectionOnly()->getKind().isText()) { + Streamer->EmitSymbolAttribute(Sym, MCSA_ELF_TypeFunction); + } + + Streamer->EmitLabel(Sym); +} + +const MCSymbolRefExpr * +ObjectWriter::GetSymbolRefExpr(const char *SymbolName, + MCSymbolRefExpr::VariantKind Kind) { + // Create symbol reference + MCSymbol *T = OutContext->getOrCreateSymbol(SymbolName); + Assembler->registerSymbol(*T); + return MCSymbolRefExpr::create(T, Kind, *OutContext); +} + +unsigned ObjectWriter::GetDFSize() { + return Streamer->getOrCreateDataFragment()->getContents().size(); +} + +bool ObjectWriter::EmitRelocDirective(const int Offset, StringRef Name, const MCExpr *Expr) { + const MCExpr *OffsetExpr = MCConstantExpr::create(Offset, *OutContext); + return Streamer->EmitRelocDirective(*OffsetExpr, Name, Expr, SMLoc()); +} + +const MCExpr *ObjectWriter::GenTargetExpr(const char *SymbolName, + MCSymbolRefExpr::VariantKind Kind, + int Delta, bool IsPCRel, int Size) { + const MCExpr *TargetExpr = GetSymbolRefExpr(SymbolName, Kind); + if (IsPCRel && Size != 0) { + // If the fixup is pc-relative, we need to bias the value to be relative to + // the start of the field, not the end of the field + TargetExpr = MCBinaryExpr::createSub( + TargetExpr, MCConstantExpr::create(Size, *OutContext), *OutContext); + } + if (Delta != 0) { + TargetExpr = MCBinaryExpr::createAdd( + TargetExpr, MCConstantExpr::create(Delta, *OutContext), *OutContext); + } + return TargetExpr; +} + +int ObjectWriter::EmitSymbolRef(const char *SymbolName, + RelocType RelocationType, int Delta) { + bool IsPCRel = false; + int Size = 0; + MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VK_None; + + // Convert RelocationType to MCSymbolRefExpr + switch (RelocationType) { + case RelocType::IMAGE_REL_BASED_ABSOLUTE: + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF); + Kind = MCSymbolRefExpr::VK_COFF_IMGREL32; + Size = 4; + break; + case RelocType::IMAGE_REL_BASED_HIGHLOW: + Size = 4; + break; + case RelocType::IMAGE_REL_BASED_DIR64: + Size = 8; + break; + case RelocType::IMAGE_REL_BASED_REL32: + Size = 4; + IsPCRel = true; + break; + case RelocType::IMAGE_REL_BASED_THUMB_MOV32: { + const unsigned Offset = GetDFSize(); + const MCExpr *TargetExpr = GenTargetExpr(SymbolName, Kind, Delta); + EmitRelocDirective(Offset, "R_ARM_THM_MOVW_ABS_NC", TargetExpr); + EmitRelocDirective(Offset + 4, "R_ARM_THM_MOVT_ABS", TargetExpr); + return 8; + } + case RelocType::IMAGE_REL_BASED_THUMB_BRANCH24: { + const MCExpr *TargetExpr = GenTargetExpr(SymbolName, Kind, Delta); + EmitRelocDirective(GetDFSize(), "R_ARM_THM_JUMP24", TargetExpr); + return 4; + } + } + + const MCExpr *TargetExpr = GenTargetExpr(SymbolName, Kind, Delta, IsPCRel, Size); + Streamer->EmitValueImpl(TargetExpr, Size, SMLoc(), IsPCRel); + return Size; +} + +void ObjectWriter::EmitWinFrameInfo(const char *FunctionName, int StartOffset, + int EndOffset, const char *BlobSymbolName) { + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF); + + // .pdata emission + MCSection *Section = ObjFileInfo->getPDataSection(); + + // If the function was emitted to a Comdat section, create an associative + // section to place the frame info in. This is due to the Windows linker + // requirement that a function and its unwind info come from the same + // object file. + MCSymbol *Fn = OutContext->getOrCreateSymbol(Twine(FunctionName)); + const MCSectionCOFF *FunctionSection = cast(&Fn->getSection()); + if (FunctionSection->getCharacteristics() & COFF::IMAGE_SCN_LNK_COMDAT) { + Section = OutContext->getAssociativeCOFFSection( + cast(Section), FunctionSection->getCOMDATSymbol()); + } + + Streamer->SwitchSection(Section); + Streamer->EmitValueToAlignment(4); + + const MCExpr *BaseRefRel = + GetSymbolRefExpr(FunctionName, MCSymbolRefExpr::VK_COFF_IMGREL32); + + // start Offset + const MCExpr *StartOfs = MCConstantExpr::create(StartOffset, *OutContext); + Streamer->EmitValue( + MCBinaryExpr::createAdd(BaseRefRel, StartOfs, *OutContext), 4); + + // end Offset + const MCExpr *EndOfs = MCConstantExpr::create(EndOffset, *OutContext); + Streamer->EmitValue(MCBinaryExpr::createAdd(BaseRefRel, EndOfs, *OutContext), + 4); + + // frame symbol reference + Streamer->EmitValue( + GetSymbolRefExpr(BlobSymbolName, MCSymbolRefExpr::VK_COFF_IMGREL32), 4); +} + +void ObjectWriter::EmitCFIStart(int Offset) { + assert(!FrameOpened && "frame should be closed before CFIStart"); + Streamer->EmitCFIStartProc(false); + FrameOpened = true; +} + +void ObjectWriter::EmitCFIEnd(int Offset) { + assert(FrameOpened && "frame should be opened before CFIEnd"); + Streamer->EmitCFIEndProc(); + FrameOpened = false; +} + +void ObjectWriter::EmitCFILsda(const char *LsdaBlobSymbolName) { + assert(FrameOpened && "frame should be opened before CFILsda"); + + // Create symbol reference + MCSymbol *T = OutContext->getOrCreateSymbol(LsdaBlobSymbolName); + Assembler->registerSymbol(*T); + Streamer->EmitCFILsda(T, llvm::dwarf::Constants::DW_EH_PE_pcrel | + llvm::dwarf::Constants::DW_EH_PE_sdata4); +} + +void ObjectWriter::EmitCFICode(int Offset, const char *Blob) { + assert(FrameOpened && "frame should be opened before CFICode"); + + const CFI_CODE *CfiCode = (const CFI_CODE *)Blob; + switch (CfiCode->CfiOpCode) { + case CFI_ADJUST_CFA_OFFSET: + assert(CfiCode->DwarfReg == DWARF_REG_ILLEGAL && + "Unexpected Register Value for OpAdjustCfaOffset"); + Streamer->EmitCFIAdjustCfaOffset(CfiCode->Offset); + break; + case CFI_REL_OFFSET: + Streamer->EmitCFIRelOffset(CfiCode->DwarfReg, CfiCode->Offset); + break; + case CFI_DEF_CFA_REGISTER: + assert(CfiCode->Offset == 0 && + "Unexpected Offset Value for OpDefCfaRegister"); + Streamer->EmitCFIDefCfaRegister(CfiCode->DwarfReg); + break; + default: + assert(false && "Unrecognized CFI"); + break; + } +} + +void ObjectWriter::EmitLabelDiff(const MCSymbol *From, const MCSymbol *To, + unsigned int Size) { + MCSymbolRefExpr::VariantKind Variant = MCSymbolRefExpr::VK_None; + const MCExpr *FromRef = MCSymbolRefExpr::create(From, Variant, *OutContext), + *ToRef = MCSymbolRefExpr::create(To, Variant, *OutContext); + const MCExpr *AddrDelta = + MCBinaryExpr::create(MCBinaryExpr::Sub, ToRef, FromRef, *OutContext); + Streamer->EmitValue(AddrDelta, Size); +} + +void ObjectWriter::EmitSymRecord(int Size, SymbolRecordKind SymbolKind) { + RecordPrefix Rec; + Rec.RecordLen = ulittle16_t(Size + sizeof(ulittle16_t)); + Rec.RecordKind = ulittle16_t((uint16_t)SymbolKind); + Streamer->EmitBytes(StringRef((char *)&Rec, sizeof(Rec))); +} + +void ObjectWriter::EmitCOFFSecRel32Value(MCExpr const *Value) { + MCDataFragment *DF = Streamer->getOrCreateDataFragment(); + MCFixup Fixup = MCFixup::create(DF->getContents().size(), Value, FK_SecRel_4); + DF->getFixups().push_back(Fixup); + DF->getContents().resize(DF->getContents().size() + 4, 0); +} + +void ObjectWriter::EmitVarDefRange(const MCSymbol *Fn, + const LocalVariableAddrRange &Range) { + const MCSymbolRefExpr *BaseSym = MCSymbolRefExpr::create(Fn, *OutContext); + const MCExpr *Offset = MCConstantExpr::create(Range.OffsetStart, *OutContext); + const MCExpr *Expr = MCBinaryExpr::createAdd(BaseSym, Offset, *OutContext); + EmitCOFFSecRel32Value(Expr); + Streamer->EmitCOFFSectionIndex(Fn); + Streamer->EmitIntValue(Range.Range, 2); +} + +void ObjectWriter::EmitCVDebugVarInfo(const MCSymbol *Fn, + const DebugVarInfo LocInfos[], + int NumVarInfos) { + for (int I = 0; I < NumVarInfos; I++) { + // Emit an S_LOCAL record + DebugVarInfo Var = LocInfos[I]; + TypeIndex Type = TypeIndex(Var.TypeIndex); + LocalSymFlags Flags = LocalSymFlags::None; + unsigned SizeofSym = sizeof(Type) + sizeof(Flags); + unsigned NameLength = Var.Name.length() + 1; + EmitSymRecord(SizeofSym + NameLength, SymbolRecordKind::LocalSym); + if (Var.IsParam) { + Flags |= LocalSymFlags::IsParameter; + } + Streamer->EmitBytes(StringRef((char *)&Type, sizeof(Type))); + Streamer->EmitIntValue(static_cast(Flags), sizeof(Flags)); + Streamer->EmitBytes(StringRef(Var.Name.c_str(), NameLength)); + + for (const auto &Range : Var.Ranges) { + // Emit a range record + switch (Range.loc.vlType) { + case ICorDebugInfo::VLT_REG: + case ICorDebugInfo::VLT_REG_FP: { + + // Currently only support integer registers. + // TODO: support xmm registers + if (Range.loc.vlReg.vlrReg >= + sizeof(cvRegMapAmd64) / sizeof(cvRegMapAmd64[0])) { + break; + } + SymbolRecordKind SymbolKind = SymbolRecordKind::DefRangeRegisterSym; + unsigned SizeofDefRangeRegisterSym = sizeof(DefRangeRegisterSym::Hdr) + + sizeof(DefRangeRegisterSym::Range); + EmitSymRecord(SizeofDefRangeRegisterSym, SymbolKind); + + DefRangeRegisterSym DefRangeRegisterSymbol(SymbolKind); + DefRangeRegisterSymbol.Range.OffsetStart = Range.startOffset; + DefRangeRegisterSymbol.Range.Range = + Range.endOffset - Range.startOffset; + DefRangeRegisterSymbol.Range.ISectStart = 0; + DefRangeRegisterSymbol.Hdr.Register = + cvRegMapAmd64[Range.loc.vlReg.vlrReg]; + unsigned Length = sizeof(DefRangeRegisterSymbol.Hdr); + Streamer->EmitBytes( + StringRef((char *)&DefRangeRegisterSymbol.Hdr, Length)); + EmitVarDefRange(Fn, DefRangeRegisterSymbol.Range); + break; + } + + case ICorDebugInfo::VLT_STK: { + + // TODO: support REGNUM_AMBIENT_SP + if (Range.loc.vlStk.vlsBaseReg >= + sizeof(cvRegMapAmd64) / sizeof(cvRegMapAmd64[0])) { + break; + } + + assert(Range.loc.vlStk.vlsBaseReg < + sizeof(cvRegMapAmd64) / sizeof(cvRegMapAmd64[0]) && + "Register number should be in the range of [REGNUM_RAX, " + "REGNUM_R15]."); + + SymbolRecordKind SymbolKind = SymbolRecordKind::DefRangeRegisterRelSym; + unsigned SizeofDefRangeRegisterRelSym = + sizeof(DefRangeRegisterRelSym::Hdr) + + sizeof(DefRangeRegisterRelSym::Range); + EmitSymRecord(SizeofDefRangeRegisterRelSym, SymbolKind); + + DefRangeRegisterRelSym DefRangeRegisterRelSymbol(SymbolKind); + DefRangeRegisterRelSymbol.Range.OffsetStart = Range.startOffset; + DefRangeRegisterRelSymbol.Range.Range = + Range.endOffset - Range.startOffset; + DefRangeRegisterRelSymbol.Range.ISectStart = 0; + DefRangeRegisterRelSymbol.Hdr.Register = + cvRegMapAmd64[Range.loc.vlStk.vlsBaseReg]; + DefRangeRegisterRelSymbol.Hdr.BasePointerOffset = + Range.loc.vlStk.vlsOffset; + + unsigned Length = sizeof(DefRangeRegisterRelSymbol.Hdr); + Streamer->EmitBytes( + StringRef((char *)&DefRangeRegisterRelSymbol.Hdr, Length)); + EmitVarDefRange(Fn, DefRangeRegisterRelSymbol.Range); + break; + } + + case ICorDebugInfo::VLT_REG_BYREF: + case ICorDebugInfo::VLT_STK_BYREF: + case ICorDebugInfo::VLT_REG_REG: + case ICorDebugInfo::VLT_REG_STK: + case ICorDebugInfo::VLT_STK_REG: + case ICorDebugInfo::VLT_STK2: + case ICorDebugInfo::VLT_FPSTK: + case ICorDebugInfo::VLT_FIXED_VA: + // TODO: for optimized debugging + break; + + default: + assert(false && "Unknown varloc type!"); + break; + } + } + } +} + +void ObjectWriter::EmitCVDebugFunctionInfo(const char *FunctionName, + int FunctionSize) { + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF); + + // Mark the end of function. + MCSymbol *FnEnd = OutContext->createTempSymbol(); + Streamer->EmitLabel(FnEnd); + + MCSection *Section = ObjFileInfo->getCOFFDebugSymbolsSection(); + Streamer->SwitchSection(Section); + // Emit debug section magic before the first entry. + if (FuncId == 1) { + Streamer->EmitIntValue(COFF::DEBUG_SECTION_MAGIC, 4); + } + MCSymbol *Fn = OutContext->getOrCreateSymbol(Twine(FunctionName)); + + // Emit a symbol subsection, required by VS2012+ to find function boundaries. + MCSymbol *SymbolsBegin = OutContext->createTempSymbol(), + *SymbolsEnd = OutContext->createTempSymbol(); + Streamer->EmitIntValue(unsigned(DebugSubsectionKind::Symbols), 4); + EmitLabelDiff(SymbolsBegin, SymbolsEnd); + Streamer->EmitLabel(SymbolsBegin); + { + ProcSym ProcSymbol(SymbolRecordKind::GlobalProcIdSym); + ProcSymbol.CodeSize = FunctionSize; + ProcSymbol.DbgEnd = FunctionSize; + + unsigned FunctionNameLength = strlen(FunctionName) + 1; + unsigned HeaderSize = + sizeof(ProcSymbol.Parent) + sizeof(ProcSymbol.End) + + sizeof(ProcSymbol.Next) + sizeof(ProcSymbol.CodeSize) + + sizeof(ProcSymbol.DbgStart) + sizeof(ProcSymbol.DbgEnd) + + sizeof(ProcSymbol.FunctionType); + unsigned SymbolSize = HeaderSize + 4 + 2 + 1 + FunctionNameLength; + EmitSymRecord(SymbolSize, SymbolRecordKind::GlobalProcIdSym); + + Streamer->EmitBytes(StringRef((char *)&ProcSymbol.Parent, HeaderSize)); + // Emit relocation + Streamer->EmitCOFFSecRel32(Fn, 0); + Streamer->EmitCOFFSectionIndex(Fn); + + // Emit flags + Streamer->EmitIntValue(0, 1); + + // Emit the function display name as a null-terminated string. + + Streamer->EmitBytes(StringRef(FunctionName, FunctionNameLength)); + + // Emit local var info + int NumVarInfos = DebugVarInfos.size(); + if (NumVarInfos > 0) { + EmitCVDebugVarInfo(Fn, &DebugVarInfos[0], NumVarInfos); + DebugVarInfos.clear(); + } + + // We're done with this function. + EmitSymRecord(0, SymbolRecordKind::ProcEnd); + } + + Streamer->EmitLabel(SymbolsEnd); + + // Every subsection must be aligned to a 4-byte boundary. + Streamer->EmitValueToAlignment(4); + + // We have an assembler directive that takes care of the whole line table. + // We also increase function id for the next function. + Streamer->EmitCVLinetableDirective(FuncId++, Fn, FnEnd); +} + +void ObjectWriter::EmitDebugFileInfo(int FileId, const char *FileName) { + assert(FileId > 0 && "FileId should be greater than 0."); + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF) { + Streamer->EmitCVFileDirective(FileId, FileName); + } else { + Streamer->EmitDwarfFileDirective(FileId, "", FileName); + } +} + +void ObjectWriter::EmitDebugFunctionInfo(const char *FunctionName, + int FunctionSize) { + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF) { + Streamer->EmitCVFuncIdDirective(FuncId); + EmitCVDebugFunctionInfo(FunctionName, FunctionSize); + } else { + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsELF) { + MCSymbol *Sym = OutContext->getOrCreateSymbol(Twine(FunctionName)); + Streamer->EmitSymbolAttribute(Sym, MCSA_ELF_TypeFunction); + Streamer->emitELFSize(Sym, + MCConstantExpr::create(FunctionSize, *OutContext)); + } + // TODO: Should test it for Macho. + } +} + +void ObjectWriter::EmitDebugVar(char *Name, int TypeIndex, bool IsParm, + int RangeCount, + const ICorDebugInfo::NativeVarInfo *Ranges) { + assert(RangeCount != 0); + DebugVarInfo NewVar(Name, TypeIndex, IsParm); + + for (int I = 0; I < RangeCount; I++) { + assert(Ranges[0].varNumber == Ranges[I].varNumber); + NewVar.Ranges.push_back(Ranges[I]); + } + + DebugVarInfos.push_back(NewVar); +} + +void ObjectWriter::EmitDebugLoc(int NativeOffset, int FileId, int LineNumber, + int ColNumber) { + assert(FileId > 0 && "FileId should be greater than 0."); + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF) { + Streamer->EmitCVFuncIdDirective(FuncId); + Streamer->EmitCVLocDirective(FuncId, FileId, LineNumber, ColNumber, false, + true, "", SMLoc()); + } else { + Streamer->EmitDwarfLocDirective(FileId, LineNumber, ColNumber, 1, 0, 0, ""); + } +} + +void ObjectWriter::EmitCVUserDefinedTypesSymbols() { + const auto &UDTs = TypeBuilder.GetUDTs(); + if (UDTs.empty()) { + return; + } + MCSection *Section = ObjFileInfo->getCOFFDebugSymbolsSection(); + Streamer->SwitchSection(Section); + + MCSymbol *SymbolsBegin = OutContext->createTempSymbol(), + *SymbolsEnd = OutContext->createTempSymbol(); + Streamer->EmitIntValue(unsigned(DebugSubsectionKind::Symbols), 4); + EmitLabelDiff(SymbolsBegin, SymbolsEnd); + Streamer->EmitLabel(SymbolsBegin); + + for (const std::pair &UDT : UDTs) { + unsigned NameLength = UDT.first.length() + 1; + unsigned RecordLength = 2 + 4 + NameLength; + Streamer->EmitIntValue(RecordLength, 2); + Streamer->EmitIntValue(unsigned(SymbolKind::S_UDT), 2); + Streamer->EmitIntValue(UDT.second.getIndex(), 4); + Streamer->EmitBytes(StringRef(UDT.first.c_str(), NameLength)); + } + Streamer->EmitLabel(SymbolsEnd); + Streamer->EmitValueToAlignment(4); +} + +void ObjectWriter::EmitDebugModuleInfo() { + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF) { + TypeBuilder.EmitTypeInformation(ObjFileInfo->getCOFFDebugTypesSection()); + EmitCVUserDefinedTypesSymbols(); + } + + // Ensure ending all sections. + for (auto Section : Sections) { + Streamer->endSection(Section); + } + + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF) { + MCSection *Section = ObjFileInfo->getCOFFDebugSymbolsSection(); + Streamer->SwitchSection(Section); + Streamer->EmitCVFileChecksumsDirective(); + Streamer->EmitCVStringTableDirective(); + } else { + OutContext->setGenDwarfForAssembly(true); + } +} + +unsigned +ObjectWriter::GetEnumTypeIndex(const EnumTypeDescriptor &TypeDescriptor, + const EnumRecordTypeDescriptor *TypeRecords) { + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF && + "only COFF is supported now"); + return TypeBuilder.GetEnumTypeIndex(TypeDescriptor, TypeRecords); +} + +unsigned +ObjectWriter::GetClassTypeIndex(const ClassTypeDescriptor &ClassDescriptor) { + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF && + "only COFF is supported now"); + return TypeBuilder.GetClassTypeIndex(ClassDescriptor); +} + +unsigned ObjectWriter::GetCompleteClassTypeIndex( + const ClassTypeDescriptor &ClassDescriptor, + const ClassFieldsTypeDescriptior &ClassFieldsDescriptor, + const DataFieldDescriptor *FieldsDescriptors) { + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF && + "only COFF is supported now"); + return TypeBuilder.GetCompleteClassTypeIndex( + ClassDescriptor, ClassFieldsDescriptor, FieldsDescriptors); +} + +unsigned +ObjectWriter::GetArrayTypeIndex(const ClassTypeDescriptor &ClassDescriptor, + const ArrayTypeDescriptor &ArrayDescriptor) { + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF && + "only COFF is supported now"); + return TypeBuilder.GetArrayTypeIndex(ClassDescriptor, ArrayDescriptor); +} + +unsigned +ObjectWriter::GetPointerTypeIndex(const PointerTypeDescriptor& PointerDescriptor) { + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF && + "only COFF is supported now"); + return TypeBuilder.GetPointerTypeIndex(PointerDescriptor); +} + +unsigned +ObjectWriter::GetMemberFunctionTypeIndex(const MemberFunctionTypeDescriptor& MemberDescriptor, + uint32_t const *const ArgumentTypes) { + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF && + "only COFF is supported now"); + return TypeBuilder.GetMemberFunctionTypeIndex(MemberDescriptor, ArgumentTypes); +} + +unsigned +ObjectWriter::GetMemberFunctionId(const MemberFunctionIdTypeDescriptor& MemberIdDescriptor) { + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF && + "only COFF is supported now"); + return TypeBuilder.GetMemberFunctionId(MemberIdDescriptor); +} + diff --git a/external/corert/src/Native/ObjWriter/objwriter.exports b/external/corert/src/Native/ObjWriter/objwriter.exports new file mode 100644 index 0000000000..c42c465df8 --- /dev/null +++ b/external/corert/src/Native/ObjWriter/objwriter.exports @@ -0,0 +1,26 @@ +InitObjWriter +FinishObjWriter +SwitchSection +SetCodeSectionAttribute +EmitAlignment +EmitBlob +EmitIntValue +EmitSymbolDef +EmitSymbolRef +EmitWinFrameInfo +EmitCFIStart +EmitCFIEnd +EmitCFICode +EmitCFILsda +EmitDebugFileInfo +EmitDebugLoc +EmitDebugFunctionInfo +EmitDebugModuleInfo +EmitDebugVar +GetEnumTypeIndex +GetClassTypeIndex +GetCompleteClassTypeIndex +GetArrayTypeIndex +GetPointerTypeIndex +GetMemberFunctionTypeIndex +GetMemberFunctionIdTypeIndex \ No newline at end of file diff --git a/external/corert/src/Native/ObjWriter/objwriter.h b/external/corert/src/Native/ObjWriter/objwriter.h new file mode 100644 index 0000000000..46b6bbd9ff --- /dev/null +++ b/external/corert/src/Native/ObjWriter/objwriter.h @@ -0,0 +1,320 @@ +//===---- objwriter.h --------------------------------*- C++ -*-===// +// +// object writer +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. +// See LICENSE file in the project root for full license information. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/Target/TargetOptions.h" +#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" + +#include "cfi.h" +#include "jitDebugInfo.h" +#include +#include +#include "typeBuilder.h" + +using namespace llvm; +using namespace llvm::codeview; + +enum CustomSectionAttributes : int32_t { + CustomSectionAttributes_ReadOnly = 0x0000, + CustomSectionAttributes_Writeable = 0x0001, + CustomSectionAttributes_Executable = 0x0002, + CustomSectionAttributes_MachO_Init_Func_Pointers = 0x0100, +}; + +enum class RelocType { + IMAGE_REL_BASED_ABSOLUTE = 0x00, + IMAGE_REL_BASED_HIGHLOW = 0x03, + IMAGE_REL_BASED_THUMB_MOV32 = 0x07, + IMAGE_REL_BASED_DIR64 = 0x0A, + IMAGE_REL_BASED_REL32 = 0x10, + IMAGE_REL_BASED_THUMB_BRANCH24 = 0x13, +}; + +class ObjectWriter { +public: + bool Init(StringRef FunctionName); + void Finish(); + + void SwitchSection(const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName); + void SetCodeSectionAttribute(const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName); + + void EmitAlignment(int ByteAlignment); + void EmitBlob(int BlobSize, const char *Blob); + void EmitIntValue(uint64_t Value, unsigned Size); + void EmitSymbolDef(const char *SymbolName); + void EmitWinFrameInfo(const char *FunctionName, int StartOffset, + int EndOffset, const char *BlobSymbolName); + int EmitSymbolRef(const char *SymbolName, RelocType RelocType, int Delta); + + void EmitDebugFileInfo(int FileId, const char *FileName); + void EmitDebugFunctionInfo(const char *FunctionName, int FunctionSize); + void EmitDebugVar(char *Name, int TypeIndex, bool IsParm, int RangeCount, + const ICorDebugInfo::NativeVarInfo *Ranges); + void EmitDebugLoc(int NativeOffset, int FileId, int LineNumber, + int ColNumber); + void EmitDebugModuleInfo(); + + void EmitCFIStart(int Offset); + void EmitCFIEnd(int Offset); + void EmitCFILsda(const char *LsdaBlobSymbolName); + void EmitCFICode(int Offset, const char *Blob); + + unsigned GetEnumTypeIndex(const EnumTypeDescriptor &TypeDescriptor, + const EnumRecordTypeDescriptor *TypeRecords); + unsigned GetClassTypeIndex(const ClassTypeDescriptor &ClassDescriptor); + unsigned GetCompleteClassTypeIndex( + const ClassTypeDescriptor &ClassDescriptor, + const ClassFieldsTypeDescriptior &ClassFieldsDescriptor, + const DataFieldDescriptor *FieldsDescriptors); + + unsigned GetArrayTypeIndex(const ClassTypeDescriptor &ClassDescriptor, + const ArrayTypeDescriptor &ArrayDescriptor); + + unsigned GetPointerTypeIndex(const PointerTypeDescriptor& PointerDescriptor); + + unsigned GetMemberFunctionTypeIndex(const MemberFunctionTypeDescriptor& MemberDescriptor, + uint32_t const *const ArgumentTypes); + + unsigned GetMemberFunctionId(const MemberFunctionIdTypeDescriptor& MemberIdDescriptor); + +private: + void EmitLabelDiff(const MCSymbol *From, const MCSymbol *To, + unsigned int Size = 4); + void EmitSymRecord(int Size, SymbolRecordKind SymbolKind); + void EmitCOFFSecRel32Value(MCExpr const *Value); + + void EmitVarDefRange(const MCSymbol *Fn, const LocalVariableAddrRange &Range); + void EmitCVDebugVarInfo(const MCSymbol *Fn, const DebugVarInfo LocInfos[], + int NumVarInfos); + void EmitCVDebugFunctionInfo(const char *FunctionName, int FunctionSize); + + const MCSymbolRefExpr *GetSymbolRefExpr( + const char *SymbolName, + MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VK_None); + + MCSection *GetSection(const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName); + + MCSection *GetSpecificSection(const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName); + + void EmitCVUserDefinedTypesSymbols(); + + void InitTripleName(); + Triple GetTriple(); + unsigned GetDFSize(); + bool EmitRelocDirective(const int Offset, StringRef Name, const MCExpr *Expr); + const MCExpr *GenTargetExpr(const char *SymbolName, + MCSymbolRefExpr::VariantKind Kind, int Delta, + bool IsPCRel = false, int Size = 0); + + +private: + std::unique_ptr RegisterInfo; + std::unique_ptr AsmInfo; + std::unique_ptr ObjFileInfo; + std::unique_ptr OutContext; + MCAsmBackend *AsmBackend; // Owned by MCStreamer + std::unique_ptr InstrInfo; + std::unique_ptr SubtargetInfo; + MCCodeEmitter *CodeEmitter; // Owned by MCStreamer + std::unique_ptr TMachine; + std::unique_ptr AssemblerPrinter; + MCAssembler *Assembler; // Owned by MCStreamer + + std::unique_ptr OS; + MCTargetOptions TargetMOptions; + bool FrameOpened; + std::vector DebugVarInfos; + + std::set Sections; + int FuncId; + + UserDefinedTypesBuilder TypeBuilder; + + std::string TripleName; + + MCObjectStreamer *Streamer; // Owned by AsmPrinter +}; + +// When object writer is created/initialized successfully, it is returned. +// Or null object is returned. Client should check this. +extern "C" ObjectWriter *InitObjWriter(const char *ObjectFilePath) { + ObjectWriter *OW = new ObjectWriter(); + if (OW->Init(ObjectFilePath)) { + return OW; + } + delete OW; + return nullptr; +} + +extern "C" void FinishObjWriter(ObjectWriter *OW) { + assert(OW && "ObjWriter is null"); + OW->Finish(); + delete OW; +} + +extern "C" void SwitchSection(ObjectWriter *OW, const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName) { + assert(OW && "ObjWriter is null"); + OW->SwitchSection(SectionName, attributes, ComdatName); +} + +extern "C" void SetCodeSectionAttribute(ObjectWriter *OW, + const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName) { + assert(OW && "ObjWriter is null"); + OW->SetCodeSectionAttribute(SectionName, attributes, ComdatName); +} + +extern "C" void EmitAlignment(ObjectWriter *OW, int ByteAlignment) { + assert(OW && "ObjWriter is null"); + OW->EmitAlignment(ByteAlignment); +} + +extern "C" void EmitBlob(ObjectWriter *OW, int BlobSize, const char *Blob) { + assert(OW && "ObjWriter null"); + OW->EmitBlob(BlobSize, Blob); +} + +extern "C" void EmitIntValue(ObjectWriter *OW, uint64_t Value, unsigned Size) { + assert(OW && "ObjWriter is null"); + OW->EmitIntValue(Value, Size); +} + +extern "C" void EmitSymbolDef(ObjectWriter *OW, const char *SymbolName) { + assert(OW && "ObjWriter is null"); + OW->EmitSymbolDef(SymbolName); +} + +extern "C" int EmitSymbolRef(ObjectWriter *OW, const char *SymbolName, + RelocType RelocType, int Delta) { + assert(OW && "ObjWriter is null"); + return OW->EmitSymbolRef(SymbolName, RelocType, Delta); +} + +extern "C" void EmitWinFrameInfo(ObjectWriter *OW, const char *FunctionName, + int StartOffset, int EndOffset, + const char *BlobSymbolName) { + assert(OW && "ObjWriter is null"); + OW->EmitWinFrameInfo(FunctionName, StartOffset, EndOffset, BlobSymbolName); +} + +extern "C" void EmitCFIStart(ObjectWriter *OW, int Offset) { + assert(OW && "ObjWriter is null"); + OW->EmitCFIStart(Offset); +} + +extern "C" void EmitCFIEnd(ObjectWriter *OW, int Offset) { + assert(OW && "ObjWriter is null"); + OW->EmitCFIEnd(Offset); +} + +extern "C" void EmitCFILsda(ObjectWriter *OW, const char *LsdaBlobSymbolName) { + assert(OW && "ObjWriter is null"); + OW->EmitCFILsda(LsdaBlobSymbolName); +} + +extern "C" void EmitCFICode(ObjectWriter *OW, int Offset, const char *Blob) { + assert(OW && "ObjWriter is null"); + OW->EmitCFICode(Offset, Blob); +} + +extern "C" void EmitDebugFileInfo(ObjectWriter *OW, int FileId, + const char *FileName) { + assert(OW && "ObjWriter is null"); + OW->EmitDebugFileInfo(FileId, FileName); +} + +extern "C" void EmitDebugFunctionInfo(ObjectWriter *OW, + const char *FunctionName, + int FunctionSize) { + assert(OW && "ObjWriter is null"); + OW->EmitDebugFunctionInfo(FunctionName, FunctionSize); +} + +extern "C" void EmitDebugVar(ObjectWriter *OW, char *Name, int TypeIndex, + bool IsParam, int RangeCount, + ICorDebugInfo::NativeVarInfo *Ranges) { + assert(OW && "ObjWriter is null"); + OW->EmitDebugVar(Name, TypeIndex, IsParam, RangeCount, Ranges); +} + +extern "C" void EmitDebugLoc(ObjectWriter *OW, int NativeOffset, int FileId, + int LineNumber, int ColNumber) { + assert(OW && "ObjWriter is null"); + OW->EmitDebugLoc(NativeOffset, FileId, LineNumber, ColNumber); +} + +// This should be invoked at the end of module emission to finalize +// debug module info. +extern "C" void EmitDebugModuleInfo(ObjectWriter *OW) { + assert(OW && "ObjWriter is null"); + OW->EmitDebugModuleInfo(); +} + +extern "C" unsigned GetEnumTypeIndex(ObjectWriter *OW, + EnumTypeDescriptor TypeDescriptor, + EnumRecordTypeDescriptor *TypeRecords) { + assert(OW && "ObjWriter is null"); + return OW->GetEnumTypeIndex(TypeDescriptor, TypeRecords); +} + +extern "C" unsigned GetClassTypeIndex(ObjectWriter *OW, + ClassTypeDescriptor ClassDescriptor) { + assert(OW && "ObjWriter is null"); + return OW->GetClassTypeIndex(ClassDescriptor); +} + +extern "C" unsigned +GetCompleteClassTypeIndex(ObjectWriter *OW, ClassTypeDescriptor ClassDescriptor, + ClassFieldsTypeDescriptior ClassFieldsDescriptor, + DataFieldDescriptor *FieldsDescriptors) { + assert(OW && "ObjWriter is null"); + return OW->GetCompleteClassTypeIndex(ClassDescriptor, ClassFieldsDescriptor, + FieldsDescriptors); +} + +extern "C" unsigned GetArrayTypeIndex(ObjectWriter *OW, + ClassTypeDescriptor ClassDescriptor, + ArrayTypeDescriptor ArrayDescriptor) { + assert(OW && "ObjWriter is null"); + return OW->GetArrayTypeIndex(ClassDescriptor, ArrayDescriptor); +} + +extern "C" unsigned GetPointerTypeIndex(ObjectWriter *OW, + PointerTypeDescriptor PointerDescriptor) { + assert(OW && "ObjWriter is null"); + return OW->GetPointerTypeIndex(PointerDescriptor); +} + +extern "C" unsigned GetMemberFunctionTypeIndex(ObjectWriter *OW, + MemberFunctionTypeDescriptor MemberDescriptor, + uint32_t *ArgumentTypes) { + assert(OW && "ObjWriter is null"); + return OW->GetMemberFunctionTypeIndex(MemberDescriptor, ArgumentTypes); +} + +extern "C" unsigned GetMemberFunctionIdTypeIndex(ObjectWriter *OW, + MemberFunctionIdTypeDescriptor MemberIdDescriptor) { + assert(OW && "ObjWriter is null"); + return OW->GetMemberFunctionId(MemberIdDescriptor); +} diff --git a/external/corert/src/Native/ObjWriter/typeBuilder.cpp b/external/corert/src/Native/ObjWriter/typeBuilder.cpp new file mode 100644 index 0000000000..2b6edacc5a --- /dev/null +++ b/external/corert/src/Native/ObjWriter/typeBuilder.cpp @@ -0,0 +1,321 @@ +//===---- typeBuilder.cpp --------------------------------*- C++ -*-===// +// +// type builder implementation using codeview::TypeTableBuilder +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. +// See LICENSE file in the project root for full license information. +// +//===----------------------------------------------------------------------===// + +#include "typeBuilder.h" +#include "llvm/BinaryFormat/COFF.h" +#include +#include + +UserDefinedTypesBuilder::UserDefinedTypesBuilder() + : Allocator(), TypeTable(Allocator), Streamer(nullptr), + TargetPointerSize(0) +{ + VFTableShapeRecord vfTableShape(TypeRecordKind::VFTableShape); + ClassVTableTypeIndex = TypeTable.writeKnownType(vfTableShape); +} + +void UserDefinedTypesBuilder::SetStreamer(MCObjectStreamer *Streamer) { + assert(this->Streamer == nullptr); + assert(Streamer != nullptr); + this->Streamer = Streamer; +} + +void UserDefinedTypesBuilder::SetTargetPointerSize(unsigned TargetPointerSize) { + assert(this->TargetPointerSize == 0); + assert(TargetPointerSize != 0); + this->TargetPointerSize = TargetPointerSize; +} + +void UserDefinedTypesBuilder::EmitCodeViewMagicVersion() { + Streamer->EmitValueToAlignment(4); + Streamer->AddComment("Debug section magic"); + Streamer->EmitIntValue(COFF::DEBUG_SECTION_MAGIC, 4); +} + +ClassOptions UserDefinedTypesBuilder::GetCommonClassOptions() { + return ClassOptions(); +} + +void UserDefinedTypesBuilder::EmitTypeInformation( + MCSection *COFFDebugTypesSection) { + + if (TypeTable.empty()) + return; + + Streamer->SwitchSection(COFFDebugTypesSection); + EmitCodeViewMagicVersion(); + + TypeTable.ForEachRecord([&](TypeIndex FieldTypeIndex, + ArrayRef Record) { + StringRef S(reinterpret_cast(Record.data()), Record.size()); + Streamer->EmitBinaryData(S); + }); +} + +unsigned UserDefinedTypesBuilder::GetEnumFieldListType( + uint64 Count, const EnumRecordTypeDescriptor *TypeRecords) { + FieldListRecordBuilder FLRB(TypeTable); + FLRB.begin(); +#ifndef NDEBUG + uint64 MaxInt = (unsigned int)-1; + assert(Count <= MaxInt && "There are too many fields inside enum"); +#endif + for (int i = 0; i < (int)Count; ++i) { + EnumRecordTypeDescriptor record = TypeRecords[i]; + EnumeratorRecord ER(MemberAccess::Public, APSInt::getUnsigned(record.Value), + record.Name); + FLRB.writeMemberType(ER); + } + TypeIndex Type = FLRB.end(true); + return Type.getIndex(); +} + +unsigned UserDefinedTypesBuilder::GetEnumTypeIndex( + const EnumTypeDescriptor &TypeDescriptor, + const EnumRecordTypeDescriptor *TypeRecords) { + + ClassOptions CO = GetCommonClassOptions(); + unsigned FieldListIndex = + GetEnumFieldListType(TypeDescriptor.ElementCount, TypeRecords); + TypeIndex FieldListIndexType = TypeIndex(FieldListIndex); + TypeIndex ElementTypeIndex = TypeIndex(TypeDescriptor.ElementType); + + EnumRecord EnumRecord(TypeDescriptor.ElementCount, CO, FieldListIndexType, + TypeDescriptor.Name, StringRef(), + ElementTypeIndex); + + TypeIndex Type = TypeTable.writeKnownType(EnumRecord); + UserDefinedTypes.push_back(std::make_pair(TypeDescriptor.Name, Type)); + return Type.getIndex(); +} + +unsigned UserDefinedTypesBuilder::GetClassTypeIndex( + const ClassTypeDescriptor &ClassDescriptor) { + TypeRecordKind Kind = + ClassDescriptor.IsStruct ? TypeRecordKind::Struct : TypeRecordKind::Class; + ClassOptions CO = ClassOptions::ForwardReference | GetCommonClassOptions(); + + TypeIndex FieldListIndex = TypeIndex(); + uint16_t memberCount = 0; + + if (!ClassDescriptor.IsStruct) { + FieldListRecordBuilder FLBR(TypeTable); + FLBR.begin(); + memberCount++; + AddClassVTShape(FLBR); + FieldListIndex = FLBR.end(true); + } + + ClassRecord CR(Kind, memberCount, CO, FieldListIndex, TypeIndex(), TypeIndex(), 0, + ClassDescriptor.Name, StringRef()); + TypeIndex FwdDeclTI = TypeTable.writeKnownType(CR); + return FwdDeclTI.getIndex(); +} + +unsigned UserDefinedTypesBuilder::GetCompleteClassTypeIndex( + const ClassTypeDescriptor &ClassDescriptor, + const ClassFieldsTypeDescriptior &ClassFieldsDescriptor, + const DataFieldDescriptor *FieldsDescriptors) { + + FieldListRecordBuilder FLBR(TypeTable); + FLBR.begin(); + + uint16_t memberCount = 0; + if (!ClassDescriptor.IsStruct) { + memberCount++; + AddClassVTShape(FLBR); + } + + if (ClassDescriptor.BaseClassId != 0) { + memberCount++; + AddBaseClass(FLBR, ClassDescriptor.BaseClassId); + } + + for (int i = 0; i < ClassFieldsDescriptor.FieldsCount; ++i) { + DataFieldDescriptor desc = FieldsDescriptors[i]; + MemberAccess Access = MemberAccess::Public; + TypeIndex MemberBaseType(desc.FieldTypeIndex); + if (desc.Offset == 0xFFFFFFFF) + { + StaticDataMemberRecord SDMR(Access, MemberBaseType, desc.Name); + FLBR.writeMemberType(SDMR); + } + else + { + int MemberOffsetInBytes = desc.Offset; + DataMemberRecord DMR(Access, MemberBaseType, MemberOffsetInBytes, + desc.Name); + FLBR.writeMemberType(DMR); + } + memberCount++; + } + TypeIndex FieldListIndex = FLBR.end(true); + TypeRecordKind Kind = + ClassDescriptor.IsStruct ? TypeRecordKind::Struct : TypeRecordKind::Class; + ClassOptions CO = GetCommonClassOptions(); + ClassRecord CR(Kind, memberCount, CO, FieldListIndex, + TypeIndex(), TypeIndex(), ClassFieldsDescriptor.Size, + ClassDescriptor.Name, StringRef()); + TypeIndex ClassIndex = TypeTable.writeKnownType(CR); + + UserDefinedTypes.push_back(std::make_pair(ClassDescriptor.Name, ClassIndex)); + + return ClassIndex.getIndex(); +} + +unsigned UserDefinedTypesBuilder::GetArrayTypeIndex( + const ClassTypeDescriptor &ClassDescriptor, + const ArrayTypeDescriptor &ArrayDescriptor) { + FieldListRecordBuilder FLBR(TypeTable); + FLBR.begin(); + + unsigned Offset = 0; + unsigned FieldsCount = 0; + + assert(ClassDescriptor.BaseClassId != 0); + + AddClassVTShape(FLBR); + FieldsCount++; + AddBaseClass(FLBR, ClassDescriptor.BaseClassId); + FieldsCount++; + Offset += TargetPointerSize; + + MemberAccess Access = MemberAccess::Public; + TypeIndex IndexType = TypeIndex(SimpleTypeKind::Int32); + DataMemberRecord CountDMR(Access, IndexType, Offset, "count"); + FLBR.writeMemberType(CountDMR); + FieldsCount++; + Offset += TargetPointerSize; + + if (ArrayDescriptor.IsMultiDimensional == 1) { + for (unsigned i = 0; i < ArrayDescriptor.Rank; ++i) { + DataMemberRecord LengthDMR(Access, TypeIndex(SimpleTypeKind::Int32), + Offset, ArrayDimentions.GetLengthName(i)); + FLBR.writeMemberType(LengthDMR); + FieldsCount++; + Offset += 4; + } + + for (unsigned i = 0; i < ArrayDescriptor.Rank; ++i) { + DataMemberRecord BoundsDMR(Access, TypeIndex(SimpleTypeKind::Int32), + Offset, ArrayDimentions.GetBoundsName(i)); + FLBR.writeMemberType(BoundsDMR); + FieldsCount++; + Offset += 4; + } + } + + TypeIndex ElementTypeIndex = TypeIndex(ArrayDescriptor.ElementType); + ArrayRecord AR(ElementTypeIndex, IndexType, ArrayDescriptor.Size, ""); + TypeIndex ArrayIndex = TypeTable.writeKnownType(AR); + DataMemberRecord ArrayDMR(Access, ArrayIndex, Offset, "values"); + FLBR.writeMemberType(ArrayDMR); + FieldsCount++; + + TypeIndex FieldListIndex = FLBR.end(true); + + assert(ClassDescriptor.IsStruct == false); + TypeRecordKind Kind = TypeRecordKind::Class; + ClassOptions CO = GetCommonClassOptions(); + ClassRecord CR(Kind, FieldsCount, CO, FieldListIndex, TypeIndex(), + TypeIndex(), ArrayDescriptor.Size, ClassDescriptor.Name, + StringRef()); + TypeIndex ClassIndex = TypeTable.writeKnownType(CR); + + UserDefinedTypes.push_back(std::make_pair(ClassDescriptor.Name, ClassIndex)); + + return ClassIndex.getIndex(); +} + +unsigned UserDefinedTypesBuilder::GetPointerTypeIndex(const PointerTypeDescriptor& PointerDescriptor) +{ + uint32_t elementType = PointerDescriptor.ElementType; + PointerKind pointerKind = PointerDescriptor.Is64Bit ? PointerKind::Near64 : PointerKind::Near32; + PointerMode pointerMode = PointerDescriptor.IsReference ? PointerMode::LValueReference : PointerMode::Pointer; + PointerOptions pointerOptions = PointerDescriptor.IsConst ? PointerOptions::Const : PointerOptions::None; + + PointerRecord PointerToClass(TypeIndex(elementType), pointerKind, pointerMode, pointerOptions, 0); + TypeIndex PointerIndex = TypeTable.writeKnownType(PointerToClass); + return PointerIndex.getIndex(); +} + +unsigned UserDefinedTypesBuilder::GetMemberFunctionTypeIndex(const MemberFunctionTypeDescriptor& MemberDescriptor, + uint32_t const *const ArgumentTypes) +{ + std::vector argumentTypes; + argumentTypes.reserve(MemberDescriptor.NumberOfArguments); + for (uint16_t iArgument = 0; iArgument < MemberDescriptor.NumberOfArguments; iArgument++) + { + argumentTypes.emplace_back(ArgumentTypes[iArgument]); + } + + ArgListRecord ArgList(TypeRecordKind::ArgList, argumentTypes); + TypeIndex ArgumentList = TypeTable.writeKnownType(ArgList); + + MemberFunctionRecord MemberFunction(TypeIndex(MemberDescriptor.ReturnType), + TypeIndex(MemberDescriptor.ContainingClass), + TypeIndex(MemberDescriptor.TypeIndexOfThisPointer), + CallingConvention(MemberDescriptor.CallingConvention), + FunctionOptions::None, MemberDescriptor.NumberOfArguments, + ArgumentList, + MemberDescriptor.ThisAdjust); + + TypeIndex MemberFunctionIndex = TypeTable.writeKnownType(MemberFunction); + return MemberFunctionIndex.getIndex(); +} + +unsigned UserDefinedTypesBuilder::GetMemberFunctionId(const MemberFunctionIdTypeDescriptor& MemberIdDescriptor) +{ + MemberFuncIdRecord MemberFuncId(TypeIndex(MemberIdDescriptor.MemberFunction), TypeIndex(MemberIdDescriptor.ParentClass), MemberIdDescriptor.Name); + TypeIndex MemberFuncIdIndex = TypeTable.writeKnownType(MemberFuncId); + return MemberFuncIdIndex.getIndex(); +} + +void UserDefinedTypesBuilder::AddBaseClass(FieldListRecordBuilder &FLBR, + unsigned BaseClassId) { + MemberAttributes def; + TypeIndex BaseTypeIndex(BaseClassId); + BaseClassRecord BCR(def, BaseTypeIndex, 0); + FLBR.writeMemberType(BCR); +} + +void UserDefinedTypesBuilder::AddClassVTShape(FieldListRecordBuilder &FLBR) { + VFPtrRecord VfPtr(ClassVTableTypeIndex); + FLBR.writeMemberType(VfPtr); +} + +const char *ArrayDimensionsDescriptor::GetLengthName(unsigned index) { + if (Lengths.size() <= index) { + Resize(index + 1); + } + return Lengths[index].c_str(); +} + +const char *ArrayDimensionsDescriptor::GetBoundsName(unsigned index) { + if (Bounds.size() <= index) { + Resize(index); + } + return Bounds[index].c_str(); +} + +void ArrayDimensionsDescriptor::Resize(unsigned NewSize) { + unsigned OldSize = Lengths.size(); + assert(OldSize == Bounds.size()); + Lengths.resize(NewSize); + Bounds.resize(NewSize); + for (unsigned i = OldSize; i < NewSize; ++i) { + std::stringstream ss; + ss << "length" << i; + ss >> Lengths[i]; + ss.clear(); + ss << "bounds" << i; + ss >> Bounds[i]; + } +} diff --git a/external/corert/src/Native/ObjWriter/typeBuilder.h b/external/corert/src/Native/ObjWriter/typeBuilder.h new file mode 100644 index 0000000000..a5037b7690 --- /dev/null +++ b/external/corert/src/Native/ObjWriter/typeBuilder.h @@ -0,0 +1,145 @@ +//===---- typeBuilder.h --------------------------------*- C++ -*-===// +// +// type builder is used to convert .Net types into CodeView descriptors. +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. +// See LICENSE file in the project root for full license information. +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" +#include "llvm/MC/MCObjectStreamer.h" + +#include +#include + +using namespace llvm; +using namespace llvm::codeview; + +typedef unsigned long long uint64; + +#pragma pack(push, 8) + +extern "C" struct EnumRecordTypeDescriptor { + uint64 Value; + char *Name; +}; + +extern "C" struct EnumTypeDescriptor { + uint32_t ElementType; + uint64 ElementCount; + char *Name; +}; + +extern "C" struct ClassTypeDescriptor { + int32_t IsStruct; + char *Name; + uint32_t BaseClassId; +}; + +extern "C" struct DataFieldDescriptor { + uint32_t FieldTypeIndex; + uint64 Offset; + char *Name; +}; + +extern "C" struct ClassFieldsTypeDescriptior { + uint64 Size; + int32_t FieldsCount; +}; + +extern "C" struct ArrayTypeDescriptor { + uint32_t Rank; + uint32_t ElementType; + uint32_t Size; + int32_t IsMultiDimensional; +}; + +extern "C" struct PointerTypeDescriptor { + uint32_t ElementType; + int32_t IsReference; + int32_t IsConst; + int32_t Is64Bit; +}; + +extern "C" struct MemberFunctionTypeDescriptor { + uint32_t ReturnType; + uint32_t ContainingClass; + uint32_t TypeIndexOfThisPointer; + int32_t ThisAdjust; + uint32_t CallingConvention; + uint16_t NumberOfArguments; +}; + +extern "C" struct MemberFunctionIdTypeDescriptor { + uint32_t MemberFunction; + uint32_t ParentClass; + char *Name; +}; + +class ArrayDimensionsDescriptor { +public: + const char *GetLengthName(unsigned index); + const char *GetBoundsName(unsigned index); + +private: + void Resize(unsigned NewSize); + + std::vector Lengths; + std::vector Bounds; +}; + +#pragma pack(pop) +class UserDefinedTypesBuilder { +public: + UserDefinedTypesBuilder(); + void SetStreamer(MCObjectStreamer *Streamer); + void SetTargetPointerSize(unsigned TargetPointerSize); + void EmitTypeInformation(MCSection *COFFDebugTypesSection); + + unsigned GetEnumTypeIndex(const EnumTypeDescriptor &TypeDescriptor, + const EnumRecordTypeDescriptor *TypeRecords); + unsigned GetClassTypeIndex(const ClassTypeDescriptor &ClassDescriptor); + unsigned GetCompleteClassTypeIndex( + const ClassTypeDescriptor &ClassDescriptor, + const ClassFieldsTypeDescriptior &ClassFieldsDescriptor, + const DataFieldDescriptor *FieldsDescriptors); + + unsigned GetArrayTypeIndex(const ClassTypeDescriptor &ClassDescriptor, + const ArrayTypeDescriptor &ArrayDescriptor); + + unsigned GetPointerTypeIndex(const PointerTypeDescriptor& PointerDescriptor); + + unsigned GetMemberFunctionTypeIndex(const MemberFunctionTypeDescriptor& MemberDescriptor, + uint32_t const *const ArgumentTypes); + + unsigned GetMemberFunctionId(const MemberFunctionIdTypeDescriptor& MemberIdDescriptor); + + const std::vector> &GetUDTs() { + return UserDefinedTypes; + } + +private: + void EmitCodeViewMagicVersion(); + ClassOptions GetCommonClassOptions(); + + unsigned GetEnumFieldListType(uint64 Count, + const EnumRecordTypeDescriptor *TypeRecords); + + void AddBaseClass(FieldListRecordBuilder &FLBR, unsigned BaseClassId); + void AddClassVTShape(FieldListRecordBuilder &FLBR); + + BumpPtrAllocator Allocator; + TypeTableBuilder TypeTable; + + MCObjectStreamer *Streamer; + unsigned TargetPointerSize; + + ArrayDimensionsDescriptor ArrayDimentions; + TypeIndex ClassVTableTypeIndex; + + std::vector> UserDefinedTypes; +}; diff --git a/external/corert/src/Native/Runtime/AsmOffsets.h b/external/corert/src/Native/Runtime/AsmOffsets.h index 71356f6f28..299454a287 100644 --- a/external/corert/src/Native/Runtime/AsmOffsets.h +++ b/external/corert/src/Native/Runtime/AsmOffsets.h @@ -30,6 +30,11 @@ ASM_OFFSET( 0, 0, Object, m_pEEType) ASM_OFFSET( 4, 8, Array, m_Length) +ASM_OFFSET( 4, 8, String, m_Length) +ASM_OFFSET( 8, C, String, m_FirstChar) +ASM_CONST( 2, 2, STRING_COMPONENT_SIZE) +ASM_CONST( E, 16, STRING_BASE_SIZE) + ASM_OFFSET( 0, 0, EEType, m_usComponentSize) ASM_OFFSET( 2, 2, EEType, m_usFlags) ASM_OFFSET( 4, 4, EEType, m_uBaseSize) @@ -46,14 +51,13 @@ ASM_OFFSET( 30, 48, Thread, m_pHackPInvokeTunnel) ASM_OFFSET( 40, 68, Thread, m_ppvHijackedReturnAddressLocation) ASM_OFFSET( 44, 70, Thread, m_pvHijackedReturnAddress) ASM_OFFSET( 48, 78, Thread, m_pExInfoStackHead) +ASM_OFFSET( 4c, 80, Thread, m_threadAbortException) ASM_SIZEOF( 14, 20, EHEnum) ASM_OFFSET( 0, 0, gc_alloc_context, alloc_ptr) ASM_OFFSET( 4, 8, gc_alloc_context, alloc_limit) -ASM_OFFSET( 4, 8, RuntimeInstance, m_pThreadStore) - #ifdef FEATURE_CACHED_INTERFACE_DISPATCH ASM_OFFSET( 4, 8, InterfaceDispatchCell, m_pCache) #ifndef BIT64 @@ -109,6 +113,10 @@ private: void BogusFunction() { // Sample usage to generate the error - FindCompileTimeConstant bogus_variable; + FindCompileTimeConstant bogus_variable; + FindCompileTimeConstant bogus_variable2; + FindCompileTimeConstant bogus_variable3; + FindCompileTimeConstant bogus_variable4; + FindCompileTimeConstant bogus_variable5; } #endif // defined(__cplusplus) && defined(USE_COMPILE_TIME_CONSTANT_FINDER) diff --git a/external/corert/src/Native/Runtime/AsmOffsetsVerify.cpp b/external/corert/src/Native/Runtime/AsmOffsetsVerify.cpp index a6e8127a70..2e7343ce1e 100644 --- a/external/corert/src/Native/Runtime/AsmOffsetsVerify.cpp +++ b/external/corert/src/Native/Runtime/AsmOffsetsVerify.cpp @@ -25,6 +25,10 @@ class AsmOffsets { static_assert(sizeof(Thread::m_rgbAllocContextBuffer) >= sizeof(gc_alloc_context), "Thread::m_rgbAllocContextBuffer is not big enough to hold a gc_alloc_context"); + // Some assembly helpers for arrays and strings are shared and use the fact that arrays and strings have similar layouts) + static_assert(offsetof(Array, m_Length) == offsetof(String, m_Length), "The length field of String and Array have different offsets"); + static_assert(sizeof(((Array*)0)->m_Length) == sizeof(((String*)0)->m_Length), "The length field of String and Array have different sizes"); + #define PLAT_ASM_OFFSET(offset, cls, member) \ static_assert((offsetof(cls, member) == 0x##offset) || (offsetof(cls, member) > 0x##offset), "Bad asm offset for '" #cls "." #member "', the actual offset is smaller than 0x" #offset "."); \ static_assert((offsetof(cls, member) == 0x##offset) || (offsetof(cls, member) < 0x##offset), "Bad asm offset for '" #cls "." #member "', the actual offset is larger than 0x" #offset "."); diff --git a/external/corert/src/Native/Runtime/CMakeLists.txt b/external/corert/src/Native/Runtime/CMakeLists.txt index 2cc88397f6..1cd4cd9497 100644 --- a/external/corert/src/Native/Runtime/CMakeLists.txt +++ b/external/corert/src/Native/Runtime/CMakeLists.txt @@ -6,12 +6,10 @@ set(COMMON_RUNTIME_SOURCES DebugEventSource.cpp DebugFuncEval.cpp DebuggerHook.cpp - dllmain.cpp eetype.cpp EHHelpers.cpp event.cpp FinalizerHelpers.cpp - gcdump.cpp GCHelpers.cpp gcheaputilities.cpp GCMemoryHelpers.cpp @@ -22,14 +20,12 @@ set(COMMON_RUNTIME_SOURCES HandleTableHelpers.cpp MathHelpers.cpp MiscHelpers.cpp - module.cpp TypeManager.cpp ObjectLayout.cpp OptionalFieldsRuntime.cpp portable.cpp profheapwalkhelper.cpp RestrictedCallouts.cpp - RHCodeMan.cpp RhConfig.cpp RuntimeInstance.cpp RWLock.cpp @@ -43,10 +39,17 @@ set(COMMON_RUNTIME_SOURCES thread.cpp threadstore.cpp UniversalTransitionHelpers.cpp - + + # ProjectN only + # gcdump.cpp + # module.cpp + # RHCodeMan.cpp + ../gc/gccommon.cpp ../gc/gceewks.cpp ../gc/gcwks.cpp + ../gc/gceesvr.cpp + ../gc/gcsvr.cpp ../gc/gcscan.cpp ../gc/handletable.cpp ../gc/handletablecache.cpp @@ -77,23 +80,34 @@ if(WIN32) windows/PalRedhawkMinWin.cpp ) - list(APPEND FULL_RUNTIME_SOURCES + list(APPEND FULL_RUNTIME_SOURCES windows/CoffNativeCodeManager.cpp ) + set(ASM_SUFFIX asm) + if(CLR_CMAKE_PLATFORM_ARCH_AMD64) set(ARCH_SOURCES_DIR amd64) - set(ASM_SUFFIX asm) + elseif(CLR_CMAKE_PLATFORM_ARCH_I386) + set(ARCH_SOURCES_DIR i386) endif() list(APPEND RUNTIME_SOURCES_ARCH_ASM ${ARCH_SOURCES_DIR}/GC.${ASM_SUFFIX} + ${ARCH_SOURCES_DIR}/GcProbe.${ASM_SUFFIX} ) else() include_directories(unix) + if(NOT CLR_CMAKE_PLATFORM_ARCH_WASM) + include_directories(../libunwind/include) + else() + include_directories($ENV{EMSCRIPTEN/system/lib/libcxxabi/include}) + endif() + include_directories(../libunwind/include) + include_directories(../libunwind) # Disable building _Unwind_XXX style APIs of libunwind, since we don't use them. add_definitions(-D_LIBUNWIND_DISABLE_ZERO_COST_APIS=1) @@ -108,6 +122,7 @@ else() list(APPEND FULL_RUNTIME_SOURCES unix/HardwareExceptions.cpp unix/UnixContext.cpp + unix/UnwindHelpers.cpp unix/UnixNativeCodeManager.cpp ../libunwind/src/Unwind-EHABI.cpp ../libunwind/src/libunwind.cpp @@ -119,15 +134,17 @@ else() ) endif() + set(ASM_SUFFIX S) if(CLR_CMAKE_PLATFORM_ARCH_AMD64) set(ARCH_SOURCES_DIR amd64) - set(ASM_SUFFIX S) elseif(CLR_CMAKE_PLATFORM_ARCH_ARM64) set(ARCH_SOURCES_DIR arm64) - set(ASM_SUFFIX S) elseif(CLR_CMAKE_PLATFORM_ARCH_ARM) set(ARCH_SOURCES_DIR arm) - set(ASM_SUFFIX S) + elseif(CLR_CMAKE_PLATFORM_ARCH_I386) + set(ARCH_SOURCES_DIR i386) + elseif(CLR_CMAKE_PLATFORM_ARCH_WASM) + set(ARCH_SOURCES_DIR wasm) endif() list(APPEND RUNTIME_SOURCES_ARCH_ASM @@ -143,7 +160,7 @@ list(APPEND RUNTIME_SOURCES_ARCH_ASM ${ARCH_SOURCES_DIR}/ExceptionHandling.${ASM_SUFFIX} ${ARCH_SOURCES_DIR}/Interlocked.${ASM_SUFFIX} ${ARCH_SOURCES_DIR}/PInvoke.${ASM_SUFFIX} - ${ARCH_SOURCES_DIR}/InteropThunksHelpers.${ASM_SUFFIX} + ${ARCH_SOURCES_DIR}/InteropThunksHelpers.${ASM_SUFFIX} ${ARCH_SOURCES_DIR}/StubDispatch.${ASM_SUFFIX} ${ARCH_SOURCES_DIR}/UniversalTransition.${ASM_SUFFIX} ${ARCH_SOURCES_DIR}/WriteBarriers.${ASM_SUFFIX} @@ -154,6 +171,7 @@ convert_to_absolute_path(ARCH_SOURCES_DIR ${ARCH_SOURCES_DIR}) include_directories(${ARCH_SOURCES_DIR}) add_definitions(-DFEATURE_BACKGROUND_GC) +add_definitions(-DFEATURE_SVR_GC) add_definitions(-DFEATURE_BASICFREEZE) add_definitions(-DFEATURE_CONSERVATIVE_GC) add_definitions(-DFEATURE_CUSTOM_IMPORTS) @@ -206,5 +224,8 @@ convert_to_absolute_path(PORTABLE_RUNTIME_SOURCES ${PORTABLE_RUNTIME_SOURCES}) convert_to_absolute_path(RUNTIME_SOURCES_ARCH_ASM ${RUNTIME_SOURCES_ARCH_ASM}) -add_subdirectory(Full) +if(NOT CLR_CMAKE_PLATFORM_ARCH_WASM) + add_subdirectory(Full) +endif() + add_subdirectory(Portable) diff --git a/external/corert/src/Native/Runtime/CachedInterfaceDispatch.cpp b/external/corert/src/Native/Runtime/CachedInterfaceDispatch.cpp index e1ce01f8ee..c49d13129e 100644 --- a/external/corert/src/Native/Runtime/CachedInterfaceDispatch.cpp +++ b/external/corert/src/Native/Runtime/CachedInterfaceDispatch.cpp @@ -86,29 +86,7 @@ static void * UpdatePointerPairAtomically(void * pPairLocation, void * pSecondPointer, bool fFailOnNonNull) { -#if defined(_X86_) || defined(_ARM_) - // Stuff the two pointers into a 64-bit value as the proposed new value for the CompareExchange64 below. - Int64 iNewValue = (Int64)((UInt64)(UIntNative)pFirstPointer | ((UInt64)(UIntNative)pSecondPointer << 32)); - - // Read the old value in the location. If fFailOnNonNull is set we just assume this was zero and we'll - // fail below if that's not the case. - Int64 iOldValue = fFailOnNonNull ? 0 : *(Int64 volatile *)pPairLocation; - - Int64 iUpdatedOldValue = PalInterlockedCompareExchange64((Int64*)pPairLocation, iNewValue, iOldValue); - if (iUpdatedOldValue == iOldValue) - { - // Successful update. Return the previous value of the second pointer. For cache entry updates - // (fFailOnNonNull == true) this is guaranteed to be NULL in this case and the result being being - // NULL in the success case is all the caller cares about. For indirection cell updates the second - // pointer represents the old cache and the caller needs this data so they can schedule the cache - // for deletion once it becomes safe to do so. - return (void*)(UInt32)(iOldValue >> 32); - } - - // The update failed due to a racing update to the same location. Return the new value of the second - // pointer (either a new cache that lost the race or a non-NULL pointer in the cache entry update case). - return pSecondPointer; -#elif defined(_AMD64_) +#if defined(BIT64) // The same comments apply to the AMD64 version. The CompareExchange looks a little different since the // API was refactored in terms of Int64 to avoid creating a 128-bit integer type. @@ -130,8 +108,28 @@ static void * UpdatePointerPairAtomically(void * pPairLocation, // Failure, return the new second pointer value. return pSecondPointer; #else -#error Unsupported architecture -#endif + // Stuff the two pointers into a 64-bit value as the proposed new value for the CompareExchange64 below. + Int64 iNewValue = (Int64)((UInt64)(UIntNative)pFirstPointer | ((UInt64)(UIntNative)pSecondPointer << 32)); + + // Read the old value in the location. If fFailOnNonNull is set we just assume this was zero and we'll + // fail below if that's not the case. + Int64 iOldValue = fFailOnNonNull ? 0 : *(Int64 volatile *)pPairLocation; + + Int64 iUpdatedOldValue = PalInterlockedCompareExchange64((Int64*)pPairLocation, iNewValue, iOldValue); + if (iUpdatedOldValue == iOldValue) + { + // Successful update. Return the previous value of the second pointer. For cache entry updates + // (fFailOnNonNull == true) this is guaranteed to be NULL in this case and the result being being + // NULL in the success case is all the caller cares about. For indirection cell updates the second + // pointer represents the old cache and the caller needs this data so they can schedule the cache + // for deletion once it becomes safe to do so. + return (void*)(UInt32)(iOldValue >> 32); + } + + // The update failed due to a racing update to the same location. Return the new value of the second + // pointer (either a new cache that lost the race or a non-NULL pointer in the cache entry update case). + return pSecondPointer; +#endif // BIT64 } // Helper method for updating an interface dispatch cache entry atomically. See comments by the usage of @@ -188,12 +186,12 @@ static InterfaceDispatchCache * UpdateCellStubAndCache(InterfaceDispatchCell * p // any more) we can place them on one of several free lists based on their size. // -#ifdef _AMD64_ +#if defined(_AMD64_) || defined(_ARM64_) // Head of the list of discarded cache blocks that can't be re-used just yet. -InterfaceDispatchCache * g_pDiscardedCacheList; // for AMD64, m_pCell is not used and we can link the discarded blocks themselves +InterfaceDispatchCache * g_pDiscardedCacheList; // for AMD64 and ARM64, m_pCell is not used and we can link the discarded blocks themselves -#else // ifdef _AMD64_ +#else // defined(_AMD64_) || defined(_ARM64_) struct DiscardedCacheBlock { @@ -207,7 +205,7 @@ static DiscardedCacheBlock * g_pDiscardedCacheList = NULL; // Free list of DiscardedCacheBlock items static DiscardedCacheBlock * g_pDiscardedCacheFree = NULL; -#endif // ifdef _AMD64_ +#endif // defined(_AMD64_) || defined(_ARM64_) // Free lists for each cache size up to the maximum. We allocate from these in preference to new memory. static InterfaceDispatchCache * g_rgFreeLists[CID_MAX_CACHE_SIZE_LOG2 + 1]; @@ -352,13 +350,13 @@ static void DiscardCache(InterfaceDispatchCache * pCache) CrstHolder lh(&g_sListLock); -#ifdef _AMD64_ +#if defined(_AMD64_) || defined(_ARM64_) - // on AMD64, we can thread the list through the blocks directly + // on AMD64 and ARM64, we can thread the list through the blocks directly pCache->m_pNextFree = g_pDiscardedCacheList; g_pDiscardedCacheList = pCache; -#else // _AMD64_ +#else // defined(_AMD64_) || defined(_ARM64_) // on other architectures, we cannot overwrite pCache->m_pNextFree yet // because it shares storage with m_pCell which may still be used as a back @@ -378,7 +376,7 @@ static void DiscardCache(InterfaceDispatchCache * pCache) g_pDiscardedCacheList = pDiscardedCacheBlock; } -#endif // _AMD64_ +#endif // defined(_AMD64_) || defined(_ARM64_) } // Called during a GC to empty the list of discarded caches (which we can now guarantee aren't being accessed) @@ -388,7 +386,7 @@ void ReclaimUnusedInterfaceDispatchCaches() // No need for any locks, we're not racing with any other threads any more. // Walk the list of discarded caches. -#ifdef _AMD64_ +#if defined(_AMD64_) || defined(_ARM64_) // on AMD64, this is threaded directly through the cache blocks InterfaceDispatchCache * pCache = g_pDiscardedCacheList; @@ -406,7 +404,7 @@ void ReclaimUnusedInterfaceDispatchCaches() pCache = pNextCache; } -#else // _AMD64_ +#else // defined(_AMD64_) || defined(_ARM64_) // on other architectures, we use an auxiliary list instead DiscardedCacheBlock * pDiscardedCacheBlock = g_pDiscardedCacheList; @@ -428,7 +426,7 @@ void ReclaimUnusedInterfaceDispatchCaches() pDiscardedCacheBlock = pNextDiscardedCacheBlock; } -#endif // _AMD64_ +#endif // defined(_AMD64_) || defined(_ARM64_) // We processed all the discarded entries, so we can simply NULL the list head. g_pDiscardedCacheList = NULL; @@ -498,11 +496,11 @@ COOP_PINVOKE_HELPER(PTR_Code, RhpUpdateDispatchCellCache, (InterfaceDispatchCell if (InterfaceDispatchCell::IsCache(newCacheValue)) { pCache = (InterfaceDispatchCache*)newCacheValue; -#ifndef _AMD64_ - // Set back pointer to interface dispatch cell for non-AMD64 - // for AMD64, we have enough registers to make this trick unnecessary +#if !defined(_AMD64_) && !defined(_ARM64_) + // Set back pointer to interface dispatch cell for non-AMD64 and non-ARM64 + // for AMD64 and ARM64, we have enough registers to make this trick unnecessary pCache->m_pCell = pCell; -#endif // _AMD64_ +#endif // !defined(_AMD64_) && !defined(_ARM64_) // Add entry to the first unused slot. InterfaceDispatchCacheEntry * pCacheEntry = &pCache->m_rgEntries[cOldCacheEntries]; diff --git a/external/corert/src/Native/Runtime/CommonMacros.h b/external/corert/src/Native/Runtime/CommonMacros.h index 8fc0a5a668..94c1e2d3ad 100644 --- a/external/corert/src/Native/Runtime/CommonMacros.h +++ b/external/corert/src/Native/Runtime/CommonMacros.h @@ -5,15 +5,6 @@ #ifndef __COMMONMACROS_H__ #define __COMMONMACROS_H__ -// Some of our header files are shared with the binder, which needs the _TARGET_* macros defined -#if defined(_TARGET_AMD64_) -#elif defined(_TARGET_X86_) -#elif defined(_TARGET_ARM_) -#elif defined(_TARGET_ARM64_) -#else -#error Unsupported architecture -#endif - #define EXTERN_C extern "C" #define FASTCALL __fastcall #define STDCALL __stdcall @@ -127,6 +118,11 @@ EXTERN_C int __cdecl memcmp(const void *,const void *,size_t); #define LOG2_PTRSIZE 3 #define POINTER_SIZE 8 +#elif defined (_WASM_) + +#define LOG2_PTRSIZE 2 +#define POINTER_SIZE 4 + #else #error Unsupported target architecture #endif @@ -158,6 +154,13 @@ EXTERN_C int __cdecl memcmp(const void *,const void *,size_t); #define OS_PAGE_SIZE 0x1000 #endif +#elif defined(_WASM_) + +#define DATA_ALIGNMENT 4 +#ifndef OS_PAGE_SIZE +#define OS_PAGE_SIZE 0x4 +#endif + #else #error Unsupported target architecture #endif @@ -233,4 +236,4 @@ typedef int32_t HRESULT; #endif // !defined(_INC_WINDOWS) && !defined(BINDER) #endif // GCENV_INCLUDED -#endif // __COMMONMACROS_H__ \ No newline at end of file +#endif // __COMMONMACROS_H__ diff --git a/external/corert/src/Native/Runtime/Debug.h b/external/corert/src/Native/Runtime/Debug.h index 926e8fc4cb..481a607fea 100644 --- a/external/corert/src/Native/Runtime/Debug.h +++ b/external/corert/src/Native/Runtime/Debug.h @@ -4,29 +4,99 @@ #pragma once -enum DebuggerGcProtectionMessage : uint32_t +// The following definitions are required for interop with the VS Debugger +// Prior to making any changes to these, please reach out to the VS Debugger +// team to make sure that your changes are not going to prevent the debugger +// from working. + +enum FuncEvalEntryPointMode : uint32_t { - RequestBufferReady = 2, - ConservativeReportingBufferReady = 3, + FixedAddress = 0, + VirtualMethodSlotOnly = 1, + InterfaceDispatch = 2, +}; + +enum FuncEvalMode : uint32_t +{ + CallParameterizedFunction = 1, + NewStringWithLength = 2, + NewParameterizedArray = 3, + NewParameterizedObjectNoConstructor = 4, + NewParameterizedObject = 5, }; enum DebuggerGcProtectionRequestKind : uint16_t { EnsureConservativeReporting = 1, RemoveConservativeReporting = 2, + EnsureHandle = 3, + RemoveHandle = 4 }; -struct GcProtectionMessage +/** + * This structure represents a request from the debugger to perform a GC protection related work. + */ +struct DebuggerGcProtectionRequest { - DebuggerGcProtectionMessage commandCode; - uint32_t unused; /* To make the data structure 64 bit aligned */ + DebuggerGcProtectionRequestKind kind; + union + { + uint16_t size; + uint16_t type; + }; + uint32_t identifier; + uint64_t address; + uint64_t payload; /* TODO, FuncEval, what would be a better name for this? */ +}; + +enum DebuggerResponseKind : uint32_t +{ + FuncEvalCompleteWithReturn = 0, + FuncEvalCompleteWithException = 1, + FuncEvalParameterBufferReady = 2, + RequestBufferReady = 3, + ConservativeReportingBufferReady = 4, + HandleReady = 5, + FuncEvalCrossThreadDependency = 6, +}; + +struct DebuggerResponse +{ + DebuggerResponseKind kind; +}; + +struct DebuggerGcProtectionResponse +{ + DebuggerResponseKind kind; + uint32_t padding; uint64_t bufferAddress; }; -struct GcProtectionRequest +struct DebuggerGcProtectionHandleReadyResponse { - DebuggerGcProtectionRequestKind kind; - uint16_t size; - uint32_t identifier; - uint64_t address; + DebuggerResponseKind kind; + uint32_t padding; + uint64_t payload; + uint64_t handle; +}; + +struct DebuggerFuncEvalCompleteWithReturnResponse +{ + DebuggerResponseKind kind; + uint32_t returnHandleIdentifier; + uint64_t returnAddress; +}; + +struct DebuggerFuncEvalParameterBufferReadyResponse +{ + DebuggerResponseKind kind; + uint32_t padding; + uint64_t bufferAddress; +}; + +struct DebuggerFuncEvalCrossThreadDependencyNotification +{ + DebuggerResponseKind kind; + uint32_t padding; + uint64_t payload; }; diff --git a/external/corert/src/Native/Runtime/DebugFuncEval.cpp b/external/corert/src/Native/Runtime/DebugFuncEval.cpp index 75ed8e808c..ae920a46a4 100644 --- a/external/corert/src/Native/Runtime/DebugFuncEval.cpp +++ b/external/corert/src/Native/Runtime/DebugFuncEval.cpp @@ -6,34 +6,36 @@ #include "CommonTypes.h" #include "DebugFuncEval.h" -GVAL_IMPL_INIT(UInt64, g_FuncEvalTarget, 0); +GVAL_IMPL_INIT(UInt32, g_FuncEvalMode, 0); GVAL_IMPL_INIT(UInt32, g_FuncEvalParameterBufferSize, 0); +GVAL_IMPL_INIT(UInt64, g_MostRecentFuncEvalHijackInstructionPointer, 0); +GPTR_IMPL_INIT(PTR_VOID, g_HighLevelDebugFuncEvalAbortHelperAddr, 0); #ifndef DACCESS_COMPILE -void* DebugFuncEval::GetFuncEvalTarget() -{ - return (void*)g_FuncEvalTarget; -} - -UInt32 DebugFuncEval::GetFuncEvalParameterBufferSize() +/* static */ UInt32 DebugFuncEval::GetFuncEvalParameterBufferSize() { return g_FuncEvalParameterBufferSize; } -/// -/// Retrieve the global FuncEval target address. -/// -/// -/// During debugging, if a FuncEval is requested, -/// the func eval infrastructure needs to know which function to call, and -/// the C# supporting code will call this API to obtain the FuncEval target address. -/// By that time, the value should have been set through the UpdateFuncEvalTarget() method -/// on the ISosRedhawk7 interface. -/// -EXTERN_C REDHAWK_API void* __cdecl RhpGetFuncEvalTargetAddress() +/* static */ UInt32 DebugFuncEval::GetFuncEvalMode() { - return DebugFuncEval::GetFuncEvalTarget(); + return g_FuncEvalMode; +} + +/* static */ UInt64 DebugFuncEval::GetMostRecentFuncEvalHijackInstructionPointer() +{ + return g_MostRecentFuncEvalHijackInstructionPointer; +} + +/* static */ HighLevelDebugFuncEvalAbortHelperType DebugFuncEval::GetHighLevelDebugFuncEvalAbortHelper() +{ + return (HighLevelDebugFuncEvalAbortHelperType)g_HighLevelDebugFuncEvalAbortHelperAddr; +} + +/* static */ void DebugFuncEval::SetHighLevelDebugFuncEvalAbortHelper(HighLevelDebugFuncEvalAbortHelperType highLevelDebugFuncEvalAbortHelper) +{ + g_HighLevelDebugFuncEvalAbortHelperAddr = (PTR_PTR_VOID)highLevelDebugFuncEvalAbortHelper; } /// @@ -43,7 +45,7 @@ EXTERN_C REDHAWK_API void* __cdecl RhpGetFuncEvalTargetAddress() /// During debugging, if a FuncEval is requested, /// the func eval infrastructure needs to know how much buffer to allocate for the debugger to /// write the parameter information in. The C# supporting code will call this API to obtain the -/// buffer size. By that time, the value should have been set through the UpdateFuncEvalParameterSize() +/// buffer size. By that time, the value should have been set through the UpdateFuncEvalParameterBufferSize() /// method on the ISosRedhawk7 interface. /// EXTERN_C REDHAWK_API UInt32 __cdecl RhpGetFuncEvalParameterBufferSize() @@ -51,4 +53,54 @@ EXTERN_C REDHAWK_API UInt32 __cdecl RhpGetFuncEvalParameterBufferSize() return DebugFuncEval::GetFuncEvalParameterBufferSize(); } +/// +/// Retrieve the global FuncEval mode. +/// +/// +/// During debugging, if a FuncEval is requested, +/// the func eval infrastructure needs to know what mode to execute the FuncEval request +/// The C# supporting code will call this API to obtain the mode. By that time, the value +/// should have been set through the UpdateFuncEvalMode() method on the ISosRedhawk7 interface. +/// +EXTERN_C REDHAWK_API UInt32 __cdecl RhpGetFuncEvalMode() +{ + return DebugFuncEval::GetFuncEvalMode(); +} + +/// +/// Initiate the func eval abort +/// +/// +/// This is the entry point of FuncEval abort +/// When the debugger decides to abort the FuncEval, it will create a remote thread calling this function. +/// This function will call back into the highLevelDebugFuncEvalAbortHelper to perform the abort. +EXTERN_C REDHAWK_API void __cdecl RhpInitiateFuncEvalAbort(void* pointerFromDebugger) +{ + HighLevelDebugFuncEvalAbortHelperType highLevelDebugFuncEvalAbortHelper = DebugFuncEval::GetHighLevelDebugFuncEvalAbortHelper(); + highLevelDebugFuncEvalAbortHelper((UInt64)pointerFromDebugger); +} + +/// +/// Set the high level debug func eval abort helper +/// +/// +/// The high level debug func eval abort helper is a function that perform the actual func eval abort +/// It is implemented in System.Private.Debug.dll +EXTERN_C REDHAWK_API void __cdecl RhpSetHighLevelDebugFuncEvalAbortHelper(HighLevelDebugFuncEvalAbortHelperType highLevelDebugFuncEvalAbortHelper) +{ + DebugFuncEval::SetHighLevelDebugFuncEvalAbortHelper(highLevelDebugFuncEvalAbortHelper); +} + +#else + +UInt64 DebugFuncEval::GetMostRecentFuncEvalHijackInstructionPointer() +{ + return g_MostRecentFuncEvalHijackInstructionPointer; +} + #endif //!DACCESS_COMPILE + +EXTERN_C void * RhpDebugFuncEvalHelper; +GPTR_IMPL_INIT(PTR_VOID, g_RhpDebugFuncEvalHelperAddr, &RhpDebugFuncEvalHelper); + +GPTR_IMPL_INIT(PTR_VOID, g_RhpInitiateFuncEvalAbortAddr, (void**)&RhpInitiateFuncEvalAbort); \ No newline at end of file diff --git a/external/corert/src/Native/Runtime/DebugFuncEval.h b/external/corert/src/Native/Runtime/DebugFuncEval.h index e38d943009..20315c35a7 100644 --- a/external/corert/src/Native/Runtime/DebugFuncEval.h +++ b/external/corert/src/Native/Runtime/DebugFuncEval.h @@ -15,22 +15,11 @@ #ifndef DACCESS_COMPILE +typedef void(*HighLevelDebugFuncEvalAbortHelperType)(UInt64); + class DebugFuncEval { public: - /// - /// Retrieve the global FuncEval target address - /// - /// - /// During debugging, if a FuncEval is requested, - /// The func eval infrastructure needs to know which function to call, and - /// It will call this API to obtain the target address. - /// By the time, the value should have been set through the UpdateFuncEvalTarget() method - /// on the ISosRedhawk7 interface. - /// - static void* GetFuncEvalTarget(); - - /// /// Retrieve the global FuncEval parameter buffer size. /// @@ -42,6 +31,53 @@ public: /// method on the ISosRedhawk7 interface. /// static UInt32 GetFuncEvalParameterBufferSize(); + + /// + /// Retrieve the global FuncEval mode. + /// + /// + /// During debugging, if a FuncEval is requested, + /// the func eval infrastructure needs to know what mode to execute the FuncEval request + /// The C# supporting code will call this API to obtain the mode. By that time, the value + /// should have been set through the UpdateFuncEvalMode() method on the ISosRedhawk7 interface. + /// + static UInt32 GetFuncEvalMode(); + + /// + /// Retrieve the most recent FuncEval Hijack instruction pointer + /// + /// + /// The most recent FuncEval Hijack instruction pointer is set through the debugger + /// It is used for the stack walker to understand the hijack frame + /// + static UInt64 GetMostRecentFuncEvalHijackInstructionPointer(); + + /// + /// Retrieve the high level debug func eval abort helper + /// + static HighLevelDebugFuncEvalAbortHelperType GetHighLevelDebugFuncEvalAbortHelper(); + + + /// + /// Set the high level debug func eval abort helper + /// + static void SetHighLevelDebugFuncEvalAbortHelper(HighLevelDebugFuncEvalAbortHelperType highLevelDebugFuncEvalAbortHelper); + +}; + +#else + +class DebugFuncEval +{ +public: + /// + /// Retrieve the most recent FuncEval Hijack instruction pointer + /// + /// + /// The most recent FuncEval Hijack instruction pointer is set through the debugger + /// It is used for the stack walker to understand the hijack frame + /// + static UInt64 GetMostRecentFuncEvalHijackInstructionPointer(); }; #endif //!DACCESS_COMPILE diff --git a/external/corert/src/Native/Runtime/DebuggerHook.cpp b/external/corert/src/Native/Runtime/DebuggerHook.cpp index 08da252132..46db25c880 100644 --- a/external/corert/src/Native/Runtime/DebuggerHook.cpp +++ b/external/corert/src/Native/Runtime/DebuggerHook.cpp @@ -5,17 +5,18 @@ #include "common.h" #include "CommonTypes.h" #include "CommonMacros.h" +#include "daccess.h" +#include "gcrhinterface.h" #include "DebuggerHook.h" #include "DebugEventSource.h" -#include "Debug.h" GVAL_IMPL_INIT(UInt32, g_numGcProtectionRequests, 0); #ifndef DACCESS_COMPILE -/* static */ DebuggerProtectedBufferList* DebuggerHook::s_debuggerProtectedBuffers = nullptr; +/* static */ DebuggerProtectedBufferListNode* DebuggerHook::s_debuggerProtectedBuffers = nullptr; -/* static */ DebuggerOwnedHandleList* DebuggerHook::s_debuggerOwnedHandleList = nullptr; +/* static */ DebuggerOwnedHandleListNode* DebuggerHook::s_debuggerOwnedHandles = nullptr; /* static */ UInt32 DebuggerHook::s_debuggeeInitiatedHandleIdentifier = 2; @@ -25,13 +26,13 @@ GVAL_IMPL_INIT(UInt32, g_numGcProtectionRequests, 0); { // The debugger has some requests with respect to GC protection. // Here we are allocating a buffer to store them - GcProtectionRequest* requests = new (nothrow) GcProtectionRequest[g_numGcProtectionRequests]; + DebuggerGcProtectionRequest* requests = new (nothrow) DebuggerGcProtectionRequest[g_numGcProtectionRequests]; // Notifying the debugger the buffer is ready to use - GcProtectionMessage command; - command.commandCode = DebuggerGcProtectionMessage::RequestBufferReady; - command.bufferAddress = (uint64_t)requests; - DebugEventSource::SendCustomEvent((void*)&command, sizeof(command)); + DebuggerGcProtectionResponse response; + response.kind = DebuggerResponseKind::RequestBufferReady; + response.bufferAddress = (uint64_t)requests; + DebugEventSource::SendCustomEvent((void*)&response, sizeof(response)); // ... debugger magic happen here ... @@ -47,66 +48,37 @@ GVAL_IMPL_INIT(UInt32, g_numGcProtectionRequests, 0); } } - // TODO: Consider an optimization to eliminate this message when they is nothing required from the + // TODO, FuncEval, consider an optimization to eliminate this message when they is nothing required from the // debugger side to fill - command.commandCode = DebuggerGcProtectionMessage::ConservativeReportingBufferReady; - DebugEventSource::SendCustomEvent((void*)&command, sizeof(command)); + response.kind = DebuggerResponseKind::ConservativeReportingBufferReady; + DebugEventSource::SendCustomEvent((void*)&response, sizeof(response)); // ... debugger magic happen here again ... for (uint32_t i = 0; i < g_numGcProtectionRequests; i++) { - if (requests[i].kind == DebuggerGcProtectionRequestKind::EnsureConservativeReporting) + DebuggerGcProtectionRequest* request = requests + i; + switch(request->kind) { - DebuggerProtectedBufferList* tail = DebuggerHook::s_debuggerProtectedBuffers; - s_debuggerProtectedBuffers = new (std::nothrow) DebuggerProtectedBufferList(); - if (s_debuggerProtectedBuffers == nullptr) - { - // TODO: We cannot handle the debugger request to protect a buffer (we have to break our promise) - // TODO: We need to figure out how to communicate this broken promise to the debugger - } - else - { - s_debuggerProtectedBuffers->address = requests[i].address; - s_debuggerProtectedBuffers->size = requests[i].size; - s_debuggerProtectedBuffers->identifier = requests[i].identifier; - s_debuggerProtectedBuffers->next = tail; - } - } - else if (requests[i].kind == DebuggerGcProtectionRequestKind::RemoveConservativeReporting) - { - DebuggerProtectedBufferList* prev = nullptr; - DebuggerProtectedBufferList* curr = DebuggerHook::s_debuggerProtectedBuffers; - while (true) - { - if (curr == nullptr) - { - // The debugger is trying to remove a conservatively reported buffer that does not exist - break; - } - if (curr->identifier == requests[i].identifier) - { - DebuggerProtectedBufferList* toDelete = curr; - if (prev == nullptr) - { - // We are trying to remove the head of the linked list - DebuggerHook::s_debuggerProtectedBuffers = curr->next; - } - else - { - prev->next = curr->next; - } + case DebuggerGcProtectionRequestKind::EnsureConservativeReporting: + EnsureConservativeReporting(request); + break; - delete toDelete; - break; - } - else - { - prev = curr; - curr = curr->next; - } - } + case DebuggerGcProtectionRequestKind::RemoveConservativeReporting: + RemoveConservativeReporting(request); + break; + + case DebuggerGcProtectionRequestKind::EnsureHandle: + EnsureHandle(request); + break; + + case DebuggerGcProtectionRequestKind::RemoveHandle: + RemoveHandle(request); + break; + + default: + assert("Debugger is providing an invalid request kind." && false); } } @@ -116,7 +88,7 @@ GVAL_IMPL_INIT(UInt32, g_numGcProtectionRequests, 0); /* static */ UInt32 DebuggerHook::RecordDebuggeeInitiatedHandle(void* objectHandle) { - DebuggerOwnedHandleList* head = new (nothrow) DebuggerOwnedHandleList(); + DebuggerOwnedHandleListNode* head = new (nothrow) DebuggerOwnedHandleListNode(); if (head == nullptr) { return 0; @@ -124,17 +96,141 @@ GVAL_IMPL_INIT(UInt32, g_numGcProtectionRequests, 0); head->handle = objectHandle; head->identifier = DebuggerHook::s_debuggeeInitiatedHandleIdentifier; - head->next = s_debuggerOwnedHandleList; - s_debuggerOwnedHandleList = head; + head->next = s_debuggerOwnedHandles; + s_debuggerOwnedHandles = head; s_debuggeeInitiatedHandleIdentifier += 2; return head->identifier; } +/* static */ void DebuggerHook::EnsureConservativeReporting(DebuggerGcProtectionRequest* request) +{ + DebuggerProtectedBufferListNode* tail = DebuggerHook::s_debuggerProtectedBuffers; + s_debuggerProtectedBuffers = new (std::nothrow) DebuggerProtectedBufferListNode(); + if (s_debuggerProtectedBuffers == nullptr) + { + s_debuggerProtectedBuffers = tail; + // TODO, FuncEval, we cannot handle the debugger request to protect a buffer (we have to break our promise) + // TODO, FuncEval, we need to figure out how to communicate this broken promise to the debugger + } + else + { + s_debuggerProtectedBuffers->address = request->address; + s_debuggerProtectedBuffers->size = request->size; + s_debuggerProtectedBuffers->identifier = request->identifier; + s_debuggerProtectedBuffers->next = tail; + } +} + +/* static */ void DebuggerHook::RemoveConservativeReporting(DebuggerGcProtectionRequest* request) +{ + DebuggerProtectedBufferListNode* prev = nullptr; + DebuggerProtectedBufferListNode* curr = DebuggerHook::s_debuggerProtectedBuffers; + while (true) + { + if (curr == nullptr) + { + assert("Debugger is trying to remove a conservative reporting entry which is no longer exist." && false); + break; + } + if (curr->identifier == request->identifier) + { + DebuggerProtectedBufferListNode* toDelete = curr; + if (prev == nullptr) + { + // We are trying to remove the head of the linked list + DebuggerHook::s_debuggerProtectedBuffers = curr->next; + } + else + { + prev->next = curr->next; + } + + delete toDelete; + break; + } + else + { + prev = curr; + curr = curr->next; + } + } +} + +/* static */ void DebuggerHook::EnsureHandle(DebuggerGcProtectionRequest* request) +{ + DebuggerOwnedHandleListNode* tail = DebuggerHook::s_debuggerOwnedHandles; + s_debuggerOwnedHandles = new (std::nothrow) DebuggerOwnedHandleListNode(); + if (s_debuggerOwnedHandles == nullptr) + { + s_debuggerOwnedHandles = tail; + // TODO, FuncEval, we cannot handle the debugger request to protect a buffer (we have to break our promise) + // TODO, FuncEval, we need to figure out how to communicate this broken promise to the debugger + } + else + { + int handleType = (int)request->type; + void* handle = RedhawkGCInterface::CreateTypedHandle((void*)request->address, handleType); + + DebuggerGcProtectionHandleReadyResponse response; + response.kind = DebuggerResponseKind::HandleReady; + response.payload = request->payload; + response.handle = (uint64_t)handle; + DebugEventSource::SendCustomEvent((void*)&response, sizeof(response)); + + s_debuggerOwnedHandles->handle = handle; + s_debuggerOwnedHandles->identifier = request->identifier; + s_debuggerOwnedHandles->next = tail; + } +} + +/* static */ void DebuggerHook::RemoveHandle(DebuggerGcProtectionRequest* request) +{ + DebuggerOwnedHandleListNode* prev = nullptr; + DebuggerOwnedHandleListNode* curr = DebuggerHook::s_debuggerOwnedHandles; + while (true) + { + if (curr == nullptr) + { + assert("Debugger is trying to remove a gc handle entry which is no longer exist." && false); + break; + } + if (curr->identifier == request->identifier) + { + DebuggerOwnedHandleListNode* toDelete = curr; + RedhawkGCInterface::DestroyTypedHandle(toDelete->handle); + + if (prev == nullptr) + { + // We are trying to remove the head of the linked list + DebuggerHook::s_debuggerOwnedHandles = curr->next; + } + else + { + prev->next = curr->next; + } + + delete toDelete; + break; + } + else + { + prev = curr; + curr = curr->next; + } + } +} + EXTERN_C REDHAWK_API UInt32 __cdecl RhpRecordDebuggeeInitiatedHandle(void* objectHandle) { return DebuggerHook::RecordDebuggeeInitiatedHandle(objectHandle); } +EXTERN_C REDHAWK_API void __cdecl RhpVerifyDebuggerCleanup() +{ + assert(DebuggerHook::s_debuggerOwnedHandles == nullptr); + assert(DebuggerHook::s_debuggerProtectedBuffers == nullptr); +} + #endif // !DACCESS_COMPILE \ No newline at end of file diff --git a/external/corert/src/Native/Runtime/DebuggerHook.h b/external/corert/src/Native/Runtime/DebuggerHook.h index bc6c87dedf..bb03123cf4 100644 --- a/external/corert/src/Native/Runtime/DebuggerHook.h +++ b/external/corert/src/Native/Runtime/DebuggerHook.h @@ -14,22 +14,23 @@ #include "CommonMacros.h" #endif #include "daccess.h" +#include "Debug.h" #ifndef DACCESS_COMPILE -struct DebuggerProtectedBufferList +struct DebuggerProtectedBufferListNode { UInt64 address; UInt16 size; UInt32 identifier; - struct DebuggerProtectedBufferList* next; + struct DebuggerProtectedBufferListNode* next; }; -struct DebuggerOwnedHandleList +struct DebuggerOwnedHandleListNode { - void* handle; - UInt32 identifier; - struct DebuggerOwnedHandleList* next; + void* handle; + UInt32 identifier; + struct DebuggerOwnedHandleListNode* next; }; class DebuggerHook @@ -37,8 +38,13 @@ class DebuggerHook public: static void OnBeforeGcCollection(); static UInt32 RecordDebuggeeInitiatedHandle(void* handle); - static DebuggerProtectedBufferList* s_debuggerProtectedBuffers; - static DebuggerOwnedHandleList* s_debuggerOwnedHandleList; + static DebuggerProtectedBufferListNode* s_debuggerProtectedBuffers; + static DebuggerOwnedHandleListNode* s_debuggerOwnedHandles; +private: + static void EnsureConservativeReporting(DebuggerGcProtectionRequest* request); + static void RemoveConservativeReporting(DebuggerGcProtectionRequest* request); + static void EnsureHandle(DebuggerGcProtectionRequest* request); + static void RemoveHandle(DebuggerGcProtectionRequest* request); static UInt32 s_debuggeeInitiatedHandleIdentifier; }; diff --git a/external/corert/src/Native/Runtime/EHHelpers.cpp b/external/corert/src/Native/Runtime/EHHelpers.cpp index 64b5f27ce0..fd3c2f7a69 100644 --- a/external/corert/src/Native/Runtime/EHHelpers.cpp +++ b/external/corert/src/Native/Runtime/EHHelpers.cpp @@ -87,16 +87,13 @@ COOP_PINVOKE_HELPER(void *, RhpGetClasslibFunctionFromCodeAddress, (void * addre // Unmanaged helper to locate one of two classlib-provided functions that the runtime needs to // implement throwing of exceptions out of Rtm, and fail-fast. This may return NULL if the classlib // found via the provided address does not have the necessary exports. -COOP_PINVOKE_HELPER(void *, RhpGetClasslibFunctionFromEEtype, (EEType * pEEtype, ClasslibFunctionId functionId)) +COOP_PINVOKE_HELPER(void *, RhpGetClasslibFunctionFromEEType, (EEType * pEEType, ClasslibFunctionId functionId)) { - if (pEEtype->HasTypeManager()) - { - return pEEtype->GetTypeManagerPtr()->AsTypeManager()->GetClasslibFunction(functionId); - } - else +#ifdef PROJECTN + if (!pEEType->HasTypeManager()) { RuntimeInstance * pRI = GetRuntimeInstance(); - Module * pModule = pRI->FindModuleByAddress(pEEtype); + Module * pModule = pRI->FindModuleByAddress(pEEType); if (pModule != NULL) { return pModule->GetClasslibFunction(functionId); @@ -106,6 +103,9 @@ COOP_PINVOKE_HELPER(void *, RhpGetClasslibFunctionFromEEtype, (EEType * pEEtype, return NULL; } } +#endif // PROJECTN + + return pEEType->GetTypeManagerPtr()->AsTypeManager()->GetClasslibFunction(functionId); } COOP_PINVOKE_HELPER(void, RhpValidateExInfoStack, ()) @@ -190,14 +190,33 @@ COOP_PINVOKE_HELPER(void, RhpCopyContextFromExInfo, pContext->Lr = pPalContext->LR; pContext->Pc = pPalContext->IP; #elif defined(_ARM64_) - PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); + pContext->X0 = pPalContext->X0; + pContext->X1 = pPalContext->X1; + // TODO: Copy registers X2-X7 when we start supporting HVA's + pContext->X19 = pPalContext->X19; + pContext->X20 = pPalContext->X20; + pContext->X21 = pPalContext->X21; + pContext->X22 = pPalContext->X22; + pContext->X23 = pPalContext->X23; + pContext->X24 = pPalContext->X24; + pContext->X25 = pPalContext->X25; + pContext->X26 = pPalContext->X26; + pContext->X27 = pPalContext->X27; + pContext->X28 = pPalContext->X28; + pContext->Fp = pPalContext->FP; + pContext->Sp = pPalContext->SP; + pContext->Lr = pPalContext->LR; + pContext->Pc = pPalContext->IP; +#elif defined(_WASM_) + // No registers, no work to do yet #else #error Not Implemented for this architecture -- RhpCopyContextFromExInfo #endif } -#if defined(_AMD64_) || defined(_ARM_) || defined(_X86_) +#if defined(_AMD64_) || defined(_ARM_) || defined(_X86_) || defined(_ARM64_) +// ARM64TODO struct DISPATCHER_CONTEXT { UIntNative ControlPc; @@ -284,7 +303,7 @@ EXTERN_C Int32 RhpPInvokeExceptionGuard() } #endif -#if defined(_AMD64_) || defined(_ARM_) || defined(_X86_) +#if defined(_AMD64_) || defined(_ARM_) || defined(_X86_) || defined(_ARM64_) || defined(_WASM_) EXTERN_C REDHAWK_API void __fastcall RhpThrowHwEx(); #else COOP_PINVOKE_HELPER(void, RhpThrowHwEx, ()) @@ -344,10 +363,7 @@ static bool InWriteBarrierHelper(UIntNative faultingIP) (UIntNative)&RhpCheckedAssignRefAVLocation, (UIntNative)&RhpCheckedLockCmpXchgAVLocation, (UIntNative)&RhpCheckedXchgAVLocation, -#ifdef CORERT - (UIntNative)&RhpLockCmpXchg32AVLocation, - (UIntNative)&RhpLockCmpXchg64AVLocation, -#else +#ifdef PROJECTN (UIntNative)&RhpCopyMultibyteDestAVLocation, (UIntNative)&RhpCopyMultibyteSrcAVLocation, (UIntNative)&RhpCopyMultibyteNoGCRefsDestAVLocation, @@ -356,6 +372,9 @@ static bool InWriteBarrierHelper(UIntNative faultingIP) (UIntNative)&RhpCopyMultibyteWithWriteBarrierSrcAVLocation, (UIntNative)&RhpCopyAnyWithWriteBarrierDestAVLocation, (UIntNative)&RhpCopyAnyWithWriteBarrierSrcAVLocation, +#else + (UIntNative)&RhpLockCmpXchg32AVLocation, + (UIntNative)&RhpLockCmpXchg64AVLocation, #endif }; @@ -376,8 +395,6 @@ static bool InWriteBarrierHelper(UIntNative faultingIP) return false; } - - static UIntNative UnwindWriteBarrierToCaller( #ifdef PLATFORM_UNIX PAL_LIMITED_CONTEXT * pContext @@ -398,7 +415,8 @@ static UIntNative UnwindWriteBarrierToCaller( #elif defined(_ARM_) || defined(_ARM64_) UIntNative adjustedFaultingIP = pContext->GetLr(); #else -#error "Unknown Architecture" + UIntNative adjustedFaultingIP = 0; // initializing to make the compiler happy + PORTABILITY_ASSERT("UnwindWriteBarrierToCaller"); #endif return adjustedFaultingIP; } diff --git a/external/corert/src/Native/Runtime/Full/CMakeLists.txt b/external/corert/src/Native/Runtime/Full/CMakeLists.txt index 4b9f5b8914..af7de3a21c 100644 --- a/external/corert/src/Native/Runtime/Full/CMakeLists.txt +++ b/external/corert/src/Native/Runtime/Full/CMakeLists.txt @@ -72,7 +72,7 @@ foreach(CONFIG IN LISTS CMAKE_CONFIGURATION_TYPES) endforeach() # Install the static Runtime library -install (TARGETS Runtime DESTINATION lib) +install (TARGETS Runtime DESTINATION sdk) if(WIN32) - install (FILES ${CMAKE_CURRENT_BINARY_DIR}/Runtime.dir/$/Runtime.pdb DESTINATION lib) + install (FILES ${CMAKE_CURRENT_BINARY_DIR}/Runtime.dir/$/Runtime.pdb DESTINATION sdk) endif() diff --git a/external/corert/src/Native/Runtime/GCHelpers.cpp b/external/corert/src/Native/Runtime/GCHelpers.cpp index 405ff7c8b5..4224db0ae9 100644 --- a/external/corert/src/Native/Runtime/GCHelpers.cpp +++ b/external/corert/src/Native/Runtime/GCHelpers.cpp @@ -205,3 +205,11 @@ COOP_PINVOKE_HELPER(Int64, RhGetGCSegmentSize, ()) return (first > second) ? first : second; } + +COOP_PINVOKE_HELPER(Int64, RhGetAllocatedBytesForCurrentThread, ()) +{ + Thread *pThread = GetThread(); + gc_alloc_context *ac = pThread->GetAllocContext(); + Int64 currentAllocated = ac->alloc_bytes + ac->alloc_bytes_loh - (ac->alloc_limit - ac->alloc_ptr); + return currentAllocated; +} diff --git a/external/corert/src/Native/Runtime/GCMemoryHelpers.inl b/external/corert/src/Native/Runtime/GCMemoryHelpers.inl index 9ab3421236..c9aade27d7 100644 --- a/external/corert/src/Native/Runtime/GCMemoryHelpers.inl +++ b/external/corert/src/Native/Runtime/GCMemoryHelpers.inl @@ -17,17 +17,18 @@ FORCEINLINE void InlineGCSafeFillMemory(void * mem, size_t size, size_t pv) UInt8 * memBytes = (UInt8 *)mem; UInt8 * endBytes = &memBytes[size]; - // handle unaligned bytes at the beginning + // handle unaligned bytes at the beginning while (!IS_ALIGNED(memBytes, sizeof(void *)) && (memBytes < endBytes)) *memBytes++ = (UInt8)pv; - // now write pointer sized pieces + // now write pointer sized pieces + // volatile ensures that this doesn't get optimized back into a memset call size_t nPtrs = (endBytes - memBytes) / sizeof(void *); - UIntNative* memPtr = (UIntNative*)memBytes; + volatile UIntNative* memPtr = (UIntNative*)memBytes; for (size_t i = 0; i < nPtrs; i++) *memPtr++ = pv; - // handle remaining bytes at the end + // handle remaining bytes at the end memBytes = (UInt8*)memPtr; while (memBytes < endBytes) *memBytes++ = (UInt8)pv; diff --git a/external/corert/src/Native/Runtime/ICodeManager.h b/external/corert/src/Native/Runtime/ICodeManager.h index 016b8fd400..efd5054d28 100644 --- a/external/corert/src/Native/Runtime/ICodeManager.h +++ b/external/corert/src/Native/Runtime/ICodeManager.h @@ -77,6 +77,13 @@ enum class ClasslibFunctionId AppendExceptionStackFrame = 3, CheckStaticClassConstruction = 4, GetSystemArrayEEType = 5, + OnFirstChanceException = 6, +}; + +enum class AssociatedDataFlags : unsigned char +{ + None = 0, + HasUnboxingStubTarget = 1, }; class ICodeManager @@ -117,5 +124,12 @@ public: virtual PTR_VOID GetMethodStartAddress(MethodInfo * pMethodInfo) = 0; + virtual PTR_VOID GetOsModuleHandle() = 0; + virtual void * GetClasslibFunction(ClasslibFunctionId functionId) = 0; + + // Returns any custom data attached to the method. Format: + // AssociatedDataFlags // 1 byte. Flags describing the data stored + // Data (stream of bytes) // Variable size (depending on flags). Custom data associated with method + virtual PTR_VOID GetAssociatedData(PTR_VOID ControlPC) = 0; }; diff --git a/external/corert/src/Native/Runtime/MathHelpers.cpp b/external/corert/src/Native/Runtime/MathHelpers.cpp index 3365e12bae..88a26a556f 100644 --- a/external/corert/src/Native/Runtime/MathHelpers.cpp +++ b/external/corert/src/Native/Runtime/MathHelpers.cpp @@ -15,8 +15,10 @@ EXTERN_C REDHAWK_API UInt64 REDHAWK_CALLCONV RhpDbl2ULng(double val) return((UInt64)val); } -// CORERT Specific - on Project N the arguments to these helpers are inverted -#ifdef CORERT +// On ProjectN the arguments to these helpers are inverted +#ifndef PROJECTN +#undef min +#undef max #include EXTERN_C REDHAWK_API float REDHAWK_CALLCONV RhpFltRem(float dividend, float divisor) @@ -67,26 +69,65 @@ EXTERN_C REDHAWK_API double REDHAWK_CALLCONV RhpDblRem(double dividend, double d // else... return(fmod(dividend,divisor)); } -#endif // CORERT + +EXTERN_C REDHAWK_API double REDHAWK_CALLCONV RhpDblRound(double value) +{ + return round(value); +} + +EXTERN_C REDHAWK_API float REDHAWK_CALLCONV RhpFltRound(float value) +{ + return roundf(value); +} + +#endif // !PROJECTN #ifdef _ARM_ EXTERN_C REDHAWK_API Int32 REDHAWK_CALLCONV RhpIDiv(Int32 i, Int32 j) { + ASSERT(j && "Divide by zero!"); + return i / j; +} + +EXTERN_C REDHAWK_API UInt32 REDHAWK_CALLCONV RhpUDiv(UInt32 i, UInt32 j) +{ + ASSERT(j && "Divide by zero!"); + return i / j; +} + +EXTERN_C REDHAWK_API Int64 REDHAWK_CALLCONV RhpLDiv(Int64 i, Int64 j) +{ + ASSERT(j && "Divide by zero!"); + return i / j; +} + +EXTERN_C REDHAWK_API UInt64 REDHAWK_CALLCONV RhpULDiv(UInt64 i, UInt64 j) +{ + ASSERT(j && "Divide by zero!"); return i / j; } EXTERN_C REDHAWK_API Int32 REDHAWK_CALLCONV RhpIMod(Int32 i, Int32 j) { + ASSERT(j && "Divide by zero!"); return i % j; } -EXTERN_C REDHAWK_API UInt32 REDHAWK_CALLCONV RhpUDiv(UInt32 i, UInt32 j) -{ - return i / j; -} - EXTERN_C REDHAWK_API UInt32 REDHAWK_CALLCONV RhpUMod(UInt32 i, UInt32 j) { + ASSERT(j && "Divide by zero!"); + return i % j; +} + +EXTERN_C REDHAWK_API Int64 REDHAWK_CALLCONV RhpLMod(Int64 i, Int64 j) +{ + ASSERT(j && "Divide by zero!"); + return i % j; +} + +EXTERN_C REDHAWK_API UInt64 REDHAWK_CALLCONV RhpULMod(UInt64 i, UInt64 j) +{ + ASSERT(j && "Divide by zero!"); return i % j; } @@ -95,29 +136,24 @@ EXTERN_C REDHAWK_API Int64 REDHAWK_CALLCONV RhpLMul(Int64 i, Int64 j) return i * j; } -EXTERN_C REDHAWK_API Int64 REDHAWK_CALLCONV RhpLDiv(Int64 i, Int64 j) -{ - return i / j; -} - -EXTERN_C REDHAWK_API Int64 REDHAWK_CALLCONV RhpLMod(Int64 i, Int64 j) -{ - return i % j; -} - EXTERN_C REDHAWK_API UInt64 REDHAWK_CALLCONV RhpULMul(UInt64 i, UInt64 j) { return i * j; } -EXTERN_C REDHAWK_API UInt64 REDHAWK_CALLCONV RhpULDiv(UInt64 i, UInt64 j) +EXTERN_C REDHAWK_API UInt64 REDHAWK_CALLCONV RhpLRsz(UInt64 i, Int32 j) { - return i / j; + return i >> j; } -EXTERN_C REDHAWK_API UInt64 REDHAWK_CALLCONV RhpULMod(UInt64 i, UInt64 j) +EXTERN_C REDHAWK_API Int64 REDHAWK_CALLCONV RhpLRsh(Int64 i, Int32 j) { - return i % j; + return i >> j; +} + +EXTERN_C REDHAWK_API Int64 REDHAWK_CALLCONV RhpLLsh(Int64 i, Int32 j) +{ + return i << j; } EXTERN_C REDHAWK_API Int64 REDHAWK_CALLCONV RhpDbl2Lng(double val) @@ -125,6 +161,16 @@ EXTERN_C REDHAWK_API Int64 REDHAWK_CALLCONV RhpDbl2Lng(double val) return (Int64)val; } +EXTERN_C REDHAWK_API Int32 REDHAWK_CALLCONV RhpDbl2Int(double val) +{ + return (Int32)val; +} + +EXTERN_C REDHAWK_API UInt32 REDHAWK_CALLCONV RhpDbl2UInt(double val) +{ + return (UInt32)val; +} + EXTERN_C REDHAWK_API double REDHAWK_CALLCONV RhpLng2Dbl(Int64 val) { return (double)val; diff --git a/external/corert/src/Native/Runtime/MiscHelpers.cpp b/external/corert/src/Native/Runtime/MiscHelpers.cpp index 27a74f19ad..05811e91da 100644 --- a/external/corert/src/Native/Runtime/MiscHelpers.cpp +++ b/external/corert/src/Native/Runtime/MiscHelpers.cpp @@ -122,150 +122,87 @@ COOP_PINVOKE_HELPER(UInt32, RhGetLoadedOSModules, (Array * pResultArray)) return cModules; } -// Get the list of currently loaded Redhawk modules (as OS HMODULE handles or TypeManager pointers as appropriate). -// The caller provides a reference -// to an array of pointer-sized elements and we return the total number of modules currently loaded (whether -// that is less than, equal to or greater than the number of elements in the array). If there are more modules -// loaded than the array will hold then the array is filled to capacity and the caller can tell further -// modules are available based on the return count. It is also possible to call this method without an array, -// in which case just the module count is returned (note that it's still possible for the module count to -// increase between calls to this method). -COOP_PINVOKE_HELPER(UInt32, RhGetLoadedModules, (Array * pResultArray)) -{ - // Note that we depend on the fact that this is a COOP helper to make writing into an unpinned array safe. - - // If a result array is passed then it should be an array type with pointer-sized components that are not - // GC-references. - ASSERT(!pResultArray || pResultArray->get_EEType()->IsArray()); - ASSERT(!pResultArray || !pResultArray->get_EEType()->HasReferenceFields()); - ASSERT(!pResultArray || pResultArray->get_EEType()->get_ComponentSize() == sizeof(void*)); - - UInt32 cResultArrayElements = pResultArray ? pResultArray->GetArrayLength() : 0; - TypeManagerHandle * pResultElements = pResultArray ? (TypeManagerHandle*)(pResultArray + 1) : NULL; - - UInt32 cModules = 0; - - FOREACH_MODULE(pModule) - { - if (pResultArray && (cModules < cResultArrayElements)) - pResultElements[cModules] = TypeManagerHandle::Create(pModule->GetOsModuleHandle()); - - cModules++; - } - END_FOREACH_MODULE - - ReaderWriterLock::ReadHolder read(&GetRuntimeInstance()->GetTypeManagerLock()); - - RuntimeInstance::TypeManagerList typeManagers = GetRuntimeInstance()->GetTypeManagerList(); - - for (RuntimeInstance::TypeManagerList::Iterator iter = typeManagers.Begin(); iter != typeManagers.End(); iter++) - { - if (pResultArray && (cModules < cResultArrayElements)) - pResultElements[cModules] = TypeManagerHandle::Create(iter->m_pTypeManager); - cModules++; - } - - return cModules; -} - COOP_PINVOKE_HELPER(HANDLE, RhGetOSModuleFromPointer, (PTR_VOID pPointerVal)) { - // TODO, this will always return nullptr in TypeManager based systems. Module * pModule = GetRuntimeInstance()->FindModuleByAddress(pPointerVal); if (pModule != NULL) return pModule->GetOsModuleHandle(); + ICodeManager * pCodeManager = GetRuntimeInstance()->FindCodeManagerByAddress(pPointerVal); + + if (pCodeManager != NULL) + return (HANDLE)pCodeManager->GetOsModuleHandle(); + return NULL; } COOP_PINVOKE_HELPER(HANDLE, RhGetOSModuleFromEEType, (EEType * pEEType)) { -#if CORERT - return pEEType->GetTypeManagerPtr()->AsTypeManager()->GetOsModuleHandle(); -#else -#if EETYPE_TYPE_MANAGER - if (pEEType->HasTypeManager()) - return pEEType->GetTypeManagerPtr()->AsTypeManager()->GetOsModuleHandle(); -#endif - - // For dynamically created types, return the module handle that contains the template type - if (pEEType->IsDynamicType()) - pEEType = pEEType->get_DynamicTemplateType(); - - if (pEEType->get_DynamicModule() != nullptr) - return nullptr; - - FOREACH_MODULE(pModule) +#ifdef PROJECTN + if (!pEEType->HasTypeManager()) { - if (pModule->ContainsReadOnlyDataAddress(pEEType) || pModule->ContainsDataAddress(pEEType)) - return pModule->GetOsModuleHandle(); - } - END_FOREACH_MODULE + // For dynamically created types, return the module handle that contains the template type + if (pEEType->IsDynamicType()) + pEEType = pEEType->get_DynamicTemplateType(); - // We should never get here (an EEType not located in any module) so fail fast to indicate the bug. - RhFailFast(); - return NULL; -#endif // !CORERT + if (pEEType->get_DynamicModule() != nullptr) + return nullptr; + + FOREACH_MODULE(pModule) + { + if (pModule->ContainsReadOnlyDataAddress(pEEType) || pModule->ContainsDataAddress(pEEType)) + return pModule->GetOsModuleHandle(); + } + END_FOREACH_MODULE + + // We should never get here (an EEType not located in any module) so fail fast to indicate the bug. + RhFailFast(); + return NULL; + } +#endif // PROJECTN + + return pEEType->GetTypeManagerPtr()->AsTypeManager()->GetOsModuleHandle(); } COOP_PINVOKE_HELPER(TypeManagerHandle, RhGetModuleFromEEType, (EEType * pEEType)) { -#if CORERT - return *pEEType->GetTypeManagerPtr(); -#else -#if EETYPE_TYPE_MANAGER - if (pEEType->HasTypeManager()) - return *pEEType->GetTypeManagerPtr(); -#endif - - // For dynamically created types, return the module handle that contains the template type - if (pEEType->IsDynamicType()) - pEEType = pEEType->get_DynamicTemplateType(); - - if (pEEType->get_DynamicModule() != nullptr) +#ifdef PROJECTN + if (!pEEType->HasTypeManager()) { + // For dynamically created types, return the module handle that contains the template type + if (pEEType->IsDynamicType()) + pEEType = pEEType->get_DynamicTemplateType(); + + if (pEEType->get_DynamicModule() != nullptr) + { + // We should never get here (an EEType not located in any module) so fail fast to indicate the bug. + RhFailFast(); + return TypeManagerHandle::Null(); + } + + FOREACH_MODULE(pModule) + { + if (pModule->ContainsReadOnlyDataAddress(pEEType) || pModule->ContainsDataAddress(pEEType)) + return TypeManagerHandle::Create(pModule->GetOsModuleHandle()); + } + END_FOREACH_MODULE + // We should never get here (an EEType not located in any module) so fail fast to indicate the bug. RhFailFast(); return TypeManagerHandle::Null(); } +#endif // PROJECTN - FOREACH_MODULE(pModule) - { - if (pModule->ContainsReadOnlyDataAddress(pEEType) || pModule->ContainsDataAddress(pEEType)) - return TypeManagerHandle::Create(pModule->GetOsModuleHandle()); - } - END_FOREACH_MODULE - - // We should never get here (an EEType not located in any module) so fail fast to indicate the bug. - RhFailFast(); - return TypeManagerHandle::Null(); -#endif // !CORERT + return *pEEType->GetTypeManagerPtr(); } COOP_PINVOKE_HELPER(Boolean, RhFindBlob, (TypeManagerHandle *pTypeManagerHandle, UInt32 blobId, UInt8 ** ppbBlob, UInt32 * pcbBlob)) { TypeManagerHandle typeManagerHandle = *pTypeManagerHandle; - if (typeManagerHandle.IsTypeManager()) - { - ReadyToRunSectionType section = - (ReadyToRunSectionType)((UInt32)ReadyToRunSectionType::ReadonlyBlobRegionStart + blobId); - ASSERT(section <= ReadyToRunSectionType::ReadonlyBlobRegionEnd); - - TypeManager* pModule = typeManagerHandle.AsTypeManager(); - - int length; - void* pBlob; - pBlob = pModule->GetModuleSection(section, &length); - - *ppbBlob = (UInt8*)pBlob; - *pcbBlob = (UInt32)length; - - return pBlob != NULL; - } -#if !CORERT - else +#ifdef PROJECTN + if (!typeManagerHandle.IsTypeManager()) { HANDLE hOsModule = typeManagerHandle.AsOsModule(); // Search for the Redhawk module contained by the OS module. @@ -302,14 +239,28 @@ COOP_PINVOKE_HELPER(Boolean, RhFindBlob, (TypeManagerHandle *pTypeManagerHandle, } } END_FOREACH_MODULE + + // If we get here we were passed a bad module handle and should fail fast since this indicates a nasty bug + // (which could lead to the wrong blob being returned in some cases). + RhFailFast(); + return FALSE; } -#endif // !CORERT +#endif // PROJECTN - // If we get here we were passed a bad module handle and should fail fast since this indicates a nasty bug - // (which could lead to the wrong blob being returned in some cases). - RhFailFast(); + ReadyToRunSectionType section = + (ReadyToRunSectionType)((UInt32)ReadyToRunSectionType::ReadonlyBlobRegionStart + blobId); + ASSERT(section <= ReadyToRunSectionType::ReadonlyBlobRegionEnd); - return FALSE; + TypeManager* pModule = typeManagerHandle.AsTypeManager(); + + int length; + void* pBlob; + pBlob = pModule->GetModuleSection(section, &length); + + *ppbBlob = (UInt8*)pBlob; + *pcbBlob = (UInt32)length; + + return pBlob != NULL; } // This helper is not called directly but is used by the implementation of RhpCheckCctor to locate the @@ -332,6 +283,11 @@ COOP_PINVOKE_HELPER(void *, GetClasslibCCtorCheck, (void * pReturnAddress)) return pCallback; } +COOP_PINVOKE_HELPER(void *, RhGetTargetOfUnboxingAndInstantiatingStub, (void * pUnboxStub)) +{ + return GetRuntimeInstance()->GetTargetOfUnboxingAndInstantiatingStub(pUnboxStub); +} + COOP_PINVOKE_HELPER(Boolean, RhpHasDispatchMap, (EEType * pEEType)) { return pEEType->HasDispatchMap(); @@ -349,7 +305,7 @@ COOP_PINVOKE_HELPER(EEType *, RhpGetArrayBaseType, (EEType * pEEType)) // Obtain the address of a thread static field for the current thread given the enclosing type and a field cookie // obtained from a fixed up binder blob field record. -COOP_PINVOKE_HELPER(UInt8 *, RhGetThreadStaticFieldAddress, (EEType * pEEType, ThreadStaticFieldOffsets* pFieldCookie)) +COOP_PINVOKE_HELPER(UInt8 *, RhGetThreadStaticFieldAddress, (EEType * pEEType, UInt32 startingOffsetInTlsBlock, UInt32 fieldOffset)) { RuntimeInstance * pRuntimeInstance = GetRuntimeInstance(); @@ -361,36 +317,57 @@ COOP_PINVOKE_HELPER(UInt8 *, RhGetThreadStaticFieldAddress, (EEType * pEEType, T if (pEEType->IsDynamicType()) { + // Specific TLS storage is allocated for each dynamic type. There is no starting offset since it's not a + // TLS storage block shared by multiple types. + ASSERT(startingOffsetInTlsBlock == 0); + // Special case for thread static fields on dynamic types: the TLS storage is managed by the runtime // for each dynamically created type with thread statics. The TLS storage size allocated for each type // is the size of all the thread statics on that type. We use the field offset to get the thread static // data for that field on the current thread. UInt8* pTlsStorage = ThreadStore::GetCurrentThread()->GetThreadLocalStorageForDynamicType(pEEType->get_DynamicThreadStaticOffset()); ASSERT(pTlsStorage != NULL); - return (pFieldCookie != NULL ? pTlsStorage + pFieldCookie->FieldOffset : pTlsStorage); + return pTlsStorage + fieldOffset; } else { - // In all other cases the field cookie contains an offset from the base of all Redhawk thread statics - // to the field. The TLS index and offset adjustment (in cases where the module was linked with native - // code using .tls) is that from the exe module. +#if EETYPE_TYPE_MANAGER && PROJECTN /* TODO: CORERT */ + if (pEEType->HasTypeManager()) + { + TypeManager* pTypeManager = pEEType->GetTypeManagerPtr()->AsTypeManager(); + ASSERT(pTypeManager != NULL); - // In the separate compilation case, the generic unification logic should assure - // that the pEEType parameter passed in is indeed the "winner" of generic unification, - // not one of the "losers". - // TODO: come up with an assert to check this. - Module * pModule = pRuntimeInstance->FindModuleByReadOnlyDataAddress(pEEType); - if (pModule == NULL) - pModule = pRuntimeInstance->FindModuleByDataAddress(pEEType); - ASSERT(pModule != NULL); - ModuleHeader * pExeModuleHeader = pModule->GetModuleHeader(); + UInt32* pTlsIndex = pTypeManager->GetPointerToTlsIndex(); + if (pTlsIndex == NULL) + return NULL; + + uiTlsIndex = *pTlsIndex; + uiFieldOffset = startingOffsetInTlsBlock + fieldOffset; + } + else +#endif + { + // The startingOffsetInTlsBlock is an offset from the base of all Redhawk thread statics + // to the field. The TLS index and offset adjustment (in cases where the module was linked with native + // code using .tls) is that from the exe module. - uiTlsIndex = *pExeModuleHeader->PointerToTlsIndex; - uiFieldOffset = pExeModuleHeader->TlsStartOffset + pFieldCookie->StartingOffsetInTlsBlock + pFieldCookie->FieldOffset; + // In the separate compilation case, the generic unification logic should assure + // that the pEEType parameter passed in is indeed the "winner" of generic unification, + // not one of the "losers". + // TODO: come up with an assert to check this. + Module * pModule = pRuntimeInstance->FindModuleByReadOnlyDataAddress(pEEType); + if (pModule == NULL) + pModule = pRuntimeInstance->FindModuleByDataAddress(pEEType); + ASSERT(pModule != NULL); + ModuleHeader * pExeModuleHeader = pModule->GetModuleHeader(); + + uiTlsIndex = *pExeModuleHeader->PointerToTlsIndex; + uiFieldOffset = pExeModuleHeader->TlsStartOffset + startingOffsetInTlsBlock + fieldOffset; + } + + // Now look at the current thread and retrieve the address of the field. + return ThreadStore::GetCurrentThread()->GetThreadLocalStorage(uiTlsIndex, uiFieldOffset); } - - // Now look at the current thread and retrieve the address of the field. - return ThreadStore::GetCurrentThread()->GetThreadLocalStorage(uiTlsIndex, uiFieldOffset); } #if _TARGET_ARM_ @@ -445,105 +422,153 @@ inline Int32 GetThumb2BlRel24(UInt16 * p) // or unboxing stub, and if so, return the address that stub jumps to COOP_PINVOKE_HELPER(UInt8 *, RhGetCodeTarget, (UInt8 * pCodeOrg)) { - // Search for the module containing the code - FOREACH_MODULE(pModule) - { - // If the code pointer doesn't point to a module's stub range, - // it can't be pointing to a stub - if (!pModule->ContainsStubAddress(pCodeOrg)) - continue; + Module * pModule = NULL; + bool unboxingStub = false; - bool unboxingStub = false; + // First, check the unboxing stubs regions known by the runtime (if any exist) + if (!GetRuntimeInstance()->IsUnboxingStub(pCodeOrg)) + { + // Search for the module containing the code + FOREACH_MODULE(pCurrentModule) + { + // If the code pointer doesn't point to a module's stub range, + // it can't be pointing to a stub + if (pCurrentModule->ContainsStubAddress(pCodeOrg)) + { + pModule = pCurrentModule; + break; + } + } + END_FOREACH_MODULE; + + if (pModule == NULL) + return pCodeOrg; + } #ifdef _TARGET_AMD64_ - UInt8 * pCode = pCodeOrg; + UInt8 * pCode = pCodeOrg; - // is this "add rcx,8"? - if (pCode[0] == 0x48 && pCode[1] == 0x83 && pCode[2] == 0xc1 && pCode[3] == 0x08) - { - // unboxing sequence - unboxingStub = true; - pCode += 4; - } - // is this an indirect jump? - if (pCode[0] == 0xff && pCode[1] == 0x25) - { - // normal import stub - dist to IAT cell is relative to the point *after* the instruction - Int32 distToIatCell = *(Int32 *)&pCode[2]; - UInt8 ** pIatCell = (UInt8 **)(pCode + 6 + distToIatCell); - ASSERT(pModule->ContainsDataAddress(pIatCell)); - return *pIatCell; - } - // is this an unboxing stub followed by a relative jump? - else if (unboxingStub && pCode[0] == 0xe9) - { - // relatie jump - dist is relative to the point *after* the instruction - Int32 distToTarget = *(Int32 *)&pCode[1]; - UInt8 * target = pCode + 5 + distToTarget; - return target; - } - return pCodeOrg; + // is this "add rcx/rdi,8"? + if (pCode[0] == 0x48 && + pCode[1] == 0x83 && +#ifdef UNIX_AMD64_ABI + pCode[2] == 0xc7 && +#else + pCode[2] == 0xc1 && +#endif + pCode[3] == 0x08) + { + // unboxing sequence + unboxingStub = true; + pCode += 4; + } + // is this an indirect jump? + if (pCode[0] == 0xff && pCode[1] == 0x25) + { + // normal import stub - dist to IAT cell is relative to the point *after* the instruction + Int32 distToIatCell = *(Int32 *)&pCode[2]; + UInt8 ** pIatCell = (UInt8 **)(pCode + 6 + distToIatCell); + ASSERT(pModule == NULL || pModule->ContainsDataAddress(pIatCell)); + return *pIatCell; + } + // is this an unboxing stub followed by a relative jump? + else if (unboxingStub && pCode[0] == 0xe9) + { + // relative jump - dist is relative to the point *after* the instruction + Int32 distToTarget = *(Int32 *)&pCode[1]; + UInt8 * target = pCode + 5 + distToTarget; + return target; + } #elif _TARGET_X86_ - UInt8 * pCode = pCodeOrg; + UInt8 * pCode = pCodeOrg; - // is this "add ecx,4"? - if (pCode[0] == 0x83 && pCode[1] == 0xc1 && pCode[2] == 0x04) - { - // unboxing sequence - unboxingStub = true; - pCode += 3; - } - // is this an indirect jump? - if (pCode[0] == 0xff && pCode[1] == 0x25) - { - // normal import stub - address of IAT follows - UInt8 **pIatCell = *(UInt8 ***)&pCode[2]; - ASSERT(pModule->ContainsDataAddress(pIatCell)); - return *pIatCell; - } - // is this an unboxing stub followed by a relative jump? - else if (unboxingStub && pCode[0] == 0xe9) - { - // relatie jump - dist is relative to the point *after* the instruction - Int32 distToTarget = *(Int32 *)&pCode[1]; - UInt8 * pTarget = pCode + 5 + distToTarget; - return pTarget; - } - return pCodeOrg; + // is this "add ecx,4"? + if (pCode[0] == 0x83 && pCode[1] == 0xc1 && pCode[2] == 0x04) + { + // unboxing sequence + unboxingStub = true; + pCode += 3; + } + // is this an indirect jump? + if (pCode[0] == 0xff && pCode[1] == 0x25) + { + // normal import stub - address of IAT follows + UInt8 **pIatCell = *(UInt8 ***)&pCode[2]; + ASSERT(pModule == NULL || pModule->ContainsDataAddress(pIatCell)); + return *pIatCell; + } + // is this an unboxing stub followed by a relative jump? + else if (unboxingStub && pCode[0] == 0xe9) + { + // relative jump - dist is relative to the point *after* the instruction + Int32 distToTarget = *(Int32 *)&pCode[1]; + UInt8 * pTarget = pCode + 5 + distToTarget; + return pTarget; + } #elif _TARGET_ARM_ - const UInt16 THUMB_BIT = 1; - UInt16 * pCode = (UInt16 *)((size_t)pCodeOrg & ~THUMB_BIT); - // is this "adds r0,4"? - if (pCode[0] == 0x3004) - { - // unboxing sequence - unboxingStub = true; - pCode += 1; - } - // is this movw r12,#imm16; movt r12,#imm16; ldr pc,[r12]? - if ((pCode[0] & 0xfbf0) == 0xf240 && (pCode[1] & 0x0f00) == 0x0c00 - && (pCode[2] & 0xfbf0) == 0xf2c0 && (pCode[3] & 0x0f00) == 0x0c00 - && pCode[4] == 0xf8dc && pCode[5] == 0xf000) + const UInt16 THUMB_BIT = 1; + UInt16 * pCode = (UInt16 *)((size_t)pCodeOrg & ~THUMB_BIT); + // is this "adds r0,4"? + if (pCode[0] == 0x3004) + { + // unboxing sequence + unboxingStub = true; + pCode += 1; + } + // is this movw r12,#imm16; movt r12,#imm16; ldr pc,[r12] + // or movw r12,#imm16; movt r12,#imm16; bx r12 + if ((pCode[0] & 0xfbf0) == 0xf240 && (pCode[1] & 0x0f00) == 0x0c00 + && (pCode[2] & 0xfbf0) == 0xf2c0 && (pCode[3] & 0x0f00) == 0x0c00 + && ((pCode[4] == 0xf8dc && pCode[5] == 0xf000) || pCode[4] == 0x4760)) + { + if (pCode[4] == 0xf8dc && pCode[5] == 0xf000) { + // ldr pc,[r12] UInt8 **pIatCell = (UInt8 **)GetThumb2Mov32(pCode); return *pIatCell; } - // is this an unboxing stub followed by a relative jump? - else if (unboxingStub && (pCode[0] & 0xf800) == 0xf000 && (pCode[1] & 0xd000) == 0x9000) + else if (pCode[4] == 0x4760) { - Int32 distToTarget = GetThumb2BlRel24(pCode); - UInt8 * pTarget = (UInt8 *)(pCode + 2) + distToTarget + THUMB_BIT; - return (UInt8 *)pTarget; + // bx r12 + return (UInt8 *)GetThumb2Mov32(pCode); } -#elif _TARGET_ARM64_ - PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); -#else -#error 'Unsupported Architecture' -#endif } - END_FOREACH_MODULE; + // is this an unboxing stub followed by a relative jump? + else if (unboxingStub && (pCode[0] & 0xf800) == 0xf000 && (pCode[1] & 0xd000) == 0x9000) + { + Int32 distToTarget = GetThumb2BlRel24(pCode); + UInt8 * pTarget = (UInt8 *)(pCode + 2) + distToTarget + THUMB_BIT; + return (UInt8 *)pTarget; + } + +#elif _TARGET_ARM64_ + UInt32 * pCode = (UInt32 *)pCodeOrg; + // is this "add x0,x0,#8"? + if (pCode[0] == 0x91002000) + { + // unboxing sequence + unboxingStub = true; + pCode++; + } + // is this an indirect jump? + if (/* ARM64TODO */ false) + { + // ARM64TODO + } + // is this an unboxing stub followed by a relative jump? + else if (unboxingStub && (pCode[0] >> 26) == 0x5) + { + // relative jump - dist is relative to the instruction + // offset = SignExtend(imm26:'00', 64); + Int64 distToTarget = ((Int64)pCode[0] << 38) >> 36; + return (UInt8 *)pCode + distToTarget; + } +#else + UNREFERENCED_PARAMETER(unboxingStub); + PORTABILITY_ASSERT("RhGetCodeTarget"); +#endif return pCodeOrg; } @@ -598,7 +623,7 @@ COOP_PINVOKE_HELPER(UInt8 *, RhGetJmpStubCodeTarget, (UInt8 * pCodeOrg)) #elif _TARGET_ARM64_ PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); #else -#error 'Unsupported Architecture' + PORTABILITY_ASSERT("RhGetJmpStubCodeTarget"); #endif } END_FOREACH_MODULE; @@ -770,3 +795,14 @@ COOP_PINVOKE_HELPER(void, RhSetThreadExitCallback, (void * pCallback)) } #endif // PLATFORM_UNIX + +EXTERN_C void * FASTCALL RecoverLoopHijackTarget(UInt32 entryIndex, ModuleHeader * pModuleHeader) +{ + Module * pModule = GetRuntimeInstance()->FindModuleByReadOnlyDataAddress(pModuleHeader); + return pModule->RecoverLoopHijackTarget(entryIndex, pModuleHeader); +} + +COOP_PINVOKE_HELPER(Int32, RhGetProcessCpuCount, ()) +{ + return PalGetProcessCpuCount(); +} diff --git a/external/corert/src/Native/Runtime/ObjectLayout.h b/external/corert/src/Native/Runtime/ObjectLayout.h index e088bcccf1..8e7e9ba850 100644 --- a/external/corert/src/Native/Runtime/ObjectLayout.h +++ b/external/corert/src/Native/Runtime/ObjectLayout.h @@ -100,3 +100,28 @@ public: void* GetArrayData(); }; typedef DPTR(Array) PTR_Array; + +//------------------------------------------------------------------------------------------------- +class String : public Object +{ + friend class AsmOffsets; + friend class StringConstants; + + UInt32 m_Length; + UInt16 m_FirstChar; +}; +typedef DPTR(String) PTR_String; + +//------------------------------------------------------------------------------------------------- +class StringConstants +{ +public: + static UIntNative const ComponentSize = sizeof(((String*)0)->m_FirstChar); + static UIntNative const BaseSize = sizeof(ObjHeader) + offsetof(String, m_FirstChar) + ComponentSize; +}; + +//------------------------------------------------------------------------------------------------- +static UIntNative const STRING_COMPONENT_SIZE = StringConstants::ComponentSize; + +//------------------------------------------------------------------------------------------------- +static UIntNative const STRING_BASE_SIZE = StringConstants::BaseSize; diff --git a/external/corert/src/Native/Runtime/PalRedhawk.h b/external/corert/src/Native/Runtime/PalRedhawk.h index 6b09beb71f..553e42cf27 100644 --- a/external/corert/src/Native/Runtime/PalRedhawk.h +++ b/external/corert/src/Native/Runtime/PalRedhawk.h @@ -31,7 +31,7 @@ #endif #ifndef _INC_WINDOWS -//#ifndef DACCESS_COMPILE +//#ifndef DACCESS_COMPILE // There are some fairly primitive type definitions below but don't pull them into the rest of Redhawk unless // we have to (in which case these definitions will move to CommonTypes.h). @@ -102,21 +102,6 @@ struct SYSTEM_INFO // defined in gcrhenv.cpp bool __SwitchToThread(uint32_t dwSleepMSec, uint32_t dwSwitchCount); -struct OSVERSIONINFOEXW -{ - UInt32 dwOSVersionInfoSize; - UInt32 dwMajorVersion; - UInt32 dwMinorVersion; - UInt32 dwBuildNumber; - UInt32 dwPlatformId; - WCHAR szCSDVersion[128]; - UInt16 wServicePackMajor; - UInt16 wServicePackMinor; - UInt16 wSuiteMask; - UInt8 wProductType; - UInt8 wReserved; -}; - struct FILETIME { UInt32 dwLowDateTime; @@ -408,35 +393,43 @@ typedef struct DECLSPEC_ALIGN(16) _CONTEXT { // Integer registers // UInt32 Cpsr; // NZVF + DAIF + CurrentEL + SPSel - UInt64 X0; - UInt64 X1; - UInt64 X2; - UInt64 X3; - UInt64 X4; - UInt64 X5; - UInt64 X6; - UInt64 X7; - UInt64 X8; - UInt64 X9; - UInt64 X10; - UInt64 X11; - UInt64 X12; - UInt64 X13; - UInt64 X14; - UInt64 X15; - UInt64 X16; - UInt64 X17; - UInt64 X18; - UInt64 X19; - UInt64 X20; - UInt64 X21; - UInt64 X22; - UInt64 X23; - UInt64 X24; - UInt64 X25; - UInt64 X26; - UInt64 X27; - UInt64 X28; + union { + struct { + UInt64 X0; + UInt64 X1; + UInt64 X2; + UInt64 X3; + UInt64 X4; + UInt64 X5; + UInt64 X6; + UInt64 X7; + UInt64 X8; + UInt64 X9; + UInt64 X10; + UInt64 X11; + UInt64 X12; + UInt64 X13; + UInt64 X14; + UInt64 X15; + UInt64 X16; + UInt64 X17; + UInt64 X18; + UInt64 X19; + UInt64 X20; + UInt64 X21; + UInt64 X22; + UInt64 X23; + UInt64 X24; + UInt64 X25; + UInt64 X26; + UInt64 X27; + UInt64 X28; +#pragma warning(push) +#pragma warning(disable:4201) // nameless struct + }; + UInt64 X[29]; + }; +#pragma warning(pop) UInt64 Fp; // X29 UInt64 Lr; // X30 UInt64 Sp; @@ -464,8 +457,16 @@ typedef struct DECLSPEC_ALIGN(16) _CONTEXT { UIntNative GetLr() { return Lr; } } CONTEXT, *PCONTEXT; -#endif +#elif defined(_WASM_) +typedef struct DECLSPEC_ALIGN(8) _CONTEXT { + // TODO: Figure out if WebAssembly has a meaningful context available + void SetIp(UIntNative ip) { } + void SetArg0Reg(UIntNative val) { } + void SetArg1Reg(UIntNative val) { } + UIntNative GetIp() { return 0; } +} CONTEXT, *PCONTEXT; +#endif #define EXCEPTION_MAXIMUM_PARAMETERS 15 // maximum number of exception parameters @@ -509,11 +510,6 @@ typedef enum _EXCEPTION_DISPOSITION { #define NULL_AREA_SIZE (64*1024) #endif -#define GetExceptionCode _exception_code -#define GetExceptionInformation (struct _EXCEPTION_POINTERS *)_exception_info -EXTERN_C unsigned long __cdecl _exception_code(void); -EXTERN_C void * __cdecl _exception_info(void); - //#endif // !DACCESS_COMPILE #endif // !_INC_WINDOWS @@ -612,9 +608,6 @@ typedef IntNative (WINAPI *FARPROC)(); #define NOERROR 0x0 -#define TLS_OUT_OF_INDEXES 0xFFFFFFFF -#define TLS_NUM_INLINE_SLOTS 64 - #define SUSPENDTHREAD_FAILED 0xFFFFFFFF #define RESUMETHREAD_FAILED 0xFFFFFFFF @@ -818,7 +811,7 @@ typedef UInt32 (__stdcall *BackgroundCallback)(_In_opt_ void* pCallbackContext); REDHAWK_PALIMPORT bool REDHAWK_PALAPI PalStartBackgroundGCThread(_In_ BackgroundCallback callback, _In_opt_ void* pCallbackContext); REDHAWK_PALIMPORT bool REDHAWK_PALAPI PalStartFinalizerThread(_In_ BackgroundCallback callback, _In_opt_ void* pCallbackContext); -typedef UInt32 (__stdcall *PalHijackCallback)(HANDLE hThread, _In_ PAL_LIMITED_CONTEXT* pThreadContext, _In_opt_ void* pCallbackContext); +typedef UInt32_BOOL (*PalHijackCallback)(HANDLE hThread, _In_ PAL_LIMITED_CONTEXT* pThreadContext, _In_opt_ void* pCallbackContext); REDHAWK_PALIMPORT UInt32 REDHAWK_PALAPI PalHijack(HANDLE hThread, _In_ PalHijackCallback callback, _In_opt_ void* pCallbackContext); #ifdef FEATURE_ETW diff --git a/external/corert/src/Native/Runtime/PalRedhawkCommon.h b/external/corert/src/Native/Runtime/PalRedhawkCommon.h index 882e525af7..55971f12fe 100644 --- a/external/corert/src/Native/Runtime/PalRedhawkCommon.h +++ b/external/corert/src/Native/Runtime/PalRedhawkCommon.h @@ -41,6 +41,7 @@ struct AMD64_ALIGN_16 Fp128 { struct PAL_LIMITED_CONTEXT { + // Includes special registers, callee saved registers and general purpose registers used to return values from functions (not floating point return registers) #ifdef _TARGET_ARM_ UIntNative R0; UIntNative R4; @@ -63,13 +64,37 @@ struct PAL_LIMITED_CONTEXT UIntNative GetFp() const { return R7; } UIntNative GetLr() const { return LR; } void SetIp(UIntNative ip) { IP = ip; } + void SetSp(UIntNative sp) { SP = sp; } #elif defined(_TARGET_ARM64_) - // @TODO: Add ARM64 registers - UIntNative IP; - UIntNative GetIp() const { PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); } - UIntNative GetSp() const { PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); } - UIntNative GetFp() const { PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); } - UIntNative GetLr() const { PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); } + UIntNative FP; + UIntNative LR; + + UIntNative X0; + UIntNative X1; + UIntNative X19; + UIntNative X20; + UIntNative X21; + UIntNative X22; + UIntNative X23; + UIntNative X24; + UIntNative X25; + UIntNative X26; + UIntNative X27; + UIntNative X28; + + UIntNative SP; + UIntNative IP; + + UInt64 D[16 - 8]; // Only the bottom 64-bit value of the V registers V8..V15 needs to be preserved + // (V0-V7 and V16-V31 are not preserved according to the ABI spec). + + + UIntNative GetIp() const { return IP; } + UIntNative GetSp() const { return SP; } + UIntNative GetFp() const { return FP; } + UIntNative GetLr() const { return LR; } + void SetIp(UIntNative ip) { IP = ip; } + void SetSp(UIntNative sp) { SP = sp; } #elif defined(UNIX_AMD64_ABI) // Param regs: rdi, rsi, rdx, rcx, r8, r9, scratch: rax, rdx (both return val), preserved: rbp, rbx, r12-r15 UIntNative IP; @@ -88,7 +113,7 @@ struct PAL_LIMITED_CONTEXT void SetIp(UIntNative ip) { IP = ip; } void SetSp(UIntNative sp) { Rsp = sp; } UIntNative GetFp() const { return Rbp; } -#else // _TARGET_ARM_ +#elif defined(_TARGET_X86_) || defined(_TARGET_AMD64_) UIntNative IP; UIntNative Rsp; UIntNative Rbp; @@ -117,6 +142,16 @@ struct PAL_LIMITED_CONTEXT UIntNative GetIp() const { return IP; } UIntNative GetSp() const { return Rsp; } UIntNative GetFp() const { return Rbp; } + void SetIp(UIntNative ip) { IP = ip; } + void SetSp(UIntNative sp) { Rsp = sp; } +#else // _TARGET_ARM_ + UIntNative IP; + + UIntNative GetIp() const { PORTABILITY_ASSERT("GetIp"); return 0; } + UIntNative GetSp() const { PORTABILITY_ASSERT("GetSp"); return 0; } + UIntNative GetFp() const { PORTABILITY_ASSERT("GetFp"); return 0; } + void SetIp(UIntNative ip) { PORTABILITY_ASSERT("SetIp"); } + void SetSp(UIntNative sp) { PORTABILITY_ASSERT("GetSp"); } #endif // _TARGET_ARM_ }; diff --git a/external/corert/src/Native/Runtime/PalRedhawkFunctions.h b/external/corert/src/Native/Runtime/PalRedhawkFunctions.h index f030ce35e1..56d4bb7ce4 100644 --- a/external/corert/src/Native/Runtime/PalRedhawkFunctions.h +++ b/external/corert/src/Native/Runtime/PalRedhawkFunctions.h @@ -100,25 +100,6 @@ inline void * PalGetProcAddress(HANDLE arg1, const char * arg2) return GetProcAddress(arg1, arg2); } -extern "C" HANDLE __stdcall GetProcessHeap(); -inline HANDLE PalGetProcessHeap() -{ - return GetProcessHeap(); -} - - -extern "C" void* __stdcall HeapAlloc(HANDLE, UInt32, UIntNative); -inline void* PalHeapAlloc(HANDLE arg1, UInt32 arg2, UIntNative arg3) -{ - return HeapAlloc(arg1, arg2, arg3); -} - -extern "C" UInt32_BOOL __stdcall HeapFree(HANDLE, UInt32, void *); -inline UInt32_BOOL PalHeapFree(HANDLE arg1, UInt32 arg2, void * arg3) -{ - return HeapFree(arg1, arg2, arg3); -} - extern "C" UInt32_BOOL __stdcall InitializeCriticalSectionEx(CRITICAL_SECTION *, UInt32, UInt32); inline UInt32_BOOL PalInitializeCriticalSectionEx(CRITICAL_SECTION * arg1, UInt32 arg2, UInt32 arg3) { @@ -185,12 +166,6 @@ inline void PalTerminateProcess(HANDLE arg1, UInt32 arg2) TerminateProcess(arg1, arg2); } -extern "C" UInt32 __stdcall WaitForMultipleObjectsEx(UInt32, HANDLE *, UInt32_BOOL, UInt32, UInt32_BOOL); -inline UInt32 PalWaitForMultipleObjectsEx(UInt32 arg1, HANDLE * arg2, UInt32_BOOL arg3, UInt32 arg4, UInt32_BOOL arg5) -{ - return WaitForMultipleObjectsEx(arg1, arg2, arg3, arg4, arg5); -} - extern "C" UInt32 __stdcall WaitForSingleObjectEx(HANDLE, UInt32, UInt32_BOOL); inline UInt32 PalWaitForSingleObjectEx(HANDLE arg1, UInt32 arg2, UInt32_BOOL arg3) { @@ -198,12 +173,6 @@ inline UInt32 PalWaitForSingleObjectEx(HANDLE arg1, UInt32 arg2, UInt32_BOOL arg } #ifdef PAL_REDHAWK_INCLUDED -extern "C" void __stdcall GetNativeSystemInfo(SYSTEM_INFO *); -inline void PalGetNativeSystemInfo(SYSTEM_INFO * arg1) -{ - GetNativeSystemInfo(arg1); -} - extern "C" void __stdcall GetSystemTimeAsFileTime(FILETIME *); inline void PalGetSystemTimeAsFileTime(FILETIME * arg1) { diff --git a/external/corert/src/Native/Runtime/Portable/CMakeLists.txt b/external/corert/src/Native/Runtime/Portable/CMakeLists.txt index b9de41ad45..f191db46d3 100644 --- a/external/corert/src/Native/Runtime/Portable/CMakeLists.txt +++ b/external/corert/src/Native/Runtime/Portable/CMakeLists.txt @@ -11,8 +11,29 @@ add_definitions(-DUSE_PORTABLE_HELPERS) add_library(PortableRuntime STATIC ${COMMON_RUNTIME_SOURCES} ${PORTABLE_RUNTIME_SOURCES}) -# Install the static Runtime library -install (TARGETS PortableRuntime DESTINATION lib) +# Get the current list of definitions +get_compile_definitions(DEFINITIONS) +set(ASM_OFFSETS_CSPP ${RUNTIME_DIR}/../../Runtime.Base/src/AsmOffsets.cspp) + if(WIN32) - install (FILES ${CMAKE_CURRENT_BINARY_DIR}/PortableRuntime.dir/$/PortableRuntime.pdb DESTINATION lib) + set(COMPILER_LANGUAGE "") + set(PREPROCESSOR_FLAGS -EP) + set(ASM_OFFSETS_CPP ${RUNTIME_DIR}/windows/AsmOffsets.cpp) +else() + set(COMPILER_LANGUAGE -x c++) + set(PREPROCESSOR_FLAGS -E -P) + set(ASM_OFFSETS_CPP ${RUNTIME_DIR}/unix/AsmOffsets.cpp) +endif() + +add_custom_command( + # The AsmOffsets.cs is consumed later by the managed build + TARGET PortableRuntime + COMMAND ${CMAKE_CXX_COMPILER} ${COMPILER_LANGUAGE} ${DEFINITIONS} ${PREPROCESSOR_FLAGS} -I"${ARCH_SOURCES_DIR}" "${ASM_OFFSETS_CSPP}" >"${CMAKE_CURRENT_BINARY_DIR}/AsmOffsets.cs" + DEPENDS "${RUNTIME_DIR}/AsmOffsets.cpp" "${RUNTIME_DIR}/AsmOffsets.h" +) + +# Install the static Runtime library +install (TARGETS PortableRuntime DESTINATION sdk) +if(WIN32) + install (FILES ${CMAKE_CURRENT_BINARY_DIR}/PortableRuntime.dir/$/PortableRuntime.pdb DESTINATION sdk) endif() diff --git a/external/corert/src/Native/Runtime/RHCodeMan.cpp b/external/corert/src/Native/Runtime/RHCodeMan.cpp index cff8c8c90c..9eefba4e06 100644 --- a/external/corert/src/Native/Runtime/RHCodeMan.cpp +++ b/external/corert/src/Native/Runtime/RHCodeMan.cpp @@ -113,27 +113,96 @@ void ReportRegisterSet(UInt8 regSet, REGDISPLAY * pContext, GCEnumContext * hCal template PTR_PTR_Object GetRegObjectAddr(REGDISPLAY * pContext) { - PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); + switch (regNum) + { + case CSR_NUM_X19: return (PTR_PTR_Object)pContext->pX19; + case CSR_NUM_X20: return (PTR_PTR_Object)pContext->pX20; + case CSR_NUM_X21: return (PTR_PTR_Object)pContext->pX21; + case CSR_NUM_X22: return (PTR_PTR_Object)pContext->pX22; + case CSR_NUM_X23: return (PTR_PTR_Object)pContext->pX23; + case CSR_NUM_X24: return (PTR_PTR_Object)pContext->pX24; + case CSR_NUM_X25: return (PTR_PTR_Object)pContext->pX25; + case CSR_NUM_X26: return (PTR_PTR_Object)pContext->pX26; + case CSR_NUM_X27: return (PTR_PTR_Object)pContext->pX27; + case CSR_NUM_X28: return (PTR_PTR_Object)pContext->pX28; + case CSR_NUM_FP : return (PTR_PTR_Object)pContext->pFP ; + } + UNREACHABLE_MSG("unexpected CalleeSavedRegNum"); } #pragma warning(pop) PTR_PTR_Object GetRegObjectAddr(CalleeSavedRegNum regNum, REGDISPLAY * pContext) { - PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); + switch (regNum) + { + case CSR_NUM_X19: return (PTR_PTR_Object)pContext->pX19; + case CSR_NUM_X20: return (PTR_PTR_Object)pContext->pX20; + case CSR_NUM_X21: return (PTR_PTR_Object)pContext->pX21; + case CSR_NUM_X22: return (PTR_PTR_Object)pContext->pX22; + case CSR_NUM_X23: return (PTR_PTR_Object)pContext->pX23; + case CSR_NUM_X24: return (PTR_PTR_Object)pContext->pX24; + case CSR_NUM_X25: return (PTR_PTR_Object)pContext->pX25; + case CSR_NUM_X26: return (PTR_PTR_Object)pContext->pX26; + case CSR_NUM_X27: return (PTR_PTR_Object)pContext->pX27; + case CSR_NUM_X28: return (PTR_PTR_Object)pContext->pX28; + case CSR_NUM_FP : return (PTR_PTR_Object)pContext->pFP ; + } + UNREACHABLE_MSG("unexpected CalleeSavedRegNum"); } PTR_PTR_Object GetScratchRegObjectAddr(ScratchRegNum regNum, REGDISPLAY * pContext) { - PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); + switch (regNum) + { + case SR_NUM_X0: return (PTR_PTR_Object)pContext->pX0; + case SR_NUM_X1: return (PTR_PTR_Object)pContext->pX1; + case SR_NUM_X2: return (PTR_PTR_Object)pContext->pX2; + case SR_NUM_X3: return (PTR_PTR_Object)pContext->pX3; + case SR_NUM_X4: return (PTR_PTR_Object)pContext->pX4; + case SR_NUM_X5: return (PTR_PTR_Object)pContext->pX5; + case SR_NUM_X6: return (PTR_PTR_Object)pContext->pX6; + case SR_NUM_X7: return (PTR_PTR_Object)pContext->pX7; + case SR_NUM_X8: return (PTR_PTR_Object)pContext->pX8; + case SR_NUM_X9: return (PTR_PTR_Object)pContext->pX9; + case SR_NUM_X10: return (PTR_PTR_Object)pContext->pX10; + case SR_NUM_X11: return (PTR_PTR_Object)pContext->pX11; + case SR_NUM_X12: return (PTR_PTR_Object)pContext->pX12; + case SR_NUM_X13: return (PTR_PTR_Object)pContext->pX13; + case SR_NUM_X14: return (PTR_PTR_Object)pContext->pX14; + case SR_NUM_X15: return (PTR_PTR_Object)pContext->pX15; + case SR_NUM_XIP0: return (PTR_PTR_Object)pContext->pX16; + case SR_NUM_XIP1: return (PTR_PTR_Object)pContext->pX17; + case SR_NUM_LR: return (PTR_PTR_Object)pContext->pLR; + } + UNREACHABLE_MSG("unexpected ScratchRegNum"); } -void ReportRegisterSet(UInt8 regSet, REGDISPLAY * pContext, GCEnumContext * hCallback) +void ReportRegisterSet(UInt8 firstEncByte, REGDISPLAY * pContext, GCEnumContext * hCallback, PTR_UInt8 & pCursor) { - PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); + // 2. 00lvRRRR [RRRRRRRR] - normal "register set" encoding, pinned and interior attributes both false + // a. l - this is the last descriptor + // b. v - extra byte follows + // c. RRRR - register mask for { lr, x19-x21 } + // d. RRRRRRRR - register mask for { x22-x28, fp } iff 'v' is 1 + + UInt16 regSet = (firstEncByte & 0xF); + if (firstEncByte & 0x10) { regSet |= (*pCursor++ << 4); } + + ASSERT(!(regSet & CSR_MASK_LR)); + if (regSet & CSR_MASK_X19) { ReportObject(hCallback, GetRegObjectAddr(pContext), 0); } + if (regSet & CSR_MASK_X20) { ReportObject(hCallback, GetRegObjectAddr(pContext), 0); } + if (regSet & CSR_MASK_X21) { ReportObject(hCallback, GetRegObjectAddr(pContext), 0); } + if (regSet & CSR_MASK_X22) { ReportObject(hCallback, GetRegObjectAddr(pContext), 0); } + if (regSet & CSR_MASK_X23) { ReportObject(hCallback, GetRegObjectAddr(pContext), 0); } + if (regSet & CSR_MASK_X24) { ReportObject(hCallback, GetRegObjectAddr(pContext), 0); } + if (regSet & CSR_MASK_X25) { ReportObject(hCallback, GetRegObjectAddr(pContext), 0); } + if (regSet & CSR_MASK_X26) { ReportObject(hCallback, GetRegObjectAddr(pContext), 0); } + if (regSet & CSR_MASK_X27) { ReportObject(hCallback, GetRegObjectAddr(pContext), 0); } + if (regSet & CSR_MASK_X28) { ReportObject(hCallback, GetRegObjectAddr(pContext), 0); } + if (regSet & CSR_MASK_FP ) { ReportObject(hCallback, GetRegObjectAddr(pContext), 0); } } - -#else // _TARGET_ARM_ && _TARGET_ARM64_ +#else // _TARGET_ARM_ || _TARGET_ARM64_ #pragma warning(push) #pragma warning(disable:4127) // conditional expression is constant @@ -209,19 +278,26 @@ void ReportRegisterSet(UInt8 regSet, REGDISPLAY * pContext, GCEnumContext * hCal #endif // _TARGET_ARM_ -void ReportRegister(UInt8 regEnc, REGDISPLAY * pContext, GCEnumContext * hCallback) +void ReportRegister(UInt8 regEnc, REGDISPLAY * pContext, GCEnumContext * hCallback, PTR_UInt8 & pCursor) { - // 3. 01liprrr - more general register encoding with pinned and interior attributes + // 3. 01liprrr [ARM64 register] - more general register encoding with pinned and interior attributes // a. l - last descriptor // b. i - interior // c. p - pinned - // d. rrr - register number { rbx, rsi, rdi, rbp, r12, r13, r14, r15 }, ARM = { r4-r11 } + // d. rrr - register number { rbx, rsi, rdi, rbp, r12, r13, r14, r15 }, ARM = { r4-r11 }, ARM64 = { x19-x25 } + // ARM64: if rrr = 0, the register number { x26-x28, fp } follows in the next byte UInt32 flags = 0; if (regEnc & 0x08) { flags |= GC_CALL_PINNED; } if (regEnc & 0x10) { flags |= GC_CALL_INTERIOR; } - PTR_PTR_Object pRoot = GetRegObjectAddr((CalleeSavedRegNum)(regEnc & 0x07), pContext); + UInt8 regNum = (regEnc & 0x07); +#ifdef _TARGET_ARM64_ + if (!regNum) { regNum = *pCursor++; } +#else + UNREFERENCED_PARAMETER(pCursor); +#endif + PTR_PTR_Object pRoot = GetRegObjectAddr((CalleeSavedRegNum)regNum, pContext); ReportObject(hCallback, pRoot, flags); } @@ -239,7 +315,10 @@ void ReportLocalSlot(UInt32 slotNum, REGDISPLAY * pContext, GCEnumContext * hCal // ARM places the FP at the top of the locals area. rbpOffset = pHeader->GetFrameSize() - ((slotNum + 1) * sizeof(void *)); #elif defined(_TARGET_ARM64_) - PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); + if (pHeader->AreFPLROnTop()) + rbpOffset = -(Int32)((slotNum + 1) * sizeof(void *)); + else + rbpOffset = ((slotNum + 2) * sizeof(void *)); #else # ifdef _TARGET_AMD64_ if (pHeader->GetFramePointerOffset() != 0) @@ -357,26 +436,60 @@ void ReportStackSlots(UInt8 firstEncByte, REGDISPLAY * pContext, GCEnumContext * } } +// Reads a 7-bit-encoded register mask: +// - 0RRRRRRR for non-ARM64 registers and { x0-x6 } ARM64 registers +// - 1RRRRRRR 0RRRRRRR for { x0-x13 } ARM64 registers +// - 1RRRRRRR 1RRRRRRR 000RRRRR for { x0-x15, xip0, xip1, lr } ARM64 registers +UInt32 ReadRegisterMaskBy7Bit(PTR_UInt8 & pCursor) +{ +#ifndef _TARGET_ARM64_ + ASSERT(!(*pCursor & 0x80)); + return *pCursor++; +#else // !_TARGET_ARM64_ + UInt32 byte0 = *pCursor++; + if (!(byte0 & 0x80)) + { + return byte0; + } + + UInt32 byte1 = *pCursor++; + if (!(byte1 & 0x80)) + { + // XOR with 0x80 discards the most significant bit of byte0 + return (byte1 << 7) ^ byte0 ^ 0x80; + } + + UInt32 byte2 = *pCursor++; + ASSERT(!(byte2 & 0xe0)); + // XOR with 0x4080 discards the most significant bits of byte0 and byte1 + return (byte2 << 14) ^ (byte1 << 7) ^ byte0 ^ 0x4080; +#endif // !_TARGET_ARM64_ +} + void ReportScratchRegs(UInt8 firstEncByte, REGDISPLAY * pContext, GCEnumContext * hCallback, PTR_UInt8 & pCursor) { - // 7. 11lip010 0RRRRRRR [0IIIIIII] [0PPPPPPP] - live scratch reg reporting, this uses the SP-xxx encoding - // from #6 since we cannot have stack locations at negative - // offsets from SP. + // 7. 11lip010 0RRRRRRR [0IIIIIII] [0PPPPPPP] - live scratch reg reporting, this uses the SP-xxx encoding + // from #6 since we cannot have stack locations at negative + // offsets from SP. // a. l - last descriptor // b. i - interior byte present // c. p - pinned byte present - // d. RRRRRRR - scratch register mask for { rax, rcx, rdx, r8, r9, r10, r11 }, ARM = { r0-r3, r12 } - // e. IIIIIII - interior scratch register mask for { rax, rcx, rdx, r8, r9, r10, r11 } iff 'i' is 1 - // f. PPPPPPP - pinned scratch register mask for { rax, rcx, rdx, r8, r9, r10, r11 } iff 'p' is 1 + // d. RRRRRRR - scratch register mask for { rax, rcx, rdx, r8, r9, r10, r11 }, ARM = { r0-r3, r12, lr } + // e. IIIIIII - interior scratch register mask for { rax, rcx, rdx, r8, r9, r10, r11 } iff 'i' is 1 + // f. PPPPPPP - pinned scratch register mask for { rax, rcx, rdx, r8, r9, r10, r11 } iff 'p' is 1 // + // For ARM64 the scheme above is extended to support the bigger register set: + // - 11lip010 0RRRRRRR [0IIIIIII] [0PPPPPPP] for { x0-x6 } + // - 11lip010 1RRRRRRR 0RRRRRRR [[1IIIIIII] 0IIIIIII] [[1PPPPPPP] 0PPPPPPP] for { x0-x13 } + // - 11lip010 1RRRRRRR 1RRRRRRR 000RRRRR [0*2(1IIIIIII) 000IIIII] [0*2(1PPPPPPP) 000PPPPP] for { x0-x15, xip0, xip1, lr } - UInt8 regs = *pCursor++; - UInt8 byrefRegs = (firstEncByte & 0x10) ? *pCursor++ : 0; - UInt8 pinnedRegs = (firstEncByte & 0x08) ? *pCursor++ : 0; + UInt32 regs = ReadRegisterMaskBy7Bit(pCursor); + UInt32 byrefRegs = (firstEncByte & 0x10) ? ReadRegisterMaskBy7Bit(pCursor) : 0; + UInt32 pinnedRegs = (firstEncByte & 0x08) ? ReadRegisterMaskBy7Bit(pCursor) : 0; for (UInt32 reg = 0; reg < RBM_SCRATCH_REG_COUNT; reg++) { - UInt8 regMask = (1 << reg); + UInt32 regMask = (1 << reg); if (regs & regMask) { @@ -514,24 +627,32 @@ ContinueUnconditionally: // ------------------------------------------------------------------------------------------------------- // // 1. Call sites with nothing to report are not encoded - // + // // 2. 00lRRRRR - normal "register set" encoding, pinned and interior attributes both false // a. l - this is the last descriptor // b. RRRRR - this is the register mask for { rbx, rsi, rdi, rbp, r12 }, ARM = { r4-r8 } - // - // 3. 01liprrr - more general register encoding with pinned and interior attributes + // + // For ARM64 the scheme above is extended to support the bigger register set: + // 00lvRRRR [RRRRRRRR] - normal "register set" encoding, pinned and interior attributes both false + // a. l - this is the last descriptor + // b. v - extra byte follows + // c. RRRR - register mask for { lr, x19-x21 } + // d. RRRRRRRR - register mask for { x22-x28, fp } iff 'v' is 1 + // + // 3. 01liprrr [ARM64 register] - more general register encoding with pinned and interior attributes // a. l - last descriptor // b. i - interior // c. p - pinned - // d. rrr - register number { rbx, rsi, rdi, rbp, r12, r13, r14, r15 }, ARM = { r4-r11 } - // + // d. rrr - register number { rbx, rsi, rdi, rbp, r12, r13, r14, r15 }, ARM = { r4-r11 }, ARM64 = { x19-x25 } + // ARM64: if rrr = 0, the register number { x26-x28, fp } follows in the next byte + // // 4. 10l1SSSS - "local stack slot set" encoding, pinned and interior attributes both false // a. l - last descriptor // b. SSSS - set of "local slots" #0 - #3 - local slot 0 is at offset -8 from the last pushed // callee saved register, local slot 1 is at offset - 16, etc - in other words, these are the // slots normally used for locals. The non-sensical encoding with SSSS = 0000 is reserved for // the "common vars" case under 8 below. - // + // // 5. 10l0ssss - "local slot" encoding // a. l - last descriptor // b. ssss - "local slot" #4 - #19 @@ -549,20 +670,25 @@ ContinueUnconditionally: // locations 0x20, 0x28, 0x38, you would give a (starting) offset of 0x20 and a mask of // 000000101 = 0x05. Up to 33 stack locations can be described. // - // 7. 11lip010 0RRRRRRR [0IIIIIII] [0PPPPPPP] - live scratch reg reporting, this uses the SP-xxx encoding - // from #6 since we cannot have stack locations at negative - // offsets from SP. + // 7. 11lip010 0RRRRRRR [0IIIIIII] [0PPPPPPP] - live scratch reg reporting, this uses the SP-xxx encoding + // from #6 since we cannot have stack locations at negative + // offsets from SP. // a. l - last descriptor // b. i - interior byte present // c. p - pinned byte present - // d. RRRRRRR - scratch register mask for { rax, rcx, rdx, r8, r9, r10, r11 }, ARM = { r0-r3, r12 } - // e. IIIIIII - interior scratch register mask for { rax, rcx, rdx, r8, r9, r10, r11 } iff 'i' is 1 - // f. PPPPPPP - pinned scratch register mask for { rax, rcx, rdx, r8, r9, r10, r11 } iff 'p' is 1 + // d. RRRRRRR - scratch register mask for { rax, rcx, rdx, r8, r9, r10, r11 }, ARM = { r0-r3, r12, lr } + // e. IIIIIII - interior scratch register mask for { rax, rcx, rdx, r8, r9, r10, r11 } iff 'i' is 1 + // f. PPPPPPP - pinned scratch register mask for { rax, rcx, rdx, r8, r9, r10, r11 } iff 'p' is 1 // - // 8. 10z10000 [ common var index ] - "common var" encoding - the common var index references a root string - // common to several call sites + // For ARM64 the scheme above is extended to support the bigger register set: + // - 11lip010 0RRRRRRR [0IIIIIII] [0PPPPPPP] for { x0-x6 } + // - 11lip010 1RRRRRRR 0RRRRRRR [[1IIIIIII] 0IIIIIII] [[1PPPPPPP] 0PPPPPPP] for { x0-x13 } + // - 11lip010 1RRRRRRR 1RRRRRRR 000RRRRR [0*2(1IIIIIII) 000IIIII] [0*2(1PPPPPPP) 000PPPPP] for { x0-x15, xip0, xip1, lr } + // + // 8. 10z10000 [ common var index ] - "common var" encoding - the common var index references a root string + // common to several call sites // a. z - common var index is 0 - // b. common var index - 0-based index referring to one of the "common var" root strings. + // b. common var index - 0-based index referring to one of the "common var" root strings. // only present if z-bit is 0 // // this encoding is case 4, "local stack slot set", with the set SSSS = 0 @@ -581,11 +707,15 @@ ContinueUnconditionally: { case 0x00: // case 2 -- "register set" +#ifndef _TARGET_ARM64_ ReportRegisterSet(b, pContext, hCallback); +#else + ReportRegisterSet(b, pContext, hCallback, pCursor); +#endif break; case 0x40: // case 3 -- "register" - ReportRegister(b, pContext, hCallback); + ReportRegister(b, pContext, hCallback, pCursor); break; case 0x80: // case 4 -- "local slot set" @@ -661,47 +791,34 @@ ContinueUnconditionally: bool EECodeManager::UnwindStackFrame(GCInfoHeader * pInfoHeader, REGDISPLAY * pContext) { -#ifdef _TARGET_ARM64_ - PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); -#endif - // We could implement this unwind if we wanted, but there really isn't any reason ASSERT(pInfoHeader->GetReturnKind() != GCInfoHeader::MRK_ReturnsToNative); bool ebpFrame = pInfoHeader->HasFramePointer(); -#ifdef _TARGET_X86_ - // @TODO .. ESP-based methods with stack changes - ASSERT_MSG(ebpFrame || !pInfoHeader->HasStackChanges(), "NYI -- ESP-based methods with stack changes"); -#endif // _TARGET_X86_ - // // Just unwind based on the info header // Int32 saveSize = pInfoHeader->GetPreservedRegsSaveSize(); UIntNative rawRSP; + +#if defined(_TARGET_AMD64_) + if (ebpFrame) { -#ifdef _TARGET_ARM_ - rawRSP = pContext->GetFP() + pInfoHeader->GetFrameSize(); -#elif defined(_TARGET_ARM64_) - PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); -#else saveSize -= sizeof(void *); // don't count RBP Int32 framePointerOffset = 0; -#ifdef _TARGET_AMD64_ framePointerOffset = pInfoHeader->GetFramePointerOffset(); -#endif rawRSP = pContext->GetFP() - saveSize - framePointerOffset; -#endif } else { rawRSP = pContext->GetSP() + pInfoHeader->GetFrameSize(); } + PTR_UIntNative RSP = (PTR_UIntNative)rawRSP; -#if defined(_TARGET_AMD64_) && !defined(UNIX_AMD64_ABI) +#if !defined(UNIX_AMD64_ABI) if (pInfoHeader->HasSavedXmmRegs()) { typedef DPTR(Fp128) PTR_Fp128; @@ -719,31 +836,11 @@ bool EECodeManager::UnwindStackFrame(GCInfoHeader * pInfoHeader, } } } -#elif defined(_TARGET_ARM_) - UInt8 vfpRegPushedCount = pInfoHeader->GetVfpRegPushedCount(); - UInt8 vfpRegFirstPushed = pInfoHeader->GetVfpRegFirstPushed(); - UInt32 regIndex = vfpRegFirstPushed - 8; - while (vfpRegPushedCount-- > 0) - { - ASSERT(regIndex < 8); - pContext->D[regIndex] = *(PTR_UInt64)RSP; - regIndex++; - RSP = (PTR_UIntNative)((PTR_UInt8)RSP + sizeof(UInt64)); - } -#elif defined(_TARGET_ARM64_) - PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); -#endif - -#if defined(_TARGET_X86_) - int registerSaveDisplacement = 0; - // registers saved at bottom of frame in Project N - registerSaveDisplacement = pInfoHeader->GetFrameSize(); #endif if (saveSize > 0) { CalleeSavedRegMask regMask = pInfoHeader->GetSavedRegs(); -#ifdef _TARGET_AMD64_ if (regMask & CSR_MASK_R15) { pContext->pR15 = RSP++; } if (regMask & CSR_MASK_R14) { pContext->pR14 = RSP++; } if (regMask & CSR_MASK_R13) { pContext->pR13 = RSP++; } @@ -751,35 +848,62 @@ bool EECodeManager::UnwindStackFrame(GCInfoHeader * pInfoHeader, if (regMask & CSR_MASK_RDI) { pContext->pRdi = RSP++; } if (regMask & CSR_MASK_RSI) { pContext->pRsi = RSP++; } if (regMask & CSR_MASK_RBX) { pContext->pRbx = RSP++; } + } + + if (ebpFrame) + { + pContext->pRbp = RSP++; + } + + // handle dynamic frame alignment + if (pInfoHeader->HasDynamicAlignment()) + { + UNREACHABLE_MSG("Dynamic frame alignment not supported on this platform"); + } + + pContext->SetAddrOfIP((PTR_PCODE)RSP); // save off the return address location + pContext->SetIP(*RSP++); // pop the return address + #elif defined(_TARGET_X86_) + + // @TODO .. ESP-based methods with stack changes + ASSERT_MSG(ebpFrame || !pInfoHeader->HasStackChanges(), "NYI -- ESP-based methods with stack changes"); + + if (ebpFrame) + { + saveSize -= sizeof(void *); // don't count RBP + Int32 framePointerOffset = 0; + rawRSP = pContext->GetFP() - saveSize - framePointerOffset; + } + else + { + rawRSP = pContext->GetSP() + pInfoHeader->GetFrameSize(); + } + + PTR_UIntNative RSP = (PTR_UIntNative)rawRSP; + + int registerSaveDisplacement = 0; + // registers saved at bottom of frame in Project N + registerSaveDisplacement = pInfoHeader->GetFrameSize(); + + if (saveSize > 0) + { + CalleeSavedRegMask regMask = pInfoHeader->GetSavedRegs(); ASSERT_MSG(ebpFrame || !(regMask & CSR_MASK_RBP), "We should never use EBP as a preserved register"); ASSERT_MSG(!(regMask & CSR_MASK_RBX) || !pInfoHeader->HasDynamicAlignment(), "Can't have EBX as preserved regster and dynamic alignment frame pointer") if (regMask & CSR_MASK_RBX) { pContext->pRbx = (PTR_UIntNative)((PTR_UInt8)RSP - registerSaveDisplacement); ++RSP; } // registers saved at bottom of frame if (regMask & CSR_MASK_RSI) { pContext->pRsi = (PTR_UIntNative)((PTR_UInt8)RSP - registerSaveDisplacement); ++RSP; } // registers saved at bottom of frame if (regMask & CSR_MASK_RDI) { pContext->pRdi = (PTR_UIntNative)((PTR_UInt8)RSP - registerSaveDisplacement); ++RSP; } // registers saved at bottom of frame -#elif defined(_TARGET_ARM_) - if (regMask & CSR_MASK_R4) { pContext->pR4 = RSP++; } - if (regMask & CSR_MASK_R5) { pContext->pR5 = RSP++; } - if (regMask & CSR_MASK_R6) { pContext->pR6 = RSP++; } - if (regMask & CSR_MASK_R7) { pContext->pR7 = RSP++; } - if (regMask & CSR_MASK_R8) { pContext->pR8 = RSP++; } - if (regMask & CSR_MASK_R9) { pContext->pR9 = RSP++; } - if (regMask & CSR_MASK_R10) { pContext->pR10 = RSP++; } - if (regMask & CSR_MASK_R11) { pContext->pR11 = RSP++; } -#elif defined(_TARGET_ARM64_) - PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); -#endif // _TARGET_AMD64_ } -#if !defined(_TARGET_ARM_) && !defined(_TARGET_ARM64_) if (ebpFrame) + { pContext->pRbp = RSP++; -#endif + } // handle dynamic frame alignment if (pInfoHeader->HasDynamicAlignment()) { -#ifdef _TARGET_X86_ ASSERT_MSG(pInfoHeader->GetParamPointerReg() == RN_EBX, "NYI: non-EBX param pointer"); // For x86 dynamically-aligned frames, we have two frame pointers, like this: // @@ -796,22 +920,138 @@ bool EECodeManager::UnwindStackFrame(GCInfoHeader * pInfoHeader, // which previous EBX was saved. RSP = (PTR_UIntNative)*(pContext->pRbx); // RSP now points to EBX save location pContext->pRbx = RSP++; // RSP now points to original caller pushed return address. -#else - UNREACHABLE_MSG("Dynamic frame alignment not supported on this platform"); -#endif } pContext->SetAddrOfIP((PTR_PCODE)RSP); // save off the return address location - pContext->SetIP(*RSP++); // pop the return address -#ifdef _TARGET_X86_ + pContext->SetIP(*RSP++); // pop the return address + // pop the callee-popped args RSP += (pInfoHeader->GetReturnPopSize() / sizeof(UIntNative)); -#endif -#ifdef _TARGET_ARM_ +#elif defined(_TARGET_ARM_) + + if (ebpFrame) + { + rawRSP = pContext->GetFP() + pInfoHeader->GetFrameSize(); + } + else + { + rawRSP = pContext->GetSP() + pInfoHeader->GetFrameSize(); + } + + PTR_UIntNative RSP = (PTR_UIntNative)rawRSP; + + UInt8 vfpRegPushedCount = pInfoHeader->GetVfpRegPushedCount(); + UInt8 vfpRegFirstPushed = pInfoHeader->GetVfpRegFirstPushed(); + UInt32 regIndex = vfpRegFirstPushed - 8; + while (vfpRegPushedCount-- > 0) + { + ASSERT(regIndex < 8); + pContext->D[regIndex] = *(PTR_UInt64)RSP; + regIndex++; + RSP = (PTR_UIntNative)((PTR_UInt8)RSP + sizeof(UInt64)); + } + + if (saveSize > 0) + { + CalleeSavedRegMask regMask = pInfoHeader->GetSavedRegs(); + if (regMask & CSR_MASK_R4) { pContext->pR4 = RSP++; } + if (regMask & CSR_MASK_R5) { pContext->pR5 = RSP++; } + if (regMask & CSR_MASK_R6) { pContext->pR6 = RSP++; } + if (regMask & CSR_MASK_R7) { pContext->pR7 = RSP++; } + if (regMask & CSR_MASK_R8) { pContext->pR8 = RSP++; } + if (regMask & CSR_MASK_R9) { pContext->pR9 = RSP++; } + if (regMask & CSR_MASK_R10) { pContext->pR10 = RSP++; } + if (regMask & CSR_MASK_R11) { pContext->pR11 = RSP++; } + } + + // handle dynamic frame alignment + if (pInfoHeader->HasDynamicAlignment()) + { + UNREACHABLE_MSG("Dynamic frame alignment not supported on this platform"); + } + + pContext->SetAddrOfIP((PTR_PCODE)RSP); // save off the return address location + pContext->SetIP(*RSP++); // pop the return address + RSP += pInfoHeader->ParmRegsPushedCount(); + #elif defined(_TARGET_ARM64_) - PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); + + if (ebpFrame) + { + rawRSP = pContext->GetFP(); + } + else + { + rawRSP = pContext->GetSP(); + } + + PTR_UIntNative RSP = (PTR_UIntNative)rawRSP; + + if (ebpFrame) + { + pContext->pFP = RSP++; + pContext->SetAddrOfIP((PTR_PCODE)RSP); // save off the return address location + pContext->SetIP(*RSP++); // pop the return address + } + + if (!pInfoHeader->AreFPLROnTop()) + { + RSP = (PTR_UIntNative)(rawRSP + pInfoHeader->GetFrameSize()); + ASSERT(!pInfoHeader->HasGSCookie()); + } + + if (saveSize > 0) + { + CalleeSavedRegMask regMask = pInfoHeader->GetSavedRegs(); + if (regMask & CSR_MASK_LR) + { + ASSERT_MSG(!ebpFrame, "Chained frame cannot have CSR_MASK_LR mask set"); + pContext->SetAddrOfIP((PTR_PCODE)RSP); // save off the return address location + pContext->SetIP(*RSP++); // pop the return address + } + if (regMask & CSR_MASK_X19) { pContext->pX19 = RSP++; } + if (regMask & CSR_MASK_X20) { pContext->pX20 = RSP++; } + if (regMask & CSR_MASK_X21) { pContext->pX21 = RSP++; } + if (regMask & CSR_MASK_X22) { pContext->pX22 = RSP++; } + if (regMask & CSR_MASK_X23) { pContext->pX23 = RSP++; } + if (regMask & CSR_MASK_X24) { pContext->pX24 = RSP++; } + if (regMask & CSR_MASK_X25) { pContext->pX25 = RSP++; } + if (regMask & CSR_MASK_X26) { pContext->pX26 = RSP++; } + if (regMask & CSR_MASK_X27) { pContext->pX27 = RSP++; } + if (regMask & CSR_MASK_X28) { pContext->pX28 = RSP++; } + if (regMask & CSR_MASK_FP ) { ASSERT(!ebpFrame); pContext->pFP = RSP++; } + } + + UInt8 vfpRegMask = (UInt8)pInfoHeader->GetVfpRegsPushedMask(); + if (vfpRegMask) + { + UInt8 regIndex = 0; // Indices 0-7 correspond to D8-D15 + do + { + ASSERT(regIndex < 8); + if (vfpRegMask & 1) + pContext->D[regIndex] = *RSP++; + + vfpRegMask >>= 1; + regIndex++; + } while (vfpRegMask); + } + + + // handle dynamic frame alignment + if (pInfoHeader->HasDynamicAlignment()) + { + UNREACHABLE_MSG("Dynamic frame alignment not supported on this platform"); + } + + RSP += pInfoHeader->ParmRegsPushedCount(); + +#else + +#error NYI - For this arch + #endif pContext->SetSP((UIntNative) dac_cast(RSP)); @@ -942,12 +1182,16 @@ PTR_PTR_VOID EECodeManager::GetReturnAddressLocationForHijack( } #ifdef _ARM_ - // We cannot get the return addres unless LR has + // We cannot get the return address unless LR has // be saved in the prolog. if (!pHeader->IsRegSaved(CSR_MASK_LR)) return NULL; #elif defined(_ARM64_) - PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); + // We can get return address if LR was saved either with FP or on its own: + bool ebpFrame = pHeader->HasFramePointer(); + if (!ebpFrame && !pHeader->IsRegSaved(CSR_MASK_LR)) { + return NULL; + } #endif // _ARM_ void ** ppvResult; @@ -960,7 +1204,8 @@ PTR_PTR_VOID EECodeManager::GetReturnAddressLocationForHijack( // Disable hijacking from epilogs on ARM until we implement GetReturnAddressLocationFromEpilog. return NULL; #elif defined(_ARM64_) - PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); + // Disable hijacking from epilogs on ARM64: + return NULL; #else ppvResult = GetReturnAddressLocationFromEpilog(pHeader, pContext, epilogOffset, epilogSize); // Early out if GetReturnAddressLocationFromEpilog indicates a non-hijackable epilog (e.g. exception @@ -978,7 +1223,7 @@ PTR_PTR_VOID EECodeManager::GetReturnAddressLocationForHijack( ppvResult = (void **)((*pContext->pR11) + sizeof(void *)); goto Finished; #elif _ARM64_ - PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); + ppvResult = (void **)(pContext->pLR); goto Finished; #else @@ -1496,6 +1741,8 @@ void ** EECodeManager::GetReturnAddressLocationFromEpilog(GCInfoHeader * pInfoHe return NULL; #elif defined(_ARM64_) + UNREFERENCED_PARAMETER(pInfoHeader); + UNREFERENCED_PARAMETER(pbEpilog); PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); #endif @@ -1579,6 +1826,7 @@ void CheckHijackInEpilog(GCInfoHeader * pInfoHeader, Code * pEpilog, Code * pEpi context.pR11 = &RBP_TEST_VAL; context.SP = RSP_TEST_VAL; #elif defined(_ARM64_) + UNREFERENCED_PARAMETER(RBP_TEST_VAL); PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); #endif @@ -2229,6 +2477,9 @@ bool VerifyEpilogBytesARM(GCInfoHeader * pInfoHeader, Code * pEpilogStart, UInt3 #elif defined(_ARM64_) bool VerifyEpilogBytesARM64(GCInfoHeader * pInfoHeader, Code * pEpilogStart, UInt32 epilogSize) { + UNREFERENCED_PARAMETER(pInfoHeader); + UNREFERENCED_PARAMETER(pEpilogStart); + UNREFERENCED_PARAMETER(epilogSize); PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); } #endif // _ARM_ diff --git a/external/corert/src/Native/Runtime/RWLock.cpp b/external/corert/src/Native/Runtime/RWLock.cpp index 39d4c61ad0..aae8de69fe 100644 --- a/external/corert/src/Native/Runtime/RWLock.cpp +++ b/external/corert/src/Native/Runtime/RWLock.cpp @@ -23,6 +23,7 @@ #include "event.h" #include "RWLock.h" #include "threadstore.h" +#include "threadstore.inl" #include "RuntimeInstance.h" // Configurable constants used across our spin locks @@ -81,7 +82,7 @@ ReaderWriterLock::WriteHolder::~WriteHolder() #endif // !DACCESS_COMPILE } -ReaderWriterLock::ReaderWriterLock() : +ReaderWriterLock::ReaderWriterLock(bool fBlockOnGc) : m_RWLock(0) #if 0 , m_WriterWaiting(false) @@ -92,6 +93,7 @@ ReaderWriterLock::ReaderWriterLock() : (PalGetProcessCpuCount() == 1) ? 0 : #endif 4000); + m_fBlockOnGc = fBlockOnGc; } @@ -247,6 +249,13 @@ void ReaderWriterLock::AcquireWriteLock() if (TryAcquireWriteLock()) return; + // Do not spin if GC is in progress because the lock will not + // be released until GC is finished. + if (m_fBlockOnGc && ThreadStore::IsTrapThreadsRequested()) + { + RedhawkGCInterface::WaitForGCCompletion(); + } + if (g_SystemInfo.dwNumberOfProcessors <= 1) { break; diff --git a/external/corert/src/Native/Runtime/RWLock.h b/external/corert/src/Native/Runtime/RWLock.h index 066fa4fdd5..6a3939cae7 100644 --- a/external/corert/src/Native/Runtime/RWLock.h +++ b/external/corert/src/Native/Runtime/RWLock.h @@ -6,6 +6,8 @@ class ReaderWriterLock { volatile Int32 m_RWLock; // lock used for R/W synchronization Int32 m_spinCount; // spin count for a reader waiting for a writer to release the lock + bool m_fBlockOnGc; // True if the spinning writers should block when GC is in progress + #if 0 // used to prevent writers from being starved by readers @@ -36,7 +38,7 @@ public: ~WriteHolder(); }; - ReaderWriterLock(); + ReaderWriterLock(bool fBlockOnGc = false); void AcquireReadLock(); void ReleaseReadLock(); diff --git a/external/corert/src/Native/Runtime/RuntimeInstance.cpp b/external/corert/src/Native/Runtime/RuntimeInstance.cpp index 7958a7a1d2..277e75c13b 100644 --- a/external/corert/src/Native/Runtime/RuntimeInstance.cpp +++ b/external/corert/src/Native/Runtime/RuntimeInstance.cpp @@ -167,21 +167,38 @@ ICodeManager * RuntimeInstance::FindCodeManagerByAddress(PTR_VOID pvAddress) return pModule; } - // TODO: JIT support in DAC + // TODO: ICodeManager support in DAC #ifndef DACCESS_COMPILE -#ifdef FEATURE_DYNAMIC_CODE for (CodeManagerEntry * pEntry = m_CodeManagerList.GetHead(); pEntry != NULL; pEntry = pEntry->m_pNext) { if (dac_cast(pvAddress) - dac_cast(pEntry->m_pvStartRange) < pEntry->m_cbRange) return pEntry->m_pCodeManager; } -#endif #endif return NULL; } -GPTR_DECL(RuntimeInstance, g_pTheRuntimeInstance); +PTR_UInt8 RuntimeInstance::GetTargetOfUnboxingAndInstantiatingStub(PTR_VOID ControlPC) +{ + ICodeManager * pCodeManager = FindCodeManagerByAddress(ControlPC); + if (pCodeManager != NULL) + { + PTR_UInt8 pData = (PTR_UInt8)pCodeManager->GetAssociatedData(ControlPC); + if (pData != NULL) + { + UInt8 flags = *pData++; + + if ((flags & (UInt8)AssociatedDataFlags::HasUnboxingStubTarget) != 0) + return pData + *dac_cast(pData); + } + } + + return NULL; +} + +GPTR_IMPL_INIT(RuntimeInstance, g_pTheRuntimeInstance, NULL); + PTR_RuntimeInstance GetRuntimeInstance() { return g_pTheRuntimeInstance; @@ -257,6 +274,14 @@ void RuntimeInstance::EnumAllStaticGCRefs(void * pfnCallback, void * pvCallbackD EnumThreadStaticGCRefDescs(pfnCallback, pvCallbackData); } +void RuntimeInstance::SetLoopHijackFlags(UInt32 flag) +{ + for (TypeManagerList::Iterator iter = m_TypeManagerList.Begin(); iter != m_TypeManagerList.End(); iter++) + { + iter->m_pTypeManager->SetLoopHijackFlag(flag); + } +} + RuntimeInstance::OsModuleList* RuntimeInstance::GetOsModuleList() { return dac_cast(dac_cast(this) + offsetof(RuntimeInstance, m_OsModuleList)); @@ -288,7 +313,8 @@ RuntimeInstance::RuntimeInstance() : m_pStaticGCRefsDescChunkList(NULL), m_pThreadStaticGCRefsDescChunkList(NULL), m_pGenericUnificationHashtable(NULL), - m_conservativeStackReportingEnabled(false) + m_conservativeStackReportingEnabled(false), + m_pUnboxingStubsRegion(NULL) { } @@ -306,10 +332,9 @@ HANDLE RuntimeInstance::GetPalInstance() return m_hPalInstance; } -bool RuntimeInstance::EnableConservativeStackReporting() +void RuntimeInstance::EnableConservativeStackReporting() { m_conservativeStackReportingEnabled = true; - return true; } EXTERN_C void REDHAWK_CALLCONV RhpSetHaveNewClasslibs(); @@ -368,7 +393,6 @@ void RuntimeInstance::UnregisterModule(Module *pModule) pModule->Destroy(); } -#ifdef FEATURE_DYNAMIC_CODE bool RuntimeInstance::RegisterCodeManager(ICodeManager * pCodeManager, PTR_VOID pvStartRange, UInt32 cbRange) { CodeManagerEntry * pEntry = new (nothrow) CodeManagerEntry(); @@ -420,7 +444,46 @@ extern "C" void __stdcall UnregisterCodeManager(ICodeManager * pCodeManager) { return GetRuntimeInstance()->UnregisterCodeManager(pCodeManager); } -#endif + +bool RuntimeInstance::RegisterUnboxingStubs(PTR_VOID pvStartRange, UInt32 cbRange) +{ + ASSERT(pvStartRange != NULL && cbRange > 0); + + UnboxingStubsRegion * pEntry = new (nothrow) UnboxingStubsRegion(); + if (NULL == pEntry) + return false; + + pEntry->m_pRegionStart = pvStartRange; + pEntry->m_cbRegion = cbRange; + + do + { + pEntry->m_pNextRegion = m_pUnboxingStubsRegion; + } + while (PalInterlockedCompareExchangePointer((void *volatile *)&m_pUnboxingStubsRegion, pEntry, pEntry->m_pNextRegion) != pEntry->m_pNextRegion); + + return true; +} + +bool RuntimeInstance::IsUnboxingStub(UInt8* pCode) +{ + UnboxingStubsRegion * pCurrent = m_pUnboxingStubsRegion; + while (pCurrent != NULL) + { + UInt8* pUnboxingStubsRegion = dac_cast(pCurrent->m_pRegionStart); + if (pCode >= pUnboxingStubsRegion && pCode < (pUnboxingStubsRegion + pCurrent->m_cbRegion)) + return true; + + pCurrent = pCurrent->m_pNextRegion; + } + + return false; +} + +extern "C" bool __stdcall RegisterUnboxingStubs(PTR_VOID pvStartRange, UInt32 cbRange) +{ + return GetRuntimeInstance()->RegisterUnboxingStubs(pvStartRange, cbRange); +} bool RuntimeInstance::RegisterTypeManager(TypeManager * pTypeManager) { @@ -480,17 +543,19 @@ RuntimeInstance::TypeManagerList& RuntimeInstance::GetTypeManagerList() } // static -RuntimeInstance * RuntimeInstance::Create(HANDLE hPalInstance) +bool RuntimeInstance::Initialize(HANDLE hPalInstance) { NewHolder pRuntimeInstance = new (nothrow) RuntimeInstance(); if (NULL == pRuntimeInstance) - return NULL; + return false; CreateHolder pThreadStore = ThreadStore::Create(pRuntimeInstance); if (NULL == pThreadStore) - return NULL; + return false; pThreadStore.SuppressRelease(); + pRuntimeInstance.SuppressRelease(); + pRuntimeInstance->m_pThreadStore = pThreadStore; pRuntimeInstance->m_hPalInstance = hPalInstance; @@ -498,12 +563,12 @@ RuntimeInstance * RuntimeInstance::Create(HANDLE hPalInstance) pRuntimeInstance->m_fProfileThreadCreated = false; #endif - pRuntimeInstance.SuppressRelease(); + ASSERT_MSG(g_pTheRuntimeInstance == NULL, "multi-instances are not supported"); + g_pTheRuntimeInstance = pRuntimeInstance; - return pRuntimeInstance; + return true; } - void RuntimeInstance::Destroy() { delete this; @@ -646,7 +711,7 @@ bool RuntimeInstance::CreateGenericAndStaticInfo(EEType * pEEType, } NewArrayHolder pGcStaticData; -#ifndef CORERT +#ifdef PROJECTN if (gcStaticDataSize > 0) { // The value of gcStaticDataSize is read from native layout info in the managed layer, where @@ -849,60 +914,4 @@ COOP_PINVOKE_HELPER(PTR_UInt8, RhGetThreadLocalStorageForDynamicType, (UInt32 uO return pCurrentThread->AllocateThreadLocalStorageForDynamicType(uOffset, tlsStorageSize, numTlsCells); } -#ifndef FEATURE_RX_THUNKS - -COOP_PINVOKE_HELPER(void*, RhpGetThunksBase, ()); -COOP_PINVOKE_HELPER(int, RhpGetNumThunkBlocksPerMapping, ()); -COOP_PINVOKE_HELPER(int, RhpGetNumThunksPerBlock, ()); -COOP_PINVOKE_HELPER(int, RhpGetThunkSize, ()); -COOP_PINVOKE_HELPER(int, RhpGetThunkBlockSize, ()); - -EXTERN_C REDHAWK_API void* __cdecl RhAllocateThunksMapping() -{ - static void* pThunksTemplateAddress = NULL; - - void *pThunkMap = NULL; - - int thunkBlocksPerMapping = RhpGetNumThunkBlocksPerMapping(); - int thunkBlockSize = RhpGetThunkBlockSize(); - int templateSize = thunkBlocksPerMapping * thunkBlockSize; - - if (pThunksTemplateAddress == NULL) - { - // First, we use the thunks directly from the thunks template sections in the module until all - // thunks in that template are used up. - pThunksTemplateAddress = RhpGetThunksBase(); - pThunkMap = pThunksTemplateAddress; - } - else - { - // We've already used the thunks template in the module for some previous thunks, and we - // cannot reuse it here. Now we need to create a new mapping of the thunks section in order to have - // more thunks - - UInt8* pModuleBase = (UInt8*)PalGetModuleHandleFromPointer(pThunksTemplateAddress); - int templateRva = (int)((UInt8*)RhpGetThunksBase() - pModuleBase); - - if (!PalAllocateThunksFromTemplate((HANDLE)pModuleBase, templateRva, templateSize, &pThunkMap)) - return NULL; - } - - if (!PalMarkThunksAsValidCallTargets( - pThunkMap, - RhpGetThunkSize(), - RhpGetNumThunksPerBlock(), - thunkBlockSize, - thunkBlocksPerMapping)) - { - if (pThunkMap != pThunksTemplateAddress) - PalFreeThunksFromTemplate(pThunkMap); - - return NULL; - } - - return pThunkMap; -} - -#endif // FEATURE_RX_THUNKS - #endif diff --git a/external/corert/src/Native/Runtime/RuntimeInstance.h b/external/corert/src/Native/Runtime/RuntimeInstance.h index eedcdea463..639a3f9a19 100644 --- a/external/corert/src/Native/Runtime/RuntimeInstance.h +++ b/external/corert/src/Native/Runtime/RuntimeInstance.h @@ -20,7 +20,6 @@ class RuntimeInstance friend struct DefaultSListTraits; friend class Thread; - PTR_RuntimeInstance m_pNext; PTR_ThreadStore m_pThreadStore; HANDLE m_hPalInstance; // this is the HANDLE passed into DllMain SList m_ModuleList; @@ -39,7 +38,6 @@ public: private: OsModuleList m_OsModuleList; -#ifdef FEATURE_DYNAMIC_CODE struct CodeManagerEntry; typedef DPTR(CodeManagerEntry) PTR_CodeManagerEntry; @@ -53,7 +51,6 @@ private: typedef SList CodeManagerList; CodeManagerList m_CodeManagerList; -#endif public: struct TypeManagerEntry @@ -133,6 +130,17 @@ private: bool m_conservativeStackReportingEnabled; + struct UnboxingStubsRegion + { + PTR_VOID m_pRegionStart; + UInt32 m_cbRegion; + UnboxingStubsRegion* m_pNextRegion; + + UnboxingStubsRegion() : m_pRegionStart(0), m_cbRegion(0), m_pNextRegion(NULL) { } + }; + + UnboxingStubsRegion* m_pUnboxingStubsRegion; + RuntimeInstance(); SList* GetModuleList(); @@ -164,13 +172,13 @@ public: Module * FindModuleByReadOnlyDataAddress(PTR_VOID Data); Module * FindModuleByOsHandle(HANDLE hOsHandle); PTR_UInt8 FindMethodStartAddress(PTR_VOID ControlPC); - bool EnableConservativeStackReporting(); + PTR_UInt8 GetTargetOfUnboxingAndInstantiatingStub(PTR_VOID ControlPC); + void EnableConservativeStackReporting(); bool IsConservativeStackReportingEnabled() { return m_conservativeStackReportingEnabled; } -#ifdef FEATURE_DYNAMIC_CODE bool RegisterCodeManager(ICodeManager * pCodeManager, PTR_VOID pvStartRange, UInt32 cbRange); void UnregisterCodeManager(ICodeManager * pCodeManager); -#endif + ICodeManager * FindCodeManagerByAddress(PTR_VOID ControlPC); bool RegisterTypeManager(TypeManager * pTypeManager); @@ -178,13 +186,16 @@ public: OsModuleList* GetOsModuleList(); ReaderWriterLock& GetTypeManagerLock(); + bool RegisterUnboxingStubs(PTR_VOID pvStartRange, UInt32 cbRange); + bool IsUnboxingStub(UInt8* pCode); + // This will hold the module list lock over each callback. Make sure // the callback will not trigger any operation that needs to make use // of the module list. typedef void (* EnumerateModulesCallbackPFN)(Module *pModule, void *pvContext); void EnumerateModulesUnderLock(EnumerateModulesCallbackPFN pCallback, void *pvContext); - static RuntimeInstance * Create(HANDLE hPalInstance); + static bool Initialize(HANDLE hPalInstance); void Destroy(); void EnumStaticGCRefDescs(void * pfnCallback, void * pvCallbackData); @@ -193,6 +204,7 @@ public: bool ShouldHijackCallsiteForGcStress(UIntNative CallsiteIP); bool ShouldHijackLoopForGcStress(UIntNative CallsiteIP); + void SetLoopHijackFlags(UInt32 flag); void EnableGcPollStress(); void UnsychronizedResetHijackedLoops(); @@ -235,6 +247,7 @@ typedef DPTR(RuntimeInstance) PTR_RuntimeInstance; PTR_RuntimeInstance GetRuntimeInstance(); +#ifdef PROJECTN #define FOREACH_MODULE(p_module_name) \ { \ @@ -247,3 +260,9 @@ PTR_RuntimeInstance GetRuntimeInstance(); } \ } \ +#else // PROJECTN + +#define FOREACH_MODULE(p_module_name) { Module * p_module_name = NULL; while (p_module_name != NULL) { +#define END_FOREACH_MODULE } } + +#endif // PROJECTN diff --git a/external/corert/src/Native/Runtime/StackFrameIterator.cpp b/external/corert/src/Native/Runtime/StackFrameIterator.cpp index 34f2aa0e41..5909416d8d 100644 --- a/external/corert/src/Native/Runtime/StackFrameIterator.cpp +++ b/external/corert/src/Native/Runtime/StackFrameIterator.cpp @@ -29,14 +29,13 @@ #include "RuntimeInstance.h" #include "rhbinder.h" +#include "DebugFuncEval.h" + // warning C4061: enumerator '{blah}' in switch of enum '{blarg}' is not explicitly handled by a case label #pragma warning(disable:4061) #if !defined(USE_PORTABLE_HELPERS) // @TODO: CORERT: these are (currently) only implemented in assembly helpers -EXTERN_C void * RhpDebugFuncEvalHelper; -GPTR_IMPL_INIT(PTR_VOID, g_RhpDebugFuncEvalHelperAddr, &RhpDebugFuncEvalHelper); - #if defined(FEATURE_DYNAMIC_CODE) EXTERN_C void * RhpUniversalTransition(); GPTR_IMPL_INIT(PTR_VOID, g_RhpUniversalTransitionAddr, (void**)&RhpUniversalTransition); @@ -52,21 +51,21 @@ GVAL_IMPL_INIT(PTR_VOID, g_ReturnFromCallDescrThunkAddr, PointerToReturnFromCall #endif #ifdef _TARGET_X86_ -EXTERN_C void * RhpCallFunclet2; -GVAL_IMPL_INIT(PTR_VOID, g_RhpCallFunclet2Addr, &RhpCallFunclet2); +EXTERN_C void * PointerToRhpCallFunclet2; +GVAL_IMPL_INIT(PTR_VOID, g_RhpCallFunclet2Addr, PointerToRhpCallFunclet2); #endif -EXTERN_C void * RhpCallCatchFunclet2; -GVAL_IMPL_INIT(PTR_VOID, g_RhpCallCatchFunclet2Addr, &RhpCallCatchFunclet2); -EXTERN_C void * RhpCallFinallyFunclet2; -GVAL_IMPL_INIT(PTR_VOID, g_RhpCallFinallyFunclet2Addr, &RhpCallFinallyFunclet2); -EXTERN_C void * RhpCallFilterFunclet2; -GVAL_IMPL_INIT(PTR_VOID, g_RhpCallFilterFunclet2Addr, &RhpCallFilterFunclet2); -EXTERN_C void * RhpThrowEx2; -GVAL_IMPL_INIT(PTR_VOID, g_RhpThrowEx2Addr, &RhpThrowEx2); -EXTERN_C void * RhpThrowHwEx2; -GVAL_IMPL_INIT(PTR_VOID, g_RhpThrowHwEx2Addr, &RhpThrowHwEx2); -EXTERN_C void * RhpRethrow2; -GVAL_IMPL_INIT(PTR_VOID, g_RhpRethrow2Addr, &RhpRethrow2); +EXTERN_C void * PointerToRhpCallCatchFunclet2; +GVAL_IMPL_INIT(PTR_VOID, g_RhpCallCatchFunclet2Addr, PointerToRhpCallCatchFunclet2); +EXTERN_C void * PointerToRhpCallFinallyFunclet2; +GVAL_IMPL_INIT(PTR_VOID, g_RhpCallFinallyFunclet2Addr, PointerToRhpCallFinallyFunclet2); +EXTERN_C void * PointerToRhpCallFilterFunclet2; +GVAL_IMPL_INIT(PTR_VOID, g_RhpCallFilterFunclet2Addr, PointerToRhpCallFilterFunclet2); +EXTERN_C void * PointerToRhpThrowEx2; +GVAL_IMPL_INIT(PTR_VOID, g_RhpThrowEx2Addr, PointerToRhpThrowEx2); +EXTERN_C void * PointerToRhpThrowHwEx2; +GVAL_IMPL_INIT(PTR_VOID, g_RhpThrowHwEx2Addr, PointerToRhpThrowHwEx2); +EXTERN_C void * PointerToRhpRethrow2; +GVAL_IMPL_INIT(PTR_VOID, g_RhpRethrow2Addr, PointerToRhpRethrow2); #endif // !defined(USE_PORTABLE_HELPERS) // Addresses of functions in the DAC won't match their runtime counterparts so we @@ -78,10 +77,8 @@ GVAL_IMPL_INIT(PTR_VOID, g_RhpRethrow2Addr, &RhpRethrow2); // ingest the updated DIA, we're instead exposing a global void * variable // holding the return address. #ifdef DACCESS_COMPILE -#define EQUALS_CODE_ADDRESS(x, func_name) ((x) == g_ ## func_name ## Addr) -#define EQUALS_RETURN_ADDRESS(x, func_name) EQUALS_CODE_ADDRESS((x), func_name) +#define EQUALS_RETURN_ADDRESS(x, func_name) ((x) == g_ ## func_name ## Addr) #else -#define EQUALS_CODE_ADDRESS(x, func_name) ((x) == &func_name) #define EQUALS_RETURN_ADDRESS(x, func_name) (((x)) == (PointerTo ## func_name)) #endif @@ -100,9 +97,6 @@ PTR_PInvokeTransitionFrame GetPInvokeTransitionFrame(PTR_VOID pTransitionFrame) return static_cast(pTransitionFrame); } -// TODO: Remove the assumption that there is only 1 func eval in progress -GVAL_IMPL_INIT(UInt64, g_debuggermagic, 0); - StackFrameIterator::StackFrameIterator(Thread * pThreadToWalk, PTR_VOID pInitialTransitionFrame) { STRESS_LOG0(LF_STACKWALK, LL_INFO10000, "----Init---- [ GC ]\n"); @@ -173,6 +167,7 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PTR_PInvokeTransit // properly walk it in parallel. ResetNextExInfoForSP((UIntNative)dac_cast(pFrame)); +#if !defined(USE_PORTABLE_HELPERS) // @TODO: CORERT: no portable version of regdisplay memset(&m_RegDisplay, 0, sizeof(m_RegDisplay)); m_RegDisplay.SetIP((PCODE)pFrame->m_RIP); m_RegDisplay.SetAddrOfIP((PTR_PCODE)PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_RIP)); @@ -184,71 +179,121 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PTR_PInvokeTransit m_RegDisplay.pLR = (PTR_UIntNative)PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_RIP); m_RegDisplay.pR11 = (PTR_UIntNative)PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_ChainPointer); - if (pFrame->m_dwFlags & PTFF_SAVE_R4) { m_RegDisplay.pR4 = pPreservedRegsCursor++; } - if (pFrame->m_dwFlags & PTFF_SAVE_R5) { m_RegDisplay.pR5 = pPreservedRegsCursor++; } - if (pFrame->m_dwFlags & PTFF_SAVE_R6) { m_RegDisplay.pR6 = pPreservedRegsCursor++; } - ASSERT(!(pFrame->m_dwFlags & PTFF_SAVE_R7)); // R7 should never contain a GC ref because we require - // a frame pointer for methods with pinvokes - if (pFrame->m_dwFlags & PTFF_SAVE_R8) { m_RegDisplay.pR8 = pPreservedRegsCursor++; } - if (pFrame->m_dwFlags & PTFF_SAVE_R9) { m_RegDisplay.pR9 = pPreservedRegsCursor++; } - if (pFrame->m_dwFlags & PTFF_SAVE_R10) { m_RegDisplay.pR10 = pPreservedRegsCursor++; } - if (pFrame->m_dwFlags & PTFF_SAVE_SP) { m_RegDisplay.SP = *pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R4) { m_RegDisplay.pR4 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R5) { m_RegDisplay.pR5 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R6) { m_RegDisplay.pR6 = pPreservedRegsCursor++; } + ASSERT(!(pFrame->m_Flags & PTFF_SAVE_R7)); // R7 should never contain a GC ref because we require + // a frame pointer for methods with pinvokes + if (pFrame->m_Flags & PTFF_SAVE_R8) { m_RegDisplay.pR8 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R9) { m_RegDisplay.pR9 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R10) { m_RegDisplay.pR10 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_SP) { m_RegDisplay.SP = *pPreservedRegsCursor++; } m_RegDisplay.pR7 = (PTR_UIntNative) PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_FramePointer); - if (pFrame->m_dwFlags & PTFF_SAVE_R0) { m_RegDisplay.pR0 = pPreservedRegsCursor++; } - if (pFrame->m_dwFlags & PTFF_SAVE_R1) { m_RegDisplay.pR1 = pPreservedRegsCursor++; } - if (pFrame->m_dwFlags & PTFF_SAVE_R2) { m_RegDisplay.pR2 = pPreservedRegsCursor++; } - if (pFrame->m_dwFlags & PTFF_SAVE_R3) { m_RegDisplay.pR3 = pPreservedRegsCursor++; } - if (pFrame->m_dwFlags & PTFF_SAVE_LR) { m_RegDisplay.pLR = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R0) { m_RegDisplay.pR0 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R1) { m_RegDisplay.pR1 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R2) { m_RegDisplay.pR2 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R3) { m_RegDisplay.pR3 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_LR) { m_RegDisplay.pLR = pPreservedRegsCursor++; } - if (pFrame->m_dwFlags & PTFF_R0_IS_GCREF) + if (pFrame->m_Flags & PTFF_R0_IS_GCREF) { m_pHijackedReturnValue = (PTR_RtuObjectRef) m_RegDisplay.pR0; m_HijackedReturnValueKind = GCRK_Object; } - if (pFrame->m_dwFlags & PTFF_R0_IS_BYREF) + if (pFrame->m_Flags & PTFF_R0_IS_BYREF) { m_pHijackedReturnValue = (PTR_RtuObjectRef) m_RegDisplay.pR0; m_HijackedReturnValueKind = GCRK_Byref; } #elif defined(_TARGET_ARM64_) - PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); + m_RegDisplay.pFP = (PTR_UIntNative)PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_FramePointer); + m_RegDisplay.pLR = (PTR_UIntNative)PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_RIP); + + ASSERT(!(pFrame->m_Flags & PTFF_SAVE_FP)); // FP should never contain a GC ref because we require + // a frame pointer for methods with pinvokes + + if (pFrame->m_Flags & PTFF_SAVE_X19) { m_RegDisplay.pX19 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X20) { m_RegDisplay.pX20 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X21) { m_RegDisplay.pX21 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X22) { m_RegDisplay.pX22 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X23) { m_RegDisplay.pX23 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X24) { m_RegDisplay.pX24 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X25) { m_RegDisplay.pX25 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X26) { m_RegDisplay.pX26 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X27) { m_RegDisplay.pX27 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X28) { m_RegDisplay.pX28 = pPreservedRegsCursor++; } + + if (pFrame->m_Flags & PTFF_SAVE_SP) { m_RegDisplay.SP = *pPreservedRegsCursor++; } + + if (pFrame->m_Flags & PTFF_SAVE_X0) { m_RegDisplay.pX0 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X1) { m_RegDisplay.pX1 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X2) { m_RegDisplay.pX2 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X3) { m_RegDisplay.pX3 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X4) { m_RegDisplay.pX4 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X5) { m_RegDisplay.pX5 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X6) { m_RegDisplay.pX6 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X7) { m_RegDisplay.pX7 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X8) { m_RegDisplay.pX8 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X9) { m_RegDisplay.pX9 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X10) { m_RegDisplay.pX10 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X11) { m_RegDisplay.pX11 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X12) { m_RegDisplay.pX12 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X13) { m_RegDisplay.pX13 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X14) { m_RegDisplay.pX14 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X15) { m_RegDisplay.pX15 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X16) { m_RegDisplay.pX16 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X17) { m_RegDisplay.pX17 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_X18) { m_RegDisplay.pX18 = pPreservedRegsCursor++; } + + if (pFrame->m_Flags & PTFF_SAVE_LR) { m_RegDisplay.pLR = pPreservedRegsCursor++; } + + if (pFrame->m_Flags & PTFF_X0_IS_GCREF) + { + m_pHijackedReturnValue = (PTR_RtuObjectRef)m_RegDisplay.pX0; + m_HijackedReturnValueKind = GCRK_Object; + } + if (pFrame->m_Flags & PTFF_X0_IS_BYREF) + { + m_pHijackedReturnValue = (PTR_RtuObjectRef)m_RegDisplay.pX0; + m_HijackedReturnValueKind = GCRK_Byref; + } #else // _TARGET_ARM_ - if (pFrame->m_dwFlags & PTFF_SAVE_RBX) { m_RegDisplay.pRbx = pPreservedRegsCursor++; } - if (pFrame->m_dwFlags & PTFF_SAVE_RSI) { m_RegDisplay.pRsi = pPreservedRegsCursor++; } - if (pFrame->m_dwFlags & PTFF_SAVE_RDI) { m_RegDisplay.pRdi = pPreservedRegsCursor++; } - ASSERT(!(pFrame->m_dwFlags & PTFF_SAVE_RBP)); // RBP should never contain a GC ref because we require - // a frame pointer for methods with pinvokes + if (pFrame->m_Flags & PTFF_SAVE_RBX) { m_RegDisplay.pRbx = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_RSI) { m_RegDisplay.pRsi = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_RDI) { m_RegDisplay.pRdi = pPreservedRegsCursor++; } + ASSERT(!(pFrame->m_Flags & PTFF_SAVE_RBP)); // RBP should never contain a GC ref because we require + // a frame pointer for methods with pinvokes #ifdef _TARGET_AMD64_ - if (pFrame->m_dwFlags & PTFF_SAVE_R12) { m_RegDisplay.pR12 = pPreservedRegsCursor++; } - if (pFrame->m_dwFlags & PTFF_SAVE_R13) { m_RegDisplay.pR13 = pPreservedRegsCursor++; } - if (pFrame->m_dwFlags & PTFF_SAVE_R14) { m_RegDisplay.pR14 = pPreservedRegsCursor++; } - if (pFrame->m_dwFlags & PTFF_SAVE_R15) { m_RegDisplay.pR15 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R12) { m_RegDisplay.pR12 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R13) { m_RegDisplay.pR13 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R14) { m_RegDisplay.pR14 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R15) { m_RegDisplay.pR15 = pPreservedRegsCursor++; } #endif // _TARGET_AMD64_ m_RegDisplay.pRbp = (PTR_UIntNative) PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_FramePointer); - if (pFrame->m_dwFlags & PTFF_SAVE_RSP) { m_RegDisplay.SP = *pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_RSP) { m_RegDisplay.SP = *pPreservedRegsCursor++; } - if (pFrame->m_dwFlags & PTFF_SAVE_RAX) { m_RegDisplay.pRax = pPreservedRegsCursor++; } - if (pFrame->m_dwFlags & PTFF_SAVE_RCX) { m_RegDisplay.pRcx = pPreservedRegsCursor++; } - if (pFrame->m_dwFlags & PTFF_SAVE_RDX) { m_RegDisplay.pRdx = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_RAX) { m_RegDisplay.pRax = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_RCX) { m_RegDisplay.pRcx = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_RDX) { m_RegDisplay.pRdx = pPreservedRegsCursor++; } #ifdef _TARGET_AMD64_ - if (pFrame->m_dwFlags & PTFF_SAVE_R8 ) { m_RegDisplay.pR8 = pPreservedRegsCursor++; } - if (pFrame->m_dwFlags & PTFF_SAVE_R9 ) { m_RegDisplay.pR9 = pPreservedRegsCursor++; } - if (pFrame->m_dwFlags & PTFF_SAVE_R10) { m_RegDisplay.pR10 = pPreservedRegsCursor++; } - if (pFrame->m_dwFlags & PTFF_SAVE_R11) { m_RegDisplay.pR11 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R8 ) { m_RegDisplay.pR8 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R9 ) { m_RegDisplay.pR9 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R10) { m_RegDisplay.pR10 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R11) { m_RegDisplay.pR11 = pPreservedRegsCursor++; } #endif // _TARGET_AMD64_ - if (pFrame->m_dwFlags & PTFF_RAX_IS_GCREF) + if (pFrame->m_Flags & PTFF_RAX_IS_GCREF) { m_pHijackedReturnValue = (PTR_RtuObjectRef) m_RegDisplay.pRax; m_HijackedReturnValueKind = GCRK_Object; } - if (pFrame->m_dwFlags & PTFF_RAX_IS_BYREF) + if (pFrame->m_Flags & PTFF_RAX_IS_BYREF) { m_pHijackedReturnValue = (PTR_RtuObjectRef) m_RegDisplay.pRax; m_HijackedReturnValueKind = GCRK_Byref; @@ -256,6 +301,8 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PTR_PInvokeTransit #endif // _TARGET_ARM_ +#endif // defined(USE_PORTABLE_HELPERS) + // @TODO: currently, we always save all registers -- how do we handle the onese we don't save once we // start only saving those that weren't already saved? @@ -370,7 +417,35 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PTR_PAL_LIMITED_CO m_RegDisplay.pR0 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R0); #elif defined(_TARGET_ARM64_) - PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); + // + // preserved regs + // + m_RegDisplay.pX19 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X19); + m_RegDisplay.pX20 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X20); + m_RegDisplay.pX21 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X21); + m_RegDisplay.pX22 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X22); + m_RegDisplay.pX23 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X23); + m_RegDisplay.pX24 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X24); + m_RegDisplay.pX25 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X25); + m_RegDisplay.pX26 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X26); + m_RegDisplay.pX27 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X27); + m_RegDisplay.pX28 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X28); + m_RegDisplay.pFP = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, FP); + m_RegDisplay.pLR = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, LR); + + // + // preserved vfp regs + // + for (Int32 i = 0; i < 16 - 8; i++) + { + m_RegDisplay.D[i] = pCtx->D[i]; + } + // + // scratch regs + // + m_RegDisplay.pX0 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X0); + m_RegDisplay.pX1 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, X1); + // TODO: Copy X2-X7 when we start supporting HVA's #elif defined(UNIX_AMD64_ABI) // @@ -395,7 +470,8 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PTR_PAL_LIMITED_CO m_RegDisplay.pR9 = NULL; m_RegDisplay.pR10 = NULL; m_RegDisplay.pR11 = NULL; -#else // _TARGET_ARM_ + +#elif defined(_TARGET_X86_) || defined(_TARGET_AMD64_) // // preserved regs // @@ -426,6 +502,8 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PTR_PAL_LIMITED_CO m_RegDisplay.pR10 = NULL; m_RegDisplay.pR11 = NULL; #endif // _TARGET_AMD64_ +#else + PORTABILITY_ASSERT("StackFrameIterator::InternalInit"); #endif // _TARGET_ARM_ } @@ -531,7 +609,17 @@ void StackFrameIterator::UpdateFromExceptionDispatch(PTR_StackFrameIterator pSou m_RegDisplay.pR11 = thisFuncletPtrs.pR11; #elif defined(_TARGET_ARM64_) - PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); + m_RegDisplay.pX19 = thisFuncletPtrs.pX19; + m_RegDisplay.pX20 = thisFuncletPtrs.pX20; + m_RegDisplay.pX21 = thisFuncletPtrs.pX21; + m_RegDisplay.pX22 = thisFuncletPtrs.pX22; + m_RegDisplay.pX23 = thisFuncletPtrs.pX23; + m_RegDisplay.pX24 = thisFuncletPtrs.pX24; + m_RegDisplay.pX25 = thisFuncletPtrs.pX25; + m_RegDisplay.pX26 = thisFuncletPtrs.pX26; + m_RegDisplay.pX27 = thisFuncletPtrs.pX27; + m_RegDisplay.pX28 = thisFuncletPtrs.pX28; + m_RegDisplay.pFP = thisFuncletPtrs.pFP; #elif defined(UNIX_AMD64_ABI) // Save the preserved regs portion of the REGDISPLAY across the unwind through the C# EH dispatch code. @@ -541,7 +629,8 @@ void StackFrameIterator::UpdateFromExceptionDispatch(PTR_StackFrameIterator pSou m_RegDisplay.pR13 = thisFuncletPtrs.pR13; m_RegDisplay.pR14 = thisFuncletPtrs.pR14; m_RegDisplay.pR15 = thisFuncletPtrs.pR15; -#else + +#elif defined(_TARGET_X86_) || defined(_TARGET_AMD64_) // Save the preserved regs portion of the REGDISPLAY across the unwind through the C# EH dispatch code. m_RegDisplay.pRbp = thisFuncletPtrs.pRbp; m_RegDisplay.pRdi = thisFuncletPtrs.pRdi; @@ -553,7 +642,9 @@ void StackFrameIterator::UpdateFromExceptionDispatch(PTR_StackFrameIterator pSou m_RegDisplay.pR14 = thisFuncletPtrs.pR14; m_RegDisplay.pR15 = thisFuncletPtrs.pR15; #endif // _TARGET_AMD64_ -#endif // _TARGET_ARM_ +#else + PORTABILITY_ASSERT("StackFrameIterator::UpdateFromExceptionDispatch"); +#endif } #ifdef _TARGET_AMD64_ @@ -583,13 +674,13 @@ void StackFrameIterator::UnwindFuncletInvokeThunk() m_ControlPC = dac_cast(*(m_RegDisplay.pIP)); ASSERT( - EQUALS_CODE_ADDRESS(m_ControlPC, RhpCallCatchFunclet2) || - EQUALS_CODE_ADDRESS(m_ControlPC, RhpCallFinallyFunclet2) || - EQUALS_CODE_ADDRESS(m_ControlPC, RhpCallFilterFunclet2) + EQUALS_RETURN_ADDRESS(m_ControlPC, RhpCallCatchFunclet2) || + EQUALS_RETURN_ADDRESS(m_ControlPC, RhpCallFinallyFunclet2) || + EQUALS_RETURN_ADDRESS(m_ControlPC, RhpCallFilterFunclet2) ); #endif - bool isFilterInvoke = EQUALS_CODE_ADDRESS(m_ControlPC, RhpCallFilterFunclet2); + bool isFilterInvoke = EQUALS_RETURN_ADDRESS(m_ControlPC, RhpCallFilterFunclet2); #if defined(UNIX_AMD64_ABI) SP = (PTR_UIntNative)(m_RegDisplay.SP); @@ -608,7 +699,7 @@ void StackFrameIterator::UnwindFuncletInvokeThunk() m_funcletPtrs.pR14 = m_RegDisplay.pR14; m_funcletPtrs.pR15 = m_RegDisplay.pR15; - if (EQUALS_CODE_ADDRESS(m_ControlPC, RhpCallCatchFunclet2)) + if (EQUALS_RETURN_ADDRESS(m_ControlPC, RhpCallCatchFunclet2)) { SP += 6 + 1; // 6 locals and stack alignment } @@ -652,9 +743,9 @@ void StackFrameIterator::UnwindFuncletInvokeThunk() m_funcletPtrs.pR14 = m_RegDisplay.pR14; m_funcletPtrs.pR15 = m_RegDisplay.pR15; - if (EQUALS_CODE_ADDRESS(m_ControlPC, RhpCallCatchFunclet2)) + if (EQUALS_RETURN_ADDRESS(m_ControlPC, RhpCallCatchFunclet2)) { - SP += 2 + 1; // 2 locals and stack alignment + SP += 3; // 3 locals } else { @@ -683,7 +774,14 @@ void StackFrameIterator::UnwindFuncletInvokeThunk() m_funcletPtrs.pRbx = m_RegDisplay.pRbx; } - SP++; // local / stack alignment + if (EQUALS_RETURN_ADDRESS(m_ControlPC, RhpCallCatchFunclet2)) + { + SP += 2; // 2 locals + } + else + { + SP++; // 1 local + } m_RegDisplay.pRdi = SP++; m_RegDisplay.pRsi = SP++; m_RegDisplay.pRbx = SP++; @@ -703,7 +801,7 @@ void StackFrameIterator::UnwindFuncletInvokeThunk() { // RhpCallCatchFunclet puts a couple of extra things on the stack that aren't put there by the other two // thunks, but we don't need to know what they are here, so we just skip them. - SP += EQUALS_CODE_ADDRESS(m_ControlPC, RhpCallCatchFunclet2) ? 3 : 1; + SP += EQUALS_RETURN_ADDRESS(m_ControlPC, RhpCallCatchFunclet2) ? 3 : 1; // Save the preserved regs portion of the REGDISPLAY across the unwind through the C# EH dispatch code. m_funcletPtrs.pR4 = m_RegDisplay.pR4; @@ -726,14 +824,61 @@ void StackFrameIterator::UnwindFuncletInvokeThunk() m_RegDisplay.pR11 = SP++; #elif defined(_TARGET_ARM64_) - PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); + PTR_UInt64 d = (PTR_UInt64)(m_RegDisplay.SP); + + for (int i = 0; i < 8; i++) + { + m_RegDisplay.D[i] = *d++; + } + + SP = (PTR_UIntNative)d; + + if (!isFilterInvoke) + { + // RhpCallCatchFunclet puts a couple of extra things on the stack that aren't put there by the other two + // thunks, but we don't need to know what they are here, so we just skip them. + SP += EQUALS_RETURN_ADDRESS(m_ControlPC, RhpCallCatchFunclet2) ? 4 : 2; + + // Save the preserved regs portion of the REGDISPLAY across the unwind through the C# EH dispatch code. + m_funcletPtrs.pX19 = m_RegDisplay.pX19; + m_funcletPtrs.pX20 = m_RegDisplay.pX20; + m_funcletPtrs.pX21 = m_RegDisplay.pX21; + m_funcletPtrs.pX22 = m_RegDisplay.pX22; + m_funcletPtrs.pX23 = m_RegDisplay.pX23; + m_funcletPtrs.pX24 = m_RegDisplay.pX24; + m_funcletPtrs.pX25 = m_RegDisplay.pX25; + m_funcletPtrs.pX26 = m_RegDisplay.pX26; + m_funcletPtrs.pX27 = m_RegDisplay.pX27; + m_funcletPtrs.pX28 = m_RegDisplay.pX28; + m_funcletPtrs.pFP = m_RegDisplay.pFP; + } + + m_RegDisplay.pFP = SP++; + + m_RegDisplay.SetAddrOfIP((PTR_PCODE)SP); + m_RegDisplay.SetIP(*SP++); + + m_RegDisplay.pX19 = SP++; + m_RegDisplay.pX20 = SP++; + m_RegDisplay.pX21 = SP++; + m_RegDisplay.pX22 = SP++; + m_RegDisplay.pX23 = SP++; + m_RegDisplay.pX24 = SP++; + m_RegDisplay.pX25 = SP++; + m_RegDisplay.pX26 = SP++; + m_RegDisplay.pX27 = SP++; + m_RegDisplay.pX28 = SP++; #else SP = (PTR_UIntNative)(m_RegDisplay.SP); ASSERT_UNCONDITIONALLY("NYI for this arch"); #endif + +#if !defined(_TARGET_ARM64_) m_RegDisplay.SetAddrOfIP((PTR_PCODE)SP); m_RegDisplay.SetIP(*SP++); +#endif + m_RegDisplay.SetSP((UIntNative)dac_cast(SP)); m_ControlPC = dac_cast(*(m_RegDisplay.pIP)); @@ -843,6 +988,42 @@ public: pRegisterSet->pRbp = GET_POINTER_TO_FIELD(m_pushedEBP); } +#elif defined(_TARGET_ARM64_) + + // Conservative GC reporting must be applied to everything between the base of the + // ReturnBlock and the top of the StackPassedArgs. +private: + UIntNative m_pushedFP; // ChildSP+000 CallerSP-0C0 (0x08 bytes) (fp) + UIntNative m_pushedLR; // ChildSP+008 CallerSP-0B8 (0x08 bytes) (lr) + UInt64 m_fpArgRegs[8]; // ChildSP+010 CallerSP-0B0 (0x40 bytes) (d0-d7) + UIntNative m_returnBlock[4]; // ChildSP+050 CallerSP-070 (0x40 bytes) + UIntNative m_intArgRegs[9]; // ChildSP+070 CallerSP-050 (0x48 bytes) (x0-x8) + UIntNative m_alignmentPad; // ChildSP+0B8 CallerSP-008 (0x08 bytes) + UIntNative m_stackPassedArgs[1]; // ChildSP+0C0 CallerSP+000 (unknown size) + +public: + PTR_UIntNative get_CallerSP() { return GET_POINTER_TO_FIELD(m_stackPassedArgs[0]); } + PTR_UIntNative get_AddressOfPushedCallerIP() { return GET_POINTER_TO_FIELD(m_pushedLR); } + PTR_UIntNative get_LowerBoundForConservativeReporting() { return GET_POINTER_TO_FIELD(m_returnBlock[0]); } + + void UnwindNonVolatileRegisters(REGDISPLAY * pRegisterSet) + { + pRegisterSet->pFP = GET_POINTER_TO_FIELD(m_pushedFP); + } +#elif defined(_TARGET_WASM_) +private: + // WASMTODO: #error NYI for this arch + UIntNative m_stackPassedArgs[1]; // Placeholder +public: + PTR_UIntNative get_CallerSP() { PORTABILITY_ASSERT("@TODO: FIXME:WASM"); return NULL; } + PTR_UIntNative get_AddressOfPushedCallerIP() { PORTABILITY_ASSERT("@TODO: FIXME:WASM"); return NULL; } + PTR_UIntNative get_LowerBoundForConservativeReporting() { PORTABILITY_ASSERT("@TODO: FIXME:WASM"); return NULL; } + + void UnwindNonVolatileRegisters(REGDISPLAY * pRegisterSet) + { + UNREFERENCED_PARAMETER(pRegisterSet); + PORTABILITY_ASSERT("@TODO: FIXME:WASM"); + } #else #error NYI for this arch #endif @@ -901,6 +1082,8 @@ void StackFrameIterator::UnwindUniversalTransitionThunk() #define STACK_ALIGN_SIZE 16 #elif defined(_TARGET_X86_) #define STACK_ALIGN_SIZE 4 +#elif defined(_TARGET_WASM_) +#define STACK_ALIGN_SIZE 4 #endif #ifdef _TARGET_AMD64_ @@ -920,10 +1103,12 @@ struct CALL_DESCR_CONTEXT UIntNative IP; }; #elif defined(_TARGET_ARM64_) -// @TODO: Add ARM64 entries struct CALL_DESCR_CONTEXT { - UIntNative IP; + UIntNative FP; + UIntNative IP; + UIntNative X19; + UIntNative X20; }; #elif defined(_TARGET_X86_) struct CALL_DESCR_CONTEXT @@ -932,6 +1117,11 @@ struct CALL_DESCR_CONTEXT UIntNative Rbp; UIntNative IP; }; +#elif defined (_TARGET_WASM_) +struct CALL_DESCR_CONTEXT +{ + UIntNative IP; +}; #else #error NYI - For this arch #endif @@ -979,7 +1169,18 @@ void StackFrameIterator::UnwindCallDescrThunk() newSP += sizeof(CALL_DESCR_CONTEXT); #elif defined(_TARGET_ARM64_) - PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); + // pFP points to the SP that we want to capture. (This arrangement allows for + // the arguments from this function to be loaded into memory with an adjustment + // to SP, like an alloca + newSP = *(PTR_UIntNative)m_RegDisplay.pFP; + PTR_CALL_DESCR_CONTEXT pContext = (PTR_CALL_DESCR_CONTEXT)newSP; + + m_RegDisplay.pX19 = PTR_TO_MEMBER(CALL_DESCR_CONTEXT, pContext, X19); + m_RegDisplay.pX20 = PTR_TO_MEMBER(CALL_DESCR_CONTEXT, pContext, X20); + + // And adjust SP to be the state that it should be in just after returning from + // the CallDescrFunction + newSP += sizeof(CALL_DESCR_CONTEXT); #elif defined(_TARGET_X86_) // RBP points to the SP that we want to capture. (This arrangement allows for @@ -995,8 +1196,10 @@ void StackFrameIterator::UnwindCallDescrThunk() // And adjust SP to be the state that it should be in just after returning from // the CallDescrFunction newSP += sizeof(CALL_DESCR_CONTEXT) - offsetof(CALL_DESCR_CONTEXT, Rbp); + #else - ASSERT_UNCONDITIONALLY("NYI for this arch"); + PORTABILITY_ASSERT("UnwindCallDescrThunk"); + PTR_CALL_DESCR_CONTEXT pContext = NULL; #endif m_RegDisplay.SetAddrOfIP(PTR_TO_MEMBER(CALL_DESCR_CONTEXT, pContext, IP)); @@ -1051,7 +1254,17 @@ void StackFrameIterator::UnwindThrowSiteThunk() m_RegDisplay.pR10 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, R10); m_RegDisplay.pR11 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, R11); #elif defined(_TARGET_ARM64_) - PORTABILITY_ASSERT("@TODO: FIXME:ARM64"); + m_RegDisplay.pX19 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, X19); + m_RegDisplay.pX20 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, X20); + m_RegDisplay.pX21 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, X21); + m_RegDisplay.pX22 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, X22); + m_RegDisplay.pX23 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, X23); + m_RegDisplay.pX24 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, X24); + m_RegDisplay.pX25 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, X25); + m_RegDisplay.pX26 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, X26); + m_RegDisplay.pX27 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, X27); + m_RegDisplay.pX28 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, X28); + m_RegDisplay.pFP = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, FP); #elif defined(_TARGET_X86_) m_RegDisplay.pRbp = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, Rbp); m_RegDisplay.pRdi = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, Rdi); @@ -1368,7 +1581,7 @@ void StackFrameIterator::PrepareToYieldFrame() ASSERT(m_pInstance->FindCodeManagerByAddress(m_ControlPC)); - bool atDebuggerHijackSite = (this->m_ControlPC == (PTR_VOID)(TADDR)g_debuggermagic); + bool atDebuggerHijackSite = (this->m_ControlPC == (PTR_VOID)(TADDR)DebugFuncEval::GetMostRecentFuncEvalHijackInstructionPointer()); if (atDebuggerHijackSite) { @@ -1472,7 +1685,7 @@ PTR_VOID StackFrameIterator::GetEffectiveSafePointAddress() return m_effectiveSafePointAddress; } -ICodeManager * StackFrameIterator::GetCodeManager() +PTR_ICodeManager StackFrameIterator::GetCodeManager() { ASSERT(IsValid()); return m_pCodeManager; @@ -1498,7 +1711,7 @@ void StackFrameIterator::CalculateCurrentMethodState() // Assume that the caller is likely to be in the same module if (m_pCodeManager == NULL || !m_pCodeManager->FindMethodInfo(m_ControlPC, &m_methodInfo)) { - m_pCodeManager = m_pInstance->FindCodeManagerByAddress(m_ControlPC); + m_pCodeManager = dac_cast(m_pInstance->FindCodeManagerByAddress(m_ControlPC)); FAILFAST_OR_DAC_FAIL(m_pCodeManager); FAILFAST_OR_DAC_FAIL(m_pCodeManager->FindMethodInfo(m_ControlPC, &m_methodInfo)); @@ -1623,20 +1836,20 @@ StackFrameIterator::ReturnAddressCategory StackFrameIterator::CategorizeUnadjust } #endif - if (EQUALS_CODE_ADDRESS(returnAddress, RhpThrowEx2) || - EQUALS_CODE_ADDRESS(returnAddress, RhpThrowHwEx2) || - EQUALS_CODE_ADDRESS(returnAddress, RhpRethrow2)) + if (EQUALS_RETURN_ADDRESS(returnAddress, RhpThrowEx2) || + EQUALS_RETURN_ADDRESS(returnAddress, RhpThrowHwEx2) || + EQUALS_RETURN_ADDRESS(returnAddress, RhpRethrow2)) { return InThrowSiteThunk; } if ( #ifdef _TARGET_X86_ - EQUALS_CODE_ADDRESS(returnAddress, RhpCallFunclet2) + EQUALS_RETURN_ADDRESS(returnAddress, RhpCallFunclet2) #else - EQUALS_CODE_ADDRESS(returnAddress, RhpCallCatchFunclet2) || - EQUALS_CODE_ADDRESS(returnAddress, RhpCallFinallyFunclet2) || - EQUALS_CODE_ADDRESS(returnAddress, RhpCallFilterFunclet2) + EQUALS_RETURN_ADDRESS(returnAddress, RhpCallCatchFunclet2) || + EQUALS_RETURN_ADDRESS(returnAddress, RhpCallFinallyFunclet2) || + EQUALS_RETURN_ADDRESS(returnAddress, RhpCallFilterFunclet2) #endif ) { diff --git a/external/corert/src/Native/Runtime/StackFrameIterator.h b/external/corert/src/Native/Runtime/StackFrameIterator.h index 343b52e84d..b5d07eda46 100644 --- a/external/corert/src/Native/Runtime/StackFrameIterator.h +++ b/external/corert/src/Native/Runtime/StackFrameIterator.h @@ -5,6 +5,8 @@ struct ExInfo; typedef DPTR(ExInfo) PTR_ExInfo; +typedef VPTR(ICodeManager) PTR_ICodeManager; + enum ExKind : UInt8 { EK_HardwareFault = 2, @@ -36,14 +38,14 @@ public: StackFrameIterator(Thread * pThreadToWalk, PTR_PAL_LIMITED_CONTEXT pCtx); - bool IsValid(); - void CalculateCurrentMethodState(); - void Next(); - PTR_VOID GetEffectiveSafePointAddress(); - REGDISPLAY * GetRegisterSet(); - ICodeManager * GetCodeManager(); - MethodInfo * GetMethodInfo(); - bool GetHijackedReturnValueLocation(PTR_RtuObjectRef * pLocation, GCRefKind * pKind); + bool IsValid(); + void CalculateCurrentMethodState(); + void Next(); + PTR_VOID GetEffectiveSafePointAddress(); + REGDISPLAY * GetRegisterSet(); + PTR_ICodeManager GetCodeManager(); + MethodInfo * GetMethodInfo(); + bool GetHijackedReturnValueLocation(PTR_RtuObjectRef * pLocation, GCRefKind * pKind); static bool IsValidReturnAddress(PTR_VOID pvAddress); @@ -151,6 +153,18 @@ private: PTR_UIntNative pR9; PTR_UIntNative pR10; PTR_UIntNative pR11; +#elif defined(_TARGET_ARM64_) + PTR_UIntNative pX19; + PTR_UIntNative pX20; + PTR_UIntNative pX21; + PTR_UIntNative pX22; + PTR_UIntNative pX23; + PTR_UIntNative pX24; + PTR_UIntNative pX25; + PTR_UIntNative pX26; + PTR_UIntNative pX27; + PTR_UIntNative pX28; + PTR_UIntNative pFP; #elif defined(UNIX_AMD64_ABI) PTR_UIntNative pRbp; PTR_UIntNative pRbx; @@ -178,7 +192,7 @@ protected: PTR_VOID m_FramePointer; PTR_VOID m_ControlPC; REGDISPLAY m_RegDisplay; - ICodeManager * m_pCodeManager; + PTR_ICodeManager m_pCodeManager; MethodInfo m_methodInfo; PTR_VOID m_effectiveSafePointAddress; PTR_RtuObjectRef m_pHijackedReturnValue; diff --git a/external/corert/src/Native/Runtime/ThunksMapping.cpp b/external/corert/src/Native/Runtime/ThunksMapping.cpp index f1a653e3cd..387b6a7065 100644 --- a/external/corert/src/Native/Runtime/ThunksMapping.cpp +++ b/external/corert/src/Native/Runtime/ThunksMapping.cpp @@ -15,18 +15,14 @@ #ifdef FEATURE_RX_THUNKS -#ifdef USE_PORTABLE_HELPERS -static_assert(false, "Cannot use the portable helpers with FEATURE_RX_THUNKS"); -#endif - #ifdef _TARGET_AMD64_ #define THUNK_SIZE 20 #elif _TARGET_X86_ #define THUNK_SIZE 12 #elif _TARGET_ARM_ #define THUNK_SIZE 20 -#elif _TARGET_ARM64_ -#define THUNK_SIZE 0x8000 // This will cause RhpGetNumThunksPerBlock to return 0 for now +#else +#define THUNK_SIZE (2 * OS_PAGE_SIZE) // This will cause RhpGetNumThunksPerBlock to return 0 #endif static_assert((THUNK_SIZE % 4) == 0, "Thunk stubs size not aligned correctly. This will cause runtime failures."); @@ -197,10 +193,10 @@ EXTERN_C REDHAWK_API void* __cdecl RhAllocateThunksMapping() *((UInt16*)pCurrentThunkAddress) = 0xbf00; pCurrentThunkAddress += 2; -#elif _TARGET_ARM64_ - - /* TODO */ ASSERT_UNCONDITIONALLY("NYI"); - +#else + UNREFERENCED_PARAMETER(pCurrentDataAddress); + UNREFERENCED_PARAMETER(pCurrentThunkAddress); + PORTABILITY_ASSERT("RhAllocateThunksMapping"); #endif } } @@ -214,4 +210,58 @@ EXTERN_C REDHAWK_API void* __cdecl RhAllocateThunksMapping() return pThunksSection; } -#endif // FEATURE_RX_THUNKS +#else // FEATURE_RX_THUNKS + +COOP_PINVOKE_HELPER(void*, RhpGetThunksBase, ()); +COOP_PINVOKE_HELPER(int, RhpGetNumThunkBlocksPerMapping, ()); +COOP_PINVOKE_HELPER(int, RhpGetNumThunksPerBlock, ()); +COOP_PINVOKE_HELPER(int, RhpGetThunkSize, ()); +COOP_PINVOKE_HELPER(int, RhpGetThunkBlockSize, ()); + +EXTERN_C REDHAWK_API void* __cdecl RhAllocateThunksMapping() +{ + static void* pThunksTemplateAddress = NULL; + + void *pThunkMap = NULL; + + int thunkBlocksPerMapping = RhpGetNumThunkBlocksPerMapping(); + int thunkBlockSize = RhpGetThunkBlockSize(); + int templateSize = thunkBlocksPerMapping * thunkBlockSize; + + if (pThunksTemplateAddress == NULL) + { + // First, we use the thunks directly from the thunks template sections in the module until all + // thunks in that template are used up. + pThunksTemplateAddress = RhpGetThunksBase(); + pThunkMap = pThunksTemplateAddress; + } + else + { + // We've already used the thunks template in the module for some previous thunks, and we + // cannot reuse it here. Now we need to create a new mapping of the thunks section in order to have + // more thunks + + UInt8* pModuleBase = (UInt8*)PalGetModuleHandleFromPointer(pThunksTemplateAddress); + int templateRva = (int)((UInt8*)RhpGetThunksBase() - pModuleBase); + + if (!PalAllocateThunksFromTemplate((HANDLE)pModuleBase, templateRva, templateSize, &pThunkMap)) + return NULL; + } + + if (!PalMarkThunksAsValidCallTargets( + pThunkMap, + RhpGetThunkSize(), + RhpGetNumThunksPerBlock(), + thunkBlockSize, + thunkBlocksPerMapping)) + { + if (pThunkMap != pThunksTemplateAddress) + PalFreeThunksFromTemplate(pThunkMap); + + return NULL; + } + + return pThunkMap; +} + +#endif // FEATURE_RX_THUNKS diff --git a/external/corert/src/Native/Runtime/TypeManager.cpp b/external/corert/src/Native/Runtime/TypeManager.cpp index 2b0da3f140..5f80ef7924 100644 --- a/external/corert/src/Native/Runtime/TypeManager.cpp +++ b/external/corert/src/Native/Runtime/TypeManager.cpp @@ -51,6 +51,7 @@ TypeManager::TypeManager(HANDLE osModule, ReadyToRunHeader * pHeader, void** pCl m_pThreadStaticsDataSection = (UInt8*)GetModuleSection(ReadyToRunSectionType::ThreadStaticRegion, &length); m_pThreadStaticsGCInfo = (StaticGcDesc*)GetModuleSection(ReadyToRunSectionType::ThreadStaticGCDescRegion, &length); m_pTlsIndex = (UInt32*)GetModuleSection(ReadyToRunSectionType::ThreadStaticIndex, &length); + m_pLoopHijackFlag = (UInt32*)GetModuleSection(ReadyToRunSectionType::LoopHijackFlag, &length); } void * TypeManager::GetModuleSection(ReadyToRunSectionType sectionId, int * length) @@ -179,7 +180,7 @@ HANDLE TypeManager::GetOsModuleHandle() bool TypeManagerHandle::IsTypeManager() { -#if !CORERT +#if PROJECTN if (((uintptr_t)_value & 1) == 0) return false; #endif diff --git a/external/corert/src/Native/Runtime/TypeManager.h b/external/corert/src/Native/Runtime/TypeManager.h index 1a5baa216a..50bf99d838 100644 --- a/external/corert/src/Native/Runtime/TypeManager.h +++ b/external/corert/src/Native/Runtime/TypeManager.h @@ -20,6 +20,7 @@ class TypeManager UInt32* m_pTlsIndex; // Pointer to TLS index if this module uses thread statics void** m_pClasslibFunctions; UInt32 m_nClasslibFunctions; + UInt32* m_pLoopHijackFlag; TypeManager(HANDLE osModule, ReadyToRunHeader * pHeader, void** pClasslibFunctions, UInt32 nClasslibFunctions); @@ -30,6 +31,8 @@ public: void EnumStaticGCRefs(void * pfnCallback, void * pvCallbackData); HANDLE GetOsModuleHandle(); void* GetClasslibFunction(ClasslibFunctionId functionId); + UInt32* GetPointerToTlsIndex() { return m_pTlsIndex; } + void SetLoopHijackFlag(UInt32 flag) { if (m_pLoopHijackFlag != nullptr) *m_pLoopHijackFlag = flag; } private: diff --git a/external/corert/src/Native/Runtime/amd64/AllocFast.S b/external/corert/src/Native/Runtime/amd64/AllocFast.S index 443d52b19a..a1e5e5fce6 100644 --- a/external/corert/src/Native/Runtime/amd64/AllocFast.S +++ b/external/corert/src/Native/Runtime/amd64/AllocFast.S @@ -3,8 +3,8 @@ // See the LICENSE file in the project root for more information. .intel_syntax noprefix -#include #include // generated by the build from AsmOffsets.cpp +#include // Allocate non-array, non-finalizable object. If the allocation doesn't fit into the current thread's // allocation context then automatically fallback to the slow allocation path. @@ -119,6 +119,85 @@ LOCAL_LABEL(NewOutOfMemory): NESTED_END RhpNewObject, _TEXT +// Allocate a string. +// RDI == EEType +// ESI == character/element count +NESTED_ENTRY RhNewString, _TEXT, NoHandler + // we want to limit the element count to the non-negative 32-bit int range + cmp rsi, 07fffffffh + ja LOCAL_LABEL(StringSizeOverflow) + + push_nonvol_reg rbx + push_nonvol_reg r12 + push_register rcx // padding + + mov rbx, rdi // save EEType + mov r12, rsi // save element count + + // rax = GetThread() + INLINE_GETTHREAD + + mov rcx, rax // rcx = Thread* + + // Compute overall allocation size (align(base size + (element size * elements), 8)). + lea rax, [r12 * STRING_COMPONENT_SIZE + STRING_BASE_SIZE + 7] + and rax, -8 + + // rax == string size + // rbx == EEType + // rcx == Thread* + // r12 == element count + + mov rdx, rax + add rax, [rcx + OFFSETOF__Thread__m_alloc_context__alloc_ptr] + jc LOCAL_LABEL(RhNewString_RarePath) + + // rax == new alloc ptr + // rbx == EEType + // rcx == Thread* + // rdx == string size + // r12 == element count + cmp rax, [rcx + OFFSETOF__Thread__m_alloc_context__alloc_limit] + ja LOCAL_LABEL(RhNewString_RarePath) + + mov [rcx + OFFSETOF__Thread__m_alloc_context__alloc_ptr], rax + + // calc the new object pointer + sub rax, rdx + + mov [rax + OFFSETOF__Object__m_pEEType], rbx + mov [rax + OFFSETOF__String__m_Length], r12d + + .cfi_remember_state + pop_register rcx // padding + pop_nonvol_reg r12 + pop_nonvol_reg rbx + ret + + .cfi_restore_state + .cfi_def_cfa_offset 32 // workaround cfi_restore_state bug +LOCAL_LABEL(RhNewString_RarePath): + mov rdi, rbx // restore EEType + mov rsi, r12 // restore element count + // passing string size in rdx + + pop_register rcx // padding + pop_nonvol_reg r12 + pop_nonvol_reg rbx + jmp C_FUNC(RhpNewArrayRare) + +LOCAL_LABEL(StringSizeOverflow): + // We get here if the size of the final string object can't be represented as an unsigned + // 32-bit value. We're going to tail-call to a managed helper that will throw + // an OOM exception that the caller of this allocator understands. + + // rdi holds EEType pointer already + xor esi, esi // Indicate that we should throw OOM. + jmp C_FUNC(RhExceptionHandling_FailedAllocation) + +NESTED_END RhNewString, _TEXT + + // Allocate one dimensional, zero based array (SZARRAY). // RDI == EEType // ESI == element count diff --git a/external/corert/src/Native/Runtime/amd64/AllocFast.asm b/external/corert/src/Native/Runtime/amd64/AllocFast.asm index c8d10c685b..6dae9dab95 100644 --- a/external/corert/src/Native/Runtime/amd64/AllocFast.asm +++ b/external/corert/src/Native/Runtime/amd64/AllocFast.asm @@ -102,6 +102,58 @@ NewOutOfMemory: NESTED_END RhpNewObject, _TEXT +;; Allocate a string. +;; RCX == EEType +;; EDX == character/element count +LEAF_ENTRY RhNewString, _TEXT + + ; we want to limit the element count to the non-negative 32-bit int range + cmp rdx, 07fffffffh + ja StringSizeOverflow + + ; Compute overall allocation size (align(base size + (element size * elements), 8)). + lea rax, [(rdx * STRING_COMPONENT_SIZE) + (STRING_BASE_SIZE + 7)] + and rax, -8 + + ; rax == string size + ; rcx == EEType + ; rdx == element count + + INLINE_GETTHREAD r10, r8 + + mov r8, rax + add rax, [r10 + OFFSETOF__Thread__m_alloc_context__alloc_ptr] + jc RhpNewArrayRare + + ; rax == new alloc ptr + ; rcx == EEType + ; rdx == element count + ; r8 == array size + ; r10 == thread + cmp rax, [r10 + OFFSETOF__Thread__m_alloc_context__alloc_limit] + ja RhpNewArrayRare + + mov [r10 + OFFSETOF__Thread__m_alloc_context__alloc_ptr], rax + + ; calc the new object pointer + sub rax, r8 + + mov [rax + OFFSETOF__Object__m_pEEType], rcx + mov [rax + OFFSETOF__String__m_Length], edx + + ret + +StringSizeOverflow: + ; We get here if the size of the final string object can't be represented as an unsigned + ; 32-bit value. We're going to tail-call to a managed helper that will throw + ; an OOM exception that the caller of this allocator understands. + + ; rcx holds EEType pointer already + xor edx, edx ; Indicate that we should throw OOM. + jmp RhExceptionHandling_FailedAllocation +LEAF_END RhNewString, _TEXT + + ;; Allocate one dimensional, zero based array (SZARRAY). ;; RCX == EEType ;; EDX == element count diff --git a/external/corert/src/Native/Runtime/amd64/AsmMacros.inc b/external/corert/src/Native/Runtime/amd64/AsmMacros.inc index 4cecf334b2..799dc2fde1 100644 --- a/external/corert/src/Native/Runtime/amd64/AsmMacros.inc +++ b/external/corert/src/Native/Runtime/amd64/AsmMacros.inc @@ -215,6 +215,24 @@ Name label proc PUBLIC Name endm +EXPORT_POINTER_TO_ADDRESS macro Name + + local AddressToExport + +AddressToExport label proc + + .const + + align 8 + +Name dq offset AddressToExport + + public Name + + .code + + endm + _tls_array equ 58h ;; offsetof(TEB, ThreadLocalStoragePointer) ;; @@ -352,7 +370,15 @@ PTFF_SAVE_RAX equ 00000100h ;; RAX is saved if it contains a GC ref PTFF_SAVE_ALL_SCRATCH equ 00007F00h PTFF_RAX_IS_GCREF equ 00010000h ;; iff PTFF_SAVE_RAX: set -> eax is Object, clear -> eax is scalar PTFF_RAX_IS_BYREF equ 00020000h ;; iff PTFF_SAVE_RAX: set -> eax is ByRef, clear -> eax is Object or scalar +PTFF_THREAD_ABORT equ 00040000h ;; indicates that ThreadAbortException should be thrown when returning from the transition +;; These must match the TrapThreadsFlags enum +TrapThreadsFlags_None equ 0 +TrapThreadsFlags_AbortInProgress equ 1 +TrapThreadsFlags_TrapThreads equ 2 + +;; This must match HwExceptionCode.STATUS_REDHAWK_THREAD_ABORT +STATUS_REDHAWK_THREAD_ABORT equ 43h ;; ;; CONSTANTS -- SYMBOLS diff --git a/external/corert/src/Native/Runtime/amd64/AsmOffsetsCpu.h b/external/corert/src/Native/Runtime/amd64/AsmOffsetsCpu.h index 6065092201..aa7d4416c0 100644 --- a/external/corert/src/Native/Runtime/amd64/AsmOffsetsCpu.h +++ b/external/corert/src/Native/Runtime/amd64/AsmOffsetsCpu.h @@ -22,7 +22,7 @@ PLAT_ASM_OFFSET(250, ExInfo, m_notifyDebuggerSP) PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_RIP) PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_FramePointer) PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_pThread) -PLAT_ASM_OFFSET(18, PInvokeTransitionFrame, m_dwFlags) +PLAT_ASM_OFFSET(18, PInvokeTransitionFrame, m_Flags) PLAT_ASM_OFFSET(20, PInvokeTransitionFrame, m_PreservedRegs) PLAT_ASM_SIZEOF(230, StackFrameIterator) @@ -83,7 +83,7 @@ PLAT_ASM_OFFSET(198, ExInfo, m_notifyDebuggerSP) PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_RIP) PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_FramePointer) PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_pThread) -PLAT_ASM_OFFSET(18, PInvokeTransitionFrame, m_dwFlags) +PLAT_ASM_OFFSET(18, PInvokeTransitionFrame, m_Flags) PLAT_ASM_OFFSET(20, PInvokeTransitionFrame, m_PreservedRegs) PLAT_ASM_SIZEOF(178, StackFrameIterator) diff --git a/external/corert/src/Native/Runtime/amd64/CallDescrWorker.S b/external/corert/src/Native/Runtime/amd64/CallDescrWorker.S index 6a980b3948..a3879d5176 100644 --- a/external/corert/src/Native/Runtime/amd64/CallDescrWorker.S +++ b/external/corert/src/Native/Runtime/amd64/CallDescrWorker.S @@ -6,17 +6,9 @@ #include NESTED_ENTRY RhCallDescrWorker, _TEXT, NoHandler -LOCAL_LABEL(ReturnFromCallDescrThunk): + + EXPORT_POINTER_TO_ADDRESS PointerToReturnFromCallDescrThunk // UNIXTODO: Implement this function int 3 NESTED_END RhCallDescrWorker, _TEXT - - .text - - .align 8 - -C_FUNC(PointerToReturnFromCallDescrThunk): - .quad LOCAL_LABEL(ReturnFromCallDescrThunk) - - .global C_FUNC(PointerToReturnFromCallDescrThunk) diff --git a/external/corert/src/Native/Runtime/amd64/CallDescrWorker.asm b/external/corert/src/Native/Runtime/amd64/CallDescrWorker.asm index a0d07ea454..59d4fb9167 100644 --- a/external/corert/src/Native/Runtime/amd64/CallDescrWorker.asm +++ b/external/corert/src/Native/Runtime/amd64/CallDescrWorker.asm @@ -57,7 +57,7 @@ StackCopyLoop: ; copy the arguments to stack top-down t DoCall: call qword ptr [rbx + OFFSETOF__CallDescrData__pTarget] ; call target function -ReturnFromCallDescrThunk label proc + EXPORT_POINTER_TO_ADDRESS PointerToReturnFromCallDescrThunk ; Symbol used to identify thunk call to managed function so the special ; case unwinder can unwind through this function. Sadly we cannot directly @@ -103,14 +103,4 @@ ReturnsDouble: NESTED_END RhCallDescrWorker, _TEXT - .const - - align 8 - -PointerToReturnFromCallDescrThunk label qword - - dq offset ReturnFromCallDescrThunk - - public PointerToReturnFromCallDescrThunk - end diff --git a/external/corert/src/Native/Runtime/amd64/ExceptionHandling.S b/external/corert/src/Native/Runtime/amd64/ExceptionHandling.S index aa1a59df10..fcf4dfbda0 100644 --- a/external/corert/src/Native/Runtime/amd64/ExceptionHandling.S +++ b/external/corert/src/Native/Runtime/amd64/ExceptionHandling.S @@ -3,8 +3,8 @@ // See the LICENSE file in the project root for more information. .intel_syntax noprefix -#include #include // generated by the build from AsmOffsets.cpp +#include ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -70,7 +70,8 @@ NESTED_ENTRY RhpThrowHwEx, _TEXT, NoHandler // rdi still contains the exception code // rsi contains the address of the ExInfo call C_FUNC(RhThrowHwEx) -ALTERNATE_ENTRY RhpThrowHwEx2 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowHwEx2 // no return int 3 @@ -150,7 +151,8 @@ NESTED_ENTRY RhpThrowEx, _TEXT, NoHandler // rdi still contains the exception object // rsi contains the address of the ExInfo call C_FUNC(RhThrowEx) -ALTERNATE_ENTRY RhpThrowEx2 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowEx2 // no return int 3 @@ -219,7 +221,8 @@ NESTED_ENTRY RhpRethrow, _TEXT, NoHandler // rdi contains the currently active ExInfo // rsi contains the address of the new ExInfo call C_FUNC(RhRethrow) -ALTERNATE_ENTRY RhpRethrow2 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpRethrow2 // no return int 3 @@ -330,7 +333,8 @@ NESTED_ENTRY RhpCallCatchFunclet, _TEXT, NoHandler mov rdi, [rdx + OFFSETOF__REGDISPLAY__SP] // rdi <- establisher frame mov rsi, [rsp + locArg0] // rsi <- exception object call qword ptr [rsp + locArg1] // call handler funclet -ALTERNATE_ENTRY RhpCallCatchFunclet2 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallCatchFunclet2 mov rdx, [rsp + locArg2] // rdx <- dispatch context @@ -468,7 +472,8 @@ NESTED_ENTRY RhpCallFinallyFunclet, _TEXT, NoHandler mov rdi, [rsi + OFFSETOF__REGDISPLAY__SP] // rdi <- establisher frame call qword ptr [rsp + locArg0] // handler funclet address -ALTERNATE_ENTRY RhpCallFinallyFunclet2 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFinallyFunclet2 mov rsi, [rsp + locArg1] // rsi <- regdisplay @@ -517,7 +522,8 @@ NESTED_ENTRY RhpCallFilterFunclet, _TEXT, NoHandler mov rsi, rdi // rsi <- exception object mov rdi, [rdx + OFFSETOF__REGDISPLAY__SP] // rdi <- establisher frame call rax -ALTERNATE_ENTRY RhpCallFilterFunclet2 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFilterFunclet2 // RAX contains the result of the filter execution diff --git a/external/corert/src/Native/Runtime/amd64/ExceptionHandling.asm b/external/corert/src/Native/Runtime/amd64/ExceptionHandling.asm index a8ba9ef77c..566dad71f1 100644 --- a/external/corert/src/Native/Runtime/amd64/ExceptionHandling.asm +++ b/external/corert/src/Native/Runtime/amd64/ExceptionHandling.asm @@ -92,14 +92,14 @@ NESTED_ENTRY RhpThrowHwEx, _TEXT ;; rcx still contains the exception code ;; rdx contains the address of the ExInfo call RhThrowHwEx -ALTERNATE_ENTRY RhpThrowHwEx2 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowHwEx2 ;; no return int 3 NESTED_END RhpThrowHwEx, _TEXT - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; RhpThrowEx @@ -184,7 +184,8 @@ NESTED_ENTRY RhpThrowEx, _TEXT ;; rcx still contains the exception object ;; rdx contains the address of the ExInfo call RhThrowEx -ALTERNATE_ENTRY RhpThrowEx2 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowEx2 ;; no return int 3 @@ -267,7 +268,8 @@ NESTED_ENTRY RhpRethrow, _TEXT ;; rcx contains the currently active ExInfo ;; rdx contains the address of the new ExInfo call RhRethrow -ALTERNATE_ENTRY RhpRethrow2 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpRethrow2 ;; no return int 3 @@ -351,11 +353,12 @@ endm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; NESTED_ENTRY RhpCallCatchFunclet, _TEXT - FUNCLET_CALL_PROLOGUE 2, 1 + FUNCLET_CALL_PROLOGUE 3, 0 ;; locals rsp_offsetof_thread = rsp_offsetof_locals rsp_offsetof_resume_ip = rsp_offsetof_locals + 8; + rsp_offsetof_is_handling_thread_abort = rsp_offsetof_locals + 16; mov [rsp + rsp_offsetof_arguments + 0h], rcx ;; save arguments for later mov [rsp + rsp_offsetof_arguments + 8h], rdx @@ -365,6 +368,9 @@ NESTED_ENTRY RhpCallCatchFunclet, _TEXT INLINE_GETTHREAD rax, rbx ;; rax <- Thread*, rbx is trashed mov [rsp + rsp_offsetof_thread], rax ;; save Thread* for later + cmp rcx, [rax + OFFSETOF__Thread__m_threadAbortException] + setz byte ptr [rsp + rsp_offsetof_is_handling_thread_abort] + ;; Clear the DoNotTriggerGc state before calling out to our managed catch funclet. lock and dword ptr [rax + OFFSETOF__Thread__m_ThreadStateFlags], NOT TSF_DoNotTriggerGc @@ -418,21 +424,21 @@ endif movdqa xmm14,[r8 + OFFSETOF__REGDISPLAY__Xmm + 8*10h] movdqa xmm15,[r8 + OFFSETOF__REGDISPLAY__Xmm + 9*10h] -ifdef CORERT ;; @TODO Reconcile +ifdef PROJECTN ;; @TODO Reconcile + mov rcx, [rsp + rsp_offsetof_arguments + 0h] ;; rcx <- exception object +else mov rcx, [r8 + OFFSETOF__REGDISPLAY__SP] ;; rcx <- establisher frame mov rdx, [rsp + rsp_offsetof_arguments + 0h] ;; rdx <- exception object -else - mov rcx, [rsp + rsp_offsetof_arguments + 0h] ;; rcx <- exception object endif call qword ptr [rsp + rsp_offsetof_arguments + 8h] ;; call handler funclet -ALTERNATE_ENTRY RhpCallCatchFunclet2 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallCatchFunclet2 mov r8, [rsp + rsp_offsetof_arguments + 10h] ;; r8 <- dispatch context ifdef _DEBUG ;; Call into some C++ code to validate the pop of the ExInfo. We only do this in debug because we ;; have to spill all the preserved registers and then refill them after the call. - mov [rsp + rsp_offsetof_resume_ip], rax ;; save resume IP for later mov rcx, [r8 + OFFSETOF__REGDISPLAY__pRbx] @@ -495,10 +501,24 @@ endif @@: mov [rdx + OFFSETOF__Thread__m_pExInfoStackHead], rcx ;; store the new head on the Thread + test [RhpTrapThreads], TrapThreadsFlags_AbortInProgress + jz @f + + ;; test if the exception handled by the catch was the ThreadAbortException + cmp byte ptr [rsp + rsp_offsetof_is_handling_thread_abort], 0 + je @f + + ;; It was the ThreadAbortException, so rethrow it + mov rcx, STATUS_REDHAWK_THREAD_ABORT + mov rdx, rax ;; rdx <- continuation address as exception RIP + mov rsp, r8 ;; reset the SP to resume SP value + jmp RhpThrowHwEx ;; Throw the ThreadAbortException as a special kind of hardware exception + ;; reset RSP and jump to the continuation address - mov rsp, r8 ;; reset the SP + @@: mov rsp, r8 ;; reset the SP to resume SP value jmp rax + NESTED_END RhpCallCatchFunclet, _TEXT ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -584,7 +604,8 @@ endif mov rcx, [rdx + OFFSETOF__REGDISPLAY__SP] ;; rcx <- establisher frame call qword ptr [rsp + rsp_offsetof_arguments + 0h] ;; handler funclet address -ALTERNATE_ENTRY RhpCallFinallyFunclet2 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFinallyFunclet2 mov rdx, [rsp + rsp_offsetof_arguments + 8h] ;; rdx <- regdisplay @@ -626,7 +647,6 @@ ALTERNATE_ENTRY RhpCallFinallyFunclet2 NESTED_END RhpCallFinallyFunclet, _TEXT - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; void* FASTCALL RhpCallFilterFunclet(RtuObjectRef exceptionObj, void* pFilterIP, REGDISPLAY* pRegDisplay) @@ -646,14 +666,15 @@ NESTED_ENTRY RhpCallFilterFunclet, _TEXT mov rbp, [rax] mov rax, rdx ;; rax <- handler funclet address -ifdef CORERT ;; @TODO Reconcile +ifdef PROJECTN ;; @TODO Reconcile + ;; RCX still contains the exception object +else mov rdx, rcx ;; rdx <- exception object mov rcx, [r8 + OFFSETOF__REGDISPLAY__SP] ;; rcx <- establisher frame -else - ;; RCX still contains the exception object endif call rax -ALTERNATE_ENTRY RhpCallFilterFunclet2 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFilterFunclet2 ;; RAX contains the result of the filter execution diff --git a/external/corert/src/Native/Runtime/amd64/GcProbe.asm b/external/corert/src/Native/Runtime/amd64/GcProbe.asm index 02e977d4ff..a07e08a08d 100644 --- a/external/corert/src/Native/Runtime/amd64/GcProbe.asm +++ b/external/corert/src/Native/Runtime/amd64/GcProbe.asm @@ -59,7 +59,10 @@ endm ;; POP_PROBE_FRAME macro extraStack movdqa xmm0, [rsp + 20h] - add rsp, 20h + 10h + (extraStack AND (10h-1)) + 4*8 + add rsp, 20h + 10h + (extraStack AND (10h-1)) + 8 + pop rbp + pop rax ; discard Thread* + pop rax ; discard BITMASK pop rbx pop rsi pop rdi @@ -124,14 +127,14 @@ endm ;; All other registers trashed ;; -EXTERN RhpWaitForGC : PROC +EXTERN RhpWaitForGCNoAbort : PROC WaitForGCCompletion macro test dword ptr [rbx + OFFSETOF__Thread__m_ThreadStateFlags], TSF_SuppressGcStress + TSF_DoNotTriggerGc jnz @F mov rcx, [rbx + OFFSETOF__Thread__m_pHackPInvokeTunnel] - call RhpWaitForGC + call RhpWaitForGCNoAbort @@: endm @@ -215,9 +218,11 @@ NESTED_END RhpGcStressProbe, _TEXT endif ;; FEATURE_GC_STRESS +EXTERN RhpThrowHwEx : PROC + NESTED_ENTRY RhpGcProbe, _TEXT - cmp [RhpTrapThreads], 0 - jne @f + test [RhpTrapThreads], TrapThreadsFlags_TrapThreads + jnz @f ret @@: PUSH_PROBE_FRAME rdx, rax, 0, rcx @@ -226,8 +231,17 @@ NESTED_ENTRY RhpGcProbe, _TEXT mov rbx, rdx WaitForGCCompletion + mov rax, [rbx + OFFSETOF__Thread__m_pHackPInvokeTunnel] + test dword ptr [rax + OFFSETOF__PInvokeTransitionFrame__m_Flags], PTFF_THREAD_ABORT + jnz Abort POP_PROBE_FRAME 0 ret +Abort: + POP_PROBE_FRAME 0 + mov rcx, STATUS_REDHAWK_THREAD_ABORT + pop rdx ;; return address as exception RIP + jmp RhpThrowHwEx ;; Throw the ThreadAbortException as a special kind of hardware exception + NESTED_END RhpGcProbe, _TEXT @@ -483,8 +497,8 @@ ifdef _DEBUG ;; If we get here, then we have been hijacked for a real GC, and our SyncState must ;; reflect that we've been requested to synchronize. - cmp [RhpTrapThreads], 0 - jne @F + test [RhpTrapThreads], TrapThreadsFlags_TrapThreads + jnz @F call RhDebugBreak @@: @@ -535,6 +549,11 @@ EXTERN g_fHasFastFxsave : BYTE FXSAVE_SIZE equ 512 +;; Trap a loop to GC. +;; Set up the P/Invoke transition frame with the original loop target as the safe point. +;; All registers, both volatile and non-volatile, are preserved. +;; Input: ModuleHeader, chunk starting index and chunk sub-index which are used to get the original loop target +;; The function is not called but jumped directly NESTED_ENTRY RhpLoopHijack, _TEXT sizeof_OutgoingScratchSpace equ 20h @@ -560,7 +579,7 @@ NESTED_ENTRY RhpLoopHijack, _TEXT ;; [rsp + 20] -> m_RIP -------| ;; [rsp + 28] -> m_FramePointer | ;; [rsp + 30] -> m_pThread | - ;; [rsp + 38] -> m_dwFlags / m_dwAlignPad2 | + ;; [rsp + 38] -> m_Flags / m_dwAlignPad2 | ;; [rsp + 40] -> rbx save | ;; [rsp + 48] -> rsi save | ;; [rsp + 50] -> rdi save | @@ -640,7 +659,7 @@ NESTED_ENTRY RhpLoopHijack, _TEXT push_nonvol_reg rdi push_nonvol_reg rsi push_nonvol_reg rbx - push_vol_reg PROBE_SAVE_FLAGS_EVERYTHING ; m_dwFlags / m_dwAlignPad2 + push_vol_reg PROBE_SAVE_FLAGS_EVERYTHING ; m_Flags / m_dwAlignPad2 ;; rdx <- GetThread(), TRASHES rcx INLINE_GETTHREAD rdx, rcx @@ -730,7 +749,7 @@ ifdef FEATURE_GC_STRESS endif ;; FEATURE_GC_STRESS lea rcx, [rsp + sizeof_OutgoingScratchSpace] ; calculate PInvokeTransitionFrame pointer - call RhpWaitForGC + call RhpWaitForGCNoAbort DoneWaitingForGc: ;; Prepare for our return by stashing a scratch register where we can pop it just before returning @@ -762,10 +781,12 @@ endif ;; FEATURE_GC_STRESS DontRestoreXmmAgain: add rsp, sizeof_OutgoingScratchSpace + mov eax, [rsp + OFFSETOF__PInvokeTransitionFrame__m_Flags] + test eax, PTFF_THREAD_ABORT pop rax ; m_RIP pop rbp ; m_FramePointer pop rax ; m_pThread - pop rax ; m_dwFlags / m_dwAlign2 + pop rax ; m_Flags / m_dwAlign2 pop rbx pop rsi pop rdi @@ -793,6 +814,15 @@ DontRestoreXmmAgain: ;; The final step is to restore eflags, rcx, and return back to the loop target location. lea rsp, [rcx - 20h] + jz @f ;; result of the test instruction before the pops above + popfq ;; restore flags + pop rcx ;; discard ModuleHeader* + pop rcx ;; restore rcx + mov rcx, STATUS_REDHAWK_THREAD_ABORT + pop rdx ;; return address as exception RIP + jmp RhpThrowHwEx ;; Throw the ThreadAbortException as a special kind of hardware exception + +@@: popfq ;; restore flags pop rcx ;; discard ModuleHeader* pop rcx ;; restore rcx @@ -800,6 +830,271 @@ DontRestoreXmmAgain: NESTED_END RhpLoopHijack, _TEXT +;; Trap to GC. +;; Set up the P/Invoke transition frame with the return address as the safe point. +;; All registers, both volatile and non-volatile, are preserved. +;; The function should be called not jumped because it's expecting the return address +NESTED_ENTRY RhpTrapToGC, _TEXT + + sizeof_OutgoingScratchSpace equ 20h + sizeof_PInvokeFrame equ OFFSETOF__PInvokeTransitionFrame__m_PreservedRegs + 15*8 + sizeof_XmmAlignPad equ 8 + sizeof_XmmSave equ FXSAVE_SIZE + sizeof_MachineFrame equ 6*8 + sizeof_InitialPushedArgs equ 3*8 ;; eflags, rcx, return value + sizeof_FixedFrame equ sizeof_OutgoingScratchSpace + sizeof_PInvokeFrame + sizeof_XmmAlignPad + sizeof_XmmSave + sizeof_MachineFrame + + ;; On the stack on entry: + ;; [rsp ] -> Return address + + ;; Prepare for our return by stashing a scratch register where we can pop it just before returning + ;; The scratch register will be used as PSP in the epilog + push rcx + + ;; save eflags before we trash them + pushfq + + ;; What we want to get to: + ;; + ;; [rsp ] -> outgoing scratch area + ;; + ;; [rsp + 20] -> m_RIP -------| + ;; [rsp + 28] -> m_FramePointer | + ;; [rsp + 30] -> m_pThread | + ;; [rsp + 38] -> m_Flags / m_dwAlignPad2 | + ;; [rsp + 40] -> rbx save | + ;; [rsp + 48] -> rsi save | + ;; [rsp + 50] -> rdi save | + ;; [rsp + 58] -> r12 save | + ;; [rsp + 60] -> r13 save | + ;; [rsp + 68] -> r14 save | PInvokeTransitionFrame + ;; [rsp + 70] -> r15 save | + ;; [rsp + 78] -> rsp save | + ;; [rsp + 80] -> rax save | + ;; [rsp + 88] -> rcx save | + ;; [rsp + 90] -> rdx save | + ;; [rsp + 98] -> r8 save | + ;; [rsp + a0] -> r9 save | + ;; [rsp + a8] -> r10 save | + ;; [rsp + b0] -> r11 save -------| + ;; + ;; [rsp + b8] -> [XmmAlignPad] + ;; + ;; [rsp + c0] -> FXSAVE area + ;; + ;; [rsp +2c0] | RIP | + ;; [rsp +2c8] | CS | + ;; [rsp +2d0] | EFLAGS | <-- 'machine frame' + ;; [rsp +2d8] | RSP | + ;; [rsp +2e0] | SS | + ;; [rsp +2e8] | padding | + ;; + ;; [rsp +2f0] [optional stack alignment] + ;; + ;; [PSP - 18] -> eflags save + ;; [PSP - 10] -> rcx save + ;; [PSP - 8] -> Return address + ;; [PSP] -> caller's frame + + test rsp, 0Fh + jz AlreadyAligned + + sub rsp, sizeof_XmmAlignPad + sizeof_XmmSave + sizeof_MachineFrame + 8 ; +8 to align RSP + push r11 ; save incoming R11 into save location + lea r11, [rsp + 8 + sizeof_XmmAlignPad + sizeof_XmmSave + sizeof_MachineFrame + 8 + sizeof_InitialPushedArgs] + jmp PspCalculated + + AlreadyAligned: + sub rsp, sizeof_XmmAlignPad + sizeof_XmmSave + sizeof_MachineFrame + push r11 ; save incoming R11 into save location + lea r11, [rsp + 8 + sizeof_XmmAlignPad + sizeof_XmmSave + sizeof_MachineFrame + sizeof_InitialPushedArgs] + + PspCalculated: + push r10 ; save incoming R10 into save location + xor r10d, r10d + + ;; + ;; Populate the 'machine frame' in the diagram above. We have only pushed up to the 'r10 save', so we have not + ;; yet pushed 0xA8 bytes of that diagram. + ;; + ;; [rsp + {offset-in-target-frame-layout-diagram} - {as-yet-unpushed-stack-size}] + mov [rsp + 2c0h - 0a8h], r10 ; init RIP to zero + mov [rsp + 2c8h - 0a8h], r10 ; init CS to zero + mov [rsp + 2d0h - 0a8h], r10 ; init EFLAGS to zero + mov [rsp + 2d8h - 0a8h], r11 ; save PSP in the 'machine frame' + mov [rsp + 2e0h - 0a8h], r10 ; init SS to zero + + .pushframe + .allocstack sizeof_XmmAlignPad + sizeof_XmmSave + 2*8 ;; only 2 of the regs from the PInvokeTransitionFrame are on the stack + + push_vol_reg r9 + push_vol_reg r8 + push_vol_reg rdx + push_vol_reg rcx + push_vol_reg rax + push_vol_reg r11 ; PSP gets saved into the PInvokeTransitionFrame + push_nonvol_reg r15 + push_nonvol_reg r14 + push_nonvol_reg r13 + push_nonvol_reg r12 + push_nonvol_reg rdi + push_nonvol_reg rsi + push_nonvol_reg rbx + push_vol_reg PROBE_SAVE_FLAGS_EVERYTHING ; m_Flags / m_dwAlignPad2 + + ;; rdx <- GetThread(), TRASHES rcx + INLINE_GETTHREAD rdx, rcx + + push_vol_reg rdx ; m_pThread + push_nonvol_reg rbp ; m_FramePointer + push_vol_reg r10 ; m_RIP + + alloc_stack sizeof_OutgoingScratchSpace + END_PROLOGUE + + mov rbx, r11 ; put PSP into RBX + mov rsi, rdx ; put Thread* into RSI + + ; RBX is PSP + ; RSI is Thread* + + fxsave [rsp + 0c0h] + + cmp [g_fHasFastFxsave], 0 ; fast fxsave won't save the xmm registers, so we must do it + jz DontSaveXmmAgain + + ;; 0C0h -> offset of FXSAVE area + ;; 0A0h -> offset of xmm0 save area within the FXSAVE area + movdqa [rsp + 0c0h + 0a0h + 0*10h], xmm0 + movdqa [rsp + 0c0h + 0a0h + 1*10h], xmm1 + movdqa [rsp + 0c0h + 0a0h + 2*10h], xmm2 + movdqa [rsp + 0c0h + 0a0h + 3*10h], xmm3 + movdqa [rsp + 0c0h + 0a0h + 4*10h], xmm4 + movdqa [rsp + 0c0h + 0a0h + 5*10h], xmm5 + movdqa [rsp + 0c0h + 0a0h + 6*10h], xmm6 + movdqa [rsp + 0c0h + 0a0h + 7*10h], xmm7 + movdqa [rsp + 0c0h + 0a0h + 8*10h], xmm8 + movdqa [rsp + 0c0h + 0a0h + 9*10h], xmm9 + movdqa [rsp + 0c0h + 0a0h + 10*10h], xmm10 + movdqa [rsp + 0c0h + 0a0h + 11*10h], xmm11 + movdqa [rsp + 0c0h + 0a0h + 12*10h], xmm12 + movdqa [rsp + 0c0h + 0a0h + 13*10h], xmm13 + movdqa [rsp + 0c0h + 0a0h + 14*10h], xmm14 + movdqa [rsp + 0c0h + 0a0h + 15*10h], xmm15 + +DontSaveXmmAgain: + mov rax, [rbx - 8] + mov [rsp + 2c0h], rax ; save return address into 'machine frame' + mov [rsp + 20h], rax ; save return address into PInvokeTransitionFrame + + ; Early out if GC stress is currently suppressed. Do this after we have computed the real address to + ; return to but before we link the transition frame onto m_pHackPInvokeTunnel (because hitting this + ; condition implies we're running restricted callouts during a GC itself and we could end up + ; overwriting a co-op frame set by the code that caused the GC in the first place, e.g. a GC.Collect + ; call). + test dword ptr [rsi + OFFSETOF__Thread__m_ThreadStateFlags], TSF_SuppressGcStress + TSF_DoNotTriggerGc + jnz DoneWaitingForGc + + ; link the frame into the Thread + lea rcx, [rsp + sizeof_OutgoingScratchSpace] ; rcx <- PInvokeTransitionFrame* + mov [rsi + OFFSETOF__Thread__m_pHackPInvokeTunnel], rcx + + ;; + ;; Unhijack this thread, if necessary. + ;; + INLINE_THREAD_UNHIJACK rsi, rax, rcx ;; trashes RAX, RCX + +ifdef FEATURE_GC_STRESS + xor eax, eax + cmp [g_fGcStressStarted], eax + jz @F + + mov rdx, [rsp + 2c0h] + mov rcx, [g_pTheRuntimeInstance] + call RuntimeInstance__ShouldHijackLoopForGcStress + cmp al, 0 + je @F + + call REDHAWKGCINTERFACE__STRESSGC +@@: +endif ;; FEATURE_GC_STRESS + + lea rcx, [rsp + sizeof_OutgoingScratchSpace] ; calculate PInvokeTransitionFrame pointer + call RhpWaitForGCNoAbort + + DoneWaitingForGc: + mov rcx, rbx ; RCX <- PSP + + fxrstor [rsp + 0c0h] + + cmp [g_fHasFastFxsave], 0 + jz DontRestoreXmmAgain + + movdqa xmm0 , [rsp + 0c0h + 0a0h + 0*10h] + movdqa xmm1 , [rsp + 0c0h + 0a0h + 1*10h] + movdqa xmm2 , [rsp + 0c0h + 0a0h + 2*10h] + movdqa xmm3 , [rsp + 0c0h + 0a0h + 3*10h] + movdqa xmm4 , [rsp + 0c0h + 0a0h + 4*10h] + movdqa xmm5 , [rsp + 0c0h + 0a0h + 5*10h] + movdqa xmm6 , [rsp + 0c0h + 0a0h + 6*10h] + movdqa xmm7 , [rsp + 0c0h + 0a0h + 7*10h] + movdqa xmm8 , [rsp + 0c0h + 0a0h + 8*10h] + movdqa xmm9 , [rsp + 0c0h + 0a0h + 9*10h] + movdqa xmm10, [rsp + 0c0h + 0a0h + 10*10h] + movdqa xmm11, [rsp + 0c0h + 0a0h + 11*10h] + movdqa xmm12, [rsp + 0c0h + 0a0h + 12*10h] + movdqa xmm13, [rsp + 0c0h + 0a0h + 13*10h] + movdqa xmm14, [rsp + 0c0h + 0a0h + 14*10h] + movdqa xmm15, [rsp + 0c0h + 0a0h + 15*10h] + +DontRestoreXmmAgain: + add rsp, sizeof_OutgoingScratchSpace + mov eax, [rsp + OFFSETOF__PInvokeTransitionFrame__m_Flags] + test eax, PTFF_THREAD_ABORT + pop rax ; m_RIP + pop rbp ; m_FramePointer + pop rax ; m_pThread + pop rax ; m_Flags / m_dwAlign2 + pop rbx + pop rsi + pop rdi + pop r12 + pop r13 + pop r14 + pop r15 + pop rax ; RSP + pop rax ; RAX save + pop rdx ; RCX save (intentionally discarding it) + pop rdx + pop r8 + pop r9 + pop r10 + pop r11 + + + ;; RCX is PSP at this point and the stack looks like this: + ;; [PSP - 18] -> eflags save + ;; [PSP - 10] -> rcx save + ;; [PSP - 8] -> return address + ;; [PSP] -> caller's frame + ;; + ;; The final step is to restore eflags, rcx, and return back to the loop target location. + + lea rsp, [rcx - 18h] + jz @f ;; result of the test instruction before the pops above + popfq ;; restore flags + pop rcx ;; restore rcx + mov rcx, STATUS_REDHAWK_THREAD_ABORT + pop rdx ;; return address as exception RIP + jmp RhpThrowHwEx ;; Throw the ThreadAbortException as a special kind of hardware exception + +@@: + popfq ;; restore flags + pop rcx ;; restore rcx + ret + +NESTED_END RhpTrapToGC, _TEXT + ifdef FEATURE_GC_STRESS ;; ;; INVARIANT: Don't trash the argument registers, the binder codegen depends on this. diff --git a/external/corert/src/Native/Runtime/amd64/InteropThunksHelpers.S b/external/corert/src/Native/Runtime/amd64/InteropThunksHelpers.S index 47011c295e..6e1e2feba4 100644 --- a/external/corert/src/Native/Runtime/amd64/InteropThunksHelpers.S +++ b/external/corert/src/Native/Runtime/amd64/InteropThunksHelpers.S @@ -3,8 +3,8 @@ // See the LICENSE file in the project root for more information. .intel_syntax noprefix -#include #include // generated by the build from AsmOffsets.cpp +#include #define POINTER_SIZE 8 LEAF_ENTRY RhCommonStub, _TEXT diff --git a/external/corert/src/Native/Runtime/amd64/PInvoke.S b/external/corert/src/Native/Runtime/amd64/PInvoke.S index 958da80f5e..bf623819db 100644 --- a/external/corert/src/Native/Runtime/amd64/PInvoke.S +++ b/external/corert/src/Native/Runtime/amd64/PInvoke.S @@ -3,8 +3,8 @@ // See the LICENSE file in the project root for more information. .intel_syntax noprefix -#include #include // generated by the build from AsmOffsets.cpp +#include // // RhpPInvoke @@ -28,14 +28,14 @@ NESTED_ENTRY RhpPInvoke, _TEXT, NoHandler mov qword ptr [rbx + OFFSETOF__PInvokeTransitionFrame__m_RIP], r11 lea r11, [rsp + 0x10] // r11 <- caller SP - mov dword ptr [rbx + OFFSETOF__PInvokeTransitionFrame__m_dwFlags], PTFF_SAVE_RSP + mov dword ptr [rbx + OFFSETOF__PInvokeTransitionFrame__m_Flags], PTFF_SAVE_RSP mov qword ptr [rbx + OFFSETOF__PInvokeTransitionFrame__m_PreservedRegs], r11 mov qword ptr [rax + OFFSETOF__Thread__m_pTransitionFrame], rbx - cmp dword ptr [C_VAR(RhpTrapThreads)], 0 + test dword ptr [C_VAR(RhpTrapThreads)], TrapThreadsFlags_TrapThreads pop_nonvol_reg rbx - jne 0f // forward branch - predicted not taken + jnz 0f // forward branch - predicted not taken ret 0: jmp C_FUNC(RhpWaitForSuspend2) @@ -50,7 +50,7 @@ NESTED_END RhpPInvoke, _TEXT LEAF_ENTRY RhpPInvokeReturn, _TEXT mov rsi, [rdi + OFFSETOF__PInvokeTransitionFrame__m_pThread] mov qword ptr [rsi + OFFSETOF__Thread__m_pTransitionFrame], 0 - cmp dword ptr [C_VAR(RhpTrapThreads)], 0 + cmp dword ptr [C_VAR(RhpTrapThreads)], TrapThreadsFlags_None jne 0f // forward branch - predicted not taken ret 0: diff --git a/external/corert/src/Native/Runtime/amd64/PInvoke.asm b/external/corert/src/Native/Runtime/amd64/PInvoke.asm index 9f5be25466..b4435852d5 100644 --- a/external/corert/src/Native/Runtime/amd64/PInvoke.asm +++ b/external/corert/src/Native/Runtime/amd64/PInvoke.asm @@ -35,8 +35,12 @@ NESTED_ENTRY RhpWaitForSuspend, _TEXT END_PROLOGUE + test [RhpTrapThreads], TrapThreadsFlags_TrapThreads + jz NoWait + call RhpWaitForSuspend2 +NoWait: movdqa xmm0, [rsp + 20h + 0*10h] movdqa xmm1, [rsp + 20h + 1*10h] movdqa xmm2, [rsp + 20h + 2*10h] @@ -55,7 +59,7 @@ NESTED_END RhpWaitForSuspend, _TEXT ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; -;; RhpWaitForGC -- rare path for RhpPInvokeReturn +;; RhpWaitForGCNoAbort -- rare path for RhpPInvokeReturn ;; ;; ;; INPUT: RCX: transition frame @@ -63,7 +67,7 @@ NESTED_END RhpWaitForSuspend, _TEXT ;; TRASHES: RCX, RDX, R8, R9, R10, R11 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -NESTED_ENTRY RhpWaitForGC, _TEXT +NESTED_ENTRY RhpWaitForGCNoAbort, _TEXT push_vol_reg rax ; don't trash the integer return value alloc_stack 30h movdqa [rsp + 20h], xmm0 ; don't trash the FP return value @@ -83,6 +87,45 @@ Done: pop rax ret +NESTED_END RhpWaitForGCNoAbort, _TEXT + +EXTERN RhpThrowHwEx : PROC + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpWaitForGC -- rare path for RhpPInvokeReturn +;; +;; +;; INPUT: RCX: transition frame +;; +;; TRASHES: RCX, RDX, R8, R9, R10, R11 +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +NESTED_ENTRY RhpWaitForGC, _TEXT + push_nonvol_reg rbx + END_PROLOGUE + + mov rbx, rcx + + test [RhpTrapThreads], TrapThreadsFlags_TrapThreads + jz NoWait + + call RhpWaitForGCNoAbort +NoWait: + test [RhpTrapThreads], TrapThreadsFlags_AbortInProgress + jz Done + test dword ptr [rbx + OFFSETOF__PInvokeTransitionFrame__m_Flags], PTFF_THREAD_ABORT + jz Done + + mov rcx, STATUS_REDHAWK_THREAD_ABORT + pop rbx + pop rdx ; return address as exception RIP + jmp RhpThrowHwEx ; Throw the ThreadAbortException as a special kind of hardware exception + +Done: + pop rbx + ret + NESTED_END RhpWaitForGC, _TEXT ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -125,8 +168,8 @@ LEAF_ENTRY RhpReversePInvoke, _TEXT mov [rax], r11 mov qword ptr [r10 + OFFSETOF__Thread__m_pTransitionFrame], 0 - cmp [RhpTrapThreads], 0 - jne TrapThread + test [RhpTrapThreads], TrapThreadsFlags_TrapThreads + jnz TrapThread ret @@ -221,7 +264,7 @@ LEAF_ENTRY RhpReversePInvokeReturn, _TEXT mov rcx, [rcx + 0] ; get previous M->U transition frame mov [rdx + OFFSETOF__Thread__m_pTransitionFrame], rcx - cmp [RhpTrapThreads], 0 + cmp [RhpTrapThreads], TrapThreadsFlags_None jne RhpWaitForSuspend ret LEAF_END RhpReversePInvokeReturn, _TEXT @@ -250,12 +293,12 @@ LEAF_ENTRY RhpPInvoke, _TEXT mov qword ptr [rcx + OFFSETOF__PInvokeTransitionFrame__m_RIP], r11 lea r11, [rsp + 8] ; r11 <- caller SP - mov dword ptr [rcx + OFFSETOF__PInvokeTransitionFrame__m_dwFlags], PTFF_SAVE_RSP + mov dword ptr [rcx + OFFSETOF__PInvokeTransitionFrame__m_Flags], PTFF_SAVE_RSP mov qword ptr [rcx + OFFSETOF__PInvokeTransitionFrame__m_PreservedRegs], r11 mov qword ptr [r10 + OFFSETOF__Thread__m_pTransitionFrame], rcx - cmp [RhpTrapThreads], 0 + cmp [RhpTrapThreads], TrapThreadsFlags_None jne @F ; forward branch - predicted not taken ret @@: @@ -275,7 +318,7 @@ LEAF_END RhpPInvoke, _TEXT LEAF_ENTRY RhpPInvokeReturn, _TEXT mov rdx, [rcx + OFFSETOF__PInvokeTransitionFrame__m_pThread] mov qword ptr [rdx + OFFSETOF__Thread__m_pTransitionFrame], 0 - cmp [RhpTrapThreads], 0 + cmp [RhpTrapThreads], TrapThreadsFlags_None jne @F ; forward branch - predicted not taken ret @@: diff --git a/external/corert/src/Native/Runtime/amd64/StubDispatch.S b/external/corert/src/Native/Runtime/amd64/StubDispatch.S index 096b59d0a2..c2fe83c9cf 100644 --- a/external/corert/src/Native/Runtime/amd64/StubDispatch.S +++ b/external/corert/src/Native/Runtime/amd64/StubDispatch.S @@ -3,8 +3,8 @@ // See the LICENSE file in the project root for more information. .intel_syntax noprefix -#include #include // generated by the build from AsmOffsets.cpp +#include LEAF_ENTRY RhpCastableObjectDispatch_CommonStub, _TEXT // UNIXTODO: Implement this function diff --git a/external/corert/src/Native/Runtime/amd64/UniversalTransition.S b/external/corert/src/Native/Runtime/amd64/UniversalTransition.S index cf421c6300..0f4d0b3ef6 100644 --- a/external/corert/src/Native/Runtime/amd64/UniversalTransition.S +++ b/external/corert/src/Native/Runtime/amd64/UniversalTransition.S @@ -125,7 +125,7 @@ NESTED_ENTRY Rhp\FunctionName, _TEXT, NoHandler lea rdi, [rsp + DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK] call r10 -LOCAL_LABEL(ReturnFrom\FunctionName): + EXPORT_POINTER_TO_ADDRESS PointerToReturnFrom\FunctionName // restore fp argument registers movdqa xmm0, [rsp + DISTANCE_FROM_CHILDSP_TO_FP_REGS + 0x00] @@ -152,15 +152,6 @@ LOCAL_LABEL(ReturnFrom\FunctionName): NESTED_END Rhp\FunctionName, _TEXT - .text - - .align 8 - -C_FUNC(PointerToReturnFrom\FunctionName): - .quad LOCAL_LABEL(ReturnFrom\FunctionName) - - .global C_FUNC(PointerToReturnFrom\FunctionName) - .endm // UNIVERSAL_TRANSITION // To enable proper step-in behavior in the debugger, we need to have two instances diff --git a/external/corert/src/Native/Runtime/amd64/UniversalTransition.asm b/external/corert/src/Native/Runtime/amd64/UniversalTransition.asm index 83aebaf6d0..b582c97294 100644 --- a/external/corert/src/Native/Runtime/amd64/UniversalTransition.asm +++ b/external/corert/src/Native/Runtime/amd64/UniversalTransition.asm @@ -127,7 +127,7 @@ endif ; TRASH_SAVED_ARGUMENT_REGISTERS lea rcx, [rsp + DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK] call r10 -ReturnFrom&FunctionName label proc + EXPORT_POINTER_TO_ADDRESS PointerToReturnFrom&FunctionName ; We cannot make the label public as that tricks DIA stackwalker into thinking ; it's the beginning of a method. For this reason we export the address @@ -155,18 +155,6 @@ ReturnFrom&FunctionName label proc NESTED_END Rhp&FunctionName, _TEXT - .const - - align 8 - -PointerToReturnFrom&FunctionName label qword - - dq offset ReturnFrom&FunctionName - - public PointerToReturnFrom&FunctionName - - .code - endm ; To enable proper step-in behavior in the debugger, we need to have two instances diff --git a/external/corert/src/Native/Runtime/arm/AllocFast.S b/external/corert/src/Native/Runtime/arm/AllocFast.S index e24c657855..e4dd26643a 100644 --- a/external/corert/src/Native/Runtime/arm/AllocFast.S +++ b/external/corert/src/Native/Runtime/arm/AllocFast.S @@ -2,89 +2,555 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#include - .syntax unified .thumb -// TODO: Implement Arm support +#include // generated by the build from AsmOffsets.cpp +#include // Allocate non-array, non-finalizable object. If the allocation doesn't fit into the current thread's // allocation context then automatically fallback to the slow allocation path. // r0 == EEType - LEAF_ENTRY RhpNewFast, _TEXT -#ifdef _DEBUG - bl C_FUNC(NYI_Assert) -#endif - LEAF_END RhpNewFast, _TEXT +LEAF_ENTRY RhpNewFast, _TEXT + PROLOG_PUSH "{r4,lr}" + mov r4, r0 // save EEType + + // r0 = GetThread() + INLINE_GETTHREAD + + // r4 contains EEType pointer + ldr r2, [r4, #OFFSETOF__EEType__m_uBaseSize] + + // r0: Thread pointer + // r4: EEType pointer + // r2: base size + + ldr r3, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + add r2, r3 + ldr r1, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp r2, r1 + bhi LOCAL_LABEL(RhpNewFast_RarePath) + + // set the new alloc pointer + str r2, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Set the new object's EEType pointer + str r4, [r3, #OFFSETOF__Object__m_pEEType] + + mov r0, r3 + + EPILOG_POP "{r4,pc}" + +LOCAL_LABEL(RhpNewFast_RarePath): + mov r0, r4 // restore EEType + mov r1, #0 + EPILOG_POP "{r4,lr}" + b C_FUNC(RhpNewObject) + +LEAF_END RhpNewFast, _TEXT // Allocate non-array object with finalizer. // r0 == EEType - LEAF_ENTRY RhpNewFinalizable, _TEXT -#ifdef _DEBUG - bl C_FUNC(NYI_Assert) -#endif - LEAF_END RhpNewFinalizable, _TEXT +// +LEAF_ENTRY RhpNewFinalizable, _TEXT + mov r1, #GC_ALLOC_FINALIZE + b C_FUNC(RhpNewObject) +LEAF_END RhpNewFinalizable, _TEXT + // Allocate non-array object. // r0 == EEType // r1 == alloc flags - NESTED_ENTRY RhpNewObject, _TEXT, NoHandler -#ifdef _DEBUG - bl C_FUNC(NYI_Assert) +NESTED_ENTRY RhpNewObject, _TEXT, NoHandler + + PUSH_COOP_PINVOKE_FRAME r3 + + // r0: EEType + // r1: alloc flags + // r3: transition frame + + // Preserve the EEType in r5. + mov r5, r0 + + ldr r2, [r0, #OFFSETOF__EEType__m_uBaseSize] // cbSize + + // void* RhpGcAlloc(EEType *pEEType, UInt32 uFlags, UIntNative cbSize, void * pTransitionFrame) + blx C_FUNC(RhpGcAlloc) + + // Set the new object's EEType pointer on success. + cbz r0, LOCAL_LABEL(NewOutOfMemory) + str r5, [r0, #OFFSETOF__Object__m_pEEType] + + // If the object is bigger than RH_LARGE_OBJECT_SIZE, we must publish it to the BGC + ldr r1, [r5, #OFFSETOF__EEType__m_uBaseSize] + movw r2, #(RH_LARGE_OBJECT_SIZE & 0xFFFF) + movt r2, #(RH_LARGE_OBJECT_SIZE >> 16) + cmp r1, r2 + blo LOCAL_LABEL(New_SkipPublish) + + // r0: already contains object + // r1: already contains object size + + bl C_FUNC(RhpPublishObject) + // r0: function returned the passed-in object + +LOCAL_LABEL(New_SkipPublish): + + POP_COOP_PINVOKE_FRAME + bx lr + +LOCAL_LABEL(NewOutOfMemory): + // This is the OOM failure path. We're going to tail-call to a managed helper that will throw + // an out of memory exception that the caller of this allocator understands. + + mov r0, r5 // EEType pointer + mov r1, #0 // Indicate that we should throw OOM. + + POP_COOP_PINVOKE_FRAME + + b C_FUNC(RhExceptionHandling_FailedAllocation) + +NESTED_END RhpNewObject, _TEXT + + +// Allocate a string. +// r0 == EEType +// r1 == element/character count +LEAF_ENTRY RhNewString, _TEXT + PROLOG_PUSH "{r4-r6,lr}" + // Make sure computing the overall allocation size won't overflow + MOV32 r12, ((0xFFFFFFFF - STRING_BASE_SIZE - 3) / STRING_COMPONENT_SIZE) + cmp r1, r12 + bhi LOCAL_LABEL(StringSizeOverflow) + + // Compute overall allocation size (align(base size + (element size * elements), 4)). + mov r2, #(STRING_BASE_SIZE + 3) +#if STRING_COMPONENT_SIZE == 2 + add r2, r2, r1, lsl #1 // r2 += characters * 2 +#else + NotImplementedComponentSize #endif - NESTED_END RhpNewObject, _TEXT + bic r2, r2, #3 + + mov r4, r0 // Save EEType + mov r5, r1 // Save element count + mov r6, r2 // Save string size + // r0 = GetThread() + INLINE_GETTHREAD + // r4 == EEType + // r5 == element count + // r6 == string size + // r0 == Thread* + + // Load potential new object address into r12. + ldr r12, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Determine whether the end of the object would lie outside of the current allocation context. If so, + // we abandon the attempt to allocate the object directly and fall back to the slow helper. + adds r6, r12 + bcs LOCAL_LABEL(RhNewString_RarePath) // if we get a carry here, the string is too large to fit below 4 GB + + ldr r12, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp r6, r12 + bhi LOCAL_LABEL(RhNewString_RarePath) + + // Reload new object address into r12. + ldr r12, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Update the alloc pointer to account for the allocation. + str r6, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Set the new object's EEType pointer and element count. + str r4, [r12, #OFFSETOF__Object__m_pEEType] + str r5, [r12, #OFFSETOF__String__m_Length] + + // Return the object allocated in r0. + mov r0, r12 + EPILOG_POP "{r4-r6,pc}" + +LOCAL_LABEL(StringSizeOverflow): + // We get here if the size of the final string object can't be represented as an unsigned + // 32-bit value. We're going to tail-call to a managed helper that will throw + // an OOM exception that the caller of this allocator understands. + + // EEType is in r0 already + mov r1, 0 // Indicate that we should throw OOM + EPILOG_POP "{r4-r6,lr}" + b C_FUNC(RhExceptionHandling_FailedAllocation) + +LOCAL_LABEL(RhNewString_RarePath): + mov r3, r0 + mov r0, r4 + mov r1, r5 + mov r2, r6 + // r0 == EEType + // r1 == element count + // r2 == string size + Thread::m_alloc_context::alloc_ptr + // r3 == Thread + EPILOG_POP "{r4-r6,lr}" + b C_FUNC(RhpNewArrayRare) + +LEAF_END RhNewString, _TEXT + // Allocate one dimensional, zero based array (SZARRAY). // r0 == EEType // r1 == element count - LEAF_ENTRY RhpNewArray, _TEXT -#ifdef _DEBUG - bl C_FUNC(NYI_Assert) -#endif - LEAF_END RhpNewArray, _TEXT +LEAF_ENTRY RhpNewArray, _TEXT + PROLOG_PUSH "{r4-r6,lr}" + + // Compute overall allocation size (align(base size + (element size * elements), 4)). + // if the element count is <= 0x10000, no overflow is possible because the component + // size is <= 0xffff (it's an unsigned 16-bit value) and thus the product is <= 0xffff0000 + // and the base size for the worst case (32 dimensional MdArray) is less than 0xffff. + ldrh r2, [r0, #OFFSETOF__EEType__m_usComponentSize] + cmp r1, #0x10000 + bhi LOCAL_LABEL(ArraySizeBig) + umull r2, r3, r2, r1 + ldr r3, [r0, #OFFSETOF__EEType__m_uBaseSize] + adds r2, r3 + adds r2, #3 +LOCAL_LABEL(ArrayAlignSize): + bic r2, r2, #3 + + mov r4, r0 // Save EEType + mov r5, r1 // Save element count + mov r6, r2 // Save array size + // r0 = GetThread() + INLINE_GETTHREAD + // r4 == EEType + // r5 == element count + // r6 == array size + // r0 == Thread* + + // Load potential new object address into r12. + ldr r12, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Determine whether the end of the object would lie outside of the current allocation context. If so, + // we abandon the attempt to allocate the object directly and fall back to the slow helper. + adds r6, r12 + bcs LOCAL_LABEL(RhpNewArray_RarePath) // if we get a carry here, the array is too large to fit below 4 GB + + ldr r12, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp r6, r12 + bhi LOCAL_LABEL(RhpNewArray_RarePath) + + // Reload new object address into r12. + ldr r12, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Update the alloc pointer to account for the allocation. + str r6, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Set the new object's EEType pointer and element count. + str r4, [r12, #OFFSETOF__Object__m_pEEType] + str r5, [r12, #OFFSETOF__Array__m_Length] + + // Return the object allocated in r0. + mov r0, r12 + EPILOG_POP "{r4-r6,pc}" + +LOCAL_LABEL(ArraySizeBig): + // if the element count is negative, it's an overflow error + cmp r1, #0 + blt LOCAL_LABEL(ArraySizeOverflow) + + // now we know the element count is in the signed int range [0..0x7fffffff] + // overflow in computing the total size of the array size gives an out of memory exception, + // NOT an overflow exception + // we already have the component size in r2 + umull r2, r3, r2, r1 + cbnz r3, LOCAL_LABEL(ArrayOutOfMemoryFinal) + ldr r3, [r0, #OFFSETOF__EEType__m_uBaseSize] + adds r2, r3 + bcs LOCAL_LABEL(ArrayOutOfMemoryFinal) + adds r2, #3 + bcs LOCAL_LABEL(ArrayOutOfMemoryFinal) + b LOCAL_LABEL(ArrayAlignSize) + +LOCAL_LABEL(ArrayOutOfMemoryFinal): + + // EEType is in r0 already + mov r1, #0 // Indicate that we should throw OOM. + EPILOG_POP "{r4-r6,lr}" + b C_FUNC(RhExceptionHandling_FailedAllocation) + +LOCAL_LABEL(ArraySizeOverflow): + // We get here if the size of the final array object can't be represented as an unsigned + // 32-bit value. We're going to tail-call to a managed helper that will throw + // an overflow exception that the caller of this allocator understands. + + // EEType is in r0 already + mov r1, #1 // Indicate that we should throw OverflowException + EPILOG_POP "{r4-r6,lr}" + b C_FUNC(RhExceptionHandling_FailedAllocation) + +LOCAL_LABEL(RhpNewArray_RarePath): + mov r3, r0 + mov r0, r4 + mov r1, r5 + mov r2, r6 + // r0 == EEType + // r1 == element count + // r2 == array size + Thread::m_alloc_context::alloc_ptr + // r3 == Thread + EPILOG_POP "{r4-r6,lr}" + b C_FUNC(RhpNewArrayRare) + +LEAF_END RhpNewArray, _TEXT + // Allocate one dimensional, zero based array (SZARRAY) using the slow path that calls a runtime helper. // r0 == EEType // r1 == element count // r2 == array size + Thread::m_alloc_context::alloc_ptr // r3 == Thread - NESTED_ENTRY RhpNewArrayRare, _TEXT, NoHandler -#ifdef _DEBUG - bl C_FUNC(NYI_Assert) -#endif - NESTED_END RhpNewArrayRare, _TEXT +NESTED_ENTRY RhpNewArrayRare, _TEXT, NoHandler + + // Recover array size by subtracting the alloc_ptr from r2. + ldr r12, [r3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + sub r2, r12 + + PUSH_COOP_PINVOKE_FRAME r3 + + // Preserve the EEType in r5 and element count in r6. + mov r5, r0 + mov r6, r1 + + mov r7, r2 // Save array size in r7 + + mov r1, #0 // uFlags + + // void* RhpGcAlloc(EEType *pEEType, UInt32 uFlags, UIntNative cbSize, void * pTransitionFrame) + blx C_FUNC(RhpGcAlloc) + + // Test for failure (NULL return). + cbz r0, LOCAL_LABEL(ArrayOutOfMemory) + + // Success, set the array's type and element count in the new object. + str r5, [r0, #OFFSETOF__Object__m_pEEType] + str r6, [r0, #OFFSETOF__Array__m_Length] + + // If the object is bigger than RH_LARGE_OBJECT_SIZE, we must publish it to the BGC + movw r2, #(RH_LARGE_OBJECT_SIZE & 0xFFFF) + movt r2, #(RH_LARGE_OBJECT_SIZE >> 16) + cmp r7, r2 + blo LOCAL_LABEL(NewArray_SkipPublish) + + // r0: already contains object + mov r1, r7 // r1: object size + bl C_FUNC(RhpPublishObject) + // r0: function returned the passed-in object + +LOCAL_LABEL(NewArray_SkipPublish): + + POP_COOP_PINVOKE_FRAME + bx lr + +LOCAL_LABEL(ArrayOutOfMemory): + + mov r0, r5 // EEType + mov r1, #0 // Indicate that we should throw OOM. + + POP_COOP_PINVOKE_FRAME + + b C_FUNC(RhExceptionHandling_FailedAllocation) + +NESTED_END RhpNewArrayRare, _TEXT // Allocate simple object (not finalizable, array or value type) on an 8 byte boundary. // r0 == EEType - LEAF_ENTRY RhpNewFastAlign8, _TEXT -#ifdef _DEBUG - bl C_FUNC(NYI_Assert) -#endif - LEAF_END RhpNewFastAlign8, _TEXT +LEAF_ENTRY RhpNewFastAlign8, _TEXT + PROLOG_PUSH "{r4,lr}" + + mov r4, r0 // save EEType + + // r0 = GetThread() + INLINE_GETTHREAD + + // Fetch object size into r2. + ldr r2, [r4, #OFFSETOF__EEType__m_uBaseSize] + + // r4: EEType pointer + // r0: Thread pointer + // r2: base size + + // Load potential new object address into r3. Cache this result in r12 as well for the common case + // where the allocation succeeds (r3 will be overwritten in the following bounds check). + ldr r3, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + mov r12, r3 + + // Check whether the current allocation context is already aligned for us. + tst r3, #0x7 + bne LOCAL_LABEL(Alloc8Failed) + + // Determine whether the end of the object would lie outside of the current allocation context. If so, + // we abandon the attempt to allocate the object directly and fall back to the slow helper. + add r2, r3 + ldr r3, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp r2, r3 + bhi LOCAL_LABEL(Alloc8Failed) + + // Update the alloc pointer to account for the allocation. + str r2, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Set the new object's EEType pointer. + str r4, [r12, #OFFSETOF__Object__m_pEEType] + + // Return the object allocated in r0. + mov r0, r12 + + EPILOG_POP "{r4,pc}" + +LOCAL_LABEL(Alloc8Failed): + // Fast allocation failed. Call slow helper with flags set to indicate an 8-byte alignment and no + // finalization. + mov r0, r4 // restore EEType + mov r1, #GC_ALLOC_ALIGN8 + EPILOG_POP "{r4,lr}" + b C_FUNC(RhpNewObject) + +LEAF_END RhpNewFastAlign8, _TEXT // Allocate a finalizable object (by definition not an array or value type) on an 8 byte boundary. // r0 == EEType - LEAF_ENTRY RhpNewFinalizableAlign8, _TEXT -#ifdef _DEBUG - bl C_FUNC(NYI_Assert) -#endif - LEAF_END RhpNewFinalizableAlign8, _TEXT +LEAF_ENTRY RhpNewFinalizableAlign8, _TEXT + mov r1, #(GC_ALLOC_FINALIZE | GC_ALLOC_ALIGN8) + b C_FUNC(RhpNewObject) +LEAF_END RhpNewFinalizableAlign8, _TEXT // Allocate a value type object (i.e. box it) on an 8 byte boundary + 4 (so that the value type payload // itself is 8 byte aligned). // r0 == EEType - LEAF_ENTRY RhpNewFastMisalign, _TEXT -#ifdef _DEBUG - bl C_FUNC(NYI_Assert) -#endif - LEAF_END RhpNewFastMisalign, _TEXT +LEAF_ENTRY RhpNewFastMisalign, _TEXT + PROLOG_PUSH "{r4,lr}" + + mov r4, r0 // save EEType + + // r0 = GetThread() + INLINE_GETTHREAD + + // Fetch object size into r2. + ldr r2, [r4, #OFFSETOF__EEType__m_uBaseSize] + + // r4: EEType pointer + // r0: Thread pointer + // r2: base size + + // Load potential new object address into r3. Cache this result in r12 as well for the common case + // where the allocation succeeds (r3 will be overwritten in the following bounds check). + ldr r3, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + mov r12, r3 + + // Check whether the current allocation context is already aligned for us (for boxing that means the + // address % 8 == 4, so the value type payload following the EEType* is actually 8-byte aligned). + tst r3, #0x7 + beq LOCAL_LABEL(BoxAlloc8Failed) + + // Determine whether the end of the object would lie outside of the current allocation context. If so, + // we abandon the attempt to allocate the object directly and fall back to the slow helper. + add r2, r3 + ldr r3, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp r2, r3 + bhi LOCAL_LABEL(BoxAlloc8Failed) + + // Update the alloc pointer to account for the allocation. + str r2, [r0, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + // Set the new object's EEType pointer. + str r4, [r12, #OFFSETOF__Object__m_pEEType] + + // Return the object allocated in r0. + mov r0, r12 + + EPILOG_POP "{r4,pc}" + +LOCAL_LABEL(BoxAlloc8Failed): + // Fast allocation failed. Call slow helper with flags set to indicate an 8+4 byte alignment and no + // finalization. + mov r0, r4 // restore EEType + mov r1, #(GC_ALLOC_ALIGN8 | GC_ALLOC_ALIGN8_BIAS) + EPILOG_POP "{r4,pc}" + b C_FUNC(RhpNewObject) + +LEAF_END RhpNewFastMisalign, _TEXT // Allocate an array on an 8 byte boundary. // r0 == EEType // r1 == element count - NESTED_ENTRY RhpNewArrayAlign8, _TEXT, NoHandler -#ifdef _DEBUG - bl C_FUNC(NYI_Assert) -#endif - NESTED_END RhpNewArrayAlign8, _TEXT +NESTED_ENTRY RhpNewArrayAlign8, _TEXT, NoHandler + + PUSH_COOP_PINVOKE_FRAME r3 + + // Compute overall allocation size (base size + align((element size * elements), 4)). + ldrh r2, [r0, #OFFSETOF__EEType__m_usComponentSize] + umull r2, r4, r2, r1 + cbnz r4, LOCAL_LABEL(Array8SizeOverflow) + adds r2, #3 + bcs LOCAL_LABEL(Array8SizeOverflow) + bic r2, r2, #3 + ldr r4, [r0, #OFFSETOF__EEType__m_uBaseSize] + adds r2, r4 + bcs LOCAL_LABEL(Array8SizeOverflow) + + // Preserve the EEType in r5 and element count in r6. + mov r5, r0 + mov r6, r1 + mov r7, r2 // Save array size in r7 + + mov r1, #GC_ALLOC_ALIGN8 // uFlags + + // void* RhpGcAlloc(EEType *pEEType, UInt32 uFlags, UIntNative cbSize, void * pTransitionFrame) + blx C_FUNC(RhpGcAlloc) + + // Test for failure (NULL return). + cbz r0, LOCAL_LABEL(Array8OutOfMemory) + + // Success, set the array's type and element count in the new object. + str r5, [r0, #OFFSETOF__Object__m_pEEType] + str r6, [r0, #OFFSETOF__Array__m_Length] + + // If the object is bigger than RH_LARGE_OBJECT_SIZE, we must publish it to the BGC + movw r2, #(RH_LARGE_OBJECT_SIZE & 0xFFFF) + movt r2, #(RH_LARGE_OBJECT_SIZE >> 16) + cmp r7, r2 + blo LOCAL_LABEL(NewArray8_SkipPublish) + + // r0: already contains object + mov r1, r7 // r1: object size + bl C_FUNC(RhpPublishObject) + // r0: function returned the passed-in object +LOCAL_LABEL(NewArray8_SkipPublish): + + POP_COOP_PINVOKE_FRAME + + bx lr + +LOCAL_LABEL(Array8SizeOverflow): + // We get here if the size of the final array object can't be represented as an unsigned + // 32-bit value. We're going to tail-call to a managed helper that will throw + // an OOM or overflow exception that the caller of this allocator understands. + + // if the element count is non-negative, it's an OOM error + cmp r1, #0 + bge LOCAL_LABEL(Array8OutOfMemory1) + + // r0 holds EEType pointer already + mov r1, #1 // Indicate that we should throw OverflowException + + POP_COOP_PINVOKE_FRAME + b C_FUNC(RhExceptionHandling_FailedAllocation) + +LOCAL_LABEL(Array8OutOfMemory): + // This is the OOM failure path. We're going to tail-call to a managed helper that will throw + // an out of memory exception that the caller of this allocator understands. + + mov r0, r5 // EEType pointer + +LOCAL_LABEL(Array8OutOfMemory1): + + mov r1, #0 // Indicate that we should throw OOM. + + POP_COOP_PINVOKE_FRAME + b C_FUNC(RhExceptionHandling_FailedAllocation) + +NESTED_END RhpNewArrayAlign8, _TEXT diff --git a/external/corert/src/Native/Runtime/arm/AllocFast.asm b/external/corert/src/Native/Runtime/arm/AllocFast.asm index 72977301bf..8ed77d3f42 100644 --- a/external/corert/src/Native/Runtime/arm/AllocFast.asm +++ b/external/corert/src/Native/Runtime/arm/AllocFast.asm @@ -113,6 +113,71 @@ NewOutOfMemory NESTED_END RhpNewObject +;; Allocate a string. +;; r0 == EEType +;; r1 == element/character count + LEAF_ENTRY RhNewString + + ; Make sure computing the overall allocation size won't overflow + MOV32 r2, ((0xFFFFFFFF - STRING_BASE_SIZE - 3) / STRING_COMPONENT_SIZE) + cmp r1, r2 + bhs StringSizeOverflow + + ; Compute overall allocation size (align(base size + (element size * elements), 4)). + mov r2, #(STRING_BASE_SIZE + 3) +#if STRING_COMPONENT_SIZE == 2 + add r2, r2, r1, lsl #1 ; r2 += characters * 2 +#else + NotImplementedComponentSize +#endif + bic r2, r2, #3 + + ; r0 == EEType + ; r1 == element count + ; r2 == string size + + INLINE_GETTHREAD r3, r12 + + ;; Load potential new object address into r12. + ldr r12, [r3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Determine whether the end of the object would lie outside of the current allocation context. If so, + ;; we abandon the attempt to allocate the object directly and fall back to the slow helper. + adds r2, r12 + bcs RhpNewArrayRare ; if we get a carry here, the array is too large to fit below 4 GB + ldr r12, [r3, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp r2, r12 + bhi RhpNewArrayRare + + ;; Reload new object address into r12. + ldr r12, [r3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Update the alloc pointer to account for the allocation. + str r2, [r3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Set the new object's EEType pointer and element count. + str r0, [r12, #OFFSETOF__Object__m_pEEType] + str r1, [r12, #OFFSETOF__String__m_Length] + + ;; Return the object allocated in r0. + mov r0, r12 + + bx lr + +StringSizeOverflow + ; We get here if the size of the final string object can't be represented as an unsigned + ; 32-bit value. We're going to tail-call to a managed helper that will throw + ; an OOM exception that the caller of this allocator understands. + + ; r0 holds EEType pointer already + mov r1, #0 ; Indicate that we should throw OOM. + b RhExceptionHandling_FailedAllocation + + LEAF_END RhNewString + + INLINE_GETTHREAD_CONSTANT_POOL + + ;; Allocate one dimensional, zero based array (SZARRAY). ;; r0 == EEType ;; r1 == element count @@ -121,7 +186,7 @@ NewOutOfMemory ; Compute overall allocation size (align(base size + (element size * elements), 4)). ; if the element count is <= 0x10000, no overflow is possible because the component ; size is <= 0xffff (it's an unsigned 16-bit value) and thus the product is <= 0xffff0000 - ; and the base size is only 12 bytes. + ; and the base size for the worst case (32 dimensional MdArray) is less than 0xffff. ldrh r2, [r0, #OFFSETOF__EEType__m_usComponentSize] cmp r1, #0x10000 bhi ArraySizeBig diff --git a/external/corert/src/Native/Runtime/arm/AsmMacros.h b/external/corert/src/Native/Runtime/arm/AsmMacros.h index a5735d7b07..1e64ddc76b 100644 --- a/external/corert/src/Native/Runtime/arm/AsmMacros.h +++ b/external/corert/src/Native/Runtime/arm/AsmMacros.h @@ -38,7 +38,15 @@ PTFF_SAVE_R0 equ 0x00000200 ;; R0 is saved if it contains a GC ref a PTFF_SAVE_ALL_SCRATCH equ 0x00003e00 ;; R0-R3,LR (R12 is trashed by the helpers anyway, but LR is relevant for loop hijacking PTFF_R0_IS_GCREF equ 0x00004000 ;; iff PTFF_SAVE_R0: set -> r0 is Object, clear -> r0 is scalar PTFF_R0_IS_BYREF equ 0x00008000 ;; iff PTFF_SAVE_R0: set -> r0 is ByRef, clear -> r0 is Object or scalar +PTFF_THREAD_ABORT equ 0x00010000 ;; indicates that ThreadAbortException should be thrown when returning from the transition +;; These must match the TrapThreadsFlags enum +TrapThreadsFlags_None equ 0 +TrapThreadsFlags_AbortInProgress equ 1 +TrapThreadsFlags_TrapThreads equ 2 + +;; This must match HwExceptionCode.STATUS_REDHAWK_THREAD_ABORT +STATUS_REDHAWK_THREAD_ABORT equ 0x43 ;; ;; Rename fields of nested structs @@ -189,6 +197,25 @@ $Name MEND + MACRO + EXPORT_POINTER_TO_ADDRESS $Name + +1 + + AREA |.rdata|, ALIGN=4, DATA, READONLY + +$Name + + DCD %BT1 + + EXPORT $Name + + TEXTAREA + + ROUT + + MEND + ;----------------------------------------------------------------------------- ; Macro used to check (in debug builds only) whether the stack is 64-bit aligned (a requirement before calling ; out into C++/OS code). Invoke this directly after your prolog (if the stack frame size is fixed) or directly @@ -209,6 +236,13 @@ $Name #endif MEND +;; Loads a 32bit constant into destination register + MACRO + MOV32 $destReg, $constant + + movw $destReg, #(($constant) & 0xFFFF) + movt $destReg, #(($constant) >> 16) + MEND ;; ;; CONSTANTS -- SYMBOLS @@ -252,4 +286,4 @@ $Name #ifdef FEATURE_GC_STRESS EXTERN $REDHAWKGCINTERFACE__STRESSGC EXTERN $THREAD__HIJACKFORGCSTRESS -#endif ;; FEATURE_GC_STRESS \ No newline at end of file +#endif ;; FEATURE_GC_STRESS diff --git a/external/corert/src/Native/Runtime/arm/AsmOffsetsCpu.h b/external/corert/src/Native/Runtime/arm/AsmOffsetsCpu.h index baf8da3854..e088014a81 100644 --- a/external/corert/src/Native/Runtime/arm/AsmOffsetsCpu.h +++ b/external/corert/src/Native/Runtime/arm/AsmOffsetsCpu.h @@ -21,7 +21,7 @@ PLAT_ASM_OFFSET(130, ExInfo, m_notifyDebuggerSP) PLAT_ASM_OFFSET(4, PInvokeTransitionFrame, m_RIP) PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_FramePointer) PLAT_ASM_OFFSET(0c, PInvokeTransitionFrame, m_pThread) -PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_dwFlags) +PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_Flags) PLAT_ASM_OFFSET(14, PInvokeTransitionFrame, m_PreservedRegs) PLAT_ASM_SIZEOF(118, StackFrameIterator) diff --git a/external/corert/src/Native/Runtime/arm/CallDescrWorker.S b/external/corert/src/Native/Runtime/arm/CallDescrWorker.S index de818954b6..dd45242976 100644 --- a/external/corert/src/Native/Runtime/arm/CallDescrWorker.S +++ b/external/corert/src/Native/Runtime/arm/CallDescrWorker.S @@ -2,6 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +.syntax unified +.thumb + +#include // generated by the build from AsmOffsets.cpp #include // TODO: Implement Arm support + +NESTED_ENTRY RhCallDescrWorker, _TEXT, NoHandler +LOCAL_LABEL(ReturnFromCallDescrThunk): + + EXPORT_POINTER_TO_ADDRESS PointerToReturnFromCallDescrThunk + + // UNIXTODO: Implement this function + EMIT_BREAKPOINT +NESTED_END RhCallDescrWorker, _TEXT + diff --git a/external/corert/src/Native/Runtime/arm/CallDescrWorker.asm b/external/corert/src/Native/Runtime/arm/CallDescrWorker.asm index 656d0e64e6..fea7d7f23b 100644 --- a/external/corert/src/Native/Runtime/arm/CallDescrWorker.asm +++ b/external/corert/src/Native/Runtime/arm/CallDescrWorker.asm @@ -59,8 +59,7 @@ LNoFloatingPoint ldr r4, [r5,#OFFSETOF__CallDescrData__pTarget] blx r4 -ReturnFromCallDescrThunk - rout + EXPORT_POINTER_TO_ADDRESS PointerToReturnFromCallDescrThunk ;; Symbol used to identify thunk call to managed function so the special ;; case unwinder can unwind through this function. Sadly we cannot directly @@ -127,12 +126,4 @@ LReturnDone NESTED_END RhCallDescrWorker - AREA |.rdata|, ALIGN=4, DATA, READONLY - -PointerToReturnFromCallDescrThunk - - DCD ReturnFromCallDescrThunk - - EXPORT PointerToReturnFromCallDescrThunk - END diff --git a/external/corert/src/Native/Runtime/arm/CallingConventionConverterHelpers.S b/external/corert/src/Native/Runtime/arm/CallingConventionConverterHelpers.S index de818954b6..ddb2cf33d4 100644 --- a/external/corert/src/Native/Runtime/arm/CallingConventionConverterHelpers.S +++ b/external/corert/src/Native/Runtime/arm/CallingConventionConverterHelpers.S @@ -2,6 +2,65 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +// TODO: Implement Arm support +.syntax unified +.thumb + +#include // generated by the build from AsmOffsets.cpp #include -// TODO: Implement Arm support +// +// void CallingConventionConverter_ReturnVoidReturnThunk() +// +LEAF_ENTRY CallingConventionConverter_ReturnVoidReturnThunk, _TEXT + bx lr +LEAF_END CallingConventionConverter_ReturnVoidReturnThunk, _TEXT + +// +// int CallingConventionConverter_ReturnIntegerReturnThunk(int) +// +LEAF_ENTRY CallingConventionConverter_ReturnIntegerReturnThunk, _TEXT + // UNIXTODO: Implement this function + EMIT_BREAKPOINT +LEAF_END CallingConventionConverter_ReturnIntegerReturnThunk, _TEXT + +// +// __jmpstub__CallingConventionConverter_CommonCallingStub +// +// struct CallingConventionConverter_CommonCallingStub_PointerData +// { +// void *ManagedCallConverterThunk; +// void *UniversalThunk; +// } +// +// struct CommonCallingStubInputData +// { +// ULONG_PTR CallingConventionId; +// CallingConventionConverter_CommonCallingStub_PointerData *commonData; // Only the ManagedCallConverterThunk field is used +// // However, it is specified just like other platforms, so the behavior of the common +// // calling stub is easier to debug +// } +// +// sp-4 - Points at CommonCallingStubInputData +// +// +LEAF_ENTRY __jmpstub__CallingConventionConverter_CommonCallingStub + // UNIXTODO: Implement this function + EMIT_BREAKPOINT +LEAF_END __jmpstub__CallingConventionConverter_CommonCallingStub + +// +// void CallingConventionConverter_GetStubs(IntPtr *returnVoidStub, IntPtr *returnIntegerStub, IntPtr *commonCallingStub) +// +LEAF_ENTRY CallingConventionConverter_GetStubs, _TEXT + // UNIXTODO: Implement this function + EMIT_BREAKPOINT +LEAF_END CallingConventionConverter_GetStubs, _TEXT + +// +// void CallingConventionConverter_SpecifyCommonStubData(CallingConventionConverter_CommonCallingStub_PointerData *commonData); +// +LEAF_ENTRY CallingConventionConverter_SpecifyCommonStubData + // UNIXTODO: Implement this function + EMIT_BREAKPOINT +LEAF_END CallingConventionConverter_SpecifyCommonStubData diff --git a/external/corert/src/Native/Runtime/arm/ExceptionHandling.S b/external/corert/src/Native/Runtime/arm/ExceptionHandling.S index de818954b6..2732282e8e 100644 --- a/external/corert/src/Native/Runtime/arm/ExceptionHandling.S +++ b/external/corert/src/Native/Runtime/arm/ExceptionHandling.S @@ -2,6 +2,491 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +.syntax unified +.thumb + +#include // generated by the build from AsmOffsets.cpp #include -// TODO: Implement Arm support +#define STACKSIZEOF_ExInfo ((SIZEOF__ExInfo + 7)&(~7)) + +#define rsp_offsetof_ExInfo 0 +#define rsp_offsetof_Context STACKSIZEOF_ExInfo + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// RhpThrowHwEx +// +// INPUT: R0: exception code of fault +// R1: faulting RIP +// +// OUTPUT: +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +NESTED_ENTRY RhpThrowHwEx, _TEXT, NoHandler + + mov r2, r0 // save exception code into r2 + mov r0, sp // get SP of fault site + + mov lr, r1 // set IP of fault site + + // Setup a PAL_LIMITED_CONTEXT on the stack { + PROLOG_VPUSH {d8-d15} + PROLOG_PUSH "{r0,lr}" // push {sp, pc} of fault site + PROLOG_PUSH "{r0,r4-r11,lr}" + // } end PAL_LIMITED_CONTEXT + + PROLOG_STACK_ALLOC STACKSIZEOF_ExInfo + + // r0: SP of fault site + // r1: IP of fault site + // r2: exception code of fault + // lr: IP of fault site (as a 'return address') + mov r4, r2 // save exception code of fault + + // r0 = GetThread() + INLINE_GETTHREAD + + // r1 <- ExInfo* + add r1, sp, #rsp_offsetof_ExInfo + mov r3, #0 + str r3, [r1, #OFFSETOF__ExInfo__m_exception] // pExInfo->m_exception = null + mov r3, #1 + strb r3, [r1, #OFFSETOF__ExInfo__m_passNumber] // pExInfo->m_passNumber = 1 + mov r3, #0xFFFFFFFF + str r3, [r1, #OFFSETOF__ExInfo__m_idxCurClause] // pExInfo->m_idxCurClause = MaxTryRegionIdx + mov r3, #2 + strb r3, [r1, #OFFSETOF__ExInfo__m_kind] // pExInfo->m_kind = ExKind.HardwareFault + + // link the ExInfo into the thread's ExInfo chain + ldr r3, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] + str r3, [r1, #OFFSETOF__ExInfo__m_pPrevExInfo] // pExInfo->m_pPrevExInfo = m_pExInfoStackHead + str r1, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] // m_pExInfoStackHead = pExInfo + + mov r0, r4 // restore the exception code + // r0 contains the exception code + // r1 contains the address of the ExInfo + bl C_FUNC(RhThrowHwEx) + + EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowHwEx2 + + // no return + EMIT_BREAKPOINT + +NESTED_END RhpThrowHwEx + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// RhpThrowEx +// +// INPUT: R0: exception object +// +// OUTPUT: +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +NESTED_ENTRY RhpThrowEx, _TEXT, NoHandler + + // Setup a PAL_LIMITED_CONTEXT on the stack { + PROLOG_VPUSH {d8-d15} + PROLOG_PUSH "{r0,lr}" // Reserve space for SP and store LR + PROLOG_PUSH "{r0,r4-r11,lr}" + // } end PAL_LIMITED_CONTEXT + + PROLOG_STACK_ALLOC STACKSIZEOF_ExInfo + + mov r4, r0 // Save exception object + // r0 = GetThread() + INLINE_GETTHREAD + + add r2, sp, #(rsp_offsetof_Context + SIZEOF__PAL_LIMITED_CONTEXT + 0x8) // r2 <- addr of return address + + // There is runtime C# code that can tail call to RhpThrowEx using a binder intrinsic. So the return + // address could have been hijacked when we were in that C# code and we must remove the hijack and + // reflect the correct return address in our exception context record. The other throw helpers don't + // need this because they cannot be tail-called from C#. + // NOTE: we cannot use INLINE_THREAD_UNHIJACK because it will write into the stack at the location + // where the tail-calling thread had saved LR, which may not match where we have saved LR. + + ldr r1, [r0, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + cbz r1, LOCAL_LABEL(NotHijacked) + + ldr r3, [r0, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + + // r4: exception object + // r1: hijacked return address + // r0: pThread + // r3: hijacked return address location + + add r12, sp, #(STACKSIZEOF_ExInfo + SIZEOF__PAL_LIMITED_CONTEXT) // re-compute SP at callsite + cmp r3, r12 // if (m_ppvHijackedReturnAddressLocation < SP at callsite) + blo LOCAL_LABEL(TailCallWasHijacked) + + // normal case where a valid return address location is hijacked + str r1, [r3] + b LOCAL_LABEL(ClearThreadState) + +LOCAL_LABEL(TailCallWasHijacked): + + // Abnormal case where the return address location is now invalid because we ended up here via a tail + // call. In this case, our hijacked return address should be the correct caller of this method. + // + + // stick the previous return address in LR as well as in the right spots in our PAL_LIMITED_CONTEXT. + mov lr, r1 + str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__LR)] + str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__IP)] + +LOCAL_LABEL(ClearThreadState): + + // clear the Thread's hijack state + mov r3, #0 + str r3, [r0, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + str r3, [r0, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + +LOCAL_LABEL(NotHiJacked): + + add r1, sp, #rsp_offsetof_ExInfo // r1 <- ExInfo* + mov r3, #0 + str r3, [r1, #OFFSETOF__ExInfo__m_exception] // init the exception object to null + mov r3, #1 + strb r3, [r1, #OFFSETOF__ExInfo__m_passNumber] // init to the first pass + strb r3, [r1, #OFFSETOF__ExInfo__m_kind] + mov r3, #0xFFFFFFFF + str r3, [r1, #OFFSETOF__ExInfo__m_idxCurClause] // ExKind.Throw + + // link the ExInfo into the thread's ExInfo chain + ldr r3, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] + str r3, [r1, #OFFSETOF__ExInfo__m_pPrevExInfo] // pExInfo->m_pPrevExInfo = m_pExInfoStackHead + str r1, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] // m_pExInfoStackHead = pExInfo + + // set the exception context field on the ExInfo + add r3, sp, #rsp_offsetof_Context // r3 <- PAL_LIMITED_CONTEXT* + str r3, [r1, #OFFSETOF__ExInfo__m_pExContext] // init ExInfo.m_pExContext + + mov r0, r4 // Restore exception object + // r0 contains the exception object + // r1 contains the address of the new ExInfo + bl C_FUNC(RhThrowEx) + + EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowEx2 + + // no return + EMIT_BREAKPOINT + +NESTED_END RhpThrowEx, _TEXT + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// void FASTCALL RhpRethrow() +// +// SUMMARY: Similar to RhpThrowEx, except that it passes along the currently active ExInfo +// +// INPUT: +// +// OUTPUT: +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +NESTED_ENTRY RhpRethrow, _TEXT, NoHandler + + // Setup a PAL_LIMITED_CONTEXT on the stack { + PROLOG_VPUSH {d8-d15} + PROLOG_PUSH "{r0,lr}" // Reserve space for SP and store LR + PROLOG_PUSH "{r0,r4-r11,lr}" + // } end PAL_LIMITED_CONTEXT + + PROLOG_STACK_ALLOC STACKSIZEOF_ExInfo + + // r0 = GetThread(); + INLINE_GETTHREAD + + // r1 <- ExInfo* + add r1, sp, #rsp_offsetof_ExInfo + + mov r3, #0 + str r3, [r1, #OFFSETOF__ExInfo__m_exception] // init the exception object to null + mov r3, #0xFFFFFFFF + str r3, [r1, #OFFSETOF__ExInfo__m_idxCurClause] + + // link the ExInfo into the thread's ExInfo chain + ldr r3, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] // r3 <- currently active ExInfo + str r3, [r1, #OFFSETOF__ExInfo__m_pPrevExInfo] // pExInfo->m_pPrevExInfo = m_pExInfoStackHead + str r1, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] // m_pExInfoStackHead = pExInfo + + // set the exception context field on the ExInfo + add r2, sp, #rsp_offsetof_Context // r2 <- PAL_LIMITED_CONTEXT* + str r2, [r1, #OFFSETOF__ExInfo__m_pExContext] // init ExInfo.m_pExContext + + mov r0, r3 + // r0 contains the currently active ExInfo + // r1 contains the address of the new ExInfo + blx C_FUNC(RhRethrow) + + EXPORT_POINTER_TO_ADDRESS PointerToRhpRethrow2 + + // no return + EMIT_BREAKPOINT + +NESTED_END RhpRethrow, _TEXT + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// void* FASTCALL RhpCallCatchFunclet(RtuObjectRef exceptionObj, +// void* pHandlerIP, +// REGDISPLAY* pRegDisplay, +// ExInfo* pExInfo) +// +// INPUT: R0: exception object +// R1: handler funclet address +// R2: REGDISPLAY* +// R3: ExInfo* +// +// OUTPUT: +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +NESTED_ENTRY RhpCallCatchFunclet, _TEXT, NoHandler + + PROLOG_PUSH "{r0,r2-r11,lr}" // r0, r2 & r3 are saved so we have the exception object, + // REGDISPLAY and ExInfo later + PROLOG_VPUSH {d8-d15} + +#define rsp_offset_r2 (8 * 8) + 4 +#define rsp_offset_r3 (8 * 8) + 8 + + mov r4, r0 // Save exception object + mov r5, r1 // Save handler funclet address + mov r6, r2 // Save REGDISPLAY* + + // Clear the DoNotTriggerGc state before calling out to our managed catch funclet, + // trashes r0-r2. + // r0 = GetThread() + INLINE_GETTHREAD + +LOCAL_LABEL(ClearRetry_Catch): + ldrex r1, [r0, #OFFSETOF__Thread__m_ThreadStateFlags] + bics r1, #TSF_DoNotTriggerGc + strex r2, r1, [r0, #OFFSETOF__Thread__m_ThreadStateFlags] + cbz r2, LOCAL_LABEL(ClearSuccess_Catch) + b LOCAL_LABEL(ClearRetry_Catch) +LOCAL_LABEL(ClearSuccess_Catch): + + mov r1, r4 // Reload exception object + mov r3, r5 // Reload handler funclet address + mov r2, r6 // Reload REGDISPLAY pointer + + // + // set preserved regs to the values expected by the funclet + // + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR4] + ldr r4, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR5] + ldr r5, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR6] + ldr r6, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR7] + ldr r7, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR8] + ldr r8, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR9] + ldr r9, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR10] + ldr r10, [r12] + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR11] + ldr r11, [r12] + + // + // load vfp preserved regs + // + add r12, r2, #OFFSETOF__REGDISPLAY__D + vldm r12!, {d8-d15} + + ldr r0, [r2, #OFFSETOF__REGDISPLAY__SP] // r0 <- establisher frame + // r1 <- exception object + blx r3 // call handler funclet + + mov r4, r0 // Save the result + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallCatchFunclet2 + + INLINE_GETTHREAD // r0 <- Thread* + // We must unhijack the thread at this point because the section of stack where the + // hijack is applied may go dead. If it does, then the next time we try to unhijack + // the thread, it will corrupt the stack. + INLINE_THREAD_UNHIJACK r0, r3, r12 // Thread in r0, trashes r3 and r1 + ldr r2, [sp, #rsp_offset_r2] // r2 <- REGDISPLAY* + ldr r3, [sp, #rsp_offset_r3] // r3 <- current ExInfo* + ldr r2, [r2, #OFFSETOF__REGDISPLAY__SP] // r2 <- resume SP value + +LOCAL_LABEL(PopExInfoLoop): + ldr r3, [r3, #OFFSETOF__ExInfo__m_pPrevExInfo] // r3 <- next ExInfo + cbz r3, LOCAL_LABEL(DonePopping) // if (pExInfo == null) { we're done } + cmp r3, r2 + blt LOCAL_LABEL(PopExInfoLoop) // if (pExInfo < resume SP} { keep going } +LOCAL_LABEL(DonePopping): + + str r3, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] // store the new head on the Thread + + // reset RSP and jump to the continuation address + mov sp, r2 + bx r4 + +NESTED_END RhpCallCatchFunclet, _TEXT + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// void FASTCALL RhpCallFinallyFunclet(void* pHandlerIP, REGDISPLAY* pRegDisplay) +// +// INPUT: R0: handler funclet address +// R1: REGDISPLAY* +// +// OUTPUT: +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +NESTED_ENTRY RhpCallFinallyFunclet, _TEXT, NoHandler + + PROLOG_PUSH "{r1,r4-r11,lr}" // r1 is saved so we have the REGDISPLAY later + PROLOG_VPUSH {d8-d15} +#define rsp_offset_r1 8 * 8 + + // + // We want to suppress hijacking between invocations of subsequent finallys. We do + // this because we cannot tolerate a GC after one finally has run (and possibly + // side-effected the GC state of the method) and then been popped off the stack, + // leaving behind no trace of its effect. + // + // So we clear the state before and set it after invocation of the handler. + // + + mov r4, r0 // Save handler funclet address + mov r5, r1 // Save REGDISPLAY* + // + // clear the DoNotTriggerGc flag, trashes r0-r2 + // + INLINE_GETTHREAD // r0 <- Thread* + +LOCAL_LABEL(ClearRetry): + ldrex r1, [r0, #OFFSETOF__Thread__m_ThreadStateFlags] + bics r1, #TSF_DoNotTriggerGc + strex r2, r1, [r0, #OFFSETOF__Thread__m_ThreadStateFlags] + cbz r2, LOCAL_LABEL(ClearSuccess) + b LOCAL_LABEL(ClearRetry) +LOCAL_LABEL(ClearSuccess): + + mov r2, r4 // reload handler funclet address + mov r1, r5 // reload REGDISPLAY pointer + + // + // set preserved regs to the values expected by the funclet + // + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR4] + ldr r4, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR5] + ldr r5, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR6] + ldr r6, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR7] + ldr r7, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR8] + ldr r8, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR9] + ldr r9, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR10] + ldr r10, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR11] + ldr r11, [r12] + + // + // load vfp preserved regs + // + add r12, r1, #OFFSETOF__REGDISPLAY__D + vldm r12!, {d8-d15} + + ldr r0, [r1, #OFFSETOF__REGDISPLAY__SP] // r0 <- establisher frame + blx r2 // handler funclet address + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFinallyFunclet2 + + ldr r1, [sp, #rsp_offset_r1] // reload REGDISPLAY pointer + + // + // save new values of preserved regs into REGDISPLAY + // + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR4] + str r4, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR5] + str r5, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR6] + str r6, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR7] + str r7, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR8] + str r8, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR9] + str r9, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR10] + str r10, [r12] + ldr r12, [r1, #OFFSETOF__REGDISPLAY__pR11] + str r11, [r12] + + // + // store vfp preserved regs + // + add r12, r1, #OFFSETOF__REGDISPLAY__D + vstm r12!, {d8-d15} + + // + // set the DoNotTriggerGc flag, trashes r0-r2 + // + INLINE_GETTHREAD // r0 <- Thread* +LOCAL_LABEL(SetRetry): + ldrex r1, [r0, #OFFSETOF__Thread__m_ThreadStateFlags] + orrs r1, #TSF_DoNotTriggerGc + strex r2, r1, [r0, #OFFSETOF__Thread__m_ThreadStateFlags] + cbz r2, LOCAL_LABEL(SetSuccess) + b LOCAL_LABEL(SetRetry) +LOCAL_LABEL(SetSuccess): + + EPILOG_VPOP {d8-d15} + EPILOG_POP "{r1,r4-r11,pc}" + +NESTED_END RhpCallFinallyFunclet, _TEXT + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// void* FASTCALL RhpCallFilterFunclet(RtuObjectRef exceptionObj, void* pFilterIP, REGDISPLAY* pRegDisplay) +// +// INPUT: R0: exception object +// R1: filter funclet address +// R2: REGDISPLAY* +// +// OUTPUT: +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +NESTED_ENTRY RhpCallFilterFunclet, _TEXT, NoHandler + + PROLOG_PUSH "{r4-r11,lr}" + PROLOG_VPUSH {d8-d15} + + ldr r12, [r2, #OFFSETOF__REGDISPLAY__pR7] + ldr r7, [r12] + + mov r12, r1 // r12 <- handler funclet address + mov r1, r0 // r1 <- exception object + ldr r0, [r2, #OFFSETOF__REGDISPLAY__SP] // r0 <- establisher frame + + // + // call the funclet + // r0 = establisher frame + // r1 = exception object + blx r12 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFilterFunclet2 + + // R0 contains the result of the filter execution + + EPILOG_VPOP {d8-d15} + EPILOG_POP "{r4-r11,pc}" + +NESTED_END RhpCallFilterFunclet, _TEXT diff --git a/external/corert/src/Native/Runtime/arm/ExceptionHandling.asm b/external/corert/src/Native/Runtime/arm/ExceptionHandling.asm index 1cfc89f5ba..bf21df089f 100644 --- a/external/corert/src/Native/Runtime/arm/ExceptionHandling.asm +++ b/external/corert/src/Native/Runtime/arm/ExceptionHandling.asm @@ -70,14 +70,14 @@ ;; r0: exception code ;; r1: ExInfo* bl RhThrowHwEx - LABELED_RETURN_ADDRESS RhpThrowHwEx2 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowHwEx2 ;; no return __debugbreak NESTED_END RhpThrowHwEx - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; RhpThrowEx @@ -172,14 +172,14 @@ NotHijacked ;; r0: exception object ;; r1: ExInfo* bl RhThrowEx - LABELED_RETURN_ADDRESS RhpThrowEx2 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowEx2 ;; no return __debugbreak NESTED_END RhpThrowEx - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; void FASTCALL RhpRethrow() @@ -229,7 +229,8 @@ NotHijacked ;; r0 contains the currently active ExInfo ;; r1 contains the address of the new ExInfo bl RhRethrow - LABELED_RETURN_ADDRESS RhpRethrow2 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpRethrow2 ;; no return __debugbreak @@ -251,10 +252,11 @@ NotHijacked ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; NESTED_ENTRY RhpCallCatchFunclet - PROLOG_PUSH {r1-r11,lr} ;; r2 & r3 are saved so we have the REGDISPLAY and ExInfo later, r1 is - ;; alignment padding, and we save the preserved regs + PROLOG_PUSH {r0,r2-r11,lr} ;; r0, r2 & r3 are saved so we have the exception object, + ;; REGDISPLAY and ExInfo later PROLOG_VPUSH {d8-d15} +#define rsp_offset_is_not_handling_thread_abort (8 * 8) + 0 #define rsp_offset_r2 (8 * 8) + 4 #define rsp_offset_r3 (8 * 8) + 8 @@ -262,6 +264,11 @@ NotHijacked ;; clear the DoNotTriggerGc flag, trashes r4-r6 ;; INLINE_GETTHREAD r5, r6 ;; r5 <- Thread*, r6 <- trashed + + ldr r4, [r5, #OFFSETOF__Thread__m_threadAbortException] + sub r4, r0 + str r4, [sp, #rsp_offset_is_not_handling_thread_abort] ;; Non-zero if the exception is not ThreadAbortException + ClearRetry_Catch ldrex r4, [r5, #OFFSETOF__Thread__m_ThreadStateFlags] bic r4, #TSF_DoNotTriggerGc @@ -323,7 +330,8 @@ ClearSuccess_Catch ;; ;; r0 still contains the exception object blx r1 - LABELED_RETURN_ADDRESS RhpCallCatchFunclet2 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallCatchFunclet2 ;; r0 contains resume IP @@ -349,13 +357,29 @@ PopExInfoLoop DonePopping str r3, [r1, #OFFSETOF__Thread__m_pExInfoStackHead] ;; store the new head on the Thread + ldr r3, =RhpTrapThreads + ldr r3, [r3] + tst r3, #TrapThreadsFlags_AbortInProgress + beq NoAbort + + ldr r3, [sp, #rsp_offset_is_not_handling_thread_abort] + cmp r3, #0 + bne NoAbort + + ;; It was the ThreadAbortException, so rethrow it + ;; reset SP + mov r1, r0 ;; r1 <- continuation address as exception PC + mov r0, #STATUS_REDHAWK_THREAD_ABORT + mov sp, r2 + b RhpThrowHwEx + +NoAbort ;; reset SP and jump to continuation address mov sp, r2 bx r0 NESTED_END RhpCallCatchFunclet - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; void FASTCALL RhpCallFinallyFunclet(void* pHandlerIP, REGDISPLAY* pRegDisplay) @@ -446,7 +470,8 @@ ClearSuccess ;; call the funclet ;; blx r0 - LABELED_RETURN_ADDRESS RhpCallFinallyFunclet2 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFinallyFunclet2 ldr r1, [sp, #rsp_offset_r1] ;; reload REGDISPLAY pointer @@ -495,7 +520,6 @@ SetSuccess INLINE_GETTHREAD_CONSTANT_POOL - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; void* FASTCALL RhpCallFilterFunclet(RtuObjectRef exceptionObj, void* pFilterIP, REGDISPLAY* pRegDisplay) @@ -520,13 +544,12 @@ SetSuccess ;; ;; r0 still contains the exception object blx r1 - LABELED_RETURN_ADDRESS RhpCallFilterFunclet2 + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFilterFunclet2 EPILOG_VPOP {d8-d15} EPILOG_POP {r4-r11,pc} NESTED_END RhpCallFilterFunclet - end diff --git a/external/corert/src/Native/Runtime/arm/GcProbe.asm b/external/corert/src/Native/Runtime/arm/GcProbe.asm index 4bb8bee8fb..d4bd72e139 100644 --- a/external/corert/src/Native/Runtime/arm/GcProbe.asm +++ b/external/corert/src/Native/Runtime/arm/GcProbe.asm @@ -7,6 +7,7 @@ TEXTAREA SETALIAS GetLoopIndirCells, ?GetLoopIndirCells@ModuleHeader@@QAAPAEXZ + ;; ARM64TODO: do same fix here as on Arm64? SETALIAS g_fGcStressStarted, ?g_GCShadow@@3PAEA SETALIAS g_pTheRuntimeInstance, ?g_pTheRuntimeInstance@@3PAVRuntimeInstance@@A SETALIAS RuntimeInstance__ShouldHijackLoopForGcStress, ?ShouldHijackLoopForGcStress@RuntimeInstance@@QAA_NI@Z @@ -28,7 +29,7 @@ m_ChainPointer field 4 ; r11 - OS frame chain used for quick stackwalks m_RIP field 4 ; lr m_FramePointer field 4 ; r7 m_pThread field 4 -m_dwFlags field 4 ; bitmask of saved registers +m_Flags field 4 ; bitmask of saved registers m_PreservedRegs field (4 * 6) ; r4-r6,r8-r10 m_CallersSP field 4 ; sp at routine entry m_SavedR0 field 4 ; r0 @@ -87,14 +88,14 @@ PROBE_FRAME_SIZE field 0 ;; ;; $threadReg : register containing the Thread* (this will be preserved) ;; $trashReg : register that can be trashed by this macro - ;; $BITMASK : value to initialize m_dwFlags field with (register or #constant) + ;; $BITMASK : value to initialize m_Flags field with (register or #constant) ;; $frameSize : total size of the method's stack frame (including probe frame size) MACRO INIT_PROBE_FRAME $threadReg, $trashReg, $BITMASK, $frameSize str $threadReg, [sp, #m_pThread] ; Thread * mov $trashReg, $BITMASK ; Bitmask of preserved registers - str $trashReg, [sp, #m_dwFlags] + str $trashReg, [sp, #m_Flags] add $trashReg, sp, #$frameSize str $trashReg, [sp, #m_CallersSP] MEND @@ -106,7 +107,7 @@ PROBE_FRAME_SIZE field 0 ;; the current thread will be calculated inline into r2 ($trashReg must not equal r2 in ;; this case) ;; $trashReg : register that can be trashed by this macro - ;; $BITMASK : value to initialize m_dwFlags field with (register or #constant) + ;; $BITMASK : value to initialize m_Flags field with (register or #constant) MACRO PROLOG_PROBE_FRAME $threadReg, $trashReg, $BITMASK @@ -198,7 +199,7 @@ __PPF_ThreadReg SETS "r2" ;; All other registers trashed ;; - EXTERN RhpWaitForGC + EXTERN RhpWaitForGCNoAbort MACRO WaitForGCCompletion @@ -208,7 +209,7 @@ __PPF_ThreadReg SETS "r2" bne %ft0 ldr r2, [r4, #OFFSETOF__Thread__m_pHackPInvokeTunnel] - bl RhpWaitForGC + bl RhpWaitForGCNoAbort 0 MEND @@ -318,10 +319,13 @@ __PPF_ThreadReg SETS "r2" NESTED_END RhpGcStressProbe #endif ;; FEATURE_GC_STRESS + EXTERN RhpThrowHwEx + LEAF_ENTRY RhpGcProbe ldr r3, =RhpTrapThreads ldr r3, [r3] - cbnz r3, %0 + tst r3, #TrapThreadsFlags_TrapThreads + bne %0 bx lr 0 b RhpGcProbeRare @@ -333,7 +337,18 @@ __PPF_ThreadReg SETS "r2" mov r4, r2 WaitForGCCompletion + ldr r2, [sp, #OFFSETOF__PInvokeTransitionFrame__m_Flags] + tst r2, #PTFF_THREAD_ABORT + bne %1 + EPILOG_PROBE_FRAME + +1 + FREE_PROBE_FRAME + EPILOG_NOP mov r0, #STATUS_REDHAWK_THREAD_ABORT + EPILOG_NOP mov r1, lr ;; return address as exception PC + EPILOG_BRANCH RhpThrowHwEx + NESTED_END RhpGcProbe LEAF_ENTRY RhpGcPoll @@ -342,7 +357,8 @@ __PPF_ThreadReg SETS "r2" push {r0} ldr r0, =RhpTrapThreads ldr r0, [r0] - cbnz r0, %0 + tst r0, #TrapThreadsFlags_TrapThreads + bne %0 pop {r0} bx lr 0 @@ -360,7 +376,7 @@ __PPF_ThreadReg SETS "r2" WaitForGCCompletion EPILOG_PROBE_FRAME - NESTED_END RhpGcPoll + NESTED_END RhpGcPollRare LEAF_ENTRY RhpGcPollStress ; @@ -550,7 +566,8 @@ DREG_SZ equ (SIZEOF__PAL_LIMITED_CONTEXT - (OFFSETOF__PAL_LIMITED_CONTEXT__L ldr r1, =RhpTrapThreads ldr r1, [r1] - cbnz r1, %0 + tst r1, #TrapThreadsFlags_TrapThreads + bne %0 bl RhDebugBreak 0 @@ -694,18 +711,27 @@ NoGcStress #endif ;; FEATURE_GC_STRESS mov r2, sp ; sp is address of PInvokeTransitionFrame - bl RhpWaitForGC + bl RhpWaitForGCNoAbort DoneWaitingForGc + ldr r12, [sp, #OFFSETOF__PInvokeTransitionFrame__m_Flags] + tst r12, #PTFF_THREAD_ABORT + bne Abort ; restore condition codes ldr r12, [sp, #m_SavedAPSR] msr apsr_nzcvqg, r12 - FREE_PROBE_FRAME EPILOG_VPOP {d16-d31} EPILOG_VPOP {d4-d15} - - EPILOG_POP {r12,pc} ; recover the hijack target and jump to it + EPILOG_POP {r12,pc} ; recover the hijack target and jump to it +Abort + FREE_PROBE_FRAME + EPILOG_VPOP {d16-d31} + EPILOG_VPOP {d4-d15} + EPILOG_POP r12 + EPILOG_NOP mov r0, #STATUS_REDHAWK_THREAD_ABORT + EPILOG_POP r1 ;hijack target address as exception PC + EPILOG_BRANCH RhpThrowHwEx LEAF_END RhpLoopHijack diff --git a/external/corert/src/Native/Runtime/arm/Interlocked.S b/external/corert/src/Native/Runtime/arm/Interlocked.S index 1d9a46ce15..5698e8a78a 100644 --- a/external/corert/src/Native/Runtime/arm/Interlocked.S +++ b/external/corert/src/Native/Runtime/arm/Interlocked.S @@ -2,7 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -// TODO: Implement InterLocked routines +.syntax unified +.thumb + +#include // generated by the build from AsmOffsets.cpp #include // WARNING: Code in EHHelpers.cpp makes assumptions about this helper, in particular: @@ -12,18 +15,44 @@ // r1 = value // r2 = comparand LEAF_ENTRY RhpLockCmpXchg32, _TEXT + dmb ALTERNATE_ENTRY RhpLockCmpXchg32AVLocation - bx lr +LOCAL_LABEL(CmpXchg32Retry): + ldrex r3, [r0] + cmp r2, r3 + bne LOCAL_LABEL(CmpXchg32Exit) + strex r12, r1, [r0] + cmp r12, #0 + bne LOCAL_LABEL(CmpXchg32Retry) +LOCAL_LABEL(CmpXchg32Exit): + mov r0, r3 + dmb + bx lr LEAF_END RhpLockCmpXchg32, _TEXT - // WARNING: Code in EHHelpers.cpp makes assumptions about this helper, in particular: // - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpLockCmpXchg64AVLocation // - Function "UnwindWriteBarrierToCaller" assumes the stack contains just the pushed return address -// r0 = destination address -// r1 = value -// r2 = comparand +// r0 = destination address +// {r2,r3} = value +// sp[0+8] = comparand LEAF_ENTRY RhpLockCmpXchg64, _TEXT ALTERNATE_ENTRY RhpLockCmpXchg64AVLocation - bx lr + ldr r12, [r0] // dummy read for null check + PROLOG_PUSH "{r4-r6,lr}" + dmb + ldrd r4, r5, [sp,#0x10] +LOCAL_LABEL(CmpXchg64Retry): + ldrexd r6, r1, [r0] + cmp r6, r4 + bne LOCAL_LABEL(CmpXchg64Exit) + cmp r1, r5 + bne LOCAL_LABEL(CmpXchg64Exit) + strexd r12, r2, r3, [r0] + cmp r12, #0 + bne LOCAL_LABEL(CmpXchg64Retry) +LOCAL_LABEL(CmpXchg64Exit): + mov r0, r6 + dmb + EPILOG_POP "{r4-r6,pc}" LEAF_END RhpLockCmpXchg64, _TEXT diff --git a/external/corert/src/Native/Runtime/arm/InteropThunksHelpers.S b/external/corert/src/Native/Runtime/arm/InteropThunksHelpers.S index 15749ba55a..9021e7c818 100644 --- a/external/corert/src/Native/Runtime/arm/InteropThunksHelpers.S +++ b/external/corert/src/Native/Runtime/arm/InteropThunksHelpers.S @@ -2,36 +2,60 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#include - .syntax unified .thumb -// TODO: Implement Arm support +#include // generated by the build from AsmOffsets.cpp +#include + +#define POINTER_SIZE 4 // // RhCommonStub // NESTED_ENTRY RhCommonStub, _TEXT, NoHandler -#ifdef _DEBUG - bl C_FUNC(NYI_Assert) -#endif + // Custom calling convention: + // red zone has pointer to the current thunk's data block (data contains 2 pointer values: context + target pointers) + // Copy red zone value into r12 so that the PROLOG_PUSH doesn't destroy it + ldr r12, [sp, #-4] + PROLOG_PUSH "{r0-r4, lr}" + PROLOG_VPUSH {d0-d7} // Capture the floating point argument registers + + mov r4, r12 + + INLINE_GET_TLS_VAR tls_thunkData + + // r0 = base address of TLS data + // r4 = address of context cell in thunk's data + + ldr r12, [r4] + str r12, [r0] + + // Now load the target address and jump to it. + ldr r12, [r4, #POINTER_SIZE] + EPILOG_VPOP {d0-d7} + EPILOG_POP "{r0-r4, lr}" + bx r12 + NESTED_END RhCommonStub, _TEXT // // IntPtr RhGetCommonStubAddress() // LEAF_ENTRY RhGetCommonStubAddress, _TEXT -#ifdef _DEBUG - bl C_FUNC(NYI_Assert) -#endif + ldr r0, =C_FUNC(RhCommonStub) + bx lr LEAF_END RhGetCommonStubAddress, _TEXT // // IntPtr RhGetCurrentThunkContext() // LEAF_ENTRY RhGetCurrentThunkContext, _TEXT -#ifdef _DEBUG - bl C_FUNC(NYI_Assert) -#endif + + PROLOG_PUSH "{r12, lr}" + + INLINE_GET_TLS_VAR tls_thunkData + + ldr r0, [r0] + EPILOG_POP "{r12, pc}" LEAF_END RhGetCurrentThunkContext, _TEXT diff --git a/external/corert/src/Native/Runtime/arm/PInvoke.S b/external/corert/src/Native/Runtime/arm/PInvoke.S index de818954b6..a88f2e4c96 100644 --- a/external/corert/src/Native/Runtime/arm/PInvoke.S +++ b/external/corert/src/Native/Runtime/arm/PInvoke.S @@ -2,6 +2,67 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#include #include -// TODO: Implement Arm support +.syntax unified +.thumb + +// +// RhpPInvoke +// +// IN: R0: address of pinvoke frame +// +// This helper assumes that its callsite is as good to start the stackwalk as the actual PInvoke callsite. +// The codegenerator must treat the callsite of this helper as GC triggering and generate the GC info for it. +// Also, the codegenerator must ensure that there are no live GC references in callee saved registers. +// + +NESTED_ENTRY RhpPInvoke, _TEXT, NoHandler + str lr, [r0, #OFFSETOF__PInvokeTransitionFrame__m_RIP] + str r7, [r0, #OFFSETOF__PInvokeTransitionFrame__m_FramePointer] + str sp, [r0, #OFFSETOF__PInvokeTransitionFrame__m_PreservedRegs] + mov r3, #PTFF_SAVE_SP + str r3, [r0, #OFFSETOF__PInvokeTransitionFrame__m_Flags] + + PROLOG_PUSH "{r5,lr}" + + mov r5, r0 + // get TLS global variable address + // r0 = GetThread() + INLINE_GETTHREAD + str r0, [r5, #OFFSETOF__PInvokeTransitionFrame__m_pThread] + str r5, [r0, #OFFSETOF__Thread__m_pTransitionFrame] + + ldr r3, =C_FUNC(RhpTrapThreads) + ldr r3, [r3] + cbnz r3, LOCAL_LABEL(InvokeRareTrapThread) // TrapThreadsFlags_None = 0 + + EPILOG_POP "{r5,pc}" + +LOCAL_LABEL(InvokeRareTrapThread): + EPILOG_POP "{r5,lr}" + b C_FUNC(RhpWaitForSuspend2) +NESTED_END RhpPInvoke, _TEXT + + +// +// RhpPInvokeReturn +// +// IN: R0: address of pinvoke frame +// +LEAF_ENTRY RhpPInvokeReturn, _TEXT + ldr r3, [r0, #OFFSETOF__PInvokeTransitionFrame__m_pThread] + + mov r2, #0 + str r2, [r3, #OFFSETOF__Thread__m_pTransitionFrame] + + ldr r3, =C_FUNC(RhpTrapThreads) + ldr r3, [r3] + cbnz r3, LOCAL_LABEL(ReturnRareTrapThread) // TrapThreadsFlags_None = 0 + + bx lr +LOCAL_LABEL(ReturnRareTrapThread): + // passing transition frame pointer in r0 + b C_FUNC(RhpWaitForGC2) +LEAF_END RhpPInvokeReturn, _TEXT diff --git a/external/corert/src/Native/Runtime/arm/PInvoke.asm b/external/corert/src/Native/Runtime/arm/PInvoke.asm index 7ac6f60523..48da17f688 100644 --- a/external/corert/src/Native/Runtime/arm/PInvoke.asm +++ b/external/corert/src/Native/Runtime/arm/PInvoke.asm @@ -31,6 +31,38 @@ NESTED_END RhpWaitForSuspend +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpWaitForGCNoAbort +;; +;; +;; INPUT: r2: transition frame +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpWaitForGCNoAbort + + PROLOG_PUSH {r0-r6,lr} ; Even number of registers to maintain 8-byte stack alignment + PROLOG_VPUSH {d0-d3} ; Save float return value registers as well + + ldr r5, [r2, #OFFSETOF__PInvokeTransitionFrame__m_pThread] + + ldr r0, [r5, #OFFSETOF__Thread__m_ThreadStateFlags] + tst r0, #TSF_DoNotTriggerGc + bne Done + + mov r0, r2 ; passing transition frame in r0 + bl RhpWaitForGC2 + +Done + EPILOG_VPOP {d0-d3} + EPILOG_POP {r0-r6,pc} + + NESTED_END RhpWaitForGCNoAbort + + EXTERN RhpThrowHwEx + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; RhpWaitForGC @@ -42,26 +74,26 @@ ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; NESTED_ENTRY RhpWaitForGC + PROLOG_PUSH {r0,lr} - PROLOG_PUSH {r0,r1,r4-r6,lr} ; Even number of registers to maintain 8-byte stack alignment - PROLOG_VPUSH {d0-d3} ; Save float return value registers as well - - ldr r5, [r2, #OFFSETOF__PInvokeTransitionFrame__m_pThread] - - ldr r0, [r5, #OFFSETOF__Thread__m_ThreadStateFlags] - tst r0, #TSF_DoNotTriggerGc - bne Done - - mov r0, r2 ; passing transition frame in r0 - bl RhpWaitForGC2 - -Done - EPILOG_VPOP {d0-d3} - EPILOG_POP {r0,r1,r4-r6,pc} - + ldr r0, =RhpTrapThreads + ldr r0, [r0] + tst r0, #TrapThreadsFlags_TrapThreads + beq NoWait + bl RhpWaitForGCNoAbort +NoWait + tst r0, #TrapThreadsFlags_AbortInProgress + beq NoAbort + ldr r0, [r2, #OFFSETOF__PInvokeTransitionFrame__m_Flags] + tst r0, #PTFF_THREAD_ABORT + beq NoAbort + EPILOG_POP {r0,r1} ; hijack target address as exception PC + EPILOG_NOP mov r0, #STATUS_REDHAWK_THREAD_ABORT + EPILOG_BRANCH RhpThrowHwEx +NoAbort + EPILOG_POP {r0,pc} NESTED_END RhpWaitForGC - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; RhpReversePInvoke @@ -106,7 +138,8 @@ ThreadAttached ldr r6, =RhpTrapThreads ldr r6, [r6] - cbnz r6, TrapThread + tst r6, #TrapThreadsFlags_TrapThreads + bne TrapThread AllDone EPILOG_POP {r5-r7,lr} @@ -192,7 +225,8 @@ BadTransition ldr r3, =RhpTrapThreads ldr r3, [r3] - cbnz r3, RareTrapThread + tst r3, #TrapThreadsFlags_TrapThreads + bne RareTrapThread bx lr diff --git a/external/corert/src/Native/Runtime/arm/StubDispatch.S b/external/corert/src/Native/Runtime/arm/StubDispatch.S index de818954b6..26ca007a7e 100644 --- a/external/corert/src/Native/Runtime/arm/StubDispatch.S +++ b/external/corert/src/Native/Runtime/arm/StubDispatch.S @@ -2,6 +2,185 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +.syntax unified +.thumb + +#include // generated by the build from AsmOffsets.cpp #include +#ifdef FEATURE_CACHED_INTERFACE_DISPATCH + // TODO: Implement Arm support +#ifdef _DEBUG +.rodata +AssertMsg: .asciz "__FILE__:%s: %s is not implemented\n" +FileName: .asciz "StubDispatch.S" +RhpCastableObjectDispatch_CommonStubName: .asciz "RhpCastableObjectDispatch_CommonStub" +RhpTailCallTLSDispatchCellName: .asciz "RhpTailCallTLSDispatchCell" +RhpCastableObjectDispatchHelper_TailCalledName: .asciz "RhpCastableObjectDispatchHelper_TailCalled" +RhpCastableObjectDispatchHelperName: .asciz "RhpCastableObjectDispatchHelper" +RhpVTableOffsetDispatchName: .asciz "RhpVTableOffsetDispatch" +.text +.macro GEN_ASSERT_FUNC func + GEN_ASSERT AssertMsg, FileName, \func +.endm +#endif + + +LEAF_ENTRY RhpCastableObjectDispatch_CommonStub, _TEXT +#ifdef _DEBUG + GEN_ASSERT_FUNC RhpCastableObjectDispatch_CommonStubName +#else + // UNIXTODO: Implement this function + EMIT_BREAKPOINT +#endif +LEAF_END RhpCastableObjectDispatch_CommonStub, _TEXT + +LEAF_ENTRY RhpTailCallTLSDispatchCell, _TEXT +#ifdef _DEBUG + GEN_ASSERT_FUNC RhpTailCallTLSDispatchCellName +#else + // UNIXTODO: Implement this function + EMIT_BREAKPOINT +#endif +LEAF_END RhpTailCallTLSDispatchCell, _TEXT + +LEAF_ENTRY RhpCastableObjectDispatchHelper_TailCalled, _TEXT +#ifdef _DEBUG + GEN_ASSERT_FUNC RhpCastableObjectDispatchHelper_TailCalledName +#else + // UNIXTODO: Implement this function + EMIT_BREAKPOINT +#endif +LEAF_END RhpCastableObjectDispatchHelper_TailCalled, _TEXT + +LEAF_ENTRY RhpCastableObjectDispatchHelper, _TEXT +#ifdef _DEBUG + GEN_ASSERT_FUNC RhpCastableObjectDispatchHelperName +#else + // UNIXTODO: Implement this function + EMIT_BREAKPOINT +#endif +LEAF_END RhpCastableObjectDispatchHelper, _TEXT + +// Macro that generates a stub consuming a cache with the given number of entries. +.macro DEFINE_INTERFACE_DISPATCH_STUB entries + +LEAF_ENTRY RhpInterfaceDispatch\entries, _TEXT + // r12 currently contains the indirection cell address. But we need more scratch registers and + // we may A/V on a null this. Both of these suggest we need a real prolog and epilog. + PROLOG_PUSH {r1-r2} + + // r12 currently holds the indirection cell address. We need to get the cache structure instead. + ldr r2, [r12, #OFFSETOF__InterfaceDispatchCell__m_pCache] + + // Load the EEType from the object instance in r0. + ldr r1, [r0] + + CurrentOffset = OFFSETOF__InterfaceDispatchCache__m_rgEntries + // For each entry in the cache, see if its EEType type matches the EEType in r1. + // If so, call the second cache entry. If not, skip the InterfaceDispatchCacheEntry. + // R1 : Instance EEType* + // R2: Cache data structure + // R12 : Trashed. On succesful check, set to the target address to jump to. + .rept \entries + ldr r12, [r2, #CurrentOffset] + cmp r1, r12 + bne 0f + ldr r12, [r2, #(CurrentOffset + 4)] + b LOCAL_LABEL(99_\entries) + 0: + CurrentOffset = CurrentOffset + 8 + .endr + + // Point r12 to the indirection cell using the back pointer in the cache block + ldr r12, [r2, #OFFSETOF__InterfaceDispatchCache__m_pCell] + + EPILOG_POP {r1-r2} + b C_FUNC(RhpInterfaceDispatchSlow) + + // Common epilog for cache hits. Have to out of line it here due to limitation on the number of + // epilogs imposed by the unwind code macros. +LOCAL_LABEL(99_\entries): + // R2 contains address of the cache block. We store it in the red zone in case the target we jump + // to needs it. Currently the RhpCastableObjectDispatchHelper is the only such target. + // R12 contains the target address to jump to + EPILOG_POP {r1} + // The red zone is only 8 bytes long, so we have to store r2 into it between the pops. + str r2, [sp, #-4] + EPILOG_POP {r2} + EPILOG_BRANCH_REG r12 + +LEAF_END RhpInterfaceDispatch\entries, _TEXT + +.endm // DEFINE_INTERFACE_DISPATCH_STUB + +// Define all the stub routines we currently need. +// +// The mrt100dbi requires these be exported to identify mrt100 code that dispatches back into managed. +// If you change or add any new dispatch stubs, please also change slr.def and dbi\process.cpp CordbProcess::GetExportStepInfo +// +DEFINE_INTERFACE_DISPATCH_STUB 1 +DEFINE_INTERFACE_DISPATCH_STUB 2 +DEFINE_INTERFACE_DISPATCH_STUB 4 +DEFINE_INTERFACE_DISPATCH_STUB 8 +DEFINE_INTERFACE_DISPATCH_STUB 16 +DEFINE_INTERFACE_DISPATCH_STUB 32 +DEFINE_INTERFACE_DISPATCH_STUB 64 + +// Stub dispatch routine for dispatch to a vtable slot +LEAF_ENTRY RhpVTableOffsetDispatch, _TEXT + // On input we have the indirection cell data structure in r12. But we need more scratch registers and + // we may A/V on a null this. Both of these suggest we need a real prolog and epilog. + PROLOG_PUSH {r1} + + // r12 currently holds the indirection cell address. We need to update it to point to the vtable + // offset instead. + ldr r12, [r12, #OFFSETOF__InterfaceDispatchCell__m_pCache] + + // Load the EEType from the object instance in r0. + ldr r1, [r0] + + // add the vtable offset to the EEType pointer + add r12, r1, r12 + + // Load the target address of the vtable into r12 + ldr r12, [r12] + + EPILOG_POP {r1} + EPILOG_BRANCH_REG r12 +LEAF_END RhpVTableOffsetDispatch, _TEXT + +// Initial dispatch on an interface when we don't have a cache yet. +LEAF_ENTRY RhpInitialInterfaceDispatch, _TEXT + // The stub that jumped here pushed r12, which contains the interface dispatch cell + // we need to pop it here + pop { r12 } + + // Just tail call to the cache miss helper. + b C_FUNC(RhpInterfaceDispatchSlow) +LEAF_END RhpInitialInterfaceDispatch, _TEXT + +// No as alternate entry due to missed thumb bit in this case +// See https://github.com/dotnet/coreclr/issues/12953 +LEAF_ENTRY RhpInitialDynamicInterfaceDispatch, _TEXT + // Just tail call to the cache miss helper. + b C_FUNC(RhpInterfaceDispatchSlow) +LEAF_END RhpInitialDynamicInterfaceDispatch, _TEXT + +// Cache miss case, call the runtime to resolve the target and update the cache. +// Use universal transition helper to allow an exception to flow out of resolution +LEAF_ENTRY RhpInterfaceDispatchSlow, _TEXT + // r12 has the interface dispatch cell address in it. + // The calling convention of the universal thunk is that the parameter + // for the universal thunk target is to be placed in sp-8 + // and the universal thunk target address is to be placed in sp-4 + str r12, [sp, #-8] + ldr r12, =C_FUNC(RhpCidResolve) + str r12, [sp, #-4] + + // jump to universal transition thunk + b C_FUNC(RhpUniversalTransition_DebugStepTailCall) +LEAF_END RhpInterfaceDispatchSlow, _TEXT + +#endif // FEATURE_CACHED_INTERFACE_DISPATCH diff --git a/external/corert/src/Native/Runtime/arm/ThunkPoolThunks.asm b/external/corert/src/Native/Runtime/arm/ThunkPoolThunks.asm index dfff2e3704..fea3f16c20 100644 --- a/external/corert/src/Native/Runtime/arm/ThunkPoolThunks.asm +++ b/external/corert/src/Native/Runtime/arm/ThunkPoolThunks.asm @@ -1,8 +1,6 @@ -;; ==++== -;; -;; Copyright (c) Microsoft Corporation. All rights reserved. -;; -;; ==--== +;; 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. #include "kxarm.h" diff --git a/external/corert/src/Native/Runtime/arm/UniversalTransition.S b/external/corert/src/Native/Runtime/arm/UniversalTransition.S index de818954b6..0ec4ece13d 100644 --- a/external/corert/src/Native/Runtime/arm/UniversalTransition.S +++ b/external/corert/src/Native/Runtime/arm/UniversalTransition.S @@ -2,6 +2,157 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +.syntax unified +.thumb + +#include // generated by the build from AsmOffsets.cpp #include -// TODO: Implement Arm support +#ifdef FEATURE_DYNAMIC_CODE + +#ifdef _DEBUG +#define TRASH_SAVED_ARGUMENT_REGISTERS +#endif + +#define COUNT_ARG_REGISTERS (4) +#define INTEGER_REGISTER_SIZE (4) +#define ARGUMENT_REGISTERS_SIZE (COUNT_ARG_REGISTERS * INTEGER_REGISTER_SIZE) + +// Largest return block is 4 doubles +#define RETURN_BLOCK_SIZE (32) + +#define COUNT_FLOAT_ARG_REGISTERS (8) +#define FLOAT_REGISTER_SIZE (8) +#define FLOAT_ARG_REGISTERS_SIZE (COUNT_FLOAT_ARG_REGISTERS * FLOAT_REGISTER_SIZE) + +#define PUSHED_LR_SIZE (4) +#define PUSHED_R11_SIZE (4) + +// +// From CallerSP to ChildSP, the stack frame is composed of the following adjacent regions: +// +// ARGUMENT_REGISTERS_SIZE +// RETURN_BLOCK_SIZE +// FLOAT_ARG_REGISTERS_SIZE +// PUSHED_LR +// PUSHED_R11 + + +#define DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK (PUSHED_R11_SIZE + PUSHED_LR_SIZE + FLOAT_ARG_REGISTERS_SIZE) + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// RhpUniversalTransition +// +// At input to this function, r0-3, d0-7 and the stack may contain any number of arguments. +// +// In addition, there are 2 extra arguments passed in the RED ZONE (8 byte negative space +// off of sp). +// sp-4 will contain the managed function that is to be called by this transition function +// sp-8 will contain the pointer sized extra argument to the managed function +// +// When invoking the callee: +// +// r0 shall contain a pointer to the TransitionBlock +// r1 shall contain the value that was in sp-8 at entry to this function +// +// Frame layout is: +// +// {StackPassedArgs} ChildSP+078 CallerSP+000 +// {IntArgRegs (r0-r3) (0x10 bytes)} ChildSP+068 CallerSP-010 +// {ReturnBlock (0x20 bytes)} ChildSP+048 CallerSP-030 +// -- The base address of the Return block is the TransitionBlock pointer, the floating point args are +// in the neg space of the TransitionBlock pointer. Note that the callee has knowledge of the exact +// layout of all pieces of the frame that lie at or above the pushed floating point registers. +// {FpArgRegs (d0-d7) (0x40 bytes)} ChildSP+008 CallerSP-070 +// {PushedLR} ChildSP+004 CallerSP-074 +// {PushedR11} ChildSP+000 CallerSP-078 +// +// NOTE: If the frame layout ever changes, the C++ UniversalTransitionStackFrame structure +// must be updated as well. +// +// NOTE: The callee receives a pointer to the base of the ReturnBlock, and the callee has +// knowledge of the exact layout of all pieces of the frame that lie at or above the pushed +// FpArgRegs. +// +// NOTE: The stack walker guarantees that conservative GC reporting will be applied to +// everything between the base of the ReturnBlock and the top of the StackPassedArgs. +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +.macro UNIVERSAL_TRANSITION FunctionName + +NESTED_ENTRY Rhp\FunctionName, _TEXT, NoHandler + // Save argument registers (including floating point) and the return address. + // NOTE: While we do that, capture the two arguments in the red zone into r12 and r3. + ldr r12, [sp, #-4] // Capture first argument from red zone into r12 + PROLOG_PUSH "{r3}" // Push r3 + ldr r3, [sp, #-4] // Capture second argument from red zone into r3 + PROLOG_PUSH "{r0-r2}" // Push the rest of the registers + PROLOG_STACK_ALLOC RETURN_BLOCK_SIZE // Save space a buffer to be used to hold return buffer data. + PROLOG_VPUSH {d0-d7} // Capture the floating point argument registers + PROLOG_PUSH "{r11,lr}" // Save caller's frame chain pointer and PC + + // Setup the arguments to the transition thunk. + mov r1, r3 + +#ifdef TRASH_SAVED_ARGUMENT_REGISTERS + + // Before calling out, trash all of the argument registers except the ones (r0, r1) that + // hold outgoing arguments. All of these registers have been saved to the transition + // frame, and the code at the call target is required to use only the transition frame + // copies when dispatching this call to the eventual callee. + + ldr r3, =C_FUNC(RhpFpTrashValues) + vldr d0, [r3, #(0 * 8)] + vldr d1, [r3, #(1 * 8)] + vldr d2, [r3, #(2 * 8)] + vldr d3, [r3, #(3 * 8)] + vldr d4, [r3, #(4 * 8)] + vldr d5, [r3, #(5 * 8)] + vldr d6, [r3, #(6 * 8)] + vldr d7, [r3, #(7 * 8)] + + ldr r3, =C_FUNC(RhpIntegerTrashValues) + ldr r2, [r3, #(2 * 4)] + ldr r3, [r3, #(3 * 4)] + +#endif // TRASH_SAVED_ARGUMENT_REGISTERS + + // Make the ReturnFromUniversalTransition alternate entry 4 byte aligned + .balign 4 + add r0, sp, #DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK // First parameter to target function is a pointer to the return block + blx r12 + + EXPORT_POINTER_TO_ADDRESS PointerToReturnFrom\FunctionName + + // We cannot make the label public as that tricks DIA stackwalker into thinking + // it's the beginning of a method. For this reason we export an auxiliary variable + // holding the address instead. + + // Move the result (the target address) to r12 so it doesn't get overridden when we restore the + // argument registers. Additionally make sure the thumb2 bit is set. + orr r12, r0, #1 + + // Restore caller's frame chain pointer and PC. + EPILOG_POP "{r11,lr}" + + // Restore the argument registers. + EPILOG_VPOP {d0-d7} + EPILOG_STACK_FREE RETURN_BLOCK_SIZE // pop return block conservatively reported area + EPILOG_POP "{r0-r3}" + + // Tailcall to the target address. + EPILOG_BRANCH_REG r12 + +NESTED_END Rhp\FunctionName, _TEXT + +.endm + +// To enable proper step-in behavior in the debugger, we need to have two instances +// of the thunk. For the first one, the debugger steps into the call in the function, +// for the other, it steps over it. +UNIVERSAL_TRANSITION UniversalTransition +UNIVERSAL_TRANSITION UniversalTransition_DebugStepTailCall + +#endif // FEATURE_DYNAMIC_CODE diff --git a/external/corert/src/Native/Runtime/arm/UniversalTransition.asm b/external/corert/src/Native/Runtime/arm/UniversalTransition.asm index 6b62403132..fffb3b4ea1 100644 --- a/external/corert/src/Native/Runtime/arm/UniversalTransition.asm +++ b/external/corert/src/Native/Runtime/arm/UniversalTransition.asm @@ -124,9 +124,7 @@ add r0, sp, #DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK ;; First parameter to target function is a pointer to the return block blx r12 -ReturnFrom$FunctionName - - rout + EXPORT_POINTER_TO_ADDRESS PointerToReturnFrom$FunctionName ; We cannot make the label public as that tricks DIA stackwalker into thinking ; it's the beginning of a method. For this reason we export an auxiliary variable @@ -149,14 +147,6 @@ ReturnFrom$FunctionName NESTED_END Rhp$FunctionName - AREA |.rdata|, ALIGN=4, DATA, READONLY - -PointerToReturnFrom$FunctionName - - DCD ReturnFrom$FunctionName - - EXPORT PointerToReturnFrom$FunctionName - MEND ; To enable proper step-in behavior in the debugger, we need to have two instances diff --git a/external/corert/src/Native/Runtime/arm/WriteBarriers.S b/external/corert/src/Native/Runtime/arm/WriteBarriers.S index 316770987a..7b83bdb9c4 100644 --- a/external/corert/src/Native/Runtime/arm/WriteBarriers.S +++ b/external/corert/src/Native/Runtime/arm/WriteBarriers.S @@ -2,18 +2,312 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -// TODO: Implement Unix write barriers +.syntax unified +.thumb + +#include // generated by the build from AsmOffsets.cpp #include -LEAF_ENTRY RhpAssignRef, _TEXT - str r1, [r0] - bx lr -LEAF_END RhpAssignRef, _TEXT +#ifdef WRITE_BARRIER_CHECK -LEAF_ENTRY RhpCheckedAssignRef, _TEXT - str r1, [r0] - bx lr -LEAF_END RhpCheckedAssignRef, _TEXT +.macro UPDATE_GC_SHADOW BASENAME, REFREG, DESTREG + + // If g_GCShadow is 0, don't perform the check. + ldr r12, =C_FUNC(g_GCShadow) + ldr r12, [r12] + cbz r12, LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_Done_\REFREG) + + // Save DESTREG since we're about to modify it (and we need the original value both within the macro and + // once we exit the macro). Note that this is naughty since we're altering the stack pointer outside of + // the prolog inside a method without a frame. But given that this is only debug code and generally we + // shouldn't be walking the stack at this point it seems preferable to recoding the all the barrier + // variants to set up frames. The compiler knows exactly which registers are trashed in the simple write + // barrier case, so we don't have any more scratch registers to play with (and doing so would only make + // things harder if at a later stage we want to allow multiple barrier versions based on the input + // registers). + push \DESTREG + + // Transform DESTREG into the equivalent address in the shadow heap. + ldr r12, =C_FUNC(g_lowest_address) + ldr r12, [r12] + sub \DESTREG, r12 + cmp \DESTREG, #0 + blo LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_PopThenDone_\REFREG) + ldr r12, =C_FUNC(g_GCShadow) + ldr r12, [r12] + add \DESTREG, r12 + ldr r12, =C_FUNC(g_GCShadowEnd) + ldr r12, [r12] + cmp \DESTREG, r12 + jhi LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_PopThenDone_\REFREG) + + // Update the shadow heap. + str \REFREG, [\DESTREG] + + // The following read must be strongly ordered wrt to the write we've just performed in order to + // prevent race conditions. + dmb + + // Now check that the real heap location still contains the value we just wrote into the shadow heap. + mov r12, \DESTREG + ldr \DESTREG, [sp] + str r12, [sp] + ldr r12, [\DESTREG] + cmp r12, \REFREG + bne LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_Invalidate_\REFREG) + + // The original DESTREG value is now restored but the stack has a value (the shadow version of the + // location) pushed. Need to discard this push before we are done. + add sp, #4 + b LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_Done_\REFREG) + +LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_Invalidate_\REFREG): + // Someone went and updated the real heap. We need to invalidate the shadow location since we can't + // guarantee whose shadow update won. + + // Retrieve shadow location from the stack and restore original DESTREG to the stack. This is an + // additional memory barrier we don't require but it's on the rare path and x86 doesn't have an xchg + // variant that doesn't implicitly specify the lock prefix. Note that INVALIDGCVALUE is a 32-bit + // immediate and therefore must be moved into a register before it can be written to the shadow + // location. + mov r12, \DESTREG + ldr \DESTREG, [sp] + str r12, [sp] + push \REFREG + movw \REFREG, #(INVALIDGCVALUE & 0xFFFF) + movt \REFREG, #(INVALIDGCVALUE >> 16) + str \REFREG, [\DESTREG] + pop \REFREG + +LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_PopThenDone_\REFREG): + // Restore original DESTREG value from the stack. + pop \DESTREG + +LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_Done_\REFREG): + +.endm + +#else // WRITE_BARRIER_CHECK + +.macro UPDATE_GC_SHADOW BASENAME, REFREG, DESTREG +.endm + +#endif // WRITE_BARRIER_CHECK + +// There are several different helpers used depending on which register holds the object reference. Since all +// the helpers have identical structure we use a macro to define this structure. Two arguments are taken, the +// name of the register that points to the location to be updated and the name of the register that holds the +// object reference (this should be in upper case as it's used in the definition of the name of the helper). +.macro DEFINE_UNCHECKED_WRITE_BARRIER_CORE BASENAME, REFREG + + // Update the shadow copy of the heap with the same value just written to the same heap. (A no-op unless + // we're in a debug build and write barrier checking has been enabled). + UPDATE_GC_SHADOW \BASENAME, \REFREG, r0 + + // If the reference is to an object that's not in an ephemeral generation we have no need to track it + // (since the object won't be collected or moved by an ephemeral collection). + ldr r12, =C_FUNC(g_ephemeral_low) + ldr r12, [r12] + cmp \REFREG, r12 + blo LOCAL_LABEL(\BASENAME\()_EXIT_\REFREG) + + ldr r12, =C_FUNC(g_ephemeral_high) + ldr r12, [r12] + cmp \REFREG, r12 + bhi LOCAL_LABEL(\BASENAME\()_EXIT_\REFREG) + + // We have a location on the GC heap being updated with a reference to an ephemeral object so we must + // track this write. The location address is translated into an offset in the card table bitmap. We set + // an entire byte in the card table since it's quicker than messing around with bitmasks and we only write + // the byte if it hasn't already been done since writes are expensive and impact scaling. + ldr r12, =C_FUNC(g_card_table) + ldr r12, [r12] + add r0, r12, r0, lsr #LOG2_CLUMP_SIZE + ldrb r12, [r0] + cmp r12, #0FFh + bne LOCAL_LABEL(\BASENAME\()_UpdateCardTable_\REFREG) + +LOCAL_LABEL(\BASENAME\()_NoBarrierRequired_\REFREG): + b LOCAL_LABEL(\BASENAME\()_EXIT_\REFREG) + +// We get here if it's necessary to update the card table. +LOCAL_LABEL(\BASENAME\()_UpdateCardTable_\REFREG): + mov r12, #0FFh + strb r12, [r0] + +LOCAL_LABEL(\BASENAME\()_EXIT_\REFREG): + +.endm + +// There are several different helpers used depending on which register holds the object reference. Since all +// the helpers have identical structure we use a macro to define this structure. One argument is taken, the +// name of the register that will hold the object reference (this should be in upper case as it's used in the +// definition of the name of the helper). +.macro DEFINE_UNCHECKED_WRITE_BARRIER REFREG, EXPORT_REG_NAME + +// Define a helper with a name of the form RhpAssignRefEAX etc. (along with suitable calling standard +// decoration). The location to be updated is in DESTREG. The object reference that will be assigned into that +// location is in one of the other general registers determined by the value of REFREG. + +// WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +// - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at WriteBarrierFunctionAvLOC +// - Function "UnwindWriteBarrierToCaller" assumes the stack contains just the pushed return address +LEAF_ENTRY RhpAssignRef\EXPORT_REG_NAME, _TEXT + +// Export the canonical write barrier under unqualified name as well +.ifc \REFREG, r1 +ALTERNATE_ENTRY RhpAssignRef +.endif + + // Use the GC write barrier as a convenient place to implement the managed memory model for ARM. The + // intent is that writes to the target object ($REFREG) will be visible across all CPUs before the + // write to the destination ($DESTREG). This covers most of the common scenarios where the programmer + // might assume strongly ordered accessess, namely where the preceding writes are used to initialize + // the object and the final write, made by this barrier in the instruction following the DMB, + // publishes that object for other threads/cpus to see. + // + // Note that none of this is relevant for single cpu machines. We may choose to implement a + // uniprocessor specific version of this barrier if uni-proc becomes a significant scenario again. + dmb + + // Write the reference into the location. Note that we rely on the fact that no GC can occur between here + // and the card table update we may perform below. +ALTERNATE_ENTRY "RhpAssignRefAvLocation"\EXPORT_REG_NAME // WriteBarrierFunctionAvLocation +.ifc \REFREG, r1 +ALTERNATE_ENTRY RhpAssignRefAVLocation +.endif + str \REFREG, [r0] + + DEFINE_UNCHECKED_WRITE_BARRIER_CORE RhpAssignRef, \REFREG + + bx lr +LEAF_END RhpAssignRef\EXPORT_REG_NAME, _TEXT +.endm + +// One day we might have write barriers for all the possible argument registers but for now we have +// just one write barrier that assumes the input register is RSI. +DEFINE_UNCHECKED_WRITE_BARRIER r1, r1 + +// +// Define the helpers used to implement the write barrier required when writing an object reference into a +// location residing on the GC heap. Such write barriers allow the GC to optimize which objects in +// non-ephemeral generations need to be scanned for references to ephemeral objects during an ephemeral +// collection. +// + +.macro DEFINE_CHECKED_WRITE_BARRIER_CORE BASENAME, REFREG + + // The location being updated might not even lie in the GC heap (a handle or stack location for instance), + // in which case no write barrier is required. + ldr r12, =C_FUNC(g_lowest_address) + ldr r12, [r12] + cmp r0, r12 + blo LOCAL_LABEL(\BASENAME\()_NoBarrierRequired_\REFREG) + ldr r12, =C_FUNC(g_highest_address) + ldr r12, [r12] + cmp r0, r12 + bhi LOCAL_LABEL(\BASENAME\()_NoBarrierRequired_\REFREG) + + DEFINE_UNCHECKED_WRITE_BARRIER_CORE \BASENAME, \REFREG + +.endm + +// There are several different helpers used depending on which register holds the object reference. Since all +// the helpers have identical structure we use a macro to define this structure. One argument is taken, the +// name of the register that will hold the object reference (this should be in upper case as it's used in the +// definition of the name of the helper). +.macro DEFINE_CHECKED_WRITE_BARRIER REFREG, EXPORT_REG_NAME + +// Define a helper with a name of the form RhpCheckedAssignRefEAX etc. (along with suitable calling standard +// decoration). The location to be updated is always in R0. The object reference that will be assigned into +// that location is in one of the other general registers determined by the value of REFREG. + +// WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +// - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen on the first instruction +// - Function "UnwindWriteBarrierToCaller" assumes the stack contains just the pushed return address +LEAF_ENTRY RhpCheckedAssignRef\EXPORT_REG_NAME, _TEXT + +// Export the canonical write barrier under unqualified name as well +.ifc \REFREG, r1 +ALTERNATE_ENTRY RhpCheckedAssignRef +.endif + + // Use the GC write barrier as a convenient place to implement the managed memory model for ARM. The + // intent is that writes to the target object ($REFREG) will be visible across all CPUs before the + // write to the destination ($DESTREG). This covers most of the common scenarios where the programmer + // might assume strongly ordered accessess, namely where the preceding writes are used to initialize + // the object and the final write, made by this barrier in the instruction following the DMB, + // publishes that object for other threads/cpus to see. + // + // Note that none of this is relevant for single cpu machines. We may choose to implement a + // uniprocessor specific version of this barrier if uni-proc becomes a significant scenario again. + dmb + // Write the reference into the location. Note that we rely on the fact that no GC can occur between here + // and the card table update we may perform below. +ALTERNATE_ENTRY "RhpCheckedAssignRefAvLocation"\EXPORT_REG_NAME // WriteBarrierFunctionAvLocation +.ifc \REFREG, r1 +ALTERNATE_ENTRY RhpCheckedAssignRefAVLocation +.endif + str \REFREG, [r0] + + DEFINE_CHECKED_WRITE_BARRIER_CORE RhpCheckedAssignRef, \REFREG + + bx lr +LEAF_END RhpCheckedAssignRef\EXPORT_REG_NAME, _TEXT +.endm + +// One day we might have write barriers for all the possible argument registers but for now we have +// just one write barrier that assumes the input register is RSI. +DEFINE_CHECKED_WRITE_BARRIER r1, r1 + +// WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +// - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpCheckedLockCmpXchgAVLocation +// - Function "UnwindWriteBarrierToCaller" assumes the stack contains just the pushed return address +// r0 = destination address +// r1 = value +// r2 = comparand +LEAF_ENTRY RhpCheckedLockCmpXchg, _TEXT + // To implement our chosen memory model for ARM we insert a memory barrier at GC write brriers. This + // barrier must occur before the object reference update, so we have to do it unconditionally even + // though the update may fail below. + dmb +ALTERNATE_ENTRY RhpCheckedLockCmpXchgAVLocation +LOCAL_LABEL(RhpCheckedLockCmpXchgRetry): + ldrex r3, [r0] + cmp r2, r3 + bne LOCAL_LABEL(RhpCheckedLockCmpXchg_NoBarrierRequired_r1) + strex r3, r1, [r0] + cmp r3, #0 + bne LOCAL_LABEL(RhpCheckedLockCmpXchgRetry) + mov r3, r2 + + DEFINE_CHECKED_WRITE_BARRIER_CORE RhpCheckedLockCmpXchg, r1 + + mov r0, r3 + bx lr +LEAF_END RhpCheckedLockCmpXchg, _TEXT + +// WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +// - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpCheckedXchgAVLocation +// - Function "UnwindWriteBarrierToCaller" assumes the stack contains just the pushed return address +// r0 = destination address +// r1 = value +LEAF_ENTRY RhpCheckedXchg, _TEXT + // To implement our chosen memory model for ARM we insert a memory barrier at GC write barriers. This + // barrier must occur before the object reference update. + dmb +ALTERNATE_ENTRY RhpCheckedXchgAVLocation +LOCAL_LABEL(RhpCheckedXchgRetry): + ldrex r2, [r0] + strex r3, r1, [r0] + cmp r3, #0 + bne LOCAL_LABEL(RhpCheckedXchgRetry) + mov r0, r2 + + DEFINE_CHECKED_WRITE_BARRIER_CORE RhpCheckedXchg, r1 + + bx lr +LEAF_END RhpCheckedXchg, _TEXT // // RhpByRefAssignRef simulates movs instruction for object references. @@ -21,14 +315,67 @@ LEAF_END RhpCheckedAssignRef, _TEXT // On entry: // r0: address of ref-field (assigned to) // r1: address of the data (source) -// r3: be trashed +// r2, r3: be trashed // // On exit: -// r0, r1 are incremented by 4, -// r3: trashed +// r0, r1 are incremented by 4, +// r2, r3: trashed // LEAF_ENTRY RhpByRefAssignRef, _TEXT - ldr r3, [r1], #4 - str r3, [r0], #4 - bx lr + ldr r2, [r1] + str r2, [r0] + + // Check whether the writes were even into the heap. If not there's no card update required. + ldr r3, =C_FUNC(g_lowest_address) + ldr r3, [r3] + cmp r0, r3 + blo LOCAL_LABEL(RhpByRefAssignRef_NotInHeap) + ldr r3, =C_FUNC(g_highest_address) + ldr r3, [r3] + cmp r0, r3 + bhi LOCAL_LABEL(RhpByRefAssignRef_NotInHeap) + + // Update the shadow copy of the heap with the same value just written to the same heap. (A no-op unless + // we're in a debug build and write barrier checking has been enabled). + UPDATE_GC_SHADOW BASENAME, r2, r0 + + // If the reference is to an object that's not in an ephemeral generation we have no need to track it + // (since the object won't be collected or moved by an ephemeral collection). + ldr r3, =C_FUNC(g_ephemeral_low) + ldr r3, [r3] + cmp r2, r3 + blo LOCAL_LABEL(RhpByRefAssignRef_NotInHeap) + ldr r3, =C_FUNC(g_ephemeral_high) + ldr r3, [r3] + cmp r2, r3 + bhi LOCAL_LABEL(RhpByRefAssignRef_NotInHeap) + + // move current r0 value into r2 and then increment the pointers + mov r2, r0 + add r1, #4 + add r0, #4 + + // We have a location on the GC heap being updated with a reference to an ephemeral object so we must + // track this write. The location address is translated into an offset in the card table bitmap. We set + // an entire byte in the card table since it's quicker than messing around with bitmasks and we only write + // the byte if it hasn't already been done since writes are expensive and impact scaling. + ldr r3, =C_FUNC(g_card_table) + ldr r3, [r3] + add r2, r3, r2, lsr #LOG2_CLUMP_SIZE + ldrb r3, [r2] + cmp r3, #0FFh + bne LOCAL_LABEL(RhpByRefAssignRef_UpdateCardTable) + bx lr + +// We get here if it's necessary to update the card table. +LOCAL_LABEL(RhpByRefAssignRef_UpdateCardTable): + mov r3, #0FFh + strb r3, [r2] + bx lr + +LOCAL_LABEL(RhpByRefAssignRef_NotInHeap): + // Increment the pointers before leaving + add r0, #4 + add r1, #4 + bx lr LEAF_END RhpByRefAssignRef, _TEXT diff --git a/external/corert/src/Native/Runtime/arm64/AllocFast.asm b/external/corert/src/Native/Runtime/arm64/AllocFast.asm new file mode 100644 index 0000000000..b01106cbfc --- /dev/null +++ b/external/corert/src/Native/Runtime/arm64/AllocFast.asm @@ -0,0 +1,290 @@ +;; 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. + +#include "AsmMacros.h" + + TEXTAREA + +;; Allocate non-array, non-finalizable object. If the allocation doesn't fit into the current thread's +;; allocation context then automatically fallback to the slow allocation path. +;; x0 == EEType + LEAF_ENTRY RhpNewFast + + ;; x1 = GetThread(), TRASHES x2 + INLINE_GETTHREAD x1, x2 + + ;; + ;; x0 contains EEType pointer + ;; + ldr w2, [x0, #OFFSETOF__EEType__m_uBaseSize] + + ;; + ;; x0: EEType pointer + ;; x1: Thread pointer + ;; x2: base size + ;; + + ;; Load potential new object address into x12. + ldr x12, [x1, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Determine whether the end of the object would lie outside of the current allocation context. If so, + ;; we abandon the attempt to allocate the object directly and fall back to the slow helper. + add x2, x2, x12 + ldr x13, [x1, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp x2, x13 + bhi RhpNewFast_RarePath + + ;; Update the alloc pointer to account for the allocation. + str x2, [x1, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Set the new object's EEType pointer + str x0, [x12, #OFFSETOF__Object__m_pEEType] + + mov x0, x12 + ret + +RhpNewFast_RarePath + mov x1, #0 + b RhpNewObject + LEAF_END RhpNewFast + + INLINE_GETTHREAD_CONSTANT_POOL + +;; Allocate non-array object with finalizer. +;; x0 == EEType + LEAF_ENTRY RhpNewFinalizable + mov x1, #GC_ALLOC_FINALIZE + b RhpNewObject + LEAF_END RhpNewFinalizable + +;; Allocate non-array object. +;; x0 == EEType +;; x1 == alloc flags + NESTED_ENTRY RhpNewObject + + PUSH_COOP_PINVOKE_FRAME x3 + + ;; x3: transition frame + + ;; Preserve the EEType in x19 + mov x19, x0 + + ldr w2, [x0, #OFFSETOF__EEType__m_uBaseSize] + + ;; Call the rest of the allocation helper. + ;; void* RhpGcAlloc(EEType *pEEType, UInt32 uFlags, UIntNative cbSize, void * pTransitionFrame) + bl RhpGcAlloc + + ;; Set the new object's EEType pointer on success. + cbz x0, NewOutOfMemory + str x19, [x0, #OFFSETOF__Object__m_pEEType] + + ;; If the object is bigger than RH_LARGE_OBJECT_SIZE, we must publish it to the BGC + ldr w1, [x19, #OFFSETOF__EEType__m_uBaseSize] + movk x2, #(RH_LARGE_OBJECT_SIZE & 0xFFFF) + movk x2, #(RH_LARGE_OBJECT_SIZE >> 16), lsl #16 + cmp x1, x2 + blo New_SkipPublish + + ;; x0: object + ;; x1: already contains object size + bl RhpPublishObject ;; x0: this function returns the object that was passed-in + +New_SkipPublish + + POP_COOP_PINVOKE_FRAME + EPILOG_RETURN + +NewOutOfMemory + ;; This is the OOM failure path. We're going to tail-call to a managed helper that will throw + ;; an out of memory exception that the caller of this allocator understands. + + mov x0, x19 ; EEType pointer + mov x1, 0 ; Indicate that we should throw OOM. + + POP_COOP_PINVOKE_FRAME + EPILOG_NOP b RhExceptionHandling_FailedAllocation + + NESTED_END RhpNewObject + +;; Allocate a string. +;; x0 == EEType +;; x1 == element/character count + LEAF_ENTRY RhNewString + ;; Make sure computing the overall allocation size won't overflow + mov x2, #0x7FFFFFFF + cmp x1, x2 + bhi StringSizeOverflow + + ;; Compute overall allocation size (align(base size + (element size * elements), 8)). + mov w2, #STRING_COMPONENT_SIZE + mov x3, #(STRING_BASE_SIZE + 7) + umaddl x2, w1, w2, x3 ; x2 = w1 * w2 + x3 + and x2, x2, #-8 + + ; x0 == EEType + ; x1 == element count + ; x2 == string size + + INLINE_GETTHREAD x3, x5 + + ;; Load potential new object address into x12. + ldr x12, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Determine whether the end of the object would lie outside of the current allocation context. If so, + ;; we abandon the attempt to allocate the object directly and fall back to the slow helper. + add x2, x2, x12 + ldr x12, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp x2, x12 + bhi RhpNewArrayRare + + ;; Reload new object address into r12. + ldr x12, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Update the alloc pointer to account for the allocation. + str x2, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Set the new object's EEType pointer and element count. + str x0, [x12, #OFFSETOF__Object__m_pEEType] + str x1, [x12, #OFFSETOF__Array__m_Length] + + ;; Return the object allocated in x0. + mov x0, x12 + + ret + +StringSizeOverflow + ; We get here if the length of the final string object can't be represented as an unsigned + ; 32-bit value. We're going to tail-call to a managed helper that will throw + ; an OOM exception that the caller of this allocator understands. + + ; x0 holds EEType pointer already + mov x1, #1 ; Indicate that we should throw OverflowException + b RhExceptionHandling_FailedAllocation + LEAF_END RhNewString + + INLINE_GETTHREAD_CONSTANT_POOL + + +;; Allocate one dimensional, zero based array (SZARRAY). +;; x0 == EEType +;; x1 == element count + LEAF_ENTRY RhpNewArray + + ;; We want to limit the element count to the non-negative 32-bit int range. + ;; If the element count is <= 0x7FFFFFFF, no overflow is possible because the component + ;; size is <= 0xffff (it's an unsigned 16-bit value), and the base size for the worst + ;; case (32 dimensional MdArray) is less than 0xffff, and thus the product fits in 64 bits. + mov x2, #0x7FFFFFFF + cmp x1, x2 + bhi ArraySizeOverflow + + ldrh w2, [x0, #OFFSETOF__EEType__m_usComponentSize] + umull x2, w1, w2 + ldr w3, [x0, #OFFSETOF__EEType__m_uBaseSize] + add x2, x2, x3 + add x2, x2, #7 + and x2, x2, #-8 + + ; x0 == EEType + ; x1 == element count + ; x2 == array size + + INLINE_GETTHREAD x3, x5 + + ;; Load potential new object address into x12. + ldr x12, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Determine whether the end of the object would lie outside of the current allocation context. If so, + ;; we abandon the attempt to allocate the object directly and fall back to the slow helper. + add x2, x2, x12 + ldr x12, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_limit] + cmp x2, x12 + bhi RhpNewArrayRare + + ;; Reload new object address into x12. + ldr x12, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Update the alloc pointer to account for the allocation. + str x2, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + + ;; Set the new object's EEType pointer and element count. + str x0, [x12, #OFFSETOF__Object__m_pEEType] + str x1, [x12, #OFFSETOF__Array__m_Length] + + ;; Return the object allocated in r0. + mov x0, x12 + + ret + +ArraySizeOverflow + ; We get here if the size of the final array object can't be represented as an unsigned + ; 32-bit value. We're going to tail-call to a managed helper that will throw + ; an overflow exception that the caller of this allocator understands. + + ; x0 holds EEType pointer already + mov x1, #1 ; Indicate that we should throw OverflowException + b RhExceptionHandling_FailedAllocation + LEAF_END RhpNewArray + + INLINE_GETTHREAD_CONSTANT_POOL + +;; Allocate one dimensional, zero based array (SZARRAY) using the slow path that calls a runtime helper. +;; x0 == EEType +;; x1 == element count +;; x2 == array size + Thread::m_alloc_context::alloc_ptr +;; x3 == Thread + NESTED_ENTRY RhpNewArrayRare + + ; Recover array size by subtracting the alloc_ptr from x2. + PROLOG_NOP ldr x12, [x3, #OFFSETOF__Thread__m_alloc_context__alloc_ptr] + PROLOG_NOP sub x2, x2, x12 + + PUSH_COOP_PINVOKE_FRAME x3 + + ; Preserve data we'll need later into the callee saved registers + mov x19, x0 ; Preserve EEType + mov x20, x1 ; Preserve element count + mov x21, x2 ; Preserve array size + + mov x1, #0 + + ;; void* RhpGcAlloc(EEType *pEEType, UInt32 uFlags, UIntNative cbSize, void * pTransitionFrame) + bl RhpGcAlloc + + ; Set the new object's EEType pointer and length on success. + cbz x0, ArrayOutOfMemory + + ; Success, set the array's type and element count in the new object. + str x19, [x0, #OFFSETOF__Object__m_pEEType] + str x20, [x0, #OFFSETOF__Array__m_Length] + + ;; If the object is bigger than RH_LARGE_OBJECT_SIZE, we must publish it to the BGC + movk x2, #(RH_LARGE_OBJECT_SIZE & 0xFFFF) + movk x2, #(RH_LARGE_OBJECT_SIZE >> 16), lsl #16 + cmp x21, x2 + blo NewArray_SkipPublish + + ;; x0 = newly allocated array. x1 = size + mov x1, x21 + bl RhpPublishObject + +NewArray_SkipPublish + + POP_COOP_PINVOKE_FRAME + EPILOG_RETURN + +ArrayOutOfMemory + ;; This is the OOM failure path. We're going to tail-call to a managed helper that will throw + ;; an out of memory exception that the caller of this allocator understands. + + mov x0, x19 ; EEType Pointer + mov x1, 0 ; Indicate that we should throw OOM. + + POP_COOP_PINVOKE_FRAME + EPILOG_NOP b RhExceptionHandling_FailedAllocation + + NESTED_END RhpNewArrayRare + + END diff --git a/external/corert/src/Native/Runtime/arm64/AsmMacros.h b/external/corert/src/Native/Runtime/arm64/AsmMacros.h index c7903d1416..f6a1b238bd 100644 --- a/external/corert/src/Native/Runtime/arm64/AsmMacros.h +++ b/external/corert/src/Native/Runtime/arm64/AsmMacros.h @@ -3,7 +3,7 @@ ;; See the LICENSE file in the project root for more information. ;; OS provided macros -#include +#include ;; generated by the build from AsmOffsets.cpp #include "AsmOffsets.inc" @@ -15,13 +15,75 @@ TSF_SuppressGcStress equ 0x08 TSF_DoNotTriggerGc equ 0x10 TSF_SuppressGcStress__OR__TSF_DoNotTriggerGC equ 0x18 +;; Bit position for the flags above, to be used with tbz/tbnz instructions +TSF_Attached_Bit equ 0 +TSF_SuppressGcStress_Bit equ 3 +TSF_DoNotTriggerGc_Bit equ 4 + ;; GC type flags GC_ALLOC_FINALIZE equ 1 GC_ALLOC_ALIGN8_BIAS equ 4 GC_ALLOC_ALIGN8 equ 8 ;; Note: these must match the defs in PInvokeTransitionFrameFlags defined in rhbinder.h -;; FIXME:ARM64 +PTFF_SAVE_X19 equ 0x00000001 +PTFF_SAVE_X20 equ 0x00000002 +PTFF_SAVE_X21 equ 0x00000004 +PTFF_SAVE_X22 equ 0x00000008 +PTFF_SAVE_X23 equ 0x00000010 +PTFF_SAVE_X24 equ 0x00000020 +PTFF_SAVE_X25 equ 0x00000040 +PTFF_SAVE_X26 equ 0x00000080 +PTFF_SAVE_X27 equ 0x00000100 +PTFF_SAVE_X28 equ 0x00000200 +PTFF_SAVE_SP equ 0x00000400 +PTFF_SAVE_ALL_PRESERVED equ 0x000003FF ;; NOTE: x19-x28 +PTFF_SAVE_X0 equ 0x00000800 +PTFF_SAVE_X1 equ 0x00001000 +PTFF_SAVE_X2 equ 0x00002000 +PTFF_SAVE_X3 equ 0x00004000 +PTFF_SAVE_X4 equ 0x00008000 +PTFF_SAVE_X5 equ 0x00010000 +PTFF_SAVE_X6 equ 0x00020000 +PTFF_SAVE_X7 equ 0x00040000 +PTFF_SAVE_X8 equ 0x00080000 +PTFF_SAVE_X9 equ 0x00100000 +PTFF_SAVE_X10 equ 0x00200000 +PTFF_SAVE_X11 equ 0x00400000 +PTFF_SAVE_X12 equ 0x00800000 +PTFF_SAVE_X13 equ 0x01000000 +PTFF_SAVE_X14 equ 0x02000000 +PTFF_SAVE_X15 equ 0x04000000 +PTFF_SAVE_X16 equ 0x08000000 +PTFF_SAVE_X17 equ 0x10000000 +PTFF_SAVE_X18 equ 0x20000000 +PTFF_SAVE_ALL_SCRATCH equ 0x3FFFF800 ;; NOTE: X0-X18 +PTFF_SAVE_FP equ 0x40000000 +PTFF_SAVE_LR equ 0x80000000 + +;; NOTE: The following flags represent the upper 32 bits of the PInvokeTransitionFrameFlags. +;; Since the assembler doesn't support 64 bit constants in any way, we need to define just +;; the upper bits here +PTFF_X0_IS_GCREF_HI equ 0x00000001 ;; iff PTFF_SAVE_X0 : set->x0 is Object, clear->x0 is scalar +PTFF_X0_IS_BYREF_HI equ 0x00000002 ;; iff PTFF_SAVE_X0 : set->x0 is ByRef, clear->x0 is Object or scalar +PTFF_X1_IS_GCREF_HI equ 0x00000004 ;; iff PTFF_SAVE_X1 : set->x1 is Object, clear->x1 is scalar +PTFF_X1_IS_BYREF_HI equ 0x00000008 ;; iff PTFF_SAVE_X1 : set->x1 is ByRef, clear->x1 is Object or scalar +PTFF_THREAD_ABORT_HI equ 0x00000010 ;; indicates that ThreadAbortException should be thrown when returning from the transition + +;; Bit position for the flags above, to be used with tbz / tbnz instructions +PTFF_THREAD_ABORT_BIT equ 36 + +;; These must match the TrapThreadsFlags enum +TrapThreadsFlags_None equ 0 +TrapThreadsFlags_AbortInProgress equ 1 +TrapThreadsFlags_TrapThreads equ 2 + +;; Bit position for the flags above, to be used with tbz / tbnz instructions +TrapThreadsFlags_AbortInProgress_Bit equ 0 +TrapThreadsFlags_TrapThreads_Bit equ 1 + +;; This must match HwExceptionCode.STATUS_REDHAWK_THREAD_ABORT +STATUS_REDHAWK_THREAD_ABORT equ 0x43 ;; ;; Rename fields of nested structs @@ -29,3 +91,227 @@ GC_ALLOC_ALIGN8 equ 8 OFFSETOF__Thread__m_alloc_context__alloc_ptr equ OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_ptr OFFSETOF__Thread__m_alloc_context__alloc_limit equ OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_limit +;; +;; IMPORTS +;; + EXTERN RhpGcAlloc + EXTERN RhpPublishObject + EXTERN RhExceptionHandling_FailedAllocation + EXTERN RhDebugBreak + EXTERN RhpWaitForSuspend2 + EXTERN RhpWaitForGC2 + EXTERN RhpReversePInvokeAttachOrTrapThread2 + EXTERN RhpCalculateStackTraceWorker + EXTERN RhThrowHwEx + EXTERN RhThrowEx + EXTERN RhRethrow + + EXTERN RhpTrapThreads + EXTERN g_lowest_address + EXTERN g_highest_address + EXTERN g_ephemeral_low + EXTERN g_ephemeral_high + EXTERN g_card_table + + +;; ----------------------------------------------------------------------------- +;; Macro used to assign an alternate name to a symbol containing characters normally disallowed in a symbol +;; name (e.g. C++ decorated names). + MACRO + SETALIAS $name, $symbol + GBLS $name +$name SETS "|$symbol|" + MEND + +;;----------------------------------------------------------------------------- +;; Macro for loading a 64-bit constant by a minimal number of instructions +;; Since the asssembles doesn't support 64 bit arithmetics in expressions, +;; the value is passed in as lo, hi pair. + MACRO + MOVL64 $Reg, $ConstantLo, $ConstantHi + + LCLS MovInstr +MovInstr SETS "movz" + + IF ((($ConstantHi):SHR:16):AND:0xffff) != 0 + $MovInstr $Reg, #((($Constant):SHR:16):AND:0xffff), lsl #48 +MovInstr SETS "movk" + ENDIF + + IF (($ConstantHi):AND:0xffff) != 0 + $MovInstr $Reg, #(($ConstantHi):AND:0xffff), lsl #32 +MovInstr SETS "movk" + ENDIF + + IF ((($ConstantLo):SHR:16):AND:0xffff) != 0 + $MovInstr $Reg, #((($ConstantLo):SHR:16):AND:0xffff), lsl #16 +MovInstr SETS "movk" + ENDIF + + $MovInstr $Reg, #(($ConstantLo):AND:0xffff) + MEND + +;; ----------------------------------------------------------------------------- +;; +;; Macro to export a pointer to an address inside a stub as a 64-bit variable +;; + MACRO + EXPORT_POINTER_TO_ADDRESS $Name + LCLS CodeLbl +CodeLbl SETS "$Name":CC:"Lbl" +$CodeLbl + AREA | .rdata | , ALIGN = 8, DATA, READONLY +$Name + DCQ $CodeLbl + EXPORT $Name + TEXTAREA + ROUT + + MEND + +;; ----------------------------------------------------------------------------- +;; +;; Macro for indicating an alternate entry point into a function. +;; + + MACRO + LABELED_RETURN_ADDRESS $ReturnAddressName + + ; export the return address name, but do not perturb the code by forcing alignment +$ReturnAddressName + EXPORT $ReturnAddressName + + ; flush any pending literal pool stuff + ROUT + + MEND + +;; ----------------------------------------------------------------------------- +;; +;; Macro to get a pointer to the Thread* object for the currently executing thread +;; + +__tls_array equ 0x58 ;; offsetof(TEB, ThreadLocalStoragePointer) + + EXTERN _tls_index + + GBLS __SECTIONREL_tls_CurrentThread +__SECTIONREL_tls_CurrentThread SETS "SECTIONREL_tls_CurrentThread" + + MACRO + INLINE_GETTHREAD $destReg, $trashReg + + ;; The following macro variables are just some assembler magic to get the name of the 32-bit version + ;; of $trashReg. It does it by string manipulation. Replaces something like x3 with w3. + LCLS TrashRegister32Bit +TrashRegister32Bit SETS "$trashReg" +TrashRegister32Bit SETS "w":CC:("$TrashRegister32Bit":RIGHT:((:LEN:TrashRegister32Bit) - 1)) + + ldr $trashReg, =_tls_index + ldr $TrashRegister32Bit, [$trashReg] + ldr $destReg, [xpr, #__tls_array] + ldr $destReg, [$destReg, $trashReg lsl #3] + ldr $trashReg, =$__SECTIONREL_tls_CurrentThread + ldr $trashReg, [$trashReg] + add $destReg, $destReg, $trashReg + MEND + + ;; INLINE_GETTHREAD_CONSTANT_POOL macro has to be used after the last function in the .asm file that used + ;; INLINE_GETTHREAD. Optionally, it can be also used after any function that used INLINE_GETTHREAD + ;; to improve density, or to reduce distance betweeen the constant pool and its use. + MACRO + INLINE_GETTHREAD_CONSTANT_POOL + EXTERN tls_CurrentThread + + ;; Section relocs are 32 bits. Using an extra DCD initialized to zero for 8-byte alignment. +$__SECTIONREL_tls_CurrentThread + DCD tls_CurrentThread + RELOC 8, tls_CurrentThread ;; SECREL + DCD 0 + +__SECTIONREL_tls_CurrentThread SETS "$__SECTIONREL_tls_CurrentThread":CC:"_" + + MEND + + MACRO + INLINE_THREAD_UNHIJACK $threadReg, $trashReg1, $trashReg2 + ;; + ;; Thread::Unhijack() + ;; + ldr $trashReg1, [$threadReg, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + cbz $trashReg1, %ft0 + + ldr $trashReg2, [$threadReg, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + str $trashReg1, [$trashReg2] + str xzr, [$threadReg, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + str xzr, [$threadReg, #OFFSETOF__Thread__m_pvHijackedReturnAddress] +0 + MEND + +;; ----------------------------------------------------------------------------- +;; +;; Macro used from unmanaged helpers called from managed code where the helper does not transition immediately +;; into pre-emptive mode but may cause a GC and thus requires the stack is crawlable. This is typically the +;; case for helpers that meddle in GC state (e.g. allocation helpers) where the code must remain in +;; cooperative mode since it handles object references and internal GC state directly but a garbage collection +;; may be inevitable. In these cases we need to be able to transition to pre-meptive mode deep within the +;; unmanaged code but still be able to initialize the stack iterator at the first stack frame which may hold +;; interesting GC references. In all our helper cases this corresponds to the most recent managed frame (e.g. +;; the helper's caller). +;; +;; This macro builds a frame describing the current state of managed code. +;; +;; INVARIANTS +;; - The macro assumes it defines the method prolog, it should typically be the first code in a method and +;; certainly appear before any attempt to alter the stack pointer. +;; - This macro uses trashReg (after its initial value has been saved in the frame) and upon exit trashReg +;; will contain the address of transition frame. +;; + +DEFAULT_FRAME_SAVE_FLAGS equ PTFF_SAVE_ALL_PRESERVED + PTFF_SAVE_SP + + MACRO + PUSH_COOP_PINVOKE_FRAME $trashReg + + PROLOG_SAVE_REG_PAIR fp, lr, #-0x80! ;; Push down stack pointer and store FP and LR + + ;; 0x10 bytes reserved for Thread* and flags + + ;; Save callee saved registers + PROLOG_SAVE_REG_PAIR x19, x20, #0x20 + PROLOG_SAVE_REG_PAIR x21, x22, #0x30 + PROLOG_SAVE_REG_PAIR x23, x24, #0x40 + PROLOG_SAVE_REG_PAIR x25, x26, #0x50 + PROLOG_SAVE_REG_PAIR x27, x28, #0x60 + + ;; Save the value of SP before stack allocation to the last slot in the frame (slot #15) + add $trashReg, sp, #0x80 + str $trashReg, [sp, #0x70] + + ;; Record the bitmask of saved registers in the frame (slot #3) + mov $trashReg, #DEFAULT_FRAME_SAVE_FLAGS + str $trashReg, [sp, #0x18] + + mov $trashReg, sp + MEND + +;; Pop the frame and restore register state preserved by PUSH_COOP_PINVOKE_FRAME + MACRO + POP_COOP_PINVOKE_FRAME + + EPILOG_RESTORE_REG_PAIR x19, x20, #0x20 + EPILOG_RESTORE_REG_PAIR x21, x22, #0x30 + EPILOG_RESTORE_REG_PAIR x23, x24, #0x40 + EPILOG_RESTORE_REG_PAIR x25, x26, #0x50 + EPILOG_RESTORE_REG_PAIR x27, x28, #0x60 + EPILOG_RESTORE_REG_PAIR fp, lr, #0x80! + MEND + + +#ifdef FEATURE_GC_STRESS + SETALIAS THREAD__HIJACKFORGCSTRESS, ?HijackForGcStress@Thread@@SAXPEAUPAL_LIMITED_CONTEXT@@@Z + SETALIAS REDHAWKGCINTERFACE__STRESSGC, ?StressGc@RedhawkGCInterface@@SAXXZ + + EXTERN $REDHAWKGCINTERFACE__STRESSGC + EXTERN $THREAD__HIJACKFORGCSTRESS +#endif ;; FEATURE_GC_STRESS diff --git a/external/corert/src/Native/Runtime/arm64/AsmOffsetsCpu.h b/external/corert/src/Native/Runtime/arm64/AsmOffsetsCpu.h index b25f11d1de..bf8b5e4299 100644 --- a/external/corert/src/Native/Runtime/arm64/AsmOffsetsCpu.h +++ b/external/corert/src/Native/Runtime/arm64/AsmOffsetsCpu.h @@ -8,7 +8,7 @@ // // NOTE: the offsets MUST be in hex notation WITHOUT the 0x prefix -PLAT_ASM_SIZEOF(240, ExInfo) +PLAT_ASM_SIZEOF(288, ExInfo) PLAT_ASM_OFFSET(0, ExInfo, m_pPrevExInfo) PLAT_ASM_OFFSET(8, ExInfo, m_pExContext) PLAT_ASM_OFFSET(10, ExInfo, m_exception) @@ -16,25 +16,50 @@ PLAT_ASM_OFFSET(18, ExInfo, m_kind) PLAT_ASM_OFFSET(19, ExInfo, m_passNumber) PLAT_ASM_OFFSET(1c, ExInfo, m_idxCurClause) PLAT_ASM_OFFSET(20, ExInfo, m_frameIter) -PLAT_ASM_OFFSET(238, ExInfo, m_notifyDebuggerSP) +PLAT_ASM_OFFSET(280, ExInfo, m_notifyDebuggerSP) -PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_RIP) -PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_FramePointer) +PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_FramePointer) +PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_RIP) PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_pThread) -PLAT_ASM_OFFSET(18, PInvokeTransitionFrame, m_dwFlags) +PLAT_ASM_OFFSET(18, PInvokeTransitionFrame, m_Flags) PLAT_ASM_OFFSET(20, PInvokeTransitionFrame, m_PreservedRegs) -PLAT_ASM_SIZEOF(218, StackFrameIterator) +PLAT_ASM_SIZEOF(260, StackFrameIterator) PLAT_ASM_OFFSET(10, StackFrameIterator, m_FramePointer) PLAT_ASM_OFFSET(18, StackFrameIterator, m_ControlPC) PLAT_ASM_OFFSET(20, StackFrameIterator, m_RegDisplay) -PLAT_ASM_SIZEOF(8, PAL_LIMITED_CONTEXT) -PLAT_ASM_OFFSET(0, PAL_LIMITED_CONTEXT, IP) +PLAT_ASM_SIZEOF(C0, PAL_LIMITED_CONTEXT) -// @TODO: Add ARM64 entries for PAL_LIMITED_CONTEXT +PLAT_ASM_OFFSET(0, PAL_LIMITED_CONTEXT, FP) +PLAT_ASM_OFFSET(8, PAL_LIMITED_CONTEXT, LR) +PLAT_ASM_OFFSET(10, PAL_LIMITED_CONTEXT, X0) +PLAT_ASM_OFFSET(18, PAL_LIMITED_CONTEXT, X1) +PLAT_ASM_OFFSET(20, PAL_LIMITED_CONTEXT, X19) +PLAT_ASM_OFFSET(28, PAL_LIMITED_CONTEXT, X20) +PLAT_ASM_OFFSET(30, PAL_LIMITED_CONTEXT, X21) +PLAT_ASM_OFFSET(38, PAL_LIMITED_CONTEXT, X22) +PLAT_ASM_OFFSET(40, PAL_LIMITED_CONTEXT, X23) +PLAT_ASM_OFFSET(48, PAL_LIMITED_CONTEXT, X24) +PLAT_ASM_OFFSET(50, PAL_LIMITED_CONTEXT, X25) +PLAT_ASM_OFFSET(58, PAL_LIMITED_CONTEXT, X26) +PLAT_ASM_OFFSET(60, PAL_LIMITED_CONTEXT, X27) +PLAT_ASM_OFFSET(68, PAL_LIMITED_CONTEXT, X28) +PLAT_ASM_OFFSET(70, PAL_LIMITED_CONTEXT, SP) +PLAT_ASM_OFFSET(78, PAL_LIMITED_CONTEXT, IP) PLAT_ASM_SIZEOF(150, REGDISPLAY) PLAT_ASM_OFFSET(f8, REGDISPLAY, SP) -// @TODO: Add ARM64 entries for REGDISPLAY +PLAT_ASM_OFFSET(98, REGDISPLAY, pX19) +PLAT_ASM_OFFSET(a0, REGDISPLAY, pX20) +PLAT_ASM_OFFSET(a8, REGDISPLAY, pX21) +PLAT_ASM_OFFSET(b0, REGDISPLAY, pX22) +PLAT_ASM_OFFSET(b8, REGDISPLAY, pX23) +PLAT_ASM_OFFSET(c0, REGDISPLAY, pX24) +PLAT_ASM_OFFSET(c8, REGDISPLAY, pX25) +PLAT_ASM_OFFSET(d0, REGDISPLAY, pX26) +PLAT_ASM_OFFSET(d8, REGDISPLAY, pX27) +PLAT_ASM_OFFSET(e0, REGDISPLAY, pX28) +PLAT_ASM_OFFSET(e8, REGDISPLAY, pFP) +PLAT_ASM_OFFSET(110, REGDISPLAY, D) diff --git a/external/corert/src/Native/Runtime/arm64/CallDescrWorker.asm b/external/corert/src/Native/Runtime/arm64/CallDescrWorker.asm new file mode 100644 index 0000000000..06a251280d --- /dev/null +++ b/external/corert/src/Native/Runtime/arm64/CallDescrWorker.asm @@ -0,0 +1,144 @@ +;; 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. + +#include "AsmMacros.h" + + TEXTAREA + +;;----------------------------------------------------------------------------- +;; This helper routine enregisters the appropriate arguments and makes the +;; actual call. +;; +;; INPUT: x0: pointer to CallDescrData struct +;; +;;----------------------------------------------------------------------------- +;;void RhCallDescrWorker(CallDescrData * pCallDescrData); + NESTED_ENTRY RhCallDescrWorker + + PROLOG_SAVE_REG_PAIR fp, lr, #-32! + PROLOG_SAVE_REG_PAIR x19, x20, #16 + + ;; Save the value of SP before we start pushing any arguments + mov x20, sp + + mov x19, x0 ; save pCallDescrData in x19 + + ldr w1, [x19, #OFFSETOF__CallDescrData__numStackSlots] + cbz w1, Ldonestack + + ;; Add frame padding to ensure frame size is a multiple of 16 (a requirement of the OS ABI). + ;; We push two registers (above) and numStackSlots arguments (below). If this comes to an odd number + ;; of slots we must pad with another. This simplifies to "if the low bit of numStackSlots is set, + ;; extend the stack another eight bytes". + ldr x0, [x19, #OFFSETOF__CallDescrData__pSrc] + add x0, x0, x1 lsl #3 ; pSrcEnd=pSrc+8*numStackSlots + ands x2, x1, #1 + beq Lstackloop + + ;; This loop copies numStackSlots words + ;; from [pSrcEnd-8,pSrcEnd-16,...] to [sp-8,sp-16,...] + + ;; Pad and store one stack slot as number of slots are odd + ldr x4, [x0,#-8]! + str x4, [sp,#-16]! + subs x1, x1, #1 + beq Ldonestack +Lstackloop + ldp x2, x4, [x0,#-16]! + stp x2, x4, [sp,#-16]! + subs x1, x1, #2 + bne Lstackloop +Ldonestack + + ;; If FP arguments are supplied in registers (x9 != NULL) then initialize all of them from the pointer + ;; given in x9. + ldr x9, [x19, #OFFSETOF__CallDescrData__pFloatArgumentRegisters] + cbz x9, LNoFloatingPoint + ldp d0, d1, [x9] + ldp d2, d3, [x9, #16] + ldp d4, d5, [x9, #32] + ldp d6, d7, [x9, #48] +LNoFloatingPoint + + ;; Copy [pArgumentRegisters, ..., pArgumentRegisters + 64] + ;; into x0, ..., x7, x8 + + ldr x9, [x19, #OFFSETOF__CallDescrData__pArgumentRegisters] + ldp x0, x1, [x9] + ldp x2, x3, [x9, #16] + ldp x4, x5, [x9, #32] + ldp x6, x7, [x9, #48] + ldr x8, [x9, #64] + + ;; call pTarget + ldr x9, [x19, #OFFSETOF__CallDescrData__pTarget] + blr x9 + + EXPORT_POINTER_TO_ADDRESS PointerToReturnFromCallDescrThunk + + ;; Symbol used to identify thunk call to managed function so the special + ;; case unwinder can unwind through this function. Sadly we cannot directly + ;; export this symbol right now because it confuses DIA unwinder to believe + ;; it's the beginning of a new method, therefore we export the address + ;; of an auxiliary variable holding the address instead. + + ldr w3, [x19, #OFFSETOF__CallDescrData__fpReturnSize] + + ;; Unlike desktop returnValue is a pointer to a return buffer, not the buffer itself + ldr x19, [x19, #OFFSETOF__CallDescrData__pReturnBuffer] + + ;; Int return case + cbz w3, LIntReturn + + ;; Float return case + cmp w3, #4 + beq LFloatOrDoubleReturn + + ;; Double return case + cmp w3, #8 + bne LCheckHFAReturn + +LFloatOrDoubleReturn + str d0, [x19] + b LReturnDone + +LCheckHFAReturn + cmp w3, #16 + beq LFloatOrDoubleHFAReturn + cmp w3, #32 + beq LFloatOrDoubleHFAReturn + b LNoHFAReturn + +LFloatOrDoubleHFAReturn + ;;Single/Double HFAReturn return case + stp d0, d1, [x19, #00] + stp d2, d3, [x19, #16] + b LReturnDone + +LNoHFAReturn + + EMIT_BREAKPOINT ; Unreachable + +LIntReturn + ;; Save return value(s) into retbuf for int + stp x0, x1, [x19] + +LReturnDone + +#ifdef _DEBUG + ;; Trash the floating point registers to ensure that the HFA return values + ;; won't survive by accident + ldp d0, d1, [sp] + ldp d2, d3, [sp, #16] +#endif + ;; Restore the value of SP + mov sp, x20 + + EPILOG_RESTORE_REG_PAIR x19, x20, #16 + EPILOG_RESTORE_REG_PAIR fp, lr, #32! + EPILOG_RETURN + + NESTED_END RhCallDescrWorker + + END diff --git a/external/corert/src/Native/Runtime/arm64/CallingConventionConverterHelpers.asm b/external/corert/src/Native/Runtime/arm64/CallingConventionConverterHelpers.asm index ac3f03b661..2d43d5bba4 100644 --- a/external/corert/src/Native/Runtime/arm64/CallingConventionConverterHelpers.asm +++ b/external/corert/src/Native/Runtime/arm64/CallingConventionConverterHelpers.asm @@ -2,4 +2,63 @@ ;; The .NET Foundation licenses this file to you under the MIT license. ;; See the LICENSE file in the project root for more information. -;; TODO \ No newline at end of file +#include "ksarm64.h" + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CallingConventionCoverter Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +POINTER_SIZE equ 0x08 + +;; +;; Note: The "__jmpstub__" prefix is used to indicate to debugger +;; that it must step-through this stub when it encounters it while +;; stepping. +;; + + ;; + ;; void CallingConventionConverter_ReturnThunk() + ;; + LEAF_ENTRY CallingConventionConverter_ReturnThunk + ret + LEAF_END CallingConventionConverter_ReturnThunk + + ;; + ;; __jmpstub__CallingConventionConverter_CommonCallingStub + ;; + ;; struct CallingConventionConverter_CommonCallingStub_PointerData + ;; { + ;; void *ManagedCallConverterThunk; + ;; void *UniversalThunk; + ;; } + ;; + ;; struct CommonCallingStubInputData + ;; { + ;; ULONG_PTR CallingConventionId; + ;; CallingConventionConverter_CommonCallingStub_PointerData *commonData; // Only the ManagedCallConverterThunk field is used + ;; // However, it is specified just like other platforms, so the behavior of the common + ;; // calling stub is easier to debug + ;; } + ;; + ;; xip0 - Points at CommonCallingStubInputData + ;; + ;; + LEAF_ENTRY __jmpstub__CallingConventionConverter_CommonCallingStub + ldr xip1, [xip0] ; put CallingConventionId into xip1 as "parameter" to universal transition thunk + ldr xip0, [xip0, #POINTER_SIZE] ; get pointer to CallingConventionConverter_CommonCallingStub_PointerData into xip0 + ldr x12, [xip0, #POINTER_SIZE] ; get address of UniversalTransitionThunk (which we'll tailcall to later) + ldr xip0, [xip0] ; get address of ManagedCallConverterThunk (target for universal thunk to call) + ret x12 + LEAF_END __jmpstub__CallingConventionConverter_CommonCallingStub + + ;; + ;; void CallingConventionConverter_GetStubs(IntPtr *returnVoidStub, IntPtr *returnIntegerStub, IntPtr *commonCallingStub) + ;; + LEAF_ENTRY CallingConventionConverter_GetStubs + ldr x12, =CallingConventionConverter_ReturnThunk + str x12, [x0] ;; ARM doesn't need different return thunks. + str x12, [x1] + ldr x12, =__jmpstub__CallingConventionConverter_CommonCallingStub + str x12, [x2] + ret + LEAF_END CallingConventionConverter_GetStubs + + END diff --git a/external/corert/src/Native/Runtime/arm64/Dummies.asm b/external/corert/src/Native/Runtime/arm64/Dummies.asm new file mode 100644 index 0000000000..0da3907bd5 --- /dev/null +++ b/external/corert/src/Native/Runtime/arm64/Dummies.asm @@ -0,0 +1,19 @@ +;; 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. + +#include "AsmMacros.h" + + TEXTAREA + + LEAF_ENTRY RhpLMod + DCW 0xdefe + bx lr + LEAF_END RhpLMod + + LEAF_ENTRY RhpLMul + DCW 0xdefe + bx lr + LEAF_END RhpLMul + + END diff --git a/external/corert/src/Native/Runtime/arm64/ExceptionHandling.asm b/external/corert/src/Native/Runtime/arm64/ExceptionHandling.asm new file mode 100644 index 0000000000..5df131bbe6 --- /dev/null +++ b/external/corert/src/Native/Runtime/arm64/ExceptionHandling.asm @@ -0,0 +1,622 @@ +;; 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. + +#include "AsmMacros.h" + + TEXTAREA + +#define STACKSIZEOF_ExInfo ((SIZEOF__ExInfo + 15)&(~15)) + +;; ----------------------------------------------------------------------------- +;; Macro used to create frame of exception throwing helpers (RhpThrowEx, RhpThrowHwEx) + MACRO + ALLOC_THROW_FRAME + + ;; Setup a PAL_LIMITED_CONTEXT on the stack { + PROLOG_SAVE_REG_PAIR fp, lr, #-SIZEOF__PAL_LIMITED_CONTEXT! + PROLOG_NOP stp x0, x1, [sp, #0x10] + PROLOG_SAVE_REG_PAIR x19, x20, #0x20 + PROLOG_SAVE_REG_PAIR x21, x22, #0x30 + PROLOG_SAVE_REG_PAIR x23, x24, #0x40 + PROLOG_SAVE_REG_PAIR x25, x26, #0x50 + PROLOG_SAVE_REG_PAIR x27, x28, #0x60 + PROLOG_NOP stp x0, lr, [sp, #0x70] ; x0 is the SP and lr is the IP of the fault site + PROLOG_NOP stp d8, d9, [sp, #0x80] + PROLOG_NOP stp d10, d11, [sp, #0x90] + PROLOG_NOP stp d12, d13, [sp, #0xA0] + PROLOG_NOP stp d14, d15, [sp, #0xB0] + ;; } end PAL_LIMITED_CONTEXT + + PROLOG_STACK_ALLOC STACKSIZEOF_ExInfo + MEND + +;; ----------------------------------------------------------------------------- +;; Macro used to create frame of funclet calling helpers (RhpCallXXXXFunclet) +;; $extraStackSize - extra stack space that the user of the macro can use to +;; store additional registers + MACRO + ALLOC_CALL_FUNCLET_FRAME $extraStackSize + + PROLOG_SAVE_REG_PAIR fp, lr, #-0x60! + PROLOG_SAVE_REG_PAIR x19, x20, #0x10 + PROLOG_SAVE_REG_PAIR x21, x22, #0x20 + PROLOG_SAVE_REG_PAIR x23, x24, #0x30 + PROLOG_SAVE_REG_PAIR x25, x26, #0x40 + PROLOG_SAVE_REG_PAIR x27, x28, #0x50 + + IF $extraStackSize != 0 + PROLOG_STACK_ALLOC $extraStackSize + ENDIF + MEND + +;; ----------------------------------------------------------------------------- +;; Macro used to free frame of funclet calling helpers (RhpCallXXXXFunclet) +;; $extraStackSize - extra stack space that the user of the macro can use to +;; store additional registers. +;; It needs to match the value passed to the corresponding +;; ALLOC_CALL_FUNCLET_FRAME. + MACRO + FREE_CALL_FUNCLET_FRAME $extraStackSize + + IF $extraStackSize != 0 + EPILOG_STACK_FREE $extraStackSize + ENDIF + + EPILOG_RESTORE_REG_PAIR x19, x20, #0x10 + EPILOG_RESTORE_REG_PAIR x21, x22, #0x20 + EPILOG_RESTORE_REG_PAIR x23, x24, #0x30 + EPILOG_RESTORE_REG_PAIR x25, x26, #0x40 + EPILOG_RESTORE_REG_PAIR x27, x28, #0x50 + EPILOG_RESTORE_REG_PAIR fp, lr, #0x60! + MEND + +;; ----------------------------------------------------------------------------- +;; Macro used to restore preserved general purpose and FP registers from REGDISPLAY +;; $regdisplayReg - register pointing to the REGDISPLAY structure + MACRO + RESTORE_PRESERVED_REGISTERS $regdisplayReg + + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX19] + ldr x19, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX20] + ldr x20, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX21] + ldr x21, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX22] + ldr x22, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX23] + ldr x23, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX24] + ldr x24, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX25] + ldr x25, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX26] + ldr x26, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX27] + ldr x27, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX28] + ldr x28, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pFP] + ldr fp, [x12] + ;; + ;; load FP preserved regs + ;; + add x12, $regdisplayReg, #OFFSETOF__REGDISPLAY__D + ldp d8, d9, [x12, #0x00] + ldp d10, d11, [x12, #0x10] + ldp d12, d13, [x12, #0x20] + ldp d14, d15, [x12, #0x30] + MEND + +;; ----------------------------------------------------------------------------- +;; Macro used to save preserved general purpose and FP registers to REGDISPLAY +;; $regdisplayReg - register pointing to the REGDISPLAY structure + MACRO + SAVE_PRESERVED_REGISTERS $regdisplayReg + + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX19] + str x19, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX20] + str x20, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX21] + str x21, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX22] + str x22, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX23] + str x23, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX24] + str x24, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX25] + str x25, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX26] + str x26, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX27] + str x27, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX28] + str x28, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pFP] + str fp, [x12] + ;; + ;; store vfp preserved regs + ;; + add x12, $regdisplayReg, #OFFSETOF__REGDISPLAY__D + stp d8, d9, [x12, #0x00] + stp d10, d11, [x12, #0x10] + stp d12, d13, [x12, #0x20] + stp d14, d15, [x12, #0x30] + MEND + +;; ----------------------------------------------------------------------------- +;; Macro used to thrash preserved general purpose registers in REGDISPLAY +;; to make sure nobody uses them +;; $regdisplayReg - register pointing to the REGDISPLAY structure + MACRO + TRASH_PRESERVED_REGISTERS_STORAGE $regdisplayReg + +#if 0 // def _DEBUG ;; @TODO: temporarily removed because trashing the frame pointer breaks the debugger + movz x3, #0xbaad, LSL #48 + movk x3, #0xdeed, LSL #32 + movk x3, #0xbaad, LSL #16 + movk x3, #0xdeed + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX19] + str x3, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX20] + str x3, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX21] + str x3, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX22] + str x3, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX23] + str x3, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX24] + str x3, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX25] + str x3, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX26] + str x3, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX27] + str x3, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pX28] + str x3, [x12] + ldr x12, [$regdisplayReg, #OFFSETOF__REGDISPLAY__pFP] + str x3, [x12] +#endif // _DEBUG + MEND + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpThrowHwEx +;; +;; INPUT: W0: exception code of fault +;; X1: faulting IP +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpThrowHwEx + +#define rsp_offsetof_ExInfo 0 +#define rsp_offsetof_Context STACKSIZEOF_ExInfo + + PROLOG_NOP mov w2, w0 ;; save exception code into x2 + PROLOG_NOP mov x0, sp ;; get SP of fault site + + PROLOG_NOP mov lr, x1 ;; set IP of fault site + + ALLOC_THROW_FRAME + + ; x0: SP of fault site + ; x1: IP of fault site + ; x2: exception code of fault + ; lr: IP of fault site (as a 'return address') + + mov w0, w2 ;; w0 <- exception code of fault + + ;; x2 = GetThread(), TRASHES x1 + INLINE_GETTHREAD x2, x1 + + add x1, sp, #rsp_offsetof_ExInfo ;; x1 <- ExInfo* + str xzr, [x1, #OFFSETOF__ExInfo__m_exception] ;; pExInfo->m_exception = null + mov w3, #1 + strb w3, [x1, #OFFSETOF__ExInfo__m_passNumber] ;; pExInfo->m_passNumber = 1 + mov w3, #0xFFFFFFFF + str w3, [x1, #OFFSETOF__ExInfo__m_idxCurClause] ;; pExInfo->m_idxCurClause = MaxTryRegionIdx + mov w3, #2 + strb w3, [x1, #OFFSETOF__ExInfo__m_kind] ;; pExInfo->m_kind = ExKind.HardwareFault + + ;; link the ExInfo into the thread's ExInfo chain + ldr x3, [x2, #OFFSETOF__Thread__m_pExInfoStackHead] + str x3, [x1, #OFFSETOF__ExInfo__m_pPrevExInfo] ;; pExInfo->m_pPrevExInfo = m_pExInfoStackHead + str x1, [x2, #OFFSETOF__Thread__m_pExInfoStackHead] ;; m_pExInfoStackHead = pExInfo + + ;; set the exception context field on the ExInfo + add x2, sp, #rsp_offsetof_Context ;; x2 <- PAL_LIMITED_CONTEXT* + str x2, [x1, #OFFSETOF__ExInfo__m_pExContext] ;; pExInfo->m_pExContext = pContext + + ;; w0: exception code + ;; x1: ExInfo* + bl RhThrowHwEx + + EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowHwEx2 + + ;; no return + EMIT_BREAKPOINT + + NESTED_END RhpThrowHwEx + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpThrowEx +;; +;; INPUT: X0: exception object +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpThrowEx + + ALLOC_THROW_FRAME + + ;; Compute and save SP at callsite. + add x1, sp, #(STACKSIZEOF_ExInfo + SIZEOF__PAL_LIMITED_CONTEXT) + str x1, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__SP)] + + ;; x2 = GetThread(), TRASHES x1 + INLINE_GETTHREAD x2, x1 + + ;; There is runtime C# code that can tail call to RhpThrowEx using a binder intrinsic. So the return + ;; address could have been hijacked when we were in that C# code and we must remove the hijack and + ;; reflect the correct return address in our exception context record. The other throw helpers don't + ;; need this because they cannot be tail-called from C#. + + ;; NOTE: we cannot use INLINE_THREAD_UNHIJACK because it will write into the stack at the location + ;; where the tail-calling thread had saved LR, which may not match where we have saved LR. + + ldr x1, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + cbz x1, NotHijacked + + ldr x3, [x2, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + + ;; x0: exception object + ;; x1: hijacked return address + ;; x2: pThread + ;; x3: hijacked return address location + + add x12, sp, #(STACKSIZEOF_ExInfo + SIZEOF__PAL_LIMITED_CONTEXT) ;; re-compute SP at callsite + cmp x3, x12 ;; if (m_ppvHijackedReturnAddressLocation < SP at callsite) + blo TailCallWasHijacked + + ;; normal case where a valid return address location is hijacked + str x1, [x3] + b ClearThreadState + +TailCallWasHijacked + + ;; Abnormal case where the return address location is now invalid because we ended up here via a tail + ;; call. In this case, our hijacked return address should be the correct caller of this method. + ;; + + ;; stick the previous return address in LR as well as in the right spots in our PAL_LIMITED_CONTEXT. + mov lr, x1 + str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__LR)] + str lr, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__IP)] + +ClearThreadState + + ;; clear the Thread's hijack state + str xzr, [x2, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + str xzr, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + +NotHijacked + + add x1, sp, #rsp_offsetof_ExInfo ;; x1 <- ExInfo* + str xzr, [x1, #OFFSETOF__ExInfo__m_exception] ;; pExInfo->m_exception = null + mov w3, #1 + strb w3, [x1, #OFFSETOF__ExInfo__m_passNumber] ;; pExInfo->m_passNumber = 1 + mov w3, #0xFFFFFFFF + str w3, [x1, #OFFSETOF__ExInfo__m_idxCurClause] ;; pExInfo->m_idxCurClause = MaxTryRegionIdx + mov w3, #1 + strb w3, [x1, #OFFSETOF__ExInfo__m_kind] ;; pExInfo->m_kind = ExKind.Throw + + ;; link the ExInfo into the thread's ExInfo chain + ldr x3, [x2, #OFFSETOF__Thread__m_pExInfoStackHead] + str x3, [x1, #OFFSETOF__ExInfo__m_pPrevExInfo] ;; pExInfo->m_pPrevExInfo = m_pExInfoStackHead + str x1, [x2, #OFFSETOF__Thread__m_pExInfoStackHead] ;; m_pExInfoStackHead = pExInfo + + ;; set the exception context field on the ExInfo + add x2, sp, #rsp_offsetof_Context ;; x2 <- PAL_LIMITED_CONTEXT* + str x2, [x1, #OFFSETOF__ExInfo__m_pExContext] ;; pExInfo->m_pExContext = pContext + + ;; x0: exception object + ;; x1: ExInfo* + bl RhThrowEx + + EXPORT_POINTER_TO_ADDRESS PointerToRhpThrowEx2 + + ;; no return + EMIT_BREAKPOINT + NESTED_END RhpThrowEx + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void FASTCALL RhpRethrow() +;; +;; SUMMARY: Similar to RhpThrowEx, except that it passes along the currently active ExInfo +;; +;; INPUT: +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpRethrow + + ALLOC_THROW_FRAME + + ;; Compute and save SP at callsite. + add x1, sp, #(STACKSIZEOF_ExInfo + SIZEOF__PAL_LIMITED_CONTEXT) + str x1, [sp, #(rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__SP)] + + ;; x2 = GetThread(), TRASHES x1 + INLINE_GETTHREAD x2, x1 + + add x1, sp, #rsp_offsetof_ExInfo ;; x1 <- ExInfo* + str xzr, [x1, #OFFSETOF__ExInfo__m_exception] ;; pExInfo->m_exception = null + mov w3, #1 + strb w3, [x1, #OFFSETOF__ExInfo__m_passNumber] ;; pExInfo->m_passNumber = 1 + mov w3, #0xFFFFFFFF + str w3, [x1, #OFFSETOF__ExInfo__m_idxCurClause] ;; pExInfo->m_idxCurClause = MaxTryRegionIdx + + ;; link the ExInfo into the thread's ExInfo chain + ldr x3, [x2, #OFFSETOF__Thread__m_pExInfoStackHead] + mov x0, x3 ;; x0 <- current ExInfo + str x3, [x1, #OFFSETOF__ExInfo__m_pPrevExInfo] ;; pExInfo->m_pPrevExInfo = m_pExInfoStackHead + str x1, [x2, #OFFSETOF__Thread__m_pExInfoStackHead] ;; m_pExInfoStackHead = pExInfo + + ;; set the exception context field on the ExInfo + add x2, sp, #rsp_offsetof_Context ;; x2 <- PAL_LIMITED_CONTEXT* + str x2, [x1, #OFFSETOF__ExInfo__m_pExContext] ;; pExInfo->m_pExContext = pContext + + ;; x0 contains the currently active ExInfo + ;; x1 contains the address of the new ExInfo + bl RhRethrow + + EXPORT_POINTER_TO_ADDRESS PointerToRhpRethrow2 + + ;; no return + EMIT_BREAKPOINT + NESTED_END RhpRethrow + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* FASTCALL RhpCallCatchFunclet(RtuObjectRef exceptionObj, void* pHandlerIP, REGDISPLAY* pRegDisplay, +;; ExInfo* pExInfo) +;; +;; INPUT: X0: exception object +;; X1: handler funclet address +;; X2: REGDISPLAY* +;; X3: ExInfo* +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpCallCatchFunclet + + ALLOC_CALL_FUNCLET_FRAME 0x60 + stp d8, d9, [sp, #0x00] + stp d10, d11, [sp, #0x10] + stp d12, d13, [sp, #0x20] + stp d14, d15, [sp, #0x30] + stp x0, x2, [sp, #0x40] ;; x0, x2 & x3 are saved so we have the exception object, REGDISPLAY and + stp x3, xzr, [sp, #0x50] ;; ExInfo later, xzr makes space for the local "is_not_handling_thread_abort" + +#define rsp_offset_is_not_handling_thread_abort 0x58 +#define rsp_offset_x2 0x48 +#define rsp_offset_x3 0x50 + + ;; + ;; clear the DoNotTriggerGc flag, trashes x4-x6 + ;; + INLINE_GETTHREAD x5, x6 ;; x5 <- Thread*, x6 <- trashed + + ldr x4, [x5, #OFFSETOF__Thread__m_threadAbortException] + sub x4, x4, x0 + str x4, [sp, #rsp_offset_is_not_handling_thread_abort] ;; Non-zero if the exception is not ThreadAbortException + + add x12, x5, #OFFSETOF__Thread__m_ThreadStateFlags + +ClearRetry_Catch + ldxr w4, [x12] + bic w4, w4, #TSF_DoNotTriggerGc + stxr w6, w4, [x12] + cbz w6, ClearSuccess_Catch + b ClearRetry_Catch +ClearSuccess_Catch + + ;; + ;; set preserved regs to the values expected by the funclet + ;; + RESTORE_PRESERVED_REGISTERS x2 + ;; + ;; trash the values at the old homes to make sure nobody uses them + ;; + TRASH_PRESERVED_REGISTERS_STORAGE x2 + + ;; + ;; call the funclet + ;; + ;; x0 still contains the exception object + blr x1 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallCatchFunclet2 + + ;; x0 contains resume IP + + ldr x2, [sp, #rsp_offset_x2] ;; x2 <- REGDISPLAY* + +;; @TODO: add debug-only validation code for ExInfo pop + + INLINE_GETTHREAD x1, x3 ;; x1 <- Thread*, x3 <- trashed + + ;; We must unhijack the thread at this point because the section of stack where the hijack is applied + ;; may go dead. If it does, then the next time we try to unhijack the thread, it will corrupt the stack. + INLINE_THREAD_UNHIJACK x1, x3, x12 ;; Thread in x1, trashes x3 and x12 + + ldr x3, [sp, #rsp_offset_x3] ;; x3 <- current ExInfo* + ldr x2, [x2, #OFFSETOF__REGDISPLAY__SP] ;; x2 <- resume SP value + +PopExInfoLoop + ldr x3, [x3, #OFFSETOF__ExInfo__m_pPrevExInfo] ;; x3 <- next ExInfo + cbz x3, DonePopping ;; if (pExInfo == null) { we're done } + cmp x3, x2 + blt PopExInfoLoop ;; if (pExInfo < resume SP} { keep going } + +DonePopping + str x3, [x1, #OFFSETOF__Thread__m_pExInfoStackHead] ;; store the new head on the Thread + + ldr x3, =RhpTrapThreads + ldr w3, [x3] + tbz x3, #TrapThreadsFlags_AbortInProgress_Bit, NoAbort + + ldr x3, [sp, #rsp_offset_is_not_handling_thread_abort] + cbnz x3, NoAbort + + ;; It was the ThreadAbortException, so rethrow it + ;; reset SP + mov x1, x0 ;; x1 <- continuation address as exception PC + mov w0, #STATUS_REDHAWK_THREAD_ABORT + mov sp, x2 + b RhpThrowHwEx + +NoAbort + ;; reset SP and jump to continuation address + mov sp, x2 + ret x0 + + NESTED_END RhpCallCatchFunclet + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void FASTCALL RhpCallFinallyFunclet(void* pHandlerIP, REGDISPLAY* pRegDisplay) +;; +;; INPUT: X0: handler funclet address +;; X1: REGDISPLAY* +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpCallFinallyFunclet + + ALLOC_CALL_FUNCLET_FRAME 0x50 + stp d8, d9, [sp, #0x00] + stp d10, d11, [sp, #0x10] + stp d12, d13, [sp, #0x20] + stp d14, d15, [sp, #0x30] + stp x0, x1, [sp, #0x40] ;; x1 is saved so we have the REGDISPLAY later, x0 is just alignment padding + +#define rsp_offset_x1 0x48 + + ;; + ;; We want to suppress hijacking between invocations of subsequent finallys. We do this because we + ;; cannot tolerate a GC after one finally has run (and possibly side-effected the GC state of the + ;; method) and then been popped off the stack, leaving behind no trace of its effect. + ;; + ;; So we clear the state before and set it after invocation of the handler. + ;; + + ;; + ;; clear the DoNotTriggerGc flag, trashes x2-x4 + ;; + INLINE_GETTHREAD x2, x3 ;; x2 <- Thread*, x3 <- trashed + + add x12, x2, #OFFSETOF__Thread__m_ThreadStateFlags + +ClearRetry + ldxr w4, [x12] + bic w4, w4, #TSF_DoNotTriggerGc + stxr w3, w4, [x12] + cbz w3, ClearSuccess + b ClearRetry +ClearSuccess + + ;; + ;; set preserved regs to the values expected by the funclet + ;; + RESTORE_PRESERVED_REGISTERS x1 + ;; + ;; trash the values at the old homes to make sure nobody uses them + ;; + TRASH_PRESERVED_REGISTERS_STORAGE x1 + + ;; + ;; call the funclet + ;; + blr x0 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFinallyFunclet2 + + ldr x1, [sp, #rsp_offset_x1] ;; reload REGDISPLAY pointer + + ;; + ;; save new values of preserved regs into REGDISPLAY + ;; + SAVE_PRESERVED_REGISTERS x1 + + ;; + ;; set the DoNotTriggerGc flag, trashes x1-x3 + ;; + INLINE_GETTHREAD x2, x3 ;; x2 <- Thread*, x3 <- trashed + + add x12, x2, #OFFSETOF__Thread__m_ThreadStateFlags +SetRetry + ldxr w1, [x12] + orr w1, w1, #TSF_DoNotTriggerGc + stxr w3, w1, [x12] + cbz w3, SetSuccess + b SetRetry +SetSuccess + + ldp d8, d9, [sp, #0x00] + ldp d10, d11, [sp, #0x10] + ldp d12, d13, [sp, #0x20] + ldp d14, d15, [sp, #0x30] + + FREE_CALL_FUNCLET_FRAME 0x50 + EPILOG_RETURN + + NESTED_END RhpCallFinallyFunclet + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* FASTCALL RhpCallFilterFunclet(RtuObjectRef exceptionObj, void* pFilterIP, REGDISPLAY* pRegDisplay) +;; +;; INPUT: X0: exception object +;; X1: filter funclet address +;; X2: REGDISPLAY* +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpCallFilterFunclet + ALLOC_CALL_FUNCLET_FRAME 0 + + ldr x12, [x2, #OFFSETOF__REGDISPLAY__pFP] + ldr fp, [x12] + + ;; + ;; call the funclet + ;; + ;; x0 still contains the exception object + blr x1 + + EXPORT_POINTER_TO_ADDRESS PointerToRhpCallFilterFunclet2 + + FREE_CALL_FUNCLET_FRAME 0 + EPILOG_RETURN + + NESTED_END RhpCallFilterFunclet + + INLINE_GETTHREAD_CONSTANT_POOL + + end diff --git a/external/corert/src/Native/Runtime/arm64/GcProbe.asm b/external/corert/src/Native/Runtime/arm64/GcProbe.asm new file mode 100644 index 0000000000..a6ee43c566 --- /dev/null +++ b/external/corert/src/Native/Runtime/arm64/GcProbe.asm @@ -0,0 +1,883 @@ +;; 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. + +#include "AsmMacros.h" + + TEXTAREA + + SETALIAS GetLoopIndirCells, ?GetLoopIndirCells@ModuleHeader@@QEAAPEAEXZ + SETALIAS g_pTheRuntimeInstance, ?g_pTheRuntimeInstance@@3PEAVRuntimeInstance@@EA + SETALIAS RuntimeInstance__ShouldHijackLoopForGcStress, ?ShouldHijackLoopForGcStress@RuntimeInstance@@QEAA_N_K@Z + + EXTERN g_fGcStressStarted + + EXTERN $g_pTheRuntimeInstance + EXTERN $RuntimeInstance__ShouldHijackLoopForGcStress + EXTERN $GetLoopIndirCells + EXTERN RecoverLoopHijackTarget + +PROBE_SAVE_FLAGS_EVERYTHING equ DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_ALL_SCRATCH + + ;; Build a map of symbols representing offsets into the transition frame (see PInvokeTransitionFrame in + ;; rhbinder.h and keep these two in sync. + map 0 + field OFFSETOF__PInvokeTransitionFrame__m_PreservedRegs + field 10 * 8 ; x19..x28 +m_CallersSP field 8 ; SP at routine entry + field 19 * 8 ; x0..x18 + field 8 ; lr +m_SavedNZCV field 8 ; Saved condition flags + field 4 * 8 ; d0..d3 +PROBE_FRAME_SIZE field 0 + + ;; Support for setting up a transition frame when performing a GC probe. In many respects this is very + ;; similar to the logic in PUSH_COOP_PINVOKE_FRAME in AsmMacros.h. In most cases setting up the + ;; transition frame comprises the entirety of the caller's prolog (and initial non-prolog code) and + ;; similarly for the epilog. Those cases can be dealt with using PROLOG_PROBE_FRAME and EPILOG_PROBE_FRAME + ;; defined below. For the special cases where additional work has to be done in the prolog we also provide + ;; the lower level macros ALLOC_PROBE_FRAME, FREE_PROBE_FRAME and INIT_PROBE_FRAME that allow more control + ;; to be asserted. + ;; + ;; Note that we currently employ a significant simplification of frame setup: we always allocate a + ;; maximally-sized PInvokeTransitionFrame and save all of the registers. Depending on the caller this can + ;; lead to upto 20 additional register saves (x0-x18, lr) or 160 bytes of stack space. I have done no + ;; analysis to see whether any of the worst cases occur on performance sensitive paths and whether the + ;; additional saves will show any measurable degradation. + + ;; Perform the parts of setting up a probe frame that can occur during the prolog (and indeed this macro + ;; can only be called from within the prolog). + MACRO + ALLOC_PROBE_FRAME $extraStackSpace + + ;; First create PInvokeTransitionFrame + PROLOG_SAVE_REG_PAIR fp, lr, #-(PROBE_FRAME_SIZE + $extraStackSpace)! ;; Push down stack pointer and store FP and LR + + ;; Slot at [sp, #0x10] is reserved for Thread * + ;; Slot at [sp, #0x18] is reserved for bitmask of saved registers + + ;; Save callee saved registers + PROLOG_SAVE_REG_PAIR x19, x20, #0x20 + PROLOG_SAVE_REG_PAIR x21, x22, #0x30 + PROLOG_SAVE_REG_PAIR x23, x24, #0x40 + PROLOG_SAVE_REG_PAIR x25, x26, #0x50 + PROLOG_SAVE_REG_PAIR x27, x28, #0x60 + + ;; Slot at [sp, #0x70] is reserved for caller sp + + ;; Save the scratch registers + PROLOG_NOP str x0, [sp, #0x78] + PROLOG_NOP stp x1, x2, [sp, #0x80] + PROLOG_NOP stp x3, x4, [sp, #0x90] + PROLOG_NOP stp x5, x6, [sp, #0xA0] + PROLOG_NOP stp x7, x8, [sp, #0xB0] + PROLOG_NOP stp x9, x10, [sp, #0xC0] + PROLOG_NOP stp x11, x12, [sp, #0xD0] + PROLOG_NOP stp x13, x14, [sp, #0xE0] + PROLOG_NOP stp x15, x16, [sp, #0xF0] + PROLOG_NOP stp x17, x18, [sp, #0x100] + PROLOG_NOP str lr, [sp, #0x110] + + ;; Slot at [sp, #0x118] is reserved for NZCV + + ; Save the floating return registers + PROLOG_NOP stp d0, d1, [sp, #0x120] + PROLOG_NOP stp d2, d3, [sp, #0x130] + + MEND + + ;; Undo the effects of an ALLOC_PROBE_FRAME. This may only be called within an epilog. Note that all + ;; registers are restored (apart for sp and pc), even volatiles. + MACRO + FREE_PROBE_FRAME $extraStackSpace + + ;; Restore the scratch registers + PROLOG_NOP ldr x0, [sp, #0x78] + PROLOG_NOP ldp x1, x2, [sp, #0x80] + PROLOG_NOP ldp x3, x4, [sp, #0x90] + PROLOG_NOP ldp x5, x6, [sp, #0xA0] + PROLOG_NOP ldp x7, x8, [sp, #0xB0] + PROLOG_NOP ldp x9, x10, [sp, #0xC0] + PROLOG_NOP ldp x11, x12, [sp, #0xD0] + PROLOG_NOP ldp x13, x14, [sp, #0xE0] + PROLOG_NOP ldp x15, x16, [sp, #0xF0] + PROLOG_NOP ldp x17, x18, [sp, #0x100] + PROLOG_NOP ldr lr, [sp, #0x110] + + ; Restore the floating return registers + EPILOG_NOP ldp d0, d1, [sp, #0x120] + EPILOG_NOP ldp d2, d3, [sp, #0x130] + + ;; Resttore callee saved registers + EPILOG_RESTORE_REG_PAIR x19, x20, #0x20 + EPILOG_RESTORE_REG_PAIR x21, x22, #0x30 + EPILOG_RESTORE_REG_PAIR x23, x24, #0x40 + EPILOG_RESTORE_REG_PAIR x25, x26, #0x50 + EPILOG_RESTORE_REG_PAIR x27, x28, #0x60 + + EPILOG_RESTORE_REG_PAIR fp, lr, #(PROBE_FRAME_SIZE + $extraStackSpace)! + MEND + + ;; Complete the setup of a probe frame allocated with ALLOC_PROBE_FRAME with the initialization that can + ;; occur only outside the prolog (includes linking the frame to the current Thread). This macro assumes SP + ;; is invariant outside of the prolog. + ;; + ;; $threadReg : register containing the Thread* (this will be preserved) + ;; $trashReg : register that can be trashed by this macro + ;; $savedRegsMask : value to initialize m_Flags field with (register or #constant) + ;; $gcFlags : value of gcref / gcbyref flags for saved registers, used only if $savedRegsMask is constant + ;; $frameSize : total size of the method's stack frame (including probe frame size) + MACRO + INIT_PROBE_FRAME $threadReg, $trashReg, $savedRegsMask, $gcFlags, $frameSize + + LCLS BitmaskStr +BitmaskStr SETS "$savedRegsMask" + + str $threadReg, [sp, #OFFSETOF__PInvokeTransitionFrame__m_pThread] ; Thread * + IF BitmaskStr:LEFT:1 == "#" + ;; The savedRegsMask is a constant, remove the leading "#" since the MOVL64 doesn't expect it +BitmaskStr SETS BitmaskStr:RIGHT:(:LEN:BitmaskStr - 1) + MOVL64 $trashReg, $BitmaskStr, $gcFlags + ELSE + ;; The savedRegsMask is a register + mov $trashReg, $savedRegsMask + ENDIF + str $trashReg, [sp, #OFFSETOF__PInvokeTransitionFrame__m_Flags] + add $trashReg, sp, #$frameSize + str $trashReg, [sp, #m_CallersSP] + MEND + + ;; Simple macro to use when setting up the probe frame can comprise the entire prolog. Call this macro + ;; first in the method (no further prolog instructions can be added after this). + ;; + ;; $threadReg : register containing the Thread* (this will be preserved). If defaulted (specify |) then + ;; the current thread will be calculated inline into r2 ($trashReg must not equal r2 in + ;; this case) + ;; $trashReg : register that can be trashed by this macro + ;; $savedRegsMask : value to initialize m_dwFlags field with (register or #constant) + ;; $gcFlags : value of gcref / gcbyref flags for saved registers, used only if $savedRegsMask is constant + MACRO + PROLOG_PROBE_FRAME $threadReg, $trashReg, $savedRegsMask, $gcFlags + + ; Local string tracking the name of the register in which the Thread* is kept. Defaults to the value + ; of $threadReg. + LCLS __PPF_ThreadReg +__PPF_ThreadReg SETS "$threadReg" + + ; Define the method prolog, allocating enough stack space for the PInvokeTransitionFrame and saving + ; incoming register values into it. + ALLOC_PROBE_FRAME 0 + + ; If the caller didn't provide a value for $threadReg then generate code to fetch the Thread* into x2. + ; Record that x2 holds the Thread* in our local variable. + IF "$threadReg" == "" + ASSERT "$trashReg" != "x2" +__PPF_ThreadReg SETS "x2" + INLINE_GETTHREAD $__PPF_ThreadReg, $trashReg + ENDIF + + ; Perform the rest of the PInvokeTransitionFrame initialization. + INIT_PROBE_FRAME $__PPF_ThreadReg, $trashReg, $savedRegsMask, $gcFlags, PROBE_FRAME_SIZE + add $trashReg, sp, xzr + str $trashReg, [$__PPF_ThreadReg, #OFFSETOF__Thread__m_pHackPInvokeTunnel] + MEND + + ; Simple macro to use when PROLOG_PROBE_FRAME was used to set up and initialize the prolog and + ; PInvokeTransitionFrame. This will define the epilog including a return via the restored LR. + MACRO + EPILOG_PROBE_FRAME + + FREE_PROBE_FRAME 0 + EPILOG_RETURN + MEND + +;; ALLOC_PROBE_FRAME will save the first 4 vfp registers, in order to avoid trashing VFP registers across the loop +;; hijack, we must save the rest -- d4-d31 (28). +EXTRA_SAVE_SIZE equ (28*8) + + MACRO + ALLOC_LOOP_HIJACK_FRAME + + PROLOG_STACK_ALLOC EXTRA_SAVE_SIZE + +;; save VFP registers that were not saved by the ALLOC_PROBE_FRAME + PROLOG_NOP stp d4, d5, [sp] + PROLOG_NOP stp d6, d7, [sp, #0x10] + PROLOG_NOP stp d8, d9, [sp, #0x20] + PROLOG_NOP stp d10, d11, [sp, #0x30] + PROLOG_NOP stp d12, d13, [sp, #0x40] + PROLOG_NOP stp d14, d15, [sp, #0x50] + PROLOG_NOP stp d16, d17, [sp, #0x60] + PROLOG_NOP stp d18, d19, [sp, #0x70] + PROLOG_NOP stp d20, d21, [sp, #0x80] + PROLOG_NOP stp d22, d23, [sp, #0x90] + PROLOG_NOP stp d24, d25, [sp, #0xA0] + PROLOG_NOP stp d26, d27, [sp, #0xB0] + PROLOG_NOP stp d28, d29, [sp, #0xC0] + PROLOG_NOP stp d30, d31, [sp, #0xD0] + + ALLOC_PROBE_FRAME 0 + MEND + + MACRO + FREE_LOOP_HIJACK_FRAME + + FREE_PROBE_FRAME 0 + +;; restore VFP registers that will not be restored by the FREE_PROBE_FRAME + PROLOG_NOP ldp d4, d5, [sp] + PROLOG_NOP ldp d6, d7, [sp, #0x10] + PROLOG_NOP ldp d8, d9, [sp, #0x20] + PROLOG_NOP ldp d10, d11, [sp, #0x30] + PROLOG_NOP ldp d12, d13, [sp, #0x40] + PROLOG_NOP ldp d14, d15, [sp, #0x50] + PROLOG_NOP ldp d16, d17, [sp, #0x60] + PROLOG_NOP ldp d18, d19, [sp, #0x70] + PROLOG_NOP ldp d20, d21, [sp, #0x80] + PROLOG_NOP ldp d22, d23, [sp, #0x90] + PROLOG_NOP ldp d24, d25, [sp, #0xA0] + PROLOG_NOP ldp d26, d27, [sp, #0xB0] + PROLOG_NOP ldp d28, d29, [sp, #0xC0] + PROLOG_NOP ldp d30, d31, [sp, #0xD0] + + EPILOG_STACK_FREE EXTRA_SAVE_SIZE + MEND + +;; +;; Macro to clear the hijack state. This is safe to do because the suspension code will not Unhijack this +;; thread if it finds it at an IP that isn't managed code. +;; +;; Register state on entry: +;; x2: thread pointer +;; +;; Register state on exit: +;; + MACRO + ClearHijackState + + str xzr, [x2, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + str xzr, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + MEND + +;; +;; The prolog for all GC suspension hijacks (normal and stress). Fixes up the hijacked return address, and +;; clears the hijack state. +;; +;; Register state on entry: +;; All registers correct for return to the original return address. +;; +;; Register state on exit: +;; x2: thread pointer +;; x3: trashed +;; + MACRO + FixupHijackedCallstack + + ;; x2 <- GetThread(), TRASHES x3 + INLINE_GETTHREAD x2, x3 + + ;; + ;; Fix the stack by restoring the original return address + ;; + ldr lr, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + + ClearHijackState + MEND + +;; +;; Set the Thread state and wait for a GC to complete. +;; +;; Register state on entry: +;; x4: thread pointer +;; +;; Register state on exit: +;; x4: thread pointer +;; All other registers trashed +;; + + EXTERN RhpWaitForGCNoAbort + + MACRO + WaitForGCCompletion + + ldr w2, [x4, #OFFSETOF__Thread__m_ThreadStateFlags] + tst w2, #TSF_SuppressGcStress__OR__TSF_DoNotTriggerGC + bne %ft0 + + ldr x2, [x4, #OFFSETOF__Thread__m_pHackPInvokeTunnel] + bl RhpWaitForGCNoAbort +0 + MEND + + MACRO + HijackTargetFakeProlog + + ;; This is a fake entrypoint for the method that 'tricks' the OS into calling our personality routine. + ;; The code here should never be executed, and the unwind info is bogus, but we don't mind since the + ;; stack is broken by the hijack anyway until after we fix it below. + PROLOG_SAVE_REG_PAIR fp, lr, #-0x10! + nop ; We also need a nop here to simulate the implied bl instruction. Without + ; this, an OS-applied -4 will back up into the method prolog and the unwind + ; will not be applied as desired. + + MEND + +;; +;; +;; +;; GC Probe Hijack targets +;; +;; + EXTERN RhpPInvokeExceptionGuard + + + NESTED_ENTRY RhpGcProbeHijackScalarWrapper, .text, RhpPInvokeExceptionGuard + brk 0xf000 ;; TODO: remove after debugging/testing stub + HijackTargetFakeProlog + + LABELED_RETURN_ADDRESS RhpGcProbeHijackScalar + + FixupHijackedCallstack + MOVL64 x12, DEFAULT_FRAME_SAVE_FLAGS, 0 + b RhpGcProbe + NESTED_END RhpGcProbeHijackScalarWrapper + + NESTED_ENTRY RhpGcProbeHijackObjectWrapper, .text, RhpPInvokeExceptionGuard + brk 0xf000 ;; TODO: remove after debugging/testing stub + HijackTargetFakeProlog + + LABELED_RETURN_ADDRESS RhpGcProbeHijackObject + + FixupHijackedCallstack + MOVL64 x12, (DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_X0), PTFF_X0_IS_GCREF_HI + b RhpGcProbe + NESTED_END RhpGcProbeHijackObjectWrapper + + NESTED_ENTRY RhpGcProbeHijackByrefWrapper, .text, RhpPInvokeExceptionGuard + brk 0xf000 ;; TODO: remove after debugging/testing stub + HijackTargetFakeProlog + + LABELED_RETURN_ADDRESS RhpGcProbeHijackByref + + FixupHijackedCallstack + MOVL64 x12, (DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_X0) , PTFF_X0_IS_BYREF_HI + b RhpGcProbe + NESTED_END RhpGcProbeHijackByrefWrapper + +#ifdef FEATURE_GC_STRESS +;; +;; +;; GC Stress Hijack targets +;; +;; + LEAF_ENTRY RhpGcStressHijackScalar + FixupHijackedCallstack + MOVL64 x12, DEFAULT_FRAME_SAVE_FLAGS, 0 + b RhpGcStressProbe + LEAF_END RhpGcStressHijackScalar + + LEAF_ENTRY RhpGcStressHijackObject + FixupHijackedCallstack + MOVL64 x12, (DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_X0), PTFF_X0_IS_GCREF_HI + b RhpGcStressProbe + LEAF_END RhpGcStressHijackObject + + LEAF_ENTRY RhpGcStressHijackByref + FixupHijackedCallstack + MOVL64 x12, (DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_X0), PTFF_X0_IS_BYREF_HI + b RhpGcStressProbe + LEAF_END RhpGcStressHijackByref + + +;; +;; Worker for our GC stress probes. Do not call directly!! +;; Instead, go through RhpGcStressHijack{Scalar|Object|Byref}. +;; This worker performs the GC Stress work and returns to the original return address. +;; +;; Register state on entry: +;; x0: hijacked function return value +;; x1: hijacked function return value +;; x2: thread pointer +;; w12: register bitmask +;; +;; Register state on exit: +;; Scratch registers, except for x0, have been trashed +;; All other registers restored as they were when the hijack was first reached. +;; + NESTED_ENTRY RhpGcStressProbe + PROLOG_PROBE_FRAME x2, x3, x12, + + bl $REDHAWKGCINTERFACE__STRESSGC + + EPILOG_PROBE_FRAME + NESTED_END RhpGcStressProbe +#endif ;; FEATURE_GC_STRESS + + LEAF_ENTRY RhpGcProbe + brk 0xf000 ;; TODO: remove after debugging/testing stub + ldr x3, =RhpTrapThreads + ldr w3, [x3] + tbnz x3, #TrapThreadsFlags_TrapThreads_Bit, RhpGcProbeRare + ret + LEAF_END RhpGcProbe + + EXTERN RhpThrowHwEx + + NESTED_ENTRY RhpGcProbeRare + brk 0xf000 ;; TODO: remove after debugging/testing stub + PROLOG_PROBE_FRAME x2, x3, x12, + + mov x4, x2 + WaitForGCCompletion + + ldr x2, [sp, #OFFSETOF__PInvokeTransitionFrame__m_Flags] + tbnz x2, #PTFF_THREAD_ABORT_BIT, %F1 + + EPILOG_PROBE_FRAME + +1 + FREE_PROBE_FRAME 0 + EPILOG_NOP mov w0, #STATUS_REDHAWK_THREAD_ABORT + EPILOG_NOP mov x1, lr ;; return address as exception PC + EPILOG_NOP b RhpThrowHwEx + NESTED_END RhpGcProbeRare + + LEAF_ENTRY RhpGcPoll + brk 0xf000 ;; TODO: remove after debugging/testing stub + ; @todo: I'm assuming it's not OK to trash any register here. If that's not true we can optimize the + ; push/pops out of this fast path. + str x0, [sp], #-0x10! + ldr x0, =RhpTrapThreads + ldr w0, [x0] + tbnz x0, #TrapThreadsFlags_TrapThreads_Bit, %F0 + ldr x0, [sp], #0x10! + ret +0 + ldr x0, [sp], #0x10! + b RhpGcPollRare + LEAF_END RhpGcPoll + + NESTED_ENTRY RhpGcPollRare + brk 0xf000 ;; TODO: remove after debugging/testing stub + PROLOG_PROBE_FRAME |, x3, #PROBE_SAVE_FLAGS_EVERYTHING, 0 + + ; Unhijack this thread, if necessary. + INLINE_THREAD_UNHIJACK x2, x0, x1 ;; trashes x0, x1 + + mov x4, x2 + WaitForGCCompletion + + EPILOG_PROBE_FRAME + NESTED_END RhpGcPollRare + + LEAF_ENTRY RhpGcPollStress + ; + ; loop hijacking is used instead + ; + brk 0xf000 + + LEAF_END RhpGcPollStress + + +#ifdef FEATURE_GC_STRESS + NESTED_ENTRY RhpHijackForGcStress + ;; This function should be called from right before epilog + + ;; Push FP and LR, and allocate stack to hold PAL_LIMITED_CONTEXT structure and VFP return value registers + PROLOG_SAVE_REG_PAIR fp, lr, #-(SIZEOF__PAL_LIMITED_CONTEXT + 0x20)! + + ;; + ;; Setup a PAL_LIMITED_CONTEXT that looks like what you'd get if you had suspended this thread at the + ;; IP after the call to this helper. + ;; + ;; This is very likely overkill since the calculation of the return address should only need SP and + ;; LR, but this is test code, so I'm not too worried about efficiency. + ;; + ;; Setup a PAL_LIMITED_CONTEXT on the stack + ;; { + ;; FP and LR already pushed. + PROLOG_NOP stp x0, x1, [sp, #0x10] + PROLOG_SAVE_REG_PAIR x19, x20, #0x20 + PROLOG_SAVE_REG_PAIR x21, x22, #0x30 + PROLOG_SAVE_REG_PAIR x23, x24, #0x40 + PROLOG_SAVE_REG_PAIR x25, x26, #0x50 + PROLOG_SAVE_REG_PAIR x27, x28, #0x60 + PROLOG_SAVE_REG lr, #0x78 + + ;; } end PAL_LIMITED_CONTEXT + + ;; Save VFP return value + stp d0, d1, [sp, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x00)] + stp d2, d3, [sp, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x10)] + + ;; Compute and save SP at callsite. + add x0, sp, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x20) ;; +0x20 for the pushes right before the context struct + str x0, [sp, #OFFSETOF__PAL_LIMITED_CONTEXT__SP] + + mov x0, sp ; Address of PAL_LIMITED_CONTEXT + bl $THREAD__HIJACKFORGCSTRESS + + ;; Restore return value registers (saved in PAL_LIMITED_CONTEXT structure) + ldp x0, x1, [sp, #0x10] + + ;; Restore VFP return value + ldp d0, d1, [sp, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x00)] + ldp d2, d3, [sp, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x10)] + + ;; Epilog + EPILOG_RESTORE_REG_PAIR x19, x20, #0x20 + EPILOG_RESTORE_REG_PAIR x21, x22, #0x30 + EPILOG_RESTORE_REG_PAIR x23, x24, #0x40 + EPILOG_RESTORE_REG_PAIR x25, x26, #0x50 + EPILOG_RESTORE_REG_PAIR x27, x28, #0x60 + EPILOG_RESTORE_REG_PAIR fp, lr, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x20)! + EPILOG_RETURN + + NESTED_END RhpHijackForGcStress + + NESTED_ENTRY RhpHijackForGcStressLeaf + ;; This should be jumped to, right before epilog + ;; x9 has the return address (we don't care about trashing scratch regs at this point) + + ;; Push FP and LR, and allocate stack to hold PAL_LIMITED_CONTEXT structure and VFP return value registers + PROLOG_SAVE_REG_PAIR fp, lr, #-(SIZEOF__PAL_LIMITED_CONTEXT + 0x20)! + + ;; + ;; Setup a PAL_LIMITED_CONTEXT that looks like what you'd get if you had suspended this thread at the + ;; IP after the call to this helper. + ;; + ;; This is very likely overkill since the calculation of the return address should only need SP and + ;; LR, but this is test code, so I'm not too worried about efficiency. + ;; + ;; Setup a PAL_LIMITED_CONTEXT on the stack + ;; { + ;; FP and LR already pushed. + PROLOG_NOP stp x0, x1, [sp, #0x10] + PROLOG_SAVE_REG_PAIR x19, x20, #0x20 + PROLOG_SAVE_REG_PAIR x21, x22, #0x30 + PROLOG_SAVE_REG_PAIR x23, x24, #0x40 + PROLOG_SAVE_REG_PAIR x25, x26, #0x50 + PROLOG_SAVE_REG_PAIR x27, x28, #0x60 + ; PROLOG_SAVE_REG macro doesn't let to use scratch reg: + PROLOG_NOP str x9, [sp, #0x78] ; this is return address from RhpHijackForGcStress; lr is return address for it's caller + + ;; } end PAL_LIMITED_CONTEXT + + ;; Save VFP return value + stp d0, d1, [sp, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x00)] + stp d2, d3, [sp, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x10)] + + ;; Compute and save SP at callsite. + add x0, sp, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x20) ;; +0x20 for the pushes right before the context struct + str x0, [sp, #OFFSETOF__PAL_LIMITED_CONTEXT__SP] + + mov x0, sp ; Address of PAL_LIMITED_CONTEXT + bl $THREAD__HIJACKFORGCSTRESS + + ;; Restore return value registers (saved in PAL_LIMITED_CONTEXT structure) + ldp x0, x1, [sp, #0x10] + + ;; Restore VFP return value + ldp d0, d1, [sp, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x00)] + ldp d2, d3, [sp, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x10)] + + ;; Epilog + EPILOG_RESTORE_REG_PAIR x19, x20, #0x20 + EPILOG_RESTORE_REG_PAIR x21, x22, #0x30 + EPILOG_RESTORE_REG_PAIR x23, x24, #0x40 + EPILOG_RESTORE_REG_PAIR x25, x26, #0x50 + EPILOG_RESTORE_REG_PAIR x27, x28, #0x60 + EPILOG_NOP ldr x9, [sp, #0x78] + EPILOG_RESTORE_REG_PAIR fp, lr, #(SIZEOF__PAL_LIMITED_CONTEXT + 0x20)! + EPILOG_NOP ret x9 + + NESTED_END RhpHijackForGcStressLeaf + +#endif ;; FEATURE_GC_STRESS + + +;; +;; The following functions are _jumped_ to when we need to transfer control from one method to another for EH +;; dispatch. These are needed to properly coordinate with the GC hijacking logic. We are essentially replacing +;; the return from the throwing method with a jump to the handler in the caller, but we need to be aware of +;; any return address hijack that may be in place for GC suspension. These routines use a quick test of the +;; return address against a specific GC hijack routine, and then fixup the stack pointer to what it would be +;; after a real return from the throwing method. Then, if we are not hijacked we can simply jump to the +;; handler in the caller. +;; +;; If we are hijacked, then we jump to a routine that will unhijack appropriatley and wait for the GC to +;; complete. There are also variants for GC stress. +;; +;; Note that at this point we are eiher hijacked or we are not, and this will not change until we return to +;; managed code. It is an invariant of the system that a thread will only attempt to hijack or unhijack +;; another thread while the target thread is suspended in managed code, and this is _not_ managed code. +;; + MACRO + RTU_EH_JUMP_HELPER $funcName, $hijackFuncName, $isStress, $stressFuncName + + LEAF_ENTRY $funcName + ldr x0, =$hijackFuncName + cmp x0, lr + beq RhpGCProbeForEHJump + + IF $isStress + ldr x0, =$stressFuncName + cmp x0, lr + beq RhpGCStressProbeForEHJump + ENDIF + + ;; We are not hijacked, so we can return to the handler. + ;; We return to keep the call/return prediction balanced. + mov lr, x2 ; Update the return address + ret + LEAF_END $funcName + MEND +;; We need an instance of the helper for each possible hijack function. The binder has enough +;; information to determine which one we need to use for any function. + RTU_EH_JUMP_HELPER RhpEHJumpScalar, RhpGcProbeHijackScalar, {false}, 0 + RTU_EH_JUMP_HELPER RhpEHJumpObject, RhpGcProbeHijackObject, {false}, 0 + RTU_EH_JUMP_HELPER RhpEHJumpByref, RhpGcProbeHijackByref, {false}, 0 +#ifdef FEATURE_GC_STRESS + RTU_EH_JUMP_HELPER RhpEHJumpScalarGCStress, RhpGcProbeHijackScalar, {true}, RhpGcStressHijackScalar + RTU_EH_JUMP_HELPER RhpEHJumpObjectGCStress, RhpGcProbeHijackObject, {true}, RhpGcStressHijackObject + RTU_EH_JUMP_HELPER RhpEHJumpByrefGCStress, RhpGcProbeHijackByref, {true}, RhpGcStressHijackByref +#endif + +;; +;; Macro to setup our frame and adjust the location of the EH object reference for EH jump probe funcs. +;; +;; Register state on entry: +;; x0: scratch +;; x1: reference to the exception object. +;; x2: handler address we want to jump to. +;; Non-volatile registers are all already correct for return to the caller. +;; The stack is as if we are just about to returned from the call +;; +;; Register state on exit: +;; x0: reference to the exception object +;; x2: thread pointer +;; + MACRO + EHJumpProbeProlog + + PROLOG_NOP mov x0, x1 ; move the ex object reference into x0 so we can report it + ALLOC_PROBE_FRAME 0x10 + str x2, [sp, #PROBE_FRAME_SIZE] + + ;; x2 <- GetThread(), TRASHES x1 + INLINE_GETTHREAD x2, x1 + + ;; Recover the original return address and update the frame + ldr lr, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + str lr, [sp, #OFFSETOF__PInvokeTransitionFrame__m_RIP] + + ;; ClearHijackState expects thread in x2 + ClearHijackState + + ; TRASHES x1 + INIT_PROBE_FRAME x2, x1, #(DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_X0), PTFF_X0_IS_GCREF_HI, (PROBE_FRAME_SIZE + 8) + add x1, sp, xzr + str x1, [x2, #OFFSETOF__Thread__m_pHackPInvokeTunnel] + MEND + +;; +;; Macro to re-adjust the location of the EH object reference, cleanup the frame, and make the +;; final jump to the handler for EH jump probe funcs. +;; +;; Register state on entry: +;; x0: reference to the exception object +;; x1-x3: scratch +;; +;; Register state on exit: +;; sp: correct for return to the caller +;; x1: reference to the exception object +;; + MACRO + EHJumpProbeEpilog + + ldr x2, [sp, #PROBE_FRAME_SIZE] + FREE_PROBE_FRAME 0x10 ; This restores exception object back into x0 + EPILOG_NOP mov x1, x0 ; Move the Exception object back into x1 where the catch handler expects it + EPILOG_NOP ret x2 + MEND + +;; +;; We are hijacked for a normal GC (not GC stress), so we need to unhijack and wait for the GC to complete. +;; +;; Register state on entry: +;; x0: reference to the exception object. +;; x2: thread +;; Non-volatile registers are all already correct for return to the caller. +;; The stack is as if we have tail called to this function (lr points to return address). +;; +;; Register state on exit: +;; x0: reference to the exception object +;; + NESTED_ENTRY RhpGCProbeForEHJump + brk 0xf000 ;; TODO: remove after debugging/testing stub + EHJumpProbeProlog + +#ifdef _DEBUG + ;; + ;; If we get here, then we have been hijacked for a real GC, and our SyncState must + ;; reflect that we've been requested to synchronize. + + ldr x1, =RhpTrapThreads + ldr w1, [x1] + tbnz x1, #TrapThreadsFlags_TrapThreads_Bit, %0 + + bl RhDebugBreak +0 +#endif ;; _DEBUG + + mov x4, x2 + WaitForGCCompletion + + EHJumpProbeEpilog + NESTED_END RhpGCProbeForEHJump + +#ifdef FEATURE_GC_STRESS +;; +;; We are hijacked for GC Stress (not a normal GC) so we need to invoke the GC stress helper. +;; +;; Register state on entry: +;; x1: reference to the exception object. +;; x2: thread +;; Non-volatile registers are all already correct for return to the caller. +;; The stack is as if we have tail called to this function (lr points to return address). +;; +;; Register state on exit: +;; x0: reference to the exception object +;; + NESTED_ENTRY RhpGCStressProbeForEHJump + brk 0xf000 ;; TODO: remove after debugging/testing stub + EHJumpProbeProlog + + bl $REDHAWKGCINTERFACE__STRESSGC + + EHJumpProbeEpilog + NESTED_END RhpGCStressProbeForEHJump + +;; +;; INVARIANT: Don't trash the argument registers, the binder codegen depends on this. +;; + LEAF_ENTRY RhpSuppressGcStress + INLINE_GETTHREAD x9, x10 + add x9, x9, #OFFSETOF__Thread__m_ThreadStateFlags +Retry + ldxr w10, [x9] + orr w10, w10, #TSF_SuppressGcStress + stxr w11, w10, [x9] + cbz w11, Success + b Retry + +Success + ret + LEAF_END RhpSuppressGcStress +#endif ;; FEATURE_GC_STRESS + +;; Helper called from hijacked loops + LEAF_ENTRY RhpLoopHijack +;; we arrive here with essentially all registers containing useful content +;; TODO: update this comment after the RhpLoopHijack is implemented in the compiler + +;; on the stack, we have two arguments: +;; - [sp+0] has the module header +;; - [sp+8] has the address of the indirection cell we jumped through +;; +;; + brk 0xf000 ;; TODO: remove after debugging/testing stub + ALLOC_LOOP_HIJACK_FRAME + + ; save condition codes + mrs x12, NZCV + str x12, [sp, #m_SavedNZCV] + + INLINE_GETTHREAD x4, x1 + INIT_PROBE_FRAME x4, x1, #PROBE_SAVE_FLAGS_EVERYTHING, 0, (PROBE_FRAME_SIZE + EXTRA_SAVE_SIZE + 8) +;; +;; compute the index of the indirection cell +;; + ldr x0, [sp,#(PROBE_FRAME_SIZE + EXTRA_SAVE_SIZE + 0)] + bl $GetLoopIndirCells + + ; x0 now has address of the first loop indir cell + ; subtract that from the address of our cell + ; and divide by 8 to give the index of our cell + ldr x1, [sp,#(PROBE_FRAME_SIZE + EXTRA_SAVE_SIZE + 8)] + sub x1, x1, x0 + lsr x0, x1, #3 + + ; x0 now has the index + ; recover the loop hijack target, passing the module header as an additional argument + ldr x1, [sp,#(PROBE_FRAME_SIZE + EXTRA_SAVE_SIZE + 0)] + bl RecoverLoopHijackTarget + + ; store the result as our pinvoke return address + str x0, [sp, #OFFSETOF__PInvokeTransitionFrame__m_RIP] + + ; also save it in the incoming parameter space for the actual return below + str x0, [sp,#(PROBE_FRAME_SIZE + EXTRA_SAVE_SIZE + 8)] + + ; Early out if GC stress is currently suppressed. Do this after we have computed the real address to + ; return to but before we link the transition frame onto m_pHackPInvokeTunnel (because hitting this + ; condition implies we're running restricted callouts during a GC itself and we could end up + ; overwriting a co-op frame set by the code that caused the GC in the first place, e.g. a GC.Collect + ; call). + ldr w1, [x4, #OFFSETOF__Thread__m_ThreadStateFlags] + tst w1, #TSF_SuppressGcStress__OR__TSF_DoNotTriggerGC + bne DoneWaitingForGc + + ; link the frame into the Thread + add x1, sp, xzr + str x1, [x4, #OFFSETOF__Thread__m_pHackPInvokeTunnel] + + ;; + ;; Unhijack this thread, if necessary. + ;; + INLINE_THREAD_UNHIJACK x4, x1, x2 ;; trashes x1, x2 + +#ifdef FEATURE_GC_STRESS + + ldr x1, =g_fGcStressStarted + ldr w1, [x1] + cbnz w1, NoGcStress + + mov x1, x0 + ldr x0, =$g_pTheRuntimeInstance + ldr x0, [x0] + bl $RuntimeInstance__ShouldHijackLoopForGcStress + cbnz x0, NoGcStress + + bl $REDHAWKGCINTERFACE__STRESSGC +NoGcStress +#endif ;; FEATURE_GC_STRESS + + add x2, sp, xzr ; sp is address of PInvokeTransitionFrame + bl RhpWaitForGCNoAbort + +DoneWaitingForGc + ldr x12, [sp, #OFFSETOF__PInvokeTransitionFrame__m_Flags] + tbnz x12, #PTFF_THREAD_ABORT_BIT, Abort + ; restore condition codes + ldr x12, [sp, #m_SavedNZCV] + msr NZCV, x12 + + FREE_LOOP_HIJACK_FRAME + + EPILOG_NOP ldr x1, [sp, #8] ; hijack target address + EPILOG_STACK_FREE 0x10 + EPILOG_NOP ret x1 ; jump to the hijack target + +Abort + FREE_LOOP_HIJACK_FRAME + + EPILOG_NOP mov w0, #STATUS_REDHAWK_THREAD_ABORT + EPILOG_NOP ldr x1, [sp, #8] ; hijack target address as exception PC + EPILOG_STACK_FREE 0x10 + EPILOG_NOP b RhpThrowHwEx + LEAF_END RhpLoopHijack + + INLINE_GETTHREAD_CONSTANT_POOL + + end diff --git a/external/corert/src/Native/Runtime/arm64/GetThread.asm b/external/corert/src/Native/Runtime/arm64/GetThread.asm new file mode 100644 index 0000000000..69e353c4b7 --- /dev/null +++ b/external/corert/src/Native/Runtime/arm64/GetThread.asm @@ -0,0 +1,30 @@ +;; 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. + +#include "AsmMacros.h" + + TEXTAREA + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpGetThread +;; +;; +;; INPUT: none +;; +;; OUTPUT: x9: Thread pointer +;; +;; MUST PRESERVE ARGUMENT REGISTERS +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + LEAF_ENTRY RhpGetThread + ;; x9 = GetThread(), TRASHES xip0 (which can be used as an intra-procedure-call scratch register) + INLINE_GETTHREAD x9, xip0 + ret + LEAF_END +FASTCALL_ENDFUNC + + INLINE_GETTHREAD_CONSTANT_POOL + + end diff --git a/external/corert/src/Native/Runtime/arm64/InteropThunksHelpers.asm b/external/corert/src/Native/Runtime/arm64/InteropThunksHelpers.asm new file mode 100644 index 0000000000..88bb7da5d5 --- /dev/null +++ b/external/corert/src/Native/Runtime/arm64/InteropThunksHelpers.asm @@ -0,0 +1,92 @@ +;; 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. + + +#include "ksarm64.h" + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DATA SECTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +__tls_array equ 0x58 ;; offsetof(TEB, ThreadLocalStoragePointer) + +POINTER_SIZE equ 0x08 + +;; TLS variables + AREA |.tls$|, DATA +ThunkParamSlot % 0x8 + + TEXTAREA + + EXTERN _tls_index + + ;; Section relocs are 32 bits. Using an extra DCD initialized to zero for 8-byte alignment. +__SECTIONREL_ThunkParamSlot + DCD ThunkParamSlot + RELOC 8, ThunkParamSlot ;; SECREL + DCD 0 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Interop Thunks Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + ;; + ;; RhCommonStub + ;; + ;; INPUT: xip0: thunk's data block + ;; + ;; TRASHES: x9, x10, x11, xip0 + ;; + LEAF_ENTRY RhCommonStub + ;; There are arbitrary callers passing arguments with arbitrary signatures. + ;; Custom calling convention: + ;; xip0 pointer to the current thunk's data block (data contains 2 pointer values: context + target pointers) + + ;; Save context data into the ThunkParamSlot thread-local variable + ;; A pointer to the delegate and function pointer for open static delegate should have been saved in the thunk's context cell during thunk allocation + ldr x10, =_tls_index + ldr w10, [x10] + ldr x9, [xpr, #__tls_array] + ldr x9, [x9, x10 lsl #3] ;; x9 <- our TLS base + + ;; x9 = base address of TLS data + ;; x10 = trashed + ;; xip0 = address of context cell in thunk's data + + ;; store thunk address in thread static + ldr x10, [xip0] + ldr x11, =__SECTIONREL_ThunkParamSlot + ldr x11, [x11] + str x10, [x9, x11] ;; ThunkParamSlot <- context slot data + + ;; Now load the target address and jump to it. + ldr xip0, [xip0, #POINTER_SIZE] + ret xip0 + + LEAF_END RhCommonStub + + ;; + ;; IntPtr RhGetCommonStubAddress() + ;; + LEAF_ENTRY RhGetCommonStubAddress + ldr x0, =RhCommonStub + ret + LEAF_END RhGetCommonStubAddress + + + ;; + ;; IntPtr RhGetCurrentThunkContext() + ;; + LEAF_ENTRY RhGetCurrentThunkContext + + ldr x1, =_tls_index + ldr w1, [x1] + ldr x0, [xpr, #__tls_array] + ldr x0, [x0, x1 lsl #3] ;; x0 <- our TLS base + + ldr x1, =__SECTIONREL_ThunkParamSlot + ldr x1, [x1] + ldr x0, [x0, x1] ;; x0 <- ThunkParamSlot + + ret + + LEAF_END RhGetCurrentThunkContext + + END diff --git a/external/corert/src/Native/Runtime/arm64/MiscStubs.asm b/external/corert/src/Native/Runtime/arm64/MiscStubs.asm new file mode 100644 index 0000000000..ba18a93c20 --- /dev/null +++ b/external/corert/src/Native/Runtime/arm64/MiscStubs.asm @@ -0,0 +1,245 @@ +;; 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. + +#include "AsmMacros.h" + + EXTERN memcpy + EXTERN memcpyGCRefs + EXTERN memcpyGCRefsWithWriteBarrier + EXTERN memcpyAnyWithWriteBarrier + EXTERN GetClasslibCCtorCheck + + TEXTAREA + +;; +;; Checks whether the static class constructor for the type indicated by the context structure has been +;; executed yet. If not the classlib is called via their CheckStaticClassConstruction callback which will +;; execute the cctor and update the context to record this fact. +;; +;; Input: +;; x0 : Address of StaticClassConstructionContext structure +;; +;; Output: +;; All volatile registers and the condition codes may be trashed. +;; + LEAF_ENTRY RhpCheckCctor + + ;; Check the m_initialized field of the context. The cctor has been run only if this equals 1 (the + ;; initial state is 0 and the remaining values are reserved for classlib use). This check is + ;; unsynchronized; if we go down the slow path and call the classlib then it is responsible for + ;; synchronizing with other threads and re-checking the value. + ldr w12, [x0, #OFFSETOF__StaticClassConstructionContext__m_initialized] + cmp w12, #1 + bne RhpCheckCctor__SlowPath + ret +RhpCheckCctor__SlowPath + mov x1, x0 + b RhpCheckCctor2 ; tail-call the check cctor helper that actually has an implementation to call + ; the cctor + + LEAF_END RhpCheckCctor + +;; +;; Checks whether the static class constructor for the type indicated by the context structure has been +;; executed yet. If not the classlib is called via their CheckStaticClassConstruction callback which will +;; execute the cctor and update the context to record this fact. +;; +;; Input: +;; x0 : Value that must be preserved in this register across the cctor check. +;; x1 : Address of StaticClassConstructionContext structure +;; +;; Output: +;; All volatile registers other than x0 may be trashed and the condition codes may also be trashed. +;; + LEAF_ENTRY RhpCheckCctor2 + + ;; Check the m_initialized field of the context. The cctor has been run only if this equals 1 (the + ;; initial state is 0 and the remaining values are reserved for classlib use). This check is + ;; unsynchronized; if we go down the slow path and call the classlib then it is responsible for + ;; synchronizing with other threads and re-checking the value. + ldr w12, [x1, #OFFSETOF__StaticClassConstructionContext__m_initialized] + cmp w12, #1 + bne RhpCheckCctor2__SlowPath + ret + + LEAF_END RhpCheckCctor2 + +;; +;; Slow path helper for RhpCheckCctor. +;; +;; Input: +;; x0 : Value that must be preserved in this register across the cctor check. +;; x1 : Address of StaticClassConstructionContext structure +;; +;; Output: +;; All volatile registers other than x0 may be trashed and the condition codes may also be trashed. +;; + NESTED_ENTRY RhpCheckCctor2__SlowPath + + ;; Need to preserve x0, x1 and lr across helper call. fp is also pushed to keep the stack 16 byte aligned. + PROLOG_SAVE_REG_PAIR fp, lr, #-0x20! + stp x0, x1, [sp, #0x10] + + ;; Call a C++ helper to retrieve the address of the classlib callback. The caller's return address is + ;; passed as the argument to the helper; it's an address in the module and is used by the helper to + ;; locate the classlib. + mov x0, lr + bl GetClasslibCCtorCheck + + ;; X0 now contains the address of the classlib method to call. The single argument is the context + ;; structure address currently in stashed on the stack. Clean up and tail call to the classlib + ;; callback so we're not on the stack should a GC occur (so we don't need to worry about transition + ;; frames). + mov x12, x0 + ldp x0, x1, [sp, #0x10] + EPILOG_RESTORE_REG_PAIR fp, lr, #0x20! + ;; tail-call the class lib cctor check function. This function is required to return its first + ;; argument, so that x0 can be preserved. + EPILOG_NOP ret x12 + + NESTED_END RhpCheckCctor__SlowPath2 + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* RhpCopyMultibyteNoGCRefs(void*, void*, size_t) +;; +;; The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +;; the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +;; it to managed code. +;; + + LEAF_ENTRY RhpCopyMultibyteNoGCRefs + + ; x0 dest + ; x1 src + ; x2 count + + cbz x2, NothingToCopy_NoGCRefs ; check for a zero-length copy + + ; Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + ; unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + ; translated to a managed exception as usual. + ALTERNATE_ENTRY RhpCopyMultibyteNoGCRefsDestAVLocation + ldrb wzr, [x0] + ALTERNATE_ENTRY RhpCopyMultibyteNoGCRefsSrcAVLocation + ldrb wzr, [x1] + + ; tail-call to plain-old-memcpy + b memcpy + +NothingToCopy_NoGCRefs + ; dest is already in x0 + ret + + LEAF_END + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* RhpCopyMultibyte(void*, void*, size_t) +;; +;; The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +;; the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +;; it to managed code. +;; + + LEAF_ENTRY RhpCopyMultibyte + + ; x0 dest + ; x1 src + ; x2 count + + ; check for a zero-length copy + cbz x2, NothingToCopy_RhpCopyMultibyte + + ; Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + ; unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + ; translated to a managed exception as usual. + ALTERNATE_ENTRY RhpCopyMultibyteDestAVLocation + ldrb wzr, [x0] + ALTERNATE_ENTRY RhpCopyMultibyteSrcAVLocation + ldrb wzr, [x1] + + ; tail-call to the GC-safe memcpy implementation + b memcpyGCRefs + +NothingToCopy_RhpCopyMultibyte + ; dest is already still in x0 + ret + + LEAF_END + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* RhpCopyMultibyteWithWriteBarrier(void*, void*, size_t) +;; +;; The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +;; the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +;; it to managed code. +;; Runs a card table update via RhpBulkWriteBarrier after the copy +;; + + LEAF_ENTRY RhpCopyMultibyteWithWriteBarrier + + ; x0 dest + ; x1 src + ; x2 count + + ; check for a zero-length copy + cbz x2, NothingToCopy_RhpCopyMultibyteWithWriteBarrier + + ; Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + ; unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + ; translated to a managed exception as usual. + ALTERNATE_ENTRY RhpCopyMultibyteWithWriteBarrierDestAVLocation + ldrb wzr, [x0] + ALTERNATE_ENTRY RhpCopyMultibyteWithWriteBarrierSrcAVLocation + ldrb wzr, [x1] + + ; tail-call to the GC-safe memcpy implementation + b memcpyGCRefsWithWriteBarrier + +NothingToCopy_RhpCopyMultibyteWithWriteBarrier + ; dest is already still in x0 + ret + LEAF_END + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; void* RhpCopyAnyWithWriteBarrier(void*, void*, size_t) +;; +;; The purpose of this wrapper is to hoist the potential null reference exceptions of copying memory up to a place where +;; the stack unwinder and exception dispatch can properly transform the exception into a managed exception and dispatch +;; it to managed code. +;; Runs a card table update via RhpBulkWriteBarrier after the copy if it contained GC pointers +;; + + LEAF_ENTRY RhpCopyAnyWithWriteBarrier + + ; x0 dest + ; x1 src + ; x2 count + + ; check for a zero-length copy + cbz x2, NothingToCopy_RhpCopyAnyWithWriteBarrier + + ; Now check the dest and src pointers. If they AV, the EH subsystem will recognize the address of the AV, + ; unwind the frame, and fixup the stack to make it look like the (managed) caller AV'ed, which will be + ; translated to a managed exception as usual. + ALTERNATE_ENTRY RhpCopyAnyWithWriteBarrierDestAVLocation + ldrb wzr, [x0] + ALTERNATE_ENTRY RhpCopyAnyWithWriteBarrierSrcAVLocation + ldrb wzr, [x1] + + ; tail-call to the GC-safe memcpy implementation + b memcpyAnyWithWriteBarrier + +NothingToCopy_RhpCopyAnyWithWriteBarrier + ; dest is already still in x0 + ret + + LEAF_END + + end diff --git a/external/corert/src/Native/Runtime/arm64/PInvoke.asm b/external/corert/src/Native/Runtime/arm64/PInvoke.asm new file mode 100644 index 0000000000..c05328314b --- /dev/null +++ b/external/corert/src/Native/Runtime/arm64/PInvoke.asm @@ -0,0 +1,301 @@ +;; 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. + +#include "AsmMacros.h" + + TEXTAREA + + IMPORT RhpReversePInvokeBadTransition + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpWaitForSuspend -- rare path for RhpPInvoke and RhpReversePInvokeReturn +;; +;; +;; INPUT: none +;; +;; TRASHES: none +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpWaitForSuspend + + ;; FP and LR registers + PROLOG_SAVE_REG_PAIR fp, lr, #-0xA0! ;; Push down stack pointer and store FP and LR + + ;; Need to save argument registers x0-x7 and the return buffer register x8 (twice just for 16B alignment) + stp x0, x1, [sp, #0x10] + stp x2, x3, [sp, #0x20] + stp x4, x5, [sp, #0x30] + stp x6, x7, [sp, #0x40] + stp x8, x8, [sp, #0x50] + + ;; Save float argument registers as well since they're volatile + stp d0, d1, [sp, #0x60] + stp d2, d3, [sp, #0x70] + stp d4, d5, [sp, #0x80] + stp d6, d7, [sp, #0x90] + + bl RhpWaitForSuspend2 + + ;; Restore floating point registers + ldp d0, d1, [sp, #0x60] + ldp d2, d3, [sp, #0x70] + ldp d4, d5, [sp, #0x80] + ldp d6, d7, [sp, #0x90] + + ;; Restore the argument registers + ldp x0, x1, [sp, #0x10] + ldp x2, x3, [sp, #0x20] + ldp x4, x5, [sp, #0x30] + ldp x6, x7, [sp, #0x40] + ldr x8, [sp, #0x50] + + ;; Restore FP and LR registers, and free the allocated stack block + EPILOG_RESTORE_REG_PAIR fp, lr, #0xA0! + EPILOG_RETURN + + NESTED_END RhpWaitForSuspend + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpWaitForGCNoAbort +;; +;; +;; INPUT: x9: transition frame +;; +;; TRASHES: None +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpWaitForGCNoAbort + + ;; FP and LR registers + PROLOG_SAVE_REG_PAIR fp, lr, #-0x40! ;; Push down stack pointer and store FP and LR + + ;; Save the integer return registers, as well as the floating return registers + stp x0, x1, [sp, #0x10] + stp d0, d1, [sp, #0x20] + stp d2, d3, [sp, #0x30] + + ldr x0, [x9, #OFFSETOF__PInvokeTransitionFrame__m_pThread] + ldr w0, [x0, #OFFSETOF__Thread__m_ThreadStateFlags] + tbnz x0, #TSF_DoNotTriggerGc_Bit, Done + + mov x0, x9 ; passing transition frame in x0 + bl RhpWaitForGC2 + +Done + ldp x0, x1, [sp, #0x10] + ldp d0, d1, [sp, #0x20] + ldp d2, d3, [sp, #0x30] + EPILOG_RESTORE_REG_PAIR fp, lr, #0x40! + EPILOG_RETURN + + NESTED_END RhpWaitForGCNoAbort + + EXTERN RhpThrowHwEx + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpWaitForGC +;; +;; +;; INPUT: x9: transition frame +;; +;; TRASHES: x0, x1, x10 +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpWaitForGC + + PROLOG_SAVE_REG_PAIR fp, lr, #-0x10! + + ldr x10, =RhpTrapThreads + ldr w10, [x10] + tbz x10, #TrapThreadsFlags_TrapThreads_Bit, NoWait + bl RhpWaitForGCNoAbort +NoWait + tbz x10, #TrapThreadsFlags_AbortInProgress_Bit, NoAbort + ldr x10, [x9, #OFFSETOF__PInvokeTransitionFrame__m_Flags] + tbz x10, #PTFF_THREAD_ABORT_BIT, NoAbort + + EPILOG_RESTORE_REG_PAIR fp, lr, #0x10! + EPILOG_NOP mov w0, #STATUS_REDHAWK_THREAD_ABORT + EPILOG_NOP mov x1, lr ; hijack target address as exception PC + EPILOG_NOP b RhpThrowHwEx + +NoAbort + EPILOG_RESTORE_REG_PAIR fp, lr, #0x10! + EPILOG_RETURN + + NESTED_END RhpWaitForGC + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpReversePInvoke +;; +;; IN: x9: address of reverse pinvoke frame +;; 0: save slot for previous M->U transition frame +;; 8: save slot for thread pointer to avoid re-calc in epilog sequence +;; +;; PRESERVES: x0 - x8 -- need to preserve these because the caller assumes they aren't trashed +;; +;; TRASHES: x10, x11 +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + LEAF_ENTRY RhpReversePInvoke + + INLINE_GETTHREAD x10, x11 ; x10 = Thread, x11 trashed + str x10, [x9, #8] ; save Thread pointer for RhpReversePInvokeReturn + + ;; x9 = reverse pinvoke frame + ;; x10 = thread + ;; x11 = scratch + + ldr w11, [x10, #OFFSETOF__Thread__m_ThreadStateFlags] + tbz x11, #TSF_Attached_Bit, AttachThread + +ThreadAttached + ;; + ;; Check for the correct mode. This is accessible via various odd things that we cannot completely + ;; prevent such as : + ;; 1) Registering a reverse pinvoke entrypoint as a vectored exception handler + ;; 2) Performing a managed delegate invoke on a reverse pinvoke delegate. + ;; + ldr x11, [x10, #OFFSETOF__Thread__m_pTransitionFrame] + cbz x11, CheckBadTransition + + ;; Save previous TransitionFrame prior to making the mode transition so that it is always valid + ;; whenever we might attempt to hijack this thread. + str x11, [x9] + + str xzr, [x10, #OFFSETOF__Thread__m_pTransitionFrame] + dmb ish + + ldr x11, =RhpTrapThreads + ldr w11, [x11] + tbnz x11, #TrapThreadsFlags_TrapThreads_Bit, TrapThread + + ret + +CheckBadTransition + ;; Allow 'bad transitions' in when the TSF_DoNotTriggerGc mode is set. This allows us to have + ;; [NativeCallable] methods that are called via the "restricted GC callouts" as well as from native, + ;; which is necessary because the methods are CCW vtable methods on interfaces passed to native. + ldr w11, [x10, #OFFSETOF__Thread__m_ThreadStateFlags] + tbz x11, #TSF_DoNotTriggerGc_Bit, BadTransition + + ;; zero-out our 'previous transition frame' save slot + mov x11, #0 + str x11, [x9] + + ;; nothing more to do + ret + +TrapThread + ;; put the previous frame back (sets us back to preemptive mode) + ldr x11, [x9] + str x11, [x10, #OFFSETOF__Thread__m_pTransitionFrame] + dmb ish + +AttachThread + ; passing address of reverse pinvoke frame in x9 + b RhpReversePInvokeAttachOrTrapThread + +BadTransition + mov x0, lr ; arg <- return address + b RhpReversePInvokeBadTransition + + LEAF_END RhpReversePInvoke + + INLINE_GETTHREAD_CONSTANT_POOL + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpReversePInvokeAttachOrTrapThread -- rare path for RhpPInvoke +;; +;; +;; INPUT: x9: address of reverse pinvoke frame +;; +;; PRESERVES: x0-x8 -- need to preserve these because the caller assumes they aren't trashed +;; +;; TRASHES: none +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + NESTED_ENTRY RhpReversePInvokeAttachOrTrapThread + + ;; FP and LR registers + PROLOG_SAVE_REG_PAIR fp, lr, #-0xA0! ;; Push down stack pointer and store FP and LR + + ;; Need to save argument registers x0-x7 and the return buffer register x8 (twice for 16B alignment) + stp x0, x1, [sp, #0x10] + stp x2, x3, [sp, #0x20] + stp x4, x5, [sp, #0x30] + stp x6, x7, [sp, #0x40] + stp x8, x8, [sp, #0x50] + + ;; Save float argument registers as well since they're volatile + stp d0, d1, [sp, #0x60] + stp d2, d3, [sp, #0x70] + stp d4, d5, [sp, #0x80] + stp d6, d7, [sp, #0x90] + + mov x0, x9 ; passing reverse pinvoke frame pointer in x0 + bl RhpReversePInvokeAttachOrTrapThread2 + + ;; Restore floating point registers + ldp d0, d1, [sp, #0x60] + ldp d2, d3, [sp, #0x70] + ldp d4, d5, [sp, #0x80] + ldp d6, d7, [sp, #0x90] + + ;; Restore the argument registers + ldp x0, x1, [sp, #0x10] + ldp x2, x3, [sp, #0x20] + ldp x4, x5, [sp, #0x30] + ldp x6, x7, [sp, #0x40] + ldr x8, [sp, #0x50] + + ;; Restore FP and LR registers, and free the allocated stack block + EPILOG_RESTORE_REG_PAIR fp, lr, #0xA0! + EPILOG_RETURN + + NESTED_END RhpReversePInvokeTrapThread + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpReversePInvokeReturn +;; +;; IN: x9: address of reverse pinvoke frame +;; 0: save slot for previous M->U transition frame +;; 8: save slot for thread pointer to avoid re-calc in epilog sequence +;; +;; TRASHES: x10, x11 +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + LEAF_ENTRY RhpReversePInvokeReturn + + ldp x10, x11, [x9] + + ;; x10: previous M->U transition frame + ;; x11: thread pointer + + str x10, [x11, #OFFSETOF__Thread__m_pTransitionFrame] + dmb ish + + ldr x10, =RhpTrapThreads + ldr w10, [x10] + tbnz x10, #TrapThreadsFlags_TrapThreads_Bit, RareTrapThread + + ret + +RareTrapThread + b RhpWaitForSuspend + + LEAF_END RhpReversePInvokeReturn + + INLINE_GETTHREAD_CONSTANT_POOL + + end diff --git a/external/corert/src/Native/Runtime/arm64/StubDispatch.asm b/external/corert/src/Native/Runtime/arm64/StubDispatch.asm new file mode 100644 index 0000000000..82a4f861e3 --- /dev/null +++ b/external/corert/src/Native/Runtime/arm64/StubDispatch.asm @@ -0,0 +1,189 @@ +;; 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. + +#include "AsmMacros.h" + + TEXTAREA + +#ifdef FEATURE_CACHED_INTERFACE_DISPATCH + + EXTERN RhpCastableObjectResolve + EXTERN RhpCidResolve + EXTERN RhpUniversalTransition_DebugStepTailCall + + EXTERN t_TLS_DispatchCell + + MACRO + GET_TLS_DISPATCH_CELL + + ldr x9, =_tls_index + ldr w9, [x9] + ldr xip1, [xpr, #__tls_array] + ldr xip1, [xip1, x9 lsl #3] + ldr x9, =SECTIONREL_t_TLS_DispatchCell + ldr x9, [x9] + ldr xip1, [xip1, x9] + MEND + + MACRO + SET_TLS_DISPATCH_CELL + ;; xip1 : Value to be assigned to the TLS variable + + ldr x9, =_tls_index + ldr w9, [x9] + ldr x10, [xpr, #__tls_array] + ldr x10, [x10, x9 lsl #3] + ldr x9, =SECTIONREL_t_TLS_DispatchCell + ldr x9, [x9] + str xip1, [x10, x9] + MEND + +SECTIONREL_t_TLS_DispatchCell + DCD t_TLS_DispatchCell + RELOC 8, t_TLS_DispatchCell ;; SECREL + DCD 0 + + ;; Macro that generates code to check a single cache entry. + MACRO + CHECK_CACHE_ENTRY $entry + ;; Check a single entry in the cache. + ;; x9 : Cache data structure. Also used for target address jump. + ;; x10 : Instance EEType* + ;; x11 : Trashed + ldr x11, [x9, #(OFFSETOF__InterfaceDispatchCache__m_rgEntries + ($entry * 16))] + cmp x10, x11 + bne %ft0 ;; Jump to label '0' + ldr x9, [x9, #(OFFSETOF__InterfaceDispatchCache__m_rgEntries + ($entry * 16) + 8)] + ret x9 +0 ;; Label '0' + MEND + + + LEAF_ENTRY RhpCastableObjectDispatch_CommonStub + ;; Custom calling convention: + ;; xip0 has pointer to the current thunk's data block + + ;; store dispatch cell address in thread static + ldr xip1, [xip0] + SET_TLS_DISPATCH_CELL + + ;; Now load the target address and jump to it. + ldr x9, [xip0, #8] + ret x9 + LEAF_END RhpCastableObjectDispatch_CommonStub + + LEAF_ENTRY RhpTailCallTLSDispatchCell + ;; Load the dispatch cell out of the TLS variable + GET_TLS_DISPATCH_CELL + + ;; Tail call to the target of the dispatch cell, preserving the cell address in xip1 + ldr x9, [xip1] + ret x9 + LEAF_END RhpTailCallTLSDispatchCell + + LEAF_ENTRY RhpCastableObjectDispatchHelper_TailCalled + ;; Load the dispatch cell out of the TLS variable + GET_TLS_DISPATCH_CELL + b RhpCastableObjectDispatchHelper + LEAF_END RhpCastableObjectDispatchHelper_TailCalled + + LEAF_ENTRY RhpCastableObjectDispatchHelper + ;; The address of the indirection cell is passed to this function in the xip1 + ;; The calling convention of the universal thunk is that the parameter + ;; for the universal thunk target is to be placed in xip1 + ;; and the universal thunk target address is to be placed in xip0 + ldr xip0, =RhpCastableObjectResolve + + b RhpUniversalTransition_DebugStepTailCall + LEAF_END RhpCastableObjectDispatchHelper + + +;; +;; Macro that generates a stub consuming a cache with the given number of entries. +;; + GBLS StubName + + MACRO + DEFINE_INTERFACE_DISPATCH_STUB $entries + +StubName SETS "RhpInterfaceDispatch$entries" + + NESTED_ENTRY $StubName + + ;; xip1 currently holds the indirection cell address. We need to get the cache structure instead. + ldr x9, [xip1, #OFFSETOF__InterfaceDispatchCell__m_pCache] + + ;; Load the EEType from the object instance in x0. + ldr x10, [x0] + + GBLA CurrentEntry +CurrentEntry SETA 0 + + WHILE CurrentEntry < $entries + CHECK_CACHE_ENTRY CurrentEntry +CurrentEntry SETA CurrentEntry + 1 + WEND + + ;; xip1 still contains the indirection cell address. + b RhpInterfaceDispatchSlow + + NESTED_END $StubName + + MEND + +;; +;; Define all the stub routines we currently need. +;; + DEFINE_INTERFACE_DISPATCH_STUB 1 + DEFINE_INTERFACE_DISPATCH_STUB 2 + DEFINE_INTERFACE_DISPATCH_STUB 4 + DEFINE_INTERFACE_DISPATCH_STUB 8 + DEFINE_INTERFACE_DISPATCH_STUB 16 + DEFINE_INTERFACE_DISPATCH_STUB 32 + DEFINE_INTERFACE_DISPATCH_STUB 64 + + +;; +;; Initial dispatch on an interface when we don't have a cache yet. +;; + LEAF_ENTRY RhpInitialInterfaceDispatch + ;; Just tail call to the cache miss helper. + b RhpInterfaceDispatchSlow + LEAF_END RhpInitialInterfaceDispatch + +;; +;; Stub dispatch routine for dispatch to a vtable slot +;; + LEAF_ENTRY RhpVTableOffsetDispatch + ;; xip1 has the interface dispatch cell address in it. + ;; load x12 to point to the vtable offset (which is stored in the m_pCache field). + ldr x12, [xip1, #OFFSETOF__InterfaceDispatchCell__m_pCache] + + ;; Load the EEType from the object instance in x0, and add it to the vtable offset + ;; to get the address in the vtable of what we want to dereference + ldr x13, [x0] + add x12, x12, x13 + + ;; Load the target address of the vtable into x12 + ldr x12, [x12] + + ret x12 + LEAF_END RhpVTableOffsetDispatch + +;; +;; Cache miss case, call the runtime to resolve the target and update the cache. +;; + LEAF_ENTRY RhpInterfaceDispatchSlow + ALTERNATE_ENTRY RhpInitialDynamicInterfaceDispatch + ;; xip1 has the interface dispatch cell address in it. + ;; Calling convention of the universal thunk is: + ;; xip0: contains target address for the thunk to call + ;; xip1: contains parameter of the thunk's target + ldr xip0, =RhpCidResolve + b RhpUniversalTransition_DebugStepTailCall + LEAF_END RhpInterfaceDispatchSlow + +#endif // FEATURE_CACHED_INTERFACE_DISPATCH + + END diff --git a/external/corert/src/Native/Runtime/arm64/ThunkPoolThunks.asm b/external/corert/src/Native/Runtime/arm64/ThunkPoolThunks.asm new file mode 100644 index 0000000000..ddd2636dbc --- /dev/null +++ b/external/corert/src/Native/Runtime/arm64/ThunkPoolThunks.asm @@ -0,0 +1,336 @@ +;; 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. + +#include "ksarm64.h" + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; STUBS & DATA SECTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +THUNK_CODESIZE equ 0x10 ;; 3 instructions, 4 bytes each (and we also have 4 bytes of padding) +THUNK_DATASIZE equ 0x10 ;; 2 qwords + +THUNK_POOL_NUM_THUNKS_PER_PAGE equ 0xFA ;; 250 thunks per page + +POINTER_SIZE equ 0x08 + + MACRO + NAMED_READONLY_DATA_SECTION $name, $areaAlias + AREA $areaAlias,DATA,READONLY +RO$name % 8 + MEND + + ;; This macro is used to declare the thunks data blocks. Unlike the macro above (which is just used for padding), + ;; this macro needs to assign labels to each data block, so we can address them using PC-relative addresses. + MACRO + NAMED_READWRITE_DATA_SECTION $name, $areaAlias, $pageIndex + AREA $areaAlias,DATA + THUNKS_DATA_PAGE_BLOCK $pageIndex + MEND + + MACRO + LOAD_DATA_ADDRESS $groupIndex, $index, $pageIndex + ALIGN 0x10 ;; make sure we align to 16-byte boundary for CFG table + + ;; Set xip0 to the address of the current thunk's data block. This is done using labels. + adr xip0, label_$groupIndex_$index_P$pageIndex + MEND + + MACRO + JUMP_TO_COMMON $groupIndex, $index + ;; start : xip0 points to the current thunks first data cell in the data page + ;; set xip0 to begining of data page : xip0 <- xip0 - (THUNK_DATASIZE * current thunk's index) + ;; fix offset to point to last QWROD in page : xip1 <- [xip0 + PAGE_SIZE - POINTER_SIZE] + ;; tailcall to the location pointed at by the last qword in the data page + ldr xip1, [xip0, #(PAGE_SIZE - POINTER_SIZE - ($groupIndex * THUNK_DATASIZE * 10 + THUNK_DATASIZE * $index))] + ret xip1 + + brk 0xf000 ;; Stubs need to be 16-byte aligned (see comment above). Filling padding with a + ;; deterministic brk instruction, instead of having it just filled with zeros. + MEND + + MACRO + THUNK_LABELED_DATA_BLOCK $groupIndex, $index, $pageIndex + + ;; Each data block contains 2 qword cells. The data block is also labeled so it can be addressed + ;; using PC relative instructions +label_$groupIndex_$index_P$pageIndex + DCQ 0 + DCQ 0 + MEND + + MACRO + TenThunks $groupIndex, $pageIndex + + ;; Each thunk will load the address of its corresponding data (from the page that immediately follows) + ;; and call a common stub. The address of the common stub is setup by the caller (last qword + ;; in the thunks data section) depending on the 'kind' of thunks needed (interop, fat function pointers, etc...) + + ;; Each data block used by a thunk consists of two qword values: + ;; - Context: some value given to the thunk as context. Example for fat-fptrs: context = generic dictionary + ;; - Target : target code that the thunk eventually jumps to. + + LOAD_DATA_ADDRESS $groupIndex,0,$pageIndex + JUMP_TO_COMMON $groupIndex,0 + + LOAD_DATA_ADDRESS $groupIndex,1,$pageIndex + JUMP_TO_COMMON $groupIndex,1 + + LOAD_DATA_ADDRESS $groupIndex,2,$pageIndex + JUMP_TO_COMMON $groupIndex,2 + + LOAD_DATA_ADDRESS $groupIndex,3,$pageIndex + JUMP_TO_COMMON $groupIndex,3 + + LOAD_DATA_ADDRESS $groupIndex,4,$pageIndex + JUMP_TO_COMMON $groupIndex,4 + + LOAD_DATA_ADDRESS $groupIndex,5,$pageIndex + JUMP_TO_COMMON $groupIndex,5 + + LOAD_DATA_ADDRESS $groupIndex,6,$pageIndex + JUMP_TO_COMMON $groupIndex,6 + + LOAD_DATA_ADDRESS $groupIndex,7,$pageIndex + JUMP_TO_COMMON $groupIndex,7 + + LOAD_DATA_ADDRESS $groupIndex,8,$pageIndex + JUMP_TO_COMMON $groupIndex,8 + + LOAD_DATA_ADDRESS $groupIndex,9,$pageIndex + JUMP_TO_COMMON $groupIndex,9 + MEND + + MACRO + TenThunkDataBlocks $groupIndex, $pageIndex + + ;; Similar to the thunks stubs block, we declare the thunks data blocks here + + THUNK_LABELED_DATA_BLOCK $groupIndex, 0, $pageIndex + THUNK_LABELED_DATA_BLOCK $groupIndex, 1, $pageIndex + THUNK_LABELED_DATA_BLOCK $groupIndex, 2, $pageIndex + THUNK_LABELED_DATA_BLOCK $groupIndex, 3, $pageIndex + THUNK_LABELED_DATA_BLOCK $groupIndex, 4, $pageIndex + THUNK_LABELED_DATA_BLOCK $groupIndex, 5, $pageIndex + THUNK_LABELED_DATA_BLOCK $groupIndex, 6, $pageIndex + THUNK_LABELED_DATA_BLOCK $groupIndex, 7, $pageIndex + THUNK_LABELED_DATA_BLOCK $groupIndex, 8, $pageIndex + THUNK_LABELED_DATA_BLOCK $groupIndex, 9, $pageIndex + MEND + + MACRO + THUNKS_PAGE_BLOCK $pageIndex + + TenThunks 0, $pageIndex + TenThunks 1, $pageIndex + TenThunks 2, $pageIndex + TenThunks 3, $pageIndex + TenThunks 4, $pageIndex + TenThunks 5, $pageIndex + TenThunks 6, $pageIndex + TenThunks 7, $pageIndex + TenThunks 8, $pageIndex + TenThunks 9, $pageIndex + TenThunks 10, $pageIndex + TenThunks 11, $pageIndex + TenThunks 12, $pageIndex + TenThunks 13, $pageIndex + TenThunks 14, $pageIndex + TenThunks 15, $pageIndex + TenThunks 16, $pageIndex + TenThunks 17, $pageIndex + TenThunks 18, $pageIndex + TenThunks 19, $pageIndex + TenThunks 20, $pageIndex + TenThunks 21, $pageIndex + TenThunks 22, $pageIndex + TenThunks 23, $pageIndex + TenThunks 24, $pageIndex + MEND + + MACRO + THUNKS_DATA_PAGE_BLOCK $pageIndex + + TenThunkDataBlocks 0, $pageIndex + TenThunkDataBlocks 1, $pageIndex + TenThunkDataBlocks 2, $pageIndex + TenThunkDataBlocks 3, $pageIndex + TenThunkDataBlocks 4, $pageIndex + TenThunkDataBlocks 5, $pageIndex + TenThunkDataBlocks 6, $pageIndex + TenThunkDataBlocks 7, $pageIndex + TenThunkDataBlocks 8, $pageIndex + TenThunkDataBlocks 9, $pageIndex + TenThunkDataBlocks 10, $pageIndex + TenThunkDataBlocks 11, $pageIndex + TenThunkDataBlocks 12, $pageIndex + TenThunkDataBlocks 13, $pageIndex + TenThunkDataBlocks 14, $pageIndex + TenThunkDataBlocks 15, $pageIndex + TenThunkDataBlocks 16, $pageIndex + TenThunkDataBlocks 17, $pageIndex + TenThunkDataBlocks 18, $pageIndex + TenThunkDataBlocks 19, $pageIndex + TenThunkDataBlocks 20, $pageIndex + TenThunkDataBlocks 21, $pageIndex + TenThunkDataBlocks 22, $pageIndex + TenThunkDataBlocks 23, $pageIndex + TenThunkDataBlocks 24, $pageIndex + MEND + + + ;; + ;; The first thunks section should be 64K aligned because it can get + ;; mapped multiple times in memory, and mapping works on allocation + ;; granularity boundaries (we don't want to map more than what we need) + ;; + ;; The easiest way to do so is by having the thunks section at the + ;; first 64K aligned virtual address in the binary. We provide a section + ;; layout file to the linker to tell it how to layout the thunks sections + ;; that we care about. (ndp\rh\src\runtime\DLLs\app\mrt100_app_sectionlayout.txt) + ;; + ;; The PE spec says images cannot have gaps between sections (other + ;; than what is required by the section alignment value in the header), + ;; therefore we need a couple of padding data sections (otherwise the + ;; OS will not load the image). + ;; + + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment0, "|.pad0|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment1, "|.pad1|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment2, "|.pad2|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment3, "|.pad3|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment4, "|.pad4|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment5, "|.pad5|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment6, "|.pad6|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment7, "|.pad7|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment8, "|.pad8|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment9, "|.pad9|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment10, "|.pad10|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment11, "|.pad11|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment12, "|.pad12|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment13, "|.pad13|" + NAMED_READONLY_DATA_SECTION PaddingFor64KAlignment14, "|.pad14|" + + ;; + ;; Declaring all the data section first since they have labels referenced by the stubs sections, to prevent + ;; compilation errors ("undefined symbols"). The stubs/data sections will be correctly laid out in the image + ;; using using the explicit layout configurations (ndp\rh\src\runtime\DLLs\mrt100_sectionlayout.txt) + ;; + NAMED_READWRITE_DATA_SECTION ThunkData0, "|.tkd0|", 0 + NAMED_READWRITE_DATA_SECTION ThunkData1, "|.tkd1|", 1 + NAMED_READWRITE_DATA_SECTION ThunkData2, "|.tkd2|", 2 + NAMED_READWRITE_DATA_SECTION ThunkData3, "|.tkd3|", 3 + NAMED_READWRITE_DATA_SECTION ThunkData4, "|.tkd4|", 4 + NAMED_READWRITE_DATA_SECTION ThunkData5, "|.tkd5|", 5 + NAMED_READWRITE_DATA_SECTION ThunkData6, "|.tkd6|", 6 + NAMED_READWRITE_DATA_SECTION ThunkData7, "|.tkd7|", 7 + + ;; + ;; Thunk Stubs + ;; NOTE: Keep number of blocks in sync with macro/constant named 'NUM_THUNK_BLOCKS' in: + ;; - ndp\FxCore\src\System.Private.CoreLib\System\Runtime\InteropServices\ThunkPool.cs + ;; - ndp\rh\src\tools\rhbind\zapimage.h + ;; + + LEAF_ENTRY ThunkPool, "|.tks0|" + THUNKS_PAGE_BLOCK 0 + LEAF_END ThunkPool + + LEAF_ENTRY ThunkPool1, "|.tks1|" + THUNKS_PAGE_BLOCK 1 + LEAF_END ThunkPool1 + + LEAF_ENTRY ThunkPool2, "|.tks2|" + THUNKS_PAGE_BLOCK 2 + LEAF_END ThunkPool2 + + LEAF_ENTRY ThunkPool3, "|.tks3|" + THUNKS_PAGE_BLOCK 3 + LEAF_END ThunkPool3 + + LEAF_ENTRY ThunkPool4, "|.tks4|" + THUNKS_PAGE_BLOCK 4 + LEAF_END ThunkPool4 + + LEAF_ENTRY ThunkPool5, "|.tks5|" + THUNKS_PAGE_BLOCK 5 + LEAF_END ThunkPool5 + + LEAF_ENTRY ThunkPool6, "|.tks6|" + THUNKS_PAGE_BLOCK 6 + LEAF_END ThunkPool6 + + LEAF_ENTRY ThunkPool7, "|.tks7|" + THUNKS_PAGE_BLOCK 7 + LEAF_END ThunkPool7 + + + ;; + ;; IntPtr RhpGetThunksBase() + ;; + ;; ARM64TODO: There is a bug in the arm64 assembler which ends up with mis-sorted Pdata entries + ;; for the functions in this file. As a work around, don't generate pdata for these small stubs. + ;; All the "No_PDATA" variants need to be removed after MASM bug 516396 is fixed. + LEAF_ENTRY_NO_PDATA RhpGetThunksBase + ;; Return the address of the first thunk pool to the caller (this is really the base address) + ldr x0, =ThunkPool + ret + LEAF_END_NO_PDATA RhpGetThunksBase + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; General Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + ;; + ;; int RhpGetNumThunksPerBlock() + ;; + LEAF_ENTRY_NO_PDATA RhpGetNumThunksPerBlock + mov x0, THUNK_POOL_NUM_THUNKS_PER_PAGE + ret + LEAF_END_NO_PDATA RhpGetNumThunksPerBlock + + ;; + ;; int RhpGetThunkSize() + ;; + LEAF_ENTRY_NO_PDATA RhpGetThunkSize + mov x0, THUNK_CODESIZE + ret + LEAF_END_NO_PDATA RhpGetThunkSize + + ;; + ;; int RhpGetNumThunkBlocksPerMapping() + ;; + LEAF_ENTRY_NO_PDATA RhpGetNumThunkBlocksPerMapping + mov x0, 8 + ret + LEAF_END_NO_PDATA RhpGetNumThunkBlocksPerMapping + + ;; + ;; int RhpGetThunkBlockSize + ;; + LEAF_ENTRY_NO_PDATA RhpGetThunkBlockSize + mov x0, PAGE_SIZE * 2 + ret + LEAF_END_NO_PDATA RhpGetThunkBlockSize + + ;; + ;; IntPtr RhpGetThunkDataBlockAddress(IntPtr thunkStubAddress) + ;; + LEAF_ENTRY_NO_PDATA RhpGetThunkDataBlockAddress + mov x12, PAGE_SIZE - 1 + bic x0, x0, x12 + mov x12, PAGE_SIZE + add x0, x0, x12 + ret + LEAF_END_NO_PDATA RhpGetThunkDataBlockAddress + + ;; + ;; IntPtr RhpGetThunkStubsBlockAddress(IntPtr thunkDataAddress) + ;; + LEAF_ENTRY_NO_PDATA RhpGetThunkStubsBlockAddress + mov x12, PAGE_SIZE - 1 + bic x0, x0, x12 + mov x12, PAGE_SIZE + sub x0, x0, x12 + ret + LEAF_END_NO_PDATA RhpGetThunkStubsBlockAddress + + END diff --git a/external/corert/src/Native/Runtime/arm64/UniversalTransition.asm b/external/corert/src/Native/Runtime/arm64/UniversalTransition.asm new file mode 100644 index 0000000000..4c21397f7a --- /dev/null +++ b/external/corert/src/Native/Runtime/arm64/UniversalTransition.asm @@ -0,0 +1,162 @@ +;; 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. + +#include "AsmMacros.h" + +#ifdef _DEBUG +#define TRASH_SAVED_ARGUMENT_REGISTERS +#endif + +#ifdef TRASH_SAVED_ARGUMENT_REGISTERS + EXTERN RhpIntegerTrashValues + EXTERN RhpFpTrashValues +#endif ;; TRASH_SAVED_ARGUMENT_REGISTERS + +;; Padding to account for the odd number of saved integer registers +#define ALIGNMENT_PADDING_SIZE (8) + +#define COUNT_ARG_REGISTERS (9) +#define INTEGER_REGISTER_SIZE (8) +#define ARGUMENT_REGISTERS_SIZE (COUNT_ARG_REGISTERS * INTEGER_REGISTER_SIZE) + +;; Largest return block is 4 doubles +#define RETURN_BLOCK_SIZE (32) + +#define COUNT_FLOAT_ARG_REGISTERS (8) +#define FLOAT_REGISTER_SIZE (8) +#define FLOAT_ARG_REGISTERS_SIZE (COUNT_FLOAT_ARG_REGISTERS * FLOAT_REGISTER_SIZE) + +#define PUSHED_LR_SIZE (8) +#define PUSHED_FP_SIZE (8) + +;; +;; From CallerSP to ChildSP, the stack frame is composed of the following adjacent regions: +;; +;; ALIGNMENT_PADDING_SIZE +;; ARGUMENT_REGISTERS_SIZE +;; RETURN_BLOCK_SIZE +;; FLOAT_ARG_REGISTERS_SIZE +;; PUSHED_LR_SIZE +;; PUSHED_FP_SIZE +;; + +#define DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK (PUSHED_FP_SIZE + PUSHED_LR_SIZE + FLOAT_ARG_REGISTERS_SIZE) + +#define STACK_SIZE (ALIGNMENT_PADDING_SIZE + ARGUMENT_REGISTERS_SIZE + RETURN_BLOCK_SIZE + FLOAT_ARG_REGISTERS_SIZE + \ + PUSHED_LR_SIZE + PUSHED_FP_SIZE) + +#define FLOAT_ARG_OFFSET (PUSHED_FP_SIZE + PUSHED_LR_SIZE) +#define ARGUMENT_REGISTERS_OFFSET (FLOAT_ARG_OFFSET + FLOAT_ARG_REGISTERS_SIZE + RETURN_BLOCK_SIZE) + +;; +;; RhpUniversalTransition +;; +;; At input to this function, x0-8, d0-7 and the stack may contain any number of arguments. +;; +;; In addition, there are 2 extra arguments passed in the intra-procedure-call scratch register: +;; xip0 will contain the managed function that is to be called by this transition function +;; xip1 will contain the pointer sized extra argument to the managed function +;; +;; When invoking the callee: +;; +;; x0 shall contain a pointer to the TransitionBlock +;; x1 shall contain the value that was in xip1 at entry to this function +;; +;; Frame layout is: +;; +;; {StackPassedArgs} ChildSP+0C0 CallerSP+000 +;; {AlignmentPad (0x8 bytes)} ChildSP+0B8 CallerSP-008 +;; {IntArgRegs (x0-x8) (0x48 bytes)} ChildSP+070 CallerSP-050 +;; {ReturnBlock (0x20 bytes)} ChildSP+050 CallerSP-070 +;; -- The base address of the Return block is the TransitionBlock pointer, the floating point args are +;; in the neg space of the TransitionBlock pointer. Note that the callee has knowledge of the exact +;; layout of all pieces of the frame that lie at or above the pushed floating point registers. +;; {FpArgRegs (d0-d7) (0x40 bytes)} ChildSP+010 CallerSP-0B0 +;; {PushedLR} ChildSP+008 CallerSP-0B8 +;; {PushedFP} ChildSP+000 CallerSP-0C0 +;; +;; NOTE: If the frame layout ever changes, the C++ UniversalTransitionStackFrame structure +;; must be updated as well. +;; +;; NOTE: The callee receives a pointer to the base of the ReturnBlock, and the callee has +;; knowledge of the exact layout of all pieces of the frame that lie at or above the pushed +;; FpArgRegs. +;; +;; NOTE: The stack walker guarantees that conservative GC reporting will be applied to +;; everything between the base of the ReturnBlock and the top of the StackPassedArgs. +;; + + TEXTAREA + + MACRO + UNIVERSAL_TRANSITION $FunctionName + + NESTED_ENTRY Rhp$FunctionName + + ;; FP and LR registers + PROLOG_SAVE_REG_PAIR fp, lr, #-STACK_SIZE! ;; Push down stack pointer and store FP and LR + + ;; Floating point registers + stp d0, d1, [sp, #(FLOAT_ARG_OFFSET )] + stp d2, d3, [sp, #(FLOAT_ARG_OFFSET + 0x10)] + stp d4, d5, [sp, #(FLOAT_ARG_OFFSET + 0x20)] + stp d6, d7, [sp, #(FLOAT_ARG_OFFSET + 0x30)] + + ;; Space for return buffer data (0x40 bytes) + + ;; Save argument registers + stp x0, x1, [sp, #(ARGUMENT_REGISTERS_OFFSET )] + stp x2, x3, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x10)] + stp x4, x5, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x20)] + stp x6, x7, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x30)] + str x8, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x40)] + +#ifdef TRASH_SAVED_ARGUMENT_REGISTERS + ;; ARM64TODO +#endif // TRASH_SAVED_ARGUMENT_REGISTERS + + add x0, sp, #DISTANCE_FROM_CHILDSP_TO_RETURN_BLOCK ;; First parameter to target function is a pointer to the return block + mov x8, x0 ;; Arm64 calling convention: Address of return block shall be passed in x8 + mov x1, xip1 ;; Second parameter to target function + blr xip0 + + ;; We cannot make the label public as that tricks DIA stackwalker into thinking + ;; it's the beginning of a method. For this reason we export an auxiliary variable + ;; holding the address instead. + EXPORT_POINTER_TO_ADDRESS PointerToReturnFrom$FunctionName + + ;; Move the result (the target address) to x12 so it doesn't get overridden when we restore the + ;; argument registers. + mov x12, x0 + + ;; Restore floating point registers + ldp d0, d1, [sp, #(FLOAT_ARG_OFFSET )] + ldp d2, d3, [sp, #(FLOAT_ARG_OFFSET + 0x10)] + ldp d4, d5, [sp, #(FLOAT_ARG_OFFSET + 0x20)] + ldp d6, d7, [sp, #(FLOAT_ARG_OFFSET + 0x30)] + + ;; Restore the argument registers + ldp x0, x1, [sp, #(ARGUMENT_REGISTERS_OFFSET )] + ldp x2, x3, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x10)] + ldp x4, x5, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x20)] + ldp x6, x7, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x30)] + ldr x8, [sp, #(ARGUMENT_REGISTERS_OFFSET + 0x40)] + + ;; Restore FP and LR registers, and free the allocated stack block + EPILOG_RESTORE_REG_PAIR fp, lr, #STACK_SIZE! + + ;; Tailcall to the target address. + EPILOG_NOP ret x12 + + NESTED_END Rhp$FunctionName + + MEND + + ; To enable proper step-in behavior in the debugger, we need to have two instances + ; of the thunk. For the first one, the debugger steps into the call in the function, + ; for the other, it steps over it. + UNIVERSAL_TRANSITION UniversalTransition + UNIVERSAL_TRANSITION UniversalTransition_DebugStepTailCall + + END diff --git a/external/corert/src/Native/Runtime/arm64/WriteBarriers.asm b/external/corert/src/Native/Runtime/arm64/WriteBarriers.asm new file mode 100644 index 0000000000..d7e8984e5b --- /dev/null +++ b/external/corert/src/Native/Runtime/arm64/WriteBarriers.asm @@ -0,0 +1,319 @@ +;; 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. + +;; +;; Define the helpers used to implement the write barrier required when writing an object reference into a +;; location residing on the GC heap. Such write barriers allow the GC to optimize which objects in +;; non-ephemeral generations need to be scanned for references to ephemeral objects during an ephemeral +;; collection. +;; + +#include "AsmMacros.h" + + TEXTAREA + +;; Macro used to copy contents of newly updated GC heap locations to a shadow copy of the heap. This is used +;; during garbage collections to verify that object references where never written to the heap without using a +;; write barrier. Note that we're potentially racing to update the shadow heap while other threads are writing +;; new references to the real heap. Since this can't be solved perfectly without critical sections around the +;; entire update process, we instead update the shadow location and then re-check the real location (as two +;; ordered operations) and if there is a disparity we'll re-write the shadow location with a special value +;; (INVALIDGCVALUE) which disables the check for that location. Since the shadow heap is only validated at GC +;; time and these write barrier operations are atomic wrt to GCs this is sufficient to guarantee that the +;; shadow heap contains only valid copies of real heap values or INVALIDGCVALUE. +#ifdef WRITE_BARRIER_CHECK + + SETALIAS g_GCShadow, ?g_GCShadow@@3PEAEEA + SETALIAS g_GCShadowEnd, ?g_GCShadowEnd@@3PEAEEA + EXTERN $g_GCShadow + EXTERN $g_GCShadowEnd + +INVALIDGCVALUE EQU 0xCCCCCCCD + + MACRO + ;; On entry: + ;; $destReg: location to be updated + ;; $refReg: objectref to be stored + ;; + ;; On exit: + ;; x9,x10: trashed + ;; other registers are preserved + ;; + UPDATE_GC_SHADOW $destReg, $refReg + + ;; If g_GCShadow is 0, don't perform the check. + adrp x9, $g_GCShadow + ldr x9, [x9, $g_GCShadow] + cbz x9, %ft1 + + ;; Save $destReg since we're about to modify it (and we need the original value both within the macro and + ;; once we exit the macro). + mov x10, $destReg + + ;; Transform $destReg into the equivalent address in the shadow heap. + adrp x9, g_lowest_address + ldr x9, [x9, g_lowest_address] + subs $destReg, $destReg, x9 + blt %ft0 + + adrp x9, $g_GCShadow + ldr x9, [x9, $g_GCShadow] + add $destReg, $destReg, x9 + + adrp x9, $g_GCShadowEnd + ldr x9, [x9, $g_GCShadowEnd] + cmp $destReg, x9 + bgt %ft0 + + ;; Update the shadow heap. + str $refReg, [$destReg] + + ;; The following read must be strongly ordered wrt to the write we've just performed in order to + ;; prevent race conditions. + dmb ish + + ;; Now check that the real heap location still contains the value we just wrote into the shadow heap. + mov x9, x10 + ldr x9, [x9] + cmp x9, $refReg + beq %ft0 + + ;; Someone went and updated the real heap. We need to invalidate the shadow location since we can't + ;; guarantee whose shadow update won. + MOVL64 x9, INVALIDGCVALUE, 0 + str x9, [$destReg] + +0 + ;; Restore original $destReg value + mov $destReg, x10 + +1 + MEND + +#else // WRITE_BARRIER_CHECK + + MACRO + UPDATE_GC_SHADOW $destReg, $refReg + MEND + +#endif // WRITE_BARRIER_CHECK + +;; There are several different helpers used depending on which register holds the object reference. Since all +;; the helpers have identical structure we use a macro to define this structure. Two arguments are taken, the +;; name of the register that points to the location to be updated and the name of the register that holds the +;; object reference (this should be in upper case as it's used in the definition of the name of the helper). + +;; Define a sub-macro first that expands to the majority of the barrier implementation. This is used below for +;; some interlocked helpers that need an inline barrier. + MACRO + ;; On entry: + ;; $destReg: location to be updated + ;; $refReg: objectref to be stored + ;; + ;; On exit: + ;; $destReg: trashed + ;; x9: trashed + ;; + INSERT_UNCHECKED_WRITE_BARRIER_CORE $destReg, $refReg + + ;; Update the shadow copy of the heap with the same value just written to the same heap. (A no-op unless + ;; we're in a debug build and write barrier checking has been enabled). + UPDATE_GC_SHADOW $destReg, $refReg + + ;; We can skip the card table write if the reference is to + ;; an object not on the epehemeral segment. + adrp x9, g_ephemeral_low + ldr x9, [x9, g_ephemeral_low] + cmp $refReg, x9 + blt %ft0 + + adrp x9, g_ephemeral_high + ldr x9, [x9, g_ephemeral_high] + cmp $refReg, x9 + bge %ft0 + + ;; Set this object's card, if it hasn't already been set. + adrp x9, g_card_table + ldr x9, [x9, g_card_table] + add $destReg, x9, $destReg lsr #11 + + ;; Check that this card hasn't already been written. Avoiding useless writes is a big win on + ;; multi-proc systems since it avoids cache thrashing. + ldrb w9, [$destReg] + cmp x9, 0xFF + beq %ft0 + + mov x9, 0xFF + strb w9, [$destReg] + +0 + ;; Exit label + MEND + + MACRO + ;; On entry: + ;; $destReg: location to be updated + ;; $refReg: objectref to be stored + ;; + ;; On exit: + ;; $destReg: trashed + ;; x9: trashed + ;; + INSERT_CHECKED_WRITE_BARRIER_CORE $destReg, $refReg + + ;; The "check" of this checked write barrier - is $destReg + ;; within the heap? if no, early out. + adrp x9, g_lowest_address + ldr x9, [x9, g_lowest_address] + cmp $destReg, x9 + blt %ft0 + + adrp x9, g_highest_address + ldr x9, [x9, g_highest_address] + cmp $destReg, x9 + bgt %ft0 + + INSERT_UNCHECKED_WRITE_BARRIER_CORE $destReg, $refReg + +0 + ;; Exit label + MEND + +;; RhpCheckedAssignRef(Object** dst, Object* src) +;; +;; Write barrier for writes to objects that may reside +;; on the managed heap. +;; +;; On entry: +;; x0 : the destination address (LHS of the assignment). +;; May not be an object reference (hence the checked). +;; x1 : the object reference (RHS of the assignment). +;; On exit: +;; x1 : trashed +;; x9 : trashed + LEAF_ENTRY RhpCheckedAssignRef + ALTERNATE_ENTRY RhpCheckedAssignRefAVLocation + ALTERNATE_ENTRY RhpCheckedAssignRefX1 + ALTERNATE_ENTRY RhpCheckedAssignRefX1AVLocation + + stlr x1, [x0] + + INSERT_CHECKED_WRITE_BARRIER_CORE x0, x1 + + ret + + LEAF_END RhpCheckedAssignRef + +;; RhpAssignRef(Object** dst, Object* src) +;; +;; Write barrier for writes to objects that are known to +;; reside on the managed heap. +;; +;; On entry: +;; x0 : the destination address (LHS of the assignment). +;; x1 : the object reference (RHS of the assignment). +;; On exit: +;; x1 : trashed +;; x9 : trashed + LEAF_ENTRY RhpAssignRef + ALTERNATE_ENTRY RhpAssignRefAVLocation + ALTERNATE_ENTRY RhpAssignRefX1 + ALTERNATE_ENTRY RhpAssignRefX1AVLocation + + stlr x1, [x0] + + INSERT_UNCHECKED_WRITE_BARRIER_CORE x0, x1 + + ret + + LEAF_END RhpAssignRef + +;; Interlocked operation helpers where the location is an objectref, thus requiring a GC write barrier upon +;; successful updates. + +;; WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen at RhpCheckedLockCmpXchgAVLocation +;; - Function "UnwindWriteBarrierToCaller" assumes no registers where pushed and LR contains the return address + +;; RhpCheckedLockCmpXchg(Object** dest, Object* value, Object* comparand) +;; +;; Interlocked compare exchange on objectref. +;; +;; On entry: +;; x0: pointer to objectref +;; x1: exchange value +;; x2: comparand +;; +;; On exit: +;; x0: original value of objectref +;; x9: trashed +;; x10: trashed +;; + LEAF_ENTRY RhpCheckedLockCmpXchg + ALTERNATE_ENTRY RhpCheckedLockCmpXchgAVLocation + +CmpXchgRetry + ;; Check location value is what we expect. + ldaxr x10, [x0] + cmp x10, x2 + bne CmpXchgNoUpdate + + ;; Current value matches comparand, attempt to update with the new value. + stlxr w9, x1, [x0] + cbnz w9, CmpXchgRetry + + ;; We've successfully updated the value of the objectref so now we need a GC write barrier. + ;; The following barrier code takes the destination in x0 and the value in x1 so the arguments are + ;; already correctly set up. + + INSERT_CHECKED_WRITE_BARRIER_CORE x0, x1 + +CmpXchgNoUpdate + ;; x10 still contains the original value. + mov x0, x10 + ret lr + + LEAF_END RhpCheckedLockCmpXchg + +;; WARNING: Code in EHHelpers.cpp makes assumptions about write barrier code, in particular: +;; - Function "InWriteBarrierHelper" assumes an AV due to passed in null pointer will happen within at RhpCheckedXchgAVLocation +;; - Function "UnwindWriteBarrierToCaller" assumes no registers where pushed and LR contains the return address + +;; RhpCheckedXchg(Object** destination, Object* value) +;; +;; Interlocked exchange on objectref. +;; +;; On entry: +;; x0: pointer to objectref +;; x1: exchange value +;; +;; On exit: +;; x0: original value of objectref +;; x9: trashed +;; x10: trashed +;; + LEAF_ENTRY RhpCheckedXchg + ALTERNATE_ENTRY RhpCheckedXchgAVLocation + +ExchangeRetry + ;; Read the existing memory location. + ldaxr x10, [x0] + + ;; Attempt to update with the new value. + stlxr w9, x1, [x0] + cbnz w9, ExchangeRetry + + ;; We've successfully updated the value of the objectref so now we need a GC write barrier. + ;; The following barrier code takes the destination in x0 and the value in x1 so the arguments are + ;; already correctly set up. + + INSERT_CHECKED_WRITE_BARRIER_CORE x0, x1 + + ;; x10 still contains the original value. + mov x0, x10 + ret + + LEAF_END RhpCheckedXchg + + end diff --git a/external/corert/src/Native/Runtime/coreclr/bitvector.h b/external/corert/src/Native/Runtime/coreclr/bitvector.h new file mode 100644 index 0000000000..0f273dbacd --- /dev/null +++ b/external/corert/src/Native/Runtime/coreclr/bitvector.h @@ -0,0 +1,462 @@ +// 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. + + +/***************************************************************************/ +/* BitVector.h */ +/***************************************************************************/ +// Routines to support a growable bitvector +/***************************************************************************/ + +#ifndef BITVECTOR_H +#define BITVECTOR_H 1 + +#ifndef LIMITED_METHOD_CONTRACT +#define LIMITED_METHOD_CONTRACT +#define UNDEF_LIMITED_METHOD_CONTRACT +#endif + +#ifndef WRAPPER_NO_CONTRACT +#define WRAPPER_NO_CONTRACT +#define UNDEF_WRAPPER_NO_CONTRACT +#endif + +#ifndef SUPPORTS_DAC +#define SUPPORTS_DAC +#define UNDEF_SUPPORTS_DAC +#endif + +#ifndef _ASSERTE +#define _ASSERTE(x) +#define UNDEF_ASSERTE +#endif + +#define USE_BITVECTOR 1 +#if USE_BITVECTOR + +/* The bitvector class is meant to be a drop in replacement for an integer + (that is you use it like an integer), however it grows as needed. + + Features: + plug compatible with normal integers; + grows as needed + Optimized for the small case when the vector fits in machine word + Uses one machine word if vector fits in machine word (minus a bit) + + Some caveates: + You should use mutator operators &=, |= ... instead of the + non-mutators whenever possible to avoid creating a temps + + Specifically did NOT supply automatic coersions to + and from short types so that the programmer is aware of + when code was being injected on his behalf. The upshot of this + is that you have to use the BitVector() toUnsigned() to convert +*/ + +/***************************************************************************/ + +class BitVector { + // Set this to be unsigned char to do testing, should be UINT_PTR for real life + + typedef uint32_t BOOL; + typedef uintptr_t ChunkType; // The size of integer type that the machine can operate on directly +// typedef BYTE ChunkType; // Use for testing + + // Maximum number of bits in our bitvector +#define MAX_PTRARG_OFS 1024 + + enum { + IS_BIG = 1, // The low bit is used to discrimate m_val and m_vals + CHUNK_BITS = sizeof(ChunkType)*8, // The number of bits that we can manipuate as a chunk + SMALL_BITS = CHUNK_BITS - 1, // The number of bits we can fit in the small representation +// SMALL_BITS = 5, // TESTING ONLY: The number of bits we can fit in the small representation + VALS_COUNT = MAX_PTRARG_OFS / CHUNK_BITS, // The number of ChunkType elements in the Vals array + }; + +public: + BitVector() + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + + m_val = 0; + } + + BOOL isBig() const + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + + return ((m_val & IS_BIG) != 0); + } + + void toBig() + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + + if (!isBig()) + { + doBigInit(smallBits()); + } + } + + explicit BitVector(ChunkType arg) + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + + if (arg > MaxVal) + { + doBigInit(arg); + } + else + { + m_val = ChunkType(arg << 1); + } + } + + BitVector(ChunkType arg, UINT shift) + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + + if ((arg > MaxVal) || (shift >= SMALL_BITS) || (arg > (MaxVal >> shift))) + { + doBigInit(arg); + doBigLeftShiftAssign(shift); + } + else + { + m_val = ChunkType(arg << (shift+1)); + } + } + +#define CONSTRUCT_ptrArgTP(arg,shift) BitVector((arg), (shift)) + + BitVector(const BitVector& arg) + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + + if (arg.isBig()) + { + doBigInit(arg); + } + else + { + m_val = arg.m_val; + } + } + + void operator <<=(unsigned shift) + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + + if ((m_val == 0) || (shift == 0)) // Zero is a special case, don't need to do anything + return; + + if (isBig() || (shift >= SMALL_BITS) || (m_val > (MaxVal >> (shift-1)))) + { + doBigLeftShiftAssign(shift); + } + else + { + m_val <<= shift; + } + } + + void operator >>=(unsigned shift) + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + + if (isBig()) + { + doBigRightShiftAssign(shift); + } + else + { + m_val >>= shift; + m_val &= ~IS_BIG; // clear the isBig bit if it got set + } + } + + void operator |=(const BitVector& arg) + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + + if (((m_val | arg.m_val) & IS_BIG) != 0) + { + doBigOrAssign(arg); + } + else + { + m_val |= arg.m_val; + } + } + + // Note that that is set difference, not subtration + void operator -=(const BitVector& arg) + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + + if (((m_val | arg.m_val) & IS_BIG) != 0) + { + doBigDiffAssign(arg); + } + else + { + m_val &= ~arg.m_val; + } + } + + void operator &=(const BitVector& arg) + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + + if (((m_val | arg.m_val) & IS_BIG) != 0) + { + doBigAndAssign(arg); + } + else + { + m_val &= arg.m_val; + } + } + + friend void setDiff(BitVector& target, const BitVector& arg) + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + + target -= arg; + } + + friend BOOL intersect(const BitVector& arg1, const BitVector& arg2) + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + + if (((arg1.m_val | arg2.m_val) & IS_BIG) != 0) + { + return arg1.doBigIntersect(arg2); + } + else + { + return ((arg1.m_val & arg2.m_val) != 0); + } + } + + BOOL operator ==(const BitVector& arg) const + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + + if ((m_val | arg.m_val) & IS_BIG) + { + return doBigEquals(arg); + } + else + { + return m_val == arg.m_val; + } + } + + BOOL operator !=(const BitVector& arg) const + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + + return !(*this == arg); + } + + friend ChunkType toUnsigned(const BitVector& arg) + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + + if (arg.isBig()) + { + return arg.m_vals.m_chunks[0]; // Note truncation + } + else + { + return arg.smallBits(); + } + } + + // Note that we require the invariant that zero is always stored in the + // small form so that this works bitvector is zero iff (m_val == 0) + friend BOOL isZero(const BitVector& arg) + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + + return arg.m_val == 0; + } + + /* currently only used in asserts */ + BitVector operator &(const BitVector& arg) const + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + + BitVector ret = *this; + ret &= arg; + return ret; + } + + int NumBits() const; + +private: + + static const ChunkType MaxVal = ((ChunkType)1 << SMALL_BITS) - 1; // Maximum value that can be stored in m_val + + // This is the structure that we use when the bit vector overflows. + // It is a simple vector. + struct Vals { + unsigned m_encodedLength; // An encoding of the current length of the 'm_chunks' array + ChunkType m_chunks[VALS_COUNT]; + + BOOL isBig() const + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + + return ((m_encodedLength & IS_BIG) != 0); + } + + unsigned GetLength() const + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + + if (isBig()) + { + unsigned length = (m_encodedLength >> 1); + _ASSERTE(length > 0); + return length; + } + else + { + return 0; + } + } + + void SetLength(unsigned length) + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + + _ASSERTE(length > 0); + _ASSERTE(length <= VALS_COUNT); + + m_encodedLength = (ChunkType) (length << 1); + m_encodedLength |= (ChunkType) IS_BIG; + } + }; + + // + // This is the instance data for the bitvector + // + // We discrimininate on this + union { + ChunkType m_val; // if m_val bit 0 is false, then bits 1-N are the bit vector + Vals m_vals; // if m_val bit 1 is true, then use Vals + }; + + + ChunkType smallBits() const + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + + _ASSERTE(!isBig()); + return (m_val >> 1); + } + +#ifdef STRIKE + void doBigInit(ChunkType arg) {} +#else + void doBigInit(ChunkType arg); +#endif + void doBigInit(const BitVector& arg); + void doBigLeftShiftAssign(unsigned arg); + void doBigRightShiftAssign(unsigned arg); + void doBigDiffAssign(const BitVector&); + void doBigAndAssign(const BitVector&); + void doBigOrAssign(const BitVector& arg); + BOOL doBigEquals(const BitVector&) const; + BOOL doBigIntersect(const BitVector&) const; +}; + +typedef BitVector ptrArgTP; + +#else // !USE_BITVECTOR + +typedef unsigned __int64 ptrArgTP; + + // Maximum number of bits in our bitvector +#define MAX_PTRARG_OFS (sizeof(ptrArgTP) * 8) + +#define CONSTRUCT_ptrArgTP(arg,shift) (((ptrArgTP) (arg)) << (shift)) + +inline BOOL isZero(const ptrArgTP& arg) +{ + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + return (arg == 0); +} + +inline ptrArgTP toUnsigned(const ptrArgTP& arg) +{ + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + return arg; +} + +inline void setDiff(ptrArgTP& target, const ptrArgTP& arg) +{ + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + + target &= ~arg; +} + +inline BOOL intersect(const ptrArgTP arg1, const ptrArgTP arg2) +{ + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + + return ((arg1 & arg2) != 0); +} + +#endif // !USE_BITVECTOR + +#ifdef UNDEF_LIMITED_METHOD_CONTRACT +#undef LIMITED_METHOD_CONTRACT +#undef UNDEF_LIMITED_METHOD_CONTRACT +#endif + +#ifdef UNDEF_WRAPPER_NO_CONTRACT +#undef WRAPPER_NO_CONTRACT +#undef UNDEF_WRAPPER_NO_CONTRACT +#endif + +#ifdef UNDEF_SUPPORTS_DAC +#undef SUPPORTS_DAC +#undef UNDEF_SUPPORTS_DAC +#endif + +#ifdef UNDEF_ASSERTE +#undef _ASSERTE +#undef UNDEF_ASSERTE +#endif + +#endif // BITVECTOR_H diff --git a/external/corert/src/Native/Runtime/coreclr/gcinfodecoder.cpp b/external/corert/src/Native/Runtime/coreclr/gcinfodecoder.cpp index 8e538d2a06..a7bc3d2047 100644 --- a/external/corert/src/Native/Runtime/coreclr/gcinfodecoder.cpp +++ b/external/corert/src/Native/Runtime/coreclr/gcinfodecoder.cpp @@ -1680,7 +1680,7 @@ OBJECTREF* GcInfoDecoder::GetCapturedRegister( PREGDISPLAY pRD ) { - _ASSERTE(regNum >= 0 && regNum <= 28); + _ASSERTE(regNum >= 0 && regNum < GEN_REG_COUNT); // The fields of CONTEXT are in the same order as // the processor encoding numbers. diff --git a/external/corert/src/Native/Runtime/coreclr/gcinfotypes.h b/external/corert/src/Native/Runtime/coreclr/gcinfotypes.h index c802d97ec6..9b949b55f4 100644 --- a/external/corert/src/Native/Runtime/coreclr/gcinfotypes.h +++ b/external/corert/src/Native/Runtime/coreclr/gcinfotypes.h @@ -18,12 +18,11 @@ #define BITS_PER_SIZE_T ((int)sizeof(size_t)*8) - //-------------------------------------------------------------------------------- -// It turns out, that ((size_t)x) << y == x, when y is not a literal +// It turns out, that ((size_t)x) << y == x, when y is not a literal // and its value is BITS_PER_SIZE_T // I guess the processor only shifts of the right operand modulo BITS_PER_SIZE_T -// In many cases, we want the above operation to yield 0, +// In many cases, we want the above operation to yield 0, // hence the following macros //-------------------------------------------------------------------------------- __forceinline size_t SAFE_SHIFT_LEFT(size_t x, size_t count) @@ -346,7 +345,7 @@ enum infoHdrAdjustConstants { SET_RET_KIND_MAX = 4, // 2 bits for ReturnKind ADJ_ENCODING_MAX = 0x7f, // Maximum valid encoding in a byte // Also used to mask off next bit from each encoding byte. - MORE_BYTES_TO_FOLLOW = 0x80 // If the High-bit of a header or adjustment byte + MORE_BYTES_TO_FOLLOW = 0x80 // If the High-bit of a header or adjustment byte // is set, then there are more adjustments to follow. }; @@ -416,7 +415,7 @@ enum infoHdrAdjust2 { #define INVALID_ARGTAB_OFFSET 0 -#include +#include "pshpack1.h" // Working set optimization: saving 12 * 128 = 1536 bytes in infoHdrShortcut struct InfoHdr; @@ -524,7 +523,7 @@ union CallPattern { unsigned val; }; -#include +#include "poppack.h" #define IH_MAX_PROLOG_SIZE (51) @@ -543,8 +542,8 @@ inline void GetInfoHdr(int index, InfoHdr * header) PTR_CBYTE FASTCALL decodeHeader(PTR_CBYTE table, UINT32 version, InfoHdr* header); -BYTE FASTCALL encodeHeaderFirst(const InfoHdr& header, InfoHdr* state, int* more, int *pCached); -BYTE FASTCALL encodeHeaderNext(const InfoHdr& header, InfoHdr* state, BYTE &codeSet); +uint8_t FASTCALL encodeHeaderFirst(const InfoHdr& header, InfoHdr* state, int* more, int *pCached); +uint8_t FASTCALL encodeHeaderNext(const InfoHdr& header, InfoHdr* state, uint8_t &codeSet); size_t FASTCALL decodeUnsigned(PTR_CBYTE src, unsigned* value); size_t FASTCALL decodeUDelta(PTR_CBYTE src, unsigned* value, unsigned lastValue); @@ -569,7 +568,7 @@ void FASTCALL decodeCallPattern(int pattern, unsigned * argMask, unsigned * codeDelta); -#endif // _TARGET_86_ +#endif // _TARGET_86_ // Stack offsets must be 8-byte aligned, so we use this unaligned // offset to represent that the method doesn't have a security object diff --git a/external/corert/src/Native/Runtime/dllmain.cpp b/external/corert/src/Native/Runtime/dllmain.cpp deleted file mode 100644 index fa82bed0fd..0000000000 --- a/external/corert/src/Native/Runtime/dllmain.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// 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. -#include "common.h" -#include "CommonTypes.h" -#include "CommonMacros.h" -#include "daccess.h" -#include "PalRedhawkCommon.h" -#include "PalRedhawk.h" -#include "gcrhinterface.h" - -#include "rhassert.h" -#include "slist.h" -#include "varint.h" -#include "regdisplay.h" -#include "StackFrameIterator.h" -#include "thread.h" -#include "holder.h" -#include "Crst.h" -#include "event.h" - -bool InitDLL(HANDLE hPalInstance); -bool UninitDLL(HANDLE hPalInstance); -void DllThreadAttach(HANDLE hPalInstance); -void DllThreadDetach(); - -EXTERN_C UInt32_BOOL WINAPI RtuDllMain(HANDLE hPalInstance, UInt32 dwReason, void* /*pvReserved*/) -{ - switch (dwReason) - { - case DLL_PROCESS_ATTACH: - { - STARTUP_TIMELINE_EVENT(PROCESS_ATTACH_BEGIN); - - if (!InitDLL(hPalInstance)) - return FALSE; - - DllThreadAttach(hPalInstance); - STARTUP_TIMELINE_EVENT(PROCESS_ATTACH_COMPLETE); - return TRUE; - } - break; - - case DLL_PROCESS_DETACH: - UninitDLL(hPalInstance); - break; - - case DLL_THREAD_ATTACH: - DllThreadAttach(hPalInstance); - break; - - case DLL_THREAD_DETACH: - DllThreadDetach(); - break; - - } - - return TRUE; -} - - - diff --git a/external/corert/src/Native/Runtime/gcdump.cpp b/external/corert/src/Native/Runtime/gcdump.cpp index ed4ee98cfa..20eeae53c9 100644 --- a/external/corert/src/Native/Runtime/gcdump.cpp +++ b/external/corert/src/Native/Runtime/gcdump.cpp @@ -244,6 +244,18 @@ void GCDump::DumpCallsiteString(UInt32 callsiteOffset, PTR_UInt8 pbCallsiteStrin if (b & CSR_MASK_R6) { gcPrintf("R6 "); count++; } if (b & CSR_MASK_R7) { gcPrintf("R7 "); count++; } if (b & CSR_MASK_R8) { gcPrintf("R8 "); count++; } +#elif defined(_TARGET_ARM64_) + // ARM64TODO: not all of these are needed? + if (b & CSR_MASK_X19) { gcPrintf("X19 "); count++; } + if (b & CSR_MASK_X20) { gcPrintf("X20 "); count++; } + if (b & CSR_MASK_X21) { gcPrintf("X21 "); count++; } + if (b & CSR_MASK_X22) { gcPrintf("X22 "); count++; } + if (b & CSR_MASK_X23) { gcPrintf("X23 "); count++; } + if (b & CSR_MASK_X24) { gcPrintf("X24 "); count++; } + if (b & CSR_MASK_X25) { gcPrintf("X25 "); count++; } + if (b & CSR_MASK_X26) { gcPrintf("X26 "); count++; } + if (b & CSR_MASK_X27) { gcPrintf("X27 "); count++; } + if (b & CSR_MASK_X28) { gcPrintf("X28 "); count++; } #else // _ARM_ if (b & CSR_MASK_RBX) { gcPrintf("RBX "); count++; } if (b & CSR_MASK_RSI) { gcPrintf("RSI "); count++; } @@ -273,15 +285,28 @@ void GCDump::DumpCallsiteString(UInt32 callsiteOffset, PTR_UInt8 pbCallsiteStrin case CSR_NUM_R9: regName = "R9"; break; case CSR_NUM_R10: regName = "R10"; break; case CSR_NUM_R11: regName = "R11"; break; +#elif defined(_TARGET_ARM64_) + case CSR_NUM_X19: regName = "X19"; break; + case CSR_NUM_X20: regName = "X20"; break; + case CSR_NUM_X21: regName = "X21"; break; + case CSR_NUM_X22: regName = "X22"; break; + case CSR_NUM_X23: regName = "X23"; break; + case CSR_NUM_X24: regName = "X24"; break; + case CSR_NUM_X25: regName = "X25"; break; + case CSR_NUM_X26: regName = "X26"; break; + case CSR_NUM_X27: regName = "X27"; break; + case CSR_NUM_X28: regName = "X28"; break; #else // _ARM_ case CSR_NUM_RBX: regName = "RBX"; break; case CSR_NUM_RSI: regName = "RSI"; break; case CSR_NUM_RDI: regName = "RDI"; break; case CSR_NUM_RBP: regName = "RBP"; break; +#ifdef _TARGET_AMD64_ case CSR_NUM_R12: regName = "R12"; break; case CSR_NUM_R13: regName = "R13"; break; case CSR_NUM_R14: regName = "R14"; break; case CSR_NUM_R15: regName = "R15"; break; +#endif // _TARGET_AMD64_ #endif // _ARM_ } gcPrintf("%02x | 3 %s%s%s \n", b, regName, interior, pinned); diff --git a/external/corert/src/Native/Runtime/gcenv.h b/external/corert/src/Native/Runtime/gcenv.h index f5a3697214..557103dbce 100644 --- a/external/corert/src/Native/Runtime/gcenv.h +++ b/external/corert/src/Native/Runtime/gcenv.h @@ -241,6 +241,15 @@ typedef DPTR(uint32_t) PTR_uint32_t; enum CLRDataEnumMemoryFlags : int; +enum ThreadType +{ + ThreadType_GC = 137, +}; + +#undef ClrFlsSetThreadType +#define ClrFlsSetThreadType(threadType) SetGCSpecialThread(threadType) +void SetGCSpecialThread(ThreadType threadType); + #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE) // Note this is not updated in a thread safe way so the value may not be accurate. We get // it accurately in full GCs if the handle count is requested. diff --git a/external/corert/src/Native/Runtime/gcrhenv.cpp b/external/corert/src/Native/Runtime/gcrhenv.cpp index e6ebc1a5fc..8a50a25cfe 100644 --- a/external/corert/src/Native/Runtime/gcrhenv.cpp +++ b/external/corert/src/Native/Runtime/gcrhenv.cpp @@ -598,12 +598,17 @@ EXTERN_C UInt32_BOOL g_fGcStressStarted = UInt32_FALSE; // UInt32_BOOL because a // static void RedhawkGCInterface::StressGc() { - if (!g_fGcStressStarted || GetThread()->IsSuppressGcStressSet() || GetThread()->IsDoNotTriggerGcSet()) + // The GarbageCollect operation below may trash the last win32 error. We save the error here so that it can be + // restored after the GC operation; + Int32 lastErrorOnEntry = PalGetLastError(); + + if (g_fGcStressStarted && !GetThread()->IsSuppressGcStressSet() && !GetThread()->IsDoNotTriggerGcSet()) { - return; + GCHeapUtilities::GetGCHeap()->GarbageCollect(); } - GCHeapUtilities::GetGCHeap()->GarbageCollect(); + // Restore the saved error + PalSetLastError(lastErrorOnEntry); } #endif // FEATURE_GC_STRESS @@ -889,6 +894,16 @@ void RedhawkGCInterface::SetLastAllocEEType(EEType * pEEType) tls_pLastAllocationEEType = pEEType; } +void RedhawkGCInterface::DestroyTypedHandle(void * handle) +{ + ::DestroyTypedHandle((OBJECTHANDLE)handle); +} + +void* RedhawkGCInterface::CreateTypedHandle(void* pObject, int type) +{ + return (void*)::CreateTypedHandle(g_HandleTableMap.pBuckets[0]->pTable[GetCurrentThreadHomeHeapNumber()], (Object*)pObject, type); +} + void GCToEEInterface::SuspendEE(SUSPEND_REASON reason) { #ifdef FEATURE_EVENT_TRACE @@ -1249,11 +1264,13 @@ gc_alloc_context * Thread::GetAllocContext() return dac_cast(dac_cast(this) + offsetof(Thread, m_rgbAllocContextBuffer)); } +#ifndef DACCESS_COMPILE bool IsGCSpecialThread() { // TODO: Implement for background GC - return false; + return ThreadStore::GetCurrentThread()->IsGCSpecial(); } +#endif // DACCESS_COMPILE GPTR_IMPL(Thread, g_pFinalizerThread); GPTR_IMPL(Thread, g_pGcThread); @@ -1270,15 +1287,23 @@ bool __SwitchToThread(uint32_t dwSleepMSec, uint32_t /*dwSwitchCount*/) return !!PalSwitchToThread(); } +void SetGCSpecialThread(ThreadType threadType) +{ + Thread *pThread = ThreadStore::RawGetCurrentThread(); + pThread->SetGCSpecial(threadType == ThreadType_GC); +} + #endif // DACCESS_COMPILE MethodTable * g_pFreeObjectMethodTable; int32_t g_TrapReturningThreads; +#ifndef DACCESS_COMPILE bool IsGCThread() { - return false; + return IsGCSpecialThread() || ThreadStore::GetSuspendingThread() == ThreadStore::GetCurrentThread(); } +#endif // DACCESS_COMPILE void LogSpewAlways(const char * /*fmt*/, ...) { diff --git a/external/corert/src/Native/Runtime/gcrhinterface.h b/external/corert/src/Native/Runtime/gcrhinterface.h index 2de4a9047f..2cea660732 100644 --- a/external/corert/src/Native/Runtime/gcrhinterface.h +++ b/external/corert/src/Native/Runtime/gcrhinterface.h @@ -157,6 +157,10 @@ public: static EEType * GetLastAllocEEType(); static void SetLastAllocEEType(EEType *pEEType); + // Used by debugger hook + static void* CreateTypedHandle(void* object, int type); + static void DestroyTypedHandle(void* handle); + private: // The EEType for the last allocation. This value is used inside of the GC allocator // to emit allocation ETW events with type information. We set this value unconditionally to avoid diff --git a/external/corert/src/Native/Runtime/gcrhscan.cpp b/external/corert/src/Native/Runtime/gcrhscan.cpp index 31ac4d7a58..e342e2c783 100644 --- a/external/corert/src/Native/Runtime/gcrhscan.cpp +++ b/external/corert/src/Native/Runtime/gcrhscan.cpp @@ -49,7 +49,7 @@ void EnumAllStaticGCRefs(EnumGcRefCallbackFunc * fn, EnumGcRefScanContext * sc) void GCToEEInterface::GcScanRoots(EnumGcRefCallbackFunc * fn, int condemned, int max_gen, EnumGcRefScanContext * sc) { - DebuggerProtectedBufferList* cursor = DebuggerHook::s_debuggerProtectedBuffers; + DebuggerProtectedBufferListNode* cursor = DebuggerHook::s_debuggerProtectedBuffers; while (cursor != nullptr) { GcEnumObjectsConservatively((PTR_PTR_Object)cursor->address, (PTR_PTR_Object)(cursor->address + cursor->size), fn, sc); diff --git a/external/corefx/src/System.Data.Odbc/src/Properties/SecurityTransparent.cs b/external/corert/src/Native/Runtime/i386/AllocFast.S similarity index 78% rename from external/corefx/src/System.Data.Odbc/src/Properties/SecurityTransparent.cs rename to external/corert/src/Native/Runtime/i386/AllocFast.S index 825afc55dc..1dc275673f 100644 --- a/external/corefx/src/System.Data.Odbc/src/Properties/SecurityTransparent.cs +++ b/external/corert/src/Native/Runtime/i386/AllocFast.S @@ -2,6 +2,4 @@ // 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.Security; - -[assembly: SecurityTransparent] +// TODO: Implement diff --git a/external/corert/src/Native/Runtime/i386/AllocFast.asm b/external/corert/src/Native/Runtime/i386/AllocFast.asm index ce817ff029..edc8c0a837 100644 --- a/external/corert/src/Native/Runtime/i386/AllocFast.asm +++ b/external/corert/src/Native/Runtime/i386/AllocFast.asm @@ -161,6 +161,135 @@ NewFinalizable_OOM: FASTCALL_ENDFUNC +;; Allocate a new string. +;; ECX == EEType +;; EDX == element count +FASTCALL_FUNC RhNewString, 8 + + push ecx + push edx + + ;; Make sure computing the aligned overall allocation size won't overflow + cmp edx, ((0FFFFFFFFh - STRING_BASE_SIZE - 3) / STRING_COMPONENT_SIZE) + ja StringSizeOverflow + + ; Compute overall allocation size (align(base size + (element size * elements), 4)). + lea eax, [(edx * STRING_COMPONENT_SIZE) + (STRING_BASE_SIZE + 3)] + and eax, -4 + + ; ECX == EEType + ; EAX == allocation size + ; EDX == scratch + + INLINE_GETTHREAD edx, ecx ; edx = GetThread(), TRASHES ecx + + ; ECX == scratch + ; EAX == allocation size + ; EDX == thread + + mov ecx, eax + add eax, [edx + OFFSETOF__Thread__m_alloc_context__alloc_ptr] + jc StringAllocContextOverflow + cmp eax, [edx + OFFSETOF__Thread__m_alloc_context__alloc_limit] + ja StringAllocContextOverflow + + ; ECX == allocation size + ; EAX == new alloc ptr + ; EDX == thread + + ; set the new alloc pointer + mov [edx + OFFSETOF__Thread__m_alloc_context__alloc_ptr], eax + + ; calc the new object pointer + sub eax, ecx + + pop edx + pop ecx + + ; set the new object's EEType pointer and element count + mov [eax + OFFSETOF__Object__m_pEEType], ecx + mov [eax + OFFSETOF__String__m_Length], edx + ret + +StringAllocContextOverflow: + ; ECX == string size + ; original ECX pushed + ; original EDX pushed + + ; Re-push original ECX + push [esp + 4] + + ; Create EBP frame. + mov [esp + 8], ebp + lea ebp, [esp + 8] + + PUSH_COOP_PINVOKE_FRAME edx + + ; Preserve the string size in edi + mov edi, ecx + + ; Get the EEType and put it in ecx. + mov ecx, dword ptr [ebp - 8] + + ; Push alloc helper arguments (thread, size, flags, EEType). + push edx ; transition frame + push edi ; Size + xor edx, edx ; Flags + ;; Passing EEType in ecx + + ;; void* RhpGcAlloc(EEType *pEEType, UInt32 uFlags, UIntNative cbSize, void * pTransitionFrame) + call RhpGcAlloc + + ; Set the new object's EEType pointer and length on success. + test eax, eax + jz StringOutOfMemoryWithFrame + + mov ecx, [ebp - 8] + mov edx, [ebp - 4] + mov [eax + OFFSETOF__Object__m_pEEType], ecx + mov [eax + OFFSETOF__String__m_Length], edx + + ;; If the object is bigger than RH_LARGE_OBJECT_SIZE, we must publish it to the BGC + cmp edi, RH_LARGE_OBJECT_SIZE + jb NewString_SkipPublish + mov ecx, eax ;; ecx: object + mov edx, edi ;; edx: object size + call RhpPublishObject ;; eax: this function returns the object that was passed-in +NewString_SkipPublish: + + POP_COOP_PINVOKE_FRAME + add esp, 8 ; pop ecx / edx + pop ebp + ret + +StringOutOfMemoryWithFrame: + ; This is the OOM failure path. We're going to tail-call to a managed helper that will throw + ; an out of memory exception that the caller of this allocator understands. + + mov eax, [ebp - 8] ; Preserve EEType pointer over POP_COOP_PINVOKE_FRAME + + POP_COOP_PINVOKE_FRAME + add esp, 8 ; pop ecx / edx + pop ebp ; restore ebp + + mov ecx, eax ; EEType pointer + xor edx, edx ; Indicate that we should throw OOM. + jmp RhExceptionHandling_FailedAllocation + +StringSizeOverflow: + ;; We get here if the size of the final string object can't be represented as an unsigned + ;; 32-bit value. We're going to tail-call to a managed helper that will throw + ;; an OOM exception that the caller of this allocator understands. + + add esp, 8 ; pop ecx / edx + + ;; ecx holds EEType pointer already + xor edx, edx ; Indicate that we should throw OOM. + jmp RhExceptionHandling_FailedAllocation + +FASTCALL_ENDFUNC + + ;; Allocate one dimensional, zero based array (SZARRAY). ;; ECX == EEType ;; EDX == element count @@ -171,7 +300,8 @@ FASTCALL_FUNC RhpNewArray, 8 ; Compute overall allocation size (align(base size + (element size * elements), 4)). ; if the element count is <= 0x10000, no overflow is possible because the component size is - ; <= 0xffff, and thus the product is <= 0xffff0000, and the base size is only 12 bytes + ; <= 0xffff, and thus the product is <= 0xffff0000, and the base size for the worst case + ; (32 dimensional MdArray) is less than 0xffff. movzx eax, word ptr [ecx + OFFSETOF__EEType__m_usComponentSize] cmp edx,010000h ja ArraySizeBig diff --git a/external/corert/src/Native/Runtime/i386/AsmMacros.inc b/external/corert/src/Native/Runtime/i386/AsmMacros.inc index ab09d7fd8d..e8af70558f 100644 --- a/external/corert/src/Native/Runtime/i386/AsmMacros.inc +++ b/external/corert/src/Native/Runtime/i386/AsmMacros.inc @@ -35,6 +35,24 @@ decoratedName label proc PUBLIC decoratedName endm +EXPORT_POINTER_TO_ADDRESS macro Name + + local AddressToExport + +AddressToExport label proc + + .const + + align 4 + +Name dd offset AddressToExport + + public Name + + .code + + endm + __tls_array equ 2Ch ;; offsetof(TEB, ThreadLocalStoragePointer) ;; @@ -137,6 +155,15 @@ PTFF_SAVE_RAX equ 00000100h ;; RAX is saved if it contains a GC ref PTFF_SAVE_ALL_SCRATCH equ 00000700h PTFF_RAX_IS_GCREF equ 00010000h ;; iff PTFF_SAVE_RAX: set -> eax is Object, clear -> eax is scalar PTFF_RAX_IS_BYREF equ 00020000h ;; iff PTFF_SAVE_RAX: set -> eax is ByRef, clear -> eax is Object or scalar +PTFF_THREAD_ABORT equ 00040000h ;; indicates that ThreadAbortException should be thrown when returning from the transition + +;; These must match the TrapThreadsFlags enum +TrapThreadsFlags_None equ 0 +TrapThreadsFlags_AbortInProgress equ 1 +TrapThreadsFlags_TrapThreads equ 2 + +;; This must match HwExceptionCode.STATUS_REDHAWK_THREAD_ABORT +STATUS_REDHAWK_THREAD_ABORT equ 43h ;; ;; Rename fields of nested structs diff --git a/external/corert/src/Native/Runtime/i386/AsmOffsetsCpu.h b/external/corert/src/Native/Runtime/i386/AsmOffsetsCpu.h index d7f70f4f26..3f78c85e23 100644 --- a/external/corert/src/Native/Runtime/i386/AsmOffsetsCpu.h +++ b/external/corert/src/Native/Runtime/i386/AsmOffsetsCpu.h @@ -21,7 +21,7 @@ PLAT_ASM_OFFSET(b8, ExInfo, m_notifyDebuggerSP) PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_RIP) PLAT_ASM_OFFSET(4, PInvokeTransitionFrame, m_FramePointer) PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_pThread) -PLAT_ASM_OFFSET(0c, PInvokeTransitionFrame, m_dwFlags) +PLAT_ASM_OFFSET(0c, PInvokeTransitionFrame, m_Flags) PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_PreservedRegs) PLAT_ASM_SIZEOF(a4, StackFrameIterator) diff --git a/external/corefx/src/System.Collections.Immutable/src/Properties/SecurityTransparent.cs b/external/corert/src/Native/Runtime/i386/CallDescrWorker.S similarity index 78% rename from external/corefx/src/System.Collections.Immutable/src/Properties/SecurityTransparent.cs rename to external/corert/src/Native/Runtime/i386/CallDescrWorker.S index 825afc55dc..1dc275673f 100644 --- a/external/corefx/src/System.Collections.Immutable/src/Properties/SecurityTransparent.cs +++ b/external/corert/src/Native/Runtime/i386/CallDescrWorker.S @@ -2,6 +2,4 @@ // 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.Security; - -[assembly: SecurityTransparent] +// TODO: Implement diff --git a/external/corert/src/Native/Runtime/i386/CallDescrWorker.asm b/external/corert/src/Native/Runtime/i386/CallDescrWorker.asm index f73bb9b644..cc4262c5e0 100644 --- a/external/corert/src/Native/Runtime/i386/CallDescrWorker.asm +++ b/external/corert/src/Native/Runtime/i386/CallDescrWorker.asm @@ -49,7 +49,7 @@ donestack: mov eax,[ebx + OFFSETOF__CallDescrData__pTarget] call eax -ReturnFromCallDescrThunk label proc + EXPORT_POINTER_TO_ADDRESS _PointerToReturnFromCallDescrThunk ; Symbol used to identify thunk call to managed function so the special ; case unwinder can unwind through this function. Sadly we cannot directly @@ -92,16 +92,6 @@ ReturnsDouble: FASTCALL_ENDFUNC - .const - - align 4 - -_PointerToReturnFromCallDescrThunk label dword - - dd offset ReturnFromCallDescrThunk - - public _PointerToReturnFromCallDescrThunk - endif end diff --git a/external/corefx/src/System.IO.Compression/src/AssemblyInfo.cs b/external/corert/src/Native/Runtime/i386/CallingConventionConverterHelpers.S similarity index 75% rename from external/corefx/src/System.IO.Compression/src/AssemblyInfo.cs rename to external/corert/src/Native/Runtime/i386/CallingConventionConverterHelpers.S index 18d3a814bd..1dc275673f 100644 --- a/external/corefx/src/System.IO.Compression/src/AssemblyInfo.cs +++ b/external/corert/src/Native/Runtime/i386/CallingConventionConverterHelpers.S @@ -2,6 +2,4 @@ // 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.Security; - -[assembly: SecurityTransparentAttribute()] \ No newline at end of file +// TODO: Implement diff --git a/external/corert/src/Native/Runtime/i386/ExceptionHandling.S b/external/corert/src/Native/Runtime/i386/ExceptionHandling.S new file mode 100644 index 0000000000..1dc275673f --- /dev/null +++ b/external/corert/src/Native/Runtime/i386/ExceptionHandling.S @@ -0,0 +1,5 @@ +// 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. + +// TODO: Implement diff --git a/external/corert/src/Native/Runtime/i386/ExceptionHandling.asm b/external/corert/src/Native/Runtime/i386/ExceptionHandling.asm index d96ed44b8c..270cb23303 100644 --- a/external/corert/src/Native/Runtime/i386/ExceptionHandling.asm +++ b/external/corert/src/Native/Runtime/i386/ExceptionHandling.asm @@ -12,6 +12,8 @@ include AsmMacros.inc RhpCallFunclet equ @RhpCallFunclet@0 +RhpThrowHwEx equ @RhpThrowHwEx@0 + extern RhpCallFunclet : proc ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -72,7 +74,8 @@ FASTCALL_FUNC RhpThrowHwEx, 0 ;; ecx still contains the exception code ;; edx contains the address of the ExInfo call RhThrowHwEx -ALTERNATE_ENTRY RhpThrowHwEx2 + + EXPORT_POINTER_TO_ADDRESS _PointerToRhpThrowHwEx2 ;; no return int 3 @@ -148,7 +151,8 @@ FASTCALL_FUNC RhpThrowEx, 0 ;; ecx still contains the exception object ;; edx contains the address of the ExInfo call RhThrowEx -ALTERNATE_ENTRY RhpThrowEx2 + + EXPORT_POINTER_TO_ADDRESS _PointerToRhpThrowEx2 ;; no return int 3 @@ -216,7 +220,8 @@ FASTCALL_FUNC RhpRethrow, 0 ;; ecx contains the currently active ExInfo ;; edx contains the address of the new ExInfo call RhRethrow -ALTERNATE_ENTRY RhpRethrow2 + + EXPORT_POINTER_TO_ADDRESS _PointerToRhpRethrow2 ;; no return int 3 @@ -269,21 +274,25 @@ endm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; FASTCALL_FUNC RhpCallCatchFunclet, 0 - FUNCLET_CALL_PROLOGUE 1 + FUNCLET_CALL_PROLOGUE 2 - esp_offsetof_ResumeIP textequ %00h ;; [esp + 00h]: continuation address - ;; [esp + 04h]: edi save - ;; [esp + 08h]: esi save - ;; [esp + 0Ch]: ebx save - esp_offsetof_PrevEBP textequ %10h ;; [esp + 10h]: prev ebp - esp_offsetof_RetAddr textequ %14h ;; [esp + 14h]: return address - esp_offsetof_RegDisplay textequ %18h ;; [esp + 18h]: REGDISPLAY* - esp_offsetof_ExInfo textequ %1ch ;; [esp + 1Ch]: ExInfo* + esp_offsetof_ResumeIP textequ %00h ;; [esp + 00h]: continuation address + esp_offsetof_is_handling_thread_abort textequ %04h ;; [esp + 04h]: set if we are handling ThreadAbortException + ;; [esp + 08h]: edi save + ;; [esp + 0ch]: esi save + ;; [esp + 10h]: ebx save + esp_offsetof_PrevEBP textequ %14h ;; [esp + 14h]: prev ebp + esp_offsetof_RetAddr textequ %18h ;; [esp + 18h]: return address + esp_offsetof_RegDisplay textequ %1ch ;; [esp + 1Ch]: REGDISPLAY* + esp_offsetof_ExInfo textequ %20h ;; [esp + 20h]: ExInfo* ;; Clear the DoNotTriggerGc state before calling out to our managed catch funclet. INLINE_GETTHREAD eax, ebx ;; eax <- Thread*, ebx is trashed lock and dword ptr [eax + OFFSETOF__Thread__m_ThreadStateFlags], NOT TSF_DoNotTriggerGc + cmp ecx, [eax + OFFSETOF__Thread__m_threadAbortException] + setz byte ptr [esp + esp_offsetof_is_handling_thread_abort] + mov edi, [esp + esp_offsetof_RegDisplay] ;; edi <- REGDISPLAY * mov eax, [edi + OFFSETOF__REGDISPLAY__pRbx] @@ -305,7 +314,8 @@ FASTCALL_FUNC RhpCallCatchFunclet, 0 ;; EDX: funclet IP ;; EAX: funclet EBP call RhpCallFunclet -ALTERNATE_ENTRY RhpCallCatchFunclet2 + + EXPORT_POINTER_TO_ADDRESS _PointerToRhpCallCatchFunclet2 ;; eax: resume IP mov [esp + esp_offsetof_ResumeIP], eax ;; save for later @@ -328,6 +338,25 @@ ALTERNATE_ENTRY RhpCallCatchFunclet2 @@: mov [edx + OFFSETOF__Thread__m_pExInfoStackHead], ecx ;; store the new head on the Thread + test [RhpTrapThreads], TrapThreadsFlags_AbortInProgress + jz @f + + ;; test if the exception handled by the catch was the ThreadAbortException + cmp byte ptr [esp + esp_offsetof_is_handling_thread_abort], 0 + je @f + + ;; RhpCallFunclet preserved our local EBP value, so let's fetch the correct one for the resume address + mov ecx, [esp + esp_offsetof_RegDisplay] ;; ecx <- REGDISPLAY * + mov ecx, [ecx + OFFSETOF__REGDISPLAY__pRbp] + mov ebp, [ecx] + + ;; It was the ThreadAbortException, so rethrow it + mov ecx, STATUS_REDHAWK_THREAD_ABORT + mov edx, [esp + esp_offsetof_ResumeIP] + mov esp, eax ;; reset the SP to resume SP value + jmp RhpThrowHwEx ;; Throw the ThreadAbortException as a special kind of hardware exception + + @@: ;; RhpCallFunclet preserved our local EBP value, so let's fetch the correct one for the resume address mov ecx, [esp + esp_offsetof_RegDisplay] ;; ecx <- REGDISPLAY * mov ecx, [ecx + OFFSETOF__REGDISPLAY__pRbp] @@ -381,7 +410,8 @@ FASTCALL_FUNC RhpCallFinallyFunclet, 0 ;; EDX: funclet IP ;; EAX: funclet EBP call RhpCallFunclet -ALTERNATE_ENTRY RhpCallFinallyFunclet2 + + EXPORT_POINTER_TO_ADDRESS _PointerToRhpCallFinallyFunclet2 pop edx ;; restore REGDISPLAY* @@ -434,7 +464,8 @@ FASTCALL_FUNC RhpCallFilterFunclet, 0 mov edx, [esp + 0] ;; reload filter funclet address call RhpCallFunclet -ALTERNATE_ENTRY RhpCallFilterFunclet2 + + EXPORT_POINTER_TO_ADDRESS _PointerToRhpCallFilterFunclet2 ;; EAX contains the result of the filter execution mov edx, [ebp + 8] diff --git a/external/corert/src/Native/Runtime/i386/GcProbe.asm b/external/corert/src/Native/Runtime/i386/GcProbe.asm index 15a2726cb0..3e8eea5076 100644 --- a/external/corert/src/Native/Runtime/i386/GcProbe.asm +++ b/external/corert/src/Native/Runtime/i386/GcProbe.asm @@ -161,18 +161,21 @@ endm ;; All other registers trashed ;; -EXTERN _RhpWaitForGC : PROC +EXTERN _RhpWaitForGCNoAbort : PROC WaitForGCCompletion macro test dword ptr [ebx + OFFSETOF__Thread__m_ThreadStateFlags], TSF_SuppressGcStress + TSF_DoNotTriggerGc jnz @F mov ecx, esp - call _RhpWaitForGC + call _RhpWaitForGCNoAbort @@: endm +RhpThrowHwEx equ @RhpThrowHwEx@0 +extern RhpThrowHwEx : proc + ;; ;; Main worker for our GC probes. Do not call directly!! This assumes that HijackFixupProlog has been done. ;; Instead, go through RhpGcProbeHijack* or RhpGcStressHijack*. This waits for the @@ -188,8 +191,8 @@ endm ;; All registers restored as they were when the hijack was first reached. ;; RhpGcProbe proc - cmp [RhpTrapThreads], 0 - jne SynchronousRendezVous + test [RhpTrapThreads], TrapThreadsFlags_TrapThreads + jnz SynchronousRendezVous HijackFixupEpilog @@ -198,12 +201,23 @@ SynchronousRendezVous: WaitForGCCompletion + mov edx, [esp + OFFSETOF__PInvokeTransitionFrame__m_Flags] ;; ;; Restore preserved registers -- they may have been updated by GC ;; PopProbeFrame + test edx, PTFF_THREAD_ABORT + jnz Abort + HijackFixupEpilog +Abort: + mov ecx, STATUS_REDHAWK_THREAD_ABORT + pop edx + pop eax ;; ecx was pushed here, but we don't care for its value + pop ebp + pop edx ;; return address as exception RIP + jmp RhpThrowHwEx RhpGcProbe endp @@ -501,8 +515,8 @@ ifdef _DEBUG ;; If we get here, then we have been hijacked for a real GC, and our SyncState must ;; reflect that we've been requested to synchronize. - cmp [RhpTrapThreads], 0 - jne @F + test [RhpTrapThreads], TrapThreadsFlags_TrapThreads + jnz @F call RhDebugBreak @@: @@ -710,7 +724,7 @@ ifdef FEATURE_GC_STRESS endif ;; FEATURE_GC_STRESS mov ecx, esp ; esp is address of PInvokeTransitionFrame - mov eax, _RhpWaitForGC + mov eax, _RhpWaitForGCNoAbort call RhpCall DoneWaitingForGc: @@ -720,8 +734,13 @@ DoneWaitingForGc: mov ecx, [ebp - 8h] mov [ebp - 4h], ecx + mov edx, [esp + OFFSETOF__PInvokeTransitionFrame__m_Flags] + ; Restore our integer register state from the PInvokeTransitionFrame PopProbeFrame + + test edx, PTFF_THREAD_ABORT + pop ecx pop edx @@ -730,6 +749,15 @@ DoneWaitingForGc: ; Pop the rest of our frame lea esp, [ebp - 4] + + jz @f ;; result of the test instruction before the pops above + + mov ecx, STATUS_REDHAWK_THREAD_ABORT + popfd ;; restore flags + pop ebp + pop edx ;; return address as exception RIP + jmp RhpThrowHwEx ;; Throw the ThreadAbortException as a special kind of hardware exception +@@: popfd pop ebp ret diff --git a/external/corert/src/Native/Runtime/i386/Interlocked.S b/external/corert/src/Native/Runtime/i386/Interlocked.S new file mode 100644 index 0000000000..1dc275673f --- /dev/null +++ b/external/corert/src/Native/Runtime/i386/Interlocked.S @@ -0,0 +1,5 @@ +// 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. + +// TODO: Implement diff --git a/external/corert/src/Native/Runtime/i386/Interlocked.asm b/external/corert/src/Native/Runtime/i386/Interlocked.asm new file mode 100644 index 0000000000..f9599b1b86 --- /dev/null +++ b/external/corert/src/Native/Runtime/i386/Interlocked.asm @@ -0,0 +1,3 @@ +;; TODO: Implement + +end diff --git a/external/corert/src/Native/Runtime/i386/InteropThunksHelpers.S b/external/corert/src/Native/Runtime/i386/InteropThunksHelpers.S new file mode 100644 index 0000000000..1dc275673f --- /dev/null +++ b/external/corert/src/Native/Runtime/i386/InteropThunksHelpers.S @@ -0,0 +1,5 @@ +// 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. + +// TODO: Implement diff --git a/external/corert/src/Native/Runtime/i386/PInvoke.S b/external/corert/src/Native/Runtime/i386/PInvoke.S new file mode 100644 index 0000000000..1dc275673f --- /dev/null +++ b/external/corert/src/Native/Runtime/i386/PInvoke.S @@ -0,0 +1,5 @@ +// 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. + +// TODO: Implement diff --git a/external/corert/src/Native/Runtime/i386/PInvoke.asm b/external/corert/src/Native/Runtime/i386/PInvoke.asm index dfe03b54b1..85fcec5f25 100644 --- a/external/corert/src/Native/Runtime/i386/PInvoke.asm +++ b/external/corert/src/Native/Runtime/i386/PInvoke.asm @@ -41,7 +41,7 @@ _RhpWaitForSuspend endp ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; -;; RhpWaitForGC +;; RhpWaitForGCNoAbort ;; ;; ;; INPUT: ECX: transition frame @@ -49,7 +49,7 @@ _RhpWaitForSuspend endp ;; OUTPUT: ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -_RhpWaitForGC proc public +_RhpWaitForGCNoAbort proc public push ebp mov ebp, esp push eax @@ -72,6 +72,46 @@ Done: pop eax pop ebp ret +_RhpWaitForGCNoAbort endp + +RhpThrowHwEx equ @RhpThrowHwEx@0 +EXTERN RhpThrowHwEx : PROC + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpWaitForGC +;; +;; +;; INPUT: ECX: transition frame +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +_RhpWaitForGC proc public + push ebp + mov ebp, esp + push ebx + + mov ebx, ecx + test [RhpTrapThreads], TrapThreadsFlags_TrapThreads + jz NoWait + + call _RhpWaitForGCNoAbort +NoWait: + test [RhpTrapThreads], TrapThreadsFlags_AbortInProgress + jz Done + test dword ptr [ebx + OFFSETOF__PInvokeTransitionFrame__m_Flags], PTFF_THREAD_ABORT + jz Done + + mov ecx, STATUS_REDHAWK_THREAD_ABORT + pop ebx + pop ebp + pop edx ; return address as exception RIP + jmp RhpThrowHwEx ; Throw the ThreadAbortException as a special kind of hardware exception +Done: + pop ebx + pop ebp + ret _RhpWaitForGC endp ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -115,8 +155,8 @@ ThreadAttached: ReverseRetry: mov dword ptr [edx + OFFSETOF__Thread__m_pTransitionFrame], 0 - cmp [RhpTrapThreads], 0 - jne ReverseTrapReturningThread + test [RhpTrapThreads], TrapThreadsFlags_TrapThreads + jnz ReverseTrapReturningThread AllDone: pop edx ; restore arg reg @@ -169,9 +209,9 @@ FASTCALL_FUNC RhpReversePInvokeReturn, 0 mov ecx, [ecx + 0] ; get previous M->U transition frame mov [edx + OFFSETOF__Thread__m_pTransitionFrame], ecx - cmp [RhpTrapThreads], 0 + test [RhpTrapThreads], TrapThreadsFlags_TrapThreads pop edx ; restore return value - jne _RhpWaitForSuspend + jnz _RhpWaitForSuspend ret FASTCALL_ENDFUNC diff --git a/external/corert/src/Native/Runtime/i386/StubDispatch.S b/external/corert/src/Native/Runtime/i386/StubDispatch.S new file mode 100644 index 0000000000..1dc275673f --- /dev/null +++ b/external/corert/src/Native/Runtime/i386/StubDispatch.S @@ -0,0 +1,5 @@ +// 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. + +// TODO: Implement diff --git a/external/corert/src/Native/Runtime/i386/UniversalTransition.S b/external/corert/src/Native/Runtime/i386/UniversalTransition.S new file mode 100644 index 0000000000..1dc275673f --- /dev/null +++ b/external/corert/src/Native/Runtime/i386/UniversalTransition.S @@ -0,0 +1,5 @@ +// 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. + +// TODO: Implement diff --git a/external/corert/src/Native/Runtime/i386/UniversalTransition.asm b/external/corert/src/Native/Runtime/i386/UniversalTransition.asm index 9ee597ea79..4d51398da2 100644 --- a/external/corert/src/Native/Runtime/i386/UniversalTransition.asm +++ b/external/corert/src/Native/Runtime/i386/UniversalTransition.asm @@ -74,7 +74,8 @@ ALTERNATE_ENTRY Rhp&FunctionName&@0 mov edx, [ebp-4] ; Get the extra argument to pass to the callee lea ecx, [ebp-10h] ; Get pointer to edx value pushed above call eax -ReturnFrom&FunctionName label proc + + EXPORT_POINTER_TO_ADDRESS _PointerToReturnFrom&FunctionName ; We cannot make the label public as that tricks DIA stackwalker into thinking ; it's the beginning of a method. For this reason we export an auxiliary variable @@ -88,18 +89,6 @@ ReturnFrom&FunctionName label proc FASTCALL_ENDFUNC - .const - - align 4 - -_PointerToReturnFrom&FunctionName label dword - - dd offset ReturnFrom&FunctionName - - public _PointerToReturnFrom&FunctionName - - .code - endm ; To enable proper step-in behavior in the debugger, we need to have two instances diff --git a/external/corert/src/Native/Runtime/i386/WriteBarriers.S b/external/corert/src/Native/Runtime/i386/WriteBarriers.S new file mode 100644 index 0000000000..1dc275673f --- /dev/null +++ b/external/corert/src/Native/Runtime/i386/WriteBarriers.S @@ -0,0 +1,5 @@ +// 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. + +// TODO: Implement diff --git a/external/corert/src/Native/Runtime/inc/ModuleHeaders.h b/external/corert/src/Native/Runtime/inc/ModuleHeaders.h index 92f5bed1cb..9198314b33 100644 --- a/external/corert/src/Native/Runtime/inc/ModuleHeaders.h +++ b/external/corert/src/Native/Runtime/inc/ModuleHeaders.h @@ -51,6 +51,8 @@ enum class ReadyToRunSectionType ThreadStaticOffsetRegion = 208, ThreadStaticGCDescRegion = 209, ThreadStaticIndex = 210, + LoopHijackFlag = 211, + ImportAddressTables = 212, // Sections 300 - 399 are reserved for RhFindBlob backwards compatibility ReadonlyBlobRegionStart = 300, diff --git a/external/corert/src/Native/Runtime/inc/TargetPtrs.h b/external/corert/src/Native/Runtime/inc/TargetPtrs.h index 4b98dc6b29..54327a2828 100644 --- a/external/corert/src/Native/Runtime/inc/TargetPtrs.h +++ b/external/corert/src/Native/Runtime/inc/TargetPtrs.h @@ -111,6 +111,8 @@ typedef UInt32 UIntTarget; typedef UInt32 UIntTarget; #elif defined(_TARGET_ARM64_) typedef UInt64 UIntTarget; +#elif defined(_TARGET_WASM_) +typedef UInt32 UIntTarget; #else #error unexpected target architecture #endif diff --git a/external/corert/src/Native/Runtime/inc/WellKnownMethodList.h b/external/corert/src/Native/Runtime/inc/WellKnownMethodList.h index 75de3a58d4..567764bbc4 100644 --- a/external/corert/src/Native/Runtime/inc/WellKnownMethodList.h +++ b/external/corert/src/Native/Runtime/inc/WellKnownMethodList.h @@ -8,3 +8,4 @@ DEFINE_WELL_KNOWN_METHOD(UnhandledExceptionHandler) DEFINE_WELL_KNOWN_METHOD(AppendExceptionStackFrame) DEFINE_WELL_KNOWN_METHOD(CheckStaticClassConstruction) DEFINE_WELL_KNOWN_METHOD(InitializeFinalizerThread) +DEFINE_WELL_KNOWN_METHOD(OnFirstChanceException) diff --git a/external/corert/src/Native/Runtime/inc/daccess.h b/external/corert/src/Native/Runtime/inc/daccess.h index 1aab9590c0..936c4aac09 100644 --- a/external/corert/src/Native/Runtime/inc/daccess.h +++ b/external/corert/src/Native/Runtime/inc/daccess.h @@ -536,7 +536,7 @@ #include "safemath.h" -#ifdef _TARGET_AMD64_ +#if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) typedef UInt64 UIntTarget; #elif defined(_TARGET_X86_) typedef UInt32 UIntTarget; @@ -1937,7 +1937,7 @@ typedef __VoidPtr PTR_CVOID; #define PTR_READ(addr, size) \ DacInstantiateTypeByAddress(addr, size, true) -// This value is used to intiailize target pointers to NULL. We want this to be TADDR type +// This value is used to initialize target pointers to NULL. We want this to be TADDR type // (as opposed to, say, __TPtrBase) so that it can be used in the non-explicit ctor overloads, // eg. as an argument default value. // We can't always just use NULL because that's 0 which (in C++) can be any integer or pointer diff --git a/external/corert/src/Native/Runtime/inc/eetype.h b/external/corert/src/Native/Runtime/inc/eetype.h index 409b1f28b1..39949b5ebf 100644 --- a/external/corert/src/Native/Runtime/inc/eetype.h +++ b/external/corert/src/Native/Runtime/inc/eetype.h @@ -258,7 +258,8 @@ private: // This type contain gc pointers HasPointersFlag = 0x0020, - // Unused = 0x0040, + // Type implements ICastable to allow dynamic resolution of interface casts. + ICastableTypeFlag = 0x0040, // This type is generic and one or more of it's type parameters is co- or contra-variant. This only // applies to interface and delegate types. @@ -287,8 +288,8 @@ public: // This type requires 8-byte alignment for its fields on certain platforms (only ARM currently). RequiresAlign8Flag = 0x00000001, - // Type implements ICastable to allow dynamic resolution of interface casts. - ICastableFlag = 0x00000002, + // Old unused flag + UNUSED1 = 0x00000002, // Type is an instantiation of Nullable. IsNullableFlag = 0x00000004, @@ -302,10 +303,8 @@ public: // This EEType has a Class Constructor HasCctorFlag = 0x0000020, - // This EEType has sealed vtable entries (note that this flag is only used for - // dynamically created types because they always have an optional field (hence the - // very explicit flag name). - IsDynamicTypeWithSealedVTableEntriesFlag = 0x00000040, + // Old unused flag + UNUSED2 = 0x00000040, // This EEType was constructed from a universal canonical template, and has // its own dynamically created DispatchMap (does not use the DispatchMap of its template type) @@ -315,8 +314,6 @@ public: IsHFAFlag = 0x00000100, // This EEType has sealed vtable entries - // This is for statically generated types - we need two different flags because - // the sealed vtable entries are reached in different ways in the static and dynamic case HasSealedVTableEntriesFlag = 0x00000200, // This dynamically created type has gc statics @@ -477,12 +474,16 @@ public: DynamicModule* get_DynamicModule(); -#if defined(EETYPE_TYPE_MANAGER) TypeManagerHandle* GetTypeManagerPtr() - { return m_ppTypeManager; } -#endif - + { #if defined(EETYPE_TYPE_MANAGER) + return m_ppTypeManager; +#else + return NULL; +#endif + } + +#ifdef PROJECTN // // PROJX-TODO // Needed while we exist in a world where some things are built using CoreRT and some built using @@ -490,9 +491,13 @@ public: // bool HasTypeManager() { +#if defined(EETYPE_TYPE_MANAGER) return m_ppTypeManager != nullptr; - } +#else + return false; #endif + } +#endif // PROJECTN #ifndef BINDER DispatchMap *GetDispatchMap(); @@ -540,18 +545,6 @@ public: bool RequiresAlign8() { return (get_RareFlags() & RequiresAlign8Flag) != 0; } - // Determine whether a type supports ICastable. - bool IsICastable() - { return (get_RareFlags() & ICastableFlag) != 0; } - - // Retrieve the address of the method that implements ICastable.IsInstanceOfInterface for - // ICastable types. - inline PTR_Code get_ICastableIsInstanceOfInterfaceMethod(); - - // Retrieve the vtable slot number of the method that implements ICastable.GetImplType for ICastable - // types. - inline PTR_Code get_ICastableGetImplTypeMethod(); - // Determine whether a type is an instantiation of Nullable. bool IsNullable() { return (get_RareFlags() & IsNullableFlag) != 0; } diff --git a/external/corert/src/Native/Runtime/inc/eetype.inl b/external/corert/src/Native/Runtime/inc/eetype.inl index 0b4908f07a..e52fed1482 100644 --- a/external/corert/src/Native/Runtime/inc/eetype.inl +++ b/external/corert/src/Native/Runtime/inc/eetype.inl @@ -46,30 +46,21 @@ inline PTR_UInt8 FollowRelativePointer(const Int32 *pDist) inline PTR_Code EEType::get_SealedVirtualSlot(UInt16 slotNumber) { ASSERT(!IsNullable()); + ASSERT((get_RareFlags() & HasSealedVTableEntriesFlag) != 0); if (IsDynamicType()) { - if ((get_RareFlags() & IsDynamicTypeWithSealedVTableEntriesFlag) != 0) - { - UInt32 cbSealedVirtualSlotsTypeOffset = GetFieldOffset(ETF_SealedVirtualSlots); - - PTR_PTR_Code pSealedVirtualsSlotTable = *(PTR_PTR_Code*)((PTR_UInt8)this + cbSealedVirtualSlotsTypeOffset); - - return pSealedVirtualsSlotTable[slotNumber]; - } - else - { - return get_DynamicTemplateType()->get_SealedVirtualSlot(slotNumber); - } + UInt32 cbSealedVirtualSlotsTypeOffset = GetFieldOffset(ETF_SealedVirtualSlots); + PTR_PTR_Code pSealedVirtualsSlotTable = *(PTR_PTR_Code*)((PTR_UInt8)this + cbSealedVirtualSlotsTypeOffset); + return pSealedVirtualsSlotTable[slotNumber]; + } + else + { + UInt32 cbSealedVirtualSlotsTypeOffset = GetFieldOffset(ETF_SealedVirtualSlots); + PTR_Int32 pSealedVirtualsSlotTable = (PTR_Int32)FollowRelativePointer((PTR_Int32)((PTR_UInt8)this + cbSealedVirtualSlotsTypeOffset)); + PTR_Code result = FollowRelativePointer(&pSealedVirtualsSlotTable[slotNumber]); + return result; } - - UInt32 cbSealedVirtualSlotsTypeOffset = GetFieldOffset(ETF_SealedVirtualSlots); - - PTR_Int32 pSealedVirtualsSlotTable = (PTR_Int32)FollowRelativePointer((PTR_Int32)((PTR_UInt8)this + cbSealedVirtualSlotsTypeOffset)); - - PTR_Code result = FollowRelativePointer(&pSealedVirtualsSlotTable[slotNumber]); - - return result; } #endif // !BINDER && !DACCESS_COMPILE @@ -152,25 +143,25 @@ inline DispatchMap * EEType::GetDispatchMap() return get_DynamicTemplateType()->GetDispatchMap(); } - // Determine this EEType's module. - RuntimeInstance * pRuntimeInstance = GetRuntimeInstance(); - -#if defined(EETYPE_TYPE_MANAGER) - if (HasTypeManager()) +#ifdef PROJECTN + if (!HasTypeManager()) { - return GetTypeManagerPtr()->AsTypeManager()->GetDispatchMapLookupTable()[idxDispatchMap]; + // Determine this EEType's module. + RuntimeInstance * pRuntimeInstance = GetRuntimeInstance(); + + // handle case of R2R cloned string type correctly - the cloned string type is just a copy + // of the real string type, with the optional fields in the library. So for consistency, + // we need to find the module from the optional fields + Module * pModule = pRuntimeInstance->FindModuleByReadOnlyDataAddress(optionalFields); + if (pModule == NULL) + pModule = pRuntimeInstance->FindModuleByDataAddress(optionalFields); + ASSERT(pModule != NULL); + + return pModule->GetDispatchMapLookupTable()[idxDispatchMap]; } -#endif +#endif // PROJECTN - // handle case of R2R cloned string type correctly - the cloned string type is just a copy - // of the real string type, with the optional fields in the library. So for consistency, - // we need to find the module from the optional fields - Module * pModule = pRuntimeInstance->FindModuleByReadOnlyDataAddress(optionalFields); - if (pModule == NULL) - pModule = pRuntimeInstance->FindModuleByDataAddress(optionalFields); - ASSERT(pModule != NULL); - - return pModule->GetDispatchMapLookupTable()[idxDispatchMap]; + return GetTypeManagerPtr()->AsTypeManager()->GetDispatchMapLookupTable()[idxDispatchMap]; } #endif // !BINDER && !DACCESS_COMPILE @@ -350,65 +341,6 @@ inline UInt32 EEType::get_RareFlags() return pOptFields->GetRareFlags(0); } -// Retrieve the vtable slot number of the method that implements ICastableFlag.IsInstanceOfInterface for -// ICastable types. -inline PTR_Code EEType::get_ICastableIsInstanceOfInterfaceMethod() -{ - EEType * eeType = this; - do - { - ASSERT(eeType->IsICastable()); - - OptionalFields * pOptFields = eeType->get_OptionalFields(); - ASSERT(pOptFields); - - UInt16 uiSlot = pOptFields->GetICastableIsInstSlot(0xffff); - if (uiSlot != 0xffff) - { - if (uiSlot < eeType->m_usNumVtableSlots) - return this->get_Slot(uiSlot); - else - return eeType->get_SealedVirtualSlot(uiSlot - eeType->m_usNumVtableSlots); - } - eeType = eeType->get_BaseType(); - } - while (eeType != NULL); - - ASSERT(!"get_ICastableIsInstanceOfInterfaceMethod"); - - return NULL; -} - -// Retrieve the vtable slot number of the method that implements ICastableFlag.GetImplType for ICastable -// types. -inline PTR_Code EEType::get_ICastableGetImplTypeMethod() -{ - EEType * eeType = this; - - do - { - ASSERT(eeType->IsICastable()); - - OptionalFields * pOptFields = eeType->get_OptionalFields(); - ASSERT(pOptFields); - - UInt16 uiSlot = pOptFields->GetICastableGetImplTypeSlot(0xffff); - if (uiSlot != 0xffff) - { - if (uiSlot < eeType->m_usNumVtableSlots) - return this->get_Slot(uiSlot); - else - return eeType->get_SealedVirtualSlot(uiSlot - eeType->m_usNumVtableSlots); - } - eeType = eeType->get_BaseType(); - } - while (eeType != NULL); - - ASSERT(!"get_ICastableGetImplTypeMethod"); - - return NULL; -} - // Retrieve the value type T from a Nullable. inline EEType * EEType::GetNullableType() { @@ -565,7 +497,22 @@ inline DynamicModule * EEType::get_DynamicModule() ((ArrayClass*)pMT->GetClass())->GetApproxArrayElementTypeHandle().AsMethodTable() : NULL; - bool fHasSealedVirtuals = pMT->GetNumVirtuals() < (pMT->GetNumVtableSlots() + pMT->GetNumAdditionalVtableSlots()); + bool isMdArray = pMT->IsArray() && ((ArrayClass*)pMT->GetClass())->GetRank() > 0; + bool isPointerArray = pMT->IsArray() && ((ArrayClass*)pMT->GetClass())->GetPointerRank() > 0; + bool isSpecialArray = isMdArray || isPointerArray; + bool fHasSealedVirtuals = !isSpecialArray && (pMT->GetNumVirtuals() < (pMT->GetNumVtableSlots() + pMT->GetNumAdditionalVtableSlots())); + bool hasICastableMethods = false; + + if (pMT->IsICastable()) + { + SLOT_INDEX *icastableMethod = pMT->GetICastableMethods(); + if (icastableMethod[0] != INVALID_SLOT_INDEX) + hasICastableMethods = true; + if (icastableMethod[1] != INVALID_SLOT_INDEX) + hasICastableMethods = true; + } + + return // Do we need a padding size for value types or unsealed classes? that could be unboxed? (!pMT->IsArray() && @@ -581,9 +528,9 @@ inline DynamicModule * EEType::get_DynamicModule() (pMT->IsHFA()) || #endif // Do we need a DispatchMap? - (pMT->GetDispatchMap() != NULL && !pMT->GetDispatchMap()->IsEmpty()) || + (!isSpecialArray && pMT->GetDispatchMap() != NULL && !pMT->GetDispatchMap()->IsEmpty()) || // Do we need to cache ICastable method vtable slots? - (pMT->IsICastable()) || + hasICastableMethods || // Is the class a Nullable instantiation (need to store the flag and possibly a field offset)? pMT->IsNullable() || (pMT->HasStaticClassConstructor() && !pMT->HasEagerStaticClassConstructor() || @@ -714,13 +661,14 @@ __forceinline UInt32 EEType::GetFieldOffset(EETypeField eField) // Binder does not use DynamicTemplateType #ifndef BINDER - UInt32 rareFlags = get_RareFlags(); - if (IsNullable() || ((rareFlags & IsDynamicTypeWithSealedVTableEntriesFlag) != 0)) + if (IsNullable()) cbOffset += sizeof(UIntTarget); + UInt32 rareFlags = get_RareFlags(); + // in the case of sealed vtable entries on static types, we have a UInt sized relative pointer if (rareFlags & HasSealedVTableEntriesFlag) - cbOffset += sizeof(UInt32); + cbOffset += (IsDynamicType() ? sizeof(UIntTarget) : sizeof(UInt32)); if (eField == ETF_DynamicDispatchMap) { @@ -748,11 +696,11 @@ __forceinline UInt32 EEType::GetFieldOffset(EETypeField eField) if (eField == ETF_DynamicModule) { - ASSERT((get_RareFlags() & HasDynamicModuleFlag) != 0); + ASSERT((rareFlags & HasDynamicModuleFlag) != 0); return cbOffset; } - if ((get_RareFlags() & HasDynamicModuleFlag) != 0) + if ((rareFlags & HasDynamicModuleFlag) != 0) cbOffset += sizeof(UIntTarget); if (eField == ETF_DynamicTemplateType) diff --git a/external/corert/src/Native/Runtime/inc/gcinfo.h b/external/corert/src/Native/Runtime/inc/gcinfo.h index 9cf0f9fdcc..eb04978613 100644 --- a/external/corert/src/Native/Runtime/inc/gcinfo.h +++ b/external/corert/src/Native/Runtime/inc/gcinfo.h @@ -7,6 +7,8 @@ #define _GCINFO_H_ /*****************************************************************************/ +// Keep definitions in this file in sync with Nutc\UTC\gcinfo.h + #ifdef _TARGET_ARM_ #define NUM_PRESERVED_REGS 9 @@ -112,6 +114,192 @@ enum ScratchRegMask SR_MASK_LR = 0x20, }; +#elif defined(_TARGET_ARM64_) + +enum RegMask +{ + RBM_NONE = 0, + + RBM_X0 = 0x00000001, + RBM_X1 = 0x00000002, + RBM_X2 = 0x00000004, + RBM_X3 = 0x00000008, + RBM_X4 = 0x00000010, + RBM_X5 = 0x00000020, + RBM_X6 = 0x00000040, + RBM_X7 = 0x00000080, + RBM_X8 = 0x00000100, // ARM64TODO: ARM64 ABI: indirect result register + RBM_X9 = 0x00000200, + RBM_X10 = 0x00000400, + RBM_X11 = 0x00000800, + RBM_X12 = 0x00001000, + RBM_X13 = 0x00002000, + RBM_X14 = 0x00004000, + RBM_X15 = 0x00008000, + + RBM_XIP0 = 0x00010000, // This one is occasionally used as a scratch register (but can be destroyed by branching or a call) + RBM_XIP1 = 0x00020000, // This one may be also used as a scratch register (but can be destroyed by branching or a call) + RBM_XPR = 0x00040000, + + RBM_X19 = 0x00080000, // RA_CALLEESAVE + RBM_X20 = 0x00100000, // RA_CALLEESAVE + RBM_X21 = 0x00200000, // RA_CALLEESAVE + RBM_X22 = 0x00400000, // RA_CALLEESAVE + RBM_X23 = 0x00800000, // RA_CALLEESAVE + RBM_X24 = 0x01000000, // RA_CALLEESAVE + RBM_X25 = 0x02000000, // RA_CALLEESAVE + RBM_X26 = 0x04000000, // RA_CALLEESAVE + RBM_X27 = 0x08000000, // RA_CALLEESAVE + RBM_X28 = 0x10000000, // RA_CALLEESAVE + + RBM_FP = 0x20000000, + RBM_LR = 0x40000000, + RBM_SP = 0x80000000, + + RBM_RETVAL = RBM_X8, + // Note: Callee saved regs: X19-X28; FP and LR are treated as callee-saved in unwinding code + RBM_CALLEE_SAVED_REG_COUNT = 12, + + // Scratch regs: X0-X15, XIP0, XIP1, LR + RBM_SCRATCH_REG_COUNT = 19, +}; + +#define NUM_PRESERVED_REGS RBM_CALLEE_SAVED_REG_COUNT + +// Number of the callee-saved registers stored in the fixed header +#define NUM_PRESERVED_REGS_LOW 9 +#define MASK_PRESERVED_REGS_LOW ((1 << NUM_PRESERVED_REGS_LOW) - 1) + +enum RegNumber +{ + RN_X0 = 0, + RN_X1 = 1, + RN_X2 = 2, + RN_X3 = 3, + RN_X4 = 4, + RN_X5 = 5, + RN_X6 = 6, + RN_X7 = 7, + RN_X8 = 8, // indirect result register + RN_X9 = 9, + RN_X10 = 10, + RN_X11 = 11, + RN_X12 = 12, + RN_X13 = 13, + RN_X14 = 14, + RN_X15 = 15, + + RN_XIP0 = 16, + RN_XIP1 = 17, + RN_XPR = 18, + + RN_X19 = 19, // RA_CALLEESAVE + RN_X20 = 20, // RA_CALLEESAVE + RN_X21 = 21, // RA_CALLEESAVE + RN_X22 = 22, // RA_CALLEESAVE + RN_X23 = 23, // RA_CALLEESAVE + RN_X24 = 24, // RA_CALLEESAVE + RN_X25 = 25, // RA_CALLEESAVE + RN_X26 = 26, // RA_CALLEESAVE + RN_X27 = 27, // RA_CALLEESAVE + RN_X28 = 28, // RA_CALLEESAVE + + RN_FP = 29, + RN_LR = 30, + RN_SP = 31, + + RN_NONE = 32, +}; + +enum CalleeSavedRegNum +{ + // NOTE: LR is omitted because it may not be live except as a 'scratch' reg + CSR_NUM_X19 = 1, + CSR_NUM_X20 = 2, + CSR_NUM_X21 = 3, + CSR_NUM_X22 = 4, + CSR_NUM_X23 = 5, + CSR_NUM_X24 = 6, + CSR_NUM_X25 = 7, + CSR_NUM_X26 = 8, + CSR_NUM_X27 = 9, + CSR_NUM_X28 = 10, + CSR_NUM_FP = 11, + CSR_NUM_NONE = 12, +}; + +enum CalleeSavedRegMask +{ + CSR_MASK_NONE = 0x00, + // LR is placed here to reduce the frequency of the long encoding + CSR_MASK_LR = 0x001, + CSR_MASK_X19 = 0x002, + CSR_MASK_X20 = 0x004, + CSR_MASK_X21 = 0x008, + CSR_MASK_X22 = 0x010, + CSR_MASK_X23 = 0x020, + CSR_MASK_X24 = 0x040, + CSR_MASK_X25 = 0x080, + CSR_MASK_X26 = 0x100, + CSR_MASK_X27 = 0x200, + CSR_MASK_X28 = 0x400, + CSR_MASK_FP = 0x800, + + CSR_MASK_ALL = 0xfff, + CSR_MASK_HIGHEST = 0x800, +}; + +enum ScratchRegNum +{ + SR_NUM_X0 = 0, + SR_NUM_X1 = 1, + SR_NUM_X2 = 2, + SR_NUM_X3 = 3, + SR_NUM_X4 = 4, + SR_NUM_X5 = 5, + SR_NUM_X6 = 6, + SR_NUM_X7 = 7, + SR_NUM_X8 = 8, + SR_NUM_X9 = 9, + SR_NUM_X10 = 10, + SR_NUM_X11 = 11, + SR_NUM_X12 = 12, + SR_NUM_X13 = 13, + SR_NUM_X14 = 14, + SR_NUM_X15 = 15, + + SR_NUM_XIP0 = 16, + SR_NUM_XIP1 = 17, + SR_NUM_LR = 18, + + SR_NUM_NONE = 19, +}; + +enum ScratchRegMask +{ + SR_MASK_NONE = 0x00, + SR_MASK_X0 = 0x01, + SR_MASK_X1 = 0x02, + SR_MASK_X2 = 0x04, + SR_MASK_X3 = 0x08, + SR_MASK_X4 = 0x10, + SR_MASK_X5 = 0x20, + SR_MASK_X6 = 0x40, + SR_MASK_X7 = 0x80, + SR_MASK_X8 = 0x100, + SR_MASK_X9 = 0x200, + SR_MASK_X10 = 0x400, + SR_MASK_X11 = 0x800, + SR_MASK_X12 = 0x1000, + SR_MASK_X13 = 0x2000, + SR_MASK_X14 = 0x4000, + SR_MASK_X15 = 0x8000, + + SR_MASK_XIP0 = 0x10000, + SR_MASK_XIP1 = 0x20000, + SR_MASK_LR = 0x40000, +}; + #else // _TARGET_ARM_ #ifdef _TARGET_AMD64_ @@ -183,10 +371,12 @@ enum CalleeSavedRegNum CSR_NUM_RSI = 0x01, CSR_NUM_RDI = 0x02, CSR_NUM_RBP = 0x03, +#ifdef _TARGET_AMD64_ CSR_NUM_R12 = 0x04, CSR_NUM_R13 = 0x05, CSR_NUM_R14 = 0x06, CSR_NUM_R15 = 0x07, +#endif // _TARGET_AMD64_ }; enum CalleeSavedRegMask @@ -215,10 +405,12 @@ enum ScratchRegNum SR_NUM_RAX = 0x00, SR_NUM_RCX = 0x01, SR_NUM_RDX = 0x02, +#ifdef _TARGET_AMD64_ SR_NUM_R8 = 0x03, SR_NUM_R9 = 0x04, SR_NUM_R10 = 0x05, SR_NUM_R11 = 0x06, +#endif // _TARGET_AMD64_ }; enum ScratchRegMask @@ -242,16 +434,24 @@ private: UInt16 hasFunclets : 1; // 0 [6] UInt16 fixedEpilogSize : 6; // 0 [7] + 1 [0:4] '0' encoding implies that epilog size varies and is encoded for each epilog UInt16 epilogCountSmall : 2; // 1 [5:6] '3' encoding implies the number of epilogs is encoded separately - UInt16 hasExtraData : 1; // 1 [7] 1: frame uses dynamic alignment or/and GS cookie + UInt16 hasExtraData : 1; // 1 [7] 1: more data follows (dynamic alignment, GS cookie, common vars, etc.) #ifdef _TARGET_ARM_ UInt16 returnKind : 2; // 2 [0:1] one of: MethodReturnKind enum UInt16 ebpFrame : 1; // 2 [2] on x64, this means "has frame pointer and it is RBP", on ARM R7 UInt16 epilogAtEnd : 1; // 2 [3] - UInt16 hasFrameSize : 1; // 2 [4] 1: frame size is encoded below, 0: frame size is 0 + UInt16 hasFrameSize : 1; // 2 [4] 1: frame size is encoded below, 0: frame size is 0 UInt16 calleeSavedRegMask : NUM_PRESERVED_REGS; // 2 [5:7] 3 [0:5] - UInt16 arm_areParmOrVfpRegsPushed:1; // 1: pushed parm register set from R0-R3 and pushed fp reg start and count is encoded below, 0: no pushed parm or fp registers -#else // _TARGET_ARM_ + UInt16 arm_areParmOrVfpRegsPushed:1; // 3 [6] 1: pushed param reg set (R0-R3) and pushed fp reg start and count are encoded below, 0: no pushed param or fp registers +#elif defined (_TARGET_ARM64_) + UInt16 returnKind : 2; // 2 [0:1] one of: MethodReturnKind enum + UInt16 ebpFrame : 1; // 2 [2] 1: has frame pointer and it is FP + UInt16 epilogAtEnd : 1; // 2 [3] + UInt16 hasFrameSize : 1; // 2 [4] 1: frame size is encoded below, 0: frame size is 0 + UInt16 arm64_longCsrMask : 1; // 2 [5] 1: high bits of calleeSavedRegMask are encoded below + UInt16 arm64_areParmOrVfpRegsPushed : 1; // 2 [6] 1: pushed param reg count (X0-X7) and pushed fp reg set (D8-D15) are encoded below, 0: no pushed param or fp registers + UInt16 arm64_calleeSavedRegMaskLow : NUM_PRESERVED_REGS_LOW; // 2 [7] 3 [0:7] +#else UInt8 returnKind : 2; // 2 [0:1] one of: MethodReturnKind enum UInt8 ebpFrame : 1; // 2 [2] on x64, this means "has frame pointer and it is RBP", on ARM R7 UInt8 epilogAtEnd : 1; // 2 [3] @@ -274,7 +474,7 @@ private: // which describes them UInt8 hasFrameSize : 1; // 3 [7] 1: frame size is encoded below, 0: frame size is 0 #endif -#endif // _TARGET_ARM_ +#endif // // OPTIONAL FIELDS FOLLOW @@ -282,9 +482,9 @@ private: // The following values are encoded with variable-length integers on disk, but are decoded into these // fields in memory. // + + // For ARM and ARM64 this field stores the offset of the callee-saved area relative to FP/SP UInt32 frameSize; // expressed in pointer-sized units, only encoded if hasFrameSize==1 - - // OPTIONAL: only encoded if returnKind = MRK_ReturnsToNative UInt32 reversePinvokeFrameOffset; // expressed in pointer-sized units away from the frame pointer @@ -315,13 +515,20 @@ private: // that can be expressed by a 'ret NNNN' instruction. Therefore, with 6 in the 'low' field and 8 in the // 'high' field, we are not losing any range here. (Although the need for that full range is debatable.) UInt8 x86_argCountHigh; +#elif defined(_TARGET_ARM_) + // OPTIONAL: only encoded if arm_areParmOrVfpRegsPushed = 1 + UInt8 arm_parmRegsPushedSet; + UInt8 arm_vfpRegFirstPushed; + UInt8 arm_vfpRegPushedCount; +#elif defined(_TARGET_ARM64_) + // OPTIONAL: high bits of calleeSavedRegMask are encoded only if arm64_longCsrMask = 1; low bits equal to arm64_calleeSavedRegMaskLow + UInt16 calleeSavedRegMask; + + // OPTIONAL: only encoded if arm64_areParmOrVfpRegsPushed = 1 + UInt8 arm64_parmRegsPushedCount; // how many of X0-X7 registers are saved + UInt8 arm64_vfpRegsPushedMask; // which of D8-D15 registers are saved #endif -#ifdef _TARGET_ARM_ - UInt8 arm_parmRegsPushedSet; - UInt8 arm_vfpRegFirstPushed; - UInt8 arm_vfpRegPushedCount; -#endif // // OPTIONAL: only encoded if hasExtraData = 1 union @@ -332,7 +539,12 @@ private: UInt8 hasGSCookie : 1; // [4] 1: frame uses GS cookie UInt8 hasCommonVars : 1; // [5] 1: method has a list of "common vars" // as an optimization for methods with many call sites and variables +#if defined(_TARGET_ARM64_) + UInt8 FPLRAreOnTop : 1; // [6] 1: FP and LR are saved on top of locals, not at the bottom (see MdmSaveFPAndLRAtTopOfLocalsArea) + UInt8 extraDataUnused : 1; // [7] unused bits +#else UInt8 extraDataUnused : 2; // [6:7] unused bits +#endif #pragma warning(suppress:4201) // nameless struct }; UInt8 extraDataHeader; @@ -424,8 +636,15 @@ public: void SetPrologSize(UInt32 sizeInBytes) { +#if defined (_TARGET_ARM64_) + // For arm64 we encode multiples of 4, rather than raw bytes, since instructions are all same size. + ASSERT((sizeInBytes & 3) == 0); + prologSize = sizeInBytes >> 2; + ASSERT(prologSize == sizeInBytes >> 2); +#else prologSize = sizeInBytes; ASSERT(prologSize == sizeInBytes); +#endif } void SetHasFunclets(bool fHasFunclets) @@ -433,6 +652,19 @@ public: hasFunclets = fHasFunclets ? 1 : 0; } + void PokeFixedEpilogSize(UInt32 sizeInBytes) + { +#if defined (_TARGET_ARM64_) + // For arm64 we encode multiples of 4, rather than raw bytes, since instructions are all same size. + ASSERT((sizeInBytes & 3) == 0); + fixedEpilogSize = sizeInBytes >> 2; + ASSERT(fixedEpilogSize == sizeInBytes >> 2); +#else + fixedEpilogSize = sizeInBytes; + ASSERT(fixedEpilogSize == sizeInBytes); +#endif + } + void SetFixedEpilogSize(UInt32 sizeInBytes, bool varyingSizes) { if (varyingSizes) @@ -440,8 +672,7 @@ public: else { ASSERT(sizeInBytes != 0); - fixedEpilogSize = sizeInBytes; - ASSERT(fixedEpilogSize == sizeInBytes); + PokeFixedEpilogSize(sizeInBytes); } } @@ -475,6 +706,14 @@ public: paramPointerReg = RN_NONE; } +#if defined(_TARGET_ARM64_) + void SetFPLROnTop(void) + { + hasExtraData = 1; + FPLRAreOnTop = 1; + } +#endif + void SetGSCookieOffset(UInt32 offsetInBytes) { ASSERT(offsetInBytes != 0); @@ -513,8 +752,12 @@ public: { #ifdef _TARGET_ARM_ ASSERT(regNum == RN_R7); -#else +#elif defined(_TARGET_AMD64_) || defined(_TARGET_X86_) ASSERT(regNum == RN_EBP); +#elif defined(_TARGET_ARM64_) + ASSERT(regNum == RN_FP); +#else + ASSERT(!"NYI"); #endif ebpFrame = 1; } @@ -554,7 +797,7 @@ public: void SetSavedRegs(CalleeSavedRegMask regMask) { - calleeSavedRegMask = regMask; + calleeSavedRegMask = (UInt16)regMask; } void SetRegSaved(CalleeSavedRegMask regMask) @@ -568,19 +811,21 @@ public: ASSERT((offsetInBytes % POINTER_SIZE) == 0); ASSERT(GetReturnKind() == MRK_ReturnsToNative); -#if defined(_TARGET_ARM_) || defined(_TARGET_AMD64_) +#if defined(_TARGET_ARM_) || defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) // The offset can be either positive or negative on ARM and x64. bool isNeg = (offsetInBytes < 0); UInt32 uOffsetInBytes = isNeg ? -offsetInBytes : offsetInBytes; UInt32 uEncodedVal = ((uOffsetInBytes / POINTER_SIZE) << 1) | (isNeg ? 1 : 0); reversePinvokeFrameOffset = uEncodedVal; ASSERT(reversePinvokeFrameOffset == uEncodedVal); -#else +#elif defined (_TARGET_X86_) // Use a positive number because it encodes better and // the offset is always negative on x86. ASSERT(offsetInBytes < 0); reversePinvokeFrameOffset = (-offsetInBytes / POINTER_SIZE); ASSERT(reversePinvokeFrameOffset == (UInt32)(-offsetInBytes / POINTER_SIZE)); +#else + ASSERT(!"NYI"); #endif } @@ -623,9 +868,21 @@ public: arm_vfpRegPushedCount = vfpRegPushedCount; arm_areParmOrVfpRegsPushed = arm_parmRegsPushedSet != 0 || vfpRegPushedCount != 0; } -#endif +#elif defined(_TARGET_ARM64_) + void SetParmRegsPushedCount(UInt8 parmRegsPushedCount) + { + // pushed parameter registers are a subset of {R0-R7} + ASSERT(parmRegsPushedCount <= 8); + arm64_parmRegsPushedCount = parmRegsPushedCount; + arm64_areParmOrVfpRegsPushed = (arm64_parmRegsPushedCount != 0) || (arm64_vfpRegsPushedMask != 0); + } -#ifdef _TARGET_AMD64_ + void SetVfpRegsPushed(UInt8 vfpRegsPushedMask) + { + arm64_vfpRegsPushedMask = vfpRegsPushedMask; + arm64_areParmOrVfpRegsPushed = (arm64_parmRegsPushedCount != 0) || (arm64_vfpRegsPushedMask != 0); + } +#elif defined(_TARGET_AMD64_) void SetSavedXmmRegs(UInt32 savedXmmRegMask) { // any subset of xmm6-xmm15 may be saved, but no registers in xmm0-xmm5 should be present @@ -633,14 +890,23 @@ public: x64_hasSavedXmmRegs = savedXmmRegMask != 0; x64_savedXmmRegMask = (UInt16)savedXmmRegMask; } -#endif // _TARGET_AMD64_ +#endif + + void SetFuncletOffset(UInt32 offset) + { + funcletOffset = offset; + } // // GETTERS // UInt32 GetPrologSize() { +#if defined (_TARGET_ARM64_) + return prologSize << 2; +#else return prologSize; +#endif } bool HasFunclets() @@ -653,10 +919,19 @@ public: return fixedEpilogSize == 0; } + UInt32 PeekFixedEpilogSize() + { +#if defined (_TARGET_ARM64_) + return fixedEpilogSize << 2; +#else + return fixedEpilogSize; +#endif + } + UInt32 GetFixedEpilogSize() { ASSERT(!HasVaryingEpilogSizes()); - return fixedEpilogSize; + return PeekFixedEpilogSize(); } UInt32 GetEpilogCount() @@ -727,6 +1002,13 @@ public: return hasGSCookie; } +#if defined(_TARGET_ARM64_) + bool AreFPLROnTop() + { + return FPLRAreOnTop; + } +#endif + UInt32 GetGSCookieOffset() { ASSERT(hasGSCookie); @@ -821,7 +1103,7 @@ public: int GetReversePinvokeFrameOffset() { -#if defined(_TARGET_ARM_) || defined(_TARGET_AMD64_) +#if defined(_TARGET_ARM_) || defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) // The offset can be either positive or negative on ARM. Int32 offsetInBytes; UInt32 uEncodedVal = reversePinvokeFrameOffset; @@ -829,11 +1111,13 @@ public: offsetInBytes = (uEncodedVal >> 1) * POINTER_SIZE; offsetInBytes = isNeg ? -offsetInBytes : offsetInBytes; return offsetInBytes; -#else +#elif defined(_TARGET_X86_) // it's always at "EBP - something", so we encode it as a positive // number and then apply the negative here. int unsignedOffset = reversePinvokeFrameOffset * POINTER_SIZE; return -unsignedOffset; +#else + ASSERT(!"NYI"); #endif } @@ -874,6 +1158,16 @@ public: { return arm_vfpRegPushedCount; } +#elif defined(_TARGET_ARM64_) + UInt8 ParmRegsPushedCount() + { + return arm64_parmRegsPushedCount; + } + + UInt8 GetVfpRegsPushedMask() + { + return arm64_vfpRegsPushedMask; + } #endif // @@ -885,6 +1179,16 @@ public: #ifdef _DEBUG UInt8 * pStart = pDest; #endif // _DEBUG + +#if defined(_TARGET_ARM64_) + UInt8 calleeSavedRegMaskHigh = calleeSavedRegMask >> NUM_PRESERVED_REGS_LOW; + arm64_calleeSavedRegMaskLow = calleeSavedRegMask & MASK_PRESERVED_REGS_LOW; + if (calleeSavedRegMaskHigh) + { + arm64_longCsrMask = 1; + } +#endif + size_t size = EC_SizeOfFixedHeader; if (pDest) { @@ -901,6 +1205,7 @@ public: #ifdef _TARGET_AMD64_ if (x64_framePtrOffsetSmall == 0x3) size += WriteUnsigned(pDest, x64_framePtrOffset); + if (x64_hasSavedXmmRegs) { ASSERT((x64_savedXmmRegMask & 0x3f) == 0); @@ -912,14 +1217,10 @@ public: { size += 1; if (pDest) - { - *pDest = x86_argCountHigh; - pDest++; - } + *pDest++ = x86_argCountHigh; } -#endif - -#ifdef _TARGET_ARM_ + ASSERT(!x86_hasStackChanges || !"NYI -- stack changes for ESP frames"); +#elif defined(_TARGET_ARM_) if (arm_areParmOrVfpRegsPushed) { // we encode a bit field where the low 4 bits represent the pushed parameter register @@ -930,6 +1231,21 @@ public: // usually, the first pushed floating point register is d8 if (arm_vfpRegFirstPushed != 8) encodedValue |= (arm_vfpRegFirstPushed+1) << (8+4); + + size += WriteUnsigned(pDest, encodedValue); + } +#elif defined(_TARGET_ARM64_) + if (calleeSavedRegMaskHigh) + { + size += 1; + if (pDest) + *pDest++ = calleeSavedRegMaskHigh; + } + + if (arm64_areParmOrVfpRegsPushed) + { + // At present arm64_parmRegsPushedCount is non-zero only for variadic functions, so place this field higher + UInt32 encodedValue = arm64_vfpRegsPushedMask | (arm64_parmRegsPushedCount << 8); size += WriteUnsigned(pDest, encodedValue); } #endif @@ -1100,6 +1416,22 @@ public: else arm_vfpRegFirstPushed = (UInt8)(vfpRegFirstPushed - 1); } +#elif defined(_TARGET_ARM64_) + calleeSavedRegMask = arm64_calleeSavedRegMaskLow; + if (arm64_longCsrMask) + { + calleeSavedRegMask |= (*pbDecode++ << NUM_PRESERVED_REGS_LOW); + } + + arm64_parmRegsPushedCount = 0; + arm64_vfpRegsPushedMask = 0; + if (arm64_areParmOrVfpRegsPushed) + { + UInt32 encodedValue = VarInt::ReadUnsigned(pbDecode); + arm64_vfpRegsPushedMask = (UInt8)encodedValue; + arm64_parmRegsPushedCount = (UInt8)(encodedValue >> 8); + ASSERT(arm64_parmRegsPushedCount <= 8); + } #endif extraDataHeader = hasExtraData ? ToUInt8(VarInt::ReadUnsigned(pbDecode)) : 0; @@ -1117,7 +1449,7 @@ public: // the per-method epilog table, so at least we're consistent with what is encoded. UInt8 mainEpilogAtEnd = epilogAtEnd; UInt16 mainEpilogCount = epilogCount; - UInt16 mainFixedEpilogSize = fixedEpilogSize; + UInt16 mainFixedEpilogSize = (UInt16) PeekFixedEpilogSize(); UInt8 mainHasCommonVars = hasCommonVars; // ------- @@ -1169,7 +1501,7 @@ public: // WORKAROUND: see above this->epilogAtEnd = mainEpilogAtEnd; this->epilogCount = mainEpilogCount; - this->fixedEpilogSize = mainFixedEpilogSize; + this->PokeFixedEpilogSize(mainFixedEpilogSize); this->hasCommonVars = mainHasCommonVars; // ------- @@ -1228,6 +1560,9 @@ public: } #elif defined(_TARGET_ARM_) if (arm_areParmOrVfpRegsPushed) { VarInt::SkipUnsigned(pbDecode); } +#elif defined(_TARGET_ARM64_) + if (arm64_longCsrMask) { pbDecode++; } + if (arm64_areParmOrVfpRegsPushed) { VarInt::SkipUnsigned(pbDecode); } #endif *pnFuncletsOut = VarInt::ReadUnsigned(pbDecode); @@ -1247,7 +1582,7 @@ public: bool IsValidEpilogOffset(UInt32 epilogOffset, UInt32 epilogSize) { if (!this->HasVaryingEpilogSizes()) - return (epilogOffset < this->fixedEpilogSize); + return (epilogOffset < this->GetFixedEpilogSize()); else return (epilogOffset < epilogSize); } @@ -1265,25 +1600,11 @@ public: default: return "unknwn"; } } + #define PRINT_CALLEE_SAVE(name, mask, val) {if ((val) & (mask)) { printf(name); }} + void PrintCalleeSavedRegs(UInt32 calleeSavedRegMask) { -#ifdef _TARGET_AMD64_ - PRINT_CALLEE_SAVE(" rbx", CSR_MASK_RBX, calleeSavedRegMask); - PRINT_CALLEE_SAVE(" rsi", CSR_MASK_RSI, calleeSavedRegMask); - PRINT_CALLEE_SAVE(" rdi", CSR_MASK_RDI, calleeSavedRegMask); - PRINT_CALLEE_SAVE(" rbp", CSR_MASK_RBP, calleeSavedRegMask); - PRINT_CALLEE_SAVE(" r12", CSR_MASK_R12, calleeSavedRegMask); - PRINT_CALLEE_SAVE(" r13", CSR_MASK_R13, calleeSavedRegMask); - PRINT_CALLEE_SAVE(" r14", CSR_MASK_R14, calleeSavedRegMask); - PRINT_CALLEE_SAVE(" r15", CSR_MASK_R15, calleeSavedRegMask); -#endif // _TARGET_AMD64_ -#ifdef _TARGET_X86_ - PRINT_CALLEE_SAVE(" ebx", CSR_MASK_RBX, calleeSavedRegMask); - PRINT_CALLEE_SAVE(" esi", CSR_MASK_RSI, calleeSavedRegMask); - PRINT_CALLEE_SAVE(" edi", CSR_MASK_RDI, calleeSavedRegMask); - PRINT_CALLEE_SAVE(" ebp", CSR_MASK_RBP, calleeSavedRegMask); -#endif // _TARGET_X86_ #ifdef _TARGET_ARM_ PRINT_CALLEE_SAVE(" r4" , CSR_MASK_R4 , calleeSavedRegMask); PRINT_CALLEE_SAVE(" r5" , CSR_MASK_R5 , calleeSavedRegMask); @@ -1294,7 +1615,36 @@ public: PRINT_CALLEE_SAVE(" r10", CSR_MASK_R10, calleeSavedRegMask); PRINT_CALLEE_SAVE(" r11", CSR_MASK_R11, calleeSavedRegMask); PRINT_CALLEE_SAVE(" lr" , CSR_MASK_LR , calleeSavedRegMask); -#endif // _TARGET_ARM_ +#elif defined(_TARGET_ARM64_) + PRINT_CALLEE_SAVE(" lr" , CSR_MASK_LR , calleeSavedRegMask); + PRINT_CALLEE_SAVE(" x19", CSR_MASK_X19, calleeSavedRegMask); + PRINT_CALLEE_SAVE(" x20", CSR_MASK_X20, calleeSavedRegMask); + PRINT_CALLEE_SAVE(" x21", CSR_MASK_X21, calleeSavedRegMask); + PRINT_CALLEE_SAVE(" x22", CSR_MASK_X22, calleeSavedRegMask); + PRINT_CALLEE_SAVE(" x23", CSR_MASK_X23, calleeSavedRegMask); + PRINT_CALLEE_SAVE(" x24", CSR_MASK_X24, calleeSavedRegMask); + PRINT_CALLEE_SAVE(" x25", CSR_MASK_X25, calleeSavedRegMask); + PRINT_CALLEE_SAVE(" x26", CSR_MASK_X26, calleeSavedRegMask); + PRINT_CALLEE_SAVE(" x27", CSR_MASK_X27, calleeSavedRegMask); + PRINT_CALLEE_SAVE(" x28", CSR_MASK_X28, calleeSavedRegMask); + PRINT_CALLEE_SAVE(" fp" , CSR_MASK_FP , calleeSavedRegMask); +#elif defined(_TARGET_X86_) + PRINT_CALLEE_SAVE(" ebx", CSR_MASK_RBX, calleeSavedRegMask); + PRINT_CALLEE_SAVE(" esi", CSR_MASK_RSI, calleeSavedRegMask); + PRINT_CALLEE_SAVE(" edi", CSR_MASK_RDI, calleeSavedRegMask); + PRINT_CALLEE_SAVE(" ebp", CSR_MASK_RBP, calleeSavedRegMask); +#elif defined(_TARGET_AMD64_) + PRINT_CALLEE_SAVE(" rbx", CSR_MASK_RBX, calleeSavedRegMask); + PRINT_CALLEE_SAVE(" rsi", CSR_MASK_RSI, calleeSavedRegMask); + PRINT_CALLEE_SAVE(" rdi", CSR_MASK_RDI, calleeSavedRegMask); + PRINT_CALLEE_SAVE(" rbp", CSR_MASK_RBP, calleeSavedRegMask); + PRINT_CALLEE_SAVE(" r12", CSR_MASK_R12, calleeSavedRegMask); + PRINT_CALLEE_SAVE(" r13", CSR_MASK_R13, calleeSavedRegMask); + PRINT_CALLEE_SAVE(" r14", CSR_MASK_R14, calleeSavedRegMask); + PRINT_CALLEE_SAVE(" r15", CSR_MASK_R15, calleeSavedRegMask); +#else +#error unknown architecture +#endif } void PrintRegNumber(UInt8 regNumber) @@ -1319,6 +1669,42 @@ public: case RN_SP: printf(" sp"); break; case RN_LR: printf(" lr"); break; case RN_PC: printf(" pc"); break; +#elif defined(_TARGET_ARM64_) + case RN_X0: printf(" x0"); break; + case RN_X1: printf(" x1"); break; + case RN_X2: printf(" x2"); break; + case RN_X3: printf(" x3"); break; + case RN_X4: printf(" x4"); break; + case RN_X5: printf(" x5"); break; + case RN_X6: printf(" x6"); break; + case RN_X7: printf(" x7"); break; + case RN_X8: printf(" x8"); break; + case RN_X9: printf(" x9"); break; + case RN_X10: printf("x10"); break; + case RN_X11: printf("x11"); break; + case RN_X12: printf("x12"); break; + case RN_X13: printf("x13"); break; + case RN_X14: printf("x14"); break; + case RN_X15: printf("x15"); break; + + case RN_XIP0: printf("xip0"); break; + case RN_XIP1: printf("xip1"); break; + case RN_XPR: printf("xpr"); break; + + case RN_X19: printf("x19"); break; + case RN_X20: printf("x20"); break; + case RN_X21: printf("x21"); break; + case RN_X22: printf("x22"); break; + case RN_X23: printf("x23"); break; + case RN_X24: printf("x24"); break; + case RN_X25: printf("x25"); break; + case RN_X26: printf("x26"); break; + case RN_X27: printf("x27"); break; + case RN_X28: printf("x28"); break; + + case RN_FP: printf(" fp"); break; + case RN_LR: printf(" lr"); break; + case RN_SP: printf(" sp"); break; #elif defined(_TARGET_X86_) case RN_EAX: printf("eax"); break; case RN_ECX: printf("ecx"); break; @@ -1354,7 +1740,7 @@ public: void Dump() { printf(" | prologSize: %02X"" | epilogSize: %02X"" | epilogCount: %02X"" | epilogAtEnd: %s\n", - prologSize, fixedEpilogSize, epilogCount, GetBoolStr(epilogAtEnd)); + GetPrologSize(), PeekFixedEpilogSize(), epilogCount, GetBoolStr(epilogAtEnd)); printf(" | frameSize: %04X"" | ebpFrame: %s"" | hasFunclets: %s"" | returnKind: %s\n", GetFrameSize(), GetBoolStr(ebpFrame), GetBoolStr(hasFunclets), GetRetKindStr(returnKind)); printf(" | regMask: %04X" " {", calleeSavedRegMask); @@ -1391,6 +1777,24 @@ public: printf(" }\n"); } } +#elif defined(_TARGET_ARM64_) + if (arm64_areParmOrVfpRegsPushed) + { + if (arm64_parmRegsPushedCount != 0) + { + printf(" | parmRegsCount: %d\n", arm64_parmRegsPushedCount); + } + if (arm64_vfpRegsPushedMask != 0) + { + printf(" | vfpRegs: %02X {", arm64_vfpRegsPushedMask); + for (int reg = 0; reg < 8; reg++) + { + if (arm64_vfpRegsPushedMask & (1 << reg)) + printf(" d%d", reg + 8); + } + printf(" }\n"); + } + } #elif defined(_TARGET_AMD64_) if (x64_hasSavedXmmRegs) { diff --git a/external/corert/src/Native/Runtime/inc/rhbinder.h b/external/corert/src/Native/Runtime/inc/rhbinder.h index 41b360e622..1dee732023 100644 --- a/external/corert/src/Native/Runtime/inc/rhbinder.h +++ b/external/corert/src/Native/Runtime/inc/rhbinder.h @@ -563,6 +563,11 @@ struct InterfaceDispatchCell // a single instruction within our stubs. enum PInvokeTransitionFrameFlags { + // NOTE: Keep in sync with ndp\FxCore\CoreRT\src\Native\Runtime\arm\AsmMacros.h + + // NOTE: The order in which registers get pushed in the PInvokeTransitionFrame's m_PreservedRegs list has + // to match the order of these flags (that's also the order in which they are read in StackFrameIterator.cpp + // standard preserved registers PTFF_SAVE_R4 = 0x00000001, PTFF_SAVE_R5 = 0x00000002, @@ -588,10 +593,78 @@ enum PInvokeTransitionFrameFlags PTFF_R0_IS_GCREF = 0x00004000, // used by hijack handler to report return value of hijacked method PTFF_R0_IS_BYREF = 0x00008000, // used by hijack handler to report return value of hijacked method + + PTFF_THREAD_ABORT = 0x00010000, // indicates that ThreadAbortException should be thrown when returning from the transition +}; +#elif defined(_TARGET_ARM64_) +enum PInvokeTransitionFrameFlags : UInt64 +{ + // NOTE: Keep in sync with ndp\FxCore\CoreRT\src\Native\Runtime\arm64\AsmMacros.h + + // NOTE: The order in which registers get pushed in the PInvokeTransitionFrame's m_PreservedRegs list has + // to match the order of these flags (that's also the order in which they are read in StackFrameIterator.cpp + + // standard preserved registers + PTFF_SAVE_X19 = 0x0000000000000001, + PTFF_SAVE_X20 = 0x0000000000000002, + PTFF_SAVE_X21 = 0x0000000000000004, + PTFF_SAVE_X22 = 0x0000000000000008, + PTFF_SAVE_X23 = 0x0000000000000010, + PTFF_SAVE_X24 = 0x0000000000000020, + PTFF_SAVE_X25 = 0x0000000000000040, + PTFF_SAVE_X26 = 0x0000000000000080, + PTFF_SAVE_X27 = 0x0000000000000100, + PTFF_SAVE_X28 = 0x0000000000000200, + + PTFF_SAVE_SP = 0x0000000000000400, // Used for 'coop pinvokes' in runtime helper routines. Methods with + // PInvokes are required to have a frame pointers, but methods which + // call runtime helpers are not. Therefore, methods that call runtime + // helpers may need SP to seed the stackwalk. + + // Scratch registers + PTFF_SAVE_X0 = 0x0000000000000800, + PTFF_SAVE_X1 = 0x0000000000001000, + PTFF_SAVE_X2 = 0x0000000000002000, + PTFF_SAVE_X3 = 0x0000000000004000, + PTFF_SAVE_X4 = 0x0000000000008000, + PTFF_SAVE_X5 = 0x0000000000010000, + PTFF_SAVE_X6 = 0x0000000000020000, + PTFF_SAVE_X7 = 0x0000000000040000, + PTFF_SAVE_X8 = 0x0000000000080000, + PTFF_SAVE_X9 = 0x0000000000100000, + PTFF_SAVE_X10 = 0x0000000000200000, + PTFF_SAVE_X11 = 0x0000000000400000, + PTFF_SAVE_X12 = 0x0000000000800000, + PTFF_SAVE_X13 = 0x0000000001000000, + PTFF_SAVE_X14 = 0x0000000002000000, + PTFF_SAVE_X15 = 0x0000000004000000, + PTFF_SAVE_X16 = 0x0000000008000000, + PTFF_SAVE_X17 = 0x0000000010000000, + PTFF_SAVE_X18 = 0x0000000020000000, + + PTFF_SAVE_FP = 0x0000000040000000, // should never be used, we require FP frames for methods with + // pinvoke and it is saved into the frame pointer field instead + + PTFF_SAVE_LR = 0x0000000080000000, // this is useful for the case of loop hijacking where we need both + // a return address pointing into the hijacked method and that method's + // lr register, which may hold a gc pointer + + // Other flags + PTFF_X0_IS_GCREF = 0x0000000100000000, // used by hijack handler to report return value of hijacked method + PTFF_X0_IS_BYREF = 0x0000000200000000, // used by hijack handler to report return value of hijacked method + PTFF_X1_IS_GCREF = 0x0000000400000000, // used by hijack handler to report return value of hijacked method + PTFF_X1_IS_BYREF = 0x0000000800000000, // used by hijack handler to report return value of hijacked method + + PTFF_THREAD_ABORT = 0x0000001000000000, // indicates that ThreadAbortException should be thrown when returning from the transition }; #else // _TARGET_ARM_ enum PInvokeTransitionFrameFlags { + // NOTE: Keep in sync with ndp\FxCore\CoreRT\src\Native\Runtime\[amd64|i386]\AsmMacros.inc + + // NOTE: The order in which registers get pushed in the PInvokeTransitionFrame's m_PreservedRegs list has + // to match the order of these flags (that's also the order in which they are read in StackFrameIterator.cpp + // standard preserved registers PTFF_SAVE_RBX = 0x00000001, PTFF_SAVE_RSI = 0x00000002, @@ -621,27 +694,49 @@ enum PInvokeTransitionFrameFlags PTFF_RAX_IS_GCREF = 0x00010000, // used by hijack handler to report return value of hijacked method PTFF_RAX_IS_BYREF = 0x00020000, // used by hijack handler to report return value of hijacked method + + PTFF_THREAD_ABORT = 0x00040000, // indicates that ThreadAbortException should be thrown when returning from the transition }; #endif // _TARGET_ARM_ #pragma warning(push) #pragma warning(disable:4200) // nonstandard extension used: zero-sized array in struct/union class Thread; +#if defined(USE_PORTABLE_HELPERS) +//the members of this structure are currently unused except m_pThread and exist only to allow compilation +//of StackFrameIterator their values are not currently being filled in and will require significant rework +//in order to satisfy the runtime requirements of StackFrameIterator +struct PInvokeTransitionFrame +{ + void* m_RIP; + Thread* m_pThread; // unused by stack crawler, this is so GetThread is only called once per method + // can be an invalid pointer in universal transition cases (which never need to call GetThread) + uint32_t m_Flags; // PInvokeTransitionFrameFlags +}; +#else // USE_PORTABLE_HELPERS struct PInvokeTransitionFrame { #ifdef _TARGET_ARM_ TgtPTR_Void m_ChainPointer; // R11, used by OS to walk stack quickly #endif +#ifdef _TARGET_ARM64_ + // On arm64, the FP and LR registers are pushed in that order when setting up frames + TgtPTR_Void m_FramePointer; + TgtPTR_Void m_RIP; +#else TgtPTR_Void m_RIP; TgtPTR_Void m_FramePointer; +#endif TgtPTR_Thread m_pThread; // unused by stack crawler, this is so GetThread is only called once per method // can be an invalid pointer in universal transition cases (which never need to call GetThread) - UInt32 m_dwFlags; // PInvokeTransitionFrameFlags -#ifdef _TARGET_AMD64_ - UInt32 m_dwAlignPad2; -#endif +#ifdef _TARGET_ARM64_ + UInt64 m_Flags; // PInvokeTransitionFrameFlags +#else + UInt32 m_Flags; // PInvokeTransitionFrameFlags +#endif UIntTarget m_PreservedRegs[]; }; +#endif // USE_PORTABLE_HELPERS #pragma warning(pop) #ifdef _TARGET_AMD64_ @@ -755,15 +850,6 @@ enum RhEHClauseKind #define RH_EH_CLAUSE_TYPED_INDIRECT RH_EH_CLAUSE_UNUSED -// Structure used to store offsets information of thread static fields, and mainly used -// by Reflection to get the address of that field in the TLS block -struct ThreadStaticFieldOffsets -{ - UInt32 StartingOffsetInTlsBlock; // Offset in the TLS block containing the thread static fields of a given type - UInt32 FieldOffset; // Offset of a thread static field from the start of its containing type's TLS fields block - // (in other words, the address of a field is 'TLS block + StartingOffsetInTlsBlock + FieldOffset') -}; - #ifndef RHDUMP // as System::__Canon is not exported by the SharedLibrary.dll, it is represented by a special "pointer" for generic unification #ifdef BINDER diff --git a/external/corert/src/Native/Runtime/inc/stressLog.h b/external/corert/src/Native/Runtime/inc/stressLog.h index 0c5addf948..f6cfaceb64 100644 --- a/external/corert/src/Native/Runtime/inc/stressLog.h +++ b/external/corert/src/Native/Runtime/inc/stressLog.h @@ -494,20 +494,6 @@ struct StressLogChunk UInt32 dwSig2; #ifndef DACCESS_COMPILE - static HANDLE s_LogChunkHeap; - - void * operator new (size_t) - { - _ASSERTE (s_LogChunkHeap != NULL); - //no need to zero memory because we could handle garbage contents - return PalHeapAlloc (s_LogChunkHeap, 0, sizeof (StressLogChunk)); - } - - void operator delete (void * chunk) - { - _ASSERTE (s_LogChunkHeap != NULL); - PalHeapFree (s_LogChunkHeap, 0, chunk); - } StressLogChunk (PTR_StressLogChunk p = NULL, PTR_StressLogChunk n = NULL) :prev (p), next (n), dwSig1 (0xCFCFCFCF), dwSig2 (0xCFCFCFCF) @@ -701,7 +687,7 @@ inline StressMsg* ThreadStressLog::AdvReadPastBoundary() { inline ThreadStressLog::ThreadStressLog() { chunkListHead = chunkListTail = curWriteChunk = NULL; - StressLogChunk * newChunk =new StressLogChunk; + StressLogChunk * newChunk = new (nothrow) StressLogChunk; //OOM or in cantalloc region if (newChunk == NULL) { @@ -754,7 +740,7 @@ FORCEINLINE bool ThreadStressLog::GrowChunkList () { return FALSE; } - StressLogChunk * newChunk = new StressLogChunk (chunkListTail, chunkListHead); + StressLogChunk * newChunk = new (nothrow) StressLogChunk (chunkListTail, chunkListHead); if (newChunk == NULL) { return FALSE; diff --git a/external/corert/src/Native/Runtime/module.cpp b/external/corert/src/Native/Runtime/module.cpp index e8e9f7ed0b..6cfa13f953 100644 --- a/external/corert/src/Native/Runtime/module.cpp +++ b/external/corert/src/Native/Runtime/module.cpp @@ -821,6 +821,9 @@ void * Module::GetClasslibFunction(ClasslibFunctionId functionId) case ClasslibFunctionId::CheckStaticClassConstruction: pMethod = m_pModuleHeader->Get_CheckStaticClassConstruction(); break; + case ClasslibFunctionId::OnFirstChanceException: + pMethod = m_pModuleHeader->Get_OnFirstChanceException(); + break; default: pMethod = NULL; break; @@ -829,6 +832,14 @@ void * Module::GetClasslibFunction(ClasslibFunctionId functionId) return pMethod; } +PTR_VOID Module::GetAssociatedData(PTR_VOID ControlPC) +{ + UNREFERENCED_PARAMETER(ControlPC); + + // Not supported for ProjectN. + return NULL; +} + // Get classlib-defined helper for running deferred static class constructors. Returns NULL if this is not the // classlib module or the classlib doesn't implement this callback. void * Module::GetClasslibCheckStaticClassConstruction() @@ -1024,12 +1035,6 @@ void Module::UnsynchronizedResetHijackedLoops() } } -EXTERN_C void * FASTCALL RecoverLoopHijackTarget(UInt32 entryIndex, ModuleHeader * pModuleHeader) -{ - Module * pModule = GetRuntimeInstance()->FindModuleByReadOnlyDataAddress(pModuleHeader); - return pModule->RecoverLoopHijackTarget(entryIndex, pModuleHeader); -} - void * Module::RecoverLoopHijackTarget(UInt32 entryIndex, ModuleHeader * pModuleHeader) { // read lock scope diff --git a/external/corert/src/Native/Runtime/module.h b/external/corert/src/Native/Runtime/module.h index a037bc828d..02d355d09b 100644 --- a/external/corert/src/Native/Runtime/module.h +++ b/external/corert/src/Native/Runtime/module.h @@ -13,6 +13,8 @@ struct VSDInterfaceTargetInfo; class DispatchMap; struct BlobHeader; +#ifdef PROJECTN + class Module : public ICodeManager { friend class AsmOffsets; @@ -102,7 +104,7 @@ public: PTR_ModuleHeader GetModuleHeader(); - HANDLE GetOsModuleHandle(); + PTR_VOID GetOsModuleHandle(); BlobHeader * GetReadOnlyBlobs(UInt32 * pcbBlobs); @@ -113,11 +115,13 @@ public: void * RecoverLoopHijackTarget(UInt32 entryIndex, ModuleHeader * pModuleHeader); + PTR_VOID GetAssociatedData(PTR_VOID ControlPC); + private: Module(ModuleHeader * pModuleHeader); #ifdef FEATURE_CUSTOM_IMPORTS static void DoCustomImports(ModuleHeader * pModuleHeader); - PTR_UInt8 GetBaseAddress() { return (PTR_UInt8)(size_t)GetOsModuleHandle(); } + PTR_UInt8 GetBaseAddress() { return (PTR_UInt8)GetOsModuleHandle(); } #endif // FEATURE_CUSTOM_IMPORTS @@ -142,3 +146,50 @@ private: ReaderWriterLock m_loopHijackMapLock; MapSHash m_loopHijackIndexToTargetMap; }; + +#else // PROJECTN + +// Stubbed out implementation of "Module" code manager. The "Module" code managed is needed for MDIL binder +// generated binaries in ProjectN only. + +class Module : public ICodeManager +{ + friend struct DefaultSListTraits; + friend class RuntimeInstance; + +public: + static Module * Create(ModuleHeader *pModuleHeader) { return NULL; } + void Destroy() { } + + bool ContainsCodeAddress(PTR_VOID pvAddr) { return false; } + bool ContainsDataAddress(PTR_VOID pvAddr) { return false; } + bool ContainsReadOnlyDataAddress(PTR_VOID pvAddr) { return false; } + bool ContainsStubAddress(PTR_VOID pvAddr) { return false; } + + static void EnumStaticGCRefsBlock(void * pfnCallback, void * pvCallbackData, PTR_StaticGcDesc pStaticGcInfo, PTR_UInt8 pbStaticData) { } + void EnumStaticGCRefs(void * pfnCallback, void * pvCallbackData) { } + + bool IsClasslibModule() { return false; } + void * GetClasslibInitializeFinalizerThread() { return NULL; } + + bool IsContainedBy(HANDLE hOsHandle) { return false; } + + DispatchMap ** GetDispatchMapLookupTable() { return NULL; } + + PTR_ModuleHeader GetModuleHeader() { return NULL; } + + EEType * GetArrayBaseType() { return NULL; } + + bool IsFinalizerInitComplete() { return false; } + void SetFinalizerInitComplete() { } + + void UnsynchronizedResetHijackedLoops() { } + void UnsynchronizedHijackAllLoops() { } + + void * RecoverLoopHijackTarget(UInt32 entryIndex, ModuleHeader * pModuleHeader) { return NULL; } + +private: + PTR_Module m_pNext; +}; + +#endif // PROJECTN diff --git a/external/corert/src/Native/Runtime/portable.cpp b/external/corert/src/Native/Runtime/portable.cpp index 74ab3c9fa4..fb7231b2dc 100644 --- a/external/corert/src/Native/Runtime/portable.cpp +++ b/external/corert/src/Native/Runtime/portable.cpp @@ -35,7 +35,7 @@ #include "GCMemoryHelpers.h" #include "GCMemoryHelpers.inl" -#ifdef USE_PORTABLE_HELPERS +#if defined(USE_PORTABLE_HELPERS) EXTERN_C REDHAWK_API void* REDHAWK_CALLCONV RhpGcAlloc(EEType *pEEType, UInt32 uFlags, UIntNative cbSize, void * pTransitionFrame); EXTERN_C REDHAWK_API void* REDHAWK_CALLCONV RhpPublishObject(void* pObject, UIntNative cbSize); @@ -171,6 +171,16 @@ COOP_PINVOKE_HELPER(Array *, RhpNewArray, (EEType * pArrayEEType, int numElement return pObject; } +COOP_PINVOKE_HELPER(String *, RhNewString, (EEType * pArrayEEType, int numElements)) +{ + // TODO: Implement. We tail call to RhpNewArray for now since there's a bunch of TODOs in the places + // that matter anyway. + return (String*)RhpNewArray(pArrayEEType, numElements); +} + +#endif +#if defined(USE_PORTABLE_HELPERS) + #ifdef _ARM_ COOP_PINVOKE_HELPER(Object *, RhpNewFinalizableAlign8, (EEType* pEEType)) { @@ -201,19 +211,6 @@ COOP_PINVOKE_HELPER(Array *, RhpNewArrayAlign8, (EEType * pArrayEEType, int numE } #endif -// -// PInvoke -// -COOP_PINVOKE_HELPER(void, RhpPInvoke, (void* pFrame)) -{ - // TODO: RhpPInvoke -} - -COOP_PINVOKE_HELPER(void, RhpPInvokeReturn, (void* pFrame)) -{ - // TODO: RhpPInvokeReturn -} - COOP_PINVOKE_HELPER(void, RhpInitialDynamicInterfaceDispatch, ()) { ASSERT_UNCONDITIONALLY("NYI"); @@ -294,6 +291,9 @@ EXTERN_C void * ReturnFromCallDescrThunk; #ifdef USE_PORTABLE_HELPERS void * ReturnFromCallDescrThunk; #endif + +#if defined(USE_PORTABLE_HELPERS) || defined(PLATFORM_UNIX) +#if !defined (_ARM64_) // // Return address hijacking // @@ -321,9 +321,12 @@ COOP_PINVOKE_HELPER(void, RhpGcStressHijackByref, ()) { ASSERT_UNCONDITIONALLY("NYI"); } +#endif +#endif // defined(USE_PORTABLE_HELPERS) || defined(PLATFORM_UNIX) -#ifdef USE_PORTABLE_HELPERS +#if defined(USE_PORTABLE_HELPERS) +#if !defined (_ARM64_) COOP_PINVOKE_HELPER(void, RhpAssignRef, (Object ** dst, Object * ref)) { // @TODO: USE_PORTABLE_HELPERS - Null check @@ -337,6 +340,7 @@ COOP_PINVOKE_HELPER(void, RhpCheckedAssignRef, (Object ** dst, Object * ref)) *dst = ref; InlineCheckedWriteBarrier(dst, ref); } +#endif COOP_PINVOKE_HELPER(Object *, RhpCheckedLockCmpXchg, (Object ** location, Object * value, Object * comparand)) { @@ -368,12 +372,19 @@ COOP_PINVOKE_HELPER(Int64, RhpLockCmpXchg64, (Int64 * location, Int64 value, Int #endif // USE_PORTABLE_HELPERS +#if !defined(_ARM64_) COOP_PINVOKE_HELPER(void, RhpMemoryBarrier, ()) { PalMemoryBarrier(); } +#endif + +#if defined(USE_PORTABLE_HELPERS) +EXTERN_C REDHAWK_API void* __cdecl RhAllocateThunksMapping() +{ + return NULL; +} -#ifdef USE_PORTABLE_HELPERS COOP_PINVOKE_HELPER(void *, RhpGetThunksBase, ()) { return NULL; @@ -443,6 +454,7 @@ COOP_PINVOKE_HELPER(void *, RhGetCurrentThunkContext, ()) #endif +#if !defined(_ARM64_) COOP_PINVOKE_HELPER(void, RhpETWLogLiveCom, (Int32 eventType, void * ccwHandle, void * objectId, void * typeRawValue, void * iUnknown, void * vTable, Int32 comRefCount, Int32 jupiterRefCount, Int32 flags)) { ASSERT_UNCONDITIONALLY("NYI"); @@ -453,3 +465,5 @@ COOP_PINVOKE_HELPER(bool, RhpETWShouldWalkCom, ()) ASSERT_UNCONDITIONALLY("NYI"); return false; } + +#endif diff --git a/external/corert/src/Native/Runtime/regdisplay.h b/external/corert/src/Native/Runtime/regdisplay.h index 24c6c5a753..aa234717e5 100644 --- a/external/corert/src/Native/Runtime/regdisplay.h +++ b/external/corert/src/Native/Runtime/regdisplay.h @@ -140,7 +140,25 @@ struct REGDISPLAY inline void SetAddrOfIP(PTR_PCODE pIP) { this->pIP = pIP; } inline void SetSP(UIntNative SP) { this->SP = SP; } }; +#elif defined(_TARGET_WASM_) -#endif // _X86_ || _AMD64_ +struct REGDISPLAY +{ + // TODO: WebAssembly doesn't really have registers. What exactly do we need here? + + UIntNative SP; + PTR_PCODE pIP; + PCODE IP; + + inline PCODE GetIP() { return NULL; } + inline PTR_PCODE GetAddrOfIP() { return NULL; } + inline UIntNative GetSP() { return 0; } + inline UIntNative GetFP() { return 0; } + + inline void SetIP(PCODE IP) { } + inline void SetAddrOfIP(PTR_PCODE pIP) { } + inline void SetSP(UIntNative SP) { } +}; +#endif // _X86_ || _AMD64_ || _ARM_ || _ARM64_ || _WASM_ typedef REGDISPLAY * PREGDISPLAY; diff --git a/external/corert/src/Native/Runtime/rhassert.cpp b/external/corert/src/Native/Runtime/rhassert.cpp index 4928befa08..70c656c30b 100644 --- a/external/corert/src/Native/Runtime/rhassert.cpp +++ b/external/corert/src/Native/Runtime/rhassert.cpp @@ -95,8 +95,17 @@ void Assert(const char * expr, const char * file, UInt32 line_num, const char * #endif //!DACCESS_COMPILE } -void NYI_Assert() +extern "C" void NYI_Assert(const char *message, ...) { +#if !defined(DACCESS_COMPILE) + va_list args; + va_start(args, message); + vprintf(message, args); + va_end(args); ASSERT_UNCONDITIONALLY("NYI"); +#else + UNREFERENCED_PARAMETER(message); +#endif } + #endif // _DEBUG diff --git a/external/corert/src/Native/Runtime/rhassert.h b/external/corert/src/Native/Runtime/rhassert.h index 5808941e84..9863822fc4 100644 --- a/external/corert/src/Native/Runtime/rhassert.h +++ b/external/corert/src/Native/Runtime/rhassert.h @@ -26,8 +26,6 @@ void Assert(const char * expr, const char * file, unsigned int line_num, const char * message); -void NYI_Assert(); - #else #define ASSERT(expr) @@ -38,7 +36,13 @@ void NYI_Assert(); #define ASSERT_UNCONDITIONALLY(message) -#endif +#endif + +#if defined(_DEBUG) + +void NYI_ASSERT(); + +#endif #define PORTABILITY_ASSERT(message) \ ASSERT_UNCONDITIONALLY(message); \ diff --git a/external/corert/src/Native/Runtime/startup.cpp b/external/corert/src/Native/Runtime/startup.cpp index 08233e855c..80f5769198 100644 --- a/external/corert/src/Native/Runtime/startup.cpp +++ b/external/corert/src/Native/Runtime/startup.cpp @@ -33,17 +33,14 @@ unsigned __int64 g_startupTimelineEvents[NUM_STARTUP_TIMELINE_EVENTS] = { 0 }; #endif // PROFILE_STARTUP -HANDLE RtuCreateRuntimeInstance(HANDLE hPalInstance); - - #ifdef PLATFORM_UNIX -Int32 __stdcall RhpHardwareExceptionHandler(UIntNative faultCode, UIntNative faultAddress, PAL_LIMITED_CONTEXT* palContext, UIntNative* arg0Reg, UIntNative* arg1Reg); +Int32 RhpHardwareExceptionHandler(UIntNative faultCode, UIntNative faultAddress, PAL_LIMITED_CONTEXT* palContext, UIntNative* arg0Reg, UIntNative* arg1Reg); #else Int32 __stdcall RhpVectoredExceptionHandler(PEXCEPTION_POINTERS pExPtrs); #endif -void CheckForPalFallback(); -void DetectCPUFeatures(); +static void CheckForPalFallback(); +static void DetectCPUFeatures(); extern RhConfig * g_pRhConfig; @@ -52,7 +49,7 @@ EXTERN_C bool g_fHasFastFxsave = false; CrstStatic g_CastCacheLock; CrstStatic g_ThunkPoolLock; -bool InitDLL(HANDLE hPalInstance) +static bool InitDLL(HANDLE hPalInstance) { CheckForPalFallback(); @@ -81,13 +78,11 @@ bool InitDLL(HANDLE hPalInstance) // // init per-instance state // - HANDLE hRuntimeInstance = RtuCreateRuntimeInstance(hPalInstance); - if (NULL == hRuntimeInstance) + if (!RuntimeInstance::Initialize(hPalInstance)) return false; + STARTUP_TIMELINE_EVENT(NONGC_INIT_COMPLETE); - // @TODO: currently we're always forcing a workstation GC. - // @TODO: GC per-instance vs per-DLL state separation RedhawkGCInterface::GCType gcType = g_pRhConfig->GetUseServerGC() ? RedhawkGCInterface::GCType_Server : RedhawkGCInterface::GCType_Workstation; @@ -122,7 +117,7 @@ bool InitDLL(HANDLE hPalInstance) return true; } -void CheckForPalFallback() +static void CheckForPalFallback() { #ifdef _DEBUG UInt32 disallowSetting = g_pRhConfig->GetDisallowRuntimeServicesFallback(); @@ -152,7 +147,7 @@ void CheckForPalFallback() void DetectCPUFeatures() { -#if !defined(CORERT) // @TODO: CORERT: DetectCPUFeatures +#ifdef PROJECTN // @TODO: CORERT: DetectCPUFeatures #ifdef _X86_ // We depend on fxsave / fxrstor. These were added to Pentium II and later, so they're pretty well guaranteed to be @@ -172,7 +167,7 @@ void DetectCPUFeatures() g_fHasFastFxsave = true; #endif -#endif // !CORERT +#endif // PROJECTN } #ifdef PROFILE_STARTUP @@ -189,7 +184,7 @@ int g_registerModuleCount = 0; RegisterModuleTrace g_registerModuleTraces[NUM_REGISTER_MODULE_TRACES] = { 0 }; -void AppendInt64(char * pBuffer, UInt32* pLen, UInt64 value) +static void AppendInt64(char * pBuffer, UInt32* pLen, UInt64 value) { char localBuffer[20]; int cch = 0; @@ -210,7 +205,7 @@ void AppendInt64(char * pBuffer, UInt32* pLen, UInt64 value) } #endif // PROFILE_STARTUP -bool UninitDLL(HANDLE /*hModDLL*/) +static void UninitDLL() { #ifdef PROFILE_STARTUP char buffer[1024]; @@ -232,18 +227,11 @@ bool UninitDLL(HANDLE /*hModDLL*/) fwrite(buffer, len, 1, stdout); #endif // PROFILE_STARTUP - return true; -} - -void DllThreadAttach(HANDLE /*hPalInstance*/) -{ - // We do not call ThreadStore::AttachThread from here because the loader lock is held. Instead, the - // threads themselves will do this on their first reverse pinvoke. } volatile bool g_processShutdownHasStarted = false; -void DllThreadDetach() +static void DllThreadDetach() { // BEWARE: loader lock is held here! @@ -304,36 +292,20 @@ COOP_PINVOKE_HELPER(UInt32_BOOL, RhpRegisterModule, (ModuleHeader *pModuleHeader return UInt32_TRUE; } -COOP_PINVOKE_HELPER(UInt32_BOOL, RhpEnableConservativeStackReporting, ()) +extern "C" bool RhInitialize() { - RuntimeInstance * pInstance = GetRuntimeInstance(); - if (!pInstance->EnableConservativeStackReporting()) - return UInt32_FALSE; + if (!PalInit()) + return false; - return UInt32_TRUE; + if (!InitDLL(PalGetModuleHandleFromPointer((void*)&RhInitialize))) + return false; + + return true; } -#endif // !DACCESS_COMPILE - -GPTR_IMPL_INIT(RuntimeInstance, g_pTheRuntimeInstance, NULL); - -#ifndef DACCESS_COMPILE - -// -// Creates a new runtime instance. -// -// @TODO: EXPORT -HANDLE RtuCreateRuntimeInstance(HANDLE hPalInstance) +COOP_PINVOKE_HELPER(void, RhpEnableConservativeStackReporting, ()) { - CreateHolder pRuntimeInstance = RuntimeInstance::Create(hPalInstance); - if (NULL == pRuntimeInstance) - return NULL; - - ASSERT_MSG(g_pTheRuntimeInstance == NULL, "multi-instances are not supported"); - g_pTheRuntimeInstance = pRuntimeInstance; - - pRuntimeInstance.SuppressRelease(); - return (HANDLE) pRuntimeInstance; + GetRuntimeInstance()->EnableConservativeStackReporting(); } // @@ -353,4 +325,33 @@ COOP_PINVOKE_HELPER(void, RhpShutdown, ()) g_processShutdownHasStarted = true; } +#ifdef _WIN32 +EXTERN_C UInt32_BOOL WINAPI RtuDllMain(HANDLE hPalInstance, UInt32 dwReason, void* /*pvReserved*/) +{ + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + { + STARTUP_TIMELINE_EVENT(PROCESS_ATTACH_BEGIN); + + if (!InitDLL(hPalInstance)) + return FALSE; + + STARTUP_TIMELINE_EVENT(PROCESS_ATTACH_COMPLETE); + } + break; + + case DLL_PROCESS_DETACH: + UninitDLL(); + break; + + case DLL_THREAD_DETACH: + DllThreadDetach(); + break; + } + + return TRUE; +} +#endif // _WIN32 + #endif // !DACCESS_COMPILE diff --git a/external/corert/src/Native/Runtime/stressLog.cpp b/external/corert/src/Native/Runtime/stressLog.cpp index cd50a3a96d..5e62780910 100644 --- a/external/corert/src/Native/Runtime/stressLog.cpp +++ b/external/corert/src/Native/Runtime/stressLog.cpp @@ -42,8 +42,6 @@ GPTR_IMPL(StressLog, g_pStressLog /*, &StressLog::theLog*/); #ifndef DACCESS_COMPILE -HANDLE StressLogChunk::s_LogChunkHeap = NULL; - /*********************************************************************************/ #if defined(_X86_) @@ -96,7 +94,6 @@ const static unsigned __int64 RECYCLE_AGE = 0x40000000L; // after a billi void StressLog::Initialize(unsigned facilities, unsigned level, unsigned maxBytesPerThread, unsigned maxBytesTotal, HANDLE hMod) { -#if !defined(CORERT) // @TODO: CORERT: disabled because of assumption that hMod is a module base address in stress log code if (theLog.MaxSizePerThread != 0) { // guard ourself against multiple initialization. First init wins. @@ -129,16 +126,6 @@ void StressLog::Initialize(unsigned facilities, unsigned level, unsigned maxByt theLog.startTimeStamp = getTimeStamp(); theLog.moduleOffset = (size_t)hMod; // HMODULES are base addresses. - -#ifndef APP_LOCAL_RUNTIME - StressLogChunk::s_LogChunkHeap = PalHeapCreate (0, STRESSLOG_CHUNK_SIZE * 128, 0); - if (StressLogChunk::s_LogChunkHeap == NULL) -#endif - { - StressLogChunk::s_LogChunkHeap = PalGetProcessHeap (); - } - _ASSERTE (StressLogChunk::s_LogChunkHeap); -#endif // !defined(CORERT) } /*********************************************************************************/ diff --git a/external/corert/src/Native/Runtime/thread.cpp b/external/corert/src/Native/Runtime/thread.cpp index db1668383a..059ec5abee 100644 --- a/external/corert/src/Native/Runtime/thread.cpp +++ b/external/corert/src/Native/Runtime/thread.cpp @@ -33,6 +33,9 @@ EXTERN_C REDHAWK_API void* REDHAWK_CALLCONV RhpHandleAlloc(void* pObject, int type); EXTERN_C REDHAWK_API void REDHAWK_CALLCONV RhHandleFree(void*); +static int (*g_RuntimeInitializationCallback)(); +static Thread* g_RuntimeInitializingThread; + #ifdef _MSC_VER extern "C" void _ReadWriteBarrier(void); #pragma intrinsic(_ReadWriteBarrier) @@ -289,10 +292,10 @@ void Thread::Construct() m_numDynamicTypesTlsCells = 0; m_pDynamicTypesTlsCells = NULL; -#if CORERT +#ifndef PROJECTN m_pThreadLocalModuleStatics = NULL; m_numThreadLocalModuleStatics = 0; -#endif // CORERT +#endif // PROJECTN // NOTE: We do not explicitly defer to the GC implementation to initialize the alloc_context. The // alloc_context will be initialized to 0 via the static initialization of tls_CurrentThread. If the @@ -320,6 +323,8 @@ void Thread::Construct() if (StressLog::StressLogOn(~0u, 0)) m_pThreadStressLog = StressLog::CreateThreadStressLog(this); #endif // STRESS_LOG + + m_threadAbortException = NULL; } bool Thread::IsInitialized() @@ -332,6 +337,8 @@ bool Thread::IsInitialized() // void Thread::SetGCSpecial(bool isGCSpecial) { + if (!IsInitialized()) + Construct(); if (isGCSpecial) SetState(TSF_IsGcSpecialThread); else @@ -376,7 +383,7 @@ void Thread::Destroy() delete[] m_pDynamicTypesTlsCells; } -#if CORERT +#ifndef PROJECTN if (m_pThreadLocalModuleStatics != NULL) { for (UInt32 i = 0; i < m_numThreadLocalModuleStatics; i++) @@ -388,7 +395,7 @@ void Thread::Destroy() } delete[] m_pThreadLocalModuleStatics; } -#endif // CORERT +#endif // !PROJECTN RedhawkGCInterface::ReleaseAllocContext(GetAllocContext()); @@ -529,6 +536,10 @@ void Thread::GcScanRootsWorker(void * pfnEnumCallback, void * pvCallbackData, St PTR_RtuObjectRef pExceptionObj = dac_cast(&curExInfo->m_exception); RedhawkGCInterface::EnumGcRef(pExceptionObj, GCRK_Object, pfnEnumCallback, pvCallbackData); } + + // Keep alive the ThreadAbortException that's stored in the target thread during thread abort + PTR_RtuObjectRef pThreadAbortExceptionObj = dac_cast(&m_threadAbortException); + RedhawkGCInterface::EnumGcRef(pThreadAbortExceptionObj, GCRK_Object, pfnEnumCallback, pvCallbackData); } #ifndef DACCESS_COMPILE @@ -1107,10 +1118,22 @@ FORCEINLINE bool Thread::InlineTryFastReversePInvoke(ReversePInvokeFrame * pFram return true; } +EXTERN_C void RhSetRuntimeInitializationCallback(int (*fPtr)()) +{ + g_RuntimeInitializationCallback = fPtr; +} + void Thread::ReversePInvokeAttachOrTrapThread(ReversePInvokeFrame * pFrame) { if (!IsStateSet(TSF_Attached)) + { + if (g_RuntimeInitializationCallback != NULL && g_RuntimeInitializingThread != this) + { + EnsureRuntimeInitialized(); + } + ThreadStore::AttachCurrentThread(); + } // If the thread is already in cooperative mode, this is a bad transition. if (IsCurrentThreadInCooperativeMode()) @@ -1141,6 +1164,24 @@ void Thread::ReversePInvokeAttachOrTrapThread(ReversePInvokeFrame * pFrame) } } +void Thread::EnsureRuntimeInitialized() +{ + while (PalInterlockedCompareExchangePointer((void *volatile *)&g_RuntimeInitializingThread, this, NULL) != NULL) + { + PalSleep(1); + } + + if (g_RuntimeInitializationCallback != NULL) + { + if (g_RuntimeInitializationCallback() != 0) + RhFailFast(); + + g_RuntimeInitializationCallback = NULL; + } + + PalInterlockedExchangePointer((void *volatile *)&g_RuntimeInitializingThread, NULL); +} + FORCEINLINE void Thread::InlineReversePInvokeReturn(ReversePInvokeFrame * pFrame) { m_pTransitionFrame = pFrame->m_savedPInvokeTransitionFrame; @@ -1150,7 +1191,48 @@ FORCEINLINE void Thread::InlineReversePInvokeReturn(ReversePInvokeFrame * pFrame } } -#if CORERT +FORCEINLINE void Thread::InlinePInvoke(PInvokeTransitionFrame * pFrame) +{ + pFrame->m_pThread = this; + // set our mode to preemptive + m_pTransitionFrame = pFrame; + + // We need to prevent compiler reordering between above write and below read. + _ReadWriteBarrier(); + + // now check if we need to trap the thread + if (ThreadStore::IsTrapThreadsRequested()) + { + RhpWaitForSuspend2(); + } +} + +FORCEINLINE void Thread::InlinePInvokeReturn(PInvokeTransitionFrame * pFrame) +{ + m_pTransitionFrame = NULL; + if (ThreadStore::IsTrapThreadsRequested()) + { + RhpWaitForGC2(pFrame); + } +} + +Object * Thread::GetThreadAbortException() +{ + return m_threadAbortException; +} + +void Thread::SetThreadAbortException(Object *exception) +{ + m_threadAbortException = exception; +} + +COOP_PINVOKE_HELPER(Object *, RhpGetThreadAbortException, ()) +{ + Thread * pCurThread = ThreadStore::RawGetCurrentThread(); + return pCurThread->GetThreadAbortException(); +} + +#ifndef PROJECTN Object* Thread::GetThreadStaticStorageForModule(UInt32 moduleIndex) { // Return a pointer to the TLS storage if it has already been @@ -1223,7 +1305,17 @@ COOP_PINVOKE_HELPER(Boolean, RhSetThreadStaticStorageForModule, (Array * pStorag Thread * pCurrentThread = ThreadStore::RawGetCurrentThread(); return pCurrentThread->SetThreadStaticStorageForModule((Object*)pStorage, moduleIndex); } -#endif // CORERT + +// This is function is used to quickly query a value that can uniquely identify a thread +COOP_PINVOKE_HELPER(UInt8*, RhCurrentNativeThreadId, ()) +{ +#ifndef PLATFORM_UNIX + return PalNtCurrentTeb(); +#else + return (UInt8*)ThreadStore::RawGetCurrentThread(); +#endif // PLATFORM_UNIX +} +#endif // !PROJECTN // Standard calling convention variant and actual implementation for RhpReversePInvokeAttachOrTrapThread EXTERN_C NOINLINE void FASTCALL RhpReversePInvokeAttachOrTrapThread2(ReversePInvokeFrame * pFrame) @@ -1253,4 +1345,20 @@ COOP_PINVOKE_HELPER(void, RhpReversePInvokeReturn2, (ReversePInvokeFrame * pFram pFrame->m_savedThread->InlineReversePInvokeReturn(pFrame); } +#ifdef USE_PORTABLE_HELPERS + +COOP_PINVOKE_HELPER(void, RhpPInvoke2, (PInvokeTransitionFrame* pFrame)) +{ + Thread * pCurThread = ThreadStore::RawGetCurrentThread(); + pCurThread->InlinePInvoke(pFrame); +} + +COOP_PINVOKE_HELPER(void, RhpPInvokeReturn2, (PInvokeTransitionFrame* pFrame)) +{ + //reenter cooperative mode + pFrame->m_pThread->InlinePInvokeReturn(pFrame); +} + +#endif //USE_PORTABLE_HELPERS + #endif // !DACCESS_COMPILE diff --git a/external/corert/src/Native/Runtime/thread.h b/external/corert/src/Native/Runtime/thread.h index 5eada83183..930114ba4a 100644 --- a/external/corert/src/Native/Runtime/thread.h +++ b/external/corert/src/Native/Runtime/thread.h @@ -14,7 +14,7 @@ class Thread; // runtime build. #define KEEP_THREAD_LAYOUT_CONSTANT -#if defined(_X86_) || defined(_ARM_) +#if defined(_X86_) || defined(_ARM_) || defined(_WASM_) # if defined(FEATURE_SVR_GC) || defined(KEEP_THREAD_LAYOUT_CONSTANT) # define SIZEOF_ALLOC_CONTEXT 40 # else // FEATURE_SVR_GC @@ -78,6 +78,7 @@ struct ThreadBuffer void ** m_ppvHijackedReturnAddressLocation; void * m_pvHijackedReturnAddress; PTR_ExInfo m_pExInfoStackHead; + Object* m_threadAbortException; // ThreadAbortException instance -set only during thread abort PTR_VOID m_pStackLow; PTR_VOID m_pStackHigh; PTR_UInt8 m_pTEB; // Pointer to OS TEB structure for this thread @@ -92,10 +93,10 @@ struct ThreadBuffer UInt32 m_numDynamicTypesTlsCells; PTR_PTR_UInt8 m_pDynamicTypesTlsCells; -#if CORERT +#ifndef PROJECTN PTR_PTR_VOID m_pThreadLocalModuleStatics; UInt32 m_numThreadLocalModuleStatics; -#endif // CORERT +#endif // PROJECTN }; struct ReversePInvokeFrame @@ -141,6 +142,7 @@ private: void ResetCachedTransitionFrame(); void CrossThreadUnhijack(); void UnhijackWorker(); + void EnsureRuntimeInitialized(); #ifdef _DEBUG bool DebugIsSuspended(); #endif @@ -254,10 +256,16 @@ public: bool InlineTryFastReversePInvoke(ReversePInvokeFrame * pFrame); void InlineReversePInvokeReturn(ReversePInvokeFrame * pFrame); -#if CORERT + void InlinePInvoke(PInvokeTransitionFrame * pFrame); + void InlinePInvokeReturn(PInvokeTransitionFrame * pFrame); + + Object * GetThreadAbortException(); + void SetThreadAbortException(Object *exception); + +#ifndef PROJECTN Object* GetThreadStaticStorageForModule(UInt32 moduleIndex); Boolean SetThreadStaticStorageForModule(Object * pStorage, UInt32 moduleIndex); -#endif // CORERT +#endif // PROJECTN }; #ifndef GCENV_INCLUDED diff --git a/external/corert/src/Native/Runtime/threadstore.cpp b/external/corert/src/Native/Runtime/threadstore.cpp index e8f650c2f6..d359c681d6 100644 --- a/external/corert/src/Native/Runtime/threadstore.cpp +++ b/external/corert/src/Native/Runtime/threadstore.cpp @@ -17,6 +17,7 @@ #include "holder.h" #include "Crst.h" #include "event.h" +#include "rhbinder.h" #include "RWLock.h" #include "threadstore.h" #include "threadstore.inl" @@ -28,7 +29,11 @@ #include "slist.inl" #include "GCMemoryHelpers.h" -EXTERN_C volatile UInt32 RhpTrapThreads = 0; +#include "Debug.h" +#include "DebugEventSource.h" +#include "DebugFuncEval.h" + +EXTERN_C volatile UInt32 RhpTrapThreads = (UInt32)TrapThreadsFlags::None; GVAL_IMPL_INIT(PTR_Thread, RhpSuspendingThread, 0); @@ -55,13 +60,18 @@ PTR_Thread ThreadStore::Iterator::GetNext() return pResult; } +//static +PTR_Thread ThreadStore::GetSuspendingThread() +{ + return (RhpSuspendingThread); +} #ifndef DACCESS_COMPILE ThreadStore::ThreadStore() : m_ThreadList(), - m_Lock() + m_Lock(true /* writers (i.e. attaching/detaching threads) should wait on GC event */) { SaveCurrentThreadOffsetForDAC(); } @@ -91,17 +101,6 @@ void ThreadStore::Destroy() delete this; } -#endif //!DACCESS_COMPILE - -//static -PTR_Thread ThreadStore::GetSuspendingThread() -{ - return (RhpSuspendingThread); -} -#ifndef DACCESS_COMPILE - -GPTR_DECL(RuntimeInstance, g_pTheRuntimeInstance); - // static void ThreadStore::AttachCurrentThread(bool fAcquireThreadStoreLock) { @@ -133,7 +132,7 @@ void ThreadStore::AttachCurrentThread(bool fAcquireThreadStoreLock) // see if that's going on and, if so, use a proper wait instead of the RWL's spinning. NOTE: when we are // called with fAcquireThreadStoreLock==false, we are being called in a situation where the GC is trying to // init a GC thread, so we must honor the flag to mean "do not block on GC" or else we will deadlock. - if (fAcquireThreadStoreLock && (RhpTrapThreads != 0)) + if (fAcquireThreadStoreLock && (RhpTrapThreads != (UInt32)TrapThreadsFlags::None)) RedhawkGCInterface::WaitForGCCompletion(); ThreadStore* pTS = GetThreadStore(); @@ -198,6 +197,7 @@ void ThreadStore::LockThreadStore() { m_Lock.AcquireReadLock(); } + void ThreadStore::UnlockThreadStore() { m_Lock.ReleaseReadLock(); @@ -205,6 +205,26 @@ void ThreadStore::UnlockThreadStore() void ThreadStore::SuspendAllThreads(CLREventStatic* pCompletionEvent) { + ThreadStore::SuspendAllThreads(pCompletionEvent, /* fireDebugEvent = */ true); +} + +void ThreadStore::SuspendAllThreads(CLREventStatic* pCompletionEvent, bool fireDebugEvent) +{ + // + // SuspendAllThreads requires all threads running + // + // Threads are by default frozen by the debugger during FuncEval + // Therefore, in case of FuncEval, we need to inform the debugger + // to unfreeze the threads. + // + if (fireDebugEvent && DebugFuncEval::GetMostRecentFuncEvalHijackInstructionPointer() != 0) + { + struct DebuggerFuncEvalCrossThreadDependencyNotification crossThreadDependencyEventPayload; + crossThreadDependencyEventPayload.kind = DebuggerResponseKind::FuncEvalCrossThreadDependency; + crossThreadDependencyEventPayload.payload = 0; + DebugEventSource::SendCustomEvent(&crossThreadDependencyEventPayload, sizeof(struct DebuggerFuncEvalCrossThreadDependencyNotification)); + } + Thread * pThisThread = GetCurrentThreadIfAvailable(); LockThreadStore(); @@ -215,10 +235,13 @@ void ThreadStore::SuspendAllThreads(CLREventStatic* pCompletionEvent) m_SuspendCompleteEvent.Reset(); // set the global trap for pinvoke leave and return - RhpTrapThreads = 1; + RhpTrapThreads |= (UInt32)TrapThreadsFlags::TrapThreads; + + // Set each module's loop hijack flag + GetRuntimeInstance()->SetLoopHijackFlags(RhpTrapThreads); // Our lock-free algorithm depends on flushing write buffers of all processors running RH code. The - // reason for this is that we essentially implement Decker's algorithm, which requires write ordering. + // reason for this is that we essentially implement Dekker's algorithm, which requires write ordering. PalFlushProcessWriteBuffers(); bool keepWaiting; @@ -279,7 +302,11 @@ void ThreadStore::ResumeAllThreads(CLREventStatic* pCompletionEvent) } END_FOREACH_THREAD - RhpTrapThreads = 0; + RhpTrapThreads &= ~(UInt32)TrapThreadsFlags::TrapThreads; + + // Reset module's hijackLoops flag + GetRuntimeInstance()->SetLoopHijackFlags(0); + RhpSuspendingThread = NULL; pCompletionEvent->Set(); UnlockThreadStore(); @@ -292,6 +319,84 @@ void ThreadStore::WaitForSuspendComplete() RhFailFast(); } +#ifndef DACCESS_COMPILE + +void ThreadStore::InitiateThreadAbort(Thread* targetThread, Object * threadAbortException, bool doRudeAbort) +{ + CLREventStatic dummyEvent; + SuspendAllThreads(&dummyEvent, /* fireDebugEvent = */ false); + // TODO: consider enabling multiple thread aborts running in parallel on different threads + ASSERT((RhpTrapThreads & (UInt32)TrapThreadsFlags::AbortInProgress) == 0); + RhpTrapThreads |= (UInt32)TrapThreadsFlags::AbortInProgress; + + targetThread->SetThreadAbortException(threadAbortException); + + // TODO: Stage 2: Queue APC to the target thread to break out of possible wait + + bool initiateAbort = false; + + if (!doRudeAbort) + { + // TODO: Stage 3: protected regions (finally, catch) handling + // If it was in a protected region, set the "throw at protected region end" flag on the native Thread object + // TODO: Stage 4: reverse PInvoke handling + // If there was a reverse Pinvoke frame between the current frame and the funceval frame of the target thread, + // find the outermost reverse Pinvoke frame below the funceval frame and set the thread abort flag in its transition frame. + // If both of these cases happened at once, find out which one of the outermost frame of the protected region + // and the outermost reverse Pinvoke frame is closer to the funceval frame and perform one of the two actions + // described above based on the one that's closer. + initiateAbort = true; + } + else + { + initiateAbort = true; + } + + if (initiateAbort) + { + PInvokeTransitionFrame* transitionFrame = reinterpret_cast(targetThread->GetTransitionFrame()); + transitionFrame->m_Flags |= PTFF_THREAD_ABORT; + } + + ResumeAllThreads(&dummyEvent); +} + +void ThreadStore::CancelThreadAbort(Thread* targetThread) +{ + CLREventStatic dummyEvent; + SuspendAllThreads(&dummyEvent, /* fireDebugEvent = */ false); + + ASSERT((RhpTrapThreads & (UInt32)TrapThreadsFlags::AbortInProgress) != 0); + RhpTrapThreads &= ~(UInt32)TrapThreadsFlags::AbortInProgress; + + PInvokeTransitionFrame* transitionFrame = reinterpret_cast(targetThread->GetTransitionFrame()); + if (transitionFrame != nullptr) + { + transitionFrame->m_Flags &= ~PTFF_THREAD_ABORT; + } + + targetThread->SetThreadAbortException(nullptr); + + ResumeAllThreads(&dummyEvent); +} + +COOP_PINVOKE_HELPER(void *, RhpGetCurrentThread, ()) +{ + return ThreadStore::GetCurrentThread(); +} + +COOP_PINVOKE_HELPER(void, RhpInitiateThreadAbort, (void* thread, Object * threadAbortException, Boolean doRudeAbort)) +{ + GetThreadStore()->InitiateThreadAbort((Thread*)thread, threadAbortException, doRudeAbort); +} + +COOP_PINVOKE_HELPER(void, RhpCancelThreadAbort, (void* thread)) +{ + GetThreadStore()->CancelThreadAbort((Thread*)thread); +} + +#endif // DACCESS_COMPILE + C_ASSERT(sizeof(Thread) == sizeof(ThreadBuffer)); EXTERN_C DECLSPEC_THREAD ThreadBuffer tls_CurrentThread = @@ -314,7 +419,6 @@ EXTERN_C DECLSPEC_THREAD ThreadBuffer tls_CurrentThread = #endif // !DACCESS_COMPILE - #ifdef _WIN32 #ifndef DACCESS_COMPILE @@ -329,7 +433,10 @@ volatile UInt32 * p_tls_index; volatile UInt32 SECTIONREL__tls_CurrentThread; EXTERN_C UInt32 _tls_index; - +#if defined(_TARGET_ARM64_) +// ARM64TODO: Re-enable optimization +#pragma optimize("", off) +#endif void ThreadStore::SaveCurrentThreadOffsetForDAC() { p_tls_index = &_tls_index; @@ -340,7 +447,9 @@ void ThreadStore::SaveCurrentThreadOffsetForDAC() SECTIONREL__tls_CurrentThread = (UInt32)((UInt8 *)&tls_CurrentThread - pOurTls); } - +#if defined(_TARGET_ARM64_) +#pragma optimize("", on) +#endif #else // DACCESS_COMPILE GPTR_IMPL(UInt32, p_tls_index); diff --git a/external/corert/src/Native/Runtime/threadstore.h b/external/corert/src/Native/Runtime/threadstore.h index 9a851c6c2e..8d6b18bc3e 100644 --- a/external/corert/src/Native/Runtime/threadstore.h +++ b/external/corert/src/Native/Runtime/threadstore.h @@ -7,6 +7,13 @@ class RuntimeInstance; class Array; typedef DPTR(RuntimeInstance) PTR_RuntimeInstance; +enum class TrapThreadsFlags +{ + None = 0, + AbortInProgress = 1, + TrapThreads = 2 +}; + class ThreadStore { SList m_ThreadList; @@ -19,6 +26,7 @@ private: void LockThreadStore(); void UnlockThreadStore(); + void SuspendAllThreads(CLREventStatic* pCompletionEvent, bool fireDebugEvent); public: class Iterator @@ -42,6 +50,8 @@ public: static void DetachCurrentThread(); #ifndef DACCESS_COMPILE static void SaveCurrentThreadOffsetForDAC(); + void InitiateThreadAbort(Thread* targetThread, Object * threadAbortException, bool doRudeAbort); + void CancelThreadAbort(Thread* targetThread); #else static PTR_Thread GetThreadFromTEB(TADDR pvTEB); #endif @@ -58,7 +68,6 @@ typedef DPTR(ThreadStore) PTR_ThreadStore; ThreadStore * GetThreadStore(); - #define FOREACH_THREAD(p_thread_name) \ { \ ThreadStore::Iterator __threads; \ diff --git a/external/corert/src/Native/Runtime/threadstore.inl b/external/corert/src/Native/Runtime/threadstore.inl index 1fbf862d55..d6003217b0 100644 --- a/external/corert/src/Native/Runtime/threadstore.inl +++ b/external/corert/src/Native/Runtime/threadstore.inl @@ -36,5 +36,5 @@ EXTERN_C volatile UInt32 RhpTrapThreads; // static inline bool ThreadStore::IsTrapThreadsRequested() { - return (RhpTrapThreads != 0); + return (RhpTrapThreads & (UInt32)TrapThreadsFlags::TrapThreads) != 0; } diff --git a/external/corert/src/Native/Runtime/unix/AsmOffsets.cpp b/external/corert/src/Native/Runtime/unix/AsmOffsets.cpp index d7409ef81e..a0c429c2f1 100644 --- a/external/corert/src/Native/Runtime/unix/AsmOffsets.cpp +++ b/external/corert/src/Native/Runtime/unix/AsmOffsets.cpp @@ -1,18 +1,10 @@ // 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. -#ifdef _ARM_ -#define PLAT_ASM_OFFSET(offset, cls, member) OFFSETOF__##cls##__##member 0x##offset -#define PLAT_ASM_SIZEOF(size, cls ) SIZEOF__##cls 0x##size -#define PLAT_ASM_CONST(constant, expr) expr 0x##constant - -#else - -#define PLAT_ASM_OFFSET(offset, cls, member) .set OFFSETOF__##cls##__##member, 0x##offset -#define PLAT_ASM_SIZEOF(size, cls ) .set SIZEOF__##cls, 0x##size -#define PLAT_ASM_CONST(constant, expr) .set expr, 0x##constant - -#endif +#define HASH_DEFINE #define +#define PLAT_ASM_OFFSET(offset, cls, member) HASH_DEFINE OFFSETOF__##cls##__##member 0x##offset +#define PLAT_ASM_SIZEOF(size, cls ) HASH_DEFINE SIZEOF__##cls 0x##size +#define PLAT_ASM_CONST(constant, expr) HASH_DEFINE expr 0x##constant #include diff --git a/external/corert/src/Native/Runtime/unix/PalRedhawkInline.h b/external/corert/src/Native/Runtime/unix/PalRedhawkInline.h index 2700a8734d..e46d9ce874 100644 --- a/external/corert/src/Native/Runtime/unix/PalRedhawkInline.h +++ b/external/corert/src/Native/Runtime/unix/PalRedhawkInline.h @@ -90,7 +90,7 @@ FORCEINLINE void PalMemoryBarrier() __sync_synchronize(); } -#define PalDebugBreak() __builtin_trap() +#define PalDebugBreak() abort() FORCEINLINE Int32 PalGetLastError() { diff --git a/external/corert/src/Native/Runtime/unix/PalRedhawkUnix.cpp b/external/corert/src/Native/Runtime/unix/PalRedhawkUnix.cpp index 998ea577a1..6d151657e1 100644 --- a/external/corert/src/Native/Runtime/unix/PalRedhawkUnix.cpp +++ b/external/corert/src/Native/Runtime/unix/PalRedhawkUnix.cpp @@ -120,6 +120,12 @@ typedef union _LARGE_INTEGER { int64_t QuadPart; } LARGE_INTEGER, *PLARGE_INTEGER; +struct FILETIME +{ + uint32_t dwLowDateTime; + uint32_t dwHighDateTime; +}; + typedef void * LPSECURITY_ATTRIBUTES; typedef void* PCONTEXT; typedef void* PEXCEPTION_RECORD; @@ -538,6 +544,7 @@ REDHAWK_PALEXPORT unsigned int REDHAWK_PALAPI PalGetCurrentProcessorNumber() #endif //HAVE_SCHED_GETCPU } +#if !defined(USE_PORTABLE_HELPERS) && !defined(FEATURE_RX_THUNKS) REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalAllocateThunksFromTemplate(HANDLE hTemplateModule, uint32_t templateRva, size_t templateSize, void** newThunksOut) { PORTABILITY_ASSERT("UNIXTODO: Implement this function"); @@ -547,9 +554,10 @@ REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalFreeThunksFromTemplate(void *pBa { PORTABILITY_ASSERT("UNIXTODO: Implement this function"); } +#endif // !USE_PORTABLE_HELPERS && !FEATURE_RX_THUNKS REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalMarkThunksAsValidCallTargets( - void *virtualAddress, + void *virtualAddress, int thunkSize, int thunksPerBlock, int thunkBlockSize, @@ -625,6 +633,10 @@ typedef UInt32(__stdcall *BackgroundCallback)(_In_opt_ void* pCallbackContext); REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalStartBackgroundWork(_In_ BackgroundCallback callback, _In_opt_ void* pCallbackContext, UInt32_BOOL highPriority) { +#ifdef _WASM_ + // No threads, so we can't start one + ASSERT(false); +#endif // _WASM_ pthread_attr_t attrs; int st = pthread_attr_init(&attrs); @@ -665,7 +677,12 @@ REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalStartBackgroundGCThread(_In_ Background REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalStartFinalizerThread(_In_ BackgroundCallback callback, _In_opt_ void* pCallbackContext) { +#ifdef _WASM_ + // WASMTODO: No threads so we can't start the finalizer thread + return true; +#else // _WASM_ return PalStartBackgroundWork(callback, pCallbackContext, UInt32_TRUE); +#endif // _WASM_ } // Returns a 64-bit tick count with a millisecond resolution. It tries its best @@ -723,12 +740,17 @@ REDHAWK_PALEXPORT UInt32 REDHAWK_PALAPI PalGetTickCount() REDHAWK_PALEXPORT HANDLE REDHAWK_PALAPI PalGetModuleHandleFromPointer(_In_ void* pointer) { HANDLE moduleHandle = NULL; + + // Emscripten's implementation of dladdr corrupts memory, + // but always returns 0 for the module handle, so just skip the call +#if !defined(_WASM_) Dl_info info; int st = dladdr(pointer, &info); if (st != 0) { moduleHandle = info.dli_fbase; } +#endif //!defined(_WASM_) return moduleHandle; } @@ -794,6 +816,11 @@ bool QueryCacheSize() } } } + +#elif defined(_WASM_) + // Processor cache size not available on WebAssembly, but we can't start up without it, so pick the same default as the GC does + success = true; + g_cbLargestOnDieCache = 256 * 1024; #else #error Do not know how to get cache size on this platform #endif // __linux__ @@ -1015,12 +1042,6 @@ extern "C" void LeaveCriticalSection(CRITICAL_SECTION * lpCriticalSection) pthread_mutex_unlock(&lpCriticalSection->mutex); } -extern "C" unsigned __int64 __readgsqword(unsigned long Offset) -{ - // UNIXTODO: The TLS stuff needs to be better abstracted. - return 0; -} - extern "C" UInt32_BOOL IsDebuggerPresent() { // UNIXTODO: Implement this function @@ -1079,24 +1100,6 @@ extern "C" UInt16 RtlCaptureStackBackTrace(UInt32 arg1, UInt32 arg2, void* arg3, return 0; } -extern "C" HANDLE GetProcessHeap() -{ - // UNIXTODO: Consider using some special value? - return (HANDLE)1; -} - -extern "C" void* HeapAlloc(HANDLE heap, UInt32 flags, UIntNative bytes) -{ - return malloc(bytes); -} - -extern "C" UInt32_BOOL HeapFree(HANDLE heap, UInt32 flags, void * mem) -{ - free(mem); - - return UInt32_TRUE; -} - typedef UInt32 (__stdcall *HijackCallback)(HANDLE hThread, _In_ PAL_LIMITED_CONTEXT* pThreadContext, _In_opt_ void* pCallbackContext); REDHAWK_PALEXPORT UInt32 REDHAWK_PALAPI PalHijack(HANDLE hThread, _In_ HijackCallback callback, _In_opt_ void* pCallbackContext) @@ -1124,6 +1127,11 @@ REDHAWK_PALEXPORT uint32_t REDHAWK_PALAPI PalCompatibleWaitAny(UInt32_BOOL alert return WaitForSingleObjectEx(pHandles[0], timeout, alertable); } +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#if !__has_builtin(_mm_pause) extern "C" void _mm_pause() // Defined for implementing PalYieldProcessor in PalRedhawk.h { @@ -1131,6 +1139,7 @@ extern "C" void _mm_pause() __asm__ volatile ("pause"); #endif } +#endif extern "C" Int32 _stricmp(const char *string1, const char *string2) { @@ -1217,8 +1226,6 @@ REDHAWK_PALEXPORT bool PalGetMaximumStackBounds(_Out_ void** ppStackLowOut, _Out return true; } -extern "C" int main(int argc, char** argv); - // retrieves the full path to the specified module, if moduleBase is NULL retreieves the full path to the // executable module of the current process. // @@ -1226,13 +1233,12 @@ extern "C" int main(int argc, char** argv); // REDHAWK_PALEXPORT Int32 PalGetModuleFileName(_Out_ const TCHAR** pModuleNameOut, HANDLE moduleBase) { - if (moduleBase == NULL) - { - // Get an address of the "main" function, which causes the dladdr to return - // path of the main executable - moduleBase = (HANDLE)&main; - } - +#if defined(_WASM_) + // Emscripten's implementation of dladdr corrupts memory and doesn't have the real name, so make up a name instead + const TCHAR* wasmModuleName = "WebAssemblyModule"; + *pModuleNameOut = wasmModuleName; + return strlen(wasmModuleName); +#else // _WASM_ Dl_info dl; if (dladdr(moduleBase, &dl) == 0) { @@ -1242,6 +1248,7 @@ REDHAWK_PALEXPORT Int32 PalGetModuleFileName(_Out_ const TCHAR** pModuleNameOut, *pModuleNameOut = dl.dli_fname; return strlen(dl.dli_fname); +#endif // defined(_WASM_) } GCSystemInfo g_SystemInfo; @@ -1330,6 +1337,21 @@ extern "C" void FlushProcessWriteBuffers() FATAL_ASSERT(status == 0, "Failed to unlock the flushProcessWriteBuffersMutex lock"); } +static const int64_t SECS_BETWEEN_1601_AND_1970_EPOCHS = 11644473600LL; +static const int64_t SECS_TO_100NS = 10000000; /* 10^7 */ + +extern "C" void GetSystemTimeAsFileTime(FILETIME *lpSystemTimeAsFileTime) +{ + struct timeval time = { 0 }; + gettimeofday(&time, NULL); + + int64_t result = ((int64_t)time.tv_sec + SECS_BETWEEN_1601_AND_1970_EPOCHS) * SECS_TO_100NS + + (time.tv_usec * 10); + + lpSystemTimeAsFileTime->dwLowDateTime = (uint32_t)result; + lpSystemTimeAsFileTime->dwHighDateTime = (uint32_t)(result >> 32); +} + extern "C" UInt32_BOOL QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount) { // TODO: More efficient, platform-specific implementation @@ -1534,7 +1556,12 @@ bool GCToOSInterface::VirtualCommit(void* address, size_t size) // true if it has succeeded, false if it has failed bool GCToOSInterface::VirtualDecommit(void* address, size_t size) { - return mprotect(address, size, PROT_NONE) == 0; + // TODO: This can fail, however the GC does not handle the failure gracefully + // Explicitly calling mmap instead of mprotect here makes it + // that much more clear to the operating system that we no + // longer need these pages. Also, GC depends on re-commited pages to + // be zeroed-out. + return mmap(address, size, PROT_NONE, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0) != NULL; } // Reset virtual memory range. Indicates that data in the memory range specified by address and size is no diff --git a/external/corert/src/Native/Runtime/unix/UnixContext.cpp b/external/corert/src/Native/Runtime/unix/UnixContext.cpp index 495a33fcf8..cf88a59943 100644 --- a/external/corert/src/Native/Runtime/unix/UnixContext.cpp +++ b/external/corert/src/Native/Runtime/unix/UnixContext.cpp @@ -18,6 +18,35 @@ #endif // HAVE_UCONTEXT_T #include "UnixContext.h" +#include "UnwindHelpers.h" + +// WebAssembly has a slightly different version of LibUnwind that doesn't define unw_get_save_loc +#if defined(_WASM_) +enum unw_save_loc_type_t +{ + UNW_SLT_NONE, /* register is not saved ("not an l-value") */ + UNW_SLT_MEMORY, /* register has been saved in memory */ + UNW_SLT_REG /* register has been saved in (another) register */ +}; +typedef enum unw_save_loc_type_t unw_save_loc_type_t; + +struct unw_save_loc_t +{ + unw_save_loc_type_t type; + union + { + unw_word_t addr; /* valid if type==UNW_SLT_MEMORY */ + unw_regnum_t regnum; /* valid if type==UNW_SLT_REG */ + } + u; +}; +typedef struct unw_save_loc_t unw_save_loc_t; + +int unw_get_save_loc(unw_cursor_t*, int, unw_save_loc_t*) +{ + return -1; +} +#endif // _WASM #ifdef __APPLE__ @@ -197,44 +226,11 @@ #endif // __APPLE__ -// Update unw_context_t from REGDISPLAY -static void RegDisplayToUnwindContext(REGDISPLAY* regDisplay, unw_context_t *unwContext) -{ -#if defined(_ARM_) - // Assuming that unw_set_reg() on cursor will point the cursor to the - // supposed stack frame is dangerous for libunwind-arm in Linux. - // It is because libunwind's unw_cursor_t has other data structure - // initialized by unw_init_local(), which are not updated by - // unw_set_reg(). - -#define ASSIGN_REG(regIndex, regName) \ - unwContext->data[regIndex] = (regDisplay->regName); - -#define ASSIGN_REG_PTR(regIndex, regName) \ - if (regDisplay->p##regName != NULL) \ - unwContext->data[regIndex] = *(regDisplay->p##regName); - - ASSIGN_REG_PTR(4, R4); - ASSIGN_REG_PTR(5, R5); - ASSIGN_REG_PTR(6, R6); - ASSIGN_REG_PTR(7, R7); - ASSIGN_REG_PTR(8, R8); - ASSIGN_REG_PTR(9, R9); - ASSIGN_REG_PTR(10, R10); - ASSIGN_REG_PTR(11, R11); - ASSIGN_REG(13, SP); - ASSIGN_REG_PTR(14, LR); - ASSIGN_REG(15, IP); - -#undef ASSIGN_REG -#undef ASSIGN_REG_PTR -#endif // _ARM_ -} - -// Update unw_cursor_t from REGDISPLAY +// Update unw_cursor_t from REGDISPLAY. +// NOTE: We don't set the IP here since the current use cases for this function +// don't require it. static void RegDisplayToUnwindCursor(REGDISPLAY* regDisplay, unw_cursor_t *cursor) { -#if defined(_AMD64_) #define ASSIGN_REG(regName1, regName2) \ unw_set_reg(cursor, regName1, regDisplay->regName2); @@ -242,7 +238,7 @@ static void RegDisplayToUnwindCursor(REGDISPLAY* regDisplay, unw_cursor_t *curso if (regDisplay->p##regName2 != NULL) \ unw_set_reg(cursor, regName1, *(regDisplay->p##regName2)); - ASSIGN_REG(UNW_REG_IP, IP) +#if defined(_AMD64_) ASSIGN_REG(UNW_REG_SP, SP) ASSIGN_REG_PTR(UNW_X86_64_RBP, Rbp) ASSIGN_REG_PTR(UNW_X86_64_RBX, Rbx) @@ -250,10 +246,63 @@ static void RegDisplayToUnwindCursor(REGDISPLAY* regDisplay, unw_cursor_t *curso ASSIGN_REG_PTR(UNW_X86_64_R13, R13) ASSIGN_REG_PTR(UNW_X86_64_R14, R14) ASSIGN_REG_PTR(UNW_X86_64_R15, R15) +#elif _ARM_ + ASSIGN_REG(UNW_ARM_SP, SP) + ASSIGN_REG_PTR(UNW_ARM_R4, R4) + ASSIGN_REG_PTR(UNW_ARM_R5, R5) + ASSIGN_REG_PTR(UNW_ARM_R6, R6) + ASSIGN_REG_PTR(UNW_ARM_R7, R7) + ASSIGN_REG_PTR(UNW_ARM_R8, R8) + ASSIGN_REG_PTR(UNW_ARM_R9, R9) + ASSIGN_REG_PTR(UNW_ARM_R10, R10) + ASSIGN_REG_PTR(UNW_ARM_R11, R11) + ASSIGN_REG_PTR(UNW_ARM_R14, LR) +#endif #undef ASSIGN_REG #undef ASSIGN_REG_PTR -#endif // _AMD64_ +} + +// Returns the unw_proc_info_t for a given IP. +bool GetUnwindProcInfo(PCODE ip, unw_proc_info_t *procInfo) +{ + int st; + + unw_context_t unwContext; + unw_cursor_t cursor; + + st = unw_getcontext(&unwContext); + if (st < 0) + { + return false; + } + +#ifdef _AMD64_ + // We manually index into the unw_context_t's internals for now because there's + // no better way to modify it. This will go away in the future when we locate the + // LSDA and other information without initializing an unwind cursor. + unwContext.data[16] = ip; +#elif _ARM_ + ((uint32_t*)(unwContext.data))[15] = ip; +#elif _WASM_ + ASSERT(false); +#else + #error "GetUnwindProcInfo is not supported on this arch yet." +#endif + + st = unw_init_local(&cursor, &unwContext); + if (st < 0) + { + return false; + } + + st = unw_get_proc_info(&cursor, procInfo); + if (st < 0) + { + return false; + } + + return true; } // Initialize unw_cursor_t and unw_context_t from REGDISPLAY @@ -267,7 +316,18 @@ bool InitializeUnwindContextAndCursor(REGDISPLAY* regDisplay, unw_cursor_t* curs return false; } - RegDisplayToUnwindContext(regDisplay, unwContext); + // Set the IP here instead of after unwinder initialization. unw_init_local + // will do some initialization of internal structures based on the IP value. + // We manually index into the unw_context_t's internals for now because there's + // no better way to modify it. This whole function will go away in the future + // when we are able to read unwind info without initializing an unwind cursor. +#ifdef _AMD64_ + unwContext->data[16] = regDisplay->IP; +#elif _ARM_ + ((uint32_t*)(unwContext->data))[15] = regDisplay->IP; +#else + #error "InitializeUnwindContextAndCursor is not supported on this arch yet." +#endif st = unw_init_local(cursor, unwContext); if (st < 0) @@ -275,7 +335,7 @@ bool InitializeUnwindContextAndCursor(REGDISPLAY* regDisplay, unw_cursor_t* curs return false; } - // Set the unwind context to the specified windows context + // Set the unwind context to the specified Windows context. RegDisplayToUnwindCursor(regDisplay, cursor); return true; @@ -325,6 +385,13 @@ static void GetContextPointer(unw_cursor_t *cursor, unw_context_t *unwContext, i GET_CONTEXT_POINTER(UNW_AARCH64_X26, 26) \ GET_CONTEXT_POINTER(UNW_AARCH64_X27, 27) \ GET_CONTEXT_POINTER(UNW_AARCH64_X28, 28) +#elif defined(_X86_) +#define GET_CONTEXT_POINTERS \ + GET_CONTEXT_POINTER(UNW_X86_EBP, Rbp) \ + GET_CONTEXT_POINTER(UNW_X86_EBX, Rbx) +#elif defined (_WASM_) +// No registers +#define GET_CONTEXT_POINTERS #else #error unsupported architecture #endif @@ -361,10 +428,23 @@ void UnwindCursorToRegDisplay(unw_cursor_t *cursor, unw_context_t *unwContext, R ASSIGN_REG(R14, R14) \ ASSIGN_REG(R15, R15) -#define ASSIGN_TWO_ARGUMENT_REGS(arg0Reg, arg1Reg) \ - MCREG_Rdi(nativeContext->uc_mcontext) = arg0Reg; \ +#define ASSIGN_TWO_ARGUMENT_REGS(arg0Reg, arg1Reg) \ + MCREG_Rdi(nativeContext->uc_mcontext) = arg0Reg; \ MCREG_Rsi(nativeContext->uc_mcontext) = arg1Reg; +#elif defined(_X86_) +#define ASSIGN_CONTROL_REGS \ + ASSIGN_REG(Eip, IP) \ + ASSIGN_REG(Esp, Rsp) + +#define ASSIGN_INTEGER_REGS \ + ASSIGN_REG(Ebx, Rbx) \ + ASSIGN_REG(Ebp, Rbp) + +#define ASSIGN_TWO_ARGUMENT_REGS(arg0Reg, arg1Reg) \ + MCREG_Ecx(nativeContext->uc_mcontext) = arg0Reg; \ + MCREG_Edx(nativeContext->uc_mcontext) = arg1Reg; + #elif defined(_ARM_) #define ASSIGN_CONTROL_REGS \ @@ -408,7 +488,11 @@ void UnwindCursorToRegDisplay(unw_cursor_t *cursor, unw_context_t *unwContext, R #define ASSIGN_TWO_ARGUMENT_REGS // MCREG_X0(nativeContext->uc_mcontext) = arg0Reg; \ // MCREG_X1(nativeContext->uc_mcontext) = arg1Reg; - +#elif defined(_WASM_) + // TODO: determine how unwinding will work on WebAssembly +#define ASSIGN_CONTROL_REGS +#define ASSIGN_INTEGER_REGS +#define ASSIGN_TWO_ARGUMENT_REGS #else #error unsupported architecture #endif @@ -498,21 +582,9 @@ uint64_t GetPC(void* context) // Find LSDA and start address for a function at address controlPC bool FindProcInfo(UIntNative controlPC, UIntNative* startAddress, UIntNative* lsda) { - unw_context_t unwContext; - unw_cursor_t cursor; - REGDISPLAY regDisplay; - memset(®Display, 0, sizeof(REGDISPLAY)); - - regDisplay.SetIP((PCODE)controlPC); - - if (!InitializeUnwindContextAndCursor(®Display, &cursor, &unwContext)) - { - return false; - } - unw_proc_info_t procInfo; - int st = unw_get_proc_info(&cursor, &procInfo); - if (st < 0) + + if (!GetUnwindProcInfo((PCODE)controlPC, &procInfo)) { return false; } @@ -528,38 +600,5 @@ bool FindProcInfo(UIntNative controlPC, UIntNative* startAddress, UIntNative* ls // Virtually unwind stack to the caller of the context specified by the REGDISPLAY bool VirtualUnwind(REGDISPLAY* pRegisterSet) { - unw_context_t unwContext; - unw_cursor_t cursor; - - if (!InitializeUnwindContextAndCursor(pRegisterSet, &cursor, &unwContext)) - { - return false; - } - - // FreeBSD, NetBSD and OSX appear to do two different things when unwinding - // 1: If it reaches where it cannot unwind anymore, say a - // managed frame. It wil return 0, but also update the $pc - // 2: If it unwinds all the way to _start it will return - // 0 from the step, but $pc will stay the same. - // The behaviour of libunwind from nongnu.org is to null the PC - // So we bank the original PC here, so we can compare it after - // the step - uintptr_t curPc = pRegisterSet->GetIP(); - - int st = unw_step(&cursor); - if (st < 0) - { - return false; - } - - // Update the REGDISPLAY to reflect the unwind - UnwindCursorToRegDisplay(&cursor, &unwContext, pRegisterSet); - - if (st == 0 && pRegisterSet->GetIP() == curPc) - { - // TODO: is this correct for CoreRT? Should we return false instead? - pRegisterSet->SetIP(0); - } - - return true; + return UnwindHelpers::StepFrame(pRegisterSet); } diff --git a/external/corert/src/Native/Runtime/unix/UnixNativeCodeManager.cpp b/external/corert/src/Native/Runtime/unix/UnixNativeCodeManager.cpp index af2a0909bd..480893f972 100644 --- a/external/corert/src/Native/Runtime/unix/UnixNativeCodeManager.cpp +++ b/external/corert/src/Native/Runtime/unix/UnixNativeCodeManager.cpp @@ -11,6 +11,7 @@ #include "ICodeManager.h" #include "UnixNativeCodeManager.h" #include "varint.h" +#include "holder.h" #include "CommonMacros.inl" @@ -24,9 +25,9 @@ #define UBF_FUNC_KIND_HANDLER 0x01 #define UBF_FUNC_KIND_FILTER 0x02 -#define UBF_FUNC_HAS_EHINFO 0x04 - -#define UBF_FUNC_REVERSE_PINVOKE 0x08 +#define UBF_FUNC_HAS_EHINFO 0x04 +#define UBF_FUNC_REVERSE_PINVOKE 0x08 +#define UBF_FUNC_HAS_ASSOCIATED_DATA 0x10 struct UnixNativeMethodInfo { @@ -129,6 +130,9 @@ void UnixNativeCodeManager::EnumGcRefs(MethodInfo * pMethodInfo, uint8_t unwindBlockFlags = *p++; + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) p += sizeof(int32_t); @@ -175,6 +179,9 @@ bool UnixNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo, uint8_t unwindBlockFlags = *p++; + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + if ((unwindBlockFlags & UBF_FUNC_REVERSE_PINVOKE) != 0) { // Reverse PInvoke transition should on the main function body only @@ -264,6 +271,9 @@ bool UnixNativeCodeManager::EHEnumInit(MethodInfo * pMethodInfo, PTR_VOID * pMet uint8_t unwindBlockFlags = *p++; + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + // return if there is no EH info associated with this method if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) == 0) { @@ -337,6 +347,11 @@ bool UnixNativeCodeManager::EHEnumNext(EHEnumState * pEHEnumState, EHClause * pE return true; } +PTR_VOID UnixNativeCodeManager::GetOsModuleHandle() +{ + return (PTR_VOID)m_moduleBase; +} + PTR_VOID UnixNativeCodeManager::GetMethodStartAddress(MethodInfo * pMethodInfo) { UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo; @@ -355,25 +370,47 @@ void * UnixNativeCodeManager::GetClasslibFunction(ClasslibFunctionId functionId) return m_pClasslibFunctions[id]; } +PTR_VOID UnixNativeCodeManager::GetAssociatedData(PTR_VOID ControlPC) +{ + UnixNativeMethodInfo methodInfo; + if (!FindMethodInfo(ControlPC, (MethodInfo*)&methodInfo)) + return NULL; + + PTR_UInt8 p = methodInfo.pMainLSDA; + + uint8_t unwindBlockFlags = *p++; + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) == 0) + return NULL; + + return dac_cast(p + *dac_cast(p)); +} + extern "C" bool __stdcall RegisterCodeManager(ICodeManager * pCodeManager, PTR_VOID pvStartRange, UInt32 cbRange); +extern "C" void __stdcall UnregisterCodeManager(ICodeManager * pCodeManager); +extern "C" bool __stdcall RegisterUnboxingStubs(PTR_VOID pvStartRange, UInt32 cbRange); extern "C" -bool RhpRegisterUnixModule(void * pModule, - void * pvStartRange, UInt32 cbRange, - void ** pClasslibFunctions, UInt32 nClasslibFunctions) +bool RhRegisterOSModule(void * pModule, + void * pvManagedCodeStartRange, UInt32 cbManagedCodeRange, + void * pvUnboxingStubsStartRange, UInt32 cbUnboxingStubsRange, + void ** pClasslibFunctions, UInt32 nClasslibFunctions) { - UnixNativeCodeManager * pUnixNativeCodeManager = new (nothrow) UnixNativeCodeManager((TADDR)pModule, + NewHolder pUnixNativeCodeManager = new (nothrow) UnixNativeCodeManager((TADDR)pModule, pClasslibFunctions, nClasslibFunctions); + if (pUnixNativeCodeManager == nullptr) + return false; + + if (!RegisterCodeManager(pUnixNativeCodeManager, pvManagedCodeStartRange, cbManagedCodeRange)) + return false; + + if (!RegisterUnboxingStubs(pvUnboxingStubsStartRange, cbUnboxingStubsRange)) { + UnregisterCodeManager(pUnixNativeCodeManager); return false; } - if (!RegisterCodeManager(pUnixNativeCodeManager, pvStartRange, cbRange)) - { - delete pUnixNativeCodeManager; - return false; - } + pUnixNativeCodeManager.SuppressRelease(); return true; } diff --git a/external/corert/src/Native/Runtime/unix/UnixNativeCodeManager.h b/external/corert/src/Native/Runtime/unix/UnixNativeCodeManager.h index e424a559ca..b8e38fbb0e 100644 --- a/external/corert/src/Native/Runtime/unix/UnixNativeCodeManager.h +++ b/external/corert/src/Native/Runtime/unix/UnixNativeCodeManager.h @@ -58,4 +58,8 @@ public: PTR_VOID GetMethodStartAddress(MethodInfo * pMethodInfo); void * GetClasslibFunction(ClasslibFunctionId functionId); + + PTR_VOID GetAssociatedData(PTR_VOID ControlPC); + + PTR_VOID GetOsModuleHandle(); }; diff --git a/external/corert/src/Native/Runtime/unix/UnwindHelpers.cpp b/external/corert/src/Native/Runtime/unix/UnwindHelpers.cpp new file mode 100644 index 0000000000..ed5100bd86 --- /dev/null +++ b/external/corert/src/Native/Runtime/unix/UnwindHelpers.cpp @@ -0,0 +1,355 @@ +// 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. + +#include "common.h" +#include "daccess.h" + +#define UNW_STEP_SUCCESS 1 +#define UNW_STEP_END 0 + +#ifdef __APPLE__ +#include +#endif + +#include +#include "UnwindHelpers.h" + +// libunwind headers +#include +#include +#include +#include +#include + +using libunwind::Registers_x86_64; +using libunwind::LocalAddressSpace; +using libunwind::EHHeaderParser; +using libunwind::DwarfInstructions; +using libunwind::UnwindInfoSections; + +LocalAddressSpace _addressSpace; + +#ifdef __APPLE__ + +struct dyld_unwind_sections +{ + const struct mach_header* mh; + const void* dwarf_section; + uintptr_t dwarf_section_length; + const void* compact_unwind_section; + uintptr_t compact_unwind_section_length; +}; + +#else // __APPLE__ + +// Passed to the callback function called by dl_iterate_phdr +struct dl_iterate_cb_data +{ + UnwindInfoSections *sects; + uintptr_t targetAddr; +}; + +// Callback called by dl_iterate_phdr. Locates unwind info sections for the target +// address. +static int LocateSectionsCallback(struct dl_phdr_info *info, size_t size, void *data) +{ + // info is a pointer to a structure containing information about the shared object + dl_iterate_cb_data* cbdata = static_cast(data); + uintptr_t addrOfInterest = (uintptr_t)cbdata->targetAddr; + + size_t object_length; + bool found_obj = false; + bool found_hdr = false; + + // If the base address of the SO is past the address we care about, move on. + if (info->dlpi_addr > addrOfInterest) + { + return 0; + } + + // Iterate through the program headers for this SO + for (ElfW(Half) i = 0; i < info->dlpi_phnum; i++) + { + const ElfW(Phdr) *phdr = &info->dlpi_phdr[i]; + + if (phdr->p_type == PT_LOAD) + { + // This is a loadable entry. Loader loads all segments of this type. + + uintptr_t begin = info->dlpi_addr + phdr->p_vaddr; + uintptr_t end = begin + phdr->p_memsz; + + if (addrOfInterest >= begin && addrOfInterest < end) + { + cbdata->sects->dso_base = begin; + object_length = phdr->p_memsz; + found_obj = true; + } + } + else if (phdr->p_type == PT_GNU_EH_FRAME) + { + // This element specifies the location and size of the exception handling + // information as defined by the .eh_frame_hdr section. + + EHHeaderParser::EHHeaderInfo hdrInfo; + + uintptr_t eh_frame_hdr_start = info->dlpi_addr + phdr->p_vaddr; + cbdata->sects->dwarf_index_section = eh_frame_hdr_start; + cbdata->sects->dwarf_index_section_length = phdr->p_memsz; + + EHHeaderParser ehp; + ehp.decodeEHHdr(_addressSpace, eh_frame_hdr_start, phdr->p_memsz, hdrInfo); + + cbdata->sects->dwarf_section = hdrInfo.eh_frame_ptr; + found_hdr = true; + } + } + + return 0; +} + +#endif // __APPLE__ + +#ifdef _TARGET_AMD64_ + +// Shim that implements methods required by libunwind over REGDISPLAY +struct Registers_REGDISPLAY : REGDISPLAY +{ + inline uint64_t getRegister(int regNum) const + { + switch (regNum) + { + case UNW_REG_IP: + return IP; + case UNW_REG_SP: + return SP; + case UNW_X86_64_RAX: + return *pRax; + case UNW_X86_64_RDX: + return *pRdx; + case UNW_X86_64_RCX: + return *pRcx; + case UNW_X86_64_RBX: + return *pRbx; + case UNW_X86_64_RSI: + return *pRsi; + case UNW_X86_64_RDI: + return *pRdi; + case UNW_X86_64_RBP: + return *pRbp; + case UNW_X86_64_RSP: + return SP; + case UNW_X86_64_R8: + return *pR8; + case UNW_X86_64_R9: + return *pR9; + case UNW_X86_64_R10: + return *pR10; + case UNW_X86_64_R11: + return *pR11; + case UNW_X86_64_R12: + return *pR12; + case UNW_X86_64_R13: + return *pR13; + case UNW_X86_64_R14: + return *pR14; + case UNW_X86_64_R15: + return *pR15; + } + + // Unsupported register requested + abort(); + } + + inline void setRegister(int regNum, uint64_t value, uint64_t location) + { + switch (regNum) + { + case UNW_REG_IP: + IP = value; + pIP = (PTR_PCODE)location; + return; + case UNW_REG_SP: + SP = value; + return; + case UNW_X86_64_RAX: + pRax = (PTR_UIntNative)location; + return; + case UNW_X86_64_RDX: + pRdx = (PTR_UIntNative)location; + return; + case UNW_X86_64_RCX: + pRcx = (PTR_UIntNative)location; + return; + case UNW_X86_64_RBX: + pRbx = (PTR_UIntNative)location; + return; + case UNW_X86_64_RSI: + pRsi = (PTR_UIntNative)location; + return; + case UNW_X86_64_RDI: + pRdi = (PTR_UIntNative)location; + return; + case UNW_X86_64_RBP: + pRbp = (PTR_UIntNative)location; + return; + case UNW_X86_64_RSP: + SP = value; + return; + case UNW_X86_64_R8: + pR8 = (PTR_UIntNative)location; + return; + case UNW_X86_64_R9: + pR9 = (PTR_UIntNative)location; + return; + case UNW_X86_64_R10: + pR10 = (PTR_UIntNative)location; + return; + case UNW_X86_64_R11: + pR11 = (PTR_UIntNative)location; + return; + case UNW_X86_64_R12: + pR12 = (PTR_UIntNative)location; + return; + case UNW_X86_64_R13: + pR13 = (PTR_UIntNative)location; + return; + case UNW_X86_64_R14: + pR14 = (PTR_UIntNative)location; + return; + case UNW_X86_64_R15: + pR15 = (PTR_UIntNative)location; + return; + } + + // Unsupported x86_64 register + abort(); + } + + // N/A for x86_64 + inline bool validFloatRegister(int) { return false; } + inline bool validVectorRegister(int) { return false; } + + inline static int lastDwarfRegNum() { return 16; } + + inline bool validRegister(int regNum) const + { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum > 15) + return false; + return true; + } + + // N/A for x86_64 + inline double getFloatRegister(int) const { abort(); } + inline void setFloatRegister(int, double) { abort(); } + inline double getVectorRegister(int) const { abort(); } + inline void setVectorRegister(int, ...) { abort(); } + + uint64_t getSP() const { return SP; } + void setSP(uint64_t value, uint64_t location) { SP = value; } + + uint64_t getIP() const { return IP; } + + void setIP(uint64_t value, uint64_t location) + { + IP = value; + pIP = (PTR_PCODE)location; + } + + uint64_t getRBP() const { return *pRbp; } + void setRBP(uint64_t value, uint64_t location) { pRbp = (PTR_UIntNative)location; } + uint64_t getRBX() const { return *pRbx; } + void setRBX(uint64_t value, uint64_t location) { pRbx = (PTR_UIntNative)location; } + uint64_t getR12() const { return *pR12; } + void setR12(uint64_t value, uint64_t location) { pR12 = (PTR_UIntNative)location; } + uint64_t getR13() const { return *pR13; } + void setR13(uint64_t value, uint64_t location) { pR13 = (PTR_UIntNative)location; } + uint64_t getR14() const { return *pR14; } + void setR14(uint64_t value, uint64_t location) { pR14 = (PTR_UIntNative)location; } + uint64_t getR15() const { return *pR15; } + void setR15(uint64_t value, uint64_t location) { pR15 = (PTR_UIntNative)location; } +}; + +#endif // _TARGET_AMD64_ + +bool DoTheStep(uintptr_t pc, UnwindInfoSections uwInfoSections, REGDISPLAY *regs) +{ +#if defined(_TARGET_AMD64_) + libunwind::UnwindCursor uc(_addressSpace); +#elif defined(_TARGET_ARM_) + libunwind::UnwindCursor uc(_addressSpace); +#else + #error "Unwinding is not implemented for this architecture yet." +#endif + + bool retVal = uc.getInfoFromDwarfSection(pc, uwInfoSections, 0 /* fdeSectionOffsetHint */); + if (!retVal) + { + return false; + } + + unw_proc_info_t procInfo; + uc.getInfo(&procInfo); + + DwarfInstructions dwarfInst; + + int stepRet = dwarfInst.stepWithDwarf(_addressSpace, pc, procInfo.unwind_info, *(Registers_REGDISPLAY*)regs); + if (stepRet != UNW_STEP_SUCCESS) + { + return false; + } + + regs->pIP = PTR_PCODE(regs->SP - sizeof(TADDR)); + + return true; +} + +UnwindInfoSections LocateUnwindSections(uintptr_t pc) +{ + UnwindInfoSections uwInfoSections; + +#ifdef __APPLE__ + // On macOS, we can use a dyld function from libSystem in order + // to find the unwind sections. + + libunwind::dyld_unwind_sections dyldInfo; + + if (libunwind::_dyld_find_unwind_sections((void *)pc, &dyldInfo)) + { + uwInfoSections.dso_base = (uintptr_t)dyldInfo.mh; + + uwInfoSections.dwarf_section = (uintptr_t)dyldInfo.dwarf_section; + uwInfoSections.dwarf_section_length = dyldInfo.dwarf_section_length; + + uwInfoSections.compact_unwind_section = (uintptr_t)dyldInfo.compact_unwind_section; + uwInfoSections.compact_unwind_section_length = dyldInfo.compact_unwind_section_length; + } +#else // __APPLE__ + + dl_iterate_cb_data cb_data = {&uwInfoSections, pc }; + dl_iterate_phdr(LocateSectionsCallback, &cb_data); + +#endif + + return uwInfoSections; +} + +bool UnwindHelpers::StepFrame(REGDISPLAY *regs) +{ + uintptr_t pc = regs->GetIP(); + + UnwindInfoSections uwInfoSections = LocateUnwindSections(pc); + if (uwInfoSections.dwarf_section == NULL) + { + return false; + } + + return DoTheStep(pc, uwInfoSections, regs); +} diff --git a/external/corert/src/Native/Runtime/unix/UnwindHelpers.h b/external/corert/src/Native/Runtime/unix/UnwindHelpers.h new file mode 100644 index 0000000000..cf9e91ea4b --- /dev/null +++ b/external/corert/src/Native/Runtime/unix/UnwindHelpers.h @@ -0,0 +1,14 @@ +// 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. + +#include "common.h" + +// This class is used to encapsulate the internals of our unwinding implementation +// and any custom versions of libunwind structures that we use for performance +// reasons. +class UnwindHelpers +{ +public: + static bool StepFrame(REGDISPLAY *regs); +}; diff --git a/external/corert/src/Native/Runtime/unix/unixasmmacrosamd64.inc b/external/corert/src/Native/Runtime/unix/unixasmmacrosamd64.inc index ed6f1db300..d94fe2d8cc 100644 --- a/external/corert/src/Native/Runtime/unix/unixasmmacrosamd64.inc +++ b/external/corert/src/Native/Runtime/unix/unixasmmacrosamd64.inc @@ -222,6 +222,19 @@ C_FUNC(\Name): .endm +.macro EXPORT_POINTER_TO_ADDRESS Name + +1: + + .data + .align 8 +C_FUNC(\Name): + .quad 1b + .global C_FUNC(\Name) + .text + +.endm + // // CONSTANTS -- INTEGER // @@ -250,6 +263,12 @@ C_FUNC(\Name): #define PTFF_SAVE_ALL_SCRATCH 00007F00h #define PTFF_RAX_IS_GCREF 00010000h // iff PTFF_SAVE_RAX: set -> eax is Object, clear -> eax is scalar #define PTFF_RAX_IS_BYREF 00020000h // iff PTFF_SAVE_RAX: set -> eax is ByRef, clear -> eax is Object or scalar +#define PTFF_THREAD_ABORT 00040000h // indicates that ThreadAbortException should be thrown when returning from the transition + +// These must match the TrapThreadsFlags enum +#define TrapThreadsFlags_None 0 +#define TrapThreadsFlags_AbortInProgress 1 +#define TrapThreadsFlags_TrapThreads 2 .macro INLINE_GET_TLS_VAR Var .att_syntax @@ -289,7 +308,9 @@ C_FUNC(\Name): DEFAULT_FRAME_SAVE_FLAGS = PTFF_SAVE_ALL_PRESERVED + PTFF_SAVE_RSP .macro PUSH_COOP_PINVOKE_FRAME trashReg - lea \trashReg, [rsp + 8h] + push_nonvol_reg rbp // push RBP frame + mov rbp, rsp + lea \trashReg, [rsp + 10h] push_register \trashReg // save caller's RSP push_nonvol_reg r15 // save preserved registers push_nonvol_reg r14 // .. @@ -298,19 +319,15 @@ DEFAULT_FRAME_SAVE_FLAGS = PTFF_SAVE_ALL_PRESERVED + PTFF_SAVE_RSP push_nonvol_reg rbx // .. push_imm DEFAULT_FRAME_SAVE_FLAGS // save the register bitmask push_register \trashReg // Thread * (unused by stackwalker) - push_nonvol_reg rbp // save caller's RBP - mov \trashReg, [rsp + 9*8] // Find the return address - push_register \trashReg // save m_RIP + mov \trashReg, [rsp + 8*8] // Find and save the callers RBP + push_register \trashReg + mov \trashReg, [rsp + 10*8] // Find and save the return address + push_register \trashReg lea \trashReg, [rsp] // trashReg == address of frame - - // stack alignment - alloc_stack 8 .endm .macro POP_COOP_PINVOKE_FRAME - // Adjust for stack alignment and discard RIP - free_stack 16 - + pop_register r10 // discard RIP pop_nonvol_reg rbp // restore RBP pop_register r10 // discard thread pop_register r10 // discard bitmask @@ -320,4 +337,5 @@ DEFAULT_FRAME_SAVE_FLAGS = PTFF_SAVE_ALL_PRESERVED + PTFF_SAVE_RSP pop_nonvol_reg r14 pop_nonvol_reg r15 pop_register r10 // discard caller RSP + pop_register r10 // discard RBP frame .endm diff --git a/external/corert/src/Native/Runtime/unix/unixasmmacrosarm.inc b/external/corert/src/Native/Runtime/unix/unixasmmacrosarm.inc index c9721ada6c..e7a78f3e23 100644 --- a/external/corert/src/Native/Runtime/unix/unixasmmacrosarm.inc +++ b/external/corert/src/Native/Runtime/unix/unixasmmacrosarm.inc @@ -2,6 +2,38 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +// +// CONSTANTS -- INTEGER +// + +// GC type flags +#define GC_ALLOC_FINALIZE 1 +#define GC_ALLOC_ALIGN8_BIAS 4 +#define GC_ALLOC_ALIGN8 8 + +#define TSF_Attached 0x01 +#define TSF_SuppressGcStress 0x08 +#define TSF_DoNotTriggerGc 0x10 + +#define PTFF_SAVE_ALL_PRESERVED 0x00000077 // NOTE: FP is not included in this set! +#define PTFF_SAVE_SP 0x00000100 +#define DEFAULT_FRAME_SAVE_FLAGS (PTFF_SAVE_ALL_PRESERVED + PTFF_SAVE_SP) + +// These must match the TrapThreadsFlags enum +#define TrapThreadsFlags_None 0 +#define TrapThreadsFlags_AbortInProgress 1 +#define TrapThreadsFlags_TrapThreads 2 + +// Rename fields of nested structs +#define OFFSETOF__Thread__m_alloc_context__alloc_ptr (OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_ptr) +#define OFFSETOF__Thread__m_alloc_context__alloc_limit (OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_limit) + +// GC minimal sized object. We use this to switch between 4 and 8 byte alignment in the GC heap (see AllocFast.asm). +#define SIZEOF__MinObject 12 + +// Maximum subsection number in .text section +#define MAX_NUMBER_SUBSECTION_TEXT 0x2000 + .macro NESTED_ENTRY Name, Section, Handler LEAF_ENTRY \Name, \Section .ifnc \Handler, NoHandler @@ -130,6 +162,16 @@ C_FUNC(\Name): vpop_nonvol_reg "\RegList" .endm +.macro PROLOG_STACK_ALLOC Size + sub sp, sp, #\Size + .pad #\Size +.endm + +.macro EPILOG_STACK_FREE Size + add sp, sp, #\Size + .pad #-\Size +.endm + //----------------------------------------------------------------------------- // Macro used to check (in debug builds only) whether the stack is 64-bit aligned (a requirement before calling // out into C++/OS code). Invoke this directly after your prolog (if the stack frame size is fixed) or directly @@ -148,3 +190,118 @@ C_FUNC(\Name): 0: #endif .endm + +// Loads a 32bit constant into destination register +.macro MOV32 DestReg, Constant + movw \DestReg, #((\Constant) & 0xFFFF) + movt \DestReg, #((\Constant) >> 16) +.endm + +.macro EXPORT_POINTER_TO_ADDRESS Name + +1: + + .data + .align 4 +C_FUNC(\Name): + .word 1b + .global C_FUNC(\Name) + .text + +.endm + +// +// Macro used from unmanaged helpers called from managed code where the helper does not transition immediately +// into pre-emptive mode but may cause a GC and thus requires the stack is crawlable. This is typically the +// case for helpers that meddle in GC state (e.g. allocation helpers) where the code must remain in +// cooperative mode since it handles object references and internal GC state directly but a garbage collection +// may be inevitable. In these cases we need to be able to transition to pre-meptive mode deep within the +// unmanaged code but still be able to initialize the stack iterator at the first stack frame which may hold +// interesting GC references. In all our helper cases this corresponds to the most recent managed frame (e.g. +// the helper's caller). +// +// This macro builds a frame describing the current state of managed code. +// +// INVARIANTS +// - The macro assumes it defines the method prolog, it should typically be the first code in a method and +// certainly appear before any attempt to alter the stack pointer. +// - This macro uses trashReg (after its initial value has been saved in the frame) and upon exit trashReg +// will contain the address of transition frame. +// +.macro PUSH_COOP_PINVOKE_FRAME trashReg + + PROLOG_STACK_ALLOC 4 // Save space for caller's SP + PROLOG_PUSH "{r4-r6,r8-r10}" // Save preserved registers + PROLOG_STACK_ALLOC 8 // Save space for flags and Thread* + PROLOG_PUSH "{r7}" // Save caller's FP + PROLOG_PUSH "{r11,lr}" // Save caller's frame-chain pointer and PC + + // Compute SP value at entry to this method and save it in the last slot of the frame (slot #11). + add \trashReg, sp, #(12 * 4) + str \trashReg, [sp, #(11 * 4)] + + // Record the bitmask of saved registers in the frame (slot #4). + mov \trashReg, #DEFAULT_FRAME_SAVE_FLAGS + str \trashReg, [sp, #(4 * 4)] + + mov \trashReg, sp +.endm + +// Pop the frame and restore register state preserved by PUSH_COOP_PINVOKE_FRAME +.macro POP_COOP_PINVOKE_FRAME + EPILOG_POP "{r11,lr}" // Restore caller's frame-chain pointer and PC (return address) + EPILOG_POP "{r7}" // Restore caller's FP + EPILOG_STACK_FREE 8 // Discard flags and Thread* + EPILOG_POP "{r4-r6,r8-r10}" // Restore preserved registers + EPILOG_STACK_FREE 4 // Discard caller's SP +.endm + +#ifdef _DEBUG +.macro GEN_ASSERT message, fileName, funcName + ldr r0, =\message + ldr r1, =\fileName + ldr r2, =\funcName + bl C_FUNC(NYI_Assert) +.endm +#endif + +// thumb with PIC version +.macro INLINE_GET_TLS_VAR Var + ldr r0, 2f +1: + add r0, pc, r0 + bl __tls_get_addr(PLT) + // push data at the end of text section + .pushsection .text, MAX_NUMBER_SUBSECTION_TEXT, "aM", %progbits, 4 + .balign 4 +2: + .4byte \Var(TLSGD) + (. - 1b - 4) + .popsection +.endm + +.macro INLINE_GETTHREAD + // Inlined version of call C_FUNC(RhpGetThread) + INLINE_GET_TLS_VAR tls_CurrentThread +.endm + +.macro INLINE_THREAD_UNHIJACK threadReg, trashReg1, trashReg2 + // + // Thread::Unhijack() + // + ldr \trashReg1, [\threadReg, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + cbz \trashReg1, 1f + + ldr \trashReg2, [\threadReg, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + str \trashReg1, [\trashReg2] + mov \trashReg1, #0 + str \trashReg1, [\threadReg, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] + str \trashReg1, [\threadReg, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + +1: +.endm + +.macro EPILOG_BRANCH_REG reg + + bx \reg + +.endm diff --git a/external/corert/src/Native/Runtime/wasm/AsmOffsetsCpu.h b/external/corert/src/Native/Runtime/wasm/AsmOffsetsCpu.h new file mode 100644 index 0000000000..b03fe89823 --- /dev/null +++ b/external/corert/src/Native/Runtime/wasm/AsmOffsetsCpu.h @@ -0,0 +1,30 @@ +// 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. + +// +// This file is used by AsmOffsets.h to validate that our +// assembly-code offsets always match their C++ counterparts. +// +// NOTE: the offsets MUST be in hex notation WITHOUT the 0x prefix + +PLAT_ASM_SIZEOF(A0, ExInfo) +PLAT_ASM_OFFSET(0, ExInfo, m_pPrevExInfo) +PLAT_ASM_OFFSET(4, ExInfo, m_pExContext) +PLAT_ASM_OFFSET(8, ExInfo, m_exception) +PLAT_ASM_OFFSET(0c, ExInfo, m_kind) +PLAT_ASM_OFFSET(0d, ExInfo, m_passNumber) +PLAT_ASM_OFFSET(10, ExInfo, m_idxCurClause) +PLAT_ASM_OFFSET(14, ExInfo, m_frameIter) +PLAT_ASM_OFFSET(9C, ExInfo, m_notifyDebuggerSP) + +PLAT_ASM_SIZEOF(88, StackFrameIterator) +PLAT_ASM_OFFSET(08, StackFrameIterator, m_FramePointer) +PLAT_ASM_OFFSET(0c, StackFrameIterator, m_ControlPC) +PLAT_ASM_OFFSET(10, StackFrameIterator, m_RegDisplay) + +PLAT_ASM_SIZEOF(4, PAL_LIMITED_CONTEXT) +PLAT_ASM_OFFSET(0, PAL_LIMITED_CONTEXT, IP) + +PLAT_ASM_SIZEOF(0c, REGDISPLAY) +PLAT_ASM_OFFSET(0, REGDISPLAY, SP) diff --git a/external/corert/src/Native/Runtime/windows/AsmOffsets.cpp b/external/corert/src/Native/Runtime/windows/AsmOffsets.cpp index 468419aa42..28415f764d 100644 --- a/external/corert/src/Native/Runtime/windows/AsmOffsets.cpp +++ b/external/corert/src/Native/Runtime/windows/AsmOffsets.cpp @@ -1,11 +1,12 @@ // 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. -#ifdef _ARM_ +#if defined(_ARM_) || defined(_ARM64_) -#define PLAT_ASM_OFFSET(offset, cls, member) OFFSETOF__##cls##__##member equ 0x##offset -#define PLAT_ASM_SIZEOF(size, cls ) SIZEOF__##cls equ 0x##size -#define PLAT_ASM_CONST(constant, expr) expr equ 0x##constant +#define HASH_DEFINE #define +#define PLAT_ASM_OFFSET(offset, cls, member) HASH_DEFINE OFFSETOF__##cls##__##member 0x##offset +#define PLAT_ASM_SIZEOF(size, cls ) HASH_DEFINE SIZEOF__##cls 0x##size +#define PLAT_ASM_CONST(constant, expr) HASH_DEFINE expr 0x##constant #else diff --git a/external/corert/src/Native/Runtime/windows/CoffNativeCodeManager.cpp b/external/corert/src/Native/Runtime/windows/CoffNativeCodeManager.cpp index 27fe4848fb..e024129042 100644 --- a/external/corert/src/Native/Runtime/windows/CoffNativeCodeManager.cpp +++ b/external/corert/src/Native/Runtime/windows/CoffNativeCodeManager.cpp @@ -13,6 +13,7 @@ #include "ICodeManager.h" #include "CoffNativeCodeManager.h" #include "varint.h" +#include "holder.h" #include "CommonMacros.inl" @@ -24,17 +25,52 @@ #define UBF_FUNC_KIND_HANDLER 0x01 #define UBF_FUNC_KIND_FILTER 0x02 -#define UBF_FUNC_HAS_EHINFO 0x04 +#define UBF_FUNC_HAS_EHINFO 0x04 +#define UBF_FUNC_REVERSE_PINVOKE 0x08 +#define UBF_FUNC_HAS_ASSOCIATED_DATA 0x10 -#define UBF_FUNC_REVERSE_PINVOKE 0x08 +#ifdef _TARGET_X86_ +// +// x86 ABI does not define RUNTIME_FUNCTION. Define our own to allow unification between x86 and other platforms. +// +typedef struct _RUNTIME_FUNCTION { + DWORD BeginAddress; + DWORD EndAddress; + DWORD UnwindData; +} RUNTIME_FUNCTION, *PRUNTIME_FUNCTION; -#if defined(_TARGET_AMD64_) +typedef struct _KNONVOLATILE_CONTEXT_POINTERS { + + // The ordering of these fields should be aligned with that + // of corresponding fields in CONTEXT + // + // (See REGDISPLAY in Runtime/regdisp.h for details) + PDWORD Edi; + PDWORD Esi; + PDWORD Ebx; + PDWORD Edx; + PDWORD Ecx; + PDWORD Eax; + + PDWORD Ebp; + +} KNONVOLATILE_CONTEXT_POINTERS, *PKNONVOLATILE_CONTEXT_POINTERS; + +typedef struct _UNWIND_INFO { + ULONG FunctionLength; +} UNWIND_INFO, *PUNWIND_INFO; + +#elif defined(_TARGET_AMD64_) + +#define UNW_FLAG_NHANDLER 0x0 +#define UNW_FLAG_EHANDLER 0x1 +#define UNW_FLAG_UHANDLER 0x2 +#define UNW_FLAG_CHAININFO 0x4 // // The following structures are defined in Windows x64 unwind info specification // http://www.bing.com/search?q=msdn+Exception+Handling+x64 // - typedef union _UNWIND_CODE { struct { uint8_t CodeOffset; @@ -45,11 +81,6 @@ typedef union _UNWIND_CODE { uint16_t FrameOffset; } UNWIND_CODE, *PUNWIND_CODE; -#define UNW_FLAG_NHANDLER 0x0 -#define UNW_FLAG_EHANDLER 0x1 -#define UNW_FLAG_UHANDLER 0x2 -#define UNW_FLAG_CHAININFO 0x4 - typedef struct _UNWIND_INFO { uint8_t Version : 3; uint8_t Flags : 5; @@ -60,11 +91,11 @@ typedef struct _UNWIND_INFO { UNWIND_CODE UnwindCode[1]; } UNWIND_INFO, *PUNWIND_INFO; +#endif // _TARGET_X86_ + typedef DPTR(struct _UNWIND_INFO) PTR_UNWIND_INFO; typedef DPTR(union _UNWIND_CODE) PTR_UNWIND_CODE; -#endif // _TARGET_AMD64_ - static PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntimeFunction, /* out */ size_t * pSize) { #if defined(_TARGET_AMD64_) @@ -85,6 +116,14 @@ static PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntim return pUnwindInfo; +#elif defined(_TARGET_X86_) + + PTR_UNWIND_INFO pUnwindInfo(dac_cast(moduleBase + pRuntimeFunction->UnwindInfoAddress)); + + *pSize = sizeof(UNWIND_INFO); + + return pUnwindInfo; + #elif defined(_TARGET_ARM_) // if this function uses packed unwind data then at least one of the two least significant bits @@ -125,7 +164,9 @@ static PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntim *pSize = size; return xdata; #else - #error unexpected target architecture + PORTABILITY_ASSERT("GetUnwindDataBlob"); + *pSize = 0; + return NULL; #endif } @@ -148,7 +189,7 @@ static int LookupUnwindInfoForMethod(UInt32 relativePc, int low, int high) { -#ifdef TARGET_ARM +#ifdef _TARGET_ARM_ relativePc |= THUMB_CODE; #endif @@ -296,6 +337,9 @@ void CoffNativeCodeManager::EnumGcRefs(MethodInfo * pMethodInfo, uint8_t unwindBlockFlags = *p++; + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) p += sizeof(int32_t); @@ -346,6 +390,9 @@ bool CoffNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo, uint8_t unwindBlockFlags = *p++; + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + if ((unwindBlockFlags & UBF_FUNC_REVERSE_PINVOKE) != 0) { // Reverse PInvoke transition should on the main function body only @@ -382,18 +429,29 @@ bool CoffNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo, memset(&contextPointers, 0xDD, sizeof(contextPointers)); #endif -#define FOR_EACH_NONVOLATILE_REGISTER(F) \ - F(Rax) F(Rcx) F(Rdx) F(Rbx) F(Rbp) F(Rsi) F(Rdi) F(R8) F(R9) F(R10) F(R11) F(R12) F(R13) F(R14) F(R15) +#ifdef _TARGET_X86_ + #define FOR_EACH_NONVOLATILE_REGISTER(F) \ + F(E, ax) F(E, cx) F(E, dx) F(E, bx) F(E, bp) F(E, si) F(E, di) + #define WORDPTR PDWORD +#else + #define FOR_EACH_NONVOLATILE_REGISTER(F) \ + F(R, ax) F(R, cx) F(R, dx) F(R, bx) F(R, bp) F(R, si) F(R, di) \ + F(R, 8) F(R, 9) F(R, 10) F(R, 11) F(R, 12) F(R, 13) F(R, 14) F(R, 15) + #define WORDPTR PDWORD64 +#endif -#define REGDISPLAY_TO_CONTEXT(reg) \ - contextPointers.reg = (PDWORD64) pRegisterSet->p##reg; \ - if (pRegisterSet->p##reg != NULL) context.reg = *(pRegisterSet->p##reg); +#define REGDISPLAY_TO_CONTEXT(prefix, reg) \ + contextPointers.prefix####reg = (WORDPTR) pRegisterSet->pR##reg; \ + if (pRegisterSet->pR##reg != NULL) context.prefix##reg = *(pRegisterSet->pR##reg); -#define CONTEXT_TO_REGDISPLAY(reg) \ - pRegisterSet->p##reg = (PTR_UIntNative) contextPointers.reg; +#define CONTEXT_TO_REGDISPLAY(prefix, reg) \ + pRegisterSet->pR##reg = (PTR_UIntNative) contextPointers.prefix####reg; FOR_EACH_NONVOLATILE_REGISTER(REGDISPLAY_TO_CONTEXT); +#ifdef _TARGET_X86_ + PORTABILITY_ASSERT("CoffNativeCodeManager::UnwindStackFrame"); +#else // _TARGET_X86_ memcpy(&context.Xmm6, pRegisterSet->Xmm, sizeof(pRegisterSet->Xmm)); context.Rsp = pRegisterSet->SP; @@ -417,6 +475,7 @@ bool CoffNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo, pRegisterSet->pIP = PTR_PCODE(pRegisterSet->SP - sizeof(TADDR)); memcpy(pRegisterSet->Xmm, &context.Xmm6, sizeof(pRegisterSet->Xmm)); +#endif // _TARGET_X86_ FOR_EACH_NONVOLATILE_REGISTER(CONTEXT_TO_REGDISPLAY); @@ -427,13 +486,81 @@ bool CoffNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo, return true; } +// Convert the return kind that was encoded by RyuJIT to the +// value that CoreRT runtime can understand and support. +GCRefKind GetGcRefKind(ReturnKind returnKind) +{ + static_assert((GCRefKind)ReturnKind::RT_Scalar == GCRK_Scalar, "ReturnKind::RT_Scalar does not match GCRK_Scalar"); + static_assert((GCRefKind)ReturnKind::RT_Object == GCRK_Object, "ReturnKind::RT_Object does not match GCRK_Object"); + static_assert((GCRefKind)ReturnKind::RT_ByRef == GCRK_Byref, "ReturnKind::RT_ByRef does not match GCRK_Byref"); + ASSERT((returnKind == RT_Scalar) || (returnKind == GCRK_Object) || (returnKind == GCRK_Byref)); + + return (GCRefKind)returnKind; +} + bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet, // in PTR_PTR_VOID * ppvRetAddrLocation, // out GCRefKind * pRetValueKind) // out { - // @TODO: CORERT: GetReturnAddressHijackInfo +#if defined(_TARGET_AMD64_) + CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo; + + size_t unwindDataBlobSize; + PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pNativeMethodInfo->runtimeFunction, &unwindDataBlobSize); + + PTR_UInt8 p = dac_cast(pUnwindDataBlob) + unwindDataBlobSize; + + uint8_t unwindBlockFlags = *p++; + + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + + // Check whether this is a funclet + if ((unwindBlockFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT) + return false; + + // Skip hijacking a reverse-pinvoke method - it doesn't get us much because we already synchronize + // with the GC on the way back to native code. + if ((unwindBlockFlags & UBF_FUNC_REVERSE_PINVOKE) != 0) + return false; + + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) + p += sizeof(int32_t); + + // Decode the GC info for the current method to detemine its return type + GcInfoDecoder decoder( + GCInfoToken(p), + GcInfoDecoderFlags(DECODE_RETURN_KIND), + 0 + ); + + GCRefKind gcRefKind = GetGcRefKind(decoder.GetReturnKind()); + + // Unwind the current method context to the caller's context to get its stack pointer + // and obtain the location of the return address on the stack + SIZE_T EstablisherFrame; + PVOID HandlerData; + CONTEXT context; + context.Rsp = pRegisterSet->GetSP(); + context.Rbp = pRegisterSet->GetFP(); + context.Rip = pRegisterSet->GetIP(); + + RtlVirtualUnwind(NULL, + dac_cast(m_moduleBase), + pRegisterSet->IP, + (PRUNTIME_FUNCTION)pNativeMethodInfo->runtimeFunction, + &context, + &HandlerData, + &EstablisherFrame, + NULL); + + *ppvRetAddrLocation = (PTR_PTR_VOID)(context.Rsp - sizeof (PVOID)); + *pRetValueKind = gcRefKind; + return true; +#else return false; +#endif // defined(_TARGET_AMD64_) } void CoffNativeCodeManager::UnsynchronizedHijackMethodLoops(MethodInfo * pMethodInfo) @@ -484,6 +611,9 @@ bool CoffNativeCodeManager::EHEnumInit(MethodInfo * pMethodInfo, PTR_VOID * pMet uint8_t unwindBlockFlags = *p++; + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + // return if there is no EH info associated with this method if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) == 0) { @@ -552,6 +682,11 @@ bool CoffNativeCodeManager::EHEnumNext(EHEnumState * pEHEnumState, EHClause * pE return true; } +PTR_VOID CoffNativeCodeManager::GetOsModuleHandle() +{ + return dac_cast(m_moduleBase); +} + PTR_VOID CoffNativeCodeManager::GetMethodStartAddress(MethodInfo * pMethodInfo) { CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo; @@ -568,32 +703,62 @@ void * CoffNativeCodeManager::GetClasslibFunction(ClasslibFunctionId functionId) return m_pClasslibFunctions[id]; } +PTR_VOID CoffNativeCodeManager::GetAssociatedData(PTR_VOID ControlPC) +{ + TADDR relativePC = dac_cast(ControlPC) - m_moduleBase; + + int MethodIndex = LookupUnwindInfoForMethod((UInt32)relativePC, m_pRuntimeFunctionTable, 0, m_nRuntimeFunctionTable - 1); + if (MethodIndex < 0) + return NULL; + + PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex; + + size_t unwindDataBlobSize; + PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pRuntimeFunction, &unwindDataBlobSize); + + PTR_UInt8 p = dac_cast(pUnwindDataBlob) + unwindDataBlobSize; + + uint8_t unwindBlockFlags = *p++; + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) == 0) + return NULL; + + UInt32 dataRVA = *(UInt32*)p; + return dac_cast(m_moduleBase + dataRVA); +} + extern "C" bool __stdcall RegisterCodeManager(ICodeManager * pCodeManager, PTR_VOID pvStartRange, UInt32 cbRange); +extern "C" void __stdcall UnregisterCodeManager(ICodeManager * pCodeManager); +extern "C" bool __stdcall RegisterUnboxingStubs(PTR_VOID pvStartRange, UInt32 cbRange); extern "C" -bool RhpRegisterCoffModule(void * pModule, - void * pvStartRange, UInt32 cbRange, - void ** pClasslibFunctions, UInt32 nClasslibFunctions) +bool RhRegisterOSModule(void * pModule, + void * pvManagedCodeStartRange, UInt32 cbManagedCodeRange, + void * pvUnboxingStubsStartRange, UInt32 cbUnboxingStubsRange, + void ** pClasslibFunctions, UInt32 nClasslibFunctions) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pModule; PIMAGE_NT_HEADERS pNTHeaders = (PIMAGE_NT_HEADERS)((TADDR)pModule + pDosHeader->e_lfanew); IMAGE_DATA_DIRECTORY * pRuntimeFunctions = &(pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION]); - CoffNativeCodeManager * pCoffNativeCodeManager = new (nothrow) CoffNativeCodeManager((TADDR)pModule, + NewHolder pCoffNativeCodeManager = new (nothrow) CoffNativeCodeManager((TADDR)pModule, dac_cast((TADDR)pModule + pRuntimeFunctions->VirtualAddress), pRuntimeFunctions->Size / sizeof(RUNTIME_FUNCTION), pClasslibFunctions, nClasslibFunctions); + if (pCoffNativeCodeManager == nullptr) + return false; + + if (!RegisterCodeManager(pCoffNativeCodeManager, pvManagedCodeStartRange, cbManagedCodeRange)) + return false; + + if (!RegisterUnboxingStubs(pvUnboxingStubsStartRange, cbUnboxingStubsRange)) { + UnregisterCodeManager(pCoffNativeCodeManager); return false; } - if (!RegisterCodeManager(pCoffNativeCodeManager, pvStartRange, cbRange)) - { - delete pCoffNativeCodeManager; - return false; - } + pCoffNativeCodeManager.SuppressRelease(); return true; } diff --git a/external/corert/src/Native/Runtime/windows/CoffNativeCodeManager.h b/external/corert/src/Native/Runtime/windows/CoffNativeCodeManager.h index 09b9603864..81b4ee6251 100644 --- a/external/corert/src/Native/Runtime/windows/CoffNativeCodeManager.h +++ b/external/corert/src/Native/Runtime/windows/CoffNativeCodeManager.h @@ -4,7 +4,7 @@ #pragma once -#if defined(_TARGET_AMD64_) +#if defined(_TARGET_AMD64_) || defined(_TARGET_X86_) struct T_RUNTIME_FUNCTION { uint32_t BeginAddress; uint32_t EndAddress; @@ -94,4 +94,8 @@ public: PTR_VOID GetMethodStartAddress(MethodInfo * pMethodInfo); void * GetClasslibFunction(ClasslibFunctionId functionId); + + PTR_VOID GetAssociatedData(PTR_VOID ControlPC); + + PTR_VOID GetOsModuleHandle(); }; diff --git a/external/corert/src/Native/Runtime/windows/PalRedhawkCommon.cpp b/external/corert/src/Native/Runtime/windows/PalRedhawkCommon.cpp index 77375ee9fd..7618b24c0d 100644 --- a/external/corert/src/Native/Runtime/windows/PalRedhawkCommon.cpp +++ b/external/corert/src/Native/Runtime/windows/PalRedhawkCommon.cpp @@ -222,7 +222,7 @@ REDHAWK_PALEXPORT void REDHAWK_PALAPI PalGetPDBInfo(HANDLE hOsHandle, _Out_ GUID } } -REDHAWK_PALEXPORT Int32 PalGetProcessCpuCount() +REDHAWK_PALEXPORT Int32 REDHAWK_PALAPI PalGetProcessCpuCount() { static int CpuCount = 0; @@ -246,7 +246,7 @@ REDHAWK_PALEXPORT Int32 PalGetProcessCpuCount() //Reads the entire contents of the file into the specified buffer, buff //returns the number of bytes read if the file is successfully read //returns 0 if the file is not found, size is greater than maxBytesToRead or the file couldn't be opened or read -REDHAWK_PALEXPORT UInt32 PalReadFileContents(_In_z_ const TCHAR* fileName, _Out_writes_all_(maxBytesToRead) char* buff, _In_ UInt32 maxBytesToRead) +REDHAWK_PALEXPORT UInt32 REDHAWK_PALAPI PalReadFileContents(_In_z_ const TCHAR* fileName, _Out_writes_all_(maxBytesToRead) char* buff, _In_ UInt32 maxBytesToRead) { WIN32_FILE_ATTRIBUTE_DATA attrData; @@ -282,7 +282,7 @@ REDHAWK_PALEXPORT UInt32 PalReadFileContents(_In_z_ const TCHAR* fileName, _Out_ // Retrieves the entire range of memory dedicated to the calling thread's stack. This does // not get the current dynamic bounds of the stack, which can be significantly smaller than // the maximum bounds. -REDHAWK_PALEXPORT bool PalGetMaximumStackBounds(_Out_ void** ppStackLowOut, _Out_ void** ppStackHighOut) +REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalGetMaximumStackBounds(_Out_ void** ppStackLowOut, _Out_ void** ppStackHighOut) { // VirtualQuery on the address of a local variable to get the allocation // base of the stack. Then use the StackBase field in the TEB to give diff --git a/external/corert/src/Native/Runtime/windows/PalRedhawkInline.h b/external/corert/src/Native/Runtime/windows/PalRedhawkInline.h index 4f3e75e63f..9341544fc0 100644 --- a/external/corert/src/Native/Runtime/windows/PalRedhawkInline.h +++ b/external/corert/src/Native/Runtime/windows/PalRedhawkInline.h @@ -53,7 +53,7 @@ FORCEINLINE Int64 PalInterlockedCompareExchange64(_Inout_ _Interlocked_operand_ return _InterlockedCompareExchange64(pDst, iValue, iComparand); } -#if defined(_AMD64_) +#if defined(_AMD64_) || defined(_ARM64_) EXTERN_C UInt8 _InterlockedCompareExchange128(Int64 volatile *, Int64, Int64, Int64 *); #pragma intrinsic(_InterlockedCompareExchange128) FORCEINLINE UInt8 PalInterlockedCompareExchange128(_Inout_ _Interlocked_operand_ Int64 volatile *pDst, Int64 iValueHigh, Int64 iValueLow, Int64 *pComparandAndResult) diff --git a/external/corert/src/Native/Runtime/windows/PalRedhawkMinWin.cpp b/external/corert/src/Native/Runtime/windows/PalRedhawkMinWin.cpp index 9f1b52c936..366afce8fe 100644 --- a/external/corert/src/Native/Runtime/windows/PalRedhawkMinWin.cpp +++ b/external/corert/src/Native/Runtime/windows/PalRedhawkMinWin.cpp @@ -17,7 +17,7 @@ #include #include #include -#ifndef CORERT +#ifdef PROJECTN #include #endif @@ -30,7 +30,6 @@ uint32_t PalEventWrite(REGHANDLE arg1, const EVENT_DESCRIPTOR * arg2, uint32_t a return EventWrite(arg1, arg2, arg3, arg4); } -#define NO_STRESS_LOG #include "gcenv.h" @@ -113,7 +112,7 @@ REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalHasCapability(PalCapability capability) // or if the thread was already registered with a different fiber. // Parameters: // thread - thread to attach -extern "C" void PalAttachThread(void* thread) +REDHAWK_PALEXPORT void REDHAWK_PALAPI PalAttachThread(void* thread) { void* threadFromCurrentFiber = FlsGetValue(g_flsIndex); @@ -135,7 +134,7 @@ extern "C" void PalAttachThread(void* thread) // thread - thread to detach // Return: // true if the thread was detached, false if there was no attached thread -extern "C" bool PalDetachThread(void* thread) +REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalDetachThread(void* thread) { ASSERT(g_flsIndex != FLS_OUT_OF_INDEXES); void* threadFromCurrentFiber = FlsGetValue(g_flsIndex); @@ -163,35 +162,7 @@ extern "C" UInt64 PalGetCurrentThreadIdForLogging() return GetCurrentThreadId(); } -#define SUPPRESS_WARNING_4127 \ - __pragma(warning(push)) \ - __pragma(warning(disable:4127)) /* conditional expression is constant*/ - -#define POP_WARNING_STATE \ - __pragma(warning(pop)) - -#define WHILE_0 \ - SUPPRESS_WARNING_4127 \ - while(0) \ - POP_WARNING_STATE \ - -#define RETURN_RESULT(success) \ - do \ - { \ - if (success) \ - return S_OK; \ - else \ - { \ - DWORD lasterror = GetLastError(); \ - if (lasterror == 0) \ - return E_FAIL; \ - return HRESULT_FROM_WIN32(lasterror); \ - } \ - } \ - WHILE_0; - -extern "C" int __stdcall PalGetModuleFileName(_Out_ const TCHAR** pModuleNameOut, HANDLE moduleBase); - +#if !defined(USE_PORTABLE_HELPERS) && !defined(FEATURE_RX_THUNKS) REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalAllocateThunksFromTemplate(_In_ HANDLE hTemplateModule, UInt32 templateRva, size_t templateSize, _Outptr_result_bytebuffer_(templateSize) void** newThunksOut) { #ifdef XBOX_ONE @@ -231,9 +202,10 @@ REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalFreeThunksFromTemplate(_In_ void return UnmapViewOfFile(pBaseAddress); #endif } +#endif // !USE_PORTABLE_HELPERS && !FEATURE_RX_THUNKS REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalMarkThunksAsValidCallTargets( - void *virtualAddress, + void *virtualAddress, int thunkSize, int thunksPerBlock, int thunkBlockSize, @@ -292,7 +264,11 @@ REDHAWK_PALEXPORT _Success_(return) bool REDHAWK_PALAPI PalGetThreadContext(HAND // at a point where the kernel cannot guarantee a completely accurate context. We'll fail the request in // this case (which should force our caller to resume the thread and try again -- since this is a fairly // narrow window we're highly likely to succeed next time). - if ((win32ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) && + // Note: in some cases (x86 WOW64, ARM32 on ARM64) the OS will not set the CONTEXT_EXCEPTION_REPORTING flag + // if the thread is executing in kernel mode (i.e. in the middle of a syscall or exception handling). + // Therefore, we should treat the absence of the CONTEXT_EXCEPTION_REPORTING flag as an indication that + // it is not safe to manipulate with the current state of the thread context. + if ((win32ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) == 0 || (win32ctx.ContextFlags & (CONTEXT_SERVICE_ACTIVE | CONTEXT_EXCEPTION_ACTIVE))) return false; @@ -329,6 +305,24 @@ REDHAWK_PALEXPORT _Success_(return) bool REDHAWK_PALAPI PalGetThreadContext(HAND pCtx->R11 = win32ctx.R11; pCtx->SP = win32ctx.Sp; pCtx->LR = win32ctx.Lr; +#elif defined(_ARM64_) + pCtx->IP = win32ctx.Pc; + pCtx->X0 = win32ctx.X0; + pCtx->X1 = win32ctx.X1; + // TODO: Copy X2-X7 when we start supporting HVA's + pCtx->X19 = win32ctx.X19; + pCtx->X20 = win32ctx.X20; + pCtx->X21 = win32ctx.X21; + pCtx->X22 = win32ctx.X22; + pCtx->X23 = win32ctx.X23; + pCtx->X24 = win32ctx.X24; + pCtx->X25 = win32ctx.X25; + pCtx->X26 = win32ctx.X26; + pCtx->X27 = win32ctx.X27; + pCtx->X28 = win32ctx.X28; + pCtx->SP = win32ctx.Sp; + pCtx->LR = win32ctx.Lr; + pCtx->FP = win32ctx.Fp; #else #error Unsupported platform #endif @@ -1062,7 +1056,8 @@ UInt32 CountBits(size_t bfBitfield) // 'answers' between the current implementation and the CLR implementation. // //#define TRACE_CACHE_TOPOLOGY -#ifdef _DEBUG +#if defined(_DEBUG) && !defined(_ARM64_) +// ARM64TODO: restore void DumpCacheTopology(_In_reads_(cRecords) SYSTEM_LOGICAL_PROCESSOR_INFORMATION * pProcInfos, UInt32 cRecords) { printf("----------------\n"); @@ -1114,7 +1109,7 @@ void DumpCacheTopologyResults(UInt32 maxCpuId, CpuVendor cpuVendor, _In_reads_(c printf(" g_cbLargestOnDieCache: 0x%08zx 0x%08zx :CLR_LargestOnDieCache(TRUE)\n", g_cbLargestOnDieCache, CLR_GetLargestOnDieCacheSize(TRUE, pProcInfos, cRecords)); printf("g_cbLargestOnDieCacheAdjusted: 0x%08zx 0x%08zx :CLR_LargestOnDieCache(FALSE)\n", g_cbLargestOnDieCacheAdjusted, CLR_GetLargestOnDieCacheSize(FALSE, pProcInfos, cRecords)); } -#endif // _DEBUG +#endif // defined(_DEBUG) && !defined(_ARM64_) // Method used to initialize the above values. bool PalQueryProcessorTopology() @@ -1287,18 +1282,21 @@ bool PalQueryProcessorTopology() g_cbLargestOnDieCache = cbCache; g_cbLargestOnDieCacheAdjusted = cbCacheAdjusted; -#ifdef _DEBUG -#ifdef TRACE_CACHE_TOPOLOGY +#if defined(_DEBUG) +#if defined(TRACE_CACHE_TOPOLOGY) && !defined(_ARM64_) +// ARM64TODO: restore DumpCacheTopologyResults(maxCpuId, cpuVendor, pProcInfos, cRecords); -#endif // TRACE_CACHE_TOPOLOGY +#endif // defined(TRACE_CACHE_TOPOLOGY) && !defined(_ARM64_) if ((CLR_GetLargestOnDieCacheSize(TRUE, pProcInfos, cRecords) != g_cbLargestOnDieCache) || (CLR_GetLargestOnDieCacheSize(FALSE, pProcInfos, cRecords) != g_cbLargestOnDieCacheAdjusted) || (CLR_GetLogicalCpuCount(pProcInfos, cRecords) != g_cLogicalCpus)) { +#if !defined(_ARM64_) DumpCacheTopologyResults(maxCpuId, cpuVendor, pProcInfos, cRecords); +#endif assert(!"QueryProcessorTopology doesn't match CLR's results. See stdout for more info."); } -#endif // TRACE_CACHE_TOPOLOGY +#endif } if (pProcInfos) @@ -1354,7 +1352,7 @@ REDHAWK_PALEXPORT _Ret_maybenull_ void* REDHAWK_PALAPI PalSetWerDataBuffer(_In_ static LARGE_INTEGER g_performanceFrequency; -#ifndef CORERT +#ifdef PROJECTN static bool g_roInitialized; #endif @@ -1368,7 +1366,7 @@ bool GCToOSInterface::Initialize() return false; } -#ifndef CORERT +#ifdef PROJECTN // TODO: Remove the RoInitialize call when we implement non-WinRT framework for classic apps HRESULT hr = RoInitialize(RO_INIT_MULTITHREADED); @@ -1392,7 +1390,7 @@ bool GCToOSInterface::Initialize() // Must be called on the same thread as Initialize. void GCToOSInterface::Shutdown() { -#ifndef CORERT +#ifdef PROJECTN if (g_roInitialized) { RoUninitialize(); diff --git a/external/corert/src/Native/System.Private.CoreLib.Native/CMakeLists.txt b/external/corert/src/Native/System.Private.CoreLib.Native/CMakeLists.txt index 741f189e41..760b75e24d 100644 --- a/external/corert/src/Native/System.Private.CoreLib.Native/CMakeLists.txt +++ b/external/corert/src/Native/System.Private.CoreLib.Native/CMakeLists.txt @@ -24,4 +24,4 @@ endif () include(configure.cmake) -install (TARGETS System.Private.CoreLib.Native DESTINATION lib) +install (TARGETS System.Private.CoreLib.Native DESTINATION sdk) diff --git a/external/corert/src/Native/System.Private.CoreLib.Native/config.h.in b/external/corert/src/Native/System.Private.CoreLib.Native/config.h.in index 32f0d03a55..3d13262a26 100644 --- a/external/corert/src/Native/System.Private.CoreLib.Native/config.h.in +++ b/external/corert/src/Native/System.Private.CoreLib.Native/config.h.in @@ -6,3 +6,5 @@ #cmakedefine01 HAVE_MACH_ABSOLUTE_TIME #cmakedefine01 HAVE_SCHED_GETCPU #cmakedefine01 HAVE_LIBUUID_H +#cmakedefine01 HAVE_UUID_GENERATE_RANDOM +#cmakedefine01 HAVE_UUID_GENERATE diff --git a/external/corert/src/Native/System.Private.CoreLib.Native/configure.cmake b/external/corert/src/Native/System.Private.CoreLib.Native/configure.cmake index c8dca1b634..c84bf89718 100644 --- a/external/corert/src/Native/System.Private.CoreLib.Native/configure.cmake +++ b/external/corert/src/Native/System.Private.CoreLib.Native/configure.cmake @@ -6,6 +6,13 @@ check_library_exists(pthread pthread_condattr_setclock "" HAVE_PTHREAD_CONDATTR_ check_include_files(uuid/uuid.h HAVE_LIBUUID_H) +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) + set(CMAKE_REQUIRED_LIBRARIES uuid) +endif() +check_function_exists(uuid_generate_random HAVE_UUID_GENERATE_RANDOM) +check_function_exists(uuid_generate HAVE_UUID_GENERATE) +set(CMAKE_REQUIRED_LIBRARIES) + check_cxx_source_runs(" #include #include diff --git a/external/corert/src/Native/System.Private.CoreLib.Native/pal_environment.cpp b/external/corert/src/Native/System.Private.CoreLib.Native/pal_environment.cpp index 1caa6fd01b..94660babad 100644 --- a/external/corert/src/Native/System.Private.CoreLib.Native/pal_environment.cpp +++ b/external/corert/src/Native/System.Private.CoreLib.Native/pal_environment.cpp @@ -17,53 +17,6 @@ extern "C" char* CoreLibNative_GetEnv(const char* variable) return getenv(variable); } -#define SECONDS_TO_MILLISECONDS 1000 -#define MILLISECONDS_TO_MICROSECONDS 1000 -#define MILLISECONDS_TO_NANOSECONDS 1000000 // 10^6 - -// Returns a 64-bit tick count with a millisecond resolution. It tries its best -// to return monotonically increasing counts and avoid being affected by changes -// to the system clock (either due to drift or due to explicit changes to system -// time). -extern "C" uint64_t CoreLibNative_GetTickCount64() -{ - uint64_t retval = 0; - -#if HAVE_MACH_ABSOLUTE_TIME - { - mach_timebase_info_data_t *machTimebaseInfo = GetMachTimebaseInfo(); - retval = (mach_absolute_time() * machTimebaseInfo->numer / machTimebaseInfo->denom) / MILLISECONDS_TO_NANOSECONDS; - } -#elif HAVE_CLOCK_MONOTONIC_COARSE || HAVE_CLOCK_MONOTONIC - { - clockid_t clockType = -#if HAVE_CLOCK_MONOTONIC_COARSE - CLOCK_MONOTONIC_COARSE; // good enough resolution, fastest speed -#else - CLOCK_MONOTONIC; -#endif - struct timespec ts; - if (clock_gettime(clockType, &ts) != 0) - { - assert(false); - return retval; - } - retval = (ts.tv_sec * SECONDS_TO_MILLISECONDS) + (ts.tv_nsec / MILLISECONDS_TO_NANOSECONDS); - } -#else - { - struct timeval tv; - if (gettimeofday(&tv, NULL) == -1) - { - assert(false); - return retval; - } - retval = (tv.tv_sec * SECONDS_TO_MILLISECONDS) + (tv.tv_usec / MILLISECONDS_TO_MICROSECONDS); - } -#endif - return retval; -} - extern "C" int32_t CoreLibNative_SchedGetCpu() { #if HAVE_SCHED_GETCPU diff --git a/external/corert/src/Native/System.Private.CoreLib.Native/pal_guid.cpp b/external/corert/src/Native/System.Private.CoreLib.Native/pal_guid.cpp index a4e40d7f11..8e35d089ee 100644 --- a/external/corert/src/Native/System.Private.CoreLib.Native/pal_guid.cpp +++ b/external/corert/src/Native/System.Private.CoreLib.Native/pal_guid.cpp @@ -19,15 +19,17 @@ typedef struct _GUID { extern "C" void CoreLibNative_CreateGuid(GUID* pGuid) { -#if HAVE_LIBUUID_H +#if HAVE_UUID_GENERATE_RANDOM uuid_generate_random(*(uuid_t*)pGuid); +#elif HAVE_UUID_GENERATE + uuid_generate(*(uuid_t*)pGuid); +#else +#error Don't know how to generate UUID on this platform +#endif - // Change the byte order of the Data1, 2 and 3, since the uuid_generate_random + // Change the byte order of the Data1, 2 and 3, since uuid_generate_random and uuid_generate // generates them with big endian while GUIDS need to have them in little endian. pGuid->Data1 = SWAP32(pGuid->Data1); pGuid->Data2 = SWAP16(pGuid->Data2); pGuid->Data3 = SWAP16(pGuid->Data3); -#else -#error Don't know how to generate UUID on this platform -#endif } diff --git a/external/corert/src/Native/System.Private.CoreLib.Native/pal_time.cpp b/external/corert/src/Native/System.Private.CoreLib.Native/pal_time.cpp index 3ee54da85f..b0cd1b1ed6 100644 --- a/external/corert/src/Native/System.Private.CoreLib.Native/pal_time.cpp +++ b/external/corert/src/Native/System.Private.CoreLib.Native/pal_time.cpp @@ -6,7 +6,8 @@ #include "pal_threading.h" #include "pal_time.h" -#include +#include +#include #if HAVE_MACH_ABSOLUTE_TIME static LowLevelMutex s_lock(true /* abortOnFailure */, nullptr /* successRef */); @@ -33,3 +34,196 @@ mach_timebase_info_data_t *InitializeTimebaseInfo() return g_isMachTimebaseInfoInitialized ? &g_machTimebaseInfo : nullptr; } #endif + +extern "C" uint64_t CoreLibNative_GetHighPrecisionCount() +{ + uint64_t counts = 0; + +#if HAVE_MACH_ABSOLUTE_TIME + { + counts = mach_absolute_time(); + } +#elif HAVE_CLOCK_MONOTONIC + { + clockid_t clockType = CLOCK_MONOTONIC; + struct timespec ts; + if (clock_gettime(clockType, &ts) != 0) + { + assert(false); + return counts; + } + counts = ts.tv_sec * NanosecondsPerSecond + ts.tv_nsec; + } +#else + { + struct timeval tv; + if (gettimeofday(&tv, nullptr) == -1) + { + assert(false); + return counts; + } + counts = tv.tv_sec * MicrosecondsPerSecond + tv.tv_usec; + } +#endif + return counts; +} + +#if HAVE_MACH_ABSOLUTE_TIME +static uint64_t s_highPrecisionCounterFrequency = 0; +#endif +extern "C" uint64_t CoreLibNative_GetHighPrecisionCounterFrequency() +{ +#if HAVE_MACH_ABSOLUTE_TIME + if (s_highPrecisionCounterFrequency != 0) + { + return s_highPrecisionCounterFrequency; + } + { + mach_timebase_info_data_t *machTimebaseInfo = GetMachTimebaseInfo(); + s_highPrecisionCounterFrequency = NanosecondsPerSecond * static_cast(machTimebaseInfo->denom) / machTimebaseInfo->numer; + } + return s_highPrecisionCounterFrequency; +#elif HAVE_CLOCK_MONOTONIC_COARSE || HAVE_CLOCK_MONOTONIC + { + return NanosecondsPerSecond; + } +#else + { + return MicrosecondsPerSecond; + } +#endif + return 0; +} + +#define SECONDS_TO_MILLISECONDS 1000 +#define MILLISECONDS_TO_MICROSECONDS 1000 +#define MILLISECONDS_TO_NANOSECONDS 1000000 // 10^6 + +// Returns a 64-bit tick count with a millisecond resolution. It tries its best +// to return monotonically increasing counts and avoid being affected by changes +// to the system clock (either due to drift or due to explicit changes to system +// time). +extern "C" uint64_t CoreLibNative_GetTickCount64() +{ + uint64_t retval = 0; + +#if HAVE_MACH_ABSOLUTE_TIME + { + mach_timebase_info_data_t *machTimebaseInfo = GetMachTimebaseInfo(); + retval = (mach_absolute_time() * machTimebaseInfo->numer / machTimebaseInfo->denom) / MILLISECONDS_TO_NANOSECONDS; + } +#elif HAVE_CLOCK_MONOTONIC_COARSE || HAVE_CLOCK_MONOTONIC + { + clockid_t clockType = +#if HAVE_CLOCK_MONOTONIC_COARSE + CLOCK_MONOTONIC_COARSE; // good enough resolution, fastest speed +#else + CLOCK_MONOTONIC; +#endif + struct timespec ts; + if (clock_gettime(clockType, &ts) != 0) + { + assert(false); + return retval; + } + retval = (ts.tv_sec * SECONDS_TO_MILLISECONDS) + (ts.tv_nsec / MILLISECONDS_TO_NANOSECONDS); + } +#else + { + struct timeval tv; + if (gettimeofday(&tv, NULL) == -1) + { + assert(false); + return retval; + } + retval = (tv.tv_sec * SECONDS_TO_MILLISECONDS) + (tv.tv_usec / MILLISECONDS_TO_MICROSECONDS); + } +#endif + return retval; +} + + +struct ProcessCpuInformation +{ + uint64_t lastRecordedCurrentTime; + uint64_t lastRecordedKernelTime; + uint64_t lastRecordedUserTime; +}; + + +/* +Function: +CoreLibNative_GetCpuUtilization + +The main purpose of this function is to compute the overall CPU utilization +for the CLR thread pool to regulate the number of worker threads. +Since there is no consistent API on Unix to get the CPU utilization +from a user process, getrusage and gettimeofday are used to +compute the current process's CPU utilization instead. + +*/ + +static long numProcessors = 0; +extern "C" int32_t CoreLibNative_GetCpuUtilization(ProcessCpuInformation* previousCpuInfo) +{ + if (numProcessors <= 0) + { + numProcessors = sysconf(_SC_NPROCESSORS_ONLN); + if (numProcessors <= 0) + { + return 0; + } + } + + uint64_t kernelTime = 0; + uint64_t userTime = 0; + + struct rusage resUsage; + if (getrusage(RUSAGE_SELF, &resUsage) == -1) + { + assert(false); + return 0; + } + else + { + kernelTime = TimeValToNanoseconds(resUsage.ru_stime); + userTime = TimeValToNanoseconds(resUsage.ru_utime); + } + + uint64_t currentTime = CoreLibNative_GetHighPrecisionCount() * NanosecondsPerSecond / CoreLibNative_GetHighPrecisionCounterFrequency(); + + uint64_t lastRecordedCurrentTime = previousCpuInfo->lastRecordedCurrentTime; + uint64_t lastRecordedKernelTime = previousCpuInfo->lastRecordedKernelTime; + uint64_t lastRecordedUserTime = previousCpuInfo->lastRecordedUserTime; + + uint64_t cpuTotalTime = 0; + if (currentTime > lastRecordedCurrentTime) + { + // cpuTotalTime is based on clock time. Since multiple threads can run in parallel, + // we need to scale cpuTotalTime cover the same amount of total CPU time. + // rusage time is already scaled across multiple processors. + cpuTotalTime = (currentTime - lastRecordedCurrentTime); + cpuTotalTime *= numProcessors; + } + + uint64_t cpuBusyTime = 0; + if (userTime >= lastRecordedUserTime && kernelTime >= lastRecordedKernelTime) + { + cpuBusyTime = (userTime - lastRecordedUserTime) + (kernelTime - lastRecordedKernelTime); + } + + int32_t cpuUtilization = 0; + if (cpuTotalTime > 0 && cpuBusyTime > 0) + { + cpuUtilization = static_cast(cpuBusyTime / cpuTotalTime); + } + + assert(cpuUtilization >= 0 && cpuUtilization <= 100); + + previousCpuInfo->lastRecordedCurrentTime = currentTime; + previousCpuInfo->lastRecordedUserTime = userTime; + previousCpuInfo->lastRecordedKernelTime = kernelTime; + + return cpuUtilization; +} + diff --git a/external/corert/src/Native/System.Private.CoreLib.Native/pal_time.h b/external/corert/src/Native/System.Private.CoreLib.Native/pal_time.h index 9cac134be8..cc6898c893 100644 --- a/external/corert/src/Native/System.Private.CoreLib.Native/pal_time.h +++ b/external/corert/src/Native/System.Private.CoreLib.Native/pal_time.h @@ -14,8 +14,11 @@ #include #endif -const int32_t MillisecondsToNanoseconds = 1000 * 1000; -const int32_t SecondsToNanoseconds = 1000 * 1000 * 1000; +const uint64_t MillisecondsPerSecond = 1000; +const uint64_t NanosecondsPerSecond = 1000 * 1000 * 1000; +const uint64_t MicrosecondsPerSecond = 1000 * 1000; +const uint64_t NanosecondsPerMicrosecond = 1000; +const uint64_t NanosecondsPerMillisecond = 1000 * 1000; inline void MillisecondsToTimeSpec(uint32_t milliseconds, timespec *t) { @@ -26,9 +29,9 @@ inline void MillisecondsToTimeSpec(uint32_t milliseconds, timespec *t) return; } - uint64_t nanoseconds = milliseconds * static_cast(MillisecondsToNanoseconds); - t->tv_sec = static_cast(nanoseconds / SecondsToNanoseconds); - t->tv_nsec = static_cast(nanoseconds % SecondsToNanoseconds); + uint64_t nanoseconds = milliseconds * NanosecondsPerMillisecond; + t->tv_sec = static_cast(nanoseconds / NanosecondsPerSecond); + t->tv_nsec = static_cast(nanoseconds % NanosecondsPerSecond); } inline void AddMillisecondsToTimeSpec(uint32_t milliseconds, timespec *t) @@ -38,15 +41,20 @@ inline void AddMillisecondsToTimeSpec(uint32_t milliseconds, timespec *t) return; } - uint64_t nanoseconds = milliseconds * static_cast(MillisecondsToNanoseconds) + t->tv_nsec; - if (nanoseconds >= SecondsToNanoseconds) + uint64_t nanoseconds = milliseconds * NanosecondsPerMillisecond + t->tv_nsec; + if (nanoseconds >= NanosecondsPerSecond) { - t->tv_sec += static_cast(nanoseconds / SecondsToNanoseconds); - nanoseconds %= SecondsToNanoseconds; + t->tv_sec += static_cast(nanoseconds / NanosecondsPerSecond); + nanoseconds %= NanosecondsPerSecond; } t->tv_nsec = static_cast(nanoseconds); } +inline uint64_t TimeValToNanoseconds(const struct timeval& t) +{ + return t.tv_sec * NanosecondsPerSecond + t.tv_usec * NanosecondsPerMicrosecond; +} + #if HAVE_MACH_ABSOLUTE_TIME extern mach_timebase_info_data_t g_machTimebaseInfo; extern bool g_isMachTimebaseInfoInitialized; diff --git a/external/corert/src/Native/gc/env/gcenv.base.h b/external/corert/src/Native/gc/env/gcenv.base.h index 9fe583f9a6..598e5b49c5 100644 --- a/external/corert/src/Native/gc/env/gcenv.base.h +++ b/external/corert/src/Native/gc/env/gcenv.base.h @@ -355,8 +355,8 @@ typedef TADDR OBJECTHANDLE; #error The Volatile type is currently only defined for Visual C++ and Clang #endif -#if defined(__clang__) && !defined(_X86_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) -#error The Volatile type is currently only defined for Clang when targeting x86, AMD64, ARM or ARM64 CPUs +#if defined(__clang__) && !defined(_X86_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_WASM_) +#error The Volatile type is currently only defined for Clang when targeting x86, AMD64, ARM, ARM64 or WASM #endif #if defined(__clang__) diff --git a/external/corert/src/Native/gc/gc.cpp.REMOVED.git-id b/external/corert/src/Native/gc/gc.cpp.REMOVED.git-id index 0b24314cf5..8c88549a14 100644 --- a/external/corert/src/Native/gc/gc.cpp.REMOVED.git-id +++ b/external/corert/src/Native/gc/gc.cpp.REMOVED.git-id @@ -1 +1 @@ -c8c044098b1d85c6800bab6cb32becdfc479de4f \ No newline at end of file +649f2cdd8e76243cb9f2b5ef198b008d75beaf78 \ No newline at end of file diff --git a/external/corert/src/Native/gc/gcpriv.h.REMOVED.git-id b/external/corert/src/Native/gc/gcpriv.h.REMOVED.git-id index 00778aa198..d6b7a815fd 100644 --- a/external/corert/src/Native/gc/gcpriv.h.REMOVED.git-id +++ b/external/corert/src/Native/gc/gcpriv.h.REMOVED.git-id @@ -1 +1 @@ -1f97d7f2d5067db14f20724518d6444a709ba25b \ No newline at end of file +e220a09d83a541201d978c627c23741472b3a1d5 \ No newline at end of file diff --git a/external/corert/src/Native/gc/handletable.cpp b/external/corert/src/Native/gc/handletable.cpp index 29ee435b51..3be1c1611d 100644 --- a/external/corert/src/Native/gc/handletable.cpp +++ b/external/corert/src/Native/gc/handletable.cpp @@ -79,7 +79,7 @@ __inline PTR_HandleTable Table(HHANDLETABLE hTable) /* * HndCreateHandleTable * - * Alocates and initializes a handle table. + * Allocates and initializes a handle table. * */ HHANDLETABLE HndCreateHandleTable(const uint32_t *pTypeFlags, uint32_t uTypeCount, ADIndex uADIndex) diff --git a/external/corert/src/Native/gc/sample/GCSample.vcxproj b/external/corert/src/Native/gc/sample/GCSample.vcxproj index 105e289c1a..b2e7fbaef1 100644 --- a/external/corert/src/Native/gc/sample/GCSample.vcxproj +++ b/external/corert/src/Native/gc/sample/GCSample.vcxproj @@ -1,5 +1,4 @@ - - + Debug @@ -11,7 +10,6 @@ - {58D6B7AE-0A12-49F0-BCF7-200ED8BA445A} Win32Proj GCSample @@ -104,4 +102,4 @@ - \ No newline at end of file + diff --git a/external/corert/src/Native/gc/sample/GCSample.vcxproj.filters b/external/corert/src/Native/gc/sample/GCSample.vcxproj.filters index f6aacfd0c7..d0ff5d6556 100644 --- a/external/corert/src/Native/gc/sample/GCSample.vcxproj.filters +++ b/external/corert/src/Native/gc/sample/GCSample.vcxproj.filters @@ -1,5 +1,4 @@ - - + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} @@ -63,4 +62,4 @@ Source Files - \ No newline at end of file + diff --git a/external/corert/src/Native/gc/unix/gcenv.unix.cpp b/external/corert/src/Native/gc/unix/gcenv.unix.cpp index 34a45b3cc1..0b4ab0ed1e 100644 --- a/external/corert/src/Native/gc/unix/gcenv.unix.cpp +++ b/external/corert/src/Native/gc/unix/gcenv.unix.cpp @@ -330,7 +330,12 @@ bool GCToOSInterface::VirtualCommit(void* address, size_t size) // true if it has succeeded, false if it has failed bool GCToOSInterface::VirtualDecommit(void* address, size_t size) { - return mprotect(address, size, PROT_NONE) == 0; + // TODO: This can fail, however the GC does not handle the failure gracefully + // Explicitly calling mmap instead of mprotect here makes it + // that much more clear to the operating system that we no + // longer need these pages. Also, GC depends on re-commited pages to + // be zeroed-out. + return mmap(address, size, PROT_NONE, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0) != NULL; } // Reset virtual memory range. Indicates that data in the memory range specified by address and size is no diff --git a/external/corert/src/Native/gen-buildsys-clang.sh b/external/corert/src/Native/gen-buildsys-clang.sh index 393133e955..0c2441f774 100755 --- a/external/corert/src/Native/gen-buildsys-clang.sh +++ b/external/corert/src/Native/gen-buildsys-clang.sh @@ -113,12 +113,23 @@ if [[ -n "$CROSSCOMPILE" ]]; then cmake_extra_defines="$cmake_extra_defines -DCMAKE_TOOLCHAIN_FILE=$1/cross/$build_arch/toolchain.cmake" fi -cmake \ - "-DCMAKE_AR=$llvm_ar" \ - "-DCMAKE_LINKER=$llvm_link" \ - "-DCMAKE_NM=$llvm_nm" \ - "-DCMAKE_OBJDUMP=$llvm_objdump" \ - "-DCMAKE_RANLIB=$llvm_ranlib" \ - "-DCMAKE_BUILD_TYPE=$build_type" \ - $cmake_extra_defines \ - "$1/src/Native" +if [ $build_arch == "wasm" ]; then + emcmake cmake \ + "-DEMSCRIPTEN_GENERATE_BITCODE_STATIC_LIBRARIES=1" \ + "-DCMAKE_TOOLCHAIN_FILE=$EMSCRIPTEN/cmake/Modules/Platform/Emscripten.cmake" \ + "-DCMAKE_BUILD_TYPE=$build_type" \ + "$1/src/Native" +else + cmake \ + "-DCMAKE_AR=$llvm_ar" \ + "-DCMAKE_LINKER=$llvm_link" \ + "-DCMAKE_NM=$llvm_nm" \ + "-DCMAKE_OBJDUMP=$llvm_objdump" \ + "-DCMAKE_RANLIB=$llvm_ranlib" \ + "-DCMAKE_BUILD_TYPE=$build_type" \ + "-DCLR_CMAKE_TARGET_ARCH=$build_arch" \ + "-DOBJWRITER_BUILD=${__ObjWriterBuild}" \ + "-DCROSS_BUILD=${__CrossBuild}" \ + $cmake_extra_defines \ + "$1/src/Native" +fi diff --git a/external/corert/src/Native/gen-buildsys-win.bat b/external/corert/src/Native/gen-buildsys-win.bat index b7fdcb4fd4..fa10ef0a44 100644 --- a/external/corert/src/Native/gen-buildsys-win.bat +++ b/external/corert/src/Native/gen-buildsys-win.bat @@ -5,20 +5,16 @@ rem This file invokes cmake and generates the build system for windows. set argC=0 for %%x in (%*) do Set /A argC+=1 -if NOT %argC%==3 GOTO :USAGE +if NOT %argC%==4 GOTO :USAGE if %1=="/?" GOTO :USAGE setlocal -set basePath=%~dp0 -:: remove quotes -set "basePath=%basePath:"=%" -:: remove trailing slash -if %basePath:~-1%==\ set "basePath=%basePath:~0,-1%" -set __VSString=12 2013 -if /i "%2" == "vs2015" (set __VSString=14 2015) -if /i "%2" == "vs2017" (set __VSString=15 2017) -if /i "%3" == "x64" (set __VSString=%__VSString% Win64) +set __CmakeGenerator=Visual Studio 15 2017 +if /i "%2" == "vs2017" (set __CmakeGenerator=Visual Studio 15 2017) +if /i "%3" == "x64" (set __CmakeGenerator=%__CmakeGenerator% Win64) +if /i "%3" == "arm64" (set __CmakeGenerator=%__CmakeGenerator% Win64) +if /i "%3" == "arm" (set __CmakeGenerator=%__CmakeGenerator% ARM) if defined CMakePath goto DoGen @@ -26,7 +22,11 @@ if defined CMakePath goto DoGen for /f "delims=" %%a in ('powershell -NoProfile -ExecutionPolicy ByPass "& %~dp0\probe-win.ps1"') do %%a :DoGen -"%CMakePath%" "-DCLR_CMAKE_TARGET_ARCH=%3" -G "Visual Studio %__VSString%" %1 +if "%3" == "wasm" ( + emcmake "%CMakePath%" "-DEMSCRIPTEN_GENERATE_BITCODE_STATIC_LIBRARIES=1" "-DCMAKE_TOOLCHAIN_FILE=%EMSCRIPTEN%/cmake/Modules/Platform/Emscripten.cmake" "-DCLR_CMAKE_TARGET_ARCH=%3" "-DCMAKE_BUILD_TYPE=%4" -G "NMake Makefiles" %1 +) else ( + "%CMakePath%" "-DCLR_CMAKE_TARGET_ARCH=%3" -G "%__CmakeGenerator%" %1 +) endlocal GOTO :DONE @@ -34,14 +34,9 @@ GOTO :DONE echo "Usage..." echo "gen-buildsys-win.bat " echo "Specify the path to the top level CMake file - /src/Native" - echo "Specify the VSVersion to be used - VS2013 or VS2015" + echo "Specify the VSVersion to be used - VS2017" + echo "Specify the build type (Debug, Release)" EXIT /B 1 :DONE EXIT /B 0 - - - - - - diff --git a/external/corert/src/Native/inc/unix/poppack.h b/external/corert/src/Native/inc/unix/poppack.h new file mode 100644 index 0000000000..aee3b49a38 --- /dev/null +++ b/external/corert/src/Native/inc/unix/poppack.h @@ -0,0 +1,40 @@ +// 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. +// + +// +// =========================================================================== +// File: poppack.h +// +// =========================================================================== +/* +Abstract: + + This file turns packing of structures off. (That is, it enables + automatic alignment of structure fields.) An include file is needed + because various compilers do this in different ways. + + poppack.h is the complement to pshpack?.h. An inclusion of poppack.h + MUST ALWAYS be preceded by an inclusion of one of pshpack?.h, in one-to-one + correspondence. + + For Microsoft compatible compilers, this file uses the pop option + to the pack pragma so that it can restore the previous saved by the + pshpack?.h include file. + +*/ + +#if ! (defined(lint) || defined(RC_INVOKED)) +#if ( _MSC_VER >= 800 && !defined(_M_I86)) || defined(_PUSHPOP_SUPPORTED) +#pragma warning(disable:4103) +#if !(defined( MIDL_PASS )) || defined( __midl ) +#pragma pack(pop) +#else +#pragma pack() +#endif +#else +#pragma pack() +#endif +#endif // ! (defined(lint) || defined(RC_INVOKED)) + diff --git a/external/corert/src/Native/inc/unix/pshpack1.h b/external/corert/src/Native/inc/unix/pshpack1.h new file mode 100644 index 0000000000..884e4c9c40 --- /dev/null +++ b/external/corert/src/Native/inc/unix/pshpack1.h @@ -0,0 +1,39 @@ +// 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. +// + +// +// =========================================================================== +// File: pshpack1.h +// +// =========================================================================== + +/*++ + +Abstract: + + This file turns 1 byte packing of structures on. (That is, it disables + automatic alignment of structure fields.) An include file is needed + because various compilers do this in different ways. For Microsoft + compatible compilers, this files uses the push option to the pack pragma + so that the poppack.h include file can restore the previous packing + reliably. + + The file poppack.h is the complement to this file. + +--*/ + +#if ! (defined(lint) || defined(RC_INVOKED)) +#if ( _MSC_VER >= 800 && !defined(_M_I86)) || defined(_PUSHPOP_SUPPORTED) +#pragma warning(disable:4103) +#if !(defined( MIDL_PASS )) || defined( __midl ) +#pragma pack(push,1) +#else +#pragma pack(1) +#endif +#else +#pragma pack(1) +#endif +#endif // ! (defined(lint) || defined(RC_INVOKED)) + diff --git a/external/corert/src/Native/inc/unix/pshpack4.h b/external/corert/src/Native/inc/unix/pshpack4.h new file mode 100644 index 0000000000..c72dec96ee --- /dev/null +++ b/external/corert/src/Native/inc/unix/pshpack4.h @@ -0,0 +1,39 @@ +// 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. +// + +// +// =========================================================================== +// File: pshpack4.h +// +// =========================================================================== + +/*++ + +Abstract: + + This file turns 4 byte packing of structures on. (That is, it disables + automatic alignment of structure fields.) An include file is needed + because various compilers do this in different ways. For Microsoft + compatible compilers, this files uses the push option to the pack pragma + so that the poppack.h include file can restore the previous packing + reliably. + + The file poppack.h is the complement to this file. + +--*/ + +#if ! (defined(lint) || defined(RC_INVOKED)) +#if ( _MSC_VER >= 800 && !defined(_M_I86)) || defined(_PUSHPOP_SUPPORTED) +#pragma warning(disable:4103) +#if !(defined( MIDL_PASS )) || defined( __midl ) +#pragma pack(push,4) +#else +#pragma pack(4) +#endif +#else +#pragma pack(4) +#endif +#endif // ! (defined(lint) || defined(RC_INVOKED)) + diff --git a/external/corert/src/Native/jitinterface/CMakeLists.txt b/external/corert/src/Native/jitinterface/CMakeLists.txt index 5883a1a0ca..46c071c8ad 100644 --- a/external/corert/src/Native/jitinterface/CMakeLists.txt +++ b/external/corert/src/Native/jitinterface/CMakeLists.txt @@ -7,7 +7,7 @@ set(NATIVE_SOURCES corinfoexception.cpp ) -if(WIN32) +if(WIN32 AND CLR_CMAKE_PLATFORM_ARCH_AMD64) set(NATIVE_SOURCES ${NATIVE_SOURCES} CodeHeap.cpp JITCodeManager.cpp @@ -16,15 +16,15 @@ if(WIN32) add_definitions(-DGCINFODECODER_NO_EE) add_definitions(-DFEATURE_REDHAWK) -endif(WIN32) +endif(WIN32 AND CLR_CMAKE_PLATFORM_ARCH_AMD64) add_library(jitinterface SHARED ${NATIVE_SOURCES} ) -install (TARGETS jitinterface DESTINATION .) +install (TARGETS jitinterface DESTINATION tools) if(WIN32) target_link_libraries(jitinterface ntdll.lib) - install (FILES ${CMAKE_CURRENT_BINARY_DIR}/$/jitinterface.pdb DESTINATION .) + install (FILES ${CMAKE_CURRENT_BINARY_DIR}/$/jitinterface.pdb DESTINATION tools) endif(WIN32) diff --git a/external/corert/src/Native/jitinterface/JITCodeManager.cpp b/external/corert/src/Native/jitinterface/JITCodeManager.cpp index fac0f426e3..198671f14e 100644 --- a/external/corert/src/Native/jitinterface/JITCodeManager.cpp +++ b/external/corert/src/Native/jitinterface/JITCodeManager.cpp @@ -319,7 +319,7 @@ static int LookupUnwindInfoForMethod(UInt32 RelativePc, int Low, int High) { -#ifdef TARGET_ARM +#ifdef _TARGET_ARM_ RelativePc |= THUMB_CODE; #endif @@ -716,6 +716,13 @@ UIntNative JITCodeManager::GetConservativeUpperBoundForOutgoingArgs(MethodInfo * return false; } +PTR_VOID JITCodeManager::GetOsModuleHandle() +{ + // Should not be called + assert(false); + return nullptr; +} + PTR_VOID JITCodeManager::GetMethodStartAddress(MethodInfo * pMethodInfo) { JITMethodInfo * pJITMethodInfo = (JITMethodInfo *)pMethodInfo; @@ -730,3 +737,10 @@ void * JITCodeManager::GetClasslibFunction(ClasslibFunctionId functionId) assert(false); return false; } + +PTR_VOID JITCodeManager::GetAssociatedData(PTR_VOID ControlPC) +{ + // @TODO: CORERT: GetAssociatedData + assert(false); + return NULL; +} diff --git a/external/corert/src/Native/jitinterface/JITCodeManager.h b/external/corert/src/Native/jitinterface/JITCodeManager.h index e763af48ab..aeff8bc899 100644 --- a/external/corert/src/Native/jitinterface/JITCodeManager.h +++ b/external/corert/src/Native/jitinterface/JITCodeManager.h @@ -95,9 +95,10 @@ public: typedef DPTR(RUNTIME_FUNCTION) PTR_RUNTIME_FUNCTION; -#ifdef _TARGET_AMD64_ -#define USE_GROWABLE_FUNCTION_TABLE 1 -#endif +// TODO: Not compatible with Windows 7 +// #ifdef _TARGET_AMD64_ +// #define USE_GROWABLE_FUNCTION_TABLE 1 +// #endif class CodeHeader { @@ -275,4 +276,8 @@ public: PTR_VOID GetMethodStartAddress(MethodInfo * pMethodInfo); void * GetClasslibFunction(ClasslibFunctionId functionId); -}; \ No newline at end of file + + PTR_VOID GetAssociatedData(PTR_VOID ControlPC); + + PTR_VOID GetOsModuleHandle(); +}; diff --git a/external/corert/src/Native/jitinterface/corinfoexception.cpp b/external/corert/src/Native/jitinterface/corinfoexception.cpp index 20c063f805..124080f9b8 100644 --- a/external/corert/src/Native/jitinterface/corinfoexception.cpp +++ b/external/corert/src/Native/jitinterface/corinfoexception.cpp @@ -5,17 +5,17 @@ #include "corinfoexception.h" #include "dllexport.h" -DLL_EXPORT CorInfoException* __stdcall AllocException(const WCHAR* message, int messageLength) +DLL_EXPORT CorInfoException* AllocException(const WCHAR* message, int messageLength) { return new CorInfoException(message, messageLength); } -DLL_EXPORT void __stdcall FreeException(CorInfoException* pException) +DLL_EXPORT void FreeException(CorInfoException* pException) { delete pException; } -DLL_EXPORT const WCHAR* __stdcall GetExceptionMessage(const CorInfoException* pException) +DLL_EXPORT const WCHAR* GetExceptionMessage(const CorInfoException* pException) { return pException->GetMessage(); } diff --git a/external/corert/src/Native/jitinterface/jithost.cpp b/external/corert/src/Native/jitinterface/jithost.cpp index 351fe72a47..62770ddcab 100644 --- a/external/corert/src/Native/jitinterface/jithost.cpp +++ b/external/corert/src/Native/jitinterface/jithost.cpp @@ -6,15 +6,27 @@ #include "dllexport.h" +#ifdef _X86_ +#ifdef PLATFORM_UNIX +#define DEFAULT_CALL_CONV __cdecl +#else +#define DEFAULT_CALL_CONV __stdcall +#endif +#else +#define DEFAULT_CALL_CONV +#define __cdecl +#define __stdcall +#endif + class JitConfigProvider { public: - virtual int getIntConfigValue( + virtual int DEFAULT_CALL_CONV getIntConfigValue( const wchar_t* name, int defaultValue ) = 0; - virtual int getStringConfigValue( + virtual int DEFAULT_CALL_CONV getStringConfigValue( const wchar_t* name, wchar_t* retBuffer, int retBufferLength @@ -78,7 +90,7 @@ public: } }; -DLL_EXPORT void* __stdcall GetJitHost(JitConfigProvider* pConfigProvider) +DLL_EXPORT void* GetJitHost(JitConfigProvider* pConfigProvider) { return new JitHost(pConfigProvider); } diff --git a/external/corert/src/Native/jitinterface/jitinterface.h b/external/corert/src/Native/jitinterface/jitinterface.h index bac5c27911..9902df6501 100644 --- a/external/corert/src/Native/jitinterface/jitinterface.h +++ b/external/corert/src/Native/jitinterface/jitinterface.h @@ -10,172 +10,179 @@ struct CORINFO_LOOKUP_KIND; struct JitInterfaceCallbacks { - unsigned int (__stdcall * getMethodAttribs)(void * thisHandle, CorInfoException** ppException, void* ftn); - void (__stdcall * setMethodAttribs)(void * thisHandle, CorInfoException** ppException, void* ftn, int attribs); - void (__stdcall * getMethodSig)(void * thisHandle, CorInfoException** ppException, void* ftn, void* sig, void* memberParent); - bool (__stdcall * getMethodInfo)(void * thisHandle, CorInfoException** ppException, void* ftn, void* info); - int (__stdcall * canInline)(void * thisHandle, CorInfoException** ppException, void* callerHnd, void* calleeHnd, unsigned int* pRestrictions); - void (__stdcall * reportInliningDecision)(void * thisHandle, CorInfoException** ppException, void* inlinerHnd, void* inlineeHnd, int inlineResult, const char* reason); - bool (__stdcall * canTailCall)(void * thisHandle, CorInfoException** ppException, void* callerHnd, void* declaredCalleeHnd, void* exactCalleeHnd, bool fIsTailPrefix); - void (__stdcall * reportTailCallDecision)(void * thisHandle, CorInfoException** ppException, void* callerHnd, void* calleeHnd, bool fIsTailPrefix, int tailCallResult, const char* reason); - void (__stdcall * getEHinfo)(void * thisHandle, CorInfoException** ppException, void* ftn, unsigned EHnumber, void* clause); - void* (__stdcall * getMethodClass)(void * thisHandle, CorInfoException** ppException, void* method); - void* (__stdcall * getMethodModule)(void * thisHandle, CorInfoException** ppException, void* method); - void (__stdcall * getMethodVTableOffset)(void * thisHandle, CorInfoException** ppException, void* method, unsigned* offsetOfIndirection, unsigned* offsetAfterIndirection); - void* (__stdcall * resolveVirtualMethod)(void * thisHandle, CorInfoException** ppException, void* virtualMethod, void* implementingClass, void* ownerType); - int (__stdcall * getIntrinsicID)(void * thisHandle, CorInfoException** ppException, void* method, bool* pMustExpand); - bool (__stdcall * isInSIMDModule)(void * thisHandle, CorInfoException** ppException, void* classHnd); - int (__stdcall * getUnmanagedCallConv)(void * thisHandle, CorInfoException** ppException, void* method); - bool (__stdcall * pInvokeMarshalingRequired)(void * thisHandle, CorInfoException** ppException, void* method, void* callSiteSig); - bool (__stdcall * satisfiesMethodConstraints)(void * thisHandle, CorInfoException** ppException, void* parent, void* method); - bool (__stdcall * isCompatibleDelegate)(void * thisHandle, CorInfoException** ppException, void* objCls, void* methodParentCls, void* method, void* delegateCls, int* pfIsOpenDelegate); - bool (__stdcall * isDelegateCreationAllowed)(void * thisHandle, CorInfoException** ppException, void* delegateHnd, void* calleeHnd); - int (__stdcall * isInstantiationOfVerifiedGeneric)(void * thisHandle, CorInfoException** ppException, void* method); - void (__stdcall * initConstraintsForVerification)(void * thisHandle, CorInfoException** ppException, void* method, int* pfHasCircularClassConstraints, int* pfHasCircularMethodConstraint); - int (__stdcall * canSkipMethodVerification)(void * thisHandle, CorInfoException** ppException, void* ftnHandle); - void (__stdcall * methodMustBeLoadedBeforeCodeIsRun)(void * thisHandle, CorInfoException** ppException, void* method); - void* (__stdcall * mapMethodDeclToMethodImpl)(void * thisHandle, CorInfoException** ppException, void* method); - void (__stdcall * getGSCookie)(void * thisHandle, CorInfoException** ppException, void* pCookieVal, void** ppCookieVal); - void (__stdcall * resolveToken)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken); - void (__stdcall * tryResolveToken)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken); - void (__stdcall * findSig)(void * thisHandle, CorInfoException** ppException, void* module, unsigned sigTOK, void* context, void* sig); - void (__stdcall * findCallSiteSig)(void * thisHandle, CorInfoException** ppException, void* module, unsigned methTOK, void* context, void* sig); - void* (__stdcall * getTokenTypeAsHandle)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken); - int (__stdcall * canSkipVerification)(void * thisHandle, CorInfoException** ppException, void* module); - bool (__stdcall * isValidToken)(void * thisHandle, CorInfoException** ppException, void* module, unsigned metaTOK); - bool (__stdcall * isValidStringRef)(void * thisHandle, CorInfoException** ppException, void* module, unsigned metaTOK); - bool (__stdcall * shouldEnforceCallvirtRestriction)(void * thisHandle, CorInfoException** ppException, void* scope); - int (__stdcall * asCorInfoType)(void * thisHandle, CorInfoException** ppException, void* cls); - const char* (__stdcall * getClassName)(void * thisHandle, CorInfoException** ppException, void* cls); - int (__stdcall * appendClassName)(void * thisHandle, CorInfoException** ppException, wchar_t** ppBuf, int* pnBufLen, void* cls, bool fNamespace, bool fFullInst, bool fAssembly); - bool (__stdcall * isValueClass)(void * thisHandle, CorInfoException** ppException, void* cls); - bool (__stdcall * canInlineTypeCheckWithObjectVTable)(void * thisHandle, CorInfoException** ppException, void* cls); - unsigned int (__stdcall * getClassAttribs)(void * thisHandle, CorInfoException** ppException, void* cls); - bool (__stdcall * isStructRequiringStackAllocRetBuf)(void * thisHandle, CorInfoException** ppException, void* cls); - void* (__stdcall * getClassModule)(void * thisHandle, CorInfoException** ppException, void* cls); - void* (__stdcall * getModuleAssembly)(void * thisHandle, CorInfoException** ppException, void* mod); - const char* (__stdcall * getAssemblyName)(void * thisHandle, CorInfoException** ppException, void* assem); - void* (__stdcall * LongLifetimeMalloc)(void * thisHandle, CorInfoException** ppException, size_t sz); - void (__stdcall * LongLifetimeFree)(void * thisHandle, CorInfoException** ppException, void* obj); - size_t (__stdcall * getClassModuleIdForStatics)(void * thisHandle, CorInfoException** ppException, void* cls, void* pModule, void** ppIndirection); - unsigned (__stdcall * getClassSize)(void * thisHandle, CorInfoException** ppException, void* cls); - unsigned (__stdcall * getClassAlignmentRequirement)(void * thisHandle, CorInfoException** ppException, void* cls, bool fDoubleAlignHint); - unsigned (__stdcall * getClassGClayout)(void * thisHandle, CorInfoException** ppException, void* cls, unsigned char* gcPtrs); - unsigned (__stdcall * getClassNumInstanceFields)(void * thisHandle, CorInfoException** ppException, void* cls); - void* (__stdcall * getFieldInClass)(void * thisHandle, CorInfoException** ppException, void* clsHnd, int num); - bool (__stdcall * checkMethodModifier)(void * thisHandle, CorInfoException** ppException, void* hMethod, const char* modifier, bool fOptional); - int (__stdcall * getNewHelper)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken, void* callerHandle); - int (__stdcall * getNewArrHelper)(void * thisHandle, CorInfoException** ppException, void* arrayCls); - int (__stdcall * getCastingHelper)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken, bool fThrowing); - int (__stdcall * getSharedCCtorHelper)(void * thisHandle, CorInfoException** ppException, void* clsHnd); - int (__stdcall * getSecurityPrologHelper)(void * thisHandle, CorInfoException** ppException, void* ftn); - void* (__stdcall * getTypeForBox)(void * thisHandle, CorInfoException** ppException, void* cls); - int (__stdcall * getBoxHelper)(void * thisHandle, CorInfoException** ppException, void* cls); - int (__stdcall * getUnBoxHelper)(void * thisHandle, CorInfoException** ppException, void* cls); - bool (__stdcall * getReadyToRunHelper)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken, void* pGenericLookupKind, int id, void* pLookup); - void (__stdcall * getReadyToRunDelegateCtorHelper)(void * thisHandle, CorInfoException** ppException, void* pTargetMethod, void* delegateType, void* pLookup); - const char* (__stdcall * getHelperName)(void * thisHandle, CorInfoException** ppException, int helpFunc); - int (__stdcall * initClass)(void * thisHandle, CorInfoException** ppException, void* field, void* method, void* context, bool speculative); - void (__stdcall * classMustBeLoadedBeforeCodeIsRun)(void * thisHandle, CorInfoException** ppException, void* cls); - void* (__stdcall * getBuiltinClass)(void * thisHandle, CorInfoException** ppException, int classId); - int (__stdcall * getTypeForPrimitiveValueClass)(void * thisHandle, CorInfoException** ppException, void* cls); - bool (__stdcall * canCast)(void * thisHandle, CorInfoException** ppException, void* child, void* parent); - bool (__stdcall * areTypesEquivalent)(void * thisHandle, CorInfoException** ppException, void* cls1, void* cls2); - void* (__stdcall * mergeClasses)(void * thisHandle, CorInfoException** ppException, void* cls1, void* cls2); - void* (__stdcall * getParentType)(void * thisHandle, CorInfoException** ppException, void* cls); - int (__stdcall * getChildType)(void * thisHandle, CorInfoException** ppException, void* clsHnd, void* clsRet); - bool (__stdcall * satisfiesClassConstraints)(void * thisHandle, CorInfoException** ppException, void* cls); - bool (__stdcall * isSDArray)(void * thisHandle, CorInfoException** ppException, void* cls); - unsigned (__stdcall * getArrayRank)(void * thisHandle, CorInfoException** ppException, void* cls); - void* (__stdcall * getArrayInitializationData)(void * thisHandle, CorInfoException** ppException, void* field, unsigned int size); - int (__stdcall * canAccessClass)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken, void* callerHandle, void* pAccessHelper); - const char* (__stdcall * getFieldName)(void * thisHandle, CorInfoException** ppException, void* ftn, const char** moduleName); - void* (__stdcall * getFieldClass)(void * thisHandle, CorInfoException** ppException, void* field); - int (__stdcall * getFieldType)(void * thisHandle, CorInfoException** ppException, void* field, void* structType, void* memberParent); - unsigned (__stdcall * getFieldOffset)(void * thisHandle, CorInfoException** ppException, void* field); - bool (__stdcall * isWriteBarrierHelperRequired)(void * thisHandle, CorInfoException** ppException, void* field); - void (__stdcall * getFieldInfo)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken, void* callerHandle, int flags, void* pResult); - bool (__stdcall * isFieldStatic)(void * thisHandle, CorInfoException** ppException, void* fldHnd); - void (__stdcall * getBoundaries)(void * thisHandle, CorInfoException** ppException, void* ftn, unsigned int* cILOffsets, unsigned int** pILOffsets, void* implictBoundaries); - void (__stdcall * setBoundaries)(void * thisHandle, CorInfoException** ppException, void* ftn, unsigned int cMap, void* pMap); - void (__stdcall * getVars)(void * thisHandle, CorInfoException** ppException, void* ftn, unsigned int* cVars, void* vars, bool* extendOthers); - void (__stdcall * setVars)(void * thisHandle, CorInfoException** ppException, void* ftn, unsigned int cVars, void* vars); - void* (__stdcall * allocateArray)(void * thisHandle, CorInfoException** ppException, unsigned int cBytes); - void (__stdcall * freeArray)(void * thisHandle, CorInfoException** ppException, void* array); - void* (__stdcall * getArgNext)(void * thisHandle, CorInfoException** ppException, void* args); - int (__stdcall * getArgType)(void * thisHandle, CorInfoException** ppException, void* sig, void* args, void* vcTypeRet); - void* (__stdcall * getArgClass)(void * thisHandle, CorInfoException** ppException, void* sig, void* args); - int (__stdcall * getHFAType)(void * thisHandle, CorInfoException** ppException, void* hClass); - int (__stdcall * GetErrorHRESULT)(void * thisHandle, CorInfoException** ppException, void* pExceptionPointers); - unsigned int (__stdcall * GetErrorMessage)(void * thisHandle, CorInfoException** ppException, wchar_t* buffer, unsigned int bufferLength); - int (__stdcall * FilterException)(void * thisHandle, CorInfoException** ppException, void* pExceptionPointers); - void (__stdcall * HandleException)(void * thisHandle, CorInfoException** ppException, void* pExceptionPointers); - void (__stdcall * ThrowExceptionForJitResult)(void * thisHandle, CorInfoException** ppException, int result); - void (__stdcall * ThrowExceptionForHelper)(void * thisHandle, CorInfoException** ppException, const void* throwHelper); - bool (__stdcall * runWithErrorTrap)(void * thisHandle, CorInfoException** ppException, void* function, void* parameter); - void (__stdcall * getEEInfo)(void * thisHandle, CorInfoException** ppException, void* pEEInfoOut); - const wchar_t* (__stdcall * getJitTimeLogFilename)(void * thisHandle, CorInfoException** ppException); - unsigned int (__stdcall * getMethodDefFromMethod)(void * thisHandle, CorInfoException** ppException, void* hMethod); - const char* (__stdcall * getMethodName)(void * thisHandle, CorInfoException** ppException, void* ftn, const char** moduleName); - unsigned (__stdcall * getMethodHash)(void * thisHandle, CorInfoException** ppException, void* ftn); - size_t (__stdcall * findNameOfToken)(void * thisHandle, CorInfoException** ppException, void* moduleHandle, unsigned int token, char* szFQName, size_t FQNameCapacity); - bool (__stdcall * getSystemVAmd64PassStructInRegisterDescriptor)(void * thisHandle, CorInfoException** ppException, void* structHnd, void* structPassInRegDescPtr); - unsigned int (__stdcall * getThreadTLSIndex)(void * thisHandle, CorInfoException** ppException, void** ppIndirection); - const void* (__stdcall * getInlinedCallFrameVptr)(void * thisHandle, CorInfoException** ppException, void** ppIndirection); - long* (__stdcall * getAddrOfCaptureThreadGlobal)(void * thisHandle, CorInfoException** ppException, void** ppIndirection); - size_t* (__stdcall * getAddrModuleDomainID)(void * thisHandle, CorInfoException** ppException, void* module); - void* (__stdcall * getHelperFtn)(void * thisHandle, CorInfoException** ppException, int ftnNum, void** ppIndirection); - void (__stdcall * getFunctionEntryPoint)(void * thisHandle, CorInfoException** ppException, void* ftn, void* pResult, int accessFlags); - void (__stdcall * getFunctionFixedEntryPoint)(void * thisHandle, CorInfoException** ppException, void* ftn, void* pResult); - void* (__stdcall * getMethodSync)(void * thisHandle, CorInfoException** ppException, void* ftn, void** ppIndirection); - int (__stdcall * getLazyStringLiteralHelper)(void * thisHandle, CorInfoException** ppException, void* handle); - void* (__stdcall * embedModuleHandle)(void * thisHandle, CorInfoException** ppException, void* handle, void** ppIndirection); - void* (__stdcall * embedClassHandle)(void * thisHandle, CorInfoException** ppException, void* handle, void** ppIndirection); - void* (__stdcall * embedMethodHandle)(void * thisHandle, CorInfoException** ppException, void* handle, void** ppIndirection); - void* (__stdcall * embedFieldHandle)(void * thisHandle, CorInfoException** ppException, void* handle, void** ppIndirection); - void (__stdcall * embedGenericHandle)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken, bool fEmbedParent, void* pResult); - void (__stdcall * getLocationOfThisType)(void * thisHandle, CorInfoException** ppException, CORINFO_LOOKUP_KIND* _return, void* context); - void* (__stdcall * getPInvokeUnmanagedTarget)(void * thisHandle, CorInfoException** ppException, void* method, void** ppIndirection); - void* (__stdcall * getAddressOfPInvokeFixup)(void * thisHandle, CorInfoException** ppException, void* method, void** ppIndirection); - void (__stdcall * getAddressOfPInvokeTarget)(void * thisHandle, CorInfoException** ppException, void* method, void* pLookup); - void* (__stdcall * GetCookieForPInvokeCalliSig)(void * thisHandle, CorInfoException** ppException, void* szMetaSig, void** ppIndirection); - bool (__stdcall * canGetCookieForPInvokeCalliSig)(void * thisHandle, CorInfoException** ppException, void* szMetaSig); - void* (__stdcall * getJustMyCodeHandle)(void * thisHandle, CorInfoException** ppException, void* method, void** ppIndirection); - void (__stdcall * GetProfilingHandle)(void * thisHandle, CorInfoException** ppException, int* pbHookFunction, void** pProfilerHandle, int* pbIndirectedHandles); - void (__stdcall * getCallInfo)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken, void* pConstrainedResolvedToken, void* callerHandle, int flags, void* pResult); - bool (__stdcall * canAccessFamily)(void * thisHandle, CorInfoException** ppException, void* hCaller, void* hInstanceType); - bool (__stdcall * isRIDClassDomainID)(void * thisHandle, CorInfoException** ppException, void* cls); - unsigned (__stdcall * getClassDomainID)(void * thisHandle, CorInfoException** ppException, void* cls, void** ppIndirection); - void* (__stdcall * getFieldAddress)(void * thisHandle, CorInfoException** ppException, void* field, void** ppIndirection); - void* (__stdcall * getVarArgsHandle)(void * thisHandle, CorInfoException** ppException, void* pSig, void** ppIndirection); - bool (__stdcall * canGetVarArgsHandle)(void * thisHandle, CorInfoException** ppException, void* pSig); - int (__stdcall * constructStringLiteral)(void * thisHandle, CorInfoException** ppException, void* module, unsigned int metaTok, void** ppValue); - int (__stdcall * emptyStringLiteral)(void * thisHandle, CorInfoException** ppException, void** ppValue); - unsigned int (__stdcall * getFieldThreadLocalStoreID)(void * thisHandle, CorInfoException** ppException, void* field, void** ppIndirection); - void (__stdcall * setOverride)(void * thisHandle, CorInfoException** ppException, void* pOverride, void* currentMethod); - void (__stdcall * addActiveDependency)(void * thisHandle, CorInfoException** ppException, void* moduleFrom, void* moduleTo); - void* (__stdcall * GetDelegateCtor)(void * thisHandle, CorInfoException** ppException, void* methHnd, void* clsHnd, void* targetMethodHnd, void* pCtorData); - void (__stdcall * MethodCompileComplete)(void * thisHandle, CorInfoException** ppException, void* methHnd); - void* (__stdcall * getTailCallCopyArgsThunk)(void * thisHandle, CorInfoException** ppException, void* pSig, int flags); - void* (__stdcall * getMemoryManager)(void * thisHandle, CorInfoException** ppException); - void (__stdcall * allocMem)(void * thisHandle, CorInfoException** ppException, unsigned int hotCodeSize, unsigned int coldCodeSize, unsigned int roDataSize, unsigned int xcptnsCount, int flag, void** hotCodeBlock, void** coldCodeBlock, void** roDataBlock); - void (__stdcall * reserveUnwindInfo)(void * thisHandle, CorInfoException** ppException, bool isFunclet, bool isColdCode, unsigned int unwindSize); - void (__stdcall * allocUnwindInfo)(void * thisHandle, CorInfoException** ppException, unsigned char* pHotCode, unsigned char* pColdCode, unsigned int startOffset, unsigned int endOffset, unsigned int unwindSize, unsigned char* pUnwindBlock, int funcKind); - void* (__stdcall * allocGCInfo)(void * thisHandle, CorInfoException** ppException, size_t size); - void (__stdcall * yieldExecution)(void * thisHandle, CorInfoException** ppException); - void (__stdcall * setEHcount)(void * thisHandle, CorInfoException** ppException, unsigned cEH); - void (__stdcall * setEHinfo)(void * thisHandle, CorInfoException** ppException, unsigned EHnumber, void* clause); - bool (__stdcall * logMsg)(void * thisHandle, CorInfoException** ppException, unsigned level, const char* fmt, va_list args); - int (__stdcall * doAssert)(void * thisHandle, CorInfoException** ppException, const char* szFile, int iLine, const char* szExpr); - void (__stdcall * reportFatalError)(void * thisHandle, CorInfoException** ppException, int result); - int (__stdcall * allocBBProfileBuffer)(void * thisHandle, CorInfoException** ppException, unsigned int count, void** profileBuffer); - int (__stdcall * getBBProfileData)(void * thisHandle, CorInfoException** ppException, void* ftnHnd, unsigned long* count, void** profileBuffer, unsigned long* numRuns); - void (__stdcall * recordCallSite)(void * thisHandle, CorInfoException** ppException, unsigned int instrOffset, void* callSig, void* methodHandle); - void (__stdcall * recordRelocation)(void * thisHandle, CorInfoException** ppException, void* location, void* target, unsigned short fRelocType, unsigned short slotNum, int addlDelta); - unsigned short (__stdcall * getRelocTypeHint)(void * thisHandle, CorInfoException** ppException, void* target); - void (__stdcall * getModuleNativeEntryPointRange)(void * thisHandle, CorInfoException** ppException, void** pStart, void** pEnd); - unsigned int (__stdcall * getExpectedTargetArchitecture)(void * thisHandle, CorInfoException** ppException); - unsigned int (__stdcall * getJitFlags)(void * thisHandle, CorInfoException** ppException, void* flags, unsigned int sizeInBytes); + unsigned int (* getMethodAttribs)(void * thisHandle, CorInfoException** ppException, void* ftn); + void (* setMethodAttribs)(void * thisHandle, CorInfoException** ppException, void* ftn, int attribs); + void (* getMethodSig)(void * thisHandle, CorInfoException** ppException, void* ftn, void* sig, void* memberParent); + bool (* getMethodInfo)(void * thisHandle, CorInfoException** ppException, void* ftn, void* info); + int (* canInline)(void * thisHandle, CorInfoException** ppException, void* callerHnd, void* calleeHnd, unsigned int* pRestrictions); + void (* reportInliningDecision)(void * thisHandle, CorInfoException** ppException, void* inlinerHnd, void* inlineeHnd, int inlineResult, const char* reason); + bool (* canTailCall)(void * thisHandle, CorInfoException** ppException, void* callerHnd, void* declaredCalleeHnd, void* exactCalleeHnd, bool fIsTailPrefix); + void (* reportTailCallDecision)(void * thisHandle, CorInfoException** ppException, void* callerHnd, void* calleeHnd, bool fIsTailPrefix, int tailCallResult, const char* reason); + void (* getEHinfo)(void * thisHandle, CorInfoException** ppException, void* ftn, unsigned EHnumber, void* clause); + void* (* getMethodClass)(void * thisHandle, CorInfoException** ppException, void* method); + void* (* getMethodModule)(void * thisHandle, CorInfoException** ppException, void* method); + void (* getMethodVTableOffset)(void * thisHandle, CorInfoException** ppException, void* method, unsigned* offsetOfIndirection, unsigned* offsetAfterIndirection, bool* isRelative); + void* (* resolveVirtualMethod)(void * thisHandle, CorInfoException** ppException, void* virtualMethod, void* implementingClass, void* ownerType); + void* (* getUnboxedEntry)(void * thisHandle, CorInfoException** ppException, void* ftn, bool* requiresInstMethodTableArg); + void* (* getDefaultEqualityComparerClass)(void * thisHandle, CorInfoException** ppException, void* elemType); + void (* expandRawHandleIntrinsic)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken, void* pResult); + int (* getIntrinsicID)(void * thisHandle, CorInfoException** ppException, void* method, bool* pMustExpand); + bool (* isInSIMDModule)(void * thisHandle, CorInfoException** ppException, void* classHnd); + int (* getUnmanagedCallConv)(void * thisHandle, CorInfoException** ppException, void* method); + int (* pInvokeMarshalingRequired)(void * thisHandle, CorInfoException** ppException, void* method, void* callSiteSig); + int (* satisfiesMethodConstraints)(void * thisHandle, CorInfoException** ppException, void* parent, void* method); + int (* isCompatibleDelegate)(void * thisHandle, CorInfoException** ppException, void* objCls, void* methodParentCls, void* method, void* delegateCls, int* pfIsOpenDelegate); + int (* isInstantiationOfVerifiedGeneric)(void * thisHandle, CorInfoException** ppException, void* method); + void (* initConstraintsForVerification)(void * thisHandle, CorInfoException** ppException, void* method, int* pfHasCircularClassConstraints, int* pfHasCircularMethodConstraint); + int (* canSkipMethodVerification)(void * thisHandle, CorInfoException** ppException, void* ftnHandle); + void (* methodMustBeLoadedBeforeCodeIsRun)(void * thisHandle, CorInfoException** ppException, void* method); + void* (* mapMethodDeclToMethodImpl)(void * thisHandle, CorInfoException** ppException, void* method); + void (* getGSCookie)(void * thisHandle, CorInfoException** ppException, void* pCookieVal, void** ppCookieVal); + void (* resolveToken)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken); + void (* tryResolveToken)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken); + void (* findSig)(void * thisHandle, CorInfoException** ppException, void* module, unsigned sigTOK, void* context, void* sig); + void (* findCallSiteSig)(void * thisHandle, CorInfoException** ppException, void* module, unsigned methTOK, void* context, void* sig); + void* (* getTokenTypeAsHandle)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken); + int (* canSkipVerification)(void * thisHandle, CorInfoException** ppException, void* module); + int (* isValidToken)(void * thisHandle, CorInfoException** ppException, void* module, unsigned metaTOK); + int (* isValidStringRef)(void * thisHandle, CorInfoException** ppException, void* module, unsigned metaTOK); + int (* shouldEnforceCallvirtRestriction)(void * thisHandle, CorInfoException** ppException, void* scope); + int (* asCorInfoType)(void * thisHandle, CorInfoException** ppException, void* cls); + const char* (* getClassName)(void * thisHandle, CorInfoException** ppException, void* cls); + const char* (* getClassNameFromMetadata)(void * thisHandle, CorInfoException** ppException, void* cls, const char** namespaceName); + void* (* getTypeInstantiationArgument)(void * thisHandle, CorInfoException** ppException, void* cls, unsigned index); + int (* appendClassName)(void * thisHandle, CorInfoException** ppException, wchar_t** ppBuf, int* pnBufLen, void* cls, int fNamespace, int fFullInst, int fAssembly); + int (* isValueClass)(void * thisHandle, CorInfoException** ppException, void* cls); + int (* canInlineTypeCheckWithObjectVTable)(void * thisHandle, CorInfoException** ppException, void* cls); + unsigned int (* getClassAttribs)(void * thisHandle, CorInfoException** ppException, void* cls); + int (* isStructRequiringStackAllocRetBuf)(void * thisHandle, CorInfoException** ppException, void* cls); + void* (* getClassModule)(void * thisHandle, CorInfoException** ppException, void* cls); + void* (* getModuleAssembly)(void * thisHandle, CorInfoException** ppException, void* mod); + const char* (* getAssemblyName)(void * thisHandle, CorInfoException** ppException, void* assem); + void* (* LongLifetimeMalloc)(void * thisHandle, CorInfoException** ppException, size_t sz); + void (* LongLifetimeFree)(void * thisHandle, CorInfoException** ppException, void* obj); + size_t (* getClassModuleIdForStatics)(void * thisHandle, CorInfoException** ppException, void* cls, void* pModule, void** ppIndirection); + unsigned (* getClassSize)(void * thisHandle, CorInfoException** ppException, void* cls); + unsigned (* getClassAlignmentRequirement)(void * thisHandle, CorInfoException** ppException, void* cls, int fDoubleAlignHint); + unsigned (* getClassGClayout)(void * thisHandle, CorInfoException** ppException, void* cls, unsigned char* gcPtrs); + unsigned (* getClassNumInstanceFields)(void * thisHandle, CorInfoException** ppException, void* cls); + void* (* getFieldInClass)(void * thisHandle, CorInfoException** ppException, void* clsHnd, int num); + int (* checkMethodModifier)(void * thisHandle, CorInfoException** ppException, void* hMethod, const char* modifier, int fOptional); + int (* getNewHelper)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken, void* callerHandle); + int (* getNewArrHelper)(void * thisHandle, CorInfoException** ppException, void* arrayCls); + int (* getCastingHelper)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken, bool fThrowing); + int (* getSharedCCtorHelper)(void * thisHandle, CorInfoException** ppException, void* clsHnd); + int (* getSecurityPrologHelper)(void * thisHandle, CorInfoException** ppException, void* ftn); + void* (* getTypeForBox)(void * thisHandle, CorInfoException** ppException, void* cls); + int (* getBoxHelper)(void * thisHandle, CorInfoException** ppException, void* cls); + int (* getUnBoxHelper)(void * thisHandle, CorInfoException** ppException, void* cls); + bool (* getReadyToRunHelper)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken, void* pGenericLookupKind, int id, void* pLookup); + void (* getReadyToRunDelegateCtorHelper)(void * thisHandle, CorInfoException** ppException, void* pTargetMethod, void* delegateType, void* pLookup); + const char* (* getHelperName)(void * thisHandle, CorInfoException** ppException, int helpFunc); + int (* initClass)(void * thisHandle, CorInfoException** ppException, void* field, void* method, void* context, int speculative); + void (* classMustBeLoadedBeforeCodeIsRun)(void * thisHandle, CorInfoException** ppException, void* cls); + void* (* getBuiltinClass)(void * thisHandle, CorInfoException** ppException, int classId); + int (* getTypeForPrimitiveValueClass)(void * thisHandle, CorInfoException** ppException, void* cls); + int (* getTypeForPrimitiveNumericClass)(void * thisHandle, CorInfoException** ppException, void* cls); + int (* canCast)(void * thisHandle, CorInfoException** ppException, void* child, void* parent); + int (* areTypesEquivalent)(void * thisHandle, CorInfoException** ppException, void* cls1, void* cls2); + int (* compareTypesForCast)(void * thisHandle, CorInfoException** ppException, void* fromClass, void* toClass); + int (* compareTypesForEquality)(void * thisHandle, CorInfoException** ppException, void* cls1, void* cls2); + void* (* mergeClasses)(void * thisHandle, CorInfoException** ppException, void* cls1, void* cls2); + void* (* getParentType)(void * thisHandle, CorInfoException** ppException, void* cls); + int (* getChildType)(void * thisHandle, CorInfoException** ppException, void* clsHnd, void* clsRet); + int (* satisfiesClassConstraints)(void * thisHandle, CorInfoException** ppException, void* cls); + int (* isSDArray)(void * thisHandle, CorInfoException** ppException, void* cls); + unsigned (* getArrayRank)(void * thisHandle, CorInfoException** ppException, void* cls); + void* (* getArrayInitializationData)(void * thisHandle, CorInfoException** ppException, void* field, unsigned int size); + int (* canAccessClass)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken, void* callerHandle, void* pAccessHelper); + const char* (* getFieldName)(void * thisHandle, CorInfoException** ppException, void* ftn, const char** moduleName); + void* (* getFieldClass)(void * thisHandle, CorInfoException** ppException, void* field); + int (* getFieldType)(void * thisHandle, CorInfoException** ppException, void* field, void* structType, void* memberParent); + unsigned (* getFieldOffset)(void * thisHandle, CorInfoException** ppException, void* field); + bool (* isWriteBarrierHelperRequired)(void * thisHandle, CorInfoException** ppException, void* field); + void (* getFieldInfo)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken, void* callerHandle, int flags, void* pResult); + bool (* isFieldStatic)(void * thisHandle, CorInfoException** ppException, void* fldHnd); + void (* getBoundaries)(void * thisHandle, CorInfoException** ppException, void* ftn, unsigned int* cILOffsets, unsigned int** pILOffsets, void* implictBoundaries); + void (* setBoundaries)(void * thisHandle, CorInfoException** ppException, void* ftn, unsigned int cMap, void* pMap); + void (* getVars)(void * thisHandle, CorInfoException** ppException, void* ftn, unsigned int* cVars, void* vars, bool* extendOthers); + void (* setVars)(void * thisHandle, CorInfoException** ppException, void* ftn, unsigned int cVars, void* vars); + void* (* allocateArray)(void * thisHandle, CorInfoException** ppException, unsigned int cBytes); + void (* freeArray)(void * thisHandle, CorInfoException** ppException, void* array); + void* (* getArgNext)(void * thisHandle, CorInfoException** ppException, void* args); + int (* getArgType)(void * thisHandle, CorInfoException** ppException, void* sig, void* args, void* vcTypeRet); + void* (* getArgClass)(void * thisHandle, CorInfoException** ppException, void* sig, void* args); + int (* getHFAType)(void * thisHandle, CorInfoException** ppException, void* hClass); + int (* GetErrorHRESULT)(void * thisHandle, CorInfoException** ppException, void* pExceptionPointers); + unsigned int (* GetErrorMessage)(void * thisHandle, CorInfoException** ppException, wchar_t* buffer, unsigned int bufferLength); + int (* FilterException)(void * thisHandle, CorInfoException** ppException, void* pExceptionPointers); + void (* HandleException)(void * thisHandle, CorInfoException** ppException, void* pExceptionPointers); + void (* ThrowExceptionForJitResult)(void * thisHandle, CorInfoException** ppException, int result); + void (* ThrowExceptionForHelper)(void * thisHandle, CorInfoException** ppException, const void* throwHelper); + bool (* runWithErrorTrap)(void * thisHandle, CorInfoException** ppException, void* function, void* parameter); + void (* getEEInfo)(void * thisHandle, CorInfoException** ppException, void* pEEInfoOut); + const wchar_t* (* getJitTimeLogFilename)(void * thisHandle, CorInfoException** ppException); + unsigned int (* getMethodDefFromMethod)(void * thisHandle, CorInfoException** ppException, void* hMethod); + const char* (* getMethodName)(void * thisHandle, CorInfoException** ppException, void* ftn, const char** moduleName); + const char* (* getMethodNameFromMetadata)(void * thisHandle, CorInfoException** ppException, void* ftn, const char** className, const char** namespaceName); + unsigned (* getMethodHash)(void * thisHandle, CorInfoException** ppException, void* ftn); + size_t (* findNameOfToken)(void * thisHandle, CorInfoException** ppException, void* moduleHandle, unsigned int token, char* szFQName, size_t FQNameCapacity); + bool (* getSystemVAmd64PassStructInRegisterDescriptor)(void * thisHandle, CorInfoException** ppException, void* structHnd, void* structPassInRegDescPtr); + unsigned int (* getThreadTLSIndex)(void * thisHandle, CorInfoException** ppException, void** ppIndirection); + const void* (* getInlinedCallFrameVptr)(void * thisHandle, CorInfoException** ppException, void** ppIndirection); + long* (* getAddrOfCaptureThreadGlobal)(void * thisHandle, CorInfoException** ppException, void** ppIndirection); + void* (* getHelperFtn)(void * thisHandle, CorInfoException** ppException, int ftnNum, void** ppIndirection); + void (* getFunctionEntryPoint)(void * thisHandle, CorInfoException** ppException, void* ftn, void* pResult, int accessFlags); + void (* getFunctionFixedEntryPoint)(void * thisHandle, CorInfoException** ppException, void* ftn, void* pResult); + void* (* getMethodSync)(void * thisHandle, CorInfoException** ppException, void* ftn, void** ppIndirection); + int (* getLazyStringLiteralHelper)(void * thisHandle, CorInfoException** ppException, void* handle); + void* (* embedModuleHandle)(void * thisHandle, CorInfoException** ppException, void* handle, void** ppIndirection); + void* (* embedClassHandle)(void * thisHandle, CorInfoException** ppException, void* handle, void** ppIndirection); + void* (* embedMethodHandle)(void * thisHandle, CorInfoException** ppException, void* handle, void** ppIndirection); + void* (* embedFieldHandle)(void * thisHandle, CorInfoException** ppException, void* handle, void** ppIndirection); + void (* embedGenericHandle)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken, int fEmbedParent, void* pResult); + void (* getLocationOfThisType)(void * thisHandle, CorInfoException** ppException, CORINFO_LOOKUP_KIND* _return, void* context); + void* (* getPInvokeUnmanagedTarget)(void * thisHandle, CorInfoException** ppException, void* method, void** ppIndirection); + void* (* getAddressOfPInvokeFixup)(void * thisHandle, CorInfoException** ppException, void* method, void** ppIndirection); + void (* getAddressOfPInvokeTarget)(void * thisHandle, CorInfoException** ppException, void* method, void* pLookup); + void* (* GetCookieForPInvokeCalliSig)(void * thisHandle, CorInfoException** ppException, void* szMetaSig, void** ppIndirection); + bool (* canGetCookieForPInvokeCalliSig)(void * thisHandle, CorInfoException** ppException, void* szMetaSig); + void* (* getJustMyCodeHandle)(void * thisHandle, CorInfoException** ppException, void* method, void** ppIndirection); + void (* GetProfilingHandle)(void * thisHandle, CorInfoException** ppException, int* pbHookFunction, void** pProfilerHandle, int* pbIndirectedHandles); + void (* getCallInfo)(void * thisHandle, CorInfoException** ppException, void* pResolvedToken, void* pConstrainedResolvedToken, void* callerHandle, int flags, void* pResult); + int (* canAccessFamily)(void * thisHandle, CorInfoException** ppException, void* hCaller, void* hInstanceType); + int (* isRIDClassDomainID)(void * thisHandle, CorInfoException** ppException, void* cls); + unsigned (* getClassDomainID)(void * thisHandle, CorInfoException** ppException, void* cls, void** ppIndirection); + void* (* getFieldAddress)(void * thisHandle, CorInfoException** ppException, void* field, void** ppIndirection); + void* (* getVarArgsHandle)(void * thisHandle, CorInfoException** ppException, void* pSig, void** ppIndirection); + bool (* canGetVarArgsHandle)(void * thisHandle, CorInfoException** ppException, void* pSig); + int (* constructStringLiteral)(void * thisHandle, CorInfoException** ppException, void* module, unsigned int metaTok, void** ppValue); + int (* emptyStringLiteral)(void * thisHandle, CorInfoException** ppException, void** ppValue); + unsigned int (* getFieldThreadLocalStoreID)(void * thisHandle, CorInfoException** ppException, void* field, void** ppIndirection); + void (* setOverride)(void * thisHandle, CorInfoException** ppException, void* pOverride, void* currentMethod); + void (* addActiveDependency)(void * thisHandle, CorInfoException** ppException, void* moduleFrom, void* moduleTo); + void* (* GetDelegateCtor)(void * thisHandle, CorInfoException** ppException, void* methHnd, void* clsHnd, void* targetMethodHnd, void* pCtorData); + void (* MethodCompileComplete)(void * thisHandle, CorInfoException** ppException, void* methHnd); + void* (* getTailCallCopyArgsThunk)(void * thisHandle, CorInfoException** ppException, void* pSig, int flags); + void* (* getMemoryManager)(void * thisHandle, CorInfoException** ppException); + void (* allocMem)(void * thisHandle, CorInfoException** ppException, unsigned int hotCodeSize, unsigned int coldCodeSize, unsigned int roDataSize, unsigned int xcptnsCount, int flag, void** hotCodeBlock, void** coldCodeBlock, void** roDataBlock); + void (* reserveUnwindInfo)(void * thisHandle, CorInfoException** ppException, int isFunclet, int isColdCode, unsigned int unwindSize); + void (* allocUnwindInfo)(void * thisHandle, CorInfoException** ppException, unsigned char* pHotCode, unsigned char* pColdCode, unsigned int startOffset, unsigned int endOffset, unsigned int unwindSize, unsigned char* pUnwindBlock, int funcKind); + void* (* allocGCInfo)(void * thisHandle, CorInfoException** ppException, size_t size); + void (* yieldExecution)(void * thisHandle, CorInfoException** ppException); + void (* setEHcount)(void * thisHandle, CorInfoException** ppException, unsigned cEH); + void (* setEHinfo)(void * thisHandle, CorInfoException** ppException, unsigned EHnumber, void* clause); + int (* logMsg)(void * thisHandle, CorInfoException** ppException, unsigned level, const char* fmt, va_list args); + int (* doAssert)(void * thisHandle, CorInfoException** ppException, const char* szFile, int iLine, const char* szExpr); + void (* reportFatalError)(void * thisHandle, CorInfoException** ppException, int result); + int (* allocBBProfileBuffer)(void * thisHandle, CorInfoException** ppException, unsigned int count, void** profileBuffer); + int (* getBBProfileData)(void * thisHandle, CorInfoException** ppException, void* ftnHnd, unsigned long* count, void** profileBuffer, unsigned long* numRuns); + void (* recordCallSite)(void * thisHandle, CorInfoException** ppException, unsigned int instrOffset, void* callSig, void* methodHandle); + void (* recordRelocation)(void * thisHandle, CorInfoException** ppException, void* location, void* target, unsigned short fRelocType, unsigned short slotNum, int addlDelta); + unsigned short (* getRelocTypeHint)(void * thisHandle, CorInfoException** ppException, void* target); + void (* getModuleNativeEntryPointRange)(void * thisHandle, CorInfoException** ppException, void** pStart, void** pEnd); + unsigned int (* getExpectedTargetArchitecture)(void * thisHandle, CorInfoException** ppException); + unsigned int (* getJitFlags)(void * thisHandle, CorInfoException** ppException, void* flags, unsigned int sizeInBytes); }; @@ -284,10 +291,10 @@ public: return _ret; } - virtual void getMethodVTableOffset(void* method, unsigned* offsetOfIndirection, unsigned* offsetAfterIndirection) + virtual void getMethodVTableOffset(void* method, unsigned* offsetOfIndirection, unsigned* offsetAfterIndirection, bool* isRelative) { CorInfoException* pException = nullptr; - _callbacks->getMethodVTableOffset(_thisHandle, &pException, method, offsetOfIndirection, offsetAfterIndirection); + _callbacks->getMethodVTableOffset(_thisHandle, &pException, method, offsetOfIndirection, offsetAfterIndirection, isRelative); if (pException != nullptr) throw pException; } @@ -301,6 +308,32 @@ public: return _ret; } + virtual void* getUnboxedEntry(void* ftn, bool* requiresInstMethodTableArg) + { + CorInfoException* pException = nullptr; + void* _ret = _callbacks->getUnboxedEntry(_thisHandle, &pException, ftn, requiresInstMethodTableArg); + if (pException != nullptr) + throw pException; + return _ret; + } + + virtual void* getDefaultEqualityComparerClass(void* elemType) + { + CorInfoException* pException = nullptr; + void* _ret = _callbacks->getDefaultEqualityComparerClass(_thisHandle, &pException, elemType); + if (pException != nullptr) + throw pException; + return _ret; + } + + virtual void expandRawHandleIntrinsic(void* pResolvedToken, void* pResult) + { + CorInfoException* pException = nullptr; + _callbacks->expandRawHandleIntrinsic(_thisHandle, &pException, pResolvedToken, pResult); + if (pException != nullptr) + throw pException; + } + virtual int getIntrinsicID(void* method, bool* pMustExpand) { CorInfoException* pException = nullptr; @@ -328,37 +361,28 @@ public: return _ret; } - virtual bool pInvokeMarshalingRequired(void* method, void* callSiteSig) + virtual int pInvokeMarshalingRequired(void* method, void* callSiteSig) { CorInfoException* pException = nullptr; - bool _ret = _callbacks->pInvokeMarshalingRequired(_thisHandle, &pException, method, callSiteSig); + int _ret = _callbacks->pInvokeMarshalingRequired(_thisHandle, &pException, method, callSiteSig); if (pException != nullptr) throw pException; return _ret; } - virtual bool satisfiesMethodConstraints(void* parent, void* method) + virtual int satisfiesMethodConstraints(void* parent, void* method) { CorInfoException* pException = nullptr; - bool _ret = _callbacks->satisfiesMethodConstraints(_thisHandle, &pException, parent, method); + int _ret = _callbacks->satisfiesMethodConstraints(_thisHandle, &pException, parent, method); if (pException != nullptr) throw pException; return _ret; } - virtual bool isCompatibleDelegate(void* objCls, void* methodParentCls, void* method, void* delegateCls, int* pfIsOpenDelegate) + virtual int isCompatibleDelegate(void* objCls, void* methodParentCls, void* method, void* delegateCls, int* pfIsOpenDelegate) { CorInfoException* pException = nullptr; - bool _ret = _callbacks->isCompatibleDelegate(_thisHandle, &pException, objCls, methodParentCls, method, delegateCls, pfIsOpenDelegate); - if (pException != nullptr) - throw pException; - return _ret; - } - - virtual bool isDelegateCreationAllowed(void* delegateHnd, void* calleeHnd) - { - CorInfoException* pException = nullptr; - bool _ret = _callbacks->isDelegateCreationAllowed(_thisHandle, &pException, delegateHnd, calleeHnd); + int _ret = _callbacks->isCompatibleDelegate(_thisHandle, &pException, objCls, methodParentCls, method, delegateCls, pfIsOpenDelegate); if (pException != nullptr) throw pException; return _ret; @@ -465,28 +489,28 @@ public: return _ret; } - virtual bool isValidToken(void* module, unsigned metaTOK) + virtual int isValidToken(void* module, unsigned metaTOK) { CorInfoException* pException = nullptr; - bool _ret = _callbacks->isValidToken(_thisHandle, &pException, module, metaTOK); + int _ret = _callbacks->isValidToken(_thisHandle, &pException, module, metaTOK); if (pException != nullptr) throw pException; return _ret; } - virtual bool isValidStringRef(void* module, unsigned metaTOK) + virtual int isValidStringRef(void* module, unsigned metaTOK) { CorInfoException* pException = nullptr; - bool _ret = _callbacks->isValidStringRef(_thisHandle, &pException, module, metaTOK); + int _ret = _callbacks->isValidStringRef(_thisHandle, &pException, module, metaTOK); if (pException != nullptr) throw pException; return _ret; } - virtual bool shouldEnforceCallvirtRestriction(void* scope) + virtual int shouldEnforceCallvirtRestriction(void* scope) { CorInfoException* pException = nullptr; - bool _ret = _callbacks->shouldEnforceCallvirtRestriction(_thisHandle, &pException, scope); + int _ret = _callbacks->shouldEnforceCallvirtRestriction(_thisHandle, &pException, scope); if (pException != nullptr) throw pException; return _ret; @@ -510,7 +534,25 @@ public: return _ret; } - virtual int appendClassName(wchar_t** ppBuf, int* pnBufLen, void* cls, bool fNamespace, bool fFullInst, bool fAssembly) + virtual const char* getClassNameFromMetadata(void* cls, const char** namespaceName) + { + CorInfoException* pException = nullptr; + const char* _ret = _callbacks->getClassNameFromMetadata(_thisHandle, &pException, cls, namespaceName); + if (pException != nullptr) + throw pException; + return _ret; + } + + virtual void* getTypeInstantiationArgument(void* cls, unsigned index) + { + CorInfoException* pException = nullptr; + void* _ret = _callbacks->getTypeInstantiationArgument(_thisHandle, &pException, cls, index); + if (pException != nullptr) + throw pException; + return _ret; + } + + virtual int appendClassName(wchar_t** ppBuf, int* pnBufLen, void* cls, int fNamespace, int fFullInst, int fAssembly) { CorInfoException* pException = nullptr; int _ret = _callbacks->appendClassName(_thisHandle, &pException, ppBuf, pnBufLen, cls, fNamespace, fFullInst, fAssembly); @@ -519,19 +561,19 @@ public: return _ret; } - virtual bool isValueClass(void* cls) + virtual int isValueClass(void* cls) { CorInfoException* pException = nullptr; - bool _ret = _callbacks->isValueClass(_thisHandle, &pException, cls); + int _ret = _callbacks->isValueClass(_thisHandle, &pException, cls); if (pException != nullptr) throw pException; return _ret; } - virtual bool canInlineTypeCheckWithObjectVTable(void* cls) + virtual int canInlineTypeCheckWithObjectVTable(void* cls) { CorInfoException* pException = nullptr; - bool _ret = _callbacks->canInlineTypeCheckWithObjectVTable(_thisHandle, &pException, cls); + int _ret = _callbacks->canInlineTypeCheckWithObjectVTable(_thisHandle, &pException, cls); if (pException != nullptr) throw pException; return _ret; @@ -546,10 +588,10 @@ public: return _ret; } - virtual bool isStructRequiringStackAllocRetBuf(void* cls) + virtual int isStructRequiringStackAllocRetBuf(void* cls) { CorInfoException* pException = nullptr; - bool _ret = _callbacks->isStructRequiringStackAllocRetBuf(_thisHandle, &pException, cls); + int _ret = _callbacks->isStructRequiringStackAllocRetBuf(_thisHandle, &pException, cls); if (pException != nullptr) throw pException; return _ret; @@ -617,7 +659,7 @@ public: return _ret; } - virtual unsigned getClassAlignmentRequirement(void* cls, bool fDoubleAlignHint) + virtual unsigned getClassAlignmentRequirement(void* cls, int fDoubleAlignHint) { CorInfoException* pException = nullptr; unsigned _ret = _callbacks->getClassAlignmentRequirement(_thisHandle, &pException, cls, fDoubleAlignHint); @@ -653,10 +695,10 @@ public: return _ret; } - virtual bool checkMethodModifier(void* hMethod, const char* modifier, bool fOptional) + virtual int checkMethodModifier(void* hMethod, const char* modifier, int fOptional) { CorInfoException* pException = nullptr; - bool _ret = _callbacks->checkMethodModifier(_thisHandle, &pException, hMethod, modifier, fOptional); + int _ret = _callbacks->checkMethodModifier(_thisHandle, &pException, hMethod, modifier, fOptional); if (pException != nullptr) throw pException; return _ret; @@ -760,7 +802,7 @@ public: return _ret; } - virtual int initClass(void* field, void* method, void* context, bool speculative) + virtual int initClass(void* field, void* method, void* context, int speculative) { CorInfoException* pException = nullptr; int _ret = _callbacks->initClass(_thisHandle, &pException, field, method, context, speculative); @@ -795,19 +837,46 @@ public: return _ret; } - virtual bool canCast(void* child, void* parent) + virtual int getTypeForPrimitiveNumericClass(void* cls) { CorInfoException* pException = nullptr; - bool _ret = _callbacks->canCast(_thisHandle, &pException, child, parent); + int _ret = _callbacks->getTypeForPrimitiveNumericClass(_thisHandle, &pException, cls); if (pException != nullptr) throw pException; return _ret; } - virtual bool areTypesEquivalent(void* cls1, void* cls2) + virtual int canCast(void* child, void* parent) { CorInfoException* pException = nullptr; - bool _ret = _callbacks->areTypesEquivalent(_thisHandle, &pException, cls1, cls2); + int _ret = _callbacks->canCast(_thisHandle, &pException, child, parent); + if (pException != nullptr) + throw pException; + return _ret; + } + + virtual int areTypesEquivalent(void* cls1, void* cls2) + { + CorInfoException* pException = nullptr; + int _ret = _callbacks->areTypesEquivalent(_thisHandle, &pException, cls1, cls2); + if (pException != nullptr) + throw pException; + return _ret; + } + + virtual int compareTypesForCast(void* fromClass, void* toClass) + { + CorInfoException* pException = nullptr; + int _ret = _callbacks->compareTypesForCast(_thisHandle, &pException, fromClass, toClass); + if (pException != nullptr) + throw pException; + return _ret; + } + + virtual int compareTypesForEquality(void* cls1, void* cls2) + { + CorInfoException* pException = nullptr; + int _ret = _callbacks->compareTypesForEquality(_thisHandle, &pException, cls1, cls2); if (pException != nullptr) throw pException; return _ret; @@ -840,19 +909,19 @@ public: return _ret; } - virtual bool satisfiesClassConstraints(void* cls) + virtual int satisfiesClassConstraints(void* cls) { CorInfoException* pException = nullptr; - bool _ret = _callbacks->satisfiesClassConstraints(_thisHandle, &pException, cls); + int _ret = _callbacks->satisfiesClassConstraints(_thisHandle, &pException, cls); if (pException != nullptr) throw pException; return _ret; } - virtual bool isSDArray(void* cls) + virtual int isSDArray(void* cls) { CorInfoException* pException = nullptr; - bool _ret = _callbacks->isSDArray(_thisHandle, &pException, cls); + int _ret = _callbacks->isSDArray(_thisHandle, &pException, cls); if (pException != nullptr) throw pException; return _ret; @@ -1104,6 +1173,15 @@ public: return _ret; } + virtual const char* getMethodNameFromMetadata(void* ftn, const char** className, const char** namespaceName) + { + CorInfoException* pException = nullptr; + const char* _ret = _callbacks->getMethodNameFromMetadata(_thisHandle, &pException, ftn, className, namespaceName); + if (pException != nullptr) + throw pException; + return _ret; + } + virtual unsigned getMethodHash(void* ftn) { CorInfoException* pException = nullptr; @@ -1158,15 +1236,6 @@ public: return _ret; } - virtual size_t* getAddrModuleDomainID(void* module) - { - CorInfoException* pException = nullptr; - size_t* _ret = _callbacks->getAddrModuleDomainID(_thisHandle, &pException, module); - if (pException != nullptr) - throw pException; - return _ret; - } - virtual void* getHelperFtn(int ftnNum, void** ppIndirection) { CorInfoException* pException = nullptr; @@ -1246,7 +1315,7 @@ public: return _ret; } - virtual void embedGenericHandle(void* pResolvedToken, bool fEmbedParent, void* pResult) + virtual void embedGenericHandle(void* pResolvedToken, int fEmbedParent, void* pResult) { CorInfoException* pException = nullptr; _callbacks->embedGenericHandle(_thisHandle, &pException, pResolvedToken, fEmbedParent, pResult); @@ -1324,19 +1393,19 @@ public: throw pException; } - virtual bool canAccessFamily(void* hCaller, void* hInstanceType) + virtual int canAccessFamily(void* hCaller, void* hInstanceType) { CorInfoException* pException = nullptr; - bool _ret = _callbacks->canAccessFamily(_thisHandle, &pException, hCaller, hInstanceType); + int _ret = _callbacks->canAccessFamily(_thisHandle, &pException, hCaller, hInstanceType); if (pException != nullptr) throw pException; return _ret; } - virtual bool isRIDClassDomainID(void* cls) + virtual int isRIDClassDomainID(void* cls) { CorInfoException* pException = nullptr; - bool _ret = _callbacks->isRIDClassDomainID(_thisHandle, &pException, cls); + int _ret = _callbacks->isRIDClassDomainID(_thisHandle, &pException, cls); if (pException != nullptr) throw pException; return _ret; @@ -1456,7 +1525,7 @@ public: throw pException; } - virtual void reserveUnwindInfo(bool isFunclet, bool isColdCode, unsigned int unwindSize) + virtual void reserveUnwindInfo(int isFunclet, int isColdCode, unsigned int unwindSize) { CorInfoException* pException = nullptr; _callbacks->reserveUnwindInfo(_thisHandle, &pException, isFunclet, isColdCode, unwindSize); @@ -1505,10 +1574,10 @@ public: throw pException; } - virtual bool logMsg(unsigned level, const char* fmt, va_list args) + virtual int logMsg(unsigned level, const char* fmt, va_list args) { CorInfoException* pException = nullptr; - bool _ret = _callbacks->logMsg(_thisHandle, &pException, level, fmt, args); + int _ret = _callbacks->logMsg(_thisHandle, &pException, level, fmt, args); if (pException != nullptr) throw pException; return _ret; diff --git a/external/corert/src/Native/jitinterface/jitwrapper.cpp b/external/corert/src/Native/jitinterface/jitwrapper.cpp index 8c12c77163..3b15321244 100644 --- a/external/corert/src/Native/jitinterface/jitwrapper.cpp +++ b/external/corert/src/Native/jitinterface/jitwrapper.cpp @@ -16,11 +16,22 @@ typedef struct _GUID { unsigned char Data4[8]; } GUID; -static const GUID JITEEVersionIdentifier = { /* f00b3f49-ddd2-49be-ba43-6e49ffa66959 */ - 0xf00b3f49, - 0xddd2, - 0x49be, - { 0xba, 0x43, 0x6e, 0x49, 0xff, 0xa6, 0x69, 0x59 } +class CORJIT_FLAGS +{ +public: + CORJIT_FLAGS(const CORJIT_FLAGS& other) + { + corJitFlags = other.corJitFlags; + } +private: + unsigned __int64 corJitFlags; +}; + +static const GUID JITEEVersionIdentifier = { /* a6860f80-01cb-4f87-82c2-a8e5a744f2fa */ + 0xa6860f80, + 0x01cb, + 0x4f87, + {0x82, 0xc2, 0xa8, 0xe5, 0xa7, 0x44, 0xf2, 0xfa} }; class Jit @@ -42,6 +53,11 @@ public: // return the version identifier that the EE expects), then the EE fails to load the JIT. // virtual void getVersionIdentifier(GUID* versionIdentifier) = 0; + + // When the EE loads the System.Numerics.Vectors assembly, it asks the JIT what length (in bytes) of + // SIMD vector it supports as an intrinsic type. Zero means that the JIT does not support SIMD + // intrinsics, so the EE should use the default size (i.e. the size of the IL implementation). + virtual unsigned getMaxIntrinsicSIMDVectorLength(CORJIT_FLAGS cpuCompileFlags) = 0; }; DLL_EXPORT int JitCompileMethod( @@ -78,3 +94,10 @@ DLL_EXPORT int JitCompileMethod( return 1; } + +DLL_EXPORT unsigned GetMaxIntrinsicSIMDVectorLength( + Jit * pJit, + CORJIT_FLAGS * flags) +{ + return pJit->getMaxIntrinsicSIMDVectorLength(*flags); +} diff --git a/external/corert/src/Native/libunwind/include/__libunwind_config.h b/external/corert/src/Native/libunwind/include/__libunwind_config.h index 93a3be1eb9..2cc8c1951f 100644 --- a/external/corert/src/Native/libunwind/include/__libunwind_config.h +++ b/external/corert/src/Native/libunwind/include/__libunwind_config.h @@ -20,8 +20,8 @@ #if defined(_LIBUNWIND_IS_NATIVE_ONLY) # if defined(__i386__) # define _LIBUNWIND_TARGET_I386 1 -# define _LIBUNWIND_CONTEXT_SIZE 25 -# define _LIBUNWIND_CURSOR_SIZE 32 +# define _LIBUNWIND_CONTEXT_SIZE 13 +# define _LIBUNWIND_CURSOR_SIZE 23 # define _LIBUNWIND_HIGHEST_DWARF_REGISTER 9 # elif defined(__x86_64__) # define _LIBUNWIND_TARGET_X86_64 1 @@ -53,6 +53,11 @@ # define _LIBUNWIND_CONTEXT_SIZE 16 # define _LIBUNWIND_CURSOR_SIZE 28 # define _LIBUNWIND_HIGHEST_DWARF_REGISTER 32 +# elif defined (_WASM_) +# define _LIBUNWIND_TARGET_WASM 1 +// TODO: Determine the right values +# define _LIBUNWIND_CONTEXT_SIZE 0xbadf00d +# define _LIBUNWIND_CURSOR_SIZE 0xbadf00d # else # error "Unsupported architecture." # endif diff --git a/external/corert/src/Native/libunwind/src/Registers.hpp b/external/corert/src/Native/libunwind/src/Registers.hpp index cca90092fd..f15c4fa542 100644 --- a/external/corert/src/Native/libunwind/src/Registers.hpp +++ b/external/corert/src/Native/libunwind/src/Registers.hpp @@ -1195,8 +1195,12 @@ public: void setFP(uint64_t value, uint64_t location) { _registers.__fp = value; _registerLocations.__fp = location; } private: +#if !defined(GEN_REG_COUNT) +#define GEN_REG_COUNT 29 +#endif + struct GPRs { - uint64_t __x[29]; // x0-x28 + uint64_t __x[GEN_REG_COUNT]; // x0-x28 uint64_t __fp; // Frame pointer x29 uint64_t __lr; // Link register x30 uint64_t __sp; // Stack pointer x31 @@ -1205,7 +1209,7 @@ private: }; struct GPRLocations { - uint64_t __x[29]; // x0-x28 + uint64_t __x[GEN_REG_COUNT]; // x0-x28 uint64_t __fp; // Frame pointer x29 uint64_t __lr; // Link register x30 uint64_t __sp; // Stack pointer x31 diff --git a/external/corert/src/Native/libunwind/src/UnwindCursor.hpp b/external/corert/src/Native/libunwind/src/UnwindCursor.hpp index 7a1149eaf6..f68eee18e9 100644 --- a/external/corert/src/Native/libunwind/src/UnwindCursor.hpp +++ b/external/corert/src/Native/libunwind/src/UnwindCursor.hpp @@ -427,6 +427,7 @@ template class UnwindCursor : public AbstractUnwindCursor{ typedef typename A::pint_t pint_t; public: + UnwindCursor(A &as); UnwindCursor(unw_context_t *context, A &as); UnwindCursor(A &as, void *threadArg); virtual ~UnwindCursor() {} @@ -469,6 +470,7 @@ private: #endif #if _LIBUNWIND_SUPPORT_DWARF_UNWIND +public: bool getInfoFromDwarfSection(pint_t pc, const UnwindInfoSections §s, uint32_t fdeSectionOffsetHint=0); int stepWithDwarfFDE() { @@ -608,6 +610,13 @@ private: bool _isSignalFrame; }; +template +UnwindCursor::UnwindCursor(A &as) + : _addressSpace(as) + , _unwindInfoMissing(false) + , _isSignalFrame(false) { + memset(&_info, 0, sizeof(_info)); +} template UnwindCursor::UnwindCursor(unw_context_t *context, A &as) diff --git a/external/corert/src/Runtime.Base/src/System/Runtime/CompilerServices/Unsafe.cs b/external/corert/src/Runtime.Base/src/Internal/Runtime/CompilerServices/Unsafe.cs similarity index 96% rename from external/corert/src/Runtime.Base/src/System/Runtime/CompilerServices/Unsafe.cs rename to external/corert/src/Runtime.Base/src/Internal/Runtime/CompilerServices/Unsafe.cs index e84cb7729c..705193a3c6 100644 --- a/external/corert/src/Runtime.Base/src/System/Runtime/CompilerServices/Unsafe.cs +++ b/external/corert/src/Runtime.Base/src/Internal/Runtime/CompilerServices/Unsafe.cs @@ -2,18 +2,21 @@ // 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.Runtime.CompilerServices; + #if BIT64 using nint = System.Int64; #else using nint = System.Int32; #endif -namespace System.Runtime.CompilerServices +namespace Internal.Runtime.CompilerServices { // // Subsetted clone of System.Runtime.CompilerServices.Unsafe for internal runtime use. // Keep in sync with https://github.com/dotnet/corefx/tree/master/src/System.Runtime.CompilerServices.Unsafe. - // + // /// /// Contains generic, low-level functionality for manipulating pointers. diff --git a/external/corert/src/Runtime.Base/src/Runtime.Base.csproj b/external/corert/src/Runtime.Base/src/Runtime.Base.csproj index e76d6957b5..ffae8483fb 100644 --- a/external/corert/src/Runtime.Base/src/Runtime.Base.csproj +++ b/external/corert/src/Runtime.Base/src/Runtime.Base.csproj @@ -1,30 +1,14 @@ - - + - {CA7FAACD-89ED-4D41-8377-92C1D9055720} Library Runtime.Base true - true true false - - $(MSBuildThisFileDirectory)/Documentation - - - - - - - - - - - - true + false @@ -46,7 +30,7 @@ - + @@ -92,7 +76,6 @@ - @@ -107,6 +90,9 @@ + + + Common\EEType.Constants.cs @@ -125,7 +111,8 @@ - + + diff --git a/external/corert/src/Runtime.Base/src/System/Array.cs b/external/corert/src/Runtime.Base/src/System/Array.cs index d25231f64d..3d6acdb7b1 100644 --- a/external/corert/src/Runtime.Base/src/System/Array.cs +++ b/external/corert/src/Runtime.Base/src/System/Array.cs @@ -5,6 +5,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Internal.Runtime.CompilerServices; + namespace System { // CONTRACT with Runtime @@ -42,7 +44,7 @@ namespace System } } - // To accomodate class libraries that wish to implement generic interfaces on arrays, all class libraries + // To accommodate class libraries that wish to implement generic interfaces on arrays, all class libraries // are now required to provide an Array class that derives from Array. internal class Array : Array { diff --git a/external/corert/src/Runtime.Base/src/System/Object.cs b/external/corert/src/Runtime.Base/src/System/Object.cs index 2e541f303e..5f1732622f 100644 --- a/external/corert/src/Runtime.Base/src/System/Object.cs +++ b/external/corert/src/Runtime.Base/src/System/Object.cs @@ -17,6 +17,8 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Internal.Runtime.CompilerServices; + // TODO: remove when m_pEEType becomes EETypePtr using EEType = Internal.Runtime.EEType; using ObjHeader = Internal.Runtime.ObjHeader; diff --git a/external/corert/src/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs b/external/corert/src/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs index 6c4b21b7d4..6ca1646c76 100644 --- a/external/corert/src/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs +++ b/external/corert/src/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs @@ -7,6 +7,7 @@ using System.Runtime; using System.Runtime.CompilerServices; using Internal.Runtime; +using Internal.Runtime.CompilerServices; namespace System.Runtime { diff --git a/external/corert/src/Runtime.Base/src/System/Runtime/CastableObjectSupport.cs b/external/corert/src/Runtime.Base/src/System/Runtime/CastableObjectSupport.cs index ec2720c32c..20f0ce285e 100644 --- a/external/corert/src/Runtime.Base/src/System/Runtime/CastableObjectSupport.cs +++ b/external/corert/src/Runtime.Base/src/System/Runtime/CastableObjectSupport.cs @@ -3,9 +3,11 @@ // See the LICENSE file in the project root for more information. using System; -using Internal.Runtime; using System.Runtime.CompilerServices; +using Internal.Runtime; +using Internal.Runtime.CompilerServices; + namespace System.Runtime { [System.Runtime.CompilerServices.EagerStaticClassConstructionAttribute] diff --git a/external/corert/src/Runtime.Base/src/System/Runtime/CompilerServices/StackOnlyAttribute.cs b/external/corert/src/Runtime.Base/src/System/Runtime/CompilerServices/IsByRefLikeAttribute.cs similarity index 81% rename from external/corert/src/Runtime.Base/src/System/Runtime/CompilerServices/StackOnlyAttribute.cs rename to external/corert/src/Runtime.Base/src/System/Runtime/CompilerServices/IsByRefLikeAttribute.cs index a51669fb12..9851dc54e5 100644 --- a/external/corert/src/Runtime.Base/src/System/Runtime/CompilerServices/StackOnlyAttribute.cs +++ b/external/corert/src/Runtime.Base/src/System/Runtime/CompilerServices/IsByRefLikeAttribute.cs @@ -7,5 +7,7 @@ using System; namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Struct)] - internal sealed class StackOnlyAttribute : Attribute { } + internal sealed class IsByRefLikeAttribute : Attribute + { + } } diff --git a/external/corert/src/Runtime.Base/src/System/Runtime/EEType.Runtime.cs b/external/corert/src/Runtime.Base/src/System/Runtime/EEType.Runtime.cs index 475b775685..3a1e8a9544 100644 --- a/external/corert/src/Runtime.Base/src/System/Runtime/EEType.Runtime.cs +++ b/external/corert/src/Runtime.Base/src/System/Runtime/EEType.Runtime.cs @@ -25,16 +25,16 @@ namespace Internal.Runtime { fixed (EEType* pThis = &this) { - IntPtr pGetArrayEEType = (IntPtr)InternalCalls.RhpGetClasslibFunctionFromEEtype(new IntPtr(pThis), EH.ClassLibFunctionId.GetSystemArrayEEType); + IntPtr pGetArrayEEType = (IntPtr)InternalCalls.RhpGetClasslibFunctionFromEEType(new IntPtr(pThis), EH.ClassLibFunctionId.GetSystemArrayEEType); if (pGetArrayEEType != IntPtr.Zero) return (EEType*)CalliIntrinsics.Call(pGetArrayEEType); } -#if CORERT - EH.FallbackFailFast(RhFailFastReason.InternalError, null); - return null; -#else +#if PROJECTN fixed (EEType* pThis = &this) return InternalCalls.RhpGetArrayBaseType(pThis); +#else + EH.FallbackFailFast(RhFailFastReason.InternalError, null); + return null; #endif } @@ -77,7 +77,7 @@ namespace Internal.Runtime { fixed (EEType* pThis = &this) { - if (!IsRuntimeAllocated && !IsDynamicType) + if (!IsDynamicType) return (IntPtr)pThis; // There are currently four types of runtime allocated EETypes, arrays, pointers, byrefs, and generic types. diff --git a/external/corert/src/Runtime.Base/src/System/Runtime/EETypePtr.cs b/external/corert/src/Runtime.Base/src/System/Runtime/EETypePtr.cs index 26355a84ac..1ea0736254 100644 --- a/external/corert/src/Runtime.Base/src/System/Runtime/EETypePtr.cs +++ b/external/corert/src/Runtime.Base/src/System/Runtime/EETypePtr.cs @@ -40,8 +40,8 @@ namespace System return (Internal.Runtime.EEType*)(void*)_value; } -#if CORERT - // This only works on CoreRT (with no fallback) because Runtime.Base doesn't have enough infrastructure +#if !PROJECTN + // This does not work on ProjectN (with no fallback) because Runtime.Base doesn't have enough infrastructure // to let us express typeof(T).TypeHandle.ToEETypePtr(). [Intrinsic] internal static EETypePtr EETypePtrOf() diff --git a/external/corert/src/Runtime.Base/src/System/Runtime/ExceptionHandling.cs b/external/corert/src/Runtime.Base/src/System/Runtime/ExceptionHandling.cs index ea552bb34c..761ad5319b 100644 --- a/external/corert/src/Runtime.Base/src/System/Runtime/ExceptionHandling.cs +++ b/external/corert/src/Runtime.Base/src/System/Runtime/ExceptionHandling.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -154,6 +155,7 @@ namespace System.Runtime AppendExceptionStackFrame = 3, CheckStaticClassConstruction = 4, GetSystemArrayEEType = 5, + OnFirstChance = 6, } // Given an address pointing somewhere into a managed module, get the classlib-defined fail-fast @@ -190,9 +192,11 @@ namespace System.Runtime #if AMD64 [StructLayout(LayoutKind.Explicit, Size = 0x4d0)] #elif ARM - [StructLayout(LayoutKind.Explicit, Size=0x1a0)] + [StructLayout(LayoutKind.Explicit, Size = 0x1a0)] #elif X86 - [StructLayout(LayoutKind.Explicit, Size=0x2cc)] + [StructLayout(LayoutKind.Explicit, Size = 0x2cc)] +#elif ARM64 + [StructLayout(LayoutKind.Explicit, Size = 0x390)] #else [StructLayout(LayoutKind.Explicit, Size = 0x10)] // this is small enough that it should trip an assert in RhpCopyContextFromExInfo #endif @@ -210,6 +214,25 @@ namespace System.Runtime #endif } + private static void OnFirstChanceExceptionViaClassLib(object exception) + { + IntPtr pOnFirstChanceFunction = + (IntPtr)InternalCalls.RhpGetClasslibFunctionFromEEType((IntPtr)exception.m_pEEType, ClassLibFunctionId.OnFirstChance); + + if (pOnFirstChanceFunction == IntPtr.Zero) + { + return; + } + + try + { + CalliIntrinsics.CallVoid(pOnFirstChanceFunction, exception); + } + catch + { + // disallow all exceptions leaking out of callbacks + } + } [MethodImpl(MethodImplOptions.NoInlining)] internal static unsafe void UnhandledExceptionFailFastViaClasslib( @@ -311,7 +334,7 @@ namespace System.Runtime return e; } - // Given an ExceptionID and an EEtype address, get an exception object of a type that the module containing + // Given an ExceptionID and an EEType address, get an exception object of a type that the module containing // the given address will understand. This finds the classlib-defined GetRuntimeException function and asks // it for the exception object. internal static Exception GetClasslibExceptionFromEEType(ExceptionIDs id, IntPtr pEEType) @@ -321,7 +344,7 @@ namespace System.Runtime IntPtr pGetRuntimeExceptionFunction = IntPtr.Zero; if (pEEType != IntPtr.Zero) { - pGetRuntimeExceptionFunction = (IntPtr)InternalCalls.RhpGetClasslibFunctionFromEEtype(pEEType, ClassLibFunctionId.GetRuntimeException); + pGetRuntimeExceptionFunction = (IntPtr)InternalCalls.RhpGetClasslibFunctionFromEEType(pEEType, ClassLibFunctionId.GetRuntimeException); } // Return the exception object we get from the classlib. @@ -400,7 +423,7 @@ namespace System.Runtime case ExceptionIDs.InvalidCast: return new InvalidCastException(); - + default: Debug.Assert(false, "unexpected ExceptionID"); FallbackFailFast(RhFailFastReason.InternalError, null); @@ -413,6 +436,7 @@ namespace System.Runtime { STATUS_REDHAWK_NULL_REFERENCE = 0x00000000u, STATUS_REDHAWK_WRITE_BARRIER_NULL_REFERENCE = 0x00000042u, + STATUS_REDHAWK_THREAD_ABORT = 0x00000043u, STATUS_DATATYPE_MISALIGNMENT = 0x80000002u, STATUS_ACCESS_VIOLATION = 0xC0000005u, @@ -445,9 +469,8 @@ namespace System.Runtime InstructionFaultFlag = 0x10 } - [StackOnly] [StructLayout(LayoutKind.Explicit)] - public struct ExInfo + public ref struct ExInfo { internal void Init(object exceptionObj, bool instructionFault = false) { @@ -526,8 +549,9 @@ namespace System.Runtime IntPtr faultingCodeAddress = exInfo._pExContext->IP; bool instructionFault = true; + ExceptionIDs exceptionId = default(ExceptionIDs); + Exception exceptionToThrow = null; - ExceptionIDs exceptionId; switch (exceptionCode) { case (uint)HwExceptionCode.STATUS_REDHAWK_NULL_REFERENCE: @@ -542,6 +566,10 @@ namespace System.Runtime exceptionId = ExceptionIDs.NullReference; break; + case (uint)HwExceptionCode.STATUS_REDHAWK_THREAD_ABORT: + exceptionToThrow = InternalCalls.RhpGetThreadAbortException(); + break; + case (uint)HwExceptionCode.STATUS_DATATYPE_MISALIGNMENT: exceptionId = ExceptionIDs.DataMisaligned; break; @@ -565,11 +593,13 @@ namespace System.Runtime // know the complete set of HW faults generated by managed code and do not need to handle // this case. FailFastViaClasslib(RhFailFastReason.InternalError, null, faultingCodeAddress); - exceptionId = ExceptionIDs.NullReference; break; } - Exception exceptionToThrow = GetClasslibException(exceptionId, faultingCodeAddress); + if (exceptionId != default(ExceptionIDs)) + { + exceptionToThrow = GetClasslibException(exceptionId, faultingCodeAddress); + } exInfo.Init(exceptionToThrow, instructionFault); DispatchEx(ref exInfo._frameIter, ref exInfo, MaxTryRegionIdx); @@ -638,7 +668,10 @@ namespace System.Runtime bool isValid = frameIter.Init(exInfo._pExContext, (exInfo._kind & ExKind.InstructionFaultFlag) != 0); Debug.Assert(isValid, "RhThrowEx called with an unexpected context"); + + OnFirstChanceExceptionViaClassLib(exceptionObj); DebuggerNotify.BeginFirstPass(exceptionObj, frameIter.ControlPC, frameIter.SP); + for (; isValid; isValid = frameIter.Next(out startIdx, out unwoundReversePInvoke)) { // For GC stackwalking, we'll happily walk across native code blocks, but for EH dispatch, we diff --git a/external/corert/src/Runtime.Base/src/System/Runtime/InternalCalls.cs b/external/corert/src/Runtime.Base/src/System/Runtime/InternalCalls.cs index 3fb1694721..0c4cc3ebf7 100644 --- a/external/corert/src/Runtime.Base/src/System/Runtime/InternalCalls.cs +++ b/external/corert/src/Runtime.Base/src/System/Runtime/InternalCalls.cs @@ -234,10 +234,10 @@ namespace System.Runtime [ManuallyManaged(GcPollPolicy.Never)] internal extern static unsafe void* RhpGetClasslibFunctionFromCodeAddress(IntPtr address, EH.ClassLibFunctionId id); - [RuntimeImport(Redhawk.BaseName, "RhpGetClasslibFunctionFromEEtype")] + [RuntimeImport(Redhawk.BaseName, "RhpGetClasslibFunctionFromEEType")] [MethodImpl(MethodImplOptions.InternalCall)] [ManuallyManaged(GcPollPolicy.Never)] - internal extern static unsafe void* RhpGetClasslibFunctionFromEEtype(IntPtr pEEType, EH.ClassLibFunctionId id); + internal extern static unsafe void* RhpGetClasslibFunctionFromEEType(IntPtr pEEType, EH.ClassLibFunctionId id); // // StackFrameIterator @@ -397,6 +397,10 @@ namespace System.Runtime [ManuallyManaged(GcPollPolicy.Never)] internal extern static int RhpGetThunkBlockSize(); + [RuntimeImport(Redhawk.BaseName, "RhpGetThreadAbortException")] + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static Exception RhpGetThreadAbortException(); + //------------------------------------------------------------------------------------------------------------ // PInvoke-based internal calls // diff --git a/external/corert/src/Runtime.Base/src/System/Runtime/InteropServices/CallingConvention.cs b/external/corert/src/Runtime.Base/src/System/Runtime/InteropServices/CallingConvention.cs index a03b8a9502..3b18fdee3a 100644 --- a/external/corert/src/Runtime.Base/src/System/Runtime/InteropServices/CallingConvention.cs +++ b/external/corert/src/Runtime.Base/src/System/Runtime/InteropServices/CallingConvention.cs @@ -5,7 +5,7 @@ namespace System.Runtime.InteropServices { // Used for the CallingConvention named argument to the DllImport and NativeCallable attribute - internal enum CallingConvention + public enum CallingConvention { Winapi = 1, Cdecl = 2, diff --git a/external/corert/src/Runtime.Base/src/System/Runtime/InteropServices/DllImportAttribute.cs b/external/corert/src/Runtime.Base/src/System/Runtime/InteropServices/DllImportAttribute.cs index 8ebc5fbe31..53a6fea049 100644 --- a/external/corert/src/Runtime.Base/src/System/Runtime/InteropServices/DllImportAttribute.cs +++ b/external/corert/src/Runtime.Base/src/System/Runtime/InteropServices/DllImportAttribute.cs @@ -5,7 +5,7 @@ namespace System.Runtime.InteropServices { [AttributeUsage(AttributeTargets.Method)] - internal sealed class DllImportAttribute : Attribute + public sealed class DllImportAttribute : Attribute { public CallingConvention CallingConvention; diff --git a/external/corert/src/Runtime.Base/src/System/Runtime/InteropServices/UnsafeGCHandle.cs b/external/corert/src/Runtime.Base/src/System/Runtime/InteropServices/UnsafeGCHandle.cs index 31b7d2a267..90eea56c3f 100644 --- a/external/corert/src/Runtime.Base/src/System/Runtime/InteropServices/UnsafeGCHandle.cs +++ b/external/corert/src/Runtime.Base/src/System/Runtime/InteropServices/UnsafeGCHandle.cs @@ -6,6 +6,8 @@ using System; using System.Runtime.CompilerServices; using System.Diagnostics; +using Internal.Runtime.CompilerServices; + namespace System.Runtime.InteropServices { /// @@ -33,12 +35,17 @@ namespace System.Runtime.InteropServices } // Target property - allows getting / updating of the handle's referent. - public Object Target + public unsafe Object Target { get { Debug.Assert(IsAllocated, "handle isn't initialized"); +#if DEBUG + // The runtime performs additional checks in debug builds return InternalCalls.RhHandleGet(_handle); +#else + return Unsafe.As(ref *(IntPtr*)_handle); +#endif } set diff --git a/external/corert/src/Runtime.Base/src/System/Runtime/RuntimeExceptionHelpers.cs b/external/corert/src/Runtime.Base/src/System/Runtime/RuntimeExceptionHelpers.cs new file mode 100644 index 0000000000..01f7d2177e --- /dev/null +++ b/external/corert/src/Runtime.Base/src/System/Runtime/RuntimeExceptionHelpers.cs @@ -0,0 +1,16 @@ +// 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.Runtime; + +namespace System +{ + public class RuntimeExceptionHelpers + { + public static void FailFast(String message) + { + InternalCalls.RhpFallbackFailFast(); + } + } +} \ No newline at end of file diff --git a/external/corert/src/Runtime.Base/src/System/Runtime/RuntimeExports.cs b/external/corert/src/Runtime.Base/src/System/Runtime/RuntimeExports.cs index 3c8868bf5c..db9b5de744 100644 --- a/external/corert/src/Runtime.Base/src/System/Runtime/RuntimeExports.cs +++ b/external/corert/src/Runtime.Base/src/System/Runtime/RuntimeExports.cs @@ -11,6 +11,7 @@ using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using Internal.Runtime; +using Internal.Runtime.CompilerServices; namespace System.Runtime { diff --git a/external/corert/src/Runtime.Base/src/System/Runtime/RuntimeImports.cs b/external/corert/src/Runtime.Base/src/System/Runtime/RuntimeImports.cs new file mode 100644 index 0000000000..064f4bf9a4 --- /dev/null +++ b/external/corert/src/Runtime.Base/src/System/Runtime/RuntimeImports.cs @@ -0,0 +1,91 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +using Internal.Runtime; + +namespace System.Runtime +{ + public struct TypeManagerHandle + { + private IntPtr _handleValue; + + public TypeManagerHandle(IntPtr handleValue) + { + _handleValue = handleValue; + } + + public IntPtr GetIntPtrUNSAFE() + { + return _handleValue; + } + + public static bool operator ==(TypeManagerHandle left, TypeManagerHandle right) + { + return left._handleValue == right._handleValue; + } + + public static bool operator !=(TypeManagerHandle left, TypeManagerHandle right) + { + return left._handleValue != right._handleValue; + } + + public bool Equals(TypeManagerHandle other) + { + return _handleValue == other._handleValue; + } + } + + internal static class RuntimeImports + { + private const string RuntimeLibrary = "[MRT]"; + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpRegisterFrozenSegment")] + internal static extern bool RhpRegisterFrozenSegment(IntPtr pSegmentStart, int length); + + [RuntimeImport(RuntimeLibrary, "RhpGetModuleSection")] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern IntPtr RhGetModuleSection(ref TypeManagerHandle module, ReadyToRunSectionType section, out int length); + + internal static IntPtr RhGetModuleSection(TypeManagerHandle module, ReadyToRunSectionType section, out int length) + { + return RhGetModuleSection(ref module, section, out length); + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpCreateTypeManager")] + internal static extern unsafe TypeManagerHandle RhpCreateTypeManager(IntPtr osModule, IntPtr moduleHeader, IntPtr* pClasslibFunctions, int nClasslibFunctions); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpRegisterOsModule")] + internal static extern unsafe IntPtr RhpRegisterOsModule(IntPtr osModule); + + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhNewObject")] + internal static extern object RhNewObject(EETypePtr pEEType); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhBulkMoveWithWriteBarrier")] + internal static extern unsafe void RhBulkMoveWithWriteBarrier(ref byte dmem, ref byte smem, uint size); + + + // Allocate handle. + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpHandleAlloc")] + private static extern IntPtr RhpHandleAlloc(Object value, GCHandleType type); + + internal static IntPtr RhHandleAlloc(Object value, GCHandleType type) + { + IntPtr h = RhpHandleAlloc(value, type); + if (h == IntPtr.Zero) + throw new OutOfMemoryException(); + return h; + } + } +} diff --git a/external/corert/src/Runtime.Base/src/System/Runtime/ThunkPool.cs b/external/corert/src/Runtime.Base/src/System/Runtime/ThunkPool.cs index 67869d4ec7..404494451e 100644 --- a/external/corert/src/Runtime.Base/src/System/Runtime/ThunkPool.cs +++ b/external/corert/src/Runtime.Base/src/System/Runtime/ThunkPool.cs @@ -81,16 +81,16 @@ namespace System.Runtime private static IntPtr ClearThumbBit(IntPtr value) { #if ARM - Debug.Assert(((nuint)value & 1) == 1); - value = (IntPtr)((nuint)value - 1); + Debug.Assert(((nint)value & 1) == 1); + value = (IntPtr)((nint)value - 1); #endif return value; } private static IntPtr SetThumbBit(IntPtr value) { #if ARM - Debug.Assert(((nuint)value & 1) == 0); - value = (IntPtr)((nuint)value + 1); + Debug.Assert(((nint)value & 1) == 0); + value = (IntPtr)((nint)value + 1); #endif return value; } @@ -296,7 +296,7 @@ namespace System.Runtime int thunkIndex = (int)((thunkAddressValue - currentThunksBlockAddress) / (nuint)Constants.ThunkCodeSize); // Compute the address of the data block that corresponds to the current thunk - IntPtr thunkDataBlockAddress = InternalCalls.RhpGetThunkDataBlockAddress((IntPtr)thunkAddressValue); + IntPtr thunkDataBlockAddress = InternalCalls.RhpGetThunkDataBlockAddress((IntPtr)((nint)thunkAddressValue)); return thunkDataBlockAddress + thunkIndex * Constants.ThunkDataSize; } @@ -427,4 +427,4 @@ namespace System.Runtime // { // } } -} \ No newline at end of file +} diff --git a/external/corert/src/Runtime.Base/src/System/Runtime/TypeCast.cs b/external/corert/src/Runtime.Base/src/System/Runtime/TypeCast.cs index 62dea84482..568892a652 100644 --- a/external/corert/src/Runtime.Base/src/System/Runtime/TypeCast.cs +++ b/external/corert/src/Runtime.Base/src/System/Runtime/TypeCast.cs @@ -7,6 +7,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Internal.Runtime; +using Internal.Runtime.CompilerServices; namespace System.Runtime { @@ -268,7 +269,7 @@ namespace System.Runtime EEType* pTargetType = (EEType*)pvTargetType; EEType* pObjType = obj.EEType; - if (CastCache.AreTypesAssignableInternal(pObjType, pTargetType, AssignmentVariation.BoxedSource)) + if (CastCache.AreTypesAssignableInternal_SourceNotTarget_BoxedSource(pObjType, pTargetType)) return obj; // If object type implements ICastable then there's one more way to check whether it implements @@ -744,7 +745,7 @@ namespace System.Runtime EEType* pTargetType = (EEType*)pvTargetEEType; EEType* pObjType = obj.EEType; - if (CastCache.AreTypesAssignableInternal(pObjType, pTargetType, AssignmentVariation.BoxedSource)) + if (CastCache.AreTypesAssignableInternal_SourceNotTarget_BoxedSource(pObjType, pTargetType)) return obj; Exception castError = null; @@ -1116,6 +1117,24 @@ namespace System.Runtime return entry.Result; } + // This method is an optimized and customized version of AreTypesAssignable that achieves better performance + // than AreTypesAssignableInternal through 2 significant changes + // 1. Removal of sourceType to targetType check (This propery must be known before calling this function. At time + // of writing, this is true as its is only used if sourceType is from an object, and targetType is an interface.) + // 2. Force inlining (This particular variant is only used in a small number of dispatch scenarios that are particularly + // high in performance impact.) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe bool AreTypesAssignableInternal_SourceNotTarget_BoxedSource(EEType* pSourceType, EEType* pTargetType) + { + Debug.Assert(pSourceType != pTargetType, "target is source"); + Key key = new Key(pSourceType, pTargetType, AssignmentVariation.BoxedSource); + Entry entry = LookupInCache(s_cache, ref key); + if (entry == null) + return CacheMiss(ref key); + + return entry.Result; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Entry LookupInCache(Entry[] cache, ref Key key) { diff --git a/external/corert/src/Runtime.Base/src/System/RuntimeTypeHandle.cs b/external/corert/src/Runtime.Base/src/System/RuntimeTypeHandle.cs index 19654f8836..37c592696a 100644 --- a/external/corert/src/Runtime.Base/src/System/RuntimeTypeHandle.cs +++ b/external/corert/src/Runtime.Base/src/System/RuntimeTypeHandle.cs @@ -11,17 +11,17 @@ using System.Runtime.InteropServices; namespace System { - internal class Type + public class Type { public RuntimeTypeHandle TypeHandle { get { return default(RuntimeTypeHandle); } } } [StructLayout(LayoutKind.Sequential)] - internal struct RuntimeTypeHandle + public struct RuntimeTypeHandle { private EETypePtr _pEEType; -#if CORERT +#if !PROJECTN [Intrinsic] internal static unsafe IntPtr GetValueInternal(RuntimeTypeHandle handle) { diff --git a/external/corert/src/System.Private.CoreLib/shared/Internal/Runtime/CompilerServices/Unsafe.cs b/external/corert/src/System.Private.CoreLib/shared/Internal/Runtime/CompilerServices/Unsafe.cs new file mode 100644 index 0000000000..cd3dd052a2 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/Internal/Runtime/CompilerServices/Unsafe.cs @@ -0,0 +1,331 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.Versioning; + +#if BIT64 +using nuint = System.UInt64; +using nint = System.Int64; +#else +using nuint = System.UInt32; +using nint = System.Int32; +#endif + +// +// The implementations of most the methods in this file are provided as intrinsics. +// In CoreCLR, the body of the functions are replaced by the EE with unsafe code. See see getILIntrinsicImplementationForUnsafe for details. +// In CoreRT, see Internal.IL.Stubs.UnsafeIntrinsics for details. +// + +namespace Internal.Runtime.CompilerServices +{ + // + // Subsetted clone of System.Runtime.CompilerServices.Unsafe for internal runtime use. + // Keep in sync with https://github.com/dotnet/corefx/tree/master/src/System.Runtime.CompilerServices.Unsafe. + // + + /// + /// For internal use only. Contains generic, low-level functionality for manipulating pointers. + /// + [CLSCompliant(false)] + public static unsafe class Unsafe + { + /// + /// Returns a pointer to the given by-ref parameter. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void* AsPointer(ref T value) + { + throw new PlatformNotSupportedException(); + + // ldarg.0 + // conv.u + // ret + } + + /// + /// Returns the size of an object of the given type parameter. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int SizeOf() + { +#if CORECLR + typeof(T).ToString(); // Type token used by the actual method body +#endif + throw new PlatformNotSupportedException(); + + // sizeof !!0 + // ret + } + + /// + /// Casts the given object to the specified type, performs no dynamic type checking. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T As(object value) where T : class + { + throw new PlatformNotSupportedException(); + + // ldarg.0 + // ret + } + + /// + /// Reinterprets the given reference as a reference to a value of type . + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref TTo As(ref TFrom source) + { + throw new PlatformNotSupportedException(); + + // ldarg.0 + // ret + } + + /// + /// Adds an element offset to the given reference. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T Add(ref T source, int elementOffset) + { +#if CORECLR + typeof(T).ToString(); // Type token used by the actual method body + throw new PlatformNotSupportedException(); +#else + return ref AddByteOffset(ref source, (IntPtr)(elementOffset * (nint)SizeOf())); +#endif + } + + /// + /// Adds an element offset to the given reference. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T Add(ref T source, IntPtr elementOffset) + { +#if CORECLR + typeof(T).ToString(); // Type token used by the actual method body + throw new PlatformNotSupportedException(); +#else + return ref AddByteOffset(ref source, (IntPtr)((nint)elementOffset * (nint)SizeOf())); +#endif + } + + /// + /// Adds an element offset to the given pointer. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void* Add(void* source, int elementOffset) + { +#if CORECLR + typeof(T).ToString(); // Type token used by the actual method body + throw new PlatformNotSupportedException(); +#else + return (byte*)source + (elementOffset * (nint)SizeOf()); +#endif + } + + /// + /// Adds an element offset to the given reference. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ref T AddByteOffset(ref T source, nuint byteOffset) + { + return ref AddByteOffset(ref source, (IntPtr)(void*)byteOffset); + } + + /// + /// Determines whether the specified references point to the same location. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool AreSame(ref T left, ref T right) + { + throw new PlatformNotSupportedException(); + + // ldarg.0 + // ldarg.1 + // ceq + // ret + } + + /// + /// Initializes a block of memory at the given location with a given initial value + /// without assuming architecture dependent alignment of the address. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void InitBlockUnaligned(ref byte startAddress, byte value, uint byteCount) + { + for (uint i = 0; i < byteCount; i++) + AddByteOffset(ref startAddress, i) = value; + } + + /// + /// Reads a value of type from the given location. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T ReadUnaligned(void* source) + { +#if CORECLR + typeof(T).ToString(); // Type token used by the actual method body + throw new PlatformNotSupportedException(); +#else + return Unsafe.As(ref *(byte*)source); +#endif + } + + /// + /// Reads a value of type from the given location. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T ReadUnaligned(ref byte source) + { +#if CORECLR + typeof(T).ToString(); // Type token used by the actual method body + throw new PlatformNotSupportedException(); +#else + return Unsafe.As(ref source); +#endif + } + + /// + /// Writes a value of type to the given location. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteUnaligned(void* destination, T value) + { +#if CORECLR + typeof(T).ToString(); // Type token used by the actual method body + throw new PlatformNotSupportedException(); +#else + Unsafe.As(ref *(byte*)destination) = value; +#endif + } + + /// + /// Writes a value of type to the given location. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteUnaligned(ref byte destination, T value) + { +#if CORECLR + typeof(T).ToString(); // Type token used by the actual method body + throw new PlatformNotSupportedException(); +#else + Unsafe.As(ref destination) = value; +#endif + } + + /// + /// Adds an element offset to the given reference. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T AddByteOffset(ref T source, IntPtr byteOffset) + { + // This method is implemented by the toolchain + throw new PlatformNotSupportedException(); + + // ldarg.0 + // ldarg.1 + // add + // ret + } + + /// + /// Reads a value of type from the given location. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Read(void* source) + { + return Unsafe.As(ref *(byte*)source); + } + + /// + /// Reads a value of type from the given location. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Read(ref byte source) + { + return Unsafe.As(ref source); + } + + /// + /// Writes a value of type to the given location. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Write(void* destination, T value) + { + Unsafe.As(ref *(byte*)destination) = value; + } + + /// + /// Writes a value of type to the given location. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Write(ref byte destination, T value) + { + Unsafe.As(ref destination) = value; + } + + /// + /// Reinterprets the given location as a reference to a value of type . + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T AsRef(void* source) + { + return ref Unsafe.As(ref *(byte*)source); + } + + /// + /// Determines the byte offset from origin to target from the given references. + /// + [Intrinsic] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IntPtr ByteOffset(ref T origin, ref T target) + { + throw new PlatformNotSupportedException(); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/Interop.IOErrors.cs b/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/Interop.IOErrors.cs index e9d6ce61d6..5babcd1d05 100644 --- a/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/Interop.IOErrors.cs +++ b/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/Interop.IOErrors.cs @@ -138,7 +138,9 @@ internal static partial class Interop new UnauthorizedAccessException(SR.UnauthorizedAccess_IODenied_NoPathName, inner); case Error.ENAMETOOLONG: - return new PathTooLongException(SR.IO_PathTooLong); + return !string.IsNullOrEmpty(path) ? + new PathTooLongException(SR.Format(SR.IO_PathTooLong_Path, path)) : + new PathTooLongException(SR.IO_PathTooLong); case Error.EWOULDBLOCK: return !string.IsNullOrEmpty(path) ? diff --git a/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Collation.cs b/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Collation.cs index 79aedd74d3..683845dbc1 100644 --- a/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Collation.cs +++ b/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Collation.cs @@ -44,7 +44,7 @@ internal static partial class Interop internal unsafe static extern int CompareStringOrdinalIgnoreCase(char* lpStr1, int cwStr1Len, char* lpStr2, int cwStr2Len); [DllImport(Libraries.GlobalizationInterop, EntryPoint = "GlobalizationNative_GetSortVersion")] - internal static extern int GetSortVersion(); + internal static extern int GetSortVersion(SafeSortHandle sortHandle); internal class SafeSortHandle : SafeHandle { diff --git a/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.ICU.cs b/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.ICU.cs new file mode 100644 index 0000000000..c690884145 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.ICU.cs @@ -0,0 +1,16 @@ +// 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.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +internal static partial class Interop +{ + internal static partial class GlobalizationInterop + { + [DllImport(Libraries.GlobalizationInterop, EntryPoint = "GlobalizationNative_LoadICU")] + internal static extern int LoadICU(); + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.TimeZoneInfo.cs b/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.TimeZoneInfo.cs index 26a9fe0579..271ec3f9dc 100644 --- a/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.TimeZoneInfo.cs +++ b/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.TimeZoneInfo.cs @@ -9,9 +9,6 @@ internal static partial class Interop { internal static partial class GlobalizationInterop { - [DllImport(Libraries.GlobalizationInterop, CharSet = CharSet.Ansi, EntryPoint = "GlobalizationNative_ReadLink")] // readlink requires char* - internal static extern bool ReadLink(string filePath, [Out] StringBuilder result, uint resultCapacity); - // needs to be kept in sync with TimeZoneDisplayNameType in System.Globalization.Native internal enum TimeZoneDisplayNameType { diff --git a/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.GetCwd.cs b/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.GetCwd.cs index a27a35c9f5..a8bef6bde8 100644 --- a/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.GetCwd.cs +++ b/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.GetCwd.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Buffers; using System.Runtime.InteropServices; internal static partial class Interop @@ -10,7 +11,7 @@ internal static partial class Interop internal static partial class Sys { [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetCwd", SetLastError = true)] - private static unsafe extern byte* GetCwd(byte* buffer, int bufferLength); + private static extern unsafe byte* GetCwd(byte* buffer, int bufferLength); internal static unsafe string GetCwd() { @@ -25,15 +26,13 @@ internal static partial class Interop } // If that was too small, try increasing large buffer sizes - // until we get one that works or until we hit MaxPath. - int maxPath = Interop.Sys.MaxPath; - if (StackLimit < maxPath) + int bufferSize = StackLimit; + do { - int bufferSize = StackLimit; - do + checked { bufferSize *= 2; } + byte[] buf = ArrayPool.Shared.Rent(bufferSize); + try { - checked { bufferSize *= 2; } - var buf = new byte[Math.Min(bufferSize, maxPath)]; fixed (byte* ptr = &buf[0]) { result = GetCwdHelper(ptr, buf.Length); @@ -43,11 +42,12 @@ internal static partial class Interop } } } - while (bufferSize < maxPath); + finally + { + ArrayPool.Shared.Return(buf); + } } - - // If we couldn't get the cwd with a MaxPath-sized buffer, something's wrong. - throw Interop.GetExceptionForIoErrno(new ErrorInfo(Interop.Error.ENAMETOOLONG)); + while (true); } private static unsafe string GetCwdHelper(byte* ptr, int bufferSize) diff --git a/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.PathConf.cs b/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.PathConf.cs index 4a1fcf67d0..eb9e32db0c 100644 --- a/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.PathConf.cs +++ b/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.PathConf.cs @@ -24,50 +24,7 @@ internal static partial class Interop PC_VDISABLE = 9, } - /// The maximum path length for the system. -1 if it hasn't yet been initialized. - private static int s_maxPath = -1; - - /// The maximum name length for the system. -1 if it hasn't yet been initialized. - private static int s_maxName = -1; - - internal static int MaxPath - { - get - { - // Benign race condition on cached value - if (s_maxPath < 0) - { - // GetMaximumPath returns a long from PathConf - // but our callers expect an int so we need to convert. - long temp = GetMaximumPath(); - if (temp > int.MaxValue) - s_maxPath = int.MaxValue; - else - s_maxPath = Convert.ToInt32(temp); - } - return s_maxPath; - } - } - - internal static int MaxName - { - get - { - // Benign race condition on cached value - if (s_maxName < 0) - { - int result = PathConf("/", PathConfName.PC_NAME_MAX); - s_maxName = result >= 0 ? result : DEFAULT_PC_NAME_MAX; - } - - return s_maxName; - } - } - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_PathConf", SetLastError = true)] private static extern int PathConf(string path, PathConfName name); - - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetMaximumPath")] - private static extern long GetMaximumPath(); } } diff --git a/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.PosixFAdvise.cs b/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.PosixFAdvise.cs index 69e39b30d2..ad8b73aed2 100644 --- a/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.PosixFAdvise.cs +++ b/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.PosixFAdvise.cs @@ -16,7 +16,7 @@ internal static partial class Interop POSIX_FADV_SEQUENTIAL = 2, /* sequential I/O access */ POSIX_FADV_WILLNEED = 3, /* will need specified pages */ POSIX_FADV_DONTNEED = 4, /* don't need the specified pages */ - POSIX_FADV_NOREUSE = 5, /* data will only be acessed once */ + POSIX_FADV_NOREUSE = 5, /* data will only be accessed once */ } /// diff --git a/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.ReadLink.cs b/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.ReadLink.cs new file mode 100644 index 0000000000..50f1ae545e --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/Interop/Unix/System.Native/Interop.ReadLink.cs @@ -0,0 +1,63 @@ +// 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.Runtime.InteropServices; +using System.Buffers; +using System.Text; + +internal static partial class Interop +{ + internal static partial class Sys + { + /// + /// Takes a path to a symbolic link and attempts to place the link target path into the buffer. If the buffer is too + /// small, the path will be truncated. No matter what, the buffer will not be null terminated. + /// + /// The path to the symlink + /// The buffer to hold the output path + /// The size of the buffer + /// + /// Returns the number of bytes placed into the buffer on success; bufferSize if the buffer is too small; and -1 on error. + /// + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_ReadLink", SetLastError = true)] + private static extern unsafe int ReadLink(string path, byte[] buffer, int bufferSize); + + /// + /// Takes a path to a symbolic link and returns the link target path. + /// + /// The path to the symlink + /// + /// Returns the link to the target path on success; and null otherwise. + /// + public static string ReadLink(string path) + { + int bufferSize = 256; + do + { + byte[] buffer = ArrayPool.Shared.Rent(bufferSize); + try + { + int resultLength = Interop.Sys.ReadLink(path, buffer, buffer.Length); + if (resultLength < 0) + { + // error + return null; + } + else if (resultLength < buffer.Length) + { + // success + return Encoding.UTF8.GetString(buffer, 0, resultLength); + } + } + finally + { + ArrayPool.Shared.Return(buffer); + } + + // buffer was too small, loop around again and try with a larger buffer. + bufferSize *= 2; + } while (true); + } + } +} \ No newline at end of file diff --git a/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Interop.Errors.cs b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Interop.Errors.cs index ff2653765c..ec52a81bf6 100644 --- a/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Interop.Errors.cs +++ b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Interop.Errors.cs @@ -40,5 +40,6 @@ internal partial class Interop internal const int ERROR_NOT_FOUND = 0x490; internal const int ERROR_BAD_IMPERSONATION_LEVEL = 0x542; internal const int E_FILENOTFOUND = unchecked((int)0x80070002); + internal const int ERROR_TIMEOUT = 0x000005B4; } } diff --git a/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Interop.Libraries.cs b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Interop.Libraries.cs index bf07a6815b..45d910bfcc 100644 --- a/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Interop.Libraries.cs +++ b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Interop.Libraries.cs @@ -9,6 +9,8 @@ internal static partial class Interop internal const string BCrypt = "BCrypt.dll"; internal const string Crypt32 = "crypt32.dll"; internal const string Kernel32 = "kernel32.dll"; + internal const string Ole32 = "ole32.dll"; internal const string OleAut32 = "oleaut32.dll"; + internal const string User32 = "user32.dll"; } } diff --git a/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs new file mode 100644 index 0000000000..16365ee651 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs @@ -0,0 +1,24 @@ +// 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 Microsoft.Win32.SafeHandles; +using System; +using System.IO; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + internal unsafe struct CREATEFILE2_EXTENDED_PARAMETERS + { + internal uint dwSize; + internal uint dwFileAttributes; + internal uint dwFileFlags; + internal uint dwSecurityQosFlags; + internal SECURITY_ATTRIBUTES* lpSecurityAttributes; + internal IntPtr hTemplateFile; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.CreateFile2.cs b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.CreateFile2.cs index 0909d3a6c8..ddc18f6c42 100644 --- a/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.CreateFile2.cs +++ b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.CreateFile2.cs @@ -3,29 +3,30 @@ // See the LICENSE file in the project root for more information. using Microsoft.Win32.SafeHandles; -using System; +using System.IO; using System.Runtime.InteropServices; internal partial class Interop { internal partial class Kernel32 { - [DllImport(Libraries.Kernel32, EntryPoint = "CreateFile2", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)] - internal static extern unsafe SafeFileHandle CreateFile2( + [DllImport(Libraries.Kernel32, EntryPoint = "CreateFile2", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern SafeFileHandle CreateFile2Private( string lpFileName, int dwDesiredAccess, - System.IO.FileShare dwShareMode, - System.IO.FileMode dwCreationDisposition, - CREATEFILE2_EXTENDED_PARAMETERS* pCreateExParams); + FileShare dwShareMode, + FileMode dwCreationDisposition, + ref Kernel32.CREATEFILE2_EXTENDED_PARAMETERS pCreateExParams); - internal unsafe struct CREATEFILE2_EXTENDED_PARAMETERS + internal static SafeFileHandle CreateFile2( + string lpFileName, + int dwDesiredAccess, + FileShare dwShareMode, + FileMode dwCreationDisposition, + ref Kernel32.CREATEFILE2_EXTENDED_PARAMETERS pCreateExParams) { - internal uint dwSize; - internal uint dwFileAttributes; - internal uint dwFileFlags; - internal uint dwSecurityQosFlags; - internal SECURITY_ATTRIBUTES* lpSecurityAttributes; - internal IntPtr hTemplateFile; + lpFileName = PathInternal.EnsureExtendedPrefixOverMaxPath(lpFileName); + return CreateFile2Private(lpFileName, dwDesiredAccess, dwShareMode, dwCreationDisposition, ref pCreateExParams); } } -} \ No newline at end of file +} diff --git a/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FreeLibrary.cs b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FreeLibrary.cs new file mode 100644 index 0000000000..c70865350a --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FreeLibrary.cs @@ -0,0 +1,15 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)] + internal static extern bool FreeLibrary(IntPtr hModule); + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.Globalization.cs b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.Globalization.cs index 7d3287fe1a..00bec5d3e2 100644 --- a/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.Globalization.cs +++ b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.Globalization.cs @@ -85,8 +85,10 @@ internal static partial class Interop char* lpString, int cchStr); +#if !PROJECTN [DllImport("Kernel32.dll", CharSet = CharSet.Auto)] internal static extern bool GetUserPreferredUILanguages(uint dwFlags, out uint pulNumLanguages, char [] pwszLanguagesBuffer, ref uint pcchLanguagesBuffer); +#endif //!PROJECTN [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] internal static extern int GetLocaleInfoEx(string lpLocaleName, uint LCType, void* lpLCData, int cchData); diff --git a/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.LoadLibraryEx.cs b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.LoadLibraryEx.cs new file mode 100644 index 0000000000..4eef5852fe --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.LoadLibraryEx.cs @@ -0,0 +1,18 @@ +// 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 Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + internal const int LOAD_LIBRARY_AS_DATAFILE = 0x00000002; + + [DllImport(Libraries.Kernel32, EntryPoint = "LoadLibraryExW", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern SafeLibraryHandle LoadLibraryEx(string libFilename, IntPtr reserved, int flags); + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Normaliz/Interop.Idna.cs b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Normaliz/Interop.Idna.cs new file mode 100644 index 0000000000..f49ce8dd61 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Normaliz/Interop.Idna.cs @@ -0,0 +1,35 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Normaliz + { + // + // Idn APIs + // + + [DllImport("Normaliz.dll", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern unsafe int IdnToAscii( + uint dwFlags, + char* lpUnicodeCharStr, + int cchUnicodeChar, + char* lpASCIICharStr, + int cchASCIIChar); + + [DllImport("Normaliz.dll", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern unsafe int IdnToUnicode( + uint dwFlags, + char* lpASCIICharStr, + int cchASCIIChar, + char* lpUnicodeCharStr, + int cchUnicodeChar); + + internal const int IDN_ALLOW_UNASSIGNED = 0x1; + internal const int IDN_USE_STD3_ASCII_RULES = 0x2; + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Normaliz/Interop.Normalization.cs b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Normaliz/Interop.Normalization.cs new file mode 100644 index 0000000000..3e49f1f64c --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/Normaliz/Interop.Normalization.cs @@ -0,0 +1,24 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Normaliz + { + [DllImport("Normaliz.dll", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern bool IsNormalizedString(int normForm, string source, int length); + + [DllImport("Normaliz.dll", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern int NormalizeString( + int normForm, + string source, + int sourceLength, + [System.Runtime.InteropServices.OutAttribute()] + char[] destination, + int destinationLength); + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/User32/Interop.Constants.cs b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/User32/Interop.Constants.cs new file mode 100644 index 0000000000..f1f09740e9 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/User32/Interop.Constants.cs @@ -0,0 +1,12 @@ +// 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. + +internal partial class Interop +{ + internal partial class User32 + { + internal const int HWND_BROADCAST = 0xffff; + internal const int WM_SETTINGCHANGE = 0x001A; + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/User32/Interop.LoadString.cs b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/User32/Interop.LoadString.cs new file mode 100644 index 0000000000..d3d575e221 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/User32/Interop.LoadString.cs @@ -0,0 +1,16 @@ +// 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.Runtime.InteropServices; +using System.Text; +using Microsoft.Win32.SafeHandles; + +internal partial class Interop +{ + internal partial class User32 + { + [DllImport(Libraries.User32, SetLastError = true, EntryPoint = "LoadStringW", CharSet = CharSet.Unicode)] + internal static extern int LoadString(SafeLibraryHandle handle, int id, [Out] StringBuilder buffer, int bufferLength); + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/User32/Interop.SendMessageTimeout.cs b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/User32/Interop.SendMessageTimeout.cs new file mode 100644 index 0000000000..c8a97e6b9d --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/Interop/Windows/User32/Interop.SendMessageTimeout.cs @@ -0,0 +1,15 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class User32 + { + [DllImport(Libraries.User32, EntryPoint = "SendMessageTimeoutW", CharSet = CharSet.Unicode)] + public static extern IntPtr SendMessageTimeout(IntPtr hWnd, int msg, IntPtr wParam, string lParam, int flags, int timeout, IntPtr pdwResult); + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/CriticalHandleZeroOrMinusOneIsInvalid.cs b/external/corert/src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/CriticalHandleZeroOrMinusOneIsInvalid.cs index 195e48df76..28d0219489 100644 --- a/external/corert/src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/CriticalHandleZeroOrMinusOneIsInvalid.cs +++ b/external/corert/src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/CriticalHandleZeroOrMinusOneIsInvalid.cs @@ -15,6 +15,6 @@ namespace Microsoft.Win32.SafeHandles { } - public override bool IsInvalid => handle.IsNull() || handle == new IntPtr(-1); + public override bool IsInvalid => handle == IntPtr.Zero || handle == new IntPtr(-1); } } diff --git a/external/corert/src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/SafeLibraryHandle.cs b/external/corert/src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/SafeLibraryHandle.cs new file mode 100644 index 0000000000..3be2e354ab --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/SafeLibraryHandle.cs @@ -0,0 +1,16 @@ +// 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. + +namespace Microsoft.Win32.SafeHandles +{ + sealed internal class SafeLibraryHandle : SafeHandleZeroOrMinusOneIsInvalid + { + internal SafeLibraryHandle() : base(true) { } + + override protected bool ReleaseHandle() + { + return Interop.Kernel32.FreeLibrary(handle); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems b/external/corert/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems index 7454068078..aaf6bfcf7c 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems +++ b/external/corert/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems @@ -6,183 +6,258 @@ c5ed3c1d-b572-46f1-8f96-522a85ce1179 - + false false false + + + true + + - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -234,6 +309,7 @@ + @@ -251,6 +327,15 @@ + + + + + + + + + @@ -261,163 +346,225 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + @@ -461,111 +608,135 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/corert/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.shproj b/external/corert/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.shproj deleted file mode 100644 index af61b3b59e..0000000000 --- a/external/corert/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.shproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - c5ed3c1d-b572-46f1-8f96-522a85ce1179 - 14.0 - - - - true - true - true - - - - - - - - diff --git a/external/corert/src/System.Private.CoreLib/shared/System/AccessViolationException.cs b/external/corert/src/System.Private.CoreLib/shared/System/AccessViolationException.cs new file mode 100644 index 0000000000..280d9b8a97 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/AccessViolationException.cs @@ -0,0 +1,51 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: Exception class representing an AV that was deemed unsafe and may have corrupted the application. +** +** +=============================================================================*/ + +using System; +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class AccessViolationException : SystemException + { + public AccessViolationException() + : base(SR.Arg_AccessViolationException) + { + HResult = HResults.E_POINTER; + } + + public AccessViolationException(String message) + : base(message) + { + HResult = HResults.E_POINTER; + } + + public AccessViolationException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.E_POINTER; + } + + protected AccessViolationException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + +#pragma warning disable 169 // Field is not used from managed. + private IntPtr _ip; // Address of faulting instruction. + private IntPtr _target; // Address that could not be accessed. + private int _accessType; // 0:read, 1:write +#pragma warning restore 169 + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Action.cs b/external/corert/src/System.Private.CoreLib/shared/System/Action.cs index b82c14d9dc..6e3ccff48c 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Action.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Action.cs @@ -4,24 +4,21 @@ namespace System { - public delegate void Action(T obj); - public delegate void Action(); + public delegate void Action(T obj); public delegate void Action(T1 arg1, T2 arg2); public delegate void Action(T1 arg1, T2 arg2, T3 arg3); public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4); + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6); + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7); + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8); public delegate TResult Func(); public delegate TResult Func(T arg); public delegate TResult Func(T1 arg1, T2 arg2); public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3); public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4); - - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6); public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7); @@ -33,3 +30,9 @@ namespace System public delegate bool Predicate(T obj); } + +namespace System.Buffers +{ + public delegate void SpanAction(Span span, TArg arg); + public delegate void ReadOnlySpanAction(ReadOnlySpan span, TArg arg); +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/AggregateException.cs b/external/corert/src/System.Private.CoreLib/shared/System/AggregateException.cs similarity index 89% rename from external/corert/src/System.Private.CoreLib/src/System/AggregateException.cs rename to external/corert/src/System.Private.CoreLib/shared/System/AggregateException.cs index dac92a3081..36b9494980 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/AggregateException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/AggregateException.cs @@ -2,12 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -// -// Public type to communicate multiple failures to an end-user. -// -// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -28,9 +22,10 @@ namespace System /// [Serializable] [DebuggerDisplay("Count = {InnerExceptionCount}")] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class AggregateException : Exception { - private ReadOnlyCollection _innerExceptions; // Complete set of exceptions. + private ReadOnlyCollection m_innerExceptions; // Complete set of exceptions. Do not rename (binary serialization) /// /// Initializes a new instance of the class. @@ -38,7 +33,7 @@ namespace System public AggregateException() : base(SR.AggregateException_ctor_DefaultMessage) { - _innerExceptions = new ReadOnlyCollection(Array.Empty()); + m_innerExceptions = new ReadOnlyCollection(Array.Empty()); } /// @@ -49,7 +44,7 @@ namespace System public AggregateException(string message) : base(message) { - _innerExceptions = new ReadOnlyCollection(Array.Empty()); + m_innerExceptions = new ReadOnlyCollection(Array.Empty()); } /// @@ -68,7 +63,7 @@ namespace System throw new ArgumentNullException(nameof(innerException)); } - _innerExceptions = new ReadOnlyCollection(new Exception[] { innerException }); + m_innerExceptions = new ReadOnlyCollection(new Exception[] { innerException }); } /// @@ -112,7 +107,7 @@ namespace System public AggregateException(string message, IEnumerable innerExceptions) // If it's already an IList, pass that along (a defensive copy will be made in the delegated ctor). If it's null, just pass along // null typed correctly. Otherwise, create an IList from the enumerable and pass that along. - : this(message, innerExceptions as IList ?? (innerExceptions == null ? (IList)null : new LowLevelListWithIList(innerExceptions))) + : this(message, innerExceptions as IList ?? (innerExceptions == null ? (List)null : new List(innerExceptions))) { } @@ -163,7 +158,7 @@ namespace System } } - _innerExceptions = new ReadOnlyCollection(exceptionsCopy); + m_innerExceptions = new ReadOnlyCollection(exceptionsCopy); } /// @@ -200,8 +195,8 @@ namespace System // null typed correctly. Otherwise, create an IList from the enumerable and pass that along. : this(message, innerExceptionInfos as IList ?? (innerExceptionInfos == null ? - (LowLevelListWithIList)null : - new LowLevelListWithIList(innerExceptionInfos))) + (List)null : + new List(innerExceptionInfos))) { } @@ -242,7 +237,7 @@ namespace System } } - _innerExceptions = new ReadOnlyCollection(exceptionsCopy); + m_innerExceptions = new ReadOnlyCollection(exceptionsCopy); } /// @@ -254,16 +249,21 @@ namespace System /// contains contextual information about the source or destination. /// The argument is null. /// The exception could not be deserialized correctly. - protected AggregateException(SerializationInfo info, StreamingContext context) - : base(info, context) + protected AggregateException(SerializationInfo info, StreamingContext context) : + base(info, context) { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + Exception[] innerExceptions = info.GetValue("InnerExceptions", typeof(Exception[])) as Exception[]; if (innerExceptions == null) { throw new SerializationException(SR.AggregateException_DeserializationFailure); } - _innerExceptions = new ReadOnlyCollection(innerExceptions); + m_innerExceptions = new ReadOnlyCollection(innerExceptions); } /// @@ -279,8 +279,8 @@ namespace System { base.GetObjectData(info, context); - Exception[] innerExceptions = new Exception[_innerExceptions.Count]; - _innerExceptions.CopyTo(innerExceptions, 0); + Exception[] innerExceptions = new Exception[m_innerExceptions.Count]; + m_innerExceptions.CopyTo(innerExceptions, 0); info.AddValue("InnerExceptions", innerExceptions, typeof(Exception[])); } @@ -308,7 +308,7 @@ namespace System /// public ReadOnlyCollection InnerExceptions { - get { return _innerExceptions; } + get { return m_innerExceptions; } } @@ -338,19 +338,19 @@ namespace System throw new ArgumentNullException(nameof(predicate)); } - LowLevelListWithIList unhandledExceptions = null; - for (int i = 0; i < _innerExceptions.Count; i++) + List unhandledExceptions = null; + for (int i = 0; i < m_innerExceptions.Count; i++) { // If the exception was not handled, lazily allocate a list of unhandled // exceptions (to be rethrown later) and add it. - if (!predicate(_innerExceptions[i])) + if (!predicate(m_innerExceptions[i])) { if (unhandledExceptions == null) { - unhandledExceptions = new LowLevelListWithIList(); + unhandledExceptions = new List(); } - unhandledExceptions.Add(_innerExceptions[i]); + unhandledExceptions.Add(m_innerExceptions[i]); } } @@ -363,23 +363,24 @@ namespace System /// - /// Flattens an instances into a single, new instance. + /// Flattens the inner instances of by expanding its contained instances + /// into a new /// /// A new, flattened . /// /// If any inner exceptions are themselves instances of /// , this method will recursively flatten all of them. The /// inner exceptions returned in the new - /// will be the union of all of the the inner exceptions from exception tree rooted at the provided + /// will be the union of all of the inner exceptions from exception tree rooted at the provided /// instance. /// public AggregateException Flatten() { // Initialize a collection to contain the flattened exceptions. - LowLevelListWithIList flattenedExceptions = new LowLevelListWithIList(); + List flattenedExceptions = new List(); // Create a list to remember all aggregates to be flattened, this will be accessed like a FIFO queue - LowLevelList exceptionsToFlatten = new LowLevelList(); + List exceptionsToFlatten = new List(); exceptionsToFlatten.Add(this); int nDequeueIndex = 0; @@ -422,22 +423,22 @@ namespace System { get { - if (_innerExceptions.Count == 0) + if (m_innerExceptions.Count == 0) { return base.Message; } - StringBuilder sb = new StringBuilder(); + StringBuilder sb = StringBuilderCache.Acquire(); sb.Append(base.Message); sb.Append(' '); - for (int i = 0; i < _innerExceptions.Count; i++) + for (int i = 0; i < m_innerExceptions.Count; i++) { sb.Append('('); - sb.Append(_innerExceptions[i].Message); + sb.Append(m_innerExceptions[i].Message); sb.Append(") "); } sb.Length -= 1; - return sb.ToString(); + return StringBuilderCache.GetStringAndRelease(sb); } } @@ -450,12 +451,12 @@ namespace System StringBuilder text = new StringBuilder(); text.Append(base.ToString()); - for (int i = 0; i < _innerExceptions.Count; i++) + for (int i = 0; i < m_innerExceptions.Count; i++) { text.Append(Environment.NewLine); text.Append("---> "); text.Append(string.Format(CultureInfo.InvariantCulture, SR.AggregateException_InnerException, i)); - text.Append(_innerExceptions[i].ToString()); + text.Append(m_innerExceptions[i].ToString()); text.Append("<---"); text.Append(Environment.NewLine); } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/ApplicationException.cs b/external/corert/src/System.Private.CoreLib/shared/System/ApplicationException.cs index cb98902de1..f36e2c1274 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/ApplicationException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/ApplicationException.cs @@ -22,7 +22,8 @@ namespace System // to create their own exceptions do so by extending this class. // ApplicationException extends but adds no new functionality to // RecoverableException. - // + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class ApplicationException : Exception { // Creates a new ApplicationException with its message string set to @@ -31,7 +32,7 @@ namespace System public ApplicationException() : base(SR.Arg_ApplicationException) { - HResult = __HResults.COR_E_APPLICATION; + HResult = HResults.COR_E_APPLICATION; } // Creates a new ApplicationException with its message string set to @@ -41,18 +42,17 @@ namespace System public ApplicationException(String message) : base(message) { - HResult = __HResults.COR_E_APPLICATION; + HResult = HResults.COR_E_APPLICATION; } public ApplicationException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_APPLICATION; + HResult = HResults.COR_E_APPLICATION; } protected ApplicationException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/ArgumentException.cs b/external/corert/src/System.Private.CoreLib/shared/System/ArgumentException.cs index de2d775c84..8a8fe3e5e4 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/ArgumentException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/ArgumentException.cs @@ -19,7 +19,8 @@ namespace System // The ArgumentException is thrown when an argument does not meet // the contract of the method. Ideally it should give a meaningful error // message describing what was wrong and which parameter is incorrect. - // + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class ArgumentException : SystemException { private String _paramName; @@ -29,7 +30,7 @@ namespace System public ArgumentException() : base(SR.Arg_ArgumentException) { - HResult = __HResults.COR_E_ARGUMENT; + HResult = HResults.COR_E_ARGUMENT; } // Creates a new ArgumentException with its message @@ -38,38 +39,39 @@ namespace System public ArgumentException(String message) : base(message) { - HResult = __HResults.COR_E_ARGUMENT; + HResult = HResults.COR_E_ARGUMENT; } public ArgumentException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_ARGUMENT; + HResult = HResults.COR_E_ARGUMENT; } public ArgumentException(String message, String paramName, Exception innerException) : base(message, innerException) { _paramName = paramName; - HResult = __HResults.COR_E_ARGUMENT; + HResult = HResults.COR_E_ARGUMENT; } public ArgumentException(String message, String paramName) : base(message) { _paramName = paramName; - HResult = __HResults.COR_E_ARGUMENT; + HResult = HResults.COR_E_ARGUMENT; } protected ArgumentException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); + _paramName = info.GetString("ParamName"); } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + info.AddValue("ParamName", _paramName, typeof(string)); } public override String Message diff --git a/external/corert/src/System.Private.CoreLib/shared/System/ArgumentNullException.cs b/external/corert/src/System.Private.CoreLib/shared/System/ArgumentNullException.cs index 74b39fed8e..80e43cc265 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/ArgumentNullException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/ArgumentNullException.cs @@ -17,7 +17,8 @@ namespace System { // The ArgumentException is thrown when an argument // is null when it shouldn't be. - // + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class ArgumentNullException : ArgumentException { // Creates a new ArgumentNullException with its message @@ -26,30 +27,29 @@ namespace System : base(SR.ArgumentNull_Generic) { // Use E_POINTER - COM used that for null pointers. Description is "invalid pointer" - HResult = __HResults.E_POINTER; + HResult = HResults.E_POINTER; } public ArgumentNullException(String paramName) : base(SR.ArgumentNull_Generic, paramName) { - HResult = __HResults.E_POINTER; + HResult = HResults.E_POINTER; } public ArgumentNullException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.E_POINTER; + HResult = HResults.E_POINTER; } public ArgumentNullException(String paramName, String message) : base(message, paramName) { - HResult = __HResults.E_POINTER; + HResult = HResults.E_POINTER; } protected ArgumentNullException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/ArgumentOutOfRangeException.cs b/external/corert/src/System.Private.CoreLib/shared/System/ArgumentOutOfRangeException.cs index 4721a503b6..604caa8ee8 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/ArgumentOutOfRangeException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/ArgumentOutOfRangeException.cs @@ -17,7 +17,9 @@ using System.Runtime.Serialization; namespace System { // The ArgumentOutOfRangeException is thrown when an argument - // is outside the legal range for that argument. + // is outside the legal range for that argument. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class ArgumentOutOfRangeException : ArgumentException { private Object _actualValue; @@ -27,25 +29,25 @@ namespace System public ArgumentOutOfRangeException() : base(SR.Arg_ArgumentOutOfRangeException) { - HResult = __HResults.COR_E_ARGUMENTOUTOFRANGE; + HResult = HResults.COR_E_ARGUMENTOUTOFRANGE; } public ArgumentOutOfRangeException(String paramName) : base(SR.Arg_ArgumentOutOfRangeException, paramName) { - HResult = __HResults.COR_E_ARGUMENTOUTOFRANGE; + HResult = HResults.COR_E_ARGUMENTOUTOFRANGE; } public ArgumentOutOfRangeException(String paramName, String message) : base(message, paramName) { - HResult = __HResults.COR_E_ARGUMENTOUTOFRANGE; + HResult = HResults.COR_E_ARGUMENTOUTOFRANGE; } public ArgumentOutOfRangeException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_ARGUMENTOUTOFRANGE; + HResult = HResults.COR_E_ARGUMENTOUTOFRANGE; } // We will not use this in the classlibs, but we'll provide it for @@ -55,18 +57,19 @@ namespace System : base(message, paramName) { _actualValue = actualValue; - HResult = __HResults.COR_E_ARGUMENTOUTOFRANGE; + HResult = HResults.COR_E_ARGUMENTOUTOFRANGE; } protected ArgumentOutOfRangeException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); + _actualValue = info.GetValue("ActualValue", typeof(object)); } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + info.AddValue("ActualValue", _actualValue, typeof(object)); } public override String Message diff --git a/external/corert/src/System.Private.CoreLib/shared/System/ArithmeticException.cs b/external/corert/src/System.Private.CoreLib/shared/System/ArithmeticException.cs index 2c8abe51fa..606f1debfd 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/ArithmeticException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/ArithmeticException.cs @@ -17,7 +17,8 @@ namespace System { // The ArithmeticException is thrown when overflow or underflow // occurs. - // + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class ArithmeticException : SystemException { // Creates a new ArithmeticException with its message string set to @@ -26,7 +27,7 @@ namespace System public ArithmeticException() : base(SR.Arg_ArithmeticException) { - HResult = __HResults.COR_E_ARITHMETIC; + HResult = HResults.COR_E_ARITHMETIC; } // Creates a new ArithmeticException with its message string set to @@ -36,18 +37,17 @@ namespace System public ArithmeticException(String message) : base(message) { - HResult = __HResults.COR_E_ARITHMETIC; + HResult = HResults.COR_E_ARITHMETIC; } public ArithmeticException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_ARITHMETIC; + HResult = HResults.COR_E_ARITHMETIC; } protected ArithmeticException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/ArraySegment.cs b/external/corert/src/System.Private.CoreLib/shared/System/ArraySegment.cs new file mode 100644 index 0000000000..d45fb0dc2b --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/ArraySegment.cs @@ -0,0 +1,368 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: Convenient wrapper for an array, an offset, and +** a count. Ideally used in streams & collections. +** Net Classes will consume an array of these. +** +** +===========================================================*/ + +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; + +namespace System +{ + // Note: users should make sure they copy the fields out of an ArraySegment onto their stack + // then validate that the fields describe valid bounds within the array. This must be done + // because assignments to value types are not atomic, and also because one thread reading + // three fields from an ArraySegment may not see the same ArraySegment from one call to another + // (ie, users could assign a new value to the old location). + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public readonly struct ArraySegment : IList, IReadOnlyList + { + // Do not replace the array allocation with Array.Empty. We don't want to have the overhead of + // instantiating another generic type in addition to ArraySegment for new type parameters. + public static ArraySegment Empty { get; } = new ArraySegment(new T[0]); + + private readonly T[] _array; // Do not rename (binary serialization) + private readonly int _offset; // Do not rename (binary serialization) + private readonly int _count; // Do not rename (binary serialization) + + public ArraySegment(T[] array) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + + _array = array; + _offset = 0; + _count = array.Length; + } + + public ArraySegment(T[] array, int offset, int count) + { + // Validate arguments, check is minimal instructions with reduced branching for inlinable fast-path + // Negative values discovered though conversion to high values when converted to unsigned + // Failure should be rare and location determination and message is delegated to failure functions + if (array == null || (uint)offset > (uint)array.Length || (uint)count > (uint)(array.Length - offset)) + ThrowHelper.ThrowArraySegmentCtorValidationFailedExceptions(array, offset, count); + + _array = array; + _offset = offset; + _count = count; + } + + public T[] Array => _array; + + public int Offset => _offset; + + public int Count => _count; + + public T this[int index] + { + get + { + if ((uint)index >= (uint)_count) + { + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + } + + return _array[_offset + index]; + } + set + { + if ((uint)index >= (uint)_count) + { + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + } + + _array[_offset + index] = value; + } + } + + public Enumerator GetEnumerator() + { + ThrowInvalidOperationIfDefault(); + return new Enumerator(this); + } + + public override int GetHashCode() + { + if (_array == null) + { + return 0; + } + + int hash = 5381; + hash = System.Numerics.Hashing.HashHelpers.Combine(hash, _offset); + hash = System.Numerics.Hashing.HashHelpers.Combine(hash, _count); + + // The array hash is expected to be an evenly-distributed mixture of bits, + // so rather than adding the cost of another rotation we just xor it. + hash ^= _array.GetHashCode(); + return hash; + } + + public void CopyTo(T[] destination) => CopyTo(destination, 0); + + public void CopyTo(T[] destination, int destinationIndex) + { + ThrowInvalidOperationIfDefault(); + System.Array.Copy(_array, _offset, destination, destinationIndex, _count); + } + + public void CopyTo(ArraySegment destination) + { + ThrowInvalidOperationIfDefault(); + destination.ThrowInvalidOperationIfDefault(); + + if (_count > destination._count) + { + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + } + + System.Array.Copy(_array, _offset, destination._array, destination._offset, _count); + } + + public override bool Equals(Object obj) + { + if (obj is ArraySegment) + return Equals((ArraySegment)obj); + else + return false; + } + + public bool Equals(ArraySegment obj) + { + return obj._array == _array && obj._offset == _offset && obj._count == _count; + } + + public ArraySegment Slice(int index) + { + ThrowInvalidOperationIfDefault(); + + if ((uint)index > (uint)_count) + { + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + } + + return new ArraySegment(_array, _offset + index, _count - index); + } + + public ArraySegment Slice(int index, int count) + { + ThrowInvalidOperationIfDefault(); + + if ((uint)index > (uint)_count || (uint)count > (uint)(_count - index)) + { + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + } + + return new ArraySegment(_array, _offset + index, count); + } + + public T[] ToArray() + { + ThrowInvalidOperationIfDefault(); + + if (_count == 0) + { + return Empty._array; + } + + var array = new T[_count]; + System.Array.Copy(_array, _offset, array, 0, _count); + return array; + } + + public static bool operator ==(ArraySegment a, ArraySegment b) + { + return a.Equals(b); + } + + public static bool operator !=(ArraySegment a, ArraySegment b) + { + return !(a == b); + } + + public static implicit operator ArraySegment(T[] array) => new ArraySegment(array); + + #region IList + T IList.this[int index] + { + get + { + ThrowInvalidOperationIfDefault(); + if (index < 0 || index >= _count) + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + + return _array[_offset + index]; + } + + set + { + ThrowInvalidOperationIfDefault(); + if (index < 0 || index >= _count) + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + + _array[_offset + index] = value; + } + } + + int IList.IndexOf(T item) + { + ThrowInvalidOperationIfDefault(); + + int index = System.Array.IndexOf(_array, item, _offset, _count); + + Debug.Assert(index == -1 || + (index >= _offset && index < _offset + _count)); + + return index >= 0 ? index - _offset : -1; + } + + void IList.Insert(int index, T item) + { + ThrowHelper.ThrowNotSupportedException(); + } + + void IList.RemoveAt(int index) + { + ThrowHelper.ThrowNotSupportedException(); + } + #endregion + + #region IReadOnlyList + T IReadOnlyList.this[int index] + { + get + { + ThrowInvalidOperationIfDefault(); + if (index < 0 || index >= _count) + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + + return _array[_offset + index]; + } + } + #endregion IReadOnlyList + + #region ICollection + bool ICollection.IsReadOnly + { + get + { + // the indexer setter does not throw an exception although IsReadOnly is true. + // This is to match the behavior of arrays. + return true; + } + } + + void ICollection.Add(T item) + { + ThrowHelper.ThrowNotSupportedException(); + } + + void ICollection.Clear() + { + ThrowHelper.ThrowNotSupportedException(); + } + + bool ICollection.Contains(T item) + { + ThrowInvalidOperationIfDefault(); + + int index = System.Array.IndexOf(_array, item, _offset, _count); + + Debug.Assert(index == -1 || + (index >= _offset && index < _offset + _count)); + + return index >= 0; + } + + bool ICollection.Remove(T item) + { + ThrowHelper.ThrowNotSupportedException(); + return default(bool); + } + #endregion + + #region IEnumerable + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + #endregion + + #region IEnumerable + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + #endregion + + private void ThrowInvalidOperationIfDefault() + { + if (_array == null) + { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NullArray); + } + } + + public struct Enumerator : IEnumerator + { + private readonly T[] _array; + private readonly int _start; + private readonly int _end; // cache Offset + Count, since it's a little slow + private int _current; + + internal Enumerator(ArraySegment arraySegment) + { + Debug.Assert(arraySegment.Array != null); + Debug.Assert(arraySegment.Offset >= 0); + Debug.Assert(arraySegment.Count >= 0); + Debug.Assert(arraySegment.Offset + arraySegment.Count <= arraySegment.Array.Length); + + _array = arraySegment.Array; + _start = arraySegment.Offset; + _end = arraySegment.Offset + arraySegment.Count; + _current = arraySegment.Offset - 1; + } + + public bool MoveNext() + { + if (_current < _end) + { + _current++; + return (_current < _end); + } + return false; + } + + public T Current + { + get + { + if (_current < _start) + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumNotStarted(); + if (_current >= _end) + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumEnded(); + return _array[_current]; + } + } + + object IEnumerator.Current => Current; + + void IEnumerator.Reset() + { + _current = _start - 1; + } + + public void Dispose() + { + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/ArrayTypeMismatchException.cs b/external/corert/src/System.Private.CoreLib/shared/System/ArrayTypeMismatchException.cs index d06a450603..49820f58f5 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/ArrayTypeMismatchException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/ArrayTypeMismatchException.cs @@ -17,7 +17,8 @@ namespace System { // The ArrayMismatchException is thrown when an attempt to store // an object of the wrong type within an array occurs. - // + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class ArrayTypeMismatchException : SystemException { // Creates a new ArrayMismatchException with its message string set to @@ -26,7 +27,7 @@ namespace System public ArrayTypeMismatchException() : base(SR.Arg_ArrayTypeMismatchException) { - HResult = __HResults.COR_E_ARRAYTYPEMISMATCH; + HResult = HResults.COR_E_ARRAYTYPEMISMATCH; } // Creates a new ArrayMismatchException with its message string set to @@ -36,18 +37,17 @@ namespace System public ArrayTypeMismatchException(String message) : base(message) { - HResult = __HResults.COR_E_ARRAYTYPEMISMATCH; + HResult = HResults.COR_E_ARRAYTYPEMISMATCH; } public ArrayTypeMismatchException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_ARRAYTYPEMISMATCH; + HResult = HResults.COR_E_ARRAYTYPEMISMATCH; } protected ArrayTypeMismatchException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/BadImageFormatException.cs b/external/corert/src/System.Private.CoreLib/shared/System/BadImageFormatException.cs index adedcb2a3f..1743075a6f 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/BadImageFormatException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/BadImageFormatException.cs @@ -17,6 +17,8 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public partial class BadImageFormatException : SystemException { private String _fileName; // The name of the corrupt PE file. @@ -25,43 +27,46 @@ namespace System public BadImageFormatException() : base(SR.Arg_BadImageFormatException) { - HResult = __HResults.COR_E_BADIMAGEFORMAT; + HResult = HResults.COR_E_BADIMAGEFORMAT; } public BadImageFormatException(String message) : base(message) { - HResult = __HResults.COR_E_BADIMAGEFORMAT; + HResult = HResults.COR_E_BADIMAGEFORMAT; } public BadImageFormatException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_BADIMAGEFORMAT; + HResult = HResults.COR_E_BADIMAGEFORMAT; } public BadImageFormatException(String message, String fileName) : base(message) { - HResult = __HResults.COR_E_BADIMAGEFORMAT; + HResult = HResults.COR_E_BADIMAGEFORMAT; _fileName = fileName; } public BadImageFormatException(String message, String fileName, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_BADIMAGEFORMAT; + HResult = HResults.COR_E_BADIMAGEFORMAT; _fileName = fileName; } protected BadImageFormatException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); + _fileName = info.GetString("BadImageFormat_FileName"); + _fusionLog = info.GetString("BadImageFormat_FusionLog"); } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + info.AddValue("BadImageFormat_FileName", _fileName, typeof(string)); + info.AddValue("BadImageFormat_FusionLog", _fusionLog, typeof(string)); } public override String Message @@ -78,7 +83,7 @@ namespace System if (_message == null) { if ((_fileName == null) && - (HResult == __HResults.COR_E_EXCEPTION)) + (HResult == HResults.COR_E_EXCEPTION)) _message = SR.Arg_BadImageFormatException; else diff --git a/external/corert/src/System.Private.CoreLib/shared/System/BitConverter.cs b/external/corert/src/System.Private.CoreLib/shared/System/BitConverter.cs new file mode 100644 index 0000000000..e3cf20eb6a --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/BitConverter.cs @@ -0,0 +1,474 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using Internal.Runtime.CompilerServices; + +namespace System +{ + // The BitConverter class contains methods for + // converting an array of bytes to one of the base data + // types, as well as for converting a base data type to an + // array of bytes. + public static class BitConverter + { + // This field indicates the "endianess" of the architecture. + // The value is set to true if the architecture is + // little endian; false if it is big endian. +#if BIGENDIAN + public static readonly bool IsLittleEndian /* = false */; +#else + public static readonly bool IsLittleEndian = true; +#endif + + // Converts a Boolean into an array of bytes with length one. + public static byte[] GetBytes(bool value) + { + byte[] r = new byte[1]; + r[0] = (value ? (byte)1 : (byte)0); + return r; + } + + // Converts a Boolean into a Span of bytes with length one. + public static bool TryWriteBytes(Span destination, bool value) + { + if (destination.Length < sizeof(byte)) + return false; + + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value ? (byte)1 : (byte)0); + return true; + } + + // Converts a char into an array of bytes with length two. + public static byte[] GetBytes(char value) + { + byte[] bytes = new byte[sizeof(char)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; + } + + // Converts a char into a Span + public static bool TryWriteBytes(Span destination, char value) + { + if (destination.Length < sizeof(char)) + return false; + + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value); + return true; + } + + // Converts a short into an array of bytes with length + // two. + public static byte[] GetBytes(short value) + { + byte[] bytes = new byte[sizeof(short)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; + } + + // Converts a short into a Span + public static bool TryWriteBytes(Span destination, short value) + { + if (destination.Length < sizeof(short)) + return false; + + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value); + return true; + } + + // Converts an int into an array of bytes with length + // four. + public static byte[] GetBytes(int value) + { + byte[] bytes = new byte[sizeof(int)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; + } + + // Converts an int into a Span + public static bool TryWriteBytes(Span destination, int value) + { + if (destination.Length < sizeof(int)) + return false; + + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value); + return true; + } + + // Converts a long into an array of bytes with length + // eight. + public static byte[] GetBytes(long value) + { + byte[] bytes = new byte[sizeof(long)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; + } + + // Converts a long into a Span + public static bool TryWriteBytes(Span destination, long value) + { + if (destination.Length < sizeof(long)) + return false; + + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value); + return true; + } + + // Converts an ushort into an array of bytes with + // length two. + [CLSCompliant(false)] + public static byte[] GetBytes(ushort value) + { + byte[] bytes = new byte[sizeof(ushort)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; + } + + // Converts a ushort into a Span + [CLSCompliant(false)] + public static bool TryWriteBytes(Span destination, ushort value) + { + if (destination.Length < sizeof(ushort)) + return false; + + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value); + return true; + } + + // Converts an uint into an array of bytes with + // length four. + [CLSCompliant(false)] + public static byte[] GetBytes(uint value) + { + byte[] bytes = new byte[sizeof(uint)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; + } + + // Converts a uint into a Span + [CLSCompliant(false)] + public static bool TryWriteBytes(Span destination, uint value) + { + if (destination.Length < sizeof(uint)) + return false; + + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value); + return true; + } + + // Converts an unsigned long into an array of bytes with + // length eight. + [CLSCompliant(false)] + public static byte[] GetBytes(ulong value) + { + byte[] bytes = new byte[sizeof(ulong)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; + } + + // Converts a ulong into a Span + [CLSCompliant(false)] + public static bool TryWriteBytes(Span destination, ulong value) + { + if (destination.Length < sizeof(ulong)) + return false; + + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value); + return true; + } + + // Converts a float into an array of bytes with length + // four. + public static byte[] GetBytes(float value) + { + byte[] bytes = new byte[sizeof(float)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; + } + + // Converts a float into a Span + public static bool TryWriteBytes(Span destination, float value) + { + if (destination.Length < sizeof(float)) + return false; + + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value); + return true; + } + + // Converts a double into an array of bytes with length + // eight. + public static byte[] GetBytes(double value) + { + byte[] bytes = new byte[sizeof(double)]; + Unsafe.As(ref bytes[0]) = value; + return bytes; + } + + // Converts a double into a Span + public static bool TryWriteBytes(Span destination, double value) + { + if (destination.Length < sizeof(double)) + return false; + + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value); + return true; + } + + // Converts an array of bytes into a char. + public static char ToChar(byte[] value, int startIndex) => unchecked((char)ToInt16(value, startIndex)); + + // Converts a Span into a char + public static char ToChar(ReadOnlySpan value) + { + if (value.Length < sizeof(char)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(value)); + } + + // Converts an array of bytes into a short. + public static short ToInt16(byte[] value, int startIndex) + { + if (value == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); + if (unchecked((uint)startIndex) >= unchecked((uint)value.Length)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index); + if (startIndex > value.Length - sizeof(short)) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall, ExceptionArgument.value); + + return Unsafe.ReadUnaligned(ref value[startIndex]); + } + + // Converts a Span into a short + public static short ToInt16(ReadOnlySpan value) + { + if (value.Length < sizeof(short)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(value)); + } + + // Converts an array of bytes into an int. + public static int ToInt32(byte[] value, int startIndex) + { + if (value == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); + if (unchecked((uint)startIndex) >= unchecked((uint)value.Length)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index); + if (startIndex > value.Length - sizeof(int)) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall, ExceptionArgument.value); + + return Unsafe.ReadUnaligned(ref value[startIndex]); + } + + // Converts a Span into an int + public static int ToInt32(ReadOnlySpan value) + { + if (value.Length < sizeof(int)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(value)); + } + + // Converts an array of bytes into a long. + public static long ToInt64(byte[] value, int startIndex) + { + if (value == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); + if (unchecked((uint)startIndex) >= unchecked((uint)value.Length)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index); + if (startIndex > value.Length - sizeof(long)) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall, ExceptionArgument.value); + + return Unsafe.ReadUnaligned(ref value[startIndex]); + } + + // Converts a Span into a long + public static long ToInt64(ReadOnlySpan value) + { + if (value.Length < sizeof(long)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(value)); + } + + // Converts an array of bytes into an ushort. + // + [CLSCompliant(false)] + public static ushort ToUInt16(byte[] value, int startIndex) => unchecked((ushort)ToInt16(value, startIndex)); + + // Converts a Span into a ushort + [CLSCompliant(false)] + public static ushort ToUInt16(ReadOnlySpan value) + { + if (value.Length < sizeof(ushort)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(value)); + } + + // Converts an array of bytes into an uint. + // + [CLSCompliant(false)] + public static uint ToUInt32(byte[] value, int startIndex) => unchecked((uint)ToInt32(value, startIndex)); + + // Convert a Span into a uint + [CLSCompliant(false)] + public static uint ToUInt32(ReadOnlySpan value) + { + if (value.Length < sizeof(uint)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(value)); + } + + // Converts an array of bytes into an unsigned long. + // + [CLSCompliant(false)] + public static ulong ToUInt64(byte[] value, int startIndex) => unchecked((ulong)ToInt64(value, startIndex)); + + // Converts a Span into an unsigned long + [CLSCompliant(false)] + public static ulong ToUInt64(ReadOnlySpan value) + { + if (value.Length < sizeof(ulong)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(value)); + } + + // Converts an array of bytes into a float. + public static float ToSingle(byte[] value, int startIndex) => Int32BitsToSingle(ToInt32(value, startIndex)); + + // Converts a Span into a float + public static float ToSingle(ReadOnlySpan value) + { + if (value.Length < sizeof(float)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(value)); + } + + // Converts an array of bytes into a double. + public static double ToDouble(byte[] value, int startIndex) => Int64BitsToDouble(ToInt64(value, startIndex)); + + // Converts a Span into a double + public static double ToDouble(ReadOnlySpan value) + { + if (value.Length < sizeof(double)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(value)); + } + + // Converts an array of bytes into a String. + public static string ToString(byte[] value, int startIndex, int length) + { + if (value == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); + if (startIndex < 0 || startIndex >= value.Length && startIndex > 0) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index); + if (length < 0) + throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_GenericPositive); + if (startIndex > value.Length - length) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall, ExceptionArgument.value); + + if (length == 0) + { + return string.Empty; + } + + if (length > (int.MaxValue / 3)) + { + // (Int32.MaxValue / 3) == 715,827,882 Bytes == 699 MB + throw new ArgumentOutOfRangeException(nameof(length), SR.Format(SR.ArgumentOutOfRange_LengthTooLarge, (int.MaxValue / 3))); + } + + return string.Create(length * 3 - 1, (value, startIndex, length), (dst, state) => + { + const string HexValues = "0123456789ABCDEF"; + + var src = new ReadOnlySpan(state.value, state.startIndex, state.length); + + int i = 0; + int j = 0; + + byte b = src[i++]; + dst[j++] = HexValues[b >> 4]; + dst[j++] = HexValues[b & 0xF]; + + while (i < src.Length) + { + b = src[i++]; + dst[j++] = '-'; + dst[j++] = HexValues[b >> 4]; + dst[j++] = HexValues[b & 0xF]; + } + }); + } + + // Converts an array of bytes into a String. + public static string ToString(byte[] value) + { + if (value == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); + return ToString(value, 0, value.Length); + } + + // Converts an array of bytes into a String. + public static string ToString(byte[] value, int startIndex) + { + if (value == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); + return ToString(value, startIndex, value.Length - startIndex); + } + + /*==================================ToBoolean=================================== + **Action: Convert an array of bytes to a boolean value. We treat this array + ** as if the first 4 bytes were an Int4 an operate on this value. + **Returns: True if the Int4 value of the first 4 bytes is non-zero. + **Arguments: value -- The byte array + ** startIndex -- The position within the array. + **Exceptions: See ToInt4. + ==============================================================================*/ + // Converts an array of bytes into a boolean. + public static bool ToBoolean(byte[] value, int startIndex) + { + if (value == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); + if (startIndex < 0) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index); + if (startIndex > value.Length - 1) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index); // differs from other overloads, which throw base ArgumentException + + return value[startIndex] != 0; + } + + public static bool ToBoolean(ReadOnlySpan value) + { + if (value.Length < sizeof(byte)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(value)) != 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe long DoubleToInt64Bits(double value) + { + return *((long*)&value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe double Int64BitsToDouble(long value) + { + return *((double*)&value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe int SingleToInt32Bits(float value) + { + return *((int*)&value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe float Int32BitsToSingle(int value) + { + return *((float*)&value); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Boolean.cs b/external/corert/src/System.Private.CoreLib/shared/System/Boolean.cs new file mode 100644 index 0000000000..896e5f18e5 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Boolean.cs @@ -0,0 +1,352 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: The boolean class serves as a wrapper for the primitive +** type boolean. +** +** +===========================================================*/ + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Boolean : IComparable, IConvertible, IComparable, IEquatable + { + // + // Member Variables + // + private bool m_value; // Do not rename (binary serialization) + + // The true value. + // + internal const int True = 1; + + // The false value. + // + internal const int False = 0; + + + // + // Internal Constants are real consts for performance. + // + + // The internal string representation of true. + // + internal const String TrueLiteral = "True"; + + // The internal string representation of false. + // + internal const String FalseLiteral = "False"; + + + // + // Public Constants + // + + // The public string representation of true. + // + public static readonly String TrueString = TrueLiteral; + + // The public string representation of false. + // + public static readonly String FalseString = FalseLiteral; + + // + // Overriden Instance Methods + // + /*=================================GetHashCode================================== + **Args: None + **Returns: 1 or 0 depending on whether this instance represents true or false. + **Exceptions: None + **Overriden From: Value + ==============================================================================*/ + // Provides a hash code for this instance. + public override int GetHashCode() + { + return (m_value) ? True : False; + } + + /*===================================ToString=================================== + **Args: None + **Returns: "True" or "False" depending on the state of the boolean. + **Exceptions: None. + ==============================================================================*/ + // Converts the boolean value of this instance to a String. + public override String ToString() + { + if (false == m_value) + { + return FalseLiteral; + } + return TrueLiteral; + } + + public String ToString(IFormatProvider provider) + { + return ToString(); + } + + public bool TryFormat(Span destination, out int charsWritten) + { + string s = m_value ? TrueLiteral : FalseLiteral; + + if (s.Length <= destination.Length) + { + bool copied = s.AsReadOnlySpan().TryCopyTo(destination); + Debug.Assert(copied); + charsWritten = s.Length; + return true; + } + else + { + charsWritten = 0; + return false; + } + } + + // Determines whether two Boolean objects are equal. + public override bool Equals(Object obj) + { + //If it's not a boolean, we're definitely not equal + if (!(obj is Boolean)) + { + return false; + } + + return (m_value == ((Boolean)obj).m_value); + } + + [NonVersionable] + public bool Equals(Boolean obj) + { + return m_value == obj; + } + + // Compares this object to another object, returning an integer that + // indicates the relationship. For booleans, false sorts before true. + // null is considered to be less than any instance. + // If object is not of type boolean, this method throws an ArgumentException. + // + // Returns a value less than zero if this object + // + public int CompareTo(Object obj) + { + if (obj == null) + { + return 1; + } + if (!(obj is Boolean)) + { + throw new ArgumentException(SR.Arg_MustBeBoolean); + } + + if (m_value == ((Boolean)obj).m_value) + { + return 0; + } + else if (m_value == false) + { + return -1; + } + return 1; + } + + public int CompareTo(Boolean value) + { + if (m_value == value) + { + return 0; + } + else if (m_value == false) + { + return -1; + } + return 1; + } + + // + // Static Methods + // + + // Determines whether a String represents true or false. + // + public static Boolean Parse(String value) + { + if (value == null) throw new ArgumentNullException(nameof(value)); + return Parse(value.AsReadOnlySpan()); + } + + public static bool Parse(ReadOnlySpan value) => + TryParse(value, out bool result) ? result : throw new FormatException(SR.Format_BadBoolean); + + // Determines whether a String represents true or false. + // + public static Boolean TryParse(String value, out Boolean result) + { + if (value == null) + { + result = false; + return false; + } + + return TryParse(value.AsReadOnlySpan(), out result); + } + + public static bool TryParse(ReadOnlySpan value, out bool result) + { + ReadOnlySpan trueSpan = TrueLiteral.AsReadOnlySpan(); + if (StringSpanHelpers.Equals(trueSpan, value, StringComparison.OrdinalIgnoreCase)) + { + result = true; + return true; + } + + ReadOnlySpan falseSpan = FalseLiteral.AsReadOnlySpan(); + if (StringSpanHelpers.Equals(falseSpan, value, StringComparison.OrdinalIgnoreCase)) + { + result = false; + return true; + } + + // Special case: Trim whitespace as well as null characters. + value = TrimWhiteSpaceAndNull(value); + + if (StringSpanHelpers.Equals(trueSpan, value, StringComparison.OrdinalIgnoreCase)) + { + result = true; + return true; + } + + if (StringSpanHelpers.Equals(falseSpan, value, StringComparison.OrdinalIgnoreCase)) + { + result = false; + return true; + } + + result = false; + return false; + } + + private static ReadOnlySpan TrimWhiteSpaceAndNull(ReadOnlySpan value) + { + const char nullChar = (char)0x0000; + + int start = 0; + while (start < value.Length) + { + if (!Char.IsWhiteSpace(value[start]) && value[start] != nullChar) + { + break; + } + start++; + } + + int end = value.Length - 1; + while (end >= start) + { + if (!Char.IsWhiteSpace(value[end]) && value[end] != nullChar) + { + break; + } + end--; + } + + return value.Slice(start, end - start + 1); + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.Boolean; + } + + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return m_value; + } + + char IConvertible.ToChar(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Boolean", "Char")); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(m_value); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(m_value); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(m_value); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Boolean", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Buffers/ArrayPoolEventSource.cs b/external/corert/src/System.Private.CoreLib/shared/System/Buffers/ArrayPoolEventSource.cs new file mode 100644 index 0000000000..9482744144 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Buffers/ArrayPoolEventSource.cs @@ -0,0 +1,78 @@ +// 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.Diagnostics.Tracing; + +namespace System.Buffers +{ + [EventSource(Name = "System.Buffers.ArrayPoolEventSource")] + internal sealed class ArrayPoolEventSource : EventSource + { + internal readonly static ArrayPoolEventSource Log = new ArrayPoolEventSource(); + + /// The reason for a BufferAllocated event. + internal enum BufferAllocatedReason : int + { + /// The pool is allocating a buffer to be pooled in a bucket. + Pooled, + /// The requested buffer size was too large to be pooled. + OverMaximumSize, + /// The pool has already allocated for pooling as many buffers of a particular size as it's allowed. + PoolExhausted + } + + /// + /// Event for when a buffer is rented. This is invoked once for every successful call to Rent, + /// regardless of whether a buffer is allocated or a buffer is taken from the pool. In a + /// perfect situation where all rented buffers are returned, we expect to see the number + /// of BufferRented events exactly match the number of BuferReturned events, with the number + /// of BufferAllocated events being less than or equal to those numbers (ideally significantly + /// less than). + /// + [Event(1, Level = EventLevel.Verbose)] + internal unsafe void BufferRented(int bufferId, int bufferSize, int poolId, int bucketId) + { + EventData* payload = stackalloc EventData[4]; + payload[0].Size = sizeof(int); + payload[0].DataPointer = ((IntPtr)(&bufferId)); + payload[1].Size = sizeof(int); + payload[1].DataPointer = ((IntPtr)(&bufferSize)); + payload[2].Size = sizeof(int); + payload[2].DataPointer = ((IntPtr)(&poolId)); + payload[3].Size = sizeof(int); + payload[3].DataPointer = ((IntPtr)(&bucketId)); + WriteEventCore(1, 4, payload); + } + + /// + /// Event for when a buffer is allocated by the pool. In an ideal situation, the number + /// of BufferAllocated events is significantly smaller than the number of BufferRented and + /// BufferReturned events. + /// + [Event(2, Level = EventLevel.Informational)] + internal unsafe void BufferAllocated(int bufferId, int bufferSize, int poolId, int bucketId, BufferAllocatedReason reason) + { + EventData* payload = stackalloc EventData[5]; + payload[0].Size = sizeof(int); + payload[0].DataPointer = ((IntPtr)(&bufferId)); + payload[1].Size = sizeof(int); + payload[1].DataPointer = ((IntPtr)(&bufferSize)); + payload[2].Size = sizeof(int); + payload[2].DataPointer = ((IntPtr)(&poolId)); + payload[3].Size = sizeof(int); + payload[3].DataPointer = ((IntPtr)(&bucketId)); + payload[4].Size = sizeof(BufferAllocatedReason); + payload[4].DataPointer = ((IntPtr)(&reason)); + WriteEventCore(2, 5, payload); + } + + /// + /// Event raised when a buffer is returned to the pool. This event is raised regardless of whether + /// the returned buffer is stored or dropped. In an ideal situation, the number of BufferReturned + /// events exactly matches the number of BufferRented events. + /// + [Event(3, Level = EventLevel.Verbose)] + internal void BufferReturned(int bufferId, int bufferSize, int poolId) => WriteEvent(3, bufferId, bufferSize, poolId); + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Buffers/IRetainable.cs b/external/corert/src/System.Private.CoreLib/shared/System/Buffers/IRetainable.cs new file mode 100644 index 0000000000..8d71fc614a --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Buffers/IRetainable.cs @@ -0,0 +1,15 @@ +// 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.Runtime; +using System.Runtime.CompilerServices; + +namespace System.Buffers +{ + public interface IRetainable + { + void Retain(); + bool Release(); + } +} \ No newline at end of file diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Buffers/MemoryHandle.cs b/external/corert/src/System.Private.CoreLib/shared/System/Buffers/MemoryHandle.cs new file mode 100644 index 0000000000..60592144a5 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Buffers/MemoryHandle.cs @@ -0,0 +1,58 @@ +// 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.Runtime; +using System.Runtime.InteropServices; + +namespace System.Buffers +{ + public unsafe struct MemoryHandle : IDisposable + { + private IRetainable _owner; + private void* _pointer; + private GCHandle _handle; + + [CLSCompliant(false)] + public MemoryHandle(IRetainable owner, void* pointer = null, GCHandle handle = default(GCHandle)) + { + _owner = owner; + _pointer = pointer; + _handle = handle; + } + + internal void AddOffset(int offset) + { + if (_pointer == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.pointer); + } + else + { + _pointer = (void*)((byte*)_pointer + offset); + } + } + + [CLSCompliant(false)] + public void* Pointer => _pointer; + + public bool HasPointer => _pointer != null; + + public void Dispose() + { + if (_handle.IsAllocated) + { + _handle.Free(); + } + + if (_owner != null) + { + _owner.Release(); + _owner = null; + } + + _pointer = null; + } + + } +} \ No newline at end of file diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Buffers/OwnedMemory.cs b/external/corert/src/System.Private.CoreLib/shared/System/Buffers/OwnedMemory.cs new file mode 100644 index 0000000000..116767073d --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Buffers/OwnedMemory.cs @@ -0,0 +1,53 @@ +// 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.Runtime; +using System.Runtime.CompilerServices; + +namespace System.Buffers +{ + public abstract class OwnedMemory : IDisposable, IRetainable + { + public abstract int Length { get; } + + public abstract Span Span { get; } + + public Memory Memory + { + get + { + if (IsDisposed) + { + ThrowHelper.ThrowObjectDisposedException(nameof(OwnedMemory), ExceptionResource.Memory_ThrowIfDisposed); + } + return new Memory(owner: this, 0, Length); + } + } + + public abstract MemoryHandle Pin(); + + protected internal abstract bool TryGetArray(out ArraySegment arraySegment); + + public void Dispose() + { + if (IsRetained) + { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Memory_OutstandingReferences); + } + Dispose(true); + GC.SuppressFinalize(this); + } + + protected abstract void Dispose(bool disposing); + + protected abstract bool IsRetained { get; } + + public abstract bool IsDisposed { get; } + + public abstract void Retain(); + + public abstract bool Release(); + + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Byte.cs b/external/corert/src/System.Private.CoreLib/shared/System/Byte.cs new file mode 100644 index 0000000000..13ceb7573d --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Byte.cs @@ -0,0 +1,288 @@ +// 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.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Byte : IComparable, IConvertible, IFormattable, IComparable, IEquatable, ISpanFormattable + { + private byte m_value; // Do not rename (binary serialization) + + // The maximum value that a Byte may represent: 255. + public const byte MaxValue = (byte)0xFF; + + // The minimum value that a Byte may represent: 0. + public const byte MinValue = 0; + + + // Compares this object to another object, returning an integer that + // indicates the relationship. + // Returns a value less than zero if this object + // null is considered to be less than any instance. + // If object is not of type byte, this method throws an ArgumentException. + // + public int CompareTo(Object value) + { + if (value == null) + { + return 1; + } + if (!(value is Byte)) + { + throw new ArgumentException(SR.Arg_MustBeByte); + } + + return m_value - (((Byte)value).m_value); + } + + public int CompareTo(Byte value) + { + return m_value - value; + } + + // Determines whether two Byte objects are equal. + public override bool Equals(Object obj) + { + if (!(obj is Byte)) + { + return false; + } + return m_value == ((Byte)obj).m_value; + } + + [NonVersionable] + public bool Equals(Byte obj) + { + return m_value == obj; + } + + // Gets a hash code for this instance. + public override int GetHashCode() + { + return m_value; + } + + public static byte Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); + } + + public static byte Parse(String s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, style, NumberFormatInfo.CurrentInfo); + } + + public static byte Parse(String s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + } + + // Parses an unsigned byte from a String in the given style. If + // a NumberFormatInfo isn't specified, the current culture's + // NumberFormatInfo is assumed. + public static byte Parse(String s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider)); + } + + public static byte Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Parse(s, style, NumberFormatInfo.GetInstance(provider)); + } + + private static byte Parse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info) + { + int i = 0; + try + { + i = Number.ParseInt32(s, style, info); + } + catch (OverflowException e) + { + throw new OverflowException(SR.Overflow_Byte, e); + } + + if (i < MinValue || i > MaxValue) throw new OverflowException(SR.Overflow_Byte); + return (byte)i; + } + + public static bool TryParse(String s, out Byte result) + { + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(ReadOnlySpan s, out byte result) + { + return TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out Byte result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out byte result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + private static bool TryParse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out Byte result) + { + result = 0; + int i; + if (!Number.TryParseInt32(s, style, info, out i)) + { + return false; + } + if (i < MinValue || i > MaxValue) + { + return false; + } + result = (byte)i; + return true; + } + + public override String ToString() + { + return Number.FormatInt32(m_value, null, null); + } + + public String ToString(String format) + { + return Number.FormatInt32(m_value, format, null); + } + + public String ToString(IFormatProvider provider) + { + return Number.FormatInt32(m_value, null, provider); + } + + public String ToString(String format, IFormatProvider provider) + { + return Number.FormatInt32(m_value, format, provider); + } + + // TODO https://github.com/dotnet/corefx/issues/25337: Remove this overload once corefx is updated to target the new signatures + public bool TryFormat(Span destination, out int charsWritten, string format, IFormatProvider provider) => + TryFormat(destination, out charsWritten, (ReadOnlySpan)format, provider); + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) + { + return Number.TryFormatInt32(m_value, format, provider, destination, out charsWritten); + } + + // + // IConvertible implementation + // + public TypeCode GetTypeCode() + { + return TypeCode.Byte; + } + + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return Convert.ToChar(m_value); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return m_value; + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(m_value); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(m_value); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(m_value); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Byte", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Char.cs b/external/corert/src/System.Private.CoreLib/shared/System/Char.cs index cb2a512193..6059830fbd 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Char.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Char.cs @@ -13,7 +13,6 @@ ===========================================================*/ using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Globalization; using System.Runtime.InteropServices; @@ -21,12 +20,13 @@ namespace System { [Serializable] [StructLayout(LayoutKind.Sequential)] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public struct Char : IComparable, IComparable, IEquatable, IConvertible { // // Member Variables // - private char _value; + private char m_value; // Do not rename (binary serialization) // // Public Constants @@ -102,7 +102,7 @@ namespace System // Calculate a hashcode for a 2 byte Unicode character. public override int GetHashCode() { - return (int)_value | ((int)_value << 16); + return (int)m_value | ((int)m_value << 16); } // Used for comparing two boxed Char objects. @@ -113,13 +113,13 @@ namespace System { return false; } - return (_value == ((Char)obj)._value); + return (m_value == ((Char)obj).m_value); } [System.Runtime.Versioning.NonVersionable] public bool Equals(Char obj) { - return _value == obj; + return m_value == obj; } // Compares this object to another object, returning an integer that @@ -128,7 +128,6 @@ namespace System // null is considered to be less than any instance. // If object is not of type Char, this method throws an ArgumentException. // - [Pure] public int CompareTo(Object value) { if (value == null) @@ -140,28 +139,23 @@ namespace System throw new ArgumentException(SR.Arg_MustBeChar); } - return (_value - ((Char)value)._value); + return (m_value - ((Char)value).m_value); } - [Pure] public int CompareTo(Char value) { - return (_value - value); + return (m_value - value); } // Overrides System.Object.ToString. - [Pure] public override String ToString() { - Contract.Ensures(Contract.Result() != null); - return Char.ToString(_value); + return Char.ToString(m_value); } - [Pure] public String ToString(IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); - return Char.ToString(_value); + return Char.ToString(m_value); } // @@ -172,7 +166,6 @@ namespace System **This static methods takes a character and returns the String representation of it. ==============================================================================*/ // Provides a string representation of a character. - [Pure] public static string ToString(char c) => string.CreateFromChar(c); public static char Parse(String s) @@ -181,7 +174,6 @@ namespace System { throw new ArgumentNullException(nameof(s)); } - Contract.EndContractBlock(); if (s.Length != 1) { @@ -213,7 +205,6 @@ namespace System **character c is considered to be a digit. ** ==============================================================================*/ // Determines whether a character is a digit. - [Pure] public static bool IsDigit(char c) { if (IsLatin1(c)) @@ -246,7 +237,6 @@ namespace System **character c is considered to be a letter. ** ==============================================================================*/ // Determines whether a character is a letter. - [Pure] public static bool IsLetter(char c) { if (IsLatin1(c)) @@ -273,11 +263,11 @@ namespace System // U+000d = CARRIAGE RETURN // U+0085 = NEXT LINE // U+00a0 = NO-BREAK SPACE - if ((c == ' ') || (c >= '\x0009' && c <= '\x000d') || c == '\x00a0' || c == '\x0085') - { - return (true); - } - return (false); + return + c == ' ' || + (uint)(c - '\x0009') <= ('\x000d' - '\x0009') || // (c >= '\x0009' && c <= '\x000d') + c == '\x00a0' || + c == '\x0085'; } /*===============================ISWHITESPACE=================================== @@ -285,7 +275,6 @@ namespace System **character c is considered to be a whitespace character. ** ==============================================================================*/ // Determines whether a character is whitespace. - [Pure] public static bool IsWhiteSpace(char c) { if (IsLatin1(c)) @@ -301,7 +290,6 @@ namespace System **Returns: True if c is an uppercase character. ==============================================================================*/ // Determines whether a character is upper-case. - [Pure] public static bool IsUpper(char c) { if (IsLatin1(c)) @@ -320,7 +308,6 @@ namespace System **Returns: True if c is an lowercase character. ==============================================================================*/ // Determines whether a character is lower-case. - [Pure] public static bool IsLower(char c) { if (IsLatin1(c)) @@ -356,7 +343,6 @@ namespace System **Returns: True if c is an punctuation mark ==============================================================================*/ // Determines whether a character is a punctuation mark. - [Pure] public static bool IsPunctuation(char c) { if (IsLatin1(c)) @@ -385,7 +371,6 @@ namespace System } // Determines whether a character is a letter or a digit. - [Pure] public static bool IsLetterOrDigit(char c) { if (IsLatin1(c)) @@ -404,7 +389,6 @@ namespace System { if (culture == null) throw new ArgumentNullException(nameof(culture)); - Contract.EndContractBlock(); return culture.TextInfo.ToUpper(c); } @@ -437,7 +421,6 @@ namespace System { if (culture == null) throw new ArgumentNullException(nameof(culture)); - Contract.EndContractBlock(); return culture.TextInfo.ToLower(c); } @@ -463,7 +446,6 @@ namespace System // // IConvertible implementation // - [Pure] public TypeCode GetTypeCode() { return TypeCode.Char; @@ -477,47 +459,47 @@ namespace System char IConvertible.ToChar(IFormatProvider provider) { - return _value; + return m_value; } sbyte IConvertible.ToSByte(IFormatProvider provider) { - return Convert.ToSByte(_value); + return Convert.ToSByte(m_value); } byte IConvertible.ToByte(IFormatProvider provider) { - return Convert.ToByte(_value); + return Convert.ToByte(m_value); } short IConvertible.ToInt16(IFormatProvider provider) { - return Convert.ToInt16(_value); + return Convert.ToInt16(m_value); } ushort IConvertible.ToUInt16(IFormatProvider provider) { - return Convert.ToUInt16(_value); + return Convert.ToUInt16(m_value); } int IConvertible.ToInt32(IFormatProvider provider) { - return Convert.ToInt32(_value); + return Convert.ToInt32(m_value); } uint IConvertible.ToUInt32(IFormatProvider provider) { - return Convert.ToUInt32(_value); + return Convert.ToUInt32(m_value); } long IConvertible.ToInt64(IFormatProvider provider) { - return Convert.ToInt64(_value); + return Convert.ToInt64(m_value); } ulong IConvertible.ToUInt64(IFormatProvider provider) { - return Convert.ToUInt64(_value); + return Convert.ToUInt64(m_value); } float IConvertible.ToSingle(IFormatProvider provider) @@ -561,7 +543,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); char c = s[index]; if (IsLatin1(c)) { @@ -579,7 +560,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); char c = s[index]; if (IsLatin1(c)) { @@ -596,7 +576,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); char c = s[index]; if (IsLatin1(c)) { @@ -618,7 +597,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); char c = s[index]; if (IsLatin1(c)) { @@ -635,7 +613,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); char c = s[index]; if (IsLatin1(c)) { @@ -686,7 +663,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); char c = s[index]; if (IsLatin1(c)) { @@ -715,7 +691,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); char c = s[index]; if (IsLatin1(c)) { @@ -765,7 +740,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); char c = s[index]; if (IsLatin1(c)) { @@ -774,13 +748,11 @@ namespace System return (CheckSeparator(CharUnicodeInfo.GetUnicodeCategory(s, index))); } - [Pure] public static bool IsSurrogate(char c) { return (c >= HIGH_SURROGATE_START && c <= LOW_SURROGATE_END); } - [Pure] public static bool IsSurrogate(String s, int index) { if (s == null) @@ -791,7 +763,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); return (IsSurrogate(s[index])); } @@ -829,7 +800,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); char c = s[index]; if (IsLatin1(c)) { @@ -847,7 +817,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); char c = s[index]; if (IsLatin1(c)) { @@ -869,7 +838,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); if (IsLatin1(s[index])) { @@ -896,7 +864,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); if (IsLatin1(s[index])) { return (GetLatin1UnicodeCategory(s[index])); @@ -917,7 +884,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); return CharUnicodeInfo.GetNumericValue(s, index); } @@ -925,13 +891,11 @@ namespace System /*================================= IsHighSurrogate ============================ ** Check if a char is a high surrogate. ==============================================================================*/ - [Pure] public static bool IsHighSurrogate(char c) { return ((c >= CharUnicodeInfo.HIGH_SURROGATE_START) && (c <= CharUnicodeInfo.HIGH_SURROGATE_END)); } - [Pure] public static bool IsHighSurrogate(String s, int index) { if (s == null) @@ -942,20 +906,17 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); return (IsHighSurrogate(s[index])); } /*================================= IsLowSurrogate ============================ ** Check if a char is a low surrogate. ==============================================================================*/ - [Pure] public static bool IsLowSurrogate(char c) { return ((c >= CharUnicodeInfo.LOW_SURROGATE_START) && (c <= CharUnicodeInfo.LOW_SURROGATE_END)); } - [Pure] public static bool IsLowSurrogate(String s, int index) { if (s == null) @@ -966,14 +927,12 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); return (IsLowSurrogate(s[index])); } /*================================= IsSurrogatePair ============================ ** Check if the string specified by the index starts with a surrogate pair. ==============================================================================*/ - [Pure] public static bool IsSurrogatePair(String s, int index) { if (s == null) @@ -984,7 +943,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); if (index + 1 < s.Length) { return (IsSurrogatePair(s[index], s[index + 1])); @@ -992,7 +950,6 @@ namespace System return (false); } - [Pure] public static bool IsSurrogatePair(char highSurrogate, char lowSurrogate) { return ((highSurrogate >= CharUnicodeInfo.HIGH_SURROGATE_START && highSurrogate <= CharUnicodeInfo.HIGH_SURROGATE_END) && @@ -1023,7 +980,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(utf32), SR.ArgumentOutOfRange_InvalidUTF32); } - Contract.EndContractBlock(); if (utf32 < UNICODE_PLANE01_START) { @@ -1058,7 +1014,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(lowSurrogate), SR.ArgumentOutOfRange_InvalidLowSurrogate); } - Contract.EndContractBlock(); return (((highSurrogate - CharUnicodeInfo.HIGH_SURROGATE_START) * 0x400) + (lowSurrogate - CharUnicodeInfo.LOW_SURROGATE_START) + UNICODE_PLANE01_START); } @@ -1081,7 +1036,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); } - Contract.EndContractBlock(); // Check if the character at index is a high surrogate. int temp1 = (int)s[index] - CharUnicodeInfo.HIGH_SURROGATE_START; if (temp1 >= 0 && temp1 <= 0x7ff) diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Collections/DictionaryEntry.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/DictionaryEntry.cs index c4879db0f1..d0cbcb6663 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Collections/DictionaryEntry.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/DictionaryEntry.cs @@ -11,10 +11,13 @@ namespace System.Collections // A DictionaryEntry holds a key and a value from a dictionary. // It is returned by IDictionaryEnumerator::GetEntry(). [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public struct DictionaryEntry { - private Object _key; - private Object _value; + private Object _key; // Do not rename (binary serialization) + private Object _value; // Do not rename (binary serialization) // Constructs a new DictionaryEnumerator by setting the Key // and Value fields appropriately. diff --git a/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs similarity index 56% rename from external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs rename to external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs index 7a360dcfe4..ab7f0dba8f 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs @@ -1,12 +1,10 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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 Internal.Runtime.CompilerServices; using System; using System.Collections; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Runtime.Serialization; #if MONO @@ -39,6 +37,9 @@ namespace System.Collections.Generic [DebuggerTypeProxy(typeof(IDictionaryDebugView<,>))] [DebuggerDisplay("Count = {Count}")] [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class Dictionary : IDictionary, IDictionary, IReadOnlyDictionary, ISerializable, IDeserializationCallback { private struct Entry @@ -49,23 +50,22 @@ namespace System.Collections.Generic public TValue value; // Value of entry } - private int[] buckets; - private Entry[] entries; - private int count; - private int version; - - private int freeList; - private int freeCount; - private IEqualityComparer comparer; - private KeyCollection keys; - private ValueCollection values; + private int[] _buckets; + private Entry[] _entries; + private int _count; + private int _freeList; + private int _freeCount; + private int _version; + private IEqualityComparer _comparer; + private KeyCollection _keys; + private ValueCollection _values; private object _syncRoot; // constants for serialization - private const string VersionName = "Version"; - private const string HashSizeName = "HashSize"; // Must save buckets.Length - private const string KeyValuePairsName = "KeyValuePairs"; - private const string ComparerName = "Comparer"; + private const string VersionName = "Version"; // Do not rename (binary serialization) + private const string HashSizeName = "HashSize"; // Do not rename (binary serialization). Must save buckets.Length + private const string KeyValuePairsName = "KeyValuePairs"; // Do not rename (binary serialization) + private const string ComparerName = "Comparer"; // Do not rename (binary serialization) public Dictionary() : this(0, null) { } @@ -75,13 +75,14 @@ namespace System.Collections.Generic public Dictionary(int capacity, IEqualityComparer comparer) { - if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity), capacity, SR.ArgumentOutOfRange_NeedNonNegNum); + if (capacity < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity); if (capacity > 0) Initialize(capacity); - this.comparer = comparer ?? EqualityComparer.Default; + _comparer = comparer ?? EqualityComparer.Default; + #if !MONO - if (this.comparer == EqualityComparer.Default) + if (_comparer == EqualityComparer.Default) { - this.comparer = (IEqualityComparer)NonRandomizedStringEqualityComparer.Default; + _comparer = (IEqualityComparer)NonRandomizedStringEqualityComparer.Default; } #endif } @@ -93,7 +94,7 @@ namespace System.Collections.Generic { if (dictionary == null) { - throw new ArgumentNullException(nameof(dictionary)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary); } // It is likely that the passed-in dictionary is Dictionary. When this is the case, @@ -103,8 +104,8 @@ namespace System.Collections.Generic if (dictionary.GetType() == typeof(Dictionary)) { Dictionary d = (Dictionary)dictionary; - int count = d.count; - Entry[] entries = d.entries; + int count = d._count; + Entry[] entries = d._entries; for (int i = 0; i < count; i++) { if (entries[i].hashCode >= 0) @@ -128,7 +129,7 @@ namespace System.Collections.Generic { if (collection == null) { - throw new ArgumentNullException(nameof(collection)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection); } foreach (KeyValuePair pair in collection) @@ -142,29 +143,28 @@ namespace System.Collections.Generic // We can't do anything with the keys and values until the entire graph has been deserialized // and we have a resonable estimate that GetHashCode is not going to fail. For the time being, // we'll just cache this. The graph is not valid until OnDeserialization has been called. - DictionaryHashHelpers.SerializationInfoTable.Add(this, info); + HashHelpers.SerializationInfoTable.Add(this, info); } public IEqualityComparer Comparer { get { - return comparer; + return _comparer; } } public int Count { - get { return count - freeCount; } + get { return _count - _freeCount; } } public KeyCollection Keys { get { - Contract.Ensures(Contract.Result() != null); - if (keys == null) keys = new KeyCollection(this); - return keys; + if (_keys == null) _keys = new KeyCollection(this); + return _keys; } } @@ -172,8 +172,8 @@ namespace System.Collections.Generic { get { - if (keys == null) keys = new KeyCollection(this); - return keys; + if (_keys == null) _keys = new KeyCollection(this); + return _keys; } } @@ -181,8 +181,8 @@ namespace System.Collections.Generic { get { - if (keys == null) keys = new KeyCollection(this); - return keys; + if (_keys == null) _keys = new KeyCollection(this); + return _keys; } } @@ -190,9 +190,8 @@ namespace System.Collections.Generic { get { - Contract.Ensures(Contract.Result() != null); - if (values == null) values = new ValueCollection(this); - return values; + if (_values == null) _values = new ValueCollection(this); + return _values; } } @@ -200,8 +199,8 @@ namespace System.Collections.Generic { get { - if (values == null) values = new ValueCollection(this); - return values; + if (_values == null) _values = new ValueCollection(this); + return _values; } } @@ -209,8 +208,8 @@ namespace System.Collections.Generic { get { - if (values == null) values = new ValueCollection(this); - return values; + if (_values == null) _values = new ValueCollection(this); + return _values; } } @@ -219,8 +218,9 @@ namespace System.Collections.Generic get { int i = FindEntry(key); - if (i >= 0) return entries[i].value; - throw new KeyNotFoundException(); + if (i >= 0) return _entries[i].value; + ThrowHelper.ThrowKeyNotFoundException(key); + return default(TValue); } set { @@ -243,7 +243,7 @@ namespace System.Collections.Generic bool ICollection>.Contains(KeyValuePair keyValuePair) { int i = FindEntry(keyValuePair.Key); - if (i >= 0 && EqualityComparer.Default.Equals(entries[i].value, keyValuePair.Value)) + if (i >= 0 && EqualityComparer.Default.Equals(_entries[i].value, keyValuePair.Value)) { return true; } @@ -253,7 +253,7 @@ namespace System.Collections.Generic bool ICollection>.Remove(KeyValuePair keyValuePair) { int i = FindEntry(keyValuePair.Key); - if (i >= 0 && EqualityComparer.Default.Equals(entries[i].value, keyValuePair.Value)) + if (i >= 0 && EqualityComparer.Default.Equals(_entries[i].value, keyValuePair.Value)) { Remove(keyValuePair.Key); return true; @@ -263,14 +263,20 @@ namespace System.Collections.Generic public void Clear() { + int count = _count; if (count > 0) { - for (int i = 0; i < buckets.Length; i++) buckets[i] = -1; - Array.Clear(entries, 0, count); - freeList = -1; - count = 0; - freeCount = 0; - version++; + int[] buckets = _buckets; + for (int i = 0; i < buckets.Length; i++) + { + buckets[i] = -1; + } + + _count = 0; + _freeList = -1; + _freeCount = 0; + _version++; + Array.Clear(_entries, 0, count); } } @@ -283,17 +289,17 @@ namespace System.Collections.Generic { if (value == null) { - for (int i = 0; i < count; i++) + for (int i = 0; i < _count; i++) { - if (entries[i].hashCode >= 0 && entries[i].value == null) return true; + if (_entries[i].hashCode >= 0 && _entries[i].value == null) return true; } } else { EqualityComparer c = EqualityComparer.Default; - for (int i = 0; i < count; i++) + for (int i = 0; i < _count; i++) { - if (entries[i].hashCode >= 0 && c.Equals(entries[i].value, value)) return true; + if (_entries[i].hashCode >= 0 && c.Equals(_entries[i].value, value)) return true; } } return false; @@ -303,21 +309,21 @@ namespace System.Collections.Generic { if (array == null) { - throw new ArgumentNullException(nameof(array)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); } if (index < 0 || index > array.Length) { - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); } if (array.Length - index < Count) { - throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); } - int count = this.count; - Entry[] entries = this.entries; + int count = _count; + Entry[] entries = _entries; for (int i = 0; i < count; i++) { if (entries[i].hashCode >= 0) @@ -341,14 +347,14 @@ namespace System.Collections.Generic { if (info == null) { - throw new ArgumentNullException(nameof(info)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info); } - info.AddValue(VersionName, version); - info.AddValue(ComparerName, comparer, typeof(IEqualityComparer)); - info.AddValue(HashSizeName, buckets == null ? 0 : buckets.Length); // This is the length of the bucket array + info.AddValue(VersionName, _version); + info.AddValue(ComparerName, _comparer, typeof(IEqualityComparer)); + info.AddValue(HashSizeName, _buckets == null ? 0 : _buckets.Length); // This is the length of the bucket array - if (buckets != null) + if (_buckets != null) { var array = new KeyValuePair[Count]; CopyTo(array, 0); @@ -360,15 +366,15 @@ namespace System.Collections.Generic { if (key == null) { - throw new ArgumentNullException(nameof(key)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); } - if (buckets != null) + if (_buckets != null) { - int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; - for (int i = buckets[hashCode % buckets.Length]; i >= 0; i = entries[i].next) + int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF; + for (int i = _buckets[hashCode % _buckets.Length]; i >= 0; i = _entries[i].next) { - if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i; + if (_entries[i].hashCode == hashCode && _comparer.Equals(_entries[i].key, key)) return i; } } return -1; @@ -377,38 +383,43 @@ namespace System.Collections.Generic private void Initialize(int capacity) { int size = HashHelpers.GetPrime(capacity); - buckets = new int[size]; - for (int i = 0; i < buckets.Length; i++) buckets[i] = -1; - entries = new Entry[size]; - freeList = -1; + int[] buckets = new int[size]; + for (int i = 0; i < buckets.Length; i++) + { + buckets[i] = -1; + } + + _freeList = -1; + _buckets = buckets; + _entries = new Entry[size]; } private bool TryInsert(TKey key, TValue value, InsertionBehavior behavior) { if (key == null) { - throw new ArgumentNullException(nameof(key)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); } - if (buckets == null) Initialize(0); - int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; - int targetBucket = hashCode % buckets.Length; + if (_buckets == null) Initialize(0); + int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF; + int targetBucket = hashCode % _buckets.Length; int collisionCount = 0; - for (int i = buckets[targetBucket]; i >= 0; i = entries[i].next) + for (int i = _buckets[targetBucket]; i >= 0; i = _entries[i].next) { - if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) + if (_entries[i].hashCode == hashCode && _comparer.Equals(_entries[i].key, key)) { if (behavior == InsertionBehavior.OverwriteExisting) { - entries[i].value = value; - version++; + _entries[i].value = value; + _version++; return true; } if (behavior == InsertionBehavior.ThrowOnExisting) { - throw new ArgumentException(SR.Format(SR.Argument_AddingDuplicate, key)); + ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(key); } return false; @@ -417,36 +428,40 @@ namespace System.Collections.Generic } int index; - - if (freeCount > 0) + if (_freeCount > 0) { - index = freeList; - freeList = entries[index].next; - freeCount--; + index = _freeList; + _freeList = _entries[index].next; + _freeCount--; } else { - if (count == entries.Length) + if (_count == _entries.Length) { Resize(); - targetBucket = hashCode % buckets.Length; + targetBucket = hashCode % _buckets.Length; } - index = count; - count++; + index = _count; + _count++; } - entries[index].hashCode = hashCode; - entries[index].next = buckets[targetBucket]; - entries[index].key = key; - entries[index].value = value; - buckets[targetBucket] = index; - version++; + _entries[index].hashCode = hashCode; + _entries[index].next = _buckets[targetBucket]; + _entries[index].key = key; + _entries[index].value = value; + _buckets[targetBucket] = index; + _version++; - if (collisionCount > HashHelpers.HashCollisionThreshold && comparer is NonRandomizedStringEqualityComparer) +#if !MONO + // If we hit the collision threshold we'll need to switch to the comparer which is using randomized string hashing + // i.e. EqualityComparer.Default. + + if (collisionCount > HashHelpers.HashCollisionThreshold && _comparer is NonRandomizedStringEqualityComparer) { - comparer = (IEqualityComparer)EqualityComparer.Default; - Resize(entries.Length, true); + _comparer = (IEqualityComparer)EqualityComparer.Default; + Resize(_entries.Length, true); } +#endif return true; } @@ -454,7 +469,8 @@ namespace System.Collections.Generic public virtual void OnDeserialization(object sender) { SerializationInfo siInfo; - DictionaryHashHelpers.SerializationInfoTable.TryGetValue(this, out siInfo); + HashHelpers.SerializationInfoTable.TryGetValue(this, out siInfo); + if (siInfo == null) { // We can return immediately if this function is called twice. @@ -464,78 +480,80 @@ namespace System.Collections.Generic int realVersion = siInfo.GetInt32(VersionName); int hashsize = siInfo.GetInt32(HashSizeName); - comparer = (IEqualityComparer)siInfo.GetValue(ComparerName, typeof(IEqualityComparer)); + _comparer = (IEqualityComparer)siInfo.GetValue(ComparerName, typeof(IEqualityComparer)); if (hashsize != 0) { - buckets = new int[hashsize]; - for (int i = 0; i < buckets.Length; i++) buckets[i] = -1; - entries = new Entry[hashsize]; - freeList = -1; + Initialize(hashsize); - KeyValuePair[] array = - (KeyValuePair[])siInfo.GetValue(KeyValuePairsName, typeof(KeyValuePair[])); + KeyValuePair[] array = (KeyValuePair[]) + siInfo.GetValue(KeyValuePairsName, typeof(KeyValuePair[])); if (array == null) { - throw new SerializationException(SR.Serialization_MissingKeys); + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MissingKeys); } for (int i = 0; i < array.Length; i++) { if (array[i].Key == null) { - throw new SerializationException(SR.Serialization_NullKey); + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_NullKey); } Add(array[i].Key, array[i].Value); } } else { - buckets = null; + _buckets = null; } - version = realVersion; - DictionaryHashHelpers.SerializationInfoTable.Remove(this); + _version = realVersion; + HashHelpers.SerializationInfoTable.Remove(this); } private void Resize() { - Resize(HashHelpers.ExpandPrime(count), false); + Resize(HashHelpers.ExpandPrime(_count), false); } private void Resize(int newSize, bool forceNewHashCodes) { - Debug.Assert(newSize >= entries.Length); - int[] newBuckets = new int[newSize]; - for (int i = 0; i < newBuckets.Length; i++) newBuckets[i] = -1; + Debug.Assert(newSize >= _entries.Length); - Entry[] newEntries = new Entry[newSize]; - Array.Copy(entries, 0, newEntries, 0, count); + int[] buckets = new int[newSize]; + for (int i = 0; i < buckets.Length; i++) + { + buckets[i] = -1; + } + Entry[] entries = new Entry[newSize]; + + int count = _count; + Array.Copy(_entries, 0, entries, 0, count); if (forceNewHashCodes) { for (int i = 0; i < count; i++) { - if (newEntries[i].hashCode != -1) + if (entries[i].hashCode != -1) { - newEntries[i].hashCode = (comparer.GetHashCode(newEntries[i].key) & 0x7FFFFFFF); + entries[i].hashCode = (_comparer.GetHashCode(entries[i].key) & 0x7FFFFFFF); } } } for (int i = 0; i < count; i++) { - if (newEntries[i].hashCode >= 0) + if (entries[i].hashCode >= 0) { - int bucket = newEntries[i].hashCode % newSize; - newEntries[i].next = newBuckets[bucket]; - newBuckets[bucket] = i; + int bucket = entries[i].hashCode % newSize; + entries[i].next = buckets[bucket]; + buckets[bucket] = i; } } - buckets = newBuckets; - entries = newEntries; + _buckets = buckets; + _entries = entries; } // The overload Remove(TKey key, out TValue value) is a copy of this method with one additional @@ -545,35 +563,48 @@ namespace System.Collections.Generic { if (key == null) { - throw new ArgumentNullException(nameof(key)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); } - if (buckets != null) + if (_buckets != null) { - int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; - int bucket = hashCode % buckets.Length; + int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF; + int bucket = hashCode % _buckets.Length; int last = -1; - for (int i = buckets[bucket]; i >= 0; last = i, i = entries[i].next) + int i = _buckets[bucket]; + while (i >= 0) { - if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) + ref Entry entry = ref _entries[i]; + + if (entry.hashCode == hashCode && _comparer.Equals(entry.key, key)) { if (last < 0) { - buckets[bucket] = entries[i].next; + _buckets[bucket] = entry.next; } else { - entries[last].next = entries[i].next; + _entries[last].next = entry.next; } - entries[i].hashCode = -1; - entries[i].next = freeList; - entries[i].key = default(TKey); - entries[i].value = default(TValue); - freeList = i; - freeCount++; - version++; + entry.hashCode = -1; + entry.next = _freeList; + + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + entry.key = default(TKey); + } + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + entry.value = default(TValue); + } + _freeList = i; + _freeCount++; + _version++; return true; } + + last = i; + i = entry.next; } } return false; @@ -586,38 +617,51 @@ namespace System.Collections.Generic { if (key == null) { - throw new ArgumentNullException(nameof(key)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); } - if (buckets != null) + if (_buckets != null) { - int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; - int bucket = hashCode % buckets.Length; + int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF; + int bucket = hashCode % _buckets.Length; int last = -1; - for (int i = buckets[bucket]; i >= 0; last = i, i = entries[i].next) + int i = _buckets[bucket]; + while (i >= 0) { - if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) + ref Entry entry = ref _entries[i]; + + if (entry.hashCode == hashCode && _comparer.Equals(entry.key, key)) { if (last < 0) { - buckets[bucket] = entries[i].next; + _buckets[bucket] = entry.next; } else { - entries[last].next = entries[i].next; + _entries[last].next = entry.next; } - value = entries[i].value; + value = entry.value; - entries[i].hashCode = -1; - entries[i].next = freeList; - entries[i].key = default(TKey); - entries[i].value = default(TValue); - freeList = i; - freeCount++; - version++; + entry.hashCode = -1; + entry.next = _freeList; + + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + entry.key = default(TKey); + } + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + entry.value = default(TValue); + } + _freeList = i; + _freeCount++; + _version++; return true; } + + last = i; + i = entry.next; } } value = default(TValue); @@ -629,7 +673,7 @@ namespace System.Collections.Generic int i = FindEntry(key); if (i >= 0) { - value = entries[i].value; + value = _entries[i].value; return true; } value = default(TValue); @@ -652,27 +696,27 @@ namespace System.Collections.Generic { if (array == null) { - throw new ArgumentNullException(nameof(array)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); } if (array.Rank != 1) { - throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); } if (array.GetLowerBound(0) != 0) { - throw new ArgumentException(SR.Arg_NonZeroLowerBound, nameof(array)); + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound); } if (index < 0 || index > array.Length) { - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); } if (array.Length - index < Count) { - throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); } KeyValuePair[] pairs = array as KeyValuePair[]; @@ -683,9 +727,8 @@ namespace System.Collections.Generic else if (array is DictionaryEntry[]) { DictionaryEntry[] dictEntryArray = array as DictionaryEntry[]; - Entry[] entries = this.entries; - - for (int i = 0; i < count; i++) + Entry[] entries = _entries; + for (int i = 0; i < _count; i++) { if (entries[i].hashCode >= 0) { @@ -698,13 +741,13 @@ namespace System.Collections.Generic object[] objects = array as object[]; if (objects == null) { - throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); } try { - int count = this.count; - Entry[] entries = this.entries; + int count = _count; + Entry[] entries = _entries; for (int i = 0; i < count; i++) { if (entries[i].hashCode >= 0) @@ -715,7 +758,7 @@ namespace System.Collections.Generic } catch (ArrayTypeMismatchException) { - throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); } } } @@ -736,7 +779,7 @@ namespace System.Collections.Generic { if (_syncRoot == null) { - System.Threading.Interlocked.CompareExchange(ref _syncRoot, new object(), null); + System.Threading.Interlocked.CompareExchange(ref _syncRoot, new Object(), null); } return _syncRoot; } @@ -771,7 +814,7 @@ namespace System.Collections.Generic int i = FindEntry((TKey)key); if (i >= 0) { - return entries[i].value; + return _entries[i].value; } } return null; @@ -780,10 +823,9 @@ namespace System.Collections.Generic { if (key == null) { - throw new ArgumentNullException(nameof(key)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); } - if (value == null && !(default(TValue) == null)) - throw new ArgumentNullException(nameof(value)); + ThrowHelper.IfNullAndNullsAreIllegalThenThrow(value, ExceptionArgument.value); try { @@ -794,12 +836,12 @@ namespace System.Collections.Generic } catch (InvalidCastException) { - throw new ArgumentException(SR.Format(SR.Arg_WrongType, value, typeof(TValue)), nameof(value)); + ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(TValue)); } } catch (InvalidCastException) { - throw new ArgumentException(SR.Format(SR.Arg_WrongType, key, typeof(TKey)), nameof(key)); + ThrowHelper.ThrowWrongKeyTypeArgumentException(key, typeof(TKey)); } } } @@ -808,7 +850,7 @@ namespace System.Collections.Generic { if (key == null) { - throw new ArgumentNullException(nameof(key)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); } return (key is TKey); } @@ -817,11 +859,9 @@ namespace System.Collections.Generic { if (key == null) { - throw new ArgumentNullException(nameof(key)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); } - - if (value == null && !(default(TValue) == null)) - throw new ArgumentNullException(nameof(value)); + ThrowHelper.IfNullAndNullsAreIllegalThenThrow(value, ExceptionArgument.value); try { @@ -833,12 +873,12 @@ namespace System.Collections.Generic } catch (InvalidCastException) { - throw new ArgumentException(SR.Format(SR.Arg_WrongType, value, typeof(TValue)), nameof(value)); + ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(TValue)); } } catch (InvalidCastException) { - throw new ArgumentException(SR.Format(SR.Arg_WrongType, key, typeof(TKey)), nameof(key)); + ThrowHelper.ThrowWrongKeyTypeArgumentException(key, typeof(TKey)); } } @@ -865,56 +905,58 @@ namespace System.Collections.Generic } } +#if MONO [Serializable] +#endif public struct Enumerator : IEnumerator>, IDictionaryEnumerator { - private Dictionary dictionary; - private int version; - private int index; - private KeyValuePair current; - private int getEnumeratorRetType; // What should Enumerator.Current return? + private Dictionary _dictionary; + private int _version; + private int _index; + private KeyValuePair _current; + private int _getEnumeratorRetType; // What should Enumerator.Current return? internal const int DictEntry = 1; internal const int KeyValuePair = 2; internal Enumerator(Dictionary dictionary, int getEnumeratorRetType) { - this.dictionary = dictionary; - version = dictionary.version; - index = 0; - this.getEnumeratorRetType = getEnumeratorRetType; - current = new KeyValuePair(); + _dictionary = dictionary; + _version = dictionary._version; + _index = 0; + _getEnumeratorRetType = getEnumeratorRetType; + _current = new KeyValuePair(); } public bool MoveNext() { - if (version != dictionary.version) + if (_version != _dictionary._version) { - throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); } // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends. // dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue - while ((uint)index < (uint)dictionary.count) + while ((uint)_index < (uint)_dictionary._count) { - if (dictionary.entries[index].hashCode >= 0) + ref Entry entry = ref _dictionary._entries[_index++]; + + if (entry.hashCode >= 0) { - current = new KeyValuePair(dictionary.entries[index].key, dictionary.entries[index].value); - index++; + _current = new KeyValuePair(entry.key, entry.value); return true; } - index++; } - index = dictionary.count + 1; - current = new KeyValuePair(); + _index = _dictionary._count + 1; + _current = new KeyValuePair(); return false; } public KeyValuePair Current { - get { return current; } + get { return _current; } } public void Dispose() @@ -925,43 +967,43 @@ namespace System.Collections.Generic { get { - if (index == 0 || (index == dictionary.count + 1)) + if (_index == 0 || (_index == _dictionary._count + 1)) { - throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); } - if (getEnumeratorRetType == DictEntry) + if (_getEnumeratorRetType == DictEntry) { - return new System.Collections.DictionaryEntry(current.Key, current.Value); + return new System.Collections.DictionaryEntry(_current.Key, _current.Value); } else { - return new KeyValuePair(current.Key, current.Value); + return new KeyValuePair(_current.Key, _current.Value); } } } void IEnumerator.Reset() { - if (version != dictionary.version) + if (_version != _dictionary._version) { - throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); } - index = 0; - current = new KeyValuePair(); + _index = 0; + _current = new KeyValuePair(); } DictionaryEntry IDictionaryEnumerator.Entry { get { - if (index == 0 || (index == dictionary.count + 1)) + if (_index == 0 || (_index == _dictionary._count + 1)) { - throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); } - return new DictionaryEntry(current.Key, current.Value); + return new DictionaryEntry(_current.Key, _current.Value); } } @@ -969,12 +1011,12 @@ namespace System.Collections.Generic { get { - if (index == 0 || (index == dictionary.count + 1)) + if (_index == 0 || (_index == _dictionary._count + 1)) { - throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); } - return current.Key; + return _current.Key; } } @@ -982,57 +1024,58 @@ namespace System.Collections.Generic { get { - if (index == 0 || (index == dictionary.count + 1)) + if (_index == 0 || (_index == _dictionary._count + 1)) { - throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); } - return current.Value; + return _current.Value; } } } [DebuggerTypeProxy(typeof(DictionaryKeyCollectionDebugView<,>))] [DebuggerDisplay("Count = {Count}")] +#if MONO [Serializable] +#endif public sealed class KeyCollection : ICollection, ICollection, IReadOnlyCollection { - private Dictionary dictionary; + private Dictionary _dictionary; public KeyCollection(Dictionary dictionary) { if (dictionary == null) { - throw new ArgumentNullException(nameof(dictionary)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary); } - this.dictionary = dictionary; + _dictionary = dictionary; } public Enumerator GetEnumerator() { - return new Enumerator(dictionary); + return new Enumerator(_dictionary); } public void CopyTo(TKey[] array, int index) { if (array == null) { - throw new ArgumentNullException(nameof(array)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); } if (index < 0 || index > array.Length) { - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); } - if (array.Length - index < dictionary.Count) + if (array.Length - index < _dictionary.Count) { - throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); } - int count = dictionary.count; - Entry[] entries = dictionary.entries; - + int count = _dictionary._count; + Entry[] entries = _dictionary._entries; for (int i = 0; i < count; i++) { if (entries[i].hashCode >= 0) array[index++] = entries[i].key; @@ -1041,7 +1084,7 @@ namespace System.Collections.Generic public int Count { - get { return dictionary.Count; } + get { return _dictionary.Count; } } bool ICollection.IsReadOnly @@ -1051,59 +1094,60 @@ namespace System.Collections.Generic void ICollection.Add(TKey item) { - throw new NotSupportedException(SR.NotSupported_KeyCollectionSet); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet); } void ICollection.Clear() { - throw new NotSupportedException(SR.NotSupported_KeyCollectionSet); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet); } bool ICollection.Contains(TKey item) { - return dictionary.ContainsKey(item); + return _dictionary.ContainsKey(item); } bool ICollection.Remove(TKey item) { - throw new NotSupportedException(SR.NotSupported_KeyCollectionSet); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet); + return false; } IEnumerator IEnumerable.GetEnumerator() { - return new Enumerator(dictionary); + return new Enumerator(_dictionary); } IEnumerator IEnumerable.GetEnumerator() { - return new Enumerator(dictionary); + return new Enumerator(_dictionary); } void ICollection.CopyTo(Array array, int index) { if (array == null) { - throw new ArgumentNullException(nameof(array)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); } if (array.Rank != 1) { - throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); } if (array.GetLowerBound(0) != 0) { - throw new ArgumentException(SR.Arg_NonZeroLowerBound, nameof(array)); + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound); } if (index < 0 || index > array.Length) { - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); } - if (array.Length - index < dictionary.Count) + if (array.Length - index < _dictionary.Count) { - throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); } TKey[] keys = array as TKey[]; @@ -1116,12 +1160,11 @@ namespace System.Collections.Generic object[] objects = array as object[]; if (objects == null) { - throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); } - int count = dictionary.count; - Entry[] entries = dictionary.entries; - + int count = _dictionary._count; + Entry[] entries = _dictionary._entries; try { for (int i = 0; i < count; i++) @@ -1131,7 +1174,7 @@ namespace System.Collections.Generic } catch (ArrayTypeMismatchException) { - throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); } } } @@ -1143,23 +1186,25 @@ namespace System.Collections.Generic object ICollection.SyncRoot { - get { return ((ICollection)dictionary).SyncRoot; } + get { return ((ICollection)_dictionary).SyncRoot; } } +#if MONO [Serializable] +#endif public struct Enumerator : IEnumerator, System.Collections.IEnumerator { - private Dictionary dictionary; - private int index; - private int version; - private TKey currentKey; + private Dictionary _dictionary; + private int _index; + private int _version; + private TKey _currentKey; internal Enumerator(Dictionary dictionary) { - this.dictionary = dictionary; - version = dictionary.version; - index = 0; - currentKey = default(TKey); + _dictionary = dictionary; + _version = dictionary._version; + _index = 0; + _currentKey = default(TKey); } public void Dispose() @@ -1168,24 +1213,24 @@ namespace System.Collections.Generic public bool MoveNext() { - if (version != dictionary.version) + if (_version != _dictionary._version) { - throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); } - while ((uint)index < (uint)dictionary.count) + while ((uint)_index < (uint)_dictionary._count) { - if (dictionary.entries[index].hashCode >= 0) + ref Entry entry = ref _dictionary._entries[_index++]; + + if (entry.hashCode >= 0) { - currentKey = dictionary.entries[index].key; - index++; + _currentKey = entry.key; return true; } - index++; } - index = dictionary.count + 1; - currentKey = default(TKey); + _index = _dictionary._count + 1; + _currentKey = default(TKey); return false; } @@ -1193,7 +1238,7 @@ namespace System.Collections.Generic { get { - return currentKey; + return _currentKey; } } @@ -1201,69 +1246,70 @@ namespace System.Collections.Generic { get { - if (index == 0 || (index == dictionary.count + 1)) + if (_index == 0 || (_index == _dictionary._count + 1)) { - throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); } - return currentKey; + return _currentKey; } } void System.Collections.IEnumerator.Reset() { - if (version != dictionary.version) + if (_version != _dictionary._version) { - throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); } - index = 0; - currentKey = default(TKey); + _index = 0; + _currentKey = default(TKey); } } } [DebuggerTypeProxy(typeof(DictionaryValueCollectionDebugView<,>))] [DebuggerDisplay("Count = {Count}")] +#if MONO [Serializable] +#endif public sealed class ValueCollection : ICollection, ICollection, IReadOnlyCollection { - private Dictionary dictionary; + private Dictionary _dictionary; public ValueCollection(Dictionary dictionary) { if (dictionary == null) { - throw new ArgumentNullException(nameof(dictionary)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary); } - this.dictionary = dictionary; + _dictionary = dictionary; } public Enumerator GetEnumerator() { - return new Enumerator(dictionary); + return new Enumerator(_dictionary); } public void CopyTo(TValue[] array, int index) { if (array == null) { - throw new ArgumentNullException(nameof(array)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); } if (index < 0 || index > array.Length) { - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); } - if (array.Length - index < dictionary.Count) + if (array.Length - index < _dictionary.Count) { - throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); } - int count = dictionary.count; - Entry[] entries = dictionary.entries; - + int count = _dictionary._count; + Entry[] entries = _dictionary._entries; for (int i = 0; i < count; i++) { if (entries[i].hashCode >= 0) array[index++] = entries[i].value; @@ -1272,7 +1318,7 @@ namespace System.Collections.Generic public int Count { - get { return dictionary.Count; } + get { return _dictionary.Count; } } bool ICollection.IsReadOnly @@ -1282,58 +1328,59 @@ namespace System.Collections.Generic void ICollection.Add(TValue item) { - throw new NotSupportedException(SR.NotSupported_ValueCollectionSet); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet); } bool ICollection.Remove(TValue item) { - throw new NotSupportedException(SR.NotSupported_ValueCollectionSet); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet); + return false; } void ICollection.Clear() { - throw new NotSupportedException(SR.NotSupported_ValueCollectionSet); + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet); } bool ICollection.Contains(TValue item) { - return dictionary.ContainsValue(item); + return _dictionary.ContainsValue(item); } IEnumerator IEnumerable.GetEnumerator() { - return new Enumerator(dictionary); + return new Enumerator(_dictionary); } IEnumerator IEnumerable.GetEnumerator() { - return new Enumerator(dictionary); + return new Enumerator(_dictionary); } void ICollection.CopyTo(Array array, int index) { if (array == null) { - throw new ArgumentNullException(nameof(array)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); } if (array.Rank != 1) { - throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); } if (array.GetLowerBound(0) != 0) { - throw new ArgumentException(SR.Arg_NonZeroLowerBound, nameof(array)); + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound); } if (index < 0 || index > array.Length) { - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); } - if (array.Length - index < dictionary.Count) - throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); + if (array.Length - index < _dictionary.Count) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); TValue[] values = array as TValue[]; if (values != null) @@ -1345,12 +1392,11 @@ namespace System.Collections.Generic object[] objects = array as object[]; if (objects == null) { - throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); } - int count = dictionary.count; - Entry[] entries = dictionary.entries; - + int count = _dictionary._count; + Entry[] entries = _dictionary._entries; try { for (int i = 0; i < count; i++) @@ -1360,7 +1406,7 @@ namespace System.Collections.Generic } catch (ArrayTypeMismatchException) { - throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); } } } @@ -1372,23 +1418,25 @@ namespace System.Collections.Generic object ICollection.SyncRoot { - get { return ((ICollection)dictionary).SyncRoot; } + get { return ((ICollection)_dictionary).SyncRoot; } } +#if MONO [Serializable] +#endif public struct Enumerator : IEnumerator, System.Collections.IEnumerator { - private Dictionary dictionary; - private int index; - private int version; - private TValue currentValue; + private Dictionary _dictionary; + private int _index; + private int _version; + private TValue _currentValue; internal Enumerator(Dictionary dictionary) { - this.dictionary = dictionary; - version = dictionary.version; - index = 0; - currentValue = default(TValue); + _dictionary = dictionary; + _version = dictionary._version; + _index = 0; + _currentValue = default(TValue); } public void Dispose() @@ -1397,23 +1445,23 @@ namespace System.Collections.Generic public bool MoveNext() { - if (version != dictionary.version) + if (_version != _dictionary._version) { - throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); } - while ((uint)index < (uint)dictionary.count) + while ((uint)_index < (uint)_dictionary._count) { - if (dictionary.entries[index].hashCode >= 0) + ref Entry entry = ref _dictionary._entries[_index++]; + + if (entry.hashCode >= 0) { - currentValue = dictionary.entries[index].value; - index++; + _currentValue = entry.value; return true; } - index++; } - index = dictionary.count + 1; - currentValue = default(TValue); + _index = _dictionary._count + 1; + _currentValue = default(TValue); return false; } @@ -1421,7 +1469,7 @@ namespace System.Collections.Generic { get { - return currentValue; + return _currentValue; } } @@ -1429,30 +1477,25 @@ namespace System.Collections.Generic { get { - if (index == 0 || (index == dictionary.count + 1)) + if (_index == 0 || (_index == _dictionary._count + 1)) { - throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); } - return currentValue; + return _currentValue; } } void System.Collections.IEnumerator.Reset() { - if (version != dictionary.version) + if (_version != _dictionary._version) { - throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); } - index = 0; - currentValue = default(TValue); + _index = 0; + _currentValue = default(TValue); } } } } - - internal class DictionaryHashHelpers - { - internal static ConditionalWeakTable SerializationInfoTable { get; } = new ConditionalWeakTable(); - } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/ICollection.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/ICollection.cs index 52852aa1fb..78ee5cb1f5 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/ICollection.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/ICollection.cs @@ -4,7 +4,6 @@ using System; using System.Runtime.CompilerServices; -using System.Diagnostics.Contracts; namespace System.Collections.Generic { diff --git a/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/DebugView.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/ICollectionDebugView.cs similarity index 76% rename from external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/DebugView.cs rename to external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/ICollectionDebugView.cs index 79330f0320..9916e857e2 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/DebugView.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/ICollectionDebugView.cs @@ -2,20 +2,20 @@ // 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.Diagnostics; -using System.Diagnostics.Contracts; namespace System.Collections.Generic { - internal sealed class Mscorlib_CollectionDebugView + internal sealed class ICollectionDebugView { - private ICollection _collection; + private readonly ICollection _collection; - public Mscorlib_CollectionDebugView(ICollection collection) + public ICollectionDebugView(ICollection collection) { if (collection == null) + { throw new ArgumentNullException(nameof(collection)); + } _collection = collection; } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IDictionary.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IDictionary.cs index a73a2f55bd..05677da3b2 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IDictionary.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IDictionary.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics.Contracts; namespace System.Collections.Generic { diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IDictionaryDebugView.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IDictionaryDebugView.cs new file mode 100644 index 0000000000..4721642fee --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IDictionaryDebugView.cs @@ -0,0 +1,80 @@ +// 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.Diagnostics; + +namespace System.Collections.Generic +{ + internal sealed class IDictionaryDebugView + { + private readonly IDictionary _dict; + + public IDictionaryDebugView(IDictionary dictionary) + { + if (dictionary == null) + throw new ArgumentNullException(nameof(dictionary)); + + _dict = dictionary; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public KeyValuePair[] Items + { + get + { + KeyValuePair[] items = new KeyValuePair[_dict.Count]; + _dict.CopyTo(items, 0); + return items; + } + } + } + + internal sealed class DictionaryKeyCollectionDebugView + { + private readonly ICollection _collection; + + public DictionaryKeyCollectionDebugView(ICollection collection) + { + if (collection == null) + throw new ArgumentNullException(nameof(collection)); + + _collection = collection; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public TKey[] Items + { + get + { + TKey[] items = new TKey[_collection.Count]; + _collection.CopyTo(items, 0); + return items; + } + } + } + + internal sealed class DictionaryValueCollectionDebugView + { + private readonly ICollection _collection; + + public DictionaryValueCollectionDebugView(ICollection collection) + { + if (collection == null) + throw new ArgumentNullException(nameof(collection)); + + _collection = collection; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public TValue[] Items + { + get + { + TValue[] items = new TValue[_collection.Count]; + _collection.CopyTo(items, 0); + return items; + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IEnumerable.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IEnumerable.cs index 84264d5cf0..ddb798e8a6 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IEnumerable.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IEnumerable.cs @@ -6,7 +6,6 @@ using System; using System.Collections; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; -using System.Diagnostics.Contracts; namespace System.Collections.Generic { diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IList.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IList.cs index 43d6659da9..2abc7b9142 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IList.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IList.cs @@ -5,7 +5,6 @@ using System; using System.Collections; using System.Runtime.CompilerServices; -using System.Diagnostics.Contracts; namespace System.Collections.Generic { diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IReadOnlyCollection.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IReadOnlyCollection.cs index 09ee89f035..9eea39de22 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IReadOnlyCollection.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IReadOnlyCollection.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace System.Collections.Generic diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IReadOnlyDictionary.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IReadOnlyDictionary.cs index 169e2958bb..300b996611 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IReadOnlyDictionary.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IReadOnlyDictionary.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics.Contracts; namespace System.Collections.Generic { diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IReadOnlyList.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IReadOnlyList.cs index 00b5be65ff..7193805b06 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IReadOnlyList.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/IReadOnlyList.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace System.Collections.Generic diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/KeyNotFoundException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/KeyNotFoundException.cs index cdd6faf030..48eddb8745 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/KeyNotFoundException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/KeyNotFoundException.cs @@ -7,29 +7,30 @@ using System.Runtime.Serialization; namespace System.Collections.Generic { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class KeyNotFoundException : SystemException { public KeyNotFoundException() : base(SR.Arg_KeyNotFound) { - HResult = __HResults.COR_E_KEYNOTFOUND; + HResult = HResults.COR_E_KEYNOTFOUND; } public KeyNotFoundException(String message) : base(message) { - HResult = __HResults.COR_E_KEYNOTFOUND; + HResult = HResults.COR_E_KEYNOTFOUND; } public KeyNotFoundException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_KEYNOTFOUND; + HResult = HResults.COR_E_KEYNOTFOUND; } protected KeyNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/KeyValuePair.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/KeyValuePair.cs index 0fe9bf67ea..612d243a2f 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/KeyValuePair.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/KeyValuePair.cs @@ -48,10 +48,13 @@ namespace System.Collections.Generic // It is used by the IEnumerable implementation for both IDictionary // and IReadOnlyDictionary. [Serializable] - public struct KeyValuePair +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif + public readonly struct KeyValuePair { - private TKey key; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private TValue value; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. + private readonly TKey key; // Do not rename (binary serialization) + private readonly TValue value; // Do not rename (binary serialization) public KeyValuePair(TKey key, TValue value) { diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/List.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/List.cs new file mode 100644 index 0000000000..56bbea895e --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/List.cs @@ -0,0 +1,1249 @@ +// 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.Collections.ObjectModel; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace System.Collections.Generic +{ + // Implements a variable-size List that uses an array of objects to store the + // elements. A List has a capacity, which is the allocated length + // of the internal array. As elements are added to a List, the capacity + // of the List is automatically increased as required by reallocating the + // internal array. + // + [DebuggerTypeProxy(typeof(ICollectionDebugView<>))] + [DebuggerDisplay("Count = {Count}")] + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class List : IList, System.Collections.IList, IReadOnlyList + { + private const int DefaultCapacity = 4; + + private T[] _items; // Do not rename (binary serialization) + private int _size; // Do not rename (binary serialization) + private int _version; // Do not rename (binary serialization) + [NonSerialized] + private object _syncRoot; + + private static readonly T[] s_emptyArray = new T[0]; + + // Constructs a List. The list is initially empty and has a capacity + // of zero. Upon adding the first element to the list the capacity is + // increased to DefaultCapacity, and then increased in multiples of two + // as required. + public List() + { + _items = s_emptyArray; + } + + // Constructs a List with a given initial capacity. The list is + // initially empty, but will have room for the given number of elements + // before any reallocations are required. + // + public List(int capacity) + { + if (capacity < 0) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + + if (capacity == 0) + _items = s_emptyArray; + else + _items = new T[capacity]; + } + + // Constructs a List, copying the contents of the given collection. The + // size and capacity of the new list will both be equal to the size of the + // given collection. + // + public List(IEnumerable collection) + { + if (collection == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection); + + ICollection c = collection as ICollection; + if (c != null) + { + int count = c.Count; + if (count == 0) + { + _items = s_emptyArray; + } + else + { + _items = new T[count]; + c.CopyTo(_items, 0); + _size = count; + } + } + else + { + _size = 0; + _items = s_emptyArray; + AddEnumerable(collection); + } + } + + // Gets and sets the capacity of this list. The capacity is the size of + // the internal array used to hold items. When set, the internal + // array of the list is reallocated to the given capacity. + // + public int Capacity + { + get + { + return _items.Length; + } + set + { + if (value < _size) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_SmallCapacity); + } + + if (value != _items.Length) + { + if (value > 0) + { + T[] newItems = new T[value]; + if (_size > 0) + { + Array.Copy(_items, 0, newItems, 0, _size); + } + _items = newItems; + } + else + { + _items = s_emptyArray; + } + } + } + } + + // Read-only property describing how many elements are in the List. + public int Count + { + get + { + return _size; + } + } + + bool System.Collections.IList.IsFixedSize + { + get { return false; } + } + + // Is this List read-only? + bool ICollection.IsReadOnly + { + get { return false; } + } + + bool System.Collections.IList.IsReadOnly + { + get { return false; } + } + + // Is this List synchronized (thread-safe)? + bool System.Collections.ICollection.IsSynchronized + { + get { return false; } + } + + // Synchronization root for this object. + object System.Collections.ICollection.SyncRoot + { + get + { + if (_syncRoot == null) + { + System.Threading.Interlocked.CompareExchange(ref _syncRoot, new object(), null); + } + return _syncRoot; + } + } + + // Sets or Gets the element at the given index. + public T this[int index] + { + get + { + // Following trick can reduce the range check by one + if ((uint)index >= (uint)_size) + { + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + } + return _items[index]; + } + + set + { + if ((uint)index >= (uint)_size) + { + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + } + _items[index] = value; + _version++; + } + } + + private static bool IsCompatibleObject(object value) + { + // Non-null values are fine. Only accept nulls if T is a class or Nullable. + // Note that default(T) is not equal to null for value types except when T is Nullable. + return ((value is T) || (value == null && default(T) == null)); + } + + object System.Collections.IList.this[int index] + { + get + { + return this[index]; + } + set + { + ThrowHelper.IfNullAndNullsAreIllegalThenThrow(value, ExceptionArgument.value); + + try + { + this[index] = (T)value; + } + catch (InvalidCastException) + { + ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T)); + } + } + } + + // Adds the given object to the end of this list. The size of the list is + // increased by one. If required, the capacity of the list is doubled + // before adding the new element. + // + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(T item) + { + var array = _items; + var size = _size; + _version++; + if ((uint)size < (uint)array.Length) + { + _size = size + 1; + array[size] = item; + } + else + { + AddWithResize(item); + } + } + + // Non-inline from List.Add to improve its code quality as uncommon path + [MethodImpl(MethodImplOptions.NoInlining)] + private void AddWithResize(T item) + { + var size = _size; + EnsureCapacity(size + 1); + _size = size + 1; + _items[size] = item; + } + + int System.Collections.IList.Add(Object item) + { + ThrowHelper.IfNullAndNullsAreIllegalThenThrow(item, ExceptionArgument.item); + + try + { + Add((T)item); + } + catch (InvalidCastException) + { + ThrowHelper.ThrowWrongValueTypeArgumentException(item, typeof(T)); + } + + return Count - 1; + } + + // Adds the elements of the given collection to the end of this list. If + // required, the capacity of the list is increased to twice the previous + // capacity or the new size, whichever is larger. + // + public void AddRange(IEnumerable collection) + { + InsertRange(_size, collection); + } + + public ReadOnlyCollection AsReadOnly() + { + return new ReadOnlyCollection(this); + } + + // Searches a section of the list for a given element using a binary search + // algorithm. Elements of the list are compared to the search value using + // the given IComparer interface. If comparer is null, elements of + // the list are compared to the search value using the IComparable + // interface, which in that case must be implemented by all elements of the + // list and the given search value. This method assumes that the given + // section of the list is already sorted; if this is not the case, the + // result will be incorrect. + // + // The method returns the index of the given value in the list. If the + // list does not contain the given value, the method returns a negative + // integer. The bitwise complement operator (~) can be applied to a + // negative result to produce the index of the first element (if any) that + // is larger than the given search value. This is also the index at which + // the search value should be inserted into the list in order for the list + // to remain sorted. + // + // The method uses the Array.BinarySearch method to perform the + // search. + // + public int BinarySearch(int index, int count, T item, IComparer comparer) + { + if (index < 0) + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); + if (count < 0) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + if (_size - index < count) + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); + + return Array.BinarySearch(_items, index, count, item, comparer); + } + + public int BinarySearch(T item) + { + return BinarySearch(0, Count, item, null); + } + + public int BinarySearch(T item, IComparer comparer) + { + return BinarySearch(0, Count, item, comparer); + } + + + // Clears the contents of List. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + int size = _size; + _size = 0; + _version++; + if (size > 0) + { + Array.Clear(_items, 0, size); // Clear the elements so that the gc can reclaim the references. + } + } + else + { + _size = 0; + _version++; + } + } + + // Contains returns true if the specified element is in the List. + // It does a linear, O(n) search. Equality is determined by calling + // EqualityComparer.Default.Equals(). + // + public bool Contains(T item) + { + // PERF: IndexOf calls Array.IndexOf, which internally + // calls EqualityComparer.Default.IndexOf, which + // is specialized for different types. This + // boosts performance since instead of making a + // virtual method call each iteration of the loop, + // via EqualityComparer.Default.Equals, we + // only make one virtual call to EqualityComparer.IndexOf. + + return _size != 0 && IndexOf(item) != -1; + } + + bool System.Collections.IList.Contains(object item) + { + if (IsCompatibleObject(item)) + { + return Contains((T)item); + } + return false; + } + + public List ConvertAll(Converter converter) + { + if (converter == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.converter); + } + + List list = new List(_size); + for (int i = 0; i < _size; i++) + { + list._items[i] = converter(_items[i]); + } + list._size = _size; + return list; + } + + // Copies this List into array, which must be of a + // compatible array type. + public void CopyTo(T[] array) + { + CopyTo(array, 0); + } + + // Copies this List into array, which must be of a + // compatible array type. + void System.Collections.ICollection.CopyTo(Array array, int arrayIndex) + { + if ((array != null) && (array.Rank != 1)) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); + } + + try + { + // Array.Copy will check for NULL. + Array.Copy(_items, 0, array, arrayIndex, _size); + } + catch (ArrayTypeMismatchException) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + } + + // Copies a section of this list to the given array at the given index. + // + // The method uses the Array.Copy method to copy the elements. + // + public void CopyTo(int index, T[] array, int arrayIndex, int count) + { + if (_size - index < count) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); + } + + // Delegate rest of error checking to Array.Copy. + Array.Copy(_items, index, array, arrayIndex, count); + } + + public void CopyTo(T[] array, int arrayIndex) + { + // Delegate rest of error checking to Array.Copy. + Array.Copy(_items, 0, array, arrayIndex, _size); + } + + // Ensures that the capacity of this list is at least the given minimum + // value. If the current capacity of the list is less than min, the + // capacity is increased to twice the current capacity or to min, + // whichever is larger. + // + private void EnsureCapacity(int min) + { + if (_items.Length < min) + { + int newCapacity = _items.Length == 0 ? DefaultCapacity : _items.Length * 2; + // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow. + // Note that this check works even when _items.Length overflowed thanks to the (uint) cast + if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength; + if (newCapacity < min) newCapacity = min; + Capacity = newCapacity; + } + } + + public bool Exists(Predicate match) + { + return FindIndex(match) != -1; + } + + public T Find(Predicate match) + { + if (match == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); + } + + for (int i = 0; i < _size; i++) + { + if (match(_items[i])) + { + return _items[i]; + } + } + return default(T); + } + + public List FindAll(Predicate match) + { + if (match == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); + } + + List list = new List(); + for (int i = 0; i < _size; i++) + { + if (match(_items[i])) + { + list.Add(_items[i]); + } + } + return list; + } + + public int FindIndex(Predicate match) + { + return FindIndex(0, _size, match); + } + + public int FindIndex(int startIndex, Predicate match) + { + return FindIndex(startIndex, _size - startIndex, match); + } + + public int FindIndex(int startIndex, int count, Predicate match) + { + if ((uint)startIndex > (uint)_size) + { + ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index(); + } + + if (count < 0 || startIndex > _size - count) + { + ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count(); + } + + if (match == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); + } + + int endIndex = startIndex + count; + for (int i = startIndex; i < endIndex; i++) + { + if (match(_items[i])) return i; + } + return -1; + } + + public T FindLast(Predicate match) + { + if (match == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); + } + + for (int i = _size - 1; i >= 0; i--) + { + if (match(_items[i])) + { + return _items[i]; + } + } + return default(T); + } + + public int FindLastIndex(Predicate match) + { + return FindLastIndex(_size - 1, _size, match); + } + + public int FindLastIndex(int startIndex, Predicate match) + { + return FindLastIndex(startIndex, startIndex + 1, match); + } + + public int FindLastIndex(int startIndex, int count, Predicate match) + { + if (match == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); + } + + if (_size == 0) + { + // Special case for 0 length List + if (startIndex != -1) + { + ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index(); + } + } + else + { + // Make sure we're not out of range + if ((uint)startIndex >= (uint)_size) + { + ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index(); + } + } + + // 2nd have of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0. + if (count < 0 || startIndex - count + 1 < 0) + { + ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count(); + } + + int endIndex = startIndex - count; + for (int i = startIndex; i > endIndex; i--) + { + if (match(_items[i])) + { + return i; + } + } + return -1; + } + + public void ForEach(Action action) + { + if (action == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.action); + } + + int version = _version; + + for (int i = 0; i < _size; i++) + { + if (version != _version) + { + break; + } + action(_items[i]); + } + + if (version != _version) + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); + } + + // Returns an enumerator for this list with the given + // permission for removal of elements. If modifications made to the list + // while an enumeration is in progress, the MoveNext and + // GetObject methods of the enumerator will throw an exception. + // + public Enumerator GetEnumerator() + { + return new Enumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(this); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return new Enumerator(this); + } + + public List GetRange(int index, int count) + { + if (index < 0) + { + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); + } + + if (count < 0) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + } + + if (_size - index < count) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); + } + + List list = new List(count); + Array.Copy(_items, index, list._items, 0, count); + list._size = count; + return list; + } + + + // Returns the index of the first occurrence of a given value in a range of + // this list. The list is searched forwards from beginning to end. + // The elements of the list are compared to the given value using the + // Object.Equals method. + // + // This method uses the Array.IndexOf method to perform the + // search. + // + public int IndexOf(T item) + { + return Array.IndexOf(_items, item, 0, _size); + } + + int System.Collections.IList.IndexOf(object item) + { + if (IsCompatibleObject(item)) + { + return IndexOf((T)item); + } + return -1; + } + + // Returns the index of the first occurrence of a given value in a range of + // this list. The list is searched forwards, starting at index + // index and ending at count number of elements. The + // elements of the list are compared to the given value using the + // Object.Equals method. + // + // This method uses the Array.IndexOf method to perform the + // search. + // + public int IndexOf(T item, int index) + { + if (index > _size) + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + return Array.IndexOf(_items, item, index, _size - index); + } + + // Returns the index of the first occurrence of a given value in a range of + // this list. The list is searched forwards, starting at index + // index and upto count number of elements. The + // elements of the list are compared to the given value using the + // Object.Equals method. + // + // This method uses the Array.IndexOf method to perform the + // search. + // + public int IndexOf(T item, int index, int count) + { + if (index > _size) + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + + if (count < 0 || index > _size - count) + ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count(); + + return Array.IndexOf(_items, item, index, count); + } + + // Inserts an element into this list at a given index. The size of the list + // is increased by one. If required, the capacity of the list is doubled + // before inserting the new element. + // + public void Insert(int index, T item) + { + // Note that insertions at the end are legal. + if ((uint)index > (uint)_size) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_ListInsert); + } + if (_size == _items.Length) EnsureCapacity(_size + 1); + if (index < _size) + { + Array.Copy(_items, index, _items, index + 1, _size - index); + } + _items[index] = item; + _size++; + _version++; + } + + void System.Collections.IList.Insert(int index, Object item) + { + ThrowHelper.IfNullAndNullsAreIllegalThenThrow(item, ExceptionArgument.item); + + try + { + Insert(index, (T)item); + } + catch (InvalidCastException) + { + ThrowHelper.ThrowWrongValueTypeArgumentException(item, typeof(T)); + } + } + + // Inserts the elements of the given collection at a given index. If + // required, the capacity of the list is increased to twice the previous + // capacity or the new size, whichever is larger. Ranges may be added + // to the end of the list by setting index to the List's size. + // + public void InsertRange(int index, IEnumerable collection) + { + if (collection == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection); + } + + if ((uint)index > (uint)_size) + { + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + } + + ICollection c = collection as ICollection; + if (c != null) + { // if collection is ICollection + int count = c.Count; + if (count > 0) + { + EnsureCapacity(_size + count); + if (index < _size) + { + Array.Copy(_items, index, _items, index + count, _size - index); + } + + // If we're inserting a List into itself, we want to be able to deal with that. + if (this == c) + { + // Copy first part of _items to insert location + Array.Copy(_items, 0, _items, index, index); + // Copy last part of _items back to inserted location + Array.Copy(_items, index + count, _items, index * 2, _size - index); + } + else + { + c.CopyTo(_items, index); + } + _size += count; + } + } + else if (index < _size) + { + // We're inserting a lazy enumerable. Call Insert on each of the constituent items. + using (IEnumerator en = collection.GetEnumerator()) + { + while (en.MoveNext()) + { + Insert(index++, en.Current); + } + } + } + else + { + // We're adding a lazy enumerable because the index is at the end of this list. + AddEnumerable(collection); + } + _version++; + } + + // Returns the index of the last occurrence of a given value in a range of + // this list. The list is searched backwards, starting at the end + // and ending at the first element in the list. The elements of the list + // are compared to the given value using the Object.Equals method. + // + // This method uses the Array.LastIndexOf method to perform the + // search. + // + public int LastIndexOf(T item) + { + if (_size == 0) + { // Special case for empty list + return -1; + } + else + { + return LastIndexOf(item, _size - 1, _size); + } + } + + // Returns the index of the last occurrence of a given value in a range of + // this list. The list is searched backwards, starting at index + // index and ending at the first element in the list. The + // elements of the list are compared to the given value using the + // Object.Equals method. + // + // This method uses the Array.LastIndexOf method to perform the + // search. + // + public int LastIndexOf(T item, int index) + { + if (index >= _size) + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + return LastIndexOf(item, index, index + 1); + } + + // Returns the index of the last occurrence of a given value in a range of + // this list. The list is searched backwards, starting at index + // index and upto count elements. The elements of + // the list are compared to the given value using the Object.Equals + // method. + // + // This method uses the Array.LastIndexOf method to perform the + // search. + // + public int LastIndexOf(T item, int index, int count) + { + if ((Count != 0) && (index < 0)) + { + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); + } + + if ((Count != 0) && (count < 0)) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + } + + if (_size == 0) + { // Special case for empty list + return -1; + } + + if (index >= _size) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_BiggerThanCollection); + } + + if (count > index + 1) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_BiggerThanCollection); + } + + return Array.LastIndexOf(_items, item, index, count); + } + + // Removes the element at the given index. The size of the list is + // decreased by one. + public bool Remove(T item) + { + int index = IndexOf(item); + if (index >= 0) + { + RemoveAt(index); + return true; + } + + return false; + } + + void System.Collections.IList.Remove(object item) + { + if (IsCompatibleObject(item)) + { + Remove((T)item); + } + } + + // This method removes all items which matches the predicate. + // The complexity is O(n). + public int RemoveAll(Predicate match) + { + if (match == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); + } + + int freeIndex = 0; // the first free slot in items array + + // Find the first item which needs to be removed. + while (freeIndex < _size && !match(_items[freeIndex])) freeIndex++; + if (freeIndex >= _size) return 0; + + int current = freeIndex + 1; + while (current < _size) + { + // Find the first item which needs to be kept. + while (current < _size && match(_items[current])) current++; + + if (current < _size) + { + // copy item to the free slot. + _items[freeIndex++] = _items[current++]; + } + } + + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + Array.Clear(_items, freeIndex, _size - freeIndex); // Clear the elements so that the gc can reclaim the references. + } + + int result = _size - freeIndex; + _size = freeIndex; + _version++; + return result; + } + + // Removes the element at the given index. The size of the list is + // decreased by one. + public void RemoveAt(int index) + { + if ((uint)index >= (uint)_size) + { + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + } + _size--; + if (index < _size) + { + Array.Copy(_items, index + 1, _items, index, _size - index); + } + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + _items[_size] = default(T); + } + _version++; + } + + // Removes a range of elements from this list. + public void RemoveRange(int index, int count) + { + if (index < 0) + { + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); + } + + if (count < 0) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + } + + if (_size - index < count) + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); + + if (count > 0) + { + int i = _size; + _size -= count; + if (index < _size) + { + Array.Copy(_items, index + count, _items, index, _size - index); + } + + _version++; + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + Array.Clear(_items, _size, count); + } + } + } + + // Reverses the elements in this list. + public void Reverse() + { + Reverse(0, Count); + } + + // Reverses the elements in a range of this list. Following a call to this + // method, an element in the range given by index and count + // which was previously located at index i will now be located at + // index index + (index + count - i - 1). + // + public void Reverse(int index, int count) + { + if (index < 0) + { + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); + } + + if (count < 0) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + } + + if (_size - index < count) + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); + + if (count > 1) + { + Array.Reverse(_items, index, count); + } + _version++; + } + + // Sorts the elements in this list. Uses the default comparer and + // Array.Sort. + public void Sort() + { + Sort(0, Count, null); + } + + // Sorts the elements in this list. Uses Array.Sort with the + // provided comparer. + public void Sort(IComparer comparer) + { + Sort(0, Count, comparer); + } + + // Sorts the elements in a section of this list. The sort compares the + // elements to each other using the given IComparer interface. If + // comparer is null, the elements are compared to each other using + // the IComparable interface, which in that case must be implemented by all + // elements of the list. + // + // This method uses the Array.Sort method to sort the elements. + // + public void Sort(int index, int count, IComparer comparer) + { + if (index < 0) + { + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); + } + + if (count < 0) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + } + + if (_size - index < count) + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); + + if (count > 1) + { + Array.Sort(_items, index, count, comparer); + } + _version++; + } + + public void Sort(Comparison comparison) + { + if (comparison == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparison); + } + + if (_size > 1) + { + ArraySortHelper.Sort(_items, 0, _size, comparison); + } + _version++; + } + + // ToArray returns an array containing the contents of the List. + // This requires copying the List, which is an O(n) operation. + public T[] ToArray() + { + if (_size == 0) + { + return s_emptyArray; + } + + T[] array = new T[_size]; + Array.Copy(_items, 0, array, 0, _size); + return array; + } + + // Sets the capacity of this list to the size of the list. This method can + // be used to minimize a list's memory overhead once it is known that no + // new elements will be added to the list. To completely clear a list and + // release all memory referenced by the list, execute the following + // statements: + // + // list.Clear(); + // list.TrimExcess(); + // + public void TrimExcess() + { + int threshold = (int)(((double)_items.Length) * 0.9); + if (_size < threshold) + { + Capacity = _size; + } + } + + public bool TrueForAll(Predicate match) + { + if (match == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); + } + + for (int i = 0; i < _size; i++) + { + if (!match(_items[i])) + { + return false; + } + } + return true; + } + + private void AddEnumerable(IEnumerable enumerable) + { + Debug.Assert(enumerable != null); + Debug.Assert(!(enumerable is ICollection), "We should have optimized for this beforehand."); + + using (IEnumerator en = enumerable.GetEnumerator()) + { + _version++; // Even if the enumerable has no items, we can update _version. + + while (en.MoveNext()) + { + // Capture Current before doing anything else. If this throws + // an exception, we want to make a clean break. + T current = en.Current; + + if (_size == _items.Length) + { + EnsureCapacity(_size + 1); + } + + _items[_size++] = current; + } + } + } + + public struct Enumerator : IEnumerator, System.Collections.IEnumerator + { + private List _list; + private int _index; + private int _version; + private T _current; + + internal Enumerator(List list) + { + _list = list; + _index = 0; + _version = list._version; + _current = default(T); + } + + public void Dispose() + { + } + + public bool MoveNext() + { + List localList = _list; + + if (_version == localList._version && ((uint)_index < (uint)localList._size)) + { + _current = localList._items[_index]; + _index++; + return true; + } + return MoveNextRare(); + } + + private bool MoveNextRare() + { + if (_version != _list._version) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); + } + + _index = _list._size + 1; + _current = default(T); + return false; + } + + public T Current + { + get + { + return _current; + } + } + + object System.Collections.IEnumerator.Current + { + get + { + if (_index == 0 || _index == _list._size + 1) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); + } + return Current; + } + } + + void System.Collections.IEnumerator.Reset() + { + if (_version != _list._version) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); + } + + _index = 0; + _current = default(T); + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs new file mode 100644 index 0000000000..ef44fefc8e --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs @@ -0,0 +1,38 @@ +// 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.Runtime.Serialization; + +namespace System.Collections.Generic +{ + // NonRandomizedStringEqualityComparer is the comparer used by default with the Dictionary + // We use NonRandomizedStringEqualityComparer as default comparer as it doesnt use the randomized string hashing which + // keeps the performance not affected till we hit collision threshold and then we switch to the comparer which is using + // randomized string hashing. + [Serializable] // Required for compatibility with .NET Core 2.0 as we exposed the NonRandomizedStringEqualityComparer inside the serialization blob +#if CORECLR + internal +#else + public +#endif + sealed class NonRandomizedStringEqualityComparer : EqualityComparer, ISerializable + { + internal static new IEqualityComparer Default { get; } = new NonRandomizedStringEqualityComparer(); + + private NonRandomizedStringEqualityComparer() { } + + // This is used by the serialization engine. + private NonRandomizedStringEqualityComparer(SerializationInfo information, StreamingContext context) { } + + public sealed override bool Equals(string x, string y) => string.Equals(x, y); + + public sealed override int GetHashCode(string obj) => obj?.GetLegacyNonRandomizedHashCode() ?? 0; + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + // We are doing this to stay compatible with .NET Framework. + info.SetType(typeof(GenericEqualityComparer)); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Collections/HashHelpers.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/HashHelpers.cs new file mode 100644 index 0000000000..49cff85b58 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/HashHelpers.cs @@ -0,0 +1,108 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; +using System.Threading; + +namespace System.Collections +{ + internal static class HashHelpers + { + public const int HashCollisionThreshold = 100; + + public const int HashPrime = 101; + + // Table of prime numbers to use as hash table sizes. + // A typical resize algorithm would pick the smallest prime number in this array + // that is larger than twice the previous capacity. + // Suppose our Hashtable currently has capacity x and enough elements are added + // such that a resize needs to occur. Resizing first computes 2x then finds the + // first prime in the table greater than 2x, i.e. if primes are ordered + // p_1, p_2, ..., p_i, ..., it finds p_n such that p_n-1 < 2x < p_n. + // Doubling is important for preserving the asymptotic complexity of the + // hashtable operations such as add. Having a prime guarantees that double + // hashing does not lead to infinite loops. IE, your hash function will be + // h1(key) + i*h2(key), 0 <= i < size. h2 and the size must be relatively prime. + public static readonly int[] primes = { + 3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919, + 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, + 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437, + 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263, + 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369}; + + public static bool IsPrime(int candidate) + { + if ((candidate & 1) != 0) + { + int limit = (int)Math.Sqrt(candidate); + for (int divisor = 3; divisor <= limit; divisor += 2) + { + if ((candidate % divisor) == 0) + return false; + } + return true; + } + return (candidate == 2); + } + + public static int GetPrime(int min) + { + if (min < 0) + throw new ArgumentException(SR.Arg_HTCapacityOverflow); + + for (int i = 0; i < primes.Length; i++) + { + int prime = primes[i]; + if (prime >= min) return prime; + } + + //outside of our predefined table. + //compute the hard way. + for (int i = (min | 1); i < Int32.MaxValue; i += 2) + { + if (IsPrime(i) && ((i - 1) % HashPrime != 0)) + return i; + } + return min; + } + + // Returns size of hashtable to grow to. + public static int ExpandPrime(int oldSize) + { + int newSize = 2 * oldSize; + + // Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow. + // Note that this check works even when _items.Length overflowed thanks to the (uint) cast + if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize) + { + Debug.Assert(MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength"); + return MaxPrimeArrayLength; + } + + return GetPrime(newSize); + } + + + // This is the maximum prime smaller than Array.MaxArrayLength + public const int MaxPrimeArrayLength = 0x7FEFFFFD; + + + // Used by Hashtable and Dictionary's SeralizationInfo .ctor's to store the SeralizationInfo + // object until OnDeserialization is called. + private static ConditionalWeakTable s_serializationInfoTable; + + internal static ConditionalWeakTable SerializationInfoTable + { + get + { + if (s_serializationInfoTable == null) + Interlocked.CompareExchange(ref s_serializationInfoTable, new ConditionalWeakTable(), null); + + return s_serializationInfoTable; + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Collections/ICollection.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/ICollection.cs index 80ea092363..f0149020d2 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Collections/ICollection.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/ICollection.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics.Contracts; namespace System.Collections { @@ -29,7 +28,7 @@ namespace System.Collections // implementation of a collection, and use one of the internal objects // found in that code. // - // In the absense of a static Synchronized method on a collection, + // In the absence of a static Synchronized method on a collection, // the expected usage for SyncRoot would look like this: // // ICollection col = ... diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Collections/IDictionary.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/IDictionary.cs index 8bc7fcf125..b934c2399c 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Collections/IDictionary.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/IDictionary.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics.Contracts; namespace System.Collections { diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Collections/IEnumerable.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/IEnumerable.cs index 91aec62423..e5edeff266 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Collections/IEnumerable.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/IEnumerable.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics.Contracts; using System.Runtime.InteropServices; namespace System.Collections @@ -12,7 +11,6 @@ namespace System.Collections { // Returns an IEnumerator for this enumerable Object. The enumerator provides // a simple way to access all the contents of a collection. - [Pure] IEnumerator GetEnumerator(); } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Collections/IList.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/IList.cs index bb2e221cc1..0110eca1f5 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Collections/IList.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/IList.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics.Contracts; namespace System.Collections { diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Collections/ListDictionaryInternal.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/ListDictionaryInternal.cs new file mode 100644 index 0000000000..a8b7a187d9 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/ListDictionaryInternal.cs @@ -0,0 +1,513 @@ +// 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. +/*============================================================ +** +** +** +** +** +** Purpose: List for exceptions. +** +** +===========================================================*/ + + +namespace System.Collections +{ + /// This is a simple implementation of IDictionary using a singly linked list. This + /// will be smaller and faster than a Hashtable if the number of elements is 10 or less. + /// This should not be used if performance is important for large numbers of elements. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#if CORERT + public +#else + internal +#endif + class ListDictionaryInternal : IDictionary + { + private DictionaryNode head; // Do not rename (binary serialization) + private int version; // Do not rename (binary serialization) + private int count; // Do not rename (binary serialization) + [NonSerialized] + private Object _syncRoot; + + public ListDictionaryInternal() + { + } + + public Object this[Object key] + { + get + { + if (key == null) + { + throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); + } + DictionaryNode node = head; + + while (node != null) + { + if (node.key.Equals(key)) + { + return node.value; + } + node = node.next; + } + return null; + } + set + { + if (key == null) + { + throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); + } + + + version++; + DictionaryNode last = null; + DictionaryNode node; + for (node = head; node != null; node = node.next) + { + if (node.key.Equals(key)) + { + break; + } + last = node; + } + if (node != null) + { + // Found it + node.value = value; + return; + } + // Not found, so add a new one + DictionaryNode newNode = new DictionaryNode(); + newNode.key = key; + newNode.value = value; + if (last != null) + { + last.next = newNode; + } + else + { + head = newNode; + } + count++; + } + } + + public int Count + { + get + { + return count; + } + } + + public ICollection Keys + { + get + { + return new NodeKeyValueCollection(this, true); + } + } + + public bool IsReadOnly + { + get + { + return false; + } + } + + public bool IsFixedSize + { + get + { + return false; + } + } + + public bool IsSynchronized + { + get + { + return false; + } + } + + public Object SyncRoot + { + get + { + if (_syncRoot == null) + { + System.Threading.Interlocked.CompareExchange(ref _syncRoot, new Object(), null); + } + return _syncRoot; + } + } + + public ICollection Values + { + get + { + return new NodeKeyValueCollection(this, false); + } + } + + public void Add(Object key, Object value) + { + if (key == null) + { + throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); + } + + + version++; + DictionaryNode last = null; + DictionaryNode node; + for (node = head; node != null; node = node.next) + { + if (node.key.Equals(key)) + { + throw new ArgumentException(SR.Format(SR.Argument_AddingDuplicate__, node.key, key)); + } + last = node; + } + if (node != null) + { + // Found it + node.value = value; + return; + } + // Not found, so add a new one + DictionaryNode newNode = new DictionaryNode(); + newNode.key = key; + newNode.value = value; + if (last != null) + { + last.next = newNode; + } + else + { + head = newNode; + } + count++; + } + + public void Clear() + { + count = 0; + head = null; + version++; + } + + public bool Contains(Object key) + { + if (key == null) + { + throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); + } + for (DictionaryNode node = head; node != null; node = node.next) + { + if (node.key.Equals(key)) + { + return true; + } + } + return false; + } + + public void CopyTo(Array array, int index) + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + + if (array.Rank != 1) + throw new ArgumentException(SR.Arg_RankMultiDimNotSupported); + + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (array.Length - index < this.Count) + throw new ArgumentException(SR.ArgumentOutOfRange_Index, nameof(index)); + + for (DictionaryNode node = head; node != null; node = node.next) + { + array.SetValue(new DictionaryEntry(node.key, node.value), index); + index++; + } + } + + public IDictionaryEnumerator GetEnumerator() + { + return new NodeEnumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new NodeEnumerator(this); + } + + public void Remove(Object key) + { + if (key == null) + { + throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); + } + version++; + DictionaryNode last = null; + DictionaryNode node; + for (node = head; node != null; node = node.next) + { + if (node.key.Equals(key)) + { + break; + } + last = node; + } + if (node == null) + { + return; + } + if (node == head) + { + head = node.next; + } + else + { + last.next = node.next; + } + count--; + } + + private class NodeEnumerator : IDictionaryEnumerator + { + private ListDictionaryInternal list; + private DictionaryNode current; + private int version; + private bool start; + + + public NodeEnumerator(ListDictionaryInternal list) + { + this.list = list; + version = list.version; + start = true; + current = null; + } + + public Object Current + { + get + { + return Entry; + } + } + + public DictionaryEntry Entry + { + get + { + if (current == null) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + return new DictionaryEntry(current.key, current.value); + } + } + + public Object Key + { + get + { + if (current == null) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + return current.key; + } + } + + public Object Value + { + get + { + if (current == null) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + return current.value; + } + } + + public bool MoveNext() + { + if (version != list.version) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + } + if (start) + { + current = list.head; + start = false; + } + else + { + if (current != null) + { + current = current.next; + } + } + return (current != null); + } + + public void Reset() + { + if (version != list.version) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + } + start = true; + current = null; + } + } + + + private class NodeKeyValueCollection : ICollection + { + private ListDictionaryInternal list; + private bool isKeys; + + public NodeKeyValueCollection(ListDictionaryInternal list, bool isKeys) + { + this.list = list; + this.isKeys = isKeys; + } + + void ICollection.CopyTo(Array array, int index) + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + if (array.Rank != 1) + throw new ArgumentException(SR.Arg_RankMultiDimNotSupported); + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum); + if (array.Length - index < list.Count) + throw new ArgumentException(SR.ArgumentOutOfRange_Index, nameof(index)); + for (DictionaryNode node = list.head; node != null; node = node.next) + { + array.SetValue(isKeys ? node.key : node.value, index); + index++; + } + } + + int ICollection.Count + { + get + { + int count = 0; + for (DictionaryNode node = list.head; node != null; node = node.next) + { + count++; + } + return count; + } + } + + bool ICollection.IsSynchronized + { + get + { + return false; + } + } + + Object ICollection.SyncRoot + { + get + { + return list.SyncRoot; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new NodeKeyValueEnumerator(list, isKeys); + } + + + private class NodeKeyValueEnumerator : IEnumerator + { + private ListDictionaryInternal list; + private DictionaryNode current; + private int version; + private bool isKeys; + private bool start; + + public NodeKeyValueEnumerator(ListDictionaryInternal list, bool isKeys) + { + this.list = list; + this.isKeys = isKeys; + version = list.version; + start = true; + current = null; + } + + public Object Current + { + get + { + if (current == null) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + return isKeys ? current.key : current.value; + } + } + + public bool MoveNext() + { + if (version != list.version) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + } + if (start) + { + current = list.head; + start = false; + } + else + { + if (current != null) + { + current = current.next; + } + } + return (current != null); + } + + public void Reset() + { + if (version != list.version) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + } + start = true; + current = null; + } + } + } + + [Serializable] + private class DictionaryNode + { + public Object key; + public Object value; + public DictionaryNode next; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Collections/ObjectModel/Collection.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/ObjectModel/Collection.cs new file mode 100644 index 0000000000..1e1b2c7959 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/ObjectModel/Collection.cs @@ -0,0 +1,396 @@ +// 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.Collections.Generic; +using System.Diagnostics; + +namespace System.Collections.ObjectModel +{ + [Serializable] + [DebuggerTypeProxy(typeof(ICollectionDebugView<>))] + [DebuggerDisplay("Count = {Count}")] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class Collection : IList, IList, IReadOnlyList + { + private IList items; // Do not rename (binary serialization) + [NonSerialized] + private Object _syncRoot; + + public Collection() + { + items = new List(); + } + + public Collection(IList list) + { + if (list == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.list); + } + items = list; + } + + public int Count + { + get { return items.Count; } + } + + protected IList Items + { + get { return items; } + } + + public T this[int index] + { + get { return items[index]; } + set + { + if (items.IsReadOnly) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + if (index < 0 || index >= items.Count) + { + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + } + + SetItem(index, value); + } + } + + public void Add(T item) + { + if (items.IsReadOnly) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + int index = items.Count; + InsertItem(index, item); + } + + public void Clear() + { + if (items.IsReadOnly) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + ClearItems(); + } + + public void CopyTo(T[] array, int index) + { + items.CopyTo(array, index); + } + + public bool Contains(T item) + { + return items.Contains(item); + } + + public IEnumerator GetEnumerator() + { + return items.GetEnumerator(); + } + + public int IndexOf(T item) + { + return items.IndexOf(item); + } + + public void Insert(int index, T item) + { + if (items.IsReadOnly) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + if (index < 0 || index > items.Count) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_ListInsert); + } + + InsertItem(index, item); + } + + public bool Remove(T item) + { + if (items.IsReadOnly) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + int index = items.IndexOf(item); + if (index < 0) return false; + RemoveItem(index); + return true; + } + + public void RemoveAt(int index) + { + if (items.IsReadOnly) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + if (index < 0 || index >= items.Count) + { + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + } + + RemoveItem(index); + } + + protected virtual void ClearItems() + { + items.Clear(); + } + + protected virtual void InsertItem(int index, T item) + { + items.Insert(index, item); + } + + protected virtual void RemoveItem(int index) + { + items.RemoveAt(index); + } + + protected virtual void SetItem(int index, T item) + { + items[index] = item; + } + + bool ICollection.IsReadOnly + { + get + { + return items.IsReadOnly; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)items).GetEnumerator(); + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get + { + if (_syncRoot == null) + { + ICollection c = items as ICollection; + if (c != null) + { + _syncRoot = c.SyncRoot; + } + else + { + System.Threading.Interlocked.CompareExchange(ref _syncRoot, new Object(), null); + } + } + return _syncRoot; + } + } + + void ICollection.CopyTo(Array array, int index) + { + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + } + + if (array.Rank != 1) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); + } + + if (array.GetLowerBound(0) != 0) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound); + } + + if (index < 0) + { + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); + } + + if (array.Length - index < Count) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); + } + + T[] tArray = array as T[]; + if (tArray != null) + { + items.CopyTo(tArray, index); + } + else + { + // + // Catch the obvious case assignment will fail. + // We can't find all possible problems by doing the check though. + // For example, if the element type of the Array is derived from T, + // we can't figure out if we can successfully copy the element beforehand. + // + Type targetType = array.GetType().GetElementType(); + Type sourceType = typeof(T); + if (!(targetType.IsAssignableFrom(sourceType) || sourceType.IsAssignableFrom(targetType))) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + + // + // We can't cast array of value type to object[], so we don't support + // widening of primitive types here. + // + object[] objects = array as object[]; + if (objects == null) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + + int count = items.Count; + try + { + for (int i = 0; i < count; i++) + { + objects[index++] = items[i]; + } + } + catch (ArrayTypeMismatchException) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + } + } + + object IList.this[int index] + { + get { return items[index]; } + set + { + ThrowHelper.IfNullAndNullsAreIllegalThenThrow(value, ExceptionArgument.value); + + try + { + this[index] = (T)value; + } + catch (InvalidCastException) + { + ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T)); + } + } + } + + bool IList.IsReadOnly + { + get + { + return items.IsReadOnly; + } + } + + bool IList.IsFixedSize + { + get + { + // There is no IList.IsFixedSize, so we must assume that only + // readonly collections are fixed size, if our internal item + // collection does not implement IList. Note that Array implements + // IList, and therefore T[] and U[] will be fixed-size. + IList list = items as IList; + if (list != null) + { + return list.IsFixedSize; + } + return items.IsReadOnly; + } + } + + int IList.Add(object value) + { + if (items.IsReadOnly) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + ThrowHelper.IfNullAndNullsAreIllegalThenThrow(value, ExceptionArgument.value); + + try + { + Add((T)value); + } + catch (InvalidCastException) + { + ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T)); + } + + return this.Count - 1; + } + + bool IList.Contains(object value) + { + if (IsCompatibleObject(value)) + { + return Contains((T)value); + } + return false; + } + + int IList.IndexOf(object value) + { + if (IsCompatibleObject(value)) + { + return IndexOf((T)value); + } + return -1; + } + + void IList.Insert(int index, object value) + { + if (items.IsReadOnly) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + ThrowHelper.IfNullAndNullsAreIllegalThenThrow(value, ExceptionArgument.value); + + try + { + Insert(index, (T)value); + } + catch (InvalidCastException) + { + ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T)); + } + } + + void IList.Remove(object value) + { + if (items.IsReadOnly) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + if (IsCompatibleObject(value)) + { + Remove((T)value); + } + } + + private static bool IsCompatibleObject(object value) + { + // Non-null values are fine. Only accept nulls if T is a class or Nullable. + // Note that default(T) is not equal to null for value types except when T is Nullable. + return ((value is T) || (value == null && default(T) == null)); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Collections/ObjectModel/ReadOnlyCollection.cs b/external/corert/src/System.Private.CoreLib/shared/System/Collections/ObjectModel/ReadOnlyCollection.cs new file mode 100644 index 0000000000..dbf88d8b8d --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Collections/ObjectModel/ReadOnlyCollection.cs @@ -0,0 +1,279 @@ +// 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.Collections.Generic; +using System.Diagnostics; + +namespace System.Collections.ObjectModel +{ + [Serializable] + [DebuggerTypeProxy(typeof(ICollectionDebugView<>))] + [DebuggerDisplay("Count = {Count}")] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class ReadOnlyCollection : IList, IList, IReadOnlyList + { + private IList list; // Do not rename (binary serialization) + [NonSerialized] + private Object _syncRoot; + + public ReadOnlyCollection(IList list) + { + if (list == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.list); + } + this.list = list; + } + + public int Count + { + get { return list.Count; } + } + + public T this[int index] + { + get { return list[index]; } + } + + public bool Contains(T value) + { + return list.Contains(value); + } + + public void CopyTo(T[] array, int index) + { + list.CopyTo(array, index); + } + + public IEnumerator GetEnumerator() + { + return list.GetEnumerator(); + } + + public int IndexOf(T value) + { + return list.IndexOf(value); + } + + protected IList Items + { + get + { + return list; + } + } + + bool ICollection.IsReadOnly + { + get { return true; } + } + + T IList.this[int index] + { + get { return list[index]; } + set + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + } + + void ICollection.Add(T value) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + void ICollection.Clear() + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + void IList.Insert(int index, T value) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + bool ICollection.Remove(T value) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + return false; + } + + void IList.RemoveAt(int index) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)list).GetEnumerator(); + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get + { + if (_syncRoot == null) + { + ICollection c = list as ICollection; + if (c != null) + { + _syncRoot = c.SyncRoot; + } + else + { + System.Threading.Interlocked.CompareExchange(ref _syncRoot, new Object(), null); + } + } + return _syncRoot; + } + } + + void ICollection.CopyTo(Array array, int index) + { + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + } + + if (array.Rank != 1) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); + } + + if (array.GetLowerBound(0) != 0) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound); + } + + if (index < 0) + { + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); + } + + if (array.Length - index < Count) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); + } + + T[] items = array as T[]; + if (items != null) + { + list.CopyTo(items, index); + } + else + { + // + // Catch the obvious case assignment will fail. + // We can't find all possible problems by doing the check though. + // For example, if the element type of the Array is derived from T, + // we can't figure out if we can successfully copy the element beforehand. + // + Type targetType = array.GetType().GetElementType(); + Type sourceType = typeof(T); + if (!(targetType.IsAssignableFrom(sourceType) || sourceType.IsAssignableFrom(targetType))) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + + // + // We can't cast array of value type to object[], so we don't support + // widening of primitive types here. + // + object[] objects = array as object[]; + if (objects == null) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + + int count = list.Count; + try + { + for (int i = 0; i < count; i++) + { + objects[index++] = list[i]; + } + } + catch (ArrayTypeMismatchException) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + } + } + + bool IList.IsFixedSize + { + get { return true; } + } + + bool IList.IsReadOnly + { + get { return true; } + } + + object IList.this[int index] + { + get { return list[index]; } + set + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + } + + int IList.Add(object value) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + return -1; + } + + void IList.Clear() + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + private static bool IsCompatibleObject(object value) + { + // Non-null values are fine. Only accept nulls if T is a class or Nullable. + // Note that default(T) is not equal to null for value types except when T is Nullable. + return ((value is T) || (value == null && default(T) == null)); + } + + bool IList.Contains(object value) + { + if (IsCompatibleObject(value)) + { + return Contains((T)value); + } + return false; + } + + int IList.IndexOf(object value) + { + if (IsCompatibleObject(value)) + { + return IndexOf((T)value); + } + return -1; + } + + void IList.Insert(int index, object value) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + void IList.Remove(object value) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + void IList.RemoveAt(int index) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Convert.cs.REMOVED.git-id b/external/corert/src/System.Private.CoreLib/shared/System/Convert.cs.REMOVED.git-id index 9766f46299..72b7d96440 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Convert.cs.REMOVED.git-id +++ b/external/corert/src/System.Private.CoreLib/shared/System/Convert.cs.REMOVED.git-id @@ -1 +1 @@ -576f78f1f1eb529eb21e915df90ae1e14da28ed7 \ No newline at end of file +488ea773387e0c95bf1e04d9917242c88b9dcbbe \ No newline at end of file diff --git a/external/corert/src/System.Private.CoreLib/shared/System/CurrentSystemTimeZone.cs b/external/corert/src/System.Private.CoreLib/shared/System/CurrentSystemTimeZone.cs index 3f17d6f371..bab6a92bf5 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/CurrentSystemTimeZone.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/CurrentSystemTimeZone.cs @@ -18,7 +18,6 @@ ============================================================*/ using System; -using System.Diagnostics.Contracts; using System.Text; using System.Collections; using System.Globalization; diff --git a/external/corert/src/System.Private.CoreLib/shared/System/DBNull.cs b/external/corert/src/System.Private.CoreLib/shared/System/DBNull.cs index 4f4d64bf66..3cee2b15c8 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/DBNull.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/DBNull.cs @@ -6,17 +6,23 @@ using System.Runtime.Serialization; namespace System { + [Serializable] public sealed class DBNull : ISerializable, IConvertible { private DBNull() { } - + + private DBNull(SerializationInfo info, StreamingContext context) + { + throw new NotSupportedException(SR.NotSupported_DBNullSerial); + } + public static readonly DBNull Value = new DBNull(); public void GetObjectData(SerializationInfo info, StreamingContext context) { - throw new PlatformNotSupportedException(); + UnitySerializationHolder.GetUnitySerializationInfo(info, UnitySerializationHolder.NullUnity); } public override string ToString() diff --git a/external/corert/src/System.Private.CoreLib/shared/System/DataMisalignedException.cs b/external/corert/src/System.Private.CoreLib/shared/System/DataMisalignedException.cs index ff5b29f1cf..2a245b6ef7 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/DataMisalignedException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/DataMisalignedException.cs @@ -13,24 +13,30 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class DataMisalignedException : SystemException { public DataMisalignedException() : base(SR.Arg_DataMisalignedException) { - HResult = __HResults.COR_E_DATAMISALIGNED; + HResult = HResults.COR_E_DATAMISALIGNED; } public DataMisalignedException(String message) : base(message) { - HResult = __HResults.COR_E_DATAMISALIGNED; + HResult = HResults.COR_E_DATAMISALIGNED; } public DataMisalignedException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_DATAMISALIGNED; + HResult = HResults.COR_E_DATAMISALIGNED; + } + + internal DataMisalignedException(SerializationInfo info, StreamingContext context) : base(info, context) + { } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/DateTime.cs b/external/corert/src/System.Private.CoreLib/shared/System/DateTime.cs index ddb72da77d..b5deefa94a 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/DateTime.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/DateTime.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Threading; using System.Globalization; using System.Runtime; @@ -18,7 +17,6 @@ using Calendar = System.Globalization.Calendar; namespace System { - // This value type represents a date and time. Every DateTime // object has a private field (Ticks) of type Int64 that stores the // date and time as the number of 100 nanosecond intervals since @@ -54,7 +52,8 @@ namespace System // [StructLayout(LayoutKind.Auto)] [Serializable] - public partial struct DateTime : IComparable, IFormattable, IConvertible, IComparable, IEquatable, ISerializable + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public readonly partial struct DateTime : IComparable, IFormattable, IConvertible, IComparable, IEquatable, ISerializable, ISpanFormattable { // Number of 100ns ticks per time unit private const long TicksPerMillisecond = 10000; @@ -91,7 +90,7 @@ namespace System internal const long MaxTicks = DaysTo10000 * TicksPerDay - 1; private const long MaxMillis = (long)DaysTo10000 * MillisPerDay; - private const long TicksTo1970 = DaysTo1970 * TicksPerDay; + internal const long UnixEpochTicks = DaysTo1970 * TicksPerDay; private const long FileTimeOffset = DaysTo1601 * TicksPerDay; private const long DoubleDateOffset = DaysTo1899 * TicksPerDay; // The minimum OA date is 0100/01/01 (Note it's year 100). @@ -114,6 +113,7 @@ namespace System public static readonly DateTime MinValue = new DateTime(MinTicks, DateTimeKind.Unspecified); public static readonly DateTime MaxValue = new DateTime(MaxTicks, DateTimeKind.Unspecified); + public static readonly DateTime UnixEpoch = new DateTime(UnixEpochTicks, DateTimeKind.Utc); private const UInt64 TicksMask = 0x3FFFFFFFFFFFFFFF; private const UInt64 FlagsMask = 0xC000000000000000; @@ -125,10 +125,10 @@ namespace System private const UInt64 KindLocalAmbiguousDst = 0xC000000000000000; private const Int32 KindShift = 62; - private const String TicksField = "ticks"; - private const String DateDataField = "_dateData"; + private const String TicksField = "ticks"; // Do not rename (binary serialization) + private const String DateDataField = "dateData"; // Do not rename (binary serialization) - // The data is stored as an unsigned 64-bit integeter + // The data is stored as an unsigned 64-bit integer // Bits 01-62: The value of 100-nanosecond ticks where 0 represents 1/1/0001 12:00am, up until the value // 12/31/9999 23:59:59.9999999 // Bits 63-64: A four-state value that describes the DateTimeKind value of the date time, with a 2nd @@ -136,7 +136,7 @@ namespace System // savings time hour and it is in daylight savings time. This allows distinction of these // otherwise ambiguous local times and prevents data loss when round tripping from Local to // UTC time. - private UInt64 _dateData; + private readonly UInt64 _dateData; // Constructs a DateTime from a tick count. The ticks // argument specifies the date as the number of 100-nanosecond intervals @@ -146,7 +146,6 @@ namespace System { if (ticks < MinTicks || ticks > MaxTicks) throw new ArgumentOutOfRangeException(nameof(ticks), SR.ArgumentOutOfRange_DateTimeBadTicks); - Contract.EndContractBlock(); _dateData = (UInt64)ticks; } @@ -165,7 +164,6 @@ namespace System { throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind)); } - Contract.EndContractBlock(); _dateData = ((UInt64)ticks | ((UInt64)kind << KindShift)); } @@ -176,7 +174,6 @@ namespace System throw new ArgumentOutOfRangeException(nameof(ticks), SR.ArgumentOutOfRange_DateTimeBadTicks); } Debug.Assert(kind == DateTimeKind.Local, "Internal Constructor is for local times only"); - Contract.EndContractBlock(); _dateData = ((UInt64)ticks | (isAmbiguousDst ? KindLocalAmbiguousDst : KindLocal)); } @@ -211,7 +208,6 @@ namespace System { throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind)); } - Contract.EndContractBlock(); Int64 ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second); _dateData = ((UInt64)ticks | ((UInt64)kind << KindShift)); } @@ -223,7 +219,6 @@ namespace System { if (calendar == null) throw new ArgumentNullException(nameof(calendar)); - Contract.EndContractBlock(); _dateData = (UInt64)calendar.ToDateTime(year, month, day, hour, minute, second, 0).Ticks; } @@ -236,7 +231,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(millisecond), SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1)); } - Contract.EndContractBlock(); Int64 ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second); ticks += millisecond * TicksPerMillisecond; if (ticks < MinTicks || ticks > MaxTicks) @@ -254,7 +248,6 @@ namespace System { throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind)); } - Contract.EndContractBlock(); Int64 ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second); ticks += millisecond * TicksPerMillisecond; if (ticks < MinTicks || ticks > MaxTicks) @@ -273,7 +266,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(millisecond), SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1)); } - Contract.EndContractBlock(); Int64 ticks = calendar.ToDateTime(year, month, day, hour, minute, second, 0).Ticks; ticks += millisecond * TicksPerMillisecond; if (ticks < MinTicks || ticks > MaxTicks) @@ -293,7 +285,6 @@ namespace System { throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind)); } - Contract.EndContractBlock(); Int64 ticks = calendar.ToDateTime(year, month, day, hour, minute, second, 0).Ticks; ticks += millisecond * TicksPerMillisecond; if (ticks < MinTicks || ticks > MaxTicks) @@ -305,7 +296,6 @@ namespace System { if (info == null) throw new ArgumentNullException(nameof(info)); - Contract.EndContractBlock(); Boolean foundTicks = false; Boolean foundDateData = false; @@ -451,10 +441,7 @@ namespace System public DateTime AddMonths(int months) { if (months < -120000 || months > 120000) throw new ArgumentOutOfRangeException(nameof(months), SR.ArgumentOutOfRange_DateTimeBadMonths); - Contract.EndContractBlock(); - int y = GetDatePart(DatePartYear); - int m = GetDatePart(DatePartMonth); - int d = GetDatePart(DatePartDay); + GetDatePart(out int y, out int m, out int d); int i = m - 1 + months; if (i >= 0) { @@ -516,7 +503,6 @@ namespace System // parameter name out of the two for the exception. throw new ArgumentOutOfRangeException("years", SR.ArgumentOutOfRange_DateTimeBadYears); } - Contract.EndContractBlock(); return AddMonths(value * 12); } @@ -590,7 +576,6 @@ namespace System public static int DaysInMonth(int year, int month) { if (month < 1 || month > 12) throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month); - Contract.EndContractBlock(); // IsLeapYear checks the year argument int[] days = IsLeapYear(year) ? s_daysToMonth366 : s_daysToMonth365; return days[month] - days[month - 1]; @@ -726,7 +711,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(fileTime), SR.ArgumentOutOfRange_FileTimeInvalid); } - Contract.EndContractBlock(); // This is the ticks in Universal time for this fileTime. long universalTicks = fileTime + FileTimeOffset; @@ -746,7 +730,6 @@ namespace System { throw new ArgumentNullException(nameof(info)); } - Contract.EndContractBlock(); // Serialize both the old and the new format info.AddValue(TicksField, InternalTicks); @@ -858,6 +841,51 @@ namespace System return n - days[m - 1] + 1; } + // Exactly the same as GetDatePart(int part), except computing all of + // year/month/day rather than just one of them. Used when all three + // are needed rather than redoing the computations for each. + internal void GetDatePart(out int year, out int month, out int day) + { + Int64 ticks = InternalTicks; + // n = number of days since 1/1/0001 + int n = (int)(ticks / TicksPerDay); + // y400 = number of whole 400-year periods since 1/1/0001 + int y400 = n / DaysPer400Years; + // n = day number within 400-year period + n -= y400 * DaysPer400Years; + // y100 = number of whole 100-year periods within 400-year period + int y100 = n / DaysPer100Years; + // Last 100-year period has an extra day, so decrement result if 4 + if (y100 == 4) y100 = 3; + // n = day number within 100-year period + n -= y100 * DaysPer100Years; + // y4 = number of whole 4-year periods within 100-year period + int y4 = n / DaysPer4Years; + // n = day number within 4-year period + n -= y4 * DaysPer4Years; + // y1 = number of whole years within 4-year period + int y1 = n / DaysPerYear; + // Last year has an extra day, so decrement result if 4 + if (y1 == 4) y1 = 3; + // compute year + year = y400 * 400 + y100 * 100 + y4 * 4 + y1 + 1; + // n = day number within year + n -= y1 * DaysPerYear; + // dayOfYear = n + 1; + // Leap year calculation looks different from IsLeapYear since y1, y4, + // and y100 are relative to year 1, not year 0 + bool leapYear = y1 == 3 && (y4 != 24 || y100 == 3); + int[] days = leapYear ? s_daysToMonth366 : s_daysToMonth365; + // All months have less than 32 days, so n >> 5 is a good conservative + // estimate for the month + int m = (n >> 5) + 1; + // m = 1-based month number + while (n >= days[m]) m++; + // compute month and day + month = m; + day = n - days[m - 1] + 1; + } + // Returns the day-of-month part of this DateTime. The returned // value is an integer between 1 and 31. // @@ -865,8 +893,6 @@ namespace System { get { - Contract.Ensures(Contract.Result() >= 1); - Contract.Ensures(Contract.Result() <= 31); return GetDatePart(DatePartDay); } } @@ -880,8 +906,6 @@ namespace System { get { - Contract.Ensures(Contract.Result() >= DayOfWeek.Sunday); - Contract.Ensures(Contract.Result() <= DayOfWeek.Saturday); return (DayOfWeek)((InternalTicks / TicksPerDay + 1) % 7); } } @@ -893,8 +917,6 @@ namespace System { get { - Contract.Ensures(Contract.Result() >= 1); - Contract.Ensures(Contract.Result() <= 366); // leap year return GetDatePart(DatePartDayOfYear); } } @@ -914,8 +936,6 @@ namespace System { get { - Contract.Ensures(Contract.Result() >= 0); - Contract.Ensures(Contract.Result() < 24); return (int)((InternalTicks / TicksPerHour) % 24); } } @@ -925,7 +945,6 @@ namespace System return (InternalKind == KindLocalAmbiguousDst); } - [Pure] public DateTimeKind Kind { get @@ -949,8 +968,6 @@ namespace System { get { - Contract.Ensures(Contract.Result() >= 0); - Contract.Ensures(Contract.Result() < 1000); return (int)((InternalTicks / TicksPerMillisecond) % 1000); } } @@ -962,8 +979,6 @@ namespace System { get { - Contract.Ensures(Contract.Result() >= 0); - Contract.Ensures(Contract.Result() < 60); return (int)((InternalTicks / TicksPerMinute) % 60); } } @@ -975,23 +990,16 @@ namespace System { get { - Contract.Ensures(Contract.Result() >= 1); return GetDatePart(DatePartMonth); } } // Returns a DateTime representing the current date and time. The - // resolution of the returned value depends on the system timer. For - // Windows NT 3.5 and later the timer resolution is approximately 10ms, - // for Windows NT 3.1 it is approximately 16ms, and for Windows 95 and 98 - // it is approximately 55ms. - // + // resolution of the returned value depends on the system timer. public static DateTime Now { get { - Contract.Ensures(Contract.Result().Kind == DateTimeKind.Local); - DateTime utc = UtcNow; Boolean isAmbiguousLocalDst = false; Int64 offset = TimeZoneInfo.GetDateTimeNowUtcOffsetFromUtc(utc, out isAmbiguousLocalDst).Ticks; @@ -1015,8 +1023,6 @@ namespace System { get { - Contract.Ensures(Contract.Result() >= 0); - Contract.Ensures(Contract.Result() < 60); return (int)((InternalTicks / TicksPerSecond) % 60); } } @@ -1063,7 +1069,6 @@ namespace System { get { - Contract.Ensures(Contract.Result() >= 1 && Contract.Result() <= 9999); return GetDatePart(DatePartYear); } } @@ -1077,7 +1082,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(year), SR.ArgumentOutOfRange_Year); } - Contract.EndContractBlock(); return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); } @@ -1087,6 +1091,7 @@ namespace System // public static DateTime Parse(String s) { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); return (DateTimeParse.Parse(s, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None)); } @@ -1096,21 +1101,31 @@ namespace System // public static DateTime Parse(String s, IFormatProvider provider) { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); return (DateTimeParse.Parse(s, DateTimeFormatInfo.GetInstance(provider), DateTimeStyles.None)); } public static DateTime Parse(String s, IFormatProvider provider, DateTimeStyles styles) { DateTimeFormatInfo.ValidateStyles(styles, nameof(styles)); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); return (DateTimeParse.Parse(s, DateTimeFormatInfo.GetInstance(provider), styles)); } + public static DateTime Parse(ReadOnlySpan s, IFormatProvider provider = null, DateTimeStyles styles = DateTimeStyles.None) + { + DateTimeFormatInfo.ValidateStyles(styles, nameof(styles)); + return DateTimeParse.Parse(s, DateTimeFormatInfo.GetInstance(provider), styles); + } + // Constructs a DateTime from a string. The string must specify a // date and optionally a time in a culture-specific or universal format. // Leading and trailing whitespace characters are allowed. // public static DateTime ParseExact(String s, String format, IFormatProvider provider) { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format); return (DateTimeParse.ParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), DateTimeStyles.None)); } @@ -1121,10 +1136,32 @@ namespace System public static DateTime ParseExact(String s, String format, IFormatProvider provider, DateTimeStyles style) { DateTimeFormatInfo.ValidateStyles(style, nameof(style)); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format); return (DateTimeParse.ParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), style)); } + // TODO https://github.com/dotnet/corefx/issues/25337: Remove this overload once corefx is updated to target the new signatures + public static DateTime ParseExact(ReadOnlySpan s, string format, IFormatProvider provider, DateTimeStyles style) + { + if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format); + return ParseExact(s, (ReadOnlySpan)format, provider, style); + } + + public static DateTime ParseExact(ReadOnlySpan s, ReadOnlySpan format, IFormatProvider provider, DateTimeStyles style = DateTimeStyles.None) + { + DateTimeFormatInfo.ValidateStyles(style, nameof(style)); + return DateTimeParse.ParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), style); + } + public static DateTime ParseExact(String s, String[] formats, IFormatProvider provider, DateTimeStyles style) + { + DateTimeFormatInfo.ValidateStyles(style, nameof(style)); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return DateTimeParse.ParseExactMultiple(s, formats, DateTimeFormatInfo.GetInstance(provider), style); + } + + public static DateTime ParseExact(ReadOnlySpan s, string[] formats, IFormatProvider provider, DateTimeStyles style = DateTimeStyles.None) { DateTimeFormatInfo.ValidateStyles(style, nameof(style)); return DateTimeParse.ParseExactMultiple(s, formats, DateTimeFormatInfo.GetInstance(provider), style); @@ -1225,75 +1262,135 @@ namespace System public String ToLongDateString() { - Contract.Ensures(Contract.Result() != null); return DateTimeFormat.Format(this, "D", DateTimeFormatInfo.CurrentInfo); } public String ToLongTimeString() { - Contract.Ensures(Contract.Result() != null); return DateTimeFormat.Format(this, "T", DateTimeFormatInfo.CurrentInfo); } public String ToShortDateString() { - Contract.Ensures(Contract.Result() != null); return DateTimeFormat.Format(this, "d", DateTimeFormatInfo.CurrentInfo); } public String ToShortTimeString() { - Contract.Ensures(Contract.Result() != null); return DateTimeFormat.Format(this, "t", DateTimeFormatInfo.CurrentInfo); } public override String ToString() { - Contract.Ensures(Contract.Result() != null); return DateTimeFormat.Format(this, null, DateTimeFormatInfo.CurrentInfo); } public String ToString(String format) { - Contract.Ensures(Contract.Result() != null); return DateTimeFormat.Format(this, format, DateTimeFormatInfo.CurrentInfo); } public String ToString(IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); return DateTimeFormat.Format(this, null, DateTimeFormatInfo.GetInstance(provider)); } public String ToString(String format, IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); return DateTimeFormat.Format(this, format, DateTimeFormatInfo.GetInstance(provider)); } + // TODO https://github.com/dotnet/corefx/issues/25337: Remove this overload once corefx is updated to target the new signatures + public bool TryFormat(Span destination, out int charsWritten, string format, IFormatProvider provider) => + TryFormat(destination, out charsWritten, (ReadOnlySpan)format, provider); + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) => + DateTimeFormat.TryFormat(this, destination, out charsWritten, format, DateTimeFormatInfo.GetInstance(provider)); + public DateTime ToUniversalTime() { return TimeZoneInfo.ConvertTimeToUtc(this, TimeZoneInfoOptions.NoThrowOnInvalidTime); } public static Boolean TryParse(String s, out DateTime result) + { + if (s == null) + { + result = default; + return false; + } + return DateTimeParse.TryParse(s, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None, out result); + } + + public static bool TryParse(ReadOnlySpan s, out DateTime result) { return DateTimeParse.TryParse(s, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None, out result); } public static Boolean TryParse(String s, IFormatProvider provider, DateTimeStyles styles, out DateTime result) + { + DateTimeFormatInfo.ValidateStyles(styles, nameof(styles)); + + if (s == null) + { + result = default; + return false; + } + + return DateTimeParse.TryParse(s, DateTimeFormatInfo.GetInstance(provider), styles, out result); + } + + public static bool TryParse(ReadOnlySpan s, IFormatProvider provider, DateTimeStyles styles, out DateTime result) { DateTimeFormatInfo.ValidateStyles(styles, nameof(styles)); return DateTimeParse.TryParse(s, DateTimeFormatInfo.GetInstance(provider), styles, out result); } public static Boolean TryParseExact(String s, String format, IFormatProvider provider, DateTimeStyles style, out DateTime result) + { + DateTimeFormatInfo.ValidateStyles(style, nameof(style)); + + if (s == null || format == null) + { + result = default; + return false; + } + + return DateTimeParse.TryParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), style, out result); + } + + // TODO https://github.com/dotnet/corefx/issues/25337: Remove this overload once corefx is updated to target the new signatures + public static bool TryParseExact(ReadOnlySpan s, string format, IFormatProvider provider, DateTimeStyles style, out DateTime result) + { + if (format == null) + { + result = default; + return false; + } + + return TryParseExact(s, (ReadOnlySpan)format, provider, style, out result); + } + + public static bool TryParseExact(ReadOnlySpan s, ReadOnlySpan format, IFormatProvider provider, DateTimeStyles style, out DateTime result) { DateTimeFormatInfo.ValidateStyles(style, nameof(style)); return DateTimeParse.TryParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), style, out result); } public static Boolean TryParseExact(String s, String[] formats, IFormatProvider provider, DateTimeStyles style, out DateTime result) + { + DateTimeFormatInfo.ValidateStyles(style, nameof(style)); + + if (s == null) + { + result = default; + return false; + } + + return DateTimeParse.TryParseExactMultiple(s, formats, DateTimeFormatInfo.GetInstance(provider), style, out result); + } + + public static bool TryParseExact(ReadOnlySpan s, string[] formats, IFormatProvider provider, DateTimeStyles style, out DateTime result) { DateTimeFormatInfo.ValidateStyles(style, nameof(style)); return DateTimeParse.TryParseExactMultiple(s, formats, DateTimeFormatInfo.GetInstance(provider), style, out result); @@ -1362,7 +1459,6 @@ namespace System // time strings for the current instance of DateTime. public String[] GetDateTimeFormats() { - Contract.Ensures(Contract.Result() != null); return (GetDateTimeFormats(CultureInfo.CurrentCulture)); } @@ -1371,7 +1467,6 @@ namespace System // time strings for the current instance of DateTime. public String[] GetDateTimeFormats(IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); return (DateTimeFormat.GetAllDateTimes(this, DateTimeFormatInfo.GetInstance(provider))); } @@ -1381,7 +1476,6 @@ namespace System // time strings for the current instance of DateTime. public String[] GetDateTimeFormats(char format) { - Contract.Ensures(Contract.Result() != null); return (GetDateTimeFormats(format, CultureInfo.CurrentCulture)); } @@ -1390,7 +1484,6 @@ namespace System // time strings for the current instance of DateTime. public String[] GetDateTimeFormats(char format, IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); return (DateTimeFormat.GetAllDateTimes(this, format, DateTimeFormatInfo.GetInstance(provider))); } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/DateTimeOffset.cs b/external/corert/src/System.Private.CoreLib/shared/System/DateTimeOffset.cs index d5ccbd9195..bb2196348c 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/DateTimeOffset.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/DateTimeOffset.cs @@ -30,15 +30,15 @@ namespace System [StructLayout(LayoutKind.Auto)] [Serializable] - public struct DateTimeOffset : IComparable, IFormattable, IComparable, IEquatable, ISerializable, IDeserializationCallback + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct DateTimeOffset : IComparable, IFormattable, IComparable, IEquatable, ISerializable, IDeserializationCallback, ISpanFormattable { // Constants internal const Int64 MaxOffset = TimeSpan.TicksPerHour * 14; internal const Int64 MinOffset = -MaxOffset; - private const long UnixEpochTicks = TimeSpan.TicksPerDay * DateTime.DaysTo1970; // 621,355,968,000,000,000 - private const long UnixEpochSeconds = UnixEpochTicks / TimeSpan.TicksPerSecond; // 62,135,596,800 - private const long UnixEpochMilliseconds = UnixEpochTicks / TimeSpan.TicksPerMillisecond; // 62,135,596,800,000 + private const long UnixEpochSeconds = DateTime.UnixEpochTicks / TimeSpan.TicksPerSecond; // 62,135,596,800 + private const long UnixEpochMilliseconds = DateTime.UnixEpochTicks / TimeSpan.TicksPerMillisecond; // 62,135,596,800,000 internal const long UnixMinSeconds = DateTime.MinTicks / TimeSpan.TicksPerSecond - UnixEpochSeconds; internal const long UnixMaxSeconds = DateTime.MaxTicks / TimeSpan.TicksPerSecond - UnixEpochSeconds; @@ -46,6 +46,7 @@ namespace System // Static Fields public static readonly DateTimeOffset MinValue = new DateTimeOffset(DateTime.MinTicks, TimeSpan.Zero); public static readonly DateTimeOffset MaxValue = new DateTimeOffset(DateTime.MaxTicks, TimeSpan.Zero); + public static readonly DateTimeOffset UnixEpoch = new DateTimeOffset(DateTime.UnixEpochTicks, TimeSpan.Zero); // Instance Fields private DateTime _dateTime; @@ -128,11 +129,7 @@ namespace System } // Returns a DateTimeOffset representing the current date and time. The - // resolution of the returned value depends on the system timer. For - // Windows NT 3.5 and later the timer resolution is approximately 10ms, - // for Windows NT 3.1 it is approximately 16ms, and for Windows 95 and 98 - // it is approximately 55ms. - // + // resolution of the returned value depends on the system timer. public static DateTimeOffset Now { get @@ -531,7 +528,7 @@ namespace System SR.Format(SR.ArgumentOutOfRange_Range, UnixMinSeconds, UnixMaxSeconds)); } - long ticks = seconds * TimeSpan.TicksPerSecond + UnixEpochTicks; + long ticks = seconds * TimeSpan.TicksPerSecond + DateTime.UnixEpochTicks; return new DateTimeOffset(ticks, TimeSpan.Zero); } @@ -546,7 +543,7 @@ namespace System SR.Format(SR.ArgumentOutOfRange_Range, MinMilliseconds, MaxMilliseconds)); } - long ticks = milliseconds * TimeSpan.TicksPerMillisecond + UnixEpochTicks; + long ticks = milliseconds * TimeSpan.TicksPerMillisecond + DateTime.UnixEpochTicks; return new DateTimeOffset(ticks, TimeSpan.Zero); } @@ -573,8 +570,8 @@ namespace System throw new ArgumentNullException(nameof(info)); } - info.AddValue("DateTime", _dateTime); - info.AddValue("OffsetMinutes", _offsetMinutes); + info.AddValue("DateTime", _dateTime); // Do not rename (binary serialization) + info.AddValue("OffsetMinutes", _offsetMinutes); // Do not rename (binary serialization) } @@ -585,8 +582,8 @@ namespace System throw new ArgumentNullException(nameof(info)); } - _dateTime = (DateTime)info.GetValue("DateTime", typeof(DateTime)); - _offsetMinutes = (Int16)info.GetValue("OffsetMinutes", typeof(Int16)); + _dateTime = (DateTime)info.GetValue("DateTime", typeof(DateTime)); // Do not rename (binary serialization) + _offsetMinutes = (Int16)info.GetValue("OffsetMinutes", typeof(Int16)); // Do not rename (binary serialization) } // Returns the hash code for this DateTimeOffset. @@ -602,6 +599,8 @@ namespace System // public static DateTimeOffset Parse(String input) { + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + TimeSpan offset; DateTime dateResult = DateTimeParse.Parse(input, DateTimeFormatInfo.CurrentInfo, @@ -616,12 +615,15 @@ namespace System // public static DateTimeOffset Parse(String input, IFormatProvider formatProvider) { + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); return Parse(input, formatProvider, DateTimeStyles.None); } public static DateTimeOffset Parse(String input, IFormatProvider formatProvider, DateTimeStyles styles) { styles = ValidateStyles(styles, nameof(styles)); + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + TimeSpan offset; DateTime dateResult = DateTimeParse.Parse(input, DateTimeFormatInfo.GetInstance(formatProvider), @@ -630,12 +632,21 @@ namespace System return new DateTimeOffset(dateResult.Ticks, offset); } + public static DateTimeOffset Parse(ReadOnlySpan input, IFormatProvider formatProvider = null, DateTimeStyles styles = DateTimeStyles.None) + { + styles = ValidateStyles(styles, nameof(styles)); + DateTime dateResult = DateTimeParse.Parse(input, DateTimeFormatInfo.GetInstance(formatProvider), styles, out TimeSpan offset); + return new DateTimeOffset(dateResult.Ticks, offset); + } + // Constructs a DateTimeOffset from a string. The string must specify a // date and optionally a time in a culture-specific or universal format. // Leading and trailing whitespace characters are allowed. // public static DateTimeOffset ParseExact(String input, String format, IFormatProvider formatProvider) { + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format); return ParseExact(input, format, formatProvider, DateTimeStyles.None); } @@ -646,6 +657,9 @@ namespace System public static DateTimeOffset ParseExact(String input, String format, IFormatProvider formatProvider, DateTimeStyles styles) { styles = ValidateStyles(styles, nameof(styles)); + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format); + TimeSpan offset; DateTime dateResult = DateTimeParse.ParseExact(input, format, @@ -655,9 +669,25 @@ namespace System return new DateTimeOffset(dateResult.Ticks, offset); } + // TODO https://github.com/dotnet/corefx/issues/25337: Remove this overload once corefx is updated to target the new signatures + public static DateTimeOffset ParseExact(ReadOnlySpan input, string format, IFormatProvider formatProvider, DateTimeStyles styles) + { + if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format); + return ParseExact(input, (ReadOnlySpan)format, formatProvider, styles); + } + + public static DateTimeOffset ParseExact(ReadOnlySpan input, ReadOnlySpan format, IFormatProvider formatProvider, DateTimeStyles styles = DateTimeStyles.None) + { + styles = ValidateStyles(styles, nameof(styles)); + DateTime dateResult = DateTimeParse.ParseExact(input, format, DateTimeFormatInfo.GetInstance(formatProvider), styles, out TimeSpan offset); + return new DateTimeOffset(dateResult.Ticks, offset); + } + public static DateTimeOffset ParseExact(String input, String[] formats, IFormatProvider formatProvider, DateTimeStyles styles) { styles = ValidateStyles(styles, nameof(styles)); + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + TimeSpan offset; DateTime dateResult = DateTimeParse.ParseExactMultiple(input, formats, @@ -667,6 +697,13 @@ namespace System return new DateTimeOffset(dateResult.Ticks, offset); } + public static DateTimeOffset ParseExact(ReadOnlySpan input, string[] formats, IFormatProvider formatProvider, DateTimeStyles styles = DateTimeStyles.None) + { + styles = ValidateStyles(styles, nameof(styles)); + DateTime dateResult = DateTimeParse.ParseExactMultiple(input, formats, DateTimeFormatInfo.GetInstance(formatProvider), styles, out TimeSpan offset); + return new DateTimeOffset(dateResult.Ticks, offset); + } + public TimeSpan Subtract(DateTimeOffset value) { return UtcDateTime.Subtract(value.UtcDateTime); @@ -690,7 +727,7 @@ namespace System // // For example, consider the DateTimeOffset 12/31/1969 12:59:59.001 +0 // ticks = 621355967990010000 - // ticksFromEpoch = ticks - UnixEpochTicks = -9990000 + // ticksFromEpoch = ticks - DateTime.UnixEpochTicks = -9990000 // secondsFromEpoch = ticksFromEpoch / TimeSpan.TicksPerSecond = 0 // // Notice that secondsFromEpoch is rounded *up* by the truncation induced by integer division, @@ -743,6 +780,13 @@ namespace System return DateTimeFormat.Format(ClockDateTime, format, DateTimeFormatInfo.GetInstance(formatProvider), Offset); } + // TODO https://github.com/dotnet/corefx/issues/25337: Remove this overload once corefx is updated to target the new signatures + public bool TryFormat(Span destination, out int charsWritten, string format, IFormatProvider provider) => + TryFormat(destination, out charsWritten, (ReadOnlySpan)format, provider); + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider formatProvider = null) => + DateTimeFormat.TryFormat(ClockDateTime, destination, out charsWritten, format, DateTimeFormatInfo.GetInstance(formatProvider), Offset); + public DateTimeOffset ToUniversalTime() { return new DateTimeOffset(UtcDateTime); @@ -761,9 +805,22 @@ namespace System return parsed; } + public static bool TryParse(ReadOnlySpan input, out DateTimeOffset result) + { + bool parsed = DateTimeParse.TryParse(input, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None, out DateTime dateResult, out TimeSpan offset); + result = new DateTimeOffset(dateResult.Ticks, offset); + return parsed; + } + public static Boolean TryParse(String input, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result) { styles = ValidateStyles(styles, nameof(styles)); + if (input == null) + { + result = default(DateTimeOffset); + return false; + } + TimeSpan offset; DateTime dateResult; Boolean parsed = DateTimeParse.TryParse(input, @@ -775,10 +832,24 @@ namespace System return parsed; } + public static bool TryParse(ReadOnlySpan input, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result) + { + styles = ValidateStyles(styles, nameof(styles)); + bool parsed = DateTimeParse.TryParse(input, DateTimeFormatInfo.GetInstance(formatProvider), styles, out DateTime dateResult, out TimeSpan offset); + result = new DateTimeOffset(dateResult.Ticks, offset); + return parsed; + } + public static Boolean TryParseExact(String input, String format, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result) { styles = ValidateStyles(styles, nameof(styles)); + if (input == null || format == null) + { + result = default(DateTimeOffset); + return false; + } + TimeSpan offset; DateTime dateResult; Boolean parsed = DateTimeParse.TryParseExact(input, @@ -791,10 +862,37 @@ namespace System return parsed; } + // TODO https://github.com/dotnet/corefx/issues/25337: Remove this overload once corefx is updated to target the new signatures + public static bool TryParseExact(ReadOnlySpan input, string format, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result) + { + if (format == null) + { + result = default; + return false; + } + + return TryParseExact(input, (ReadOnlySpan)format, formatProvider, styles, out result); + } + + public static bool TryParseExact( + ReadOnlySpan input, ReadOnlySpan format, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result) + { + styles = ValidateStyles(styles, nameof(styles)); + bool parsed = DateTimeParse.TryParseExact(input, format, DateTimeFormatInfo.GetInstance(formatProvider), styles, out DateTime dateResult, out TimeSpan offset); + result = new DateTimeOffset(dateResult.Ticks, offset); + return parsed; + } + public static Boolean TryParseExact(String input, String[] formats, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result) { styles = ValidateStyles(styles, nameof(styles)); + if (input == null) + { + result = default(DateTimeOffset); + return false; + } + TimeSpan offset; DateTime dateResult; Boolean parsed = DateTimeParse.TryParseExactMultiple(input, @@ -807,6 +905,15 @@ namespace System return parsed; } + public static bool TryParseExact( + ReadOnlySpan input, string[] formats, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result) + { + styles = ValidateStyles(styles, nameof(styles)); + bool parsed = DateTimeParse.TryParseExactMultiple(input, formats, DateTimeFormatInfo.GetInstance(formatProvider), styles, out DateTime dateResult, out TimeSpan offset); + result = new DateTimeOffset(dateResult.Ticks, offset); + return parsed; + } + // Ensures the TimeSpan is valid to go in a DateTimeOffset. private static Int16 ValidateOffset(TimeSpan offset) { diff --git a/external/corert/src/System.Private.CoreLib/shared/System/DefaultBinder.cs b/external/corert/src/System.Private.CoreLib/shared/System/DefaultBinder.cs index 9adf702a02..f8d8c0c168 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/DefaultBinder.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/DefaultBinder.cs @@ -8,10 +8,10 @@ using CultureInfo = System.Globalization.CultureInfo; namespace System { -#if CORECLR - internal -#else +#if CORERT public sealed +#else + internal #endif partial class DefaultBinder : Binder { @@ -42,7 +42,7 @@ namespace System state = null; -#region Map named parameters to candidate parameter postions +#region Map named parameters to candidate parameter positions // We are creating an paramOrder array to act as a mapping // between the order of the args and the actual order of the // parameters in the method. This order may differ because @@ -530,7 +530,7 @@ namespace System for (i = 0; i < types.Length; i++) { realTypes[i] = types[i].UnderlyingSystemType; - if (!(realTypes[i].IsRuntimeImplemented())) + if (!(realTypes[i].IsRuntimeImplemented() || realTypes[i] is SignatureType)) throw new ArgumentException(SR.Arg_MustBeType, nameof(types)); } types = realTypes; @@ -552,19 +552,30 @@ namespace System for (j = 0; j < types.Length; j++) { Type pCls = par[j].ParameterType; - if (pCls == types[j]) + if (types[j].MatchesParameterTypeExactly(par[j])) continue; if (pCls == typeof(object)) continue; + + Type type = types[j]; + if (type is SignatureType signatureType) + { + if (!(candidates[i] is MethodInfo methodInfo)) + break; + type = signatureType.TryResolveAgainstGenericMethod(methodInfo); + if (type == null) + break; + } + if (pCls.IsPrimitive) { - if (!(types[j].UnderlyingSystemType.IsRuntimeImplemented()) || - !CanChangePrimitive(types[j].UnderlyingSystemType, pCls.UnderlyingSystemType)) + if (!(type.UnderlyingSystemType.IsRuntimeImplemented()) || + !CanChangePrimitive(type.UnderlyingSystemType, pCls.UnderlyingSystemType)) break; } else { - if (!pCls.IsAssignableFrom(types[j])) + if (!pCls.IsAssignableFrom(type)) break; } } @@ -918,11 +929,22 @@ namespace System if (c1 == c2) return 0; - if (c1 == t) - return 1; + if (t is SignatureType signatureType) + { + if (signatureType.MatchesExactly(c1)) + return 1; - if (c2 == t) - return 2; + if (signatureType.MatchesExactly(c2)) + return 2; + } + else + { + if (c1 == t) + return 1; + + if (c2 == t) + return 2; + } bool c1FromC2; bool c2FromC1; diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggableAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggableAttribute.cs new file mode 100644 index 0000000000..d05f8471b3 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggableAttribute.cs @@ -0,0 +1,54 @@ +// 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. + +namespace System.Diagnostics +{ + // Attribute class used by the compiler to mark modules. + // If present, then debugging information for everything in the + // assembly was generated by the compiler, and will be preserved + // by the Runtime so that the debugger can provide full functionality + // in the case of JIT attach. If not present, then the compiler may + // or may not have included debugging information, and the Runtime + // won't preserve the debugging info, which will make debugging after + // a JIT attach difficult. + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module, AllowMultiple = false)] + public sealed class DebuggableAttribute : Attribute + { + [Flags] + public enum DebuggingModes + { + None = 0x0, + Default = 0x1, + DisableOptimizations = 0x100, + IgnoreSymbolStoreSequencePoints = 0x2, + EnableEditAndContinue = 0x4 + } + + public DebuggableAttribute(bool isJITTrackingEnabled, bool isJITOptimizerDisabled) + { + DebuggingFlags = 0; + + if (isJITTrackingEnabled) + { + DebuggingFlags |= DebuggingModes.Default; + } + + if (isJITOptimizerDisabled) + { + DebuggingFlags |= DebuggingModes.DisableOptimizations; + } + } + + public DebuggableAttribute(DebuggingModes modes) + { + DebuggingFlags = modes; + } + + public bool IsJITTrackingEnabled => (DebuggingFlags & DebuggingModes.Default) != 0; + + public bool IsJITOptimizerDisabled => (DebuggingFlags & DebuggingModes.DisableOptimizations) != 0; + + public DebuggingModes DebuggingFlags { get; } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerBrowsableAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerBrowsableAttribute.cs new file mode 100644 index 0000000000..b9d62225a9 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerBrowsableAttribute.cs @@ -0,0 +1,41 @@ +// 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. + +namespace System.Diagnostics +{ + // DebuggerBrowsableState states are defined as follows: + // Never never show this element + // Expanded expansion of the class is done, so that all visible internal members are shown + // Collapsed expansion of the class is not performed. Internal visible members are hidden + // RootHidden The target element itself should not be shown, but should instead be + // automatically expanded to have its members displayed. + // Default value is collapsed + + // Please also change the code which validates DebuggerBrowsableState variable (in this file) + // if you change this enum. + public enum DebuggerBrowsableState + { + Never = 0, + //Expanded is not supported in this release + //Expanded = 1, + Collapsed = 2, + RootHidden = 3 + } + + + // the one currently supported with the csee.dat + // (mcee.dat, autoexp.dat) file. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)] + public sealed class DebuggerBrowsableAttribute : Attribute + { + public DebuggerBrowsableAttribute(DebuggerBrowsableState state) + { + if (state < DebuggerBrowsableState.Never || state > DebuggerBrowsableState.RootHidden) + throw new ArgumentOutOfRangeException(nameof(state)); + + State = state; + } + public DebuggerBrowsableState State { get; } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerDisplayAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerDisplayAttribute.cs new file mode 100644 index 0000000000..7aae4b9397 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerDisplayAttribute.cs @@ -0,0 +1,51 @@ +// 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. + +namespace System.Diagnostics +{ + // This attribute is used to control what is displayed for the given class or field + // in the data windows in the debugger. The single argument to this attribute is + // the string that will be displayed in the value column for instances of the type. + // This string can include text between { and } which can be either a field, + // property or method (as will be documented in mscorlib). In the C# case, + // a general expression will be allowed which only has implicit access to the this pointer + // for the current instance of the target type. The expression will be limited, + // however: there is no access to aliases, locals, or pointers. + // In addition, attributes on properties referenced in the expression are not processed. + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class DebuggerDisplayAttribute : Attribute + { + private Type _target; + + public DebuggerDisplayAttribute(string value) + { + Value = value ?? ""; + Name = ""; + Type = ""; + } + + public string Value { get; } + + public string Name { get; set; } + + public string Type { get; set; } + + public Type Target + { + get => _target; + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + TargetTypeName = value.AssemblyQualifiedName; + _target = value; + } + } + + public string TargetTypeName { get; set; } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerHiddenAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerHiddenAttribute.cs new file mode 100644 index 0000000000..ace452e911 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerHiddenAttribute.cs @@ -0,0 +1,12 @@ +// 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. + +namespace System.Diagnostics +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Constructor, Inherited = false)] + public sealed class DebuggerHiddenAttribute : Attribute + { + public DebuggerHiddenAttribute() { } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerNonUserCodeAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerNonUserCodeAttribute.cs new file mode 100644 index 0000000000..1b61cb7262 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerNonUserCodeAttribute.cs @@ -0,0 +1,12 @@ +// 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. + +namespace System.Diagnostics +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Constructor | AttributeTargets.Struct, Inherited = false)] + public sealed class DebuggerNonUserCodeAttribute : Attribute + { + public DebuggerNonUserCodeAttribute() { } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerStepThroughAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerStepThroughAttribute.cs new file mode 100644 index 0000000000..82a164771e --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerStepThroughAttribute.cs @@ -0,0 +1,16 @@ +// 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. + +namespace System.Diagnostics +{ +#if PROJECTN + // Used by the IL2IL toolchain to mark generated code to control debugger stepping policy + [System.Runtime.CompilerServices.DependencyReductionRoot] +#endif + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false)] + public sealed class DebuggerStepThroughAttribute : Attribute + { + public DebuggerStepThroughAttribute() { } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerStepperBoundaryAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerStepperBoundaryAttribute.cs new file mode 100644 index 0000000000..647f2fdb00 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerStepperBoundaryAttribute.cs @@ -0,0 +1,13 @@ +// 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. + +namespace System.Diagnostics +{ + /// Indicates the code following the attribute is to be executed in run, not step, mode. + [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method, Inherited = false)] + public sealed class DebuggerStepperBoundaryAttribute : Attribute + { + public DebuggerStepperBoundaryAttribute() { } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerTypeProxyAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerTypeProxyAttribute.cs new file mode 100644 index 0000000000..445834e056 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerTypeProxyAttribute.cs @@ -0,0 +1,46 @@ +// 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. + +namespace System.Diagnostics +{ + [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class DebuggerTypeProxyAttribute : Attribute + { + private Type _target; + + public DebuggerTypeProxyAttribute(Type type) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + ProxyTypeName = type.AssemblyQualifiedName; + } + + public DebuggerTypeProxyAttribute(string typeName) + { + ProxyTypeName = typeName; + } + + public string ProxyTypeName { get; } + + public Type Target + { + get => _target; + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + TargetTypeName = value.AssemblyQualifiedName; + _target = value; + } + } + + public string TargetTypeName { get; set; } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerVisualizerAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerVisualizerAttribute.cs new file mode 100644 index 0000000000..032eca8a15 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerVisualizerAttribute.cs @@ -0,0 +1,97 @@ +// 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. + +namespace System.Diagnostics +{ + /// + /// Signifies that the attributed type has a visualizer which is pointed + /// to by the parameter type name strings. + /// + [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class DebuggerVisualizerAttribute : Attribute + { + private Type _target; + + public DebuggerVisualizerAttribute(string visualizerTypeName) + { + VisualizerTypeName = visualizerTypeName; + } + + public DebuggerVisualizerAttribute(string visualizerTypeName, string visualizerObjectSourceTypeName) + { + VisualizerTypeName = visualizerTypeName; + VisualizerObjectSourceTypeName = visualizerObjectSourceTypeName; + } + + public DebuggerVisualizerAttribute(string visualizerTypeName, Type visualizerObjectSource) + { + if (visualizerObjectSource == null) + { + throw new ArgumentNullException(nameof(visualizerObjectSource)); + } + + VisualizerTypeName = visualizerTypeName; + VisualizerObjectSourceTypeName = visualizerObjectSource.AssemblyQualifiedName; + } + + public DebuggerVisualizerAttribute(Type visualizer) + { + if (visualizer == null) + { + throw new ArgumentNullException(nameof(visualizer)); + } + + VisualizerTypeName = visualizer.AssemblyQualifiedName; + } + + public DebuggerVisualizerAttribute(Type visualizer, Type visualizerObjectSource) + { + if (visualizer == null) + { + throw new ArgumentNullException(nameof(visualizer)); + } + if (visualizerObjectSource == null) + { + throw new ArgumentNullException(nameof(visualizerObjectSource)); + } + + VisualizerTypeName = visualizer.AssemblyQualifiedName; + VisualizerObjectSourceTypeName = visualizerObjectSource.AssemblyQualifiedName; + } + + public DebuggerVisualizerAttribute(Type visualizer, string visualizerObjectSourceTypeName) + { + if (visualizer == null) + { + throw new ArgumentNullException(nameof(visualizer)); + } + + VisualizerTypeName = visualizer.AssemblyQualifiedName; + VisualizerObjectSourceTypeName = visualizerObjectSourceTypeName; + } + + public string VisualizerObjectSourceTypeName { get; } + + public string VisualizerTypeName { get; } + + public string Description { get; set; } + + public Type Target + { + get => _target; + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + TargetTypeName = value.AssemblyQualifiedName; + _target = value; + } + } + + public string TargetTypeName { get; set; } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/StackTraceHiddenAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/StackTraceHiddenAttribute.cs new file mode 100644 index 0000000000..474274ac08 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/StackTraceHiddenAttribute.cs @@ -0,0 +1,12 @@ +// 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. + +namespace System.Diagnostics +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Struct, Inherited = false)] + internal sealed class StackTraceHiddenAttribute : Attribute + { + public StackTraceHiddenAttribute() { } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/ActivityTracker.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/ActivityTracker.cs index e32abd2895..9ac32c3bd6 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/ActivityTracker.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/ActivityTracker.cs @@ -510,7 +510,8 @@ namespace System.Diagnostics.Tracing // Compute the checksum uint* sumPtr = (uint*)outPtr; // We set the last DWORD the sum of the first 3 DWORDS in the GUID. This - sumPtr[3] = sumPtr[0] + sumPtr[1] + sumPtr[2] + 0x599D99AD; // This last number is a random number (it identifies us as us) + // This last number is a random number (it identifies us as us) the process ID to make it unique per process. + sumPtr[3] = (sumPtr[0] + sumPtr[1] + sumPtr[2] + 0x599D99AD) ^ EventSource.s_currentPid; return (int)(ptr - ((byte*)outPtr)); } @@ -524,7 +525,7 @@ namespace System.Diagnostics.Tracing /// private static unsafe void WriteNibble(ref byte* ptr, byte* endPtr, uint value) { - Debug.Assert(0 <= value && value < 16); + Debug.Assert(value < 16); Debug.Assert(ptr < endPtr); if (*ptr != 0) diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventDescriptor.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventDescriptor.cs index 8fb471a99f..b036b28b4b 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventDescriptor.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventDescriptor.cs @@ -89,12 +89,12 @@ namespace System.Diagnostics.Tracing { if (id < 0) { - throw new ArgumentOutOfRangeException(nameof(id), Resources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(id), SR.ArgumentOutOfRange_NeedNonNegNum); } if (id > ushort.MaxValue) { - throw new ArgumentOutOfRangeException(nameof(id), Resources.GetResourceString("ArgumentOutOfRange_NeedValidId", 1, ushort.MaxValue)); + throw new ArgumentOutOfRangeException(nameof(id), SR.Format(SR.ArgumentOutOfRange_NeedValidId, 1, ushort.MaxValue)); } m_traceloggingId = 0; @@ -107,12 +107,12 @@ namespace System.Diagnostics.Tracing if (task < 0) { - throw new ArgumentOutOfRangeException(nameof(task), Resources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(task), SR.ArgumentOutOfRange_NeedNonNegNum); } if (task > ushort.MaxValue) { - throw new ArgumentOutOfRangeException(nameof(task), Resources.GetResourceString("ArgumentOutOfRange_NeedValidId", 1, ushort.MaxValue)); + throw new ArgumentOutOfRangeException(nameof(task), SR.Format(SR.ArgumentOutOfRange_NeedValidId, 1, ushort.MaxValue)); } m_task = (ushort)task; diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventProvider.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventProvider.cs index 5292551314..64c2491769 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventProvider.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventProvider.cs @@ -87,7 +87,8 @@ namespace System.Diagnostics.Tracing private long m_allKeywordMask; // Match all keyword private List m_liveSessions; // current live sessions (Tuple) private bool m_enabled; // Enabled flag from Trace callback - private Guid m_providerId; // Control Guid + private string m_providerName; // Control name + private Guid m_providerId; // Control Guid internal bool m_disposed; // when true provider has unregistered [ThreadStatic] @@ -140,16 +141,19 @@ namespace System.Diagnostics.Tracing // // // - internal unsafe void Register(Guid providerGuid) + internal unsafe void Register(EventSource eventSource) { - m_providerId = providerGuid; uint status; m_etwCallback = new UnsafeNativeMethods.ManifestEtw.EtwEnableCallback(EtwEnableCallBack); - status = EventRegister(ref m_providerId, m_etwCallback); + status = EventRegister(eventSource, m_etwCallback); if (status != 0) { - throw new ArgumentException(Win32Native.GetMessage(unchecked((int)status))); +#if PLATFORM_WINDOWS && !ES_BUILD_STANDALONE + throw new ArgumentException(Interop.Kernel32.GetMessage(unchecked((int)status))); +#else + throw new ArgumentException(Convert.ToString(unchecked((int)status))); +#endif } } @@ -488,7 +492,7 @@ namespace System.Diagnostics.Tracing // at least some of the time. // Determine our session from what is in the registry. - string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerId + "}"; + string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerName + "}"; if (System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)) == 8) regKey = @"Software" + @"\Wow6432Node" + regKey; else @@ -563,7 +567,7 @@ namespace System.Diagnostics.Tracing if (filterData == null) { #if (!ES_BUILD_PCL && !ES_BUILD_PN && PLATFORM_WINDOWS) - string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerId + "}"; + string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerName + "}"; if (System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)) == 8) regKey = @"HKEY_LOCAL_MACHINE\Software" + @"\Wow6432Node" + regKey; else @@ -1184,11 +1188,12 @@ namespace System.Diagnostics.Tracing // These are look-alikes to the Manifest based ETW OS APIs that have been shimmed to work // either with Manifest ETW or Classic ETW (if Manifest based ETW is not available). - private unsafe uint EventRegister(ref Guid providerId, UnsafeNativeMethods.ManifestEtw.EtwEnableCallback enableCallback) + private unsafe uint EventRegister(EventSource eventSource, UnsafeNativeMethods.ManifestEtw.EtwEnableCallback enableCallback) { - m_providerId = providerId; + m_providerName = eventSource.Name; + m_providerId = eventSource.Guid; m_etwCallback = enableCallback; - return m_eventProvider.EventRegister(ref providerId, enableCallback, null, ref m_regHandle); + return m_eventProvider.EventRegister(eventSource, enableCallback, null, ref m_regHandle); } private uint EventUnregister(long registrationHandle) @@ -1221,11 +1226,12 @@ namespace System.Diagnostics.Tracing { // Register an event provider. unsafe uint IEventProvider.EventRegister( - ref Guid providerId, + EventSource eventSource, UnsafeNativeMethods.ManifestEtw.EtwEnableCallback enableCallback, void* callbackContext, ref long registrationHandle) { + Guid providerId = eventSource.Guid; return UnsafeNativeMethods.ManifestEtw.EventRegister( ref providerId, enableCallback, @@ -1278,7 +1284,7 @@ namespace System.Diagnostics.Tracing internal sealed class NoOpEventProvider : IEventProvider { unsafe uint IEventProvider.EventRegister( - ref Guid providerId, + EventSource eventSource, UnsafeNativeMethods.ManifestEtw.EtwEnableCallback enableCallback, void* callbackContext, ref long registrationHandle) diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs.REMOVED.git-id b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs.REMOVED.git-id index bdba4080f3..4f26205c3c 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs.REMOVED.git-id +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs.REMOVED.git-id @@ -1 +1 @@ -3349c069c6a4dd3462332758e57fe7ba5a4bf874 \ No newline at end of file +f6d48998436a4b293130436942ccb5891b740605 \ No newline at end of file diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSourceException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSourceException.cs index 73e32aaf53..be1bf3940a 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSourceException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSourceException.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Resources; using System.Runtime.Serialization; #if ES_BUILD_STANDALONE @@ -18,16 +19,16 @@ namespace System.Diagnostics.Tracing /// /// Exception that is thrown when an error occurs during EventSource operation. /// -#if !CORECLR && !ES_BUILD_PN && !ES_BUILD_PCL && !CORERT +#if !ES_BUILD_PCL [Serializable] -#endif // !CORECLR && !ES_BUILD_PN && !ES_BUILD_PCL && !CORERT +#endif public class EventSourceException : Exception { /// /// Initializes a new instance of the EventSourceException class. /// public EventSourceException() : - base(Resources.GetResourceString("EventSource_ListenerWriteFailure")) { } + base(SR.EventSource_ListenerWriteFailure) { } /// /// Initializes a new instance of the EventSourceException class with a specified error message. @@ -48,6 +49,6 @@ namespace System.Diagnostics.Tracing #endif internal EventSourceException(Exception innerException) : - base(Resources.GetResourceString("EventSource_ListenerWriteFailure"), innerException) { } + base(SR.EventSource_ListenerWriteFailure, innerException) { } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IEventProvider.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IEventProvider.cs index 71a2fe4d44..d34f80eb51 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IEventProvider.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IEventProvider.cs @@ -2,6 +2,7 @@ // 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 Microsoft.Win32; #if ES_BUILD_STANDALONE @@ -15,7 +16,7 @@ namespace System.Diagnostics.Tracing { // Register an event provider. unsafe uint EventRegister( - ref Guid providerId, + EventSource eventSource, UnsafeNativeMethods.ManifestEtw.EtwEnableCallback enableCallback, void* callbackContext, ref long registrationHandle); diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/StubEnvironment.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/StubEnvironment.cs index b365841d5f..1b3ca8004c 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/StubEnvironment.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/StubEnvironment.cs @@ -52,29 +52,6 @@ namespace System.Diagnostics.Tracing.Internal #if ES_BUILD_AGAINST_DOTNET_V35 -namespace Microsoft.Diagnostics.Contracts.Internal -{ - internal class Contract - { - public static void Assert(bool invariant) - { - Assert(invariant, string.Empty); - } - public static void Assert(bool invariant, string message) - { - if (!invariant) - { - if (System.Diagnostics.Debugger.IsAttached) - System.Diagnostics.Debugger.Break(); - throw new Exception("Assertion failed: " + message); - } - } - public static void EndContractBlock() - { } - } -} - - namespace Microsoft.Internal { using System.Text; @@ -379,3 +356,18 @@ namespace System } } #endif + +#if ES_BUILD_STANDALONE +namespace Microsoft.Win32 +{ + using System.Runtime.InteropServices; + using System.Security; + + [SuppressUnmanagedCodeSecurityAttribute()] + internal static class Win32Native + { + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + internal static extern uint GetCurrentProcessId(); + } +} +#endif diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/DataCollector.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/DataCollector.cs index 27aae820e9..1444c267cb 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/DataCollector.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/DataCollector.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Resources; using System.Runtime.InteropServices; using System.Security; @@ -85,7 +86,7 @@ namespace System.Diagnostics.Tracing var scratchNew = scratchOld + size; if (this.scratchEnd < scratchNew) { - throw new IndexOutOfRangeException(Resources.GetResourceString("EventSource_AddScalarOutOfRange")); + throw new IndexOutOfRangeException(SR.EventSource_AddScalarOutOfRange); } this.ScalarsBegin(); @@ -272,20 +273,20 @@ namespace System.Diagnostics.Tracing var pinsTemp = this.pins; if (this.pinsEnd <= pinsTemp) { - throw new IndexOutOfRangeException(Resources.GetResourceString("EventSource_PinArrayOutOfRange")); + throw new IndexOutOfRangeException(SR.EventSource_PinArrayOutOfRange); } var datasTemp = this.datas; if (this.datasEnd <= datasTemp) { - throw new IndexOutOfRangeException(Resources.GetResourceString("EventSource_DataDescriptorsOutOfRange")); + throw new IndexOutOfRangeException(SR.EventSource_DataDescriptorsOutOfRange); } this.pins = pinsTemp + 1; this.datas = datasTemp + 1; *pinsTemp = GCHandle.Alloc(value, GCHandleType.Pinned); - datasTemp->m_Ptr = (long)(ulong)(UIntPtr)(void*)pinsTemp->AddrOfPinnedObject(); + datasTemp->DataPointer = pinsTemp->AddrOfPinnedObject(); datasTemp->m_Size = size; } @@ -296,10 +297,10 @@ namespace System.Diagnostics.Tracing var datasTemp = this.datas; if (this.datasEnd <= datasTemp) { - throw new IndexOutOfRangeException(Resources.GetResourceString("EventSource_DataDescriptorsOutOfRange")); + throw new IndexOutOfRangeException(SR.EventSource_DataDescriptorsOutOfRange); } - datasTemp->m_Ptr = (long)(ulong)(UIntPtr)this.scratch; + datasTemp->DataPointer = (IntPtr) this.scratch; this.writingScalars = true; } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs index 5967ad6ab5..30a941195f 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs @@ -2,6 +2,7 @@ // 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.Collections.Generic; using System.Collections; using System.Diagnostics; @@ -53,7 +54,7 @@ namespace System.Diagnostics.Tracing position++; } - throw new System.Collections.Generic.KeyNotFoundException(); + throw new System.Collections.Generic.KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key.ToString())); } set { diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs index 38c1767462..865082f767 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs @@ -36,7 +36,6 @@ namespace System.Diagnostics.Tracing { if (eventSource == null) throw new ArgumentNullException(nameof(eventSource)); - Contract.EndContractBlock(); this.eventSource = eventSource; } @@ -77,8 +76,8 @@ namespace System.Diagnostics.Tracing /// /// Writes a Start event with the specified name and data. If the start event is not active (because the provider - /// is not on or keyword-level indiates the event is off, then the returned activity is simply the 'this' poitner - /// and it is effectively like the Start d + /// is not on or keyword-level indicates the event is off, then the returned activity is simply the 'this' pointer + /// and it is effectively like start did not get called. /// /// A new activityID GUID is generated and the returned /// EventSourceActivity remembers this activity and will mark every event (including the start stop and any writes) @@ -139,7 +138,7 @@ namespace System.Diagnostics.Tracing } /// /// Used if you wish to use the non-default stop name (which is the start name with Start replace with 'Stop') - /// This can be useful to indicate unusual ways of stoping (but it is still STRONGLY recommeded that + /// This can be useful to indicate unusual ways of stopping (but it is still STRONGLY recommended that /// you start with the same prefix used for the start event and you end with the 'Stop' suffix. /// public void Stop(string eventName) @@ -149,7 +148,7 @@ namespace System.Diagnostics.Tracing } /// /// Used if you wish to use the non-default stop name (which is the start name with Start replace with 'Stop') - /// This can be useful to indicate unusual ways of stoping (but it is still STRONGLY recommeded that + /// This can be useful to indicate unusual ways of stopping (but it is still STRONGLY recommended that /// you start with the same prefix used for the start event and you end with the 'Stop' suffix. /// public void Stop(string eventName, T data) @@ -158,7 +157,7 @@ namespace System.Diagnostics.Tracing } /// - /// Writes an event associated with this activity to the eventSource associted with this activity. + /// Writes an event associated with this activity to the eventSource associated with this activity. /// May only be called when the activity is in the Started state. /// /// diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/FieldMetadata.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/FieldMetadata.cs index 309226b84d..9c7c6369ec 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/FieldMetadata.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/FieldMetadata.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Resources; using Encoding = System.Text.Encoding; #if ES_BUILD_STANDALONE @@ -128,17 +129,17 @@ namespace System.Diagnostics.Tracing { if (coreType == (int)TraceLoggingDataType.Nil) { - throw new NotSupportedException(Resources.GetResourceString("EventSource_NotSupportedArrayOfNil")); + throw new NotSupportedException(SR.EventSource_NotSupportedArrayOfNil); } if (coreType == (int)TraceLoggingDataType.Binary) { - throw new NotSupportedException(Resources.GetResourceString("EventSource_NotSupportedArrayOfBinary")); + throw new NotSupportedException(SR.EventSource_NotSupportedArrayOfBinary); } #if !BROKEN_UNTIL_M3 if (coreType == (int)TraceLoggingDataType.Utf16String || coreType == (int)TraceLoggingDataType.MbcsString) { - throw new NotSupportedException(Resources.GetResourceString("EventSource_NotSupportedArrayOfNullTerminatedString")); + throw new NotSupportedException(SR.EventSource_NotSupportedArrayOfNullTerminatedString); } #endif } @@ -160,7 +161,7 @@ namespace System.Diagnostics.Tracing this.outType++; if ((this.outType & Statics.OutTypeMask) == 0) { - throw new NotSupportedException(Resources.GetResourceString("EventSource_TooManyFields")); + throw new NotSupportedException(SR.EventSource_TooManyFields); } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs index 3ea781252f..ae60888493 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs @@ -1,4 +1,9 @@ -using System.Reflection; +// 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.Reflection; using System.Runtime.InteropServices; using System.Diagnostics; @@ -8,7 +13,11 @@ using Contract = System.Diagnostics.Contracts.Contract; using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract; #endif +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else namespace System.Diagnostics.Tracing +#endif { /// /// Holds property values of any type. For common value types, we have inline storage so that we don't need @@ -16,7 +25,13 @@ namespace System.Diagnostics.Tracing /// /// To get the value of a property quickly, use a delegate produced by . /// - internal unsafe struct PropertyValue +#if ES_BUILD_PN + [CLSCompliant(false)] + public +#else + internal +#endif + unsafe readonly struct PropertyValue { /// /// Union of well-known value types, to avoid boxing those types. @@ -198,7 +213,12 @@ namespace System.Diagnostics.Tracing return helper.GetPropertyGetter(property); } - private abstract class TypeHelper +#if ES_BUILD_PN + public +#else + private +#endif + abstract class TypeHelper { public abstract Func GetPropertyGetter(PropertyInfo property); @@ -208,7 +228,12 @@ namespace System.Diagnostics.Tracing } } - private sealed class ReferenceTypeHelper : TypeHelper where TContainer : class +#if ES_BUILD_PN + public +#else + private +#endif + sealed class ReferenceTypeHelper : TypeHelper where TContainer : class { public override Func GetPropertyGetter(PropertyInfo property) { diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/Statics.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/Statics.cs index 9fa776753d..05539ab4fd 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/Statics.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/Statics.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using System.Resources; using System.Runtime.CompilerServices; using Encoding = System.Text.Encoding; @@ -531,7 +532,7 @@ namespace System.Diagnostics.Tracing if (recursionCheck.Contains(dataType)) { - throw new NotSupportedException(Resources.GetResourceString("EventSource_RecursiveTypeDefinition")); + throw new NotSupportedException(SR.EventSource_RecursiveTypeDefinition); } recursionCheck.Add(dataType); @@ -714,7 +715,7 @@ namespace System.Diagnostics.Tracing } else { - throw new ArgumentException(Resources.GetResourceString("EventSource_NonCompliantTypeError", dataType.Name)); + throw new ArgumentException(SR.Format(SR.EventSource_NonCompliantTypeError, dataType.Name)); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs index 529948daf8..cc416a96d9 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs @@ -182,7 +182,7 @@ namespace System.Diagnostics.Tracing /// /// Core type. - /// Special case: Struct indicates that this field plus the the + /// Special case: Struct indicates that this field plus the /// subsequent N logical fields are to be considered as one logical /// field (i.e. a nested structure). The OutType is used to encode N. /// The maximum value for N is 127. This field has no payload by diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs index e73339949a..bf29d71844 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs @@ -24,6 +24,7 @@ using EventDescriptor = Microsoft.Diagnostics.Tracing.EventDescriptor; #endif using System; +using System.Resources; using System.Runtime.InteropServices; using System.Security; using System.Collections.ObjectModel; @@ -80,7 +81,7 @@ namespace System.Diagnostics.Tracing /// /// Also specify a list of key-value pairs called traits (you must pass an even number of strings). /// The first string is the key and the second is the value. These are not interpreted by EventSource - /// itself but may be interprated the listeners. Can be fetched with GetTrait(string). + /// itself but may be interpreted the listeners. Can be fetched with GetTrait(string). /// /// /// The name of the event source. Must not be null. @@ -102,7 +103,6 @@ namespace System.Diagnostics.Tracing { throw new ArgumentNullException(nameof(eventSourceName)); } - Contract.EndContractBlock(); } /// @@ -117,8 +117,6 @@ namespace System.Diagnostics.Tracing throw new ArgumentNullException(nameof(eventName)); } - Contract.EndContractBlock(); - if (!this.IsEnabled()) { return; @@ -144,8 +142,6 @@ namespace System.Diagnostics.Tracing throw new ArgumentNullException(nameof(eventName)); } - Contract.EndContractBlock(); - if (!this.IsEnabled()) { return; @@ -442,7 +438,10 @@ namespace System.Diagnostics.Tracing var pinCount = eventTypes.pinCount; var scratch = stackalloc byte[eventTypes.scratchSize]; var descriptors = stackalloc EventData[eventTypes.dataCount + 3]; + var pins = stackalloc GCHandle[pinCount]; + for (int i = 0; i < pinCount; i++) + pins[i] = default(GCHandle); fixed (byte* pMetadata0 = this.providerMetadata, @@ -562,7 +561,7 @@ namespace System.Diagnostics.Tracing if (eventTypes.typeInfos[i].DataType == typeof(string)) { // Write out the size of the string - descriptors[numDescrs].m_Ptr = (long)&descriptors[numDescrs + 1].m_Size; + descriptors[numDescrs].DataPointer = (IntPtr) (&descriptors[numDescrs + 1].m_Size); descriptors[numDescrs].m_Size = 2; numDescrs++; @@ -619,7 +618,10 @@ namespace System.Diagnostics.Tracing var pinCount = eventTypes.pinCount; var scratch = stackalloc byte[eventTypes.scratchSize]; var descriptors = stackalloc EventData[eventTypes.dataCount + 3]; + var pins = stackalloc GCHandle[pinCount]; + for (int i = 0; i < pinCount; i++) + pins[i] = default(GCHandle); fixed (byte* pMetadata0 = this.providerMetadata, @@ -744,9 +746,9 @@ namespace System.Diagnostics.Tracing { DataCollector.ThreadInstance.Disable(); - for (int i = 0; i != cPins; i++) + for (int i = 0; i < cPins; i++) { - if (IntPtr.Zero != (IntPtr)pPins[i]) + if (pPins[i].IsAllocated) { pPins[i].Free(); } @@ -773,7 +775,7 @@ namespace System.Diagnostics.Tracing } else { - throw new ArgumentException(Resources.GetResourceString("UnknownEtwTrait", etwTrait), "traits"); + throw new ArgumentException(SR.Format(SR.EventSource_UnknownEtwTrait, etwTrait), "traits"); } } string value = m_traits[i + 1]; @@ -816,7 +818,7 @@ namespace System.Diagnostics.Tracing { if (!(i + 1 < value.Length)) { - throw new ArgumentException(Resources.GetResourceString("EvenHexDigits"), "traits"); + throw new ArgumentException(SR.EventSource_EvenHexDigits, "traits"); } metaData.Add((byte)(HexDigit(value[i]) * 16 + HexDigit(value[i + 1]))); i++; @@ -829,7 +831,7 @@ namespace System.Diagnostics.Tracing } else { - throw new ArgumentException(Resources.GetResourceString("IllegalValue", value), "traits"); + throw new ArgumentException(SR.Format(SR.EventSource_IllegalValue, value), "traits"); } return metaData.Count - startPos; @@ -853,7 +855,7 @@ namespace System.Diagnostics.Tracing return (c - 'A' + 10); } - throw new ArgumentException(Resources.GetResourceString("BadHexDigit", c), "traits"); + throw new ArgumentException(SR.Format(SR.EventSource_BadHexDigit, c), "traits"); } private NameInfo UpdateDescriptor( diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTypes.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTypes.cs index c2239671bb..3c775a3cef 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTypes.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTypes.cs @@ -97,8 +97,6 @@ namespace System.Diagnostics.Tracing throw new ArgumentNullException(nameof(name)); } - Contract.EndContractBlock(); - this.typeInfos = MakeArray(paramInfos); this.name = name; this.tags = tags; @@ -135,8 +133,6 @@ namespace System.Diagnostics.Tracing throw new ArgumentNullException(nameof(defaultName)); } - Contract.EndContractBlock(); - this.typeInfos = typeInfos; this.name = defaultName; this.tags = tags; @@ -215,8 +211,6 @@ namespace System.Diagnostics.Tracing throw new ArgumentNullException(nameof(paramInfos)); } - Contract.EndContractBlock(); - var recursionCheck = new List(paramInfos.Length); var result = new TraceLoggingTypeInfo[paramInfos.Length]; for (int i = 0; i < paramInfos.Length; ++i) @@ -234,8 +228,6 @@ namespace System.Diagnostics.Tracing throw new ArgumentNullException(nameof(types)); } - Contract.EndContractBlock(); - var recursionCheck = new List(types.Length); var result = new TraceLoggingTypeInfo[types.Length]; for (int i = 0; i < types.Length; i++) @@ -254,8 +246,6 @@ namespace System.Diagnostics.Tracing throw new ArgumentNullException(nameof(typeInfos)); } - Contract.EndContractBlock(); - return (TraceLoggingTypeInfo[])typeInfos.Clone(); ; } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingMetadataCollector.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingMetadataCollector.cs index 41225c8626..1db1a28c9d 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingMetadataCollector.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingMetadataCollector.cs @@ -232,7 +232,7 @@ namespace System.Diagnostics.Tracing if (this.BeginningBufferedArray) { - throw new NotSupportedException(Resources.GetResourceString("EventSource_NotSupportedNestedArraysEnums")); + throw new NotSupportedException(SR.EventSource_NotSupportedNestedArraysEnums); } this.impl.AddScalar(2); @@ -244,7 +244,7 @@ namespace System.Diagnostics.Tracing { if (this.bufferedArrayFieldCount >= 0) { - throw new NotSupportedException(Resources.GetResourceString("EventSource_NotSupportedNestedArraysEnums")); + throw new NotSupportedException(SR.EventSource_NotSupportedNestedArraysEnums); } this.bufferedArrayFieldCount = 0; @@ -255,7 +255,7 @@ namespace System.Diagnostics.Tracing { if (this.bufferedArrayFieldCount != 1) { - throw new InvalidOperationException(Resources.GetResourceString("EventSource_IncorrentlyAuthoredTypeInfo")); + throw new InvalidOperationException(SR.EventSource_IncorrentlyAuthoredTypeInfo); } this.bufferedArrayFieldCount = int.MinValue; @@ -274,7 +274,7 @@ namespace System.Diagnostics.Tracing { if (this.BeginningBufferedArray) { - throw new NotSupportedException(Resources.GetResourceString("EventSource_NotSupportedCustomSerializedData")); + throw new NotSupportedException(SR.EventSource_NotSupportedCustomSerializedData); } this.impl.AddScalar(2); diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingTypeInfo.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingTypeInfo.cs index d68e106b0b..511a4fe480 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingTypeInfo.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingTypeInfo.cs @@ -39,8 +39,6 @@ namespace System.Diagnostics.Tracing throw new ArgumentNullException(nameof(dataType)); } - Contract.EndContractBlock(); - this.name = dataType.Name; this.dataType = dataType; this.propertyValueFactory = PropertyValue.GetFactory(dataType); @@ -64,8 +62,6 @@ namespace System.Diagnostics.Tracing throw new ArgumentNullException(nameof(name)); } - Contract.EndContractBlock(); - Statics.CheckName(name); this.name = name; diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/Winmeta.cs b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/Winmeta.cs index ac756b6059..c60ca5b365 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/Winmeta.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/Winmeta.cs @@ -55,9 +55,6 @@ namespace System.Diagnostics.Tracing /// /// WindowsEventTask. Custom values must be in the range from 1 through 65534 /// -#if (!ES_BUILD_STANDALONE && !ES_BUILD_PN) - [System.Runtime.CompilerServices.FriendAccessAllowed] -#endif public enum EventTask { /// @@ -68,9 +65,6 @@ namespace System.Diagnostics.Tracing /// /// EventOpcode. Custom values must be in the range from 11 through 239 /// -#if (!ES_BUILD_STANDALONE && !ES_BUILD_PN) - [System.Runtime.CompilerServices.FriendAccessAllowed] -#endif public enum EventOpcode { /// @@ -124,9 +118,6 @@ namespace System.Diagnostics.Tracing /// EventChannel. Custom values must be in the range from 16 through 255. Currently only predefined values allowed. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1028:EnumStorageShouldBeInt32", Justification = "Backwards compatibility")] -#if (!ES_BUILD_STANDALONE && !ES_BUILD_PN) - [System.Runtime.CompilerServices.FriendAccessAllowed] -#endif public enum EventChannel : byte { /// diff --git a/external/corert/src/System.Private.CoreLib/shared/System/DivideByZeroException.cs b/external/corert/src/System.Private.CoreLib/shared/System/DivideByZeroException.cs index 0fad2f8d56..b309695ff3 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/DivideByZeroException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/DivideByZeroException.cs @@ -15,29 +15,30 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class DivideByZeroException : ArithmeticException { public DivideByZeroException() : base(SR.Arg_DivideByZero) { - HResult = __HResults.COR_E_DIVIDEBYZERO; + HResult = HResults.COR_E_DIVIDEBYZERO; } public DivideByZeroException(String message) : base(message) { - HResult = __HResults.COR_E_DIVIDEBYZERO; + HResult = HResults.COR_E_DIVIDEBYZERO; } public DivideByZeroException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_DIVIDEBYZERO; + HResult = HResults.COR_E_DIVIDEBYZERO; } protected DivideByZeroException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/DllNotFoundException.cs b/external/corert/src/System.Private.CoreLib/shared/System/DllNotFoundException.cs index 8c69e45eda..14fb50d9c5 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/DllNotFoundException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/DllNotFoundException.cs @@ -16,29 +16,30 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class DllNotFoundException : TypeLoadException { public DllNotFoundException() : base(SR.Arg_DllNotFoundException) { - HResult = __HResults.COR_E_DLLNOTFOUND; + HResult = HResults.COR_E_DLLNOTFOUND; } public DllNotFoundException(String message) : base(message) { - HResult = __HResults.COR_E_DLLNOTFOUND; + HResult = HResults.COR_E_DLLNOTFOUND; } public DllNotFoundException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_DLLNOTFOUND; + HResult = HResults.COR_E_DLLNOTFOUND; } protected DllNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Double.cs b/external/corert/src/System.Private.CoreLib/shared/System/Double.cs new file mode 100644 index 0000000000..3652963ef6 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Double.cs @@ -0,0 +1,448 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: A representation of an IEEE double precision +** floating point number. +** +** +===========================================================*/ + +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Double : IComparable, IConvertible, IFormattable, IComparable, IEquatable, ISpanFormattable + { + private double m_value; // Do not rename (binary serialization) + + // + // Public Constants + // + public const double MinValue = -1.7976931348623157E+308; + public const double MaxValue = 1.7976931348623157E+308; + + // Note Epsilon should be a double whose hex representation is 0x1 + // on little endian machines. + public const double Epsilon = 4.9406564584124654E-324; + public const double NegativeInfinity = (double)-1.0 / (double)(0.0); + public const double PositiveInfinity = (double)1.0 / (double)(0.0); + public const double NaN = (double)0.0 / (double)0.0; + + // We use this explicit definition to avoid the confusion between 0.0 and -0.0. + internal const double NegativeZero = -0.0; + + /// Determines whether the specified value is finite (zero, subnormal, or normal). + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsFinite(double d) + { + var bits = BitConverter.DoubleToInt64Bits(d); + return (bits & 0x7FFFFFFFFFFFFFFF) < 0x7FF0000000000000; + } + + /// Determines whether the specified value is infinite. + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsInfinity(double d) + { + var bits = BitConverter.DoubleToInt64Bits(d); + return (bits & 0x7FFFFFFFFFFFFFFF) == 0x7FF0000000000000; + } + + /// Determines whether the specified value is NaN. + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsNaN(double d) + { + var bits = BitConverter.DoubleToInt64Bits(d); + return (bits & 0x7FFFFFFFFFFFFFFF) > 0x7FF0000000000000; + } + + /// Determines whether the specified value is negative. + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsNegative(double d) + { + var bits = unchecked((ulong)BitConverter.DoubleToInt64Bits(d)); + return (bits & 0x8000000000000000) == 0x8000000000000000; + } + + /// Determines whether the specified value is negative infinity. + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNegativeInfinity(double d) + { + return (d == double.NegativeInfinity); + } + + /// Determines whether the specified value is normal. + [NonVersionable] + // This is probably not worth inlining, it has branches and should be rarely called + public unsafe static bool IsNormal(double d) + { + var bits = BitConverter.DoubleToInt64Bits(d); + bits &= 0x7FFFFFFFFFFFFFFF; + return (bits < 0x7FF0000000000000) && (bits != 0) && ((bits & 0x7FF0000000000000) != 0); + } + + /// Determines whether the specified value is positive infinity. + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsPositiveInfinity(double d) + { + return (d == double.PositiveInfinity); + } + + /// Determines whether the specified value is subnormal. + [NonVersionable] + // This is probably not worth inlining, it has branches and should be rarely called + public unsafe static bool IsSubnormal(double d) + { + var bits = BitConverter.DoubleToInt64Bits(d); + bits &= 0x7FFFFFFFFFFFFFFF; + return (bits < 0x7FF0000000000000) && (bits != 0) && ((bits & 0x7FF0000000000000) == 0); + } + + // Compares this object to another object, returning an instance of System.Relation. + // Null is considered less than any instance. + // + // If object is not of type Double, this method throws an ArgumentException. + // + // Returns a value less than zero if this object + // + public int CompareTo(Object value) + { + if (value == null) + { + return 1; + } + if (value is Double) + { + double d = (double)value; + if (m_value < d) return -1; + if (m_value > d) return 1; + if (m_value == d) return 0; + + // At least one of the values is NaN. + if (IsNaN(m_value)) + return (IsNaN(d) ? 0 : -1); + else + return 1; + } + throw new ArgumentException(SR.Arg_MustBeDouble); + } + + public int CompareTo(Double value) + { + if (m_value < value) return -1; + if (m_value > value) return 1; + if (m_value == value) return 0; + + // At least one of the values is NaN. + if (IsNaN(m_value)) + return (IsNaN(value) ? 0 : -1); + else + return 1; + } + + // True if obj is another Double with the same value as the current instance. This is + // a method of object equality, that only returns true if obj is also a double. + public override bool Equals(Object obj) + { + if (!(obj is Double)) + { + return false; + } + double temp = ((Double)obj).m_value; + // This code below is written this way for performance reasons i.e the != and == check is intentional. + if (temp == m_value) + { + return true; + } + return IsNaN(temp) && IsNaN(m_value); + } + + [NonVersionable] + public static bool operator ==(Double left, Double right) + { + return left == right; + } + + [NonVersionable] + public static bool operator !=(Double left, Double right) + { + return left != right; + } + + [NonVersionable] + public static bool operator <(Double left, Double right) + { + return left < right; + } + + [NonVersionable] + public static bool operator >(Double left, Double right) + { + return left > right; + } + + [NonVersionable] + public static bool operator <=(Double left, Double right) + { + return left <= right; + } + + [NonVersionable] + public static bool operator >=(Double left, Double right) + { + return left >= right; + } + + public bool Equals(Double obj) + { + if (obj == m_value) + { + return true; + } + return IsNaN(obj) && IsNaN(m_value); + } + + //The hashcode for a double is the absolute value of the integer representation + //of that double. + // + public unsafe override int GetHashCode() + { + double d = m_value; + if (d == 0) + { + // Ensure that 0 and -0 have the same hash code + return 0; + } + long value = *(long*)(&d); + return unchecked((int)value) ^ ((int)(value >> 32)); + } + + public override String ToString() + { + return Number.FormatDouble(m_value, null, NumberFormatInfo.CurrentInfo); + } + + public String ToString(String format) + { + return Number.FormatDouble(m_value, format, NumberFormatInfo.CurrentInfo); + } + + public String ToString(IFormatProvider provider) + { + return Number.FormatDouble(m_value, null, NumberFormatInfo.GetInstance(provider)); + } + + public String ToString(String format, IFormatProvider provider) + { + return Number.FormatDouble(m_value, format, NumberFormatInfo.GetInstance(provider)); + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) + { + return Number.TryFormatDouble(m_value, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); + } + + public static double Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseDouble(s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo); + } + + public static double Parse(String s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseDouble(s, style, NumberFormatInfo.CurrentInfo); + } + + public static double Parse(String s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseDouble(s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.GetInstance(provider)); + } + + public static double Parse(String s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseDouble(s, style, NumberFormatInfo.GetInstance(provider)); + } + + // Parses a double from a String in the given style. If + // a NumberFormatInfo isn't specified, the current culture's + // NumberFormatInfo is assumed. + // + // This method will not throw an OverflowException, but will return + // PositiveInfinity or NegativeInfinity for a number that is too + // large or too small. + + public static double Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return Number.ParseDouble(s, style, NumberFormatInfo.GetInstance(provider)); + } + + + + public static bool TryParse(String s, out double result) + { + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(ReadOnlySpan s, out double result) + { + return TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out double result) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out double result) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + private static bool TryParse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out double result) + { + bool success = Number.TryParseDouble(s, style, info, out result); + if (!success) + { + ReadOnlySpan sTrim = StringSpanHelpers.Trim(s); + if (StringSpanHelpers.Equals(sTrim, info.PositiveInfinitySymbol)) + { + result = PositiveInfinity; + } + else if (StringSpanHelpers.Equals(sTrim, info.NegativeInfinitySymbol)) + { + result = NegativeInfinity; + } + else if (StringSpanHelpers.Equals(sTrim, info.NaNSymbol)) + { + result = NaN; + } + else + { + return false; // We really failed + } + } + return true; + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.Double; + } + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Double", "Char")); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(m_value); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(m_value); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return m_value; + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Double", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/DuplicateWaitObjectException.cs b/external/corert/src/System.Private.CoreLib/shared/System/DuplicateWaitObjectException.cs index 7eadead8c7..77303846a3 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/DuplicateWaitObjectException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/DuplicateWaitObjectException.cs @@ -17,7 +17,8 @@ namespace System { // The DuplicateWaitObjectException is thrown when an object // appears more than once in the list of objects to WaitAll or WaitAny. - // + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class DuplicateWaitObjectException : ArgumentException { private static volatile String s_duplicateWaitObjectMessage = null; @@ -37,30 +38,29 @@ namespace System public DuplicateWaitObjectException() : base(DuplicateWaitObjectMessage) { - HResult = __HResults.COR_E_DUPLICATEWAITOBJECT; + HResult = HResults.COR_E_DUPLICATEWAITOBJECT; } public DuplicateWaitObjectException(String parameterName) : base(DuplicateWaitObjectMessage, parameterName) { - HResult = __HResults.COR_E_DUPLICATEWAITOBJECT; + HResult = HResults.COR_E_DUPLICATEWAITOBJECT; } public DuplicateWaitObjectException(String parameterName, String message) : base(message, parameterName) { - HResult = __HResults.COR_E_DUPLICATEWAITOBJECT; + HResult = HResults.COR_E_DUPLICATEWAITOBJECT; } public DuplicateWaitObjectException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_DUPLICATEWAITOBJECT; + HResult = HResults.COR_E_DUPLICATEWAITOBJECT; } protected DuplicateWaitObjectException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Empty.cs b/external/corert/src/System.Private.CoreLib/shared/System/Empty.cs new file mode 100644 index 0000000000..186b92078e --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Empty.cs @@ -0,0 +1,25 @@ +// 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. + +namespace System +{ +#if CORERT + public // Needs to be public so that Reflection.Core can see it. +#else + internal +#endif + sealed class Empty + { + private Empty() + { + } + + public static readonly Empty Value = new Empty(); + + public override string ToString() + { + return string.Empty; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/EntryPointNotFoundException.cs b/external/corert/src/System.Private.CoreLib/shared/System/EntryPointNotFoundException.cs index 0b881cec05..dac1cdb971 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/EntryPointNotFoundException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/EntryPointNotFoundException.cs @@ -16,29 +16,30 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class EntryPointNotFoundException : TypeLoadException { public EntryPointNotFoundException() : base(SR.Arg_EntryPointNotFoundException) { - HResult = __HResults.COR_E_ENTRYPOINTNOTFOUND; + HResult = HResults.COR_E_ENTRYPOINTNOTFOUND; } public EntryPointNotFoundException(String message) : base(message) { - HResult = __HResults.COR_E_ENTRYPOINTNOTFOUND; + HResult = HResults.COR_E_ENTRYPOINTNOTFOUND; } public EntryPointNotFoundException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_ENTRYPOINTNOTFOUND; + HResult = HResults.COR_E_ENTRYPOINTNOTFOUND; } protected EntryPointNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/EventArgs.cs b/external/corert/src/System.Private.CoreLib/shared/System/EventArgs.cs index be12af25b6..f3561a8d0b 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/EventArgs.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/EventArgs.cs @@ -7,6 +7,8 @@ using System; namespace System { // The base class for all event classes. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class EventArgs { public static readonly EventArgs Empty = new EventArgs(); diff --git a/external/corert/src/System.Private.CoreLib/shared/System/ExecutionEngineException.cs b/external/corert/src/System.Private.CoreLib/shared/System/ExecutionEngineException.cs index b89f6d9a8d..5edd5cf19f 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/ExecutionEngineException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/ExecutionEngineException.cs @@ -21,24 +21,30 @@ using System.Runtime.Serialization; namespace System { [Obsolete("This type previously indicated an unspecified fatal error in the runtime. The runtime no longer raises this exception so this type is obsolete.")] + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class ExecutionEngineException : SystemException { public ExecutionEngineException() : base(SR.Arg_ExecutionEngineException) { - HResult = __HResults.COR_E_EXECUTIONENGINE; + HResult = HResults.COR_E_EXECUTIONENGINE; } public ExecutionEngineException(String message) : base(message) { - HResult = __HResults.COR_E_EXECUTIONENGINE; + HResult = HResults.COR_E_EXECUTIONENGINE; } public ExecutionEngineException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_EXECUTIONENGINE; + HResult = HResults.COR_E_EXECUTIONENGINE; + } + + internal ExecutionEngineException(SerializationInfo info, StreamingContext context) : base(info, context) + { } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/FieldAccessException.cs b/external/corert/src/System.Private.CoreLib/shared/System/FieldAccessException.cs index b56d749771..cb28264d61 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/FieldAccessException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/FieldAccessException.cs @@ -13,29 +13,30 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class FieldAccessException : MemberAccessException { public FieldAccessException() : base(SR.Arg_FieldAccessException) { - HResult = __HResults.COR_E_FIELDACCESS; + HResult = HResults.COR_E_FIELDACCESS; } public FieldAccessException(String message) : base(message) { - HResult = __HResults.COR_E_FIELDACCESS; + HResult = HResults.COR_E_FIELDACCESS; } public FieldAccessException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_FIELDACCESS; + HResult = HResults.COR_E_FIELDACCESS; } protected FieldAccessException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/FormatException.cs b/external/corert/src/System.Private.CoreLib/shared/System/FormatException.cs index 9baaac2353..b0e273369c 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/FormatException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/FormatException.cs @@ -15,29 +15,30 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class FormatException : SystemException { public FormatException() : base(SR.Arg_FormatException) { - HResult = __HResults.COR_E_FORMAT; + HResult = HResults.COR_E_FORMAT; } public FormatException(String message) : base(message) { - HResult = __HResults.COR_E_FORMAT; + HResult = HResults.COR_E_FORMAT; } public FormatException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_FORMAT; + HResult = HResults.COR_E_FORMAT; } protected FormatException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/BidiCategory.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/BidiCategory.cs new file mode 100644 index 0000000000..abe6950893 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/BidiCategory.cs @@ -0,0 +1,33 @@ +// 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. + +namespace System.Globalization +{ + internal enum BidiCategory + { + LeftToRight = 0, + LeftToRightEmbedding = 1, + LeftToRightOverride = 2, + RightToLeft = 3, + RightToLeftArabic = 4, + RightToLeftEmbedding = 5, + RightToLeftOverride = 6, + PopDirectionalFormat = 7, + EuropeanNumber = 8, + EuropeanNumberSeparator = 9, + EuropeanNumberTerminator = 10, + ArabicNumber = 11, + CommonNumberSeparator = 12, + NonSpacingMark = 13, + BoundaryNeutral = 14, + ParagraphSeparator = 15, + SegmentSeparator = 16, + Whitespace = 17, + OtherNeutrals = 18, + LeftToRightIsolate = 19, + RightToLeftIsolate = 20, + FirstStrongIsolate = 21, + PopDirectionIsolate = 22, + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/Calendar.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/Calendar.cs new file mode 100644 index 0000000000..49ad597ceb --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/Calendar.cs @@ -0,0 +1,840 @@ +// 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.Diagnostics; +using System.Runtime.Serialization; + +namespace System.Globalization +{ + // This abstract class represents a calendar. A calendar reckons time in + // divisions such as weeks, months and years. The number, length and start of + // the divisions vary in each calendar. + // + // Any instant in time can be represented as an n-tuple of numeric values using + // a particular calendar. For example, the next vernal equinox occurs at (0.0, 0 + // , 46, 8, 20, 3, 1999) in the Gregorian calendar. An implementation of + // Calendar can map any DateTime value to such an n-tuple and vice versa. The + // DateTimeFormat class can map between such n-tuples and a textual + // representation such as "8:46 AM March 20th 1999 AD". + // + // Most calendars identify a year which begins the current era. There may be any + // number of previous eras. The Calendar class identifies the eras as enumerated + // integers where the current era (CurrentEra) has the value zero. + // + // For consistency, the first unit in each interval, e.g. the first month, is + // assigned the value one. + // The calculation of hour/minute/second is moved to Calendar from GregorianCalendar, + // since most of the calendars (or all?) have the same way of calcuating hour/minute/second. + + public abstract class Calendar : ICloneable + { + // Number of 100ns (10E-7 second) ticks per time unit + internal const long TicksPerMillisecond = 10000; + internal const long TicksPerSecond = TicksPerMillisecond * 1000; + internal const long TicksPerMinute = TicksPerSecond * 60; + internal const long TicksPerHour = TicksPerMinute * 60; + internal const long TicksPerDay = TicksPerHour * 24; + + // Number of milliseconds per time unit + internal const int MillisPerSecond = 1000; + internal const int MillisPerMinute = MillisPerSecond * 60; + internal const int MillisPerHour = MillisPerMinute * 60; + internal const int MillisPerDay = MillisPerHour * 24; + + // Number of days in a non-leap year + internal const int DaysPerYear = 365; + // Number of days in 4 years + internal const int DaysPer4Years = DaysPerYear * 4 + 1; + // Number of days in 100 years + internal const int DaysPer100Years = DaysPer4Years * 25 - 1; + // Number of days in 400 years + internal const int DaysPer400Years = DaysPer100Years * 4 + 1; + + // Number of days from 1/1/0001 to 1/1/10000 + internal const int DaysTo10000 = DaysPer400Years * 25 - 366; + + internal const long MaxMillis = (long)DaysTo10000 * MillisPerDay; + + private int _currentEraValue = -1; + + private bool _isReadOnly = false; + + // The minimum supported DateTime range for the calendar. + + public virtual DateTime MinSupportedDateTime + { + get + { + return (DateTime.MinValue); + } + } + + // The maximum supported DateTime range for the calendar. + + public virtual DateTime MaxSupportedDateTime + { + get + { + return (DateTime.MaxValue); + } + } + + public virtual CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.Unknown; + } + } + + protected Calendar() + { + //Do-nothing constructor. + } + + /// + // This can not be abstract, otherwise no one can create a subclass of Calendar. + // + internal virtual CalendarId ID + { + get + { + return CalendarId.UNINITIALIZED_VALUE; + } + } + + /// + // Return the Base calendar ID for calendars that didn't have defined data in calendarData + // + + internal virtual CalendarId BaseCalendarID + { + get { return ID; } + } + + //////////////////////////////////////////////////////////////////////// + // + // IsReadOnly + // + // Detect if the object is readonly. + // + //////////////////////////////////////////////////////////////////////// + public bool IsReadOnly + { + get { return (_isReadOnly); } + } + + //////////////////////////////////////////////////////////////////////// + // + // Clone + // + // Is the implementation of ICloneable. + // + //////////////////////////////////////////////////////////////////////// + public virtual object Clone() + { + object o = MemberwiseClone(); + ((Calendar)o).SetReadOnlyState(false); + return (o); + } + + //////////////////////////////////////////////////////////////////////// + // + // ReadOnly + // + // Create a cloned readonly instance or return the input one if it is + // readonly. + // + //////////////////////////////////////////////////////////////////////// + public static Calendar ReadOnly(Calendar calendar) + { + if (calendar == null) { throw new ArgumentNullException(nameof(calendar)); } + if (calendar.IsReadOnly) { return (calendar); } + + Calendar clonedCalendar = (Calendar)(calendar.MemberwiseClone()); + clonedCalendar.SetReadOnlyState(true); + + return (clonedCalendar); + } + + internal void VerifyWritable() + { + if (_isReadOnly) + { + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + } + } + + internal void SetReadOnlyState(bool readOnly) + { + _isReadOnly = readOnly; + } + + + /*=================================CurrentEraValue========================== + **Action: This is used to convert CurretEra(0) to an appropriate era value. + **Returns: + **Arguments: + **Exceptions: + **Notes: + ** The value is from calendar.nlp. + ============================================================================*/ + + internal virtual int CurrentEraValue + { + get + { + // The following code assumes that the current era value can not be -1. + if (_currentEraValue == -1) + { + Debug.Assert(BaseCalendarID != CalendarId.UNINITIALIZED_VALUE, "[Calendar.CurrentEraValue] Expected a real calendar ID"); + _currentEraValue = CalendarData.GetCalendarData(BaseCalendarID).iCurrentEra; + } + return (_currentEraValue); + } + } + + // The current era for a calendar. + + public const int CurrentEra = 0; + + internal int twoDigitYearMax = -1; + + internal static void CheckAddResult(long ticks, DateTime minValue, DateTime maxValue) + { + if (ticks < minValue.Ticks || ticks > maxValue.Ticks) + { + throw new ArgumentException( + String.Format(CultureInfo.InvariantCulture, SR.Format(SR.Argument_ResultCalendarRange, + minValue, maxValue))); + } + } + + internal DateTime Add(DateTime time, double value, int scale) + { + // From ECMA CLI spec, Partition III, section 3.27: + // + // If overflow occurs converting a floating-point type to an integer, or if the floating-point value + // being converted to an integer is a NaN, the value returned is unspecified. + // + // Based upon this, this method should be performing the comparison against the double + // before attempting a cast. Otherwise, the result is undefined. + double tempMillis = (value * scale + (value >= 0 ? 0.5 : -0.5)); + if (!((tempMillis > -(double)MaxMillis) && (tempMillis < (double)MaxMillis))) + { + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_AddValue); + } + + long millis = (long)tempMillis; + long ticks = time.Ticks + millis * TicksPerMillisecond; + CheckAddResult(ticks, MinSupportedDateTime, MaxSupportedDateTime); + return (new DateTime(ticks)); + } + + // Returns the DateTime resulting from adding the given number of + // milliseconds to the specified DateTime. The result is computed by rounding + // the number of milliseconds given by value to the nearest integer, + // and adding that interval to the specified DateTime. The value + // argument is permitted to be negative. + // + + public virtual DateTime AddMilliseconds(DateTime time, double milliseconds) + { + return (Add(time, milliseconds, 1)); + } + + + // Returns the DateTime resulting from adding a fractional number of + // days to the specified DateTime. The result is computed by rounding the + // fractional number of days given by value to the nearest + // millisecond, and adding that interval to the specified DateTime. The + // value argument is permitted to be negative. + // + + public virtual DateTime AddDays(DateTime time, int days) + { + return (Add(time, days, MillisPerDay)); + } + + // Returns the DateTime resulting from adding a fractional number of + // hours to the specified DateTime. The result is computed by rounding the + // fractional number of hours given by value to the nearest + // millisecond, and adding that interval to the specified DateTime. The + // value argument is permitted to be negative. + // + + public virtual DateTime AddHours(DateTime time, int hours) + { + return (Add(time, hours, MillisPerHour)); + } + + + // Returns the DateTime resulting from adding a fractional number of + // minutes to the specified DateTime. The result is computed by rounding the + // fractional number of minutes given by value to the nearest + // millisecond, and adding that interval to the specified DateTime. The + // value argument is permitted to be negative. + // + + public virtual DateTime AddMinutes(DateTime time, int minutes) + { + return (Add(time, minutes, MillisPerMinute)); + } + + + // Returns the DateTime resulting from adding the given number of + // months to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year and month parts of the specified DateTime by + // value months, and, if required, adjusting the day part of the + // resulting date downwards to the last day of the resulting month in the + // resulting year. The time-of-day part of the result is the same as the + // time-of-day part of the specified DateTime. + // + // In more precise terms, considering the specified DateTime to be of the + // form y / m / d + t, where y is the + // year, m is the month, d is the day, and t is the + // time-of-day, the result is y1 / m1 / d1 + t, + // where y1 and m1 are computed by adding value months + // to y and m, and d1 is the largest value less than + // or equal to d that denotes a valid day in month m1 of year + // y1. + // + + public abstract DateTime AddMonths(DateTime time, int months); + + // Returns the DateTime resulting from adding a number of + // seconds to the specified DateTime. The result is computed by rounding the + // fractional number of seconds given by value to the nearest + // millisecond, and adding that interval to the specified DateTime. The + // value argument is permitted to be negative. + // + + public virtual DateTime AddSeconds(DateTime time, int seconds) + { + return Add(time, seconds, MillisPerSecond); + } + + // Returns the DateTime resulting from adding a number of + // weeks to the specified DateTime. The + // value argument is permitted to be negative. + // + + public virtual DateTime AddWeeks(DateTime time, int weeks) + { + return (AddDays(time, weeks * 7)); + } + + + // Returns the DateTime resulting from adding the given number of + // years to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year part of the specified DateTime by value + // years. If the month and day of the specified DateTime is 2/29, and if the + // resulting year is not a leap year, the month and day of the resulting + // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day + // parts of the result are the same as those of the specified DateTime. + // + + public abstract DateTime AddYears(DateTime time, int years); + + // Returns the day-of-month part of the specified DateTime. The returned + // value is an integer between 1 and 31. + // + + public abstract int GetDayOfMonth(DateTime time); + + // Returns the day-of-week part of the specified DateTime. The returned value + // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates + // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates + // Thursday, 5 indicates Friday, and 6 indicates Saturday. + // + + public abstract DayOfWeek GetDayOfWeek(DateTime time); + + // Returns the day-of-year part of the specified DateTime. The returned value + // is an integer between 1 and 366. + // + + public abstract int GetDayOfYear(DateTime time); + + // Returns the number of days in the month given by the year and + // month arguments. + // + + public virtual int GetDaysInMonth(int year, int month) + { + return (GetDaysInMonth(year, month, CurrentEra)); + } + + // Returns the number of days in the month given by the year and + // month arguments for the specified era. + // + + public abstract int GetDaysInMonth(int year, int month, int era); + + // Returns the number of days in the year given by the year argument for the current era. + // + + public virtual int GetDaysInYear(int year) + { + return (GetDaysInYear(year, CurrentEra)); + } + + // Returns the number of days in the year given by the year argument for the current era. + // + + public abstract int GetDaysInYear(int year, int era); + + // Returns the era for the specified DateTime value. + + public abstract int GetEra(DateTime time); + + /*=================================Eras========================== + **Action: Get the list of era values. + **Returns: The int array of the era names supported in this calendar. + ** null if era is not used. + **Arguments: None. + **Exceptions: None. + ============================================================================*/ + + + public abstract int[] Eras + { + get; + } + + + // Returns the hour part of the specified DateTime. The returned value is an + // integer between 0 and 23. + // + + public virtual int GetHour(DateTime time) + { + return ((int)((time.Ticks / TicksPerHour) % 24)); + } + + // Returns the millisecond part of the specified DateTime. The returned value + // is an integer between 0 and 999. + // + + public virtual double GetMilliseconds(DateTime time) + { + return (double)((time.Ticks / TicksPerMillisecond) % 1000); + } + + // Returns the minute part of the specified DateTime. The returned value is + // an integer between 0 and 59. + // + + public virtual int GetMinute(DateTime time) + { + return ((int)((time.Ticks / TicksPerMinute) % 60)); + } + + // Returns the month part of the specified DateTime. The returned value is an + // integer between 1 and 12. + // + + public abstract int GetMonth(DateTime time); + + // Returns the number of months in the specified year in the current era. + + public virtual int GetMonthsInYear(int year) + { + return (GetMonthsInYear(year, CurrentEra)); + } + + // Returns the number of months in the specified year and era. + + public abstract int GetMonthsInYear(int year, int era); + + // Returns the second part of the specified DateTime. The returned value is + // an integer between 0 and 59. + // + + public virtual int GetSecond(DateTime time) + { + return ((int)((time.Ticks / TicksPerSecond) % 60)); + } + + /*=================================GetFirstDayWeekOfYear========================== + **Action: Get the week of year using the FirstDay rule. + **Returns: the week of year. + **Arguments: + ** time + ** firstDayOfWeek the first day of week (0=Sunday, 1=Monday, ... 6=Saturday) + **Notes: + ** The CalendarWeekRule.FirstDay rule: Week 1 begins on the first day of the year. + ** Assume f is the specifed firstDayOfWeek, + ** and n is the day of week for January 1 of the specified year. + ** Assign offset = n - f; + ** Case 1: offset = 0 + ** E.g. + ** f=1 + ** weekday 0 1 2 3 4 5 6 0 1 + ** date 1/1 + ** week# 1 2 + ** then week of year = (GetDayOfYear(time) - 1) / 7 + 1 + ** + ** Case 2: offset < 0 + ** e.g. + ** n=1 f=3 + ** weekday 0 1 2 3 4 5 6 0 + ** date 1/1 + ** week# 1 2 + ** This means that the first week actually starts 5 days before 1/1. + ** So week of year = (GetDayOfYear(time) + (7 + offset) - 1) / 7 + 1 + ** Case 3: offset > 0 + ** e.g. + ** f=0 n=2 + ** weekday 0 1 2 3 4 5 6 0 1 2 + ** date 1/1 + ** week# 1 2 + ** This means that the first week actually starts 2 days before 1/1. + ** So Week of year = (GetDayOfYear(time) + offset - 1) / 7 + 1 + ============================================================================*/ + + internal int GetFirstDayWeekOfYear(DateTime time, int firstDayOfWeek) + { + int dayOfYear = GetDayOfYear(time) - 1; // Make the day of year to be 0-based, so that 1/1 is day 0. + // Calculate the day of week for the first day of the year. + // dayOfWeek - (dayOfYear % 7) is the day of week for the first day of this year. Note that + // this value can be less than 0. It's fine since we are making it positive again in calculating offset. + int dayForJan1 = (int)GetDayOfWeek(time) - (dayOfYear % 7); + int offset = (dayForJan1 - firstDayOfWeek + 14) % 7; + Debug.Assert(offset >= 0, "Calendar.GetFirstDayWeekOfYear(): offset >= 0"); + return ((dayOfYear + offset) / 7 + 1); + } + + private int GetWeekOfYearFullDays(DateTime time, int firstDayOfWeek, int fullDays) + { + int dayForJan1; + int offset; + int day; + + int dayOfYear = GetDayOfYear(time) - 1; // Make the day of year to be 0-based, so that 1/1 is day 0. + // + // Calculate the number of days between the first day of year (1/1) and the first day of the week. + // This value will be a positive value from 0 ~ 6. We call this value as "offset". + // + // If offset is 0, it means that the 1/1 is the start of the first week. + // Assume the first day of the week is Monday, it will look like this: + // Sun Mon Tue Wed Thu Fri Sat + // 12/31 1/1 1/2 1/3 1/4 1/5 1/6 + // +--> First week starts here. + // + // If offset is 1, it means that the first day of the week is 1 day ahead of 1/1. + // Assume the first day of the week is Monday, it will look like this: + // Sun Mon Tue Wed Thu Fri Sat + // 1/1 1/2 1/3 1/4 1/5 1/6 1/7 + // +--> First week starts here. + // + // If offset is 2, it means that the first day of the week is 2 days ahead of 1/1. + // Assume the first day of the week is Monday, it will look like this: + // Sat Sun Mon Tue Wed Thu Fri Sat + // 1/1 1/2 1/3 1/4 1/5 1/6 1/7 1/8 + // +--> First week starts here. + + + + // Day of week is 0-based. + // Get the day of week for 1/1. This can be derived from the day of week of the target day. + // Note that we can get a negative value. It's ok since we are going to make it a positive value when calculating the offset. + dayForJan1 = (int)GetDayOfWeek(time) - (dayOfYear % 7); + + // Now, calculate the offset. Subtract the first day of week from the dayForJan1. And make it a positive value. + offset = (firstDayOfWeek - dayForJan1 + 14) % 7; + if (offset != 0 && offset >= fullDays) + { + // + // If the offset is greater than the value of fullDays, it means that + // the first week of the year starts on the week where Jan/1 falls on. + // + offset -= 7; + } + // + // Calculate the day of year for specified time by taking offset into account. + // + day = dayOfYear - offset; + if (day >= 0) + { + // + // If the day of year value is greater than zero, get the week of year. + // + return (day / 7 + 1); + } + // + // Otherwise, the specified time falls on the week of previous year. + // Call this method again by passing the last day of previous year. + // + // the last day of the previous year may "underflow" to no longer be a valid date time for + // this calendar if we just subtract so we need the subclass to provide us with + // that information + if (time <= MinSupportedDateTime.AddDays(dayOfYear)) + { + return GetWeekOfYearOfMinSupportedDateTime(firstDayOfWeek, fullDays); + } + return (GetWeekOfYearFullDays(time.AddDays(-(dayOfYear + 1)), firstDayOfWeek, fullDays)); + } + + private int GetWeekOfYearOfMinSupportedDateTime(int firstDayOfWeek, int minimumDaysInFirstWeek) + { + int dayOfYear = GetDayOfYear(MinSupportedDateTime) - 1; // Make the day of year to be 0-based, so that 1/1 is day 0. + int dayOfWeekOfFirstOfYear = (int)GetDayOfWeek(MinSupportedDateTime) - dayOfYear % 7; + + // Calculate the offset (how many days from the start of the year to the start of the week) + int offset = (firstDayOfWeek + 7 - dayOfWeekOfFirstOfYear) % 7; + if (offset == 0 || offset >= minimumDaysInFirstWeek) + { + // First of year falls in the first week of the year + return 1; + } + + int daysInYearBeforeMinSupportedYear = DaysInYearBeforeMinSupportedYear - 1; // Make the day of year to be 0-based, so that 1/1 is day 0. + int dayOfWeekOfFirstOfPreviousYear = dayOfWeekOfFirstOfYear - 1 - (daysInYearBeforeMinSupportedYear % 7); + + // starting from first day of the year, how many days do you have to go forward + // before getting to the first day of the week? + int daysInInitialPartialWeek = (firstDayOfWeek - dayOfWeekOfFirstOfPreviousYear + 14) % 7; + int day = daysInYearBeforeMinSupportedYear - daysInInitialPartialWeek; + if (daysInInitialPartialWeek >= minimumDaysInFirstWeek) + { + // If the offset is greater than the minimum Days in the first week, it means that + // First of year is part of the first week of the year even though it is only a partial week + // add another week + day += 7; + } + + return (day / 7 + 1); + } + + // it would be nice to make this abstract but we can't since that would break previous implementations + protected virtual int DaysInYearBeforeMinSupportedYear + { + get + { + return 365; + } + } + + + // Returns the week of year for the specified DateTime. The returned value is an + // integer between 1 and 53. + // + + public virtual int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek) + { + if ((int)firstDayOfWeek < 0 || (int)firstDayOfWeek > 6) + { + throw new ArgumentOutOfRangeException( + nameof(firstDayOfWeek), SR.Format(SR.ArgumentOutOfRange_Range, + DayOfWeek.Sunday, DayOfWeek.Saturday)); + } + switch (rule) + { + case CalendarWeekRule.FirstDay: + return (GetFirstDayWeekOfYear(time, (int)firstDayOfWeek)); + case CalendarWeekRule.FirstFullWeek: + return (GetWeekOfYearFullDays(time, (int)firstDayOfWeek, 7)); + case CalendarWeekRule.FirstFourDayWeek: + return (GetWeekOfYearFullDays(time, (int)firstDayOfWeek, 4)); + } + throw new ArgumentOutOfRangeException( + nameof(rule), SR.Format(SR.ArgumentOutOfRange_Range, + CalendarWeekRule.FirstDay, CalendarWeekRule.FirstFourDayWeek)); + } + + // Returns the year part of the specified DateTime. The returned value is an + // integer between 1 and 9999. + // + + public abstract int GetYear(DateTime time); + + // Checks whether a given day in the current era is a leap day. This method returns true if + // the date is a leap day, or false if not. + // + + public virtual bool IsLeapDay(int year, int month, int day) + { + return (IsLeapDay(year, month, day, CurrentEra)); + } + + // Checks whether a given day in the specified era is a leap day. This method returns true if + // the date is a leap day, or false if not. + // + + public abstract bool IsLeapDay(int year, int month, int day, int era); + + // Checks whether a given month in the current era is a leap month. This method returns true if + // month is a leap month, or false if not. + // + + public virtual bool IsLeapMonth(int year, int month) + { + return (IsLeapMonth(year, month, CurrentEra)); + } + + // Checks whether a given month in the specified era is a leap month. This method returns true if + // month is a leap month, or false if not. + // + + public abstract bool IsLeapMonth(int year, int month, int era); + + // Returns the leap month in a calendar year of the current era. This method returns 0 + // if this calendar does not have leap month, or this year is not a leap year. + // + + public virtual int GetLeapMonth(int year) + { + return (GetLeapMonth(year, CurrentEra)); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this calendar does not have leap month, or this year is not a leap year. + // + + public virtual int GetLeapMonth(int year, int era) + { + if (!IsLeapYear(year, era)) + return 0; + + int monthsCount = GetMonthsInYear(year, era); + for (int month = 1; month <= monthsCount; month++) + { + if (IsLeapMonth(year, month, era)) + return month; + } + + return 0; + } + + // Checks whether a given year in the current era is a leap year. This method returns true if + // year is a leap year, or false if not. + // + + public virtual bool IsLeapYear(int year) + { + return (IsLeapYear(year, CurrentEra)); + } + + // Checks whether a given year in the specified era is a leap year. This method returns true if + // year is a leap year, or false if not. + // + + public abstract bool IsLeapYear(int year, int era); + + // Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid. + // + + public virtual DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond) + { + return (ToDateTime(year, month, day, hour, minute, second, millisecond, CurrentEra)); + } + + // Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid. + // + + public abstract DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era); + + internal virtual Boolean TryToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era, out DateTime result) + { + result = DateTime.MinValue; + try + { + result = ToDateTime(year, month, day, hour, minute, second, millisecond, era); + return true; + } + catch (ArgumentException) + { + return false; + } + } + + internal virtual bool IsValidYear(int year, int era) + { + return (year >= GetYear(MinSupportedDateTime) && year <= GetYear(MaxSupportedDateTime)); + } + + internal virtual bool IsValidMonth(int year, int month, int era) + { + return (IsValidYear(year, era) && month >= 1 && month <= GetMonthsInYear(year, era)); + } + + internal virtual bool IsValidDay(int year, int month, int day, int era) + { + return (IsValidMonth(year, month, era) && day >= 1 && day <= GetDaysInMonth(year, month, era)); + } + + + // Returns and assigns the maximum value to represent a two digit year. This + // value is the upper boundary of a 100 year range that allows a two digit year + // to be properly translated to a four digit year. For example, if 2029 is the + // upper boundary, then a two digit value of 30 should be interpreted as 1930 + // while a two digit value of 29 should be interpreted as 2029. In this example + // , the 100 year range would be from 1930-2029. See ToFourDigitYear(). + + public virtual int TwoDigitYearMax + { + get + { + return (twoDigitYearMax); + } + + set + { + VerifyWritable(); + twoDigitYearMax = value; + } + } + + // Converts the year value to the appropriate century by using the + // TwoDigitYearMax property. For example, if the TwoDigitYearMax value is 2029, + // then a two digit value of 30 will get converted to 1930 while a two digit + // value of 29 will get converted to 2029. + + public virtual int ToFourDigitYear(int year) + { + if (year < 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (year < 100) + { + return ((TwoDigitYearMax / 100 - (year > TwoDigitYearMax % 100 ? 1 : 0)) * 100 + year); + } + // If the year value is above 100, just return the year value. Don't have to do + // the TwoDigitYearMax comparison. + return (year); + } + + // Return the tick count corresponding to the given hour, minute, second. + // Will check the if the parameters are valid. + internal static long TimeToTicks(int hour, int minute, int second, int millisecond) + { + if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >= 0 && second < 60) + { + if (millisecond < 0 || millisecond >= MillisPerSecond) + { + throw new ArgumentOutOfRangeException( + nameof(millisecond), + String.Format( + CultureInfo.InvariantCulture, + SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1))); + } + return InternalGlobalizationHelper.TimeToTicks(hour, minute, second) + millisecond * TicksPerMillisecond; + } + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadHourMinuteSecond); + } + + internal static int GetSystemTwoDigitYearSetting(CalendarId CalID, int defaultYearValue) + { + int twoDigitYearMax = CalendarData.GetTwoDigitYearMax(CalID); + if (twoDigitYearMax < 0) + { + twoDigitYearMax = defaultYearValue; + } + return (twoDigitYearMax); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CalendarData.Unix.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CalendarData.Unix.cs index a2ceeb1e67..3a8029d9a0 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CalendarData.Unix.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CalendarData.Unix.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.InteropServices; using System.Security; using System.Text; @@ -326,7 +325,7 @@ namespace System.Globalization } catch (Exception e) { - Debug.Assert(false, e.ToString()); + Debug.Fail(e.ToString()); // we ignore the managed exceptions here because EnumCalendarInfoCallback will get called from the native code. // If we don't ignore the exception here that can cause the runtime to fail fast. } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CalendarData.Windows.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CalendarData.Windows.cs similarity index 96% rename from external/corert/src/System.Private.CoreLib/src/System/Globalization/CalendarData.Windows.cs rename to external/corert/src/System.Private.CoreLib/shared/System/Globalization/CalendarData.Windows.cs index 50e93841ff..a67761ac6a 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CalendarData.Windows.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CalendarData.Windows.cs @@ -3,10 +3,11 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; -using System.Diagnostics.Contracts; using System.Collections.Generic; +using Internal.Runtime.CompilerServices; namespace System.Globalization { @@ -14,6 +15,8 @@ namespace System.Globalization { private bool LoadCalendarDataFromSystem(String localeName, CalendarId calendarId) { + Debug.Assert(!GlobalizationMode.Invariant); + bool ret = true; uint useOverrides = this.bUseUserOverrides ? 0 : CAL_NOUSEROVERRIDE; @@ -108,6 +111,11 @@ namespace System.Globalization // Get native two digit year max internal static int GetTwoDigitYearMax(CalendarId calendarId) { + if (GlobalizationMode.Invariant) + { + return Invariant.iTwoDigitYearMax; + } + int twoDigitYearMax = -1; if (!CallGetCalendarInfoEx(null, calendarId, (uint)CAL_ITWODIGITYEARMAX, out twoDigitYearMax)) @@ -121,9 +129,11 @@ namespace System.Globalization // Call native side to figure out which calendars are allowed internal static int GetCalendars(String localeName, bool useUserOverride, CalendarId[] calendars) { + Debug.Assert(!GlobalizationMode.Invariant); + EnumCalendarsData data = new EnumCalendarsData(); data.userOverride = 0; - data.calendars = new LowLevelList(); + data.calendars = new List(); // First call GetLocaleInfo if necessary if (useUserOverride) @@ -154,6 +164,8 @@ namespace System.Globalization private static bool SystemSupportsTaiwaneseCalendar() { + Debug.Assert(!GlobalizationMode.Invariant); + string data; // Taiwanese calendar get listed as one of the optional zh-TW calendars only when having zh-TW UI return CallGetCalendarInfoEx("zh-TW", CalendarId.TAIWAN, CAL_SCALNAME, out data); @@ -265,7 +277,7 @@ namespace System.Globalization private class EnumData { public string userOverride; - public LowLevelList strings; + public List strings; } // EnumCalendarInfoExEx callback itself. @@ -293,8 +305,7 @@ namespace System.Globalization { EnumData context = new EnumData(); context.userOverride = null; - context.strings = new LowLevelList(); - + context.strings = new List(); // First call GetLocaleInfo if necessary if (((lcType != 0) && ((lcType & CAL_NOUSEROVERRIDE) == 0)) && // Get user locale, see if it matches localeName. @@ -419,7 +430,7 @@ namespace System.Globalization private class EnumCalendarsData { public int userOverride; // user override value (if found) - public LowLevelList calendars; // list of calendars found so far + public List calendars; // list of calendars found so far } // [NativeCallable(CallingConvention = CallingConvention.StdCall)] @@ -442,6 +453,8 @@ namespace System.Globalization private static unsafe String GetUserDefaultLocaleName() { + Debug.Assert(!GlobalizationMode.Invariant); + const int LOCALE_NAME_MAX_LENGTH = 85; const uint LOCALE_SNAME = 0x0000005c; const string LOCALE_NAME_USER_DEFAULT = null; diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CalendarData.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CalendarData.cs new file mode 100644 index 0000000000..ea70a1ce9a --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CalendarData.cs @@ -0,0 +1,378 @@ +// 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.Diagnostics; + +namespace System.Globalization +{ + // List of calendar data + // Note the we cache overrides. + // Note that localized names (resource names) aren't available from here. + // + // NOTE: Calendars depend on the locale name that creates it. Only a few + // properties are available without locales using CalendarData.GetCalendar(CalendarData) + + internal partial class CalendarData + { + // Max calendars + internal const int MAX_CALENDARS = 23; + + // Identity + internal String sNativeName; // Calendar Name for the locale + + // Formats + internal String[] saShortDates; // Short Data format, default first + internal String[] saYearMonths; // Year/Month Data format, default first + internal String[] saLongDates; // Long Data format, default first + internal String sMonthDay; // Month/Day format + + // Calendar Parts Names + internal String[] saEraNames; // Names of Eras + internal String[] saAbbrevEraNames; // Abbreviated Era Names + internal String[] saAbbrevEnglishEraNames; // Abbreviated Era Names in English + internal String[] saDayNames; // Day Names, null to use locale data, starts on Sunday + internal String[] saAbbrevDayNames; // Abbrev Day Names, null to use locale data, starts on Sunday + internal String[] saSuperShortDayNames; // Super short Day of week names + internal String[] saMonthNames; // Month Names (13) + internal String[] saAbbrevMonthNames; // Abbrev Month Names (13) + internal String[] saMonthGenitiveNames; // Genitive Month Names (13) + internal String[] saAbbrevMonthGenitiveNames; // Genitive Abbrev Month Names (13) + internal String[] saLeapYearMonthNames; // Multiple strings for the month names in a leap year. + + // Integers at end to make marshaller happier + internal int iTwoDigitYearMax = 2029; // Max 2 digit year (for Y2K bug data entry) + internal int iCurrentEra = 0; // current era # (usually 1) + + // Use overrides? + internal bool bUseUserOverrides; // True if we want user overrides. + + // Static invariant for the invariant locale + internal static readonly CalendarData Invariant = CreateInvariant(); + + // Private constructor + private CalendarData() { } + + // Invariant factory + private static CalendarData CreateInvariant() + { + // Set our default/gregorian US calendar data + // Calendar IDs are 1-based, arrays are 0 based. + CalendarData invariant = new CalendarData(); + + // Set default data for calendar + // Note that we don't load resources since this IS NOT supposed to change (by definition) + invariant.sNativeName = "Gregorian Calendar"; // Calendar Name + + // Year + invariant.iTwoDigitYearMax = 2029; // Max 2 digit year (for Y2K bug data entry) + invariant.iCurrentEra = 1; // Current era # + + // Formats + invariant.saShortDates = new String[] { "MM/dd/yyyy", "yyyy-MM-dd" }; // short date format + invariant.saLongDates = new String[] { "dddd, dd MMMM yyyy" }; // long date format + invariant.saYearMonths = new String[] { "yyyy MMMM" }; // year month format + invariant.sMonthDay = "MMMM dd"; // Month day pattern + + // Calendar Parts Names + invariant.saEraNames = new String[] { "A.D." }; // Era names + invariant.saAbbrevEraNames = new String[] { "AD" }; // Abbreviated Era names + invariant.saAbbrevEnglishEraNames = new String[] { "AD" }; // Abbreviated era names in English + invariant.saDayNames = new String[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };// day names + invariant.saAbbrevDayNames = new String[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; // abbreviated day names + invariant.saSuperShortDayNames = new String[] { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" }; // The super short day names + invariant.saMonthNames = new String[] { "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December", String.Empty}; // month names + invariant.saAbbrevMonthNames = new String[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", String.Empty}; // abbreviated month names + invariant.saMonthGenitiveNames = invariant.saMonthNames; // Genitive month names (same as month names for invariant) + invariant.saAbbrevMonthGenitiveNames = invariant.saAbbrevMonthNames; // Abbreviated genitive month names (same as abbrev month names for invariant) + invariant.saLeapYearMonthNames = invariant.saMonthNames; // leap year month names are unused in Gregorian English (invariant) + + invariant.bUseUserOverrides = false; + + return invariant; + } + + // + // Get a bunch of data for a calendar + // + internal CalendarData(String localeName, CalendarId calendarId, bool bUseUserOverrides) + { + this.bUseUserOverrides = bUseUserOverrides; + + Debug.Assert(!GlobalizationMode.Invariant); + + if (!LoadCalendarDataFromSystem(localeName, calendarId)) + { + Debug.Fail("[CalendarData] LoadCalendarDataFromSystem call isn't expected to fail for calendar " + calendarId + " locale " + localeName); + + // Something failed, try invariant for missing parts + // This is really not good, but we don't want the callers to crash. + if (this.sNativeName == null) this.sNativeName = String.Empty; // Calendar Name for the locale. + + // Formats + if (this.saShortDates == null) this.saShortDates = Invariant.saShortDates; // Short Data format, default first + if (this.saYearMonths == null) this.saYearMonths = Invariant.saYearMonths; // Year/Month Data format, default first + if (this.saLongDates == null) this.saLongDates = Invariant.saLongDates; // Long Data format, default first + if (this.sMonthDay == null) this.sMonthDay = Invariant.sMonthDay; // Month/Day format + + // Calendar Parts Names + if (this.saEraNames == null) this.saEraNames = Invariant.saEraNames; // Names of Eras + if (this.saAbbrevEraNames == null) this.saAbbrevEraNames = Invariant.saAbbrevEraNames; // Abbreviated Era Names + if (this.saAbbrevEnglishEraNames == null) this.saAbbrevEnglishEraNames = Invariant.saAbbrevEnglishEraNames; // Abbreviated Era Names in English + if (this.saDayNames == null) this.saDayNames = Invariant.saDayNames; // Day Names, null to use locale data, starts on Sunday + if (this.saAbbrevDayNames == null) this.saAbbrevDayNames = Invariant.saAbbrevDayNames; // Abbrev Day Names, null to use locale data, starts on Sunday + if (this.saSuperShortDayNames == null) this.saSuperShortDayNames = Invariant.saSuperShortDayNames; // Super short Day of week names + if (this.saMonthNames == null) this.saMonthNames = Invariant.saMonthNames; // Month Names (13) + if (this.saAbbrevMonthNames == null) this.saAbbrevMonthNames = Invariant.saAbbrevMonthNames; // Abbrev Month Names (13) + // Genitive and Leap names can follow the fallback below + } + + if (calendarId == CalendarId.TAIWAN) + { + if (SystemSupportsTaiwaneseCalendar()) + { + // We got the month/day names from the OS (same as gregorian), but the native name is wrong + this.sNativeName = "\x4e2d\x83ef\x6c11\x570b\x66c6"; + } + else + { + this.sNativeName = String.Empty; + } + } + + // Check for null genitive names (in case unmanaged side skips it for non-gregorian calendars, etc) + if (this.saMonthGenitiveNames == null || this.saMonthGenitiveNames.Length == 0 || String.IsNullOrEmpty(this.saMonthGenitiveNames[0])) + this.saMonthGenitiveNames = this.saMonthNames; // Genitive month names (same as month names for invariant) + if (this.saAbbrevMonthGenitiveNames == null || this.saAbbrevMonthGenitiveNames.Length == 0 || String.IsNullOrEmpty(this.saAbbrevMonthGenitiveNames[0])) + this.saAbbrevMonthGenitiveNames = this.saAbbrevMonthNames; // Abbreviated genitive month names (same as abbrev month names for invariant) + if (this.saLeapYearMonthNames == null || this.saLeapYearMonthNames.Length == 0 || String.IsNullOrEmpty(this.saLeapYearMonthNames[0])) + this.saLeapYearMonthNames = this.saMonthNames; + + InitializeEraNames(localeName, calendarId); + + InitializeAbbreviatedEraNames(localeName, calendarId); + + // Abbreviated English Era Names are only used for the Japanese calendar. + if (calendarId == CalendarId.JAPAN) + { + this.saAbbrevEnglishEraNames = JapaneseCalendar.EnglishEraNames(); + } + else + { + // For all others just use the an empty string (doesn't matter we'll never ask for it for other calendars) + this.saAbbrevEnglishEraNames = new String[] { "" }; + } + + // Japanese is the only thing with > 1 era. Its current era # is how many ever + // eras are in the array. (And the others all have 1 string in the array) + this.iCurrentEra = this.saEraNames.Length; + } + + private void InitializeEraNames(string localeName, CalendarId calendarId) + { + // Note that the saEraNames only include "A.D." We don't have localized names for other calendars available from windows + switch (calendarId) + { + // For Localized Gregorian we really expect the data from the OS. + case CalendarId.GREGORIAN: + // Fallback for CoreCLR < Win7 or culture.dll missing + if (this.saEraNames == null || this.saEraNames.Length == 0 || String.IsNullOrEmpty(this.saEraNames[0])) + { + this.saEraNames = new String[] { "A.D." }; + } + break; + + // The rest of the calendars have constant data, so we'll just use that + case CalendarId.GREGORIAN_US: + case CalendarId.JULIAN: + this.saEraNames = new String[] { "A.D." }; + break; + case CalendarId.HEBREW: + this.saEraNames = new String[] { "C.E." }; + break; + case CalendarId.HIJRI: + case CalendarId.UMALQURA: + if (localeName == "dv-MV") + { + // Special case for Divehi + this.saEraNames = new String[] { "\x0780\x07a8\x0796\x07b0\x0783\x07a9" }; + } + else + { + this.saEraNames = new String[] { "\x0628\x0639\x062F \x0627\x0644\x0647\x062C\x0631\x0629" }; + } + break; + case CalendarId.GREGORIAN_ARABIC: + case CalendarId.GREGORIAN_XLIT_ENGLISH: + case CalendarId.GREGORIAN_XLIT_FRENCH: + // These are all the same: + this.saEraNames = new String[] { "\x0645" }; + break; + + case CalendarId.GREGORIAN_ME_FRENCH: + this.saEraNames = new String[] { "ap. J.-C." }; + break; + + case CalendarId.TAIWAN: + if (SystemSupportsTaiwaneseCalendar()) + { + this.saEraNames = new String[] { "\x4e2d\x83ef\x6c11\x570b" }; + } + else + { + this.saEraNames = new String[] { String.Empty }; + } + break; + + case CalendarId.KOREA: + this.saEraNames = new String[] { "\xb2e8\xae30" }; + break; + + case CalendarId.THAI: + this.saEraNames = new String[] { "\x0e1e\x002e\x0e28\x002e" }; + break; + + case CalendarId.JAPAN: + case CalendarId.JAPANESELUNISOLAR: + this.saEraNames = JapaneseCalendar.EraNames(); + break; + + case CalendarId.PERSIAN: + if (this.saEraNames == null || this.saEraNames.Length == 0 || String.IsNullOrEmpty(this.saEraNames[0])) + { + this.saEraNames = new String[] { "\x0647\x002e\x0634" }; + } + break; + + default: + // Most calendars are just "A.D." + this.saEraNames = Invariant.saEraNames; + break; + } + } + + private void InitializeAbbreviatedEraNames(string localeName, CalendarId calendarId) + { + // Note that the saAbbrevEraNames only include "AD" We don't have localized names for other calendars available from windows + switch (calendarId) + { + // For Localized Gregorian we really expect the data from the OS. + case CalendarId.GREGORIAN: + // Fallback for CoreCLR < Win7 or culture.dll missing + if (this.saAbbrevEraNames == null || this.saAbbrevEraNames.Length == 0 || String.IsNullOrEmpty(this.saAbbrevEraNames[0])) + { + this.saAbbrevEraNames = new String[] { "AD" }; + } + break; + + // The rest of the calendars have constant data, so we'll just use that + case CalendarId.GREGORIAN_US: + case CalendarId.JULIAN: + this.saAbbrevEraNames = new String[] { "AD" }; + break; + case CalendarId.JAPAN: + case CalendarId.JAPANESELUNISOLAR: + this.saAbbrevEraNames = JapaneseCalendar.AbbrevEraNames(); + break; + case CalendarId.HIJRI: + case CalendarId.UMALQURA: + if (localeName == "dv-MV") + { + // Special case for Divehi + this.saAbbrevEraNames = new String[] { "\x0780\x002e" }; + } + else + { + this.saAbbrevEraNames = new String[] { "\x0647\x0640" }; + } + break; + case CalendarId.TAIWAN: + // Get era name and abbreviate it + this.saAbbrevEraNames = new String[1]; + if (this.saEraNames[0].Length == 4) + { + this.saAbbrevEraNames[0] = this.saEraNames[0].Substring(2, 2); + } + else + { + this.saAbbrevEraNames[0] = this.saEraNames[0]; + } + break; + + case CalendarId.PERSIAN: + if (this.saAbbrevEraNames == null || this.saAbbrevEraNames.Length == 0 || String.IsNullOrEmpty(this.saAbbrevEraNames[0])) + { + this.saAbbrevEraNames = this.saEraNames; + } + break; + + default: + // Most calendars just use the full name + this.saAbbrevEraNames = this.saEraNames; + break; + } + } + + internal static CalendarData GetCalendarData(CalendarId calendarId) + { + // + // Get a calendar. + // Unfortunately we depend on the locale in the OS, so we need a locale + // no matter what. So just get the appropriate calendar from the + // appropriate locale here + // + + // Get a culture name + // TODO: Note that this doesn't handle the new calendars (lunisolar, etc) + String culture = CalendarIdToCultureName(calendarId); + + // Return our calendar + return CultureInfo.GetCultureInfo(culture)._cultureData.GetCalendar(calendarId); + } + + private static String CalendarIdToCultureName(CalendarId calendarId) + { + switch (calendarId) + { + case CalendarId.GREGORIAN_US: + return "fa-IR"; // "fa-IR" Iran + + case CalendarId.JAPAN: + return "ja-JP"; // "ja-JP" Japan + + case CalendarId.TAIWAN: + return "zh-TW"; // zh-TW Taiwan + + case CalendarId.KOREA: + return "ko-KR"; // "ko-KR" Korea + + case CalendarId.HIJRI: + case CalendarId.GREGORIAN_ARABIC: + case CalendarId.UMALQURA: + return "ar-SA"; // "ar-SA" Saudi Arabia + + case CalendarId.THAI: + return "th-TH"; // "th-TH" Thailand + + case CalendarId.HEBREW: + return "he-IL"; // "he-IL" Israel + + case CalendarId.GREGORIAN_ME_FRENCH: + return "ar-DZ"; // "ar-DZ" Algeria + + case CalendarId.GREGORIAN_XLIT_ENGLISH: + case CalendarId.GREGORIAN_XLIT_FRENCH: + return "ar-IQ"; // "ar-IQ"; Iraq + + default: + // Default to gregorian en-US + break; + } + + return "en-US"; + } + } +} + diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CalendricalCalculationsHelper.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CalendricalCalculationsHelper.cs index 7de75d6aee..e0a3072b22 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CalendricalCalculationsHelper.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CalendricalCalculationsHelper.cs @@ -215,7 +215,7 @@ namespace System.Globalization } } - Debug.Assert(false, "Not expected to come here"); + Debug.Fail("Not expected to come here"); return DefaultEphemerisCorrection(year); } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CharUnicodeInfo.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CharUnicodeInfo.cs similarity index 96% rename from external/corert/src/System.Private.CoreLib/src/System/Globalization/CharUnicodeInfo.cs rename to external/corert/src/System.Private.CoreLib/shared/System/Globalization/CharUnicodeInfo.cs index f641b35154..3881272287 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CharUnicodeInfo.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CharUnicodeInfo.cs @@ -13,7 +13,6 @@ //////////////////////////////////////////////////////////////////////////// using System.Diagnostics; -using System.Diagnostics.Contracts; namespace System.Globalization { @@ -34,8 +33,6 @@ namespace System.Globalization internal const int UNICODE_CATEGORY_OFFSET = 0; internal const int BIDI_CATEGORY_OFFSET = 1; - - // The starting codepoint for Unicode plane 1. Plane 1 contains 0x010000 ~ 0x01ffff. internal const int UNICODE_PLANE01_START = 0x10000; @@ -178,7 +175,7 @@ namespace System.Globalization fixed (ushort* pUshortPtr = &(s_pNumericLevel1Index[index])) { byte* pBytePtr = (byte*)pUshortPtr; - fixed (byte* pByteNum = &s_pNumericValues[0]) + fixed (byte* pByteNum = s_pNumericValues) { double* pDouble = (double*)pByteNum; return pDouble[pBytePtr[(ch & 0x000f)]]; @@ -234,7 +231,6 @@ namespace System.Globalization { throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); } - Contract.EndContractBlock(); return (InternalGetNumericValue(InternalConvertToUtf32(s, index))); } @@ -254,7 +250,6 @@ namespace System.Globalization { throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); } - Contract.EndContractBlock(); return (sbyte)(InternalGetDigitValues(InternalConvertToUtf32(s, index)) >> 8); } @@ -276,7 +271,6 @@ namespace System.Globalization throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); } - Contract.EndContractBlock(); return (sbyte)(InternalGetDigitValues(InternalConvertToUtf32(s, index)) & 0x00FF); } @@ -293,7 +287,6 @@ namespace System.Globalization { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); return InternalGetUnicodeCategory(s, index); } @@ -362,6 +355,19 @@ namespace System.Globalization return (InternalGetUnicodeCategory(InternalConvertToUtf32(value, index))); } + internal static BidiCategory GetBidiCategory(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + return ((BidiCategory) InternalGetCategoryValue(InternalConvertToUtf32(s, index), BIDI_CATEGORY_OFFSET)); + } + //////////////////////////////////////////////////////////////////////// // // Get the Unicode category of the character starting at index. If the character is in BMP, charLength will return 1. diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/ChineseLunisolarCalendar.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/ChineseLunisolarCalendar.cs index e09011a9d8..d2b52b97be 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/ChineseLunisolarCalendar.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/ChineseLunisolarCalendar.cs @@ -2,7 +2,6 @@ // 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.Diagnostics.Contracts; namespace System.Globalization { @@ -320,7 +319,6 @@ namespace System.Globalization CultureInfo.CurrentCulture, SR.ArgumentOutOfRange_Range, MIN_LUNISOLAR_YEAR, MAX_LUNISOLAR_YEAR)); } - Contract.EndContractBlock(); return s_yinfo[lunarYear - MIN_LUNISOLAR_YEAR, index]; } @@ -345,7 +343,6 @@ namespace System.Globalization CultureInfo.CurrentCulture, SR.ArgumentOutOfRange_Range, MIN_LUNISOLAR_YEAR, MAX_LUNISOLAR_YEAR)); } - Contract.EndContractBlock(); return year; } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CompareInfo.Invariant.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CompareInfo.Invariant.cs new file mode 100644 index 0000000000..13725bcc51 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CompareInfo.Invariant.cs @@ -0,0 +1,236 @@ +// 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.Diagnostics; + +namespace System.Globalization +{ + public partial class CompareInfo + { + internal static unsafe int InvariantIndexOf(string source, string value, int startIndex, int count, bool ignoreCase) + { + Debug.Assert(source != null); + Debug.Assert(value != null); + Debug.Assert(startIndex >= 0 && startIndex < source.Length); + + fixed (char* pSource = source) fixed (char* pValue = value) + { + char* pSrc = &pSource[startIndex]; + int index = InvariantFindString(pSrc, count, pValue, value.Length, ignoreCase, start : true); + if (index >= 0) + { + return index + startIndex; + } + return -1; + } + } + + internal static unsafe int InvariantLastIndexOf(string source, string value, int startIndex, int count, bool ignoreCase) + { + Debug.Assert(source != null); + Debug.Assert(value != null); + Debug.Assert(startIndex >= 0 && startIndex < source.Length); + + fixed (char* pSource = source) fixed (char* pValue = value) + { + char* pSrc = &pSource[startIndex - count + 1]; + int index = InvariantFindString(pSrc, count, pValue, value.Length, ignoreCase, start : false); + if (index >= 0) + { + return index + startIndex - count + 1; + } + return -1; + } + } + + private static unsafe int InvariantFindString(char* source, int sourceCount, char* value, int valueCount, bool ignoreCase, bool start) + { + int ctrSource = 0; // index value into source + int ctrValue = 0; // index value into value + char sourceChar; // Character for case lookup in source + char valueChar; // Character for case lookup in value + int lastSourceStart; + + Debug.Assert(source != null); + Debug.Assert(value != null); + Debug.Assert(sourceCount >= 0); + Debug.Assert(valueCount >= 0); + + if (valueCount == 0) + { + return start ? 0 : sourceCount - 1; + } + + if (sourceCount < valueCount) + { + return -1; + } + + if (start) + { + lastSourceStart = sourceCount - valueCount; + if (ignoreCase) + { + char firstValueChar = InvariantToUpper(value[0]); + for (ctrSource = 0; ctrSource <= lastSourceStart; ctrSource++) + { + sourceChar = InvariantToUpper(source[ctrSource]); + if (sourceChar != firstValueChar) + { + continue; + } + + for (ctrValue = 1; ctrValue < valueCount; ctrValue++) + { + sourceChar = InvariantToUpper(source[ctrSource + ctrValue]); + valueChar = InvariantToUpper(value[ctrValue]); + + if (sourceChar != valueChar) + { + break; + } + } + + if (ctrValue == valueCount) + { + return ctrSource; + } + } + } + else + { + char firstValueChar = value[0]; + for (ctrSource = 0; ctrSource <= lastSourceStart; ctrSource++) + { + sourceChar = source[ctrSource]; + if (sourceChar != firstValueChar) + { + continue; + } + + for (ctrValue = 1; ctrValue < valueCount; ctrValue++) + { + sourceChar = source[ctrSource + ctrValue]; + valueChar = value[ctrValue]; + + if (sourceChar != valueChar) + { + break; + } + } + + if (ctrValue == valueCount) + { + return ctrSource; + } + } + } + } + else + { + lastSourceStart = sourceCount - valueCount; + if (ignoreCase) + { + char firstValueChar = InvariantToUpper(value[0]); + for (ctrSource = lastSourceStart; ctrSource >= 0; ctrSource--) + { + sourceChar = InvariantToUpper(source[ctrSource]); + if (sourceChar != firstValueChar) + { + continue; + } + for (ctrValue = 1; ctrValue < valueCount; ctrValue++) + { + sourceChar = InvariantToUpper(source[ctrSource + ctrValue]); + valueChar = InvariantToUpper(value[ctrValue]); + + if (sourceChar != valueChar) + { + break; + } + } + + if (ctrValue == valueCount) + { + return ctrSource; + } + } + } + else + { + char firstValueChar = value[0]; + for (ctrSource = lastSourceStart; ctrSource >= 0; ctrSource--) + { + sourceChar = source[ctrSource]; + if (sourceChar != firstValueChar) + { + continue; + } + + for (ctrValue = 1; ctrValue < valueCount; ctrValue++) + { + sourceChar = source[ctrSource + ctrValue]; + valueChar = value[ctrValue]; + + if (sourceChar != valueChar) + { + break; + } + } + + if (ctrValue == valueCount) + { + return ctrSource; + } + } + } + } + + return -1; + } + + private static char InvariantToUpper(char c) + { + return (uint)(c - 'a') <= (uint)('z' - 'a') ? (char)(c - 0x20) : c; + } + + private unsafe SortKey InvariantCreateSortKey(string source, CompareOptions options) + { + if (source == null) { throw new ArgumentNullException(nameof(source)); } + + if ((options & ValidSortkeyCtorMaskOffFlags) != 0) + { + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); + } + + byte [] keyData; + if (source.Length == 0) + { + keyData = Array.Empty(); + } + else + { + // In the invariant mode, all string comparisons are done as ordinal so when generating the sort keys we generate it according to this fact + keyData = new byte[source.Length * sizeof(char)]; + + fixed (char* pChar = source) fixed (byte* pByte = keyData) + { + if ((options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0) + { + short *pShort = (short *) pByte; + for (int i=0; i null } - return CompareString(string1, 0, string1.Length, string2, 0, string2.Length, options); + if (_invariantMode) + { + if ((options & CompareOptions.IgnoreCase) != 0) + return CompareOrdinalIgnoreCase(string1, 0, string1.Length, string2, 0, string2.Length); + + return String.CompareOrdinal(string1, string2); + } + + return CompareString(string1.AsReadOnlySpan(), string2.AsReadOnlySpan(), options); } + // TODO https://github.com/dotnet/coreclr/issues/13827: + // This method shouldn't be necessary, as we should be able to just use the overload + // that takes two spans. But due to this issue, that's adding significant overhead. + internal unsafe int Compare(ReadOnlySpan string1, string string2, CompareOptions options) + { + if (options == CompareOptions.OrdinalIgnoreCase) + { + return CompareOrdinalIgnoreCase(string1, string2.AsReadOnlySpan()); + } + + // Verify the options before we do any real comparison. + if ((options & CompareOptions.Ordinal) != 0) + { + if (options != CompareOptions.Ordinal) + { + throw new ArgumentException(SR.Argument_CompareOptionOrdinal, nameof(options)); + } + + return string.CompareOrdinal(string1, string2.AsReadOnlySpan()); + } + + if ((options & ValidCompareMaskOffFlags) != 0) + { + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); + } + + // null sorts less than any other string. + if (string2 == null) + { + return 1; + } + + if (_invariantMode) + { + return (options & CompareOptions.IgnoreCase) != 0 ? + CompareOrdinalIgnoreCase(string1, string2.AsReadOnlySpan()) : + string.CompareOrdinal(string1, string2.AsReadOnlySpan()); + } + + return CompareString(string1, string2, options); + } + + // TODO https://github.com/dotnet/corefx/issues/21395: Expose this publicly? + internal unsafe virtual int Compare(ReadOnlySpan string1, ReadOnlySpan string2, CompareOptions options) + { + if (options == CompareOptions.OrdinalIgnoreCase) + { + return CompareOrdinalIgnoreCase(string1, string2); + } + + // Verify the options before we do any real comparison. + if ((options & CompareOptions.Ordinal) != 0) + { + if (options != CompareOptions.Ordinal) + { + throw new ArgumentException(SR.Argument_CompareOptionOrdinal, nameof(options)); + } + + return string.CompareOrdinal(string1, string2); + } + + if ((options & ValidCompareMaskOffFlags) != 0) + { + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); + } + + if (_invariantMode) + { + return (options & CompareOptions.IgnoreCase) != 0 ? + CompareOrdinalIgnoreCase(string1, string2) : + string.CompareOrdinal(string1, string2); + } + + return CompareString(string1, string2, options); + } //////////////////////////////////////////////////////////////////////// // @@ -325,26 +433,26 @@ namespace System.Globalization //////////////////////////////////////////////////////////////////////// - public unsafe virtual int Compare(String string1, int offset1, int length1, String string2, int offset2, int length2) + public unsafe virtual int Compare(string string1, int offset1, int length1, string string2, int offset2, int length2) { return Compare(string1, offset1, length1, string2, offset2, length2, 0); } - public unsafe virtual int Compare(String string1, int offset1, String string2, int offset2, CompareOptions options) + public virtual int Compare(string string1, int offset1, string string2, int offset2, CompareOptions options) { return Compare(string1, offset1, string1 == null ? 0 : string1.Length - offset1, string2, offset2, string2 == null ? 0 : string2.Length - offset2, options); } - public unsafe virtual int Compare(String string1, int offset1, String string2, int offset2) + public virtual int Compare(string string1, int offset1, string string2, int offset2) { return Compare(string1, offset1, string2, offset2, 0); } - public unsafe virtual int Compare(String string1, int offset1, int length1, String string2, int offset2, int length2, CompareOptions options) + public virtual int Compare(string string1, int offset1, int length1, string string2, int offset2, int length2, CompareOptions options) { if (options == CompareOptions.OrdinalIgnoreCase) { @@ -405,9 +513,19 @@ namespace System.Globalization return CompareOrdinal(string1, offset1, length1, string2, offset2, length2); } - return CompareString(string1, offset1, length1, - string2, offset2, length2, - options); + + if (_invariantMode) + { + if ((options & CompareOptions.IgnoreCase) != 0) + return CompareOrdinalIgnoreCase(string1, offset1, length1, string2, offset2, length2); + + return CompareOrdinal(string1, offset1, length1, string2, offset2, length2); + } + + return CompareString( + string1.AsReadOnlySpan().Slice(offset1, length1), + string2.AsReadOnlySpan().Slice(offset2, length2), + options); } private static int CompareOrdinal(string string1, int offset1, int length1, string string2, int offset2, int length2) @@ -422,7 +540,7 @@ namespace System.Globalization } // - // CompareOrdinalIgnoreCase compare two string oridnally with ignoring the case. + // CompareOrdinalIgnoreCase compare two string ordinally with ignoring the case. // it assumes the strings are Ascii string till we hit non Ascii character in strA or strB and then we continue the comparison by // calling the OS. // @@ -430,16 +548,24 @@ namespace System.Globalization { Debug.Assert(indexA + lengthA <= strA.Length); Debug.Assert(indexB + lengthB <= strB.Length); + return CompareOrdinalIgnoreCase(strA.AsReadOnlySpan().Slice(indexA, lengthA), strB.AsReadOnlySpan().Slice(indexB, lengthB)); + } - int length = Math.Min(lengthA, lengthB); + internal static unsafe int CompareOrdinalIgnoreCase(ReadOnlySpan strA, ReadOnlySpan strB) + { + int length = Math.Min(strA.Length, strB.Length); int range = length; - fixed (char* ap = strA) fixed (char* bp = strB) + fixed (char* ap = &MemoryMarshal.GetReference(strA)) + fixed (char* bp = &MemoryMarshal.GetReference(strB)) { - char* a = ap + indexA; - char* b = bp + indexB; + char* a = ap; + char* b = bp; - while (length != 0 && (*a <= 0x80) && (*b <= 0x80)) + // in InvariantMode we support all range and not only the ascii characters. + char maxChar = (char) (GlobalizationMode.Invariant ? 0xFFFF : 0x80); + + while (length != 0 && (*a <= maxChar) && (*b <= maxChar)) { int charA = *a; int charB = *b; @@ -452,10 +578,10 @@ namespace System.Globalization } // uppercase both chars - notice that we need just one compare per char - if ((uint)(charA - 'a') <= (uint)('z' - 'a')) charA -= 0x20; - if ((uint)(charB - 'a') <= (uint)('z' - 'a')) charB -= 0x20; + if ((uint)(charA - 'a') <= 'z' - 'a') charA -= 0x20; + if ((uint)(charB - 'a') <= 'z' - 'a') charB -= 0x20; - //Return the (case-insensitive) difference between them. + // Return the (case-insensitive) difference between them. if (charA != charB) return charA - charB; @@ -465,11 +591,13 @@ namespace System.Globalization } if (length == 0) - return lengthA - lengthB; + return strA.Length - strB.Length; + + Debug.Assert(!GlobalizationMode.Invariant); range -= length; - return CompareStringOrdinalIgnoreCase(a, lengthA - range, b, lengthB - range); + return CompareStringOrdinalIgnoreCase(a, strA.Length - range, b, strB.Length - range); } } @@ -481,14 +609,13 @@ namespace System.Globalization // String.Empty, true is returned. // //////////////////////////////////////////////////////////////////////// - public unsafe virtual bool IsPrefix(String source, String prefix, CompareOptions options) + public virtual bool IsPrefix(string source, string prefix, CompareOptions options) { if (source == null || prefix == null) { throw new ArgumentNullException((source == null ? nameof(source) : nameof(prefix)), SR.ArgumentNull_String); } - Contract.EndContractBlock(); if (prefix.Length == 0) { @@ -515,10 +642,15 @@ namespace System.Globalization throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); } + if (_invariantMode) + { + return source.StartsWith(prefix, (options & CompareOptions.IgnoreCase) != 0 ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); + } + return StartsWith(source, prefix, options); } - public virtual bool IsPrefix(String source, String prefix) + public virtual bool IsPrefix(string source, string prefix) { return (IsPrefix(source, prefix, 0)); } @@ -531,14 +663,13 @@ namespace System.Globalization // String.Empty, true is returned. // //////////////////////////////////////////////////////////////////////// - public unsafe virtual bool IsSuffix(String source, String suffix, CompareOptions options) + public virtual bool IsSuffix(string source, string suffix, CompareOptions options) { if (source == null || suffix == null) { throw new ArgumentNullException((source == null ? nameof(source) : nameof(suffix)), SR.ArgumentNull_String); } - Contract.EndContractBlock(); if (suffix.Length == 0) { @@ -565,11 +696,16 @@ namespace System.Globalization throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); } + if (_invariantMode) + { + return source.EndsWith(suffix, (options & CompareOptions.IgnoreCase) != 0 ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); + } + return EndsWith(source, suffix, options); } - public virtual bool IsSuffix(String source, String suffix) + public virtual bool IsSuffix(string source, string suffix) { return (IsSuffix(source, suffix, 0)); } @@ -588,95 +724,87 @@ namespace System.Globalization //////////////////////////////////////////////////////////////////////// - public unsafe virtual int IndexOf(String source, char value) + public virtual int IndexOf(string source, char value) { if (source == null) throw new ArgumentNullException(nameof(source)); - Contract.EndContractBlock(); return IndexOf(source, value, 0, source.Length, CompareOptions.None); } - public unsafe virtual int IndexOf(String source, String value) + public virtual int IndexOf(string source, string value) { if (source == null) throw new ArgumentNullException(nameof(source)); - Contract.EndContractBlock(); return IndexOf(source, value, 0, source.Length, CompareOptions.None); } - public unsafe virtual int IndexOf(String source, char value, CompareOptions options) + public virtual int IndexOf(string source, char value, CompareOptions options) { if (source == null) throw new ArgumentNullException(nameof(source)); - Contract.EndContractBlock(); return IndexOf(source, value, 0, source.Length, options); } - public unsafe virtual int IndexOf(String source, String value, CompareOptions options) + public virtual int IndexOf(string source, string value, CompareOptions options) { if (source == null) throw new ArgumentNullException(nameof(source)); - Contract.EndContractBlock(); return IndexOf(source, value, 0, source.Length, options); } - public unsafe virtual int IndexOf(String source, char value, int startIndex) + public virtual int IndexOf(string source, char value, int startIndex) { if (source == null) throw new ArgumentNullException(nameof(source)); - Contract.EndContractBlock(); return IndexOf(source, value, startIndex, source.Length - startIndex, CompareOptions.None); } - public unsafe virtual int IndexOf(String source, String value, int startIndex) + public virtual int IndexOf(string source, string value, int startIndex) { if (source == null) throw new ArgumentNullException(nameof(source)); - Contract.EndContractBlock(); return IndexOf(source, value, startIndex, source.Length - startIndex, CompareOptions.None); } - public unsafe virtual int IndexOf(String source, char value, int startIndex, CompareOptions options) + public virtual int IndexOf(string source, char value, int startIndex, CompareOptions options) { if (source == null) throw new ArgumentNullException(nameof(source)); - Contract.EndContractBlock(); return IndexOf(source, value, startIndex, source.Length - startIndex, options); } - public unsafe virtual int IndexOf(String source, String value, int startIndex, CompareOptions options) + public virtual int IndexOf(string source, string value, int startIndex, CompareOptions options) { if (source == null) throw new ArgumentNullException(nameof(source)); - Contract.EndContractBlock(); return IndexOf(source, value, startIndex, source.Length - startIndex, options); } - public unsafe virtual int IndexOf(String source, char value, int startIndex, int count) + public virtual int IndexOf(string source, char value, int startIndex, int count) { return IndexOf(source, value, startIndex, count, CompareOptions.None); } - public unsafe virtual int IndexOf(String source, String value, int startIndex, int count) + public virtual int IndexOf(string source, string value, int startIndex, int count) { return IndexOf(source, value, startIndex, count, CompareOptions.None); } - public unsafe virtual int IndexOf(String source, char value, int startIndex, int count, CompareOptions options) + public unsafe virtual int IndexOf(string source, char value, int startIndex, int count, CompareOptions options) { // Validate inputs if (source == null) @@ -687,7 +815,6 @@ namespace System.Globalization if (count < 0 || startIndex > source.Length - count) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count); - Contract.EndContractBlock(); if (options == CompareOptions.OrdinalIgnoreCase) { @@ -698,12 +825,14 @@ namespace System.Globalization // Ordinal can't be selected with other flags if ((options & ValidIndexMaskOffFlags) != 0 && (options != CompareOptions.Ordinal)) throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); + + if (_invariantMode) + return IndexOfOrdinal(source, new string(value, 1), startIndex, count, ignoreCase: (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0); return IndexOfCore(source, new string(value, 1), startIndex, count, options, null); } - - public unsafe virtual int IndexOf(String source, String value, int startIndex, int count, CompareOptions options) + public unsafe virtual int IndexOf(string source, string value, int startIndex, int count, CompareOptions options) { // Validate inputs if (source == null) @@ -715,7 +844,6 @@ namespace System.Globalization { throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); } - Contract.EndContractBlock(); // In Everett we used to return -1 for empty string even if startIndex is negative number so we keeping same behavior here. // We return 0 if both source and value are empty strings for Everett compatibility too. @@ -746,9 +874,69 @@ namespace System.Globalization if ((options & ValidIndexMaskOffFlags) != 0 && (options != CompareOptions.Ordinal)) throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); + if (_invariantMode) + return IndexOfOrdinal(source, value, startIndex, count, ignoreCase: (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0); + return IndexOfCore(source, value, startIndex, count, options, null); } + // The following IndexOf overload is mainly used by String.Replace. This overload assumes the parameters are already validated + // and the caller is passing a valid matchLengthPtr pointer. + internal unsafe int IndexOf(string source, string value, int startIndex, int count, CompareOptions options, int* matchLengthPtr) + { + Debug.Assert(source != null); + Debug.Assert(value != null); + Debug.Assert(startIndex >= 0); + Debug.Assert(matchLengthPtr != null); + *matchLengthPtr = 0; + + if (source.Length == 0) + { + if (value.Length == 0) + { + return 0; + } + return -1; + } + + if (startIndex >= source.Length) + { + return -1; + } + + if (options == CompareOptions.OrdinalIgnoreCase) + { + int res = IndexOfOrdinal(source, value, startIndex, count, ignoreCase: true); + if (res >= 0) + { + *matchLengthPtr = value.Length; + } + return res; + } + + if (_invariantMode) + { + int res = IndexOfOrdinal(source, value, startIndex, count, ignoreCase: (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0); + if (res >= 0) + { + *matchLengthPtr = value.Length; + } + return res; + } + + return IndexOfCore(source, value, startIndex, count, options, matchLengthPtr); + } + + internal int IndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase) + { + if (_invariantMode) + { + return InvariantIndexOf(source, value, startIndex, count, ignoreCase); + } + + return IndexOfOrdinalCore(source, value, startIndex, count, ignoreCase); + } + //////////////////////////////////////////////////////////////////////// // // LastIndexOf @@ -763,11 +951,20 @@ namespace System.Globalization //////////////////////////////////////////////////////////////////////// - public unsafe virtual int LastIndexOf(String source, char value) + public virtual int LastIndexOf(String source, char value) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + + // Can't start at negative index, so make sure we check for the length == 0 case. + return LastIndexOf(source, value, source.Length - 1, source.Length, CompareOptions.None); + } + + + public virtual int LastIndexOf(string source, string value) { if (source == null) throw new ArgumentNullException(nameof(source)); - Contract.EndContractBlock(); // Can't start at negative index, so make sure we check for the length == 0 case. return LastIndexOf(source, value, source.Length - 1, @@ -775,81 +972,65 @@ namespace System.Globalization } - public virtual int LastIndexOf(String source, String value) + public virtual int LastIndexOf(string source, char value, CompareOptions options) { if (source == null) throw new ArgumentNullException(nameof(source)); - Contract.EndContractBlock(); - - // Can't start at negative index, so make sure we check for the length == 0 case. - return LastIndexOf(source, value, source.Length - 1, - source.Length, CompareOptions.None); - } - - - public virtual int LastIndexOf(String source, char value, CompareOptions options) - { - if (source == null) - throw new ArgumentNullException(nameof(source)); - Contract.EndContractBlock(); // Can't start at negative index, so make sure we check for the length == 0 case. return LastIndexOf(source, value, source.Length - 1, source.Length, options); } - public unsafe virtual int LastIndexOf(String source, String value, CompareOptions options) + public virtual int LastIndexOf(string source, string value, CompareOptions options) { if (source == null) throw new ArgumentNullException(nameof(source)); - Contract.EndContractBlock(); // Can't start at negative index, so make sure we check for the length == 0 case. - return LastIndexOf(source, value, source.Length - 1, - source.Length, options); + return LastIndexOf(source, value, source.Length - 1, source.Length, options); } - public unsafe virtual int LastIndexOf(String source, char value, int startIndex) + public virtual int LastIndexOf(string source, char value, int startIndex) { return LastIndexOf(source, value, startIndex, startIndex + 1, CompareOptions.None); } - public unsafe virtual int LastIndexOf(String source, String value, int startIndex) + public virtual int LastIndexOf(string source, string value, int startIndex) { return LastIndexOf(source, value, startIndex, startIndex + 1, CompareOptions.None); } - public unsafe virtual int LastIndexOf(String source, char value, int startIndex, CompareOptions options) + public virtual int LastIndexOf(string source, char value, int startIndex, CompareOptions options) { return LastIndexOf(source, value, startIndex, startIndex + 1, options); } - public unsafe virtual int LastIndexOf(String source, String value, int startIndex, CompareOptions options) + public virtual int LastIndexOf(string source, string value, int startIndex, CompareOptions options) { return LastIndexOf(source, value, startIndex, startIndex + 1, options); } - public unsafe virtual int LastIndexOf(String source, char value, int startIndex, int count) + public virtual int LastIndexOf(string source, char value, int startIndex, int count) { return LastIndexOf(source, value, startIndex, count, CompareOptions.None); } - public unsafe virtual int LastIndexOf(String source, String value, int startIndex, int count) + public virtual int LastIndexOf(string source, string value, int startIndex, int count) { return LastIndexOf(source, value, startIndex, count, CompareOptions.None); } - public unsafe virtual int LastIndexOf(String source, char value, int startIndex, int count, CompareOptions options) + public virtual int LastIndexOf(string source, char value, int startIndex, int count, CompareOptions options) { // Verify Arguments if (source == null) throw new ArgumentNullException(nameof(source)); - Contract.EndContractBlock(); // Validate CompareOptions // Ordinal can't be selected with other flags @@ -883,18 +1064,20 @@ namespace System.Globalization return source.LastIndexOf(value.ToString(), startIndex, count, StringComparison.OrdinalIgnoreCase); } + if (_invariantMode) + return InvariantLastIndexOf(source, new string(value, 1), startIndex, count, (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0); + return LastIndexOfCore(source, value.ToString(), startIndex, count, options); } - public unsafe virtual int LastIndexOf(String source, String value, int startIndex, int count, CompareOptions options) + public virtual int LastIndexOf(string source, string value, int startIndex, int count, CompareOptions options) { // Verify Arguments if (source == null) throw new ArgumentNullException(nameof(source)); if (value == null) throw new ArgumentNullException(nameof(value)); - Contract.EndContractBlock(); // Validate CompareOptions // Ordinal can't be selected with other flags @@ -932,9 +1115,22 @@ namespace System.Globalization return LastIndexOfOrdinal(source, value, startIndex, count, ignoreCase: true); } + if (_invariantMode) + return InvariantLastIndexOf(source, value, startIndex, count, (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0); + return LastIndexOfCore(source, value, startIndex, count, options); } + internal int LastIndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase) + { + if (_invariantMode) + { + return InvariantLastIndexOf(source, value, startIndex, count, ignoreCase); + } + + return LastIndexOfOrdinalCore(source, value, startIndex, count, ignoreCase); + } + //////////////////////////////////////////////////////////////////////// // // GetSortKey @@ -942,14 +1138,20 @@ namespace System.Globalization // Gets the SortKey for the given string with the given options. // //////////////////////////////////////////////////////////////////////// - public unsafe virtual SortKey GetSortKey(String source, CompareOptions options) + public virtual SortKey GetSortKey(string source, CompareOptions options) { + if (_invariantMode) + return InvariantCreateSortKey(source, options); + return CreateSortKey(source, options); } - public unsafe virtual SortKey GetSortKey(String source) + public virtual SortKey GetSortKey(string source) { + if (_invariantMode) + return InvariantCreateSortKey(source, CompareOptions.None); + return CreateSortKey(source, CompareOptions.None); } @@ -1032,7 +1234,6 @@ namespace System.Globalization { throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); } - Contract.EndContractBlock(); return GetHashCodeOfStringCore(source, options); } @@ -1070,7 +1271,7 @@ namespace System.Globalization // CompareInfo. // //////////////////////////////////////////////////////////////////////// - public override String ToString() + public override string ToString() { return ("CompareInfo - " + this.Name); } @@ -1079,12 +1280,23 @@ namespace System.Globalization { get { - if (_sortVersion == null) + if (m_SortVersion == null) { - _sortVersion = GetSortVersion(); + if (_invariantMode) + { + m_SortVersion = new SortVersion(0, CultureInfo.LOCALE_INVARIANT, new Guid(0, 0, 0, 0, 0, 0, 0, + (byte) (CultureInfo.LOCALE_INVARIANT >> 24), + (byte) ((CultureInfo.LOCALE_INVARIANT & 0x00FF0000) >> 16), + (byte) ((CultureInfo.LOCALE_INVARIANT & 0x0000FF00) >> 8), + (byte) (CultureInfo.LOCALE_INVARIANT & 0xFF))); + } + else + { + m_SortVersion = GetSortVersion(); + } } - return _sortVersion; + return m_SortVersion; } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CultureData.Unix.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CultureData.Unix.cs index 4f685de580..3b4b60fc8a 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CultureData.Unix.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CultureData.Unix.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.InteropServices; using System.Security; using System.Text; @@ -149,7 +148,7 @@ namespace System.Globalization { // Failed, just use empty string StringBuilderCache.Release(sb); - Debug.Assert(false, "[CultureData.GetLocaleInfo(LocaleStringData)] Failed"); + Debug.Fail("[CultureData.GetLocaleInfo(LocaleStringData)] Failed"); return String.Empty; } return StringBuilderCache.GetStringAndRelease(sb); @@ -174,7 +173,7 @@ namespace System.Globalization if (!result) { // Failed, just use 0 - Debug.Assert(false, "[CultureData.GetLocaleInfo(LocaleNumberData)] failed"); + Debug.Fail("[CultureData.GetLocaleInfo(LocaleNumberData)] failed"); } return value; @@ -189,7 +188,7 @@ namespace System.Globalization bool result = Interop.GlobalizationInterop.GetLocaleInfoGroupingSizes(_sWindowsName, (uint)type, ref primaryGroupingSize, ref secondaryGroupingSize); if (!result) { - Debug.Assert(false, "[CultureData.GetLocaleInfo(LocaleGroupingData type)] failed"); + Debug.Fail("[CultureData.GetLocaleInfo(LocaleGroupingData type)] failed"); } if (secondaryGroupingSize == 0) @@ -216,7 +215,7 @@ namespace System.Globalization { // Failed, just use empty string StringBuilderCache.Release(sb); - Debug.Assert(false, "[CultureData.GetTimeFormatString(bool shortFormat)] Failed"); + Debug.Fail("[CultureData.GetTimeFormatString(bool shortFormat)] Failed"); return String.Empty; } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CultureNotFoundException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CultureNotFoundException.cs index dde1a8b2ba..10e8b1f836 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CultureNotFoundException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/CultureNotFoundException.cs @@ -6,6 +6,8 @@ using System.Runtime.Serialization; namespace System.Globalization { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class CultureNotFoundException : ArgumentException { private string _invalidCultureName; // unrecognized culture name @@ -58,12 +60,15 @@ namespace System.Globalization protected CultureNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); + _invalidCultureId = (int?)info.GetValue("InvalidCultureId", typeof(int?)); + _invalidCultureName = (string)info.GetValue("InvalidCultureName", typeof(string)); } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + info.AddValue("InvalidCultureId", _invalidCultureId, typeof(int?)); + info.AddValue("InvalidCultureName", _invalidCultureName, typeof(string)); } public virtual Nullable InvalidCultureId diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormat.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormat.cs index 840409f55a..d15cc1cc8c 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormat.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormat.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Globalization; using System.Text; @@ -211,7 +210,7 @@ namespace System outputBuffer.Append(HebrewNumber.ToString(digits)); } - internal static int ParseRepeatPattern(String format, int pos, char patternChar) + internal static int ParseRepeatPattern(ReadOnlySpan format, int pos, char patternChar) { int len = format.Length; int index = pos + 1; @@ -299,7 +298,7 @@ namespace System // The pos should point to a quote character. This method will // append to the result StringBuilder the string encloed by the quote character. // - internal static int ParseQuoteString(String format, int pos, StringBuilder result) + internal static int ParseQuoteString(ReadOnlySpan format, int pos, StringBuilder result) { // // NOTE : pos will be the index of the quote character in the 'format' string. @@ -362,7 +361,7 @@ namespace System // Return value of -1 means 'pos' is already at the end of the 'format' string. // Otherwise, return value is the int value of the next character. // - internal static int ParseNextChar(String format, int pos) + internal static int ParseNextChar(ReadOnlySpan format, int pos) { if (pos >= format.Length - 1) { @@ -384,7 +383,7 @@ namespace System // tokenLen The len of the current pattern character. This indicates how many "M" that we have. // patternToMatch The pattern that we want to search. This generally uses "d" // - private static bool IsUseGenitiveForm(String format, int index, int tokenLen, char patternToMatch) + private static bool IsUseGenitiveForm(ReadOnlySpan format, int index, int tokenLen, char patternToMatch) { int i; int repeat = 0; @@ -447,12 +446,19 @@ namespace System // // Actions: Format the DateTime instance using the specified format. // - private static String FormatCustomized(DateTime dateTime, String format, DateTimeFormatInfo dtfi, TimeSpan offset) + private static StringBuilder FormatCustomized( + DateTime dateTime, ReadOnlySpan format, DateTimeFormatInfo dtfi, TimeSpan offset, StringBuilder result) { Calendar cal = dtfi.Calendar; - StringBuilder result = StringBuilderCache.Acquire(); - // This is a flag to indicate if we are format the dates using Hebrew calendar. + bool resultBuilderIsPooled = false; + if (result == null) + { + resultBuilderIsPooled = true; + result = StringBuilderCache.Acquire(); + } + + // This is a flag to indicate if we are format the dates using Hebrew calendar. bool isHebrewCalendar = (cal.ID == CalendarId.HEBREW); // This is a flag to indicate if we are formating hour/minute/second only. bool bTimeOnly = true; @@ -533,6 +539,10 @@ namespace System } else { + if (resultBuilderIsPooled) + { + StringBuilderCache.Release(result); + } throw new FormatException(SR.Format_InvalidString); } break; @@ -690,9 +700,11 @@ namespace System nextChar = ParseNextChar(format, i); // nextChar will be -1 if we already reach the end of the format string. // Besides, we will not allow "%%" appear in the pattern. - if (nextChar >= 0 && nextChar != (int)'%') + if (nextChar >= 0 && nextChar != '%') { - result.Append(FormatCustomized(dateTime, ((char)nextChar).ToString(), dtfi, offset)); + char nextCharChar = (char)nextChar; + StringBuilder origStringBuilder = FormatCustomized(dateTime, ReadOnlySpan.DangerousCreate(null, ref nextCharChar, 1), dtfi, offset, result); + Debug.Assert(ReferenceEquals(origStringBuilder, result)); tokenLen = 2; } else @@ -701,6 +713,10 @@ namespace System // This means that '%' is at the end of the format string or // "%%" appears in the format string. // + if (resultBuilderIsPooled) + { + StringBuilderCache.Release(result); + } throw new FormatException(SR.Format_InvalidString); } break; @@ -724,6 +740,10 @@ namespace System // // This means that '\' is at the end of the formatting string. // + if (resultBuilderIsPooled) + { + StringBuilderCache.Release(result); + } throw new FormatException(SR.Format_InvalidString); } break; @@ -738,12 +758,12 @@ namespace System } i += tokenLen; } - return StringBuilderCache.GetStringAndRelease(result); + return result; } // output the 'z' famliy of formats, which output a the offset from UTC, e.g. "-07:30" - private static void FormatCustomizedTimeZone(DateTime dateTime, TimeSpan offset, String format, Int32 tokenLen, Boolean timeOnly, StringBuilder result) + private static void FormatCustomizedTimeZone(DateTime dateTime, TimeSpan offset, ReadOnlySpan format, Int32 tokenLen, Boolean timeOnly, StringBuilder result) { // See if the instance already has an offset Boolean dateTimeFormat = (offset == NullOffset); @@ -836,8 +856,7 @@ namespace System AppendNumber(result, offset.Minutes, 2); } - - internal static String GetRealFormat(String format, DateTimeFormatInfo dtfi) + internal static String GetRealFormat(ReadOnlySpan format, DateTimeFormatInfo dtfi) { String realFormat = null; @@ -904,7 +923,7 @@ namespace System // This method also convert the dateTime if necessary (e.g. when the format is in Universal time), // and change dtfi if necessary (e.g. when the format should use invariant culture). // - private static String ExpandPredefinedFormat(String format, ref DateTime dateTime, ref DateTimeFormatInfo dtfi, ref TimeSpan offset) + private static String ExpandPredefinedFormat(ReadOnlySpan format, ref DateTime dateTime, ref DateTimeFormatInfo dtfi, ref TimeSpan offset) { switch (format[0]) { @@ -958,8 +977,7 @@ namespace System dateTime = dateTime.ToUniversalTime(); break; } - format = GetRealFormat(format, dtfi); - return (format); + return GetRealFormat(format, dtfi); } internal static String Format(DateTime dateTime, String format, DateTimeFormatInfo dtfi) @@ -967,11 +985,35 @@ namespace System return Format(dateTime, format, dtfi, NullOffset); } + internal static string Format(DateTime dateTime, String format, DateTimeFormatInfo dtfi, TimeSpan offset) => + StringBuilderCache.GetStringAndRelease(FormatStringBuilder(dateTime, format, dtfi, offset)); - internal static String Format(DateTime dateTime, String format, DateTimeFormatInfo dtfi, TimeSpan offset) + internal static bool TryFormat(DateTime dateTime, Span destination, out int charsWritten, ReadOnlySpan format, DateTimeFormatInfo dtfi) => + TryFormat(dateTime, destination, out charsWritten, format, dtfi, NullOffset); + + internal static bool TryFormat(DateTime dateTime, Span destination, out int charsWritten, ReadOnlySpan format, DateTimeFormatInfo dtfi, TimeSpan offset) { - Contract.Requires(dtfi != null); - if (format == null || format.Length == 0) + StringBuilder sb = FormatStringBuilder(dateTime, format, dtfi, offset); + + bool success = sb.Length <= destination.Length; + if (success) + { + sb.CopyTo(0, destination, sb.Length); + charsWritten = sb.Length; + } + else + { + charsWritten = 0; + } + + StringBuilderCache.Release(sb); + return success; + } + + internal static StringBuilder FormatStringBuilder(DateTime dateTime, ReadOnlySpan format, DateTimeFormatInfo dtfi, TimeSpan offset) + { + Debug.Assert(dtfi != null); + if (format.Length == 0) { Boolean timeOnlySpecialCase = false; if (dateTime.Ticks < Calendar.TicksPerDay) @@ -1006,26 +1048,12 @@ namespace System if (offset == NullOffset) { // Default DateTime.ToString case. - if (timeOnlySpecialCase) - { - format = "s"; - } - else - { - format = "G"; - } + format = timeOnlySpecialCase ? "s" : "G"; } else { // Default DateTimeOffset.ToString case. - if (timeOnlySpecialCase) - { - format = RoundtripDateTimeUnfixed; - } - else - { - format = dtfi.DateTimeOffsetPattern; - } + format = timeOnlySpecialCase ? RoundtripDateTimeUnfixed : dtfi.DateTimeOffsetPattern; } } @@ -1044,10 +1072,10 @@ namespace System format = ExpandPredefinedFormat(format, ref dateTime, ref dtfi, ref offset); } - return (FormatCustomized(dateTime, format, dtfi, offset)); + return FormatCustomized(dateTime, format, dtfi, offset, result: null); } - internal static string FastFormatRfc1123(DateTime dateTime, TimeSpan offset, DateTimeFormatInfo dtfi) + internal static StringBuilder FastFormatRfc1123(DateTime dateTime, TimeSpan offset, DateTimeFormatInfo dtfi) { // ddd, dd MMM yyyy HH:mm:ss GMT const int Rfc1123FormatLength = 29; @@ -1059,33 +1087,35 @@ namespace System dateTime = dateTime - offset; } + dateTime.GetDatePart(out int year, out int month, out int day); result.Append(InvariantAbbreviatedDayNames[(int)dateTime.DayOfWeek]); result.Append(','); result.Append(' '); - AppendNumber(result, dateTime.Day, 2); + AppendNumber(result, day, 2); result.Append(' '); - result.Append(InvariantAbbreviatedMonthNames[dateTime.Month - 1]); + result.Append(InvariantAbbreviatedMonthNames[month - 1]); result.Append(' '); - AppendNumber(result, dateTime.Year, 4); + AppendNumber(result, year, 4); result.Append(' '); AppendHHmmssTimeOfDay(result, dateTime); result.Append(' '); result.Append(Gmt); - return StringBuilderCache.GetStringAndRelease(result); + return result; } - internal static string FastFormatRoundtrip(DateTime dateTime, TimeSpan offset) + internal static StringBuilder FastFormatRoundtrip(DateTime dateTime, TimeSpan offset) { // yyyy-MM-ddTHH:mm:ss.fffffffK const int roundTripFormatLength = 28; StringBuilder result = StringBuilderCache.Acquire(roundTripFormatLength); - AppendNumber(result, dateTime.Year, 4); + dateTime.GetDatePart(out int year, out int month, out int day); + AppendNumber(result, year, 4); result.Append('-'); - AppendNumber(result, dateTime.Month, 2); + AppendNumber(result, month, 2); result.Append('-'); - AppendNumber(result, dateTime.Day, 2); + AppendNumber(result, day, 2); result.Append('T'); AppendHHmmssTimeOfDay(result, dateTime); result.Append('.'); @@ -1095,7 +1125,7 @@ namespace System FormatCustomizedRoundripTimeZone(dateTime, offset, result); - return StringBuilderCache.GetStringAndRelease(result); + return result; } private static void AppendHHmmssTimeOfDay(StringBuilder result, DateTime dateTime) @@ -1128,7 +1158,7 @@ namespace System internal static String[] GetAllDateTimes(DateTime dateTime, char format, DateTimeFormatInfo dtfi) { - Contract.Requires(dtfi != null); + Debug.Assert(dtfi != null); String[] allFormats = null; String[] results = null; @@ -1199,7 +1229,7 @@ namespace System // This is a placeholder for an MDA to detect when the user is using a // local DateTime with a format that will be interpreted as UTC. - internal static void InvalidFormatForLocal(String format, DateTime dateTime) + internal static void InvalidFormatForLocal(ReadOnlySpan format, DateTime dateTime) { } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfo.cs.REMOVED.git-id b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfo.cs.REMOVED.git-id index 2930049bd9..86188236ad 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfo.cs.REMOVED.git-id +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfo.cs.REMOVED.git-id @@ -1 +1 @@ -5d3239ebfcebe689c41e34ee7e593c473d39ff4a \ No newline at end of file +b6a4621508c3d6e89c5d852f98f883758ac459b2 \ No newline at end of file diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfoScanner.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfoScanner.cs index 15af1b7d84..c38e7a26b8 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfoScanner.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfoScanner.cs @@ -24,7 +24,6 @@ using System.Text; namespace System.Globalization { - #if CORECLR using StringStringDictionary = Dictionary; using StringList = List; diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/DateTimeParse.cs.REMOVED.git-id b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/DateTimeParse.cs.REMOVED.git-id index ca6c2365e1..4a1ad61c3f 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/DateTimeParse.cs.REMOVED.git-id +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/DateTimeParse.cs.REMOVED.git-id @@ -1 +1 @@ -910fbf2ff0204ef7f3af1b77c79f10d685464d41 \ No newline at end of file +cbf90945c82ee66e157e3d872a92dc3dc4c2e4c6 \ No newline at end of file diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/DaylightTime.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/DaylightTime.cs index 10f074dc24..e6920b3666 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/DaylightTime.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/DaylightTime.cs @@ -33,7 +33,7 @@ namespace System.Globalization } // Value type version of DaylightTime - internal struct DaylightTimeStruct + internal readonly struct DaylightTimeStruct { public DaylightTimeStruct(DateTime start, DateTime end, TimeSpan delta) { diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/EastAsianLunisolarCalendar.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/EastAsianLunisolarCalendar.cs index 0697b602db..358f4df182 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/EastAsianLunisolarCalendar.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/EastAsianLunisolarCalendar.cs @@ -2,7 +2,6 @@ // 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.Diagnostics.Contracts; namespace System.Globalization { @@ -62,12 +61,11 @@ namespace System.Globalization nameof(sexagenaryYear), SR.Format(SR.ArgumentOutOfRange_Range, 1, 60)); } - Contract.EndContractBlock(); return ((sexagenaryYear - 1) % 10) + 1; } - // Return the Terrestial Branch from the the 60-year cycle. + // Return the Terrestial Branch from the 60-year cycle. // The returned value is from 1 ~ 12. // @@ -79,7 +77,6 @@ namespace System.Globalization nameof(sexagenaryYear), SR.Format(SR.ArgumentOutOfRange_Range, 1, 60)); } - Contract.EndContractBlock(); return ((sexagenaryYear - 1) % 12) + 1; } @@ -168,7 +165,6 @@ namespace System.Globalization String.Format(CultureInfo.InvariantCulture, SR.ArgumentOutOfRange_CalendarRange, MinSupportedDateTime, MaxSupportedDateTime)); } - Contract.EndContractBlock(); } internal void CheckEraRange(int era) @@ -440,7 +436,6 @@ namespace System.Globalization nameof(months), SR.Format(SR.ArgumentOutOfRange_Range, -120000, 120000)); } - Contract.EndContractBlock(); CheckTicksRange(time.Ticks); @@ -699,7 +694,6 @@ namespace System.Globalization throw new ArgumentOutOfRangeException(nameof(year), SR.ArgumentOutOfRange_NeedNonNegNum); } - Contract.EndContractBlock(); year = base.ToFourDigitYear(year); CheckYearRange(year, CurrentEra); diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/GregorianCalendar.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/GregorianCalendar.cs new file mode 100644 index 0000000000..16023209ea --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/GregorianCalendar.cs @@ -0,0 +1,598 @@ +// 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.Globalization; +using System.Threading; + +namespace System.Globalization +{ + // This calendar recognizes two era values: + // 0 CurrentEra (AD) + // 1 BeforeCurrentEra (BC) + public class GregorianCalendar : Calendar + { + /* + A.D. = anno Domini + */ + + public const int ADEra = 1; + + // + // This is the max Gregorian year can be represented by DateTime class. The limitation + // is derived from DateTime class. + // + internal const int MaxYear = 9999; + + internal GregorianCalendarTypes m_type; + + internal static readonly int[] DaysToMonth365 = + { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 + }; + + internal static readonly int[] DaysToMonth366 = + { + 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 + }; + + private static volatile Calendar s_defaultInstance; + + + public override DateTime MinSupportedDateTime + { + get + { + return (DateTime.MinValue); + } + } + + public override DateTime MaxSupportedDateTime + { + get + { + return (DateTime.MaxValue); + } + } + + public override CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.SolarCalendar; + } + } + + /*=================================GetDefaultInstance========================== + **Action: Internal method to provide a default intance of GregorianCalendar. Used by NLS+ implementation + ** and other calendars. + **Returns: + **Arguments: + **Exceptions: + ============================================================================*/ + + internal static Calendar GetDefaultInstance() + { + if (s_defaultInstance == null) + { + s_defaultInstance = new GregorianCalendar(); + } + return (s_defaultInstance); + } + + // Construct an instance of gregorian calendar. + + public GregorianCalendar() : + this(GregorianCalendarTypes.Localized) + { + } + + + public GregorianCalendar(GregorianCalendarTypes type) + { + if ((int)type < (int)GregorianCalendarTypes.Localized || (int)type > (int)GregorianCalendarTypes.TransliteratedFrench) + { + throw new ArgumentOutOfRangeException( + nameof(type), + SR.Format(SR.ArgumentOutOfRange_Range, + GregorianCalendarTypes.Localized, GregorianCalendarTypes.TransliteratedFrench)); + } + this.m_type = type; + } + + public virtual GregorianCalendarTypes CalendarType + { + get + { + return (m_type); + } + + set + { + VerifyWritable(); + + switch (value) + { + case GregorianCalendarTypes.Localized: + case GregorianCalendarTypes.USEnglish: + case GregorianCalendarTypes.MiddleEastFrench: + case GregorianCalendarTypes.Arabic: + case GregorianCalendarTypes.TransliteratedEnglish: + case GregorianCalendarTypes.TransliteratedFrench: + m_type = value; + break; + + default: + throw new ArgumentOutOfRangeException("m_type", SR.ArgumentOutOfRange_Enum); + } + } + } + + internal override CalendarId ID + { + get + { + // By returning different ID for different variations of GregorianCalendar, + // we can support the Transliterated Gregorian calendar. + // DateTimeFormatInfo will use this ID to get formatting information about + // the calendar. + return ((CalendarId)m_type); + } + } + + + /*=================================GetAbsoluteDate========================== + **Action: Gets the absolute date for the given Gregorian date. The absolute date means + ** the number of days from January 1st, 1 A.D. + **Returns: the absolute date + **Arguments: + ** year the Gregorian year + ** month the Gregorian month + ** day the day + **Exceptions: + ** ArgumentOutOfRangException if year, month, day value is valid. + **Note: + ** This is an internal method used by DateToTicks() and the calculations of Hijri and Hebrew calendars. + ** Number of Days in Prior Years (both common and leap years) + + ** Number of Days in Prior Months of Current Year + + ** Number of Days in Current Month + ** + ============================================================================*/ + + internal static long GetAbsoluteDate(int year, int month, int day) + { + if (year >= 1 && year <= MaxYear && month >= 1 && month <= 12) + { + int[] days = ((year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))) ? DaysToMonth366 : DaysToMonth365; + if (day >= 1 && (day <= days[month] - days[month - 1])) + { + int y = year - 1; + int absoluteDate = y * 365 + y / 4 - y / 100 + y / 400 + days[month - 1] + day - 1; + return (absoluteDate); + } + } + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay); + } + + // Returns the tick count corresponding to the given year, month, and day. + // Will check the if the parameters are valid. + internal virtual long DateToTicks(int year, int month, int day) + { + return (GetAbsoluteDate(year, month, day) * TicksPerDay); + } + + // Returns the DateTime resulting from adding the given number of + // months to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year and month parts of the specified DateTime by + // value months, and, if required, adjusting the day part of the + // resulting date downwards to the last day of the resulting month in the + // resulting year. The time-of-day part of the result is the same as the + // time-of-day part of the specified DateTime. + // + // In more precise terms, considering the specified DateTime to be of the + // form y / m / d + t, where y is the + // year, m is the month, d is the day, and t is the + // time-of-day, the result is y1 / m1 / d1 + t, + // where y1 and m1 are computed by adding value months + // to y and m, and d1 is the largest value less than + // or equal to d that denotes a valid day in month m1 of year + // y1. + // + + public override DateTime AddMonths(DateTime time, int months) + { + if (months < -120000 || months > 120000) + { + throw new ArgumentOutOfRangeException( + nameof(months), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + -120000, + 120000)); + } + time.GetDatePart(out int y, out int m, out int d); + int i = m - 1 + months; + if (i >= 0) + { + m = i % 12 + 1; + y = y + i / 12; + } + else + { + m = 12 + (i + 1) % 12; + y = y + (i - 11) / 12; + } + int[] daysArray = (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) ? DaysToMonth366 : DaysToMonth365; + int days = (daysArray[m] - daysArray[m - 1]); + + if (d > days) + { + d = days; + } + long ticks = DateToTicks(y, m, d) + time.Ticks % TicksPerDay; + Calendar.CheckAddResult(ticks, MinSupportedDateTime, MaxSupportedDateTime); + + return (new DateTime(ticks)); + } + + + // Returns the DateTime resulting from adding the given number of + // years to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year part of the specified DateTime by value + // years. If the month and day of the specified DateTime is 2/29, and if the + // resulting year is not a leap year, the month and day of the resulting + // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day + // parts of the result are the same as those of the specified DateTime. + // + + public override DateTime AddYears(DateTime time, int years) + { + return (AddMonths(time, years * 12)); + } + + // Returns the day-of-month part of the specified DateTime. The returned + // value is an integer between 1 and 31. + // + + public override int GetDayOfMonth(DateTime time) + { + return time.Day; + } + + // Returns the day-of-week part of the specified DateTime. The returned value + // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates + // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates + // Thursday, 5 indicates Friday, and 6 indicates Saturday. + // + + public override DayOfWeek GetDayOfWeek(DateTime time) + { + return ((DayOfWeek)((int)(time.Ticks / TicksPerDay + 1) % 7)); + } + + // Returns the day-of-year part of the specified DateTime. The returned value + // is an integer between 1 and 366. + // + + public override int GetDayOfYear(DateTime time) + { + return time.DayOfYear; + } + + // Returns the number of days in the month given by the year and + // month arguments. + // + + public override int GetDaysInMonth(int year, int month, int era) + { + if (era == CurrentEra || era == ADEra) + { + if (year < 1 || year > MaxYear) + { + throw new ArgumentOutOfRangeException(nameof(year), SR.Format(SR.ArgumentOutOfRange_Range, + 1, MaxYear)); + } + if (month < 1 || month > 12) + { + throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month); + } + int[] days = ((year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ? DaysToMonth366 : DaysToMonth365); + return (days[month] - days[month - 1]); + } + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + + // Returns the number of days in the year given by the year argument for the current era. + // + + public override int GetDaysInYear(int year, int era) + { + if (era == CurrentEra || era == ADEra) + { + if (year >= 1 && year <= MaxYear) + { + return ((year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ? 366 : 365); + } + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + MaxYear)); + } + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + + // Returns the era for the specified DateTime value. + + public override int GetEra(DateTime time) + { + return (ADEra); + } + + + public override int[] Eras + { + get + { + return (new int[] { ADEra }); + } + } + + + // Returns the month part of the specified DateTime. The returned value is an + // integer between 1 and 12. + // + + public override int GetMonth(DateTime time) + { + return time.Month; + } + + // Returns the number of months in the specified year and era. + + public override int GetMonthsInYear(int year, int era) + { + if (era == CurrentEra || era == ADEra) + { + if (year >= 1 && year <= MaxYear) + { + return (12); + } + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + MaxYear)); + } + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + + // Returns the year part of the specified DateTime. The returned value is an + // integer between 1 and 9999. + // + + public override int GetYear(DateTime time) + { + return time.Year; + } + + internal override bool IsValidYear(int year, int era) => year >= 1 && year <= MaxYear; + + internal override bool IsValidDay(int year, int month, int day, int era) + { + if ((era != CurrentEra && era != ADEra) || + year < 1 || year > MaxYear || + month < 1 || month > 12 || + day < 1) + { + return false; + } + + int[] days = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ? DaysToMonth366 : DaysToMonth365; + return day <= (days[month] - days[month - 1]); + } + + // Checks whether a given day in the specified era is a leap day. This method returns true if + // the date is a leap day, or false if not. + // + + public override bool IsLeapDay(int year, int month, int day, int era) + { + if (month < 1 || month > 12) + { + throw new ArgumentOutOfRangeException(nameof(month), SR.Format(SR.ArgumentOutOfRange_Range, + 1, 12)); + } + + if (era != CurrentEra && era != ADEra) + { + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + if (year < 1 || year > MaxYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + SR.Format(SR.ArgumentOutOfRange_Range, 1, MaxYear)); + } + + if (day < 1 || day > GetDaysInMonth(year, month)) + { + throw new ArgumentOutOfRangeException(nameof(day), SR.Format(SR.ArgumentOutOfRange_Range, + 1, GetDaysInMonth(year, month))); + } + if (!IsLeapYear(year)) + { + return (false); + } + if (month == 2 && day == 29) + { + return (true); + } + return (false); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this calendar does not have leap month, or this year is not a leap year. + // + + public override int GetLeapMonth(int year, int era) + { + if (era != CurrentEra && era != ADEra) + { + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + if (year < 1 || year > MaxYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, 1, MaxYear)); + } + return (0); + } + + // Checks whether a given month in the specified era is a leap month. This method returns true if + // month is a leap month, or false if not. + // + + public override bool IsLeapMonth(int year, int month, int era) + { + if (era != CurrentEra && era != ADEra) + { + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + + if (year < 1 || year > MaxYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, 1, MaxYear)); + } + + if (month < 1 || month > 12) + { + throw new ArgumentOutOfRangeException(nameof(month), SR.Format(SR.ArgumentOutOfRange_Range, + 1, 12)); + } + return (false); + } + + // Checks whether a given year in the specified era is a leap year. This method returns true if + // year is a leap year, or false if not. + // + + public override bool IsLeapYear(int year, int era) + { + if (era == CurrentEra || era == ADEra) + { + if (year >= 1 && year <= MaxYear) + { + return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); + } + + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, 1, MaxYear)); + } + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + + // Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid. + // + + public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) + { + if (era == CurrentEra || era == ADEra) + { + return new DateTime(year, month, day, hour, minute, second, millisecond); + } + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + + internal override Boolean TryToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era, out DateTime result) + { + if (era == CurrentEra || era == ADEra) + { + try + { + result = new DateTime(year, month, day, hour, minute, second, millisecond); + return true; + } + catch (ArgumentOutOfRangeException) + { + result = DateTime.Now; + return false; + } + catch (ArgumentException) + { + result = DateTime.Now; + return false; + } + } + result = DateTime.MinValue; + return false; + } + + private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 2029; + + + public override int TwoDigitYearMax + { + get + { + if (twoDigitYearMax == -1) + { + twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX); + } + return (twoDigitYearMax); + } + + set + { + VerifyWritable(); + if (value < 99 || value > MaxYear) + { + throw new ArgumentOutOfRangeException( + "year", + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 99, + MaxYear)); + } + twoDigitYearMax = value; + } + } + + + public override int ToFourDigitYear(int year) + { + if (year < 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedNonNegNum); + } + + if (year > MaxYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, 1, MaxYear)); + } + return (base.ToFourDigitYear(year)); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/GregorianCalendarHelper.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/GregorianCalendarHelper.cs new file mode 100644 index 0000000000..2842bd3a16 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/GregorianCalendarHelper.cs @@ -0,0 +1,651 @@ +// 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.Threading; + +namespace System.Globalization +{ + // Gregorian Calendars use Era Info + internal class EraInfo + { + internal int era; // The value of the era. + internal long ticks; // The time in ticks when the era starts + internal int yearOffset; // The offset to Gregorian year when the era starts. + // Gregorian Year = Era Year + yearOffset + // Era Year = Gregorian Year - yearOffset + internal int minEraYear; // Min year value in this era. Generally, this value is 1, but this may + // be affected by the DateTime.MinValue; + internal int maxEraYear; // Max year value in this era. (== the year length of the era + 1) + + internal String eraName; // The era name + internal String abbrevEraName; // Abbreviated Era Name + internal String englishEraName; // English era name + + internal EraInfo(int era, int startYear, int startMonth, int startDay, int yearOffset, int minEraYear, int maxEraYear) + { + this.era = era; + this.yearOffset = yearOffset; + this.minEraYear = minEraYear; + this.maxEraYear = maxEraYear; + this.ticks = new DateTime(startYear, startMonth, startDay).Ticks; + } + + internal EraInfo(int era, int startYear, int startMonth, int startDay, int yearOffset, int minEraYear, int maxEraYear, + String eraName, String abbrevEraName, String englishEraName) + { + this.era = era; + this.yearOffset = yearOffset; + this.minEraYear = minEraYear; + this.maxEraYear = maxEraYear; + this.ticks = new DateTime(startYear, startMonth, startDay).Ticks; + this.eraName = eraName; + this.abbrevEraName = abbrevEraName; + this.englishEraName = englishEraName; + } + } + + // This calendar recognizes two era values: + // 0 CurrentEra (AD) + // 1 BeforeCurrentEra (BC) + internal class GregorianCalendarHelper + { + // 1 tick = 100ns = 10E-7 second + // Number of ticks per time unit + internal const long TicksPerMillisecond = 10000; + internal const long TicksPerSecond = TicksPerMillisecond * 1000; + internal const long TicksPerMinute = TicksPerSecond * 60; + internal const long TicksPerHour = TicksPerMinute * 60; + internal const long TicksPerDay = TicksPerHour * 24; + + // Number of milliseconds per time unit + internal const int MillisPerSecond = 1000; + internal const int MillisPerMinute = MillisPerSecond * 60; + internal const int MillisPerHour = MillisPerMinute * 60; + internal const int MillisPerDay = MillisPerHour * 24; + + // Number of days in a non-leap year + internal const int DaysPerYear = 365; + // Number of days in 4 years + internal const int DaysPer4Years = DaysPerYear * 4 + 1; + // Number of days in 100 years + internal const int DaysPer100Years = DaysPer4Years * 25 - 1; + // Number of days in 400 years + internal const int DaysPer400Years = DaysPer100Years * 4 + 1; + + // Number of days from 1/1/0001 to 1/1/10000 + internal const int DaysTo10000 = DaysPer400Years * 25 - 366; + + internal const long MaxMillis = (long)DaysTo10000 * MillisPerDay; + + internal const int DatePartYear = 0; + internal const int DatePartDayOfYear = 1; + internal const int DatePartMonth = 2; + internal const int DatePartDay = 3; + + // + // This is the max Gregorian year can be represented by DateTime class. The limitation + // is derived from DateTime class. + // + internal int MaxYear + { + get + { + return (m_maxYear); + } + } + + internal static readonly int[] DaysToMonth365 = + { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 + }; + + internal static readonly int[] DaysToMonth366 = + { + 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 + }; + + internal int m_maxYear = 9999; + internal int m_minYear; + internal Calendar m_Cal; + + internal EraInfo[] m_EraInfo; + internal int[] m_eras = null; + + + // Construct an instance of gregorian calendar. + internal GregorianCalendarHelper(Calendar cal, EraInfo[] eraInfo) + { + m_Cal = cal; + m_EraInfo = eraInfo; + m_maxYear = m_EraInfo[0].maxEraYear; + m_minYear = m_EraInfo[0].minEraYear; ; + } + + /*=================================GetGregorianYear========================== + **Action: Get the Gregorian year value for the specified year in an era. + **Returns: The Gregorian year value. + **Arguments: + ** year the year value in Japanese calendar + ** era the Japanese emperor era value. + **Exceptions: + ** ArgumentOutOfRangeException if year value is invalid or era value is invalid. + ============================================================================*/ + + internal int GetGregorianYear(int year, int era) + { + if (year < 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedNonNegNum); + } + + if (era == Calendar.CurrentEra) + { + era = m_Cal.CurrentEraValue; + } + + for (int i = 0; i < m_EraInfo.Length; i++) + { + if (era == m_EraInfo[i].era) + { + if (year < m_EraInfo[i].minEraYear || year > m_EraInfo[i].maxEraYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + m_EraInfo[i].minEraYear, + m_EraInfo[i].maxEraYear)); + } + return (m_EraInfo[i].yearOffset + year); + } + } + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + + internal bool IsValidYear(int year, int era) + { + if (year < 0) + { + return false; + } + + if (era == Calendar.CurrentEra) + { + era = m_Cal.CurrentEraValue; + } + + for (int i = 0; i < m_EraInfo.Length; i++) + { + if (era == m_EraInfo[i].era) + { + if (year < m_EraInfo[i].minEraYear || year > m_EraInfo[i].maxEraYear) + { + return false; + } + return true; + } + } + return false; + } + + + // Returns a given date part of this DateTime. This method is used + // to compute the year, day-of-year, month, or day part. + internal virtual int GetDatePart(long ticks, int part) + { + CheckTicksRange(ticks); + // n = number of days since 1/1/0001 + int n = (int)(ticks / TicksPerDay); + // y400 = number of whole 400-year periods since 1/1/0001 + int y400 = n / DaysPer400Years; + // n = day number within 400-year period + n -= y400 * DaysPer400Years; + // y100 = number of whole 100-year periods within 400-year period + int y100 = n / DaysPer100Years; + // Last 100-year period has an extra day, so decrement result if 4 + if (y100 == 4) y100 = 3; + // n = day number within 100-year period + n -= y100 * DaysPer100Years; + // y4 = number of whole 4-year periods within 100-year period + int y4 = n / DaysPer4Years; + // n = day number within 4-year period + n -= y4 * DaysPer4Years; + // y1 = number of whole years within 4-year period + int y1 = n / DaysPerYear; + // Last year has an extra day, so decrement result if 4 + if (y1 == 4) y1 = 3; + // If year was requested, compute and return it + if (part == DatePartYear) + { + return (y400 * 400 + y100 * 100 + y4 * 4 + y1 + 1); + } + // n = day number within year + n -= y1 * DaysPerYear; + // If day-of-year was requested, return it + if (part == DatePartDayOfYear) + { + return (n + 1); + } + // Leap year calculation looks different from IsLeapYear since y1, y4, + // and y100 are relative to year 1, not year 0 + bool leapYear = (y1 == 3 && (y4 != 24 || y100 == 3)); + int[] days = leapYear ? DaysToMonth366 : DaysToMonth365; + // All months have less than 32 days, so n >> 5 is a good conservative + // estimate for the month + int m = (n >> 5) + 1; + // m = 1-based month number + while (n >= days[m]) m++; + // If month was requested, return it + if (part == DatePartMonth) return (m); + // Return 1-based day-of-month + return (n - days[m - 1] + 1); + } + + /*=================================GetAbsoluteDate========================== + **Action: Gets the absolute date for the given Gregorian date. The absolute date means + ** the number of days from January 1st, 1 A.D. + **Returns: the absolute date + **Arguments: + ** year the Gregorian year + ** month the Gregorian month + ** day the day + **Exceptions: + ** ArgumentOutOfRangException if year, month, day value is valid. + **Note: + ** This is an internal method used by DateToTicks() and the calculations of Hijri and Hebrew calendars. + ** Number of Days in Prior Years (both common and leap years) + + ** Number of Days in Prior Months of Current Year + + ** Number of Days in Current Month + ** + ============================================================================*/ + + internal static long GetAbsoluteDate(int year, int month, int day) + { + if (year >= 1 && year <= 9999 && month >= 1 && month <= 12) + { + int[] days = ((year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))) ? DaysToMonth366 : DaysToMonth365; + if (day >= 1 && (day <= days[month] - days[month - 1])) + { + int y = year - 1; + int absoluteDate = y * 365 + y / 4 - y / 100 + y / 400 + days[month - 1] + day - 1; + return (absoluteDate); + } + } + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay); + } + + // Returns the tick count corresponding to the given year, month, and day. + // Will check the if the parameters are valid. + internal static long DateToTicks(int year, int month, int day) + { + return (GetAbsoluteDate(year, month, day) * TicksPerDay); + } + + // Return the tick count corresponding to the given hour, minute, second. + // Will check the if the parameters are valid. + internal static long TimeToTicks(int hour, int minute, int second, int millisecond) + { + //TimeSpan.TimeToTicks is a family access function which does no error checking, so + //we need to put some error checking out here. + if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >= 0 && second < 60) + { + if (millisecond < 0 || millisecond >= MillisPerSecond) + { + throw new ArgumentOutOfRangeException( + nameof(millisecond), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 0, + MillisPerSecond - 1)); + } + return (InternalGlobalizationHelper.TimeToTicks(hour, minute, second) + millisecond * TicksPerMillisecond); ; + } + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadHourMinuteSecond); + } + + + internal void CheckTicksRange(long ticks) + { + if (ticks < m_Cal.MinSupportedDateTime.Ticks || ticks > m_Cal.MaxSupportedDateTime.Ticks) + { + throw new ArgumentOutOfRangeException( + "time", + String.Format( + CultureInfo.InvariantCulture, + SR.ArgumentOutOfRange_CalendarRange, + m_Cal.MinSupportedDateTime, + m_Cal.MaxSupportedDateTime)); + } + } + + // Returns the DateTime resulting from adding the given number of + // months to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year and month parts of the specified DateTime by + // value months, and, if required, adjusting the day part of the + // resulting date downwards to the last day of the resulting month in the + // resulting year. The time-of-day part of the result is the same as the + // time-of-day part of the specified DateTime. + // + // In more precise terms, considering the specified DateTime to be of the + // form y / m / d + t, where y is the + // year, m is the month, d is the day, and t is the + // time-of-day, the result is y1 / m1 / d1 + t, + // where y1 and m1 are computed by adding value months + // to y and m, and d1 is the largest value less than + // or equal to d that denotes a valid day in month m1 of year + // y1. + // + public DateTime AddMonths(DateTime time, int months) + { + if (months < -120000 || months > 120000) + { + throw new ArgumentOutOfRangeException( + nameof(months), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + -120000, + 120000)); + } + CheckTicksRange(time.Ticks); + + int y = GetDatePart(time.Ticks, DatePartYear); + int m = GetDatePart(time.Ticks, DatePartMonth); + int d = GetDatePart(time.Ticks, DatePartDay); + int i = m - 1 + months; + if (i >= 0) + { + m = i % 12 + 1; + y = y + i / 12; + } + else + { + m = 12 + (i + 1) % 12; + y = y + (i - 11) / 12; + } + int[] daysArray = (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) ? DaysToMonth366 : DaysToMonth365; + int days = (daysArray[m] - daysArray[m - 1]); + + if (d > days) + { + d = days; + } + long ticks = DateToTicks(y, m, d) + (time.Ticks % TicksPerDay); + Calendar.CheckAddResult(ticks, m_Cal.MinSupportedDateTime, m_Cal.MaxSupportedDateTime); + return (new DateTime(ticks)); + } + + // Returns the DateTime resulting from adding the given number of + // years to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year part of the specified DateTime by value + // years. If the month and day of the specified DateTime is 2/29, and if the + // resulting year is not a leap year, the month and day of the resulting + // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day + // parts of the result are the same as those of the specified DateTime. + // + public DateTime AddYears(DateTime time, int years) + { + return (AddMonths(time, years * 12)); + } + + // Returns the day-of-month part of the specified DateTime. The returned + // value is an integer between 1 and 31. + // + public int GetDayOfMonth(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartDay)); + } + + // Returns the day-of-week part of the specified DateTime. The returned value + // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates + // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates + // Thursday, 5 indicates Friday, and 6 indicates Saturday. + // + public DayOfWeek GetDayOfWeek(DateTime time) + { + CheckTicksRange(time.Ticks); + return ((DayOfWeek)((time.Ticks / TicksPerDay + 1) % 7)); + } + + // Returns the day-of-year part of the specified DateTime. The returned value + // is an integer between 1 and 366. + // + public int GetDayOfYear(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartDayOfYear)); + } + + // Returns the number of days in the month given by the year and + // month arguments. + // + public int GetDaysInMonth(int year, int month, int era) + { + // + // Convert year/era value to Gregorain year value. + // + year = GetGregorianYear(year, era); + if (month < 1 || month > 12) + { + throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month); + } + int[] days = ((year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ? DaysToMonth366 : DaysToMonth365); + return (days[month] - days[month - 1]); + } + + // Returns the number of days in the year given by the year argument for the current era. + // + + public int GetDaysInYear(int year, int era) + { + // + // Convert year/era value to Gregorain year value. + // + year = GetGregorianYear(year, era); + return ((year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ? 366 : 365); + } + + // Returns the era for the specified DateTime value. + public int GetEra(DateTime time) + { + long ticks = time.Ticks; + // The assumption here is that m_EraInfo is listed in reverse order. + for (int i = 0; i < m_EraInfo.Length; i++) + { + if (ticks >= m_EraInfo[i].ticks) + { + return (m_EraInfo[i].era); + } + } + throw new ArgumentOutOfRangeException(nameof(time), SR.ArgumentOutOfRange_Era); + } + + + public int[] Eras + { + get + { + if (m_eras == null) + { + m_eras = new int[m_EraInfo.Length]; + for (int i = 0; i < m_EraInfo.Length; i++) + { + m_eras[i] = m_EraInfo[i].era; + } + } + return ((int[])m_eras.Clone()); + } + } + + // Returns the month part of the specified DateTime. The returned value is an + // integer between 1 and 12. + // + public int GetMonth(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartMonth)); + } + + // Returns the number of months in the specified year and era. + public int GetMonthsInYear(int year, int era) + { + year = GetGregorianYear(year, era); + return (12); + } + + // Returns the year part of the specified DateTime. The returned value is an + // integer between 1 and 9999. + // + public int GetYear(DateTime time) + { + long ticks = time.Ticks; + int year = GetDatePart(ticks, DatePartYear); + for (int i = 0; i < m_EraInfo.Length; i++) + { + if (ticks >= m_EraInfo[i].ticks) + { + return (year - m_EraInfo[i].yearOffset); + } + } + throw new ArgumentException(SR.Argument_NoEra); + } + + // Returns the year that match the specified Gregorian year. The returned value is an + // integer between 1 and 9999. + // + public int GetYear(int year, DateTime time) + { + long ticks = time.Ticks; + for (int i = 0; i < m_EraInfo.Length; i++) + { + // while calculating dates with JapaneseLuniSolarCalendar, we can run into cases right after the start of the era + // and still belong to the month which is started in previous era. Calculating equivalent calendar date will cause + // using the new era info which will have the year offset equal to the year we are calculating year = m_EraInfo[i].yearOffset + // which will end up with zero as calendar year. + // We should use the previous era info instead to get the right year number. Example of such date is Feb 2nd 1989 + if (ticks >= m_EraInfo[i].ticks && year > m_EraInfo[i].yearOffset) + { + return (year - m_EraInfo[i].yearOffset); + } + } + throw new ArgumentException(SR.Argument_NoEra); + } + + // Checks whether a given day in the specified era is a leap day. This method returns true if + // the date is a leap day, or false if not. + // + public bool IsLeapDay(int year, int month, int day, int era) + { + // year/month/era checking is done in GetDaysInMonth() + if (day < 1 || day > GetDaysInMonth(year, month, era)) + { + throw new ArgumentOutOfRangeException( + nameof(day), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + GetDaysInMonth(year, month, era))); + } + + if (!IsLeapYear(year, era)) + { + return (false); + } + + if (month == 2 && day == 29) + { + return (true); + } + + return (false); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this calendar does not have leap month, or this year is not a leap year. + // + public int GetLeapMonth(int year, int era) + { + year = GetGregorianYear(year, era); + return (0); + } + + // Checks whether a given month in the specified era is a leap month. This method returns true if + // month is a leap month, or false if not. + // + public bool IsLeapMonth(int year, int month, int era) + { + year = GetGregorianYear(year, era); + if (month < 1 || month > 12) + { + throw new ArgumentOutOfRangeException( + nameof(month), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + 12)); + } + return (false); + } + + // Checks whether a given year in the specified era is a leap year. This method returns true if + // year is a leap year, or false if not. + // + public bool IsLeapYear(int year, int era) + { + year = GetGregorianYear(year, era); + return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); + } + + // Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid. + // + public DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) + { + year = GetGregorianYear(year, era); + long ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second, millisecond); + CheckTicksRange(ticks); + return (new DateTime(ticks)); + } + + public virtual int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek) + { + CheckTicksRange(time.Ticks); + // Use GregorianCalendar to get around the problem that the implmentation in Calendar.GetWeekOfYear() + // can call GetYear() that exceeds the supported range of the Gregorian-based calendars. + return (GregorianCalendar.GetDefaultInstance().GetWeekOfYear(time, rule, firstDayOfWeek)); + } + + + public int ToFourDigitYear(int year, int twoDigitYearMax) + { + if (year < 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedPosNum); + } + + if (year < 100) + { + int y = year % 100; + return ((twoDigitYearMax / 100 - (y > twoDigitYearMax % 100 ? 1 : 0)) * 100 + y); + } + + if (year < m_minYear || year > m_maxYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, m_minYear, m_maxYear)); + } + // If the year value is above 100, just return the year value. Don't have to do + // the TwoDigitYearMax comparison. + return (year); + } + } +} + diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/HebrewCalendar.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/HebrewCalendar.cs index 6ba4f082f1..533d3831bf 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/HebrewCalendar.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/HebrewCalendar.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; namespace System.Globalization { @@ -536,9 +535,7 @@ namespace System.Globalization // // Save the Gregorian date values. // - gregorianYear = time.Year; - gregorianMonth = time.Month; - gregorianDay = time.Day; + time.GetDatePart(out gregorianYear, out gregorianMonth, out gregorianDay); __DateBuffer lunarDate = new __DateBuffer(); // lunar month and day for Jan 1 @@ -1097,7 +1094,6 @@ namespace System.Globalization throw new ArgumentOutOfRangeException(nameof(year), SR.ArgumentOutOfRange_NeedNonNegNum); } - Contract.EndContractBlock(); if (year < 100) { diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/HijriCalendar.Win32.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/HijriCalendar.Win32.cs index 869b809bff..09b1f20c48 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/HijriCalendar.Win32.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/HijriCalendar.Win32.cs @@ -45,7 +45,6 @@ namespace System.Globalization try { // Open in read-only mode. - // Use InternalOpenSubKey so that we avoid the security check. key = RegistryKey.GetBaseKey(RegistryKey.HKEY_CURRENT_USER).OpenSubKey(InternationalRegKey, false); } //If this fails for any reason, we'll just return 0. @@ -68,10 +67,9 @@ namespace System.Globalization hijriAdvance = -1; else { - str = str.Substring(HijriAdvanceRegKeyEntry.Length); try { - int advance = Int32.Parse(str.ToString(), CultureInfo.InvariantCulture); + int advance = Int32.Parse(str.AsReadOnlySpan().Slice(HijriAdvanceRegKeyEntry.Length), provider:CultureInfo.InvariantCulture); if ((advance >= MinAdvancedHijri) && (advance <= MaxAdvancedHijri)) { hijriAdvance = advance; diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/HijriCalendar.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/HijriCalendar.cs index 125248a685..6755844620 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/HijriCalendar.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/HijriCalendar.cs @@ -2,7 +2,6 @@ // 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.Diagnostics.Contracts; namespace System.Globalization { @@ -187,7 +186,7 @@ namespace System.Globalization set { - // NOTE: Check the value of Min/MaxAdavncedHijri with Arabic speakers to see if the assumption is good. + // NOTE: Check the value of Min/MaxAdvancedHijri with Arabic speakers to see if the assumption is good. if (value < MinAdvancedHijri || value > MaxAdvancedHijri) { throw new ArgumentOutOfRangeException( @@ -198,7 +197,6 @@ namespace System.Globalization MinAdvancedHijri, MaxAdvancedHijri)); } - Contract.EndContractBlock(); VerifyWritable(); _hijriAdvance = value; @@ -303,7 +301,7 @@ namespace System.Globalization // HijriYear = (int)(((NumDays - 227013) * 30) / 10631) + 1; - long daysToHijriYear = DaysUpToHijriYear(HijriYear); // The absoulte date for HijriYear + long daysToHijriYear = DaysUpToHijriYear(HijriYear); // The absolute date for HijriYear long daysOfHijriYear = GetDaysInYear(HijriYear, CurrentEra); // The number of days for (HijriYear+1) year. if (NumDays < daysToHijriYear) @@ -395,7 +393,6 @@ namespace System.Globalization -120000, 120000)); } - Contract.EndContractBlock(); // Get the date in Hijri calendar. int y = GetDatePart(time.Ticks, DatePartYear); int m = GetDatePart(time.Ticks, DatePartMonth); @@ -467,7 +464,6 @@ namespace System.Globalization // Returns the number of days in the month given by the year and // month arguments. // - [Pure] public override int GetDaysInMonth(int year, int month, int era) { CheckYearMonthRange(year, month, era); @@ -653,7 +649,6 @@ namespace System.Globalization throw new ArgumentOutOfRangeException(nameof(year), SR.ArgumentOutOfRange_NeedNonNegNum); } - Contract.EndContractBlock(); if (year < 100) { diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/IdnMapping.Windows.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/IdnMapping.Windows.cs new file mode 100644 index 0000000000..35da7343e7 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/IdnMapping.Windows.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. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Globalization +{ + public sealed partial class IdnMapping + { + private unsafe string GetAsciiCore(char* unicode, int count) + { + Debug.Assert(!GlobalizationMode.Invariant); + + uint flags = Flags; + + // Determine the required length + int length = Interop.Normaliz.IdnToAscii(flags, unicode, count, null, 0); + if (length == 0) + { + ThrowForZeroLength(unicode: true); + } + + // Do the conversion + const int StackAllocThreshold = 512; // arbitrary limit to switch from stack to heap allocation + if (length < StackAllocThreshold) + { + char* output = stackalloc char[length]; + return GetAsciiCore(unicode, count, flags, output, length); + } + else + { + char[] output = new char[length]; + fixed (char* pOutput = &output[0]) + { + return GetAsciiCore(unicode, count, flags, pOutput, length); + } + } + } + + private unsafe string GetAsciiCore(char* unicode, int count, uint flags, char* output, int outputLength) + { + Debug.Assert(!GlobalizationMode.Invariant); + + int length = Interop.Normaliz.IdnToAscii(flags, unicode, count, output, outputLength); + if (length == 0) + { + ThrowForZeroLength(unicode: true); + } + Debug.Assert(length == outputLength); + return new string(output, 0, length); + } + + private unsafe string GetUnicodeCore(char* ascii, int count) + { + Debug.Assert(!GlobalizationMode.Invariant); + + uint flags = Flags; + + // Determine the required length + int length = Interop.Normaliz.IdnToUnicode(flags, ascii, count, null, 0); + if (length == 0) + { + ThrowForZeroLength(unicode: false); + } + + // Do the conversion + const int StackAllocThreshold = 512; // arbitrary limit to switch from stack to heap allocation + if (length < StackAllocThreshold) + { + char* output = stackalloc char[length]; + return GetUnicodeCore(ascii, count, flags, output, length); + } + else + { + char[] output = new char[length]; + fixed (char* pOutput = &output[0]) + { + return GetUnicodeCore(ascii, count, flags, pOutput, length); + } + } + } + + private unsafe string GetUnicodeCore(char* ascii, int count, uint flags, char* output, int outputLength) + { + Debug.Assert(!GlobalizationMode.Invariant); + + int length = Interop.Normaliz.IdnToUnicode(flags, ascii, count, output, outputLength); + if (length == 0) + { + ThrowForZeroLength(unicode: false); + } + Debug.Assert(length == outputLength); + return new string(output, 0, length); + } + + // ----------------------------- + // ---- PAL layer ends here ---- + // ----------------------------- + + private uint Flags + { + get + { + int flags = + (AllowUnassigned ? Interop.Normaliz.IDN_ALLOW_UNASSIGNED : 0) | + (UseStd3AsciiRules ? Interop.Normaliz.IDN_USE_STD3_ASCII_RULES : 0); + return (uint)flags; + } + } + + private static void ThrowForZeroLength(bool unicode) + { + int lastError = Marshal.GetLastWin32Error(); + + throw new ArgumentException( + lastError == Interop.Errors.ERROR_INVALID_NAME ? SR.Argument_IdnIllegalName : + (unicode ? SR.Argument_InvalidCharSequenceNoIndex : SR.Argument_IdnBadPunycode), + unicode ? "unicode" : "ascii"); + } + } +} + diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/IdnMapping.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/IdnMapping.cs new file mode 100644 index 0000000000..176e5feed5 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/IdnMapping.cs @@ -0,0 +1,893 @@ +// 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. + +// This file contains the IDN functions and implementation. +// +// This allows encoding of non-ASCII domain names in a "punycode" form, +// for example: +// +// \u5B89\u5BA4\u5948\u7F8E\u6075-with-SUPER-MONKEYS +// +// is encoded as: +// +// xn---with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n +// +// Additional options are provided to allow unassigned IDN characters and +// to validate according to the Std3ASCII Rules (like DNS names). +// +// There are also rules regarding bidirectionality of text and the length +// of segments. +// +// For additional rules see also: +// RFC 3490 - Internationalizing Domain Names in Applications (IDNA) +// RFC 3491 - Nameprep: A Stringprep Profile for Internationalized Domain Names (IDN) +// RFC 3492 - Punycode: A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA) + +using System.Diagnostics; +using System.Text; + +namespace System.Globalization +{ + // IdnMapping class used to map names to Punycode + public sealed partial class IdnMapping + { + private bool _allowUnassigned; + private bool _useStd3AsciiRules; + + public IdnMapping() + { + } + + public bool AllowUnassigned + { + get { return _allowUnassigned; } + set { _allowUnassigned = value; } + } + + public bool UseStd3AsciiRules + { + get { return _useStd3AsciiRules; } + set { _useStd3AsciiRules = value; } + } + + // Gets ASCII (Punycode) version of the string + public string GetAscii(string unicode) + { + return GetAscii(unicode, 0); + } + + public string GetAscii(string unicode, int index) + { + if (unicode == null) + throw new ArgumentNullException(nameof(unicode)); + return GetAscii(unicode, index, unicode.Length - index); + } + + public string GetAscii(string unicode, int index, int count) + { + if (unicode == null) + throw new ArgumentNullException(nameof(unicode)); + if (index < 0 || count < 0) + throw new ArgumentOutOfRangeException((index < 0) ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + if (index > unicode.Length) + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + if (index > unicode.Length - count) + throw new ArgumentOutOfRangeException(nameof(unicode), SR.ArgumentOutOfRange_IndexCountBuffer); + + if (count == 0) + { + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(unicode)); + } + if (unicode[index + count - 1] == 0) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidCharSequence, index + count - 1), nameof(unicode)); + } + + if (GlobalizationMode.Invariant) + { + return GetAsciiInvariant(unicode, index, count); + } + + unsafe + { + fixed (char* pUnicode = unicode) + { + return GetAsciiCore(pUnicode + index, count); + } + } + } + + // Gets Unicode version of the string. Normalized and limited to IDNA characters. + public string GetUnicode(string ascii) + { + return GetUnicode(ascii, 0); + } + + public string GetUnicode(string ascii, int index) + { + if (ascii == null) + throw new ArgumentNullException(nameof(ascii)); + return GetUnicode(ascii, index, ascii.Length - index); + } + + public string GetUnicode(string ascii, int index, int count) + { + if (ascii == null) + throw new ArgumentNullException(nameof(ascii)); + if (index < 0 || count < 0) + throw new ArgumentOutOfRangeException((index < 0) ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + if (index > ascii.Length) + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + if (index > ascii.Length - count) + throw new ArgumentOutOfRangeException(nameof(ascii), SR.ArgumentOutOfRange_IndexCountBuffer); + + // This is a case (i.e. explicitly null-terminated input) where behavior in .NET and Win32 intentionally differ. + // The .NET APIs should (and did in v4.0 and earlier) throw an ArgumentException on input that includes a terminating null. + // The Win32 APIs fail on an embedded null, but not on a terminating null. + if (count > 0 && ascii[index + count - 1] == (char)0) + throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(ascii)); + + if (GlobalizationMode.Invariant) + { + return GetUnicodeInvariant(ascii, index, count); + } + + unsafe + { + fixed (char* pAscii = ascii) + { + return GetUnicodeCore(pAscii + index, count); + } + } + } + + public override bool Equals(object obj) + { + IdnMapping that = obj as IdnMapping; + return + that != null && + _allowUnassigned == that._allowUnassigned && + _useStd3AsciiRules == that._useStd3AsciiRules; + } + + public override int GetHashCode() + { + return (_allowUnassigned ? 100 : 200) + (_useStd3AsciiRules ? 1000 : 2000); + } + + // + // Invariant implementation + // + + private const char c_delimiter = '-'; + private const string c_strAcePrefix = "xn--"; + private const int c_labelLimit = 63; // Not including dots + private const int c_defaultNameLimit = 255; // Including dots + private const int c_initialN = 0x80; + private const int c_maxint = 0x7ffffff; + private const int c_initialBias = 72; + private const int c_punycodeBase = 36; + private const int c_tmin = 1; + private const int c_tmax = 26; + private const int c_skew = 38; + private const int c_damp = 700; + + + // Legal "dot" separators (i.e: . in www.microsoft.com) + private static char[] c_Dots = { '.', '\u3002', '\uFF0E', '\uFF61' }; + + private string GetAsciiInvariant(string unicode, int index, int count) + { + if (index > 0 || count < unicode.Length) + { + unicode = unicode.Substring(index, count); + } + + // Check for ASCII only string, which will be unchanged + if (ValidateStd3AndAscii(unicode, UseStd3AsciiRules, true)) + { + return unicode; + } + + // Cannot be null terminated (normalization won't help us with this one, and + // may have returned false before checking the whole string above) + Debug.Assert(count >= 1, "[IdnMapping.GetAscii] Expected 0 length strings to fail before now."); + if (unicode[unicode.Length - 1] <= 0x1f) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidCharSequence, unicode.Length - 1), nameof(unicode)); + } + + // Have to correctly IDNA normalize the string and Unassigned flags + bool bHasLastDot = (unicode.Length > 0) && IsDot(unicode[unicode.Length - 1]); + + // Make sure we didn't normalize away something after a last dot + if ((!bHasLastDot) && unicode.Length > 0 && IsDot(unicode[unicode.Length - 1])) + { + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(unicode)); + } + + // May need to check Std3 rules again for non-ascii + if (UseStd3AsciiRules) + { + ValidateStd3AndAscii(unicode, true, false); + } + + // Go ahead and encode it + return PunycodeEncode(unicode); + } + + // See if we're only ASCII + static bool ValidateStd3AndAscii(string unicode, bool bUseStd3, bool bCheckAscii) + { + // If its empty, then its too small + if (unicode.Length == 0) + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(unicode)); + + int iLastDot = -1; + + // Loop the whole string + for (int i = 0; i < unicode.Length; i++) + { + // Aren't allowing control chars (or 7f, but idn tables catch that, they don't catch \0 at end though) + if (unicode[i] <= 0x1f) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidCharSequence, i ), nameof(unicode)); + } + + // If its Unicode or a control character, return false (non-ascii) + if (bCheckAscii && unicode[i] >= 0x7f) + return false; + + // Check for dots + if (IsDot(unicode[i])) + { + // Can't have 2 dots in a row + if (i == iLastDot + 1) + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(unicode)); + + // If its too far between dots then fail + if (i - iLastDot > c_labelLimit + 1) + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(unicode)); + + // If validating Std3, then char before dot can't be - char + if (bUseStd3 && i > 0) + ValidateStd3(unicode[i - 1], true); + + // Remember where the last dot is + iLastDot = i; + continue; + } + + // If necessary, make sure its a valid std3 character + if (bUseStd3) + { + ValidateStd3(unicode[i], (i == iLastDot + 1)); + } + } + + // If we never had a dot, then we need to be shorter than the label limit + if (iLastDot == -1 && unicode.Length > c_labelLimit) + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(unicode)); + + // Need to validate entire string length, 1 shorter if last char wasn't a dot + if (unicode.Length > c_defaultNameLimit - (IsDot(unicode[unicode.Length - 1]) ? 0 : 1)) + throw new ArgumentException(SR.Format(SR.Argument_IdnBadNameSize, + c_defaultNameLimit - (IsDot(unicode[unicode.Length - 1]) ? 0 : 1)), nameof(unicode)); + + // If last char wasn't a dot we need to check for trailing - + if (bUseStd3 && !IsDot(unicode[unicode.Length - 1])) + ValidateStd3(unicode[unicode.Length - 1], true); + + return true; + } + + /* PunycodeEncode() converts Unicode to Punycode. The input */ + /* is represented as an array of Unicode code points (not code */ + /* units; surrogate pairs are not allowed), and the output */ + /* will be represented as an array of ASCII code points. The */ + /* output string is *not* null-terminated; it will contain */ + /* zeros if and only if the input contains zeros. (Of course */ + /* the caller can leave room for a terminator and add one if */ + /* needed.) The input_length is the number of code points in */ + /* the input. The output_length is an in/out argument: the */ + /* caller passes in the maximum number of code points that it */ + + /* can receive, and on successful return it will contain the */ + /* number of code points actually output. The case_flags array */ + /* holds input_length boolean values, where nonzero suggests that */ + /* the corresponding Unicode character be forced to uppercase */ + /* after being decoded (if possible), and zero suggests that */ + /* it be forced to lowercase (if possible). ASCII code points */ + /* are encoded literally, except that ASCII letters are forced */ + /* to uppercase or lowercase according to the corresponding */ + /* uppercase flags. If case_flags is a null pointer then ASCII */ + /* letters are left as they are, and other code points are */ + /* treated as if their uppercase flags were zero. The return */ + /* value can be any of the punycode_status values defined above */ + /* except punycode_bad_input; if not punycode_success, then */ + /* output_size and output might contain garbage. */ + static string PunycodeEncode(string unicode) + { + // 0 length strings aren't allowed + if (unicode.Length == 0) + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(unicode)); + + StringBuilder output = new StringBuilder(unicode.Length); + int iNextDot = 0; + int iAfterLastDot = 0; + int iOutputAfterLastDot = 0; + + // Find the next dot + while (iNextDot < unicode.Length) + { + // Find end of this segment + iNextDot = unicode.IndexOfAny(c_Dots, iAfterLastDot); + Debug.Assert(iNextDot <= unicode.Length, "[IdnMapping.punycode_encode]IndexOfAny is broken"); + if (iNextDot < 0) + iNextDot = unicode.Length; + + // Only allowed to have empty . section at end (www.microsoft.com.) + if (iNextDot == iAfterLastDot) + { + // Only allowed to have empty sections as trailing . + if (iNextDot != unicode.Length) + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(unicode)); + // Last dot, stop + break; + } + + // We'll need an Ace prefix + output.Append(c_strAcePrefix); + + // Everything resets every segment. + bool bRightToLeft = false; + + // Check for RTL. If right-to-left, then 1st & last chars must be RTL + BidiCategory eBidi = CharUnicodeInfo.GetBidiCategory(unicode, iAfterLastDot); + if (eBidi == BidiCategory.RightToLeft || eBidi == BidiCategory.RightToLeftArabic) + { + // It has to be right to left. + bRightToLeft = true; + + // Check last char + int iTest = iNextDot - 1; + if (Char.IsLowSurrogate(unicode, iTest)) + { + iTest--; + } + + eBidi = CharUnicodeInfo.GetBidiCategory(unicode, iTest); + if (eBidi != BidiCategory.RightToLeft && eBidi != BidiCategory.RightToLeftArabic) + { + // Oops, last wasn't RTL, last should be RTL if first is RTL + throw new ArgumentException(SR.Argument_IdnBadBidi, nameof(unicode)); + } + } + + // Handle the basic code points + int basicCount; + int numProcessed = 0; // Num code points that have been processed so far (this segment) + for (basicCount = iAfterLastDot; basicCount < iNextDot; basicCount++) + { + // Can't be lonely surrogate because it would've thrown in normalization + Debug.Assert(Char.IsLowSurrogate(unicode, basicCount) == false, "[IdnMapping.punycode_encode]Unexpected low surrogate"); + + // Double check our bidi rules + BidiCategory testBidi = CharUnicodeInfo.GetBidiCategory(unicode, basicCount); + + // If we're RTL, we can't have LTR chars + if (bRightToLeft && testBidi == BidiCategory.LeftToRight) + { + // Oops, throw error + throw new ArgumentException(SR.Argument_IdnBadBidi, nameof(unicode)); + } + + // If we're not RTL we can't have RTL chars + if (!bRightToLeft && (testBidi == BidiCategory.RightToLeft || testBidi == BidiCategory.RightToLeftArabic)) + { + // Oops, throw error + throw new ArgumentException(SR.Argument_IdnBadBidi, nameof(unicode)); + } + + // If its basic then add it + if (Basic(unicode[basicCount])) + { + output.Append(EncodeBasic(unicode[basicCount])); + numProcessed++; + } + // If its a surrogate, skip the next since our bidi category tester doesn't handle it. + else if (Char.IsSurrogatePair(unicode, basicCount)) + basicCount++; + } + + int numBasicCodePoints = numProcessed; // number of basic code points + + // Stop if we ONLY had basic code points + if (numBasicCodePoints == iNextDot - iAfterLastDot) + { + // Get rid of xn-- and this segments done + output.Remove(iOutputAfterLastDot, c_strAcePrefix.Length); + } + else + { + // If it has some non-basic code points the input cannot start with xn-- + if (unicode.Length - iAfterLastDot >= c_strAcePrefix.Length && + unicode.Substring(iAfterLastDot, c_strAcePrefix.Length).Equals( + c_strAcePrefix, StringComparison.OrdinalIgnoreCase)) + throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(unicode)); + + // Need to do ACE encoding + int numSurrogatePairs = 0; // number of surrogate pairs so far + + // Add a delimiter (-) if we had any basic code points (between basic and encoded pieces) + if (numBasicCodePoints > 0) + { + output.Append(c_delimiter); + } + + // Initialize the state + int n = c_initialN; + int delta = 0; + int bias = c_initialBias; + + // Main loop + while (numProcessed < (iNextDot - iAfterLastDot)) + { + /* All non-basic code points < n have been */ + /* handled already. Find the next larger one: */ + int j; + int m; + int test = 0; + for (m = c_maxint, j = iAfterLastDot; + j < iNextDot; + j += IsSupplementary(test) ? 2 : 1) + { + test = Char.ConvertToUtf32(unicode, j); + if (test >= n && test < m) m = test; + } + + /* Increase delta enough to advance the decoder's */ + /* state to , but guard against overflow: */ + delta += (int)((m - n) * ((numProcessed - numSurrogatePairs) + 1)); + Debug.Assert(delta > 0, "[IdnMapping.cs]1 punycode_encode - delta overflowed int"); + n = m; + + for (j = iAfterLastDot; j < iNextDot; j+= IsSupplementary(test) ? 2 : 1) + { + // Make sure we're aware of surrogates + test = Char.ConvertToUtf32(unicode, j); + + // Adjust for character position (only the chars in our string already, some + // haven't been processed. + + if (test < n) + { + delta++; + Debug.Assert(delta > 0, "[IdnMapping.cs]2 punycode_encode - delta overflowed int"); + } + + if (test == n) + { + // Represent delta as a generalized variable-length integer: + int q, k; + for (q = delta, k = c_punycodeBase; ; k += c_punycodeBase) + { + int t = k <= bias ? c_tmin : k >= bias + c_tmax ? c_tmax : k - bias; + if (q < t) break; + Debug.Assert(c_punycodeBase != t, "[IdnMapping.punycode_encode]Expected c_punycodeBase (36) to be != t"); + output.Append(EncodeDigit(t + (q - t) % (c_punycodeBase - t))); + q = (q - t) / (c_punycodeBase - t); + } + + output.Append(EncodeDigit(q)); + bias = Adapt(delta, (numProcessed - numSurrogatePairs) + 1, numProcessed == numBasicCodePoints); + delta = 0; + numProcessed++; + + if (IsSupplementary(m)) + { + numProcessed++; + numSurrogatePairs++; + } + } + } + ++delta; + ++n; + Debug.Assert(delta > 0, "[IdnMapping.cs]3 punycode_encode - delta overflowed int"); + } + } + + // Make sure its not too big + if (output.Length - iOutputAfterLastDot > c_labelLimit) + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(unicode)); + + // Done with this segment, add dot if necessary + if (iNextDot != unicode.Length) + output.Append('.'); + + iAfterLastDot = iNextDot + 1; + iOutputAfterLastDot = output.Length; + } + + // Throw if we're too long + if (output.Length > c_defaultNameLimit - (IsDot(unicode[unicode.Length-1]) ? 0 : 1)) + throw new ArgumentException(SR.Format(SR.Argument_IdnBadNameSize, + c_defaultNameLimit - (IsDot(unicode[unicode.Length-1]) ? 0 : 1)), nameof(unicode)); + // Return our output string + return output.ToString(); + } + + // Is it a dot? + // are we U+002E (., full stop), U+3002 (ideographic full stop), U+FF0E (fullwidth full stop), or + // U+FF61 (halfwidth ideographic full stop). + // Note: IDNA Normalization gets rid of dots now, but testing for last dot is before normalization + private static bool IsDot(char c) + { + return c == '.' || c == '\u3002' || c == '\uFF0E' || c == '\uFF61'; + } + + private static bool IsSupplementary(int cTest) + { + return cTest >= 0x10000; + } + + private static bool Basic(uint cp) + { + // Is it in ASCII range? + return cp < 0x80; + } + + // Validate Std3 rules for a character + private static void ValidateStd3(char c, bool bNextToDot) + { + // Check for illegal characters + if ((c <= ',' || c == '/' || (c >= ':' && c <= '@') || // Lots of characters not allowed + (c >= '[' && c <= '`') || (c >= '{' && c <= (char)0x7F)) || + (c == '-' && bNextToDot)) + throw new ArgumentException(SR.Format(SR.Argument_IdnBadStd3, c), nameof(c)); + } + + private string GetUnicodeInvariant(string ascii, int index, int count) + { + if (index > 0 || count < ascii.Length) + { + // We're only using part of the string + ascii = ascii.Substring(index, count); + } + // Convert Punycode to Unicode + string strUnicode = PunycodeDecode(ascii); + + // Output name MUST obey IDNA rules & round trip (casing differences are allowed) + if (!ascii.Equals(GetAscii(strUnicode), StringComparison.OrdinalIgnoreCase)) + throw new ArgumentException(SR.Argument_IdnIllegalName, nameof(ascii)); + + return strUnicode; + } + + /* PunycodeDecode() converts Punycode to Unicode. The input is */ + /* represented as an array of ASCII code points, and the output */ + /* will be represented as an array of Unicode code points. The */ + /* input_length is the number of code points in the input. The */ + /* output_length is an in/out argument: the caller passes in */ + /* the maximum number of code points that it can receive, and */ + /* on successful return it will contain the actual number of */ + /* code points output. The case_flags array needs room for at */ + /* least output_length values, or it can be a null pointer if the */ + /* case information is not needed. A nonzero flag suggests that */ + /* the corresponding Unicode character be forced to uppercase */ + /* by the caller (if possible), while zero suggests that it be */ + /* forced to lowercase (if possible). ASCII code points are */ + /* output already in the proper case, but their flags will be set */ + /* appropriately so that applying the flags would be harmless. */ + /* The return value can be any of the punycode_status values */ + /* defined above; if not punycode_success, then output_length, */ + /* output, and case_flags might contain garbage. On success, the */ + /* decoder will never need to write an output_length greater than */ + /* input_length, because of how the encoding is defined. */ + + private static string PunycodeDecode(string ascii) + { + // 0 length strings aren't allowed + if (ascii.Length == 0) + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(ascii)); + + // Throw if we're too long + if (ascii.Length > c_defaultNameLimit - (IsDot(ascii[ascii.Length-1]) ? 0 : 1)) + throw new ArgumentException(SR.Format(SR.Argument_IdnBadNameSize, + c_defaultNameLimit - (IsDot(ascii[ascii.Length-1]) ? 0 : 1)), nameof(ascii)); + + // output stringbuilder + StringBuilder output = new StringBuilder(ascii.Length); + + // Dot searching + int iNextDot = 0; + int iAfterLastDot = 0; + int iOutputAfterLastDot = 0; + + while (iNextDot < ascii.Length) + { + // Find end of this segment + iNextDot = ascii.IndexOf('.', iAfterLastDot); + if (iNextDot < 0 || iNextDot > ascii.Length) + iNextDot = ascii.Length; + + // Only allowed to have empty . section at end (www.microsoft.com.) + if (iNextDot == iAfterLastDot) + { + // Only allowed to have empty sections as trailing . + if (iNextDot != ascii.Length) + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(ascii)); + + // Last dot, stop + break; + } + + // In either case it can't be bigger than segment size + if (iNextDot - iAfterLastDot > c_labelLimit) + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(ascii)); + + // See if this section's ASCII or ACE + if (ascii.Length < c_strAcePrefix.Length + iAfterLastDot || + !ascii.Substring(iAfterLastDot,c_strAcePrefix.Length).Equals(c_strAcePrefix, StringComparison.OrdinalIgnoreCase)) + { + // Its ASCII, copy it + output.Append(ascii.Substring(iAfterLastDot, iNextDot - iAfterLastDot)); + } + else + { + // Not ASCII, bump up iAfterLastDot to be after ACE Prefix + iAfterLastDot += c_strAcePrefix.Length; + + // Get number of basic code points (where delimiter is) + // numBasicCodePoints < 0 if there're no basic code points + int iTemp = ascii.LastIndexOf(c_delimiter, iNextDot - 1); + + // Trailing - not allowed + if (iTemp == iNextDot - 1) + throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(ascii)); + + int numBasicCodePoints; + if (iTemp <= iAfterLastDot) + numBasicCodePoints = 0; + else + { + numBasicCodePoints = iTemp - iAfterLastDot; + + // Copy all the basic code points, making sure they're all in the allowed range, + // and losing the casing for all of them. + for (int copyAscii = iAfterLastDot; copyAscii < iAfterLastDot + numBasicCodePoints; copyAscii++) + { + // Make sure we don't allow unicode in the ascii part + if (ascii[copyAscii] > 0x7f) + throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(ascii)); + + // When appending make sure they get lower cased + output.Append((char)(ascii[copyAscii] >= 'A' && ascii[copyAscii] <='Z' ? ascii[copyAscii] - 'A' + 'a' : ascii[copyAscii])); + } + } + + // Get ready for main loop. Start at beginning if we didn't have any + // basic code points, otherwise start after the -. + // asciiIndex will be next character to read from ascii + int asciiIndex = iAfterLastDot + (numBasicCodePoints > 0 ? numBasicCodePoints + 1 : 0); + + // initialize our state + int n = c_initialN; + int bias = c_initialBias; + int i = 0; + + int w, k; + + // no Supplementary characters yet + int numSurrogatePairs = 0; + + // Main loop, read rest of ascii + while (asciiIndex < iNextDot) + { + /* Decode a generalized variable-length integer into delta, */ + /* which gets added to i. The overflow checking is easier */ + /* if we increase i as we go, then subtract off its starting */ + /* value at the end to obtain delta. */ + int oldi = i; + + for (w = 1, k = c_punycodeBase; ; k += c_punycodeBase) + { + // Check to make sure we aren't overrunning our ascii string + if (asciiIndex >= iNextDot) + throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(ascii)); + + // decode the digit from the next char + int digit = DecodeDigit(ascii[asciiIndex++]); + + Debug.Assert(w > 0, "[IdnMapping.punycode_decode]Expected w > 0"); + if (digit > (c_maxint - i) / w) + throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(ascii)); + + i += (int)(digit * w); + int t = k <= bias ? c_tmin : k >= bias + c_tmax ? c_tmax : k - bias; + if (digit < t) + break; + Debug.Assert(c_punycodeBase != t, "[IdnMapping.punycode_decode]Expected t != c_punycodeBase (36)"); + if (w > c_maxint / (c_punycodeBase - t)) + throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(ascii)); + w *= (c_punycodeBase - t); + } + + bias = Adapt(i - oldi, (output.Length - iOutputAfterLastDot - numSurrogatePairs) + 1, oldi == 0); + + /* i was supposed to wrap around from output.Length to 0, */ + /* incrementing n each time, so we'll fix that now: */ + Debug.Assert((output.Length - iOutputAfterLastDot - numSurrogatePairs) + 1 > 0, + "[IdnMapping.punycode_decode]Expected to have added > 0 characters this segment"); + if (i / ((output.Length - iOutputAfterLastDot - numSurrogatePairs) + 1) > c_maxint - n) + throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(ascii)); + n += (int)(i / (output.Length - iOutputAfterLastDot - numSurrogatePairs + 1)); + i %= (output.Length - iOutputAfterLastDot - numSurrogatePairs + 1); + + // Make sure n is legal + if ((n < 0 || n > 0x10ffff) || (n >= 0xD800 && n <= 0xDFFF)) + throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(ascii)); + + // insert n at position i of the output: Really tricky if we have surrogates + int iUseInsertLocation; + String strTemp = Char.ConvertFromUtf32(n); + + // If we have supplimentary characters + if (numSurrogatePairs > 0) + { + // Hard way, we have supplimentary characters + int iCount; + for (iCount = i, iUseInsertLocation = iOutputAfterLastDot; iCount > 0; iCount--, iUseInsertLocation++) + { + // If its a surrogate, we have to go one more + if (iUseInsertLocation >= output.Length) + throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(ascii)); + if (Char.IsSurrogate(output[iUseInsertLocation])) + iUseInsertLocation++; + } + } + else + { + // No Supplementary chars yet, just add i + iUseInsertLocation = iOutputAfterLastDot + i; + } + + // Insert it + output.Insert(iUseInsertLocation, strTemp); + + // If it was a surrogate increment our counter + if (IsSupplementary(n)) + numSurrogatePairs++; + + // Index gets updated + i++; + } + + // Do BIDI testing + bool bRightToLeft = false; + + // Check for RTL. If right-to-left, then 1st & last chars must be RTL + BidiCategory eBidi = CharUnicodeInfo.GetBidiCategory(output.ToString(), iOutputAfterLastDot); + if (eBidi == BidiCategory.RightToLeft || eBidi == BidiCategory.RightToLeftArabic) + { + // It has to be right to left. + bRightToLeft = true; + } + + // Check the rest of them to make sure RTL/LTR is consistent + for (int iTest = iOutputAfterLastDot; iTest < output.Length; iTest++) + { + // This might happen if we run into a pair + if (Char.IsLowSurrogate(output.ToString(), iTest)) + continue; + + // Check to see if its LTR + eBidi = CharUnicodeInfo.GetBidiCategory(output.ToString(), iTest); + if ((bRightToLeft && eBidi == BidiCategory.LeftToRight) || + (!bRightToLeft && (eBidi == BidiCategory.RightToLeft || eBidi == BidiCategory.RightToLeftArabic))) + throw new ArgumentException(SR.Argument_IdnBadBidi, nameof(ascii)); + } + + // Its also a requirement that the last one be RTL if 1st is RTL + if (bRightToLeft && eBidi != BidiCategory.RightToLeft && eBidi != BidiCategory.RightToLeftArabic) + { + // Oops, last wasn't RTL, last should be RTL if first is RTL + throw new ArgumentException(SR.Argument_IdnBadBidi, nameof(ascii)); + } + } + + // See if this label was too long + if (iNextDot - iAfterLastDot > c_labelLimit) + throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(ascii)); + + // Done with this segment, add dot if necessary + if (iNextDot != ascii.Length) + output.Append('.'); + + iAfterLastDot = iNextDot + 1; + iOutputAfterLastDot = output.Length; + } + + // Throw if we're too long + if (output.Length > c_defaultNameLimit - (IsDot(output[output.Length-1]) ? 0 : 1)) + throw new ArgumentException(SR.Format(SR.Argument_IdnBadNameSize, c_defaultNameLimit - (IsDot(output[output.Length-1]) ? 0 : 1)), nameof(ascii)); + + // Return our output string + return output.ToString(); + } + + // DecodeDigit(cp) returns the numeric value of a basic code */ + // point (for use in representing integers) in the range 0 to */ + // c_punycodeBase-1, or <0 if cp is does not represent a value. */ + + private static int DecodeDigit(char cp) + { + if (cp >= '0' && cp <= '9') + return cp - '0' + 26; + + // Two flavors for case differences + if (cp >= 'a' && cp <= 'z') + return cp - 'a'; + + if (cp >= 'A' && cp <= 'Z') + return cp - 'A'; + + // Expected 0-9, A-Z or a-z, everything else is illegal + throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(cp)); + } + + private static int Adapt(int delta, int numpoints, bool firsttime) + { + uint k; + + delta = firsttime ? delta / c_damp : delta / 2; + Debug.Assert(numpoints != 0, "[IdnMapping.adapt]Expected non-zero numpoints."); + delta += delta / numpoints; + + for (k = 0; delta > ((c_punycodeBase - c_tmin) * c_tmax) / 2; k += c_punycodeBase) + { + delta /= c_punycodeBase - c_tmin; + } + + Debug.Assert(delta + c_skew != 0, "[IdnMapping.adapt]Expected non-zero delta+skew."); + return (int)(k + (c_punycodeBase - c_tmin + 1) * delta / (delta + c_skew)); + } + + /* EncodeBasic(bcp,flag) forces a basic code point to lowercase */ + /* if flag is false, uppercase if flag is true, and returns */ + /* the resulting code point. The code point is unchanged if it */ + /* is caseless. The behavior is undefined if bcp is not a basic */ + /* code point. */ + + static char EncodeBasic(char bcp) + { + if (HasUpperCaseFlag(bcp)) + bcp += (char)('a' - 'A'); + + return bcp; + } + + // Return whether a punycode code point is flagged as being upper case. + private static bool HasUpperCaseFlag(char punychar) + { + return (punychar >= 'A' && punychar <= 'Z'); + } + + /* EncodeDigit(d,flag) returns the basic code point whose value */ + /* (when used for representing integers) is d, which needs to be in */ + /* the range 0 to punycodeBase-1. The lowercase form is used unless flag is */ + /* true, in which case the uppercase form is used. */ + + private static char EncodeDigit(int d) + { + Debug.Assert(d >= 0 && d < c_punycodeBase, "[IdnMapping.encode_digit]Expected 0 <= d < punycodeBase"); + // 26-35 map to ASCII 0-9 + if (d > 25) return (char)(d - 26 + '0'); + + // 0-25 map to a-z or A-Z + return (char)(d + 'a'); + } + + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/JapaneseCalendar.Win32.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/JapaneseCalendar.Win32.cs index a83c4fad9e..9ea6c21c2e 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/JapaneseCalendar.Win32.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/JapaneseCalendar.Win32.cs @@ -159,9 +159,10 @@ namespace System.Globalization int month; int day; - if (!Int32.TryParse(value.Substring(0, 4), NumberStyles.None, NumberFormatInfo.InvariantInfo, out year) || - !Int32.TryParse(value.Substring(5, 2), NumberStyles.None, NumberFormatInfo.InvariantInfo, out month) || - !Int32.TryParse(value.Substring(8, 2), NumberStyles.None, NumberFormatInfo.InvariantInfo, out day)) + ReadOnlySpan valueSpan = value.AsReadOnlySpan(); + if (!Int32.TryParse(valueSpan.Slice(0, 4), NumberStyles.None, NumberFormatInfo.InvariantInfo, out year) || + !Int32.TryParse(valueSpan.Slice(5, 2), NumberStyles.None, NumberFormatInfo.InvariantInfo, out month) || + !Int32.TryParse(valueSpan.Slice(8, 2), NumberStyles.None, NumberFormatInfo.InvariantInfo, out day)) { // Couldn't convert integer, fail return null; diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/JapaneseCalendar.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/JapaneseCalendar.cs index 0db1e6517a..50195d7f1c 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/JapaneseCalendar.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/JapaneseCalendar.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; namespace System.Globalization { @@ -225,7 +224,6 @@ namespace System.Globalization } - [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems. public override int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek) { return (helper.GetWeekOfYear(time, rule, firstDayOfWeek)); @@ -300,7 +298,6 @@ namespace System.Globalization throw new ArgumentOutOfRangeException(nameof(year), SR.ArgumentOutOfRange_NeedPosNum); } - Contract.EndContractBlock(); if (year > helper.MaxYear) { diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/JapaneseLunisolarCalendar.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/JapaneseLunisolarCalendar.cs index a90c4e8f21..e8a2dcd637 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/JapaneseLunisolarCalendar.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/JapaneseLunisolarCalendar.cs @@ -2,7 +2,6 @@ // 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.Diagnostics.Contracts; namespace System.Globalization { @@ -211,7 +210,6 @@ namespace System.Globalization MIN_LUNISOLAR_YEAR, MAX_LUNISOLAR_YEAR)); } - Contract.EndContractBlock(); return s_yinfo[lunarYear - MIN_LUNISOLAR_YEAR, index]; } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/JulianCalendar.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/JulianCalendar.cs index 8d94290547..82e4d589d3 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/JulianCalendar.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/JulianCalendar.cs @@ -2,7 +2,6 @@ // 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.Diagnostics.Contracts; namespace System.Globalization { @@ -221,7 +220,6 @@ namespace System.Globalization -120000, 120000)); } - Contract.EndContractBlock(); int y = GetDatePart(time.Ticks, DatePartYear); int m = GetDatePart(time.Ticks, DatePartMonth); int d = GetDatePart(time.Ticks, DatePartDay); @@ -424,7 +422,6 @@ namespace System.Globalization throw new ArgumentOutOfRangeException(nameof(year), SR.ArgumentOutOfRange_NeedNonNegNum); } - Contract.EndContractBlock(); if (year > MaxYear) { diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/KoreanCalendar.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/KoreanCalendar.cs index ef7495f07d..9168b664e0 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/KoreanCalendar.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/KoreanCalendar.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; namespace System.Globalization { @@ -153,7 +152,6 @@ namespace System.Globalization } - [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems. public override int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek) { return (helper.GetWeekOfYear(time, rule, firstDayOfWeek)); @@ -257,7 +255,6 @@ namespace System.Globalization throw new ArgumentOutOfRangeException(nameof(year), SR.ArgumentOutOfRange_NeedNonNegNum); } - Contract.EndContractBlock(); return (helper.ToFourDigitYear(year, this.TwoDigitYearMax)); } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/KoreanLunisolarCalendar.cs.REMOVED.git-id b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/KoreanLunisolarCalendar.cs.REMOVED.git-id index 0a3ef2907e..184ae46bbb 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/KoreanLunisolarCalendar.cs.REMOVED.git-id +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/KoreanLunisolarCalendar.cs.REMOVED.git-id @@ -1 +1 @@ -8364532c9eea29a43db417c49cba72befe5e6352 \ No newline at end of file +8cdade866c50ba78d20e7913ee0c9d88b0d1e0ed \ No newline at end of file diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/LocaleData.Unix.cs.REMOVED.git-id b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/LocaleData.Unix.cs.REMOVED.git-id index ce624977ea..e3bad5c749 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/LocaleData.Unix.cs.REMOVED.git-id +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/LocaleData.Unix.cs.REMOVED.git-id @@ -1 +1 @@ -d4c58d8a3dd5b909db00dc4d510385beba50973a \ No newline at end of file +ccf1078ac6369f6e9dfd610d27cdc94b588fc84d \ No newline at end of file diff --git a/external/corert/src/System.Private.CoreLib/src/System/Text/Normalization.Dummy.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/Normalization.Unix.cs similarity index 59% rename from external/corert/src/System.Private.CoreLib/src/System/Text/Normalization.Dummy.cs rename to external/corert/src/System.Private.CoreLib/shared/System/Globalization/Normalization.Unix.cs index 15632ded89..a25c1b9380 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Text/Normalization.Dummy.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/Normalization.Unix.cs @@ -2,25 +2,65 @@ // 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.Security; +using System.Diagnostics; using System.Text; -namespace System.Text +namespace System.Globalization { - static partial class Normalization + internal static partial class Normalization { - public static bool IsNormalized(this string strInput, NormalizationForm normalizationForm) + internal static bool IsNormalized(string strInput, NormalizationForm normalizationForm) { + if (GlobalizationMode.Invariant) + { + // In Invariant mode we assume all characters are normalized. + // This is because we don't support any linguistic operation on the strings + return true; + } + ValidateArguments(strInput, normalizationForm); - return true; + int ret = Interop.GlobalizationInterop.IsNormalized(normalizationForm, strInput, strInput.Length); + + if (ret == -1) + { + throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex, nameof(strInput)); + } + + return ret == 1; } - public static string Normalize(this string strInput, NormalizationForm normalizationForm) + internal static string Normalize(string strInput, NormalizationForm normalizationForm) { + if (GlobalizationMode.Invariant) + { + // In Invariant mode we assume all characters are normalized. + // This is because we don't support any linguistic operation on the strings + return strInput; + } + ValidateArguments(strInput, normalizationForm); - return strInput; + char[] buf = new char[strInput.Length]; + + for (int attempts = 2; attempts > 0; attempts--) + { + int realLen = Interop.GlobalizationInterop.NormalizeString(normalizationForm, strInput, strInput.Length, buf, buf.Length); + + if (realLen == -1) + { + throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex, nameof(strInput)); + } + + if (realLen <= buf.Length) + { + return new string(buf, 0, realLen); + } + + buf = new char[realLen]; + } + + throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex, nameof(strInput)); } // ----------------------------- @@ -29,10 +69,7 @@ namespace System.Text private static void ValidateArguments(string strInput, NormalizationForm normalizationForm) { - if (strInput == null) - { - throw new ArgumentNullException(nameof(strInput)); - } + Debug.Assert(strInput != null); if (normalizationForm != NormalizationForm.FormC && normalizationForm != NormalizationForm.FormD && normalizationForm != NormalizationForm.FormKC && normalizationForm != NormalizationForm.FormKD) diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/Normalization.Windows.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/Normalization.Windows.cs new file mode 100644 index 0000000000..8418c53b6c --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/Normalization.Windows.cs @@ -0,0 +1,148 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; + +namespace System.Globalization +{ + internal static partial class Normalization + { + internal static bool IsNormalized(string strInput, NormalizationForm normalizationForm) + { + if (GlobalizationMode.Invariant) + { + // In Invariant mode we assume all characters are normalized. + // This is because we don't support any linguistic operation on the strings + return true; + } + + Debug.Assert(strInput != null); + + // The only way to know if IsNormalizedString failed is through checking the Win32 last error + // IsNormalizedString pinvoke has SetLastError attribute property which will set the last error + // to 0 (ERROR_SUCCESS) before executing the calls. + bool result = Interop.Normaliz.IsNormalizedString((int)normalizationForm, strInput, strInput.Length); + + int lastError = Marshal.GetLastWin32Error(); + switch (lastError) + { + case Interop.Errors.ERROR_SUCCESS: + break; + + case Interop.Errors.ERROR_INVALID_PARAMETER: + case Interop.Errors.ERROR_NO_UNICODE_TRANSLATION: + if (normalizationForm != NormalizationForm.FormC && + normalizationForm != NormalizationForm.FormD && + normalizationForm != NormalizationForm.FormKC && + normalizationForm != NormalizationForm.FormKD) + { + throw new ArgumentException(SR.Argument_InvalidNormalizationForm, nameof(normalizationForm)); + } + + throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex, nameof(strInput)); + + case Interop.Errors.ERROR_NOT_ENOUGH_MEMORY: + throw new OutOfMemoryException(); + + default: + throw new InvalidOperationException(SR.Format(SR.UnknownError_Num, lastError)); + } + + return result; + } + + internal static string Normalize(string strInput, NormalizationForm normalizationForm) + { + if (GlobalizationMode.Invariant) + { + // In Invariant mode we assume all characters are normalized. + // This is because we don't support any linguistic operation on the strings + return strInput; + } + + Debug.Assert(strInput != null); + + // we depend on Win32 last error when calling NormalizeString + // NormalizeString pinvoke has SetLastError attribute property which will set the last error + // to 0 (ERROR_SUCCESS) before executing the calls. + + // Guess our buffer size first + int iLength = Interop.Normaliz.NormalizeString((int)normalizationForm, strInput, strInput.Length, null, 0); + + int lastError = Marshal.GetLastWin32Error(); + // Could have an error (actually it'd be quite hard to have an error here) + if ((lastError != Interop.Errors.ERROR_SUCCESS) || iLength < 0) + { + if (lastError == Interop.Errors.ERROR_INVALID_PARAMETER) + { + if (normalizationForm != NormalizationForm.FormC && + normalizationForm != NormalizationForm.FormD && + normalizationForm != NormalizationForm.FormKC && + normalizationForm != NormalizationForm.FormKD) + { + throw new ArgumentException(SR.Argument_InvalidNormalizationForm, nameof(normalizationForm)); + } + + throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex, nameof(strInput)); + } + + // We shouldn't really be able to get here..., guessing length is + // a trivial math function... + // Can't really be Out of Memory, but just in case: + if (lastError == Interop.Errors.ERROR_NOT_ENOUGH_MEMORY) + throw new OutOfMemoryException(); + + // Who knows what happened? Not us! + throw new InvalidOperationException(SR.Format(SR.UnknownError_Num, lastError)); + } + + // Don't break for empty strings (only possible for D & KD and not really possible at that) + if (iLength == 0) return string.Empty; + + // Someplace to stick our buffer + char[] cBuffer = null; + + for (;;) + { + // (re)allocation buffer and normalize string + cBuffer = new char[iLength]; + + // NormalizeString pinvoke has SetLastError attribute property which will set the last error + // to 0 (ERROR_SUCCESS) before executing the calls. + iLength = Interop.Normaliz.NormalizeString((int)normalizationForm, strInput, strInput.Length, cBuffer, cBuffer.Length); + lastError = Marshal.GetLastWin32Error(); + + if (lastError == Interop.Errors.ERROR_SUCCESS) + break; + + // Could have an error (actually it'd be quite hard to have an error here) + switch (lastError) + { + // Do appropriate stuff for the individual errors: + case Interop.Errors.ERROR_INSUFFICIENT_BUFFER: + iLength = Math.Abs(iLength); + Debug.Assert(iLength > cBuffer.Length, "Buffer overflow should have iLength > cBuffer.Length"); + continue; + + case Interop.Errors.ERROR_INVALID_PARAMETER: + case Interop.Errors.ERROR_NO_UNICODE_TRANSLATION: + // Illegal code point or order found. Ie: FFFE or D800 D800, etc. + throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex, nameof(strInput)); + + case Interop.Errors.ERROR_NOT_ENOUGH_MEMORY: + throw new OutOfMemoryException(); + + default: + // We shouldn't get here... + throw new InvalidOperationException(SR.Format(SR.UnknownError_Num, lastError)); + } + } + + // Copy our buffer into our new string, which will be the appropriate size + return new string(cBuffer, 0, iLength); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/NumberFormatInfo.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/NumberFormatInfo.cs index 9fea694cca..a98023dc96 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/NumberFormatInfo.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/NumberFormatInfo.cs @@ -3,8 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics.Contracts; -using System.Runtime.Serialization; using System.Text; namespace System.Globalization @@ -70,7 +68,6 @@ namespace System.Globalization internal String perMilleSymbol = "\u2030"; - [OptionalField(VersionAdded = 2)] internal String[] nativeDigits = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; internal int numberDecimalDigits = 2; @@ -82,29 +79,17 @@ namespace System.Globalization internal int percentNegativePattern = 0; internal int percentDecimalDigits = 2; - [OptionalField(VersionAdded = 2)] internal int digitSubstitution = (int)DigitShapes.None; internal bool isReadOnly = false; // Is this NumberFormatInfo for invariant culture? - - [OptionalField(VersionAdded = 2)] internal bool m_isInvariant = false; public NumberFormatInfo() : this(null) { } - [OnSerializing] - private void OnSerializing(StreamingContext ctx) { } - - [OnDeserializing] - private void OnDeserializing(StreamingContext ctx) { } - - [OnDeserialized] - private void OnDeserialized(StreamingContext ctx) { } - private static void VerifyDecimalSeparator(String decSep, String propertyName) { if (decSep == null) @@ -117,7 +102,6 @@ namespace System.Globalization { throw new ArgumentException(SR.Argument_EmptyDecString); } - Contract.EndContractBlock(); } private static void VerifyGroupSeparator(String groupSep, String propertyName) @@ -127,7 +111,6 @@ namespace System.Globalization throw new ArgumentNullException(propertyName, SR.ArgumentNull_String); } - Contract.EndContractBlock(); } private static void VerifyNativeDigits(string[] nativeDig, string propertyName) @@ -141,7 +124,6 @@ namespace System.Globalization { throw new ArgumentException(SR.Argument_InvalidNativeDigitCount, propertyName); } - Contract.EndContractBlock(); for (int i = 0; i < nativeDig.Length; i++) { @@ -205,14 +187,12 @@ namespace System.Globalization } } - [Pure] private void VerifyWritable() { if (isReadOnly) { throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); } - Contract.EndContractBlock(); } // Returns a default NumberFormatInfo that will be universally @@ -238,40 +218,25 @@ namespace System.Globalization public static NumberFormatInfo GetInstance(IFormatProvider formatProvider) { - // Fast case for a regular CultureInfo - NumberFormatInfo info; - CultureInfo cultureProvider = formatProvider as CultureInfo; - if (cultureProvider != null && !cultureProvider._isInherited) + return formatProvider == null ? + CurrentInfo : // Fast path for a null provider + GetProviderNonNull(formatProvider); + + NumberFormatInfo GetProviderNonNull(IFormatProvider provider) { - info = cultureProvider.numInfo; - if (info != null) + // Fast path for a regular CultureInfo + if (provider is CultureInfo cultureProvider && !cultureProvider._isInherited) { - return info; - } - else - { - return cultureProvider.NumberFormat; + return cultureProvider.numInfo ?? cultureProvider.NumberFormat; } + + return + provider as NumberFormatInfo ?? // Fast path for an NFI + provider.GetFormat(typeof(NumberFormatInfo)) as NumberFormatInfo ?? + CurrentInfo; } - // Fast case for an NFI; - info = formatProvider as NumberFormatInfo; - if (info != null) - { - return info; - } - if (formatProvider != null) - { - info = formatProvider.GetFormat(typeof(NumberFormatInfo)) as NumberFormatInfo; - if (info != null) - { - return info; - } - } - return CurrentInfo; } - - public Object Clone() { NumberFormatInfo n = (NumberFormatInfo)MemberwiseClone(); @@ -295,7 +260,6 @@ namespace System.Globalization 0, 99)); } - Contract.EndContractBlock(); VerifyWritable(); currencyDecimalDigits = value; } @@ -359,7 +323,6 @@ namespace System.Globalization throw new ArgumentNullException(nameof(CurrencyGroupSizes), SR.ArgumentNull_Obj); } - Contract.EndContractBlock(); VerifyWritable(); Int32[] inputSizes = (Int32[])value.Clone(); @@ -383,7 +346,6 @@ namespace System.Globalization throw new ArgumentNullException(nameof(NumberGroupSizes), SR.ArgumentNull_Obj); } - Contract.EndContractBlock(); VerifyWritable(); Int32[] inputSizes = (Int32[])value.Clone(); @@ -406,7 +368,6 @@ namespace System.Globalization throw new ArgumentNullException(nameof(PercentGroupSizes), SR.ArgumentNull_Obj); } - Contract.EndContractBlock(); VerifyWritable(); Int32[] inputSizes = (Int32[])value.Clone(); CheckGroupSize(nameof(PercentGroupSizes), inputSizes); @@ -437,7 +398,6 @@ namespace System.Globalization throw new ArgumentNullException(nameof(CurrencySymbol), SR.ArgumentNull_String); } - Contract.EndContractBlock(); VerifyWritable(); currencySymbol = value; } @@ -477,7 +437,6 @@ namespace System.Globalization throw new ArgumentNullException(nameof(NaNSymbol), SR.ArgumentNull_String); } - Contract.EndContractBlock(); VerifyWritable(); nanSymbol = value; } @@ -500,7 +459,6 @@ namespace System.Globalization 0, 15)); } - Contract.EndContractBlock(); VerifyWritable(); currencyNegativePattern = value; } @@ -525,7 +483,6 @@ namespace System.Globalization 0, 4)); } - Contract.EndContractBlock(); VerifyWritable(); numberNegativePattern = value; } @@ -550,7 +507,6 @@ namespace System.Globalization 0, 3)); } - Contract.EndContractBlock(); VerifyWritable(); percentPositivePattern = value; } @@ -575,7 +531,6 @@ namespace System.Globalization 0, 11)); } - Contract.EndContractBlock(); VerifyWritable(); percentNegativePattern = value; } @@ -595,7 +550,6 @@ namespace System.Globalization throw new ArgumentNullException(nameof(NegativeInfinitySymbol), SR.ArgumentNull_String); } - Contract.EndContractBlock(); VerifyWritable(); negativeInfinitySymbol = value; } @@ -612,7 +566,6 @@ namespace System.Globalization throw new ArgumentNullException(nameof(NegativeSign), SR.ArgumentNull_String); } - Contract.EndContractBlock(); VerifyWritable(); negativeSign = value; } @@ -634,7 +587,6 @@ namespace System.Globalization 0, 99)); } - Contract.EndContractBlock(); VerifyWritable(); numberDecimalDigits = value; } @@ -680,7 +632,6 @@ namespace System.Globalization 0, 3)); } - Contract.EndContractBlock(); VerifyWritable(); currencyPositivePattern = value; } @@ -700,7 +651,6 @@ namespace System.Globalization throw new ArgumentNullException(nameof(PositiveInfinitySymbol), SR.ArgumentNull_String); } - Contract.EndContractBlock(); VerifyWritable(); positiveInfinitySymbol = value; } @@ -717,7 +667,6 @@ namespace System.Globalization throw new ArgumentNullException(nameof(PositiveSign), SR.ArgumentNull_String); } - Contract.EndContractBlock(); VerifyWritable(); positiveSign = value; } @@ -739,7 +688,6 @@ namespace System.Globalization 0, 99)); } - Contract.EndContractBlock(); VerifyWritable(); percentDecimalDigits = value; } @@ -783,7 +731,6 @@ namespace System.Globalization throw new ArgumentNullException(nameof(PercentSymbol), SR.ArgumentNull_String); } - Contract.EndContractBlock(); VerifyWritable(); percentSymbol = value; } @@ -800,7 +747,6 @@ namespace System.Globalization throw new ArgumentNullException(nameof(PerMilleSymbol), SR.ArgumentNull_String); } - Contract.EndContractBlock(); VerifyWritable(); perMilleSymbol = value; } @@ -839,7 +785,6 @@ namespace System.Globalization { throw new ArgumentNullException(nameof(nfi)); } - Contract.EndContractBlock(); if (nfi.IsReadOnly) { return (nfi); @@ -863,7 +808,6 @@ namespace System.Globalization { throw new ArgumentException(SR.Argument_InvalidNumberStyles, nameof(style)); } - Contract.EndContractBlock(); if ((style & NumberStyles.AllowHexSpecifier) != 0) { // Check for hex number if ((style & ~NumberStyles.HexNumber) != 0) @@ -880,7 +824,6 @@ namespace System.Globalization { throw new ArgumentException(SR.Argument_InvalidNumberStyles, nameof(style)); } - Contract.EndContractBlock(); if ((style & NumberStyles.AllowHexSpecifier) != 0) { // Check for hex number throw new ArgumentException(SR.Arg_HexStyleNotSupported); diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/PersianCalendar.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/PersianCalendar.cs index 78a081e1b9..10912f85b1 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/PersianCalendar.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/PersianCalendar.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; namespace System.Globalization { @@ -289,7 +288,6 @@ namespace System.Globalization -120000, 120000)); } - Contract.EndContractBlock(); // Get the date in Persian calendar. int y = GetDatePart(time.Ticks, DatePartYear); int m = GetDatePart(time.Ticks, DatePartMonth); @@ -521,7 +519,6 @@ namespace System.Globalization int daysInMonth = GetDaysInMonth(year, month, era); if (day < 1 || day > daysInMonth) { - // BCLDebug.Log("year = " + year + ", month = " + month + ", day = " + day); throw new ArgumentOutOfRangeException( nameof(day), String.Format( @@ -582,7 +579,6 @@ namespace System.Globalization throw new ArgumentOutOfRangeException(nameof(year), SR.ArgumentOutOfRange_NeedNonNegNum); } - Contract.EndContractBlock(); if (year < 100) { diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/RegionInfo.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/RegionInfo.cs new file mode 100644 index 0000000000..2cad7bb31c --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/RegionInfo.cs @@ -0,0 +1,399 @@ +// 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. + +//////////////////////////////////////////////////////////////////////////// +// +// +// Purpose: This class represents settings specified by de jure or +// de facto standards for a particular country/region. In +// contrast to CultureInfo, the RegionInfo does not represent +// preferences of the user and does not depend on the user's +// language or culture. +// +// +//////////////////////////////////////////////////////////////////////////// + +using System.Diagnostics; + +namespace System.Globalization +{ + public class RegionInfo + { + //--------------------------------------------------------------------// + // Internal Information // + //--------------------------------------------------------------------// + + // + // Variables. + // + + // + // Name of this region (ie: es-US): serialized, the field used for deserialization + // + internal String _name; + + // + // The CultureData instance that we are going to read data from. + // + internal CultureData _cultureData; + + // + // The RegionInfo for our current region + // + internal static volatile RegionInfo s_currentRegionInfo; + + + //////////////////////////////////////////////////////////////////////// + // + // RegionInfo Constructors + // + // Note: We prefer that a region be created with a full culture name (ie: en-US) + // because otherwise the native strings won't be right. + // + // In Silverlight we enforce that RegionInfos must be created with a full culture name + // + //////////////////////////////////////////////////////////////////////// + public RegionInfo(String name) + { + if (name == null) + throw new ArgumentNullException(nameof(name)); + + if (name.Length == 0) //The InvariantCulture has no matching region + { + throw new ArgumentException(SR.Argument_NoRegionInvariantCulture, nameof(name)); + } + + + // + // For CoreCLR we only want the region names that are full culture names + // + _cultureData = CultureData.GetCultureDataForRegion(name, true); + if (_cultureData == null) + throw new ArgumentException( + String.Format( + CultureInfo.CurrentCulture, + SR.Argument_InvalidCultureName, name), nameof(name)); + + + // Not supposed to be neutral + if (_cultureData.IsNeutralCulture) + throw new ArgumentException(SR.Format(SR.Argument_InvalidNeutralRegionName, name), nameof(name)); + + SetName(name); + } + + public RegionInfo(int culture) + { + if (culture == CultureInfo.LOCALE_INVARIANT) //The InvariantCulture has no matching region + { + throw new ArgumentException(SR.Argument_NoRegionInvariantCulture); + } + + if (culture == CultureInfo.LOCALE_NEUTRAL) + { + // Not supposed to be neutral + throw new ArgumentException(SR.Format(SR.Argument_CultureIsNeutral, culture), nameof(culture)); + } + + if (culture == CultureInfo.LOCALE_CUSTOM_DEFAULT) + { + // Not supposed to be neutral + throw new ArgumentException(SR.Format(SR.Argument_CustomCultureCannotBePassedByNumber, culture), nameof(culture)); + } + + _cultureData = CultureData.GetCultureData(culture, true); + _name = _cultureData.SREGIONNAME; + + if (_cultureData.IsNeutralCulture) + { + // Not supposed to be neutral + throw new ArgumentException(SR.Format(SR.Argument_CultureIsNeutral, culture), nameof(culture)); + } + } + + internal RegionInfo(CultureData cultureData) + { + _cultureData = cultureData; + _name = _cultureData.SREGIONNAME; + } + + private void SetName(string name) + { + // Use the name of the region we found + _name = _cultureData.SREGIONNAME; + } + + //////////////////////////////////////////////////////////////////////// + // + // GetCurrentRegion + // + // This instance provides methods based on the current user settings. + // These settings are volatile and may change over the lifetime of the + // thread. + // + //////////////////////////////////////////////////////////////////////// + public static RegionInfo CurrentRegion + { + get + { + RegionInfo temp = s_currentRegionInfo; + if (temp == null) + { + temp = new RegionInfo(CultureInfo.CurrentCulture._cultureData); + + // Need full name for custom cultures + temp._name = temp._cultureData.SREGIONNAME; + s_currentRegionInfo = temp; + } + return temp; + } + } + + //////////////////////////////////////////////////////////////////////// + // + // GetName + // + // Returns the name of the region (ie: en-US) + // + //////////////////////////////////////////////////////////////////////// + public virtual String Name + { + get + { + Debug.Assert(_name != null, "Expected RegionInfo._name to be populated already"); + return (_name); + } + } + + //////////////////////////////////////////////////////////////////////// + // + // GetEnglishName + // + // Returns the name of the region in English. (ie: United States) + // + //////////////////////////////////////////////////////////////////////// + public virtual String EnglishName + { + get + { + return (_cultureData.SENGCOUNTRY); + } + } + + + //////////////////////////////////////////////////////////////////////// + // + // GetDisplayName + // + // Returns the display name (localized) of the region. (ie: United States + // if the current UI language is en-US) + // + //////////////////////////////////////////////////////////////////////// + public virtual String DisplayName + { + get + { + return (_cultureData.SLOCALIZEDCOUNTRY); + } + } + + + //////////////////////////////////////////////////////////////////////// + // + // GetNativeName + // + // Returns the native name of the region. (ie: Deutschland) + // WARNING: You need a full locale name for this to make sense. + // + //////////////////////////////////////////////////////////////////////// + public virtual String NativeName + { + get + { + return (_cultureData.SNATIVECOUNTRY); + } + } + + //////////////////////////////////////////////////////////////////////// + // + // TwoLetterISORegionName + // + // Returns the two letter ISO region name (ie: US) + // + //////////////////////////////////////////////////////////////////////// + public virtual String TwoLetterISORegionName + { + get + { + return (_cultureData.SISO3166CTRYNAME); + } + } + + //////////////////////////////////////////////////////////////////////// + // + // ThreeLetterISORegionName + // + // Returns the three letter ISO region name (ie: USA) + // + //////////////////////////////////////////////////////////////////////// + public virtual String ThreeLetterISORegionName + { + get + { + return (_cultureData.SISO3166CTRYNAME2); + } + } + + //////////////////////////////////////////////////////////////////////// + // + // ThreeLetterWindowsRegionName + // + // Returns the three letter windows region name (ie: USA) + // + //////////////////////////////////////////////////////////////////////// + public virtual String ThreeLetterWindowsRegionName + { + get + { + // ThreeLetterWindowsRegionName is really same as ThreeLetterISORegionName + return ThreeLetterISORegionName; + } + } + + + //////////////////////////////////////////////////////////////////////// + // + // IsMetric + // + // Returns true if this region uses the metric measurement system + // + //////////////////////////////////////////////////////////////////////// + public virtual bool IsMetric + { + get + { + int value = _cultureData.IMEASURE; + return (value == 0); + } + } + + public virtual int GeoId + { + get + { + return (_cultureData.IGEOID); + } + } + + //////////////////////////////////////////////////////////////////////// + // + // CurrencyEnglishName + // + // English name for this region's currency, ie: Swiss Franc + // + //////////////////////////////////////////////////////////////////////// + public virtual string CurrencyEnglishName + { + get + { + return (_cultureData.SENGLISHCURRENCY); + } + } + + //////////////////////////////////////////////////////////////////////// + // + // CurrencyNativeName + // + // Native name for this region's currency, ie: Schweizer Franken + // WARNING: You need a full locale name for this to make sense. + // + //////////////////////////////////////////////////////////////////////// + public virtual string CurrencyNativeName + { + get + { + return (_cultureData.SNATIVECURRENCY); + } + } + + //////////////////////////////////////////////////////////////////////// + // + // CurrencySymbol + // + // Currency Symbol for this locale, ie: Fr. or $ + // + //////////////////////////////////////////////////////////////////////// + public virtual String CurrencySymbol + { + get + { + return (_cultureData.SCURRENCY); + } + } + + //////////////////////////////////////////////////////////////////////// + // + // ISOCurrencySymbol + // + // ISO Currency Symbol for this locale, ie: CHF + // + //////////////////////////////////////////////////////////////////////// + public virtual String ISOCurrencySymbol + { + get + { + return (_cultureData.SINTLSYMBOL); + } + } + + //////////////////////////////////////////////////////////////////////// + // + // Equals + // + // Implements Object.Equals(). Returns a boolean indicating whether + // or not object refers to the same RegionInfo as the current instance. + // + // RegionInfos are considered equal if and only if they have the same name + // (ie: en-US) + // + //////////////////////////////////////////////////////////////////////// + public override bool Equals(Object value) + { + RegionInfo that = value as RegionInfo; + if (that != null) + { + return this.Name.Equals(that.Name); + } + + return (false); + } + + //////////////////////////////////////////////////////////////////////// + // + // GetHashCode + // + // Implements Object.GetHashCode(). Returns the hash code for the + // CultureInfo. The hash code is guaranteed to be the same for RegionInfo + // A and B where A.Equals(B) is true. + // + //////////////////////////////////////////////////////////////////////// + public override int GetHashCode() + { + return (this.Name.GetHashCode()); + } + + + //////////////////////////////////////////////////////////////////////// + // + // ToString + // + // Implements Object.ToString(). Returns the name of the Region, ie: es-US + // + //////////////////////////////////////////////////////////////////////// + public override String ToString() + { + return (Name); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/SortKey.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/SortKey.cs index d65e097cb4..647db75b63 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/SortKey.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/SortKey.cs @@ -13,9 +13,7 @@ using System; using System.Runtime.CompilerServices; -using System.Runtime.Serialization; using System.Diagnostics; -using System.Diagnostics.Contracts; namespace System.Globalization { @@ -25,12 +23,8 @@ namespace System.Globalization // Internal Information // //--------------------------------------------------------------------// - [OptionalField(VersionAdded = 3)] internal string _localeName; // locale identifier - [OptionalField(VersionAdded = 1)] // LCID field so serialization is Whidbey compatible though we don't officially support it - internal int _win32LCID; - internal CompareOptions _options; // options internal string _string; // original string internal byte[] _keyData; // sortkey data @@ -47,26 +41,6 @@ namespace System.Globalization _string = str; } - [OnSerializing] - private void OnSerializing(StreamingContext context) - { - //set LCID to proper value for Whidbey serialization (no other use) - if (_win32LCID == 0) - { - _win32LCID = CultureInfo.GetCultureInfo(_localeName).LCID; - } - } - - [OnDeserialized] - private void OnDeserialized(StreamingContext context) - { - //set locale name to proper value after Whidbey deserialization - if (String.IsNullOrEmpty(_localeName) && _win32LCID != 0) - { - _localeName = CultureInfo.GetCultureInfo(_win32LCID).Name; - } - } - //////////////////////////////////////////////////////////////////////// // // GetOriginalString @@ -114,7 +88,6 @@ namespace System.Globalization { throw new ArgumentNullException((sortkey1 == null ? nameof(sortkey1) : nameof(sortkey2))); } - Contract.EndContractBlock(); byte[] key1Data = sortkey1._keyData; byte[] key2Data = sortkey2._keyData; diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/SortVersion.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/SortVersion.cs index a7aef6d84b..46e9a833ec 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/SortVersion.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/SortVersion.cs @@ -5,16 +5,17 @@ namespace System.Globalization { [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class SortVersion : IEquatable { - private int _nlsVersion; - private Guid _sortId; + private int m_NlsVersion; // Do not rename (binary serialization) + private Guid m_SortId; // Do not rename (binary serialization) public int FullVersion { get { - return _nlsVersion; + return m_NlsVersion; } } @@ -22,19 +23,19 @@ namespace System.Globalization { get { - return _sortId; + return m_SortId; } } public SortVersion(int fullVersion, Guid sortId) { - _sortId = sortId; - _nlsVersion = fullVersion; + m_SortId = sortId; + m_NlsVersion = fullVersion; } internal SortVersion(int nlsVersion, int effectiveId, Guid customVersion) { - _nlsVersion = nlsVersion; + m_NlsVersion = nlsVersion; if (customVersion == Guid.Empty) { @@ -45,7 +46,7 @@ namespace System.Globalization customVersion = new Guid(0, 0, 0, 0, 0, 0, 0, b1, b2, b3, b4); } - _sortId = customVersion; + m_SortId = customVersion; } public override bool Equals(object obj) @@ -66,12 +67,12 @@ namespace System.Globalization return false; } - return _nlsVersion == other._nlsVersion && _sortId == other._sortId; + return m_NlsVersion == other.m_NlsVersion && m_SortId == other.m_SortId; } public override int GetHashCode() { - return _nlsVersion * 7 | _sortId.GetHashCode(); + return m_NlsVersion * 7 | m_SortId.GetHashCode(); } public static bool operator ==(SortVersion left, SortVersion right) diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/StringInfo.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/StringInfo.cs index 87d1b9f684..aa62b2c474 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/StringInfo.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/StringInfo.cs @@ -14,17 +14,13 @@ using System; using System.Diagnostics; -using System.Diagnostics.Contracts; -using System.Runtime.Serialization; namespace System.Globalization { public class StringInfo { - [OptionalField(VersionAdded = 2)] private string _str; - [NonSerialized] private int[] _indexes; // Legacy constructor @@ -36,21 +32,6 @@ namespace System.Globalization this.String = value; } - [OnDeserializing] - private void OnDeserializing(StreamingContext ctx) - { - _str = String.Empty; - } - - [OnDeserialized] - private void OnDeserialized(StreamingContext ctx) - { - if (_str.Length == 0) - { - _indexes = null; - } - } - public override bool Equals(Object value) { StringInfo that = value as StringInfo; @@ -95,7 +76,6 @@ namespace System.Globalization throw new ArgumentNullException(nameof(String), SR.ArgumentNull_String); } - Contract.EndContractBlock(); _str = value; _indexes = null; @@ -280,7 +260,6 @@ namespace System.Globalization { throw new ArgumentNullException(nameof(str)); } - Contract.EndContractBlock(); int len = str.Length; if (index < 0 || index >= len) @@ -311,7 +290,6 @@ namespace System.Globalization { throw new ArgumentNullException(nameof(str)); } - Contract.EndContractBlock(); int len = str.Length; if (index < 0 || (index > len)) @@ -340,7 +318,6 @@ namespace System.Globalization { throw new ArgumentNullException(nameof(str)); } - Contract.EndContractBlock(); int len = str.Length; int[] result = new int[len]; diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TaiwanCalendar.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TaiwanCalendar.cs index ec4188161a..c5161138de 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TaiwanCalendar.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TaiwanCalendar.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; namespace System.Globalization { @@ -160,7 +159,6 @@ namespace System.Globalization } - [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems. public override int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek) { return (helper.GetWeekOfYear(time, rule, firstDayOfWeek)); @@ -265,7 +263,6 @@ namespace System.Globalization throw new ArgumentOutOfRangeException(nameof(year), SR.ArgumentOutOfRange_NeedPosNum); } - Contract.EndContractBlock(); if (year > helper.MaxYear) { diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TaiwanLunisolarCalendar.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TaiwanLunisolarCalendar.cs index 1e2ec62a71..60e84f7f33 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TaiwanLunisolarCalendar.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TaiwanLunisolarCalendar.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics.Contracts; namespace System.Globalization { @@ -270,7 +269,6 @@ namespace System.Globalization MIN_LUNISOLAR_YEAR, MAX_LUNISOLAR_YEAR)); } - Contract.EndContractBlock(); return s_yinfo[lunarYear - MIN_LUNISOLAR_YEAR, index]; } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TextElementEnumerator.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TextElementEnumerator.cs new file mode 100644 index 0000000000..8b0f102a77 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TextElementEnumerator.cs @@ -0,0 +1,117 @@ +// 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. + +//////////////////////////////////////////////////////////////////////////// +// +// +// Purpose: +// +// +//////////////////////////////////////////////////////////////////////////// + +using System.Collections; +using System.Diagnostics; + +namespace System.Globalization +{ + // + // This is public because GetTextElement() is public. + // + + public class TextElementEnumerator : IEnumerator + { + private String _str; + private int _index; + private int _startIndex; + + private int _strLen; // This is the length of the total string, counting from the beginning of string. + + private int _currTextElementLen; // The current text element lenght after MoveNext() is called. + + private UnicodeCategory _uc; + + private int _charLen; // The next abstract char to look at after MoveNext() is called. It could be 1 or 2, depending on if it is a surrogate or not. + + internal TextElementEnumerator(String str, int startIndex, int strLen) + { + Debug.Assert(str != null, "TextElementEnumerator(): str != null"); + Debug.Assert(startIndex >= 0 && strLen >= 0, "TextElementEnumerator(): startIndex >= 0 && strLen >= 0"); + Debug.Assert(strLen >= startIndex, "TextElementEnumerator(): strLen >= startIndex"); + _str = str; + _startIndex = startIndex; + _strLen = strLen; + Reset(); + } + + public bool MoveNext() + { + if (_index >= _strLen) + { + // Make the _index to be greater than _strLen so that we can throw exception if GetTextElement() is called. + _index = _strLen + 1; + return (false); + } + _currTextElementLen = StringInfo.GetCurrentTextElementLen(_str, _index, _strLen, ref _uc, ref _charLen); + _index += _currTextElementLen; + return (true); + } + + // + // Get the current text element. + // + + public Object Current + { + get + { + return (GetTextElement()); + } + } + + // + // Get the current text element. + // + + public String GetTextElement() + { + if (_index == _startIndex) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumNotStarted); + } + if (_index > _strLen) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumEnded); + } + + return (_str.Substring(_index - _currTextElementLen, _currTextElementLen)); + } + + // + // Get the starting index of the current text element. + // + + public int ElementIndex + { + get + { + if (_index == _startIndex) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumNotStarted); + } + return (_index - _currTextElementLen); + } + } + + + public void Reset() + { + _index = _startIndex; + if (_index < _strLen) + { + // If we have more than 1 character, get the category of the current char. + _uc = CharUnicodeInfo.InternalGetUnicodeCategory(_str, _index, out _charLen); + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Unix.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Unix.cs new file mode 100644 index 0000000000..dd2433d18f --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Unix.cs @@ -0,0 +1,117 @@ +// 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.Diagnostics; +using System.Security; +using System.Text; + +namespace System.Globalization +{ + public partial class TextInfo + { + private Tristate _needsTurkishCasing = Tristate.NotInitialized; + + private void FinishInitialization() + { + } + + private unsafe string ChangeCase(string s, bool toUpper) + { + Debug.Assert(!_invariantMode); + + Debug.Assert(s != null); + + if (s.Length == 0) + { + return string.Empty; + } + + string result = string.FastAllocateString(s.Length); + + fixed (char* pSource = s) + { + fixed (char* pResult = result) + { +#if CORECLR + if (IsAsciiCasingSameAsInvariant && s.IsAscii()) + { + int length = s.Length; + char* a = pSource, b = pResult; + if (toUpper) + { + while (length-- != 0) + { + *b++ = ToUpperAsciiInvariant(*a++); + } + } + else + { + while (length-- != 0) + { + *b++ = ToLowerAsciiInvariant(*a++); + } + } + } + else +#endif + { + ChangeCase(pSource, s.Length, pResult, result.Length, toUpper); + } + } + } + + return result; + } + + private unsafe char ChangeCase(char c, bool toUpper) + { + Debug.Assert(!_invariantMode); + + char dst = default(char); + + ChangeCase(&c, 1, &dst, 1, toUpper); + + return dst; + } + + // ----------------------------- + // ---- PAL layer ends here ---- + // ----------------------------- + + private bool NeedsTurkishCasing(string localeName) + { + Debug.Assert(localeName != null); + + return CultureInfo.GetCultureInfo(localeName).CompareInfo.Compare("\u0131", "I", CompareOptions.IgnoreCase) == 0; + } + + private bool IsInvariant { get { return _cultureName.Length == 0; } } + + internal unsafe void ChangeCase(char* src, int srcLen, char* dstBuffer, int dstBufferCapacity, bool bToUpper) + { + Debug.Assert(!_invariantMode); + + if (IsInvariant) + { + Interop.GlobalizationInterop.ChangeCaseInvariant(src, srcLen, dstBuffer, dstBufferCapacity, bToUpper); + } + else + { + if (_needsTurkishCasing == Tristate.NotInitialized) + { + _needsTurkishCasing = NeedsTurkishCasing(_textInfoName) ? Tristate.True : Tristate.False; + } + if (_needsTurkishCasing == Tristate.True) + { + Interop.GlobalizationInterop.ChangeCaseTurkish(src, srcLen, dstBuffer, dstBufferCapacity, bToUpper); + } + else + { + Interop.GlobalizationInterop.ChangeCase(src, srcLen, dstBuffer, dstBufferCapacity, bToUpper); + } + } + } + + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Windows.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Windows.cs new file mode 100644 index 0000000000..8b27e42d1d --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.Windows.cs @@ -0,0 +1,113 @@ +// 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.Diagnostics; + +namespace System.Globalization +{ + public partial class TextInfo + { + private unsafe void FinishInitialization() + { + if (_invariantMode) + { + _sortHandle = IntPtr.Zero; + return; + } + + const uint LCMAP_SORTHANDLE = 0x20000000; + + IntPtr handle; + int ret = Interop.Kernel32.LCMapStringEx(_textInfoName, LCMAP_SORTHANDLE, null, 0, &handle, IntPtr.Size, null, null, IntPtr.Zero); + _sortHandle = ret > 0 ? handle : IntPtr.Zero; + } + + private unsafe string ChangeCase(string s, bool toUpper) + { + Debug.Assert(!_invariantMode); + + Debug.Assert(s != null); + + // + // Get the length of the string. + // + int nLengthInput = s.Length; + + // + // Check if we have the empty string. + // + if (nLengthInput == 0) + { + return s; + } + + int ret; + + // Check for Invariant to avoid A/V in LCMapStringEx + uint linguisticCasing = IsInvariantLocale(_textInfoName) ? 0 : LCMAP_LINGUISTIC_CASING; + + // + // Create the result string. + // + string result = string.FastAllocateString(nLengthInput); + + fixed (char* pSource = s) + fixed (char* pResult = result) + { + ret = Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _textInfoName, + linguisticCasing | (toUpper ? LCMAP_UPPERCASE : LCMAP_LOWERCASE), + pSource, + nLengthInput, + pResult, + nLengthInput, + null, + null, + _sortHandle); + } + + if (ret == 0) + { + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + } + + Debug.Assert(ret == nLengthInput, "Expected getting the same length of the original string"); + return result; + } + + private unsafe char ChangeCase(char c, bool toUpper) + { + Debug.Assert(!_invariantMode); + + char retVal = '\0'; + + // Check for Invariant to avoid A/V in LCMapStringEx + uint linguisticCasing = IsInvariantLocale(_textInfoName) ? 0 : LCMAP_LINGUISTIC_CASING; + + Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _textInfoName, + toUpper ? LCMAP_UPPERCASE | linguisticCasing : LCMAP_LOWERCASE | linguisticCasing, + &c, + 1, + &retVal, + 1, + null, + null, + _sortHandle); + + return retVal; + } + + // PAL Ends here + + private IntPtr _sortHandle; + + private const uint LCMAP_LINGUISTIC_CASING = 0x01000000; + private const uint LCMAP_LOWERCASE = 0x00000100; + private const uint LCMAP_UPPERCASE = 0x00000200; + + private static bool IsInvariantLocale(string localeName) + { + return localeName == ""; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/TextInfo.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.cs similarity index 65% rename from external/corert/src/System.Private.CoreLib/src/System/Globalization/TextInfo.cs rename to external/corert/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.cs index 058a0bd628..59a3188bbb 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/TextInfo.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TextInfo.cs @@ -13,19 +13,13 @@ //////////////////////////////////////////////////////////////////////////// using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.Serialization; using System.Text; - namespace System.Globalization { public partial class TextInfo : ICloneable, IDeserializationCallback { - ////--------------------------------------------------------------------// - //// Internal Information // - ////--------------------------------------------------------------------// - private enum Tristate : byte { NotInitialized, @@ -33,28 +27,25 @@ namespace System.Globalization False, } - //// - //// Variables. - //// - - private String _listSeparator; + private string _listSeparator; private bool _isReadOnly = false; - //// _cultureName is the name of the creating culture. Note that we consider this authoratative, - //// if the culture's textinfo changes when deserializing, then behavior may change. - //// (ala Whidbey behavior). This is the only string Arrowhead needs to serialize. - //// _cultureData is the data that backs this class. - //// _textInfoName is the actual name of the textInfo (from cultureData.STEXTINFO) - //// this can be the same as _cultureName on Silverlight since the OS knows - //// how to do the sorting. However in the desktop, when we call the sorting dll, it doesn't - //// know how to resolve custom locle names to sort ids so we have to have alredy resolved this. - //// + /* _cultureName is the name of the creating culture. + _cultureData is the data that backs this class. + _textInfoName is the actual name of the textInfo (from cultureData.STEXTINFO) + In the desktop, when we call the sorting dll, it doesn't + know how to resolve custom locle names to sort ids so we have to have already resolved this. + */ + + private readonly string _cultureName; // Name of the culture that created this text info + private readonly CultureData _cultureData; // Data record for the culture that made us, not for this textinfo + private readonly string _textInfoName; // Name of the text info we're using (ie: _cultureData.STEXTINFO) - private readonly String _cultureName; // Name of the culture that created this text info - private CultureData _cultureData; // Data record for the culture that made us, not for this textinfo - private String _textInfoName; // Name of the text info we're using (ie: _cultureData.STEXTINFO) private Tristate _isAsciiCasingSameAsInvariant = Tristate.NotInitialized; + // _invariantMode is defined for the perf reason as accessing the instance field is faster than access the static property GlobalizationMode.Invariant + private readonly bool _invariantMode = GlobalizationMode.Invariant; + // Invariant text info internal static TextInfo Invariant { @@ -67,139 +58,75 @@ namespace System.Globalization } internal volatile static TextInfo s_Invariant; - void IDeserializationCallback.OnDeserialization(Object sender) + ////////////////////////////////////////////////////////////////////////// + //// + //// TextInfo Constructors + //// + //// Implements CultureInfo.TextInfo. + //// + ////////////////////////////////////////////////////////////////////////// + internal TextInfo(CultureData cultureData) { - OnDeserialized(); + // This is our primary data source, we don't need most of the rest of this + _cultureData = cultureData; + _cultureName = _cultureData.CultureName; + _textInfoName = _cultureData.STEXTINFO; + + FinishInitialization(); } - private void OnDeserialized() + void IDeserializationCallback.OnDeserialization(Object sender) { - // this method will be called twice because of the support of IDeserializationCallback - if (_cultureData == null) - { - // Get the text info name belonging to that culture - _cultureData = CultureInfo.GetCultureInfo(_cultureName)._cultureData; - _textInfoName = _cultureData.STEXTINFO; - FinishInitialization(_textInfoName); - } + throw new PlatformNotSupportedException(); } // // Internal ordinal comparison functions // - internal static int GetHashCodeOrdinalIgnoreCase(String s) + internal static int GetHashCodeOrdinalIgnoreCase(string s) { // This is the same as an case insensitive hash for Invariant // (not necessarily true for sorting, but OK for casing & then we apply normal hash code rules) - return (Invariant.GetCaseInsensitiveHashCode(s)); + return Invariant.GetCaseInsensitiveHashCode(s); } // Currently we don't have native functions to do this, so we do it the hard way - internal static int IndexOfStringOrdinalIgnoreCase(String source, String value, int startIndex, int count) + internal static int IndexOfStringOrdinalIgnoreCase(string source, string value, int startIndex, int count) { - if (count > source.Length || count < 0 || startIndex < 0 || startIndex >= source.Length || startIndex + count > source.Length) + if (count > source.Length || count < 0 || startIndex < 0 || startIndex > source.Length - count) { return -1; } - return CompareInfo.IndexOfOrdinal(source, value, startIndex, count, ignoreCase: true); + return CultureInfo.InvariantCulture.CompareInfo.IndexOfOrdinal(source, value, startIndex, count, ignoreCase: true); } // Currently we don't have native functions to do this, so we do it the hard way - internal static int LastIndexOfStringOrdinalIgnoreCase(String source, String value, int startIndex, int count) + internal static int LastIndexOfStringOrdinalIgnoreCase(string source, string value, int startIndex, int count) { if (count > source.Length || count < 0 || startIndex < 0 || startIndex > source.Length - 1 || (startIndex - count + 1 < 0)) { return -1; } - return CompareInfo.LastIndexOfOrdinal(source, value, startIndex, count, ignoreCase: true); + return CultureInfo.InvariantCulture.CompareInfo.LastIndexOfOrdinal(source, value, startIndex, count, ignoreCase: true); } - //////////////////////////////////////////////////////////////////////// - // - // CodePage - // - // Returns the number of the code page used by this writing system. - // The type parameter can be any of the following values: - // ANSICodePage - // OEMCodePage - // MACCodePage - // - //////////////////////////////////////////////////////////////////////// + public virtual int ANSICodePage => _cultureData.IDEFAULTANSICODEPAGE; + public virtual int OEMCodePage => _cultureData.IDEFAULTOEMCODEPAGE; - public virtual int ANSICodePage - { - get - { - return (_cultureData.IDEFAULTANSICODEPAGE); - } - } + public virtual int MacCodePage => _cultureData.IDEFAULTMACCODEPAGE; + public virtual int EBCDICCodePage => _cultureData.IDEFAULTEBCDICCODEPAGE; - public virtual int OEMCodePage - { - get - { - return (_cultureData.IDEFAULTOEMCODEPAGE); - } - } + // Just use the LCID from our text info name + public int LCID => CultureInfo.GetCultureInfo(_textInfoName).LCID; + public string CultureName => _textInfoName; - public virtual int MacCodePage - { - get - { - return (_cultureData.IDEFAULTMACCODEPAGE); - } - } - - - public virtual int EBCDICCodePage - { - get - { - return (_cultureData.IDEFAULTEBCDICCODEPAGE); - } - } - - public int LCID - { - get - { - // Just use the LCID from our text info name - return CultureInfo.GetCultureInfo(_textInfoName).LCID; - } - } - - ////////////////////////////////////////////////////////////////////////// - //// - //// CultureName - //// - //// The name of the culture associated with the current TextInfo. - //// - ////////////////////////////////////////////////////////////////////////// - public string CultureName - { - get - { - return _textInfoName; - } - } - - //////////////////////////////////////////////////////////////////////// - // - // IsReadOnly - // - // Detect if the object is readonly. - // - //////////////////////////////////////////////////////////////////////// - public bool IsReadOnly - { - get { return (_isReadOnly); } - } + public bool IsReadOnly => _isReadOnly; ////////////////////////////////////////////////////////////////////////// //// @@ -212,7 +139,7 @@ namespace System.Globalization { object o = MemberwiseClone(); ((TextInfo)o).SetReadOnlyState(false); - return (o); + return o; } //////////////////////////////////////////////////////////////////////// @@ -226,13 +153,12 @@ namespace System.Globalization public static TextInfo ReadOnly(TextInfo textInfo) { if (textInfo == null) { throw new ArgumentNullException(nameof(textInfo)); } - Contract.EndContractBlock(); - if (textInfo.IsReadOnly) { return (textInfo); } + if (textInfo.IsReadOnly) { return textInfo; } TextInfo clonedTextInfo = (TextInfo)(textInfo.MemberwiseClone()); clonedTextInfo.SetReadOnlyState(true); - return (clonedTextInfo); + return clonedTextInfo; } private void VerifyWritable() @@ -256,7 +182,7 @@ namespace System.Globalization // Returns the string used to separate items in a list. // //////////////////////////////////////////////////////////////////////// - public virtual String ListSeparator + public virtual string ListSeparator { get { @@ -264,7 +190,7 @@ namespace System.Globalization { _listSeparator = _cultureData.SLIST; } - return (_listSeparator); + return _listSeparator; } set @@ -288,25 +214,123 @@ namespace System.Globalization //////////////////////////////////////////////////////////////////////// public unsafe virtual char ToLower(char c) { - if (IsAscii(c) && IsAsciiCasingSameAsInvariant) + if (_invariantMode || (IsAscii(c) && IsAsciiCasingSameAsInvariant)) { return ToLowerAsciiInvariant(c); } - return (ChangeCase(c, toUpper: false)); + + return ChangeCase(c, toUpper: false); } - public unsafe virtual String ToLower(String str) + public unsafe virtual string ToLower(string str) { if (str == null) { throw new ArgumentNullException(nameof(str)); } + if (_invariantMode) + { + return ToLowerAsciiInvariant(str); + } + return ChangeCase(str, toUpper: false); } - private static Char ToLowerAsciiInvariant(Char c) + private unsafe string ToLowerAsciiInvariant(string s) + { + if (s.Length == 0) + { + return string.Empty; + } + + fixed (char* pSource = s) + { + int i = 0; + while (i < s.Length) + { + if ((uint)(pSource[i] - 'A') <= (uint)('Z' - 'A')) + { + break; + } + i++; + } + + if (i >= s.Length) + { + return s; + } + + string result = string.FastAllocateString(s.Length); + fixed (char* pResult = result) + { + for (int j = 0; j < i; j++) + { + pResult[j] = pSource[j]; + } + + pResult[i] = (char)(pSource[i] | 0x20); + i++; + + while (i < s.Length) + { + pResult[i] = ToLowerAsciiInvariant(pSource[i]); + i++; + } + } + + return result; + } + } + + private unsafe string ToUpperAsciiInvariant(string s) + { + if (s.Length == 0) + { + return string.Empty; + } + + fixed (char* pSource = s) + { + int i = 0; + while (i < s.Length) + { + if ((uint)(pSource[i] - 'a') <= (uint)('z' - 'a')) + { + break; + } + i++; + } + + if (i >= s.Length) + { + return s; + } + + string result = string.FastAllocateString(s.Length); + fixed (char* pResult = result) + { + for (int j = 0; j < i; j++) + { + pResult[j] = pSource[j]; + } + + pResult[i] = (char)(pSource[i] & ~0x20); + i++; + + while (i < s.Length) + { + pResult[i] = ToUpperAsciiInvariant(pSource[i]); + i++; + } + } + + return result; + } + } + + private static char ToLowerAsciiInvariant(char c) { if ((uint)(c - 'A') <= (uint)('Z' - 'A')) { - c = (Char)(c | 0x20); + c = (char)(c | 0x20); } return c; } @@ -321,30 +345,36 @@ namespace System.Globalization //////////////////////////////////////////////////////////////////////// public unsafe virtual char ToUpper(char c) { - if (IsAscii(c) && IsAsciiCasingSameAsInvariant) + if (_invariantMode || (IsAscii(c) && IsAsciiCasingSameAsInvariant)) { return ToUpperAsciiInvariant(c); } - return (ChangeCase(c, toUpper: true)); + + return ChangeCase(c, toUpper: true); } - public unsafe virtual String ToUpper(String str) + public unsafe virtual string ToUpper(string str) { if (str == null) { throw new ArgumentNullException(nameof(str)); } + if (_invariantMode) + { + return ToUpperAsciiInvariant(str); + } + return ChangeCase(str, toUpper: true); } - private static Char ToUpperAsciiInvariant(Char c) + internal static char ToUpperAsciiInvariant(char c) { if ((uint)(c - 'a') <= (uint)('z' - 'a')) { - c = (Char)(c & ~0x20); + c = (char)(c & ~0x20); } return c; } - private static bool IsAscii(Char c) + private static bool IsAscii(char c) { return c < 0x80; } @@ -367,13 +397,7 @@ namespace System.Globalization // // Returns true if the dominant direction of text and UI such as the relative position of buttons and scroll bars // - public bool IsRightToLeft - { - get - { - return _cultureData.IsRightToLeft; - } - } + public bool IsRightToLeft => _cultureData.IsRightToLeft; //////////////////////////////////////////////////////////////////////// // @@ -389,10 +413,10 @@ namespace System.Globalization if (that != null) { - return this.CultureName.Equals(that.CultureName); + return CultureName.Equals(that.CultureName); } - return (false); + return false; } //////////////////////////////////////////////////////////////////////// @@ -406,7 +430,7 @@ namespace System.Globalization //////////////////////////////////////////////////////////////////////// public override int GetHashCode() { - return (this.CultureName.GetHashCode()); + return CultureName.GetHashCode(); } //////////////////////////////////////////////////////////////////////// @@ -417,9 +441,9 @@ namespace System.Globalization // TextInfo. // //////////////////////////////////////////////////////////////////////// - public override String ToString() + public override string ToString() { - return ("TextInfo - " + _cultureData.CultureName); + return "TextInfo - " + _cultureData.CultureName; } // @@ -436,16 +460,15 @@ namespace System.Globalization // influence which letter or letters of a "word" are uppercased when titlecasing strings. For example // "l'arbre" is considered two words in French, whereas "can't" is considered one word in English. // - public unsafe String ToTitleCase(String str) + public unsafe string ToTitleCase(string str) { if (str == null) { throw new ArgumentNullException(nameof(str)); } - Contract.EndContractBlock(); if (str.Length == 0) { - return (str); + return str; } StringBuilder result = new StringBuilder(); @@ -459,11 +482,11 @@ namespace System.Globalization int charLen; charType = CharUnicodeInfo.InternalGetUnicodeCategory(str, i, out charLen); - if (Char.CheckLetter(charType)) + if (char.CheckLetter(charType)) { // Special case to check for Dutch specific titlecasing with "IJ" characters // at the beginning of a word - if (isDutchCulture && i < str.Length - 1 && (str[i] == 'i' || str[i] == 'I') && (str[i + 1] == 'j' || str[i + 1] == 'J')) + if (isDutchCulture && i < str.Length - 1 && (str[i] == 'i' || str[i] == 'I') && (str[i+1] == 'j' || str[i+1] == 'J')) { result.Append("IJ"); i += 2; @@ -504,7 +527,7 @@ namespace System.Globalization { if (lowercaseData == null) { - lowercaseData = this.ToLower(str); + lowercaseData = ToLower(str); } result.Append(lowercaseData, lowercaseStart, i - lowercaseStart); } @@ -519,7 +542,7 @@ namespace System.Globalization { // This category is considered to be part of the word. // This is any category that is marked as false in wordSeprator array. - i += charLen; + i+= charLen; } else { @@ -536,7 +559,7 @@ namespace System.Globalization { if (lowercaseData == null) { - lowercaseData = this.ToLower(str); + lowercaseData = ToLower(str); } result.Append(lowercaseData, lowercaseStart, count); } @@ -558,10 +581,10 @@ namespace System.Globalization i = AddNonLetter(ref result, ref str, i, charLen); } } - return (result.ToString()); + return result.ToString(); } - private static int AddNonLetter(ref StringBuilder result, ref String input, int inputIndex, int charLen) + private static int AddNonLetter(ref StringBuilder result, ref string input, int inputIndex, int charLen) { Debug.Assert(charLen == 1 || charLen == 2, "[TextInfo.AddNonLetter] CharUnicodeInfo.InternalGetUnicodeCategory returned an unexpected charLen!"); if (charLen == 2) @@ -577,7 +600,7 @@ namespace System.Globalization return inputIndex; } - private int AddTitlecaseLetter(ref StringBuilder result, ref String input, int inputIndex, int charLen) + private int AddTitlecaseLetter(ref StringBuilder result, ref string input, int inputIndex, int charLen) { Debug.Assert(charLen == 1 || charLen == 2, "[TextInfo.AddTitlecaseLetter] CharUnicodeInfo.InternalGetUnicodeCategory returned an unexpected charLen!"); @@ -594,25 +617,25 @@ namespace System.Globalization { // // For AppCompat, the Titlecase Case Mapping data from NDP 2.0 is used below. - case (char)0x01C4: // DZ with Caron -> Dz with Caron - case (char)0x01C5: // Dz with Caron -> Dz with Caron - case (char)0x01C6: // dz with Caron -> Dz with Caron - result.Append((char)0x01C5); + case (char) 0x01C4: // DZ with Caron -> Dz with Caron + case (char) 0x01C5: // Dz with Caron -> Dz with Caron + case (char) 0x01C6: // dz with Caron -> Dz with Caron + result.Append((char) 0x01C5); break; - case (char)0x01C7: // LJ -> Lj - case (char)0x01C8: // Lj -> Lj - case (char)0x01C9: // lj -> Lj - result.Append((char)0x01C8); + case (char) 0x01C7: // LJ -> Lj + case (char) 0x01C8: // Lj -> Lj + case (char) 0x01C9: // lj -> Lj + result.Append((char) 0x01C8); break; - case (char)0x01CA: // NJ -> Nj - case (char)0x01CB: // Nj -> Nj - case (char)0x01CC: // nj -> Nj - result.Append((char)0x01CB); + case (char) 0x01CA: // NJ -> Nj + case (char) 0x01CB: // Nj -> Nj + case (char) 0x01CC: // nj -> Nj + result.Append((char) 0x01CB); break; - case (char)0x01F1: // DZ -> Dz - case (char)0x01F2: // Dz -> Dz - case (char)0x01F3: // dz -> Dz - result.Append((char)0x01F2); + case (char) 0x01F1: // DZ -> Dz + case (char) 0x01F2: // Dz -> Dz + case (char) 0x01F3: // dz -> Dz + result.Append((char) 0x01F2); break; default: result.Append(ToUpper(input[inputIndex])); @@ -627,41 +650,41 @@ namespace System.Globalization // When we find a starting letter, the following array decides if a category should be // considered as word seprator or not. // - private const int c_wordSeparatorMask = - /* false */ (0 << 0) | // UppercaseLetter = 0, - /* false */ (0 << 1) | // LowercaseLetter = 1, - /* false */ (0 << 2) | // TitlecaseLetter = 2, - /* false */ (0 << 3) | // ModifierLetter = 3, - /* false */ (0 << 4) | // OtherLetter = 4, - /* false */ (0 << 5) | // NonSpacingMark = 5, - /* false */ (0 << 6) | // SpacingCombiningMark = 6, - /* false */ (0 << 7) | // EnclosingMark = 7, - /* false */ (0 << 8) | // DecimalDigitNumber = 8, - /* false */ (0 << 9) | // LetterNumber = 9, - /* false */ (0 << 10) | // OtherNumber = 10, - /* true */ (1 << 11) | // SpaceSeparator = 11, - /* true */ (1 << 12) | // LineSeparator = 12, - /* true */ (1 << 13) | // ParagraphSeparator = 13, - /* true */ (1 << 14) | // Control = 14, - /* true */ (1 << 15) | // Format = 15, - /* false */ (0 << 16) | // Surrogate = 16, - /* false */ (0 << 17) | // PrivateUse = 17, - /* true */ (1 << 18) | // ConnectorPunctuation = 18, - /* true */ (1 << 19) | // DashPunctuation = 19, - /* true */ (1 << 20) | // OpenPunctuation = 20, - /* true */ (1 << 21) | // ClosePunctuation = 21, - /* true */ (1 << 22) | // InitialQuotePunctuation = 22, - /* true */ (1 << 23) | // FinalQuotePunctuation = 23, - /* true */ (1 << 24) | // OtherPunctuation = 24, - /* true */ (1 << 25) | // MathSymbol = 25, - /* true */ (1 << 26) | // CurrencySymbol = 26, - /* true */ (1 << 27) | // ModifierSymbol = 27, - /* true */ (1 << 28) | // OtherSymbol = 28, - /* false */ (0 << 29); // OtherNotAssigned = 29; - - private static bool IsWordSeparator(UnicodeCategory category) + private const int c_wordSeparatorMask = + /* false */ (0 << 0) | // UppercaseLetter = 0, + /* false */ (0 << 1) | // LowercaseLetter = 1, + /* false */ (0 << 2) | // TitlecaseLetter = 2, + /* false */ (0 << 3) | // ModifierLetter = 3, + /* false */ (0 << 4) | // OtherLetter = 4, + /* false */ (0 << 5) | // NonSpacingMark = 5, + /* false */ (0 << 6) | // SpacingCombiningMark = 6, + /* false */ (0 << 7) | // EnclosingMark = 7, + /* false */ (0 << 8) | // DecimalDigitNumber = 8, + /* false */ (0 << 9) | // LetterNumber = 9, + /* false */ (0 << 10) | // OtherNumber = 10, + /* true */ (1 << 11) | // SpaceSeparator = 11, + /* true */ (1 << 12) | // LineSeparator = 12, + /* true */ (1 << 13) | // ParagraphSeparator = 13, + /* true */ (1 << 14) | // Control = 14, + /* true */ (1 << 15) | // Format = 15, + /* false */ (0 << 16) | // Surrogate = 16, + /* false */ (0 << 17) | // PrivateUse = 17, + /* true */ (1 << 18) | // ConnectorPunctuation = 18, + /* true */ (1 << 19) | // DashPunctuation = 19, + /* true */ (1 << 20) | // OpenPunctuation = 20, + /* true */ (1 << 21) | // ClosePunctuation = 21, + /* true */ (1 << 22) | // InitialQuotePunctuation = 22, + /* true */ (1 << 23) | // FinalQuotePunctuation = 23, + /* true */ (1 << 24) | // OtherPunctuation = 24, + /* true */ (1 << 25) | // MathSymbol = 25, + /* true */ (1 << 26) | // CurrencySymbol = 26, + /* true */ (1 << 27) | // ModifierSymbol = 27, + /* true */ (1 << 28) | // OtherSymbol = 28, + /* false */ (0 << 29); // OtherNotAssigned = 29; + + private static bool IsWordSeparator(UnicodeCategory category) { - return (c_wordSeparatorMask & (1 << (int)category)) != 0; + return (c_wordSeparatorMask & (1 << (int) category)) != 0; } private static bool IsLetterCategory(UnicodeCategory uc) @@ -676,7 +699,7 @@ namespace System.Globalization // // Get case-insensitive hash code for the specified string. // - internal unsafe int GetCaseInsensitiveHashCode(String str) + internal unsafe int GetCaseInsensitiveHashCode(string str) { // Validate inputs if (str == null) @@ -714,7 +737,7 @@ namespace System.Globalization return (int)hash; } - private unsafe int GetCaseInsensitiveHashCodeSlow(String str) + private unsafe int GetCaseInsensitiveHashCodeSlow(string str) { Debug.Assert(str != null); diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/ThaiBuddhistCalendar.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/ThaiBuddhistCalendar.cs index e1646bfa8e..4f8fd8f019 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/ThaiBuddhistCalendar.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/ThaiBuddhistCalendar.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; namespace System.Globalization { @@ -122,7 +121,6 @@ namespace System.Globalization } - [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems. public override int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek) { return (helper.GetWeekOfYear(time, rule, firstDayOfWeek)); @@ -226,7 +224,6 @@ namespace System.Globalization throw new ArgumentOutOfRangeException(nameof(year), SR.ArgumentOutOfRange_NeedNonNegNum); } - Contract.EndContractBlock(); return (helper.ToFourDigitYear(year, this.TwoDigitYearMax)); } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TimeSpanFormat.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TimeSpanFormat.cs new file mode 100644 index 0000000000..bf12b246b0 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TimeSpanFormat.cs @@ -0,0 +1,536 @@ +// 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.Text; +using System.Diagnostics; + +namespace System.Globalization +{ + internal static class TimeSpanFormat + { + private static unsafe void AppendNonNegativeInt32(StringBuilder sb, int n, int digits) + { + Debug.Assert(n >= 0); + uint value = (uint)n; + + const int MaxUInt32Digits = 10; + char* buffer = stackalloc char[MaxUInt32Digits]; + + int index = 0; + do + { + uint div = value / 10; + buffer[index++] = (char)(value - (div * 10) + '0'); + value = div; + } + while (value != 0); + Debug.Assert(index <= MaxUInt32Digits); + + for (int i = digits - index; i > 0; --i) sb.Append('0'); + for (int i = index - 1; i >= 0; --i) sb.Append(buffer[i]); + } + + internal static readonly FormatLiterals PositiveInvariantFormatLiterals = TimeSpanFormat.FormatLiterals.InitInvariant(isNegative: false); + internal static readonly FormatLiterals NegativeInvariantFormatLiterals = TimeSpanFormat.FormatLiterals.InitInvariant(isNegative: true); + + internal enum Pattern + { + None = 0, + Minimum = 1, + Full = 2, + } + + /// Main method called from TimeSpan.ToString. + internal static string Format(TimeSpan value, string format, IFormatProvider formatProvider) => + StringBuilderCache.GetStringAndRelease(FormatToBuilder(value, format, formatProvider)); + + /// Main method called from TimeSpan.TryFormat. + internal static bool TryFormat(TimeSpan value, Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider formatProvider) + { + StringBuilder sb = FormatToBuilder(value, format, formatProvider); + if (sb.Length <= destination.Length) + { + charsWritten = sb.Length; + sb.CopyTo(0, destination, sb.Length); + StringBuilderCache.Release(sb); + return true; + } + else + { + StringBuilderCache.Release(sb); + charsWritten = 0; + return false; + } + } + + private static StringBuilder FormatToBuilder(TimeSpan value, ReadOnlySpan format, IFormatProvider formatProvider) + { + if (format.Length == 0) + { + format = "c"; + } + + // Standard formats + if (format.Length == 1) + { + char f = format[0]; + switch (f) + { + case 'c': + case 't': + case 'T': + return FormatStandard( + value, + isInvariant: true, + format: format, + pattern: Pattern.Minimum); + + case 'g': + case 'G': + DateTimeFormatInfo dtfi = DateTimeFormatInfo.GetInstance(formatProvider); + return FormatStandard( + value, + isInvariant: false, + format: value.Ticks < 0 ? dtfi.FullTimeSpanNegativePattern : dtfi.FullTimeSpanPositivePattern, + pattern: f == 'g' ? Pattern.Minimum : Pattern.Full); + + default: + throw new FormatException(SR.Format_InvalidString); + } + } + + // Custom formats + return FormatCustomized(value, format, DateTimeFormatInfo.GetInstance(formatProvider), result: null); + } + + /// Format the TimeSpan instance using the specified format. + private static StringBuilder FormatStandard(TimeSpan value, bool isInvariant, ReadOnlySpan format, Pattern pattern) + { + StringBuilder sb = StringBuilderCache.Acquire(InternalGlobalizationHelper.StringBuilderDefaultCapacity); + int day = (int)(value.Ticks / TimeSpan.TicksPerDay); + long time = value.Ticks % TimeSpan.TicksPerDay; + + if (value.Ticks < 0) + { + day = -day; + time = -time; + } + int hours = (int)(time / TimeSpan.TicksPerHour % 24); + int minutes = (int)(time / TimeSpan.TicksPerMinute % 60); + int seconds = (int)(time / TimeSpan.TicksPerSecond % 60); + int fraction = (int)(time % TimeSpan.TicksPerSecond); + + FormatLiterals literal; + if (isInvariant) + { + literal = value.Ticks < 0 ? + NegativeInvariantFormatLiterals : + PositiveInvariantFormatLiterals; + } + else + { + literal = new FormatLiterals(); + literal.Init(format, pattern == Pattern.Full); + } + + if (fraction != 0) + { + // truncate the partial second to the specified length + fraction = (int)(fraction / TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - literal.ff)); + } + + // Pattern.Full: [-]dd.hh:mm:ss.fffffff + // Pattern.Minimum: [-][d.]hh:mm:ss[.fffffff] + + sb.Append(literal.Start); // [-] + if (pattern == Pattern.Full || day != 0) + { + sb.Append(day); // [dd] + sb.Append(literal.DayHourSep); // [.] + } // + AppendNonNegativeInt32(sb, hours, literal.hh); // hh + sb.Append(literal.HourMinuteSep); // : + AppendNonNegativeInt32(sb, minutes, literal.mm); // mm + sb.Append(literal.MinuteSecondSep); // : + AppendNonNegativeInt32(sb, seconds, literal.ss); // ss + if (!isInvariant && pattern == Pattern.Minimum) + { + int effectiveDigits = literal.ff; + while (effectiveDigits > 0) + { + if (fraction % 10 == 0) + { + fraction = fraction / 10; + effectiveDigits--; + } + else + { + break; + } + } + if (effectiveDigits > 0) + { + sb.Append(literal.SecondFractionSep); // [.FFFFFFF] + sb.Append((fraction).ToString(DateTimeFormat.fixedNumberFormats[effectiveDigits - 1], CultureInfo.InvariantCulture)); + } + } + else if (pattern == Pattern.Full || fraction != 0) + { + sb.Append(literal.SecondFractionSep); // [.] + AppendNonNegativeInt32(sb, fraction, literal.ff); // [fffffff] + } + sb.Append(literal.End); + + return sb; + } + + /// Format the TimeSpan instance using the specified format. + private static StringBuilder FormatCustomized(TimeSpan value, ReadOnlySpan format, DateTimeFormatInfo dtfi, StringBuilder result) + { + Debug.Assert(dtfi != null); + + bool resultBuilderIsPooled = false; + if (result == null) + { + result = StringBuilderCache.Acquire(InternalGlobalizationHelper.StringBuilderDefaultCapacity); + resultBuilderIsPooled = true; + } + + int day = (int)(value.Ticks / TimeSpan.TicksPerDay); + long time = value.Ticks % TimeSpan.TicksPerDay; + + if (value.Ticks < 0) + { + day = -day; + time = -time; + } + int hours = (int)(time / TimeSpan.TicksPerHour % 24); + int minutes = (int)(time / TimeSpan.TicksPerMinute % 60); + int seconds = (int)(time / TimeSpan.TicksPerSecond % 60); + int fraction = (int)(time % TimeSpan.TicksPerSecond); + + long tmp = 0; + int i = 0; + int tokenLen; + + while (i < format.Length) + { + char ch = format[i]; + int nextChar; + switch (ch) + { + case 'h': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > 2) + { + goto default; // to release the builder and throw + } + DateTimeFormat.FormatDigits(result, hours, tokenLen); + break; + case 'm': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > 2) + { + goto default; // to release the builder and throw + } + DateTimeFormat.FormatDigits(result, minutes, tokenLen); + break; + case 's': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > 2) + { + goto default; // to release the builder and throw + } + DateTimeFormat.FormatDigits(result, seconds, tokenLen); + break; + case 'f': + // + // The fraction of a second in single-digit precision. The remaining digits are truncated. + // + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits) + { + goto default; // to release the builder and throw + } + + tmp = fraction; + tmp /= TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen); + result.Append((tmp).ToString(DateTimeFormat.fixedNumberFormats[tokenLen - 1], CultureInfo.InvariantCulture)); + break; + case 'F': + // + // Displays the most significant digit of the seconds fraction. Nothing is displayed if the digit is zero. + // + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits) + { + goto default; // to release the builder and throw + } + + tmp = fraction; + tmp /= TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen); + int effectiveDigits = tokenLen; + while (effectiveDigits > 0) + { + if (tmp % 10 == 0) + { + tmp = tmp / 10; + effectiveDigits--; + } + else + { + break; + } + } + if (effectiveDigits > 0) + { + result.Append((tmp).ToString(DateTimeFormat.fixedNumberFormats[effectiveDigits - 1], CultureInfo.InvariantCulture)); + } + break; + case 'd': + // + // tokenLen == 1 : Day as digits with no leading zero. + // tokenLen == 2+: Day as digits with leading zero for single-digit days. + // + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > 8) + { + goto default; // to release the builder and throw + } + + DateTimeFormat.FormatDigits(result, day, tokenLen, true); + break; + case '\'': + case '\"': + tokenLen = DateTimeFormat.ParseQuoteString(format, i, result); + break; + case '%': + // Optional format character. + // For example, format string "%d" will print day + // Most of the cases, "%" can be ignored. + nextChar = DateTimeFormat.ParseNextChar(format, i); + // nextChar will be -1 if we already reach the end of the format string. + // Besides, we will not allow "%%" appear in the pattern. + if (nextChar >= 0 && nextChar != (int)'%') + { + char nextCharChar = (char)nextChar; + StringBuilder origStringBuilder = FormatCustomized(value, ReadOnlySpan.DangerousCreate(null, ref nextCharChar, 1), dtfi, result); + Debug.Assert(ReferenceEquals(origStringBuilder, result)); + tokenLen = 2; + } + else + { + // + // This means that '%' is at the end of the format string or + // "%%" appears in the format string. + // + goto default; // to release the builder and throw + } + break; + case '\\': + // Escaped character. Can be used to insert character into the format string. + // For example, "\d" will insert the character 'd' into the string. + // + nextChar = DateTimeFormat.ParseNextChar(format, i); + if (nextChar >= 0) + { + result.Append(((char)nextChar)); + tokenLen = 2; + } + else + { + // + // This means that '\' is at the end of the formatting string. + // + goto default; // to release the builder and throw + } + break; + default: + // Invalid format string + if (resultBuilderIsPooled) + { + StringBuilderCache.Release(result); + } + throw new FormatException(SR.Format_InvalidString); + } + i += tokenLen; + } + return result; + } + + internal struct FormatLiterals + { + internal string AppCompatLiteral; + internal int dd; + internal int hh; + internal int mm; + internal int ss; + internal int ff; + + private string[] _literals; + + internal string Start => _literals[0]; + internal string DayHourSep => _literals[1]; + internal string HourMinuteSep => _literals[2]; + internal string MinuteSecondSep => _literals[3]; + internal string SecondFractionSep => _literals[4]; + internal string End => _literals[5]; + + /* factory method for static invariant FormatLiterals */ + internal static FormatLiterals InitInvariant(bool isNegative) + { + FormatLiterals x = new FormatLiterals(); + x._literals = new string[6]; + x._literals[0] = isNegative ? "-" : string.Empty; + x._literals[1] = "."; + x._literals[2] = ":"; + x._literals[3] = ":"; + x._literals[4] = "."; + x._literals[5] = string.Empty; + x.AppCompatLiteral = ":."; // MinuteSecondSep+SecondFractionSep; + x.dd = 2; + x.hh = 2; + x.mm = 2; + x.ss = 2; + x.ff = DateTimeFormat.MaxSecondsFractionDigits; + return x; + } + + // For the "v1" TimeSpan localized patterns, the data is simply literal field separators with + // the constants guaranteed to include DHMSF ordered greatest to least significant. + // Once the data becomes more complex than this we will need to write a proper tokenizer for + // parsing and formatting + internal void Init(ReadOnlySpan format, bool useInvariantFieldLengths) + { + dd = hh = mm = ss = ff = 0; + _literals = new string[6]; + for (int i = 0; i < _literals.Length; i++) + { + _literals[i] = string.Empty; + } + + StringBuilder sb = StringBuilderCache.Acquire(InternalGlobalizationHelper.StringBuilderDefaultCapacity); + bool inQuote = false; + char quote = '\''; + int field = 0; + + for (int i = 0; i < format.Length; i++) + { + switch (format[i]) + { + case '\'': + case '\"': + if (inQuote && (quote == format[i])) + { + /* we were in a quote and found a matching exit quote, so we are outside a quote now */ + if (field >= 0 && field <= 5) + { + _literals[field] = sb.ToString(); + sb.Length = 0; + inQuote = false; + } + else + { + Debug.Fail($"Unexpected field value: {field}"); + return; // how did we get here? + } + } + else if (!inQuote) + { + /* we are at the start of a new quote block */ + quote = format[i]; + inQuote = true; + } + else + { + /* we were in a quote and saw the other type of quote character, so we are still in a quote */ + } + break; + case '%': + Debug.Fail("Unexpected special token '%', Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + goto default; + case '\\': + if (!inQuote) + { + i++; /* skip next character that is escaped by this backslash or percent sign */ + break; + } + goto default; + case 'd': + if (!inQuote) + { + Debug.Assert((field == 0 && sb.Length == 0) || field == 1, "field == 0 || field == 1, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + field = 1; // DayHourSep + dd++; + } + break; + case 'h': + if (!inQuote) + { + Debug.Assert((field == 1 && sb.Length == 0) || field == 2, "field == 1 || field == 2, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + field = 2; // HourMinuteSep + hh++; + } + break; + case 'm': + if (!inQuote) + { + Debug.Assert((field == 2 && sb.Length == 0) || field == 3, "field == 2 || field == 3, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + field = 3; // MinuteSecondSep + mm++; + } + break; + case 's': + if (!inQuote) + { + Debug.Assert((field == 3 && sb.Length == 0) || field == 4, "field == 3 || field == 4, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + field = 4; // SecondFractionSep + ss++; + } + break; + case 'f': + case 'F': + if (!inQuote) + { + Debug.Assert((field == 4 && sb.Length == 0) || field == 5, "field == 4 || field == 5, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + field = 5; // End + ff++; + } + break; + default: + sb.Append(format[i]); + break; + } + } + + Debug.Assert(field == 5); + AppCompatLiteral = MinuteSecondSep + SecondFractionSep; + + Debug.Assert(0 < dd && dd < 3, "0 < dd && dd < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + Debug.Assert(0 < hh && hh < 3, "0 < hh && hh < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + Debug.Assert(0 < mm && mm < 3, "0 < mm && mm < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + Debug.Assert(0 < ss && ss < 3, "0 < ss && ss < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + Debug.Assert(0 < ff && ff < 8, "0 < ff && ff < 8, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + + if (useInvariantFieldLengths) + { + dd = 2; + hh = 2; + mm = 2; + ss = 2; + ff = DateTimeFormat.MaxSecondsFractionDigits; + } + else + { + if (dd < 1 || dd > 2) dd = 2; // The DTFI property has a problem. let's try to make the best of the situation. + if (hh < 1 || hh > 2) hh = 2; + if (mm < 1 || mm > 2) mm = 2; + if (ss < 1 || ss > 2) ss = 2; + if (ff < 1 || ff > 7) ff = 7; + } + StringBuilderCache.Release(sb); + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TimeSpanParse.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TimeSpanParse.cs new file mode 100644 index 0000000000..ae77957cec --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/TimeSpanParse.cs @@ -0,0 +1,1666 @@ +// 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. + +//////////////////////////////////////////////////////////////////////////// +// +// Purpose: Used by TimeSpan to parse a time interval string. +// +// Standard Format: +// -=-=-=-=-=-=-=- +// "c": Constant format. [-][d'.']hh':'mm':'ss['.'fffffff] +// Not culture sensitive. Default format (and null/empty format string) map to this format. +// +// "g": General format, short: [-][d':']h':'mm':'ss'.'FFFFFFF +// Only print what's needed. Localized (if you want Invariant, pass in Invariant). +// The fractional seconds separator is localized, equal to the culture's DecimalSeparator. +// +// "G": General format, long: [-]d':'hh':'mm':'ss'.'fffffff +// Always print days and 7 fractional digits. Localized (if you want Invariant, pass in Invariant). +// The fractional seconds separator is localized, equal to the culture's DecimalSeparator. +// +// * "TryParseTimeSpan" is the main method for Parse/TryParse +// +// - TimeSpanTokenizer.GetNextToken() is used to split the input string into number and literal tokens. +// - TimeSpanRawInfo.ProcessToken() adds the next token into the parsing intermediary state structure +// - ProcessTerminalState() uses the fully initialized TimeSpanRawInfo to find a legal parse match. +// The terminal states are attempted as follows: +// foreach (+InvariantPattern, -InvariantPattern, +LocalizedPattern, -LocalizedPattern) try +// 1 number => d +// 2 numbers => h:m +// 3 numbers => h:m:s | d.h:m | h:m:.f +// 4 numbers => h:m:s.f | d.h:m:s | d.h:m:.f +// 5 numbers => d.h:m:s.f +// +// Custom Format: +// -=-=-=-=-=-=-= +// +// * "TryParseExactTimeSpan" is the main method for ParseExact/TryParseExact methods +// * "TryParseExactMultipleTimeSpan" is the main method for ParseExact/TryparseExact +// methods that take a string[] of formats +// +// - For single-letter formats "TryParseTimeSpan" is called (see above) +// - For multi-letter formats "TryParseByFormat" is called +// - TryParseByFormat uses helper methods (ParseExactLiteral, ParseExactDigits, etc) +// which drive the underlying TimeSpanTokenizer. However, unlike standard formatting which +// operates on whole-tokens, ParseExact operates at the character-level. As such, +// TimeSpanTokenizer.NextChar and TimeSpanTokenizer.BackOne() are called directly. +// +//////////////////////////////////////////////////////////////////////////// + +using System.Diagnostics; +using System.Text; + +namespace System.Globalization +{ + internal static class TimeSpanParse + { + private const int MaxFractionDigits = 7; + private const int MaxDays = 10675199; + private const int MaxHours = 23; + private const int MaxMinutes = 59; + private const int MaxSeconds = 59; + private const int MaxFraction = 9999999; + + private enum ParseFailureKind : byte + { + None = 0, + ArgumentNull = 1, + Format = 2, + FormatWithParameter = 3, + Overflow = 4, + } + + [Flags] + private enum TimeSpanStandardStyles : byte + { + // Standard Format Styles + None = 0x00000000, + Invariant = 0x00000001, //Allow Invariant Culture + Localized = 0x00000002, //Allow Localized Culture + RequireFull = 0x00000004, //Require the input to be in DHMSF format + Any = Invariant | Localized, + } + + // TimeSpan Token Types + private enum TTT : byte + { + None = 0, // None of the TimeSpanToken fields are set + End = 1, // '\0' + Num = 2, // Number + Sep = 3, // literal + NumOverflow = 4, // Number that overflowed + } + + private ref struct TimeSpanToken + { + internal TTT _ttt; + internal int _num; // Store the number that we are parsing (if any) + internal int _zeroes; // Store the number of leading zeroes (if any) + internal ReadOnlySpan _sep; // Store the literal that we are parsing (if any) + + public TimeSpanToken(TTT type) : this(type, 0, 0, default(ReadOnlySpan)) { } + + public TimeSpanToken(int number) : this(TTT.Num, number, 0, default(ReadOnlySpan)) { } + + public TimeSpanToken(int number, int leadingZeroes) : this(TTT.Num, number, leadingZeroes, default(ReadOnlySpan)) { } + + public TimeSpanToken(TTT type, int number, int leadingZeroes, ReadOnlySpan separator) + { + _ttt = type; + _num = number; + _zeroes = leadingZeroes; + _sep = separator; + } + + public bool IsInvalidFraction() + { + Debug.Assert(_ttt == TTT.Num); + Debug.Assert(_num > -1); + + if (_num > MaxFraction || _zeroes > MaxFractionDigits) + return true; + + if (_num == 0 || _zeroes == 0) + return false; + + // num > 0 && zeroes > 0 && num <= maxValue && zeroes <= maxPrecision + return _num >= MaxFraction / Pow10(_zeroes - 1); + } + } + + private ref struct TimeSpanTokenizer + { + private ReadOnlySpan _value; + private int _pos; + + internal TimeSpanTokenizer(ReadOnlySpan input) : this(input, 0) { } + + internal TimeSpanTokenizer(ReadOnlySpan input, int startPosition) + { + _value = input; + _pos = startPosition; + } + + /// Returns the next token in the input string + /// Used by the parsing routines that operate on standard-formats. + internal TimeSpanToken GetNextToken() + { + // Get the position of the next character to be processed. If there is no + // next character, we're at the end. + int pos = _pos; + Debug.Assert(pos > -1); + if (pos >= _value.Length) + { + return new TimeSpanToken(TTT.End); + } + + // Now retrieve that character. If it's a digit, we're processing a number. + int num = _value[pos] - '0'; + if ((uint)num <= 9) + { + int zeroes = 0; + if (num == 0) + { + // Read all leading zeroes. + zeroes = 1; + while (true) + { + int digit; + if (++_pos >= _value.Length || (uint)(digit = _value[_pos] - '0') > 9) + { + return new TimeSpanToken(TTT.Num, 0, zeroes, default(ReadOnlySpan)); + } + + if (digit == 0) + { + zeroes++; + continue; + } + + num = digit; + break; + } + } + + // Continue to read as long as we're reading digits. + while (++_pos < _value.Length) + { + int digit = _value[_pos] - '0'; + if ((uint)digit > 9) + { + break; + } + + num = num * 10 + digit; + if ((num & 0xF0000000) != 0) + { + return new TimeSpanToken(TTT.NumOverflow); + } + } + + return new TimeSpanToken(TTT.Num, num, zeroes, default(ReadOnlySpan)); + } + + // Otherwise, we're processing a separator, and we've already processed the first + // character of it. Continue processing characters as long as they're not digits. + int length = 1; + while (true) + { + if (++_pos >= _value.Length || (uint)(_value[_pos] - '0') <= 9) + { + break; + } + length++; + } + + // Return the separator. + return new TimeSpanToken(TTT.Sep, 0, 0, _value.Slice(pos, length)); + } + + internal bool EOL => _pos >= (_value.Length - 1); + + internal void BackOne() + { + if (_pos > 0) --_pos; + } + + internal char NextChar + { + get + { + int pos = ++_pos; + return (uint)pos < (uint)_value.Length ? + _value[pos] : + (char)0; + } + } + } + + /// Stores intermediary parsing state for the standard formats. + private ref struct TimeSpanRawInfo + { + internal TimeSpanFormat.FormatLiterals PositiveInvariant => TimeSpanFormat.PositiveInvariantFormatLiterals; + internal TimeSpanFormat.FormatLiterals NegativeInvariant => TimeSpanFormat.NegativeInvariantFormatLiterals; + + internal TimeSpanFormat.FormatLiterals PositiveLocalized + { + get + { + if (!_posLocInit) + { + _posLoc = new TimeSpanFormat.FormatLiterals(); + _posLoc.Init(_fullPosPattern, false); + _posLocInit = true; + } + return _posLoc; + } + } + + internal TimeSpanFormat.FormatLiterals NegativeLocalized + { + get + { + if (!_negLocInit) + { + _negLoc = new TimeSpanFormat.FormatLiterals(); + _negLoc.Init(_fullNegPattern, false); + _negLocInit = true; + } + return _negLoc; + } + } + + internal bool FullAppCompatMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 5 + && _numCount == 4 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.DayHourSep) + && StringSpanHelpers.Equals(_literals2, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals3, pattern.AppCompatLiteral) + && StringSpanHelpers.Equals(_literals4, pattern.End); + + internal bool PartialAppCompatMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 4 + && _numCount == 3 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals2, pattern.AppCompatLiteral) + && StringSpanHelpers.Equals(_literals3, pattern.End); + + /// DHMSF (all values matched) + internal bool FullMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == MaxLiteralTokens + && _numCount == MaxNumericTokens + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.DayHourSep) + && StringSpanHelpers.Equals(_literals2, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals3, pattern.MinuteSecondSep) + && StringSpanHelpers.Equals(_literals4, pattern.SecondFractionSep) + && StringSpanHelpers.Equals(_literals5, pattern.End); + + /// D (no hours, minutes, seconds, or fractions) + internal bool FullDMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 2 + && _numCount == 1 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.End); + + /// HM (no days, seconds, or fractions) + internal bool FullHMMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 3 + && _numCount == 2 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals2, pattern.End); + + /// DHM (no seconds or fraction) + internal bool FullDHMMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 4 + && _numCount == 3 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.DayHourSep) + && StringSpanHelpers.Equals(_literals2, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals3, pattern.End); + + /// HMS (no days or fraction) + internal bool FullHMSMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 4 + && _numCount == 3 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals2, pattern.MinuteSecondSep) + && StringSpanHelpers.Equals(_literals3, pattern.End); + + /// DHMS (no fraction) + internal bool FullDHMSMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 5 + && _numCount == 4 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.DayHourSep) + && StringSpanHelpers.Equals(_literals2, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals3, pattern.MinuteSecondSep) + && StringSpanHelpers.Equals(_literals4, pattern.End); + + /// HMSF (no days) + internal bool FullHMSFMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 5 + && _numCount == 4 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals2, pattern.MinuteSecondSep) + && StringSpanHelpers.Equals(_literals3, pattern.SecondFractionSep) + && StringSpanHelpers.Equals(_literals4, pattern.End); + + internal TTT _lastSeenTTT; + internal int _tokenCount; + internal int _sepCount; + internal int _numCount; + + private TimeSpanFormat.FormatLiterals _posLoc; + private TimeSpanFormat.FormatLiterals _negLoc; + private bool _posLocInit; + private bool _negLocInit; + private string _fullPosPattern; + private string _fullNegPattern; + + private const int MaxTokens = 11; + private const int MaxLiteralTokens = 6; + private const int MaxNumericTokens = 5; + + internal TimeSpanToken _numbers0, _numbers1, _numbers2, _numbers3, _numbers4; // MaxNumbericTokens = 5 + internal ReadOnlySpan _literals0, _literals1, _literals2, _literals3, _literals4, _literals5; // MaxLiteralTokens=6 + + internal void Init(DateTimeFormatInfo dtfi) + { + Debug.Assert(dtfi != null); + + _lastSeenTTT = TTT.None; + _tokenCount = 0; + _sepCount = 0; + _numCount = 0; + + _fullPosPattern = dtfi.FullTimeSpanPositivePattern; + _fullNegPattern = dtfi.FullTimeSpanNegativePattern; + _posLocInit = false; + _negLocInit = false; + } + + internal bool ProcessToken(ref TimeSpanToken tok, ref TimeSpanResult result) + { + switch (tok._ttt) + { + case TTT.Num: + if ((_tokenCount == 0 && !AddSep(default(ReadOnlySpan), ref result)) || !AddNum(tok, ref result)) + { + return false; + } + break; + + case TTT.Sep: + if (!AddSep(tok._sep, ref result)) + { + return false; + } + break; + + case TTT.NumOverflow: + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + + default: + // Some unknown token or a repeat token type in the input + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + _lastSeenTTT = tok._ttt; + Debug.Assert(_tokenCount == (_sepCount + _numCount), "tokenCount == (SepCount + NumCount)"); + return true; + } + + private bool AddSep(ReadOnlySpan sep, ref TimeSpanResult result) + { + if (_sepCount >= MaxLiteralTokens || _tokenCount >= MaxTokens) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + switch (_sepCount++) + { + case 0: _literals0 = sep; break; + case 1: _literals1 = sep; break; + case 2: _literals2 = sep; break; + case 3: _literals3 = sep; break; + case 4: _literals4 = sep; break; + default: _literals5 = sep; break; + } + + _tokenCount++; + return true; + } + private bool AddNum(TimeSpanToken num, ref TimeSpanResult result) + { + if (_numCount >= MaxNumericTokens || _tokenCount >= MaxTokens) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + switch (_numCount++) + { + case 0: _numbers0 = num; break; + case 1: _numbers1 = num; break; + case 2: _numbers2 = num; break; + case 3: _numbers3 = num; break; + default: _numbers4 = num; break; + } + + _tokenCount++; + return true; + } + } + + /// Store the result of the parsing. + private struct TimeSpanResult + { + internal TimeSpan parsedTimeSpan; + private readonly bool _throwOnFailure; + + internal TimeSpanResult(bool throwOnFailure) + { + parsedTimeSpan = default(TimeSpan); + _throwOnFailure = throwOnFailure; + } + + internal bool SetFailure(ParseFailureKind kind, string resourceKey, object messageArgument = null, string argumentName = null) + { + if (!_throwOnFailure) + { + return false; + } + + string message = SR.GetResourceString(resourceKey); + switch (kind) + { + case ParseFailureKind.ArgumentNull: + Debug.Assert(argumentName != null); + throw new ArgumentNullException(argumentName, message); + + case ParseFailureKind.FormatWithParameter: + throw new FormatException(SR.Format(message, messageArgument)); + + case ParseFailureKind.Overflow: + throw new OverflowException(message); + + default: + Debug.Assert(kind == ParseFailureKind.Format, $"Unexpected failure {kind}"); + throw new FormatException(message); + } + } + } + + internal static long Pow10(int pow) + { + switch (pow) + { + case 0: return 1; + case 1: return 10; + case 2: return 100; + case 3: return 1000; + case 4: return 10000; + case 5: return 100000; + case 6: return 1000000; + case 7: return 10000000; + default: return (long)Math.Pow(10, pow); + } + } + + private static bool TryTimeToTicks(bool positive, TimeSpanToken days, TimeSpanToken hours, TimeSpanToken minutes, TimeSpanToken seconds, TimeSpanToken fraction, out long result) + { + if (days._num > MaxDays || + hours._num > MaxHours || + minutes._num > MaxMinutes || + seconds._num > MaxSeconds || + fraction.IsInvalidFraction()) + { + result = 0; + return false; + } + + long ticks = ((long)days._num * 3600 * 24 + (long)hours._num * 3600 + (long)minutes._num * 60 + seconds._num) * 1000; + if (ticks > InternalGlobalizationHelper.MaxMilliSeconds || ticks < InternalGlobalizationHelper.MinMilliSeconds) + { + result = 0; + return false; + } + + // Normalize the fraction component + // + // string representation => (zeroes,num) => resultant fraction ticks + // --------------------- ------------ ------------------------ + // ".9999999" => (0,9999999) => 9,999,999 ticks (same as constant maxFraction) + // ".1" => (0,1) => 1,000,000 ticks + // ".01" => (1,1) => 100,000 ticks + // ".001" => (2,1) => 10,000 ticks + long f = fraction._num; + if (f != 0) + { + long lowerLimit = InternalGlobalizationHelper.TicksPerTenthSecond; + if (fraction._zeroes > 0) + { + long divisor = Pow10(fraction._zeroes); + lowerLimit = lowerLimit / divisor; + } + + while (f < lowerLimit) + { + f *= 10; + } + } + + result = ticks * TimeSpan.TicksPerMillisecond + f; + if (positive && result < 0) + { + result = 0; + return false; + } + + return true; + } + + internal static TimeSpan Parse(ReadOnlySpan input, IFormatProvider formatProvider) + { + var parseResult = new TimeSpanResult(throwOnFailure: true); + bool success = TryParseTimeSpan(input, TimeSpanStandardStyles.Any, formatProvider, ref parseResult); + Debug.Assert(success, "Should have thrown on failure"); + return parseResult.parsedTimeSpan; + } + + internal static bool TryParse(ReadOnlySpan input, IFormatProvider formatProvider, out TimeSpan result) + { + var parseResult = new TimeSpanResult(throwOnFailure: false); + + if (TryParseTimeSpan(input, TimeSpanStandardStyles.Any, formatProvider, ref parseResult)) + { + result = parseResult.parsedTimeSpan; + return true; + } + + result = default(TimeSpan); + return false; + } + + internal static TimeSpan ParseExact(ReadOnlySpan input, ReadOnlySpan format, IFormatProvider formatProvider, TimeSpanStyles styles) + { + var parseResult = new TimeSpanResult(throwOnFailure: true); + bool success = TryParseExactTimeSpan(input, format, formatProvider, styles, ref parseResult); + Debug.Assert(success, "Should have thrown on failure"); + return parseResult.parsedTimeSpan; + } + + internal static bool TryParseExact(ReadOnlySpan input, ReadOnlySpan format, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result) + { + var parseResult = new TimeSpanResult(throwOnFailure: false); + + if (TryParseExactTimeSpan(input, format, formatProvider, styles, ref parseResult)) + { + result = parseResult.parsedTimeSpan; + return true; + } + + result = default(TimeSpan); + return false; + } + + internal static TimeSpan ParseExactMultiple(ReadOnlySpan input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles) + { + var parseResult = new TimeSpanResult(throwOnFailure: true); + bool success = TryParseExactMultipleTimeSpan(input, formats, formatProvider, styles, ref parseResult); + Debug.Assert(success, "Should have thrown on failure"); + return parseResult.parsedTimeSpan; + } + + internal static bool TryParseExactMultiple(ReadOnlySpan input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result) + { + var parseResult = new TimeSpanResult(throwOnFailure: false); + + if (TryParseExactMultipleTimeSpan(input, formats, formatProvider, styles, ref parseResult)) + { + result = parseResult.parsedTimeSpan; + return true; + } + + result = default(TimeSpan); + return false; + } + + /// Common private Parse method called by both Parse and TryParse. + private static bool TryParseTimeSpan(ReadOnlySpan input, TimeSpanStandardStyles style, IFormatProvider formatProvider, ref TimeSpanResult result) + { + input = input.Trim(); + if (input.IsEmpty) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + var tokenizer = new TimeSpanTokenizer(input); + + var raw = new TimeSpanRawInfo(); + raw.Init(DateTimeFormatInfo.GetInstance(formatProvider)); + + TimeSpanToken tok = tokenizer.GetNextToken(); + + // The following loop will break out when we reach the end of the str or + // when we can determine that the input is invalid. + while (tok._ttt != TTT.End) + { + if (!raw.ProcessToken(ref tok, ref result)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + tok = tokenizer.GetNextToken(); + } + Debug.Assert(tokenizer.EOL); + + if (!ProcessTerminalState(ref raw, style, ref result)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + return true; + } + + /// + /// Validate the terminal state of a standard format parse. + /// Sets result.parsedTimeSpan on success. + /// Calculates the resultant TimeSpan from the TimeSpanRawInfo. + /// + /// + /// try => +InvariantPattern, -InvariantPattern, +LocalizedPattern, -LocalizedPattern + /// 1) Verify Start matches + /// 2) Verify End matches + /// 3) 1 number => d + /// 2 numbers => h:m + /// 3 numbers => h:m:s | d.h:m | h:m:.f + /// 4 numbers => h:m:s.f | d.h:m:s | d.h:m:.f + /// 5 numbers => d.h:m:s.f + /// + private static bool ProcessTerminalState(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) + { + if (raw._lastSeenTTT == TTT.Num) + { + TimeSpanToken tok = new TimeSpanToken(); + tok._ttt = TTT.Sep; + if (!raw.ProcessToken(ref tok, ref result)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + } + + switch (raw._numCount) + { + case 1: return ProcessTerminal_D(ref raw, style, ref result); + case 2: return ProcessTerminal_HM(ref raw, style, ref result); + case 3: return ProcessTerminal_HM_S_D(ref raw, style, ref result); + case 4: return ProcessTerminal_HMS_F_D(ref raw, style, ref result); + case 5: return ProcessTerminal_DHMSF(ref raw, style, ref result); + default: return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + } + + /// Validate the 5-number "Days.Hours:Minutes:Seconds.Fraction" terminal case. + private static bool ProcessTerminal_DHMSF(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) + { + if (raw._sepCount != 6) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + Debug.Assert(raw._numCount == 5); + + bool inv = (style & TimeSpanStandardStyles.Invariant) != 0; + bool loc = (style & TimeSpanStandardStyles.Localized) != 0; + bool positive = false; + bool match = false; + + if (inv) + { + if (raw.FullMatch(raw.PositiveInvariant)) + { + match = true; + positive = true; + } + if (!match && raw.FullMatch(raw.NegativeInvariant)) + { + match = true; + positive = false; + } + } + + if (loc) + { + if (!match && raw.FullMatch(raw.PositiveLocalized)) + { + match = true; + positive = true; + } + if (!match && raw.FullMatch(raw.NegativeLocalized)) + { + match = true; + positive = false; + } + } + + if (match) + { + long ticks; + + if (!TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, raw._numbers4, out ticks)) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + + if (!positive) + { + ticks = -ticks; + if (ticks > 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + + result.parsedTimeSpan._ticks = ticks; + return true; + } + + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + + /// + /// Validate the ambiguous 4-number "Hours:Minutes:Seconds.Fraction", "Days.Hours:Minutes:Seconds", + /// or "Days.Hours:Minutes:.Fraction" terminal case. + /// + private static bool ProcessTerminal_HMS_F_D(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) + { + if (raw._sepCount != 5 || (style & TimeSpanStandardStyles.RequireFull) != 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + Debug.Assert(raw._numCount == 4); + + bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0); + bool loc = ((style & TimeSpanStandardStyles.Localized) != 0); + + long ticks = 0; + bool positive = false, match = false, overflow = false; + var zero = new TimeSpanToken(0); + + if (inv) + { + if (raw.FullHMSFMatch(raw.PositiveInvariant)) + { + positive = true; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMSMatch(raw.PositiveInvariant)) + { + positive = true; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullAppCompatMatch(raw.PositiveInvariant)) + { + positive = true; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, raw._numbers3, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullHMSFMatch(raw.NegativeInvariant)) + { + positive = false; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMSMatch(raw.NegativeInvariant)) + { + positive = false; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullAppCompatMatch(raw.NegativeInvariant)) + { + positive = false; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, raw._numbers3, out ticks); + overflow = overflow || !match; + } + } + + if (loc) + { + if (!match && raw.FullHMSFMatch(raw.PositiveLocalized)) + { + positive = true; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMSMatch(raw.PositiveLocalized)) + { + positive = true; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullAppCompatMatch(raw.PositiveLocalized)) + { + positive = true; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, raw._numbers3, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullHMSFMatch(raw.NegativeLocalized)) + { + positive = false; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMSMatch(raw.NegativeLocalized)) + { + positive = false; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullAppCompatMatch(raw.NegativeLocalized)) + { + positive = false; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, raw._numbers3, out ticks); + overflow = overflow || !match; + } + } + + if (match) + { + if (!positive) + { + ticks = -ticks; + if (ticks > 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + + result.parsedTimeSpan._ticks = ticks; + return true; + } + + return overflow ? + result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)) : // we found at least one literal pattern match but the numbers just didn't fit + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); // we couldn't find a thing + } + + /// Validate the ambiguous 3-number "Hours:Minutes:Seconds", "Days.Hours:Minutes", or "Hours:Minutes:.Fraction" terminal case. + private static bool ProcessTerminal_HM_S_D(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) + { + if (raw._sepCount != 4 || (style & TimeSpanStandardStyles.RequireFull) != 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + Debug.Assert(raw._numCount == 3); + + bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0); + bool loc = ((style & TimeSpanStandardStyles.Localized) != 0); + + bool positive = false, match = false, overflow = false; + var zero = new TimeSpanToken(0); + long ticks = 0; + + if (inv) + { + if (raw.FullHMSMatch(raw.PositiveInvariant)) + { + positive = true; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMMatch(raw.PositiveInvariant)) + { + positive = true; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.PartialAppCompatMatch(raw.PositiveInvariant)) + { + positive = true; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, zero, raw._numbers2, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullHMSMatch(raw.NegativeInvariant)) + { + positive = false; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMMatch(raw.NegativeInvariant)) + { + positive = false; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.PartialAppCompatMatch(raw.NegativeInvariant)) + { + positive = false; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, zero, raw._numbers2, out ticks); + overflow = overflow || !match; + } + } + + if (loc) + { + if (!match && raw.FullHMSMatch(raw.PositiveLocalized)) + { + positive = true; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMMatch(raw.PositiveLocalized)) + { + positive = true; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.PartialAppCompatMatch(raw.PositiveLocalized)) + { + positive = true; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, zero, raw._numbers2, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullHMSMatch(raw.NegativeLocalized)) + { + positive = false; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMMatch(raw.NegativeLocalized)) + { + positive = false; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.PartialAppCompatMatch(raw.NegativeLocalized)) + { + positive = false; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, zero, raw._numbers2, out ticks); + overflow = overflow || !match; + } + } + + if (match) + { + if (!positive) + { + ticks = -ticks; + if (ticks > 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + + result.parsedTimeSpan._ticks = ticks; + return true; + } + + return overflow ? + result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)) : // we found at least one literal pattern match but the numbers just didn't fit + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); // we couldn't find a thing + } + + /// Validate the 2-number "Hours:Minutes" terminal case. + private static bool ProcessTerminal_HM(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) + { + if (raw._sepCount != 3 || (style & TimeSpanStandardStyles.RequireFull) != 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + Debug.Assert(raw._numCount == 2); + + bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0); + bool loc = ((style & TimeSpanStandardStyles.Localized) != 0); + + bool positive = false, match = false; + + if (inv) + { + if (raw.FullHMMatch(raw.PositiveInvariant)) + { + match = true; + positive = true; + } + + if (!match && raw.FullHMMatch(raw.NegativeInvariant)) + { + match = true; + positive = false; + } + } + + if (loc) + { + if (!match && raw.FullHMMatch(raw.PositiveLocalized)) + { + match = true; + positive = true; + } + + if (!match && raw.FullHMMatch(raw.NegativeLocalized)) + { + match = true; + positive = false; + } + } + + if (match) + { + long ticks = 0; + var zero = new TimeSpanToken(0); + + if (!TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, zero, zero, out ticks)) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + + if (!positive) + { + ticks = -ticks; + if (ticks > 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + + result.parsedTimeSpan._ticks = ticks; + return true; + } + + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + /// Validate the 1-number "Days" terminal case. + private static bool ProcessTerminal_D(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) + { + if (raw._sepCount != 2 || (style & TimeSpanStandardStyles.RequireFull) != 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + Debug.Assert(raw._numCount == 1); + + bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0); + bool loc = ((style & TimeSpanStandardStyles.Localized) != 0); + + bool positive = false, match = false; + + if (inv) + { + if (raw.FullDMatch(raw.PositiveInvariant)) + { + match = true; + positive = true; + } + + if (!match && raw.FullDMatch(raw.NegativeInvariant)) + { + match = true; + positive = false; + } + } + + if (loc) + { + if (!match && raw.FullDMatch(raw.PositiveLocalized)) + { + match = true; + positive = true; + } + + if (!match && raw.FullDMatch(raw.NegativeLocalized)) + { + match = true; + positive = false; + } + } + + if (match) + { + long ticks = 0; + var zero = new TimeSpanToken(0); + + if (!TryTimeToTicks(positive, raw._numbers0, zero, zero, zero, zero, out ticks)) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + + if (!positive) + { + ticks = -ticks; + if (ticks > 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + + result.parsedTimeSpan._ticks = ticks; + return true; + } + + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + /// Common private ParseExact method called by both ParseExact and TryParseExact. + private static bool TryParseExactTimeSpan(ReadOnlySpan input, ReadOnlySpan format, IFormatProvider formatProvider, TimeSpanStyles styles, ref TimeSpanResult result) + { + if (format.Length == 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier)); + } + + if (format.Length == 1) + { + switch (format[0]) + { + case 'c': + case 't': + case 'T': + return TryParseTimeSpanConstant(input, ref result); // fast path for legacy style TimeSpan formats. + + case 'g': + return TryParseTimeSpan(input, TimeSpanStandardStyles.Localized, formatProvider, ref result); + + case 'G': + return TryParseTimeSpan(input, TimeSpanStandardStyles.Localized | TimeSpanStandardStyles.RequireFull, formatProvider, ref result); + + default: + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier)); + } + } + + return TryParseByFormat(input, format, styles, ref result); + } + + /// Parse the TimeSpan instance using the specified format. Used by TryParseExactTimeSpan. + private static bool TryParseByFormat(ReadOnlySpan input, ReadOnlySpan format, TimeSpanStyles styles, ref TimeSpanResult result) + { + bool seenDD = false; // already processed days? + bool seenHH = false; // already processed hours? + bool seenMM = false; // already processed minutes? + bool seenSS = false; // already processed seconds? + bool seenFF = false; // already processed fraction? + + int dd = 0; // parsed days + int hh = 0; // parsed hours + int mm = 0; // parsed minutes + int ss = 0; // parsed seconds + int leadingZeroes = 0; // number of leading zeroes in the parsed fraction + int ff = 0; // parsed fraction + int i = 0; // format string position + int tokenLen = 0; // length of current format token, used to update index 'i' + + var tokenizer = new TimeSpanTokenizer(input, -1); + + while (i < format.Length) + { + char ch = format[i]; + int nextFormatChar; + switch (ch) + { + case 'h': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > 2 || seenHH || !ParseExactDigits(ref tokenizer, tokenLen, out hh)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + seenHH = true; + break; + + case 'm': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > 2 || seenMM || !ParseExactDigits(ref tokenizer, tokenLen, out mm)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + seenMM = true; + break; + + case 's': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > 2 || seenSS || !ParseExactDigits(ref tokenizer, tokenLen, out ss)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + seenSS = true; + break; + + case 'f': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits || seenFF || !ParseExactDigits(ref tokenizer, tokenLen, tokenLen, out leadingZeroes, out ff)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + seenFF = true; + break; + + case 'F': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits || seenFF) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + ParseExactDigits(ref tokenizer, tokenLen, tokenLen, out leadingZeroes, out ff); + seenFF = true; + break; + + case 'd': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + int tmp = 0; + if (tokenLen > 8 || seenDD || !ParseExactDigits(ref tokenizer, (tokenLen < 2) ? 1 : tokenLen, (tokenLen < 2) ? 8 : tokenLen, out tmp, out dd)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + seenDD = true; + break; + + case '\'': + case '\"': + StringBuilder enquotedString = StringBuilderCache.Acquire(); + if (!DateTimeParse.TryParseQuoteString(format, i, enquotedString, out tokenLen)) + { + StringBuilderCache.Release(enquotedString); + return result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadQuote), ch); + } + if (!ParseExactLiteral(ref tokenizer, enquotedString)) + { + StringBuilderCache.Release(enquotedString); + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + StringBuilderCache.Release(enquotedString); + break; + + case '%': + // Optional format character. + // For example, format string "%d" will print day + // Most of the cases, "%" can be ignored. + nextFormatChar = DateTimeFormat.ParseNextChar(format, i); + + // nextFormatChar will be -1 if we already reach the end of the format string. + // Besides, we will not allow "%%" appear in the pattern. + if (nextFormatChar >= 0 && nextFormatChar != '%') + { + tokenLen = 1; // skip the '%' and process the format character + break; + } + else + { + // This means that '%' is at the end of the format string or + // "%%" appears in the format string. + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + + case '\\': + // Escaped character. Can be used to insert character into the format string. + // For example, "\d" will insert the character 'd' into the string. + // + nextFormatChar = DateTimeFormat.ParseNextChar(format, i); + if (nextFormatChar >= 0 && tokenizer.NextChar == (char)nextFormatChar) + { + tokenLen = 2; + } + else + { + // This means that '\' is at the end of the format string or the literal match failed. + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + break; + + default: + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + + i += tokenLen; + } + + + if (!tokenizer.EOL) + { + // the custom format didn't consume the entire input + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + bool positive = (styles & TimeSpanStyles.AssumeNegative) == 0; + if (TryTimeToTicks(positive, new TimeSpanToken(dd), + new TimeSpanToken(hh), + new TimeSpanToken(mm), + new TimeSpanToken(ss), + new TimeSpanToken(ff, leadingZeroes), + out long ticks)) + { + if (!positive) + { + ticks = -ticks; + } + + result.parsedTimeSpan._ticks = ticks; + return true; + } + else + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + + private static bool ParseExactDigits(ref TimeSpanTokenizer tokenizer, int minDigitLength, out int result) + { + result = 0; + int zeroes = 0; + int maxDigitLength = (minDigitLength == 1) ? 2 : minDigitLength; + return ParseExactDigits(ref tokenizer, minDigitLength, maxDigitLength, out zeroes, out result); + } + + private static bool ParseExactDigits(ref TimeSpanTokenizer tokenizer, int minDigitLength, int maxDigitLength, out int zeroes, out int result) + { + int tmpResult = 0, tmpZeroes = 0; + + int tokenLength = 0; + while (tokenLength < maxDigitLength) + { + char ch = tokenizer.NextChar; + if (ch < '0' || ch > '9') + { + tokenizer.BackOne(); + break; + } + + tmpResult = tmpResult * 10 + (ch - '0'); + if (tmpResult == 0) tmpZeroes++; + tokenLength++; + } + + zeroes = tmpZeroes; + result = tmpResult; + return tokenLength >= minDigitLength; + } + + private static bool ParseExactLiteral(ref TimeSpanTokenizer tokenizer, StringBuilder enquotedString) + { + for (int i = 0; i < enquotedString.Length; i++) + { + if (enquotedString[i] != tokenizer.NextChar) + { + return false; + } + } + + return true; + } + + /// + /// Parses the "c" (constant) format. This code is 100% identical to the non-globalized v1.0-v3.5 TimeSpan.Parse() routine + /// and exists for performance/appcompat with legacy callers who cannot move onto the globalized Parse overloads. + /// + private static bool TryParseTimeSpanConstant(ReadOnlySpan input, ref TimeSpanResult result) => + new StringParser().TryParse(input, ref result); + + private ref struct StringParser + { + private ReadOnlySpan _str; + private char _ch; + private int _pos; + private int _len; + + internal void NextChar() + { + if (_pos < _len) + { + _pos++; + } + + _ch = _pos < _len ? + _str[_pos] : + (char)0; + } + + internal char NextNonDigit() + { + int i = _pos; + while (i < _len) + { + char ch = _str[i]; + if (ch < '0' || ch > '9') return ch; + i++; + } + + return (char)0; + } + + internal bool TryParse(ReadOnlySpan input, ref TimeSpanResult result) + { + result.parsedTimeSpan._ticks = 0; + + _str = input; + _len = input.Length; + _pos = -1; + NextChar(); + SkipBlanks(); + + bool negative = false; + if (_ch == '-') + { + negative = true; + NextChar(); + } + + long time; + if (NextNonDigit() == ':') + { + if (!ParseTime(out time, ref result)) + { + return false; + }; + } + else + { + int days; + if (!ParseInt((int)(0x7FFFFFFFFFFFFFFFL / TimeSpan.TicksPerDay), out days, ref result)) + { + return false; + } + + time = days * TimeSpan.TicksPerDay; + + if (_ch == '.') + { + NextChar(); + long remainingTime; + if (!ParseTime(out remainingTime, ref result)) + { + return false; + }; + time += remainingTime; + } + } + + if (negative) + { + time = -time; + // Allow -0 as well + if (time > 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + else + { + if (time < 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + + SkipBlanks(); + + if (_pos < _len) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + result.parsedTimeSpan._ticks = time; + return true; + } + + internal bool ParseInt(int max, out int i, ref TimeSpanResult result) + { + i = 0; + int p = _pos; + while (_ch >= '0' && _ch <= '9') + { + if ((i & 0xF0000000) != 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + + i = i * 10 + _ch - '0'; + if (i < 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + + NextChar(); + } + + if (p == _pos) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + if (i > max) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + + return true; + } + + internal bool ParseTime(out long time, ref TimeSpanResult result) + { + time = 0; + int unit; + + if (!ParseInt(23, out unit, ref result)) + { + return false; + } + + time = unit * TimeSpan.TicksPerHour; + if (_ch != ':') + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + NextChar(); + + if (!ParseInt(59, out unit, ref result)) + { + return false; + } + + time += unit * TimeSpan.TicksPerMinute; + + if (_ch == ':') + { + NextChar(); + + // allow seconds with the leading zero + if (_ch != '.') + { + if (!ParseInt(59, out unit, ref result)) + { + return false; + } + time += unit * TimeSpan.TicksPerSecond; + } + + if (_ch == '.') + { + NextChar(); + int f = (int)TimeSpan.TicksPerSecond; + while (f > 1 && _ch >= '0' && _ch <= '9') + { + f /= 10; + time += (_ch - '0') * f; + NextChar(); + } + } + } + + return true; + } + + internal void SkipBlanks() + { + while (_ch == ' ' || _ch == '\t') NextChar(); + } + } + + /// Common private ParseExactMultiple method called by both ParseExactMultiple and TryParseExactMultiple. + private static bool TryParseExactMultipleTimeSpan(ReadOnlySpan input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, ref TimeSpanResult result) + { + if (formats == null) + { + return result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), argumentName: nameof(formats)); + } + + if (input.Length == 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + if (formats.Length == 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier)); + } + + // Do a loop through the provided formats and see if we can parse succesfully in + // one of the formats. + for (int i = 0; i < formats.Length; i++) + { + if (formats[i] == null || formats[i].Length == 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier)); + } + + // Create a new non-throwing result each time to ensure the runs are independent. + TimeSpanResult innerResult = new TimeSpanResult(throwOnFailure: false); + + if (TryParseExactTimeSpan(input, formats[i], formatProvider, styles, ref innerResult)) + { + result.parsedTimeSpan = innerResult.parsedTimeSpan; + return true; + } + } + + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/UmAlQuraCalendar.cs b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/UmAlQuraCalendar.cs index c03ac23d95..21a938f8f1 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Globalization/UmAlQuraCalendar.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Globalization/UmAlQuraCalendar.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; namespace System.Globalization { @@ -331,9 +330,7 @@ namespace System.Globalization } dt = dt.AddDays(nDays); - yg = dt.Year; - mg = dt.Month; - dg = dt.Day; + dt.GetDatePart(out yg, out mg, out dg); } /*=================================GetAbsoluteDateUmAlQura========================== @@ -519,7 +516,6 @@ namespace System.Globalization -120000, 120000)); } - Contract.EndContractBlock(); // Get the date in UmAlQura calendar. int y = GetDatePart(time, DatePartYear); int m = GetDatePart(time, DatePartMonth); @@ -824,7 +820,6 @@ namespace System.Globalization MinCalendarYear, MaxCalendarYear)); } - Contract.EndContractBlock(); VerifyWritable(); // We allow year 99 to be set so that one can make ToFourDigitYearMax a no-op by setting TwoDigitYearMax to 99. twoDigitYearMax = value; @@ -840,7 +835,6 @@ namespace System.Globalization throw new ArgumentOutOfRangeException(nameof(year), SR.ArgumentOutOfRange_NeedNonNegNum); } - Contract.EndContractBlock(); if (year < 100) { diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Guid.cs b/external/corert/src/System.Private.CoreLib/shared/System/Guid.cs new file mode 100644 index 0000000000..423d5bc78c --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Guid.cs @@ -0,0 +1,1436 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using Internal.Runtime.CompilerServices; + +namespace System +{ + // Represents a Globally Unique Identifier. + [StructLayout(LayoutKind.Sequential)] + [Serializable] + [Runtime.Versioning.NonVersionable] // This only applies to field layout + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public partial struct Guid : IFormattable, IComparable, IComparable, IEquatable, ISpanFormattable + { + public static readonly Guid Empty = new Guid(); + + //////////////////////////////////////////////////////////////////////////////// + // Member variables + //////////////////////////////////////////////////////////////////////////////// + private int _a; // Do not rename (binary serialization) + private short _b; // Do not rename (binary serialization) + private short _c; // Do not rename (binary serialization) + private byte _d; // Do not rename (binary serialization) + private byte _e; // Do not rename (binary serialization) + private byte _f; // Do not rename (binary serialization) + private byte _g; // Do not rename (binary serialization) + private byte _h; // Do not rename (binary serialization) + private byte _i; // Do not rename (binary serialization) + private byte _j; // Do not rename (binary serialization) + private byte _k; // Do not rename (binary serialization) + + //////////////////////////////////////////////////////////////////////////////// + // Constructors + //////////////////////////////////////////////////////////////////////////////// + + // Creates a new guid from an array of bytes. + public Guid(byte[] b) : + this(new ReadOnlySpan(b ?? throw new ArgumentNullException(nameof(b)))) + { + } + + // Creates a new guid from a read-only span. + public Guid(ReadOnlySpan b) + { + if (b.Length != 16) + throw new ArgumentException(SR.Format(SR.Arg_GuidArrayCtor, "16"), nameof(b)); + + _a = b[3] << 24 | b[2] << 16 | b[1] << 8 | b[0]; + _b = (short)(b[5] << 8 | b[4]); + _c = (short)(b[7] << 8 | b[6]); + _d = b[8]; + _e = b[9]; + _f = b[10]; + _g = b[11]; + _h = b[12]; + _i = b[13]; + _j = b[14]; + _k = b[15]; + } + + [CLSCompliant(false)] + public Guid(uint a, ushort b, ushort c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k) + { + _a = (int)a; + _b = (short)b; + _c = (short)c; + _d = d; + _e = e; + _f = f; + _g = g; + _h = h; + _i = i; + _j = j; + _k = k; + } + + // Creates a new GUID initialized to the value represented by the arguments. + // + public Guid(int a, short b, short c, byte[] d) + { + if (d == null) + throw new ArgumentNullException(nameof(d)); + // Check that array is not too big + if (d.Length != 8) + throw new ArgumentException(SR.Format(SR.Arg_GuidArrayCtor, "8"), nameof(d)); + + _a = a; + _b = b; + _c = c; + _d = d[0]; + _e = d[1]; + _f = d[2]; + _g = d[3]; + _h = d[4]; + _i = d[5]; + _j = d[6]; + _k = d[7]; + } + + // Creates a new GUID initialized to the value represented by the + // arguments. The bytes are specified like this to avoid endianness issues. + // + public Guid(int a, short b, short c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k) + { + _a = a; + _b = b; + _c = c; + _d = d; + _e = e; + _f = f; + _g = g; + _h = h; + _i = i; + _j = j; + _k = k; + } + + [Flags] + private enum GuidStyles + { + None = 0x00000000, + AllowParenthesis = 0x00000001, //Allow the guid to be enclosed in parens + AllowBraces = 0x00000002, //Allow the guid to be enclosed in braces + AllowDashes = 0x00000004, //Allow the guid to contain dash group separators + AllowHexPrefix = 0x00000008, //Allow the guid to contain {0xdd,0xdd} + RequireParenthesis = 0x00000010, //Require the guid to be enclosed in parens + RequireBraces = 0x00000020, //Require the guid to be enclosed in braces + RequireDashes = 0x00000040, //Require the guid to contain dash group separators + RequireHexPrefix = 0x00000080, //Require the guid to contain {0xdd,0xdd} + + HexFormat = RequireBraces | RequireHexPrefix, /* X */ + NumberFormat = None, /* N */ + DigitFormat = RequireDashes, /* D */ + BraceFormat = RequireBraces | RequireDashes, /* B */ + ParenthesisFormat = RequireParenthesis | RequireDashes, /* P */ + + Any = AllowParenthesis | AllowBraces | AllowDashes | AllowHexPrefix, + } + private enum GuidParseThrowStyle + { + None = 0, + All = 1, + AllButOverflow = 2 + } + private enum ParseFailureKind + { + None = 0, + ArgumentNull = 1, + Format = 2, + FormatWithParameter = 3, + NativeException = 4, + FormatWithInnerException = 5 + } + + // This will store the result of the parsing. And it will eventually be used to construct a Guid instance. + private struct GuidResult + { + internal Guid _parsedGuid; + internal GuidParseThrowStyle _throwStyle; + + private ParseFailureKind _failure; + private string _failureMessageID; + private object _failureMessageFormatArgument; + private string _failureArgumentName; + private Exception _innerException; + + internal void Init(GuidParseThrowStyle canThrow) + { + _throwStyle = canThrow; + } + + internal void SetFailure(Exception nativeException) + { + _failure = ParseFailureKind.NativeException; + _innerException = nativeException; + } + + internal void SetFailure(ParseFailureKind failure, string failureMessageID) + { + SetFailure(failure, failureMessageID, null, null, null); + } + + internal void SetFailure(ParseFailureKind failure, string failureMessageID, object failureMessageFormatArgument) + { + SetFailure(failure, failureMessageID, failureMessageFormatArgument, null, null); + } + + internal void SetFailure(ParseFailureKind failure, string failureMessageID, object failureMessageFormatArgument, + string failureArgumentName, Exception innerException) + { + Debug.Assert(failure != ParseFailureKind.NativeException, "ParseFailureKind.NativeException should not be used with this overload"); + _failure = failure; + _failureMessageID = failureMessageID; + _failureMessageFormatArgument = failureMessageFormatArgument; + _failureArgumentName = failureArgumentName; + _innerException = innerException; + if (_throwStyle != GuidParseThrowStyle.None) + { + throw GetGuidParseException(); + } + } + + internal Exception GetGuidParseException() + { + switch (_failure) + { + case ParseFailureKind.ArgumentNull: + return new ArgumentNullException(_failureArgumentName, SR.GetResourceString(_failureMessageID)); + + case ParseFailureKind.FormatWithInnerException: + return new FormatException(SR.GetResourceString(_failureMessageID), _innerException); + + case ParseFailureKind.FormatWithParameter: + return new FormatException(SR.Format(SR.GetResourceString(_failureMessageID), _failureMessageFormatArgument)); + + case ParseFailureKind.Format: + return new FormatException(SR.GetResourceString(_failureMessageID)); + + case ParseFailureKind.NativeException: + return _innerException; + + default: + Debug.Fail("Unknown GuidParseFailure: " + _failure); + return new FormatException(SR.Format_GuidUnrecognized); + } + } + } + + // Creates a new guid based on the value in the string. The value is made up + // of hex digits speared by the dash ("-"). The string may begin and end with + // brackets ("{", "}"). + // + // The string must be of the form dddddddd-dddd-dddd-dddd-dddddddddddd. where + // d is a hex digit. (That is 8 hex digits, followed by 4, then 4, then 4, + // then 12) such as: "CA761232-ED42-11CE-BACD-00AA0057B223" + // + public Guid(string g) + { + if (g == null) + { + throw new ArgumentNullException(nameof(g)); + } + + GuidResult result = new GuidResult(); + result.Init(GuidParseThrowStyle.All); + if (TryParseGuid(g, GuidStyles.Any, ref result)) + { + this = result._parsedGuid; + } + else + { + throw result.GetGuidParseException(); + } + } + + public static Guid Parse(string input) => + Parse(input != null ? (ReadOnlySpan)input : throw new ArgumentNullException(nameof(input))); + + public static Guid Parse(ReadOnlySpan input) + { + GuidResult result = new GuidResult(); + result.Init(GuidParseThrowStyle.AllButOverflow); + if (TryParseGuid(input, GuidStyles.Any, ref result)) + { + return result._parsedGuid; + } + else + { + throw result.GetGuidParseException(); + } + } + + public static bool TryParse(string input, out Guid result) + { + if (input == null) + { + result = default(Guid); + return false; + } + + return TryParse((ReadOnlySpan)input, out result); + } + + public static bool TryParse(ReadOnlySpan input, out Guid result) + { + GuidResult parseResult = new GuidResult(); + parseResult.Init(GuidParseThrowStyle.None); + if (TryParseGuid(input, GuidStyles.Any, ref parseResult)) + { + result = parseResult._parsedGuid; + return true; + } + else + { + result = default(Guid); + return false; + } + } + + public static Guid ParseExact(string input, string format) => + ParseExact( + input != null ? (ReadOnlySpan)input : throw new ArgumentNullException(nameof(input)), + format != null ? (ReadOnlySpan)format : throw new ArgumentNullException(nameof(format))); + + public static Guid ParseExact(ReadOnlySpan input, ReadOnlySpan format) + { + if (format.Length != 1) + { + // all acceptable format strings are of length 1 + throw new FormatException(SR.Format_InvalidGuidFormatSpecification); + } + + GuidStyles style; + switch (format[0]) + { + case 'D': + case 'd': + style = GuidStyles.DigitFormat; + break; + case 'N': + case 'n': + style = GuidStyles.NumberFormat; + break; + case 'B': + case 'b': + style = GuidStyles.BraceFormat; + break; + case 'P': + case 'p': + style = GuidStyles.ParenthesisFormat; + break; + case 'X': + case 'x': + style = GuidStyles.HexFormat; + break; + default: + throw new FormatException(SR.Format_InvalidGuidFormatSpecification); + } + + GuidResult result = new GuidResult(); + result.Init(GuidParseThrowStyle.AllButOverflow); + if (TryParseGuid(input, style, ref result)) + { + return result._parsedGuid; + } + else + { + throw result.GetGuidParseException(); + } + } + + public static bool TryParseExact(string input, string format, out Guid result) + { + if (input == null) + { + result = default(Guid); + return false; + } + + return TryParseExact((ReadOnlySpan)input, format, out result); + } + + public static bool TryParseExact(ReadOnlySpan input, ReadOnlySpan format, out Guid result) + { + if (format.Length != 1) + { + result = default(Guid); + return false; + } + + GuidStyles style; + switch (format[0]) + { + case 'D': + case 'd': + style = GuidStyles.DigitFormat; + break; + case 'N': + case 'n': + style = GuidStyles.NumberFormat; + break; + case 'B': + case 'b': + style = GuidStyles.BraceFormat; + break; + case 'P': + case 'p': + style = GuidStyles.ParenthesisFormat; + break; + case 'X': + case 'x': + style = GuidStyles.HexFormat; + break; + default: + // invalid guid format specification + result = default(Guid); + return false; + } + + GuidResult parseResult = new GuidResult(); + parseResult.Init(GuidParseThrowStyle.None); + if (TryParseGuid(input, style, ref parseResult)) + { + result = parseResult._parsedGuid; + return true; + } + else + { + result = default(Guid); + return false; + } + } + + private static bool TryParseGuid(ReadOnlySpan guidString, GuidStyles flags, ref GuidResult result) + { + guidString = guidString.Trim(); // Remove whitespace from beginning and end + + if (guidString.Length == 0) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); + return false; + } + + // Check for dashes + bool dashesExistInString = guidString.IndexOf('-') >= 0; + + if (dashesExistInString) + { + if ((flags & (GuidStyles.AllowDashes | GuidStyles.RequireDashes)) == 0) + { + // dashes are not allowed + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); + return false; + } + } + else + { + if ((flags & GuidStyles.RequireDashes) != 0) + { + // dashes are required + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); + return false; + } + } + + // Check for braces + bool bracesExistInString = (guidString.IndexOf('{', 0) >= 0); + + if (bracesExistInString) + { + if ((flags & (GuidStyles.AllowBraces | GuidStyles.RequireBraces)) == 0) + { + // braces are not allowed + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); + return false; + } + } + else + { + if ((flags & GuidStyles.RequireBraces) != 0) + { + // braces are required + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); + return false; + } + } + + // Check for parenthesis + bool parenthesisExistInString = (guidString.IndexOf('(', 0) >= 0); + + if (parenthesisExistInString) + { + if ((flags & (GuidStyles.AllowParenthesis | GuidStyles.RequireParenthesis)) == 0) + { + // parenthesis are not allowed + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); + return false; + } + } + else + { + if ((flags & GuidStyles.RequireParenthesis) != 0) + { + // parenthesis are required + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); + return false; + } + } + + try + { + // let's get on with the parsing + if (dashesExistInString) + { + // Check if it's of the form [{|(]dddddddd-dddd-dddd-dddd-dddddddddddd[}|)] + return TryParseGuidWithDashes(guidString, ref result); + } + else if (bracesExistInString) + { + // Check if it's of the form {0xdddddddd,0xdddd,0xdddd,{0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd}} + return TryParseGuidWithHexPrefix(guidString, ref result); + } + else + { + // Check if it's of the form dddddddddddddddddddddddddddddddd + return TryParseGuidWithNoStyle(guidString, ref result); + } + } + catch (IndexOutOfRangeException ex) + { + result.SetFailure(ParseFailureKind.FormatWithInnerException, nameof(SR.Format_GuidUnrecognized), null, null, ex); + return false; + } + catch (ArgumentException ex) + { + result.SetFailure(ParseFailureKind.FormatWithInnerException, nameof(SR.Format_GuidUnrecognized), null, null, ex); + return false; + } + } + + // Check if it's of the form {0xdddddddd,0xdddd,0xdddd,{0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd}} + private static bool TryParseGuidWithHexPrefix(ReadOnlySpan guidString, ref GuidResult result) + { + int numStart = 0; + int numLen = 0; + + // Eat all of the whitespace + guidString = EatAllWhitespace(guidString); + + // Check for leading '{' + if (guidString.Length == 0 || guidString[0] != '{') + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidBrace)); + return false; + } + + // Check for '0x' + if (!IsHexPrefix(guidString, 1)) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidHexPrefix), "{0xdddddddd, etc}"); + return false; + } + + // Find the end of this hex number (since it is not fixed length) + numStart = 3; + numLen = guidString.IndexOf(',', numStart) - numStart; + if (numLen <= 0) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma)); + return false; + } + + if (!StringToInt(guidString.Slice(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._a, ref result)) + return false; + + // Check for '0x' + if (!IsHexPrefix(guidString, numStart + numLen + 1)) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidHexPrefix), "{0xdddddddd, 0xdddd, etc}"); + return false; + } + // +3 to get by ',0x' + numStart = numStart + numLen + 3; + numLen = guidString.IndexOf(',', numStart) - numStart; + if (numLen <= 0) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma)); + return false; + } + + // Read in the number + if (!StringToShort(guidString.Slice(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._b, ref result)) + return false; + // Check for '0x' + if (!IsHexPrefix(guidString, numStart + numLen + 1)) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidHexPrefix), "{0xdddddddd, 0xdddd, 0xdddd, etc}"); + return false; + } + // +3 to get by ',0x' + numStart = numStart + numLen + 3; + numLen = guidString.IndexOf(',', numStart) - numStart; + if (numLen <= 0) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma)); + return false; + } + + // Read in the number + if (!StringToShort(guidString.Slice(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._c, ref result)) + return false; + + // Check for '{' + if (guidString.Length <= numStart + numLen + 1 || guidString[numStart + numLen + 1] != '{') + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidBrace)); + return false; + } + + // Prepare for loop + numLen++; + Span bytes = stackalloc byte[8]; + + for (int i = 0; i < bytes.Length; i++) + { + // Check for '0x' + if (!IsHexPrefix(guidString, numStart + numLen + 1)) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidHexPrefix), "{... { ... 0xdd, ...}}"); + return false; + } + + // +3 to get by ',0x' or '{0x' for first case + numStart = numStart + numLen + 3; + + // Calculate number length + if (i < 7) // first 7 cases + { + numLen = guidString.IndexOf(',', numStart) - numStart; + if (numLen <= 0) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma)); + return false; + } + } + else // last case ends with '}', not ',' + { + numLen = guidString.IndexOf('}', numStart) - numStart; + if (numLen <= 0) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidBraceAfterLastNumber)); + return false; + } + } + + // Read in the number + int signedNumber; + if (!StringToInt(guidString.Slice(numStart, numLen), -1, ParseNumbers.IsTight, out signedNumber, ref result)) + { + return false; + } + uint number = (uint)signedNumber; + + // check for overflow + if (number > 255) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Overflow_Byte)); + return false; + } + bytes[i] = (byte)number; + } + + result._parsedGuid._d = bytes[0]; + result._parsedGuid._e = bytes[1]; + result._parsedGuid._f = bytes[2]; + result._parsedGuid._g = bytes[3]; + result._parsedGuid._h = bytes[4]; + result._parsedGuid._i = bytes[5]; + result._parsedGuid._j = bytes[6]; + result._parsedGuid._k = bytes[7]; + + // Check for last '}' + if (numStart + numLen + 1 >= guidString.Length || guidString[numStart + numLen + 1] != '}') + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidEndBrace)); + return false; + } + + // Check if we have extra characters at the end + if (numStart + numLen + 1 != guidString.Length - 1) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_ExtraJunkAtEnd)); + return false; + } + + return true; + } + + // Check if it's of the form dddddddddddddddddddddddddddddddd + private static bool TryParseGuidWithNoStyle(ReadOnlySpan guidString, ref GuidResult result) + { + int startPos = 0; + int temp; + long templ; + int currentPos = 0; + + if (guidString.Length != 32) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); + return false; + } + + for (int i = 0; i < guidString.Length; i++) + { + char ch = guidString[i]; + if (ch >= '0' && ch <= '9') + { + continue; + } + else + { + char upperCaseCh = char.ToUpperInvariant(ch); + if (upperCaseCh >= 'A' && upperCaseCh <= 'F') + { + continue; + } + } + + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvalidChar)); + return false; + } + + if (!StringToInt(guidString.Slice(startPos, 8) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._a, ref result)) + return false; + + startPos += 8; + if (!StringToShort(guidString.Slice(startPos, 4), -1, ParseNumbers.IsTight, out result._parsedGuid._b, ref result)) + return false; + + startPos += 4; + if (!StringToShort(guidString.Slice(startPos, 4), -1, ParseNumbers.IsTight, out result._parsedGuid._c, ref result)) + return false; + + startPos += 4; + if (!StringToInt(guidString.Slice(startPos, 4), -1, ParseNumbers.IsTight, out temp, ref result)) + return false; + + startPos += 4; + currentPos = startPos; + + if (!StringToLong(guidString, ref currentPos, ParseNumbers.NoSpace, out templ, ref result)) + return false; + + if (currentPos - startPos != 12) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); + return false; + } + + result._parsedGuid._d = (byte)(temp >> 8); + result._parsedGuid._e = (byte)(temp); + temp = (int)(templ >> 32); + result._parsedGuid._f = (byte)(temp >> 8); + result._parsedGuid._g = (byte)(temp); + temp = (int)(templ); + result._parsedGuid._h = (byte)(temp >> 24); + result._parsedGuid._i = (byte)(temp >> 16); + result._parsedGuid._j = (byte)(temp >> 8); + result._parsedGuid._k = (byte)(temp); + + return true; + } + + // Check if it's of the form [{|(]dddddddd-dddd-dddd-dddd-dddddddddddd[}|)] + private static bool TryParseGuidWithDashes(ReadOnlySpan guidString, ref GuidResult result) + { + int startPos = 0; + int temp; + long templ; + int currentPos = 0; + + // check to see that it's the proper length + if (guidString[0] == '{') + { + if (guidString.Length != 38 || guidString[37] != '}') + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); + return false; + } + startPos = 1; + } + else if (guidString[0] == '(') + { + if (guidString.Length != 38 || guidString[37] != ')') + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); + return false; + } + startPos = 1; + } + else if (guidString.Length != 36) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); + return false; + } + + if (guidString[8 + startPos] != '-' || + guidString[13 + startPos] != '-' || + guidString[18 + startPos] != '-' || + guidString[23 + startPos] != '-') + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidDashes)); + return false; + } + + currentPos = startPos; + if (!StringToInt(guidString, ref currentPos, 8, ParseNumbers.NoSpace, out temp, ref result)) + return false; + result._parsedGuid._a = temp; + ++currentPos; //Increment past the '-'; + + if (!StringToInt(guidString, ref currentPos, 4, ParseNumbers.NoSpace, out temp, ref result)) + return false; + result._parsedGuid._b = (short)temp; + ++currentPos; //Increment past the '-'; + + if (!StringToInt(guidString, ref currentPos, 4, ParseNumbers.NoSpace, out temp, ref result)) + return false; + result._parsedGuid._c = (short)temp; + ++currentPos; //Increment past the '-'; + + if (!StringToInt(guidString, ref currentPos, 4, ParseNumbers.NoSpace, out temp, ref result)) + return false; + ++currentPos; //Increment past the '-'; + startPos = currentPos; + + if (!StringToLong(guidString, ref currentPos, ParseNumbers.NoSpace, out templ, ref result)) + return false; + + if (currentPos - startPos != 12) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); + return false; + } + result._parsedGuid._d = (byte)(temp >> 8); + result._parsedGuid._e = (byte)(temp); + temp = (int)(templ >> 32); + result._parsedGuid._f = (byte)(temp >> 8); + result._parsedGuid._g = (byte)(temp); + temp = (int)(templ); + result._parsedGuid._h = (byte)(temp >> 24); + result._parsedGuid._i = (byte)(temp >> 16); + result._parsedGuid._j = (byte)(temp >> 8); + result._parsedGuid._k = (byte)(temp); + + return true; + } + + private static bool StringToShort(ReadOnlySpan str, int requiredLength, int flags, out short result, ref GuidResult parseResult) + { + int parsePos = 0; + return StringToShort(str, ref parsePos, requiredLength, flags, out result, ref parseResult); + } + + private static bool StringToShort(ReadOnlySpan str, ref int parsePos, int requiredLength, int flags, out short result, ref GuidResult parseResult) + { + result = 0; + int x; + bool retValue = StringToInt(str, ref parsePos, requiredLength, flags, out x, ref parseResult); + result = (short)x; + return retValue; + } + + private static bool StringToInt(ReadOnlySpan str, int requiredLength, int flags, out int result, ref GuidResult parseResult) + { + int parsePos = 0; + return StringToInt(str, ref parsePos, requiredLength, flags, out result, ref parseResult); + } + + private static bool StringToInt(ReadOnlySpan str, ref int parsePos, int requiredLength, int flags, out int result, ref GuidResult parseResult) + { + result = 0; + + int currStart = parsePos; + try + { + result = ParseNumbers.StringToInt(str, 16, flags, ref parsePos); + } + catch (OverflowException ex) + { + if (parseResult._throwStyle == GuidParseThrowStyle.All) + { + throw; + } + else if (parseResult._throwStyle == GuidParseThrowStyle.AllButOverflow) + { + throw new FormatException(SR.Format_GuidUnrecognized, ex); + } + else + { + parseResult.SetFailure(ex); + return false; + } + } + catch (Exception ex) + { + if (parseResult._throwStyle == GuidParseThrowStyle.None) + { + parseResult.SetFailure(ex); + return false; + } + else + { + throw; + } + } + + //If we didn't parse enough characters, there's clearly an error. + if (requiredLength != -1 && parsePos - currStart != requiredLength) + { + parseResult.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvalidChar)); + return false; + } + return true; + } + + private static unsafe bool StringToLong(ReadOnlySpan str, ref int parsePos, int flags, out long result, ref GuidResult parseResult) + { + result = 0; + + try + { + result = ParseNumbers.StringToLong(str, 16, flags, ref parsePos); + } + catch (OverflowException ex) + { + if (parseResult._throwStyle == GuidParseThrowStyle.All) + { + throw; + } + else if (parseResult._throwStyle == GuidParseThrowStyle.AllButOverflow) + { + throw new FormatException(SR.Format_GuidUnrecognized, ex); + } + else + { + parseResult.SetFailure(ex); + return false; + } + } + catch (Exception ex) + { + if (parseResult._throwStyle == GuidParseThrowStyle.None) + { + parseResult.SetFailure(ex); + return false; + } + else + { + throw; + } + } + return true; + } + + private static ReadOnlySpan EatAllWhitespace(ReadOnlySpan str) + { + // Find the first whitespace character. If there is none, just return the input. + int i; + for (i = 0; i < str.Length && !char.IsWhiteSpace(str[i]); i++) ; + if (i == str.Length) + { + return str; + } + + // There was at least one whitespace. Copy over everything prior to it to a new array. + var chArr = new char[str.Length]; + int newLength = 0; + if (i > 0) + { + newLength = i; + str.Slice(0, i).CopyTo(chArr); + } + + // Loop through the remaining chars, copying over non-whitespace. + for (; i < str.Length; i++) + { + char c = str[i]; + if (!char.IsWhiteSpace(c)) + { + chArr[newLength++] = c; + } + } + + // Return the string with the whitespace removed. + return new ReadOnlySpan(chArr, 0, newLength); + } + + private static bool IsHexPrefix(ReadOnlySpan str, int i) => + i + 1 < str.Length && + str[i] == '0' && + (str[i + 1] == 'x' || char.ToLowerInvariant(str[i + 1]) == 'x'); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteByteHelper(Span destination) + { + destination[0] = (byte)(_a); + destination[1] = (byte)(_a >> 8); + destination[2] = (byte)(_a >> 16); + destination[3] = (byte)(_a >> 24); + destination[4] = (byte)(_b); + destination[5] = (byte)(_b >> 8); + destination[6] = (byte)(_c); + destination[7] = (byte)(_c >> 8); + destination[8] = _d; + destination[9] = _e; + destination[10] = _f; + destination[11] = _g; + destination[12] = _h; + destination[13] = _i; + destination[14] = _j; + destination[15] = _k; + } + + // Returns an unsigned byte array containing the GUID. + public byte[] ToByteArray() + { + var g = new byte[16]; + WriteByteHelper(g); + return g; + } + + // Returns whether bytes are sucessfully written to given span. + public bool TryWriteBytes(Span destination) + { + if (destination.Length < 16) + return false; + + WriteByteHelper(destination); + return true; + } + + // Returns the guid in "registry" format. + public override string ToString() + { + return ToString("D", null); + } + + public override int GetHashCode() + { + // Simply XOR all the bits of the GUID 32 bits at a time. + return _a ^ Unsafe.Add(ref _a, 1) ^ Unsafe.Add(ref _a, 2) ^ Unsafe.Add(ref _a, 3); + } + + // Returns true if and only if the guid represented + // by o is the same as this instance. + public override bool Equals(object o) + { + Guid g; + // Check that o is a Guid first + if (o == null || !(o is Guid)) + return false; + else g = (Guid)o; + + // Now compare each of the elements + return g._a == _a && + Unsafe.Add(ref g._a, 1) == Unsafe.Add(ref _a, 1) && + Unsafe.Add(ref g._a, 2) == Unsafe.Add(ref _a, 2) && + Unsafe.Add(ref g._a, 3) == Unsafe.Add(ref _a, 3); + } + + public bool Equals(Guid g) + { + // Now compare each of the elements + return g._a == _a && + Unsafe.Add(ref g._a, 1) == Unsafe.Add(ref _a, 1) && + Unsafe.Add(ref g._a, 2) == Unsafe.Add(ref _a, 2) && + Unsafe.Add(ref g._a, 3) == Unsafe.Add(ref _a, 3); + } + + private int GetResult(uint me, uint them) + { + if (me < them) + { + return -1; + } + return 1; + } + + public int CompareTo(object value) + { + if (value == null) + { + return 1; + } + if (!(value is Guid)) + { + throw new ArgumentException(SR.Arg_MustBeGuid, nameof(value)); + } + Guid g = (Guid)value; + + if (g._a != _a) + { + return GetResult((uint)_a, (uint)g._a); + } + + if (g._b != _b) + { + return GetResult((uint)_b, (uint)g._b); + } + + if (g._c != _c) + { + return GetResult((uint)_c, (uint)g._c); + } + + if (g._d != _d) + { + return GetResult(_d, g._d); + } + + if (g._e != _e) + { + return GetResult(_e, g._e); + } + + if (g._f != _f) + { + return GetResult(_f, g._f); + } + + if (g._g != _g) + { + return GetResult(_g, g._g); + } + + if (g._h != _h) + { + return GetResult(_h, g._h); + } + + if (g._i != _i) + { + return GetResult(_i, g._i); + } + + if (g._j != _j) + { + return GetResult(_j, g._j); + } + + if (g._k != _k) + { + return GetResult(_k, g._k); + } + + return 0; + } + + public int CompareTo(Guid value) + { + if (value._a != _a) + { + return GetResult((uint)_a, (uint)value._a); + } + + if (value._b != _b) + { + return GetResult((uint)_b, (uint)value._b); + } + + if (value._c != _c) + { + return GetResult((uint)_c, (uint)value._c); + } + + if (value._d != _d) + { + return GetResult(_d, value._d); + } + + if (value._e != _e) + { + return GetResult(_e, value._e); + } + + if (value._f != _f) + { + return GetResult(_f, value._f); + } + + if (value._g != _g) + { + return GetResult(_g, value._g); + } + + if (value._h != _h) + { + return GetResult(_h, value._h); + } + + if (value._i != _i) + { + return GetResult(_i, value._i); + } + + if (value._j != _j) + { + return GetResult(_j, value._j); + } + + if (value._k != _k) + { + return GetResult(_k, value._k); + } + + return 0; + } + + public static bool operator ==(Guid a, Guid b) + { + // Now compare each of the elements + return a._a == b._a && + Unsafe.Add(ref a._a, 1) == Unsafe.Add(ref b._a, 1) && + Unsafe.Add(ref a._a, 2) == Unsafe.Add(ref b._a, 2) && + Unsafe.Add(ref a._a, 3) == Unsafe.Add(ref b._a, 3); + } + + public static bool operator !=(Guid a, Guid b) + { + // Now compare each of the elements + return a._a != b._a || + Unsafe.Add(ref a._a, 1) != Unsafe.Add(ref b._a, 1) || + Unsafe.Add(ref a._a, 2) != Unsafe.Add(ref b._a, 2) || + Unsafe.Add(ref a._a, 3) != Unsafe.Add(ref b._a, 3); + } + + public string ToString(string format) + { + return ToString(format, null); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static char HexToChar(int a) + { + a = a & 0xf; + return (char)((a > 9) ? a - 10 + 0x61 : a + 0x30); + } + + unsafe private static int HexsToChars(char* guidChars, int a, int b) + { + guidChars[0] = HexToChar(a >> 4); + guidChars[1] = HexToChar(a); + + guidChars[2] = HexToChar(b >> 4); + guidChars[3] = HexToChar(b); + + return 4; + } + + unsafe private static int HexsToCharsHexOutput(char* guidChars, int a, int b) + { + guidChars[0] = '0'; + guidChars[1] = 'x'; + + guidChars[2] = HexToChar(a >> 4); + guidChars[3] = HexToChar(a); + + guidChars[4] = ','; + guidChars[5] = '0'; + guidChars[6] = 'x'; + + guidChars[7] = HexToChar(b >> 4); + guidChars[8] = HexToChar(b); + + return 9; + } + + // IFormattable interface + // We currently ignore provider + public string ToString(string format, IFormatProvider provider) + { + if (format == null || format.Length == 0) + format = "D"; + + // all acceptable format strings are of length 1 + if (format.Length != 1) + throw new FormatException(SR.Format_InvalidGuidFormatSpecification); + + int guidSize; + switch (format[0]) + { + case 'D': + case 'd': + guidSize = 36; + break; + case 'N': + case 'n': + guidSize = 32; + break; + case 'B': + case 'b': + case 'P': + case 'p': + guidSize = 38; + break; + case 'X': + case 'x': + guidSize = 68; + break; + default: + throw new FormatException(SR.Format_InvalidGuidFormatSpecification); + } + + string guidString = string.FastAllocateString(guidSize); + + int bytesWritten; + bool result = TryFormat(new Span(ref guidString.GetRawStringData(), guidString.Length), out bytesWritten, format); + Debug.Assert(result && bytesWritten == guidString.Length, "Formatting guid should have succeeded."); + + return guidString; + } + + // Returns whether the guid is successfully formatted as a span. + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default) + { + if (format.Length == 0) + format = "D"; + + // all acceptable format strings are of length 1 + if (format.Length != 1) + throw new FormatException(SR.Format_InvalidGuidFormatSpecification); + + bool dash = true; + bool hex = false; + int braces = 0; + + int guidSize; + + switch (format[0]) + { + case 'D': + case 'd': + guidSize = 36; + break; + case 'N': + case 'n': + dash = false; + guidSize = 32; + break; + case 'B': + case 'b': + braces = '{' + ('}' << 16); + guidSize = 38; + break; + case 'P': + case 'p': + braces = '(' + (')' << 16); + guidSize = 38; + break; + case 'X': + case 'x': + braces = '{' + ('}' << 16); + dash = false; + hex = true; + guidSize = 68; + break; + default: + throw new FormatException(SR.Format_InvalidGuidFormatSpecification); + } + + if (destination.Length < guidSize) + { + charsWritten = 0; + return false; + } + + unsafe + { + fixed (char* guidChars = &MemoryMarshal.GetReference(destination)) + { + char * p = guidChars; + + if (braces != 0) + *p++ = (char)braces; + + if (hex) + { + // {0xdddddddd,0xdddd,0xdddd,{0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd}} + *p++ = '0'; + *p++ = 'x'; + p += HexsToChars(p, _a >> 24, _a >> 16); + p += HexsToChars(p, _a >> 8, _a); + *p++ = ','; + *p++ = '0'; + *p++ = 'x'; + p += HexsToChars(p, _b >> 8, _b); + *p++ = ','; + *p++ = '0'; + *p++ = 'x'; + p += HexsToChars(p, _c >> 8, _c); + *p++ = ','; + *p++ = '{'; + p += HexsToCharsHexOutput(p, _d, _e); + *p++ = ','; + p += HexsToCharsHexOutput(p, _f, _g); + *p++ = ','; + p += HexsToCharsHexOutput(p, _h, _i); + *p++ = ','; + p += HexsToCharsHexOutput(p, _j, _k); + *p++ = '}'; + } + else + { + // [{|(]dddddddd[-]dddd[-]dddd[-]dddd[-]dddddddddddd[}|)] + p += HexsToChars(p, _a >> 24, _a >> 16); + p += HexsToChars(p, _a >> 8, _a); + if (dash) + *p++ = '-'; + p += HexsToChars(p, _b >> 8, _b); + if (dash) + *p++ = '-'; + p += HexsToChars(p, _c >> 8, _c); + if (dash) + *p++ = '-'; + p += HexsToChars(p, _d, _e); + if (dash) + *p++ = '-'; + p += HexsToChars(p, _f, _g); + p += HexsToChars(p, _h, _i); + p += HexsToChars(p, _j, _k); + } + + if (braces != 0) + *p++ = (char)(braces >> 16); + + Debug.Assert(p - guidChars == guidSize); + } + } + + charsWritten = guidSize; + return true; + } + + bool ISpanFormattable.TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider provider) + { + // Like with the IFormattable implementation, provider is ignored. + return TryFormat(destination, out charsWritten, format); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/HResults.cs b/external/corert/src/System.Private.CoreLib/shared/System/HResults.cs new file mode 100644 index 0000000000..ffc47d85bf --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/HResults.cs @@ -0,0 +1,126 @@ +// 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. + +//============================================================================= +// +// +// Purpose: Define HResult constants. Every exception has one of these. +// +// +//===========================================================================*/ +// Note: FACILITY_URT is defined as 0x13 (0x8013xxxx). Within that +// range, 0x1yyy is for Runtime errors (used for Security, Metadata, etc). +// In that subrange, 0x15zz and 0x16zz have been allocated for classlib-type +// HResults. Also note that some of our HResults have to map to certain +// COM HR's, etc. + +// Reflection will use 0x1600 -> 0x161f. IO will use 0x1620 -> 0x163f. +// Security will use 0x1640 -> 0x165f + + +using System; + +namespace System +{ + internal static partial class HResults + { + internal const int COR_E_ABANDONEDMUTEX = unchecked((int)0x8013152D); + internal const int COR_E_AMBIGUOUSMATCH = unchecked((int)0x8000211D); + internal const int COR_E_APPDOMAINUNLOADED = unchecked((int)0x80131014); + internal const int COR_E_APPLICATION = unchecked((int)0x80131600); + internal const int COR_E_ARGUMENT = unchecked((int)0x80070057); + internal const int COR_E_ARGUMENTOUTOFRANGE = unchecked((int)0x80131502); + internal const int COR_E_ARITHMETIC = unchecked((int)0x80070216); + internal const int COR_E_ARRAYTYPEMISMATCH = unchecked((int)0x80131503); + internal const int COR_E_BADEXEFORMAT = unchecked((int)0x800700C1); + internal const int COR_E_BADIMAGEFORMAT = unchecked((int)0x8007000B); + internal const int COR_E_CANNOTUNLOADAPPDOMAIN = unchecked((int)0x80131015); + internal const int COR_E_COMEMULATE = unchecked((int)0x80131535); + internal const int COR_E_CONTEXTMARSHAL = unchecked((int)0x80131504); + internal const int COR_E_CUSTOMATTRIBUTEFORMAT = unchecked((int)0x80131605); + internal const int COR_E_DATAMISALIGNED = unchecked((int)0x80131541); + internal const int COR_E_DIRECTORYNOTFOUND = unchecked((int)0x80070003); + internal const int COR_E_DIVIDEBYZERO = unchecked((int)0x80020012); // DISP_E_DIVBYZERO + internal const int COR_E_DLLNOTFOUND = unchecked((int)0x80131524); + internal const int COR_E_DUPLICATEWAITOBJECT = unchecked((int)0x80131529); + internal const int COR_E_ENDOFSTREAM = unchecked((int)0x80070026); // OS defined + internal const int COR_E_ENTRYPOINTNOTFOUND = unchecked((int)0x80131523); + internal const int COR_E_EXCEPTION = unchecked((int)0x80131500); + internal const int COR_E_EXECUTIONENGINE = unchecked((int)0x80131506); + internal const int COR_E_FIELDACCESS = unchecked((int)0x80131507); + internal const int COR_E_FILELOAD = unchecked((int)0x80131621); + internal const int COR_E_FILENOTFOUND = unchecked((int)0x80070002); + internal const int COR_E_FORMAT = unchecked((int)0x80131537); + internal const int COR_E_HOSTPROTECTION = unchecked((int)0x80131640); + internal const int COR_E_INDEXOUTOFRANGE = unchecked((int)0x80131508); + internal const int COR_E_INSUFFICIENTEXECUTIONSTACK = unchecked((int)0x80131578); + internal const int COR_E_INSUFFICIENTMEMORY = unchecked((int)0x8013153D); + internal const int COR_E_INVALIDCAST = unchecked((int)0x80004002); + internal const int COR_E_INVALIDCOMOBJECT = unchecked((int)0x80131527); + internal const int COR_E_INVALIDFILTERCRITERIA = unchecked((int)0x80131601); + internal const int COR_E_INVALIDOLEVARIANTTYPE = unchecked((int)0x80131531); + internal const int COR_E_INVALIDOPERATION = unchecked((int)0x80131509); + internal const int COR_E_INVALIDPROGRAM = unchecked((int)0x8013153A); + internal const int COR_E_IO = unchecked((int)0x80131620); + internal const int COR_E_KEYNOTFOUND = unchecked((int)0x80131577); + internal const int COR_E_MARSHALDIRECTIVE = unchecked((int)0x80131535); + internal const int COR_E_MEMBERACCESS = unchecked((int)0x8013151A); + internal const int COR_E_METHODACCESS = unchecked((int)0x80131510); + internal const int COR_E_MISSINGFIELD = unchecked((int)0x80131511); + internal const int COR_E_MISSINGMANIFESTRESOURCE = unchecked((int)0x80131532); + internal const int COR_E_MISSINGMEMBER = unchecked((int)0x80131512); + internal const int COR_E_MISSINGMETHOD = unchecked((int)0x80131513); + internal const int COR_E_MISSINGSATELLITEASSEMBLY = unchecked((int)0x80131536); + internal const int COR_E_MULTICASTNOTSUPPORTED = unchecked((int)0x80131514); + internal const int COR_E_NOTFINITENUMBER = unchecked((int)0x80131528); + internal const int COR_E_NOTSUPPORTED = unchecked((int)0x80131515); + internal const int COR_E_NULLREFERENCE = unchecked((int)0x80004003); + internal const int COR_E_OBJECTDISPOSED = unchecked((int)0x80131622); + internal const int COR_E_OPERATIONCANCELED = unchecked((int)0x8013153B); + internal const int COR_E_OUTOFMEMORY = unchecked((int)0x8007000E); + internal const int COR_E_OVERFLOW = unchecked((int)0x80131516); + internal const int COR_E_PATHTOOLONG = unchecked((int)0x800700CE); + internal const int COR_E_PLATFORMNOTSUPPORTED = unchecked((int)0x80131539); + internal const int COR_E_RANK = unchecked((int)0x80131517); + internal const int COR_E_REFLECTIONTYPELOAD = unchecked((int)0x80131602); + internal const int COR_E_RUNTIMEWRAPPED = unchecked((int)0x8013153E); + internal const int COR_E_SAFEARRAYRANKMISMATCH = unchecked((int)0x80131538); + internal const int COR_E_SAFEARRAYTYPEMISMATCH = unchecked((int)0x80131533); + internal const int COR_E_SAFEHANDLEMISSINGATTRIBUTE = unchecked((int)0x80131623); + internal const int COR_E_SECURITY = unchecked((int)0x8013150A); + internal const int COR_E_SEMAPHOREFULL = unchecked((int)0x8013152B); + internal const int COR_E_SERIALIZATION = unchecked((int)0x8013150C); + internal const int COR_E_STACKOVERFLOW = unchecked((int)0x800703E9); + internal const int COR_E_SYNCHRONIZATIONLOCK = unchecked((int)0x80131518); + internal const int COR_E_SYSTEM = unchecked((int)0x80131501); + internal const int COR_E_TARGET = unchecked((int)0x80131603); + internal const int COR_E_TARGETINVOCATION = unchecked((int)0x80131604); + internal const int COR_E_TARGETPARAMCOUNT = unchecked((int)0x8002000E); + internal const int COR_E_THREADABORTED = unchecked((int)0x80131530); + internal const int COR_E_THREADINTERRUPTED = unchecked((int)0x80131519); + internal const int COR_E_THREADSTART = unchecked((int)0x80131525); + internal const int COR_E_THREADSTATE = unchecked((int)0x80131520); + internal const int COR_E_THREADSTOP = unchecked((int)0x80131521); + internal const int COR_E_TIMEOUT = unchecked((int)0x80131505); + internal const int COR_E_TYPEACCESS = unchecked((int)0x80131543); + internal const int COR_E_TYPEINITIALIZATION = unchecked((int)0x80131534); + internal const int COR_E_TYPELOAD = unchecked((int)0x80131522); + internal const int COR_E_TYPEUNLOADED = unchecked((int)0x80131013); + internal const int COR_E_UNAUTHORIZEDACCESS = unchecked((int)0x80070005); + internal const int COR_E_UNSUPPORTEDFORMAT = unchecked((int)0x80131523); + internal const int COR_E_VERIFICATION = unchecked((int)0x8013150D); + internal const int COR_E_WAITHANDLECANNOTBEOPENED = unchecked((int)0x8013152C); + internal const int DISP_E_OVERFLOW = unchecked((int)0x8002000A); + internal const int E_BOUNDS = unchecked((int)0x8000000B); + internal const int E_CHANGED_STATE = unchecked((int)0x8000000C); + internal const int E_FAIL = unchecked((int)0x80004005); + internal const int E_HANDLE = unchecked((int)0x80070006); + internal const int E_INVALIDARG = unchecked((int)0x80070057); + internal const int E_NOTIMPL = unchecked((int)0x80004001); + internal const int E_POINTER = unchecked((int)0x80004003); + internal const int ERROR_MRM_MAP_NOT_FOUND = unchecked((int)0x80073B1F); + internal const int RO_E_CLOSED = unchecked((int)0x80000013); + internal const int TYPE_E_TYPEMISMATCH = unchecked((int)0x80028CA0); + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/HashCode.cs b/external/corert/src/System.Private.CoreLib/shared/System/HashCode.cs new file mode 100644 index 0000000000..4be57be02a --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/HashCode.cs @@ -0,0 +1,430 @@ +// 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. + +/* + +The xxHash32 implementation is based on the code published by Yann Collet: +https://raw.githubusercontent.com/Cyan4973/xxHash/5c174cfa4e45a42f94082dc0d4539b39696afea1/xxhash.c + + xxHash - Fast Hash algorithm + Copyright (C) 2012-2016, Yann Collet + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - xxHash homepage: http://www.xxhash.com + - xxHash source repository : https://github.com/Cyan4973/xxHash + +*/ + +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace System +{ + // xxHash32 is used for the hash code. + // https://github.com/Cyan4973/xxHash + + public struct HashCode + { + private static readonly uint s_seed = GenerateGlobalSeed(); + + private const uint Prime1 = 2654435761U; + private const uint Prime2 = 2246822519U; + private const uint Prime3 = 3266489917U; + private const uint Prime4 = 668265263U; + private const uint Prime5 = 374761393U; + + private uint _v1, _v2, _v3, _v4; + private uint _queue1, _queue2, _queue3; + private uint _length; + + private static unsafe uint GenerateGlobalSeed() + { + uint result; + Interop.GetRandomBytes((byte*)&result, sizeof(uint)); + return result; + } + + public static int Combine(T1 value1) + { + // Provide a way of diffusing bits from something with a limited + // input hash space. For example, many enums only have a few + // possible hashes, only using the bottom few bits of the code. Some + // collections are built on the assumption that hashes are spread + // over a larger space, so diffusing the bits may help the + // collection work more efficiently. + + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + + uint hash = MixEmptyState(); + hash += 4; + + hash = QueueRound(hash, hc1); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + + uint hash = MixEmptyState(); + hash += 8; + + hash = QueueRound(hash, hc1); + hash = QueueRound(hash, hc2); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + + uint hash = MixEmptyState(); + hash += 12; + + hash = QueueRound(hash, hc1); + hash = QueueRound(hash, hc2); + hash = QueueRound(hash, hc3); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + var hc4 = (uint)(value4?.GetHashCode() ?? 0); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + uint hash = MixState(v1, v2, v3, v4); + hash += 16; + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + var hc4 = (uint)(value4?.GetHashCode() ?? 0); + var hc5 = (uint)(value5?.GetHashCode() ?? 0); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + uint hash = MixState(v1, v2, v3, v4); + hash += 20; + + hash = QueueRound(hash, hc5); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + var hc4 = (uint)(value4?.GetHashCode() ?? 0); + var hc5 = (uint)(value5?.GetHashCode() ?? 0); + var hc6 = (uint)(value6?.GetHashCode() ?? 0); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + uint hash = MixState(v1, v2, v3, v4); + hash += 24; + + hash = QueueRound(hash, hc5); + hash = QueueRound(hash, hc6); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + var hc4 = (uint)(value4?.GetHashCode() ?? 0); + var hc5 = (uint)(value5?.GetHashCode() ?? 0); + var hc6 = (uint)(value6?.GetHashCode() ?? 0); + var hc7 = (uint)(value7?.GetHashCode() ?? 0); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + uint hash = MixState(v1, v2, v3, v4); + hash += 28; + + hash = QueueRound(hash, hc5); + hash = QueueRound(hash, hc6); + hash = QueueRound(hash, hc7); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + var hc4 = (uint)(value4?.GetHashCode() ?? 0); + var hc5 = (uint)(value5?.GetHashCode() ?? 0); + var hc6 = (uint)(value6?.GetHashCode() ?? 0); + var hc7 = (uint)(value7?.GetHashCode() ?? 0); + var hc8 = (uint)(value8?.GetHashCode() ?? 0); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + v1 = Round(v1, hc5); + v2 = Round(v2, hc6); + v3 = Round(v3, hc7); + v4 = Round(v4, hc8); + + uint hash = MixState(v1, v2, v3, v4); + hash += 32; + + hash = MixFinal(hash); + return (int)hash; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Rol(uint value, int count) + => (value << count) | (value >> (32 - count)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Initialize(out uint v1, out uint v2, out uint v3, out uint v4) + { + v1 = s_seed + Prime1 + Prime2; + v2 = s_seed + Prime2; + v3 = s_seed; + v4 = s_seed - Prime1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Round(uint hash, uint input) + { + hash += input * Prime2; + hash = Rol(hash, 13); + hash *= Prime1; + return hash; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint QueueRound(uint hash, uint queuedValue) + { + hash += queuedValue * Prime3; + return Rol(hash, 17) * Prime4; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint MixState(uint v1, uint v2, uint v3, uint v4) + { + return Rol(v1, 1) + Rol(v2, 7) + Rol(v3, 12) + Rol(v4, 18); + } + + private static uint MixEmptyState() + { + return s_seed + Prime5; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint MixFinal(uint hash) + { + hash ^= hash >> 15; + hash *= Prime2; + hash ^= hash >> 13; + hash *= Prime3; + hash ^= hash >> 16; + return hash; + } + + public void Add(T value) + { + Add(value?.GetHashCode() ?? 0); + } + + public void Add(T value, IEqualityComparer comparer) + { + Add(comparer != null ? comparer.GetHashCode(value) : (value?.GetHashCode() ?? 0)); + } + + private void Add(int value) + { + // The original xxHash works as follows: + // 0. Initialize immediately. We can't do this in a struct (no + // default ctor). + // 1. Accumulate blocks of length 16 (4 uints) into 4 accumulators. + // 2. Accumulate remaining blocks of length 4 (1 uint) into the + // hash. + // 3. Accumulate remaining blocks of length 1 into the hash. + + // There is no need for #3 as this type only accepts ints. _queue1, + // _queue2 and _queue3 are basically a buffer so that when + // ToHashCode is called we can execute #2 correctly. + + // We need to initialize the xxHash32 state (_v1 to _v4) lazily (see + // #0) nd the last place that can be done if you look at the + // original code is just before the first block of 16 bytes is mixed + // in. The xxHash32 state is never used for streams containing fewer + // than 16 bytes. + + // To see what's really going on here, have a look at the Combine + // methods. + + var val = (uint)value; + + // Storing the value of _length locally shaves of quite a few bytes + // in the resulting machine code. + uint previousLength = _length++; + uint position = previousLength % 4; + + // Switch can't be inlined. + + if (position == 0) + _queue1 = val; + else if (position == 1) + _queue2 = val; + else if (position == 2) + _queue3 = val; + else // position == 3 + { + if (previousLength == 3) + Initialize(out _v1, out _v2, out _v3, out _v4); + + _v1 = Round(_v1, _queue1); + _v2 = Round(_v2, _queue2); + _v3 = Round(_v3, _queue3); + _v4 = Round(_v4, val); + } + } + + public int ToHashCode() + { + // Storing the value of _length locally shaves of quite a few bytes + // in the resulting machine code. + uint length = _length; + + // position refers to the *next* queue position in this method, so + // position == 1 means that _queue1 is populated; _queue2 would have + // been populated on the next call to Add. + uint position = length % 4; + + // If the length is less than 4, _v1 to _v4 don't contain anything + // yet. xxHash32 treats this differently. + + uint hash = length < 4 ? MixEmptyState() : MixState(_v1, _v2, _v3, _v4); + + // _length is incremented once per Add(Int32) and is therefore 4 + // times too small (xxHash length is in bytes, not ints). + + hash += length * 4; + + // Mix what remains in the queue + + // Switch can't be inlined right now, so use as few branches as + // possible by manually excluding impossible scenarios (position > 1 + // is always false if position is not > 0). + if (position > 0) + { + hash = QueueRound(hash, _queue1); + if (position > 1) + { + hash = QueueRound(hash, _queue2); + if (position > 2) + hash = QueueRound(hash, _queue3); + } + } + + hash = MixFinal(hash); + return (int)hash; + } + +#pragma warning disable 0809 + // Obsolete member 'memberA' overrides non-obsolete member 'memberB'. + // Disallowing GetHashCode and Equals is by design + + // * We decided to not override GetHashCode() to produce the hash code + // as this would be weird, both naming-wise as well as from a + // behavioral standpoint (GetHashCode() should return the object's + // hash code, not the one being computed). + + // * Even though ToHashCode() can be called safely multiple times on + // this implementation, it is not part of the contract. If the + // implementation has to change in the future we don't want to worry + // about people who might have incorrectly used this type. + + [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code.", error: true)] + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => throw new NotSupportedException(SR.HashCode_HashCodeNotSupported); + + [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes.", error: true)] + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => throw new NotSupportedException(SR.HashCode_EqualityNotSupported); +#pragma warning restore 0809 + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IFormattable.cs b/external/corert/src/System.Private.CoreLib/shared/System/IFormattable.cs index 28a7d70571..1f2f7022cc 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IFormattable.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IFormattable.cs @@ -3,13 +3,11 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics.Contracts; namespace System { public interface IFormattable { - [Pure] String ToString(String format, IFormatProvider formatProvider); } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/BinaryWriter.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/BinaryWriter.cs new file mode 100644 index 0000000000..ad1d31f577 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/BinaryWriter.cs @@ -0,0 +1,441 @@ +// 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.Text; +using System.Diagnostics; +using System.Buffers; + +namespace System.IO +{ + // This abstract base class represents a writer that can write + // primitives to an arbitrary stream. A subclass can override methods to + // give unique encodings. + // + public class BinaryWriter : IDisposable + { + public static readonly BinaryWriter Null = new BinaryWriter(); + + protected Stream OutStream; + private byte[] _buffer; // temp space for writing primitives to. + private Encoding _encoding; + private Encoder _encoder; + + private bool _leaveOpen; + + // Perf optimization stuff + private byte[] _largeByteBuffer; // temp space for writing chars. + private int _maxChars; // max # of chars we can put in _largeByteBuffer + // Size should be around the max number of chars/string * Encoding's max bytes/char + private const int LargeByteBufferSize = 256; + + // Protected default constructor that sets the output stream + // to a null stream (a bit bucket). + protected BinaryWriter() + { + OutStream = Stream.Null; + _buffer = new byte[16]; + _encoding = EncodingCache.UTF8NoBOM; + _encoder = _encoding.GetEncoder(); + } + + public BinaryWriter(Stream output) : this(output, EncodingCache.UTF8NoBOM, false) + { + } + + public BinaryWriter(Stream output, Encoding encoding) : this(output, encoding, false) + { + } + + public BinaryWriter(Stream output, Encoding encoding, bool leaveOpen) + { + if (output == null) + throw new ArgumentNullException(nameof(output)); + if (encoding == null) + throw new ArgumentNullException(nameof(encoding)); + if (!output.CanWrite) + throw new ArgumentException(SR.Argument_StreamNotWritable); + + OutStream = output; + _buffer = new byte[16]; + _encoding = encoding; + _encoder = _encoding.GetEncoder(); + _leaveOpen = leaveOpen; + } + + // Closes this writer and releases any system resources associated with the + // writer. Following a call to Close, any operations on the writer + // may raise exceptions. + public virtual void Close() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (_leaveOpen) + OutStream.Flush(); + else + OutStream.Close(); + } + } + + public void Dispose() + { + Dispose(true); + } + + // Returns the stream associated with the writer. It flushes all pending + // writes before returning. All subclasses should override Flush to + // ensure that all buffered data is sent to the stream. + public virtual Stream BaseStream + { + get + { + Flush(); + return OutStream; + } + } + + // Clears all buffers for this writer and causes any buffered data to be + // written to the underlying device. + public virtual void Flush() + { + OutStream.Flush(); + } + + public virtual long Seek(int offset, SeekOrigin origin) + { + return OutStream.Seek(offset, origin); + } + + // Writes a boolean to this stream. A single byte is written to the stream + // with the value 0 representing false or the value 1 representing true. + // + public virtual void Write(bool value) + { + _buffer[0] = (byte)(value ? 1 : 0); + OutStream.Write(_buffer, 0, 1); + } + + // Writes a byte to this stream. The current position of the stream is + // advanced by one. + // + public virtual void Write(byte value) + { + OutStream.WriteByte(value); + } + + // Writes a signed byte to this stream. The current position of the stream + // is advanced by one. + // + [CLSCompliant(false)] + public virtual void Write(sbyte value) + { + OutStream.WriteByte((byte)value); + } + + // Writes a byte array to this stream. + // + // This default implementation calls the Write(Object, int, int) + // method to write the byte array. + // + public virtual void Write(byte[] buffer) + { + if (buffer == null) + throw new ArgumentNullException(nameof(buffer)); + OutStream.Write(buffer, 0, buffer.Length); + } + + // Writes a section of a byte array to this stream. + // + // This default implementation calls the Write(Object, int, int) + // method to write the byte array. + // + public virtual void Write(byte[] buffer, int index, int count) + { + OutStream.Write(buffer, index, count); + } + + + // Writes a character to this stream. The current position of the stream is + // advanced by two. + // Note this method cannot handle surrogates properly in UTF-8. + // + public unsafe virtual void Write(char ch) + { + if (Char.IsSurrogate(ch)) + throw new ArgumentException(SR.Arg_SurrogatesNotAllowedAsSingleChar); + + Debug.Assert(_encoding.GetMaxByteCount(1) <= 16, "_encoding.GetMaxByteCount(1) <= 16)"); + int numBytes = 0; + fixed (byte* pBytes = &_buffer[0]) + { + numBytes = _encoder.GetBytes(&ch, 1, pBytes, _buffer.Length, flush: true); + } + OutStream.Write(_buffer, 0, numBytes); + } + + // Writes a character array to this stream. + // + // This default implementation calls the Write(Object, int, int) + // method to write the character array. + // + public virtual void Write(char[] chars) + { + if (chars == null) + throw new ArgumentNullException(nameof(chars)); + + byte[] bytes = _encoding.GetBytes(chars, 0, chars.Length); + OutStream.Write(bytes, 0, bytes.Length); + } + + // Writes a section of a character array to this stream. + // + // This default implementation calls the Write(Object, int, int) + // method to write the character array. + // + public virtual void Write(char[] chars, int index, int count) + { + byte[] bytes = _encoding.GetBytes(chars, index, count); + OutStream.Write(bytes, 0, bytes.Length); + } + + + // Writes a double to this stream. The current position of the stream is + // advanced by eight. + // + public unsafe virtual void Write(double value) + { + ulong TmpValue = *(ulong*)&value; + _buffer[0] = (byte)TmpValue; + _buffer[1] = (byte)(TmpValue >> 8); + _buffer[2] = (byte)(TmpValue >> 16); + _buffer[3] = (byte)(TmpValue >> 24); + _buffer[4] = (byte)(TmpValue >> 32); + _buffer[5] = (byte)(TmpValue >> 40); + _buffer[6] = (byte)(TmpValue >> 48); + _buffer[7] = (byte)(TmpValue >> 56); + OutStream.Write(_buffer, 0, 8); + } + + public virtual void Write(decimal value) + { + Decimal.GetBytes(value, _buffer); + OutStream.Write(_buffer, 0, 16); + } + + // Writes a two-byte signed integer to this stream. The current position of + // the stream is advanced by two. + // + public virtual void Write(short value) + { + _buffer[0] = (byte)value; + _buffer[1] = (byte)(value >> 8); + OutStream.Write(_buffer, 0, 2); + } + + // Writes a two-byte unsigned integer to this stream. The current position + // of the stream is advanced by two. + // + [CLSCompliant(false)] + public virtual void Write(ushort value) + { + _buffer[0] = (byte)value; + _buffer[1] = (byte)(value >> 8); + OutStream.Write(_buffer, 0, 2); + } + + // Writes a four-byte signed integer to this stream. The current position + // of the stream is advanced by four. + // + public virtual void Write(int value) + { + _buffer[0] = (byte)value; + _buffer[1] = (byte)(value >> 8); + _buffer[2] = (byte)(value >> 16); + _buffer[3] = (byte)(value >> 24); + OutStream.Write(_buffer, 0, 4); + } + + // Writes a four-byte unsigned integer to this stream. The current position + // of the stream is advanced by four. + // + [CLSCompliant(false)] + public virtual void Write(uint value) + { + _buffer[0] = (byte)value; + _buffer[1] = (byte)(value >> 8); + _buffer[2] = (byte)(value >> 16); + _buffer[3] = (byte)(value >> 24); + OutStream.Write(_buffer, 0, 4); + } + + // Writes an eight-byte signed integer to this stream. The current position + // of the stream is advanced by eight. + // + public virtual void Write(long value) + { + _buffer[0] = (byte)value; + _buffer[1] = (byte)(value >> 8); + _buffer[2] = (byte)(value >> 16); + _buffer[3] = (byte)(value >> 24); + _buffer[4] = (byte)(value >> 32); + _buffer[5] = (byte)(value >> 40); + _buffer[6] = (byte)(value >> 48); + _buffer[7] = (byte)(value >> 56); + OutStream.Write(_buffer, 0, 8); + } + + // Writes an eight-byte unsigned integer to this stream. The current + // position of the stream is advanced by eight. + // + [CLSCompliant(false)] + public virtual void Write(ulong value) + { + _buffer[0] = (byte)value; + _buffer[1] = (byte)(value >> 8); + _buffer[2] = (byte)(value >> 16); + _buffer[3] = (byte)(value >> 24); + _buffer[4] = (byte)(value >> 32); + _buffer[5] = (byte)(value >> 40); + _buffer[6] = (byte)(value >> 48); + _buffer[7] = (byte)(value >> 56); + OutStream.Write(_buffer, 0, 8); + } + + // Writes a float to this stream. The current position of the stream is + // advanced by four. + // + public unsafe virtual void Write(float value) + { + uint TmpValue = *(uint*)&value; + _buffer[0] = (byte)TmpValue; + _buffer[1] = (byte)(TmpValue >> 8); + _buffer[2] = (byte)(TmpValue >> 16); + _buffer[3] = (byte)(TmpValue >> 24); + OutStream.Write(_buffer, 0, 4); + } + + + // Writes a length-prefixed string to this stream in the BinaryWriter's + // current Encoding. This method first writes the length of the string as + // a four-byte unsigned integer, and then writes that many characters + // to the stream. + // + public unsafe virtual void Write(String value) + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + int len = _encoding.GetByteCount(value); + Write7BitEncodedInt(len); + + if (_largeByteBuffer == null) + { + _largeByteBuffer = new byte[LargeByteBufferSize]; + _maxChars = _largeByteBuffer.Length / _encoding.GetMaxByteCount(1); + } + + if (len <= _largeByteBuffer.Length) + { + //Debug.Assert(len == _encoding.GetBytes(chars, 0, chars.Length, _largeByteBuffer, 0), "encoding's GetByteCount & GetBytes gave different answers! encoding type: "+_encoding.GetType().Name); + _encoding.GetBytes(value, 0, value.Length, _largeByteBuffer, 0); + OutStream.Write(_largeByteBuffer, 0, len); + } + else + { + // Aggressively try to not allocate memory in this loop for + // runtime performance reasons. Use an Encoder to write out + // the string correctly (handling surrogates crossing buffer + // boundaries properly). + int charStart = 0; + int numLeft = value.Length; +#if DEBUG + int totalBytes = 0; +#endif + while (numLeft > 0) + { + // Figure out how many chars to process this round. + int charCount = (numLeft > _maxChars) ? _maxChars : numLeft; + int byteLen; + + checked + { + if (charStart < 0 || charCount < 0 || charStart > value.Length - charCount) + { + throw new ArgumentOutOfRangeException(nameof(charCount)); + } + fixed (char* pChars = value) + { + fixed (byte* pBytes = &_largeByteBuffer[0]) + { + byteLen = _encoder.GetBytes(pChars + charStart, charCount, pBytes, _largeByteBuffer.Length, charCount == numLeft); + } + } + } +#if DEBUG + totalBytes += byteLen; + Debug.Assert(totalBytes <= len && byteLen <= _largeByteBuffer.Length, "BinaryWriter::Write(String) - More bytes encoded than expected!"); +#endif + OutStream.Write(_largeByteBuffer, 0, byteLen); + charStart += charCount; + numLeft -= charCount; + } +#if DEBUG + Debug.Assert(totalBytes == len, "BinaryWriter::Write(String) - Didn't write out all the bytes!"); +#endif + } + } + + public virtual void Write(ReadOnlySpan buffer) + { + if (GetType() == typeof(BinaryWriter)) + { + OutStream.Write(buffer); + } + else + { + byte[] array = ArrayPool.Shared.Rent(buffer.Length); + try + { + buffer.CopyTo(array); + Write(array, 0, buffer.Length); + } + finally + { + ArrayPool.Shared.Return(array); + } + } + } + + public virtual void Write(ReadOnlySpan chars) + { + byte[] bytes = ArrayPool.Shared.Rent(_encoding.GetMaxByteCount(chars.Length)); + try + { + int bytesWritten = _encoding.GetBytes(chars, bytes); + Write(bytes, 0, bytesWritten); + } + finally + { + ArrayPool.Shared.Return(bytes); + } + } + + protected void Write7BitEncodedInt(int value) + { + // Write out an int 7 bits at a time. The high bit of the byte, + // when on, tells reader to continue reading more bytes. + uint v = (uint)value; // support negative numbers + while (v >= 0x80) + { + Write((byte)(v | 0x80)); + v >>= 7; + } + Write((byte)v); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/DirectoryNotFoundException.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/DirectoryNotFoundException.cs index de09760fcb..d7c6eacb9a 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/DirectoryNotFoundException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/DirectoryNotFoundException.cs @@ -12,30 +12,31 @@ namespace System.IO * the Win32 errorcode-as-HRESULT ERROR_PATH_NOT_FOUND (0x80070003) * and STG_E_PATHNOTFOUND (0x80030003). */ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class DirectoryNotFoundException : IOException { public DirectoryNotFoundException() : base(SR.Arg_DirectoryNotFoundException) { - HResult = __HResults.COR_E_DIRECTORYNOTFOUND; + HResult = HResults.COR_E_DIRECTORYNOTFOUND; } public DirectoryNotFoundException(string message) : base(message) { - HResult = __HResults.COR_E_DIRECTORYNOTFOUND; + HResult = HResults.COR_E_DIRECTORYNOTFOUND; } public DirectoryNotFoundException(string message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_DIRECTORYNOTFOUND; + HResult = HResults.COR_E_DIRECTORYNOTFOUND; } protected DirectoryNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/DisableMediaInsertionPrompt.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/DisableMediaInsertionPrompt.cs new file mode 100644 index 0000000000..aa10e8d883 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/DisableMediaInsertionPrompt.cs @@ -0,0 +1,36 @@ +// 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. + +namespace System.IO +{ + /// + /// Simple wrapper to safely disable the normal media insertion prompt for + /// removable media (floppies, cds, memory cards, etc.) + /// + /// + /// Note that removable media file systems lazily load. After starting the OS + /// they won't be loaded until you have media in the drive- and as such the + /// prompt won't happen. You have to have had media in at least once to get + /// the file system to load and then have removed it. + /// + internal struct DisableMediaInsertionPrompt : IDisposable + { + private bool _disableSuccess; + private uint _oldMode; + + public static DisableMediaInsertionPrompt Create() + { + DisableMediaInsertionPrompt prompt = new DisableMediaInsertionPrompt(); + prompt._disableSuccess = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS, out prompt._oldMode); + return prompt; + } + + public void Dispose() + { + uint ignore; + if (_disableSuccess) + Interop.Kernel32.SetThreadErrorMode(_oldMode, out ignore); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/DriveNotFoundException.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/DriveNotFoundException.cs new file mode 100644 index 0000000000..3f2c88c74b --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/DriveNotFoundException.cs @@ -0,0 +1,36 @@ +// 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.Runtime.Serialization; + +namespace System.IO +{ + //Thrown when trying to access a drive that is not available. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + internal class DriveNotFoundException : IOException + { + public DriveNotFoundException() + : base(SR.Arg_DriveNotFoundException) + { + HResult = HResults.COR_E_DIRECTORYNOTFOUND; + } + + public DriveNotFoundException(string message) + : base(message) + { + HResult = HResults.COR_E_DIRECTORYNOTFOUND; + } + + public DriveNotFoundException(string message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_DIRECTORYNOTFOUND; + } + + protected DriveNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/EndOfStreamException.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/EndOfStreamException.cs index 68e1b2c882..606073ad9a 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/EndOfStreamException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/EndOfStreamException.cs @@ -6,30 +6,31 @@ using System.Runtime.Serialization; namespace System.IO { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class EndOfStreamException : IOException { public EndOfStreamException() : base(SR.Arg_EndOfStreamException) { - HResult = __HResults.COR_E_ENDOFSTREAM; + HResult = HResults.COR_E_ENDOFSTREAM; } public EndOfStreamException(string message) : base(message) { - HResult = __HResults.COR_E_ENDOFSTREAM; + HResult = HResults.COR_E_ENDOFSTREAM; } public EndOfStreamException(string message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_ENDOFSTREAM; + HResult = HResults.COR_E_ENDOFSTREAM; } protected EndOfStreamException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/Error.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/Error.cs index 2aef895181..1e319a0680 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/Error.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/Error.cs @@ -6,14 +6,12 @@ using System; using System.Runtime.InteropServices; using System.Text; using System.Globalization; -using System.Diagnostics.Contracts; namespace System.IO { /// /// Provides centralized methods for creating exceptions for System.IO.FileSystem. /// - [Pure] internal static class Error { internal static Exception GetStreamIsClosed() diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/FileLoadException.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/FileLoadException.cs index 6fdf2d58cf..8c31244e1d 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/FileLoadException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/FileLoadException.cs @@ -6,36 +6,38 @@ using System.Runtime.Serialization; namespace System.IO { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public partial class FileLoadException : IOException { public FileLoadException() : base(SR.IO_FileLoad) { - HResult = __HResults.COR_E_FILELOAD; + HResult = HResults.COR_E_FILELOAD; } public FileLoadException(string message) : base(message) { - HResult = __HResults.COR_E_FILELOAD; + HResult = HResults.COR_E_FILELOAD; } public FileLoadException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_FILELOAD; + HResult = HResults.COR_E_FILELOAD; } public FileLoadException(string message, string fileName) : base(message) { - HResult = __HResults.COR_E_FILELOAD; + HResult = HResults.COR_E_FILELOAD; FileName = fileName; } public FileLoadException(string message, string fileName, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_FILELOAD; + HResult = HResults.COR_E_FILELOAD; FileName = fileName; } @@ -82,12 +84,15 @@ namespace System.IO protected FileLoadException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); + FileName = info.GetString("FileLoad_FileName"); + FusionLog = info.GetString("FileLoad_FusionLog"); } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + info.AddValue("FileLoad_FileName", FileName, typeof(string)); + info.AddValue("FileLoad_FusionLog", FusionLog, typeof(string)); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/FileNotFoundException.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/FileNotFoundException.cs index 374c976055..72cff4cfc0 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/FileNotFoundException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/FileNotFoundException.cs @@ -7,37 +7,39 @@ using System.Runtime.Serialization; namespace System.IO { // Thrown when trying to access a file that doesn't exist on disk. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public partial class FileNotFoundException : IOException { public FileNotFoundException() : base(SR.IO_FileNotFound) { - HResult = __HResults.COR_E_FILENOTFOUND; + HResult = HResults.COR_E_FILENOTFOUND; } public FileNotFoundException(string message) : base(message) { - HResult = __HResults.COR_E_FILENOTFOUND; + HResult = HResults.COR_E_FILENOTFOUND; } public FileNotFoundException(string message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_FILENOTFOUND; + HResult = HResults.COR_E_FILENOTFOUND; } public FileNotFoundException(string message, string fileName) : base(message) { - HResult = __HResults.COR_E_FILENOTFOUND; + HResult = HResults.COR_E_FILENOTFOUND; FileName = fileName; } public FileNotFoundException(string message, string fileName, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_FILENOTFOUND; + HResult = HResults.COR_E_FILENOTFOUND; FileName = fileName; } @@ -55,7 +57,7 @@ namespace System.IO if (_message == null) { if ((FileName == null) && - (HResult == System.__HResults.COR_E_EXCEPTION)) + (HResult == System.HResults.COR_E_EXCEPTION)) _message = SR.IO_FileNotFound; else if (FileName != null) @@ -93,12 +95,15 @@ namespace System.IO protected FileNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); + FileName = info.GetString("FileNotFound_FileName"); + FusionLog = info.GetString("FileNotFound_FusionLog"); } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + info.AddValue("FileNotFound_FileName", FileName, typeof(string)); + info.AddValue("FileNotFound_FusionLog", FusionLog, typeof(string)); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStream.Unix.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStream.Unix.cs index 7d860ac2fe..34164abc33 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStream.Unix.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStream.Unix.cs @@ -4,6 +4,7 @@ using Microsoft.Win32.SafeHandles; using System.Diagnostics; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -97,24 +98,27 @@ namespace System.IO ignoreNotSupported: true); // just a hint. } - // Jump to the end of the file if opened as Append. if (_mode == FileMode.Append) { - _appendStart = SeekCore(0, SeekOrigin.End); + // Jump to the end of the file if opened as Append. + _appendStart = SeekCore(_fileHandle, 0, SeekOrigin.End); + } + else if (mode == FileMode.Create || mode == FileMode.Truncate) + { + // Truncate the file now if the file mode requires it. This ensures that the file only will be truncated + // if opened successfully. + CheckFileCall(Interop.Sys.FTruncate(_fileHandle, 0)); } } /// Initializes a stream from an already open file handle (file descriptor). - /// The handle to the file. - /// The size of the buffer to use when buffering. - /// Whether access to the stream is performed asynchronously. - private void InitFromHandle(SafeFileHandle handle) + private void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO) { - if (_useAsyncIO) + if (useAsyncIO) _asyncState = new AsyncState(); - if (CanSeekCore) // use non-virtual CanSeekCore rather than CanSeek to avoid making virtual call during ctor - SeekCore(0, SeekOrigin.Current); + if (CanSeekCore(handle)) // use non-virtual CanSeekCore rather than CanSeek to avoid making virtual call during ctor + SeekCore(handle, 0, SeekOrigin.Current); } /// Translates the FileMode, FileAccess, and FileOptions values into flags to be passed when opening the file. @@ -131,24 +135,18 @@ namespace System.IO { default: case FileMode.Open: // Open maps to the default behavior for open(...). No flags needed. + case FileMode.Truncate: // We truncate the file after getting the lock break; case FileMode.Append: // Append is the same as OpenOrCreate, except that we'll also separately jump to the end later case FileMode.OpenOrCreate: + case FileMode.Create: // We truncate the file after getting the lock flags |= Interop.Sys.OpenFlags.O_CREAT; break; - case FileMode.Create: - flags |= (Interop.Sys.OpenFlags.O_CREAT | Interop.Sys.OpenFlags.O_TRUNC); - break; - case FileMode.CreateNew: flags |= (Interop.Sys.OpenFlags.O_CREAT | Interop.Sys.OpenFlags.O_EXCL); break; - - case FileMode.Truncate: - flags |= Interop.Sys.OpenFlags.O_TRUNC; - break; } // Translate FileAccess. All possible values map cleanly to corresponding values for open. @@ -189,26 +187,27 @@ namespace System.IO } /// Gets a value indicating whether the current stream supports seeking. - public override bool CanSeek => CanSeekCore; + public override bool CanSeek => CanSeekCore(_fileHandle); /// Gets a value indicating whether the current stream supports seeking. - /// Separated out of CanSeek to enable making non-virtual call to this logic. - private bool CanSeekCore + /// + /// Separated out of CanSeek to enable making non-virtual call to this logic. + /// We also pass in the file handle to allow the constructor to use this before it stashes the handle. + /// + private bool CanSeekCore(SafeFileHandle fileHandle) { - get + if (fileHandle.IsClosed) { - if (_fileHandle.IsClosed) - { - return false; - } - - if (!_canSeek.HasValue) - { - // Lazily-initialize whether we're able to seek, tested by seeking to our current location. - _canSeek = Interop.Sys.LSeek(_fileHandle, 0, Interop.Sys.SeekWhence.SEEK_CUR) >= 0; - } - return _canSeek.Value; + return false; } + + if (!_canSeek.HasValue) + { + // Lazily-initialize whether we're able to seek, tested by seeking to our current location. + _canSeek = Interop.Sys.LSeek(fileHandle, 0, Interop.Sys.SeekWhence.SEEK_CUR) >= 0; + } + + return _canSeek.Value; } private long GetLengthInternal() @@ -288,13 +287,20 @@ namespace System.IO } } + private void FlushWriteBufferForWriteByte() + { + _asyncState?.Wait(); + try { FlushWriteBuffer(); } + finally { _asyncState?.Release(); } + } + /// Writes any data in the write buffer to the underlying stream and resets the buffer. private void FlushWriteBuffer() { AssertBufferInvariants(); if (_writePos > 0) { - WriteNative(GetBuffer(), 0, _writePos); + WriteNative(new ReadOnlySpan(GetBuffer(), 0, _writePos)); _writePos = 0; } } @@ -357,7 +363,7 @@ namespace System.IO if (_filePosition != value) { - SeekCore(value, SeekOrigin.Begin); + SeekCore(_fileHandle, value, SeekOrigin.Begin); } CheckFileCall(Interop.Sys.FTruncate(_fileHandle, value)); @@ -367,54 +373,17 @@ namespace System.IO { if (origPos < value) { - SeekCore(origPos, SeekOrigin.Begin); + SeekCore(_fileHandle, origPos, SeekOrigin.Begin); } else { - SeekCore(0, SeekOrigin.End); + SeekCore(_fileHandle, 0, SeekOrigin.End); } } } /// Reads a block of bytes from the stream and writes the data in a given buffer. - /// - /// When this method returns, contains the specified byte array with the values between offset and - /// (offset + count - 1) replaced by the bytes read from the current source. - /// - /// The byte offset in array at which the read bytes will be placed. - /// The maximum number of bytes to read. - /// - /// The total number of bytes read into the buffer. This might be less than the number of bytes requested - /// if that number of bytes are not currently available, or zero if the end of the stream is reached. - /// - public override int Read(byte[] array, int offset, int count) - { - ValidateReadWriteArgs(array, offset, count); - - if (_useAsyncIO) - { - _asyncState.Wait(); - try { return ReadCore(array, offset, count); } - finally { _asyncState.Release(); } - } - else - { - return ReadCore(array, offset, count); - } - } - - /// Reads a block of bytes from the stream and writes the data in a given buffer. - /// - /// When this method returns, contains the specified byte array with the values between offset and - /// (offset + count - 1) replaced by the bytes read from the current source. - /// - /// The byte offset in array at which the read bytes will be placed. - /// The maximum number of bytes to read. - /// - /// The total number of bytes read into the buffer. This might be less than the number of bytes requested - /// if that number of bytes are not currently available, or zero if the end of the stream is reached. - /// - private int ReadCore(byte[] array, int offset, int count) + private int ReadSpan(Span destination) { PrepareForReading(); @@ -431,16 +400,16 @@ namespace System.IO // If we're not able to seek, then we're not able to rewind the stream (i.e. flushing // a read buffer), in which case we don't want to use a read buffer. Similarly, if // the user has asked for more data than we can buffer, we also want to skip the buffer. - if (!CanSeek || (count >= _bufferLength)) + if (!CanSeek || (destination.Length >= _bufferLength)) { // Read directly into the user's buffer _readPos = _readLength = 0; - return ReadNative(array, offset, count); + return ReadNative(destination); } else { // Read into our buffer. - _readLength = numBytesAvailable = ReadNative(GetBuffer(), 0, _bufferLength); + _readLength = numBytesAvailable = ReadNative(GetBuffer()); _readPos = 0; if (numBytesAvailable == 0) { @@ -456,8 +425,8 @@ namespace System.IO // Now that we know there's data in the buffer, read from it into the user's buffer. Debug.Assert(numBytesAvailable > 0, "Data must be in the buffer to be here"); - int bytesRead = Math.Min(numBytesAvailable, count); - Buffer.BlockCopy(GetBuffer(), _readPos, array, offset, bytesRead); + int bytesRead = Math.Min(numBytesAvailable, destination.Length); + new Span(GetBuffer(), _readPos, bytesRead).CopyTo(destination); _readPos += bytesRead; // We may not have had enough data in the buffer to completely satisfy the user's request. @@ -468,38 +437,33 @@ namespace System.IO // behavior, we do the same thing here on Unix. Note that we may still get less the requested // amount, as the OS may give us back fewer than we request, either due to reaching the end of // file, or due to its own whims. - if (!readFromOS && bytesRead < count) + if (!readFromOS && bytesRead < destination.Length) { - Debug.Assert(_readPos == _readLength, "bytesToRead should only be < count if numBytesAvailable < count"); + Debug.Assert(_readPos == _readLength, "bytesToRead should only be < destination.Length if numBytesAvailable < destination.Length"); _readPos = _readLength = 0; // no data left in the read buffer - bytesRead += ReadNative(array, offset + bytesRead, count - bytesRead); + bytesRead += ReadNative(destination.Slice(bytesRead)); } return bytesRead; } - /// Unbuffered, reads a block of bytes from the stream and writes the data in a given buffer. - /// - /// When this method returns, contains the specified byte array with the values between offset and - /// (offset + count - 1) replaced by the bytes read from the current source. - /// - /// The byte offset in array at which the read bytes will be placed. - /// The maximum number of bytes to read. + /// Unbuffered, reads a block of bytes from the file handle into the given buffer. + /// The buffer into which data from the file is read. /// /// The total number of bytes read into the buffer. This might be less than the number of bytes requested /// if that number of bytes are not currently available, or zero if the end of the stream is reached. /// - private unsafe int ReadNative(byte[] array, int offset, int count) + private unsafe int ReadNative(Span buffer) { FlushWriteBuffer(); // we're about to read; dump the write buffer VerifyOSHandlePosition(); int bytesRead; - fixed (byte* bufPtr = array) + fixed (byte* bufPtr = &MemoryMarshal.GetReference(buffer)) { - bytesRead = CheckFileCall(Interop.Sys.Read(_fileHandle, bufPtr + offset, count)); - Debug.Assert(bytesRead <= count); + bytesRead = CheckFileCall(Interop.Sys.Read(_fileHandle, bufPtr, buffer.Length)); + Debug.Assert(bytesRead <= buffer.Length); } _filePosition += bytesRead; return bytesRead; @@ -509,135 +473,97 @@ namespace System.IO /// Asynchronously reads a sequence of bytes from the current stream and advances /// the position within the stream by the number of bytes read. /// - /// The buffer to write the data into. - /// The byte offset in buffer at which to begin writing data from the stream. - /// The maximum number of bytes to read. + /// The buffer to write the data into. /// The token to monitor for cancellation requests. + /// If the operation completes synchronously, the number of bytes read. /// A task that represents the asynchronous read operation. - private Task ReadAsyncInternal(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + private Task ReadAsyncInternal(Memory destination, CancellationToken cancellationToken, out int synchronousResult) { - if (_useAsyncIO) + Debug.Assert(_useAsyncIO); + + if (!CanRead) // match Windows behavior; this gets thrown synchronously { - if (!CanRead) // match Windows behavior; this gets thrown synchronously + throw Error.GetReadNotSupported(); + } + + // Serialize operations using the semaphore. + Task waitTask = _asyncState.WaitAsync(); + + // If we got ownership immediately, and if there's enough data in our buffer + // to satisfy the full request of the caller, hand back the buffered data. + // While it would be a legal implementation of the Read contract, we don't + // hand back here less than the amount requested so as to match the behavior + // in ReadCore that will make a native call to try to fulfill the remainder + // of the request. + if (waitTask.Status == TaskStatus.RanToCompletion) + { + int numBytesAvailable = _readLength - _readPos; + if (numBytesAvailable >= destination.Length) { - throw Error.GetReadNotSupported(); - } - - // Serialize operations using the semaphore. - Task waitTask = _asyncState.WaitAsync(); - - // If we got ownership immediately, and if there's enough data in our buffer - // to satisfy the full request of the caller, hand back the buffered data. - // While it would be a legal implementation of the Read contract, we don't - // hand back here less than the amount requested so as to match the behavior - // in ReadCore that will make a native call to try to fulfill the remainder - // of the request. - if (waitTask.Status == TaskStatus.RanToCompletion) - { - int numBytesAvailable = _readLength - _readPos; - if (numBytesAvailable >= count) - { - try - { - PrepareForReading(); - - Buffer.BlockCopy(GetBuffer(), _readPos, buffer, offset, count); - _readPos += count; - - return _asyncState._lastSuccessfulReadTask != null && _asyncState._lastSuccessfulReadTask.Result == count ? - _asyncState._lastSuccessfulReadTask : - (_asyncState._lastSuccessfulReadTask = Task.FromResult(count)); - } - catch (Exception exc) - { - return Task.FromException(exc); - } - finally - { - _asyncState.Release(); - } - } - } - - // Otherwise, issue the whole request asynchronously. - _asyncState.Update(buffer, offset, count); - return waitTask.ContinueWith((t, s) => - { - // The options available on Unix for writing asynchronously to an arbitrary file - // handle typically amount to just using another thread to do the synchronous write, - // which is exactly what this implementation does. This does mean there are subtle - // differences in certain FileStream behaviors between Windows and Unix when multiple - // asynchronous operations are issued against the stream to execute concurrently; on - // Unix the operations will be serialized due to the usage of a semaphore, but the - // position /length information won't be updated until after the write has completed, - // whereas on Windows it may happen before the write has completed. - - Debug.Assert(t.Status == TaskStatus.RanToCompletion); - var thisRef = (FileStream)s; try { - byte[] b = thisRef._asyncState._buffer; - thisRef._asyncState._buffer = null; // remove reference to user's buffer - return thisRef.ReadCore(b, thisRef._asyncState._offset, thisRef._asyncState._count); + PrepareForReading(); + + new Span(GetBuffer(), _readPos, destination.Length).CopyTo(destination.Span); + _readPos += destination.Length; + + synchronousResult = destination.Length; + return null; } - finally { thisRef._asyncState.Release(); } - }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default); + catch (Exception exc) + { + synchronousResult = 0; + return Task.FromException(exc); + } + finally + { + _asyncState.Release(); + } + } } - else + + // Otherwise, issue the whole request asynchronously. + synchronousResult = 0; + _asyncState.Memory = destination; + return waitTask.ContinueWith((t, s) => { - return base.ReadAsync(buffer, offset, count, cancellationToken); - } + // The options available on Unix for writing asynchronously to an arbitrary file + // handle typically amount to just using another thread to do the synchronous write, + // which is exactly what this implementation does. This does mean there are subtle + // differences in certain FileStream behaviors between Windows and Unix when multiple + // asynchronous operations are issued against the stream to execute concurrently; on + // Unix the operations will be serialized due to the usage of a semaphore, but the + // position /length information won't be updated until after the write has completed, + // whereas on Windows it may happen before the write has completed. + + Debug.Assert(t.Status == TaskStatus.RanToCompletion); + var thisRef = (FileStream)s; + try + { + Memory memory = thisRef._asyncState.Memory; + thisRef._asyncState.Memory = default(Memory); + return thisRef.ReadSpan(memory.Span); + } + finally { thisRef._asyncState.Release(); } + }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default); } - /// - /// Reads a byte from the stream and advances the position within the stream - /// by one byte, or returns -1 if at the end of the stream. - /// - /// The unsigned byte cast to an Int32, or -1 if at the end of the stream. - public override int ReadByte() + /// Reads from the file handle into the buffer, overwriting anything in it. + private int FillReadBufferForReadByte() { - if (_useAsyncIO) - { - _asyncState.Wait(); - try { return ReadByteCore(); } - finally { _asyncState.Release(); } - } - else - { - return ReadByteCore(); - } + _asyncState?.Wait(); + try { return ReadNative(_buffer); } + finally { _asyncState?.Release(); } } /// Writes a block of bytes to the file stream. - /// The buffer containing data to write to the stream. - /// The zero-based byte offset in array from which to begin copying bytes to the stream. - /// The maximum number of bytes to write. - public override void Write(byte[] array, int offset, int count) - { - ValidateReadWriteArgs(array, offset, count); - - if (_useAsyncIO) - { - _asyncState.Wait(); - try { WriteCore(array, offset, count); } - finally { _asyncState.Release(); } - } - else - { - WriteCore(array, offset, count); - } - } - - /// Writes a block of bytes to the file stream. - /// The buffer containing data to write to the stream. - /// The zero-based byte offset in array from which to begin copying bytes to the stream. - /// The maximum number of bytes to write. - private void WriteCore(byte[] array, int offset, int count) + /// The buffer containing data to write to the stream. + private void WriteSpan(ReadOnlySpan source) { PrepareForWriting(); // If no data is being written, nothing more to do. - if (count == 0) + if (source.Length == 0) { return; } @@ -649,21 +575,17 @@ namespace System.IO // If there's space remaining in the buffer, then copy as much as // we can from the user's buffer into ours. int spaceRemaining = _bufferLength - _writePos; - if (spaceRemaining > 0) + if (spaceRemaining >= source.Length) { - int bytesToCopy = Math.Min(spaceRemaining, count); - Buffer.BlockCopy(array, offset, GetBuffer(), _writePos, bytesToCopy); - _writePos += bytesToCopy; - - // If we've successfully copied all of the user's data, we're done. - if (count == bytesToCopy) - { - return; - } - - // Otherwise, keep track of how much more data needs to be handled. - offset += bytesToCopy; - count -= bytesToCopy; + source.CopyTo(new Span(GetBuffer()).Slice(_writePos)); + _writePos += source.Length; + return; + } + else if (spaceRemaining > 0) + { + source.Slice(0, spaceRemaining).CopyTo(new Span(GetBuffer()).Slice(_writePos)); + _writePos += spaceRemaining; + source = source.Slice(spaceRemaining); } // At this point, the buffer is full, so flush it out. @@ -674,35 +596,33 @@ namespace System.IO // the user's looking to write more data than we can store in the buffer), // skip the buffer. Otherwise, put the remaining data into the buffer. Debug.Assert(_writePos == 0); - if (count >= _bufferLength) + if (source.Length >= _bufferLength) { - WriteNative(array, offset, count); + WriteNative(source); } else { - Buffer.BlockCopy(array, offset, GetBuffer(), _writePos, count); - _writePos = count; + source.CopyTo(new Span(GetBuffer())); + _writePos = source.Length; } } /// Unbuffered, writes a block of bytes to the file stream. - /// The buffer containing data to write to the stream. - /// The zero-based byte offset in array from which to begin copying bytes to the stream. - /// The maximum number of bytes to write. - private unsafe void WriteNative(byte[] array, int offset, int count) + /// The buffer containing data to write to the stream. + private unsafe void WriteNative(ReadOnlySpan source) { VerifyOSHandlePosition(); - fixed (byte* bufPtr = array) + fixed (byte* bufPtr = &MemoryMarshal.GetReference(source)) { + int offset = 0; + int count = source.Length; while (count > 0) { int bytesWritten = CheckFileCall(Interop.Sys.Write(_fileHandle, bufPtr + offset, count)); - Debug.Assert(bytesWritten <= count); - _filePosition += bytesWritten; - count -= bytesWritten; offset += bytesWritten; + count -= bytesWritten; } } } @@ -712,103 +632,77 @@ namespace System.IO /// the current position within this stream by the number of bytes written, and /// monitors cancellation requests. /// - /// The buffer to write data from. - /// The zero-based byte offset in buffer from which to begin copying bytes to the stream. - /// The maximum number of bytes to write. + /// The buffer to write data from. /// The token to monitor for cancellation requests. /// A task that represents the asynchronous write operation. - private Task WriteAsyncInternal(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + private Task WriteAsyncInternal(ReadOnlyMemory source, CancellationToken cancellationToken) { + Debug.Assert(_useAsyncIO); + if (cancellationToken.IsCancellationRequested) return Task.FromCanceled(cancellationToken); if (_fileHandle.IsClosed) throw Error.GetFileNotOpen(); - if (_useAsyncIO) + if (!CanWrite) // match Windows behavior; this gets thrown synchronously { - if (!CanWrite) // match Windows behavior; this gets thrown synchronously + throw Error.GetWriteNotSupported(); + } + + // Serialize operations using the semaphore. + Task waitTask = _asyncState.WaitAsync(); + + // If we got ownership immediately, and if there's enough space in our buffer + // to buffer the entire write request, then do so and we're done. + if (waitTask.Status == TaskStatus.RanToCompletion) + { + int spaceRemaining = _bufferLength - _writePos; + if (spaceRemaining >= source.Length) { - throw Error.GetWriteNotSupported(); - } - - // Serialize operations using the semaphore. - Task waitTask = _asyncState.WaitAsync(); - - // If we got ownership immediately, and if there's enough space in our buffer - // to buffer the entire write request, then do so and we're done. - if (waitTask.Status == TaskStatus.RanToCompletion) - { - int spaceRemaining = _bufferLength - _writePos; - if (spaceRemaining >= count) - { - try - { - PrepareForWriting(); - - Buffer.BlockCopy(buffer, offset, GetBuffer(), _writePos, count); - _writePos += count; - - return Task.CompletedTask; - } - catch (Exception exc) - { - return Task.FromException(exc); - } - finally - { - _asyncState.Release(); - } - } - } - - // Otherwise, issue the whole request asynchronously. - _asyncState.Update(buffer, offset, count); - return waitTask.ContinueWith((t, s) => - { - // The options available on Unix for writing asynchronously to an arbitrary file - // handle typically amount to just using another thread to do the synchronous write, - // which is exactly what this implementation does. This does mean there are subtle - // differences in certain FileStream behaviors between Windows and Unix when multiple - // asynchronous operations are issued against the stream to execute concurrently; on - // Unix the operations will be serialized due to the usage of a semaphore, but the - // position /length information won't be updated until after the write has completed, - // whereas on Windows it may happen before the write has completed. - - Debug.Assert(t.Status == TaskStatus.RanToCompletion); - var thisRef = (FileStream)s; try { - byte[] b = thisRef._asyncState._buffer; - thisRef._asyncState._buffer = null; // remove reference to user's buffer - thisRef.WriteCore(b, thisRef._asyncState._offset, thisRef._asyncState._count); - } - finally { thisRef._asyncState.Release(); } - }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default); - } - else - { - return base.WriteAsync(buffer, offset, count, cancellationToken); - } - } + PrepareForWriting(); - /// - /// Writes a byte to the current position in the stream and advances the position - /// within the stream by one byte. - /// - /// The byte to write to the stream. - public override void WriteByte(byte value) // avoids an array allocation in the base implementation - { - if (_useAsyncIO) - { - _asyncState.Wait(); - try { WriteByteCore(value); } - finally { _asyncState.Release(); } + source.Span.CopyTo(new Span(GetBuffer(), _writePos, source.Length)); + _writePos += source.Length; + + return Task.CompletedTask; + } + catch (Exception exc) + { + return Task.FromException(exc); + } + finally + { + _asyncState.Release(); + } + } } - else + + // Otherwise, issue the whole request asynchronously. + _asyncState.ReadOnlyMemory = source; + return waitTask.ContinueWith((t, s) => { - WriteByteCore(value); - } + // The options available on Unix for writing asynchronously to an arbitrary file + // handle typically amount to just using another thread to do the synchronous write, + // which is exactly what this implementation does. This does mean there are subtle + // differences in certain FileStream behaviors between Windows and Unix when multiple + // asynchronous operations are issued against the stream to execute concurrently; on + // Unix the operations will be serialized due to the usage of a semaphore, but the + // position/length information won't be updated until after the write has completed, + // whereas on Windows it may happen before the write has completed. + + Debug.Assert(t.Status == TaskStatus.RanToCompletion); + var thisRef = (FileStream)s; + try + { + ReadOnlyMemory readOnlyMemory = thisRef._asyncState.ReadOnlyMemory; + thisRef._asyncState.ReadOnlyMemory = default(ReadOnlyMemory); + thisRef.WriteSpan(readOnlyMemory.Span); + } + finally { thisRef._asyncState.Release(); } + }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default); } /// Sets the current position of this stream to the given value. @@ -851,16 +745,16 @@ namespace System.IO long oldPos = 0; if (_appendStart >= 0) { - oldPos = SeekCore(0, SeekOrigin.Current); + oldPos = SeekCore(_fileHandle, 0, SeekOrigin.Current); } // Jump to the new location - long pos = SeekCore(offset, origin); + long pos = SeekCore(_fileHandle, offset, origin); // Prevent users from overwriting data in a file that was opened in append mode. if (_appendStart != -1 && pos < _appendStart) { - SeekCore(oldPos, SeekOrigin.Begin); + SeekCore(_fileHandle, oldPos, SeekOrigin.Begin); throw new IOException(SR.IO_SeekAppendOverwrite); } @@ -875,12 +769,12 @@ namespace System.IO /// point for offset, using a value of type SeekOrigin. /// /// The new position in the stream. - private long SeekCore(long offset, SeekOrigin origin) + private long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin) { - Debug.Assert(!_fileHandle.IsClosed && (GetType() != typeof(FileStream) || CanSeek)); // verify that we can seek, but only if CanSeek won't be a virtual call (which could happen in the ctor) + Debug.Assert(!fileHandle.IsClosed && (GetType() != typeof(FileStream) || CanSeekCore(fileHandle))); // verify that we can seek, but only if CanSeek won't be a virtual call (which could happen in the ctor) Debug.Assert(origin >= SeekOrigin.Begin && origin <= SeekOrigin.End); - long pos = CheckFileCall(Interop.Sys.LSeek(_fileHandle, offset, (Interop.Sys.SeekWhence)(int)origin)); // SeekOrigin values are the same as Interop.libc.SeekWhence values + long pos = CheckFileCall(Interop.Sys.LSeek(fileHandle, offset, (Interop.Sys.SeekWhence)(int)origin)); // SeekOrigin values are the same as Interop.libc.SeekWhence values _filePosition = pos; return pos; } @@ -909,25 +803,11 @@ namespace System.IO /// State used when the stream is in async mode. private sealed class AsyncState : SemaphoreSlim { - /// The caller's buffer currently being used by the active async operation. - internal byte[] _buffer; - /// The caller's offset currently being used by the active async operation. - internal int _offset; - /// The caller's count currently being used by the active async operation. - internal int _count; - /// The last task successfully, synchronously returned task from ReadAsync. - internal Task _lastSuccessfulReadTask; + internal ReadOnlyMemory ReadOnlyMemory; + internal Memory Memory; /// Initialize the AsyncState. internal AsyncState() : base(initialCount: 1, maxCount: 1) { } - - /// Sets the active buffer, offset, and count. - internal void Update(byte[] buffer, int offset, int count) - { - _buffer = buffer; - _offset = offset; - _count = count; - } } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStream.Win32.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStream.Win32.cs index 61cd007895..4b75ad6dad 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStream.Win32.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStream.Win32.cs @@ -3,13 +3,17 @@ // See the LICENSE file in the project root for more information. using Microsoft.Win32.SafeHandles; -using System.Runtime.InteropServices; namespace System.IO { public partial class FileStream : Stream { private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) + { + return CreateFileOpenHandle(mode, share, options); + } + + private unsafe SafeFileHandle CreateFileOpenHandle(FileMode mode, FileShare share, FileOptions options) { Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share); @@ -30,44 +34,13 @@ namespace System.IO // For mitigating local elevation of privilege attack through named pipes // make sure we always call CreateFile with SECURITY_ANONYMOUS so that the // named pipe server can't impersonate a high privileged client security context + // (note that this is the effective default on CreateFile2) flagsAndAttributes |= (Interop.Kernel32.SecurityOptions.SECURITY_SQOS_PRESENT | Interop.Kernel32.SecurityOptions.SECURITY_ANONYMOUS); - // Don't pop up a dialog for reading from an empty floppy drive - uint oldMode; - bool success = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS, out oldMode); - try + using (DisableMediaInsertionPrompt.Create()) { - SafeFileHandle fileHandle = Interop.Kernel32.CreateFile(_path, fAccess, share, ref secAttrs, mode, flagsAndAttributes, IntPtr.Zero); - fileHandle.IsAsync = _useAsyncIO; - - if (fileHandle.IsInvalid) - { - // Return a meaningful exception with the full path. - - // NT5 oddity - when trying to open "C:\" as a Win32FileStream, - // we usually get ERROR_PATH_NOT_FOUND from the OS. We should - // probably be consistent w/ every other directory. - int errorCode = Marshal.GetLastWin32Error(); - - if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && _path.Length == PathInternal.GetRootLength(_path)) - errorCode = Interop.Errors.ERROR_ACCESS_DENIED; - - throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); - } - - int fileType = Interop.Kernel32.GetFileType(fileHandle); - if (fileType != Interop.Kernel32.FileTypes.FILE_TYPE_DISK) - { - fileHandle.Dispose(); - throw new NotSupportedException(SR.NotSupported_FileStreamOnNonFiles); - } - - return fileHandle; - } - finally - { - if (success) - Interop.Kernel32.SetThreadErrorMode(oldMode, out oldMode); + return ValidateFileHandle( + Interop.Kernel32.CreateFile(_path, fAccess, share, ref secAttrs, mode, flagsAndAttributes, IntPtr.Zero)); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStream.WinRT.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStream.WinRT.cs index b9a9f8a783..304088eea7 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStream.WinRT.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStream.WinRT.cs @@ -13,7 +13,7 @@ namespace System.IO { Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share); - int fAccess = + int access = ((_access & FileAccess.Read) == FileAccess.Read ? GENERIC_READ : 0) | ((_access & FileAccess.Write) == FileAccess.Write ? GENERIC_WRITE : 0); @@ -30,31 +30,15 @@ namespace System.IO parameters.dwFileFlags = (uint)options; parameters.lpSecurityAttributes = &secAttrs; - SafeFileHandle fileHandle = Interop.Kernel32.CreateFile2( - lpFileName: _path, - dwDesiredAccess: fAccess, - dwShareMode: share, - dwCreationDisposition: mode, - pCreateExParams: ¶meters); - - fileHandle.IsAsync = _useAsyncIO; - - if (fileHandle.IsInvalid) + using (DisableMediaInsertionPrompt.Create()) { - // Return a meaningful exception with the full path. - - // NT5 oddity - when trying to open "C:\" as a Win32FileStream, - // we usually get ERROR_PATH_NOT_FOUND from the OS. We should - // probably be consistent w/ every other directory. - int errorCode = Marshal.GetLastWin32Error(); - - if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && _path.Length == PathInternal.GetRootLength(_path)) - errorCode = Interop.Errors.ERROR_ACCESS_DENIED; - - throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); + return ValidateFileHandle(Interop.Kernel32.CreateFile2( + lpFileName: _path, + dwDesiredAccess: access, + dwShareMode: share, + dwCreationDisposition: mode, + pCreateExParams: ref parameters)); } - - return fileHandle; } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs index c036ee6a83..477b9430fc 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs @@ -48,7 +48,6 @@ namespace System.IO private static unsafe IOCompletionCallback s_ioCallback = FileStreamCompletionSource.IOCallback; - private Task _lastSynchronouslyCompletedTask = null; // cached task for read ops that complete synchronously private Task _activeBufferOperation = null; // tracks in-progress async ops using the buffer private PreAllocatedOverlapped _preallocatedOverlapped; // optimization for async ops to avoid per-op allocations private FileStreamCompletionSource _currentOverlappedOwner; // async op currently using the preallocated overlapped @@ -100,7 +99,7 @@ namespace System.IO // For Append mode... if (mode == FileMode.Append) { - _appendStart = SeekCore(0, SeekOrigin.End); + _appendStart = SeekCore(_fileHandle, 0, SeekOrigin.End); } else { @@ -108,9 +107,28 @@ namespace System.IO } } - private void InitFromHandle(SafeFileHandle handle) + private void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAsyncIO) { - int handleType = Interop.Kernel32.GetFileType(_fileHandle); +#if DEBUG + bool hadBinding = handle.ThreadPoolBinding != null; + + try + { +#endif + InitFromHandleImpl(handle, access, useAsyncIO); +#if DEBUG + } + catch + { + Debug.Assert(hadBinding || handle.ThreadPoolBinding == null, "We should never error out with a ThreadPoolBinding we've added"); + throw; + } +#endif + } + + private void InitFromHandleImpl(SafeFileHandle handle, FileAccess access, bool useAsyncIO) + { + int handleType = Interop.Kernel32.GetFileType(handle); Debug.Assert(handleType == Interop.Kernel32.FileTypes.FILE_TYPE_DISK || handleType == Interop.Kernel32.FileTypes.FILE_TYPE_PIPE || handleType == Interop.Kernel32.FileTypes.FILE_TYPE_CHAR, "FileStream was passed an unknown file type!"); _canSeek = handleType == Interop.Kernel32.FileTypes.FILE_TYPE_DISK; @@ -128,11 +146,11 @@ namespace System.IO // If, however, we've already bound this file handle to our completion port, // don't try to bind it again because it will fail. A handle can only be // bound to a single completion port at a time. - if (_useAsyncIO && !GetSuppressBindHandle(handle)) + if (useAsyncIO && !(handle.IsAsync ?? false)) { try { - _fileHandle.ThreadPoolBinding = ThreadPoolBoundHandle.BindHandle(_fileHandle); + handle.ThreadPoolBinding = ThreadPoolBoundHandle.BindHandle(handle); } catch (Exception ex) { @@ -141,23 +159,18 @@ namespace System.IO throw new ArgumentException(SR.Arg_HandleNotAsync, nameof(handle), ex); } } - else if (!_useAsyncIO) + else if (!useAsyncIO) { if (handleType != Interop.Kernel32.FileTypes.FILE_TYPE_PIPE) - VerifyHandleIsSync(); + VerifyHandleIsSync(handle, access); } if (_canSeek) - SeekCore(0, SeekOrigin.Current); + SeekCore(handle, 0, SeekOrigin.Current); else _filePosition = 0; } - private static bool GetSuppressBindHandle(SafeFileHandle handle) - { - return handle.IsAsync.HasValue ? handle.IsAsync.Value : false; - } - private unsafe static Interop.Kernel32.SECURITY_ATTRIBUTES GetSecAttrs(FileShare share) { Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = default(Interop.Kernel32.SECURITY_ATTRIBUTES); @@ -173,15 +186,13 @@ namespace System.IO // Verifies that this handle supports synchronous IO operations (unless you // didn't open it for either reading or writing). - private unsafe void VerifyHandleIsSync() + private unsafe static void VerifyHandleIsSync(SafeFileHandle handle, FileAccess access) { - Debug.Assert(!_useAsyncIO); - // Do NOT use this method on pipes. Reading or writing to a pipe may // cause an app to block incorrectly, introducing a deadlock (depending // on whether a write will wake up an already-blocked thread or this // Win32FileStream's thread). - Debug.Assert(Interop.Kernel32.GetFileType(_fileHandle) != Interop.Kernel32.FileTypes.FILE_TYPE_PIPE); + Debug.Assert(Interop.Kernel32.GetFileType(handle) != Interop.Kernel32.FileTypes.FILE_TYPE_PIPE); byte* bytes = stackalloc byte[1]; int numBytesReadWritten; @@ -191,20 +202,25 @@ namespace System.IO // has been a write on the other end. We'll just have to deal with it, // For the read end of a pipe, you can mess up and // accidentally read synchronously from an async pipe. - if ((_access & FileAccess.Read) != 0) // don't use the virtual CanRead or CanWrite, as this may be used in the ctor + if ((access & FileAccess.Read) != 0) // don't use the virtual CanRead or CanWrite, as this may be used in the ctor { - r = Interop.Kernel32.ReadFile(_fileHandle, bytes, 0, out numBytesReadWritten, IntPtr.Zero); + r = Interop.Kernel32.ReadFile(handle, bytes, 0, out numBytesReadWritten, IntPtr.Zero); } - else if ((_access & FileAccess.Write) != 0) // don't use the virtual CanRead or CanWrite, as this may be used in the ctor + else if ((access & FileAccess.Write) != 0) // don't use the virtual CanRead or CanWrite, as this may be used in the ctor { - r = Interop.Kernel32.WriteFile(_fileHandle, bytes, 0, out numBytesReadWritten, IntPtr.Zero); + r = Interop.Kernel32.WriteFile(handle, bytes, 0, out numBytesReadWritten, IntPtr.Zero); } if (r == 0) { - int errorCode = GetLastWin32ErrorAndDisposeHandleIfInvalid(throwIfInvalidHandle: true); - if (errorCode == ERROR_INVALID_PARAMETER) - throw new ArgumentException(SR.Arg_HandleNotSync, "handle"); + int errorCode = Marshal.GetLastWin32Error(); + switch (errorCode) + { + case Interop.Errors.ERROR_INVALID_PARAMETER: + throw new ArgumentException(SR.Arg_HandleNotSync, "handle"); + case Interop.Errors.ERROR_INVALID_HANDLE: + throw Win32Marshal.GetExceptionForWin32Error(errorCode); + } } } @@ -296,7 +312,7 @@ namespace System.IO // If the buffer is already flushed, don't spin up the OS write if (_writePos == 0) return Task.CompletedTask; - Task flushTask = WriteInternalCoreAsync(GetBuffer(), 0, _writePos, cancellationToken); + Task flushTask = WriteAsyncInternalCore(new ReadOnlyMemory(GetBuffer(), 0, _writePos), cancellationToken); _writePos = 0; // Update the active buffer operation @@ -307,6 +323,8 @@ namespace System.IO return flushTask; } + private void FlushWriteBufferForWriteByte() => FlushWriteBuffer(); + // Writes are buffered. Anytime the buffer fills up // (_writePos + delta > _bufferSize) or the buffer switches to reading // and there is left over data (_writePos > 0), this function must be called. @@ -338,7 +356,7 @@ namespace System.IO } else { - WriteCore(GetBuffer(), 0, _writePos); + WriteCore(new ReadOnlySpan(GetBuffer(), 0, _writePos)); } _writePos = 0; @@ -372,7 +390,7 @@ namespace System.IO VerifyOSHandlePosition(); if (_filePosition != value) - SeekCore(value, SeekOrigin.Begin); + SeekCore(_fileHandle, value, SeekOrigin.Begin); if (!Interop.Kernel32.SetEndOfFile(_fileHandle)) { int errorCode = Marshal.GetLastWin32Error(); @@ -384,9 +402,9 @@ namespace System.IO if (origPos != value) { if (origPos < value) - SeekCore(origPos, SeekOrigin.Begin); + SeekCore(_fileHandle, origPos, SeekOrigin.Begin); else - SeekCore(0, SeekOrigin.End); + SeekCore(_fileHandle, 0, SeekOrigin.End); } } @@ -394,14 +412,9 @@ namespace System.IO // accessing its fields by ref. This avoids a compiler warning. private FileStreamCompletionSource CompareExchangeCurrentOverlappedOwner(FileStreamCompletionSource newSource, FileStreamCompletionSource existingSource) => Interlocked.CompareExchange(ref _currentOverlappedOwner, newSource, existingSource); - public override int Read(byte[] array, int offset, int count) - { - ValidateReadWriteArgs(array, offset, count); - return ReadCore(array, offset, count); - } - - private int ReadCore(byte[] array, int offset, int count) + private int ReadSpan(Span destination) { + Debug.Assert(!_useAsyncIO, "Must only be used when in synchronous mode"); Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both."); @@ -413,23 +426,23 @@ namespace System.IO { if (!CanRead) throw Error.GetReadNotSupported(); if (_writePos > 0) FlushWriteBuffer(); - if (!CanSeek || (count >= _bufferLength)) + if (!CanSeek || (destination.Length >= _bufferLength)) { - n = ReadNative(array, offset, count); + n = ReadNative(destination); // Throw away read buffer. _readPos = 0; _readLength = 0; return n; } - n = ReadNative(GetBuffer(), 0, _bufferLength); + n = ReadNative(GetBuffer()); if (n == 0) return 0; isBlocked = n < _bufferLength; _readPos = 0; _readLength = n; } // Now copy min of count or numBytesAvailable (i.e. near EOF) to array. - if (n > count) n = count; - Buffer.BlockCopy(GetBuffer(), _readPos, array, offset, n); + if (n > destination.Length) n = destination.Length; + new ReadOnlySpan(GetBuffer(), _readPos, n).CopyTo(destination); _readPos += n; // We may have read less than the number of bytes the user asked @@ -449,10 +462,10 @@ namespace System.IO // read some more from the underlying stream. However, if we got // fewer bytes from the underlying stream than we asked for (i.e. we're // probably blocked), don't ask for more bytes. - if (n < count && !isBlocked) + if (n < destination.Length && !isBlocked) { Debug.Assert(_readPos == _readLength, "Read buffer should be empty!"); - int moreBytesRead = ReadNative(array, offset + n, count - n); + int moreBytesRead = ReadNative(destination.Slice(n)); n += moreBytesRead; // We've just made our buffer inconsistent with our position // pointer. We must throw away the read buffer. @@ -465,28 +478,28 @@ namespace System.IO } [Conditional("DEBUG")] - private void AssertCanRead(byte[] buffer, int offset, int count) + private void AssertCanRead() { Debug.Assert(!_fileHandle.IsClosed, "!_fileHandle.IsClosed"); Debug.Assert(CanRead, "CanRead"); - Debug.Assert(buffer != null, "buffer != null"); - Debug.Assert(_writePos == 0, "_writePos == 0"); - Debug.Assert(offset >= 0, "offset is negative"); - Debug.Assert(count >= 0, "count is negative"); } - private unsafe int ReadNative(byte[] buffer, int offset, int count) - { - AssertCanRead(buffer, offset, count); + /// Reads from the file handle into the buffer, overwriting anything in it. + private int FillReadBufferForReadByte() => + _useAsyncIO ? + ReadNativeAsync(new Memory(_buffer), 0, CancellationToken.None).GetAwaiter().GetResult() : + ReadNative(_buffer); - if (_useAsyncIO) - return ReadNativeAsync(buffer, offset, count, 0, CancellationToken.None).GetAwaiter().GetResult(); + private unsafe int ReadNative(Span buffer) + { + Debug.Assert(!_useAsyncIO, $"{nameof(ReadNative)} doesn't work on asynchronous file streams."); + AssertCanRead(); // Make sure we are reading from the right spot VerifyOSHandlePosition(); int errorCode = 0; - int r = ReadFileNative(_fileHandle, buffer, offset, count, null, out errorCode); + int r = ReadFileNative(_fileHandle, buffer, null, out errorCode); if (r == -1) { @@ -540,13 +553,13 @@ namespace System.IO VerifyOSHandlePosition(); long oldPos = _filePosition + (_readPos - _readLength); - long pos = SeekCore(offset, origin); + long pos = SeekCore(_fileHandle, offset, origin); // Prevent users from overwriting data in a file that was opened in // append mode. if (_appendStart != -1 && pos < _appendStart) { - SeekCore(oldPos, SeekOrigin.Begin); + SeekCore(_fileHandle, oldPos, SeekOrigin.Begin); throw new IOException(SR.IO_SeekAppendOverwrite); } @@ -562,7 +575,6 @@ namespace System.IO { if (_readPos > 0) { - //Console.WriteLine("Seek: seeked for 0, adjusting buffer back by: "+_readPos+" _readLen: "+_readLen); Buffer.BlockCopy(GetBuffer(), _readPos, GetBuffer(), 0, _readLength - _readPos); _readLength -= _readPos; _readPos = 0; @@ -570,17 +582,16 @@ namespace System.IO // If we still have buffered data, we must update the stream's // position so our Position property is correct. if (_readLength > 0) - SeekCore(_readLength, SeekOrigin.Current); + SeekCore(_fileHandle, _readLength, SeekOrigin.Current); } else if (oldPos - _readPos < pos && pos < oldPos + _readLength - _readPos) { int diff = (int)(pos - oldPos); - //Console.WriteLine("Seek: diff was "+diff+", readpos was "+_readPos+" adjusting buffer - shrinking by "+ (_readPos + diff)); Buffer.BlockCopy(GetBuffer(), _readPos + diff, GetBuffer(), 0, _readLength - (_readPos + diff)); _readLength -= (_readPos + diff); _readPos = 0; if (_readLength > 0) - SeekCore(_readLength, SeekOrigin.Current); + SeekCore(_fileHandle, _readLength, SeekOrigin.Current); } else { @@ -597,18 +608,23 @@ namespace System.IO // This doesn't do argument checking. Necessary for SetLength, which must // set the file pointer beyond the end of the file. This will update the // internal position - // This is called during construction so it should avoid any virtual - // calls - private long SeekCore(long offset, SeekOrigin origin) + private long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, bool closeInvalidHandle = false) { - Debug.Assert(!_fileHandle.IsClosed && _canSeek, "!_handle.IsClosed && _parent.CanSeek"); - Debug.Assert(origin >= SeekOrigin.Begin && origin <= SeekOrigin.End, "origin>=SeekOrigin.Begin && origin<=SeekOrigin.End"); + Debug.Assert(!fileHandle.IsClosed && _canSeek, "!fileHandle.IsClosed && _canSeek"); + Debug.Assert(origin >= SeekOrigin.Begin && origin <= SeekOrigin.End, "origin >= SeekOrigin.Begin && origin <= SeekOrigin.End"); + long ret = 0; - if (!Interop.Kernel32.SetFilePointerEx(_fileHandle, offset, out ret, (uint)origin)) + if (!Interop.Kernel32.SetFilePointerEx(fileHandle, offset, out ret, (uint)origin)) { - int errorCode = GetLastWin32ErrorAndDisposeHandleIfInvalid(); - throw Win32Marshal.GetExceptionForWin32Error(errorCode); + if (closeInvalidHandle) + { + throw Win32Marshal.GetExceptionForWin32Error(GetLastWin32ErrorAndDisposeHandleIfInvalid(throwIfInvalidHandle: false)); + } + else + { + throw Win32Marshal.GetExceptionForWin32Error(Marshal.GetLastWin32Error()); + } } _filePosition = ret; @@ -624,9 +640,9 @@ namespace System.IO _preallocatedOverlapped = new PreAllocatedOverlapped(s_ioCallback, this, _buffer); } - public override void Write(byte[] array, int offset, int count) + private void WriteSpan(ReadOnlySpan source) { - ValidateReadWriteArgs(array, offset, count); + Debug.Assert(!_useAsyncIO, "Must only be used when in synchronous mode"); if (_writePos == 0) { @@ -650,65 +666,56 @@ namespace System.IO int numBytes = _bufferLength - _writePos; // space left in buffer if (numBytes > 0) { - if (numBytes > count) - numBytes = count; - Buffer.BlockCopy(array, offset, GetBuffer(), _writePos, numBytes); - _writePos += numBytes; - if (count == numBytes) return; - offset += numBytes; - count -= numBytes; + if (numBytes >= source.Length) + { + source.CopyTo(new Span(GetBuffer()).Slice(_writePos)); + _writePos += source.Length; + return; + } + else + { + source.Slice(0, numBytes).CopyTo(new Span(GetBuffer()).Slice(_writePos)); + _writePos += numBytes; + source = source.Slice(numBytes); + } } // Reset our buffer. We essentially want to call FlushWrite // without calling Flush on the underlying Stream. - if (_useAsyncIO) - { - WriteInternalCoreAsync(GetBuffer(), 0, _writePos, CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - WriteCore(GetBuffer(), 0, _writePos); - } + WriteCore(new ReadOnlySpan(GetBuffer(), 0, _writePos)); _writePos = 0; } + // If the buffer would slow writes down, avoid buffer completely. - if (count >= _bufferLength) + if (source.Length >= _bufferLength) { Debug.Assert(_writePos == 0, "FileStream cannot have buffered data to write here! Your stream will be corrupted."); - WriteCore(array, offset, count); + WriteCore(source); return; } - else if (count == 0) + else if (source.Length == 0) { return; // Don't allocate a buffer then call memcpy for 0 bytes. } // Copy remaining bytes into buffer, to write at a later date. - Buffer.BlockCopy(array, offset, GetBuffer(), _writePos, count); - _writePos = count; + source.CopyTo(new Span(GetBuffer()).Slice(_writePos)); + _writePos = source.Length; return; } - private unsafe void WriteCore(byte[] buffer, int offset, int count) + private unsafe void WriteCore(ReadOnlySpan source) { + Debug.Assert(!_useAsyncIO); Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); Debug.Assert(CanWrite, "_parent.CanWrite"); - - Debug.Assert(buffer != null, "buffer != null"); Debug.Assert(_readPos == _readLength, "_readPos == _readLen"); - Debug.Assert(offset >= 0, "offset is negative"); - Debug.Assert(count >= 0, "count is negative"); - if (_useAsyncIO) - { - WriteInternalCoreAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult(); - return; - } // Make sure we are writing to the position that we think we are VerifyOSHandlePosition(); int errorCode = 0; - int r = WriteFileNative(_fileHandle, buffer, offset, count, null, out errorCode); + int r = WriteFileNative(_fileHandle, source, null, out errorCode); if (r == -1) { @@ -720,9 +727,8 @@ namespace System.IO else { // ERROR_INVALID_PARAMETER may be returned for writes - // where the position is too large (i.e. writing at Int64.MaxValue - // on Win9x) OR for synchronous writes to a handle opened - // asynchronously. + // where the position is too large or for synchronous writes + // to a handle opened asynchronously. if (errorCode == ERROR_INVALID_PARAMETER) throw new IOException(SR.IO_FileTooLongOrHandleNotSync); throw Win32Marshal.GetExceptionForWin32Error(errorCode); @@ -733,15 +739,9 @@ namespace System.IO return; } - private Task ReadAsyncInternal(byte[] array, int offset, int numBytes, CancellationToken cancellationToken) + private Task ReadAsyncInternal(Memory destination, CancellationToken cancellationToken, out int synchronousResult) { - // If async IO is not supported on this platform or - // if this Win32FileStream was not opened with FileOptions.Asynchronous. - if (!_useAsyncIO) - { - return base.ReadAsync(array, offset, numBytes, cancellationToken); - } - + Debug.Assert(_useAsyncIO); if (!CanRead) throw Error.GetReadNotSupported(); Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both."); @@ -763,18 +763,17 @@ namespace System.IO // pipes. But don't completely ignore buffered data either. if (_readPos < _readLength) { - int n = _readLength - _readPos; - if (n > numBytes) n = numBytes; - Buffer.BlockCopy(GetBuffer(), _readPos, array, offset, n); + int n = Math.Min(_readLength - _readPos, destination.Length); + new Span(GetBuffer(), _readPos, n).CopyTo(destination.Span); _readPos += n; - - // Return a completed task - return TaskFromResultOrCache(n); + synchronousResult = n; + return null; } else { Debug.Assert(_writePos == 0, "Win32FileStream must not have buffered write data here! Pipes should be unidirectional."); - return ReadNativeAsync(array, offset, numBytes, 0, cancellationToken); + synchronousResult = 0; + return ReadNativeAsync(destination, 0, cancellationToken); } } @@ -795,17 +794,16 @@ namespace System.IO // problem, and any async read less than 64K gets turned into a // synchronous read by NT anyways... -- - if (numBytes < _bufferLength) + if (destination.Length < _bufferLength) { - Task readTask = ReadNativeAsync(GetBuffer(), 0, _bufferLength, 0, cancellationToken); + Task readTask = ReadNativeAsync(new Memory(GetBuffer()), 0, cancellationToken); _readLength = readTask.GetAwaiter().GetResult(); - int n = _readLength; - if (n > numBytes) n = numBytes; - Buffer.BlockCopy(GetBuffer(), 0, array, offset, n); + int n = Math.Min(_readLength, destination.Length); + new Span(GetBuffer(), 0, n).CopyTo(destination.Span); _readPos = n; - // Return a completed task (recycling the one above if possible) - return (_readLength == n ? readTask : TaskFromResultOrCache(n)); + synchronousResult = n; + return null; } else { @@ -813,20 +811,21 @@ namespace System.IO // with our read buffer. Throw away the read buffer's contents. _readPos = 0; _readLength = 0; - return ReadNativeAsync(array, offset, numBytes, 0, cancellationToken); + synchronousResult = 0; + return ReadNativeAsync(destination, 0, cancellationToken); } } else { - int n = _readLength - _readPos; - if (n > numBytes) n = numBytes; - Buffer.BlockCopy(GetBuffer(), _readPos, array, offset, n); + int n = Math.Min(_readLength - _readPos, destination.Length); + new Span(GetBuffer(), _readPos, n).CopyTo(destination.Span); _readPos += n; - if (n >= numBytes) + if (n == destination.Length) { // Return a completed task - return TaskFromResultOrCache(n); + synchronousResult = n; + return null; } else { @@ -837,19 +836,21 @@ namespace System.IO // Throw away read buffer. _readPos = 0; _readLength = 0; - return ReadNativeAsync(array, offset + n, numBytes - n, n, cancellationToken); + synchronousResult = 0; + return ReadNativeAsync(destination.Slice(n), n, cancellationToken); } } } - unsafe private Task ReadNativeAsync(byte[] bytes, int offset, int numBytes, int numBufferedBytesRead, CancellationToken cancellationToken) + unsafe private Task ReadNativeAsync(Memory destination, int numBufferedBytesRead, CancellationToken cancellationToken) { - AssertCanRead(bytes, offset, numBytes); + AssertCanRead(); Debug.Assert(_useAsyncIO, "ReadNativeAsync doesn't work on synchronous file streams!"); // Create and store async stream class library specific data in the async result - - FileStreamCompletionSource completionSource = new FileStreamCompletionSource(this, numBufferedBytesRead, bytes, cancellationToken); + FileStreamCompletionSource completionSource = destination.TryGetArray(out ArraySegment memoryArray) ? + new FileStreamCompletionSource(this, numBufferedBytesRead, memoryArray.Array) : + new MemoryFileStreamCompletionSource(this, numBufferedBytesRead, destination); NativeOverlapped* intOverlapped = completionSource.Overlapped; // Calculate position in the file we should be at after the read is done @@ -860,12 +861,16 @@ namespace System.IO // Make sure we are reading from the position that we think we are VerifyOSHandlePosition(); - if (_filePosition + numBytes > len) + if (_filePosition + destination.Length > len) { if (_filePosition <= len) - numBytes = (int)(len - _filePosition); + { + destination = destination.Slice(0, (int)(len - _filePosition)); + } else - numBytes = 0; + { + destination = default(Memory); + } } // Now set the position to read from in the NativeOverlapped struct @@ -882,12 +887,13 @@ namespace System.IO // the file pointer when writing to a UNC path! // So changed the code below to seek to an absolute // location, not a relative one. ReadFile seems consistent though. - SeekCore(numBytes, SeekOrigin.Current); + SeekCore(_fileHandle, destination.Length, SeekOrigin.Current); } // queue an async ReadFile operation and pass in a packed overlapped int errorCode = 0; - int r = ReadFileNative(_fileHandle, bytes, offset, numBytes, intOverlapped, out errorCode); + int r = ReadFileNative(_fileHandle, destination.Span, intOverlapped, out errorCode); + // ReadFile, the OS version, will return 0 on failure. But // my ReadFileNative wrapper returns -1. My wrapper will return // the following: @@ -898,7 +904,7 @@ namespace System.IO // read back from this call when using overlapped structures! You must // not pass in a non-null lpNumBytesRead to ReadFile when using // overlapped structures! This is by design NT behavior. - if (r == -1 && numBytes != -1) + if (r == -1) { // For pipes, when they hit EOF, they will come here. if (errorCode == ERROR_BROKEN_PIPE) @@ -915,7 +921,7 @@ namespace System.IO { if (!_fileHandle.IsClosed && CanSeek) // Update Position - It could be anywhere. { - SeekCore(0, SeekOrigin.Current); + SeekCore(_fileHandle, 0, SeekOrigin.Current); } completionSource.ReleaseNativeResource(); @@ -929,10 +935,10 @@ namespace System.IO throw Win32Marshal.GetExceptionForWin32Error(errorCode); } } - else + else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING { // Only once the IO is pending do we register for cancellation - completionSource.RegisterForCancellation(); + completionSource.RegisterForCancellation(cancellationToken); } } else @@ -944,33 +950,19 @@ namespace System.IO // synchronously or asynchronously. We absolutely must not // set asyncResult._numBytes here, since will never have correct // results. - //Console.WriteLine("ReadFile returned: "+r+" (0x"+Int32.Format(r, "x")+") The IO completed synchronously, but the user callback was called on a separate thread"); } return completionSource.Task; } - // Reads a byte from the file stream. Returns the byte cast to an int - // or -1 if reading from the end of the stream. - public override int ReadByte() + private Task WriteAsyncInternal(ReadOnlyMemory source, CancellationToken cancellationToken) { - return ReadByteCore(); - } - - private Task WriteAsyncInternal(byte[] array, int offset, int numBytes, CancellationToken cancellationToken) - { - // If async IO is not supported on this platform or - // if this Win32FileStream was not opened with FileOptions.Asynchronous. - if (!_useAsyncIO) - { - return base.WriteAsync(array, offset, numBytes, cancellationToken); - } - - if (!CanWrite) throw Error.GetWriteNotSupported(); - + Debug.Assert(_useAsyncIO); Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both."); Debug.Assert(!_isPipe || (_readPos == 0 && _readLength == 0), "Win32FileStream must not have buffered data here! Pipes should be unidirectional."); + if (!CanWrite) throw Error.GetWriteNotSupported(); + bool writeDataStoredInBuffer = false; if (!_isPipe) // avoid async buffering with pipes, as doing so can lead to deadlocks (see comments in ReadInternalAsyncCore) { @@ -994,10 +986,10 @@ namespace System.IO // - There's no active flush operation, such that we don't have to worry about the existing buffer being in use. // - And the data we're trying to write fits in the buffer, meaning it wasn't already filled by previous writes. // In that case, just store it in the buffer. - if (numBytes < _bufferLength && !HasActiveBufferOperation && numBytes <= remainingBuffer) + if (source.Length < _bufferLength && !HasActiveBufferOperation && source.Length <= remainingBuffer) { - Buffer.BlockCopy(array, offset, GetBuffer(), _writePos, numBytes); - _writePos += numBytes; + source.Span.CopyTo(new Span(GetBuffer(), _writePos, source.Length)); + _writePos += source.Length; writeDataStoredInBuffer = true; // There is one special-but-common case, common because devs often use @@ -1006,7 +998,7 @@ namespace System.IO // then we're done and can return a completed task now. But if we filled the buffer // completely, we want to do the asynchronous flush/write as part of this operation // rather than waiting until the next write that fills the buffer. - if (numBytes != remainingBuffer) + if (source.Length != remainingBuffer) return Task.CompletedTask; Debug.Assert(_writePos == _bufferLength); @@ -1062,40 +1054,37 @@ namespace System.IO // Finally, issue the write asynchronously, and return a Task that logically // represents the write operation, including any flushing done. - Task writeTask = WriteInternalCoreAsync(array, offset, numBytes, cancellationToken); + Task writeTask = WriteAsyncInternalCore(source, cancellationToken); return (flushTask == null || flushTask.Status == TaskStatus.RanToCompletion) ? writeTask : (writeTask.Status == TaskStatus.RanToCompletion) ? flushTask : Task.WhenAll(flushTask, writeTask); } - private unsafe Task WriteInternalCoreAsync(byte[] bytes, int offset, int numBytes, CancellationToken cancellationToken) + private unsafe Task WriteAsyncInternalCore(ReadOnlyMemory source, CancellationToken cancellationToken) { Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); Debug.Assert(CanWrite, "_parent.CanWrite"); - Debug.Assert(bytes != null, "bytes != null"); Debug.Assert(_readPos == _readLength, "_readPos == _readLen"); Debug.Assert(_useAsyncIO, "WriteInternalCoreAsync doesn't work on synchronous file streams!"); - Debug.Assert(offset >= 0, "offset is negative"); - Debug.Assert(numBytes >= 0, "numBytes is negative"); // Create and store async stream class library specific data in the async result - FileStreamCompletionSource completionSource = new FileStreamCompletionSource(this, 0, bytes, cancellationToken); + FileStreamCompletionSource completionSource = MemoryMarshal.TryGetArray(source, out ArraySegment array) ? + new FileStreamCompletionSource(this, 0, array.Array) : + new MemoryFileStreamCompletionSource(this, 0, source); NativeOverlapped* intOverlapped = completionSource.Overlapped; if (CanSeek) { // Make sure we set the length of the file appropriately. long len = Length; - //Console.WriteLine("WriteInternalCoreAsync - Calculating end pos. pos: "+pos+" len: "+len+" numBytes: "+numBytes); // Make sure we are writing to the position that we think we are VerifyOSHandlePosition(); - if (_filePosition + numBytes > len) + if (_filePosition + source.Length > len) { - //Console.WriteLine("WriteInternalCoreAsync - Setting length to: "+(pos + numBytes)); - SetLengthCore(_filePosition + numBytes); + SetLengthCore(_filePosition + source.Length); } // Now set the position to read from in the NativeOverlapped struct @@ -1106,14 +1095,12 @@ namespace System.IO // When using overlapped IO, the OS is not supposed to // touch the file pointer location at all. We will adjust it // ourselves. This isn't threadsafe. - SeekCore(numBytes, SeekOrigin.Current); + SeekCore(_fileHandle, source.Length, SeekOrigin.Current); } - //Console.WriteLine("WriteInternalCoreAsync finishing. pos: "+pos+" numBytes: "+numBytes+" _pos: "+_pos+" Position: "+Position); - int errorCode = 0; // queue an async WriteFile operation and pass in a packed overlapped - int r = WriteFileNative(_fileHandle, bytes, offset, numBytes, intOverlapped, out errorCode); + int r = WriteFileNative(_fileHandle, source.Span, intOverlapped, out errorCode); // WriteFile, the OS version, will return 0 on failure. But // my WriteFileNative wrapper returns -1. My wrapper will return @@ -1125,10 +1112,8 @@ namespace System.IO // written back from this call when using overlapped IO! You must // not pass in a non-null lpNumBytesWritten to WriteFile when using // overlapped structures! This is ByDesign NT behavior. - if (r == -1 && numBytes != -1) + if (r == -1) { - //Console.WriteLine("WriteFile returned 0; Write will complete asynchronously (if errorCode==3e5) errorCode: 0x{0:x}", errorCode); - // For pipes, when they are closed on the other side, they will come here. if (errorCode == ERROR_NO_DATA) { @@ -1141,7 +1126,7 @@ namespace System.IO { if (!_fileHandle.IsClosed && CanSeek) // Update Position - It could be anywhere. { - SeekCore(0, SeekOrigin.Current); + SeekCore(_fileHandle, 0, SeekOrigin.Current); } completionSource.ReleaseNativeResource(); @@ -1155,10 +1140,10 @@ namespace System.IO throw Win32Marshal.GetExceptionForWin32Error(errorCode); } } - else // ERROR_IO_PENDING + else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING { // Only once the IO is pending do we register for cancellation - completionSource.RegisterForCancellation(); + completionSource.RegisterForCancellation(cancellationToken); } } else @@ -1170,17 +1155,11 @@ namespace System.IO // synchronously or asynchronously. We absolutely must not // set asyncResult._numBytes here, since will never have correct // results. - //Console.WriteLine("WriteFile returned: "+r+" (0x"+Int32.Format(r, "x")+") The IO completed synchronously, but the user callback was called on another thread."); } return completionSource.Task; } - public override void WriteByte(byte value) - { - WriteByteCore(value); - } - // Windows API definitions, from winbase.h and others private const int FILE_ATTRIBUTE_NORMAL = 0x00000080; @@ -1201,35 +1180,19 @@ namespace System.IO private const int ERROR_IO_PENDING = 997; // __ConsoleStream also uses this code. - private unsafe int ReadFileNative(SafeFileHandle handle, byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int errorCode) + private unsafe int ReadFileNative(SafeFileHandle handle, Span bytes, NativeOverlapped* overlapped, out int errorCode) { Debug.Assert(handle != null, "handle != null"); - Debug.Assert(offset >= 0, "offset >= 0"); - Debug.Assert(count >= 0, "count >= 0"); - Debug.Assert(bytes != null, "bytes != null"); - // Don't corrupt memory when multiple threads are erroneously writing - // to this stream simultaneously. - if (bytes.Length - offset < count) - throw new IndexOutOfRangeException(SR.IndexOutOfRange_IORaceCondition); - Debug.Assert((_useAsyncIO && overlapped != null) || (!_useAsyncIO && overlapped == null), "Async IO and overlapped parameters inconsistent in call to ReadFileNative."); - // You can't use the fixed statement on an array of length 0. - if (bytes.Length == 0) - { - errorCode = 0; - return 0; - } - - int r = 0; + int r; int numBytesRead = 0; - fixed (byte* p = &bytes[0]) + fixed (byte* p = &MemoryMarshal.GetReference(bytes)) { - if (_useAsyncIO) - r = Interop.Kernel32.ReadFile(handle, p + offset, count, IntPtr.Zero, overlapped); - else - r = Interop.Kernel32.ReadFile(handle, p + offset, count, out numBytesRead, IntPtr.Zero); + r = _useAsyncIO ? + Interop.Kernel32.ReadFile(handle, p, bytes.Length, IntPtr.Zero, overlapped) : + Interop.Kernel32.ReadFile(handle, p, bytes.Length, out numBytesRead, IntPtr.Zero); } if (r == 0) @@ -1244,37 +1207,19 @@ namespace System.IO } } - private unsafe int WriteFileNative(SafeFileHandle handle, byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int errorCode) + private unsafe int WriteFileNative(SafeFileHandle handle, ReadOnlySpan buffer, NativeOverlapped* overlapped, out int errorCode) { Debug.Assert(handle != null, "handle != null"); - Debug.Assert(offset >= 0, "offset >= 0"); - Debug.Assert(count >= 0, "count >= 0"); - Debug.Assert(bytes != null, "bytes != null"); - // Don't corrupt memory when multiple threads are erroneously writing - // to this stream simultaneously. (the OS is reading from - // the array we pass to WriteFile, but if we read beyond the end and - // that memory isn't allocated, we could get an AV.) - if (bytes.Length - offset < count) - throw new IndexOutOfRangeException(SR.IndexOutOfRange_IORaceCondition); - Debug.Assert((_useAsyncIO && overlapped != null) || (!_useAsyncIO && overlapped == null), "Async IO and overlapped parameters inconsistent in call to WriteFileNative."); - // You can't use the fixed statement on an array of length 0. - if (bytes.Length == 0) - { - errorCode = 0; - return 0; - } - int numBytesWritten = 0; - int r = 0; + int r; - fixed (byte* p = &bytes[0]) + fixed (byte* p = &MemoryMarshal.GetReference(buffer)) { - if (_useAsyncIO) - r = Interop.Kernel32.WriteFile(handle, p + offset, count, IntPtr.Zero, overlapped); - else - r = Interop.Kernel32.WriteFile(handle, p + offset, count, out numBytesWritten, IntPtr.Zero); + r = _useAsyncIO ? + Interop.Kernel32.WriteFile(handle, p, buffer.Length, IntPtr.Zero, overlapped) : + Interop.Kernel32.WriteFile(handle, p, buffer.Length, out numBytesWritten, IntPtr.Zero); } if (r == 0) @@ -1450,7 +1395,7 @@ namespace System.IO } // Kick off the read. - synchronousSuccess = ReadFileNative(_fileHandle, copyBuffer, 0, copyBuffer.Length, readAwaitable._nativeOverlapped, out errorCode) >= 0; + synchronousSuccess = ReadFileNative(_fileHandle, copyBuffer, readAwaitable._nativeOverlapped, out errorCode) >= 0; } // If the operation did not synchronously succeed, it either failed or initiated the asynchronous operation. @@ -1545,7 +1490,7 @@ namespace System.IO // Make sure the stream's current position reflects where we ended up if (!_fileHandle.IsClosed && CanSeek) { - SeekCore(0, SeekOrigin.End); + SeekCore(_fileHandle, 0, SeekOrigin.End); } } } @@ -1674,20 +1619,6 @@ namespace System.IO } } - private Task TaskFromResultOrCache(int result) - { - Task completedTask = _lastSynchronouslyCompletedTask; - Debug.Assert(completedTask == null || completedTask.Status == TaskStatus.RanToCompletion, "Cached task should have completed successfully"); - - if ((completedTask == null) || (completedTask.Result != result)) - { - completedTask = Task.FromResult(result); - _lastSynchronouslyCompletedTask = completedTask; - } - - return completedTask; - } - private void LockInternal(long position, long length) { int positionLow = unchecked((int)(position)); @@ -1713,5 +1644,25 @@ namespace System.IO throw Win32Marshal.GetExceptionForLastWin32Error(); } } + private SafeFileHandle ValidateFileHandle(SafeFileHandle fileHandle) + { + if (fileHandle.IsInvalid) + { + // Return a meaningful exception with the full path. + + // NT5 oddity - when trying to open "C:\" as a Win32FileStream, + // we usually get ERROR_PATH_NOT_FOUND from the OS. We should + // probably be consistent w/ every other directory. + int errorCode = Marshal.GetLastWin32Error(); + + if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && _path.Length == PathInternal.GetRootLength(_path)) + errorCode = Interop.Errors.ERROR_ACCESS_DENIED; + + throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); + } + + fileHandle.IsAsync = _useAsyncIO; + return fileHandle; + } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStream.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStream.cs index 7db8518435..65c63bcc53 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStream.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStream.cs @@ -51,6 +51,9 @@ namespace System.IO /// private readonly bool _useAsyncIO; + /// cached task for read ops that complete synchronously + private Task _lastSynchronouslyCompletedTask = null; + /// /// Currently cached position in the stream. This should always mirror the underlying file's actual position, /// and should only ever be out of sync if another stream with access to this same file manipulates it, at which @@ -81,8 +84,33 @@ namespace System.IO [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. http://go.microsoft.com/fwlink/?linkid=14202")] public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync) - : this(new SafeFileHandle(handle, ownsHandle), access, bufferSize, isAsync) { + SafeFileHandle safeHandle = new SafeFileHandle(handle, ownsHandle: ownsHandle); + try + { + ValidateAndInitFromHandle(safeHandle, access, bufferSize, isAsync); + } + catch + { + // We don't want to take ownership of closing passed in handles + // *unless* the constructor completes successfully. + GC.SuppressFinalize(safeHandle); + + // This would also prevent Close from being called, but is unnecessary + // as we've removed the object from the finalizer queue. + // + // safeHandle.SetHandleAsInvalid(); + throw; + } + + // Note: Cleaner to set the following fields in ValidateAndInitFromHandle, + // but we can't as they're readonly. + _access = access; + _useAsyncIO = isAsync; + + // As the handle was passed in, we must set the handle field at the very end to + // avoid the finalizer closing the handle when we throw errors. + _fileHandle = safeHandle; } public FileStream(SafeFileHandle handle, FileAccess access) @@ -95,7 +123,7 @@ namespace System.IO { } - public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) + private void ValidateAndInitFromHandle(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) { if (handle.IsInvalid) throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle)); @@ -110,13 +138,24 @@ namespace System.IO if (handle.IsAsync.HasValue && isAsync != handle.IsAsync.Value) throw new ArgumentException(SR.Arg_HandleNotAsync, nameof(handle)); - _access = access; - _useAsyncIO = isAsync; _exposedHandle = true; _bufferLength = bufferSize; - _fileHandle = handle; - InitFromHandle(handle); + InitFromHandle(handle, access, isAsync); + } + + public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) + { + ValidateAndInitFromHandle(handle, access, bufferSize, isAsync); + + // Note: Cleaner to set the following fields in ValidateAndInitFromHandle, + // but we can't as they're readonly. + _access = access; + _useAsyncIO = isAsync; + + // As the handle was passed in, we must set the handle field at the very end to + // avoid the finalizer closing the handle when we throw errors. + _fileHandle = handle; } public FileStream(string path, FileMode mode) : @@ -256,6 +295,36 @@ namespace System.IO return FlushAsyncInternal(cancellationToken); } + public override int Read(byte[] array, int offset, int count) + { + ValidateReadWriteArgs(array, offset, count); + return _useAsyncIO ? + ReadAsyncTask(array, offset, count, CancellationToken.None).GetAwaiter().GetResult() : + ReadSpan(new Span(array, offset, count)); + } + + public override int Read(Span destination) + { + if (GetType() == typeof(FileStream) && !_useAsyncIO) + { + if (_fileHandle.IsClosed) + { + throw Error.GetFileNotOpen(); + } + return ReadSpan(destination); + } + else + { + // This type is derived from FileStream and/or the stream is in async mode. If this is a + // derived type, it may have overridden Read(byte[], int, int) prior to this Read(Span) + // overload being introduced. In that case, this Read(Span) overload should use the behavior + // of Read(byte[],int,int) overload. Or if the stream is in async mode, we can't call the + // synchronous ReadSpan, so we similarly call the base Read, which will turn delegate to + // Read(byte[],int,int), which will do the right thing if we're in async mode. + return base.Read(destination); + } + } + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { if (buffer == null) @@ -268,10 +337,12 @@ namespace System.IO throw new ArgumentException(SR.Argument_InvalidOffLen /*, no good single parameter name to pass*/); // If we have been inherited into a subclass, the following implementation could be incorrect - // since it does not call through to Read() or ReadAsync() which a subclass might have overridden. + // since it does not call through to Read() which a subclass might have overridden. // To be safe we will only use this implementation in cases where we know it is safe to do so, // and delegate to our base class (which will call into Read/ReadAsync) when we are not sure. - if (GetType() != typeof(FileStream)) + // Similarly, if we weren't opened for asynchronous I/O, call to the base implementation so that + // Read is invoked asynchronously. + if (GetType() != typeof(FileStream) || !_useAsyncIO) return base.ReadAsync(buffer, offset, count, cancellationToken); if (cancellationToken.IsCancellationRequested) @@ -280,7 +351,86 @@ namespace System.IO if (IsClosed) throw Error.GetFileNotOpen(); - return ReadAsyncInternal(buffer, offset, count, cancellationToken); + return ReadAsyncTask(buffer, offset, count, cancellationToken); + } + + public override ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default(CancellationToken)) + { + if (!_useAsyncIO || GetType() != typeof(FileStream)) + { + // If we're not using async I/O, delegate to the base, which will queue a call to Read. + // Or if this isn't a concrete FileStream, a derived type may have overridden ReadAsync(byte[],...), + // which was introduced first, so delegate to the base which will delegate to that. + return base.ReadAsync(destination, cancellationToken); + } + + if (cancellationToken.IsCancellationRequested) + { + return new ValueTask(Task.FromCanceled(cancellationToken)); + } + + if (IsClosed) + { + throw Error.GetFileNotOpen(); + } + + Task t = ReadAsyncInternal(destination, cancellationToken, out int synchronousResult); + return t != null ? + new ValueTask(t) : + new ValueTask(synchronousResult); + } + + private Task ReadAsyncTask(byte[] array, int offset, int count, CancellationToken cancellationToken) + { + Task t = ReadAsyncInternal(new Memory(array, offset, count), cancellationToken, out int synchronousResult); + + if (t == null) + { + t = _lastSynchronouslyCompletedTask; + Debug.Assert(t == null || t.IsCompletedSuccessfully, "Cached task should have completed successfully"); + + if (t == null || t.Result != synchronousResult) + { + _lastSynchronouslyCompletedTask = t = Task.FromResult(synchronousResult); + } + } + + return t; + } + + public override void Write(byte[] array, int offset, int count) + { + ValidateReadWriteArgs(array, offset, count); + if (_useAsyncIO) + { + WriteAsyncInternal(new ReadOnlyMemory(array, offset, count), CancellationToken.None).GetAwaiter().GetResult(); + } + else + { + WriteSpan(new ReadOnlySpan(array, offset, count)); + } + } + + public override void Write(ReadOnlySpan destination) + { + if (GetType() == typeof(FileStream) && !_useAsyncIO) + { + if (_fileHandle.IsClosed) + { + throw Error.GetFileNotOpen(); + } + WriteSpan(destination); + } + else + { + // This type is derived from FileStream and/or the stream is in async mode. If this is a + // derived type, it may have overridden Write(byte[], int, int) prior to this Write(ReadOnlySpan) + // overload being introduced. In that case, this Write(ReadOnlySpan) overload should use the behavior + // of Write(byte[],int,int) overload. Or if the stream is in async mode, we can't call the + // synchronous WriteSpan, so we similarly call the base Write, which will turn delegate to + // Write(byte[],int,int), which will do the right thing if we're in async mode. + base.Write(destination); + } } public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) @@ -298,7 +448,7 @@ namespace System.IO // since it does not call through to Write() or WriteAsync() which a subclass might have overridden. // To be safe we will only use this implementation in cases where we know it is safe to do so, // and delegate to our base class (which will call into Write/WriteAsync) when we are not sure. - if (GetType() != typeof(FileStream)) + if (!_useAsyncIO || GetType() != typeof(FileStream)) return base.WriteAsync(buffer, offset, count, cancellationToken); if (cancellationToken.IsCancellationRequested) @@ -307,7 +457,30 @@ namespace System.IO if (IsClosed) throw Error.GetFileNotOpen(); - return WriteAsyncInternal(buffer, offset, count, cancellationToken); + return WriteAsyncInternal(new ReadOnlyMemory(buffer, offset, count), cancellationToken); + } + + public override Task WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken = default(CancellationToken)) + { + if (!_useAsyncIO || GetType() != typeof(FileStream)) + { + // If we're not using async I/O, delegate to the base, which will queue a call to Write. + // Or if this isn't a concrete FileStream, a derived type may have overridden WriteAsync(byte[],...), + // which was introduced first, so delegate to the base which will delegate to that. + return base.WriteAsync(source, cancellationToken); + } + + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + if (IsClosed) + { + throw Error.GetFileNotOpen(); + } + + return WriteAsyncInternal(source, cancellationToken); } /// @@ -425,7 +598,7 @@ namespace System.IO if (verifyPosition && CanSeek) { long oldPos = _filePosition; // SeekCore will override the current _position, so save it now - long curPos = SeekCore(0, SeekOrigin.Current); + long curPos = SeekCore(_fileHandle, 0, SeekOrigin.Current); if (oldPos != curPos) { // For reads, this is non-fatal but we still could have returned corrupted @@ -553,12 +726,16 @@ namespace System.IO if (rewind != 0) { Debug.Assert(CanSeek, "FileStream will lose buffered read data now."); - SeekCore(rewind, SeekOrigin.Current); + SeekCore(_fileHandle, rewind, SeekOrigin.Current); } _readPos = _readLength = 0; } - private int ReadByteCore() + /// + /// Reads a byte from the file stream. Returns the byte cast to an int + /// or -1 if reading from the end of the stream. + /// + public override int ReadByte() { PrepareForReading(); @@ -566,9 +743,7 @@ namespace System.IO if (_readPos == _readLength) { FlushWriteBuffer(); - Debug.Assert(_bufferLength > 0, "_bufferSize > 0"); - - _readLength = ReadNative(buffer, 0, _bufferLength); + _readLength = FillReadBufferForReadByte(); _readPos = 0; if (_readLength == 0) { @@ -579,13 +754,18 @@ namespace System.IO return buffer[_readPos++]; } - private void WriteByteCore(byte value) + /// + /// Writes a byte to the current position in the stream and advances the position + /// within the stream by one byte. + /// + /// The byte to write to the stream. + public override void WriteByte(byte value) { PrepareForWriting(); // Flush the write buffer if it's full if (_writePos == _bufferLength) - FlushWriteBuffer(); + FlushWriteBufferForWriteByte(); // We now have space in the buffer. Store the byte. GetBuffer()[_writePos++] = value; @@ -636,7 +816,7 @@ namespace System.IO if (!IsAsync) return base.BeginRead(array, offset, numBytes, callback, state); else - return TaskToApm.Begin(ReadAsyncInternal(array, offset, numBytes, CancellationToken.None), callback, state); + return TaskToApm.Begin(ReadAsyncTask(array, offset, numBytes, CancellationToken.None), callback, state); } public override IAsyncResult BeginWrite(byte[] array, int offset, int numBytes, AsyncCallback callback, object state) @@ -656,7 +836,7 @@ namespace System.IO if (!IsAsync) return base.BeginWrite(array, offset, numBytes, callback, state); else - return TaskToApm.Begin(WriteAsyncInternal(array, offset, numBytes, CancellationToken.None), callback, state); + return TaskToApm.Begin(WriteAsyncInternal(new ReadOnlyMemory(array, offset, numBytes), CancellationToken.None), callback, state); } public override int EndRead(IAsyncResult asyncResult) diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStreamCompletionSource.Win32.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStreamCompletionSource.Win32.cs index 7dca13335e..4e19f465bd 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStreamCompletionSource.Win32.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/FileStreamCompletionSource.Win32.cs @@ -2,11 +2,11 @@ // 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.Security; +using System.Buffers; +using System.Diagnostics; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using System.Runtime.InteropServices; -using System.Diagnostics; namespace System.IO { @@ -15,7 +15,7 @@ namespace System.IO // This is an internal object extending TaskCompletionSource with fields // for all of the relevant data necessary to complete the IO operation. // This is used by IOCallback and all of the async methods. - unsafe private sealed class FileStreamCompletionSource : TaskCompletionSource + private unsafe class FileStreamCompletionSource : TaskCompletionSource { private const long NoResult = 0; private const long ResultSuccess = (long)1 << 32; @@ -28,7 +28,6 @@ namespace System.IO private readonly FileStream _stream; private readonly int _numBufferedBytes; - private readonly CancellationToken _cancellationToken; private CancellationTokenRegistration _cancellationRegistration; #if DEBUG private bool _cancellationHasBeenRegistered; @@ -37,20 +36,19 @@ namespace System.IO private long _result; // Using long since this needs to be used in Interlocked APIs // Using RunContinuationsAsynchronously for compat reasons (old API used Task.Factory.StartNew for continuations) - internal FileStreamCompletionSource(FileStream stream, int numBufferedBytes, byte[] bytes, CancellationToken cancellationToken) + internal FileStreamCompletionSource(FileStream stream, int numBufferedBytes, byte[] bytes) : base(TaskCreationOptions.RunContinuationsAsynchronously) { _numBufferedBytes = numBufferedBytes; _stream = stream; _result = NoResult; - _cancellationToken = cancellationToken; - // Create the native overlapped. We try to use the preallocated overlapped if possible: - // it's possible if the byte buffer is the same one that's associated with the preallocated overlapped - // and if no one else is currently using the preallocated overlapped. This is the fast-path for cases - // where the user-provided buffer is smaller than the FileStream's buffer (such that the FileStream's + // Create the native overlapped. We try to use the preallocated overlapped if possible: it's possible if the byte + // buffer is null (there's nothing to pin) or the same one that's associated with the preallocated overlapped (and + // thus is already pinned) and if no one else is currently using the preallocated overlapped. This is the fast-path + // for cases where the user-provided buffer is smaller than the FileStream's buffer (such that the FileStream's // buffer is used) and where operations on the FileStream are not being performed concurrently. - _overlapped = ReferenceEquals(bytes, _stream._buffer) && _stream.CompareExchangeCurrentOverlappedOwner(this, null) == null ? + _overlapped = (bytes == null || ReferenceEquals(bytes, _stream._buffer)) && _stream.CompareExchangeCurrentOverlappedOwner(this, null) == null ? _stream._fileHandle.ThreadPoolBinding.AllocateNativeOverlapped(_stream._preallocatedOverlapped) : _stream._fileHandle.ThreadPoolBinding.AllocateNativeOverlapped(s_ioCallback, this, bytes); Debug.Assert(_overlapped != null, "AllocateNativeOverlapped returned null"); @@ -67,15 +65,16 @@ namespace System.IO TrySetResult(numBytes + _numBufferedBytes); } - public void RegisterForCancellation() + public void RegisterForCancellation(CancellationToken cancellationToken) { #if DEBUG + Debug.Assert(cancellationToken.CanBeCanceled); Debug.Assert(!_cancellationHasBeenRegistered, "Cannot register for cancellation twice"); _cancellationHasBeenRegistered = true; #endif - // Quick check to make sure that the cancellation token supports cancellation, and that the IO hasn't completed - if ((_cancellationToken.CanBeCanceled) && (_overlapped != null)) + // Quick check to make sure the IO hasn't completed + if (_overlapped != null) { var cancelCallback = s_cancelCallback; if (cancelCallback == null) s_cancelCallback = cancelCallback = Cancel; @@ -84,7 +83,7 @@ namespace System.IO long packedResult = Interlocked.CompareExchange(ref _result, RegisteringCancellation, NoResult); if (packedResult == NoResult) { - _cancellationRegistration = _cancellationToken.Register(cancelCallback, this); + _cancellationRegistration = cancellationToken.Register(cancelCallback, this); // Switch the result, just in case IO completed while we were setting the registration packedResult = Interlocked.Exchange(ref _result, NoResult); @@ -104,7 +103,7 @@ namespace System.IO } } - internal void ReleaseNativeResource() + internal virtual void ReleaseNativeResource() { // Ensure that cancellation has been completed and cleaned up. _cancellationRegistration.Dispose(); @@ -172,6 +171,7 @@ namespace System.IO private void CompleteCallback(ulong packedResult) { // Free up the native resource and cancellation registration + CancellationToken cancellationToken = _cancellationRegistration.Token; // access before disposing registration ReleaseNativeResource(); // Unpack the result and send it to the user @@ -181,7 +181,7 @@ namespace System.IO int errorCode = unchecked((int)(packedResult & uint.MaxValue)); if (errorCode == Interop.Errors.ERROR_OPERATION_ABORTED) { - TrySetCanceled(_cancellationToken.IsCancellationRequested ? _cancellationToken : new CancellationToken(true)); + TrySetCanceled(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)); } else { @@ -218,5 +218,28 @@ namespace System.IO } } } + + /// + /// Extends with to support disposing of a + /// when the operation has completed. This should only be used + /// when memory doesn't wrap a byte[]. + /// + private sealed class MemoryFileStreamCompletionSource : FileStreamCompletionSource + { + private MemoryHandle _handle; // mutable struct; do not make this readonly + + internal MemoryFileStreamCompletionSource(FileStream stream, int numBufferedBytes, ReadOnlyMemory memory) : + base(stream, numBufferedBytes, bytes: null) // this type handles the pinning, so null is passed for bytes + { + Debug.Assert(!MemoryMarshal.TryGetArray(memory, out ArraySegment array), "The base should be used directly if we can get the array."); + _handle = memory.Retain(pin: true); + } + + internal override void ReleaseNativeResource() + { + _handle.Dispose(); + base.ReleaseNativeResource(); + } + } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/IOException.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/IOException.cs new file mode 100644 index 0000000000..89b25d5142 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/IOException.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. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.Serialization; + +namespace System.IO +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class IOException : SystemException + { + public IOException() + : base(SR.Arg_IOException) + { + HResult = HResults.COR_E_IO; + } + + public IOException(String message) + : base(message) + { + HResult = HResults.COR_E_IO; + } + + public IOException(String message, int hresult) + : base(message) + { + HResult = hresult; + } + + public IOException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_IO; + } + + protected IOException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/MemoryStream.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/MemoryStream.cs new file mode 100644 index 0000000000..c5e5ea918b --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/MemoryStream.cs @@ -0,0 +1,823 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace System.IO +{ + // A MemoryStream represents a Stream in memory (ie, it has no backing store). + // This stream may reduce the need for temporary buffers and files in + // an application. + // + // There are two ways to create a MemoryStream. You can initialize one + // from an unsigned byte array, or you can create an empty one. Empty + // memory streams are resizable, while ones created with a byte array provide + // a stream "view" of the data. + public class MemoryStream : Stream + { + private byte[] _buffer; // Either allocated internally or externally. + private int _origin; // For user-provided arrays, start at this origin + private int _position; // read/write head. + private int _length; // Number of bytes within the memory stream + private int _capacity; // length of usable portion of buffer for stream + // Note that _capacity == _buffer.Length for non-user-provided byte[]'s + + private bool _expandable; // User-provided buffers aren't expandable. + private bool _writable; // Can user write to this stream? + private bool _exposable; // Whether the array can be returned to the user. + private bool _isOpen; // Is this stream open or closed? + + private Task _lastReadTask; // The last successful task returned from ReadAsync + + private const int MemStreamMaxLength = int.MaxValue; + + public MemoryStream() + : this(0) + { + } + + public MemoryStream(int capacity) + { + if (capacity < 0) + throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_NegativeCapacity); + + _buffer = capacity != 0 ? new byte[capacity] : Array.Empty(); + _capacity = capacity; + _expandable = true; + _writable = true; + _exposable = true; + _origin = 0; // Must be 0 for byte[]'s created by MemoryStream + _isOpen = true; + } + + public MemoryStream(byte[] buffer) + : this(buffer, true) + { + } + + public MemoryStream(byte[] buffer, bool writable) + { + if (buffer == null) + throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); + + _buffer = buffer; + _length = _capacity = buffer.Length; + _writable = writable; + _exposable = false; + _origin = 0; + _isOpen = true; + } + + public MemoryStream(byte[] buffer, int index, int count) + : this(buffer, index, count, true, false) + { + } + + public MemoryStream(byte[] buffer, int index, int count, bool writable) + : this(buffer, index, count, writable, false) + { + } + + public MemoryStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible) + { + if (buffer == null) + throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + if (buffer.Length - index < count) + throw new ArgumentException(SR.Argument_InvalidOffLen); + + _buffer = buffer; + _origin = _position = index; + _length = _capacity = index + count; + _writable = writable; + _exposable = publiclyVisible; // Can TryGetBuffer/GetBuffer return the array? + _expandable = false; + _isOpen = true; + } + + public override bool CanRead => _isOpen; + + public override bool CanSeek => _isOpen; + + public override bool CanWrite => _writable; + + private void EnsureNotClosed() + { + if (!_isOpen) + throw Error.GetStreamIsClosed(); + } + + private void EnsureWriteable() + { + if (!CanWrite) + throw Error.GetWriteNotSupported(); + } + + protected override void Dispose(bool disposing) + { + try + { + if (disposing) + { + _isOpen = false; + _writable = false; + _expandable = false; + // Don't set buffer to null - allow TryGetBuffer, GetBuffer & ToArray to work. + _lastReadTask = null; + } + } + finally + { + // Call base.Close() to cleanup async IO resources + base.Dispose(disposing); + } + } + + // returns a bool saying whether we allocated a new array. + private bool EnsureCapacity(int value) + { + // Check for overflow + if (value < 0) + throw new IOException(SR.IO_StreamTooLong); + + if (value > _capacity) + { + int newCapacity = value; + if (newCapacity < 256) + { + newCapacity = 256; + } + + // We are ok with this overflowing since the next statement will deal + // with the cases where _capacity*2 overflows. + if (newCapacity < _capacity * 2) + { + newCapacity = _capacity * 2; + } + + // We want to expand the array up to Array.MaxByteArrayLength + // And we want to give the user the value that they asked for + if ((uint)(_capacity * 2) > Array.MaxByteArrayLength) + { + newCapacity = value > Array.MaxByteArrayLength ? value : Array.MaxByteArrayLength; + } + + Capacity = newCapacity; + return true; + } + return false; + } + + public override void Flush() + { + } + + public override Task FlushAsync(CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + + try + { + Flush(); + return Task.CompletedTask; + } + catch (Exception ex) + { + return Task.FromException(ex); + } + } + + + public virtual byte[] GetBuffer() + { + if (!_exposable) + throw new UnauthorizedAccessException(SR.UnauthorizedAccess_MemStreamBuffer); + return _buffer; + } + + public virtual bool TryGetBuffer(out ArraySegment buffer) + { + if (!_exposable) + { + buffer = default(ArraySegment); + return false; + } + + buffer = new ArraySegment(_buffer, offset: _origin, count: (_length - _origin)); + return true; + } + + // -------------- PERF: Internal functions for fast direct access of MemoryStream buffer (cf. BinaryReader for usage) --------------- + + // PERF: Internal sibling of GetBuffer, always returns a buffer (cf. GetBuffer()) + internal byte[] InternalGetBuffer() + { + return _buffer; + } + + // PERF: True cursor position, we don't need _origin for direct access + internal int InternalGetPosition() + { + return _position; + } + + // PERF: Takes out Int32 as fast as possible + internal int InternalReadInt32() + { + EnsureNotClosed(); + + int pos = (_position += 4); // use temp to avoid a race condition + if (pos > _length) + { + _position = _length; + throw Error.GetEndOfFile(); + } + return (int)(_buffer[pos - 4] | _buffer[pos - 3] << 8 | _buffer[pos - 2] << 16 | _buffer[pos - 1] << 24); + } + + // PERF: Get actual length of bytes available for read; do sanity checks; shift position - i.e. everything except actual copying bytes + internal int InternalEmulateRead(int count) + { + EnsureNotClosed(); + + int n = _length - _position; + if (n > count) + n = count; + if (n < 0) + n = 0; + + Debug.Assert(_position + n >= 0, "_position + n >= 0"); // len is less than 2^31 -1. + _position += n; + return n; + } + + // Gets & sets the capacity (number of bytes allocated) for this stream. + // The capacity cannot be set to a value less than the current length + // of the stream. + // + public virtual int Capacity + { + get + { + EnsureNotClosed(); + return _capacity - _origin; + } + set + { + // Only update the capacity if the MS is expandable and the value is different than the current capacity. + // Special behavior if the MS isn't expandable: we don't throw if value is the same as the current capacity + if (value < Length) + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_SmallCapacity); + + EnsureNotClosed(); + + if (!_expandable && (value != Capacity)) + throw new NotSupportedException(SR.NotSupported_MemStreamNotExpandable); + + // MemoryStream has this invariant: _origin > 0 => !expandable (see ctors) + if (_expandable && value != _capacity) + { + if (value > 0) + { + byte[] newBuffer = new byte[value]; + if (_length > 0) + { + Buffer.BlockCopy(_buffer, 0, newBuffer, 0, _length); + } + _buffer = newBuffer; + } + else + { + _buffer = null; + } + _capacity = value; + } + } + } + + public override long Length + { + get + { + EnsureNotClosed(); + return _length - _origin; + } + } + + public override long Position + { + get + { + EnsureNotClosed(); + return _position - _origin; + } + set + { + if (value < 0) + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); + + EnsureNotClosed(); + + if (value > MemStreamMaxLength) + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_StreamLength); + _position = _origin + (int)value; + } + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (buffer == null) + throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + if (buffer.Length - offset < count) + throw new ArgumentException(SR.Argument_InvalidOffLen); + + EnsureNotClosed(); + + int n = _length - _position; + if (n > count) + n = count; + if (n <= 0) + return 0; + + Debug.Assert(_position + n >= 0, "_position + n >= 0"); // len is less than 2^31 -1. + + if (n <= 8) + { + int byteCount = n; + while (--byteCount >= 0) + buffer[offset + byteCount] = _buffer[_position + byteCount]; + } + else + Buffer.BlockCopy(_buffer, _position, buffer, offset, n); + _position += n; + + return n; + } + + public override int Read(Span destination) + { + if (GetType() != typeof(MemoryStream)) + { + // MemoryStream is not sealed, and a derived type may have overridden Read(byte[], int, int) prior + // to this Read(Span) overload being introduced. In that case, this Read(Span) overload + // should use the behavior of Read(byte[],int,int) overload. + return base.Read(destination); + } + + EnsureNotClosed(); + + int n = Math.Min(_length - _position, destination.Length); + if (n <= 0) + return 0; + + // TODO https://github.com/dotnet/coreclr/issues/15076: + // Read(byte[], int, int) has an n <= 8 optimization, presumably based + // on benchmarking. Determine if/where such a cut-off is here and add + // an equivalent optimization if necessary. + new Span(_buffer, _position, n).CopyTo(destination); + + _position += n; + return n; + } + + public override Task ReadAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + if (buffer == null) + throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + if (buffer.Length - offset < count) + throw new ArgumentException(SR.Argument_InvalidOffLen); + + // If cancellation was requested, bail early + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + + try + { + int n = Read(buffer, offset, count); + var t = _lastReadTask; + Debug.Assert(t == null || t.Status == TaskStatus.RanToCompletion, + "Expected that a stored last task completed successfully"); + return (t != null && t.Result == n) ? t : (_lastReadTask = Task.FromResult(n)); + } + catch (OperationCanceledException oce) + { + return Task.FromCancellation(oce); + } + catch (Exception exception) + { + return Task.FromException(exception); + } + } + + public override ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default(CancellationToken)) + { + if (cancellationToken.IsCancellationRequested) + { + return new ValueTask(Task.FromCanceled(cancellationToken)); + } + + try + { + // ReadAsync(Memory,...) needs to delegate to an existing virtual to do the work, in case an existing derived type + // has changed or augmented the logic associated with reads. If the Memory wraps an array, we could delegate to + // ReadAsync(byte[], ...), but that would defeat part of the purpose, as ReadAsync(byte[], ...) often needs to allocate + // a Task for the return value, so we want to delegate to one of the synchronous methods. We could always + // delegate to the Read(Span) method, and that's the most efficient solution when dealing with a concrete + // MemoryStream, but if we're dealing with a type derived from MemoryStream, Read(Span) will end up delegating + // to Read(byte[], ...), which requires it to get a byte[] from ArrayPool and copy the data. So, we special-case the + // very common case of the Memory wrapping an array: if it does, we delegate to Read(byte[], ...) with it, + // as that will be efficient in both cases, and we fall back to Read(Span) if the Memory wrapped something + // else; if this is a concrete MemoryStream, that'll be efficient, and only in the case where the Memory wrapped + // something other than an array and this is a MemoryStream-derived type that doesn't override Read(Span) will + // it then fall back to doing the ArrayPool/copy behavior. + return new ValueTask( + destination.TryGetArray(out ArraySegment destinationArray) ? + Read(destinationArray.Array, destinationArray.Offset, destinationArray.Count) : + Read(destination.Span)); + } + catch (OperationCanceledException oce) + { + return new ValueTask(Task.FromCancellation(oce)); + } + catch (Exception exception) + { + return new ValueTask(Task.FromException(exception)); + } + } + + public override int ReadByte() + { + EnsureNotClosed(); + + if (_position >= _length) + return -1; + + return _buffer[_position++]; + } + + public override void CopyTo(Stream destination, int bufferSize) + { + // Since we did not originally override this method, validate the arguments + // the same way Stream does for back-compat. + StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize); + + // If we have been inherited into a subclass, the following implementation could be incorrect + // since it does not call through to Read() which a subclass might have overridden. + // To be safe we will only use this implementation in cases where we know it is safe to do so, + // and delegate to our base class (which will call into Read) when we are not sure. + if (GetType() != typeof(MemoryStream)) + { + base.CopyTo(destination, bufferSize); + return; + } + + int originalPosition = _position; + + // Seek to the end of the MemoryStream. + int remaining = InternalEmulateRead(_length - originalPosition); + + // If we were already at or past the end, there's no copying to do so just quit. + if (remaining > 0) + { + // Call Write() on the other Stream, using our internal buffer and avoiding any + // intermediary allocations. + destination.Write(_buffer, originalPosition, remaining); + } + } + + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + // This implementation offers better performance compared to the base class version. + + StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize); + + // If we have been inherited into a subclass, the following implementation could be incorrect + // since it does not call through to ReadAsync() which a subclass might have overridden. + // To be safe we will only use this implementation in cases where we know it is safe to do so, + // and delegate to our base class (which will call into ReadAsync) when we are not sure. + if (GetType() != typeof(MemoryStream)) + return base.CopyToAsync(destination, bufferSize, cancellationToken); + + // If cancelled - return fast: + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + + // Avoid copying data from this buffer into a temp buffer: + // (require that InternalEmulateRead does not throw, + // otherwise it needs to be wrapped into try-catch-Task.FromException like memStrDest.Write below) + + int pos = _position; + int n = InternalEmulateRead(_length - _position); + + // If we were already at or past the end, there's no copying to do so just quit. + if (n == 0) + return Task.CompletedTask; + + // If destination is not a memory stream, write there asynchronously: + MemoryStream memStrDest = destination as MemoryStream; + if (memStrDest == null) + return destination.WriteAsync(_buffer, pos, n, cancellationToken); + + try + { + // If destination is a MemoryStream, CopyTo synchronously: + memStrDest.Write(_buffer, pos, n); + return Task.CompletedTask; + } + catch (Exception ex) + { + return Task.FromException(ex); + } + } + + + public override long Seek(long offset, SeekOrigin loc) + { + EnsureNotClosed(); + + if (offset > MemStreamMaxLength) + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_StreamLength); + + switch (loc) + { + case SeekOrigin.Begin: + { + int tempPosition = unchecked(_origin + (int)offset); + if (offset < 0 || tempPosition < _origin) + throw new IOException(SR.IO_SeekBeforeBegin); + _position = tempPosition; + break; + } + case SeekOrigin.Current: + { + int tempPosition = unchecked(_position + (int)offset); + if (unchecked(_position + offset) < _origin || tempPosition < _origin) + throw new IOException(SR.IO_SeekBeforeBegin); + _position = tempPosition; + break; + } + case SeekOrigin.End: + { + int tempPosition = unchecked(_length + (int)offset); + if (unchecked(_length + offset) < _origin || tempPosition < _origin) + throw new IOException(SR.IO_SeekBeforeBegin); + _position = tempPosition; + break; + } + default: + throw new ArgumentException(SR.Argument_InvalidSeekOrigin); + } + + Debug.Assert(_position >= 0, "_position >= 0"); + return _position; + } + + // Sets the length of the stream to a given value. The new + // value must be nonnegative and less than the space remaining in + // the array, int.MaxValue - origin + // Origin is 0 in all cases other than a MemoryStream created on + // top of an existing array and a specific starting offset was passed + // into the MemoryStream constructor. The upper bounds prevents any + // situations where a stream may be created on top of an array then + // the stream is made longer than the maximum possible length of the + // array (int.MaxValue). + // + public override void SetLength(long value) + { + if (value < 0 || value > int.MaxValue) + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_StreamLength); + + EnsureWriteable(); + + // Origin wasn't publicly exposed above. + Debug.Assert(MemStreamMaxLength == int.MaxValue); // Check parameter validation logic in this method if this fails. + if (value > (int.MaxValue - _origin)) + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_StreamLength); + + int newLength = _origin + (int)value; + bool allocatedNewArray = EnsureCapacity(newLength); + if (!allocatedNewArray && newLength > _length) + Array.Clear(_buffer, _length, newLength - _length); + _length = newLength; + if (_position > newLength) + _position = newLength; + } + + public virtual byte[] ToArray() + { + int count = _length - _origin; + if (count == 0) + return Array.Empty(); + byte[] copy = new byte[count]; + Buffer.BlockCopy(_buffer, _origin, copy, 0, count); + return copy; + } + + public override void Write(byte[] buffer, int offset, int count) + { + if (buffer == null) + throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + if (buffer.Length - offset < count) + throw new ArgumentException(SR.Argument_InvalidOffLen); + + EnsureNotClosed(); + EnsureWriteable(); + + int i = _position + count; + // Check for overflow + if (i < 0) + throw new IOException(SR.IO_StreamTooLong); + + if (i > _length) + { + bool mustZero = _position > _length; + if (i > _capacity) + { + bool allocatedNewArray = EnsureCapacity(i); + if (allocatedNewArray) + { + mustZero = false; + } + } + if (mustZero) + { + Array.Clear(_buffer, _length, i - _length); + } + _length = i; + } + if ((count <= 8) && (buffer != _buffer)) + { + int byteCount = count; + while (--byteCount >= 0) + { + _buffer[_position + byteCount] = buffer[offset + byteCount]; + } + } + else + { + Buffer.BlockCopy(buffer, offset, _buffer, _position, count); + } + _position = i; + } + + public override void Write(ReadOnlySpan source) + { + if (GetType() != typeof(MemoryStream)) + { + // MemoryStream is not sealed, and a derived type may have overridden Write(byte[], int, int) prior + // to this Write(Span) overload being introduced. In that case, this Write(Span) overload + // should use the behavior of Write(byte[],int,int) overload. + base.Write(source); + return; + } + + EnsureNotClosed(); + EnsureWriteable(); + + // Check for overflow + int i = _position + source.Length; + if (i < 0) + throw new IOException(SR.IO_StreamTooLong); + + if (i > _length) + { + bool mustZero = _position > _length; + if (i > _capacity) + { + bool allocatedNewArray = EnsureCapacity(i); + if (allocatedNewArray) + { + mustZero = false; + } + } + if (mustZero) + { + Array.Clear(_buffer, _length, i - _length); + } + _length = i; + } + + source.CopyTo(new Span(_buffer, _position, source.Length)); + _position = i; + } + + public override Task WriteAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + if (buffer == null) + throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + if (buffer.Length - offset < count) + throw new ArgumentException(SR.Argument_InvalidOffLen); + + // If cancellation is already requested, bail early + if (cancellationToken.IsCancellationRequested) + return Task.FromCanceled(cancellationToken); + + try + { + Write(buffer, offset, count); + return Task.CompletedTask; + } + catch (OperationCanceledException oce) + { + return Task.FromCancellation(oce); + } + catch (Exception exception) + { + return Task.FromException(exception); + } + } + + public override Task WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken = default(CancellationToken)) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + try + { + // See corresponding comment in ReadAsync for why we don't just always use Write(ReadOnlySpan). + // Unlike ReadAsync, we could delegate to WriteAsync(byte[], ...) here, but we don't for consistency. + if (MemoryMarshal.TryGetArray(source, out ArraySegment sourceArray)) + { + Write(sourceArray.Array, sourceArray.Offset, sourceArray.Count); + } + else + { + Write(source.Span); + } + return Task.CompletedTask; + } + catch (OperationCanceledException oce) + { + return Task.FromCancellation(oce); + } + catch (Exception exception) + { + return Task.FromException(exception); + } + } + + public override void WriteByte(byte value) + { + EnsureNotClosed(); + EnsureWriteable(); + + if (_position >= _length) + { + int newLength = _position + 1; + bool mustZero = _position > _length; + if (newLength >= _capacity) + { + bool allocatedNewArray = EnsureCapacity(newLength); + if (allocatedNewArray) + { + mustZero = false; + } + } + if (mustZero) + { + Array.Clear(_buffer, _length, _position - _length); + } + _length = newLength; + } + _buffer[_position++] = value; + } + + // Writes this MemoryStream to another stream. + public virtual void WriteTo(Stream stream) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream), SR.ArgumentNull_Stream); + + EnsureNotClosed(); + + stream.Write(_buffer, _origin, _length - _origin); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/Path.Unix.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/Path.Unix.cs index 68c5f70036..1143c05208 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/Path.Unix.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/Path.Unix.cs @@ -14,8 +14,6 @@ namespace System.IO public static char[] GetInvalidPathChars() => new char[] { '\0' }; - internal static int MaxPath => Interop.Sys.MaxPath; - // Expands the given path to a fully qualified path. public static string GetFullPath(string path) { @@ -23,10 +21,11 @@ namespace System.IO throw new ArgumentNullException(nameof(path)); if (path.Length == 0) - throw new ArgumentException(SR.Arg_PathIllegal, nameof(path)); - - PathInternal.CheckInvalidPathChars(path); + throw new ArgumentException(SR.Arg_PathEmpty, nameof(path)); + if (path.IndexOf('\0') != -1) + throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path)); + // Expand with current directory if necessary if (!IsPathRooted(path)) { @@ -40,11 +39,6 @@ namespace System.IO Debug.Assert(collapsedString.Length < path.Length || collapsedString.ToString() == path, "Either we've removed characters, or the string should be unmodified from the input path."); - if (collapsedString.Length > Interop.Sys.MaxPath) - { - throw new PathTooLongException(SR.IO_PathTooLong); - } - string result = collapsedString.Length == 0 ? PathInternal.DirectorySeparatorCharAsString : collapsedString; return result; @@ -67,15 +61,12 @@ namespace System.IO sb.Append(path, 0, skip); } - int componentCharCount = 0; for (int i = skip; i < path.Length; i++) { char c = path[i]; if (PathInternal.IsDirectorySeparator(c) && i + 1 < path.Length) { - componentCharCount = 0; - // Skip this character if it's a directory separator and if the next character is, too, // e.g. "parent//child" => "parent/child" if (PathInternal.IsDirectorySeparator(path[i + 1])) @@ -118,11 +109,6 @@ namespace System.IO } } - if (++componentCharCount > Interop.Sys.MaxName) - { - throw new PathTooLongException(SR.IO_PathTooLong); - } - // Normalize the directory separator if needed if (c != PathInternal.DirectorySeparatorChar && c == PathInternal.AltDirectorySeparatorChar) { @@ -189,7 +175,6 @@ namespace System.IO if (path == null) return false; - PathInternal.CheckInvalidPathChars(path); return path.Length > 0 && path[0] == PathInternal.DirectorySeparatorChar; } @@ -198,10 +183,10 @@ namespace System.IO public static string GetPathRoot(string path) { if (path == null) return null; - if (string.IsNullOrWhiteSpace(path)) - throw new ArgumentException(SR.Arg_PathIllegal, nameof(path)); + if (PathInternal.IsEffectivelyEmpty(path)) + throw new ArgumentException(SR.Arg_PathEmpty, nameof(path)); - return IsPathRooted(path) ? PathInternal.DirectorySeparatorCharAsString : String.Empty; + return IsPathRooted(path) ? PathInternal.DirectorySeparatorCharAsString : String.Empty; } /// Gets whether the system is case-sensitive. diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/Path.Windows.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/Path.Windows.cs index 1e573cd95d..5d92d3b490 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/Path.Windows.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/Path.Windows.cs @@ -70,13 +70,13 @@ namespace System.IO || (path.Length >= startIndex && path[startIndex - 1] == PathInternal.VolumeSeparatorChar && !PathInternal.IsValidDriveChar(path[startIndex - 2])) || (path.Length > startIndex && path.IndexOf(PathInternal.VolumeSeparatorChar, startIndex) != -1)) { - throw new NotSupportedException(SR.Argument_PathFormatNotSupported); + throw new NotSupportedException(SR.Format(SR.Argument_PathFormatNotSupported_Path, path)); } } // Technically this doesn't matter but we used to throw for this case - if (string.IsNullOrWhiteSpace(path)) - throw new ArgumentException(SR.Arg_PathIllegal); + if (PathInternal.IsEffectivelyEmpty(path)) + throw new ArgumentException(SR.Arg_PathEmpty, nameof(path)); // We don't want to check invalid characters for device format- see comments for extended above string fullPath = PathHelper.Normalize(path, checkInvalidCharacters: !isDevice, expandShortPaths: true); @@ -119,8 +119,6 @@ namespace System.IO { if (path != null) { - PathInternal.CheckInvalidPathChars(path); - int length = path.Length; if ((length >= 1 && PathInternal.IsDirectorySeparator(path[0])) || (length >= 2 && PathInternal.IsValidDriveChar(path[0]) && path[1] == PathInternal.VolumeSeparatorChar)) @@ -141,10 +139,8 @@ namespace System.IO public static string GetPathRoot(string path) { if (path == null) return null; - if (string.IsNullOrWhiteSpace(path)) - throw new ArgumentException(SR.Arg_PathIllegal, nameof(path)); - - PathInternal.CheckInvalidPathChars(path); + if (PathInternal.IsEffectivelyEmpty(path)) + throw new ArgumentException(SR.Arg_PathEmpty, nameof(path)); // Need to return the normalized directory separator path = PathInternal.NormalizeDirectorySeparators(path); diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/Path.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/Path.cs index 9feb2873d8..9f3f486000 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/Path.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/Path.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Text; namespace System.IO @@ -42,8 +41,6 @@ namespace System.IO { if (path != null) { - PathInternal.CheckInvalidPathChars(path); - string s = path; for (int i = path.Length - 1; i >= 0; i--) { @@ -76,13 +73,12 @@ namespace System.IO // "\\server\share"). public static string GetDirectoryName(string path) { - if (string.IsNullOrWhiteSpace(path)) - { - if (path == null) return null; - throw new ArgumentException(SR.Arg_PathIllegal, nameof(path)); - } + if (path == null) + return null; + + if (PathInternal.IsEffectivelyEmpty(path)) + throw new ArgumentException(SR.Arg_PathEmpty, nameof(path)); - PathInternal.CheckInvalidPathChars(path); path = PathInternal.NormalizeDirectorySeparators(path); int root = PathInternal.GetRootLength(path); @@ -100,13 +96,11 @@ namespace System.IO // period (".") character of the extension except when you have a terminal period when you get string.Empty, such as ".exe" or // ".cpp". The returned value is null if the given path is // null or if the given path does not include an extension. - [Pure] public static string GetExtension(string path) { if (path == null) return null; - PathInternal.CheckInvalidPathChars(path); int length = path.Length; for (int i = length - 1; i >= 0; i--) { @@ -127,7 +121,6 @@ namespace System.IO // Returns the name and extension parts of the given path. The resulting // string contains the characters of path that follow the last // separator in path. The resulting string is null if path is null. - [Pure] public static string GetFileName(string path) { if (path == null) @@ -138,7 +131,6 @@ namespace System.IO return path.Substring(offset, count); } - [Pure] public static string GetFileNameWithoutExtension(string path) { if (path == null) @@ -166,17 +158,38 @@ namespace System.IO return new string(pRandomFileName, 0, RandomFileNameLength); } + /// + /// Returns true if the path is fixed to a specific drive or UNC path. This method does no + /// validation of the path (URIs will be returned as relative as a result). + /// Returns false if the path specified is relative to the current drive or working directory. + /// + /// + /// Handles paths that use the alternate directory separator. It is a frequent mistake to + /// assume that rooted paths are not relative. This isn't the case. + /// "C:a" is drive relative- meaning that it will be resolved against the current directory + /// for C: (rooted, but relative). "C:\a" is rooted and not relative (the current directory + /// will not be used to modify the path). + /// + /// + /// Thrown if is null. + /// + public static bool IsPathFullyQualified(string path) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + return !PathInternal.IsPartiallyQualified(path); + } + // Tests if a path includes a file extension. The result is // true if the characters that follow the last directory // separator ('\\' or '/') or volume separator (':') in the path include // a period (".") other than a terminal period. The result is false otherwise. - [Pure] public static bool HasExtension(string path) { if (path != null) { - PathInternal.CheckInvalidPathChars(path); - for (int i = path.Length - 1; i >= 0; i--) { char ch = path[i]; @@ -194,10 +207,6 @@ namespace System.IO { if (path1 == null || path2 == null) throw new ArgumentNullException((path1 == null) ? nameof(path1) : nameof(path2)); - Contract.EndContractBlock(); - - PathInternal.CheckInvalidPathChars(path1); - PathInternal.CheckInvalidPathChars(path2); return CombineNoChecks(path1, path2); } @@ -206,11 +215,6 @@ namespace System.IO { if (path1 == null || path2 == null || path3 == null) throw new ArgumentNullException((path1 == null) ? nameof(path1) : (path2 == null) ? nameof(path2) : nameof(path3)); - Contract.EndContractBlock(); - - PathInternal.CheckInvalidPathChars(path1); - PathInternal.CheckInvalidPathChars(path2); - PathInternal.CheckInvalidPathChars(path3); return CombineNoChecks(path1, path2, path3); } @@ -219,12 +223,6 @@ namespace System.IO { if (path1 == null || path2 == null || path3 == null || path4 == null) throw new ArgumentNullException((path1 == null) ? nameof(path1) : (path2 == null) ? nameof(path2) : (path3 == null) ? nameof(path3) : nameof(path4)); - Contract.EndContractBlock(); - - PathInternal.CheckInvalidPathChars(path1); - PathInternal.CheckInvalidPathChars(path2); - PathInternal.CheckInvalidPathChars(path3); - PathInternal.CheckInvalidPathChars(path4); return CombineNoChecks(path1, path2, path3, path4); } @@ -235,7 +233,6 @@ namespace System.IO { throw new ArgumentNullException(nameof(paths)); } - Contract.EndContractBlock(); int finalSize = 0; int firstComponent = 0; @@ -255,8 +252,6 @@ namespace System.IO continue; } - PathInternal.CheckInvalidPathChars(paths[i]); - if (IsPathRooted(paths[i])) { firstComponent = i; @@ -491,7 +486,7 @@ namespace System.IO private static string GetRelativePath(string relativeTo, string path, StringComparison comparisonType) { if (string.IsNullOrEmpty(relativeTo)) throw new ArgumentNullException(nameof(relativeTo)); - if (string.IsNullOrWhiteSpace(path)) throw new ArgumentNullException(nameof(path)); + if (PathInternal.IsEffectivelyEmpty(path)) throw new ArgumentNullException(nameof(path)); Debug.Assert(comparisonType == StringComparison.Ordinal || comparisonType == StringComparison.OrdinalIgnoreCase); relativeTo = GetFullPath(relativeTo); diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs index e2ead93185..a0dba661f8 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/PathHelper.Windows.cs @@ -23,7 +23,7 @@ namespace System.IO /// Normalize the given path. /// /// - /// Normalizes via Win32 GetFullPathName(). It will also trim all "typical" whitespace at the end of the path (see s_trimEndChars). Will also trim initial + /// Normalizes via Win32 GetFullPathName(). Will also trim initial /// spaces if the path is determined to be rooted. /// /// Note that invalid characters will be checked after the path is normalized, which could remove bad characters. (C:\|\..\a.txt -- C:\a.txt) @@ -47,15 +47,6 @@ namespace System.IO { GetFullPathName(path, ref fullPath); - // Trim whitespace off the end of the string. Win32 normalization trims only U+0020. - fullPath.TrimEnd(PathInternal.s_trimEndChars); - - if (fullPath.Length >= PathInternal.MaxLongPath) - { - // Fullpath is genuinely too long - throw new PathTooLongException(SR.IO_PathTooLong); - } - // Checking path validity used to happen before getting the full path name. To avoid additional input allocation // (to trim trailing whitespace) we now do it after the Win32 call. This will allow legitimate paths through that // used to get kicked back (notably segments with invalid characters might get removed via ".."). @@ -67,7 +58,6 @@ namespace System.IO // // - Illegal path characters. // - Invalid UNC paths like \\, \\server, \\server\. - // - Segments that are too long (over MaxComponentLength) // As the path could be > 30K, we'll combine the validity scan. None of these checks are performed by the Win32 // GetFullPathName() API. @@ -108,8 +98,6 @@ namespace System.IO break; case '\\': segmentLength = index - lastSeparator - 1; - if (segmentLength > PathInternal.MaxComponentLength) - throw new PathTooLongException(SR.IO_PathTooLong + fullPath.ToString()); lastSeparator = index; if (foundTilde) @@ -128,7 +116,7 @@ namespace System.IO // If we're at the end of the path and this is the first separator, we're missing the share. // Otherwise we're good, so ignore UNC tracking from here. if (index == fullPath.Length - 1) - throw new ArgumentException(SR.Arg_PathIllegalUNC); + throw new ArgumentException(SR.Format(SR.Arg_PathIllegalUNC_Path, fullPath.ToString())); else possibleBadUnc = false; } @@ -145,11 +133,9 @@ namespace System.IO } if (possibleBadUnc) - throw new ArgumentException(SR.Arg_PathIllegalUNC); + throw new ArgumentException(SR.Format(SR.Arg_PathIllegalUNC_Path, fullPath.ToString())); segmentLength = fullPath.Length - lastSeparator - 1; - if (segmentLength > PathInternal.MaxComponentLength) - throw new PathTooLongException(SR.IO_PathTooLong); if (foundTilde && segmentLength <= MaxShortName) possibleShortPath = true; diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/PathInternal.Unix.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/PathInternal.Unix.cs index ac9c4e77cd..2f65a4252b 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/PathInternal.Unix.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/PathInternal.Unix.cs @@ -22,13 +22,6 @@ namespace System.IO internal const string ParentDirectoryPrefix = @"../"; - /// Returns a value indicating if the given path contains invalid characters. - internal static bool HasIllegalCharacters(string path) - { - Debug.Assert(path != null); - return path.IndexOf(InvalidPathChar) >= 0; - } - internal static int GetRootLength(string path) { return path.Length > 0 && IsDirectorySeparator(path[0]) ? 1 : 0; @@ -105,5 +98,15 @@ namespace System.IO path.Length > 1 && IsDirectorySeparator(path[path.Length - 1]) ? // exclude root "/" path.Substring(0, path.Length - 1) : path; + + /// + /// Returns true if the path is effectively empty for the current OS. + /// For unix, this is empty or null. For Windows, this is empty, null, or + /// just spaces ((char)32). + /// + internal static bool IsEffectivelyEmpty(string path) + { + return string.IsNullOrEmpty(path); + } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/PathInternal.Windows.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/PathInternal.Windows.cs index ee2d29c032..f315f43fd5 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/PathInternal.Windows.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/PathInternal.Windows.cs @@ -56,14 +56,12 @@ namespace System.IO internal const int MaxShortPath = 260; internal const int MaxShortDirectoryPath = 248; - internal const int MaxLongPath = short.MaxValue; // \\?\, \\.\, \??\ internal const int DevicePrefixLength = 4; // \\ internal const int UncPrefixLength = 2; // \\?\UNC\, \\.\UNC\ internal const int UncExtendedPrefixLength = 8; - internal const int MaxComponentLength = 255; /// /// Returns true if the given character is a valid drive letter @@ -148,36 +146,6 @@ namespace System.IO && path[3] == '\\'; } - /// - /// Returns a value indicating if the given path contains invalid characters (", <, >, | - /// NUL, or any ASCII char whose integer representation is in the range of 1 through 31). - /// Does not check for wild card characters ? and *. - /// - internal static bool HasIllegalCharacters(string path) - { - // This is equivalent to IndexOfAny(InvalidPathChars) >= 0, - // except faster since IndexOfAny grows slower as the input - // array grows larger. - // Since we know that some of the characters we're looking - // for are contiguous in the alphabet-- the path cannot contain - // characters 0-31-- we can optimize this for our specific use - // case and use simple comparison operations. - - for (int i = 0; i < path.Length; i++) - { - char c = path[i]; - if (c <= '|') // fast path for common case - '|' is highest illegal character - { - if (c <= '\u001f' || c == '|') - { - return true; - } - } - } - - return false; - } - /// /// Check for known wildcard characters. '*' and '?' are the most common ones. /// @@ -439,9 +407,22 @@ namespace System.IO return IsDirectorySeparator(ch) || VolumeSeparatorChar == ch; } - internal static string TrimEndingDirectorySeparator(string path) => - EndsInDirectorySeparator(path) ? - path.Substring(0, path.Length - 1) : - path; + /// + /// Returns true if the path is effectively empty for the current OS. + /// For unix, this is empty or null. For Windows, this is empty, null, or + /// just spaces ((char)32). + /// + internal static bool IsEffectivelyEmpty(string path) + { + if (string.IsNullOrEmpty(path)) + return true; + + foreach (char c in path) + { + if (c != ' ') + return false; + } + return true; + } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/PathInternal.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/PathInternal.cs index 0dab5b968a..bfd69e9251 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/PathInternal.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/PathInternal.cs @@ -10,37 +10,6 @@ namespace System.IO /// Contains internal path helpers that are shared between many projects. internal static partial class PathInternal { - // Trim trailing white spaces, tabs etc but don't be aggressive in removing everything that has UnicodeCategory of trailing space. - // string.WhitespaceChars will trim more aggressively than what the underlying FS does (for ex, NTFS, FAT). - // - // (This is for compatibility with old behavior.) - internal static readonly char[] s_trimEndChars = - { - (char)0x9, // Horizontal tab - (char)0xA, // Line feed - (char)0xB, // Vertical tab - (char)0xC, // Form feed - (char)0xD, // Carriage return - (char)0x20, // Space - (char)0x85, // Next line - (char)0xA0 // Non breaking space - }; - - /// - /// Checks for invalid path characters in the given path. - /// - /// Thrown if the path is null. - /// Thrown if the path has invalid characters. - /// The path to check for invalid characters. - internal static void CheckInvalidPathChars(string path) - { - if (path == null) - throw new ArgumentNullException(nameof(path)); - - if (HasIllegalCharacters(path)) - throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path)); - } - /// /// Returns the start index of the filename /// in the given path, or 0 if no directory @@ -56,7 +25,6 @@ namespace System.IO internal static int FindFileNameIndex(string path) { Debug.Assert(path != null); - CheckInvalidPathChars(path); for (int i = path.Length - 1; i >= 0; i--) { @@ -162,7 +130,7 @@ namespace System.IO // Terminal ".." . Files names cannot end in ".." if (index + 2 == searchPattern.Length || IsDirectorySeparator(searchPattern[index + 2])) - throw new ArgumentException(SR.Arg_InvalidSearchPattern); + throw new ArgumentException(SR.Format(SR.Arg_InvalidSearchPattern, searchPattern)); searchPattern = searchPattern.Substring(index + 2); } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/PathTooLongException.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/PathTooLongException.cs index 64c8e6c7e6..7af2452736 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/PathTooLongException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/PathTooLongException.cs @@ -8,30 +8,31 @@ using System.Runtime.Serialization; namespace System.IO { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class PathTooLongException : IOException { public PathTooLongException() : base(SR.IO_PathTooLong) { - HResult = __HResults.COR_E_PATHTOOLONG; + HResult = HResults.COR_E_PATHTOOLONG; } public PathTooLongException(string message) : base(message) { - HResult = __HResults.COR_E_PATHTOOLONG; + HResult = HResults.COR_E_PATHTOOLONG; } public PathTooLongException(string message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_PATHTOOLONG; + HResult = HResults.COR_E_PATHTOOLONG; } protected PathTooLongException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/PinnedBufferMemoryStream.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/PinnedBufferMemoryStream.cs index c8e720b7ac..dfcc05d066 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/PinnedBufferMemoryStream.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/PinnedBufferMemoryStream.cs @@ -17,7 +17,6 @@ using System; using System.Runtime.InteropServices; using System.Diagnostics; -using System.Diagnostics.Contracts; namespace System.IO { @@ -30,22 +29,19 @@ namespace System.IO { Debug.Assert(array != null, "Array can't be null"); - int len = array.Length; - // Handle 0 length byte arrays specially. - if (len == 0) - { - array = new byte[1]; - len = 0; - } - _array = array; _pinningHandle = GCHandle.Alloc(array, GCHandleType.Pinned); // Now the byte[] is pinned for the lifetime of this instance. // But I also need to get a pointer to that block of memory... - fixed (byte* ptr = &_array[0]) + int len = array.Length; + fixed (byte* ptr = &MemoryMarshal.GetReference((Span)array)) Initialize(ptr, len, len, FileAccess.Read); } + public override int Read(Span destination) => ReadCore(destination); + + public override void Write(ReadOnlySpan source) => WriteCore(source); + ~PinnedBufferMemoryStream() { Dispose(false); diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryAccessor.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryAccessor.cs new file mode 100644 index 0000000000..1247ad6d5d --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryAccessor.cs @@ -0,0 +1,670 @@ +// 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. + +/*============================================================ +** +** +** +** +** Purpose: Provides a fast, AV free, cross-language way of +** accessing unmanaged memory in a random fashion. +** +** +===========================================================*/ + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Internal.Runtime.CompilerServices; + +namespace System.IO +{ + /// Perf notes: ReadXXX, WriteXXX (for basic types) acquire and release the + /// SafeBuffer pointer rather than relying on generic Read(T) from SafeBuffer because + /// this gives better throughput; benchmarks showed about 12-15% better. + public class UnmanagedMemoryAccessor : IDisposable + { + private SafeBuffer _buffer; + private long _offset; + private long _capacity; + private FileAccess _access; + private bool _isOpen; + private bool _canRead; + private bool _canWrite; + + protected UnmanagedMemoryAccessor() + { + _isOpen = false; + } + + public UnmanagedMemoryAccessor(SafeBuffer buffer, long offset, long capacity) + { + Initialize(buffer, offset, capacity, FileAccess.Read); + } + + public UnmanagedMemoryAccessor(SafeBuffer buffer, long offset, long capacity, FileAccess access) + { + Initialize(buffer, offset, capacity, access); + } + + protected void Initialize(SafeBuffer buffer, long offset, long capacity, FileAccess access) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (capacity < 0) + { + throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (buffer.ByteLength < (ulong)(offset + capacity)) + { + throw new ArgumentException(SR.Argument_OffsetAndCapacityOutOfBounds); + } + if (access < FileAccess.Read || access > FileAccess.ReadWrite) + { + throw new ArgumentOutOfRangeException(nameof(access)); + } + + if (_isOpen) + { + throw new InvalidOperationException(SR.InvalidOperation_CalledTwice); + } + + unsafe + { + byte* pointer = null; + + try + { + buffer.AcquirePointer(ref pointer); + if (((byte*)((long)pointer + offset + capacity)) < pointer) + { + throw new ArgumentException(SR.Argument_UnmanagedMemAccessorWrapAround); + } + } + finally + { + if (pointer != null) + { + buffer.ReleasePointer(); + } + } + } + + _offset = offset; + _buffer = buffer; + _capacity = capacity; + _access = access; + _isOpen = true; + _canRead = (_access & FileAccess.Read) != 0; + _canWrite = (_access & FileAccess.Write) != 0; + } + + public long Capacity => _capacity; + + public bool CanRead => _isOpen && _canRead; + + public bool CanWrite => _isOpen && _canWrite; + + protected virtual void Dispose(bool disposing) + { + _isOpen = false; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected bool IsOpen => _isOpen; + + // ************** Read Methods ****************/ + + public bool ReadBoolean(long position) => ReadByte(position) != 0; + + public byte ReadByte(long position) + { + EnsureSafeToRead(position, sizeof(byte)); + + byte result; + unsafe + { + byte* pointer = null; + + try + { + _buffer.AcquirePointer(ref pointer); + result = *((byte*)(pointer + _offset + position)); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + return result; + } + + public char ReadChar(long position) => unchecked((char)ReadInt16(position)); + + public short ReadInt16(long position) + { + EnsureSafeToRead(position, sizeof(short)); + + short result; + unsafe + { + byte* pointer = null; + + try + { + _buffer.AcquirePointer(ref pointer); + result = Unsafe.ReadUnaligned(pointer + _offset + position); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + return result; + } + + public int ReadInt32(long position) + { + EnsureSafeToRead(position, sizeof(int)); + + int result; + unsafe + { + byte* pointer = null; + + try + { + _buffer.AcquirePointer(ref pointer); + result = Unsafe.ReadUnaligned(pointer + _offset + position); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + return result; + } + + public long ReadInt64(long position) + { + EnsureSafeToRead(position, sizeof(long)); + + long result; + unsafe + { + byte* pointer = null; + + try + { + _buffer.AcquirePointer(ref pointer); + result = Unsafe.ReadUnaligned(pointer + _offset + position); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + return result; + } + + public decimal ReadDecimal(long position) + { + const int ScaleMask = 0x00FF0000; + const int SignMask = unchecked((int)0x80000000); + + EnsureSafeToRead(position, sizeof(decimal)); + + int lo, mid, hi, flags; + + unsafe + { + byte* pointer = null; + try + { + _buffer.AcquirePointer(ref pointer); + pointer += (_offset + position); + + lo = Unsafe.ReadUnaligned(pointer); + mid = Unsafe.ReadUnaligned(pointer + 4); + hi = Unsafe.ReadUnaligned(pointer + 8); + flags = Unsafe.ReadUnaligned(pointer + 12); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + + // Check for invalid Decimal values + if (!((flags & ~(SignMask | ScaleMask)) == 0 && (flags & ScaleMask) <= (28 << 16))) + { + throw new ArgumentException(SR.Arg_BadDecimal); // Throw same Exception type as Decimal(int[]) ctor for compat + } + + bool isNegative = (flags & SignMask) != 0; + byte scale = (byte)(flags >> 16); + + return new decimal(lo, mid, hi, isNegative, scale); + } + + public float ReadSingle(long position) => BitConverter.Int32BitsToSingle(ReadInt32(position)); + + public double ReadDouble(long position) => BitConverter.Int64BitsToDouble(ReadInt64(position)); + + [CLSCompliant(false)] + public sbyte ReadSByte(long position) => unchecked((sbyte)ReadByte(position)); + + [CLSCompliant(false)] + public ushort ReadUInt16(long position) => unchecked((ushort)ReadInt16(position)); + + [CLSCompliant(false)] + public uint ReadUInt32(long position) => unchecked((uint)ReadInt32(position)); + + [CLSCompliant(false)] + public ulong ReadUInt64(long position) => unchecked((ulong)ReadInt64(position)); + + // Reads a struct of type T from unmanaged memory, into the reference pointed to by ref value. + // Note: this method is not safe, since it overwrites the contents of a structure, it can be + // used to modify the private members of a struct. + // This method is most performant when used with medium to large sized structs + // (larger than 8 bytes -- though this is number is JIT and architecture dependent). As + // such, it is best to use the ReadXXX methods for small standard types such as ints, longs, + // bools, etc. + public void Read(long position, out T structure) where T : struct + { + if (position < 0) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum); + } + + if (!_isOpen) + { + throw new ObjectDisposedException(nameof(UnmanagedMemoryAccessor), SR.ObjectDisposed_ViewAccessorClosed); + } + if (!_canRead) + { + throw new NotSupportedException(SR.NotSupported_Reading); + } + + uint sizeOfT = SafeBuffer.SizeOf(); + if (position > _capacity - sizeOfT) + { + if (position >= _capacity) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired); + } + else + { + throw new ArgumentException(SR.Format(SR.Argument_NotEnoughBytesToRead, typeof(T)), nameof(position)); + } + } + + structure = _buffer.Read((ulong)(_offset + position)); + } + + // Reads 'count' structs of type T from unmanaged memory, into 'array' starting at 'offset'. + // Note: this method is not safe, since it overwrites the contents of structures, it can + // be used to modify the private members of a struct. + public int ReadArray(long position, T[] array, int offset, int count) where T : struct + { + if (array == null) + { + throw new ArgumentNullException(nameof(array), SR.ArgumentNull_Buffer); + } + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (array.Length - offset < count) + { + throw new ArgumentException(SR.Argument_InvalidOffLen); + } + if (!_isOpen) + { + throw new ObjectDisposedException(nameof(UnmanagedMemoryAccessor), SR.ObjectDisposed_ViewAccessorClosed); + } + if (!_canRead) + { + throw new NotSupportedException(SR.NotSupported_Reading); + } + if (position < 0) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum); + } + + uint sizeOfT = SafeBuffer.AlignedSizeOf(); + + // only check position and ask for fewer Ts if count is too big + if (position >= _capacity) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired); + } + + int n = count; + long spaceLeft = _capacity - position; + if (spaceLeft < 0) + { + n = 0; + } + else + { + ulong spaceNeeded = (ulong)(sizeOfT * count); + if ((ulong)spaceLeft < spaceNeeded) + { + n = (int)(spaceLeft / sizeOfT); + } + } + + _buffer.ReadArray((ulong)(_offset + position), array, offset, n); + + return n; + } + + // ************** Write Methods ****************/ + + public void Write(long position, bool value) => Write(position, (byte)(value ? 1 : 0)); + + public void Write(long position, byte value) + { + EnsureSafeToWrite(position, sizeof(byte)); + + unsafe + { + byte* pointer = null; + + try + { + _buffer.AcquirePointer(ref pointer); + *((byte*)(pointer + _offset + position)) = value; + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + } + + public void Write(long position, char value) => Write(position, unchecked((short)value)); + + public void Write(long position, short value) + { + EnsureSafeToWrite(position, sizeof(short)); + + unsafe + { + byte* pointer = null; + + try + { + _buffer.AcquirePointer(ref pointer); + Unsafe.WriteUnaligned(pointer + _offset + position, value); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + } + + public void Write(long position, int value) + { + EnsureSafeToWrite(position, sizeof(int)); + + unsafe + { + byte* pointer = null; + + try + { + _buffer.AcquirePointer(ref pointer); + Unsafe.WriteUnaligned(pointer + _offset + position, value); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + } + + public void Write(long position, long value) + { + EnsureSafeToWrite(position, sizeof(long)); + + unsafe + { + byte* pointer = null; + + try + { + _buffer.AcquirePointer(ref pointer); + Unsafe.WriteUnaligned(pointer + _offset + position, value); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + } + + public void Write(long position, decimal value) + { + EnsureSafeToWrite(position, sizeof(decimal)); + + unsafe + { + int* valuePtr = (int*)(&value); + int flags = *valuePtr; + int hi = *(valuePtr + 1); + int lo = *(valuePtr + 2); + int mid = *(valuePtr + 3); + + byte* pointer = null; + try + { + _buffer.AcquirePointer(ref pointer); + pointer += (_offset + position); + + Unsafe.WriteUnaligned(pointer, lo); + Unsafe.WriteUnaligned(pointer + 4, mid); + Unsafe.WriteUnaligned(pointer + 8, hi); + Unsafe.WriteUnaligned(pointer + 12, flags); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } + } + + public void Write(long position, float value) => Write(position, BitConverter.SingleToInt32Bits(value)); + + public void Write(long position, double value) => Write(position, BitConverter.DoubleToInt64Bits(value)); + + [CLSCompliant(false)] + public void Write(long position, sbyte value) => Write(position, unchecked((byte)value)); + + [CLSCompliant(false)] + public void Write(long position, ushort value) => Write(position, unchecked((short)value)); + + [CLSCompliant(false)] + public void Write(long position, uint value) => Write(position, unchecked((int)value)); + + [CLSCompliant(false)] + public void Write(long position, ulong value) => Write(position, unchecked((long)value)); + + // Writes the struct pointed to by ref value into unmanaged memory. Note that this method + // is most performant when used with medium to large sized structs (larger than 8 bytes + // though this is number is JIT and architecture dependent). As such, it is best to use + // the WriteX methods for small standard types such as ints, longs, bools, etc. + public void Write(long position, ref T structure) where T : struct + { + if (position < 0) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (!_isOpen) + { + throw new ObjectDisposedException(nameof(UnmanagedMemoryAccessor), SR.ObjectDisposed_ViewAccessorClosed); + } + if (!_canWrite) + { + throw new NotSupportedException(SR.NotSupported_Writing); + } + + uint sizeOfT = SafeBuffer.SizeOf(); + if (position > _capacity - sizeOfT) + { + if (position >= _capacity) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired); + } + else + { + throw new ArgumentException(SR.Format(SR.Argument_NotEnoughBytesToWrite, typeof(T)), nameof(position)); + } + } + + _buffer.Write((ulong)(_offset + position), structure); + } + + // Writes 'count' structs of type T from 'array' (starting at 'offset') into unmanaged memory. + public void WriteArray(long position, T[] array, int offset, int count) where T : struct + { + if (array == null) + { + throw new ArgumentNullException(nameof(array), SR.ArgumentNull_Buffer); + } + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (array.Length - offset < count) + { + throw new ArgumentException(SR.Argument_InvalidOffLen); + } + if (position < 0) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (position >= Capacity) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired); + } + + if (!_isOpen) + { + throw new ObjectDisposedException(nameof(UnmanagedMemoryAccessor), SR.ObjectDisposed_ViewAccessorClosed); + } + if (!_canWrite) + { + throw new NotSupportedException(SR.NotSupported_Writing); + } + + _buffer.WriteArray((ulong)(_offset + position), array, offset, count); + } + + private void EnsureSafeToRead(long position, int sizeOfType) + { + if (!_isOpen) + { + throw new ObjectDisposedException(nameof(UnmanagedMemoryAccessor), SR.ObjectDisposed_ViewAccessorClosed); + } + if (!_canRead) + { + throw new NotSupportedException(SR.NotSupported_Reading); + } + if (position < 0) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (position > _capacity - sizeOfType) + { + if (position >= _capacity) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired); + } + else + { + throw new ArgumentException(SR.Argument_NotEnoughBytesToRead, nameof(position)); + } + } + } + + private void EnsureSafeToWrite(long position, int sizeOfType) + { + if (!_isOpen) + { + throw new ObjectDisposedException(nameof(UnmanagedMemoryAccessor), SR.ObjectDisposed_ViewAccessorClosed); + } + if (!_canWrite) + { + throw new NotSupportedException(SR.NotSupported_Writing); + } + if (position < 0) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (position > _capacity - sizeOfType) + { + if (position >= _capacity) + { + throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired); + } + else + { + throw new ArgumentException(SR.Argument_NotEnoughBytesToWrite, nameof(position)); + } + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStream.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStream.cs index b78f50fe7b..171113542f 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStream.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStream.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; @@ -111,7 +110,6 @@ namespace System.IO { throw new ArgumentOutOfRangeException(nameof(access)); } - Contract.EndContractBlock(); if (_isOpen) { @@ -178,7 +176,6 @@ namespace System.IO throw new ArgumentOutOfRangeException((length < 0) ? nameof(length) : nameof(capacity), SR.ArgumentOutOfRange_NeedNonNegNum); if (length > capacity) throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_LengthGreaterThanCapacity); - Contract.EndContractBlock(); // Check for wraparound. if (((byte*)((long)pointer + capacity)) < pointer) throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_UnmanagedMemStreamWrapAround); @@ -200,7 +197,6 @@ namespace System.IO /// public override bool CanRead { - [Pure] get { return _isOpen && (_access & FileAccess.Read) != 0; } } @@ -209,7 +205,6 @@ namespace System.IO /// public override bool CanSeek { - [Pure] get { return _isOpen; } } @@ -218,7 +213,6 @@ namespace System.IO /// public override bool CanWrite { - [Pure] get { return _isOpen && (_access & FileAccess.Write) != 0; } } @@ -237,12 +231,30 @@ namespace System.IO base.Dispose(disposing); } + private void EnsureNotClosed() + { + if (!_isOpen) + throw Error.GetStreamIsClosed(); + } + + private void EnsureReadable() + { + if (!CanRead) + throw Error.GetReadNotSupported(); + } + + private void EnsureWriteable() + { + if (!CanWrite) + throw Error.GetWriteNotSupported(); + } + /// /// Since it's a memory stream, this method does nothing. /// public override void Flush() { - if (!_isOpen) throw Error.GetStreamIsClosed(); + EnsureNotClosed(); } /// @@ -273,7 +285,7 @@ namespace System.IO { get { - if (!_isOpen) throw Error.GetStreamIsClosed(); + EnsureNotClosed(); return Interlocked.Read(ref _length); } } @@ -285,7 +297,7 @@ namespace System.IO { get { - if (!_isOpen) throw Error.GetStreamIsClosed(); + EnsureNotClosed(); return _capacity; } } @@ -298,14 +310,12 @@ namespace System.IO get { if (!CanSeek) throw Error.GetStreamIsClosed(); - Contract.EndContractBlock(); return Interlocked.Read(ref _position); } set { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); if (!CanSeek) throw Error.GetStreamIsClosed(); - Contract.EndContractBlock(); Interlocked.Exchange(ref _position, value); } @@ -319,8 +329,10 @@ namespace System.IO { get { - if (_buffer != null) throw new NotSupportedException(SR.NotSupported_UmsSafeBuffer); - if (!_isOpen) throw Error.GetStreamIsClosed(); + if (_buffer != null) + throw new NotSupportedException(SR.NotSupported_UmsSafeBuffer); + + EnsureNotClosed(); // Use a temp to avoid a race long pos = Interlocked.Read(ref _position); @@ -331,8 +343,10 @@ namespace System.IO } set { - if (_buffer != null) throw new NotSupportedException(SR.NotSupported_UmsSafeBuffer); - if (!_isOpen) throw Error.GetStreamIsClosed(); + if (_buffer != null) + throw new NotSupportedException(SR.NotSupported_UmsSafeBuffer); + + EnsureNotClosed(); if (value < _mem) throw new IOException(SR.IO_SeekBeforeBegin); @@ -361,29 +375,50 @@ namespace System.IO throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); if (buffer.Length - offset < count) throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.EndContractBlock(); // Keep this in sync with contract validation in ReadAsync - if (!_isOpen) throw Error.GetStreamIsClosed(); - if (!CanRead) throw Error.GetReadNotSupported(); + return ReadCore(new Span(buffer, offset, count)); + } + + public override int Read(Span destination) + { + if (GetType() == typeof(UnmanagedMemoryStream)) + { + return ReadCore(destination); + } + else + { + // UnmanagedMemoryStream is not sealed, and a derived type may have overridden Read(byte[], int, int) prior + // to this Read(Span) overload being introduced. In that case, this Read(Span) overload + // should use the behavior of Read(byte[],int,int) overload. + return base.Read(destination); + } + } + + internal int ReadCore(Span destination) + { + EnsureNotClosed(); + EnsureReadable(); // Use a local variable to avoid a race where another thread // changes our position after we decide we can read some bytes. long pos = Interlocked.Read(ref _position); long len = Interlocked.Read(ref _length); - long n = len - pos; - if (n > count) - n = count; + long n = Math.Min(len - pos, destination.Length); if (n <= 0) + { return 0; + } int nInt = (int)n; // Safe because n <= count, which is an Int32 if (nInt < 0) + { return 0; // _position could be beyond EOF + } Debug.Assert(pos + nInt >= 0, "_position + n >= 0"); // len is less than 2^63 -1. unsafe { - fixed (byte* pBuffer = buffer) + fixed (byte* pBuffer = &MemoryMarshal.GetReference(destination)) { if (_buffer != null) { @@ -393,7 +428,7 @@ namespace System.IO try { _buffer.AcquirePointer(ref pointer); - Buffer.Memcpy(pBuffer + offset, pointer + pos + _offset, nInt); + Buffer.Memcpy(pBuffer, pointer + pos + _offset, nInt); } finally { @@ -405,7 +440,7 @@ namespace System.IO } else { - Buffer.Memcpy(pBuffer + offset, _mem + pos, nInt); + Buffer.Memcpy(pBuffer, _mem + pos, nInt); } } } @@ -431,7 +466,6 @@ namespace System.IO throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); if (buffer.Length - offset < count) throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.EndContractBlock(); // contract validation copied from Read(...) if (cancellationToken.IsCancellationRequested) return Task.FromCanceled(cancellationToken); @@ -449,14 +483,51 @@ namespace System.IO } } + /// + /// Reads bytes from stream and puts them into the buffer + /// + /// Buffer to read the bytes to. + /// Token that can be used to cancel this operation. + public override ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default(CancellationToken)) + { + if (cancellationToken.IsCancellationRequested) + { + return new ValueTask(Task.FromCanceled(cancellationToken)); + } + + try + { + // ReadAsync(Memory,...) needs to delegate to an existing virtual to do the work, in case an existing derived type + // has changed or augmented the logic associated with reads. If the Memory wraps an array, we could delegate to + // ReadAsync(byte[], ...), but that would defeat part of the purpose, as ReadAsync(byte[], ...) often needs to allocate + // a Task for the return value, so we want to delegate to one of the synchronous methods. We could always + // delegate to the Read(Span) method, and that's the most efficient solution when dealing with a concrete + // UnmanagedMemoryStream, but if we're dealing with a type derived from UnmanagedMemoryStream, Read(Span) will end up delegating + // to Read(byte[], ...), which requires it to get a byte[] from ArrayPool and copy the data. So, we special-case the + // very common case of the Memory wrapping an array: if it does, we delegate to Read(byte[], ...) with it, + // as that will be efficient in both cases, and we fall back to Read(Span) if the Memory wrapped something + // else; if this is a concrete UnmanagedMemoryStream, that'll be efficient, and only in the case where the Memory wrapped + // something other than an array and this is an UnmanagedMemoryStream-derived type that doesn't override Read(Span) will + // it then fall back to doing the ArrayPool/copy behavior. + return new ValueTask( + destination.TryGetArray(out ArraySegment destinationArray) ? + Read(destinationArray.Array, destinationArray.Offset, destinationArray.Count) : + Read(destination.Span)); + } + catch (Exception ex) + { + return new ValueTask(Task.FromException(ex)); + } + } + /// /// Returns the byte at the stream current Position and advances the Position. /// /// public override int ReadByte() { - if (!_isOpen) throw Error.GetStreamIsClosed(); - if (!CanRead) throw Error.GetReadNotSupported(); + EnsureNotClosed(); + EnsureReadable(); long pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition long len = Interlocked.Read(ref _length); @@ -502,7 +573,8 @@ namespace System.IO /// public override long Seek(long offset, SeekOrigin loc) { - if (!_isOpen) throw Error.GetStreamIsClosed(); + EnsureNotClosed(); + switch (loc) { case SeekOrigin.Begin: @@ -542,11 +614,11 @@ namespace System.IO { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); if (_buffer != null) throw new NotSupportedException(SR.NotSupported_UmsSafeBuffer); - if (!_isOpen) throw Error.GetStreamIsClosed(); - if (!CanWrite) throw Error.GetWriteNotSupported(); + + EnsureNotClosed(); + EnsureWriteable(); if (value > _capacity) throw new IOException(SR.IO_FixedCapacity); @@ -583,17 +655,38 @@ namespace System.IO throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); if (buffer.Length - offset < count) throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.EndContractBlock(); // Keep contract validation in sync with WriteAsync(..) - if (!_isOpen) throw Error.GetStreamIsClosed(); - if (!CanWrite) throw Error.GetWriteNotSupported(); + WriteCore(new Span(buffer, offset, count)); + } + + public override void Write(ReadOnlySpan source) + { + if (GetType() == typeof(UnmanagedMemoryStream)) + { + WriteCore(source); + } + else + { + // UnmanagedMemoryStream is not sealed, and a derived type may have overridden Write(byte[], int, int) prior + // to this Write(Span) overload being introduced. In that case, this Write(Span) overload + // should use the behavior of Write(byte[],int,int) overload. + base.Write(source); + } + } + + internal unsafe void WriteCore(ReadOnlySpan source) + { + EnsureNotClosed(); + EnsureWriteable(); long pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition long len = Interlocked.Read(ref _length); - long n = pos + count; + long n = pos + source.Length; // Check for overflow if (n < 0) + { throw new IOException(SR.IO_StreamTooLong); + } if (n > _capacity) { @@ -606,10 +699,7 @@ namespace System.IO // zero any memory in the middle. if (pos > len) { - unsafe - { - Buffer.ZeroMemory(_mem + len, pos - len); - } + Buffer.ZeroMemory(_mem + len, pos - len); } // set length after zeroing memory to avoid race condition of accessing unzeroed memory @@ -619,39 +709,37 @@ namespace System.IO } } - unsafe + fixed (byte* pBuffer = &MemoryMarshal.GetReference(source)) { - fixed (byte* pBuffer = buffer) + if (_buffer != null) { - if (_buffer != null) + long bytesLeft = _capacity - pos; + if (bytesLeft < source.Length) { - long bytesLeft = _capacity - pos; - if (bytesLeft < count) - { - throw new ArgumentException(SR.Arg_BufferTooSmall); - } - - byte* pointer = null; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - _buffer.AcquirePointer(ref pointer); - Buffer.Memcpy(pointer + pos + _offset, pBuffer + offset, count); - } - finally - { - if (pointer != null) - { - _buffer.ReleasePointer(); - } - } + throw new ArgumentException(SR.Arg_BufferTooSmall); } - else + + byte* pointer = null; + RuntimeHelpers.PrepareConstrainedRegions(); + try { - Buffer.Memcpy(_mem + pos, pBuffer + offset, count); + _buffer.AcquirePointer(ref pointer); + Buffer.Memcpy(pointer + pos + _offset, pBuffer, source.Length); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } } } + else + { + Buffer.Memcpy(_mem + pos, pBuffer, source.Length); + } } + Interlocked.Exchange(ref _position, n); return; } @@ -674,7 +762,6 @@ namespace System.IO throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); if (buffer.Length - offset < count) throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.EndContractBlock(); // contract validation copied from Write(..) if (cancellationToken.IsCancellationRequested) return Task.FromCanceled(cancellationToken); @@ -691,14 +778,46 @@ namespace System.IO } } + /// + /// Writes buffer into the stream. The operation completes synchronously. + /// + /// Buffer that will be written. + /// Token that can be used to cancel the operation. + public override Task WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken = default(CancellationToken)) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + try + { + // See corresponding comment in ReadAsync for why we don't just always use Write(ReadOnlySpan). + // Unlike ReadAsync, we could delegate to WriteAsync(byte[], ...) here, but we don't for consistency. + if (MemoryMarshal.TryGetArray(source, out ArraySegment sourceArray)) + { + Write(sourceArray.Array, sourceArray.Offset, sourceArray.Count); + } + else + { + Write(source.Span); + } + return Task.CompletedTask; + } + catch (Exception ex) + { + return Task.FromException(ex); + } + } + /// /// Writes a byte to the stream and advances the current Position. /// /// public override void WriteByte(byte value) { - if (!_isOpen) throw Error.GetStreamIsClosed(); - if (!CanWrite) throw Error.GetWriteNotSupported(); + EnsureNotClosed(); + EnsureWriteable(); long pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition long len = Interlocked.Read(ref _length); diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs index d547e771d7..90bb21ac5b 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs @@ -14,7 +14,6 @@ using System; using System.Runtime.InteropServices; using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; using System.Threading; using System.Threading.Tasks; @@ -82,7 +81,6 @@ namespace System.IO { return (int)_unmanagedStream.Capacity; } - [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems. set { throw new IOException(SR.IO_FixedCapacity); @@ -114,6 +112,11 @@ namespace System.IO return _unmanagedStream.Read(buffer, offset, count); } + public override int Read(Span destination) + { + return _unmanagedStream.Read(destination); + } + public override int ReadByte() { return _unmanagedStream.ReadByte(); @@ -136,6 +139,11 @@ namespace System.IO _unmanagedStream.Write(buffer, offset, count); } + public override void Write(ReadOnlySpan source) + { + _unmanagedStream.Write(source); + } + public override void WriteByte(byte value) { _unmanagedStream.WriteByte(value); @@ -146,7 +154,6 @@ namespace System.IO { if (stream == null) throw new ArgumentNullException(nameof(stream), SR.ArgumentNull_Stream); - Contract.EndContractBlock(); byte[] buffer = ToArray(); @@ -183,7 +190,6 @@ namespace System.IO if (!destination.CanWrite) throw new NotSupportedException(SR.NotSupported_UnwritableStream); - Contract.EndContractBlock(); return _unmanagedStream.CopyToAsync(destination, bufferSize, cancellationToken); } @@ -200,11 +206,21 @@ namespace System.IO return _unmanagedStream.ReadAsync(buffer, offset, count, cancellationToken); } + public override ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default(CancellationToken)) + { + return _unmanagedStream.ReadAsync(destination, cancellationToken); + } + public override Task WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken) { return _unmanagedStream.WriteAsync(buffer, offset, count, cancellationToken); } + + public override Task WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken = default(CancellationToken)) + { + return _unmanagedStream.WriteAsync(source, cancellationToken); + } } // class UnmanagedMemoryStreamWrapper } // namespace diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IO/Win32Marshal.cs b/external/corert/src/System.Private.CoreLib/shared/System/IO/Win32Marshal.cs index f6701f6f6e..e25d3ccc37 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IO/Win32Marshal.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IO/Win32Marshal.cs @@ -74,10 +74,16 @@ namespace System.IO return new IOException(SR.Format(SR.IO_AlreadyExists_Name, path), MakeHRFromErrorCode(errorCode)); case Interop.Errors.ERROR_FILENAME_EXCED_RANGE: - return new PathTooLongException(SR.IO_PathTooLong); + if (path.Length == 0) + return new PathTooLongException(SR.IO_PathTooLong); + else + return new PathTooLongException(SR.Format(SR.IO_PathTooLong_Path, path)); + + case Interop.Errors.ERROR_INVALID_DRIVE: + throw new DriveNotFoundException(SR.Format(SR.IO_DriveNotFound_Drive, path)); case Interop.Errors.ERROR_INVALID_PARAMETER: - return new IOException(GetMessage(errorCode), MakeHRFromErrorCode(errorCode)); + return new IOException(Interop.Kernel32.GetMessage(errorCode), MakeHRFromErrorCode(errorCode)); case Interop.Errors.ERROR_SHARING_VIOLATION: if (path.Length == 0) @@ -95,7 +101,7 @@ namespace System.IO return new OperationCanceledException(); default: - return new IOException(GetMessage(errorCode), MakeHRFromErrorCode(errorCode)); + return new IOException(Interop.Kernel32.GetMessage(errorCode), MakeHRFromErrorCode(errorCode)); } } @@ -108,13 +114,5 @@ namespace System.IO return unchecked(((int)0x80070000) | errorCode); } - - /// - /// Returns a string message for the specified Win32 error code. - /// - internal static string GetMessage(int errorCode) - { - return Interop.Kernel32.GetMessage(errorCode); - } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/ISpanFormattable.cs b/external/corert/src/System.Private.CoreLib/shared/System/ISpanFormattable.cs new file mode 100644 index 0000000000..df46b5bcd1 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/ISpanFormattable.cs @@ -0,0 +1,11 @@ +// 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. + +namespace System +{ + internal interface ISpanFormattable + { + bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider provider); + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IndexOutOfRangeException.cs b/external/corert/src/System.Private.CoreLib/shared/System/IndexOutOfRangeException.cs index d5b24b35d5..b6d93ef568 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/IndexOutOfRangeException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/IndexOutOfRangeException.cs @@ -15,24 +15,30 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class IndexOutOfRangeException : SystemException { public IndexOutOfRangeException() : base(SR.Arg_IndexOutOfRangeException) { - HResult = __HResults.COR_E_INDEXOUTOFRANGE; + HResult = HResults.COR_E_INDEXOUTOFRANGE; } public IndexOutOfRangeException(String message) : base(message) { - HResult = __HResults.COR_E_INDEXOUTOFRANGE; + HResult = HResults.COR_E_INDEXOUTOFRANGE; } public IndexOutOfRangeException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_INDEXOUTOFRANGE; + HResult = HResults.COR_E_INDEXOUTOFRANGE; + } + + internal IndexOutOfRangeException(SerializationInfo info, StreamingContext context) : base(info, context) + { } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/InsufficientExecutionStackException.cs b/external/corert/src/System.Private.CoreLib/shared/System/InsufficientExecutionStackException.cs index 41df3ae970..4822028f85 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/InsufficientExecutionStackException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/InsufficientExecutionStackException.cs @@ -6,24 +6,30 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class InsufficientExecutionStackException : SystemException { public InsufficientExecutionStackException() : base(SR.Arg_InsufficientExecutionStackException) { - HResult = __HResults.COR_E_INSUFFICIENTEXECUTIONSTACK; + HResult = HResults.COR_E_INSUFFICIENTEXECUTIONSTACK; } public InsufficientExecutionStackException(String message) : base(message) { - HResult = __HResults.COR_E_INSUFFICIENTEXECUTIONSTACK; + HResult = HResults.COR_E_INSUFFICIENTEXECUTIONSTACK; } public InsufficientExecutionStackException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_INSUFFICIENTEXECUTIONSTACK; + HResult = HResults.COR_E_INSUFFICIENTEXECUTIONSTACK; + } + + internal InsufficientExecutionStackException(SerializationInfo info, StreamingContext context) : base(info, context) + { } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Int16.cs b/external/corert/src/System.Private.CoreLib/shared/System/Int16.cs new file mode 100644 index 0000000000..fecc87e9fe --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Int16.cs @@ -0,0 +1,314 @@ +// 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.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Int16 : IComparable, IConvertible, IFormattable, IComparable, IEquatable, ISpanFormattable + { + private short m_value; // Do not rename (binary serialization) + + public const short MaxValue = (short)0x7FFF; + public const short MinValue = unchecked((short)0x8000); + + // Compares this object to another object, returning an integer that + // indicates the relationship. + // Returns a value less than zero if this object + // null is considered to be less than any instance. + // If object is not of type Int16, this method throws an ArgumentException. + // + public int CompareTo(Object value) + { + if (value == null) + { + return 1; + } + + if (value is Int16) + { + return m_value - ((Int16)value).m_value; + } + + throw new ArgumentException(SR.Arg_MustBeInt16); + } + + public int CompareTo(Int16 value) + { + return m_value - value; + } + + public override bool Equals(Object obj) + { + if (!(obj is Int16)) + { + return false; + } + return m_value == ((Int16)obj).m_value; + } + + [NonVersionable] + public bool Equals(Int16 obj) + { + return m_value == obj; + } + + // Returns a HashCode for the Int16 + public override int GetHashCode() + { + return ((int)((ushort)m_value) | (((int)m_value) << 16)); + } + + + public override String ToString() + { + return Number.FormatInt32(m_value, null, null); + } + + public String ToString(IFormatProvider provider) + { + return Number.FormatInt32(m_value, null, provider); + } + + public String ToString(String format) + { + return ToString(format, null); + } + + public String ToString(String format, IFormatProvider provider) + { + if (m_value < 0 && format != null && format.Length > 0 && (format[0] == 'X' || format[0] == 'x')) + { + uint temp = (uint)(m_value & 0x0000FFFF); + return Number.FormatUInt32(temp, format, provider); + } + + return Number.FormatInt32(m_value, format, provider); + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) + { + if (m_value < 0 && format.Length > 0 && (format[0] == 'X' || format[0] == 'x')) + { + uint temp = (uint)(m_value & 0x0000FFFF); + return Number.TryFormatUInt32(temp, format, provider, destination, out charsWritten); + } + return Number.TryFormatInt32(m_value, format, provider, destination, out charsWritten); + } + + public static short Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); + } + + public static short Parse(String s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, style, NumberFormatInfo.CurrentInfo); + } + + public static short Parse(String s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + } + + public static short Parse(String s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider)); + } + + public static short Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Parse(s, style, NumberFormatInfo.GetInstance(provider)); + } + + private static short Parse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info) + { + int i = 0; + try + { + i = Number.ParseInt32(s, style, info); + } + catch (OverflowException e) + { + throw new OverflowException(SR.Overflow_Int16, e); + } + + // We need this check here since we don't allow signs to specified in hex numbers. So we fixup the result + // for negative numbers + if ((style & NumberStyles.AllowHexSpecifier) != 0) + { // We are parsing a hexadecimal number + if ((i < 0) || (i > UInt16.MaxValue)) + { + throw new OverflowException(SR.Overflow_Int16); + } + return (short)i; + } + + if (i < MinValue || i > MaxValue) throw new OverflowException(SR.Overflow_Int16); + return (short)i; + } + + public static bool TryParse(String s, out Int16 result) + { + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(ReadOnlySpan s, out short result) + { + return TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out Int16 result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out short result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + private static bool TryParse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out Int16 result) + { + result = 0; + int i; + if (!Number.TryParseInt32(s, style, info, out i)) + { + return false; + } + + // We need this check here since we don't allow signs to specified in hex numbers. So we fixup the result + // for negative numbers + if ((style & NumberStyles.AllowHexSpecifier) != 0) + { // We are parsing a hexadecimal number + if ((i < 0) || i > UInt16.MaxValue) + { + return false; + } + result = (Int16)i; + return true; + } + + if (i < MinValue || i > MaxValue) + { + return false; + } + result = (Int16)i; + return true; + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.Int16; + } + + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return Convert.ToChar(m_value); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return m_value; + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(m_value); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(m_value); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(m_value); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Int16", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Int32.cs b/external/corert/src/System.Private.CoreLib/shared/System/Int32.cs new file mode 100644 index 0000000000..b573e950e4 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Int32.cs @@ -0,0 +1,269 @@ +// 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.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Int32 : IComparable, IConvertible, IFormattable, IComparable, IEquatable, ISpanFormattable + { + private int m_value; // Do not rename (binary serialization) + + public const int MaxValue = 0x7fffffff; + public const int MinValue = unchecked((int)0x80000000); + + // Compares this object to another object, returning an integer that + // indicates the relationship. + // Returns : + // 0 if the values are equal + // Negative number if _value is less than value + // Positive number if _value is more than value + // null is considered to be less than any instance, hence returns positive number + // If object is not of type Int32, this method throws an ArgumentException. + // + public int CompareTo(Object value) + { + if (value == null) + { + return 1; + } + if (value is Int32) + { + // NOTE: Cannot use return (_value - value) as this causes a wrap + // around in cases where _value - value > MaxValue. + int i = (int)value; + if (m_value < i) return -1; + if (m_value > i) return 1; + return 0; + } + throw new ArgumentException(SR.Arg_MustBeInt32); + } + + public int CompareTo(int value) + { + // NOTE: Cannot use return (_value - value) as this causes a wrap + // around in cases where _value - value > MaxValue. + if (m_value < value) return -1; + if (m_value > value) return 1; + return 0; + } + + public override bool Equals(Object obj) + { + if (!(obj is Int32)) + { + return false; + } + return m_value == ((Int32)obj).m_value; + } + + [NonVersionable] + public bool Equals(Int32 obj) + { + return m_value == obj; + } + + // The absolute value of the int contained. + public override int GetHashCode() + { + return m_value; + } + + public override String ToString() + { + return Number.FormatInt32(m_value, null, null); + } + + public String ToString(String format) + { + return Number.FormatInt32(m_value, format, null); + } + + public String ToString(IFormatProvider provider) + { + return Number.FormatInt32(m_value, null, provider); + } + + public String ToString(String format, IFormatProvider provider) + { + return Number.FormatInt32(m_value, format, provider); + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) + { + return Number.TryFormatInt32(m_value, format, provider, destination, out charsWritten); + } + + public static int Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); + } + + public static int Parse(String s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt32(s, style, NumberFormatInfo.CurrentInfo); + } + + // Parses an integer from a String in the given style. If + // a NumberFormatInfo isn't specified, the current culture's + // NumberFormatInfo is assumed. + // + public static int Parse(String s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt32(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + } + + // Parses an integer from a String in the given style. If + // a NumberFormatInfo isn't specified, the current culture's + // NumberFormatInfo is assumed. + // + public static int Parse(String s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt32(s, style, NumberFormatInfo.GetInstance(provider)); + } + + public static int Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.ParseInt32(s, style, NumberFormatInfo.GetInstance(provider)); + } + + // Parses an integer from a String. Returns false rather + // than throwing exceptin if input is invalid + // + public static bool TryParse(String s, out Int32 result) + { + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(ReadOnlySpan s, out int result) + { + return Number.TryParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + // Parses an integer from a String in the given style. Returns false rather + // than throwing exceptin if input is invalid + // + public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out Int32 result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseInt32(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out int result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseInt32(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.Int32; + } + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return Convert.ToChar(m_value); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return m_value; + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(m_value); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(m_value); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Int32", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Int64.cs b/external/corert/src/System.Private.CoreLib/shared/System/Int64.cs new file mode 100644 index 0000000000..0bcca87309 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Int64.cs @@ -0,0 +1,257 @@ +// 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.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Int64 : IComparable, IConvertible, IFormattable, IComparable, IEquatable, ISpanFormattable + { + private long m_value; // Do not rename (binary serialization) + + public const long MaxValue = 0x7fffffffffffffffL; + public const long MinValue = unchecked((long)0x8000000000000000L); + + // Compares this object to another object, returning an integer that + // indicates the relationship. + // Returns a value less than zero if this object + // null is considered to be less than any instance. + // If object is not of type Int64, this method throws an ArgumentException. + // + public int CompareTo(Object value) + { + if (value == null) + { + return 1; + } + if (value is Int64) + { + // Need to use compare because subtraction will wrap + // to positive for very large neg numbers, etc. + long i = (long)value; + if (m_value < i) return -1; + if (m_value > i) return 1; + return 0; + } + throw new ArgumentException(SR.Arg_MustBeInt64); + } + + public int CompareTo(Int64 value) + { + // Need to use compare because subtraction will wrap + // to positive for very large neg numbers, etc. + if (m_value < value) return -1; + if (m_value > value) return 1; + return 0; + } + + public override bool Equals(Object obj) + { + if (!(obj is Int64)) + { + return false; + } + return m_value == ((Int64)obj).m_value; + } + + [NonVersionable] + public bool Equals(Int64 obj) + { + return m_value == obj; + } + + // The value of the lower 32 bits XORed with the uppper 32 bits. + public override int GetHashCode() + { + return (unchecked((int)((long)m_value)) ^ (int)(m_value >> 32)); + } + + public override String ToString() + { + return Number.FormatInt64(m_value, null, null); + } + + public String ToString(IFormatProvider provider) + { + return Number.FormatInt64(m_value, null, provider); + } + + public String ToString(String format) + { + return Number.FormatInt64(m_value, format, null); + } + + public String ToString(String format, IFormatProvider provider) + { + return Number.FormatInt64(m_value, format, provider); + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) + { + return Number.TryFormatInt64(m_value, format, provider, destination, out charsWritten); + } + + public static long Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); + } + + public static long Parse(String s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt64(s, style, NumberFormatInfo.CurrentInfo); + } + + public static long Parse(String s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt64(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + } + + + // Parses a long from a String in the given style. If + // a NumberFormatInfo isn't specified, the current culture's + // NumberFormatInfo is assumed. + // + public static long Parse(String s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt64(s, style, NumberFormatInfo.GetInstance(provider)); + } + + public static long Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.ParseInt64(s, style, NumberFormatInfo.GetInstance(provider)); + } + + public static Boolean TryParse(String s, out Int64 result) + { + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(ReadOnlySpan s, out long result) + { + return Number.TryParseInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + public static Boolean TryParse(String s, NumberStyles style, IFormatProvider provider, out Int64 result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseInt64(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out long result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseInt64(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.Int64; + } + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return Convert.ToChar(m_value); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(m_value); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return m_value; + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(m_value); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(m_value); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Int64", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/IntPtr.cs b/external/corert/src/System.Private.CoreLib/shared/System/IntPtr.cs new file mode 100644 index 0000000000..45c2ded160 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/IntPtr.cs @@ -0,0 +1,235 @@ +// 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.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; +using System.Runtime.Versioning; + +#if BIT64 +using nint = System.Int64; +#else +using nint = System.Int32; +#endif + +namespace System +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct IntPtr : IEquatable, ISerializable + { + // WARNING: We allow diagnostic tools to directly inspect this member (_value). + // See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details. + // Please do not change the type, the name, or the semantic usage of this member without understanding the implication for tools. + // Get in touch with the diagnostics team if you have questions. + unsafe private void* _value; // Do not rename (binary serialization) + + [Intrinsic] + public static readonly IntPtr Zero; + + [Intrinsic] + [NonVersionable] + public unsafe IntPtr(int value) + { + _value = (void*)value; + } + + [Intrinsic] + [NonVersionable] + public unsafe IntPtr(long value) + { +#if BIT64 + _value = (void*)value; +#else + _value = (void*)checked((int)value); +#endif + } + + [CLSCompliant(false)] + [Intrinsic] + [NonVersionable] + public unsafe IntPtr(void* value) + { + _value = value; + } + + private unsafe IntPtr(SerializationInfo info, StreamingContext context) + { + long l = info.GetInt64("value"); + + if (Size == 4 && (l > int.MaxValue || l < int.MinValue)) + throw new ArgumentException(SR.Serialization_InvalidPtrValue); + + _value = (void*)l; + } + + unsafe void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + throw new ArgumentNullException(nameof(info)); + + info.AddValue("value", ToInt64()); + } + + public unsafe override bool Equals(Object obj) + { + if (obj is IntPtr) + { + return (_value == ((IntPtr)obj)._value); + } + return false; + } + + unsafe bool IEquatable.Equals(IntPtr value) + { + return _value == value._value; + } + + public unsafe override int GetHashCode() + { +#if BIT64 + long l = (long)_value; + return (unchecked((int)l) ^ (int)(l >> 32)); +#else + return unchecked((int)_value); +#endif + } + + [Intrinsic] + [NonVersionable] + public unsafe int ToInt32() + { +#if BIT64 + long l = (long)_value; + return checked((int)l); +#else + return (int)_value; +#endif + } + + [Intrinsic] + [NonVersionable] + public unsafe long ToInt64() + { + return (nint)_value; + } + + [Intrinsic] + [NonVersionable] + public static unsafe explicit operator IntPtr(int value) + { + return new IntPtr(value); + } + + [Intrinsic] + [NonVersionable] + public static unsafe explicit operator IntPtr(long value) + { + return new IntPtr(value); + } + + [CLSCompliant(false)] + [Intrinsic] + [NonVersionable] + public static unsafe explicit operator IntPtr(void* value) + { + return new IntPtr(value); + } + + [CLSCompliant(false)] + [Intrinsic] + [NonVersionable] + public static unsafe explicit operator void* (IntPtr value) + { + return value._value; + } + + [Intrinsic] + [NonVersionable] + public static unsafe explicit operator int(IntPtr value) + { +#if BIT64 + long l = (long)value._value; + return checked((int)l); +#else + return (int)value._value; +#endif + } + + [Intrinsic] + [NonVersionable] + public static unsafe explicit operator long(IntPtr value) + { + return (nint)value._value; + } + + [Intrinsic] + [NonVersionable] + public static unsafe bool operator ==(IntPtr value1, IntPtr value2) + { + return value1._value == value2._value; + } + + [Intrinsic] + [NonVersionable] + public static unsafe bool operator !=(IntPtr value1, IntPtr value2) + { + return value1._value != value2._value; + } + + [NonVersionable] + public static IntPtr Add(IntPtr pointer, int offset) + { + return pointer + offset; + } + + [Intrinsic] + [NonVersionable] + public static unsafe IntPtr operator +(IntPtr pointer, int offset) + { + return new IntPtr((nint)pointer._value + offset); + } + + [NonVersionable] + public static IntPtr Subtract(IntPtr pointer, int offset) + { + return pointer - offset; + } + + [Intrinsic] + [NonVersionable] + public static unsafe IntPtr operator -(IntPtr pointer, int offset) + { + return new IntPtr((nint)pointer._value - offset); + } + + public static int Size + { + [Intrinsic] + [NonVersionable] + get + { + return sizeof(nint); + } + } + + [CLSCompliant(false)] + [Intrinsic] + [NonVersionable] + public unsafe void* ToPointer() + { + return _value; + } + + public unsafe override string ToString() + { + return ((nint)_value).ToString(CultureInfo.InvariantCulture); + } + + public unsafe string ToString(string format) + { + return ((nint)_value).ToString(format, CultureInfo.InvariantCulture); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/InvalidCastException.cs b/external/corert/src/System.Private.CoreLib/shared/System/InvalidCastException.cs index cf359ac0b5..055643278a 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/InvalidCastException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/InvalidCastException.cs @@ -12,24 +12,26 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class InvalidCastException : SystemException { public InvalidCastException() : base(SR.Arg_InvalidCastException) { - HResult = __HResults.COR_E_INVALIDCAST; + HResult = HResults.COR_E_INVALIDCAST; } public InvalidCastException(String message) : base(message) { - HResult = __HResults.COR_E_INVALIDCAST; + HResult = HResults.COR_E_INVALIDCAST; } public InvalidCastException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_INVALIDCAST; + HResult = HResults.COR_E_INVALIDCAST; } public InvalidCastException(String message, int errorCode) @@ -40,7 +42,6 @@ namespace System protected InvalidCastException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/InvalidOperationException.cs b/external/corert/src/System.Private.CoreLib/shared/System/InvalidOperationException.cs index ad743e05ff..62c222af40 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/InvalidOperationException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/InvalidOperationException.cs @@ -16,29 +16,30 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class InvalidOperationException : SystemException { public InvalidOperationException() : base(SR.Arg_InvalidOperationException) { - HResult = __HResults.COR_E_INVALIDOPERATION; + HResult = HResults.COR_E_INVALIDOPERATION; } public InvalidOperationException(String message) : base(message) { - HResult = __HResults.COR_E_INVALIDOPERATION; + HResult = HResults.COR_E_INVALIDOPERATION; } public InvalidOperationException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_INVALIDOPERATION; + HResult = HResults.COR_E_INVALIDOPERATION; } protected InvalidOperationException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/InvalidProgramException.cs b/external/corert/src/System.Private.CoreLib/shared/System/InvalidProgramException.cs index 47e7325836..c8047c548b 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/InvalidProgramException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/InvalidProgramException.cs @@ -15,24 +15,28 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class InvalidProgramException : SystemException { public InvalidProgramException() : base(SR.InvalidProgram_Default) { - HResult = __HResults.COR_E_INVALIDPROGRAM; + HResult = HResults.COR_E_INVALIDPROGRAM; } public InvalidProgramException(String message) : base(message) { - HResult = __HResults.COR_E_INVALIDPROGRAM; + HResult = HResults.COR_E_INVALIDPROGRAM; } public InvalidProgramException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_INVALIDPROGRAM; + HResult = HResults.COR_E_INVALIDPROGRAM; } + + internal InvalidProgramException(SerializationInfo info, StreamingContext context) : base(info, context) { } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/InvalidTimeZoneException.cs b/external/corert/src/System.Private.CoreLib/shared/System/InvalidTimeZoneException.cs index 8b300f453d..25b155e8d1 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/InvalidTimeZoneException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/InvalidTimeZoneException.cs @@ -6,6 +6,8 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class InvalidTimeZoneException : Exception { public InvalidTimeZoneException() @@ -24,7 +26,6 @@ namespace System protected InvalidTimeZoneException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Lazy.cs b/external/corert/src/System.Private.CoreLib/shared/System/Lazy.cs index 55f915b65d..6410c2e285 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Lazy.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Lazy.cs @@ -15,7 +15,6 @@ using System.Diagnostics; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; -using System.Runtime.Serialization; using System.Threading; namespace System @@ -84,7 +83,7 @@ namespace System break; default: - Debug.Assert(false, "internal constructor, this should never occur"); + Debug.Fail("internal constructor, this should never occur"); break; } @@ -119,7 +118,7 @@ namespace System return LazyThreadSafetyMode.ExecutionAndPublication; default: - Debug.Assert(false, "Invalid logic; State should always have a valid value"); + Debug.Fail("Invalid logic; State should always have a valid value"); return default(LazyThreadSafetyMode); } } @@ -182,8 +181,7 @@ namespace System /// using parameters to the type's constructors. /// /// - [Serializable] - [DebuggerTypeProxy(typeof(System_LazyDebugView<>))] + [DebuggerTypeProxy(typeof(LazyDebugView<>))] [DebuggerDisplay("ThreadSafetyMode={Mode}, IsValueCreated={IsValueCreated}, IsValueFaulted={IsValueFaulted}, Value={ValueForDebugDisplay}")] public class Lazy { @@ -192,13 +190,11 @@ namespace System return (T)LazyHelper.CreateViaDefaultConstructor(typeof(T)); } - // _state, a volatile reference, is set to null after m_value has been set - [NonSerialized] + // _state, a volatile reference, is set to null after _value has been set private volatile LazyHelper _state; // we ensure that _factory when finished is set to null to allow garbage collector to clean up // any referenced items - [NonSerialized] private Func _factory; // _value eventually stores the lazily created value. It is valid when _state = null. @@ -262,7 +258,7 @@ namespace System /// Initializes a new instance of the /// class that uses 's default constructor and a specified thread-safety mode. /// - /// The lazy thread-safety mode mode + /// The lazy thread-safety mode /// mode contains an invalid valuee public Lazy(LazyThreadSafetyMode mode) : this(null, mode, useDefaultConstructor:true) @@ -440,15 +436,6 @@ namespace System return Value; } - /// Forces initialization during serialization. - /// The StreamingContext for the serialization operation. - [OnSerializing] - private void OnSerializing(StreamingContext context) - { - // Force initialization - T dummy = Value; - } - /// Creates and returns a string representation of this instance. /// The result of calling on the . @@ -521,14 +508,14 @@ namespace System /// A debugger view of the Lazy<T> to surface additional debugging properties and /// to ensure that the Lazy<T> does not become initialized if it was not already. - internal sealed class System_LazyDebugView + internal sealed class LazyDebugView { //The Lazy object being viewed. private readonly Lazy _lazy; /// Constructs a new debugger view object for the provided Lazy object. /// A Lazy object to browse in the debugger. - public System_LazyDebugView(Lazy lazy) + public LazyDebugView(Lazy lazy) { _lazy = lazy; } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Math.cs b/external/corert/src/System.Private.CoreLib/shared/System/Math.cs new file mode 100644 index 0000000000..bdb237da38 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Math.cs @@ -0,0 +1,831 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: Some floating-point math operations +** +** +===========================================================*/ + +//This class contains only static members and doesn't require serialization. + +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; + +namespace System +{ + public static partial class Math + { + public const double E = 2.7182818284590452354; + + public const double PI = 3.14159265358979323846; + + private const int maxRoundingDigits = 15; + + private static double doubleRoundLimit = 1e16d; + + // This table is required for the Round function which can specify the number of digits to round to + private static double[] roundPower10Double = new double[] { + 1E0, 1E1, 1E2, 1E3, 1E4, 1E5, 1E6, 1E7, 1E8, + 1E9, 1E10, 1E11, 1E12, 1E13, 1E14, 1E15 + }; + + public static short Abs(short value) + { + return (value >= 0) ? value : AbsHelper(value); + } + + public static int Abs(int value) + { + return (value >= 0) ? value : AbsHelper(value); + } + + public static long Abs(long value) + { + return (value >= 0) ? value : AbsHelper(value); + } + + [CLSCompliant(false)] + public static sbyte Abs(sbyte value) + { + return (value >= 0) ? value : AbsHelper(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Abs(decimal value) + { + return decimal.Abs(value); + } + + public static long BigMul(int a, int b) + { + return ((long)a) * b; + } + + public static int DivRem(int a, int b, out int result) + { + // TODO https://github.com/dotnet/coreclr/issues/3439: + // Restore to using % and / when the JIT is able to eliminate one of the idivs. + // In the meantime, a * and - is measurably faster than an extra /. + + int div = a / b; + result = a - (div * b); + return div; + } + + public static long DivRem(long a, long b, out long result) + { + // TODO https://github.com/dotnet/coreclr/issues/3439: + // Restore to using % and / when the JIT is able to eliminate one of the idivs. + // In the meantime, a * and - is measurably faster than an extra /. + + long div = a / b; + result = a - (div * b); + return div; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Ceiling(decimal d) + { + return decimal.Ceiling(d); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte Clamp(byte value, byte min, byte max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Clamp(decimal value, decimal min, decimal max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Clamp(double value, double min, double max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static short Clamp(short value, short min, short max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Clamp(int value, int min, int max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long Clamp(long value, long min, long max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static sbyte Clamp(sbyte value, sbyte min, sbyte max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Clamp(float value, float min, float max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort Clamp(ushort value, ushort min, ushort max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static uint Clamp(uint value, uint min, uint max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static ulong Clamp(ulong value, ulong min, ulong max) + { + if (min > max) + { + ThrowMinMaxException(min, max); + } + + if (value < min) + { + return min; + } + else if (value > max) + { + return max; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Floor(decimal d) + { + return decimal.Floor(d); + } + + public static double IEEERemainder(double x, double y) + { + if (double.IsNaN(x)) + { + return x; // IEEE 754-2008: NaN payload must be preserved + } + + if (double.IsNaN(y)) + { + return y; // IEEE 754-2008: NaN payload must be preserved + } + + var regularMod = x % y; + + if (double.IsNaN(regularMod)) + { + return double.NaN; + } + + if ((regularMod == 0) && double.IsNegative(x)) + { + return double.NegativeZero; + } + + var alternativeResult = (regularMod - (Abs(y) * Sign(x))); + + if (Abs(alternativeResult) == Abs(regularMod)) + { + var divisionResult = x / y; + var roundedResult = Round(divisionResult); + + if (Abs(roundedResult) > Abs(divisionResult)) + { + return alternativeResult; + } + else + { + return regularMod; + } + } + + if (Abs(alternativeResult) < Abs(regularMod)) + { + return alternativeResult; + } + else + { + return regularMod; + } + } + + public static double Log(double a, double newBase) + { + if (double.IsNaN(a)) + { + return a; // IEEE 754-2008: NaN payload must be preserved + } + + if (double.IsNaN(newBase)) + { + return newBase; // IEEE 754-2008: NaN payload must be preserved + } + + if (newBase == 1) + { + return double.NaN; + } + + if ((a != 1) && ((newBase == 0) || double.IsPositiveInfinity(newBase))) + { + return double.NaN; + } + + return (Log(a) / Log(newBase)); + } + + [NonVersionable] + public static byte Max(byte val1, byte val2) + { + return (val1 >= val2) ? val1 : val2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Max(decimal val1, decimal val2) + { + return decimal.Max(val1, val2); + } + + public static double Max(double val1, double val2) + { + if (val1 > val2) + { + return val1; + } + + if (double.IsNaN(val1)) + { + return val1; + } + + return val2; + } + + [NonVersionable] + public static short Max(short val1, short val2) + { + return (val1 >= val2) ? val1 : val2; + } + + [NonVersionable] + public static int Max(int val1, int val2) + { + return (val1 >= val2) ? val1 : val2; + } + + [NonVersionable] + public static long Max(long val1, long val2) + { + return (val1 >= val2) ? val1 : val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static sbyte Max(sbyte val1, sbyte val2) + { + return (val1 >= val2) ? val1 : val2; + } + + public static float Max(float val1, float val2) + { + if (val1 > val2) + { + return val1; + } + + if (float.IsNaN(val1)) + { + return val1; + } + + return val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static ushort Max(ushort val1, ushort val2) + { + return (val1 >= val2) ? val1 : val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static uint Max(uint val1, uint val2) + { + return (val1 >= val2) ? val1 : val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static ulong Max(ulong val1, ulong val2) + { + return (val1 >= val2) ? val1 : val2; + } + + [NonVersionable] + public static byte Min(byte val1, byte val2) + { + return (val1 <= val2) ? val1 : val2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Min(decimal val1, decimal val2) + { + return decimal.Min(val1, val2); + } + + public static double Min(double val1, double val2) + { + if (val1 < val2) + { + return val1; + } + + if (double.IsNaN(val1)) + { + return val1; + } + + return val2; + } + + [NonVersionable] + public static short Min(short val1, short val2) + { + return (val1 <= val2) ? val1 : val2; + } + + [NonVersionable] + public static int Min(int val1, int val2) + { + return (val1 <= val2) ? val1 : val2; + } + + [NonVersionable] + public static long Min(long val1, long val2) + { + return (val1 <= val2) ? val1 : val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static sbyte Min(sbyte val1, sbyte val2) + { + return (val1 <= val2) ? val1 : val2; + } + + public static float Min(float val1, float val2) + { + if (val1 < val2) + { + return val1; + } + + if (float.IsNaN(val1)) + { + return val1; + } + + return val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static ushort Min(ushort val1, ushort val2) + { + return (val1 <= val2) ? val1 : val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static uint Min(uint val1, uint val2) + { + return (val1 <= val2) ? val1 : val2; + } + + [CLSCompliant(false)] + [NonVersionable] + public static ulong Min(ulong val1, ulong val2) + { + return (val1 <= val2) ? val1 : val2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Round(decimal d) + { + return decimal.Round(d, 0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Round(decimal d, int decimals) + { + return decimal.Round(d, decimals); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Round(decimal d, MidpointRounding mode) + { + return decimal.Round(d, 0, mode); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Round(decimal d, int decimals, MidpointRounding mode) + { + return decimal.Round(d, decimals, mode); + } + + [Intrinsic] + public static double Round(double a) + { + // ************************************************************************************ + // IMPORTANT: Do not change this implementation without also updating Math.Round(double), + // FloatingPointUtils::round(double), and FloatingPointUtils::round(float) + // ************************************************************************************ + + // If the number has no fractional part do nothing + // This shortcut is necessary to workaround precision loss in borderline cases on some platforms + + if (a == (double)((long)a)) + { + return a; + } + + // We had a number that was equally close to 2 integers. + // We need to return the even one. + + double flrTempVal = Floor(a + 0.5); + + if ((a == (Floor(a) + 0.5)) && (FMod(flrTempVal, 2.0) != 0)) + { + flrTempVal -= 1.0; + } + + return copysign(flrTempVal, a); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Round(double value, int digits) + { + return Round(value, digits, MidpointRounding.ToEven); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Round(double value, MidpointRounding mode) + { + return Round(value, 0, mode); + } + + public static unsafe double Round(double value, int digits, MidpointRounding mode) + { + if ((digits < 0) || (digits > maxRoundingDigits)) + { + throw new ArgumentOutOfRangeException(nameof(digits), SR.ArgumentOutOfRange_RoundingDigits); + } + + if (mode < MidpointRounding.ToEven || mode > MidpointRounding.AwayFromZero) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidEnumValue, mode, nameof(MidpointRounding)), nameof(mode)); + } + + if (Abs(value) < doubleRoundLimit) + { + var power10 = roundPower10Double[digits]; + + value *= power10; + + if (mode == MidpointRounding.AwayFromZero) + { + var fraction = ModF(value, &value); + + if (Abs(fraction) >= 0.5) + { + value += Sign(fraction); + } + } + else + { + value = Round(value); + } + + value /= power10; + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Sign(decimal value) + { + return decimal.Sign(ref value); + } + + public static int Sign(double value) + { + if (value < 0) + { + return -1; + } + else if (value > 0) + { + return 1; + } + else if (value == 0) + { + return 0; + } + + throw new ArithmeticException(SR.Arithmetic_NaN); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Sign(short value) + { + return Sign((int)value); + } + + public static int Sign(int value) + { + return unchecked(value >> 31 | (int)((uint)-value >> 31)); + } + + public static int Sign(long value) + { + return unchecked((int)(value >> 63 | (long)((ulong)-value >> 63))); + } + + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Sign(sbyte value) + { + return Sign((int)value); + } + + public static int Sign(float value) + { + if (value < 0) + { + return -1; + } + else if (value > 0) + { + return 1; + } + else if (value == 0) + { + return 0; + } + + throw new ArithmeticException(SR.Arithmetic_NaN); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal Truncate(decimal d) + { + return decimal.Truncate(d); + } + + public static unsafe double Truncate(double d) + { + ModF(d, &d); + return d; + } + + private static short AbsHelper(short value) + { + Debug.Assert(value < 0, "AbsHelper should only be called for negative values! (workaround for JIT inlining)"); + + if (value == short.MinValue) + { + throw new OverflowException(SR.Overflow_NegateTwosCompNum); + } + + return ((short)(-value)); + } + + private static int AbsHelper(int value) + { + Debug.Assert(value < 0, "AbsHelper should only be called for negative values! (workaround for JIT inlining)"); + + if (value == int.MinValue) + { + throw new OverflowException(SR.Overflow_NegateTwosCompNum); + } + + return -value; + } + + private static long AbsHelper(long value) + { + Debug.Assert(value < 0, "AbsHelper should only be called for negative values! (workaround for JIT inlining)"); + + if (value == long.MinValue) + { + throw new OverflowException(SR.Overflow_NegateTwosCompNum); + } + + return -value; + } + + private static sbyte AbsHelper(sbyte value) + { + Debug.Assert(value < 0, "AbsHelper should only be called for negative values! (workaround for JIT inlining)"); + + if (value == sbyte.MinValue) + { + throw new OverflowException(SR.Overflow_NegateTwosCompNum); + } + + return ((sbyte)(-value)); + } + + private static unsafe double copysign(double x, double y) + { + var xbits = BitConverter.DoubleToInt64Bits(x); + var ybits = BitConverter.DoubleToInt64Bits(y); + + // If the sign bits of x and y are not the same, + // flip the sign bit of x and return the new value; + // otherwise, just return x + + if (((xbits ^ ybits) >> 63) != 0) + { + return BitConverter.Int64BitsToDouble(xbits ^ long.MinValue); + } + + return x; + } + + private static void ThrowMinMaxException(T min, T max) + { + throw new ArgumentException(SR.Format(SR.Argument_MinMaxValue, min, max)); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/MathF.cs b/external/corert/src/System.Private.CoreLib/shared/System/MathF.cs new file mode 100644 index 0000000000..5b7e48b062 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/MathF.cs @@ -0,0 +1,235 @@ +// 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. + +/*============================================================ +** +** Purpose: Some single-precision floating-point math operations +** +===========================================================*/ + +//This class contains only static members and doesn't require serialization. + +using System.Runtime; +using System.Runtime.CompilerServices; + +namespace System +{ + public static partial class MathF + { + public const float E = 2.71828183f; + + public const float PI = 3.14159265f; + + private const int maxRoundingDigits = 6; + + // This table is required for the Round function which can specify the number of digits to round to + private static float[] roundPower10Single = new float[] { + 1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f, 1e6f + }; + + private static float singleRoundLimit = 1e8f; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Abs(float x) + { + return Math.Abs(x); + } + + public static float IEEERemainder(float x, float y) + { + if (float.IsNaN(x)) + { + return x; // IEEE 754-2008: NaN payload must be preserved + } + + if (float.IsNaN(y)) + { + return y; // IEEE 754-2008: NaN payload must be preserved + } + + var regularMod = x % y; + + if (float.IsNaN(regularMod)) + { + return float.NaN; + } + + if ((regularMod == 0) && float.IsNegative(x)) + { + return float.NegativeZero; + } + + var alternativeResult = (regularMod - (Abs(y) * Sign(x))); + + if (Abs(alternativeResult) == Abs(regularMod)) + { + var divisionResult = x / y; + var roundedResult = Round(divisionResult); + + if (Abs(roundedResult) > Abs(divisionResult)) + { + return alternativeResult; + } + else + { + return regularMod; + } + } + + if (Abs(alternativeResult) < Abs(regularMod)) + { + return alternativeResult; + } + else + { + return regularMod; + } + } + + public static float Log(float x, float y) + { + if (float.IsNaN(x)) + { + return x; // IEEE 754-2008: NaN payload must be preserved + } + + if (float.IsNaN(y)) + { + return y; // IEEE 754-2008: NaN payload must be preserved + } + + if (y == 1) + { + return float.NaN; + } + + if ((x != 1) && ((y == 0) || float.IsPositiveInfinity(y))) + { + return float.NaN; + } + + return Log(x) / Log(y); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Max(float x, float y) + { + return Math.Max(x, y); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Min(float x, float y) + { + return Math.Min(x, y); + } + + [Intrinsic] + public static float Round(float x) + { + // ************************************************************************************ + // IMPORTANT: Do not change this implementation without also updating Math.Round(double), + // FloatingPointUtils::round(double), and FloatingPointUtils::round(float) + // ************************************************************************************ + + // If the number has no fractional part do nothing + // This shortcut is necessary to workaround precision loss in borderline cases on some platforms + + if (x == (float)((int)x)) + { + return x; + } + + // We had a number that was equally close to 2 integers. + // We need to return the even one. + + float flrTempVal = Floor(x + 0.5f); + + if ((x == (Floor(x) + 0.5f)) && (FMod(flrTempVal, 2.0f) != 0)) + { + flrTempVal -= 1.0f; + } + + return CopySign(flrTempVal, x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Round(float x, int digits) + { + return Round(x, digits, MidpointRounding.ToEven); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Round(float x, MidpointRounding mode) + { + return Round(x, 0, mode); + } + + public static unsafe float Round(float x, int digits, MidpointRounding mode) + { + if ((digits < 0) || (digits > maxRoundingDigits)) + { + throw new ArgumentOutOfRangeException(nameof(digits), SR.ArgumentOutOfRange_RoundingDigits); + } + + if (mode < MidpointRounding.ToEven || mode > MidpointRounding.AwayFromZero) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidEnum, mode, nameof(MidpointRounding)), nameof(mode)); + } + + if (Abs(x) < singleRoundLimit) + { + var power10 = roundPower10Single[digits]; + + x *= power10; + + if (mode == MidpointRounding.AwayFromZero) + { + var fraction = ModF(x, &x); + + if (Abs(fraction) >= 0.5f) + { + x += Sign(fraction); + } + } + else + { + x = Round(x); + } + + x /= power10; + } + + return x; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Sign(float x) + { + return Math.Sign(x); + } + + public static unsafe float Truncate(float x) + { + ModF(x, &x); + return x; + } + + private static unsafe float CopySign(float x, float y) + { + var xbits = BitConverter.SingleToInt32Bits(x); + var ybits = BitConverter.SingleToInt32Bits(y); + + // If the sign bits of x and y are not the same, + // flip the sign bit of x and return the new value; + // otherwise, just return x + + if (((xbits ^ ybits) >> 31) != 0) + { + return BitConverter.Int32BitsToSingle(xbits ^ int.MinValue); + } + + return x; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/MemberAccessException.cs b/external/corert/src/System.Private.CoreLib/shared/System/MemberAccessException.cs index abca952f19..dfea52dbed 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/MemberAccessException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/MemberAccessException.cs @@ -14,7 +14,8 @@ namespace System { // The MemberAccessException is thrown when trying to access a class // member fails. - // + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class MemberAccessException : SystemException { // Creates a new MemberAccessException with its message string set to @@ -23,7 +24,7 @@ namespace System public MemberAccessException() : base(SR.Arg_AccessException) { - HResult = __HResults.COR_E_MEMBERACCESS; + HResult = HResults.COR_E_MEMBERACCESS; } // Creates a new MemberAccessException with its message string set to @@ -33,18 +34,17 @@ namespace System public MemberAccessException(String message) : base(message) { - HResult = __HResults.COR_E_MEMBERACCESS; + HResult = HResults.COR_E_MEMBERACCESS; } public MemberAccessException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_MEMBERACCESS; + HResult = HResults.COR_E_MEMBERACCESS; } protected MemberAccessException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corefx/src/System.Memory/src/System/Memory.cs b/external/corert/src/System.Private.CoreLib/shared/System/Memory.cs similarity index 64% rename from external/corefx/src/System.Memory/src/System/Memory.cs rename to external/corert/src/System.Private.CoreLib/shared/System/Memory.cs index 491d7702f6..2286d699d3 100644 --- a/external/corefx/src/System.Memory/src/System/Memory.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Memory.cs @@ -3,29 +3,28 @@ // See the LICENSE file in the project root for more information. using System.Buffers; -#if !MONO -using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute; -using EditorBrowsableState = System.ComponentModel.EditorBrowsableState; -#endif using System.Diagnostics; -using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute; +using EditorBrowsableState = System.ComponentModel.EditorBrowsableState; +using Internal.Runtime.CompilerServices; namespace System { - /// - /// Memory represents a contiguous region of arbitrary memory similar to Span. - /// Unlike Span, it is not a byref-like type. - /// [DebuggerDisplay("{DebuggerDisplay,nq}")] [DebuggerTypeProxy(typeof(MemoryDebugView<>))] public readonly struct Memory { - // The highest order bit of _index is used to discern whether _arrayOrOwnedMemory is an array or an owned memory - // if (_index >> 31) == 1, object _arrayOrOwnedMemory is an OwnedMemory - // else, object _arrayOrOwnedMemory is a T[] - private readonly object _arrayOrOwnedMemory; + // NOTE: With the current implementation, Memory and ReadOnlyMemory must have the same layout, + // as code uses Unsafe.As to cast between them. + + // The highest order bit of _index is used to discern whether _object is an array/string or an owned memory + // if (_index >> 31) == 1, object _object is an OwnedMemory + // else, object _object is a T[] or a string. It can only be a string if the Memory was created by + // using unsafe / marshaling code to reinterpret a ReadOnlyMemory wrapped around a string as + // a Memory. + private readonly object _object; private readonly int _index; private readonly int _length; @@ -44,9 +43,9 @@ namespace System if (array == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); if (default(T) == null && array.GetType() != typeof(T[])) - ThrowHelper.ThrowArrayTypeMismatchException_ArrayTypeMustBeExactMatch(typeof(T)); + ThrowHelper.ThrowArrayTypeMismatchException(); - _arrayOrOwnedMemory = array; + _object = array; _index = 0; _length = array.Length; } @@ -70,11 +69,11 @@ namespace System if (array == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); if (default(T) == null && array.GetType() != typeof(T[])) - ThrowHelper.ThrowArrayTypeMismatchException_ArrayTypeMustBeExactMatch(typeof(T)); + ThrowHelper.ThrowArrayTypeMismatchException(); if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); + ThrowHelper.ThrowArgumentOutOfRangeException(); - _arrayOrOwnedMemory = array; + _object = array; _index = start; _length = length; } @@ -86,15 +85,21 @@ namespace System if (owner == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.ownedMemory); if (index < 0 || length < 0) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); + ThrowHelper.ThrowArgumentOutOfRangeException(); - _arrayOrOwnedMemory = owner; + _object = owner; _index = index | (1 << 31); // Before using _index, check if _index < 0, then 'and' it with RemoveOwnedFlagBitMask _length = length; } - //Debugger Display = {T[length]} - private string DebuggerDisplay => string.Format("{{{0}[{1}]}}", typeof(T).Name, _length); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Memory(object obj, int index, int length) + { + // No validation performed; caller must provide any necessary validation. + _object = obj; + _index = index; + _length = length; + } /// /// Defines an implicit conversion of an array to a @@ -109,18 +114,16 @@ namespace System /// /// Defines an implicit conversion of a to a /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator ReadOnlyMemory(Memory memory) - { - if (memory._index < 0) - return new ReadOnlyMemory((OwnedMemory)memory._arrayOrOwnedMemory, memory._index & RemoveOwnedFlagBitMask, memory._length); - return new ReadOnlyMemory((T[])memory._arrayOrOwnedMemory, memory._index, memory._length); - } + public static implicit operator ReadOnlyMemory(Memory memory) => + Unsafe.As, ReadOnlyMemory>(ref memory); + + //Debugger Display = {T[length]} + private string DebuggerDisplay => string.Format("{{{0}[{1}]}}", typeof(T).Name, _length); /// /// Returns an empty /// - public static Memory Empty { get; } = SpanHelpers.PerTypeValues.EmptyArray; + public static Memory Empty => default; /// /// The number of items in the memory. @@ -143,11 +146,11 @@ namespace System public Memory Slice(int start) { if ((uint)start > (uint)_length) + { ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); + } - if (_index < 0) - return new Memory((OwnedMemory)_arrayOrOwnedMemory, (_index & RemoveOwnedFlagBitMask) + start, _length - start); - return new Memory((T[])_arrayOrOwnedMemory, _index + start, _length - start); + return new Memory(_object, _index + start, _length - start); } /// @@ -162,11 +165,11 @@ namespace System public Memory Slice(int start, int length) { if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start)) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); - - if (_index < 0) - return new Memory((OwnedMemory)_arrayOrOwnedMemory, (_index & RemoveOwnedFlagBitMask) + start, length); - return new Memory((T[])_arrayOrOwnedMemory, _index + start, length); + { + ThrowHelper.ThrowArgumentOutOfRangeException(); + } + + return new Memory(_object, _index + start, length); } /// @@ -178,30 +181,77 @@ namespace System get { if (_index < 0) - return ((OwnedMemory)_arrayOrOwnedMemory).Span.Slice(_index & RemoveOwnedFlagBitMask, _length); - return new Span((T[])_arrayOrOwnedMemory, _index, _length); + { + return ((OwnedMemory)_object).Span.Slice(_index & RemoveOwnedFlagBitMask, _length); + } + else if (typeof(T) == typeof(char) && _object is string s) + { + // This is dangerous, returning a writable span for a string that should be immutable. + // However, we need to handle the case where a ReadOnlyMemory was created from a string + // and then cast to a Memory. Such a cast can only be done with unsafe or marshaling code, + // in which case that's the dangerous operation performed by the dev, and we're just following + // suit here to make it work as best as possible. + return new Span(ref Unsafe.As(ref s.GetRawStringData()), s.Length).Slice(_index, _length); + } + else if (_object != null) + { + return new Span((T[])_object, _index, _length); + } + else + { + return default; + } } } /// - /// Returns a handle for the array. - /// If pin is true, the GC will not move the array and hence its address can be taken + /// Copies the contents of the memory into the destination. If the source + /// and destination overlap, this method behaves as if the original values are in + /// a temporary location before the destination is overwritten. + /// + /// The Memory to copy items into. + /// + /// Thrown when the destination is shorter than the source. + /// /// + public void CopyTo(Memory destination) => Span.CopyTo(destination.Span); + + /// + /// Copies the contents of the memory into the destination. If the source + /// and destination overlap, this method behaves as if the original values are in + /// a temporary location before the destination is overwritten. + /// + /// If the destination is shorter than the source, this method + /// return false and no data is written to the destination. + /// + /// The span to copy items into. + public bool TryCopyTo(Memory destination) => Span.TryCopyTo(destination.Span); + public unsafe MemoryHandle Retain(bool pin = false) { - MemoryHandle memoryHandle; + MemoryHandle memoryHandle = default; if (pin) { if (_index < 0) { - memoryHandle = ((OwnedMemory)_arrayOrOwnedMemory).Pin(); + memoryHandle = ((OwnedMemory)_object).Pin(); memoryHandle.AddOffset((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf()); } - else + else if (typeof(T) == typeof(char) && _object is string s) + { + // This case can only happen if a ReadOnlyMemory was created around a string + // and then that was cast to a Memory using unsafe / marshaling code. This needs + // to work, however, so that code that uses a single Memory field to store either + // a readable ReadOnlyMemory or a writable Memory can still be pinned and + // used for interop purposes. + GCHandle handle = GCHandle.Alloc(s, GCHandleType.Pinned); + void* pointer = Unsafe.Add(Unsafe.AsPointer(ref s.GetRawStringData()), _index); + memoryHandle = new MemoryHandle(null, pointer, handle); + } + else if (_object is T[] array) { - var array = (T[])_arrayOrOwnedMemory; var handle = GCHandle.Alloc(array, GCHandleType.Pinned); - void* pointer = Unsafe.Add((void*)handle.AddrOfPinnedObject(), _index); + void* pointer = Unsafe.Add(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index); memoryHandle = new MemoryHandle(null, pointer, handle); } } @@ -209,12 +259,8 @@ namespace System { if (_index < 0) { - ((OwnedMemory)_arrayOrOwnedMemory).Retain(); - memoryHandle = new MemoryHandle((OwnedMemory)_arrayOrOwnedMemory); - } - else - { - memoryHandle = new MemoryHandle(null); + ((OwnedMemory)_object).Retain(); + memoryHandle = new MemoryHandle((OwnedMemory)_object); } } return memoryHandle; @@ -228,15 +274,15 @@ namespace System { if (_index < 0) { - if (((OwnedMemory)_arrayOrOwnedMemory).TryGetArray(out var segment)) + if (((OwnedMemory)_object).TryGetArray(out var segment)) { arraySegment = new ArraySegment(segment.Array, segment.Offset + (_index & RemoveOwnedFlagBitMask), _length); return true; } } - else + else if (_object is T[] arr) { - arraySegment = new ArraySegment((T[])_arrayOrOwnedMemory, _index, _length); + arraySegment = new ArraySegment(arr, _index, _length); return true; } @@ -251,13 +297,7 @@ namespace System /// public T[] ToArray() => Span.ToArray(); - /// - /// Determines whether the specified object is equal to the current object. - /// Returns true if the object is Memory or ReadOnlyMemory and if both objects point to the same array and have the same length. - /// -#if !MONO [EditorBrowsable(EditorBrowsableState.Never)] -#endif public override bool Equals(object obj) { if (obj is ReadOnlyMemory) @@ -281,20 +321,15 @@ namespace System public bool Equals(Memory other) { return - _arrayOrOwnedMemory == other._arrayOrOwnedMemory && + _object == other._object && _index == other._index && _length == other._length; } - /// - /// Serves as the default hash function. - /// -#if !MONO [EditorBrowsable(EditorBrowsableState.Never)] -#endif public override int GetHashCode() { - return CombineHashCodes(_arrayOrOwnedMemory.GetHashCode(), (_index & RemoveOwnedFlagBitMask).GetHashCode(), _length.GetHashCode()); + return _object != null ? CombineHashCodes(_object.GetHashCode(), _index.GetHashCode(), _length.GetHashCode()) : 0; } private static int CombineHashCodes(int left, int right) diff --git a/external/corefx/src/System.Memory/src/System/MemoryDebugView.cs b/external/corert/src/System.Private.CoreLib/shared/System/MemoryDebugView.cs similarity index 70% rename from external/corefx/src/System.Memory/src/System/MemoryDebugView.cs rename to external/corert/src/System.Private.CoreLib/shared/System/MemoryDebugView.cs index 3ade65f268..2706d09279 100644 --- a/external/corefx/src/System.Memory/src/System/MemoryDebugView.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/MemoryDebugView.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Runtime.InteropServices; namespace System { @@ -27,13 +28,20 @@ namespace System // https://devdiv.visualstudio.com/DevDiv/_workitems?id=286592 get { - if (_memory.DangerousTryGetArray(out ArraySegment segment)) + if (MemoryMarshal.TryGetArray(_memory, out ArraySegment segment)) { T[] array = new T[_memory.Length]; Array.Copy(segment.Array, segment.Offset, array, 0, array.Length); return array; } - return SpanHelpers.PerTypeValues.EmptyArray; + + if (typeof(T) == typeof(char) && + ((ReadOnlyMemory)(object)_memory).TryGetString(out string text, out int start, out int length)) + { + return (T[])(object)text.Substring(start, length).ToCharArray(); + } + + return Array.Empty(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/MethodAccessException.cs b/external/corert/src/System.Private.CoreLib/shared/System/MethodAccessException.cs index 2c9c998c15..1ca0297b94 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/MethodAccessException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/MethodAccessException.cs @@ -13,29 +13,30 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class MethodAccessException : MemberAccessException { public MethodAccessException() : base(SR.Arg_MethodAccessException) { - HResult = __HResults.COR_E_METHODACCESS; + HResult = HResults.COR_E_METHODACCESS; } public MethodAccessException(String message) : base(message) { - HResult = __HResults.COR_E_METHODACCESS; + HResult = HResults.COR_E_METHODACCESS; } public MethodAccessException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_METHODACCESS; + HResult = HResults.COR_E_METHODACCESS; } protected MethodAccessException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/MissingMethodException.cs b/external/corert/src/System.Private.CoreLib/shared/System/MissingMethodException.cs index 967f434302..abb6c0e97b 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/MissingMethodException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/MissingMethodException.cs @@ -15,24 +15,26 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class MissingMethodException : MissingMemberException { public MissingMethodException() : base(SR.Arg_MissingMethodException) { - HResult = __HResults.COR_E_MISSINGMETHOD; + HResult = HResults.COR_E_MISSINGMETHOD; } public MissingMethodException(string message) : base(message) { - HResult = __HResults.COR_E_MISSINGMETHOD; + HResult = HResults.COR_E_MISSINGMETHOD; } public MissingMethodException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_MISSINGMETHOD; + HResult = HResults.COR_E_MISSINGMETHOD; } public MissingMethodException(string className, string methodName) @@ -44,7 +46,6 @@ namespace System protected MissingMethodException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public override string Message diff --git a/external/corert/src/System.Private.CoreLib/shared/System/MulticastNotSupportedException.cs b/external/corert/src/System.Private.CoreLib/shared/System/MulticastNotSupportedException.cs index 493671e227..cc6c77023e 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/MulticastNotSupportedException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/MulticastNotSupportedException.cs @@ -11,24 +11,30 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class MulticastNotSupportedException : SystemException { public MulticastNotSupportedException() : base(SR.Arg_MulticastNotSupportedException) { - HResult = __HResults.COR_E_MULTICASTNOTSUPPORTED; + HResult = HResults.COR_E_MULTICASTNOTSUPPORTED; } public MulticastNotSupportedException(String message) : base(message) { - HResult = __HResults.COR_E_MULTICASTNOTSUPPORTED; + HResult = HResults.COR_E_MULTICASTNOTSUPPORTED; } public MulticastNotSupportedException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_MULTICASTNOTSUPPORTED; + HResult = HResults.COR_E_MULTICASTNOTSUPPORTED; + } + + internal MulticastNotSupportedException(SerializationInfo info, StreamingContext context) : base(info, context) + { } } } diff --git a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationTextInput.cs b/external/corert/src/System.Private.CoreLib/shared/System/NonSerializedAttribute.cs similarity index 51% rename from external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationTextInput.cs rename to external/corert/src/System.Private.CoreLib/shared/System/NonSerializedAttribute.cs index d0c8d2f34a..cabd5a2aa2 100644 --- a/external/corefx/src/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationTextInput.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/NonSerializedAttribute.cs @@ -2,13 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace System.ServiceModel.Syndication +namespace System { - public class SyndicationTextInput + [AttributeUsage(AttributeTargets.Field, Inherited = false)] + public sealed class NonSerializedAttribute : Attribute { - public string Description; - public string title; - public SyndicationLink link; - public string name; + public NonSerializedAttribute() + { + } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/NotFiniteNumberException.cs b/external/corert/src/System.Private.CoreLib/shared/System/NotFiniteNumberException.cs index 99882b9eb2..b9c9af06d3 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/NotFiniteNumberException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/NotFiniteNumberException.cs @@ -6,6 +6,8 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class NotFiniteNumberException : ArithmeticException { private double _offendingNumber; @@ -14,51 +16,52 @@ namespace System : base(SR.Arg_NotFiniteNumberException) { _offendingNumber = 0; - HResult = __HResults.COR_E_NOTFINITENUMBER; + HResult = HResults.COR_E_NOTFINITENUMBER; } public NotFiniteNumberException(double offendingNumber) : base() { _offendingNumber = offendingNumber; - HResult = __HResults.COR_E_NOTFINITENUMBER; + HResult = HResults.COR_E_NOTFINITENUMBER; } - public NotFiniteNumberException(String message) + public NotFiniteNumberException(string message) : base(message) { _offendingNumber = 0; - HResult = __HResults.COR_E_NOTFINITENUMBER; + HResult = HResults.COR_E_NOTFINITENUMBER; } - public NotFiniteNumberException(String message, double offendingNumber) + public NotFiniteNumberException(string message, double offendingNumber) : base(message) { _offendingNumber = offendingNumber; - HResult = __HResults.COR_E_NOTFINITENUMBER; + HResult = HResults.COR_E_NOTFINITENUMBER; } - public NotFiniteNumberException(String message, Exception innerException) + public NotFiniteNumberException(string message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_NOTFINITENUMBER; + HResult = HResults.COR_E_NOTFINITENUMBER; } - public NotFiniteNumberException(String message, double offendingNumber, Exception innerException) + public NotFiniteNumberException(string message, double offendingNumber, Exception innerException) : base(message, innerException) { _offendingNumber = offendingNumber; - HResult = __HResults.COR_E_NOTFINITENUMBER; + HResult = HResults.COR_E_NOTFINITENUMBER; } protected NotFiniteNumberException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); + _offendingNumber = info.GetInt32("OffendingNumber"); } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + info.AddValue("OffendingNumber", _offendingNumber, typeof(int)); } public double OffendingNumber diff --git a/external/corert/src/System.Private.CoreLib/shared/System/NotImplementedException.cs b/external/corert/src/System.Private.CoreLib/shared/System/NotImplementedException.cs index ae62527fac..1a3b6afcd4 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/NotImplementedException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/NotImplementedException.cs @@ -16,27 +16,28 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class NotImplementedException : SystemException { public NotImplementedException() : base(SR.Arg_NotImplementedException) { - HResult = __HResults.E_NOTIMPL; + HResult = HResults.E_NOTIMPL; } public NotImplementedException(String message) : base(message) { - HResult = __HResults.E_NOTIMPL; + HResult = HResults.E_NOTIMPL; } public NotImplementedException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.E_NOTIMPL; + HResult = HResults.E_NOTIMPL; } protected NotImplementedException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/NotSupportedException.cs b/external/corert/src/System.Private.CoreLib/shared/System/NotSupportedException.cs index 8532e5ad2f..3180bc2837 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/NotSupportedException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/NotSupportedException.cs @@ -15,29 +15,30 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class NotSupportedException : SystemException { public NotSupportedException() : base(SR.Arg_NotSupportedException) { - HResult = __HResults.COR_E_NOTSUPPORTED; + HResult = HResults.COR_E_NOTSUPPORTED; } public NotSupportedException(String message) : base(message) { - HResult = __HResults.COR_E_NOTSUPPORTED; + HResult = HResults.COR_E_NOTSUPPORTED; } public NotSupportedException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_NOTSUPPORTED; + HResult = HResults.COR_E_NOTSUPPORTED; } protected NotSupportedException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/NullReferenceException.cs b/external/corert/src/System.Private.CoreLib/shared/System/NullReferenceException.cs index f689345654..c2e722470c 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/NullReferenceException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/NullReferenceException.cs @@ -15,29 +15,30 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class NullReferenceException : SystemException { public NullReferenceException() : base(SR.Arg_NullReferenceException) { - HResult = __HResults.COR_E_NULLREFERENCE; + HResult = HResults.COR_E_NULLREFERENCE; } public NullReferenceException(String message) : base(message) { - HResult = __HResults.COR_E_NULLREFERENCE; + HResult = HResults.COR_E_NULLREFERENCE; } public NullReferenceException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_NULLREFERENCE; + HResult = HResults.COR_E_NULLREFERENCE; } protected NullReferenceException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Nullable.cs b/external/corert/src/System.Private.CoreLib/shared/System/Nullable.cs new file mode 100644 index 0000000000..73ad6056c2 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Nullable.cs @@ -0,0 +1,152 @@ +// 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.Collections.Generic; +using System.Runtime.Versioning; + +namespace System +{ + // Because we have special type system support that says a a boxed Nullable + // can be used where a boxed is use, Nullable can not implement any intefaces + // at all (since T may not). Do NOT add any interfaces to Nullable! + // + [Serializable] + [NonVersionable] // This only applies to field layout + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Nullable where T : struct + { + private readonly bool hasValue; // Do not rename (binary serialization) + internal T value; // Do not rename (binary serialization) or make readonly (can be mutated in ToString, etc.) + + [NonVersionable] + public Nullable(T value) + { + this.value = value; + hasValue = true; + } + + public bool HasValue + { + [NonVersionable] + get + { + return hasValue; + } + } + + public T Value + { + get + { + if (!hasValue) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_NoValue(); + } + return value; + } + } + + [NonVersionable] + public T GetValueOrDefault() + { + return value; + } + + [NonVersionable] + public T GetValueOrDefault(T defaultValue) + { + return hasValue ? value : defaultValue; + } + + public override bool Equals(object other) + { + if (!hasValue) return other == null; + if (other == null) return false; + return value.Equals(other); + } + + public override int GetHashCode() + { + return hasValue ? value.GetHashCode() : 0; + } + + public override string ToString() + { + return hasValue ? value.ToString() : ""; + } + + [NonVersionable] + public static implicit operator Nullable(T value) + { + return new Nullable(value); + } + + [NonVersionable] + public static explicit operator T(Nullable value) + { + return value.Value; + } + } + + public static class Nullable + { + public static int Compare(Nullable n1, Nullable n2) where T : struct + { + if (n1.HasValue) + { + if (n2.HasValue) return Comparer.Default.Compare(n1.value, n2.value); + return 1; + } + if (n2.HasValue) return -1; + return 0; + } + + public static bool Equals(Nullable n1, Nullable n2) where T : struct + { + if (n1.HasValue) + { + if (n2.HasValue) return EqualityComparer.Default.Equals(n1.value, n2.value); + return false; + } + if (n2.HasValue) return false; + return true; + } + + // If the type provided is not a Nullable Type, return null. + // Otherwise, returns the underlying type of the Nullable type + public static Type GetUnderlyingType(Type nullableType) + { + if ((object)nullableType == null) + { + throw new ArgumentNullException(nameof(nullableType)); + } + +#if CORERT + // This is necessary to handle types without reflection metadata + if (nullableType.TryGetEEType(out EETypePtr nullableEEType)) + { + if (nullableEEType.IsGeneric) + { + if (nullableEEType.IsNullable) + { + return Internal.Reflection.Core.NonPortable.RuntimeTypeUnifier.GetRuntimeTypeForEEType(nullableEEType.NullableType); + } + } + return null; + } +#endif + + if (nullableType.IsGenericType && !nullableType.IsGenericTypeDefinition) + { + // instantiated generic type only + Type genericType = nullableType.GetGenericTypeDefinition(); + if (Object.ReferenceEquals(genericType, typeof(Nullable<>))) + { + return nullableType.GetGenericArguments()[0]; + } + } + return null; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Number.Formatting.cs b/external/corert/src/System.Private.CoreLib/shared/System/Number.Formatting.cs new file mode 100644 index 0000000000..70b35a08aa --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Number.Formatting.cs @@ -0,0 +1,2237 @@ +// 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.Diagnostics; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace System +{ + // The Format methods provided by the numeric classes convert + // the numeric value to a string using the format string given by the + // format parameter. If the format parameter is null or + // an empty string, the number is formatted as if the string "G" (general + // format) was specified. The info parameter specifies the + // NumberFormatInfo instance to use when formatting the number. If the + // info parameter is null or omitted, the numeric formatting information + // is obtained from the current culture. The NumberFormatInfo supplies + // such information as the characters to use for decimal and thousand + // separators, and the spelling and placement of currency symbols in monetary + // values. + // + // Format strings fall into two categories: Standard format strings and + // user-defined format strings. A format string consisting of a single + // alphabetic character (A-Z or a-z), optionally followed by a sequence of + // digits (0-9), is a standard format string. All other format strings are + // used-defined format strings. + // + // A standard format string takes the form Axx, where A is an + // alphabetic character called the format specifier and xx is a + // sequence of digits called the precision specifier. The format + // specifier controls the type of formatting applied to the number and the + // precision specifier controls the number of significant digits or decimal + // places of the formatting operation. The following table describes the + // supported standard formats. + // + // C c - Currency format. The number is + // converted to a string that represents a currency amount. The conversion is + // controlled by the currency format information of the NumberFormatInfo + // used to format the number. The precision specifier indicates the desired + // number of decimal places. If the precision specifier is omitted, the default + // currency precision given by the NumberFormatInfo is used. + // + // D d - Decimal format. This format is + // supported for integral types only. The number is converted to a string of + // decimal digits, prefixed by a minus sign if the number is negative. The + // precision specifier indicates the minimum number of digits desired in the + // resulting string. If required, the number will be left-padded with zeros to + // produce the number of digits given by the precision specifier. + // + // E e Engineering (scientific) format. + // The number is converted to a string of the form + // "-d.ddd...E+ddd" or "-d.ddd...e+ddd", where each + // 'd' indicates a digit (0-9). The string starts with a minus sign if the + // number is negative, and one digit always precedes the decimal point. The + // precision specifier indicates the desired number of digits after the decimal + // point. If the precision specifier is omitted, a default of 6 digits after + // the decimal point is used. The format specifier indicates whether to prefix + // the exponent with an 'E' or an 'e'. The exponent is always consists of a + // plus or minus sign and three digits. + // + // F f Fixed point format. The number is + // converted to a string of the form "-ddd.ddd....", where each + // 'd' indicates a digit (0-9). The string starts with a minus sign if the + // number is negative. The precision specifier indicates the desired number of + // decimal places. If the precision specifier is omitted, the default numeric + // precision given by the NumberFormatInfo is used. + // + // G g - General format. The number is + // converted to the shortest possible decimal representation using fixed point + // or scientific format. The precision specifier determines the number of + // significant digits in the resulting string. If the precision specifier is + // omitted, the number of significant digits is determined by the type of the + // number being converted (10 for int, 19 for long, 7 for + // float, 15 for double, 19 for Currency, and 29 for + // Decimal). Trailing zeros after the decimal point are removed, and the + // resulting string contains a decimal point only if required. The resulting + // string uses fixed point format if the exponent of the number is less than + // the number of significant digits and greater than or equal to -4. Otherwise, + // the resulting string uses scientific format, and the case of the format + // specifier controls whether the exponent is prefixed with an 'E' or an 'e'. + // + // N n Number format. The number is + // converted to a string of the form "-d,ddd,ddd.ddd....", where + // each 'd' indicates a digit (0-9). The string starts with a minus sign if the + // number is negative. Thousand separators are inserted between each group of + // three digits to the left of the decimal point. The precision specifier + // indicates the desired number of decimal places. If the precision specifier + // is omitted, the default numeric precision given by the + // NumberFormatInfo is used. + // + // X x - Hexadecimal format. This format is + // supported for integral types only. The number is converted to a string of + // hexadecimal digits. The format specifier indicates whether to use upper or + // lower case characters for the hexadecimal digits above 9 ('X' for 'ABCDEF', + // and 'x' for 'abcdef'). The precision specifier indicates the minimum number + // of digits desired in the resulting string. If required, the number will be + // left-padded with zeros to produce the number of digits given by the + // precision specifier. + // + // Some examples of standard format strings and their results are shown in the + // table below. (The examples all assume a default NumberFormatInfo.) + // + // Value Format Result + // 12345.6789 C $12,345.68 + // -12345.6789 C ($12,345.68) + // 12345 D 12345 + // 12345 D8 00012345 + // 12345.6789 E 1.234568E+004 + // 12345.6789 E10 1.2345678900E+004 + // 12345.6789 e4 1.2346e+004 + // 12345.6789 F 12345.68 + // 12345.6789 F0 12346 + // 12345.6789 F6 12345.678900 + // 12345.6789 G 12345.6789 + // 12345.6789 G7 12345.68 + // 123456789 G7 1.234568E8 + // 12345.6789 N 12,345.68 + // 123456789 N4 123,456,789.0000 + // 0x2c45e x 2c45e + // 0x2c45e X 2C45E + // 0x2c45e X8 0002C45E + // + // Format strings that do not start with an alphabetic character, or that start + // with an alphabetic character followed by a non-digit, are called + // user-defined format strings. The following table describes the formatting + // characters that are supported in user defined format strings. + // + // + // 0 - Digit placeholder. If the value being + // formatted has a digit in the position where the '0' appears in the format + // string, then that digit is copied to the output string. Otherwise, a '0' is + // stored in that position in the output string. The position of the leftmost + // '0' before the decimal point and the rightmost '0' after the decimal point + // determines the range of digits that are always present in the output + // string. + // + // # - Digit placeholder. If the value being + // formatted has a digit in the position where the '#' appears in the format + // string, then that digit is copied to the output string. Otherwise, nothing + // is stored in that position in the output string. + // + // . - Decimal point. The first '.' character + // in the format string determines the location of the decimal separator in the + // formatted value; any additional '.' characters are ignored. The actual + // character used as a the decimal separator in the output string is given by + // the NumberFormatInfo used to format the number. + // + // , - Thousand separator and number scaling. + // The ',' character serves two purposes. First, if the format string contains + // a ',' character between two digit placeholders (0 or #) and to the left of + // the decimal point if one is present, then the output will have thousand + // separators inserted between each group of three digits to the left of the + // decimal separator. The actual character used as a the decimal separator in + // the output string is given by the NumberFormatInfo used to format the + // number. Second, if the format string contains one or more ',' characters + // immediately to the left of the decimal point, or after the last digit + // placeholder if there is no decimal point, then the number will be divided by + // 1000 times the number of ',' characters before it is formatted. For example, + // the format string '0,,' will represent 100 million as just 100. Use of the + // ',' character to indicate scaling does not also cause the formatted number + // to have thousand separators. Thus, to scale a number by 1 million and insert + // thousand separators you would use the format string '#,##0,,'. + // + // % - Percentage placeholder. The presence of + // a '%' character in the format string causes the number to be multiplied by + // 100 before it is formatted. The '%' character itself is inserted in the + // output string where it appears in the format string. + // + // E+ E- e+ e- - Scientific notation. + // If any of the strings 'E+', 'E-', 'e+', or 'e-' are present in the format + // string and are immediately followed by at least one '0' character, then the + // number is formatted using scientific notation with an 'E' or 'e' inserted + // between the number and the exponent. The number of '0' characters following + // the scientific notation indicator determines the minimum number of digits to + // output for the exponent. The 'E+' and 'e+' formats indicate that a sign + // character (plus or minus) should always precede the exponent. The 'E-' and + // 'e-' formats indicate that a sign character should only precede negative + // exponents. + // + // \ - Literal character. A backslash character + // causes the next character in the format string to be copied to the output + // string as-is. The backslash itself isn't copied, so to place a backslash + // character in the output string, use two backslashes (\\) in the format + // string. + // + // 'ABC' "ABC" - Literal string. Characters + // enclosed in single or double quotation marks are copied to the output string + // as-is and do not affect formatting. + // + // ; - Section separator. The ';' character is + // used to separate sections for positive, negative, and zero numbers in the + // format string. + // + // Other - All other characters are copied to + // the output string in the position they appear. + // + // For fixed point formats (formats not containing an 'E+', 'E-', 'e+', or + // 'e-'), the number is rounded to as many decimal places as there are digit + // placeholders to the right of the decimal point. If the format string does + // not contain a decimal point, the number is rounded to the nearest + // integer. If the number has more digits than there are digit placeholders to + // the left of the decimal point, the extra digits are copied to the output + // string immediately before the first digit placeholder. + // + // For scientific formats, the number is rounded to as many significant digits + // as there are digit placeholders in the format string. + // + // To allow for different formatting of positive, negative, and zero values, a + // user-defined format string may contain up to three sections separated by + // semicolons. The results of having one, two, or three sections in the format + // string are described in the table below. + // + // Sections: + // + // One - The format string applies to all values. + // + // Two - The first section applies to positive values + // and zeros, and the second section applies to negative values. If the number + // to be formatted is negative, but becomes zero after rounding according to + // the format in the second section, then the resulting zero is formatted + // according to the first section. + // + // Three - The first section applies to positive + // values, the second section applies to negative values, and the third section + // applies to zeros. The second section may be left empty (by having no + // characters between the semicolons), in which case the first section applies + // to all non-zero values. If the number to be formatted is non-zero, but + // becomes zero after rounding according to the format in the first or second + // section, then the resulting zero is formatted according to the third + // section. + // + // For both standard and user-defined formatting operations on values of type + // float and double, if the value being formatted is a NaN (Not + // a Number) or a positive or negative infinity, then regardless of the format + // string, the resulting string is given by the NaNSymbol, + // PositiveInfinitySymbol, or NegativeInfinitySymbol property of + // the NumberFormatInfo used to format the number. + + internal static partial class Number + { + internal const int DecimalPrecision = 29; // Decimal.DecCalc also uses this value + private const int FloatPrecision = 7; + private const int DoublePrecision = 15; + private const int ScaleNAN = unchecked((int)0x80000000); + private const int ScaleINF = 0x7FFFFFFF; + private const int MaxUInt32HexDigits = 8; + private const int MaxUInt32DecDigits = 10; + private const int MaxUInt64DecDigits = 20; + private const int CharStackBufferSize = 32; + private const string PosNumberFormat = "#"; + + private static readonly string[] s_posCurrencyFormats = + { + "$#", "#$", "$ #", "# $" + }; + + private static readonly string[] s_negCurrencyFormats = + { + "($#)", "-$#", "$-#", "$#-", + "(#$)", "-#$", "#-$", "#$-", + "-# $", "-$ #", "# $-", "$ #-", + "$ -#", "#- $", "($ #)", "(# $)" + }; + + private static readonly string[] s_posPercentFormats = + { + "# %", "#%", "%#", "% #" + }; + + private static readonly string[] s_negPercentFormats = + { + "-# %", "-#%", "-%#", + "%-#", "%#-", + "#-%", "#%-", + "-% #", "# %-", "% #-", + "% -#", "#- %" + }; + + private static readonly string[] s_negNumberFormats = + { + "(#)", "-#", "- #", "#-", "# -", + }; + + public static string FormatDecimal(decimal value, ReadOnlySpan format, NumberFormatInfo info) + { + char fmt = ParseFormatSpecifier(format, out int digits); + + NumberBuffer number = default; + DecimalToNumber(value, ref number); + + ValueStringBuilder sb; + unsafe + { + char* stackPtr = stackalloc char[CharStackBufferSize]; + sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + } + + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, isDecimal:true); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + + return sb.ToString(); + } + + public static bool TryFormatDecimal(decimal value, ReadOnlySpan format, NumberFormatInfo info, Span destination, out int charsWritten) + { + char fmt = ParseFormatSpecifier(format, out int digits); + + NumberBuffer number = default; + DecimalToNumber(value, ref number); + + ValueStringBuilder sb; + unsafe + { + char* stackPtr = stackalloc char[CharStackBufferSize]; + sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + } + + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, isDecimal: true); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + + return sb.TryCopyTo(destination, out charsWritten); + } + + private static unsafe void DecimalToNumber(decimal value, ref NumberBuffer number) + { + decimal d = value; + + char* buffer = number.digits; + number.precision = DecimalPrecision; + number.sign = d.IsNegative; + + char* p = buffer + DecimalPrecision; + while ((d.Mid | d.High) != 0) + { + p = UInt32ToDecChars(p, decimal.DecDivMod1E9(ref d), 9); + } + p = UInt32ToDecChars(p, d.Low, 0); + + int i = (int)(buffer + DecimalPrecision - p); + number.scale = i - d.Scale; + + char* dst = number.digits; + while (--i >= 0) + { + *dst++ = *p++; + } + *dst = '\0'; + } + + public static string FormatDouble(double value, string format, NumberFormatInfo info) + { + Span stackBuffer = stackalloc char[CharStackBufferSize]; + var sb = new ValueStringBuilder(stackBuffer); + return FormatDouble(ref sb, value, format, info) ?? sb.ToString(); + } + + public static bool TryFormatDouble(double value, ReadOnlySpan format, NumberFormatInfo info, Span destination, out int charsWritten) + { + Span stackBuffer = stackalloc char[CharStackBufferSize]; + var sb = new ValueStringBuilder(stackBuffer); + string s = FormatDouble(ref sb, value, format, info); + return s != null ? + TryCopyTo(s, destination, out charsWritten) : + sb.TryCopyTo(destination, out charsWritten); + } + + /// Formats the specified value according to the specified format and info. + /// + /// Non-null if an existing string can be returned, in which case the builder will be unmodified. + /// Null if no existing string was returned, in which case the formatted output is in the builder. + /// + private static string FormatDouble(ref ValueStringBuilder sb, double value, ReadOnlySpan format, NumberFormatInfo info) + { + char fmt = ParseFormatSpecifier(format, out int digits); + int precision = DoublePrecision; + NumberBuffer number = default; + + switch (fmt) + { + case 'R': + case 'r': + { + // In order to give numbers that are both friendly to display and round-trippable, we parse the + // number using 15 digits and then determine if it round trips to the same value. If it does, we + // convert that NUMBER to a string, otherwise we reparse using 17 digits and display that. + DoubleToNumber(value, DoublePrecision, ref number); + if (number.scale == ScaleNAN) + { + return info.NaNSymbol; + } + else if (number.scale == ScaleINF) + { + return number.sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol; + } + + if (NumberToDouble(ref number) == value) + { + NumberToString(ref sb, ref number, 'G', DoublePrecision, info, isDecimal: false); + } + else + { + DoubleToNumber(value, 17, ref number); + NumberToString(ref sb, ref number, 'G', 17, info, isDecimal: false); + } + + return null; + } + + case 'E': + case 'e': + // Round values less than E14 to 15 digits + if (digits > 14) + { + precision = 17; + } + break; + + case 'G': + case 'g': + // Round values less than G15 to 15 digits. G16 and G17 will not be touched. + if (digits > 15) + { + precision = 17; + } + break; + } + + DoubleToNumber(value, precision, ref number); + if (number.scale == ScaleNAN) + { + return info.NaNSymbol; + } + else if (number.scale == ScaleINF) + { + return number.sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol; + } + + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, isDecimal: false); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + + return null; + } + + public static string FormatSingle(float value, string format, NumberFormatInfo info) + { + Span stackBuffer = stackalloc char[CharStackBufferSize]; + var sb = new ValueStringBuilder(stackBuffer); + return FormatSingle(ref sb, value, format, info) ?? sb.ToString(); + } + + public static bool TryFormatSingle(float value, ReadOnlySpan format, NumberFormatInfo info, Span destination, out int charsWritten) + { + Span stackBuffer = stackalloc char[CharStackBufferSize]; + var sb = new ValueStringBuilder(stackBuffer); + string s = FormatSingle(ref sb, value, format, info); + return s != null ? + TryCopyTo(s, destination, out charsWritten) : + sb.TryCopyTo(destination, out charsWritten); + } + + /// Formats the specified value according to the specified format and info. + /// + /// Non-null if an existing string can be returned, in which case the builder will be unmodified. + /// Null if no existing string was returned, in which case the formatted output is in the builder. + /// + private static string FormatSingle(ref ValueStringBuilder sb, float value, ReadOnlySpan format, NumberFormatInfo info) + { + char fmt = ParseFormatSpecifier(format, out int digits); + int precision = FloatPrecision; + NumberBuffer number = default; + + switch (fmt) + { + case 'R': + case 'r': + { + // In order to give numbers that are both friendly to display and round-trippable, we parse the + // number using 7 digits and then determine if it round trips to the same value. If it does, we + // convert that NUMBER to a string, otherwise we reparse using 9 digits and display that. + DoubleToNumber(value, FloatPrecision, ref number); + if (number.scale == ScaleNAN) + { + return info.NaNSymbol; + } + else if (number.scale == ScaleINF) + { + return number.sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol; + } + + if ((float)NumberToDouble(ref number) == value) + { + NumberToString(ref sb, ref number, 'G', FloatPrecision, info, isDecimal: false); + } + else + { + DoubleToNumber(value, 9, ref number); + NumberToString(ref sb, ref number, 'G', 9, info, isDecimal: false); + } + return null; + } + + case 'E': + case 'e': + // Round values less than E14 to 15 digits. + if (digits > 6) + { + precision = 9; + } + break; + + case 'G': + case 'g': + // Round values less than G15 to 15 digits. G16 and G17 will not be touched. + if (digits > 7) + { + precision = 9; + } + break; + } + + DoubleToNumber(value, precision, ref number); + if (number.scale == ScaleNAN) + { + return info.NaNSymbol; + } + else if (number.scale == ScaleINF) + { + return number.sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol; + } + + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, false); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + return null; + } + + private static bool TryCopyTo(string source, Span destination, out int charsWritten) + { + Debug.Assert(source != null); + + if (source.AsReadOnlySpan().TryCopyTo(destination)) + { + charsWritten = source.Length; + return true; + } + + charsWritten = 0; + return false; + } + + public static string FormatInt32(int value, ReadOnlySpan format, IFormatProvider provider) + { + // Fast path for default format with a non-negative value + if (value >= 0 && format.Length == 0) + { + return UInt32ToDecStr((uint)value, digits: -1); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') + { + return value >= 0 ? + UInt32ToDecStr((uint)value, digits) : + NegativeInt32ToDecStr(value, digits, info.NegativeSign); + } + else if (fmtUpper == 'X') + { + // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase + // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code produces lowercase. + return Int32ToHexStr(value, (char)(fmt - ('X' - 'A' + 10)), digits); + } + else + { + NumberBuffer number = default; + Int32ToNumber(value, ref number); + ValueStringBuilder sb; + unsafe + { + char* stackPtr = stackalloc char[CharStackBufferSize]; + sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + } + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, false); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + return sb.ToString(); + } + } + + public static bool TryFormatInt32(int value, ReadOnlySpan format, IFormatProvider provider, Span destination, out int charsWritten) + { + // Fast path for default format with a non-negative value + if (value >= 0 && format.Length == 0) + { + return TryUInt32ToDecStr((uint)value, digits: -1, destination, out charsWritten); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') + { + return value >= 0 ? + TryUInt32ToDecStr((uint)value, digits, destination, out charsWritten) : + TryNegativeInt32ToDecStr(value, digits, info.NegativeSign, destination, out charsWritten); + } + else if (fmtUpper == 'X') + { + // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase + // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code produces lowercase. + return TryInt32ToHexStr(value, (char)(fmt - ('X' - 'A' + 10)), digits, destination, out charsWritten); + } + else + { + NumberBuffer number = default; + Int32ToNumber(value, ref number); + ValueStringBuilder sb; + unsafe + { + char* stackPtr = stackalloc char[CharStackBufferSize]; + sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + } + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, false); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + return sb.TryCopyTo(destination, out charsWritten); + } + } + + public static string FormatUInt32(uint value, ReadOnlySpan format, IFormatProvider provider) + { + // Fast path for default format + if (format.Length == 0) + { + return UInt32ToDecStr(value, digits: -1); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') + { + return UInt32ToDecStr(value, digits); + } + else if (fmtUpper == 'X') + { + // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase + // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code produces lowercase. + return Int32ToHexStr((int)value, (char)(fmt - ('X' - 'A' + 10)), digits); + } + else + { + NumberBuffer number = default; + UInt32ToNumber(value, ref number); + ValueStringBuilder sb; + unsafe + { + char* stackPtr = stackalloc char[CharStackBufferSize]; + sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + } + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, false); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + return sb.ToString(); + } + } + + public static bool TryFormatUInt32(uint value, ReadOnlySpan format, IFormatProvider provider, Span destination, out int charsWritten) + { + // Fast path for default format + if (format.Length == 0) + { + return TryUInt32ToDecStr(value, digits: -1, destination, out charsWritten); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') + { + return TryUInt32ToDecStr(value, digits, destination, out charsWritten); + } + else if (fmtUpper == 'X') + { + // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase + // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code produces lowercase. + return TryInt32ToHexStr((int)value, (char)(fmt - ('X' - 'A' + 10)), digits, destination, out charsWritten); + } + else + { + NumberBuffer number = default; + UInt32ToNumber(value, ref number); + ValueStringBuilder sb; + unsafe + { + char* stackPtr = stackalloc char[CharStackBufferSize]; + sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + } + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, false); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + return sb.TryCopyTo(destination, out charsWritten); + } + } + + public static string FormatInt64(long value, ReadOnlySpan format, IFormatProvider provider) + { + // Fast path for default format with a non-negative value + if (value >= 0 && format.Length == 0) + { + return UInt64ToDecStr((ulong)value, digits: -1); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') + { + return value >= 0 ? + UInt64ToDecStr((ulong)value, digits) : + NegativeInt64ToDecStr(value, digits, info.NegativeSign); + } + else if (fmtUpper == 'X') + { + // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase + // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code + // produces lowercase. + return Int64ToHexStr(value, (char)(fmt - ('X' - 'A' + 10)), digits); + } + else + { + NumberBuffer number = default; + Int64ToNumber(value, ref number); + ValueStringBuilder sb; + unsafe + { + char* stackPtr = stackalloc char[CharStackBufferSize]; + sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + } + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, false); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + return sb.ToString(); + } + } + + public static bool TryFormatInt64(long value, ReadOnlySpan format, IFormatProvider provider, Span destination, out int charsWritten) + { + // Fast path for default format with a non-negative value + if (value >= 0 && format.Length == 0) + { + return TryUInt64ToDecStr((ulong)value, digits: -1, destination, out charsWritten); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') + { + return value >= 0 ? + TryUInt64ToDecStr((ulong)value, digits, destination, out charsWritten) : + TryNegativeInt64ToDecStr(value, digits, info.NegativeSign, destination, out charsWritten); + } + else if (fmtUpper == 'X') + { + // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase + // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code + // produces lowercase. + return TryInt64ToHexStr(value, (char)(fmt - ('X' - 'A' + 10)), digits, destination, out charsWritten); + } + else + { + NumberBuffer number = default; + Int64ToNumber(value, ref number); + ValueStringBuilder sb; + unsafe + { + char* stackPtr = stackalloc char[CharStackBufferSize]; + sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + } + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, false); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + return sb.TryCopyTo(destination, out charsWritten); + } + } + + public static string FormatUInt64(ulong value, ReadOnlySpan format, IFormatProvider provider) + { + // Fast path for default format + if (format.Length == 0) + { + return UInt64ToDecStr(value, digits: -1); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') + { + return UInt64ToDecStr(value, digits); + } + else if (fmtUpper == 'X') + { + // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase + // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code + // produces lowercase. + return Int64ToHexStr((long)value, (char)(fmt - ('X' - 'A' + 10)), digits); + } + else + { + NumberBuffer number = default; + UInt64ToNumber(value, ref number); + ValueStringBuilder sb; + unsafe + { + char* stackPtr = stackalloc char[CharStackBufferSize]; + sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + } + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, false); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + return sb.ToString(); + } + } + + public static bool TryFormatUInt64(ulong value, ReadOnlySpan format, IFormatProvider provider, Span destination, out int charsWritten) + { + // Fast path for default format + if (format.Length == 0) + { + return TryUInt64ToDecStr(value, digits: -1, destination, out charsWritten); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') + { + return TryUInt64ToDecStr(value, digits, destination, out charsWritten); + } + else if (fmtUpper == 'X') + { + // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase + // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code + // produces lowercase. + return TryInt64ToHexStr((long)value, (char)(fmt - ('X' - 'A' + 10)), digits, destination, out charsWritten); + } + else + { + NumberBuffer number = default; + UInt64ToNumber(value, ref number); + ValueStringBuilder sb; + unsafe + { + char* stackPtr = stackalloc char[CharStackBufferSize]; + sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + } + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info, false); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + return sb.TryCopyTo(destination, out charsWritten); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] // called from only one location + private static unsafe void Int32ToNumber(int value, ref NumberBuffer number) + { + number.precision = Int32Precision; + + if (value >= 0) + { + number.sign = false; + } + else + { + number.sign = true; + value = -value; + } + + char* buffer = number.digits; + char* p = UInt32ToDecChars(buffer + Int32Precision, (uint)value, 0); + int i = (int)(buffer + Int32Precision - p); + + number.scale = i; + + char* dst = number.digits; + while (--i >= 0) + *dst++ = *p++; + *dst = '\0'; + } + + private static unsafe string NegativeInt32ToDecStr(int value, int digits, string sNegative) + { + Debug.Assert(value < 0); + + if (digits < 1) + digits = 1; + + int bufferLength = Math.Max(digits, MaxUInt32DecDigits) + sNegative.Length; + int index = bufferLength; + + char* buffer = stackalloc char[bufferLength]; + char* p = UInt32ToDecChars(buffer + bufferLength, (uint)(-value), digits); + for (int i = sNegative.Length - 1; i >= 0; i--) + { + *(--p) = sNegative[i]; + } + + Debug.Assert(buffer + bufferLength - p >= 0 && buffer <= p); + return new string(p, 0, (int)(buffer + bufferLength - p)); + } + + private static unsafe bool TryNegativeInt32ToDecStr(int value, int digits, string sNegative, Span destination, out int charsWritten) + { + Debug.Assert(value < 0); + + if (digits < 1) + digits = 1; + + int bufferLength = Math.Max(digits, MaxUInt32DecDigits) + sNegative.Length; + int index = bufferLength; + + char* buffer = stackalloc char[bufferLength]; + char* p = UInt32ToDecChars(buffer + bufferLength, (uint)(-value), digits); + for (int i = sNegative.Length - 1; i >= 0; i--) + { + *(--p) = sNegative[i]; + } + + Debug.Assert(buffer + bufferLength - p >= 0 && buffer <= p); + return TryCopyTo(p, (int)(buffer + bufferLength - p), destination, out charsWritten); + } + + private static unsafe string Int32ToHexStr(int value, char hexBase, int digits) + { + if (digits < 1) + digits = 1; + + int bufferLength = Math.Max(digits, MaxUInt32HexDigits); + char* buffer = stackalloc char[bufferLength]; + + char* p = Int32ToHexChars(buffer + bufferLength, (uint)value, hexBase, digits); + return new string(p, 0, (int)(buffer + bufferLength - p)); + } + + private static unsafe bool TryInt32ToHexStr(int value, char hexBase, int digits, Span destination, out int charsWritten) + { + if (digits < 1) + digits = 1; + + int bufferLength = Math.Max(digits, MaxUInt32HexDigits); + char* buffer = stackalloc char[bufferLength]; + + char* p = Int32ToHexChars(buffer + bufferLength, (uint)value, hexBase, digits); + return TryCopyTo(p, (int)(buffer + bufferLength - p), destination, out charsWritten); + } + + private static unsafe char* Int32ToHexChars(char* buffer, uint value, int hexBase, int digits) + { + while (--digits >= 0 || value != 0) + { + byte digit = (byte)(value & 0xF); + *(--buffer) = (char)(digit + (digit < 10 ? (byte)'0' : hexBase)); + value >>= 4; + } + return buffer; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] // called from only one location + private static unsafe void UInt32ToNumber(uint value, ref NumberBuffer number) + { + number.precision = UInt32Precision; + number.sign = false; + + char* buffer = number.digits; + char* p = UInt32ToDecChars(buffer + UInt32Precision, value, 0); + int i = (int)(buffer + UInt32Precision - p); + number.scale = i; + + char* dst = number.digits; + while (--i >= 0) + *dst++ = *p++; + *dst = '\0'; + } + + internal static unsafe char* UInt32ToDecChars(char* bufferEnd, uint value, int digits) + { + while (--digits >= 0 || value != 0) + { + // TODO https://github.com/dotnet/coreclr/issues/3439 + uint newValue = value / 10; + *(--bufferEnd) = (char)(value - (newValue * 10) + '0'); + value = newValue; + } + return bufferEnd; + } + + private static unsafe string UInt32ToDecStr(uint value, int digits) + { + if (digits <= 1) + { + char* buffer = stackalloc char[MaxUInt32DecDigits]; + + char* start = buffer + MaxUInt32DecDigits; + char* p = start; + do + { + // TODO https://github.com/dotnet/coreclr/issues/3439 + uint div = value / 10; + *(--p) = (char)('0' + value - (div * 10)); + value = div; + } + while (value != 0); + + return new string(p, 0, (int)(start - p)); + } + else + { + int bufferSize = Math.Max(digits, MaxUInt32DecDigits); + char* buffer = stackalloc char[bufferSize]; + char* p = UInt32ToDecChars(buffer + bufferSize, value, digits); + return new string(p, 0, (int)(buffer + bufferSize - p)); + } + } + + private static unsafe bool TryUInt32ToDecStr(uint value, int digits, Span destination, out int charsWritten) + { + if (digits <= 1) + { + char* buffer = stackalloc char[MaxUInt32DecDigits]; + char* start = buffer + MaxUInt32DecDigits; + char* p = start; + do + { + // TODO https://github.com/dotnet/coreclr/issues/3439 + uint div = value / 10; + *(--p) = (char)('0' + value - (div * 10)); + value = div; + } + while (value != 0); + return TryCopyTo(p, (int)(start - p), destination, out charsWritten); + } + else + { + int bufferSize = Math.Max(digits, MaxUInt32DecDigits); + char* buffer = stackalloc char[bufferSize]; + char* p = UInt32ToDecChars(buffer + bufferSize, value, digits); + return TryCopyTo(p, (int)(buffer + bufferSize - p), destination, out charsWritten); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe bool TryCopyTo(char* src, int length, Span destination, out int charsWritten) + { + if (length <= destination.Length) + { + bool copied = new ReadOnlySpan(src, length).TryCopyTo(destination); + Debug.Assert(copied); + charsWritten = length; + return true; + } + else + { + charsWritten = 0; + return false; + } + } + + private static unsafe void Int64ToNumber(long input, ref NumberBuffer number) + { + ulong value = (ulong)input; + number.sign = input < 0; + number.precision = Int64Precision; + if (number.sign) + { + value = (ulong)(-input); + } + + char* buffer = number.digits; + char* p = buffer + Int64Precision; + while (High32(value) != 0) + p = UInt32ToDecChars(p, Int64DivMod1E9(ref value), 9); + p = UInt32ToDecChars(p, Low32(value), 0); + int i = (int)(buffer + Int64Precision - p); + + number.scale = i; + + char* dst = number.digits; + while (--i >= 0) + *dst++ = *p++; + *dst = '\0'; + } + + private static unsafe string NegativeInt64ToDecStr(long input, int digits, string sNegative) + { + Debug.Assert(input < 0); + + if (digits < 1) + { + digits = 1; + } + + ulong value = (ulong)(-input); + + int bufferLength = Math.Max(digits, MaxUInt64DecDigits) + sNegative.Length; + int index = bufferLength; + + char* buffer = stackalloc char[bufferLength]; + char* p = buffer + bufferLength; + while (High32(value) != 0) + { + p = UInt32ToDecChars(p, Int64DivMod1E9(ref value), 9); + digits -= 9; + } + p = UInt32ToDecChars(p, Low32(value), digits); + + for (int i = sNegative.Length - 1; i >= 0; i--) + { + *(--p) = sNegative[i]; + } + + return new string(p, 0, (int)(buffer + bufferLength - p)); + } + + private static unsafe bool TryNegativeInt64ToDecStr(long input, int digits, string sNegative, Span destination, out int charsWritten) + { + Debug.Assert(input < 0); + + if (digits < 1) + { + digits = 1; + } + + ulong value = (ulong)(-input); + + int bufferLength = Math.Max(digits, MaxUInt64DecDigits) + sNegative.Length; + int index = bufferLength; + + char* buffer = stackalloc char[bufferLength]; + char* p = buffer + bufferLength; + while (High32(value) != 0) + { + p = UInt32ToDecChars(p, Int64DivMod1E9(ref value), 9); + digits -= 9; + } + p = UInt32ToDecChars(p, Low32(value), digits); + + for (int i = sNegative.Length - 1; i >= 0; i--) + { + *(--p) = sNegative[i]; + } + + return TryCopyTo(p, (int)(buffer + bufferLength - p), destination, out charsWritten); + } + + private static unsafe string Int64ToHexStr(long value, char hexBase, int digits) + { + int bufferLength = Math.Max(digits, MaxUInt32HexDigits * 2); + char* buffer = stackalloc char[bufferLength]; + int index = bufferLength; + + char* p; + if (High32((ulong)value) != 0) + { + p = Int32ToHexChars(buffer + index, Low32((ulong)value), hexBase, 8); + p = Int32ToHexChars(p, High32((ulong)value), hexBase, digits - 8); + } + else + { + p = Int32ToHexChars(buffer + index, Low32((ulong)value), hexBase, Math.Max(digits, 1)); + } + + return new string(p, 0, (int)(buffer + bufferLength - p)); + } + + private static unsafe bool TryInt64ToHexStr(long value, char hexBase, int digits, Span destination, out int charsWritten) + { + int bufferLength = Math.Max(digits, MaxUInt32HexDigits * 2); + char* buffer = stackalloc char[bufferLength]; + int index = bufferLength; + + char* p; + if (High32((ulong)value) != 0) + { + p = Int32ToHexChars(buffer + index, Low32((ulong)value), hexBase, 8); + p = Int32ToHexChars(p, High32((ulong)value), hexBase, digits - 8); + } + else + { + p = Int32ToHexChars(buffer + index, Low32((ulong)value), hexBase, Math.Max(digits, 1)); + } + + return TryCopyTo(p, (int)(buffer + bufferLength - p), destination, out charsWritten); + } + + private static unsafe void UInt64ToNumber(ulong value, ref NumberBuffer number) + { + number.precision = UInt64Precision; + number.sign = false; + + char* buffer = number.digits; + char* p = buffer + UInt64Precision; + + while (High32(value) != 0) + p = UInt32ToDecChars(p, Int64DivMod1E9(ref value), 9); + p = UInt32ToDecChars(p, Low32(value), 0); + int i = (int)(buffer + UInt64Precision - p); + + number.scale = i; + + char* dst = number.digits; + while (--i >= 0) + *dst++ = *p++; + *dst = '\0'; + } + + private static unsafe string UInt64ToDecStr(ulong value, int digits) + { + if (digits < 1) + digits = 1; + + int bufferSize = Math.Max(digits, MaxUInt64DecDigits); + char* buffer = stackalloc char[bufferSize]; + char* p = buffer + bufferSize; + while (High32(value) != 0) + { + p = UInt32ToDecChars(p, Int64DivMod1E9(ref value), 9); + digits -= 9; + } + p = UInt32ToDecChars(p, Low32(value), digits); + + return new string(p, 0, (int)(buffer + bufferSize - p)); + } + + private static unsafe bool TryUInt64ToDecStr(ulong value, int digits, Span destination, out int charsWritten) + { + if (digits < 1) + digits = 1; + + int bufferSize = Math.Max(digits, MaxUInt64DecDigits); + char* buffer = stackalloc char[bufferSize]; + char* p = buffer + bufferSize; + while (High32(value) != 0) + { + p = UInt32ToDecChars(p, Int64DivMod1E9(ref value), 9); + digits -= 9; + } + p = UInt32ToDecChars(p, Low32(value), digits); + + return TryCopyTo(p, (int)(buffer + bufferSize - p), destination, out charsWritten); + } + + internal static unsafe char ParseFormatSpecifier(ReadOnlySpan format, out int digits) + { + char c = default; + if (format.Length > 0) + { + // If the format begins with a symbol, see if it's a standard format + // with or without a specified number of digits. + c = format[0]; + if ((uint)(c - 'A') <= 'Z' - 'A' || + (uint)(c - 'a') <= 'z' - 'a') + { + // Fast path for sole symbol, e.g. "D" + if (format.Length == 1) + { + digits = -1; + return c; + } + + if (format.Length == 2) + { + // Fast path for symbol and single digit, e.g. "X4" + int d = format[1] - '0'; + if ((uint)d < 10) + { + digits = d; + return c; + } + } + else if (format.Length == 3) + { + // Fast path for symbol and double digit, e.g. "F12" + int d1 = format[1] - '0', d2 = format[2] - '0'; + if ((uint)d1 < 10 && (uint)d2 < 10) + { + digits = d1 * 10 + d2; + return c; + } + } + + // Fallback for symbol and any length digits. The digits value must be >= 0 && <= 99, + // but it can begin with any number of 0s, and thus we may need to check more than two + // digits. Further, for compat, we need to stop when we hit a null char. + int n = 0; + int i = 1; + while (i < format.Length && (((uint)format[i] - '0') < 10) && n < 10) + { + n = (n * 10) + format[i++] - '0'; + } + + // If we're at the end of the digits rather than having stopped because we hit something + // other than a digit or overflowed, return the standard format info. + if (i == format.Length || format[i] == '\0') + { + digits = n; + return c; + } + } + } + + // Default empty format to be "G"; custom format is signified with '\0'. + digits = -1; + return format.Length == 0 || c == '\0' ? // For compat, treat '\0' as the end of the specifier, even if the specifier extends beyond it. + 'G' : + '\0'; + } + + internal static unsafe void NumberToString(ref ValueStringBuilder sb, ref NumberBuffer number, char format, int nMaxDigits, NumberFormatInfo info, bool isDecimal) + { + int nMinDigits = -1; + + switch (format) + { + case 'C': + case 'c': + { + nMinDigits = nMaxDigits >= 0 ? nMaxDigits : info.CurrencyDecimalDigits; + if (nMaxDigits < 0) + nMaxDigits = info.CurrencyDecimalDigits; + + RoundNumber(ref number, number.scale + nMaxDigits); // Don't change this line to use digPos since digCount could have its sign changed. + + FormatCurrency(ref sb, ref number, nMinDigits, nMaxDigits, info); + + break; + } + + case 'F': + case 'f': + { + if (nMaxDigits < 0) + nMaxDigits = nMinDigits = info.NumberDecimalDigits; + else + nMinDigits = nMaxDigits; + + RoundNumber(ref number, number.scale + nMaxDigits); + + if (number.sign) + sb.Append(info.NegativeSign); + + FormatFixed(ref sb, ref number, nMinDigits, nMaxDigits, info, null, info.NumberDecimalSeparator, null); + + break; + } + + case 'N': + case 'n': + { + if (nMaxDigits < 0) + nMaxDigits = nMinDigits = info.NumberDecimalDigits; // Since we are using digits in our calculation + else + nMinDigits = nMaxDigits; + + RoundNumber(ref number, number.scale + nMaxDigits); + + FormatNumber(ref sb, ref number, nMinDigits, nMaxDigits, info); + + break; + } + + case 'E': + case 'e': + { + if (nMaxDigits < 0) + nMaxDigits = nMinDigits = 6; + else + nMinDigits = nMaxDigits; + nMaxDigits++; + + RoundNumber(ref number, nMaxDigits); + + if (number.sign) + sb.Append(info.NegativeSign); + + FormatScientific(ref sb, ref number, nMinDigits, nMaxDigits, info, format); + + break; + } + + case 'G': + case 'g': + { + bool enableRounding = true; + if (nMaxDigits < 1) + { + if (isDecimal && (nMaxDigits == -1)) + { + // Default to 29 digits precision only for G formatting without a precision specifier + // This ensures that the PAL code pads out to the correct place even when we use the default precision + nMaxDigits = nMinDigits = DecimalPrecision; + enableRounding = false; // Turn off rounding for ECMA compliance to output trailing 0's after decimal as significant + } + else + { + // This ensures that the PAL code pads out to the correct place even when we use the default precision + nMaxDigits = nMinDigits = number.precision; + } + } + else + nMinDigits = nMaxDigits; + + if (enableRounding) // Don't round for G formatting without precision + RoundNumber(ref number, nMaxDigits); // This also fixes up the minus zero case + else + { + if (isDecimal && (number.digits[0] == 0)) + { + // Minus zero should be formatted as 0 + number.sign = false; + } + } + + if (number.sign) + sb.Append(info.NegativeSign); + + FormatGeneral(ref sb, ref number, nMinDigits, nMaxDigits, info, (char)(format - ('G' - 'E')), !enableRounding); + + break; + } + + case 'P': + case 'p': + { + if (nMaxDigits < 0) + nMaxDigits = nMinDigits = info.PercentDecimalDigits; + else + nMinDigits = nMaxDigits; + number.scale += 2; + + RoundNumber(ref number, number.scale + nMaxDigits); + + FormatPercent(ref sb, ref number, nMinDigits, nMaxDigits, info); + + break; + } + + default: + throw new FormatException(SR.Argument_BadFormatSpecifier); + } + } + + internal static unsafe void NumberToStringFormat(ref ValueStringBuilder sb, ref NumberBuffer number, ReadOnlySpan format, NumberFormatInfo info) + { + int digitCount; + int decimalPos; + int firstDigit; + int lastDigit; + int digPos; + bool scientific; + int thousandPos; + int thousandCount = 0; + bool thousandSeps; + int scaleAdjust; + int adjust; + + int section; + int src; + char* dig = number.digits; + char ch; + + section = FindSection(format, dig[0] == 0 ? 2 : number.sign ? 1 : 0); + + while (true) + { + digitCount = 0; + decimalPos = -1; + firstDigit = 0x7FFFFFFF; + lastDigit = 0; + scientific = false; + thousandPos = -1; + thousandSeps = false; + scaleAdjust = 0; + src = section; + + fixed (char* pFormat = &MemoryMarshal.GetReference(format)) + { + while (src < format.Length && (ch = pFormat[src++]) != 0 && ch != ';') + { + switch (ch) + { + case '#': + digitCount++; + break; + case '0': + if (firstDigit == 0x7FFFFFFF) + firstDigit = digitCount; + digitCount++; + lastDigit = digitCount; + break; + case '.': + if (decimalPos < 0) + decimalPos = digitCount; + break; + case ',': + if (digitCount > 0 && decimalPos < 0) + { + if (thousandPos >= 0) + { + if (thousandPos == digitCount) + { + thousandCount++; + break; + } + thousandSeps = true; + } + thousandPos = digitCount; + thousandCount = 1; + } + break; + case '%': + scaleAdjust += 2; + break; + case '\x2030': + scaleAdjust += 3; + break; + case '\'': + case '"': + while (src < format.Length && pFormat[src] != 0 && pFormat[src++] != ch) + ; + break; + case '\\': + if (src < format.Length && pFormat[src] != 0) + src++; + break; + case 'E': + case 'e': + if ((src < format.Length && pFormat[src] == '0') || + (src + 1 < format.Length && (pFormat[src] == '+' || pFormat[src] == '-') && pFormat[src + 1] == '0')) + { + while (++src < format.Length && pFormat[src] == '0'); + scientific = true; + } + break; + } + } + } + + if (decimalPos < 0) + decimalPos = digitCount; + + if (thousandPos >= 0) + { + if (thousandPos == decimalPos) + scaleAdjust -= thousandCount * 3; + else + thousandSeps = true; + } + + if (dig[0] != 0) + { + number.scale += scaleAdjust; + int pos = scientific ? digitCount : number.scale + digitCount - decimalPos; + RoundNumber(ref number, pos); + if (dig[0] == 0) + { + src = FindSection(format, 2); + if (src != section) + { + section = src; + continue; + } + } + } + else + { + number.sign = false; // We need to format -0 without the sign set. + number.scale = 0; // Decimals with scale ('0.00') should be rounded. + } + + break; + } + + firstDigit = firstDigit < decimalPos ? decimalPos - firstDigit : 0; + lastDigit = lastDigit > decimalPos ? decimalPos - lastDigit : 0; + if (scientific) + { + digPos = decimalPos; + adjust = 0; + } + else + { + digPos = number.scale > decimalPos ? number.scale : decimalPos; + adjust = number.scale - decimalPos; + } + src = section; + + // Adjust can be negative, so we make this an int instead of an unsigned int. + // Adjust represents the number of characters over the formatting e.g. format string is "0000" and you are trying to + // format 100000 (6 digits). Means adjust will be 2. On the other hand if you are trying to format 10 adjust will be + // -2 and we'll need to fixup these digits with 0 padding if we have 0 formatting as in this example. + Span thousandsSepPos = stackalloc int[4]; + int thousandsSepCtr = -1; + + if (thousandSeps) + { + // We need to precompute this outside the number formatting loop + if (info.NumberGroupSeparator.Length > 0) + { + // We need this array to figure out where to insert the thousands separator. We would have to traverse the string + // backwards. PIC formatting always traverses forwards. These indices are precomputed to tell us where to insert + // the thousands separator so we can get away with traversing forwards. Note we only have to compute up to digPos. + // The max is not bound since you can have formatting strings of the form "000,000..", and this + // should handle that case too. + + int[] groupDigits = info.numberGroupSizes; + + int groupSizeIndex = 0; // Index into the groupDigits array. + int groupTotalSizeCount = 0; + int groupSizeLen = groupDigits.Length; // The length of groupDigits array. + if (groupSizeLen != 0) + groupTotalSizeCount = groupDigits[groupSizeIndex]; // The current running total of group size. + int groupSize = groupTotalSizeCount; + + int totalDigits = digPos + ((adjust < 0) ? adjust : 0); // Actual number of digits in o/p + int numDigits = (firstDigit > totalDigits) ? firstDigit : totalDigits; + while (numDigits > groupTotalSizeCount) + { + if (groupSize == 0) + break; + ++thousandsSepCtr; + if (thousandsSepCtr >= thousandsSepPos.Length) + { + var newThousandsSepPos = new int[thousandsSepPos.Length * 2]; + bool copied = thousandsSepPos.TryCopyTo(newThousandsSepPos); + Debug.Assert(copied, "Expect copy to succeed, as the new array is larger than the original"); + thousandsSepPos = newThousandsSepPos; + } + + thousandsSepPos[thousandsSepCtr] = groupTotalSizeCount; + if (groupSizeIndex < groupSizeLen - 1) + { + groupSizeIndex++; + groupSize = groupDigits[groupSizeIndex]; + } + groupTotalSizeCount += groupSize; + } + } + } + + if (number.sign && section == 0) + sb.Append(info.NegativeSign); + + bool decimalWritten = false; + + fixed (char* pFormat = &MemoryMarshal.GetReference(format)) + { + char* cur = dig; + + while (src < format.Length && (ch = pFormat[src++]) != 0 && ch != ';') + { + if (adjust > 0) + { + switch (ch) + { + case '#': + case '0': + case '.': + while (adjust > 0) + { + // digPos will be one greater than thousandsSepPos[thousandsSepCtr] since we are at + // the character after which the groupSeparator needs to be appended. + sb.Append(*cur != 0 ? *cur++ : '0'); + if (thousandSeps && digPos > 1 && thousandsSepCtr >= 0) + { + if (digPos == thousandsSepPos[thousandsSepCtr] + 1) + { + sb.Append(info.NumberGroupSeparator); + thousandsSepCtr--; + } + } + digPos--; + adjust--; + } + break; + } + } + + switch (ch) + { + case '#': + case '0': + { + if (adjust < 0) + { + adjust++; + ch = digPos <= firstDigit ? '0' : '\0'; + } + else + { + ch = *cur != 0 ? *cur++ : digPos > lastDigit ? '0' : '\0'; + } + if (ch != 0) + { + sb.Append(ch); + if (thousandSeps && digPos > 1 && thousandsSepCtr >= 0) + { + if (digPos == thousandsSepPos[thousandsSepCtr] + 1) + { + sb.Append(info.NumberGroupSeparator); + thousandsSepCtr--; + } + } + } + + digPos--; + break; + } + case '.': + { + if (digPos != 0 || decimalWritten) + { + // For compatibility, don't echo repeated decimals + break; + } + // If the format has trailing zeros or the format has a decimal and digits remain + if (lastDigit < 0 || (decimalPos < digitCount && *cur != 0)) + { + sb.Append(info.NumberDecimalSeparator); + decimalWritten = true; + } + break; + } + case '\x2030': + sb.Append(info.PerMilleSymbol); + break; + case '%': + sb.Append(info.PercentSymbol); + break; + case ',': + break; + case '\'': + case '"': + while (src < format.Length && pFormat[src] != 0 && pFormat[src] != ch) + sb.Append(pFormat[src++]); + if (src < format.Length && pFormat[src] != 0) + src++; + break; + case '\\': + if (src < format.Length && pFormat[src] != 0) + sb.Append(pFormat[src++]); + break; + case 'E': + case 'e': + { + bool positiveSign = false; + int i = 0; + if (scientific) + { + if (src < format.Length && pFormat[src] == '0') + { + // Handles E0, which should format the same as E-0 + i++; + } + else if (src+1 < format.Length && pFormat[src] == '+' && pFormat[src + 1] == '0') + { + // Handles E+0 + positiveSign = true; + } + else if (src+1 < format.Length && pFormat[src] == '-' && pFormat[src + 1] == '0') + { + // Handles E-0 + // Do nothing, this is just a place holder s.t. we don't break out of the loop. + } + else + { + sb.Append(ch); + break; + } + + while (++src < format.Length && pFormat[src] == '0') + i++; + if (i > 10) + i = 10; + + int exp = dig[0] == 0 ? 0 : number.scale - decimalPos; + FormatExponent(ref sb, info, exp, ch, i, positiveSign); + scientific = false; + } + else + { + sb.Append(ch); // Copy E or e to output + if (src < format.Length) + { + if (pFormat[src] == '+' || pFormat[src] == '-') + sb.Append(pFormat[src++]); + while (src < format.Length && pFormat[src] == '0') + sb.Append(pFormat[src++]); + } + } + break; + } + default: + sb.Append(ch); + break; + } + } + } + } + + private static void FormatCurrency(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info) + { + string fmt = number.sign ? + s_negCurrencyFormats[info.CurrencyNegativePattern] : + s_posCurrencyFormats[info.CurrencyPositivePattern]; + + foreach (char ch in fmt) + { + switch (ch) + { + case '#': + FormatFixed(ref sb, ref number, nMinDigits, nMaxDigits, info, info.currencyGroupSizes, info.CurrencyDecimalSeparator, info.CurrencyGroupSeparator); + break; + case '-': + sb.Append(info.NegativeSign); + break; + case '$': + sb.Append(info.CurrencySymbol); + break; + default: + sb.Append(ch); + break; + } + } + } + + private static unsafe void FormatFixed(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, int[] groupDigits, string sDecimal, string sGroup) + { + int digPos = number.scale; + char* dig = number.digits; + + if (digPos > 0) + { + if (groupDigits != null) + { + int groupSizeIndex = 0; // Index into the groupDigits array. + int groupSizeCount = groupDigits[groupSizeIndex]; // The current total of group size. + int bufferSize = digPos; // The length of the result buffer string. + int groupSize = 0; // The current group size. + + // Find out the size of the string buffer for the result. + if (groupDigits.Length != 0) // You can pass in 0 length arrays + { + while (digPos > groupSizeCount) + { + groupSize = groupDigits[groupSizeIndex]; + if (groupSize == 0) + break; + + bufferSize += sGroup.Length; + if (groupSizeIndex < groupDigits.Length - 1) + groupSizeIndex++; + + groupSizeCount += groupDigits[groupSizeIndex]; + if (groupSizeCount < 0 || bufferSize < 0) + throw new ArgumentOutOfRangeException(); // If we overflow + } + + groupSize = groupSizeCount == 0 ? 0 : groupDigits[0]; // If you passed in an array with one entry as 0, groupSizeCount == 0 + } + + groupSizeIndex = 0; + int digitCount = 0; + int digLength = string.wcslen(dig); + int digStart = (digPos < digLength) ? digPos : digLength; + fixed (char* spanPtr = &MemoryMarshal.GetReference(sb.AppendSpan(bufferSize))) + { + char* p = spanPtr + bufferSize - 1; + for (int i = digPos - 1; i >= 0; i--) + { + *(p--) = (i < digStart) ? dig[i] : '0'; + + if (groupSize > 0) + { + digitCount++; + if ((digitCount == groupSize) && (i != 0)) + { + for (int j = sGroup.Length - 1; j >= 0; j--) + *(p--) = sGroup[j]; + + if (groupSizeIndex < groupDigits.Length - 1) + { + groupSizeIndex++; + groupSize = groupDigits[groupSizeIndex]; + } + digitCount = 0; + } + } + } + + Debug.Assert(p >= spanPtr - 1, "Underflow"); + dig += digStart; + } + } + else + { + do + { + sb.Append(*dig != 0 ? *dig++ : '0'); + } + while (--digPos > 0); + } + } + else + { + sb.Append('0'); + } + + if (nMaxDigits > 0) + { + sb.Append(sDecimal); + if ((digPos < 0) && (nMaxDigits > 0)) + { + int zeroes = Math.Min(-digPos, nMaxDigits); + sb.Append('0', zeroes); + digPos += zeroes; + nMaxDigits -= zeroes; + } + + while (nMaxDigits > 0) + { + sb.Append((*dig != 0) ? *dig++ : '0'); + nMaxDigits--; + } + } + } + + private static void FormatNumber(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info) + { + string fmt = number.sign ? + s_negNumberFormats[info.NumberNegativePattern] : + PosNumberFormat; + + foreach (char ch in fmt) + { + switch (ch) + { + case '#': + FormatFixed(ref sb, ref number, nMinDigits, nMaxDigits, info, info.numberGroupSizes, info.NumberDecimalSeparator, info.NumberGroupSeparator); + break; + case '-': + sb.Append(info.NegativeSign); + break; + default: + sb.Append(ch); + break; + } + } + } + + private static unsafe void FormatScientific(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, char expChar) + { + char* dig = number.digits; + + sb.Append((*dig != 0) ? *dig++ : '0'); + + if (nMaxDigits != 1) // For E0 we would like to suppress the decimal point + sb.Append(info.NumberDecimalSeparator); + + while (--nMaxDigits > 0) + sb.Append((*dig != 0) ? *dig++ : '0'); + + int e = number.digits[0] == 0 ? 0 : number.scale - 1; + FormatExponent(ref sb, info, e, expChar, 3, true); + } + + private static unsafe void FormatExponent(ref ValueStringBuilder sb, NumberFormatInfo info, int value, char expChar, int minDigits, bool positiveSign) + { + sb.Append(expChar); + + if (value < 0) + { + sb.Append(info.NegativeSign); + value = -value; + } + else + { + if (positiveSign) + sb.Append(info.PositiveSign); + } + + char* digits = stackalloc char[MaxUInt32DecDigits]; + char* p = UInt32ToDecChars(digits + MaxUInt32DecDigits, (uint)value, minDigits); + int i = (int)(digits + MaxUInt32DecDigits - p); + sb.Append(p, (int)(digits + MaxUInt32DecDigits - p)); + } + + private static unsafe void FormatGeneral(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, char expChar, bool bSuppressScientific) + { + int digPos = number.scale; + bool scientific = false; + + if (!bSuppressScientific) + { + // Don't switch to scientific notation + if (digPos > nMaxDigits || digPos < -3) + { + digPos = 1; + scientific = true; + } + } + + char* dig = number.digits; + + if (digPos > 0) + { + do + { + sb.Append((*dig != 0) ? *dig++ : '0'); + } while (--digPos > 0); + } + else + { + sb.Append('0'); + } + + if (*dig != 0 || digPos < 0) + { + sb.Append(info.NumberDecimalSeparator); + + while (digPos < 0) + { + sb.Append('0'); + digPos++; + } + + while (*dig != 0) + sb.Append(*dig++); + } + + if (scientific) + FormatExponent(ref sb, info, number.scale - 1, expChar, 2, true); + } + + private static void FormatPercent(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info) + { + string fmt = number.sign ? + s_negPercentFormats[info.PercentNegativePattern] : + s_posPercentFormats[info.PercentPositivePattern]; + + foreach (char ch in fmt) + { + switch (ch) + { + case '#': + FormatFixed(ref sb, ref number, nMinDigits, nMaxDigits, info, info.percentGroupSizes, info.PercentDecimalSeparator, info.PercentGroupSeparator); + break; + case '-': + sb.Append(info.NegativeSign); + break; + case '%': + sb.Append(info.PercentSymbol); + break; + default: + sb.Append(ch); + break; + } + } + } + + private static unsafe void RoundNumber(ref NumberBuffer number, int pos) + { + char* dig = number.digits; + + int i = 0; + while (i < pos && dig[i] != 0) + i++; + + if (i == pos && dig[i] >= '5') + { + while (i > 0 && dig[i - 1] == '9') + i--; + + if (i > 0) + { + dig[i - 1]++; + } + else + { + number.scale++; + dig[0] = '1'; + i = 1; + } + } + else + { + while (i > 0 && dig[i - 1] == '0') + i--; + } + if (i == 0) + { + number.scale = 0; + number.sign = false; + } + dig[i] = '\0'; + } + + private static unsafe int FindSection(ReadOnlySpan format, int section) + { + int src; + char ch; + + if (section == 0) + return 0; + + fixed (char* pFormat = &MemoryMarshal.GetReference(format)) + { + src = 0; + for (;;) + { + if (src >= format.Length) + { + return 0; + } + + switch (ch = pFormat[src++]) + { + case '\'': + case '"': + while (src < format.Length && pFormat[src] != 0 && pFormat[src++] != ch) + ; + break; + case '\\': + if (src < format.Length && pFormat[src] != 0) + src++; + break; + case ';': + if (--section != 0) + break; + if (src < format.Length && pFormat[src] != 0 && pFormat[src] != ';') + return src; + goto case '\0'; + case '\0': + return 0; + } + } + } + } + + private static uint Low32(ulong value) => (uint)value; + + private static uint High32(ulong value) => (uint)((value & 0xFFFFFFFF00000000) >> 32); + + private static uint Int64DivMod1E9(ref ulong value) + { + uint rem = (uint)(value % 1000000000); + value /= 1000000000; + return rem; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Number.NumberBuffer.cs b/external/corert/src/System.Private.CoreLib/shared/System/Number.NumberBuffer.cs new file mode 100644 index 0000000000..2316f99bd3 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Number.NumberBuffer.cs @@ -0,0 +1,31 @@ +// 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.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using Internal.Runtime.CompilerServices; + +namespace System +{ + internal static partial class Number + { + private const int NumberMaxDigits = 50; // needs to == NUMBER_MAXDIGITS in coreclr's src/classlibnative/bcltype/number.h. + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal unsafe ref struct NumberBuffer // needs to match layout of NUMBER in coreclr's src/classlibnative/bcltype/number.h + { + public int precision; + public int scale; + private int _sign; + private DigitsAndNullTerminator _digits; + private char* _allDigits; + + public bool sign { get => _sign != 0; set => _sign = value ? 1 : 0; } + public char* digits => (char*)Unsafe.AsPointer(ref _digits); + + [StructLayout(LayoutKind.Sequential, Size = (NumberMaxDigits + 1) * sizeof(char))] + private struct DigitsAndNullTerminator { } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Number.Parsing.cs b/external/corert/src/System.Private.CoreLib/shared/System/Number.Parsing.cs new file mode 100644 index 0000000000..46951094eb --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Number.Parsing.cs @@ -0,0 +1,964 @@ +// 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.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; + +namespace System +{ + // The Parse methods provided by the numeric classes convert a + // string to a numeric value. The optional style parameter specifies the + // permitted style of the numeric string. It must be a combination of bit flags + // from the NumberStyles enumeration. The optional info parameter + // specifies the NumberFormatInfo instance to use when parsing the + // string. If the info parameter is null or omitted, the numeric + // formatting information is obtained from the current culture. + // + // Numeric strings produced by the Format methods using the Currency, + // Decimal, Engineering, Fixed point, General, or Number standard formats + // (the C, D, E, F, G, and N format specifiers) are guaranteed to be parseable + // by the Parse methods if the NumberStyles.Any style is + // specified. Note, however, that the Parse methods do not accept + // NaNs or Infinities. + + internal partial class Number + { + private const int Int32Precision = 10; + private const int UInt32Precision = Int32Precision; + private const int Int64Precision = 19; + private const int UInt64Precision = 20; + + private static bool HexNumberToInt32(ref NumberBuffer number, ref int value) + { + uint passedValue = 0; + bool returnValue = HexNumberToUInt32(ref number, ref passedValue); + value = (int)passedValue; + return returnValue; + } + + private static bool HexNumberToInt64(ref NumberBuffer number, ref long value) + { + ulong passedValue = 0; + bool returnValue = HexNumberToUInt64(ref number, ref passedValue); + value = (long)passedValue; + return returnValue; + } + + private static unsafe bool HexNumberToUInt32(ref NumberBuffer number, ref uint value) + { + int i = number.scale; + if (i > UInt32Precision || i < number.precision) + { + return false; + } + char* p = number.digits; + Debug.Assert(p != null); + + uint n = 0; + while (--i >= 0) + { + if (n > ((uint)0xFFFFFFFF / 16)) + { + return false; + } + n *= 16; + if (*p != '\0') + { + uint newN = n; + if (*p != '\0') + { + if (*p >= '0' && *p <= '9') + { + newN += (uint)(*p - '0'); + } + else + { + if (*p >= 'A' && *p <= 'F') + { + newN += (uint)((*p - 'A') + 10); + } + else + { + Debug.Assert(*p >= 'a' && *p <= 'f'); + newN += (uint)((*p - 'a') + 10); + } + } + p++; + } + + // Detect an overflow here... + if (newN < n) + { + return false; + } + n = newN; + } + } + value = n; + return true; + } + + private static unsafe bool HexNumberToUInt64(ref NumberBuffer number, ref ulong value) + { + int i = number.scale; + if (i > UInt64Precision || i < number.precision) + { + return false; + } + char* p = number.digits; + Debug.Assert(p != null); + + ulong n = 0; + while (--i >= 0) + { + if (n > (0xFFFFFFFFFFFFFFFF / 16)) + { + return false; + } + n *= 16; + if (*p != '\0') + { + ulong newN = n; + if (*p != '\0') + { + if (*p >= '0' && *p <= '9') + { + newN += (ulong)(*p - '0'); + } + else + { + if (*p >= 'A' && *p <= 'F') + { + newN += (ulong)((*p - 'A') + 10); + } + else + { + Debug.Assert(*p >= 'a' && *p <= 'f'); + newN += (ulong)((*p - 'a') + 10); + } + } + p++; + } + + // Detect an overflow here... + if (newN < n) + { + return false; + } + n = newN; + } + } + value = n; + return true; + } + + private static unsafe bool NumberToInt32(ref NumberBuffer number, ref int value) + { + int i = number.scale; + if (i > Int32Precision || i < number.precision) + { + return false; + } + char* p = number.digits; + Debug.Assert(p != null); + int n = 0; + while (--i >= 0) + { + if ((uint)n > (0x7FFFFFFF / 10)) + { + return false; + } + n *= 10; + if (*p != '\0') + { + n += (int)(*p++ - '0'); + } + } + if (number.sign) + { + n = -n; + if (n > 0) + { + return false; + } + } + else + { + if (n < 0) + { + return false; + } + } + value = n; + return true; + } + + private static unsafe bool NumberToInt64(ref NumberBuffer number, ref long value) + { + int i = number.scale; + if (i > Int64Precision || i < number.precision) + { + return false; + } + char* p = number.digits; + Debug.Assert(p != null); + long n = 0; + while (--i >= 0) + { + if ((ulong)n > (0x7FFFFFFFFFFFFFFF / 10)) + { + return false; + } + n *= 10; + if (*p != '\0') + { + n += (int)(*p++ - '0'); + } + } + if (number.sign) + { + n = -n; + if (n > 0) + { + return false; + } + } + else + { + if (n < 0) + { + return false; + } + } + value = n; + return true; + } + + private static unsafe bool NumberToUInt32(ref NumberBuffer number, ref uint value) + { + int i = number.scale; + if (i > UInt32Precision || i < number.precision || number.sign) + { + return false; + } + char* p = number.digits; + Debug.Assert(p != null); + uint n = 0; + while (--i >= 0) + { + if (n > (0xFFFFFFFF / 10)) + { + return false; + } + n *= 10; + if (*p != '\0') + { + uint newN = n + (uint)(*p++ - '0'); + // Detect an overflow here... + if (newN < n) + { + return false; + } + n = newN; + } + } + value = n; + return true; + } + + private static unsafe bool NumberToUInt64(ref NumberBuffer number, ref ulong value) + { + int i = number.scale; + if (i > UInt64Precision || i < number.precision || number.sign) + { + return false; + } + char* p = number.digits; + Debug.Assert(p != null); + ulong n = 0; + while (--i >= 0) + { + if (n > (0xFFFFFFFFFFFFFFFF / 10)) + { + return false; + } + n *= 10; + if (*p != '\0') + { + ulong newN = n + (ulong)(*p++ - '0'); + // Detect an overflow here... + if (newN < n) + { + return false; + } + n = newN; + } + } + value = n; + return true; + } + + internal unsafe static int ParseInt32(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info) + { + NumberBuffer number = default; + int i = 0; + + StringToNumber(s, style, ref number, info, false); + + if ((style & NumberStyles.AllowHexSpecifier) != 0) + { + if (!HexNumberToInt32(ref number, ref i)) + { + throw new OverflowException(SR.Overflow_Int32); + } + } + else + { + if (!NumberToInt32(ref number, ref i)) + { + throw new OverflowException(SR.Overflow_Int32); + } + } + return i; + } + + internal unsafe static long ParseInt64(ReadOnlySpan value, NumberStyles options, NumberFormatInfo numfmt) + { + NumberBuffer number = default; + long i = 0; + + StringToNumber(value, options, ref number, numfmt, false); + + if ((options & NumberStyles.AllowHexSpecifier) != 0) + { + if (!HexNumberToInt64(ref number, ref i)) + { + throw new OverflowException(SR.Overflow_Int64); + } + } + else + { + if (!NumberToInt64(ref number, ref i)) + { + throw new OverflowException(SR.Overflow_Int64); + } + } + return i; + } + + internal unsafe static uint ParseUInt32(ReadOnlySpan value, NumberStyles options, NumberFormatInfo numfmt) + { + NumberBuffer number = default; + uint i = 0; + + StringToNumber(value, options, ref number, numfmt, false); + + if ((options & NumberStyles.AllowHexSpecifier) != 0) + { + if (!HexNumberToUInt32(ref number, ref i)) + { + throw new OverflowException(SR.Overflow_UInt32); + } + } + else + { + if (!NumberToUInt32(ref number, ref i)) + { + throw new OverflowException(SR.Overflow_UInt32); + } + } + + return i; + } + + internal unsafe static ulong ParseUInt64(ReadOnlySpan value, NumberStyles options, NumberFormatInfo numfmt) + { + NumberBuffer number = default; + ulong i = 0; + + StringToNumber(value, options, ref number, numfmt, false); + if ((options & NumberStyles.AllowHexSpecifier) != 0) + { + if (!HexNumberToUInt64(ref number, ref i)) + { + throw new OverflowException(SR.Overflow_UInt64); + } + } + else + { + if (!NumberToUInt64(ref number, ref i)) + { + throw new OverflowException(SR.Overflow_UInt64); + } + } + return i; + } + + private unsafe static bool ParseNumber(ref char* str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal) + { + const int StateSign = 0x0001; + const int StateParens = 0x0002; + const int StateDigits = 0x0004; + const int StateNonZero = 0x0008; + const int StateDecimal = 0x0010; + const int StateCurrency = 0x0020; + + number.scale = 0; + number.sign = false; + string decSep; // decimal separator from NumberFormatInfo. + string groupSep; // group separator from NumberFormatInfo. + string currSymbol = null; // currency symbol from NumberFormatInfo. + + bool parsingCurrency = false; + if ((options & NumberStyles.AllowCurrencySymbol) != 0) + { + currSymbol = numfmt.CurrencySymbol; + + // The idea here is to match the currency separators and on failure match the number separators to keep the perf of VB's IsNumeric fast. + // The values of decSep are setup to use the correct relevant separator (currency in the if part and decimal in the else part). + decSep = numfmt.CurrencyDecimalSeparator; + groupSep = numfmt.CurrencyGroupSeparator; + parsingCurrency = true; + } + else + { + decSep = numfmt.NumberDecimalSeparator; + groupSep = numfmt.NumberGroupSeparator; + } + + int state = 0; + char* p = str; + char ch = *p; + char* next; + + while (true) + { + // Eat whitespace unless we've found a sign which isn't followed by a currency symbol. + // "-Kr 1231.47" is legal but "- 1231.47" is not. + if (!IsWhite(ch) || (options & NumberStyles.AllowLeadingWhite) == 0 || ((state & StateSign) != 0 && ((state & StateCurrency) == 0 && numfmt.NumberNegativePattern != 2))) + { + if ((((options & NumberStyles.AllowLeadingSign) != 0) && (state & StateSign) == 0) && ((next = MatchChars(p, numfmt.PositiveSign)) != null || ((next = MatchChars(p, numfmt.NegativeSign)) != null && (number.sign = true)))) + { + state |= StateSign; + p = next - 1; + } + else if (ch == '(' && ((options & NumberStyles.AllowParentheses) != 0) && ((state & StateSign) == 0)) + { + state |= StateSign | StateParens; + number.sign = true; + } + else if (currSymbol != null && (next = MatchChars(p, currSymbol)) != null) + { + state |= StateCurrency; + currSymbol = null; + // We already found the currency symbol. There should not be more currency symbols. Set + // currSymbol to NULL so that we won't search it again in the later code path. + p = next - 1; + } + else + { + break; + } + } + ch = *++p; + } + int digCount = 0; + int digEnd = 0; + while (true) + { + if ((ch >= '0' && ch <= '9') || (((options & NumberStyles.AllowHexSpecifier) != 0) && ((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')))) + { + state |= StateDigits; + + if (ch != '0' || (state & StateNonZero) != 0) + { + if (digCount < NumberMaxDigits) + { + number.digits[digCount++] = ch; + if (ch != '0' || parseDecimal) + { + digEnd = digCount; + } + } + if ((state & StateDecimal) == 0) + { + number.scale++; + } + state |= StateNonZero; + } + else if ((state & StateDecimal) != 0) + { + number.scale--; + } + } + else if (((options & NumberStyles.AllowDecimalPoint) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, decSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, numfmt.NumberDecimalSeparator)) != null)) + { + state |= StateDecimal; + p = next - 1; + } + else if (((options & NumberStyles.AllowThousands) != 0) && ((state & StateDigits) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, groupSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, numfmt.NumberGroupSeparator)) != null)) + { + p = next - 1; + } + else + { + break; + } + ch = *++p; + } + + bool negExp = false; + number.precision = digEnd; + number.digits[digEnd] = '\0'; + if ((state & StateDigits) != 0) + { + if ((ch == 'E' || ch == 'e') && ((options & NumberStyles.AllowExponent) != 0)) + { + char* temp = p; + ch = *++p; + if ((next = MatchChars(p, numfmt.positiveSign)) != null) + { + ch = *(p = next); + } + else if ((next = MatchChars(p, numfmt.negativeSign)) != null) + { + ch = *(p = next); + negExp = true; + } + if (ch >= '0' && ch <= '9') + { + int exp = 0; + do + { + exp = exp * 10 + (ch - '0'); + ch = *++p; + if (exp > 1000) + { + exp = 9999; + while (ch >= '0' && ch <= '9') + { + ch = *++p; + } + } + } while (ch >= '0' && ch <= '9'); + if (negExp) + { + exp = -exp; + } + number.scale += exp; + } + else + { + p = temp; + ch = *p; + } + } + while (true) + { + if (!IsWhite(ch) || (options & NumberStyles.AllowTrailingWhite) == 0) + { + if (((options & NumberStyles.AllowTrailingSign) != 0 && ((state & StateSign) == 0)) && ((next = MatchChars(p, numfmt.PositiveSign)) != null || (((next = MatchChars(p, numfmt.NegativeSign)) != null) && (number.sign = true)))) + { + state |= StateSign; + p = next - 1; + } + else if (ch == ')' && ((state & StateParens) != 0)) + { + state &= ~StateParens; + } + else if (currSymbol != null && (next = MatchChars(p, currSymbol)) != null) + { + currSymbol = null; + p = next - 1; + } + else + { + break; + } + } + ch = *++p; + } + if ((state & StateParens) == 0) + { + if ((state & StateNonZero) == 0) + { + if (!parseDecimal) + { + number.scale = 0; + } + if ((state & StateDecimal) == 0) + { + number.sign = false; + } + } + str = p; + return true; + } + } + str = p; + return false; + } + + internal unsafe static bool TryParseInt32(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out int result) + { + NumberBuffer number = default; + result = 0; + + if (!TryStringToNumber(s, style, ref number, info, false)) + { + return false; + } + + if ((style & NumberStyles.AllowHexSpecifier) != 0) + { + if (!HexNumberToInt32(ref number, ref result)) + { + return false; + } + } + else + { + if (!NumberToInt32(ref number, ref result)) + { + return false; + } + } + return true; + } + + internal unsafe static bool TryParseInt64(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out long result) + { + NumberBuffer number = default; + result = 0; + + if (!TryStringToNumber(s, style, ref number, info, false)) + { + return false; + } + + if ((style & NumberStyles.AllowHexSpecifier) != 0) + { + if (!HexNumberToInt64(ref number, ref result)) + { + return false; + } + } + else + { + if (!NumberToInt64(ref number, ref result)) + { + return false; + } + } + return true; + } + + internal unsafe static bool TryParseUInt32(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out uint result) + { + NumberBuffer number = default; + result = 0; + + if (!TryStringToNumber(s, style, ref number, info, false)) + { + return false; + } + + if ((style & NumberStyles.AllowHexSpecifier) != 0) + { + if (!HexNumberToUInt32(ref number, ref result)) + { + return false; + } + } + else + { + if (!NumberToUInt32(ref number, ref result)) + { + return false; + } + } + return true; + } + + internal unsafe static bool TryParseUInt64(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out ulong result) + { + NumberBuffer number = default; + result = 0; + + if (!TryStringToNumber(s, style, ref number, info, false)) + { + return false; + } + + if ((style & NumberStyles.AllowHexSpecifier) != 0) + { + if (!HexNumberToUInt64(ref number, ref result)) + { + return false; + } + } + else + { + if (!NumberToUInt64(ref number, ref result)) + { + return false; + } + } + return true; + } + + internal unsafe static decimal ParseDecimal(ReadOnlySpan value, NumberStyles options, NumberFormatInfo numfmt) + { + NumberBuffer number = default; + decimal result = 0; + + StringToNumber(value, options, ref number, numfmt, true); + + if (!NumberBufferToDecimal(ref number, ref result)) + { + throw new OverflowException(SR.Overflow_Decimal); + } + return result; + } + + internal unsafe static double ParseDouble(ReadOnlySpan value, NumberStyles options, NumberFormatInfo numfmt) + { + NumberBuffer number = default; + double d = 0; + + if (!TryStringToNumber(value, options, ref number, numfmt, false)) + { + //If we failed TryStringToNumber, it may be from one of our special strings. + //Check the three with which we're concerned and rethrow if it's not one of + //those strings. + ReadOnlySpan sTrim = value.Trim(); + if (StringSpanHelpers.Equals(sTrim, numfmt.PositiveInfinitySymbol)) + { + return double.PositiveInfinity; + } + if (StringSpanHelpers.Equals(sTrim, numfmt.NegativeInfinitySymbol)) + { + return double.NegativeInfinity; + } + if (StringSpanHelpers.Equals(sTrim, numfmt.NaNSymbol)) + { + return double.NaN; + } + throw new FormatException(SR.Format_InvalidString); + } + + if (!NumberBufferToDouble(ref number, ref d)) + { + throw new OverflowException(SR.Overflow_Double); + } + + return d; + } + + internal unsafe static float ParseSingle(ReadOnlySpan value, NumberStyles options, NumberFormatInfo numfmt) + { + NumberBuffer number = default; + double d = 0; + + if (!TryStringToNumber(value, options, ref number, numfmt, false)) + { + //If we failed TryStringToNumber, it may be from one of our special strings. + //Check the three with which we're concerned and rethrow if it's not one of + //those strings. + ReadOnlySpan sTrim = value.Trim(); + if (StringSpanHelpers.Equals(sTrim, numfmt.PositiveInfinitySymbol)) + { + return float.PositiveInfinity; + } + if (StringSpanHelpers.Equals(sTrim, numfmt.NegativeInfinitySymbol)) + { + return float.NegativeInfinity; + } + if (StringSpanHelpers.Equals(sTrim, numfmt.NaNSymbol)) + { + return float.NaN; + } + throw new FormatException(SR.Format_InvalidString); + } + + if (!NumberBufferToDouble(ref number, ref d)) + { + throw new OverflowException(SR.Overflow_Single); + } + float castSingle = (float)d; + if (float.IsInfinity(castSingle)) + { + throw new OverflowException(SR.Overflow_Single); + } + return castSingle; + } + + internal unsafe static bool TryParseDecimal(ReadOnlySpan value, NumberStyles options, NumberFormatInfo numfmt, out decimal result) + { + NumberBuffer number = default; + result = 0; + + if (!TryStringToNumber(value, options, ref number, numfmt, true)) + { + return false; + } + + if (!NumberBufferToDecimal(ref number, ref result)) + { + return false; + } + return true; + } + + internal unsafe static bool TryParseDouble(ReadOnlySpan value, NumberStyles options, NumberFormatInfo numfmt, out double result) + { + NumberBuffer number = default; + result = 0; + + + if (!TryStringToNumber(value, options, ref number, numfmt, false)) + { + return false; + } + if (!NumberBufferToDouble(ref number, ref result)) + { + return false; + } + return true; + } + + internal unsafe static bool TryParseSingle(ReadOnlySpan value, NumberStyles options, NumberFormatInfo numfmt, out float result) + { + NumberBuffer number = default; + result = 0; + double d = 0; + + if (!TryStringToNumber(value, options, ref number, numfmt, false)) + { + return false; + } + if (!NumberBufferToDouble(ref number, ref d)) + { + return false; + } + float castSingle = (float)d; + if (float.IsInfinity(castSingle)) + { + return false; + } + + result = castSingle; + return true; + } + + private static unsafe void StringToNumber(ReadOnlySpan str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal) + { + Debug.Assert(info != null); + fixed (char* stringPointer = &MemoryMarshal.GetReference(str)) + { + char* p = stringPointer; + if (!ParseNumber(ref p, options, ref number, info, parseDecimal) + || (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer)))) + { + throw new FormatException(SR.Format_InvalidString); + } + } + } + + internal static unsafe bool TryStringToNumber(ReadOnlySpan str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal) + { + Debug.Assert(numfmt != null); + fixed (char* stringPointer = &MemoryMarshal.GetReference(str)) + { + char* p = stringPointer; + if (!ParseNumber(ref p, options, ref number, numfmt, parseDecimal) + || (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer)))) + { + return false; + } + } + + return true; + } + + private static bool TrailingZeros(ReadOnlySpan s, int index) + { + // For compatibility, we need to allow trailing zeros at the end of a number string + for (int i = index; i < s.Length; i++) + { + if (s[i] != '\0') + { + return false; + } + } + + return true; + } + + private unsafe static char* MatchChars(char* p, string str) + { + fixed (char* stringPointer = str) + { + return MatchChars(p, stringPointer); + } + } + + private unsafe static char* MatchChars(char* p, char* str) + { + Debug.Assert(p != null && str != null); + + if (*str == '\0') + { + return null; + } + + // We only hurt the failure case + // This fix is for French or Kazakh cultures. Since a user cannot type 0xA0 as a + // space character we use 0x20 space character instead to mean the same. + while (*p == *str || (*str == '\u00a0' && *p == '\u0020')) + { + p++; + str++; + if (*str == '\0') return p; + } + + return null; + } + + private static bool IsWhite(char ch) => ch == 0x20 || (ch >= 0x09 && ch <= 0x0D); + + private static bool NumberBufferToDouble(ref NumberBuffer number, ref double value) + { + double d = NumberToDouble(ref number); + uint e = DoubleHelper.Exponent(d); + ulong m = DoubleHelper.Mantissa(d); + + if (e == 0x7FF) + { + return false; + } + + if (e == 0 && m == 0) + { + d = 0; + } + + value = d; + return true; + } + + private static class DoubleHelper + { + public static unsafe uint Exponent(double d) => + (*((uint*)&d + 1) >> 20) & 0x000007ff; + + public static unsafe ulong Mantissa(double d) => + *((ulong*)&d) & 0x000fffffffffffff; + + public static unsafe bool Sign(double d) => + (*((uint*)&d + 1) >> 31) != 0; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/ObjectDisposedException.cs b/external/corert/src/System.Private.CoreLib/shared/System/ObjectDisposedException.cs index 6e8e6b2d74..3daed13bb6 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/ObjectDisposedException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/ObjectDisposedException.cs @@ -11,6 +11,8 @@ namespace System /// The exception that is thrown when accessing an object that was /// disposed. /// + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class ObjectDisposedException : InvalidOperationException { private String _objectName; @@ -28,25 +30,26 @@ namespace System public ObjectDisposedException(String objectName, String message) : base(message) { - HResult = __HResults.COR_E_OBJECTDISPOSED; + HResult = HResults.COR_E_OBJECTDISPOSED; _objectName = objectName; } public ObjectDisposedException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_OBJECTDISPOSED; + HResult = HResults.COR_E_OBJECTDISPOSED; } protected ObjectDisposedException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); + _objectName = info.GetString("ObjectName"); } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + info.AddValue("ObjectName", ObjectName, typeof(string)); } /// @@ -69,7 +72,7 @@ namespace System { get { - if ((_objectName == null)) // && !CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) + if (_objectName == null) { return String.Empty; } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/OperationCanceledException.cs b/external/corert/src/System.Private.CoreLib/shared/System/OperationCanceledException.cs index 2c7654854f..8a472c9ff0 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/OperationCanceledException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/OperationCanceledException.cs @@ -17,6 +17,8 @@ using System.Threading; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class OperationCanceledException : SystemException { [NonSerialized] @@ -31,19 +33,19 @@ namespace System public OperationCanceledException() : base(SR.OperationCanceled) { - HResult = __HResults.COR_E_OPERATIONCANCELED; + HResult = HResults.COR_E_OPERATIONCANCELED; } public OperationCanceledException(String message) : base(message) { - HResult = __HResults.COR_E_OPERATIONCANCELED; + HResult = HResults.COR_E_OPERATIONCANCELED; } public OperationCanceledException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_OPERATIONCANCELED; + HResult = HResults.COR_E_OPERATIONCANCELED; } @@ -67,7 +69,6 @@ namespace System protected OperationCanceledException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/OverflowException.cs b/external/corert/src/System.Private.CoreLib/shared/System/OverflowException.cs index 4052e41a18..963825b350 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/OverflowException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/OverflowException.cs @@ -15,29 +15,30 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class OverflowException : ArithmeticException { public OverflowException() : base(SR.Arg_OverflowException) { - HResult = __HResults.COR_E_OVERFLOW; + HResult = HResults.COR_E_OVERFLOW; } public OverflowException(String message) : base(message) { - HResult = __HResults.COR_E_OVERFLOW; + HResult = HResults.COR_E_OVERFLOW; } public OverflowException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_OVERFLOW; + HResult = HResults.COR_E_OVERFLOW; } protected OverflowException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/ParamsArray.cs b/external/corert/src/System.Private.CoreLib/shared/System/ParamsArray.cs index 1c73bc5856..043ee679ee 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/ParamsArray.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/ParamsArray.cs @@ -4,7 +4,7 @@ namespace System { - internal struct ParamsArray + internal readonly struct ParamsArray { // Sentinel fixed-length arrays eliminate the need for a "count" field keeping this // struct down to just 4 fields. These are only used for their "Length" property, diff --git a/external/corert/src/System.Private.CoreLib/shared/System/ParseNumbers.cs b/external/corert/src/System.Private.CoreLib/shared/System/ParseNumbers.cs new file mode 100644 index 0000000000..dab4cb2190 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/ParseNumbers.cs @@ -0,0 +1,664 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; + +namespace System +{ + /// Methods for parsing numbers and strings. + internal static class ParseNumbers + { + internal const int LeftAlign = 0x0001; + internal const int RightAlign = 0x0004; + internal const int PrefixSpace = 0x0008; + internal const int PrintSign = 0x0010; + internal const int PrintBase = 0x0020; + internal const int PrintAsI1 = 0x0040; + internal const int PrintAsI2 = 0x0080; + internal const int PrintAsI4 = 0x0100; + internal const int TreatAsUnsigned = 0x0200; + internal const int TreatAsI1 = 0x0400; + internal const int TreatAsI2 = 0x0800; + internal const int IsTight = 0x1000; + internal const int NoSpace = 0x2000; + internal const int PrintRadixBase = 0x4000; + + private const int MinRadix = 2; + private const int MaxRadix = 36; + + public static unsafe long StringToLong(ReadOnlySpan s, int radix, int flags) + { + int pos = 0; + return StringToLong(s, radix, flags, ref pos); + } + + public static long StringToLong(ReadOnlySpan s, int radix, int flags, ref int currPos) + { + int i = currPos; + + // Do some radix checking. + // A radix of -1 says to use whatever base is spec'd on the number. + // Parse in Base10 until we figure out what the base actually is. + int r = (-1 == radix) ? 10 : radix; + + if (r != 2 && r != 10 && r != 8 && r != 16) + throw new ArgumentException(SR.Arg_InvalidBase, nameof(radix)); + + int length = s.Length; + + if (i < 0 || i >= length) + throw new ArgumentOutOfRangeException(SR.ArgumentOutOfRange_Index); + + // Get rid of the whitespace and then check that we've still got some digits to parse. + if (((flags & IsTight) == 0) && ((flags & NoSpace) == 0)) + { + EatWhiteSpace(s, ref i); + if (i == length) + throw new FormatException(SR.Format_EmptyInputString); + } + + // Check for a sign + int sign = 1; + if (s[i] == '-') + { + if (r != 10) + throw new ArgumentException(SR.Arg_CannotHaveNegativeValue); + + if ((flags & TreatAsUnsigned) != 0) + throw new OverflowException(SR.Overflow_NegativeUnsigned); + + sign = -1; + i++; + } + else if (s[i] == '+') + { + i++; + } + + if ((radix == -1 || radix == 16) && (i + 1 < length) && s[i] == '0') + { + if (s[i + 1] == 'x' || s[i + 1] == 'X') + { + r = 16; + i += 2; + } + } + + int grabNumbersStart = i; + long result = GrabLongs(r, s, ref i, (flags & TreatAsUnsigned) != 0); + + // Check if they passed us a string with no parsable digits. + if (i == grabNumbersStart) + throw new FormatException(SR.Format_NoParsibleDigits); + + if ((flags & IsTight) != 0) + { + //If we've got effluvia left at the end of the string, complain. + if (i < length) + throw new FormatException(SR.Format_ExtraJunkAtEnd); + } + + // Put the current index back into the correct place. + currPos = i; + + // Return the value properly signed. + if ((ulong)result == 0x8000000000000000 && sign == 1 && r == 10 && ((flags & TreatAsUnsigned) == 0)) + throw new OverflowException(SR.Overflow_Int64); + + if (r == 10) + { + result *= sign; + } + + return result; + } + + public static int StringToInt(ReadOnlySpan s, int radix, int flags) + { + int pos = 0; + return StringToInt(s, radix, flags, ref pos); + } + + public static int StringToInt(ReadOnlySpan s, int radix, int flags, ref int currPos) + { + // They're requied to tell me where to start parsing. + int i = currPos; + + // Do some radix checking. + // A radix of -1 says to use whatever base is spec'd on the number. + // Parse in Base10 until we figure out what the base actually is. + int r = (-1 == radix) ? 10 : radix; + + if (r != 2 && r != 10 && r != 8 && r != 16) + throw new ArgumentException(SR.Arg_InvalidBase, nameof(radix)); + + int length = s.Length; + + if (i < 0 || i >= length) + throw new ArgumentOutOfRangeException(SR.ArgumentOutOfRange_Index); + + // Get rid of the whitespace and then check that we've still got some digits to parse. + if (((flags & IsTight) == 0) && ((flags & NoSpace) == 0)) + { + EatWhiteSpace(s, ref i); + if (i == length) + throw new FormatException(SR.Format_EmptyInputString); + } + + // Check for a sign + int sign = 1; + if (s[i] == '-') + { + if (r != 10) + throw new ArgumentException(SR.Arg_CannotHaveNegativeValue); + + if ((flags & TreatAsUnsigned) != 0) + throw new OverflowException(SR.Overflow_NegativeUnsigned); + + sign = -1; + i++; + } + else if (s[i] == '+') + { + i++; + } + + // Consume the 0x if we're in an unknown base or in base-16. + if ((radix == -1 || radix == 16) && (i + 1 < length) && s[i] == '0') + { + if (s[i + 1] == 'x' || s[i + 1] == 'X') + { + r = 16; + i += 2; + } + } + + int grabNumbersStart = i; + int result = GrabInts(r, s, ref i, ((flags & TreatAsUnsigned) != 0)); + + // Check if they passed us a string with no parsable digits. + if (i == grabNumbersStart) + throw new FormatException(SR.Format_NoParsibleDigits); + + if ((flags & IsTight) != 0) + { + // If we've got effluvia left at the end of the string, complain. + if (i < length) + throw new FormatException(SR.Format_ExtraJunkAtEnd); + } + + // Put the current index back into the correct place. + currPos = i; + + // Return the value properly signed. + if ((flags & TreatAsI1) != 0) + { + if ((uint)result > 0xFF) + throw new OverflowException(SR.Overflow_SByte); + } + else if ((flags & TreatAsI2) != 0) + { + if ((uint)result > 0xFFFF) + throw new OverflowException(SR.Overflow_Int16); + } + else if ((uint)result == 0x80000000 && sign == 1 && r == 10 && ((flags & TreatAsUnsigned) == 0)) + { + throw new OverflowException(SR.Overflow_Int32); + } + + if (r == 10) + { + result *= sign; + } + + return result; + } + + public static string IntToString(int n, int radix, int width, char paddingChar, int flags) + { + Span buffer = stackalloc char[66]; // Longest possible string length for an integer in binary notation with prefix + + if (radix < MinRadix || radix > MaxRadix) + throw new ArgumentException(SR.Arg_InvalidBase, nameof(radix)); + + // If the number is negative, make it positive and remember the sign. + // If the number is MIN_VALUE, this will still be negative, so we'll have to + // special case this later. + bool isNegative = false; + uint l; + if (n < 0) + { + isNegative = true; + + // For base 10, write out -num, but other bases write out the + // 2's complement bit pattern + l = (10 == radix) ? (uint)-n : (uint)n; + } + else + { + l = (uint)n; + } + + // The conversion to a uint will sign extend the number. In order to ensure + // that we only get as many bits as we expect, we chop the number. + if ((flags & PrintAsI1) != 0) + { + l &= 0xFF; + } + else if ((flags & PrintAsI2) != 0) + { + l &= 0xFFFF; + } + + // Special case the 0. + int index; + if (0 == l) + { + buffer[0] = '0'; + index = 1; + } + else + { + index = 0; + for (int i = 0; i < buffer.Length; i++) // for(...;i buffer = stackalloc char[67]; // Longest possible string length for an integer in binary notation with prefix + + if (radix < MinRadix || radix > MaxRadix) + throw new ArgumentException(SR.Arg_InvalidBase, nameof(radix)); + + //If the number is negative, make it positive and remember the sign. + ulong ul; + bool isNegative = false; + if (n < 0) + { + isNegative = true; + + // For base 10, write out -num, but other bases write out the + // 2's complement bit pattern + ul = (10 == radix) ? (ulong)(-n) : (ulong)n; + } + else + { + ul = (ulong)n; + } + + if ((flags & PrintAsI1) != 0) + { + ul = ul & 0xFF; + } + else if ((flags & PrintAsI2) != 0) + { + ul = ul & 0xFFFF; + } + else if ((flags & PrintAsI4) != 0) + { + ul = ul & 0xFFFFFFFF; + } + + //Special case the 0. + int index; + if (0 == ul) + { + buffer[0] = '0'; + index = 1; + } + else + { + index = 0; + for (int i = 0; i < buffer.Length; i++) // for loop instead of do{...}while(l!=0) to help JIT eliminate span bounds checks + { + ulong div = ul / (ulong)radix; // TODO https://github.com/dotnet/coreclr/issues/3439 + int charVal = (int)(ul - (div * (ulong)radix)); + ul = div; + + buffer[i] = (charVal < 10) ? + (char)(charVal + '0') : + (char)(charVal + 'a' - 10); + + if (ul == 0) + { + index = i + 1; + break; + } + } + Debug.Assert(ul == 0, $"Expected {ul} == 0"); + } + + //If they want the base, append that to the string (in reverse order) + if (radix != 10 && ((flags & PrintBase) != 0)) + { + if (16 == radix) + { + buffer[index++] = 'x'; + buffer[index++] = '0'; + } + else if (8 == radix) + { + buffer[index++] = '0'; + } + else if ((flags & PrintRadixBase) != 0) + { + buffer[index++] = '#'; + buffer[index++] = (char)((radix % 10) + '0'); + buffer[index++] = (char)((radix / 10) + '0'); + } + } + + if (10 == radix) + { + //If it was negative, append the sign. + if (isNegative) + { + buffer[index++] = '-'; + } + + //else if they requested, add the '+'; + else if ((flags & PrintSign) != 0) + { + buffer[index++] = '+'; + } + + //If they requested a leading space, put it on. + else if ((flags & PrefixSpace) != 0) + { + buffer[index++] = ' '; + } + } + + // Figure out the size of and allocate the resulting string + string result = string.FastAllocateString(Math.Max(width, index)); + unsafe + { + // Put the characters into the string in reverse order. + // Fill the remaining space, if there is any, with the correct padding character. + fixed (char* resultPtr = result) + { + char* p = resultPtr; + int padding = result.Length - index; + + if ((flags & LeftAlign) != 0) + { + for (int i = 0; i < padding; i++) + { + *p++ = paddingChar; + } + + for (int i = 0; i < index; i++) + { + *p++ = buffer[index - i - 1]; + } + } + else + { + for (int i = 0; i < index; i++) + { + *p++ = buffer[index - i - 1]; + } + + for (int i = 0; i < padding; i++) + { + *p++ = paddingChar; + } + } + + Debug.Assert((p - resultPtr) == result.Length, $"Expected {p - resultPtr} == {result.Length}"); + } + } + return result; + } + + private static void EatWhiteSpace(ReadOnlySpan s, ref int i) + { + int localIndex = i; + for (; localIndex < s.Length && char.IsWhiteSpace(s[localIndex]); localIndex++); + i = localIndex; + } + + private static long GrabLongs(int radix, ReadOnlySpan s, ref int i, bool isUnsigned) + { + ulong result = 0; + ulong maxVal; + + // Allow all non-decimal numbers to set the sign bit. + if (radix == 10 && !isUnsigned) + { + maxVal = 0x7FFFFFFFFFFFFFFF / 10; + + // Read all of the digits and convert to a number + while (i < s.Length && IsDigit(s[i], radix, out int value)) + { + // Check for overflows - this is sufficient & correct. + if (result > maxVal || ((long)result) < 0) + { + ThrowOverflowInt64Exception(); + } + + result = result * (ulong)radix + (ulong)value; + i++; + } + + if ((long)result < 0 && result != 0x8000000000000000) + { + ThrowOverflowInt64Exception(); + } + } + else + { + Debug.Assert(radix == 2 || radix == 8 || radix == 10 || radix == 16); + maxVal = + radix == 10 ? 0xffffffffffffffff / 10 : + radix == 16 ? 0xffffffffffffffff / 16 : + radix == 8 ? 0xffffffffffffffff / 8 : + 0xffffffffffffffff / 2; + + // Read all of the digits and convert to a number + while (i < s.Length && IsDigit(s[i], radix, out int value)) + { + // Check for overflows - this is sufficient & correct. + if (result > maxVal) + { + ThrowOverflowUInt64Exception(); + } + + ulong temp = result * (ulong)radix + (ulong)value; + + if (temp < result) // this means overflow as well + { + ThrowOverflowUInt64Exception(); + } + + result = temp; + i++; + } + } + + return (long)result; + } + + private static int GrabInts(int radix, ReadOnlySpan s, ref int i, bool isUnsigned) + { + uint result = 0; + uint maxVal; + + // Allow all non-decimal numbers to set the sign bit. + if (radix == 10 && !isUnsigned) + { + maxVal = (0x7FFFFFFF / 10); + + // Read all of the digits and convert to a number + while (i < s.Length && IsDigit(s[i], radix, out int value)) + { + // Check for overflows - this is sufficient & correct. + if (result > maxVal || (int)result < 0) + { + ThrowOverflowInt32Exception(); + } + result = result * (uint)radix + (uint)value; + i++; + } + if ((int)result < 0 && result != 0x80000000) + { + ThrowOverflowInt32Exception(); + } + } + else + { + Debug.Assert(radix == 2 || radix == 8 || radix == 10 || radix == 16); + maxVal = + radix == 10 ? 0xffffffff / 10 : + radix == 16 ? 0xffffffff / 16 : + radix == 8 ? 0xffffffff / 8 : + 0xffffffff / 2; + + // Read all of the digits and convert to a number + while (i < s.Length && IsDigit(s[i], radix, out int value)) + { + // Check for overflows - this is sufficient & correct. + if (result > maxVal) + { + throw new OverflowException(SR.Overflow_UInt32); + } + + uint temp = result * (uint)radix + (uint)value; + + if (temp < result) // this means overflow as well + { + ThrowOverflowUInt32Exception(); + } + + result = temp; + i++; + } + } + + return (int)result; + } + + private static void ThrowOverflowInt32Exception() => throw new OverflowException(SR.Overflow_Int32); + private static void ThrowOverflowInt64Exception() => throw new OverflowException(SR.Overflow_Int64); + private static void ThrowOverflowUInt32Exception() => throw new OverflowException(SR.Overflow_UInt32); + private static void ThrowOverflowUInt64Exception() => throw new OverflowException(SR.Overflow_UInt64); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsDigit(char c, int radix, out int result) + { + int tmp; + if ((uint)(c - '0') <= 9) + { + result = tmp = c - '0'; + } + else if ((uint)(c - 'A') <= 'Z' - 'A') + { + result = tmp = c - 'A' + 10; + } + else if ((uint)(c - 'a') <= 'z' - 'a') + { + result = tmp = c - 'a' + 10; + } + else + { + result = -1; + return false; + } + + return tmp < radix; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/PlatformNotSupportedException.cs b/external/corert/src/System.Private.CoreLib/shared/System/PlatformNotSupportedException.cs index f679ac9454..5039f3f441 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/PlatformNotSupportedException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/PlatformNotSupportedException.cs @@ -15,29 +15,30 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class PlatformNotSupportedException : NotSupportedException { public PlatformNotSupportedException() : base(SR.Arg_PlatformNotSupported) { - HResult = __HResults.COR_E_PLATFORMNOTSUPPORTED; + HResult = HResults.COR_E_PLATFORMNOTSUPPORTED; } public PlatformNotSupportedException(String message) : base(message) { - HResult = __HResults.COR_E_PLATFORMNOTSUPPORTED; + HResult = HResults.COR_E_PLATFORMNOTSUPPORTED; } public PlatformNotSupportedException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_PLATFORMNOTSUPPORTED; + HResult = HResults.COR_E_PLATFORMNOTSUPPORTED; } protected PlatformNotSupportedException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Random.cs b/external/corert/src/System.Private.CoreLib/shared/System/Random.cs index 4affed8a1a..20f035e2e8 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Random.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Random.cs @@ -2,20 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -/*============================================================ -** -** -** Purpose: A random number generator. -** -** -===========================================================*/ - -using System; -using System.Runtime; -using System.Runtime.CompilerServices; -using System.Globalization; -using System.Diagnostics.Contracts; - namespace System { public class Random @@ -23,11 +9,10 @@ namespace System // // Private Constants // - private const int MBIG = Int32.MaxValue; + private const int MBIG = int.MaxValue; private const int MSEED = 161803398; private const int MZ = 0; - // // Member Variables // @@ -64,7 +49,7 @@ namespace System int mj, mk; //Initialize our Seed array. - int subtraction = (Seed == Int32.MinValue) ? Int32.MaxValue : Math.Abs(Seed); + int subtraction = (Seed == int.MinValue) ? int.MaxValue : Math.Abs(Seed); mj = MSEED - subtraction; _seedArray[55] = mj; mk = 1; @@ -195,8 +180,8 @@ namespace System result = -result; } double d = result; - d += (Int32.MaxValue - 1); // get a number in range [0 .. 2 * Int32MaxValue - 1) - d /= 2 * (uint)Int32.MaxValue - 1; + d += (int.MaxValue - 1); // get a number in range [0 .. 2 * Int32MaxValue - 1) + d /= 2 * (uint)int.MaxValue - 1; return d; } @@ -213,10 +198,9 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(minValue), SR.Format(SR.Argument_MinMaxValue, nameof(minValue), nameof(maxValue))); } - Contract.EndContractBlock(); long range = (long)maxValue - minValue; - if (range <= (long)Int32.MaxValue) + if (range <= int.MaxValue) { return ((int)(Sample() * range) + minValue); } @@ -238,7 +222,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(maxValue), SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(maxValue))); } - Contract.EndContractBlock(); return (int)(Sample() * maxValue); } @@ -263,10 +246,17 @@ namespace System public virtual void NextBytes(byte[] buffer) { if (buffer == null) throw new ArgumentNullException(nameof(buffer)); - Contract.EndContractBlock(); for (int i = 0; i < buffer.Length; i++) { - buffer[i] = (byte)(InternalSample() % (Byte.MaxValue + 1)); + buffer[i] = (byte)InternalSample(); + } + } + + public virtual void NextBytes(Span buffer) + { + for (int i = 0; i < buffer.Length; i++) + { + buffer[i] = (byte)Next(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/RankException.cs b/external/corert/src/System.Private.CoreLib/shared/System/RankException.cs index 15759ea75d..bdd2cd51f1 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/RankException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/RankException.cs @@ -16,29 +16,30 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class RankException : SystemException { public RankException() : base(SR.Arg_RankException) { - HResult = __HResults.COR_E_RANK; + HResult = HResults.COR_E_RANK; } public RankException(String message) : base(message) { - HResult = __HResults.COR_E_RANK; + HResult = HResults.COR_E_RANK; } public RankException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_RANK; + HResult = HResults.COR_E_RANK; } protected RankException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corefx/src/System.Memory/src/System/ReadOnlyMemory.cs b/external/corert/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs similarity index 61% rename from external/corefx/src/System.Memory/src/System/ReadOnlyMemory.cs rename to external/corert/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs index 3295b214ef..5d8f00f8ad 100644 --- a/external/corefx/src/System.Memory/src/System/ReadOnlyMemory.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs @@ -3,33 +3,34 @@ // See the LICENSE file in the project root for more information. using System.Buffers; -#if !MONO -using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute; -using EditorBrowsableState = System.ComponentModel.EditorBrowsableState; -#endif using System.Diagnostics; -using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute; +using EditorBrowsableState = System.ComponentModel.EditorBrowsableState; +using Internal.Runtime.CompilerServices; namespace System { /// - /// ReadOnlyMemory represents a contiguous region of arbitrary similar to ReadOnlySpan. - /// Unlike ReadOnlySpan, it is not a byref-like type. + /// Represents a contiguous region of memory, similar to . + /// Unlike , it is not a byref-like type. /// [DebuggerDisplay("{DebuggerDisplay,nq}")] [DebuggerTypeProxy(typeof(MemoryDebugView<>))] public readonly struct ReadOnlyMemory { - // The highest order bit of _index is used to discern whether _arrayOrOwnedMemory is an array or an owned memory - // if (_index >> 31) == 1, object _arrayOrOwnedMemory is an OwnedMemory - // else, object _arrayOrOwnedMemory is a T[] - private readonly object _arrayOrOwnedMemory; + // NOTE: With the current implementation, Memory and ReadOnlyMemory must have the same layout, + // as code uses Unsafe.As to cast between them. + + // The highest order bit of _index is used to discern whether _object is an array/string or an owned memory + // if (_index >> 31) == 1, _object is an OwnedMemory + // else, _object is a T[] or string + private readonly object _object; private readonly int _index; private readonly int _length; - private const int RemoveOwnedFlagBitMask = 0x7FFFFFFF; + internal const int RemoveOwnedFlagBitMask = 0x7FFFFFFF; /// /// Creates a new memory over the entirety of the target array. @@ -44,7 +45,7 @@ namespace System if (array == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - _arrayOrOwnedMemory = array; + _object = array; _index = 0; _length = array.Length; } @@ -68,24 +69,23 @@ namespace System if (array == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); + ThrowHelper.ThrowArgumentOutOfRangeException(); - _arrayOrOwnedMemory = array; + _object = array; _index = start; _length = length; } - - // Constructor for internal use only. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal ReadOnlyMemory(OwnedMemory owner, int index, int length) - { - if (owner == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.ownedMemory); - if (index < 0 || length < 0) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); - _arrayOrOwnedMemory = owner; - _index = index | (1 << 31); // Before using _index, check if _index < 0, then 'and' it with RemoveOwnedFlagBitMask + /// Creates a new memory over the existing object, start, and length. No validation is performed. + /// The target object. + /// The index at which to begin the memory. + /// The number of items in the memory. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ReadOnlyMemory(object obj, int start, int length) + { + // No validation performed; caller must provide any necessary validation. + _object = obj; + _index = start; _length = length; } @@ -93,19 +93,19 @@ namespace System private string DebuggerDisplay => string.Format("{{{0}[{1}]}}", typeof(T).Name, _length); /// - /// Defines an implicit conversion of an array to a + /// Defines an implicit conversion of an array to a /// public static implicit operator ReadOnlyMemory(T[] array) => new ReadOnlyMemory(array); /// - /// Defines an implicit conversion of a to a + /// Defines an implicit conversion of a to a /// public static implicit operator ReadOnlyMemory(ArraySegment arraySegment) => new ReadOnlyMemory(arraySegment.Array, arraySegment.Offset, arraySegment.Count); /// - /// Returns an empty + /// Returns an empty /// - public static ReadOnlyMemory Empty { get; } = SpanHelpers.PerTypeValues.EmptyArray; + public static ReadOnlyMemory Empty => default; /// /// The number of items in the memory. @@ -128,11 +128,11 @@ namespace System public ReadOnlyMemory Slice(int start) { if ((uint)start > (uint)_length) + { ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); + } - if (_index < 0) - return new ReadOnlyMemory((OwnedMemory)_arrayOrOwnedMemory, (_index & RemoveOwnedFlagBitMask) + start, _length - start); - return new ReadOnlyMemory((T[])_arrayOrOwnedMemory, _index + start, _length - start); + return new ReadOnlyMemory(_object, _index + start, _length - start); } /// @@ -147,11 +147,11 @@ namespace System public ReadOnlyMemory Slice(int start, int length) { if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start)) + { ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); + } - if (_index < 0) - return new ReadOnlyMemory((OwnedMemory)_arrayOrOwnedMemory, (_index & RemoveOwnedFlagBitMask) + start, length); - return new ReadOnlyMemory((T[])_arrayOrOwnedMemory, _index + start, length); + return new ReadOnlyMemory(_object, _index + start, length); } /// @@ -163,30 +163,72 @@ namespace System get { if (_index < 0) - return ((OwnedMemory)_arrayOrOwnedMemory).Span.Slice(_index & RemoveOwnedFlagBitMask, _length); - return new ReadOnlySpan((T[])_arrayOrOwnedMemory, _index, _length); + { + return ((OwnedMemory)_object).Span.Slice(_index & RemoveOwnedFlagBitMask, _length); + } + else if (typeof(T) == typeof(char) && _object is string s) + { + return new ReadOnlySpan(ref Unsafe.As(ref s.GetRawStringData()), s.Length).Slice(_index, _length); + } + else if (_object != null) + { + return new ReadOnlySpan((T[])_object, _index, _length); + } + else + { + return default; + } } } /// - /// Returns a handle for the array. - /// If pin is true, the GC will not move the array and hence its address can be taken + /// Copies the contents of the read-only memory into the destination. If the source + /// and destination overlap, this method behaves as if the original values are in + /// a temporary location before the destination is overwritten. + /// + /// The Memory to copy items into. + /// + /// Thrown when the destination is shorter than the source. + /// /// + public void CopyTo(Memory destination) => Span.CopyTo(destination.Span); + + /// + /// Copies the contents of the readonly-only memory into the destination. If the source + /// and destination overlap, this method behaves as if the original values are in + /// a temporary location before the destination is overwritten. + /// + /// If the destination is shorter than the source, this method + /// return false and no data is written to the destination. + /// + /// The span to copy items into. + public bool TryCopyTo(Memory destination) => Span.TryCopyTo(destination.Span); + + /// Creates a handle for the memory. + /// + /// If pin is true, the GC will not move the array until the returned + /// is disposed, enabling the memory's address can be taken and used. + /// public unsafe MemoryHandle Retain(bool pin = false) { - MemoryHandle memoryHandle; + MemoryHandle memoryHandle = default; if (pin) { if (_index < 0) { - memoryHandle = ((OwnedMemory)_arrayOrOwnedMemory).Pin(); + memoryHandle = ((OwnedMemory)_object).Pin(); memoryHandle.AddOffset((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf()); } - else + else if (typeof(T) == typeof(char) && _object is string s) + { + GCHandle handle = GCHandle.Alloc(s, GCHandleType.Pinned); + void* pointer = Unsafe.Add(Unsafe.AsPointer(ref s.GetRawStringData()), _index); + memoryHandle = new MemoryHandle(null, pointer, handle); + } + else if (_object is T[] array) { - var array = (T[])_arrayOrOwnedMemory; var handle = GCHandle.Alloc(array, GCHandleType.Pinned); - void* pointer = Unsafe.Add((void*)handle.AddrOfPinnedObject(), _index); + void* pointer = Unsafe.Add(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index); memoryHandle = new MemoryHandle(null, pointer, handle); } } @@ -194,44 +236,13 @@ namespace System { if (_index < 0) { - ((OwnedMemory)_arrayOrOwnedMemory).Retain(); - memoryHandle = new MemoryHandle((OwnedMemory)_arrayOrOwnedMemory); - } - else - { - memoryHandle = new MemoryHandle(null); + ((OwnedMemory)_object).Retain(); + memoryHandle = new MemoryHandle((OwnedMemory)_object); } } return memoryHandle; } - /// - /// Get an array segment from the underlying memory. - /// If unable to get the array segment, return false with a default array segment. - /// -#if !MONO - [EditorBrowsable(EditorBrowsableState.Never)] -#endif - public bool DangerousTryGetArray(out ArraySegment arraySegment) - { - if (_index < 0) - { - if (((OwnedMemory)_arrayOrOwnedMemory).TryGetArray(out var segment)) - { - arraySegment = new ArraySegment(segment.Array, segment.Offset + (_index & RemoveOwnedFlagBitMask), _length); - return true; - } - } - else - { - arraySegment = new ArraySegment((T[])_arrayOrOwnedMemory, _index, _length); - return true; - } - - arraySegment = default(ArraySegment); - return false; - } - /// /// Copies the contents from the memory into a new array. This heap /// allocates, so should generally be avoided, however it is sometimes @@ -239,12 +250,8 @@ namespace System /// public T[] ToArray() => Span.ToArray(); - /// - /// Determines whether the specified object is equal to the current object. - /// -#if !MONO + /// Determines whether the specified object is equal to the current object. [EditorBrowsable(EditorBrowsableState.Never)] -#endif public override bool Equals(object obj) { if (obj is ReadOnlyMemory readOnlyMemory) @@ -268,20 +275,16 @@ namespace System public bool Equals(ReadOnlyMemory other) { return - _arrayOrOwnedMemory == other._arrayOrOwnedMemory && + _object == other._object && _index == other._index && _length == other._length; } - /// - /// Serves as the default hash function. - /// -#if !MONO - [EditorBrowsable( EditorBrowsableState.Never)] -#endif + /// Returns the hash code for this + [EditorBrowsable(EditorBrowsableState.Never)] public override int GetHashCode() { - return CombineHashCodes(_arrayOrOwnedMemory.GetHashCode(), (_index & RemoveOwnedFlagBitMask).GetHashCode(), _length.GetHashCode()); + return _object != null ? CombineHashCodes(_object.GetHashCode(), _index.GetHashCode(), _length.GetHashCode()) : 0; } private static int CombineHashCodes(int left, int right) @@ -294,5 +297,16 @@ namespace System return CombineHashCodes(CombineHashCodes(h1, h2), h3); } + /// Gets the state of the memory as individual fields. + /// The offset. + /// The count. + /// The object. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal object GetObjectStartLength(out int start, out int length) + { + start = _index; + length = _length; + return _object; + } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/ReadOnlySpan.cs b/external/corert/src/System.Private.CoreLib/shared/System/ReadOnlySpan.cs index 0851b255df..c49dddf986 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/ReadOnlySpan.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/ReadOnlySpan.cs @@ -2,10 +2,11 @@ // 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.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; -using EditorBrowsableState = System.ComponentModel.EditorBrowsableState; -using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute; +using System.Runtime.Versioning; +using Internal.Runtime.CompilerServices; #pragma warning disable 0809 //warning CS0809: Obsolete member 'Span.Equals(object)' overrides non-obsolete member 'object.Equals(object)' @@ -15,10 +16,13 @@ namespace System /// ReadOnlySpan represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed /// or native memory, or to memory allocated on the stack. It is type- and memory-safe. /// - public struct ReadOnlySpan + [DebuggerTypeProxy(typeof(SpanDebugView<>))] + [DebuggerDisplay("{DebuggerDisplay,nq}")] + [NonVersionable] + public readonly ref struct ReadOnlySpan { /// A byref or a native ptr. - private readonly ByReference _pointer; + internal readonly ByReference _pointer; /// The number of elements this ReadOnlySpan contains. #if PROJECTN [Bound] @@ -41,29 +45,6 @@ namespace System _length = array.Length; } - /// - /// Creates a new read-only span over the portion of the target array beginning - /// at 'start' index and covering the remainder of the array. - /// - /// The target array. - /// The index at which to begin the read-only span. - /// Thrown when is a null - /// reference (Nothing in Visual Basic). - /// - /// Thrown when the specified is not in the range (<0 or >=Length). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlySpan(T[] array, int start) - { - if (array == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - if ((uint)start > (uint)array.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(); - - _pointer = new ByReference(ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), start)); - _length = array.Length - start; - } - /// /// Creates a new read-only span over the portion of the target array beginning /// at 'start' index and ending at 'end' index (exclusive). @@ -125,6 +106,7 @@ namespace System /// A reference to data within that object. /// The number of elements the memory contains. [MethodImpl(MethodImplOptions.AggressiveInlining)] + [EditorBrowsable(EditorBrowsableState.Never)] public static ReadOnlySpan DangerousCreate(object obj, ref T objectData, int length) => new ReadOnlySpan(ref objectData, length); // Constructor for internal use only. @@ -137,12 +119,16 @@ namespace System _length = length; } + //Debugger Display = {T[length]} + private string DebuggerDisplay => string.Format("{{{0}[{1}]}}", typeof(T).Name, _length); + /// /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T DangerousGetPinnableReference() + [EditorBrowsable(EditorBrowsableState.Never)] + internal ref readonly T DangerousGetPinnableReference() { return ref _pointer.Value; } @@ -150,12 +136,26 @@ namespace System /// /// The number of items in the read-only span. /// - public int Length => _length; + public int Length + { + [NonVersionable] + get + { + return _length; + } + } /// /// Returns true if Length is 0. /// - public bool IsEmpty => _length == 0; + public bool IsEmpty + { + [NonVersionable] + get + { + return _length == 0; + } + } /// /// Returns the specified element of the read-only span. @@ -165,24 +165,23 @@ namespace System /// /// Thrown when index less than 0 or index greater than or equal to Length /// - public T this[int index] + public ref readonly T this[int index] { #if PROJECTN [BoundsChecking] get { - return Unsafe.Add(ref _pointer.Value, index); + return ref Unsafe.Add(ref _pointer.Value, index); } #else -#if CORERT [Intrinsic] -#endif [MethodImpl(MethodImplOptions.AggressiveInlining)] + [NonVersionable] get { if ((uint)index >= (uint)_length) ThrowHelper.ThrowIndexOutOfRangeException(); - return Unsafe.Add(ref _pointer.Value, index); + return ref Unsafe.Add(ref _pointer.Value, index); } #endif } @@ -263,12 +262,13 @@ namespace System /// /// Defines an implicit conversion of an array to a /// - public static implicit operator ReadOnlySpan(T[] array) => new ReadOnlySpan(array); + public static implicit operator ReadOnlySpan(T[] array) => array != null ? new ReadOnlySpan(array) : default; /// /// Defines an implicit conversion of a to a /// - public static implicit operator ReadOnlySpan(ArraySegment arraySegment) => new ReadOnlySpan(arraySegment.Array, arraySegment.Offset, arraySegment.Count); + public static implicit operator ReadOnlySpan(ArraySegment arraySegment) + => arraySegment.Array != null ? new ReadOnlySpan(arraySegment.Array, arraySegment.Offset, arraySegment.Count) : default; /// /// Forms a slice out of the given read-only span, beginning at 'start'. @@ -322,5 +322,47 @@ namespace System /// Returns a 0-length read-only span whose base is the null pointer. /// public static ReadOnlySpan Empty => default(ReadOnlySpan); + + /// Gets an enumerator for this span. + public Enumerator GetEnumerator() => new Enumerator(this); + + /// Enumerates the elements of a . + public ref struct Enumerator + { + /// The span being enumerated. + private readonly ReadOnlySpan _span; + /// The next index to yield. + private int _index; + + /// Initialize the enumerator. + /// The span to enumerate. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Enumerator(ReadOnlySpan span) + { + _span = span; + _index = -1; + } + + /// Advances the enumerator to the next element of the span. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + int index = _index + 1; + if (index < _span.Length) + { + _index = index; + return true; + } + + return false; + } + + /// Gets the element at the current position of the enumerator. + public ref readonly T Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref _span[_index]; + } + } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AmbiguousMatchException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AmbiguousMatchException.cs index a0075bbc0b..643a127c49 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AmbiguousMatchException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AmbiguousMatchException.cs @@ -6,24 +6,30 @@ using System.Runtime.Serialization; namespace System.Reflection { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class AmbiguousMatchException : SystemException { public AmbiguousMatchException() : base(SR.RFLCT_Ambiguous) { - HResult = __HResults.COR_E_AMBIGUOUSMATCH; + HResult = HResults.COR_E_AMBIGUOUSMATCH; } public AmbiguousMatchException(string message) : base(message) { - HResult = __HResults.COR_E_AMBIGUOUSMATCH; + HResult = HResults.COR_E_AMBIGUOUSMATCH; } public AmbiguousMatchException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_AMBIGUOUSMATCH; + HResult = HResults.COR_E_AMBIGUOUSMATCH; + } + + internal AmbiguousMatchException(SerializationInfo info, StreamingContext context) : base(info, context) + { } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/Assembly.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/Assembly.cs index b965c9f7fb..7280869c02 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/Assembly.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/Assembly.cs @@ -60,6 +60,7 @@ namespace System.Reflection public virtual IEnumerable ExportedTypes => GetExportedTypes(); public virtual Type[] GetExportedTypes() { throw NotImplemented.ByDesign; } + public virtual Type[] GetForwardedTypes() { throw NotImplemented.ByDesign; } public virtual string CodeBase { get { throw NotImplemented.ByDesign; } } public virtual MethodInfo EntryPoint { get { throw NotImplemented.ByDesign; } } @@ -125,10 +126,7 @@ namespace System.Reflection public virtual FileStream[] GetFiles() => GetFiles(getResourceModules: false); public virtual FileStream[] GetFiles(bool getResourceModules) { throw NotImplemented.ByDesign; } - public virtual void GetObjectData(SerializationInfo info, StreamingContext context) - { - throw new PlatformNotSupportedException(); - } + public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { throw NotImplemented.ByDesign; } public override string ToString() { diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/BindingFlags.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/BindingFlags.cs index 26c875d0f9..7ba83e20da 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/BindingFlags.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/BindingFlags.cs @@ -46,5 +46,6 @@ namespace System.Reflection // These are a couple of misc attributes used IgnoreReturn = 0x01000000, // This is used in COM Interop + DoNotWrapExceptions = 0x02000000, // Disables wrapping exceptions in TargetInvocationException } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/CustomAttributeFormatException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/CustomAttributeFormatException.cs index 13766ae8d0..1d7d4a7671 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/CustomAttributeFormatException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/CustomAttributeFormatException.cs @@ -6,6 +6,8 @@ using System.Runtime.Serialization; namespace System.Reflection { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class CustomAttributeFormatException : FormatException { public CustomAttributeFormatException() @@ -21,13 +23,12 @@ namespace System.Reflection public CustomAttributeFormatException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_CUSTOMATTRIBUTEFORMAT; + HResult = HResults.COR_E_CUSTOMATTRIBUTEFORMAT; } protected CustomAttributeFormatException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/IReflect.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/IReflect.cs index 51141ae47c..a7e84b6168 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/IReflect.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/IReflect.cs @@ -47,7 +47,7 @@ namespace System.Reflection MemberInfo[] GetMembers(BindingFlags bindingAttr); // Description of the Binding Process. - // We must invoke a method that is accessable and for which the provided + // We must invoke a method that is accessible and for which the provided // parameters have the most specific match. A method may be called if // 1. The number of parameters in the method declaration equals the number of // arguments provided to the invocation @@ -59,7 +59,7 @@ namespace System.Reflection // of methods is filtered by the name, number of arguments and a set of search modifiers // defined in the Binder. // - // After the method is selected, it will be invoked. Accessability is checked + // After the method is selected, it will be invoked. Accessibility is checked // at that point. The search may be control which set of methods are searched based // upon the accessibility attribute associated with the method. // diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/IntrospectionExtensions.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/IntrospectionExtensions.cs index 6a18fdaa72..acf5987d44 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/IntrospectionExtensions.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/IntrospectionExtensions.cs @@ -13,7 +13,10 @@ namespace System.Reflection if (type == null) throw new ArgumentNullException(nameof(type)); - return ((IReflectableType)type).GetTypeInfo(); // Unguarded cast is unbecoming but kept for compatibility. + if (type is IReflectableType reflectableType) + return reflectableType.GetTypeInfo(); + + return new TypeDelegator(type); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/InvalidFilterCriteriaException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/InvalidFilterCriteriaException.cs index 07880a768d..dedcc54f4c 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/InvalidFilterCriteriaException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/InvalidFilterCriteriaException.cs @@ -6,6 +6,8 @@ using System.Runtime.Serialization; namespace System.Reflection { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class InvalidFilterCriteriaException : ApplicationException { public InvalidFilterCriteriaException() @@ -21,13 +23,12 @@ namespace System.Reflection public InvalidFilterCriteriaException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_INVALIDFILTERCRITERIA; + HResult = HResults.COR_E_INVALIDFILTERCRITERIA; } protected InvalidFilterCriteriaException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/MethodInfo.Internal.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/MethodInfo.Internal.cs new file mode 100644 index 0000000000..2806be639e --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/MethodInfo.Internal.cs @@ -0,0 +1,16 @@ +// 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. + +namespace System.Reflection +{ + public abstract partial class MethodInfo : MethodBase + { +#if CORERT + public // Needs to be public so that Reflection.Core can see it. +#else + internal +#endif + virtual int GenericParameterCount => GetGenericArguments().Length; + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/MethodInfo.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/MethodInfo.cs index 3f60149e87..95c62ba7f0 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/MethodInfo.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/MethodInfo.cs @@ -4,7 +4,7 @@ namespace System.Reflection { - public abstract class MethodInfo : MethodBase + public abstract partial class MethodInfo : MethodBase { protected MethodInfo() { } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/Module.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/Module.cs index 7822e9ff10..56f83c40d9 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/Module.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/Module.cs @@ -110,10 +110,7 @@ namespace System.Reflection public Type ResolveType(int metadataToken) => ResolveType(metadataToken, null, null); public virtual Type ResolveType(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) { throw NotImplemented.ByDesign; } - public virtual void GetObjectData(SerializationInfo info, StreamingContext context) - { - throw new PlatformNotSupportedException(); - } + public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { throw NotImplemented.ByDesign; } public override bool Equals(object o) => base.Equals(o); public override int GetHashCode() => base.GetHashCode(); diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/ParameterInfo.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/ParameterInfo.cs index fd130e569b..94bfffaa53 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/ParameterInfo.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/ParameterInfo.cs @@ -54,7 +54,46 @@ namespace System.Reflection public object GetRealObject(StreamingContext context) { - throw new PlatformNotSupportedException(); + // Once all the serializable fields have come in we can set up the real + // instance based on just two of them (MemberImpl and PositionImpl). + + if (MemberImpl == null) + throw new SerializationException(SR.Serialization_InsufficientState); + + ParameterInfo[] args = null; + + switch (MemberImpl.MemberType) + { + case MemberTypes.Constructor: + case MemberTypes.Method: + if (PositionImpl == -1) + { + if (MemberImpl.MemberType == MemberTypes.Method) + return ((MethodInfo)MemberImpl).ReturnParameter; + else + throw new SerializationException(SR.Serialization_BadParameterInfo); + } + else + { + args = ((MethodBase)MemberImpl).GetParametersNoCopy(); + + if (args != null && PositionImpl < args.Length) + return args[PositionImpl]; + else + throw new SerializationException(SR.Serialization_BadParameterInfo); + } + + case MemberTypes.Property: + args = ((PropertyInfo)MemberImpl).GetIndexParameters(); + + if (args != null && PositionImpl > -1 && PositionImpl < args.Length) + return args[PositionImpl]; + else + throw new SerializationException(SR.Serialization_BadParameterInfo); + + default: + throw new SerializationException(SR.Serialization_NoParameterInfo); + } } public override string ToString() => ParameterType.FormatTypeName() + " " + Name; diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/ParameterModifier.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/ParameterModifier.cs index 640fee284a..0fb75ffbd8 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/ParameterModifier.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/ParameterModifier.cs @@ -4,7 +4,7 @@ namespace System.Reflection { - public struct ParameterModifier + public readonly struct ParameterModifier { private readonly bool[] _byRef; diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/Pointer.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/Pointer.cs index 55376c66c0..e1a9990cf0 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/Pointer.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/Pointer.cs @@ -46,6 +46,6 @@ namespace System.Reflection } internal Type GetPointerType() => _ptrType; - internal object GetPointerValue() => (IntPtr)_ptr; + internal IntPtr GetPointerValue() => (IntPtr)_ptr; } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/ReflectionTypeLoadException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/ReflectionTypeLoadException.cs index ca0c6ab0db..d78c068958 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/ReflectionTypeLoadException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/ReflectionTypeLoadException.cs @@ -3,9 +3,12 @@ // See the LICENSE file in the project root for more information. using System.Runtime.Serialization; +using System.Text; namespace System.Reflection { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class ReflectionTypeLoadException : SystemException, ISerializable { public ReflectionTypeLoadException(Type[] classes, Exception[] exceptions) @@ -13,7 +16,7 @@ namespace System.Reflection { Types = classes; LoaderExceptions = exceptions; - HResult = __HResults.COR_E_REFLECTIONTYPELOAD; + HResult = HResults.COR_E_REFLECTIONTYPELOAD; } public ReflectionTypeLoadException(Type[] classes, Exception[] exceptions, string message) @@ -21,12 +24,60 @@ namespace System.Reflection { Types = classes; LoaderExceptions = exceptions; - HResult = __HResults.COR_E_REFLECTIONTYPELOAD; + HResult = HResults.COR_E_REFLECTIONTYPELOAD; + } + + private ReflectionTypeLoadException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + LoaderExceptions = (Exception[])(info.GetValue("Exceptions", typeof(Exception[]))); } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + info.AddValue("Types", null, typeof(Type[])); + info.AddValue("Exceptions", LoaderExceptions, typeof(Exception[])); + } + + public override string Message + { + get + { + if (LoaderExceptions == null || LoaderExceptions.Length == 0) + { + return base.Message; + } + + StringBuilder text = new StringBuilder(); + text.AppendLine(base.Message); + foreach (Exception e in LoaderExceptions) + { + if (e != null) + { + text.AppendLine(e.Message); + } + } + return text.ToString(); + } + } + + public override string ToString() + { + StringBuilder text = new StringBuilder(); + text.AppendLine(base.ToString()); + if (LoaderExceptions != null) + { + foreach (Exception e in LoaderExceptions) + { + if (e != null) + { + text.AppendLine(e.ToString()); + } + } + } + + return text.ToString(); } public Type[] Types { get; } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureArrayType.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureArrayType.cs new file mode 100644 index 0000000000..52011b8a9d --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureArrayType.cs @@ -0,0 +1,46 @@ +// 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.Diagnostics; + +namespace System.Reflection +{ + internal sealed class SignatureArrayType : SignatureHasElementType + { + internal SignatureArrayType(SignatureType elementType, int rank, bool isMultiDim) + : base(elementType) + { + Debug.Assert(rank > 0); + Debug.Assert(rank == 1 || isMultiDim); + + _rank = rank; + _isMultiDim = isMultiDim; + } + + protected sealed override bool IsArrayImpl() => true; + protected sealed override bool IsByRefImpl() => false; + protected sealed override bool IsPointerImpl() => false; + + public sealed override bool IsSZArray => !_isMultiDim; + public sealed override bool IsVariableBoundArray => _isMultiDim; + + public sealed override int GetArrayRank() => _rank; + + protected sealed override string Suffix + { + get + { + if (!_isMultiDim) + return "[]"; + else if (_rank == 1) + return "[*]"; + else + return "[" + new string(',', _rank - 1) + "]"; + } + } + + private readonly int _rank; + private readonly bool _isMultiDim; + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureByRefType.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureByRefType.cs new file mode 100644 index 0000000000..eb5f6de42e --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureByRefType.cs @@ -0,0 +1,27 @@ +// 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; + +namespace System.Reflection +{ + internal sealed class SignatureByRefType : SignatureHasElementType + { + internal SignatureByRefType(SignatureType elementType) + : base(elementType) + { + } + + protected sealed override bool IsArrayImpl() => false; + protected sealed override bool IsByRefImpl() => true; + protected sealed override bool IsPointerImpl() => false; + + public sealed override bool IsSZArray => false; + public sealed override bool IsVariableBoundArray => false; + + public sealed override int GetArrayRank() => throw new ArgumentException(SR.Argument_HasToBeArrayClass); + + protected sealed override string Suffix => "&"; + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureConstructedGenericType.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureConstructedGenericType.cs new file mode 100644 index 0000000000..cd97ffa21b --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureConstructedGenericType.cs @@ -0,0 +1,74 @@ +// 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.Text; +using System.Diagnostics; + +namespace System.Reflection +{ + internal sealed class SignatureConstructedGenericType : SignatureType + { + internal SignatureConstructedGenericType(Type genericTypeDefinition, Type[] genericTypeArguments) + { + Debug.Assert(genericTypeDefinition != null && genericTypeArguments != null); + _genericTypeDefinition = genericTypeDefinition; + _genericTypeArguments = (Type[])(genericTypeArguments.Clone()); + } + + public sealed override bool IsTypeDefinition => false; + public sealed override bool IsGenericTypeDefinition => false; + protected sealed override bool HasElementTypeImpl() => false; + protected sealed override bool IsArrayImpl() => false; + protected sealed override bool IsByRefImpl() => false; + public sealed override bool IsByRefLike => _genericTypeDefinition.IsByRefLike; + protected sealed override bool IsPointerImpl() => false; + public sealed override bool IsSZArray => false; + public sealed override bool IsVariableBoundArray => false; + public sealed override bool IsConstructedGenericType => true; + public sealed override bool IsGenericParameter => false; + public sealed override bool IsGenericTypeParameter => false; + public sealed override bool IsGenericMethodParameter => false; + public sealed override bool ContainsGenericParameters + { + get + { + for (int i = 0; i < _genericTypeArguments.Length; i++) + { + if (_genericTypeArguments[i].ContainsGenericParameters) + return true; + } + return false; + } + } + + internal sealed override SignatureType ElementType => null; + public sealed override int GetArrayRank() => throw new ArgumentException(SR.Argument_HasToBeArrayClass); + public sealed override Type GetGenericTypeDefinition() => _genericTypeDefinition; + public sealed override Type[] GetGenericArguments() => GenericTypeArguments; + public sealed override Type[] GenericTypeArguments => (Type[])(_genericTypeArguments.Clone()); + public sealed override int GenericParameterPosition => throw new InvalidOperationException(SR.Arg_NotGenericParameter); + + public sealed override string Name => _genericTypeDefinition.Name; + public sealed override string Namespace => _genericTypeDefinition.Namespace; + + public sealed override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append(_genericTypeDefinition.ToString()); + sb.Append('['); + for (int i = 0; i < _genericTypeArguments.Length; i++) + { + if (i != 0) + sb.Append(','); + sb.Append(_genericTypeArguments[i].ToString()); + } + sb.Append(']'); + return sb.ToString(); + } + + private readonly Type _genericTypeDefinition; + private readonly Type[] _genericTypeArguments; + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureGenericMethodParameterType.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureGenericMethodParameterType.cs new file mode 100644 index 0000000000..d0790283fb --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureGenericMethodParameterType.cs @@ -0,0 +1,19 @@ +// 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. + +namespace System.Reflection +{ + internal sealed class SignatureGenericMethodParameterType : SignatureGenericParameterType + { + internal SignatureGenericMethodParameterType(int position) + : base(position) + { + } + + public sealed override bool IsGenericTypeParameter => false; + public sealed override bool IsGenericMethodParameter => true; + + public sealed override string Name => "!!" + GenericParameterPosition; + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureGenericParameterType.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureGenericParameterType.cs new file mode 100644 index 0000000000..fee7bce353 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureGenericParameterType.cs @@ -0,0 +1,46 @@ +// 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.Diagnostics; + +namespace System.Reflection +{ + internal abstract class SignatureGenericParameterType : SignatureType + { + protected SignatureGenericParameterType(int position) + { + Debug.Assert(position >= 0); + _position = position; + } + + public sealed override bool IsTypeDefinition => false; + public sealed override bool IsGenericTypeDefinition => false; + protected sealed override bool HasElementTypeImpl() => false; + protected sealed override bool IsArrayImpl() => false; + protected sealed override bool IsByRefImpl() => false; + public sealed override bool IsByRefLike => false; + protected sealed override bool IsPointerImpl() => false; + public sealed override bool IsSZArray => false; + public sealed override bool IsVariableBoundArray => false; + public sealed override bool IsConstructedGenericType => false; + public sealed override bool IsGenericParameter => true; + public abstract override bool IsGenericMethodParameter { get; } + public sealed override bool ContainsGenericParameters => true; + + internal sealed override SignatureType ElementType => null; + public sealed override int GetArrayRank() => throw new ArgumentException(SR.Argument_HasToBeArrayClass); + public sealed override Type GetGenericTypeDefinition() => throw new InvalidOperationException(SR.InvalidOperation_NotGenericType); + public sealed override Type[] GetGenericArguments() => Array.Empty(); + public sealed override Type[] GenericTypeArguments => Array.Empty(); + public sealed override int GenericParameterPosition => _position; + + public abstract override string Name { get; } + public sealed override string Namespace => null; + + public sealed override string ToString() => Name; + + private readonly int _position; + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureHasElementType.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureHasElementType.cs new file mode 100644 index 0000000000..e74e5f5aa2 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureHasElementType.cs @@ -0,0 +1,49 @@ +// 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.Diagnostics; + +namespace System.Reflection +{ + internal abstract class SignatureHasElementType : SignatureType + { + protected SignatureHasElementType(SignatureType elementType) + { + Debug.Assert(elementType != null); + _elementType = elementType; + } + + public sealed override bool IsTypeDefinition => false; + public sealed override bool IsGenericTypeDefinition => false; + protected sealed override bool HasElementTypeImpl() => true; + protected abstract override bool IsArrayImpl(); + protected abstract override bool IsByRefImpl(); + public sealed override bool IsByRefLike => false; + protected abstract override bool IsPointerImpl(); + public abstract override bool IsSZArray { get; } + public abstract override bool IsVariableBoundArray { get; } + public sealed override bool IsConstructedGenericType => false; + public sealed override bool IsGenericParameter => false; + public sealed override bool IsGenericTypeParameter => false; + public sealed override bool IsGenericMethodParameter => false; + public sealed override bool ContainsGenericParameters => _elementType.ContainsGenericParameters; + + internal sealed override SignatureType ElementType => _elementType; + public abstract override int GetArrayRank(); + public sealed override Type GetGenericTypeDefinition() => throw new InvalidOperationException(SR.InvalidOperation_NotGenericType); + public sealed override Type[] GetGenericArguments() => Array.Empty(); + public sealed override Type[] GenericTypeArguments => Array.Empty(); + public sealed override int GenericParameterPosition => throw new InvalidOperationException(SR.Arg_NotGenericParameter); + + public sealed override string Name => _elementType.Name + Suffix; + public sealed override string Namespace => _elementType.Namespace; + + public sealed override string ToString() => _elementType.ToString() + Suffix; + + protected abstract string Suffix { get; } + + private readonly SignatureType _elementType; + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignaturePointerType.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignaturePointerType.cs new file mode 100644 index 0000000000..a75a208165 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignaturePointerType.cs @@ -0,0 +1,27 @@ +// 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; + +namespace System.Reflection +{ + internal sealed class SignaturePointerType : SignatureHasElementType + { + internal SignaturePointerType(SignatureType elementType) + : base(elementType) + { + } + + protected sealed override bool IsArrayImpl() => false; + protected sealed override bool IsByRefImpl() => false; + protected sealed override bool IsPointerImpl() => true; + + public sealed override bool IsSZArray => false; + public sealed override bool IsVariableBoundArray => false; + + public sealed override int GetArrayRank() => throw new ArgumentException(SR.Argument_HasToBeArrayClass); + + protected sealed override string Suffix => "*"; + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureType.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureType.cs new file mode 100644 index 0000000000..40a0590448 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureType.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.Globalization; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace System.Reflection +{ + // + // Signature Types are highly restricted Type objects that can be passed in the Type[] parameter to Type.GetMethod(). Their primary + // use is to pass types representing generic parameters defined by the method, or types composed from them. Passing in the normal + // generic parameter Type obtained from MethodInfo.GetGenericArguments() is usually impractical (if you had the method, you wouldn't + // be looking for it!) + // + internal abstract class SignatureType : Type + { + public sealed override bool IsSignatureType => true; + + // Type flavor predicates + public abstract override bool IsTypeDefinition { get; } + protected abstract override bool HasElementTypeImpl(); + protected abstract override bool IsArrayImpl(); + public abstract override bool IsSZArray { get; } + public abstract override bool IsVariableBoundArray { get; } + protected abstract override bool IsByRefImpl(); + public abstract override bool IsByRefLike { get; } + protected abstract override bool IsPointerImpl(); + public sealed override bool IsGenericType => IsGenericTypeDefinition || IsConstructedGenericType; + public abstract override bool IsGenericTypeDefinition { get; } + public abstract override bool IsConstructedGenericType { get; } + public abstract override bool IsGenericParameter { get; } + public abstract override bool IsGenericTypeParameter { get; } + public abstract override bool IsGenericMethodParameter { get; } + public abstract override bool ContainsGenericParameters { get; } + public sealed override MemberTypes MemberType => MemberTypes.TypeInfo; + + // Compositors + public sealed override Type MakeArrayType() => new SignatureArrayType(this, rank: 1, isMultiDim: false); + public sealed override Type MakeArrayType(int rank) + { + if (rank <= 0) + throw new IndexOutOfRangeException(); + return new SignatureArrayType(this, rank: rank, isMultiDim: true); + } + public sealed override Type MakeByRefType() => new SignatureByRefType(this); + public sealed override Type MakePointerType() => new SignaturePointerType(this); + public sealed override Type MakeGenericType(params Type[] typeArguments) => throw new NotSupportedException(SR.NotSupported_SignatureType); // There is no SignatureType for type definition types so it would never be legal to call this. + + // Dissectors + public sealed override Type GetElementType() => ElementType; + public abstract override int GetArrayRank(); + public abstract override Type GetGenericTypeDefinition(); + public abstract override Type[] GenericTypeArguments { get; } + public abstract override Type[] GetGenericArguments(); + public abstract override int GenericParameterPosition { get; } + internal abstract SignatureType ElementType { get; } + + // Identity +#if DEBUG + public sealed override bool Equals(object o) => base.Equals(o); + public sealed override bool Equals(Type o) => base.Equals(o); + public sealed override int GetHashCode() => base.GetHashCode(); +#endif + public sealed override Type UnderlyingSystemType => this; // Equals(Type) depends on this. + + // Naming and diagnostics + public abstract override string Name { get; } + public abstract override string Namespace { get; } + public sealed override string FullName => null; + public sealed override string AssemblyQualifiedName => null; + public abstract override string ToString(); + + // Not supported on Signature Types + public sealed override Assembly Assembly => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Module Module => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type ReflectedType => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type BaseType => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type[] GetInterfaces() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsAssignableFrom(Type c) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override int MetadataToken => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type DeclaringType => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override MethodBase DeclaringMethod => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type[] GetGenericParameterConstraints() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override GenericParameterAttributes GenericParameterAttributes => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsEnumDefined(object value) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override string GetEnumName(object value) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override string[] GetEnumNames() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type GetEnumUnderlyingType() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Array GetEnumValues() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Guid GUID => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override TypeCode GetTypeCodeImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override TypeAttributes GetAttributeFlagsImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override EventInfo GetEvent(string name, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override EventInfo[] GetEvents(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override FieldInfo GetField(string name, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override FieldInfo[] GetFields(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override MemberInfo[] GetMembers(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override MethodInfo[] GetMethods(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type GetNestedType(string name, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type[] GetNestedTypes(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override PropertyInfo[] GetProperties(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override MethodInfo GetMethodImpl(string name, int genericParameterCount, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override MemberInfo[] FindMembers(MemberTypes memberType, BindingFlags bindingAttr, MemberFilter filter, object filterCriteria) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override MemberInfo[] GetMember(string name, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override MemberInfo[] GetMember(string name, MemberTypes type, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override MemberInfo[] GetDefaultMembers() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override EventInfo[] GetEvents() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override object[] GetCustomAttributes(bool inherit) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override object[] GetCustomAttributes(Type attributeType, bool inherit) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsDefined(Type attributeType, bool inherit) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override IList GetCustomAttributesData() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type GetInterface(string name, bool ignoreCase) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override bool IsCOMObjectImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override bool IsPrimitiveImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override IEnumerable CustomAttributes => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type[] FindInterfaces(TypeFilter filter, object filterCriteria) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override InterfaceMapping GetInterfaceMap(Type interfaceType) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override bool IsContextfulImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsEnum => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsEquivalentTo(Type other) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsInstanceOfType(object o) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override bool IsMarshalByRefImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsSecurityCritical => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsSecuritySafeCritical => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsSecurityTransparent => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsSerializable => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsSubclassOf(Type c) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override bool IsValueTypeImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override StructLayoutAttribute StructLayoutAttribute => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override RuntimeTypeHandle TypeHandle => throw new NotSupportedException(SR.NotSupported_SignatureType); + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureTypeExtensions.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureTypeExtensions.cs new file mode 100644 index 0000000000..5847944f14 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/SignatureTypeExtensions.cs @@ -0,0 +1,227 @@ +// 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.Reflection; +using System.Diagnostics; + +namespace System.Reflection +{ +#if CORERT + public // Needs to be public so that Reflection.Core can see it. +#else + internal +#endif + static class SignatureTypeExtensions + { + /// + /// This is semantically identical to + /// + /// parameter.ParameterType == pattern.TryResolveAgainstGenericMethod(parameter.Member) + /// + /// but without the allocation overhead of TryResolve. + /// + public static bool MatchesParameterTypeExactly(this Type pattern, ParameterInfo parameter) + { + if (pattern is SignatureType signatureType) + return signatureType.MatchesExactly(parameter.ParameterType); + else + return pattern == (object)(parameter.ParameterType); + } + + /// + /// This is semantically identical to + /// + /// actual == pattern.TryResolveAgainstGenericMethod(parameterMember) + /// + /// but without the allocation overhead of TryResolve. + /// + internal static bool MatchesExactly(this SignatureType pattern, Type actual) + { + if (pattern.IsSZArray) + { + return actual.IsSZArray && pattern.ElementType.MatchesExactly(actual.GetElementType()); + } + else if (pattern.IsVariableBoundArray) + { + return actual.IsVariableBoundArray && pattern.GetArrayRank() == actual.GetArrayRank() && pattern.ElementType.MatchesExactly(actual.GetElementType()); + } + else if (pattern.IsByRef) + { + return actual.IsByRef && pattern.ElementType.MatchesExactly(actual.GetElementType()); + } + else if (pattern.IsPointer) + { + return actual.IsPointer && pattern.ElementType.MatchesExactly(actual.GetElementType()); + } + else if (pattern.IsConstructedGenericType) + { + if (!actual.IsConstructedGenericType) + return false; + if (!(pattern.GetGenericTypeDefinition() == actual.GetGenericTypeDefinition())) + return false; + Type[] patternGenericTypeArguments = pattern.GenericTypeArguments; + Type[] actualGenericTypeArguments = actual.GenericTypeArguments; + int count = patternGenericTypeArguments.Length; + if (count != actualGenericTypeArguments.Length) + return false; + for (int i = 0; i < count; i++) + { + Type patternGenericTypeArgument = patternGenericTypeArguments[i]; + if (patternGenericTypeArgument is SignatureType signatureType) + { + if (!signatureType.MatchesExactly(actualGenericTypeArguments[i])) + return false; + } + else + { + if (patternGenericTypeArgument != actualGenericTypeArguments[i]) + return false; + } + } + return true; + } + else if (pattern.IsGenericMethodParameter) + { + if (!actual.IsGenericMethodParameter) + return false; + if (pattern.GenericParameterPosition != actual.GenericParameterPosition) + return false; + return true; + } + else + { + return false; + } + } + + /// + /// Translates a SignatureType into its equivalent resolved Type by recursively substituting all generic parameter references + /// with its corresponding generic parameter definition. This is slow so MatchesExactly or MatchesParameterTypeExactly should be + /// substituted instead whenever possible. This is only used by the DefaultBinder when its fast-path checks have been exhausted and + /// it needs to call non-trivial methods like IsAssignableFrom which SignatureTypes will never support. + /// + /// Because this method is used to eliminate method candidates in a GetMethod() lookup, it is entirely possible that the Type + /// might not be creatable due to conflicting generic constraints. Since this merely implies that this candidate is not + /// the method we're looking for, we return null rather than let the TypeLoadException bubble up. The DefaultBinder will catch + /// the null and continue its search for a better candidate. + /// + internal static Type TryResolveAgainstGenericMethod(this SignatureType signatureType, MethodInfo genericMethod) + { + return signatureType.TryResolve(genericMethod.GetGenericArguments()); + } + + private static Type TryResolve(this SignatureType signatureType, Type[] genericMethodParameters) + { + if (signatureType.IsSZArray) + { + return signatureType.ElementType.TryResolve(genericMethodParameters)?.TryMakeArrayType(); + } + else if (signatureType.IsVariableBoundArray) + { + return signatureType.ElementType.TryResolve(genericMethodParameters)?.TryMakeArrayType(signatureType.GetArrayRank()); + } + else if (signatureType.IsByRef) + { + return signatureType.ElementType.TryResolve(genericMethodParameters)?.TryMakeByRefType(); + } + else if (signatureType.IsPointer) + { + return signatureType.ElementType.TryResolve(genericMethodParameters)?.TryMakePointerType(); + } + else if (signatureType.IsConstructedGenericType) + { + Type[] genericTypeArguments = signatureType.GenericTypeArguments; + int count = genericTypeArguments.Length; + Type[] newGenericTypeArguments = new Type[count]; + for (int i = 0; i < count; i++) + { + Type genericTypeArgument = genericTypeArguments[i]; + if (genericTypeArgument is SignatureType signatureGenericTypeArgument) + { + newGenericTypeArguments[i] = signatureGenericTypeArgument.TryResolve(genericMethodParameters); + if (newGenericTypeArguments[i] == null) + return null; + } + else + { + newGenericTypeArguments[i] = genericTypeArgument; + } + } + return signatureType.GetGenericTypeDefinition().TryMakeGenericType(newGenericTypeArguments); + } + else if (signatureType.IsGenericMethodParameter) + { + int position = signatureType.GenericParameterPosition; + if (position >= genericMethodParameters.Length) + return null; + return genericMethodParameters[position]; + } + else + { + return null; + } + } + + private static Type TryMakeArrayType(this Type type) + { + try + { + return type.MakeArrayType(); + } + catch + { + return null; + } + } + + private static Type TryMakeArrayType(this Type type, int rank) + { + try + { + return type.MakeArrayType(rank); + } + catch + { + return null; + } + } + + private static Type TryMakeByRefType(this Type type) + { + try + { + return type.MakeByRefType(); + } + catch + { + return null; + } + } + + private static Type TryMakePointerType(this Type type) + { + try + { + return type.MakePointerType(); + } + catch + { + return null; + } + } + + private static Type TryMakeGenericType(this Type type, Type[] instantiation) + { + try + { + return type.MakeGenericType(instantiation); + } + catch + { + return null; + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/TargetException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/TargetException.cs index 6e43f56fa4..c200000738 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/TargetException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/TargetException.cs @@ -6,6 +6,8 @@ using System.Runtime.Serialization; namespace System.Reflection { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class TargetException : ApplicationException { public TargetException() @@ -21,13 +23,12 @@ namespace System.Reflection public TargetException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_TARGET; + HResult = HResults.COR_E_TARGET; } protected TargetException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/TargetInvocationException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/TargetInvocationException.cs index 8d0bfef40d..822ddfdfb2 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/TargetInvocationException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/TargetInvocationException.cs @@ -6,18 +6,25 @@ using System.Runtime.Serialization; namespace System.Reflection { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class TargetInvocationException : ApplicationException { public TargetInvocationException(Exception inner) : base(SR.Arg_TargetInvocationException, inner) { - HResult = __HResults.COR_E_TARGETINVOCATION; + HResult = HResults.COR_E_TARGETINVOCATION; } public TargetInvocationException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_TARGETINVOCATION; + HResult = HResults.COR_E_TARGETINVOCATION; + } + + internal TargetInvocationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/TargetParameterCountException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/TargetParameterCountException.cs index e200cdb94f..c68c467290 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/TargetParameterCountException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/TargetParameterCountException.cs @@ -6,24 +6,31 @@ using System.Runtime.Serialization; namespace System.Reflection { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class TargetParameterCountException : ApplicationException { public TargetParameterCountException() : base(SR.Arg_TargetParameterCountException) { - HResult = __HResults.COR_E_TARGETPARAMCOUNT; + HResult = HResults.COR_E_TARGETPARAMCOUNT; } public TargetParameterCountException(string message) : base(message) { - HResult = __HResults.COR_E_TARGETPARAMCOUNT; + HResult = HResults.COR_E_TARGETPARAMCOUNT; } public TargetParameterCountException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_TARGETPARAMCOUNT; + HResult = HResults.COR_E_TARGETPARAMCOUNT; + } + + internal TargetParameterCountException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/TypeDelegator.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/TypeDelegator.cs index bcbff05d62..e0be6e87b6 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/TypeDelegator.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/TypeDelegator.cs @@ -105,10 +105,16 @@ namespace System.Reflection protected override bool IsArrayImpl() => typeImpl.IsArray; protected override bool IsPrimitiveImpl() => typeImpl.IsPrimitive; protected override bool IsByRefImpl() => typeImpl.IsByRef; + public override bool IsGenericTypeParameter => typeImpl.IsGenericTypeParameter; + public override bool IsGenericMethodParameter => typeImpl.IsGenericMethodParameter; protected override bool IsPointerImpl() => typeImpl.IsPointer; protected override bool IsValueTypeImpl() => typeImpl.IsValueType; protected override bool IsCOMObjectImpl() => typeImpl.IsCOMObject; + public override bool IsByRefLike => typeImpl.IsByRefLike; public override bool IsConstructedGenericType => typeImpl.IsConstructedGenericType; + + public override bool IsCollectible => typeImpl.IsCollectible; + public override Type GetElementType() => typeImpl.GetElementType(); protected override bool HasElementTypeImpl() => typeImpl.HasElementType; diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Resources/MissingManifestResourceException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Resources/MissingManifestResourceException.cs index ec814393d0..20914ac7e1 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Resources/MissingManifestResourceException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Resources/MissingManifestResourceException.cs @@ -7,30 +7,31 @@ using System.Runtime.Serialization; namespace System.Resources { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class MissingManifestResourceException : SystemException { public MissingManifestResourceException() : base(SR.Arg_MissingManifestResourceException) { - HResult = System.__HResults.COR_E_MISSINGMANIFESTRESOURCE; + HResult = System.HResults.COR_E_MISSINGMANIFESTRESOURCE; } public MissingManifestResourceException(string message) : base(message) { - HResult = System.__HResults.COR_E_MISSINGMANIFESTRESOURCE; + HResult = System.HResults.COR_E_MISSINGMANIFESTRESOURCE; } public MissingManifestResourceException(string message, Exception inner) : base(message, inner) { - HResult = System.__HResults.COR_E_MISSINGMANIFESTRESOURCE; + HResult = System.HResults.COR_E_MISSINGMANIFESTRESOURCE; } protected MissingManifestResourceException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Resources/MissingSatelliteAssemblyException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Resources/MissingSatelliteAssemblyException.cs index d2ddc992ac..af547b21f1 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Resources/MissingSatelliteAssemblyException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Resources/MissingSatelliteAssemblyException.cs @@ -20,6 +20,8 @@ using System.Runtime.Serialization; namespace System.Resources { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class MissingSatelliteAssemblyException : SystemException { private String _cultureName; @@ -27,32 +29,31 @@ namespace System.Resources public MissingSatelliteAssemblyException() : base(SR.MissingSatelliteAssembly_Default) { - HResult = System.__HResults.COR_E_MISSINGSATELLITEASSEMBLY; + HResult = System.HResults.COR_E_MISSINGSATELLITEASSEMBLY; } public MissingSatelliteAssemblyException(string message) : base(message) { - HResult = System.__HResults.COR_E_MISSINGSATELLITEASSEMBLY; + HResult = System.HResults.COR_E_MISSINGSATELLITEASSEMBLY; } public MissingSatelliteAssemblyException(string message, String cultureName) : base(message) { - HResult = System.__HResults.COR_E_MISSINGSATELLITEASSEMBLY; + HResult = System.HResults.COR_E_MISSINGSATELLITEASSEMBLY; _cultureName = cultureName; } public MissingSatelliteAssemblyException(string message, Exception inner) : base(message, inner) { - HResult = System.__HResults.COR_E_MISSINGSATELLITEASSEMBLY; + HResult = System.HResults.COR_E_MISSINGSATELLITEASSEMBLY; } protected MissingSatelliteAssemblyException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public String CultureName diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Resources/RuntimeResourceSet.cs b/external/corert/src/System.Private.CoreLib/shared/System/Resources/RuntimeResourceSet.cs index 41d7541c24..a63e68c19d 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Resources/RuntimeResourceSet.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Resources/RuntimeResourceSet.cs @@ -21,7 +21,6 @@ using System.Globalization; using System.Reflection; using System.Runtime.Versioning; using System.Diagnostics; -using System.Diagnostics.Contracts; namespace System.Resources { @@ -161,10 +160,10 @@ namespace System.Resources // into smaller chunks, each of size sqrt(n), would be substantially better for // resource files containing thousands of resources. // -#if CORECLR - internal -#else +#if CORERT public // On CoreRT, this must be public because of need to whitelist past the ReflectionBlock. +#else + internal #endif sealed class RuntimeResourceSet : ResourceSet, IEnumerable { @@ -285,7 +284,6 @@ namespace System.Resources throw new ArgumentNullException(nameof(key)); if (Reader == null || _resCache == null) throw new ObjectDisposedException(null, SR.ObjectDisposed_ResourceSet); - Contract.EndContractBlock(); Object value = null; ResourceLocator resLocation; @@ -433,4 +431,4 @@ namespace System.Resources return value; } } -} \ No newline at end of file +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/AsyncMethodBuilderAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/AsyncMethodBuilderAttribute.cs new file mode 100644 index 0000000000..688a3a01ba --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/AsyncMethodBuilderAttribute.cs @@ -0,0 +1,21 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + /// + /// Indicates the type of the async method builder that should be used by a language compiler to + /// build the attributed type when used as the return type of an async method. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Delegate | AttributeTargets.Enum, Inherited = false, AllowMultiple = false)] + public sealed class AsyncMethodBuilderAttribute : Attribute + { + /// Initializes the . + /// The of the associated builder. + public AsyncMethodBuilderAttribute(Type builderType) => BuilderType = builderType; + + /// Gets the of the associated builder. + public Type BuilderType { get; } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs new file mode 100644 index 0000000000..b5ecd7924c --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs @@ -0,0 +1,111 @@ +// 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.Runtime.InteropServices; +using System.Security; +using System.Threading.Tasks; + +namespace System.Runtime.CompilerServices +{ + /// Represents a builder for asynchronous methods that returns a . + /// The type of the result. + [StructLayout(LayoutKind.Auto)] + public struct AsyncValueTaskMethodBuilder + { + /// The to which most operations are delegated. + private AsyncTaskMethodBuilder _methodBuilder; // mutable struct; do not make it readonly + /// The result for this builder, if it's completed before any awaits occur. + private TResult _result; + /// true if contains the synchronous result for the async method; otherwise, false. + private bool _haveResult; + /// true if the builder should be used for setting/getting the result; otherwise, false. + private bool _useBuilder; + + /// Creates an instance of the struct. + /// The initialized instance. + public static AsyncValueTaskMethodBuilder Create() => +#if CORERT + // corert's AsyncTaskMethodBuilder.Create() currently does additional debugger-related + // work, so we need to delegate to it. + new AsyncValueTaskMethodBuilder() { _methodBuilder = AsyncTaskMethodBuilder.Create() }; +#else + // _methodBuilder should be initialized to AsyncTaskMethodBuilder.Create(), but on coreclr + // that Create() is a nop, so we can just return the default here. + default(AsyncValueTaskMethodBuilder); +#endif + + /// Begins running the builder with the associated state machine. + /// The type of the state machine. + /// The state machine instance, passed by reference. + public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine => + _methodBuilder.Start(ref stateMachine); // will provide the right ExecutionContext semantics + + /// Associates the builder with the specified state machine. + /// The state machine instance to associate with the builder. + public void SetStateMachine(IAsyncStateMachine stateMachine) => _methodBuilder.SetStateMachine(stateMachine); + + /// Marks the task as successfully completed. + /// The result to use to complete the task. + public void SetResult(TResult result) + { + if (_useBuilder) + { + _methodBuilder.SetResult(result); + } + else + { + _result = result; + _haveResult = true; + } + } + + /// Marks the task as failed and binds the specified exception to the task. + /// The exception to bind to the task. + public void SetException(Exception exception) => _methodBuilder.SetException(exception); + + /// Gets the task for this builder. + public ValueTask Task + { + get + { + if (_haveResult) + { + return new ValueTask(_result); + } + else + { + _useBuilder = true; + return new ValueTask(_methodBuilder.Task); + } + } + } + + /// Schedules the state machine to proceed to the next action when the specified awaiter completes. + /// The type of the awaiter. + /// The type of the state machine. + /// the awaiter + /// The state machine. + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine + { + _useBuilder = true; + _methodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine); + } + + /// Schedules the state machine to proceed to the next action when the specified awaiter completes. + /// The type of the awaiter. + /// The type of the state machine. + /// the awaiter + /// The state machine. + [SecuritySafeCritical] + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine + { + _useBuilder = true; + _methodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs new file mode 100644 index 0000000000..4e8ce691be --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs @@ -0,0 +1,88 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace System.Runtime.CompilerServices +{ + /// Provides an awaitable type that enables configured awaits on a . + /// The type of the result produced. + [StructLayout(LayoutKind.Auto)] + public readonly struct ConfiguredValueTaskAwaitable + { + /// The wrapped . + private readonly ValueTask _value; + /// true to attempt to marshal the continuation back to the original context captured; otherwise, false. + private readonly bool _continueOnCapturedContext; + + /// Initializes the awaitable. + /// The wrapped . + /// + /// true to attempt to marshal the continuation back to the original synchronization context captured; otherwise, false. + /// + internal ConfiguredValueTaskAwaitable(ValueTask value, bool continueOnCapturedContext) + { + _value = value; + _continueOnCapturedContext = continueOnCapturedContext; + } + + /// Returns an awaiter for this instance. + public ConfiguredValueTaskAwaiter GetAwaiter() => + new ConfiguredValueTaskAwaiter(_value, _continueOnCapturedContext); + + /// Provides an awaiter for a . + [StructLayout(LayoutKind.Auto)] + public struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion, IConfiguredValueTaskAwaiter + { + /// The value being awaited. + private ValueTask _value; // Methods are called on this; avoid making it readonly so as to avoid unnecessary copies + /// The value to pass to ConfigureAwait. + internal readonly bool _continueOnCapturedContext; + + /// Initializes the awaiter. + /// The value to be awaited. + /// The value to pass to ConfigureAwait. + internal ConfiguredValueTaskAwaiter(ValueTask value, bool continueOnCapturedContext) + { + _value = value; + _continueOnCapturedContext = continueOnCapturedContext; + } + + /// Gets whether the has completed. + public bool IsCompleted => _value.IsCompleted; + + /// Gets the result of the ValueTask. + [StackTraceHidden] + public TResult GetResult() => + _value._task == null ? + _value._result : + _value._task.GetAwaiter().GetResult(); + + /// Schedules the continuation action for the . + public void OnCompleted(Action continuation) => + _value.AsTask().ConfigureAwait(_continueOnCapturedContext).GetAwaiter().OnCompleted(continuation); + + /// Schedules the continuation action for the . + public void UnsafeOnCompleted(Action continuation) => + _value.AsTask().ConfigureAwait(_continueOnCapturedContext).GetAwaiter().UnsafeOnCompleted(continuation); + + /// Gets the task underlying . + internal Task AsTask() => _value.AsTask(); + + /// Gets the task underlying the incomplete . + /// This method is used when awaiting and IsCompleted returned false; thus we expect the value task to be wrapping a non-null task. + (Task task, bool continueOnCapturedContext) IConfiguredValueTaskAwaiter.GetTask() => (_value.AsTaskExpectNonNull(), _continueOnCapturedContext); + } + } + + /// + /// Internal interface used to enable extract the Task from arbitrary configured ValueTask awaiters. + /// + internal interface IConfiguredValueTaskAwaiter + { + (Task task, bool continueOnCapturedContext) GetTask(); + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/CustomConstantAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/CustomConstantAttribute.cs new file mode 100644 index 0000000000..f75693eb40 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/CustomConstantAttribute.cs @@ -0,0 +1,12 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)] + public abstract class CustomConstantAttribute : Attribute + { + public abstract Object Value { get; } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs new file mode 100644 index 0000000000..813e6803bf --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs @@ -0,0 +1,19 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)] + public sealed class DateTimeConstantAttribute : CustomConstantAttribute + { + private DateTime _date; + + public DateTimeConstantAttribute(long ticks) + { + _date = new DateTime(ticks); + } + + public override Object Value => _date; + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/DecimalConstantAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/DecimalConstantAttribute.cs new file mode 100644 index 0000000000..19db84eb43 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/DecimalConstantAttribute.cs @@ -0,0 +1,39 @@ +// 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. + +// Note: If you add a new ctor overloads you need to update ParameterInfo.RawDefaultValue + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)] + public sealed class DecimalConstantAttribute : Attribute + { + private Decimal _dec; + + [CLSCompliant(false)] + public DecimalConstantAttribute( + byte scale, + byte sign, + uint hi, + uint mid, + uint low + ) + { + _dec = new Decimal((int)low, (int)mid, (int)hi, (sign != 0), scale); + } + + public DecimalConstantAttribute( + byte scale, + byte sign, + int hi, + int mid, + int low + ) + { + _dec = new Decimal(low, mid, hi, (sign != 0), scale); + } + + public Decimal Value => _dec; + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/IntrinsicAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/IntrinsicAttribute.cs new file mode 100644 index 0000000000..381b4c63f7 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/IntrinsicAttribute.cs @@ -0,0 +1,14 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + // Calls to methods or references to fields marked with this attribute may be replaced at + // some call sites with jit intrinsic expansions. + // Types marked with this attribute may be specially treated by the rumtime/compiler. + [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Field, Inherited = false)] + internal sealed class IntrinsicAttribute : Attribute + { + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/MethodImplAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/MethodImplAttribute.cs new file mode 100644 index 0000000000..8e8f93c268 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/MethodImplAttribute.cs @@ -0,0 +1,29 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + // Custom attribute to specify additional method properties. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false)] + sealed public class MethodImplAttribute : Attribute + { + public MethodCodeType MethodCodeType; + + public MethodImplAttribute(MethodImplOptions methodImplOptions) + { + Value = methodImplOptions; + } + + public MethodImplAttribute(short value) + { + Value = (MethodImplOptions)value; + } + + public MethodImplAttribute() + { + } + + public MethodImplOptions Value { get; } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/RuntimeFeature.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/RuntimeFeature.cs index 090acff559..c6a97eca20 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/RuntimeFeature.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/RuntimeFeature.cs @@ -11,17 +11,24 @@ namespace System.Runtime.CompilerServices /// public const string PortablePdb = nameof(PortablePdb); +#if FEATURE_DEFAULT_INTERFACES + /// + /// Indicates that this version of runtime supports default interface method implementations. + /// + public const string DefaultImplementationsOfInterfaces = nameof(DefaultImplementationsOfInterfaces); +#endif + /// /// Checks whether a certain feature is supported by the Runtime. /// public static bool IsSupported(string feature) { - // Features should be added as public const string fields in the same class. - // Example: public const string FeatureName = nameof(FeatureName); - switch (feature) { - case nameof(PortablePdb): + case PortablePdb: +#if FEATURE_DEFAULT_INTERFACES + case DefaultImplementationsOfInterfaces: +#endif return true; } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/RuntimeWrappedException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/RuntimeWrappedException.cs new file mode 100644 index 0000000000..72996c6dfc --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/RuntimeWrappedException.cs @@ -0,0 +1,41 @@ +// 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.Runtime.Serialization; + +namespace System.Runtime.CompilerServices +{ + /// + /// Exception used to wrap all non-CLS compliant exceptions. + /// + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class RuntimeWrappedException : Exception + { + private object _wrappedException; // EE expects this name + + // Not an api but has to be public as System.Linq.Expression invokes this through Reflection when an expression + // throws an object that doesn't derive from Exception. + public RuntimeWrappedException(object thrownObject) + : base(SR.RuntimeWrappedException) + { + HResult = HResults.COR_E_RUNTIMEWRAPPED; + _wrappedException = thrownObject; + } + + private RuntimeWrappedException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + _wrappedException = info.GetValue("WrappedException", typeof(object)); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("WrappedException", _wrappedException, typeof(object)); + } + + public object WrappedException => _wrappedException; + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs new file mode 100644 index 0000000000..7bc8b5cc7d --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs @@ -0,0 +1,53 @@ +// 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.Diagnostics; +using System.Threading.Tasks; + +namespace System.Runtime.CompilerServices +{ + /// Provides an awaiter for a . + public struct ValueTaskAwaiter : ICriticalNotifyCompletion, IValueTaskAwaiter + { + /// The value being awaited. + private ValueTask _value; // Methods are called on this; avoid making it readonly so as to avoid unnecessary copies + + /// Initializes the awaiter. + /// The value to be awaited. + internal ValueTaskAwaiter(ValueTask value) => _value = value; + + /// Gets whether the has completed. + public bool IsCompleted => _value.IsCompleted; + + /// Gets the result of the ValueTask. + [StackTraceHidden] + public TResult GetResult() => + _value._task == null ? + _value._result : + _value._task.GetAwaiter().GetResult(); + + /// Schedules the continuation action for this ValueTask. + public void OnCompleted(Action continuation) => + _value.AsTask().ConfigureAwait(continueOnCapturedContext: true).GetAwaiter().OnCompleted(continuation); + + /// Schedules the continuation action for this ValueTask. + public void UnsafeOnCompleted(Action continuation) => + _value.AsTask().ConfigureAwait(continueOnCapturedContext: true).GetAwaiter().UnsafeOnCompleted(continuation); + + /// Gets the task underlying . + internal Task AsTask() => _value.AsTask(); + + /// Gets the task underlying the incomplete . + /// This method is used when awaiting and IsCompleted returned false; thus we expect the value task to be wrapping a non-null task. + Task IValueTaskAwaiter.GetTask() => _value.AsTaskExpectNonNull(); + } + + /// + /// Internal interface used to enable extract the Task from arbitrary ValueTask awaiters. + /// > + internal interface IValueTaskAwaiter + { + Task GetTask(); + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/ExceptionServices/ExceptionNotification.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/ExceptionServices/ExceptionNotification.cs new file mode 100644 index 0000000000..605588d657 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/ExceptionServices/ExceptionNotification.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.Runtime.ExceptionServices +{ + // Definition of the argument-type passed to the FirstChanceException event handler + public class FirstChanceExceptionEventArgs : EventArgs + { + public FirstChanceExceptionEventArgs(Exception exception) + { + Exception = exception; + } + + // Returns the exception object pertaining to the first chance exception + public Exception Exception { get; } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/ExceptionServices/HandleProcessCorruptedStateExceptionsAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/ExceptionServices/HandleProcessCorruptedStateExceptionsAttribute.cs new file mode 100644 index 0000000000..cc1bc81e5a --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/ExceptionServices/HandleProcessCorruptedStateExceptionsAttribute.cs @@ -0,0 +1,16 @@ +// 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. + +namespace System.Runtime.ExceptionServices +{ + // This attribute can be applied to methods to indicate that ProcessCorruptedState + // Exceptions should be delivered to them. + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public sealed class HandleProcessCorruptedStateExceptionsAttribute : Attribute + { + public HandleProcessCorruptedStateExceptionsAttribute() + { + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/BestFitMappingAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/BestFitMappingAttribute.cs new file mode 100644 index 0000000000..4ebee1538c --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/BestFitMappingAttribute.cs @@ -0,0 +1,19 @@ +// 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. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)] + public sealed class BestFitMappingAttribute : Attribute + { + public BestFitMappingAttribute(bool BestFitMapping) + { + this.BestFitMapping = BestFitMapping; + } + + public bool BestFitMapping { get; } + + public bool ThrowOnUnmappableChar; + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/DefaultCharSetAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/DefaultCharSetAttribute.cs new file mode 100644 index 0000000000..7a486f7017 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/DefaultCharSetAttribute.cs @@ -0,0 +1,17 @@ +// 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. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Module, Inherited = false)] + public sealed class DefaultCharSetAttribute : Attribute + { + public DefaultCharSetAttribute(CharSet charSet) + { + CharSet = charSet; + } + + public CharSet CharSet { get; } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/DefaultDllImportSearchPathsAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/DefaultDllImportSearchPathsAttribute.cs new file mode 100644 index 0000000000..1ff27fbbd5 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/DefaultDllImportSearchPathsAttribute.cs @@ -0,0 +1,17 @@ +// 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. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Method, AllowMultiple = false)] + public sealed class DefaultDllImportSearchPathsAttribute : Attribute + { + public DefaultDllImportSearchPathsAttribute(DllImportSearchPath paths) + { + Paths = paths; + } + + public DllImportSearchPath Paths { get; } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/DllImportAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/DllImportAttribute.cs new file mode 100644 index 0000000000..97f870d49c --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/DllImportAttribute.cs @@ -0,0 +1,26 @@ +// 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. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + public sealed class DllImportAttribute : Attribute + { + public DllImportAttribute(string dllName) + { + Value = dllName; + } + + public string Value { get; } + + public string EntryPoint; + public CharSet CharSet; + public bool SetLastError; + public bool ExactSpelling; + public CallingConvention CallingConvention; + public bool BestFitMapping; + public bool PreserveSig; + public bool ThrowOnUnmappableChar; + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/DllImportSearchPath.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/DllImportSearchPath.cs new file mode 100644 index 0000000000..8dbdb40be9 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/DllImportSearchPath.cs @@ -0,0 +1,18 @@ +// 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. + +namespace System.Runtime.InteropServices +{ + [Flags] + public enum DllImportSearchPath + { + UseDllDirectoryForDependencies = 0x100, + ApplicationDirectory = 0x200, + UserDirectories = 0x400, + System32 = 0x800, + SafeDirectories = 0x1000, + AssemblyDirectory = 0x2, + LegacyBehavior = 0x0 + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/ExternalException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/ExternalException.cs index 81b9a46928..160fe301e8 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/ExternalException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/ExternalException.cs @@ -20,25 +20,26 @@ namespace System.Runtime.InteropServices { // Base exception for COM Interop errors &; Structured Exception Handler // exceptions. - // + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class ExternalException : SystemException { public ExternalException() : base(SR.Arg_ExternalException) { - HResult = __HResults.E_FAIL; + HResult = HResults.E_FAIL; } public ExternalException(string message) : base(message) { - HResult = __HResults.E_FAIL; + HResult = HResults.E_FAIL; } public ExternalException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.E_FAIL; + HResult = HResults.E_FAIL; } public ExternalException(string message, int errorCode) @@ -50,7 +51,6 @@ namespace System.Runtime.InteropServices protected ExternalException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } public virtual int ErrorCode diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/FieldOffsetAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/FieldOffsetAttribute.cs new file mode 100644 index 0000000000..27e1097749 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/FieldOffsetAttribute.cs @@ -0,0 +1,17 @@ +// 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. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Field, Inherited = false)] + public sealed class FieldOffsetAttribute : Attribute + { + public FieldOffsetAttribute(int offset) + { + Value = offset; + } + + public int Value { get; } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/GuidAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/GuidAttribute.cs new file mode 100644 index 0000000000..cf60b9bf70 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/GuidAttribute.cs @@ -0,0 +1,17 @@ +// 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. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Struct | AttributeTargets.Delegate, Inherited = false)] + public sealed class GuidAttribute : Attribute + { + public GuidAttribute(string guid) + { + Value = guid; + } + + public string Value { get; } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/HandleRef.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/HandleRef.cs new file mode 100644 index 0000000000..64d0553130 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/HandleRef.cs @@ -0,0 +1,47 @@ +// 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. + +namespace System.Runtime.InteropServices +{ + public struct HandleRef + { + // ! Do not add or rearrange fields as the EE depends on this layout. + //------------------------------------------------------------------ + private object _wrapper; + private IntPtr _handle; + //------------------------------------------------------------------ + + public HandleRef(object wrapper, IntPtr handle) + { + _wrapper = wrapper; + _handle = handle; + } + + public object Wrapper + { + get + { + return _wrapper; + } + } + + public IntPtr Handle + { + get + { + return _handle; + } + } + + public static explicit operator IntPtr(HandleRef value) + { + return value._handle; + } + + public static IntPtr ToIntPtr(HandleRef value) + { + return value._handle; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/InAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/InAttribute.cs new file mode 100644 index 0000000000..39f5a958bc --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/InAttribute.cs @@ -0,0 +1,14 @@ +// 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. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + public sealed class InAttribute : Attribute + { + public InAttribute() + { + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MarshalAsAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MarshalAsAttribute.cs new file mode 100644 index 0000000000..4a64050ed1 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MarshalAsAttribute.cs @@ -0,0 +1,39 @@ +// 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. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.ReturnValue, Inherited = false)] + public sealed class MarshalAsAttribute : Attribute + { + public MarshalAsAttribute(UnmanagedType unmanagedType) + { + Value = unmanagedType; + } + public MarshalAsAttribute(short unmanagedType) + { + Value = (UnmanagedType)unmanagedType; + } + + public UnmanagedType Value { get; } + + // Fields used with SubType = SafeArray. + public VarEnum SafeArraySubType; + public Type SafeArrayUserDefinedSubType; + + // Field used with iid_is attribute (interface pointers). + public int IidParameterIndex; + + // Fields used with SubType = ByValArray and LPArray. + // Array size = parameter(PI) * PM + C + public UnmanagedType ArraySubType; + public short SizeParamIndex; // param index PI + public int SizeConst; // constant C + + // Fields used with SubType = CustomMarshaler + public string MarshalType; // Name of marshaler class + public Type MarshalTypeRef; // Type of marshaler class + public string MarshalCookie; // cookie to pass to marshaler + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MarshalDirectiveException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MarshalDirectiveException.cs new file mode 100644 index 0000000000..1d0d59fab6 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MarshalDirectiveException.cs @@ -0,0 +1,46 @@ +// 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. + +/*============================================================================= +** +** +** Purpose: This exception is thrown when the marshaller encounters a signature +** that has an invalid MarshalAs CA for a given argument or is not +** supported. +** +=============================================================================*/ + + +using System; +using System.Runtime.Serialization; + +namespace System.Runtime.InteropServices +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class MarshalDirectiveException : SystemException + { + public MarshalDirectiveException() + : base(SR.Arg_MarshalDirectiveException) + { + HResult = HResults.COR_E_MARSHALDIRECTIVE; + } + + public MarshalDirectiveException(String message) + : base(message) + { + HResult = HResults.COR_E_MARSHALDIRECTIVE; + } + + public MarshalDirectiveException(String message, Exception inner) + : base(message, inner) + { + HResult = HResults.COR_E_MARSHALDIRECTIVE; + } + + protected MarshalDirectiveException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs new file mode 100644 index 0000000000..2651df53a0 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs @@ -0,0 +1,54 @@ +// 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.Buffers; +using System.Runtime.CompilerServices; +using Internal.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + /// + /// Provides a collection of methods for interoperating with , , + /// , and . + /// + public static class MemoryMarshal + { + /// Creates a from a . + /// The . + /// A representing the same memory as the , but writable. + /// + /// must be used with extreme caution. is used + /// to represent immutable data and other memory that is not meant to be written to; instances created + /// by should not be written to. The method exists to enable variables typed + /// as but only used for reading to store a . + /// + public static Memory AsMemory(ReadOnlyMemory readOnlyMemory) => + Unsafe.As, Memory>(ref readOnlyMemory); + + public static ref T GetReference(Span span) => ref span._pointer.Value; + + public static ref T GetReference(ReadOnlySpan span) => ref span._pointer.Value; + + public static bool TryGetArray(ReadOnlyMemory readOnlyMemory, out ArraySegment arraySegment) + { + object obj = readOnlyMemory.GetObjectStartLength(out int index, out int length); + if (index < 0) + { + if (((OwnedMemory)obj).TryGetArray(out var segment)) + { + arraySegment = new ArraySegment(segment.Array, segment.Offset + (index & ReadOnlyMemory.RemoveOwnedFlagBitMask), length); + return true; + } + } + else if (obj is T[] arr) + { + arraySegment = new ArraySegment(arr, index, length); + return true; + } + + arraySegment = default; + return false; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/OptionalAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/OptionalAttribute.cs new file mode 100644 index 0000000000..5ac75d7b3e --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/OptionalAttribute.cs @@ -0,0 +1,14 @@ +// 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. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + public sealed class OptionalAttribute : Attribute + { + public OptionalAttribute() + { + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/OutAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/OutAttribute.cs new file mode 100644 index 0000000000..338ceac91e --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/OutAttribute.cs @@ -0,0 +1,14 @@ +// 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. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + public sealed class OutAttribute : Attribute + { + public OutAttribute() + { + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/PreserveSigAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/PreserveSigAttribute.cs new file mode 100644 index 0000000000..464e1abcbe --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/PreserveSigAttribute.cs @@ -0,0 +1,14 @@ +// 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. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + public sealed class PreserveSigAttribute : Attribute + { + public PreserveSigAttribute() + { + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/SafeBuffer.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/SafeBuffer.cs new file mode 100644 index 0000000000..455e413928 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/SafeBuffer.cs @@ -0,0 +1,405 @@ +// 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. + +/*============================================================ +** +** Purpose: Unsafe code that uses pointers should use +** SafePointer to fix subtle lifetime problems with the +** underlying resource. +** +===========================================================*/ + +// Design points: +// *) Avoid handle-recycling problems (including ones triggered via +// resurrection attacks) for all accesses via pointers. This requires tying +// together the lifetime of the unmanaged resource with the code that reads +// from that resource, in a package that uses synchronization to enforce +// the correct semantics during finalization. We're using SafeHandle's +// ref count as a gate on whether the pointer can be dereferenced because that +// controls the lifetime of the resource. +// +// *) Keep the penalties for using this class small, both in terms of space +// and time. Having multiple threads reading from a memory mapped file +// will already require 2 additional interlocked operations. If we add in +// a "current position" concept, that requires additional space in memory and +// synchronization. Since the position in memory is often (but not always) +// something that can be stored on the stack, we can save some memory by +// excluding it from this object. However, avoiding the need for +// synchronization is a more significant win. This design allows multiple +// threads to read and write memory simultaneously without locks (as long as +// you don't write to a region of memory that overlaps with what another +// thread is accessing). +// +// *) Space-wise, we use the following memory, including SafeHandle's fields: +// Object Header MT* handle int bool bool <2 pad bytes> length +// On 32 bit platforms: 24 bytes. On 64 bit platforms: 40 bytes. +// (We can safe 4 bytes on x86 only by shrinking SafeHandle) +// +// *) Wrapping a SafeHandle would have been a nice solution, but without an +// ordering between critical finalizable objects, it would have required +// changes to each SafeHandle subclass to opt in to being usable from a +// SafeBuffer (or some clever exposure of SafeHandle's state fields and a +// way of forcing ReleaseHandle to run even after the SafeHandle has been +// finalized with a ref count > 1). We can use less memory and create fewer +// objects by simply inserting a SafeBuffer into the class hierarchy. +// +// *) In an ideal world, we could get marshaling support for SafeBuffer that +// would allow us to annotate a P/Invoke declaration, saying this parameter +// specifies the length of the buffer, and the units of that length are X. +// P/Invoke would then pass that size parameter to SafeBuffer. +// [DllImport(...)] +// static extern SafeMemoryHandle AllocCharBuffer(int numChars); +// If we could put an attribute on the SafeMemoryHandle saying numChars is +// the element length, and it must be multiplied by 2 to get to the byte +// length, we can simplify the usage model for SafeBuffer. +// +// *) This class could benefit from a constraint saying T is a value type +// containing no GC references. + +// Implementation notes: +// *) The Initialize method must be called before you use any instance of +// a SafeBuffer. To avoid race conditions when storing SafeBuffers in statics, +// you either need to take a lock when publishing the SafeBuffer, or you +// need to create a local, initialize the SafeBuffer, then assign to the +// static variable (perhaps using Interlocked.CompareExchange). Of course, +// assignments in a static class constructor are under a lock implicitly. + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using Internal.Runtime.CompilerServices; +using Microsoft.Win32.SafeHandles; + +namespace System.Runtime.InteropServices +{ + public abstract unsafe class SafeBuffer : SafeHandleZeroOrMinusOneIsInvalid + { + // Steal UIntPtr.MaxValue as our uninitialized value. + private static readonly UIntPtr Uninitialized = (UIntPtr.Size == 4) ? + ((UIntPtr)UInt32.MaxValue) : ((UIntPtr)UInt64.MaxValue); + + private UIntPtr _numBytes; + + protected SafeBuffer(bool ownsHandle) : base(ownsHandle) + { + _numBytes = Uninitialized; + } + + /// + /// Specifies the size of the region of memory, in bytes. Must be + /// called before using the SafeBuffer. + /// + /// Number of valid bytes in memory. + [CLSCompliant(false)] + public void Initialize(ulong numBytes) + { + if (IntPtr.Size == 4 && numBytes > UInt32.MaxValue) + throw new ArgumentOutOfRangeException(nameof(numBytes), SR.ArgumentOutOfRange_AddressSpace); + + if (numBytes >= (ulong)Uninitialized) + throw new ArgumentOutOfRangeException(nameof(numBytes), SR.ArgumentOutOfRange_UIntPtrMax); + + _numBytes = (UIntPtr)numBytes; + } + + /// + /// Specifies the size of the region in memory, as the number of + /// elements in an array. Must be called before using the SafeBuffer. + /// + [CLSCompliant(false)] + public void Initialize(uint numElements, uint sizeOfEachElement) + { + if (IntPtr.Size == 4 && numElements * sizeOfEachElement > UInt32.MaxValue) + throw new ArgumentOutOfRangeException("numBytes", SR.ArgumentOutOfRange_AddressSpace); + + if (numElements * sizeOfEachElement >= (ulong)Uninitialized) + throw new ArgumentOutOfRangeException(nameof(numElements), SR.ArgumentOutOfRange_UIntPtrMax); + + _numBytes = checked((UIntPtr)(numElements * sizeOfEachElement)); + } + + /// + /// Specifies the size of the region in memory, as the number of + /// elements in an array. Must be called before using the SafeBuffer. + /// + [CLSCompliant(false)] + public void Initialize(uint numElements) where T : struct + { + Initialize(numElements, AlignedSizeOf()); + } + + // Callers should ensure that they check whether the pointer ref param + // is null when AcquirePointer returns. If it is not null, they must + // call ReleasePointer. This method calls DangerousAddRef + // & exposes the pointer. Unlike Read, it does not alter the "current + // position" of the pointer. Here's how to use it: + // + // byte* pointer = null; + // try { + // safeBuffer.AcquirePointer(ref pointer); + // // Use pointer here, with your own bounds checking + // } + // finally { + // if (pointer != null) + // safeBuffer.ReleasePointer(); + // } + // + // Note: If you cast this byte* to a T*, you have to worry about + // whether your pointer is aligned. Additionally, you must take + // responsibility for all bounds checking with this pointer. + /// + /// Obtain the pointer from a SafeBuffer for a block of code, + /// with the express responsibility for bounds checking and calling + /// ReleasePointer later to ensure the pointer can be freed later. + /// This method either completes successfully or throws an exception + /// and returns with pointer set to null. + /// + /// A byte*, passed by reference, to receive + /// the pointer from within the SafeBuffer. You must set + /// pointer to null before calling this method. + [CLSCompliant(false)] + public void AcquirePointer(ref byte* pointer) + { + if (_numBytes == Uninitialized) + throw NotInitialized(); + + pointer = null; + + bool junk = false; + DangerousAddRef(ref junk); + pointer = (byte*)handle; + } + + public void ReleasePointer() + { + if (_numBytes == Uninitialized) + throw NotInitialized(); + + DangerousRelease(); + } + + /// + /// Read a value type from memory at the given offset. This is + /// equivalent to: return *(T*)(bytePtr + byteOffset); + /// + /// The value type to read + /// Where to start reading from memory. You + /// may have to consider alignment. + /// An instance of T read from memory. + [CLSCompliant(false)] + public T Read(ulong byteOffset) where T : struct + { + if (_numBytes == Uninitialized) + throw NotInitialized(); + + uint sizeofT = SizeOf(); + byte* ptr = (byte*)handle + byteOffset; + SpaceCheck(ptr, sizeofT); + + // return *(T*) (_ptr + byteOffset); + T value = default(T); + bool mustCallRelease = false; + try + { + DangerousAddRef(ref mustCallRelease); + + fixed (byte* pStructure = &Unsafe.As(ref value)) + Buffer.Memmove(pStructure, ptr, sizeofT); + } + finally + { + if (mustCallRelease) + DangerousRelease(); + } + return value; + } + + [CLSCompliant(false)] + public void ReadArray(ulong byteOffset, T[] array, int index, int count) + where T : struct + { + if (array == null) + throw new ArgumentNullException(nameof(array), SR.ArgumentNull_Buffer); + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + if (array.Length - index < count) + throw new ArgumentException(SR.Argument_InvalidOffLen); + + if (_numBytes == Uninitialized) + throw NotInitialized(); + + uint sizeofT = SizeOf(); + uint alignedSizeofT = AlignedSizeOf(); + byte* ptr = (byte*)handle + byteOffset; + SpaceCheck(ptr, checked((ulong)(alignedSizeofT * count))); + + bool mustCallRelease = false; + try + { + DangerousAddRef(ref mustCallRelease); + + if (count > 0) + { + unsafe + { + fixed (byte* pStructure = &Unsafe.As(ref array[index])) + { + for (int i = 0; i < count; i++) + Buffer.Memmove(pStructure + sizeofT * i, ptr + alignedSizeofT * i, sizeofT); + } + } + } + } + finally + { + if (mustCallRelease) + DangerousRelease(); + } + } + + /// + /// Write a value type to memory at the given offset. This is + /// equivalent to: *(T*)(bytePtr + byteOffset) = value; + /// + /// The type of the value type to write to memory. + /// The location in memory to write to. You + /// may have to consider alignment. + /// The value type to write to memory. + [CLSCompliant(false)] + public void Write(ulong byteOffset, T value) where T : struct + { + if (_numBytes == Uninitialized) + throw NotInitialized(); + + uint sizeofT = SizeOf(); + byte* ptr = (byte*)handle + byteOffset; + SpaceCheck(ptr, sizeofT); + + // *((T*) (_ptr + byteOffset)) = value; + bool mustCallRelease = false; + try + { + DangerousAddRef(ref mustCallRelease); + + fixed (byte* pStructure = &Unsafe.As(ref value)) + Buffer.Memmove(ptr, pStructure, sizeofT); + } + finally + { + if (mustCallRelease) + DangerousRelease(); + } + } + + [CLSCompliant(false)] + public void WriteArray(ulong byteOffset, T[] array, int index, int count) + where T : struct + { + if (array == null) + throw new ArgumentNullException(nameof(array), SR.ArgumentNull_Buffer); + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + if (array.Length - index < count) + throw new ArgumentException(SR.Argument_InvalidOffLen); + + if (_numBytes == Uninitialized) + throw NotInitialized(); + + uint sizeofT = SizeOf(); + uint alignedSizeofT = AlignedSizeOf(); + byte* ptr = (byte*)handle + byteOffset; + SpaceCheck(ptr, checked((ulong)(alignedSizeofT * count))); + + bool mustCallRelease = false; + try + { + DangerousAddRef(ref mustCallRelease); + + if (count > 0) + { + unsafe + { + fixed (byte* pStructure = &Unsafe.As(ref array[index])) + { + for (int i = 0; i < count; i++) + Buffer.Memmove(ptr + alignedSizeofT * i, pStructure + sizeofT * i, sizeofT); + } + } + } + } + finally + { + if (mustCallRelease) + DangerousRelease(); + } + } + + /// + /// Returns the number of bytes in the memory region. + /// + [CLSCompliant(false)] + public ulong ByteLength + { + get + { + if (_numBytes == Uninitialized) + throw NotInitialized(); + + return (ulong)_numBytes; + } + } + + /* No indexer. The perf would be misleadingly bad. People should use + * AcquirePointer and ReleasePointer instead. */ + + private void SpaceCheck(byte* ptr, ulong sizeInBytes) + { + if ((ulong)_numBytes < sizeInBytes) + NotEnoughRoom(); + if ((ulong)(ptr - (byte*)handle) > ((ulong)_numBytes) - sizeInBytes) + NotEnoughRoom(); + } + + private static void NotEnoughRoom() + { + throw new ArgumentException(SR.Arg_BufferTooSmall); + } + + private static InvalidOperationException NotInitialized() + { + return new InvalidOperationException(SR.InvalidOperation_MustCallInitialize); + } + + /// + /// Returns the size that SafeBuffer (and hence, UnmanagedMemoryAccessor) reserves in the unmanaged buffer for each element of an array of T. This is not the same + /// value that sizeof(T) returns! Since the primary use case is to parse memory mapped files, we cannot change this algorithm as this defines a de-facto serialization format. + /// Throws if T contains GC references. + /// + internal static uint AlignedSizeOf() where T : struct + { + uint size = SizeOf(); + if (size == 1 || size == 2) + { + return size; + } + + return (uint)(((size + 3) & (~3))); + } + + /// + /// Returns same value as sizeof(T) but throws if T contains GC references. + /// + internal static uint SizeOf() where T : struct + { + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + throw new ArgumentException(SR.Argument_NeedStructWithNoRefs); + + return (uint)Unsafe.SizeOf(); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/StructLayoutAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/StructLayoutAttribute.cs new file mode 100644 index 0000000000..c4cce9956e --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/StructLayoutAttribute.cs @@ -0,0 +1,26 @@ +// 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. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)] + public sealed class StructLayoutAttribute : Attribute + { + public StructLayoutAttribute(LayoutKind layoutKind) + { + Value = layoutKind; + } + + public StructLayoutAttribute(short layoutKind) + { + Value = (LayoutKind)layoutKind; + } + + public LayoutKind Value { get; } + + public int Pack; + public int Size; + public CharSet CharSet; + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/UnmanagedFunctionPointerAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/UnmanagedFunctionPointerAttribute.cs index 2d69c95afe..c4f96903ee 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/UnmanagedFunctionPointerAttribute.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/UnmanagedFunctionPointerAttribute.cs @@ -7,11 +7,6 @@ namespace System.Runtime.InteropServices [AttributeUsage(AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] public sealed class UnmanagedFunctionPointerAttribute : Attribute { - public bool BestFitMapping; - public bool SetLastError; - public bool ThrowOnUnmappableChar; - public CharSet CharSet; - public UnmanagedFunctionPointerAttribute() { CallingConvention = CallingConvention.Winapi; @@ -23,5 +18,10 @@ namespace System.Runtime.InteropServices } public CallingConvention CallingConvention { get; } + + public bool BestFitMapping; + public bool SetLastError; + public bool ThrowOnUnmappableChar; + public CharSet CharSet; } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/Serialization/SerializationException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/Serialization/SerializationException.cs index bfe3313659..1c9c21eabb 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/Serialization/SerializationException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/Serialization/SerializationException.cs @@ -6,6 +6,8 @@ using System.Runtime.Serialization; namespace System.Runtime.Serialization { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class SerializationException : SystemException { private static String s_nullMessage = SR.SerializationException; @@ -15,25 +17,24 @@ namespace System.Runtime.Serialization public SerializationException() : base(s_nullMessage) { - HResult = __HResults.COR_E_SERIALIZATION; + HResult = HResults.COR_E_SERIALIZATION; } public SerializationException(String message) : base(message) { - HResult = __HResults.COR_E_SERIALIZATION; + HResult = HResults.COR_E_SERIALIZATION; } public SerializationException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_SERIALIZATION; + HResult = HResults.COR_E_SERIALIZATION; } protected SerializationException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/Serialization/StreamingContext.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/Serialization/StreamingContext.cs index 4fe90cad8d..cdcb1c335b 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/Serialization/StreamingContext.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/Serialization/StreamingContext.cs @@ -4,7 +4,7 @@ namespace System.Runtime.Serialization { - public struct StreamingContext + public readonly struct StreamingContext { private readonly object _additionalContext; private readonly StreamingContextStates _state; diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/Versioning/TargetFrameworkAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/Versioning/TargetFrameworkAttribute.cs index 54ccdf2c81..a819066382 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Runtime/Versioning/TargetFrameworkAttribute.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Runtime/Versioning/TargetFrameworkAttribute.cs @@ -13,7 +13,6 @@ ===========================================================*/ using System; -using System.Diagnostics.Contracts; namespace System.Runtime.Versioning { @@ -28,7 +27,6 @@ namespace System.Runtime.Versioning { if (frameworkName == null) throw new ArgumentNullException(nameof(frameworkName)); - Contract.EndContractBlock(); _frameworkName = frameworkName; } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/SByte.cs b/external/corert/src/System.Private.CoreLib/shared/System/SByte.cs new file mode 100644 index 0000000000..c7cee2adc2 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/SByte.cs @@ -0,0 +1,332 @@ +// 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.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [CLSCompliant(false)] [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct SByte : IComparable, IConvertible, IFormattable, IComparable, IEquatable, ISpanFormattable + { + private sbyte m_value; // Do not rename (binary serialization) + + // The maximum value that a Byte may represent: 127. + public const sbyte MaxValue = (sbyte)0x7F; + + // The minimum value that a Byte may represent: -128. + public const sbyte MinValue = unchecked((sbyte)0x80); + + + // Compares this object to another object, returning an integer that + // indicates the relationship. + // Returns a value less than zero if this object + // null is considered to be less than any instance. + // If object is not of type SByte, this method throws an ArgumentException. + // + public int CompareTo(Object obj) + { + if (obj == null) + { + return 1; + } + if (!(obj is SByte)) + { + throw new ArgumentException(SR.Arg_MustBeSByte); + } + return m_value - ((SByte)obj).m_value; + } + + public int CompareTo(SByte value) + { + return m_value - value; + } + + // Determines whether two Byte objects are equal. + public override bool Equals(Object obj) + { + if (!(obj is SByte)) + { + return false; + } + return m_value == ((SByte)obj).m_value; + } + + [NonVersionable] + public bool Equals(SByte obj) + { + return m_value == obj; + } + + // Gets a hash code for this instance. + public override int GetHashCode() + { + return ((int)m_value ^ (int)m_value << 8); + } + + + // Provides a string representation of a byte. + public override String ToString() + { + return Number.FormatInt32(m_value, null, null); + } + + public String ToString(IFormatProvider provider) + { + return Number.FormatInt32(m_value, null, provider); + } + + public String ToString(String format) + { + return ToString(format, null); + } + + public String ToString(String format, IFormatProvider provider) + { + if (m_value < 0 && format != null && format.Length > 0 && (format[0] == 'X' || format[0] == 'x')) + { + uint temp = (uint)(m_value & 0x000000FF); + return Number.FormatUInt32(temp, format, provider); + } + return Number.FormatInt32(m_value, format, provider); + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) + { + if (m_value < 0 && format.Length > 0 && (format[0] == 'X' || format[0] == 'x')) + { + uint temp = (uint)(m_value & 0x000000FF); + return Number.TryFormatUInt32(temp, format, provider, destination, out charsWritten); + } + return Number.TryFormatInt32(m_value, format, provider, destination, out charsWritten); + } + + [CLSCompliant(false)] + public static sbyte Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); + } + + [CLSCompliant(false)] + public static sbyte Parse(String s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, style, NumberFormatInfo.CurrentInfo); + } + + [CLSCompliant(false)] + public static sbyte Parse(String s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + } + + // Parses a signed byte from a String in the given style. If + // a NumberFormatInfo isn't specified, the current culture's + // NumberFormatInfo is assumed. + // + [CLSCompliant(false)] + public static sbyte Parse(String s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static sbyte Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Parse(s, style, NumberFormatInfo.GetInstance(provider)); + } + + private static sbyte Parse(String s, NumberStyles style, NumberFormatInfo info) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, style, info); + } + + private static sbyte Parse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info) + { + int i = 0; + try + { + i = Number.ParseInt32(s, style, info); + } + catch (OverflowException e) + { + throw new OverflowException(SR.Overflow_SByte, e); + } + + if ((style & NumberStyles.AllowHexSpecifier) != 0) + { // We are parsing a hexadecimal number + if ((i < 0) || i > Byte.MaxValue) + { + throw new OverflowException(SR.Overflow_SByte); + } + return (sbyte)i; + } + + if (i < MinValue || i > MaxValue) throw new OverflowException(SR.Overflow_SByte); + return (sbyte)i; + } + + [CLSCompliant(false)] + public static bool TryParse(String s, out SByte result) + { + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan s, out sbyte result) + { + return TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + [CLSCompliant(false)] + public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out SByte result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out sbyte result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + private static bool TryParse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out SByte result) + { + result = 0; + int i; + if (!Number.TryParseInt32(s, style, info, out i)) + { + return false; + } + + if ((style & NumberStyles.AllowHexSpecifier) != 0) + { // We are parsing a hexadecimal number + if ((i < 0) || i > Byte.MaxValue) + { + return false; + } + result = (sbyte)i; + return true; + } + + if (i < MinValue || i > MaxValue) + { + return false; + } + result = (sbyte)i; + return true; + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.SByte; + } + + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return Convert.ToChar(m_value); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return m_value; + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return m_value; + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(m_value); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(m_value); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "SByte", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Security/CryptographicException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Security/CryptographicException.cs index 7c4fa176f3..78ee290693 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Security/CryptographicException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Security/CryptographicException.cs @@ -7,6 +7,8 @@ using System.Runtime.Serialization; namespace System.Security.Cryptography { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class CryptographicException : SystemException { public CryptographicException() @@ -38,7 +40,6 @@ namespace System.Security.Cryptography protected CryptographicException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Security/SecurityException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Security/SecurityException.cs index 538f475343..61504c3ba1 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Security/SecurityException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Security/SecurityException.cs @@ -7,37 +7,46 @@ using System.Runtime.Serialization; namespace System.Security { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class SecurityException : SystemException { + private const string DemandedName = "Demanded"; + private const string GrantedSetName = "GrantedSet"; + private const string RefusedSetName = "RefusedSet"; + private const string DeniedName = "Denied"; + private const string PermitOnlyName = "PermitOnly"; + private const string UrlName = "Url"; + public SecurityException() : base(SR.Arg_SecurityException) { - HResult = __HResults.COR_E_SECURITY; + HResult = HResults.COR_E_SECURITY; } public SecurityException(string message) : base(message) { - HResult = __HResults.COR_E_SECURITY; + HResult = HResults.COR_E_SECURITY; } public SecurityException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_SECURITY; + HResult = HResults.COR_E_SECURITY; } public SecurityException(string message, Type type) : base(message) { - HResult = __HResults.COR_E_SECURITY; + HResult = HResults.COR_E_SECURITY; PermissionType = type; } public SecurityException(string message, Type type, string state) : base(message) { - HResult = __HResults.COR_E_SECURITY; + HResult = HResults.COR_E_SECURITY; PermissionType = type; PermissionState = state; } @@ -45,12 +54,26 @@ namespace System.Security protected SecurityException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); + Demanded = (string)info.GetValueNoThrow(DemandedName, typeof(string)); + GrantedSet = (string)info.GetValueNoThrow(GrantedSetName, typeof(string)); + RefusedSet = (string)info.GetValueNoThrow(RefusedSetName, typeof(string)); + DenySetInstance = (string)info.GetValueNoThrow(DeniedName, typeof(string)); + PermitOnlySetInstance = (string)info.GetValueNoThrow(PermitOnlyName, typeof(string)); + Url = (string)info.GetValueNoThrow(UrlName, typeof(string)); } public override string ToString() => base.ToString(); - public override void GetObjectData(SerializationInfo info, StreamingContext context) => base.GetObjectData(info, context); + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue(DemandedName, Demanded, typeof(string)); + info.AddValue(GrantedSetName, GrantedSet, typeof(string)); + info.AddValue(RefusedSetName, RefusedSet, typeof(string)); + info.AddValue(DeniedName, DenySetInstance, typeof(string)); + info.AddValue(PermitOnlyName, PermitOnlySetInstance, typeof(string)); + info.AddValue(UrlName, Url, typeof(string)); + } public object Demanded { get; set; } public object DenySetInstance { get; set; } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Security/VerificationException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Security/VerificationException.cs index ea5a75906e..e2afd4cabe 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Security/VerificationException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Security/VerificationException.cs @@ -6,30 +6,31 @@ using System.Runtime.Serialization; namespace System.Security { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class VerificationException : SystemException { public VerificationException() : base(SR.Verification_Exception) { - HResult = __HResults.COR_E_VERIFICATION; + HResult = HResults.COR_E_VERIFICATION; } public VerificationException(string message) : base(message) { - HResult = __HResults.COR_E_VERIFICATION; + HResult = HResults.COR_E_VERIFICATION; } public VerificationException(string message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_VERIFICATION; + HResult = HResults.COR_E_VERIFICATION; } protected VerificationException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/SerializableAttribute.cs b/external/corert/src/System.Private.CoreLib/shared/System/SerializableAttribute.cs new file mode 100644 index 0000000000..c256931373 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/SerializableAttribute.cs @@ -0,0 +1,12 @@ +// 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. + +namespace System +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Delegate, Inherited = false)] + public sealed class SerializableAttribute : Attribute + { + public SerializableAttribute() { } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Single.cs b/external/corert/src/System.Private.CoreLib/shared/System/Single.cs new file mode 100644 index 0000000000..df97427d38 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Single.cs @@ -0,0 +1,439 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: A wrapper class for the primitive type float. +** +** +===========================================================*/ + +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Single : IComparable, IConvertible, IFormattable, IComparable, IEquatable, ISpanFormattable + { + private float m_value; // Do not rename (binary serialization) + + // + // Public constants + // + public const float MinValue = (float)-3.40282346638528859e+38; + public const float Epsilon = (float)1.4e-45; + public const float MaxValue = (float)3.40282346638528859e+38; + public const float PositiveInfinity = (float)1.0 / (float)0.0; + public const float NegativeInfinity = (float)-1.0 / (float)0.0; + public const float NaN = (float)0.0 / (float)0.0; + + // We use this explicit definition to avoid the confusion between 0.0 and -0.0. + internal const float NegativeZero = (float)-0.0; + + /// Determines whether the specified value is finite (zero, subnormal, or normal). + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFinite(float f) + { + var bits = BitConverter.SingleToInt32Bits(f); + return (bits & 0x7FFFFFFF) < 0x7F800000; + } + + /// Determines whether the specified value is infinite. + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsInfinity(float f) + { + var bits = BitConverter.SingleToInt32Bits(f); + return (bits & 0x7FFFFFFF) == 0x7F800000; + } + + /// Determines whether the specified value is NaN. + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsNaN(float f) + { + var bits = BitConverter.SingleToInt32Bits(f); + return (bits & 0x7FFFFFFF) > 0x7F800000; + } + + /// Determines whether the specified value is negative. + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsNegative(float f) + { + var bits = unchecked((uint)BitConverter.SingleToInt32Bits(f)); + return (bits & 0x80000000) == 0x80000000; + } + + /// Determines whether the specified value is negative infinity. + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsNegativeInfinity(float f) + { + return (f == float.NegativeInfinity); + } + + /// Determines whether the specified value is normal. + [NonVersionable] + // This is probably not worth inlining, it has branches and should be rarely called + public unsafe static bool IsNormal(float f) + { + var bits = BitConverter.SingleToInt32Bits(f); + bits &= 0x7FFFFFFF; + return (bits < 0x7F800000) && (bits != 0) && ((bits & 0x7F800000) != 0); + } + + /// Determines whether the specified value is positive infinity. + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsPositiveInfinity(float f) + { + return (f == float.PositiveInfinity); + } + + /// Determines whether the specified value is subnormal. + [NonVersionable] + // This is probably not worth inlining, it has branches and should be rarely called + public unsafe static bool IsSubnormal(float f) + { + var bits = BitConverter.SingleToInt32Bits(f); + bits &= 0x7FFFFFFF; + return (bits < 0x7F800000) && (bits != 0) && ((bits & 0x7F800000) == 0); + } + + // Compares this object to another object, returning an integer that + // indicates the relationship. + // Returns a value less than zero if this object + // null is considered to be less than any instance. + // If object is not of type Single, this method throws an ArgumentException. + // + public int CompareTo(Object value) + { + if (value == null) + { + return 1; + } + if (value is Single) + { + float f = (float)value; + if (m_value < f) return -1; + if (m_value > f) return 1; + if (m_value == f) return 0; + + // At least one of the values is NaN. + if (IsNaN(m_value)) + return (IsNaN(f) ? 0 : -1); + else // f is NaN. + return 1; + } + throw new ArgumentException(SR.Arg_MustBeSingle); + } + + + public int CompareTo(Single value) + { + if (m_value < value) return -1; + if (m_value > value) return 1; + if (m_value == value) return 0; + + // At least one of the values is NaN. + if (IsNaN(m_value)) + return (IsNaN(value) ? 0 : -1); + else // f is NaN. + return 1; + } + + [NonVersionable] + public static bool operator ==(Single left, Single right) + { + return left == right; + } + + [NonVersionable] + public static bool operator !=(Single left, Single right) + { + return left != right; + } + + [NonVersionable] + public static bool operator <(Single left, Single right) + { + return left < right; + } + + [NonVersionable] + public static bool operator >(Single left, Single right) + { + return left > right; + } + + [NonVersionable] + public static bool operator <=(Single left, Single right) + { + return left <= right; + } + + [NonVersionable] + public static bool operator >=(Single left, Single right) + { + return left >= right; + } + + public override bool Equals(Object obj) + { + if (!(obj is Single)) + { + return false; + } + float temp = ((Single)obj).m_value; + if (temp == m_value) + { + return true; + } + + return IsNaN(temp) && IsNaN(m_value); + } + + public bool Equals(Single obj) + { + if (obj == m_value) + { + return true; + } + + return IsNaN(obj) && IsNaN(m_value); + } + + public unsafe override int GetHashCode() + { + float f = m_value; + if (f == 0) + { + // Ensure that 0 and -0 have the same hash code + return 0; + } + int v = *(int*)(&f); + return v; + } + + public override String ToString() + { + return Number.FormatSingle(m_value, null, NumberFormatInfo.CurrentInfo); + } + + public String ToString(IFormatProvider provider) + { + return Number.FormatSingle(m_value, null, NumberFormatInfo.GetInstance(provider)); + } + + public String ToString(String format) + { + return Number.FormatSingle(m_value, format, NumberFormatInfo.CurrentInfo); + } + + public String ToString(String format, IFormatProvider provider) + { + return Number.FormatSingle(m_value, format, NumberFormatInfo.GetInstance(provider)); + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) + { + return Number.TryFormatSingle(m_value, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); + } + + // Parses a float from a String in the given style. If + // a NumberFormatInfo isn't specified, the current culture's + // NumberFormatInfo is assumed. + // + // This method will not throw an OverflowException, but will return + // PositiveInfinity or NegativeInfinity for a number that is too + // large or too small. + // + public static float Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseSingle(s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo); + } + + public static float Parse(String s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseSingle(s, style, NumberFormatInfo.CurrentInfo); + } + + public static float Parse(String s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseSingle(s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.GetInstance(provider)); + } + + public static float Parse(String s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseSingle(s, style, NumberFormatInfo.GetInstance(provider)); + } + + public static float Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return Number.ParseSingle(s, style, NumberFormatInfo.GetInstance(provider)); + } + + public static Boolean TryParse(String s, out Single result) + { + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(ReadOnlySpan s, out float result) + { + return TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo, out result); + } + + public static Boolean TryParse(String s, NumberStyles style, IFormatProvider provider, out Single result) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out float result) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + private static Boolean TryParse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out Single result) + { + bool success = Number.TryParseSingle(s, style, info, out result); + if (!success) + { + ReadOnlySpan sTrim = s.Trim(); + if (StringSpanHelpers.Equals(sTrim, info.PositiveInfinitySymbol)) + { + result = PositiveInfinity; + } + else if (StringSpanHelpers.Equals(sTrim, info.NegativeInfinitySymbol)) + { + result = NegativeInfinity; + } + else if (StringSpanHelpers.Equals(sTrim, info.NaNSymbol)) + { + result = NaN; + } + else + { + return false; // We really failed + } + } + return true; + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.Single; + } + + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Single", "Char")); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(m_value); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return m_value; + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(m_value); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Single", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Span.NonGeneric.cs b/external/corert/src/System.Private.CoreLib/shared/System/Span.NonGeneric.cs index 4cdba2181a..775426f6b1 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Span.NonGeneric.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Span.NonGeneric.cs @@ -5,6 +5,9 @@ using System.Diagnostics; using System.Runtime; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using Internal.Runtime.CompilerServices; #if BIT64 using nuint = System.UInt64; @@ -15,10 +18,47 @@ using nuint = System.UInt32; namespace System { /// - /// Extension methods and non-generic helpers for Span and ReadOnlySpan + /// Extension methods and non-generic helpers for Span, ReadOnlySpan, Memory, and ReadOnlyMemory. /// public static class Span { + /// Creates a new over the portion of the target string. + /// The target string. + /// Thrown when is a null reference (Nothing in Visual Basic). + public static ReadOnlyMemory AsReadOnlyMemory(this string text) + { + if (text == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); + } + + return new ReadOnlyMemory(text, 0, text.Length); + } + + /// Attempts to get the underlying from a . + /// The memory that may be wrapping a object. + /// The string. + /// The starting location in . + /// The number of items in . + /// + public static bool TryGetString(this ReadOnlyMemory readOnlyMemory, out string text, out int start, out int length) + { + if (readOnlyMemory.GetObjectStartLength(out int offset, out int count) is string s) + { + text = s; + start = offset; + length = count; + return true; + } + else + { + text = null; + start = 0; + length = 0; + return false; + } + } + /// /// Casts a Span of one primitive type to Span of bytes. /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety. @@ -35,7 +75,7 @@ namespace System ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); return new Span( - ref Unsafe.As(ref source.DangerousGetPinnableReference()), + ref Unsafe.As(ref MemoryMarshal.GetReference(source)), checked(source.Length * Unsafe.SizeOf())); } @@ -55,7 +95,7 @@ namespace System ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); return new ReadOnlySpan( - ref Unsafe.As(ref source.DangerousGetPinnableReference()), + ref Unsafe.As(ref MemoryMarshal.GetReference(source)), checked(source.Length * Unsafe.SizeOf())); } @@ -107,7 +147,7 @@ namespace System ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo)); return new ReadOnlySpan( - ref Unsafe.As(ref source.DangerousGetPinnableReference()), + ref Unsafe.As(ref MemoryMarshal.GetReference(source)), checked((int)((long)source.Length * Unsafe.SizeOf() / Unsafe.SizeOf()))); } @@ -118,7 +158,7 @@ namespace System /// Thrown when is a null /// reference (Nothing in Visual Basic). [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan AsSpan(this string text) + public static ReadOnlySpan AsReadOnlySpan(this string text) { if (text == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); @@ -165,7 +205,7 @@ namespace System if (byteLength == 0) return; -#if AMD64 && CORECLR +#if CORECLR && (AMD64 || ARM64) if (byteLength > 4096) goto PInvoke; Unsafe.InitBlockUnaligned(ref b, 0, (uint)byteLength); return; diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Span.cs b/external/corert/src/System.Private.CoreLib/shared/System/Span.cs index 2494ce3145..5a813174d9 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Span.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Span.cs @@ -2,11 +2,11 @@ // 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.ComponentModel; using System.Diagnostics; -using System.Runtime; using System.Runtime.CompilerServices; -using EditorBrowsableState = System.ComponentModel.EditorBrowsableState; -using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute; +using System.Runtime.Versioning; +using Internal.Runtime.CompilerServices; #pragma warning disable 0809 //warning CS0809: Obsolete member 'Span.Equals(object)' overrides non-obsolete member 'object.Equals(object)' @@ -22,10 +22,13 @@ namespace System /// Span represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed /// or native memory, or to memory allocated on the stack. It is type- and memory-safe. /// - public struct Span + [DebuggerTypeProxy(typeof(SpanDebugView<>))] + [DebuggerDisplay("{DebuggerDisplay,nq}")] + [NonVersionable] + public readonly ref struct Span { /// A byref or a native ptr. - private readonly ByReference _pointer; + internal readonly ByReference _pointer; /// The number of elements this Span contains. #if PROJECTN [Bound] @@ -51,32 +54,6 @@ namespace System _length = array.Length; } - /// - /// Creates a new span over the portion of the target array beginning - /// at 'start' index and covering the remainder of the array. - /// - /// The target array. - /// The index at which to begin the span. - /// Thrown when is a null - /// reference (Nothing in Visual Basic). - /// Thrown when is covariant and array's type is not exactly T[]. - /// - /// Thrown when the specified is not in the range (<0 or >=Length). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span(T[] array, int start) - { - if (array == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - if (default(T) == null && array.GetType() != typeof(T[])) - ThrowHelper.ThrowArrayTypeMismatchException(); - if ((uint)start > (uint)array.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(); - - _pointer = new ByReference(ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), start)); - _length = array.Length - start; - } - /// /// Creates a new span over the portion of the target array beginning /// at 'start' index and ending at 'end' index (exclusive). @@ -141,6 +118,7 @@ namespace System /// A reference to data within that object. /// The number of elements the memory contains. [MethodImpl(MethodImplOptions.AggressiveInlining)] + [EditorBrowsable(EditorBrowsableState.Never)] public static Span DangerousCreate(object obj, ref T objectData, int length) => new Span(ref objectData, length); // Constructor for internal use only. @@ -153,11 +131,16 @@ namespace System _length = length; } + //Debugger Display = {T[length]} + private string DebuggerDisplay => string.Format("{{{0}[{1}]}}", typeof(T).Name, _length); + /// /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. /// - public ref T DangerousGetPinnableReference() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [EditorBrowsable(EditorBrowsableState.Never)] + internal ref T DangerousGetPinnableReference() { return ref _pointer.Value; } @@ -165,12 +148,26 @@ namespace System /// /// The number of items in the span. /// - public int Length => _length; + public int Length + { + [NonVersionable] + get + { + return _length; + } + } /// /// Returns true if Length is 0. /// - public bool IsEmpty => _length == 0; + public bool IsEmpty + { + [NonVersionable] + get + { + return _length == 0; + } + } /// /// Returns a reference to specified element of the Span. @@ -189,10 +186,9 @@ namespace System return ref Unsafe.Add(ref _pointer.Value, index); } #else -#if CORERT [Intrinsic] -#endif [MethodImpl(MethodImplOptions.AggressiveInlining)] + [NonVersionable] get { if ((uint)index >= (uint)_length) @@ -347,12 +343,13 @@ namespace System /// /// Defines an implicit conversion of an array to a /// - public static implicit operator Span(T[] array) => new Span(array); + public static implicit operator Span(T[] array) => array != null ? new Span(array) : default; /// /// Defines an implicit conversion of a to a /// - public static implicit operator Span(ArraySegment arraySegment) => new Span(arraySegment.Array, arraySegment.Offset, arraySegment.Count); + public static implicit operator Span(ArraySegment arraySegment) + => arraySegment.Array != null ? new Span(arraySegment.Array, arraySegment.Offset, arraySegment.Count) : default; /// /// Defines an implicit conversion of a to a @@ -412,5 +409,47 @@ namespace System /// Returns an empty /// public static Span Empty => default(Span); + + /// Gets an enumerator for this span. + public Enumerator GetEnumerator() => new Enumerator(this); + + /// Enumerates the elements of a . + public ref struct Enumerator + { + /// The span being enumerated. + private readonly Span _span; + /// The next index to yield. + private int _index; + + /// Initialize the enumerator. + /// The span to enumerate. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Enumerator(Span span) + { + _span = span; + _index = -1; + } + + /// Advances the enumerator to the next element of the span. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + int index = _index + 1; + if (index < _span.Length) + { + _index = index; + return true; + } + + return false; + } + + /// Gets the element at the current position of the enumerator. + public ref T Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref _span[_index]; + } + } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/SpanDebugView.cs b/external/corert/src/System.Private.CoreLib/shared/System/SpanDebugView.cs new file mode 100644 index 0000000000..caa12ef9ed --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/SpanDebugView.cs @@ -0,0 +1,28 @@ +// 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.Diagnostics; +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace System +{ + internal sealed class SpanDebugView + { + private readonly T[] _array; + + public SpanDebugView(Span span) + { + _array = span.ToArray(); + } + + public SpanDebugView(ReadOnlySpan span) + { + _array = span.ToArray(); + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public T[] Items => _array; + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/StackOverflowException.cs b/external/corert/src/System.Private.CoreLib/shared/System/StackOverflowException.cs index fb0e88246c..6f954cc75a 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/StackOverflowException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/StackOverflowException.cs @@ -15,24 +15,30 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class StackOverflowException : SystemException { public StackOverflowException() : base(SR.Arg_StackOverflowException) { - HResult = __HResults.COR_E_STACKOVERFLOW; + HResult = HResults.COR_E_STACKOVERFLOW; } public StackOverflowException(String message) : base(message) { - HResult = __HResults.COR_E_STACKOVERFLOW; + HResult = HResults.COR_E_STACKOVERFLOW; } public StackOverflowException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_STACKOVERFLOW; + HResult = HResults.COR_E_STACKOVERFLOW; + } + + internal StackOverflowException(SerializationInfo info, StreamingContext context) : base(info, context) + { } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/String.Searching.cs b/external/corert/src/System.Private.CoreLib/shared/System/String.Searching.cs new file mode 100644 index 0000000000..5aa002b724 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/String.Searching.cs @@ -0,0 +1,611 @@ +// 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.Globalization; +using System.Runtime.InteropServices; + +namespace System +{ + public partial class String + { + public bool Contains(string value) + { + return (IndexOf(value, StringComparison.Ordinal) >= 0); + } + + public bool Contains(string value, StringComparison comparisonType) + { + return (IndexOf(value, comparisonType) >= 0); + } + + public bool Contains(char value) + { + return IndexOf(value) != -1; + } + + public bool Contains(char value, StringComparison comparisonType) + { + return IndexOf(value, comparisonType) != -1; + } + + // Returns the index of the first occurrence of a specified character in the current instance. + // The search starts at startIndex and runs thorough the next count characters. + // + public int IndexOf(char value) + { + return IndexOf(value, 0, this.Length); + } + + public int IndexOf(char value, int startIndex) + { + return IndexOf(value, startIndex, this.Length - startIndex); + } + + public int IndexOf(char value, StringComparison comparisonType) + { + switch (comparisonType) + { + case StringComparison.CurrentCulture: + return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this, value, CompareOptions.None); + + case StringComparison.CurrentCultureIgnoreCase: + return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this, value, CompareOptions.IgnoreCase); + + case StringComparison.InvariantCulture: + return CultureInfo.InvariantCulture.CompareInfo.IndexOf(this, value, CompareOptions.None); + + case StringComparison.InvariantCultureIgnoreCase: + return CultureInfo.InvariantCulture.CompareInfo.IndexOf(this, value, CompareOptions.IgnoreCase); + + case StringComparison.Ordinal: + return CultureInfo.InvariantCulture.CompareInfo.IndexOf(this, value, CompareOptions.Ordinal); + + case StringComparison.OrdinalIgnoreCase: + return CultureInfo.InvariantCulture.CompareInfo.IndexOf(this, value, CompareOptions.OrdinalIgnoreCase); + + default: + throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType)); + } + } + + public unsafe int IndexOf(char value, int startIndex, int count) + { + if (startIndex < 0 || startIndex > Length) + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); + + if (count < 0 || count > Length - startIndex) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count); + + fixed (char* pChars = &_firstChar) + { + char* pCh = pChars + startIndex; + + while (count >= 4) + { + if (*pCh == value) goto ReturnIndex; + if (*(pCh + 1) == value) goto ReturnIndex1; + if (*(pCh + 2) == value) goto ReturnIndex2; + if (*(pCh + 3) == value) goto ReturnIndex3; + + count -= 4; + pCh += 4; + } + + while (count > 0) + { + if (*pCh == value) + goto ReturnIndex; + + count--; + pCh++; + } + + return -1; + + ReturnIndex3: pCh++; + ReturnIndex2: pCh++; + ReturnIndex1: pCh++; + ReturnIndex: + return (int)(pCh - pChars); + } + } + + // Returns the index of the first occurrence of any specified character in the current instance. + // The search starts at startIndex and runs to startIndex + count - 1. + // + public int IndexOfAny(char[] anyOf) + { + return IndexOfAny(anyOf, 0, this.Length); + } + + public int IndexOfAny(char[] anyOf, int startIndex) + { + return IndexOfAny(anyOf, startIndex, this.Length - startIndex); + } + + public int IndexOfAny(char[] anyOf, int startIndex, int count) + { + if (anyOf == null) + throw new ArgumentNullException(nameof(anyOf)); + + if ((uint)startIndex > (uint)Length) + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); + + if ((uint)count > (uint)(Length - startIndex)) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count); + + if (anyOf.Length == 2) + { + // Very common optimization for directory separators (/, \), quotes (", '), brackets, etc + return IndexOfAny(anyOf[0], anyOf[1], startIndex, count); + } + else if (anyOf.Length == 3) + { + return IndexOfAny(anyOf[0], anyOf[1], anyOf[2], startIndex, count); + } + else if (anyOf.Length > 3) + { + return IndexOfCharArray(anyOf, startIndex, count); + } + else if (anyOf.Length == 1) + { + return IndexOf(anyOf[0], startIndex, count); + } + else // anyOf.Length == 0 + { + return -1; + } + } + + private unsafe int IndexOfAny(char value1, char value2, int startIndex, int count) + { + fixed (char* pChars = &_firstChar) + { + char* pCh = pChars + startIndex; + + while (count > 0) + { + char c = *pCh; + + if (c == value1 || c == value2) + return (int)(pCh - pChars); + + // Possibly reads outside of count and can include null terminator + // Handled in the return logic + c = *(pCh + 1); + + if (c == value1 || c == value2) + return (count == 1 ? -1 : (int)(pCh - pChars) + 1); + + pCh += 2; + count -= 2; + } + + return -1; + } + } + + private unsafe int IndexOfAny(char value1, char value2, char value3, int startIndex, int count) + { + fixed (char* pChars = &_firstChar) + { + char* pCh = pChars + startIndex; + + while (count > 0) + { + char c = *pCh; + + if (c == value1 || c == value2 || c == value3) + return (int)(pCh - pChars); + + pCh++; + count--; + } + + return -1; + } + } + + private unsafe int IndexOfCharArray(char[] anyOf, int startIndex, int count) + { + // use probabilistic map, see InitializeProbabilisticMap + ProbabilisticMap map = default(ProbabilisticMap); + uint* charMap = (uint*)↦ + + InitializeProbabilisticMap(charMap, anyOf); + + fixed (char* pChars = &_firstChar) + { + char* pCh = pChars + startIndex; + + while (count > 0) + { + int thisChar = *pCh; + + if (IsCharBitSet(charMap, (byte)thisChar) && + IsCharBitSet(charMap, (byte)(thisChar >> 8)) && + ArrayContains((char)thisChar, anyOf)) + { + return (int)(pCh - pChars); + } + + count--; + pCh++; + } + + return -1; + } + } + + private const int PROBABILISTICMAP_BLOCK_INDEX_MASK = 0x7; + private const int PROBABILISTICMAP_BLOCK_INDEX_SHIFT = 0x3; + private const int PROBABILISTICMAP_SIZE = 0x8; + + // A probabilistic map is an optimization that is used in IndexOfAny/ + // LastIndexOfAny methods. The idea is to create a bit map of the characters we + // are searching for and use this map as a "cheap" check to decide if the + // current character in the string exists in the array of input characters. + // There are 256 bits in the map, with each character mapped to 2 bits. Every + // character is divided into 2 bytes, and then every byte is mapped to 1 bit. + // The character map is an array of 8 integers acting as map blocks. The 3 lsb + // in each byte in the character is used to index into this map to get the + // right block, the value of the remaining 5 msb are used as the bit position + // inside this block. + private static unsafe void InitializeProbabilisticMap(uint* charMap, ReadOnlySpan anyOf) + { + bool hasAscii = false; + uint* charMapLocal = charMap; // https://github.com/dotnet/coreclr/issues/14264 + + for (int i = 0; i < anyOf.Length; ++i) + { + int c = anyOf[i]; + + // Map low bit + SetCharBit(charMapLocal, (byte)c); + + // Map high bit + c >>= 8; + + if (c == 0) + { + hasAscii = true; + } + else + { + SetCharBit(charMapLocal, (byte)c); + } + } + + if (hasAscii) + { + // Common to search for ASCII symbols. Just set the high value once. + charMapLocal[0] |= 1u; + } + } + + private static bool ArrayContains(char searchChar, char[] anyOf) + { + for (int i = 0; i < anyOf.Length; i++) + { + if (anyOf[i] == searchChar) + return true; + } + + return false; + } + + private unsafe static bool IsCharBitSet(uint* charMap, byte value) + { + return (charMap[value & PROBABILISTICMAP_BLOCK_INDEX_MASK] & (1u << (value >> PROBABILISTICMAP_BLOCK_INDEX_SHIFT))) != 0; + } + + private unsafe static void SetCharBit(uint* charMap, byte value) + { + charMap[value & PROBABILISTICMAP_BLOCK_INDEX_MASK] |= 1u << (value >> PROBABILISTICMAP_BLOCK_INDEX_SHIFT); + } + + public int IndexOf(String value) + { + return IndexOf(value, StringComparison.CurrentCulture); + } + + public int IndexOf(String value, int startIndex) + { + return IndexOf(value, startIndex, StringComparison.CurrentCulture); + } + + public int IndexOf(String value, int startIndex, int count) + { + if (startIndex < 0 || startIndex > this.Length) + { + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); + } + + if (count < 0 || count > this.Length - startIndex) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count); + } + + return IndexOf(value, startIndex, count, StringComparison.CurrentCulture); + } + + public int IndexOf(String value, StringComparison comparisonType) + { + return IndexOf(value, 0, this.Length, comparisonType); + } + + public int IndexOf(String value, int startIndex, StringComparison comparisonType) + { + return IndexOf(value, startIndex, this.Length - startIndex, comparisonType); + } + + public int IndexOf(String value, int startIndex, int count, StringComparison comparisonType) + { + // Validate inputs + if (value == null) + throw new ArgumentNullException(nameof(value)); + + if (startIndex < 0 || startIndex > this.Length) + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); + + if (count < 0 || startIndex > this.Length - count) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count); + + switch (comparisonType) + { + case StringComparison.CurrentCulture: + return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.None); + + case StringComparison.CurrentCultureIgnoreCase: + return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.IgnoreCase); + + case StringComparison.InvariantCulture: + return CultureInfo.InvariantCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.None); + + case StringComparison.InvariantCultureIgnoreCase: + return CultureInfo.InvariantCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.IgnoreCase); + + case StringComparison.Ordinal: + return CultureInfo.InvariantCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.Ordinal); + + case StringComparison.OrdinalIgnoreCase: + return TextInfo.IndexOfStringOrdinalIgnoreCase(this, value, startIndex, count); + + default: + throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType)); + } + } + + // Returns the index of the last occurrence of a specified character in the current instance. + // The search starts at startIndex and runs backwards to startIndex - count + 1. + // The character at position startIndex is included in the search. startIndex is the larger + // index within the string. + // + public int LastIndexOf(char value) + { + return LastIndexOf(value, this.Length - 1, this.Length); + } + + public int LastIndexOf(char value, int startIndex) + { + return LastIndexOf(value, startIndex, startIndex + 1); + } + + public unsafe int LastIndexOf(char value, int startIndex, int count) + { + if (Length == 0) + return -1; + + if (startIndex < 0 || startIndex >= Length) + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); + + if (count < 0 || count - 1 > startIndex) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count); + + fixed (char* pChars = &_firstChar) + { + char* pCh = pChars + startIndex; + + //We search [startIndex..EndIndex] + while (count >= 4) + { + if (*pCh == value) goto ReturnIndex; + if (*(pCh - 1) == value) goto ReturnIndex1; + if (*(pCh - 2) == value) goto ReturnIndex2; + if (*(pCh - 3) == value) goto ReturnIndex3; + + count -= 4; + pCh -= 4; + } + + while (count > 0) + { + if (*pCh == value) + goto ReturnIndex; + + count--; + pCh--; + } + + return -1; + + ReturnIndex3: pCh--; + ReturnIndex2: pCh--; + ReturnIndex1: pCh--; + ReturnIndex: + return (int)(pCh - pChars); + } + } + + // Returns the index of the last occurrence of any specified character in the current instance. + // The search starts at startIndex and runs backwards to startIndex - count + 1. + // The character at position startIndex is included in the search. startIndex is the larger + // index within the string. + // + public int LastIndexOfAny(char[] anyOf) + { + return LastIndexOfAny(anyOf, this.Length - 1, this.Length); + } + + public int LastIndexOfAny(char[] anyOf, int startIndex) + { + return LastIndexOfAny(anyOf, startIndex, startIndex + 1); + } + + public unsafe int LastIndexOfAny(char[] anyOf, int startIndex, int count) + { + if (anyOf == null) + throw new ArgumentNullException(nameof(anyOf)); + + if (Length == 0) + return -1; + + if ((uint)startIndex >= (uint)Length) + { + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); + } + + if ((count < 0) || ((count - 1) > startIndex)) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count); + } + + if (anyOf.Length > 1) + { + return LastIndexOfCharArray(anyOf, startIndex, count); + } + else if (anyOf.Length == 1) + { + return LastIndexOf(anyOf[0], startIndex, count); + } + else // anyOf.Length == 0 + { + return -1; + } + } + + private unsafe int LastIndexOfCharArray(char[] anyOf, int startIndex, int count) + { + // use probabilistic map, see InitializeProbabilisticMap + ProbabilisticMap map = default(ProbabilisticMap); + uint* charMap = (uint*)↦ + + InitializeProbabilisticMap(charMap, anyOf); + + fixed (char* pChars = &_firstChar) + { + char* pCh = pChars + startIndex; + + while (count > 0) + { + int thisChar = *pCh; + + if (IsCharBitSet(charMap, (byte)thisChar) && + IsCharBitSet(charMap, (byte)(thisChar >> 8)) && + ArrayContains((char)thisChar, anyOf)) + { + return (int)(pCh - pChars); + } + + count--; + pCh--; + } + + return -1; + } + } + + // Returns the index of the last occurrence of any character in value in the current instance. + // The search starts at startIndex and runs backwards to startIndex - count + 1. + // The character at position startIndex is included in the search. startIndex is the larger + // index within the string. + // + public int LastIndexOf(String value) + { + return LastIndexOf(value, this.Length - 1, this.Length, StringComparison.CurrentCulture); + } + + public int LastIndexOf(String value, int startIndex) + { + return LastIndexOf(value, startIndex, startIndex + 1, StringComparison.CurrentCulture); + } + + public int LastIndexOf(String value, int startIndex, int count) + { + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count); + } + + return LastIndexOf(value, startIndex, count, StringComparison.CurrentCulture); + } + + public int LastIndexOf(String value, StringComparison comparisonType) + { + return LastIndexOf(value, this.Length - 1, this.Length, comparisonType); + } + + public int LastIndexOf(String value, int startIndex, StringComparison comparisonType) + { + return LastIndexOf(value, startIndex, startIndex + 1, comparisonType); + } + + public int LastIndexOf(String value, int startIndex, int count, StringComparison comparisonType) + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + // Special case for 0 length input strings + if (this.Length == 0 && (startIndex == -1 || startIndex == 0)) + return (value.Length == 0) ? 0 : -1; + + // Now after handling empty strings, make sure we're not out of range + if (startIndex < 0 || startIndex > this.Length) + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); + + // Make sure that we allow startIndex == this.Length + if (startIndex == this.Length) + { + startIndex--; + if (count > 0) + count--; + } + + // 2nd half of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0. + if (count < 0 || startIndex - count + 1 < 0) + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count); + + // If we are looking for nothing, just return startIndex + if (value.Length == 0) + return startIndex; + + switch (comparisonType) + { + case StringComparison.CurrentCulture: + return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(this, value, startIndex, count, CompareOptions.None); + + case StringComparison.CurrentCultureIgnoreCase: + return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(this, value, startIndex, count, CompareOptions.IgnoreCase); + + case StringComparison.InvariantCulture: + return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf(this, value, startIndex, count, CompareOptions.None); + + case StringComparison.InvariantCultureIgnoreCase: + return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf(this, value, startIndex, count, CompareOptions.IgnoreCase); + + case StringComparison.Ordinal: + return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf(this, value, startIndex, count, CompareOptions.Ordinal); + + case StringComparison.OrdinalIgnoreCase: + return TextInfo.LastIndexOfStringOrdinalIgnoreCase(this, value, startIndex, count); + + default: + throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType)); + } + } + + [StructLayout(LayoutKind.Explicit, Size = PROBABILISTICMAP_SIZE * sizeof(uint))] + private struct ProbabilisticMap { } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/StringComparer.cs b/external/corert/src/System.Private.CoreLib/shared/System/StringComparer.cs index b327e770d5..73c013599d 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/StringComparer.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/StringComparer.cs @@ -5,23 +5,23 @@ using System.Collections; using System.Collections.Generic; using System.Globalization; -using System.Diagnostics.Contracts; +using System.Runtime.Serialization; namespace System { [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public abstract class StringComparer : IComparer, IEqualityComparer, IComparer, IEqualityComparer { private static readonly CultureAwareComparer s_invariantCulture = new CultureAwareComparer(CultureInfo.InvariantCulture, false); private static readonly CultureAwareComparer s_invariantCultureIgnoreCase = new CultureAwareComparer(CultureInfo.InvariantCulture, true); - private static readonly OrdinalComparer s_ordinal = new OrdinalComparer(); + private static readonly OrdinalCaseSensitiveComparer s_ordinal = new OrdinalCaseSensitiveComparer(); private static readonly OrdinalIgnoreCaseComparer s_ordinalIgnoreCase = new OrdinalIgnoreCaseComparer(); public static StringComparer InvariantCulture { get { - Contract.Ensures(Contract.Result() != null); return s_invariantCulture; } } @@ -30,7 +30,6 @@ namespace System { get { - Contract.Ensures(Contract.Result() != null); return s_invariantCultureIgnoreCase; } } @@ -39,7 +38,6 @@ namespace System { get { - Contract.Ensures(Contract.Result() != null); return new CultureAwareComparer(CultureInfo.CurrentCulture, false); } } @@ -48,7 +46,6 @@ namespace System { get { - Contract.Ensures(Contract.Result() != null); return new CultureAwareComparer(CultureInfo.CurrentCulture, true); } } @@ -57,7 +54,6 @@ namespace System { get { - Contract.Ensures(Contract.Result() != null); return s_ordinal; } } @@ -66,7 +62,6 @@ namespace System { get { - Contract.Ensures(Contract.Result() != null); return s_ordinalIgnoreCase; } } @@ -99,8 +94,6 @@ namespace System { throw new ArgumentNullException(nameof(culture)); } - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); return new CultureAwareComparer(culture, ignoreCase); } @@ -154,7 +147,6 @@ namespace System { throw new ArgumentNullException(nameof(obj)); } - Contract.EndContractBlock(); string s = obj as string; if (s != null) @@ -170,30 +162,33 @@ namespace System } [Serializable] - internal sealed class CultureAwareComparer : StringComparer + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class CultureAwareComparer : StringComparer { - private readonly CompareInfo _compareInfo; - private readonly CompareOptions _options; + private readonly CompareInfo _compareInfo; // Do not rename (binary serialization) + private readonly bool _ignoreCase; // Do not rename (binary serialization) internal CultureAwareComparer(CultureInfo culture, bool ignoreCase) { _compareInfo = culture.CompareInfo; - _options = ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None; + _ignoreCase = ignoreCase; } + private CompareOptions Options => _ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None; + public override int Compare(string x, string y) { if (object.ReferenceEquals(x, y)) return 0; if (x == null) return -1; if (y == null) return 1; - return _compareInfo.Compare(x, y, _options); + return _compareInfo.Compare(x, y, Options); } public override bool Equals(string x, string y) { if (object.ReferenceEquals(x, y)) return true; if (x == null || y == null) return false; - return _compareInfo.Compare(x, y, _options) == 0; + return _compareInfo.Compare(x, y, Options) == 0; } public override int GetHashCode(string obj) @@ -202,7 +197,7 @@ namespace System { throw new ArgumentNullException(nameof(obj)); } - return _compareInfo.GetHashCodeOfString(obj, _options); + return _compareInfo.GetHashCodeOfString(obj, Options); } // Equals method for the comparer itself. @@ -211,20 +206,103 @@ namespace System CultureAwareComparer comparer = obj as CultureAwareComparer; return comparer != null && - _options == comparer._options && + _ignoreCase == comparer._ignoreCase && _compareInfo.Equals(comparer._compareInfo); } public override int GetHashCode() { int hashCode = _compareInfo.GetHashCode(); - return _options == CompareOptions.None ? hashCode : ~hashCode; + return _ignoreCase ? ~hashCode : hashCode; } } [Serializable] - internal sealed class OrdinalComparer : StringComparer + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class OrdinalComparer : StringComparer { + private readonly bool _ignoreCase; // Do not rename (binary serialization) + + internal OrdinalComparer(bool ignoreCase) + { + _ignoreCase = ignoreCase; + } + + public override int Compare(string x, string y) + { + if (ReferenceEquals(x, y)) + return 0; + if (x == null) + return -1; + if (y == null) + return 1; + + if (_ignoreCase) + { + return string.Compare(x, y, StringComparison.OrdinalIgnoreCase); + } + + return string.CompareOrdinal(x, y); + } + + public override bool Equals(string x, string y) + { + if (ReferenceEquals(x, y)) + return true; + if (x == null || y == null) + return false; + + if (_ignoreCase) + { + if (x.Length != y.Length) + { + return false; + } + return (string.Compare(x, y, StringComparison.OrdinalIgnoreCase) == 0); + } + return x.Equals(y); + } + + public override int GetHashCode(string obj) + { + if (obj == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.obj); + } + + if (_ignoreCase) + { + return TextInfo.GetHashCodeOrdinalIgnoreCase(obj); + } + + return obj.GetHashCode(); + } + + // Equals method for the comparer itself. + public override bool Equals(object obj) + { + OrdinalComparer comparer = obj as OrdinalComparer; + if (comparer == null) + { + return false; + } + return (this._ignoreCase == comparer._ignoreCase); + } + + public override int GetHashCode() + { + int hashCode = nameof(OrdinalComparer).GetHashCode(); + return _ignoreCase ? (~hashCode) : hashCode; + } + } + + [Serializable] + internal sealed class OrdinalCaseSensitiveComparer : OrdinalComparer, ISerializable + { + public OrdinalCaseSensitiveComparer() : base(false) + { + } + public override int Compare(string x, string y) => string.CompareOrdinal(x, y); public override bool Equals(string x, string y) => string.Equals(x, y); @@ -233,23 +311,25 @@ namespace System { if (obj == null) { -#if CORECLR ThrowHelper.ThrowArgumentNullException(ExceptionArgument.obj); -#else - throw new ArgumentNullException(nameof(obj)); -#endif } return obj.GetHashCode(); } - // Equals/GetHashCode methods for the comparer itself. - public override bool Equals(object obj) => obj is OrdinalComparer; - public override int GetHashCode() => nameof(OrdinalComparer).GetHashCode(); + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.SetType(typeof(OrdinalComparer)); + info.AddValue("_ignoreCase", false); + } } [Serializable] - internal sealed class OrdinalIgnoreCaseComparer : StringComparer + internal sealed class OrdinalIgnoreCaseComparer : OrdinalComparer, ISerializable { + public OrdinalIgnoreCaseComparer() : base(true) + { + } + public override int Compare(string x, string y) => string.Compare(x, y, StringComparison.OrdinalIgnoreCase); public override bool Equals(string x, string y) => string.Equals(x, y, StringComparison.OrdinalIgnoreCase); @@ -258,17 +338,15 @@ namespace System { if (obj == null) { -#if CORECLR ThrowHelper.ThrowArgumentNullException(ExceptionArgument.obj); -#else - throw new ArgumentNullException(nameof(obj)); -#endif } return TextInfo.GetHashCodeOrdinalIgnoreCase(obj); } - // Equals/GetHashCode methods for the comparer itself. - public override bool Equals(object obj) => obj is OrdinalIgnoreCaseComparer; - public override int GetHashCode() => nameof(OrdinalIgnoreCaseComparer).GetHashCode(); + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.SetType(typeof(OrdinalComparer)); + info.AddValue("_ignoreCase", true); + } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/StringSpanHelpers.cs b/external/corert/src/System.Private.CoreLib/shared/System/StringSpanHelpers.cs new file mode 100644 index 0000000000..1c127b19d0 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/StringSpanHelpers.cs @@ -0,0 +1,129 @@ +// 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.Globalization; + +namespace System +{ + /// Helpers for string-like operations on spans of chars. + internal static class StringSpanHelpers + { + // TODO https://github.com/dotnet/corefx/issues/21395: Provide public, efficient implementations + + public static bool Equals(this ReadOnlySpan left, ReadOnlySpan right, StringComparison comparisonType) => + comparisonType == StringComparison.Ordinal ? Equals(left, right) : + comparisonType == StringComparison.OrdinalIgnoreCase ? EqualsOrdinalIgnoreCase(left, right) : + throw new ArgumentOutOfRangeException(nameof(comparisonType)); + + public static bool Equals(this ReadOnlySpan left, string right) => + Equals(left, right.AsReadOnlySpan()); + + public static bool Equals(this ReadOnlySpan left, ReadOnlySpan right) + { + if (left.Length != right.Length) + { + return false; + } + + for (int i = 0; i < left.Length; i++) + { + if (left[i] != right[i]) + { + return false; + } + } + + return true; + } + + private static bool EqualsOrdinalIgnoreCase(this ReadOnlySpan left, ReadOnlySpan right) + { + if (left.Length != right.Length) + { + return false; + } + + for (int i = 0; i < left.Length; i++) + { + char x = left[i], y = right[i]; + if (x != y && + TextInfo.ToUpperAsciiInvariant(x) != TextInfo.ToUpperAsciiInvariant(y)) + { + return false; + } + } + + return true; + } + + public static ReadOnlySpan Trim(this ReadOnlySpan source) + { + int startIndex = 0, endIndex = source.Length - 1; + + while (startIndex <= endIndex && char.IsWhiteSpace(source[startIndex])) + { + startIndex++; + } + + while (endIndex >= startIndex && char.IsWhiteSpace(source[endIndex])) + { + endIndex--; + } + + return source.Slice(startIndex, endIndex - startIndex + 1); + } + + public static int IndexOf(this ReadOnlySpan source, char value) => + IndexOf(source, value, 0); + + public static int IndexOf(this ReadOnlySpan source, char value, int startIndex) + { + for (int i = startIndex; i < source.Length; i++) + { + if (source[i] == value) + { + return i; + } + } + + return -1; + } + + public static bool Contains(this ReadOnlySpan source, char value) + { + for (int i = 0; i < source.Length; i++) + { + if (source[i] == value) + { + return true; + } + } + + return false; + } + + public static ReadOnlySpan Remove(this ReadOnlySpan source, int startIndex, int count) + { + if (startIndex < 0) throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex); + if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NegativeCount); + if (count > source.Length - startIndex) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_IndexCount); + + if (count == 0) + { + return source; + } + + int newLength = source.Length - count; + if (newLength == 0) + { + return ReadOnlySpan.Empty; + } + + Span result = new char[newLength]; + source.Slice(0, startIndex).CopyTo(result); + source.Slice(startIndex + count).CopyTo(result.Slice(startIndex)); + return result; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/SystemException.cs b/external/corert/src/System.Private.CoreLib/shared/System/SystemException.cs index d5bcde7efc..b7e8e42175 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/SystemException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/SystemException.cs @@ -6,29 +6,30 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class SystemException : Exception { public SystemException() : base(SR.Arg_SystemException) { - HResult = __HResults.COR_E_SYSTEM; + HResult = HResults.COR_E_SYSTEM; } public SystemException(String message) : base(message) { - HResult = __HResults.COR_E_SYSTEM; + HResult = HResults.COR_E_SYSTEM; } public SystemException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_SYSTEM; + HResult = HResults.COR_E_SYSTEM; } protected SystemException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/ASCIIEncoding.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/ASCIIEncoding.cs index 628ec9a3fa..e89943a192 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Text/ASCIIEncoding.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/ASCIIEncoding.cs @@ -4,8 +4,7 @@ using System; using System.Diagnostics; -using System.Diagnostics.Contracts; -using System.Runtime.Serialization; +using System.Runtime.InteropServices; namespace System.Text { @@ -67,7 +66,6 @@ namespace System.Text if (chars.Length - index < count) throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); // If no input, return 0, avoid fixed empty array problem if (count == 0) @@ -88,7 +86,6 @@ namespace System.Text // Validate input if (chars==null) throw new ArgumentNullException("chars"); - Contract.EndContractBlock(); fixed (char* pChars = chars) return GetByteCount(pChars, chars.Length, null); @@ -107,7 +104,6 @@ namespace System.Text if (count < 0) throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Call it with empty encoder return GetByteCount(chars, count, null); @@ -132,15 +128,10 @@ namespace System.Text if (byteIndex < 0 || byteIndex > bytes.Length) throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); int byteCount = bytes.Length - byteIndex; - // Fixed doesn't like empty byte arrays - if (bytes.Length == 0) - bytes = new byte[1]; - - fixed (char* pChars = chars) fixed (byte* pBytes = &bytes[0]) + fixed (char* pChars = chars) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null); } @@ -173,20 +164,15 @@ namespace System.Text if (byteIndex < 0 || byteIndex > bytes.Length) throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); - // If nothing to encode return 0, avoid fixed problem + // If nothing to encode return 0 if (charCount == 0) return 0; // Just call pointer version int byteCount = bytes.Length - byteIndex; - // Fixed doesn't like empty byte arrays - if (bytes.Length == 0) - bytes = new byte[1]; - - fixed (char* pChars = chars) fixed (byte* pBytes = &bytes[0]) + fixed (char* pChars = chars) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) // Remember that byteCount is # to decode, not size of array. return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null); } @@ -204,7 +190,6 @@ namespace System.Text if (charCount < 0 || byteCount < 0) throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); return GetBytes(chars, charCount, bytes, byteCount, null); } @@ -228,7 +213,6 @@ namespace System.Text if (bytes.Length - index < count) throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); // If no input just return 0, fixed doesn't like 0 length arrays if (count == 0) @@ -252,7 +236,6 @@ namespace System.Text if (count < 0) throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); return GetCharCount(bytes, count, null); } @@ -277,7 +260,6 @@ namespace System.Text if (charIndex < 0 || charIndex > chars.Length) throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); // If no input, return 0 & avoid fixed problem if (byteCount == 0) @@ -286,11 +268,7 @@ namespace System.Text // Just call pointer version int charCount = chars.Length - charIndex; - // Fixed doesn't like empty char arrays - if (chars.Length == 0) - chars = new char[1]; - - fixed (byte* pBytes = bytes) fixed (char* pChars = &chars[0]) + fixed (byte* pBytes = bytes) fixed (char* pChars = &MemoryMarshal.GetReference((Span)chars)) // Remember that charCount is # to decode, not size of array return GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, null); } @@ -308,7 +286,6 @@ namespace System.Text if (charCount < 0 || byteCount < 0) throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); return GetChars(bytes, byteCount, chars, charCount, null); } @@ -333,7 +310,6 @@ namespace System.Text if (bytes.Length - byteIndex < byteCount) throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); // Avoid problems with empty input buffer if (byteCount == 0) return String.Empty; @@ -371,7 +347,7 @@ namespace System.Text if (encoder != null) { - charLeftOver = encoder.charLeftOver; + charLeftOver = encoder._charLeftOver; Debug.Assert(charLeftOver == 0 || Char.IsHighSurrogate(charLeftOver), "[ASCIIEncoding.GetByteCount]leftover character should be high surrogate"); @@ -382,7 +358,7 @@ namespace System.Text { // We always need the fallback buffer in get bytes so we can flush any remaining ones if necessary fallbackBuffer = encoder.FallbackBuffer; - if (fallbackBuffer.Remaining > 0 && encoder.m_throwOnOverflow) + if (fallbackBuffer.Remaining > 0 && encoder._throwOnOverflow) throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, encoder.Fallback.GetType())); // Set our internal fallback interesting things. @@ -390,7 +366,7 @@ namespace System.Text } // Verify that we have no fallbackbuffer, for ASCII its always empty, so just assert - Debug.Assert(!encoder.m_throwOnOverflow || !encoder.InternalHasFallbackBuffer || + Debug.Assert(!encoder._throwOnOverflow || !encoder.InternalHasFallbackBuffer || encoder.FallbackBuffer.Remaining == 0, "[ASCIICodePageEncoding.GetByteCount]Expected empty fallback buffer"); } @@ -511,7 +487,7 @@ namespace System.Text if (encoder != null) { - charLeftOver = encoder.charLeftOver; + charLeftOver = encoder._charLeftOver; fallback = encoder.Fallback as EncoderReplacementFallback; // We mustn't have left over fallback data when counting @@ -519,7 +495,7 @@ namespace System.Text { // We always need the fallback buffer in get bytes so we can flush any remaining ones if necessary fallbackBuffer = encoder.FallbackBuffer; - if (fallbackBuffer.Remaining > 0 && encoder.m_throwOnOverflow) + if (fallbackBuffer.Remaining > 0 && encoder._throwOnOverflow) throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, encoder.Fallback.GetType())); // Set our internal fallback interesting things. @@ -530,7 +506,7 @@ namespace System.Text "[ASCIIEncoding.GetBytes]leftover character should be high surrogate"); // Verify that we have no fallbackbuffer, for ASCII its always empty, so just assert - Debug.Assert(!encoder.m_throwOnOverflow || !encoder.InternalHasFallbackBuffer || + Debug.Assert(!encoder._throwOnOverflow || !encoder.InternalHasFallbackBuffer || encoder.FallbackBuffer.Remaining == 0, "[ASCIICodePageEncoding.GetBytes]Expected empty fallback buffer"); } @@ -588,8 +564,8 @@ namespace System.Text // Clear encoder if (encoder != null) { - encoder.charLeftOver = (char)0; - encoder.m_charsUsed = (int)(chars - charStart); + encoder._charLeftOver = (char)0; + encoder._charsUsed = (int)(chars - charStart); } return (int)(bytes - byteStart); @@ -686,14 +662,14 @@ namespace System.Text // Fallback stuck it in encoder if necessary, but we have to clear MustFlush cases if (fallbackBuffer != null && !fallbackBuffer.bUsedEncoder) // Clear it in case of MustFlush - encoder.charLeftOver = (char)0; + encoder._charLeftOver = (char)0; // Set our chars used count - encoder.m_charsUsed = (int)(chars - charStart); + encoder._charsUsed = (int)(chars - charStart); } Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0 || - (encoder != null && !encoder.m_throwOnOverflow), + (encoder != null && !encoder._throwOnOverflow), "[ASCIIEncoding.GetBytes]Expected Empty fallback buffer at end"); return (int)(bytes - byteStart); @@ -714,7 +690,7 @@ namespace System.Text else { fallback = decoder.Fallback as DecoderReplacementFallback; - Debug.Assert(!decoder.m_throwOnOverflow || !decoder.InternalHasFallbackBuffer || + Debug.Assert(!decoder._throwOnOverflow || !decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0, "[ASCIICodePageEncoding.GetCharCount]Expected empty fallback buffer"); } @@ -797,7 +773,7 @@ namespace System.Text else { fallback = decoder.Fallback as DecoderReplacementFallback; - Debug.Assert(!decoder.m_throwOnOverflow || !decoder.InternalHasFallbackBuffer || + Debug.Assert(!decoder._throwOnOverflow || !decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0, "[ASCIICodePageEncoding.GetChars]Expected empty fallback buffer"); } @@ -830,7 +806,7 @@ namespace System.Text // bytes & chars used are the same if (decoder != null) - decoder.m_bytesUsed = (int)(bytes - byteStart); + decoder._bytesUsed = (int)(bytes - byteStart); return (int)(chars - charStart); } @@ -896,7 +872,7 @@ namespace System.Text // Might have had decoder fallback stuff. if (decoder != null) - decoder.m_bytesUsed = (int)(bytes - byteStart); + decoder._bytesUsed = (int)(bytes - byteStart); // Expect Empty fallback buffer for GetChars Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0, @@ -911,7 +887,6 @@ namespace System.Text if (charCount < 0) throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Characters would be # of characters + 1 in case high surrogate is ? * max fallback long byteCount = (long)charCount + 1; @@ -932,7 +907,6 @@ namespace System.Text if (byteCount < 0) throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Just return length, SBCS stay the same length because they don't map to surrogate long charCount = (long)byteCount; diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/Decoder.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/Decoder.cs index aefe1f64bd..b827648fc1 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Text/Decoder.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/Decoder.cs @@ -2,11 +2,10 @@ // 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.Runtime.Serialization; using System.Text; using System; using System.Diagnostics; -using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; namespace System.Text { @@ -23,15 +22,9 @@ namespace System.Text // public abstract class Decoder { - internal DecoderFallback m_fallback = null; + internal DecoderFallback _fallback = null; - [NonSerialized] - internal DecoderFallbackBuffer m_fallbackBuffer = null; - - internal void SerializeDecoder(SerializationInfo info) - { - info.AddValue("m_fallback", this.m_fallback); - } + internal DecoderFallbackBuffer _fallbackBuffer = null; protected Decoder() { @@ -42,22 +35,21 @@ namespace System.Text { get { - return m_fallback; + return _fallback; } set { if (value == null) throw new ArgumentNullException(nameof(value)); - Contract.EndContractBlock(); // Can't change fallback if buffer is wrong - if (m_fallbackBuffer != null && m_fallbackBuffer.Remaining > 0) + if (_fallbackBuffer != null && _fallbackBuffer.Remaining > 0) throw new ArgumentException( SR.Argument_FallbackBufferNotEmpty, nameof(value)); - m_fallback = value; - m_fallbackBuffer = null; + _fallback = value; + _fallbackBuffer = null; } } @@ -67,15 +59,15 @@ namespace System.Text { get { - if (m_fallbackBuffer == null) + if (_fallbackBuffer == null) { - if (m_fallback != null) - m_fallbackBuffer = m_fallback.CreateFallbackBuffer(); + if (_fallback != null) + _fallbackBuffer = _fallback.CreateFallbackBuffer(); else - m_fallbackBuffer = DecoderFallback.ReplacementFallback.CreateFallbackBuffer(); + _fallbackBuffer = DecoderFallback.ReplacementFallback.CreateFallbackBuffer(); } - return m_fallbackBuffer; + return _fallbackBuffer; } } @@ -83,7 +75,7 @@ namespace System.Text { get { - return m_fallbackBuffer != null; + return _fallbackBuffer != null; } } @@ -101,8 +93,7 @@ namespace System.Text byte[] byteTemp = Array.Empty(); char[] charTemp = new char[GetCharCount(byteTemp, 0, 0, true)]; GetChars(byteTemp, 0, 0, charTemp, 0, true); - if (m_fallbackBuffer != null) - m_fallbackBuffer.Reset(); + _fallbackBuffer?.Reset(); } // Returns the number of characters the next call to GetChars will @@ -131,7 +122,6 @@ namespace System.Text if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); byte[] arrbyte = new byte[count]; int index; @@ -142,6 +132,14 @@ namespace System.Text return GetCharCount(arrbyte, 0, count); } + public virtual unsafe int GetCharCount(ReadOnlySpan bytes, bool flush) + { + fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + { + return GetCharCount(bytesPtr, bytes.Length, flush); + } + } + // Decodes a range of bytes in a byte array into a range of characters // in a character array. The method decodes byteCount bytes from // bytes starting at index byteIndex, storing the resulting @@ -195,7 +193,6 @@ namespace System.Text if (byteCount < 0 || charCount < 0) throw new ArgumentOutOfRangeException((byteCount < 0 ? nameof(byteCount) : nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Get the byte array to convert byte[] arrByte = new byte[byteCount]; @@ -228,6 +225,15 @@ namespace System.Text return charCount; } + public virtual unsafe int GetChars(ReadOnlySpan bytes, Span chars, bool flush) + { + fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) + { + return GetChars(bytesPtr, bytes.Length, charsPtr, chars.Length, flush); + } + } + // This method is used when the output buffer might not be large enough. // It will decode until it runs out of bytes, and then it will return // true if it the entire input was converted. In either case it @@ -265,7 +271,6 @@ namespace System.Text if (chars.Length - charIndex < charCount) throw new ArgumentOutOfRangeException(nameof(chars), SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); bytesUsed = byteCount; @@ -276,7 +281,7 @@ namespace System.Text { charsUsed = GetChars(bytes, byteIndex, bytesUsed, chars, charIndex, flush); completed = (bytesUsed == byteCount && - (m_fallbackBuffer == null || m_fallbackBuffer.Remaining == 0)); + (_fallbackBuffer == null || _fallbackBuffer.Remaining == 0)); return; } @@ -310,7 +315,6 @@ namespace System.Text if (byteCount < 0 || charCount < 0) throw new ArgumentOutOfRangeException((byteCount < 0 ? nameof(byteCount) : nameof(charCount)), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Get ready to do it bytesUsed = byteCount; @@ -322,7 +326,7 @@ namespace System.Text { charsUsed = GetChars(bytes, bytesUsed, chars, charCount, flush); completed = (bytesUsed == byteCount && - (m_fallbackBuffer == null || m_fallbackBuffer.Remaining == 0)); + (_fallbackBuffer == null || _fallbackBuffer.Remaining == 0)); return; } @@ -334,5 +338,14 @@ namespace System.Text // Oops, we didn't have anything, we'll have to throw an overflow throw new ArgumentException(SR.Argument_ConversionOverflow); } + + public virtual unsafe void Convert(ReadOnlySpan bytes, Span chars, bool flush, out int bytesUsed, out int charsUsed, out bool completed) + { + fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) + { + Convert(bytesPtr, bytes.Length, charsPtr, chars.Length, flush, out bytesUsed, out charsUsed, out completed); + } + } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/DecoderBestFitFallback.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/DecoderBestFitFallback.cs new file mode 100644 index 0000000000..30c817c91a --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/DecoderBestFitFallback.cs @@ -0,0 +1,242 @@ +// 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. + +// +// This is used internally to create best fit behavior as per the original windows best fit behavior. +// + +using System.Diagnostics; +using System.Threading; + +namespace System.Text +{ + internal sealed class InternalDecoderBestFitFallback : DecoderFallback + { + // Our variables + internal Encoding _encoding = null; + internal char[] _arrayBestFit = null; + internal char _cReplacement = '?'; + + internal InternalDecoderBestFitFallback(Encoding encoding) + { + // Need to load our replacement characters table. + _encoding = encoding; + } + + public override DecoderFallbackBuffer CreateFallbackBuffer() + { + return new InternalDecoderBestFitFallbackBuffer(this); + } + + // Maximum number of characters that this instance of this fallback could return + public override int MaxCharCount + { + get + { + return 1; + } + } + + public override bool Equals(Object value) + { + InternalDecoderBestFitFallback that = value as InternalDecoderBestFitFallback; + if (that != null) + { + return (_encoding.CodePage == that._encoding.CodePage); + } + return (false); + } + + public override int GetHashCode() + { + return _encoding.CodePage; + } + } + + internal sealed class InternalDecoderBestFitFallbackBuffer : DecoderFallbackBuffer + { + // Our variables + private char _cBestFit = '\0'; + private int _iCount = -1; + private int _iSize; + private InternalDecoderBestFitFallback _oFallback; + + // Private object for locking instead of locking on a public type for SQL reliability work. + private static Object s_InternalSyncObject; + private static Object InternalSyncObject + { + get + { + if (s_InternalSyncObject == null) + { + Object o = new Object(); + Interlocked.CompareExchange(ref s_InternalSyncObject, o, null); + } + return s_InternalSyncObject; + } + } + + // Constructor + public InternalDecoderBestFitFallbackBuffer(InternalDecoderBestFitFallback fallback) + { + _oFallback = fallback; + + if (_oFallback._arrayBestFit == null) + { + // Lock so we don't confuse ourselves. + lock (InternalSyncObject) + { + // Double check before we do it again. + if (_oFallback._arrayBestFit == null) + _oFallback._arrayBestFit = fallback._encoding.GetBestFitBytesToUnicodeData(); + } + } + } + + // Fallback methods + public override bool Fallback(byte[] bytesUnknown, int index) + { + // We expect no previous fallback in our buffer + Debug.Assert(_iCount < 1, "[DecoderReplacementFallbackBuffer.Fallback] Calling fallback without a previously empty buffer"); + + _cBestFit = TryBestFit(bytesUnknown); + if (_cBestFit == '\0') + _cBestFit = _oFallback._cReplacement; + + _iCount = _iSize = 1; + + return true; + } + + // Default version is overridden in DecoderReplacementFallback.cs + public override char GetNextChar() + { + // We want it to get < 0 because == 0 means that the current/last character is a fallback + // and we need to detect recursion. We could have a flag but we already have this counter. + _iCount--; + + // Do we have anything left? 0 is now last fallback char, negative is nothing left + if (_iCount < 0) + return '\0'; + + // Need to get it out of the buffer. + // Make sure it didn't wrap from the fast count-- path + if (_iCount == int.MaxValue) + { + _iCount = -1; + return '\0'; + } + + // Return the best fit character + return _cBestFit; + } + + public override bool MovePrevious() + { + // Exception fallback doesn't have anywhere to back up to. + if (_iCount >= 0) + _iCount++; + + // Return true if we could do it. + return (_iCount >= 0 && _iCount <= _iSize); + } + + // How many characters left to output? + public override int Remaining + { + get + { + return (_iCount > 0) ? _iCount : 0; + } + } + + // Clear the buffer + public override unsafe void Reset() + { + _iCount = -1; + byteStart = null; + } + + // This version just counts the fallback and doesn't actually copy anything. + internal unsafe override int InternalFallback(byte[] bytes, byte* pBytes) + // Right now this has both bytes and bytes[], since we might have extra bytes, hence the + // array, and we might need the index, hence the byte* + { + // return our replacement string Length (always 1 for InternalDecoderBestFitFallback, either + // a best fit char or ? + return 1; + } + + // private helper methods + private char TryBestFit(byte[] bytesCheck) + { + // Need to figure out our best fit character, low is beginning of array, high is 1 AFTER end of array + int lowBound = 0; + int highBound = _oFallback._arrayBestFit.Length; + int index; + char cCheck; + + // Check trivial case first (no best fit) + if (highBound == 0) + return '\0'; + + // If our array is too small or too big we can't check + if (bytesCheck.Length == 0 || bytesCheck.Length > 2) + return '\0'; + + if (bytesCheck.Length == 1) + cCheck = unchecked((char)bytesCheck[0]); + else + cCheck = unchecked((char)((bytesCheck[0] << 8) + bytesCheck[1])); + + // Check trivial out of range case + if (cCheck < _oFallback._arrayBestFit[0] || cCheck > _oFallback._arrayBestFit[highBound - 2]) + return '\0'; + + // Binary search the array + int iDiff; + while ((iDiff = (highBound - lowBound)) > 6) + { + // Look in the middle, which is complicated by the fact that we have 2 #s for each pair, + // so we don't want index to be odd because it must be word aligned. + // Also note that index can never == highBound (because diff is rounded down) + index = ((iDiff / 2) + lowBound) & 0xFFFE; + + char cTest = _oFallback._arrayBestFit[index]; + if (cTest == cCheck) + { + // We found it + Debug.Assert(index + 1 < _oFallback._arrayBestFit.Length, + "[InternalDecoderBestFitFallbackBuffer.TryBestFit]Expected replacement character at end of array"); + return _oFallback._arrayBestFit[index + 1]; + } + else if (cTest < cCheck) + { + // We weren't high enough + lowBound = index; + } + else + { + // We weren't low enough + highBound = index; + } + } + + for (index = lowBound; index < highBound; index += 2) + { + if (_oFallback._arrayBestFit[index] == cCheck) + { + // We found it + Debug.Assert(index + 1 < _oFallback._arrayBestFit.Length, + "[InternalDecoderBestFitFallbackBuffer.TryBestFit]Expected replacement character at end of array"); + return _oFallback._arrayBestFit[index + 1]; + } + } + + // Char wasn't in our table + return '\0'; + } + } +} + diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/DecoderExceptionFallback.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/DecoderExceptionFallback.cs new file mode 100644 index 0000000000..8bfc1f32d3 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/DecoderExceptionFallback.cs @@ -0,0 +1,155 @@ +// 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.Globalization; +using System.Runtime.Serialization; + +namespace System.Text +{ + public sealed class DecoderExceptionFallback : DecoderFallback + { + // Construction + public DecoderExceptionFallback() + { + } + + public override DecoderFallbackBuffer CreateFallbackBuffer() + { + return new DecoderExceptionFallbackBuffer(); + } + + // Maximum number of characters that this instance of this fallback could return + public override int MaxCharCount + { + get + { + return 0; + } + } + + public override bool Equals(Object value) + { + DecoderExceptionFallback that = value as DecoderExceptionFallback; + if (that != null) + { + return (true); + } + return (false); + } + + public override int GetHashCode() + { + return 879; + } + } + + + public sealed class DecoderExceptionFallbackBuffer : DecoderFallbackBuffer + { + public override bool Fallback(byte[] bytesUnknown, int index) + { + Throw(bytesUnknown, index); + return true; + } + + public override char GetNextChar() + { + return (char)0; + } + + public override bool MovePrevious() + { + // Exception fallback doesn't have anywhere to back up to. + return false; + } + + // Exceptions are always empty + public override int Remaining + { + get + { + return 0; + } + } + + private void Throw(byte[] bytesUnknown, int index) + { + // Create a string representation of our bytes. + StringBuilder strBytes = new StringBuilder(bytesUnknown.Length * 3); + + int i; + for (i = 0; i < bytesUnknown.Length && i < 20; i++) + { + strBytes.Append('['); + strBytes.Append(bytesUnknown[i].ToString("X2", CultureInfo.InvariantCulture)); + strBytes.Append(']'); + } + + // In case the string's really long + if (i == 20) + strBytes.Append(" ..."); + + // Known index + throw new DecoderFallbackException( + SR.Format(SR.Argument_InvalidCodePageBytesIndex, + strBytes, index), bytesUnknown, index); + } + } + + // Exception for decoding unknown byte sequences. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class DecoderFallbackException : ArgumentException + { + private byte[] _bytesUnknown = null; + private int _index = 0; + + public DecoderFallbackException() + : base(SR.Arg_ArgumentException) + { + HResult = HResults.COR_E_ARGUMENT; + } + + public DecoderFallbackException(String message) + : base(message) + { + HResult = HResults.COR_E_ARGUMENT; + } + + public DecoderFallbackException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_ARGUMENT; + } + + public DecoderFallbackException(String message, byte[] bytesUnknown, int index) + : base(message) + { + _bytesUnknown = bytesUnknown; + _index = index; + } + + private DecoderFallbackException(SerializationInfo serializationInfo, StreamingContext streamingContext) + : base(serializationInfo, streamingContext) + { + } + + public byte[] BytesUnknown + { + get + { + return (_bytesUnknown); + } + } + + public int Index + { + get + { + return _index; + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/DecoderFallback.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/DecoderFallback.cs new file mode 100644 index 0000000000..11b9539b5c --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/DecoderFallback.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. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Globalization; +using System.Threading; + +namespace System.Text +{ + public abstract class DecoderFallback + { + private static DecoderFallback s_replacementFallback; // Default fallback, uses no best fit & "?" + private static DecoderFallback s_exceptionFallback; + + public static DecoderFallback ReplacementFallback => + s_replacementFallback ?? Interlocked.CompareExchange(ref s_replacementFallback, new DecoderReplacementFallback(), null) ?? s_replacementFallback; + + + public static DecoderFallback ExceptionFallback => + s_exceptionFallback ?? Interlocked.CompareExchange(ref s_exceptionFallback, new DecoderExceptionFallback(), null) ?? s_exceptionFallback; + + // Fallback + // + // Return the appropriate unicode string alternative to the character that need to fall back. + // Most implementations will be: + // return new MyCustomDecoderFallbackBuffer(this); + + public abstract DecoderFallbackBuffer CreateFallbackBuffer(); + + // Maximum number of characters that this instance of this fallback could return + + public abstract int MaxCharCount { get; } + } + + + public abstract class DecoderFallbackBuffer + { + // Most implementations will probably need an implementation-specific constructor + + // internal methods that cannot be overridden that let us do our fallback thing + // These wrap the internal methods so that we can check for people doing stuff that's incorrect + + public abstract bool Fallback(byte[] bytesUnknown, int index); + + // Get next character + + public abstract char GetNextChar(); + + // Back up a character + + public abstract bool MovePrevious(); + + // How many chars left in this fallback? + + public abstract int Remaining { get; } + + // Clear the buffer + + public virtual void Reset() + { + while (GetNextChar() != (char)0) ; + } + + // Internal items to help us figure out what we're doing as far as error messages, etc. + // These help us with our performance and messages internally + internal unsafe byte* byteStart; + internal unsafe char* charEnd; + + // Internal Reset + internal unsafe void InternalReset() + { + byteStart = null; + Reset(); + } + + // Set the above values + // This can't be part of the constructor because DecoderFallbacks would have to know how to implement these. + internal unsafe void InternalInitialize(byte* byteStart, char* charEnd) + { + this.byteStart = byteStart; + this.charEnd = charEnd; + } + + // Fallback the current byte by sticking it into the remaining char buffer. + // This can only be called by our encodings (other have to use the public fallback methods), so + // we can use our DecoderNLS here too (except we don't). + // Returns true if we are successful, false if we can't fallback the character (no buffer space) + // So caller needs to throw buffer space if return false. + // Right now this has both bytes and bytes[], since we might have extra bytes, hence the + // array, and we might need the index, hence the byte* + // Don't touch ref chars unless we succeed + internal unsafe virtual bool InternalFallback(byte[] bytes, byte* pBytes, ref char* chars) + { + Debug.Assert(byteStart != null, "[DecoderFallback.InternalFallback]Used InternalFallback without calling InternalInitialize"); + + // See if there's a fallback character and we have an output buffer then copy our string. + if (this.Fallback(bytes, (int)(pBytes - byteStart - bytes.Length))) + { + // Copy the chars to our output + char ch; + char* charTemp = chars; + bool bHighSurrogate = false; + while ((ch = GetNextChar()) != 0) + { + // Make sure no mixed up surrogates + if (Char.IsSurrogate(ch)) + { + if (Char.IsHighSurrogate(ch)) + { + // High Surrogate + if (bHighSurrogate) + throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex); + bHighSurrogate = true; + } + else + { + // Low surrogate + if (bHighSurrogate == false) + throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex); + bHighSurrogate = false; + } + } + + if (charTemp >= charEnd) + { + // No buffer space + return false; + } + + *(charTemp++) = ch; + } + + // Need to make sure that bHighSurrogate isn't true + if (bHighSurrogate) + throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex); + + // Now we aren't going to be false, so its OK to update chars + chars = charTemp; + } + + return true; + } + + // This version just counts the fallback and doesn't actually copy anything. + internal unsafe virtual int InternalFallback(byte[] bytes, byte* pBytes) + // Right now this has both bytes and bytes[], since we might have extra bytes, hence the + // array, and we might need the index, hence the byte* + { + Debug.Assert(byteStart != null, "[DecoderFallback.InternalFallback]Used InternalFallback without calling InternalInitialize"); + + // See if there's a fallback character and we have an output buffer then copy our string. + if (this.Fallback(bytes, (int)(pBytes - byteStart - bytes.Length))) + { + int count = 0; + + char ch; + bool bHighSurrogate = false; + while ((ch = GetNextChar()) != 0) + { + // Make sure no mixed up surrogates + if (Char.IsSurrogate(ch)) + { + if (Char.IsHighSurrogate(ch)) + { + // High Surrogate + if (bHighSurrogate) + throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex); + bHighSurrogate = true; + } + else + { + // Low surrogate + if (bHighSurrogate == false) + throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex); + bHighSurrogate = false; + } + } + + count++; + } + + // Need to make sure that bHighSurrogate isn't true + if (bHighSurrogate) + throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex); + + return count; + } + + // If no fallback return 0 + return 0; + } + + // private helper methods + internal void ThrowLastBytesRecursive(byte[] bytesUnknown) + { + // Create a string representation of our bytes. + StringBuilder strBytes = new StringBuilder(bytesUnknown.Length * 3); + int i; + for (i = 0; i < bytesUnknown.Length && i < 20; i++) + { + if (strBytes.Length > 0) + strBytes.Append(' '); + strBytes.AppendFormat(CultureInfo.InvariantCulture, "\\x{0:X2}", bytesUnknown[i]); + } + // In case the string's really long + if (i == 20) + strBytes.Append(" ..."); + + // Throw it, using our complete bytes + throw new ArgumentException( + SR.Format(SR.Argument_RecursiveFallbackBytes, + strBytes.ToString()), nameof(bytesUnknown)); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/DecoderNLS.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/DecoderNLS.cs new file mode 100644 index 0000000000..ee88c22f9d --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/DecoderNLS.cs @@ -0,0 +1,242 @@ +// 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.Runtime.Serialization; +using System.Text; +using System; +using System.Runtime.InteropServices; + +namespace System.Text +{ + // A Decoder is used to decode a sequence of blocks of bytes into a + // sequence of blocks of characters. Following instantiation of a decoder, + // sequential blocks of bytes are converted into blocks of characters through + // calls to the GetChars method. The decoder maintains state between the + // conversions, allowing it to correctly decode byte sequences that span + // adjacent blocks. + // + // Instances of specific implementations of the Decoder abstract base + // class are typically obtained through calls to the GetDecoder method + // of Encoding objects. + + internal class DecoderNLS : Decoder + { + // Remember our encoding + private Encoding _encoding; + private bool _mustFlush; + internal bool _throwOnOverflow; + internal int _bytesUsed; + + internal DecoderNLS(Encoding encoding) + { + _encoding = encoding; + _fallback = this._encoding.DecoderFallback; + this.Reset(); + } + + // This is used by our child deserializers + internal DecoderNLS() + { + _encoding = null; + this.Reset(); + } + + public override void Reset() + { + _fallbackBuffer?.Reset(); + } + + public override unsafe int GetCharCount(byte[] bytes, int index, int count) + { + return GetCharCount(bytes, index, count, false); + } + + public override unsafe int GetCharCount(byte[] bytes, int index, int count, bool flush) + { + // Validate Parameters + if (bytes == null) + throw new ArgumentNullException(nameof(bytes), + SR.ArgumentNull_Array); + + if (index < 0 || count < 0) + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), + SR.ArgumentOutOfRange_NeedNonNegNum); + + if (bytes.Length - index < count) + throw new ArgumentOutOfRangeException(nameof(bytes), + SR.ArgumentOutOfRange_IndexCountBuffer); + + // Just call pointer version + fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) + return GetCharCount(pBytes + index, count, flush); + } + + public unsafe override int GetCharCount(byte* bytes, int count, bool flush) + { + // Validate parameters + if (bytes == null) + throw new ArgumentNullException(nameof(bytes), + SR.ArgumentNull_Array); + + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), + SR.ArgumentOutOfRange_NeedNonNegNum); + + // Remember the flush + _mustFlush = flush; + _throwOnOverflow = true; + + // By default just call the encoding version, no flush by default + return _encoding.GetCharCount(bytes, count, this); + } + + public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount, + char[] chars, int charIndex) + { + return GetChars(bytes, byteIndex, byteCount, chars, charIndex, false); + } + + public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount, + char[] chars, int charIndex, bool flush) + { + // Validate Parameters + if (bytes == null || chars == null) + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), + SR.ArgumentNull_Array); + + if (byteIndex < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((byteIndex < 0 ? nameof(byteIndex) : nameof(byteCount)), + SR.ArgumentOutOfRange_NeedNonNegNum); + + if (bytes.Length - byteIndex < byteCount) + throw new ArgumentOutOfRangeException(nameof(bytes), + SR.ArgumentOutOfRange_IndexCountBuffer); + + if (charIndex < 0 || charIndex > chars.Length) + throw new ArgumentOutOfRangeException(nameof(charIndex), + SR.ArgumentOutOfRange_Index); + + int charCount = chars.Length - charIndex; + + // Just call pointer version + fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) + fixed (char* pChars = &MemoryMarshal.GetReference((Span)chars)) + // Remember that charCount is # to decode, not size of array + return GetChars(pBytes + byteIndex, byteCount, + pChars + charIndex, charCount, flush); + } + + public unsafe override int GetChars(byte* bytes, int byteCount, + char* chars, int charCount, bool flush) + { + // Validate parameters + if (chars == null || bytes == null) + throw new ArgumentNullException((chars == null ? nameof(chars) : nameof(bytes)), + SR.ArgumentNull_Array); + + if (byteCount < 0 || charCount < 0) + throw new ArgumentOutOfRangeException((byteCount < 0 ? nameof(byteCount) : nameof(charCount)), + SR.ArgumentOutOfRange_NeedNonNegNum); + + // Remember our flush + _mustFlush = flush; + _throwOnOverflow = true; + + // By default just call the encodings version + return _encoding.GetChars(bytes, byteCount, chars, charCount, this); + } + + // This method is used when the output buffer might not be big enough. + // Just call the pointer version. (This gets chars) + public override unsafe void Convert(byte[] bytes, int byteIndex, int byteCount, + char[] chars, int charIndex, int charCount, bool flush, + out int bytesUsed, out int charsUsed, out bool completed) + { + // Validate parameters + if (bytes == null || chars == null) + throw new ArgumentNullException((bytes == null ? nameof(bytes) : nameof(chars)), + SR.ArgumentNull_Array); + + if (byteIndex < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((byteIndex < 0 ? nameof(byteIndex) : nameof(byteCount)), + SR.ArgumentOutOfRange_NeedNonNegNum); + + if (charIndex < 0 || charCount < 0) + throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)), + SR.ArgumentOutOfRange_NeedNonNegNum); + + if (bytes.Length - byteIndex < byteCount) + throw new ArgumentOutOfRangeException(nameof(bytes), + SR.ArgumentOutOfRange_IndexCountBuffer); + + if (chars.Length - charIndex < charCount) + throw new ArgumentOutOfRangeException(nameof(chars), + SR.ArgumentOutOfRange_IndexCountBuffer); + + // Just call the pointer version (public overrides can't do this) + fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) + { + fixed (char* pChars = &MemoryMarshal.GetReference((Span)chars)) + { + Convert(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, flush, + out bytesUsed, out charsUsed, out completed); + } + } + } + + // This is the version that used pointers. We call the base encoding worker function + // after setting our appropriate internal variables. This is getting chars + public unsafe override void Convert(byte* bytes, int byteCount, + char* chars, int charCount, bool flush, + out int bytesUsed, out int charsUsed, out bool completed) + { + // Validate input parameters + if (chars == null || bytes == null) + throw new ArgumentNullException(chars == null ? nameof(chars) : nameof(bytes), + SR.ArgumentNull_Array); + + if (byteCount < 0 || charCount < 0) + throw new ArgumentOutOfRangeException((byteCount < 0 ? nameof(byteCount) : nameof(charCount)), + SR.ArgumentOutOfRange_NeedNonNegNum); + + // We don't want to throw + _mustFlush = flush; + _throwOnOverflow = false; + _bytesUsed = 0; + + // Do conversion + charsUsed = _encoding.GetChars(bytes, byteCount, chars, charCount, this); + bytesUsed = _bytesUsed; + + // Its completed if they've used what they wanted AND if they didn't want flush or if we are flushed + completed = (bytesUsed == byteCount) && (!flush || !this.HasState) && + (_fallbackBuffer == null || _fallbackBuffer.Remaining == 0); + + // Our data thingy are now full, we can return + } + + public bool MustFlush + { + get + { + return _mustFlush; + } + } + + // Anything left in our decoder? + internal virtual bool HasState + { + get + { + return false; + } + } + + // Allow encoding to clear our must flush instead of throwing (in ThrowCharsOverflow) + internal void ClearMustFlush() + { + _mustFlush = false; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/DecoderReplacementFallback.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/DecoderReplacementFallback.cs new file mode 100644 index 0000000000..422b80bb2f --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/DecoderReplacementFallback.cs @@ -0,0 +1,204 @@ +// 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.Diagnostics; + +namespace System.Text +{ + public sealed class DecoderReplacementFallback : DecoderFallback + { + // Our variables + private String _strDefault; + + // Construction. Default replacement fallback uses no best fit and ? replacement string + public DecoderReplacementFallback() : this("?") + { + } + + public DecoderReplacementFallback(String replacement) + { + if (replacement == null) + throw new ArgumentNullException(nameof(replacement)); + + // Make sure it doesn't have bad surrogate pairs + bool bFoundHigh = false; + for (int i = 0; i < replacement.Length; i++) + { + // Found a surrogate? + if (Char.IsSurrogate(replacement, i)) + { + // High or Low? + if (Char.IsHighSurrogate(replacement, i)) + { + // if already had a high one, stop + if (bFoundHigh) + break; // break & throw at the bFoundHIgh below + bFoundHigh = true; + } + else + { + // Low, did we have a high? + if (!bFoundHigh) + { + // Didn't have one, make if fail when we stop + bFoundHigh = true; + break; + } + + // Clear flag + bFoundHigh = false; + } + } + // If last was high we're in trouble (not surrogate so not low surrogate, so break) + else if (bFoundHigh) + break; + } + if (bFoundHigh) + throw new ArgumentException(SR.Format(SR.Argument_InvalidCharSequenceNoIndex, nameof(replacement))); + + _strDefault = replacement; + } + + public String DefaultString + { + get + { + return _strDefault; + } + } + + public override DecoderFallbackBuffer CreateFallbackBuffer() + { + return new DecoderReplacementFallbackBuffer(this); + } + + // Maximum number of characters that this instance of this fallback could return + public override int MaxCharCount + { + get + { + return _strDefault.Length; + } + } + + public override bool Equals(Object value) + { + DecoderReplacementFallback that = value as DecoderReplacementFallback; + if (that != null) + { + return (_strDefault == that._strDefault); + } + return (false); + } + + public override int GetHashCode() + { + return _strDefault.GetHashCode(); + } + } + + + + public sealed class DecoderReplacementFallbackBuffer : DecoderFallbackBuffer + { + // Store our default string + private String _strDefault; + private int _fallbackCount = -1; + private int _fallbackIndex = -1; + + // Construction + public DecoderReplacementFallbackBuffer(DecoderReplacementFallback fallback) + { + _strDefault = fallback.DefaultString; + } + + // Fallback Methods + public override bool Fallback(byte[] bytesUnknown, int index) + { + // We expect no previous fallback in our buffer + // We can't call recursively but others might (note, we don't test on last char!!!) + if (_fallbackCount >= 1) + { + ThrowLastBytesRecursive(bytesUnknown); + } + + // Go ahead and get our fallback + if (_strDefault.Length == 0) + return false; + + _fallbackCount = _strDefault.Length; + _fallbackIndex = -1; + + return true; + } + + public override char GetNextChar() + { + // We want it to get < 0 because == 0 means that the current/last character is a fallback + // and we need to detect recursion. We could have a flag but we already have this counter. + _fallbackCount--; + _fallbackIndex++; + + // Do we have anything left? 0 is now last fallback char, negative is nothing left + if (_fallbackCount < 0) + return '\0'; + + // Need to get it out of the buffer. + // Make sure it didn't wrap from the fast count-- path + if (_fallbackCount == int.MaxValue) + { + _fallbackCount = -1; + return '\0'; + } + + // Now make sure its in the expected range + Debug.Assert(_fallbackIndex < _strDefault.Length && _fallbackIndex >= 0, + "Index exceeds buffer range"); + + return _strDefault[_fallbackIndex]; + } + + public override bool MovePrevious() + { + // Back up one, only if we just processed the last character (or earlier) + if (_fallbackCount >= -1 && _fallbackIndex >= 0) + { + _fallbackIndex--; + _fallbackCount++; + return true; + } + + // Return false 'cause we couldn't do it. + return false; + } + + // How many characters left to output? + public override int Remaining + { + get + { + // Our count is 0 for 1 character left. + return (_fallbackCount < 0) ? 0 : _fallbackCount; + } + } + + // Clear the buffer + public override unsafe void Reset() + { + _fallbackCount = -1; + _fallbackIndex = -1; + byteStart = null; + } + + // This version just counts the fallback and doesn't actually copy anything. + internal unsafe override int InternalFallback(byte[] bytes, byte* pBytes) + // Right now this has both bytes and bytes[], since we might have extra bytes, hence the + // array, and we might need the index, hence the byte* + { + // return our replacement string Length + return _strDefault.Length; + } + } +} + diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/Encoder.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/Encoder.cs index c4b54ce75c..fb1bdb8038 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Text/Encoder.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/Encoder.cs @@ -2,11 +2,10 @@ // 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.Runtime.Serialization; using System.Text; using System; using System.Diagnostics; -using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; namespace System.Text { @@ -23,15 +22,9 @@ namespace System.Text // public abstract class Encoder { - internal EncoderFallback m_fallback = null; + internal EncoderFallback _fallback = null; - [NonSerialized] - internal EncoderFallbackBuffer m_fallbackBuffer = null; - - internal void SerializeEncoder(SerializationInfo info) - { - info.AddValue("m_fallback", this.m_fallback); - } + internal EncoderFallbackBuffer _fallbackBuffer = null; protected Encoder() { @@ -42,22 +35,21 @@ namespace System.Text { get { - return m_fallback; + return _fallback; } set { if (value == null) throw new ArgumentNullException(nameof(value)); - Contract.EndContractBlock(); // Can't change fallback if buffer is wrong - if (m_fallbackBuffer != null && m_fallbackBuffer.Remaining > 0) + if (_fallbackBuffer != null && _fallbackBuffer.Remaining > 0) throw new ArgumentException( SR.Argument_FallbackBufferNotEmpty, nameof(value)); - m_fallback = value; - m_fallbackBuffer = null; + _fallback = value; + _fallbackBuffer = null; } } @@ -67,15 +59,15 @@ namespace System.Text { get { - if (m_fallbackBuffer == null) + if (_fallbackBuffer == null) { - if (m_fallback != null) - m_fallbackBuffer = m_fallback.CreateFallbackBuffer(); + if (_fallback != null) + _fallbackBuffer = _fallback.CreateFallbackBuffer(); else - m_fallbackBuffer = EncoderFallback.ReplacementFallback.CreateFallbackBuffer(); + _fallbackBuffer = EncoderFallback.ReplacementFallback.CreateFallbackBuffer(); } - return m_fallbackBuffer; + return _fallbackBuffer; } } @@ -83,7 +75,7 @@ namespace System.Text { get { - return m_fallbackBuffer != null; + return _fallbackBuffer != null; } } @@ -101,8 +93,8 @@ namespace System.Text char[] charTemp = { }; byte[] byteTemp = new byte[GetByteCount(charTemp, 0, 0, true)]; GetBytes(charTemp, 0, 0, byteTemp, 0, true); - if (m_fallbackBuffer != null) - m_fallbackBuffer.Reset(); + if (_fallbackBuffer != null) + _fallbackBuffer.Reset(); } // Returns the number of bytes the next call to GetBytes will @@ -128,7 +120,6 @@ namespace System.Text if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); char[] arrChar = new char[count]; int index; @@ -139,6 +130,14 @@ namespace System.Text return GetByteCount(arrChar, 0, count, flush); } + public virtual unsafe int GetByteCount(ReadOnlySpan chars, bool flush) + { + fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) + { + return GetByteCount(charsPtr, chars.Length, flush); + } + } + // Encodes a range of characters in a character array into a range of bytes // in a byte array. The method encodes charCount characters from // chars starting at index charIndex, storing the resulting @@ -189,7 +188,6 @@ namespace System.Text if (charCount < 0 || byteCount < 0) throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Get the char array to convert char[] arrChar = new char[charCount]; @@ -221,6 +219,15 @@ namespace System.Text return byteCount; } + public virtual unsafe int GetBytes(ReadOnlySpan chars, Span bytes, bool flush) + { + fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) + fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + { + return GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length, flush); + } + } + // This method is used to avoid running out of output buffer space. // It will encode until it runs out of chars, and then it will return // true if it the entire input was converted. In either case it @@ -258,7 +265,6 @@ namespace System.Text if (bytes.Length - byteIndex < byteCount) throw new ArgumentOutOfRangeException(nameof(bytes), SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); charsUsed = charCount; @@ -271,7 +277,7 @@ namespace System.Text { bytesUsed = GetBytes(chars, charIndex, charsUsed, bytes, byteIndex, flush); completed = (charsUsed == charCount && - (m_fallbackBuffer == null || m_fallbackBuffer.Remaining == 0)); + (_fallbackBuffer == null || _fallbackBuffer.Remaining == 0)); return; } @@ -303,7 +309,6 @@ namespace System.Text if (charCount < 0 || byteCount < 0) throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Get ready to do it charsUsed = charCount; @@ -315,7 +320,7 @@ namespace System.Text { bytesUsed = GetBytes(chars, charsUsed, bytes, byteCount, flush); completed = (charsUsed == charCount && - (m_fallbackBuffer == null || m_fallbackBuffer.Remaining == 0)); + (_fallbackBuffer == null || _fallbackBuffer.Remaining == 0)); return; } @@ -327,6 +332,15 @@ namespace System.Text // Oops, we didn't have anything, we'll have to throw an overflow throw new ArgumentException(SR.Argument_ConversionOverflow); } + + public virtual unsafe void Convert(ReadOnlySpan chars, Span bytes, bool flush, out int charsUsed, out int bytesUsed, out bool completed) + { + fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) + fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + { + Convert(charsPtr, chars.Length, bytesPtr, bytes.Length, flush, out charsUsed, out bytesUsed, out completed); + } + } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/EncoderBestFitFallback.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/EncoderBestFitFallback.cs new file mode 100644 index 0000000000..7f3be2a7a6 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/EncoderBestFitFallback.cs @@ -0,0 +1,242 @@ +// 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. + +// +// This is used internally to create best fit behavior as per the original windows best fit behavior. +// + +using System.Diagnostics; +using System.Globalization; +using System.Threading; + +namespace System.Text +{ + internal class InternalEncoderBestFitFallback : EncoderFallback + { + // Our variables + internal Encoding _encoding = null; + internal char[] _arrayBestFit = null; + + internal InternalEncoderBestFitFallback(Encoding encoding) + { + // Need to load our replacement characters table. + _encoding = encoding; + } + + public override EncoderFallbackBuffer CreateFallbackBuffer() + { + return new InternalEncoderBestFitFallbackBuffer(this); + } + + // Maximum number of characters that this instance of this fallback could return + public override int MaxCharCount + { + get + { + return 1; + } + } + + public override bool Equals(Object value) + { + InternalEncoderBestFitFallback that = value as InternalEncoderBestFitFallback; + if (that != null) + { + return (_encoding.CodePage == that._encoding.CodePage); + } + return (false); + } + + public override int GetHashCode() + { + return _encoding.CodePage; + } + } + + internal sealed class InternalEncoderBestFitFallbackBuffer : EncoderFallbackBuffer + { + // Our variables + private char _cBestFit = '\0'; + private InternalEncoderBestFitFallback _oFallback; + private int _iCount = -1; + private int _iSize; + + // Private object for locking instead of locking on a public type for SQL reliability work. + private static Object s_InternalSyncObject; + private static Object InternalSyncObject + { + get + { + if (s_InternalSyncObject == null) + { + Object o = new Object(); + Interlocked.CompareExchange(ref s_InternalSyncObject, o, null); + } + return s_InternalSyncObject; + } + } + + // Constructor + public InternalEncoderBestFitFallbackBuffer(InternalEncoderBestFitFallback fallback) + { + _oFallback = fallback; + + if (_oFallback._arrayBestFit == null) + { + // Lock so we don't confuse ourselves. + lock (InternalSyncObject) + { + // Double check before we do it again. + if (_oFallback._arrayBestFit == null) + _oFallback._arrayBestFit = fallback._encoding.GetBestFitUnicodeToBytesData(); + } + } + } + + // Fallback methods + public override bool Fallback(char charUnknown, int index) + { + // If we had a buffer already we're being recursive, throw, it's probably at the suspect + // character in our array. + // Shouldn't be able to get here for all of our code pages, table would have to be messed up. + Debug.Assert(_iCount < 1, "[InternalEncoderBestFitFallbackBuffer.Fallback(non surrogate)] Fallback char " + ((int)_cBestFit).ToString("X4", CultureInfo.InvariantCulture) + " caused recursive fallback"); + + _iCount = _iSize = 1; + _cBestFit = TryBestFit(charUnknown); + if (_cBestFit == '\0') + _cBestFit = '?'; + + return true; + } + + public override bool Fallback(char charUnknownHigh, char charUnknownLow, int index) + { + // Double check input surrogate pair + if (!Char.IsHighSurrogate(charUnknownHigh)) + throw new ArgumentOutOfRangeException(nameof(charUnknownHigh), + SR.Format(SR.ArgumentOutOfRange_Range, + 0xD800, 0xDBFF)); + + if (!Char.IsLowSurrogate(charUnknownLow)) + throw new ArgumentOutOfRangeException(nameof(charUnknownLow), + SR.Format(SR.ArgumentOutOfRange_Range, + 0xDC00, 0xDFFF)); + + // If we had a buffer already we're being recursive, throw, it's probably at the suspect + // character in our array. 0 is processing last character, < 0 is not falling back + // Shouldn't be able to get here, table would have to be messed up. + Debug.Assert(_iCount < 1, "[InternalEncoderBestFitFallbackBuffer.Fallback(surrogate)] Fallback char " + ((int)_cBestFit).ToString("X4", CultureInfo.InvariantCulture) + " caused recursive fallback"); + + // Go ahead and get our fallback, surrogates don't have best fit + _cBestFit = '?'; + _iCount = _iSize = 2; + + return true; + } + + // Default version is overridden in EncoderReplacementFallback.cs + public override char GetNextChar() + { + // We want it to get < 0 because == 0 means that the current/last character is a fallback + // and we need to detect recursion. We could have a flag but we already have this counter. + _iCount--; + + // Do we have anything left? 0 is now last fallback char, negative is nothing left + if (_iCount < 0) + return '\0'; + + // Need to get it out of the buffer. + // Make sure it didn't wrap from the fast count-- path + if (_iCount == int.MaxValue) + { + _iCount = -1; + return '\0'; + } + + // Return the best fit character + return _cBestFit; + } + + public override bool MovePrevious() + { + // Exception fallback doesn't have anywhere to back up to. + if (_iCount >= 0) + _iCount++; + + // Return true if we could do it. + return (_iCount >= 0 && _iCount <= _iSize); + } + + + // How many characters left to output? + public override int Remaining + { + get + { + return (_iCount > 0) ? _iCount : 0; + } + } + + // Clear the buffer + public override unsafe void Reset() + { + _iCount = -1; + charStart = null; + bFallingBack = false; + } + + // private helper methods + private char TryBestFit(char cUnknown) + { + // Need to figure out our best fit character, low is beginning of array, high is 1 AFTER end of array + int lowBound = 0; + int highBound = _oFallback._arrayBestFit.Length; + int index; + + // Binary search the array + int iDiff; + while ((iDiff = (highBound - lowBound)) > 6) + { + // Look in the middle, which is complicated by the fact that we have 2 #s for each pair, + // so we don't want index to be odd because we want to be on word boundaries. + // Also note that index can never == highBound (because diff is rounded down) + index = ((iDiff / 2) + lowBound) & 0xFFFE; + + char cTest = _oFallback._arrayBestFit[index]; + if (cTest == cUnknown) + { + // We found it + Debug.Assert(index + 1 < _oFallback._arrayBestFit.Length, + "[InternalEncoderBestFitFallbackBuffer.TryBestFit]Expected replacement character at end of array"); + return _oFallback._arrayBestFit[index + 1]; + } + else if (cTest < cUnknown) + { + // We weren't high enough + lowBound = index; + } + else + { + // We weren't low enough + highBound = index; + } + } + + for (index = lowBound; index < highBound; index += 2) + { + if (_oFallback._arrayBestFit[index] == cUnknown) + { + // We found it + Debug.Assert(index + 1 < _oFallback._arrayBestFit.Length, + "[InternalEncoderBestFitFallbackBuffer.TryBestFit]Expected replacement character at end of array"); + return _oFallback._arrayBestFit[index + 1]; + } + } + + // Char wasn't in our table + return '\0'; + } + } +} + diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/EncoderExceptionFallback.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/EncoderExceptionFallback.cs new file mode 100644 index 0000000000..66de1940f6 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/EncoderExceptionFallback.cs @@ -0,0 +1,195 @@ +// 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.Runtime.Serialization; + +namespace System.Text +{ + public sealed class EncoderExceptionFallback : EncoderFallback + { + // Construction + public EncoderExceptionFallback() + { + } + + public override EncoderFallbackBuffer CreateFallbackBuffer() + { + return new EncoderExceptionFallbackBuffer(); + } + + // Maximum number of characters that this instance of this fallback could return + public override int MaxCharCount + { + get + { + return 0; + } + } + + public override bool Equals(Object value) + { + EncoderExceptionFallback that = value as EncoderExceptionFallback; + if (that != null) + { + return (true); + } + return (false); + } + + public override int GetHashCode() + { + return 654; + } + } + + + public sealed class EncoderExceptionFallbackBuffer : EncoderFallbackBuffer + { + public EncoderExceptionFallbackBuffer() { } + public override bool Fallback(char charUnknown, int index) + { + // Fall back our char + throw new EncoderFallbackException( + SR.Format(SR.Argument_InvalidCodePageConversionIndex, (int)charUnknown, index), charUnknown, index); + } + + public override bool Fallback(char charUnknownHigh, char charUnknownLow, int index) + { + if (!Char.IsHighSurrogate(charUnknownHigh)) + { + throw new ArgumentOutOfRangeException(nameof(charUnknownHigh), + SR.Format(SR.ArgumentOutOfRange_Range, 0xD800, 0xDBFF)); + } + if (!Char.IsLowSurrogate(charUnknownLow)) + { + throw new ArgumentOutOfRangeException(nameof(charUnknownLow), + SR.Format(SR.ArgumentOutOfRange_Range, 0xDC00, 0xDFFF)); + } + + int iTemp = Char.ConvertToUtf32(charUnknownHigh, charUnknownLow); + + // Fall back our char + throw new EncoderFallbackException( + SR.Format(SR.Argument_InvalidCodePageConversionIndex, iTemp, index), charUnknownHigh, charUnknownLow, index); + } + + public override char GetNextChar() + { + return (char)0; + } + + public override bool MovePrevious() + { + // Exception fallback doesn't have anywhere to back up to. + return false; + } + + // Exceptions are always empty + public override int Remaining + { + get + { + return 0; + } + } + } + + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class EncoderFallbackException : ArgumentException + { + private char _charUnknown; + private char _charUnknownHigh; + private char _charUnknownLow; + private int _index; + + public EncoderFallbackException() + : base(SR.Arg_ArgumentException) + { + HResult = HResults.COR_E_ARGUMENT; + } + + public EncoderFallbackException(String message) + : base(message) + { + HResult = HResults.COR_E_ARGUMENT; + } + + public EncoderFallbackException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_ARGUMENT; + } + + internal EncoderFallbackException( + String message, char charUnknown, int index) : base(message) + { + _charUnknown = charUnknown; + _index = index; + } + + internal EncoderFallbackException( + String message, char charUnknownHigh, char charUnknownLow, int index) : base(message) + { + if (!Char.IsHighSurrogate(charUnknownHigh)) + { + throw new ArgumentOutOfRangeException(nameof(charUnknownHigh), + SR.Format(SR.ArgumentOutOfRange_Range, 0xD800, 0xDBFF)); + } + if (!Char.IsLowSurrogate(charUnknownLow)) + { + throw new ArgumentOutOfRangeException(nameof(CharUnknownLow), + SR.Format(SR.ArgumentOutOfRange_Range, 0xDC00, 0xDFFF)); + } + + _charUnknownHigh = charUnknownHigh; + _charUnknownLow = charUnknownLow; + _index = index; + } + + private EncoderFallbackException(SerializationInfo serializationInfo, StreamingContext streamingContext) + : base(serializationInfo, streamingContext) + { + } + + public char CharUnknown + { + get + { + return (_charUnknown); + } + } + + public char CharUnknownHigh + { + get + { + return (_charUnknownHigh); + } + } + + public char CharUnknownLow + { + get + { + return (_charUnknownLow); + } + } + + public int Index + { + get + { + return _index; + } + } + + // Return true if the unknown character is a surrogate pair. + public bool IsUnknownSurrogate() + { + return (_charUnknownHigh != '\0'); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/EncoderFallback.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/EncoderFallback.cs new file mode 100644 index 0000000000..d860a02a76 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/EncoderFallback.cs @@ -0,0 +1,202 @@ +// 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.Diagnostics; +using System.Threading; + +namespace System.Text +{ + public abstract class EncoderFallback + { + private static EncoderFallback s_replacementFallback; // Default fallback, uses no best fit & "?" + private static EncoderFallback s_exceptionFallback; + + // Get each of our generic fallbacks. + + public static EncoderFallback ReplacementFallback + { + get + { + if (s_replacementFallback == null) + Interlocked.CompareExchange(ref s_replacementFallback, new EncoderReplacementFallback(), null); + + return s_replacementFallback; + } + } + + + public static EncoderFallback ExceptionFallback + { + get + { + if (s_exceptionFallback == null) + Interlocked.CompareExchange(ref s_exceptionFallback, new EncoderExceptionFallback(), null); + + return s_exceptionFallback; + } + } + + // Fallback + // + // Return the appropriate unicode string alternative to the character that need to fall back. + // Most implementations will be: + // return new MyCustomEncoderFallbackBuffer(this); + + public abstract EncoderFallbackBuffer CreateFallbackBuffer(); + + // Maximum number of characters that this instance of this fallback could return + + public abstract int MaxCharCount { get; } + } + + + public abstract class EncoderFallbackBuffer + { + // Most implementations will probably need an implementation-specific constructor + + // Public methods that cannot be overridden that let us do our fallback thing + // These wrap the internal methods so that we can check for people doing stuff that is incorrect + + public abstract bool Fallback(char charUnknown, int index); + + public abstract bool Fallback(char charUnknownHigh, char charUnknownLow, int index); + + // Get next character + + public abstract char GetNextChar(); + + // Back up a character + + public abstract bool MovePrevious(); + + // How many chars left in this fallback? + + public abstract int Remaining { get; } + + // Not sure if this should be public or not. + // Clear the buffer + + public virtual void Reset() + { + while (GetNextChar() != (char)0) ; + } + + // Internal items to help us figure out what we're doing as far as error messages, etc. + // These help us with our performance and messages internally + internal unsafe char* charStart; + internal unsafe char* charEnd; + internal EncoderNLS encoder; + internal bool setEncoder; + internal bool bUsedEncoder; + internal bool bFallingBack = false; + internal int iRecursionCount = 0; + private const int iMaxRecursion = 250; + + // Internal Reset + // For example, what if someone fails a conversion and wants to reset one of our fallback buffers? + internal unsafe void InternalReset() + { + charStart = null; + bFallingBack = false; + iRecursionCount = 0; + Reset(); + } + + // Set the above values + // This can't be part of the constructor because EncoderFallbacks would have to know how to implement these. + internal unsafe void InternalInitialize(char* charStart, char* charEnd, EncoderNLS encoder, bool setEncoder) + { + this.charStart = charStart; + this.charEnd = charEnd; + this.encoder = encoder; + this.setEncoder = setEncoder; + this.bUsedEncoder = false; + this.bFallingBack = false; + this.iRecursionCount = 0; + } + + internal char InternalGetNextChar() + { + char ch = GetNextChar(); + bFallingBack = (ch != 0); + if (ch == 0) iRecursionCount = 0; + return ch; + } + + // Fallback the current character using the remaining buffer and encoder if necessary + // This can only be called by our encodings (other have to use the public fallback methods), so + // we can use our EncoderNLS here too. + // setEncoder is true if we're calling from a GetBytes method, false if we're calling from a GetByteCount + // + // Note that this could also change the contents of this.encoder, which is the same + // object that the caller is using, so the caller could mess up the encoder for us + // if they aren't careful. + internal unsafe virtual bool InternalFallback(char ch, ref char* chars) + { + // Shouldn't have null charStart + Debug.Assert(charStart != null, + "[EncoderFallback.InternalFallbackBuffer]Fallback buffer is not initialized"); + + // Get our index, remember chars was preincremented to point at next char, so have to -1 + int index = (int)(chars - charStart) - 1; + + // See if it was a high surrogate + if (Char.IsHighSurrogate(ch)) + { + // See if there's a low surrogate to go with it + if (chars >= this.charEnd) + { + // Nothing left in input buffer + // No input, return 0 if mustflush is false + if (this.encoder != null && !this.encoder.MustFlush) + { + // Done, nothing to fallback + if (this.setEncoder) + { + bUsedEncoder = true; + this.encoder._charLeftOver = ch; + } + bFallingBack = false; + return false; + } + } + else + { + // Might have a low surrogate + char cNext = *chars; + if (Char.IsLowSurrogate(cNext)) + { + // If already falling back then fail + if (bFallingBack && iRecursionCount++ > iMaxRecursion) + ThrowLastCharRecursive(Char.ConvertToUtf32(ch, cNext)); + + // Next is a surrogate, add it as surrogate pair, and increment chars + chars++; + bFallingBack = Fallback(ch, cNext, index); + return bFallingBack; + } + // Next isn't a low surrogate, just fallback the high surrogate + } + } + + // If already falling back then fail + if (bFallingBack && iRecursionCount++ > iMaxRecursion) + ThrowLastCharRecursive((int)ch); + + // Fall back our char + bFallingBack = Fallback(ch, index); + + return bFallingBack; + } + + // private helper methods + internal void ThrowLastCharRecursive(int charRecursive) + { + // Throw it, using our complete character + throw new ArgumentException( + SR.Format(SR.Argument_RecursiveFallback, + charRecursive), "chars"); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/EncoderNLS.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/EncoderNLS.cs new file mode 100644 index 0000000000..e83666f7a3 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/EncoderNLS.cs @@ -0,0 +1,239 @@ +// 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.Text; +using System; +using System.Runtime.InteropServices; + +namespace System.Text +{ + // An Encoder is used to encode a sequence of blocks of characters into + // a sequence of blocks of bytes. Following instantiation of an encoder, + // sequential blocks of characters are converted into blocks of bytes through + // calls to the GetBytes method. The encoder maintains state between the + // conversions, allowing it to correctly encode character sequences that span + // adjacent blocks. + // + // Instances of specific implementations of the Encoder abstract base + // class are typically obtained through calls to the GetEncoder method + // of Encoding objects. + // + + internal class EncoderNLS : Encoder + { + // Need a place for the last left over character, most of our encodings use this + internal char _charLeftOver; + private Encoding _encoding; + private bool _mustFlush; + internal bool _throwOnOverflow; + internal int _charsUsed; + + internal EncoderNLS(Encoding encoding) + { + _encoding = encoding; + _fallback = _encoding.EncoderFallback; + this.Reset(); + } + + internal EncoderNLS() + { + _encoding = null; + this.Reset(); + } + + public override void Reset() + { + _charLeftOver = (char)0; + if (_fallbackBuffer != null) + _fallbackBuffer.Reset(); + } + + public override unsafe int GetByteCount(char[] chars, int index, int count, bool flush) + { + // Validate input parameters + if (chars == null) + throw new ArgumentNullException(nameof(chars), + SR.ArgumentNull_Array); + + if (index < 0 || count < 0) + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), + SR.ArgumentOutOfRange_NeedNonNegNum); + + if (chars.Length - index < count) + throw new ArgumentOutOfRangeException(nameof(chars), + SR.ArgumentOutOfRange_IndexCountBuffer); + + // Just call the pointer version + int result = -1; + fixed (char* pChars = &MemoryMarshal.GetReference((Span)chars)) + { + result = GetByteCount(pChars + index, count, flush); + } + return result; + } + + public unsafe override int GetByteCount(char* chars, int count, bool flush) + { + // Validate input parameters + if (chars == null) + throw new ArgumentNullException(nameof(chars), + SR.ArgumentNull_Array); + + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), + SR.ArgumentOutOfRange_NeedNonNegNum); + + _mustFlush = flush; + _throwOnOverflow = true; + return _encoding.GetByteCount(chars, count, this); + } + + public override unsafe int GetBytes(char[] chars, int charIndex, int charCount, + byte[] bytes, int byteIndex, bool flush) + { + // Validate parameters + if (chars == null || bytes == null) + throw new ArgumentNullException((chars == null ? nameof(chars) : nameof(bytes)), + SR.ArgumentNull_Array); + + if (charIndex < 0 || charCount < 0) + throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)), + SR.ArgumentOutOfRange_NeedNonNegNum); + + if (chars.Length - charIndex < charCount) + throw new ArgumentOutOfRangeException(nameof(chars), + SR.ArgumentOutOfRange_IndexCountBuffer); + + if (byteIndex < 0 || byteIndex > bytes.Length) + throw new ArgumentOutOfRangeException(nameof(byteIndex), + SR.ArgumentOutOfRange_Index); + + int byteCount = bytes.Length - byteIndex; + + // Just call pointer version + fixed (char* pChars = &MemoryMarshal.GetReference((Span)chars)) + fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) + + // Remember that charCount is # to decode, not size of array. + return GetBytes(pChars + charIndex, charCount, + pBytes + byteIndex, byteCount, flush); + } + + public unsafe override int GetBytes(char* chars, int charCount, byte* bytes, int byteCount, bool flush) + { + // Validate parameters + if (chars == null || bytes == null) + throw new ArgumentNullException((chars == null ? nameof(chars) : nameof(bytes)), + SR.ArgumentNull_Array); + + if (byteCount < 0 || charCount < 0) + throw new ArgumentOutOfRangeException((byteCount < 0 ? nameof(byteCount) : nameof(charCount)), + SR.ArgumentOutOfRange_NeedNonNegNum); + + _mustFlush = flush; + _throwOnOverflow = true; + return _encoding.GetBytes(chars, charCount, bytes, byteCount, this); + } + + // This method is used when your output buffer might not be large enough for the entire result. + // Just call the pointer version. (This gets bytes) + public override unsafe void Convert(char[] chars, int charIndex, int charCount, + byte[] bytes, int byteIndex, int byteCount, bool flush, + out int charsUsed, out int bytesUsed, out bool completed) + { + // Validate parameters + if (chars == null || bytes == null) + throw new ArgumentNullException((chars == null ? nameof(chars) : nameof(bytes)), + SR.ArgumentNull_Array); + + if (charIndex < 0 || charCount < 0) + throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)), + SR.ArgumentOutOfRange_NeedNonNegNum); + + if (byteIndex < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((byteIndex < 0 ? nameof(byteIndex) : nameof(byteCount)), + SR.ArgumentOutOfRange_NeedNonNegNum); + + if (chars.Length - charIndex < charCount) + throw new ArgumentOutOfRangeException(nameof(chars), + SR.ArgumentOutOfRange_IndexCountBuffer); + + if (bytes.Length - byteIndex < byteCount) + throw new ArgumentOutOfRangeException(nameof(bytes), + SR.ArgumentOutOfRange_IndexCountBuffer); + + // Just call the pointer version (can't do this for non-msft encoders) + fixed (char* pChars = &MemoryMarshal.GetReference((Span)chars)) + { + fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) + { + Convert(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, flush, + out charsUsed, out bytesUsed, out completed); + } + } + } + + // This is the version that uses pointers. We call the base encoding worker function + // after setting our appropriate internal variables. This is getting bytes + public override unsafe void Convert(char* chars, int charCount, + byte* bytes, int byteCount, bool flush, + out int charsUsed, out int bytesUsed, out bool completed) + { + // Validate input parameters + if (bytes == null || chars == null) + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), + SR.ArgumentNull_Array); + if (charCount < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)), + SR.ArgumentOutOfRange_NeedNonNegNum); + + // We don't want to throw + _mustFlush = flush; + _throwOnOverflow = false; + _charsUsed = 0; + + // Do conversion + bytesUsed = _encoding.GetBytes(chars, charCount, bytes, byteCount, this); + charsUsed = _charsUsed; + + // Its completed if they've used what they wanted AND if they didn't want flush or if we are flushed + completed = (charsUsed == charCount) && (!flush || !this.HasState) && + (_fallbackBuffer == null || _fallbackBuffer.Remaining == 0); + + // Our data thingys are now full, we can return + } + + public Encoding Encoding + { + get + { + return _encoding; + } + } + + public bool MustFlush + { + get + { + return _mustFlush; + } + } + + + // Anything left in our encoder? + internal virtual bool HasState + { + get + { + return (_charLeftOver != (char)0); + } + } + + // Allow encoding to clear our must flush instead of throwing (in ThrowBytesOverflow) + internal void ClearMustFlush() + { + _mustFlush = false; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/EncoderReplacementFallback.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/EncoderReplacementFallback.cs new file mode 100644 index 0000000000..a1d0bbcd95 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/EncoderReplacementFallback.cs @@ -0,0 +1,227 @@ +// 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.Runtime; +using System.Diagnostics; + +namespace System.Text +{ + public sealed class EncoderReplacementFallback : EncoderFallback + { + // Our variables + private String _strDefault; + + // Construction. Default replacement fallback uses no best fit and ? replacement string + public EncoderReplacementFallback() : this("?") + { + } + + public EncoderReplacementFallback(String replacement) + { + // Must not be null + if (replacement == null) + throw new ArgumentNullException(nameof(replacement)); + + // Make sure it doesn't have bad surrogate pairs + bool bFoundHigh = false; + for (int i = 0; i < replacement.Length; i++) + { + // Found a surrogate? + if (Char.IsSurrogate(replacement, i)) + { + // High or Low? + if (Char.IsHighSurrogate(replacement, i)) + { + // if already had a high one, stop + if (bFoundHigh) + break; // break & throw at the bFoundHIgh below + bFoundHigh = true; + } + else + { + // Low, did we have a high? + if (!bFoundHigh) + { + // Didn't have one, make if fail when we stop + bFoundHigh = true; + break; + } + + // Clear flag + bFoundHigh = false; + } + } + // If last was high we're in trouble (not surrogate so not low surrogate, so break) + else if (bFoundHigh) + break; + } + if (bFoundHigh) + throw new ArgumentException(SR.Format(SR.Argument_InvalidCharSequenceNoIndex, nameof(replacement))); + + _strDefault = replacement; + } + + public String DefaultString + { + get + { + return _strDefault; + } + } + + public override EncoderFallbackBuffer CreateFallbackBuffer() + { + return new EncoderReplacementFallbackBuffer(this); + } + + // Maximum number of characters that this instance of this fallback could return + public override int MaxCharCount + { + get + { + return _strDefault.Length; + } + } + + public override bool Equals(Object value) + { + EncoderReplacementFallback that = value as EncoderReplacementFallback; + if (that != null) + { + return (_strDefault == that._strDefault); + } + return (false); + } + + public override int GetHashCode() + { + return _strDefault.GetHashCode(); + } + } + + + + public sealed class EncoderReplacementFallbackBuffer : EncoderFallbackBuffer + { + // Store our default string + private String _strDefault; + private int _fallbackCount = -1; + private int _fallbackIndex = -1; + + // Construction + public EncoderReplacementFallbackBuffer(EncoderReplacementFallback fallback) + { + // 2X in case we're a surrogate pair + _strDefault = fallback.DefaultString + fallback.DefaultString; + } + + // Fallback Methods + public override bool Fallback(char charUnknown, int index) + { + // If we had a buffer already we're being recursive, throw, it's probably at the suspect + // character in our array. + if (_fallbackCount >= 1) + { + // If we're recursive we may still have something in our buffer that makes this a surrogate + if (char.IsHighSurrogate(charUnknown) && _fallbackCount >= 0 && + char.IsLowSurrogate(_strDefault[_fallbackIndex + 1])) + ThrowLastCharRecursive(Char.ConvertToUtf32(charUnknown, _strDefault[_fallbackIndex + 1])); + + // Nope, just one character + ThrowLastCharRecursive(unchecked((int)charUnknown)); + } + + // Go ahead and get our fallback + // Divide by 2 because we aren't a surrogate pair + _fallbackCount = _strDefault.Length / 2; + _fallbackIndex = -1; + + return _fallbackCount != 0; + } + + public override bool Fallback(char charUnknownHigh, char charUnknownLow, int index) + { + // Double check input surrogate pair + if (!Char.IsHighSurrogate(charUnknownHigh)) + throw new ArgumentOutOfRangeException(nameof(charUnknownHigh), + SR.Format(SR.ArgumentOutOfRange_Range, 0xD800, 0xDBFF)); + + if (!Char.IsLowSurrogate(charUnknownLow)) + throw new ArgumentOutOfRangeException(nameof(charUnknownLow), + SR.Format(SR.ArgumentOutOfRange_Range, 0xDC00, 0xDFFF)); + + // If we had a buffer already we're being recursive, throw, it's probably at the suspect + // character in our array. + if (_fallbackCount >= 1) + ThrowLastCharRecursive(Char.ConvertToUtf32(charUnknownHigh, charUnknownLow)); + + // Go ahead and get our fallback + _fallbackCount = _strDefault.Length; + _fallbackIndex = -1; + + return _fallbackCount != 0; + } + + public override char GetNextChar() + { + // We want it to get < 0 because == 0 means that the current/last character is a fallback + // and we need to detect recursion. We could have a flag but we already have this counter. + _fallbackCount--; + _fallbackIndex++; + + // Do we have anything left? 0 is now last fallback char, negative is nothing left + if (_fallbackCount < 0) + return '\0'; + + // Need to get it out of the buffer. + // Make sure it didn't wrap from the fast count-- path + if (_fallbackCount == int.MaxValue) + { + _fallbackCount = -1; + return '\0'; + } + + // Now make sure its in the expected range + Debug.Assert(_fallbackIndex < _strDefault.Length && _fallbackIndex >= 0, + "Index exceeds buffer range"); + + return _strDefault[_fallbackIndex]; + } + + public override bool MovePrevious() + { + // Back up one, only if we just processed the last character (or earlier) + if (_fallbackCount >= -1 && _fallbackIndex >= 0) + { + _fallbackIndex--; + _fallbackCount++; + return true; + } + + // Return false 'cause we couldn't do it. + return false; + } + + // How many characters left to output? + public override int Remaining + { + get + { + // Our count is 0 for 1 character left. + return (_fallbackCount < 0) ? 0 : _fallbackCount; + } + } + + // Clear the buffer + public override unsafe void Reset() + { + _fallbackCount = -1; + _fallbackIndex = 0; + charStart = null; + bFallingBack = false; + } + } +} + diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/Encoding.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/Encoding.cs new file mode 100644 index 0000000000..e469180ce6 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/Encoding.cs @@ -0,0 +1,1789 @@ +// 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.Diagnostics; +using System.Globalization; +using System.Threading; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Diagnostics.CodeAnalysis; + +namespace System.Text +{ + // This abstract base class represents a character encoding. The class provides + // methods to convert arrays and strings of Unicode characters to and from + // arrays of bytes. A number of Encoding implementations are provided in + // the System.Text package, including: + // + // ASCIIEncoding, which encodes Unicode characters as single 7-bit + // ASCII characters. This encoding only supports character values between 0x00 + // and 0x7F. + // BaseCodePageEncoding, which encapsulates a Windows code page. Any + // installed code page can be accessed through this encoding, and conversions + // are performed using the WideCharToMultiByte and + // MultiByteToWideChar Windows API functions. + // UnicodeEncoding, which encodes each Unicode character as two + // consecutive bytes. Both little-endian (code page 1200) and big-endian (code + // page 1201) encodings are recognized. + // UTF7Encoding, which encodes Unicode characters using the UTF-7 + // encoding (UTF-7 stands for UCS Transformation Format, 7-bit form). This + // encoding supports all Unicode character values, and can also be accessed + // as code page 65000. + // UTF8Encoding, which encodes Unicode characters using the UTF-8 + // encoding (UTF-8 stands for UCS Transformation Format, 8-bit form). This + // encoding supports all Unicode character values, and can also be accessed + // as code page 65001. + // UTF32Encoding, both 12000 (little endian) & 12001 (big endian) + // + // In addition to directly instantiating Encoding objects, an + // application can use the ForCodePage, GetASCII, + // GetDefault, GetUnicode, GetUTF7, and GetUTF8 + // methods in this class to obtain encodings. + // + // Through an encoding, the GetBytes method is used to convert arrays + // of characters to arrays of bytes, and the GetChars method is used to + // convert arrays of bytes to arrays of characters. The GetBytes and + // GetChars methods maintain no state between conversions, and are + // generally intended for conversions of complete blocks of bytes and + // characters in one operation. When the data to be converted is only available + // in sequential blocks (such as data read from a stream) or when the amount of + // data is so large that it needs to be divided into smaller blocks, an + // application may choose to use a Decoder or an Encoder to + // perform the conversion. Decoders and encoders allow sequential blocks of + // data to be converted and they maintain the state required to support + // conversions of data that spans adjacent blocks. Decoders and encoders are + // obtained using the GetDecoder and GetEncoder methods. + // + // The core GetBytes and GetChars methods require the caller + // to provide the destination buffer and ensure that the buffer is large enough + // to hold the entire result of the conversion. When using these methods, + // either directly on an Encoding object or on an associated + // Decoder or Encoder, an application can use one of two methods + // to allocate destination buffers. + // + // The GetByteCount and GetCharCount methods can be used to + // compute the exact size of the result of a particular conversion, and an + // appropriately sized buffer for that conversion can then be allocated. + // The GetMaxByteCount and GetMaxCharCount methods can be + // be used to compute the maximum possible size of a conversion of a given + // number of bytes or characters, and a buffer of that size can then be reused + // for multiple conversions. + // + // The first method generally uses less memory, whereas the second method + // generally executes faster. + // + + public abstract class Encoding : ICloneable + { + // For netcore we use UTF8 as default encoding since ANSI isn't available + private static readonly UTF8Encoding.UTF8EncodingSealed s_defaultEncoding = new UTF8Encoding.UTF8EncodingSealed(encoderShouldEmitUTF8Identifier: false); + + // Returns an encoding for the system's current ANSI code page. + public static Encoding Default => s_defaultEncoding; + + // + // The following values are from mlang.idl. These values + // should be in sync with those in mlang.idl. + // + internal const int MIMECONTF_MAILNEWS = 0x00000001; + internal const int MIMECONTF_BROWSER = 0x00000002; + internal const int MIMECONTF_SAVABLE_MAILNEWS = 0x00000100; + internal const int MIMECONTF_SAVABLE_BROWSER = 0x00000200; + + // Special Case Code Pages + private const int CodePageDefault = 0; + private const int CodePageNoOEM = 1; // OEM Code page not supported + private const int CodePageNoMac = 2; // MAC code page not supported + private const int CodePageNoThread = 3; // Thread code page not supported + private const int CodePageNoSymbol = 42; // Symbol code page not supported + private const int CodePageUnicode = 1200; // Unicode + private const int CodePageBigEndian = 1201; // Big Endian Unicode + private const int CodePageWindows1252 = 1252; // Windows 1252 code page + + // 20936 has same code page as 10008, so we'll special case it + private const int CodePageMacGB2312 = 10008; + private const int CodePageGB2312 = 20936; + private const int CodePageMacKorean = 10003; + private const int CodePageDLLKorean = 20949; + + // ISO 2022 Code Pages + private const int ISO2022JP = 50220; + private const int ISO2022JPESC = 50221; + private const int ISO2022JPSISO = 50222; + private const int ISOKorean = 50225; + private const int ISOSimplifiedCN = 50227; + private const int EUCJP = 51932; + private const int ChineseHZ = 52936; // HZ has ~}~{~~ sequences + + // 51936 is the same as 936 + private const int DuplicateEUCCN = 51936; + private const int EUCCN = 936; + + private const int EUCKR = 51949; + + // Latin 1 & ASCII Code Pages + internal const int CodePageASCII = 20127; // ASCII + internal const int ISO_8859_1 = 28591; // Latin1 + + // ISCII + private const int ISCIIAssemese = 57006; + private const int ISCIIBengali = 57003; + private const int ISCIIDevanagari = 57002; + private const int ISCIIGujarathi = 57010; + private const int ISCIIKannada = 57008; + private const int ISCIIMalayalam = 57009; + private const int ISCIIOriya = 57007; + private const int ISCIIPanjabi = 57011; + private const int ISCIITamil = 57004; + private const int ISCIITelugu = 57005; + + // GB18030 + private const int GB18030 = 54936; + + // Other + private const int ISO_8859_8I = 38598; + private const int ISO_8859_8_Visual = 28598; + + // 50229 is currently unsupported // "Chinese Traditional (ISO-2022)" + private const int ENC50229 = 50229; + + // Special code pages + private const int CodePageUTF7 = 65000; + private const int CodePageUTF8 = 65001; + private const int CodePageUTF32 = 12000; + private const int CodePageUTF32BE = 12001; + + internal int _codePage = 0; + + internal CodePageDataItem _dataItem = null; + + // Because of encoders we may be read only + [OptionalField(VersionAdded = 2)] + private bool _isReadOnly = true; + + // Encoding (encoder) fallback + internal EncoderFallback encoderFallback = null; + internal DecoderFallback decoderFallback = null; + + protected Encoding() : this(0) + { + } + + + protected Encoding(int codePage) + { + // Validate code page + if (codePage < 0) + { + throw new ArgumentOutOfRangeException(nameof(codePage)); + } + + // Remember code page + _codePage = codePage; + + // Use default encoder/decoder fallbacks + this.SetDefaultFallbacks(); + } + + // This constructor is needed to allow any sub-classing implementation to provide encoder/decoder fallback objects + // because the encoding object is always created as read-only object and don't allow setting encoder/decoder fallback + // after the creation is done. + protected Encoding(int codePage, EncoderFallback encoderFallback, DecoderFallback decoderFallback) + { + // Validate code page + if (codePage < 0) + { + throw new ArgumentOutOfRangeException(nameof(codePage)); + } + + // Remember code page + _codePage = codePage; + + this.encoderFallback = encoderFallback ?? new InternalEncoderBestFitFallback(this); + this.decoderFallback = decoderFallback ?? new InternalDecoderBestFitFallback(this); + } + + // Default fallback that we'll use. + internal virtual void SetDefaultFallbacks() + { + // For UTF-X encodings, we use a replacement fallback with an "\xFFFD" string, + // For ASCII we use "?" replacement fallback, etc. + encoderFallback = new InternalEncoderBestFitFallback(this); + decoderFallback = new InternalDecoderBestFitFallback(this); + } + + // Converts a byte array from one encoding to another. The bytes in the + // bytes array are converted from srcEncoding to + // dstEncoding, and the returned value is a new byte array + // containing the result of the conversion. + // + public static byte[] Convert(Encoding srcEncoding, Encoding dstEncoding, + byte[] bytes) + { + if (bytes == null) + throw new ArgumentNullException(nameof(bytes)); + + return Convert(srcEncoding, dstEncoding, bytes, 0, bytes.Length); + } + + // Converts a range of bytes in a byte array from one encoding to another. + // This method converts count bytes from bytes starting at + // index index from srcEncoding to dstEncoding, and + // returns a new byte array containing the result of the conversion. + // + public static byte[] Convert(Encoding srcEncoding, Encoding dstEncoding, + byte[] bytes, int index, int count) + { + if (srcEncoding == null || dstEncoding == null) + { + throw new ArgumentNullException((srcEncoding == null ? nameof(srcEncoding) : nameof(dstEncoding)), + SR.ArgumentNull_Array); + } + if (bytes == null) + { + throw new ArgumentNullException(nameof(bytes), + SR.ArgumentNull_Array); + } + + return dstEncoding.GetBytes(srcEncoding.GetChars(bytes, index, count)); + } + + public static void RegisterProvider(EncodingProvider provider) + { + // Parameters validated inside EncodingProvider + EncodingProvider.AddProvider(provider); + } + + public static Encoding GetEncoding(int codepage) + { + Encoding result = EncodingProvider.GetEncodingFromProvider(codepage); + if (result != null) + return result; + + // + // NOTE: If you add a new encoding that can be retrieved by codepage, be sure to + // add the corresponding item in EncodingTable. + // Otherwise, the code below will throw exception when trying to call + // EncodingTable.GetDataItem(). + // + if (codepage < 0 || codepage > 65535) + { + throw new ArgumentOutOfRangeException( + nameof(codepage), SR.Format(SR.ArgumentOutOfRange_Range, 0, 65535)); + } + + + switch (codepage) + { + case CodePageDefault: return Default; // 0 + case CodePageUnicode: return Unicode; // 1200 + case CodePageBigEndian: return BigEndianUnicode; // 1201 + case CodePageUTF32: return UTF32; // 12000 + case CodePageUTF32BE: return BigEndianUTF32; // 12001 + case CodePageUTF7: return UTF7; // 65000 + case CodePageUTF8: return UTF8; // 65001 + case CodePageASCII: return ASCII; // 20127 + case ISO_8859_1: return Latin1; // 28591 + + // We don't allow the following special code page values that Win32 allows. + case CodePageNoOEM: // 1 CP_OEMCP + case CodePageNoMac: // 2 CP_MACCP + case CodePageNoThread: // 3 CP_THREAD_ACP + case CodePageNoSymbol: // 42 CP_SYMBOL + throw new ArgumentException(SR.Format(SR.Argument_CodepageNotSupported, codepage), nameof(codepage)); + } + + // Is it a valid code page? + if (EncodingTable.GetCodePageDataItem(codepage) == null) + { + throw new NotSupportedException( + SR.Format(SR.NotSupported_NoCodepageData, codepage)); + } + + return UTF8; + } + + public static Encoding GetEncoding(int codepage, + EncoderFallback encoderFallback, DecoderFallback decoderFallback) + { + Encoding baseEncoding = EncodingProvider.GetEncodingFromProvider(codepage, encoderFallback, decoderFallback); + + if (baseEncoding != null) + return baseEncoding; + + // Get the default encoding (which is cached and read only) + baseEncoding = GetEncoding(codepage); + + // Clone it and set the fallback + Encoding fallbackEncoding = (Encoding)baseEncoding.Clone(); + fallbackEncoding.EncoderFallback = encoderFallback; + fallbackEncoding.DecoderFallback = decoderFallback; + + return fallbackEncoding; + } + + // Returns an Encoding object for a given name or a given code page value. + // + public static Encoding GetEncoding(String name) + { + Encoding baseEncoding = EncodingProvider.GetEncodingFromProvider(name); + if (baseEncoding != null) + return baseEncoding; + + // + // NOTE: If you add a new encoding that can be requested by name, be sure to + // add the corresponding item in EncodingTable. + // Otherwise, the code below will throw exception when trying to call + // EncodingTable.GetCodePageFromName(). + // + return GetEncoding(EncodingTable.GetCodePageFromName(name)); + } + + // Returns an Encoding object for a given name or a given code page value. + // + public static Encoding GetEncoding(String name, + EncoderFallback encoderFallback, DecoderFallback decoderFallback) + { + Encoding baseEncoding = EncodingProvider.GetEncodingFromProvider(name, encoderFallback, decoderFallback); + if (baseEncoding != null) + return baseEncoding; + + // + // NOTE: If you add a new encoding that can be requested by name, be sure to + // add the corresponding item in EncodingTable. + // Otherwise, the code below will throw exception when trying to call + // EncodingTable.GetCodePageFromName(). + // + return (GetEncoding(EncodingTable.GetCodePageFromName(name), encoderFallback, decoderFallback)); + } + + // Return a list of all EncodingInfo objects describing all of our encodings + public static EncodingInfo[] GetEncodings() + { + return EncodingTable.GetEncodings(); + } + + public virtual byte[] GetPreamble() + { + return Array.Empty(); + } + + public virtual ReadOnlySpan Preamble => GetPreamble(); + + private void GetDataItem() + { + if (_dataItem == null) + { + _dataItem = EncodingTable.GetCodePageDataItem(_codePage); + if (_dataItem == null) + { + throw new NotSupportedException(SR.Format(SR.NotSupported_NoCodepageData, _codePage)); + } + } + } + + // Returns the name for this encoding that can be used with mail agent body tags. + // If the encoding may not be used, the string is empty. + + public virtual String BodyName + { + get + { + if (_dataItem == null) + { + GetDataItem(); + } + return (_dataItem.BodyName); + } + } + + // Returns the human-readable description of the encoding ( e.g. Hebrew (DOS)). +#if PROJECTN + public virtual String EncodingName + { + get + { + string encodingName = GetLocalizedEncodingNameResource(this.CodePage); + if (encodingName == null) + { + throw new NotSupportedException(SR.Format(SR.MissingEncodingNameResource, this.CodePage)); + } + + if (encodingName.StartsWith("Globalization_cp_", StringComparison.Ordinal)) + { + // On ProjectN, resource strings are stripped from retail builds and replaced by + // their identifier names. Since this property is meant to be a localized string, + // but we don't localize ProjectN, we specifically need to do something reasonable + // in this case. This currently returns the English name of the encoding from a + // static data table. + encodingName = EncodingTable.GetCodePageDataItem(this.CodePage).EnglishName; + if (encodingName == null) + { + throw new NotSupportedException(SR.Format(SR.MissingEncodingNameResource, this.WebName, this.CodePage)); + } + } + return encodingName; + } + } + + private static string GetLocalizedEncodingNameResource(int codePage) + { + switch (codePage) + { + case 1200: return SR.Globalization_cp_1200; + case 1201: return SR.Globalization_cp_1201; + case 12000: return SR.Globalization_cp_12000; + case 12001: return SR.Globalization_cp_12001; + case 20127: return SR.Globalization_cp_20127; + case 28591: return SR.Globalization_cp_28591; + case 65000: return SR.Globalization_cp_65000; + case 65001: return SR.Globalization_cp_65001; + default: return null; + } + } +#else + public virtual String EncodingName + { + get + { + return SR.GetResourceString("Globalization_cp_" + _codePage.ToString()); + } + } +#endif + // Returns the name for this encoding that can be used with mail agent header + // tags. If the encoding may not be used, the string is empty. + + public virtual String HeaderName + { + get + { + if (_dataItem == null) + { + GetDataItem(); + } + return (_dataItem.HeaderName); + } + } + + // Returns the IANA preferred name for this encoding. + public virtual String WebName + { + get + { + if (_dataItem == null) + { + GetDataItem(); + } + return (_dataItem.WebName); + } + } + + // Returns the windows code page that most closely corresponds to this encoding. + + public virtual int WindowsCodePage + { + get + { + if (_dataItem == null) + { + GetDataItem(); + } + return (_dataItem.UIFamilyCodePage); + } + } + + + // True if and only if the encoding is used for display by browsers clients. + + public virtual bool IsBrowserDisplay + { + get + { + if (_dataItem == null) + { + GetDataItem(); + } + return ((_dataItem.Flags & MIMECONTF_BROWSER) != 0); + } + } + + // True if and only if the encoding is used for saving by browsers clients. + + public virtual bool IsBrowserSave + { + get + { + if (_dataItem == null) + { + GetDataItem(); + } + return ((_dataItem.Flags & MIMECONTF_SAVABLE_BROWSER) != 0); + } + } + + // True if and only if the encoding is used for display by mail and news clients. + + public virtual bool IsMailNewsDisplay + { + get + { + if (_dataItem == null) + { + GetDataItem(); + } + return ((_dataItem.Flags & MIMECONTF_MAILNEWS) != 0); + } + } + + + // True if and only if the encoding is used for saving documents by mail and + // news clients + + public virtual bool IsMailNewsSave + { + get + { + if (_dataItem == null) + { + GetDataItem(); + } + return ((_dataItem.Flags & MIMECONTF_SAVABLE_MAILNEWS) != 0); + } + } + + // True if and only if the encoding only uses single byte code points. (Ie, ASCII, 1252, etc) + + public virtual bool IsSingleByte + { + get + { + return false; + } + } + + + public EncoderFallback EncoderFallback + { + get + { + return encoderFallback; + } + + set + { + if (this.IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + + if (value == null) + throw new ArgumentNullException(nameof(value)); + + encoderFallback = value; + } + } + + + public DecoderFallback DecoderFallback + { + get + { + return decoderFallback; + } + + set + { + if (this.IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + + if (value == null) + throw new ArgumentNullException(nameof(value)); + + decoderFallback = value; + } + } + + + public virtual Object Clone() + { + Encoding newEncoding = (Encoding)this.MemberwiseClone(); + + // New one should be readable + newEncoding._isReadOnly = false; + return newEncoding; + } + + + public bool IsReadOnly + { + get + { + return (_isReadOnly); + } + } + + // Returns an encoding for the ASCII character set. The returned encoding + // will be an instance of the ASCIIEncoding class. + + public static Encoding ASCII => ASCIIEncoding.s_default; + + // Returns an encoding for the Latin1 character set. The returned encoding + // will be an instance of the Latin1Encoding class. + // + // This is for our optimizations + private static Encoding Latin1 => Latin1Encoding.s_default; + + // Returns the number of bytes required to encode the given character + // array. + // + public virtual int GetByteCount(char[] chars) + { + if (chars == null) + { + throw new ArgumentNullException(nameof(chars), + SR.ArgumentNull_Array); + } + + return GetByteCount(chars, 0, chars.Length); + } + + public virtual int GetByteCount(String s) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + + char[] chars = s.ToCharArray(); + return GetByteCount(chars, 0, chars.Length); + } + + // Returns the number of bytes required to encode a range of characters in + // a character array. + // + public abstract int GetByteCount(char[] chars, int index, int count); + + // Returns the number of bytes required to encode a string range. + // + public int GetByteCount(string s, int index, int count) + { + if (s == null) + throw new ArgumentNullException(nameof(s), + SR.ArgumentNull_String); + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index), + SR.ArgumentOutOfRange_NeedNonNegNum); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), + SR.ArgumentOutOfRange_NeedNonNegNum); + if (index > s.Length - count) + throw new ArgumentOutOfRangeException(nameof(index), + SR.ArgumentOutOfRange_IndexCount); + + unsafe + { + fixed (char* pChar = s) + { + return GetByteCount(pChar + index, count); + } + } + } + + // We expect this to be the workhorse for NLS encodings + // unfortunately for existing overrides, it has to call the [] version, + // which is really slow, so this method should be avoided if you're calling + // a 3rd party encoding. + [CLSCompliant(false)] + public virtual unsafe int GetByteCount(char* chars, int count) + { + // Validate input parameters + if (chars == null) + throw new ArgumentNullException(nameof(chars), + SR.ArgumentNull_Array); + + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), + SR.ArgumentOutOfRange_NeedNonNegNum); + + char[] arrChar = new char[count]; + int index; + + for (index = 0; index < count; index++) + arrChar[index] = chars[index]; + + return GetByteCount(arrChar, 0, count); + } + + public virtual unsafe int GetByteCount(ReadOnlySpan chars) + { + fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) + { + return GetByteCount(charsPtr, chars.Length); + } + } + + // For NLS Encodings, workhorse takes an encoder (may be null) + // Always validate parameters before calling internal version, which will only assert. + internal virtual unsafe int GetByteCount(char* chars, int count, EncoderNLS encoder) + { + Debug.Assert(chars != null); + Debug.Assert(count >= 0); + + return GetByteCount(chars, count); + } + + // Returns a byte array containing the encoded representation of the given + // character array. + // + public virtual byte[] GetBytes(char[] chars) + { + if (chars == null) + { + throw new ArgumentNullException(nameof(chars), + SR.ArgumentNull_Array); + } + return GetBytes(chars, 0, chars.Length); + } + + // Returns a byte array containing the encoded representation of a range + // of characters in a character array. + // + public virtual byte[] GetBytes(char[] chars, int index, int count) + { + byte[] result = new byte[GetByteCount(chars, index, count)]; + GetBytes(chars, index, count, result, 0); + return result; + } + + // Encodes a range of characters in a character array into a range of bytes + // in a byte array. An exception occurs if the byte array is not large + // enough to hold the complete encoding of the characters. The + // GetByteCount method can be used to determine the exact number of + // bytes that will be produced for a given range of characters. + // Alternatively, the GetMaxByteCount method can be used to + // determine the maximum number of bytes that will be produced for a given + // number of characters, regardless of the actual character values. + // + public abstract int GetBytes(char[] chars, int charIndex, int charCount, + byte[] bytes, int byteIndex); + + // Returns a byte array containing the encoded representation of the given + // string. + // + public virtual byte[] GetBytes(String s) + { + if (s == null) + throw new ArgumentNullException(nameof(s), + SR.ArgumentNull_String); + + int byteCount = GetByteCount(s); + byte[] bytes = new byte[byteCount]; + int bytesReceived = GetBytes(s, 0, s.Length, bytes, 0); + Debug.Assert(byteCount == bytesReceived); + return bytes; + } + + // Returns a byte array containing the encoded representation of the given + // string range. + // + public byte[] GetBytes(string s, int index, int count) + { + if (s == null) + throw new ArgumentNullException(nameof(s), + SR.ArgumentNull_String); + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index), + SR.ArgumentOutOfRange_NeedNonNegNum); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), + SR.ArgumentOutOfRange_NeedNonNegNum); + if (index > s.Length - count) + throw new ArgumentOutOfRangeException(nameof(index), + SR.ArgumentOutOfRange_IndexCount); + + unsafe + { + fixed (char* pChar = s) + { + int byteCount = GetByteCount(pChar + index, count); + if (byteCount == 0) + return Array.Empty(); + + byte[] bytes = new byte[byteCount]; + fixed (byte* pBytes = &bytes[0]) + { + int bytesReceived = GetBytes(pChar + index, count, pBytes, byteCount); + Debug.Assert(byteCount == bytesReceived); + } + return bytes; + } + } + } + + public virtual int GetBytes(String s, int charIndex, int charCount, + byte[] bytes, int byteIndex) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + return GetBytes(s.ToCharArray(), charIndex, charCount, bytes, byteIndex); + } + + // This is our internal workhorse + // Always validate parameters before calling internal version, which will only assert. + internal virtual unsafe int GetBytes(char* chars, int charCount, + byte* bytes, int byteCount, EncoderNLS encoder) + { + return GetBytes(chars, charCount, bytes, byteCount); + } + + // We expect this to be the workhorse for NLS Encodings, but for existing + // ones we need a working (if slow) default implementation) + // + // WARNING WARNING WARNING + // + // WARNING: If this breaks it could be a security threat. Obviously we + // call this internally, so you need to make sure that your pointers, counts + // and indexes are correct when you call this method. + // + // In addition, we have internal code, which will be marked as "safe" calling + // this code. However this code is dependent upon the implementation of an + // external GetBytes() method, which could be overridden by a third party and + // the results of which cannot be guaranteed. We use that result to copy + // the byte[] to our byte* output buffer. If the result count was wrong, we + // could easily overflow our output buffer. Therefore we do an extra test + // when we copy the buffer so that we don't overflow byteCount either. + + [CLSCompliant(false)] + public virtual unsafe int GetBytes(char* chars, int charCount, + byte* bytes, int byteCount) + { + // Validate input parameters + if (bytes == null || chars == null) + throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars), + SR.ArgumentNull_Array); + + if (charCount < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)), + SR.ArgumentOutOfRange_NeedNonNegNum); + + // Get the char array to convert + char[] arrChar = new char[charCount]; + + int index; + for (index = 0; index < charCount; index++) + arrChar[index] = chars[index]; + + // Get the byte array to fill + byte[] arrByte = new byte[byteCount]; + + // Do the work + int result = GetBytes(arrChar, 0, charCount, arrByte, 0); + + Debug.Assert(result <= byteCount, "[Encoding.GetBytes]Returned more bytes than we have space for"); + + // Copy the byte array + // WARNING: We MUST make sure that we don't copy too many bytes. We can't + // rely on result because it could be a 3rd party implementation. We need + // to make sure we never copy more than byteCount bytes no matter the value + // of result + if (result < byteCount) + byteCount = result; + + // Copy the data, don't overrun our array! + for (index = 0; index < byteCount; index++) + bytes[index] = arrByte[index]; + + return byteCount; + } + + public virtual unsafe int GetBytes(ReadOnlySpan chars, Span bytes) + { + fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) + fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + { + return GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length); + } + } + + // Returns the number of characters produced by decoding the given byte + // array. + // + public virtual int GetCharCount(byte[] bytes) + { + if (bytes == null) + { + throw new ArgumentNullException(nameof(bytes), + SR.ArgumentNull_Array); + } + return GetCharCount(bytes, 0, bytes.Length); + } + + // Returns the number of characters produced by decoding a range of bytes + // in a byte array. + // + public abstract int GetCharCount(byte[] bytes, int index, int count); + + // We expect this to be the workhorse for NLS Encodings, but for existing + // ones we need a working (if slow) default implementation) + [CLSCompliant(false)] + public virtual unsafe int GetCharCount(byte* bytes, int count) + { + // Validate input parameters + if (bytes == null) + throw new ArgumentNullException(nameof(bytes), + SR.ArgumentNull_Array); + + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), + SR.ArgumentOutOfRange_NeedNonNegNum); + + byte[] arrbyte = new byte[count]; + int index; + + for (index = 0; index < count; index++) + arrbyte[index] = bytes[index]; + + return GetCharCount(arrbyte, 0, count); + } + + public virtual unsafe int GetCharCount(ReadOnlySpan bytes) + { + fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + { + return GetCharCount(bytesPtr, bytes.Length); + } + } + + // This is our internal workhorse + // Always validate parameters before calling internal version, which will only assert. + internal virtual unsafe int GetCharCount(byte* bytes, int count, DecoderNLS decoder) + { + return GetCharCount(bytes, count); + } + + // Returns a character array containing the decoded representation of a + // given byte array. + // + public virtual char[] GetChars(byte[] bytes) + { + if (bytes == null) + { + throw new ArgumentNullException(nameof(bytes), + SR.ArgumentNull_Array); + } + return GetChars(bytes, 0, bytes.Length); + } + + // Returns a character array containing the decoded representation of a + // range of bytes in a byte array. + // + public virtual char[] GetChars(byte[] bytes, int index, int count) + { + char[] result = new char[GetCharCount(bytes, index, count)]; + GetChars(bytes, index, count, result, 0); + return result; + } + + // Decodes a range of bytes in a byte array into a range of characters in a + // character array. An exception occurs if the character array is not large + // enough to hold the complete decoding of the bytes. The + // GetCharCount method can be used to determine the exact number of + // characters that will be produced for a given range of bytes. + // Alternatively, the GetMaxCharCount method can be used to + // determine the maximum number of characters that will be produced for a + // given number of bytes, regardless of the actual byte values. + // + + public abstract int GetChars(byte[] bytes, int byteIndex, int byteCount, + char[] chars, int charIndex); + + + // We expect this to be the workhorse for NLS Encodings, but for existing + // ones we need a working (if slow) default implementation) + // + // WARNING WARNING WARNING + // + // WARNING: If this breaks it could be a security threat. Obviously we + // call this internally, so you need to make sure that your pointers, counts + // and indexes are correct when you call this method. + // + // In addition, we have internal code, which will be marked as "safe" calling + // this code. However this code is dependent upon the implementation of an + // external GetChars() method, which could be overridden by a third party and + // the results of which cannot be guaranteed. We use that result to copy + // the char[] to our char* output buffer. If the result count was wrong, we + // could easily overflow our output buffer. Therefore we do an extra test + // when we copy the buffer so that we don't overflow charCount either. + + [CLSCompliant(false)] + public virtual unsafe int GetChars(byte* bytes, int byteCount, + char* chars, int charCount) + { + // Validate input parameters + if (chars == null || bytes == null) + throw new ArgumentNullException(chars == null ? nameof(chars) : nameof(bytes), + SR.ArgumentNull_Array); + + if (byteCount < 0 || charCount < 0) + throw new ArgumentOutOfRangeException((byteCount < 0 ? nameof(byteCount) : nameof(charCount)), + SR.ArgumentOutOfRange_NeedNonNegNum); + + // Get the byte array to convert + byte[] arrByte = new byte[byteCount]; + + int index; + for (index = 0; index < byteCount; index++) + arrByte[index] = bytes[index]; + + // Get the char array to fill + char[] arrChar = new char[charCount]; + + // Do the work + int result = GetChars(arrByte, 0, byteCount, arrChar, 0); + + Debug.Assert(result <= charCount, "[Encoding.GetChars]Returned more chars than we have space for"); + + // Copy the char array + // WARNING: We MUST make sure that we don't copy too many chars. We can't + // rely on result because it could be a 3rd party implementation. We need + // to make sure we never copy more than charCount chars no matter the value + // of result + if (result < charCount) + charCount = result; + + // Copy the data, don't overrun our array! + for (index = 0; index < charCount; index++) + chars[index] = arrChar[index]; + + return charCount; + } + + public virtual unsafe int GetChars(ReadOnlySpan bytes, Span chars) + { + fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) + { + return GetChars(bytesPtr, bytes.Length, charsPtr, chars.Length); + } + } + + // This is our internal workhorse + // Always validate parameters before calling internal version, which will only assert. + internal virtual unsafe int GetChars(byte* bytes, int byteCount, + char* chars, int charCount, DecoderNLS decoder) + { + return GetChars(bytes, byteCount, chars, charCount); + } + + + [CLSCompliant(false)] + public unsafe string GetString(byte* bytes, int byteCount) + { + if (bytes == null) + throw new ArgumentNullException(nameof(bytes), SR.ArgumentNull_Array); + + if (byteCount < 0) + throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_NeedNonNegNum); + + return String.CreateStringFromEncoding(bytes, byteCount, this); + } + + public unsafe string GetString(ReadOnlySpan bytes) + { + fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + { + return GetString(bytesPtr, bytes.Length); + } + } + + + // Returns the code page identifier of this encoding. The returned value is + // an integer between 0 and 65535 if the encoding has a code page + // identifier, or -1 if the encoding does not represent a code page. + // + + public virtual int CodePage + { + get + { + return _codePage; + } + } + + // IsAlwaysNormalized + // Returns true if the encoding is always normalized for the specified encoding form + public bool IsAlwaysNormalized() + { + return this.IsAlwaysNormalized(NormalizationForm.FormC); + } + + public virtual bool IsAlwaysNormalized(NormalizationForm form) + { + // Assume false unless the encoding knows otherwise + return false; + } + + // Returns a Decoder object for this encoding. The returned object + // can be used to decode a sequence of bytes into a sequence of characters. + // Contrary to the GetChars family of methods, a Decoder can + // convert partial sequences of bytes into partial sequences of characters + // by maintaining the appropriate state between the conversions. + // + // This default implementation returns a Decoder that simply + // forwards calls to the GetCharCount and GetChars methods to + // the corresponding methods of this encoding. Encodings that require state + // to be maintained between successive conversions should override this + // method and return an instance of an appropriate Decoder + // implementation. + // + + public virtual Decoder GetDecoder() + { + return new DefaultDecoder(this); + } + + // Returns an Encoder object for this encoding. The returned object + // can be used to encode a sequence of characters into a sequence of bytes. + // Contrary to the GetBytes family of methods, an Encoder can + // convert partial sequences of characters into partial sequences of bytes + // by maintaining the appropriate state between the conversions. + // + // This default implementation returns an Encoder that simply + // forwards calls to the GetByteCount and GetBytes methods to + // the corresponding methods of this encoding. Encodings that require state + // to be maintained between successive conversions should override this + // method and return an instance of an appropriate Encoder + // implementation. + // + + public virtual Encoder GetEncoder() + { + return new DefaultEncoder(this); + } + + // Returns the maximum number of bytes required to encode a given number of + // characters. This method can be used to determine an appropriate buffer + // size for byte arrays passed to the GetBytes method of this + // encoding or the GetBytes method of an Encoder for this + // encoding. All encodings must guarantee that no buffer overflow + // exceptions will occur if buffers are sized according to the results of + // this method. + // + // WARNING: If you're using something besides the default replacement encoder fallback, + // then you could have more bytes than this returned from an actual call to GetBytes(). + // + public abstract int GetMaxByteCount(int charCount); + + // Returns the maximum number of characters produced by decoding a given + // number of bytes. This method can be used to determine an appropriate + // buffer size for character arrays passed to the GetChars method of + // this encoding or the GetChars method of a Decoder for this + // encoding. All encodings must guarantee that no buffer overflow + // exceptions will occur if buffers are sized according to the results of + // this method. + // + public abstract int GetMaxCharCount(int byteCount); + + // Returns a string containing the decoded representation of a given byte + // array. + // + public virtual String GetString(byte[] bytes) + { + if (bytes == null) + throw new ArgumentNullException(nameof(bytes), + SR.ArgumentNull_Array); + + return GetString(bytes, 0, bytes.Length); + } + + // Returns a string containing the decoded representation of a range of + // bytes in a byte array. + // + // Internally we override this for performance + // + public virtual String GetString(byte[] bytes, int index, int count) + { + return new String(GetChars(bytes, index, count)); + } + + // Returns an encoding for Unicode format. The returned encoding will be + // an instance of the UnicodeEncoding class. + // + // It will use little endian byte order, but will detect + // input in big endian if it finds a byte order mark per Unicode 2.0. + + public static Encoding Unicode => UnicodeEncoding.s_littleEndianDefault; + + // Returns an encoding for Unicode format. The returned encoding will be + // an instance of the UnicodeEncoding class. + // + // It will use big endian byte order, but will detect + // input in little endian if it finds a byte order mark per Unicode 2.0. + + public static Encoding BigEndianUnicode => UnicodeEncoding.s_bigEndianDefault; + + // Returns an encoding for the UTF-7 format. The returned encoding will be + // an instance of the UTF7Encoding class. + + public static Encoding UTF7 => UTF7Encoding.s_default; + + // Returns an encoding for the UTF-8 format. The returned encoding will be + // an instance of the UTF8Encoding class. + + public static Encoding UTF8 => UTF8Encoding.s_default; + + // Returns an encoding for the UTF-32 format. The returned encoding will be + // an instance of the UTF32Encoding class. + + public static Encoding UTF32 => UTF32Encoding.s_default; + + // Returns an encoding for the UTF-32 format. The returned encoding will be + // an instance of the UTF32Encoding class. + // + // It will use big endian byte order. + + private static Encoding BigEndianUTF32 => UTF32Encoding.s_bigEndianDefault; + + public override bool Equals(Object value) + { + Encoding that = value as Encoding; + if (that != null) + return (_codePage == that._codePage) && + (EncoderFallback.Equals(that.EncoderFallback)) && + (DecoderFallback.Equals(that.DecoderFallback)); + return (false); + } + + + public override int GetHashCode() + { + return _codePage + this.EncoderFallback.GetHashCode() + this.DecoderFallback.GetHashCode(); + } + + internal virtual char[] GetBestFitUnicodeToBytesData() + { + // Normally we don't have any best fit data. + return Array.Empty(); + } + + internal virtual char[] GetBestFitBytesToUnicodeData() + { + // Normally we don't have any best fit data. + return Array.Empty(); + } + + internal void ThrowBytesOverflow() + { + // Special message to include fallback type in case fallback's GetMaxCharCount is broken + // This happens if user has implemented an encoder fallback with a broken GetMaxCharCount + throw new ArgumentException( + SR.Format(SR.Argument_EncodingConversionOverflowBytes, EncodingName, EncoderFallback.GetType()), "bytes"); + } + + internal void ThrowBytesOverflow(EncoderNLS encoder, bool nothingEncoded) + { + if (encoder == null || encoder._throwOnOverflow || nothingEncoded) + { + if (encoder != null && encoder.InternalHasFallbackBuffer) + encoder.FallbackBuffer.InternalReset(); + // Special message to include fallback type in case fallback's GetMaxCharCount is broken + // This happens if user has implemented an encoder fallback with a broken GetMaxCharCount + ThrowBytesOverflow(); + } + + // If we didn't throw, we are in convert and have to remember our flushing + encoder.ClearMustFlush(); + } + + internal void ThrowCharsOverflow() + { + // Special message to include fallback type in case fallback's GetMaxCharCount is broken + // This happens if user has implemented a decoder fallback with a broken GetMaxCharCount + throw new ArgumentException( + SR.Format(SR.Argument_EncodingConversionOverflowChars, EncodingName, DecoderFallback.GetType()), "chars"); + } + + internal void ThrowCharsOverflow(DecoderNLS decoder, bool nothingDecoded) + { + if (decoder == null || decoder._throwOnOverflow || nothingDecoded) + { + if (decoder != null && decoder.InternalHasFallbackBuffer) + decoder.FallbackBuffer.InternalReset(); + + // Special message to include fallback type in case fallback's GetMaxCharCount is broken + // This happens if user has implemented a decoder fallback with a broken GetMaxCharCount + ThrowCharsOverflow(); + } + + // If we didn't throw, we are in convert and have to remember our flushing + decoder.ClearMustFlush(); + } + + internal sealed class DefaultEncoder : Encoder, IObjectReference + { + private Encoding _encoding; + + public DefaultEncoder(Encoding encoding) + { + _encoding = encoding; + } + + public Object GetRealObject(StreamingContext context) + { + throw new PlatformNotSupportedException(); + } + + // Returns the number of bytes the next call to GetBytes will + // produce if presented with the given range of characters and the given + // value of the flush parameter. The returned value takes into + // account the state in which the encoder was left following the last call + // to GetBytes. The state of the encoder is not affected by a call + // to this method. + // + + public override int GetByteCount(char[] chars, int index, int count, bool flush) + { + return _encoding.GetByteCount(chars, index, count); + } + + public unsafe override int GetByteCount(char* chars, int count, bool flush) + { + return _encoding.GetByteCount(chars, count); + } + + // Encodes a range of characters in a character array into a range of bytes + // in a byte array. The method encodes charCount characters from + // chars starting at index charIndex, storing the resulting + // bytes in bytes starting at index byteIndex. The encoding + // takes into account the state in which the encoder was left following the + // last call to this method. The flush parameter indicates whether + // the encoder should flush any shift-states and partial characters at the + // end of the conversion. To ensure correct termination of a sequence of + // blocks of encoded bytes, the last call to GetBytes should specify + // a value of true for the flush parameter. + // + // An exception occurs if the byte array is not large enough to hold the + // complete encoding of the characters. The GetByteCount method can + // be used to determine the exact number of bytes that will be produced for + // a given range of characters. Alternatively, the GetMaxByteCount + // method of the Encoding that produced this encoder can be used to + // determine the maximum number of bytes that will be produced for a given + // number of characters, regardless of the actual character values. + // + + public override int GetBytes(char[] chars, int charIndex, int charCount, + byte[] bytes, int byteIndex, bool flush) + { + return _encoding.GetBytes(chars, charIndex, charCount, bytes, byteIndex); + } + + public unsafe override int GetBytes(char* chars, int charCount, + byte* bytes, int byteCount, bool flush) + { + return _encoding.GetBytes(chars, charCount, bytes, byteCount); + } + } + + internal sealed class DefaultDecoder : Decoder, IObjectReference + { + private Encoding _encoding; + + public DefaultDecoder(Encoding encoding) + { + _encoding = encoding; + } + + public Object GetRealObject(StreamingContext context) + { + throw new PlatformNotSupportedException(); + } + + // Returns the number of characters the next call to GetChars will + // produce if presented with the given range of bytes. The returned value + // takes into account the state in which the decoder was left following the + // last call to GetChars. The state of the decoder is not affected + // by a call to this method. + // + + public override int GetCharCount(byte[] bytes, int index, int count) + { + return GetCharCount(bytes, index, count, false); + } + + public override int GetCharCount(byte[] bytes, int index, int count, bool flush) + { + return _encoding.GetCharCount(bytes, index, count); + } + + public unsafe override int GetCharCount(byte* bytes, int count, bool flush) + { + // By default just call the encoding version, no flush by default + return _encoding.GetCharCount(bytes, count); + } + + // Decodes a range of bytes in a byte array into a range of characters + // in a character array. The method decodes byteCount bytes from + // bytes starting at index byteIndex, storing the resulting + // characters in chars starting at index charIndex. The + // decoding takes into account the state in which the decoder was left + // following the last call to this method. + // + // An exception occurs if the character array is not large enough to + // hold the complete decoding of the bytes. The GetCharCount method + // can be used to determine the exact number of characters that will be + // produced for a given range of bytes. Alternatively, the + // GetMaxCharCount method of the Encoding that produced this + // decoder can be used to determine the maximum number of characters that + // will be produced for a given number of bytes, regardless of the actual + // byte values. + // + + public override int GetChars(byte[] bytes, int byteIndex, int byteCount, + char[] chars, int charIndex) + { + return GetChars(bytes, byteIndex, byteCount, chars, charIndex, false); + } + + public override int GetChars(byte[] bytes, int byteIndex, int byteCount, + char[] chars, int charIndex, bool flush) + { + return _encoding.GetChars(bytes, byteIndex, byteCount, chars, charIndex); + } + + public unsafe override int GetChars(byte* bytes, int byteCount, + char* chars, int charCount, bool flush) + { + // By default just call the encoding's version + return _encoding.GetChars(bytes, byteCount, chars, charCount); + } + } + + internal class EncodingCharBuffer + { + private unsafe char* _chars; + private unsafe char* _charStart; + private unsafe char* _charEnd; + private int _charCountResult = 0; + private Encoding _enc; + private DecoderNLS _decoder; + private unsafe byte* _byteStart; + private unsafe byte* _byteEnd; + private unsafe byte* _bytes; + private DecoderFallbackBuffer _fallbackBuffer; + + internal unsafe EncodingCharBuffer(Encoding enc, DecoderNLS decoder, char* charStart, int charCount, + byte* byteStart, int byteCount) + { + _enc = enc; + _decoder = decoder; + + _chars = charStart; + _charStart = charStart; + _charEnd = charStart + charCount; + + _byteStart = byteStart; + _bytes = byteStart; + _byteEnd = byteStart + byteCount; + + if (_decoder == null) + _fallbackBuffer = enc.DecoderFallback.CreateFallbackBuffer(); + else + _fallbackBuffer = _decoder.FallbackBuffer; + + // If we're getting chars or getting char count we don't expect to have + // to remember fallbacks between calls (so it should be empty) + Debug.Assert(_fallbackBuffer.Remaining == 0, + "[Encoding.EncodingCharBuffer.EncodingCharBuffer]Expected empty fallback buffer for getchars/charcount"); + _fallbackBuffer.InternalInitialize(_bytes, _charEnd); + } + + internal unsafe bool AddChar(char ch, int numBytes) + { + if (_chars != null) + { + if (_chars >= _charEnd) + { + // Throw maybe + _bytes -= numBytes; // Didn't encode these bytes + _enc.ThrowCharsOverflow(_decoder, _bytes <= _byteStart); // Throw? + return false; // No throw, but no store either + } + + *(_chars++) = ch; + } + _charCountResult++; + return true; + } + + internal unsafe bool AddChar(char ch) + { + return AddChar(ch, 1); + } + + + internal unsafe bool AddChar(char ch1, char ch2, int numBytes) + { + // Need room for 2 chars + if (_chars >= _charEnd - 1) + { + // Throw maybe + _bytes -= numBytes; // Didn't encode these bytes + _enc.ThrowCharsOverflow(_decoder, _bytes <= _byteStart); // Throw? + return false; // No throw, but no store either + } + return AddChar(ch1, numBytes) && AddChar(ch2, numBytes); + } + + internal unsafe void AdjustBytes(int count) + { + _bytes += count; + } + + internal unsafe bool MoreData + { + get + { + return _bytes < _byteEnd; + } + } + + // Do we have count more bytes? + internal unsafe bool EvenMoreData(int count) + { + return (_bytes <= _byteEnd - count); + } + + // GetNextByte shouldn't be called unless the caller's already checked more data or even more data, + // but we'll double check just to make sure. + internal unsafe byte GetNextByte() + { + Debug.Assert(_bytes < _byteEnd, "[EncodingCharBuffer.GetNextByte]Expected more date"); + if (_bytes >= _byteEnd) + return 0; + return *(_bytes++); + } + + internal unsafe int BytesUsed + { + get + { + return (int)(_bytes - _byteStart); + } + } + + internal unsafe bool Fallback(byte fallbackByte) + { + // Build our buffer + byte[] byteBuffer = new byte[] { fallbackByte }; + + // Do the fallback and add the data. + return Fallback(byteBuffer); + } + + internal unsafe bool Fallback(byte byte1, byte byte2) + { + // Build our buffer + byte[] byteBuffer = new byte[] { byte1, byte2 }; + + // Do the fallback and add the data. + return Fallback(byteBuffer); + } + + internal unsafe bool Fallback(byte byte1, byte byte2, byte byte3, byte byte4) + { + // Build our buffer + byte[] byteBuffer = new byte[] { byte1, byte2, byte3, byte4 }; + + // Do the fallback and add the data. + return Fallback(byteBuffer); + } + + internal unsafe bool Fallback(byte[] byteBuffer) + { + // Do the fallback and add the data. + if (_chars != null) + { + char* pTemp = _chars; + if (_fallbackBuffer.InternalFallback(byteBuffer, _bytes, ref _chars) == false) + { + // Throw maybe + _bytes -= byteBuffer.Length; // Didn't use how many ever bytes we're falling back + _fallbackBuffer.InternalReset(); // We didn't use this fallback. + _enc.ThrowCharsOverflow(_decoder, _chars == _charStart); // Throw? + return false; // No throw, but no store either + } + _charCountResult += unchecked((int)(_chars - pTemp)); + } + else + { + _charCountResult += _fallbackBuffer.InternalFallback(byteBuffer, _bytes); + } + + return true; + } + + internal unsafe int Count + { + get + { + return _charCountResult; + } + } + } + + internal class EncodingByteBuffer + { + private unsafe byte* _bytes; + private unsafe byte* _byteStart; + private unsafe byte* _byteEnd; + private unsafe char* _chars; + private unsafe char* _charStart; + private unsafe char* _charEnd; + private int _byteCountResult = 0; + private Encoding _enc; + private EncoderNLS _encoder; + internal EncoderFallbackBuffer fallbackBuffer; + + internal unsafe EncodingByteBuffer(Encoding inEncoding, EncoderNLS inEncoder, + byte* inByteStart, int inByteCount, char* inCharStart, int inCharCount) + { + _enc = inEncoding; + _encoder = inEncoder; + + _charStart = inCharStart; + _chars = inCharStart; + _charEnd = inCharStart + inCharCount; + + _bytes = inByteStart; + _byteStart = inByteStart; + _byteEnd = inByteStart + inByteCount; + + if (_encoder == null) + this.fallbackBuffer = _enc.EncoderFallback.CreateFallbackBuffer(); + else + { + this.fallbackBuffer = _encoder.FallbackBuffer; + // If we're not converting we must not have data in our fallback buffer + if (_encoder._throwOnOverflow && _encoder.InternalHasFallbackBuffer && + this.fallbackBuffer.Remaining > 0) + throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, + _encoder.Encoding.EncodingName, _encoder.Fallback.GetType())); + } + fallbackBuffer.InternalInitialize(_chars, _charEnd, _encoder, _bytes != null); + } + + internal unsafe bool AddByte(byte b, int moreBytesExpected) + { + Debug.Assert(moreBytesExpected >= 0, "[EncodingByteBuffer.AddByte]expected non-negative moreBytesExpected"); + if (_bytes != null) + { + if (_bytes >= _byteEnd - moreBytesExpected) + { + // Throw maybe. Check which buffer to back up (only matters if Converting) + this.MovePrevious(true); // Throw if necessary + return false; // No throw, but no store either + } + + *(_bytes++) = b; + } + _byteCountResult++; + return true; + } + + internal unsafe bool AddByte(byte b1) + { + return (AddByte(b1, 0)); + } + + internal unsafe bool AddByte(byte b1, byte b2) + { + return (AddByte(b1, b2, 0)); + } + + internal unsafe bool AddByte(byte b1, byte b2, int moreBytesExpected) + { + return (AddByte(b1, 1 + moreBytesExpected) && AddByte(b2, moreBytesExpected)); + } + + internal unsafe bool AddByte(byte b1, byte b2, byte b3) + { + return AddByte(b1, b2, b3, (int)0); + } + + internal unsafe bool AddByte(byte b1, byte b2, byte b3, int moreBytesExpected) + { + return (AddByte(b1, 2 + moreBytesExpected) && + AddByte(b2, 1 + moreBytesExpected) && + AddByte(b3, moreBytesExpected)); + } + + internal unsafe bool AddByte(byte b1, byte b2, byte b3, byte b4) + { + return (AddByte(b1, 3) && + AddByte(b2, 2) && + AddByte(b3, 1) && + AddByte(b4, 0)); + } + + internal unsafe void MovePrevious(bool bThrow) + { + if (fallbackBuffer.bFallingBack) + fallbackBuffer.MovePrevious(); // don't use last fallback + else + { + Debug.Assert(_chars > _charStart || + ((bThrow == true) && (_bytes == _byteStart)), + "[EncodingByteBuffer.MovePrevious]expected previous data or throw"); + if (_chars > _charStart) + _chars--; // don't use last char + } + + if (bThrow) + _enc.ThrowBytesOverflow(_encoder, _bytes == _byteStart); // Throw? (and reset fallback if not converting) + } + + internal unsafe bool Fallback(char charFallback) + { + // Do the fallback + return fallbackBuffer.InternalFallback(charFallback, ref _chars); + } + + internal unsafe bool MoreData + { + get + { + // See if fallbackBuffer is not empty or if there's data left in chars buffer. + return ((fallbackBuffer.Remaining > 0) || (_chars < _charEnd)); + } + } + + internal unsafe char GetNextChar() + { + // See if there's something in our fallback buffer + char cReturn = fallbackBuffer.InternalGetNextChar(); + + // Nothing in the fallback buffer, return our normal data. + if (cReturn == 0) + { + if (_chars < _charEnd) + cReturn = *(_chars++); + } + + return cReturn; + } + + internal unsafe int CharsUsed + { + get + { + return (int)(_chars - _charStart); + } + } + + internal unsafe int Count + { + get + { + return _byteCountResult; + } + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/EncodingNLS.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/EncodingNLS.cs index 835bf8fb37..d5de9e553a 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Text/EncodingNLS.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/EncodingNLS.cs @@ -3,9 +3,9 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics.Contracts; using System.Collections; using System.Globalization; +using System.Runtime.InteropServices; using System.Threading; namespace System.Text @@ -47,7 +47,6 @@ namespace System.Text if (chars.Length - index < count) throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); // If no input, return 0, avoid fixed empty array problem if (count == 0) @@ -67,7 +66,6 @@ namespace System.Text // Validate input if (s==null) throw new ArgumentNullException("s"); - Contract.EndContractBlock(); fixed (char* pChars = s) return GetByteCount(pChars, s.Length, null); @@ -84,7 +82,6 @@ namespace System.Text if (count < 0) throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Call it with empty encoder return GetByteCount(chars, count, null); @@ -108,15 +105,10 @@ namespace System.Text if (byteIndex < 0 || byteIndex > bytes.Length) throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); int byteCount = bytes.Length - byteIndex; - // Fixed doesn't like empty arrays - if (bytes.Length == 0) - bytes = new byte[1]; - - fixed (char* pChars = s) fixed (byte* pBytes = &bytes[0]) + fixed (char* pChars = s) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null); } @@ -148,7 +140,6 @@ namespace System.Text if (byteIndex < 0 || byteIndex > bytes.Length) throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); // If nothing to encode return 0, avoid fixed problem if (charCount == 0) @@ -157,13 +148,9 @@ namespace System.Text // Just call pointer version int byteCount = bytes.Length - byteIndex; - // Fixed doesn't like empty arrays - if (bytes.Length == 0) - bytes = new byte[1]; - - fixed (char* pChars = chars) fixed (byte* pBytes = &bytes[0]) + fixed (char* pChars = chars) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) // Remember that byteCount is # to decode, not size of array. - return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null); + return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null); } // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) @@ -177,7 +164,6 @@ namespace System.Text if (charCount < 0 || byteCount < 0) throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); return GetBytes(chars, charCount, bytes, byteCount, null); } @@ -200,7 +186,6 @@ namespace System.Text if (bytes.Length - index < count) throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); // If no input just return 0, fixed doesn't like 0 length arrays if (count == 0) @@ -222,7 +207,6 @@ namespace System.Text if (count < 0) throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); return GetCharCount(bytes, count, null); } @@ -246,7 +230,6 @@ namespace System.Text if (charIndex < 0 || charIndex > chars.Length) throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); // If no input, return 0 & avoid fixed problem if (byteCount == 0) @@ -255,11 +238,7 @@ namespace System.Text // Just call pointer version int charCount = chars.Length - charIndex; - // Fixed doesn't like empty arrays - if (chars.Length == 0) - chars = new char[1]; - - fixed (byte* pBytes = bytes) fixed (char* pChars = &chars[0]) + fixed (byte* pBytes = bytes) fixed (char* pChars = &MemoryMarshal.GetReference((Span)chars)) // Remember that charCount is # to decode, not size of array return GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, null); } @@ -275,7 +254,6 @@ namespace System.Text if (charCount < 0 || byteCount < 0) throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); return GetChars(bytes, byteCount, chars, charCount, null); } @@ -298,7 +276,6 @@ namespace System.Text if (bytes.Length - index < count) throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); // Avoid problems with empty input buffer if (count == 0) return String.Empty; diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/Latin1Encoding.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/Latin1Encoding.cs new file mode 100644 index 0000000000..335eb76e3c --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/Latin1Encoding.cs @@ -0,0 +1,884 @@ +// 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.Diagnostics; + +namespace System.Text +{ + // + // Latin1Encoding is a simple override to optimize the GetString version of Latin1Encoding. + // because of the best fit cases we can't do this when encoding the string, only when decoding + // + internal class Latin1Encoding : EncodingNLS + { + // Used by Encoding.Latin1 for lazy initialization + // The initialization code will not be run until a static member of the class is referenced + internal static readonly Latin1Encoding s_default = new Latin1Encoding(); + + // We only use the best-fit table, of which ASCII is a superset for us. + public Latin1Encoding() : base(Encoding.ISO_8859_1) + { + } + + // GetByteCount + // Note: We start by assuming that the output will be the same as count. Having + // an encoder or fallback may change that assumption + internal override unsafe int GetByteCount(char* chars, int charCount, EncoderNLS encoder) + { + // Just need to ASSERT, this is called by something else internal that checked parameters already + Debug.Assert(charCount >= 0, "[Latin1Encoding.GetByteCount]count is negative"); + Debug.Assert(chars != null, "[Latin1Encoding.GetByteCount]chars is null"); + + // Assert because we shouldn't be able to have a null encoder. + Debug.Assert(encoderFallback != null, "[Latin1Encoding.GetByteCount]Attempting to use null fallback encoder"); + + char charLeftOver = (char)0; + + // If we have an encoder AND we aren't using default fallback, + // then we may have a complicated count. + EncoderReplacementFallback fallback; + if (encoder != null) + { + charLeftOver = encoder._charLeftOver; + Debug.Assert(charLeftOver == 0 || Char.IsHighSurrogate(charLeftOver), + "[Latin1Encoding.GetByteCount]leftover character should be high surrogate"); + + fallback = encoder.Fallback as EncoderReplacementFallback; + + // Verify that we have no fallbackbuffer, for Latin1 its always empty, so just assert + Debug.Assert(!encoder._throwOnOverflow || !encoder.InternalHasFallbackBuffer || + encoder.FallbackBuffer.Remaining == 0, + "[Latin1CodePageEncoding.GetByteCount]Expected empty fallback buffer"); + } + else + fallback = this.EncoderFallback as EncoderReplacementFallback; + + if ((fallback != null && fallback.MaxCharCount == 1)/* || bIsBestFit*/) + { + // Replacement fallback encodes surrogate pairs as two ?? (or two whatever), so return size is always + // same as input size. + // Note that no existing SBCS code pages map code points to supplimentary characters, so this is easy. + + // We could however have 1 extra byte if the last call had an encoder and a funky fallback and + // if we don't use the funky fallback this time. + + // Do we have an extra char left over from last time? + if (charLeftOver > 0) + charCount++; + + return (charCount); + } + + // Count is more complicated if you have a funky fallback + // For fallback we may need a fallback buffer, we know we're not default fallback + int byteCount = 0; + + // Start by assuming default count, then +/- for fallback characters + char* charEnd = chars + charCount; + + // For fallback we may need a fallback buffer, we know we aren't default fallback. + EncoderFallbackBuffer fallbackBuffer = null; + char* charsForFallback; + + // We may have a left over character from last time, try and process it. + if (charLeftOver > 0) + { + // Initialize the buffer + Debug.Assert(encoder != null, + "[Latin1Encoding.GetByteCount]Expected encoder if we have charLeftOver"); + fallbackBuffer = encoder.FallbackBuffer; + fallbackBuffer.InternalInitialize(chars, charEnd, encoder, false); + + // Since left over char was a surrogate, it'll have to be fallen back. + // Get Fallback + // This will fallback a pair if *chars is a low surrogate + charsForFallback = chars; + fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback); + chars = charsForFallback; + } + + // Now we may have fallback char[] already from the encoder + + // Go ahead and do it, including the fallback. + char ch; + while ((ch = (fallbackBuffer == null) ? '\0' : fallbackBuffer.InternalGetNextChar()) != 0 || + chars < charEnd) + { + // First unwind any fallback + if (ch == 0) + { + // No fallback, just get next char + ch = *chars; + chars++; + } + + // Check for fallback, this'll catch surrogate pairs too. + // no chars >= 0x100 are allowed. + if (ch > 0xff) + { + // Initialize the buffer + if (fallbackBuffer == null) + { + if (encoder == null) + fallbackBuffer = this.encoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = encoder.FallbackBuffer; + fallbackBuffer.InternalInitialize(charEnd - charCount, charEnd, encoder, false); + } + + // Get Fallback + charsForFallback = chars; + fallbackBuffer.InternalFallback(ch, ref charsForFallback); + chars = charsForFallback; + continue; + } + + // We'll use this one + byteCount++; + } + + Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0, + "[Latin1Encoding.GetByteCount]Expected Empty fallback buffer"); + + return byteCount; + } + + internal override unsafe int GetBytes(char* chars, int charCount, + byte* bytes, int byteCount, EncoderNLS encoder) + { + // Just need to ASSERT, this is called by something else internal that checked parameters already + Debug.Assert(bytes != null, "[Latin1Encoding.GetBytes]bytes is null"); + Debug.Assert(byteCount >= 0, "[Latin1Encoding.GetBytes]byteCount is negative"); + Debug.Assert(chars != null, "[Latin1Encoding.GetBytes]chars is null"); + Debug.Assert(charCount >= 0, "[Latin1Encoding.GetBytes]charCount is negative"); + + // Assert because we shouldn't be able to have a null encoder. + Debug.Assert(encoderFallback != null, "[Latin1Encoding.GetBytes]Attempting to use null encoder fallback"); + + // Get any left over characters & check fast or slower fallback type + char charLeftOver = (char)0; + EncoderReplacementFallback fallback = null; + if (encoder != null) + { + charLeftOver = encoder._charLeftOver; + fallback = encoder.Fallback as EncoderReplacementFallback; + Debug.Assert(charLeftOver == 0 || Char.IsHighSurrogate(charLeftOver), + "[Latin1Encoding.GetBytes]leftover character should be high surrogate"); + + // Verify that we have no fallbackbuffer, for ASCII its always empty, so just assert + Debug.Assert(!encoder._throwOnOverflow || !encoder.InternalHasFallbackBuffer || + encoder.FallbackBuffer.Remaining == 0, + "[Latin1CodePageEncoding.GetBytes]Expected empty fallback buffer"); + } + else + { + fallback = this.EncoderFallback as EncoderReplacementFallback; + } + + // prepare our end + char* charEnd = chars + charCount; + byte* byteStart = bytes; + char* charStart = chars; + + // See if we do the fast default or slightly slower fallback + if (fallback != null && fallback.MaxCharCount == 1) + { + // Fast version + char cReplacement = fallback.DefaultString[0]; + + // Check for replacements in range, otherwise fall back to slow version. + if (cReplacement <= (char)0xff) + { + // We should have exactly as many output bytes as input bytes, unless there's a left + // over character, in which case we may need one more. + + // If we had a left over character will have to add a ? (This happens if they had a funky + // fallback last time, but not this time.) (We can't spit any out though + // because with fallback encoder each surrogate is treated as a seperate code point) + if (charLeftOver > 0) + { + // Have to have room + // Throw even if doing no throw version because this is just 1 char, + // so buffer will never be big enough + if (byteCount == 0) + ThrowBytesOverflow(encoder, true); + + // This'll make sure we still have more room and also make sure our return value is correct. + *(bytes++) = (byte)cReplacement; + byteCount--; // We used one of the ones we were counting. + } + + // This keeps us from overrunning our output buffer + if (byteCount < charCount) + { + // Throw or make buffer smaller? + ThrowBytesOverflow(encoder, byteCount < 1); + + // Just use what we can + charEnd = chars + byteCount; + } + + // We just do a quick copy + while (chars < charEnd) + { + char ch2 = *(chars++); + if (ch2 > 0x00ff) *(bytes++) = (byte)cReplacement; + else *(bytes++) = (byte)ch2; + } + + // Clear encoder + if (encoder != null) + { + encoder._charLeftOver = (char)0; + encoder._charsUsed = (int)(chars - charStart); + } + return (int)(bytes - byteStart); + } + } + + // Slower version, have to do real fallback. + + // prepare our end + byte* byteEnd = bytes + byteCount; + + // For fallback we may need a fallback buffer, we know we aren't default fallback, create & init it + EncoderFallbackBuffer fallbackBuffer = null; + char* charsForFallback; + + // We may have a left over character from last time, try and process it. + if (charLeftOver > 0) + { + // Since left over char was a surrogate, it'll have to be fallen back. + // Get Fallback + Debug.Assert(encoder != null, + "[Latin1Encoding.GetBytes]Expected encoder if we have charLeftOver"); + fallbackBuffer = encoder.FallbackBuffer; + fallbackBuffer.InternalInitialize(chars, charEnd, encoder, true); + + // Since left over char was a surrogate, it'll have to be fallen back. + // Get Fallback + // This will fallback a pair if *chars is a low surrogate + charsForFallback = chars; + fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback); + chars = charsForFallback; + + if (fallbackBuffer.Remaining > byteEnd - bytes) + { + // Throw it, if we don't have enough for this we never will + ThrowBytesOverflow(encoder, true); + } + } + + // Now we may have fallback char[] already from the encoder fallback above + + // Go ahead and do it, including the fallback. + char ch; + while ((ch = (fallbackBuffer == null) ? '\0' : fallbackBuffer.InternalGetNextChar()) != 0 || + chars < charEnd) + { + // First unwind any fallback + if (ch == 0) + { + // No fallback, just get next char + ch = *chars; + chars++; + } + + // Check for fallback, this'll catch surrogate pairs too. + // All characters >= 0x100 must fall back. + if (ch > 0xff) + { + // Initialize the buffer + if (fallbackBuffer == null) + { + if (encoder == null) + fallbackBuffer = this.encoderFallback.CreateFallbackBuffer(); + else + fallbackBuffer = encoder.FallbackBuffer; + fallbackBuffer.InternalInitialize(charEnd - charCount, charEnd, encoder, true); + } + + // Get Fallback + charsForFallback = chars; + fallbackBuffer.InternalFallback(ch, ref charsForFallback); + chars = charsForFallback; + + // Make sure we have enough room. Each fallback char will be 1 output char + // (or else cause a recursion exception) + if (fallbackBuffer.Remaining > byteEnd - bytes) + { + // Didn't use this char, throw it. Chars should've advanced by now + // If we had encoder fallback data it would've thrown before the loop + Debug.Assert(chars > charStart, + "[Latin1Encoding.GetBytes]Expected chars to have advanced (fallback case)"); + chars--; + fallbackBuffer.InternalReset(); + + // Throw it + ThrowBytesOverflow(encoder, chars == charStart); + break; + } + + continue; + } + + // We'll use this one + // Bounds check + if (bytes >= byteEnd) + { + // didn't use this char, we'll throw or use buffer + Debug.Assert(fallbackBuffer == null || fallbackBuffer.bFallingBack == false, + "[Latin1Encoding.GetBytes]Expected fallback to have throw initially if insufficient space"); + if (fallbackBuffer == null || fallbackBuffer.bFallingBack == false) + { + Debug.Assert(chars > charStart, + "[Latin1Encoding.GetBytes]Expected chars to have advanced (fallback case)"); + chars--; // don't use last char + } + ThrowBytesOverflow(encoder, chars == charStart); // throw ? + break; // don't throw, stop + } + + // Go ahead and add it + *bytes = unchecked((byte)ch); + bytes++; + } + + // Need to do encoder stuff + if (encoder != null) + { + // Fallback stuck it in encoder if necessary, but we have to clear MustFlush cases + if (fallbackBuffer != null && !fallbackBuffer.bUsedEncoder) + // Clear it in case of MustFlush + encoder._charLeftOver = (char)0; + + // Set our chars used count + encoder._charsUsed = (int)(chars - charStart); + } + + Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0, + "[Latin1Encoding.GetBytes]Expected Empty fallback buffer"); + + return (int)(bytes - byteStart); + } + + // This is internal and called by something else, + internal override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS decoder) + { + // Just assert, we're called internally so these should be safe, checked already + Debug.Assert(bytes != null, "[Latin1Encoding.GetCharCount]bytes is null"); + Debug.Assert(count >= 0, "[Latin1Encoding.GetCharCount]byteCount is negative"); + + // Just return length, SBCS stay the same length because they don't map to surrogate + // pairs and we don't have to fallback because all latin1Encoding code points are unicode + return count; + } + + internal override unsafe int GetChars(byte* bytes, int byteCount, + char* chars, int charCount, DecoderNLS decoder) + { + // Just need to ASSERT, this is called by something else internal that checked parameters already + Debug.Assert(bytes != null, "[Latin1Encoding.GetChars]bytes is null"); + Debug.Assert(byteCount >= 0, "[Latin1Encoding.GetChars]byteCount is negative"); + Debug.Assert(chars != null, "[Latin1Encoding.GetChars]chars is null"); + Debug.Assert(charCount >= 0, "[Latin1Encoding.GetChars]charCount is negative"); + + // Need byteCount chars, otherwise too small buffer + if (charCount < byteCount) + { + // Buffer too small. Do we throw? + ThrowCharsOverflow(decoder, charCount < 1); + + // Don't throw, correct buffer size + byteCount = charCount; + } + + // Do it our fast way + byte* byteEnd = bytes + byteCount; + + // Quick loop, all bytes are the same as chars, so no fallbacks for latin1 + while (bytes < byteEnd) + { + *(chars) = unchecked((char)*(bytes)); + chars++; + bytes++; + } + + // Might need to know input bytes used + if (decoder != null) + decoder._bytesUsed = byteCount; + + // Converted sequence is same length as input, so output charsUsed is same as byteCount; + return byteCount; + } + + public override int GetMaxByteCount(int charCount) + { + if (charCount < 0) + throw new ArgumentOutOfRangeException(nameof(charCount), + SR.ArgumentOutOfRange_NeedNonNegNum); + + // Characters would be # of characters + 1 in case high surrogate is ? * max fallback + long byteCount = (long)charCount + 1; + + if (EncoderFallback.MaxCharCount > 1) + byteCount *= EncoderFallback.MaxCharCount; + + // 1 to 1 for most characters. Only surrogates with fallbacks have less. + + if (byteCount > 0x7fffffff) + throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_GetByteCountOverflow); + return (int)byteCount; + } + + public override int GetMaxCharCount(int byteCount) + { + if (byteCount < 0) + throw new ArgumentOutOfRangeException(nameof(byteCount), + SR.ArgumentOutOfRange_NeedNonNegNum); + + // Just return length, SBCS stay the same length because they don't map to surrogate + long charCount = (long)byteCount; + + // 1 to 1 for most characters. Only surrogates with fallbacks have less, unknown fallbacks could be longer. + if (DecoderFallback.MaxCharCount > 1) + charCount *= DecoderFallback.MaxCharCount; + + if (charCount > 0x7fffffff) + throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_GetCharCountOverflow); + + return (int)charCount; + } + + // True if and only if the encoding only uses single byte code points. (Ie, ASCII, 1252, etc) + public override bool IsSingleByte + { + get + { + return true; + } + } + + public override bool IsAlwaysNormalized(NormalizationForm form) + { + // Latin-1 contains precomposed characters, so normal for Form C. + // Since some are composed, not normal for D & KD. + // Also some letters like 0x00A8 (spacing diarisis) have compatibility decompositions, so false for KD & KC. + + // Only true for form C. + return (form == NormalizationForm.FormC); + } + // Since our best fit table is small we'll hard code it + internal override char[] GetBestFitUnicodeToBytesData() + { + // Get our best fit data + return Latin1Encoding.arrayCharBestFit; + } + + // Best fit for ASCII, and since it works for ASCII, we use it for latin1 as well. + private static readonly char[] arrayCharBestFit = + { +// The first many are in case you wanted to use this for ASCIIEncoding, which we don't need to do any more. +// (char)0x00a0, (char)0x0020, // No-Break Space -> Space +// (char)0x00a1, (char)0x0021, // Inverted Exclamation Mark -> ! +// (char)0x00a2, (char)0x0063, // Cent Sign -> c +// (char)0x00a3, (char)0x003f, // Pound Sign +// (char)0x00a4, (char)0x0024, // Currency Sign -> $ +// (char)0x00a5, (char)0x0059, // Yen Sign -> Y +// (char)0x00a6, (char)0x007c, // Broken Bar -> | +// (char)0x00a7, (char)0x003f, // Section Sign +// (char)0x00a8, (char)0x003f, // Diaeresis +// (char)0x00a9, (char)0x0043, // Copyright Sign -> C +// (char)0x00aa, (char)0x0061, // Feminine Ordinal Indicator -> a +// (char)0x00ab, (char)0x003c, // Left-Pointing Double Angle Quotation Mark -> < +// (char)0x00ac, (char)0x003f, // Not Sign +// (char)0x00ad, (char)0x002d, // Soft Hyphen -> - +// (char)0x00ae, (char)0x0052, // Registered Sign -> R +// (char)0x00af, (char)0x003f, // Macron +// (char)0x00b0, (char)0x003f, // Degree Sign +// (char)0x00b1, (char)0x003f, // Plus-Minus Sign +// (char)0x00b2, (char)0x0032, // Superscript Two -> 2 +// (char)0x00b3, (char)0x0033, // Superscript Three -> 3 +// (char)0x00b4, (char)0x003f, // Acute Accent +// (char)0x00b5, (char)0x003f, // Micro Sign +// (char)0x00b6, (char)0x003f, // Pilcrow Sign +// (char)0x00b7, (char)0x002e, // Middle Dot -> . +// (char)0x00b8, (char)0x002c, // Cedilla -> , +// (char)0x00b9, (char)0x0031, // Superscript One -> 1 +// (char)0x00ba, (char)0x006f, // Masculine Ordinal Indicator -> o +// (char)0x00bb, (char)0x003e, // Right-Pointing Double Angle Quotation Mark -> > +// (char)0x00bc, (char)0x003f, // Vulgar Fraction One Quarter +// (char)0x00bd, (char)0x003f, // Vulgar Fraction One Half +// (char)0x00be, (char)0x003f, // Vulgar Fraction Three Quarters +// (char)0x00bf, (char)0x003f, // Inverted Question Mark +// (char)0x00c0, (char)0x0041, // Latin Capital Letter A With Grave -> A +// (char)0x00c1, (char)0x0041, // Latin Capital Letter A With Acute -> A +// (char)0x00c2, (char)0x0041, // Latin Capital Letter A With Circumflex -> A +// (char)0x00c3, (char)0x0041, // Latin Capital Letter A With Tilde -> A +// (char)0x00c4, (char)0x0041, // Latin Capital Letter A With Diaeresis -> A +// (char)0x00c5, (char)0x0041, // Latin Capital Letter A With Ring Above -> A +// (char)0x00c6, (char)0x0041, // Latin Capital Ligature Ae -> A +// (char)0x00c7, (char)0x0043, // Latin Capital Letter C With Cedilla -> C +// (char)0x00c8, (char)0x0045, // Latin Capital Letter E With Grave -> E +// (char)0x00c9, (char)0x0045, // Latin Capital Letter E With Acute -> E +// (char)0x00ca, (char)0x0045, // Latin Capital Letter E With Circumflex -> E +// (char)0x00cb, (char)0x0045, // Latin Capital Letter E With Diaeresis -> E +// (char)0x00cc, (char)0x0049, // Latin Capital Letter I With Grave -> I +// (char)0x00cd, (char)0x0049, // Latin Capital Letter I With Acute -> I +// (char)0x00ce, (char)0x0049, // Latin Capital Letter I With Circumflex -> I +// (char)0x00cf, (char)0x0049, // Latin Capital Letter I With Diaeresis -> I +// (char)0x00d0, (char)0x0044, // Latin Capital Letter Eth -> D +// (char)0x00d1, (char)0x004e, // Latin Capital Letter N With Tilde -> N +// (char)0x00d2, (char)0x004f, // Latin Capital Letter O With Grave -> O +// (char)0x00d3, (char)0x004f, // Latin Capital Letter O With Acute -> O +// (char)0x00d4, (char)0x004f, // Latin Capital Letter O With Circumflex -> O +// (char)0x00d5, (char)0x004f, // Latin Capital Letter O With Tilde -> O +// (char)0x00d6, (char)0x004f, // Latin Capital Letter O With Diaeresis -> O +// (char)0x00d7, (char)0x003f, // Multiplication Sign +// (char)0x00d8, (char)0x004f, // Latin Capital Letter O With Stroke -> O +// (char)0x00d9, (char)0x0055, // Latin Capital Letter U With Grave -> U +// (char)0x00da, (char)0x0055, // Latin Capital Letter U With Acute -> U +// (char)0x00db, (char)0x0055, // Latin Capital Letter U With Circumflex -> U +// (char)0x00dc, (char)0x0055, // Latin Capital Letter U With Diaeresis -> U +// (char)0x00dd, (char)0x0059, // Latin Capital Letter Y With Acute -> Y +// (char)0x00de, (char)0x003f, // Latin Capital Letter Thorn +// (char)0x00df, (char)0x003f, // Latin Small Letter Sharp S +// (char)0x00e0, (char)0x0061, // Latin Small Letter A With Grave -> a +// (char)0x00e1, (char)0x0061, // Latin Small Letter A With Acute -> a +// (char)0x00e2, (char)0x0061, // Latin Small Letter A With Circumflex -> a +// (char)0x00e3, (char)0x0061, // Latin Small Letter A With Tilde -> a +// (char)0x00e4, (char)0x0061, // Latin Small Letter A With Diaeresis -> a +// (char)0x00e5, (char)0x0061, // Latin Small Letter A With Ring Above -> a +// (char)0x00e6, (char)0x0061, // Latin Small Ligature Ae -> a +// (char)0x00e7, (char)0x0063, // Latin Small Letter C With Cedilla -> c +// (char)0x00e8, (char)0x0065, // Latin Small Letter E With Grave -> e +// (char)0x00e9, (char)0x0065, // Latin Small Letter E With Acute -> e +// (char)0x00ea, (char)0x0065, // Latin Small Letter E With Circumflex -> e +// (char)0x00eb, (char)0x0065, // Latin Small Letter E With Diaeresis -> e +// (char)0x00ec, (char)0x0069, // Latin Small Letter I With Grave -> i +// (char)0x00ed, (char)0x0069, // Latin Small Letter I With Acute -> i +// (char)0x00ee, (char)0x0069, // Latin Small Letter I With Circumflex -> i +// (char)0x00ef, (char)0x0069, // Latin Small Letter I With Diaeresis -> i +// (char)0x00f0, (char)0x003f, // Latin Small Letter Eth +// (char)0x00f1, (char)0x006e, // Latin Small Letter N With Tilde -> n +// (char)0x00f2, (char)0x006f, // Latin Small Letter O With Grave -> o +// (char)0x00f3, (char)0x006f, // Latin Small Letter O With Acute -> o +// (char)0x00f4, (char)0x006f, // Latin Small Letter O With Circumflex -> o +// (char)0x00f5, (char)0x006f, // Latin Small Letter O With Tilde -> o +// (char)0x00f6, (char)0x006f, // Latin Small Letter O With Diaeresis -> o +// (char)0x00f7, (char)0x003f, // Division Sign +// (char)0x00f8, (char)0x006f, // Latin Small Letter O With Stroke -> o +// (char)0x00f9, (char)0x0075, // Latin Small Letter U With Grave -> u +// (char)0x00fa, (char)0x0075, // Latin Small Letter U With Acute -> u +// (char)0x00fb, (char)0x0075, // Latin Small Letter U With Circumflex -> u +// (char)0x00fc, (char)0x0075, // Latin Small Letter U With Diaeresis -> u +// (char)0x00fd, (char)0x0079, // Latin Small Letter Y With Acute -> y +// (char)0x00fe, (char)0x003f, // Latin Small Letter Thorn +// (char)0x00ff, (char)0x0079, // Latin Small Letter Y With Diaeresis -> y + (char)0x0100, (char)0x0041, // Latin Capital Letter A With Macron -> A + (char)0x0101, (char)0x0061, // Latin Small Letter A With Macron -> a + (char)0x0102, (char)0x0041, // Latin Capital Letter A With Breve -> A + (char)0x0103, (char)0x0061, // Latin Small Letter A With Breve -> a + (char)0x0104, (char)0x0041, // Latin Capital Letter A With Ogonek -> A + (char)0x0105, (char)0x0061, // Latin Small Letter A With Ogonek -> a + (char)0x0106, (char)0x0043, // Latin Capital Letter C With Acute -> C + (char)0x0107, (char)0x0063, // Latin Small Letter C With Acute -> c + (char)0x0108, (char)0x0043, // Latin Capital Letter C With Circumflex -> C + (char)0x0109, (char)0x0063, // Latin Small Letter C With Circumflex -> c + (char)0x010a, (char)0x0043, // Latin Capital Letter C With Dot Above -> C + (char)0x010b, (char)0x0063, // Latin Small Letter C With Dot Above -> c + (char)0x010c, (char)0x0043, // Latin Capital Letter C With Caron -> C + (char)0x010d, (char)0x0063, // Latin Small Letter C With Caron -> c + (char)0x010e, (char)0x0044, // Latin Capital Letter D With Caron -> D + (char)0x010f, (char)0x0064, // Latin Small Letter D With Caron -> d + (char)0x0110, (char)0x0044, // Latin Capital Letter D With Stroke -> D + (char)0x0111, (char)0x0064, // Latin Small Letter D With Stroke -> d + (char)0x0112, (char)0x0045, // Latin Capital Letter E With Macron -> E + (char)0x0113, (char)0x0065, // Latin Small Letter E With Macron -> e + (char)0x0114, (char)0x0045, // Latin Capital Letter E With Breve -> E + (char)0x0115, (char)0x0065, // Latin Small Letter E With Breve -> e + (char)0x0116, (char)0x0045, // Latin Capital Letter E With Dot Above -> E + (char)0x0117, (char)0x0065, // Latin Small Letter E With Dot Above -> e + (char)0x0118, (char)0x0045, // Latin Capital Letter E With Ogonek -> E + (char)0x0119, (char)0x0065, // Latin Small Letter E With Ogonek -> e + (char)0x011a, (char)0x0045, // Latin Capital Letter E With Caron -> E + (char)0x011b, (char)0x0065, // Latin Small Letter E With Caron -> e + (char)0x011c, (char)0x0047, // Latin Capital Letter G With Circumflex -> G + (char)0x011d, (char)0x0067, // Latin Small Letter G With Circumflex -> g + (char)0x011e, (char)0x0047, // Latin Capital Letter G With Breve -> G + (char)0x011f, (char)0x0067, // Latin Small Letter G With Breve -> g + (char)0x0120, (char)0x0047, // Latin Capital Letter G With Dot Above -> G + (char)0x0121, (char)0x0067, // Latin Small Letter G With Dot Above -> g + (char)0x0122, (char)0x0047, // Latin Capital Letter G With Cedilla -> G + (char)0x0123, (char)0x0067, // Latin Small Letter G With Cedilla -> g + (char)0x0124, (char)0x0048, // Latin Capital Letter H With Circumflex -> H + (char)0x0125, (char)0x0068, // Latin Small Letter H With Circumflex -> h + (char)0x0126, (char)0x0048, // Latin Capital Letter H With Stroke -> H + (char)0x0127, (char)0x0068, // Latin Small Letter H With Stroke -> h + (char)0x0128, (char)0x0049, // Latin Capital Letter I With Tilde -> I + (char)0x0129, (char)0x0069, // Latin Small Letter I With Tilde -> i + (char)0x012a, (char)0x0049, // Latin Capital Letter I With Macron -> I + (char)0x012b, (char)0x0069, // Latin Small Letter I With Macron -> i + (char)0x012c, (char)0x0049, // Latin Capital Letter I With Breve -> I + (char)0x012d, (char)0x0069, // Latin Small Letter I With Breve -> i + (char)0x012e, (char)0x0049, // Latin Capital Letter I With Ogonek -> I + (char)0x012f, (char)0x0069, // Latin Small Letter I With Ogonek -> i + (char)0x0130, (char)0x0049, // Latin Capital Letter I With Dot Above -> I + (char)0x0131, (char)0x0069, // Latin Small Letter Dotless I -> i + (char)0x0134, (char)0x004a, // Latin Capital Letter J With Circumflex -> J + (char)0x0135, (char)0x006a, // Latin Small Letter J With Circumflex -> j + (char)0x0136, (char)0x004b, // Latin Capital Letter K With Cedilla -> K + (char)0x0137, (char)0x006b, // Latin Small Letter K With Cedilla -> k + (char)0x0139, (char)0x004c, // Latin Capital Letter L With Acute -> L + (char)0x013a, (char)0x006c, // Latin Small Letter L With Acute -> l + (char)0x013b, (char)0x004c, // Latin Capital Letter L With Cedilla -> L + (char)0x013c, (char)0x006c, // Latin Small Letter L With Cedilla -> l + (char)0x013d, (char)0x004c, // Latin Capital Letter L With Caron -> L + (char)0x013e, (char)0x006c, // Latin Small Letter L With Caron -> l + (char)0x0141, (char)0x004c, // Latin Capital Letter L With Stroke -> L + (char)0x0142, (char)0x006c, // Latin Small Letter L With Stroke -> l + (char)0x0143, (char)0x004e, // Latin Capital Letter N With Acute -> N + (char)0x0144, (char)0x006e, // Latin Small Letter N With Acute -> n + (char)0x0145, (char)0x004e, // Latin Capital Letter N With Cedilla -> N + (char)0x0146, (char)0x006e, // Latin Small Letter N With Cedilla -> n + (char)0x0147, (char)0x004e, // Latin Capital Letter N With Caron -> N + (char)0x0148, (char)0x006e, // Latin Small Letter N With Caron -> n + (char)0x014c, (char)0x004f, // Latin Capital Letter O With Macron -> O + (char)0x014d, (char)0x006f, // Latin Small Letter O With Macron -> o + (char)0x014e, (char)0x004f, // Latin Capital Letter O With Breve -> O + (char)0x014f, (char)0x006f, // Latin Small Letter O With Breve -> o + (char)0x0150, (char)0x004f, // Latin Capital Letter O With Double Acute -> O + (char)0x0151, (char)0x006f, // Latin Small Letter O With Double Acute -> o + (char)0x0152, (char)0x004f, // Latin Capital Ligature Oe -> O + (char)0x0153, (char)0x006f, // Latin Small Ligature Oe -> o + (char)0x0154, (char)0x0052, // Latin Capital Letter R With Acute -> R + (char)0x0155, (char)0x0072, // Latin Small Letter R With Acute -> r + (char)0x0156, (char)0x0052, // Latin Capital Letter R With Cedilla -> R + (char)0x0157, (char)0x0072, // Latin Small Letter R With Cedilla -> r + (char)0x0158, (char)0x0052, // Latin Capital Letter R With Caron -> R + (char)0x0159, (char)0x0072, // Latin Small Letter R With Caron -> r + (char)0x015a, (char)0x0053, // Latin Capital Letter S With Acute -> S + (char)0x015b, (char)0x0073, // Latin Small Letter S With Acute -> s + (char)0x015c, (char)0x0053, // Latin Capital Letter S With Circumflex -> S + (char)0x015d, (char)0x0073, // Latin Small Letter S With Circumflex -> s + (char)0x015e, (char)0x0053, // Latin Capital Letter S With Cedilla -> S + (char)0x015f, (char)0x0073, // Latin Small Letter S With Cedilla -> s + (char)0x0160, (char)0x0053, // Latin Capital Letter S With Caron -> S + (char)0x0161, (char)0x0073, // Latin Small Letter S With Caron -> s + (char)0x0162, (char)0x0054, // Latin Capital Letter T With Cedilla -> T + (char)0x0163, (char)0x0074, // Latin Small Letter T With Cedilla -> t + (char)0x0164, (char)0x0054, // Latin Capital Letter T With Caron -> T + (char)0x0165, (char)0x0074, // Latin Small Letter T With Caron -> t + (char)0x0166, (char)0x0054, // Latin Capital Letter T With Stroke -> T + (char)0x0167, (char)0x0074, // Latin Small Letter T With Stroke -> t + (char)0x0168, (char)0x0055, // Latin Capital Letter U With Tilde -> U + (char)0x0169, (char)0x0075, // Latin Small Letter U With Tilde -> u + (char)0x016a, (char)0x0055, // Latin Capital Letter U With Macron -> U + (char)0x016b, (char)0x0075, // Latin Small Letter U With Macron -> u + (char)0x016c, (char)0x0055, // Latin Capital Letter U With Breve -> U + (char)0x016d, (char)0x0075, // Latin Small Letter U With Breve -> u + (char)0x016e, (char)0x0055, // Latin Capital Letter U With Ring Above -> U + (char)0x016f, (char)0x0075, // Latin Small Letter U With Ring Above -> u + (char)0x0170, (char)0x0055, // Latin Capital Letter U With Double Acute -> U + (char)0x0171, (char)0x0075, // Latin Small Letter U With Double Acute -> u + (char)0x0172, (char)0x0055, // Latin Capital Letter U With Ogonek -> U + (char)0x0173, (char)0x0075, // Latin Small Letter U With Ogonek -> u + (char)0x0174, (char)0x0057, // Latin Capital Letter W With Circumflex -> W + (char)0x0175, (char)0x0077, // Latin Small Letter W With Circumflex -> w + (char)0x0176, (char)0x0059, // Latin Capital Letter Y With Circumflex -> Y + (char)0x0177, (char)0x0079, // Latin Small Letter Y With Circumflex -> y + (char)0x0178, (char)0x0059, // Latin Capital Letter Y With Diaeresis -> Y + (char)0x0179, (char)0x005a, // Latin Capital Letter Z With Acute -> Z + (char)0x017a, (char)0x007a, // Latin Small Letter Z With Acute -> z + (char)0x017b, (char)0x005a, // Latin Capital Letter Z With Dot Above -> Z + (char)0x017c, (char)0x007a, // Latin Small Letter Z With Dot Above -> z + (char)0x017d, (char)0x005a, // Latin Capital Letter Z With Caron -> Z + (char)0x017e, (char)0x007a, // Latin Small Letter Z With Caron -> z + (char)0x0180, (char)0x0062, // Latin Small Letter B With Stroke -> b + (char)0x0189, (char)0x0044, // Latin Capital Letter African D -> D + (char)0x0191, (char)0x0046, // Latin Capital Letter F With Hook -> F + (char)0x0192, (char)0x0066, // Latin Small Letter F With Hook -> f + (char)0x0197, (char)0x0049, // Latin Capital Letter I With Stroke -> I + (char)0x019a, (char)0x006c, // Latin Small Letter L With Bar -> l + (char)0x019f, (char)0x004f, // Latin Capital Letter O With Middle Tilde -> O + (char)0x01a0, (char)0x004f, // Latin Capital Letter O With Horn -> O + (char)0x01a1, (char)0x006f, // Latin Small Letter O With Horn -> o + (char)0x01ab, (char)0x0074, // Latin Small Letter T With Palatal Hook -> t + (char)0x01ae, (char)0x0054, // Latin Capital Letter T With Retroflex Hook -> T + (char)0x01af, (char)0x0055, // Latin Capital Letter U With Horn -> U + (char)0x01b0, (char)0x0075, // Latin Small Letter U With Horn -> u + (char)0x01b6, (char)0x007a, // Latin Small Letter Z With Stroke -> z + (char)0x01cd, (char)0x0041, // Latin Capital Letter A With Caron -> A + (char)0x01ce, (char)0x0061, // Latin Small Letter A With Caron -> a + (char)0x01cf, (char)0x0049, // Latin Capital Letter I With Caron -> I + (char)0x01d0, (char)0x0069, // Latin Small Letter I With Caron -> i + (char)0x01d1, (char)0x004f, // Latin Capital Letter O With Caron -> O + (char)0x01d2, (char)0x006f, // Latin Small Letter O With Caron -> o + (char)0x01d3, (char)0x0055, // Latin Capital Letter U With Caron -> U + (char)0x01d4, (char)0x0075, // Latin Small Letter U With Caron -> u + (char)0x01d5, (char)0x0055, // Latin Capital Letter U With Diaeresis And Macron -> U + (char)0x01d6, (char)0x0075, // Latin Small Letter U With Diaeresis And Macron -> u + (char)0x01d7, (char)0x0055, // Latin Capital Letter U With Diaeresis And Acute -> U + (char)0x01d8, (char)0x0075, // Latin Small Letter U With Diaeresis And Acute -> u + (char)0x01d9, (char)0x0055, // Latin Capital Letter U With Diaeresis And Caron -> U + (char)0x01da, (char)0x0075, // Latin Small Letter U With Diaeresis And Caron -> u + (char)0x01db, (char)0x0055, // Latin Capital Letter U With Diaeresis And Grave -> U + (char)0x01dc, (char)0x0075, // Latin Small Letter U With Diaeresis And Grave -> u + (char)0x01de, (char)0x0041, // Latin Capital Letter A With Diaeresis And Macron -> A + (char)0x01df, (char)0x0061, // Latin Small Letter A With Diaeresis And Macron -> a + (char)0x01e4, (char)0x0047, // Latin Capital Letter G With Stroke -> G + (char)0x01e5, (char)0x0067, // Latin Small Letter G With Stroke -> g + (char)0x01e6, (char)0x0047, // Latin Capital Letter G With Caron -> G + (char)0x01e7, (char)0x0067, // Latin Small Letter G With Caron -> g + (char)0x01e8, (char)0x004b, // Latin Capital Letter K With Caron -> K + (char)0x01e9, (char)0x006b, // Latin Small Letter K With Caron -> k + (char)0x01ea, (char)0x004f, // Latin Capital Letter O With Ogonek -> O + (char)0x01eb, (char)0x006f, // Latin Small Letter O With Ogonek -> o + (char)0x01ec, (char)0x004f, // Latin Capital Letter O With Ogonek And Macron -> O + (char)0x01ed, (char)0x006f, // Latin Small Letter O With Ogonek And Macron -> o + (char)0x01f0, (char)0x006a, // Latin Small Letter J With Caron -> j + (char)0x0261, (char)0x0067, // Latin Small Letter Script G -> g + (char)0x02b9, (char)0x0027, // Modifier Letter Prime -> ' + (char)0x02ba, (char)0x0022, // Modifier Letter Double Prime -> " + (char)0x02bc, (char)0x0027, // Modifier Letter Apostrophe -> ' + (char)0x02c4, (char)0x005e, // Modifier Letter Up Arrowhead -> ^ + (char)0x02c6, (char)0x005e, // Modifier Letter Circumflex Accent -> ^ + (char)0x02c8, (char)0x0027, // Modifier Letter Vertical Line -> ' + (char)0x02c9, (char)0x003f, // Modifier Letter Macron + (char)0x02ca, (char)0x003f, // Modifier Letter Acute Accent + (char)0x02cb, (char)0x0060, // Modifier Letter Grave Accent -> ` + (char)0x02cd, (char)0x005f, // Modifier Letter Low Macron -> _ + (char)0x02da, (char)0x003f, // Ring Above + (char)0x02dc, (char)0x007e, // Small Tilde -> ~ + (char)0x0300, (char)0x0060, // Combining Grave Accent -> ` + (char)0x0302, (char)0x005e, // Combining Circumflex Accent -> ^ + (char)0x0303, (char)0x007e, // Combining Tilde -> ~ + (char)0x030e, (char)0x0022, // Combining Double Vertical Line Above -> " + (char)0x0331, (char)0x005f, // Combining Macron Below -> _ + (char)0x0332, (char)0x005f, // Combining Low Line -> _ + (char)0x2000, (char)0x0020, // En Quad + (char)0x2001, (char)0x0020, // Em Quad + (char)0x2002, (char)0x0020, // En Space + (char)0x2003, (char)0x0020, // Em Space + (char)0x2004, (char)0x0020, // Three-Per-Em Space + (char)0x2005, (char)0x0020, // Four-Per-Em Space + (char)0x2006, (char)0x0020, // Six-Per-Em Space + (char)0x2010, (char)0x002d, // Hyphen -> - + (char)0x2011, (char)0x002d, // Non-Breaking Hyphen -> - + (char)0x2013, (char)0x002d, // En Dash -> - + (char)0x2014, (char)0x002d, // Em Dash -> - + (char)0x2018, (char)0x0027, // Left Single Quotation Mark -> ' + (char)0x2019, (char)0x0027, // Right Single Quotation Mark -> ' + (char)0x201a, (char)0x002c, // Single Low-9 Quotation Mark -> , + (char)0x201c, (char)0x0022, // Left Double Quotation Mark -> " + (char)0x201d, (char)0x0022, // Right Double Quotation Mark -> " + (char)0x201e, (char)0x0022, // Double Low-9 Quotation Mark -> " + (char)0x2020, (char)0x003f, // Dagger + (char)0x2021, (char)0x003f, // Double Dagger + (char)0x2022, (char)0x002e, // Bullet -> . + (char)0x2026, (char)0x002e, // Horizontal Ellipsis -> . + (char)0x2030, (char)0x003f, // Per Mille Sign + (char)0x2032, (char)0x0027, // Prime -> ' + (char)0x2035, (char)0x0060, // Reversed Prime -> ` + (char)0x2039, (char)0x003c, // Single Left-Pointing Angle Quotation Mark -> < + (char)0x203a, (char)0x003e, // Single Right-Pointing Angle Quotation Mark -> > + (char)0x2122, (char)0x0054, // Trade Mark Sign -> T + (char)0xff01, (char)0x0021, // Fullwidth Exclamation Mark -> ! + (char)0xff02, (char)0x0022, // Fullwidth Quotation Mark -> " + (char)0xff03, (char)0x0023, // Fullwidth Number Sign -> # + (char)0xff04, (char)0x0024, // Fullwidth Dollar Sign -> $ + (char)0xff05, (char)0x0025, // Fullwidth Percent Sign -> % + (char)0xff06, (char)0x0026, // Fullwidth Ampersand -> & + (char)0xff07, (char)0x0027, // Fullwidth Apostrophe -> ' + (char)0xff08, (char)0x0028, // Fullwidth Left Parenthesis -> ( + (char)0xff09, (char)0x0029, // Fullwidth Right Parenthesis -> ) + (char)0xff0a, (char)0x002a, // Fullwidth Asterisk -> * + (char)0xff0b, (char)0x002b, // Fullwidth Plus Sign -> + + (char)0xff0c, (char)0x002c, // Fullwidth Comma -> , + (char)0xff0d, (char)0x002d, // Fullwidth Hyphen-Minus -> - + (char)0xff0e, (char)0x002e, // Fullwidth Full Stop -> . + (char)0xff0f, (char)0x002f, // Fullwidth Solidus -> / + (char)0xff10, (char)0x0030, // Fullwidth Digit Zero -> 0 + (char)0xff11, (char)0x0031, // Fullwidth Digit One -> 1 + (char)0xff12, (char)0x0032, // Fullwidth Digit Two -> 2 + (char)0xff13, (char)0x0033, // Fullwidth Digit Three -> 3 + (char)0xff14, (char)0x0034, // Fullwidth Digit Four -> 4 + (char)0xff15, (char)0x0035, // Fullwidth Digit Five -> 5 + (char)0xff16, (char)0x0036, // Fullwidth Digit Six -> 6 + (char)0xff17, (char)0x0037, // Fullwidth Digit Seven -> 7 + (char)0xff18, (char)0x0038, // Fullwidth Digit Eight -> 8 + (char)0xff19, (char)0x0039, // Fullwidth Digit Nine -> 9 + (char)0xff1a, (char)0x003a, // Fullwidth Colon -> : + (char)0xff1b, (char)0x003b, // Fullwidth Semicolon -> ; + (char)0xff1c, (char)0x003c, // Fullwidth Less-Than Sign -> < + (char)0xff1d, (char)0x003d, // Fullwidth Equals Sign -> = + (char)0xff1e, (char)0x003e, // Fullwidth Greater-Than Sign -> > + (char)0xff1f, (char)0x003f, // Fullwidth Question Mark + (char)0xff20, (char)0x0040, // Fullwidth Commercial At -> @ + (char)0xff21, (char)0x0041, // Fullwidth Latin Capital Letter A -> A + (char)0xff22, (char)0x0042, // Fullwidth Latin Capital Letter B -> B + (char)0xff23, (char)0x0043, // Fullwidth Latin Capital Letter C -> C + (char)0xff24, (char)0x0044, // Fullwidth Latin Capital Letter D -> D + (char)0xff25, (char)0x0045, // Fullwidth Latin Capital Letter E -> E + (char)0xff26, (char)0x0046, // Fullwidth Latin Capital Letter F -> F + (char)0xff27, (char)0x0047, // Fullwidth Latin Capital Letter G -> G + (char)0xff28, (char)0x0048, // Fullwidth Latin Capital Letter H -> H + (char)0xff29, (char)0x0049, // Fullwidth Latin Capital Letter I -> I + (char)0xff2a, (char)0x004a, // Fullwidth Latin Capital Letter J -> J + (char)0xff2b, (char)0x004b, // Fullwidth Latin Capital Letter K -> K + (char)0xff2c, (char)0x004c, // Fullwidth Latin Capital Letter L -> L + (char)0xff2d, (char)0x004d, // Fullwidth Latin Capital Letter M -> M + (char)0xff2e, (char)0x004e, // Fullwidth Latin Capital Letter N -> N + (char)0xff2f, (char)0x004f, // Fullwidth Latin Capital Letter O -> O + (char)0xff30, (char)0x0050, // Fullwidth Latin Capital Letter P -> P + (char)0xff31, (char)0x0051, // Fullwidth Latin Capital Letter Q -> Q + (char)0xff32, (char)0x0052, // Fullwidth Latin Capital Letter R -> R + (char)0xff33, (char)0x0053, // Fullwidth Latin Capital Letter S -> S + (char)0xff34, (char)0x0054, // Fullwidth Latin Capital Letter T -> T + (char)0xff35, (char)0x0055, // Fullwidth Latin Capital Letter U -> U + (char)0xff36, (char)0x0056, // Fullwidth Latin Capital Letter V -> V + (char)0xff37, (char)0x0057, // Fullwidth Latin Capital Letter W -> W + (char)0xff38, (char)0x0058, // Fullwidth Latin Capital Letter X -> X + (char)0xff39, (char)0x0059, // Fullwidth Latin Capital Letter Y -> Y + (char)0xff3a, (char)0x005a, // Fullwidth Latin Capital Letter Z -> Z + (char)0xff3b, (char)0x005b, // Fullwidth Left Square Bracket -> [ + (char)0xff3c, (char)0x005c, // Fullwidth Reverse Solidus -> \ + (char)0xff3d, (char)0x005d, // Fullwidth Right Square Bracket -> ] + (char)0xff3e, (char)0x005e, // Fullwidth Circumflex Accent -> ^ + (char)0xff3f, (char)0x005f, // Fullwidth Low Line -> _ + (char)0xff40, (char)0x0060, // Fullwidth Grave Accent -> ` + (char)0xff41, (char)0x0061, // Fullwidth Latin Small Letter A -> a + (char)0xff42, (char)0x0062, // Fullwidth Latin Small Letter B -> b + (char)0xff43, (char)0x0063, // Fullwidth Latin Small Letter C -> c + (char)0xff44, (char)0x0064, // Fullwidth Latin Small Letter D -> d + (char)0xff45, (char)0x0065, // Fullwidth Latin Small Letter E -> e + (char)0xff46, (char)0x0066, // Fullwidth Latin Small Letter F -> f + (char)0xff47, (char)0x0067, // Fullwidth Latin Small Letter G -> g + (char)0xff48, (char)0x0068, // Fullwidth Latin Small Letter H -> h + (char)0xff49, (char)0x0069, // Fullwidth Latin Small Letter I -> i + (char)0xff4a, (char)0x006a, // Fullwidth Latin Small Letter J -> j + (char)0xff4b, (char)0x006b, // Fullwidth Latin Small Letter K -> k + (char)0xff4c, (char)0x006c, // Fullwidth Latin Small Letter L -> l + (char)0xff4d, (char)0x006d, // Fullwidth Latin Small Letter M -> m + (char)0xff4e, (char)0x006e, // Fullwidth Latin Small Letter N -> n + (char)0xff4f, (char)0x006f, // Fullwidth Latin Small Letter O -> o + (char)0xff50, (char)0x0070, // Fullwidth Latin Small Letter P -> p + (char)0xff51, (char)0x0071, // Fullwidth Latin Small Letter Q -> q + (char)0xff52, (char)0x0072, // Fullwidth Latin Small Letter R -> r + (char)0xff53, (char)0x0073, // Fullwidth Latin Small Letter S -> s + (char)0xff54, (char)0x0074, // Fullwidth Latin Small Letter T -> t + (char)0xff55, (char)0x0075, // Fullwidth Latin Small Letter U -> u + (char)0xff56, (char)0x0076, // Fullwidth Latin Small Letter V -> v + (char)0xff57, (char)0x0077, // Fullwidth Latin Small Letter W -> w + (char)0xff58, (char)0x0078, // Fullwidth Latin Small Letter X -> x + (char)0xff59, (char)0x0079, // Fullwidth Latin Small Letter Y -> y + (char)0xff5a, (char)0x007a, // Fullwidth Latin Small Letter Z -> z + (char)0xff5b, (char)0x007b, // Fullwidth Left Curly Bracket -> { + (char)0xff5c, (char)0x007c, // Fullwidth Vertical Line -> | + (char)0xff5d, (char)0x007d, // Fullwidth Right Curly Bracket -> } + (char)0xff5e, (char)0x007e // Fullwidth Tilde -> ~ + }; + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/Normalization.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/Normalization.cs deleted file mode 100644 index dc8bc2af71..0000000000 --- a/external/corert/src/System.Private.CoreLib/shared/System/Text/Normalization.cs +++ /dev/null @@ -1,29 +0,0 @@ -// 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. - -namespace System.Text -{ - // This is the enumeration for Normalization Forms - public enum NormalizationForm - { - FormC = 1, - FormD = 2, - FormKC = 5, - FormKD = 6 - } - - internal enum ExtendedNormalizationForms - { - FormC = 1, - FormD = 2, - FormKC = 5, - FormKD = 6, - FormIdna = 0xd, - FormCDisallowUnassigned = 0x101, - FormDDisallowUnassigned = 0x102, - FormKCDisallowUnassigned = 0x105, - FormKDDisallowUnassigned = 0x106, - FormIdnaDisallowUnassigned = 0x10d - } -} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/NormalizationForm.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/NormalizationForm.cs new file mode 100644 index 0000000000..976756251a --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/NormalizationForm.cs @@ -0,0 +1,14 @@ +// 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. + +namespace System.Text +{ + public enum NormalizationForm + { + FormC = 1, + FormD = 2, + FormKC = 5, + FormKD = 6 + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs index 3a06114bff..d9da9377d0 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs @@ -7,12 +7,12 @@ using System.Runtime; using System.Runtime.Serialization; using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Threading; using System.Globalization; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Collections.Generic; namespace System.Text @@ -25,127 +25,131 @@ namespace System.Text // The methods contained within this class do not return a new StringBuilder // object unless specified otherwise. This class may be used in conjunction with the String // class to carry out modifications upon strings. - // - // When passing null into a constructor in VJ and VC, the null - // should be explicitly type cast. - // For Example: - // StringBuilder sb1 = new StringBuilder((StringBuilder)null); - // StringBuilder sb2 = new StringBuilder((String)null); - // Console.WriteLine(sb1); - // Console.WriteLine(sb2); - // [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed partial class StringBuilder : ISerializable { // A StringBuilder is internally represented as a linked list of blocks each of which holds // a chunk of the string. It turns out string as a whole can also be represented as just a chunk, // so that is what we do. - // - // - // CLASS VARIABLES - // - // - internal char[] m_ChunkChars; // The characters in this block - internal StringBuilder m_ChunkPrevious; // Link to the block logically before this block - internal int m_ChunkLength; // The index in m_ChunkChars that represent the end of the block - internal int m_ChunkOffset; // The logical offset (sum of all characters in previous blocks) - internal int m_MaxCapacity = 0; + /// + /// The character buffer for this chunk. + /// + internal char[] m_ChunkChars; - // - // - // STATIC CONSTANTS - // - // + /// + /// The chunk that logically precedes this chunk. + /// + internal StringBuilder m_ChunkPrevious; + + /// + /// The number of characters in this chunk. + /// This is the number of elements in that are in use, from the start of the buffer. + /// + internal int m_ChunkLength; + + /// + /// The logical offset of this chunk's characters in the string it is a part of. + /// This is the sum of the number of characters in preceding blocks. + /// + internal int m_ChunkOffset; + + /// + /// The maximum capacity this builder is allowed to have. + /// + internal int m_MaxCapacity; + + /// + /// The default capacity of a . + /// internal const int DefaultCapacity = 16; - private const String CapacityField = "Capacity"; - private const String MaxCapacityField = "m_MaxCapacity"; - private const String StringValueField = "m_StringValue"; - private const String ThreadIDField = "m_currentThread"; + + private const string CapacityField = "Capacity"; // Do not rename (binary serialization) + private const string MaxCapacityField = "m_MaxCapacity"; // Do not rename (binary serialization) + private const string StringValueField = "m_StringValue"; // Do not rename (binary serialization) + private const string ThreadIDField = "m_currentThread"; // Do not rename (binary serialization) + // We want to keep chunk arrays out of large object heap (< 85K bytes ~ 40K chars) to be sure. // Making the maximum chunk size big means less allocation code called, but also more waste // in unused characters and slower inserts / replaces (since you do need to slide characters over // within a buffer). internal const int MaxChunkSize = 8000; - // - // - //CONSTRUCTORS - // - // - - // Creates a new empty string builder (i.e., it represents String.Empty) - // with the default capacity (16 characters). + /// + /// Initializes a new instance of the class. + /// public StringBuilder() { m_MaxCapacity = int.MaxValue; m_ChunkChars = new char[DefaultCapacity]; } - // Create a new empty string builder (i.e., it represents String.Empty) - // with the specified capacity. + /// + /// Initializes a new instance of the class. + /// + /// The initial capacity of this builder. public StringBuilder(int capacity) : this(capacity, int.MaxValue) { } - // Creates a new string builder from the specified string. If value - // is a null String (i.e., if it represents String.NullString) - // then the new string builder will also be null (i.e., it will also represent - // String.NullString). - // - public StringBuilder(String value) + /// + /// Initializes a new instance of the class. + /// + /// The initial contents of this builder. + public StringBuilder(string value) : this(value, DefaultCapacity) { } - // Creates a new string builder from the specified string with the specified - // capacity. If value is a null String (i.e., if it represents - // String.NullString) then the new string builder will also be null - // (i.e., it will also represent String.NullString). - // The maximum number of characters this string may contain is set by capacity. - // - public StringBuilder(String value, int capacity) - : this(value, 0, ((value != null) ? value.Length : 0), capacity) + /// + /// Initializes a new instance of the class. + /// + /// The initial contents of this builder. + /// The initial capacity of this builder. + public StringBuilder(string value, int capacity) + : this(value, 0, value?.Length ?? 0, capacity) { } - // Creates a new string builder from the specifed substring with the specified - // capacity. The maximum number of characters is set by capacity. - // - public StringBuilder(String value, int startIndex, int length, int capacity) + /// + /// Initializes a new instance of the class. + /// + /// The initial contents of this builder. + /// The index to start in . + /// The number of characters to read in . + /// The initial capacity of this builder. + public StringBuilder(string value, int startIndex, int length, int capacity) { if (capacity < 0) { - throw new ArgumentOutOfRangeException(nameof(capacity), - SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(capacity))); + throw new ArgumentOutOfRangeException(nameof(capacity), SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(capacity))); } if (length < 0) { - throw new ArgumentOutOfRangeException(nameof(length), - SR.Format(SR.ArgumentOutOfRange_MustBeNonNegNum, nameof(length))); + throw new ArgumentOutOfRangeException(nameof(length), SR.Format(SR.ArgumentOutOfRange_MustBeNonNegNum, nameof(length))); } if (startIndex < 0) { throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex); } - Contract.EndContractBlock(); if (value == null) { - value = String.Empty; + value = string.Empty; } if (startIndex > value.Length - length) { throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_IndexLength); } - m_MaxCapacity = Int32.MaxValue; + + m_MaxCapacity = int.MaxValue; if (capacity == 0) { capacity = DefaultCapacity; } - if (capacity < length) - capacity = length; + capacity = Math.Max(capacity, length); m_ChunkChars = new char[capacity]; m_ChunkLength = length; @@ -153,12 +157,17 @@ namespace System.Text unsafe { fixed (char* sourcePtr = value) + { ThreadSafeCopy(sourcePtr + startIndex, m_ChunkChars, 0, length); + } } } - // Creates an empty StringBuilder with a minimum capacity of capacity - // and a maximum capacity of maxCapacity. + /// + /// Initializes a new instance of the class. + /// + /// The initial capacity of this builder. + /// The maximum capacity of this builder. public StringBuilder(int capacity, int maxCapacity) { if (capacity > maxCapacity) @@ -171,10 +180,8 @@ namespace System.Text } if (capacity < 0) { - throw new ArgumentOutOfRangeException(nameof(capacity), - SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(capacity))); + throw new ArgumentOutOfRangeException(nameof(capacity), SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(capacity))); } - Contract.EndContractBlock(); if (capacity == 0) { @@ -188,8 +195,9 @@ namespace System.Text private StringBuilder(SerializationInfo info, StreamingContext context) { if (info == null) + { throw new ArgumentNullException(nameof(info)); - Contract.EndContractBlock(); + } int persistedCapacity = 0; string persistedString = null; @@ -213,7 +221,7 @@ namespace System.Text capacityPresent = true; break; default: - // Ignore other fields for forward compatibility. + // Ignore other fields for forwards-compatibility. break; } } @@ -221,7 +229,7 @@ namespace System.Text // Check values and set defaults if (persistedString == null) { - persistedString = String.Empty; + persistedString = string.Empty; } if (persistedMaxCapacity < 1 || persistedString.Length > persistedMaxCapacity) { @@ -231,16 +239,9 @@ namespace System.Text if (!capacityPresent) { // StringBuilder in V1.X did not persist the Capacity, so this is a valid legacy code path. - persistedCapacity = DefaultCapacity; - if (persistedCapacity < persistedString.Length) - { - persistedCapacity = persistedString.Length; - } - if (persistedCapacity > persistedMaxCapacity) - { - persistedCapacity = persistedMaxCapacity; - } + persistedCapacity = Math.Min(Math.Max(DefaultCapacity, persistedString.Length), persistedMaxCapacity); } + if (persistedCapacity < 0 || persistedCapacity < persistedString.Length || persistedCapacity > persistedMaxCapacity) { throw new SerializationException(SR.Serialization_StringBuilderCapacity); @@ -252,7 +253,7 @@ namespace System.Text persistedString.CopyTo(0, m_ChunkChars, 0, persistedString.Length); m_ChunkLength = persistedString.Length; m_ChunkPrevious = null; - VerifyClassInvariant(); + AssertInvariants(); } void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) @@ -261,9 +262,8 @@ namespace System.Text { throw new ArgumentNullException(nameof(info)); } - Contract.EndContractBlock(); - VerifyClassInvariant(); + AssertInvariants(); info.AddValue(MaxCapacityField, m_MaxCapacity); info.AddValue(CapacityField, Capacity); info.AddValue(StringValueField, ToString()); @@ -271,30 +271,31 @@ namespace System.Text info.AddValue(ThreadIDField, 0); } - [System.Diagnostics.Conditional("_DEBUG")] - private void VerifyClassInvariant() + [System.Diagnostics.Conditional("DEBUG")] + private void AssertInvariants() { - Debug.Assert((uint)(m_ChunkOffset + m_ChunkChars.Length) >= m_ChunkOffset, "Integer Overflow"); + Debug.Assert(m_ChunkOffset + m_ChunkChars.Length >= m_ChunkOffset, "The length of the string is greater than int.MaxValue."); + StringBuilder currentBlock = this; int maxCapacity = this.m_MaxCapacity; for (;;) { - // All blocks have copy of the maxCapacity. - Debug.Assert(currentBlock.m_MaxCapacity == maxCapacity, "Bad maxCapacity"); - Debug.Assert(currentBlock.m_ChunkChars != null, "Empty Buffer"); + // All blocks have the same max capacity. + Debug.Assert(currentBlock.m_MaxCapacity == maxCapacity); + Debug.Assert(currentBlock.m_ChunkChars != null); - Debug.Assert(currentBlock.m_ChunkLength <= currentBlock.m_ChunkChars.Length, "Out of range length"); - Debug.Assert(currentBlock.m_ChunkLength >= 0, "Negative length"); - Debug.Assert(currentBlock.m_ChunkOffset >= 0, "Negative offset"); + Debug.Assert(currentBlock.m_ChunkLength <= currentBlock.m_ChunkChars.Length); + Debug.Assert(currentBlock.m_ChunkLength >= 0); + Debug.Assert(currentBlock.m_ChunkOffset >= 0); StringBuilder prevBlock = currentBlock.m_ChunkPrevious; if (prevBlock == null) { - Debug.Assert(currentBlock.m_ChunkOffset == 0, "First chunk's offset is not 0"); + Debug.Assert(currentBlock.m_ChunkOffset == 0); break; } - // There are no gaps in the blocks. - Debug.Assert(currentBlock.m_ChunkOffset == prevBlock.m_ChunkOffset + prevBlock.m_ChunkLength, "There is a gap between chunks!"); + // There are no gaps in the blocks. + Debug.Assert(currentBlock.m_ChunkOffset == prevBlock.m_ChunkOffset + prevBlock.m_ChunkLength); currentBlock = prevBlock; } } @@ -316,7 +317,6 @@ namespace System.Text { throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_SmallCapacity); } - Contract.EndContractBlock(); if (Capacity != value) { @@ -328,22 +328,25 @@ namespace System.Text } } - public int MaxCapacity - { - get { return m_MaxCapacity; } - } + /// + /// Gets the maximum capacity this builder is allowed to have. + /// + public int MaxCapacity => m_MaxCapacity; - // Ensures that the capacity of this string builder is at least the specified value. - // If capacity is greater than the capacity of this string builder, then the capacity - // is set to capacity; otherwise the capacity is unchanged. - // + /// + /// Ensures that the capacity of this builder is at least the specified value. + /// + /// The new capacity for this builder. + /// + /// If is less than or equal to the current capacity of + /// this builder, the capacity remains unchanged. + /// public int EnsureCapacity(int capacity) { if (capacity < 0) { throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_NegativeCapacity); } - Contract.EndContractBlock(); if (Capacity < capacity) Capacity = capacity; @@ -352,18 +355,18 @@ namespace System.Text public override String ToString() { - Contract.Ensures(Contract.Result() != null); - - VerifyClassInvariant(); + AssertInvariants(); if (Length == 0) - return String.Empty; + { + return string.Empty; + } - string ret = string.FastAllocateString(Length); + string result = string.FastAllocateString(Length); StringBuilder chunk = this; unsafe { - fixed (char* destinationPtr = ret) + fixed (char* destinationPtr = result) { do { @@ -375,7 +378,7 @@ namespace System.Text int chunkLength = chunk.m_ChunkLength; // Check that we will not overrun our boundaries. - if ((uint)(chunkLength + chunkOffset) <= (uint)ret.Length && (uint)chunkLength <= (uint)sourceArray.Length) + if ((uint)(chunkLength + chunkOffset) <= (uint)result.Length && (uint)chunkLength <= (uint)sourceArray.Length) { fixed (char* sourcePtr = &sourceArray[0]) string.wstrcpy(destinationPtr + chunkOffset, sourcePtr, chunkLength); @@ -386,19 +389,21 @@ namespace System.Text } } chunk = chunk.m_ChunkPrevious; - } while (chunk != null); + } + while (chunk != null); - return ret; + return result; } } } - - // Converts a substring of this string builder to a String. - public String ToString(int startIndex, int length) + /// + /// Creates a string from a substring of this builder. + /// + /// The index to start in this builder. + /// The number of characters to read in this builder. + public string ToString(int startIndex, int length) { - Contract.Ensures(Contract.Result() != null); - int currentLength = this.Length; if (startIndex < 0) { @@ -412,29 +417,28 @@ namespace System.Text { throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NegativeLength); } - if (startIndex > (currentLength - length)) + if (startIndex > currentLength - length) { throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_IndexLength); } - VerifyClassInvariant(); + AssertInvariants(); StringBuilder chunk = this; int sourceEndIndex = startIndex + length; - string ret = string.FastAllocateString(length); + string result = string.FastAllocateString(length); int curDestIndex = length; unsafe { - fixed (char* destinationPtr = ret) + fixed (char* destinationPtr = result) { while (curDestIndex > 0) { int chunkEndIndex = sourceEndIndex - chunk.m_ChunkOffset; if (chunkEndIndex >= 0) { - if (chunkEndIndex > chunk.m_ChunkLength) - chunkEndIndex = chunk.m_ChunkLength; + chunkEndIndex = Math.Min(chunkEndIndex, chunk.m_ChunkLength); int countLeft = curDestIndex; int chunkCount = countLeft; @@ -448,7 +452,7 @@ namespace System.Text if (chunkCount > 0) { - // work off of local variables so that they are stable even in the presence of race conditions + // Work off of local variables so that they are stable even in the presence of race conditions char[] sourceArray = chunk.m_ChunkChars; // Check that we will not overrun our boundaries. @@ -466,27 +470,24 @@ namespace System.Text chunk = chunk.m_ChunkPrevious; } - return ret; + return result; } } } - // Convenience method for sb.Length=0; public StringBuilder Clear() { this.Length = 0; return this; } - // Sets the length of the String in this buffer. If length is less than the current - // instance, the StringBuilder is truncated. If length is greater than the current - // instance, nulls are appended. The capacity is adjusted to be the same as the length. - + /// + /// Gets or sets the length of this builder. + /// public int Length { get { - Contract.Ensures(Contract.Result() >= 0); return m_ChunkOffset + m_ChunkLength; } set @@ -501,7 +502,6 @@ namespace System.Text { throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_SmallCapacity); } - Contract.EndContractBlock(); int originalCapacity = Capacity; @@ -509,29 +509,27 @@ namespace System.Text { m_ChunkLength = 0; m_ChunkOffset = 0; - Debug.Assert(Capacity >= originalCapacity, "setting the Length should never decrease the Capacity"); + Debug.Assert(Capacity >= originalCapacity); return; } int delta = value - Length; - // if the specified length is greater than the current length if (delta > 0) { - // the end of the string value of the current StringBuilder object is padded with the Unicode NULL character - Append('\0', delta); // We could improve on this, but who does this anyway? + // Pad ourselves with null characters. + Append('\0', delta); } - // if the specified length is less than or equal to the current length else { StringBuilder chunk = FindChunkForIndex(value); if (chunk != this) { - // we crossed a chunk boundary when reducing the Length, we must replace this middle-chunk with a new - // larger chunk to ensure the original capacity is preserved + // We crossed a chunk boundary when reducing the Length. We must replace this middle-chunk with a new larger chunk, + // to ensure the original capacity is preserved. int newLen = originalCapacity - chunk.m_ChunkOffset; char[] newArray = new char[newLen]; - Debug.Assert(newLen > chunk.m_ChunkChars.Length, "the new chunk should be larger than the one it is replacing"); + Debug.Assert(newLen > chunk.m_ChunkChars.Length, "The new chunk should be larger than the one it is replacing."); Array.Copy(chunk.m_ChunkChars, 0, newArray, 0, chunk.m_ChunkLength); m_ChunkChars = newArray; @@ -539,13 +537,13 @@ namespace System.Text m_ChunkOffset = chunk.m_ChunkOffset; } m_ChunkLength = value - chunk.m_ChunkOffset; - VerifyClassInvariant(); + AssertInvariants(); } - Debug.Assert(Capacity >= originalCapacity, "setting the Length should never decrease the Capacity"); + Debug.Assert(Capacity >= originalCapacity); } } - [System.Runtime.CompilerServices.IndexerName("Chars")] + [IndexerName("Chars")] public char this[int index] { get @@ -557,12 +555,16 @@ namespace System.Text if (indexInBlock >= 0) { if (indexInBlock >= chunk.m_ChunkLength) + { throw new IndexOutOfRangeException(); + } return chunk.m_ChunkChars[indexInBlock]; } chunk = chunk.m_ChunkPrevious; if (chunk == null) + { throw new IndexOutOfRangeException(); + } } } set @@ -574,26 +576,32 @@ namespace System.Text if (indexInBlock >= 0) { if (indexInBlock >= chunk.m_ChunkLength) + { throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + } chunk.m_ChunkChars[indexInBlock] = value; return; } chunk = chunk.m_ChunkPrevious; if (chunk == null) + { throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + } } } } - // Appends a character at the end of this string builder. The capacity is adjusted as needed. + /// + /// Appends a character 0 or more times to the end of this builder. + /// + /// The character to append. + /// The number of times to append . public StringBuilder Append(char value, int repeatCount) { if (repeatCount < 0) { throw new ArgumentOutOfRangeException(nameof(repeatCount), SR.ArgumentOutOfRange_NegativeCount); } - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); if (repeatCount == 0) { @@ -608,28 +616,34 @@ namespace System.Text throw new ArgumentOutOfRangeException(nameof(repeatCount), SR.ArgumentOutOfRange_LengthGreaterThanCapacity); } - int idx = m_ChunkLength; + int index = m_ChunkLength; while (repeatCount > 0) { - if (idx < m_ChunkChars.Length) + if (index < m_ChunkChars.Length) { - m_ChunkChars[idx++] = value; + m_ChunkChars[index++] = value; --repeatCount; } else { - m_ChunkLength = idx; + m_ChunkLength = index; ExpandByABlock(repeatCount); - Debug.Assert(m_ChunkLength == 0, "Expand should create a new block"); - idx = 0; + Debug.Assert(m_ChunkLength == 0); + index = 0; } } - m_ChunkLength = idx; - VerifyClassInvariant(); + + m_ChunkLength = index; + AssertInvariants(); return this; } - // Appends an array of characters at the end of this string builder. The capacity is adjusted as needed. + /// + /// Appends a range of characters to the end of this builder. + /// + /// The characters to append. + /// The index to start in . + /// The number of characters to read in . public StringBuilder Append(char[] value, int startIndex, int charCount) { if (startIndex < 0) @@ -640,8 +654,6 @@ namespace System.Text { throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_GenericPositive); } - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); if (value == null) { @@ -649,6 +661,7 @@ namespace System.Text { return this; } + throw new ArgumentNullException(nameof(value)); } if (charCount > value.Length - startIndex) @@ -660,32 +673,33 @@ namespace System.Text { return this; } + unsafe { fixed (char* valueChars = &value[startIndex]) { Append(valueChars, charCount); - return this; } } } - // Appends a copy of this string at the end of this string builder. + /// + /// Appends a string to the end of this builder. + /// + /// The string to append. public StringBuilder Append(String value) { - Contract.Ensures(Contract.Result() != null); - if (value != null) { - // This is a hand specialization of the 'AppendHelper' code below. - // We could have just called AppendHelper. + // We could have just called AppendHelper here; this is a hand-specialization of that code. char[] chunkChars = m_ChunkChars; int chunkLength = m_ChunkLength; int valueLen = value.Length; int newCurrentIndex = chunkLength + valueLen; - if (newCurrentIndex < chunkChars.Length) // Use strictly < to avoid issue if count == 0, newIndex == length + + if (newCurrentIndex < chunkChars.Length) // Use strictly < to avoid issues if count == 0, newIndex == length { if (valueLen <= 2) { @@ -700,46 +714,53 @@ namespace System.Text { fixed (char* valuePtr = value) fixed (char* destPtr = &chunkChars[chunkLength]) + { string.wstrcpy(destPtr, valuePtr, valueLen); + } } } + m_ChunkLength = newCurrentIndex; } else + { AppendHelper(value); + } } + return this; } - - // We put this fixed in its own helper to avoid the cost zero initing valueChars in the + // We put this fixed in its own helper to avoid the cost of zero-initing `valueChars` in the // case we don't actually use it. private void AppendHelper(string value) { unsafe { fixed (char* valueChars = value) + { Append(valueChars, value.Length); + } } } - // Appends a copy of the characters in value from startIndex to startIndex + - // count at the end of this string builder. - public StringBuilder Append(String value, int startIndex, int count) + /// + /// Appends part of a string to the end of this builder. + /// + /// The string to append. + /// The index to start in . + /// The number of characters to read in . + public StringBuilder Append(string value, int startIndex, int count) { if (startIndex < 0) { throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); } - if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_GenericPositive); } - Contract.Ensures(Contract.Result() != null); - //If the value being added is null, eat the null - //and return. if (value == null) { if (startIndex == 0 && count == 0) @@ -764,21 +785,15 @@ namespace System.Text fixed (char* valueChars = value) { Append(valueChars + startIndex, count); - return this; } } } - public StringBuilder AppendLine() - { - Contract.Ensures(Contract.Result() != null); - return Append(Environment.NewLine); - } + public StringBuilder AppendLine() => Append(Environment.NewLine); public StringBuilder AppendLine(string value) { - Contract.Ensures(Contract.Result() != null); Append(value); return Append(Environment.NewLine); } @@ -790,15 +805,9 @@ namespace System.Text throw new ArgumentNullException(nameof(destination)); } - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count), SR.Arg_NegativeArgCount); - } - if (destinationIndex < 0) { - throw new ArgumentOutOfRangeException(nameof(destinationIndex), - SR.Format(SR.ArgumentOutOfRange_MustBeNonNegNum, nameof(destinationIndex))); + throw new ArgumentOutOfRangeException(nameof(destinationIndex), SR.Format(SR.ArgumentOutOfRange_MustBeNonNegNum, nameof(destinationIndex))); } if (destinationIndex > destination.Length - count) @@ -806,6 +815,16 @@ namespace System.Text throw new ArgumentException(SR.ArgumentOutOfRange_OffsetOut); } + CopyTo(sourceIndex, new Span(destination).Slice(destinationIndex), count); + } + + public void CopyTo(int sourceIndex, Span destination, int count) + { + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.Arg_NegativeArgCount); + } + if ((uint)sourceIndex > (uint)Length) { throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_Index); @@ -815,20 +834,18 @@ namespace System.Text { throw new ArgumentException(SR.Arg_LongerThanSrcString); } - Contract.EndContractBlock(); - VerifyClassInvariant(); + AssertInvariants(); StringBuilder chunk = this; int sourceEndIndex = sourceIndex + count; - int curDestIndex = destinationIndex + count; + int curDestIndex = count; while (count > 0) { int chunkEndIndex = sourceEndIndex - chunk.m_ChunkOffset; if (chunkEndIndex >= 0) { - if (chunkEndIndex > chunk.m_ChunkLength) - chunkEndIndex = chunk.m_ChunkLength; + chunkEndIndex = Math.Min(chunkEndIndex, chunk.m_ChunkLength); int chunkCount = count; int chunkStartIndex = chunkEndIndex - count; @@ -840,49 +857,46 @@ namespace System.Text curDestIndex -= chunkCount; count -= chunkCount; - // SafeCritical: we ensure that chunkStartIndex + chunkCount are within range of m_chunkChars - // as well as ensuring that curDestIndex + chunkCount are within range of destination + // We ensure that chunkStartIndex + chunkCount are within range of m_chunkChars as well as + // ensuring that curDestIndex + chunkCount are within range of destination ThreadSafeCopy(chunk.m_ChunkChars, chunkStartIndex, destination, curDestIndex, chunkCount); } chunk = chunk.m_ChunkPrevious; } } - // Inserts multiple copies of a string into this string builder at the specified position. - // Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, this - // string builder is not changed. - // + /// + /// Inserts a string 0 or more times into this builder at the specified position. + /// + /// The index to insert in this builder. + /// The string to insert. + /// The number of times to insert the string. public StringBuilder Insert(int index, String value, int count) { if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); } - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); - //Range check the index. int currentLength = Length; if ((uint)index > (uint)currentLength) { throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); } - //If value is null, empty or count is 0, do nothing. This is ECMA standard. - if (value == null || value.Length == 0 || count == 0) + if (string.IsNullOrEmpty(value) || count == 0) { return this; } - //Ensure we don't insert more chars than we can hold, and we don't - //have any integer overflow in our inserted characters. + // Ensure we don't insert more chars than we can hold, and we don't + // have any integer overflow in our new length. long insertingChars = (long)value.Length * count; if (insertingChars > MaxCapacity - this.Length) { throw new OutOfMemoryException(); } - Debug.Assert(insertingChars + this.Length < Int32.MaxValue); + Debug.Assert(insertingChars + this.Length < int.MaxValue); StringBuilder chunk; int indexInChunk; @@ -902,10 +916,12 @@ namespace System.Text } } - // Removes the specified characters from this string builder. - // The length of this string builder is reduced by - // length, but the capacity is unaffected. - // + /// + /// Removes a range of characters from this builder. + /// + /// + /// This method does not reduce the capacity of this builder. + /// public StringBuilder Remove(int startIndex, int length) { if (length < 0) @@ -922,12 +938,9 @@ namespace System.Text { throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_Index); } - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); if (Length == length && startIndex == 0) { - // Optimization. If we are deleting everything Length = 0; return this; } @@ -938,150 +951,95 @@ namespace System.Text int indexInChunk; Remove(startIndex, length, out chunk, out indexInChunk); } + return this; } - // Appends a boolean to the end of this string builder. - // The capacity is adjusted as needed. - public StringBuilder Append(bool value) - { - Contract.Ensures(Contract.Result() != null); - return Append(value.ToString()); - } + public StringBuilder Append(bool value) => Append(value.ToString()); - // Appends an sbyte to this string builder. - // The capacity is adjusted as needed. - [CLSCompliant(false)] - public StringBuilder Append(sbyte value) - { - Contract.Ensures(Contract.Result() != null); - return Append(value.ToString()); - } - - // Appends a ubyte to this string builder. - // The capacity is adjusted as needed. - public StringBuilder Append(byte value) - { - Contract.Ensures(Contract.Result() != null); - return Append(value.ToString()); - } - - // Appends a character at the end of this string builder. The capacity is adjusted as needed. public StringBuilder Append(char value) { - Contract.Ensures(Contract.Result() != null); - if (m_ChunkLength < m_ChunkChars.Length) + { m_ChunkChars[m_ChunkLength++] = value; + } else + { Append(value, 1); + } + return this; } - // Appends a short to this string builder. - // The capacity is adjusted as needed. - public StringBuilder Append(short value) - { - Contract.Ensures(Contract.Result() != null); - return Append(value.ToString()); - } - - // Appends an int to this string builder. - // The capacity is adjusted as needed. - public StringBuilder Append(int value) - { - Contract.Ensures(Contract.Result() != null); - return Append(value.ToString()); - } - - // Appends a long to this string builder. - // The capacity is adjusted as needed. - public StringBuilder Append(long value) - { - Contract.Ensures(Contract.Result() != null); - return Append(value.ToString()); - } - - // Appends a float to this string builder. - // The capacity is adjusted as needed. - public StringBuilder Append(float value) - { - Contract.Ensures(Contract.Result() != null); - return Append(value.ToString()); - } - - // Appends a double to this string builder. - // The capacity is adjusted as needed. - public StringBuilder Append(double value) - { - Contract.Ensures(Contract.Result() != null); - return Append(value.ToString()); - } - - public StringBuilder Append(decimal value) - { - Contract.Ensures(Contract.Result() != null); - return Append(value.ToString()); - } - - // Appends an ushort to this string builder. - // The capacity is adjusted as needed. [CLSCompliant(false)] - public StringBuilder Append(ushort value) - { - Contract.Ensures(Contract.Result() != null); - return Append(value.ToString()); - } + public StringBuilder Append(sbyte value) => AppendSpanFormattable(value); + + public StringBuilder Append(byte value) => AppendSpanFormattable(value); + + public StringBuilder Append(short value) => AppendSpanFormattable(value); + + public StringBuilder Append(int value) => AppendSpanFormattable(value); + + public StringBuilder Append(long value) => AppendSpanFormattable(value); + + public StringBuilder Append(float value) => AppendSpanFormattable(value); + + public StringBuilder Append(double value) => AppendSpanFormattable(value); + + public StringBuilder Append(decimal value) => AppendSpanFormattable(value); - // Appends an uint to this string builder. - // The capacity is adjusted as needed. [CLSCompliant(false)] - public StringBuilder Append(uint value) - { - Contract.Ensures(Contract.Result() != null); - return Append(value.ToString()); - } + public StringBuilder Append(ushort value) => AppendSpanFormattable(value); - // Appends an unsigned long to this string builder. - // The capacity is adjusted as needed. [CLSCompliant(false)] - public StringBuilder Append(ulong value) - { - Contract.Ensures(Contract.Result() != null); - return Append(value.ToString()); - } + public StringBuilder Append(uint value) => AppendSpanFormattable(value); - // Appends an Object to this string builder. - // The capacity is adjusted as needed. - public StringBuilder Append(Object value) - { - Contract.Ensures(Contract.Result() != null); + [CLSCompliant(false)] + public StringBuilder Append(ulong value) => AppendSpanFormattable(value); - if (null == value) + private StringBuilder AppendSpanFormattable(T value) where T : ISpanFormattable + { + if (value.TryFormat(RemainingCurrentChunk, out int charsWritten, format: default, provider: null)) { - //Appending null is now a no-op. + m_ChunkLength += charsWritten; return this; } + return Append(value.ToString()); } - // Appends all of the characters in value to the current instance. + public StringBuilder Append(object value) => (value == null) ? this : Append(value.ToString()); + public StringBuilder Append(char[] value) { - Contract.Ensures(Contract.Result() != null); - - if (null != value && value.Length > 0) + if (value?.Length > 0) { unsafe { fixed (char* valueChars = &value[0]) + { Append(valueChars, value.Length); + } + } + } + return this; + } + + public StringBuilder Append(ReadOnlySpan value) + { + if (value.Length > 0) + { + unsafe + { + fixed (char* valueChars = &MemoryMarshal.GetReference(value)) + { + Append(valueChars, value.Length); + } } } return this; } - #region AppendJoin public unsafe StringBuilder AppendJoin(string separator, params object[] values) @@ -1125,28 +1083,38 @@ namespace System.Text { return AppendJoinCore(&separator, 1, values); } - private unsafe StringBuilder AppendJoinCore(char* separator, int separatorLength, IEnumerable values) { - if (values == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); + Debug.Assert(separator != null); + Debug.Assert(separatorLength >= 0); - using (var en = values.GetEnumerator()) + if (values == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); + } + + using (IEnumerator en = values.GetEnumerator()) { if (!en.MoveNext()) + { return this; + } var value = en.Current; if (value != null) + { Append(value.ToString()); + } while (en.MoveNext()) { Append(separator, separatorLength); value = en.Current; if (value != null) + { Append(value.ToString()); + } } } return this; @@ -1155,43 +1123,39 @@ namespace System.Text private unsafe StringBuilder AppendJoinCore(char* separator, int separatorLength, T[] values) { if (values == null) + { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); + } if (values.Length == 0) + { return this; + } if (values[0] != null) + { Append(values[0].ToString()); + } - for (var i = 1; i < values.Length; i++) + for (int i = 1; i < values.Length; i++) { Append(separator, separatorLength); if (values[i] != null) + { Append(values[i].ToString()); + } } return this; } #endregion - - /*====================================Insert==================================== - ** - ==============================================================================*/ - - // Returns a reference to the StringBuilder with ; value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. - // public StringBuilder Insert(int index, String value) { if ((uint)index > (uint)Length) { throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); } - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); if (value != null) { @@ -1204,59 +1168,17 @@ namespace System.Text return this; } - // Returns a reference to the StringBuilder with ; value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. - // - public StringBuilder Insert(int index, bool value) - { - Contract.Ensures(Contract.Result() != null); - return Insert(index, value.ToString(), 1); - } + public StringBuilder Insert(int index, bool value) => Insert(index, value.ToString(), 1); - // Returns a reference to the StringBuilder with ; value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. - // [CLSCompliant(false)] - public StringBuilder Insert(int index, sbyte value) - { - Contract.Ensures(Contract.Result() != null); - return Insert(index, value.ToString(), 1); - } + public StringBuilder Insert(int index, sbyte value) => Insert(index, value.ToString(), 1); - // Returns a reference to the StringBuilder with ; value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. - // - public StringBuilder Insert(int index, byte value) - { - Contract.Ensures(Contract.Result() != null); - return Insert(index, value.ToString(), 1); - } + public StringBuilder Insert(int index, byte value) => Insert(index, value.ToString(), 1); - // Returns a reference to the StringBuilder with ; value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. - // - public StringBuilder Insert(int index, short value) - { - Contract.Ensures(Contract.Result() != null); - return Insert(index, value.ToString(), 1); - } + public StringBuilder Insert(int index, short value) => Insert(index, value.ToString(), 1); - // Returns a reference to the StringBuilder with ; value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. public StringBuilder Insert(int index, char value) { - Contract.Ensures(Contract.Result() != null); - unsafe { Insert(index, &value, 1); @@ -1264,40 +1186,26 @@ namespace System.Text return this; } - // Returns a reference to the StringBuilder with ; value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. - // public StringBuilder Insert(int index, char[] value) { if ((uint)index > (uint)Length) { throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); } - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); if (value != null) Insert(index, value, 0, value.Length); return this; } - // Returns a reference to the StringBuilder with charCount characters from - // value inserted into the buffer at index. Existing characters are shifted - // to make room for the new text and capacity is adjusted as required. If value is null, the StringBuilder - // is unchanged. Characters are taken from value starting at position startIndex. public StringBuilder Insert(int index, char[] value, int startIndex, int charCount) { - Contract.Ensures(Contract.Result() != null); - int currentLength = Length; if ((uint)index > (uint)currentLength) { throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); } - //If they passed in a null char array, just jump out quickly. if (value == null) { if (startIndex == 0 && charCount == 0) @@ -1307,7 +1215,6 @@ namespace System.Text throw new ArgumentNullException(nameof(value), SR.ArgumentNull_String); } - //Range check the array. if (startIndex < 0) { throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex); @@ -1334,121 +1241,50 @@ namespace System.Text return this; } - // Returns a reference to the StringBuilder with ; value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. - // - public StringBuilder Insert(int index, int value) - { - Contract.Ensures(Contract.Result() != null); - return Insert(index, value.ToString(), 1); - } + public StringBuilder Insert(int index, int value) => Insert(index, value.ToString(), 1); - // Returns a reference to the StringBuilder with ; value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. - // - public StringBuilder Insert(int index, long value) - { - Contract.Ensures(Contract.Result() != null); - return Insert(index, value.ToString(), 1); - } + public StringBuilder Insert(int index, long value) => Insert(index, value.ToString(), 1); - // Returns a reference to the StringBuilder with ; value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. - // - public StringBuilder Insert(int index, float value) - { - Contract.Ensures(Contract.Result() != null); - return Insert(index, value.ToString(), 1); - } + public StringBuilder Insert(int index, float value) => Insert(index, value.ToString(), 1); - // Returns a reference to the StringBuilder with ; value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. - // - public StringBuilder Insert(int index, double value) - { - Contract.Ensures(Contract.Result() != null); - return Insert(index, value.ToString(), 1); - } + public StringBuilder Insert(int index, double value) => Insert(index, value.ToString(), 1); - public StringBuilder Insert(int index, decimal value) - { - Contract.Ensures(Contract.Result() != null); - return Insert(index, value.ToString(), 1); - } + public StringBuilder Insert(int index, decimal value) => Insert(index, value.ToString(), 1); - // Returns a reference to the StringBuilder with value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. - // [CLSCompliant(false)] - public StringBuilder Insert(int index, ushort value) - { - Contract.Ensures(Contract.Result() != null); - return Insert(index, value.ToString(), 1); - } + public StringBuilder Insert(int index, ushort value) => Insert(index, value.ToString(), 1); - // Returns a reference to the StringBuilder with value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. - // [CLSCompliant(false)] - public StringBuilder Insert(int index, uint value) - { - Contract.Ensures(Contract.Result() != null); - return Insert(index, value.ToString(), 1); - } + public StringBuilder Insert(int index, uint value) => Insert(index, value.ToString(), 1); - // Returns a reference to the StringBuilder with value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. - // [CLSCompliant(false)] - public StringBuilder Insert(int index, ulong value) - { - Contract.Ensures(Contract.Result() != null); - return Insert(index, value.ToString(), 1); - } + public StringBuilder Insert(int index, ulong value) => Insert(index, value.ToString(), 1); - // Returns a reference to this string builder with value inserted into - // the buffer at index. Existing characters are shifted to make room for the - // new text. The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. No changes are made if value is null. - // - public StringBuilder Insert(int index, Object value) + public StringBuilder Insert(int index, Object value) => (value == null) ? this : Insert(index, value.ToString(), 1); + + public StringBuilder Insert(int index, ReadOnlySpan value) { - Contract.Ensures(Contract.Result() != null); - if (null == value) + if ((uint)index > (uint)Length) { - return this; + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); } - return Insert(index, value.ToString(), 1); + + if (value.Length > 0) + { + unsafe + { + fixed (char* sourcePtr = &MemoryMarshal.GetReference(value)) + Insert(index, sourcePtr, value.Length); + } + } + return this; } - public StringBuilder AppendFormat(String format, Object arg0) - { - Contract.Ensures(Contract.Result() != null); - return AppendFormatHelper(null, format, new ParamsArray(arg0)); - } + public StringBuilder AppendFormat(String format, Object arg0) => AppendFormatHelper(null, format, new ParamsArray(arg0)); - public StringBuilder AppendFormat(String format, Object arg0, Object arg1) - { - Contract.Ensures(Contract.Result() != null); - return AppendFormatHelper(null, format, new ParamsArray(arg0, arg1)); - } + public StringBuilder AppendFormat(String format, Object arg0, Object arg1) => AppendFormatHelper(null, format, new ParamsArray(arg0, arg1)); - public StringBuilder AppendFormat(String format, Object arg0, Object arg1, Object arg2) - { - Contract.Ensures(Contract.Result() != null); - return AppendFormatHelper(null, format, new ParamsArray(arg0, arg1, arg2)); - } + public StringBuilder AppendFormat(String format, Object arg0, Object arg1, Object arg2) => AppendFormatHelper(null, format, new ParamsArray(arg0, arg1, arg2)); public StringBuilder AppendFormat(String format, params Object[] args) { @@ -1456,31 +1292,18 @@ namespace System.Text { // To preserve the original exception behavior, throw an exception about format if both // args and format are null. The actual null check for format is in AppendFormatHelper. - throw new ArgumentNullException((format == null) ? nameof(format) : nameof(args)); + string paramName = (format == null) ? nameof(format) : nameof(args); + throw new ArgumentNullException(paramName); } - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); return AppendFormatHelper(null, format, new ParamsArray(args)); } - public StringBuilder AppendFormat(IFormatProvider provider, String format, Object arg0) - { - Contract.Ensures(Contract.Result() != null); - return AppendFormatHelper(provider, format, new ParamsArray(arg0)); - } + public StringBuilder AppendFormat(IFormatProvider provider, String format, Object arg0) => AppendFormatHelper(provider, format, new ParamsArray(arg0)); - public StringBuilder AppendFormat(IFormatProvider provider, String format, Object arg0, Object arg1) - { - Contract.Ensures(Contract.Result() != null); - return AppendFormatHelper(provider, format, new ParamsArray(arg0, arg1)); - } + public StringBuilder AppendFormat(IFormatProvider provider, String format, Object arg0, Object arg1) => AppendFormatHelper(provider, format, new ParamsArray(arg0, arg1)); - public StringBuilder AppendFormat(IFormatProvider provider, String format, Object arg0, Object arg1, Object arg2) - { - Contract.Ensures(Contract.Result() != null); - return AppendFormatHelper(provider, format, new ParamsArray(arg0, arg1, arg2)); - } + public StringBuilder AppendFormat(IFormatProvider provider, String format, Object arg0, Object arg1, Object arg2) => AppendFormatHelper(provider, format, new ParamsArray(arg0, arg1, arg2)); public StringBuilder AppendFormat(IFormatProvider provider, String format, params Object[] args) { @@ -1488,10 +1311,9 @@ namespace System.Text { // To preserve the original exception behavior, throw an exception about format if both // args and format are null. The actual null check for format is in AppendFormatHelper. - throw new ArgumentNullException((format == null) ? nameof(format) : nameof(args)); + string paramName = (format == null) ? nameof(format) : nameof(args); + throw new ArgumentNullException(paramName); } - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); return AppendFormatHelper(provider, format, new ParamsArray(args)); } @@ -1501,9 +1323,9 @@ namespace System.Text throw new FormatException(SR.Format_InvalidString); } - // undocumented exclusive limits on the range for Argument Hole Index and Argument Hole Alignment. - private const int Index_Limit = 1000000; // Note: 0 <= ArgIndex < Index_Limit - private const int Width_Limit = 1000000; // Note: -Width_Limit < ArgAlign < Width_Limit + // Undocumented exclusive limits on the range for Argument Hole Index and Argument Hole Alignment. + private const int IndexLimit = 1000000; // Note: 0 <= ArgIndex < IndexLimit + private const int WidthLimit = 1000000; // Note: -WidthLimit < ArgAlign < WidthLimit internal StringBuilder AppendFormatHelper(IFormatProvider provider, String format, ParamsArray args) { @@ -1511,8 +1333,6 @@ namespace System.Text { throw new ArgumentNullException(nameof(format)); } - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); int pos = 0; int len = format.Length; @@ -1582,7 +1402,8 @@ namespace System.Text if (pos == len) FormatError(); ch = format[pos]; // so long as character is digit and value of the index is less than 1000000 ( index limit ) - } while (ch >= '0' && ch <= '9' && index < Index_Limit); + } + while (ch >= '0' && ch <= '9' && index < IndexLimit); // If value of index is not within the range of the arguments passed in then error (Index out of range) if (index >= args.Length) throw new FormatException(SR.Format_IndexOutOfRange); @@ -1631,7 +1452,8 @@ namespace System.Text if (pos == len) FormatError(); ch = format[pos]; // So long a current character is a digit and the value of width is less than 100000 ( width limit ) - } while (ch >= '0' && ch <= '9' && width < Width_Limit); + } + while (ch >= '0' && ch <= '9' && width < WidthLimit); // end of parsing Argument Alignment } @@ -1643,6 +1465,7 @@ namespace System.Text // Object arg = args[index]; String itemFormat = null; + ReadOnlySpan itemFormatSpan = default; // used if itemFormat is null // Is current character a colon? which indicates start of formatting parameter. if (ch == ':') { @@ -1697,13 +1520,13 @@ namespace System.Text if (startPos != pos) { // There was no brace escaping, extract the item format as a single string - itemFormat = format.Substring(startPos, pos - startPos); + itemFormatSpan = format.AsReadOnlySpan().Slice(startPos, pos - startPos); } } else { unescapedItemFormat.Append(format, startPos, pos - startPos); - itemFormat = unescapedItemFormat.ToString(); + itemFormatSpan = itemFormat = unescapedItemFormat.ToString(); unescapedItemFormat.Clear(); } } @@ -1714,15 +1537,38 @@ namespace System.Text String s = null; if (cf != null) { + if (itemFormatSpan.Length != 0 && itemFormat == null) + { + itemFormat = new string(itemFormatSpan); + } s = cf.Format(itemFormat, arg, provider); } if (s == null) { - IFormattable formattableArg = arg as IFormattable; - - if (formattableArg != null) + // If arg is ISpanFormattable and the beginning doesn't need padding, + // try formatting it into the remaining current chunk. + if (arg is ISpanFormattable spanFormattableArg && + (leftJustify || width == 0) && + spanFormattableArg.TryFormat(RemainingCurrentChunk, out int charsWritten, itemFormatSpan, provider)) { + m_ChunkLength += charsWritten; + + // Pad the end, if needed. + int padding = width - charsWritten; + if (leftJustify && padding > 0) Append(' ', padding); + + // Continue to parse other characters. + continue; + } + + // Otherwise, fallback to trying IFormattable or calling ToString. + if (arg is IFormattable formattableArg) + { + if (itemFormatSpan.Length != 0 && itemFormat == null) + { + itemFormat = new string(itemFormatSpan); + } s = formattableArg.ToString(itemFormat, provider); } else if (arg != null) @@ -1741,18 +1587,21 @@ namespace System.Text return this; } - // Returns a reference to the current StringBuilder with all instances of oldString - // replaced with newString. If startIndex and count are specified, - // we only replace strings completely contained in the range of startIndex to startIndex + - // count. The strings to be replaced are checked on an ordinal basis (e.g. not culture aware). If - // newValue is null, instances of oldValue are removed (e.g. replaced with nothing.). - // - public StringBuilder Replace(String oldValue, String newValue) - { - Contract.Ensures(Contract.Result() != null); - return Replace(oldValue, newValue, 0, Length); - } + /// + /// Replaces all instances of one string with another in this builder. + /// + /// The string to replace. + /// The string to replace with. + /// + /// If is null, instances of + /// are removed from this builder. + /// + public StringBuilder Replace(String oldValue, String newValue) => Replace(oldValue, newValue, 0, Length); + /// + /// Determines if the contents of this builder are equal to the contents of another builder. + /// + /// The other builder. public bool Equals(StringBuilder sb) { if (sb == null) @@ -1768,7 +1617,6 @@ namespace System.Text int sbChunkIndex = sbChunk.m_ChunkLength; for (;;) { - // Decrement the pointer to the 'this' StringBuilder --thisChunkIndex; --sbChunkIndex; @@ -1780,7 +1628,6 @@ namespace System.Text thisChunkIndex = thisChunk.m_ChunkLength + thisChunkIndex; } - // Decrement the pointer to the 'this' StringBuilder while (sbChunkIndex < 0) { sbChunk = sbChunk.m_ChunkPrevious; @@ -1798,10 +1645,19 @@ namespace System.Text } } + /// + /// Replaces all instances of one string with another in part of this builder. + /// + /// The string to replace. + /// The string to replace with. + /// The index to start in this builder. + /// The number of characters to read in this builder. + /// + /// If is null, instances of + /// are removed from this builder. + /// public StringBuilder Replace(String oldValue, String newValue, int startIndex, int count) { - Contract.Ensures(Contract.Result() != null); - int currentLength = Length; if ((uint)startIndex > (uint)currentLength) { @@ -1820,12 +1676,11 @@ namespace System.Text throw new ArgumentException(SR.Argument_EmptyName, nameof(oldValue)); } - if (newValue == null) - newValue = ""; + newValue = newValue ?? string.Empty; int deltaLength = newValue.Length - oldValue.Length; - int[] replacements = null; // A list of replacement positions in a chunk to apply + int[] replacements = null; // A list of replacement positions in a chunk to apply int replacementsCount = 0; // Find the chunk, indexInChunk for the starting point @@ -1836,14 +1691,16 @@ namespace System.Text // Look for a match in the chunk,indexInChunk pointer if (StartsWith(chunk, indexInChunk, count, oldValue)) { - // Push it on my replacements array (with growth), we will do all replacements in a + // Push it on the replacements array (with growth), we will do all replacements in a // given chunk in one operation below (see ReplaceAllInChunk) so we don't have to slide - // many times. + // many times. if (replacements == null) + { replacements = new int[5]; + } else if (replacementsCount >= replacements.Length) { - Array.Resize(ref replacements, replacements.Length * 3 / 2 + 4); // grow by 1.5X but more in the beginning + Array.Resize(ref replacements, replacements.Length * 3 / 2 + 4); // Grow by ~1.5x, but more in the begining } replacements[replacementsCount++] = indexInChunk; indexInChunk += oldValue.Length; @@ -1855,13 +1712,13 @@ namespace System.Text --count; } - if (indexInChunk >= chunk.m_ChunkLength || count == 0) // Have we moved out of the current chunk + if (indexInChunk >= chunk.m_ChunkLength || count == 0) // Have we moved out of the current chunk? { - // Replacing mutates the blocks, so we need to convert to logical index and back afterward. + // Replacing mutates the blocks, so we need to convert to a logical index and back afterwards. int index = indexInChunk + chunk.m_ChunkOffset; int indexBeforeAdjustment = index; - // See if we accumulated any replacements, if so apply them + // See if we accumulated any replacements, if so apply them. ReplaceAllInChunk(replacements, replacementsCount, chunk, oldValue.Length, newValue); // The replacement has affected the logical index. Adjust it. index += ((newValue.Length - oldValue.Length) * replacementsCount); @@ -1869,26 +1726,33 @@ namespace System.Text chunk = FindChunkForIndex(index); indexInChunk = index - chunk.m_ChunkOffset; - Debug.Assert(chunk != null || count == 0, "Chunks ended prematurely"); + Debug.Assert(chunk != null || count == 0, "Chunks ended prematurely!"); } } - VerifyClassInvariant(); + + AssertInvariants(); return this; } - // Returns a StringBuilder with all instances of oldChar replaced with - // newChar. The size of the StringBuilder is unchanged because we're only - // replacing characters. If startIndex and count are specified, we - // only replace characters in the range from startIndex to startIndex+count - // + /// + /// Replaces all instances of one character with another in this builder. + /// + /// The character to replace. + /// The character to replace with. public StringBuilder Replace(char oldChar, char newChar) { return Replace(oldChar, newChar, 0, Length); } + + /// + /// Replaces all instances of one character with another in this builder. + /// + /// The character to replace. + /// The character to replace with. + /// The index to start in this builder. + /// The number of characters to read in this builder. public StringBuilder Replace(char oldChar, char newChar, int startIndex, int count) { - Contract.Ensures(Contract.Result() != null); - int currentLength = Length; if ((uint)startIndex > (uint)currentLength) { @@ -1902,6 +1766,7 @@ namespace System.Text int endIndex = startIndex + count; StringBuilder chunk = this; + for (;;) { int endIndexInChunk = endIndex - chunk.m_ChunkOffset; @@ -1921,12 +1786,16 @@ namespace System.Text break; chunk = chunk.m_ChunkPrevious; } + + AssertInvariants(); return this; } /// - /// Appends 'value' of length 'count' to the stringBuilder. + /// Appends a character buffer to this builder. /// + /// The pointer to the start of the buffer. + /// The number of characters in the buffer. [CLSCompliant(false)] public unsafe StringBuilder Append(char* value, int valueCount) { @@ -1964,20 +1833,23 @@ namespace System.Text // Expand the builder to add another chunk. int restLength = valueCount - firstLength; ExpandByABlock(restLength); - Debug.Assert(m_ChunkLength == 0, "Expand did not make a new block"); + Debug.Assert(m_ChunkLength == 0, "A new block was not created."); // Copy the second chunk ThreadSafeCopy(value + firstLength, m_ChunkChars, 0, restLength); m_ChunkLength = restLength; } - VerifyClassInvariant(); + AssertInvariants(); return this; } /// - /// Inserts 'value' of length 'cou + /// Inserts a character buffer into this builder at the specified position. /// - unsafe private void Insert(int index, char* value, int valueCount) + /// The index to insert in this builder. + /// The pointer to the start of the buffer. + /// The number of characters in the buffer. + private unsafe void Insert(int index, char* value, int valueCount) { if ((uint)index > (uint)Length) { @@ -1994,15 +1866,22 @@ namespace System.Text } /// - /// 'replacements' is a list of index (relative to the begining of the 'chunk' to remove - /// 'removeCount' characters and replace them with 'value'. This routine does all those - /// replacements in bulk (and therefore very efficiently. - /// with the string 'value'. + /// Replaces strings at specified indices with a new string in a chunk. /// + /// The list of indices, relative to the beginning of the chunk, to remove at. + /// The number of replacements to make. + /// The source chunk. + /// The number of characters to remove at each replacement. + /// The string to insert at each replacement. + /// + /// This routine is very efficient because it does replacements in bulk. + /// private void ReplaceAllInChunk(int[] replacements, int replacementsCount, StringBuilder sourceChunk, int removeCount, string value) { if (replacementsCount <= 0) + { return; + } unsafe { @@ -2026,7 +1905,9 @@ namespace System.Text int gapStart = replacements[i] + removeCount; i++; if (i >= replacementsCount) + { break; + } int gapEnd = replacements[i]; Debug.Assert(gapStart < sourceChunk.m_ChunkChars.Length, "gap starts at end of buffer. Should not happen"); @@ -2034,7 +1915,7 @@ namespace System.Text Debug.Assert(gapEnd <= sourceChunk.m_ChunkLength, "gap too big"); if (delta != 0) // can skip the sliding of gaps if source an target string are the same size. { - // Copy the gap data between the current replacement and the the next replacement + // Copy the gap data between the current replacement and the next replacement fixed (char* sourcePtr = &sourceChunk.m_ChunkChars[gapStart]) ReplaceInPlaceAtChunk(ref targetChunk, ref targetIndexInChunk, sourcePtr, gapEnd - gapStart); } @@ -2053,15 +1934,21 @@ namespace System.Text } /// - /// Returns true if the string that is starts at 'chunk' and 'indexInChunk, and has a logical - /// length of 'count' starts with the string 'value'. + /// Returns a value indicating whether a substring of a builder starts with a specified prefix. /// + /// The chunk in which the substring starts. + /// The index in at which the substring starts. + /// The logical count of the substring. + /// The prefix. private bool StartsWith(StringBuilder chunk, int indexInChunk, int count, string value) { for (int i = 0; i < value.Length; i++) { if (count == 0) + { return false; + } + if (indexInChunk >= chunk.m_ChunkLength) { chunk = Next(chunk); @@ -2070,22 +1957,32 @@ namespace System.Text indexInChunk = 0; } - // See if there no match, break out of the inner for loop if (value[i] != chunk.m_ChunkChars[indexInChunk]) + { return false; + } indexInChunk++; --count; } + return true; } /// - /// ReplaceInPlaceAtChunk is the logical equivalent of 'memcpy'. Given a chunk and ann index in - /// that chunk, it copies in 'count' characters from 'value' and updates 'chunk, and indexInChunk to - /// point at the end of the characters just copyied (thus you can splice in strings from multiple - /// places by calling this mulitple times. + /// Replaces characters at a specified location with the contents of a character buffer. + /// This function is the logical equivalent of memcpy. /// + /// + /// The chunk in which to start replacing characters. + /// Receives the chunk in which character replacement ends. + /// + /// + /// The index in to start replacing characters at. + /// Receives the index at which character replacement ends. + /// + /// The pointer to the start of the character buffer. + /// The number of characters in the buffer. unsafe private void ReplaceInPlaceAtChunk(ref StringBuilder chunk, ref int indexInChunk, char* value, int count) { if (count != 0) @@ -2093,7 +1990,7 @@ namespace System.Text for (;;) { int lengthInChunk = chunk.m_ChunkLength - indexInChunk; - Debug.Assert(lengthInChunk >= 0, "index not in chunk"); + Debug.Assert(lengthInChunk >= 0, "Index isn't in the chunk."); int lengthToCopy = Math.Min(lengthInChunk, count); ThreadSafeCopy(value, chunk.m_ChunkChars, indexInChunk, lengthToCopy); @@ -2107,17 +2004,18 @@ namespace System.Text } count -= lengthToCopy; if (count == 0) + { break; + } value += lengthToCopy; } } } - /// - /// We have to prevent modification off the end of an array. - /// The only way to do this is to copy all interesting variables out of the heap and then do the - /// bounds check. This is what we do here. - /// + /// + /// This method prevents out-of-bounds writes in the case a different thread updates a field in the builder just before a copy begins. + /// All interesting variables are copied out of the heap into the parameters of this method, and then bounds checks are run. + /// private static unsafe void ThreadSafeCopy(char* sourcePtr, char[] destination, int destinationIndex, int count) { if (count > 0) @@ -2134,101 +2032,118 @@ namespace System.Text } } - private static void ThreadSafeCopy(char[] source, int sourceIndex, char[] destination, int destinationIndex, int count) + private static unsafe void ThreadSafeCopy(char[] source, int sourceIndex, Span destination, int destinationIndex, int count) { if (count > 0) { - if ((uint)sourceIndex <= (uint)source.Length && (sourceIndex + count) <= source.Length) - { - unsafe - { - fixed (char* sourcePtr = &source[sourceIndex]) - ThreadSafeCopy(sourcePtr, destination, destinationIndex, count); - } - } - else + if ((uint)sourceIndex > (uint)source.Length || count > source.Length - sourceIndex) { throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_Index); } + + if ((uint)destinationIndex > (uint)destination.Length || count > destination.Length - destinationIndex) + { + throw new ArgumentOutOfRangeException(nameof(destinationIndex), SR.ArgumentOutOfRange_Index); + } + + fixed (char* sourcePtr = &source[sourceIndex]) + fixed (char* destinationPtr = &MemoryMarshal.GetReference(destination)) + string.wstrcpy(destinationPtr + destinationIndex, sourcePtr, count); } } /// - /// Finds the chunk for the logical index (number of characters in the whole stringbuilder) 'index' - /// YOu can then get the offset in this chunk by subtracting the m_BlockOffset field from 'index' + /// Gets the chunk corresponding to the logical index in this builder. /// - /// - /// + /// The logical index in this builder. + /// + /// After calling this method, you can obtain the actual index within the chunk by + /// subtracting from . + /// private StringBuilder FindChunkForIndex(int index) { - Debug.Assert(0 <= index && index <= Length, "index not in string"); + Debug.Assert(0 <= index && index <= Length); - StringBuilder ret = this; - while (ret.m_ChunkOffset > index) - ret = ret.m_ChunkPrevious; + StringBuilder result = this; + while (result.m_ChunkOffset > index) + { + result = result.m_ChunkPrevious; + } - Debug.Assert(ret != null, "index not in string"); - return ret; + Debug.Assert(result != null); + return result; } /// - /// Finds the chunk for the logical byte index 'byteIndex' + /// Gets the chunk corresponding to the logical byte index in this builder. /// - /// - /// + /// The logical byte index in this builder. private StringBuilder FindChunkForByte(int byteIndex) { - Debug.Assert(0 <= byteIndex && byteIndex <= Length * sizeof(char), "Byte Index not in string"); + Debug.Assert(0 <= byteIndex && byteIndex <= Length * sizeof(char)); - StringBuilder ret = this; - while (ret.m_ChunkOffset * sizeof(char) > byteIndex) - ret = ret.m_ChunkPrevious; + StringBuilder result = this; + while (result.m_ChunkOffset * sizeof(char) > byteIndex) + { + result = result.m_ChunkPrevious; + } - Debug.Assert(ret != null, "Byte Index not in string"); - return ret; + Debug.Assert(result != null); + return result; } - /// - /// Finds the chunk that logically follows the 'chunk' chunk. Chunks only persist the pointer to - /// the chunk that is logically before it, so this routine has to start at the this pointer (which - /// is a assumed to point at the chunk representing the whole stringbuilder) and search - /// until it finds the current chunk (thus is O(n)). So it is more expensive than a field fetch! - /// - private StringBuilder Next(StringBuilder chunk) + /// Gets a span representing the remaining space available in the current chunk. + private Span RemainingCurrentChunk { - if (chunk == this) - return null; - return FindChunkForIndex(chunk.m_ChunkOffset + chunk.m_ChunkLength); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Span(m_ChunkChars, m_ChunkLength, m_ChunkChars.Length - m_ChunkLength); } /// - /// Assumes that 'this' is the last chunk in the list and that it is full. Upon return the 'this' - /// block is updated so that it is a new block that has at least 'minBlockCharCount' characters. - /// that can be used to copy characters into it. + /// Finds the chunk that logically succeeds the specified chunk. /// + /// The chunk whose successor should be found. + /// + /// Each chunk only stores the pointer to its logical predecessor, so this routine has to start + /// from the 'this' pointer (which is assumed to represent the whole StringBuilder) and work its + /// way down until it finds the specified chunk (which is O(n)). Thus, it is more expensive than + /// a field fetch. + /// + private StringBuilder Next(StringBuilder chunk) => chunk == this ? null : FindChunkForIndex(chunk.m_ChunkOffset + chunk.m_ChunkLength); + + /// + /// Transfers the character buffer from this chunk to a new chunk, and allocates a new buffer with a minimum size for this chunk. + /// + /// The minimum size of the new buffer to be allocated for this chunk. + /// + /// This method requires that the current chunk is full. Otherwise, there's no point in shifting the characters over. + /// It also assumes that 'this' is the last chunk in the linked list. + /// private void ExpandByABlock(int minBlockCharCount) { - Contract.Requires(Capacity == Length, "Expand expect to be called only when there is no space left"); // We are currently full - Contract.Requires(minBlockCharCount > 0, "Expansion request must be positive"); + Debug.Assert(Capacity == Length, nameof(ExpandByABlock) + " should only be called when there is no space left."); + Debug.Assert(minBlockCharCount > 0); - VerifyClassInvariant(); + AssertInvariants(); if ((minBlockCharCount + Length) > m_MaxCapacity || minBlockCharCount + Length < minBlockCharCount) + { throw new ArgumentOutOfRangeException("requiredLength", SR.ArgumentOutOfRange_SmallCapacity); + } - // Compute the length of the new block we need - // We make the new chunk at least big enough for the current need (minBlockCharCount) - // But also as big as the current length (thus doubling capacity), up to a maximum - // (so we stay in the small object heap, and never allocate really big chunks even if - // the string gets really big. + // - We always need to make the new chunk at least as big as was requested (`minBlockCharCount`). + // - We'd also prefer to make it at least at big as the current length (thus doubling capacity). + // - But this is only up to a maximum, so we stay in the small object heap, and never allocate + // really big chunks even if the string gets really big. int newBlockLength = Math.Max(minBlockCharCount, Math.Min(Length, MaxChunkSize)); - // Copy the current block to the new block, and initialize this to point at the new buffer. + // Move all of the data from this chunk to a new one, via a few O(1) pointer adjustments. + // Then, have this chunk point to the new one as its predecessor. m_ChunkPrevious = new StringBuilder(this); m_ChunkOffset += m_ChunkLength; m_ChunkLength = 0; - // Check for integer overflow (logical buffer size > int.MaxInt) + // Check for integer overflow (logical buffer size > int.MaxValue) if (m_ChunkOffset + newBlockLength < newBlockLength) { m_ChunkChars = null; @@ -2236,14 +2151,24 @@ namespace System.Text } m_ChunkChars = new char[newBlockLength]; - VerifyClassInvariant(); + AssertInvariants(); } /// - /// Used by ExpandByABlock to create a new chunk. The new chunk is a copied from 'from' - /// In particular the buffer is shared. It is expected that 'from' chunk (which represents - /// the whole list, is then updated to point to point to this new chunk. + /// Creates a new chunk with fields copied from an existing chunk. /// + /// The chunk from which to copy fields. + /// + /// + /// This method runs in O(1) time. It does not copy data within the character buffer + /// holds, but copies the reference to the character buffer itself + /// (plus a few other fields). + /// + /// + /// Callers are expected to update subsequently to point to this + /// chunk as its predecessor. + /// + /// private StringBuilder(StringBuilder from) { m_ChunkLength = from.m_ChunkLength; @@ -2251,29 +2176,46 @@ namespace System.Text m_ChunkChars = from.m_ChunkChars; m_ChunkPrevious = from.m_ChunkPrevious; m_MaxCapacity = from.m_MaxCapacity; - VerifyClassInvariant(); + + AssertInvariants(); } /// - /// Creates a gap of size 'count' at the logical offset (count of characters in the whole string - /// builder) 'index'. It returns the 'chunk' and 'indexInChunk' which represents a pointer to - /// this gap that was just created. You can then use 'ReplaceInPlaceAtChunk' to fill in the - /// chunk - /// - /// ReplaceAllChunks relies on the fact that indexes above 'index' are NOT moved outside 'chunk' - /// by this process (because we make the space by creating the cap BEFORE the chunk). If we - /// change this ReplaceAllChunks needs to be updated. - /// - /// If dontMoveFollowingChars is true, then the room must be made by inserting a chunk BEFORE the - /// current chunk (this is what it does most of the time anyway) + /// Creates a gap at a logical index with the specified count. /// - private void MakeRoom(int index, int count, out StringBuilder chunk, out int indexInChunk, bool doneMoveFollowingChars) + /// The logical index in this builder. + /// The number of characters in the gap. + /// Receives the chunk containing the gap. + /// The index in that points to the gap. + /// + /// - If true, then room must be made by inserting a chunk before the current chunk. + /// - If false, then room can be made by shifting characters ahead of + /// in this block forward by provided the characters will still fit in + /// the current chunk after being shifted. + /// - Providing false does not make a difference most of the time, but it can matter when someone + /// inserts lots of small strings at a position in the buffer. + /// + /// + /// + /// Since chunks do not contain references to their successors, it is not always possible for us to make room + /// by inserting space after in case this chunk runs out of space. Thus, we make room + /// by inserting space before the specified index, and having logical indices refer to new locations by the end + /// of this method. + /// + /// + /// can be used in conjunction with this method to fill in the newly created gap. + /// + /// + private void MakeRoom(int index, int count, out StringBuilder chunk, out int indexInChunk, bool doNotMoveFollowingChars) { - VerifyClassInvariant(); - Debug.Assert(count > 0, "Count must be strictly positive"); - Debug.Assert(index >= 0, "Index can't be negative"); + AssertInvariants(); + Debug.Assert(count > 0); + Debug.Assert(index >= 0); + if (count + Length > m_MaxCapacity || count + Length < count) + { throw new ArgumentOutOfRangeException("requiredLength", SR.ArgumentOutOfRange_SmallCapacity); + } chunk = this; while (chunk.m_ChunkOffset > index) @@ -2283,12 +2225,11 @@ namespace System.Text } indexInChunk = index - chunk.m_ChunkOffset; - // Cool, we have some space in this block, and you don't have to copy much to get it, go ahead - // and use it. This happens typically when you repeatedly insert small strings at a spot - // (typically the absolute front) of the buffer. - if (!doneMoveFollowingChars && chunk.m_ChunkLength <= DefaultCapacity * 2 && chunk.m_ChunkChars.Length - chunk.m_ChunkLength >= count) + // Cool, we have some space in this block, and we don't have to copy much to get at it, so go ahead and use it. + // This typically happens when someone repeatedly inserts small strings at a spot (usually the absolute front) of the buffer. + if (!doNotMoveFollowingChars && chunk.m_ChunkLength <= DefaultCapacity * 2 && chunk.m_ChunkChars.Length - chunk.m_ChunkLength >= count) { - for (int i = chunk.m_ChunkLength; i > indexInChunk;) + for (int i = chunk.m_ChunkLength; i > indexInChunk; ) { --i; chunk.m_ChunkChars[i + count] = chunk.m_ChunkChars[i]; @@ -2297,11 +2238,11 @@ namespace System.Text return; } - // Allocate space for the new chunk (will go before this one) + // Allocate space for the new chunk, which will go before the current one. StringBuilder newChunk = new StringBuilder(Math.Max(count, DefaultCapacity), chunk.m_MaxCapacity, chunk.m_ChunkPrevious); newChunk.m_ChunkLength = count; - // Copy the head of the buffer to the new buffer. + // Copy the head of the current buffer to the new buffer. int copyCount1 = Math.Min(count, indexInChunk); if (copyCount1 > 0) { @@ -2311,7 +2252,7 @@ namespace System.Text { ThreadSafeCopy(chunkCharsPtr, newChunk.m_ChunkChars, 0, copyCount1); - // Slide characters in the current buffer over to make room. + // Slide characters over in the current buffer to make room. int copyCount2 = indexInChunk - copyCount1; if (copyCount2 >= 0) { @@ -2322,7 +2263,8 @@ namespace System.Text } } - chunk.m_ChunkPrevious = newChunk; // Wire in the new chunk + // Wire in the new chunk. + chunk.m_ChunkPrevious = newChunk; chunk.m_ChunkOffset += count; if (copyCount1 < count) { @@ -2330,32 +2272,44 @@ namespace System.Text indexInChunk = copyCount1; } - VerifyClassInvariant(); + AssertInvariants(); } /// - /// Used by MakeRoom to allocate another chunk. + /// Used by to allocate another chunk. /// + /// The size of the character buffer for this chunk. + /// The maximum capacity, to be stored in this chunk. + /// The predecessor of this chunk. private StringBuilder(int size, int maxCapacity, StringBuilder previousBlock) { - Debug.Assert(size > 0, "size not positive"); - Debug.Assert(maxCapacity > 0, "maxCapacity not positive"); + Debug.Assert(size > 0); + Debug.Assert(maxCapacity > 0); + m_ChunkChars = new char[size]; m_MaxCapacity = maxCapacity; m_ChunkPrevious = previousBlock; if (previousBlock != null) + { m_ChunkOffset = previousBlock.m_ChunkOffset + previousBlock.m_ChunkLength; - VerifyClassInvariant(); + } + + AssertInvariants(); } /// - /// Removes 'count' characters from the logical index 'startIndex' and returns the chunk and - /// index in the chunk of that logical index in the out parameters. + /// Removes a specified number of characters beginning at a logical index in this builder. /// + /// The logical index in this builder to start removing characters. + /// The number of characters to remove. + /// Receives the new chunk containing the logical index. + /// + /// Receives the new index in that is associated with the logical index. + /// private void Remove(int startIndex, int count, out StringBuilder chunk, out int indexInChunk) { - VerifyClassInvariant(); - Debug.Assert(startIndex >= 0 && startIndex < Length, "startIndex not in string"); + AssertInvariants(); + Debug.Assert(startIndex >= 0 && startIndex < Length); int endIndex = startIndex + count; @@ -2384,21 +2338,21 @@ namespace System.Text } chunk = chunk.m_ChunkPrevious; } - Debug.Assert(chunk != null, "fell off beginning of string!"); + Debug.Assert(chunk != null, "We fell off the beginning of the string!"); int copyTargetIndexInChunk = indexInChunk; int copyCount = endChunk.m_ChunkLength - endIndexInChunk; if (endChunk != chunk) { copyTargetIndexInChunk = 0; - // Remove the characters after startIndex to end of the chunk + // Remove the characters after `startIndex` to the end of the chunk. chunk.m_ChunkLength = indexInChunk; - // Remove the characters in chunks between start and end chunk + // Remove the characters in chunks between the start and the end chunk. endChunk.m_ChunkPrevious = chunk; endChunk.m_ChunkOffset = chunk.m_ChunkOffset + chunk.m_ChunkLength; - // If the start is 0 then we can throw away the whole start chunk + // If the start is 0, then we can throw away the whole start chunk. if (indexInChunk == 0) { endChunk.m_ChunkPrevious = chunk.m_ChunkPrevious; @@ -2407,15 +2361,17 @@ namespace System.Text } endChunk.m_ChunkLength -= (endIndexInChunk - copyTargetIndexInChunk); - // SafeCritical: We ensure that endIndexInChunk + copyCount is within range of m_ChunkChars and - // also ensure that copyTargetIndexInChunk + copyCount is within the chunk - // - // Remove any characters in the end chunk, by sliding the characters down. - if (copyTargetIndexInChunk != endIndexInChunk) // Sometimes no move is necessary - ThreadSafeCopy(endChunk.m_ChunkChars, endIndexInChunk, endChunk.m_ChunkChars, copyTargetIndexInChunk, copyCount); + // SafeCritical: We ensure that `endIndexInChunk + copyCount` is within range of `m_ChunkChars`, and + // also ensure that `copyTargetIndexInChunk + copyCount` is within the chunk. - Debug.Assert(chunk != null, "fell off beginning of string!"); - VerifyClassInvariant(); + // Remove any characters in the end chunk, by sliding the characters down. + if (copyTargetIndexInChunk != endIndexInChunk) // Sometimes no move is necessary + { + ThreadSafeCopy(endChunk.m_ChunkChars, endIndexInChunk, endChunk.m_ChunkChars, copyTargetIndexInChunk, copyCount); + } + + Debug.Assert(chunk != null, "We fell off the beginning of the string!"); + AssertInvariants(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/UTF32Encoding.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/UTF32Encoding.cs index 450aee2a4a..7828775ea0 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Text/UTF32Encoding.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/UTF32Encoding.cs @@ -8,8 +8,8 @@ using System; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Globalization; +using System.Runtime.InteropServices; namespace System.Text { @@ -39,6 +39,9 @@ namespace System.Text internal static readonly UTF32Encoding s_default = new UTF32Encoding(bigEndian: false, byteOrderMark: true); internal static readonly UTF32Encoding s_bigEndianDefault = new UTF32Encoding(bigEndian: true, byteOrderMark: true); + private static readonly byte[] s_bigEndianPreamble = new byte[4] { 0x00, 0x00, 0xFE, 0xFF }; + private static readonly byte[] s_littleEndianPreamble = new byte[4] { 0xFF, 0xFE, 0x00, 0x00 }; + private bool _emitUTF32ByteOrderMark = false; private bool _isThrowException = false; private bool _bigEndian = false; @@ -84,7 +87,7 @@ namespace System.Text // The following methods are copied from EncodingNLS.cs. - // Unfortunately EncodingNLS.cs is internal and we're public, so we have to reimpliment them here. + // Unfortunately EncodingNLS.cs is internal and we're public, so we have to re-implement them here. // These should be kept in sync for the following classes: // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding @@ -107,7 +110,6 @@ namespace System.Text if (chars.Length - index < count) throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); // If no input, return 0, avoid fixed empty array problem if (count == 0) @@ -128,7 +130,6 @@ namespace System.Text // Validate input if (s==null) throw new ArgumentNullException("s"); - Contract.EndContractBlock(); fixed (char* pChars = s) return GetByteCount(pChars, s.Length, null); @@ -147,7 +148,6 @@ namespace System.Text if (count < 0) throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Call it with empty encoder return GetByteCount(chars, count, null); @@ -172,15 +172,10 @@ namespace System.Text if (byteIndex < 0 || byteIndex > bytes.Length) throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); int byteCount = bytes.Length - byteIndex; - // Fix our input array if 0 length because fixed doesn't like 0 length arrays - if (bytes.Length == 0) - bytes = new byte[1]; - - fixed (char* pChars = s) fixed (byte* pBytes = &bytes[0]) + fixed (char* pChars = s) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null); } @@ -213,7 +208,6 @@ namespace System.Text if (byteIndex < 0 || byteIndex > bytes.Length) throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); // If nothing to encode return 0, avoid fixed problem if (charCount == 0) @@ -222,11 +216,7 @@ namespace System.Text // Just call pointer version int byteCount = bytes.Length - byteIndex; - // Fix our input array if 0 length because fixed doesn't like 0 length arrays - if (bytes.Length == 0) - bytes = new byte[1]; - - fixed (char* pChars = chars) fixed (byte* pBytes = &bytes[0]) + fixed (char* pChars = chars) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) // Remember that byteCount is # to decode, not size of array. return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null); } @@ -244,7 +234,6 @@ namespace System.Text if (charCount < 0 || byteCount < 0) throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); return GetBytes(chars, charCount, bytes, byteCount, null); } @@ -268,7 +257,6 @@ namespace System.Text if (bytes.Length - index < count) throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); // If no input just return 0, fixed doesn't like 0 length arrays. if (count == 0) @@ -292,7 +280,6 @@ namespace System.Text if (count < 0) throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); return GetCharCount(bytes, count, null); } @@ -317,7 +304,6 @@ namespace System.Text if (charIndex < 0 || charIndex > chars.Length) throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); // If no input, return 0 & avoid fixed problem if (byteCount == 0) @@ -326,11 +312,7 @@ namespace System.Text // Just call pointer version int charCount = chars.Length - charIndex; - // Fix our input array if 0 length because fixed doesn't like 0 length arrays - if (chars.Length == 0) - chars = new char[1]; - - fixed (byte* pBytes = bytes) fixed (char* pChars = &chars[0]) + fixed (byte* pBytes = bytes) fixed (char* pChars = &MemoryMarshal.GetReference((Span)chars)) // Remember that charCount is # to decode, not size of array return GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, null); } @@ -348,7 +330,6 @@ namespace System.Text if (charCount < 0 || byteCount < 0) throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); return GetChars(bytes, byteCount, chars, charCount, null); } @@ -372,7 +353,6 @@ namespace System.Text if (bytes.Length - index < count) throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); // Avoid problems with empty input buffer if (count == 0) return String.Empty; @@ -403,7 +383,7 @@ namespace System.Text if (encoder != null) { - highSurrogate = encoder.charLeftOver; + highSurrogate = encoder._charLeftOver; fallbackBuffer = encoder.FallbackBuffer; // We mustn't have left over fallback data when counting @@ -509,7 +489,7 @@ namespace System.Text throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_GetByteCountOverflow); // Shouldn't have anything in fallback buffer for GetByteCount - // (don't have to check m_throwOnOverflow for count) + // (don't have to check _throwOnOverflow for count) Debug.Assert(fallbackBuffer.Remaining == 0, "[UTF32Encoding.GetByteCount]Expected empty fallback buffer at end"); @@ -538,11 +518,11 @@ namespace System.Text if (encoder != null) { - highSurrogate = encoder.charLeftOver; + highSurrogate = encoder._charLeftOver; fallbackBuffer = encoder.FallbackBuffer; // We mustn't have left over fallback data when not converting - if (encoder.m_throwOnOverflow && fallbackBuffer.Remaining > 0) + if (encoder._throwOnOverflow && fallbackBuffer.Remaining > 0) throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, encoder.Fallback.GetType())); } else @@ -709,10 +689,10 @@ namespace System.Text if (encoder != null) { // Remember our left over surrogate (or 0 if flushing) - encoder.charLeftOver = highSurrogate; + encoder._charLeftOver = highSurrogate; // Need # chars used - encoder.m_charsUsed = (int)(chars - charStart); + encoder._charsUsed = (int)(chars - charStart); } // return the new length @@ -746,7 +726,7 @@ namespace System.Text fallbackBuffer = decoder.FallbackBuffer; // Shouldn't have anything in fallback buffer for GetCharCount - // (don't have to check m_throwOnOverflow for chars or count) + // (don't have to check _throwOnOverflow for chars or count) Debug.Assert(fallbackBuffer.Remaining == 0, "[UTF32Encoding.GetCharCount]Expected empty fallback buffer at start"); } @@ -853,7 +833,7 @@ namespace System.Text throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_GetByteCountOverflow); // Shouldn't have anything in fallback buffer for GetCharCount - // (don't have to check m_throwOnOverflow for chars or count) + // (don't have to check _throwOnOverflow for chars or count) Debug.Assert(fallbackBuffer.Remaining == 0, "[UTF32Encoding.GetCharCount]Expected empty fallback buffer at end"); @@ -894,7 +874,7 @@ namespace System.Text fallbackBuffer = baseDecoder.FallbackBuffer; // Shouldn't have anything in fallback buffer for GetChars - // (don't have to check m_throwOnOverflow for chars) + // (don't have to check _throwOnOverflow for chars) Debug.Assert(fallbackBuffer.Remaining == 0, "[UTF32Encoding.GetChars]Expected empty fallback buffer at start"); } @@ -957,7 +937,6 @@ namespace System.Text if (!fallbackResult) { - // Couldn't fallback, throw or wait til next time // We either read enough bytes for bytes-=4 to work, or we're // going to throw in ThrowCharsOverflow because chars == charStart @@ -1065,11 +1044,11 @@ namespace System.Text { decoder.iChar = (int)iChar; decoder.readByteCount = readCount; - decoder.m_bytesUsed = (int)(bytes - byteStart); + decoder._bytesUsed = (int)(bytes - byteStart); } // Shouldn't have anything in fallback buffer for GetChars - // (don't have to check m_throwOnOverflow for chars) + // (don't have to check _throwOnOverflow for chars) Debug.Assert(fallbackBuffer.Remaining == 0, "[UTF32Encoding.GetChars]Expected empty fallback buffer at end"); @@ -1111,7 +1090,6 @@ namespace System.Text if (charCount < 0) throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Characters would be # of characters + 1 in case left over high surrogate is ? * max fallback long byteCount = (long)charCount + 1; @@ -1134,7 +1112,6 @@ namespace System.Text if (byteCount < 0) throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // A supplementary character becomes 2 surrogate characters, so 4 input bytes becomes 2 chars, // plus we may have 1 surrogate char left over if the decoder has 3 bytes in it already for a non-bmp char. @@ -1177,6 +1154,10 @@ namespace System.Text return Array.Empty(); } + public override ReadOnlySpan Preamble => + GetType() != typeof(UTF32Encoding) ? GetPreamble() : // in case a derived UTF32Encoding overrode GetPreamble + _emitUTF32ByteOrderMark ? (_bigEndian ? s_bigEndianPreamble : s_littleEndianPreamble) : + Array.Empty(); public override bool Equals(Object value) { @@ -1214,8 +1195,8 @@ namespace System.Text { this.iChar = 0; this.readByteCount = 0; - if (m_fallbackBuffer != null) - m_fallbackBuffer.Reset(); + if (_fallbackBuffer != null) + _fallbackBuffer.Reset(); } // Anything left in our decoder? diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/UTF7Encoding.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/UTF7Encoding.cs new file mode 100644 index 0000000000..0246c28915 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/UTF7Encoding.cs @@ -0,0 +1,978 @@ +// 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. + +// +// Don't override IsAlwaysNormalized because it is just a Unicode Transformation and could be confused. +// + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Text +{ + public class UTF7Encoding : Encoding + { + private const String base64Chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + // 0123456789111111111122222222223333333333444444444455555555556666 + // 012345678901234567890123456789012345678901234567890123 + + // These are the characters that can be directly encoded in UTF7. + private const String directChars = + "\t\n\r '(),-./0123456789:?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + // These are the characters that can be optionally directly encoded in UTF7. + private const String optionalChars = + "!\"#$%&*;<=>@[]^_`{|}"; + + // Used by Encoding.UTF7 for lazy initialization + // The initialization code will not be run until a static member of the class is referenced + internal static readonly UTF7Encoding s_default = new UTF7Encoding(); + + // The set of base 64 characters. + private byte[] _base64Bytes; + // The decoded bits for every base64 values. This array has a size of 128 elements. + // The index is the code point value of the base 64 characters. The value is -1 if + // the code point is not a valid base 64 character. Otherwise, the value is a value + // from 0 ~ 63. + private sbyte[] _base64Values; + // The array to decide if a Unicode code point below 0x80 can be directly encoded in UTF7. + // This array has a size of 128. + private bool[] _directEncode; + + private bool _allowOptionals; + + private const int UTF7_CODEPAGE = 65000; + + + public UTF7Encoding() + : this(false) + { + } + + public UTF7Encoding(bool allowOptionals) + : base(UTF7_CODEPAGE) //Set the data item. + { + // Allowing optionals? + _allowOptionals = allowOptionals; + + // Make our tables + MakeTables(); + } + + private void MakeTables() + { + // Build our tables + _base64Bytes = new byte[64]; + for (int i = 0; i < 64; i++) _base64Bytes[i] = (byte)base64Chars[i]; + _base64Values = new sbyte[128]; + for (int i = 0; i < 128; i++) _base64Values[i] = -1; + for (int i = 0; i < 64; i++) _base64Values[_base64Bytes[i]] = (sbyte)i; + _directEncode = new bool[128]; + int count = directChars.Length; + for (int i = 0; i < count; i++) + { + _directEncode[directChars[i]] = true; + } + + if (_allowOptionals) + { + count = optionalChars.Length; + for (int i = 0; i < count; i++) + { + _directEncode[optionalChars[i]] = true; + } + } + } + + // We go ahead and set this because Encoding expects it, however nothing can fall back in UTF7. + internal override void SetDefaultFallbacks() + { + // UTF7 had an odd decoderFallback behavior, and the Encoder fallback + // is irrelevant because we encode surrogates individually and never check for unmatched ones + // (so nothing can fallback during encoding) + this.encoderFallback = new EncoderReplacementFallback(String.Empty); + this.decoderFallback = new DecoderUTF7Fallback(); + } + + public override bool Equals(Object value) + { + UTF7Encoding that = value as UTF7Encoding; + if (that != null) + { + return (_allowOptionals == that._allowOptionals) && + (EncoderFallback.Equals(that.EncoderFallback)) && + (DecoderFallback.Equals(that.DecoderFallback)); + } + return (false); + } + + // Compared to all the other encodings, variations of UTF7 are unlikely + + public override int GetHashCode() + { + return this.CodePage + this.EncoderFallback.GetHashCode() + this.DecoderFallback.GetHashCode(); + } + + // The following methods are copied from EncodingNLS.cs. + // Unfortunately EncodingNLS.cs is internal and we're public, so we have to re-implement them here. + // These should be kept in sync for the following classes: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + // Returns the number of bytes required to encode a range of characters in + // a character array. + // + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe int GetByteCount(char[] chars, int index, int count) + { + // Validate input parameters + if (chars == null) + throw new ArgumentNullException("chars", SR.ArgumentNull_Array); + + if (index < 0 || count < 0) + throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (chars.Length - index < count) + throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); + + // If no input, return 0, avoid fixed empty array problem + if (count == 0) + return 0; + + // Just call the pointer version + fixed (char* pChars = chars) + return GetByteCount(pChars + index, count, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe int GetByteCount(string s) + { + // Validate input + if (s==null) + throw new ArgumentNullException("s"); + + fixed (char* pChars = s) + return GetByteCount(pChars, s.Length, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + [CLSCompliant(false)] + public override unsafe int GetByteCount(char* chars, int count) + { + // Validate Parameters + if (chars == null) + throw new ArgumentNullException("chars", SR.ArgumentNull_Array); + + if (count < 0) + throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); + + // Call it with empty encoder + return GetByteCount(chars, count, null); + } + + // Parent method is safe. + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + public override unsafe int GetBytes(string s, int charIndex, int charCount, + byte[] bytes, int byteIndex) + { + if (s == null || bytes == null) + throw new ArgumentNullException((s == null ? "s" : "bytes"), SR.ArgumentNull_Array); + + if (charIndex < 0 || charCount < 0) + throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (s.Length - charIndex < charCount) + throw new ArgumentOutOfRangeException("s", SR.ArgumentOutOfRange_IndexCount); + + if (byteIndex < 0 || byteIndex > bytes.Length) + throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); + + int byteCount = bytes.Length - byteIndex; + + fixed (char* pChars = s) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) + return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null); + } + + // Encodes a range of characters in a character array into a range of bytes + // in a byte array. An exception occurs if the byte array is not large + // enough to hold the complete encoding of the characters. The + // GetByteCount method can be used to determine the exact number of + // bytes that will be produced for a given range of characters. + // Alternatively, the GetMaxByteCount method can be used to + // determine the maximum number of bytes that will be produced for a given + // number of characters, regardless of the actual character values. + // + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe int GetBytes(char[] chars, int charIndex, int charCount, + byte[] bytes, int byteIndex) + { + // Validate parameters + if (chars == null || bytes == null) + throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array); + + if (charIndex < 0 || charCount < 0) + throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (chars.Length - charIndex < charCount) + throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); + + if (byteIndex < 0 || byteIndex > bytes.Length) + throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); + + // If nothing to encode return 0, avoid fixed problem + if (charCount == 0) + return 0; + + // Just call pointer version + int byteCount = bytes.Length - byteIndex; + + fixed (char* pChars = chars) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) + // Remember that byteCount is # to decode, not size of array. + return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + [CLSCompliant(false)] + public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount) + { + // Validate Parameters + if (bytes == null || chars == null) + throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + + if (charCount < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + return GetBytes(chars, charCount, bytes, byteCount, null); + } + + // Returns the number of characters produced by decoding a range of bytes + // in a byte array. + // + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe int GetCharCount(byte[] bytes, int index, int count) + { + // Validate Parameters + if (bytes == null) + throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + + if (index < 0 || count < 0) + throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (bytes.Length - index < count) + throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + + // If no input just return 0, fixed doesn't like 0 length arrays. + if (count == 0) + return 0; + + // Just call pointer version + fixed (byte* pBytes = bytes) + return GetCharCount(pBytes + index, count, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + [CLSCompliant(false)] + public override unsafe int GetCharCount(byte* bytes, int count) + { + // Validate Parameters + if (bytes == null) + throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + + if (count < 0) + throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); + + return GetCharCount(bytes, count, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount, + char[] chars, int charIndex) + { + // Validate Parameters + if (bytes == null || chars == null) + throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + + if (byteIndex < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if ( bytes.Length - byteIndex < byteCount) + throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + + if (charIndex < 0 || charIndex > chars.Length) + throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index); + + // If no input, return 0 & avoid fixed problem + if (byteCount == 0) + return 0; + + // Just call pointer version + int charCount = chars.Length - charIndex; + + fixed (byte* pBytes = bytes) fixed (char* pChars = &MemoryMarshal.GetReference((Span)chars)) + // Remember that charCount is # to decode, not size of array + return GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, null); + } + + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + + [CLSCompliant(false)] + public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount) + { + // Validate Parameters + if (bytes == null || chars == null) + throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array); + + if (charCount < 0 || byteCount < 0) + throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); + + return GetChars(bytes, byteCount, chars, charCount, null); + } + + // Returns a string containing the decoded representation of a range of + // bytes in a byte array. + // + // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) + // So if you fix this, fix the others. Currently those include: + // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding + // parent method is safe + + public override unsafe String GetString(byte[] bytes, int index, int count) + { + // Validate Parameters + if (bytes == null) + throw new ArgumentNullException("bytes", SR.ArgumentNull_Array); + + if (index < 0 || count < 0) + throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (bytes.Length - index < count) + throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); + + // Avoid problems with empty input buffer + if (count == 0) return String.Empty; + + fixed (byte* pBytes = bytes) + return String.CreateStringFromEncoding( + pBytes + index, count, this); + } + + // + // End of standard methods copied from EncodingNLS.cs + // + + internal override unsafe int GetByteCount(char* chars, int count, EncoderNLS baseEncoder) + { + Debug.Assert(chars != null, "[UTF7Encoding.GetByteCount]chars!=null"); + Debug.Assert(count >= 0, "[UTF7Encoding.GetByteCount]count >=0"); + + // Just call GetBytes with bytes == null + return GetBytes(chars, count, null, 0, baseEncoder); + } + + internal override unsafe int GetBytes(char* chars, int charCount, + byte* bytes, int byteCount, EncoderNLS baseEncoder) + { + Debug.Assert(byteCount >= 0, "[UTF7Encoding.GetBytes]byteCount >=0"); + Debug.Assert(chars != null, "[UTF7Encoding.GetBytes]chars!=null"); + Debug.Assert(charCount >= 0, "[UTF7Encoding.GetBytes]charCount >=0"); + + // Get encoder info + UTF7Encoding.Encoder encoder = (UTF7Encoding.Encoder)baseEncoder; + + // Default bits & count + int bits = 0; + int bitCount = -1; + + // prepare our helpers + Encoding.EncodingByteBuffer buffer = new Encoding.EncodingByteBuffer( + this, encoder, bytes, byteCount, chars, charCount); + + if (encoder != null) + { + bits = encoder.bits; + bitCount = encoder.bitCount; + + // May have had too many left over + while (bitCount >= 6) + { + bitCount -= 6; + // If we fail we'll never really have enough room + if (!buffer.AddByte(_base64Bytes[(bits >> bitCount) & 0x3F])) + ThrowBytesOverflow(encoder, buffer.Count == 0); + } + } + + while (buffer.MoreData) + { + char currentChar = buffer.GetNextChar(); + + if (currentChar < 0x80 && _directEncode[currentChar]) + { + if (bitCount >= 0) + { + if (bitCount > 0) + { + // Try to add the next byte + if (!buffer.AddByte(_base64Bytes[bits << 6 - bitCount & 0x3F])) + break; // Stop here, didn't throw + + bitCount = 0; + } + + // Need to get emit '-' and our char, 2 bytes total + if (!buffer.AddByte((byte)'-')) + break; // Stop here, didn't throw + + bitCount = -1; + } + + // Need to emit our char + if (!buffer.AddByte((byte)currentChar)) + break; // Stop here, didn't throw + } + else if (bitCount < 0 && currentChar == '+') + { + if (!buffer.AddByte((byte)'+', (byte)'-')) + break; // Stop here, didn't throw + } + else + { + if (bitCount < 0) + { + // Need to emit a + and 12 bits (3 bytes) + // Only 12 of the 16 bits will be emitted this time, the other 4 wait 'til next time + if (!buffer.AddByte((byte)'+')) + break; // Stop here, didn't throw + + // We're now in bit mode, but haven't stored data yet + bitCount = 0; + } + + // Add our bits + bits = bits << 16 | currentChar; + bitCount += 16; + + while (bitCount >= 6) + { + bitCount -= 6; + if (!buffer.AddByte(_base64Bytes[(bits >> bitCount) & 0x3F])) + { + bitCount += 6; // We didn't use these bits + currentChar = buffer.GetNextChar(); // We're processing this char still, but AddByte + // --'d it when we ran out of space + break; // Stop here, not enough room for bytes + } + } + + if (bitCount >= 6) + break; // Didn't have room to encode enough bits + } + } + + // Now if we have bits left over we have to encode them. + // MustFlush may have been cleared by encoding.ThrowBytesOverflow earlier if converting + if (bitCount >= 0 && (encoder == null || encoder.MustFlush)) + { + // Do we have bits we have to stick in? + if (bitCount > 0) + { + if (buffer.AddByte(_base64Bytes[(bits << (6 - bitCount)) & 0x3F])) + { + // Emitted spare bits, 0 bits left + bitCount = 0; + } + } + + // If converting and failed bitCount above, then we'll fail this too + if (buffer.AddByte((byte)'-')) + { + // turned off bit mode'; + bits = 0; + bitCount = -1; + } + else + // If not successful, convert will maintain state for next time, also + // AddByte will have decremented our char count, however we need it to remain the same + buffer.GetNextChar(); + } + + // Do we have an encoder we're allowed to use? + // bytes == null if counting, so don't use encoder then + if (bytes != null && encoder != null) + { + // We already cleared bits & bitcount for mustflush case + encoder.bits = bits; + encoder.bitCount = bitCount; + encoder._charsUsed = buffer.CharsUsed; + } + + return buffer.Count; + } + + internal override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder) + { + Debug.Assert(count >= 0, "[UTF7Encoding.GetCharCount]count >=0"); + Debug.Assert(bytes != null, "[UTF7Encoding.GetCharCount]bytes!=null"); + + // Just call GetChars with null char* to do counting + return GetChars(bytes, count, null, 0, baseDecoder); + } + + internal override unsafe int GetChars(byte* bytes, int byteCount, + char* chars, int charCount, DecoderNLS baseDecoder) + { + Debug.Assert(byteCount >= 0, "[UTF7Encoding.GetChars]byteCount >=0"); + Debug.Assert(bytes != null, "[UTF7Encoding.GetChars]bytes!=null"); + Debug.Assert(charCount >= 0, "[UTF7Encoding.GetChars]charCount >=0"); + + // Might use a decoder + UTF7Encoding.Decoder decoder = (UTF7Encoding.Decoder)baseDecoder; + + // Get our output buffer info. + Encoding.EncodingCharBuffer buffer = new Encoding.EncodingCharBuffer( + this, decoder, chars, charCount, bytes, byteCount); + + // Get decoder info + int bits = 0; + int bitCount = -1; + bool firstByte = false; + if (decoder != null) + { + bits = decoder.bits; + bitCount = decoder.bitCount; + firstByte = decoder.firstByte; + + Debug.Assert(firstByte == false || decoder.bitCount <= 0, + "[UTF7Encoding.GetChars]If remembered bits, then first byte flag shouldn't be set"); + } + + // We may have had bits in the decoder that we couldn't output last time, so do so now + if (bitCount >= 16) + { + // Check our decoder buffer + if (!buffer.AddChar((char)((bits >> (bitCount - 16)) & 0xFFFF))) + ThrowCharsOverflow(decoder, true); // Always throw, they need at least 1 char even in Convert + + // Used this one, clean up extra bits + bitCount -= 16; + } + + // Loop through the input + while (buffer.MoreData) + { + byte currentByte = buffer.GetNextByte(); + int c; + + if (bitCount >= 0) + { + // + // Modified base 64 encoding. + // + sbyte v; + if (currentByte < 0x80 && ((v = _base64Values[currentByte]) >= 0)) + { + firstByte = false; + bits = (bits << 6) | ((byte)v); + bitCount += 6; + if (bitCount >= 16) + { + c = (bits >> (bitCount - 16)) & 0xFFFF; + bitCount -= 16; + } + // If not enough bits just continue + else continue; + } + else + { + // If it wasn't a base 64 byte, everything's going to turn off base 64 mode + bitCount = -1; + + if (currentByte != '-') + { + // >= 0x80 (because of 1st if statemtn) + // We need this check since the _base64Values[b] check below need b <= 0x7f. + // This is not a valid base 64 byte. Terminate the shifted-sequence and + // emit this byte. + + // not in base 64 table + // According to the RFC 1642 and the example code of UTF-7 + // in Unicode 2.0, we should just zero-extend the invalid UTF7 byte + + // Chars won't be updated unless this works, try to fallback + if (!buffer.Fallback(currentByte)) + break; // Stop here, didn't throw + + // Used that byte, we're done with it + continue; + } + + // + // The encoding for '+' is "+-". + // + if (firstByte) c = '+'; + // We just turn it off if not emitting a +, so we're done. + else continue; + } + // + // End of modified base 64 encoding block. + // + } + else if (currentByte == '+') + { + // + // Found the start of a modified base 64 encoding block or a plus sign. + // + bitCount = 0; + firstByte = true; + continue; + } + else + { + // Normal character + if (currentByte >= 0x80) + { + // Try to fallback + if (!buffer.Fallback(currentByte)) + break; // Stop here, didn't throw + + // Done falling back + continue; + } + + // Use the normal character + c = currentByte; + } + + if (c >= 0) + { + // Check our buffer + if (!buffer.AddChar((char)c)) + { + // No room. If it was a plain char we'll try again later. + // Note, we'll consume this byte and stick it in decoder, even if we can't output it + if (bitCount >= 0) // Can we rememmber this byte (char) + { + buffer.AdjustBytes(+1); // Need to readd the byte that AddChar subtracted when it failed + bitCount += 16; // We'll still need that char we have in our bits + } + break; // didn't throw, stop + } + } + } + + // Stick stuff in the decoder if we can (chars == null if counting, so don't store decoder) + if (chars != null && decoder != null) + { + // MustFlush? (Could've been cleared by ThrowCharsOverflow if Convert & didn't reach end of buffer) + if (decoder.MustFlush) + { + // RFC doesn't specify what would happen if we have non-0 leftover bits, we just drop them + decoder.bits = 0; + decoder.bitCount = -1; + decoder.firstByte = false; + } + else + { + decoder.bits = bits; + decoder.bitCount = bitCount; + decoder.firstByte = firstByte; + } + decoder._bytesUsed = buffer.BytesUsed; + } + // else ignore any hanging bits. + + // Return our count + return buffer.Count; + } + + + public override System.Text.Decoder GetDecoder() + { + return new UTF7Encoding.Decoder(this); + } + + + public override System.Text.Encoder GetEncoder() + { + return new UTF7Encoding.Encoder(this); + } + + + public override int GetMaxByteCount(int charCount) + { + if (charCount < 0) + throw new ArgumentOutOfRangeException(nameof(charCount), + SR.ArgumentOutOfRange_NeedNonNegNum); + + // Suppose that every char can not be direct-encoded, we know that + // a byte can encode 6 bits of the Unicode character. And we will + // also need two extra bytes for the shift-in ('+') and shift-out ('-') mark. + // Therefore, the max byte should be: + // byteCount = 2 + Math.Ceiling((double)charCount * 16 / 6); + // That is always <= 2 + 3 * charCount; + // Longest case is alternating encoded, direct, encoded data for 5 + 1 + 5... bytes per char. + // UTF7 doesn't have left over surrogates, but if no input we may need an output - to turn off + // encoding if MustFlush is true. + + // Its easiest to think of this as 2 bytes to turn on/off the base64 mode, then 3 bytes per char. + // 3 bytes is 18 bits of encoding, which is more than we need, but if its direct encoded then 3 + // bytes allows us to turn off and then back on base64 mode if necessary. + + // Note that UTF7 encoded surrogates individually and isn't worried about mismatches, so all + // code points are encodable int UTF7. + long byteCount = (long)charCount * 3 + 2; + + // check for overflow + if (byteCount > 0x7fffffff) + throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_GetByteCountOverflow); + + return (int)byteCount; + } + + + public override int GetMaxCharCount(int byteCount) + { + if (byteCount < 0) + throw new ArgumentOutOfRangeException(nameof(byteCount), + SR.ArgumentOutOfRange_NeedNonNegNum); + + // Worst case is 1 char per byte. Minimum 1 for left over bits in case decoder is being flushed + // Also note that we ignore extra bits (per spec), so UTF7 doesn't have unknown in this direction. + int charCount = byteCount; + if (charCount == 0) charCount = 1; + + return charCount; + } + + // Of all the amazing things... This MUST be Decoder so that our com name + // for System.Text.Decoder doesn't change + private sealed class Decoder : DecoderNLS + { + /*private*/ + internal int bits; + /*private*/ + internal int bitCount; + /*private*/ + internal bool firstByte; + + public Decoder(UTF7Encoding encoding) : base(encoding) + { + // base calls reset + } + + public override void Reset() + { + this.bits = 0; + this.bitCount = -1; + this.firstByte = false; + if (_fallbackBuffer != null) + _fallbackBuffer.Reset(); + } + + // Anything left in our encoder? + internal override bool HasState + { + get + { + // NOTE: This forces the last -, which some encoder might not encode. If we + // don't see it we don't think we're done reading. + return (this.bitCount != -1); + } + } + } + + // Of all the amazing things... This MUST be Encoder so that our com name + // for System.Text.Encoder doesn't change + private sealed class Encoder : EncoderNLS + { + /*private*/ + internal int bits; + /*private*/ + internal int bitCount; + + public Encoder(UTF7Encoding encoding) : base(encoding) + { + // base calls reset + } + + public override void Reset() + { + this.bitCount = -1; + this.bits = 0; + if (_fallbackBuffer != null) + _fallbackBuffer.Reset(); + } + + // Anything left in our encoder? + internal override bool HasState + { + get + { + return (this.bits != 0 || this.bitCount != -1); + } + } + } + + // Preexisting UTF7 behavior for bad bytes was just to spit out the byte as the next char + // and turn off base64 mode if it was in that mode. We still exit the mode, but now we fallback. + private sealed class DecoderUTF7Fallback : DecoderFallback + { + // Construction. Default replacement fallback uses no best fit and ? replacement string + public DecoderUTF7Fallback() + { + } + + public override DecoderFallbackBuffer CreateFallbackBuffer() + { + return new DecoderUTF7FallbackBuffer(this); + } + + // Maximum number of characters that this instance of this fallback could return + public override int MaxCharCount + { + get + { + // returns 1 char per bad byte + return 1; + } + } + + public override bool Equals(Object value) + { + DecoderUTF7Fallback that = value as DecoderUTF7Fallback; + if (that != null) + { + return true; + } + return (false); + } + + public override int GetHashCode() + { + return 984; + } + } + + private sealed class DecoderUTF7FallbackBuffer : DecoderFallbackBuffer + { + // Store our default string + private char cFallback = (char)0; + private int iCount = -1; + private int iSize; + + // Construction + public DecoderUTF7FallbackBuffer(DecoderUTF7Fallback fallback) + { + } + + // Fallback Methods + public override bool Fallback(byte[] bytesUnknown, int index) + { + // We expect no previous fallback in our buffer + Debug.Assert(iCount < 0, "[DecoderUTF7FallbackBuffer.Fallback] Can't have recursive fallbacks"); + Debug.Assert(bytesUnknown.Length == 1, "[DecoderUTF7FallbackBuffer.Fallback] Only possible fallback case should be 1 unknown byte"); + + // Go ahead and get our fallback + cFallback = (char)bytesUnknown[0]; + + // Any of the fallback characters can be handled except for 0 + if (cFallback == 0) + { + return false; + } + + iCount = iSize = 1; + + return true; + } + + public override char GetNextChar() + { + if (iCount-- > 0) + return cFallback; + + // Note: this means that 0 in UTF7 stream will never be emitted. + return (char)0; + } + + public override bool MovePrevious() + { + if (iCount >= 0) + { + iCount++; + } + + // return true if we were allowed to do this + return (iCount >= 0 && iCount <= iSize); + } + + // Return # of chars left in this fallback + public override int Remaining + { + get + { + return (iCount > 0) ? iCount : 0; + } + } + + // Clear the buffer + public override unsafe void Reset() + { + iCount = -1; + byteStart = null; + } + + // This version just counts the fallback and doesn't actually copy anything. + internal unsafe override int InternalFallback(byte[] bytes, byte* pBytes) + // Right now this has both bytes and bytes[], since we might have extra bytes, hence the + // array, and we might need the index, hence the byte* + { + // We expect no previous fallback in our buffer + Debug.Assert(iCount < 0, "[DecoderUTF7FallbackBuffer.InternalFallback] Can't have recursive fallbacks"); + if (bytes.Length != 1) + { + throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex); + } + + // Can't fallback a byte 0, so return for that case, 1 otherwise. + return bytes[0] == 0 ? 0 : 1; + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/UTF8Encoding.cs.REMOVED.git-id b/external/corert/src/System.Private.CoreLib/shared/System/Text/UTF8Encoding.cs.REMOVED.git-id index dfbff07c62..6176a63d8f 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Text/UTF8Encoding.cs.REMOVED.git-id +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/UTF8Encoding.cs.REMOVED.git-id @@ -1 +1 @@ -ee5c92c3a869f9f71a0c5893438a42c99fb94439 \ No newline at end of file +a89af8edb0b2b357812747b41f60144b3881dee9 \ No newline at end of file diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/UnicodeEncoding.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/UnicodeEncoding.cs index 846946ce94..342bf53d62 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Text/UnicodeEncoding.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/UnicodeEncoding.cs @@ -8,9 +8,8 @@ using System; using System.Globalization; -using System.Runtime.Serialization; using System.Diagnostics; -using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; namespace System.Text { @@ -21,7 +20,9 @@ namespace System.Text internal static readonly UnicodeEncoding s_bigEndianDefault = new UnicodeEncoding(bigEndian: true, byteOrderMark: true); internal static readonly UnicodeEncoding s_littleEndianDefault = new UnicodeEncoding(bigEndian: false, byteOrderMark: true); - [OptionalField(VersionAdded = 2)] + private static readonly byte[] s_bigEndianPreamble = new byte[2] { 0xfe, 0xff }; + private static readonly byte[] s_littleEndianPreamble = new byte[2] { 0xff, 0xfe }; + internal bool isThrowException = false; internal bool bigEndian = false; @@ -55,15 +56,6 @@ namespace System.Text SetDefaultFallbacks(); } - #region Serialization - [OnDeserializing] - private void OnDeserializing(StreamingContext ctx) - { - // In Everett it is false. Whidbey will overwrite this value. - isThrowException = false; - } - #endregion Serialization - internal override void SetDefaultFallbacks() { // For UTF-X encodings, we use a replacement fallback with an empty string @@ -80,7 +72,7 @@ namespace System.Text } // The following methods are copied from EncodingNLS.cs. - // Unfortunately EncodingNLS.cs is internal and we're public, so we have to reimpliment them here. + // Unfortunately EncodingNLS.cs is internal and we're public, so we have to re-implement them here. // These should be kept in sync for the following classes: // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding // @@ -104,7 +96,6 @@ namespace System.Text if (chars.Length - index < count) throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); // If no input, return 0, avoid fixed empty array problem if (count == 0) @@ -125,7 +116,6 @@ namespace System.Text // Validate input if (s==null) throw new ArgumentNullException("s"); - Contract.EndContractBlock(); fixed (char* pChars = s) return GetByteCount(pChars, s.Length, null); @@ -144,7 +134,6 @@ namespace System.Text if (count < 0) throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Call it with empty encoder return GetByteCount(chars, count, null); @@ -169,15 +158,10 @@ namespace System.Text if (byteIndex < 0 || byteIndex > bytes.Length) throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); int byteCount = bytes.Length - byteIndex; - // Fixed doesn't like 0 length arrays. - if (bytes.Length == 0) - bytes = new byte[1]; - - fixed (char* pChars = s) fixed (byte* pBytes = &bytes[0]) + fixed (char* pChars = s) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null); } @@ -210,7 +194,6 @@ namespace System.Text if (byteIndex < 0 || byteIndex > bytes.Length) throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); // If nothing to encode return 0, avoid fixed problem if (charCount == 0) @@ -219,11 +202,7 @@ namespace System.Text // Just call pointer version int byteCount = bytes.Length - byteIndex; - // Fixed doesn't like 0 length arrays. - if (bytes.Length == 0) - bytes = new byte[1]; - - fixed (char* pChars = chars) fixed (byte* pBytes = &bytes[0]) + fixed (char* pChars = chars) fixed (byte* pBytes = &MemoryMarshal.GetReference((Span)bytes)) // Remember that byteCount is # to decode, not size of array. return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null); } @@ -241,7 +220,6 @@ namespace System.Text if (charCount < 0 || byteCount < 0) throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); return GetBytes(chars, charCount, bytes, byteCount, null); } @@ -265,7 +243,6 @@ namespace System.Text if (bytes.Length - index < count) throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); // If no input just return 0, fixed doesn't like 0 length arrays if (count == 0) @@ -289,7 +266,6 @@ namespace System.Text if (count < 0) throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); return GetCharCount(bytes, count, null); } @@ -314,7 +290,6 @@ namespace System.Text if (charIndex < 0 || charIndex > chars.Length) throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); // If no input, return 0 & avoid fixed problem if (byteCount == 0) @@ -323,11 +298,7 @@ namespace System.Text // Just call pointer version int charCount = chars.Length - charIndex; - // Fixed doesn't like 0 length arrays. - if (chars.Length == 0) - chars = new char[1]; - - fixed (byte* pBytes = bytes) fixed (char* pChars = &chars[0]) + fixed (byte* pBytes = bytes) fixed (char* pChars = &MemoryMarshal.GetReference((Span)chars)) // Remember that charCount is # to decode, not size of array return GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, null); } @@ -345,7 +316,6 @@ namespace System.Text if (charCount < 0 || byteCount < 0) throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); return GetChars(bytes, byteCount, chars, charCount, null); } @@ -369,7 +339,6 @@ namespace System.Text if (bytes.Length - index < count) throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); // Avoid problems with empty input buffer if (count == 0) return String.Empty; @@ -414,7 +383,7 @@ namespace System.Text if (encoder != null) { - charLeftOver = encoder.charLeftOver; + charLeftOver = encoder._charLeftOver; // Assume extra bytes to encode charLeftOver if it existed if (charLeftOver > 0) @@ -461,7 +430,7 @@ namespace System.Text { // See if we potentially have surrogates (0x8000 bit set) // (We're either big endian on a big endian machine or little endian on - // a little endian machine so this'll work) + // a little endian machine so that'll work) if ((0x8000800080008000 & *longChars) != 0) { // See if any of these are high or low surrogates (0xd800 - 0xdfff). If the high @@ -589,7 +558,7 @@ namespace System.Text // Set our internal fallback interesting things. fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false); } - charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered fallbackBuffer.InternalFallback(ch, ref charsForFallback); chars = charsForFallback; continue; @@ -622,7 +591,7 @@ namespace System.Text // Set our internal fallback interesting things. fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false); } - charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback); chars = charsForFallback; @@ -663,7 +632,7 @@ namespace System.Text // Set our internal fallback interesting things. fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false); } - charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback); chars = charsForFallback; charLeftOver = (char)0; @@ -674,7 +643,7 @@ namespace System.Text } // Shouldn't have anything in fallback buffer for GetByteCount - // (don't have to check m_throwOnOverflow for count) + // (don't have to check _throwOnOverflow for count) Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0, "[UnicodeEncoding.GetByteCount]Expected empty fallback buffer at end"); @@ -707,14 +676,14 @@ namespace System.Text // Get our encoder, but don't clear it yet. if (encoder != null) { - charLeftOver = encoder.charLeftOver; + charLeftOver = encoder._charLeftOver; // We mustn't have left over fallback data when counting if (encoder.InternalHasFallbackBuffer) { // We always need the fallback buffer in get bytes so we can flush any remaining ones if necessary fallbackBuffer = encoder.FallbackBuffer; - if (fallbackBuffer.Remaining > 0 && encoder.m_throwOnOverflow) + if (fallbackBuffer.Remaining > 0 && encoder._throwOnOverflow) throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, encoder.Fallback.GetType())); // Set our internal fallback interesting things. @@ -760,7 +729,7 @@ namespace System.Text { // See if we potentially have surrogates (0x8000 bit set) // (We're either big endian on a big endian machine or little endian on - // a little endian machine so this'll work) + // a little endian machine so that'll work) if ((0x8000800080008000 & *longChars) != 0) { // See if any of these are high or low surrogates (0xd800 - 0xdfff). If the high @@ -824,9 +793,9 @@ namespace System.Text #endif // BIGENDIAN #if BIT64 - (unchecked((long)chars) & 7) != (unchecked((long)bytes) & 7) && // Only do this if chars & bytes are out of line, otherwise faster loop'll be faster next time + (unchecked((long)chars) & 7) != (unchecked((long)bytes) & 7) && // Only do this if chars & bytes are out of line, otherwise faster loop will be faster next time #else - (unchecked((int)chars) & 3) != (unchecked((int)bytes) & 3) && // Only do this if chars & bytes are out of line, otherwise faster loop'll be faster next time + (unchecked((int)chars) & 3) != (unchecked((int)bytes) & 3) && // Only do this if chars & bytes are out of line, otherwise faster loop will be faster next time #endif // BIT64 (unchecked((int)(bytes)) & 1) == 0) { @@ -908,7 +877,7 @@ namespace System.Text fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true); } - charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback); chars = charsForFallback; @@ -937,7 +906,7 @@ namespace System.Text fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true); } - charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered fallbackBuffer.InternalFallback(ch, ref charsForFallback); chars = charsForFallback; continue; @@ -1007,7 +976,7 @@ namespace System.Text fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true); } - charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback); chars = charsForFallback; @@ -1073,8 +1042,8 @@ namespace System.Text fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true); } - // If we're not flushing, this'll remember the left over character. - charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered + // If we're not flushing, that'll remember the left over character. + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback); chars = charsForFallback; @@ -1088,8 +1057,8 @@ namespace System.Text // Not flushing, remember it in the encoder if (encoder != null) { - encoder.charLeftOver = charLeftOver; - encoder.m_charsUsed = (int)(chars - charStart); + encoder._charLeftOver = charLeftOver; + encoder._charsUsed = (int)(chars - charStart); } // Remember charLeftOver if we must, or clear it if we're flushing @@ -1098,7 +1067,7 @@ namespace System.Text "[UnicodeEncoding.GetBytes] Expected no left over characters if flushing"); Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0 || - encoder == null || !encoder.m_throwOnOverflow, + encoder == null || !encoder._throwOnOverflow, "[UnicodeEncoding.GetBytes]Expected empty fallback buffer if not converting"); // We used to copy it fast, but this doesn't check for surrogates @@ -1148,7 +1117,7 @@ namespace System.Text } // Shouldn't have anything in fallback buffer for GetCharCount - // (don't have to check m_throwOnOverflow for count) + // (don't have to check _throwOnOverflow for count) Debug.Assert(!decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0, "[UnicodeEncoding.GetCharCount]Expected empty fallback buffer at start"); } @@ -1156,7 +1125,7 @@ namespace System.Text while (bytes < byteEnd) { // If we're aligned then maybe we can do it fast - // This'll hurt if we're unaligned because we'll always test but never be aligned + // That'll hurt if we're unaligned because we'll always test but never be aligned #if !NO_FAST_UNICODE_LOOP #if BIGENDIAN if (bigEndian && @@ -1177,7 +1146,7 @@ namespace System.Text { // See if we potentially have surrogates (0x8000 bit set) // (We're either big endian on a big endian machine or little endian on - // a little endian machine so this'll work) + // a little endian machine so that'll work) if ((0x8000800080008000 & *longBytes) != 0) { // See if any of these are high or low surrogates (0xd800 - 0xdfff). If the high @@ -1338,7 +1307,7 @@ namespace System.Text else if (lastChar > 0) { // Had a high surrogate, expected a low surrogate - // Uncount the last high surrogate + // Un-count the last high surrogate charCount--; // fall back the high surrogate. @@ -1434,7 +1403,7 @@ namespace System.Text charCount--; // Shouldn't have anything in fallback buffer for GetCharCount - // (don't have to check m_throwOnOverflow for count) + // (don't have to check _throwOnOverflow for count) Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0, "[UnicodeEncoding.GetCharCount]Expected empty fallback buffer at end"); @@ -1462,7 +1431,7 @@ namespace System.Text lastChar = decoder.lastChar; // Shouldn't have anything in fallback buffer for GetChars - // (don't have to check m_throwOnOverflow for chars) + // (don't have to check _throwOnOverflow for chars) Debug.Assert(!decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0, "[UnicodeEncoding.GetChars]Expected empty fallback buffer at start"); } @@ -1479,7 +1448,7 @@ namespace System.Text while (bytes < byteEnd) { // If we're aligned then maybe we can do it fast - // This'll hurt if we're unaligned because we'll always test but never be aligned + // That'll hurt if we're unaligned because we'll always test but never be aligned #if !NO_FAST_UNICODE_LOOP #if BIGENDIAN if (bigEndian && @@ -1509,7 +1478,7 @@ namespace System.Text { // See if we potentially have surrogates (0x8000 bit set) // (We're either big endian on a big endian machine or little endian on - // a little endian machine so this'll work) + // a little endian machine so that'll work) if ((0x8000800080008000 & *longBytes) != 0) { // See if any of these are high or low surrogates (0xd800 - 0xdfff). If the high @@ -1617,7 +1586,7 @@ namespace System.Text fallbackBuffer.InternalInitialize(byteStart, charEnd); } - charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallback); chars = charsForFallback; @@ -1669,7 +1638,7 @@ namespace System.Text fallbackBuffer.InternalInitialize(byteStart, charEnd); } - charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallback); chars = charsForFallback; @@ -1731,7 +1700,7 @@ namespace System.Text fallbackBuffer.InternalInitialize(byteStart, charEnd); } - charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallback); chars = charsForFallback; @@ -1796,7 +1765,7 @@ namespace System.Text fallbackBuffer.InternalInitialize(byteStart, charEnd); } - charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallback); chars = charsForFallback; @@ -1836,7 +1805,7 @@ namespace System.Text } // No hanging odd bytes allowed if must flush - charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered + charsForFallback = chars; // Avoid passing chars by reference to allow it to be en-registered bool fallbackResult = fallbackBuffer.InternalFallback(new byte[] { unchecked((byte)lastByte) }, bytes, ref charsForFallback); chars = charsForFallback; @@ -1866,7 +1835,7 @@ namespace System.Text // + " " + ((int)lastChar).ToString("X4") + " " + lastByte.ToString("X2") ); - decoder.m_bytesUsed = (int)(bytes - byteStart); + decoder._bytesUsed = (int)(bytes - byteStart); decoder.lastChar = lastChar; decoder.lastByte = lastByte; } @@ -1875,7 +1844,7 @@ namespace System.Text // System.IO.__UnmanagedMemoryStream.memcpyimpl((byte*)chars, bytes, byteCount); // Shouldn't have anything in fallback buffer for GetChars - // (don't have to check m_throwOnOverflow for count or chars) + // (don't have to check _throwOnOverflow for count or chars) Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0, "[UnicodeEncoding.GetChars]Expected empty fallback buffer at end"); @@ -1909,13 +1878,16 @@ namespace System.Text return Array.Empty(); } + public override ReadOnlySpan Preamble => + GetType() != typeof(UnicodeEncoding) ? GetPreamble() : // in case a derived UnicodeEncoding overrode GetPreamble + byteOrderMark ? (bigEndian ? s_bigEndianPreamble : s_littleEndianPreamble) : + Array.Empty(); public override int GetMaxByteCount(int charCount) { if (charCount < 0) throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // Characters would be # of characters + 1 in case left over high surrogate is ? * max fallback long byteCount = (long)charCount + 1; @@ -1938,7 +1910,6 @@ namespace System.Text if (byteCount < 0) throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); // long because byteCount could be biggest int. // 1 char per 2 bytes. Round up in case 1 left over in decoder. @@ -1946,7 +1917,7 @@ namespace System.Text // Might also need an extra 1 if there's a left over high surrogate in the decoder. long charCount = (long)(byteCount >> 1) + (byteCount & 1) + 1; - // Don't forget fallback (in case they have a bunch of lonely surrogates or something bizzare like that) + // Don't forget fallback (in case they have a bunch of lonely surrogates or something bizarre like that) if (DecoderFallback.MaxCharCount > 1) charCount *= DecoderFallback.MaxCharCount; @@ -1964,7 +1935,7 @@ namespace System.Text { // // Big Endian Unicode has different code page (1201) than small Endian one (1200), - // so we still have to check m_codePage here. + // so we still have to check _codePage here. // return (CodePage == that.CodePage) && byteOrderMark == that.byteOrderMark && @@ -1982,7 +1953,7 @@ namespace System.Text (byteOrderMark ? 4 : 0) + (bigEndian ? 8 : 0); } - private sealed class Decoder : System.Text.DecoderNLS, ISerializable + private sealed class Decoder : System.Text.DecoderNLS { internal int lastByte = -1; internal char lastChar = '\0'; @@ -1992,23 +1963,12 @@ namespace System.Text // base calls reset } - internal Decoder(SerializationInfo info, StreamingContext context) - { - throw new PlatformNotSupportedException(); - } - - // ISerializable implementation - void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) - { - throw new PlatformNotSupportedException(); - } - public override void Reset() { lastByte = -1; lastChar = '\0'; - if (m_fallbackBuffer != null) - m_fallbackBuffer.Reset(); + if (_fallbackBuffer != null) + _fallbackBuffer.Reset(); } // Anything left in our decoder? diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Text/ValueStringBuilder.cs b/external/corert/src/System.Private.CoreLib/shared/System/Text/ValueStringBuilder.cs new file mode 100644 index 0000000000..0a91eb0f63 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Text/ValueStringBuilder.cs @@ -0,0 +1,185 @@ +// 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.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace System.Text +{ + internal ref struct ValueStringBuilder + { + private char[] _arrayToReturnToPool; + private Span _chars; + private int _pos; + + public ValueStringBuilder(Span initialBuffer) + { + _arrayToReturnToPool = null; + _chars = initialBuffer; + _pos = 0; + } + + public int Length => _pos; + + public override string ToString() + { + var s = new string(_chars.Slice(0, _pos)); + Clear(); + return s; + } + + public bool TryCopyTo(Span destination, out int charsWritten) + { + if (_chars.Slice(0, _pos).TryCopyTo(destination)) + { + charsWritten = _pos; + Clear(); + return true; + } + else + { + charsWritten = 0; + Clear(); + return false; + } + } + + public void Insert(int index, char value, int count) + { + if (_pos > _chars.Length - count) + { + Grow(count); + } + + int remaining = _pos - index; + _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count)); + _chars.Slice(index, count).Fill(value); + _pos += count; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(char c) + { + int pos = _pos; + if (pos < _chars.Length) + { + _chars[pos] = c; + _pos = pos + 1; + } + else + { + GrowAndAppend(c); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(string s) + { + int pos = _pos; + if (s.Length == 1 && pos < _chars.Length) // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc. + { + _chars[pos] = s[0]; + _pos = pos + 1; + } + else + { + AppendSlow(s); + } + } + + private void AppendSlow(string s) + { + int pos = _pos; + if (pos > _chars.Length - s.Length) + { + Grow(s.Length); + } + + bool copied = s.AsReadOnlySpan().TryCopyTo(_chars.Slice(pos)); + Debug.Assert(copied, "Grow should have made enough room to successfully copy"); + _pos += s.Length; + } + + public void Append(char c, int count) + { + if (_pos > _chars.Length - count) + { + Grow(count); + } + + Span dst = _chars.Slice(_pos, count); + for (int i = 0; i < dst.Length; i++) + { + dst[i] = c; + } + _pos += count; + } + + public unsafe void Append(char* value, int length) + { + int pos = _pos; + if (pos > _chars.Length - length) + { + Grow(length); + } + + Span dst = _chars.Slice(_pos, length); + for (int i = 0; i < dst.Length; i++) + { + dst[i] = *value++; + } + _pos += length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span AppendSpan(int length) + { + int origPos = _pos; + if (origPos > _chars.Length - length) + { + Grow(length); + } + + _pos = origPos + length; + return _chars.Slice(origPos, length); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void GrowAndAppend(char c) + { + Grow(1); + Append(c); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void Grow(int requiredAdditionalCapacity) + { + Debug.Assert(requiredAdditionalCapacity > _chars.Length - _pos); + + char[] poolArray = ArrayPool.Shared.Rent(Math.Max(_pos + requiredAdditionalCapacity, _chars.Length * 2)); + + bool success = _chars.TryCopyTo(poolArray); + Debug.Assert(success); + + char[] toReturn = _arrayToReturnToPool; + _chars = _arrayToReturnToPool = poolArray; + if (toReturn != null) + { + ArrayPool.Shared.Return(toReturn); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Clear() + { + char[] toReturn = _arrayToReturnToPool; + this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again + if (toReturn != null) + { + ArrayPool.Shared.Return(toReturn); + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Threading/AbandonedMutexException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Threading/AbandonedMutexException.cs index 15bc5a7341..c7e604f33d 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Threading/AbandonedMutexException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Threading/AbandonedMutexException.cs @@ -14,6 +14,8 @@ using System.Runtime.Serialization; namespace System.Threading { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class AbandonedMutexException : SystemException { private int _mutexIndex = -1; @@ -22,46 +24,45 @@ namespace System.Threading public AbandonedMutexException() : base(SR.Threading_AbandonedMutexException) { - HResult = __HResults.COR_E_ABANDONEDMUTEX; + HResult = HResults.COR_E_ABANDONEDMUTEX; } public AbandonedMutexException(String message) : base(message) { - HResult = __HResults.COR_E_ABANDONEDMUTEX; + HResult = HResults.COR_E_ABANDONEDMUTEX; } public AbandonedMutexException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_ABANDONEDMUTEX; + HResult = HResults.COR_E_ABANDONEDMUTEX; } public AbandonedMutexException(int location, WaitHandle handle) : base(SR.Threading_AbandonedMutexException) { - HResult = __HResults.COR_E_ABANDONEDMUTEX; + HResult = HResults.COR_E_ABANDONEDMUTEX; SetupException(location, handle); } public AbandonedMutexException(String message, int location, WaitHandle handle) : base(message) { - HResult = __HResults.COR_E_ABANDONEDMUTEX; + HResult = HResults.COR_E_ABANDONEDMUTEX; SetupException(location, handle); } public AbandonedMutexException(String message, Exception inner, int location, WaitHandle handle) : base(message, inner) { - HResult = __HResults.COR_E_ABANDONEDMUTEX; + HResult = HResults.COR_E_ABANDONEDMUTEX; SetupException(location, handle); } protected AbandonedMutexException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } private void SetupException(int location, WaitHandle handle) diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Threading/DeferredDisposableLifetime.cs b/external/corert/src/System.Private.CoreLib/shared/System/Threading/DeferredDisposableLifetime.cs index 63d8984b55..828df23b80 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Threading/DeferredDisposableLifetime.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Threading/DeferredDisposableLifetime.cs @@ -21,7 +21,7 @@ namespace System.Threading /// Indicates whether the object has been disposed. /// /// - /// If the refount reaches zero before the object is disposed, this method will be called with + /// If the refcount reaches zero before the object is disposed, this method will be called with /// set to false. If the object is then disposed, this method will be /// called again, with set to true. If the refcount reaches zero /// after the object has already been disposed, this will be called a single time, with diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs b/external/corert/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs index a573af3383..2d5f5be190 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @@ -12,7 +12,6 @@ ===========================================================*/ using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.ExceptionServices; using System.Runtime.Serialization; @@ -103,7 +102,6 @@ namespace System.Threading { throw new InvalidOperationException(SR.InvalidOperation_CannotSupressFlowMultipleTimes); } - Contract.EndContractBlock(); executionContext = executionContext.ShallowClone(isFlowSuppressed: true); var asyncFlowControl = new AsyncFlowControl(); @@ -120,7 +118,6 @@ namespace System.Threading { throw new InvalidOperationException(SR.InvalidOperation_CannotRestoreUnsupressedFlow); } - Contract.EndContractBlock(); currentThread.ExecutionContext = executionContext.ShallowClone(isFlowSuppressed: false); } @@ -322,7 +319,6 @@ namespace System.Threading { throw new InvalidOperationException(SR.InvalidOperation_AsyncFlowCtrlCtxMismatch); } - Contract.EndContractBlock(); _thread = null; ExecutionContext.RestoreFlow(); diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Threading/LazyInitializer.cs b/external/corert/src/System.Private.CoreLib/shared/System/Threading/LazyInitializer.cs new file mode 100644 index 0000000000..f422ab9172 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Threading/LazyInitializer.cs @@ -0,0 +1,288 @@ +// 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. + +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// a set of lightweight static helpers for lazy initialization. +// +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +using System.Diagnostics; + +namespace System.Threading +{ + /// + /// Provides lazy initialization routines. + /// + /// + /// These routines avoid needing to allocate a dedicated, lazy-initialization instance, instead using + /// references to ensure targets have been initialized as they are accessed. + /// + public static class LazyInitializer + { + /// + /// Initializes a target reference type with the type's default constructor if the target has not + /// already been initialized. + /// + /// The reference type of the reference to be initialized. + /// A reference of type to initialize if it has not + /// already been initialized. + /// The initialized reference of type . + /// Type does not have a default + /// constructor. + /// + /// Permissions to access the constructor of type were missing. + /// + /// + /// + /// This method may only be used on reference types. To ensure initialization of value + /// types, see other overloads of EnsureInitialized. + /// + /// + /// This method may be used concurrently by multiple threads to initialize . + /// In the event that multiple threads access this method concurrently, multiple instances of + /// may be created, but only one will be stored into . In such an occurrence, this method will not dispose of the + /// objects that were not stored. If such objects must be disposed, it is up to the caller to determine + /// if an object was not used and to then dispose of the object appropriately. + /// + /// + public static T EnsureInitialized(ref T target) where T : class => + Volatile.Read(ref target) ?? EnsureInitializedCore(ref target); + + /// + /// Initializes a target reference type with the type's default constructor (slow path) + /// + /// The reference type of the reference to be initialized. + /// The variable that need to be initialized + /// The initialized variable + private static T EnsureInitializedCore(ref T target) where T : class + { + try + { + Interlocked.CompareExchange(ref target, Activator.CreateInstance(), null); + } + catch (MissingMethodException) + { + throw new MissingMemberException(SR.Lazy_CreateValue_NoParameterlessCtorForT); + } + + Debug.Assert(target != null); + return target; + } + + /// + /// Initializes a target reference type using the specified function if it has not already been + /// initialized. + /// + /// The reference type of the reference to be initialized. + /// The reference of type to initialize if it has not + /// already been initialized. + /// The invoked to initialize the + /// reference. + /// The initialized reference of type . + /// Type does not have a + /// default constructor. + /// returned + /// null. + /// + /// + /// This method may only be used on reference types, and may + /// not return a null reference (Nothing in Visual Basic). To ensure initialization of value types or + /// to allow null reference types, see other overloads of EnsureInitialized. + /// + /// + /// This method may be used concurrently by multiple threads to initialize . + /// In the event that multiple threads access this method concurrently, multiple instances of + /// may be created, but only one will be stored into . In such an occurrence, this method will not dispose of the + /// objects that were not stored. If such objects must be disposed, it is up to the caller to determine + /// if an object was not used and to then dispose of the object appropriately. + /// + /// + public static T EnsureInitialized(ref T target, Func valueFactory) where T : class => + Volatile.Read(ref target) ?? EnsureInitializedCore(ref target, valueFactory); + + /// + /// Initialize the target using the given delegate (slow path). + /// + /// The reference type of the reference to be initialized. + /// The variable that need to be initialized + /// The delegate that will be executed to initialize the target + /// The initialized variable + private static T EnsureInitializedCore(ref T target, Func valueFactory) where T : class + { + T value = valueFactory(); + if (value == null) + { + throw new InvalidOperationException(SR.Lazy_StaticInit_InvalidOperation); + } + + Interlocked.CompareExchange(ref target, value, null); + Debug.Assert(target != null); + return target; + } + + /// + /// Initializes a target reference or value type with its default constructor if it has not already + /// been initialized. + /// + /// The type of the reference to be initialized. + /// A reference or value of type to initialize if it + /// has not already been initialized. + /// A reference to a boolean that determines whether the target has already + /// been initialized. + /// A reference to an object used as the mutually exclusive lock for initializing + /// . If is null, a new object will be instantiated. + /// The initialized value of type . + public static T EnsureInitialized(ref T target, ref bool initialized, ref object syncLock) + { + // Fast path. + if (Volatile.Read(ref initialized)) + { + return target; + } + + return EnsureInitializedCore(ref target, ref initialized, ref syncLock); + } + + /// + /// Ensure the target is initialized and return the value (slow path). This overload permits nulls + /// and also works for value type targets. Uses the type's default constructor to create the value. + /// + /// The type of target. + /// A reference to the target to be initialized. + /// A reference to a location tracking whether the target has been initialized. + /// A reference to a location containing a mutual exclusive lock. If is null, + /// a new object will be instantiated. + /// + /// The initialized object. + private static T EnsureInitializedCore(ref T target, ref bool initialized, ref object syncLock) + { + // Lazily initialize the lock if necessary and then double check if initialization is still required. + lock (EnsureLockInitialized(ref syncLock)) + { + if (!Volatile.Read(ref initialized)) + { + try + { + target = Activator.CreateInstance(); + } + catch (MissingMethodException) + { + throw new MissingMemberException(SR.Lazy_CreateValue_NoParameterlessCtorForT); + } + + Volatile.Write(ref initialized, true); + } + } + + return target; + } + + /// + /// Initializes a target reference or value type with a specified function if it has not already been + /// initialized. + /// + /// The type of the reference to be initialized. + /// A reference or value of type to initialize if it + /// has not already been initialized. + /// A reference to a boolean that determines whether the target has already + /// been initialized. + /// A reference to an object used as the mutually exclusive lock for initializing + /// . If is null, a new object will be instantiated. + /// The invoked to initialize the + /// reference or value. + /// The initialized value of type . + public static T EnsureInitialized(ref T target, ref bool initialized, ref object syncLock, Func valueFactory) + { + // Fast path. + if (Volatile.Read(ref initialized)) + { + return target; + } + + return EnsureInitializedCore(ref target, ref initialized, ref syncLock, valueFactory); + } + + /// + /// Ensure the target is initialized and return the value (slow path). This overload permits nulls + /// and also works for value type targets. Uses the supplied function to create the value. + /// + /// The type of target. + /// A reference to the target to be initialized. + /// A reference to a location tracking whether the target has been initialized. + /// A reference to a location containing a mutual exclusive lock. If is null, + /// a new object will be instantiated. + /// + /// The to invoke in order to produce the lazily-initialized value. + /// + /// The initialized object. + private static T EnsureInitializedCore(ref T target, ref bool initialized, ref object syncLock, Func valueFactory) + { + // Lazily initialize the lock if necessary and then double check if initialization is still required. + lock (EnsureLockInitialized(ref syncLock)) + { + if (!Volatile.Read(ref initialized)) + { + target = valueFactory(); + Volatile.Write(ref initialized, true); + } + } + + return target; + } + + /// + /// Initializes a target reference type with a specified function if it has not already been initialized. + /// + /// The type of the reference to be initialized. Has to be reference type. + /// A reference of type to initialize if it has not already been initialized. + /// A reference to an object used as the mutually exclusive lock for initializing + /// . If is null, a new object will be instantiated. + /// The invoked to initialize the reference. + /// The initialized value of type . + public static T EnsureInitialized(ref T target, ref object syncLock, Func valueFactory) where T : class => + Volatile.Read(ref target) ?? EnsureInitializedCore(ref target, ref syncLock, valueFactory); + + /// + /// Ensure the target is initialized and return the value (slow path). This overload works only for reference type targets. + /// Uses the supplied function to create the value. + /// + /// The type of target. Has to be reference type. + /// A reference to the target to be initialized. + /// A reference to a location containing a mutual exclusive lock. If is null, + /// a new object will be instantiated. + /// + /// The to invoke in order to produce the lazily-initialized value. + /// + /// The initialized object. + private static T EnsureInitializedCore(ref T target, ref object syncLock, Func valueFactory) where T : class + { + // Lazily initialize the lock if necessary and then double check if initialization is still required. + lock (EnsureLockInitialized(ref syncLock)) + { + if (Volatile.Read(ref target) == null) + { + Volatile.Write(ref target, valueFactory()); + if (target == null) + { + throw new InvalidOperationException(SR.Lazy_StaticInit_InvalidOperation); + } + } + } + + return target; + } + + /// + /// Ensure the lock object is initialized. + /// + /// A reference to a location containing a mutual exclusive lock. If is null, + /// a new object will be instantiated. + /// Initialized lock object. + private static object EnsureLockInitialized(ref object syncLock) => + syncLock ?? + Interlocked.CompareExchange(ref syncLock, new object(), null) ?? + syncLock; + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Threading/LockRecursionException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Threading/LockRecursionException.cs index 86e19032b3..c76c7a5040 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Threading/LockRecursionException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Threading/LockRecursionException.cs @@ -7,6 +7,8 @@ using System.Runtime.Serialization; namespace System.Threading { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class LockRecursionException : System.Exception { public LockRecursionException() @@ -25,7 +27,6 @@ namespace System.Threading protected LockRecursionException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Threading/ReaderWriterLockSlim.cs b/external/corert/src/System.Private.CoreLib/shared/System/Threading/ReaderWriterLockSlim.cs new file mode 100644 index 0000000000..3c4aad603a --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Threading/ReaderWriterLockSlim.cs @@ -0,0 +1,1682 @@ +// 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 Internal.Runtime.Augments; +using System.Diagnostics; // for TraceInformation +using System.Runtime.CompilerServices; + +namespace System.Threading +{ + public enum LockRecursionPolicy + { + NoRecursion = 0, + SupportsRecursion = 1, + } + + // + // ReaderWriterCount tracks how many of each kind of lock is held by each thread. + // We keep a linked list for each thread, attached to a ThreadStatic field. + // These are reused wherever possible, so that a given thread will only + // allocate N of these, where N is the maximum number of locks held simultaneously + // by that thread. + // + internal class ReaderWriterCount + { + // Which lock does this object belong to? This is a numeric ID for two reasons: + // 1) We don't want this field to keep the lock object alive, and a WeakReference would + // be too expensive. + // 2) Setting the value of a long is faster than setting the value of a reference. + // The "hot" paths in ReaderWriterLockSlim are short enough that this actually + // matters. + public long lockID; + + // How many reader locks does this thread hold on this ReaderWriterLockSlim instance? + public int readercount; + + // Ditto for writer/upgrader counts. These are only used if the lock allows recursion. + // But we have to have the fields on every ReaderWriterCount instance, because + // we reuse it for different locks. + public int writercount; + public int upgradecount; + + // Next RWC in this thread's list. + public ReaderWriterCount next; + } + + /// + /// A reader-writer lock implementation that is intended to be simple, yet very + /// efficient. In particular only 1 interlocked operation is taken for any lock + /// operation (we use spin locks to achieve this). The spin lock is never held + /// for more than a few instructions (in particular, we never call event APIs + /// or in fact any non-trivial API while holding the spin lock). + /// + public class ReaderWriterLockSlim : IDisposable + { + private static readonly int ProcessorCount = Environment.ProcessorCount; + + //Specifying if the lock can be reacquired recursively. + private readonly bool _fIsReentrant; + + // Lock specification for _spinLock: This lock protects exactly the local fields associated with this + // instance of ReaderWriterLockSlim. It does NOT protect the memory associated with + // the events that hang off this lock (eg writeEvent, readEvent upgradeEvent). + SpinLock _spinLock; + + // These variables allow use to avoid Setting events (which is expensive) if we don't have to. + private uint _numWriteWaiters; // maximum number of threads that can be doing a WaitOne on the writeEvent + private uint _numReadWaiters; // maximum number of threads that can be doing a WaitOne on the readEvent + private uint _numWriteUpgradeWaiters; // maximum number of threads that can be doing a WaitOne on the upgradeEvent (at most 1). + private uint _numUpgradeWaiters; + + private WaiterStates _waiterStates; + + private int _upgradeLockOwnerId; + private int _writeLockOwnerId; + + // conditions we wait on. + private EventWaitHandle _writeEvent; // threads waiting to acquire a write lock go here. + private EventWaitHandle _readEvent; // threads waiting to acquire a read lock go here (will be released in bulk) + private EventWaitHandle _upgradeEvent; // thread waiting to acquire the upgrade lock + private EventWaitHandle _waitUpgradeEvent; // thread waiting to upgrade from the upgrade lock to a write lock go here (at most one) + + // Every lock instance has a unique ID, which is used by ReaderWriterCount to associate itself with the lock + // without holding a reference to it. + private static long s_nextLockID; + private long _lockID; + + // See comments on ReaderWriterCount. + [ThreadStatic] + private static ReaderWriterCount t_rwc; + + private bool _fUpgradeThreadHoldingRead; + + private const int MaxSpinCount = 20; + + //The uint, that contains info like if the writer lock is held, num of + //readers etc. + private uint _owners; + + //Various R/W masks + //Note: + //The Uint is divided as follows: + // + //Writer-Owned Waiting-Writers Waiting Upgraders Num-Readers + // 31 30 29 28.......0 + // + //Dividing the uint, allows to vastly simplify logic for checking if a + //reader should go in etc. Setting the writer bit will automatically + //make the value of the uint much larger than the max num of readers + //allowed, thus causing the check for max_readers to fail. + + private const uint WRITER_HELD = 0x80000000; + private const uint WAITING_WRITERS = 0x40000000; + private const uint WAITING_UPGRADER = 0x20000000; + + //The max readers is actually one less then its theoretical max. + //This is done in order to prevent reader count overflows. If the reader + //count reaches max, other readers will wait. + private const uint MAX_READER = 0x10000000 - 2; + + private const uint READER_MASK = 0x10000000 - 1; + + private bool _fDisposed; + + private void InitializeThreadCounts() + { + _upgradeLockOwnerId = -1; + _writeLockOwnerId = -1; + } + + public ReaderWriterLockSlim() + : this(LockRecursionPolicy.NoRecursion) + { + } + + public ReaderWriterLockSlim(LockRecursionPolicy recursionPolicy) + { + if (recursionPolicy == LockRecursionPolicy.SupportsRecursion) + { + _fIsReentrant = true; + } + InitializeThreadCounts(); + _waiterStates = WaiterStates.NoWaiters; + _lockID = Interlocked.Increment(ref s_nextLockID); + } + + private bool HasNoWaiters + { + get + { +#if DEBUG + Debug.Assert(_spinLock.IsHeld); +#endif + + return (_waiterStates & WaiterStates.NoWaiters) != WaiterStates.None; + } + set + { +#if DEBUG + Debug.Assert(_spinLock.IsHeld); +#endif + + if (value) + { + _waiterStates |= WaiterStates.NoWaiters; + } + else + { + _waiterStates &= ~WaiterStates.NoWaiters; + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsRWEntryEmpty(ReaderWriterCount rwc) + { + if (rwc.lockID == 0) + return true; + else if (rwc.readercount == 0 && rwc.writercount == 0 && rwc.upgradecount == 0) + return true; + else + return false; + } + + private bool IsRwHashEntryChanged(ReaderWriterCount lrwc) + { + return lrwc.lockID != _lockID; + } + + /// + /// This routine retrieves/sets the per-thread counts needed to enforce the + /// various rules related to acquiring the lock. + /// + /// DontAllocate is set to true if the caller just wants to get an existing + /// entry for this thread, but doesn't want to add one if an existing one + /// could not be found. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ReaderWriterCount GetThreadRWCount(bool dontAllocate) + { + ReaderWriterCount rwc = t_rwc; + ReaderWriterCount empty = null; + while (rwc != null) + { + if (rwc.lockID == _lockID) + return rwc; + + if (!dontAllocate && empty == null && IsRWEntryEmpty(rwc)) + empty = rwc; + + rwc = rwc.next; + } + + if (dontAllocate) + return null; + + if (empty == null) + { + empty = new ReaderWriterCount(); + empty.next = t_rwc; + t_rwc = empty; + } + + empty.lockID = _lockID; + return empty; + } + + public void EnterReadLock() + { + TryEnterReadLock(-1); + } + + // + // Common timeout support + // + private struct TimeoutTracker + { + private int _total; + private int _start; + + public TimeoutTracker(TimeSpan timeout) + { + long ltm = (long)timeout.TotalMilliseconds; + if (ltm < -1 || ltm > (long)Int32.MaxValue) + throw new ArgumentOutOfRangeException(nameof(timeout)); + _total = (int)ltm; + if (_total != -1 && _total != 0) + _start = Environment.TickCount; + else + _start = 0; + } + + public TimeoutTracker(int millisecondsTimeout) + { + if (millisecondsTimeout < -1) + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout)); + _total = millisecondsTimeout; + if (_total != -1 && _total != 0) + _start = Environment.TickCount; + else + _start = 0; + } + + public int RemainingMilliseconds + { + get + { + if (_total == -1 || _total == 0) + return _total; + + int elapsed = Environment.TickCount - _start; + // elapsed may be negative if TickCount has overflowed by 2^31 milliseconds. + if (elapsed < 0 || elapsed >= _total) + return 0; + + return _total - elapsed; + } + } + + public bool IsExpired + { + get + { + return RemainingMilliseconds == 0; + } + } + } + + public bool TryEnterReadLock(TimeSpan timeout) + { + return TryEnterReadLock(new TimeoutTracker(timeout)); + } + + public bool TryEnterReadLock(int millisecondsTimeout) + { + return TryEnterReadLock(new TimeoutTracker(millisecondsTimeout)); + } + + private bool TryEnterReadLock(TimeoutTracker timeout) + { + return TryEnterReadLockCore(timeout); + } + + private bool TryEnterReadLockCore(TimeoutTracker timeout) + { + if (_fDisposed) + throw new ObjectDisposedException(null); + + ReaderWriterCount lrwc = null; + int id = Environment.CurrentManagedThreadId; + + if (!_fIsReentrant) + { + if (id == _writeLockOwnerId) + { + //Check for AW->AR + throw new LockRecursionException(SR.LockRecursionException_ReadAfterWriteNotAllowed); + } + + _spinLock.Enter(EnterSpinLockReason.EnterAnyRead); + + lrwc = GetThreadRWCount(false); + + //Check if the reader lock is already acquired. Note, we could + //check the presence of a reader by not allocating rwc (But that + //would lead to two lookups in the common case. It's better to keep + //a count in the structure). + if (lrwc.readercount > 0) + { + _spinLock.Exit(); + throw new LockRecursionException(SR.LockRecursionException_RecursiveReadNotAllowed); + } + else if (id == _upgradeLockOwnerId) + { + //The upgrade lock is already held. + //Update the global read counts and exit. + + lrwc.readercount++; + _owners++; + _spinLock.Exit(); + return true; + } + } + else + { + _spinLock.Enter(EnterSpinLockReason.EnterAnyRead); + lrwc = GetThreadRWCount(false); + if (lrwc.readercount > 0) + { + lrwc.readercount++; + _spinLock.Exit(); + return true; + } + else if (id == _upgradeLockOwnerId) + { + //The upgrade lock is already held. + //Update the global read counts and exit. + lrwc.readercount++; + _owners++; + _spinLock.Exit(); + _fUpgradeThreadHoldingRead = true; + return true; + } + else if (id == _writeLockOwnerId) + { + //The write lock is already held. + //Update global read counts here, + lrwc.readercount++; + _owners++; + _spinLock.Exit(); + return true; + } + } + + bool retVal = true; + int spinCount = 0; + + for (; ;) + { + // We can enter a read lock if there are only read-locks have been given out + // and a writer is not trying to get in. + + if (_owners < MAX_READER) + { + // Good case, there is no contention, we are basically done + _owners++; // Indicate we have another reader + lrwc.readercount++; + break; + } + + if (timeout.IsExpired) + { + _spinLock.Exit(); + return false; + } + + if (spinCount < MaxSpinCount && ShouldSpinForEnterAnyRead()) + { + _spinLock.Exit(); + spinCount++; + SpinWait(spinCount); + _spinLock.Enter(EnterSpinLockReason.EnterAnyRead); + //The per-thread structure may have been recycled as the lock is acquired (due to message pumping), load again. + if (IsRwHashEntryChanged(lrwc)) + lrwc = GetThreadRWCount(false); + continue; + } + + // Drat, we need to wait. Mark that we have waiters and wait. + if (_readEvent == null) // Create the needed event + { + LazyCreateEvent(ref _readEvent, EnterLockType.Read); + if (IsRwHashEntryChanged(lrwc)) + lrwc = GetThreadRWCount(false); + continue; // since we left the lock, start over. + } + + retVal = WaitOnEvent(_readEvent, ref _numReadWaiters, timeout, EnterLockType.Read); + if (!retVal) + { + return false; + } + if (IsRwHashEntryChanged(lrwc)) + lrwc = GetThreadRWCount(false); + } + + _spinLock.Exit(); + return retVal; + } + + public void EnterWriteLock() + { + TryEnterWriteLock(-1); + } + + public bool TryEnterWriteLock(TimeSpan timeout) + { + return TryEnterWriteLock(new TimeoutTracker(timeout)); + } + + public bool TryEnterWriteLock(int millisecondsTimeout) + { + return TryEnterWriteLock(new TimeoutTracker(millisecondsTimeout)); + } + + private bool TryEnterWriteLock(TimeoutTracker timeout) + { + return TryEnterWriteLockCore(timeout); + } + + private bool TryEnterWriteLockCore(TimeoutTracker timeout) + { + if (_fDisposed) + throw new ObjectDisposedException(null); + + int id = Environment.CurrentManagedThreadId; + ReaderWriterCount lrwc; + bool upgradingToWrite = false; + + if (!_fIsReentrant) + { + EnterSpinLockReason enterMyLockReason; + if (id == _writeLockOwnerId) + { + //Check for AW->AW + throw new LockRecursionException(SR.LockRecursionException_RecursiveWriteNotAllowed); + } + else if (id == _upgradeLockOwnerId) + { + //AU->AW case is allowed once. + upgradingToWrite = true; + enterMyLockReason = EnterSpinLockReason.UpgradeToWrite; + } + else + { + enterMyLockReason = EnterSpinLockReason.EnterWrite; + } + _spinLock.Enter(enterMyLockReason); + + lrwc = GetThreadRWCount(true); + + //Can't acquire write lock with reader lock held. + if (lrwc != null && lrwc.readercount > 0) + { + _spinLock.Exit(); + throw new LockRecursionException(SR.LockRecursionException_WriteAfterReadNotAllowed); + } + } + else + { + EnterSpinLockReason enterMyLockReason; + if (id == _writeLockOwnerId) + { + enterMyLockReason = EnterSpinLockReason.EnterRecursiveWrite; + } + else if (id == _upgradeLockOwnerId) + { + enterMyLockReason = EnterSpinLockReason.UpgradeToWrite; + } + else + { + enterMyLockReason = EnterSpinLockReason.EnterWrite; + } + _spinLock.Enter(enterMyLockReason); + + lrwc = GetThreadRWCount(false); + + if (id == _writeLockOwnerId) + { + lrwc.writercount++; + _spinLock.Exit(); + return true; + } + else if (id == _upgradeLockOwnerId) + { + upgradingToWrite = true; + } + else if (lrwc.readercount > 0) + { + //Write locks may not be acquired if only read locks have been + //acquired. + _spinLock.Exit(); + throw new LockRecursionException(SR.LockRecursionException_WriteAfterReadNotAllowed); + } + } + + bool retVal = true; + int spinCount = 0; + + for (; ;) + { + if (IsWriterAcquired()) + { + // Good case, there is no contention, we are basically done + SetWriterAcquired(); + break; + } + + //Check if there is just one upgrader, and no readers. + //Assumption: Only one thread can have the upgrade lock, so the + //following check will fail for all other threads that may sneak in + //when the upgrading thread is waiting. + + if (upgradingToWrite) + { + uint readercount = GetNumReaders(); + + if (readercount == 1) + { + //Good case again, there is just one upgrader, and no readers. + SetWriterAcquired(); // indicate we have a writer. + break; + } + else if (readercount == 2) + { + if (lrwc != null) + { + if (IsRwHashEntryChanged(lrwc)) + lrwc = GetThreadRWCount(false); + + if (lrwc.readercount > 0) + { + //This check is needed for EU->ER->EW case, as the owner count will be two. + Debug.Assert(_fIsReentrant); + Debug.Assert(_fUpgradeThreadHoldingRead); + + //Good case again, there is just one upgrader, and no readers. + SetWriterAcquired(); // indicate we have a writer. + break; + } + } + } + } + + if (timeout.IsExpired) + { + _spinLock.Exit(); + return false; + } + + if (spinCount < MaxSpinCount && ShouldSpinForEnterAnyWrite(upgradingToWrite)) + { + _spinLock.Exit(); + spinCount++; + SpinWait(spinCount); + _spinLock.Enter(upgradingToWrite ? EnterSpinLockReason.UpgradeToWrite : EnterSpinLockReason.EnterWrite); + continue; + } + + if (upgradingToWrite) + { + if (_waitUpgradeEvent == null) // Create the needed event + { + LazyCreateEvent(ref _waitUpgradeEvent, EnterLockType.UpgradeToWrite); + continue; // since we left the lock, start over. + } + + Debug.Assert(_numWriteUpgradeWaiters == 0, "There can be at most one thread with the upgrade lock held."); + + retVal = WaitOnEvent(_waitUpgradeEvent, ref _numWriteUpgradeWaiters, timeout, EnterLockType.UpgradeToWrite); + + //The lock is not held in case of failure. + if (!retVal) + return false; + } + else + { + // Drat, we need to wait. Mark that we have waiters and wait. + if (_writeEvent == null) // create the needed event. + { + LazyCreateEvent(ref _writeEvent, EnterLockType.Write); + continue; // since we left the lock, start over. + } + + retVal = WaitOnEvent(_writeEvent, ref _numWriteWaiters, timeout, EnterLockType.Write); + //The lock is not held in case of failure. + if (!retVal) + return false; + } + } + + Debug.Assert((_owners & WRITER_HELD) > 0); + + if (_fIsReentrant) + { + if (IsRwHashEntryChanged(lrwc)) + lrwc = GetThreadRWCount(false); + lrwc.writercount++; + } + + _spinLock.Exit(); + + _writeLockOwnerId = id; + + return true; + } + + public void EnterUpgradeableReadLock() + { + TryEnterUpgradeableReadLock(-1); + } + + public bool TryEnterUpgradeableReadLock(TimeSpan timeout) + { + return TryEnterUpgradeableReadLock(new TimeoutTracker(timeout)); + } + + public bool TryEnterUpgradeableReadLock(int millisecondsTimeout) + { + return TryEnterUpgradeableReadLock(new TimeoutTracker(millisecondsTimeout)); + } + + private bool TryEnterUpgradeableReadLock(TimeoutTracker timeout) + { + return TryEnterUpgradeableReadLockCore(timeout); + } + + private bool TryEnterUpgradeableReadLockCore(TimeoutTracker timeout) + { + if (_fDisposed) + throw new ObjectDisposedException(null); + + int id = Environment.CurrentManagedThreadId; + ReaderWriterCount lrwc; + + if (!_fIsReentrant) + { + if (id == _upgradeLockOwnerId) + { + //Check for AU->AU + throw new LockRecursionException(SR.LockRecursionException_RecursiveUpgradeNotAllowed); + } + else if (id == _writeLockOwnerId) + { + //Check for AU->AW + throw new LockRecursionException(SR.LockRecursionException_UpgradeAfterWriteNotAllowed); + } + + _spinLock.Enter(EnterSpinLockReason.EnterAnyRead); + lrwc = GetThreadRWCount(true); + //Can't acquire upgrade lock with reader lock held. + if (lrwc != null && lrwc.readercount > 0) + { + _spinLock.Exit(); + throw new LockRecursionException(SR.LockRecursionException_UpgradeAfterReadNotAllowed); + } + } + else + { + _spinLock.Enter(EnterSpinLockReason.EnterAnyRead); + lrwc = GetThreadRWCount(false); + + if (id == _upgradeLockOwnerId) + { + lrwc.upgradecount++; + _spinLock.Exit(); + return true; + } + else if (id == _writeLockOwnerId) + { + //Write lock is already held, Just update the global state + //to show presence of upgrader. + Debug.Assert((_owners & WRITER_HELD) > 0); + _owners++; + _upgradeLockOwnerId = id; + lrwc.upgradecount++; + if (lrwc.readercount > 0) + _fUpgradeThreadHoldingRead = true; + _spinLock.Exit(); + return true; + } + else if (lrwc.readercount > 0) + { + //Upgrade locks may not be acquired if only read locks have been + //acquired. + _spinLock.Exit(); + throw new LockRecursionException(SR.LockRecursionException_UpgradeAfterReadNotAllowed); + } + } + + bool retVal = true; + int spinCount = 0; + + for (; ;) + { + //Once an upgrade lock is taken, it's like having a reader lock held + //until upgrade or downgrade operations are performed. + + if ((_upgradeLockOwnerId == -1) && (_owners < MAX_READER)) + { + _owners++; + _upgradeLockOwnerId = id; + break; + } + + if (timeout.IsExpired) + { + _spinLock.Exit(); + return false; + } + + if (spinCount < MaxSpinCount && ShouldSpinForEnterAnyRead()) + { + _spinLock.Exit(); + spinCount++; + SpinWait(spinCount); + _spinLock.Enter(EnterSpinLockReason.EnterAnyRead); + continue; + } + + // Drat, we need to wait. Mark that we have waiters and wait. + if (_upgradeEvent == null) // Create the needed event + { + LazyCreateEvent(ref _upgradeEvent, EnterLockType.UpgradeableRead); + continue; // since we left the lock, start over. + } + + //Only one thread with the upgrade lock held can proceed. + retVal = WaitOnEvent(_upgradeEvent, ref _numUpgradeWaiters, timeout, EnterLockType.UpgradeableRead); + if (!retVal) + return false; + } + + if (_fIsReentrant) + { + //The lock may have been dropped getting here, so make a quick check to see whether some other + //thread did not grab the entry. + if (IsRwHashEntryChanged(lrwc)) + lrwc = GetThreadRWCount(false); + lrwc.upgradecount++; + } + + _spinLock.Exit(); + + return true; + } + + public void ExitReadLock() + { + ReaderWriterCount lrwc = null; + + _spinLock.Enter(EnterSpinLockReason.ExitAnyRead); + + lrwc = GetThreadRWCount(true); + + if (lrwc == null || lrwc.readercount < 1) + { + //You have to be holding the read lock to make this call. + _spinLock.Exit(); + throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedRead); + } + + if (_fIsReentrant) + { + if (lrwc.readercount > 1) + { + lrwc.readercount--; + _spinLock.Exit(); + return; + } + + if (Environment.CurrentManagedThreadId == _upgradeLockOwnerId) + { + _fUpgradeThreadHoldingRead = false; + } + } + + Debug.Assert(_owners > 0, "ReleasingReaderLock: releasing lock and no read lock taken"); + + --_owners; + + Debug.Assert(lrwc.readercount == 1); + lrwc.readercount--; + + ExitAndWakeUpAppropriateWaiters(); + } + + public void ExitWriteLock() + { + ReaderWriterCount lrwc; + if (!_fIsReentrant) + { + if (Environment.CurrentManagedThreadId != _writeLockOwnerId) + { + //You have to be holding the write lock to make this call. + throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedWrite); + } + _spinLock.Enter(EnterSpinLockReason.ExitAnyWrite); + } + else + { + _spinLock.Enter(EnterSpinLockReason.ExitAnyWrite); + lrwc = GetThreadRWCount(false); + + if (lrwc == null) + { + _spinLock.Exit(); + throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedWrite); + } + + if (lrwc.writercount < 1) + { + _spinLock.Exit(); + throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedWrite); + } + + lrwc.writercount--; + + if (lrwc.writercount > 0) + { + _spinLock.Exit(); + return; + } + } + + Debug.Assert((_owners & WRITER_HELD) > 0, "Calling ReleaseWriterLock when no write lock is held"); + + ClearWriterAcquired(); + + _writeLockOwnerId = -1; + + ExitAndWakeUpAppropriateWaiters(); + } + + public void ExitUpgradeableReadLock() + { + ReaderWriterCount lrwc; + if (!_fIsReentrant) + { + if (Environment.CurrentManagedThreadId != _upgradeLockOwnerId) + { + //You have to be holding the upgrade lock to make this call. + throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedUpgrade); + } + _spinLock.Enter(EnterSpinLockReason.ExitAnyRead); + } + else + { + _spinLock.Enter(EnterSpinLockReason.ExitAnyRead); + lrwc = GetThreadRWCount(true); + + if (lrwc == null) + { + _spinLock.Exit(); + throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedUpgrade); + } + + if (lrwc.upgradecount < 1) + { + _spinLock.Exit(); + throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedUpgrade); + } + + lrwc.upgradecount--; + + if (lrwc.upgradecount > 0) + { + _spinLock.Exit(); + return; + } + + _fUpgradeThreadHoldingRead = false; + } + + _owners--; + _upgradeLockOwnerId = -1; + + ExitAndWakeUpAppropriateWaiters(); + } + + /// + /// A routine for lazily creating a event outside the lock (so if errors + /// happen they are outside the lock and that we don't do much work + /// while holding a spin lock). If all goes well, reenter the lock and + /// set 'waitEvent' + /// + private void LazyCreateEvent(ref EventWaitHandle waitEvent, EnterLockType enterLockType) + { +#if DEBUG + Debug.Assert(_spinLock.IsHeld); + Debug.Assert(waitEvent == null); +#endif + + _spinLock.Exit(); + + var newEvent = + new EventWaitHandle( + false, + enterLockType == EnterLockType.Read ? EventResetMode.ManualReset : EventResetMode.AutoReset); + + EnterSpinLockReason enterMyLockReason; + switch (enterLockType) + { + case EnterLockType.Read: + case EnterLockType.UpgradeableRead: + enterMyLockReason = EnterSpinLockReason.EnterAnyRead | EnterSpinLockReason.Wait; + break; + + case EnterLockType.Write: + enterMyLockReason = EnterSpinLockReason.EnterWrite | EnterSpinLockReason.Wait; + break; + + default: + Debug.Assert(enterLockType == EnterLockType.UpgradeToWrite); + enterMyLockReason = EnterSpinLockReason.UpgradeToWrite | EnterSpinLockReason.Wait; + break; + } + _spinLock.Enter(enterMyLockReason); + + if (waitEvent == null) // maybe someone snuck in. + waitEvent = newEvent; + else + newEvent.Dispose(); + } + + /// + /// Waits on 'waitEvent' with a timeout + /// Before the wait 'numWaiters' is incremented and is restored before leaving this routine. + /// + private bool WaitOnEvent( + EventWaitHandle waitEvent, + ref uint numWaiters, + TimeoutTracker timeout, + EnterLockType enterLockType) + { +#if DEBUG + Debug.Assert(_spinLock.IsHeld); +#endif + + WaiterStates waiterSignaledState = WaiterStates.None; + EnterSpinLockReason enterMyLockReason; + switch (enterLockType) + { + case EnterLockType.UpgradeableRead: + waiterSignaledState = WaiterStates.UpgradeableReadWaiterSignaled; + goto case EnterLockType.Read; + + case EnterLockType.Read: + enterMyLockReason = EnterSpinLockReason.EnterAnyRead; + break; + + case EnterLockType.Write: + waiterSignaledState = WaiterStates.WriteWaiterSignaled; + enterMyLockReason = EnterSpinLockReason.EnterWrite; + break; + + default: + Debug.Assert(enterLockType == EnterLockType.UpgradeToWrite); + enterMyLockReason = EnterSpinLockReason.UpgradeToWrite; + break; + } + + // It was not possible to acquire the RW lock because some other thread was holding some type of lock. The other + // thread, when it releases its lock, will wake appropriate waiters. Along with resetting the wait event, clear the + // waiter signaled bit for this type of waiter if applicable, to indicate that a waiter of this type is no longer + // signaled. + // + // If the waiter signaled bit is not updated upon event reset, the following scenario would lead to deadlock: + // - Thread T0 signals the write waiter event or the upgradeable read waiter event to wake a waiter + // - There are no threads waiting on the event, but T1 is in WaitOnEvent() after exiting the spin lock and before + // actually waiting on the event (that is, it's recorded that there is one waiter for the event). It remains in + // this region for a while, in the repro case it typically gets context-switched out. + // - T2 acquires the RW lock in some fashion that blocks T0 or T3 from acquiring the RW lock + // - T0 or T3 fails to acquire the RW lock enough times for it to enter WaitOnEvent for the same event as T1 + // - T0 or T3 resets the event + // - T2 releases the RW lock and does not wake a waiter because the reset at the previous step lost a signal but + // _waiterStates was not updated to reflect that + // - T1 and other threads begin waiting on the event, but there's no longer any thread that would wake them + if (waiterSignaledState != WaiterStates.None && (_waiterStates & waiterSignaledState) != WaiterStates.None) + { + _waiterStates &= ~waiterSignaledState; + } + waitEvent.Reset(); + + numWaiters++; + HasNoWaiters = false; + + //Setting these bits will prevent new readers from getting in. + if (_numWriteWaiters == 1) + SetWritersWaiting(); + if (_numWriteUpgradeWaiters == 1) + SetUpgraderWaiting(); + + bool waitSuccessful = false; + _spinLock.Exit(); // Do the wait outside of any lock + + try + { + waitSuccessful = waitEvent.WaitOne(timeout.RemainingMilliseconds); + } + finally + { + _spinLock.Enter(enterMyLockReason); + + --numWaiters; + + if (waitSuccessful && + waiterSignaledState != WaiterStates.None && + (_waiterStates & waiterSignaledState) != WaiterStates.None) + { + // Indicate that a signaled waiter of this type has woken. Since non-read waiters are signaled to wake one + // at a time, we avoid waking up more than one waiter of that type upon successive enter/exit loops until + // the signaled thread actually wakes up. For example, if there are multiple write waiters and one thread is + // repeatedly entering and exiting a write lock, every exit would otherwise signal a different write waiter + // to wake up unnecessarily when only one woken waiter may actually succeed in entering the write lock. + _waiterStates &= ~waiterSignaledState; + } + + if (_numWriteWaiters == 0 && _numWriteUpgradeWaiters == 0 && _numUpgradeWaiters == 0 && _numReadWaiters == 0) + HasNoWaiters = true; + + if (_numWriteWaiters == 0) + ClearWritersWaiting(); + if (_numWriteUpgradeWaiters == 0) + ClearUpgraderWaiting(); + + if (!waitSuccessful) // We may also be about to throw for some reason. Exit myLock. + { + if (enterLockType >= EnterLockType.Write) + { + // Write waiters block read waiters from acquiring the lock. Since this was the last write waiter, try + // to wake up the appropriate read waiters. + ExitAndWakeUpAppropriateReadWaiters(); + } + else + { + _spinLock.Exit(); + } + } + } + return waitSuccessful; + } + + /// + /// Determines the appropriate events to set, leaves the locks, and sets the events. + /// + private void ExitAndWakeUpAppropriateWaiters() + { +#if DEBUG + Debug.Assert(_spinLock.IsHeld); +#endif + if (HasNoWaiters) + { + _spinLock.Exit(); + return; + } + + ExitAndWakeUpAppropriateWaitersPreferringWriters(); + } + + private void ExitAndWakeUpAppropriateWaitersPreferringWriters() + { + uint readercount = GetNumReaders(); + + //We need this case for EU->ER->EW case, as the read count will be 2 in + //that scenario. + if (_fIsReentrant) + { + if (_numWriteUpgradeWaiters > 0 && _fUpgradeThreadHoldingRead && readercount == 2) + { + _spinLock.Exit(); // Exit before signaling to improve efficiency (wakee will need the lock) + _waitUpgradeEvent.Set(); // release all upgraders (however there can be at most one). + return; + } + } + + if (readercount == 1 && _numWriteUpgradeWaiters > 0) + { + //We have to be careful now, as we are dropping the lock. + //No new writes should be allowed to sneak in if an upgrade + //was pending. + + _spinLock.Exit(); // Exit before signaling to improve efficiency (wakee will need the lock) + _waitUpgradeEvent.Set(); // release all upgraders (however there can be at most one). + } + else if (readercount == 0 && _numWriteWaiters > 0) + { + // Check if a waiter of the same type has already been signaled but hasn't woken yet. If so, avoid signaling + // and waking another waiter unnecessarily. + WaiterStates signaled = _waiterStates & WaiterStates.WriteWaiterSignaled; + if (signaled == WaiterStates.None) + { + _waiterStates |= WaiterStates.WriteWaiterSignaled; + } + + _spinLock.Exit(); // Exit before signaling to improve efficiency (wakee will need the lock) + + if (signaled == WaiterStates.None) + { + _writeEvent.Set(); // release one writer. + } + } + else + { + ExitAndWakeUpAppropriateReadWaiters(); + } + } + + private void ExitAndWakeUpAppropriateReadWaiters() + { +#if DEBUG + Debug.Assert(_spinLock.IsHeld); +#endif + + if (_numWriteWaiters != 0 || _numWriteUpgradeWaiters != 0 || HasNoWaiters) + { + _spinLock.Exit(); + return; + } + + Debug.Assert(_numReadWaiters != 0 || _numUpgradeWaiters != 0); + + bool setReadEvent = _numReadWaiters != 0; + bool setUpgradeEvent = _numUpgradeWaiters != 0 && _upgradeLockOwnerId == -1; + if (setUpgradeEvent) + { + // Check if a waiter of the same type has already been signaled but hasn't woken yet. If so, avoid signaling + // and waking another waiter unnecessarily. + if ((_waiterStates & WaiterStates.UpgradeableReadWaiterSignaled) == WaiterStates.None) + { + _waiterStates |= WaiterStates.UpgradeableReadWaiterSignaled; + } + else + { + setUpgradeEvent = false; + } + } + + _spinLock.Exit(); // Exit before signaling to improve efficiency (wakee will need the lock) + + if (setReadEvent) + _readEvent.Set(); // release all readers. + + if (setUpgradeEvent) + _upgradeEvent.Set(); //release one upgrader. + } + + private bool IsWriterAcquired() + { + return (_owners & ~WAITING_WRITERS) == 0; + } + + private void SetWriterAcquired() + { + _owners |= WRITER_HELD; // indicate we have a writer. + } + + private void ClearWriterAcquired() + { + _owners &= ~WRITER_HELD; + } + + private void SetWritersWaiting() + { + _owners |= WAITING_WRITERS; + } + + private void ClearWritersWaiting() + { + _owners &= ~WAITING_WRITERS; + } + + private void SetUpgraderWaiting() + { + _owners |= WAITING_UPGRADER; + } + + private void ClearUpgraderWaiting() + { + _owners &= ~WAITING_UPGRADER; + } + + private uint GetNumReaders() + { + return _owners & READER_MASK; + } + + private bool ShouldSpinForEnterAnyRead() + { + // If there is a write waiter or write upgrade waiter, the waiter would block a reader from acquiring the RW lock + // because the waiter takes precedence. In that case, the reader is not likely to make progress by spinning. + // Although another thread holding a write lock would prevent this thread from acquiring a read lock, it is by + // itself not a good enough reason to skip spinning. + return HasNoWaiters || (_numWriteWaiters == 0 && _numWriteUpgradeWaiters == 0); + } + + private bool ShouldSpinForEnterAnyWrite(bool isUpgradeToWrite) + { + // If there is a write upgrade waiter, the waiter would block a writer from acquiring the RW lock because the waiter + // holds a read lock. In that case, the writer is not likely to make progress by spinning. Regarding upgrading to a + // write lock, there is no type of waiter that would block the upgrade from happening. Although another thread + // holding a read or write lock would prevent this thread from acquiring the write lock, it is by itself not a good + // enough reason to skip spinning. + return isUpgradeToWrite || _numWriteUpgradeWaiters == 0; + } + + private static void SpinWait(int spinCount) + { + const int LockSpinCycles = 20; + + //Exponential back-off + if ((spinCount < 5) && (ProcessorCount > 1)) + { + RuntimeThread.SpinWait(LockSpinCycles * spinCount); + } + else + { + RuntimeThread.Sleep(0); + } + + // Don't want to Sleep(1) in this spin wait: + // - Don't want to spin for that long, since a proper wait will follow when the spin wait fails. The artifical + // delay introduced by Sleep(1) will in some cases be much longer than desired. + // - Sleep(1) would put the thread into a wait state, and a proper wait will follow when the spin wait fails + // anyway, so it's preferable to put the thread into the proper wait state + } + + public void Dispose() + { + Dispose(true); + } + + private void Dispose(bool disposing) + { + if (disposing && !_fDisposed) + { + if (WaitingReadCount > 0 || WaitingUpgradeCount > 0 || WaitingWriteCount > 0) + throw new SynchronizationLockException(SR.SynchronizationLockException_IncorrectDispose); + + if (IsReadLockHeld || IsUpgradeableReadLockHeld || IsWriteLockHeld) + throw new SynchronizationLockException(SR.SynchronizationLockException_IncorrectDispose); + + if (_writeEvent != null) + { + _writeEvent.Dispose(); + _writeEvent = null; + } + + if (_readEvent != null) + { + _readEvent.Dispose(); + _readEvent = null; + } + + if (_upgradeEvent != null) + { + _upgradeEvent.Dispose(); + _upgradeEvent = null; + } + + if (_waitUpgradeEvent != null) + { + _waitUpgradeEvent.Dispose(); + _waitUpgradeEvent = null; + } + + _fDisposed = true; + } + } + + public bool IsReadLockHeld + { + get + { + if (RecursiveReadCount > 0) + return true; + else + return false; + } + } + + public bool IsUpgradeableReadLockHeld + { + get + { + if (RecursiveUpgradeCount > 0) + return true; + else + return false; + } + } + + public bool IsWriteLockHeld + { + get + { + if (RecursiveWriteCount > 0) + return true; + else + return false; + } + } + + public LockRecursionPolicy RecursionPolicy + { + get + { + if (_fIsReentrant) + { + return LockRecursionPolicy.SupportsRecursion; + } + else + { + return LockRecursionPolicy.NoRecursion; + } + } + } + + public int CurrentReadCount + { + get + { + int numreaders = (int)GetNumReaders(); + + if (_upgradeLockOwnerId != -1) + return numreaders - 1; + else + return numreaders; + } + } + + + public int RecursiveReadCount + { + get + { + int count = 0; + ReaderWriterCount lrwc = GetThreadRWCount(true); + if (lrwc != null) + count = lrwc.readercount; + + return count; + } + } + + public int RecursiveUpgradeCount + { + get + { + if (_fIsReentrant) + { + int count = 0; + + ReaderWriterCount lrwc = GetThreadRWCount(true); + if (lrwc != null) + count = lrwc.upgradecount; + + return count; + } + else + { + if (Environment.CurrentManagedThreadId == _upgradeLockOwnerId) + return 1; + else + return 0; + } + } + } + + public int RecursiveWriteCount + { + get + { + if (_fIsReentrant) + { + int count = 0; + + ReaderWriterCount lrwc = GetThreadRWCount(true); + if (lrwc != null) + count = lrwc.writercount; + + return count; + } + else + { + if (Environment.CurrentManagedThreadId == _writeLockOwnerId) + return 1; + else + return 0; + } + } + } + + public int WaitingReadCount + { + get + { + return (int)_numReadWaiters; + } + } + + public int WaitingUpgradeCount + { + get + { + return (int)_numUpgradeWaiters; + } + } + + public int WaitingWriteCount + { + get + { + return (int)_numWriteWaiters; + } + } + + private struct SpinLock + { + private int _isLocked; + + /// + /// Used to deprioritize threads attempting to enter the lock when they would not make progress after doing so. + /// avoids acquiring the lock as long as the operation for which it + /// was called is deprioritized. + /// + /// Layout: + /// - Low 16 bits: Number of threads that have deprioritized an enter-any-write operation + /// - High 16 bits: Number of threads that have deprioritized an enter-any-read operation + /// + private int _enterDeprioritizationState; + + // Layout-specific constants for _enterDeprioritizationState + private const int DeprioritizeEnterAnyReadIncrement = 1 << 16; + private const int DeprioritizeEnterAnyWriteIncrement = 1; + + // The variables controlling spinning behavior of this spin lock + private const int LockSpinCycles = 20; + private const int LockSpinCount = 10; + private const int LockSleep0Count = 5; + private const int DeprioritizedLockSleep1Count = 5; + + private static int GetEnterDeprioritizationStateChange(EnterSpinLockReason reason) + { + EnterSpinLockReason operation = reason & EnterSpinLockReason.OperationMask; + switch (operation) + { + case EnterSpinLockReason.EnterAnyRead: + return 0; + + case EnterSpinLockReason.ExitAnyRead: + // A read lock is held until this thread is able to exit it, so deprioritize enter-write threads as they + // will not be able to make progress + return DeprioritizeEnterAnyWriteIncrement; + + case EnterSpinLockReason.EnterWrite: + // Writers are typically much less frequent and much less in number than readers. Waiting writers take + // precedence over new read attempts in order to let current readers release their lock and allow a + // writer to obtain the lock. Before a writer can register as a waiter though, the presence of just + // relatively few enter-read spins can easily starve the enter-write from even entering this lock, + // delaying its spin loop for an unreasonable duration. + // + // Deprioritize enter-read to preference enter-write. This makes it easier for enter-write threads to + // starve enter-read threads. However, writers can already by design starve readers. A waiting writer + // blocks enter-read threads and a new enter-write that needs to wait will be given precedence over + // previously waiting enter-read threads. So this is not a new problem, and the RW lock is designed for + // scenarios where writers are rare compared to readers. + return DeprioritizeEnterAnyReadIncrement; + + default: + Debug.Assert( + operation == EnterSpinLockReason.UpgradeToWrite || + operation == EnterSpinLockReason.EnterRecursiveWrite || + operation == EnterSpinLockReason.ExitAnyWrite); + + // UpgradeToWrite: + // - A read lock is held and an exit-read is not nearby, so deprioritize enter-write threads as they + // will not be able to make progress. This thread also intends to enter a write lock, so deprioritize + // enter -read threads as well, see case EnterSpinLockReason.EnterWrite for the rationale. + // EnterRecursiveWrite, ExitAnyWrite: + // - In both cases, a write lock is held until this thread is able to exit it, so deprioritize + // enter -read and enter-write threads as they will not be able to make progress + return DeprioritizeEnterAnyReadIncrement + DeprioritizeEnterAnyWriteIncrement; + } + } + + private ushort EnterForEnterAnyReadDeprioritizedCount + { + get + { + Debug.Assert(DeprioritizeEnterAnyReadIncrement == (1 << 16)); + return (ushort)((uint)_enterDeprioritizationState >> 16); + } + } + + private ushort EnterForEnterAnyWriteDeprioritizedCount + { + get + { + Debug.Assert(DeprioritizeEnterAnyWriteIncrement == 1); + return (ushort)_enterDeprioritizationState; + } + } + + private bool IsEnterDeprioritized(EnterSpinLockReason reason) + { + Debug.Assert((reason & EnterSpinLockReason.Wait) != 0 || reason == (reason & EnterSpinLockReason.OperationMask)); + Debug.Assert( + (reason & EnterSpinLockReason.Wait) == 0 || + (reason & EnterSpinLockReason.OperationMask) == EnterSpinLockReason.EnterAnyRead || + (reason & EnterSpinLockReason.OperationMask) == EnterSpinLockReason.EnterWrite || + (reason & EnterSpinLockReason.OperationMask) == EnterSpinLockReason.UpgradeToWrite); + + switch (reason) + { + default: + Debug.Assert( + (reason & EnterSpinLockReason.Wait) != 0 || + reason == EnterSpinLockReason.ExitAnyRead || + reason == EnterSpinLockReason.EnterRecursiveWrite || + reason == EnterSpinLockReason.ExitAnyWrite); + return false; + + case EnterSpinLockReason.EnterAnyRead: + return EnterForEnterAnyReadDeprioritizedCount != 0; + + case EnterSpinLockReason.EnterWrite: + Debug.Assert((GetEnterDeprioritizationStateChange(reason) & DeprioritizeEnterAnyWriteIncrement) == 0); + return EnterForEnterAnyWriteDeprioritizedCount != 0; + + case EnterSpinLockReason.UpgradeToWrite: + Debug.Assert((GetEnterDeprioritizationStateChange(reason) & DeprioritizeEnterAnyWriteIncrement) != 0); + return EnterForEnterAnyWriteDeprioritizedCount > 1; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool TryEnter() + { + return Interlocked.CompareExchange(ref _isLocked, 1, 0) == 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Enter(EnterSpinLockReason reason) + { + if (!TryEnter()) + { + EnterSpin(reason); + } + } + + private void EnterSpin(EnterSpinLockReason reason) + { + int deprioritizationStateChange = GetEnterDeprioritizationStateChange(reason); + if (deprioritizationStateChange != 0) + { + Interlocked.Add(ref _enterDeprioritizationState, deprioritizationStateChange); + } + + int processorCount = ProcessorCount; + for (int spinIndex = 0; ; spinIndex++) + { + if (spinIndex < LockSpinCount && processorCount > 1) + { + RuntimeThread.SpinWait(LockSpinCycles * (spinIndex + 1)); // Wait a few dozen instructions to let another processor release lock. + } + else if (spinIndex < (LockSpinCount + LockSleep0Count)) + { + RuntimeThread.Sleep(0); // Give up my quantum. + } + else + { + RuntimeThread.Sleep(1); // Give up my quantum. + } + + if (!IsEnterDeprioritized(reason)) + { + if (_isLocked == 0 && TryEnter()) + { + if (deprioritizationStateChange != 0) + { + Interlocked.Add(ref _enterDeprioritizationState, -deprioritizationStateChange); + } + return; + } + continue; + } + + // It's possible for an Enter thread to be deprioritized for an extended duration. It's undesirable for a + // deprioritized thread to keep waking up to spin despite a Sleep(1) when a large number of such threads are + // involved. After a threshold of Sleep(1)s, ignore the deprioritization and enter this lock to allow this + // thread to stop spinning and hopefully enter a proper wait state. + Debug.Assert( + reason == EnterSpinLockReason.EnterAnyRead || + reason == EnterSpinLockReason.EnterWrite || + reason == EnterSpinLockReason.UpgradeToWrite); + if (spinIndex >= (LockSpinCount + LockSleep0Count + DeprioritizedLockSleep1Count)) + { + reason |= EnterSpinLockReason.Wait; + spinIndex = -1; + } + } + } + + public void Exit() + { + Debug.Assert(_isLocked != 0, "Exiting spin lock that is not held"); + Volatile.Write(ref _isLocked, 0); + } + +#if DEBUG + public bool IsHeld => _isLocked != 0; +#endif + } + + [Flags] + private enum WaiterStates : byte + { + None = 0x0, + + // Used for quick check when there are no waiters + NoWaiters = 0x1, + + // Used to avoid signaling more than one waiter to wake up when only one can make progress, see WaitOnEvent + WriteWaiterSignaled = 0x2, + UpgradeableReadWaiterSignaled = 0x4 + // Write upgrade waiters are excluded because there can only be one at any given time + } + + private enum EnterSpinLockReason + { + EnterAnyRead = 0, + ExitAnyRead = 1, + EnterWrite = 2, + UpgradeToWrite = 3, + EnterRecursiveWrite = 4, + ExitAnyWrite = 5, + + OperationMask = 0x7, + + Wait = 0x8 + } + + private enum EnterLockType + { + Read, + UpgradeableRead, + Write, + UpgradeToWrite + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Threading/SemaphoreFullException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Threading/SemaphoreFullException.cs index 777463b60d..18558b19e0 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Threading/SemaphoreFullException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Threading/SemaphoreFullException.cs @@ -7,6 +7,8 @@ using System.Runtime.Serialization; namespace System.Threading { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class SemaphoreFullException : SystemException { public SemaphoreFullException() : base(SR.Threading_SemaphoreFullException) @@ -23,7 +25,6 @@ namespace System.Threading protected SemaphoreFullException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Threading/SpinWait.cs b/external/corert/src/System.Private.CoreLib/shared/System/Threading/SpinWait.cs new file mode 100644 index 0000000000..414ad1852f --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Threading/SpinWait.cs @@ -0,0 +1,359 @@ +// 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. + +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// Central spin logic used across the entire code-base. +// +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +using System.Diagnostics; +using Internal.Runtime.Augments; + +namespace System.Threading +{ + // SpinWait is just a little value type that encapsulates some common spinning + // logic. It ensures we always yield on single-proc machines (instead of using busy + // waits), and that we work well on HT. It encapsulates a good mixture of spinning + // and real yielding. It's a value type so that various areas of the engine can use + // one by allocating it on the stack w/out unnecessary GC allocation overhead, e.g.: + // + // void f() { + // SpinWait wait = new SpinWait(); + // while (!p) { wait.SpinOnce(); } + // ... + // } + // + // Internally it just maintains a counter that is used to decide when to yield, etc. + // + // A common usage is to spin before blocking. In those cases, the NextSpinWillYield + // property allows a user to decide to fall back to waiting once it returns true: + // + // void f() { + // SpinWait wait = new SpinWait(); + // while (!p) { + // if (wait.NextSpinWillYield) { /* block! */ } + // else { wait.SpinOnce(); } + // } + // ... + // } + + /// + /// Provides support for spin-based waiting. + /// + /// + /// + /// encapsulates common spinning logic. On single-processor machines, yields are + /// always used instead of busy waits, and on computers with Intel(R) processors employing Hyper-Threading + /// technology, it helps to prevent hardware thread starvation. SpinWait encapsulates a good mixture of + /// spinning and true yielding. + /// + /// + /// is a value type, which means that low-level code can utilize SpinWait without + /// fear of unnecessary allocation overheads. SpinWait is not generally useful for ordinary applications. + /// In most cases, you should use the synchronization classes provided by the .NET Framework, such as + /// . For most purposes where spin waiting is required, however, + /// the type should be preferred over the method. + /// + /// + /// While SpinWait is designed to be used in concurrent applications, it is not designed to be + /// used from multiple threads concurrently. SpinWait's members are not thread-safe. If multiple + /// threads must spin, each should use its own instance of SpinWait. + /// + /// + public struct SpinWait + { + // These constants determine the frequency of yields versus spinning. The + // numbers may seem fairly arbitrary, but were derived with at least some + // thought in the design document. I fully expect they will need to change + // over time as we gain more experience with performance. + internal const int YieldThreshold = 10; // When to switch over to a true yield. + private const int Sleep0EveryHowManyYields = 5; // After how many yields should we Sleep(0)? + internal const int DefaultSleep1Threshold = 20; // After how many yields should we Sleep(1) frequently? + + /// + /// A suggested number of spin iterations before doing a proper wait, such as waiting on an event that becomes signaled + /// when the resource becomes available. + /// + /// + /// These numbers were arrived at by experimenting with different numbers in various cases that currently use it. It's + /// only a suggested value and typically works well when the proper wait is something like an event. + /// + /// Spinning less can lead to early waiting and more context switching, spinning more can decrease latency but may use + /// up some CPU time unnecessarily. Depends on the situation too, for instance SemaphoreSlim uses more iterations + /// because the waiting there is currently a lot more expensive (involves more spinning, taking a lock, etc.). It also + /// depends on the likelihood of the spin being successful and how long the wait would be but those are not accounted + /// for here. + /// + internal static readonly int SpinCountforSpinBeforeWait = PlatformHelper.IsSingleProcessor ? 1 : 35; + internal const int Sleep1ThresholdForSpinBeforeWait = 40; // should be greater than SpinCountforSpinBeforeWait + + // The number of times we've spun already. + private int _count; + + /// + /// Gets the number of times has been called on this instance. + /// + public int Count + { + get => _count; + internal set + { + Debug.Assert(value >= 0); + _count = value; + } + } + + /// + /// Gets whether the next call to will yield the processor, triggering a + /// forced context switch. + /// + /// Whether the next call to will yield the processor, triggering a + /// forced context switch. + /// + /// On a single-CPU machine, always yields the processor. On machines with + /// multiple CPUs, may yield after an unspecified number of calls. + /// + public bool NextSpinWillYield => _count >= YieldThreshold || PlatformHelper.IsSingleProcessor; + + /// + /// Performs a single spin. + /// + /// + /// This is typically called in a loop, and may change in behavior based on the number of times a + /// has been called thus far on this instance. + /// + public void SpinOnce() + { + SpinOnce(DefaultSleep1Threshold); + } + + internal void SpinOnce(int sleep1Threshold) + { + Debug.Assert(sleep1Threshold >= YieldThreshold || PlatformHelper.IsSingleProcessor); // so that NextSpinWillYield behaves as requested + + // (_count - YieldThreshold) % 2 == 0: The purpose of this check is to interleave Thread.Yield/Sleep(0) with + // Thread.SpinWait. Otherwise, the following issues occur: + // - When there are no threads to switch to, Yield and Sleep(0) become no-op and it turns the spin loop into a + // busy-spin that may quickly reach the max spin count and cause the thread to enter a wait state, or may + // just busy-spin for longer than desired before a Sleep(1). Completing the spin loop too early can cause + // excessive context switcing if a wait follows, and entering the Sleep(1) stage too early can cause + // excessive delays. + // - If there are multiple threads doing Yield and Sleep(0) (typically from the same spin loop due to + // contention), they may switch between one another, delaying work that can make progress. + if (( + _count >= YieldThreshold && + (_count >= sleep1Threshold || (_count - YieldThreshold) % 2 == 0) + ) || + PlatformHelper.IsSingleProcessor) + { + // + // We must yield. + // + // We prefer to call Thread.Yield first, triggering a SwitchToThread. This + // unfortunately doesn't consider all runnable threads on all OS SKUs. In + // some cases, it may only consult the runnable threads whose ideal processor + // is the one currently executing code. Thus we occasionally issue a call to + // Sleep(0), which considers all runnable threads at equal priority. Even this + // is insufficient since we may be spin waiting for lower priority threads to + // execute; we therefore must call Sleep(1) once in a while too, which considers + // all runnable threads, regardless of ideal processor and priority, but may + // remove the thread from the scheduler's queue for 10+ms, if the system is + // configured to use the (default) coarse-grained system timer. + // + + if (_count >= sleep1Threshold) + { + RuntimeThread.Sleep(1); + } + else + { + int yieldsSoFar = _count >= YieldThreshold ? (_count - YieldThreshold) / 2 : _count; + if ((yieldsSoFar % Sleep0EveryHowManyYields) == (Sleep0EveryHowManyYields - 1)) + { + RuntimeThread.Sleep(0); + } + else + { + RuntimeThread.Yield(); + } + } + } + else + { + // + // Otherwise, we will spin. + // + // We do this using the CLR's SpinWait API, which is just a busy loop that + // issues YIELD/PAUSE instructions to ensure multi-threaded CPUs can react + // intelligently to avoid starving. (These are NOOPs on other CPUs.) We + // choose a number for the loop iteration count such that each successive + // call spins for longer, to reduce cache contention. We cap the total + // number of spins we are willing to tolerate to reduce delay to the caller, + // since we expect most callers will eventually block anyway. + // + // Also, cap the maximum spin count to a value such that many thousands of CPU cycles would not be wasted doing + // the equivalent of YieldProcessor(), as that that point SwitchToThread/Sleep(0) are more likely to be able to + // allow other useful work to run. Long YieldProcessor() loops can help to reduce contention, but Sleep(1) is + // usually better for that. + // + // RuntimeThread.OptimalMaxSpinWaitsPerSpinIteration: + // - See Thread::InitializeYieldProcessorNormalized(), which describes and calculates this value. + // + int n = RuntimeThread.OptimalMaxSpinWaitsPerSpinIteration; + if (_count <= 30 && (1 << _count) < n) + { + n = 1 << _count; + } + RuntimeThread.SpinWait(n); + } + + // Finally, increment our spin counter. + _count = (_count == int.MaxValue ? YieldThreshold : _count + 1); + } + + /// + /// Resets the spin counter. + /// + /// + /// This makes and behave as though no calls + /// to had been issued on this instance. If a instance + /// is reused many times, it may be useful to reset it to avoid yielding too soon. + /// + public void Reset() + { + _count = 0; + } + + #region Static Methods + /// + /// Spins until the specified condition is satisfied. + /// + /// A delegate to be executed over and over until it returns true. + /// The argument is null. + public static void SpinUntil(Func condition) + { +#if DEBUG + bool result = +#endif + SpinUntil(condition, Timeout.Infinite); +#if DEBUG + Debug.Assert(result); +#endif + } + + /// + /// Spins until the specified condition is satisfied or until the specified timeout is expired. + /// + /// A delegate to be executed over and over until it returns true. + /// + /// A that represents the number of milliseconds to wait, + /// or a TimeSpan that represents -1 milliseconds to wait indefinitely. + /// True if the condition is satisfied within the timeout; otherwise, false + /// The argument is null. + /// is a negative number + /// other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater than + /// . + public static bool SpinUntil(Func condition, TimeSpan timeout) + { + // Validate the timeout + long totalMilliseconds = (long)timeout.TotalMilliseconds; + if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) + { + throw new System.ArgumentOutOfRangeException( + nameof(timeout), timeout, SR.SpinWait_SpinUntil_TimeoutWrong); + } + + // Call wait with the timeout milliseconds + return SpinUntil(condition, (int)totalMilliseconds); + } + + /// + /// Spins until the specified condition is satisfied or until the specified timeout is expired. + /// + /// A delegate to be executed over and over until it returns true. + /// The number of milliseconds to wait, or (-1) to wait indefinitely. + /// True if the condition is satisfied within the timeout; otherwise, false + /// The argument is null. + /// is a + /// negative number other than -1, which represents an infinite time-out. + public static bool SpinUntil(Func condition, int millisecondsTimeout) + { + if (millisecondsTimeout < Timeout.Infinite) + { + throw new ArgumentOutOfRangeException( + nameof(millisecondsTimeout), millisecondsTimeout, SR.SpinWait_SpinUntil_TimeoutWrong); + } + if (condition == null) + { + throw new ArgumentNullException(nameof(condition), SR.SpinWait_SpinUntil_ArgumentNull); + } + uint startTime = 0; + if (millisecondsTimeout != 0 && millisecondsTimeout != Timeout.Infinite) + { + startTime = TimeoutHelper.GetTime(); + } + SpinWait spinner = new SpinWait(); + while (!condition()) + { + if (millisecondsTimeout == 0) + { + return false; + } + + spinner.SpinOnce(); + + if (millisecondsTimeout != Timeout.Infinite && spinner.NextSpinWillYield) + { + if (millisecondsTimeout <= (TimeoutHelper.GetTime() - startTime)) + { + return false; + } + } + } + return true; + } + #endregion + } + + /// + /// A helper class to get the number of processors, it updates the numbers of processors every sampling interval. + /// + internal static class PlatformHelper + { + private const int PROCESSOR_COUNT_REFRESH_INTERVAL_MS = 30000; // How often to refresh the count, in milliseconds. + private static volatile int s_processorCount; // The last count seen. + private static volatile int s_lastProcessorCountRefreshTicks; // The last time we refreshed. + + /// + /// Gets the number of available processors + /// + internal static int ProcessorCount + { + get + { + int now = Environment.TickCount; + int procCount = s_processorCount; + if (procCount == 0 || (now - s_lastProcessorCountRefreshTicks) >= PROCESSOR_COUNT_REFRESH_INTERVAL_MS) + { + s_processorCount = procCount = Environment.ProcessorCount; + s_lastProcessorCountRefreshTicks = now; + } + + Debug.Assert(procCount > 0, + "Processor count should be greater than 0."); + + return procCount; + } + } + + /// + /// Gets whether the current machine has only a single processor. + /// + /// This typically does not change on a machine, so it's checked only once. + internal static readonly bool IsSingleProcessor = ProcessorCount == 1; + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Threading/SynchronizationLockException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Threading/SynchronizationLockException.cs index c64fc9ced8..e050e1539d 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Threading/SynchronizationLockException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Threading/SynchronizationLockException.cs @@ -16,29 +16,30 @@ using System.Runtime.Serialization; namespace System.Threading { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class SynchronizationLockException : SystemException { public SynchronizationLockException() : base(SR.Arg_SynchronizationLockException) { - HResult = __HResults.COR_E_SYNCHRONIZATIONLOCK; + HResult = HResults.COR_E_SYNCHRONIZATIONLOCK; } public SynchronizationLockException(String message) : base(message) { - HResult = __HResults.COR_E_SYNCHRONIZATIONLOCK; + HResult = HResults.COR_E_SYNCHRONIZATIONLOCK; } public SynchronizationLockException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_SYNCHRONIZATIONLOCK; + HResult = HResults.COR_E_SYNCHRONIZATIONLOCK; } protected SynchronizationLockException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Threading/Tasks/TaskCanceledException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Threading/Tasks/TaskCanceledException.cs index 6657bcd36c..a6ec030452 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Threading/Tasks/TaskCanceledException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Threading/Tasks/TaskCanceledException.cs @@ -19,10 +19,12 @@ namespace System.Threading.Tasks /// /// Represents an exception used to communicate task cancellation. /// + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class TaskCanceledException : OperationCanceledException { [NonSerialized] - private Task m_canceledTask; // The task which has been canceled. + private readonly Task _canceledTask; // The task which has been canceled. /// /// Initializes a new instance of the class. @@ -59,7 +61,7 @@ namespace System.Threading.Tasks public TaskCanceledException(Task task) : base(SR.TaskCanceledException_ctor_DefaultMessage, task != null ? task.CancellationToken : new CancellationToken()) { - m_canceledTask = task; + _canceledTask = task; } /// @@ -70,7 +72,6 @@ namespace System.Threading.Tasks /// The that contains contextual information about the source or destination. protected TaskCanceledException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } /// @@ -81,9 +82,6 @@ namespace System.Threading.Tasks /// , in which case /// this property will return null. /// - public Task Task - { - get { return m_canceledTask; } - } + public Task Task => _canceledTask; } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Threading/Tasks/TaskCompletionSource.cs b/external/corert/src/System.Private.CoreLib/shared/System/Threading/Tasks/TaskCompletionSource.cs new file mode 100644 index 0000000000..992e9db767 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Threading/Tasks/TaskCompletionSource.cs @@ -0,0 +1,349 @@ +// 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. + +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// +// +// TaskCompletionSource is the producer end of an unbound future. Its +// Task member may be distributed as the consumer end of the future. +// +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +using System; +using System.Diagnostics; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.ExceptionServices; +using System.Threading; + +// Disable the "reference to volatile field not treated as volatile" error. +#pragma warning disable 0420 + +namespace System.Threading.Tasks +{ + /// + /// Represents the producer side of a unbound to a + /// delegate, providing access to the consumer side through the property. + /// + /// + /// + /// It is often the case that a is desired to + /// represent another asynchronous operation. + /// TaskCompletionSource is provided for this purpose. It enables + /// the creation of a task that can be handed out to consumers, and those consumers can use the members + /// of the task as they would any other. However, unlike most tasks, the state of a task created by a + /// TaskCompletionSource is controlled explicitly by the methods on TaskCompletionSource. This enables the + /// completion of the external asynchronous operation to be propagated to the underlying Task. The + /// separation also ensures that consumers are not able to transition the state without access to the + /// corresponding TaskCompletionSource. + /// + /// + /// All members of are thread-safe + /// and may be used from multiple threads concurrently. + /// + /// + /// The type of the result value associated with this . + public class TaskCompletionSource + { + private readonly Task _task; + + /// + /// Creates a . + /// + public TaskCompletionSource() + { + _task = new Task(); + } + + /// + /// Creates a + /// with the specified options. + /// + /// + /// The created + /// by this instance and accessible through its property + /// will be instantiated using the specified . + /// + /// The options to use when creating the underlying + /// . + /// + /// The represent options invalid for use + /// with a . + /// + public TaskCompletionSource(TaskCreationOptions creationOptions) + : this(null, creationOptions) + { + } + + /// + /// Creates a + /// with the specified state. + /// + /// The state to use as the underlying + /// 's AsyncState. + public TaskCompletionSource(object state) + : this(state, TaskCreationOptions.None) + { + } + + /// + /// Creates a with + /// the specified state and options. + /// + /// The options to use when creating the underlying + /// . + /// The state to use as the underlying + /// 's AsyncState. + /// + /// The represent options invalid for use + /// with a . + /// + public TaskCompletionSource(object state, TaskCreationOptions creationOptions) + { + _task = new Task(state, creationOptions); + } + + + /// + /// Gets the created + /// by this . + /// + /// + /// This property enables a consumer access to the that is controlled by this instance. + /// The , , + /// , and + /// methods (and their "Try" variants) on this instance all result in the relevant state + /// transitions on this underlying Task. + /// + public Task Task => _task; + + /// Spins until the underlying task is completed. + /// This should only be called if the task is in the process of being completed by another thread. + private void SpinUntilCompleted() + { + // Spin wait until the completion is finalized by another thread. + var sw = new SpinWait(); + while (!_task.IsCompleted) + sw.SpinOnce(); + } + + /// + /// Attempts to transition the underlying + /// into the + /// Faulted + /// state. + /// + /// The exception to bind to this . + /// True if the operation was successful; otherwise, false. + /// This operation will return false if the + /// is already in one + /// of the three final states: + /// RanToCompletion, + /// Faulted, or + /// Canceled. + /// + /// The argument is null. + /// The was disposed. + public bool TrySetException(Exception exception) + { + if (exception == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exception); + + bool rval = _task.TrySetException(exception); + if (!rval && !_task.IsCompleted) SpinUntilCompleted(); + return rval; + } + + /// + /// Attempts to transition the underlying + /// into the + /// Faulted + /// state. + /// + /// The collection of exceptions to bind to this . + /// True if the operation was successful; otherwise, false. + /// This operation will return false if the + /// is already in one + /// of the three final states: + /// RanToCompletion, + /// Faulted, or + /// Canceled. + /// + /// The argument is null. + /// There are one or more null elements in . + /// The collection is empty. + /// The was disposed. + public bool TrySetException(IEnumerable exceptions) + { + if (exceptions == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exceptions); + + List defensiveCopy = new List(); + foreach (Exception e in exceptions) + { + if (e == null) + ThrowHelper.ThrowArgumentException(ExceptionResource.TaskCompletionSourceT_TrySetException_NullException, ExceptionArgument.exceptions); + defensiveCopy.Add(e); + } + + if (defensiveCopy.Count == 0) + ThrowHelper.ThrowArgumentException(ExceptionResource.TaskCompletionSourceT_TrySetException_NoExceptions, ExceptionArgument.exceptions); + + bool rval = _task.TrySetException(defensiveCopy); + if (!rval && !_task.IsCompleted) SpinUntilCompleted(); + return rval; + } + + /// + /// Transitions the underlying + /// into the + /// Faulted + /// state. + /// + /// The exception to bind to this . + /// The argument is null. + /// + /// The underlying is already in one + /// of the three final states: + /// RanToCompletion, + /// Faulted, or + /// Canceled. + /// + /// The was disposed. + public void SetException(Exception exception) + { + if (exception == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exception); + + if (!TrySetException(exception)) + { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted); + } + } + + /// + /// Transitions the underlying + /// into the + /// Faulted + /// state. + /// + /// The collection of exceptions to bind to this . + /// The argument is null. + /// There are one or more null elements in . + /// + /// The underlying is already in one + /// of the three final states: + /// RanToCompletion, + /// Faulted, or + /// Canceled. + /// + /// The was disposed. + public void SetException(IEnumerable exceptions) + { + if (!TrySetException(exceptions)) + { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted); + } + } + + + /// + /// Attempts to transition the underlying + /// into the + /// RanToCompletion + /// state. + /// + /// The result value to bind to this . + /// True if the operation was successful; otherwise, false. + /// This operation will return false if the + /// is already in one + /// of the three final states: + /// RanToCompletion, + /// Faulted, or + /// Canceled. + /// + /// The was disposed. + public bool TrySetResult(TResult result) + { + bool rval = _task.TrySetResult(result); + if (!rval) SpinUntilCompleted(); + return rval; + } + + /// + /// Transitions the underlying + /// into the + /// RanToCompletion + /// state. + /// + /// The result value to bind to this . + /// + /// The underlying is already in one + /// of the three final states: + /// RanToCompletion, + /// Faulted, or + /// Canceled. + /// + /// The was disposed. + public void SetResult(TResult result) + { + if (!TrySetResult(result)) + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted); + } + + /// + /// Attempts to transition the underlying + /// into the + /// Canceled + /// state. + /// + /// True if the operation was successful; otherwise, false. + /// This operation will return false if the + /// is already in one + /// of the three final states: + /// RanToCompletion, + /// Faulted, or + /// Canceled. + /// + /// The was disposed. + public bool TrySetCanceled() + { + return TrySetCanceled(default(CancellationToken)); + } + + // Enables a token to be stored into the canceled task + public bool TrySetCanceled(CancellationToken cancellationToken) + { + bool rval = _task.TrySetCanceled(cancellationToken); + if (!rval && !_task.IsCompleted) SpinUntilCompleted(); + return rval; + } + + /// + /// Transitions the underlying + /// into the + /// Canceled + /// state. + /// + /// + /// The underlying is already in one + /// of the three final states: + /// RanToCompletion, + /// Faulted, or + /// Canceled. + /// + /// The was disposed. + public void SetCanceled() + { + if (!TrySetCanceled()) + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Threading/Tasks/TaskSchedulerException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Threading/Tasks/TaskSchedulerException.cs index 2888415d0d..85ec497219 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Threading/Tasks/TaskSchedulerException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Threading/Tasks/TaskSchedulerException.cs @@ -20,6 +20,8 @@ namespace System.Threading.Tasks /// Represents an exception used to communicate an invalid operation by a /// . /// + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class TaskSchedulerException : Exception { /// @@ -71,7 +73,6 @@ namespace System.Threading.Tasks protected TaskSchedulerException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Threading/Tasks/TaskToApm.cs b/external/corert/src/System.Private.CoreLib/shared/System/Threading/Tasks/TaskToApm.cs new file mode 100644 index 0000000000..add41f588e --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Threading/Tasks/TaskToApm.cs @@ -0,0 +1,189 @@ +// 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. + +// Helper methods for using Tasks to implement the APM pattern. +// +// Example usage, wrapping a Task-returning FooAsync method with Begin/EndFoo methods: +// +// public IAsyncResult BeginFoo(..., AsyncCallback callback, object state) +// { +// Task t = FooAsync(...); +// return TaskToApm.Begin(t, callback, state); +// } +// public int EndFoo(IAsyncResult asyncResult) +// { +// return TaskToApm.End(asyncResult); +// } + +using System.Diagnostics; + +namespace System.Threading.Tasks +{ + /// + /// Provides support for efficiently using Tasks to implement the APM (Begin/End) pattern. + /// + internal static class TaskToApm + { + /// + /// Marshals the Task as an IAsyncResult, using the supplied callback and state + /// to implement the APM pattern. + /// + /// The Task to be marshaled. + /// The callback to be invoked upon completion. + /// The state to be stored in the IAsyncResult. + /// An IAsyncResult to represent the task's asynchronous operation. + public static IAsyncResult Begin(Task task, AsyncCallback callback, object state) + { + Debug.Assert(task != null); + + // If the task has already completed, then since the Task's CompletedSynchronously==false + // and we want it to be true, we need to create a new IAsyncResult. (We also need the AsyncState to match.) + IAsyncResult asyncResult; + if (task.IsCompleted) + { + // Synchronous completion. + asyncResult = new TaskWrapperAsyncResult(task, state, completedSynchronously: true); + callback?.Invoke(asyncResult); + } + else + { + // For asynchronous completion we need to schedule a callback. Whether we can use the Task as the IAsyncResult + // depends on whether the Task's AsyncState has reference equality with the requested state. + asyncResult = task.AsyncState == state ? (IAsyncResult)task : new TaskWrapperAsyncResult(task, state, completedSynchronously: false); + if (callback != null) + { + InvokeCallbackWhenTaskCompletes(task, callback, asyncResult); + } + } + return asyncResult; + } + + /// Processes an IAsyncResult returned by Begin. + /// The IAsyncResult to unwrap. + public static void End(IAsyncResult asyncResult) + { + Task task; + + // If the IAsyncResult is our task-wrapping IAsyncResult, extract the Task. + var twar = asyncResult as TaskWrapperAsyncResult; + if (twar != null) + { + task = twar.Task; + Debug.Assert(task != null, "TaskWrapperAsyncResult should never wrap a null Task."); + } + else + { + // Otherwise, the IAsyncResult should be a Task. + task = asyncResult as Task; + } + + // Make sure we actually got a task, then complete the operation by waiting on it. + if (task == null) + { + throw new ArgumentNullException(); + } + + task.GetAwaiter().GetResult(); + } + + /// Processes an IAsyncResult returned by Begin. + /// The IAsyncResult to unwrap. + public static TResult End(IAsyncResult asyncResult) + { + Task task; + + // If the IAsyncResult is our task-wrapping IAsyncResult, extract the Task. + var twar = asyncResult as TaskWrapperAsyncResult; + if (twar != null) + { + task = twar.Task as Task; + Debug.Assert(twar.Task != null, "TaskWrapperAsyncResult should never wrap a null Task."); + } + else + { + // Otherwise, the IAsyncResult should be a Task. + task = asyncResult as Task; + } + + // Make sure we actually got a task, then complete the operation by waiting on it. + if (task == null) + { + throw new ArgumentNullException(); + } + + return task.GetAwaiter().GetResult(); + } + + /// Invokes the callback asynchronously when the task has completed. + /// The Task to await. + /// The callback to invoke when the Task completes. + /// The Task used as the IAsyncResult. + private static void InvokeCallbackWhenTaskCompletes(Task antecedent, AsyncCallback callback, IAsyncResult asyncResult) + { + Debug.Assert(antecedent != null); + Debug.Assert(callback != null); + Debug.Assert(asyncResult != null); + + // We use OnCompleted rather than ContinueWith in order to avoid running synchronously + // if the task has already completed by the time we get here. This is separated out into + // its own method currently so that we only pay for the closure if necessary. + antecedent.ConfigureAwait(continueOnCapturedContext: false) + .GetAwaiter() + .OnCompleted(() => callback(asyncResult)); + + // PERFORMANCE NOTE: + // Assuming we're in the default ExecutionContext, the "slow path" of an incomplete + // task will result in four allocations: the new IAsyncResult, the delegate+closure + // in this method, and the continuation object inside of OnCompleted (necessary + // to capture both the Action delegate and the ExecutionContext in a single object). + // In the future, if performance requirements drove a need, those four + // allocations could be reduced to one. This would be achieved by having TaskWrapperAsyncResult + // also implement ITaskCompletionAction (and optionally IThreadPoolWorkItem). It would need + // additional fields to store the AsyncCallback and an ExecutionContext. Once configured, + // it would be set into the Task as a continuation. Its Invoke method would then be run when + // the antecedent completed, and, doing all of the necessary work to flow ExecutionContext, + // it would invoke the AsyncCallback. It could also have a field on it for the antecedent, + // so that the End method would have access to the completed antecedent. For related examples, + // see other implementations of ITaskCompletionAction, and in particular ReadWriteTask + // used in Stream.Begin/EndXx's implementation. + } + + /// + /// Provides a simple IAsyncResult that wraps a Task. This, in effect, allows + /// for overriding what's seen for the CompletedSynchronously and AsyncState values. + /// + private sealed class TaskWrapperAsyncResult : IAsyncResult + { + /// The wrapped Task. + internal readonly Task Task; + /// The new AsyncState value. + private readonly object _state; + /// The new CompletedSynchronously value. + private readonly bool _completedSynchronously; + + /// Initializes the IAsyncResult with the Task to wrap and the overriding AsyncState and CompletedSynchronously values. + /// The Task to wrap. + /// The new AsyncState value + /// The new CompletedSynchronously value. + internal TaskWrapperAsyncResult(Task task, object state, bool completedSynchronously) + { + Debug.Assert(task != null); + Debug.Assert(!completedSynchronously || task.IsCompleted, "If completedSynchronously is true, the task must be completed."); + + this.Task = task; + _state = state; + _completedSynchronously = completedSynchronously; + } + + // The IAsyncResult implementation. + // - IsCompleted and AsyncWaitHandle just pass through to the Task. + // - AsyncState and CompletedSynchronously return the corresponding values stored in this object. + + object IAsyncResult.AsyncState { get { return _state; } } + bool IAsyncResult.CompletedSynchronously { get { return _completedSynchronously; } } + bool IAsyncResult.IsCompleted { get { return this.Task.IsCompleted; } } + WaitHandle IAsyncResult.AsyncWaitHandle { get { return ((IAsyncResult)this.Task).AsyncWaitHandle; } } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs b/external/corert/src/System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs new file mode 100644 index 0000000000..de9b016328 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs @@ -0,0 +1,178 @@ +// 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.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.Threading.Tasks +{ + /// + /// Provides a value type that wraps a and a , + /// only one of which is used. + /// + /// The type of the result. + /// + /// + /// Methods may return an instance of this value type when it's likely that the result of their + /// operations will be available synchronously and when the method is expected to be invoked so + /// frequently that the cost of allocating a new for each call will + /// be prohibitive. + /// + /// + /// There are tradeoffs to using a instead of a . + /// For example, while a can help avoid an allocation in the case where the + /// successful result is available synchronously, it also contains two fields whereas a + /// as a reference type is a single field. This means that a method call ends up returning two fields worth of + /// data instead of one, which is more data to copy. It also means that if a method that returns one of these + /// is awaited within an async method, the state machine for that async method will be larger due to needing + /// to store the struct that's two fields instead of a single reference. + /// + /// + /// Further, for uses other than consuming the result of an asynchronous operation via await, + /// can lead to a more convoluted programming model, which can in turn actually + /// lead to more allocations. For example, consider a method that could return either a + /// with a cached task as a common result or a . If the consumer of the result + /// wants to use it as a , such as to use with in methods like Task.WhenAll and Task.WhenAny, + /// the would first need to be converted into a using + /// , which leads to an allocation that would have been avoided if a cached + /// had been used in the first place. + /// + /// + /// As such, the default choice for any asynchronous method should be to return a or + /// . Only if performance analysis proves it worthwhile should a + /// be used instead of . There is no non-generic version of + /// as the Task.CompletedTask property may be used to hand back a successfully completed singleton in the case where + /// a -returning method completes synchronously and successfully. + /// + /// + [AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder<>))] + [StructLayout(LayoutKind.Auto)] + public readonly struct ValueTask : IEquatable> + { + /// The task to be used if the operation completed asynchronously or if it completed synchronously but non-successfully. + internal readonly Task _task; + /// The result to be used if the operation completed successfully synchronously. + internal readonly TResult _result; + + /// Initialize the with the result of the successful operation. + /// The result. + public ValueTask(TResult result) + { + _task = null; + _result = result; + } + + /// + /// Initialize the with a that represents the operation. + /// + /// The task. + public ValueTask(Task task) + { + if (task == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.task); + } + + _task = task; + _result = default(TResult); + } + + /// Returns the hash code for this instance. + public override int GetHashCode() => + _task != null ? _task.GetHashCode() : + _result != null ? _result.GetHashCode() : + 0; + + /// Returns a value indicating whether this value is equal to a specified . + public override bool Equals(object obj) => + obj is ValueTask && + Equals((ValueTask)obj); + + /// Returns a value indicating whether this value is equal to a specified value. + public bool Equals(ValueTask other) => + _task != null || other._task != null ? + _task == other._task : + EqualityComparer.Default.Equals(_result, other._result); + + /// Returns a value indicating whether two values are equal. + public static bool operator==(ValueTask left, ValueTask right) => + left.Equals(right); + + /// Returns a value indicating whether two values are not equal. + public static bool operator!=(ValueTask left, ValueTask right) => + !left.Equals(right); + + /// + /// Gets a object to represent this ValueTask. It will + /// either return the wrapped task object if one exists, or it'll manufacture a new + /// task object to represent the result. + /// + public Task AsTask() => + // Return the task if we were constructed from one, otherwise manufacture one. We don't + // cache the generated task into _task as it would end up changing both equality comparison + // and the hash code we generate in GetHashCode. + _task ?? AsyncTaskMethodBuilder.GetTaskForResult(_result); + + internal Task AsTaskExpectNonNull() => + // Return the task if we were constructed from one, otherwise manufacture one. + // Unlike AsTask(), this method is called only when we expect _task to be non-null, + // and thus we don't want GetTaskForResult inlined. + _task ?? GetTaskForResultNoInlining(); + + [MethodImpl(MethodImplOptions.NoInlining)] + private Task GetTaskForResultNoInlining() => AsyncTaskMethodBuilder.GetTaskForResult(_result); + + /// Gets whether the represents a completed operation. + public bool IsCompleted => _task == null || _task.IsCompleted; + + /// Gets whether the represents a successfully completed operation. + public bool IsCompletedSuccessfully => _task == null || _task.IsCompletedSuccessfully; + + /// Gets whether the represents a failed operation. + public bool IsFaulted => _task != null && _task.IsFaulted; + + /// Gets whether the represents a canceled operation. + public bool IsCanceled => _task != null && _task.IsCanceled; + + /// Gets the result. + public TResult Result => _task == null ? _result : _task.GetAwaiter().GetResult(); + + /// Gets an awaiter for this value. + public ValueTaskAwaiter GetAwaiter() => new ValueTaskAwaiter(this); + + /// Configures an awaiter for this value. + /// + /// true to attempt to marshal the continuation back to the captured context; otherwise, false. + /// + public ConfiguredValueTaskAwaitable ConfigureAwait(bool continueOnCapturedContext) => + new ConfiguredValueTaskAwaitable(this, continueOnCapturedContext); + + /// Gets a string-representation of this . + public override string ToString() + { + if (_task != null) + { + return _task.IsCompletedSuccessfully && _task.Result != null ? + _task.Result.ToString() : + string.Empty; + } + else + { + return _result != null ? + _result.ToString() : + string.Empty; + } + } + + // TODO https://github.com/dotnet/corefx/issues/22171: + // Remove CreateAsyncMethodBuilder once the C# compiler relies on the AsyncBuilder attribute. + + /// Creates a method builder for use with an async method. + /// The created builder. + [EditorBrowsable(EditorBrowsableState.Never)] // intended only for compiler consumption + public static AsyncValueTaskMethodBuilder CreateAsyncMethodBuilder() => AsyncValueTaskMethodBuilder.Create(); + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Threading/ThreadAbortException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Threading/ThreadAbortException.cs index 5773f2726a..360e84d256 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Threading/ThreadAbortException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Threading/ThreadAbortException.cs @@ -18,13 +18,20 @@ using System.Runtime.Serialization; namespace System.Threading { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class ThreadAbortException : SystemException { - private ThreadAbortException() + internal ThreadAbortException() { - HResult = __HResults.COR_E_THREADABORTED; + HResult = HResults.COR_E_THREADABORTED; } public object ExceptionState => null; + + internal ThreadAbortException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Threading/ThreadStartException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Threading/ThreadStartException.cs index 7a87943ed1..5172555418 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Threading/ThreadStartException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Threading/ThreadStartException.cs @@ -6,18 +6,25 @@ using System.Runtime.Serialization; namespace System.Threading { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class ThreadStartException : SystemException { internal ThreadStartException() : base(SR.Arg_ThreadStartException) { - HResult = __HResults.COR_E_THREADSTART; + HResult = HResults.COR_E_THREADSTART; } internal ThreadStartException(Exception reason) : base(SR.Arg_ThreadStartException, reason) { - HResult = __HResults.COR_E_THREADSTART; + HResult = HResults.COR_E_THREADSTART; + } + + private ThreadStartException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Threading/ThreadStateException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Threading/ThreadStateException.cs index 9477cb1ae4..6fc06a8516 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Threading/ThreadStateException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Threading/ThreadStateException.cs @@ -16,30 +16,31 @@ using System.Runtime.Serialization; namespace System.Threading { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class ThreadStateException : SystemException { public ThreadStateException() : base(SR.Arg_ThreadStateException) { - HResult = __HResults.COR_E_THREADSTATE; + HResult = HResults.COR_E_THREADSTATE; } public ThreadStateException(String message) : base(message) { - HResult = __HResults.COR_E_THREADSTATE; + HResult = HResults.COR_E_THREADSTATE; } public ThreadStateException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_THREADSTATE; + HResult = HResults.COR_E_THREADSTATE; } protected ThreadStateException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Threading/TimeoutHelper.cs b/external/corert/src/System.Private.CoreLib/shared/System/Threading/TimeoutHelper.cs index c66c9add92..c96a4d00d6 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Threading/TimeoutHelper.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Threading/TimeoutHelper.cs @@ -13,7 +13,7 @@ namespace System.Threading internal static class TimeoutHelper { /// - /// Returns the Environment.TickCount as a start time in milliseconds as a uint, TickCount tools over from postive to negative every ~ 25 days + /// Returns the Environment.TickCount as a start time in milliseconds as a uint, TickCount tools over from positive to negative every ~ 25 days /// then ~25 days to back to positive again, uint is sued to ignore the sign and double the range to 50 days /// /// @@ -26,7 +26,7 @@ namespace System.Threading /// Helper function to measure and update the elapsed time /// /// The first time (in milliseconds) observed when the wait started - /// The orginal wait timeoutout in milliseconds + /// The original wait timeout in milliseconds /// The new wait time in milliseconds, -1 if the time expired public static int UpdateTimeOut(uint startTime, int originalWaitMillisecondsTimeout) { diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Threading/WaitHandleCannotBeOpenedException.cs b/external/corert/src/System.Private.CoreLib/shared/System/Threading/WaitHandleCannotBeOpenedException.cs index 770e70d7ab..e60e46d2be 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Threading/WaitHandleCannotBeOpenedException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Threading/WaitHandleCannotBeOpenedException.cs @@ -6,26 +6,27 @@ using System.Runtime.Serialization; namespace System.Threading { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class WaitHandleCannotBeOpenedException : ApplicationException { public WaitHandleCannotBeOpenedException() : base(SR.Threading_WaitHandleCannotBeOpenedException) { - HResult = __HResults.COR_E_WAITHANDLECANNOTBEOPENED; + HResult = HResults.COR_E_WAITHANDLECANNOTBEOPENED; } public WaitHandleCannotBeOpenedException(String message) : base(message) { - HResult = __HResults.COR_E_WAITHANDLECANNOTBEOPENED; + HResult = HResults.COR_E_WAITHANDLECANNOTBEOPENED; } public WaitHandleCannotBeOpenedException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_WAITHANDLECANNOTBEOPENED; + HResult = HResults.COR_E_WAITHANDLECANNOTBEOPENED; } protected WaitHandleCannotBeOpenedException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/TimeSpan.cs b/external/corert/src/System.Private.CoreLib/shared/System/TimeSpan.cs new file mode 100644 index 0000000000..4293ed505b --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/TimeSpan.cs @@ -0,0 +1,564 @@ +// 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.Text; +using System; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Globalization; + +namespace System +{ + // TimeSpan represents a duration of time. A TimeSpan can be negative + // or positive. + // + // TimeSpan is internally represented as a number of milliseconds. While + // this maps well into units of time such as hours and days, any + // periods longer than that aren't representable in a nice fashion. + // For instance, a month can be between 28 and 31 days, while a year + // can contain 365 or 364 days. A decade can have between 1 and 3 leapyears, + // depending on when you map the TimeSpan into the calendar. This is why + // we do not provide Years() or Months(). + // + // Note: System.TimeSpan needs to interop with the WinRT structure + // type Windows::Foundation:TimeSpan. These types are currently binary-compatible in + // memory so no custom marshalling is required. If at any point the implementation + // details of this type should change, or new fields added, we need to remember to add + // an appropriate custom ILMarshaler to keep WInRT interop scenarios enabled. + // + [Serializable] + public struct TimeSpan : IComparable, IComparable, IEquatable, IFormattable, ISpanFormattable + { + public const long TicksPerMillisecond = 10000; + private const double MillisecondsPerTick = 1.0 / TicksPerMillisecond; + + public const long TicksPerSecond = TicksPerMillisecond * 1000; // 10,000,000 + private const double SecondsPerTick = 1.0 / TicksPerSecond; // 0.0001 + + public const long TicksPerMinute = TicksPerSecond * 60; // 600,000,000 + private const double MinutesPerTick = 1.0 / TicksPerMinute; // 1.6666666666667e-9 + + public const long TicksPerHour = TicksPerMinute * 60; // 36,000,000,000 + private const double HoursPerTick = 1.0 / TicksPerHour; // 2.77777777777777778e-11 + + public const long TicksPerDay = TicksPerHour * 24; // 864,000,000,000 + private const double DaysPerTick = 1.0 / TicksPerDay; // 1.1574074074074074074e-12 + + private const int MillisPerSecond = 1000; + private const int MillisPerMinute = MillisPerSecond * 60; // 60,000 + private const int MillisPerHour = MillisPerMinute * 60; // 3,600,000 + private const int MillisPerDay = MillisPerHour * 24; // 86,400,000 + + internal const long MaxSeconds = Int64.MaxValue / TicksPerSecond; + internal const long MinSeconds = Int64.MinValue / TicksPerSecond; + + internal const long MaxMilliSeconds = Int64.MaxValue / TicksPerMillisecond; + internal const long MinMilliSeconds = Int64.MinValue / TicksPerMillisecond; + + internal const long TicksPerTenthSecond = TicksPerMillisecond * 100; + + public static readonly TimeSpan Zero = new TimeSpan(0); + + public static readonly TimeSpan MaxValue = new TimeSpan(Int64.MaxValue); + public static readonly TimeSpan MinValue = new TimeSpan(Int64.MinValue); + + // internal so that DateTime doesn't have to call an extra get + // method for some arithmetic operations. + internal long _ticks; // Do not rename (binary serialization) + + public TimeSpan(long ticks) + { + this._ticks = ticks; + } + + public TimeSpan(int hours, int minutes, int seconds) + { + _ticks = TimeToTicks(hours, minutes, seconds); + } + + public TimeSpan(int days, int hours, int minutes, int seconds) + : this(days, hours, minutes, seconds, 0) + { + } + + public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds) + { + Int64 totalMilliSeconds = ((Int64)days * 3600 * 24 + (Int64)hours * 3600 + (Int64)minutes * 60 + seconds) * 1000 + milliseconds; + if (totalMilliSeconds > MaxMilliSeconds || totalMilliSeconds < MinMilliSeconds) + throw new ArgumentOutOfRangeException(null, SR.Overflow_TimeSpanTooLong); + _ticks = (long)totalMilliSeconds * TicksPerMillisecond; + } + + public long Ticks + { + get { return _ticks; } + } + + public int Days + { + get { return (int)(_ticks / TicksPerDay); } + } + + public int Hours + { + get { return (int)((_ticks / TicksPerHour) % 24); } + } + + public int Milliseconds + { + get { return (int)((_ticks / TicksPerMillisecond) % 1000); } + } + + public int Minutes + { + get { return (int)((_ticks / TicksPerMinute) % 60); } + } + + public int Seconds + { + get { return (int)((_ticks / TicksPerSecond) % 60); } + } + + public double TotalDays + { + get { return ((double)_ticks) * DaysPerTick; } + } + + public double TotalHours + { + get { return (double)_ticks * HoursPerTick; } + } + + public double TotalMilliseconds + { + get + { + double temp = (double)_ticks * MillisecondsPerTick; + if (temp > MaxMilliSeconds) + return (double)MaxMilliSeconds; + + if (temp < MinMilliSeconds) + return (double)MinMilliSeconds; + + return temp; + } + } + + public double TotalMinutes + { + get { return (double)_ticks * MinutesPerTick; } + } + + public double TotalSeconds + { + get { return (double)_ticks * SecondsPerTick; } + } + + public TimeSpan Add(TimeSpan ts) + { + long result = _ticks + ts._ticks; + // Overflow if signs of operands was identical and result's + // sign was opposite. + // >> 63 gives the sign bit (either 64 1's or 64 0's). + if ((_ticks >> 63 == ts._ticks >> 63) && (_ticks >> 63 != result >> 63)) + throw new OverflowException(SR.Overflow_TimeSpanTooLong); + return new TimeSpan(result); + } + + + // Compares two TimeSpan values, returning an integer that indicates their + // relationship. + // + public static int Compare(TimeSpan t1, TimeSpan t2) + { + if (t1._ticks > t2._ticks) return 1; + if (t1._ticks < t2._ticks) return -1; + return 0; + } + + // Returns a value less than zero if this object + public int CompareTo(Object value) + { + if (value == null) return 1; + if (!(value is TimeSpan)) + throw new ArgumentException(SR.Arg_MustBeTimeSpan); + long t = ((TimeSpan)value)._ticks; + if (_ticks > t) return 1; + if (_ticks < t) return -1; + return 0; + } + + public int CompareTo(TimeSpan value) + { + long t = value._ticks; + if (_ticks > t) return 1; + if (_ticks < t) return -1; + return 0; + } + + public static TimeSpan FromDays(double value) + { + return Interval(value, MillisPerDay); + } + + public TimeSpan Duration() + { + if (Ticks == TimeSpan.MinValue.Ticks) + throw new OverflowException(SR.Overflow_Duration); + return new TimeSpan(_ticks >= 0 ? _ticks : -_ticks); + } + + public override bool Equals(Object value) + { + if (value is TimeSpan) + { + return _ticks == ((TimeSpan)value)._ticks; + } + return false; + } + + public bool Equals(TimeSpan obj) + { + return _ticks == obj._ticks; + } + + public static bool Equals(TimeSpan t1, TimeSpan t2) + { + return t1._ticks == t2._ticks; + } + + public override int GetHashCode() + { + return (int)_ticks ^ (int)(_ticks >> 32); + } + + public static TimeSpan FromHours(double value) + { + return Interval(value, MillisPerHour); + } + + private static TimeSpan Interval(double value, int scale) + { + if (Double.IsNaN(value)) + throw new ArgumentException(SR.Arg_CannotBeNaN); + double tmp = value * scale; + double millis = tmp + (value >= 0 ? 0.5 : -0.5); + if ((millis > Int64.MaxValue / TicksPerMillisecond) || (millis < Int64.MinValue / TicksPerMillisecond)) + throw new OverflowException(SR.Overflow_TimeSpanTooLong); + return new TimeSpan((long)millis * TicksPerMillisecond); + } + + public static TimeSpan FromMilliseconds(double value) + { + return Interval(value, 1); + } + + public static TimeSpan FromMinutes(double value) + { + return Interval(value, MillisPerMinute); + } + + public TimeSpan Negate() + { + if (Ticks == TimeSpan.MinValue.Ticks) + throw new OverflowException(SR.Overflow_NegateTwosCompNum); + return new TimeSpan(-_ticks); + } + + public static TimeSpan FromSeconds(double value) + { + return Interval(value, MillisPerSecond); + } + + public TimeSpan Subtract(TimeSpan ts) + { + long result = _ticks - ts._ticks; + // Overflow if signs of operands was different and result's + // sign was opposite from the first argument's sign. + // >> 63 gives the sign bit (either 64 1's or 64 0's). + if ((_ticks >> 63 != ts._ticks >> 63) && (_ticks >> 63 != result >> 63)) + throw new OverflowException(SR.Overflow_TimeSpanTooLong); + return new TimeSpan(result); + } + + public TimeSpan Multiply(double factor) => this * factor; + + public TimeSpan Divide(double divisor) => this / divisor; + + public double Divide(TimeSpan ts) => this / ts; + + public static TimeSpan FromTicks(long value) + { + return new TimeSpan(value); + } + + internal static long TimeToTicks(int hour, int minute, int second) + { + // totalSeconds is bounded by 2^31 * 2^12 + 2^31 * 2^8 + 2^31, + // which is less than 2^44, meaning we won't overflow totalSeconds. + long totalSeconds = (long)hour * 3600 + (long)minute * 60 + (long)second; + if (totalSeconds > MaxSeconds || totalSeconds < MinSeconds) + throw new ArgumentOutOfRangeException(null, SR.Overflow_TimeSpanTooLong); + return totalSeconds * TicksPerSecond; + } + + // See System.Globalization.TimeSpanParse and System.Globalization.TimeSpanFormat + #region ParseAndFormat + private static void ValidateStyles(TimeSpanStyles style, String parameterName) + { + if (style != TimeSpanStyles.None && style != TimeSpanStyles.AssumeNegative) + throw new ArgumentException(SR.Argument_InvalidTimeSpanStyles, parameterName); + } + public static TimeSpan Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + /* Constructs a TimeSpan from a string. Leading and trailing white space characters are allowed. */ + return TimeSpanParse.Parse(s, null); + } + public static TimeSpan Parse(String input, IFormatProvider formatProvider) + { + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + return TimeSpanParse.Parse(input, formatProvider); + } + public static TimeSpan Parse(ReadOnlySpan input, IFormatProvider formatProvider = null) + { + return TimeSpanParse.Parse(input, formatProvider); + } + public static TimeSpan ParseExact(String input, String format, IFormatProvider formatProvider) + { + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format); + return TimeSpanParse.ParseExact(input, format, formatProvider, TimeSpanStyles.None); + } + public static TimeSpan ParseExact(String input, String[] formats, IFormatProvider formatProvider) + { + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None); + } + public static TimeSpan ParseExact(String input, String format, IFormatProvider formatProvider, TimeSpanStyles styles) + { + ValidateStyles(styles, nameof(styles)); + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + if (format == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.format); + return TimeSpanParse.ParseExact(input, format, formatProvider, styles); + } + + public static TimeSpan ParseExact(ReadOnlySpan input, ReadOnlySpan format, IFormatProvider formatProvider, TimeSpanStyles styles = TimeSpanStyles.None) + { + ValidateStyles(styles, nameof(styles)); + return TimeSpanParse.ParseExact(input, format, formatProvider, styles); + } + public static TimeSpan ParseExact(String input, String[] formats, IFormatProvider formatProvider, TimeSpanStyles styles) + { + ValidateStyles(styles, nameof(styles)); + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, styles); + } + public static TimeSpan ParseExact(ReadOnlySpan input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles = TimeSpanStyles.None) + { + ValidateStyles(styles, nameof(styles)); + return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, styles); + } + public static Boolean TryParse(String s, out TimeSpan result) + { + if (s == null) + { + result = default(TimeSpan); + return false; + } + return TimeSpanParse.TryParse(s, null, out result); + } + public static bool TryParse(ReadOnlySpan s, out TimeSpan result) + { + return TimeSpanParse.TryParse(s, null, out result); + } + + public static Boolean TryParse(String input, IFormatProvider formatProvider, out TimeSpan result) + { + if (input == null) + { + result = default(TimeSpan); + return false; + } + return TimeSpanParse.TryParse(input, formatProvider, out result); + } + public static bool TryParse(ReadOnlySpan input, IFormatProvider formatProvider, out TimeSpan result) + { + return TimeSpanParse.TryParse(input, formatProvider, out result); + } + public static Boolean TryParseExact(String input, String format, IFormatProvider formatProvider, out TimeSpan result) + { + if (input == null || format == null) + { + result = default; + return false; + } + return TimeSpanParse.TryParseExact(input, format, formatProvider, TimeSpanStyles.None, out result); + } + + public static bool TryParseExact(ReadOnlySpan input, ReadOnlySpan format, IFormatProvider formatProvider, out TimeSpan result) + { + return TimeSpanParse.TryParseExact(input, format, formatProvider, TimeSpanStyles.None, out result); + } + public static Boolean TryParseExact(String input, String[] formats, IFormatProvider formatProvider, out TimeSpan result) + { + if (input == null) + { + result = default(TimeSpan); + return false; + } + return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None, out result); + } + public static bool TryParseExact(ReadOnlySpan input, string[] formats, IFormatProvider formatProvider, out TimeSpan result) + { + return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None, out result); + } + + public static Boolean TryParseExact(String input, String format, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result) + { + ValidateStyles(styles, nameof(styles)); + if (input == null || format == null) + { + result = default; + return false; + } + + return TimeSpanParse.TryParseExact(input, format, formatProvider, styles, out result); + } + + public static bool TryParseExact(ReadOnlySpan input, ReadOnlySpan format, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result) + { + ValidateStyles(styles, nameof(styles)); + return TimeSpanParse.TryParseExact(input, format, formatProvider, styles, out result); + } + public static Boolean TryParseExact(String input, String[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result) + { + ValidateStyles(styles, nameof(styles)); + if (input == null) + { + result = default(TimeSpan); + return false; + } + return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, styles, out result); + } + + public static bool TryParseExact(ReadOnlySpan input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result) + { + ValidateStyles(styles, nameof(styles)); + return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, styles, out result); + } + public override String ToString() + { + return TimeSpanFormat.Format(this, null, null); + } + public String ToString(String format) + { + return TimeSpanFormat.Format(this, format, null); + } + public String ToString(String format, IFormatProvider formatProvider) + { + return TimeSpanFormat.Format(this, format, formatProvider); + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider formatProvider = null) + { + return TimeSpanFormat.TryFormat(this, destination, out charsWritten, format, formatProvider); + } + #endregion + + public static TimeSpan operator -(TimeSpan t) + { + if (t._ticks == TimeSpan.MinValue._ticks) + throw new OverflowException(SR.Overflow_NegateTwosCompNum); + return new TimeSpan(-t._ticks); + } + + public static TimeSpan operator -(TimeSpan t1, TimeSpan t2) + { + return t1.Subtract(t2); + } + + public static TimeSpan operator +(TimeSpan t) + { + return t; + } + + public static TimeSpan operator +(TimeSpan t1, TimeSpan t2) + { + return t1.Add(t2); + } + + public static TimeSpan operator *(TimeSpan timeSpan, double factor) + { + if (double.IsNaN(factor)) + { + throw new ArgumentException(SR.Arg_CannotBeNaN, nameof(factor)); + } + + // Rounding to the nearest tick is as close to the result we would have with unlimited + // precision as possible, and so likely to have the least potential to surprise. + double ticks = Math.Round(timeSpan.Ticks * factor); + if (ticks > long.MaxValue | ticks < long.MinValue) + { + throw new OverflowException(SR.Overflow_TimeSpanTooLong); + } + + return FromTicks((long)ticks); + } + + public static TimeSpan operator *(double factor, TimeSpan timeSpan) => timeSpan * factor; + + public static TimeSpan operator /(TimeSpan timeSpan, double divisor) + { + if (double.IsNaN(divisor)) + { + throw new ArgumentException(SR.Arg_CannotBeNaN, nameof(divisor)); + } + + double ticks = Math.Round(timeSpan.Ticks / divisor); + if (ticks > long.MaxValue | ticks < long.MinValue || double.IsNaN(ticks)) + { + throw new OverflowException(SR.Overflow_TimeSpanTooLong); + } + + return FromTicks((long)ticks); + } + + // Using floating-point arithmetic directly means that infinities can be returned, which is reasonable + // if we consider TimeSpan.FromHours(1) / TimeSpan.Zero asks how many zero-second intervals there are in + // an hour for which infinity is the mathematic correct answer. Having TimeSpan.Zero / TimeSpan.Zero return NaN + // is perhaps less useful, but no less useful than an exception. + public static double operator /(TimeSpan t1, TimeSpan t2) => t1.Ticks / (double)t2.Ticks; + + public static bool operator ==(TimeSpan t1, TimeSpan t2) + { + return t1._ticks == t2._ticks; + } + + public static bool operator !=(TimeSpan t1, TimeSpan t2) + { + return t1._ticks != t2._ticks; + } + + public static bool operator <(TimeSpan t1, TimeSpan t2) + { + return t1._ticks < t2._ticks; + } + + public static bool operator <=(TimeSpan t1, TimeSpan t2) + { + return t1._ticks <= t2._ticks; + } + + public static bool operator >(TimeSpan t1, TimeSpan t2) + { + return t1._ticks > t2._ticks; + } + + public static bool operator >=(TimeSpan t1, TimeSpan t2) + { + return t1._ticks >= t2._ticks; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/TimeZone.cs b/external/corert/src/System.Private.CoreLib/shared/System/TimeZone.cs index 88e2e21864..d4059babfc 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/TimeZone.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/TimeZone.cs @@ -26,7 +26,6 @@ using System.Globalization; namespace System { - [Serializable] [Obsolete("System.TimeZone has been deprecated. Please investigate the use of System.TimeZoneInfo instead.")] public abstract class TimeZone { diff --git a/external/corert/src/System.Private.CoreLib/shared/System/TimeZoneNotFoundException.cs b/external/corert/src/System.Private.CoreLib/shared/System/TimeZoneNotFoundException.cs index 5b7ce10268..f83c6443c3 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/TimeZoneNotFoundException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/TimeZoneNotFoundException.cs @@ -6,6 +6,8 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class TimeZoneNotFoundException : Exception { public TimeZoneNotFoundException() @@ -24,7 +26,6 @@ namespace System protected TimeZoneNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/TimeoutException.cs b/external/corert/src/System.Private.CoreLib/shared/System/TimeoutException.cs index 4ba95bc47b..ddaa47747d 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/TimeoutException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/TimeoutException.cs @@ -15,29 +15,30 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class TimeoutException : SystemException { public TimeoutException() : base(SR.Arg_TimeoutException) { - HResult = __HResults.COR_E_TIMEOUT; + HResult = HResults.COR_E_TIMEOUT; } public TimeoutException(String message) : base(message) { - HResult = __HResults.COR_E_TIMEOUT; + HResult = HResults.COR_E_TIMEOUT; } public TimeoutException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_TIMEOUT; + HResult = HResults.COR_E_TIMEOUT; } protected TimeoutException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Tuple.cs b/external/corert/src/System.Private.CoreLib/shared/System/Tuple.cs similarity index 82% rename from external/corert/src/System.Private.CoreLib/src/System/Tuple.cs rename to external/corert/src/System.Private.CoreLib/shared/System/Tuple.cs index fdf89d5e38..d0c8462330 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Tuple.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Tuple.cs @@ -105,9 +105,12 @@ namespace System } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple { - private readonly T1 m_Item1; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. + private readonly T1 m_Item1; // Do not rename (binary serialization) public T1 Item1 { get { return m_Item1; } } @@ -118,7 +121,7 @@ namespace System public override Boolean Equals(Object obj) { - return ((IStructuralEquatable)this).Equals(obj, ObjectEqualityComparer.Default); + return ((IStructuralEquatable)this).Equals(obj, EqualityComparer.Default); } Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer) @@ -137,7 +140,7 @@ namespace System Int32 IComparable.CompareTo(Object obj) { - return ((IStructuralComparable)this).CompareTo(obj, LowLevelComparer.Default); + return ((IStructuralComparable)this).CompareTo(obj, Comparer.Default); } Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer) @@ -156,7 +159,7 @@ namespace System public override int GetHashCode() { - return ((IStructuralEquatable)this).GetHashCode(ObjectEqualityComparer.Default); + return ((IStructuralEquatable)this).GetHashCode(EqualityComparer.Default); } Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer) @@ -171,7 +174,7 @@ namespace System public override string ToString() { StringBuilder sb = new StringBuilder(); - sb.Append("("); + sb.Append('('); return ((ITupleInternal)this).ToString(sb); } @@ -204,10 +207,13 @@ namespace System } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple { - private readonly T1 m_Item1; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T2 m_Item2; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. + private readonly T1 m_Item1; // Do not rename (binary serialization) + private readonly T2 m_Item2; // Do not rename (binary serialization) public T1 Item1 { get { return m_Item1; } } public T2 Item2 { get { return m_Item2; } } @@ -220,7 +226,7 @@ namespace System public override Boolean Equals(Object obj) { - return ((IStructuralEquatable)this).Equals(obj, ObjectEqualityComparer.Default); ; + return ((IStructuralEquatable)this).Equals(obj, EqualityComparer.Default); ; } Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer) @@ -239,7 +245,7 @@ namespace System Int32 IComparable.CompareTo(Object obj) { - return ((IStructuralComparable)this).CompareTo(obj, LowLevelComparer.Default); + return ((IStructuralComparable)this).CompareTo(obj, Comparer.Default); } Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer) @@ -264,7 +270,7 @@ namespace System public override int GetHashCode() { - return ((IStructuralEquatable)this).GetHashCode(ObjectEqualityComparer.Default); + return ((IStructuralEquatable)this).GetHashCode(EqualityComparer.Default); } Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer) @@ -279,7 +285,7 @@ namespace System public override string ToString() { StringBuilder sb = new StringBuilder(); - sb.Append("("); + sb.Append('('); return ((ITupleInternal)this).ToString(sb); } @@ -318,11 +324,14 @@ namespace System } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple { - private readonly T1 m_Item1; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T2 m_Item2; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T3 m_Item3; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. + private readonly T1 m_Item1; // Do not rename (binary serialization) + private readonly T2 m_Item2; // Do not rename (binary serialization) + private readonly T3 m_Item3; // Do not rename (binary serialization) public T1 Item1 { get { return m_Item1; } } public T2 Item2 { get { return m_Item2; } } @@ -337,7 +346,7 @@ namespace System public override Boolean Equals(Object obj) { - return ((IStructuralEquatable)this).Equals(obj, ObjectEqualityComparer.Default); ; + return ((IStructuralEquatable)this).Equals(obj, EqualityComparer.Default); ; } Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer) @@ -356,7 +365,7 @@ namespace System Int32 IComparable.CompareTo(Object obj) { - return ((IStructuralComparable)this).CompareTo(obj, LowLevelComparer.Default); + return ((IStructuralComparable)this).CompareTo(obj, Comparer.Default); } Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer) @@ -385,7 +394,7 @@ namespace System public override int GetHashCode() { - return ((IStructuralEquatable)this).GetHashCode(ObjectEqualityComparer.Default); + return ((IStructuralEquatable)this).GetHashCode(EqualityComparer.Default); } Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer) @@ -400,7 +409,7 @@ namespace System public override string ToString() { StringBuilder sb = new StringBuilder(); - sb.Append("("); + sb.Append('('); return ((ITupleInternal)this).ToString(sb); } @@ -443,12 +452,15 @@ namespace System } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple { - private readonly T1 m_Item1; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T2 m_Item2; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T3 m_Item3; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T4 m_Item4; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. + private readonly T1 m_Item1; // Do not rename (binary serialization) + private readonly T2 m_Item2; // Do not rename (binary serialization) + private readonly T3 m_Item3; // Do not rename (binary serialization) + private readonly T4 m_Item4; // Do not rename (binary serialization) public T1 Item1 { get { return m_Item1; } } public T2 Item2 { get { return m_Item2; } } @@ -465,7 +477,7 @@ namespace System public override Boolean Equals(Object obj) { - return ((IStructuralEquatable)this).Equals(obj, ObjectEqualityComparer.Default); ; + return ((IStructuralEquatable)this).Equals(obj, EqualityComparer.Default); ; } Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer) @@ -484,7 +496,7 @@ namespace System Int32 IComparable.CompareTo(Object obj) { - return ((IStructuralComparable)this).CompareTo(obj, LowLevelComparer.Default); + return ((IStructuralComparable)this).CompareTo(obj, Comparer.Default); } Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer) @@ -517,7 +529,7 @@ namespace System public override int GetHashCode() { - return ((IStructuralEquatable)this).GetHashCode(ObjectEqualityComparer.Default); + return ((IStructuralEquatable)this).GetHashCode(EqualityComparer.Default); } Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer) @@ -532,7 +544,7 @@ namespace System public override string ToString() { StringBuilder sb = new StringBuilder(); - sb.Append("("); + sb.Append('('); return ((ITupleInternal)this).ToString(sb); } @@ -579,13 +591,16 @@ namespace System } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple { - private readonly T1 m_Item1; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T2 m_Item2; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T3 m_Item3; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T4 m_Item4; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T5 m_Item5; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. + private readonly T1 m_Item1; // Do not rename (binary serialization) + private readonly T2 m_Item2; // Do not rename (binary serialization) + private readonly T3 m_Item3; // Do not rename (binary serialization) + private readonly T4 m_Item4; // Do not rename (binary serialization) + private readonly T5 m_Item5; // Do not rename (binary serialization) public T1 Item1 { get { return m_Item1; } } public T2 Item2 { get { return m_Item2; } } @@ -604,7 +619,7 @@ namespace System public override Boolean Equals(Object obj) { - return ((IStructuralEquatable)this).Equals(obj, ObjectEqualityComparer.Default); ; + return ((IStructuralEquatable)this).Equals(obj, EqualityComparer.Default); ; } Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer) @@ -623,7 +638,7 @@ namespace System Int32 IComparable.CompareTo(Object obj) { - return ((IStructuralComparable)this).CompareTo(obj, LowLevelComparer.Default); + return ((IStructuralComparable)this).CompareTo(obj, Comparer.Default); } Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer) @@ -660,7 +675,7 @@ namespace System public override int GetHashCode() { - return ((IStructuralEquatable)this).GetHashCode(ObjectEqualityComparer.Default); + return ((IStructuralEquatable)this).GetHashCode(EqualityComparer.Default); } Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer) @@ -675,7 +690,7 @@ namespace System public override string ToString() { StringBuilder sb = new StringBuilder(); - sb.Append("("); + sb.Append('('); return ((ITupleInternal)this).ToString(sb); } @@ -726,14 +741,17 @@ namespace System } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple { - private readonly T1 m_Item1; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T2 m_Item2; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T3 m_Item3; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T4 m_Item4; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T5 m_Item5; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T6 m_Item6; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. + private readonly T1 m_Item1; // Do not rename (binary serialization) + private readonly T2 m_Item2; // Do not rename (binary serialization) + private readonly T3 m_Item3; // Do not rename (binary serialization) + private readonly T4 m_Item4; // Do not rename (binary serialization) + private readonly T5 m_Item5; // Do not rename (binary serialization) + private readonly T6 m_Item6; // Do not rename (binary serialization) public T1 Item1 { get { return m_Item1; } } public T2 Item2 { get { return m_Item2; } } @@ -754,7 +772,7 @@ namespace System public override Boolean Equals(Object obj) { - return ((IStructuralEquatable)this).Equals(obj, ObjectEqualityComparer.Default); ; + return ((IStructuralEquatable)this).Equals(obj, EqualityComparer.Default); ; } Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer) @@ -773,7 +791,7 @@ namespace System Int32 IComparable.CompareTo(Object obj) { - return ((IStructuralComparable)this).CompareTo(obj, LowLevelComparer.Default); + return ((IStructuralComparable)this).CompareTo(obj, Comparer.Default); } Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer) @@ -814,7 +832,7 @@ namespace System public override int GetHashCode() { - return ((IStructuralEquatable)this).GetHashCode(ObjectEqualityComparer.Default); + return ((IStructuralEquatable)this).GetHashCode(EqualityComparer.Default); } Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer) @@ -829,7 +847,7 @@ namespace System public override string ToString() { StringBuilder sb = new StringBuilder(); - sb.Append("("); + sb.Append('('); return ((ITupleInternal)this).ToString(sb); } @@ -884,15 +902,18 @@ namespace System } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple { - private readonly T1 m_Item1; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T2 m_Item2; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T3 m_Item3; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T4 m_Item4; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T5 m_Item5; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T6 m_Item6; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T7 m_Item7; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. + private readonly T1 m_Item1; // Do not rename (binary serialization) + private readonly T2 m_Item2; // Do not rename (binary serialization) + private readonly T3 m_Item3; // Do not rename (binary serialization) + private readonly T4 m_Item4; // Do not rename (binary serialization) + private readonly T5 m_Item5; // Do not rename (binary serialization) + private readonly T6 m_Item6; // Do not rename (binary serialization) + private readonly T7 m_Item7; // Do not rename (binary serialization) public T1 Item1 { get { return m_Item1; } } public T2 Item2 { get { return m_Item2; } } @@ -915,7 +936,7 @@ namespace System public override Boolean Equals(Object obj) { - return ((IStructuralEquatable)this).Equals(obj, ObjectEqualityComparer.Default); ; + return ((IStructuralEquatable)this).Equals(obj, EqualityComparer.Default); ; } Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer) @@ -934,7 +955,7 @@ namespace System Int32 IComparable.CompareTo(Object obj) { - return ((IStructuralComparable)this).CompareTo(obj, LowLevelComparer.Default); + return ((IStructuralComparable)this).CompareTo(obj, Comparer.Default); } Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer) @@ -979,7 +1000,7 @@ namespace System public override int GetHashCode() { - return ((IStructuralEquatable)this).GetHashCode(ObjectEqualityComparer.Default); + return ((IStructuralEquatable)this).GetHashCode(EqualityComparer.Default); } Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer) @@ -994,7 +1015,7 @@ namespace System public override string ToString() { StringBuilder sb = new StringBuilder(); - sb.Append("("); + sb.Append('('); return ((ITupleInternal)this).ToString(sb); } @@ -1053,16 +1074,19 @@ namespace System } [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple { - private readonly T1 m_Item1; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T2 m_Item2; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T3 m_Item3; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T4 m_Item4; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T5 m_Item5; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T6 m_Item6; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly T7 m_Item7; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. - private readonly TRest m_Rest; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. + private readonly T1 m_Item1; // Do not rename (binary serialization) + private readonly T2 m_Item2; // Do not rename (binary serialization) + private readonly T3 m_Item3; // Do not rename (binary serialization) + private readonly T4 m_Item4; // Do not rename (binary serialization) + private readonly T5 m_Item5; // Do not rename (binary serialization) + private readonly T6 m_Item6; // Do not rename (binary serialization) + private readonly T7 m_Item7; // Do not rename (binary serialization) + private readonly TRest m_Rest; // Do not rename (binary serialization) public T1 Item1 { get { return m_Item1; } } public T2 Item2 { get { return m_Item2; } } @@ -1092,7 +1116,7 @@ namespace System public override Boolean Equals(Object obj) { - return ((IStructuralEquatable)this).Equals(obj, ObjectEqualityComparer.Default); ; + return ((IStructuralEquatable)this).Equals(obj, EqualityComparer.Default); ; } Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer) @@ -1111,7 +1135,7 @@ namespace System Int32 IComparable.CompareTo(Object obj) { - return ((IStructuralComparable)this).CompareTo(obj, LowLevelComparer.Default); + return ((IStructuralComparable)this).CompareTo(obj, Comparer.Default); } Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer) @@ -1160,7 +1184,7 @@ namespace System public override int GetHashCode() { - return ((IStructuralEquatable)this).GetHashCode(ObjectEqualityComparer.Default); + return ((IStructuralEquatable)this).GetHashCode(EqualityComparer.Default); } Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer) @@ -1188,7 +1212,7 @@ namespace System case 7: return Tuple.CombineHashCodes(comparer.GetHashCode(m_Item1), comparer.GetHashCode(m_Item2), comparer.GetHashCode(m_Item3), comparer.GetHashCode(m_Item4), comparer.GetHashCode(m_Item5), comparer.GetHashCode(m_Item6), comparer.GetHashCode(m_Item7), t.GetHashCode(comparer)); } - Debug.Assert(false, "Missed all cases for computing Tuple hash code"); + Debug.Fail("Missed all cases for computing Tuple hash code"); return -1; } @@ -1199,7 +1223,7 @@ namespace System public override string ToString() { StringBuilder sb = new StringBuilder(); - sb.Append("("); + sb.Append('('); return ((ITupleInternal)this).ToString(sb); } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Type.cs b/external/corert/src/System.Private.CoreLib/shared/System/Type.cs index 2ba58918a0..a0d219ddd4 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Type.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Type.cs @@ -41,12 +41,16 @@ namespace System protected abstract bool IsPointerImpl(); public virtual bool IsConstructedGenericType { get { throw NotImplemented.ByDesign; } } public virtual bool IsGenericParameter => false; + public virtual bool IsGenericTypeParameter => IsGenericParameter && DeclaringMethod == null; + public virtual bool IsGenericMethodParameter => IsGenericParameter && DeclaringMethod != null; public virtual bool IsGenericType => false; public virtual bool IsGenericTypeDefinition => false; public virtual bool IsSZArray { get { throw NotImplemented.ByDesign; } } public virtual bool IsVariableBoundArray => IsArray && !IsSZArray; + public virtual bool IsByRefLike => throw new NotSupportedException(SR.NotSupported_SubclassOverride); + public bool HasElementType => HasElementTypeImpl(); protected abstract bool HasElementTypeImpl(); public abstract Type GetElementType(); @@ -98,6 +102,8 @@ namespace System public bool IsContextful => IsContextfulImpl(); protected virtual bool IsContextfulImpl() => false; + public virtual bool IsCollectible => true; + public virtual bool IsEnum => IsSubclassOf(typeof(Enum)); public bool IsMarshalByRef => IsMarshalByRefImpl(); protected virtual bool IsMarshalByRefImpl() => false; @@ -106,6 +112,8 @@ namespace System public bool IsValueType => IsValueTypeImpl(); protected virtual bool IsValueTypeImpl() => IsSubclassOf(typeof(ValueType)); + public virtual bool IsSignatureType => false; + public virtual bool IsSecurityCritical { get { throw NotImplemented.ByDesign; } } public virtual bool IsSecuritySafeCritical { get { throw NotImplemented.ByDesign; } } public virtual bool IsSecurityTransparent { get { throw NotImplemented.ByDesign; } } @@ -177,6 +185,27 @@ namespace System protected abstract MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers); + public MethodInfo GetMethod(string name, int genericParameterCount, Type[] types) => GetMethod(name, genericParameterCount, types, null); + public MethodInfo GetMethod(string name, int genericParameterCount, Type[] types, ParameterModifier[] modifiers) => GetMethod(name, genericParameterCount, Type.DefaultLookup, null, types, modifiers); + public MethodInfo GetMethod(string name, int genericParameterCount, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers) => GetMethod(name, genericParameterCount, bindingAttr, binder, CallingConventions.Any, types, modifiers); + public MethodInfo GetMethod(string name, int genericParameterCount, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + if (name == null) + throw new ArgumentNullException(nameof(name)); + if (genericParameterCount < 0) + throw new ArgumentException(SR.ArgumentOutOfRange_NeedNonNegNum, nameof(genericParameterCount)); + if (types == null) + throw new ArgumentNullException(nameof(types)); + for (int i = 0; i < types.Length; i++) + { + if (types[i] == null) + throw new ArgumentNullException(nameof(types)); + } + return GetMethodImpl(name, genericParameterCount, bindingAttr, binder, callConvention, types, modifiers); + } + + protected virtual MethodInfo GetMethodImpl(string name, int genericParameterCount, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) => throw new NotSupportedException(); + public MethodInfo[] GetMethods() => GetMethods(Type.DefaultLookup); public abstract MethodInfo[] GetMethods(BindingFlags bindingAttr); @@ -317,6 +346,13 @@ namespace System public virtual Type MakeGenericType(params Type[] typeArguments) { throw new NotSupportedException(SR.NotSupported_SubclassOverride); } public virtual Type MakePointerType() { throw new NotSupportedException(); } + public static Type MakeGenericMethodParameter(int position) + { + if (position < 0) + throw new ArgumentException(SR.ArgumentOutOfRange_MustBeNonNegNum, nameof(position)); + return new SignatureGenericMethodParameterType(position); + } + public override string ToString() => "Type: " + Name; // Why do we add the "Type: " prefix? public override bool Equals(object o) => o == null ? false : Equals(o as Type); diff --git a/external/corert/src/System.Private.CoreLib/shared/System/TypeAccessException.cs b/external/corert/src/System.Private.CoreLib/shared/System/TypeAccessException.cs index 302dcb1ac1..e12a8b08dd 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/TypeAccessException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/TypeAccessException.cs @@ -8,29 +8,30 @@ namespace System { // TypeAccessException derives from TypeLoadException rather than MemberAccessException because in // pre-v4 releases of the runtime TypeLoadException was used in lieu of a TypeAccessException. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class TypeAccessException : TypeLoadException { public TypeAccessException() : base(SR.Arg_TypeAccessException) { - HResult = __HResults.COR_E_TYPEACCESS; + HResult = HResults.COR_E_TYPEACCESS; } public TypeAccessException(string message) : base(message) { - HResult = __HResults.COR_E_TYPEACCESS; + HResult = HResults.COR_E_TYPEACCESS; } public TypeAccessException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_TYPEACCESS; + HResult = HResults.COR_E_TYPEACCESS; } protected TypeAccessException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/TypeInitializationException.cs b/external/corert/src/System.Private.CoreLib/shared/System/TypeInitializationException.cs index 8d0b8a9f79..4bf2906217 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/TypeInitializationException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/TypeInitializationException.cs @@ -19,6 +19,8 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class TypeInitializationException : SystemException { private String _typeName; @@ -28,7 +30,7 @@ namespace System private TypeInitializationException() : base(SR.TypeInitialization_Default) { - HResult = __HResults.COR_E_TYPEINITIALIZATION; + HResult = HResults.COR_E_TYPEINITIALIZATION; } @@ -41,19 +43,26 @@ namespace System // for Interop only, though it's not particularly useful. internal TypeInitializationException(String message) : base(message) { - HResult = __HResults.COR_E_TYPEINITIALIZATION; + HResult = HResults.COR_E_TYPEINITIALIZATION; } internal TypeInitializationException(String fullTypeName, String message, Exception innerException) : base(message, innerException) { _typeName = fullTypeName; - HResult = __HResults.COR_E_TYPEINITIALIZATION; + HResult = HResults.COR_E_TYPEINITIALIZATION; + } + + internal TypeInitializationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + _typeName = info.GetString("TypeName"); } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); + info.AddValue("TypeName", TypeName, typeof(string)); } public String TypeName diff --git a/external/corert/src/System.Private.CoreLib/shared/System/TypeUnloadedException.cs b/external/corert/src/System.Private.CoreLib/shared/System/TypeUnloadedException.cs index c7ed71c9cb..a01ef37a8b 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/TypeUnloadedException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/TypeUnloadedException.cs @@ -6,30 +6,31 @@ using System.Runtime.Serialization; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class TypeUnloadedException : SystemException { public TypeUnloadedException() : base(SR.Arg_TypeUnloadedException) { - HResult = __HResults.COR_E_TYPEUNLOADED; + HResult = HResults.COR_E_TYPEUNLOADED; } public TypeUnloadedException(string message) : base(message) { - HResult = __HResults.COR_E_TYPEUNLOADED; + HResult = HResults.COR_E_TYPEUNLOADED; } public TypeUnloadedException(string message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_TYPEUNLOADED; + HResult = HResults.COR_E_TYPEUNLOADED; } protected TypeUnloadedException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/UInt16.cs b/external/corert/src/System.Private.CoreLib/shared/System/UInt16.cs new file mode 100644 index 0000000000..3047d18198 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/UInt16.cs @@ -0,0 +1,289 @@ +// 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.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [CLSCompliant(false)] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct UInt16 : IComparable, IConvertible, IFormattable, IComparable, IEquatable, ISpanFormattable + { + private ushort m_value; // Do not rename (binary serialization) + + public const ushort MaxValue = (ushort)0xFFFF; + public const ushort MinValue = 0; + + + // Compares this object to another object, returning an integer that + // indicates the relationship. + // Returns a value less than zero if this object + // null is considered to be less than any instance. + // If object is not of type UInt16, this method throws an ArgumentException. + // + public int CompareTo(Object value) + { + if (value == null) + { + return 1; + } + if (value is UInt16) + { + return ((int)m_value - (int)(((UInt16)value).m_value)); + } + throw new ArgumentException(SR.Arg_MustBeUInt16); + } + + public int CompareTo(UInt16 value) + { + return ((int)m_value - (int)value); + } + + public override bool Equals(Object obj) + { + if (!(obj is UInt16)) + { + return false; + } + return m_value == ((UInt16)obj).m_value; + } + + [NonVersionable] + public bool Equals(UInt16 obj) + { + return m_value == obj; + } + + // Returns a HashCode for the UInt16 + public override int GetHashCode() + { + return (int)m_value; + } + + // Converts the current value to a String in base-10 with no extra padding. + public override String ToString() + { + return Number.FormatUInt32(m_value, null, null); + } + + public String ToString(IFormatProvider provider) + { + return Number.FormatUInt32(m_value, null, provider); + } + + + public String ToString(String format) + { + return Number.FormatUInt32(m_value, format, null); + } + + public String ToString(String format, IFormatProvider provider) + { + return Number.FormatUInt32(m_value, format, provider); + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) + { + return Number.TryFormatUInt32(m_value, format, provider, destination, out charsWritten); + } + + [CLSCompliant(false)] + public static ushort Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); + } + + [CLSCompliant(false)] + public static ushort Parse(String s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, style, NumberFormatInfo.CurrentInfo); + } + + + [CLSCompliant(false)] + public static ushort Parse(String s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static ushort Parse(String s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static ushort Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Parse(s, style, NumberFormatInfo.GetInstance(provider)); + } + + private static ushort Parse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info) + { + uint i = 0; + try + { + i = Number.ParseUInt32(s, style, info); + } + catch (OverflowException e) + { + throw new OverflowException(SR.Overflow_UInt16, e); + } + + if (i > MaxValue) throw new OverflowException(SR.Overflow_UInt16); + return (ushort)i; + } + + [CLSCompliant(false)] + public static bool TryParse(String s, out UInt16 result) + { + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan s, out ushort result) + { + return TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + [CLSCompliant(false)] + public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out UInt16 result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return TryParse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out ushort result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + private static bool TryParse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out UInt16 result) + { + result = 0; + UInt32 i; + if (!Number.TryParseUInt32(s, style, info, out i)) + { + return false; + } + if (i > MaxValue) + { + return false; + } + result = (UInt16)i; + return true; + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.UInt16; + } + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return Convert.ToChar(m_value); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return m_value; + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(m_value); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(m_value); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(m_value); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "UInt16", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/UInt32.cs b/external/corert/src/System.Private.CoreLib/shared/System/UInt32.cs new file mode 100644 index 0000000000..1e33dcf17b --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/UInt32.cs @@ -0,0 +1,265 @@ +// 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.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [CLSCompliant(false)] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct UInt32 : IComparable, IConvertible, IFormattable, IComparable, IEquatable, ISpanFormattable + { + private uint m_value; // Do not rename (binary serialization) + + public const uint MaxValue = (uint)0xffffffff; + public const uint MinValue = 0U; + + + // Compares this object to another object, returning an integer that + // indicates the relationship. + // Returns a value less than zero if this object + // null is considered to be less than any instance. + // If object is not of type UInt32, this method throws an ArgumentException. + // + public int CompareTo(Object value) + { + if (value == null) + { + return 1; + } + if (value is UInt32) + { + // Need to use compare because subtraction will wrap + // to positive for very large neg numbers, etc. + uint i = (uint)value; + if (m_value < i) return -1; + if (m_value > i) return 1; + return 0; + } + throw new ArgumentException(SR.Arg_MustBeUInt32); + } + + public int CompareTo(UInt32 value) + { + // Need to use compare because subtraction will wrap + // to positive for very large neg numbers, etc. + if (m_value < value) return -1; + if (m_value > value) return 1; + return 0; + } + + public override bool Equals(Object obj) + { + if (!(obj is UInt32)) + { + return false; + } + return m_value == ((UInt32)obj).m_value; + } + + [NonVersionable] + public bool Equals(UInt32 obj) + { + return m_value == obj; + } + + // The absolute value of the int contained. + public override int GetHashCode() + { + return ((int)m_value); + } + + // The base 10 representation of the number with no extra padding. + public override String ToString() + { + return Number.FormatUInt32(m_value, null, null); + } + + public String ToString(IFormatProvider provider) + { + return Number.FormatUInt32(m_value, null, provider); + } + + public String ToString(String format) + { + return Number.FormatUInt32(m_value, format, null); + } + + public String ToString(String format, IFormatProvider provider) + { + return Number.FormatUInt32(m_value, format, provider); + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) + { + return Number.TryFormatUInt32(m_value, format, provider, destination, out charsWritten); + } + + [CLSCompliant(false)] + public static uint Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); + } + + [CLSCompliant(false)] + public static uint Parse(String s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt32(s, style, NumberFormatInfo.CurrentInfo); + } + + + [CLSCompliant(false)] + public static uint Parse(String s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt32(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static uint Parse(String s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt32(s, style, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static uint Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.ParseUInt32(s, style, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static bool TryParse(String s, out UInt32 result) + { + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseUInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan s, out uint result) + { + return Number.TryParseUInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + [CLSCompliant(false)] + public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out UInt32 result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseUInt32(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out uint result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseUInt32(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.UInt32; + } + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return Convert.ToChar(m_value); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(m_value); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return m_value; + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(m_value); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(m_value); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "UInt32", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/UInt64.cs b/external/corert/src/System.Private.CoreLib/shared/System/UInt64.cs new file mode 100644 index 0000000000..d30fbe1e42 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/UInt64.cs @@ -0,0 +1,262 @@ +// 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.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [CLSCompliant(false)] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct UInt64 : IComparable, IConvertible, IFormattable, IComparable, IEquatable, ISpanFormattable + { + private ulong m_value; // Do not rename (binary serialization) + + public const ulong MaxValue = (ulong)0xffffffffffffffffL; + public const ulong MinValue = 0x0; + + // Compares this object to another object, returning an integer that + // indicates the relationship. + // Returns a value less than zero if this object + // null is considered to be less than any instance. + // If object is not of type UInt64, this method throws an ArgumentException. + // + public int CompareTo(Object value) + { + if (value == null) + { + return 1; + } + if (value is UInt64) + { + // Need to use compare because subtraction will wrap + // to positive for very large neg numbers, etc. + ulong i = (ulong)value; + if (m_value < i) return -1; + if (m_value > i) return 1; + return 0; + } + throw new ArgumentException(SR.Arg_MustBeUInt64); + } + + public int CompareTo(UInt64 value) + { + // Need to use compare because subtraction will wrap + // to positive for very large neg numbers, etc. + if (m_value < value) return -1; + if (m_value > value) return 1; + return 0; + } + + public override bool Equals(Object obj) + { + if (!(obj is UInt64)) + { + return false; + } + return m_value == ((UInt64)obj).m_value; + } + + [NonVersionable] + public bool Equals(UInt64 obj) + { + return m_value == obj; + } + + // The value of the lower 32 bits XORed with the uppper 32 bits. + public override int GetHashCode() + { + return ((int)m_value) ^ (int)(m_value >> 32); + } + + public override String ToString() + { + return Number.FormatUInt64(m_value, null, null); + } + + public String ToString(IFormatProvider provider) + { + return Number.FormatUInt64(m_value, null, provider); + } + + public String ToString(String format) + { + return Number.FormatUInt64(m_value, format, null); + } + + public String ToString(String format, IFormatProvider provider) + { + return Number.FormatUInt64(m_value, format, provider); + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) + { + return Number.TryFormatUInt64(m_value, format, provider, destination, out charsWritten); + } + + [CLSCompliant(false)] + public static ulong Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); + } + + [CLSCompliant(false)] + public static ulong Parse(String s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt64(s, style, NumberFormatInfo.CurrentInfo); + } + + [CLSCompliant(false)] + public static ulong Parse(string s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt64(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static ulong Parse(String s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt64(s, style, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static ulong Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.ParseUInt64(s, style, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static Boolean TryParse(String s, out UInt64 result) + { + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseUInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan s, out ulong result) + { + return Number.TryParseUInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + [CLSCompliant(false)] + public static Boolean TryParse(String s, NumberStyles style, IFormatProvider provider, out UInt64 result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseUInt64(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out ulong result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseUInt64(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.UInt64; + } + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return Convert.ToChar(m_value); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(m_value); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return m_value; + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(m_value); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(m_value); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "UInt64", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/UIntPtr.cs b/external/corert/src/System.Private.CoreLib/shared/System/UIntPtr.cs new file mode 100644 index 0000000000..23750e95fa --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/UIntPtr.cs @@ -0,0 +1,221 @@ +// 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.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; +using System.Runtime.Versioning; + +#if BIT64 +using nuint = System.UInt64; +#else +using nuint = System.UInt32; +#endif + +namespace System +{ + [Serializable] + [CLSCompliant(false)] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct UIntPtr : IEquatable, ISerializable + { + unsafe private void* _value; // Do not rename (binary serialization) + + [Intrinsic] + public static readonly UIntPtr Zero; + + [Intrinsic] + [NonVersionable] + public unsafe UIntPtr(uint value) + { + _value = (void*)value; + } + + [Intrinsic] + [NonVersionable] + public unsafe UIntPtr(ulong value) + { +#if BIT64 + _value = (void*)value; +#else + _value = (void*)checked((uint)value); +#endif + } + + [Intrinsic] + [NonVersionable] + public unsafe UIntPtr(void* value) + { + _value = value; + } + + private unsafe UIntPtr(SerializationInfo info, StreamingContext context) + { + ulong l = info.GetUInt64("value"); + + if (Size == 4 && l > uint.MaxValue) + throw new ArgumentException(SR.Serialization_InvalidPtrValue); + + _value = (void*)l; + } + + unsafe void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + throw new ArgumentNullException(nameof(info)); + + info.AddValue("value", ToUInt64()); + } + + public unsafe override bool Equals(Object obj) + { + if (obj is UIntPtr) + { + return (_value == ((UIntPtr)obj)._value); + } + return false; + } + + unsafe bool IEquatable.Equals(UIntPtr value) + { + return _value == value._value; + } + + public unsafe override int GetHashCode() + { +#if BIT64 + ulong l = (ulong)_value; + return (unchecked((int)l) ^ (int)(l >> 32)); +#else + return unchecked((int)_value); +#endif + } + + [Intrinsic] + [NonVersionable] + public unsafe uint ToUInt32() + { +#if BIT64 + return checked((uint)_value); +#else + return (uint)_value; +#endif + } + + [Intrinsic] + [NonVersionable] + public unsafe ulong ToUInt64() + { + return (ulong)_value; + } + + [Intrinsic] + [NonVersionable] + public static explicit operator UIntPtr(uint value) + { + return new UIntPtr(value); + } + + [Intrinsic] + [NonVersionable] + public static explicit operator UIntPtr(ulong value) + { + return new UIntPtr(value); + } + + [Intrinsic] + [NonVersionable] + public static unsafe explicit operator UIntPtr(void* value) + { + return new UIntPtr(value); + } + + [Intrinsic] + [NonVersionable] + public static unsafe explicit operator void* (UIntPtr value) + { + return value._value; + } + + [Intrinsic] + [NonVersionable] + public static unsafe explicit operator uint(UIntPtr value) + { +#if BIT64 + return checked((uint)value._value); +#else + return (uint)value._value; +#endif + } + + [Intrinsic] + [NonVersionable] + public static unsafe explicit operator ulong(UIntPtr value) + { + return (ulong)value._value; + } + + [Intrinsic] + [NonVersionable] + public static unsafe bool operator ==(UIntPtr value1, UIntPtr value2) + { + return value1._value == value2._value; + } + + [Intrinsic] + [NonVersionable] + public static unsafe bool operator !=(UIntPtr value1, UIntPtr value2) + { + return value1._value != value2._value; + } + + [NonVersionable] + public static UIntPtr Add(UIntPtr pointer, int offset) + { + return pointer + offset; + } + + [Intrinsic] + [NonVersionable] + public static unsafe UIntPtr operator +(UIntPtr pointer, int offset) + { + return new UIntPtr((nuint)pointer._value + (nuint)offset); + } + + [NonVersionable] + public static UIntPtr Subtract(UIntPtr pointer, int offset) + { + return pointer - offset; + } + + [Intrinsic] + [NonVersionable] + public static unsafe UIntPtr operator -(UIntPtr pointer, int offset) + { + return new UIntPtr((nuint)pointer._value - (nuint)offset); + } + + public static unsafe int Size + { + [Intrinsic] + [NonVersionable] + get + { + return sizeof(nuint); + } + } + + [Intrinsic] + [NonVersionable] + public unsafe void* ToPointer() + { + return _value; + } + + public unsafe override string ToString() + { + return ((nuint)_value).ToString(CultureInfo.InvariantCulture); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/UnauthorizedAccessException.cs b/external/corert/src/System.Private.CoreLib/shared/System/UnauthorizedAccessException.cs index 667d576292..a28f6dd73c 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/UnauthorizedAccessException.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/UnauthorizedAccessException.cs @@ -18,30 +18,31 @@ using System.Runtime.Serialization; namespace System { // The UnauthorizedAccessException is thrown when access errors - // occur from IO or other OS methods. + // occur from IO or other OS methods. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class UnauthorizedAccessException : SystemException { public UnauthorizedAccessException() : base(SR.Arg_UnauthorizedAccessException) { - HResult = __HResults.COR_E_UNAUTHORIZEDACCESS; + HResult = HResults.COR_E_UNAUTHORIZEDACCESS; } public UnauthorizedAccessException(String message) : base(message) { - HResult = __HResults.COR_E_UNAUTHORIZEDACCESS; + HResult = HResults.COR_E_UNAUTHORIZEDACCESS; } public UnauthorizedAccessException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_UNAUTHORIZEDACCESS; + HResult = HResults.COR_E_UNAUTHORIZEDACCESS; } protected UnauthorizedAccessException(SerializationInfo info, StreamingContext context) : base(info, context) { - throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/shared/System/UnitySerializationHolder.cs b/external/corert/src/System.Private.CoreLib/shared/System/UnitySerializationHolder.cs new file mode 100644 index 0000000000..2f30356709 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/shared/System/UnitySerializationHolder.cs @@ -0,0 +1,66 @@ +// 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.Runtime.Serialization; + +namespace System +{ + /// + /// Holds Null class for which we guarantee that there is only ever one instance of. + /// This only exists for compatibility with .NET Framework. + /// + [Serializable] +#if CORERT + public +#else + internal +#endif + sealed class UnitySerializationHolder : ISerializable, IObjectReference + { + internal const int NullUnity = 0x0002; + private readonly int _unityType; + private readonly string _data; + + /// + /// A helper method that returns the SerializationInfo that a class utilizing + /// UnitySerializationHelper should return from a call to GetObjectData. It contains + /// the unityType (defined above) and any optional data (used only for the reflection types). + /// + internal static void GetUnitySerializationInfo(SerializationInfo info, int unityType) + { + info.SetType(typeof(UnitySerializationHolder)); + info.AddValue("Data", null, typeof(string)); + info.AddValue("UnityType", unityType); + info.AddValue("AssemblyName", string.Empty); + } + + public UnitySerializationHolder(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + // We are ignoring any other serialization input as we are only concerned about DBNull. + // We also store data and use it for erorr logging. + _unityType = info.GetInt32("UnityType"); + _data = info.GetString("Data"); + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) => + throw new NotSupportedException(SR.NotSupported_UnitySerHolder); + + public object GetRealObject(StreamingContext context) + { + // We are only support deserializing DBNull and throwing for everything else. + if (_unityType != NullUnity) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidUnity, _data ?? "UnityType")); + } + + // We are always returning the same DBNull instance. + return DBNull.Value; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/shared/System/ValueTuple.cs.REMOVED.git-id b/external/corert/src/System.Private.CoreLib/shared/System/ValueTuple.cs.REMOVED.git-id index 6f36b5d902..0fb79fb5ba 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/ValueTuple.cs.REMOVED.git-id +++ b/external/corert/src/System.Private.CoreLib/shared/System/ValueTuple.cs.REMOVED.git-id @@ -1 +1 @@ -e0cd02e9144ed87d3d51949f57fe406d78a50a41 \ No newline at end of file +ddbf22ed6553f66401bd92a5e86ebb7e377acc16 \ No newline at end of file diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Version.cs b/external/corert/src/System.Private.CoreLib/shared/System/Version.cs index 54b2052ddb..df16be2cd2 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Version.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Version.cs @@ -4,7 +4,6 @@ using System.Globalization; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Text; namespace System @@ -16,14 +15,14 @@ namespace System // specified component. [Serializable] - public sealed class Version : ICloneable, IComparable - , IComparable, IEquatable + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class Version : ICloneable, IComparable, IComparable, IEquatable, ISpanFormattable { // AssemblyName depends on the order staying the same - private readonly int _Major; - private readonly int _Minor; - private readonly int _Build = -1; - private readonly int _Revision = -1; + private readonly int _Major; // Do not rename (binary serialization) + private readonly int _Minor; // Do not rename (binary serialization) + private readonly int _Build = -1; // Do not rename (binary serialization) + private readonly int _Revision = -1; // Do not rename (binary serialization) public Version(int major, int minor, int build, int revision) { @@ -38,7 +37,6 @@ namespace System if (revision < 0) throw new ArgumentOutOfRangeException(nameof(revision), SR.ArgumentOutOfRange_Version); - Contract.EndContractBlock(); _Major = major; _Minor = minor; @@ -57,7 +55,6 @@ namespace System if (build < 0) throw new ArgumentOutOfRangeException(nameof(build), SR.ArgumentOutOfRange_Version); - Contract.EndContractBlock(); _Major = major; _Minor = minor; @@ -71,7 +68,6 @@ namespace System if (minor < 0) throw new ArgumentOutOfRangeException(nameof(minor), SR.ArgumentOutOfRange_Version); - Contract.EndContractBlock(); _Major = major; _Minor = minor; @@ -196,83 +192,105 @@ namespace System return accumulator; } - public override String ToString() - { - if (_Build == -1) return (ToString(2)); - if (_Revision == -1) return (ToString(3)); - return (ToString(4)); - } + public override string ToString() => + ToString(DefaultFormatFieldCount); - public String ToString(int fieldCount) + public string ToString(int fieldCount) => + fieldCount == 0 ? string.Empty : + fieldCount == 1 ? _Major.ToString() : + StringBuilderCache.GetStringAndRelease(ToCachedStringBuilder(fieldCount)); + + public bool TryFormat(Span destination, out int charsWritten) => + TryFormat(destination, DefaultFormatFieldCount, out charsWritten); + + public bool TryFormat(Span destination, int fieldCount, out int charsWritten) { - StringBuilder sb; - switch (fieldCount) + if (fieldCount == 0) { - case 0: - return (String.Empty); - case 1: - return (_Major.ToString()); - case 2: - sb = StringBuilderCache.Acquire(); - AppendPositiveNumber(_Major, sb); - sb.Append('.'); - AppendPositiveNumber(_Minor, sb); - return StringBuilderCache.GetStringAndRelease(sb); - default: - if (_Build == -1) - throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "2"), nameof(fieldCount)); - - if (fieldCount == 3) - { - sb = StringBuilderCache.Acquire(); - AppendPositiveNumber(_Major, sb); - sb.Append('.'); - AppendPositiveNumber(_Minor, sb); - sb.Append('.'); - AppendPositiveNumber(_Build, sb); - return StringBuilderCache.GetStringAndRelease(sb); - } - - if (_Revision == -1) - throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "3"), nameof(fieldCount)); - - if (fieldCount == 4) - { - sb = StringBuilderCache.Acquire(); - AppendPositiveNumber(_Major, sb); - sb.Append('.'); - AppendPositiveNumber(_Minor, sb); - sb.Append('.'); - AppendPositiveNumber(_Build, sb); - sb.Append('.'); - AppendPositiveNumber(_Revision, sb); - return StringBuilderCache.GetStringAndRelease(sb); - } - - throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "4"), nameof(fieldCount)); + charsWritten = 0; + return true; } + else if (fieldCount == 1) + { + return _Major.TryFormat(destination, out charsWritten); + } + + StringBuilder sb = ToCachedStringBuilder(fieldCount); + if (sb.Length <= destination.Length) + { + sb.CopyTo(0, destination, sb.Length); + StringBuilderCache.Release(sb); + charsWritten = sb.Length; + return true; + } + + StringBuilderCache.Release(sb); + charsWritten = 0; + return false; } - // - // AppendPositiveNumber is an optimization to append a number to a StringBuilder object without - // doing any boxing and not even creating intermediate string. - // Note: as we always have positive numbers then it is safe to convert the number to string - // regardless of the current culture as we'll not have any punctuation marks in the number - // - private const int ZERO_CHAR_VALUE = (int)'0'; - private static void AppendPositiveNumber(int num, StringBuilder sb) + bool ISpanFormattable.TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider provider) { - Debug.Assert(num >= 0, "AppendPositiveNumber expect positive numbers"); + // format and provider are ignored. + return TryFormat(destination, out charsWritten); + } - int index = sb.Length; - int reminder; + private int DefaultFormatFieldCount => + _Build == -1 ? 2 : + _Revision == -1 ? 3 : + 4; - do + private StringBuilder ToCachedStringBuilder(int fieldCount) + { + // Note: As we always have positive numbers then it is safe to convert the number to string + // regardless of the current culture as we'll not have any punctuation marks in the number. + + if (fieldCount == 2) { - reminder = num % 10; - num = num / 10; - sb.Insert(index, (char)(ZERO_CHAR_VALUE + reminder)); - } while (num > 0); + StringBuilder sb = StringBuilderCache.Acquire(); + sb.Append(_Major); + sb.Append('.'); + sb.Append(_Minor); + return sb; + } + else + { + if (_Build == -1) + { + throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "2"), nameof(fieldCount)); + } + + if (fieldCount == 3) + { + StringBuilder sb = StringBuilderCache.Acquire(); + sb.Append(_Major); + sb.Append('.'); + sb.Append(_Minor); + sb.Append('.'); + sb.Append(_Build); + return sb; + } + + if (_Revision == -1) + { + throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "3"), nameof(fieldCount)); + } + + if (fieldCount == 4) + { + StringBuilder sb = StringBuilderCache.Acquire(); + sb.Append(_Major); + sb.Append('.'); + sb.Append(_Minor); + sb.Append('.'); + sb.Append(_Build); + sb.Append('.'); + sb.Append(_Revision); + return sb; + } + + throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "4"), nameof(fieldCount)); + } } public static Version Parse(string input) @@ -281,104 +299,108 @@ namespace System { throw new ArgumentNullException(nameof(input)); } - Contract.EndContractBlock(); - VersionResult r = new VersionResult(); - r.Init(nameof(input), true); - if (!TryParseVersion(input, ref r)) - { - throw r.GetVersionParseException(); - } - return r.m_parsedVersion; + return ParseVersion(input.AsReadOnlySpan(), throwOnFailure: true); } + public static Version Parse(ReadOnlySpan input) => + ParseVersion(input, throwOnFailure: true); + public static bool TryParse(string input, out Version result) { - VersionResult r = new VersionResult(); - r.Init(nameof(input), false); - bool b = TryParseVersion(input, ref r); - result = r.m_parsedVersion; - return b; + if (input == null) + { + result = null; + return false; + } + + return (result = ParseVersion(input.AsReadOnlySpan(), throwOnFailure: false)) != null; } - private static bool TryParseVersion(string version, ref VersionResult result) + public static bool TryParse(ReadOnlySpan input, out Version result) => + (result = ParseVersion(input, throwOnFailure: false)) != null; + + private static Version ParseVersion(ReadOnlySpan input, bool throwOnFailure) { + // Find the separator between major and minor. It must exist. + int majorEnd = input.IndexOf('.'); + if (majorEnd < 0) + { + if (throwOnFailure) throw new ArgumentException(SR.Arg_VersionString, nameof(input)); + return null; + } + + // Find the ends of the optional minor and build portions. + // We musn't have any separators after build. + int buildEnd = -1; + int minorEnd = input.IndexOf('.', majorEnd + 1); + if (minorEnd != -1) + { + buildEnd = input.IndexOf('.', minorEnd + 1); + if (buildEnd != -1) + { + if (input.IndexOf('.', buildEnd + 1) != -1) + { + if (throwOnFailure) throw new ArgumentException(SR.Arg_VersionString, nameof(input)); + return null; + } + } + } + int major, minor, build, revision; - if ((Object)version == null) + // Parse the major version + if (!TryParseComponent(input.Slice(0, majorEnd), nameof(input), throwOnFailure, out major)) { - result.SetFailure(ParseFailureKind.ArgumentNullException); - return false; + return null; } - String[] parsedComponents = version.Split('.'); - int parsedComponentsLength = parsedComponents.Length; - if ((parsedComponentsLength < 2) || (parsedComponentsLength > 4)) + if (minorEnd != -1) { - result.SetFailure(ParseFailureKind.ArgumentException); - return false; - } - - if (!TryParseComponent(parsedComponents[0], nameof(version), ref result, out major)) - { - return false; - } - - if (!TryParseComponent(parsedComponents[1], nameof(version), ref result, out minor)) - { - return false; - } - - parsedComponentsLength -= 2; - - if (parsedComponentsLength > 0) - { - if (!TryParseComponent(parsedComponents[2], "build", ref result, out build)) + // If there's more than a major and minor, parse the minor, too. + if (!TryParseComponent(input.Slice(majorEnd + 1, minorEnd - majorEnd - 1), nameof(input), throwOnFailure, out minor)) { - return false; + return null; } - parsedComponentsLength--; - - if (parsedComponentsLength > 0) + if (buildEnd != -1) { - if (!TryParseComponent(parsedComponents[3], "revision", ref result, out revision)) - { - return false; - } - else - { - result.m_parsedVersion = new Version(major, minor, build, revision); - } + // major.minor.build.revision + return + TryParseComponent(input.Slice(minorEnd + 1, buildEnd - minorEnd - 1), nameof(build), throwOnFailure, out build) && + TryParseComponent(input.Slice(buildEnd + 1), nameof(revision), throwOnFailure, out revision) ? + new Version(major, minor, build, revision) : + null; } else { - result.m_parsedVersion = new Version(major, minor, build); + // major.minor.build + return TryParseComponent(input.Slice(minorEnd + 1), nameof(build), throwOnFailure, out build) ? + new Version(major, minor, build) : + null; } } else { - result.m_parsedVersion = new Version(major, minor); + // major.minor + return TryParseComponent(input.Slice(majorEnd + 1), nameof(input), throwOnFailure, out minor) ? + new Version(major, minor) : + null; } - - return true; } - private static bool TryParseComponent(string component, string componentName, ref VersionResult result, out int parsedComponent) + private static bool TryParseComponent(ReadOnlySpan component, string componentName, bool throwOnFailure, out int parsedComponent) { - if (!Int32.TryParse(component, NumberStyles.Integer, CultureInfo.InvariantCulture, out parsedComponent)) + if (throwOnFailure) { - result.SetFailure(ParseFailureKind.FormatException, component); - return false; + if ((parsedComponent = int.Parse(component, NumberStyles.Integer, CultureInfo.InvariantCulture)) < 0) + { + throw new ArgumentOutOfRangeException(componentName, SR.ArgumentOutOfRange_Version); + } + return true; } - if (parsedComponent < 0) - { - result.SetFailure(ParseFailureKind.ArgumentOutOfRangeException, componentName); - return false; - } - - return true; + return int.TryParse(component, NumberStyles.Integer, CultureInfo.InvariantCulture, out parsedComponent) && parsedComponent >= 0; } public static bool operator ==(Version v1, Version v2) @@ -400,7 +422,6 @@ namespace System { if ((Object)v1 == null) throw new ArgumentNullException(nameof(v1)); - Contract.EndContractBlock(); return (v1.CompareTo(v2) < 0); } @@ -408,7 +429,6 @@ namespace System { if ((Object)v1 == null) throw new ArgumentNullException(nameof(v1)); - Contract.EndContractBlock(); return (v1.CompareTo(v2) <= 0); } @@ -421,75 +441,5 @@ namespace System { return (v2 <= v1); } - - internal enum ParseFailureKind - { - ArgumentNullException, - ArgumentException, - ArgumentOutOfRangeException, - FormatException - } - - internal struct VersionResult - { - internal Version m_parsedVersion; - internal ParseFailureKind m_failure; - internal string m_exceptionArgument; - internal string m_argumentName; - internal bool m_canThrow; - - internal void Init(string argumentName, bool canThrow) - { - m_canThrow = canThrow; - m_argumentName = argumentName; - } - - internal void SetFailure(ParseFailureKind failure) - { - SetFailure(failure, String.Empty); - } - - internal void SetFailure(ParseFailureKind failure, string argument) - { - m_failure = failure; - m_exceptionArgument = argument; - if (m_canThrow) - { - throw GetVersionParseException(); - } - } - - internal Exception GetVersionParseException() - { - switch (m_failure) - { - case ParseFailureKind.ArgumentNullException: - return new ArgumentNullException(m_argumentName); - case ParseFailureKind.ArgumentException: - return new ArgumentException(SR.Arg_VersionString); - case ParseFailureKind.ArgumentOutOfRangeException: - return new ArgumentOutOfRangeException(m_exceptionArgument, SR.ArgumentOutOfRange_Version); - case ParseFailureKind.FormatException: - // Regenerate the FormatException as would be thrown by Int32.Parse() - try - { - Int32.Parse(m_exceptionArgument, CultureInfo.InvariantCulture); - } - catch (FormatException e) - { - return e; - } - catch (OverflowException e) - { - return e; - } - Debug.Assert(false, "Int32.Parse() did not throw exception but TryParse failed: " + m_exceptionArgument); - return new FormatException(SR.Format_InvalidString); - default: - Debug.Assert(false, "Unmatched case in Version.GetVersionParseException() for value: " + m_failure); - return new ArgumentException(SR.Arg_VersionString); - } - } - } } } diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/DeveloperExperience/DeveloperExperience.cs b/external/corert/src/System.Private.CoreLib/src/Internal/DeveloperExperience/DeveloperExperience.cs index a8ace31885..0c5c05d9e2 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/DeveloperExperience/DeveloperExperience.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/DeveloperExperience/DeveloperExperience.cs @@ -7,6 +7,7 @@ using System.Text; using System.Runtime; using System.Diagnostics; using System.Diagnostics.Contracts; +using System.Reflection; using Internal.Runtime.Augments; @@ -22,30 +23,28 @@ namespace Internal.DeveloperExperience public virtual String CreateStackTraceString(IntPtr ip, bool includeFileInfo) { - ReflectionExecutionDomainCallbacks reflectionCallbacks = RuntimeAugments.CallbacksIfAvailable; - String moduleFullFileName = null; - - if (reflectionCallbacks != null) + StackTraceMetadataCallbacks stackTraceCallbacks = RuntimeAugments.StackTraceCallbacksIfAvailable; + if (stackTraceCallbacks != null) { IntPtr methodStart = RuntimeImports.RhFindMethodStartAddress(ip); if (methodStart != IntPtr.Zero) { - string methodName = string.Empty; - try + string methodName = stackTraceCallbacks.TryGetMethodNameFromStartAddress(methodStart); + if (methodName != null) { - methodName = reflectionCallbacks.GetMethodNameFromStartAddressIfAvailable(methodStart); - } - catch { } - - if (!string.IsNullOrEmpty(methodName)) + if (ip != methodStart) + { + methodName += " + 0x" + (ip.ToInt64() - methodStart.ToInt64()).ToString("x"); + } return methodName; + } } - - // If we don't have precise information, try to map it at least back to the right module. - IntPtr moduleBase = RuntimeImports.RhGetOSModuleFromPointer(ip); - moduleFullFileName = RuntimeAugments.TryGetFullPathToApplicationModule(moduleBase); } + // If we don't have precise information, try to map it at least back to the right module. + IntPtr moduleBase = RuntimeImports.RhGetOSModuleFromPointer(ip); + string moduleFullFileName = RuntimeAugments.TryGetFullPathToApplicationModule(moduleBase); + // Without any callbacks or the ability to map ip correctly we better admit that we don't know if (string.IsNullOrEmpty(moduleFullFileName)) { @@ -68,6 +67,24 @@ namespace Internal.DeveloperExperience columnNumber = 0; } + public virtual void TryGetILOffsetWithinMethod(IntPtr ip, out int ilOffset) + { + ilOffset = StackFrame.OFFSET_UNKNOWN; + } + + /// + /// Makes reasonable effort to get the MethodBase reflection info. Returns null if it can't. + /// + public virtual void TryGetMethodBase(IntPtr methodStartAddress, out MethodBase method) + { + ReflectionExecutionDomainCallbacks reflectionCallbacks = RuntimeAugments.CallbacksIfAvailable; + method = null; + if (reflectionCallbacks != null) + { + method = reflectionCallbacks.GetMethodBaseFromStartAddressIfAvailable(methodStartAddress); + } + } + public virtual bool OnContractFailure(String stackTrace, ContractFailureKind contractFailureKind, String displayMessage, String userMessage, String conditionText, Exception innerException) { Debug.WriteLine("Assertion failed: " + (displayMessage == null ? "" : displayMessage)); diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Diagnostics/StackTraceHelper.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Diagnostics/StackTraceHelper.cs index a0e282d464..bce6bf8458 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Diagnostics/StackTraceHelper.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Diagnostics/StackTraceHelper.cs @@ -3,16 +3,11 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics; using System.Text; -using Internal.DeveloperExperience; namespace Internal.Diagnostics { - /* Right now lists of IPs represent the data model for stack traces. You might wonder why we don't encapsulate this with an OO API such as StackTrace/StackFrame. - Sadly the public definition StackFrame on desktop/contract includes a reference to MethodBase, which can't be referenced in corefx.dll - If we wanted to, we could create a LowLevelStackTrace type which could be encapsulated or extended by the public contract - */ - public static class StackTraceHelper { public static string FormatStackTrace(IntPtr[] ips, bool includeFileInfo) @@ -22,35 +17,7 @@ namespace Internal.Diagnostics public static string FormatStackTrace(IntPtr[] ips, int skipFrames, bool includeFileInfo) { - StringBuilder sb = new StringBuilder(); - for (int i = skipFrames; i < ips.Length; i++) - { - if (i != skipFrames) - sb.AppendLine(); - - IntPtr ip = ips[i]; - if (ip == SpecialIP.EdiSeparator) - { - sb.Append("--- End of stack trace from previous location where exception was thrown ---"); - } - else - { - sb.Append(" at "); - sb.Append(FormatStackFrame(ip, includeFileInfo)); - } - } - - return sb.ToString(); - } - - public static string FormatStackFrame(IntPtr ip, bool includeFileInfo) - { - return Internal.DeveloperExperience.DeveloperExperience.Default.CreateStackTraceString(ip, includeFileInfo); - } - - public static void TryGetSourceLineInfo(IntPtr ip, out string fileName, out int lineNumber, out int columnNumber) - { - Internal.DeveloperExperience.DeveloperExperience.Default.TryGetSourceLineInfo(ip, out fileName, out lineNumber, out columnNumber); + return new StackTrace(ips, skipFrames, ips.Length, includeFileInfo).ToString(); } public static class SpecialIP diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/IntrinsicSupport/ComparerHelpers.cs b/external/corert/src/System.Private.CoreLib/src/Internal/IntrinsicSupport/ComparerHelpers.cs index 83b775baaf..bcbd541dd0 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/IntrinsicSupport/ComparerHelpers.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/IntrinsicSupport/ComparerHelpers.cs @@ -10,8 +10,10 @@ // type is being compared. using System; -using System.Collections; using System.Collections.Generic; +using System.Runtime; + +using Internal.IntrinsicSupport; using Internal.Runtime.Augments; namespace Internal.IntrinsicSupport @@ -20,32 +22,30 @@ namespace Internal.IntrinsicSupport { private static bool ImplementsIComparable(RuntimeTypeHandle t) { - int interfaceCount = RuntimeAugments.GetInterfaceCount(t); + EETypePtr objectType = t.ToEETypePtr(); + EETypePtr icomparableType = typeof(IComparable<>).TypeHandle.ToEETypePtr(); + int interfaceCount = objectType.Interfaces.Count; for (int i = 0; i < interfaceCount; i++) { - RuntimeTypeHandle interfaceType = RuntimeAugments.GetInterface(t, i); + EETypePtr interfaceType = objectType.Interfaces[i]; - if (!RuntimeAugments.IsGenericType(interfaceType)) + if (!interfaceType.IsGeneric) continue; - RuntimeTypeHandle genericDefinition; - RuntimeTypeHandle[] genericTypeArgs; - genericDefinition = RuntimeAugments.GetGenericInstantiation(interfaceType, - out genericTypeArgs); - - if (genericDefinition.Equals(typeof(IComparable<>).TypeHandle)) + if (interfaceType.GenericDefinition == icomparableType) { - if (genericTypeArgs.Length != 1) + var instantiation = interfaceType.Instantiation; + if (instantiation.Length != 1) continue; - if (RuntimeAugments.IsValueType(t)) + if (objectType.IsValueType) { - if (genericTypeArgs[0].Equals(t)) + if (instantiation[0] == objectType) { return true; } } - else if (RuntimeAugments.IsAssignableFrom(genericTypeArgs[0], t)) + else if (RuntimeImports.AreTypesAssignable(objectType, instantiation[0])) { return true; } @@ -94,7 +94,7 @@ namespace Internal.IntrinsicSupport return RuntimeAugments.NewObject(comparerType); } - private static Comparer GetUnknownComparer() + internal static Comparer GetUnknownComparer() { return (Comparer)GetComparer(typeof(T).TypeHandle); } @@ -116,7 +116,7 @@ namespace Internal.IntrinsicSupport // This routine emulates System.Collection.Comparer.Default.Compare(), which lives in the System.Collections.NonGenerics contract. // To avoid adding a reference to that contract just for this hack, we'll replicate the implementation here. - private static int CompareObjects(object x, object y) + internal static int CompareObjects(object x, object y) { if (x == y) return 0; @@ -145,56 +145,80 @@ namespace Internal.IntrinsicSupport throw new ArgumentException(SR.Argument_ImplementIComparable); } - - //----------------------------------------------------------------------- - // Implementations of EqualityComparer for the various possible scenarios - //----------------------------------------------------------------------- - - private sealed class GenericComparer : Comparer where T : IComparable - { - public sealed override int Compare(T x, T y) - { - if (x != null) - { - if (y != null) - return x.CompareTo(y); - - return 1; - } - - if (y != null) - return -1; - - return 0; - } - } - - private sealed class NullableComparer : Comparer> where T : struct, IComparable - { - public sealed override int Compare(Nullable x, Nullable y) - { - if (x.HasValue) - { - if (y.HasValue) - return x.Value.CompareTo(y.Value); - - return 1; - } - - if (y.HasValue) - return -1; - - return 0; - } - } - - private sealed class ObjectComparer : Comparer - { - public sealed override int Compare(T x, T y) - { - return ComparerHelpers.CompareObjects(x, y); - } - } + } +} + +namespace System.Collections.Generic +{ + //----------------------------------------------------------------------- + // Implementations of EqualityComparer for the various possible scenarios + // Because these are serializable, they must not be renamed + //----------------------------------------------------------------------- + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class GenericComparer : Comparer where T : IComparable + { + public sealed override int Compare(T x, T y) + { + if (x != null) + { + if (y != null) + return x.CompareTo(y); + + return 1; + } + + if (y != null) + return -1; + + return 0; + } + + // Equals method for the comparer itself. + public sealed override bool Equals(Object obj) => obj != null && GetType() == obj.GetType(); + + public sealed override int GetHashCode() => GetType().GetHashCode(); + } + + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class NullableComparer : Comparer> where T : struct, IComparable + { + public sealed override int Compare(Nullable x, Nullable y) + { + if (x.HasValue) + { + if (y.HasValue) + return x.Value.CompareTo(y.Value); + + return 1; + } + + if (y.HasValue) + return -1; + + return 0; + } + + // Equals method for the comparer itself. + public sealed override bool Equals(Object obj) => obj != null && GetType() == obj.GetType(); + + public sealed override int GetHashCode() => GetType().GetHashCode(); + } + + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class ObjectComparer : Comparer + { + public sealed override int Compare(T x, T y) + { + return ComparerHelpers.CompareObjects(x, y); + } + + // Equals method for the comparer itself. + public sealed override bool Equals(Object obj) => obj != null && GetType() == obj.GetType(); + + public sealed override int GetHashCode() => GetType().GetHashCode(); } } diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/IntrinsicSupport/EqualityComparerHelpers.cs b/external/corert/src/System.Private.CoreLib/src/Internal/IntrinsicSupport/EqualityComparerHelpers.cs index f7a3253c13..73ed2b25df 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/IntrinsicSupport/EqualityComparerHelpers.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/IntrinsicSupport/EqualityComparerHelpers.cs @@ -15,6 +15,9 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; +using Internal.IntrinsicSupport; using Internal.Runtime.Augments; namespace Internal.IntrinsicSupport @@ -23,25 +26,24 @@ namespace Internal.IntrinsicSupport { private static bool ImplementsIEquatable(RuntimeTypeHandle t) { - int interfaceCount = RuntimeAugments.GetInterfaceCount(t); + EETypePtr objectType = t.ToEETypePtr(); + EETypePtr iequatableType = typeof(IEquatable<>).TypeHandle.ToEETypePtr(); + int interfaceCount = objectType.Interfaces.Count; for (int i = 0; i < interfaceCount; i++) { - RuntimeTypeHandle interfaceType = RuntimeAugments.GetInterface(t, i); + EETypePtr interfaceType = objectType.Interfaces[i]; - if (!RuntimeAugments.IsGenericType(interfaceType)) + if (!interfaceType.IsGeneric) continue; - RuntimeTypeHandle genericDefinition; - RuntimeTypeHandle[] genericTypeArgs; - genericDefinition = RuntimeAugments.GetGenericInstantiation(interfaceType, - out genericTypeArgs); - - if (genericDefinition.Equals(typeof(IEquatable<>).TypeHandle)) + if (interfaceType.GenericDefinition == iequatableType) { - if (genericTypeArgs.Length != 1) + var instantiation = interfaceType.Instantiation; + + if (instantiation.Length != 1) continue; - if (genericTypeArgs[0].Equals(t)) + if (instantiation[0] == objectType) { return true; } @@ -53,12 +55,7 @@ namespace Internal.IntrinsicSupport private static bool IsEnum(RuntimeTypeHandle t) { - RuntimeTypeHandle baseType; - bool success = RuntimeAugments.TryGetBaseType(t, out baseType); - if (!success) - return false; - - return baseType.Equals(typeof(System.Enum).TypeHandle); + return t.ToEETypePtr().IsEnum; } // this function utilizes the template type loader to generate new @@ -110,7 +107,7 @@ namespace Internal.IntrinsicSupport //---------------------------------------------------------------------- // target functions of intrinsic replacement in EqualityComparer.get_Default //---------------------------------------------------------------------- - private static EqualityComparer GetUnknownEquatableComparer() + internal static EqualityComparer GetUnknownEquatableComparer() { return (EqualityComparer)GetComparer(typeof(T).TypeHandle); } @@ -140,7 +137,8 @@ namespace Internal.IntrinsicSupport //----------------------------------------------------------------------- // This one is an intrinsic that is used to make enum comparisions more efficient. - private static bool EnumOnlyEquals(T x, T y) where T : struct + [Intrinsic] + internal static bool EnumOnlyEquals(T x, T y) where T : struct { return x.Equals(y); } @@ -165,98 +163,190 @@ namespace Internal.IntrinsicSupport return true; } - - //----------------------------------------------------------------------- - // Implementations of EqualityComparer for the various possible scenarios - //----------------------------------------------------------------------- - - // The methods in this class look identical to the inherited methods, but the calls - // to Equal bind to IEquatable.Equals(T) instead of Object.Equals(Object) - private sealed class GenericEqualityComparer : EqualityComparer where T : IEquatable + // These functions look odd, as they are part of a complex series of compiler intrinsics + // designed to produce very high quality code for equality comparison cases without utilizing + // reflection like other platforms. The major complication is that the specification of + // IndexOf is that it is supposed to use IEquatable if possible, but that requirement + // cannot be expressed in IL directly due to the lack of constraints. + // Instead, specialization at call time is used within the compiler. + // + // General Approach + // - Perform fancy redirection for EqualityComparerHelpers.GetComparerForReferenceTypesOnly(). If T is a reference + // type or UniversalCanon, have this redirect to EqualityComparer.get_Default, Otherwise, use + // the function as is. (will return null in that case) + // - Change the contents of the IndexOf functions to have a pair of loops. One for if + // GetComparerForReferenceTypesOnly returns null, and one for when it does not. + // - If it does not return null, call the EqualityComparer code. + // - If it does return null, use a special function StructOnlyEquals(). + // - Calls to that function result in calls to a pair of helper function in + // EqualityComparerHelpers (StructOnlyEqualsIEquatable, or StructOnlyEqualsNullable) + // depending on whether or not they are the right function to call. + // - The end result is that in optimized builds, we have the same single function compiled size + // characteristics that the old EqualsOnlyComparer.Equals function had, but we maintain + // correctness as well. + [Intrinsic] + internal static EqualityComparer GetComparerForReferenceTypesOnly() { - public sealed override bool Equals(T x, T y) - { - if (x != null) - { - if (y != null) - return x.Equals(y); - return false; - } - - if (y != null) - return false; - - return true; - } - - public sealed override int GetHashCode(T obj) - { - if (obj == null) - return 0; - - return obj.GetHashCode(); - } +#if PROJECTN + // When T is a reference type or a universal canon type, then this will redirect to EqualityComparer.Default. + return null; +#else + return EqualityComparer.Default; +#endif } - private sealed class NullableEqualityComparer : EqualityComparer> where T : struct, IEquatable + [Intrinsic] + internal static bool StructOnlyEquals(T left, T right) { - public sealed override bool Equals(Nullable x, Nullable y) - { - if (x.HasValue) - { - if (y.HasValue) - return x.Value.Equals(y.Value); - return false; - } - - if (y.HasValue) - return false; - - return true; - } - - public sealed override int GetHashCode(Nullable obj) - { - return obj.GetHashCode(); - } - } - - private sealed class EnumEqualityComparer : EqualityComparer where T : struct - { - public sealed override bool Equals(T x, T y) - { - return EqualityComparerHelpers.EnumOnlyEquals(x, y); - } - - public sealed override int GetHashCode(T obj) - { - return obj.GetHashCode(); - } - } - - private sealed class ObjectEqualityComparer : EqualityComparer - { - public sealed override bool Equals(T x, T y) - { - if (x != null) - { - if (y != null) - return x.Equals(y); - return false; - } - - if (y != null) - return false; - - return true; - } - - public sealed override int GetHashCode(T obj) - { - if (obj == null) - return 0; - return obj.GetHashCode(); - } + return left.Equals(right); } } } + +namespace System.Collections.Generic +{ + //----------------------------------------------------------------------- + // Implementations of EqualityComparer for the various possible scenarios + // Names must match other runtimes for serialization + //----------------------------------------------------------------------- + + // The methods in this class look identical to the inherited methods, but the calls + // to Equal bind to IEquatable.Equals(T) instead of Object.Equals(Object) + [Serializable] + public sealed class GenericEqualityComparer : EqualityComparer where T : IEquatable + { + public sealed override bool Equals(T x, T y) + { + if (x != null) + { + if (y != null) + return x.Equals(y); + return false; + } + + if (y != null) + return false; + + return true; + } + + public sealed override int GetHashCode(T obj) + { + if (obj == null) + return 0; + + return obj.GetHashCode(); + } + + // Equals method for the comparer itself. + public sealed override bool Equals(Object obj) => obj is GenericEqualityComparer; + + public sealed override int GetHashCode() => typeof(GenericEqualityComparer).GetHashCode(); + } + + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class NullableEqualityComparer : EqualityComparer> where T : struct, IEquatable + { + public sealed override bool Equals(Nullable x, Nullable y) + { + if (x.HasValue) + { + if (y.HasValue) + return x.Value.Equals(y.Value); + return false; + } + + if (y.HasValue) + return false; + + return true; + } + + public sealed override int GetHashCode(Nullable obj) + { + return obj.GetHashCode(); + } + + + // Equals method for the comparer itself. + public sealed override bool Equals(Object obj) => obj is NullableEqualityComparer; + + public sealed override int GetHashCode() => typeof(NullableEqualityComparer).GetHashCode(); + } + + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class EnumEqualityComparer : EqualityComparer, ISerializable where T : struct + { + public sealed override bool Equals(T x, T y) + { + return EqualityComparerHelpers.EnumOnlyEquals(x, y); + } + + public sealed override int GetHashCode(T obj) + { + return obj.GetHashCode(); + } + + internal EnumEqualityComparer() { } + + private EnumEqualityComparer(SerializationInfo info, StreamingContext context) { } + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + // For back-compat we need to serialize the comparers for enums with underlying types other than int as ObjectEqualityComparer + if (Type.GetTypeCode(Enum.GetUnderlyingType(typeof(T))) != TypeCode.Int32) + { + info.SetType(typeof(ObjectEqualityComparer)); + } + } + + // Equals method for the comparer itself. + public override bool Equals(Object obj) => obj is EnumEqualityComparer; + + public override int GetHashCode() => typeof(EnumEqualityComparer).GetHashCode(); + } + + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class ObjectEqualityComparer : EqualityComparer + { + public sealed override bool Equals(T x, T y) + { + if (x != null) + { + if (y != null) + return x.Equals(y); + return false; + } + + if (y != null) + return false; + + return true; + } + + public sealed override int GetHashCode(T obj) + { + if (obj == null) + return 0; + return obj.GetHashCode(); + } + + // Equals method for the comparer itself. + public sealed override bool Equals(Object obj) + { + if(obj == null) + { + return false; + } + + // This needs to use GetType instead of typeof to avoid infinite recursion in the type loader + return obj.GetType().Equals(GetType()); + } + + // This needs to use GetType instead of typeof to avoid infinite recursion in the type loader + public sealed override int GetHashCode() => GetType().GetHashCode(); + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs index 55e9007e7a..ebf0d31fb3 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs @@ -24,6 +24,8 @@ using System.Globalization; using RhCorElementType = System.Runtime.RuntimeImports.RhCorElementType; +using EnumInfo = Internal.Runtime.Augments.EnumInfo; + namespace Internal.Reflection.Augments { public static class ReflectionAugments @@ -93,6 +95,16 @@ namespace Internal.Reflection.Augments return TypeCode.Object; } + public static Type MakeGenericSignatureType(Type genericTypeDefinition, Type[] genericTypeArguments) + { + return new SignatureConstructedGenericType(genericTypeDefinition, genericTypeArguments); + } + + public static TypeLoadException CreateTypeLoadException(string message, string typeName) + { + return new TypeLoadException(message, typeName); + } + internal static ReflectionCoreCallbacks ReflectionCoreCallbacks { get @@ -113,7 +125,7 @@ namespace Internal.Reflection.Augments // public abstract class ReflectionCoreCallbacks { - public abstract Assembly Load(AssemblyName refName); + public abstract Assembly Load(AssemblyName refName, bool throwOnFileNotFound); public abstract Assembly Load(byte[] rawAssembly, byte[] pdbSymbolStore); public abstract MethodBase GetMethodFromHandle(RuntimeMethodHandle runtimeMethodHandle); @@ -149,5 +161,7 @@ namespace Internal.Reflection.Augments public abstract void MakeTypedReference(object target, FieldInfo[] flds, out Type type, out int offset); public abstract Assembly[] GetLoadedAssemblies(); + + public abstract EnumInfo GetEnumInfo(Type type); } } diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Reflection/Core/NonPortable/RawRuntimeTypeHandleKey.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Reflection/Core/NonPortable/RawRuntimeTypeHandleKey.cs deleted file mode 100644 index 9f8022684e..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Reflection/Core/NonPortable/RawRuntimeTypeHandleKey.cs +++ /dev/null @@ -1,63 +0,0 @@ -// 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.Runtime; -using System.Diagnostics; - -namespace Internal.Reflection.Core.NonPortable -{ - // - // This is essentially a workaround for the fact that neither IntPtr or RuntimeTypeHandle implements IEquatable<>. - // Note that for performance, this key implements equality as a IntPtr compare rather than calling RuntimeImports.AreTypesEquivalent(). - // That is, this key is designed for use in caches, not unifiers. - // - internal struct RawRuntimeTypeHandleKey : IEquatable - { - public RawRuntimeTypeHandleKey(RuntimeTypeHandle runtimeTypeHandle) - { - _runtimeTypeHandle = runtimeTypeHandle; - } - - public RuntimeTypeHandle RuntimeTypeHandle - { - get - { - return _runtimeTypeHandle; - } - } - - public override bool Equals(Object obj) - { - if (!(obj is RawRuntimeTypeHandleKey)) - return false; - return Equals((RawRuntimeTypeHandleKey)obj); - } - - public bool Equals(RawRuntimeTypeHandleKey other) - { - // - // Note: This compares the handle as raw IntPtr's. This is NOT equivalent to testing for semantic type identity. Redhawk can - // and does create multiple EETypes for the same type identity. This is only considered ok here because - // this key is designed for caching Object.GetType() and Type.GetTypeFromHandle() results, not for establishing - // a canonical Type instance for a given semantic type identity. - // - return this.RuntimeTypeHandle.RawValue == other.RuntimeTypeHandle.RawValue; - } - - public override int GetHashCode() - { - // - // Note: This treats the handle as raw IntPtr's. This is NOT equivalent to testing for semantic type identity. Redhawk can - // and does create multiple EETypes for the same type identity. This is only considered ok here because - // this key is designed for caching Object.GetType() and Type.GetTypeFromHandle() results, not for establishing - // a canonical Type instance for a given semantic type identity. - // - return this.RuntimeTypeHandle.RawValue.GetHashCode(); - } - - private RuntimeTypeHandle _runtimeTypeHandle; - } -} - diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Reflection/Core/NonPortable/ReflectionCoreNonPortable.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Reflection/Core/NonPortable/ReflectionCoreNonPortable.cs deleted file mode 100644 index 7886f100f9..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Reflection/Core/NonPortable/ReflectionCoreNonPortable.cs +++ /dev/null @@ -1,29 +0,0 @@ -// 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.Runtime.CompilerServices; - -namespace Internal.Reflection.Core.NonPortable -{ - public static class ReflectionCoreNonPortable - { - public static TypeLoadException CreateTypeLoadException(String message, String typeName) - { - return new TypeLoadException(message, typeName); - } - - internal static Type GetTypeForRuntimeTypeHandle(RuntimeTypeHandle runtimeTypeHandle) - { - return RuntimeTypeUnifier.GetTypeForRuntimeTypeHandle(runtimeTypeHandle); - } - - internal static Type GetRuntimeTypeForEEType(EETypePtr eeType) - { - return RuntimeTypeUnifier.GetTypeForRuntimeTypeHandle(new RuntimeTypeHandle(eeType)); - } - } -} - - diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Reflection/Core/NonPortable/RuntimeTypeUnifier.Internals.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Reflection/Core/NonPortable/RuntimeTypeUnifier.Internals.cs deleted file mode 100644 index 9bc3ca7b91..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Reflection/Core/NonPortable/RuntimeTypeUnifier.Internals.cs +++ /dev/null @@ -1,80 +0,0 @@ -// 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.Runtime; -using System.Diagnostics; -using System.Collections.Generic; -using System.Collections.Concurrent; - -using Internal.Runtime.Augments; - -namespace Internal.Reflection.Core.NonPortable -{ - internal static partial class RuntimeTypeUnifier - { - // - // TypeTable mapping raw RuntimeTypeHandles (normalized or otherwise) to Types. - // - // Unlike most unifier tables, RuntimeTypeHandleToRuntimeTypeCache exists for fast lookup, not unification. It hashes and compares - // on the raw IntPtr value of the RuntimeTypeHandle. Because Redhawk can and does create multiple EETypes for the same - // semantically identical type, the same RuntimeType can legitimately appear twice in this table. The factory, however, - // does a second lookup in the true unifying tables rather than creating the Type itself. - // Thus, the one-to-one relationship between Type reference identity and Type semantic identity is preserved. - // - private sealed class RuntimeTypeHandleToTypeCache : ConcurrentUnifierW - { - private RuntimeTypeHandleToTypeCache() { } - - protected sealed override Type Factory(RawRuntimeTypeHandleKey rawRuntimeTypeHandleKey) - { - RuntimeTypeHandle runtimeTypeHandle = rawRuntimeTypeHandleKey.RuntimeTypeHandle; - - // Desktop compat: Allows Type.GetTypeFromHandle(default(RuntimeTypeHandle)) to map to null. - if (runtimeTypeHandle.RawValue == (IntPtr)0) - return null; - EETypePtr eeType = runtimeTypeHandle.ToEETypePtr(); - ReflectionExecutionDomainCallbacks callbacks = RuntimeAugments.Callbacks; - - if (eeType.IsDefType) - { - if (eeType.IsGenericTypeDefinition) - { - return callbacks.GetNamedTypeForHandle(runtimeTypeHandle, isGenericTypeDefinition: true); - } - else if (eeType.IsGeneric) - { - return callbacks.GetConstructedGenericTypeForHandle(runtimeTypeHandle); - } - else - { - return callbacks.GetNamedTypeForHandle(runtimeTypeHandle, isGenericTypeDefinition: false); - } - } - else if (eeType.IsArray) - { - if (!eeType.IsSzArray) - return callbacks.GetMdArrayTypeForHandle(runtimeTypeHandle, eeType.ArrayRank); - else - return callbacks.GetArrayTypeForHandle(runtimeTypeHandle); - } - else if (eeType.IsPointer) - { - return callbacks.GetPointerTypeForHandle(runtimeTypeHandle); - } - else if (eeType.IsByRef) - { - return callbacks.GetByRefTypeForHandle(runtimeTypeHandle); - } - else - { - throw new ArgumentException(SR.Arg_InvalidRuntimeTypeHandle); - } - } - - public static readonly RuntimeTypeHandleToTypeCache Table = new RuntimeTypeHandleToTypeCache(); - } - } -} - diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Reflection/Core/NonPortable/RuntimeTypeUnifier.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Reflection/Core/NonPortable/RuntimeTypeUnifier.cs index 609a1fc080..37c1e310b2 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Reflection/Core/NonPortable/RuntimeTypeUnifier.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Reflection/Core/NonPortable/RuntimeTypeUnifier.cs @@ -5,16 +5,18 @@ using System; using System.Diagnostics; using System.Collections.Generic; +using System.Collections.Concurrent; using System.Runtime.CompilerServices; +using Internal.Runtime.Augments; + namespace Internal.Reflection.Core.NonPortable { // // ! If you change this policy to not unify all instances, you must change the implementation of Equals/GetHashCode in the runtime type classes. // - // The RuntimeTypeUnifier and its companion RuntimeTypeUnifierEx maintains a record of all System.Type objects - // created by the runtime. The split into two classes is an artifact of reflection being implemented partly in System.Private.CoreLib and - // partly in S.R.R. + // The RuntimeTypeUnifier maintains a record of all System.Type objects created by the runtime. The split into two classes is an artifact of + // reflection being implemented partly in System.Private.CoreLib and partly in S.R.R. // // Though the present incarnation enforces the "one instance per semantic identity rule", its surface area is also designed // to be able to switch to a non-unified model if desired. @@ -42,11 +44,72 @@ namespace Internal.Reflection.Core.NonPortable // Retrieves the unified Type object for given RuntimeTypeHandle (this is basically the Type.GetTypeFromHandle() api without the input validation.) // public static Type GetTypeForRuntimeTypeHandle(RuntimeTypeHandle runtimeTypeHandle) + => RuntimeTypeHandleToTypeCache.Table.GetOrAdd(runtimeTypeHandle.RawValue); + + internal static Type GetRuntimeTypeForEEType(EETypePtr eeType) + => RuntimeTypeHandleToTypeCache.Table.GetOrAdd(eeType.RawValue); + + // + // TypeTable mapping raw RuntimeTypeHandles (normalized or otherwise) to Types. + // + // Unlike most unifier tables, RuntimeTypeHandleToRuntimeTypeCache exists for fast lookup, not unification. It hashes and compares + // on the raw IntPtr value of the RuntimeTypeHandle. Because Redhawk can and does create multiple EETypes for the same + // semantically identical type, the same RuntimeType can legitimately appear twice in this table. The factory, however, + // does a second lookup in the true unifying tables rather than creating the Type itself. + // Thus, the one-to-one relationship between Type reference identity and Type semantic identity is preserved. + // + private sealed class RuntimeTypeHandleToTypeCache : ConcurrentUnifierW { - Type type = RuntimeTypeHandleToTypeCache.Table.GetOrAdd(new RawRuntimeTypeHandleKey(runtimeTypeHandle)); - return type; + private RuntimeTypeHandleToTypeCache() { } + + protected sealed override Type Factory(IntPtr rawRuntimeTypeHandleKey) + { + EETypePtr eeType = new EETypePtr(rawRuntimeTypeHandleKey); + RuntimeTypeHandle runtimeTypeHandle = new RuntimeTypeHandle(eeType); + + // Desktop compat: Allows Type.GetTypeFromHandle(default(RuntimeTypeHandle)) to map to null. + if (runtimeTypeHandle.IsNull) + return null; + + ReflectionExecutionDomainCallbacks callbacks = RuntimeAugments.Callbacks; + + if (eeType.IsDefType) + { + if (eeType.IsGenericTypeDefinition) + { + return callbacks.GetNamedTypeForHandle(runtimeTypeHandle, isGenericTypeDefinition: true); + } + else if (eeType.IsGeneric) + { + return callbacks.GetConstructedGenericTypeForHandle(runtimeTypeHandle); + } + else + { + return callbacks.GetNamedTypeForHandle(runtimeTypeHandle, isGenericTypeDefinition: false); + } + } + else if (eeType.IsArray) + { + if (!eeType.IsSzArray) + return callbacks.GetMdArrayTypeForHandle(runtimeTypeHandle, eeType.ArrayRank); + else + return callbacks.GetArrayTypeForHandle(runtimeTypeHandle); + } + else if (eeType.IsPointer) + { + return callbacks.GetPointerTypeForHandle(runtimeTypeHandle); + } + else if (eeType.IsByRef) + { + return callbacks.GetByRefTypeForHandle(runtimeTypeHandle); + } + else + { + throw new ArgumentException(SR.Arg_InvalidRuntimeTypeHandle); + } + } + + public static readonly RuntimeTypeHandleToTypeCache Table = new RuntimeTypeHandleToTypeCache(); } } } - - diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/AsyncCausalityTracer.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/AsyncCausalityTracer.cs new file mode 100644 index 0000000000..bcd9583b06 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/AsyncCausalityTracer.cs @@ -0,0 +1,46 @@ +// 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; + +namespace Internal.Runtime.Augments +{ + public enum CausalityRelation + { + AssignDelegate = 0, + Join = 1, + Choice = 2, + Cancel = 3, + Error = 4, + } + + public enum CausalitySource + { + Application = 0, + Library = 1, + System = 2, + } + + public enum CausalityTraceLevel + { + Required = 0, + Important = 1, + Verbose = 2, + } + + public enum AsyncStatus + { + Started = 0, + Completed = 1, + Canceled = 2, + Error = 3, + } + + public enum CausalitySynchronousWork + { + CompletionNotification = 0, + ProgressNotification = 1, + Execution = 2, + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/DesktopSupportCallbacks.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/DesktopSupportCallbacks.cs deleted file mode 100644 index 2a1c78308a..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/DesktopSupportCallbacks.cs +++ /dev/null @@ -1,30 +0,0 @@ -// 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 Internal.Runtime.CompilerServices; - -namespace Internal.Runtime.Augments -{ - /// - /// This helper class is used to provide desktop support quirks to the runtime. - /// This is currently used to provide resources to console lab tests. To activate the quirks, - /// set up an instance of a class derived from this one using the method - /// - /// Internal.Runtime.Augments.RuntimeAugments.InitializeDesktopSupportCallbacks(DesktopSupportCallbacks callbacks); - /// - /// - [CLSCompliant(false)] - public abstract class DesktopSupportCallbacks - { - /// - /// Helper function to open an "application package" file. It actually returns a Stream - /// but we cannot use that signature here due to layering limitations. - /// - /// File path / name - /// An initialized Stream instance or null when the file doesn't exist; - /// throws when compat quirks are not enabled - public abstract object OpenFileIfExists(string fileName); - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnumInfo.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnumInfo.cs index 7a65936d1b..ff1d196359 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnumInfo.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnumInfo.cs @@ -4,22 +4,128 @@ using System; using System.Collections.Generic; - -using Internal.Reflection.Core.NonPortable; +using System.Diagnostics; +using System.Reflection; +using System.Runtime; +using System.Runtime.CompilerServices; namespace Internal.Runtime.Augments { -#pragma warning disable 3003 - - // - // Abstract base for reflection-based information regarding an Enum type. - // - public abstract class EnumInfo + public sealed class EnumInfo { - public abstract Type UnderlyingType { get; } - public abstract Array Values { get; } - public abstract KeyValuePair[] NamesAndValues { get; } - public abstract bool HasFlagsAttribute { get; } + public EnumInfo(Type enumType) + { + Debug.Assert(enumType != null); + Debug.Assert(enumType.IsRuntimeImplemented()); + Debug.Assert(enumType.IsEnum); + + UnderlyingType = ComputeUnderlyingType(enumType); + + FieldInfo[] fields = enumType.GetFields(BindingFlags.Public | BindingFlags.Static); + int numValues = fields.Length; + object[] rawValues = new object[numValues]; + KeyValuePair[] namesAndValues = new KeyValuePair[numValues]; + for (int i = 0; i < numValues; i++) + { + FieldInfo field = fields[i]; + object rawValue = field.GetRawConstantValue(); + rawValues[i] = rawValue; + + ulong rawUnboxedValue; + if (rawValue is ulong) + { + rawUnboxedValue = (ulong)rawValue; + } + else + { + // This conversion is this way for compatibility: do a value-preseving cast to long - then store (and compare) as ulong. This affects + // the order in which the Enum apis return names and values. + rawUnboxedValue = (ulong)(((IConvertible)rawValue).ToInt64(null)); + } + namesAndValues[i] = new KeyValuePair(field.Name, rawUnboxedValue); + } + + Array.Sort(keys: namesAndValues, items: rawValues, comparer: NamesAndValueComparer.Default); + NamesAndValues = namesAndValues; + + // Create the unboxed version of values for the Values property to return. (We didn't do this earlier because + // declaring "rawValues" as "Array" would prevent us from using the generic overload of Array.Sort()). + // + // The array element type is the underlying type, not the enum type. (The enum type could be an open generic.) + Values = Array.CreateInstance(UnderlyingType, numValues); + Array.Copy(rawValues, Values, numValues); + + HasFlagsAttribute = enumType.IsDefined(typeof(FlagsAttribute), inherit: false); + } + + internal Type UnderlyingType { get; } + internal Array Values { get; } + internal KeyValuePair[] NamesAndValues { get; } + internal bool HasFlagsAttribute { get; } + + private static RuntimeImports.RhCorElementType ComputeCorElementType(Type enumType) + { + if (enumType.ContainsGenericParameters) + { + // This is an open generic enum (typeof(Outer<>).NestedEnum). We cannot safely call EETypePtr.CorElementType for this case so fall back to Reflection. + FieldInfo[] candidates = enumType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly); + if (candidates.Length == 0) + throw RuntimeAugments.Callbacks.CreateMissingMetadataException(enumType); // Most likely cause. + if (candidates.Length > 1) + throw new BadImageFormatException(); + enumType = candidates[0].FieldType; + } + return enumType.TypeHandle.ToEETypePtr().CorElementType; + } + + private static Type ComputeUnderlyingType(Type enumType) + { + RuntimeImports.RhCorElementType corElementType = ComputeCorElementType(enumType); + switch (corElementType) + { + case RuntimeImports.RhCorElementType.ELEMENT_TYPE_BOOLEAN: + return CommonRuntimeTypes.Boolean; + case RuntimeImports.RhCorElementType.ELEMENT_TYPE_CHAR: + return CommonRuntimeTypes.Char; + case RuntimeImports.RhCorElementType.ELEMENT_TYPE_I1: + return CommonRuntimeTypes.SByte; + case RuntimeImports.RhCorElementType.ELEMENT_TYPE_U1: + return CommonRuntimeTypes.Byte; + case RuntimeImports.RhCorElementType.ELEMENT_TYPE_I2: + return CommonRuntimeTypes.Int16; + case RuntimeImports.RhCorElementType.ELEMENT_TYPE_U2: + return CommonRuntimeTypes.UInt16; + case RuntimeImports.RhCorElementType.ELEMENT_TYPE_I4: + return CommonRuntimeTypes.Int32; + case RuntimeImports.RhCorElementType.ELEMENT_TYPE_U4: + return CommonRuntimeTypes.UInt32; + case RuntimeImports.RhCorElementType.ELEMENT_TYPE_I8: + return CommonRuntimeTypes.Int64; + case RuntimeImports.RhCorElementType.ELEMENT_TYPE_U8: + return CommonRuntimeTypes.UInt64; + default: + throw new NotSupportedException(); + } + } + + // + // Sort comparer for NamesAndValues + // + private sealed class NamesAndValueComparer : IComparer> + { + public int Compare(KeyValuePair kv1, KeyValuePair kv2) + { + ulong x = kv1.Value; + ulong y = kv2.Value; + if (x < y) + return -1; + else if (x > y) + return 1; + else + return 0; + } + + public static IComparer> Default = new NamesAndValueComparer(); + } } } - diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.CoreRT.Win32.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.CoreRT.Win32.cs new file mode 100644 index 0000000000..5f6a15f01f --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.CoreRT.Win32.cs @@ -0,0 +1,17 @@ +// 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.Runtime; + +namespace Internal.Runtime.Augments +{ + public static partial class EnvironmentAugments + { + private static void ExitRaw() + { + Interop.Kernel32.ExitProcess(s_latchedExitCode); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.CoreRT.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.CoreRT.cs index 8174af9b59..1939702e07 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.CoreRT.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.CoreRT.cs @@ -3,50 +3,22 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections; using System.Runtime; -using System.Runtime.CompilerServices; namespace Internal.Runtime.Augments { /// For internal use only. Exposes runtime functionality to the Environments implementation in corefx. public static partial class EnvironmentAugments { - public static int CurrentManagedThreadId => System.Threading.ManagedThreadId.Current; - public static void FailFast(string message, Exception error) => RuntimeExceptionHelpers.FailFast(message, error); - public static void Exit(int exitCode) { -#if CORERT s_latchedExitCode = exitCode; ShutdownCore(); RuntimeImports.RhpShutdown(); - Interop.ExitProcess(s_latchedExitCode); -#else - // This needs to be implemented for ProjectN. - throw new PlatformNotSupportedException(); -#endif - } - - internal static void ShutdownCore() - { - // Here we'll handle AppDomain.ProcessExit, shut down threading etc. - } - - private static int s_latchedExitCode; - public static int ExitCode - { - get - { - return s_latchedExitCode; - } - set - { - s_latchedExitCode = value; - } + ExitRaw(); } private static string[] s_commandLineArgs; @@ -58,38 +30,7 @@ namespace Internal.Runtime.Augments public static string[] GetCommandLineArgs() { - return (string[])s_commandLineArgs?.Clone(); + return (string[])s_commandLineArgs.Clone(); } - - public static bool HasShutdownStarted => false; // .NET Core does not have shutdown finalization - - public static string StackTrace - { - // Disable inlining to have predictable stack frame to skip - [MethodImpl(MethodImplOptions.NoInlining)] - get - { - // RhGetCurrentThreadStackTrace returns the number of frames(cFrames) added to input buffer. - // It returns a negative value, -cFrames which is the required array size, if the buffer is too small. - // Initial array length is deliberately chosen to be 0 so that we reallocate to exactly the right size - // for StackFrameHelper.FormatStackTrace call. If we want to do this optimistically with one call change - // FormatStackTrace to accept an explicit length. - IntPtr[] frameIPs = Array.Empty(); - int cFrames = RuntimeImports.RhGetCurrentThreadStackTrace(frameIPs); - if (cFrames < 0) - { - frameIPs = new IntPtr[-cFrames]; - cFrames = RuntimeImports.RhGetCurrentThreadStackTrace(frameIPs); - if (cFrames < 0) - { - return ""; - } - } - - return Internal.Diagnostics.StackTraceHelper.FormatStackTrace(frameIPs, 1, true); - } - } - - public static int TickCount => Environment.TickCount; } } diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.FromRegistry.Win32.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.FromRegistry.Win32.cs new file mode 100644 index 0000000000..fdeabbc78d --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.FromRegistry.Win32.cs @@ -0,0 +1,90 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; + +using Microsoft.Win32; + +namespace Internal.Runtime.Augments +{ + /// For internal use only. Exposes runtime functionality to the Environments implementation in corefx. + public static partial class EnvironmentAugments + { + private static string GetEnvironmentVariableFromRegistry(string variable, bool fromMachine) + { + Debug.Assert(variable != null); + using (RegistryKey environmentKey = OpenEnvironmentKeyIfExists(fromMachine: fromMachine, writable: false)) + { + return environmentKey?.GetValue(variable) as string; + } + } + + private static void SetEnvironmentVariableFromRegistry(string variable, string value, bool fromMachine) + { + Debug.Assert(variable != null); + + // User-wide environment variables stored in the registry are limited to 255 chars for the environment variable name. + const int MaxUserEnvVariableLength = 255; + if (variable.Length >= MaxUserEnvVariableLength) + throw new ArgumentException(SR.Argument_LongEnvVarValue, nameof(variable)); + + using (RegistryKey environmentKey = OpenEnvironmentKeyIfExists(fromMachine: fromMachine, writable: true)) + { + if (environmentKey != null) + { + if (value == null) + { + environmentKey.DeleteValue(variable, throwOnMissingValue: false); + } + else + { + environmentKey.SetValue(variable, value); + } + } + } + + // send a WM_SETTINGCHANGE message to all windows + IntPtr r = Interop.User32.SendMessageTimeout(new IntPtr(Interop.User32.HWND_BROADCAST), Interop.User32.WM_SETTINGCHANGE, IntPtr.Zero, "Environment", 0, 1000, IntPtr.Zero); + if (r == IntPtr.Zero) + Debug.Fail("SetEnvironmentVariable failed: " + Marshal.GetLastWin32Error()); + } + + private static IEnumerable> EnumerateEnvironmentVariablesFromRegistry(bool fromMachine) + { + using (RegistryKey environmentKey = OpenEnvironmentKeyIfExists(fromMachine: fromMachine, writable: false)) + { + if (environmentKey != null) + { + foreach (string name in environmentKey.GetValueNames()) + { + string value = environmentKey.GetValue(name, string.Empty).ToString(); + yield return new KeyValuePair(name, value); + } + } + } + } + + private static RegistryKey OpenEnvironmentKeyIfExists(bool fromMachine, bool writable) + { + RegistryKey baseKey; + string keyName; + + if (fromMachine) + { + baseKey = Registry.LocalMachine; + keyName = @"System\CurrentControlSet\Control\Session Manager\Environment"; + } + else + { + baseKey = Registry.CurrentUser; + keyName = "Environment"; + } + + return baseKey.OpenSubKey(keyName, writable: writable); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.FromRegistry.WithoutRegistry.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.FromRegistry.WithoutRegistry.cs new file mode 100644 index 0000000000..4867bda854 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.FromRegistry.WithoutRegistry.cs @@ -0,0 +1,31 @@ +// 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.Collections.Generic; +using System.Diagnostics; + +namespace Internal.Runtime.Augments +{ + /// For internal use only. Exposes runtime functionality to the Environments implementation in corefx. + public static partial class EnvironmentAugments + { + private static string GetEnvironmentVariableFromRegistry(string variable, bool fromMachine) + { + Debug.Assert(variable != null); + return null; // Systems without registries pretend that the registry environment subkeys are empty lists. + } + + private static void SetEnvironmentVariableFromRegistry(string variable, string value, bool fromMachine) + { + Debug.Assert(variable != null); + return; // Systems without registries pretend that the registry environment subkeys are empty lists that throw all write requests into a black hole. + } + + private static IEnumerable> EnumerateEnvironmentVariablesFromRegistry(bool fromMachine) + { + return Array.Empty>(); // Systems without registries pretend that the registry environment subkeys are empty lists. + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.ProjectN.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.ProjectN.cs new file mode 100644 index 0000000000..f49f9cf924 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.ProjectN.cs @@ -0,0 +1,21 @@ +// 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.Runtime; + +namespace Internal.Runtime.Augments +{ + /// For internal use only. Exposes runtime functionality to the Environments implementation in corefx. + public static partial class EnvironmentAugments + { + public static void Exit(int exitCode) + { + // This needs to be implemented for ProjectN. + throw new PlatformNotSupportedException(); + } + + public static string[] GetCommandLineArgs() => CommandLine.InternalCreateCommandLine(includeArg0: true); + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Unix.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Unix.cs index f499dce71b..6d28d393b2 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Unix.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Unix.cs @@ -32,5 +32,10 @@ namespace Internal.Runtime.Augments return Array.Empty>(); } + + private static void ExitRaw() + { + Interop.Sys.Exit(s_latchedExitCode); + } } } diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Windows.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Windows.cs index f244e29a3d..71b5063fee 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Windows.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Windows.cs @@ -61,7 +61,7 @@ namespace Internal.Runtime.Augments // which is not accurate. throw new ArgumentException(SR.Argument_LongEnvVarValue); default: - throw new ArgumentException(Win32Marshal.GetMessage(errorCode)); + throw new ArgumentException(Interop.Kernel32.GetMessage(errorCode)); } } } diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.cs index e216725803..02c6717a15 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections; +using System.Diagnostics; using System.Collections.Generic; using System.Runtime; using System.Runtime.CompilerServices; @@ -20,12 +20,39 @@ namespace Internal.Runtime.Augments return GetEnvironmentVariableCore(variable); } + public static string GetEnvironmentVariable(string variable, EnvironmentVariableTarget target) + { + if (target == EnvironmentVariableTarget.Process) + return GetEnvironmentVariable(variable); + + if (variable == null) + throw new ArgumentNullException(nameof(variable)); + + bool fromMachine = ValidateAndConvertRegistryTarget(target); + return GetEnvironmentVariableFromRegistry(variable, fromMachine: fromMachine); + } + public static void SetEnvironmentVariable(string variable, string value) { ValidateVariableAndValue(variable, ref value); + SetEnvironmentVariableCore(variable, value); } + public static void SetEnvironmentVariable(string variable, string value, EnvironmentVariableTarget target) + { + if (target == EnvironmentVariableTarget.Process) + { + SetEnvironmentVariable(variable, value); + return; + } + + ValidateVariableAndValue(variable, ref value); + + bool fromMachine = ValidateAndConvertRegistryTarget(target); + SetEnvironmentVariableFromRegistry(variable, value, fromMachine: fromMachine); + } + private static void ValidateVariableAndValue(string variable, ref string value) { const int MaxEnvVariableValueLength = 32767; @@ -56,37 +83,78 @@ namespace Internal.Runtime.Augments } } - // TODO Perf: Once CoreCLR gets PopulateEnvironmentVariables(), get rid of GetEnvironmentVariables() and have - // corefx call PopulateEnvironmentVariables() instead so we don't have to create a dictionary just to copy it into - // another dictionary. - public static IDictionary GetEnvironmentVariables() - { - IDictionary dictionary = new Dictionary(EnumerateEnvironmentVariables()); - return dictionary; - } - - public static string GetEnvironmentVariable(string variable, EnvironmentVariableTarget target) + public static IEnumerable> EnumerateEnvironmentVariables(EnvironmentVariableTarget target) { if (target == EnvironmentVariableTarget.Process) - return GetEnvironmentVariable(variable); - throw new NotImplementedException(); + return EnumerateEnvironmentVariables(); + + bool fromMachine = ValidateAndConvertRegistryTarget(target); + return EnumerateEnvironmentVariablesFromRegistry(fromMachine: fromMachine); } - public static void SetEnvironmentVariable(string variable, string value, EnvironmentVariableTarget target) + private static bool ValidateAndConvertRegistryTarget(EnvironmentVariableTarget target) { - if (target == EnvironmentVariableTarget.Process) + Debug.Assert(target != EnvironmentVariableTarget.Process); + if (target == EnvironmentVariableTarget.Machine) + return true; + else if (target == EnvironmentVariableTarget.User) + return false; + else + throw new ArgumentOutOfRangeException(nameof(target), target, SR.Format(SR.Arg_EnumIllegalVal, target)); + } + + public static int CurrentManagedThreadId => System.Threading.ManagedThreadId.Current; + public static void FailFast(string message, Exception error) => RuntimeExceptionHelpers.FailFast(message, error); + + internal static void ShutdownCore() + { + // Here we'll handle AppDomain.ProcessExit, shut down threading etc. + } + + private static int s_latchedExitCode; + public static int ExitCode + { + get { - SetEnvironmentVariable(variable, value); - return; + return s_latchedExitCode; + } + set + { + s_latchedExitCode = value; } - throw new NotImplementedException(); } - public static IDictionary GetEnvironmentVariables(EnvironmentVariableTarget target) + public static bool HasShutdownStarted => false; // .NET Core does not have shutdown finalization + + public static string StackTrace { - if (target == EnvironmentVariableTarget.Process) - return GetEnvironmentVariables(); - throw new NotImplementedException(); + // Disable inlining to have predictable stack frame to skip + [MethodImpl(MethodImplOptions.NoInlining)] + get + { + // RhGetCurrentThreadStackTrace returns the number of frames(cFrames) added to input buffer. + // It returns a negative value, -cFrames which is the required array size, if the buffer is too small. + // Initial array length is deliberately chosen to be 0 so that we reallocate to exactly the right size + // for StackFrameHelper.FormatStackTrace call. If we want to do this optimistically with one call change + // FormatStackTrace to accept an explicit length. + IntPtr[] frameIPs = Array.Empty(); + int cFrames = RuntimeImports.RhGetCurrentThreadStackTrace(frameIPs); + if (cFrames < 0) + { + frameIPs = new IntPtr[-cFrames]; + cFrames = RuntimeImports.RhGetCurrentThreadStackTrace(frameIPs); + if (cFrames < 0) + { + return ""; + } + } + + return Internal.Diagnostics.StackTraceHelper.FormatStackTrace(frameIPs, 1, true); + } } + + public static int TickCount => Environment.TickCount; + + public static int ProcessorCount => Environment.ProcessorCount; } } diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/InteropCallbacks.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/InteropCallbacks.cs index 4dd2c4369e..7424ebcc5c 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/InteropCallbacks.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/InteropCallbacks.cs @@ -12,5 +12,15 @@ namespace Internal.Runtime.Augments public abstract class InteropCallbacks { public abstract bool TryGetMarshallerDataForDelegate(RuntimeTypeHandle delegateTypeHandle, out McgPInvokeDelegateData delegateData); + + public abstract bool TryGetStructUnmarshalStub(RuntimeTypeHandle structureTypeHandle, out IntPtr unmarshalStub); + + public abstract bool TryGetStructMarshalStub(RuntimeTypeHandle structureTypeHandle, out IntPtr marshalStub); + + public abstract bool TryGetDestroyStructureStub(RuntimeTypeHandle structureTypeHandle, out IntPtr destroyStructureStub, out bool hasInvalidLayout); + + public abstract bool TryGetStructFieldOffset(RuntimeTypeHandle structureTypeHandle, string fieldName, out bool structExists, out uint offset); + + public abstract bool TryGetStructUnsafeStructSize(RuntimeTypeHandle structureTypeHandle, out int size); } } diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs index 94b79e2213..cf6b61bfe7 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs @@ -46,9 +46,8 @@ namespace Internal.Runtime.Augments // Flotsam and jetsam. public abstract Exception CreateMissingMetadataException(Type typeWithMissingMetadata); - public abstract EnumInfo GetEnumInfoIfAvailable(Type enumType); public abstract String GetBetterDiagnosticInfoIfAvailable(RuntimeTypeHandle runtimeTypeHandle); - public abstract String GetMethodNameFromStartAddressIfAvailable(IntPtr methodStartAddress); + public abstract MethodBase GetMethodBaseFromStartAddressIfAvailable(IntPtr methodStartAddress); public abstract int ValueTypeGetHashCodeUsingReflection(object valueType); public abstract bool ValueTypeEqualsUsingReflection(object left, object right); diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs index 63f21775c1..c8b3b83a15 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs @@ -22,9 +22,12 @@ using System.Runtime; using System.Reflection; using System.Diagnostics; using System.Collections.Generic; +using System.IO; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; +using System.Threading; +using Internal.Runtime; using Internal.Reflection.Core.NonPortable; using Internal.Runtime.CompilerServices; @@ -32,19 +35,12 @@ using Volatile = System.Threading.Volatile; namespace Internal.Runtime.Augments { - public enum CanonTypeKind - { - NormalCanon, - UniversalCanon - } - public static class RuntimeAugments { /// - /// Callbacks used for desktop emulation in console lab tests. Initialized by - /// InitializeDesktopSupport; currently the only provided method is OpenFileIfExists. + /// Callbacks used for metadata-based stack trace resolution. /// - private static DesktopSupportCallbacks s_desktopSupportCallbacks; + private static StackTraceMetadataCallbacks s_stackTraceMetadataCallbacks; //============================================================================================== // One-time initialization. @@ -68,9 +64,9 @@ namespace Internal.Runtime.Augments } [CLSCompliant(false)] - public static void InitializeDesktopSupport(DesktopSupportCallbacks callbacks) + public static void InitializeStackTraceMetadataSupport(StackTraceMetadataCallbacks callbacks) { - s_desktopSupportCallbacks = callbacks; + s_stackTraceMetadataCallbacks = callbacks; } //============================================================================================== @@ -85,9 +81,6 @@ namespace Internal.Runtime.Augments // Strings: The .ctor performs both the construction and initialization // and compiler special cases these. // - // IntPtr/UIntPtr: These have intrinsic constructors and it happens, special-casing these in the class library - // is the lesser evil compared to special-casing them in the toolchain. - // // Nullable: the boxed result is the underlying type rather than Nullable so the constructor // cannot truly initialize it. // @@ -98,8 +91,6 @@ namespace Internal.Runtime.Augments EETypePtr eeType = typeHandle.ToEETypePtr(); if (eeType.IsNullable || eeType == EETypePtr.EETypePtrOf() - || eeType == EETypePtr.EETypePtrOf() - || eeType == EETypePtr.EETypePtrOf() ) return null; return RuntimeImports.RhNewObject(eeType); @@ -208,9 +199,9 @@ namespace Internal.Runtime.Augments // // Helper to extract the artifact that uniquely identifies a method in the runtime mapping tables. // - public static IntPtr GetDelegateLdFtnResult(Delegate d, out RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate, out bool isOpenResolver) + public static IntPtr GetDelegateLdFtnResult(Delegate d, out RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate, out bool isOpenResolver, out bool isInterpreterEntrypoint) { - return d.GetFunctionPointer(out typeOfFirstParameterIfInstanceDelegate, out isOpenResolver); + return d.GetFunctionPointer(out typeOfFirstParameterIfInstanceDelegate, out isOpenResolver, out isInterpreterEntrypoint); } public static void GetDelegateData(Delegate delegateObj, out object firstParameter, out object helperObject, out IntPtr extraFunctionPointerOrData, out IntPtr functionPointer) @@ -223,12 +214,7 @@ namespace Internal.Runtime.Augments public static int GetLoadedModules(TypeManagerHandle[] resultArray) { - return (int)RuntimeImports.RhGetLoadedModules(resultArray); - } - - public static int GetLoadedOSModules(IntPtr[] resultArray) - { - return (int)RuntimeImports.RhGetLoadedOSModules(resultArray); + return Internal.Runtime.CompilerHelpers.StartupCodeHelpers.GetLoadedModules(resultArray); } public static IntPtr GetOSModuleFromPointer(IntPtr pointerVal) @@ -256,9 +242,9 @@ namespace Internal.Runtime.Augments return new RuntimeTypeHandle(new EETypePtr(ldTokenResult)); } - public static unsafe IntPtr GetThreadStaticFieldAddress(RuntimeTypeHandle typeHandle, IntPtr fieldCookie) + public static unsafe IntPtr GetThreadStaticFieldAddress(RuntimeTypeHandle typeHandle, int threadStaticsBlockOffset, int fieldOffset) { - return new IntPtr(RuntimeImports.RhGetThreadStaticFieldAddress(typeHandle.ToEETypePtr(), fieldCookie)); + return new IntPtr(RuntimeImports.RhGetThreadStaticFieldAddress(typeHandle.ToEETypePtr(), threadStaticsBlockOffset, fieldOffset)); } public static unsafe void StoreValueTypeField(IntPtr address, Object fieldValue, RuntimeTypeHandle fieldType) @@ -266,11 +252,26 @@ namespace Internal.Runtime.Augments RuntimeImports.RhUnbox(fieldValue, *(void**)&address, fieldType.ToEETypePtr()); } + public static unsafe ref byte GetRawData(object obj) + { + return ref obj.GetRawData(); + } + public static unsafe Object LoadValueTypeField(IntPtr address, RuntimeTypeHandle fieldType) { return RuntimeImports.RhBox(fieldType.ToEETypePtr(), *(void**)&address); } + public static unsafe object LoadPointerTypeField(IntPtr address, RuntimeTypeHandle fieldType) + { + return Pointer.Box(*(void**)address, Type.GetTypeFromHandle(fieldType)); + } + + public static unsafe void StoreValueTypeField(ref byte address, Object fieldValue, RuntimeTypeHandle fieldType) + { + RuntimeImports.RhUnbox(fieldValue, ref address, fieldType.ToEETypePtr()); + } + public static unsafe void StoreValueTypeField(Object obj, int fieldOffset, Object fieldValue, RuntimeTypeHandle fieldType) { fixed (IntPtr* pObj = &obj.m_pEEType) @@ -291,6 +292,16 @@ namespace Internal.Runtime.Augments } } + public static unsafe Object LoadPointerTypeField(Object obj, int fieldOffset, RuntimeTypeHandle fieldType) + { + fixed (IntPtr* pObj = &obj.m_pEEType) + { + IntPtr pData = (IntPtr)pObj; + IntPtr pField = pData + fieldOffset; + return LoadPointerTypeField(pField, fieldType); + } + } + public static unsafe void StoreReferenceTypeField(IntPtr address, Object fieldValue) { Volatile.Write(ref Unsafe.As(ref *(IntPtr*)address), fieldValue); @@ -354,6 +365,19 @@ namespace Internal.Runtime.Augments return Unsafe.As(ref Unsafe.Add(ref typedReference.Value, fieldOffset)); } + [CLSCompliant(false)] + public static object LoadPointerTypeFieldValueFromValueType(TypedReference typedReference, int fieldOffset, RuntimeTypeHandle fieldTypeHandle) + { + Debug.Assert(TypedReference.TargetTypeToken(typedReference).ToEETypePtr().IsValueType); + Debug.Assert(fieldTypeHandle.ToEETypePtr().IsPointer); + + IntPtr ptrValue = Unsafe.As(ref Unsafe.Add(ref typedReference.Value, fieldOffset)); + unsafe + { + return Pointer.Box((void*)ptrValue, Type.GetTypeFromHandle(fieldTypeHandle)); + } + } + public static unsafe int ObjectHeaderSize => sizeof(EETypePtr); [DebuggerGuidedStepThroughAttribute] @@ -366,6 +390,7 @@ namespace Internal.Runtime.Augments object defaultParametersContext, object[] parameters, BinderBundle binderBundle, + bool wrapInTargetInvocationException, bool invokeMethodHelperIsThisCall, bool methodToCallIsThisCall) { @@ -378,6 +403,7 @@ namespace Internal.Runtime.Augments defaultParametersContext, parameters, binderBundle, + wrapInTargetInvocationException, invokeMethodHelperIsThisCall, methodToCallIsThisCall); System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); @@ -558,8 +584,10 @@ namespace Internal.Runtime.Augments return (int)typeHandle.ToEETypePtr().ValueTypeSize; } + [Intrinsic] public static RuntimeTypeHandle GetCanonType(CanonTypeKind kind) { +#if PROJECTN switch (kind) { case CanonTypeKind.NormalCanon: @@ -570,6 +598,10 @@ namespace Internal.Runtime.Augments Debug.Assert(false); return default(RuntimeTypeHandle); } +#else + // Compiler needs to expand this. This is not expressible in IL. + throw new NotSupportedException(); +#endif } public static RuntimeTypeHandle GetGenericDefinition(RuntimeTypeHandle typeHandle) @@ -612,6 +644,8 @@ namespace Internal.Runtime.Augments return typeHandle.ToEETypePtr().IsArray; } + public static bool IsByRefLike(RuntimeTypeHandle typeHandle) => typeHandle.ToEETypePtr().IsByRefLike; + public static bool IsDynamicType(RuntimeTypeHandle typeHandle) { return typeHandle.ToEETypePtr().IsDynamicType; @@ -719,9 +753,7 @@ namespace Internal.Runtime.Augments public static String TryGetFullPathToMainApplication() { Func delegateToAnythingInsideMergedApp = TryGetFullPathToMainApplication; - RuntimeTypeHandle thDummy; - bool boolDummy; - IntPtr ipToAnywhereInsideMergedApp = delegateToAnythingInsideMergedApp.GetFunctionPointer(out thDummy, out boolDummy); + IntPtr ipToAnywhereInsideMergedApp = delegateToAnythingInsideMergedApp.GetFunctionPointer(out RuntimeTypeHandle _, out bool _, out bool _); IntPtr moduleBase = RuntimeImports.RhGetOSModuleFromPointer(ipToAnywhereInsideMergedApp); return TryGetFullPathToApplicationModule(moduleBase); } @@ -768,15 +800,15 @@ namespace Internal.Runtime.Augments return RuntimeImports.RhGetCodeTarget(functionPointer); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr RuntimeCacheLookup(IntPtr context, IntPtr signature, int registeredResolutionFunction, object contextObject, out IntPtr auxResult) + public static IntPtr GetTargetOfUnboxingAndInstantiatingStub(IntPtr functionPointer) { - return TypeLoaderExports.RuntimeCacheLookupInCache(context, signature, registeredResolutionFunction, contextObject, out auxResult); + return RuntimeImports.RhGetTargetOfUnboxingAndInstantiatingStub(functionPointer); } - public static int RegisterResolutionFunctionWithRuntimeCache(IntPtr functionPointer) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IntPtr RuntimeCacheLookup(IntPtr context, IntPtr signature, RuntimeObjectFactory factory, object contextObject, out IntPtr auxResult) { - return TypeLoaderExports.RegisterResolutionFunction(functionPointer); + return TypeLoaderExports.RuntimeCacheLookupInCache(context, signature, factory, contextObject, out auxResult); } //============================================================================================== @@ -833,6 +865,27 @@ namespace Internal.Runtime.Augments } } + internal static StackTraceMetadataCallbacks StackTraceCallbacksIfAvailable + { + get + { + return s_stackTraceMetadataCallbacks; + } + } + + public static string TryGetMethodDisplayStringFromIp(IntPtr ip) + { + StackTraceMetadataCallbacks callbacks = StackTraceCallbacksIfAvailable; + if (callbacks == null) + return null; + + ip = RuntimeImports.RhFindMethodStartAddress(ip); + if (ip == IntPtr.Zero) + return null; + + return callbacks.TryGetMethodNameFromStartAddress(ip); + } + private static volatile ReflectionExecutionDomainCallbacks s_reflectionExecutionDomainCallbacks; private static TypeLoaderCallbacks s_typeLoaderCallbacks; private static InteropCallbacks s_interopCallbacks; @@ -907,22 +960,6 @@ namespace Internal.Runtime.Augments return RuntimeImports.RhGetThunkSize(); } - public static unsafe IntPtr GetRawAddrOfPinnedObject(IntPtr gcHandleAsIntPtr) - { - GCHandle gcHandle = (GCHandle)gcHandleAsIntPtr; - Debug.Assert(gcHandle.IsPinned()); - - Object target = gcHandle.Target; - - if (target == null) - return IntPtr.Zero; - - fixed (IntPtr* pTargetEEType = &target.m_pEEType) - { - return (IntPtr)pTargetEEType; - } - } - [DebuggerStepThrough] /* TEMP workaround due to bug 149078 */ [MethodImpl(MethodImplOptions.NoInlining)] @@ -939,26 +976,6 @@ namespace Internal.Runtime.Augments RuntimeImports.RhCallDescrWorkerNative(callDescr); } - /// - /// This method opens a file if it exists. For console apps, ILC will inject a call to - /// InitializeDesktopSupport in StartupCodeTrigger. This will set up the - /// _desktopSupportCallbacks that can be then used to open files. - /// The return type is actually a Stream (which we cannot use here due to layering). - /// This mechanism shields AppX builds from the cost of merging in System.IO.FileSystem. - /// - /// File path / name - /// An initialized Stream instance or null if the file doesn't exist; - /// throws when the desktop compat quirks are not enabled - public static object OpenFileIfExists(string path) - { - if (s_desktopSupportCallbacks == null) - { - throw new NotSupportedException(); - } - - return s_desktopSupportCallbacks.OpenFileIfExists(path); - } - public static Delegate CreateObjectArrayDelegate(Type delegateType, Func invoker) { return Delegate.CreateObjectArrayDelegate(delegateType, invoker); @@ -967,10 +984,15 @@ namespace Internal.Runtime.Augments [System.Runtime.InteropServices.McgIntrinsicsAttribute] internal class RawCalliHelper { + [DebuggerHidden] + [DebuggerStepThrough] public static unsafe void Call(System.IntPtr pfn, void* arg1, ref T arg2) { // This will be filled in by an IL transform } + + [DebuggerHidden] + [DebuggerStepThrough] public static unsafe void Call(System.IntPtr pfn, void* arg1, ref T arg2, ref U arg3) { // This will be filled in by an IL transform @@ -1050,6 +1072,11 @@ namespace Internal.Runtime.Augments } } + public static bool FileExists(string path) + { + return InternalFile.Exists(path); + } + public static string GetLastResortString(RuntimeTypeHandle typeHandle) { return typeHandle.LastResortToString; @@ -1060,22 +1087,28 @@ namespace Internal.Runtime.Augments RuntimeImports.RhpSetHighLevelDebugFuncEvalHelper(highLevelDebugFuncEvalHelper); } + public static void RhpSetHighLevelDebugFuncEvalAbortHelper(IntPtr highLevelDebugFuncEvalAbortHelper) + { + RuntimeImports.RhpSetHighLevelDebugFuncEvalAbortHelper(highLevelDebugFuncEvalAbortHelper); + } + public static void RhpSendCustomEventToDebugger(IntPtr payload, int length) { RuntimeImports.RhpSendCustomEventToDebugger(payload, length); } - public static IntPtr RhpGetFuncEvalTargetAddress() - { - return RuntimeImports.RhpGetFuncEvalTargetAddress(); - } - [CLSCompliant(false)] public static uint RhpGetFuncEvalParameterBufferSize() { return RuntimeImports.RhpGetFuncEvalParameterBufferSize(); } + [CLSCompliant(false)] + public static uint RhpGetFuncEvalMode() + { + return RuntimeImports.RhpGetFuncEvalMode(); + } + [CLSCompliant(false)] public static unsafe uint RhpRecordDebuggeeInitiatedHandle(IntPtr objectHandle) { @@ -1101,6 +1134,32 @@ namespace Internal.Runtime.Augments { return RuntimeImports.RhGetOSModuleForMrt(); } + + public static void RhpVerifyDebuggerCleanup() + { + RuntimeImports.RhpVerifyDebuggerCleanup(); + } + + public static IntPtr RhpGetCurrentThread() + { + return RuntimeImports.RhpGetCurrentThread(); + } + + public static void RhpInitiateThreadAbort(IntPtr thread, bool rude) + { + Exception ex = new ThreadAbortException(); + RuntimeImports.RhpInitiateThreadAbort(thread, ex, rude); + } + + public static void RhpCancelThreadAbort(IntPtr thread) + { + RuntimeImports.RhpCancelThreadAbort(thread); + } + + public static void RhYield() + { + RuntimeImports.RhYield(); + } } } @@ -1113,4 +1172,3 @@ namespace System.Runtime.InteropServices internal static IntPtr AddrOf(T ftn) { throw new PlatformNotSupportedException(); } } } - diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.Unix.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.Unix.cs index 8eff85572b..7b9d380e76 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.Unix.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.Unix.cs @@ -64,10 +64,14 @@ namespace Internal.Runtime.Augments [NativeCallable] private static void OnThreadExit() { - // Set the Stopped bit and signal the current thread as stopped RuntimeThread currentThread = t_currentThread; if (currentThread != null) { + // Inform the wait subsystem that the thread is exiting. For instance, this would abandon any mutexes locked by + // the thread. + WaitSubsystem.OnThreadExiting(currentThread); + + // Set the Stopped bit and signal the current thread as stopped int state = currentThread._threadState; if ((state & (int)(ThreadState.Stopped | ThreadState.Aborted)) == 0) { @@ -131,7 +135,7 @@ namespace Internal.Runtime.Augments } /// - /// This an entry point for managed threads created by applicatoin + /// This is an entry point for managed threads created by application /// [NativeCallable] private static IntPtr ThreadEntryPoint(IntPtr parameter) diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.Windows.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.Windows.cs index 4d129db592..6d0e937dcb 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.Windows.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.Windows.cs @@ -223,7 +223,7 @@ namespace Internal.Runtime.Augments } else { - result = WaitHandle.WaitForSingleObject(waitHandle.DangerousGetHandle(), millisecondsTimeout); + result = WaitHandle.WaitForSingleObject(waitHandle.DangerousGetHandle(), millisecondsTimeout, true); } return result == (int)Interop.Constants.WaitObject0; @@ -274,7 +274,7 @@ namespace Internal.Runtime.Augments } /// - /// This an entry point for managed threads created by applicatoin + /// This is an entry point for managed threads created by application /// [NativeCallable(CallingConvention = CallingConvention.StdCall)] private static uint ThreadEntryPoint(IntPtr parameter) @@ -362,7 +362,7 @@ namespace Internal.Runtime.Augments break; default: - Debug.Assert(false, "NA apartment without NA qualifier"); + Debug.Fail("NA apartment without NA qualifier"); break; } break; @@ -370,7 +370,7 @@ namespace Internal.Runtime.Augments break; default: - Debug.Assert(false, "bad return from CoGetApartmentType"); + Debug.Fail("bad return from CoGetApartmentType"); break; } diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.cs index b92f483aba..b21d9ced8b 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.cs @@ -5,6 +5,7 @@ using Microsoft.Win32.SafeHandles; using System; using System.Diagnostics; +using System.Globalization; using System.Runtime; using System.Runtime.InteropServices; using System.Threading; @@ -74,38 +75,69 @@ namespace Internal.Runtime.Augments { get { - RuntimeThread currentThread = t_currentThread; - return t_currentThread ?? InitializeExistingThread(); + return t_currentThread ?? InitializeExistingThread(false); } } // Slow path executed once per thread - private static RuntimeThread InitializeExistingThread() + private static RuntimeThread InitializeExistingThread(bool threadPoolThread) { + Debug.Assert(t_currentThread == null); + var currentThread = new RuntimeThread(); currentThread._managedThreadId = System.Threading.ManagedThreadId.GetCurrentThreadId(); Debug.Assert(currentThread._threadState == (int)ThreadState.Unstarted); + + ThreadState state = threadPoolThread ? ThreadPoolThread : 0; + // The main thread is foreground, other ones are background - if (currentThread._managedThreadId.Id == System.Threading.ManagedThreadId.IdMainThread) + if (currentThread._managedThreadId.Id != System.Threading.ManagedThreadId.IdMainThread) { - currentThread._threadState = (int)(ThreadState.Running); - } - else - { - currentThread._threadState = (int)(ThreadState.Running | ThreadState.Background); + state |= ThreadState.Background; } + + currentThread._threadState = (int)(state | ThreadState.Running); currentThread.PlatformSpecificInitializeExistingThread(); currentThread._priority = currentThread.GetPriorityLive(); t_currentThread = currentThread; + + if (threadPoolThread) + { + RoInitialize(); + } + return currentThread; } - public static void InitializeThreadPoolThread() + // Use ThreadPoolCallbackWrapper instead of calling this function directly + internal static RuntimeThread InitializeThreadPoolThread() { - if (t_currentThread == null) + return t_currentThread ?? InitializeExistingThread(true); + } + + /// + /// Resets properties of the current thread pool thread that may have been changed by a user callback. + /// + internal void ResetThreadPoolThread() + { + Debug.Assert(this == RuntimeThread.CurrentThread); + + CultureInfo.ResetThreadCulture(); + + if (_name != null) { - InitializeExistingThread().SetThreadStateBit(ThreadPoolThread); - RoInitialize(); + _name = null; + } + + if (!GetThreadStateBit(ThreadState.Background)) + { + SetThreadStateBit(ThreadState.Background); + } + + ThreadPriority newPriority = ThreadPriority.Normal; + if ((_priority != newPriority) && SetPriorityLive(newPriority)) + { + _priority = newPriority; } } @@ -194,6 +226,21 @@ namespace Internal.Runtime.Augments } return GetThreadStateBit(ThreadPoolThread); } + internal set + { + if (IsDead()) + { + throw new ThreadStateException(SR.ThreadState_Dead_State); + } + if (value) + { + SetThreadStateBit(ThreadPoolThread); + } + else + { + ClearThreadStateBit(ThreadPoolThread); + } + } } public int ManagedThreadId => _managedThreadId.Id; @@ -324,6 +371,15 @@ namespace Internal.Runtime.Augments } public static void Sleep(int millisecondsTimeout) => SleepInternal(VerifyTimeoutMilliseconds(millisecondsTimeout)); + + /// + /// Max value to be passed into for optimal delaying. Currently, the value comes from + /// defaults in CoreCLR's Thread::InitializeYieldProcessorNormalized(). This value is supposed to be normalized to be + /// appropriate for the processor. + /// TODO: See issue https://github.com/dotnet/corert/issues/4430 + /// + internal static readonly int OptimalMaxSpinWaitsPerSpinIteration = 64; + public static void SpinWait(int iterations) => RuntimeImports.RhSpinWait(iterations); public static bool Yield() => RuntimeImports.RhYield(); diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/StackTraceMetadataCallbacks.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/StackTraceMetadataCallbacks.cs new file mode 100644 index 0000000000..000b207ce0 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/StackTraceMetadataCallbacks.cs @@ -0,0 +1,29 @@ +// 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 Internal.Runtime.CompilerServices; + +namespace Internal.Runtime.Augments +{ + /// + /// This helper class is used to access metadata-based resolution of call stack addresses. + /// To activate the stack trace resolution support, set up an instance of a class + /// derived from this one using the method + /// + /// Internal.Runtime.Augments.RuntimeAugments.InitializeStackTraceMetadataSupport(StackTraceMetadataCallbacks callbacks); + /// + /// + [CLSCompliant(false)] + public abstract class StackTraceMetadataCallbacks + { + /// + /// Helper function to format a given method address using the stack trace metadata. + /// Return null if stack trace information is not available. + /// + /// Memory address representing the start of a method + /// Formatted method name or null if metadata for the method is not available + public abstract string TryGetMethodNameFromStartAddress(IntPtr methodStartAddress); + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/TypeLoaderCallbacks.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/TypeLoaderCallbacks.cs index 07428417a3..b1b216906e 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/TypeLoaderCallbacks.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/TypeLoaderCallbacks.cs @@ -3,6 +3,9 @@ // See the LICENSE file in the project root for more information. using System; +using System.Runtime; +using System.Collections.Generic; + using Internal.Runtime.CompilerServices; namespace Internal.Runtime.Augments @@ -19,7 +22,10 @@ namespace Internal.Runtime.Augments public abstract IntPtr TryGetDefaultConstructorForType(RuntimeTypeHandle runtimeTypeHandle); public abstract bool TryGetGenericVirtualTargetForTypeAndSlot(RuntimeTypeHandle targetHandle, ref RuntimeTypeHandle declaringType, RuntimeTypeHandle[] genericArguments, ref string methodName, ref RuntimeSignature methodSignature, out IntPtr methodPointer, out IntPtr dictionaryPointer, out bool slotUpdated); public abstract bool GetRuntimeFieldHandleComponents(RuntimeFieldHandle runtimeFieldHandle, out RuntimeTypeHandle declaringTypeHandle, out string fieldName); - + public abstract bool TryGetPointerTypeForTargetType(RuntimeTypeHandle pointeeTypeHandle, out RuntimeTypeHandle pointerTypeHandle); + public abstract bool TryGetArrayTypeForElementType(RuntimeTypeHandle elementTypeHandle, bool isMdArray, int rank, out RuntimeTypeHandle arrayTypeHandle); + public abstract IntPtr UpdateFloatingDictionary(IntPtr context, IntPtr dictionaryPtr); + /// /// Register a new runtime-allocated code thunk in the diagnostic stream. /// diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/WinRTInterop.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/WinRTInterop.cs index 14626c5f79..6d999fb6f0 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/WinRTInterop.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/Augments/WinRTInterop.cs @@ -57,44 +57,6 @@ namespace Internal.Runtime.Augments private const string c_EarlyCallingExceptionMessage = "WinRT Interop has not been initialized yet. If trying to access something in a static variable initialization or static constructor try to do this work lazily on first use instead."; } - public enum CausalityRelation - { - AssignDelegate = 0, - Join = 1, - Choice = 2, - Cancel = 3, - Error = 4, - } - - public enum CausalitySource - { - Application = 0, - Library = 1, - System = 2, - } - - public enum CausalityTraceLevel - { - Required = 0, - Important = 1, - Verbose = 2, - } - - public enum AsyncStatus - { - Started = 0, - Completed = 1, - Canceled = 2, - Error = 3, - } - - public enum CausalitySynchronousWork - { - CompletionNotification = 0, - ProgressNotification = 1, - Execution = 2, - } - [CLSCompliant(false)] public abstract class WinRTInteropCallbacks { @@ -105,11 +67,8 @@ namespace Internal.Runtime.Augments public abstract string GetRegionDisplayName(string isoCountryCode); public abstract Object GetUserDefaultCulture(); public abstract void SetGlobalDefaultCulture(Object culture); - public abstract Object GetCurrentCoreDispatcher(); - public abstract void PostToCoreDispatcher(Object dispatcher, Action action, object state); - public abstract Object GetResourceMap(string subtreeName); - public abstract string GetResourceString(object resourceMap, string resourceName, string languageName); - public abstract string GetResourceString(object resourceMap, string resourceName, string languageName, string neutralResourcesCulture); + public abstract Object GetCurrentWinRTDispatcher(); + public abstract void PostToWinRTDispatcher(Object dispatcher, Action action, object state); public abstract bool IsAppxModel(); public abstract bool ReportUnhandledError(Exception ex); public abstract void SetCOMWeakReferenceTarget(object weakReference, object target); diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs index 91ccdf34d8..90a4c51569 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs @@ -42,6 +42,12 @@ namespace Internal.Runtime.CompilerHelpers if (buffer == null) return; + if (str == null) + { + buffer[0] = '\0'; + return; + } + Debug.Assert(str.Length >= length); fixed (char* pStr = str) diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs index b97a6e2123..c31521d806 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs @@ -6,6 +6,8 @@ using System; using System.Runtime; using System.Runtime.CompilerServices; +using Internal.Runtime; + namespace Internal.Runtime.CompilerHelpers { /// @@ -215,6 +217,110 @@ namespace Internal.Runtime.CompilerHelpers return ThrowIntOvf(); } +#if ARM + private const string RuntimeLibrary = "[MRT]"; + + [RuntimeImport(RuntimeLibrary, "RhpIDiv")] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern Int32 RhpIDiv(Int32 i, Int32 j); + + public static int IDiv(Int32 i, Int32 j) + { + if (j == 0) + return ThrowIntDivByZero(); + else if (j == -1 && i == Int32.MinValue) + return ThrowIntArithExc(); + else + return RhpIDiv(i, j); + } + + [RuntimeImport(RuntimeLibrary, "RhpUDiv")] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern UInt32 RhpUDiv(UInt32 i, UInt32 j); + + public static long UDiv(UInt32 i, UInt32 j) + { + if (j == 0) + return ThrowUIntDivByZero(); + else + return RhpUDiv(i, j); + } + + [RuntimeImport(RuntimeLibrary, "RhpULDiv")] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern UInt64 RhpULDiv(UInt64 i, UInt64 j); + + public static ulong ULDiv(UInt64 i, UInt64 j) + { + if (j == 0) + return ThrowULngDivByZero(); + else + return RhpULDiv(i, j); + } + + [RuntimeImport(RuntimeLibrary, "RhpLDiv")] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern Int64 RhpLDiv(Int64 i, Int64 j); + + public static long LDiv(Int64 i, Int64 j) + { + if (j == 0) + return ThrowLngDivByZero(); + else if (j == -1 && i == Int64.MinValue) + return ThrowLngArithExc(); + else + return RhpLDiv(i, j); + } + + [RuntimeImport(RuntimeLibrary, "RhpIMod")] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern Int32 RhpIMod(Int32 i, Int32 j); + + public static int IMod(Int32 i, Int32 j) + { + if (j == 0) + return ThrowIntDivByZero(); + else + return RhpIMod(i, j); + } + + [RuntimeImport(RuntimeLibrary, "RhpUMod")] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern UInt32 RhpUMod(UInt32 i, UInt32 j); + + public static long UMod(UInt32 i, UInt32 j) + { + if (j == 0) + return ThrowUIntDivByZero(); + else + return RhpUMod(i, j); + } + + [RuntimeImport(RuntimeLibrary, "RhpULMod")] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern UInt64 RhpULMod(UInt64 i, UInt64 j); + + public static ulong ULMod(UInt64 i, UInt64 j) + { + if (j == 0) + return ThrowULngDivByZero(); + else + return RhpULMod(i, j); + } + + [RuntimeImport(RuntimeLibrary, "RhpLMod")] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern Int64 RhpLMod(Int64 i, Int64 j); + + public static long LMod(Int64 i, Int64 j) + { + if (j == 0) + return ThrowLngDivByZero(); + else + return RhpLMod(i, j); + } +#endif // ARM + // // Matching return types of throw helpers enables tailcalling them. It improves performance // of the hot path because of it does not need to raise full stackframe. @@ -243,5 +349,43 @@ namespace Internal.Runtime.CompilerHelpers { throw new OverflowException(); } + +#if ARM + [MethodImpl(MethodImplOptions.NoInlining)] + private static int ThrowIntDivByZero() + { + throw new DivideByZeroException(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static uint ThrowUIntDivByZero() + { + throw new DivideByZeroException(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static long ThrowLngDivByZero() + { + throw new DivideByZeroException(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static ulong ThrowULngDivByZero() + { + throw new DivideByZeroException(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static int ThrowIntArithExc() + { + throw new ArithmeticException(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static long ThrowLngArithExc() + { + throw new ArithmeticException(); + } +#endif // ARM } } diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ReflectionHelpers.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ReflectionHelpers.cs index e156e6306f..5aaba38190 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ReflectionHelpers.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ReflectionHelpers.cs @@ -39,12 +39,12 @@ namespace Internal.Runtime.CompilerHelpers [System.Runtime.CompilerServices.DependencyReductionRoot] public static MethodBase GetCurrentMethodNonGeneric(RuntimeMethodHandle methodHandle) { -#if CORERT - return MethodBase.GetMethodFromHandle(methodHandle); -#else +#if PROJECTN // The compiler should ideally provide us with a RuntimeMethodHandle for the uninstantiated thing, // but the Project N toolchain cannot express a RuntimeMethodHandle for a generic definition of a generic method. return MethodBase.GetMethodFromHandle(methodHandle).MetadataDefinitionMethod; +#else + return MethodBase.GetMethodFromHandle(methodHandle); #endif } @@ -52,12 +52,12 @@ namespace Internal.Runtime.CompilerHelpers [System.Runtime.CompilerServices.DependencyReductionRoot] public static MethodBase GetCurrentMethodGeneric(RuntimeMethodHandle methodHandle, RuntimeTypeHandle typeHandle) { -#if CORERT - return MethodBase.GetMethodFromHandle(methodHandle, typeHandle); -#else +#if PROJECTN // The compiler should ideally provide us with a RuntimeMethodHandle for the uninstantiated thing, // but the Project N toolchain cannot express a RuntimeMethodHandle for a generic definition of a generic method. return MethodBase.GetMethodFromHandle(methodHandle, typeHandle).MetadataDefinitionMethod; +#else + return MethodBase.GetMethodFromHandle(methodHandle, typeHandle); #endif } } diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/StartupCode/StartupCodeHelpers.GlobalTables.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/StartupCode/StartupCodeHelpers.GlobalTables.cs deleted file mode 100644 index b785eed963..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/StartupCode/StartupCodeHelpers.GlobalTables.cs +++ /dev/null @@ -1,67 +0,0 @@ -// 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.Runtime; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Text; - -using Internal.NativeFormat; - -using Debug = Internal.Runtime.CompilerHelpers.StartupDebug; - -namespace Internal.Runtime.CompilerHelpers -{ - public partial class StartupCodeHelpers - { - /// - /// Each managed module linked into the final binary may have its own global tables for strings, - /// statics, etc that need initializing. InitializeGlobalTables walks through the modules - /// and offers each a chance to initialize its global tables. - /// - private static unsafe void InitializeGlobalTablesForModule(IntPtr moduleManager) - { - // Configure the module indirection cell with the newly created ModuleManager. This allows EETypes to find - // their interface dispatch map tables. - int length; - IntPtr* section = (IntPtr*)RuntimeImports.GetModuleSection(moduleManager, (int)ReadyToRunSectionType.ModuleManagerIndirection, out length); - *section = moduleManager; - - // Initialize statics if any are present - IntPtr staticsSection = RuntimeImports.GetModuleSection(moduleManager, (int)ReadyToRunSectionType.GCStaticRegion, out length); - if (staticsSection != IntPtr.Zero) - { - Debug.Assert(length % IntPtr.Size == 0); - InitializeStatics(staticsSection, length); - } - - // Initialize frozen object segment with GC present - IntPtr frozenObjectSection = RuntimeImports.GetModuleSection(moduleManager, (int)ReadyToRunSectionType.FrozenObjectRegion, out length); - if (frozenObjectSection != IntPtr.Zero) - { - Debug.Assert(length % IntPtr.Size == 0); - InitializeFrozenObjectSegment(frozenObjectSection, length); - } - } - - private static unsafe void InitializeStatics(IntPtr gcStaticRegionStart, int length) - { - IntPtr gcStaticRegionEnd = (IntPtr)((byte*)gcStaticRegionStart + length); - for (IntPtr* block = (IntPtr*)gcStaticRegionStart; block < (IntPtr*)gcStaticRegionEnd; block++) - { - // Gc Static regions can be shared by modules linked together during compilation. To ensure each - // is initialized once, the static region pointer is stored with lowest bit set in the image. - // The first time we initialize the static region its pointer is replaced with an object reference - // whose lowest bit is no longer set. - IntPtr* pBlock = (IntPtr*)*block; - if (((*pBlock).ToInt64() & 0x1L) == 1) - { - object obj = RuntimeImports.RhNewObject(new EETypePtr(new IntPtr((*pBlock).ToInt64() & ~0x1L))); - *pBlock = RuntimeImports.RhHandleAlloc(obj, GCHandleType.Normal); - } - } - } - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/StartupCode/StartupCodeHelpers.ProjectN.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/StartupCode/StartupCodeHelpers.ProjectN.cs new file mode 100644 index 0000000000..5f265ff4cf --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/StartupCode/StartupCodeHelpers.ProjectN.cs @@ -0,0 +1,34 @@ +// 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.Runtime; + +using Debug = Internal.Runtime.CompilerHelpers.StartupDebug; + +namespace Internal.Runtime.CompilerHelpers +{ + public static partial class StartupCodeHelpers + { + /// + /// Register a runtime module for subsequent retrieval using GetLoadedModules(). + /// + /// Arbitrary pointer within the module to register + public static void RegisterModuleFromPointer(IntPtr pointerWithinModule) + { + IntPtr startOfModule = RuntimeImports.RhGetOSModuleFromPointer(pointerWithinModule); + TypeManagerHandle newModuleHandle = new TypeManagerHandle(startOfModule); + for (int moduleIndex = 0; moduleIndex < s_moduleCount; moduleIndex++) + { + if (s_modules[moduleIndex].OsModuleBase == startOfModule) + { + // Module already registered + return; + } + } + + AddModule(newModuleHandle); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/StartupCode/StartupCodeHelpers.Reflection.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/StartupCode/StartupCodeHelpers.Reflection.cs index 18cf2328aa..5d5efbc931 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/StartupCode/StartupCodeHelpers.Reflection.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/StartupCode/StartupCodeHelpers.Reflection.cs @@ -14,19 +14,7 @@ namespace Internal.Runtime.CompilerHelpers // ProjectN doesn't have access to a convenient always-reflection-enabled type to use. // (We can't use the type because of IL2IL toolchain limitations.) -#if CORERT - private static RuntimeTypeHandle s_entryAssemblyType; - - internal static void InitializeEntryAssembly(RuntimeTypeHandle entryAssemblyType) - { - s_entryAssemblyType = entryAssemblyType; - } - - internal static Assembly GetEntryAssembly() - { - return Type.GetTypeFromHandle(s_entryAssemblyType).Assembly; - } -#else +#if PROJECTN private static string s_entryAssemblyName; // The only reason why this is public is because the Project N IL2IL toolchain will remove this method @@ -46,6 +34,18 @@ namespace Internal.Runtime.CompilerHelpers } return null; } +#else + private static RuntimeTypeHandle s_entryAssemblyType; + + internal static void InitializeEntryAssembly(RuntimeTypeHandle entryAssemblyType) + { + s_entryAssemblyType = entryAssemblyType; + } + + internal static Assembly GetEntryAssembly() + { + return Type.GetTypeFromHandle(s_entryAssemblyType).Assembly; + } #endif } } diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/SynchronizedMethodHelpers.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/SynchronizedMethodHelpers.cs index ba0eaeed0d..30694aeadb 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/SynchronizedMethodHelpers.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/SynchronizedMethodHelpers.cs @@ -56,7 +56,7 @@ namespace Internal.Runtime.CompilerHelpers private static Object GetStaticLockObject(IntPtr pEEType) { - return Internal.Reflection.Core.NonPortable.ReflectionCoreNonPortable.GetRuntimeTypeForEEType(new System.EETypePtr(pEEType)); + return Internal.Reflection.Core.NonPortable.RuntimeTypeUnifier.GetRuntimeTypeForEEType(new System.EETypePtr(pEEType)); } } } diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.cs index 31c78a53ad..92053f709c 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.cs @@ -80,7 +80,12 @@ namespace Internal.Runtime.CompilerHelpers throw TypeLoaderExceptionHelper.CreateFileNotFoundException(id, fileName); } - public static void ThrowInvalidProgramException(ExceptionStringID id, string methodName) + public static void ThrowInvalidProgramException(ExceptionStringID id) + { + throw TypeLoaderExceptionHelper.CreateInvalidProgramException(id); + } + + public static void ThrowInvalidProgramExceptionWithArgument(ExceptionStringID id, string methodName) { throw TypeLoaderExceptionHelper.CreateInvalidProgramException(id, methodName); } diff --git a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/TypeLoaderExceptionHelper.cs b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/TypeLoaderExceptionHelper.cs index 8d33f6670a..40294efd93 100644 --- a/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/TypeLoaderExceptionHelper.cs +++ b/external/corert/src/System.Private.CoreLib/src/Internal/Runtime/TypeLoaderExceptionHelper.cs @@ -48,6 +48,11 @@ namespace Internal.Runtime throw new System.IO.FileNotFoundException(SR.Format(GetFormatString(id), fileName), fileName); } + public static Exception CreateInvalidProgramException(ExceptionStringID id) + { + throw new InvalidProgramException(GetFormatString(id)); + } + public static Exception CreateInvalidProgramException(ExceptionStringID id, string methodName) { throw new InvalidProgramException(SR.Format(GetFormatString(id), methodName)); @@ -70,12 +75,16 @@ namespace Internal.Runtime return SR.ClassLoad_ExplicitLayout; case ExceptionStringID.ClassLoadRankTooLarge: return SR.ClassLoad_RankTooLarge; + case ExceptionStringID.InvalidProgramDefault: + return SR.InvalidProgram_Default; case ExceptionStringID.InvalidProgramSpecific: return SR.InvalidProgram_Specific; case ExceptionStringID.InvalidProgramVararg: return SR.InvalidProgram_Vararg; case ExceptionStringID.InvalidProgramCallVirtFinalize: return SR.InvalidProgram_CallVirtFinalize; + case ExceptionStringID.InvalidProgramNativeCallable: + return SR.InvalidProgram_NativeCallable; case ExceptionStringID.MissingField: return SR.EE_MissingField; case ExceptionStringID.MissingMethod: @@ -86,7 +95,7 @@ namespace Internal.Runtime return SR.Arg_BadImageFormatException; default: Debug.Assert(false); - throw new NotImplementedException(); + return ""; } } } diff --git a/external/corert/src/System.Private.CoreLib/src/Interop/Interop.manual.cs b/external/corert/src/System.Private.CoreLib/src/Interop/Interop.manual.cs index d7728d22ee..a6c2be7be7 100644 --- a/external/corert/src/System.Private.CoreLib/src/Interop/Interop.manual.cs +++ b/external/corert/src/System.Private.CoreLib/src/Interop/Interop.manual.cs @@ -129,51 +129,3 @@ internal partial class Interop uint dwFlags); } } - -namespace System.Runtime.InteropServices -{ - internal class Marshal - { - public static int GetLastWin32Error() - { - return PInvokeMarshal.GetLastWin32Error(); - } - - public static unsafe IntPtr AllocHGlobal(IntPtr cb) - { - return PInvokeMarshal.AllocHGlobal(cb); - } - - public static unsafe IntPtr AllocHGlobal(int cb) - { - return PInvokeMarshal.AllocHGlobal(cb); - } - - public static void FreeHGlobal(IntPtr hglobal) - { - PInvokeMarshal.FreeHGlobal(hglobal); - } - - public static unsafe IntPtr AllocCoTaskMem(int cb) - { - return PInvokeMarshal.AllocCoTaskMem(cb); - } - - public static void FreeCoTaskMem(IntPtr ptr) - { - PInvokeMarshal.FreeCoTaskMem(ptr); - } - - public static void Copy(IntPtr source, byte[] destination, int startIndex, int length) - { - InteropExtensions.CopyToManaged(source, destination, startIndex, length); - } - -#if PLATFORM_UNIX - public static unsafe String PtrToStringAnsi(IntPtr ptr) - { - return PInvokeMarshal.PtrToStringAnsi(ptr); - } -#endif - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/MembersMustExist.AnalyzerData b/external/corert/src/System.Private.CoreLib/src/MembersMustExist.AnalyzerData index e321c569a7..1ac5206dd0 100644 --- a/external/corert/src/System.Private.CoreLib/src/MembersMustExist.AnalyzerData +++ b/external/corert/src/System.Private.CoreLib/src/MembersMustExist.AnalyzerData @@ -94,7 +94,6 @@ private static extern int System.Runtime.RuntimeImports.RhCompatibleReentrantWai internal static extern bool System.Runtime.RuntimeImports.RhCreateGenericInstanceDescForType2(System.EETypePtr pEEType, int arity, int nonGcStaticDataSize, int nonGCStaticDataOffset, int gcStaticDataSize, int threadStaticsOffset, void* pGcStaticsDesc, void* pThreadStaticsDesc, int* pGenericVarianceFlags) private static extern bool System.Runtime.RuntimeImports.RhFindBlob(ref System.Runtime.TypeManagerHandle typeManagerHandle, uint blobId, byte** ppbBlob, uint* pcbBlob) internal static extern System.IntPtr System.Runtime.RuntimeImports.RhFindMethodStartAddress(System.IntPtr codeAddr) -internal static extern System.IntPtr System.Runtime.RuntimeImports.RhGetCodeTarget(System.IntPtr pCode) internal static extern System.IntPtr System.Runtime.RuntimeImports.RhGetJmpStubCodeTarget(System.IntPtr pCode) internal static extern long System.Runtime.RuntimeImports.RhGetCurrentObjSize() internal static extern int System.Runtime.RuntimeImports.RhGetCurrentThreadStackTrace(System.IntPtr[] outputBuffer) @@ -109,7 +108,6 @@ internal static extern int System.Runtime.RuntimeImports.RhGetGeneration(object internal static extern long System.Runtime.RuntimeImports.RhGetLastGCDuration(int generation) internal static extern long System.Runtime.RuntimeImports.RhGetLastGCStartTime(int generation) internal static extern uint System.Runtime.RuntimeImports.RhGetLoadedOSModules(System.IntPtr[] resultArray) -internal static extern uint System.Runtime.RuntimeImports.RhGetLoadedModules(System.Runtime.TypeManagerHandle[] resultArray) internal static extern int System.Runtime.RuntimeImports.RhGetLohCompactionMode() internal static extern int System.Runtime.RuntimeImports.RhGetMaxGcGeneration() internal static extern int System.Runtime.RuntimeImports.RhGetModuleFileName(System.IntPtr moduleHandle, out char* moduleName) @@ -118,14 +116,13 @@ internal static extern System.Runtime.TypeManagerHandle System.Runtime.RuntimeIm internal static extern System.IntPtr System.Runtime.RuntimeImports.RhGetOSModuleFromPointer(System.IntPtr pointerVal) internal static extern System.IntPtr System.Runtime.RuntimeImports.RhGetRuntimeHelperForType(System.EETypePtr pEEType, System.Runtime.RuntimeImports.RuntimeHelperKind kind) internal static extern System.IntPtr System.Runtime.RuntimeImports.RhGetThreadLocalStorageForDynamicType(int index, int tlsStorageSize, int numTlsCells) -internal static extern byte* System.Runtime.RuntimeImports.RhGetThreadStaticFieldAddress(System.EETypePtr pEEType, System.IntPtr fieldCookie) internal static extern System.IntPtr System.Runtime.RuntimeImports.RhGetUniversalTransitionThunk() internal static System.IntPtr System.Runtime.RuntimeImports.RhHandleAlloc(object value, System.Runtime.InteropServices.GCHandleType type) internal static System.IntPtr System.Runtime.RuntimeImports.RhHandleAllocDependent(object primary, object secondary) internal static System.IntPtr System.Runtime.RuntimeImports.RhHandleAllocVariable(object value, uint type) internal static extern uint System.Runtime.RuntimeImports.RhHandleCompareExchangeVariableType(System.IntPtr handle, uint oldType, uint newType) internal static extern void System.Runtime.RuntimeImports.RhHandleFree(System.IntPtr handle) -internal static extern object System.Runtime.RuntimeImports.RhHandleGet(System.IntPtr handle) +internal static object System.Runtime.RuntimeImports.RhHandleGet(System.IntPtr handle) internal static extern object System.Runtime.RuntimeImports.RhHandleGetDependent(System.IntPtr handle, out object secondary) internal static extern uint System.Runtime.RuntimeImports.RhHandleGetVariableType(System.IntPtr handle) internal static extern void System.Runtime.RuntimeImports.RhHandleSet(System.IntPtr handle, object value) @@ -135,7 +132,6 @@ internal static extern bool System.Runtime.RuntimeImports.RhIsPromoted(object ob internal static extern bool System.Runtime.RuntimeImports.RhIsServerGc() internal static extern object System.Runtime.RuntimeImports.RhMemberwiseClone(object obj) internal static extern System.Array System.Runtime.RuntimeImports.RhNewArray(System.EETypePtr pEEType, int length) -internal static extern string System.Runtime.RuntimeImports.RhNewArrayAsString(System.EETypePtr pEEType, int length) internal static extern System.IntPtr System.Runtime.RuntimeImports.RhNewInterfaceDispatchCell(System.EETypePtr pEEType, int slotNumber) internal static extern object System.Runtime.RuntimeImports.RhNewObject(System.EETypePtr pEEType) internal static extern void System.Runtime.RuntimeImports.RhpEtwExceptionThrown(char* exceptionTypeName, char* exceptionMessage, System.IntPtr faultingIP, long hresult) @@ -210,7 +206,6 @@ public static System.IntPtr System.IntPtr.operator +(System.IntPtr pointer, int public static bool System.IntPtr.operator ==(System.IntPtr value1, System.IntPtr value2) public static int System.IntPtr.Size.get public static System.IntPtr System.IntPtr.Subtract(System.IntPtr pointer, int offset) -internal bool System.IntPtr.IsNull() public int System.IntPtr.ToInt32() public long System.IntPtr.ToInt64() public void* System.IntPtr.ToPointer() diff --git a/external/corert/src/System.Private.CoreLib/src/Microsoft/Win32/Registry.cs b/external/corert/src/System.Private.CoreLib/src/Microsoft/Win32/Registry.cs new file mode 100644 index 0000000000..4afd135aca --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/Microsoft/Win32/Registry.cs @@ -0,0 +1,34 @@ +// 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; + +namespace Microsoft.Win32 +{ + /** + * Registry encapsulation. Contains members representing all top level system + * keys. + * + * @security(checkClassLinking=on) + */ + //This class contains only static members and does not need to be serializable. + internal static class Registry + { + /** + * Current User Key. + * + * This key should be used as the root for all user specific settings. + */ + public static readonly RegistryKey CurrentUser = RegistryKey.GetBaseKey(RegistryKey.HKEY_CURRENT_USER); + + /** + * Local Machine Key. + * + * This key should be used as the root for all machine specific settings. + */ + public static readonly RegistryKey LocalMachine = RegistryKey.GetBaseKey(RegistryKey.HKEY_LOCAL_MACHINE); + } +} + diff --git a/external/corert/src/System.Private.CoreLib/src/Microsoft/Win32/RegistryKey.Windows.cs b/external/corert/src/System.Private.CoreLib/src/Microsoft/Win32/RegistryKey.Windows.cs index 2a7a14c556..6b0cc89629 100644 --- a/external/corert/src/System.Private.CoreLib/src/Microsoft/Win32/RegistryKey.Windows.cs +++ b/external/corert/src/System.Private.CoreLib/src/Microsoft/Win32/RegistryKey.Windows.cs @@ -125,6 +125,33 @@ namespace Microsoft.Win32 return null; } + private void DeleteValueCore(string name, bool throwOnMissingValue) + { + int errorCode = Interop.Advapi32.RegDeleteValue(_hkey, name); + + // + // From windows 2003 server, if the name is too long we will get error code ERROR_FILENAME_EXCED_RANGE + // This still means the name doesn't exist. We need to be consistent with previous OS. + // + if (errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND || + errorCode == Interop.Errors.ERROR_FILENAME_EXCED_RANGE) + { + if (throwOnMissingValue) + { + throw new ArgumentException(SR.Arg_RegSubKeyValueAbsent); + } + else + { + // Otherwise, reset and just return giving no indication to the user. + // (For compatibility) + errorCode = 0; + } + } + // We really should throw an exception here if errorCode was bad, + // but we can't for compatibility reasons. + Debug.Assert(errorCode == 0, "RegDeleteValue failed. Here's your error code: " + errorCode); + } + /// /// Retrieves a new RegistryKey that represents the requested key. Valid /// values are: @@ -512,6 +539,11 @@ namespace Microsoft.Win32 // pass in the whole char[] to prevent truncating a character data = new string(blob); } + + if (!doNotExpand) + { + data = Environment.ExpandEnvironmentVariables((string)data); + } } break; case Interop.mincore.RegistryValues.REG_MULTI_SZ: @@ -614,6 +646,21 @@ namespace Microsoft.Win32 (RegistryValueKind)type; } + // We only need to set String values, this is a cut-down version of SetValueCore(string name, object value, RegistryValueKind valueKind) + // that supports only that. + private unsafe void SetValueCore(string name, string value) + { + int ret = Interop.Advapi32.RegSetValueEx(_hkey, name, 0, RegistryValueKind.String, value, checked(value.Length * 2 + 2)); + if (ret == 0) + { + SetDirty(); + } + else + { + Win32Error(ret, null); + } + } + /// /// After calling GetLastWin32Error(), it clears the last error field, /// so you must save the HResult and pass it to this method. This method diff --git a/external/corert/src/System.Private.CoreLib/src/Microsoft/Win32/RegistryKey.cs b/external/corert/src/System.Private.CoreLib/src/Microsoft/Win32/RegistryKey.cs index 1ecde304a1..1fd407afa2 100644 --- a/external/corert/src/System.Private.CoreLib/src/Microsoft/Win32/RegistryKey.cs +++ b/external/corert/src/System.Private.CoreLib/src/Microsoft/Win32/RegistryKey.cs @@ -13,7 +13,11 @@ using System.Text; namespace Microsoft.Win32 { /// Registry encapsulation. To get an instance of a RegistryKey use the Registry class's static members then call OpenSubKey. +#if REGISTRY_ASSEMBLY + public +#else internal +#endif sealed partial class RegistryKey : IDisposable { public static readonly IntPtr HKEY_CLASSES_ROOT = new IntPtr(unchecked((int)0x80000000)); @@ -157,6 +161,12 @@ namespace Microsoft.Win32 return CreateSubKeyInternalCore(subkey, writable, registryOptions); } + public void DeleteValue(string name, bool throwOnMissingValue) + { + EnsureWriteable(); + DeleteValueCore(name, throwOnMissingValue); + } + public static RegistryKey OpenBaseKey(RegistryHive hKey, RegistryView view) { ValidateKeyView(view); @@ -343,6 +353,20 @@ namespace Microsoft.Win32 } } + //The actual api is SetValue(string name, object value, RegistryValueKind valueKind) but we only need to set Strings + // so this is a cut-down version that supports on that. + internal void SetValue(string name, string value) + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + if (name != null && name.Length > MaxValueLength) + throw new ArgumentException(SR.Arg_RegValStrLenBug, nameof(name)); + + EnsureWriteable(); + SetValueCore(name, value); + } + /// Retrieves a string representation of this key. /// A string representing the key. public override string ToString() diff --git a/external/corert/src/System.Private.CoreLib/src/Resources/Strings.resx.REMOVED.git-id b/external/corert/src/System.Private.CoreLib/src/Resources/Strings.resx.REMOVED.git-id index 508c3b6de7..4b508a3bdf 100644 --- a/external/corert/src/System.Private.CoreLib/src/Resources/Strings.resx.REMOVED.git-id +++ b/external/corert/src/System.Private.CoreLib/src/Resources/Strings.resx.REMOVED.git-id @@ -1 +1 @@ -8e91db3650d47b62c316085e8106b8ad93bf1651 \ No newline at end of file +24977e34b093bb0384adecc5b062042d2bf831ea \ No newline at end of file diff --git a/external/corert/src/System.Private.CoreLib/src/Resources/System.Private.CoreLib.rd.xml b/external/corert/src/System.Private.CoreLib/src/Resources/System.Private.CoreLib.rd.xml index 923b42dd72..4fab005662 100644 --- a/external/corert/src/System.Private.CoreLib/src/Resources/System.Private.CoreLib.rd.xml +++ b/external/corert/src/System.Private.CoreLib/src/Resources/System.Private.CoreLib.rd.xml @@ -93,6 +93,8 @@ + + @@ -112,6 +114,9 @@ - + + diff --git a/external/corert/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/external/corert/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj index a0ce62b496..847dd80f1b 100644 --- a/external/corert/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/external/corert/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -1,5 +1,4 @@ - - + System.Private.CoreLib @@ -8,7 +7,11 @@ Library true true + true + + MSFT + true @@ -17,8 +20,8 @@ - - + + true true @@ -36,6 +39,10 @@ FEATURE_COMINTEROP;$(DefineConstants) + + false + true + Internal\Runtime\RuntimeConstants.cs @@ -43,9 +50,6 @@ ExceptionStringID.cs - - System\Threading\Tasks - @@ -73,6 +77,9 @@ Internal\Runtime\ModuleHeaders.cs + + + Common\src\Internal\NativeFormat\NativeFormatReader.cs @@ -111,22 +118,23 @@ - + - + + + + + - - - @@ -134,7 +142,8 @@ - + + @@ -159,7 +168,6 @@ - @@ -170,113 +178,71 @@ - - + - + - - - - - - - + - - - - - - - - - - - - - - - - + + + - - - - + + + - - - - + + - - - - - - - - - - - - - - - + + - - - + - - - - @@ -288,49 +254,36 @@ - - - - - - - - - + + - - - - - - @@ -341,23 +294,10 @@ - - - - - - - - - - - - - @@ -368,8 +308,9 @@ - + + @@ -377,11 +318,9 @@ - - @@ -394,7 +333,6 @@ - @@ -403,14 +341,13 @@ + - - @@ -423,28 +360,19 @@ - - - - - - - - - - + @@ -454,6 +382,12 @@ + + Interop\Windows\advapi32\Interop.RegDeleteValue.cs + + + Interop\Windows\advapi32\Interop.RegSetValueEx.cs + Interop\Windows\mincore\Interop.RegistryOptions.cs @@ -481,6 +415,9 @@ Interop\Windows\mincore\Interop.RegQueryValueEx.cs + + Interop\Windows\mincore\Interop.ExitProcess.cs + @@ -490,24 +427,48 @@ - + + + Interop\Windows\kernel32\Interop.ExpandEnvironmentStrings.cs + Interop\Windows\kernel32\Interop.Memory.cs Interop\Windows\kernel32\Interop.MultiByteToWideChar.cs + + Interop\Windows\kernel32\Interop.CompletionPort.cs + + + Interop\Windows\kernel32\Interop.CriticalSection.cs + + + Interop\Windows\kernel32\Interop.ConditionVariable.cs + + + Interop\Windows\kernel32\Interop.GetSystemTimes.cs + Interop\Windows\kernel32\Interop.GetModuleFileName.cs + + Interop\Windows\kernel32\Interop.QueryPerformance.cs + Interop\Windows\mincore\Interop.GetCurrentThreadId.cs Interop\Windows\mincore\Interop.SetLastError.cs + + Interop\Windows\mincore\Interop.VirtualAllocFromApp.cs + + + Interop\Windows\kernel32\Interop.VirtualAlloc.cs + Interop\Windows\mincore\Interop.MUI.cs @@ -550,10 +511,13 @@ Interop\Windows\mincore\Interop.DynamicLoad.cs + + + - + @@ -568,16 +532,19 @@ - - - - - - Interop\Windows\mincore\Interop.Idna.cs - - - Interop\Windows\mincore\Interop.Normalization.cs - + + + + + + + + + + + + + @@ -587,13 +554,7 @@ Interop\Windows\mincore\Interop.GetLastError.cs - - Interop\Windows\mincore\Interop.SYSTEM_INFO.cs - - - Interop\Windows\mincore\Interop.GetNativeSystemInfo.cs - - + Interop\Windows\mincore\Interop.GetSystemDirectory.cs @@ -619,28 +580,26 @@ - + - - - + + - + - @@ -659,12 +618,18 @@ Interop\Unix\System.Private.CoreLib.Native\Interop.Exit.cs + + Interop\Unix\System.Private.CoreLib.Native\Interop.GetCpuUtilization.cs + Interop\Unix\System.Private.CoreLib.Native\Interop.GetEnv.cs Interop\Unix\System.Private.CoreLib.Native\Interop.GetExecutableAbsolutePath.cs + + Interop\Unix\System.Private.CoreLib.Native\Interop.HighPrecisionCounter.cs + Interop\Unix\System.Private.CoreLib.Native\Interop.MemAllocFree.cs @@ -677,8 +642,8 @@ Interop\Unix\System.Private.CoreLib.Native\Interop.SchedGetCpu.cs - - Interop\Unix\System.Private.CoreLib.Native\Interop.FormatProvider.FormatAndParse.cs + + Interop\Unix\System.Private.CoreLib.Native\Interop.Number.cs Interop\Unix\System.Private.CoreLib.Native\Interop.DynamicLoad.cs @@ -689,9 +654,6 @@ Interop\Unix\System.Private.CoreLib.Native\Interop.ErrNo.cs - - Interop\Unix\System.Native\Interop.SysConf.cs - @@ -703,9 +665,6 @@ System\Collections\Generic\LowLevelDictionary.cs - - System\Collections\HashHelpers.cs - System\Collections\Concurrent\ConcurrentUnifier.cs @@ -724,9 +683,6 @@ Internal\LowLevelLinq\LowLevelEnumerable.ToArray.cs - - System\Globalization\FormatProvider.Number.cs - System\Numerics\Hashing\HashHelpers.cs @@ -748,12 +704,12 @@ System\CommonRuntimeTypes.cs - - System\__HResults.cs - Common\src\Internal\NativeFormat\NativeFormatReader.Primitives.cs + + Internal\Runtime\CanonTypeKind.cs + Internal\Runtime\EEType.cs @@ -765,7 +721,7 @@ - + true @@ -819,9 +775,6 @@ Runtime.Base\src\System\Runtime\CompilerServices\ManuallyManagedAttribute.cs - - Runtime.Base\src\System\Runtime\InteropServices\UnsafeGCHandle.cs - Runtime.Base\src\RhBaseName.cs @@ -830,7 +783,8 @@ - + + diff --git a/external/corert/src/System.Private.CoreLib/src/System.Private.CoreLib.sln b/external/corert/src/System.Private.CoreLib/src/System.Private.CoreLib.sln index dd31bf6ef1..3b34bbff11 100755 --- a/external/corert/src/System.Private.CoreLib/src/System.Private.CoreLib.sln +++ b/external/corert/src/System.Private.CoreLib/src/System.Private.CoreLib.sln @@ -1,16 +1,13 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26228.0 +VisualStudioVersion = 15.0.26228.9 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Private.CoreLib", "System.Private.CoreLib.csproj", "{BE95C560-B508-4588-8907-F9FC5BC1A0CF}" EndProject -Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "System.Private.CoreLib.Shared", "..\shared\System.Private.CoreLib.Shared.shproj", "{C5ED3C1D-B572-46F1-8F96-522A85CE1179}" -EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution ..\shared\System.Private.CoreLib.Shared.projitems*{be95c560-b508-4588-8907-f9fc5bc1a0cf}*SharedItemsImports = 4 - ..\shared\System.Private.CoreLib.Shared.projitems*{c5ed3c1d-b572-46f1-8f96-522a85ce1179}*SharedItemsImports = 13 EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|arm = Debug|arm diff --git a/external/corert/src/System.Private.CoreLib/src/System/Activator.cs b/external/corert/src/System.Private.CoreLib/src/System/Activator.cs index e77006e0a3..8ecfa24b8e 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Activator.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Activator.cs @@ -35,12 +35,15 @@ namespace System EETypePtr eetype = EETypePtr.EETypePtrOf(); - // ProjectN:936613 - Early exit for variable sized types (strings, arrays, etc.) as we cannot call - // CreateInstanceIntrinsic on them since the intrinsic will attempt to allocate an instance of these types - // and that is verboten (it results in silent heap corruption!). - if (eetype.ComponentSize != 0) + if (!RuntimeHelpers.IsReference()) + { + // Early out for valuetypes since we don't support default constructors anyway. + // This lets codegens that expand IsReference optimize away the rest of this code. + } + else if (eetype.ComponentSize != 0) { // ComponentSize > 0 indicates an array-like type (e.g. string, array, etc). + // Allocating this using the normal allocator would result in silent heap corruption. missingDefaultConstructor = true; } else if (eetype.IsInterface) @@ -54,8 +57,19 @@ namespace System try { +#if PROJECTN t = CreateInstanceIntrinsic(); - System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); +#else + t = (T)(RuntimeImports.RhNewObject(eetype)); + + // Run the default constructor. If the default constructor was missing, codegen + // will expand DefaultConstructorOf to ClassWithMissingConstructor::.ctor + // and we detect that later. + IntPtr defaultConstructor = DefaultConstructorOf(); + RawCalliHelper.Call(defaultConstructor, t); + DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); +#endif } catch (Exception e) { @@ -81,27 +95,14 @@ namespace System } [Intrinsic] - [DebuggerGuidedStepThrough] - private static T CreateInstanceIntrinsic() + private extern static T CreateInstanceIntrinsic(); + + [Intrinsic] + private static IntPtr DefaultConstructorOf() { - // Fallback implementation for codegens that don't support this intrinsic. - // This uses the type loader and doesn't have the kind of guarantees about it always working - // as the intrinsic expansion has. Also, it's slower. - - EETypePtr eetype = EETypePtr.EETypePtrOf(); - - // The default(T) check can be evaluated statically and will result in the body of this method - // becoming empty for valuetype Ts. We still need a dynamic IsNullable check to cover Nullables though. - // This will obviously need work once we start supporting default valuetype constructors. - if (default(T) == null && !eetype.IsNullable) - { - object o = null; - TypeLoaderExports.ActivatorCreateInstanceAny(ref o, eetype.RawValue); - System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); - return (T)o; - } - - return default(T); + // Codegens must expand this intrinsic. + // We could implement a fallback with the type loader if we wanted to, but it will be slow and unreliable. + throw new NotSupportedException(); } [ThreadStatic] diff --git a/external/corert/src/System.Private.CoreLib/src/System/AppContext.cs b/external/corert/src/System.Private.CoreLib/src/System/AppContext.cs index 5b593b70cf..6b316a4487 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/AppContext.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/AppContext.cs @@ -5,8 +5,11 @@ using System.Collections; using System.Collections.Generic; using System.IO; +using System.Reflection; +using System.Runtime; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using System.Text; namespace System @@ -24,6 +27,9 @@ namespace System private static readonly Dictionary s_switchMap = new Dictionary(); private static Dictionary s_localStore = new Dictionary(); private static string s_defaultBaseDirectory; + // AppDomain lives in CoreFX, but some of this class's events need to pass in AppDomains, so people registering those + // events need to first pass in an AppDomain that we stash here to pass back in the events. + private static object s_appDomain; static AppContext() { @@ -31,14 +37,13 @@ namespace System AppContextDefaultValues.PopulateDefaultValues(); } - public static string TargetFrameworkName + public static void SetAppDomain(object appDomain) { - get - { - throw new NotImplementedException(); - } + s_appDomain = appDomain; } + public static string TargetFrameworkName => Assembly.GetEntryAssembly()?.GetCustomAttribute()?.FrameworkName; + public static string BaseDirectory { get @@ -69,17 +74,6 @@ namespace System if (name == null) throw new ArgumentNullException(nameof(name)); - // SetData should only be used to set values that don't already exist. - object currentVal; - lock (((ICollection)s_localStore).SyncRoot) - { - s_localStore.TryGetValue(name, out currentVal); - } - if (currentVal != null) - { - throw new InvalidOperationException(SR.InvalidOperation_SetData_OnlyOnce); - } - lock (((ICollection)s_localStore).SyncRoot) { s_localStore[name] = data; @@ -102,7 +96,7 @@ namespace System } } - private static void OnFirstChanceException(object sender, FirstChanceExceptionEventArgs e) + internal static void OnFirstChanceException(object sender, FirstChanceExceptionEventArgs e) { var firstChanceException = FirstChanceException; if (firstChanceException != null) @@ -111,6 +105,12 @@ namespace System } } + [RuntimeExport("OnFirstChanceException")] + internal static void OnFirstChanceException(object e) + { + OnFirstChanceException(s_appDomain, new FirstChanceExceptionEventArgs((Exception)e)); + } + private static void OnProcessExit(object sender, EventArgs e) { var processExit = ProcessExit; diff --git a/external/corert/src/System.Private.CoreLib/src/System/AppContextConfigHelper.cs b/external/corert/src/System.Private.CoreLib/src/System/AppContextConfigHelper.cs new file mode 100644 index 0000000000..466e1636a0 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/AppContextConfigHelper.cs @@ -0,0 +1,88 @@ +// 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.Globalization; + +namespace System +{ + internal static class AppContextConfigHelper + { + internal static int GetInt32Config(string configName, int defaultValue, bool allowNegative = true) + { + try + { + object config = AppContext.GetData(configName); + int result = defaultValue; + switch (config) + { + case string str: + if (str.StartsWith("0x")) + { + result = Convert.ToInt32(str, 16); + } + else if (str.StartsWith("0")) + { + result = Convert.ToInt32(str, 8); + } + else + { + result = int.Parse(str, NumberStyles.AllowLeadingSign, NumberFormatInfo.InvariantInfo); + } + break; + case IConvertible convertible: + result = convertible.ToInt32(NumberFormatInfo.InvariantInfo); + break; + } + return !allowNegative && result < 0 ? defaultValue : result; + } + catch (FormatException) + { + return defaultValue; + } + catch (OverflowException) + { + return defaultValue; + } + } + + + internal static short GetInt16Config(string configName, short defaultValue, bool allowNegative = true) + { + try + { + object config = AppContext.GetData(configName); + short result = defaultValue; + switch (config) + { + case string str: + if (str.StartsWith("0x")) + { + result = Convert.ToInt16(str, 16); + } + else if (str.StartsWith("0")) + { + result = Convert.ToInt16(str, 8); + } + else + { + result = short.Parse(str, NumberStyles.AllowLeadingSign, NumberFormatInfo.InvariantInfo); + } + break; + case IConvertible convertible: + result = convertible.ToInt16(NumberFormatInfo.InvariantInfo); + break; + } + return !allowNegative && result < 0 ? defaultValue : result; + } + catch (FormatException) + { + return defaultValue; + } + catch (OverflowException) + { + return defaultValue; + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/AppContextDefaultValues.cs b/external/corert/src/System.Private.CoreLib/src/System/AppContextDefaultValues.cs index a838dfd0ff..a0093c94a3 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/AppContextDefaultValues.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/AppContextDefaultValues.cs @@ -100,7 +100,7 @@ namespace System { value = value.Substring(1); } - Version realVersion = new Version(value); + Version realVersion = Version.Parse(value); // The version class will represent some unset values as -1 internally (instead of 0). version = realVersion.Major * 10000; if (realVersion.Minor > 0) diff --git a/external/corert/src/System.Private.CoreLib/src/System/ArgIterator.cs b/external/corert/src/System.Private.CoreLib/src/System/ArgIterator.cs deleted file mode 100644 index d0d33f7a42..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/ArgIterator.cs +++ /dev/null @@ -1,60 +0,0 @@ -// 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.Runtime.InteropServices; - -namespace System -{ - [StructLayout(LayoutKind.Sequential)] - public struct ArgIterator - { - public ArgIterator(RuntimeArgumentHandle arglist) - { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_ArgIterator); // https://github.com/dotnet/corert/issues/395 - } - - [CLSCompliant(false)] - public unsafe ArgIterator(RuntimeArgumentHandle arglist, void* ptr) - { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_ArgIterator); // https://github.com/dotnet/corert/issues/395 - } - - public void End() - { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_ArgIterator); // https://github.com/dotnet/corert/issues/395 - } - - public override bool Equals(object o) - { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_ArgIterator); // https://github.com/dotnet/corert/issues/395 - } - - public override int GetHashCode() - { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_ArgIterator); // https://github.com/dotnet/corert/issues/395 - } - - [CLSCompliant(false)] - public TypedReference GetNextArg() - { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_ArgIterator); // https://github.com/dotnet/corert/issues/395 - } - - [CLSCompliant(false)] - public TypedReference GetNextArg(RuntimeTypeHandle rth) - { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_ArgIterator); // https://github.com/dotnet/corert/issues/395 - } - - public unsafe RuntimeTypeHandle GetNextArgType() - { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_ArgIterator); // https://github.com/dotnet/corert/issues/395 - } - - public int GetRemainingCount() - { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_ArgIterator); // https://github.com/dotnet/corert/issues/395 - } - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Array.CoreRT.cs b/external/corert/src/System.Private.CoreLib/src/System/Array.CoreRT.cs index aaa4af5230..9ae235f14b 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Array.CoreRT.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Array.CoreRT.cs @@ -10,10 +10,11 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; -using System.Diagnostics.Contracts; using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; using Internal.Reflection.Core.NonPortable; +using Internal.IntrinsicSupport; using EEType = Internal.Runtime.EEType; #if BIT64 @@ -36,6 +37,7 @@ namespace System // CS0649: Field '{blah}' is never assigned to, and will always have its default value #pragma warning disable 649 // This field should be the first field in Array as the runtime/compilers depend on it + [NonSerialized] private int _numComponents; #pragma warning restore @@ -78,12 +80,6 @@ namespace System if ((object)elementType == null) throw new ArgumentNullException(nameof(elementType)); - Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Rank == 1); - Contract.EndContractBlock(); - - elementType = elementType.UnderlyingSystemType; - return CreateSzArray(elementType, length); } @@ -96,13 +92,6 @@ namespace System if (length2 < 0) throw new ArgumentOutOfRangeException(nameof(length2)); - Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Rank == 2); - Contract.Ensures(Contract.Result().GetLength(0) == length1); - Contract.Ensures(Contract.Result().GetLength(1) == length2); - - elementType = elementType.UnderlyingSystemType; - Type arrayType = GetArrayTypeFromElementType(elementType, true, 2); int* pLengths = stackalloc int[2]; pLengths[0] = length1; @@ -121,14 +110,6 @@ namespace System if (length3 < 0) throw new ArgumentOutOfRangeException(nameof(length3)); - Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Rank == 3); - Contract.Ensures(Contract.Result().GetLength(0) == length1); - Contract.Ensures(Contract.Result().GetLength(1) == length2); - Contract.Ensures(Contract.Result().GetLength(2) == length3); - - elementType = elementType.UnderlyingSystemType; - Type arrayType = GetArrayTypeFromElementType(elementType, true, 3); int* pLengths = stackalloc int[3]; pLengths[0] = length1; @@ -146,12 +127,6 @@ namespace System if (lengths.Length == 0) throw new ArgumentException(SR.Arg_NeedAtLeast1Rank); - Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Rank == lengths.Length); - Contract.EndContractBlock(); - - elementType = elementType.UnderlyingSystemType; - if (lengths.Length == 1) { int length = lengths[0]; @@ -175,11 +150,6 @@ namespace System throw new ArgumentException(SR.Arg_RanksAndBounds); if (lengths.Length == 0) throw new ArgumentException(SR.Arg_NeedAtLeast1Rank); - Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Rank == lengths.Length); - Contract.EndContractBlock(); - - elementType = elementType.UnderlyingSystemType; return CreateMultiDimArray(elementType, lengths, lowerBounds); } @@ -213,10 +183,8 @@ namespace System private static Type GetArrayTypeFromElementType(Type elementType, bool multiDim, int rank) { - if (!elementType.IsRuntimeImplemented()) - throw new InvalidOperationException(SR.InvalidOperation_ArrayCreateInstance_NotARuntimeType); - if (elementType.Equals(typeof(void))) - throw new NotSupportedException(SR.NotSupported_VoidArray); + elementType = elementType.UnderlyingSystemType; + ValidateElementType(elementType); if (multiDim) return elementType.MakeArrayType(rank); @@ -224,6 +192,22 @@ namespace System return elementType.MakeArrayType(); } + private static void ValidateElementType(Type elementType) + { + if (!elementType.IsRuntimeImplemented()) + throw new ArgumentException(SR.Arg_MustBeType, nameof(elementType)); + while (elementType.IsArray) + { + elementType = elementType.GetElementType(); + } + if (elementType.IsByRef || elementType.IsByRefLike) + throw new NotSupportedException(SR.NotSupported_ByRefLikeArray); + if (elementType.Equals(CommonRuntimeTypes.Void)) + throw new NotSupportedException(SR.NotSupported_VoidArray); + if (elementType.ContainsGenericParameters) + throw new NotSupportedException(SR.NotSupported_OpenType); + } + public void Initialize() { // Project N port note: On the desktop, this api is a nop unless the array element type is a value type with @@ -868,73 +852,6 @@ namespace System } } - /// - /// Copy the contents of a native buffer into a managed array. This requires that the type of the - /// destination array be blittable. - /// - /// Unmanaged memory to copy from. - /// Array to copy into. The type of the elements of the array must be blittable - /// First index in the destination array to begin copying into - /// Number of elements to copy - internal static unsafe void CopyToManaged(IntPtr source, Array destination, int startIndex, int length) - { - if (source == IntPtr.Zero) - throw new ArgumentNullException(nameof(source)); - if (destination == null) - throw new ArgumentNullException(nameof(destination)); - if (!destination.IsElementTypeBlittable) - throw new ArgumentException(nameof(destination), SR.Arg_CopyNonBlittableArray); - if (startIndex < 0) - throw new ArgumentOutOfRangeException(nameof(startIndex), SR.Arg_CopyOutOfRange); - if (length < 0) - throw new ArgumentOutOfRangeException(nameof(length), SR.Arg_CopyOutOfRange); - if ((uint)startIndex + (uint)length > (uint)destination.Length) - throw new ArgumentOutOfRangeException(nameof(startIndex), SR.Arg_CopyOutOfRange); - - nuint bytesToCopy = (nuint)length * destination.ElementSize; - nuint startOffset = (nuint)startIndex * destination.ElementSize; - - fixed (byte* pDestination = &destination.GetRawArrayData()) - { - byte* destinationData = pDestination + startOffset; - Buffer.Memmove(destinationData, (byte*)source, bytesToCopy); - } - } - - /// - /// Copy the contents of the source array into unmanaged memory. This requires that the type of - /// the source array be blittable. - /// - /// Array to copy from. This must be non-null and have blittable elements - /// First index in the source array to begin copying - /// Pointer to the unmanaged memory to blit the memory into - /// Number of elements to copy - internal static unsafe void CopyToNative(Array source, int startIndex, IntPtr destination, int length) - { - if (source == null) - throw new ArgumentNullException(nameof(source)); - if (!source.IsElementTypeBlittable) - throw new ArgumentException(nameof(source), SR.Arg_CopyNonBlittableArray); - if (destination == IntPtr.Zero) - throw new ArgumentNullException(nameof(destination)); - if (startIndex < 0) - throw new ArgumentOutOfRangeException(nameof(startIndex), SR.Arg_CopyOutOfRange); - if (length < 0) - throw new ArgumentOutOfRangeException(nameof(length), SR.Arg_CopyOutOfRange); - if ((uint)startIndex + (uint)length > (uint)source.Length) - throw new ArgumentOutOfRangeException(nameof(startIndex), SR.Arg_CopyOutOfRange); - Contract.EndContractBlock(); - - nuint bytesToCopy = (nuint)length * source.ElementSize; - nuint startOffset = (nuint)startIndex * source.ElementSize; - - fixed (byte* pSource = &source.GetRawArrayData()) - { - byte* sourceData = pSource + startOffset; - Buffer.Memmove((byte*)destination, sourceData, bytesToCopy); - } - } - public static void Clear(Array array, int index, int length) { if (!RuntimeImports.TryArrayClear(array, index, length)) @@ -959,6 +876,7 @@ namespace System // implementation of advanced range check elimination in future. // Keep in sync with vm\gcscan.cpp and HashHelpers.MaxPrimeArrayLength. internal const int MaxArrayLength = 0X7FEFFFFF; + internal const int MaxByteArrayLength = MaxArrayLength; public int GetLength(int dimension) { @@ -1019,37 +937,6 @@ namespace System return ret; } - // These functions look odd, as they are part of a complex series of compiler intrinsics - // designed to produce very high quality code for equality comparison cases without utilizing - // reflection like other platforms. The major complication is that the specification of - // IndexOf is that it is supposed to use IEquatable if possible, but that requirement - // cannot be expressed in IL directly due to the lack of constraints. - // Instead, specialization at call time is used within the compiler. - // - // General Approach - // - Perform fancy redirection for Array.GetComparerForReferenceTypesOnly(). If T is a reference - // type or UniversalCanon, have this redirect to EqualityComparer.get_Default, Otherwise, use - // the function as is. (will return null in that case) - // - Change the contents of the IndexOf functions to have a pair of loops. One for if - // GetComparerForReferenceTypesOnly returns null, and one for when it does not. - // - If it does not return null, call the EqualityComparer code. - // - If it does return null, use a special function StructOnlyEquals(). - // - Calls to that function result in calls to a pair of helper function in - // EqualityComparerHelpers (StructOnlyEqualsIEquatable, or StructOnlyEqualsNullable) - // depending on whether or not they are the right function to call. - // - The end result is that in optimized builds, we have the same single function compiled size - // characteristics that the old EqualsOnlyComparer.Equals function had, but we maintain - // correctness as well. - private static EqualityComparer GetComparerForReferenceTypesOnly() - { -#if !CORERT - // When T is a reference type or a universal canon type, then this will redirect to EqualityComparer.Default. - return null; -#else - return EqualityComparer.Default; -#endif - } - // Wraps an IComparer inside an IComparer. private sealed class ComparerAsComparerT : IComparer { @@ -1135,7 +1022,6 @@ namespace System { if (Rank != 2) throw new ArgumentException(SR.Arg_Need2DArray); - Contract.EndContractBlock(); int* pIndices = stackalloc int[2]; pIndices[0] = index1; @@ -1147,7 +1033,6 @@ namespace System { if (Rank != 3) throw new ArgumentException(SR.Arg_Need3DArray); - Contract.EndContractBlock(); int* pIndices = stackalloc int[3]; pIndices[0] = index1; @@ -1265,7 +1150,6 @@ namespace System { if (Rank != 2) throw new ArgumentException(SR.Arg_Need2DArray); - Contract.EndContractBlock(); int* pIndices = stackalloc int[2]; pIndices[0] = index1; @@ -1277,7 +1161,6 @@ namespace System { if (Rank != 3) throw new ArgumentException(SR.Arg_Need3DArray); - Contract.EndContractBlock(); int* pIndices = stackalloc int[3]; pIndices[0] = index1; @@ -1370,11 +1253,6 @@ namespace System } } - private static bool StructOnlyEquals(T left, T right) - { - return left.Equals(right); - } - private sealed partial class ArrayEnumerator : IEnumerator, ICloneable { public Object Current @@ -1400,27 +1278,10 @@ namespace System } } - internal bool IsElementTypeBlittable - { - get - { - if (ElementEEType.IsPrimitive) - return true; - - if (ElementEEType.IsValueType && !ElementEEType.HasPointers) - return true; - - if (ElementEEType.IsPointer) - return true; - - return false; - } - } - private static int IndexOfImpl(T[] array, T value, int startIndex, int count) { - // See comment above Array.GetComparerForReferenceTypesOnly for details - EqualityComparer comparer = GetComparerForReferenceTypesOnly(); + // See comment in EqualityComparerHelpers.GetComparerForReferenceTypesOnly for details + EqualityComparer comparer = EqualityComparerHelpers.GetComparerForReferenceTypesOnly(); int endIndex = startIndex + count; if (comparer != null) @@ -1435,7 +1296,7 @@ namespace System { for (int i = startIndex; i < endIndex; i++) { - if (StructOnlyEquals(array[i], value)) + if (EqualityComparerHelpers.StructOnlyEquals(array[i], value)) return i; } } @@ -1445,8 +1306,8 @@ namespace System private static int LastIndexOfImpl(T[] array, T value, int startIndex, int count) { - // See comment above Array.GetComparerForReferenceTypesOnly for details - EqualityComparer comparer = GetComparerForReferenceTypesOnly(); + // See comment in EqualityComparerHelpers.GetComparerForReferenceTypesOnly for details + EqualityComparer comparer = EqualityComparerHelpers.GetComparerForReferenceTypesOnly(); int endIndex = startIndex - count + 1; if (comparer != null) @@ -1461,7 +1322,7 @@ namespace System { for (int i = startIndex; i >= endIndex; i--) { - if (StructOnlyEquals(array[i], value)) + if (EqualityComparerHelpers.StructOnlyEquals(array[i], value)) return i; } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Array.cs b/external/corert/src/System.Private.CoreLib/src/System/Array.cs index 3ea25af7c2..184964df19 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Array.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Array.cs @@ -10,10 +10,15 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; -using System.Diagnostics.Contracts; + +using Internal.Runtime.CompilerServices; namespace System { + [Serializable] +#if !MONO + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#endif public abstract partial class Array : ICollection, IEnumerable, IList, IStructuralComparable, IStructuralEquatable, ICloneable { public static Array CreateInstance(Type elementType, params long[] lengths) @@ -259,10 +264,6 @@ namespace System if (converter == null) throw new ArgumentNullException(nameof(converter)); - Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == array.Length); - Contract.EndContractBlock(); - TOutput[] newArray = new TOutput[array.Length]; for (int i = 0; i < array.Length; i++) { @@ -295,7 +296,6 @@ namespace System { if (index > Int32.MaxValue || index < Int32.MinValue) throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_HugeArrayNotSupported); - Contract.EndContractBlock(); this.CopyTo(array, (int)index); } @@ -308,8 +308,6 @@ namespace System if (action == null) throw new ArgumentNullException(nameof(action)); - Contract.EndContractBlock(); - for (int i = 0; i < array.Length; i++) { action(array[i]); @@ -342,7 +340,6 @@ namespace System { if (index > Int32.MaxValue || index < Int32.MinValue) throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_HugeArrayNotSupported); - Contract.EndContractBlock(); return this.GetValue((int)index); } @@ -353,7 +350,6 @@ namespace System throw new ArgumentOutOfRangeException(nameof(index1), SR.ArgumentOutOfRange_HugeArrayNotSupported); if (index2 > Int32.MaxValue || index2 < Int32.MinValue) throw new ArgumentOutOfRangeException(nameof(index2), SR.ArgumentOutOfRange_HugeArrayNotSupported); - Contract.EndContractBlock(); return this.GetValue((int)index1, (int)index2); } @@ -366,7 +362,6 @@ namespace System throw new ArgumentOutOfRangeException(nameof(index2), SR.ArgumentOutOfRange_HugeArrayNotSupported); if (index3 > Int32.MaxValue || index3 < Int32.MinValue) throw new ArgumentOutOfRangeException(nameof(index3), SR.ArgumentOutOfRange_HugeArrayNotSupported); - Contract.EndContractBlock(); return this.GetValue((int)index1, (int)index2, (int)index3); } @@ -377,7 +372,6 @@ namespace System throw new ArgumentNullException(nameof(indices)); if (Rank != indices.Length) throw new ArgumentException(SR.Arg_RankIndices); - Contract.EndContractBlock(); int[] intIndices = new int[indices.Length]; @@ -879,22 +873,15 @@ namespace System if (array.Rank != 1) throw new RankException(SR.Rank_MultiDimNotSupported); - int i = index; - int j = index + length - 1; Object[] objArray = array as Object[]; if (objArray != null) { - while (i < j) - { - Object temp = objArray[i]; - objArray[i] = objArray[j]; - objArray[j] = temp; - i++; - j--; - } + Array.Reverse(objArray, index, length); } else { + int i = index; + int j = index + length - 1; while (i < j) { Object temp = array.GetValue(i); @@ -923,13 +910,22 @@ namespace System if (array.Length - index < length) throw new ArgumentException(SR.Argument_InvalidOffLen); +#if !MONO + ref T p = ref Unsafe.As(ref array.GetRawSzArrayData()); +#endif int i = index; int j = index + length - 1; while (i < j) { +#if MONO T temp = array[i]; array[i] = array[j]; array[j] = temp; +#else + T temp = Unsafe.Add(ref p, i); + Unsafe.Add(ref p, i) = Unsafe.Add(ref p, j); + Unsafe.Add(ref p, j) = temp; +#endif i++; j--; } @@ -1128,7 +1124,6 @@ namespace System { if (keys == null) throw new ArgumentNullException(nameof(keys)); - Contract.EndContractBlock(); Sort(keys, items, 0, keys.Length, null); } @@ -1141,7 +1136,6 @@ namespace System { if (keys == null) throw new ArgumentNullException(nameof(keys)); - Contract.EndContractBlock(); Sort(keys, items, 0, keys.Length, comparer); } @@ -1153,7 +1147,6 @@ namespace System throw new ArgumentOutOfRangeException((length < 0 ? nameof(length) : nameof(index)), SR.ArgumentOutOfRange_NeedNonNegNum); if (keys.Length - index < length || (items != null && index > items.Length - length)) throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.EndContractBlock(); if (length > 1) { @@ -1460,4 +1453,4 @@ namespace System } } } -} \ No newline at end of file +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Attribute.cs b/external/corert/src/System.Private.CoreLib/src/System/Attribute.cs index 5445ffbd04..5f20722e2b 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Attribute.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Attribute.cs @@ -7,6 +7,8 @@ using System.Diagnostics; namespace System { [AttributeUsageAttribute(AttributeTargets.All, Inherited = true, AllowMultiple = false)] + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public abstract partial class Attribute { protected Attribute() { } diff --git a/external/corert/src/System.Private.CoreLib/src/System/BitConverter.cs b/external/corert/src/System.Private.CoreLib/src/System/BitConverter.cs deleted file mode 100644 index c436704b76..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/BitConverter.cs +++ /dev/null @@ -1,472 +0,0 @@ -// 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 Internal.Runtime.CompilerServices; -using System.Diagnostics; -using System.Diagnostics.Contracts; -using System.Security; - -namespace System -{ - // The BitConverter class contains methods for - // converting an array of bytes to one of the base data - // types, as well as for converting a base data type to an - // array of bytes. - public static class BitConverter - { - // This field indicates the "endianess" of the architecture. - // The value is set to true if the architecture is - // little endian; false if it is big endian. -#if BIGENDIAN - public static readonly bool IsLittleEndian /* = false */; -#else - public static readonly bool IsLittleEndian = true; -#endif - - // Converts a Boolean into an array of bytes with length one. - public static byte[] GetBytes(bool value) - { - Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == 1); - - byte[] r = new byte[1]; - r[0] = (value ? (byte)1 : (byte)0); - return r; - } - - // Converts a char into an array of bytes with length two. - public static byte[] GetBytes(char value) - { - Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == 2); - - return GetBytes((short)value); - } - - // Converts a short into an array of bytes with length - // two. - public static unsafe byte[] GetBytes(short value) - { - Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == 2); - - byte[] bytes = new byte[2]; - fixed (byte* b = &bytes[0]) - *((short*)b) = value; - return bytes; - } - - // Converts an int into an array of bytes with length - // four. - public static unsafe byte[] GetBytes(int value) - { - Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == 4); - - byte[] bytes = new byte[4]; - fixed (byte* b = &bytes[0]) - *((int*)b) = value; - return bytes; - } - - // Converts a long into an array of bytes with length - // eight. - public static unsafe byte[] GetBytes(long value) - { - Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == 8); - - byte[] bytes = new byte[8]; - fixed (byte* b = &bytes[0]) - *((long*)b) = value; - return bytes; - } - - // Converts an ushort into an array of bytes with - // length two. - [CLSCompliant(false)] - public static byte[] GetBytes(ushort value) - { - Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == 2); - - return GetBytes(unchecked((short)value)); - } - - // Converts an uint into an array of bytes with - // length four. - [CLSCompliant(false)] - public static byte[] GetBytes(uint value) - { - Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == 4); - - return GetBytes(unchecked((int)value)); - } - - // Converts an unsigned long into an array of bytes with - // length eight. - [CLSCompliant(false)] - public static byte[] GetBytes(ulong value) - { - Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == 8); - - return GetBytes(unchecked((long)value)); - } - - // Converts a float into an array of bytes with length - // four. - public static unsafe byte[] GetBytes(float value) - { - Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == 4); - - return GetBytes(*(int*)&value); - } - - // Converts a double into an array of bytes with length - // eight. - public static unsafe byte[] GetBytes(double value) - { - Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == 8); - - return GetBytes(*(long*)&value); - } - - // Converts an array of bytes into a char. - public static char ToChar(byte[] value, int startIndex) - { - if (value == null) - ThrowValueArgumentNull(); - if (unchecked((uint)startIndex) >= value.Length) - ThrowStartIndexArgumentOutOfRange(); - if (startIndex > value.Length - 2) - ThrowValueArgumentTooSmall(); - Contract.EndContractBlock(); - - return unchecked((char)ToInt16(value, startIndex)); - } - - // Converts an array of bytes into a short. - public static unsafe short ToInt16(byte[] value, int startIndex) - { - if (value == null) - ThrowValueArgumentNull(); - if (unchecked((uint)startIndex) >= value.Length) - ThrowStartIndexArgumentOutOfRange(); - if (startIndex > value.Length - 2) - ThrowValueArgumentTooSmall(); - Contract.EndContractBlock(); - - fixed (byte* pbyte = &value[startIndex]) - { - if (startIndex % 2 == 0) - { - // data is aligned - return *((short*)pbyte); - } - else if (IsLittleEndian) - { - return (short)((*pbyte) | (*(pbyte + 1) << 8)); - } - else - { - return (short)((*pbyte << 8) | (*(pbyte + 1))); - } - } - } - - // Converts an array of bytes into an int. - public static unsafe int ToInt32(byte[] value, int startIndex) - { - if (value == null) - ThrowValueArgumentNull(); - if (unchecked((uint)startIndex) >= value.Length) - ThrowStartIndexArgumentOutOfRange(); - if (startIndex > value.Length - 4) - ThrowValueArgumentTooSmall(); - Contract.EndContractBlock(); - - fixed (byte* pbyte = &value[startIndex]) - { - if (startIndex % 4 == 0) - { - // data is aligned - return *((int*)pbyte); - } - else if (IsLittleEndian) - { - return (*pbyte) | (*(pbyte + 1) << 8) | (*(pbyte + 2) << 16) | (*(pbyte + 3) << 24); - } - else - { - return (*pbyte << 24) | (*(pbyte + 1) << 16) | (*(pbyte + 2) << 8) | (*(pbyte + 3)); - } - } - } - - // Converts an array of bytes into a long. - public static unsafe long ToInt64(byte[] value, int startIndex) - { - if (value == null) - ThrowValueArgumentNull(); - if (unchecked((uint)startIndex) >= value.Length) - ThrowStartIndexArgumentOutOfRange(); - if (startIndex > value.Length - 8) - ThrowValueArgumentTooSmall(); - Contract.EndContractBlock(); - - fixed (byte* pbyte = &value[startIndex]) - { - if (startIndex % 8 == 0) - { - // data is aligned - return *((long*)pbyte); - } - else if (IsLittleEndian) - { - int i1 = (*pbyte) | (*(pbyte + 1) << 8) | (*(pbyte + 2) << 16) | (*(pbyte + 3) << 24); - int i2 = (*(pbyte + 4)) | (*(pbyte + 5) << 8) | (*(pbyte + 6) << 16) | (*(pbyte + 7) << 24); - return unchecked((uint)i1) | ((long)i2 << 32); - } - else - { - int i1 = (*pbyte << 24) | (*(pbyte + 1) << 16) | (*(pbyte + 2) << 8) | (*(pbyte + 3)); - int i2 = (*(pbyte + 4) << 24) | (*(pbyte + 5) << 16) | (*(pbyte + 6) << 8) | (*(pbyte + 7)); - return unchecked((uint)i2) | ((long)i1 << 32); - } - } - } - - - // Converts an array of bytes into an ushort. - // - [CLSCompliant(false)] - public static ushort ToUInt16(byte[] value, int startIndex) - { - if (value == null) - ThrowValueArgumentNull(); - if (unchecked((uint)startIndex) >= value.Length) - ThrowStartIndexArgumentOutOfRange(); - if (startIndex > value.Length - 2) - ThrowValueArgumentTooSmall(); - Contract.EndContractBlock(); - - return unchecked((ushort)ToInt16(value, startIndex)); - } - - // Converts an array of bytes into an uint. - // - [CLSCompliant(false)] - public static uint ToUInt32(byte[] value, int startIndex) - { - if (value == null) - ThrowValueArgumentNull(); - if (unchecked((uint)startIndex) >= value.Length) - ThrowStartIndexArgumentOutOfRange(); - if (startIndex > value.Length - 4) - ThrowValueArgumentTooSmall(); - Contract.EndContractBlock(); - - return unchecked((uint)ToInt32(value, startIndex)); - } - - // Converts an array of bytes into an unsigned long. - // - [CLSCompliant(false)] - public static ulong ToUInt64(byte[] value, int startIndex) - { - if (value == null) - ThrowValueArgumentNull(); - if (unchecked((uint)startIndex) >= value.Length) - ThrowStartIndexArgumentOutOfRange(); - if (startIndex > value.Length - 8) - ThrowValueArgumentTooSmall(); - Contract.EndContractBlock(); - - return unchecked((ulong)ToInt64(value, startIndex)); - } - - // Converts an array of bytes into a float. - public static unsafe float ToSingle(byte[] value, int startIndex) - { - if (value == null) - ThrowValueArgumentNull(); - if (unchecked((uint)startIndex) >= value.Length) - ThrowStartIndexArgumentOutOfRange(); - if (startIndex > value.Length - 4) - ThrowValueArgumentTooSmall(); - Contract.EndContractBlock(); - - int val = ToInt32(value, startIndex); - return *(float*)&val; - } - - // Converts an array of bytes into a double. - public static unsafe double ToDouble(byte[] value, int startIndex) - { - if (value == null) - ThrowValueArgumentNull(); - if (unchecked((uint)startIndex) >= value.Length) - ThrowStartIndexArgumentOutOfRange(); - if (startIndex > value.Length - 8) - ThrowValueArgumentTooSmall(); - Contract.EndContractBlock(); - - long val = ToInt64(value, startIndex); - return *(double*)&val; - } - - private static char GetHexValue(int i) - { - Debug.Assert(i >= 0 && i < 16, "i is out of range."); - if (i < 10) - { - return (char)(i + '0'); - } - - return (char)(i - 10 + 'A'); - } - - // Converts an array of bytes into a String. - public static string ToString(byte[] value, int startIndex, int length) - { - if (value == null) - ThrowValueArgumentNull(); - if (startIndex < 0 || startIndex >= value.Length && startIndex > 0) - ThrowStartIndexArgumentOutOfRange(); - if (length < 0) - throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_GenericPositive); - if (startIndex > value.Length - length) - ThrowValueArgumentTooSmall(); - Contract.EndContractBlock(); - - if (length == 0) - { - return string.Empty; - } - - if (length > (int.MaxValue / 3)) - { - // (Int32.MaxValue / 3) == 715,827,882 Bytes == 699 MB - throw new ArgumentOutOfRangeException(nameof(length), SR.Format(SR.ArgumentOutOfRange_LengthTooLarge, (int.MaxValue / 3))); - } - - int chArrayLength = length * 3; - const int StackLimit = 512; // arbitrary limit to switch from stack to heap allocation - unsafe - { - if (chArrayLength < StackLimit) - { - char* chArrayPtr = stackalloc char[chArrayLength]; - return ToString(value, startIndex, length, chArrayPtr, chArrayLength); - } - else - { - char[] chArray = new char[chArrayLength]; - fixed (char* chArrayPtr = &chArray[0]) - return ToString(value, startIndex, length, chArrayPtr, chArrayLength); - } - } - } - - private static unsafe string ToString(byte[] value, int startIndex, int length, char* chArray, int chArrayLength) - { - Debug.Assert(length > 0); - Debug.Assert(chArrayLength == length * 3); - - char* p = chArray; - int endIndex = startIndex + length; - for (int i = startIndex; i < endIndex; i++) - { - byte b = value[i]; - *p++ = GetHexValue(b >> 4); - *p++ = GetHexValue(b & 0xF); - *p++ = '-'; - } - - // We don't need the last '-' character - return new string(chArray, 0, chArrayLength - 1); - } - - // Converts an array of bytes into a String. - public static string ToString(byte[] value) - { - if (value == null) - ThrowValueArgumentNull(); - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); - return ToString(value, 0, value.Length); - } - - // Converts an array of bytes into a String. - public static string ToString(byte[] value, int startIndex) - { - if (value == null) - ThrowValueArgumentNull(); - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); - return ToString(value, startIndex, value.Length - startIndex); - } - - /*==================================ToBoolean=================================== - **Action: Convert an array of bytes to a boolean value. We treat this array - ** as if the first 4 bytes were an Int4 an operate on this value. - **Returns: True if the Int4 value of the first 4 bytes is non-zero. - **Arguments: value -- The byte array - ** startIndex -- The position within the array. - **Exceptions: See ToInt4. - ==============================================================================*/ - // Converts an array of bytes into a boolean. - public static bool ToBoolean(byte[] value, int startIndex) - { - if (value == null) - ThrowValueArgumentNull(); - if (startIndex < 0) - ThrowStartIndexArgumentOutOfRange(); - if (startIndex > value.Length - 1) - ThrowStartIndexArgumentOutOfRange(); // differs from other overloads, which throw base ArgumentException - Contract.EndContractBlock(); - - return value[startIndex] != 0; - } - - public static unsafe long DoubleToInt64Bits(double value) - { - return *((long*)&value); - } - - public static unsafe double Int64BitsToDouble(long value) - { - return *((double*)&value); - } - - public static unsafe int SingleToInt32Bits(float value) - { - return *((int*)&value); - } - - public static unsafe float Int32BitsToSingle(int value) - { - return *((float*)&value); - } - - private static void ThrowValueArgumentNull() - { - throw new ArgumentNullException("value"); - } - - private static void ThrowStartIndexArgumentOutOfRange() - { - throw new ArgumentOutOfRangeException("startIndex", SR.ArgumentOutOfRange_Index); - } - - private static void ThrowValueArgumentTooSmall() - { - throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall, "value"); - } - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Buffer.cs b/external/corert/src/System.Private.CoreLib/src/System/Buffer.cs index 0bdad3203d..5e31e69fdd 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Buffer.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Buffer.cs @@ -9,10 +9,11 @@ using System; using System.Runtime; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; +using Internal.Runtime.CompilerServices; + #if BIT64 using nuint = System.UInt64; #else @@ -21,15 +22,8 @@ using nuint = System.UInt32; namespace System { - [EagerStaticClassConstruction] public static class Buffer { - /// - /// This field is used to quickly check whether an array that is given to the BlockCopy - /// method is a byte array and the code can take optimized path. - /// - private static readonly EETypePtr s_byteArrayEEType = EETypePtr.EETypePtrOf(); - public static unsafe void BlockCopy(Array src, int srcOffset, Array dst, int dstOffset, int count) @@ -43,8 +37,8 @@ namespace System throw new ArgumentNullException(nameof(dst)); // Use optimized path for byte arrays since this is the main scenario for Buffer::BlockCopy - EETypePtr byteArrayEEType = s_byteArrayEEType; - if (src.EETypePtr.FastEquals(byteArrayEEType)) + // We only need an unreliable comparison since the slow path can handle the byte[] case too. + if (src.EETypePtr.FastEqualsUnreliable(EETypePtr.EETypePtrOf())) { uSrcLen = (nuint)src.Length; } @@ -58,7 +52,9 @@ namespace System if (src != dst) { - if (dst.EETypePtr.FastEquals(byteArrayEEType)) + // Use optimized path for byte arrays since this is the main scenario for Buffer::BlockCopy + // We only need an unreliable comparison since the slow path can handle the byte[] case too. + if (dst.EETypePtr.FastEqualsUnreliable(EETypePtr.EETypePtrOf())) { uDstLen = (nuint)dst.Length; } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Buffers/ArrayPoolEventSource.cs b/external/corert/src/System.Private.CoreLib/src/System/Buffers/ArrayPoolEventSource.cs deleted file mode 100644 index 02691a3a7d..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/Buffers/ArrayPoolEventSource.cs +++ /dev/null @@ -1,37 +0,0 @@ -// 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. - -namespace System.Buffers -{ - internal sealed class ArrayPoolEventSource - { - internal readonly static ArrayPoolEventSource Log = new ArrayPoolEventSource(); - - // TODO: EventSource instrumentation https://github.com/dotnet/corert/issues/2414 - - internal bool IsEnabled() - { - return false; - } - - internal enum BufferAllocatedReason : int - { - Pooled, - OverMaximumSize, - PoolExhausted - } - - internal void BufferRented(int bufferId, int bufferSize, int poolId, int bucketId) - { - } - - internal void BufferAllocated(int bufferId, int bufferSize, int poolId, int bucketId, BufferAllocatedReason reason) - { - } - - internal void BufferReturned(int bufferId, int bufferSize, int poolId) - { - } - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/System/ByReference.cs b/external/corert/src/System.Private.CoreLib/src/System/ByReference.cs index 2644a8a552..6a077dc6d4 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/ByReference.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/ByReference.cs @@ -9,9 +9,8 @@ namespace System // ByReference is meant to be used to represent "ref T" fields. It is working // around lack of first class support for byref fields in C# and IL. The JIT and // type loader have special handling for it that turns it into a thin wrapper around ref T. - [StackOnly] [System.Runtime.CompilerServices.DependencyReductionRoot] // TODO: put this in System.Private.ILToolchain contract instead - internal struct ByReference + internal ref struct ByReference { // CS0169: The private field '{blah}' is never used #pragma warning disable 169 @@ -24,7 +23,7 @@ namespace System // Implemented as a JIT intrinsic - This default implementation is for // completeness and to provide a concrete error if called via reflection // or if intrinsic is missed. - throw new System.PlatformNotSupportedException(); + throw new NotSupportedException(); } public ref T Value @@ -35,7 +34,7 @@ namespace System // Implemented as a JIT intrinsic - This default implementation is for // completeness and to provide a concrete error if called via reflection // or if the intrinsic is missed. - throw new System.PlatformNotSupportedException(); + throw new NotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Collections/Concurrent/LowLevelConcurrentQueue.cs b/external/corert/src/System.Private.CoreLib/src/System/Collections/Concurrent/LowLevelConcurrentQueue.cs index fd51be3e8e..5e799db68e 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Collections/Concurrent/LowLevelConcurrentQueue.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Collections/Concurrent/LowLevelConcurrentQueue.cs @@ -1,24 +1,21 @@ // 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. -#pragma warning disable 0420 -// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// A lock-free, concurrent queue primitive, and its associated debugger view type. +// +// This is a stripped-down version of ConcurrentQueue, for use from within the System.Threading +// surface to eliminate a dependency on System.Collections.Concurrent. +// Please try to keep this in sync with the public ConcurrentQueue implementation. // -// A lock-free, concurrent queue primitive, and its associated debugger view type. -// -// This is a stripped-down version of ConcurrentQueue, for use from within the System.Threading -// surface to eliminate a dependency on System.Collections.Concurrent. -// Please try to keep this in sync with the public ConcurrentQueue implementation. -// -// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -using System; -using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; -using System.Security; +using System.Runtime.Serialization; using System.Threading; namespace System.Collections.Concurrent @@ -28,42 +25,143 @@ namespace System.Collections.Concurrent /// /// Specifies the type of elements in the queue. /// - /// All public and protected members of are thread-safe and may be used + /// All public and protected members of are thread-safe and may be used /// concurrently from multiple threads. /// - internal class LowLevelConcurrentQueue /*: IProducerConsumerCollection*/ : IEnumerable + [DebuggerDisplay("Count = {Count}")] + internal class LowLevelConcurrentQueue : /* IProducerConsumerCollection, */ IReadOnlyCollection { - //fields of ConcurrentQueue - private volatile Segment _head; + // This implementation provides an unbounded, multi-producer multi-consumer queue + // that supports the standard Enqueue/TryDequeue operations, as well as support for + // snapshot enumeration (GetEnumerator, ToArray, CopyTo), peeking, and Count/IsEmpty. + // It is composed of a linked list of bounded ring buffers, each of which has a head + // and a tail index, isolated from each other to minimize false sharing. As long as + // the number of elements in the queue remains less than the size of the current + // buffer (Segment), no additional allocations are required for enqueued items. When + // the number of items exceeds the size of the current segment, the current segment is + // "frozen" to prevent further enqueues, and a new segment is linked from it and set + // as the new tail segment for subsequent enqueues. As old segments are consumed by + // dequeues, the head reference is updated to point to the segment that dequeuers should + // try next. To support snapshot enumeration, segments also support the notion of + // preserving for observation, whereby they avoid overwriting state as part of dequeues. + // Any operation that requires a snapshot results in all current segments being + // both frozen for enqueues and preserved for observation: any new enqueues will go + // to new segments, and dequeuers will consume from the existing segments but without + // overwriting the existing data. - private volatile Segment _tail; - - private const int SEGMENT_SIZE = 32; - - //number of snapshot takers, GetEnumerator(), ToList() and ToArray() operations take snapshot. - internal volatile int m_numSnapshotTakers = 0; + /// Initial length of the segments used in the queue. + private const int InitialSegmentLength = 32; + /// + /// Maximum length of the segments used in the queue. This is a somewhat arbitrary limit: + /// larger means that as long as we don't exceed the size, we avoid allocating more segments, + /// but if we do exceed it, then the segment becomes garbage. + /// + private const int MaxSegmentLength = 1024 * 1024; /// - /// Initializes a new instance of the class. + /// Lock used to protect cross-segment operations, including any updates to or + /// and any operations that need to get a consistent view of them. + /// + [NonSerialized] + private Lock _crossSegmentLock; + /// The current tail segment. + [NonSerialized] + private volatile Segment _tail; + /// The current head segment. + [NonSerialized] + private volatile Segment _head; + /// Field used to temporarily store the contents of the queue for serialization. + private T[] _serializationArray; + + /// + /// Initializes a new instance of the class. /// public LowLevelConcurrentQueue() { - _head = _tail = new Segment(0, this); + _crossSegmentLock = new Lock(); + _tail = _head = new Segment(InitialSegmentLength); } - /// - /// Returns an enumerator that iterates through a collection. - /// - /// An that can be used to iterate through the collection. - IEnumerator IEnumerable.GetEnumerator() + /// Set the data array to be serialized. + [OnSerializing] + private void OnSerializing(StreamingContext context) { - return ((IEnumerable)this).GetEnumerator(); + _serializationArray = ToArray(); + } + + /// Clear the data array that was serialized. + [OnSerialized] + private void OnSerialized(StreamingContext context) + { + _serializationArray = null; + } + + /// Construct the queue from the deserialized . + [OnDeserialized] + private void OnDeserialized(StreamingContext context) + { + Debug.Assert(_serializationArray != null); + InitializeFromCollection(_serializationArray); + _serializationArray = null; } /// - /// Gets a value that indicates whether the is empty. + /// Initializes the contents of the queue from an existing collection. /// - /// true if the is empty; otherwise, false. + /// A collection from which to copy elements. + private void InitializeFromCollection(IEnumerable collection) + { + _crossSegmentLock = new Lock(); + + // Determine the initial segment size. We'll use the default, + // unless the collection is known to be larger than than, in which + // case we round its length up to a power of 2, as all segments must + // be a power of 2 in length. + int length = InitialSegmentLength; + var c = collection as ICollection; + if (c != null) + { + int count = c.Count; + if (count > length) + { + length = Math.Min(RoundUpToPowerOf2(count), MaxSegmentLength); + } + } + + // Initialize the segment and add all of the data to it. + _tail = _head = new Segment(length); + foreach (T item in collection) + { + Enqueue(item); + } + } + + /// + /// Initializes a new instance of the class that contains elements copied + /// from the specified collection. + /// + /// + /// The collection whose elements are copied to the new . + /// + /// The argument is null. + public LowLevelConcurrentQueue(IEnumerable collection) + { + if (collection == null) + { + throw new ArgumentNullException(nameof(collection)); + } + + InitializeFromCollection(collection); + } + + /// Returns an enumerator that iterates through a collection. + /// An that can be used to iterate through the collection. + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this).GetEnumerator(); + + /// + /// Gets a value that indicates whether the is empty. + /// + /// true if the is empty; otherwise, false. /// /// For determining whether the collection contains any items, use of this property is recommended /// rather than retrieving the number of items from the property and comparing it @@ -75,74 +173,47 @@ namespace System.Collections.Concurrent { get { - Segment head = _head; - if (!head.IsEmpty) - //fast route 1: - //if current head is not empty, then queue is not empty - return false; - else if (head.Next == null) - //fast route 2: - //if current head is empty and it's the last segment - //then queue is empty - return true; - else - //slow route: - //current head is empty and it is NOT the last segment, - //it means another thread is growing new segment - { - SpinWait spin = new SpinWait(); - while (head.IsEmpty) - { - if (head.Next == null) - return true; - - spin.SpinOnce(); - head = _head; - } - return false; - } + // IsEmpty == !TryPeek. We use a "resultUsed:false" peek in order to avoid marking + // segments as preserved for observation, making IsEmpty a cheaper way than either + // TryPeek(out T) or Count == 0 to check whether any elements are in the queue. + T ignoredResult; + return !TryPeek(out ignoredResult, resultUsed: false); } } - /// - /// Store the position of the current head and tail positions. - /// - /// return the head segment - /// return the tail segment - /// return the head offset, value range [0, SEGMENT_SIZE] - /// return the tail offset, value range [-1, SEGMENT_SIZE-1] - private void GetHeadTailPositions(out Segment head, out Segment tail, - out int headLow, out int tailHigh) + /// Copies the elements stored in the to a new array. + /// A new array containing a snapshot of elements copied from the . + public T[] ToArray() { - head = _head; - tail = _tail; - headLow = head.Low; - tailHigh = tail.High; - SpinWait spin = new SpinWait(); + // Snap the current contents for enumeration. + Segment head, tail; + int headHead, tailTail; + SnapForObservation(out head, out headHead, out tail, out tailTail); - //we loop until the observed values are stable and sensible. - //This ensures that any update order by other methods can be tolerated. - while ( - //if head and tail changed, retry - head != _head || tail != _tail - //if low and high pointers, retry - || headLow != head.Low || tailHigh != tail.High - //if head jumps ahead of tail because of concurrent grow and dequeue, retry - || head.m_index > tail.m_index) + // Count the number of items in that snapped set, and use it to allocate an + // array of the right size. + long count = GetCount(head, headHead, tail, tailTail); + T[] arr = new T[count]; + + // Now enumerate the contents, copying each element into the array. + using (IEnumerator e = Enumerate(head, headHead, tail, tailTail)) { - spin.SpinOnce(); - head = _head; - tail = _tail; - headLow = head.Low; - tailHigh = tail.High; + int i = 0; + while (e.MoveNext()) + { + arr[i++] = e.Current; + } + Debug.Assert(count == i); } + + // And return it. + return arr; } - /// - /// Gets the number of elements contained in the . + /// Gets the number of elements contained in the . /// - /// The number of elements contained in the . + /// The number of elements contained in the . /// /// For determining whether the collection contains any items, use of the /// property is recommended rather than retrieving the number of items from the @@ -152,36 +223,191 @@ namespace System.Collections.Concurrent { get { - //store head and tail positions in buffer, Segment head, tail; - int headLow, tailHigh; - GetHeadTailPositions(out head, out tail, out headLow, out tailHigh); - - if (head == tail) + int headHead, headTail, tailHead, tailTail; + var spinner = new SpinWait(); + while (true) { - return tailHigh - headLow + 1; + // Capture the head and tail, as well as the head's head and tail. + head = _head; + tail = _tail; + headHead = Volatile.Read(ref head._headAndTail.Head); + headTail = Volatile.Read(ref head._headAndTail.Tail); + + if (head == tail) + { + // There was a single segment in the queue. If the captured + // values still (or again) represent reality, return the segment's + // count. A single segment should be the most common case once the + // queue's size has stabilized after segments have grown to + // the point where growing is no longer needed. + if (head == _head && + head == _tail && + headHead == Volatile.Read(ref head._headAndTail.Head) && + headTail == Volatile.Read(ref head._headAndTail.Tail)) + { + return GetCount(head, headHead, headTail); + } + } + else if (head._nextSegment == tail) + { + // There were two segments in the queue. Get the positions + // from the tail, and if the captured values still (or again) match + // reality, return the sum of the counts from both segments. + tailHead = Volatile.Read(ref tail._headAndTail.Head); + tailTail = Volatile.Read(ref tail._headAndTail.Tail); + if (head == _head && + tail == _tail && + headHead == Volatile.Read(ref head._headAndTail.Head) && + headTail == Volatile.Read(ref head._headAndTail.Tail) && + tailHead == Volatile.Read(ref tail._headAndTail.Head) && + tailTail == Volatile.Read(ref tail._headAndTail.Tail)) + { + // We got stable values, so we can just compute the sizes based on those + // values and return the sum of the counts of the segments. + return GetCount(head, headHead, headTail) + GetCount(tail, tailHead, tailTail); + } + } + else + { + // There were more than two segments. Take the slower path, where we freeze the + // queue and then count the now stable segments. + SnapForObservation(out head, out headHead, out tail, out tailTail); + return unchecked((int)GetCount(head, headHead, tail, tailTail)); + } + + // We raced with enqueues/dequeues and captured an inconsistent picture of the queue. + // Spin and try again. + spinner.SpinOnce(); } - - //head segment - int count = SEGMENT_SIZE - headLow; - - //middle segment(s), if any, are full. - //We don't deal with overflow to be consistent with the behavior of generic types in CLR. - count += SEGMENT_SIZE * ((int)(tail.m_index - head.m_index - 1)); - - //tail segment - count += tailHigh + 1; - - return count; } } + /// Computes the number of items in a segment based on a fixed head and tail in that segment. + private static int GetCount(Segment s, int head, int tail) + { + if (head != tail && head != tail - s.FreezeOffset) + { + head &= s._slotsMask; + tail &= s._slotsMask; + return head < tail ? tail - head : s._slots.Length - head + tail; + } + return 0; + } + + /// Gets the number of items in snapped region. + private static long GetCount(Segment head, int headHead, Segment tail, int tailTail) + { + // All of the segments should have been both frozen for enqueues and preserved for observation. + // Validate that here for head and tail; we'll validate it for intermediate segments later. + Debug.Assert(head._preservedForObservation); + Debug.Assert(head._frozenForEnqueues); + Debug.Assert(tail._preservedForObservation); + Debug.Assert(tail._frozenForEnqueues); + + long count = 0; + + // Head segment. We've already marked it as frozen for enqueues, so its tail position is fixed, + // and we've already marked it as preserved for observation (before we grabbed the head), so we + // can safely enumerate from its head to its tail and access its elements. + int headTail = (head == tail ? tailTail : Volatile.Read(ref head._headAndTail.Tail)) - head.FreezeOffset; + if (headHead < headTail) + { + // Mask the head and tail for the head segment + headHead &= head._slotsMask; + headTail &= head._slotsMask; + + // Increase the count by either the one or two regions, based on whether tail + // has wrapped to be less than head. + count += headHead < headTail ? + headTail - headHead : + head._slots.Length - headHead + headTail; + } + + // We've enumerated the head. If the tail is different from the head, we need to + // enumerate the remaining segments. + if (head != tail) + { + // Count the contents of each segment between head and tail, not including head and tail. + // Since there were segments before these, for our purposes we consider them to start at + // the 0th element, and since there is at least one segment after each, each was frozen + // by the time we snapped it, so we can iterate until each's frozen tail. + for (Segment s = head._nextSegment; s != tail; s = s._nextSegment) + { + Debug.Assert(s._preservedForObservation); + Debug.Assert(s._frozenForEnqueues); + count += s._headAndTail.Tail - s.FreezeOffset; + } + + // Finally, enumerate the tail. As with the intermediate segments, there were segments + // before this in the snapped region, so we can start counting from the beginning. Unlike + // the intermediate segments, we can't just go until the Tail, as that could still be changing; + // instead we need to go until the tail we snapped for observation. + count += tailTail - tail.FreezeOffset; + } + + // Return the computed count. + return count; + } + /// - /// Returns an enumerator that iterates through the . + /// Copies the elements to an existing one-dimensional Array, starting at the specified array index. /// + /// The one-dimensional Array that is the + /// destination of the elements copied from the + /// . The Array must have zero-based + /// indexing. + /// The zero-based index in at which copying + /// begins. + /// is a null reference (Nothing in + /// Visual Basic). + /// is less than + /// zero. + /// is equal to or greater than the + /// length of the + /// -or- The number of elements in the source is greater than the + /// available space from to the end of the destination . + /// + public void CopyTo(T[] array, int index) + { + if (array == null) + { + throw new ArgumentNullException(nameof(array)); + } + if (index < 0) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + // Snap for enumeration + Segment head, tail; + int headHead, tailTail; + SnapForObservation(out head, out headHead, out tail, out tailTail); + + // Get the number of items to be enumerated + long count = GetCount(head, headHead, tail, tailTail); + if (index > array.Length - count) + { + throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); + } + + // Copy the items to the target array + int i = index; + using (IEnumerator e = Enumerate(head, headHead, tail, tailTail)) + { + while (e.MoveNext()) + { + array[i++] = e.Current; + } + } + Debug.Assert(count == i - index); + } + + /// Returns an enumerator that iterates through the . /// An enumerator for the contents of the . + /// cref="LowLevelConcurrentQueue{T}"/>. /// /// The enumeration represents a moment-in-time snapshot of the contents /// of the queue. It does not reflect any updates to the collection after @@ -190,426 +416,653 @@ namespace System.Collections.Concurrent /// public IEnumerator GetEnumerator() { - // Increments the number of active snapshot takers. This increment must happen before the snapshot is - // taken. At the same time, Decrement must happen after the enumeration is over. Only in this way, can it - // eliminate race condition when Segment.TryRemove() checks whether m_numSnapshotTakers == 0. - Interlocked.Increment(ref m_numSnapshotTakers); - - // Takes a snapshot of the queue. - // A design flaw here: if a Thread.Abort() happens, we cannot decrement m_numSnapshotTakers. But we cannot - // wrap the following with a try/finally block, otherwise the decrement will happen before the yield return - // statements in the GetEnumerator (head, tail, headLow, tailHigh) method. Segment head, tail; - int headLow, tailHigh; - GetHeadTailPositions(out head, out tail, out headLow, out tailHigh); - - //If we put yield-return here, the iterator will be lazily evaluated. As a result a snapshot of - // the queue is not taken when GetEnumerator is initialized but when MoveNext() is first called. - // This is inconsistent with existing generic collections. In order to prevent it, we capture the - // value of m_head in a buffer and call out to a helper method. - //The old way of doing this was to return the ToList().GetEnumerator(), but ToList() was an - // unnecessary perfomance hit. - return GetEnumerator(head, tail, headLow, tailHigh); + int headHead, tailTail; + SnapForObservation(out head, out headHead, out tail, out tailTail); + return Enumerate(head, headHead, tail, tailTail); } /// - /// Helper method of GetEnumerator to seperate out yield return statement, and prevent lazy evaluation. + /// Gets the head and tail information of the current contents of the queue. + /// After this call returns, the specified region can be enumerated any number + /// of times and will not change. /// - private IEnumerator GetEnumerator(Segment head, Segment tail, int headLow, int tailHigh) + private void SnapForObservation(out Segment head, out int headHead, out Segment tail, out int tailTail) { - try + using (LockHolder.Hold(_crossSegmentLock)) // _head and _tail may only change while the lock is held. { - SpinWait spin = new SpinWait(); + // Snap the head and tail + head = _head; + tail = _tail; + Debug.Assert(head != null); + Debug.Assert(tail != null); + Debug.Assert(tail._nextSegment == null); - if (head == tail) + // Mark them and all segments in between as preserving, and ensure no additional items + // can be added to the tail. + for (Segment s = head; ; s = s._nextSegment) { - for (int i = headLow; i <= tailHigh; i++) - { - // If the position is reserved by an Enqueue operation, but the value is not written into, - // spin until the value is available. - spin.Reset(); - while (!head.m_state[i].m_value) - { - spin.SpinOnce(); - } - yield return head.m_array[i]; - } + s._preservedForObservation = true; + if (s == tail) break; + Debug.Assert(s._frozenForEnqueues); // any non-tail should already be marked + } + tail.EnsureFrozenForEnqueues(); // we want to prevent the tailTail from moving + + // At this point, any dequeues from any segment won't overwrite the value, and + // none of the existing segments can have new items enqueued. + + headHead = Volatile.Read(ref head._headAndTail.Head); + tailTail = Volatile.Read(ref tail._headAndTail.Tail); + } + } + + /// Gets the item stored in the th entry in . + private T GetItemWhenAvailable(Segment segment, int i) + { + Debug.Assert(segment._preservedForObservation); + + // Get the expected value for the sequence number + int expectedSequenceNumberAndMask = (i + 1) & segment._slotsMask; + + // If the expected sequence number is not yet written, we're still waiting for + // an enqueuer to finish storing it. Spin until it's there. + if ((segment._slots[i].SequenceNumber & segment._slotsMask) != expectedSequenceNumberAndMask) + { + var spinner = new SpinWait(); + while ((Volatile.Read(ref segment._slots[i].SequenceNumber) & segment._slotsMask) != expectedSequenceNumberAndMask) + { + spinner.SpinOnce(); + } + } + + // Return the value from the slot. + return segment._slots[i].Item; + } + + private IEnumerator Enumerate(Segment head, int headHead, Segment tail, int tailTail) + { + Debug.Assert(head._preservedForObservation); + Debug.Assert(head._frozenForEnqueues); + Debug.Assert(tail._preservedForObservation); + Debug.Assert(tail._frozenForEnqueues); + + // Head segment. We've already marked it as not accepting any more enqueues, + // so its tail position is fixed, and we've already marked it as preserved for + // enumeration (before we grabbed its head), so we can safely enumerate from + // its head to its tail. + int headTail = (head == tail ? tailTail : Volatile.Read(ref head._headAndTail.Tail)) - head.FreezeOffset; + if (headHead < headTail) + { + headHead &= head._slotsMask; + headTail &= head._slotsMask; + + if (headHead < headTail) + { + for (int i = headHead; i < headTail; i++) yield return GetItemWhenAvailable(head, i); } else { - //iterate on head segment - for (int i = headLow; i < SEGMENT_SIZE; i++) - { - // If the position is reserved by an Enqueue operation, but the value is not written into, - // spin until the value is available. - spin.Reset(); - while (!head.m_state[i].m_value) - { - spin.SpinOnce(); - } - yield return head.m_array[i]; - } - //iterate on middle segments - Segment curr = head.Next; - while (curr != tail) - { - for (int i = 0; i < SEGMENT_SIZE; i++) - { - // If the position is reserved by an Enqueue operation, but the value is not written into, - // spin until the value is available. - spin.Reset(); - while (!curr.m_state[i].m_value) - { - spin.SpinOnce(); - } - yield return curr.m_array[i]; - } - curr = curr.Next; - } - - //iterate on tail segment - for (int i = 0; i <= tailHigh; i++) - { - // If the position is reserved by an Enqueue operation, but the value is not written into, - // spin until the value is available. - spin.Reset(); - while (!tail.m_state[i].m_value) - { - spin.SpinOnce(); - } - yield return tail.m_array[i]; - } + for (int i = headHead; i < head._slots.Length; i++) yield return GetItemWhenAvailable(head, i); + for (int i = 0; i < headTail; i++) yield return GetItemWhenAvailable(head, i); } } - finally + + // We've enumerated the head. If the tail is the same, we're done. + if (head != tail) { - // This Decrement must happen after the enumeration is over. - Interlocked.Decrement(ref m_numSnapshotTakers); + // Each segment between head and tail, not including head and tail. Since there were + // segments before these, for our purposes we consider it to start at the 0th element. + for (Segment s = head._nextSegment; s != tail; s = s._nextSegment) + { + Debug.Assert(s._preservedForObservation, "Would have had to been preserved as a segment part of enumeration"); + Debug.Assert(s._frozenForEnqueues, "Would have had to be frozen for enqueues as it's intermediate"); + + int sTail = s._headAndTail.Tail - s.FreezeOffset; + for (int i = 0; i < sTail; i++) + { + yield return GetItemWhenAvailable(s, i); + } + } + + // Enumerate the tail. Since there were segments before this, we can just start at + // its beginning, and iterate until the tail we already grabbed. + tailTail -= tail.FreezeOffset; + for (int i = 0; i < tailTail; i++) + { + yield return GetItemWhenAvailable(tail, i); + } } } - /// - /// Adds an object to the end of the . - /// - /// The object to add to the end of the . The value can be a null reference - /// (Nothing in Visual Basic) for reference types. + /// Round the specified value up to the next power of 2, if it isn't one already. + private static int RoundUpToPowerOf2(int i) + { + --i; + i |= i >> 1; + i |= i >> 2; + i |= i >> 4; + i |= i >> 8; + i |= i >> 16; + return i + 1; + } + + /// Adds an object to the end of the . + /// + /// The object to add to the end of the . + /// The value can be a null reference (Nothing in Visual Basic) for reference types. /// public void Enqueue(T item) { - SpinWait spin = new SpinWait(); - while (true) + // Try to enqueue to the current tail. + if (!_tail.TryEnqueue(item)) { - Segment tail = _tail; - if (tail.TryAppend(item)) - return; - spin.SpinOnce(); + // If we're unable to, we need to take a slow path that will + // try to add a new tail segment. + EnqueueSlow(item); } } + /// Adds to the end of the queue, adding a new segment if necessary. + private void EnqueueSlow(T item) + { + while (true) + { + Segment tail = _tail; + + // Try to append to the existing tail. + if (tail.TryEnqueue(item)) + { + return; + } + + // If we were unsuccessful, take the lock so that we can compare and manipulate + // the tail. Assuming another enqueuer hasn't already added a new segment, + // do so, then loop around to try enqueueing again. + using (LockHolder.Hold(_crossSegmentLock)) // _head and _tail may only change while the lock is held. + { + if (tail == _tail) + { + // Make sure no one else can enqueue to this segment. + tail.EnsureFrozenForEnqueues(); + + // We determine the new segment's length based on the old length. + // In general, we double the size of the segment, to make it less likely + // that we'll need to grow again. However, if the tail segment is marked + // as preserved for observation, something caused us to avoid reusing this + // segment, and if that happens a lot and we grow, we'll end up allocating + // lots of wasted space. As such, in such situations we reset back to the + // initial segment length; if these observations are happening frequently, + // this will help to avoid wasted memory, and if they're not, we'll + // relatively quickly grow again to a larger size. + int nextSize = tail._preservedForObservation ? InitialSegmentLength : Math.Min(tail.Capacity * 2, MaxSegmentLength); + var newTail = new Segment(nextSize); + + // Hook up the new tail. + tail._nextSegment = newTail; + _tail = newTail; + } + } + } + } /// /// Attempts to remove and return the object at the beginning of the . + /// cref="LowLevelConcurrentQueue{T}"/>. /// /// /// When this method returns, if the operation was successful, contains the /// object removed. If no object was available to be removed, the value is unspecified. /// - /// true if an element was removed and returned from the beggining of the - /// succesfully; otherwise, false. - public bool TryDequeue(out T result) + /// + /// true if an element was removed and returned from the beginning of the + /// successfully; otherwise, false. + /// + public bool TryDequeue(out T result) => + _head.TryDequeue(out result) || // fast-path that operates just on the head segment + TryDequeueSlow(out result); // slow path that needs to fix up segments + + /// Tries to dequeue an item, removing empty segments as needed. + private bool TryDequeueSlow(out T item) { - while (!IsEmpty) + while (true) { + // Get the current head Segment head = _head; - if (head.TryRemove(out result)) + + // Try to take. If we're successful, we're done. + if (head.TryDequeue(out item)) + { return true; - //since method IsEmpty spins, we don't need to spin in the while loop + } + + // Check to see whether this segment is the last. If it is, we can consider + // this to be a moment-in-time empty condition (even though between the TryDequeue + // check and this check, another item could have arrived). + if (head._nextSegment == null) + { + item = default(T); + return false; + } + + // At this point we know that head.Next != null, which means + // this segment has been frozen for additional enqueues. But between + // the time that we ran TryDequeue and checked for a next segment, + // another item could have been added. Try to dequeue one more time + // to confirm that the segment is indeed empty. + Debug.Assert(head._frozenForEnqueues); + if (head.TryDequeue(out item)) + { + return true; + } + + // This segment is frozen (nothing more can be added) and empty (nothing is in it). + // Update head to point to the next segment in the list, assuming no one's beat us to it. + using (LockHolder.Hold(_crossSegmentLock)) // _head and _tail may only change while the lock is held. + { + if (head == _head) + { + _head = head._nextSegment; + } + } } + } + + /// + /// Attempts to return an object from the beginning of the + /// without removing it. + /// + /// + /// When this method returns, contains an object from + /// the beginning of the or default(T) + /// if the operation failed. + /// + /// true if and object was returned successfully; otherwise, false. + /// + /// For determining whether the collection contains any items, use of the + /// property is recommended rather than peeking. + /// + public bool TryPeek(out T result) => TryPeek(out result, resultUsed: true); + + /// Attempts to retrieve the value for the first element in the queue. + /// The value of the first element, if found. + /// true if the result is neede; otherwise false if only the true/false outcome is needed. + /// true if an element was found; otherwise, false. + private bool TryPeek(out T result, bool resultUsed) + { + // Starting with the head segment, look through all of the segments + // for the first one we can find that's not empty. + Segment s = _head; + while (true) + { + // Grab the next segment from this one, before we peek. + // This is to be able to see whether the value has changed + // during the peek operation. + Segment next = Volatile.Read(ref s._nextSegment); + + // Peek at the segment. If we find an element, we're done. + if (s.TryPeek(out result, resultUsed)) + { + return true; + } + + // The current segment was empty at the moment we checked. + + if (next != null) + { + // If prior to the peek there was already a next segment, then + // during the peek no additional items could have been enqueued + // to it and we can just move on to check the next segment. + Debug.Assert(next == s._nextSegment); + s = next; + } + else if (Volatile.Read(ref s._nextSegment) == null) + { + // The next segment is null. Nothing more to peek at. + break; + } + + // The next segment was null before we peeked but non-null after. + // That means either when we peeked the first segment had + // already been frozen but the new segment not yet added, + // or that the first segment was empty and between the time + // that we peeked and then checked _nextSegment, so many items + // were enqueued that we filled the first segment and went + // into the next. Since we need to peek in order, we simply + // loop around again to peek on the same segment. The next + // time around on this segment we'll then either successfully + // peek or we'll find that next was non-null before peeking, + // and we'll traverse to that segment. + } + result = default(T); return false; } /// - /// private class for ConcurrentQueue. - /// a queue is a linked list of small arrays, each node is called a segment. - /// A segment contains an array, a pointer to the next segment, and m_low, m_high indices recording - /// the first and last valid elements of the array. + /// Removes all objects from the . /// - private class Segment + public void Clear() { - //we define two volatile arrays: m_array and m_state. Note that the accesses to the array items - //do not get volatile treatment. But we don't need to worry about loading adjacent elements or - //store/load on adjacent elements would suffer reordering. - // - Two stores: these are at risk, but CLRv2 memory model guarantees store-release hence we are safe. - // - Two loads: because one item from two volatile arrays are accessed, the loads of the array references - // are sufficient to prevent reordering of the loads of the elements. - internal volatile T[] m_array; - - // For each entry in m_array, the corresponding entry in m_state indicates whether this position contains - // a valid value. m_state is initially all false. - internal volatile VolatileBool[] m_state; - - //pointer to the next segment. null if the current segment is the last segment - private volatile Segment _next; - - //We use this zero based index to track how many segments have been created for the queue, and - //to compute how many active segments are there currently. - // * The number of currently active segments is : m_tail.m_index - m_head.m_index + 1; - // * m_index is incremented with every Segment.Grow operation. We use Int64 type, and we can safely - // assume that it never overflows. To overflow, we need to do 2^63 increments, even at a rate of 4 - // billion (2^32) increments per second, it takes 2^31 seconds, which is about 64 years. - internal readonly long m_index; - - //indices of where the first and last valid values - // - m_low points to the position of the next element to pop from this segment, range [0, infinity) - // m_low >= SEGMENT_SIZE implies the segment is disposable - // - m_high points to the position of the latest pushed element, range [-1, infinity) - // m_high == -1 implies the segment is new and empty - // m_high >= SEGMENT_SIZE-1 means this segment is ready to grow. - // and the thread who sets m_high to SEGMENT_SIZE-1 is responsible to grow the segment - // - Math.Min(m_low, SEGMENT_SIZE) > Math.Min(m_high, SEGMENT_SIZE-1) implies segment is empty - // - initially m_low =0 and m_high=-1; - private volatile int _low; - private volatile int _high; - - private volatile LowLevelConcurrentQueue _source; - - /// - /// Create and initialize a segment with the specified index. - /// - internal Segment(long index, LowLevelConcurrentQueue source) + using (LockHolder.Hold(_crossSegmentLock)) // _head and _tail may only change while the lock is held. { - m_array = new T[SEGMENT_SIZE]; - m_state = new VolatileBool[SEGMENT_SIZE]; //all initialized to false - _high = -1; - Debug.Assert(index >= 0); - m_index = index; - _source = source; + // Simply substitute a new segment for the existing head/tail, + // as is done in the constructor. Operations currently in flight + // may still read from or write to an existing segment that's + // getting dropped, meaning that in flight operations may not be + // linear with regards to this clear operation. To help mitigate + // in-flight operations enqueuing onto the tail that's about to + // be dropped, we first freeze it; that'll force enqueuers to take + // this lock to synchronize and see the new tail. + _tail.EnsureFrozenForEnqueues(); + _tail = _head = new Segment(InitialSegmentLength); } + } - /// - /// return the next segment - /// - internal Segment Next + /// + /// Provides a multi-producer, multi-consumer thread-safe bounded segment. When the queue is full, + /// enqueues fail and return false. When the queue is empty, dequeues fail and return null. + /// These segments are linked together to form the unbounded . + /// + [DebuggerDisplay("Capacity = {Capacity}")] + private sealed class Segment + { + // Segment design is inspired by the algorithm outlined at: + // http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue + + /// The array of items in this queue. Each slot contains the item in that slot and its "sequence number". + internal readonly Slot[] _slots; + /// Mask for quickly accessing a position within the queue's array. + internal readonly int _slotsMask; + /// The head and tail positions, with padding to help avoid false sharing contention. + /// Dequeueing happens from the head, enqueueing happens at the tail. + internal PaddedHeadAndTail _headAndTail; // mutable struct: do not make this readonly + + /// Indicates whether the segment has been marked such that dequeues don't overwrite the removed data. + internal bool _preservedForObservation; + /// Indicates whether the segment has been marked such that no additional items may be enqueued. + internal bool _frozenForEnqueues; + /// The segment following this one in the queue, or null if this segment is the last in the queue. + internal Segment _nextSegment; + + /// Creates the segment. + /// + /// The maximum number of elements the segment can contain. Must be a power of 2. + /// + public Segment(int boundedLength) { - get { return _next; } - } + // Validate the length + Debug.Assert(boundedLength >= 2, $"Must be >= 2, got {boundedLength}"); + Debug.Assert((boundedLength & (boundedLength - 1)) == 0, $"Must be a power of 2, got {boundedLength}"); + // Initialize the slots and the mask. The mask is used as a way of quickly doing "% _slots.Length", + // instead letting us do "& _slotsMask". + _slots = new Slot[boundedLength]; + _slotsMask = boundedLength - 1; - /// - /// return true if the current segment is empty (doesn't have any element available to dequeue, - /// false otherwise - /// - internal bool IsEmpty - { - get { return (Low > High); } - } - - /// - /// Add an element to the tail of the current segment - /// exclusively called by ConcurrentQueue.InitializedFromCollection - /// InitializeFromCollection is responsible to guaratee that there is no index overflow, - /// and there is no contention - /// - /// - internal void UnsafeAdd(T value) - { - Debug.Assert(_high < SEGMENT_SIZE - 1); - _high++; - m_array[_high] = value; - m_state[_high].m_value = true; - } - - /// - /// Create a new segment and append to the current one - /// Does not update the m_tail pointer - /// exclusively called by ConcurrentQueue.InitializedFromCollection - /// InitializeFromCollection is responsible to guaratee that there is no index overflow, - /// and there is no contention - /// - /// the reference to the new Segment - internal Segment UnsafeGrow() - { - Debug.Assert(_high >= SEGMENT_SIZE - 1); - Segment newSegment = new Segment(m_index + 1, _source); //m_index is Int64, we don't need to worry about overflow - _next = newSegment; - return newSegment; - } - - /// - /// Create a new segment and append to the current one - /// Update the m_tail pointer - /// This method is called when there is no contention - /// - internal void Grow() - { - //no CAS is needed, since there is no contention (other threads are blocked, busy waiting) - Segment newSegment = new Segment(m_index + 1, _source); //m_index is Int64, we don't need to worry about overflow - _next = newSegment; - Debug.Assert(_source._tail == this); - _source._tail = _next; - } - - - /// - /// Try to append an element at the end of this segment. - /// - /// the element to append - /// The tail. - /// true if the element is appended, false if the current segment is full - /// if appending the specified element succeeds, and after which the segment is full, - /// then grow the segment - internal bool TryAppend(T value) - { - //quickly check if m_high is already over the boundary, if so, bail out - if (_high >= SEGMENT_SIZE - 1) + // Initialize the sequence number for each slot. The sequence number provides a ticket that + // allows dequeuers to know whether they can dequeue and enqueuers to know whether they can + // enqueue. An enqueuer at position N can enqueue when the sequence number is N, and a dequeuer + // for position N can dequeue when the sequence number is N + 1. When an enqueuer is done writing + // at position N, it sets the sequence number to N so that a dequeuer will be able to dequeue, + // and when a dequeuer is done dequeueing at position N, it sets the sequence number to N + _slots.Length, + // so that when an enqueuer loops around the slots, it'll find that the sequence number at + // position N is N. This also means that when an enqueuer finds that at position N the sequence + // number is < N, there is still a value in that slot, i.e. the segment is full, and when a + // dequeuer finds that the value in a slot is < N + 1, there is nothing currently available to + // dequeue. (It is possible for multiple enqueuers to enqueue concurrently, writing into + // subsequent slots, and to have the first enqueuer take longer, so that the slots for 1, 2, 3, etc. + // may have values, but the 0th slot may still be being filled... in that case, TryDequeue will + // return false.) + for (int i = 0; i < _slots.Length; i++) { - return false; + _slots[i].SequenceNumber = i; } + } - //Now we will use a CAS to increment m_high, and store the result in newhigh. - //Depending on how many free spots left in this segment and how many threads are doing this Increment - //at this time, the returning "newhigh" can be - // 1) < SEGMENT_SIZE - 1 : we took a spot in this segment, and not the last one, just insert the value - // 2) == SEGMENT_SIZE - 1 : we took the last spot, insert the value AND grow the segment - // 3) > SEGMENT_SIZE - 1 : we failed to reserve a spot in this segment, we return false to - // Queue.Enqueue method, telling it to try again in the next segment. + /// Gets the number of elements this segment can store. + internal int Capacity => _slots.Length; - int newhigh = SEGMENT_SIZE; //initial value set to be over the boundary + /// Gets the "freeze offset" for this segment. + internal int FreezeOffset => _slots.Length * 2; - //We need do Interlocked.Increment and value/state update in a finally block to ensure that they run - //without interuption. This is to prevent anything from happening between them, and another dequeue - //thread maybe spinning forever to wait for m_state[] to be true; - try - { } - finally + /// + /// Ensures that the segment will not accept any subsequent enqueues that aren't already underway. + /// + /// + /// When we mark a segment as being frozen for additional enqueues, + /// we set the bool, but that's mostly + /// as a small helper to avoid marking it twice. The real marking comes + /// by modifying the Tail for the segment, increasing it by this + /// . This effectively knocks it off the + /// sequence expected by future enqueuers, such that any additional enqueuer + /// will be unable to enqueue due to it not lining up with the expected + /// sequence numbers. This value is chosen specially so that Tail will grow + /// to a value that maps to the same slot but that won't be confused with + /// any other enqueue/dequeue sequence number. + /// + internal void EnsureFrozenForEnqueues() // must only be called while queue's segment lock is held + { + if (!_frozenForEnqueues) // flag used to ensure we don't increase the Tail more than once if frozen more than once { - newhigh = Interlocked.Increment(ref _high); - if (newhigh <= SEGMENT_SIZE - 1) - { - m_array[newhigh] = value; - m_state[newhigh].m_value = true; - } + _frozenForEnqueues = true; - //if this thread takes up the last slot in the segment, then this thread is responsible - //to grow a new segment. Calling Grow must be in the finally block too for reliability reason: - //if thread abort during Grow, other threads will be left busy spinning forever. - if (newhigh == SEGMENT_SIZE - 1) + // Increase the tail by FreezeOffset, spinning until we're successful in doing so. + var spinner = new SpinWait(); + while (true) { - Grow(); + int tail = Volatile.Read(ref _headAndTail.Tail); + if (Interlocked.CompareExchange(ref _headAndTail.Tail, tail + FreezeOffset, tail) == tail) + { + break; + } + spinner.SpinOnce(); } } - - //if newhigh <= SEGMENT_SIZE-1, it means the current thread successfully takes up a spot - return newhigh <= SEGMENT_SIZE - 1; } - - /// - /// try to remove an element from the head of current segment - /// - /// The result. - /// The head. - /// return false only if the current segment is empty - internal bool TryRemove(out T result) + /// Tries to dequeue an element from the queue. + public bool TryDequeue(out T item) { - SpinWait spin = new SpinWait(); - int lowLocal = Low, highLocal = High; - while (lowLocal <= highLocal) + // Loop in case of contention... + var spinner = new SpinWait(); + while (true) { - //try to update m_low - if (Interlocked.CompareExchange(ref _low, lowLocal + 1, lowLocal) == lowLocal) + // Get the head at which to try to dequeue. + int currentHead = Volatile.Read(ref _headAndTail.Head); + int slotsIndex = currentHead & _slotsMask; + + // Read the sequence number for the head position. + int sequenceNumber = Volatile.Read(ref _slots[slotsIndex].SequenceNumber); + + // We can dequeue from this slot if it's been filled by an enqueuer, which + // would have left the sequence number at pos+1. + int diff = sequenceNumber - (currentHead + 1); + if (diff == 0) { - //if the specified value is not available (this spot is taken by a push operation, - // but the value is not written into yet), then spin - SpinWait spinLocal = new SpinWait(); - while (!m_state[lowLocal].m_value) + // We may be racing with other dequeuers. Try to reserve the slot by incrementing + // the head. Once we've done that, no one else will be able to read from this slot, + // and no enqueuer will be able to read from this slot until we've written the new + // sequence number. WARNING: The next few lines are not reliable on a runtime that + // supports thread aborts. If a thread abort were to sneak in after the CompareExchange + // but before the Volatile.Write, enqueuers trying to enqueue into this slot would + // spin indefinitely. If this implementation is ever used on such a platform, this + // if block should be wrapped in a finally / prepared region. + if (Interlocked.CompareExchange(ref _headAndTail.Head, currentHead + 1, currentHead) == currentHead) { - spinLocal.SpinOnce(); - } - result = m_array[lowLocal]; - - // If there is no other thread taking snapshot (GetEnumerator(), ToList(), etc), reset the deleted entry to null. - // It is ok if after this conditional check m_numSnapshotTakers becomes > 0, because new snapshots won't include - // the deleted entry at m_array[lowLocal]. - if (_source.m_numSnapshotTakers <= 0) - { - m_array[lowLocal] = default(T); //release the reference to the object. - } - - //if the current thread sets m_low to SEGMENT_SIZE, which means the current segment becomes - //disposable, then this thread is responsible to dispose this segment, and reset m_head - if (lowLocal + 1 >= SEGMENT_SIZE) - { - // Invariant: we only dispose the current m_head, not any other segment - // In usual situation, disposing a segment is simply seting m_head to m_head.m_next - // But there is one special case, where m_head and m_tail points to the same and ONLY - //segment of the queue: Another thread A is doing Enqueue and finds that it needs to grow, - //while the *current* thread is doing *this* Dequeue operation, and finds that it needs to - //dispose the current (and ONLY) segment. Then we need to wait till thread A finishes its - //Grow operation, this is the reason of having the following while loop - spinLocal = new SpinWait(); - while (_next == null) + // Successfully reserved the slot. Note that after the above CompareExchange, other threads + // trying to dequeue from this slot will end up spinning until we do the subsequent Write. + item = _slots[slotsIndex].Item; + if (!Volatile.Read(ref _preservedForObservation)) { - spinLocal.SpinOnce(); + // If we're preserving, though, we don't zero out the slot, as we need it for + // enumerations, peeking, ToArray, etc. And we don't update the sequence number, + // so that an enqueuer will see it as full and be forced to move to a new segment. + _slots[slotsIndex].Item = default(T); + Volatile.Write(ref _slots[slotsIndex].SequenceNumber, currentHead + _slots.Length); } - Debug.Assert(_source._head == this); - _source._head = _next; + return true; } + } + else if (diff < 0) + { + // The sequence number was less than what we needed, which means this slot doesn't + // yet contain a value we can dequeue, i.e. the segment is empty. Technically it's + // possible that multiple enqueuers could have written concurrently, with those + // getting later slots actually finishing first, so there could be elements after + // this one that are available, but we need to dequeue in order. So before declaring + // failure and that the segment is empty, we check the tail to see if we're actually + // empty or if we're just waiting for items in flight or after this one to become available. + bool frozen = _frozenForEnqueues; + int currentTail = Volatile.Read(ref _headAndTail.Tail); + if (currentTail - currentHead <= 0 || (frozen && (currentTail - FreezeOffset - currentHead <= 0))) + { + item = default(T); + return false; + } + + // It's possible it could have become frozen after we checked _frozenForEnqueues + // and before reading the tail. That's ok: in that rare race condition, we just + // loop around again. + } + + // Lost a race. Spin a bit, then try again. + spinner.SpinOnce(); + } + } + + /// Tries to peek at an element from the queue, without removing it. + public bool TryPeek(out T result, bool resultUsed) + { + if (resultUsed) + { + // In order to ensure we don't get a torn read on the value, we mark the segment + // as preserving for observation. Additional items can still be enqueued to this + // segment, but no space will be freed during dequeues, such that the segment will + // no longer be reusable. + _preservedForObservation = true; + Interlocked.MemoryBarrier(); + } + + // Loop in case of contention... + var spinner = new SpinWait(); + while (true) + { + // Get the head at which to try to peek. + int currentHead = Volatile.Read(ref _headAndTail.Head); + int slotsIndex = currentHead & _slotsMask; + + // Read the sequence number for the head position. + int sequenceNumber = Volatile.Read(ref _slots[slotsIndex].SequenceNumber); + + // We can peek from this slot if it's been filled by an enqueuer, which + // would have left the sequence number at pos+1. + int diff = sequenceNumber - (currentHead + 1); + if (diff == 0) + { + result = resultUsed ? _slots[slotsIndex].Item : default(T); return true; } - else + else if (diff < 0) { - //CAS failed due to contention: spin briefly and retry - spin.SpinOnce(); - lowLocal = Low; highLocal = High; + // The sequence number was less than what we needed, which means this slot doesn't + // yet contain a value we can peek, i.e. the segment is empty. Technically it's + // possible that multiple enqueuers could have written concurrently, with those + // getting later slots actually finishing first, so there could be elements after + // this one that are available, but we need to peek in order. So before declaring + // failure and that the segment is empty, we check the tail to see if we're actually + // empty or if we're just waiting for items in flight or after this one to become available. + bool frozen = _frozenForEnqueues; + int currentTail = Volatile.Read(ref _headAndTail.Tail); + if (currentTail - currentHead <= 0 || (frozen && (currentTail - FreezeOffset - currentHead <= 0))) + { + result = default(T); + return false; + } + + // It's possible it could have become frozen after we checked _frozenForEnqueues + // and before reading the tail. That's ok: in that rare race condition, we just + // loop around again. } - }//end of while - result = default(T); - return false; - } - /// - /// return the position of the head of the current segment - /// Value range [0, SEGMENT_SIZE], if it's SEGMENT_SIZE, it means this segment is exhausted and thus empty - /// - internal int Low - { - get - { - return Math.Min(_low, SEGMENT_SIZE); + // Lost a race. Spin a bit, then try again. + spinner.SpinOnce(); } } /// - /// return the logical position of the tail of the current segment - /// Value range [-1, SEGMENT_SIZE-1]. When it's -1, it means this is a new segment and has no elemnet yet + /// Attempts to enqueue the item. If successful, the item will be stored + /// in the queue and true will be returned; otherwise, the item won't be stored, and false + /// will be returned. /// - internal int High + public bool TryEnqueue(T item) { - get + // Loop in case of contention... + var spinner = new SpinWait(); + while (true) { - //if m_high > SEGMENT_SIZE, it means it's out of range, we should return - //SEGMENT_SIZE-1 as the logical position - return Math.Min(_high, SEGMENT_SIZE - 1); + // Get the tail at which to try to return. + int currentTail = Volatile.Read(ref _headAndTail.Tail); + int slotsIndex = currentTail & _slotsMask; + + // Read the sequence number for the tail position. + int sequenceNumber = Volatile.Read(ref _slots[slotsIndex].SequenceNumber); + + // The slot is empty and ready for us to enqueue into it if its sequence + // number matches the slot. + int diff = sequenceNumber - currentTail; + if (diff == 0) + { + // We may be racing with other enqueuers. Try to reserve the slot by incrementing + // the tail. Once we've done that, no one else will be able to write to this slot, + // and no dequeuer will be able to read from this slot until we've written the new + // sequence number. WARNING: The next few lines are not reliable on a runtime that + // supports thread aborts. If a thread abort were to sneak in after the CompareExchange + // but before the Volatile.Write, other threads will spin trying to access this slot. + // If this implementation is ever used on such a platform, this if block should be + // wrapped in a finally / prepared region. + if (Interlocked.CompareExchange(ref _headAndTail.Tail, currentTail + 1, currentTail) == currentTail) + { + // Successfully reserved the slot. Note that after the above CompareExchange, other threads + // trying to return will end up spinning until we do the subsequent Write. + _slots[slotsIndex].Item = item; + Volatile.Write(ref _slots[slotsIndex].SequenceNumber, currentTail + 1); + return true; + } + } + else if (diff < 0) + { + // The sequence number was less than what we needed, which means this slot still + // contains a value, i.e. the segment is full. Technically it's possible that multiple + // dequeuers could have read concurrently, with those getting later slots actually + // finishing first, so there could be spaces after this one that are available, but + // we need to enqueue in order. + return false; + } + + // Lost a race. Spin a bit, then try again. + spinner.SpinOnce(); } } + + /// Represents a slot in the queue. + [StructLayout(LayoutKind.Auto)] + [DebuggerDisplay("Item = {Item}, SequenceNumber = {SequenceNumber}")] + internal struct Slot + { + /// The item. + public T Item; + /// The sequence number for this slot, used to synchronize between enqueuers and dequeuers. + public int SequenceNumber; + } } - }//end of class Segment + } - /// - /// A wrapper struct for volatile bool, please note the copy of the struct it self will not be volatile - /// for example this statement will not include in volatilness operation volatileBool1 = volatileBool2 the jit will copy the struct and will ignore the volatile - /// - internal struct VolatileBool + /// Padded head and tail indices, to avoid false sharing between producers and consumers. + [DebuggerDisplay("Head = {Head}, Tail = {Tail}")] + [StructLayout(LayoutKind.Explicit, Size = 192)] // padding before/between/after fields based on typical cache line size of 64 + internal struct PaddedHeadAndTail { - public VolatileBool(bool value) - { - m_value = value; - } - public volatile bool m_value; + [FieldOffset(64)] public int Head; + [FieldOffset(128)] public int Tail; } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.cs b/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.cs index 1b6f323833..ec4bcd6b92 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; #if MONO using System.Diagnostics.Private; #endif @@ -15,11 +14,11 @@ namespace System.Collections.Generic internal static class IntrospectiveSortUtilities { // This is the threshold where Introspective sort switches to Insertion sort. - // Imperically, 16 seems to speed up most cases without slowing down others, at least for integers. + // Empirically, 16 seems to speed up most cases without slowing down others, at least for integers. // Large value types may benefit from a smaller number. internal const int IntrosortSizeThreshold = 16; - internal static int FloorLog2(int n) + internal static int FloorLog2PlusOne(int n) { int result = 0; while (n >= 1) @@ -169,7 +168,7 @@ namespace System.Collections.Generic if (length < 2) return; - IntroSort(keys, left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2(keys.Length), comparer); + IntroSort(keys, left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(keys.Length), comparer); } private static void IntroSort(T[] keys, int lo, int hi, int depthLimit, Comparison comparer) @@ -226,7 +225,6 @@ namespace System.Collections.Generic Debug.Assert(lo >= 0); Debug.Assert(hi > lo); Debug.Assert(hi < keys.Length); - Contract.Ensures(Contract.Result() >= lo && Contract.Result() <= hi); // Compute median-of-three. But also partition them, since we've done the comparison. int middle = lo + ((hi - lo) / 2); @@ -383,10 +381,11 @@ namespace System.Collections.Generic private static void SwapIfGreaterWithItems(TKey[] keys, TValue[] values, IComparer comparer, int a, int b) { Debug.Assert(keys != null); - Debug.Assert(values == null || values.Length >= keys.Length); Debug.Assert(comparer != null); Debug.Assert(0 <= a && a < keys.Length); Debug.Assert(0 <= b && b < keys.Length); + Debug.Assert(values == null || (0 <= a && a < values.Length)); + Debug.Assert(values == null || (0 <= b && b < values.Length)); if (a != b) { @@ -435,7 +434,7 @@ namespace System.Collections.Generic if (length < 2) return; - IntroSort(keys, values, left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2(keys.Length), comparer); + IntroSort(keys, values, left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(keys.Length), comparer); } private static void IntroSort(TKey[] keys, TValue[] values, int lo, int hi, int depthLimit, IComparer comparer) @@ -494,7 +493,6 @@ namespace System.Collections.Generic Debug.Assert(lo >= 0); Debug.Assert(hi > lo); Debug.Assert(hi < keys.Length); - Contract.Ensures(Contract.Result() >= lo && Contract.Result() <= hi); // Compute median-of-three. But also partition them, since we've done the comparison. int middle = lo + ((hi - lo) / 2); diff --git a/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs b/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs index acce28b277..4bb79e4612 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs @@ -2,32 +2,48 @@ // 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.Collections; -using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; + +using Internal.IntrinsicSupport; namespace System.Collections.Generic { [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public abstract class Comparer : IComparer, IComparer { + // WARNING: We allow diagnostic tools to directly inspect this member (_default). + // See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details. + // Please do not change the type, the name, or the semantic usage of this member without understanding the implication for tools. + // Get in touch with the diagnostics team if you have questions. + private static Comparer _default; + + [Intrinsic] + private static Comparer Create() + { +#if PROJECTN + // The compiler will overwrite the Create method with optimized + // instantiation-specific implementation. + _default = null; + throw new NotSupportedException(); +#else + // The compiler will overwrite the Create method with optimized + // instantiation-specific implementation. + // This body serves as a fallback when instantiation-specific implementation is unavailable. + return (_default = ComparerHelpers.GetUnknownComparer()); +#endif + } + protected Comparer() { } - // .NET Native for UWP toolchain overwrites the Default property with optimized - // instantiation-specific implementation. - - // TODO: Initialize the _default field via implicit static constructor for better performance - // (https://github.com/dotnet/coreclr/pull/4340). - public static Comparer Default { get { - if (_default == null) - _default = new DefaultComparer(); - return _default; + // Lazy initialization produces smaller code for CoreRT than initialization in constructor + return _default ?? Create(); } } @@ -35,8 +51,6 @@ namespace System.Collections.Generic public static Comparer Create(Comparison comparison) { - Contract.Ensures(Contract.Result>() != null); - if (comparison == null) throw new ArgumentNullException(nameof(comparison)); @@ -50,15 +64,9 @@ namespace System.Collections.Generic if (x is T && y is T) return Compare((T)x, (T)y); throw new ArgumentException(SR.Argument_InvalidArgumentForComparison); } - - // WARNING: We allow diagnostic tools to directly inspect this member (_default). - // See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details. - // Please do not change the type, the name, or the semantic usage of this member without understanding the implication for tools. - // Get in touch with the diagnostics team if you have questions. - private static Comparer _default; } - [Serializable] +#if false internal class DefaultComparer : Comparer { public override int Compare(T x, T y) @@ -102,8 +110,8 @@ namespace System.Collections.Generic throw new ArgumentException(SR.Argument_ImplementIComparable); } } +#endif - [Serializable] internal class ComparisonComparer : Comparer { private readonly Comparison _comparison; diff --git a/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/CompatibilityEqualityComparers.cs b/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/CompatibilityEqualityComparers.cs new file mode 100644 index 0000000000..33aef24b9c --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/CompatibilityEqualityComparers.cs @@ -0,0 +1,52 @@ +// 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.Runtime.Serialization; + +namespace System.Collections.Generic +{ + // Comparers that exist for serialization compatibility with .NET Framework + + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class ByteEqualityComparer : EqualityComparer + { + public override bool Equals(byte x, byte y) + { + return x == y; + } + + public override int GetHashCode(byte obj) + { + return obj.GetHashCode(); + } + + // Equals method for the comparer itself. + public override bool Equals(Object obj) => obj != null && GetType() == obj.GetType(); + + public override int GetHashCode() => GetType().GetHashCode(); + } + + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class SByteEnumEqualityComparer : EnumEqualityComparer where T : struct + { + private SByteEnumEqualityComparer(SerializationInfo information, StreamingContext context) { } + } + + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class ShortEnumEqualityComparer : EnumEqualityComparer where T : struct + { + private ShortEnumEqualityComparer(SerializationInfo information, StreamingContext context) { } + } + + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public sealed class LongEnumEqualityComparer : EnumEqualityComparer where T : struct + { + private LongEnumEqualityComparer(SerializationInfo information, StreamingContext context) { } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs b/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs index 06aecf2af0..691f7d9148 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs @@ -3,39 +3,51 @@ // See the LICENSE file in the project root for more information. -using System; -using System.Collections; +using System.Runtime.CompilerServices; + +using Internal.IntrinsicSupport; namespace System.Collections.Generic { [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public abstract class EqualityComparer : IEqualityComparer, IEqualityComparer { - protected EqualityComparer() - { - } - - // .NET Native for UWP toolchain overwrites the Default property with optimized - // instantiation-specific implementation. - - // TODO: Change the _default field to non-volatile and initialize it via implicit static - // constructor for better performance (https://github.com/dotnet/coreclr/pull/4340). - - public static EqualityComparer Default - { - get - { - if (_default == null) - _default = new DefaultEqualityComparer(); - return _default; - } - } - // WARNING: We allow diagnostic tools to directly inspect this member (_default). // See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details. // Please do not change the type, the name, or the semantic usage of this member without understanding the implication for tools. // Get in touch with the diagnostics team if you have questions. - private static volatile EqualityComparer _default; + private static EqualityComparer _default; + + [Intrinsic] + private static EqualityComparer Create() + { +#if PROJECTN + // The compiler will overwrite the Create method with optimized + // instantiation-specific implementation. + _default = null; + throw new NotSupportedException(); +#else + // The compiler will overwrite the Create method with optimized + // instantiation-specific implementation. + // This body serves as a fallback when instantiation-specific implementation is unavailable. + return (_default = EqualityComparerHelpers.GetUnknownEquatableComparer()); +#endif + } + + protected EqualityComparer() + { + } + + public static EqualityComparer Default + { + [Intrinsic] + get + { + // Lazy initialization produces smaller code for CoreRT than initialization in constructor + return _default ?? Create(); + } + } public abstract bool Equals(T x, T y); @@ -62,7 +74,7 @@ namespace System.Collections.Generic } } - [Serializable] +#if false internal sealed class DefaultEqualityComparer : EqualityComparer { public DefaultEqualityComparer() @@ -85,5 +97,22 @@ namespace System.Collections.Generic return 0; return obj.GetHashCode(); } + + // Equals method for the comparer itself. + public sealed override bool Equals(Object obj) + { + if (obj == null) + { + return false; + } + + // This needs to use GetType instead of typeof to avoid infinite recursion in the type loader + return obj.GetType().Equals(GetType()); + } + + + // This needs to use GetType instead of typeof to avoid infinite recursion in the type loader + public sealed override int GetHashCode() => GetType().GetHashCode(); } +#endif } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs b/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs deleted file mode 100644 index 996f7e2a19..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// 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. - -namespace System.Collections.Generic -{ - // NonRandomizedStringEqualityComparer is the comparer used by default with the Dictionary - // As the randomized string hashing is now turned on with no opt-out, we need to keep the performance not affected - // as much as possible in the main stream scenarios like Dictionary - // We use NonRandomizedStringEqualityComparer as default comparer as it doesnt use the randomized string hashing which - // keep the performance not affected till we hit collision threshold and then we switch to the comparer which is using - // randomized string hashing. - [Serializable] - internal sealed class NonRandomizedStringEqualityComparer : EqualityComparer - { - private static volatile IEqualityComparer s_nonRandomizedComparer; - - internal static new IEqualityComparer Default => s_nonRandomizedComparer ?? (s_nonRandomizedComparer = new NonRandomizedStringEqualityComparer()); - - public sealed override bool Equals(string x, string y) => string.Equals(x, y); - - public sealed override int GetHashCode(string obj) - { - if (obj == null) - return 0; - return obj.GetLegacyNonRandomizedHashCode(); - } - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Collections/ObjectEqualityComparer.cs b/external/corert/src/System.Private.CoreLib/src/System/Collections/ObjectEqualityComparer.cs deleted file mode 100644 index 91c05bd2ff..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/Collections/ObjectEqualityComparer.cs +++ /dev/null @@ -1,36 +0,0 @@ -// 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.Collections; - -namespace System.Collections.Generic -{ - internal sealed class ObjectEqualityComparer : IEqualityComparer - { - internal static readonly ObjectEqualityComparer Default = new ObjectEqualityComparer(); - - private ObjectEqualityComparer() - { - } - - int IEqualityComparer.GetHashCode(object obj) - { - if (obj == null) - return 0; - else return obj.GetHashCode(); - } - - bool IEqualityComparer.Equals(object x, object y) - { - if (x == null) - return y == null; - - if (y == null) - return false; - - return x.Equals(y); - } - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/System/DateTime.Unix.CoreRT.cs b/external/corert/src/System.Private.CoreLib/src/System/DateTime.Unix.CoreRT.cs index 884c9f12c9..6c1c4bacac 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/DateTime.Unix.CoreRT.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/DateTime.Unix.CoreRT.cs @@ -2,19 +2,16 @@ // 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.Diagnostics.Contracts; - namespace System { - public partial struct DateTime + public readonly partial struct DateTime { public static DateTime UtcNow { get { - Contract.Ensures(Contract.Result().Kind == DateTimeKind.Utc); // For performance, use a private constructor that does not validate arguments. - return new DateTime(((ulong)(Interop.Sys.GetSystemTimeAsTicks() + TicksTo1970)) | KindUtc); + return new DateTime(((ulong)(Interop.Sys.GetSystemTimeAsTicks() + DateTime.UnixEpochTicks)) | KindUtc); } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/DateTime.Windows.CoreRT.cs b/external/corert/src/System.Private.CoreLib/src/System/DateTime.Windows.CoreRT.cs index d42c2f19d7..a8abb5fd97 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/DateTime.Windows.CoreRT.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/DateTime.Windows.CoreRT.cs @@ -2,17 +2,14 @@ // 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.Diagnostics.Contracts; - namespace System { - public partial struct DateTime + public readonly partial struct DateTime { public static unsafe DateTime UtcNow { get { - Contract.Ensures(Contract.Result().Kind == DateTimeKind.Utc); long ticks; Interop.mincore.GetSystemTimeAsFileTime(&ticks); // For performance, use a private constructor that does not validate arguments. diff --git a/external/corert/src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs.REMOVED.git-id b/external/corert/src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs.REMOVED.git-id index e1a4bd5c76..ca121adcd2 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs.REMOVED.git-id +++ b/external/corert/src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs.REMOVED.git-id @@ -1 +1 @@ -a51d4c70373578173c74e2ce3c56ab071f5e6991 \ No newline at end of file +5e67f3f61baf3054844a4218cb88abb58f3a4f20 \ No newline at end of file diff --git a/external/corert/src/System.Private.CoreLib/src/System/Decimal.cs b/external/corert/src/System.Private.CoreLib/src/System/Decimal.cs index 7ce9d22e5c..e9498a5294 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Decimal.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Decimal.cs @@ -2,7 +2,7 @@ // 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.Diagnostics.Contracts; +using System.Diagnostics; using System.Globalization; using System.Runtime.InteropServices; using System.Runtime.Serialization; @@ -52,8 +52,9 @@ namespace System // Decimal throws an OverflowException if the value is not within // the range of the Decimal type. [Serializable] - [StructLayout(LayoutKind.Sequential)] - public partial struct Decimal : IFormattable, IComparable, IConvertible, IComparable, IEquatable, IDeserializationCallback + [StructLayout(LayoutKind.Explicit)] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public partial struct Decimal : IFormattable, IComparable, IConvertible, IComparable, IEquatable, IDeserializationCallback, ISpanFormattable { // Sign mask for the flags field. A value of zero in this bit indicates a // positive Decimal value, and a value of one in this bit indicates a @@ -99,21 +100,33 @@ namespace System // and finally bit 31 indicates the sign of the Decimal value, 0 meaning // positive and 1 meaning negative. // - // NOTE: Do not change the order in which these fields are declared. The - // native methods in this class rely on this particular order. - private uint _flags; - private uint _hi; - private uint _lo; - private uint _mid; + // NOTE: Do not change the offsets of these fields. This structure maps to the OleAut DECIMAL structure + // and can be passed as such in P/Invokes. + [FieldOffset(0)] + private int flags; // Do not rename (binary serialization) + [FieldOffset(4)] + private int hi; // Do not rename (binary serialization) + [FieldOffset(8)] + private int lo; // Do not rename (binary serialization) + [FieldOffset(12)] + private int mid; // Do not rename (binary serialization) + // NOTE: This set of fields overlay the ones exposed to serialization (which have to be signed ints for serialization compat.) + // The code inside Decimal was ported from C++ and expect unsigned values. + [FieldOffset(0), NonSerialized] + private uint uflags; + [FieldOffset(4), NonSerialized] + private uint uhi; + [FieldOffset(8), NonSerialized] + private uint ulo; + [FieldOffset(12), NonSerialized] + private uint umid; - // Constructs a zero Decimal. - //public Decimal() { - // lo = 0; - // mid = 0; - // hi = 0; - // flags = 0; - //} + /// + /// The low and mid fields combined in little-endian order + /// + [FieldOffset(8), NonSerialized] + private ulong ulomidLE; // Constructs a Decimal from an integer value. // @@ -124,16 +137,16 @@ namespace System int value_copy = value; if (value_copy >= 0) { - _flags = 0; + uflags = 0; } else { - _flags = SignMask; + uflags = SignMask; value_copy = -value_copy; } - _lo = (uint)value_copy; - _mid = 0; - _hi = 0; + lo = value_copy; + mid = 0; + hi = 0; } // Constructs a Decimal from an unsigned integer value. @@ -141,10 +154,10 @@ namespace System [CLSCompliant(false)] public Decimal(uint value) { - _flags = 0; - _lo = value; - _mid = 0; - _hi = 0; + uflags = 0; + ulo = value; + umid = 0; + uhi = 0; } // Constructs a Decimal from a long value. @@ -156,16 +169,15 @@ namespace System long value_copy = value; if (value_copy >= 0) { - _flags = 0; + uflags = 0; } else { - _flags = SignMask; + uflags = SignMask; value_copy = -value_copy; } - _lo = (uint)value_copy; - _mid = (uint)(value_copy >> 32); - _hi = 0; + Low64 = (ulong)value_copy; + uhi = 0; } // Constructs a Decimal from an unsigned long value. @@ -173,10 +185,9 @@ namespace System [CLSCompliant(false)] public Decimal(ulong value) { - _flags = 0; - _lo = (uint)value; - _mid = (uint)(value >> 32); - _hi = 0; + uflags = 0; + Low64 = value; + uhi = 0; } // Constructs a Decimal from a float value. @@ -204,10 +215,10 @@ namespace System { Decimal d = default(Decimal); - ulong absoluteCy; // has to be ulong to accomodate the case where cy == long.MinValue. + ulong absoluteCy; // has to be ulong to accommodate the case where cy == long.MinValue. if (cy < 0) { - d.Sign = true; + d.IsNegative = true; absoluteCy = (ulong)(-cy); } else @@ -264,10 +275,6 @@ namespace System // public Decimal(int[] bits) { - _lo = 0; - _mid = 0; - _hi = 0; - _flags = 0; SetBits(bits); } @@ -275,16 +282,15 @@ namespace System { if (bits == null) throw new ArgumentNullException(nameof(bits)); - Contract.EndContractBlock(); if (bits.Length == 4) { uint f = (uint)bits[3]; if (IsValid(f)) { - _lo = (uint)bits[0]; - _mid = (uint)bits[1]; - _hi = (uint)bits[2]; - _flags = f; + lo = bits[0]; + mid = bits[1]; + hi = bits[2]; + uflags = f; return; } } @@ -297,13 +303,12 @@ namespace System { if (scale > 28) throw new ArgumentOutOfRangeException(nameof(scale), SR.ArgumentOutOfRange_DecimalScale); - Contract.EndContractBlock(); - _lo = (uint)lo; - _mid = (uint)mid; - _hi = (uint)hi; - _flags = ((uint)scale) << 16; + this.lo = lo; + this.mid = mid; + this.hi = hi; + uflags = ((uint)scale) << 16; if (isNegative) - _flags |= SignMask; + uflags |= SignMask; } void IDeserializationCallback.OnDeserialization(Object sender) @@ -325,10 +330,10 @@ namespace System { if ((flags & ~(SignMask | ScaleMask)) == 0 && (flags & ScaleMask) <= (28 << 16)) { - _lo = (uint)lo; - _mid = (uint)mid; - _hi = (uint)hi; - _flags = (uint)flags; + this.lo = lo; + this.mid = mid; + this.hi = hi; + this.flags = flags; return; } throw new ArgumentException(SR.Arg_DecBitCtor); @@ -340,7 +345,7 @@ namespace System // internal static Decimal Abs(Decimal d) { - return new Decimal((int)d._lo, (int)d._mid, (int)d._hi, (int)(d._flags & ~SignMask)); + return new Decimal(d.lo, d.mid, d.hi, (int)(d.uflags & ~SignMask)); } @@ -463,26 +468,27 @@ namespace System // public override String ToString() { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatDecimal(this, null, null); + return Number.FormatDecimal(this, null, NumberFormatInfo.CurrentInfo); } public String ToString(String format) { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatDecimal(this, format, null); + return Number.FormatDecimal(this, format, NumberFormatInfo.CurrentInfo); } public String ToString(IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatDecimal(this, null, provider); + return Number.FormatDecimal(this, null, NumberFormatInfo.GetInstance(provider)); } public String ToString(String format, IFormatProvider provider) { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatDecimal(this, format, provider); + return Number.FormatDecimal(this, format, NumberFormatInfo.GetInstance(provider)); + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) + { + return Number.TryFormatDecimal(this, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); } @@ -495,7 +501,8 @@ namespace System // public static Decimal Parse(String s) { - return FormatProvider.ParseDecimal(s, NumberStyles.Number, null); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseDecimal(s, NumberStyles.Number, NumberFormatInfo.CurrentInfo); } internal const NumberStyles InvalidNumberStyles = ~(NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite @@ -511,7 +518,6 @@ namespace System { throw new ArgumentException(SR.Argument_InvalidNumberStyles, nameof(style)); } - Contract.EndContractBlock(); if ((style & NumberStyles.AllowHexSpecifier) != 0) { // Check for hex number throw new ArgumentException(SR.Arg_HexStyleNotSupported); @@ -521,29 +527,60 @@ namespace System public static Decimal Parse(String s, NumberStyles style) { ValidateParseStyleFloatingPoint(style); - return FormatProvider.ParseDecimal(s, style, null); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseDecimal(s, style, NumberFormatInfo.CurrentInfo); } public static Decimal Parse(String s, IFormatProvider provider) { - return FormatProvider.ParseDecimal(s, NumberStyles.Number, provider); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseDecimal(s, NumberStyles.Number, NumberFormatInfo.GetInstance(provider)); } public static Decimal Parse(String s, NumberStyles style, IFormatProvider provider) { ValidateParseStyleFloatingPoint(style); - return FormatProvider.ParseDecimal(s, style, provider); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseDecimal(s, style, NumberFormatInfo.GetInstance(provider)); + } + + public static Decimal Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider) + { + ValidateParseStyleFloatingPoint(style); + return Number.ParseDecimal(s, style, NumberFormatInfo.GetInstance(provider)); } public static Boolean TryParse(String s, out Decimal result) { - return FormatProvider.TryParseDecimal(s, NumberStyles.Number, null, out result); + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseDecimal(s, NumberStyles.Number, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(ReadOnlySpan s, out decimal result) + { + return Number.TryParseDecimal(s, NumberStyles.Number, NumberFormatInfo.CurrentInfo, out result); } public static Boolean TryParse(String s, NumberStyles style, IFormatProvider provider, out Decimal result) { ValidateParseStyleFloatingPoint(style); - return FormatProvider.TryParseDecimal(s, style, provider, out result); + if (s == null) + { + result = 0; + return false; + } + return Number.TryParseDecimal(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out decimal result) + { + ValidateParseStyleFloatingPoint(style); + return Number.TryParseDecimal(s, style, NumberFormatInfo.GetInstance(provider), out result); } // Returns a binary representation of a Decimal. The return value is an @@ -558,7 +595,31 @@ namespace System // public static int[] GetBits(Decimal d) { - return new int[] { (int)d._lo, (int)d._mid, (int)d._hi, (int)d._flags }; + return new int[] { d.lo, d.mid, d.hi, d.flags }; + } + + internal static void GetBytes(Decimal d, byte[] buffer) + { + Debug.Assert((buffer != null && buffer.Length >= 16), "[GetBytes]buffer != null && buffer.Length >= 16"); + buffer[0] = (byte)d.lo; + buffer[1] = (byte)(d.lo >> 8); + buffer[2] = (byte)(d.lo >> 16); + buffer[3] = (byte)(d.lo >> 24); + + buffer[4] = (byte)d.mid; + buffer[5] = (byte)(d.mid >> 8); + buffer[6] = (byte)(d.mid >> 16); + buffer[7] = (byte)(d.mid >> 24); + + buffer[8] = (byte)d.hi; + buffer[9] = (byte)(d.hi >> 8); + buffer[10] = (byte)(d.hi >> 16); + buffer[11] = (byte)(d.hi >> 24); + + buffer[12] = (byte)d.flags; + buffer[13] = (byte)(d.flags >> 8); + buffer[14] = (byte)(d.flags >> 16); + buffer[15] = (byte)(d.flags >> 24); } // Returns the larger of two Decimal values. @@ -585,9 +646,8 @@ namespace System // public static Decimal Multiply(Decimal d1, Decimal d2) { - Decimal decRes; - DecCalc.VarDecMul(ref d1, ref d2, out decRes); - return decRes; + DecCalc.VarDecMul(ref d1, ref d2); + return d1; } // Returns the negated value of the given Decimal. If d is non-zero, @@ -595,7 +655,7 @@ namespace System // public static Decimal Negate(Decimal d) { - return new Decimal((int)d._lo, (int)d._mid, (int)d._hi, (int)(d._flags ^ SignMask)); + return new Decimal(d.lo, d.mid, d.hi, (int)(d.uflags ^ SignMask)); } // Rounds a Decimal value to a given number of decimal places. The value @@ -635,7 +695,6 @@ namespace System throw new ArgumentOutOfRangeException(nameof(decimals), SR.ArgumentOutOfRange_DecimalRound); if (mode < MidpointRounding.ToEven || mode > MidpointRounding.AwayFromZero) throw new ArgumentException(SR.Format(SR.Argument_InvalidEnumValue, mode, "MidpointRounding"), nameof(mode)); - Contract.EndContractBlock(); if (mode == MidpointRounding.ToEven) { @@ -650,6 +709,8 @@ namespace System return d; } + internal static int Sign(ref decimal d) => (d.lo | d.mid | d.hi) == 0 ? 0 : (d.flags >> 31) | 1; + // Subtracts two Decimal values. // public static Decimal Subtract(Decimal d1, Decimal d2) @@ -673,7 +734,7 @@ namespace System { throw new OverflowException(SR.Overflow_Byte, e); } - if (temp < Byte.MinValue || temp > Byte.MaxValue) throw new OverflowException(SR.Overflow_Byte); + if (temp != (byte)temp) throw new OverflowException(SR.Overflow_Byte); return (byte)temp; } @@ -693,7 +754,7 @@ namespace System { throw new OverflowException(SR.Overflow_SByte, e); } - if (temp < SByte.MinValue || temp > SByte.MaxValue) throw new OverflowException(SR.Overflow_SByte); + if (temp != (sbyte)temp) throw new OverflowException(SR.Overflow_SByte); return (sbyte)temp; } @@ -712,7 +773,7 @@ namespace System { throw new OverflowException(SR.Overflow_Int16, e); } - if (temp < Int16.MinValue || temp > Int16.MaxValue) throw new OverflowException(SR.Overflow_Int16); + if (temp != (short)temp) throw new OverflowException(SR.Overflow_Int16); return (short)temp; } @@ -731,10 +792,10 @@ namespace System public static int ToInt32(Decimal d) { if (d.Scale != 0) DecCalc.VarDecFix(ref d); - if (d._hi == 0 && d._mid == 0) + if (d.hi == 0 && d.mid == 0) { - int i = (int)d._lo; - if (!d.Sign) + int i = d.lo; + if (!d.IsNegative) { if (i >= 0) return i; } @@ -754,10 +815,10 @@ namespace System public static long ToInt64(Decimal d) { if (d.Scale != 0) DecCalc.VarDecFix(ref d); - if (d._hi == 0) + if (d.uhi == 0) { - long l = d._lo | (long)(int)d._mid << 32; - if (!d.Sign) + long l = d.ulo | (long)(int)d.umid << 32; + if (!d.IsNegative) { if (l >= 0) return l; } @@ -786,7 +847,7 @@ namespace System { throw new OverflowException(SR.Overflow_UInt16, e); } - if (temp < UInt16.MinValue || temp > UInt16.MaxValue) throw new OverflowException(SR.Overflow_UInt16); + if (temp != (ushort)temp) throw new OverflowException(SR.Overflow_UInt16); return (ushort)temp; } @@ -798,10 +859,10 @@ namespace System public static uint ToUInt32(Decimal d) { if (d.Scale != 0) DecCalc.VarDecFix(ref d); - if (d._hi == 0 && d._mid == 0) + if (d.uhi == 0 && d.umid == 0) { - if (!d.Sign || d._lo == 0) - return d._lo; + if (!d.IsNegative || d.ulo == 0) + return d.ulo; } throw new OverflowException(SR.Overflow_UInt32); } @@ -814,10 +875,10 @@ namespace System public static ulong ToUInt64(Decimal d) { if (d.Scale != 0) DecCalc.VarDecFix(ref d); - if (d._hi == 0) + if (d.uhi == 0) { - ulong l = (ulong)d._lo | ((ulong)d._mid << 32); - if (!d.Sign || l == 0) + ulong l = (ulong)d.ulo | ((ulong)d.umid << 32); + if (!d.IsNegative || l == 0) return l; } throw new OverflowException(SR.Overflow_UInt64); diff --git a/external/corert/src/System.Private.CoreLib/src/System/Delegate.cs b/external/corert/src/System.Private.CoreLib/src/System/Delegate.cs index 8360890cc3..0ec444eaa5 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Delegate.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Delegate.cs @@ -109,16 +109,26 @@ namespace System /// /// This value indicates if the returned pointer is an open resolver structure. /// + /// + /// Delegate points to an object array thunk (the delegate wraps a Func delegate). This + /// is typically a delegate pointing to the LINQ expression interpreter. + /// /// - unsafe internal IntPtr GetFunctionPointer(out RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate, out bool isOpenResolver) + unsafe internal IntPtr GetFunctionPointer(out RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate, out bool isOpenResolver, out bool isInterpreterEntrypoint) { typeOfFirstParameterIfInstanceDelegate = default(RuntimeTypeHandle); isOpenResolver = false; + isInterpreterEntrypoint = false; if (GetThunk(MulticastThunk) == m_functionPointer) { return IntPtr.Zero; } + else if (GetThunk(ObjectArrayThunk) == m_functionPointer) + { + isInterpreterEntrypoint = true; + return IntPtr.Zero; + } else if (m_extraFunctionPointerOrData != IntPtr.Zero) { if (GetThunk(OpenInstanceThunk) == m_functionPointer) @@ -363,7 +373,7 @@ namespace System else { IntPtr invokeThunk = this.GetThunk(DelegateInvokeThunk); - object result = System.InvokeUtils.CallDynamicInvokeMethod(this.m_firstParameter, this.m_functionPointer, this, invokeThunk, IntPtr.Zero, this, args, binderBundle: null); + object result = System.InvokeUtils.CallDynamicInvokeMethod(this.m_firstParameter, this.m_functionPointer, this, invokeThunk, IntPtr.Zero, this, args, binderBundle: null, wrapInTargetInvocationException: true); DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); return result; } @@ -792,7 +802,7 @@ namespace System public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { - throw new NotSupportedException(); + throw new PlatformNotSupportedException(SR.Serialization_DelegatesNotSupported); } internal bool IsOpenStatic @@ -899,8 +909,7 @@ namespace System else { RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate; - bool isOpenThunk; - IntPtr functionPointer = GetFunctionPointer(out typeOfFirstParameterIfInstanceDelegate, out isOpenThunk); + IntPtr functionPointer = GetFunctionPointer(out typeOfFirstParameterIfInstanceDelegate, out bool _, out bool _); if (!FunctionPointerOps.IsGenericMethodPointer(functionPointer)) { return DebuggerFunctionPointerFormattingHook(functionPointer, typeOfFirstParameterIfInstanceDelegate); diff --git a/external/corert/src/System.Private.CoreLib/src/System/DelegateSerializationHolder.cs b/external/corert/src/System.Private.CoreLib/src/System/DelegateSerializationHolder.cs deleted file mode 100644 index 9d01ebfaf0..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/DelegateSerializationHolder.cs +++ /dev/null @@ -1,139 +0,0 @@ -// 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.Reflection; -using System.Runtime.Serialization; -using System.Diagnostics; -using System.Diagnostics.Contracts; - -namespace System -{ - [Serializable] - public sealed class DelegateSerializationHolder : IObjectReference, ISerializable - { - internal static DelegateEntry GetDelegateSerializationInfo(SerializationInfo info, Type delegateType, Object target, MethodInfo method, int targetIndex) - { - // Used for MulticastDelegate - - Debug.Assert(method != null); - - Type c = delegateType.BaseType; - - if (c == null || (c != typeof(Delegate) && c != typeof(MulticastDelegate))) - throw new ArgumentException(SR.Arg_MustBeDelegate); - - if (method.DeclaringType == null) - throw new NotSupportedException(SR.NotSupported_GlobalMethodSerialization); - - DelegateEntry de = new DelegateEntry(delegateType, target, method); - - if (info.MemberCount == 0) - { - info.SetType(typeof(DelegateSerializationHolder)); - info.AddValue("DelegateEntry", de, typeof(DelegateEntry)); - } - - return de; - } - - [Serializable] - internal class DelegateEntry - { - public Type DelegateType; - public Object TargetObject; - public MethodInfo TargetMethod; - public DelegateEntry NextEntry; - - internal DelegateEntry(Type delegateType, Object target, MethodInfo targetMethod) - { - DelegateType = delegateType; - TargetObject = target; - TargetMethod = targetMethod; - } - } - - private DelegateEntry _delegateEntry; - private int _delegatesCount; - - public DelegateSerializationHolder(SerializationInfo info, StreamingContext context) - { - if (info == null) - throw new ArgumentNullException(nameof(info)); - Contract.EndContractBlock(); - - try - { - _delegateEntry = (DelegateEntry)info.GetValue("DelegateEntry", typeof(DelegateEntry)); - } - catch - { - // TODO: If we *really* want to support cross-runtime serialization and deserialization of delegates, - // we'll have to implemenent the handling of the old format we have today in CoreCLR. - // This is not a requirement today. - _delegateEntry = null; - } - - _delegatesCount = 0; - DelegateEntry currentEntry = _delegateEntry; - while (currentEntry != null) - { - _delegatesCount++; - currentEntry = currentEntry.NextEntry; - } - } - - private void ThrowInsufficientState(string field) - { - throw new SerializationException(SR.Format(SR.Serialization_InsufficientDeserializationState, field)); - } - - private Delegate GetDelegate(DelegateEntry de) - { - try - { - if (de.DelegateType == null) - ThrowInsufficientState("DelegateType"); - - if (de.TargetMethod == null) - ThrowInsufficientState("TargetMethod"); - - return Delegate.CreateDelegate(de.DelegateType, de.TargetObject, de.TargetMethod); - } - catch (Exception e) when (!(e is SerializationException)) - { - throw new SerializationException(e.Message, e); - } - } - - public Object GetRealObject(StreamingContext context) - { - if (_delegateEntry == null) - ThrowInsufficientState("DelegateEntry"); - - if (_delegatesCount == 1) - { - return GetDelegate(_delegateEntry); - } - else - { - Delegate[] invocationList = new Delegate[_delegatesCount]; - - int index = _delegatesCount - 1; - for (DelegateEntry de = _delegateEntry; de != null; de = de.NextEntry) - { - // Be careful to match the index we pass to GetDelegate (used to look up extra information for each delegate) to - // the order we process the entries: we're actually looking at them in reverse order. - invocationList[index--] = GetDelegate(de); - } - return ((MulticastDelegate)invocationList[0]).NewMulticastDelegate(invocationList, invocationList.Length); - } - } - - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - throw new NotSupportedException(SR.NotSupported_DelegateSerHolderSerial); - } - } -} \ No newline at end of file diff --git a/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/Contracts/Contracts.cs b/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/Contracts/Contracts.cs index 7e27c4dfda..4ff7706e9b 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/Contracts/Contracts.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/Contracts/Contracts.cs @@ -861,6 +861,7 @@ namespace System.Diagnostics.Contracts #endregion } + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public enum ContractFailureKind { Precondition, diff --git a/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/Contracts/ContractsBCL.cs b/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/Contracts/ContractsBCL.cs index 1112c8fbea..56626bb8f9 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/Contracts/ContractsBCL.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/Contracts/ContractsBCL.cs @@ -42,7 +42,7 @@ namespace System.Diagnostics.Contracts if (t_assertingMustUseRewriter) { - System.Diagnostics.Debug.Assert(false, "Asserting that we must use the rewriter went reentrant. Didn't rewrite this System.Private.CoreLib?"); + System.Diagnostics.Debug.Fail("Asserting that we must use the rewriter went reentrant. Didn't rewrite this System.Private.CoreLib?"); return; } t_assertingMustUseRewriter = true; @@ -197,26 +197,23 @@ namespace System.Diagnostics.Contracts } #endif // !FEATURE_CORECLR -#if FEATURE_SERIALIZATION [Serializable] -#else - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors")] -#endif - [SuppressMessage("Microsoft.Design", "CA1064:ExceptionsShouldBePublic")] - internal sealed class ContractException : Exception + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + // Needs to be public for type forwarding serialization support. + public sealed class ContractException : Exception { - private readonly ContractFailureKind _Kind; - private readonly string _UserMessage; - private readonly string _Condition; + private readonly ContractFailureKind _kind; + private readonly string _userMessage; + private readonly string _condition; [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - public ContractFailureKind Kind { get { return _Kind; } } + public ContractFailureKind Kind { get { return _kind; } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public string Failure { get { return this.Message; } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - public string UserMessage { get { return _UserMessage; } } + public string UserMessage { get { return _userMessage; } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - public string Condition { get { return _Condition; } } + public string Condition { get { return _condition; } } // Called by COM Interop, if we see COR_E_CODECONTRACTFAILED as an HRESULT. private ContractException() @@ -228,35 +225,26 @@ namespace System.Diagnostics.Contracts : base(failure, innerException) { HResult = System.Runtime.CompilerServices.ContractHelper.COR_E_CODECONTRACTFAILED; - _Kind = kind; - _UserMessage = userMessage; - _Condition = condition; + _kind = kind; + _userMessage = userMessage; + _condition = condition; } -#if FEATURE_SERIALIZATION + private ContractException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { - _Kind = (ContractFailureKind)info.GetInt32("Kind"); - _UserMessage = info.GetString("UserMessage"); - _Condition = info.GetString("Condition"); + _kind = (ContractFailureKind)info.GetInt32("Kind"); + _userMessage = info.GetString("UserMessage"); + _condition = info.GetString("Condition"); } -#endif // FEATURE_SERIALIZATION -#if FEATURE_UNTRUSTED_CALLERS && FEATURE_SERIALIZATION -#if FEATURE_LINK_DEMAND && FEATURE_SERIALIZATION - [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] -#endif // FEATURE_LINK_DEMAND -#endif // FEATURE_UNTRUSTED_CALLERS -#if FEATURE_SERIALIZATION public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { base.GetObjectData(info, context); - - info.AddValue("Kind", _Kind); - info.AddValue("UserMessage", _UserMessage); - info.AddValue("Condition", _Condition); + info.AddValue("Kind", _kind); + info.AddValue("UserMessage", _userMessage); + info.AddValue("Condition", _condition); } -#endif } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/Debug.CoreRT.cs b/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/Debug.CoreRT.cs index 0ac9268287..d000bf3c1d 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/Debug.CoreRT.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/Debug.CoreRT.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Internal.DeveloperExperience; -using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Security; diff --git a/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/Debug.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/Debug.Windows.cs index 161d69e45d..aae5c6c37d 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/Debug.Windows.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/Debug.Windows.cs @@ -17,7 +17,7 @@ namespace System.Diagnostics bool result = DeveloperExperience.Default.OnContractFailure(stackTrace, ContractFailureKind.Assert, fullMessage, null, null, null); if (!result) { - ExitProcess(); + RuntimeExceptionHelpers.FailFast(fullMessage); } } @@ -53,10 +53,5 @@ namespace System.Diagnostics { Interop.mincore.OutputDebugString(message ?? string.Empty); } - - private static void ExitProcess() - { - Interop.mincore.ExitProcess((uint)(Interop.Constants.EFail)); - } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/DebuggerAttributes.cs b/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/DebuggerAttributes.cs deleted file mode 100644 index d19bc5a79f..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/DebuggerAttributes.cs +++ /dev/null @@ -1,366 +0,0 @@ -// 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. - -/*============================================================ -** -** -** -** Purpose: Attributes for debugger -** -** -===========================================================*/ - -using System; -using System.Runtime.InteropServices; -using System.Diagnostics.Contracts; - -namespace System.Diagnostics -{ - // - // This attribute is used by the IL2IL toolchain to mark generated code to control debugger stepping policy - // - [System.Runtime.CompilerServices.DependencyReductionRoot] - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false)] - public sealed class DebuggerStepThroughAttribute : Attribute - { - public DebuggerStepThroughAttribute() { } - } - - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Constructor, Inherited = false)] - public sealed class DebuggerGuidedStepThroughAttribute : Attribute - { - public DebuggerGuidedStepThroughAttribute() { } - } - - // This attribute is not in Win8P because it is not portable and so we aren't including it, if it turns out to be important we can consider - // adding it into the next version of System.Diagnostics.Debug contract assembly - //[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false)] - //public sealed class DebuggerStepperBoundaryAttribute : Attribute - //{ - // public DebuggerStepperBoundaryAttribute() { } - //} - - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Constructor, Inherited = false)] - public sealed class DebuggerHiddenAttribute : Attribute - { - public DebuggerHiddenAttribute() { } - } - - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Constructor | AttributeTargets.Struct, Inherited = false)] - public sealed class DebuggerNonUserCodeAttribute : Attribute - { - public DebuggerNonUserCodeAttribute() { } - } - - // Attribute class used by the compiler to mark modules. - // If present, then debugging information for everything in the - // assembly was generated by the compiler, and will be preserved - // by the Runtime so that the debugger can provide full functionality - // in the case of JIT attach. If not present, then the compiler may - // or may not have included debugging information, and the Runtime - // won't preserve the debugging info, which will make debugging after - // a JIT attach difficult. - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module, AllowMultiple = false)] - public sealed class DebuggableAttribute : Attribute - { - [Flags] - public enum DebuggingModes - { - None = 0x0, - Default = 0x1, - DisableOptimizations = 0x100, - IgnoreSymbolStoreSequencePoints = 0x2, - EnableEditAndContinue = 0x4 - } - - public DebuggableAttribute(bool isJITTrackingEnabled, bool isJITOptimizerDisabled) - { - DebuggingFlags = 0; - - if (isJITTrackingEnabled) - { - DebuggingFlags |= DebuggingModes.Default; - } - - if (isJITOptimizerDisabled) - { - DebuggingFlags |= DebuggingModes.DisableOptimizations; - } - } - - public DebuggableAttribute(DebuggingModes modes) - { - DebuggingFlags = modes; - } - - public bool IsJITTrackingEnabled => (DebuggingFlags & DebuggingModes.Default) != 0; - - public bool IsJITOptimizerDisabled => (DebuggingFlags & DebuggingModes.DisableOptimizations) != 0; - - public DebuggingModes DebuggingFlags { get; } - } - - // DebuggerBrowsableState states are defined as follows: - // Never never show this element - // Expanded expansion of the class is done, so that all visible internal members are shown - // Collapsed expansion of the class is not performed. Internal visible members are hidden - // RootHidden The target element itself should not be shown, but should instead be - // automatically expanded to have its members displayed. - // Default value is collapsed - - // Please also change the code which validates DebuggerBrowsableState variable (in this file) - // if you change this enum. - public enum DebuggerBrowsableState - { - Never = 0, - //Expanded is not supported in this release - //Expanded = 1, - Collapsed = 2, - RootHidden = 3 - } - - - // the one currently supported with the csee.dat - // (mcee.dat, autoexp.dat) file. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)] - public sealed class DebuggerBrowsableAttribute : Attribute - { - private DebuggerBrowsableState _state; - public DebuggerBrowsableAttribute(DebuggerBrowsableState state) - { - if (state < DebuggerBrowsableState.Never || state > DebuggerBrowsableState.RootHidden) - throw new ArgumentOutOfRangeException(nameof(state)); - Contract.EndContractBlock(); - - _state = state; - } - public DebuggerBrowsableState State - { - get { return _state; } - } - } - - - // DebuggerTypeProxyAttribute - [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)] - public sealed class DebuggerTypeProxyAttribute : Attribute - { - private string _typeName; - private string _targetName; - private Type _target; - - public DebuggerTypeProxyAttribute(Type type) - { - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } - Contract.EndContractBlock(); - - _typeName = type.AssemblyQualifiedName; - } - - public DebuggerTypeProxyAttribute(string typeName) - { - _typeName = typeName; - } - public string ProxyTypeName - { - get { return _typeName; } - } - - public Type Target - { - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - Contract.EndContractBlock(); - - _targetName = value.AssemblyQualifiedName; - _target = value; - } - - get { return _target; } - } - - public string TargetTypeName - { - get { return _targetName; } - set { _targetName = value; } - } - } - - // This attribute is used to control what is displayed for the given class or field - // in the data windows in the debugger. The single argument to this attribute is - // the string that will be displayed in the value column for instances of the type. - // This string can include text between { and } which can be either a field, - // property or method (as will be documented in mscorlib). In the C# case, - // a general expression will be allowed which only has implicit access to the this pointer - // for the current instance of the target type. The expression will be limited, - // however: there is no access to aliases, locals, or pointers. - // In addition, attributes on properties referenced in the expression are not processed. - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Assembly, AllowMultiple = true)] - public sealed class DebuggerDisplayAttribute : Attribute - { - private string _name; - private string _value; - private string _type; - private string _targetName; - private Type _target; - - public DebuggerDisplayAttribute(string value) - { - if (value == null) - { - _value = ""; - } - else - { - _value = value; - } - _name = ""; - _type = ""; - } - - public string Value - { - get { return _value; } - } - - public string Name - { - get { return _name; } - set { _name = value; } - } - - public string Type - { - get { return _type; } - set { _type = value; } - } - - public Type Target - { - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - Contract.EndContractBlock(); - - _targetName = value.AssemblyQualifiedName; - _target = value; - } - get { return _target; } - } - - public string TargetTypeName - { - get { return _targetName; } - set { _targetName = value; } - } - } - ///// - ///// Signifies that the attributed type has a visualizer which is pointed - ///// to by the parameter type name strings. - ///// - //[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)] - //public sealed class DebuggerVisualizerAttribute: Attribute - //{ - // private string visualizerObjectSourceName; - // private string visualizerName; - // private string description; - // private string targetName; - // private Type target; - - // public DebuggerVisualizerAttribute(string visualizerTypeName) - // { - // this.visualizerName = visualizerTypeName; - // } - // public DebuggerVisualizerAttribute(string visualizerTypeName, string visualizerObjectSourceTypeName) - // { - // this.visualizerName = visualizerTypeName; - // this.visualizerObjectSourceName = visualizerObjectSourceTypeName; - // } - // public DebuggerVisualizerAttribute(string visualizerTypeName, Type visualizerObjectSource) - // { - // if (visualizerObjectSource == null) { - // throw new ArgumentNullException("visualizerObjectSource"); - // } - // Contract.EndContractBlock(); - // this.visualizerName = visualizerTypeName; - // this.visualizerObjectSourceName = visualizerObjectSource.AssemblyQualifiedName; - // } - // public DebuggerVisualizerAttribute(Type visualizer) - // { - // if (visualizer == null) { - // throw new ArgumentNullException("visualizer"); - // } - // Contract.EndContractBlock(); - // this.visualizerName = visualizer.AssemblyQualifiedName; - // } - // public DebuggerVisualizerAttribute(Type visualizer, Type visualizerObjectSource) - // { - // if (visualizer == null) { - // throw new ArgumentNullException("visualizer"); - // } - // if (visualizerObjectSource == null) { - // throw new ArgumentNullException("visualizerObjectSource"); - // } - // Contract.EndContractBlock(); - // this.visualizerName = visualizer.AssemblyQualifiedName; - // this.visualizerObjectSourceName = visualizerObjectSource.AssemblyQualifiedName; - // } - // public DebuggerVisualizerAttribute(Type visualizer, string visualizerObjectSourceTypeName) - // { - // if (visualizer == null) { - // throw new ArgumentNullException("visualizer"); - // } - // Contract.EndContractBlock(); - // this.visualizerName = visualizer.AssemblyQualifiedName; - // this.visualizerObjectSourceName = visualizerObjectSourceTypeName; - // } - - // public string VisualizerObjectSourceTypeName - // { - // get { return visualizerObjectSourceName; } - // } - // public string VisualizerTypeName - // { - // get { return visualizerName; } - // } - // public string Description - // { - // get { return description; } - // set { description = value; } - // } - - // public Type Target - // { - // set { - // if( value == null) { - // throw new ArgumentNullException("value"); - // } - // Contract.EndContractBlock(); - - // targetName = value.AssemblyQualifiedName; - // target = value; - // } - - // get { return target; } - // } - - // public string TargetTypeName - // { - // set { targetName = value; } - // get { return targetName; } - // } - //} -} - - diff --git a/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/DebuggerGuidedStepThroughAttribute.cs b/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/DebuggerGuidedStepThroughAttribute.cs new file mode 100644 index 0000000000..ff6219b79e --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/DebuggerGuidedStepThroughAttribute.cs @@ -0,0 +1,12 @@ +// 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. + +namespace System.Diagnostics +{ + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Constructor, Inherited = false)] + public sealed class DebuggerGuidedStepThroughAttribute : Attribute + { + public DebuggerGuidedStepThroughAttribute() { } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/StackFrame.CoreRT.cs b/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/StackFrame.CoreRT.cs new file mode 100644 index 0000000000..cc8ac5a3d6 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/StackFrame.CoreRT.cs @@ -0,0 +1,143 @@ +// 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.Reflection; +using System.Runtime; +using System.Text; + +using Internal.DeveloperExperience; +using Internal.Diagnostics; + +namespace System.Diagnostics +{ + /// + /// Stack frame represents a single frame in a stack trace; frames + /// corresponding to methods with available symbolic information + /// provide source file / line information. Some frames may provide IL + /// offset information and / or MethodBase reflection information. + /// There is no good reason for the methods of this class to be virtual. + /// + public partial class StackFrame + { + /// + /// IP address representing this stack frame. + /// + private IntPtr _ipAddress; + + /// + /// File info flag to use for stack trace-style formatting. + /// + private bool _needFileInfo; + + /// + /// Constructs a StackFrame corresponding to a given IP address. + /// + internal StackFrame(IntPtr ipAddress, bool needFileInfo) + { + InitializeForIpAddress(ipAddress, needFileInfo); + } + + /// + /// Internal stack frame initialization based on IP address. + /// + private void InitializeForIpAddress(IntPtr ipAddress, bool needFileInfo) + { + _ipAddress = ipAddress; + _needFileInfo = needFileInfo; + + if (_ipAddress == StackTraceHelper.SpecialIP.EdiSeparator) + { + SetIsLastFrameFromForeignExceptionStackTrace(true); + } + else if (_ipAddress != IntPtr.Zero) + { + IntPtr methodStartAddress = RuntimeImports.RhFindMethodStartAddress(ipAddress); + + _nativeOffset = (int)(_ipAddress.ToInt64() - methodStartAddress.ToInt64()); + + DeveloperExperience.Default.TryGetILOffsetWithinMethod(_ipAddress, out _ilOffset); + DeveloperExperience.Default.TryGetMethodBase(methodStartAddress, out _method); + + if (needFileInfo) + { + DeveloperExperience.Default.TryGetSourceLineInfo( + _ipAddress, + out _fileName, + out _lineNumber, + out _columnNumber); + } + } + } + + /// + /// Internal stack frame initialization based on frame index within the stack of the current thread. + /// + private void BuildStackFrame(int frameIndex, bool needFileInfo) + { + IntPtr ipAddress = LocateIpAddressForStackFrame(frameIndex); + InitializeForIpAddress(ipAddress, needFileInfo); + } + + /// + /// Locate IP address corresponding to a given frame. Ignore .NET Native-specific rethrow markers. + /// + private IntPtr LocateIpAddressForStackFrame(int frameIndex) + { + IntPtr[] frameArray = new IntPtr[frameIndex + 1]; + int returnedFrameCount = RuntimeImports.RhGetCurrentThreadStackTrace(frameArray); + int realFrameCount = (returnedFrameCount >= 0 ? returnedFrameCount : frameArray.Length); + if (frameIndex < realFrameCount) + { + return frameArray[frameIndex]; + } + + // No more frames are available + return IntPtr.Zero; + } + + /// + /// Return native IP address for this stack frame. + /// + internal IntPtr GetNativeIPAddress() + { + return _ipAddress; + } + + /// + /// Check whether method info is available. + /// + internal bool HasMethod() + { + return _method != null; + } + + /// + /// Format stack frame without MethodBase info. Return true if the stack info + /// is valid and line information should be appended if available. + /// + private bool AppendStackFrameWithoutMethodBase(StringBuilder builder) + { + builder.Append(DeveloperExperience.Default.CreateStackTraceString(_ipAddress, includeFileInfo: false)); + return true; + } + + /// + /// Builds a representation of the stack frame for use in the stack trace. + /// + internal void AppendToStackTrace(StringBuilder builder) + { + if (_ipAddress == StackTraceHelper.SpecialIP.EdiSeparator) + { + builder.AppendLine(SR.StackTrace_EndStackTraceFromPreviousThrow); + } + else + { + builder.Append(SR.StackTrace_AtWord); + builder.AppendLine(DeveloperExperience.Default.CreateStackTraceString(_ipAddress, _needFileInfo)); + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/StackFrame.cs b/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/StackFrame.cs index d04056f1dd..fe06d85f2c 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/StackFrame.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/StackFrame.cs @@ -3,41 +3,82 @@ // See the LICENSE file in the project root for more information. +using System.Text; using System; -using System.Diagnostics.Contracts; +using System.IO; using System.Reflection; namespace System.Diagnostics { /// - /// Stack frame represents a single frame in a stack trace; frames - /// corresponding to methods with available symbolic information - /// provide source file / line information. Some frames may provide IL - /// offset information and / or MethodBase reflection information. /// There is no good reason for the methods of this class to be virtual. /// - [Serializable] - public class StackFrame + public partial class StackFrame { /// - /// Constant returned when the native or IL offset is unknown + /// Reflection information for the method if available, null otherwise. /// - public const int OFFSET_UNKNOWN = -1; + private MethodBase _method; + + /// + /// Native offset of the current instruction within the current method if available, + /// OFFSET_UNKNOWN otherwise. + /// + private int _nativeOffset; + + /// + /// IL offset of the current instruction within the current method if available, + /// OFFSET_UNKNOWN otherwise. + /// + private int _ilOffset; + + /// + /// Source file name representing the current code location if available, null otherwise. + /// + private String _fileName; + + /// + /// Line number representing the current code location if available, 0 otherwise. + /// + private int _lineNumber; + + /// + /// Column number representing the current code location if available, 0 otherwise. + /// + private int _columnNumber; + + /// + /// This flag is set to true when the frame represents a rethrow marker. + /// + private bool _isLastFrameFromForeignExceptionStackTrace; + + internal void InitMembers() + { + _method = null; + _nativeOffset = OFFSET_UNKNOWN; + _ilOffset = OFFSET_UNKNOWN; + _fileName = null; + _lineNumber = 0; + _columnNumber = 0; + _isLastFrameFromForeignExceptionStackTrace = false; + } /// /// Constructs a StackFrame corresponding to the active stack frame. /// public StackFrame() { - throw new NotImplementedException(); + InitMembers(); + BuildStackFrame(0 + StackTrace.METHODS_TO_SKIP, false); } /// /// Constructs a StackFrame corresponding to the active stack frame. /// - public StackFrame(bool fNeedFileInfo) + public StackFrame(bool needFileInfo) { - throw new NotImplementedException(); + InitMembers(); + BuildStackFrame(0 + StackTrace.METHODS_TO_SKIP, needFileInfo); } /// @@ -45,15 +86,17 @@ namespace System.Diagnostics /// public StackFrame(int skipFrames) { - throw new NotImplementedException(); + InitMembers(); + BuildStackFrame(skipFrames + StackTrace.METHODS_TO_SKIP, false); } /// /// Constructs a StackFrame corresponding to a calling stack frame. /// - public StackFrame(int skipFrames, bool fNeedFileInfo) + public StackFrame(int skipFrames, bool needFileInfo) { - throw new NotImplementedException(); + InitMembers(); + BuildStackFrame(skipFrames + StackTrace.METHODS_TO_SKIP, needFileInfo); } /// @@ -63,7 +106,11 @@ namespace System.Diagnostics /// public StackFrame(String fileName, int lineNumber) { - throw new NotImplementedException(); + InitMembers(); + BuildStackFrame(StackTrace.METHODS_TO_SKIP, false); + _fileName = fileName; + _lineNumber = lineNumber; + _columnNumber = 0; } /// @@ -73,7 +120,56 @@ namespace System.Diagnostics /// public StackFrame(String fileName, int lineNumber, int colNumber) { - throw new NotImplementedException(); + InitMembers(); + BuildStackFrame(StackTrace.METHODS_TO_SKIP, false); + _fileName = fileName; + _lineNumber = lineNumber; + _columnNumber = colNumber; + } + + /// + /// Constant returned when the native or IL offset is unknown + /// + public const int OFFSET_UNKNOWN = -1; + + internal virtual void SetMethodBase(MethodBase mb) + { + _method = mb; + } + + internal virtual void SetOffset(int iOffset) + { + _nativeOffset = iOffset; + } + + internal virtual void SetILOffset(int iOffset) + { + _ilOffset = iOffset; + } + + internal virtual void SetFileName(String strFName) + { + _fileName = strFName; + } + + internal virtual void SetLineNumber(int iLine) + { + _lineNumber = iLine; + } + + internal virtual void SetColumnNumber(int iCol) + { + _columnNumber = iCol; + } + + internal virtual void SetIsLastFrameFromForeignExceptionStackTrace(bool fIsLastFrame) + { + _isLastFrameFromForeignExceptionStackTrace = fIsLastFrame; + } + + internal virtual bool GetIsLastFrameFromForeignExceptionStackTrace() + { + return _isLastFrameFromForeignExceptionStackTrace; } /// @@ -81,9 +177,7 @@ namespace System.Diagnostics /// public virtual MethodBase GetMethod() { - Contract.Ensures(Contract.Result() != null); - - throw new NotImplementedException(); + return _method; } /// @@ -92,9 +186,10 @@ namespace System.Diagnostics /// public virtual int GetNativeOffset() { - throw new NotImplementedException(); + return _nativeOffset; } + /// /// Returns the offset from the start of the IL code for the /// method being executed. This offset may be approximate depending @@ -102,7 +197,7 @@ namespace System.Diagnostics /// public virtual int GetILOffset() { - throw new NotImplementedException(); + return _ilOffset; } /// @@ -112,7 +207,7 @@ namespace System.Diagnostics /// public virtual String GetFileName() { - throw new NotImplementedException(); + return _fileName; } /// @@ -122,7 +217,7 @@ namespace System.Diagnostics /// public virtual int GetFileLineNumber() { - throw new NotImplementedException(); + return _lineNumber; } /// @@ -132,7 +227,7 @@ namespace System.Diagnostics /// public virtual int GetFileColumnNumber() { - throw new NotImplementedException(); + return _columnNumber; } /// @@ -140,7 +235,69 @@ namespace System.Diagnostics /// public override String ToString() { - throw new NotImplementedException(); + StringBuilder sb = new StringBuilder(255); + bool includeFileInfoIfAvailable; + + if (_method != null) + { + sb.Append(_method.Name); + + // deal with the generic portion of the method + if (_method is MethodInfo methodInfo && methodInfo.IsGenericMethod) + { + Type[] typars = methodInfo.GetGenericArguments(); + + sb.Append('<'); + int k = 0; + bool fFirstTyParam = true; + while (k < typars.Length) + { + if (fFirstTyParam == false) + sb.Append(','); + else + fFirstTyParam = false; + + sb.Append(typars[k].Name); + k++; + } + + sb.Append('>'); + } + includeFileInfoIfAvailable = true; + } + else + { + includeFileInfoIfAvailable = AppendStackFrameWithoutMethodBase(sb); + } + + if (includeFileInfoIfAvailable) + { + sb.Append(" at offset "); + if (_nativeOffset == OFFSET_UNKNOWN) + sb.Append(""); + else + sb.Append(_nativeOffset); + + sb.Append(" in file:line:column "); + + bool useFileName = (_fileName != null); + + if (!useFileName) + sb.Append(""); + else + sb.Append(_fileName); + sb.Append(':'); + sb.Append(_lineNumber); + sb.Append(':'); + sb.Append(_columnNumber); + } + else + { + sb.Append(""); + } + sb.Append(Environment.NewLine); + + return sb.ToString(); } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/StackFrameExtensions.cs b/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/StackFrameExtensions.cs index e0be5238ed..a938233d1a 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/StackFrameExtensions.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/StackFrameExtensions.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Runtime; namespace System.Diagnostics { @@ -13,7 +14,7 @@ namespace System.Diagnostics /// public static IntPtr GetNativeImageBase(this StackFrame stackFrame) { - throw new NotImplementedException(); + return RuntimeImports.RhGetOSModuleFromPointer(stackFrame.GetNativeIPAddress()); } /// @@ -21,7 +22,7 @@ namespace System.Diagnostics /// public static IntPtr GetNativeIP(this StackFrame stackFrame) { - throw new NotImplementedException(); + return stackFrame.GetNativeIPAddress(); } /// @@ -30,7 +31,7 @@ namespace System.Diagnostics /// public static bool HasILOffset(this StackFrame stackFrame) { - throw new NotImplementedException(); + return stackFrame.GetILOffset() != StackFrame.OFFSET_UNKNOWN; } /// @@ -38,7 +39,7 @@ namespace System.Diagnostics /// public static bool HasMethod(this StackFrame stackFrame) { - throw new NotImplementedException(); + return stackFrame.HasMethod(); } /// @@ -46,7 +47,8 @@ namespace System.Diagnostics /// public static bool HasNativeImage(this StackFrame stackFrame) { - throw new NotImplementedException(); + // In .NET Native, everything has a native image (at least today) + return true; } /// @@ -54,7 +56,7 @@ namespace System.Diagnostics /// public static bool HasSource(this StackFrame stackFrame) { - throw new NotImplementedException(); + return stackFrame.GetFileName() != null; } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs b/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs index e83fb80fe3..7dc82a5908 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs @@ -3,6 +3,10 @@ // See the LICENSE file in the project root for more information. using System; +using System.Runtime; +using System.Text; + +using Internal.Diagnostics; namespace System.Diagnostics { @@ -13,25 +17,29 @@ namespace System.Diagnostics /// StackTrace, we use an InheritanceDemand to prevent partially-trusted /// subclasses. /// - [Serializable] public class StackTrace { public const int METHODS_TO_SKIP = 0; + /// + /// Stack frames comprising this stack trace. + /// + private StackFrame[] _stackFrames; + /// /// Constructs a stack trace from the current location. /// public StackTrace() { - throw new NotImplementedException(); + InitializeForThreadFrameIndex(METHODS_TO_SKIP, needFileInfo: false); } /// /// Constructs a stack trace from the current location. /// - public StackTrace(bool fNeedFileInfo) + public StackTrace(bool needFileInfo) { - throw new NotImplementedException(); + InitializeForThreadFrameIndex(METHODS_TO_SKIP, needFileInfo); } /// @@ -40,16 +48,16 @@ namespace System.Diagnostics /// public StackTrace(int skipFrames) { - throw new NotImplementedException(); + InitializeForThreadFrameIndex(skipFrames, needFileInfo: false); } /// /// Constructs a stack trace from the current location, in a caller's /// frame /// - public StackTrace(int skipFrames, bool fNeedFileInfo) + public StackTrace(int skipFrames, bool needFileInfo) { - throw new NotImplementedException(); + InitializeForThreadFrameIndex(skipFrames, needFileInfo); } /// @@ -57,15 +65,15 @@ namespace System.Diagnostics /// public StackTrace(Exception e) { - throw new NotImplementedException(); + InitializeForExceptionFrameIndex(e, METHODS_TO_SKIP, needFileInfo: false); } /// /// Constructs a stack trace from the current location. /// - public StackTrace(Exception e, bool fNeedFileInfo) + public StackTrace(Exception e, bool needFileInfo) { - throw new NotImplementedException(); + InitializeForExceptionFrameIndex(e, METHODS_TO_SKIP, needFileInfo); } /// @@ -74,16 +82,16 @@ namespace System.Diagnostics /// public StackTrace(Exception e, int skipFrames) { - throw new NotImplementedException(); + InitializeForExceptionFrameIndex(e, skipFrames, needFileInfo: false); } /// /// Constructs a stack trace from the current location, in a caller's /// frame /// - public StackTrace(Exception e, int skipFrames, bool fNeedFileInfo) + public StackTrace(Exception e, int skipFrames, bool needFileInfo) { - throw new NotImplementedException(); + InitializeForExceptionFrameIndex(e, skipFrames, needFileInfo); } /// @@ -92,7 +100,62 @@ namespace System.Diagnostics /// public StackTrace(StackFrame frame) { - throw new NotImplementedException(); + _stackFrames = new StackFrame[] { frame }; + } + + /// + /// Construct a stack trace based on a subset of a precomputed array of IP addresses. + /// + /// Array of IP addresses to use as the stack trace + /// Starting index in the array to use + /// Ending index in the array (one plus the last element) + /// True when source file / line information is requested + internal StackTrace(IntPtr[] ipAddresses, int startIndex, int endIndex, bool needFileInfo) + { + InitializeForIpAddressArray(ipAddresses, startIndex, endIndex, needFileInfo); + } + + /// + /// Initialize the stack trace based on current thread and given initial frame index. + /// + private void InitializeForThreadFrameIndex(int skipFrames, bool needFileInfo) + { + int frameCount = -RuntimeImports.RhGetCurrentThreadStackTrace(Array.Empty()); + Debug.Assert(frameCount >= 0); + IntPtr[] stackTrace = new IntPtr[frameCount]; + int trueFrameCount = RuntimeImports.RhGetCurrentThreadStackTrace(stackTrace); + Debug.Assert(trueFrameCount == frameCount); + InitializeForIpAddressArray(stackTrace, skipFrames, frameCount, needFileInfo); + } + + /// + /// Initialize the stack trace based on a given exception and initial frame index. + /// + private void InitializeForExceptionFrameIndex(Exception exception, int skipFrames, bool needFileInfo) + { + if (exception == null) + { + throw new ArgumentNullException(nameof(exception)); + } + IntPtr[] stackIPs = exception.GetStackIPs(); + InitializeForIpAddressArray(stackIPs, skipFrames, stackIPs.Length, needFileInfo); + } + + /// + /// Initialize the stack trace based on a given array of IP addresses. + /// + private void InitializeForIpAddressArray(IntPtr[] ipAddresses, int skipFrames, int endFrameIndex, bool needFileInfo) + { + int frameCount = (skipFrames < endFrameIndex ? endFrameIndex - skipFrames : 0); + if (frameCount > 0) + { + _stackFrames = new StackFrame[frameCount]; + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) + { + IntPtr ipAddress = ipAddresses[frameIndex + skipFrames]; + _stackFrames[frameIndex] = new StackFrame(ipAddress, needFileInfo); + } + } } /// @@ -100,7 +163,7 @@ namespace System.Diagnostics /// public virtual int FrameCount { - get { throw new NotImplementedException(); } + get { return _stackFrames != null ? _stackFrames.Length : 0; } } /// @@ -109,7 +172,11 @@ namespace System.Diagnostics /// public virtual StackFrame GetFrame(int index) { - throw new NotImplementedException(); + if (_stackFrames != null && index >= 0 && index < _stackFrames.Length) + { + return _stackFrames[index]; + } + return null; } /// @@ -120,7 +187,14 @@ namespace System.Diagnostics /// public virtual StackFrame[] GetFrames() { - throw new NotImplementedException(); + if (_stackFrames == null) + { + return null; + } + + StackFrame[] array = new StackFrame[_stackFrames.Length]; + Array.Copy(_stackFrames, array, _stackFrames.Length); + return array; } /// @@ -128,7 +202,17 @@ namespace System.Diagnostics /// public override String ToString() { - throw new NotImplementedException(); + if (_stackFrames == null) + { + return ""; + } + + StringBuilder builder = new StringBuilder(); + foreach (StackFrame frame in _stackFrames) + { + frame.AppendToStackTrace(builder); + } + return builder.ToString(); } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/Tracing/UnsafeNativeMethods.cs b/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/Tracing/UnsafeNativeMethods.cs index e4a08a8c73..3d2e41eba6 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/Tracing/UnsafeNativeMethods.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Diagnostics/Tracing/UnsafeNativeMethods.cs @@ -72,7 +72,7 @@ namespace Microsoft.Win32 [In] ref Guid providerId, [In]EtwEnableCallback enableCallback, [In]void* callbackContext, - [In] ref long registrationHandle + [In][Out] ref long registrationHandle ); // diff --git a/external/corert/src/System.Private.CoreLib/src/System/Double.cs b/external/corert/src/System.Private.CoreLib/src/System/Double.cs deleted file mode 100644 index 006d991e16..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/Double.cs +++ /dev/null @@ -1,388 +0,0 @@ -// 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. - -/*============================================================ -** -** -** -** Purpose: A representation of an IEEE double precision -** floating point number. -** -** -===========================================================*/ - -using System.Globalization; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; -using System.Diagnostics.Contracts; - -namespace System -{ - [StructLayout(LayoutKind.Sequential)] - public struct Double : IComparable, IFormattable, IComparable, IEquatable, IConvertible - { - private double _value; - - // - // Public Constants - // - public const double MinValue = -1.7976931348623157E+308; - public const double MaxValue = 1.7976931348623157E+308; - - // Note Epsilon should be a double whose hex representation is 0x1 - // on little endian machines. - public const double Epsilon = 4.9406564584124654E-324; - public const double NegativeInfinity = (double)-1.0 / (double)(0.0); - public const double PositiveInfinity = (double)1.0 / (double)(0.0); - public const double NaN = (double)0.0 / (double)0.0; - - // 0x8000000000000000 is exactly same as -0.0. We use this explicit definition to avoid the confusion between 0.0 and -0.0. - internal static double NegativeZero = Int64BitsToDouble(unchecked((long)0x8000000000000000)); - - private static unsafe double Int64BitsToDouble(long value) - { - return *((double*)&value); - } - - [Pure] - public static unsafe bool IsInfinity(double d) - { - return (*(long*)(&d) & 0x7FFFFFFFFFFFFFFF) == 0x7FF0000000000000; - } - - [Pure] - public static bool IsPositiveInfinity(double d) - { - //Jit will generate inlineable code with this - if (d == double.PositiveInfinity) - { - return true; - } - else - { - return false; - } - } - - [Pure] - public static bool IsNegativeInfinity(double d) - { - //Jit will generate inlineable code with this - if (d == double.NegativeInfinity) - { - return true; - } - else - { - return false; - } - } - - - [Pure] - internal static unsafe bool IsNegative(double d) - { - return (*(UInt64*)(&d) & 0x8000000000000000) == 0x8000000000000000; - } - - - [Pure] - public static unsafe bool IsNaN(double d) - { - return (*(UInt64*)(&d) & 0x7FFFFFFFFFFFFFFFL) > 0x7FF0000000000000L; - } - - // Compares this object to another object, returning an instance of System.Relation. - // Null is considered less than any instance. - // - // If object is not of type Double, this method throws an ArgumentException. - // - // Returns a value less than zero if this object - // - public int CompareTo(Object value) - { - if (value == null) - { - return 1; - } - if (value is Double) - { - double d = (double)value; - if (_value < d) return -1; - if (_value > d) return 1; - if (_value == d) return 0; - - // At least one of the values is NaN. - if (IsNaN(_value)) - return (IsNaN(d) ? 0 : -1); - else - return 1; - } - throw new ArgumentException(SR.Arg_MustBeDouble); - } - - public int CompareTo(Double value) - { - if (_value < value) return -1; - if (_value > value) return 1; - if (_value == value) return 0; - - // At least one of the values is NaN. - if (IsNaN(_value)) - return (IsNaN(value) ? 0 : -1); - else - return 1; - } - - // True if obj is another Double with the same value as the current instance. This is - // a method of object equality, that only returns true if obj is also a double. - public override bool Equals(Object obj) - { - if (!(obj is Double)) - { - return false; - } - double temp = ((Double)obj)._value; - // This code below is written this way for performance reasons i.e the != and == check is intentional. - if (temp == _value) - { - return true; - } - return IsNaN(temp) && IsNaN(_value); - } - - [NonVersionable] - public static bool operator ==(Double left, Double right) - { - return left == right; - } - - [NonVersionable] - public static bool operator !=(Double left, Double right) - { - return left != right; - } - - [NonVersionable] - public static bool operator <(Double left, Double right) - { - return left < right; - } - - [NonVersionable] - public static bool operator >(Double left, Double right) - { - return left > right; - } - - [NonVersionable] - public static bool operator <=(Double left, Double right) - { - return left <= right; - } - - [NonVersionable] - public static bool operator >=(Double left, Double right) - { - return left >= right; - } - - public bool Equals(Double obj) - { - if (obj == _value) - { - return true; - } - return IsNaN(obj) && IsNaN(_value); - } - - //The hashcode for a double is the absolute value of the integer representation - //of that double. - // - public unsafe override int GetHashCode() - { - double d = _value; - if (d == 0) - { - // Ensure that 0 and -0 have the same hash code - return 0; - } - long value = *(long*)(&d); - return unchecked((int)value) ^ ((int)(value >> 32)); - } - - public override String ToString() - { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatDouble(_value, null, null); - } - - public String ToString(String format) - { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatDouble(_value, format, null); - } - - public String ToString(IFormatProvider provider) - { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatDouble(_value, null, provider); - } - - public String ToString(String format, IFormatProvider provider) - { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatDouble(_value, format, provider); - } - - public static double Parse(String s) - { - return Parse(s, NumberStyles.Float | NumberStyles.AllowThousands, null); - } - - public static double Parse(String s, NumberStyles style) - { - Decimal.ValidateParseStyleFloatingPoint(style); - return Parse(s, style, null); - } - - public static double Parse(String s, IFormatProvider provider) - { - return Parse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider); - } - - public static double Parse(String s, NumberStyles style, IFormatProvider provider) - { - Decimal.ValidateParseStyleFloatingPoint(style); - return FormatProvider.ParseDouble(s, style, provider); - } - - // Parses a double from a String in the given style. If - // a NumberFormatInfo isn't specified, the current culture's - // NumberFormatInfo is assumed. - // - // This method will not throw an OverflowException, but will return - // PositiveInfinity or NegativeInfinity for a number that is too - // large or too small. - // - public static bool TryParse(String s, out double result) - { - return TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, null, out result); - } - - public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out double result) - { - Decimal.ValidateParseStyleFloatingPoint(style); - if (s == null) - { - result = 0; - return false; - } - bool success = FormatProvider.TryParseDouble(s, style, provider, out result); - if (!success) - { - String sTrim = s.Trim(); - if (FormatProvider.IsPositiveInfinity(sTrim, provider)) - { - result = PositiveInfinity; - } - else if (FormatProvider.IsNegativeInfinity(sTrim, provider)) - { - result = NegativeInfinity; - } - else if (FormatProvider.IsNaNSymbol(sTrim, provider)) - { - result = NaN; - } - else - return false; // We really failed - } - return true; - } - - // - // IConvertible implementation - // - - public TypeCode GetTypeCode() - { - return TypeCode.Double; - } - - bool IConvertible.ToBoolean(IFormatProvider provider) - { - return Convert.ToBoolean(_value); - } - - char IConvertible.ToChar(IFormatProvider provider) - { - throw new InvalidCastException(String.Format(SR.InvalidCast_FromTo, "Double", "Char")); - } - - sbyte IConvertible.ToSByte(IFormatProvider provider) - { - return Convert.ToSByte(_value); - } - - byte IConvertible.ToByte(IFormatProvider provider) - { - return Convert.ToByte(_value); - } - - short IConvertible.ToInt16(IFormatProvider provider) - { - return Convert.ToInt16(_value); - } - - ushort IConvertible.ToUInt16(IFormatProvider provider) - { - return Convert.ToUInt16(_value); - } - - int IConvertible.ToInt32(IFormatProvider provider) - { - return Convert.ToInt32(_value); - } - - uint IConvertible.ToUInt32(IFormatProvider provider) - { - return Convert.ToUInt32(_value); - } - - long IConvertible.ToInt64(IFormatProvider provider) - { - return Convert.ToInt64(_value); - } - - ulong IConvertible.ToUInt64(IFormatProvider provider) - { - return Convert.ToUInt64(_value); - } - - float IConvertible.ToSingle(IFormatProvider provider) - { - return Convert.ToSingle(_value); - } - - double IConvertible.ToDouble(IFormatProvider provider) - { - return _value; - } - - Decimal IConvertible.ToDecimal(IFormatProvider provider) - { - return Convert.ToDecimal(_value); - } - - DateTime IConvertible.ToDateTime(IFormatProvider provider) - { - throw new InvalidCastException(String.Format(SR.InvalidCast_FromTo, "Double", "DateTime")); - } - - Object IConvertible.ToType(Type type, IFormatProvider provider) - { - return Convert.DefaultToType((IConvertible)this, type, provider); - } - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/System/EETypePtr.cs b/external/corert/src/System.Private.CoreLib/src/System/EETypePtr.cs index 23d3a72ed0..fa5abb6b09 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/EETypePtr.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/EETypePtr.cs @@ -90,6 +90,19 @@ namespace System return RuntimeImports.AreTypesEquivalent(this, other); } + // + // An even faster version of FastEquals that only checks if two EEType pointers are identical. + // Note: this method might return false for cases where FastEquals would return true. + // Only use if you know what you're doing. + // + internal bool FastEqualsUnreliable(EETypePtr other) + { + Debug.Assert(!this.IsNull); + Debug.Assert(!other.IsNull); + + return this.RawValue == other.RawValue; + } + // Caution: You cannot safely compare RawValue's as RH does NOT unify EETypes. Use the == or Equals() methods exposed by EETypePtr itself. internal IntPtr RawValue { @@ -169,6 +182,10 @@ namespace System } } + /// + /// WARNING: Never call unless the EEType came from an instanced object. Nested enums can be open generics (typeof(Outer<>).NestedEnum) + /// and this helper has undefined behavior when passed such as a enum. + /// internal bool IsEnum { get diff --git a/external/corert/src/System.Private.CoreLib/src/System/Enum.cs b/external/corert/src/System.Private.CoreLib/src/System/Enum.cs index 5e4adf1b52..e863605c97 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Enum.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Enum.cs @@ -2,20 +2,21 @@ // 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.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Globalization; using System.Runtime; using System.Runtime.CompilerServices; using System.Text; +using Internal.Runtime.CompilerServices; using Internal.Runtime.Augments; -using Internal.Reflection.Core.NonPortable; +using Internal.Reflection.Augments; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public abstract class Enum : ValueType, IComparable, IFormattable, IConvertible { public int CompareTo(Object target) @@ -141,16 +142,20 @@ namespace System if (enumType == null) throw new ArgumentNullException(nameof(enumType)); - EnumInfo enumInfo = GetEnumInfo(enumType); + if (!enumType.IsEnum) + throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType)); if (value == null) throw new ArgumentNullException(nameof(value)); if (format == null) throw new ArgumentNullException(nameof(format)); - Contract.EndContractBlock(); - if (value.EETypePtr.IsEnum) + if (!enumType.IsRuntimeImplemented()) + throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType)); + + EnumInfo enumInfo = GetEnumInfo(enumType); + if (value is Enum) { EETypePtr enumTypeEEType; if ((!enumType.TryGetEEType(out enumTypeEEType)) || enumTypeEEType != value.EETypePtr) @@ -170,7 +175,7 @@ namespace System ulong rawValue; if (!TryGetUnboxedValueOfEnumOrInteger(value, out rawValue)) { - Debug.Assert(false, "Caller was expected to do enough validation to avoid reaching this."); + Debug.Fail("Caller was expected to do enough validation to avoid reaching this."); throw new ArgumentException(); } @@ -283,7 +288,7 @@ namespace System } default: - Debug.Assert(false, "Invalid Object type in Format"); + Debug.Fail("Invalid Object type in Format"); throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType); } } @@ -368,7 +373,7 @@ namespace System } default: - Debug.Assert(false, "Invalid Object type in Format"); + Debug.Fail("Invalid Object type in Format"); throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType); } } @@ -493,6 +498,9 @@ namespace System // For desktop compatibility, do not bounce an incoming integer that's the wrong size. // Do a value-preserving cast of both it and the enum values and do a 64-bit compare. + if (!enumType.IsEnum) + throw new ArgumentException(SR.Arg_MustBeEnum); + EnumInfo enumInfo = GetEnumInfo(enumType); String nameOrNull = GetNameIfAny(enumInfo, rawValue); return nameOrNull; @@ -506,6 +514,9 @@ namespace System if (!enumType.IsRuntimeImplemented()) return enumType.GetEnumNames(); + if (!enumType.IsEnum) + throw new ArgumentException(SR.Arg_MustBeEnum); + KeyValuePair[] namesAndValues = GetEnumInfo(enumType).NamesAndValues; String[] names = new String[namesAndValues.Length]; for (int i = 0; i < namesAndValues.Length; i++) @@ -518,55 +529,38 @@ namespace System if (enumType == null) throw new ArgumentNullException(nameof(enumType)); - RuntimeTypeHandle runtimeTypeHandle = enumType.TypeHandle; - EETypePtr eeType = runtimeTypeHandle.ToEETypePtr(); - if (!eeType.IsEnum) + if (!enumType.IsRuntimeImplemented()) + return enumType.GetEnumUnderlyingType(); + + if (!enumType.IsEnum) throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType)); - switch (eeType.CorElementType) - { - case RuntimeImports.RhCorElementType.ELEMENT_TYPE_BOOLEAN: - return CommonRuntimeTypes.Boolean; - case RuntimeImports.RhCorElementType.ELEMENT_TYPE_CHAR: - return CommonRuntimeTypes.Char; - case RuntimeImports.RhCorElementType.ELEMENT_TYPE_I1: - return CommonRuntimeTypes.SByte; - case RuntimeImports.RhCorElementType.ELEMENT_TYPE_U1: - return CommonRuntimeTypes.Byte; - case RuntimeImports.RhCorElementType.ELEMENT_TYPE_I2: - return CommonRuntimeTypes.Int16; - case RuntimeImports.RhCorElementType.ELEMENT_TYPE_U2: - return CommonRuntimeTypes.UInt16; - case RuntimeImports.RhCorElementType.ELEMENT_TYPE_I4: - return CommonRuntimeTypes.Int32; - case RuntimeImports.RhCorElementType.ELEMENT_TYPE_U4: - return CommonRuntimeTypes.UInt32; - case RuntimeImports.RhCorElementType.ELEMENT_TYPE_I8: - return CommonRuntimeTypes.Int64; - case RuntimeImports.RhCorElementType.ELEMENT_TYPE_U8: - return CommonRuntimeTypes.UInt64; - default: - throw new ArgumentException(); - } + return GetEnumInfo(enumType).UnderlyingType; } public static Array GetValues(Type enumType) { if (enumType == null) throw new ArgumentNullException(nameof(enumType)); + + if (!enumType.IsRuntimeImplemented()) + return enumType.GetEnumValues(); + + if (!enumType.IsEnum) + throw new ArgumentException(SR.Arg_MustBeEnum); + Array values = GetEnumInfo(enumType).Values; int count = values.Length; - EETypePtr enumArrayType = enumType.MakeArrayType().TypeHandle.ToEETypePtr(); - Array result = RuntimeImports.RhNewArray(enumArrayType, count); + Array result = Array.CreateInstance(enumType, count); Array.CopyImplValueTypeArrayNoInnerGcRefs(values, 0, result, 0, count); return result; } + [Intrinsic] public Boolean HasFlag(Enum flag) { if (flag == null) throw new ArgumentNullException(nameof(flag)); - Contract.EndContractBlock(); if (!(this.EETypePtr == flag.EETypePtr)) throw new ArgumentException(SR.Format(SR.Argument_EnumTypeDoesNotMatch, flag.GetType(), this.GetType())); @@ -603,6 +597,9 @@ namespace System if (value.EETypePtr == EETypePtr.EETypePtrOf()) { + if (!enumType.IsEnum) + throw new ArgumentException(SR.Arg_MustBeEnum); + EnumInfo enumInfo = GetEnumInfo(enumType); foreach (KeyValuePair kv in enumInfo.NamesAndValues) { @@ -623,20 +620,27 @@ namespace System } EnumInfo enumInfo = null; - if (value.EETypePtr.IsEnum) + if (value is Enum) { if (!ValueTypeMatchesEnumType(enumType, value)) throw new ArgumentException(SR.Format(SR.Arg_EnumAndObjectMustBeSameType, value.GetType(), enumType)); } else { + if (!enumType.IsEnum) + throw new ArgumentException(SR.Arg_MustBeEnum); + enumInfo = GetEnumInfo(enumType); if (!(enumInfo.UnderlyingType.TypeHandle.ToEETypePtr() == value.EETypePtr)) throw new ArgumentException(SR.Format(SR.Arg_EnumUnderlyingTypeAndObjectMustBeSameType, value.GetType(), enumInfo.UnderlyingType)); } if (enumInfo == null) + { + if (!enumType.IsEnum) + throw new ArgumentException(SR.Arg_MustBeEnum); enumInfo = GetEnumInfo(enumType); + } String nameOrNull = GetNameIfAny(enumInfo, rawValue); return nameOrNull != null; } @@ -701,26 +705,33 @@ namespace System if (enumType == null) throw new ArgumentNullException(nameof(enumType)); + if (!enumType.IsEnum) + throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType)); + if (!enumType.IsRuntimeImplemented()) throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType)); - EETypePtr enumEEType = enumType.TypeHandle.ToEETypePtr(); - if (!enumEEType.IsEnum) - throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType)); + // Check for the unfortunate "typeof(Outer<>).InnerEnum" corner case. + if (enumType.ContainsGenericParameters) + throw new InvalidOperationException(SR.Format(SR.Arg_OpenType, enumType.ToString())); - unsafe - { - byte* pValue = (byte*)&value; - AdjustForEndianness(ref pValue, enumEEType); - return RuntimeImports.RhBox(enumEEType, pValue); - } + EETypePtr enumEEType = enumType.TypeHandle.ToEETypePtr(); + return ToObject(enumEEType, value); + } + + internal unsafe static object ToObject(EETypePtr enumEEType, long value) + { + Debug.Assert(enumEEType.IsEnum); + + byte* pValue = (byte*)&value; + AdjustForEndianness(ref pValue, enumEEType); + return RuntimeImports.RhBox(enumEEType, pValue); } public static Object ToObject(Type enumType, Object value) { if (value == null) throw new ArgumentNullException(nameof(value)); - Contract.EndContractBlock(); // Delegate rest of error checking to the other functions TypeCode typeCode = Convert.GetTypeCode(value); @@ -810,7 +821,7 @@ namespace System if (format == null || format.Length == 0) format = "G"; - EnumInfo enumInfo = GetEnumInfoIfAvailable(this.GetType()); + EnumInfo enumInfo = GetEnumInfo(this.GetType()); // Project N port note: If Reflection info isn't available, fallback to ToString() which will substitute a numeric value for the "correct" output. // This scenario has been hit frequently when throwing exceptions formatted with error strings containing enum substitations. @@ -833,27 +844,13 @@ namespace System return ToString(); } - // - // Note: this helper also checks if the enumType is in fact an Enum and throws an user-visible ArgumentException if it's not. - // private static EnumInfo GetEnumInfo(Type enumType) { - EnumInfo enumInfo = GetEnumInfoIfAvailable(enumType); - if (enumInfo == null) - throw RuntimeAugments.Callbacks.CreateMissingMetadataException(enumType); - return enumInfo; - } + Debug.Assert(enumType != null); + Debug.Assert(enumType.IsRuntimeImplemented()); + Debug.Assert(enumType.IsEnum); - // - // Note: this helper also checks if the enumType is in fact an Enum and throws an user-visible ArgumentException if it's not. - // - private static EnumInfo GetEnumInfoIfAvailable(Type enumType) - { - RuntimeTypeHandle runtimeTypeHandle = enumType.TypeHandle; - if (!runtimeTypeHandle.ToEETypePtr().IsEnum) - throw new ArgumentException(SR.Arg_MustBeEnum); - - return s_enumInfoCache.GetOrAdd(new TypeUnificationKey(enumType)); + return ReflectionAugments.ReflectionCoreCallbacks.GetEnumInfo(enumType); } // @@ -975,6 +972,9 @@ namespace System if (!enumType.IsRuntimeImplemented()) throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType)); + if (!enumType.IsEnum) + throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType)); + if (value == null) { exception = new ArgumentNullException(nameof(value)); @@ -996,10 +996,11 @@ namespace System return false; } - EETypePtr enumEEType = enumType.TypeHandle.ToEETypePtr(); - if (!enumEEType.IsEnum) - throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType)); + // Check for the unfortunate "typeof(Outer<>).InnerEnum" corner case. + if (enumType.ContainsGenericParameters) + throw new InvalidOperationException(SR.Format(SR.Arg_OpenType, enumType.ToString())); + EETypePtr enumEEType = enumType.TypeHandle.ToEETypePtr(); if (TryParseAsInteger(enumEEType, value, firstNonWhitespaceIndex, out result)) return true; @@ -1010,9 +1011,7 @@ namespace System } // Parse as string. Now (and only now) do we look for metadata information. - EnumInfo enumInfo = RuntimeAugments.Callbacks.GetEnumInfoIfAvailable(enumType); - if (enumInfo == null) - throw RuntimeAugments.Callbacks.CreateMissingMetadataException(enumType); + EnumInfo enumInfo = GetEnumInfo(enumType); ulong v = 0; // Port note: The docs are silent on how multiple matches are resolved when doing case-insensitive parses. @@ -1268,76 +1267,37 @@ namespace System } } - - private sealed class EnumInfoUnifier : ConcurrentUnifierW - { - protected override EnumInfo Factory(TypeUnificationKey key) - { - return RuntimeAugments.Callbacks.GetEnumInfoIfAvailable(key.Type); - } - } - - private static EnumInfoUnifier s_enumInfoCache = new EnumInfoUnifier(); - - #region IConvertible public TypeCode GetTypeCode() { - Type enumType = this.GetType(); - Type underlyingType = GetUnderlyingType(enumType); + RuntimeImports.RhCorElementType corElementType = this.EETypePtr.CorElementType; - if (underlyingType == typeof(Int32)) + switch (corElementType) { - return TypeCode.Int32; + case RuntimeImports.RhCorElementType.ELEMENT_TYPE_I4: + return TypeCode.Int32; + case RuntimeImports.RhCorElementType.ELEMENT_TYPE_I1: + return TypeCode.SByte; + case RuntimeImports.RhCorElementType.ELEMENT_TYPE_I2: + return TypeCode.Int16; + case RuntimeImports.RhCorElementType.ELEMENT_TYPE_I8: + return TypeCode.Int64; + case RuntimeImports.RhCorElementType.ELEMENT_TYPE_U4: + return TypeCode.UInt32; + case RuntimeImports.RhCorElementType.ELEMENT_TYPE_U1: + return TypeCode.Byte; + case RuntimeImports.RhCorElementType.ELEMENT_TYPE_U2: + return TypeCode.UInt16; + case RuntimeImports.RhCorElementType.ELEMENT_TYPE_U8: + return TypeCode.UInt64; + case RuntimeImports.RhCorElementType.ELEMENT_TYPE_BOOLEAN: + return TypeCode.Boolean; + case RuntimeImports.RhCorElementType.ELEMENT_TYPE_CHAR: + return TypeCode.Char; + default: + Debug.Fail("Unknown underlying type."); + throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType); } - - if (underlyingType == typeof(sbyte)) - { - return TypeCode.SByte; - } - - if (underlyingType == typeof(Int16)) - { - return TypeCode.Int16; - } - - if (underlyingType == typeof(Int64)) - { - return TypeCode.Int64; - } - - if (underlyingType == typeof(UInt32)) - { - return TypeCode.UInt32; - } - - if (underlyingType == typeof(byte)) - { - return TypeCode.Byte; - } - - if (underlyingType == typeof(UInt16)) - { - return TypeCode.UInt16; - } - - if (underlyingType == typeof(UInt64)) - { - return TypeCode.UInt64; - } - - if (underlyingType == typeof(Boolean)) - { - return TypeCode.Boolean; - } - - if (underlyingType == typeof(Char)) - { - return TypeCode.Char; - } - - Debug.Assert(false, "Unknown underlying type."); - throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType); } bool IConvertible.ToBoolean(IFormatProvider provider) diff --git a/external/corert/src/System.Private.CoreLib/src/System/Environment.Unix.cs b/external/corert/src/System.Private.CoreLib/src/System/Environment.Unix.cs index c3d24b9214..c45d54bd8f 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Environment.Unix.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Environment.Unix.cs @@ -20,8 +20,6 @@ namespace System } } - public static int ProcessorCount => (int)Interop.Sys.SysConf(Interop.Sys.SysConfName._SC_NPROCESSORS_ONLN); - private static int ComputeExecutionId() { int executionId = Interop.Sys.SchedGetCpu(); @@ -33,5 +31,13 @@ namespace System return executionId; } + +#if DEBUG + [Obsolete("ExpandEnvironmentVariables() only called on Windows so not implemented on Unix.")] + public static string ExpandEnvironmentVariables(string name) + { + throw new PlatformNotSupportedException("ExpandEnvironmentVariables() only called on Windows so not implemented on Unix."); + } +#endif } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Environment.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/Environment.Windows.cs index 9ea39a7561..f5fca35ab4 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Environment.Windows.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Environment.Windows.cs @@ -2,6 +2,8 @@ // 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.Buffers; + namespace System { internal static partial class Environment @@ -10,17 +12,29 @@ namespace System internal static long TickCount64 => (long)Interop.mincore.GetTickCount64(); - public static int ProcessorCount + private static int ComputeExecutionId() => (int)Interop.mincore.GetCurrentProcessorNumber(); + + public static string ExpandEnvironmentVariables(string name) { - get + if (name == null) + throw new ArgumentNullException(nameof(name)); + + int currentSize = 128; + for (;;) { - // @TODO: can we finally fix this to return the actual number of processors when there are >64? - Interop.mincore.SYSTEM_INFO info; - Interop.mincore.GetNativeSystemInfo(out info); - return (int)info.dwNumberOfProcessors; + char[] buffer = ArrayPool.Shared.Rent(currentSize); + + int actualSize = Interop.Kernel32.ExpandEnvironmentStrings(name, buffer, buffer.Length); + if (actualSize <= buffer.Length) + { + string result = (actualSize != 0) ? new string(buffer, 0, actualSize) : null; + ArrayPool.Shared.Return(buffer); + return result; + } + + ArrayPool.Shared.Return(buffer); + currentSize = actualSize; } } - - private static int ComputeExecutionId() => (int)Interop.mincore.GetCurrentProcessorNumber(); } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Environment.cs b/external/corert/src/System.Private.CoreLib/src/System/Environment.cs index 7ee9f2ddad..9835a2592e 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Environment.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Environment.cs @@ -12,13 +12,7 @@ ** ============================================================*/ -using System.Runtime; using System.Diagnostics; -using System.Globalization; -using System.Collections; -using System.Text; -using System.Runtime.InteropServices; -using Microsoft.Win32; using System.Runtime.CompilerServices; using System.Threading; @@ -149,5 +143,13 @@ namespace System return EnvironmentAugments.StackTrace; } } + + public static int ProcessorCount + { + get + { + return Runtime.RuntimeImports.RhGetProcessCpuCount(); + } + } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Exception.cs b/external/corert/src/System.Private.CoreLib/src/System/Exception.cs index a949110e6c..6e4aba99c0 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Exception.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Exception.cs @@ -14,12 +14,14 @@ using Internal.Diagnostics; namespace System { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class Exception : ISerializable { private void Init() { _message = null; - HResult = __HResults.COR_E_EXCEPTION; + HResult = HResults.COR_E_EXCEPTION; } public Exception() @@ -47,7 +49,18 @@ namespace System protected Exception(SerializationInfo info, StreamingContext context) { - throw new NotImplementedException(); + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + _message = info.GetString("Message"); // Do not rename (binary serialization) + _data = (IDictionary)(info.GetValueNoThrow("Data", typeof(IDictionary))); // Do not rename (binary serialization) + _innerException = (Exception)(info.GetValue("InnerException", typeof(Exception))); // Do not rename (binary serialization) + _helpURL = info.GetString("HelpURL"); // Do not rename (binary serialization) + _stackTrace = info.GetString("StackTraceString"); + HResult = info.GetInt32("HResult"); // Do not rename (binary serialization) + _source = info.GetString("Source"); // Do not rename (binary serialization) } public virtual String Message @@ -73,7 +86,7 @@ namespace System get { if (_data == null) - _data = new LowLevelListDictionary(); + _data = new ListDictionaryInternal(); return _data; } @@ -212,7 +225,33 @@ namespace System public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { - throw new NotImplementedException(); + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + if (_source == null) + { + _source = Source; // Set the Source information correctly before serialization + } + + if (_message == null) + { + _message = Message; // Set the Message information correctly before serialization + } + + info.AddValue("ClassName", GetClassName(), typeof(String)); // Do not rename (binary serialization) + info.AddValue("Message", _message, typeof(String)); // Do not rename (binary serialization) + info.AddValue("Data", _data, typeof(IDictionary)); // Do not rename (binary serialization) + info.AddValue("InnerException", _innerException, typeof(Exception)); // Do not rename (binary serialization) + info.AddValue("HelpURL", _helpURL, typeof(String)); // Do not rename (binary serialization) + info.AddValue("StackTraceString", StackTrace, typeof(String)); // Do not rename (binary serialization) + info.AddValue("RemoteStackTraceString", null, typeof(String)); // Do not rename (binary serialization) + info.AddValue("RemoteStackIndex", 0, typeof(Int32)); // Do not rename (binary serialization) + info.AddValue("ExceptionMethod", null, typeof(String)); // Do not rename (binary serialization) + info.AddValue("HResult", HResult); // Do not rename (binary serialization) + info.AddValue("Source", _source, typeof(String)); // Do not rename (binary serialization) + info.AddValue("WatsonBuckets", null, typeof(String)); // Do not rename (binary serialization) } private string GetStackTrace(bool needFileInfo) @@ -295,6 +334,9 @@ namespace System private int _HResult; // HResult + // To maintain compatibility across runtimes, if this object was deserialized, it will store its stack trace as a string + private String _stackTrace; + public int HResult { get { return _HResult; } @@ -307,6 +349,9 @@ namespace System { get { + if (_stackTrace != null) + return _stackTrace; + if (!HasBeenThrown) return null; @@ -407,7 +452,7 @@ namespace System // CORERT-TODO: RhpEtwExceptionThrown // https://github.com/dotnet/corert/issues/2457 -#if !CORERT +#if PROJECTN if (isFirstFrame) { string typeName = !outOfMemory ? ex.GetType().ToString() : "System.OutOfMemoryException"; diff --git a/external/corert/src/System.Private.CoreLib/src/System/GC.cs b/external/corert/src/System.Private.CoreLib/src/System/GC.cs index a4ec1f6f62..053785d750 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/GC.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/GC.cs @@ -12,7 +12,6 @@ using System.Runtime; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Security; using Internal.Runtime.Augments; @@ -265,7 +264,7 @@ namespace System /// of memory is available. /// True if the disallowing of garbage collection was successful, False otherwise /// If the amount of memory requested - /// is too large for the GC to accomodate + /// is too large for the GC to accommodate /// If the GC is already in a NoGCRegion public static bool TryStartNoGCRegion(long totalSize) { @@ -324,24 +323,41 @@ namespace System private static bool StartNoGCRegionWorker(long totalSize, bool hasLohSize, long lohSize, bool disallowFullBlockingGC) { - StartNoGCRegionStatus status = - (StartNoGCRegionStatus)RuntimeImports.RhStartNoGCRegion(totalSize, hasLohSize, lohSize, disallowFullBlockingGC); - if (status == StartNoGCRegionStatus.AmountTooLarge) + if (totalSize <= 0) { throw new ArgumentOutOfRangeException( nameof(totalSize), - SR.ArgumentOutOfRangeException_NoGCRegionSizeTooLarge); - } - else if (status == StartNoGCRegionStatus.AlreadyInProgress) - { - throw new InvalidOperationException( - SR.InvalidOperationException_AlreadyInNoGCRegion); - } - else if (status == StartNoGCRegionStatus.NotEnoughMemory) - { - return false; + SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(totalSize))); } + if (hasLohSize) + { + if (lohSize <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(lohSize), + SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(lohSize))); + } + + if (lohSize > totalSize) + { + throw new ArgumentOutOfRangeException(nameof(lohSize), SR.ArgumentOutOfRange_NoGCLohSizeGreaterTotalSize); + } + } + + StartNoGCRegionStatus status = + (StartNoGCRegionStatus)RuntimeImports.RhStartNoGCRegion(totalSize, hasLohSize, lohSize, disallowFullBlockingGC); + switch (status) + { + case StartNoGCRegionStatus.NotEnoughMemory: + return false; + case StartNoGCRegionStatus.AlreadyInProgress: + throw new InvalidOperationException(SR.InvalidOperationException_AlreadyInNoGCRegion); + case StartNoGCRegionStatus.AmountTooLarge: + throw new ArgumentOutOfRangeException(nameof(totalSize), SR.ArgumentOutOfRangeException_NoGCRegionSizeTooLarge); + } + + Debug.Assert(status == StartNoGCRegionStatus.Succeeded); return true; } @@ -610,5 +626,10 @@ namespace System return size; } + + public static long GetAllocatedBytesForCurrentThread() + { + return RuntimeImports.RhGetAllocatedBytesForCurrentThread(); + } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CalendarData.Dummy.cs b/external/corert/src/System.Private.CoreLib/src/System/Globalization/CalendarData.Dummy.cs index 7eee3414d1..7d3778d3ef 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CalendarData.Dummy.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Globalization/CalendarData.Dummy.cs @@ -5,7 +5,6 @@ using System; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; -using System.Diagnostics.Contracts; using System.Collections.Generic; namespace System.Globalization diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.Dummy.cs b/external/corert/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.Dummy.cs index b971ce5766..8a7678ecaf 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.Dummy.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.Dummy.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; namespace System.Globalization { @@ -14,7 +13,7 @@ namespace System.Globalization _sortName = culture.SortName; } - internal static unsafe int IndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase) + internal static unsafe int IndexOfOrdinalCore(string source, string value, int startIndex, int count, bool ignoreCase) { fixed (char* pSource = source) fixed (char* pValue = value) { @@ -28,7 +27,7 @@ namespace System.Globalization } } - internal static unsafe int LastIndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase) + internal static unsafe int LastIndexOfOrdinalCore(string source, string value, int startIndex, int count, bool ignoreCase) { fixed (char* pSource = source) fixed (char* pValue = value) { @@ -400,7 +399,6 @@ namespace System.Globalization private unsafe SortKey CreateSortKey(String source, CompareOptions options) { if (source == null) { throw new ArgumentNullException(nameof(source)); } - Contract.EndContractBlock(); if ((options & ValidSortkeyCtorMaskOffFlags) != 0) { diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.Unix.cs b/external/corert/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.Unix.cs index 659e39e94d..cbf3fb555c 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.Unix.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.Unix.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security; @@ -21,21 +20,31 @@ namespace System.Globalization private void InitSort(CultureInfo culture) { _sortName = culture.SortName; - Interop.GlobalizationInterop.ResultCode resultCode = Interop.GlobalizationInterop.GetSortHandle(GetNullTerminatedUtf8String(_sortName), out _sortHandle); - if (resultCode != Interop.GlobalizationInterop.ResultCode.Success) + + if (_invariantMode) { - _sortHandle.Dispose(); - - if (resultCode == Interop.GlobalizationInterop.ResultCode.OutOfMemory) - throw new OutOfMemoryException(); - - throw new ExternalException(SR.Arg_ExternalException); + _isAsciiEqualityOrdinal = true; + } + else + { + Interop.GlobalizationInterop.ResultCode resultCode = Interop.GlobalizationInterop.GetSortHandle(GetNullTerminatedUtf8String(_sortName), out _sortHandle); + if (resultCode != Interop.GlobalizationInterop.ResultCode.Success) + { + _sortHandle.Dispose(); + + if (resultCode == Interop.GlobalizationInterop.ResultCode.OutOfMemory) + throw new OutOfMemoryException(); + + throw new ExternalException(SR.Arg_ExternalException); + } + _isAsciiEqualityOrdinal = (_sortName == "en-US" || _sortName == ""); } - _isAsciiEqualityOrdinal = (_sortName == "en-US" || _sortName == ""); } - internal static unsafe int IndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase) + internal static unsafe int IndexOfOrdinalCore(string source, string value, int startIndex, int count, bool ignoreCase) { + Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(source != null); Debug.Assert(value != null); @@ -78,8 +87,10 @@ namespace System.Globalization return -1; } - internal static unsafe int LastIndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase) + internal static unsafe int LastIndexOfOrdinalCore(string source, string value, int startIndex, int count, bool ignoreCase) { + Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(source != null); Debug.Assert(value != null); @@ -125,36 +136,45 @@ namespace System.Globalization return -1; } - private int GetHashCodeOfStringCore(string source, CompareOptions options) - { - Debug.Assert(source != null); - Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); - - return GetHashCodeOfStringCore(source, options, forceRandomizedHashing: false, additionalEntropy: 0); - } - private static unsafe int CompareStringOrdinalIgnoreCase(char* string1, int count1, char* string2, int count2) { + Debug.Assert(!GlobalizationMode.Invariant); + return Interop.GlobalizationInterop.CompareStringOrdinalIgnoreCase(string1, count1, string2, count2); } - private unsafe int CompareString(string string1, int offset1, int length1, string string2, int offset2, int length2, CompareOptions options) + // TODO https://github.com/dotnet/coreclr/issues/13827: + // This method shouldn't be necessary, as we should be able to just use the overload + // that takes two spans. But due to this issue, that's adding significant overhead. + private unsafe int CompareString(ReadOnlySpan string1, string string2, CompareOptions options) { - Debug.Assert(string1 != null); + Debug.Assert(!_invariantMode); Debug.Assert(string2 != null); Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); - fixed (char* pString1 = string1) + fixed (char* pString1 = &MemoryMarshal.GetReference(string1)) + fixed (char* pString2 = &string2.GetRawStringData()) { - fixed (char* pString2 = string2) - { - return Interop.GlobalizationInterop.CompareString(_sortHandle, pString1 + offset1, length1, pString2 + offset2, length2, options); - } + return Interop.GlobalizationInterop.CompareString(_sortHandle, pString1, string1.Length, pString2, string2.Length, options); + } + } + + private unsafe int CompareString(ReadOnlySpan string1, ReadOnlySpan string2, CompareOptions options) + { + Debug.Assert(!_invariantMode); + Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); + + fixed (char* pString1 = &MemoryMarshal.GetReference(string1)) + fixed (char* pString2 = &MemoryMarshal.GetReference(string2)) + { + return Interop.GlobalizationInterop.CompareString(_sortHandle, pString1, string1.Length, pString2, string2.Length, options); } } internal unsafe int IndexOfCore(string source, string target, int startIndex, int count, CompareOptions options, int* matchLengthPtr) { + Debug.Assert(!_invariantMode); + Debug.Assert(!string.IsNullOrEmpty(source)); Debug.Assert(target != null); Debug.Assert((options & CompareOptions.OrdinalIgnoreCase) == 0); @@ -200,6 +220,8 @@ namespace System.Globalization private unsafe int LastIndexOfCore(string source, string target, int startIndex, int count, CompareOptions options) { + Debug.Assert(!_invariantMode); + Debug.Assert(!string.IsNullOrEmpty(source)); Debug.Assert(target != null); Debug.Assert((options & CompareOptions.OrdinalIgnoreCase) == 0); @@ -211,7 +233,7 @@ namespace System.Globalization if (options == CompareOptions.Ordinal) { - return LastIndexOfOrdinal(source, target, startIndex, count, ignoreCase: false); + return LastIndexOfOrdinalCore(source, target, startIndex, count, ignoreCase: false); } #if CORECLR @@ -235,6 +257,8 @@ namespace System.Globalization private bool StartsWith(string source, string prefix, CompareOptions options) { + Debug.Assert(!_invariantMode); + Debug.Assert(!string.IsNullOrEmpty(source)); Debug.Assert(!string.IsNullOrEmpty(prefix)); Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); @@ -251,6 +275,8 @@ namespace System.Globalization private bool EndsWith(string source, string suffix, CompareOptions options) { + Debug.Assert(!_invariantMode); + Debug.Assert(!string.IsNullOrEmpty(source)); Debug.Assert(!string.IsNullOrEmpty(suffix)); Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); @@ -267,8 +293,9 @@ namespace System.Globalization private unsafe SortKey CreateSortKey(String source, CompareOptions options) { + Debug.Assert(!_invariantMode); + if (source==null) { throw new ArgumentNullException(nameof(source)); } - Contract.EndContractBlock(); if ((options & ValidSortkeyCtorMaskOffFlags) != 0) { @@ -296,6 +323,8 @@ namespace System.Globalization private unsafe static bool IsSortable(char *text, int length) { + Debug.Assert(!GlobalizationMode.Invariant); + int index = 0; UnicodeCategory uc; @@ -335,8 +364,10 @@ namespace System.Globalization // ---- PAL layer ends here ---- // ----------------------------- - internal unsafe int GetHashCodeOfStringCore(string source, CompareOptions options, bool forceRandomizedHashing, long additionalEntropy) + internal unsafe int GetHashCodeOfStringCore(string source, CompareOptions options) { + Debug.Assert(!_invariantMode); + Debug.Assert(source != null); Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); @@ -352,7 +383,7 @@ namespace System.Globalization { byte* pSortKey = stackalloc byte[sortKeyLength]; Interop.GlobalizationInterop.GetSortKey(_sortHandle, source, source.Length, pSortKey, sortKeyLength, options); - return InternalHashSortKey(pSortKey, sortKeyLength, false, additionalEntropy); + return InternalHashSortKey(pSortKey, sortKeyLength); } byte[] sortKey = new byte[sortKeyLength]; @@ -360,38 +391,32 @@ namespace System.Globalization fixed (byte* pSortKey = &sortKey[0]) { Interop.GlobalizationInterop.GetSortKey(_sortHandle, source, source.Length, pSortKey, sortKeyLength, options); - return InternalHashSortKey(pSortKey, sortKeyLength, false, additionalEntropy); + return InternalHashSortKey(pSortKey, sortKeyLength); } } - private static unsafe int InternalHashSortKey(byte* sortKey, int sortKeyLength, bool forceRandomizedHashing, long additionalEntropy) + private static unsafe int InternalHashSortKey(byte* sortKey, int sortKeyLength) { - if (forceRandomizedHashing || additionalEntropy != 0) - { - // TODO: Random hashing is yet to be done - // Active Issue: https://github.com/dotnet/corert/issues/2588 - throw new NotImplementedException(); - } - else - { - int hash1 = 5381; - int hash2 = hash1; - if (sortKeyLength == 0) - { - return 0; - } - if (sortKeyLength == 1) - { - return (((hash1 << 5) + hash1) ^ sortKey[0]) + (hash2 * 1566083941); - } + // TODO: Random hashing is yet to be done + // Active Issue: https://github.com/dotnet/corert/issues/2588 - for (int i = 0; i < (sortKeyLength & ~1); i += 2) - { - hash1 = ((hash1 << 5) + hash1) ^ sortKey[i]; - hash2 = ((hash2 << 5) + hash2) ^ sortKey[i+1]; - } - return hash1 + (hash2 * 1566083941); + int hash1 = 5381; + int hash2 = hash1; + if (sortKeyLength == 0) + { + return 0; } + if (sortKeyLength == 1) + { + return (((hash1 << 5) + hash1) ^ sortKey[0]) + (hash2 * 1566083941); + } + + for (int i = 0; i < (sortKeyLength & ~1); i += 2) + { + hash1 = ((hash1 << 5) + hash1) ^ sortKey[i]; + hash2 = ((hash2 << 5) + hash2) ^ sortKey[i+1]; + } + return hash1 + (hash2 * 1566083941); } private static CompareOptions GetOrdinalCompareOptions(CompareOptions options) @@ -428,7 +453,9 @@ namespace System.Globalization private SortVersion GetSortVersion() { - int sortVersion = Interop.GlobalizationInterop.GetSortVersion(); + Debug.Assert(!_invariantMode); + + int sortVersion = Interop.GlobalizationInterop.GetSortVersion(_sortHandle); return new SortVersion(sortVersion, LCID, new Guid(sortVersion, 0, 0, 0, 0, 0, 0, (byte) (LCID >> 24), (byte) ((LCID & 0x00FF0000) >> 16), diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.Windows.cs index 4456550620..958dc727c3 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.Windows.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.Windows.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; namespace System.Globalization { @@ -11,13 +11,20 @@ namespace System.Globalization { private unsafe void InitSort(CultureInfo culture) { - const uint LCMAP_SORTHANDLE = 0x20000000; - _sortName = culture.SortName; - IntPtr handle; - int ret = Interop.Kernel32.LCMapStringEx(_sortName, LCMAP_SORTHANDLE, null, 0, &handle, IntPtr.Size, null, null, IntPtr.Zero); - _sortHandle = ret > 0 ? handle : IntPtr.Zero; + if (_invariantMode) + { + _sortHandle = IntPtr.Zero; + } + else + { + const uint LCMAP_SORTHANDLE = 0x20000000; + + IntPtr handle; + int ret = Interop.Kernel32.LCMapStringEx(_sortName, LCMAP_SORTHANDLE, null, 0, &handle, IntPtr.Size, null, null, IntPtr.Zero); + _sortHandle = ret > 0 ? handle : IntPtr.Zero; + } } private static unsafe int FindStringOrdinal( @@ -29,6 +36,8 @@ namespace System.Globalization int cchValue, bool bIgnoreCase) { + Debug.Assert(!GlobalizationMode.Invariant); + fixed (char* pSource = stringSource) fixed (char* pValue = value) { @@ -43,16 +52,20 @@ namespace System.Globalization } } - internal static int IndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase) + internal static int IndexOfOrdinalCore(string source, string value, int startIndex, int count, bool ignoreCase) { + Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(source != null); Debug.Assert(value != null); return FindStringOrdinal(FIND_FROMSTART, source, startIndex, count, value, value.Length, ignoreCase); } - internal static int LastIndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase) + internal static int LastIndexOfOrdinalCore(string source, string value, int startIndex, int count, bool ignoreCase) { + Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(source != null); Debug.Assert(value != null); @@ -61,6 +74,8 @@ namespace System.Globalization private unsafe int GetHashCodeOfStringCore(string source, CompareOptions options) { + Debug.Assert(!_invariantMode); + Debug.Assert(source != null); Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); @@ -88,29 +103,66 @@ namespace System.Globalization private static unsafe int CompareStringOrdinalIgnoreCase(char* string1, int count1, char* string2, int count2) { + Debug.Assert(!GlobalizationMode.Invariant); + // Use the OS to compare and then convert the result to expected value by subtracting 2 return Interop.Kernel32.CompareStringOrdinal(string1, count1, string2, count2, true) - 2; } - private unsafe int CompareString(string string1, int offset1, int length1, string string2, int offset2, int length2, CompareOptions options) + // TODO https://github.com/dotnet/coreclr/issues/13827: + // This method shouldn't be necessary, as we should be able to just use the overload + // that takes two spans. But due to this issue, that's adding significant overhead. + private unsafe int CompareString(ReadOnlySpan string1, string string2, CompareOptions options) { - Debug.Assert(string1 != null); Debug.Assert(string2 != null); + Debug.Assert(!_invariantMode); Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); string localeName = _sortHandle != IntPtr.Zero ? null : _sortName; fixed (char* pLocaleName = localeName) - fixed (char* pString1 = string1) - fixed (char* pString2 = string2) + fixed (char* pString1 = &MemoryMarshal.GetReference(string1)) + fixed (char* pString2 = &string2.GetRawStringData()) { int result = Interop.Kernel32.CompareStringEx( pLocaleName, (uint)GetNativeCompareFlags(options), - pString1 + offset1, - length1, - pString2 + offset2, - length2, + pString1, + string1.Length, + pString2, + string2.Length, + null, + null, + _sortHandle); + + if (result == 0) + { + Environment.FailFast("CompareStringEx failed"); + } + + // Map CompareStringEx return value to -1, 0, 1. + return result - 2; + } + } + + private unsafe int CompareString(ReadOnlySpan string1, ReadOnlySpan string2, CompareOptions options) + { + Debug.Assert(!_invariantMode); + Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); + + string localeName = _sortHandle != IntPtr.Zero ? null : _sortName; + + fixed (char* pLocaleName = localeName) + fixed (char* pString1 = &MemoryMarshal.GetReference(string1)) + fixed (char* pString2 = &MemoryMarshal.GetReference(string2)) + { + int result = Interop.Kernel32.CompareStringEx( + pLocaleName, + (uint)GetNativeCompareFlags(options), + pString1, + string1.Length, + pString2, + string2.Length, null, null, _sortHandle); @@ -135,6 +187,8 @@ namespace System.Globalization int cchValue, int* pcchFound) { + Debug.Assert(!_invariantMode); + string localeName = _sortHandle != IntPtr.Zero ? null : _sortName; fixed (char* pLocaleName = localeName) @@ -204,6 +258,8 @@ namespace System.Globalization private unsafe int LastIndexOfCore(string source, string target, int startIndex, int count, CompareOptions options) { + Debug.Assert(!_invariantMode); + Debug.Assert(!string.IsNullOrEmpty(source)); Debug.Assert(target != null); Debug.Assert((options & CompareOptions.OrdinalIgnoreCase) == 0); @@ -239,6 +295,8 @@ namespace System.Globalization private unsafe bool StartsWith(string source, string prefix, CompareOptions options) { + Debug.Assert(!_invariantMode); + Debug.Assert(!string.IsNullOrEmpty(source)); Debug.Assert(!string.IsNullOrEmpty(prefix)); Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); @@ -255,6 +313,8 @@ namespace System.Globalization private unsafe bool EndsWith(string source, string suffix, CompareOptions options) { + Debug.Assert(!_invariantMode); + Debug.Assert(!string.IsNullOrEmpty(source)); Debug.Assert(!string.IsNullOrEmpty(suffix)); Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); @@ -358,8 +418,9 @@ namespace System.Globalization private unsafe SortKey CreateSortKey(String source, CompareOptions options) { + Debug.Assert(!_invariantMode); + if (source == null) { throw new ArgumentNullException(nameof(source)); } - Contract.EndContractBlock(); if ((options & ValidSortkeyCtorMaskOffFlags) != 0) { @@ -403,6 +464,8 @@ namespace System.Globalization private static unsafe bool IsSortable(char* text, int length) { + Debug.Assert(!GlobalizationMode.Invariant); + return Interop.Kernel32.IsNLSDefinedString(Interop.Kernel32.COMPARE_STRING, 0, IntPtr.Zero, text, length); } @@ -446,6 +509,8 @@ namespace System.Globalization private unsafe SortVersion GetSortVersion() { + Debug.Assert(!_invariantMode); + Interop.Kernel32.NlsVersionInfoEx nlsVersion = new Interop.Kernel32.NlsVersionInfoEx(); Interop.Kernel32.GetNLSVersionEx(Interop.Kernel32.COMPARE_STRING, _sortName, &nlsVersion); return new SortVersion( diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CultureData.Dummy.cs b/external/corert/src/System.Private.CoreLib/src/System/Globalization/CultureData.Dummy.cs index e9815b40bd..703b5f3c3c 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CultureData.Dummy.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Globalization/CultureData.Dummy.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Diagnostics.Contracts; using System.Runtime.InteropServices; using System.Text; using Internal.Runtime.Augments; diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs index 28ebe7239b..373fd26a89 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs @@ -8,6 +8,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; +using Internal.Runtime.CompilerServices; + #if ENABLE_WINRT using Internal.Runtime.Augments; #endif @@ -630,13 +632,17 @@ namespace System.Globalization return null; } - private int LocaleNameToLCID(string cultureName) + private static int LocaleNameToLCID(string cultureName) { + Debug.Assert(!GlobalizationMode.Invariant); + return Interop.Kernel32.LocaleNameToLCID(cultureName, Interop.Kernel32.LOCALE_ALLOW_NEUTRAL_NAMES); } private static unsafe string LCIDToLocaleName(int culture) { + Debug.Assert(!GlobalizationMode.Invariant); + char *pBuffer = stackalloc char[Interop.Kernel32.LOCALE_NAME_MAX_LENGTH + 1]; // +1 for the null termination int length = Interop.Kernel32.LCIDToLocaleName(culture, pBuffer, Interop.Kernel32.LOCALE_NAME_MAX_LENGTH + 1, Interop.Kernel32.LOCALE_ALLOW_NEUTRAL_NAMES); @@ -685,6 +691,8 @@ namespace System.Globalization private static CultureInfo[] EnumCultures(CultureTypes types) { + Debug.Assert(!GlobalizationMode.Invariant); + uint flags = 0; #pragma warning disable 618 diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Unix.cs b/external/corert/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Unix.cs index e9fe86acee..5a06bccd48 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Unix.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Unix.cs @@ -13,6 +13,9 @@ namespace System.Globalization internal static CultureInfo GetUserDefaultCulture() { + if (GlobalizationMode.Invariant) + return CultureInfo.InvariantCulture; + CultureInfo cultureInfo = null; string localeName; if (CultureData.GetDefaultLocaleName(out localeName)) diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Windows.cs index b395c1a7fa..f5a6992e66 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Windows.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Windows.cs @@ -31,6 +31,9 @@ namespace System.Globalization internal static CultureInfo GetUserDefaultCulture() { + if (GlobalizationMode.Invariant) + return CultureInfo.InvariantCulture; + const uint LOCALE_SNAME = 0x0000005c; const string LOCALE_NAME_USER_DEFAULT = null; const string LOCALE_NAME_SYSTEM_DEFAULT = "!x-sys-default-locale"; diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.cs b/external/corert/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.cs index a03ca1ed8f..bc4411ab78 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.cs @@ -28,7 +28,6 @@ using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.Serialization; using System.Threading; @@ -49,7 +48,7 @@ namespace System.Globalization //--------------------------------------------------------------------// // We use an RFC4646 type string to construct CultureInfo. - // This string is stored in m_name and is authoritative. + // This string is stored in _name and is authoritative. // We use the _cultureData to get the data for our object private bool _isReadOnly; @@ -67,11 +66,11 @@ namespace System.Globalization internal bool _isInherited; - private CultureInfo m_consoleFallbackCulture; + private CultureInfo _consoleFallbackCulture; // Names are confusing. Here are 3 names we have: // - // new CultureInfo() m_name _nonSortName _sortName + // new CultureInfo() _name _nonSortName _sortName // en-US en-US en-US en-US // de-de_phoneb de-DE_phoneb de-DE de-DE_phoneb // fj-fj (custom) fj-FJ fj-FJ en-US (if specified sort is en-US) @@ -83,7 +82,7 @@ namespace System.Globalization // Note that the name used to be serialized for Everett; it is now serialized // because alernate sorts can have alternate names. // This has a de-DE, de-DE_phoneb or fj-FJ style name - internal string m_name; + internal string _name; // This will hold the non sorting name to be returned from CultureInfo.Name property. // This has a de-DE style name even for de-DE_phoneb type cultures @@ -172,7 +171,6 @@ namespace System.Globalization { } - public CultureInfo(String name, bool useUserOverride) { if (name == null) @@ -188,10 +186,30 @@ namespace System.Globalization throw new CultureNotFoundException( nameof(name), name, SR.Argument_CultureNotSupported); - this.m_name = this._cultureData.CultureName; + this._name = this._cultureData.CultureName; this._isInherited = !this.EETypePtr.FastEquals(EETypePtr.EETypePtrOf()); } + private CultureInfo(CultureData cultureData) + { + Debug.Assert(cultureData != null); + _cultureData = cultureData; + _name = cultureData.CultureName; + _isInherited = false; + } + + private static CultureInfo CreateCultureInfoNoThrow(string name, bool useUserOverride) + { + Debug.Assert(name != null); + CultureData cultureData = CultureData.GetCultureData(name, useUserOverride); + if (cultureData == null) + { + return null; + } + + return new CultureInfo(cultureData); + } + public CultureInfo(int culture) : this(culture, true) { } @@ -203,7 +221,6 @@ namespace System.Globalization { throw new ArgumentOutOfRangeException(nameof(culture), SR.ArgumentOutOfRange_NeedPosNum); } - Contract.EndContractBlock(); InitializeFromCultureId(culture, useUserOverride); } @@ -227,7 +244,7 @@ namespace System.Globalization break; } _isInherited = (this.GetType() != typeof(System.Globalization.CultureInfo)); - m_name = _cultureData.CultureName; + _name = _cultureData.CultureName; } // Constructor called by SQL Server's special munged culture - creates a culture with @@ -242,13 +259,12 @@ namespace System.Globalization { throw new ArgumentNullException(nameof(cultureName), SR.ArgumentNull_String); } - Contract.EndContractBlock(); _cultureData = CultureData.GetCultureData(cultureName, false); if (_cultureData == null) throw new CultureNotFoundException(nameof(cultureName), cultureName, SR.Argument_CultureNotSupported); - m_name = _cultureData.CultureName; + _name = _cultureData.CultureName; CultureInfo altCulture = GetCultureInfo(textAndCompareCultureName); _compareInfo = altCulture.CompareInfo; @@ -290,8 +306,6 @@ namespace System.Globalization // public static CultureInfo CreateSpecificCulture(String name) { - Contract.Ensures(Contract.Result() != null); - CultureInfo culture; try @@ -510,16 +524,21 @@ namespace System.Globalization } } + internal static void ResetThreadCulture() + { + s_currentThreadCulture = null; + s_currentThreadUICulture = null; + } + public static CultureInfo InstalledUICulture { get { - Contract.Ensures(Contract.Result() != null); if (s_userDefaultCulture == null) { Init(); } - Contract.Assert(s_userDefaultCulture != null, "[CultureInfo.InstalledUICulture] s_userDefaultCulture != null"); + Debug.Assert(s_userDefaultCulture != null, "[CultureInfo.InstalledUICulture] s_userDefaultCulture != null"); return s_userDefaultCulture; } } @@ -594,26 +613,24 @@ namespace System.Globalization if (null == _parent) { CultureInfo culture = null; - try - { - string parentName = this._cultureData.SPARENT; + string parentName = _cultureData.SPARENT; - if (String.IsNullOrEmpty(parentName)) - { - culture = InvariantCulture; - } - else - { - culture = new CultureInfo(parentName, this._cultureData.UseUserOverride); - } - } - catch (ArgumentException) + if (String.IsNullOrEmpty(parentName)) { - // For whatever reason our IPARENT or SPARENT wasn't correct, so use invariant - // We can't allow ourselves to fail. In case of custom cultures the parent of the - // current custom culture isn't installed. culture = InvariantCulture; } + else + { + culture = CreateCultureInfoNoThrow(parentName, _cultureData.UseUserOverride); + if (culture == null) + { + // For whatever reason our IPARENT or SPARENT wasn't correct, so use invariant + // We can't allow ourselves to fail. In case of custom cultures the parent of the + // current custom culture isn't installed. + culture = InvariantCulture; + } + } + Interlocked.CompareExchange(ref _parent, culture, null); } return _parent; @@ -624,7 +641,7 @@ namespace System.Globalization { get { - return (this._cultureData.ILANGUAGE); + return _cultureData.ILANGUAGE; } } @@ -638,7 +655,6 @@ namespace System.Globalization public static CultureInfo[] GetCultures(CultureTypes types) { - Contract.Ensures(Contract.Result() != null); // internally we treat UserCustomCultures as Supplementals but v2 // treats as Supplementals and Replacements if ((types & CultureTypes.UserCustomCulture) == CultureTypes.UserCustomCulture) @@ -663,7 +679,7 @@ namespace System.Globalization // We return non sorting name here. if (_nonSortName == null) { - _nonSortName = this._cultureData.SNAME; + _nonSortName = _cultureData.SNAME; if (_nonSortName == null) { _nonSortName = String.Empty; @@ -680,7 +696,7 @@ namespace System.Globalization { if (_sortName == null) { - _sortName = this._cultureData.SCOMPAREINFO; + _sortName = _cultureData.SCOMPAREINFO; } return _sortName; @@ -691,8 +707,6 @@ namespace System.Globalization { get { - Contract.Ensures(Contract.Result() != null); - // special case the compatibility cultures switch (this.Name) { @@ -719,8 +733,7 @@ namespace System.Globalization { get { - Contract.Ensures(Contract.Result() != null); - Debug.Assert(m_name != null, "[CultureInfo.DisplayName] Always expect m_name to be set"); + Debug.Assert(_name != null, "[CultureInfo.DisplayName] Always expect _name to be set"); return _cultureData.SLOCALIZEDDISPLAYNAME; } @@ -739,8 +752,7 @@ namespace System.Globalization { get { - Contract.Ensures(Contract.Result() != null); - return (this._cultureData.SNATIVEDISPLAYNAME); + return _cultureData.SNATIVEDISPLAYNAME; } } @@ -757,8 +769,7 @@ namespace System.Globalization { get { - Contract.Ensures(Contract.Result() != null); - return (this._cultureData.SENGDISPLAYNAME); + return _cultureData.SENGDISPLAYNAME; } } @@ -767,8 +778,7 @@ namespace System.Globalization { get { - Contract.Ensures(Contract.Result() != null); - return (this._cultureData.SISO639LANGNAME); + return _cultureData.SISO639LANGNAME; } } @@ -777,7 +787,6 @@ namespace System.Globalization { get { - Contract.Ensures(Contract.Result() != null); return _cultureData.SISO639LANGNAME2; } } @@ -794,7 +803,6 @@ namespace System.Globalization { get { - Contract.Ensures(Contract.Result() != null); return _cultureData.SABBREVLANGNAME; } } @@ -815,7 +823,7 @@ namespace System.Globalization // Since CompareInfo's don't have any overrideable properties, get the CompareInfo from // the Non-Overridden CultureInfo so that we only create one CompareInfo per culture CompareInfo temp = UseUserOverride - ? GetCultureInfo(this.m_name).CompareInfo + ? GetCultureInfo(this._name).CompareInfo : new CompareInfo(this); if (OkayToCacheClassWithCompatibilityBehavior) { @@ -925,7 +933,7 @@ namespace System.Globalization public override String ToString() { - return m_name; + return _name; } @@ -1132,8 +1140,6 @@ namespace System.Globalization { get { - Contract.Ensures(Contract.Result() != null); - // // This property always returns a new copy of the calendar array. // @@ -1157,14 +1163,12 @@ namespace System.Globalization public CultureInfo GetConsoleFallbackUICulture() { - Contract.Ensures(Contract.Result() != null); - - CultureInfo temp = m_consoleFallbackCulture; + CultureInfo temp = _consoleFallbackCulture; if (temp == null) { temp = CreateSpecificCulture(_cultureData.SCONSOLEFALLBACKNAME); temp._isReadOnly = true; - m_consoleFallbackCulture = temp; + _consoleFallbackCulture = temp; } return (temp); } @@ -1212,8 +1216,6 @@ namespace System.Globalization { throw new ArgumentNullException(nameof(ci)); } - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); if (ci.IsReadOnly) { @@ -1395,7 +1397,7 @@ namespace System.Globalization { // Remember our name (as constructed). Do NOT use alternate sort name versions because // we have internal state representing the sort. (So someone would get the wrong cached version) - string newName = CultureData.AnsiToLower(retval.m_name); + string newName = CultureData.AnsiToLower(retval._name); // We add this new culture info object to both tables. using (LockHolder.Hold(s_lock)) @@ -1436,8 +1438,7 @@ namespace System.Globalization { throw new ArgumentOutOfRangeException(nameof(culture), SR.ArgumentOutOfRange_NeedPosNum); } - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); + CultureInfo retval = GetCultureInfoHelper(culture, null, null); if (null == retval) { @@ -1480,9 +1481,6 @@ namespace System.Globalization throw new ArgumentNullException(nameof(altName)); } - Contract.Ensures(Contract.Result() != null); - Contract.EndContractBlock(); - CultureInfo retval = GetCultureInfoHelper(-1, name, altName); if (retval == null) { @@ -1495,8 +1493,6 @@ namespace System.Globalization // This function is deprecated, we don't like it public static CultureInfo GetCultureInfoByIetfLanguageTag(string name) { - Contract.Ensures(Contract.Result() != null); - // Disallow old zh-CHT/zh-CHS names if (name == "zh-CHT" || name == "zh-CHS") { @@ -1515,4 +1511,3 @@ namespace System.Globalization } } } - diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/FormatProvider.FormatAndParse.Unix.cs b/external/corert/src/System.Private.CoreLib/src/System/Globalization/FormatProvider.FormatAndParse.Unix.cs deleted file mode 100644 index e1d78f9ef7..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/FormatProvider.FormatAndParse.Unix.cs +++ /dev/null @@ -1,168 +0,0 @@ -// 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.Runtime; -using System.Globalization; -using System.Diagnostics; - -namespace System.Globalization -{ - internal partial class FormatProvider - { - private partial class Number - { - // buffer size to hold digits (40), decimal point, number sign, exponent, exponent symbol 'e', exponent sign and null. - private const int MAX_BUFFER_SIZE = 50; - - private static unsafe void DoubleToNumber(double value, int precision, ref NumberBuffer number) - { - Debug.Assert(precision > 0 && precision < 40); - - number.precision = precision; - if (DoubleHelper.Exponent(value) == 0x7ff) - { - number.scale = DoubleHelper.Mantissa(value) != 0 ? SCALE_NAN : SCALE_INF; - number.sign = DoubleHelper.Sign(value); - number.digits[0] = '\0'; - return; - } - - byte* tempBuffer = stackalloc byte[MAX_BUFFER_SIZE]; - char* dst = number.digits; - - number.scale = 0; - number.sign = false; - *dst = '\0'; - - if (value < 0.0) - { - number.sign = true; - } - - if (value == 0.0) - { - for (int j = 0; j < precision; j++) - { - dst[j] = '0'; - } - dst[precision] = '\0'; - return; - } - - // - // Get the number formatted as a string in the form x.xxxxxxexxxx - // - - // "%.40e" - byte* format = stackalloc byte[6]; - format[0] = (byte)'%'; - format[1] = (byte)'.'; - format[2] = (byte)'4'; - format[3] = (byte)'0'; - format[4] = (byte)'e'; - format[5] = 0; - - int tempBufferLength = Interop.Sys.DoubleToString(value, format, tempBuffer, MAX_BUFFER_SIZE); - Debug.Assert(tempBufferLength > 0 && MAX_BUFFER_SIZE > tempBufferLength); - - // - // Calculate the exponent value - // - - int exponentIndex = tempBufferLength - 1; - while (tempBuffer[exponentIndex] != (byte)'e' && exponentIndex > 0) - { - exponentIndex--; - } - - Debug.Assert(exponentIndex > 0 && (exponentIndex < tempBufferLength - 1)); - - int i = exponentIndex + 1; - int exponentSign = 1; - if (tempBuffer[i] == '-') - { - exponentSign = -1; - i++; - } - else if (tempBuffer[i] == '+') - { - i++; - } - - int exponentValue = 0; - while (i < tempBufferLength) - { - Debug.Assert(tempBuffer[i] >= (byte)'0' && tempBuffer[i] <= (byte)'9'); - exponentValue = exponentValue * 10 + (tempBuffer[i] - (byte)'0'); - i++; - } - exponentValue *= exponentSign; - - // - // Determine decimal location. - // - - if (exponentValue == 0) - { - number.scale = 1; - } - else - { - number.scale = exponentValue + 1; - } - - // - // Copy the string from the temp buffer upto precision characters, removing the sign, and decimal as required. - // - - i = 0; - int mantissaIndex = 0; - while (i < precision && mantissaIndex < exponentIndex) - { - if (tempBuffer[mantissaIndex] >= (byte)'0' && tempBuffer[mantissaIndex] <= (byte)'9') - { - dst[i] = (char)tempBuffer[mantissaIndex]; - i++; - } - mantissaIndex++; - } - - while (i < precision) - { - dst[i] = '0'; // append zeros as needed - i++; - } - - dst[i] = '\0'; - - // - // Round if needed - // - - if (mantissaIndex >= exponentIndex || tempBuffer[mantissaIndex] < (byte)'5') - { - return; // rounding is not needed - } - - i = precision - 1; - while (dst[i] == '9' && i > 0) - { - dst[i] = '0'; - i--; - } - - if (i == 0 && dst[i] == '9') - { - dst[i] = '1'; - number.scale++; - } - else - { - dst[i]++; - } - } - } - } -} \ No newline at end of file diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/FormatProvider.FormatAndParse.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/Globalization/FormatProvider.FormatAndParse.Windows.cs deleted file mode 100644 index 5d535dc32b..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/FormatProvider.FormatAndParse.Windows.cs +++ /dev/null @@ -1,47 +0,0 @@ -// 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.Globalization; -using System.Runtime; - -namespace System.Globalization -{ - internal partial class FormatProvider - { - private partial class Number - { - private static unsafe void DoubleToNumber(double value, int precision, ref NumberBuffer number) - { - number.precision = precision; - if (DoubleHelper.Exponent(value) == 0x7ff) - { - number.scale = DoubleHelper.Mantissa(value) != 0 ? SCALE_NAN : SCALE_INF; - number.sign = DoubleHelper.Sign(value); - number.digits[0] = '\0'; - } - else - { - byte* src = stackalloc byte[_CVTBUFSIZE]; - int sign; - fixed (NumberBuffer* pNumber = &number) - { - RuntimeImports._ecvt_s(src, _CVTBUFSIZE, value, precision, &pNumber->scale, &sign); - } - number.sign = sign != 0; - - char* dst = number.digits; - if ((char)*src != '0') - { - while (*src != 0) - *dst++ = (char)*src++; - } - *dst = '\0'; - } - } - } - } -} - - diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/FormatProvider.FormatAndParse.cs b/external/corert/src/System.Private.CoreLib/src/System/Globalization/FormatProvider.FormatAndParse.cs deleted file mode 100644 index 41a7462694..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/FormatProvider.FormatAndParse.cs +++ /dev/null @@ -1,1789 +0,0 @@ -// 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.Diagnostics; -using System.Diagnostics.Contracts; - -namespace System.Globalization -{ - internal partial class FormatProvider - { - private partial class Number - { - private const Int32 INT32_PRECISION = 10; - private const Int32 UINT32_PRECISION = INT32_PRECISION; - private const Int32 INT64_PRECISION = 19; - private const Int32 UINT64_PRECISION = 20; - private const int FLOAT_PRECISION = 7; - private const int DOUBLE_PRECISION = 15; - - private static Boolean HexNumberToInt32(ref NumberBuffer number, ref Int32 value) - { - UInt32 passedValue = 0; - Boolean returnValue = HexNumberToUInt32(ref number, ref passedValue); - value = (Int32)passedValue; - return returnValue; - } - - private static Boolean HexNumberToInt64(ref NumberBuffer number, ref Int64 value) - { - UInt64 passedValue = 0; - Boolean returnValue = HexNumberToUInt64(ref number, ref passedValue); - value = (Int64)passedValue; - return returnValue; - } - - private static unsafe Boolean HexNumberToUInt32(ref NumberBuffer number, ref UInt32 value) - { - Int32 i = number.scale; - if (i > UINT32_PRECISION || i < number.precision) - { - return false; - } - Char* p = number.digits; - Debug.Assert(p != null, ""); - - UInt32 n = 0; - while (--i >= 0) - { - if (n > ((UInt32)0xFFFFFFFF / 16)) - { - return false; - } - n *= 16; - if (*p != '\0') - { - UInt32 newN = n; - if (*p != '\0') - { - if (*p >= '0' && *p <= '9') - { - newN += (UInt32)(*p - '0'); - } - else - { - if (*p >= 'A' && *p <= 'F') - { - newN += (UInt32)((*p - 'A') + 10); - } - else - { - Debug.Assert(*p >= 'a' && *p <= 'f', ""); - newN += (UInt32)((*p - 'a') + 10); - } - } - p++; - } - - // Detect an overflow here... - if (newN < n) - { - return false; - } - n = newN; - } - } - value = n; - return true; - } - - private static unsafe Boolean HexNumberToUInt64(ref NumberBuffer number, ref UInt64 value) - { - Int32 i = number.scale; - if (i > UINT64_PRECISION || i < number.precision) - { - return false; - } - Char* p = number.digits; - Debug.Assert(p != null, ""); - - UInt64 n = 0; - while (--i >= 0) - { - if (n > (0xFFFFFFFFFFFFFFFF / 16)) - { - return false; - } - n *= 16; - if (*p != '\0') - { - UInt64 newN = n; - if (*p != '\0') - { - if (*p >= '0' && *p <= '9') - { - newN += (UInt64)(*p - '0'); - } - else - { - if (*p >= 'A' && *p <= 'F') - { - newN += (UInt64)((*p - 'A') + 10); - } - else - { - Debug.Assert(*p >= 'a' && *p <= 'f', ""); - newN += (UInt64)((*p - 'a') + 10); - } - } - p++; - } - - // Detect an overflow here... - if (newN < n) - { - return false; - } - n = newN; - } - } - value = n; - return true; - } - - private static unsafe Boolean NumberToInt32(ref NumberBuffer number, ref Int32 value) - { - Int32 i = number.scale; - if (i > INT32_PRECISION || i < number.precision) - { - return false; - } - char* p = number.digits; - Debug.Assert(p != null, ""); - Int32 n = 0; - while (--i >= 0) - { - if ((UInt32)n > (0x7FFFFFFF / 10)) - { - return false; - } - n *= 10; - if (*p != '\0') - { - n += (Int32)(*p++ - '0'); - } - } - if (number.sign) - { - n = -n; - if (n > 0) - { - return false; - } - } - else - { - if (n < 0) - { - return false; - } - } - value = n; - return true; - } - - private static unsafe Boolean NumberToInt64(ref NumberBuffer number, ref Int64 value) - { - Int32 i = number.scale; - if (i > INT64_PRECISION || i < number.precision) - { - return false; - } - char* p = number.digits; - Debug.Assert(p != null, ""); - Int64 n = 0; - while (--i >= 0) - { - if ((UInt64)n > (0x7FFFFFFFFFFFFFFF / 10)) - { - return false; - } - n *= 10; - if (*p != '\0') - { - n += (Int32)(*p++ - '0'); - } - } - if (number.sign) - { - n = -n; - if (n > 0) - { - return false; - } - } - else - { - if (n < 0) - { - return false; - } - } - value = n; - return true; - } - - private static unsafe Boolean NumberToUInt32(ref NumberBuffer number, ref UInt32 value) - { - Int32 i = number.scale; - if (i > UINT32_PRECISION || i < number.precision || number.sign) - { - return false; - } - char* p = number.digits; - Debug.Assert(p != null, ""); - UInt32 n = 0; - while (--i >= 0) - { - if (n > (0xFFFFFFFF / 10)) - { - return false; - } - n *= 10; - if (*p != '\0') - { - UInt32 newN = n + (UInt32)(*p++ - '0'); - // Detect an overflow here... - if (newN < n) - { - return false; - } - n = newN; - } - } - value = n; - return true; - } - - private static unsafe Boolean NumberToUInt64(ref NumberBuffer number, ref UInt64 value) - { - Int32 i = number.scale; - if (i > UINT64_PRECISION || i < number.precision || number.sign) - { - return false; - } - char* p = number.digits; - Debug.Assert(p != null, ""); - UInt64 n = 0; - while (--i >= 0) - { - if (n > (0xFFFFFFFFFFFFFFFF / 10)) - { - return false; - } - n *= 10; - if (*p != '\0') - { - UInt64 newN = n + (UInt64)(*p++ - '0'); - // Detect an overflow here... - if (newN < n) - { - return false; - } - n = newN; - } - } - value = n; - return true; - } - - internal static Decimal ParseDecimal(String value, NumberStyles options, IFormatProvider provider) - { - NumberFormatInfo numfmt = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - NumberBuffer number = new NumberBuffer(); - Decimal result = 0; - - StringToNumber(value, options, ref number, numfmt, true); - - if (!NumberBufferToDecimal(number, ref result)) - throw new OverflowException(SR.Overflow_Decimal); - - return result; - } - - internal static unsafe Double ParseDouble(String value, NumberStyles options, IFormatProvider provider) - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - - NumberFormatInfo numfmt = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - NumberBuffer number = new NumberBuffer(); - Double d = 0; - - if (!TryStringToNumber(value, options, ref number, numfmt, false)) - { - //If we failed TryStringToNumber, it may be from one of our special strings. - //Check the three with which we're concerned and rethrow if it's not one of - //those strings. - String sTrim = value.Trim(); - if (sTrim.Equals(numfmt.PositiveInfinitySymbol)) - { - return Double.PositiveInfinity; - } - if (sTrim.Equals(numfmt.NegativeInfinitySymbol)) - { - return Double.NegativeInfinity; - } - if (sTrim.Equals(numfmt.NaNSymbol)) - { - return Double.NaN; - } - throw new FormatException(SR.Format_InvalidString); - } - - if (!NumberBufferToDouble(number, ref d)) - { - throw new OverflowException(SR.Overflow_Double); - } - - return d; - } - - internal static unsafe Int32 ParseInt32(String s, NumberStyles style, IFormatProvider provider) - { - NumberFormatInfo info = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - - NumberBuffer number = new NumberBuffer(); - Int32 i = 0; - - StringToNumber(s, style, ref number, info, false); - - if ((style & NumberStyles.AllowHexSpecifier) != 0) - { - if (!HexNumberToInt32(ref number, ref i)) - { - throw new OverflowException(SR.Overflow_Int32); - } - } - else - { - if (!NumberToInt32(ref number, ref i)) - { - throw new OverflowException(SR.Overflow_Int32); - } - } - return i; - } - - internal static unsafe Int64 ParseInt64(String value, NumberStyles options, IFormatProvider provider) - { - NumberFormatInfo numfmt = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - - NumberBuffer number = new NumberBuffer(); - Int64 i = 0; - - StringToNumber(value, options, ref number, numfmt, false); - - if ((options & NumberStyles.AllowHexSpecifier) != 0) - { - if (!HexNumberToInt64(ref number, ref i)) - { - throw new OverflowException(SR.Overflow_Int64); - } - } - else - { - if (!NumberToInt64(ref number, ref i)) - { - throw new OverflowException(SR.Overflow_Int64); - } - } - return i; - } - - internal static unsafe Single ParseSingle(String value, NumberStyles options, IFormatProvider provider) - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - - NumberFormatInfo numfmt = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - NumberBuffer number = new NumberBuffer(); - Double d = 0; - - if (!TryStringToNumber(value, options, ref number, numfmt, false)) - { - //If we failed TryStringToNumber, it may be from one of our special strings. - //Check the three with which we're concerned and rethrow if it's not one of - //those strings. - String sTrim = value.Trim(); - if (sTrim.Equals(numfmt.PositiveInfinitySymbol)) - { - return Single.PositiveInfinity; - } - if (sTrim.Equals(numfmt.NegativeInfinitySymbol)) - { - return Single.NegativeInfinity; - } - if (sTrim.Equals(numfmt.NaNSymbol)) - { - return Single.NaN; - } - throw new FormatException(SR.Format_InvalidString); - } - - if (!NumberBufferToDouble(number, ref d)) - { - throw new OverflowException(SR.Overflow_Single); - } - Single castSingle = (Single)d; - if (Single.IsInfinity(castSingle)) - { - throw new OverflowException(SR.Overflow_Single); - } - return castSingle; - } - - internal static unsafe UInt32 ParseUInt32(String value, NumberStyles options, IFormatProvider provider) - { - NumberFormatInfo numfmt = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - - NumberBuffer number = new NumberBuffer(); - UInt32 i = 0; - - StringToNumber(value, options, ref number, numfmt, false); - - if ((options & NumberStyles.AllowHexSpecifier) != 0) - { - if (!HexNumberToUInt32(ref number, ref i)) - { - throw new OverflowException(SR.Overflow_UInt32); - } - } - else - { - if (!NumberToUInt32(ref number, ref i)) - { - throw new OverflowException(SR.Overflow_UInt32); - } - } - - return i; - } - - internal static unsafe UInt64 ParseUInt64(String value, NumberStyles options, IFormatProvider provider) - { - NumberFormatInfo numfmt = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - NumberBuffer number = new NumberBuffer(); - UInt64 i = 0; - - StringToNumber(value, options, ref number, numfmt, false); - if ((options & NumberStyles.AllowHexSpecifier) != 0) - { - if (!HexNumberToUInt64(ref number, ref i)) - { - throw new OverflowException(SR.Overflow_UInt64); - } - } - else - { - if (!NumberToUInt64(ref number, ref i)) - { - throw new OverflowException(SR.Overflow_UInt64); - } - } - return i; - } - - private static unsafe void StringToNumber(String str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo info, Boolean parseDecimal) - { - if (str == null) - { - throw new ArgumentNullException("String"); - } - Contract.EndContractBlock(); - Debug.Assert(info != null, ""); - fixed (char* stringPointer = str) - { - char* p = stringPointer; - if (!ParseNumber(ref p, options, ref number, null, info, parseDecimal) - || (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer)))) - { - throw new FormatException(SR.Format_InvalidString); - } - } - } - - internal static unsafe Boolean TryParseDecimal(String value, NumberStyles options, IFormatProvider provider, out Decimal result) - { - NumberFormatInfo numfmt = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - - NumberBuffer number = new NumberBuffer(); - result = 0; - - if (!TryStringToNumber(value, options, ref number, numfmt, true)) - { - return false; - } - - if (!NumberBufferToDecimal(number, ref result)) - { - return false; - } - return true; - } - - internal static unsafe Boolean TryParseDouble(String value, NumberStyles options, IFormatProvider provider, out Double result) - { - NumberFormatInfo numfmt = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - NumberBuffer number = new NumberBuffer(); - result = 0; - - - if (!TryStringToNumber(value, options, ref number, numfmt, false)) - { - return false; - } - if (!NumberBufferToDouble(number, ref result)) - { - return false; - } - return true; - } - - internal static unsafe Boolean TryParseInt32(String s, NumberStyles style, IFormatProvider provider, out Int32 result) - { - NumberFormatInfo info = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - - NumberBuffer number = new NumberBuffer(); - result = 0; - - if (!TryStringToNumber(s, style, ref number, info, false)) - { - return false; - } - - if ((style & NumberStyles.AllowHexSpecifier) != 0) - { - if (!HexNumberToInt32(ref number, ref result)) - { - return false; - } - } - else - { - if (!NumberToInt32(ref number, ref result)) - { - return false; - } - } - return true; - } - - internal static unsafe Boolean TryParseInt64(String s, NumberStyles style, IFormatProvider provider, out Int64 result) - { - NumberFormatInfo info = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - - NumberBuffer number = new NumberBuffer(); - result = 0; - - if (!TryStringToNumber(s, style, ref number, info, false)) - { - return false; - } - - if ((style & NumberStyles.AllowHexSpecifier) != 0) - { - if (!HexNumberToInt64(ref number, ref result)) - { - return false; - } - } - else - { - if (!NumberToInt64(ref number, ref result)) - { - return false; - } - } - return true; - } - - internal static unsafe Boolean TryParseSingle(String value, NumberStyles options, IFormatProvider provider, out Single result) - { - NumberFormatInfo numfmt = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - NumberBuffer number = new NumberBuffer(); - result = 0; - Double d = 0; - - if (!TryStringToNumber(value, options, ref number, numfmt, false)) - { - return false; - } - if (!NumberBufferToDouble(number, ref d)) - { - return false; - } - Single castSingle = (Single)d; - if (Single.IsInfinity(castSingle)) - { - return false; - } - - result = castSingle; - return true; - } - - internal static unsafe Boolean TryParseUInt32(String s, NumberStyles style, IFormatProvider provider, out UInt32 result) - { - NumberFormatInfo info = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - - NumberBuffer number = new NumberBuffer(); - result = 0; - - if (!TryStringToNumber(s, style, ref number, info, false)) - { - return false; - } - - if ((style & NumberStyles.AllowHexSpecifier) != 0) - { - if (!HexNumberToUInt32(ref number, ref result)) - { - return false; - } - } - else - { - if (!NumberToUInt32(ref number, ref result)) - { - return false; - } - } - return true; - } - - internal static unsafe Boolean TryParseUInt64(String s, NumberStyles style, IFormatProvider provider, out UInt64 result) - { - NumberFormatInfo info = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - - NumberBuffer number = new NumberBuffer(); - result = 0; - - if (!TryStringToNumber(s, style, ref number, info, false)) - { - return false; - } - - if ((style & NumberStyles.AllowHexSpecifier) != 0) - { - if (!HexNumberToUInt64(ref number, ref result)) - { - return false; - } - } - else - { - if (!NumberToUInt64(ref number, ref result)) - { - return false; - } - } - return true; - } - - internal static Boolean TryStringToNumber(String str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, Boolean parseDecimal) - { - return TryStringToNumber(str, options, ref number, null, numfmt, parseDecimal); - } - - // ********************************************************************************************************** - // - // The remaining code in this module is an almost direct translation from the original unmanaged version in - // the CLR. The code uses NumberBuffer directly instead of an analog of the NUMBER unmanaged data structure - // but this causes next to no differences since we've modified NumberBuffer to take account of the changes (it - // has an inline array of digits and no need of a pack operation to prepare for use by the "unmanaged" code). - // - // Some minor cleanup has been done (e.g. taking advantage of StringBuilder instead of having to precompute - // string buffer sizes) but there's still plenty of opportunity to further C#'ize this code and potentially - // better unify it with the code above. - // - - private const int SCALE_NAN = unchecked((int)0x80000000); - private const int SCALE_INF = 0x7FFFFFFF; - - private const int _CVTBUFSIZE = 349; - - public static String FormatDecimal(Decimal value, String format, IFormatProvider provider) - { - NumberFormatInfo info = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - - NumberBuffer number = new NumberBuffer(); - DecimalToNumber(value, ref number); - - int digits; - char fmt = ParseFormatSpecifier(format, out digits); - if (fmt != 0) - return NumberToString(number, fmt, digits, info, true); - else - return NumberToStringFormat(number, format, info); - } - - public static String FormatDouble(double value, String format, IFormatProvider provider) - { - NumberFormatInfo info = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - - int digits; - char fmt = ParseFormatSpecifier(format, out digits); - - int precision = DOUBLE_PRECISION; - NumberBuffer number = new NumberBuffer(); - - switch (fmt) - { - case 'R': - case 'r': - { - // In order to give numbers that are both friendly to display and round-trippable, we parse - // the number using 15 digits and then determine if it round trips to the same value. If it - // does, we convert that NUMBER to a string, otherwise we reparse using 17 digits and display - // that. - DoubleToNumber(value, DOUBLE_PRECISION, ref number); - if (number.scale == SCALE_NAN) - return info.NaNSymbol; - if (number.scale == SCALE_INF) - return number.sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol; - - if (NumberToDouble(number) == value) - return NumberToString(number, 'G', DOUBLE_PRECISION, info, false); - - DoubleToNumber(value, 17, ref number); - - return NumberToString(number, 'G', 17, info, false); - } - - case 'E': - case 'e': - // Here we round values less than E14 to 15 digits - if (digits > 14) - precision = 17; - break; - - case 'G': - case 'g': - // Here we round values less than G15 to 15 digits, G16 and G17 will not be touched - if (digits > 15) - precision = 17; - break; - } - - DoubleToNumber(value, precision, ref number); - if (number.scale == SCALE_NAN) - return info.NaNSymbol; - if (number.scale == SCALE_INF) - return number.sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol; - - if (fmt != 0) - return NumberToString(number, fmt, digits, info, false); - else - return NumberToStringFormat(number, format, info); - } - - public static String FormatSingle(float value, String format, IFormatProvider provider) - { - NumberFormatInfo info = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - - int digits; - char fmt = ParseFormatSpecifier(format, out digits); - - int precision = FLOAT_PRECISION; - NumberBuffer number = new NumberBuffer(); - - switch (fmt) - { - case 'R': - case 'r': - { - // In order to give numbers that are both friendly to display and round-trippable, we parse - // the number using 7 digits and then determine if it round trips to the same value. If it - // does, we convert that NUMBER to a string, otherwise we reparse using 9 digits and display - // that. - DoubleToNumber(value, FLOAT_PRECISION, ref number); - if (number.scale == SCALE_NAN) - return info.NaNSymbol; - if (number.scale == SCALE_INF) - return number.sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol; - - if ((float)NumberToDouble(number) == value) - return NumberToString(number, 'G', FLOAT_PRECISION, info, false); - - DoubleToNumber(value, 9, ref number); - - return NumberToString(number, 'G', 9, info, false); - } - - case 'E': - case 'e': - // Here we round values less than E14 to 15 digits - if (digits > 6) - precision = 9; - break; - - case 'G': - case 'g': - // Here we round values less than G15 to 15 digits, G16 and G17 will not be touched - if (digits > 7) - precision = 9; - break; - } - - DoubleToNumber(value, precision, ref number); - if (number.scale == SCALE_NAN) - return info.NaNSymbol; - if (number.scale == SCALE_INF) - return number.sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol; - - if (fmt != 0) - return NumberToString(number, fmt, digits, info, false); - else - return NumberToStringFormat(number, format, info); - } - - public static String FormatInt32(int value, String format, IFormatProvider provider) - { - NumberFormatInfo info = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - - int digits; - char fmt = ParseFormatSpecifier(format, out digits); - - // ANDing fmt with FFDF has the effect of uppercasing the character because we've removed the bit - // that marks lower-case. - switch (fmt) - { - case 'G': - case 'g': - if (digits > 0) - { - NumberBuffer number = new NumberBuffer(); - Int32ToNumber(value, ref number); - if (fmt != 0) - return NumberToString(number, fmt, digits, info, false); - return NumberToStringFormat(number, format, info); - } - // fall through - goto case 'D'; - - case 'D': - case 'd': - return Int32ToDecStr(value, digits, info.NegativeSign); - - case 'X': - case 'x': - // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase - // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code - // produces lowercase. - return Int32ToHexStr(value, (char)(fmt - ('X' - 'A' + 10)), digits); - - default: - { - NumberBuffer number = new NumberBuffer(); - Int32ToNumber(value, ref number); - if (fmt != 0) - return NumberToString(number, fmt, digits, info, false); - return NumberToStringFormat(number, format, info); - } - } - } - - public static String FormatUInt32(uint value, String format, IFormatProvider provider) - { - NumberFormatInfo info = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - - int digits; - char fmt = ParseFormatSpecifier(format, out digits); - - // ANDing fmt with FFDF has the effect of uppercasing the character because we've removed the bit - // that marks lower-case. - switch (fmt) - { - case 'G': - case 'g': - if (digits > 0) - { - NumberBuffer number = new NumberBuffer(); - UInt32ToNumber(value, ref number); - if (fmt != 0) - return NumberToString(number, fmt, digits, info, false); - return NumberToStringFormat(number, format, info); - } - // fall through - goto case 'D'; - - case 'D': - case 'd': - return UInt32ToDecStr(value, digits); - - case 'X': - case 'x': - // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase - // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code - // produces lowercase. - return Int32ToHexStr((int)value, (char)(fmt - ('X' - 'A' + 10)), digits); - - default: - { - NumberBuffer number = new NumberBuffer(); - UInt32ToNumber(value, ref number); - if (fmt != 0) - return NumberToString(number, fmt, digits, info, false); - return NumberToStringFormat(number, format, info); - } - } - } - - public static String FormatInt64(long value, String format, IFormatProvider provider) - { - NumberFormatInfo info = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - - int digits; - char fmt = ParseFormatSpecifier(format, out digits); - - // ANDing fmt with FFDF has the effect of uppercasing the character because we've removed the bit - // that marks lower-case. - switch (fmt) - { - case 'G': - case 'g': - if (digits > 0) - { - NumberBuffer number = new NumberBuffer(); - Int64ToNumber(value, ref number); - if (fmt != 0) - return NumberToString(number, fmt, digits, info, false); - return NumberToStringFormat(number, format, info); - } - // fall through - goto case 'D'; - - case 'D': - case 'd': - return Int64ToDecStr(value, digits, info.NegativeSign); - - case 'X': - case 'x': - // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase - // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code - // produces lowercase. - return Int64ToHexStr(value, (char)(fmt - ('X' - 'A' + 10)), digits); - - default: - { - NumberBuffer number = new NumberBuffer(); - Int64ToNumber(value, ref number); - if (fmt != 0) - return NumberToString(number, fmt, digits, info, false); - return NumberToStringFormat(number, format, info); - } - } - } - - public static String FormatUInt64(ulong value, String format, IFormatProvider provider) - { - NumberFormatInfo info = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - - int digits; - char fmt = ParseFormatSpecifier(format, out digits); - - // ANDing fmt with FFDF has the effect of uppercasing the character because we've removed the bit - // that marks lower-case. - switch (fmt) - { - case 'G': - case 'g': - if (digits > 0) - { - NumberBuffer number = new NumberBuffer(); - UInt64ToNumber(value, ref number); - if (fmt != 0) - return NumberToString(number, fmt, digits, info, false); - return NumberToStringFormat(number, format, info); - } - // fall through - goto case 'D'; - - case 'D': - case 'd': - return UInt64ToDecStr(value, digits); - - case 'X': - case 'x': - // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase - // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code - // produces lowercase. - return Int64ToHexStr((long)value, (char)(fmt - ('X' - 'A' + 10)), digits); - - default: - { - NumberBuffer number = new NumberBuffer(); - UInt64ToNumber(value, ref number); - if (fmt != 0) - return NumberToString(number, fmt, digits, info, false); - return NumberToStringFormat(number, format, info); - } - } - } - - internal static Boolean NumberBufferToDouble(NumberBuffer number, ref Double value) - { - double d = NumberToDouble(number); - - uint e = DoubleHelper.Exponent(d); - ulong m = DoubleHelper.Mantissa(d); - if (e == 0x7FF) - return false; - if (e == 0 && m == 0) - d = 0; - - value = d; - - return true; - } - - internal static unsafe void Int32ToDecChars(char[] buffer, ref int index, uint value, int digits) - { - while (--digits >= 0 || value != 0) - { - buffer[--index] = (char)(value % 10 + '0'); - value /= 10; - } - } - - public static bool IsPositiveInfinity(string s, IFormatProvider provider) - { - NumberFormatInfo nfi = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - return s.Equals(nfi.PositiveInfinitySymbol); - } - - public static bool IsNegativeInfinity(string s, IFormatProvider provider) - { - NumberFormatInfo nfi = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - return s.Equals(nfi.NegativeInfinitySymbol); - } - - public static bool IsNaNSymbol(string s, IFormatProvider provider) - { - NumberFormatInfo nfi = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); - return s.Equals(nfi.NaNSymbol); - } - - #region Decimal Number Formatting Helpers - private static unsafe bool NumberBufferToDecimal(Number.NumberBuffer number, ref Decimal value) - { - Decimal d = new Decimal(); - - char* p = number.digits; - int e = number.scale; - if (*p == 0) - { - // To avoid risking an app-compat issue with pre 4.5 (where some app was illegally using Reflection to examine the internal scale bits), we'll only force - // the scale to 0 if the scale was previously positive (previously, such cases were unparsable to a bug.) - if (e > 0) - { - e = 0; - } - } - else - { - if (e > DECIMAL_PRECISION) - return false; - - while (((e > 0) || ((*p != 0) && (e > -28))) && - ((d.High < 0x19999999) || ((d.High == 0x19999999) && - ((d.Mid < 0x99999999) || ((d.Mid == 0x99999999) && - ((d.Low < 0x99999999) || ((d.Low == 0x99999999) && - (*p <= '5')))))))) - { - Decimal.DecMul10(ref d); - if (*p != 0) - Decimal.DecAddInt32(ref d, (uint)(*p++ - '0')); - e--; - } - - if (*p++ >= '5') - { - bool round = true; - if ((*(p - 1) == '5') && ((*(p - 2) % 2) == 0)) - { - // Check if previous digit is even, only if the when we are unsure whether hows to do - // Banker's rounding. For digits > 5 we will be roundinp up anyway. - int count = 20; // Look at the next 20 digits to check to round - while ((*p == '0') && (count != 0)) - { - p++; - count--; - } - if ((*p == '\0') || (count == 0)) - round = false;// Do nothing - } - - if (round) - { - Decimal.DecAddInt32(ref d, 1); - if ((d.High | d.Mid | d.Low) == 0) - { - d.High = 0x19999999; - d.Mid = 0x99999999; - d.Low = 0x9999999A; - e++; - } - } - } - } - - if (e > 0) - return false; - - if (e <= -DECIMAL_PRECISION) - { - // Parsing a large scale zero can give you more precision than fits in the decimal. - // This should only happen for actual zeros or very small numbers that round to zero. - d.High = 0; - d.Low = 0; - d.Mid = 0; - d.Scale = DECIMAL_PRECISION - 1; - } - else - { - d.Scale = -e; - } - d.Sign = number.sign; - - value = d; - return true; - } - - private static unsafe void DecimalToNumber(Decimal value, ref Number.NumberBuffer number) - { - Decimal d = value; - - char* buffer = number.digits; - number.precision = DECIMAL_PRECISION; - number.sign = d.Sign; - - int index = DECIMAL_PRECISION; - while (d.Mid != 0 | d.High != 0) - Number.Int32ToDecChars(buffer, ref index, Decimal.DecDivMod1E9(ref d), 9); - - Number.Int32ToDecChars(buffer, ref index, d.Low, 0); - - int i = DECIMAL_PRECISION - index; - number.scale = i - d.Scale; - - char* dst = number.digits; - while (--i >= 0) - *dst++ = buffer[index++]; - *dst = '\0'; - } - - #endregion - - /*=========================================================== - Portable NumberToDouble implementation - -------------------------------------- - - - does the conversion with the best possible precision. - - does not use any float arithmetic so it is not sensitive - to differences in precision of floating point calculations - across platforms. - - The internal integer representation of the float number is - UINT64 mantissa + INT exponent. The mantissa is kept normalized - ie with the most significant one being 63-th bit of UINT64. - ===========================================================*/ - - // - // get 32-bit integer from at most 9 digits - // - private static unsafe uint DigitsToInt(char* p, int count) - { - char* end = p + count; - uint res = (uint)*p - '0'; - for (p = p + 1; p < end; p++) - res = 10 * res + (uint)*p - '0'; - return res; - } - - // - // helper to multiply two 32-bit uints - // - private static ulong Mul32x32To64(uint a, uint b) - { - return (ulong)a * (ulong)b; - } - - // - // multiply two numbers in the internal integer representation - // - private static ulong Mul64Lossy(ulong a, ulong b, ref int pexp) - { - // it's ok to lose some precision here - Mul64 will be called - // at most twice during the conversion, so the error won't propagate - // to any of the 53 significant bits of the result - ulong val = Mul32x32To64((uint)(a >> 32), (uint)(b >> 32)) + - (Mul32x32To64((uint)(a >> 32), (uint)(b)) >> 32) + - (Mul32x32To64((uint)(a), (uint)(b >> 32)) >> 32); - - // normalize - if ((val & 0x8000000000000000) == 0) - { - val <<= 1; - pexp -= 1; - } - - return val; - } - - // - // precomputed tables with powers of 10. These allows us to do at most - // two Mul64 during the conversion. This is important not only - // for speed, but also for precision because of Mul64 computes with 1 bit error. - // - - private static readonly ulong[] s_rgval64Power10 = - { - // powers of 10 - /*1*/ 0xa000000000000000, - /*2*/ 0xc800000000000000, - /*3*/ 0xfa00000000000000, - /*4*/ 0x9c40000000000000, - /*5*/ 0xc350000000000000, - /*6*/ 0xf424000000000000, - /*7*/ 0x9896800000000000, - /*8*/ 0xbebc200000000000, - /*9*/ 0xee6b280000000000, - /*10*/ 0x9502f90000000000, - /*11*/ 0xba43b74000000000, - /*12*/ 0xe8d4a51000000000, - /*13*/ 0x9184e72a00000000, - /*14*/ 0xb5e620f480000000, - /*15*/ 0xe35fa931a0000000, - - // powers of 0.1 - /*1*/ 0xcccccccccccccccd, - /*2*/ 0xa3d70a3d70a3d70b, - /*3*/ 0x83126e978d4fdf3c, - /*4*/ 0xd1b71758e219652e, - /*5*/ 0xa7c5ac471b478425, - /*6*/ 0x8637bd05af6c69b7, - /*7*/ 0xd6bf94d5e57a42be, - /*8*/ 0xabcc77118461ceff, - /*9*/ 0x89705f4136b4a599, - /*10*/ 0xdbe6fecebdedd5c2, - /*11*/ 0xafebff0bcb24ab02, - /*12*/ 0x8cbccc096f5088cf, - /*13*/ 0xe12e13424bb40e18, - /*14*/ 0xb424dc35095cd813, - /*15*/ 0x901d7cf73ab0acdc, - }; - - private static readonly sbyte[] s_rgexp64Power10 = - { - // exponents for both powers of 10 and 0.1 - /*1*/ 4, - /*2*/ 7, - /*3*/ 10, - /*4*/ 14, - /*5*/ 17, - /*6*/ 20, - /*7*/ 24, - /*8*/ 27, - /*9*/ 30, - /*10*/ 34, - /*11*/ 37, - /*12*/ 40, - /*13*/ 44, - /*14*/ 47, - /*15*/ 50, - }; - - private static readonly ulong[] s_rgval64Power10By16 = - { - // powers of 10^16 - /*1*/ 0x8e1bc9bf04000000, - /*2*/ 0x9dc5ada82b70b59e, - /*3*/ 0xaf298d050e4395d6, - /*4*/ 0xc2781f49ffcfa6d4, - /*5*/ 0xd7e77a8f87daf7fa, - /*6*/ 0xefb3ab16c59b14a0, - /*7*/ 0x850fadc09923329c, - /*8*/ 0x93ba47c980e98cde, - /*9*/ 0xa402b9c5a8d3a6e6, - /*10*/ 0xb616a12b7fe617a8, - /*11*/ 0xca28a291859bbf90, - /*12*/ 0xe070f78d39275566, - /*13*/ 0xf92e0c3537826140, - /*14*/ 0x8a5296ffe33cc92c, - /*15*/ 0x9991a6f3d6bf1762, - /*16*/ 0xaa7eebfb9df9de8a, - /*17*/ 0xbd49d14aa79dbc7e, - /*18*/ 0xd226fc195c6a2f88, - /*19*/ 0xe950df20247c83f8, - /*20*/ 0x81842f29f2cce373, - /*21*/ 0x8fcac257558ee4e2, - - // powers of 0.1^16 - /*1*/ 0xe69594bec44de160, - /*2*/ 0xcfb11ead453994c3, - /*3*/ 0xbb127c53b17ec165, - /*4*/ 0xa87fea27a539e9b3, - /*5*/ 0x97c560ba6b0919b5, - /*6*/ 0x88b402f7fd7553ab, - /*7*/ 0xf64335bcf065d3a0, - /*8*/ 0xddd0467c64bce4c4, - /*9*/ 0xc7caba6e7c5382ed, - /*10*/ 0xb3f4e093db73a0b7, - /*11*/ 0xa21727db38cb0053, - /*12*/ 0x91ff83775423cc29, - /*13*/ 0x8380dea93da4bc82, - /*14*/ 0xece53cec4a314f00, - /*15*/ 0xd5605fcdcf32e217, - /*16*/ 0xc0314325637a1978, - /*17*/ 0xad1c8eab5ee43ba2, - /*18*/ 0x9becce62836ac5b0, - /*19*/ 0x8c71dcd9ba0b495c, - /*20*/ 0xfd00b89747823938, - /*21*/ 0xe3e27a444d8d991a, - }; - - private static readonly short[] s_rgexp64Power10By16 = - { - // exponents for both powers of 10^16 and 0.1^16 - /*1*/ 54, - /*2*/ 107, - /*3*/ 160, - /*4*/ 213, - /*5*/ 266, - /*6*/ 319, - /*7*/ 373, - /*8*/ 426, - /*9*/ 479, - /*10*/ 532, - /*11*/ 585, - /*12*/ 638, - /*13*/ 691, - /*14*/ 745, - /*15*/ 798, - /*16*/ 851, - /*17*/ 904, - /*18*/ 957, - /*19*/ 1010, - /*20*/ 1064, - /*21*/ 1117, - }; - - private static int abs(int value) - { - if (value < 0) - return -value; - return value; - } - - private static unsafe double NumberToDouble(NumberBuffer number) - { - ulong val; - int exp; - char* src = number.digits; - int remaining; - int total; - int count; - int scale; - int absscale; - int index; - - total = wcslen(src); - remaining = total; - - // skip the leading zeros - while (*src == '0') - { - remaining--; - src++; - } - - if (remaining == 0) - return 0; - - count = Math.Min(remaining, 9); - remaining -= count; - val = DigitsToInt(src, count); - - if (remaining > 0) - { - count = Math.Min(remaining, 9); - remaining -= count; - - // get the denormalized power of 10 - uint mult = (uint)(s_rgval64Power10[count - 1] >> (64 - s_rgexp64Power10[count - 1])); - val = Mul32x32To64((uint)val, mult) + DigitsToInt(src + 9, count); - } - - scale = number.scale - (total - remaining); - absscale = abs(scale); - if (absscale >= 22 * 16) - { - // overflow / underflow - ulong result = (scale > 0) ? 0x7FF0000000000000 : 0ul; - if (number.sign) - result |= 0x8000000000000000; - return *(double*)&result; - } - - exp = 64; - - // normalize the mantissa - if ((val & 0xFFFFFFFF00000000) == 0) { val <<= 32; exp -= 32; } - if ((val & 0xFFFF000000000000) == 0) { val <<= 16; exp -= 16; } - if ((val & 0xFF00000000000000) == 0) { val <<= 8; exp -= 8; } - if ((val & 0xF000000000000000) == 0) { val <<= 4; exp -= 4; } - if ((val & 0xC000000000000000) == 0) { val <<= 2; exp -= 2; } - if ((val & 0x8000000000000000) == 0) { val <<= 1; exp -= 1; } - - index = absscale & 15; - if (index != 0) - { - int multexp = s_rgexp64Power10[index - 1]; - // the exponents are shared between the inverted and regular table - exp += (scale < 0) ? (-multexp + 1) : multexp; - - ulong multval = s_rgval64Power10[index + ((scale < 0) ? 15 : 0) - 1]; - val = Mul64Lossy(val, multval, ref exp); - } - - index = absscale >> 4; - if (index != 0) - { - int multexp = s_rgexp64Power10By16[index - 1]; - // the exponents are shared between the inverted and regular table - exp += (scale < 0) ? (-multexp + 1) : multexp; - - ulong multval = s_rgval64Power10By16[index + ((scale < 0) ? 21 : 0) - 1]; - val = Mul64Lossy(val, multval, ref exp); - } - - - // round & scale down - if (((int)val & (1 << 10)) != 0) - { - // IEEE round to even - ulong tmp = val + ((1 << 10) - 1) + (ulong)(((int)val >> 11) & 1); - if (tmp < val) - { - // overflow - tmp = (tmp >> 1) | 0x8000000000000000; - exp += 1; - } - val = tmp; - } - - // return the exponent to a biased state - exp += 0x3FE; - - // handle overflow, underflow, "Epsilon - 1/2 Epsilon", denormalized, and the normal case - if (exp <= 0) - { - if (exp == -52 && (val >= 0x8000000000000058)) - { - // round X where {Epsilon > X >= 2.470328229206232730000000E-324} up to Epsilon (instead of down to zero) - val = 0x0000000000000001; - } - else if (exp <= -52) - { - // underflow - val = 0; - } - else - { - // denormalized - val >>= (-exp + 11 + 1); - } - } - else if (exp >= 0x7FF) - { - // overflow - val = 0x7FF0000000000000; - } - else - { - // normal postive exponent case - val = ((ulong)exp << 52) + ((val >> 11) & 0x000FFFFFFFFFFFFF); - } - - if (number.sign) - val |= 0x8000000000000000; - - return *(double*)&val; - } - - private static unsafe void Int32ToNumber(int value, ref NumberBuffer number) - { - number.precision = INT32_PRECISION; - - if (value >= 0) - { - number.sign = false; - } - else - { - number.sign = true; - value = -value; - } - - char* buffer = number.digits; - int index = INT32_PRECISION; - Int32ToDecChars(buffer, ref index, (uint)value, 0); - int i = INT32_PRECISION - index; - - number.scale = i; - - char* dst = number.digits; - while (--i >= 0) - *dst++ = buffer[index++]; - *dst = '\0'; - } - - private static unsafe string Int32ToDecStr(int value, int digits, string sNegative) - { - if (digits < 1) - digits = 1; - - int maxDigitsLength = (digits > 15) ? digits : 15; // Since an int32 can have maximum of 10 chars as a String - int bufferLength = (maxDigitsLength > 100) ? maxDigitsLength : 100; - int negLength = 0; - string src = null; - - if (value < 0) - { - src = sNegative; - negLength = sNegative.Length; - if (negLength > bufferLength - maxDigitsLength) - bufferLength = negLength + maxDigitsLength; - } - - char* buffer = stackalloc char[bufferLength]; - - int index = bufferLength; - Int32ToDecChars(buffer, ref index, (uint)(value >= 0 ? value : -value), digits); - - if (value < 0) - { - for (int i = negLength - 1; i >= 0; i--) - buffer[--index] = src[i]; - } - - return new string(buffer, index, bufferLength - index); - } - - private static string Int32ToHexStr(int value, char hexBase, int digits) - { - if (digits < 1) - digits = 1; - char[] buffer = new char[100]; - int index = 100; - Int32ToHexChars(buffer, ref index, (uint)value, hexBase, digits); - return new string(buffer, index, 100 - index); - } - - private static void Int32ToHexChars(char[] buffer, ref int index, uint value, int hexBase, int digits) - { - while (--digits >= 0 || value != 0) - { - byte digit = (byte)(value & 0xF); - buffer[--index] = (char)(digit + (digit < 10 ? (byte)'0' : hexBase)); - value >>= 4; - } - } - - private static unsafe void UInt32ToNumber(uint value, ref NumberBuffer number) - { - number.precision = UINT32_PRECISION; - number.sign = false; - - char* buffer = number.digits; - int index = UINT32_PRECISION; - Int32ToDecChars(buffer, ref index, value, 0); - int i = UINT32_PRECISION - index; - - number.scale = i; - - char* dst = number.digits; - while (--i >= 0) - *dst++ = buffer[index++]; - *dst = '\0'; - } - - private static unsafe string UInt32ToDecStr(uint value, int digits) - { - if (digits < 1) - digits = 1; - - char* buffer = stackalloc char[100]; - int index = 100; - Int32ToDecChars(buffer, ref index, value, digits); - - return new string(buffer, index, 100 - index); - } - - private static unsafe void Int64ToNumber(long input, ref NumberBuffer number) - { - ulong value = (ulong)input; - number.sign = input < 0; - number.precision = INT64_PRECISION; - if (number.sign) - { - value = (ulong)(-input); - } - - char* buffer = number.digits; - int index = INT64_PRECISION; - while (High32(value) != 0) - Int32ToDecChars(buffer, ref index, Int64DivMod1E9(ref value), 9); - Int32ToDecChars(buffer, ref index, Low32(value), 0); - int i = INT64_PRECISION - index; - - number.scale = i; - - char* dst = number.digits; - while (--i >= 0) - *dst++ = buffer[index++]; - *dst = '\0'; - } - - private static uint Low32(ulong value) - { - return (uint)value; - } - - private static uint High32(ulong value) - { - return (uint)(((ulong)value & 0xFFFFFFFF00000000) >> 32); - } - - private static uint Int64DivMod1E9(ref ulong value) - { - uint rem = (uint)(value % 1000000000); - value /= 1000000000; - return rem; - } - - private static unsafe string Int64ToDecStr(long input, int digits, string sNegative) - { - if (digits < 1) - digits = 1; - - ulong value = (ulong)input; - int sign = (int)High32(value); - - // digits as specified in the format string can be at most 99. - int maxDigitsLength = (digits > 20) ? digits : 20; - int bufferLength = (maxDigitsLength > 100) ? maxDigitsLength : 100; - - if (sign < 0) - { - value = (ulong)(-input); - int negLength = sNegative.Length; - if (negLength > bufferLength - maxDigitsLength) - bufferLength = negLength + maxDigitsLength; - } - - char* buffer = stackalloc char[bufferLength]; - int index = bufferLength; - while (High32(value) != 0) - { - Int32ToDecChars(buffer, ref index, Int64DivMod1E9(ref value), 9); - digits -= 9; - } - Int32ToDecChars(buffer, ref index, Low32(value), digits); - - if (sign < 0) - { - for (int i = sNegative.Length - 1; i >= 0; i--) - buffer[--index] = sNegative[i]; - } - - return new string(buffer, index, bufferLength - index); - } - - private static string Int64ToHexStr(long value, char hexBase, int digits) - { - char[] buffer = new char[100]; - int index = 100; - - if (High32((ulong)value) != 0) - { - Int32ToHexChars(buffer, ref index, Low32((ulong)value), hexBase, 8); - Int32ToHexChars(buffer, ref index, High32((ulong)value), hexBase, digits - 8); - } - else - { - if (digits < 1) - digits = 1; - Int32ToHexChars(buffer, ref index, Low32((ulong)value), hexBase, digits); - } - - return new string(buffer, index, 100 - index); - } - - private static unsafe void UInt64ToNumber(ulong value, ref NumberBuffer number) - { - number.precision = UINT64_PRECISION; - number.sign = false; - - char* buffer = number.digits; - int index = UINT64_PRECISION; - - while (High32(value) != 0) - Int32ToDecChars(buffer, ref index, Int64DivMod1E9(ref value), 9); - Int32ToDecChars(buffer, ref index, Low32(value), 0); - int i = UINT64_PRECISION - index; - - number.scale = i; - - char* dst = number.digits; - while (--i >= 0) - *dst++ = buffer[index++]; - *dst = '\0'; - } - - private static unsafe string UInt64ToDecStr(ulong value, int digits) - { - if (digits < 1) - digits = 1; - - char* buffer = stackalloc char[100]; - int index = 100; - while (High32(value) != 0) - { - Int32ToDecChars(buffer, ref index, Int64DivMod1E9(ref value), 9); - digits -= 9; - } - Int32ToDecChars(buffer, ref index, Low32(value), digits); - - return new string(buffer, index, 100 - index); - } - - private static class DoubleHelper - { - public static unsafe uint Exponent(double d) - { - return (*((uint*)&d + 1) >> 20) & 0x000007ff; - } - - public static unsafe ulong Mantissa(double d) - { - return (ulong)*((uint*)&d) | ((ulong)(*((uint*)&d + 1) & 0x000fffff) << 32); - } - - public static unsafe bool Sign(double d) - { - return (*((uint*)&d + 1) >> 31) != 0; - } - } - } - } -} - - diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/FormatProvider.NumberBuffer.cs b/external/corert/src/System.Private.CoreLib/src/System/Globalization/FormatProvider.NumberBuffer.cs deleted file mode 100644 index b36a6565b8..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/FormatProvider.NumberBuffer.cs +++ /dev/null @@ -1,44 +0,0 @@ -// 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.Globalization; -using System.Runtime; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Text; -using System.Security; -using System.Diagnostics.Contracts; - -namespace System.Globalization -{ - internal partial class FormatProvider - { - private partial class Number - { - // WARNING: Don't allocate these on the heap, the "digits" property will return an unmanaged pointer - // to an interior character array. - [System.Runtime.CompilerServices.StackOnly] - [StructLayout(LayoutKind.Sequential)] - internal unsafe struct NumberBuffer - { - public Int32 precision; - public Int32 scale; - public Boolean sign; - - // Inline array of NumberMaxDigits characters. - private fixed char buffer[32]; - - public char* digits - { - get - { - // This is only safe if the caller allocated the NumberBuffer on the stack or pinned it. - return ((NumberBuffer*)Unsafe.AsPointer(ref this))->buffer; - } - } - } - } - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/FormatProvider.cs b/external/corert/src/System.Private.CoreLib/src/System/Globalization/FormatProvider.cs deleted file mode 100644 index c321361346..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/FormatProvider.cs +++ /dev/null @@ -1,208 +0,0 @@ -// 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; - -namespace System.Globalization -{ - // Internal Contract for all Globalization APIs that are needed by lower levels of System.Private.CoreLib. - // This is class acts as a gateway between everything in System.Private.CoreLib and System.Globalization. - internal partial class FormatProvider - { - public static IFormatProvider InvariantCulture { get { return CultureInfo.InvariantCulture; } } - - #region Char/String Conversions - public static string ToLower(string s) - { - return CultureInfo.CurrentCulture.TextInfo.ToLower(s); - } - public static string ToLowerInvariant(string s) - { - return CultureInfo.InvariantCulture.TextInfo.ToLower(s); - } - public static string ToUpper(string s) - { - return CultureInfo.CurrentCulture.TextInfo.ToUpper(s); - } - public static string ToUpperInvariant(string s) - { - return CultureInfo.InvariantCulture.TextInfo.ToUpper(s); - } - #endregion - - #region Culture Comparisons - public static int GetHashCodeInvariantIgnoreCase(string source) - { - return CultureInfo.InvariantCulture.CompareInfo.GetHashCodeOfString(source, CompareOptions.IgnoreCase); - } - public static int GetHashCodeOrdinalIgnoreCase(string source) - { - return TextInfo.GetHashCodeOrdinalIgnoreCase(source); - } - public static int Compare(String string1, int offset1, int length1, String string2, int offset2, int length2) - { - return CultureInfo.CurrentCulture.CompareInfo.Compare(string1, offset1, length1, string2, offset2, length2, CompareOptions.None); - } - public static int CompareIgnoreCase(String string1, int offset1, int length1, String string2, int offset2, int length2) - { - return CultureInfo.CurrentCulture.CompareInfo.Compare(string1, offset1, length1, string2, offset2, length2, CompareOptions.IgnoreCase); - } - public static int CompareOrdinalIgnoreCase(String string1, int offset1, int length1, String string2, int offset2, int length2) - { - return CompareInfo.CompareOrdinalIgnoreCase(string1, offset1, length1, string2, offset2, length2); - } - public static int IndexOf(String source, String value, int startIndex, int count) - { - return CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, startIndex, count, CompareOptions.None); - } - public static int IndexOfIgnoreCase(String source, String value, int startIndex, int count) - { - return CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, startIndex, count, CompareOptions.IgnoreCase); - } - public static bool IsPrefix(String source, String prefix) - { - return CultureInfo.CurrentCulture.CompareInfo.IsPrefix(source, prefix, CompareOptions.None); - } - public static bool IsPrefixIgnoreCase(String source, String prefix) - { - return CultureInfo.CurrentCulture.CompareInfo.IsPrefix(source, prefix, CompareOptions.IgnoreCase); - } - public static bool IsSuffix(String source, String suffix) - { - return CultureInfo.CurrentCulture.CompareInfo.IsSuffix(source, suffix, CompareOptions.None); - } - public static bool IsSuffixIgnoreCase(String source, String suffix) - { - return CultureInfo.CurrentCulture.CompareInfo.IsSuffix(source, suffix, CompareOptions.IgnoreCase); - } - public static int LastIndexOf(String source, String value, int startIndex, int count) - { - return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(source, value, startIndex, count, CompareOptions.None); - } - public static int LastIndexOfIgnoreCase(String source, String value, int startIndex, int count) - { - return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(source, value, startIndex, count, CompareOptions.IgnoreCase); - } - public static int OrdinalIndexOf(String source, String value, int startIndex, int count) - { - return CultureInfo.InvariantCulture.CompareInfo.IndexOf(source, value, startIndex, count, CompareOptions.Ordinal); - } - public static int OrdinalIndexOfIgnoreCase(String source, String value, int startIndex, int count) - { - return TextInfo.IndexOfStringOrdinalIgnoreCase(source, value, startIndex, count); - } - public static int OrdinalLastIndexOf(String source, String value, int startIndex, int count) - { - return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf(source, value, startIndex, count, CompareOptions.Ordinal); - } - public static int OrdinalLastIndexOfIgnoreCase(String source, String value, int startIndex, int count) - { - return TextInfo.LastIndexOfStringOrdinalIgnoreCase(source, value, startIndex, count); - } - #endregion - - #region Formatting - // provider if null means we use NumberFormatInfo.CurrenctInfo otherwise we use NumberFormatInfo.GetInstance(provider) - public static String FormatDecimal(Decimal value, String format, IFormatProvider provider) - { - return FormatProvider.Number.FormatDecimal(value, format, provider); - } - public static String FormatDouble(double value, String format, IFormatProvider provider) - { - return FormatProvider.Number.FormatDouble(value, format, provider); - } - public static String FormatInt32(int value, String format, IFormatProvider provider) - { - return FormatProvider.Number.FormatInt32(value, format, provider); - } - public static String FormatInt64(long value, String format, IFormatProvider provider) - { - return FormatProvider.Number.FormatInt64(value, format, provider); - } - public static String FormatSingle(float value, String format, IFormatProvider provider) - { - return FormatProvider.Number.FormatSingle(value, format, provider); - } - public static String FormatUInt32(uint value, String format, IFormatProvider provider) - { - return FormatProvider.Number.FormatUInt32(value, format, provider); - } - public static String FormatUInt64(ulong value, String format, IFormatProvider provider) - { - return FormatProvider.Number.FormatUInt64(value, format, provider); - } - #endregion - - #region Parsing - public static Decimal ParseDecimal(String value, NumberStyles options, IFormatProvider provider) - { - return FormatProvider.Number.ParseDecimal(value, options, provider); - } - public static Double ParseDouble(String value, NumberStyles options, IFormatProvider provider) - { - return FormatProvider.Number.ParseDouble(value, options, provider); - } - public static int ParseInt32(String s, NumberStyles styles, IFormatProvider provider) - { - return FormatProvider.Number.ParseInt32(s, styles, provider); - } - public static Int64 ParseInt64(String value, NumberStyles options, IFormatProvider provider) - { - return FormatProvider.Number.ParseInt64(value, options, provider); - } - public static Single ParseSingle(String value, NumberStyles options, IFormatProvider provider) - { - return FormatProvider.Number.ParseSingle(value, options, provider); - } - public static UInt32 ParseUInt32(String value, NumberStyles options, IFormatProvider provider) - { - return FormatProvider.Number.ParseUInt32(value, options, provider); - } - public static UInt64 ParseUInt64(String value, NumberStyles options, IFormatProvider provider) - { - return FormatProvider.Number.ParseUInt64(value, options, provider); - } - public static Boolean TryParseDecimal(String value, NumberStyles options, IFormatProvider provider, out Decimal result) - { - return FormatProvider.Number.TryParseDecimal(value, options, provider, out result); - } - public static Boolean TryParseDouble(String value, NumberStyles options, IFormatProvider provider, out Double result) - { - return FormatProvider.Number.TryParseDouble(value, options, provider, out result); - } - public static Boolean TryParseInt32(String s, NumberStyles style, IFormatProvider provider, out Int32 result) - { - return FormatProvider.Number.TryParseInt32(s, style, provider, out result); - } - public static Boolean TryParseInt64(String s, NumberStyles style, IFormatProvider provider, out Int64 result) - { - return FormatProvider.Number.TryParseInt64(s, style, provider, out result); - } - public static Boolean TryParseSingle(String value, NumberStyles options, IFormatProvider provider, out Single result) - { - return FormatProvider.Number.TryParseSingle(value, options, provider, out result); - } - public static Boolean TryParseUInt32(String s, NumberStyles style, IFormatProvider provider, out UInt32 result) - { - return FormatProvider.Number.TryParseUInt32(s, style, provider, out result); - } - public static Boolean TryParseUInt64(String s, NumberStyles style, IFormatProvider provider, out UInt64 result) - { - return FormatProvider.Number.TryParseUInt64(s, style, provider, out result); - } - public static bool IsPositiveInfinity(string s, IFormatProvider provider) - { - return FormatProvider.Number.IsPositiveInfinity(s, provider); - } - public static bool IsNegativeInfinity(string s, IFormatProvider provider) - { - return FormatProvider.Number.IsNegativeInfinity(s, provider); - } - public static bool IsNaNSymbol(string s, IFormatProvider provider) - { - return FormatProvider.Number.IsNaNSymbol(s, provider); - } - #endregion - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs b/external/corert/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs new file mode 100644 index 0000000000..e4ab832d5e --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs @@ -0,0 +1,26 @@ +// 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. + +namespace System.Globalization +{ + internal static partial class GlobalizationMode + { + private static bool GetGlobalizationInvariantMode() + { + // CORERT-TODO: Enable System.Globalization.Invariant switch + // bool invariantEnabled = CLRConfig.GetBoolValue(c_InvariantModeConfigSwitch); + bool invariantEnabled = false; + if (!invariantEnabled) + { + if (Interop.GlobalizationInterop.LoadICU() == 0) + { + string message = "Couldn't find a valid ICU package installed on the system. " + + "Set the configuration flag System.Globalization.Invariant to true if you want to run with no globalization support."; + Environment.FailFast(message); + } + } + return invariantEnabled; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs new file mode 100644 index 0000000000..7c6bf01615 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs @@ -0,0 +1,16 @@ +// 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. + +namespace System.Globalization +{ + internal static partial class GlobalizationMode + { + private static bool GetGlobalizationInvariantMode() + { + // CORERT-TODO: Enable System.Globalization.Invariant switch + // return CLRConfig.GetBoolValue(c_InvariantModeConfigSwitch); + return false; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs b/external/corert/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs index ea56795c9f..266774d911 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs @@ -4,8 +4,9 @@ namespace System.Globalization { - internal static class GlobalizationMode + internal static partial class GlobalizationMode { - internal static bool Invariant { get; } = false; + private const string c_InvariantModeConfigSwitch = "System.Globalization.Invariant"; + internal static bool Invariant { get; } = GetGlobalizationInvariantMode(); } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/IdnMapping.cs b/external/corert/src/System.Private.CoreLib/src/System/Globalization/IdnMapping.cs deleted file mode 100644 index 8424472751..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/IdnMapping.cs +++ /dev/null @@ -1,152 +0,0 @@ -// 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. - -// This file contains the IDN functions and implementation. -// -// This allows encoding of non-ASCII domain names in a "punycode" form, -// for example: -// -// \u5B89\u5BA4\u5948\u7F8E\u6075-with-SUPER-MONKEYS -// -// is encoded as: -// -// xn---with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n -// -// Additional options are provided to allow unassigned IDN characters and -// to validate according to the Std3ASCII Rules (like DNS names). -// -// There are also rules regarding bidirectionality of text and the length -// of segments. -// -// For additional rules see also: -// RFC 3490 - Internationalizing Domain Names in Applications (IDNA) -// RFC 3491 - Nameprep: A Stringprep Profile for Internationalized Domain Names (IDN) -// RFC 3492 - Punycode: A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA) - -using System.Diagnostics.Contracts; - -namespace System.Globalization -{ - // IdnMapping class used to map names to Punycode - public sealed partial class IdnMapping - { - private bool _allowUnassigned; - private bool _useStd3AsciiRules; - - public IdnMapping() - { - } - - public bool AllowUnassigned - { - get { return _allowUnassigned; } - set { _allowUnassigned = value; } - } - - public bool UseStd3AsciiRules - { - get { return _useStd3AsciiRules; } - set { _useStd3AsciiRules = value; } - } - - // Gets ASCII (Punycode) version of the string - public string GetAscii(string unicode) - { - return GetAscii(unicode, 0); - } - - public string GetAscii(string unicode, int index) - { - if (unicode == null) - throw new ArgumentNullException(nameof(unicode)); - Contract.EndContractBlock(); - return GetAscii(unicode, index, unicode.Length - index); - } - - public string GetAscii(string unicode, int index, int count) - { - if (unicode == null) - throw new ArgumentNullException(nameof(unicode)); - if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0) ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - if (index > unicode.Length) - throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); - if (index > unicode.Length - count) - throw new ArgumentOutOfRangeException(nameof(unicode), SR.ArgumentOutOfRange_IndexCountBuffer); - Contract.EndContractBlock(); - - if (count == 0) - { - throw new ArgumentException(SR.Argument_IdnBadLabelSize, nameof(unicode)); - } - if (unicode[index + count - 1] == 0) - { - throw new ArgumentException(SR.Format(SR.Argument_InvalidCharSequence, index + count - 1), nameof(unicode)); - } - - unsafe - { - fixed (char* pUnicode = unicode) - { - return GetAsciiCore(pUnicode + index, count); - } - } - } - - // Gets Unicode version of the string. Normalized and limited to IDNA characters. - public string GetUnicode(string ascii) - { - return GetUnicode(ascii, 0); - } - - public string GetUnicode(string ascii, int index) - { - if (ascii == null) - throw new ArgumentNullException(nameof(ascii)); - Contract.EndContractBlock(); - return GetUnicode(ascii, index, ascii.Length - index); - } - - public string GetUnicode(string ascii, int index, int count) - { - if (ascii == null) - throw new ArgumentNullException(nameof(ascii)); - if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0) ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - if (index > ascii.Length) - throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); - if (index > ascii.Length - count) - throw new ArgumentOutOfRangeException(nameof(ascii), SR.ArgumentOutOfRange_IndexCountBuffer); - - // This is a case (i.e. explicitly null-terminated input) where behavior in .NET and Win32 intentionally differ. - // The .NET APIs should (and did in v4.0 and earlier) throw an ArgumentException on input that includes a terminating null. - // The Win32 APIs fail on an embedded null, but not on a terminating null. - if (count > 0 && ascii[index + count - 1] == (char)0) - throw new ArgumentException(SR.Argument_IdnBadPunycode, nameof(ascii)); - Contract.EndContractBlock(); - - unsafe - { - fixed (char* pAscii = ascii) - { - return GetUnicodeCore(pAscii + index, count); - } - } - } - - public override bool Equals(object obj) - { - IdnMapping that = obj as IdnMapping; - return - that != null && - _allowUnassigned == that._allowUnassigned && - _useStd3AsciiRules == that._useStd3AsciiRules; - } - - public override int GetHashCode() - { - return (_allowUnassigned ? 100 : 200) + (_useStd3AsciiRules ? 1000 : 2000); - } - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/Normalization.Dummy.cs b/external/corert/src/System.Private.CoreLib/src/System/Globalization/Normalization.Dummy.cs new file mode 100644 index 0000000000..c051ff0940 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Globalization/Normalization.Dummy.cs @@ -0,0 +1,21 @@ +// 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.Text; + +namespace System.Globalization +{ + internal static partial class Normalization + { + internal static bool IsNormalized(string strInput, NormalizationForm normalizationForm) + { + return true; + } + + internal static string Normalize(string strInput, NormalizationForm normalizationForm) + { + return strInput; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/TextInfo.Dummy.cs b/external/corert/src/System.Private.CoreLib/src/System/Globalization/TextInfo.Dummy.cs index a3c4debeb1..e1a1255524 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/TextInfo.Dummy.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Globalization/TextInfo.Dummy.cs @@ -2,20 +2,11 @@ // 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.Diagnostics.Contracts; - namespace System.Globalization { public partial class TextInfo { - internal unsafe TextInfo(CultureData cultureData) - { - _cultureData = cultureData; - _cultureName = _cultureData.CultureName; - _textInfoName = _cultureData.STEXTINFO; - } - - private unsafe void FinishInitialization(string textInfoName) + private unsafe void FinishInitialization() { } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/TextInfo.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/Globalization/TextInfo.Windows.cs deleted file mode 100644 index 052e95a772..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/TextInfo.Windows.cs +++ /dev/null @@ -1,121 +0,0 @@ -// 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.Diagnostics; - -namespace System.Globalization -{ - public partial class TextInfo - { - ////////////////////////////////////////////////////////////////////////// - //// - //// TextInfo Constructors - //// - //// Implements CultureInfo.TextInfo. - //// - ////////////////////////////////////////////////////////////////////////// - internal unsafe TextInfo(CultureData cultureData) - { - // This is our primary data source, we don't need most of the rest of this - _cultureData = cultureData; - _cultureName = _cultureData.CultureName; - _textInfoName = _cultureData.STEXTINFO; - FinishInitialization(_textInfoName); - } - - private unsafe void FinishInitialization(string textInfoName) - { - const uint LCMAP_SORTHANDLE = 0x20000000; - - long handle; - int ret = Interop.Kernel32.LCMapStringEx(_textInfoName, LCMAP_SORTHANDLE, null, 0, &handle, IntPtr.Size, null, null, IntPtr.Zero); - _sortHandle = ret > 0 ? (IntPtr)handle : IntPtr.Zero; - } - - private unsafe string ChangeCase(string s, bool toUpper) - { - Debug.Assert(s != null); - - // - // Get the length of the string. - // - int nLengthInput = s.Length; - - // - // Check if we have the empty string. - // - if (nLengthInput == 0) - { - return s; - } - else - { - int ret; - - // Check for Invariant to avoid A/V in LCMapStringEx - uint linguisticCasing = IsInvariantLocale(_textInfoName) ? 0 : LCMAP_LINGUISTIC_CASING; - - // - // Create the result string. - // - string result = string.FastAllocateString(nLengthInput); - - fixed (char* pSource = s) - fixed (char* pResult = result) - { - ret = Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _textInfoName, - toUpper ? LCMAP_UPPERCASE | linguisticCasing : LCMAP_LOWERCASE | linguisticCasing, - pSource, - nLengthInput, - pResult, - nLengthInput, - null, - null, - _sortHandle); - } - - if (0 == ret) - { - throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); - } - - Debug.Assert(ret == nLengthInput, "Expected getting the same length of the original string"); - return result; - } - } - - private unsafe char ChangeCase(char c, bool toUpper) - { - char retVal = '\0'; - - // Check for Invariant to avoid A/V in LCMapStringEx - uint linguisticCasing = IsInvariantLocale(_textInfoName) ? 0 : LCMAP_LINGUISTIC_CASING; - - Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _textInfoName, - toUpper ? LCMAP_UPPERCASE | linguisticCasing : LCMAP_LOWERCASE | linguisticCasing, - &c, - 1, - &retVal, - 1, - null, - null, - _sortHandle); - - return retVal; - } - - // PAL Ends here - - private IntPtr _sortHandle; - - private const uint LCMAP_LINGUISTIC_CASING = 0x01000000; - private const uint LCMAP_LOWERCASE = 0x00000100; - private const uint LCMAP_UPPERCASE = 0x00000200; - - private static bool IsInvariantLocale(string localeName) - { - return localeName == ""; - } - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs b/external/corert/src/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs deleted file mode 100644 index 327fdd0239..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs +++ /dev/null @@ -1,1810 +0,0 @@ -// 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. - -// -//////////////////////////////////////////////////////////////////////////// -// -// -// Purpose: This class is called by TimeSpan to parse a time interval string. -// -// Standard Format: -// -=-=-=-=-=-=-=- -// "c": Constant format. [-][d'.']hh':'mm':'ss['.'fffffff] -// Not culture sensitive. Default format (and null/empty format string) map to this format. -// -// "g": General format, short: [-][d':']h':'mm':'ss'.'FFFFFFF -// Only print what's needed. Localized (if you want Invariant, pass in Invariant). -// The fractional seconds separator is localized, equal to the culture's DecimalSeparator. -// -// "G": General format, long: [-]d':'hh':'mm':'ss'.'fffffff -// Always print days and 7 fractional digits. Localized (if you want Invariant, pass in Invariant). -// The fractional seconds separator is localized, equal to the culture's DecimalSeparator. -// -// -// * "TryParseTimeSpan" is the main method for Parse/TryParse -// -// - TimeSpanTokenizer.GetNextToken() is used to split the input string into number and literal tokens. -// - TimeSpanRawInfo.ProcessToken() adds the next token into the parsing intermediary state structure -// - ProcessTerminalState() uses the fully initialized TimeSpanRawInfo to find a legal parse match. -// The terminal states are attempted as follows: -// foreach (+InvariantPattern, -InvariantPattern, +LocalizedPattern, -LocalizedPattern) try -// 1 number => d -// 2 numbers => h:m -// 3 numbers => h:m:s | d.h:m | h:m:.f -// 4 numbers => h:m:s.f | d.h:m:s | d.h:m:.f -// 5 numbers => d.h:m:s.f -// -// Custom Format: -// -=-=-=-=-=-=-= -// -// * "TryParseExactTimeSpan" is the main method for ParseExact/TryParseExact methods -// * "TryParseExactMultipleTimeSpan" is the main method for ParseExact/TryparseExact -// methods that take a String[] of formats -// -// - For single-letter formats "TryParseTimeSpan" is called (see above) -// - For multi-letter formats "TryParseByFormat" is called -// - TryParseByFormat uses helper methods (ParseExactLiteral, ParseExactDigits, etc) -// which drive the underlying TimeSpanTokenizer. However, unlike standard formatting which -// operates on whole-tokens, ParseExact operates at the character-level. As such, -// TimeSpanTokenizer.NextChar and TimeSpanTokenizer.BackOne() are called directly. -// -//////////////////////////////////////////////////////////////////////////// - -using System.Diagnostics; -using System.Text; - -namespace System.Globalization -{ - //[Flags] - //public enum TimeSpanStyles { - // None = 0x00000000, - // AssumeNegative = 0x00000001, - //} - - internal static class TimeSpanParse - { - internal const int unlimitedDigits = -1; - internal const int maxFractionDigits = 7; - - internal const int maxDays = 10675199; - internal const int maxHours = 23; - internal const int maxMinutes = 59; - internal const int maxSeconds = 59; - internal const int maxFraction = 9999999; - - #region InternalSupport - private enum TimeSpanThrowStyle - { - None = 0, - All = 1, - } - - private enum ParseFailureKind - { - None = 0, - ArgumentNull = 1, - Format = 2, - FormatWithParameter = 3, - Overflow = 4, - } - [Flags] - - private enum TimeSpanStandardStyles - { // Standard Format Styles - None = 0x00000000, - Invariant = 0x00000001, //Allow Invariant Culture - Localized = 0x00000002, //Allow Localized Culture - RequireFull = 0x00000004, //Require the input to be in DHMSF format - Any = Invariant | Localized, - } - - // TimeSpan Token Types - private enum TTT - { - None = 0, // None of the TimeSpanToken fields are set - End = 1, // '\0' - Num = 2, // Number - Sep = 3, // literal - NumOverflow = 4, // Number that overflowed - } - - private static readonly TimeSpanToken s_zero = new TimeSpanToken(0); - private struct TimeSpanToken - { - internal TTT ttt; - internal int num; // Store the number that we are parsing (if any) - internal int zeroes; // Store the number of leading zeroes (if any) - internal String sep; // Store the literal that we are parsing (if any) - - public TimeSpanToken(int number) - { - ttt = TTT.Num; - num = number; - zeroes = 0; - sep = null; - } - - public TimeSpanToken(int leadingZeroes, int number) - { - ttt = TTT.Num; - num = number; - zeroes = leadingZeroes; - sep = null; - } - - public bool IsInvalidNumber(int maxValue, int maxPrecision) - { - Debug.Assert(ttt == TTT.Num); - Debug.Assert(num > -1); - Debug.Assert(maxValue > 0); - Debug.Assert(maxPrecision == maxFractionDigits || maxPrecision == unlimitedDigits); - - if (num > maxValue) - return true; - if (maxPrecision == unlimitedDigits) - return false; // all validation past this point applies only to fields with precision limits - if (zeroes > maxPrecision) - return true; - if (num == 0 || zeroes == 0) - return false; - - // num > 0 && zeroes > 0 && num <= maxValue && zeroes <= maxPrecision - return (num >= (maxValue / (long)Math.Pow(10, zeroes - 1))); - } - } - - // - // TimeSpanTokenizer - // - // Actions: TimeSpanTokenizer.GetNextToken() returns the next token in the input string. - // - private struct TimeSpanTokenizer - { - private int _pos; - private String _value; - - internal void Init(String input) - { - Init(input, 0); - } - internal void Init(String input, int startPosition) - { - _pos = startPosition; - _value = input; - } - // used by the parsing routines that operate on standard-formats - internal TimeSpanToken GetNextToken() - { - Debug.Assert(_pos > -1); - - TimeSpanToken tok = new TimeSpanToken(); - char ch = CurrentChar; - - if (ch == (char)0) - { - tok.ttt = TTT.End; - return tok; - } - - if (ch >= '0' && ch <= '9') - { - tok.ttt = TTT.Num; - tok.num = 0; - tok.zeroes = 0; - do - { - if ((tok.num & 0xF0000000) != 0) - { - tok.ttt = TTT.NumOverflow; - return tok; - } - tok.num = tok.num * 10 + ch - '0'; - if (tok.num == 0) tok.zeroes++; - if (tok.num < 0) - { - tok.ttt = TTT.NumOverflow; - return tok; - } - ch = NextChar; - } while (ch >= '0' && ch <= '9'); - return tok; - } - else - { - tok.ttt = TTT.Sep; - int startIndex = _pos; - int length = 0; - - while (ch != (char)0 && (ch < '0' || '9' < ch)) - { - ch = NextChar; - length++; - } - tok.sep = _value.Substring(startIndex, length); - return tok; - } - } - - internal Boolean EOL - { - get - { - return _pos >= (_value.Length - 1); - } - } - // BackOne, NextChar, CurrentChar - used by ParseExact (ParseByFormat) to operate - // on custom-formats where exact character-by-character control is allowed - internal void BackOne() - { - if (_pos > 0) --_pos; - } - - internal char NextChar - { - get - { - _pos++; - return CurrentChar; - } - } - internal char CurrentChar - { - get - { - if (_pos > -1 && _pos < _value.Length) - { - return _value[_pos]; - } - else - { - return (char)0; - } - } - } - } - - - - // This stores intermediary parsing state for the standard formats - private struct TimeSpanRawInfo - { - internal TimeSpanFormat.FormatLiterals PositiveInvariant - { - get - { - return TimeSpanFormat.PositiveInvariantFormatLiterals; - } - } - internal TimeSpanFormat.FormatLiterals NegativeInvariant - { - get - { - return TimeSpanFormat.NegativeInvariantFormatLiterals; - } - } - - internal TimeSpanFormat.FormatLiterals PositiveLocalized - { - get - { - if (!_posLocInit) - { - _posLoc = new TimeSpanFormat.FormatLiterals(); - _posLoc.Init(_fullPosPattern, false); - _posLocInit = true; - } - return _posLoc; - } - } - internal TimeSpanFormat.FormatLiterals NegativeLocalized - { - get - { - if (!_negLocInit) - { - _negLoc = new TimeSpanFormat.FormatLiterals(); - _negLoc.Init(_fullNegPattern, false); - _negLocInit = true; - } - return _negLoc; - } - } - internal Boolean FullAppCompatMatch(TimeSpanFormat.FormatLiterals pattern) - { - return SepCount == 5 - && NumCount == 4 - && pattern.Start == literals[0] - && pattern.DayHourSep == literals[1] - && pattern.HourMinuteSep == literals[2] - && pattern.AppCompatLiteral == literals[3] - && pattern.End == literals[4]; - } - internal Boolean PartialAppCompatMatch(TimeSpanFormat.FormatLiterals pattern) - { - return SepCount == 4 - && NumCount == 3 - && pattern.Start == literals[0] - && pattern.HourMinuteSep == literals[1] - && pattern.AppCompatLiteral == literals[2] - && pattern.End == literals[3]; - } - // DHMSF (all values matched) - internal Boolean FullMatch(TimeSpanFormat.FormatLiterals pattern) - { - return SepCount == MaxLiteralTokens - && NumCount == MaxNumericTokens - && pattern.Start == literals[0] - && pattern.DayHourSep == literals[1] - && pattern.HourMinuteSep == literals[2] - && pattern.MinuteSecondSep == literals[3] - && pattern.SecondFractionSep == literals[4] - && pattern.End == literals[5]; - } - // D (no hours, minutes, seconds, or fractions) - internal Boolean FullDMatch(TimeSpanFormat.FormatLiterals pattern) - { - return SepCount == 2 - && NumCount == 1 - && pattern.Start == literals[0] - && pattern.End == literals[1]; - } - // HM (no days, seconds, or fractions) - internal Boolean FullHMMatch(TimeSpanFormat.FormatLiterals pattern) - { - return SepCount == 3 - && NumCount == 2 - && pattern.Start == literals[0] - && pattern.HourMinuteSep == literals[1] - && pattern.End == literals[2]; - } - // DHM (no seconds or fraction) - internal Boolean FullDHMMatch(TimeSpanFormat.FormatLiterals pattern) - { - return SepCount == 4 - && NumCount == 3 - && pattern.Start == literals[0] - && pattern.DayHourSep == literals[1] - && pattern.HourMinuteSep == literals[2] - && pattern.End == literals[3]; - } - // HMS (no days or fraction) - internal Boolean FullHMSMatch(TimeSpanFormat.FormatLiterals pattern) - { - return SepCount == 4 - && NumCount == 3 - && pattern.Start == literals[0] - && pattern.HourMinuteSep == literals[1] - && pattern.MinuteSecondSep == literals[2] - && pattern.End == literals[3]; - } - // DHMS (no fraction) - internal Boolean FullDHMSMatch(TimeSpanFormat.FormatLiterals pattern) - { - return SepCount == 5 - && NumCount == 4 - && pattern.Start == literals[0] - && pattern.DayHourSep == literals[1] - && pattern.HourMinuteSep == literals[2] - && pattern.MinuteSecondSep == literals[3] - && pattern.End == literals[4]; - } - // HMSF (no days) - internal Boolean FullHMSFMatch(TimeSpanFormat.FormatLiterals pattern) - { - return SepCount == 5 - && NumCount == 4 - && pattern.Start == literals[0] - && pattern.HourMinuteSep == literals[1] - && pattern.MinuteSecondSep == literals[2] - && pattern.SecondFractionSep == literals[3] - && pattern.End == literals[4]; - } - - internal TTT lastSeenTTT; - internal int tokenCount; - internal int SepCount; - internal int NumCount; - internal String[] literals; - internal TimeSpanToken[] numbers; // raw numbers - - private TimeSpanFormat.FormatLiterals _posLoc; - private TimeSpanFormat.FormatLiterals _negLoc; - private Boolean _posLocInit; - private Boolean _negLocInit; - private String _fullPosPattern; - private String _fullNegPattern; - - private const int MaxTokens = 11; - private const int MaxLiteralTokens = 6; - private const int MaxNumericTokens = 5; - - internal void Init(DateTimeFormatInfo dtfi) - { - Debug.Assert(dtfi != null); - - lastSeenTTT = TTT.None; - tokenCount = 0; - SepCount = 0; - NumCount = 0; - - literals = new String[MaxLiteralTokens]; - numbers = new TimeSpanToken[MaxNumericTokens]; - - _fullPosPattern = dtfi.FullTimeSpanPositivePattern; - _fullNegPattern = dtfi.FullTimeSpanNegativePattern; - _posLocInit = false; - _negLocInit = false; - } - - internal Boolean ProcessToken(ref TimeSpanToken tok, ref TimeSpanResult result) - { - if (tok.ttt == TTT.NumOverflow) - { - result.SetFailure(ParseFailureKind.Overflow, SR.Overflow_TimeSpanElementTooLarge, null); - return false; - } - if (tok.ttt != TTT.Sep && tok.ttt != TTT.Num) - { - // Some unknown token or a repeat token type in the input - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan, null); - return false; - } - - switch (tok.ttt) - { - case TTT.Sep: - if (!AddSep(tok.sep, ref result)) return false; - break; - case TTT.Num: - if (tokenCount == 0) - { - if (!AddSep(String.Empty, ref result)) return false; - } - if (!AddNum(tok, ref result)) return false; - break; - default: - break; - } - - lastSeenTTT = tok.ttt; - Debug.Assert(tokenCount == (SepCount + NumCount), "tokenCount == (SepCount + NumCount)"); - return true; - } - - private bool AddSep(String sep, ref TimeSpanResult result) - { - if (SepCount >= MaxLiteralTokens || tokenCount >= MaxTokens) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan, null); - return false; - } - literals[SepCount++] = sep; - tokenCount++; - return true; - } - private bool AddNum(TimeSpanToken num, ref TimeSpanResult result) - { - if (NumCount >= MaxNumericTokens || tokenCount >= MaxTokens) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan, null); - return false; - } - numbers[NumCount++] = num; - tokenCount++; - return true; - } - } - - // This will store the result of the parsing. And it will eventually be used to construct a TimeSpan instance. - private struct TimeSpanResult - { - internal TimeSpan parsedTimeSpan; - internal TimeSpanThrowStyle throwStyle; - - internal ParseFailureKind m_failure; - internal string m_failureMessageFormat; - internal object m_failureMessageFormatArgument; - internal string m_failureArgumentName; - - internal void Init(TimeSpanThrowStyle canThrow) - { - parsedTimeSpan = default(TimeSpan); - throwStyle = canThrow; - } - internal void SetFailure(ParseFailureKind failure, string failureMessageID) - { - SetFailure(failure, failureMessageID, null, null); - } - internal void SetFailure(ParseFailureKind failure, string failureMessageID, object failureMessageFormatArgument) - { - SetFailure(failure, failureMessageID, failureMessageFormatArgument, null); - } - internal void SetFailure(ParseFailureKind failure, string failureMessageFormat, object failureMessageFormatArgument, - string failureArgumentName) - { - m_failure = failure; - m_failureMessageFormat = failureMessageFormat; - m_failureMessageFormatArgument = failureMessageFormatArgument; - m_failureArgumentName = failureArgumentName; - if (throwStyle != TimeSpanThrowStyle.None) - { - throw GetTimeSpanParseException(); - } - } - - internal Exception GetTimeSpanParseException() - { - switch (m_failure) - { - case ParseFailureKind.ArgumentNull: - return new ArgumentNullException(m_failureArgumentName, m_failureMessageFormat); - - case ParseFailureKind.FormatWithParameter: - return new FormatException(SR.Format(m_failureMessageFormat, m_failureMessageFormatArgument)); - - case ParseFailureKind.Format: - return new FormatException(m_failureMessageFormat); - - case ParseFailureKind.Overflow: - return new OverflowException(m_failureMessageFormat); - - default: - Debug.Assert(false, "Unknown TimeSpanParseFailure: " + m_failure); - return new FormatException(SR.Format_InvalidString); - } - } - } - - private static bool TryTimeToTicks(bool positive, TimeSpanToken days, TimeSpanToken hours, TimeSpanToken minutes, TimeSpanToken seconds, TimeSpanToken fraction, out long result) - { - if (days.IsInvalidNumber(maxDays, unlimitedDigits) - || hours.IsInvalidNumber(maxHours, unlimitedDigits) - || minutes.IsInvalidNumber(maxMinutes, unlimitedDigits) - || seconds.IsInvalidNumber(maxSeconds, unlimitedDigits) - || fraction.IsInvalidNumber(maxFraction, maxFractionDigits)) - { - result = 0; - return false; - } - - Int64 ticks = ((Int64)days.num * 3600 * 24 + (Int64)hours.num * 3600 + (Int64)minutes.num * 60 + seconds.num) * 1000; - if (ticks > InternalGlobalizationHelper.MaxMilliSeconds || ticks < InternalGlobalizationHelper.MinMilliSeconds) - { - result = 0; - return false; - } - - // Normalize the fraction component - // - // string representation => (zeroes,num) => resultant fraction ticks - // --------------------- ------------ ------------------------ - // ".9999999" => (0,9999999) => 9,999,999 ticks (same as constant maxFraction) - // ".1" => (0,1) => 1,000,000 ticks - // ".01" => (1,1) => 100,000 ticks - // ".001" => (2,1) => 10,000 ticks - long f = fraction.num; - if (f != 0) - { - long lowerLimit = InternalGlobalizationHelper.TicksPerTenthSecond; - if (fraction.zeroes > 0) - { - long divisor = (long)Math.Pow(10, fraction.zeroes); - lowerLimit = lowerLimit / divisor; - } - while (f < lowerLimit) - { - f *= 10; - } - } - result = ((long)ticks * InternalGlobalizationHelper.TicksPerMillisecond) + f; - if (positive && result < 0) - { - result = 0; - return false; - } - return true; - } - #endregion - - - // ---- SECTION: internal static methods called by System.TimeSpan ---------* - // - // [Try]Parse, [Try]ParseExact, and [Try]ParseExactMultiple - // - // Actions: Main methods called from TimeSpan.Parse - #region ParseMethods - internal static TimeSpan Parse(String input, IFormatProvider formatProvider) - { - TimeSpanResult parseResult = new TimeSpanResult(); - parseResult.Init(TimeSpanThrowStyle.All); - - if (TryParseTimeSpan(input, TimeSpanStandardStyles.Any, formatProvider, ref parseResult)) - { - return parseResult.parsedTimeSpan; - } - else - { - throw parseResult.GetTimeSpanParseException(); - } - } - internal static Boolean TryParse(String input, IFormatProvider formatProvider, out TimeSpan result) - { - TimeSpanResult parseResult = new TimeSpanResult(); - parseResult.Init(TimeSpanThrowStyle.None); - - if (TryParseTimeSpan(input, TimeSpanStandardStyles.Any, formatProvider, ref parseResult)) - { - result = parseResult.parsedTimeSpan; - return true; - } - else - { - result = default(TimeSpan); - return false; - } - } - internal static TimeSpan ParseExact(String input, String format, IFormatProvider formatProvider, TimeSpanStyles styles) - { - TimeSpanResult parseResult = new TimeSpanResult(); - parseResult.Init(TimeSpanThrowStyle.All); - - if (TryParseExactTimeSpan(input, format, formatProvider, styles, ref parseResult)) - { - return parseResult.parsedTimeSpan; - } - else - { - throw parseResult.GetTimeSpanParseException(); - } - } - internal static Boolean TryParseExact(String input, String format, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result) - { - TimeSpanResult parseResult = new TimeSpanResult(); - parseResult.Init(TimeSpanThrowStyle.None); - - if (TryParseExactTimeSpan(input, format, formatProvider, styles, ref parseResult)) - { - result = parseResult.parsedTimeSpan; - return true; - } - else - { - result = default(TimeSpan); - return false; - } - } - internal static TimeSpan ParseExactMultiple(String input, String[] formats, IFormatProvider formatProvider, TimeSpanStyles styles) - { - TimeSpanResult parseResult = new TimeSpanResult(); - parseResult.Init(TimeSpanThrowStyle.All); - - if (TryParseExactMultipleTimeSpan(input, formats, formatProvider, styles, ref parseResult)) - { - return parseResult.parsedTimeSpan; - } - else - { - throw parseResult.GetTimeSpanParseException(); - } - } - internal static Boolean TryParseExactMultiple(String input, String[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result) - { - TimeSpanResult parseResult = new TimeSpanResult(); - parseResult.Init(TimeSpanThrowStyle.None); - - if (TryParseExactMultipleTimeSpan(input, formats, formatProvider, styles, ref parseResult)) - { - result = parseResult.parsedTimeSpan; - return true; - } - else - { - result = default(TimeSpan); - return false; - } - } - #endregion - - - // ---- SECTION: private static methods that do the actual work ---------* - #region TryParseTimeSpan - // - // TryParseTimeSpan - // - // Actions: Common private Parse method called by both Parse and TryParse - // - private static Boolean TryParseTimeSpan(String input, TimeSpanStandardStyles style, IFormatProvider formatProvider, ref TimeSpanResult result) - { - if (input == null) - { - result.SetFailure(ParseFailureKind.ArgumentNull, SR.ArgumentNull_String, null, nameof(input)); - return false; - } - - input = input.Trim(); - if (input == String.Empty) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan); - return false; - } - - TimeSpanTokenizer tokenizer = new TimeSpanTokenizer(); - tokenizer.Init(input); - - TimeSpanRawInfo raw = new TimeSpanRawInfo(); - raw.Init(DateTimeFormatInfo.GetInstance(formatProvider)); - - TimeSpanToken tok = tokenizer.GetNextToken(); - - /* The following loop will break out when we reach the end of the str or - * when we can determine that the input is invalid. */ - while (tok.ttt != TTT.End) - { - if (!raw.ProcessToken(ref tok, ref result)) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan); - return false; - } - tok = tokenizer.GetNextToken(); - } - if (!tokenizer.EOL) - { - // embedded nulls in the input string - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan); - return false; - } - if (!ProcessTerminalState(ref raw, style, ref result)) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan); - return false; - } - return true; - } - - - - // - // ProcessTerminalState - // - // Actions: Validate the terminal state of a standard format parse. - // Sets result.parsedTimeSpan on success. - // - // Calculates the resultant TimeSpan from the TimeSpanRawInfo - // - // try => +InvariantPattern, -InvariantPattern, +LocalizedPattern, -LocalizedPattern - // 1) Verify Start matches - // 2) Verify End matches - // 3) 1 number => d - // 2 numbers => h:m - // 3 numbers => h:m:s | d.h:m | h:m:.f - // 4 numbers => h:m:s.f | d.h:m:s | d.h:m:.f - // 5 numbers => d.h:m:s.f - private static Boolean ProcessTerminalState(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) - { - if (raw.lastSeenTTT == TTT.Num) - { - TimeSpanToken tok = new TimeSpanToken(); - tok.ttt = TTT.Sep; - tok.sep = String.Empty; - if (!raw.ProcessToken(ref tok, ref result)) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan); - return false; - } - } - - switch (raw.NumCount) - { - case 1: - return ProcessTerminal_D(ref raw, style, ref result); - case 2: - return ProcessTerminal_HM(ref raw, style, ref result); - case 3: - return ProcessTerminal_HM_S_D(ref raw, style, ref result); - case 4: - return ProcessTerminal_HMS_F_D(ref raw, style, ref result); - case 5: - return ProcessTerminal_DHMSF(ref raw, style, ref result); - default: - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan); - return false; - } - } - - // - // ProcessTerminal_DHMSF - // - // Actions: Validate the 5-number "Days.Hours:Minutes:Seconds.Fraction" terminal case. - // Sets result.parsedTimeSpan on success. - // - private static Boolean ProcessTerminal_DHMSF(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) - { - if (raw.SepCount != 6 || raw.NumCount != 5) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan); - return false; - } - - bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0); - bool loc = ((style & TimeSpanStandardStyles.Localized) != 0); - - bool positive = false; - bool match = false; - - if (inv) - { - if (raw.FullMatch(raw.PositiveInvariant)) - { - match = true; - positive = true; - } - if (!match && raw.FullMatch(raw.NegativeInvariant)) - { - match = true; - positive = false; - } - } - if (loc) - { - if (!match && raw.FullMatch(raw.PositiveLocalized)) - { - match = true; - positive = true; - } - if (!match && raw.FullMatch(raw.NegativeLocalized)) - { - match = true; - positive = false; - } - } - long ticks; - if (match) - { - if (!TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], raw.numbers[3], raw.numbers[4], out ticks)) - { - result.SetFailure(ParseFailureKind.Overflow, SR.Overflow_TimeSpanElementTooLarge); - return false; - } - if (!positive) - { - ticks = -ticks; - if (ticks > 0) - { - result.SetFailure(ParseFailureKind.Overflow, SR.Overflow_TimeSpanElementTooLarge); - return false; - } - } - result.parsedTimeSpan._ticks = ticks; - return true; - } - - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan); - return false; - } - - // - // ProcessTerminal_HMS_F_D - // - // Actions: Validate the ambiguous 4-number "Hours:Minutes:Seconds.Fraction", "Days.Hours:Minutes:Seconds", or "Days.Hours:Minutes:.Fraction" terminal case. - // Sets result.parsedTimeSpan on success. - // - private static Boolean ProcessTerminal_HMS_F_D(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) - { - if (raw.SepCount != 5 || raw.NumCount != 4 || (style & TimeSpanStandardStyles.RequireFull) != 0) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan); - return false; - } - - bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0); - bool loc = ((style & TimeSpanStandardStyles.Localized) != 0); - - long ticks = 0; - bool positive = false; - bool match = false; - bool overflow = false; - - if (inv) - { - if (raw.FullHMSFMatch(raw.PositiveInvariant)) - { - positive = true; - match = TryTimeToTicks(positive, s_zero, raw.numbers[0], raw.numbers[1], raw.numbers[2], raw.numbers[3], out ticks); - overflow = overflow || !match; - } - if (!match && raw.FullDHMSMatch(raw.PositiveInvariant)) - { - positive = true; - match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], raw.numbers[3], s_zero, out ticks); - overflow = overflow || !match; - } - if (!match && raw.FullAppCompatMatch(raw.PositiveInvariant)) - { - positive = true; - match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], s_zero, raw.numbers[3], out ticks); - overflow = overflow || !match; - } - if (!match && raw.FullHMSFMatch(raw.NegativeInvariant)) - { - positive = false; - match = TryTimeToTicks(positive, s_zero, raw.numbers[0], raw.numbers[1], raw.numbers[2], raw.numbers[3], out ticks); - overflow = overflow || !match; - } - if (!match && raw.FullDHMSMatch(raw.NegativeInvariant)) - { - positive = false; - match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], raw.numbers[3], s_zero, out ticks); - overflow = overflow || !match; - } - if (!match && raw.FullAppCompatMatch(raw.NegativeInvariant)) - { - positive = false; - match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], s_zero, raw.numbers[3], out ticks); - overflow = overflow || !match; - } - } - if (loc) - { - if (!match && raw.FullHMSFMatch(raw.PositiveLocalized)) - { - positive = true; - match = TryTimeToTicks(positive, s_zero, raw.numbers[0], raw.numbers[1], raw.numbers[2], raw.numbers[3], out ticks); - overflow = overflow || !match; - } - if (!match && raw.FullDHMSMatch(raw.PositiveLocalized)) - { - positive = true; - match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], raw.numbers[3], s_zero, out ticks); - overflow = overflow || !match; - } - if (!match && raw.FullAppCompatMatch(raw.PositiveLocalized)) - { - positive = true; - match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], s_zero, raw.numbers[3], out ticks); - overflow = overflow || !match; - } - if (!match && raw.FullHMSFMatch(raw.NegativeLocalized)) - { - positive = false; - match = TryTimeToTicks(positive, s_zero, raw.numbers[0], raw.numbers[1], raw.numbers[2], raw.numbers[3], out ticks); - overflow = overflow || !match; - } - if (!match && raw.FullDHMSMatch(raw.NegativeLocalized)) - { - positive = false; - match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], raw.numbers[3], s_zero, out ticks); - overflow = overflow || !match; - } - if (!match && raw.FullAppCompatMatch(raw.NegativeLocalized)) - { - positive = false; - match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], s_zero, raw.numbers[3], out ticks); - overflow = overflow || !match; - } - } - - if (match) - { - if (!positive) - { - ticks = -ticks; - if (ticks > 0) - { - result.SetFailure(ParseFailureKind.Overflow, SR.Overflow_TimeSpanElementTooLarge); - return false; - } - } - result.parsedTimeSpan._ticks = ticks; - return true; - } - - if (overflow) - { - // we found at least one literal pattern match but the numbers just didn't fit - result.SetFailure(ParseFailureKind.Overflow, SR.Overflow_TimeSpanElementTooLarge); - return false; - } - else - { - // we couldn't find a thing - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan); - return false; - } - } - - // - // ProcessTerminal_HM_S_D - // - // Actions: Validate the ambiguous 3-number "Hours:Minutes:Seconds", "Days.Hours:Minutes", or "Hours:Minutes:.Fraction" terminal case - // - private static Boolean ProcessTerminal_HM_S_D(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) - { - if (raw.SepCount != 4 || raw.NumCount != 3 || (style & TimeSpanStandardStyles.RequireFull) != 0) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan); - return false; - } - - bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0); - bool loc = ((style & TimeSpanStandardStyles.Localized) != 0); - - bool positive = false; - bool match = false; - bool overflow = false; - - long ticks = 0; - - if (inv) - { - if (raw.FullHMSMatch(raw.PositiveInvariant)) - { - positive = true; - match = TryTimeToTicks(positive, s_zero, raw.numbers[0], raw.numbers[1], raw.numbers[2], s_zero, out ticks); - overflow = overflow || !match; - } - if (!match && raw.FullDHMMatch(raw.PositiveInvariant)) - { - positive = true; - match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], s_zero, s_zero, out ticks); - overflow = overflow || !match; - } - if (!match && raw.PartialAppCompatMatch(raw.PositiveInvariant)) - { - positive = true; - match = TryTimeToTicks(positive, s_zero, raw.numbers[0], raw.numbers[1], s_zero, raw.numbers[2], out ticks); - overflow = overflow || !match; - } - if (!match && raw.FullHMSMatch(raw.NegativeInvariant)) - { - positive = false; - match = TryTimeToTicks(positive, s_zero, raw.numbers[0], raw.numbers[1], raw.numbers[2], s_zero, out ticks); - overflow = overflow || !match; - } - if (!match && raw.FullDHMMatch(raw.NegativeInvariant)) - { - positive = false; - match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], s_zero, s_zero, out ticks); - overflow = overflow || !match; - } - if (!match && raw.PartialAppCompatMatch(raw.NegativeInvariant)) - { - positive = false; - match = TryTimeToTicks(positive, s_zero, raw.numbers[0], raw.numbers[1], s_zero, raw.numbers[2], out ticks); - overflow = overflow || !match; - } - } - if (loc) - { - if (!match && raw.FullHMSMatch(raw.PositiveLocalized)) - { - positive = true; - match = TryTimeToTicks(positive, s_zero, raw.numbers[0], raw.numbers[1], raw.numbers[2], s_zero, out ticks); - overflow = overflow || !match; - } - if (!match && raw.FullDHMMatch(raw.PositiveLocalized)) - { - positive = true; - match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], s_zero, s_zero, out ticks); - overflow = overflow || !match; - } - if (!match && raw.PartialAppCompatMatch(raw.PositiveLocalized)) - { - positive = true; - match = TryTimeToTicks(positive, s_zero, raw.numbers[0], raw.numbers[1], s_zero, raw.numbers[2], out ticks); - overflow = overflow || !match; - } - if (!match && raw.FullHMSMatch(raw.NegativeLocalized)) - { - positive = false; - match = TryTimeToTicks(positive, s_zero, raw.numbers[0], raw.numbers[1], raw.numbers[2], s_zero, out ticks); - overflow = overflow || !match; - } - if (!match && raw.FullDHMMatch(raw.NegativeLocalized)) - { - positive = false; - match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], s_zero, s_zero, out ticks); - overflow = overflow || !match; - } - if (!match && raw.PartialAppCompatMatch(raw.NegativeLocalized)) - { - positive = false; - match = TryTimeToTicks(positive, s_zero, raw.numbers[0], raw.numbers[1], s_zero, raw.numbers[2], out ticks); - overflow = overflow || !match; - } - } - - if (match) - { - if (!positive) - { - ticks = -ticks; - if (ticks > 0) - { - result.SetFailure(ParseFailureKind.Overflow, SR.Overflow_TimeSpanElementTooLarge); - return false; - } - } - result.parsedTimeSpan._ticks = ticks; - return true; - } - - if (overflow) - { - // we found at least one literal pattern match but the numbers just didn't fit - result.SetFailure(ParseFailureKind.Overflow, SR.Overflow_TimeSpanElementTooLarge); - return false; - } - else - { - // we couldn't find a thing - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan); - return false; - } - } - - // - // ProcessTerminal_HM - // - // Actions: Validate the 2-number "Hours:Minutes" terminal case - // - private static Boolean ProcessTerminal_HM(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) - { - if (raw.SepCount != 3 || raw.NumCount != 2 || (style & TimeSpanStandardStyles.RequireFull) != 0) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan); - return false; - } - - bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0); - bool loc = ((style & TimeSpanStandardStyles.Localized) != 0); - - bool positive = false; - bool match = false; - - if (inv) - { - if (raw.FullHMMatch(raw.PositiveInvariant)) - { - match = true; - positive = true; - } - if (!match && raw.FullHMMatch(raw.NegativeInvariant)) - { - match = true; - positive = false; - } - } - if (loc) - { - if (!match && raw.FullHMMatch(raw.PositiveLocalized)) - { - match = true; - positive = true; - } - if (!match && raw.FullHMMatch(raw.NegativeLocalized)) - { - match = true; - positive = false; - } - } - - long ticks = 0; - if (match) - { - if (!TryTimeToTicks(positive, s_zero, raw.numbers[0], raw.numbers[1], s_zero, s_zero, out ticks)) - { - result.SetFailure(ParseFailureKind.Overflow, SR.Overflow_TimeSpanElementTooLarge); - return false; - } - if (!positive) - { - ticks = -ticks; - if (ticks > 0) - { - result.SetFailure(ParseFailureKind.Overflow, SR.Overflow_TimeSpanElementTooLarge); - return false; - } - } - result.parsedTimeSpan._ticks = ticks; - return true; - } - - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan); - return false; - } - - - // - // ProcessTerminal_D - // - // Actions: Validate the 1-number "Days" terminal case - // - private static Boolean ProcessTerminal_D(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) - { - if (raw.SepCount != 2 || raw.NumCount != 1 || (style & TimeSpanStandardStyles.RequireFull) != 0) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan); - return false; - } - - bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0); - bool loc = ((style & TimeSpanStandardStyles.Localized) != 0); - - bool positive = false; - bool match = false; - - if (inv) - { - if (raw.FullDMatch(raw.PositiveInvariant)) - { - match = true; - positive = true; - } - if (!match && raw.FullDMatch(raw.NegativeInvariant)) - { - match = true; - positive = false; - } - } - if (loc) - { - if (!match && raw.FullDMatch(raw.PositiveLocalized)) - { - match = true; - positive = true; - } - if (!match && raw.FullDMatch(raw.NegativeLocalized)) - { - match = true; - positive = false; - } - } - - long ticks = 0; - if (match) - { - if (!TryTimeToTicks(positive, raw.numbers[0], s_zero, s_zero, s_zero, s_zero, out ticks)) - { - result.SetFailure(ParseFailureKind.Overflow, SR.Overflow_TimeSpanElementTooLarge); - return false; - } - if (!positive) - { - ticks = -ticks; - if (ticks > 0) - { - result.SetFailure(ParseFailureKind.Overflow, SR.Overflow_TimeSpanElementTooLarge); - return false; - } - } - result.parsedTimeSpan._ticks = ticks; - return true; - } - - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan); - return false; - } - #endregion - - #region TryParseExactTimeSpan - // - // TryParseExactTimeSpan - // - // Actions: Common private ParseExact method called by both ParseExact and TryParseExact - // - private static Boolean TryParseExactTimeSpan(String input, String format, IFormatProvider formatProvider, TimeSpanStyles styles, ref TimeSpanResult result) - { - if (input == null) - { - result.SetFailure(ParseFailureKind.ArgumentNull, SR.ArgumentNull_String, null, nameof(input)); - return false; - } - if (format == null) - { - result.SetFailure(ParseFailureKind.ArgumentNull, SR.ArgumentNull_String, null, nameof(format)); - return false; - } - if (format.Length == 0) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_BadFormatSpecifier); - return false; - } - - if (format.Length == 1) - { - TimeSpanStandardStyles style = TimeSpanStandardStyles.None; - - if (format[0] == 'c' || format[0] == 't' || format[0] == 'T') - { - // fast path for legacy style TimeSpan formats. - return TryParseTimeSpanConstant(input, ref result); - } - else if (format[0] == 'g') - { - style = TimeSpanStandardStyles.Localized; - } - else if (format[0] == 'G') - { - style = TimeSpanStandardStyles.Localized | TimeSpanStandardStyles.RequireFull; - } - else - { - result.SetFailure(ParseFailureKind.Format, SR.Format_BadFormatSpecifier); - return false; - } - return TryParseTimeSpan(input, style, formatProvider, ref result); - } - - return TryParseByFormat(input, format, styles, ref result); - } - - // - // TryParseByFormat - // - // Actions: Parse the TimeSpan instance using the specified format. Used by TryParseExactTimeSpan. - // - private static Boolean TryParseByFormat(String input, String format, TimeSpanStyles styles, ref TimeSpanResult result) - { - Debug.Assert(input != null, "input != null"); - Debug.Assert(format != null, "format != null"); - - bool seenDD = false; // already processed days? - bool seenHH = false; // already processed hours? - bool seenMM = false; // already processed minutes? - bool seenSS = false; // already processed seconds? - bool seenFF = false; // already processed fraction? - int dd = 0; // parsed days - int hh = 0; // parsed hours - int mm = 0; // parsed minutes - int ss = 0; // parsed seconds - int leadingZeroes = 0; // number of leading zeroes in the parsed fraction - int ff = 0; // parsed fraction - int i = 0; // format string position - int tokenLen = 0; // length of current format token, used to update index 'i' - - TimeSpanTokenizer tokenizer = new TimeSpanTokenizer(); - tokenizer.Init(input, -1); - - while (i < format.Length) - { - char ch = format[i]; - int nextFormatChar; - switch (ch) - { - case 'h': - tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); - if (tokenLen > 2 || seenHH || !ParseExactDigits(ref tokenizer, tokenLen, out hh)) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_InvalidString); - return false; - } - seenHH = true; - break; - case 'm': - tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); - if (tokenLen > 2 || seenMM || !ParseExactDigits(ref tokenizer, tokenLen, out mm)) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_InvalidString); - return false; - } - seenMM = true; - break; - case 's': - tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); - if (tokenLen > 2 || seenSS || !ParseExactDigits(ref tokenizer, tokenLen, out ss)) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_InvalidString); - return false; - } - seenSS = true; - break; - case 'f': - tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); - if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits || seenFF || !ParseExactDigits(ref tokenizer, tokenLen, tokenLen, out leadingZeroes, out ff)) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_InvalidString); - return false; - } - seenFF = true; - break; - case 'F': - tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); - if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits || seenFF) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_InvalidString); - return false; - } - ParseExactDigits(ref tokenizer, tokenLen, tokenLen, out leadingZeroes, out ff); - seenFF = true; - break; - case 'd': - tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); - int tmp = 0; - if (tokenLen > 8 || seenDD || !ParseExactDigits(ref tokenizer, (tokenLen < 2) ? 1 : tokenLen, (tokenLen < 2) ? 8 : tokenLen, out tmp, out dd)) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_InvalidString); - return false; - } - seenDD = true; - break; - case '\'': - case '\"': - StringBuilder enquotedString = new StringBuilder(); - if (!DateTimeParse.TryParseQuoteString(format, i, enquotedString, out tokenLen)) - { - result.SetFailure(ParseFailureKind.FormatWithParameter, SR.Format_BadQuote, ch); - return false; - } - if (!ParseExactLiteral(ref tokenizer, enquotedString)) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_InvalidString); - return false; - } - break; - case '%': - // Optional format character. - // For example, format string "%d" will print day - // Most of the cases, "%" can be ignored. - nextFormatChar = DateTimeFormat.ParseNextChar(format, i); - // nextFormatChar will be -1 if we already reach the end of the format string. - // Besides, we will not allow "%%" appear in the pattern. - if (nextFormatChar >= 0 && nextFormatChar != (int)'%') - { - tokenLen = 1; // skip the '%' and process the format character - break; - } - else - { - // This means that '%' is at the end of the format string or - // "%%" appears in the format string. - result.SetFailure(ParseFailureKind.Format, SR.Format_InvalidString); - return false; - } - case '\\': - // Escaped character. Can be used to insert character into the format string. - // For example, "\d" will insert the character 'd' into the string. - // - nextFormatChar = DateTimeFormat.ParseNextChar(format, i); - if (nextFormatChar >= 0 && tokenizer.NextChar == (char)nextFormatChar) - { - tokenLen = 2; - } - else - { - // This means that '\' is at the end of the format string or the literal match failed. - result.SetFailure(ParseFailureKind.Format, SR.Format_InvalidString); - return false; - } - break; - default: - result.SetFailure(ParseFailureKind.Format, SR.Format_InvalidString); - return false; - } - i += tokenLen; - } - - - if (!tokenizer.EOL) - { - // the custom format didn't consume the entire input - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan); - return false; - } - - long ticks = 0; - bool positive = (styles & TimeSpanStyles.AssumeNegative) == 0; - if (TryTimeToTicks(positive, new TimeSpanToken(dd), - new TimeSpanToken(hh), - new TimeSpanToken(mm), - new TimeSpanToken(ss), - new TimeSpanToken(leadingZeroes, ff), - out ticks)) - { - if (!positive) ticks = -ticks; - result.parsedTimeSpan._ticks = ticks; - return true; - } - else - { - result.SetFailure(ParseFailureKind.Overflow, SR.Overflow_TimeSpanElementTooLarge); - return false; - } - } - - private static Boolean ParseExactDigits(ref TimeSpanTokenizer tokenizer, int minDigitLength, out int result) - { - result = 0; - int zeroes = 0; - int maxDigitLength = (minDigitLength == 1) ? 2 : minDigitLength; - return ParseExactDigits(ref tokenizer, minDigitLength, maxDigitLength, out zeroes, out result); - } - private static Boolean ParseExactDigits(ref TimeSpanTokenizer tokenizer, int minDigitLength, int maxDigitLength, out int zeroes, out int result) - { - result = 0; - zeroes = 0; - - int tokenLength = 0; - while (tokenLength < maxDigitLength) - { - char ch = tokenizer.NextChar; - if (ch < '0' || ch > '9') - { - tokenizer.BackOne(); - break; - } - result = result * 10 + (ch - '0'); - if (result == 0) zeroes++; - tokenLength++; - } - return (tokenLength >= minDigitLength); - } - private static Boolean ParseExactLiteral(ref TimeSpanTokenizer tokenizer, StringBuilder enquotedString) - { - for (int i = 0; i < enquotedString.Length; i++) - { - if (enquotedString[i] != tokenizer.NextChar) - return false; - } - return true; - } - #endregion - - #region TryParseTimeSpanConstant - // - // TryParseTimeSpanConstant - // - // Actions: Parses the "c" (constant) format. This code is 100% identical to the non-globalized v1.0-v3.5 TimeSpan.Parse() routine - // and exists for performance/appcompat with legacy callers who cannot move onto the globalized Parse overloads. - // - private static Boolean TryParseTimeSpanConstant(String input, ref TimeSpanResult result) - { - return (new StringParser().TryParse(input, ref result)); - } - - private struct StringParser - { - private String _str; - private char _ch; - private int _pos; - private int _len; - - internal void NextChar() - { - if (_pos < _len) _pos++; - _ch = _pos < _len ? _str[_pos] : (char)0; - } - - internal char NextNonDigit() - { - int i = _pos; - while (i < _len) - { - char ch = _str[i]; - if (ch < '0' || ch > '9') return ch; - i++; - } - return (char)0; - } - - internal bool TryParse(String input, ref TimeSpanResult result) - { - result.parsedTimeSpan._ticks = 0; - - if (input == null) - { - result.SetFailure(ParseFailureKind.ArgumentNull, SR.ArgumentNull_String, null, nameof(input)); - return false; - } - _str = input; - _len = input.Length; - _pos = -1; - NextChar(); - SkipBlanks(); - bool negative = false; - if (_ch == '-') - { - negative = true; - NextChar(); - } - long time; - if (NextNonDigit() == ':') - { - if (!ParseTime(out time, ref result)) - { - return false; - }; - } - else - { - int days; - if (!ParseInt((int)(0x7FFFFFFFFFFFFFFFL / TimeSpan.TicksPerDay), out days, ref result)) - { - return false; - } - time = days * TimeSpan.TicksPerDay; - if (_ch == '.') - { - NextChar(); - long remainingTime; - if (!ParseTime(out remainingTime, ref result)) - { - return false; - }; - time += remainingTime; - } - } - if (negative) - { - time = -time; - // Allow -0 as well - if (time > 0) - { - result.SetFailure(ParseFailureKind.Overflow, SR.Overflow_TimeSpanElementTooLarge); - return false; - } - } - else - { - if (time < 0) - { - result.SetFailure(ParseFailureKind.Overflow, SR.Overflow_TimeSpanElementTooLarge); - return false; - } - } - SkipBlanks(); - if (_pos < _len) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan); - return false; - } - result.parsedTimeSpan._ticks = time; - return true; - } - - internal bool ParseInt(int max, out int i, ref TimeSpanResult result) - { - i = 0; - int p = _pos; - while (_ch >= '0' && _ch <= '9') - { - if ((i & 0xF0000000) != 0) - { - result.SetFailure(ParseFailureKind.Overflow, SR.Overflow_TimeSpanElementTooLarge); - return false; - } - i = i * 10 + _ch - '0'; - if (i < 0) - { - result.SetFailure(ParseFailureKind.Overflow, SR.Overflow_TimeSpanElementTooLarge); - return false; - } - NextChar(); - } - if (p == _pos) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan); - return false; - } - if (i > max) - { - result.SetFailure(ParseFailureKind.Overflow, SR.Overflow_TimeSpanElementTooLarge); - return false; - } - return true; - } - - internal bool ParseTime(out long time, ref TimeSpanResult result) - { - time = 0; - int unit; - if (!ParseInt(23, out unit, ref result)) - { - return false; - } - time = unit * TimeSpan.TicksPerHour; - if (_ch != ':') - { - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan); - return false; - } - NextChar(); - if (!ParseInt(59, out unit, ref result)) - { - return false; - } - time += unit * TimeSpan.TicksPerMinute; - if (_ch == ':') - { - NextChar(); - // allow seconds with the leading zero - if (_ch != '.') - { - if (!ParseInt(59, out unit, ref result)) - { - return false; - } - time += unit * TimeSpan.TicksPerSecond; - } - if (_ch == '.') - { - NextChar(); - int f = (int)TimeSpan.TicksPerSecond; - while (f > 1 && _ch >= '0' && _ch <= '9') - { - f /= 10; - time += (_ch - '0') * f; - NextChar(); - } - } - } - return true; - } - - internal void SkipBlanks() - { - while (_ch == ' ' || _ch == '\t') NextChar(); - } - } - #endregion - - #region TryParseExactMultipleTimeSpan - // - // TryParseExactMultipleTimeSpan - // - // Actions: Common private ParseExactMultiple method called by both ParseExactMultiple and TryParseExactMultiple - // - private static Boolean TryParseExactMultipleTimeSpan(String input, String[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, ref TimeSpanResult result) - { - if (input == null) - { - result.SetFailure(ParseFailureKind.ArgumentNull, SR.ArgumentNull_String, null, nameof(input)); - return false; - } - if (formats == null) - { - result.SetFailure(ParseFailureKind.ArgumentNull, SR.ArgumentNull_String, null, nameof(formats)); - return false; - } - - if (input.Length == 0) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan); - return false; - } - - if (formats.Length == 0) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_BadFormatSpecifier); - return false; - } - - // - // Do a loop through the provided formats and see if we can parse succesfully in - // one of the formats. - // - for (int i = 0; i < formats.Length; i++) - { - if (formats[i] == null || formats[i].Length == 0) - { - result.SetFailure(ParseFailureKind.Format, SR.Format_BadFormatSpecifier); - return false; - } - - // Create a new non-throwing result each time to ensure the runs are independent. - TimeSpanResult innerResult = new TimeSpanResult(); - innerResult.Init(TimeSpanThrowStyle.None); - - if (TryParseExactTimeSpan(input, formats[i], formatProvider, styles, ref innerResult)) - { - result.parsedTimeSpan = innerResult.parsedTimeSpan; - return true; - } - } - - result.SetFailure(ParseFailureKind.Format, SR.Format_BadTimeSpan); - return (false); - } - #endregion - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Guid.CoreRT.cs b/external/corert/src/System.Private.CoreLib/src/System/Guid.CoreRT.cs new file mode 100644 index 0000000000..3881ca4aaf --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Guid.CoreRT.cs @@ -0,0 +1,20 @@ +// 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 Internal.Runtime.CompilerServices; + +namespace System +{ + partial struct Guid + { + internal bool Equals(ref Guid g) + { + // Now compare each of the elements + return g._a == _a && + Unsafe.Add(ref g._a, 1) == Unsafe.Add(ref _a, 1) && + Unsafe.Add(ref g._a, 2) == Unsafe.Add(ref _a, 2) && + Unsafe.Add(ref g._a, 3) == Unsafe.Add(ref _a, 3); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Guid.Unix.cs b/external/corert/src/System.Private.CoreLib/src/System/Guid.Unix.cs index ed94b36d7e..326362d042 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Guid.Unix.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Guid.Unix.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.InteropServices; namespace System @@ -17,7 +16,6 @@ namespace System // CoCreateGuid should never return Guid.Empty, since it attempts to maintain some // uniqueness guarantees. It should also never return a known GUID, but it's unclear // how extensively it checks for known values. - Contract.Ensures(Contract.Result() != Guid.Empty); Interop.Sys.CreateGuid(out Guid g); return g; diff --git a/external/corert/src/System.Private.CoreLib/src/System/Guid.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/Guid.Windows.cs index 89fe587909..f21dfbd6b2 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Guid.Windows.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Guid.Windows.cs @@ -2,8 +2,6 @@ // 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.Diagnostics.Contracts; - namespace System { partial struct Guid @@ -15,7 +13,6 @@ namespace System // CoCreateGuid should never return Guid.Empty, since it attempts to maintain some // uniqueness guarantees. It should also never return a known GUID, but it's unclear // how extensively it checks for known values. - Contract.Ensures(Contract.Result() != Guid.Empty); Guid g; int hr = Interop.mincore.CoCreateGuid(out g); diff --git a/external/corert/src/System.Private.CoreLib/src/System/HighPerformanceCounter.Unix.cs b/external/corert/src/System.Private.CoreLib/src/System/HighPerformanceCounter.Unix.cs new file mode 100644 index 0000000000..06298f0f80 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/HighPerformanceCounter.Unix.cs @@ -0,0 +1,14 @@ +// 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. + +namespace System +{ + internal static class HighPerformanceCounter + { + public static ulong TickCount => Interop.Sys.GetHighPrecisionCount(); + + // Cache the frequency on the managed side to avoid the cost of P/Invoke on every access to Frequency + public static ulong Frequency { get; } = Interop.Sys.GetHighPrecisionCounterFrequency(); + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/HighPerformanceCounter.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/HighPerformanceCounter.Windows.cs new file mode 100644 index 0000000000..70be268d4b --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/HighPerformanceCounter.Windows.cs @@ -0,0 +1,26 @@ +// 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. + +namespace System +{ + internal static class HighPerformanceCounter + { + public static ulong TickCount + { + get + { + Interop.Kernel32.QueryPerformanceCounter(out ulong counter); + return counter; + } + } + + public static ulong Frequency { get; } = GetFrequency(); + + private static ulong GetFrequency() + { + Interop.Kernel32.QueryPerformanceFrequency(out ulong frequency); + return frequency; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/IO/BinaryReader.cs b/external/corert/src/System.Private.CoreLib/src/System/IO/BinaryReader.cs index 45d21d7bcb..314d01cc71 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/IO/BinaryReader.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/IO/BinaryReader.cs @@ -3,8 +3,10 @@ // See the LICENSE file in the project root for more information. using Internal.Runtime.CompilerServices; +using System; using System.Text; using System.Diagnostics; +using System.Runtime.InteropServices; namespace System.IO { @@ -293,7 +295,7 @@ namespace System.IO stringLength = Read7BitEncodedInt(); if (stringLength < 0) { - throw new IOException(SR.Format(SR.IO_IO_InvalidStringLen_Len, stringLength)); + throw new IOException(SR.Format(SR.IO_InvalidStringLen_Len, stringLength)); } if (stringLength == 0) @@ -365,17 +367,26 @@ namespace System.IO } // SafeCritical: index and count have already been verified to be a valid range for the buffer - return InternalReadChars(buffer, index, count); + return InternalReadChars(new Span(buffer, index, count)); } - private int InternalReadChars(char[] buffer, int index, int count) + public virtual int Read(Span buffer) + { + if (_stream == null) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_FileClosed); + } + + return InternalReadChars(buffer); + } + + private int InternalReadChars(Span buffer) { - Debug.Assert(buffer != null); - Debug.Assert(index >= 0 && count >= 0); Debug.Assert(_stream != null); int numBytes = 0; - int charsRemaining = count; + int index = 0; + int charsRemaining = buffer.Length; if (_charBytes == null) { @@ -418,11 +429,29 @@ namespace System.IO if (numBytes == 0) { - return (count - charsRemaining); + return (buffer.Length - charsRemaining); } Debug.Assert(byteBuffer != null, "expected byteBuffer to be non-null"); - charsRead = _decoder.GetChars(byteBuffer, position, numBytes, buffer, index, flush: false); + checked + { + if (position < 0 || numBytes < 0 || position > byteBuffer.Length - numBytes) + { + throw new ArgumentOutOfRangeException(nameof(numBytes)); + } + if (index < 0 || charsRemaining < 0 || index > buffer.Length - charsRemaining) + { + throw new ArgumentOutOfRangeException(nameof(charsRemaining)); + } + unsafe + { + fixed (byte* pBytes = byteBuffer) + fixed (char* pChars = &MemoryMarshal.GetReference(buffer)) + { + charsRead = _decoder.GetChars(pBytes + position, numBytes, pChars + index, charsRemaining, flush: false); + } + } + } charsRemaining -= charsRead; index += charsRead; @@ -433,7 +462,7 @@ namespace System.IO // we may have read fewer than the number of characters requested if end of stream reached // or if the encoding makes the char count too big for the buffer (e.g. fallback sequence) - return (count - charsRemaining); + return (buffer.Length - charsRemaining); } private int InternalReadOneChar() @@ -536,7 +565,7 @@ namespace System.IO // SafeCritical: we own the chars buffer, and therefore can guarantee that the index and count are valid char[] chars = new char[count]; - int n = InternalReadChars(chars, 0, count); + int n = InternalReadChars(new Span(chars)); if (n != count) { char[] copy = new char[n]; @@ -573,6 +602,16 @@ namespace System.IO return _stream.Read(buffer, index, count); } + public virtual int Read(Span buffer) + { + if (_stream == null) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_FileClosed); + } + + return _stream.Read(buffer); + } + public virtual byte[] ReadBytes(int count) { if (count < 0) diff --git a/external/corert/src/System.Private.CoreLib/src/System/IO/File.Unix.cs b/external/corert/src/System.Private.CoreLib/src/System/IO/InternalFile.Unix.cs similarity index 88% rename from external/corert/src/System.Private.CoreLib/src/System/IO/File.Unix.cs rename to external/corert/src/System.Private.CoreLib/src/System/IO/InternalFile.Unix.cs index 3cf5bf8360..a51c69bce1 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/IO/File.Unix.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/IO/InternalFile.Unix.cs @@ -4,7 +4,7 @@ namespace System.IO { - internal static partial class File + internal static partial class InternalFile { internal static bool InternalExists(String path) { diff --git a/external/corert/src/System.Private.CoreLib/src/System/IO/File.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/IO/InternalFile.Windows.cs similarity index 99% rename from external/corert/src/System.Private.CoreLib/src/System/IO/File.Windows.cs rename to external/corert/src/System.Private.CoreLib/src/System/IO/InternalFile.Windows.cs index 4bd8e94266..59dffd7d9e 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/IO/File.Windows.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/IO/InternalFile.Windows.cs @@ -8,7 +8,7 @@ using System.Runtime.InteropServices; namespace System.IO { - internal static partial class File + internal static partial class InternalFile { internal static bool InternalExists(String path) { diff --git a/external/corert/src/System.Private.CoreLib/src/System/IO/File.cs b/external/corert/src/System.Private.CoreLib/src/System/IO/InternalFile.cs similarity index 97% rename from external/corert/src/System.Private.CoreLib/src/System/IO/File.cs rename to external/corert/src/System.Private.CoreLib/src/System/IO/InternalFile.cs index 30513c8949..726e25419f 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/IO/File.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/IO/InternalFile.cs @@ -7,7 +7,7 @@ using System.Security; namespace System.IO { - internal static partial class File + internal static partial class InternalFile { // Tests if a file exists. The result is true if the file // given by the specified path exists; otherwise, the result is diff --git a/external/corert/src/System.Private.CoreLib/src/System/IO/Stream.cs b/external/corert/src/System.Private.CoreLib/src/System/IO/Stream.cs index 19a3a905e4..b4f8ab30de 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/IO/Stream.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/IO/Stream.cs @@ -2,8 +2,9 @@ // 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.Buffers; using System.Diagnostics; -using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -31,20 +32,17 @@ namespace System.IO public abstract bool CanRead { - [Pure] get; } // If CanSeek is false, Position, Seek, Length, and SetLength should throw. public abstract bool CanSeek { - [Pure] get; } public virtual bool CanTimeout { - [Pure] get { return false; @@ -53,7 +51,6 @@ namespace System.IO public abstract bool CanWrite { - [Pure] get; } @@ -94,36 +91,7 @@ namespace System.IO public Task CopyToAsync(Stream destination) { - int bufferSize = DefaultCopyBufferSize; - - if (CanSeek) - { - long length = Length; - long position = Position; - if (length <= position) // Handles negative overflows - { - // If we go down this branch, it means there are - // no bytes left in this stream. - - // Ideally we would just return Task.CompletedTask here, - // but CopyToAsync(Stream, int, CancellationToken) was already - // virtual at the time this optimization was introduced. So - // if it does things like argument validation (checking if destination - // is null and throwing an exception), then await fooStream.CopyToAsync(null) - // would no longer throw if there were no bytes left. On the other hand, - // we also can't roll our own argument validation and return Task.CompletedTask, - // because it would be a breaking change if the stream's override didn't throw before, - // or in a different order. So for simplicity, we just set the bufferSize to 1 - // (not 0 since the default implementation throws for 0) and forward to the virtual method. - bufferSize = 1; - } - else - { - long remaining = length - position; - if (remaining > 0) // In the case of a positive overflow, stick to the default size - bufferSize = (int)Math.Min(bufferSize, remaining); - } - } + int bufferSize = GetCopyBufferSize(); return CopyToAsync(destination, bufferSize); } @@ -133,6 +101,13 @@ namespace System.IO return CopyToAsync(destination, bufferSize, CancellationToken.None); } + public Task CopyToAsync(Stream destination, CancellationToken cancellationToken) + { + int bufferSize = GetCopyBufferSize(); + + return CopyToAsync(destination, bufferSize, cancellationToken); + } + public virtual Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) { StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize); @@ -161,26 +136,7 @@ namespace System.IO // the current position. public void CopyTo(Stream destination) { - int bufferSize = DefaultCopyBufferSize; - - if (CanSeek) - { - long length = Length; - long position = Position; - if (length <= position) // Handles negative overflows - { - // No bytes left in stream - // Call the other overload with a bufferSize of 1, - // in case it's made virtual in the future - bufferSize = 1; - } - else - { - long remaining = length - position; - if (remaining > 0) // In the case of a positive overflow, stick to the default size - bufferSize = (int)Math.Min(bufferSize, remaining); - } - } + int bufferSize = GetCopyBufferSize(); CopyTo(destination, bufferSize); } @@ -197,6 +153,36 @@ namespace System.IO } } + private int GetCopyBufferSize() + { + int bufferSize = DefaultCopyBufferSize; + + if (CanSeek) + { + long length = Length; + long position = Position; + if (length <= position) // Handles negative overflows + { + // There are no bytes left in the stream to copy. + // However, because CopyTo{Async} is virtual, we need to + // ensure that any override is still invoked to provide its + // own validation, so we use the smallest legal buffer size here. + bufferSize = 1; + } + else + { + long remaining = length - position; + if (remaining > 0) + { + // In the case of a positive overflow, stick to the default size + bufferSize = (int)Math.Min(bufferSize, remaining); + } + } + } + + return bufferSize; + } + public virtual void Close() { Dispose(true); @@ -246,11 +232,6 @@ namespace System.IO public virtual Task ReadAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken) { - if (!CanRead) - { - throw new NotSupportedException(SR.NotSupported_UnreadableStream); - } - return cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) : Task.Factory.FromAsync( @@ -259,8 +240,42 @@ namespace System.IO buffer, offset, count, this); } - public virtual IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) => - TaskToApm.Begin(ReadAsyncInternal(buffer, offset, count), callback, state); + public virtual ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default(CancellationToken)) + { + if (destination.TryGetArray(out ArraySegment array)) + { + return new ValueTask(ReadAsync(array.Array, array.Offset, array.Count, cancellationToken)); + } + else + { + byte[] buffer = ArrayPool.Shared.Rent(destination.Length); + return FinishReadAsync(ReadAsync(buffer, 0, destination.Length, cancellationToken), buffer, destination); + + async ValueTask FinishReadAsync(Task readTask, byte[] localBuffer, Memory localDestination) + { + try + { + int result = await readTask.ConfigureAwait(false); + new Span(localBuffer, 0, result).CopyTo(localDestination.Span); + return result; + } + finally + { + ArrayPool.Shared.Return(localBuffer); + } + } + } + } + + public virtual IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + { + if (!CanRead) + { + throw new NotSupportedException(SR.NotSupported_UnreadableStream); + } + + return TaskToApm.Begin(ReadAsyncInternal(buffer, offset, count), callback, state); + } public virtual int EndRead(IAsyncResult asyncResult) => TaskToApm.End(asyncResult); @@ -293,11 +308,6 @@ namespace System.IO public virtual Task WriteAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken) { - if (!CanWrite) - { - throw new NotSupportedException(SR.NotSupported_UnwritableStream); - } - return cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) : Task.Factory.FromAsync( @@ -306,8 +316,41 @@ namespace System.IO buffer, offset, count, this); } - public virtual IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) => - TaskToApm.Begin(WriteAsyncInternal(buffer, offset, count), callback, state); + public virtual Task WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken = default(CancellationToken)) + { + if (MemoryMarshal.TryGetArray(source, out ArraySegment array)) + { + return WriteAsync(array.Array, array.Offset, array.Count, cancellationToken); + } + else + { + byte[] buffer = ArrayPool.Shared.Rent(source.Length); + source.Span.CopyTo(buffer); + return FinishWriteAsync(WriteAsync(buffer, 0, source.Length, cancellationToken), buffer); + + async Task FinishWriteAsync(Task writeTask, byte[] localBuffer) + { + try + { + await writeTask.ConfigureAwait(false); + } + finally + { + ArrayPool.Shared.Return(localBuffer); + } + } + } + } + + public virtual IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + { + if (!CanWrite) + { + throw new NotSupportedException(SR.NotSupported_UnwritableStream); + } + + return TaskToApm.Begin(WriteAsyncInternal(buffer, offset, count), callback, state); + } public virtual void EndWrite(IAsyncResult asyncResult) => TaskToApm.End(asyncResult); @@ -339,6 +382,23 @@ namespace System.IO public abstract int Read(byte[] buffer, int offset, int count); + public virtual int Read(Span destination) + { + ArrayPool pool = ArrayPool.Shared; + byte[] buffer = pool.Rent(destination.Length); + try + { + int numRead = Read(buffer, 0, destination.Length); + if ((uint)numRead > destination.Length) + { + throw new IOException(SR.IO_StreamTooLong); + } + new Span(buffer, 0, numRead).CopyTo(destination); + return numRead; + } + finally { pool.Return(buffer); } + } + // Reads one byte from the stream by calling Read(byte[], int, int). // Will return an unsigned byte cast to an int or -1 on end of stream. // This implementation does not perform well because it allocates a new @@ -358,6 +418,18 @@ namespace System.IO public abstract void Write(byte[] buffer, int offset, int count); + public virtual void Write(ReadOnlySpan source) + { + ArrayPool pool = ArrayPool.Shared; + byte[] buffer = pool.Rent(source.Length); + try + { + source.CopyTo(buffer); + Write(buffer, 0, source.Length); + } + finally { pool.Return(buffer); } + } + // Writes one byte from the stream by calling Write(byte[], int, int). // This implementation does not perform well because it allocates a new // byte[] each time you call it, and should be overridden by any @@ -390,31 +462,13 @@ namespace System.IO { internal NullStream() { } - public override bool CanRead - { - [Pure] - get - { return true; } - } + public override bool CanRead => true; - public override bool CanWrite - { - [Pure] - get - { return true; } - } + public override bool CanWrite => true; - public override bool CanSeek - { - [Pure] - get - { return true; } - } + public override bool CanSeek => true; - public override long Length - { - get { return 0; } - } + public override long Length => 0; public override long Position { @@ -461,12 +515,23 @@ namespace System.IO return 0; } + public override int Read(Span destination) + { + return 0; + } + #pragma warning disable 1998 // async method with no await public override async Task ReadAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); return 0; } + + public override async ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return 0; + } #pragma warning restore 1998 public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) => @@ -484,11 +549,20 @@ namespace System.IO { } + public override void Write(ReadOnlySpan source) + { + } + #pragma warning disable 1998 // async method with no await public override async Task WriteAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); } + + public override async Task WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + } #pragma warning restore 1998 public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) => @@ -513,7 +587,6 @@ namespace System.IO // SyncStream is a wrapper around a stream that takes // a lock for every operation making it thread safe. - [Serializable] private sealed class SyncStream : Stream, IDisposable { private Stream _stream; @@ -526,32 +599,13 @@ namespace System.IO _stream = stream; } - public override bool CanRead - { - [Pure] - get { return _stream.CanRead; } - } + public override bool CanRead => _stream.CanRead; - public override bool CanWrite - { - [Pure] - get { return _stream.CanWrite; } - } + public override bool CanWrite => _stream.CanWrite; - public override bool CanSeek - { - [Pure] - get { return _stream.CanSeek; } - } + public override bool CanSeek => _stream.CanSeek; - public override bool CanTimeout - { - [Pure] - get - { - return _stream.CanTimeout; - } - } + public override bool CanTimeout => _stream.CanTimeout; public override long Length { @@ -652,6 +706,12 @@ namespace System.IO return _stream.Read(bytes, offset, count); } + public override int Read(Span destination) + { + lock (_stream) + return _stream.Read(destination); + } + public override int ReadByte() { lock (_stream) @@ -704,6 +764,12 @@ namespace System.IO _stream.Write(bytes, offset, count); } + public override void Write(ReadOnlySpan source) + { + lock (_stream) + _stream.Write(source); + } + public override void WriteByte(byte b) { lock (_stream) diff --git a/external/corert/src/System.Private.CoreLib/src/System/IO/UnmanagedMemoryAccessor.cs b/external/corert/src/System.Private.CoreLib/src/System/IO/UnmanagedMemoryAccessor.cs deleted file mode 100644 index 2e572babaf..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/IO/UnmanagedMemoryAccessor.cs +++ /dev/null @@ -1,965 +0,0 @@ -// 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.Diagnostics; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; - -namespace System.IO -{ - /// Perf notes: ReadXXX, WriteXXX (for basic types) acquire and release the - /// SafeBuffer pointer rather than relying on generic Read(T) from SafeBuffer because - /// this gives better throughput; benchmarks showed about 12-15% better. - public class UnmanagedMemoryAccessor : IDisposable - { - private SafeBuffer _buffer; - private Int64 _offset; - private Int64 _capacity; - private FileAccess _access; - private bool _isOpen; - private bool _canRead; - private bool _canWrite; - - /// - /// Allows to efficiently read typed data from memory or SafeBuffer - /// - protected UnmanagedMemoryAccessor() - { - _isOpen = false; - } - - #region SafeBuffer ctors and initializers - /// - /// Creates an instance over a slice of a SafeBuffer. - /// - /// Buffer containing raw bytes. - /// First byte belonging to the slice. - /// Number of bytes in the slice. - // - public UnmanagedMemoryAccessor(SafeBuffer buffer, Int64 offset, Int64 capacity) - { - Initialize(buffer, offset, capacity, FileAccess.Read); - } - - /// - /// Creates an instance over a slice of a SafeBuffer. - /// - /// Buffer containing raw bytes. - /// First byte belonging to the slice. - /// Number of bytes in the slice. - /// Access permissions. - public UnmanagedMemoryAccessor(SafeBuffer buffer, Int64 offset, Int64 capacity, FileAccess access) - { - Initialize(buffer, offset, capacity, access); - } - - protected void Initialize(SafeBuffer buffer, Int64 offset, Int64 capacity, FileAccess access) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - if (offset < 0) - { - throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); - } - if (capacity < 0) - { - throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_NeedNonNegNum); - } - if (buffer.ByteLength < (UInt64)(offset + capacity)) - { - throw new ArgumentException(SR.Argument_OffsetAndCapacityOutOfBounds); - } - if (access < FileAccess.Read || access > FileAccess.ReadWrite) - { - throw new ArgumentOutOfRangeException(nameof(access)); - } - Contract.EndContractBlock(); - - if (_isOpen) - { - throw new InvalidOperationException(SR.InvalidOperation_CalledTwice); - } - - unsafe - { - byte* pointer = null; - - try - { - buffer.AcquirePointer(ref pointer); - if (((byte*)((Int64)pointer + offset + capacity)) < pointer) - { - throw new ArgumentException(SR.Argument_UnmanagedMemAccessorWrapAround); - } - } - finally - { - if (pointer != null) - { - buffer.ReleasePointer(); - } - } - } - - _offset = offset; - _buffer = buffer; - _capacity = capacity; - _access = access; - _isOpen = true; - _canRead = (_access & FileAccess.Read) != 0; - _canWrite = (_access & FileAccess.Write) != 0; - } - - #endregion - - /// - /// Number of bytes in the accessor. - /// - public Int64 Capacity - { - get - { - return _capacity; - } - } - - /// - /// Returns true if the accessor can be read; otherwise returns false. - /// - public bool CanRead - { - get - { - return _isOpen && _canRead; - } - } - - /// - /// Returns true if the accessor can be written to; otherwise returns false. - /// - public bool CanWrite - { - get - { - return _isOpen && _canWrite; - } - } - - /// - /// Closes the accessor. - /// - /// - protected virtual void Dispose(bool disposing) - { - _isOpen = false; - } - - /// - /// Closes the accessor. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Returns true if the accessor is open. - /// - protected bool IsOpen - { - get { return _isOpen; } - } - - /// - /// reads a Boolean value at given position - /// - public bool ReadBoolean(Int64 position) - { - return ReadByte(position) != 0; - } - - /// - /// reads a Byte value at given position - /// - public byte ReadByte(Int64 position) - { - int sizeOfType = sizeof(byte); - EnsureSafeToRead(position, sizeOfType); - - byte result; - unsafe - { - byte* pointer = null; - - try - { - _buffer.AcquirePointer(ref pointer); - result = *((byte*)(pointer + _offset + position)); - } - finally - { - if (pointer != null) - { - _buffer.ReleasePointer(); - } - } - } - return result; - } - - /// - /// reads a Char value at given position - /// - public char ReadChar(Int64 position) - { - return (char)ReadInt16(position); - } - - /// - /// reads an Int16 value at given position - /// - public Int16 ReadInt16(Int64 position) - { - int sizeOfType = sizeof(Int16); - EnsureSafeToRead(position, sizeOfType); - - Int16 result; - - unsafe - { - byte* pointer = null; - - try - { - _buffer.AcquirePointer(ref pointer); - pointer += (_offset + position); - - - // check if pointer is aligned - if (((int)pointer & (sizeOfType - 1)) == 0) - { - result = *((Int16*)(pointer)); - } - else - { - result = (Int16)(*pointer | *(pointer + 1) << 8); - } - } - finally - { - if (pointer != null) - { - _buffer.ReleasePointer(); - } - } - } - - return result; - } - - /// - /// reads an Int32 value at given position - /// - public Int32 ReadInt32(Int64 position) - { - int sizeOfType = sizeof(Int32); - EnsureSafeToRead(position, sizeOfType); - - Int32 result; - unsafe - { - byte* pointer = null; - - try - { - _buffer.AcquirePointer(ref pointer); - pointer += (_offset + position); - - - // check if pointer is aligned - if (((int)pointer & (sizeOfType - 1)) == 0) - { - result = *((Int32*)(pointer)); - } - else - { - result = (Int32)(*pointer | *(pointer + 1) << 8 | *(pointer + 2) << 16 | *(pointer + 3) << 24); - } - } - finally - { - if (pointer != null) - { - _buffer.ReleasePointer(); - } - } - } - - return result; - } - - /// - /// reads an Int64 value at given position - /// - public Int64 ReadInt64(Int64 position) - { - int sizeOfType = sizeof(Int64); - EnsureSafeToRead(position, sizeOfType); - - Int64 result; - unsafe - { - byte* pointer = null; - - try - { - _buffer.AcquirePointer(ref pointer); - pointer += (_offset + position); - - // check if pointer is aligned - if (((int)pointer & (sizeOfType - 1)) == 0) - { - result = *((Int64*)(pointer)); - } - else - { - int lo = *pointer | *(pointer + 1) << 8 | *(pointer + 2) << 16 | *(pointer + 3) << 24; - int hi = *(pointer + 4) | *(pointer + 5) << 8 | *(pointer + 6) << 16 | *(pointer + 7) << 24; - result = (Int64)(((Int64)hi << 32) | (UInt32)lo); - } - } - finally - { - if (pointer != null) - { - _buffer.ReleasePointer(); - } - } - } - - return result; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe Int32 UnsafeReadInt32(byte* pointer) - { - Int32 result; - // check if pointer is aligned - if (((int)pointer & (sizeof(Int32) - 1)) == 0) - { - result = *((Int32*)pointer); - } - else - { - result = (Int32)(*(pointer) | *(pointer + 1) << 8 | *(pointer + 2) << 16 | *(pointer + 3) << 24); - } - - return result; - } - - /// - /// Reads a Decimal value at the specified position. - /// - /// The position of the first byte of the value. - /// - public Decimal ReadDecimal(Int64 position) - { - const int ScaleMask = 0x00FF0000; - const int SignMask = unchecked((int)0x80000000); - - int sizeOfType = sizeof(Decimal); - EnsureSafeToRead(position, sizeOfType); - - unsafe - { - byte* pointer = null; - try - { - _buffer.AcquirePointer(ref pointer); - pointer += (_offset + position); - - int lo = UnsafeReadInt32(pointer); - int mid = UnsafeReadInt32(pointer + 4); - int hi = UnsafeReadInt32(pointer + 8); - int flags = UnsafeReadInt32(pointer + 12); - - // Check for invalid Decimal values - if (!((flags & ~(SignMask | ScaleMask)) == 0 && (flags & ScaleMask) <= (28 << 16))) - { - throw new ArgumentException(SR.Arg_BadDecimal); // Throw same Exception type as Decimal(int[]) ctor for compat - } - - bool isNegative = (flags & SignMask) != 0; - byte scale = (byte)(flags >> 16); - - return new decimal(lo, mid, hi, isNegative, scale); - } - finally - { - if (pointer != null) - { - _buffer.ReleasePointer(); - } - } - } - } - - /// - /// reads a Single value at given position - /// - /// - /// - public Single ReadSingle(Int64 position) - { - unsafe - { - Int32 result = ReadInt32(position); - return *((Single*)&result); - } - } - - /// - /// reads a Double value at given position - /// - public Double ReadDouble(Int64 position) - { - unsafe - { - Int64 result = ReadInt64(position); - return *((Double*)&result); - } - } - - /// - /// Reads an SByte value at the specified position. - /// - /// The position of the first byte of the value. - /// - [CLSCompliant(false)] - public SByte ReadSByte(Int64 position) - { - return (SByte)ReadByte(position); - } - - /// - /// reads a UInt16 value at given position - /// - [CLSCompliant(false)] - public UInt16 ReadUInt16(Int64 position) - { - return (UInt16)ReadInt16(position); - } - - /// - /// Reads a UInt32 value at the specified position. - /// - /// The position of the first byte of the value. - /// - [CLSCompliant(false)] - public UInt32 ReadUInt32(Int64 position) - { - return (UInt32)ReadInt32(position); - } - - /// - /// Reads a UInt64 value at the specified position. - /// - /// The position of the first byte of the value. - /// - [CLSCompliant(false)] - public UInt64 ReadUInt64(Int64 position) - { - return (UInt64)ReadInt64(position); - } - - /// - // Reads a struct of type T from unmanaged memory, into the reference pointed to by ref value. - // Note: this method is not safe, since it overwrites the contents of a structure, it can be - // used to modify the private members of a struct. - // This method is most performant when used with medium to large sized structs - // (larger than 8 bytes -- though this is number is JIT and architecture dependent). As - // such, it is best to use the ReadXXX methods for small standard types such as ints, longs, - // bools, etc. - /// - public void Read(Int64 position, out T structure) where T : struct - { - int sizeOfType = Unsafe.SizeOf(); - EnsureSafeToRead(position, sizeOfType); - - structure = _buffer.Read((UInt64)(_offset + position)); - } - - /// - // Reads 'count' structs of type T from unmanaged memory, into 'array' starting at 'offset'. - // Note: this method is not safe, since it overwrites the contents of structures, it can - // be used to modify the private members of a struct. - /// - public int ReadArray(Int64 position, T[] array, Int32 offset, Int32 count) where T : struct - { - if (array == null) - { - throw new ArgumentNullException(nameof(array), SR.ArgumentNull_Buffer); - } - if (offset < 0) - { - throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); - } - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - } - if (array.Length - offset < count) - { - throw new ArgumentException(SR.Argument_InvalidOffLen); - } - Contract.EndContractBlock(); - - EnsureSafeToRead(position, 0); - - // ask for fewer Ts if count is too big - - UInt32 sizeOfT = SafeBuffer.AlignedSizeOf(); - - int n = count; - long spaceLeft = _capacity - position; - if (spaceLeft < 0) - { - n = 0; - } - else - { - ulong spaceNeeded = (ulong)(sizeOfT * count); - if ((ulong)spaceLeft < spaceNeeded) - { - n = (int)(spaceLeft / sizeOfT); - } - } - - _buffer.ReadArray((UInt64)(_offset + position), array, offset, n); - - return n; - } - - // ************** Write Methods ****************/ - - // The following 13 WriteXXX methods write a value of type XXX into unmanaged memory at 'position'. - // The bounds of the unmanaged memory are checked against to ensure that there is enough - // space after 'position' to write a value of type XXX. XXX can be a bool, byte, char, decimal, - // double, short, int, long, sbyte, float, ushort, uint, or ulong. - - /// - /// Writes the value at the specified position. - /// - /// The position of the first byte. - /// Value to be written to the memory - public void Write(Int64 position, bool value) - { - Write(position, (byte)(value ? 1 : 0)); - } - - /// - /// Writes the value at the specified position. - /// - /// The position of the first byte. - /// Value to be written to the memory - public void Write(Int64 position, byte value) - { - int sizeOfType = sizeof(byte); - EnsureSafeToWrite(position, sizeOfType); - - unsafe - { - byte* pointer = null; - - try - { - _buffer.AcquirePointer(ref pointer); - *((byte*)(pointer + _offset + position)) = value; - } - finally - { - if (pointer != null) - { - _buffer.ReleasePointer(); - } - } - } - } - - /// - /// Writes the value at the specified position. - /// - /// The position of the first byte. - /// Value to be written to the memory - public void Write(Int64 position, char value) - { - Write(position, (Int16)value); - } - - /// - /// Writes the value at the specified position. - /// - /// The position of the first byte. - /// Value to be written to the memory - public void Write(Int64 position, Int16 value) - { - int sizeOfType = sizeof(Int16); - EnsureSafeToWrite(position, sizeOfType); - - unsafe - { - byte* pointer = null; - - try - { - _buffer.AcquirePointer(ref pointer); - pointer += (_offset + position); - - // check if pointer is aligned - if (((int)pointer & (sizeOfType - 1)) == 0) - { - *((Int16*)pointer) = value; - } - else - { - *(pointer) = (byte)value; - *(pointer + 1) = (byte)(value >> 8); - } - } - finally - { - if (pointer != null) - { - _buffer.ReleasePointer(); - } - } - } - } - - /// - /// Writes the value at the specified position. - /// - /// The position of the first byte. - /// Value to be written to the memory - public void Write(Int64 position, Int32 value) - { - int sizeOfType = sizeof(Int32); - EnsureSafeToWrite(position, sizeOfType); - - unsafe - { - byte* pointer = null; - - try - { - _buffer.AcquirePointer(ref pointer); - pointer += (_offset + position); - - // check if pointer is aligned - if (((int)pointer & (sizeOfType - 1)) == 0) - { - *((Int32*)pointer) = value; - } - else - { - *(pointer) = (byte)value; - *(pointer + 1) = (byte)(value >> 8); - *(pointer + 2) = (byte)(value >> 16); - *(pointer + 3) = (byte)(value >> 24); - } - } - finally - { - if (pointer != null) - { - _buffer.ReleasePointer(); - } - } - } - } - - /// - /// Writes the value at the specified position. - /// - /// The position of the first byte. - /// Value to be written to the memory - public void Write(Int64 position, Int64 value) - { - int sizeOfType = sizeof(Int64); - EnsureSafeToWrite(position, sizeOfType); - - unsafe - { - byte* pointer = null; - - try - { - _buffer.AcquirePointer(ref pointer); - pointer += (_offset + position); - - // check if pointer is aligned - if (((int)pointer & (sizeOfType - 1)) == 0) - { - *((Int64*)pointer) = value; - } - else - { - *(pointer) = (byte)value; - *(pointer + 1) = (byte)(value >> 8); - *(pointer + 2) = (byte)(value >> 16); - *(pointer + 3) = (byte)(value >> 24); - *(pointer + 4) = (byte)(value >> 32); - *(pointer + 5) = (byte)(value >> 40); - *(pointer + 6) = (byte)(value >> 48); - *(pointer + 7) = (byte)(value >> 56); - } - } - finally - { - if (pointer != null) - { - _buffer.ReleasePointer(); - } - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe void UnsafeWriteInt32(byte* pointer, Int32 value) - { - // check if pointer is aligned - if (((int)pointer & (sizeof(Int32) - 1)) == 0) - { - *((Int32*)pointer) = value; - } - else - { - *(pointer) = (byte)value; - *(pointer + 1) = (byte)(value >> 8); - *(pointer + 2) = (byte)(value >> 16); - *(pointer + 3) = (byte)(value >> 24); - } - } - - /// - /// Writes the value at the specified position. - /// - /// The position of the first byte. - /// Value to be written to the memory - public void Write(Int64 position, Decimal value) - { - int sizeOfType = sizeof(Decimal); - EnsureSafeToWrite(position, sizeOfType); - - unsafe - { - byte* pointer = null; - try - { - _buffer.AcquirePointer(ref pointer); - pointer += (_offset + position); - - int* valuePtr = (int*)(&value); - int flags = *valuePtr; - int hi = *(valuePtr + 1); - int lo = *(valuePtr + 2); - int mid = *(valuePtr + 3); - - UnsafeWriteInt32(pointer, lo); - UnsafeWriteInt32(pointer + 4, mid); - UnsafeWriteInt32(pointer + 8, hi); - UnsafeWriteInt32(pointer + 12, flags); - } - finally - { - if (pointer != null) - { - _buffer.ReleasePointer(); - } - } - } - } - - /// - /// Writes the value at the specified position. - /// - /// The position of the first byte. - /// Value to be written to the memory - public void Write(Int64 position, Single value) - { - unsafe - { - Write(position, *(Int32*)&value); - } - } - - /// - /// Writes the value at the specified position. - /// - /// The position of the first byte. - /// Value to be written to the memory - public void Write(Int64 position, Double value) - { - unsafe - { - Write(position, *(Int64*)&value); - } - } - - /// - /// Writes the value at the specified position. - /// - /// The position of the first byte. - /// Value to be written to the memory - [CLSCompliant(false)] - public void Write(Int64 position, SByte value) - { - Write(position, (byte)value); - } - - /// - /// Writes the value at the specified position. - /// - /// The position of the first byte. - /// Value to be written to the memory - [CLSCompliant(false)] - public void Write(Int64 position, UInt16 value) - { - Write(position, (Int16)value); - } - - /// - /// Writes the value at the specified position. - /// - /// The position of the first byte. - /// Value to be written to the memory - [CLSCompliant(false)] - public void Write(Int64 position, UInt32 value) - { - Write(position, (Int32)value); - } - - /// - /// Writes the value at the specified position. - /// - /// The position of the first byte. - /// Value to be written to the memory - [CLSCompliant(false)] - public void Write(Int64 position, UInt64 value) - { - Write(position, (Int64)value); - } - - /// - // Writes the struct pointed to by ref value into unmanaged memory. Note that this method - // is most performant when used with medium to large sized structs (larger than 8 bytes - // though this is number is JIT and architecture dependent). As such, it is best to use - // the WriteX methods for small standard types such as ints, longs, bools, etc. - /// - public void Write(Int64 position, ref T structure) where T : struct - { - int sizeOfType = Unsafe.SizeOf(); - EnsureSafeToWrite(position, sizeOfType); - - _buffer.Write((UInt64)(_offset + position), structure); - } - - /// - // Writes 'count' structs of type T from 'array' (starting at 'offset') into unmanaged memory. - /// - public void WriteArray(Int64 position, T[] array, Int32 offset, Int32 count) where T : struct - { - if (array == null) - { - throw new ArgumentNullException(nameof(array), SR.ArgumentNull_Buffer); - } - if (offset < 0) - { - throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); - } - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); - } - if (array.Length - offset < count) - { - throw new ArgumentException(SR.Argument_InvalidOffLen); - } - Contract.EndContractBlock(); - - EnsureSafeToWrite(position, 0); - - UInt32 sizeOfT = SafeBuffer.AlignedSizeOf(); - - long spaceLeft = _capacity - position; - ulong spaceNeeded = (ulong)(sizeOfT * count); - if ((ulong)spaceLeft < spaceNeeded) - { - throw new ArgumentException(SR.Argument_NotEnoughBytesToWrite, nameof(position)); - } - - _buffer.WriteArray((UInt64)(_offset + position), array, offset, count); - } - - private void EnsureSafeToRead(Int64 position, int sizeOfType) - { - if (!_isOpen) - { - throw new ObjectDisposedException(nameof(UnmanagedMemoryAccessor), SR.ObjectDisposed_ViewAccessorClosed); - } - if (!_canRead) - { - throw new NotSupportedException(SR.NotSupported_Reading); - } - if (position < 0) - { - throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum); - } - if (position > _capacity - sizeOfType) - { - if (position >= _capacity) - { - throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired); - } - else - { - throw new ArgumentException(SR.Argument_NotEnoughBytesToRead, nameof(position)); - } - } - } - - private void EnsureSafeToWrite(Int64 position, int sizeOfType) - { - if (!_isOpen) - { - throw new ObjectDisposedException(nameof(UnmanagedMemoryAccessor), SR.ObjectDisposed_ViewAccessorClosed); - } - if (!_canWrite) - { - throw new NotSupportedException(SR.NotSupported_Writing); - } - if (position < 0) - { - throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum); - } - if (position > _capacity - sizeOfType) - { - if (position >= _capacity) - { - throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired); - } - else - { - throw new ArgumentException(SR.Argument_NotEnoughBytesToWrite, nameof(position)); - } - } - } - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/System/InsufficientMemoryException.cs b/external/corert/src/System.Private.CoreLib/src/System/InsufficientMemoryException.cs index 36c174cdec..fbde8ce1f6 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/InsufficientMemoryException.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/InsufficientMemoryException.cs @@ -22,6 +22,7 @@ using System.Runtime.Serialization; namespace System { [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class InsufficientMemoryException : OutOfMemoryException { // There may be a problem here interacting with the ResourceManager in out of memory conditions, @@ -29,21 +30,23 @@ namespace System public InsufficientMemoryException() : base(SR.Arg_OutOfMemoryException) { - HResult = __HResults.COR_E_INSUFFICIENTMEMORY; + HResult = HResults.COR_E_INSUFFICIENTMEMORY; } public InsufficientMemoryException(String message) : base(message) { - HResult = __HResults.COR_E_INSUFFICIENTMEMORY; + HResult = HResults.COR_E_INSUFFICIENTMEMORY; } public InsufficientMemoryException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_INSUFFICIENTMEMORY; + HResult = HResults.COR_E_INSUFFICIENTMEMORY; } - internal InsufficientMemoryException(SerializationInfo info, StreamingContext context) : base(info, context) { } + private InsufficientMemoryException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/InvokeUtils.cs b/external/corert/src/System.Private.CoreLib/src/System/InvokeUtils.cs index f33b4ecd93..8b92cfef31 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/InvokeUtils.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/InvokeUtils.cs @@ -10,6 +10,7 @@ using System.Diagnostics; using Internal.Reflection.Core.NonPortable; using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; namespace System { @@ -123,18 +124,24 @@ namespace System return CreateChangeTypeException(srcEEType, dstEEType, semantics); } - if (!((srcEEType.IsEnum || srcEEType.IsPrimitive) && (dstEEType.IsEnum || dstEEType.IsPrimitive || dstEEType.IsPointer))) + if (dstEEType.IsPointer) + { + Exception exception = ConvertPointerIfPossible(srcObject, srcEEType, dstEEType, semantics, out IntPtr dstIntPtr); + if (exception != null) + { + dstObject = null; + return exception; + } + dstObject = dstIntPtr; + return null; + } + + if (!(srcEEType.IsPrimitive && dstEEType.IsPrimitive)) { dstObject = null; return CreateChangeTypeException(srcEEType, dstEEType, semantics); } - if (dstEEType.IsPointer) - { - dstObject = null; - return new NotImplementedException(); //TODO: https://github.com/dotnet/corert/issues/2113 - } - RuntimeImports.RhCorElementType dstCorElementType = dstEEType.CorElementType; if (!srcEEType.CorElementTypeInfo.CanWidenTo(dstCorElementType)) { @@ -145,43 +152,53 @@ namespace System switch (dstCorElementType) { case RuntimeImports.RhCorElementType.ELEMENT_TYPE_BOOLEAN: - dstObject = Convert.ToBoolean(srcObject); + bool boolValue = Convert.ToBoolean(srcObject); + dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, boolValue ? 1 : 0) : boolValue; break; case RuntimeImports.RhCorElementType.ELEMENT_TYPE_CHAR: - dstObject = Convert.ToChar(srcObject); + char charValue = Convert.ToChar(srcObject); + dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, charValue) : charValue; break; case RuntimeImports.RhCorElementType.ELEMENT_TYPE_I1: - dstObject = Convert.ToSByte(srcObject); + sbyte sbyteValue = Convert.ToSByte(srcObject); + dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, sbyteValue) : sbyteValue; break; case RuntimeImports.RhCorElementType.ELEMENT_TYPE_I2: - dstObject = Convert.ToInt16(srcObject); + short shortValue = Convert.ToInt16(srcObject); + dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, shortValue) : shortValue; break; case RuntimeImports.RhCorElementType.ELEMENT_TYPE_I4: - dstObject = Convert.ToInt32(srcObject); + int intValue = Convert.ToInt32(srcObject); + dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, intValue) : intValue; break; case RuntimeImports.RhCorElementType.ELEMENT_TYPE_I8: - dstObject = Convert.ToInt64(srcObject); + long longValue = Convert.ToInt64(srcObject); + dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, longValue) : longValue; break; case RuntimeImports.RhCorElementType.ELEMENT_TYPE_U1: - dstObject = Convert.ToByte(srcObject); + byte byteValue = Convert.ToByte(srcObject); + dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, byteValue) : byteValue; break; case RuntimeImports.RhCorElementType.ELEMENT_TYPE_U2: - dstObject = Convert.ToUInt16(srcObject); + ushort ushortValue = Convert.ToUInt16(srcObject); + dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, ushortValue) : ushortValue; break; case RuntimeImports.RhCorElementType.ELEMENT_TYPE_U4: - dstObject = Convert.ToUInt32(srcObject); + uint uintValue = Convert.ToUInt32(srcObject); + dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, uintValue) : uintValue; break; case RuntimeImports.RhCorElementType.ELEMENT_TYPE_U8: - dstObject = Convert.ToUInt64(srcObject); + ulong ulongValue = Convert.ToUInt64(srcObject); + dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, (long)ulongValue) : ulongValue; break; case RuntimeImports.RhCorElementType.ELEMENT_TYPE_R4: @@ -207,21 +224,36 @@ namespace System break; default: - Debug.Assert(false, "Unexpected CorElementType: " + dstCorElementType + ": Not a valid widening target."); + Debug.Fail("Unexpected CorElementType: " + dstCorElementType + ": Not a valid widening target."); dstObject = null; return CreateChangeTypeException(srcEEType, dstEEType, semantics); } - if (dstEEType.IsEnum) - { - Type dstType = ReflectionCoreNonPortable.GetRuntimeTypeForEEType(dstEEType); - dstObject = Enum.ToObject(dstType, dstObject); - } - Debug.Assert(dstObject.EETypePtr == dstEEType); return null; } + private static Exception ConvertPointerIfPossible(object srcObject, EETypePtr srcEEType, EETypePtr dstEEType, CheckArgumentSemantics semantics, out IntPtr dstIntPtr) + { + if (srcObject is IntPtr srcIntPtr) + { + dstIntPtr = srcIntPtr; + return null; + } + + if (srcObject is Pointer srcPointer) + { + if (dstEEType == typeof(void*).TypeHandle.ToEETypePtr() || RuntimeImports.AreTypesAssignable(pSourceType: srcPointer.GetPointerType().TypeHandle.ToEETypePtr(), pTargetType: dstEEType)) + { + dstIntPtr = srcPointer.GetPointerValue(); + return null; + } + } + + dstIntPtr = IntPtr.Zero; + return CreateChangeTypeException(srcEEType, dstEEType, semantics); + } + private static Exception CreateChangeTypeException(EETypePtr srcEEType, EETypePtr dstEEType, CheckArgumentSemantics semantics) { switch (semantics) @@ -232,7 +264,7 @@ namespace System case CheckArgumentSemantics.ArraySet: return CreateChangeTypeInvalidCastException(srcEEType, dstEEType); default: - Debug.Assert(false, "Unexpected CheckArgumentSemantics value: " + semantics); + Debug.Fail("Unexpected CheckArgumentSemantics value: " + semantics); throw new InvalidOperationException(); } } @@ -338,6 +370,7 @@ namespace System object targetMethodOrDelegate, object[] parameters, BinderBundle binderBundle, + bool wrapInTargetInvocationException, bool invokeMethodHelperIsThisCall = true, bool methodToCallIsThisCall = true) { @@ -346,7 +379,6 @@ namespace System // isn't always the exact type (byref stripped off, enums converted to int, etc.) Debug.Assert(!(binderBundle != null && !(targetMethodOrDelegate is MethodBase)), "The only callers that can pass a custom binder are those servicing MethodBase.Invoke() apis."); - bool fDontWrapInTargetInvocationException = false; bool parametersNeedCopyBack = false; ArgSetupState argSetupState = default(ArgSetupState); @@ -404,6 +436,10 @@ namespace System return result; } + catch (Exception e) when (wrapInTargetInvocationException && argSetupState.fComplete) + { + throw new TargetInvocationException(e); + } finally { if (parametersNeedCopyBack) @@ -411,11 +447,7 @@ namespace System Array.Copy(s_parameters, parameters, parameters.Length); } - if (!argSetupState.fComplete) - { - fDontWrapInTargetInvocationException = true; - } - else + if (argSetupState.fComplete) { // Nullable objects can't take advantage of the ability to update the boxed value on the heap directly, so perform // an update of the parameters array now. @@ -432,17 +464,6 @@ namespace System } } } - catch (Exception e) - { - if (fDontWrapInTargetInvocationException) - { - throw; - } - else - { - throw new System.Reflection.TargetInvocationException(e); - } - } finally { // Restore state of thread static helper statics @@ -488,7 +509,7 @@ namespace System ref ArgSetupState argSetupState) { // This method is implemented elsewhere in the toolchain - throw new PlatformNotSupportedException(); + throw new NotSupportedException(); } [DebuggerStepThrough] @@ -500,7 +521,7 @@ namespace System bool isTargetThisCall) { // This method is implemented elsewhere in the toolchain - throw new PlatformNotSupportedException(); + throw new NotSupportedException(); } [DebuggerStepThrough] @@ -513,7 +534,7 @@ namespace System bool isTargetThisCall) { // This method is implemented elsewhere in the toolchain - throw new PlatformNotSupportedException(); + throw new NotSupportedException(); } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/MDArray.cs.REMOVED.git-id b/external/corert/src/System.Private.CoreLib/src/System/MDArray.cs.REMOVED.git-id index 908c77f699..dd4807f766 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/MDArray.cs.REMOVED.git-id +++ b/external/corert/src/System.Private.CoreLib/src/System/MDArray.cs.REMOVED.git-id @@ -1 +1 @@ -a7256bdc7eda4f47f3fc4bb64362a943f3ac923a \ No newline at end of file +6e42ea5600b2f76dd40380dd66fb47932bdb92ad \ No newline at end of file diff --git a/external/corert/src/System.Private.CoreLib/src/System/Marvin.cs b/external/corert/src/System.Private.CoreLib/src/System/Marvin.cs index 91a7c1021b..32ae26eb13 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Marvin.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Marvin.cs @@ -5,6 +5,8 @@ using System.Diagnostics; using System.Runtime.CompilerServices; +using Internal.Runtime.CompilerServices; + namespace System { internal static class Marvin diff --git a/external/corert/src/System.Private.CoreLib/src/System/Math.CoreRT.cs b/external/corert/src/System.Private.CoreLib/src/System/Math.CoreRT.cs new file mode 100644 index 0000000000..dc4e40a175 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Math.CoreRT.cs @@ -0,0 +1,146 @@ +// 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. + +/*============================================================ +** +** Purpose: Some floating-point math operations +** +===========================================================*/ + +//This class contains only static members and doesn't require serialization. + +using System.Runtime; +using System.Runtime.CompilerServices; + +namespace System +{ + public static partial class Math + { + [Intrinsic] + public static float Abs(float value) + { + return (float)RuntimeImports.fabs(value); + } + + [Intrinsic] + public static double Abs(double value) + { + return RuntimeImports.fabs(value); + } + + [Intrinsic] + public static double Acos(double d) + { + return RuntimeImports.acos(d); + } + + [Intrinsic] + public static double Asin(double d) + { + return RuntimeImports.asin(d); + } + + [Intrinsic] + public static double Atan(double d) + { + return RuntimeImports.atan(d); + } + + [Intrinsic] + public static double Atan2(double y, double x) + { + return RuntimeImports.atan2(y, x); + } + + [Intrinsic] + public static double Ceiling(double a) + { + return RuntimeImports.ceil(a); + } + + [Intrinsic] + public static double Cos(double d) + { + return RuntimeImports.cos(d); + } + + [Intrinsic] + public static double Cosh(double value) + { + return RuntimeImports.cosh(value); + } + + [Intrinsic] + public static double Exp(double d) + { + return RuntimeImports.exp(d); + } + + [Intrinsic] + public static double Floor(double d) + { + return RuntimeImports.floor(d); + } + + [Intrinsic] + public static double Log(double d) + { + return RuntimeImports.log(d); + } + + [Intrinsic] + public static double Log10(double d) + { + return RuntimeImports.log10(d); + } + + [Intrinsic] + public static double Pow(double x, double y) + { + return RuntimeImports.pow(x, y); + } + + [Intrinsic] + public static double Sin(double a) + { + return RuntimeImports.sin(a); + } + + [Intrinsic] + public static double Sinh(double value) + { + return RuntimeImports.sinh(value); + } + + [Intrinsic] + public static double Sqrt(double d) + { + return RuntimeImports.sqrt(d); + } + + [Intrinsic] + public static double Tan(double a) + { + return RuntimeImports.tan(a); + } + + [Intrinsic] + public static double Tanh(double value) + { + return RuntimeImports.tanh(value); + } + + [Intrinsic] + private static double FMod(double x, double y) + { + return RuntimeImports.fmod(x, y); + } + + [Intrinsic] + private static unsafe double ModF(double x, double* intptr) + { + return RuntimeImports.modf(x, intptr); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Math.cs b/external/corert/src/System.Private.CoreLib/src/System/Math.cs deleted file mode 100644 index 738f2d46ac..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/Math.cs +++ /dev/null @@ -1,688 +0,0 @@ -// 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. - -/*============================================================ -** -** -** -** Purpose: Some floating-point math operations -** -** -===========================================================*/ - -using System.Runtime; -using System.Runtime.CompilerServices; -using System.Runtime.Versioning; -using System.Diagnostics; -using System.Diagnostics.Contracts; - -namespace System -{ - public static class Math - { - private static double s_doubleRoundLimit = 1e16d; - - private const int maxRoundingDigits = 15; - - // This table is required for the Round function which can specify the number of digits to round to - private static double[] s_roundPower10Double = new double[] { - 1E0, 1E1, 1E2, 1E3, 1E4, 1E5, 1E6, 1E7, 1E8, - 1E9, 1E10, 1E11, 1E12, 1E13, 1E14, 1E15 - }; - - public const double PI = 3.14159265358979323846; - public const double E = 2.7182818284590452354; - - [Intrinsic] - public static double Acos(double d) - { - return RuntimeImports.acos(d); - } - - [Intrinsic] - public static double Asin(double d) - { - return RuntimeImports.asin(d); - } - - [Intrinsic] - public static double Atan(double d) - { - return RuntimeImports.atan(d); - } - - [Intrinsic] - public static double Atan2(double y, double x) - { - return RuntimeImports.atan2(y, x); - } - - public static Decimal Ceiling(Decimal d) - { - return Decimal.Ceiling(d); - } - - [Intrinsic] - public static double Ceiling(double a) - { - return RuntimeImports.ceil(a); - } - - [Intrinsic] - public static double Cos(double d) - { - return RuntimeImports.cos(d); - } - - [Intrinsic] - public static double Cosh(double value) - { - return RuntimeImports.cosh(value); - } - - public static Decimal Floor(Decimal d) - { - return Decimal.Floor(d); - } - - [Intrinsic] - public static double Floor(double d) - { - return RuntimeImports.floor(d); - } - - private static unsafe double InternalRound(double value, int digits, MidpointRounding mode) - { - if (Abs(value) < s_doubleRoundLimit) - { - Double power10 = s_roundPower10Double[digits]; - value *= power10; - if (mode == MidpointRounding.AwayFromZero) - { - double fraction = RuntimeImports.modf(value, &value); - if (Abs(fraction) >= 0.5d) - { - value += Sign(fraction); - } - } - else - { - // On X86 this can be inlined to just a few instructions - value = Round(value); - } - value /= power10; - } - return value; - } - - [Intrinsic] - public static double Sin(double a) - { - return RuntimeImports.sin(a); - } - - [Intrinsic] - public static double Tan(double a) - { - return RuntimeImports.tan(a); - } - - [Intrinsic] - public static double Sinh(double value) - { - return RuntimeImports.sinh(value); - } - - [Intrinsic] - public static double Tanh(double value) - { - return RuntimeImports.tanh(value); - } - - [Intrinsic] - public static double Round(double a) - { - // If the number has no fractional part do nothing - // This shortcut is necessary to workaround precision loss in borderline cases on some platforms - if (a == (double)(Int64)a) - return a; - - double tempVal = a + 0.5; - // We had a number that was equally close to 2 integers. - // We need to return the even one. - double flrTempVal = RuntimeImports.floor(tempVal); - if (flrTempVal == tempVal) - { - if (0.0 != RuntimeImports.fmod(tempVal, 2.0)) - { - flrTempVal -= 1.0; - } - } - - if (flrTempVal == 0 && Double.IsNegative(a)) - { - flrTempVal = Double.NegativeZero; - } - return flrTempVal; - } - - public static double Round(double value, int digits) - { - if ((digits < 0) || (digits > maxRoundingDigits)) - throw new ArgumentOutOfRangeException(nameof(digits), SR.ArgumentOutOfRange_RoundingDigits); - Contract.EndContractBlock(); - return InternalRound(value, digits, MidpointRounding.ToEven); - } - - public static double Round(double value, MidpointRounding mode) - { - return Round(value, 0, mode); - } - - public static double Round(double value, int digits, MidpointRounding mode) - { - if ((digits < 0) || (digits > maxRoundingDigits)) - throw new ArgumentOutOfRangeException(nameof(digits), SR.ArgumentOutOfRange_RoundingDigits); - if (mode < MidpointRounding.ToEven || mode > MidpointRounding.AwayFromZero) - { - throw new ArgumentException(SR.Format(SR.Argument_InvalidEnumValue, mode, "MidpointRounding"), nameof(mode)); - } - Contract.EndContractBlock(); - return InternalRound(value, digits, mode); - } - - public static Decimal Round(Decimal d) - { - return Decimal.Round(d, 0); - } - - public static Decimal Round(Decimal d, int decimals) - { - return Decimal.Round(d, decimals); - } - - public static Decimal Round(Decimal d, MidpointRounding mode) - { - return Decimal.Round(d, 0, mode); - } - - public static Decimal Round(Decimal d, int decimals, MidpointRounding mode) - { - return Decimal.Round(d, decimals, mode); - } - - public static Decimal Truncate(Decimal d) - { - return Decimal.Truncate(d); - } - - public static unsafe double Truncate(double d) - { - double intpart; - RuntimeImports.modf(d, &intpart); - return intpart; - } - - [Intrinsic] - public static double Sqrt(double d) - { - return RuntimeImports.sqrt(d); - } - - [Intrinsic] - public static double Log(double d) - { - return RuntimeImports.log(d); - } - - [Intrinsic] - public static double Log10(double d) - { - return RuntimeImports.log10(d); - } - - [Intrinsic] - public static double Exp(double d) - { - return RuntimeImports.exp(d); - } - - [Intrinsic] - public static double Pow(double x, double y) - { - return RuntimeImports.pow(x, y); - } - - - public static double IEEERemainder(double x, double y) - { - if (Double.IsNaN(x)) - { - return x; // IEEE 754-2008: NaN payload must be preserved - } - if (Double.IsNaN(y)) - { - return y; // IEEE 754-2008: NaN payload must be preserved - } - - double regularMod = x % y; - if (Double.IsNaN(regularMod)) - { - return Double.NaN; - } - if (regularMod == 0) - { - if (Double.IsNegative(x)) - { - return Double.NegativeZero; - } - } - double alternativeResult; - alternativeResult = regularMod - (Math.Abs(y) * Math.Sign(x)); - if (Math.Abs(alternativeResult) == Math.Abs(regularMod)) - { - double divisionResult = x / y; - double roundedResult = Math.Round(divisionResult); - if (Math.Abs(roundedResult) > Math.Abs(divisionResult)) - { - return alternativeResult; - } - else - { - return regularMod; - } - } - if (Math.Abs(alternativeResult) < Math.Abs(regularMod)) - { - return alternativeResult; - } - else - { - return regularMod; - } - } - - /*================================Abs========================================= - **Returns the absolute value of it's argument. - ============================================================================*/ - - [CLSCompliant(false)] - public static sbyte Abs(sbyte value) - { - if (value >= 0) - return value; - else - return AbsHelper(value); - } - - private static sbyte AbsHelper(sbyte value) - { - Debug.Assert(value < 0, "AbsHelper should only be called for negative values!"); - if (value == SByte.MinValue) - throw new OverflowException(SR.Overflow_NegateTwosCompNum); - Contract.EndContractBlock(); - return ((sbyte)(-value)); - } - - public static short Abs(short value) - { - if (value >= 0) - return value; - else - return AbsHelper(value); - } - - private static short AbsHelper(short value) - { - Debug.Assert(value < 0, "AbsHelper should only be called for negative values!"); - if (value == Int16.MinValue) - throw new OverflowException(SR.Overflow_NegateTwosCompNum); - Contract.EndContractBlock(); - return (short)-value; - } - - public static int Abs(int value) - { - if (value >= 0) - return value; - else - return AbsHelper(value); - } - - private static int AbsHelper(int value) - { - Debug.Assert(value < 0, "AbsHelper should only be called for negative values!"); - if (value == Int32.MinValue) - throw new OverflowException(SR.Overflow_NegateTwosCompNum); - Contract.EndContractBlock(); - return -value; - } - - public static long Abs(long value) - { - if (value >= 0) - return value; - else - return AbsHelper(value); - } - - private static long AbsHelper(long value) - { - Debug.Assert(value < 0, "AbsHelper should only be called for negative values!"); - if (value == Int64.MinValue) - throw new OverflowException(SR.Overflow_NegateTwosCompNum); - Contract.EndContractBlock(); - return -value; - } - - [Intrinsic] - public static float Abs(float value) - { - return (float)RuntimeImports.fabs(value); - } - - [Intrinsic] - public static double Abs(double value) - { - return RuntimeImports.fabs(value); - } - - public static Decimal Abs(Decimal value) - { - return Decimal.Abs(value); - } - - /*================================MAX========================================= - **Returns the larger of val1 and val2 - ============================================================================*/ - [CLSCompliant(false)] - [NonVersionable] - public static sbyte Max(sbyte val1, sbyte val2) - { - return (val1 >= val2) ? val1 : val2; - } - - [NonVersionable] - public static byte Max(byte val1, byte val2) - { - return (val1 >= val2) ? val1 : val2; - } - - [NonVersionable] - public static short Max(short val1, short val2) - { - return (val1 >= val2) ? val1 : val2; - } - - [CLSCompliant(false)] - [NonVersionable] - public static ushort Max(ushort val1, ushort val2) - { - return (val1 >= val2) ? val1 : val2; - } - - [NonVersionable] - public static int Max(int val1, int val2) - { - return (val1 >= val2) ? val1 : val2; - } - - [CLSCompliant(false)] - [NonVersionable] - public static uint Max(uint val1, uint val2) - { - return (val1 >= val2) ? val1 : val2; - } - - [NonVersionable] - public static long Max(long val1, long val2) - { - return (val1 >= val2) ? val1 : val2; - } - - [CLSCompliant(false)] - [NonVersionable] - public static ulong Max(ulong val1, ulong val2) - { - return (val1 >= val2) ? val1 : val2; - } - - public static float Max(float val1, float val2) - { - if (val1 > val2) - return val1; - - if (Single.IsNaN(val1)) - return val1; - - return val2; - } - - public static double Max(double val1, double val2) - { - if (val1 > val2) - return val1; - - if (Double.IsNaN(val1)) - return val1; - - return val2; - } - - public static Decimal Max(Decimal val1, Decimal val2) - { - return Decimal.Max(val1, val2); - } - - /*================================MIN========================================= - **Returns the smaller of val1 and val2. - ============================================================================*/ - [CLSCompliant(false)] - [NonVersionable] - public static sbyte Min(sbyte val1, sbyte val2) - { - return (val1 <= val2) ? val1 : val2; - } - - [NonVersionable] - public static byte Min(byte val1, byte val2) - { - return (val1 <= val2) ? val1 : val2; - } - - [NonVersionable] - public static short Min(short val1, short val2) - { - return (val1 <= val2) ? val1 : val2; - } - - [CLSCompliant(false)] - [NonVersionable] - public static ushort Min(ushort val1, ushort val2) - { - return (val1 <= val2) ? val1 : val2; - } - - [NonVersionable] - public static int Min(int val1, int val2) - { - return (val1 <= val2) ? val1 : val2; - } - - [CLSCompliant(false)] - [NonVersionable] - public static uint Min(uint val1, uint val2) - { - return (val1 <= val2) ? val1 : val2; - } - - [NonVersionable] - public static long Min(long val1, long val2) - { - return (val1 <= val2) ? val1 : val2; - } - - [CLSCompliant(false)] - [NonVersionable] - public static ulong Min(ulong val1, ulong val2) - { - return (val1 <= val2) ? val1 : val2; - } - - public static float Min(float val1, float val2) - { - if (val1 < val2) - return val1; - - if (Single.IsNaN(val1)) - return val1; - - return val2; - } - - public static double Min(double val1, double val2) - { - if (val1 < val2) - return val1; - - if (Double.IsNaN(val1)) - return val1; - - return val2; - } - - public static Decimal Min(Decimal val1, Decimal val2) - { - return Decimal.Min(val1, val2); - } - - /*=====================================Log====================================== - ** - ==============================================================================*/ - public static double Log(double a, double newBase) - { - if (Double.IsNaN(a)) - { - return a; // IEEE 754-2008: NaN payload must be preserved - } - if (Double.IsNaN(newBase)) - { - return newBase; // IEEE 754-2008: NaN payload must be preserved - } - - if (newBase == 1) - return Double.NaN; - if (a != 1 && (newBase == 0 || Double.IsPositiveInfinity(newBase))) - return Double.NaN; - - return (Log(a) / Log(newBase)); - } - - - // Sign function for VB. Returns -1, 0, or 1 if the sign of the number - // is negative, 0, or positive. Throws for floating point NaN's. - [CLSCompliant(false)] - public static int Sign(sbyte value) - { - if (value < 0) - return -1; - else if (value > 0) - return 1; - else - return 0; - } - - - // Sign function for VB. Returns -1, 0, or 1 if the sign of the number - // is negative, 0, or positive. Throws for floating point NaN's. - public static int Sign(short value) - { - if (value < 0) - return -1; - else if (value > 0) - return 1; - else - return 0; - } - - // Sign function for VB. Returns -1, 0, or 1 if the sign of the number - // is negative, 0, or positive. Throws for floating point NaN's. - public static int Sign(int value) - { - if (value < 0) - return -1; - else if (value > 0) - return 1; - else - return 0; - } - - public static int Sign(long value) - { - if (value < 0) - return -1; - else if (value > 0) - return 1; - else - return 0; - } - - public static int Sign(float value) - { - if (value < 0) - return -1; - else if (value > 0) - return 1; - else if (value == 0) - return 0; - throw new ArithmeticException(SR.Arithmetic_NaN); - } - - public static int Sign(double value) - { - if (value < 0) - return -1; - else if (value > 0) - return 1; - else if (value == 0) - return 0; - throw new ArithmeticException(SR.Arithmetic_NaN); - } - - public static int Sign(Decimal value) - { - if (value < 0) - return -1; - else if (value > 0) - return 1; - else - return 0; - } - - public static long BigMul(int a, int b) - { - return ((long)a) * b; - } - - public static int DivRem(int a, int b, out int result) - { - // TODO https://github.com/dotnet/coreclr/issues/3439: - // Restore to using % and / when the JIT is able to eliminate one of the idivs. - // In the meantime, a * and - is measurably faster than an extra /. - int div = a / b; - result = a - (div * b); - return div; - } - - public static long DivRem(long a, long b, out long result) - { - // TODO https://github.com/dotnet/coreclr/issues/3439: - // Restore to using % and / when the JIT is able to eliminate one of the idivs. - // In the meantime, a * and - is measurably faster than an extra /. - long div = a / b; - result = a - (div * b); - return div; - } - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/System/MathF.CoreRT.cs b/external/corert/src/System.Private.CoreLib/src/System/MathF.CoreRT.cs new file mode 100644 index 0000000000..b67b8bbd40 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/MathF.CoreRT.cs @@ -0,0 +1,139 @@ +// 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. + +/*============================================================ +** +** Purpose: Some single-precision floating-point math operations +** +===========================================================*/ + +//This class contains only static members and doesn't require serialization. + +using System.Runtime; +using System.Runtime.CompilerServices; + +namespace System +{ + public static partial class MathF + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Acos(float x) + { + return (float)Math.Acos(x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Asin(float x) + { + return (float)Math.Asin(x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Atan(float x) + { + return (float)Math.Atan(x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Atan2(float y, float x) + { + return (float)Math.Atan2(y, x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Ceiling(float x) + { + return (float)Math.Ceiling(x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Cos(float x) + { + return (float)Math.Cos(x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Cosh(float x) + { + return (float)Math.Cosh(x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Exp(float x) + { + return (float)Math.Exp(x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Floor(float x) + { + return (float)Math.Floor(x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Log(float x) + { + return (float)Math.Log(x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Log10(float x) + { + return (float)Math.Log10(x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Pow(float x, float y) + { + return (float)Math.Pow(x, y); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Sin(float x) + { + return (float)Math.Sin(x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Sinh(float x) + { + return (float)Math.Sinh(x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Sqrt(float x) + { + return (float)Math.Sqrt(x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Tan(float x) + { + return (float)Math.Tan(x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Tanh(float x) + { + return (float)Math.Tanh(x); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float FMod(float x, float y) + { + return (float)RuntimeImports.fmod(x, y); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe float ModF(float x, float* intptr) + { + //todo : https://github.com/dotnet/corert/issues/3167 + double d = x; + double r = RuntimeImports.modf(d, &d); + + *intptr = (float)d; + return (float)r; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/MathF.cs b/external/corert/src/System.Private.CoreLib/src/System/MathF.cs deleted file mode 100644 index 456c001374..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/MathF.cs +++ /dev/null @@ -1,258 +0,0 @@ -// 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.Runtime; -using System.Runtime.CompilerServices; - -namespace System -{ - public static class MathF - { - public const float PI = (float)Math.PI; - - public const float E = 2.71828183f; - - private static float singleRoundLimit = 1e8f; - - private const int maxRoundingDigits = 6; - - // This table is required for the Round function which can specify the number of digits to round to - private static float[] roundPower10Single = new float[] { - 1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f, 1e6f - }; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Abs(float x) - { - return Math.Abs(x); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Acos(float x) - { - return (float)Math.Acos(x); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Asin(float x) - { - return (float)Math.Asin(x); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Atan(float x) - { - return (float)Math.Atan(x); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Atan2(float y, float x) - { - return (float)Math.Atan2(y, x); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Cos(float x) - { - return (float)Math.Cos(x); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Ceiling(float x) - { - return (float)Math.Ceiling(x); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Cosh(float x) { return (float)Math.Cosh(x); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Exp(float x) { return (float)Math.Exp(x); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Floor(float x) { return (float)Math.Floor(x); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Log(float x) - { - return (float)Math.Log(x); - } - public static float Log(float x, float y) - { - if (float.IsNaN(x)) - { - return x; // IEEE 754-2008: NaN payload must be preserved - } - - if (float.IsNaN(y)) - { - return y; // IEEE 754-2008: NaN payload must be preserved - } - - if (y == 1) - { - return float.NaN; - } - - if ((x != 1) && ((y == 0) || float.IsPositiveInfinity(y))) - { - return float.NaN; - } - - return Log(x) / Log(y); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Log10(float x) - { - return (float)Math.Log10(x); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Max(float x, float y) - { - return Math.Max(x, y); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Min(float x, float y) - { - return Math.Min(x, y); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Pow(float x, float y) - { - return (float)Math.Pow(x, y); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Round(float x) - { - return (float)Math.Round(x); - } - - public static float Round(float x, int digits) - { - if ((digits < 0) || (digits > maxRoundingDigits)) - { - throw new ArgumentOutOfRangeException(nameof(digits), SR.ArgumentOutOfRange_RoundingDigits); - } - - return InternalRound(x, digits, MidpointRounding.ToEven); - } - - public static float Round(float x, int digits, MidpointRounding mode) - { - if ((digits < 0) || (digits > maxRoundingDigits)) - { - throw new ArgumentOutOfRangeException(nameof(digits), SR.ArgumentOutOfRange_RoundingDigits); - } - - if (mode < MidpointRounding.ToEven || mode > MidpointRounding.AwayFromZero) - { - throw new ArgumentException(SR.Format(SR.Argument_InvalidEnum, mode, nameof(MidpointRounding)), nameof(mode)); - } - - return InternalRound(x, digits, mode); - } - - public static float Round(float x, MidpointRounding mode) - { - if (mode < MidpointRounding.ToEven || mode > MidpointRounding.AwayFromZero) - { - throw new ArgumentException(SR.Format(SR.Argument_InvalidEnum, mode, nameof(MidpointRounding)), nameof(mode)); - } - - return InternalRound(x, 0, mode); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float IEEERemainder(float x, float y) - { - return (float)Math.IEEERemainder(x, y); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Sin(float x) - { - return (float)Math.Sin(x); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Sqrt(float x) - { - return (float)Math.Sqrt(x); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Tan(float x) - { - return (float)Math.Tan(x); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Sign(float x) - { - return Math.Sign(x); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Sinh(float x) - { - return (float)Math.Sinh(x); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Tanh(float x) - { - return (float)Math.Tanh(x); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Truncate(float x) => InternalTruncate(x); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe float SplitFractionSingle(float* x) - { - //todo : https://github.com/dotnet/corert/issues/3167 - return (float)RuntimeImports.modf(*x, (double*)x); - } - - private unsafe static float InternalTruncate(float x) - { - SplitFractionSingle(&x); - return x; - } - - private static unsafe float InternalRound(float x, int digits, MidpointRounding mode) - { - if (Abs(x) < singleRoundLimit) - { - var power10 = roundPower10Single[digits]; - - x *= power10; - - if (mode == MidpointRounding.AwayFromZero) - { - var fraction = SplitFractionSingle(&x); - - if (Abs(fraction) >= 0.5f) - { - x += Sign(fraction); - } - } - else - { - x = Round(x); - } - - x /= power10; - } - - return x; - } - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/System/MissingFieldException.cs b/external/corert/src/System.Private.CoreLib/src/System/MissingFieldException.cs index 3f9691a077..95d517ccad 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/MissingFieldException.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/MissingFieldException.cs @@ -14,24 +14,25 @@ using System.Runtime.Serialization; namespace System { [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class MissingFieldException : MissingMemberException { public MissingFieldException() : base(SR.Arg_MissingFieldException) { - HResult = __HResults.COR_E_MISSINGFIELD; + HResult = HResults.COR_E_MISSINGFIELD; } public MissingFieldException(String message) : base(message) { - HResult = __HResults.COR_E_MISSINGFIELD; + HResult = HResults.COR_E_MISSINGFIELD; } public MissingFieldException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_MISSINGFIELD; + HResult = HResults.COR_E_MISSINGFIELD; } public MissingFieldException(string className, string methodName) diff --git a/external/corert/src/System.Private.CoreLib/src/System/MissingMemberException.cs b/external/corert/src/System.Private.CoreLib/src/System/MissingMemberException.cs index 97e1ac8494..3d2fedc5de 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/MissingMemberException.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/MissingMemberException.cs @@ -11,30 +11,30 @@ ** =============================================================================*/ -using System.Diagnostics.Contracts; using System.Runtime.Serialization; namespace System { [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class MissingMemberException : MemberAccessException { public MissingMemberException() : base(SR.Arg_MissingMemberException) { - HResult = __HResults.COR_E_MISSINGMEMBER; + HResult = HResults.COR_E_MISSINGMEMBER; } public MissingMemberException(String message) : base(message) { - HResult = __HResults.COR_E_MISSINGMEMBER; + HResult = HResults.COR_E_MISSINGMEMBER; } public MissingMemberException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_MISSINGMEMBER; + HResult = HResults.COR_E_MISSINGMEMBER; } public MissingMemberException(string className, string memberName) @@ -46,16 +46,16 @@ namespace System protected MissingMemberException(SerializationInfo info, StreamingContext context) : base(info, context) { - ClassName = (String)info.GetString("MMClassName"); - MemberName = (String)info.GetString("MMMemberName"); + ClassName = info.GetString("MMClassName"); + MemberName = info.GetString("MMMemberName"); Signature = (byte[])info.GetValue("MMSignature", typeof(byte[])); } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); - info.AddValue("MMClassName", ClassName, typeof(String)); - info.AddValue("MMMemberName", MemberName, typeof(String)); + info.AddValue("MMClassName", ClassName, typeof(string)); + info.AddValue("MMMemberName", MemberName, typeof(string)); info.AddValue("MMSignature", Signature, typeof(byte[])); } diff --git a/external/corert/src/System.Private.CoreLib/src/System/MulticastDelegate.cs b/external/corert/src/System.Private.CoreLib/src/System/MulticastDelegate.cs index 6c1fa919ba..7368f2a156 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/MulticastDelegate.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/MulticastDelegate.cs @@ -153,33 +153,7 @@ namespace System public override void GetObjectData(SerializationInfo info, StreamingContext context) { - Delegate[] invocationList = m_helperObject as Delegate[]; - if (invocationList == null) - { - if (Method == null) - throw new SerializationException(SR.DelegateSer_InsufficientMetadata); - - DelegateSerializationHolder.GetDelegateSerializationInfo(info, this.GetType(), Target, Method, 0); - } - else - { - int targetIndex = 0; - DelegateSerializationHolder.DelegateEntry previousEntry = null; - int invocationCount = (int)m_extraFunctionPointerOrData; - for (int i = invocationCount; --i >= 0;) - { - MulticastDelegate d = (MulticastDelegate)invocationList[i]; - - if (d.Method == null) - throw new SerializationException(SR.DelegateSer_InsufficientMetadata); - - DelegateSerializationHolder.DelegateEntry de = DelegateSerializationHolder.GetDelegateSerializationInfo(info, d.GetType(), d.Target, d.Method, targetIndex++); - if (previousEntry != null) - previousEntry.NextEntry = de; - - previousEntry = de; - } - } + throw new PlatformNotSupportedException(SR.Serialization_DelegatesNotSupported); } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Number.CoreRT.cs b/external/corert/src/System.Private.CoreLib/src/System/Number.CoreRT.cs new file mode 100644 index 0000000000..1c7fdadce1 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Number.CoreRT.cs @@ -0,0 +1,735 @@ +// 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.Globalization; +using System.Text; + +namespace System +{ + // The Number class implements methods for formatting and parsing + // numeric values. To format and parse numeric values, applications should + // use the Format and Parse methods provided by the numeric + // classes (Byte, Int16, Int32, Int64, + // Single, Double, Currency, and Decimal). Those + // Format and Parse methods share a common implementation + // provided by this class, and are thus documented in detail here. + // + // Formatting + // + // The Format methods provided by the numeric classes are all of the + // form + // + // public static String Format(XXX value, String format); + // public static String Format(XXX value, String format, NumberFormatInfo info); + // + // where XXX is the name of the particular numeric class. The methods convert + // the numeric value to a string using the format string given by the + // format parameter. If the format parameter is null or + // an empty string, the number is formatted as if the string "G" (general + // format) was specified. The info parameter specifies the + // NumberFormatInfo instance to use when formatting the number. If the + // info parameter is null or omitted, the numeric formatting information + // is obtained from the current culture. The NumberFormatInfo supplies + // such information as the characters to use for decimal and thousand + // separators, and the spelling and placement of currency symbols in monetary + // values. + // + // Format strings fall into two categories: Standard format strings and + // user-defined format strings. A format string consisting of a single + // alphabetic character (A-Z or a-z), optionally followed by a sequence of + // digits (0-9), is a standard format string. All other format strings are + // used-defined format strings. + // + // A standard format string takes the form Axx, where A is an + // alphabetic character called the format specifier and xx is a + // sequence of digits called the precision specifier. The format + // specifier controls the type of formatting applied to the number and the + // precision specifier controls the number of significant digits or decimal + // places of the formatting operation. The following table describes the + // supported standard formats. + // + // C c - Currency format. The number is + // converted to a string that represents a currency amount. The conversion is + // controlled by the currency format information of the NumberFormatInfo + // used to format the number. The precision specifier indicates the desired + // number of decimal places. If the precision specifier is omitted, the default + // currency precision given by the NumberFormatInfo is used. + // + // D d - Decimal format. This format is + // supported for integral types only. The number is converted to a string of + // decimal digits, prefixed by a minus sign if the number is negative. The + // precision specifier indicates the minimum number of digits desired in the + // resulting string. If required, the number will be left-padded with zeros to + // produce the number of digits given by the precision specifier. + // + // E e Engineering (scientific) format. + // The number is converted to a string of the form + // "-d.ddd...E+ddd" or "-d.ddd...e+ddd", where each + // 'd' indicates a digit (0-9). The string starts with a minus sign if the + // number is negative, and one digit always precedes the decimal point. The + // precision specifier indicates the desired number of digits after the decimal + // point. If the precision specifier is omitted, a default of 6 digits after + // the decimal point is used. The format specifier indicates whether to prefix + // the exponent with an 'E' or an 'e'. The exponent is always consists of a + // plus or minus sign and three digits. + // + // F f Fixed point format. The number is + // converted to a string of the form "-ddd.ddd....", where each + // 'd' indicates a digit (0-9). The string starts with a minus sign if the + // number is negative. The precision specifier indicates the desired number of + // decimal places. If the precision specifier is omitted, the default numeric + // precision given by the NumberFormatInfo is used. + // + // G g - General format. The number is + // converted to the shortest possible decimal representation using fixed point + // or scientific format. The precision specifier determines the number of + // significant digits in the resulting string. If the precision specifier is + // omitted, the number of significant digits is determined by the type of the + // number being converted (10 for int, 19 for long, 7 for + // float, 15 for double, 19 for Currency, and 29 for + // Decimal). Trailing zeros after the decimal point are removed, and the + // resulting string contains a decimal point only if required. The resulting + // string uses fixed point format if the exponent of the number is less than + // the number of significant digits and greater than or equal to -4. Otherwise, + // the resulting string uses scientific format, and the case of the format + // specifier controls whether the exponent is prefixed with an 'E' or an + // 'e'. + // + // N n Number format. The number is + // converted to a string of the form "-d,ddd,ddd.ddd....", where + // each 'd' indicates a digit (0-9). The string starts with a minus sign if the + // number is negative. Thousand separators are inserted between each group of + // three digits to the left of the decimal point. The precision specifier + // indicates the desired number of decimal places. If the precision specifier + // is omitted, the default numeric precision given by the + // NumberFormatInfo is used. + // + // X x - Hexadecimal format. This format is + // supported for integral types only. The number is converted to a string of + // hexadecimal digits. The format specifier indicates whether to use upper or + // lower case characters for the hexadecimal digits above 9 ('X' for 'ABCDEF', + // and 'x' for 'abcdef'). The precision specifier indicates the minimum number + // of digits desired in the resulting string. If required, the number will be + // left-padded with zeros to produce the number of digits given by the + // precision specifier. + // + // Some examples of standard format strings and their results are shown in the + // table below. (The examples all assume a default NumberFormatInfo.) + // + // Value Format Result + // 12345.6789 C $12,345.68 + // -12345.6789 C ($12,345.68) + // 12345 D 12345 + // 12345 D8 00012345 + // 12345.6789 E 1.234568E+004 + // 12345.6789 E10 1.2345678900E+004 + // 12345.6789 e4 1.2346e+004 + // 12345.6789 F 12345.68 + // 12345.6789 F0 12346 + // 12345.6789 F6 12345.678900 + // 12345.6789 G 12345.6789 + // 12345.6789 G7 12345.68 + // 123456789 G7 1.234568E8 + // 12345.6789 N 12,345.68 + // 123456789 N4 123,456,789.0000 + // 0x2c45e x 2c45e + // 0x2c45e X 2C45E + // 0x2c45e X8 0002C45E + // + // Format strings that do not start with an alphabetic character, or that start + // with an alphabetic character followed by a non-digit, are called + // user-defined format strings. The following table describes the formatting + // characters that are supported in user defined format strings. + // + // + // 0 - Digit placeholder. If the value being + // formatted has a digit in the position where the '0' appears in the format + // string, then that digit is copied to the output string. Otherwise, a '0' is + // stored in that position in the output string. The position of the leftmost + // '0' before the decimal point and the rightmost '0' after the decimal point + // determines the range of digits that are always present in the output + // string. + // + // # - Digit placeholder. If the value being + // formatted has a digit in the position where the '#' appears in the format + // string, then that digit is copied to the output string. Otherwise, nothing + // is stored in that position in the output string. + // + // . - Decimal point. The first '.' character + // in the format string determines the location of the decimal separator in the + // formatted value; any additional '.' characters are ignored. The actual + // character used as a the decimal separator in the output string is given by + // the NumberFormatInfo used to format the number. + // + // , - Thousand separator and number scaling. + // The ',' character serves two purposes. First, if the format string contains + // a ',' character between two digit placeholders (0 or #) and to the left of + // the decimal point if one is present, then the output will have thousand + // separators inserted between each group of three digits to the left of the + // decimal separator. The actual character used as a the decimal separator in + // the output string is given by the NumberFormatInfo used to format the + // number. Second, if the format string contains one or more ',' characters + // immediately to the left of the decimal point, or after the last digit + // placeholder if there is no decimal point, then the number will be divided by + // 1000 times the number of ',' characters before it is formatted. For example, + // the format string '0,,' will represent 100 million as just 100. Use of the + // ',' character to indicate scaling does not also cause the formatted number + // to have thousand separators. Thus, to scale a number by 1 million and insert + // thousand separators you would use the format string '#,##0,,'. + // + // % - Percentage placeholder. The presence of + // a '%' character in the format string causes the number to be multiplied by + // 100 before it is formatted. The '%' character itself is inserted in the + // output string where it appears in the format string. + // + // E+ E- e+ e- - Scientific notation. + // If any of the strings 'E+', 'E-', 'e+', or 'e-' are present in the format + // string and are immediately followed by at least one '0' character, then the + // number is formatted using scientific notation with an 'E' or 'e' inserted + // between the number and the exponent. The number of '0' characters following + // the scientific notation indicator determines the minimum number of digits to + // output for the exponent. The 'E+' and 'e+' formats indicate that a sign + // character (plus or minus) should always precede the exponent. The 'E-' and + // 'e-' formats indicate that a sign character should only precede negative + // exponents. + // + // \ - Literal character. A backslash character + // causes the next character in the format string to be copied to the output + // string as-is. The backslash itself isn't copied, so to place a backslash + // character in the output string, use two backslashes (\\) in the format + // string. + // + // 'ABC' "ABC" - Literal string. Characters + // enclosed in single or double quotation marks are copied to the output string + // as-is and do not affect formatting. + // + // ; - Section separator. The ';' character is + // used to separate sections for positive, negative, and zero numbers in the + // format string. + // + // Other - All other characters are copied to + // the output string in the position they appear. + // + // For fixed point formats (formats not containing an 'E+', 'E-', 'e+', or + // 'e-'), the number is rounded to as many decimal places as there are digit + // placeholders to the right of the decimal point. If the format string does + // not contain a decimal point, the number is rounded to the nearest + // integer. If the number has more digits than there are digit placeholders to + // the left of the decimal point, the extra digits are copied to the output + // string immediately before the first digit placeholder. + // + // For scientific formats, the number is rounded to as many significant digits + // as there are digit placeholders in the format string. + // + // To allow for different formatting of positive, negative, and zero values, a + // user-defined format string may contain up to three sections separated by + // semicolons. The results of having one, two, or three sections in the format + // string are described in the table below. + // + // Sections: + // + // One - The format string applies to all values. + // + // Two - The first section applies to positive values + // and zeros, and the second section applies to negative values. If the number + // to be formatted is negative, but becomes zero after rounding according to + // the format in the second section, then the resulting zero is formatted + // according to the first section. + // + // Three - The first section applies to positive + // values, the second section applies to negative values, and the third section + // applies to zeros. The second section may be left empty (by having no + // characters between the semicolons), in which case the first section applies + // to all non-zero values. If the number to be formatted is non-zero, but + // becomes zero after rounding according to the format in the first or second + // section, then the resulting zero is formatted according to the third + // section. + // + // For both standard and user-defined formatting operations on values of type + // float and double, if the value being formatted is a NaN (Not + // a Number) or a positive or negative infinity, then regardless of the format + // string, the resulting string is given by the NaNSymbol, + // PositiveInfinitySymbol, or NegativeInfinitySymbol property of + // the NumberFormatInfo used to format the number. + // + // Parsing + // + // The Parse methods provided by the numeric classes are all of the form + // + // public static XXX Parse(String s); + // public static XXX Parse(String s, int style); + // public static XXX Parse(String s, int style, NumberFormatInfo info); + // + // where XXX is the name of the particular numeric class. The methods convert a + // string to a numeric value. The optional style parameter specifies the + // permitted style of the numeric string. It must be a combination of bit flags + // from the NumberStyles enumeration. The optional info parameter + // specifies the NumberFormatInfo instance to use when parsing the + // string. If the info parameter is null or omitted, the numeric + // formatting information is obtained from the current culture. + // + // Numeric strings produced by the Format methods using the Currency, + // Decimal, Engineering, Fixed point, General, or Number standard formats + // (the C, D, E, F, G, and N format specifiers) are guaranteed to be parseable + // by the Parse methods if the NumberStyles.Any style is + // specified. Note, however, that the Parse methods do not accept + // NaNs or Infinities. + // + // This class contains only static members and does not need to be serializable + + + internal static partial class Number + { + private const int _CVTBUFSIZE = 349; + + public static bool IsPositiveInfinity(string s, IFormatProvider provider) + { + NumberFormatInfo nfi = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); + return s.Equals(nfi.PositiveInfinitySymbol); + } + + public static bool IsNegativeInfinity(string s, IFormatProvider provider) + { + NumberFormatInfo nfi = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); + return s.Equals(nfi.NegativeInfinitySymbol); + } + + public static bool IsNaNSymbol(string s, IFormatProvider provider) + { + NumberFormatInfo nfi = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider); + return s.Equals(nfi.NaNSymbol); + } + + #region Decimal Number Formatting Helpers + private static unsafe bool NumberBufferToDecimal(ref Number.NumberBuffer number, ref Decimal value) + { + Decimal d = new Decimal(); + + char* p = number.digits; + int e = number.scale; + if (*p == 0) + { + // To avoid risking an app-compat issue with pre 4.5 (where some app was illegally using Reflection to examine the internal scale bits), we'll only force + // the scale to 0 if the scale was previously positive (previously, such cases were unparsable to a bug.) + if (e > 0) + { + e = 0; + } + } + else + { + if (e > DecimalPrecision) + return false; + + while (((e > 0) || ((*p != 0) && (e > -28))) && + ((d.High < 0x19999999) || ((d.High == 0x19999999) && + ((d.Mid < 0x99999999) || ((d.Mid == 0x99999999) && + ((d.Low < 0x99999999) || ((d.Low == 0x99999999) && + (*p <= '5')))))))) + { + Decimal.DecMul10(ref d); + if (*p != 0) + Decimal.DecAddInt32(ref d, (uint)(*p++ - '0')); + e--; + } + + if (*p++ >= '5') + { + bool round = true; + if ((*(p - 1) == '5') && ((*(p - 2) % 2) == 0)) + { + // Check if previous digit is even, only if the when we are unsure whether hows to do + // Banker's rounding. For digits > 5 we will be roundinp up anyway. + int count = 20; // Look at the next 20 digits to check to round + while ((*p == '0') && (count != 0)) + { + p++; + count--; + } + if ((*p == '\0') || (count == 0)) + round = false;// Do nothing + } + + if (round) + { + Decimal.DecAddInt32(ref d, 1); + if ((d.High | d.Mid | d.Low) == 0) + { + d.High = 0x19999999; + d.Mid = 0x99999999; + d.Low = 0x9999999A; + e++; + } + } + } + } + + if (e > 0) + return false; + + if (e <= -DecimalPrecision) + { + // Parsing a large scale zero can give you more precision than fits in the decimal. + // This should only happen for actual zeros or very small numbers that round to zero. + d.High = 0; + d.Low = 0; + d.Mid = 0; + d.Scale = DecimalPrecision - 1; + } + else + { + d.Scale = -e; + } + d.IsNegative = number.sign; + + value = d; + return true; + } + + #endregion + + /*=========================================================== + Portable NumberToDouble implementation + -------------------------------------- + + - does the conversion with the best possible precision. + - does not use any float arithmetic so it is not sensitive + to differences in precision of floating point calculations + across platforms. + + The internal integer representation of the float number is + UINT64 mantissa + INT exponent. The mantissa is kept normalized + ie with the most significant one being 63-th bit of UINT64. + ===========================================================*/ + + // + // get 32-bit integer from at most 9 digits + // + private static unsafe uint DigitsToInt(char* p, int count) + { + char* end = p + count; + uint res = (uint)*p - '0'; + for (p = p + 1; p < end; p++) + res = 10 * res + (uint)*p - '0'; + return res; + } + + // + // helper to multiply two 32-bit uints + // + private static ulong Mul32x32To64(uint a, uint b) + { + return (ulong)a * (ulong)b; + } + + // + // multiply two numbers in the internal integer representation + // + private static ulong Mul64Lossy(ulong a, ulong b, ref int pexp) + { + // it's ok to lose some precision here - Mul64 will be called + // at most twice during the conversion, so the error won't propagate + // to any of the 53 significant bits of the result + ulong val = Mul32x32To64((uint)(a >> 32), (uint)(b >> 32)) + + (Mul32x32To64((uint)(a >> 32), (uint)(b)) >> 32) + + (Mul32x32To64((uint)(a), (uint)(b >> 32)) >> 32); + + // normalize + if ((val & 0x8000000000000000) == 0) + { + val <<= 1; + pexp -= 1; + } + + return val; + } + + // + // precomputed tables with powers of 10. These allows us to do at most + // two Mul64 during the conversion. This is important not only + // for speed, but also for precision because of Mul64 computes with 1 bit error. + // + + private static readonly ulong[] s_rgval64Power10 = + { + // powers of 10 + /*1*/ 0xa000000000000000, + /*2*/ 0xc800000000000000, + /*3*/ 0xfa00000000000000, + /*4*/ 0x9c40000000000000, + /*5*/ 0xc350000000000000, + /*6*/ 0xf424000000000000, + /*7*/ 0x9896800000000000, + /*8*/ 0xbebc200000000000, + /*9*/ 0xee6b280000000000, + /*10*/ 0x9502f90000000000, + /*11*/ 0xba43b74000000000, + /*12*/ 0xe8d4a51000000000, + /*13*/ 0x9184e72a00000000, + /*14*/ 0xb5e620f480000000, + /*15*/ 0xe35fa931a0000000, + + // powers of 0.1 + /*1*/ 0xcccccccccccccccd, + /*2*/ 0xa3d70a3d70a3d70b, + /*3*/ 0x83126e978d4fdf3c, + /*4*/ 0xd1b71758e219652e, + /*5*/ 0xa7c5ac471b478425, + /*6*/ 0x8637bd05af6c69b7, + /*7*/ 0xd6bf94d5e57a42be, + /*8*/ 0xabcc77118461ceff, + /*9*/ 0x89705f4136b4a599, + /*10*/ 0xdbe6fecebdedd5c2, + /*11*/ 0xafebff0bcb24ab02, + /*12*/ 0x8cbccc096f5088cf, + /*13*/ 0xe12e13424bb40e18, + /*14*/ 0xb424dc35095cd813, + /*15*/ 0x901d7cf73ab0acdc, + }; + + private static readonly sbyte[] s_rgexp64Power10 = + { + // exponents for both powers of 10 and 0.1 + /*1*/ 4, + /*2*/ 7, + /*3*/ 10, + /*4*/ 14, + /*5*/ 17, + /*6*/ 20, + /*7*/ 24, + /*8*/ 27, + /*9*/ 30, + /*10*/ 34, + /*11*/ 37, + /*12*/ 40, + /*13*/ 44, + /*14*/ 47, + /*15*/ 50, + }; + + private static readonly ulong[] s_rgval64Power10By16 = + { + // powers of 10^16 + /*1*/ 0x8e1bc9bf04000000, + /*2*/ 0x9dc5ada82b70b59e, + /*3*/ 0xaf298d050e4395d6, + /*4*/ 0xc2781f49ffcfa6d4, + /*5*/ 0xd7e77a8f87daf7fa, + /*6*/ 0xefb3ab16c59b14a0, + /*7*/ 0x850fadc09923329c, + /*8*/ 0x93ba47c980e98cde, + /*9*/ 0xa402b9c5a8d3a6e6, + /*10*/ 0xb616a12b7fe617a8, + /*11*/ 0xca28a291859bbf90, + /*12*/ 0xe070f78d39275566, + /*13*/ 0xf92e0c3537826140, + /*14*/ 0x8a5296ffe33cc92c, + /*15*/ 0x9991a6f3d6bf1762, + /*16*/ 0xaa7eebfb9df9de8a, + /*17*/ 0xbd49d14aa79dbc7e, + /*18*/ 0xd226fc195c6a2f88, + /*19*/ 0xe950df20247c83f8, + /*20*/ 0x81842f29f2cce373, + /*21*/ 0x8fcac257558ee4e2, + + // powers of 0.1^16 + /*1*/ 0xe69594bec44de160, + /*2*/ 0xcfb11ead453994c3, + /*3*/ 0xbb127c53b17ec165, + /*4*/ 0xa87fea27a539e9b3, + /*5*/ 0x97c560ba6b0919b5, + /*6*/ 0x88b402f7fd7553ab, + /*7*/ 0xf64335bcf065d3a0, + /*8*/ 0xddd0467c64bce4c4, + /*9*/ 0xc7caba6e7c5382ed, + /*10*/ 0xb3f4e093db73a0b7, + /*11*/ 0xa21727db38cb0053, + /*12*/ 0x91ff83775423cc29, + /*13*/ 0x8380dea93da4bc82, + /*14*/ 0xece53cec4a314f00, + /*15*/ 0xd5605fcdcf32e217, + /*16*/ 0xc0314325637a1978, + /*17*/ 0xad1c8eab5ee43ba2, + /*18*/ 0x9becce62836ac5b0, + /*19*/ 0x8c71dcd9ba0b495c, + /*20*/ 0xfd00b89747823938, + /*21*/ 0xe3e27a444d8d991a, + }; + + private static readonly short[] s_rgexp64Power10By16 = + { + // exponents for both powers of 10^16 and 0.1^16 + /*1*/ 54, + /*2*/ 107, + /*3*/ 160, + /*4*/ 213, + /*5*/ 266, + /*6*/ 319, + /*7*/ 373, + /*8*/ 426, + /*9*/ 479, + /*10*/ 532, + /*11*/ 585, + /*12*/ 638, + /*13*/ 691, + /*14*/ 745, + /*15*/ 798, + /*16*/ 851, + /*17*/ 904, + /*18*/ 957, + /*19*/ 1010, + /*20*/ 1064, + /*21*/ 1117, + }; + + private static int abs(int value) + { + if (value < 0) + return -value; + return value; + } + + private static unsafe double NumberToDouble(ref NumberBuffer number) + { + ulong val; + int exp; + char* src = number.digits; + int remaining; + int total; + int count; + int scale; + int absscale; + int index; + + total = string.wcslen(src); + remaining = total; + + // skip the leading zeros + while (*src == '0') + { + remaining--; + src++; + } + + if (remaining == 0) + return 0; + + count = Math.Min(remaining, 9); + remaining -= count; + val = DigitsToInt(src, count); + + if (remaining > 0) + { + count = Math.Min(remaining, 9); + remaining -= count; + + // get the denormalized power of 10 + uint mult = (uint)(s_rgval64Power10[count - 1] >> (64 - s_rgexp64Power10[count - 1])); + val = Mul32x32To64((uint)val, mult) + DigitsToInt(src + 9, count); + } + + scale = number.scale - (total - remaining); + absscale = abs(scale); + if (absscale >= 22 * 16) + { + // overflow / underflow + ulong result = (scale > 0) ? 0x7FF0000000000000 : 0ul; + if (number.sign) + result |= 0x8000000000000000; + return *(double*)&result; + } + + exp = 64; + + // normalize the mantissa + if ((val & 0xFFFFFFFF00000000) == 0) + { val <<= 32; exp -= 32; } + if ((val & 0xFFFF000000000000) == 0) + { val <<= 16; exp -= 16; } + if ((val & 0xFF00000000000000) == 0) + { val <<= 8; exp -= 8; } + if ((val & 0xF000000000000000) == 0) + { val <<= 4; exp -= 4; } + if ((val & 0xC000000000000000) == 0) + { val <<= 2; exp -= 2; } + if ((val & 0x8000000000000000) == 0) + { val <<= 1; exp -= 1; } + + index = absscale & 15; + if (index != 0) + { + int multexp = s_rgexp64Power10[index - 1]; + // the exponents are shared between the inverted and regular table + exp += (scale < 0) ? (-multexp + 1) : multexp; + + ulong multval = s_rgval64Power10[index + ((scale < 0) ? 15 : 0) - 1]; + val = Mul64Lossy(val, multval, ref exp); + } + + index = absscale >> 4; + if (index != 0) + { + int multexp = s_rgexp64Power10By16[index - 1]; + // the exponents are shared between the inverted and regular table + exp += (scale < 0) ? (-multexp + 1) : multexp; + + ulong multval = s_rgval64Power10By16[index + ((scale < 0) ? 21 : 0) - 1]; + val = Mul64Lossy(val, multval, ref exp); + } + + + // round & scale down + if (((int)val & (1 << 10)) != 0) + { + // IEEE round to even + ulong tmp = val + ((1 << 10) - 1) + (ulong)(((int)val >> 11) & 1); + if (tmp < val) + { + // overflow + tmp = (tmp >> 1) | 0x8000000000000000; + exp += 1; + } + val = tmp; + } + + // return the exponent to a biased state + exp += 0x3FE; + + // handle overflow, underflow, "Epsilon - 1/2 Epsilon", denormalized, and the normal case + if (exp <= 0) + { + if (exp == -52 && (val >= 0x8000000000000058)) + { + // round X where {Epsilon > X >= 2.470328229206232730000000E-324} up to Epsilon (instead of down to zero) + val = 0x0000000000000001; + } + else if (exp <= -52) + { + // underflow + val = 0; + } + else + { + // denormalized + val >>= (-exp + 11 + 1); + } + } + else if (exp >= 0x7FF) + { + // overflow + val = 0x7FF0000000000000; + } + else + { + // normal postive exponent case + val = ((ulong)exp << 52) + ((val >> 11) & 0x000FFFFFFFFFFFFF); + } + + if (number.sign) + val |= 0x8000000000000000; + + return *(double*)&val; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Number.Unix.cs b/external/corert/src/System.Private.CoreLib/src/System/Number.Unix.cs new file mode 100644 index 0000000000..113eda5671 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Number.Unix.cs @@ -0,0 +1,164 @@ +// 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.Diagnostics; +using System.Runtime; +using System.Globalization; + +namespace System +{ + internal partial class Number + { + // buffer size to hold digits (40), decimal point, number sign, exponent, exponent symbol 'e', exponent sign and null. + private const int MAX_BUFFER_SIZE = 50; + + private static unsafe void DoubleToNumber(double value, int precision, ref NumberBuffer number) + { + Debug.Assert(precision > 0 && precision < 40); + + number.precision = precision; + if (DoubleHelper.Exponent(value) == 0x7ff) + { + number.scale = DoubleHelper.Mantissa(value) != 0 ? ScaleNAN : ScaleINF; + number.sign = DoubleHelper.Sign(value); + number.digits[0] = '\0'; + return; + } + + byte* tempBuffer = stackalloc byte[MAX_BUFFER_SIZE]; + char* dst = number.digits; + + number.scale = 0; + number.sign = false; + *dst = '\0'; + + if (value < 0.0) + { + number.sign = true; + } + + if (value == 0.0) + { + for (int j = 0; j < precision; j++) + { + dst[j] = '0'; + } + dst[precision] = '\0'; + return; + } + + // + // Get the number formatted as a string in the form x.xxxxxxexxxx + // + + // "%.40e" + byte* format = stackalloc byte[6]; + format[0] = (byte)'%'; + format[1] = (byte)'.'; + format[2] = (byte)'4'; + format[3] = (byte)'0'; + format[4] = (byte)'e'; + format[5] = 0; + + int tempBufferLength = Interop.Sys.DoubleToString(value, format, tempBuffer, MAX_BUFFER_SIZE); + Debug.Assert(tempBufferLength > 0 && MAX_BUFFER_SIZE > tempBufferLength); + + // + // Calculate the exponent value + // + + int exponentIndex = tempBufferLength - 1; + while (tempBuffer[exponentIndex] != (byte)'e' && exponentIndex > 0) + { + exponentIndex--; + } + + Debug.Assert(exponentIndex > 0 && (exponentIndex < tempBufferLength - 1)); + + int i = exponentIndex + 1; + int exponentSign = 1; + if (tempBuffer[i] == '-') + { + exponentSign = -1; + i++; + } + else if (tempBuffer[i] == '+') + { + i++; + } + + int exponentValue = 0; + while (i < tempBufferLength) + { + Debug.Assert(tempBuffer[i] >= (byte)'0' && tempBuffer[i] <= (byte)'9'); + exponentValue = exponentValue * 10 + (tempBuffer[i] - (byte)'0'); + i++; + } + exponentValue *= exponentSign; + + // + // Determine decimal location. + // + + if (exponentValue == 0) + { + number.scale = 1; + } + else + { + number.scale = exponentValue + 1; + } + + // + // Copy the string from the temp buffer upto precision characters, removing the sign, and decimal as required. + // + + i = 0; + int mantissaIndex = 0; + while (i < precision && mantissaIndex < exponentIndex) + { + if (tempBuffer[mantissaIndex] >= (byte)'0' && tempBuffer[mantissaIndex] <= (byte)'9') + { + dst[i] = (char)tempBuffer[mantissaIndex]; + i++; + } + mantissaIndex++; + } + + while (i < precision) + { + dst[i] = '0'; // append zeros as needed + i++; + } + + dst[i] = '\0'; + + // + // Round if needed + // + + if (mantissaIndex >= exponentIndex || tempBuffer[mantissaIndex] < (byte)'5') + { + return; // rounding is not needed + } + + i = precision - 1; + while (dst[i] == '9' && i > 0) + { + dst[i] = '0'; + i--; + } + + if (i == 0 && dst[i] == '9') + { + dst[i] = '1'; + number.scale++; + } + else + { + dst[i]++; + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Number.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/Number.Windows.cs new file mode 100644 index 0000000000..f5180e99ef --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Number.Windows.cs @@ -0,0 +1,43 @@ +// 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.Globalization; +using System.Runtime; + +namespace System +{ + internal partial class Number + { + private static unsafe void DoubleToNumber(double value, int precision, ref NumberBuffer number) + { + number.precision = precision; + if (DoubleHelper.Exponent(value) == 0x7ff) + { + number.scale = DoubleHelper.Mantissa(value) != 0 ? ScaleNAN : ScaleINF; + number.sign = DoubleHelper.Sign(value); + number.digits[0] = '\0'; + } + else + { + byte* src = stackalloc byte[_CVTBUFSIZE]; + int sign; + fixed (NumberBuffer* pNumber = &number) + { + RuntimeImports._ecvt_s(src, _CVTBUFSIZE, value, precision, &pNumber->scale, &sign); + } + number.sign = sign != 0; + + char* dst = number.digits; + if ((char)*src != '0') + { + while (*src != 0) + *dst++ = (char)*src++; + } + *dst = '\0'; + } + } + } +} + + diff --git a/external/corert/src/System.Private.CoreLib/src/System/Object.cs b/external/corert/src/System.Private.CoreLib/src/System/Object.cs index b4936b2a4e..17a2f99cf8 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Object.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Object.cs @@ -21,6 +21,7 @@ using System.Runtime.Versioning; using Internal.Reflection.Core.NonPortable; using Internal.Runtime; +using Internal.Runtime.CompilerServices; namespace System { @@ -34,6 +35,8 @@ namespace System // services to subclasses. // PREFER: public class Object + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public unsafe class Object { // CS0649: Field '{blah}' is never assigned to, and will always have its default value @@ -42,6 +45,7 @@ namespace System // statement on partially typed objects. Wouldn't have to do this if we could directly declared pinned // locals. // @TODO: Consider making this EETypePtr instead of void *. + [NonSerialized] internal IntPtr m_pEEType; #pragma warning restore @@ -73,7 +77,7 @@ namespace System [Intrinsic] public Type GetType() { - return ReflectionCoreNonPortable.GetRuntimeTypeForEEType(EETypePtr); + return RuntimeTypeUnifier.GetRuntimeTypeForEEType(EETypePtr); } public virtual String ToString() diff --git a/external/corert/src/System.Private.CoreLib/src/System/OutOfMemoryException.cs b/external/corert/src/System.Private.CoreLib/src/System/OutOfMemoryException.cs index 6fbb494aae..ef42ba7524 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/OutOfMemoryException.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/OutOfMemoryException.cs @@ -16,26 +16,29 @@ using System.Runtime.Serialization; namespace System { [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class OutOfMemoryException : SystemException { public OutOfMemoryException() : base(SR.Arg_OutOfMemoryException) { - HResult = __HResults.COR_E_OUTOFMEMORY; + HResult = HResults.COR_E_OUTOFMEMORY; } public OutOfMemoryException(String message) : base(message) { - HResult = __HResults.COR_E_OUTOFMEMORY; + HResult = HResults.COR_E_OUTOFMEMORY; } public OutOfMemoryException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_OUTOFMEMORY; + HResult = HResults.COR_E_OUTOFMEMORY; } - protected OutOfMemoryException(SerializationInfo info, StreamingContext context) : base(info, context) { } + protected OutOfMemoryException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/ParseNumbers.cs b/external/corert/src/System.Private.CoreLib/src/System/ParseNumbers.cs deleted file mode 100644 index 5133c77fd4..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/ParseNumbers.cs +++ /dev/null @@ -1,610 +0,0 @@ -// 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. - -/*============================================================ -** -** -** -** Purpose: Methods for Parsing numbers and Strings. -** -** -===========================================================*/ - -using System; -using System.Text; -using System.Runtime.CompilerServices; - -namespace System -{ - internal static class ParseNumbers - { - internal const int LeftAlign = 0x0001; - internal const int RightAlign = 0x0004; - internal const int PrefixSpace = 0x0008; - internal const int PrintSign = 0x0010; - internal const int PrintBase = 0x0020; - internal const int PrintAsI1 = 0x0040; - internal const int PrintAsI2 = 0x0080; - internal const int PrintAsI4 = 0x0100; - internal const int TreatAsUnsigned = 0x0200; - internal const int TreatAsI1 = 0x0400; - internal const int TreatAsI2 = 0x0800; - internal const int IsTight = 0x1000; - internal const int NoSpace = 0x2000; - internal const int PrintRadixBase = 0x4000; - - private const int MinRadix = 2; - private const int MaxRadix = 36; - - public static unsafe long StringToLong(System.String s, int radix, int flags) - { - int pos = 0; - return StringToLong(s, radix, flags, ref pos); - } - - public static long StringToLong(string s, int radix, int flags, ref int currPos) - { - long result = 0; - - int sign = 1; - int length; - int i; - int grabNumbersStart = 0; - int r; - - if (s != null) - { - i = currPos; - - // Do some radix checking. - // A radix of -1 says to use whatever base is spec'd on the number. - // Parse in Base10 until we figure out what the base actually is. - r = (-1 == radix) ? 10 : radix; - - if (r != 2 && r != 10 && r != 8 && r != 16) - throw new ArgumentException(SR.Arg_InvalidBase, nameof(radix)); - - length = s.Length; - - if (i < 0 || i >= length) - throw new ArgumentOutOfRangeException(SR.ArgumentOutOfRange_Index); - - // Get rid of the whitespace and then check that we've still got some digits to parse. - if (((flags & IsTight) == 0) && ((flags & NoSpace) == 0)) - { - EatWhiteSpace(s, ref i); - if (i == length) - throw new FormatException(SR.Format_EmptyInputString); - } - - // Check for a sign - if (s[i] == '-') - { - if (r != 10) - throw new ArgumentException(SR.Arg_CannotHaveNegativeValue); - - if ((flags & TreatAsUnsigned) != 0) - throw new OverflowException(SR.Overflow_NegativeUnsigned); - - sign = -1; - i++; - } - else if (s[i] == '+') - { - i++; - } - - if ((radix == -1 || radix == 16) && (i + 1 < length) && s[i] == '0') - { - if (s[i + 1] == 'x' || s[i + 1] == 'X') - { - r = 16; - i += 2; - } - } - - grabNumbersStart = i; - result = GrabLongs(r, s, ref i, (flags & TreatAsUnsigned) != 0); - - // Check if they passed us a string with no parsable digits. - if (i == grabNumbersStart) - throw new FormatException(SR.Format_NoParsibleDigits); - - if ((flags & IsTight) != 0) - { - //If we've got effluvia left at the end of the string, complain. - if (i < length) - throw new FormatException(SR.Format_ExtraJunkAtEnd); - } - - // Put the current index back into the correct place. - currPos = i; - - // Return the value properly signed. - if ((ulong)result == 0x8000000000000000 && sign == 1 && r == 10 && ((flags & TreatAsUnsigned) == 0)) - throw new OverflowException(SR.Overflow_Int64); - - if (r == 10) - result *= sign; - } - else - { - result = 0; - } - - return result; - } - - public static int StringToInt(string s, int radix, int flags) - { - int pos = 0; - return StringToInt(s, radix, flags, ref pos); - } - - public static int StringToInt(string s, int radix, int flags, ref int currPos) - { - int result = 0; - - int sign = 1; - int length; - int i; - int grabNumbersStart = 0; - int r; - - if (s != null) - { - // They're requied to tell me where to start parsing. - i = currPos; - - // Do some radix checking. - // A radix of -1 says to use whatever base is spec'd on the number. - // Parse in Base10 until we figure out what the base actually is. - r = (-1 == radix) ? 10 : radix; - - if (r != 2 && r != 10 && r != 8 && r != 16) - throw new ArgumentException(SR.Arg_InvalidBase, nameof(radix)); - - length = s.Length; - - if (i < 0 || i >= length) - throw new ArgumentOutOfRangeException(SR.ArgumentOutOfRange_Index); - - // Get rid of the whitespace and then check that we've still got some digits to parse. - if (((flags & IsTight) == 0) && ((flags & NoSpace) == 0)) - { - EatWhiteSpace(s, ref i); - if (i == length) - throw new FormatException(SR.Format_EmptyInputString); - } - - // Check for a sign - if (s[i] == '-') - { - if (r != 10) - throw new ArgumentException(SR.Arg_CannotHaveNegativeValue); - - if ((flags & TreatAsUnsigned) != 0) - throw new OverflowException(SR.Overflow_NegativeUnsigned); - - sign = -1; - i++; - } - else if (s[i] == '+') - { - i++; - } - - // Consume the 0x if we're in an unknown base or in base-16. - if ((radix == -1 || radix == 16) && (i + 1 < length) && s[i] == '0') - { - if (s[i + 1] == 'x' || s[i + 1] == 'X') - { - r = 16; - i += 2; - } - } - - grabNumbersStart = i; - result = GrabInts(r, s, ref i, ((flags & TreatAsUnsigned) != 0)); - - // Check if they passed us a string with no parsable digits. - if (i == grabNumbersStart) - throw new FormatException(SR.Format_NoParsibleDigits); - - if ((flags & IsTight) != 0) - { - // If we've got effluvia left at the end of the string, complain. - if (i < length) - throw new FormatException(SR.Format_ExtraJunkAtEnd); - } - - // Put the current index back into the correct place. - currPos = i; - - // Return the value properly signed. - if ((flags & TreatAsI1) != 0) - { - if ((uint)result > 0xFF) - throw new OverflowException(SR.Overflow_SByte); - } - else if ((flags & TreatAsI2) != 0) - { - if ((uint)result > 0xFFFF) - throw new OverflowException(SR.Overflow_Int16); - } - else if ((uint)result == 0x80000000 && sign == 1 && r == 10 && ((flags & TreatAsUnsigned) == 0)) - { - throw new OverflowException(SR.Overflow_Int32); - } - - if (r == 10) - result *= sign; - } - else - { - result = 0; - } - - return result; - } - - public static String IntToString(int n, int radix, int width, char paddingChar, int flags) - { - bool isNegative = false; - int index = 0; - int buffLength; - int i; - uint l; - char[] buffer = new char[66]; // Longest possible string length for an integer in binary notation with prefix - - if (radix < MinRadix || radix > MaxRadix) - throw new ArgumentException(SR.Arg_InvalidBase, nameof(radix)); - - // If the number is negative, make it positive and remember the sign. - // If the number is MIN_VALUE, this will still be negative, so we'll have to - // special case this later. - if (n < 0) - { - isNegative = true; - - // For base 10, write out -num, but other bases write out the - // 2's complement bit pattern - if (10 == radix) - l = (uint)-n; - else - l = (uint)n; - } - else - { - l = (uint)n; - } - - // The conversion to a uint will sign extend the number. In order to ensure - // that we only get as many bits as we expect, we chop the number. - if ((flags & PrintAsI1) != 0) - l &= 0xFF; - else if ((flags & PrintAsI2) != 0) - l &= 0xFFFF; - - // Special case the 0. - if (0 == l) - { - buffer[0] = '0'; - index = 1; - } - else - { - do - { - uint charVal = l % (uint)radix; - l /= (uint)radix; - if (charVal < 10) - buffer[index++] = (char)(charVal + '0'); - else - buffer[index++] = (char)(charVal + 'a' - 10); - } - while (l != 0); - } - - // If they want the base, append that to the string (in reverse order) - if (radix != 10 && ((flags & PrintBase) != 0)) - { - if (16 == radix) - { - buffer[index++] = 'x'; - buffer[index++] = '0'; - } - else if (8 == radix) - { - buffer[index++] = '0'; - } - } - - if (10 == radix) - { - // If it was negative, append the sign, else if they requested, add the '+'. - // If they requested a leading space, put it on. - if (isNegative) - buffer[index++] = '-'; - else if ((flags & PrintSign) != 0) - buffer[index++] = '+'; - else if ((flags & PrefixSpace) != 0) - buffer[index++] = ' '; - } - - // Figure out the size of our string. - if (width <= index) - buffLength = index; - else - buffLength = width; - - StringBuilder sb = new StringBuilder(buffLength); - - // Put the characters into the String in reverse order - // Fill the remaining space -- if there is any -- - // with the correct padding character. - if ((flags & LeftAlign) != 0) - { - for (i = 0; i < index; i++) - sb.Append(buffer[index - i - 1]); - - if (buffLength > index) - sb.Append(paddingChar, buffLength - index); - } - else - { - if (buffLength > index) - sb.Append(paddingChar, buffLength - index); - - for (i = 0; i < index; i++) - sb.Append(buffer[index - i - 1]); - } - - return sb.ToString(); - } - - public static String LongToString(long n, int radix, int width, char paddingChar, int flags) - { - bool isNegative = false; - int index = 0; - int charVal; - ulong ul; - int i; - int buffLength = 0; - char[] buffer = new char[67];//Longest possible string length for an integer in binary notation with prefix - - if (radix < MinRadix || radix > MaxRadix) - throw new ArgumentException(SR.Arg_InvalidBase, nameof(radix)); - - //If the number is negative, make it positive and remember the sign. - if (n < 0) - { - isNegative = true; - - // For base 10, write out -num, but other bases write out the - // 2's complement bit pattern - if (10 == radix) - ul = (ulong)(-n); - else - ul = (ulong)n; - } - else - { - ul = (ulong)n; - } - - if ((flags & PrintAsI1) != 0) - ul = ul & 0xFF; - else if ((flags & PrintAsI2) != 0) - ul = ul & 0xFFFF; - else if ((flags & PrintAsI4) != 0) - ul = ul & 0xFFFFFFFF; - - //Special case the 0. - if (0 == ul) - { - buffer[0] = '0'; - index = 1; - } - else - { - //Pull apart the number and put the digits (in reverse order) into the buffer. - for (index = 0; ul > 0; ul = ul / (ulong)radix, index++) - { - if ((charVal = (int)(ul % (ulong)radix)) < 10) - buffer[index] = (char)(charVal + '0'); - else - buffer[index] = (char)(charVal + 'a' - 10); - } - } - - //If they want the base, append that to the string (in reverse order) - if (radix != 10 && ((flags & PrintBase) != 0)) - { - if (16 == radix) - { - buffer[index++] = 'x'; - buffer[index++] = '0'; - } - else if (8 == radix) - { - buffer[index++] = '0'; - } - else if ((flags & PrintRadixBase) != 0) - { - buffer[index++] = '#'; - buffer[index++] = (char)((radix % 10) + '0'); - buffer[index++] = (char)((radix / 10) + '0'); - } - } - - if (10 == radix) - { - //If it was negative, append the sign. - if (isNegative) - { - buffer[index++] = '-'; - } - - //else if they requested, add the '+'; - else if ((flags & PrintSign) != 0) - { - buffer[index++] = '+'; - } - - //If they requested a leading space, put it on. - else if ((flags & PrefixSpace) != 0) - { - buffer[index++] = ' '; - } - } - - //Figure out the size of our string. - if (width <= index) - buffLength = index; - else - buffLength = width; - - StringBuilder sb = new StringBuilder(buffLength); - - //Put the characters into the String in reverse order - //Fill the remaining space -- if there is any -- - //with the correct padding character. - if ((flags & LeftAlign) != 0) - { - for (i = 0; i < index; i++) - sb.Append(buffer[index - i - 1]); - - if (buffLength > index) - sb.Append(paddingChar, buffLength - index); - } - else - { - if (buffLength > index) - sb.Append(paddingChar, buffLength - index); - - for (i = 0; i < index; i++) - sb.Append(buffer[index - i - 1]); - } - - return sb.ToString(); - } - - private static void EatWhiteSpace(string s, ref int i) - { - for (; i < s.Length && char.IsWhiteSpace(s[i]); i++) - ; - } - - private static long GrabLongs(int radix, string s, ref int i, bool isUnsigned) - { - ulong result = 0; - int value; - ulong maxVal; - - // Allow all non-decimal numbers to set the sign bit. - if (radix == 10 && !isUnsigned) - { - maxVal = 0x7FFFFFFFFFFFFFFF / 10; - - // Read all of the digits and convert to a number - while (i < s.Length && (IsDigit(s[i], radix, out value))) - { - // Check for overflows - this is sufficient & correct. - if (result > maxVal || ((long)result) < 0) - throw new OverflowException(SR.Overflow_Int64); - result = result * (ulong)radix + (ulong)value; - i++; - } - - if ((long)result < 0 && result != 0x8000000000000000) - throw new OverflowException(SR.Overflow_Int64); - } - else - { - maxVal = 0xffffffffffffffff / (ulong)radix; - - // Read all of the digits and convert to a number - while (i < s.Length && (IsDigit(s[i], radix, out value))) - { - // Check for overflows - this is sufficient & correct. - if (result > maxVal) - throw new OverflowException(SR.Overflow_UInt64); - - ulong temp = result * (ulong)radix + (ulong)value; - if (temp < result) // this means overflow as well - throw new OverflowException(SR.Overflow_UInt64); - result = temp; - - i++; - } - } - - return (long)result; - } - - private static int GrabInts(int radix, string s, ref int i, bool isUnsigned) - { - uint result = 0; - int value; - uint maxVal; - - // Allow all non-decimal numbers to set the sign bit. - if (radix == 10 && !isUnsigned) - { - maxVal = (0x7FFFFFFF / 10); - - // Read all of the digits and convert to a number - while (i < s.Length && (IsDigit(s[i], radix, out value))) - { - // Check for overflows - this is sufficient & correct. - if (result > maxVal || (int)result < 0) - throw new OverflowException(SR.Overflow_Int32); - result = result * (uint)radix + (uint)value; - i++; - } - if ((int)result < 0 && result != 0x80000000) - throw new OverflowException(SR.Overflow_Int32); - } - else - { - maxVal = 0xffffffff / (uint)radix; - - // Read all of the digits and convert to a number - while (i < s.Length && (IsDigit(s[i], radix, out value))) - { - // Check for overflows - this is sufficient & correct. - if (result > maxVal) - throw new OverflowException(SR.Overflow_UInt32); - // the above check won't cover 4294967296 to 4294967299 - uint temp = result * (uint)radix + (uint)value; - if (temp < result) // this means overflow as well - throw new OverflowException(SR.Overflow_UInt32); - - result = temp; - i++; - } - } - - return (int)result; - } - - private static bool IsDigit(char c, int radix, out int result) - { - if (c >= '0' && c <= '9') - result = c - '0'; - else if (c >= 'A' && c <= 'Z') - result = c - 'A' + 10; - else if (c >= 'a' && c <= 'z') - result = c - 'a' + 10; - else - result = -1; - - if ((result >= 0) && (result < radix)) - return true; - - return false; - } - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Reflection/Assembly.CoreRT.cs b/external/corert/src/System.Private.CoreLib/src/System/Reflection/Assembly.CoreRT.cs index e7e59817e5..7679e0cb8b 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Reflection/Assembly.CoreRT.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Reflection/Assembly.CoreRT.cs @@ -18,7 +18,7 @@ namespace System.Reflection public static Assembly GetCallingAssembly() { throw new PlatformNotSupportedException(); } - public static Assembly Load(AssemblyName assemblyRef) => ReflectionAugments.ReflectionCoreCallbacks.Load(assemblyRef); + public static Assembly Load(AssemblyName assemblyRef) => ReflectionAugments.ReflectionCoreCallbacks.Load(assemblyRef, throwOnFileNotFound: true); public static Assembly Load(byte[] rawAssembly, byte[] rawSymbolStore) => ReflectionAugments.ReflectionCoreCallbacks.Load(rawAssembly, rawSymbolStore); public static Assembly Load(string assemblyString) diff --git a/external/corert/src/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs b/external/corert/src/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs index 32baab22e0..3dadda396b 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs @@ -28,13 +28,6 @@ namespace System.Reflection runtimeAssemblyName.CopyToAssemblyName(this); } - // Constructs a new AssemblyName during deserialization. (Needs to public so we can whitelist in Reflection). - public AssemblyName(SerializationInfo info, StreamingContext context) - { - //The graph is not valid until OnDeserialization() has been called. - _siInfo = info; - } - public object Clone() { AssemblyName n = new AssemblyName(); @@ -182,51 +175,18 @@ namespace System.Reflection public void GetObjectData(SerializationInfo info, StreamingContext context) { - if (info == null) - throw new ArgumentNullException(nameof(info)); - - //Allocate the serialization info and serialize our static data. - info.AddValue("_Name", Name); - info.AddValue("_PublicKey", _publicKey, typeof(byte[])); - info.AddValue("_PublicKeyToken", _publicKeyToken, typeof(byte[])); - info.AddValue("_CultureInfo", (CultureInfo == null) ? -1 : CultureInfo.LCID); - info.AddValue("_CodeBase", CodeBase); - info.AddValue("_Version", Version); - info.AddValue("_HashAlgorithm", HashAlgorithm, typeof(AssemblyHashAlgorithm)); - info.AddValue("_StrongNameKeyPair", KeyPair, typeof(StrongNameKeyPair)); - info.AddValue("_VersionCompatibility", VersionCompatibility, typeof(AssemblyVersionCompatibility)); - info.AddValue("_Flags", _flags, typeof(AssemblyNameFlags)); - - // These are fields used (and set) internally by the full framework only. The fields are optional but the full framework - // will catch an exception internally if they aren't there so to avoid that annoyance, we'll emit them using their default values. - info.AddValue("_HashAlgorithmForControl", AssemblyHashAlgorithm.None, typeof(AssemblyHashAlgorithm)); - info.AddValue("_HashForControl", null, typeof(byte[])); + throw new PlatformNotSupportedException(); } public void OnDeserialization(object sender) { - // Deserialization has already been performed - if (_siInfo == null) - return; - - Name = _siInfo.GetString("_Name"); - _publicKey = (byte[])_siInfo.GetValue("_PublicKey", typeof(byte[])); - _publicKeyToken = (byte[])_siInfo.GetValue("_PublicKeyToken", typeof(byte[])); - int lcid = (int)_siInfo.GetInt32("_CultureInfo"); - if (lcid != -1) - CultureInfo = new CultureInfo(lcid); - - CodeBase = _siInfo.GetString("_CodeBase"); - Version = (Version)_siInfo.GetValue("_Version", typeof(Version)); - HashAlgorithm = (AssemblyHashAlgorithm)_siInfo.GetValue("_HashAlgorithm", typeof(AssemblyHashAlgorithm)); - KeyPair = (StrongNameKeyPair)_siInfo.GetValue("_StrongNameKeyPair", typeof(StrongNameKeyPair)); - VersionCompatibility = (AssemblyVersionCompatibility)_siInfo.GetValue("_VersionCompatibility", typeof(AssemblyVersionCompatibility)); - _flags = (AssemblyNameFlags)_siInfo.GetValue("_Flags", typeof(AssemblyNameFlags)); - - _siInfo = null; + throw new PlatformNotSupportedException(); } - public static AssemblyName GetAssemblyName(string assemblyFile) { throw new NotImplementedException(); } // TODO: https://github.com/dotnet/corert/issues/3253 + public static AssemblyName GetAssemblyName(string assemblyFile) + { + throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported_AssemblyName_GetAssemblyName); + } /// /// Compares the simple names disregarding Version, Culture and PKT. While this clearly does not @@ -254,8 +214,6 @@ namespace System.Reflection private AssemblyNameFlags _flags; private byte[] _publicKey; private byte[] _publicKeyToken; - - private SerializationInfo _siInfo; } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Reflection/AssemblyNameParser.cs b/external/corert/src/System.Private.CoreLib/src/System/Reflection/AssemblyNameParser.cs index a2c779611a..a29919faa0 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Reflection/AssemblyNameParser.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Reflection/AssemblyNameParser.cs @@ -41,10 +41,10 @@ namespace System.Reflection String name; AssemblyNameLexer.Token token = lexer.GetNext(out name); if (token != AssemblyNameLexer.Token.String) - throw new FileLoadException(); + throw new FileLoadException(SR.InvalidAssemblyName); if (name == String.Empty || name.IndexOfAny(s_illegalCharactersInSimpleName) != -1) - throw new FileLoadException(); + throw new FileLoadException(SR.InvalidAssemblyName); Version version = null; String cultureName = null; @@ -56,12 +56,12 @@ namespace System.Reflection while (token != AssemblyNameLexer.Token.End) { if (token != AssemblyNameLexer.Token.Comma) - throw new FileLoadException(); + throw new FileLoadException(SR.InvalidAssemblyName); String attributeName; token = lexer.GetNext(out attributeName); if (token != AssemblyNameLexer.Token.String) - throw new FileLoadException(); + throw new FileLoadException(SR.InvalidAssemblyName); token = lexer.GetNext(); // Compat note: Inside AppX apps, the desktop CLR's AssemblyName parser skips past any elements that don't follow the "=" pattern. @@ -71,19 +71,19 @@ namespace System.Reflection continue; if (token != AssemblyNameLexer.Token.Equals) - throw new FileLoadException(); + throw new FileLoadException(SR.InvalidAssemblyName); String attributeValue; token = lexer.GetNext(out attributeValue); if (token != AssemblyNameLexer.Token.String) - throw new FileLoadException(); + throw new FileLoadException(SR.InvalidAssemblyName); if (attributeName == String.Empty) - throw new FileLoadException(); + throw new FileLoadException(SR.InvalidAssemblyName); for (int i = 0; i < alreadySeen.Count; i++) { if (alreadySeen[i].Equals(attributeName, StringComparison.OrdinalIgnoreCase)) - throw new FileLoadException(); // Cannot specify the same attribute twice. + throw new FileLoadException(SR.InvalidAssemblyName); // Cannot specify the same attribute twice. } alreadySeen.Add(attributeName); @@ -116,7 +116,7 @@ namespace System.Reflection // nothing to do } else - throw new FileLoadException(); + throw new FileLoadException(SR.InvalidAssemblyName); } if (attributeName.Equals("ContentType", StringComparison.OrdinalIgnoreCase)) @@ -124,7 +124,7 @@ namespace System.Reflection if (attributeValue.Equals("WindowsRuntime", StringComparison.OrdinalIgnoreCase)) flags |= (AssemblyNameFlags)(((int)AssemblyContentType.WindowsRuntime) << 9); else - throw new FileLoadException(); + throw new FileLoadException(SR.InvalidAssemblyName); } // Desktop compat: If we got here, the attribute name is unknown to us. Ignore it (as long it's not duplicated.) @@ -137,7 +137,7 @@ namespace System.Reflection { String[] parts = attributeValue.Split('.'); if (parts.Length > 4) - throw new FileLoadException(); + throw new FileLoadException(SR.InvalidAssemblyName); ushort[] versionNumbers = new ushort[4]; for (int i = 0; i < versionNumbers.Length; i++) { @@ -149,26 +149,21 @@ namespace System.Reflection for (int j = 0; j < parts[i].Length; j++) { if (!Char.IsDigit(parts[i][j])) - throw new FileLoadException(); + throw new FileLoadException(SR.InvalidAssemblyName); } if (!(ushort.TryParse(parts[i], out versionNumbers[i]))) { - if (parts[i] == string.Empty) - { - // Desktop compat: Empty strings are a synonym for 0 - versionNumbers[i] = 0; - } - else - { - throw new FileLoadException(); - } + throw new FileLoadException(SR.InvalidAssemblyName); } } } - if (parts.Length == 1) - return null; // Desktop compat: if only major version present, treat as no version. - + if (versionNumbers[0] == ushort.MaxValue || versionNumbers[1] == ushort.MaxValue) + throw new FileLoadException(SR.InvalidAssemblyName); + 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]); } @@ -191,7 +186,7 @@ namespace System.Reflection return Array.Empty(); if (attributeValue.Length != 8 * 2) - throw new FileLoadException(); + throw new FileLoadException(SR.InvalidAssemblyName); byte[] pkt = new byte[8]; int srcIndex = 0; @@ -216,7 +211,7 @@ namespace System.Reflection return ProcessorArchitecture.Amd64; if (attributeValue.Equals("arm", StringComparison.OrdinalIgnoreCase)) return ProcessorArchitecture.Arm; - throw new FileLoadException(); + throw new FileLoadException(SR.InvalidAssemblyName); } private static byte ParseHexNybble(char c) @@ -227,7 +222,7 @@ namespace System.Reflection return (byte)(c - 'a' + 10); if (c >= 'A' && c <= 'F') return (byte)(c - 'A' + 10); - throw new FileLoadException(); + throw new FileLoadException(SR.InvalidAssemblyName); } private static readonly char[] s_illegalCharactersInSimpleName = { '/', '\\', ':' }; diff --git a/external/corert/src/System.Private.CoreLib/src/System/Reflection/LocalVariableInfo.cs b/external/corert/src/System.Private.CoreLib/src/System/Reflection/LocalVariableInfo.cs index ec483b41c3..31e58caeb7 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Reflection/LocalVariableInfo.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Reflection/LocalVariableInfo.cs @@ -33,7 +33,7 @@ namespace System.Reflection get { // Don't laugh - this is really how the desktop behaves if you don't override. - Debug.Assert(false, "type must be set!"); + Debug.Fail("type must be set!"); return null; } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Reflection/TypeInfo.Workaround.cs b/external/corert/src/System.Private.CoreLib/src/System/Reflection/TypeInfo.Workaround.cs deleted file mode 100644 index acb1d532dd..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/Reflection/TypeInfo.Workaround.cs +++ /dev/null @@ -1,18 +0,0 @@ -// 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.Collections.Generic; - -namespace System.Reflection -{ - public abstract partial class TypeInfo : Type, IReflectableType - { - // TODO https://github.com/dotnet/corefx/issues/9805: These are inherited from Type and shouldn't need to be redeclared on TypeInfo but - // they are a well-known methods to the reducer. - - public override abstract Assembly Assembly { get; } - public override abstract Type BaseType { get; } - public override Type MakeGenericType(params Type[] typeArguments) => base.MakeGenericType(typeArguments); - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Resources/FastResourceComparer.cs b/external/corert/src/System.Private.CoreLib/src/System/Resources/FastResourceComparer.cs index e05ed8960a..3af7a1546c 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Resources/FastResourceComparer.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Resources/FastResourceComparer.cs @@ -17,7 +17,7 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Diagnostics.Contracts; +using System.Diagnostics; namespace System.Resources { @@ -82,8 +82,8 @@ namespace System.Resources // little endian unicode. Pass in the number of valid chars. public unsafe static int CompareOrdinal(String a, byte[] bytes, int bCharLength) { - Contract.Assert(a != null && bytes != null, "FastResourceComparer::CompareOrdinal must have non-null params"); - Contract.Assert(bCharLength * 2 <= bytes.Length, "FastResourceComparer::CompareOrdinal - numChars is too big!"); + Debug.Assert(a != null && bytes != null, "FastResourceComparer::CompareOrdinal must have non-null params"); + Debug.Assert(bCharLength * 2 <= bytes.Length, "FastResourceComparer::CompareOrdinal - numChars is too big!"); // This is a managed version of strcmp, but I can't take advantage // of a terminating 0, unlike strcmp in C. int i = 0; @@ -118,9 +118,9 @@ namespace System.Resources // The byte* must point to little endian Unicode characters. internal unsafe static int CompareOrdinal(byte* a, int byteLen, String b) { - Contract.Assert((byteLen & 1) == 0, "CompareOrdinal is expecting a UTF-16 string length, which must be even!"); - Contract.Assert(a != null && b != null, "Null args not allowed."); - Contract.Assert(byteLen >= 0, "byteLen must be non-negative."); + Debug.Assert((byteLen & 1) == 0, "CompareOrdinal is expecting a UTF-16 string length, which must be even!"); + Debug.Assert(a != null && b != null, "Null args not allowed."); + Debug.Assert(byteLen >= 0, "byteLen must be non-negative."); int r = 0; int i = 0; diff --git a/external/corert/src/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs b/external/corert/src/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs index 07353f4b69..54240cb5f0 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs @@ -26,8 +26,7 @@ namespace System.Resources using System.Text; using System.Threading; using System.Diagnostics; - using System.Diagnostics.Contracts; - + internal class FileBasedResourceGroveler : IResourceGroveler { private ResourceManager.ResourceManagerMediator _mediator; @@ -90,14 +89,14 @@ namespace System.Resources if (_mediator.ModuleDir != null) { String path = Path.Combine(_mediator.ModuleDir, fileName); - if (File.Exists(path)) + if (InternalFile.Exists(path)) { return path; } } // look in . - if (File.Exists(fileName)) + if (InternalFile.Exists(fileName)) return fileName; return null; // give up. @@ -132,4 +131,4 @@ namespace System.Resources } } } -} \ No newline at end of file +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.cs b/external/corert/src/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.cs index e7f48be901..4fdec754f8 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.cs @@ -15,22 +15,22 @@ ===========================================================*/ #define RESOURCE_SATELLITE_CONFIG +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Text; +using System.Threading; +using Microsoft.Win32; + namespace System.Resources { - using System; - using System.Collections; - using System.Collections.Generic; - using System.Globalization; - using System.IO; - using System.Reflection; - using System.Runtime.InteropServices; - using System.Runtime.CompilerServices; - using System.Runtime.Versioning; - using System.Text; - using System.Threading; - using System.Diagnostics.Contracts; - using Microsoft.Win32; - // // Note: this type is integral to the construction of exception objects, // and sometimes this has to be done in low memory situtations (OOM) or @@ -48,14 +48,14 @@ namespace System.Resources { // here and below: convert asserts to preconditions where appropriate when we get // contracts story in place. - Contract.Requires(mediator != null, "mediator shouldn't be null; check caller"); + Debug.Assert(mediator != null, "mediator shouldn't be null; check caller"); _mediator = mediator; } public ResourceSet GrovelForResourceSet(CultureInfo culture, Dictionary localResourceSets, bool tryParents, bool createIfNotExists) { - Contract.Assert(culture != null, "culture shouldn't be null; check caller"); - Contract.Assert(localResourceSets != null, "localResourceSets shouldn't be null; check caller"); + Debug.Assert(culture != null, "culture shouldn't be null; check caller"); + Debug.Assert(localResourceSets != null, "localResourceSets shouldn't be null; check caller"); ResourceSet rs = null; Stream stream = null; @@ -150,7 +150,7 @@ namespace System.Resources internal static CultureInfo GetNeutralResourcesLanguage(Assembly a, ref UltimateResourceFallbackLocation fallbackLocation) { - Contract.Assert(a != null, "assembly != null"); + Debug.Assert(a != null, "assembly != null"); string cultureName = null; short fallback = 0; if (GetNeutralResourcesLanguageAttribute(a, @@ -181,7 +181,7 @@ namespace System.Resources // fires, please fix the build process for the BCL directory. if (a == typeof(Object).GetTypeInfo().Assembly) { - Contract.Assert(false, a.GetName().Name + "'s NeutralResourcesLanguageAttribute is a malformed culture name! name: \"" + cultureName + "\" Exception: " + e); + Debug.Fail(a.GetName().Name + "'s NeutralResourcesLanguageAttribute is a malformed culture name! name: \"" + cultureName + "\" Exception: " + e); return CultureInfo.InvariantCulture; } @@ -197,7 +197,7 @@ namespace System.Resources // This method could use some refactoring. One thing at a time. internal ResourceSet CreateResourceSet(Stream store, Assembly assembly) { - Contract.Assert(store != null, "I need a Stream!"); + Debug.Assert(store != null, "I need a Stream!"); // Check to see if this is a Stream the ResourceManager understands, // and check for the correct resource reader type. if (store.CanSeek && store.Length > 4) @@ -265,7 +265,7 @@ namespace System.Resources Type resSetType; if (_mediator.UserResourceSet == null) { - Contract.Assert(resSetTypeName != null, "We should have a ResourceSet type name from the custom resource file here."); + Debug.Assert(resSetTypeName != null, "We should have a ResourceSet type name from the custom resource file here."); resSetType = Type.GetType(resSetTypeName, true, false); } else @@ -322,8 +322,8 @@ namespace System.Resources private Stream GetManifestResourceStream(Assembly satellite, String fileName) { - Contract.Requires(satellite != null, "satellite shouldn't be null; check caller"); - Contract.Requires(fileName != null, "fileName shouldn't be null; check caller"); + Debug.Assert(satellite != null, "satellite shouldn't be null; check caller"); + Debug.Assert(fileName != null, "fileName shouldn't be null; check caller"); Stream stream = satellite.GetManifestResourceStream(_mediator.LocationInfo, fileName); if (stream == null) @@ -340,8 +340,8 @@ namespace System.Resources // even optionally case-insensitive. private Stream CaseInsensitiveManifestResourceStreamLookup(Assembly satellite, String name) { - Contract.Requires(satellite != null, "satellite shouldn't be null; check caller"); - Contract.Requires(name != null, "name shouldn't be null; check caller"); + Debug.Assert(satellite != null, "satellite shouldn't be null; check caller"); + Debug.Assert(name != null, "name shouldn't be null; check caller"); StringBuilder sb = new StringBuilder(); if (_mediator.LocationInfo != null) @@ -425,8 +425,8 @@ namespace System.Resources // creating a ResourceReader via Reflection. private bool CanUseDefaultResourceClasses(String readerTypeName, String resSetTypeName) { - Contract.Assert(readerTypeName != null, "readerTypeName shouldn't be null; check caller"); - Contract.Assert(resSetTypeName != null, "resSetTypeName shouldn't be null; check caller"); + Debug.Assert(readerTypeName != null, "readerTypeName shouldn't be null; check caller"); + Debug.Assert(resSetTypeName != null, "resSetTypeName shouldn't be null; check caller"); if (_mediator.UserResourceSet != null) return false; @@ -492,7 +492,7 @@ namespace System.Resources if (_mediator.MainAssembly == typeof(Object).GetTypeInfo().Assembly && _mediator.BaseName.Equals(System.CoreLib.Name)) { // This would break CultureInfo & all our exceptions. - Contract.Assert(false, "Couldn't get " + System.CoreLib.Name + ResourceManager.ResFileExtension + " from " + System.CoreLib.Name + "'s assembly" + Environment.NewLine + Environment.NewLine + "Are you building the runtime on your machine? Chances are the BCL directory didn't build correctly. Type 'build -c' in the BCL directory. If you get build errors, look at buildd.log. If you then can't figure out what's wrong (and you aren't changing the assembly-related metadata code), ask a BCL dev.\n\nIf you did NOT build the runtime, you shouldn't be seeing this and you've found a bug."); + Debug.Fail("Couldn't get " + System.CoreLib.Name + ResourceManager.ResFileExtension + " from " + System.CoreLib.Name + "'s assembly" + Environment.NewLine + Environment.NewLine + "Are you building the runtime on your machine? Chances are the BCL directory didn't build correctly. Type 'build -c' in the BCL directory. If you get build errors, look at buildd.log. If you then can't figure out what's wrong (and you aren't changing the assembly-related metadata code), ask a BCL dev.\n\nIf you did NOT build the runtime, you shouldn't be seeing this and you've found a bug."); // We cannot continue further - simply FailFast. string mesgFailFast = System.CoreLib.Name + ResourceManager.ResFileExtension + " couldn't be found! Large parts of the BCL won't work!"; @@ -533,4 +533,4 @@ namespace System.Resources return false; } } -} \ No newline at end of file +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Resources/ResourceManager.cs b/external/corert/src/System.Private.CoreLib/src/System/Resources/ResourceManager.cs index 20f68d05ba..cabbff6a75 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Resources/ResourceManager.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Resources/ResourceManager.cs @@ -10,13 +10,13 @@ #define FEATURE_APPX #endif // ENABLE_WINRT +using Internal.Reflection.Augments; using Internal.Runtime.Augments; using Internal.Runtime.CompilerServices; using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Globalization; using System.IO; using System.Reflection; @@ -50,8 +50,23 @@ namespace System.Resources Version version, bool throwOnFileNotFound) { - // TODO: Make this work (but we can't throw NotImplemented because that would break all resource lookups) - return null; + AssemblyName mainAssemblyAn = mainAssembly.GetName(); + AssemblyName an = new AssemblyName(); + + an.CultureInfo = culture; + an.Name = name; + an.SetPublicKeyToken(mainAssemblyAn.GetPublicKeyToken()); + an.Flags = mainAssemblyAn.Flags; + an.Version = version ?? mainAssemblyAn.Version; + + Assembly retAssembly = ReflectionAugments.ReflectionCoreCallbacks.Load(an, false); + + if (retAssembly == mainAssembly || (retAssembly == null && throwOnFileNotFound)) + { + throw new FileNotFoundException(SR.Format(SR.IO_FileNotFound_FileName, an.Name)); + } + + return retAssembly; } } @@ -168,7 +183,6 @@ namespace System.Resources // belonging to that type may not be initialized. FrameworkEventSource.Log // is one such example. // - [Serializable] public class ResourceManager { internal class CultureNameResourceSetPair @@ -268,12 +282,10 @@ namespace System.Resources SetAppXConfiguration(); - if (_bUsingModernResourceManagement == false) - { - _lastUsedResourceCache = new CultureNameResourceSetPair(); - ResourceManagerMediator mediator = new ResourceManagerMediator(this); - resourceGroveler = new ManifestBasedResourceGroveler(mediator); - } + // Now we can use the managed resources even when using PRI's to support the APIs GetObject, GetStream...etc. + _lastUsedResourceCache = new CultureNameResourceSetPair(); + ResourceManagerMediator mediator = new ResourceManagerMediator(this); + resourceGroveler = new ManifestBasedResourceGroveler(mediator); } // Constructs a Resource Manager for files beginning with @@ -292,7 +304,6 @@ namespace System.Resources throw new ArgumentNullException(nameof(baseName)); if (null == resourceDir) throw new ArgumentNullException(nameof(resourceDir)); - Contract.EndContractBlock(); BaseNameField = baseName; @@ -313,7 +324,6 @@ namespace System.Resources if (null == assembly) throw new ArgumentNullException(nameof(assembly)); - Contract.EndContractBlock(); MainAssembly = assembly; BaseNameField = baseName; @@ -398,18 +408,16 @@ namespace System.Resources // security check in each constructor prevents it. private void CommonAssemblyInit() { - if (_bUsingModernResourceManagement == false) - { - UseManifest = true; + // Now we can use the managed resources even when using PRI's to support the APIs GetObject, GetStream...etc. + UseManifest = true; - _resourceSets = new Dictionary(); - _lastUsedResourceCache = new CultureNameResourceSetPair(); + _resourceSets = new Dictionary(); + _lastUsedResourceCache = new CultureNameResourceSetPair(); - _fallbackLoc = UltimateResourceFallbackLocation.MainAssembly; + _fallbackLoc = UltimateResourceFallbackLocation.MainAssembly; - ResourceManagerMediator mediator = new ResourceManagerMediator(this); - resourceGroveler = new ManifestBasedResourceGroveler(mediator); - } + ResourceManagerMediator mediator = new ResourceManagerMediator(this); + resourceGroveler = new ManifestBasedResourceGroveler(mediator); _neutralResourcesCulture = ManifestBasedResourceGroveler.GetNeutralResourcesLanguage(MainAssembly, ref _fallbackLoc); } @@ -419,14 +427,7 @@ namespace System.Resources { get { - if (_bUsingModernResourceManagement) - { - throw new PlatformNotSupportedException(SR.Format(SR.PlatformNotSupported_ResourceManager_ResWFileUnsupportedProperty, nameof(BaseName))); - } - else - { - return BaseNameField; - } + return BaseNameField; } } @@ -434,28 +435,8 @@ namespace System.Resources // GetString or GetObject. public virtual bool IgnoreCase { - get - { - if (_bUsingModernResourceManagement) - { - return false; - } - else - { - return _ignoreCase; - } - } - set - { - if (_bUsingModernResourceManagement) - { - throw new PlatformNotSupportedException(SR.Format(SR.PlatformNotSupported_ResourceManager_ResWFileUnsupportedProperty, nameof(IgnoreCase))); - } - else - { - _ignoreCase = value; - } - } + get { return _ignoreCase; } + set { _ignoreCase = value; } } // Returns the Type of the ResourceSet the ResourceManager uses @@ -464,14 +445,7 @@ namespace System.Resources { get { - if (_bUsingModernResourceManagement) - { - throw new PlatformNotSupportedException(SR.Format(SR.PlatformNotSupported_ResourceManager_ResWFileUnsupportedProperty, nameof(ResourceSetType))); - } - else - { - return (_userResourceSet == null) ? typeof(RuntimeResourceSet) : _userResourceSet; - } + return (_userResourceSet == null) ? typeof(RuntimeResourceSet) : _userResourceSet; } } @@ -479,25 +453,11 @@ namespace System.Resources { get { - if (_bUsingModernResourceManagement) - { - throw new PlatformNotSupportedException(SR.Format(SR.PlatformNotSupported_ResourceManager_ResWFileUnsupportedProperty, nameof(FallbackLocation))); - } - else - { - return _fallbackLoc; - } + return _fallbackLoc; } set { - if (_bUsingModernResourceManagement) - { - throw new PlatformNotSupportedException(SR.Format(SR.PlatformNotSupported_ResourceManager_ResWFileUnsupportedProperty, nameof(FallbackLocation))); - } - else - { - _fallbackLoc = value; - } + _fallbackLoc = value; } } @@ -547,9 +507,6 @@ namespace System.Resources // such as ".ResX", or a completely different format for naming files. protected virtual String GetResourceFileName(CultureInfo culture) { - if (_bUsingModernResourceManagement) - throw new PlatformNotSupportedException(SR.Format(SR.PlatformNotSupported_ResourceManager_ResWFileUnsupportedMethod, nameof(GetResourceFileName))); - StringBuilder sb = new StringBuilder(255); sb.Append(BaseNameField); // If this is the neutral culture, don't append culture name. @@ -634,9 +591,6 @@ namespace System.Resources // public virtual ResourceSet GetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents) { - if (_bUsingModernResourceManagement) - throw new PlatformNotSupportedException(SR.Format(SR.PlatformNotSupported_ResourceManager_ResWFileUnsupportedMethod, nameof(GetResourceSet))); - if (null == culture) throw new ArgumentNullException(nameof(culture)); @@ -672,9 +626,6 @@ namespace System.Resources // This will take a minimal number of locks. protected virtual ResourceSet InternalGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents) { - if (_bUsingModernResourceManagement) - throw new PlatformNotSupportedException(SR.Format(SR.PlatformNotSupported_ResourceManager_ResWFileUnsupportedMethod, nameof(InternalGetResourceSet))); - Dictionary localResourceSets = _resourceSets; ResourceSet rs = null; CultureInfo foundCulture = null; @@ -782,7 +733,7 @@ namespace System.Resources foreach (SatelliteContractVersionAttribute attr in attrs) { - Contract.Assert(v == null, "Cannot have multiple instances of SatelliteContractVersionAttribute on an assembly!"); + Debug.Assert(v == null, "Cannot have multiple instances of SatelliteContractVersionAttribute on an assembly!"); v = attr.Version; } @@ -794,7 +745,7 @@ namespace System.Resources Version ver; try { - ver = new Version(v); + ver = Version.Parse(v); } catch (ArgumentOutOfRangeException e) { @@ -804,7 +755,7 @@ namespace System.Resources // BCL directory. if (a == typeof(Object).GetTypeInfo().Assembly) { - Contract.Assert(false, System.CoreLib.Name + "'s SatelliteContractVersionAttribute is a malformed version string!"); + Debug.Fail(System.CoreLib.Name + "'s SatelliteContractVersionAttribute is a malformed version string!"); return null; } @@ -827,7 +778,7 @@ namespace System.Resources String typeName2, AssemblyName asmName2) { - Contract.Assert(asmTypeName1 != null, "asmTypeName1 was unexpectedly null"); + Debug.Assert(asmTypeName1 != null, "asmTypeName1 was unexpectedly null"); // First, compare type names int comma = asmTypeName1.IndexOf(','); @@ -883,9 +834,9 @@ namespace System.Resources #if FEATURE_APPX // Throws WinRT hresults private string GetStringFromPRI(String stringName, String startingCulture, String neutralResourcesCulture) { - Contract.Assert(_bUsingModernResourceManagement); - Contract.Assert(_WinRTResourceManager != null); - Contract.Assert(_PRIonAppXInitialized); + Debug.Assert(_bUsingModernResourceManagement); + Debug.Assert(_WinRTResourceManager != null); + Debug.Assert(_PRIonAppXInitialized); if (stringName.Length == 0) return null; @@ -926,13 +877,17 @@ namespace System.Resources [NonSerialized] private PRIExceptionInfo _PRIExceptionInfo; // Written only by SetAppXConfiguration - // When running under AppX, the toolchain stamped assemblies with embedded resources - // with HasEmbeddedStringResourcesAttribute + // If it is framework assembly we'll return true. the reason is in .NetNative we don't merge the resources to the app PRI file. + // The framework assemblies are tagged with attribute [assembly: AssemblyMetadata(".NETFrameworkAssembly", "")] private bool ShouldUseSatelliteAssemblyResourceLookupUnderAppX(Assembly resourcesAssembly) { - foreach (CustomAttributeData attrData in resourcesAssembly.CustomAttributes) + if (typeof(Object).Assembly == resourcesAssembly) + return true; + + foreach (var attrib in resourcesAssembly.GetCustomAttributes()) { - if (attrData.AttributeType.Equals(typeof(HasEmbeddedStringResourcesAttribute))) + AssemblyMetadataAttribute meta = attrib as AssemblyMetadataAttribute; + if (meta != null && meta.Key.Equals(".NETFrameworkAssembly")) { return true; } @@ -947,40 +902,38 @@ namespace System.Resources private void SetAppXConfiguration() { - Contract.Assert(_bUsingModernResourceManagement == false); // Only this function writes to this member + Debug.Assert(_bUsingModernResourceManagement == false); // Only this function writes to this member #if FEATURE_APPX - Contract.Assert(_WinRTResourceManager == null); // Only this function writes to this member - Contract.Assert(_PRIonAppXInitialized == false); // Only this function writes to this member - Contract.Assert(_PRIExceptionInfo == null); // Only this function writes to this member - - bool bUsingSatelliteAssembliesUnderAppX = false; + Debug.Assert(_WinRTResourceManager == null); // Only this function writes to this member + Debug.Assert(_PRIonAppXInitialized == false); // Only this function writes to this member + Debug.Assert(_PRIExceptionInfo == null); // Only this function writes to this member Assembly resourcesAssembly = MainAssembly; if (resourcesAssembly != null) { - s_IsAppXModel = true; - - // If we have the type information from the ResourceManager(Type) constructor, we use it. Otherwise, we use BaseNameField. - String reswFilename = _locationInfo == null ? BaseNameField : _locationInfo.FullName; - - // The only way this can happen is if a class inherited from ResourceManager and - // did not set the BaseNameField before calling the protected ResourceManager() constructor. - // For other constructors, we would already have thrown an ArgumentNullException by now. - // Throwing an ArgumentNullException now is not the right thing to do because technically - // ResourceManager() takes no arguments, and because it is not documented as throwing - // any exceptions. Instead, let's go through the rest of the initialization with this set to - // an empty string. We may in fact fail earlier for another reason, but otherwise we will - // throw a MissingManifestResourceException when GetString is called indicating that a - // resW filename called "" could not be found. - if (reswFilename == null) - reswFilename = String.Empty; - - WindowsRuntimeResourceManagerBase WRRM = null; - bool bWRRM_Initialized = false; - - if (!bUsingSatelliteAssembliesUnderAppX) +#if ENABLE_WINRT + WinRTInteropCallbacks callbacks = WinRTInterop.UnsafeCallbacks; + if (callbacks != null && callbacks.IsAppxModel()) + s_IsAppXModel = true; +#endif + if (s_IsAppXModel) { + // If we have the type information from the ResourceManager(Type) constructor, we use it. Otherwise, we use BaseNameField. + String reswFilename = _locationInfo == null ? BaseNameField : _locationInfo.FullName; + + // The only way this can happen is if a class inherited from ResourceManager and + // did not set the BaseNameField before calling the protected ResourceManager() constructor. + // For other constructors, we would already have thrown an ArgumentNullException by now. + // Throwing an ArgumentNullException now is not the right thing to do because technically + // ResourceManager() takes no arguments, and because it is not documented as throwing + // any exceptions. Instead, let's go through the rest of the initialization with this set to + // an empty string. We may in fact fail earlier for another reason, but otherwise we will + // throw a MissingManifestResourceException when GetString is called indicating that a + // resW filename called "" could not be found. + if (reswFilename == null) + reswFilename = String.Empty; + // See AssemblyNative::IsFrameworkAssembly for details on which kinds of assemblies are considered Framework assemblies. // The Modern Resource Manager is not used for such assemblies - they continue to use satellite assemblies (i.e. .resources.dll files). _bUsingModernResourceManagement = !ShouldUseSatelliteAssemblyResourceLookupUnderAppX(resourcesAssembly); @@ -989,9 +942,6 @@ namespace System.Resources { // Only now are we certain that we need the PRI file. - // Note that if IsAppXDesignMode is false, we haven't checked if the PRI file exists. - // This is by design. We will find out in the call to WindowsRuntimeResourceManager.Initialize below. - // At this point it is important NOT to set _bUsingModernResourceManagement to false // if the PRI file does not exist because we are now certain we need to load PRI // resources. We want to fail by throwing a MissingManifestResourceException @@ -1000,62 +950,61 @@ namespace System.Resources // the MissingManifestResourceException from this function, but from GetString. See the // comment below on the reason for this. - if (WRRM != null && bWRRM_Initialized) + _WinRTResourceManager = GetWinRTResourceManager(); + + try { - // Reuse the one successfully created earlier - _WinRTResourceManager = WRRM; - _PRIonAppXInitialized = true; + _PRIonAppXInitialized = _WinRTResourceManager.Initialize(reswFilename, out _PRIExceptionInfo); + + // Note that _PRIExceptionInfo might be null - this is OK. + // In that case we will just throw the generic + // MissingManifestResource_NoPRIresources exception. + // See the implementation of GetString for more details. } - else + // We would like to be able to throw a MissingManifestResourceException here if PRI resources + // could not be loaded for a recognized reason. However, the ResourceManager constructors + // that call SetAppXConfiguration are not documented as throwing MissingManifestResourceException, + // and since they are part of the portable profile, we cannot start throwing a new exception type + // as that would break existing portable libraries. Hence we must save the exception information + // now and throw the exception on the first call to GetString. + catch (FileNotFoundException) { - _WinRTResourceManager = GetWinRTResourceManager(); - - try - { - _PRIonAppXInitialized = _WinRTResourceManager.Initialize(reswFilename, out _PRIExceptionInfo); - - // Note that _PRIExceptionInfo might be null - this is OK. - // In that case we will just throw the generic - // MissingManifestResource_NoPRIresources exception. - // See the implementation of GetString for more details. - } - // We would like to be able to throw a MissingManifestResourceException here if PRI resources - // could not be loaded for a recognized reason. However, the ResourceManager constructors - // that call SetAppXConfiguration are not documented as throwing MissingManifestResourceException, - // and since they are part of the portable profile, we cannot start throwing a new exception type - // as that would break existing portable libraries. Hence we must save the exception information - // now and throw the exception on the first call to GetString. - catch (FileNotFoundException) - { - // We will throw MissingManifestResource_NoPRIresources from GetString - // when we see that _PRIonAppXInitialized is false. - } - catch (Exception e) - { - // ERROR_MRM_MAP_NOT_FOUND can be thrown by the call to ResourceManager.get_AllResourceMaps - // in WindowsRuntimeResourceManager.Initialize. - // In this case _PRIExceptionInfo is now null and we will just throw the generic - // MissingManifestResource_NoPRIresources exception. - // See the implementation of GetString for more details. - if (e.HResult != __HResults.ERROR_MRM_MAP_NOT_FOUND) - throw; // Unexpected exception code. Bubble it up to the caller. - } - // Allow all other exception types to bubble up to the caller. - - // Yes, this causes us to potentially throw exception types that are not documented. - - // Ultimately the tradeoff is the following: - // -We could ignore unknown exceptions or rethrow them as inner exceptions - // of exceptions that the ResourceManager class is already documented as throwing. - // This would allow existing portable libraries to gracefully recover if they don't care - // too much about the ResourceManager object they are using. However it could - // mask potentially fatal errors that we are not aware of, such as a disk drive failing. - - - // The alternative, which we chose, is to throw unknown exceptions. This may tear - // down the process if the portable library and app don't expect this exception type. - // On the other hand, this won't mask potentially fatal errors we don't know about. + // We will throw MissingManifestResource_NoPRIresources from GetString + // when we see that _PRIonAppXInitialized is false. } + catch (Exception e) + { + // ERROR_MRM_MAP_NOT_FOUND can be thrown by the call to ResourceManager.get_AllResourceMaps + // in WindowsRuntimeResourceManager.Initialize. + // In this case _PRIExceptionInfo is now null and we will just throw the generic + // MissingManifestResource_NoPRIresources exception. + // See the implementation of GetString for more details. + if (e.HResult != HResults.ERROR_MRM_MAP_NOT_FOUND) + throw; // Unexpected exception code. Bubble it up to the caller. + } + + if (!_PRIonAppXInitialized) + { + // if we couldn't find resources in the PRI, we fallback using the managed resources. + // if try to get resources that no existing, we'll throw anyway. + _bUsingModernResourceManagement = false; + } + + // Allow all other exception types to bubble up to the caller. + + // Yes, this causes us to potentially throw exception types that are not documented. + + // Ultimately the tradeoff is the following: + // -We could ignore unknown exceptions or rethrow them as inner exceptions + // of exceptions that the ResourceManager class is already documented as throwing. + // This would allow existing portable libraries to gracefully recover if they don't care + // too much about the ResourceManager object they are using. However it could + // mask potentially fatal errors that we are not aware of, such as a disk drive failing. + + + // The alternative, which we chose, is to throw unknown exceptions. This may tear + // down the process if the portable library and app don't expect this exception type. + // On the other hand, this won't mask potentially fatal errors we don't know about. } } } @@ -1082,7 +1031,6 @@ namespace System.Resources { if (null == name) throw new ArgumentNullException(nameof(name)); - Contract.EndContractBlock(); // Recursion guard so framework resource lookups can't stack overflow if (ts_recursionCount > 10) @@ -1193,9 +1141,6 @@ namespace System.Resources // public virtual Object GetObject(String name) { - if (_bUsingModernResourceManagement) - throw new PlatformNotSupportedException(SR.Format(SR.PlatformNotSupported_ResourceManager_ResWFileUnsupportedMethod, nameof(GetObject))); - return GetObject(name, (CultureInfo)null, true); } @@ -1204,9 +1149,6 @@ namespace System.Resources // Returns null if the resource wasn't found. public virtual Object GetObject(String name, CultureInfo culture) { - if (_bUsingModernResourceManagement) - throw new PlatformNotSupportedException(SR.Format(SR.PlatformNotSupported_ResourceManager_ResWFileUnsupportedMethod, nameof(GetObject))); - return GetObject(name, culture, true); } @@ -1214,7 +1156,6 @@ namespace System.Resources { if (null == name) throw new ArgumentNullException(nameof(name)); - Contract.EndContractBlock(); #if FEATURE_APPX if(s_IsAppXModel) @@ -1293,17 +1234,11 @@ namespace System.Resources public UnmanagedMemoryStream GetStream(String name) { - if (_bUsingModernResourceManagement) - throw new PlatformNotSupportedException(SR.Format(SR.PlatformNotSupported_ResourceManager_ResWFileUnsupportedMethod, nameof(GetStream))); - return GetStream(name, (CultureInfo)null); } public UnmanagedMemoryStream GetStream(String name, CultureInfo culture) { - if (_bUsingModernResourceManagement) - throw new PlatformNotSupportedException(SR.Format(SR.PlatformNotSupported_ResourceManager_ResWFileUnsupportedMethod, nameof(GetStream))); - Object obj = GetObject(name, culture, false); UnmanagedMemoryStream ums = obj as UnmanagedMemoryStream; if (ums == null && obj != null) diff --git a/external/corert/src/System.Private.CoreLib/src/System/Resources/ResourceReader.cs b/external/corert/src/System.Private.CoreLib/src/System/Resources/ResourceReader.cs index 7bf3e3d2c7..96fea18f60 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Resources/ResourceReader.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Resources/ResourceReader.cs @@ -15,18 +15,18 @@ ** ===========================================================*/ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Runtime.Versioning; +using System.Text; + namespace System.Resources { - using System; - using System.IO; - using System.Text; - using System.Collections; - using System.Collections.Generic; - using System.Reflection; - using System.Globalization; - using System.Runtime.Versioning; - using System.Diagnostics.Contracts; - // Provides the default implementation of IResourceReader, reading // .resources file from the system default binary format. This class // can be treated as an enumerator once. @@ -62,7 +62,7 @@ namespace System.Resources internal static bool CanCache(ResourceTypeCode value) { - Contract.Assert(value >= 0, "negative ResourceTypeCode. What?"); + Debug.Assert(value >= 0, "negative ResourceTypeCode. What?"); return value <= ResourceTypeCode.LastPrimitive; } } @@ -128,7 +128,6 @@ namespace System.Resources throw new ArgumentNullException(nameof(stream)); if (!stream.CanRead) throw new ArgumentException(SR.Argument_StreamNotReadable); - Contract.EndContractBlock(); _resCache = new Dictionary(FastResourceComparer.Default); _store = new BinaryReader(stream, Encoding.UTF8); @@ -144,9 +143,9 @@ namespace System.Resources // and values, coupled to this ResourceReader). internal ResourceReader(Stream stream, Dictionary resCache) { - Contract.Requires(stream != null, "Need a stream!"); - Contract.Requires(stream.CanRead, "Stream should be readable!"); - Contract.Requires(resCache != null, "Need a Dictionary!"); + Debug.Assert(stream != null, "Need a stream!"); + Debug.Assert(stream.CanRead, "Stream should be readable!"); + Debug.Assert(resCache != null, "Need a Dictionary!"); _resCache = resCache; _store = new BinaryReader(stream, Encoding.UTF8); @@ -238,8 +237,8 @@ namespace System.Resources private unsafe int GetNameHash(int index) { - Contract.Assert(index >= 0 && index < _numResources, "Bad index into hash array. index: " + index); - Contract.Assert((_ums == null && _nameHashes != null && _nameHashesPtr == null) || + Debug.Assert(index >= 0 && index < _numResources, "Bad index into hash array. index: " + index); + Debug.Assert((_ums == null && _nameHashes != null && _nameHashesPtr == null) || (_ums != null && _nameHashes == null && _nameHashesPtr != null), "Internal state mangled."); if (_ums == null) return _nameHashes[index]; @@ -249,8 +248,8 @@ namespace System.Resources private unsafe int GetNamePosition(int index) { - Contract.Assert(index >= 0 && index < _numResources, "Bad index into name position array. index: " + index); - Contract.Assert((_ums == null && _namePositions != null && _namePositionsPtr == null) || + Debug.Assert(index >= 0 && index < _numResources, "Bad index into name position array. index: " + index); + Debug.Assert((_ums == null && _namePositions != null && _namePositionsPtr == null) || (_ums != null && _namePositions == null && _namePositionsPtr != null), "Internal state mangled."); int r; if (_ums == null) @@ -287,7 +286,7 @@ namespace System.Resources // This does a binary search through the names. internal int FindPosForResource(String name) { - Contract.Assert(_store != null, "ResourceReader is closed!"); + Debug.Assert(_store != null, "ResourceReader is closed!"); int hash = FastResourceComparer.HashFunction(name); // Binary search over the hashes. Use the _namePositions array to // determine where they exist in the underlying stream. @@ -367,7 +366,7 @@ namespace System.Resources // so no one else can cause us to seek in the stream. private unsafe bool CompareStringEqualsName(String name) { - Contract.Assert(_store != null, "ResourceReader is closed!"); + Debug.Assert(_store != null, "ResourceReader is closed!"); int byteLen = Read7BitEncodedInt(); if (byteLen < 0) { @@ -409,7 +408,7 @@ namespace System.Resources // index. The parameter is NOT a virtual offset. private unsafe String AllocateStringForNameIndex(int index, out int dataOffset) { - Contract.Assert(_store != null, "ResourceReader is closed!"); + Debug.Assert(_store != null, "ResourceReader is closed!"); byte[] bytes; int byteLen; long nameVA = GetNamePosition(index); @@ -467,7 +466,7 @@ namespace System.Resources // index. The parameter is NOT a virtual offset. private Object GetValueForNameIndex(int index) { - Contract.Assert(_store != null, "ResourceReader is closed!"); + Debug.Assert(_store != null, "ResourceReader is closed!"); long nameVA = GetNamePosition(index); lock (this) { @@ -493,7 +492,7 @@ namespace System.Resources // no one can cause us to do a seek in here. internal String LoadString(int pos) { - Contract.Assert(_store != null, "ResourceReader is closed!"); + Debug.Assert(_store != null, "ResourceReader is closed!"); _store.BaseStream.Seek(_dataSectionOffset + pos, SeekOrigin.Begin); String s = null; int typeIndex = Read7BitEncodedInt(); @@ -549,8 +548,8 @@ namespace System.Resources // no one can cause us to do a seek in here. internal Object LoadObjectV1(int pos) { - Contract.Assert(_store != null, "ResourceReader is closed!"); - Contract.Assert(_version == 1, ".resources file was not a V1 .resources file!"); + Debug.Assert(_store != null, "ResourceReader is closed!"); + Debug.Assert(_version == 1, ".resources file was not a V1 .resources file!"); try { @@ -624,8 +623,8 @@ namespace System.Resources internal Object LoadObjectV2(int pos, out ResourceTypeCode typeCode) { - Contract.Assert(_store != null, "ResourceReader is closed!"); - Contract.Assert(_version >= 2, ".resources file was not a V2 (or higher) .resources file!"); + Debug.Assert(_store != null, "ResourceReader is closed!"); + Debug.Assert(_version >= 2, ".resources file was not a V2 (or higher) .resources file!"); try { @@ -729,7 +728,7 @@ namespace System.Resources byte[] bytes = new byte[len]; int r = _ums.Read(bytes, 0, len); - Contract.Assert(r == len, "ResourceReader needs to use a blocking read here. (Call _store.ReadBytes(len)?)"); + Debug.Assert(r == len, "ResourceReader needs to use a blocking read here. (Call _store.ReadBytes(len)?)"); return bytes; } @@ -778,7 +777,7 @@ namespace System.Resources // for the default resource file format. private void ReadResources() { - Contract.Assert(_store != null, "ResourceReader is closed!"); + Debug.Assert(_store != null, "ResourceReader is closed!"); try { @@ -992,7 +991,7 @@ namespace System.Resources _store.BaseStream.Position = oldPos; } } - Contract.Assert(_typeTable[typeIndex] != null, "Should have found a type!"); + Debug.Assert(_typeTable[typeIndex] != null, "Should have found a type!"); return _typeTable[typeIndex]; } @@ -1000,7 +999,6 @@ namespace System.Resources { if (resourceName == null) throw new ArgumentNullException(nameof(resourceName)); - Contract.EndContractBlock(); if (_resCache == null) throw new InvalidOperationException(SR.ResourceReaderIsClosed); @@ -1039,10 +1037,10 @@ namespace System.Resources Array.Sort(sortedDataPositions); int index = Array.BinarySearch(sortedDataPositions, dataPos); - Contract.Assert(index >= 0 && index < _numResources, "Couldn't find data position within sorted data positions array!"); + Debug.Assert(index >= 0 && index < _numResources, "Couldn't find data position within sorted data positions array!"); long nextData = (index < _numResources - 1) ? sortedDataPositions[index + 1] + _dataSectionOffset : _store.BaseStream.Length; int len = (int)(nextData - (dataPos + _dataSectionOffset)); - Contract.Assert(len >= 0 && len <= (int)_store.BaseStream.Length - dataPos + _dataSectionOffset, "Length was negative or outside the bounds of the file!"); + Debug.Assert(len >= 0 && len <= (int)_store.BaseStream.Length - dataPos + _dataSectionOffset, "Length was negative or outside the bounds of the file!"); // Read type code then byte[] _store.BaseStream.Position = _dataSectionOffset + dataPos; @@ -1065,16 +1063,16 @@ namespace System.Resources private String TypeNameFromTypeCode(ResourceTypeCode typeCode) { - Contract.Requires(typeCode >= 0, "can't be negative"); + Debug.Assert(typeCode >= 0, "can't be negative"); if (typeCode < ResourceTypeCode.StartOfUserTypes) { - Contract.Assert(!String.Equals(typeCode.ToString(), "LastPrimitive"), "Change ResourceTypeCode metadata order so LastPrimitive isn't what Enum.ToString prefers."); + Debug.Assert(!String.Equals(typeCode.ToString(), "LastPrimitive"), "Change ResourceTypeCode metadata order so LastPrimitive isn't what Enum.ToString prefers."); return "ResourceTypeCode." + typeCode.ToString(); } else { int typeIndex = typeCode - ResourceTypeCode.StartOfUserTypes; - Contract.Assert(typeIndex >= 0 && typeIndex < _typeTable.Length, "TypeCode is broken or corrupted!"); + Debug.Assert(typeIndex >= 0 && typeIndex < _typeTable.Length, "TypeCode is broken or corrupted!"); long oldPos = _store.BaseStream.Position; try { @@ -1209,4 +1207,4 @@ namespace System.Resources } } } -} \ No newline at end of file +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Resources/ResourceSet.cs b/external/corert/src/System.Private.CoreLib/src/System/Resources/ResourceSet.cs index 97c3771737..5b1fd99a7a 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Resources/ResourceSet.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Resources/ResourceSet.cs @@ -21,7 +21,6 @@ using System.Runtime.InteropServices; using System.Reflection; using System.Runtime.Serialization; using System.Runtime.Versioning; -using System.Diagnostics.Contracts; using System.Collections.Generic; namespace System.Resources @@ -32,7 +31,6 @@ namespace System.Resources // enumerates over an IResourceReader, loading every name and value, and // stores them in a hash table. Custom IResourceReaders can be used. // - [Serializable] public class ResourceSet : IDisposable, IEnumerable { [NonSerialized] @@ -80,7 +78,6 @@ namespace System.Resources { if (reader == null) throw new ArgumentNullException(nameof(reader)); - Contract.EndContractBlock(); Reader = reader; CommonInit(); ReadResources(); @@ -237,7 +234,6 @@ namespace System.Resources { if (name == null) throw new ArgumentNullException("name"); - Contract.EndContractBlock(); Dictionary copyOfTable = _table; // Avoid a race with Dispose diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CommandLine.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/CommandLine.Windows.cs index 73ffd6af68..7756a0bf2c 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CommandLine.Windows.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/CommandLine.Windows.cs @@ -14,22 +14,24 @@ namespace System.Runtime internal static class CommandLine { + [System.Diagnostics.DebuggerHidden] [NativeCallable(EntryPoint="InvokeExeMain", CallingConvention = CallingConvention.Cdecl)] public unsafe static int InvokeExeMain(IntPtr pfnUserMain) { string[] commandLine = InternalCreateCommandLine(); return RawCalliHelper.Call(pfnUserMain, commandLine); } - - [RuntimeExport("CreateCommandLine")] - public static unsafe string[] InternalCreateCommandLine() - { - char * pCmdLine = Interop.mincore.GetCommandLine(); - int nArgs = SegmentCommandLine(pCmdLine, null); + [RuntimeExport("CreateCommandLine")] + public static string[] InternalCreateCommandLine() => InternalCreateCommandLine(includeArg0: false); + + internal static unsafe string[] InternalCreateCommandLine(bool includeArg0) + { + char* pCmdLine = Interop.mincore.GetCommandLine(); + int nArgs = SegmentCommandLine(pCmdLine, null, includeArg0); string[] argArray = new string[nArgs]; - SegmentCommandLine(pCmdLine, argArray); + SegmentCommandLine(pCmdLine, argArray, includeArg0); return argArray; } @@ -40,37 +42,32 @@ namespace System.Runtime // // This functions interface mimics the CommandLineToArgvW api. // - private static unsafe int SegmentCommandLine(char * pCmdLine, string[] argArray) + private static unsafe int SegmentCommandLine(char * pCmdLine, string[] argArray, bool includeArg0) { int nArgs = 0; - char c; - bool inquote; - - // First, parse the program name (argv[0]). Argv[0] is parsed under special rules. Anything up to - // the first whitespace outside a quoted subtring is accepted. Backslashes are treated as normal - // characters. char* psrc = pCmdLine; - inquote = false; - do { - if (*psrc == '"') + // First, parse the program name (argv[0]). Argv[0] is parsed under special rules. Anything up to + // the first whitespace outside a quoted subtring is accepted. Backslashes are treated as normal + // characters. + char* psrcOrig = psrc; + + int arg0Len = ScanArgument0(ref psrc, null); + if (includeArg0) { - inquote = !inquote; - c = *psrc++; - continue; + if (argArray != null) + { + char[] arg0 = new char[arg0Len]; + ScanArgument0(ref psrcOrig, arg0); + argArray[nArgs] = new string(arg0); + } + nArgs++; } - - c = *psrc++; - } while ((c != '\0' && (inquote || (c != ' ' && c != '\t')))); - - if (c == '\0') - { - psrc--; } - inquote = false; + bool inquote = false; // loop on each argument for (;;) @@ -106,6 +103,38 @@ namespace System.Runtime return nArgs; } + private static unsafe int ScanArgument0(ref char* psrc, char[] arg) + { + // Argv[0] is parsed under special rules. Anything up to + // the first whitespace outside a quoted subtring is accepted. Backslashes are treated as normal + // characters. + int charIdx = 0; + bool inquote = false; + for (;;) + { + char c = *psrc++; + if (c == '"') + { + inquote = !inquote; + continue; + } + + if (c == '\0' || (!inquote && (c == ' ' || c == '\t'))) + { + psrc--; + break; + } + + if (arg != null) + { + arg[charIdx] = c; + } + charIdx++; + } + + return charIdx; + } + private static unsafe int ScanArgument(ref char* psrc, ref bool inquote, char[] arg) { int charIdx = 0; diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs index 74b1a1b77b..3cabd25e67 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs @@ -12,12 +12,12 @@ // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.ExceptionServices; using System.Threading; using System.Threading.Tasks; using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; using AsyncStatus = Internal.Runtime.Augments.AsyncStatus; using CausalityRelation = Internal.Runtime.Augments.CausalityRelation; @@ -343,7 +343,6 @@ namespace System.Runtime.CompilerServices internal static void SetException(Task task, Exception exception) { if (exception == null) throw new ArgumentNullException(nameof(exception)); - Contract.EndContractBlock(); // If the exception represents cancellation, cancel the task. Otherwise, fault the task. var oce = exception as OperationCanceledException; @@ -587,17 +586,13 @@ namespace System.Runtime.CompilerServices /// /// The result for which we need a task. /// The completed task containing the result. - private static Task GetTaskForResult(TResult result) + internal static Task GetTaskForResult(TResult result) { // Currently NUTC does not perform the optimization needed by this method. The result is that // every call to this method results in quite a lot of work, including many allocations, which // is the opposite of the intent. For now, let's just return a new Task each time. // Bug 719350 tracks re-optimizing this in ProjectN. #if false - Contract.Ensures( - EqualityComparer.Default.Equals(result, Contract.Result>().Result), - "The returned task's Result must return the same value as the specified result value."); - // The goal of this function is to be give back a cached task if possible, // or to otherwise give back a new task. To give back a cached task, // we need to be able to evaluate the incoming result value, and we need @@ -869,7 +864,6 @@ namespace System.Runtime.CompilerServices // if (stateMachine == null) throw new ArgumentNullException(nameof(stateMachine)); - Contract.EndContractBlock(); if (cachedMoveNextAction == null) throw new InvalidOperationException(SR.AsyncMethodBuilder_InstanceNotInitialized); Action unwrappedMoveNextAction = TryGetStateMachineForDebugger(cachedMoveNextAction); @@ -911,7 +905,6 @@ namespace System.Runtime.CompilerServices internal static void ThrowAsync(Exception exception, SynchronizationContext targetContext) { if (exception == null) throw new ArgumentNullException(nameof(exception)); - Contract.EndContractBlock(); // If the user supplied a SynchronizationContext... if (targetContext != null) diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastableObject.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastableObject.cs index 512bf20e40..4b723113c9 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastableObject.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastableObject.cs @@ -20,6 +20,7 @@ namespace System.Runtime.CompilerServices public abstract class CastableObject : ICastableObject { // THIS FIELD IS USED BY THE RUNTIME DIRECTLY! IT MUST NOT BE REMOVED BY THE REDUCER + [System.Diagnostics.DebuggerBrowsable(Diagnostics.DebuggerBrowsableState.Never)] private object _hiddenCacheField; object ICastableObject.CastToInterface(EETypePtr interfaceType, bool produceCastErrorException, out Exception castError) diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs index 44dfdeea0e..51abcf771f 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs @@ -30,7 +30,7 @@ namespace System.Runtime.CompilerServices // // No attempt is made to detect or break deadlocks due to other synchronization mechanisms. //============================================================================================================== -#if !CORERT +#if PROJECTN [RuntimeExport("CheckStaticClassConstruction")] public static unsafe void* CheckStaticClassConstruction(void* returnValue, StaticClassConstructionContext* pContext) { diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs index 97e7cee910..562c195db4 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs @@ -8,6 +8,8 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.Threading; +using Internal.Runtime.CompilerServices; + namespace System.Runtime.CompilerServices { #region ConditionalWeakTable @@ -375,53 +377,6 @@ namespace System.Runtime.CompilerServices #endregion - #region internal members - - //-------------------------------------------------------------------------------------------- - // Find a key that equals (value equality) with the given key - don't use in perf critical path - // Note that it calls out to Object.Equals which may calls the override version of Equals - // and that may take locks and leads to deadlock - // Currently it is only used by WinRT event code and you should only use this function - // if you know for sure that either you won't run into dead locks or you need to live with the - // possiblity - //-------------------------------------------------------------------------------------------- - internal TKey FindEquivalentKeyUnsafe(TKey key, out TValue value) - { - using (LockHolder.Hold(_lock)) - { - return _container.FindEquivalentKeyUnsafe(key, out value); - } - } - - //-------------------------------------------------------------------------------------------- - // Returns a collection of keys - don't use in perf critical path - //-------------------------------------------------------------------------------------------- - internal ICollection Keys - { - get - { - using (LockHolder.Hold(_lock)) - { - return _container.Keys; - } - } - } - - //-------------------------------------------------------------------------------------------- - // Returns a collection of values - don't use in perf critical path - //-------------------------------------------------------------------------------------------- - internal ICollection Values - { - get - { - using (LockHolder.Hold(_lock)) - { - return _container.Values; - } - } - } - #endregion - #region Private Members //---------------------------------------------------------------------------------------- @@ -824,86 +779,6 @@ namespace System.Runtime.CompilerServices return newContainer; } - internal ICollection Keys - { - get - { - LowLevelListWithIList list = new LowLevelListWithIList(); - - for (int bucket = 0; bucket < _buckets.Length; ++bucket) - { - for (int entriesIndex = _buckets[bucket]; entriesIndex != -1; entriesIndex = _entries[entriesIndex].next) - { - TKey thisKey = Unsafe.As(_entries[entriesIndex].depHnd.GetPrimary()); - if (thisKey != null) - { - list.Add(thisKey); - } - } - } - - GC.KeepAlive(this); // ensure we don't get finalized while accessing DependentHandles. - return list; - } - } - - internal ICollection Values - { - get - { - LowLevelListWithIList list = new LowLevelListWithIList(); - - for (int bucket = 0; bucket < _buckets.Length; ++bucket) - { - for (int entriesIndex = _buckets[bucket]; entriesIndex != -1; entriesIndex = _entries[entriesIndex].next) - { - Object primary = null; - Object secondary = null; - - primary = _entries[entriesIndex].depHnd.GetPrimaryAndSecondary(out secondary); - - // Now that we've secured a strong reference to the secondary, must check the primary again - // to ensure it didn't expire (otherwise, we open a race where TryGetValue misreports an - // expired key as a live key with a null value.) - if (primary != null) - { - list.Add(Unsafe.As(secondary)); - } - } - } - - GC.KeepAlive(this); // ensure we don't get finalized while accessing DependentHandles. - return list; - } - } - - internal TKey FindEquivalentKeyUnsafe(TKey key, out TValue value) - { - for (int bucket = 0; bucket < _buckets.Length; ++bucket) - { - for (int entriesIndex = _buckets[bucket]; entriesIndex != -1; entriesIndex = _entries[entriesIndex].next) - { - if (_entries[entriesIndex].hashCode == -1) - { - continue; // removed entry whose handle is awaiting condemnation by the finalizer. - } - - object thisKey, thisValue; - thisKey = _entries[entriesIndex].depHnd.GetPrimaryAndSecondary(out thisValue); - if (Object.Equals(thisKey, key)) - { - GC.KeepAlive(this); // ensure we don't get finalized while accessing DependentHandles. - value = Unsafe.As(thisValue); - return Unsafe.As(thisKey); - } - } - } - - GC.KeepAlive(this); // ensure we don't get finalized while accessing DependentHandles. - value = default(TValue); - return null; - } - //---------------------------------------------------------------------------------------- // Precondition: // Must hold _lock. diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/DependencyReductionTypeRemoved.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/DependencyReductionTypeRemoved.cs index 39f8430e3c..436069fc3f 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/DependencyReductionTypeRemoved.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/DependencyReductionTypeRemoved.cs @@ -16,7 +16,7 @@ namespace System.Runtime.CompilerServices { public DependencyReductionTypeRemoved() { - Debug.Assert(false, "A type that was removed by dependency reduction has been instantiated."); + Debug.Fail("A type that was removed by dependency reduction has been instantiated."); throw new Exception("A type that was removed by dependency reduction has been instantiated."); } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/InternalCompilerAttributes.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/InternalCompilerAttributes.cs index 2de06e4874..90451a69fc 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/InternalCompilerAttributes.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/InternalCompilerAttributes.cs @@ -7,10 +7,6 @@ using System.Runtime.CompilerServices; namespace System.Runtime.CompilerServices { - // This attribute is only for use in a Class Library - [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field)] - internal sealed class IntrinsicAttribute : Attribute { } - #if PROJECTN [AttributeUsage(AttributeTargets.Field)] internal sealed class BoundAttribute : Attribute { } @@ -18,7 +14,4 @@ namespace System.Runtime.CompilerServices [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] public sealed class BoundsCheckingAttribute : Attribute { } #endif - - [AttributeUsage(AttributeTargets.Struct)] - public sealed class StackOnlyAttribute : Attribute { } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs index b5312b6011..d5c425a3ba 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -157,6 +157,9 @@ namespace System.Runtime.CompilerServices public static int OffsetToStringData { + // Workaround to allow WebAssembly to define a size here without a special CoreLib build + // https://github.com/dotnet/corert/issues/4506 includes removing this. + [Intrinsic] get { // Number of bytes from the address pointed to by a reference to @@ -219,6 +222,13 @@ namespace System.Runtime.CompilerServices return !pEEType.IsValueType || pEEType.HasPointers; } + [Intrinsic] + public static bool IsReference() + { + var pEEType = EETypePtr.EETypePtrOf(); + return !pEEType.IsValueType; + } + // Constrained Execution Regions APIs are NOP's because we do not support CERs in .NET Core at all. public static void ProbeForSufficientStack() { } public static void PrepareConstrainedRegions() { } @@ -271,7 +281,7 @@ namespace System.Runtime.CompilerServices throw new SerializationException(SR.Format(SR.Serialization_InvalidType, type.ToString())); } - if (type.IsArray || type.IsByRef || type.IsPointer) + if (type.HasElementType || type.IsGenericParameter) { throw new ArgumentException(SR.Argument_InvalidValue); } @@ -305,7 +315,7 @@ namespace System.Runtime.CompilerServices if (eeTypePtr.IsNullable) { - return GetUninitializedObject(ReflectionCoreNonPortable.GetRuntimeTypeForEEType(eeTypePtr.NullableType)); + return GetUninitializedObject(RuntimeTypeUnifier.GetRuntimeTypeForEEType(eeTypePtr.NullableType)); } // Triggering the .cctor here is slightly different than desktop/CoreCLR, which diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/TaskAwaiter.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/TaskAwaiter.cs index 565dfd362d..33b5b0b26b 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/TaskAwaiter.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/TaskAwaiter.cs @@ -54,7 +54,7 @@ namespace System.Runtime.CompilerServices { /// Provides an awaiter for awaiting a . /// This type is intended for compiler use only. - public struct TaskAwaiter : ICriticalNotifyCompletion + public readonly struct TaskAwaiter : ICriticalNotifyCompletion { /// The task being awaited. private readonly Task m_task; @@ -99,6 +99,7 @@ namespace System.Runtime.CompilerServices /// The awaiter was not properly initialized. /// The task was canceled. /// The task completed in a Faulted state. + [StackTraceHidden] public void GetResult() { ValidateEnd(m_task); @@ -109,6 +110,7 @@ namespace System.Runtime.CompilerServices /// prior to completing the await. /// /// The awaited task. + [StackTraceHidden] internal static void ValidateEnd(Task task) { // Fast checks that can be inlined. @@ -125,6 +127,7 @@ namespace System.Runtime.CompilerServices /// the await on the task, and throws an exception if the task did not complete successfully. /// /// The awaited task. + [StackTraceHidden] private static void HandleNonSuccessAndDebuggerNotification(Task task) { // NOTE: The JIT refuses to inline ValidateEnd when it contains the contents @@ -148,6 +151,7 @@ namespace System.Runtime.CompilerServices } /// Throws an exception to handle a task that completed in a state other than RanToCompletion. + [StackTraceHidden] private static void ThrowForNonSuccess(Task task) { Debug.Assert(task.IsCompleted, "Task must have been completed by now."); @@ -165,7 +169,7 @@ namespace System.Runtime.CompilerServices if (oceEdi != null) { oceEdi.Throw(); - Debug.Assert(false, "Throw() should have thrown"); + Debug.Fail("Throw() should have thrown"); } throw new TaskCanceledException(task); @@ -176,12 +180,12 @@ namespace System.Runtime.CompilerServices if (edis.Count > 0) { edis[0].Throw(); - Debug.Assert(false, "Throw() should have thrown"); + Debug.Fail("Throw() should have thrown"); break; // Necessary to compile: non-reachable, but compiler can't determine that } else { - Debug.Assert(false, "There should be exceptions if we're Faulted."); + Debug.Fail("There should be exceptions if we're Faulted."); throw task.Exception; } } @@ -253,7 +257,7 @@ namespace System.Runtime.CompilerServices /// Provides an awaiter for awaiting a . /// This type is intended for compiler use only. - public struct TaskAwaiter : ICriticalNotifyCompletion + public readonly struct TaskAwaiter : ICriticalNotifyCompletion { /// The task being awaited. private readonly Task m_task; @@ -299,6 +303,7 @@ namespace System.Runtime.CompilerServices /// The awaiter was not properly initialized. /// The task was canceled. /// The task completed in a Faulted state. + [StackTraceHidden] public TResult GetResult() { TaskAwaiter.ValidateEnd(m_task); @@ -308,7 +313,7 @@ namespace System.Runtime.CompilerServices /// Provides an awaitable object that allows for configured awaits on . /// This type is intended for compiler use only. - public struct ConfiguredTaskAwaitable + public readonly struct ConfiguredTaskAwaitable { /// The task being awaited. private readonly ConfiguredTaskAwaitable.ConfiguredTaskAwaiter m_configuredTaskAwaiter; @@ -333,7 +338,7 @@ namespace System.Runtime.CompilerServices /// Provides an awaiter for a . /// This type is intended for compiler use only. - public struct ConfiguredTaskAwaiter : ICriticalNotifyCompletion + public readonly struct ConfiguredTaskAwaiter : ICriticalNotifyCompletion { /// The task being awaited. private readonly Task m_task; @@ -386,6 +391,7 @@ namespace System.Runtime.CompilerServices /// The awaiter was not properly initialized. /// The task was canceled. /// The task completed in a Faulted state. + [StackTraceHidden] public void GetResult() { TaskAwaiter.ValidateEnd(m_task); @@ -395,7 +401,7 @@ namespace System.Runtime.CompilerServices /// Provides an awaitable object that allows for configured awaits on . /// This type is intended for compiler use only. - public struct ConfiguredTaskAwaitable + public readonly struct ConfiguredTaskAwaitable { /// The underlying awaitable on whose logic this awaitable relies. private readonly ConfiguredTaskAwaitable.ConfiguredTaskAwaiter m_configuredTaskAwaiter; @@ -419,7 +425,7 @@ namespace System.Runtime.CompilerServices /// Provides an awaiter for a . /// This type is intended for compiler use only. - public struct ConfiguredTaskAwaiter : ICriticalNotifyCompletion + public readonly struct ConfiguredTaskAwaiter : ICriticalNotifyCompletion { /// The task being awaited. private readonly Task m_task; @@ -471,6 +477,7 @@ namespace System.Runtime.CompilerServices /// The awaiter was not properly initialized. /// The task was canceled. /// The task completed in a Faulted state. + [StackTraceHidden] public TResult GetResult() { TaskAwaiter.ValidateEnd(m_task); diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs deleted file mode 100644 index 4c38b38938..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs +++ /dev/null @@ -1,171 +0,0 @@ -// 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.Runtime.Versioning; - -#if BIT64 -using nint = System.Int64; -using nuint = System.UInt64; -#else -using nint = System.Int32; -using nuint = System.UInt32; -#endif - -namespace System.Runtime.CompilerServices -{ - // - // Subsetted clone of System.Runtime.CompilerServices.Unsafe for internal runtime use. - // Keep in sync with https://github.com/dotnet/corefx/tree/master/src/System.Runtime.CompilerServices.Unsafe. - // - - /// - /// Contains generic, low-level functionality for manipulating pointers. - /// - [CLSCompliant(false)] - public static class Unsafe - { - /// - /// Reads a value of type from the given location. - /// - [NonVersionable] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe T Read(void* source) - { - return Unsafe.As(ref *(byte*)source); - } - - /// - /// Writes a value of type to the given location. - /// - [NonVersionable] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void Write(void* source, T value) - { - Unsafe.As(ref *(byte*)source) = value; - } - - /// - /// Returns a pointer to the given by-ref parameter. - /// - [Intrinsic] - [NonVersionable] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void* AsPointer(ref T source) - { - // This method is implemented by the toolchain - throw new PlatformNotSupportedException(); - - // ldarg.0 - // conv.u - // ret - } - - /// - /// Returns the size of an object of the given type parameter. - /// - [Intrinsic] - [NonVersionable] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int SizeOf() - { - // This method is implemented by the toolchain - throw new PlatformNotSupportedException(); - - // sizeof !!0 - // ret - } - - /// - /// Casts the given object to the specified type, performs no dynamic type checking. - /// - [Intrinsic] - [NonVersionable] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T As(Object value) where T : class - { - // This method is implemented by the toolchain - throw new PlatformNotSupportedException(); - - // ldarg.0 - // ret - } - - /// - /// Reinterprets the given reference as a reference to a value of type . - /// - [Intrinsic] - [NonVersionable] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref TTo As(ref TFrom source) - { - // This method is implemented by the toolchain - throw new PlatformNotSupportedException(); - - // ldarg.0 - // ret - } - - [Intrinsic] - [NonVersionable] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static unsafe ref T AddByteOffset(ref T source, nuint byteOffset) - { - return ref AddByteOffset(ref source, (IntPtr)(void*)byteOffset); - } - - [Intrinsic] - [NonVersionable] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T AddByteOffset(ref T source, IntPtr byteOffset) - { - // This method is implemented by the toolchain - throw new PlatformNotSupportedException(); - - // ldarg.0 - // ldarg.1 - // add - // ret - } - - /// - /// Adds an element offset to the given reference. - /// - [NonVersionable] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T Add(ref T source, int elementOffset) - { - return ref AddByteOffset(ref source, (IntPtr)(elementOffset * (nint)SizeOf())); - } - - /// - /// Determines whether the specified references point to the same location. - /// - [Intrinsic] - [NonVersionable] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool AreSame(ref T left, ref T right) - { - // This method is implemented by the toolchain - throw new PlatformNotSupportedException(); - - // ldarg.0 - // ldarg.1 - // ceq - // ret - } - - /// - /// Initializes a block of memory at the given location with a given initial value - /// without assuming architecture dependent alignment of the address. - /// - [Intrinsic] - [NonVersionable] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void InitBlockUnaligned(ref byte startAddress, byte value, uint byteCount) - { - for (uint i = 0; i < byteCount; i++) - AddByteOffset(ref startAddress, i) = value; - } - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/YieldAwaitable.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/YieldAwaitable.cs index d8a922b568..bcd1f616bb 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/YieldAwaitable.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/YieldAwaitable.cs @@ -25,7 +25,6 @@ using System; using System.Security; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Threading; using System.Threading.Tasks; @@ -38,7 +37,7 @@ namespace System.Runtime.CompilerServices /// Provides an awaitable context for switching into a target environment. /// This type is intended for compiler use only. - public struct YieldAwaitable + public readonly struct YieldAwaitable { /// Gets an awaiter for this . /// An awaiter for this awaitable. @@ -76,7 +75,6 @@ namespace System.Runtime.CompilerServices { // Validate arguments if (continuation == null) throw new ArgumentNullException(nameof(continuation)); - Contract.EndContractBlock(); // Get the current SynchronizationContext, and if there is one, // post the continuation to it. However, treat the base type diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/ExceptionIDs.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/ExceptionIDs.cs index c7b7f07afc..52d5830c7f 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/ExceptionIDs.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/ExceptionIDs.cs @@ -1,13 +1,6 @@ // 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. -//------------------------------------------------------------------------------------------------------------ -// @TODO: this type is related to throwing exceptions out of Rtm. If we did not have to throw -// out of Rtm, then we would note have to have the code below to get a classlib exception object given -// an exception id, or the special functions to back up the MDIL THROW_* instructions, or the allocation -// failure helper. If we could move to a world where we never throw out of Rtm, perhaps to other generated code, -// then we could remove all of this. -//------------------------------------------------------------------------------------------------------------ namespace System.Runtime { diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionDispatchInfo.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionDispatchInfo.cs index ea4dc9f536..c87f6c5275 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionDispatchInfo.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionDispatchInfo.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics; namespace System.Runtime.ExceptionServices { @@ -54,6 +55,7 @@ namespace System.Runtime.ExceptionServices // This method will restore the original stack trace and bucketing details before throwing // the exception so that it is easy, from debugging standpoint, to understand what really went wrong on // the original thread. + [StackTraceHidden] public void Throw() { // Restore the exception dispatch details before throwing the exception. diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionNotification.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionNotification.cs deleted file mode 100644 index 19392869d1..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionNotification.cs +++ /dev/null @@ -1,34 +0,0 @@ -// 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. -/*============================================================================= -** -** -** Purpose: Contains definitions for supporting Exception Notifications. -** -** -=============================================================================*/ - -using System; - -namespace System.Runtime.ExceptionServices -{ - // Definition of the argument-type passed to the FirstChanceException event handler - public class FirstChanceExceptionEventArgs : EventArgs - { - // Constructor - public FirstChanceExceptionEventArgs(Exception exception) - { - _exception = exception; - } - - // Returns the exception object pertaining to the first chance exception - public Exception Exception - { - get { return _exception; } - } - - // Represents the FirstChance exception instance - private Exception _exception; - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/GcSettings.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/GcSettings.cs index 6630d9dce3..24fb8dbc8d 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/GcSettings.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/GcSettings.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Security; using System.Diagnostics; @@ -74,7 +73,6 @@ namespace System.Runtime { throw new ArgumentOutOfRangeException(SR.ArgumentOutOfRange_Enum); } - Contract.EndContractBlock(); RuntimeImports.RhSetLohCompactionMode((int)value); } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs index 258a2f906d..5bae816fb4 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs @@ -3,8 +3,10 @@ // See the LICENSE file in the project root for more information. using System; -using System.Threading; using System.Runtime.CompilerServices; +using System.Threading; + +using Internal.Runtime.CompilerServices; namespace System.Runtime.InteropServices { @@ -114,10 +116,8 @@ namespace System.Runtime.InteropServices throw new InvalidOperationException(); // SR.InvalidOperation_HandleIsNotInitialized); } -#if CORERT // TODO: Higher level ProjectN frameworks took dependency on this validation missing if (IsPinned()) GCHandleValidatePinnedObject(value); -#endif RuntimeImports.RhHandleSet(GetHandleValue(), value); } @@ -150,28 +150,17 @@ namespace System.Runtime.InteropServices if (target == null) return default(IntPtr); - String targetAsString = target as String; - if (targetAsString != null) + if (target is String targetAsString) { - fixed (char* ptr = targetAsString) - { - return (IntPtr)ptr; - } + return (IntPtr)Unsafe.AsPointer(ref targetAsString.GetRawStringData()); } - Array targetAsArray = target as Array; - if (targetAsArray != null) + if (target is Array targetAsArray) { - fixed (byte* ptr = &targetAsArray.GetRawArrayData()) - { - return (IntPtr)ptr; - } + return (IntPtr)Unsafe.AsPointer(ref targetAsArray.GetRawArrayData()); } - fixed (byte* ptr = &target.GetRawData()) - { - return (IntPtr)ptr; - } + return (IntPtr)Unsafe.AsPointer(ref target.GetRawData()); } } @@ -267,29 +256,10 @@ namespace System.Runtime.InteropServices #endif } - // - // C# port of GCHandleValidatePinnedObject(OBJECTREF) in MarshalNative.cpp. - // private static void GCHandleValidatePinnedObject(Object obj) { - if (obj == null) - return; - if (obj is String) - return; - EETypePtr eeType = obj.EETypePtr; - if (eeType.IsArray) - { - EETypePtr elementEEType = eeType.ArrayElementType; - if (elementEEType.IsPrimitive) - return; - if (elementEEType.IsValueType && elementEEType.MightBeBlittable()) - return; - } - else if (eeType.MightBeBlittable()) - { - return; - } - throw new ArgumentException(SR.Argument_NotIsomorphic); + if (obj != null && !obj.IsBlittable()) + throw new ArgumentException(SR.Argument_NotIsomorphic); } // The actual integer handle value that the EE uses internally. diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/InteropExtensions.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/InteropExtensions.cs index 6cb60e738a..7bdb228f02 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/InteropExtensions.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/InteropExtensions.cs @@ -5,9 +5,11 @@ using System; using System.Runtime.CompilerServices; using System.Collections.Generic; -using Internal.Runtime.Augments; using System.Diagnostics; +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; + namespace System.Runtime.InteropServices { /// @@ -48,50 +50,28 @@ namespace System.Runtime.InteropServices } } - public static void CopyToManaged(IntPtr source, Array destination, int startIndex, int length) - { - Array.CopyToManaged(source, destination, startIndex, length); - } - - public static void CopyToNative(Array array, int startIndex, IntPtr destination, int length) - { - Array.CopyToNative(array, startIndex, destination, length); - } - public static int GetElementSize(this Array array) { return array.EETypePtr.ComponentSize; } - public static unsafe IntPtr GetAddrOfPinnedArrayFromEETypeField(this Array array) + internal static bool MightBeBlittable(this EETypePtr eeType) { - return (IntPtr)Unsafe.AsPointer(ref array.GetRawArrayData()); + // + // This is used as the approximate implementation of MethodTable::IsBlittable(). It will err in the direction of declaring + // things blittable since it is used for argument validation only. + // + return !eeType.HasPointers; } public static bool IsBlittable(this RuntimeTypeHandle handle) { - // - // @todo: B#754744 This is used as the Project N equivalent of MethodTable::IsBlittable(). The current implementation is rather... approximate. - // - return handle.ToEETypePtr().IsPrimitive || - !handle.ToEETypePtr().HasPointers; + return handle.ToEETypePtr().MightBeBlittable(); } - internal static bool MightBeBlittable(this EETypePtr eeType) + public static bool IsBlittable(this Object obj) { - // - // @todo: B#754744 This is used as the Project N equivalent of MethodTable::IsBlittable(). The current implementation is rather... approximate. This - // version will err in the direction of declaring things blittable. This is used for the pinned GCHandle validation code where false positives - // are the lesser evil (on the grounds that for V1, at least, app developers will almost always be testing IL versions of their apps and will notice - // any failures on that platform.) - // - return eeType.IsPrimitive || - !eeType.HasPointers; - } - - public static bool IsElementTypeBlittable(this Array array) - { - return array.IsElementTypeBlittable; + return obj.EETypePtr.MightBeBlittable(); } public static bool IsGenericType(this RuntimeTypeHandle handle) @@ -112,42 +92,6 @@ namespace System.Runtime.InteropServices return genericTypeDefinitionHandle.ToEETypePtr().ToPointer()->GenericArgumentCount; } - public static TKey FindEquivalentKeyUnsafe( - this ConditionalWeakTable table, - TKey key, - out TValue value - ) - where TKey : class - where TValue : class - { - return table.FindEquivalentKeyUnsafe(key, out value); - } - - public static System.Collections.Generic.ICollection GetValues( - this ConditionalWeakTable table - ) - where TKey : class - where TValue : class - { - return table.Values; - } - - public static System.Collections.Generic.ICollection GetKeys( - this ConditionalWeakTable table - ) - where TKey : class - where TValue : class - { - return table.Keys; - } - - public static void Clear( - this ConditionalWeakTable table) - where TKey : class - where TValue : class - { - table.Clear(); - } //TODO:Remove Delegate.GetNativeFunctionPointer public static IntPtr GetNativeFunctionPointer(this Delegate del) { @@ -155,8 +99,7 @@ namespace System.Runtime.InteropServices } public static IntPtr GetFunctionPointer(this Delegate del, out RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate) { - bool dummyIsOpenInstanceFunction; - return del.GetFunctionPointer(out typeOfFirstParameterIfInstanceDelegate, out dummyIsOpenInstanceFunction); + return del.GetFunctionPointer(out typeOfFirstParameterIfInstanceDelegate, out bool _, out bool _); } // @@ -170,10 +113,9 @@ namespace System.Runtime.InteropServices if (!del.IsOpenStatic) return IntPtr.Zero; - bool dummyIsOpenInstanceFunction; RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate; - IntPtr funcPtr = del.GetFunctionPointer(out typeOfFirstParameterIfInstanceDelegate, out dummyIsOpenInstanceFunction); + IntPtr funcPtr = del.GetFunctionPointer(out typeOfFirstParameterIfInstanceDelegate, out bool _, out bool _); // if the function pointer points to a jump stub return the target return RuntimeImports.RhGetJmpStubCodeTarget(funcPtr); @@ -497,16 +439,6 @@ namespace System.Runtime.InteropServices RuntimeThread.RestoreReentrantWaits(); } - public static IntPtr MemAlloc(UIntPtr sizeInBytes) - { - return Interop.MemAlloc(sizeInBytes); - } - - public static void MemFree(IntPtr allocatedMemory) - { - Interop.MemFree(allocatedMemory); - } - public static IntPtr GetCriticalHandle(CriticalHandle criticalHandle) { return criticalHandle.GetHandleInternal(); diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs new file mode 100644 index 0000000000..aaac87eb2d --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs @@ -0,0 +1,66 @@ +// 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. + + +namespace System.Runtime.InteropServices +{ + /// + /// This is an internal hacky implementation of Marshal + /// The original implementation of Marshal resides in S.P.Interop + /// + internal class Marshal + { + public static unsafe String PtrToStringUni(IntPtr ptr, int len) + { + return PInvokeMarshal.PtrToStringUni(ptr, len); + } + + public static unsafe String PtrToStringUni(IntPtr ptr) + { + return PInvokeMarshal.PtrToStringUni(ptr); + } + + public static int GetLastWin32Error() + { + return PInvokeMarshal.GetLastWin32Error(); + } + + public static unsafe IntPtr AllocHGlobal(IntPtr cb) + { + return PInvokeMarshal.AllocHGlobal(cb); + } + + public static unsafe IntPtr AllocHGlobal(int cb) + { + return PInvokeMarshal.AllocHGlobal(cb); + } + + public static void FreeHGlobal(IntPtr hglobal) + { + PInvokeMarshal.FreeHGlobal(hglobal); + } + + public static unsafe IntPtr AllocCoTaskMem(int cb) + { + return PInvokeMarshal.AllocCoTaskMem(cb); + } + + public static void FreeCoTaskMem(IntPtr ptr) + { + PInvokeMarshal.FreeCoTaskMem(ptr); + } + + public static void Copy(IntPtr source, byte[] destination, int startIndex, int length) + { + PInvokeMarshal.CopyToManaged(source, destination, startIndex, length); + } + +#if PLATFORM_UNIX + public static unsafe String PtrToStringAnsi(IntPtr ptr) + { + return PInvokeMarshal.PtrToStringAnsi(ptr); + } +#endif + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Unix.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Unix.cs index 41dae2a3a1..8a97ce15ac 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Unix.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Unix.cs @@ -18,11 +18,16 @@ namespace System.Runtime.InteropServices s_lastWin32Error = Interop.Sys.GetErrNo(); } - internal static void ClearLastWin32Error() + public static void ClearLastWin32Error() { Interop.Sys.ClearErrNo(); } + private static bool IsWin32Atom(IntPtr ptr) + { + return false; + } + public static unsafe String PtrToStringAnsi(IntPtr ptr) { if (IntPtr.Zero == ptr) diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Windows.cs index 999b516e20..3bb2673619 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Windows.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Windows.cs @@ -38,7 +38,7 @@ namespace System.Runtime.InteropServices s_lastWin32Error = Interop.mincore.GetLastError(); } - internal static void ClearLastWin32Error() + public static void ClearLastWin32Error() { Interop.mincore.SetLastError(0); } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs index 1630e652df..79c3ed85a5 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs @@ -2,15 +2,22 @@ // 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.Diagnostics.Contracts; using System.Security; -using Internal.Runtime.CompilerHelpers; -using Internal.Runtime.Augments; using Debug = System.Diagnostics.Debug; using System.Collections.Generic; using System.Threading; using System.Runtime.CompilerServices; +using Internal.Runtime.Augments; +using Internal.Runtime.CompilerHelpers; +using Internal.Runtime.CompilerServices; + +#if BIT64 +using nuint = System.UInt64; +#else +using nuint = System.UInt32; +#endif + namespace System.Runtime.InteropServices { /// @@ -69,7 +76,6 @@ namespace System.Runtime.InteropServices { throw new ArgumentNullException(nameof(s)); } - Contract.EndContractBlock(); return s.MarshalToString(globalAlloc: true, unicode: false); } @@ -80,7 +86,6 @@ namespace System.Runtime.InteropServices { throw new ArgumentNullException(nameof(s)); } - Contract.EndContractBlock(); return s.MarshalToString(globalAlloc: true, unicode: true); ; } @@ -91,7 +96,6 @@ namespace System.Runtime.InteropServices { throw new ArgumentNullException(nameof(s)); } - Contract.EndContractBlock(); return s.MarshalToString(globalAlloc: false, unicode: false); } @@ -102,11 +106,69 @@ namespace System.Runtime.InteropServices { throw new ArgumentNullException(nameof(s)); } - Contract.EndContractBlock(); return s.MarshalToString(globalAlloc: false, unicode: true); } + public static unsafe void CopyToManaged(IntPtr source, Array destination, int startIndex, int length) + { + if (source == IntPtr.Zero) + throw new ArgumentNullException(nameof(source)); + if (destination == null) + throw new ArgumentNullException(nameof(destination)); + if (!destination.IsBlittable()) + throw new ArgumentException(nameof(destination), SR.Arg_CopyNonBlittableArray); + if (startIndex < 0) + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.Arg_CopyOutOfRange); + if (length < 0) + throw new ArgumentOutOfRangeException(nameof(length), SR.Arg_CopyOutOfRange); + if ((uint)startIndex + (uint)length > (uint)destination.Length) + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.Arg_CopyOutOfRange); + + nuint bytesToCopy = (nuint)length * destination.ElementSize; + nuint startOffset = (nuint)startIndex * destination.ElementSize; + + fixed (byte* pDestination = &destination.GetRawArrayData()) + { + byte* destinationData = pDestination + startOffset; + Buffer.Memmove(destinationData, (byte*)source, bytesToCopy); + } + } + + public static unsafe void CopyToNative(Array source, int startIndex, IntPtr destination, int length) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + if (!source.IsBlittable()) + throw new ArgumentException(nameof(source), SR.Arg_CopyNonBlittableArray); + if (destination == IntPtr.Zero) + throw new ArgumentNullException(nameof(destination)); + if (startIndex < 0) + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.Arg_CopyOutOfRange); + if (length < 0) + throw new ArgumentOutOfRangeException(nameof(length), SR.Arg_CopyOutOfRange); + if ((uint)startIndex + (uint)length > (uint)source.Length) + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.Arg_CopyOutOfRange); + + nuint bytesToCopy = (nuint)length * source.ElementSize; + nuint startOffset = (nuint)startIndex * source.ElementSize; + + fixed (byte* pSource = &source.GetRawArrayData()) + { + byte* sourceData = pSource + startOffset; + Buffer.Memmove((byte*)destination, sourceData, bytesToCopy); + } + } + + public static unsafe IntPtr UnsafeAddrOfPinnedArrayElement(Array arr, int index) + { + if (arr == null) + throw new ArgumentNullException(nameof(arr)); + + byte* p = (byte*)Unsafe.AsPointer(ref arr.GetRawArrayData()) + (nuint)index * arr.ElementSize; + return (IntPtr)p; + } + #region Delegate marshalling private static object s_thunkPoolHeap; @@ -375,11 +437,36 @@ namespace System.Runtime.InteropServices [McgIntrinsics] private static unsafe class CalliIntrinsics { - internal static T Call(IntPtr pfn, IntPtr arg0) { throw new NotImplementedException(); } + internal static T Call(IntPtr pfn, IntPtr arg0) { throw new NotSupportedException(); } } #endregion #region String marshalling + public static unsafe String PtrToStringUni(IntPtr ptr, int len) + { + if (ptr == IntPtr.Zero) + throw new ArgumentNullException(nameof(ptr)); + if (len < 0) + throw new ArgumentException(nameof(len)); + + return new String((char*)ptr, 0, len); + } + + public static unsafe String PtrToStringUni(IntPtr ptr) + { + if (IntPtr.Zero == ptr) + { + return null; + } + else if (IsWin32Atom(ptr)) + { + return null; + } + else + { + return new String((char*)ptr); + } + } public static unsafe void StringBuilderToUnicodeString(System.Text.StringBuilder stringBuilder, ushort* destination) { diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/UnsafeGCHandle.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/UnsafeGCHandle.cs new file mode 100644 index 0000000000..d7fbf75971 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/InteropServices/UnsafeGCHandle.cs @@ -0,0 +1,90 @@ +// 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.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; + +using Internal.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + /// + /// The unsafe version of the GCHandle structure. + /// + /// + /// Differences from the GCHandle structure: + /// + /// The constructor assumes the handle type is valid; no range check is performed. + /// The pinned flag is not stored in the _handle field. + /// The Target getter and setter assume the UnsafeGCHandle has been allocated. + /// No blittable check is performed when allocating a pinned UnsafeGCHandle or setting its target. + /// The GetRawTargetAddress method returns the raw address of the target (the pointer to + /// its m_pEEType field). + /// The Free method is not thread-safe and does not throw if the UnsafeGCHandle + /// has not been allocated or has been already freed. + /// + /// + [StructLayout(LayoutKind.Sequential)] + public struct UnsafeGCHandle + { + // IMPORTANT: This must be kept in sync with the GCHandleType enum. + private const GCHandleType MaxHandleType = GCHandleType.Pinned; + + // The actual integer handle value that the EE uses internally. + private IntPtr _handle; + + // Allocate a handle storing the object and the type. + private UnsafeGCHandle(Object value, GCHandleType type) + { + Debug.Assert((uint)type <= (uint)MaxHandleType, "Unexpected handle type"); + _handle = RuntimeImports.RhHandleAlloc(value, type); + } + + public static UnsafeGCHandle Alloc(Object value, GCHandleType type) + { + return new UnsafeGCHandle(value, type); + } + + // Target property - allows getting / updating of the handle's referent. + public Object Target + { + get + { + Debug.Assert(IsAllocated, "Handle is not initialized"); + return RuntimeImports.RhHandleGet(_handle); + } + + set + { + Debug.Assert(IsAllocated, "Handle is not initialized"); + RuntimeImports.RhHandleSet(_handle, value); + } + } + + // Frees a GC handle. This method is not thread-safe! + public void Free() + { + if (_handle != default(IntPtr)) + { + RuntimeImports.RhHandleFree(_handle); + } + } + + // Returns the raw address of the target assuming it is pinned. + public unsafe IntPtr GetRawTargetAddress() + { + return *(IntPtr*)_handle; + } + + // Determine whether this handle has been allocated or not. + public bool IsAllocated + { + get + { + return _handle != default(IntPtr); + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs index aa960c7522..6ad477eadb 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs @@ -13,6 +13,7 @@ namespace System.Runtime.Loader public static Assembly[] GetLoadedAssemblies() => ReflectionAugments.ReflectionCoreCallbacks.GetLoadedAssemblies(); // These events are never called +#pragma warning disable 0067 public static event AssemblyLoadEventHandler AssemblyLoad; public static event ResolveEventHandler TypeResolve; public static event ResolveEventHandler ResourceResolve; @@ -20,6 +21,7 @@ namespace System.Runtime.Loader public event Func Resolving; public event Action Unloading; +#pragma warning restore 0067 public static AssemblyLoadContext Default { get; } = new DefaultAssemblyLoadContext(); diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/MemoryFailPoint.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/MemoryFailPoint.cs index 1712a0465f..4c68823e5c 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/MemoryFailPoint.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/MemoryFailPoint.cs @@ -22,7 +22,6 @@ using System.Runtime.CompilerServices; using System.Runtime.ConstrainedExecution; using System.Runtime.Versioning; using System.Diagnostics; -using System.Diagnostics.Contracts; /* This class allows an application to fail before starting certain @@ -244,7 +243,11 @@ namespace System.Runtime UIntPtr numBytes = new UIntPtr(segmentSize); unsafe { +#if ENABLE_WINRT + void* pMemory = Interop.mincore.VirtualAllocFromApp(null, numBytes, Interop.Kernel32.MEM_COMMIT, Interop.Kernel32.PAGE_READWRITE); +#else void* pMemory = Interop.Kernel32.VirtualAlloc(null, numBytes, Interop.Kernel32.MEM_COMMIT, Interop.Kernel32.PAGE_READWRITE); +#endif if (pMemory != null) { bool r = Interop.Kernel32.VirtualFree(pMemory, UIntPtr.Zero, Interop.Kernel32.MEM_RELEASE); @@ -285,7 +288,7 @@ namespace System.Runtime break; default: - Debug.Assert(false, "Fell through switch statement!"); + Debug.Fail("Fell through switch statement!"); break; } } @@ -449,7 +452,6 @@ namespace System.Runtime } #if DEBUG - [Serializable] internal sealed class MemoryFailPointState { private ulong _segmentSize; @@ -499,4 +501,4 @@ namespace System.Runtime } #endif } -} \ No newline at end of file +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs index 4a9c71d989..65473f205d 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -6,7 +6,9 @@ using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; + using Internal.Runtime; +using Internal.Runtime.CompilerServices; #if BIT64 using nuint = System.UInt64; @@ -33,19 +35,36 @@ namespace System.Runtime [RuntimeImport(RuntimeLibrary, "RhpSetHighLevelDebugFuncEvalHelper")] internal static extern void RhpSetHighLevelDebugFuncEvalHelper(IntPtr highLevelDebugFuncEvalHelper); + [DllImport(RuntimeLibrary, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern void RhpSetHighLevelDebugFuncEvalAbortHelper(IntPtr highLevelDebugFuncEvalAbortHelper); + [MethodImpl(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhpSendCustomEventToDebugger")] internal static extern void RhpSendCustomEventToDebugger(IntPtr payload, int length); - [DllImport(RuntimeLibrary, ExactSpelling = true)] - internal static extern IntPtr RhpGetFuncEvalTargetAddress(); - - [DllImport(RuntimeLibrary, ExactSpelling = true)] + [DllImport(RuntimeLibrary, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] internal static extern uint RhpGetFuncEvalParameterBufferSize(); - [DllImport(RuntimeLibrary, ExactSpelling = true)] + [DllImport(RuntimeLibrary, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern uint RhpGetFuncEvalMode(); + + [DllImport(RuntimeLibrary, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe uint RhpRecordDebuggeeInitiatedHandle(void* objectHandle); + [DllImport(RuntimeLibrary, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern void RhpVerifyDebuggerCleanup(); + + [DllImport(RuntimeLibrary, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr RhpGetCurrentThread(); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpInitiateThreadAbort")] + internal static extern void RhpInitiateThreadAbort(IntPtr thread, Exception exception, bool doRudeAbort); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhpCancelThreadAbort")] + internal static extern void RhpCancelThreadAbort(IntPtr thread); + // // calls to GC // These methods are needed to implement System.GC like functionality (optional) @@ -175,10 +194,18 @@ namespace System.Runtime [RuntimeImport(RuntimeLibrary, "RhGetGCSegmentSize")] internal static extern ulong RhGetGCSegmentSize(); + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetAllocatedBytesForCurrentThread")] + internal static extern long RhGetAllocatedBytesForCurrentThread(); + [MethodImpl(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhCompareObjectContentsAndPadding")] internal extern static bool RhCompareObjectContentsAndPadding(object obj1, object obj2); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetProcessCpuCount")] + internal static extern int RhGetProcessCpuCount(); + // // calls for GCHandle. // These methods are needed to implement GCHandle class like functionality (optional) @@ -231,7 +258,17 @@ namespace System.Runtime // Get object reference from handle. [MethodImpl(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhHandleGet")] - internal static extern Object RhHandleGet(IntPtr handle); + private static extern Object _RhHandleGet(IntPtr handle); + + internal static unsafe Object RhHandleGet(IntPtr handle) + { +#if DEBUG + // The runtime performs additional checks in debug builds + return _RhHandleGet(handle); +#else + return Unsafe.As(ref *(IntPtr*)handle); +#endif + } // Get primary and secondary object references from dependent handle. [MethodImpl(MethodImplOptions.InternalCall)] @@ -310,10 +347,9 @@ namespace System.Runtime [RuntimeImport(RuntimeLibrary, "RhNewArray")] internal static extern Array RhNewArray(EETypePtr pEEType, int length); - // @todo: Should we just have a proper export for this? [MethodImpl(MethodImplOptions.InternalCall)] - [RuntimeImport(RuntimeLibrary, "RhNewArray")] - internal static extern String RhNewArrayAsString(EETypePtr pEEType, int length); + [RuntimeImport(RuntimeLibrary, "RhNewString")] + internal static extern String RhNewString(EETypePtr pEEType, int length); [MethodImpl(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhBox")] @@ -499,34 +535,9 @@ namespace System.Runtime return RhGetModuleSection(ref module, section, out length); } -#if CORERT - internal static uint RhGetLoadedOSModules(IntPtr[] resultArray) - { - IntPtr[] loadedModules = Internal.Runtime.CompilerHelpers.StartupCodeHelpers.OSModules; - if (resultArray != null) - { - Array.Copy(loadedModules, resultArray, Math.Min(loadedModules.Length, resultArray.Length)); - } - return (uint)loadedModules.Length; - } - - internal static uint RhGetLoadedModules(TypeManagerHandle[] resultArray) - { - TypeManagerHandle[] loadedModules = Internal.Runtime.CompilerHelpers.StartupCodeHelpers.Modules; - if (resultArray != null) - { - Array.Copy(loadedModules, resultArray, Math.Min(loadedModules.Length, resultArray.Length)); - } - return (uint)loadedModules.Length; - } -#else [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhGetLoadedOSModules")] internal static extern uint RhGetLoadedOSModules(IntPtr[] resultArray); - [MethodImplAttribute(MethodImplOptions.InternalCall)] - [RuntimeImport(RuntimeLibrary, "RhGetLoadedModules")] - internal static extern uint RhGetLoadedModules(TypeManagerHandle[] resultArray); -#endif [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhGetOSModuleFromPointer")] @@ -546,9 +557,9 @@ namespace System.Runtime [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhGetThreadStaticFieldAddress")] - internal static extern unsafe byte* RhGetThreadStaticFieldAddress(EETypePtr pEEType, IntPtr fieldCookie); + internal static extern unsafe byte* RhGetThreadStaticFieldAddress(EETypePtr pEEType, int threadStaticsBlockOffset, int fieldOffset); -#if CORERT +#if !PROJECTN [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhGetThreadStaticStorageForModule")] internal static unsafe extern Array RhGetThreadStaticStorageForModule(Int32 moduleIndex); @@ -556,6 +567,10 @@ namespace System.Runtime [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhSetThreadStaticStorageForModule")] internal static unsafe extern bool RhSetThreadStaticStorageForModule(Array storage, Int32 moduleIndex); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhCurrentNativeThreadId")] + internal static unsafe extern IntPtr RhCurrentNativeThreadId(); #endif [MethodImplAttribute(MethodImplOptions.InternalCall)] @@ -568,12 +583,16 @@ namespace System.Runtime [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhGetCodeTarget")] - internal static extern IntPtr RhGetCodeTarget(IntPtr pCode); + public static extern IntPtr RhGetCodeTarget(IntPtr pCode); [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhGetJmpStubCodeTarget")] internal static extern IntPtr RhGetJmpStubCodeTarget(IntPtr pCode); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetTargetOfUnboxingAndInstantiatingStub")] + public static extern IntPtr RhGetTargetOfUnboxingAndInstantiatingStub(IntPtr pCode); + // // EH helpers // @@ -709,7 +728,7 @@ namespace System.Runtime [RuntimeImport(RuntimeLibrary, "RhpEtwExceptionThrown")] internal extern static unsafe void RhpEtwExceptionThrown(char* exceptionTypeName, char* exceptionMessage, IntPtr faultingIP, long hresult); -#if CORERT +#if !PROJECTN // // Interlocked helpers // @@ -740,7 +759,7 @@ namespace System.Runtime [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhpMemoryBarrier")] internal extern static void MemoryBarrier(); -#endif // CORERT +#endif // !PROJECTN [Intrinsic] [MethodImplAttribute(MethodImplOptions.InternalCall)] diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/Serialization/SerializationInfo.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/Serialization/SerializationInfo.cs index 3d49dad7d4..7bb19995ba 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/Serialization/SerializationInfo.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/Serialization/SerializationInfo.cs @@ -2,7 +2,7 @@ // 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.Collections; +using System.Collections.Generic; using System.Diagnostics; using System.Reflection; @@ -18,7 +18,7 @@ namespace System.Runtime.Serialization private object[] _values; private Type[] _types; private int _count; - private LowLevelListDictionary _nameToIndex; // TODO: Replace with Dictionary + private Dictionary _nameToIndex; private IFormatterConverter _converter; private string _rootTypeName; @@ -44,7 +44,7 @@ namespace System.Runtime.Serialization _names = new string[DefaultSize]; _values = new object[DefaultSize]; _types = new Type[DefaultSize]; - _nameToIndex = new LowLevelListDictionary(); + _nameToIndex = new Dictionary(); _converter = converter; } @@ -143,7 +143,9 @@ namespace System.Runtime.Serialization _types = newTypes; } - internal void UpdateValue(string name, object value, Type type) + // This isn't a public API, but it gets invoked dynamically by + // BinaryFormatter + public void UpdateValue(string name, object value, Type type) { Debug.Assert(null != name, "[SerializationInfo.UpdateValue]name!=null"); Debug.Assert(null != value, "[SerializationInfo.UpdateValue]value!=null"); @@ -264,7 +266,7 @@ namespace System.Runtime.Serialization internal void AddValueInternal(string name, object value, Type type) { - if (_nameToIndex.Contains(name)) + if (_nameToIndex.ContainsKey(name)) { throw new SerializationException(SR.Serialization_SameNameTwice); } @@ -290,8 +292,12 @@ namespace System.Runtime.Serialization throw new ArgumentNullException(nameof(name)); } - object indexObj = _nameToIndex[name]; - return indexObj is int ? (int)indexObj : -1; + int index; + if (_nameToIndex.TryGetValue(name, out index)) + { + return index; + } + return -1; } private object GetElement(string name, out Type foundType) diff --git a/external/corert/src/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs b/external/corert/src/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs index d9e14e0a18..c42a690f56 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs @@ -13,7 +13,9 @@ namespace System.Runtime { public static class TypeLoaderExports { +#if PROJECTN [RuntimeExport("GetThreadStaticsForDynamicType")] +#endif public static IntPtr GetThreadStaticsForDynamicType(int index) { IntPtr result = RuntimeImports.RhGetThreadLocalStorageForDynamicType(index, 0, 0); @@ -30,7 +32,9 @@ namespace System.Runtime return result; } +#if PROJECTN [RuntimeExport("ActivatorCreateInstanceAny")] +#endif public static unsafe void ActivatorCreateInstanceAny(ref object ptrToData, IntPtr pEETypePtr) { EETypePtr pEEType = new EETypePtr(pEETypePtr); @@ -50,7 +54,14 @@ namespace System.Runtime Entry entry = LookupInCache(s_cache, pEETypePtr, pEETypePtr); if (entry == null) { - entry = CacheMiss(pEETypePtr, pEETypePtr, SignatureKind.DefaultConstructor); + entry = CacheMiss(pEETypePtr, pEETypePtr, + (IntPtr context, IntPtr signature, object contextObject, ref IntPtr auxResult) => + { + IntPtr result = RuntimeAugments.TypeLoaderCallbacks.TryGetDefaultConstructorForType(new RuntimeTypeHandle(new EETypePtr(context))); + if (result == IntPtr.Zero) + result = RuntimeAugments.GetFallbackDefaultConstructor(); + return result; + }); } RawCalliHelper.Call(entry.Result, ptrToData); } @@ -75,17 +86,15 @@ namespace System.Runtime private static Lock s_lock; private static GCHandle s_previousCache; - private volatile static IntPtr[] s_resolutionFunctionPointers; - private static int s_nextResolutionFunctionPointerIndex; internal static void Initialize() { s_cache = new Entry[1]; - s_resolutionFunctionPointers = new IntPtr[4]; - s_nextResolutionFunctionPointerIndex = (int)SignatureKind.Count; } +#if PROJECTN [RuntimeExport("GenericLookup")] +#endif public static IntPtr GenericLookup(IntPtr context, IntPtr signature) { Entry entry = LookupInCache(s_cache, context, signature); @@ -96,7 +105,9 @@ namespace System.Runtime return entry.Result; } +#if PROJECTN [RuntimeExport("GenericLookupAndCallCtor")] +#endif public static void GenericLookupAndCallCtor(Object arg, IntPtr context, IntPtr signature) { Entry entry = LookupInCache(s_cache, context, signature); @@ -107,7 +118,9 @@ namespace System.Runtime RawCalliHelper.Call(entry.Result, arg); } +#if PROJECTN [RuntimeExport("GenericLookupAndAllocObject")] +#endif public static Object GenericLookupAndAllocObject(IntPtr context, IntPtr signature) { Entry entry = LookupInCache(s_cache, context, signature); @@ -118,7 +131,9 @@ namespace System.Runtime return RawCalliHelper.Call(entry.Result, entry.AuxResult); } +#if PROJECTN [RuntimeExport("GenericLookupAndAllocArray")] +#endif public static Object GenericLookupAndAllocArray(IntPtr context, IntPtr arg, IntPtr signature) { Entry entry = LookupInCache(s_cache, context, signature); @@ -129,7 +144,9 @@ namespace System.Runtime return RawCalliHelper.Call(entry.Result, entry.AuxResult, arg); } +#if PROJECTN [RuntimeExport("GenericLookupAndCheckArrayElemType")] +#endif public static void GenericLookupAndCheckArrayElemType(IntPtr context, object arg, IntPtr signature) { Entry entry = LookupInCache(s_cache, context, signature); @@ -140,7 +157,9 @@ namespace System.Runtime RawCalliHelper.Call(entry.Result, entry.AuxResult, arg); } +#if PROJECTN [RuntimeExport("GenericLookupAndCast")] +#endif public static Object GenericLookupAndCast(Object arg, IntPtr context, IntPtr signature) { Entry entry = LookupInCache(s_cache, context, signature); @@ -151,12 +170,33 @@ namespace System.Runtime return RawCalliHelper.Call(entry.Result, arg, entry.AuxResult); } +#if PROJECTN + [RuntimeExport("UpdateTypeFloatingDictionary")] +#endif + public static IntPtr UpdateTypeFloatingDictionary(IntPtr eetypePtr, IntPtr dictionaryPtr) + { + // No caching needed. Update is in-place, and happens once per dictionary + return RuntimeAugments.TypeLoaderCallbacks.UpdateFloatingDictionary(eetypePtr, dictionaryPtr); + } + +#if PROJECTN + [RuntimeExport("UpdateMethodFloatingDictionary")] +#endif + public static IntPtr UpdateMethodFloatingDictionary(IntPtr dictionaryPtr) + { + // No caching needed. Update is in-place, and happens once per dictionary + return RuntimeAugments.TypeLoaderCallbacks.UpdateFloatingDictionary(dictionaryPtr, dictionaryPtr); + } + public static unsafe IntPtr GetDelegateThunk(object delegateObj, int whichThunk) { Entry entry = LookupInCache(s_cache, delegateObj.m_pEEType, new IntPtr(whichThunk)); if (entry == null) { - entry = CacheMiss(delegateObj.m_pEEType, new IntPtr(whichThunk), SignatureKind.GenericDelegateThunk, delegateObj); + entry = CacheMiss(delegateObj.m_pEEType, new IntPtr(whichThunk), + (IntPtr context, IntPtr signature, object contextObject, ref IntPtr auxResult) + => RuntimeAugments.TypeLoaderCallbacks.GetDelegateThunk((Delegate)contextObject, (int)signature), + delegateObj); } return entry.Result; } @@ -166,7 +206,9 @@ namespace System.Runtime Entry entry = LookupInCache(s_cache, obj.m_pEEType, *(IntPtr*)&slot); if (entry == null) { - entry = CacheMiss(obj.m_pEEType, *(IntPtr*)&slot, SignatureKind.GenericVirtualMethod); + entry = CacheMiss(obj.m_pEEType, *(IntPtr*)&slot, + (IntPtr context, IntPtr signature, object contextObject, ref IntPtr auxResult) + => Internal.Runtime.CompilerServices.GenericVirtualMethodSupport.GVMLookupForSlot(new RuntimeTypeHandle(new EETypePtr(context)), *(RuntimeMethodHandle*)&signature)); } return entry.Result; } @@ -177,7 +219,10 @@ namespace System.Runtime Entry entry = LookupInCache(s_cache, obj.m_pEEType, openResolver); if (entry == null) { - entry = CacheMiss(obj.m_pEEType, openResolver, SignatureKind.OpenInstanceResolver, obj); + entry = CacheMiss(obj.m_pEEType, openResolver, + (IntPtr context, IntPtr signature, object contextObject, ref IntPtr auxResult) + => Internal.Runtime.CompilerServices.OpenMethodResolver.ResolveMethodWorker(signature, contextObject), + obj); } return entry.Result; } @@ -196,59 +241,27 @@ namespace System.Runtime return entry; } - private enum SignatureKind - { - GenericDictionary, - GenericVirtualMethod, - OpenInstanceResolver, - DefaultConstructor, - GenericDelegateThunk, - Count - } - - internal static int RegisterResolutionFunction(IntPtr resolutionFunction) - { - if (s_lock == null) - Interlocked.CompareExchange(ref s_lock, new Lock(), null); - - s_lock.Acquire(); - try - { - int newResolutionFunctionId = s_nextResolutionFunctionPointerIndex; - IntPtr[] resolutionFunctionPointers = null; - if (newResolutionFunctionId < s_resolutionFunctionPointers.Length) - { - resolutionFunctionPointers = s_resolutionFunctionPointers; - } - else - { - resolutionFunctionPointers = new IntPtr[s_resolutionFunctionPointers.Length * 2]; - Array.Copy(s_resolutionFunctionPointers, resolutionFunctionPointers, s_resolutionFunctionPointers.Length); - s_resolutionFunctionPointers = resolutionFunctionPointers; - } - Volatile.Write(ref s_resolutionFunctionPointers[newResolutionFunctionId], resolutionFunction); - s_nextResolutionFunctionPointerIndex++; - return newResolutionFunctionId; - } - finally - { - s_lock.Release(); - } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static IntPtr RuntimeCacheLookupInCache(IntPtr context, IntPtr signature, int registeredResolutionFunction, object contextObject, out IntPtr auxResult) + internal static IntPtr RuntimeCacheLookupInCache(IntPtr context, IntPtr signature, RuntimeObjectFactory factory, object contextObject, out IntPtr auxResult) { Entry entry = LookupInCache(s_cache, context, signature); if (entry == null) { - entry = CacheMiss(context, signature, (SignatureKind)registeredResolutionFunction, contextObject); + entry = CacheMiss(context, signature, factory, contextObject); } auxResult = entry.AuxResult; return entry.Result; } - private static unsafe Entry CacheMiss(IntPtr context, IntPtr signature, SignatureKind signatureKind = SignatureKind.GenericDictionary, object contextObject = null) + private static Entry CacheMiss(IntPtr ctx, IntPtr sig) + { + return CacheMiss(ctx, sig, + (IntPtr context, IntPtr signature, object contextObject, ref IntPtr auxResult) => + RuntimeAugments.TypeLoaderCallbacks.GenericLookupFromContextAndSignature(context, signature, out auxResult) + ); + } + + private static unsafe Entry CacheMiss(IntPtr context, IntPtr signature, RuntimeObjectFactory factory, object contextObject = null) { IntPtr result = IntPtr.Zero, auxResult = IntPtr.Zero; bool previouslyCached = false; @@ -276,31 +289,7 @@ namespace System.Runtime // if (!previouslyCached) { - switch (signatureKind) - { - case SignatureKind.GenericDictionary: - result = RuntimeAugments.TypeLoaderCallbacks.GenericLookupFromContextAndSignature(context, signature, out auxResult); - break; - case SignatureKind.GenericVirtualMethod: - result = Internal.Runtime.CompilerServices.GenericVirtualMethodSupport.GVMLookupForSlot(new RuntimeTypeHandle(new EETypePtr(context)), *(RuntimeMethodHandle*)&signature); - break; - case SignatureKind.OpenInstanceResolver: - result = Internal.Runtime.CompilerServices.OpenMethodResolver.ResolveMethodWorker(signature, contextObject); - break; - case SignatureKind.DefaultConstructor: - { - result = RuntimeAugments.TypeLoaderCallbacks.TryGetDefaultConstructorForType(new RuntimeTypeHandle(new EETypePtr(context))); - if (result == IntPtr.Zero) - result = RuntimeAugments.GetFallbackDefaultConstructor(); - } - break; - case SignatureKind.GenericDelegateThunk: - result = RuntimeAugments.TypeLoaderCallbacks.GetDelegateThunk((Delegate)contextObject, (int)signature); - break; - default: - result = RawCalliHelper.Call(s_resolutionFunctionPointers[(int)signatureKind], context, signature, contextObject, out auxResult); - break; - } + result = factory(context, signature, contextObject, ref auxResult); } // @@ -430,6 +419,8 @@ namespace System.Runtime } } + public delegate IntPtr RuntimeObjectFactory(IntPtr context, IntPtr signature, object contextObject, ref IntPtr auxResult); + [System.Runtime.InteropServices.McgIntrinsicsAttribute] internal class RawCalliHelper { diff --git a/external/corert/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs b/external/corert/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs index baa46d8973..bc12ca38b5 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs @@ -341,7 +341,7 @@ namespace System pMetadata->NestingLevel = ExceptionMetadata.NestingLevel; pMetadata->ExceptionCCWPtr = ExceptionMetadata.ExceptionCCWPtr; - Array.CopyToNative(SerializedExceptionData, 0, (IntPtr)(pSerializedData + sizeof(ExceptionMetadataStruct)), SerializedExceptionData.Length); + PInvokeMarshal.CopyToNative(SerializedExceptionData, 0, (IntPtr)(pSerializedData + sizeof(ExceptionMetadataStruct)), SerializedExceptionData.Length); } return serializedData; } @@ -405,8 +405,9 @@ namespace System uint currentThreadId = (uint)Environment.CurrentNativeThreadId; // Reset nesting levels for exceptions on this thread that might not be currently in flight - foreach (ExceptionData exceptionData in s_exceptionDataTable.GetValues()) + foreach (KeyValuePair item in s_exceptionDataTable) { + ExceptionData exceptionData = item.Value; if (exceptionData.ExceptionMetadata.ThreadId == currentThreadId) { exceptionData.ExceptionMetadata.NestingLevel = -1; @@ -505,8 +506,10 @@ namespace System checked { - foreach (ExceptionData exceptionData in s_exceptionDataTable.GetValues()) + foreach (KeyValuePair item in s_exceptionDataTable) { + ExceptionData exceptionData = item.Value; + // Already serialized currentException if (currentExceptionData != null && exceptionData.ExceptionMetadata.ExceptionId == currentExceptionData.ExceptionMetadata.ExceptionId) { @@ -529,7 +532,7 @@ namespace System { checked { - int loadedModuleCount = RuntimeAugments.GetLoadedOSModules(null); + int loadedModuleCount = (int)RuntimeImports.RhGetLoadedOSModules(null); int cbModuleHandles = sizeof(System.IntPtr) * loadedModuleCount; int cbFinalBuffer = sizeof(ERROR_REPORT_BUFFER_HEADER) + sizeof(SERIALIZED_ERROR_REPORT_HEADER) + cbModuleHandles; for (int i = 0; i < serializedExceptions.Count; i++) @@ -557,15 +560,15 @@ namespace System for (int i = 0; i < serializedExceptions.Count; i++) { int cbChunk = serializedExceptions[i].Length; - Array.CopyToNative(serializedExceptions[i], 0, (IntPtr)pCursor, cbChunk); + PInvokeMarshal.CopyToNative(serializedExceptions[i], 0, (IntPtr)pCursor, cbChunk); cbRemaining -= cbChunk; pCursor += cbChunk; } // copy the module-handle array to report buffer IntPtr[] loadedModuleHandles = new IntPtr[loadedModuleCount]; - RuntimeAugments.GetLoadedOSModules(loadedModuleHandles); - Array.CopyToNative(loadedModuleHandles, 0, (IntPtr)pCursor, loadedModuleHandles.Length); + RuntimeImports.RhGetLoadedOSModules(loadedModuleHandles); + PInvokeMarshal.CopyToNative(loadedModuleHandles, 0, (IntPtr)pCursor, loadedModuleHandles.Length); cbRemaining -= cbModuleHandles; pCursor += cbModuleHandles; diff --git a/external/corert/src/System.Private.CoreLib/src/System/RuntimeFieldHandle.cs b/external/corert/src/System.Private.CoreLib/src/System/RuntimeFieldHandle.cs index e643c0b566..844447a934 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/RuntimeFieldHandle.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/RuntimeFieldHandle.cs @@ -11,7 +11,6 @@ using Internal.Runtime.Augments; namespace System { - [Serializable] [StructLayoutAttribute(LayoutKind.Sequential)] public struct RuntimeFieldHandle : ISerializable { @@ -67,46 +66,9 @@ namespace System return !left.Equals(right); } - public RuntimeFieldHandle(SerializationInfo info, StreamingContext context) - { - if (info == null) - throw new ArgumentNullException(nameof(info)); - - try - { - FieldInfo field = (FieldInfo)info.GetValue("FieldObj", typeof(FieldInfo)); - if (field == null) - throw new SerializationException(SR.Serialization_InsufficientState); - - this = field.FieldHandle; - } - catch (Exception e) when (!(e is SerializationException)) - { - throw new SerializationException(e.Message, e); - } - } - public void GetObjectData(SerializationInfo info, StreamingContext context) { - if (info == null) - throw new ArgumentNullException(nameof(info)); - - try - { - if (_value == IntPtr.Zero) - throw new SerializationException(SR.Serialization_InvalidFieldState); - - string fieldName; - RuntimeTypeHandle declaringType; - RuntimeAugments.TypeLoaderCallbacks.GetRuntimeFieldHandleComponents(this, out declaringType, out fieldName); - - FieldInfo field = FieldInfo.GetFieldFromHandle(this, declaringType); - info.AddValue("FieldObj", field, typeof(FieldInfo)); - } - catch (Exception e) when (!(e is SerializationException)) - { - throw new SerializationException(e.Message, e); - } + throw new PlatformNotSupportedException(); } } -} \ No newline at end of file +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs b/external/corert/src/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs index df32dc355f..ba276010e3 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs @@ -13,7 +13,6 @@ using Internal.Reflection.Augments; namespace System { - [Serializable] [StructLayout(LayoutKind.Sequential)] public struct RuntimeMethodHandle : ISerializable { @@ -108,48 +107,9 @@ namespace System return ReflectionAugments.ReflectionCoreCallbacks.GetFunctionPointer(this, declaringType); } - public RuntimeMethodHandle(SerializationInfo info, StreamingContext context) - { - if (info == null) - throw new ArgumentNullException(nameof(info)); - - try - { - MethodBase method = (MethodBase)info.GetValue("MethodObj", typeof(MethodBase)); - if (method == null) - throw new SerializationException(SR.Serialization_InsufficientState); - - this = method.MethodHandle; - } - catch (Exception e) when (!(e is SerializationException)) - { - throw new SerializationException(e.Message, e); - } - } - public void GetObjectData(SerializationInfo info, StreamingContext context) { - if (info == null) - throw new ArgumentNullException(nameof(info)); - - try - { - if (_value == IntPtr.Zero) - throw new SerializationException(SR.Serialization_InvalidFieldState); - - RuntimeTypeHandle declaringType; - MethodNameAndSignature nameAndSignature; - RuntimeTypeHandle[] genericArgs; - RuntimeAugments.TypeLoaderCallbacks.GetRuntimeMethodHandleComponents(this, out declaringType, out nameAndSignature, out genericArgs); - - // This is either a RuntimeMethodInfo or a RuntimeConstructorInfo - MethodBase method = MethodInfo.GetMethodFromHandle(this, declaringType); - info.AddValue("MethodObj", method, typeof(MethodBase)); - } - catch (Exception e) when (!(e is SerializationException)) - { - throw new SerializationException(e.Message, e); - } + throw new PlatformNotSupportedException(); } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/RuntimeTypeHandle.cs b/external/corert/src/System.Private.CoreLib/src/System/RuntimeTypeHandle.cs index cbd8ee5273..c713560e67 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/RuntimeTypeHandle.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/RuntimeTypeHandle.cs @@ -13,7 +13,6 @@ using Internal.Runtime.Augments; namespace System { - [Serializable] [StructLayout(LayoutKind.Sequential)] public unsafe struct RuntimeTypeHandle : IEquatable, ISerializable { @@ -101,44 +100,9 @@ namespace System return type.Module.ModuleHandle; } - public RuntimeTypeHandle(SerializationInfo info, StreamingContext context) - { - if (info == null) - throw new ArgumentNullException(nameof(info)); - - try - { - Type type = (Type)info.GetValue("TypeObj", typeof(Type)); - if (type == null) - throw new SerializationException(SR.Serialization_InsufficientState); - - _value = type.TypeHandle.ToEETypePtr().RawValue; - } - catch (Exception e) when (!(e is SerializationException)) - { - throw new SerializationException(e.Message, e); - } - } - public void GetObjectData(SerializationInfo info, StreamingContext context) { - if (info == null) - throw new ArgumentNullException(nameof(info)); - - try - { - if(_value == IntPtr.Zero) - throw new SerializationException(SR.Serialization_InvalidFieldState); - - Type type = Type.GetTypeFromHandle(this); - Debug.Assert(type != null); - - info.AddValue("TypeObj", type, typeof(Type)); - } - catch (Exception e) when (!(e is SerializationException)) - { - throw new SerializationException(e.Message, e); - } + throw new PlatformNotSupportedException(); } internal EETypePtr ToEETypePtr() diff --git a/external/corert/src/System.Private.CoreLib/src/System/Single.cs b/external/corert/src/System.Private.CoreLib/src/System/Single.cs deleted file mode 100644 index 17ea16d918..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/Single.cs +++ /dev/null @@ -1,344 +0,0 @@ -// 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. - -/*============================================================ -** -** -** -** Purpose: A wrapper class for the primitive type float. -** -** -===========================================================*/ - -using System.Globalization; -using System; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; -using System.Diagnostics.Contracts; - -namespace System -{ - [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)] - public struct Single : IComparable, IFormattable, IComparable, IEquatable, IConvertible - { - private float _value; - - // - // Public constants - // - public const float MinValue = (float)-3.40282346638528859e+38; - public const float Epsilon = (float)1.4e-45; - public const float MaxValue = (float)3.40282346638528859e+38; - public const float PositiveInfinity = (float)1.0 / (float)0.0; - public const float NegativeInfinity = (float)-1.0 / (float)0.0; - public const float NaN = (float)0.0 / (float)0.0; - - [Pure] - public static unsafe bool IsInfinity(float f) - { - return (*(int*)(&f) & 0x7FFFFFFF) == 0x7F800000; - } - - [Pure] - public static unsafe bool IsPositiveInfinity(float f) - { - return *(int*)(&f) == 0x7F800000; - } - - [Pure] - public static unsafe bool IsNegativeInfinity(float f) - { - return *(int*)(&f) == unchecked((int)0xFF800000); - } - - [Pure] - public static unsafe bool IsNaN(float f) - { - return (*(int*)(&f) & 0x7FFFFFFF) > 0x7F800000; - } - - // Compares this object to another object, returning an integer that - // indicates the relationship. - // Returns a value less than zero if this object - // null is considered to be less than any instance. - // If object is not of type Single, this method throws an ArgumentException. - // - public int CompareTo(Object value) - { - if (value == null) - { - return 1; - } - if (value is Single) - { - float f = (float)value; - if (_value < f) return -1; - if (_value > f) return 1; - if (_value == f) return 0; - - // At least one of the values is NaN. - if (IsNaN(_value)) - return (IsNaN(f) ? 0 : -1); - else // f is NaN. - return 1; - } - throw new ArgumentException(SR.Arg_MustBeSingle); - } - - - public int CompareTo(Single value) - { - if (_value < value) return -1; - if (_value > value) return 1; - if (_value == value) return 0; - - // At least one of the values is NaN. - if (IsNaN(_value)) - return (IsNaN(value) ? 0 : -1); - else // f is NaN. - return 1; - } - - public static bool operator ==(Single left, Single right) - { - return left == right; - } - - public static bool operator !=(Single left, Single right) - { - return left != right; - } - - public static bool operator <(Single left, Single right) - { - return left < right; - } - - public static bool operator >(Single left, Single right) - { - return left > right; - } - - public static bool operator <=(Single left, Single right) - { - return left <= right; - } - - public static bool operator >=(Single left, Single right) - { - return left >= right; - } - - public override bool Equals(Object obj) - { - if (!(obj is Single)) - { - return false; - } - float temp = ((Single)obj)._value; - if (temp == _value) - { - return true; - } - - return IsNaN(temp) && IsNaN(_value); - } - - public bool Equals(Single obj) - { - if (obj == _value) - { - return true; - } - - return IsNaN(obj) && IsNaN(_value); - } - - public unsafe override int GetHashCode() - { - float f = _value; - if (f == 0) - { - // Ensure that 0 and -0 have the same hash code - return 0; - } - int v = *(int*)(&f); - return v; - } - - public override String ToString() - { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatSingle(_value, null, null); - } - - public String ToString(IFormatProvider provider) - { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatSingle(_value, null, provider); - } - - public String ToString(String format) - { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatSingle(_value, format, null); - } - - public String ToString(String format, IFormatProvider provider) - { - Contract.Ensures(Contract.Result() != null); - return FormatProvider.FormatSingle(_value, format, provider); - } - - // Parses a float from a String in the given style. If - // a NumberFormatInfo isn't specified, the current culture's - // NumberFormatInfo is assumed. - // - // This method will not throw an OverflowException, but will return - // PositiveInfinity or NegativeInfinity for a number that is too - // large or too small. - // - public static float Parse(String s) - { - return Parse(s, NumberStyles.Float | NumberStyles.AllowThousands, null); - } - - public static float Parse(String s, NumberStyles style) - { - Decimal.ValidateParseStyleFloatingPoint(style); - return Parse(s, style, null); - } - - public static float Parse(String s, IFormatProvider provider) - { - return Parse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider); - } - - public static float Parse(String s, NumberStyles style, IFormatProvider provider) - { - Decimal.ValidateParseStyleFloatingPoint(style); - return FormatProvider.ParseSingle(s, style, provider); - } - - public static Boolean TryParse(String s, out Single result) - { - return TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, null, out result); - } - - public static Boolean TryParse(String s, NumberStyles style, IFormatProvider provider, out Single result) - { - Decimal.ValidateParseStyleFloatingPoint(style); - if (s == null) - { - result = 0; - return false; - } - bool success = FormatProvider.TryParseSingle(s, style, provider, out result); - if (!success) - { - String sTrim = s.Trim(); - if (FormatProvider.IsPositiveInfinity(sTrim, provider)) - { - result = PositiveInfinity; - } - else if (FormatProvider.IsNegativeInfinity(sTrim, provider)) - { - result = NegativeInfinity; - } - else if (FormatProvider.IsNaNSymbol(sTrim, provider)) - { - result = NaN; - } - else - return false; // We really failed - } - return true; - } - - // - // IConvertible implementation - // - - public TypeCode GetTypeCode() - { - return TypeCode.Single; - } - - - bool IConvertible.ToBoolean(IFormatProvider provider) - { - return Convert.ToBoolean(_value); - } - - char IConvertible.ToChar(IFormatProvider provider) - { - throw new InvalidCastException(String.Format(SR.InvalidCast_FromTo, "Single", "Char")); - } - - sbyte IConvertible.ToSByte(IFormatProvider provider) - { - return Convert.ToSByte(_value); - } - - byte IConvertible.ToByte(IFormatProvider provider) - { - return Convert.ToByte(_value); - } - - short IConvertible.ToInt16(IFormatProvider provider) - { - return Convert.ToInt16(_value); - } - - ushort IConvertible.ToUInt16(IFormatProvider provider) - { - return Convert.ToUInt16(_value); - } - - int IConvertible.ToInt32(IFormatProvider provider) - { - return Convert.ToInt32(_value); - } - - uint IConvertible.ToUInt32(IFormatProvider provider) - { - return Convert.ToUInt32(_value); - } - - long IConvertible.ToInt64(IFormatProvider provider) - { - return Convert.ToInt64(_value); - } - - ulong IConvertible.ToUInt64(IFormatProvider provider) - { - return Convert.ToUInt64(_value); - } - - float IConvertible.ToSingle(IFormatProvider provider) - { - return _value; - } - - double IConvertible.ToDouble(IFormatProvider provider) - { - return Convert.ToDouble(_value); - } - - Decimal IConvertible.ToDecimal(IFormatProvider provider) - { - return Convert.ToDecimal(_value); - } - - DateTime IConvertible.ToDateTime(IFormatProvider provider) - { - throw new InvalidCastException(String.Format(SR.InvalidCast_FromTo, "Single", "DateTime")); - } - - Object IConvertible.ToType(Type type, IFormatProvider provider) - { - return Convert.DefaultToType((IConvertible)this, type, provider); - } - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/System/String.Comparison.cs b/external/corert/src/System.Private.CoreLib/src/System/String.Comparison.cs index 2fb4ee310b..b5d3c1787a 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/String.Comparison.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/String.Comparison.cs @@ -6,6 +6,8 @@ using System.Diagnostics; using System.Globalization; using System.Runtime.CompilerServices; +using Internal.Runtime.CompilerServices; + namespace System { public partial class String @@ -449,10 +451,10 @@ namespace System switch (comparisonType) { case StringComparison.CurrentCulture: - return FormatProvider.Compare(strA, 0, strA.Length, strB, 0, strB.Length); + return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, 0, strA.Length, strB, 0, strB.Length, CompareOptions.None); case StringComparison.CurrentCultureIgnoreCase: - return FormatProvider.CompareIgnoreCase(strA, 0, strA.Length, strB, 0, strB.Length); + return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, 0, strA.Length, strB, 0, strB.Length, CompareOptions.IgnoreCase); case StringComparison.Ordinal: // Most common case: first character is different. @@ -465,7 +467,7 @@ namespace System return CompareOrdinalHelper(strA, strB); case StringComparison.OrdinalIgnoreCase: - return FormatProvider.CompareOrdinalIgnoreCase(strA, 0, strA.Length, strB, 0, strB.Length); + return CompareInfo.CompareOrdinalIgnoreCase(strA, 0, strA.Length, strB, 0, strB.Length); case StringComparison.InvariantCulture: return CultureInfo.InvariantCulture.CompareInfo.Compare(strA, strB, CompareOptions.None); @@ -550,9 +552,8 @@ namespace System lengthB = Math.Min(lengthB, strB.Length - indexB); } - return ignoreCase ? - FormatProvider.CompareIgnoreCase(strA, indexA, lengthA, strB, indexB, lengthB) : - FormatProvider.Compare(strA, indexA, lengthA, strB, indexB, lengthB); + CompareOptions options = ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None; + return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, options); } // Determines whether two string regions match. The substring of strA beginning @@ -640,16 +641,16 @@ namespace System switch (comparisonType) { case StringComparison.CurrentCulture: - return FormatProvider.Compare(strA, indexA, lengthA, strB, indexB, lengthB); + return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.None); case StringComparison.CurrentCultureIgnoreCase: - return FormatProvider.CompareIgnoreCase(strA, indexA, lengthA, strB, indexB, lengthB); + return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.IgnoreCase); case StringComparison.Ordinal: return CompareOrdinalHelper(strA, indexA, lengthA, strB, indexB, lengthB); case StringComparison.OrdinalIgnoreCase: - return FormatProvider.CompareOrdinalIgnoreCase(strA, indexA, lengthA, strB, indexB, lengthB); + return CompareInfo.CompareOrdinalIgnoreCase(strA, indexA, lengthA, strB, indexB, lengthB); case StringComparison.InvariantCulture: return CultureInfo.InvariantCulture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.None); @@ -692,6 +693,25 @@ namespace System return CompareOrdinalHelper(strA, strB); } + // TODO https://github.com/dotnet/corefx/issues/21395: Expose this publicly? + internal static int CompareOrdinal(ReadOnlySpan strA, ReadOnlySpan strB) + { + // TODO: This needs to be optimized / unrolled. It can't just use CompareOrdinalHelper(str, str) + // (changed to accept spans) because its implementation is based on a string layout, + // in a way that doesn't work when there isn't guaranteed to be a null terminator. + + int minLength = Math.Min(strA.Length, strB.Length); + for (int i = 0; i < minLength; i++) + { + if (strA[i] != strB[i]) + { + return strA[i] - strB[i]; + } + } + + return strA.Length - strB.Length; + } + public static int CompareOrdinal(String strA, int indexA, String strB, int indexB, int length) { if (strA == null || strB == null) @@ -802,16 +822,16 @@ namespace System switch (comparisonType) { case StringComparison.CurrentCulture: - return FormatProvider.IsSuffix(this, value); + return CultureInfo.CurrentCulture.CompareInfo.IsSuffix(this, value, CompareOptions.None); case StringComparison.CurrentCultureIgnoreCase: - return FormatProvider.IsSuffixIgnoreCase(this, value); + return CultureInfo.CurrentCulture.CompareInfo.IsSuffix(this, value, CompareOptions.IgnoreCase); case StringComparison.Ordinal: return this.Length < value.Length ? false : (CompareOrdinalHelper(this, this.Length - value.Length, value.Length, value, 0, value.Length) == 0); case StringComparison.OrdinalIgnoreCase: - return this.Length < value.Length ? false : (FormatProvider.CompareOrdinalIgnoreCase(this, this.Length - value.Length, value.Length, value, 0, value.Length) == 0); + return this.Length < value.Length ? false : (CompareInfo.CompareOrdinalIgnoreCase(this, this.Length - value.Length, value.Length, value, 0, value.Length) == 0); case StringComparison.InvariantCulture: return CultureInfo.InvariantCulture.CompareInfo.IsSuffix(this, value, CompareOptions.None); @@ -908,10 +928,10 @@ namespace System switch (comparisonType) { case StringComparison.CurrentCulture: - return (FormatProvider.Compare(this, 0, this.Length, value, 0, value.Length) == 0); + return (CultureInfo.CurrentCulture.CompareInfo.Compare(this, 0, this.Length, value, 0, value.Length, CompareOptions.None) == 0); case StringComparison.CurrentCultureIgnoreCase: - return (FormatProvider.CompareIgnoreCase(this, 0, this.Length, value, 0, value.Length) == 0); + return (CultureInfo.CurrentCulture.CompareInfo.Compare(this, 0, this.Length, value, 0, value.Length, CompareOptions.IgnoreCase) == 0); case StringComparison.Ordinal: if (this.Length != value.Length) @@ -923,7 +943,7 @@ namespace System return false; else { - return FormatProvider.CompareOrdinalIgnoreCase(this, 0, this.Length, value, 0, value.Length) == 0; + return CompareInfo.CompareOrdinalIgnoreCase(this, 0, this.Length, value, 0, value.Length) == 0; } case StringComparison.InvariantCulture: @@ -972,10 +992,10 @@ namespace System switch (comparisonType) { case StringComparison.CurrentCulture: - return (FormatProvider.Compare(a, 0, a.Length, b, 0, b.Length) == 0); + return (CultureInfo.CurrentCulture.CompareInfo.Compare(a, 0, a.Length, b, 0, b.Length, CompareOptions.None) == 0); case StringComparison.CurrentCultureIgnoreCase: - return (FormatProvider.CompareIgnoreCase(a, 0, a.Length, b, 0, b.Length) == 0); + return (CultureInfo.CurrentCulture.CompareInfo.Compare(a, 0, a.Length, b, 0, b.Length, CompareOptions.IgnoreCase) == 0); case StringComparison.Ordinal: if (a.Length != b.Length) @@ -987,7 +1007,7 @@ namespace System return false; else { - return FormatProvider.CompareOrdinalIgnoreCase(a, 0, a.Length, b, 0, b.Length) == 0; + return CompareInfo.CompareOrdinalIgnoreCase(a, 0, a.Length, b, 0, b.Length) == 0; } case StringComparison.InvariantCulture: @@ -1026,6 +1046,10 @@ namespace System return Marvin.ComputeHash32(ref Unsafe.As(ref _firstChar), _stringLength * 2, Marvin.DefaultSeed); } + // Gets a hash code for this string and this comparison. If strings A and B and comparison C are such + // that String.Equals(A, B, C), then they will return the same hash code with this comparison C. + public int GetHashCode(StringComparison comparisonType) => StringComparer.FromComparison(comparisonType).GetHashCode(this); + // Use this if and only if you need the hashcode to not change across app domains (e.g. you have an app domain agile // hash table). internal int GetLegacyNonRandomizedHashCode() @@ -1113,10 +1137,10 @@ namespace System switch (comparisonType) { case StringComparison.CurrentCulture: - return FormatProvider.IsPrefix(this, value); + return CultureInfo.CurrentCulture.CompareInfo.IsPrefix(this, value, CompareOptions.None); case StringComparison.CurrentCultureIgnoreCase: - return FormatProvider.IsPrefixIgnoreCase(this, value); + return CultureInfo.CurrentCulture.CompareInfo.IsPrefix(this, value, CompareOptions.IgnoreCase); case StringComparison.Ordinal: if (this.Length < value.Length || _firstChar != value._firstChar) @@ -1132,7 +1156,7 @@ namespace System { return false; } - return FormatProvider.CompareOrdinalIgnoreCase(this, 0, value.Length, value, 0, value.Length) == 0; + return CompareInfo.CompareOrdinalIgnoreCase(this, 0, value.Length, value, 0, value.Length) == 0; case StringComparison.InvariantCulture: return CultureInfo.InvariantCulture.CompareInfo.IsPrefix(this, value, CompareOptions.None); diff --git a/external/corert/src/System.Private.CoreLib/src/System/String.Manipulation.cs b/external/corert/src/System.Private.CoreLib/src/System/String.Manipulation.cs index e277618bfc..f069b39425 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/String.Manipulation.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/String.Manipulation.cs @@ -5,8 +5,11 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; +using System.Runtime.CompilerServices; using System.Text; +using Internal.Runtime.CompilerServices; + namespace System { public partial class String @@ -138,45 +141,82 @@ namespace System if (values == null) throw new ArgumentNullException(nameof(values)); - using (IEnumerator en = values.GetEnumerator()) + if (typeof(T) == typeof(char)) { - if (!en.MoveNext()) - return string.Empty; - - // We called MoveNext once, so this will be the first item - T currentValue = en.Current; - - // Call ToString before calling MoveNext again, since - // we want to stay consistent with the below loop - // Everything should be called in the order - // MoveNext-Current-ToString, unless further optimizations - // can be made, to avoid breaking changes - string firstString = currentValue?.ToString(); - - // If there's only 1 item, simply call ToString on that - if (!en.MoveNext()) + // Special-case T==char, as we can handle that case much more efficiently, + // and string.Concat(IEnumerable) can be used as an efficient + // enumerable-based equivalent of new string(char[]). + using (IEnumerator en = Unsafe.As>(values).GetEnumerator()) { - // We have to handle the case of either currentValue - // or its ToString being null - return firstString ?? string.Empty; - } - - StringBuilder result = StringBuilderCache.Acquire(); - - result.Append(firstString); - - do - { - currentValue = en.Current; - - if (currentValue != null) + if (!en.MoveNext()) { - result.Append(currentValue.ToString()); + // There weren't any chars. Return the empty string. + return Empty; } - } - while (en.MoveNext()); - return StringBuilderCache.GetStringAndRelease(result); + char c = en.Current; // save the first char + + if (!en.MoveNext()) + { + // There was only one char. Return a string from it directly. + return CreateFromChar(c); + } + + // Create the StringBuilder, add the chars we've already enumerated, + // add the rest, and then get the resulting string. + StringBuilder result = StringBuilderCache.Acquire(); + result.Append(c); // first value + do + { + c = en.Current; + result.Append(c); + } + while (en.MoveNext()); + return StringBuilderCache.GetStringAndRelease(result); + } + } + else + { + using (IEnumerator en = values.GetEnumerator()) + { + if (!en.MoveNext()) + return string.Empty; + + // We called MoveNext once, so this will be the first item + T currentValue = en.Current; + + // Call ToString before calling MoveNext again, since + // we want to stay consistent with the below loop + // Everything should be called in the order + // MoveNext-Current-ToString, unless further optimizations + // can be made, to avoid breaking changes + string firstString = currentValue?.ToString(); + + // If there's only 1 item, simply call ToString on that + if (!en.MoveNext()) + { + // We have to handle the case of either currentValue + // or its ToString being null + return firstString ?? string.Empty; + } + + StringBuilder result = StringBuilderCache.Acquire(); + + result.Append(firstString); + + do + { + currentValue = en.Current; + + if (currentValue != null) + { + result.Append(currentValue.ToString()); + } + } + while (en.MoveNext()); + + return StringBuilderCache.GetStringAndRelease(result); + } } } @@ -877,6 +917,91 @@ namespace System return Substring(0, startIndex); } + public string Replace(string oldValue, string newValue, bool ignoreCase, CultureInfo culture) + { + return ReplaceCore(oldValue, newValue, culture, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None); + } + + public string Replace(string oldValue, string newValue, StringComparison comparisonType) + { + switch (comparisonType) + { + case StringComparison.CurrentCulture: + return ReplaceCore(oldValue, newValue, CultureInfo.CurrentCulture, CompareOptions.None); + + case StringComparison.CurrentCultureIgnoreCase: + return ReplaceCore(oldValue, newValue, CultureInfo.CurrentCulture, CompareOptions.IgnoreCase); + + case StringComparison.InvariantCulture: + return ReplaceCore(oldValue, newValue, CultureInfo.InvariantCulture, CompareOptions.None); + + case StringComparison.InvariantCultureIgnoreCase: + return ReplaceCore(oldValue, newValue, CultureInfo.InvariantCulture, CompareOptions.IgnoreCase); + + case StringComparison.Ordinal: + return ReplaceCore(oldValue, newValue, CultureInfo.InvariantCulture, CompareOptions.Ordinal); + + case StringComparison.OrdinalIgnoreCase: + return ReplaceCore(oldValue, newValue, CultureInfo.InvariantCulture, CompareOptions.OrdinalIgnoreCase); + + default: + throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType)); + } + } + + private unsafe String ReplaceCore(string oldValue, string newValue, CultureInfo culture, CompareOptions options) + { + if (oldValue == null) + throw new ArgumentNullException(nameof(oldValue)); + if (oldValue.Length == 0) + throw new ArgumentException(SR.Argument_StringZeroLength, nameof(oldValue)); + + // If they asked to replace oldValue with a null, replace all occurrences + // with the empty string. + if (newValue == null) + newValue = string.Empty; + + CultureInfo referenceCulture = culture ?? CultureInfo.CurrentCulture; + StringBuilder result = StringBuilderCache.Acquire(); + + int startIndex = 0; + int index = 0; + + int matchLength = 0; + + bool hasDoneAnyReplacements = false; + CompareInfo ci = referenceCulture.CompareInfo; + + do + { + index = ci.IndexOf(this, oldValue, startIndex, _stringLength - startIndex, options, &matchLength); + if (index >= 0) + { + // append the unmodified portion of string + result.Append(this, startIndex, index - startIndex); + + // append the replacement + result.Append(newValue); + + startIndex = index + matchLength; + hasDoneAnyReplacements = true; + } + else if (!hasDoneAnyReplacements) + { + // small optimization, + // if we have not done any replacements, + // we will return the original string + return this; + } + else + { + result.Append(this, startIndex, _stringLength - startIndex); + } + } while (index >= 0); + + return StringBuilderCache.GetStringAndRelease(result); + } + // Replaces all instances of oldChar with newChar. // public String Replace(char oldChar, char newChar) @@ -1516,7 +1641,7 @@ namespace System // Creates a copy of this string in lower case. The culture is set by culture. public String ToLower() { - return FormatProvider.ToLower(this); + return CultureInfo.CurrentCulture.TextInfo.ToLower(this); } // Creates a copy of this string in lower case. The culture is set by culture. @@ -1532,12 +1657,12 @@ namespace System // Creates a copy of this string in lower case based on invariant culture. public String ToLowerInvariant() { - return FormatProvider.ToLowerInvariant(this); + return CultureInfo.InvariantCulture.TextInfo.ToLower(this); } public String ToUpper() { - return FormatProvider.ToUpper(this); + return CultureInfo.CurrentCulture.TextInfo.ToUpper(this); } // Creates a copy of this string in upper case. The culture is set by culture. @@ -1553,7 +1678,7 @@ namespace System //Creates a copy of this string in upper case based on invariant culture. public String ToUpperInvariant() { - return FormatProvider.ToUpperInvariant(this); + return CultureInfo.InvariantCulture.TextInfo.ToUpper(this); } // Trims the whitespace from both ends of the string. Whitespace is defined by diff --git a/external/corert/src/System.Private.CoreLib/src/System/String.cs b/external/corert/src/System.Private.CoreLib/src/System/String.cs index e094fdd88b..076bd02a8c 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/String.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/String.cs @@ -9,16 +9,19 @@ ** ===========================================================*/ +using System.Buffers; using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.Contracts; +using System.Globalization; using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; +using Internal.Runtime.CompilerServices; + namespace System { // The String class represents a static string of characters. Many of @@ -73,6 +76,8 @@ namespace System // [StructLayout(LayoutKind.Sequential)] [System.Runtime.CompilerServices.EagerStaticClassConstructionAttribute] + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed partial class String : IComparable, IEnumerable, IEnumerable, IComparable, IEquatable, IConvertible, ICloneable { #if BIT64 @@ -94,7 +99,9 @@ namespace System // See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details. // Please do not change the type, the name, or the semantic usage of this member without understanding the implication for tools. // Get in touch with the diagnostics team if you have questions. + [NonSerialized] private int _stringLength; + [NonSerialized] private char _firstChar; #pragma warning restore @@ -143,7 +150,6 @@ namespace System if (startIndex > value.Length - length) throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); - Contract.EndContractBlock(); if (length > 0) { @@ -210,7 +216,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex); } - Contract.EndContractBlock(); char* pFrom = ptr + startIndex; if (pFrom < ptr) @@ -391,6 +396,50 @@ namespace System throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NegativeCount); } + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public extern String(ReadOnlySpan value); + + [DependencyReductionRoot] + private unsafe static string Ctor(ReadOnlySpan value) + { + if (value.Length == 0) + { + return Empty; + } + + string result = FastAllocateString(value.Length); + fixed (char* dest = &result._firstChar, src = &MemoryMarshal.GetReference(value)) + { + wstrcpy(dest, src, value.Length); + } + return result; + } + + public static string Create(int length, TState state, SpanAction action) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + if (length > 0) + { + string result = FastAllocateString(length); + action(new Span(ref result.GetRawStringData(), length), state); + return result; + } + + if (length == 0) + { + return Empty; + } + + throw new ArgumentOutOfRangeException(nameof(length)); + } + + public static implicit operator ReadOnlySpan(string value) => + value != null ? new ReadOnlySpan(ref value.GetRawStringData(), value.Length) : default; + public object Clone() { return this; @@ -583,19 +632,12 @@ namespace System internal static String FastAllocateString(int length) { - try - { - // We allocate one extra char as an interop convenience so that our strings are null- - // terminated, however, we don't pass the extra +1 to the array allocation because the base - // size of this object includes the _firstChar field. - string newStr = RuntimeImports.RhNewArrayAsString(EETypePtr.EETypePtrOf(), length); - Debug.Assert(newStr._stringLength == length); - return newStr; - } - catch (OverflowException) - { - throw new OutOfMemoryException(); - } + // We allocate one extra char as an interop convenience so that our strings are null- + // terminated, however, we don't pass the extra +1 to the string allocation because the base + // size of this object includes the _firstChar field. + string newStr = RuntimeImports.RhNewString(EETypePtr.EETypePtrOf(), length); + Debug.Assert(newStr._stringLength == length); + return newStr; } internal static unsafe void wstrcpy(char* dmem, char* smem, int charCount) diff --git a/external/corert/src/System.Private.CoreLib/src/System/Text/CodePageDataItem.cs b/external/corert/src/System.Private.CoreLib/src/System/Text/CodePageDataItem.cs index 65eee6aaa3..53fe5fc3e7 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Text/CodePageDataItem.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Text/CodePageDataItem.cs @@ -4,7 +4,6 @@ namespace System.Text { - [Serializable] internal class CodePageDataItem { public int CodePage { get; } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Text/EncodingData.cs b/external/corert/src/System.Private.CoreLib/src/System/Text/EncodingData.cs index 3ea6ed2e6e..bff11e7441 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Text/EncodingData.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Text/EncodingData.cs @@ -213,9 +213,9 @@ namespace System.Text // private const string s_webNames = "utf-16" + // 1200 - "utf-16be" + // 1201 + "utf-16BE" + // 1201 "utf-32" + // 12000 - "utf-32be" + // 12001 + "utf-32BE" + // 12001 "us-ascii" + // 20127 "iso-8859-1" + // 28591 "utf-7" + // 65000 diff --git a/external/corert/src/System.Private.CoreLib/src/System/Text/EncodingTable.cs b/external/corert/src/System.Private.CoreLib/src/System/Text/EncodingTable.cs index 7c9949c1f8..f9a05d8ca8 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Text/EncodingTable.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Text/EncodingTable.cs @@ -4,7 +4,6 @@ using System.Collections.Concurrent; using System.Diagnostics; -using System.Diagnostics.Contracts; namespace System.Text { @@ -31,7 +30,6 @@ namespace System.Text { if (name == null) throw new ArgumentNullException(nameof(name)); - Contract.EndContractBlock(); return (int)NameToCodePageCache.Instance.GetOrAdd(name); } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Text/Normalization.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/Text/Normalization.Windows.cs deleted file mode 100644 index 521ab8c3d1..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/Text/Normalization.Windows.cs +++ /dev/null @@ -1,130 +0,0 @@ -// 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.Diagnostics; -using System.Diagnostics.Contracts; -using System.Runtime.InteropServices; -using System.Security; -using System.Text; - -namespace System.Text -{ - internal static partial class Normalization - { - public static bool IsNormalized(this string strInput, NormalizationForm normalizationForm) - { - if (strInput == null) - { - throw new ArgumentNullException(nameof(strInput)); - } - Contract.EndContractBlock(); - - // The only way to know if IsNormalizedString failed is through checking the Win32 last error - Interop.mincore.SetLastError(Interop.ERROR_SUCCESS); - bool result = Interop.mincore.IsNormalizedString((int)normalizationForm, strInput, strInput.Length); - - int lastError = Interop.mincore.GetLastError(); - switch (lastError) - { - case Interop.ERROR_SUCCESS: - break; - - case Interop.ERROR_INVALID_PARAMETER: - case Interop.ERROR_NO_UNICODE_TRANSLATION: - throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex, nameof(strInput)); - - case Interop.ERROR_NOT_ENOUGH_MEMORY: - throw new OutOfMemoryException(SR.Arg_OutOfMemoryException); - - default: - throw new InvalidOperationException(SR.Format(SR.UnknownError_Num, lastError)); - } - - return result; - } - - public static string Normalize(this string strInput, NormalizationForm normalizationForm) - { - if (strInput == null) - { - throw new ArgumentNullException(nameof(strInput)); - } - Contract.EndContractBlock(); - - // we depend on Win32 last error when calling NormalizeString - Interop.mincore.SetLastError(Interop.ERROR_SUCCESS); - - // Guess our buffer size first - int iLength = Interop.mincore.NormalizeString((int)normalizationForm, strInput, strInput.Length, null, 0); - - int lastError = Interop.mincore.GetLastError(); - // Could have an error (actually it'd be quite hard to have an error here) - if ((lastError != Interop.ERROR_SUCCESS) || - iLength < 0) - { - if (lastError == Interop.ERROR_INVALID_PARAMETER) - throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex, nameof(strInput)); - - // We shouldn't really be able to get here..., guessing length is - // a trivial math function... - // Can't really be Out of Memory, but just in case: - if (lastError == Interop.ERROR_NOT_ENOUGH_MEMORY) - throw new OutOfMemoryException(SR.Arg_OutOfMemoryException); - - // Who knows what happened? Not us! - throw new InvalidOperationException(SR.Format(SR.UnknownError_Num, lastError)); - } - - // Don't break for empty strings (only possible for D & KD and not really possible at that) - if (iLength == 0) return string.Empty; - - // Someplace to stick our buffer - char[] cBuffer = null; - - for (;;) - { - // (re)allocation buffer and normalize string - cBuffer = new char[iLength]; - - // Reset last error - Interop.mincore.SetLastError(Interop.ERROR_SUCCESS); - iLength = Interop.mincore.NormalizeString((int)normalizationForm, strInput, strInput.Length, cBuffer, cBuffer.Length); - lastError = Interop.mincore.GetLastError(); - - if (lastError == Interop.ERROR_SUCCESS) - break; - - // Could have an error (actually it'd be quite hard to have an error here) - switch (lastError) - { - // Do appropriate stuff for the individual errors: - case Interop.ERROR_INSUFFICIENT_BUFFER: - iLength = Math.Abs(iLength); - Debug.Assert(iLength > cBuffer.Length, "Buffer overflow should have iLength > cBuffer.Length"); - continue; - - case Interop.ERROR_INVALID_PARAMETER: - case Interop.ERROR_NO_UNICODE_TRANSLATION: - // Illegal code point or order found. Ie: FFFE or D800 D800, etc. - throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex, nameof(strInput)); - - case Interop.ERROR_NOT_ENOUGH_MEMORY: - throw new OutOfMemoryException(SR.Arg_OutOfMemoryException); - - default: - // We shouldn't get here... - throw new InvalidOperationException(SR.Format(SR.UnknownError_Num, lastError)); - } - } - - // Copy our buffer into our new string, which will be the appropriate size - return new string(cBuffer, 0, iLength); - } - - // ----------------------------- - // ---- PAL layer ends here ---- - // ----------------------------- - } -} - diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/CancellationToken.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/CancellationToken.cs index 9215de0f54..e3495aeb4f 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/CancellationToken.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/CancellationToken.cs @@ -36,12 +36,12 @@ namespace System.Threading /// /// [DebuggerDisplay("IsCancellationRequested = {IsCancellationRequested}")] - public struct CancellationToken + public readonly struct CancellationToken { // The backing TokenSource. // if null, it implicitly represents the same thing as new CancellationToken(false). // When required, it will be instantiated to reflect this. - private CancellationTokenSource m_source; + private readonly CancellationTokenSource m_source; //!! warning. If more fields are added, the assumptions in CreateLinkedToken may no longer be valid /* Properties */ @@ -110,18 +110,7 @@ namespace System.Threading /// /// The associated CancellationTokenSource has been disposed. - public WaitHandle WaitHandle - { - get - { - if (m_source == null) - { - InitializeDefaultSource(); - } - - return m_source.WaitHandle; - } - } + public WaitHandle WaitHandle => (m_source ?? CancellationTokenSource.InternalGetStaticSource(false)).WaitHandle; // public CancellationToken() // this constructor is implicit for structs @@ -485,16 +474,5 @@ namespace System.Threading { throw new ObjectDisposedException(null, SR.CancellationToken_SourceDisposed); } - - // ----------------------------------- - // Private helpers - - private void InitializeDefaultSource() - { - // Lazy is slower, and although multiple threads may try and set m_source repeatedly, the race is benign. - // Alternative: LazyInititalizer.EnsureInitialized(ref m_source, ()=>CancellationTokenSource.InternalGetStaticSource(false)); - - m_source = CancellationTokenSource.InternalGetStaticSource(false); - } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/CancellationTokenRegistration.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/CancellationTokenRegistration.cs index 961423eb4f..d7180b16d3 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/CancellationTokenRegistration.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/CancellationTokenRegistration.cs @@ -6,7 +6,6 @@ //////////////////////////////////////////////////////////////////////////////// -using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace System.Threading @@ -17,7 +16,7 @@ namespace System.Threading /// /// To unregister a callback, dispose the corresponding Registration instance. /// - public struct CancellationTokenRegistration : IEquatable, IDisposable + public readonly struct CancellationTokenRegistration : IEquatable, IDisposable { private readonly CancellationCallbackInfo m_callbackInfo; private readonly SparselyPopulatedArrayAddInfo m_registrationInfo; @@ -30,6 +29,13 @@ namespace System.Threading m_registrationInfo = registrationInfo; } + /// + /// Gets the with which this registration is associated. If the + /// registration isn't associated with a token (such as after the registration has been disposed), + /// this will return a default token. + /// + public CancellationToken Token => m_callbackInfo?.CancellationTokenSource.Token ?? default(CancellationToken); + /// /// Attempts to deregister the item. If it's already being run, this may fail. /// Entails a full memory fence. diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs index 8752e24791..30302b9368 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs @@ -9,7 +9,6 @@ using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.InteropServices; namespace System.Threading diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.CpuUtilizationReader.Unix.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.CpuUtilizationReader.Unix.cs new file mode 100644 index 0000000000..2d1ad01e7e --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.CpuUtilizationReader.Unix.cs @@ -0,0 +1,16 @@ +// 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. + +namespace System.Threading +{ + internal partial class ClrThreadPool + { + private class CpuUtilizationReader + { + private Interop.Sys.ProcessCpuInformation _cpuInfo; + + public int CurrentUtilization => Interop.Sys.GetCpuUtilization(ref _cpuInfo); // Updates cpuInfo as a side effect for the next call + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.CpuUtilizationReader.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.CpuUtilizationReader.Windows.cs new file mode 100644 index 0000000000..b7d7f8ae45 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.CpuUtilizationReader.Windows.cs @@ -0,0 +1,54 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Threading +{ + internal partial class ClrThreadPool + { + private class CpuUtilizationReader + { + private struct ProcessCpuInformation + { + public long idleTime; + public long kernelTime; + public long userTime; + } + + private ProcessCpuInformation _processCpuInfo = new ProcessCpuInformation(); + + public int CurrentUtilization + { + get + { + if (!Interop.Kernel32.GetSystemTimes(out var idleTime, out var kernelTime, out var userTime)) + { + int error = Marshal.GetLastWin32Error(); + var exception = new OutOfMemoryException(); + exception.SetErrorCode(error); + throw exception; + } + + long cpuTotalTime = ((long)userTime - _processCpuInfo.userTime) + ((long)kernelTime - _processCpuInfo.kernelTime); + long cpuBusyTime = cpuTotalTime - ((long)idleTime - _processCpuInfo.idleTime); + + _processCpuInfo.kernelTime = (long)kernelTime; + _processCpuInfo.userTime = (long)userTime; + _processCpuInfo.idleTime = (long)idleTime; + + if (cpuTotalTime > 0 && cpuBusyTime > 0) + { + long reading = cpuBusyTime * 100 / cpuTotalTime; + reading = Math.Min(reading, 100); + Debug.Assert(0 <= reading); + return (int)reading; + } + return 0; + } + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.GateThread.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.GateThread.cs new file mode 100644 index 0000000000..874e8f9b8c --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.GateThread.cs @@ -0,0 +1,145 @@ +// 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.Diagnostics; +using Internal.Runtime.Augments; + +namespace System.Threading +{ + internal partial class ClrThreadPool + { + private static class GateThread + { + private const int GateThreadDelayMs = 500; + private const int DequeueDelayThresholdMs = GateThreadDelayMs * 2; + private const int GateThreadRunningMask = 0x4; + + private static int s_runningState; + + private static AutoResetEvent s_runGateThreadEvent = new AutoResetEvent(true); + + private static LowLevelLock s_createdLock = new LowLevelLock(); + + private static readonly CpuUtilizationReader s_cpu = new CpuUtilizationReader(); + private const int MaxRuns = 2; + + // TODO: CoreCLR: Worker Tracking in CoreCLR? (Config name: ThreadPool_EnableWorkerTracking) + private static void GateThreadStart() + { + var initialCpuRead = s_cpu.CurrentUtilization; // The first reading is over a time range other than what we are focusing on, so we do not use the read. + + AppContext.TryGetSwitch("System.Threading.ThreadPool.DisableStarvationDetection", out bool disableStarvationDetection); + AppContext.TryGetSwitch("System.Threading.ThreadPool.DebugBreakOnWorkerStarvation", out bool debuggerBreakOnWorkStarvation); + + while (true) + { + s_runGateThreadEvent.WaitOne(); + do + { + RuntimeThread.Sleep(GateThreadDelayMs); + + ThreadPoolInstance._cpuUtilization = s_cpu.CurrentUtilization; + + if (!disableStarvationDetection) + { + if (ThreadPoolInstance._numRequestedWorkers > 0 && SufficientDelaySinceLastDequeue()) + { + try + { + ThreadPoolInstance._hillClimbingThreadAdjustmentLock.Acquire(); + ThreadCounts counts = ThreadCounts.VolatileReadCounts(ref ThreadPoolInstance._separated.counts); + // don't add a thread if we're at max or if we are already in the process of adding threads + while (counts.numExistingThreads < ThreadPoolInstance._maxThreads && counts.numExistingThreads >= counts.numThreadsGoal) + { + if (debuggerBreakOnWorkStarvation) + { + Debugger.Break(); + } + + ThreadCounts newCounts = counts; + newCounts.numThreadsGoal = (short)(newCounts.numExistingThreads + 1); + ThreadCounts oldCounts = ThreadCounts.CompareExchangeCounts(ref ThreadPoolInstance._separated.counts, newCounts, counts); + if (oldCounts == counts) + { + HillClimbing.ThreadPoolHillClimber.ForceChange(newCounts.numThreadsGoal, HillClimbing.StateOrTransition.Starvation); + WorkerThread.MaybeAddWorkingWorker(); + break; + } + counts = oldCounts; + } + } + finally + { + ThreadPoolInstance._hillClimbingThreadAdjustmentLock.Release(); + } + } + } + } while (ThreadPoolInstance._numRequestedWorkers > 0 || Interlocked.Decrement(ref s_runningState) > GetRunningStateForNumRuns(0)); + } + } + + // called by logic to spawn new worker threads, return true if it's been too long + // since the last dequeue operation - takes number of worker threads into account + // in deciding "too long" + private static bool SufficientDelaySinceLastDequeue() + { + int delay = Environment.TickCount - Volatile.Read(ref ThreadPoolInstance._separated.lastDequeueTime); + + int minimumDelay; + + if(ThreadPoolInstance._cpuUtilization < CpuUtilizationLow) + { + minimumDelay = GateThreadDelayMs; + } + else + { + ThreadCounts counts = ThreadCounts.VolatileReadCounts(ref ThreadPoolInstance._separated.counts); + int numThreads = counts.numThreadsGoal; + minimumDelay = numThreads * DequeueDelayThresholdMs; + } + return delay > minimumDelay; + } + + // This is called by a worker thread + internal static void EnsureRunning() + { + int numRunsMask = Interlocked.Exchange(ref s_runningState, GetRunningStateForNumRuns(MaxRuns)); + if ((numRunsMask & GateThreadRunningMask) == 0) + { + bool created = false; + try + { + CreateGateThread(); + created = true; + } + finally + { + if (!created) + { + Interlocked.Exchange(ref s_runningState, 0); + } + } + } + else if (numRunsMask == GetRunningStateForNumRuns(0)) + { + s_runGateThreadEvent.Set(); + } + } + + private static int GetRunningStateForNumRuns(int numRuns) + { + Debug.Assert(numRuns >= 0); + Debug.Assert(numRuns <= MaxRuns); + return GateThreadRunningMask | numRuns; + } + + private static void CreateGateThread() + { + RuntimeThread gateThread = RuntimeThread.Create(GateThreadStart); + gateThread.IsBackground = true; + gateThread.Start(); + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.HillClimbing.Complex.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.HillClimbing.Complex.cs new file mode 100644 index 0000000000..1e07d7187f --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.HillClimbing.Complex.cs @@ -0,0 +1,41 @@ +// 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. + + +namespace System.Threading +{ + internal partial class ClrThreadPool + { + private partial class HillClimbing + { + private struct Complex + { + public Complex(double real, double imaginary) + { + Real = real; + Imaginary = imaginary; + } + + public double Imaginary { get; } + public double Real { get; } + + public static Complex operator*(double scalar, Complex complex) => new Complex(scalar * complex.Real, scalar * complex.Imaginary); + + public static Complex operator*(Complex complex, double scalar) => scalar * complex; + + public static Complex operator/(Complex complex, double scalar) => new Complex(complex.Real / scalar, complex.Imaginary / scalar); + + public static Complex operator-(Complex lhs, Complex rhs) => new Complex(lhs.Real - rhs.Real, lhs.Imaginary - rhs.Imaginary); + + public static Complex operator/(Complex lhs, Complex rhs) + { + double denom = rhs.Real * rhs.Real + rhs.Imaginary * rhs.Imaginary; + return new Complex((lhs.Real * rhs.Real + lhs.Imaginary * rhs.Imaginary) / denom, (-lhs.Real * rhs.Imaginary + lhs.Imaginary * rhs.Real) / denom); + } + + public double Abs() => Math.Sqrt(Real * Real + Imaginary * Imaginary); + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.HillClimbing.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.HillClimbing.cs new file mode 100644 index 0000000000..1ed661931d --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.HillClimbing.cs @@ -0,0 +1,455 @@ +// 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.Diagnostics; +using System.Globalization; + +namespace System.Threading +{ + internal partial class ClrThreadPool + { + /// + /// Hill climbing algorithm used for determining the number of threads needed for the thread pool. + /// + private partial class HillClimbing + { + private static readonly Lazy s_threadPoolHillClimber = new Lazy(CreateHillClimber, true); + public static HillClimbing ThreadPoolHillClimber => s_threadPoolHillClimber.Value; + + private const int DefaultSampleIntervalMsLow = 10; + private const int DefaultSampleIntervalMsHigh = 200; + + private static HillClimbing CreateHillClimber() + { + // Default values pulled from CoreCLR + return new HillClimbing(wavePeriod: AppContextConfigHelper.GetInt32Config("HillClimbing_WavePeriod", 4), + maxWaveMagnitude: AppContextConfigHelper.GetInt32Config("HillClimbing_MaxWaveMagnitude", 20), + waveMagnitudeMultiplier: AppContextConfigHelper.GetInt32Config("HillClimbing_WaveMagnitudeMultiplier", 100) / 100.0, + waveHistorySize: AppContextConfigHelper.GetInt32Config("HillClimbing_WaveHistorySize", 8), + targetThroughputRatio: AppContextConfigHelper.GetInt32Config("HillClimbing_Bias", 15) / 100.0, + targetSignalToNoiseRatio: AppContextConfigHelper.GetInt32Config("HillClimbing_TargetSignalToNoiseRatio", 300) / 100.0, + maxChangePerSecond: AppContextConfigHelper.GetInt32Config("HillClimbing_MaxChangePerSecond", 4), + maxChangePerSample: AppContextConfigHelper.GetInt32Config("HillClimbing_MaxChangePerSample", 20), + sampleIntervalMsLow: AppContextConfigHelper.GetInt32Config("HillClimbing_SampleIntervalLow", DefaultSampleIntervalMsLow, allowNegative: false), + sampleIntervalMsHigh: AppContextConfigHelper.GetInt32Config("HillClimbing_SampleIntervalHigh", DefaultSampleIntervalMsHigh, allowNegative: false), + errorSmoothingFactor: AppContextConfigHelper.GetInt32Config("HillClimbing_ErrorSmoothingFactor", 1) / 100.0, + gainExponent: AppContextConfigHelper.GetInt32Config("HillClimbing_GainExponent", 200) / 100.0, + maxSampleError: AppContextConfigHelper.GetInt32Config("HillClimbing_MaxSampleErrorPercent", 15) / 100.0 + ); + } + private const int LogCapacity = 200; + + public enum StateOrTransition + { + Warmup, + Initializing, + RandomMove, + ClimbingMove, + ChangePoint, + Stabilizing, + Starvation, // Used as a message from the thread pool for a forced transition + ThreadTimedOut, // Usage as a message from the thread pool for a forced transition + } + + private struct LogEntry + { + public int tickCount; + public StateOrTransition stateOrTransition; + public int newControlSetting; + public int lastHistoryCount; + public double lastHistoryMean; + } + + private readonly int _wavePeriod; + private readonly int _samplesToMeasure; + private readonly double _targetThroughputRatio; + private readonly double _targetSignalToNoiseRatio; + private readonly double _maxChangePerSecond; + private readonly double _maxChangePerSample; + private readonly int _maxThreadWaveMagnitude; + private readonly int _sampleIntervalMsLow; + private readonly double _threadMagnitudeMultiplier; + private readonly int _sampleIntervalMsHigh; + private readonly double _throughputErrorSmoothingFactor; + private readonly double _gainExponent; + private readonly double _maxSampleError; + + private double _currentControlSetting; + private long _totalSamples; + private int _lastThreadCount; + private double _averageThroughputNoise; + private double _secondsElapsedSinceLastChange; + private double _completionsSinceLastChange; + private int _accumulatedCompletionCount; + private double _accumulatedSampleDurationSeconds; + private double[] _samples; + private double[] _threadCounts; + private int _currentSampleMs; + + private Random _randomIntervalGenerator = new Random(); + + private LogEntry[] _log = new LogEntry[LogCapacity]; + private int _logStart = 0; + private int _logSize = 0; + + public HillClimbing(int wavePeriod, int maxWaveMagnitude, double waveMagnitudeMultiplier, int waveHistorySize, double targetThroughputRatio, + double targetSignalToNoiseRatio, double maxChangePerSecond, double maxChangePerSample, int sampleIntervalMsLow, int sampleIntervalMsHigh, + double errorSmoothingFactor, double gainExponent, double maxSampleError) + { + _wavePeriod = wavePeriod; + _maxThreadWaveMagnitude = maxWaveMagnitude; + _threadMagnitudeMultiplier = waveMagnitudeMultiplier; + _samplesToMeasure = wavePeriod * waveHistorySize; + _targetThroughputRatio = targetThroughputRatio; + _targetSignalToNoiseRatio = targetSignalToNoiseRatio; + _maxChangePerSecond = maxChangePerSecond; + _maxChangePerSample = maxChangePerSample; + if (sampleIntervalMsLow <= sampleIntervalMsHigh) + { + _sampleIntervalMsLow = sampleIntervalMsLow; + _sampleIntervalMsHigh = sampleIntervalMsHigh; + } + else + { + _sampleIntervalMsLow = DefaultSampleIntervalMsLow; + _sampleIntervalMsHigh = DefaultSampleIntervalMsHigh; + } + _throughputErrorSmoothingFactor = errorSmoothingFactor; + _gainExponent = gainExponent; + _maxSampleError = maxSampleError; + + _samples = new double[_samplesToMeasure]; + _threadCounts = new double[_samplesToMeasure]; + + _currentSampleMs = _randomIntervalGenerator.Next(_sampleIntervalMsLow, _sampleIntervalMsHigh + 1); + } + + public (int newThreadCount, int newSampleMs) Update(int currentThreadCount, double sampleDurationSeconds, int numCompletions) + { + + // + // If someone changed the thread count without telling us, update our records accordingly. + // + if (currentThreadCount != _lastThreadCount) + ForceChange(currentThreadCount, StateOrTransition.Initializing); + + // + // Update the cumulative stats for this thread count + // + _secondsElapsedSinceLastChange += sampleDurationSeconds; + _completionsSinceLastChange += numCompletions; + + // + // Add in any data we've already collected about this sample + // + sampleDurationSeconds += _accumulatedSampleDurationSeconds; + numCompletions += _accumulatedCompletionCount; + + // + // We need to make sure we're collecting reasonably accurate data. Since we're just counting the end + // of each work item, we are goinng to be missing some data about what really happened during the + // sample interval. The count produced by each thread includes an initial work item that may have + // started well before the start of the interval, and each thread may have been running some new + // work item for some time before the end of the interval, which did not yet get counted. So + // our count is going to be off by +/- threadCount workitems. + // + // The exception is that the thread that reported to us last time definitely wasn't running any work + // at that time, and the thread that's reporting now definitely isn't running a work item now. So + // we really only need to consider threadCount-1 threads. + // + // Thus the percent error in our count is +/- (threadCount-1)/numCompletions. + // + // We cannot rely on the frequency-domain analysis we'll be doing later to filter out this error, because + // of the way it accumulates over time. If this sample is off by, say, 33% in the negative direction, + // then the next one likely will be too. The one after that will include the sum of the completions + // we missed in the previous samples, and so will be 33% positive. So every three samples we'll have + // two "low" samples and one "high" sample. This will appear as periodic variation right in the frequency + // range we're targeting, which will not be filtered by the frequency-domain translation. + // + if (_totalSamples > 0 && ((currentThreadCount - 1.0) / numCompletions) >= _maxSampleError) + { + // not accurate enough yet. Let's accumulate the data so far, and tell the ThreadPool + // to collect a little more. + _accumulatedSampleDurationSeconds = sampleDurationSeconds; + _accumulatedCompletionCount = numCompletions; + return (currentThreadCount, 10); + } + + // + // We've got enouugh data for our sample; reset our accumulators for next time. + // + _accumulatedSampleDurationSeconds = 0; + _accumulatedCompletionCount = 0; + + // + // Add the current thread count and throughput sample to our history + // + double throughput = numCompletions / sampleDurationSeconds; + + ClrThreadPoolEventSource.Log.WorkerThreadAdjustmentSample(throughput); + + int sampleIndex = (int)(_totalSamples % _samplesToMeasure); + _samples[sampleIndex] = throughput; + _threadCounts[sampleIndex] = currentThreadCount; + _totalSamples++; + + // + // Set up defaults for our metrics + // + Complex threadWaveComponent = default(Complex); + Complex throughputWaveComponent = default(Complex); + double throughputErrorEstimate = 0; + Complex ratio = default(Complex); + double confidence = 0; + + StateOrTransition state = StateOrTransition.Warmup; + + // + // How many samples will we use? It must be at least the three wave periods we're looking for, and it must also be a whole + // multiple of the primary wave's period; otherwise the frequency we're looking for will fall between two frequency bands + // in the Fourier analysis, and we won't be able to measure it accurately. + // + int sampleCount = ((int)Math.Min(_totalSamples - 1, _samplesToMeasure)) / _wavePeriod * _wavePeriod; + + if (sampleCount > _wavePeriod) + { + // + // Average the throughput and thread count samples, so we can scale the wave magnitudes later. + // + double sampleSum = 0; + double threadSum = 0; + for (int i = 0; i < sampleCount; i++) + { + sampleSum += _samples[(_totalSamples - sampleCount + i) % _samplesToMeasure]; + threadSum += _threadCounts[(_totalSamples - sampleCount + i) % _samplesToMeasure]; + } + double averageThroughput = sampleSum / sampleCount; + double averageThreadCount = threadSum / sampleCount; + + if (averageThroughput > 0 && averageThreadCount > 0) + { + // + // Calculate the periods of the adjacent frequency bands we'll be using to measure noise levels. + // We want the two adjacent Fourier frequency bands. + // + double adjacentPeriod1 = sampleCount / (((double)sampleCount / _wavePeriod) + 1); + double adjacentPeriod2 = sampleCount / (((double)sampleCount / _wavePeriod) - 1); + + // + // Get the the three different frequency components of the throughput (scaled by average + // throughput). Our "error" estimate (the amount of noise that might be present in the + // frequency band we're really interested in) is the average of the adjacent bands. + // + throughputWaveComponent = GetWaveComponent(_samples, sampleCount, _wavePeriod) / averageThroughput; + throughputErrorEstimate = (GetWaveComponent(_samples, sampleCount, adjacentPeriod1) / averageThroughput).Abs(); + if (adjacentPeriod2 <= sampleCount) + { + throughputErrorEstimate = Math.Max(throughputErrorEstimate, (GetWaveComponent(_samples, sampleCount, adjacentPeriod2) / averageThroughput).Abs()); + } + + // + // Do the same for the thread counts, so we have something to compare to. We don't measure thread count + // noise, because there is none; these are exact measurements. + // + threadWaveComponent = GetWaveComponent(_threadCounts, sampleCount, _wavePeriod) / averageThreadCount; + + // + // Update our moving average of the throughput noise. We'll use this later as feedback to + // determine the new size of the thread wave. + // + if (_averageThroughputNoise == 0) + _averageThroughputNoise = throughputErrorEstimate; + else + _averageThroughputNoise = (_throughputErrorSmoothingFactor * throughputErrorEstimate) + ((1.0 - _throughputErrorSmoothingFactor) * _averageThroughputNoise); + + if (threadWaveComponent.Abs() > 0) + { + // + // Adjust the throughput wave so it's centered around the target wave, and then calculate the adjusted throughput/thread ratio. + // + ratio = (throughputWaveComponent - (_targetThroughputRatio * threadWaveComponent)) / threadWaveComponent; + state = StateOrTransition.ClimbingMove; + } + else + { + ratio = new Complex(0, 0); + state = StateOrTransition.Stabilizing; + } + + // + // Calculate how confident we are in the ratio. More noise == less confident. This has + // the effect of slowing down movements that might be affected by random noise. + // + double noiseForConfidence = Math.Max(_averageThroughputNoise, throughputErrorEstimate); + if (noiseForConfidence > 0) + confidence = (threadWaveComponent.Abs() / noiseForConfidence) / _targetSignalToNoiseRatio; + else + confidence = 1.0; //there is no noise! + + } + } + + // + // We use just the real part of the complex ratio we just calculated. If the throughput signal + // is exactly in phase with the thread signal, this will be the same as taking the magnitude of + // the complex move and moving that far up. If they're 180 degrees out of phase, we'll move + // backward (because this indicates that our changes are having the opposite of the intended effect). + // If they're 90 degrees out of phase, we won't move at all, because we can't tell wether we're + // having a negative or positive effect on throughput. + // + double move = Math.Min(1.0, Math.Max(-1.0, ratio.Real)); + + // + // Apply our confidence multiplier. + // + move *= Math.Min(1.0, Math.Max(0.0, confidence)); + + // + // Now apply non-linear gain, such that values around zero are attenuated, while higher values + // are enhanced. This allows us to move quickly if we're far away from the target, but more slowly + // if we're getting close, giving us rapid ramp-up without wild oscillations around the target. + // + double gain = _maxChangePerSecond * sampleDurationSeconds; + move = Math.Pow(Math.Abs(move), _gainExponent) * (move >= 0.0 ? 1 : -1) * gain; + move = Math.Min(move, _maxChangePerSample); + + // + // If the result was positive, and CPU is > 95%, refuse the move. + // + if (move > 0.0 && ThreadPoolInstance._cpuUtilization > CpuUtilizationHigh) + move = 0.0; + + // + // Apply the move to our control setting + // + _currentControlSetting += move; + + // + // Calculate the new thread wave magnitude, which is based on the moving average we've been keeping of + // the throughput error. This average starts at zero, so we'll start with a nice safe little wave at first. + // + int newThreadWaveMagnitude = (int)(0.5 + (_currentControlSetting * _averageThroughputNoise * _targetSignalToNoiseRatio * _threadMagnitudeMultiplier * 2.0)); + newThreadWaveMagnitude = Math.Min(newThreadWaveMagnitude, _maxThreadWaveMagnitude); + newThreadWaveMagnitude = Math.Max(newThreadWaveMagnitude, 1); + + // + // Make sure our control setting is within the ThreadPool's limits + // + int maxThreads = ThreadPoolInstance._maxThreads; + int minThreads = ThreadPoolInstance._minThreads; + + _currentControlSetting = Math.Min(maxThreads - newThreadWaveMagnitude, _currentControlSetting); + _currentControlSetting = Math.Max(minThreads, _currentControlSetting); + + // + // Calculate the new thread count (control setting + square wave) + // + int newThreadCount = (int)(_currentControlSetting + newThreadWaveMagnitude * ((_totalSamples / (_wavePeriod / 2)) % 2)); + + // + // Make sure the new thread count doesn't exceed the ThreadPool's limits + // + newThreadCount = Math.Min(maxThreads, newThreadCount); + newThreadCount = Math.Max(minThreads, newThreadCount); + + // + // Record these numbers for posterity + // + + ClrThreadPoolEventSource.Log.WorkerThreadAdjustmentStats(sampleDurationSeconds, throughput, threadWaveComponent.Real, throughputWaveComponent.Real, + throughputErrorEstimate, _averageThroughputNoise, ratio.Real, confidence, _currentControlSetting, (ushort)newThreadWaveMagnitude); + + + // + // If all of this caused an actual change in thread count, log that as well. + // + if (newThreadCount != currentThreadCount) + ChangeThreadCount(newThreadCount, state); + + // + // Return the new thread count and sample interval. This is randomized to prevent correlations with other periodic + // changes in throughput. Among other things, this prevents us from getting confused by Hill Climbing instances + // running in other processes. + // + // If we're at minThreads, and we seem to be hurting performance by going higher, we can't go any lower to fix this. So + // we'll simply stay at minThreads much longer, and only occasionally try a higher value. + // + int newSampleInterval; + if (ratio.Real < 0.0 && newThreadCount == minThreads) + newSampleInterval = (int)(0.5 + _currentSampleMs * (10.0 * Math.Max(-ratio.Real, 1.0))); + else + newSampleInterval = _currentSampleMs; + + return (newThreadCount, newSampleInterval); + } + + private void ChangeThreadCount(int newThreadCount, StateOrTransition state) + { + _lastThreadCount = newThreadCount; + _currentSampleMs = _randomIntervalGenerator.Next(_sampleIntervalMsLow, _sampleIntervalMsHigh + 1); + double throughput = _secondsElapsedSinceLastChange > 0 ? _completionsSinceLastChange / _secondsElapsedSinceLastChange : 0; + LogTransition(newThreadCount, throughput, state); + _secondsElapsedSinceLastChange = 0; + _completionsSinceLastChange = 0; + } + + private void LogTransition(int newThreadCount, double throughput, StateOrTransition stateOrTransition) + { + // Use the _log array as a circular array for log entries + int index = (_logStart + _logSize) % LogCapacity; + + if(_logSize == LogCapacity) + { + _logStart = (_logStart + 1) % LogCapacity; + _logSize--; // hide this slot while we update it + } + + ref LogEntry entry = ref _log[index]; + + entry.tickCount = Environment.TickCount; + entry.stateOrTransition = stateOrTransition; + entry.newControlSetting = newThreadCount; + entry.lastHistoryCount = ((int)Math.Min(_totalSamples, _samplesToMeasure) / _wavePeriod) * _wavePeriod; + entry.lastHistoryMean = throughput; + + _logSize++; + + ClrThreadPoolEventSource.Log.WorkerThreadAdjustmentAdjustment(throughput, newThreadCount, (int)stateOrTransition); + } + + public void ForceChange(int newThreadCount, StateOrTransition state) + { + if(_lastThreadCount != newThreadCount) + { + _currentControlSetting += newThreadCount - _lastThreadCount; + ChangeThreadCount(newThreadCount, state); + } + } + + private Complex GetWaveComponent(double[] samples, int numSamples, double period) + { + Debug.Assert(numSamples >= period); // can't measure a wave that doesn't fit + Debug.Assert(period >= 2); // can't measure above the Nyquist frequency + Debug.Assert(numSamples <= samples.Length); // can't measure more samples than we have + + // + // Calculate the sinusoid with the given period. + // We're using the Goertzel algorithm for this. See http://en.wikipedia.org/wiki/Goertzel_algorithm. + // + + double w = 2 * Math.PI / period; + double cos = Math.Cos(w); + double coeff = 2 * cos; + double q0 = 0, q1 = 0, q2 = 0; + for(int i = 0; i < numSamples; ++i) + { + q0 = coeff * q1 - q2 + samples[(_totalSamples - numSamples + i) % _samplesToMeasure]; + q2 = q1; + q1 = q0; + } + return new Complex(q1 - q2 * cos, q2 * Math.Sin(w)) / numSamples; + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.ThreadCounts.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.ThreadCounts.cs new file mode 100644 index 0000000000..1f2fe886ee --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.ThreadCounts.cs @@ -0,0 +1,85 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Threading +{ + internal partial class ClrThreadPool + { + /// + /// Tracks information on the number of threads we want/have in different states in our thread pool. + /// + [StructLayout(LayoutKind.Explicit)] + struct ThreadCounts + { + /// + /// Max possible thread pool threads we want to have. + /// + [FieldOffset(0)] + public short numThreadsGoal; + + /// + /// Number of thread pool threads that currently exist. + /// + [FieldOffset(2)] + public short numExistingThreads; + + /// + /// Number of threads processing work items. + /// + [FieldOffset(4)] + public short numProcessingWork; + + [FieldOffset(0)] + private long _asLong; + + public static ThreadCounts VolatileReadCounts(ref ThreadCounts counts) + { + return new ThreadCounts + { + _asLong = Volatile.Read(ref counts._asLong) + }; + } + + public static ThreadCounts CompareExchangeCounts(ref ThreadCounts location, ThreadCounts newCounts, ThreadCounts oldCounts) + { + ThreadCounts result = new ThreadCounts + { + _asLong = Interlocked.CompareExchange(ref location._asLong, newCounts._asLong, oldCounts._asLong) + }; + + if (result == oldCounts) + { + result.Validate(); + newCounts.Validate(); + } + return result; + } + + public static bool operator ==(ThreadCounts lhs, ThreadCounts rhs) => lhs._asLong == rhs._asLong; + + public static bool operator !=(ThreadCounts lhs, ThreadCounts rhs) => lhs._asLong != rhs._asLong; + + public override bool Equals(object obj) + { + return obj is ThreadCounts counts && this._asLong == counts._asLong; + } + + public override int GetHashCode() + { + return (int)(_asLong >> 8) + numThreadsGoal; + } + + private void Validate() + { + Debug.Assert(numThreadsGoal > 0, "Goal must be positive"); + Debug.Assert(numExistingThreads >= 0, "Number of existing threads must be non-zero"); + Debug.Assert(numProcessingWork >= 0, "Number of threads processing work must be non-zero"); + Debug.Assert(numProcessingWork <= numExistingThreads, $"Num processing work ({numProcessingWork}) must be less than or equal to Num existing threads ({numExistingThreads})"); + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.WaitThread.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.WaitThread.cs new file mode 100644 index 0000000000..8a13659737 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.WaitThread.cs @@ -0,0 +1,422 @@ +// 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.Diagnostics; +using Internal.Runtime.Augments; +using System.Runtime.InteropServices; + +namespace System.Threading +{ + internal partial class ClrThreadPool + { + /// + /// A linked list of s. + /// + private WaitThreadNode _waitThreadsHead; + private WaitThreadNode _waitThreadsTail; + + private LowLevelLock _waitThreadLock = new LowLevelLock(); + + /// + /// Register a wait handle on a . + /// + /// A description of the requested registration. + internal void RegisterWaitHandle(RegisteredWaitHandle handle) + { + _waitThreadLock.Acquire(); + try + { + if (_waitThreadsHead == null) // Lazily create the first wait thread. + { + _waitThreadsTail = _waitThreadsHead = new WaitThreadNode + { + Thread = new WaitThread() + }; + } + + // Register the wait handle on the first wait thread that is not at capacity. + WaitThreadNode prev; + WaitThreadNode current = _waitThreadsHead; + do + { + if (current.Thread.RegisterWaitHandle(handle)) + { + return; + } + prev = current; + current = current.Next; + } while (current != null); + + // If all wait threads are full, create a new one. + prev.Next = _waitThreadsTail = new WaitThreadNode + { + Thread = new WaitThread() + }; + prev.Next.Thread.RegisterWaitHandle(handle); + return; + } + finally + { + _waitThreadLock.Release(); + } + } + + /// + /// Attempt to remove the given wait thread from the list. It is only removed if there are no user-provided waits on the thread. + /// + /// The thread to remove. + /// true if the thread was successfully removed; otherwise, false + private bool TryRemoveWaitThread(WaitThread thread) + { + _waitThreadLock.Acquire(); + try + { + if (thread.AnyUserWaits) + { + return false; + } + RemoveWaitThread(thread); + } + finally + { + _waitThreadLock.Release(); + } + return true; + } + + /// + /// Removes the wait thread from the list. + /// + /// The wait thread to remove from the list. + private void RemoveWaitThread(WaitThread thread) + { + if (_waitThreadsHead.Thread == thread) + { + _waitThreadsHead = _waitThreadsHead.Next; + return; + } + + WaitThreadNode prev; + WaitThreadNode current = _waitThreadsHead; + + do + { + prev = current; + current = current.Next; + } while (current != null && current.Thread != thread); + + Debug.Assert(current != null, "The wait thread to remove was not found in the list of thread pool wait threads."); + + if (current != null) + { + prev.Next = current.Next; + } + } + + private class WaitThreadNode + { + public WaitThread Thread { get; set; } + public WaitThreadNode Next { get; set; } + } + + /// + /// A thread pool wait thread. + /// + internal class WaitThread + { + /// + /// The info for a completed wait on a specific . + /// + private struct CompletedWaitHandle + { + public CompletedWaitHandle(RegisteredWaitHandle completedHandle, bool timedOut) + { + CompletedHandle = completedHandle; + TimedOut = timedOut; + } + + public RegisteredWaitHandle CompletedHandle { get; } + public bool TimedOut { get; } + } + + /// + /// The wait handles registered on this wait thread. + /// + private readonly RegisteredWaitHandle[] _registeredWaits = new RegisteredWaitHandle[WaitHandle.MaxWaitHandles - 1]; + /// + /// The raw wait handles to wait on. + /// + /// + /// The zeroth element of this array is always . + /// + private readonly WaitHandle[] _waitHandles = new WaitHandle[WaitHandle.MaxWaitHandles]; + /// + /// The number of user-registered waits on this wait thread. + /// + private int _numUserWaits = 0; + + /// + /// A list of removals of wait handles that are waiting for the wait thread to process. + /// + private readonly RegisteredWaitHandle[] _pendingRemoves = new RegisteredWaitHandle[WaitHandle.MaxWaitHandles - 1]; + /// + /// The number of pending removals. + /// + private int _numPendingRemoves = 0; + + /// + /// An event to notify the wait thread that there are pending adds or removals of wait handles so it needs to wake up. + /// + private readonly AutoResetEvent _changeHandlesEvent = new AutoResetEvent(false); + + internal bool AnyUserWaits => _numUserWaits != 0; + + public WaitThread() + { + _waitHandles[0] = _changeHandlesEvent; + RuntimeThread waitThread = RuntimeThread.Create(WaitThreadStart); + waitThread.IsBackground = true; + waitThread.Start(); + } + + /// + /// The main routine for the wait thread. + /// + private void WaitThreadStart() + { + while (true) + { + ProcessRemovals(); + int numUserWaits = _numUserWaits; + int preWaitTimeMs = Environment.TickCount; + + // Recalculate Timeout + int timeoutDurationMs = Timeout.Infinite; + if (numUserWaits == 0) + { + timeoutDurationMs = ThreadPoolThreadTimeoutMs; + } + else + { + for (int i = 0; i < numUserWaits; i++) + { + if (_registeredWaits[i].IsInfiniteTimeout) + { + continue; + } + + int handleTimeoutDurationMs = _registeredWaits[i].TimeoutTimeMs - preWaitTimeMs; + + if (timeoutDurationMs == Timeout.Infinite) + { + timeoutDurationMs = handleTimeoutDurationMs > 0 ? handleTimeoutDurationMs : 0; + } + else + { + timeoutDurationMs = Math.Min(handleTimeoutDurationMs > 0 ? handleTimeoutDurationMs : 0, timeoutDurationMs); + } + + if (timeoutDurationMs == 0) + { + break; + } + } + } + + int signaledHandleIndex = WaitHandle.WaitAny(_waitHandles, numUserWaits + 1, timeoutDurationMs); + + if (signaledHandleIndex == 0) // If we were woken up for a change in our handles, continue. + { + continue; + } + + RegisteredWaitHandle signaledHandle = signaledHandleIndex != WaitHandle.WaitTimeout ? _registeredWaits[signaledHandleIndex - 1] : null; + + if (signaledHandle != null) + { + QueueWaitCompletion(signaledHandle, false); + } + else + { + if(numUserWaits == 0) + { + if (ThreadPoolInstance.TryRemoveWaitThread(this)) + { + return; + } + } + + int elapsedDurationMs = Environment.TickCount - preWaitTimeMs; // Calculate using relative time to ensure we don't have issues with overflow wraparound + for (int i = 0; i < numUserWaits; i++) + { + RegisteredWaitHandle registeredHandle = _registeredWaits[i]; + int handleTimeoutDurationMs = registeredHandle.TimeoutTimeMs - preWaitTimeMs; + if (elapsedDurationMs >= handleTimeoutDurationMs) + { + QueueWaitCompletion(registeredHandle, true); + } + } + } + } + } + + /// + /// Go through the array and remove those registered wait handles from the + /// and arrays, filling the holes along the way. + /// + private void ProcessRemovals() + { + ThreadPoolInstance._waitThreadLock.Acquire(); + try + { + Debug.Assert(_numPendingRemoves >= 0); + Debug.Assert(_numPendingRemoves <= _pendingRemoves.Length); + Debug.Assert(_numUserWaits >= 0); + Debug.Assert(_numUserWaits <= _registeredWaits.Length); + Debug.Assert(_numPendingRemoves <= _numUserWaits, $"Num removals {_numPendingRemoves} should be less than or equal to num user waits {_numUserWaits}"); + + if (_numPendingRemoves == 0 || _numUserWaits == 0) + { + return; + } + int originalNumUserWaits = _numUserWaits; + int originalNumPendingRemoves = _numPendingRemoves; + + // This is O(N^2), but max(N) = 63 and N will usually be very low + for (int i = 0; i < _numPendingRemoves; i++) + { + for (int j = 0; j < _numUserWaits; j++) + { + if (_pendingRemoves[i] == _registeredWaits[j]) + { + _registeredWaits[j].OnRemoveWait(); + _registeredWaits[j] = _registeredWaits[_numUserWaits - 1]; + _waitHandles[j + 1] = _waitHandles[_numUserWaits]; + _registeredWaits[_numUserWaits - 1] = null; + _waitHandles[_numUserWaits] = null; + --_numUserWaits; + _pendingRemoves[i] = null; + break; + } + } + Debug.Assert(_pendingRemoves[i] == null); + } + _numPendingRemoves = 0; + + Debug.Assert(originalNumUserWaits - originalNumPendingRemoves == _numUserWaits, + $"{originalNumUserWaits} - {originalNumPendingRemoves} == {_numUserWaits}"); + } + finally + { + ThreadPoolInstance._waitThreadLock.Release(); + } + } + + /// + /// Queue a call to on the ThreadPool. + /// + /// The handle that completed. + /// Whether or not the wait timed out. + private void QueueWaitCompletion(RegisteredWaitHandle registeredHandle, bool timedOut) + { + registeredHandle.RequestCallback(); + // If the handle is a repeating handle, set up the next call. Otherwise, remove it from the wait thread. + if (registeredHandle.Repeating) + { + registeredHandle.RestartTimeout(Environment.TickCount); + } + else + { + UnregisterWait(registeredHandle, blocking: false); // We shouldn't block the wait thread on the unregistration. + } + ThreadPool.QueueUserWorkItem(CompleteWait, new CompletedWaitHandle(registeredHandle, timedOut)); + } + + /// + /// Process the completion of a user-registered wait (call the callback). + /// + /// A object representing the wait completion. + private void CompleteWait(object state) + { + CompletedWaitHandle handle = (CompletedWaitHandle)state; + handle.CompletedHandle.PerformCallback(handle.TimedOut); + } + + /// + /// Register a wait handle on this . + /// + /// The handle to register. + /// If the handle was successfully registered on this wait thread. + public bool RegisterWaitHandle(RegisteredWaitHandle handle) + { + ThreadPoolInstance._waitThreadLock.VerifyIsLocked(); + if (_numUserWaits == WaitHandle.MaxWaitHandles - 1) + { + return false; + } + + _registeredWaits[_numUserWaits] = handle; + _waitHandles[_numUserWaits + 1] = handle.Handle; + _numUserWaits++; + + handle.WaitThread = this; + + _changeHandlesEvent.Set(); + return true; + } + + /// + /// Unregisters a wait handle. + /// + /// The handle to unregister. + /// + /// As per CoreCLR's behavior, if the user passes in an invalid + /// into , then the unregistration of the wait handle is blocking. + /// Otherwise, the unregistration of the wait handle is queued on the wait thread. + /// + public void UnregisterWait(RegisteredWaitHandle handle) + { + UnregisterWait(handle, true); + } + + /// + /// Unregister a wait handle. + /// + /// The wait handle to unregister. + /// Should the unregistration block at all. + private void UnregisterWait(RegisteredWaitHandle handle, bool blocking) + { + bool pendingRemoval = false; + // TODO: Optimization: Try to unregister wait directly if it isn't being waited on. + ThreadPoolInstance._waitThreadLock.Acquire(); + try + { + // If this handle is not already pending removal and hasn't already been removed + if (Array.IndexOf(_registeredWaits, handle) != -1 && Array.IndexOf(_pendingRemoves, handle) == -1) + { + _pendingRemoves[_numPendingRemoves++] = handle; + _changeHandlesEvent.Set(); // Tell the wait thread that there are changes pending. + pendingRemoval = true; + } + } + finally + { + ThreadPoolInstance._waitThreadLock.Release(); + } + + if (blocking) + { + if (handle.IsBlocking) + { + handle.WaitForCallbacks(); + } + else if (pendingRemoval) + { + handle.WaitForRemoval(); + } + } + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.WorkerThread.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.WorkerThread.cs new file mode 100644 index 0000000000..507eeb4729 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.WorkerThread.cs @@ -0,0 +1,246 @@ +// 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.Globalization; +using Internal.LowLevelLinq; +using Internal.Runtime.Augments; + +namespace System.Threading +{ + internal partial class ClrThreadPool + { + /// + /// The worker thread infastructure for the CLR thread pool. + /// + private static class WorkerThread + { + /// + /// Semaphore for controlling how many threads are currently working. + /// + private static LowLevelLifoSemaphore s_semaphore = new LowLevelLifoSemaphore(0, MaxPossibleThreadCount); + + private static void WorkerThreadStart() + { + ClrThreadPoolEventSource.Log.WorkerThreadStart(ThreadCounts.VolatileReadCounts(ref ThreadPoolInstance._separated.counts).numExistingThreads); + RuntimeThread currentThread = RuntimeThread.CurrentThread; + while (true) + { + while (WaitForRequest()) + { + if (TakeActiveRequest()) + { + Volatile.Write(ref ThreadPoolInstance._separated.lastDequeueTime, Environment.TickCount); + if (ThreadPoolWorkQueue.Dispatch()) + { + // If the queue runs out of work for us, we need to update the number of working workers to reflect that we are done working for now + RemoveWorkingWorker(); + } + } + else + { + // If we woke up but couldn't find a request, we need to update the number of working workers to reflect that we are done working for now + RemoveWorkingWorker(); + } + } + + ThreadPoolInstance._hillClimbingThreadAdjustmentLock.Acquire(); + try + { + // At this point, the thread's wait timed out. We are shutting down this thread. + // We are going to decrement the number of exisiting threads to no longer include this one + // and then change the max number of threads in the thread pool to reflect that we don't need as many + // as we had. Finally, we are going to tell hill climbing that we changed the max number of threads. + ThreadCounts counts = ThreadCounts.VolatileReadCounts(ref ThreadPoolInstance._separated.counts); + while (true) + { + if (counts.numExistingThreads == counts.numProcessingWork) + { + // In this case, enough work came in that this thread should not time out and should go back to work. + break; + } + + ThreadCounts newCounts = counts; + newCounts.numExistingThreads--; + newCounts.numThreadsGoal = Math.Max(ThreadPoolInstance._minThreads, Math.Min(newCounts.numExistingThreads, newCounts.numThreadsGoal)); + ThreadCounts oldCounts = ThreadCounts.CompareExchangeCounts(ref ThreadPoolInstance._separated.counts, newCounts, counts); + if (oldCounts == counts) + { + HillClimbing.ThreadPoolHillClimber.ForceChange(newCounts.numThreadsGoal, HillClimbing.StateOrTransition.ThreadTimedOut); + ClrThreadPoolEventSource.Log.WorkerThreadStop(newCounts.numExistingThreads); + return; + } + } + } + finally + { + ThreadPoolInstance._hillClimbingThreadAdjustmentLock.Release(); + } + } + } + + /// + /// Waits for a request to work. + /// + /// If this thread was woken up before it timed out. + private static bool WaitForRequest() + { + ClrThreadPoolEventSource.Log.WorkerThreadWait(ThreadCounts.VolatileReadCounts(ref ThreadPoolInstance._separated.counts).numExistingThreads); + return s_semaphore.Wait(ThreadPoolThreadTimeoutMs); + } + + /// + /// Reduce the number of working workers by one, but maybe add back a worker (possibily this thread) if a thread request comes in while we are marking this thread as not working. + /// + private static void RemoveWorkingWorker() + { + ThreadCounts currentCounts = ThreadCounts.VolatileReadCounts(ref ThreadPoolInstance._separated.counts); + while (true) + { + ThreadCounts newCounts = currentCounts; + newCounts.numProcessingWork--; + ThreadCounts oldCounts = ThreadCounts.CompareExchangeCounts(ref ThreadPoolInstance._separated.counts, newCounts, currentCounts); + + if (oldCounts == currentCounts) + { + break; + } + currentCounts = oldCounts; + } + + // It's possible that we decided we had thread requests just before a request came in, + // but reduced the worker count *after* the request came in. In this case, we might + // miss the notification of a thread request. So we wake up a thread (maybe this one!) + // if there is work to do. + if (ThreadPoolInstance._numRequestedWorkers > 0) + { + MaybeAddWorkingWorker(); + } + } + + internal static void MaybeAddWorkingWorker() + { + ThreadCounts counts = ThreadCounts.VolatileReadCounts(ref ThreadPoolInstance._separated.counts); + ThreadCounts newCounts; + while (true) + { + newCounts = counts; + newCounts.numProcessingWork = Math.Max(counts.numProcessingWork, Math.Min((short)(counts.numProcessingWork + 1), counts.numThreadsGoal)); + newCounts.numExistingThreads = Math.Max(counts.numExistingThreads, newCounts.numProcessingWork); + + if (newCounts == counts) + { + return; + } + + ThreadCounts oldCounts = ThreadCounts.CompareExchangeCounts(ref ThreadPoolInstance._separated.counts, newCounts, counts); + + if (oldCounts == counts) + { + break; + } + + counts = oldCounts; + } + + int toCreate = newCounts.numExistingThreads - counts.numExistingThreads; + int toRelease = newCounts.numProcessingWork - counts.numProcessingWork; + + if (toRelease > 0) + { + s_semaphore.Release(toRelease); + } + + while (toCreate > 0) + { + if (TryCreateWorkerThread()) + { + toCreate--; + } + else + { + counts = ThreadCounts.VolatileReadCounts(ref ThreadPoolInstance._separated.counts); + while (true) + { + newCounts = counts; + newCounts.numProcessingWork -= (short)toCreate; + newCounts.numExistingThreads -= (short)toCreate; + + ThreadCounts oldCounts = ThreadCounts.CompareExchangeCounts(ref ThreadPoolInstance._separated.counts, newCounts, counts); + if(oldCounts == counts) + { + break; + } + counts = oldCounts; + } + toCreate = 0; + } + } + } + + /// + /// Returns if the current thread should stop processing work on the thread pool. + /// A thread should stop processing work on the thread pool when work remains only when + /// there are more worker threads in the thread pool than we currently want. + /// + /// Whether or not this thread should stop processing work even if there is still work in the queue. + internal static bool ShouldStopProcessingWorkNow() + { + ThreadCounts counts = ThreadCounts.VolatileReadCounts(ref ThreadPoolInstance._separated.counts); + while (true) + { + if (counts.numExistingThreads <= counts.numThreadsGoal) + { + return false; + } + + ThreadCounts newCounts = counts; + newCounts.numProcessingWork--; + + ThreadCounts oldCounts = ThreadCounts.CompareExchangeCounts(ref ThreadPoolInstance._separated.counts, newCounts, counts); + + if (oldCounts == counts) + { + return true; + } + counts = oldCounts; + } + } + + private static bool TakeActiveRequest() + { + int count = ThreadPoolInstance._numRequestedWorkers; + while (count > 0) + { + int prevCount = Interlocked.CompareExchange(ref ThreadPoolInstance._numRequestedWorkers, count - 1, count); + if (prevCount == count) + { + return true; + } + count = prevCount; + } + return false; + } + + private static bool TryCreateWorkerThread() + { + try + { + RuntimeThread workerThread = RuntimeThread.Create(WorkerThreadStart); + workerThread.IsThreadPoolThread = true; + workerThread.IsBackground = true; + workerThread.Start(); + } + catch (ThreadStartException) + { + return false; + } + catch (OutOfMemoryException) + { + return false; + } + return true; + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.cs new file mode 100644 index 0000000000..2d1993a625 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPool.cs @@ -0,0 +1,288 @@ +// 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.Runtime.InteropServices; + +namespace System.Threading +{ + /// + /// A thread-pool run and managed on the CLR. + /// + internal partial class ClrThreadPool + { +#pragma warning disable IDE1006 // Naming Styles + public static readonly ClrThreadPool ThreadPoolInstance = new ClrThreadPool(); +#pragma warning restore IDE1006 // Naming Styles + + private const int ThreadPoolThreadTimeoutMs = 20 * 1000; // If you change this make sure to change the timeout times in the tests. + + private const short MaxPossibleThreadCount = short.MaxValue; + + private const int CpuUtilizationHigh = 95; + private const int CpuUtilizationLow = 80; + private int _cpuUtilization = 0; + + + private static readonly short s_forcedMinWorkerThreads = AppContextConfigHelper.GetInt16Config("System.Threading.ThreadPool.MinThreads", 0); + private static readonly short s_forcedMaxWorkerThreads = AppContextConfigHelper.GetInt16Config("System.Threading.ThreadPool.MaxThreads", 0); + + private short _minThreads = (short)ThreadPoolGlobals.processorCount; + private short _maxThreads = MaxPossibleThreadCount; + private readonly LowLevelLock _maxMinThreadLock = new LowLevelLock(); + + [StructLayout(LayoutKind.Explicit, Size = CacheLineSize * 5)] + private struct CacheLineSeparated + { +#if ARM64 + private const int CacheLineSize = 128; +#else + private const int CacheLineSize = 64; +#endif + [FieldOffset(CacheLineSize * 1)] + public ThreadCounts counts; + [FieldOffset(CacheLineSize * 2)] + public int lastDequeueTime; + [FieldOffset(CacheLineSize * 3)] + public int priorCompletionCount; + [FieldOffset(CacheLineSize * 3 + sizeof(int))] + public int priorCompletedWorkRequestsTime; + [FieldOffset(CacheLineSize * 3 + sizeof(int) * 2)] + public int nextCompletedWorkRequestsTime; + } + + private CacheLineSeparated _separated; + private ulong _currentSampleStartTime; + private int _completionCount = 0; + private int _threadAdjustmentIntervalMs; + + private LowLevelLock _hillClimbingThreadAdjustmentLock = new LowLevelLock(); + + private volatile int _numRequestedWorkers = 0; + + private ClrThreadPool() + { + _separated = new CacheLineSeparated + { + counts = new ThreadCounts + { + numThreadsGoal = s_forcedMinWorkerThreads > 0 ? s_forcedMinWorkerThreads : _minThreads + } + }; + } + + public bool SetMinThreads(int minThreads) + { + _maxMinThreadLock.Acquire(); + try + { + if (minThreads < 0 || minThreads > _maxThreads) + { + return false; + } + else + { + short threads = (short)Math.Min(minThreads, MaxPossibleThreadCount); + if (s_forcedMinWorkerThreads == 0) + { + _minThreads = threads; + + ThreadCounts counts = ThreadCounts.VolatileReadCounts(ref _separated.counts); + while (counts.numThreadsGoal < _minThreads) + { + ThreadCounts newCounts = counts; + newCounts.numThreadsGoal = _minThreads; + + ThreadCounts oldCounts = ThreadCounts.CompareExchangeCounts(ref _separated.counts, newCounts, counts); + if (oldCounts == counts) + { + counts = newCounts; + + if (newCounts.numThreadsGoal > oldCounts.numThreadsGoal && _numRequestedWorkers > 0) + { + WorkerThread.MaybeAddWorkingWorker(); + } + } + else + { + counts = oldCounts; + } + } + } + return true; + } + } + finally + { + _maxMinThreadLock.Release(); + } + } + + public int GetMinThreads() => _minThreads; + + public bool SetMaxThreads(int maxThreads) + { + _maxMinThreadLock.Acquire(); + try + { + if (maxThreads < _minThreads || maxThreads == 0) + { + return false; + } + else + { + short threads = (short)Math.Min(maxThreads, MaxPossibleThreadCount); + if (s_forcedMaxWorkerThreads == 0) + { + _maxThreads = threads; + + ThreadCounts counts = ThreadCounts.VolatileReadCounts(ref _separated.counts); + while (counts.numThreadsGoal > _maxThreads) + { + ThreadCounts newCounts = counts; + newCounts.numThreadsGoal = _maxThreads; + + ThreadCounts oldCounts = ThreadCounts.CompareExchangeCounts(ref _separated.counts, newCounts, counts); + if (oldCounts == counts) + { + counts = newCounts; + } + else + { + counts = oldCounts; + } + } + } + return true; + } + } + finally + { + _maxMinThreadLock.Release(); + } + } + + public int GetMaxThreads() => _maxThreads; + + public int GetAvailableThreads() + { + ThreadCounts counts = ThreadCounts.VolatileReadCounts(ref _separated.counts); + int count = _maxThreads - counts.numExistingThreads; + if (count < 0) + { + return 0; + } + return count; + } + + internal bool NotifyWorkItemComplete() + { + // TODO: Check perf. Might need to make this thread-local. + Interlocked.Increment(ref _completionCount); + Volatile.Write(ref _separated.lastDequeueTime, Environment.TickCount); + + if (ShouldAdjustMaxWorkersActive()) + { + bool acquiredLock = _hillClimbingThreadAdjustmentLock.TryAcquire(); + try + { + if (acquiredLock) + { + AdjustMaxWorkersActive(); + } + } + finally + { + if (acquiredLock) + { + _hillClimbingThreadAdjustmentLock.Release(); + } + } + } + + return !WorkerThread.ShouldStopProcessingWorkNow(); + } + + // + // This method must only be called if ShouldAdjustMaxWorkersActive has returned true, *and* + // _hillClimbingThreadAdjustmentLock is held. + // + private void AdjustMaxWorkersActive() + { + _hillClimbingThreadAdjustmentLock.VerifyIsLocked(); + int currentTicks = Environment.TickCount; + int totalNumCompletions = Volatile.Read(ref _completionCount); + int numCompletions = totalNumCompletions - _separated.priorCompletionCount; + ulong startTime = _currentSampleStartTime; + ulong endTime = HighPerformanceCounter.TickCount; + ulong freq = HighPerformanceCounter.Frequency; + + double elapsedSeconds = (double)(endTime - startTime) / freq; + + if(elapsedSeconds * 1000 >= _threadAdjustmentIntervalMs / 2) + { + ThreadCounts currentCounts = ThreadCounts.VolatileReadCounts(ref _separated.counts); + int newMax; + (newMax, _threadAdjustmentIntervalMs) = HillClimbing.ThreadPoolHillClimber.Update(currentCounts.numThreadsGoal, elapsedSeconds, numCompletions); + + while(newMax != currentCounts.numThreadsGoal) + { + ThreadCounts newCounts = currentCounts; + newCounts.numThreadsGoal = (short)newMax; + + ThreadCounts oldCounts = ThreadCounts.CompareExchangeCounts(ref _separated.counts, newCounts, currentCounts); + if (oldCounts == currentCounts) + { + // + // If we're increasing the max, inject a thread. If that thread finds work, it will inject + // another thread, etc., until nobody finds work or we reach the new maximum. + // + // If we're reducing the max, whichever threads notice this first will sleep and timeout themselves. + // + if (newMax > oldCounts.numThreadsGoal) + { + WorkerThread.MaybeAddWorkingWorker(); + } + break; + } + else + { + if(oldCounts.numThreadsGoal > currentCounts.numThreadsGoal && oldCounts.numThreadsGoal >= newMax) + { + // someone (probably the gate thread) increased the thread count more than + // we are about to do. Don't interfere. + break; + } + + currentCounts = oldCounts; + } + } + _separated.priorCompletionCount = totalNumCompletions; + _separated.nextCompletedWorkRequestsTime = currentTicks + _threadAdjustmentIntervalMs; + Volatile.Write(ref _separated.priorCompletedWorkRequestsTime, currentTicks); + _currentSampleStartTime = endTime; + } + } + + private bool ShouldAdjustMaxWorkersActive() + { + // We need to subtract by prior time because Environment.TickCount can wrap around, making a comparison of absolute times unreliable. + int priorTime = Volatile.Read(ref _separated.priorCompletedWorkRequestsTime); + int requiredInterval = _separated.nextCompletedWorkRequestsTime - priorTime; + int elapsedInterval = Environment.TickCount - priorTime; + if(elapsedInterval >= requiredInterval) + { + ThreadCounts counts = ThreadCounts.VolatileReadCounts(ref _separated.counts); + return counts.numExistingThreads >= counts.numThreadsGoal; + } + return false; + } + + internal void RequestWorker() + { + Interlocked.Increment(ref _numRequestedWorkers); + WorkerThread.MaybeAddWorkingWorker(); + GateThread.EnsureRunning(); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPoolEventSource.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPoolEventSource.cs new file mode 100644 index 0000000000..5a8e4ce03b --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/ClrThreadPoolEventSource.cs @@ -0,0 +1,113 @@ +// 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.Diagnostics.Tracing; + +namespace System.Threading +{ + [EventSource(Name = "Microsoft-Windows-DotNETRuntime", Guid = "{e13c0d23-ccbc-4e12-931b-d9cc2eee27e4}")] + public sealed class ClrThreadPoolEventSource : EventSource + { + private const string WorkerThreadMessage = "WorkerThreadCount=%1"; + private const string WorkerThreadAdjustmentSampleMessage = "Throughput=%1"; + private const string WorkerThreadAdjustmentAdjustmentEventMessage = "AverageThroughput=%1;%nNewWorkerThreadCount=%2;%nReason=%3"; + private const string WorkerThreadAdjustmentStatsEventMessage = "Duration=%1;%nThroughput=%2;%nThreadWave=%3;%nThroughputWave=%4;%nThroughputErrorEstimate=%5;%nAverageThroughputErrorEstimate=%6;%nThroughputRatio=%7;%nConfidence=%8;%nNewControlSetting=%9;%nNewThreadWaveMagnitude=%10"; + + // We have these constants here because EventTask, EventOpcode, and EventKeywords are public APIs, and we don't want to expose these values to the end user to use in their events. + private const EventTask WorkerThreadTask = (EventTask)16; + private const EventTask WorkerThreadAdjustmentTask = (EventTask)18; + + private const EventOpcode WaitOpcode = (EventOpcode)90; + private const EventOpcode SampleOpcode = (EventOpcode)100; + private const EventOpcode AdjustmentOpcode = (EventOpcode)101; + private const EventOpcode StatsOpcode = (EventOpcode)102; + private const EventKeywords ThreadingKeyword = (EventKeywords)0x10000; + + private ClrThreadPoolEventSource() + { + } + + [Event(1, Level = EventLevel.Informational, Message = WorkerThreadMessage, Task = WorkerThreadTask, Opcode = EventOpcode.Start, Version = 0, Keywords = ThreadingKeyword)] + public void WorkerThreadStart(short numExistingThreads) + { + WriteEvent(1, numExistingThreads); + } + + [Event(2, Level = EventLevel.Informational, Message = WorkerThreadMessage, Task = WorkerThreadTask, Opcode = EventOpcode.Stop, Version = 0, Keywords = ThreadingKeyword)] + public void WorkerThreadStop(short numExistingThreads) + { + WriteEvent(2, numExistingThreads); + } + + [Event(3, Level = EventLevel.Informational, Message = WorkerThreadMessage, Task = WorkerThreadTask, Opcode = WaitOpcode, Version = 0, Keywords = ThreadingKeyword)] + public void WorkerThreadWait(short numExistingThreads) + { + WriteEvent(3, numExistingThreads); + } + + [Event(4, Level = EventLevel.Informational, Message = WorkerThreadAdjustmentSampleMessage, Opcode = SampleOpcode, Version = 0, Task = WorkerThreadAdjustmentTask, Keywords = ThreadingKeyword)] + public unsafe void WorkerThreadAdjustmentSample(double throughput) + { + if (IsEnabled()) + { + EventData* data = stackalloc EventData[1]; + data[0].DataPointer = (IntPtr)(&throughput); + data[0].Size = sizeof(double); + WriteEventCore(4, 1, data); + } + } + + [Event(5, Level = EventLevel.Informational, Message = WorkerThreadAdjustmentSampleMessage, Opcode = AdjustmentOpcode, Version = 0, Task = WorkerThreadAdjustmentTask, Keywords = ThreadingKeyword)] + public unsafe void WorkerThreadAdjustmentAdjustment(double averageThroughput, int newWorkerThreadCount, int stateOrTransition) + { + if (IsEnabled()) + { + EventData* data = stackalloc EventData[3]; + data[0].DataPointer = (IntPtr)(&averageThroughput); + data[0].Size = sizeof(double); + data[1].DataPointer = (IntPtr)(&newWorkerThreadCount); + data[1].Size = sizeof(int); + data[2].DataPointer = (IntPtr)(&stateOrTransition); + data[2].Size = sizeof(int); + WriteEventCore(5, 3, data); + } + } + + [Event(6, Level = EventLevel.Verbose, Message = WorkerThreadAdjustmentSampleMessage, Opcode = StatsOpcode, Version = 0, Task = WorkerThreadAdjustmentTask, Keywords = ThreadingKeyword)] + [CLSCompliant(false)] + public unsafe void WorkerThreadAdjustmentStats(double duration, double throughput, double threadWave, double throughputWave, double throughputErrorEstimate, + double averageThroughputNoise, double ratio, double confidence, double currentControlSetting, ushort newThreadWaveMagnitude) + { + if (IsEnabled()) + { + EventData* data = stackalloc EventData[10]; + data[0].DataPointer = (IntPtr)(&duration); + data[0].Size = sizeof(double); + data[1].DataPointer = (IntPtr)(&throughput); + data[1].Size = sizeof(double); + data[2].DataPointer = (IntPtr)(&threadWave); + data[2].Size = sizeof(double); + data[3].DataPointer = (IntPtr)(&throughputWave); + data[3].Size = sizeof(double); + data[4].DataPointer = (IntPtr)(&throughputErrorEstimate); + data[4].Size = sizeof(double); + data[5].DataPointer = (IntPtr)(&averageThroughputNoise); + data[5].Size = sizeof(double); + data[6].DataPointer = (IntPtr)(&ratio); + data[6].Size = sizeof(double); + data[7].DataPointer = (IntPtr)(&confidence); + data[7].Size = sizeof(double); + data[8].DataPointer = (IntPtr)(¤tControlSetting); + data[8].Size = sizeof(double); + data[9].DataPointer = (IntPtr)(&newThreadWaveMagnitude); + data[9].Size = sizeof(ushort); + WriteEventCore(6, 10, data); + } + } + +#pragma warning disable IDE1006 // Naming Styles + public static readonly ClrThreadPoolEventSource Log = new ClrThreadPoolEventSource(); +#pragma warning restore IDE1006 // Naming Styles + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/Condition.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/Condition.cs index 8cfd936bb7..b854aa9467 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/Condition.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/Condition.cs @@ -44,7 +44,7 @@ namespace System.Threading for (Waiter current = _waitersHead; current != null; current = current.next) if (current == waiter) return; - Debug.Assert(false, "Waiter is not in the waiter list"); + Debug.Fail("Waiter is not in the waiter list"); } private unsafe void AssertIsNotInList(Waiter waiter) @@ -54,7 +54,7 @@ namespace System.Threading for (Waiter current = _waitersHead; current != null; current = current.next) if (current == waiter) - Debug.Assert(false, "Waiter is in the waiter list, but should not be"); + Debug.Fail("Waiter is in the waiter list, but should not be"); } private unsafe void AddWaiter(Waiter waiter) diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/EventWaitHandle.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/EventWaitHandle.Windows.cs index 0fa14ffefb..28b938ab29 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/EventWaitHandle.Windows.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/EventWaitHandle.Windows.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; @@ -77,8 +76,6 @@ namespace System.Threading throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, Interop.Constants.MaxPath), nameof(name)); } - Contract.EndContractBlock(); - result = null; SafeWaitHandle myHandle = Interop.mincore.OpenEvent(AccessRights, false, name); diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/EventWaitHandle.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/EventWaitHandle.cs index 046b106cda..04026d587c 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/EventWaitHandle.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/EventWaitHandle.cs @@ -15,7 +15,6 @@ using System; using System.Runtime.InteropServices; -using System.Diagnostics.Contracts; using Microsoft.Win32.SafeHandles; using System.IO; using System.Diagnostics; @@ -27,7 +26,6 @@ namespace System.Threading public EventWaitHandle(bool initialState, EventResetMode mode) { VerifyMode(mode); - Contract.EndContractBlock(); bool createdNew; CreateEventCore(initialState, mode, null, out createdNew); @@ -37,7 +35,6 @@ namespace System.Threading { VerifyNameForCreate(name); VerifyMode(mode); - Contract.EndContractBlock(); bool createdNew; CreateEventCore(initialState, mode, name, out createdNew); @@ -47,7 +44,6 @@ namespace System.Threading { VerifyNameForCreate(name); VerifyMode(mode); - Contract.EndContractBlock(); CreateEventCore(initialState, mode, name, out createdNew); } @@ -125,5 +121,10 @@ namespace System.Threading waitHandle.DangerousRelease(); } } + + internal static bool Set(IntPtr handle) + { + return SetCore(handle); + } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/Interlocked.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/Interlocked.cs index 5cce895c09..e07fbf97ba 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/Interlocked.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/Interlocked.cs @@ -6,227 +6,13 @@ using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.Versioning; +using Internal.Runtime.CompilerServices; + namespace System.Threading { public static class Interlocked { -#if CORERT - - #region CompareExchange - - [Intrinsic] - public static int CompareExchange(ref int location1, int value, int comparand) - { - return RuntimeImports.InterlockedCompareExchange(ref location1, value, comparand); - } - - [Intrinsic] - public static long CompareExchange(ref long location1, long value, long comparand) - { - return RuntimeImports.InterlockedCompareExchange(ref location1, value, comparand); - } - - [Intrinsic] - public static IntPtr CompareExchange(ref IntPtr location1, IntPtr value, IntPtr comparand) - { - return RuntimeImports.InterlockedCompareExchange(ref location1, value, comparand); - } - - [Intrinsic] - public static unsafe float CompareExchange(ref float location1, float value, float comparand) - { - float ret; - *(int*)&ret = CompareExchange(ref Unsafe.As(ref location1), *(int*)&value, *(int*)&comparand); - return ret; - } - - [Intrinsic] - public static unsafe double CompareExchange(ref double location1, double value, double comparand) - { - double ret; - *(long*)&ret = CompareExchange(ref Unsafe.As(ref location1), *(long*)&value, *(long*)&comparand); - return ret; - } - - [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T CompareExchange(ref T location1, T value, T comparand) where T : class - { - return Unsafe.As(RuntimeImports.InterlockedCompareExchange(ref Unsafe.As(ref location1), value, comparand)); - } - - [Intrinsic] - public static object CompareExchange(ref object location1, object value, object comparand) - { - return RuntimeImports.InterlockedCompareExchange(ref location1, value, comparand); - } - - #endregion - - #region Exchange - - [Intrinsic] - public static int Exchange(ref int location1, int value) - { - int oldValue; - - do - { - oldValue = location1; - } while (CompareExchange(ref location1, value, oldValue) != oldValue); - - return oldValue; - } - - [Intrinsic] - public static long Exchange(ref long location1, long value) - { - long oldValue; - - do - { - oldValue = location1; - } while (CompareExchange(ref location1, value, oldValue) != oldValue); - - return oldValue; - } - - [Intrinsic] - public static IntPtr Exchange(ref IntPtr location1, IntPtr value) - { - IntPtr oldValue; - - do - { - oldValue = location1; - } while (CompareExchange(ref location1, value, oldValue) != oldValue); - - return oldValue; - } - - [Intrinsic] - public static unsafe float Exchange(ref float location1, float value) - { - float ret; - *(int*)&ret = Exchange(ref Unsafe.As(ref location1), *(int*)&value); - return ret; - } - - [Intrinsic] - public static unsafe double Exchange(ref double location1, double value) - { - double ret; - *(long*)&ret = Exchange(ref Unsafe.As(ref location1), *(long*)&value); - return ret; - } - - [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T Exchange(ref T location1, T value) where T : class - { - return Unsafe.As(RuntimeImports.InterlockedExchange(ref Unsafe.As(ref location1), value)); - } - - [Intrinsic] - public static object Exchange(ref object location1, object value) - { - return RuntimeImports.InterlockedExchange(ref location1, value); - } - - #endregion - - #region Increment - - [Intrinsic] - public static int Increment(ref int location) - { - return ExchangeAdd(ref location, 1) + 1; - } - - [Intrinsic] - public static long Increment(ref long location) - { - return ExchangeAdd(ref location, 1) + 1; - } - - #endregion - - #region Decrement - - [Intrinsic] - public static int Decrement(ref int location) - { - return ExchangeAdd(ref location, -1) - 1; - } - - [Intrinsic] - public static long Decrement(ref long location) - { - return ExchangeAdd(ref location, -1) - 1; - } - - #endregion - - #region Add - - [Intrinsic] - public static int Add(ref int location1, int value) - { - return ExchangeAdd(ref location1, value) + value; - } - - [Intrinsic] - public static long Add(ref long location1, long value) - { - return ExchangeAdd(ref location1, value) + value; - } - - [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int ExchangeAdd(ref int location1, int value) - { - int oldValue; - - do - { - oldValue = location1; - } while (CompareExchange(ref location1, oldValue + value, oldValue) != oldValue); - - return oldValue; - } - - [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static long ExchangeAdd(ref long location1, long value) - { - long oldValue; - - do - { - oldValue = location1; - } while (CompareExchange(ref location1, oldValue + value, oldValue) != oldValue); - - return oldValue; - } - - #endregion - - #region MemoryBarrier - [Intrinsic] - public static void MemoryBarrier() - { - RuntimeImports.MemoryBarrier(); - } - #endregion - - #region Read - public static long Read(ref long location) - { - return CompareExchange(ref location, 0, 0); - } - #endregion - -#else // CORERT +#if PROJECTN #region CompareExchange @@ -494,7 +280,223 @@ namespace System.Threading } #endregion -#endif // CORERT +#else // PROJECTN + + #region CompareExchange + + [Intrinsic] + public static int CompareExchange(ref int location1, int value, int comparand) + { + return RuntimeImports.InterlockedCompareExchange(ref location1, value, comparand); + } + + [Intrinsic] + public static long CompareExchange(ref long location1, long value, long comparand) + { + return RuntimeImports.InterlockedCompareExchange(ref location1, value, comparand); + } + + [Intrinsic] + public static IntPtr CompareExchange(ref IntPtr location1, IntPtr value, IntPtr comparand) + { + return RuntimeImports.InterlockedCompareExchange(ref location1, value, comparand); + } + + [Intrinsic] + public static unsafe float CompareExchange(ref float location1, float value, float comparand) + { + float ret; + *(int*)&ret = CompareExchange(ref Unsafe.As(ref location1), *(int*)&value, *(int*)&comparand); + return ret; + } + + [Intrinsic] + public static unsafe double CompareExchange(ref double location1, double value, double comparand) + { + double ret; + *(long*)&ret = CompareExchange(ref Unsafe.As(ref location1), *(long*)&value, *(long*)&comparand); + return ret; + } + + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T CompareExchange(ref T location1, T value, T comparand) where T : class + { + return Unsafe.As(RuntimeImports.InterlockedCompareExchange(ref Unsafe.As(ref location1), value, comparand)); + } + + [Intrinsic] + public static object CompareExchange(ref object location1, object value, object comparand) + { + return RuntimeImports.InterlockedCompareExchange(ref location1, value, comparand); + } + + #endregion + + #region Exchange + + [Intrinsic] + public static int Exchange(ref int location1, int value) + { + int oldValue; + + do + { + oldValue = location1; + } while (CompareExchange(ref location1, value, oldValue) != oldValue); + + return oldValue; + } + + [Intrinsic] + public static long Exchange(ref long location1, long value) + { + long oldValue; + + do + { + oldValue = location1; + } while (CompareExchange(ref location1, value, oldValue) != oldValue); + + return oldValue; + } + + [Intrinsic] + public static IntPtr Exchange(ref IntPtr location1, IntPtr value) + { + IntPtr oldValue; + + do + { + oldValue = location1; + } while (CompareExchange(ref location1, value, oldValue) != oldValue); + + return oldValue; + } + + [Intrinsic] + public static unsafe float Exchange(ref float location1, float value) + { + float ret; + *(int*)&ret = Exchange(ref Unsafe.As(ref location1), *(int*)&value); + return ret; + } + + [Intrinsic] + public static unsafe double Exchange(ref double location1, double value) + { + double ret; + *(long*)&ret = Exchange(ref Unsafe.As(ref location1), *(long*)&value); + return ret; + } + + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Exchange(ref T location1, T value) where T : class + { + return Unsafe.As(RuntimeImports.InterlockedExchange(ref Unsafe.As(ref location1), value)); + } + + [Intrinsic] + public static object Exchange(ref object location1, object value) + { + return RuntimeImports.InterlockedExchange(ref location1, value); + } + + #endregion + + #region Increment + + [Intrinsic] + public static int Increment(ref int location) + { + return ExchangeAdd(ref location, 1) + 1; + } + + [Intrinsic] + public static long Increment(ref long location) + { + return ExchangeAdd(ref location, 1) + 1; + } + + #endregion + + #region Decrement + + [Intrinsic] + public static int Decrement(ref int location) + { + return ExchangeAdd(ref location, -1) - 1; + } + + [Intrinsic] + public static long Decrement(ref long location) + { + return ExchangeAdd(ref location, -1) - 1; + } + + #endregion + + #region Add + + [Intrinsic] + public static int Add(ref int location1, int value) + { + return ExchangeAdd(ref location1, value) + value; + } + + [Intrinsic] + public static long Add(ref long location1, long value) + { + return ExchangeAdd(ref location1, value) + value; + } + + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int ExchangeAdd(ref int location1, int value) + { + int oldValue; + + do + { + oldValue = location1; + } while (CompareExchange(ref location1, oldValue + value, oldValue) != oldValue); + + return oldValue; + } + + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static long ExchangeAdd(ref long location1, long value) + { + long oldValue; + + do + { + oldValue = location1; + } while (CompareExchange(ref location1, oldValue + value, oldValue) != oldValue); + + return oldValue; + } + + #endregion + + #region MemoryBarrier + [Intrinsic] + public static void MemoryBarrier() + { + RuntimeImports.MemoryBarrier(); + } + #endregion + + #region Read + public static long Read(ref long location) + { + return CompareExchange(ref location, 0, 0); + } + #endregion + +#endif // PROJECTN public static void MemoryBarrierProcessWide() { diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/Lock.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/Lock.cs index 6c494b8c65..9669b21ebc 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/Lock.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/Lock.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Runtime; using System.Runtime.CompilerServices; using Internal.Runtime.Augments; @@ -40,8 +41,8 @@ namespace System.Threading private volatile int _state; - private int _owningThreadId; private uint _recursionCount; + private IntPtr _owningThreadId; private volatile AutoResetEvent _lazyEvent; private AutoResetEvent Event @@ -59,6 +60,13 @@ namespace System.Threading } } +#if PROJECTN + // Use a compiler intrinsic for .NET Native + private static IntPtr CurrentNativeThreadId => (IntPtr)Environment.CurrentNativeThreadId; +#else + private static IntPtr CurrentNativeThreadId => (IntPtr)RuntimeImports.RhCurrentNativeThreadId(); +#endif // PROJECTN + // On platforms where CurrentNativeThreadId redirects to ManagedThreadId.Current the inlined // version of Lock.Acquire has the ManagedThreadId.Current call not inlined, while the non-inlined // version has it inlined. So it saves code to keep this function not inlined while having @@ -66,14 +74,14 @@ namespace System.Threading [MethodImpl(MethodImplOptions.NoInlining)] public void Acquire() { - int currentThreadId = Environment.CurrentNativeThreadId; + IntPtr currentThreadId = CurrentNativeThreadId; // // Make one quick attempt to acquire an uncontended lock // if (Interlocked.CompareExchange(ref _state, Locked, Uncontended) == Uncontended) { - Debug.Assert(_owningThreadId == 0); + Debug.Assert(_owningThreadId == IntPtr.Zero); Debug.Assert(_recursionCount == 0); _owningThreadId = currentThreadId; return; @@ -96,14 +104,14 @@ namespace System.Threading if (millisecondsTimeout < -1) throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); - int currentThreadId = Environment.CurrentNativeThreadId; + IntPtr currentThreadId = CurrentNativeThreadId; // // Make one quick attempt to acquire an uncontended lock // if (Interlocked.CompareExchange(ref _state, Locked, Uncontended) == Uncontended) { - Debug.Assert(_owningThreadId == 0); + Debug.Assert(_owningThreadId == IntPtr.Zero); Debug.Assert(_recursionCount == 0); _owningThreadId = currentThreadId; return true; @@ -115,7 +123,7 @@ namespace System.Threading return TryAcquireContended(currentThreadId, millisecondsTimeout); } - private bool TryAcquireContended(int currentThreadId, int millisecondsTimeout) + private bool TryAcquireContended(IntPtr currentThreadId, int millisecondsTimeout) { // // If we already own the lock, just increment the recursion count. @@ -159,7 +167,7 @@ namespace System.Threading RuntimeThread.SpinWait(spins); spins *= 2; } - else + else if (oldState != 0) { // // We reached our spin limit, and need to wait. Increment the waiter count. @@ -221,7 +229,7 @@ namespace System.Threading GotTheLock: Debug.Assert((_state | Locked) != 0); - Debug.Assert(_owningThreadId == 0); + Debug.Assert(_owningThreadId == IntPtr.Zero); Debug.Assert(_recursionCount == 0); _owningThreadId = currentThreadId; return true; @@ -250,7 +258,7 @@ namespace System.Threading // because while we're doing this check the current thread is definitely still // alive. // - int currentThreadId = Environment.CurrentNativeThreadId; + IntPtr currentThreadId = CurrentNativeThreadId; bool acquired = (currentThreadId == _owningThreadId); if (acquired) Debug.Assert((_state & Locked) != 0); @@ -286,7 +294,7 @@ namespace System.Threading private void ReleaseCore() { Debug.Assert(_recursionCount == 0); - _owningThreadId = 0; + _owningThreadId = IntPtr.Zero; // // Make one quick attempt to release an uncontended lock @@ -303,7 +311,7 @@ namespace System.Threading private void ReleaseContended() { Debug.Assert(_recursionCount == 0); - Debug.Assert(_owningThreadId == 0); + Debug.Assert(_owningThreadId == IntPtr.Zero); while (true) { diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs new file mode 100644 index 0000000000..ce3a097a1a --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs @@ -0,0 +1,34 @@ +// 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. + +namespace System.Threading +{ + /// + /// A LIFO semaphore. + /// Waits on this semaphore are uninterruptible. + /// + internal sealed class LowLevelLifoSemaphore : IDisposable + { + private WaitSubsystem.WaitableObject _semaphore; + + public LowLevelLifoSemaphore(int initialSignalCount, int maximumSignalCount) + { + _semaphore = WaitSubsystem.WaitableObject.NewSemaphore(initialSignalCount, maximumSignalCount); + } + + public bool Wait(int timeoutMs) + { + return WaitSubsystem.Wait(_semaphore, timeoutMs, false, true); + } + + public int Release(int count) + { + return WaitSubsystem.ReleaseSemaphore(_semaphore, count); + } + + public void Dispose() + { + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Windows.cs new file mode 100644 index 0000000000..88ca534200 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Windows.cs @@ -0,0 +1,63 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Threading +{ + /// + /// A LIFO semaphore implemented using Win32 IO Completion Ports. + /// + /// + /// IO Completion ports release waiting threads in LIFO order, so we can use them to create a LIFO semaphore. + /// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365198(v=vs.85).aspx under How I/O Completion Ports Work. + /// From the docs "Threads that block their execution on an I/O completion port are released in last-in-first-out (LIFO) order." + /// + internal sealed class LowLevelLifoSemaphore : IDisposable + { + private IntPtr _completionPort; + + public LowLevelLifoSemaphore(int initialSignalCount, int maximumSignalCount) + { + Debug.Assert(initialSignalCount >= 0, "Windows LowLevelLifoSemaphore does not support a negative signal count"); // TODO: Track actual signal count to enable this + _completionPort = Interop.Kernel32.CreateIoCompletionPort(new IntPtr(-1), IntPtr.Zero, UIntPtr.Zero, 1); + if (_completionPort == IntPtr.Zero) + { + var error = Marshal.GetLastWin32Error(); + var exception = new OutOfMemoryException(); + exception.SetErrorCode(error); + throw exception; + } + Release(initialSignalCount); + } + + public bool Wait(int timeoutMs) + { + bool success = Interop.Kernel32.GetQueuedCompletionStatus(_completionPort, out var numberOfBytes, out var completionKey, out var pointerToOverlapped, timeoutMs); + Debug.Assert(success || (Marshal.GetLastWin32Error() == WaitHandle.WaitTimeout)); + return success; + } + + public int Release(int count) + { + for (int i = 0; i < count; i++) + { + if(!Interop.Kernel32.PostQueuedCompletionStatus(_completionPort, 1, UIntPtr.Zero, IntPtr.Zero)) + { + var lastError = Marshal.GetLastWin32Error(); + var exception = new OutOfMemoryException(); + exception.SetErrorCode(lastError); + throw exception; + } + } + return 0; // TODO: Track actual signal count to calculate this + } + + public void Dispose() + { + Interop.Kernel32.CloseHandle(_completionPort); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/LowLevelLock.Unix.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/LowLevelLock.cs similarity index 95% rename from external/corert/src/System.Private.CoreLib/src/System/Threading/LowLevelLock.Unix.cs rename to external/corert/src/System.Private.CoreLib/src/System/Threading/LowLevelLock.cs index ee2dec1d06..719541856f 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/LowLevelLock.Unix.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/LowLevelLock.cs @@ -61,10 +61,26 @@ namespace System.Threading { VerifyIsNotLockedByAnyThread(); - _monitor.Dispose(); + if (_monitor != null) + { + _monitor.Dispose(); + } + GC.SuppressFinalize(this); } +#if DEBUG + public bool IsLocked + { + get + { + bool isLocked = _ownerThread == RuntimeThread.CurrentThread; + Debug.Assert(!isLocked || (_state & LockedMask) != 0); + return isLocked; + } + } +#endif + public void VerifyIsLocked() { #if DEBUG @@ -170,7 +186,6 @@ namespace System.Threading _monitor.Wait(); /// Indicate to that the signaled thread has woken up - Debug.Assert(_isAnyWaitingThreadSignaled); _isAnyWaitingThreadSignaled = false; state = _state; diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.Unix.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.Unix.cs index b733c32527..f87ba7a91f 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.Unix.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.Unix.cs @@ -12,14 +12,10 @@ namespace System.Threading /// /// Used by the wait subsystem on Unix, so this class cannot have any dependencies on the wait subsystem. /// - internal sealed class LowLevelMonitor : IDisposable + internal sealed partial class LowLevelMonitor : IDisposable { private IntPtr _nativeMonitor; -#if DEBUG - private RuntimeThread _ownerThread; -#endif - public LowLevelMonitor() { _nativeMonitor = Interop.Sys.LowLevelMonitor_New(); @@ -27,21 +23,10 @@ namespace System.Threading { throw new OutOfMemoryException(); } - -#if DEBUG - _ownerThread = null; -#endif } - ~LowLevelMonitor() + private void DisposeCore() { - Dispose(); - } - - public void Dispose() - { - VerifyIsNotLockedByAnyThread(); - if (_nativeMonitor == IntPtr.Zero) { return; @@ -49,92 +34,39 @@ namespace System.Threading Interop.Sys.LowLevelMonitor_Delete(_nativeMonitor); _nativeMonitor = IntPtr.Zero; - GC.SuppressFinalize(this); } - public void VerifyIsLocked() + private void AcquireCore() { -#if DEBUG - Debug.Assert(_ownerThread == RuntimeThread.CurrentThread); -#endif - } - - public void VerifyIsNotLocked() - { -#if DEBUG - Debug.Assert(_ownerThread != RuntimeThread.CurrentThread); -#endif - } - - private void VerifyIsNotLockedByAnyThread() - { -#if DEBUG - Debug.Assert(_ownerThread == null); -#endif - } - - private void ResetOwnerThread() - { -#if DEBUG - VerifyIsLocked(); - _ownerThread = null; -#endif - } - - private void SetOwnerThreadToCurrent() - { -#if DEBUG - VerifyIsNotLockedByAnyThread(); - _ownerThread = RuntimeThread.CurrentThread; -#endif - } - - public void Acquire() - { - VerifyIsNotLocked(); Interop.Sys.LowLevelMutex_Acquire(_nativeMonitor); - SetOwnerThreadToCurrent(); } - public void Release() + private void ReleaseCore() { - ResetOwnerThread(); Interop.Sys.LowLevelMutex_Release(_nativeMonitor); } - public void Wait() + private void WaitCore() { - ResetOwnerThread(); Interop.Sys.LowLevelMonitor_Wait(_nativeMonitor); - SetOwnerThreadToCurrent(); } - public bool Wait(int timeoutMilliseconds) + private bool WaitCore(int timeoutMilliseconds) { Debug.Assert(timeoutMilliseconds >= -1); if (timeoutMilliseconds < 0) { - Wait(); + WaitCore(); return true; } - ResetOwnerThread(); - bool waitResult = Interop.Sys.LowLevelMonitor_TimedWait(_nativeMonitor, timeoutMilliseconds); - SetOwnerThreadToCurrent(); - return waitResult; + return Interop.Sys.LowLevelMonitor_TimedWait(_nativeMonitor, timeoutMilliseconds); } - public void Signal_Release() + private void Signal_ReleaseCore() { - ResetOwnerThread(); Interop.Sys.LowLevelMonitor_Signal_Release(_nativeMonitor); } - - /// The following methods typical in a monitor are omitted since they are currently not necessary for the way in which - /// this class is used: - /// - TryAcquire - /// - Signal (use instead) - /// - SignalAll } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.Windows.cs new file mode 100644 index 0000000000..602908d6d2 --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.Windows.cs @@ -0,0 +1,67 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; +using Internal.Runtime.Augments; + +namespace System.Threading +{ + /// + /// Wraps a critical section and condition variable. + /// + internal sealed partial class LowLevelMonitor : IDisposable + { + private Interop.Kernel32.CRITICAL_SECTION _criticalSection; + private Interop.Kernel32.CONDITION_VARIABLE _conditionVariable; + + public LowLevelMonitor() + { + Interop.Kernel32.InitializeCriticalSection(out _criticalSection); + Interop.Kernel32.InitializeConditionVariable(out _conditionVariable); + } + + private void DisposeCore() + { + Interop.Kernel32.DeleteCriticalSection(ref _criticalSection); + } + + private void AcquireCore() + { + Interop.Kernel32.EnterCriticalSection(ref _criticalSection); + } + + private void ReleaseCore() + { + Interop.Kernel32.LeaveCriticalSection(ref _criticalSection); + } + + private void WaitCore() + { + WaitCore(-1); + } + + private bool WaitCore(int timeoutMilliseconds) + { + bool waitResult = Interop.Kernel32.SleepConditionVariableCS(ref _conditionVariable, ref _criticalSection, timeoutMilliseconds); + if (!waitResult) + { + int lastError = Marshal.GetLastWin32Error(); + if (lastError != Interop.Errors.ERROR_TIMEOUT) + { + var exception = new OutOfMemoryException(); + exception.SetErrorCode(lastError); + throw exception; + } + } + return waitResult; + } + + private void Signal_ReleaseCore() + { + Interop.Kernel32.WakeConditionVariable(ref _conditionVariable); + Interop.Kernel32.LeaveCriticalSection(ref _criticalSection); + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.cs new file mode 100644 index 0000000000..88f1f5725c --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.cs @@ -0,0 +1,116 @@ +// 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.Diagnostics; +using Internal.Runtime.Augments; + +namespace System.Threading +{ + /// + /// Wraps a non-recursive mutex and condition. + /// + /// Used by the wait subsystem on Unix, so this class cannot have any dependencies on the wait subsystem. + /// + internal sealed partial class LowLevelMonitor : IDisposable + { +#if DEBUG + private RuntimeThread _ownerThread = null; +#endif + + ~LowLevelMonitor() + { + Dispose(); + } + + public void Dispose() + { + VerifyIsNotLockedByAnyThread(); + DisposeCore(); + GC.SuppressFinalize(this); + } + +#if DEBUG + public bool IsLocked => _ownerThread == RuntimeThread.CurrentThread; +#endif + + public void VerifyIsLocked() + { +#if DEBUG + Debug.Assert(IsLocked); +#endif + } + + public void VerifyIsNotLocked() + { +#if DEBUG + Debug.Assert(!IsLocked); +#endif + } + + private void VerifyIsNotLockedByAnyThread() + { +#if DEBUG + Debug.Assert(_ownerThread == null); +#endif + } + + private void ResetOwnerThread() + { +#if DEBUG + VerifyIsLocked(); + _ownerThread = null; +#endif + } + + private void SetOwnerThreadToCurrent() + { +#if DEBUG + VerifyIsNotLockedByAnyThread(); + _ownerThread = RuntimeThread.CurrentThread; +#endif + } + + public void Acquire() + { + VerifyIsNotLocked(); + AcquireCore(); + SetOwnerThreadToCurrent(); + } + + public void Release() + { + ResetOwnerThread(); + ReleaseCore(); + } + + public void Wait() + { + ResetOwnerThread(); + WaitCore(); + SetOwnerThreadToCurrent(); + } + + public bool Wait(int timeoutMilliseconds) + { + Debug.Assert(timeoutMilliseconds >= -1); + + ResetOwnerThread(); + bool waitResult = WaitCore(timeoutMilliseconds); + SetOwnerThreadToCurrent(); + return waitResult; + } + + public void Signal_Release() + { + ResetOwnerThread(); + Signal_ReleaseCore(); + } + + /// The following methods typical in a monitor are omitted since they are currently not necessary for the way in which + /// this class is used: + /// - TryAcquire + /// - Signal (use instead) + /// - SignalAll + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/ManualResetEventSlim.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/ManualResetEventSlim.cs index 8e29da182a..a674901ea4 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/ManualResetEventSlim.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/ManualResetEventSlim.cs @@ -15,7 +15,6 @@ using System.Diagnostics; using System.Runtime.InteropServices; -using System.Diagnostics.Contracts; using Internal.Runtime.Augments; namespace System.Threading @@ -247,9 +246,6 @@ namespace System.Threading /// private void EnsureLockObjectCreated() { - Contract.Ensures(m_lock != null); - Contract.Ensures(m_condition != null); - if (m_lock == null) { Lock newObj = new Lock(); diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/Monitor.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/Monitor.cs index ef73d84e76..e825d625bb 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/Monitor.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/Monitor.cs @@ -16,6 +16,8 @@ using System; using System.Diagnostics; using System.Runtime.CompilerServices; +using Internal.Runtime.CompilerServices; + namespace System.Threading { public static class Monitor diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/Mutex.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/Mutex.Windows.cs index a783b4948c..2b3c25061a 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/Mutex.Windows.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/Mutex.Windows.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; @@ -57,7 +56,6 @@ namespace System.Threading { throw new ArgumentException(SR.Format(SR.Argument_WaitHandleNameTooLong, Interop.Constants.MaxPath), nameof(name)); } - Contract.EndContractBlock(); result = null; diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/Mutex.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/Mutex.cs index 5e9f5c034d..6db36cb59b 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/Mutex.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/Mutex.cs @@ -20,7 +20,6 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; -using System.Diagnostics.Contracts; namespace System.Threading { @@ -29,7 +28,6 @@ namespace System.Threading public Mutex(bool initiallyOwned, string name, out bool createdNew) { VerifyNameForCreate(name); - Contract.EndContractBlock(); CreateMutexCore(initiallyOwned, name, out createdNew); } @@ -37,7 +35,6 @@ namespace System.Threading public Mutex(bool initiallyOwned, string name) { VerifyNameForCreate(name); - Contract.EndContractBlock(); bool createdNew; CreateMutexCore(initiallyOwned, name, out createdNew); diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/ObjectHeader.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/ObjectHeader.cs index c494344ca6..e516b7ca1c 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/ObjectHeader.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/ObjectHeader.cs @@ -36,6 +36,22 @@ namespace System.Threading private const int BIT_SBLK_IS_HASHCODE = 1 << IS_HASHCODE_BIT_NUMBER; internal const int MASK_HASHCODE_INDEX = BIT_SBLK_IS_HASHCODE - 1; +#if ARM || ARM64 + [MethodImpl(MethodImplOptions.NoInlining)] +#else + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + public static unsafe int ReadVolatileMemory(int* pHeader) + { + // While in x86/amd64 Volatile.Read is cheap, in arm we have to pay the + // cost of a barrier. We do no inlining to get around that. +#if ARM || ARM64 + return *pHeader; +#else + return Volatile.Read(ref *pHeader); +#endif + } + /// /// Returns the hash code assigned to the object. If no hash code has yet been assigned, /// it assigns one in a thread-safe way. @@ -51,7 +67,7 @@ namespace System.Threading { // The header is 4 bytes before m_pEEType field on all architectures int* pHeader = (int*)pEEType - 1; - int bits = *pHeader; + int bits = ReadVolatileMemory(pHeader); int hashOrIndex = bits & MASK_HASHCODE_INDEX; if ((bits & BIT_SBLK_IS_HASHCODE) != 0) { @@ -147,7 +163,7 @@ namespace System.Threading int* pHeader = (int*)pEEType - 1; int hashOrIndex; - if (GetSyncEntryIndex(*pHeader, out hashOrIndex)) + if (GetSyncEntryIndex(ReadVolatileMemory(pHeader), out hashOrIndex)) { // Already have a sync entry for this object, return the synchronization object // stored in the entry. diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/Overlapped.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/Overlapped.cs index ac85348597..787436965f 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/Overlapped.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/Overlapped.cs @@ -26,9 +26,10 @@ using System.Runtime.Versioning; using System.Security; using System.Runtime.ConstrainedExecution; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Collections.Concurrent; +using Internal.Runtime.CompilerServices; + namespace System.Threading { #region class _IOCompletionCallback @@ -130,7 +131,7 @@ namespace System.Threading m_iocbHelper = null; m_overlapped = null; m_userObject = null; - Debug.Assert(m_pinSelf.IsNull(), "OverlappedData has not been freed: m_pinSelf"); + Debug.Assert(m_pinSelf == IntPtr.Zero, "OverlappedData has not been freed: m_pinSelf"); m_pinSelf = IntPtr.Zero; // Reuse m_pinnedData array m_nativeOverlapped = default(NativeOverlapped); @@ -138,7 +139,7 @@ namespace System.Threading internal unsafe NativeOverlapped* Pack(IOCompletionCallback iocb, Object userData) { - if (!m_pinSelf.IsNull()) + if (m_pinSelf != IntPtr.Zero) { throw new InvalidOperationException(SR.InvalidOperation_Overlapped_Pack); } @@ -150,7 +151,7 @@ namespace System.Threading internal unsafe NativeOverlapped* UnsafePack(IOCompletionCallback iocb, Object userData) { - if (!m_pinSelf.IsNull()) + if (m_pinSelf != IntPtr.Zero) { throw new InvalidOperationException(SR.InvalidOperation_Overlapped_Pack); } @@ -213,7 +214,7 @@ namespace System.Threading private void FreeNativeOverlapped() { IntPtr pinSelf = m_pinSelf; - if (!pinSelf.IsNull()) + if (pinSelf != IntPtr.Zero) { if (Interlocked.CompareExchange(ref m_pinSelf, IntPtr.Zero, pinSelf) == pinSelf) { @@ -241,9 +242,7 @@ namespace System.Threading } void* pOverlappedData = (byte*)nativeOverlappedPtr - s_nativeOverlappedOffset; - // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/428968 - //return Unsafe.Read(&pOverlappedData); - throw new NotImplementedException(); + return Unsafe.Read(&pOverlappedData); } private static unsafe void CalculateNativeOverlappedOffset() @@ -370,7 +369,6 @@ namespace System.Threading { if (nativeOverlappedPtr == null) throw new ArgumentNullException(nameof(nativeOverlappedPtr)); - Contract.EndContractBlock(); Overlapped overlapped = OverlappedData.GetOverlappedFromNative(nativeOverlappedPtr).m_overlapped; @@ -382,7 +380,6 @@ namespace System.Threading { if (nativeOverlappedPtr == null) throw new ArgumentNullException(nameof(nativeOverlappedPtr)); - Contract.EndContractBlock(); Overlapped overlapped = OverlappedData.GetOverlappedFromNative(nativeOverlappedPtr).m_overlapped; OverlappedData.FreeNativeOverlapped(nativeOverlappedPtr); diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/SemaphoreSlim.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/SemaphoreSlim.cs index 51a92a1f83..ba2c0ecd97 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/SemaphoreSlim.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/SemaphoreSlim.cs @@ -89,7 +89,6 @@ namespace System.Threading bool setSuccessfully = TrySetResult(true); Debug.Assert(setSuccessfully, "Should have been able to complete task"); } - //void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae) { /* nop */ } } #endregion diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/SynchronizationContext.WinRT.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/SynchronizationContext.WinRT.cs index 5972a6e005..64803c2b2c 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/SynchronizationContext.WinRT.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/SynchronizationContext.WinRT.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Internal.Runtime.Augments; -using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace System.Threading @@ -24,17 +23,21 @@ namespace System.Threading // // It's important that we always return the same SynchronizationContext object for any particular ICoreDispatcher - // object, as long as any existing instance is still reachable. This allows reference equality checks against the - // SynchronizationContext to determine if two instances represent the same dispatcher. Async frameworks rely on this. - // To accomplish this, we use a ConditionalWeakTable to track which instances of WinRTSynchronizationContext are bound - // to each ICoreDispatcher instance. + // or IDispatcherQueue object, as long as any existing instance is still reachable. This allows reference equality + // checks against the SynchronizationContext to determine if two instances represent the same dispatcher. Async + // frameworks rely on this. To accomplish this, we use a ConditionalWeakTable to track which instance of + // SynchronizationContext is bound to each ICoreDispatcher/IDispatcherQueue instance. // private static readonly ConditionalWeakTable s_winRTContextCache = new ConditionalWeakTable(); private static SynchronizationContext GetWinRTContext() { - var dispatcher = WinRTInterop.Callbacks.GetCurrentCoreDispatcher(); + // Optimization: WinRT dispatchers are supported for STA and ASTA apartment types only + if (RuntimeThread.GetCurrentApartmentType() != RuntimeThread.ApartmentType.STA) + return null; + + object dispatcher = WinRTInterop.Callbacks.GetCurrentWinRTDispatcher(); if (dispatcher == null) return null; @@ -42,7 +45,6 @@ namespace System.Threading } } - internal sealed class WinRTSynchronizationContext : SynchronizationContext { private readonly Object m_dispatcher; @@ -99,10 +101,9 @@ namespace System.Threading { if (d == null) throw new ArgumentNullException(nameof(d)); - Contract.EndContractBlock(); var invoker = new Invoker(d, state); - WinRTInterop.Callbacks.PostToCoreDispatcher(m_dispatcher, Invoker.InvokeDelegate, invoker); + WinRTInterop.Callbacks.PostToWinRTDispatcher(m_dispatcher, Invoker.InvokeDelegate, invoker); } public override void Send(SendOrPostCallback d, object state) diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/SynchronizationContext.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/SynchronizationContext.cs index 63e6694b0a..92df1152a1 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/SynchronizationContext.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/SynchronizationContext.cs @@ -12,7 +12,6 @@ ===========================================================*/ using Internal.Runtime.Augments; -using System.Diagnostics.Contracts; namespace System.Threading { @@ -84,7 +83,6 @@ namespace System.Threading { throw new ArgumentNullException(nameof(waitHandles)); } - Contract.EndContractBlock(); return WaitHandle.WaitForMultipleObjectsIgnoringSyncContext(waitHandles, waitHandles.Length, waitAll, millisecondsTimeout); } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs index 28ebe3a2a3..f7fbbac7b3 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs @@ -18,7 +18,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; namespace System.Threading.Tasks { @@ -104,7 +103,6 @@ namespace System.Threading.Tasks if (taskScheduler == null) throw new ArgumentNullException(nameof(taskScheduler)); if (maxConcurrencyLevel == 0 || maxConcurrencyLevel < -1) throw new ArgumentOutOfRangeException(nameof(maxConcurrencyLevel)); if (maxItemsPerTask == 0 || maxItemsPerTask < -1) throw new ArgumentOutOfRangeException(nameof(maxItemsPerTask)); - Contract.EndContractBlock(); // Store configuration m_underlyingTaskScheduler = taskScheduler; @@ -707,14 +705,14 @@ namespace System.Threading.Tasks } } - /// Asserts that a given synchronization object is either held or not held. - /// The monitor to check. + /// Asserts that a given Lock object is either held or not held. + /// The Lock object to check. /// Whether we want to assert that it's currently held or not held. [Conditional("DEBUG")] - internal static void ContractAssertMonitorStatus(object syncObj, bool held) + private static void ContractAssertMonitorStatus(Lock syncObj, bool held) { - Debug.Assert(syncObj != null, "The monitor object to check must be provided."); - Debug.Assert(Monitor.IsEntered(syncObj) == held, "The locking scheme was not correctly followed."); + Debug.Assert(syncObj != null, "The Lock object to check must be provided."); + Debug.Assert(syncObj.IsAcquired == held, "The locking scheme was not correctly followed."); } /// Gets the options to use for tasks. diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs index cafb979aec..ed2c06d745 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs @@ -550,7 +550,7 @@ namespace System.Threading.Tasks m_result = funcWithState(m_stateObject); return; } - Debug.Assert(false, "Invalid m_action in Task"); + Debug.Fail("Invalid m_action in Task"); } #region Await Support diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/FutureFactory.cs.REMOVED.git-id b/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/FutureFactory.cs.REMOVED.git-id index e702c0b75b..2a6ed083c9 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/FutureFactory.cs.REMOVED.git-id +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/FutureFactory.cs.REMOVED.git-id @@ -1 +1 @@ -cb1906f3634d14d83397ed3614d89f4032e48caf \ No newline at end of file +b27b1858fea5bbb83b6ef0a139929bdcebcba74d \ No newline at end of file diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs.REMOVED.git-id b/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs.REMOVED.git-id index d6a3d4d1df..8a18e60b59 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs.REMOVED.git-id +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs.REMOVED.git-id @@ -1 +1 @@ -becb5862072686af010675ee5769714312e431e3 \ No newline at end of file +04a0c1a960a88fa95d87deb30f8e57e685b66894 \ No newline at end of file diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/TaskContinuation.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/TaskContinuation.cs index 4bdc7f5141..5c59699f96 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/TaskContinuation.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/TaskContinuation.cs @@ -68,7 +68,7 @@ namespace System.Threading.Tasks actionWithState(antecedent, m_stateObject); return; } - Debug.Assert(false, "Invalid m_action in ContinuationTaskFromTask"); + Debug.Fail("Invalid m_action in ContinuationTaskFromTask"); } } @@ -116,7 +116,7 @@ namespace System.Threading.Tasks m_result = funcWithState(antecedent, m_stateObject); return; } - Debug.Assert(false, "Invalid m_action in ContinuationResultTaskFromTask"); + Debug.Fail("Invalid m_action in ContinuationResultTaskFromTask"); } } @@ -164,7 +164,7 @@ namespace System.Threading.Tasks actionWithState(antecedent, m_stateObject); return; } - Debug.Assert(false, "Invalid m_action in ContinuationTaskFromResultTask"); + Debug.Fail("Invalid m_action in ContinuationTaskFromResultTask"); } } @@ -212,7 +212,7 @@ namespace System.Threading.Tasks m_result = funcWithState(antecedent, m_stateObject); return; } - Debug.Assert(false, "Invalid m_action in ContinuationResultTaskFromResultTask"); + Debug.Fail("Invalid m_action in ContinuationResultTaskFromResultTask"); } } @@ -597,11 +597,6 @@ namespace System.Threading.Tasks ExecutionContext.Run(m_capturedContext, GetInvokeActionCallback(), m_action); } - ///// - ///// The ThreadPool calls this if a ThreadAbortException is thrown while trying to execute this workitem. - ///// - //void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae) { /* nop */ } - /// Cached delegate that invokes an Action passed as an object parameter. private static ContextCallback s_invokeActionCallback; diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/TaskFactory.cs.REMOVED.git-id b/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/TaskFactory.cs.REMOVED.git-id index 700aecb2ba..c9d581633a 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/TaskFactory.cs.REMOVED.git-id +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/TaskFactory.cs.REMOVED.git-id @@ -1 +1 @@ -4cf0754e63e151d318c23d428611ee48711a9a0c \ No newline at end of file +627a760c70dad24b763acc3e566b6cf1c18ae6e7 \ No newline at end of file diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/TaskScheduler.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/TaskScheduler.cs index 38bfe77165..653345cef6 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/TaskScheduler.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/TaskScheduler.cs @@ -5,14 +5,10 @@ // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // // - // // This file contains the primary interface and management of tasks and queues. // // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -// Disable the "reference to volatile field not treated as volatile" error. -#pragma warning disable 0420 - using System.Collections.Generic; using System.Diagnostics; @@ -251,8 +247,8 @@ namespace System.Threading.Tasks // Member variables // - // The global container that keeps track of TaskScheduler instances. s_activeTaskSchedulers must be initialized before s_defaultTaskScheduler. - private static readonly ConditionalWeakTable s_activeTaskSchedulers = new ConditionalWeakTable(); + // The global container that keeps track of TaskScheduler instances for debugging purposes. + private static ConditionalWeakTable s_activeTaskSchedulers; // An AppDomain-wide default manager. private static readonly TaskScheduler s_defaultTaskScheduler = new ThreadPoolTaskScheduler(); @@ -275,10 +271,28 @@ namespace System.Threading.Tasks /// protected TaskScheduler() { - // Protected constructor. It's here to ensure all user implemented TaskSchedulers will be - // registered in the active schedulers list. - Debug.Assert(s_activeTaskSchedulers != null, "Expected non-null s_activeTaskSchedulers"); - s_activeTaskSchedulers.Add(this, null); +#if false // Debugger support + // Register the scheduler in the active scheduler list. This is only relevant when debugging, + // so we only pay the cost if the debugger is attached when the scheduler is created. This + // means that the internal TaskScheduler.GetTaskSchedulersForDebugger() will only include + // schedulers created while the debugger is attached. + if (Debugger.IsAttached) + { + AddToActiveTaskSchedulers(); + } +#endif + } + + /// Adds this scheduler ot the active schedulers tracking collection for debugging purposes. + private void AddToActiveTaskSchedulers() + { + ConditionalWeakTable activeTaskSchedulers = s_activeTaskSchedulers; + if (activeTaskSchedulers == null) + { + Interlocked.CompareExchange(ref s_activeTaskSchedulers, new ConditionalWeakTable(), null); + activeTaskSchedulers = s_activeTaskSchedulers; + } + activeTaskSchedulers.Add(this, null); } /// @@ -524,20 +538,35 @@ namespace System.Threading.Tasks /// It should not be called by any other codepaths. /// /// An array of TaskScheduler instances. - //internal static TaskScheduler[] GetTaskSchedulersForDebugger() - //{ - // Debug.Assert(s_activeTaskSchedulers != null, "Expected non-null s_activeTaskSchedulers"); + internal static TaskScheduler[] GetTaskSchedulersForDebugger() + { + if (s_activeTaskSchedulers == null) + { + // No schedulers were tracked. Just give back the default. + return new TaskScheduler[] { s_defaultTaskScheduler }; + } - // ICollection schedulers = s_activeTaskSchedulers.Keys; - // var arr = new TaskScheduler[schedulers.Count]; - // schedulers.CopyTo(arr, 0); - // foreach (var scheduler in arr) - // { - // Debug.Assert(scheduler != null, "Table returned an incorrect Count or CopyTo failed"); - // int tmp = scheduler.Id; // force Ids for debugger - // } - // return arr; - //} + LowLevelList schedulers = new LowLevelList(); + foreach (var item in s_activeTaskSchedulers) + { + schedulers.Add(item.Key); + } + + if (!schedulers.Contains(s_defaultTaskScheduler)) + { + // Make sure the default is included, in case the debugger attached + // after it was created. + schedulers.Add(s_defaultTaskScheduler); + } + + var arr = schedulers.ToArray(); + foreach (var scheduler in arr) + { + Debug.Assert(scheduler != null, "Table returned an incorrect Count or CopyTo failed"); + int tmp = scheduler.Id; // force Ids for debugger + } + return arr; + } /// /// Nested class that provides debugger view for TaskScheduler @@ -550,17 +579,16 @@ namespace System.Threading.Tasks m_taskScheduler = scheduler; } - // returns the scheduler’s Id + // returns the scheduler's Id public Int32 Id { get { return m_taskScheduler.Id; } } - // returns the scheduler’s GetScheduledTasks + // returns the scheduler's GetScheduledTasks public IEnumerable ScheduledTasks { - get - { return m_taskScheduler.GetScheduledTasks(); } + get { return m_taskScheduler.GetScheduledTasks(); } } } } @@ -645,16 +673,7 @@ namespace System.Threading.Tasks } // preallocated SendOrPostCallback delegate - private static SendOrPostCallback s_postCallback = new SendOrPostCallback(PostCallback); - - // this is where the actual task invocation occures - private static void PostCallback(object obj) - { - Task task = (Task)obj; - - // calling ExecuteEntry with double execute check enabled because a user implemented SynchronizationContext could be buggy - task.ExecuteEntry(true); - } + private static readonly SendOrPostCallback s_postCallback = s => ((Task)s).ExecuteEntry(true); // with double-execute check because SC could be buggy } /// diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/ThreadInterruptedException.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/ThreadInterruptedException.cs index 12cedf3b1e..8a10cc9c99 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/ThreadInterruptedException.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/ThreadInterruptedException.cs @@ -17,24 +17,25 @@ using System.Runtime.Serialization; namespace System.Threading { [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class ThreadInterruptedException : SystemException { public ThreadInterruptedException() : base(SR.Threading_ThreadInterrupted) { - HResult = __HResults.COR_E_THREADINTERRUPTED; + HResult = HResults.COR_E_THREADINTERRUPTED; } public ThreadInterruptedException(String message) : base(message) { - HResult = __HResults.COR_E_THREADINTERRUPTED; + HResult = HResults.COR_E_THREADINTERRUPTED; } public ThreadInterruptedException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_THREADINTERRUPTED; + HResult = HResults.COR_E_THREADINTERRUPTED; } protected ThreadInterruptedException(SerializationInfo info, StreamingContext context) diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs new file mode 100644 index 0000000000..f7dff9475e --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs @@ -0,0 +1,413 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +namespace System.Threading +{ + // + // Portable implementation of ThreadPool + // + + /// + /// An object representing the registration of a via . + /// + public sealed class RegisteredWaitHandle : MarshalByRefObject + { + internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper, + int millisecondsTimeout, bool repeating) + { + Handle = waitHandle; + Callback = callbackHelper; + TimeoutDurationMs = millisecondsTimeout; + Repeating = repeating; + RestartTimeout(Environment.TickCount); + } + + ~RegisteredWaitHandle() + { + if(WaitThread != null) + { + Unregister(null); + } + } + + private static AutoResetEvent s_cachedEvent; + + private static AutoResetEvent RentEvent() + { + AutoResetEvent resetEvent = Interlocked.Exchange(ref s_cachedEvent, (AutoResetEvent)null); + if (resetEvent == null) + { + resetEvent = new AutoResetEvent(false); + } + return resetEvent; + } + + private static void ReturnEvent(AutoResetEvent resetEvent) + { + if (Interlocked.CompareExchange(ref s_cachedEvent, resetEvent, null) != null) + { + resetEvent.Dispose(); + } + } + + /// + /// The callback to execute when the wait on either times out or completes. + /// + internal _ThreadPoolWaitOrTimerCallback Callback { get; } + + + /// + /// The that was registered. + /// + internal WaitHandle Handle { get; } + + /// + /// The time this handle times out at in ms. + /// + internal int TimeoutTimeMs { get; private set; } + + private int TimeoutDurationMs { get; } + + internal bool IsInfiniteTimeout => TimeoutDurationMs == -1; + + internal void RestartTimeout(int currentTimeMs) + { + TimeoutTimeMs = currentTimeMs + TimeoutDurationMs; + } + + /// + /// Whether or not the wait is a repeating wait. + /// + internal bool Repeating { get; } + + /// + /// The the user passed in via . + /// + private SafeWaitHandle UserUnregisterWaitHandle { get; set; } + + private IntPtr UserUnregisterWaitHandleValue { get; set; } + + internal bool IsBlocking => UserUnregisterWaitHandleValue == (IntPtr)(-1); + + /// + /// The this was registered on. + /// + internal ClrThreadPool.WaitThread WaitThread { get; set; } + + /// + /// The number of callbacks that are currently queued on the Thread Pool or executing. + /// + private int _numRequestedCallbacks; + + private LowLevelLock _callbackLock = new LowLevelLock(); + + /// + /// Notes if we need to signal the user's unregister event after all callbacks complete. + /// + private bool _signalAfterCallbacksComplete; + + private bool _unregisterCalled; + + private bool _unregistered; + + private AutoResetEvent _callbacksComplete; + + private AutoResetEvent _removed; + + /// + /// Unregisters this wait handle registration from the wait threads. + /// + /// The event to signal when the handle is unregistered. + /// If the handle was successfully marked to be removed and the provided wait handle was set as the user provided event. + /// + /// This method will only return true on the first call. + /// Passing in a wait handle with a value of -1 will result in a blocking wait, where Unregister will not return until the full unregistration is completed. + /// + public bool Unregister(WaitHandle waitObject) + { + GC.SuppressFinalize(this); + _callbackLock.Acquire(); + bool needToRollBackRefCountOnException = false; + try + { + if (_unregisterCalled) + { + return false; + } + + UserUnregisterWaitHandle = waitObject?.SafeWaitHandle; + UserUnregisterWaitHandle?.DangerousAddRef(); + needToRollBackRefCountOnException = true; + + UserUnregisterWaitHandleValue = UserUnregisterWaitHandle?.DangerousGetHandle() ?? IntPtr.Zero; + + if (_unregistered) + { + SignalUserWaitHandle(); + return true; + } + + if (IsBlocking) + { + _callbacksComplete = RentEvent(); + } + else + { + _removed = RentEvent(); + } + _unregisterCalled = true; + } + catch (Exception) // Rollback state on exception + { + if (_removed != null) + { + ReturnEvent(_removed); + _removed = null; + } + else if (_callbacksComplete != null) + { + ReturnEvent(_callbacksComplete); + _callbacksComplete = null; + } + + UserUnregisterWaitHandleValue = IntPtr.Zero; + + if (needToRollBackRefCountOnException) + { + UserUnregisterWaitHandle?.DangerousRelease(); + } + + UserUnregisterWaitHandle = null; + throw; + } + finally + { + _callbackLock.Release(); + } + + WaitThread.UnregisterWait(this); + return true; + } + + /// + /// Signal if it has not been signaled yet and is a valid handle. + /// + private void SignalUserWaitHandle() + { + _callbackLock.VerifyIsLocked(); + SafeWaitHandle handle = UserUnregisterWaitHandle; + IntPtr handleValue = UserUnregisterWaitHandleValue; + try + { + if (handleValue != IntPtr.Zero && handleValue != (IntPtr)(-1)) + { + EventWaitHandle.Set(handleValue); + } + } + finally + { + handle?.DangerousRelease(); + _callbacksComplete?.Set(); + _unregistered = true; + } + } + + /// + /// Perform the registered callback if the has not been signaled. + /// + /// Whether or not the wait timed out. + internal void PerformCallback(bool timedOut) + { +#if DEBUG + _callbackLock.Acquire(); + try + { + Debug.Assert(_numRequestedCallbacks != 0); + } + finally + { + _callbackLock.Release(); + } +#endif + _ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(Callback, timedOut); + CompleteCallbackRequest(); + } + + /// + /// Tell this handle that there is a callback queued on the thread pool for this handle. + /// + internal void RequestCallback() + { + _callbackLock.Acquire(); + try + { + _numRequestedCallbacks++; + } + finally + { + _callbackLock.Release(); + } + } + + /// + /// Called when the wait thread removes this handle registration. This will signal the user's event if there are no callbacks pending, + /// or note that the user's event must be signaled when the callbacks complete. + /// + internal void OnRemoveWait() + { + _callbackLock.Acquire(); + try + { + _removed?.Set(); + if (_numRequestedCallbacks == 0) + { + SignalUserWaitHandle(); + } + else + { + _signalAfterCallbacksComplete = true; + } + } + finally + { + _callbackLock.Release(); + } + } + + /// + /// Reduces the number of callbacks requested. If there are no more callbacks and the user's handle is queued to be signaled, signal it. + /// + private void CompleteCallbackRequest() + { + _callbackLock.Acquire(); + try + { + --_numRequestedCallbacks; + if (_numRequestedCallbacks == 0 && _signalAfterCallbacksComplete) + { + SignalUserWaitHandle(); + } + } + finally + { + _callbackLock.Release(); + } + } + + /// + /// Wait for all queued callbacks and the full unregistration to complete. + /// + internal void WaitForCallbacks() + { + Debug.Assert(IsBlocking); + Debug.Assert(_unregisterCalled); // Should only be called when the wait is unregistered by the user. + + _callbacksComplete.WaitOne(); + ReturnEvent(_callbacksComplete); + _callbacksComplete = null; + } + + internal void WaitForRemoval() + { + Debug.Assert(!IsBlocking); + Debug.Assert(_unregisterCalled); // Should only be called when the wait is unregistered by the user. + + _removed.WaitOne(); + ReturnEvent(_removed); + _removed = null; + } + } + + public static partial class ThreadPool + { + public static bool SetMaxThreads(int workerThreads, int completionPortThreads) + { + if (workerThreads < 0 || completionPortThreads < 0) + { + return false; + } + return ClrThreadPool.ThreadPoolInstance.SetMaxThreads(workerThreads); + } + + public static void GetMaxThreads(out int workerThreads, out int completionPortThreads) + { + // Note that worker threads and completion port threads share the same thread pool. + // The total number of threads cannot exceed MaxThreadCount. + workerThreads = ClrThreadPool.ThreadPoolInstance.GetMaxThreads(); + completionPortThreads = 1; + } + + public static bool SetMinThreads(int workerThreads, int completionPortThreads) + { + if (workerThreads < 0 || completionPortThreads < 0) + { + return false; + } + return ClrThreadPool.ThreadPoolInstance.SetMinThreads(workerThreads); + } + + public static void GetMinThreads(out int workerThreads, out int completionPortThreads) + { + // All threads are pre-created at present + workerThreads = ClrThreadPool.ThreadPoolInstance.GetMinThreads(); + completionPortThreads = 0; + } + + public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads) + { + workerThreads = ClrThreadPool.ThreadPoolInstance.GetAvailableThreads(); + completionPortThreads = 0; + } + + /// + /// This method is called to request a new thread pool worker to handle pending work. + /// + internal static void RequestWorkerThread() + { + ClrThreadPool.ThreadPoolInstance.RequestWorker(); + } + + internal static bool KeepDispatching(int startTickCount) + { + return true; + } + + internal static void NotifyWorkItemProgress() + { + ClrThreadPool.ThreadPoolInstance.NotifyWorkItemComplete(); + } + + internal static bool NotifyWorkItemComplete() + { + return ClrThreadPool.ThreadPoolInstance.NotifyWorkItemComplete(); + } + + private static RegisteredWaitHandle RegisterWaitForSingleObject( + WaitHandle waitObject, + WaitOrTimerCallback callBack, + Object state, + uint millisecondsTimeOutInterval, + bool executeOnlyOnce, + bool flowExecutionContext) + { + if (waitObject == null) + throw new ArgumentNullException(nameof(waitObject)); + + if (callBack == null) + throw new ArgumentNullException(nameof(callBack)); + + RegisteredWaitHandle registeredHandle = new RegisteredWaitHandle( + waitObject, + new _ThreadPoolWaitOrTimerCallback(callBack, state, flowExecutionContext), + (int)millisecondsTimeOutInterval, + !executeOnlyOnce); + ClrThreadPool.ThreadPoolInstance.RegisterWaitHandle(registeredHandle); + return registeredHandle; + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs deleted file mode 100644 index d8ef3f7a74..0000000000 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs +++ /dev/null @@ -1,114 +0,0 @@ -// 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 Internal.Runtime.Augments; -using System.Runtime.InteropServices; - -namespace System.Threading -{ - // - // Unix-specific implementation of ThreadPool - // - public static partial class ThreadPool - { - // TODO: this is a very primitive (temporary) implementation of Thread Pool to allow Tasks to be - // used on Unix. All of this code must be replaced with proper implementation. - - /// - /// Max allowed number of threads in the thread pool. This is just arbitrary number - /// that is used to prevent unbounded creation of threads. - /// It should by high enough to provide sufficient number of thread pool workers - /// in case if some threads get blocked while running user code. - /// - private static readonly int MaxThreadCount = 4 * ThreadPoolGlobals.processorCount; - - /// - /// Semaphore that is used to release waiting thread pool workers when new work becomes available. - /// - private static SemaphoreSlim s_semaphore = new SemaphoreSlim(0); - - /// - /// Number of worker threads created by the thread pool. - /// - private static volatile int s_workerCount = 0; - - public static bool SetMaxThreads(int workerThreads, int completionPortThreads) - { - // Not supported at present - return false; - } - - public static void GetMaxThreads(out int workerThreads, out int completionPortThreads) - { - // Note that worker threads and completion port threads share the same thread pool. - // The total number of threads cannot exceed MaxThreadCount. - workerThreads = MaxThreadCount; - completionPortThreads = MaxThreadCount; - } - - public static bool SetMinThreads(int workerThreads, int completionPortThreads) - { - // Not supported at present - return false; - } - - public static void GetMinThreads(out int workerThreads, out int completionPortThreads) - { - // All threads are pre-created at present - workerThreads = MaxThreadCount; - completionPortThreads = MaxThreadCount; - } - - public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads) - { - // Make sure we return a non-negative value if thread pool defaults are changed - int availableThreads = Math.Max(MaxThreadCount - ThreadPoolGlobals.workQueue.numWorkingThreads, 0); - - workerThreads = availableThreads; - completionPortThreads = availableThreads; - } - - /// - /// This method is called to request a new thread pool worker to handle pending work. - /// - internal static void QueueDispatch() - { - // For simplicity of the state management, we pre-create all thread pool workers on the first - // request and then use the semaphore to release threads as new requests come in. - if ((s_workerCount == 0) && Interlocked.Exchange(ref s_workerCount, MaxThreadCount) == 0) - { - for (int i = 0; i < MaxThreadCount; i++) - { - if (!Interop.Sys.RuntimeThread_CreateThread(IntPtr.Zero /*use default stack size*/, - AddrofIntrinsics.AddrOf(ThreadPoolDispatchCallback), IntPtr.Zero)) - { - throw new OutOfMemoryException(); - } - } - } - - // Release one thread to handle the new request - s_semaphore.Release(1); - } - - /// - /// This method is an entry point of a thread pool worker thread. - /// - [NativeCallable] - private static IntPtr ThreadPoolDispatchCallback(IntPtr context) - { - RuntimeThread.InitializeThreadPoolThread(); - - do - { - // Handle pending requests - ThreadPoolWorkQueue.Dispatch(); - - // Wait for new requests to arrive - s_semaphore.Wait(); - - } while (true); - } - } -} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index 1c7086527a..5ca0604b4e 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Internal.Runtime.Augments; +using Microsoft.Win32.SafeHandles; using System.Diagnostics; using System.Runtime.InteropServices; @@ -11,8 +11,228 @@ namespace System.Threading // // Windows-specific implementation of ThreadPool // + public sealed class RegisteredWaitHandle : MarshalByRefObject + { + private readonly Lock _lock; + private SafeWaitHandle _waitHandle; + private readonly _ThreadPoolWaitOrTimerCallback _callbackHelper; + private readonly uint _millisecondsTimeout; + private bool _repeating; + private bool _unregistering; + + // Handle to this object to keep it alive + private GCHandle _gcHandle; + + // Pointer to the TP_WAIT structure + private IntPtr _tpWait; + + internal RegisteredWaitHandle(SafeWaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper, + uint millisecondsTimeout, bool repeating) + { + _lock = new Lock(); + + // Protect the handle from closing while we are waiting on it (VSWhidbey 285642) + waitHandle.DangerousAddRef(); + _waitHandle = waitHandle; + + _callbackHelper = callbackHelper; + _millisecondsTimeout = millisecondsTimeout; + _repeating = repeating; + + // Allocate _gcHandle and _tpWait as the last step and make sure they are never leaked + _gcHandle = GCHandle.Alloc(this); + + _tpWait = Interop.mincore.CreateThreadpoolWait( + AddrofIntrinsics.AddrOf(RegisteredWaitCallback), (IntPtr)_gcHandle, IntPtr.Zero); + + if (_tpWait == IntPtr.Zero) + { + _gcHandle.Free(); + throw new OutOfMemoryException(); + } + } + + [NativeCallable(CallingConvention = CallingConvention.StdCall)] + internal static void RegisteredWaitCallback(IntPtr instance, IntPtr context, IntPtr wait, uint waitResult) + { + var wrapper = ThreadPoolCallbackWrapper.Enter(); + GCHandle handle = (GCHandle)context; + RegisteredWaitHandle registeredWaitHandle = (RegisteredWaitHandle)handle.Target; + Debug.Assert((handle == registeredWaitHandle._gcHandle) && (wait == registeredWaitHandle._tpWait)); + + bool timedOut = (waitResult == (uint)Interop.Constants.WaitTimeout); + registeredWaitHandle.PerformCallback(timedOut); + wrapper.Exit(); + } + + private void PerformCallback(bool timedOut) + { + bool lockAcquired; + var spinner = new SpinWait(); + + // Prevent the race condition with Unregister and the previous PerformCallback call, which may still be + // holding the _lock. + while (!(lockAcquired = _lock.TryAcquire(0)) && !Volatile.Read(ref _unregistering)) + { + spinner.SpinOnce(); + } + + // If another thread is running Unregister, no need to restart the timer or clean up + if (lockAcquired) + { + try + { + if (!_unregistering) + { + if (_repeating) + { + // Allow this wait to fire again. Restart the timer before executing the callback. + RestartWait(); + } + else + { + // This wait will not be fired again. Free the GC handle to allow the GC to collect this object. + Debug.Assert(_gcHandle.IsAllocated); + _gcHandle.Free(); + } + } + } + finally + { + _lock.Release(); + } + } + + _ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(_callbackHelper, timedOut); + } + + internal unsafe void RestartWait() + { + long timeout; + long* pTimeout = null; // Null indicates infinite timeout + + if (_millisecondsTimeout != Timeout.UnsignedInfinite) + { + timeout = -10000L * _millisecondsTimeout; + pTimeout = &timeout; + } + + // We can use DangerousGetHandle because of DangerousAddRef in the constructor + Interop.mincore.SetThreadpoolWait(_tpWait, _waitHandle.DangerousGetHandle(), (IntPtr)pTimeout); + } + + public bool Unregister(WaitHandle waitObject) + { + // Hold the lock during the synchronous part of Unregister (as in CoreCLR) + using (LockHolder.Hold(_lock)) + { + if (!_unregistering) + { + // Ensure callbacks will not call SetThreadpoolWait anymore + _unregistering = true; + + // Cease queueing more callbacks + Interop.mincore.SetThreadpoolWait(_tpWait, IntPtr.Zero, IntPtr.Zero); + + // Should we wait for callbacks synchronously? Note that we treat the zero handle as the asynchronous case. + SafeWaitHandle safeWaitHandle = waitObject?.SafeWaitHandle; + bool blocking = ((safeWaitHandle != null) && (safeWaitHandle.DangerousGetHandle() == Interop.InvalidHandleValue)); + + if (blocking) + { + FinishUnregistering(); + } + else + { + // Wait for callbacks and dispose resources asynchronously + ThreadPool.QueueUserWorkItem(FinishUnregisteringAsync, safeWaitHandle); + } + + return true; + } + } + return false; + } + + private void FinishUnregistering() + { + Debug.Assert(_unregistering); + + // Wait for outstanding wait callbacks to complete + Interop.mincore.WaitForThreadpoolWaitCallbacks(_tpWait, false); + + // Now it is safe to dispose resources + Interop.mincore.CloseThreadpoolWait(_tpWait); + _tpWait = IntPtr.Zero; + + if (_gcHandle.IsAllocated) + { + _gcHandle.Free(); + } + + Debug.Assert(_waitHandle != null); + _waitHandle.DangerousRelease(); + _waitHandle = null; + + GC.SuppressFinalize(this); + } + + private void FinishUnregisteringAsync(object waitObject) + { + FinishUnregistering(); + + // Signal the provided wait object + SafeWaitHandle safeWaitHandle = (SafeWaitHandle)waitObject; + + if ((safeWaitHandle != null) && !safeWaitHandle.IsInvalid) + { + Interop.mincore.SetEvent(safeWaitHandle); + } + } + + ~RegisteredWaitHandle() + { + // If _gcHandle is allocated, it points to this object, so this object must not be collected by the GC + Debug.Assert(!_gcHandle.IsAllocated); + + // If this object gets resurrected and another thread calls Unregister, that creates a race condition. + // Do not block the finalizer thread. If another thread is running Unregister, it will clean up for us. + // The _lock may be null in case of OOM in the constructor. + if ((_lock != null) && _lock.TryAcquire(0)) + { + try + { + if (!_unregistering) + { + _unregistering = true; + + if (_tpWait != IntPtr.Zero) + { + // There must be no in-flight callbacks; just dispose resources + Interop.mincore.CloseThreadpoolWait(_tpWait); + _tpWait = IntPtr.Zero; + } + + if (_waitHandle != null) + { + _waitHandle.DangerousRelease(); + _waitHandle = null; + } + } + } + finally + { + _lock.Release(); + } + } + } + } + public static partial class ThreadPool { + // Time in ms for which ThreadPoolWorkQueue.Dispatch keeps executing work items before returning to the OS + private const uint DispatchQuantum = 30; + /// /// The maximum number of threads in the default thread pool on Windows 10 as computed by /// TppComputeDefaultMaxThreads(TppMaxGlobalPool). @@ -59,15 +279,34 @@ namespace System.Threading completionPortThreads = availableThreads; } + internal static bool KeepDispatching(int startTickCount) + { + // Note: this function may incorrectly return false due to TickCount overflow + // if work item execution took around a multiple of 2^32 milliseconds (~49.7 days), + // which is improbable. + return ((uint)(Environment.TickCount - startTickCount) < DispatchQuantum); + } + + internal static void NotifyWorkItemProgress() + { + } + + internal static bool NotifyWorkItemComplete() + { + return true; + } + [NativeCallable(CallingConvention = CallingConvention.StdCall)] private static void DispatchCallback(IntPtr instance, IntPtr context, IntPtr work) { - RuntimeThread.InitializeThreadPoolThread(); + var wrapper = ThreadPoolCallbackWrapper.Enter(); Debug.Assert(s_work == work); ThreadPoolWorkQueue.Dispatch(); + // We reset the thread after executing each callback + wrapper.Exit(resetThread: false); } - internal static void QueueDispatch() + internal static void RequestWorkerThread() { if (s_work == IntPtr.Zero) { @@ -83,5 +322,26 @@ namespace System.Threading Interop.mincore.SubmitThreadpoolWork(s_work); } + + private static RegisteredWaitHandle RegisterWaitForSingleObject( + WaitHandle waitObject, + WaitOrTimerCallback callBack, + Object state, + uint millisecondsTimeOutInterval, + bool executeOnlyOnce, + bool flowExecutionContext) + { + if (waitObject == null) + throw new ArgumentNullException(nameof(waitObject)); + + if (callBack == null) + throw new ArgumentNullException(nameof(callBack)); + + var callbackHelper = new _ThreadPoolWaitOrTimerCallback(callBack, state, flowExecutionContext); + var registeredWaitHandle = new RegisteredWaitHandle(waitObject.SafeWaitHandle, callbackHelper, millisecondsTimeOutInterval, !executeOnlyOnce); + + registeredWaitHandle.RestartWait(); + return registeredWaitHandle; + } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/ThreadPool.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/ThreadPool.cs index a21d027425..e83cdce77d 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/ThreadPool.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/ThreadPool.cs @@ -27,8 +27,11 @@ */ using Internal.Runtime.Augments; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; namespace System.Threading { @@ -48,71 +51,72 @@ namespace System.Threading internal sealed class ThreadPoolWorkQueue { - // Simple sparsely populated array to allow lock-free reading. - internal class SparseArray where T : class + internal static class WorkStealingQueueList { - private volatile T[] m_array; - private readonly Lock m_lock = new Lock(); + private static volatile WorkStealingQueue[] _queues = new WorkStealingQueue[0]; - internal SparseArray(int initialSize) - { - m_array = new T[initialSize]; - } + public static WorkStealingQueue[] Queues => _queues; - internal T[] Current - { - get { return m_array; } - } - - internal int Add(T e) + public static void Add(WorkStealingQueue queue) { + Debug.Assert(queue != null); while (true) { - T[] array = m_array; - using (LockHolder.Hold(m_lock)) - { - for (int i = 0; i < array.Length; i++) - { - if (array[i] == null) - { - Volatile.Write(ref array[i], e); - return i; - } - else if (i == array.Length - 1) - { - // Must resize. If we raced and lost, we start over again. - if (array != m_array) - continue; + WorkStealingQueue[] oldQueues = _queues; + Debug.Assert(Array.IndexOf(oldQueues, queue) == -1); - T[] newArray = new T[array.Length * 2]; - Array.Copy(array, newArray, i + 1); - newArray[i + 1] = e; - m_array = newArray; - return i + 1; - } - } + var newQueues = new WorkStealingQueue[oldQueues.Length + 1]; + Array.Copy(oldQueues, 0, newQueues, 0, oldQueues.Length); + newQueues[newQueues.Length - 1] = queue; + if (Interlocked.CompareExchange(ref _queues, newQueues, oldQueues) == oldQueues) + { + break; } } } - internal void Remove(T e) + public static void Remove(WorkStealingQueue queue) { - T[] array = m_array; - using (LockHolder.Hold(m_lock)) + Debug.Assert(queue != null); + while (true) { - for (int i = 0; i < m_array.Length; i++) + WorkStealingQueue[] oldQueues = _queues; + if (oldQueues.Length == 0) { - if (m_array[i] == e) - { - Volatile.Write(ref m_array[i], null); - break; - } + return; + } + + int pos = Array.IndexOf(oldQueues, queue); + if (pos == -1) + { + Debug.Fail("Should have found the queue"); + return; + } + + var newQueues = new WorkStealingQueue[oldQueues.Length - 1]; + if (pos == 0) + { + Array.Copy(oldQueues, 1, newQueues, 0, newQueues.Length); + } + else if (pos == oldQueues.Length - 1) + { + Array.Copy(oldQueues, 0, newQueues, 0, newQueues.Length); + } + else + { + Array.Copy(oldQueues, 0, newQueues, 0, pos); + Array.Copy(oldQueues, pos + 1, newQueues, pos, newQueues.Length - pos); + } + + if (Interlocked.CompareExchange(ref _queues, newQueues, oldQueues) == oldQueues) + { + break; } } } } - internal class WorkStealingQueue + internal sealed class WorkStealingQueue { private const int INITIAL_SIZE = 32; internal volatile IThreadPoolWorkItem[] m_array = new IThreadPoolWorkItem[INITIAL_SIZE]; @@ -128,7 +132,7 @@ namespace System.Threading private volatile int m_headIndex = START_INDEX; private volatile int m_tailIndex = START_INDEX; - private SpinLock m_foreignLock = new SpinLock(false); + private SpinLock m_foreignLock = new SpinLock(enableThreadOwnerTracking: false); public void LocalPush(IThreadPoolWorkItem obj) { @@ -162,7 +166,7 @@ namespace System.Threading finally { if (lockTaken) - m_foreignLock.Exit(true); + m_foreignLock.Exit(useMemoryBarrier: true); } } @@ -187,7 +191,7 @@ namespace System.Threading if (count >= m_mask) { // We're full; expand the queue by doubling its size. - IThreadPoolWorkItem[] newArray = new IThreadPoolWorkItem[m_array.Length << 1]; + var newArray = new IThreadPoolWorkItem[m_array.Length << 1]; for (int i = 0; i < m_array.Length; i++) newArray[i] = m_array[(i + head) & m_mask]; @@ -204,23 +208,20 @@ namespace System.Threading finally { if (lockTaken) - m_foreignLock.Exit(false); + m_foreignLock.Exit(useMemoryBarrier: false); } } } + [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")] public bool LocalFindAndPop(IThreadPoolWorkItem obj) { // Fast path: check the tail. If equal, we can skip the lock. if (m_array[(m_tailIndex - 1) & m_mask] == obj) { - IThreadPoolWorkItem unused; - if (LocalPop(out unused)) - { - Debug.Assert(unused == obj); - return true; - } - return false; + IThreadPoolWorkItem unused = LocalPop(); + Debug.Assert(unused == null || unused == obj); + return unused != null; } // Else, do an O(N) search for the work item. The theory of work stealing and our @@ -236,13 +237,12 @@ namespace System.Threading if (m_array[i & m_mask] == obj) { // If we found the element, block out steals to avoid interference. - // @TODO: optimize away the lock? bool lockTaken = false; try { m_foreignLock.Enter(ref lockTaken); - // If we lost the race, bail. + // If we encountered a race condition, bail. if (m_array[i & m_mask] == null) return false; @@ -262,7 +262,7 @@ namespace System.Threading finally { if (lockTaken) - m_foreignLock.Exit(false); + m_foreignLock.Exit(useMemoryBarrier: false); } } } @@ -270,18 +270,20 @@ namespace System.Threading return false; } - public bool LocalPop(out IThreadPoolWorkItem obj) + public IThreadPoolWorkItem LocalPop() => m_headIndex < m_tailIndex ? LocalPopCore() : null; + + [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")] + private IThreadPoolWorkItem LocalPopCore() { while (true) { - // Decrement the tail using a fence to ensure subsequent read doesn't come before. int tail = m_tailIndex; if (m_headIndex >= tail) { - obj = null; - return false; + return null; } + // Decrement the tail using a fence to ensure subsequent read doesn't come before. tail -= 1; Interlocked.Exchange(ref m_tailIndex, tail); @@ -289,13 +291,13 @@ namespace System.Threading if (m_headIndex <= tail) { int idx = tail & m_mask; - obj = Volatile.Read(ref m_array[idx]); + IThreadPoolWorkItem obj = Volatile.Read(ref m_array[idx]); // Check for nulls in the array. if (obj == null) continue; m_array[idx] = null; - return true; + return obj; } else { @@ -309,224 +311,82 @@ namespace System.Threading { // Element still available. Take it. int idx = tail & m_mask; - obj = Volatile.Read(ref m_array[idx]); + IThreadPoolWorkItem obj = Volatile.Read(ref m_array[idx]); // Check for nulls in the array. if (obj == null) continue; m_array[idx] = null; - return true; + return obj; } else { - // We lost the race, element was stolen, restore the tail. + // If we encountered a race condition and element was stolen, restore the tail. m_tailIndex = tail + 1; - obj = null; - return false; + return null; } } finally { if (lockTaken) - m_foreignLock.Exit(false); + m_foreignLock.Exit(useMemoryBarrier: false); } } } } - public bool TrySteal(out IThreadPoolWorkItem obj, ref bool missedSteal) - { - return TrySteal(out obj, ref missedSteal, 0); // no blocking by default. - } + public bool CanSteal => m_headIndex < m_tailIndex; - private bool TrySteal(out IThreadPoolWorkItem obj, ref bool missedSteal, int millisecondsTimeout) + public IThreadPoolWorkItem TrySteal(ref bool missedSteal) { - obj = null; - while (true) { - if (m_headIndex >= m_tailIndex) - return false; - - bool taken = false; - try + if (CanSteal) { - m_foreignLock.TryEnter(millisecondsTimeout, ref taken); - if (taken) + bool taken = false; + try { - // Increment head, and ensure read of tail doesn't move before it (fence). - int head = m_headIndex; - Interlocked.Exchange(ref m_headIndex, head + 1); - - if (head < m_tailIndex) + m_foreignLock.TryEnter(ref taken); + if (taken) { - int idx = head & m_mask; - obj = Volatile.Read(ref m_array[idx]); + // Increment head, and ensure read of tail doesn't move before it (fence). + int head = m_headIndex; + Interlocked.Exchange(ref m_headIndex, head + 1); - // Check for nulls in the array. - if (obj == null) continue; + if (head < m_tailIndex) + { + int idx = head & m_mask; + IThreadPoolWorkItem obj = Volatile.Read(ref m_array[idx]); - m_array[idx] = null; - return true; - } - else - { - // Failed, restore head. - m_headIndex = head; - obj = null; - missedSteal = true; + // Check for nulls in the array. + if (obj == null) continue; + + m_array[idx] = null; + return obj; + } + else + { + // Failed, restore head. + m_headIndex = head; + } } } - else + finally { - missedSteal = true; + if (taken) + m_foreignLock.Exit(useMemoryBarrier: false); } - } - finally - { - if (taken) - m_foreignLock.Exit(false); + + missedSteal = true; } - return false; + return null; } } } - internal class QueueSegment - { - // Holds a segment of the queue. Enqueues/Dequeues start at element 0, and work their way up. - internal readonly IThreadPoolWorkItem[] nodes; - private const int QueueSegmentLength = 256; - - // Holds the indexes of the lowest and highest valid elements of the nodes array. - // The low index is in the lower 16 bits, high index is in the upper 16 bits. - // Use GetIndexes and CompareExchangeIndexes to manipulate this. - private volatile int indexes; - - // The next segment in the queue. - public volatile QueueSegment Next; - - - private const int SixteenBits = 0xffff; - - private void GetIndexes(out int upper, out int lower) - { - int i = indexes; - upper = (i >> 16) & SixteenBits; - lower = i & SixteenBits; - - Debug.Assert(upper >= lower); - Debug.Assert(upper <= nodes.Length); - Debug.Assert(lower <= nodes.Length); - Debug.Assert(upper >= 0); - Debug.Assert(lower >= 0); - } - - private bool CompareExchangeIndexes(ref int prevUpper, int newUpper, ref int prevLower, int newLower) - { - Debug.Assert(newUpper >= newLower); - Debug.Assert(newUpper <= nodes.Length); - Debug.Assert(newLower <= nodes.Length); - Debug.Assert(newUpper >= 0); - Debug.Assert(newLower >= 0); - Debug.Assert(newUpper >= prevUpper); - Debug.Assert(newLower >= prevLower); - Debug.Assert(newUpper == prevUpper ^ newLower == prevLower); - - int oldIndexes = (prevUpper << 16) | (prevLower & SixteenBits); - int newIndexes = (newUpper << 16) | (newLower & SixteenBits); - int prevIndexes = Interlocked.CompareExchange(ref indexes, newIndexes, oldIndexes); - prevUpper = (prevIndexes >> 16) & SixteenBits; - prevLower = prevIndexes & SixteenBits; - return prevIndexes == oldIndexes; - } - - public QueueSegment() - { - Debug.Assert(QueueSegmentLength <= SixteenBits); - nodes = new IThreadPoolWorkItem[QueueSegmentLength]; - } - - - public bool IsUsedUp() - { - int upper, lower; - GetIndexes(out upper, out lower); - return (upper == nodes.Length) && - (lower == nodes.Length); - } - - public bool TryEnqueue(IThreadPoolWorkItem node) - { - // - // If there's room in this segment, atomically increment the upper count (to reserve - // space for this node), then store the node. - // Note that this leaves a window where it will look like there is data in that - // array slot, but it hasn't been written yet. This is taken care of in TryDequeue - // with a busy-wait loop, waiting for the element to become non-null. This implies - // that we can never store null nodes in this data structure. - // - Debug.Assert(null != node); - - int upper, lower; - GetIndexes(out upper, out lower); - - while (true) - { - if (upper == nodes.Length) - return false; - - if (CompareExchangeIndexes(ref upper, upper + 1, ref lower, lower)) - { - Debug.Assert(Volatile.Read(ref nodes[upper]) == null); - Volatile.Write(ref nodes[upper], node); - return true; - } - } - } - - public bool TryDequeue(out IThreadPoolWorkItem node) - { - // - // If there are nodes in this segment, increment the lower count, then take the - // element we find there. - // - int upper, lower; - GetIndexes(out upper, out lower); - - while (true) - { - if (lower == upper) - { - node = null; - return false; - } - - if (CompareExchangeIndexes(ref upper, upper, ref lower, lower + 1)) - { - // It's possible that a concurrent call to Enqueue hasn't yet - // written the node reference to the array. We need to spin until - // it shows up. - SpinWait spinner = new SpinWait(); - while ((node = Volatile.Read(ref nodes[lower])) == null) - spinner.SpinOnce(); - - // Null-out the reference so the object can be GC'd earlier. - nodes[lower] = null; - - return true; - } - } - } - } - - // The head and tail of the queue. We enqueue to the head, and dequeue from the tail. - internal volatile QueueSegment queueHead; - internal volatile QueueSegment queueTail; - - internal static SparseArray allThreadQueues = new SparseArray(16); //TODO: base this on processor count, once the security restrictions are removed from Environment.ProcessorCount - + internal readonly LowLevelConcurrentQueue workItems = new LowLevelConcurrentQueue(); + private volatile int numOutstandingThreadRequests = 0; // The number of threads executing work items in the Dispatch method @@ -534,20 +394,16 @@ namespace System.Threading public ThreadPoolWorkQueue() { - queueTail = queueHead = new QueueSegment(); } - public ThreadPoolWorkQueueThreadLocals EnsureCurrentThreadHasQueue() - { - if (null == ThreadPoolWorkQueueThreadLocals.Current) - ThreadPoolWorkQueueThreadLocals.Current = new ThreadPoolWorkQueueThreadLocals(this); - return ThreadPoolWorkQueueThreadLocals.Current; - } + public ThreadPoolWorkQueueThreadLocals EnsureCurrentThreadHasQueue() => + ThreadPoolWorkQueueThreadLocals.threadLocals ?? + (ThreadPoolWorkQueueThreadLocals.threadLocals = new ThreadPoolWorkQueueThreadLocals(this)); internal void EnsureThreadRequested() { // - // If we have not yet requested #procs threads from the VM, then request a new thread. + // If we have not yet requested #procs threads, then request a new thread. // int count = numOutstandingThreadRequests; while (count < ThreadPoolGlobals.processorCount) @@ -555,7 +411,7 @@ namespace System.Threading int prev = Interlocked.CompareExchange(ref numOutstandingThreadRequests, count + 1, count); if (prev == count) { - ThreadPool.QueueDispatch(); + ThreadPool.RequestWorkerThread(); break; } count = prev; @@ -584,7 +440,7 @@ namespace System.Threading { ThreadPoolWorkQueueThreadLocals tl = null; if (!forceGlobal) - tl = ThreadPoolWorkQueueThreadLocals.Current; + tl = ThreadPoolWorkQueueThreadLocals.threadLocals; if (null != tl) { @@ -592,18 +448,7 @@ namespace System.Threading } else { - QueueSegment head = queueHead; - - while (!head.TryEnqueue(callback)) - { - Interlocked.CompareExchange(ref head.Next, new QueueSegment(), null); - - while (head.Next != null) - { - Interlocked.CompareExchange(ref queueHead, head.Next, head); - head = queueHead; - } - } + workItems.Enqueue(callback); } EnsureThreadRequested(); @@ -611,80 +456,58 @@ namespace System.Threading internal bool LocalFindAndPop(IThreadPoolWorkItem callback) { - ThreadPoolWorkQueueThreadLocals tl = ThreadPoolWorkQueueThreadLocals.Current; - if (null == tl) - return false; - - return tl.workStealingQueue.LocalFindAndPop(callback); + ThreadPoolWorkQueueThreadLocals tl = ThreadPoolWorkQueueThreadLocals.threadLocals; + return tl != null && tl.workStealingQueue.LocalFindAndPop(callback); } - public void Dequeue(ThreadPoolWorkQueueThreadLocals tl, out IThreadPoolWorkItem callback, out bool missedSteal) + public IThreadPoolWorkItem Dequeue(ThreadPoolWorkQueueThreadLocals tl, ref bool missedSteal) { - callback = null; - missedSteal = false; - WorkStealingQueue wsq = tl.workStealingQueue; + WorkStealingQueue localWsq = tl.workStealingQueue; + IThreadPoolWorkItem callback; - if (wsq.LocalPop(out callback)) - Debug.Assert(null != callback); - - if (null == callback) + if ((callback = localWsq.LocalPop()) == null && // first try the local queue + !workItems.TryDequeue(out callback)) // then try the global queue { - QueueSegment tail = queueTail; - while (true) - { - if (tail.TryDequeue(out callback)) - { - Debug.Assert(null != callback); - break; - } - - if (null == tail.Next || !tail.IsUsedUp()) - { - break; - } - else - { - Interlocked.CompareExchange(ref queueTail, tail.Next, tail); - tail = queueTail; - } - } - } - - if (null == callback) - { - WorkStealingQueue[] otherQueues = allThreadQueues.Current; - int i = tl.random.Next(otherQueues.Length); - int c = otherQueues.Length; + // finally try to steal from another thread's local queue + WorkStealingQueue[] queues = WorkStealingQueueList.Queues; + int c = queues.Length; + Debug.Assert(c > 0, "There must at least be a queue for this thread."); + int maxIndex = c - 1; + int i = tl.random.Next(c); while (c > 0) { - WorkStealingQueue otherQueue = Volatile.Read(ref otherQueues[i % otherQueues.Length]); - if (otherQueue != null && - otherQueue != wsq && - otherQueue.TrySteal(out callback, ref missedSteal)) + i = (i < maxIndex) ? i + 1 : 0; + WorkStealingQueue otherQueue = queues[i]; + if (otherQueue != localWsq && otherQueue.CanSteal) { - Debug.Assert(null != callback); - break; + callback = otherQueue.TrySteal(ref missedSteal); + if (callback != null) + { + break; + } } - i++; c--; } } + + return callback; } - - //Per-appDomain quantum (in ms) for which the thread keeps processing - //requests in the current domain. - private const uint tpQuantum = 30U; - - internal static void Dispatch() + /// + /// Dispatches work items to this thread. + /// + /// + /// true if this thread did as much work as was available or its quantum expired. + /// false if this thread stopped working early. + /// + internal static bool Dispatch() { var workQueue = ThreadPoolGlobals.workQueue; // - // The clock is ticking! We have ThreadPoolGlobals.tpQuantum milliseconds to get some work done, and then - // we need to return to the VM. + // Save the start time // - int quantumStartTime = Environment.TickCount; + int startTickCount = Environment.TickCount; // // Update our records to indicate that an outstanding request for a thread has now been fulfilled. @@ -709,25 +532,26 @@ namespace System.Threading ThreadPoolWorkQueueThreadLocals tl = workQueue.EnsureCurrentThreadHasQueue(); // - // Loop until our quantum expires. + // Loop until our quantum expires or there is no work. // - while ((Environment.TickCount - quantumStartTime) < tpQuantum) + while (ThreadPool.KeepDispatching(startTickCount)) { bool missedSteal = false; - workQueue.Dequeue(tl, out workItem, out missedSteal); + workItem = workQueue.Dequeue(tl, ref missedSteal); if (workItem == null) { // - // No work. We're going to return to the VM once we leave this protected region. + // No work. // If we missed a steal, though, there may be more work in the queue. - // Instead of looping around and trying again, we'll just request another thread. This way - // we won't starve other AppDomains while we spin trying to get locks, and hopefully the thread + // Instead of looping around and trying again, we'll just request another thread. Hopefully the thread // that owns the contended work-stealing queue will pick up its own workitems in the meantime, // which will be more efficient than this thread doing it anyway. // needAnotherThread = missedSteal; - return; + + // Tell the VM we're returning normally, not because Hill Climbing asked us to return. + return true; } // @@ -746,12 +570,21 @@ namespace System.Threading workItem = null; SynchronizationContext.SetSynchronizationContext(null); } + + RuntimeThread.CurrentThread.ResetThreadPoolThread(); + + if (!ThreadPool.NotifyWorkItemComplete()) + return false; } + + // If we get here, it's because our quantum expired. + return true; } catch (Exception e) { // Work items should not allow exceptions to escape. For example, Task catches and stores any exceptions. Environment.FailFast("Unhandled exception in ThreadPool dispatch loop", e); + return true; // Will never actually be executed because Environment.FailFast doesn't return } finally { @@ -768,22 +601,46 @@ namespace System.Threading } } + // Simple random number generator. We don't need great randomness, we just need a little and for it to be fast. + internal struct FastRandom // xorshift prng + { + private uint _w, _x, _y, _z; + + public FastRandom(int seed) + { + _x = (uint)seed; + _w = 88675123; + _y = 362436069; + _z = 521288629; + } + + public int Next(int maxValue) + { + Debug.Assert(maxValue > 0); + + uint t = _x ^ (_x << 11); + _x = _y; _y = _z; _z = _w; + _w = _w ^ (_w >> 19) ^ (t ^ (t >> 8)); + + return (int)(_w % (uint)maxValue); + } + } + // Holds a WorkStealingQueue, and remmoves it from the list when this object is no longer referened. internal sealed class ThreadPoolWorkQueueThreadLocals { [ThreadStatic] - public static ThreadPoolWorkQueueThreadLocals Current; - + public static ThreadPoolWorkQueueThreadLocals threadLocals; public readonly ThreadPoolWorkQueue workQueue; public readonly ThreadPoolWorkQueue.WorkStealingQueue workStealingQueue; - public readonly Random random = new Random(Environment.CurrentManagedThreadId); + public FastRandom random = new FastRandom(Environment.CurrentManagedThreadId); // mutable struct, do not copy or make readonly public ThreadPoolWorkQueueThreadLocals(ThreadPoolWorkQueue tpq) { workQueue = tpq; workStealingQueue = new ThreadPoolWorkQueue.WorkStealingQueue(); - ThreadPoolWorkQueue.allThreadQueues.Add(workStealingQueue); + ThreadPoolWorkQueue.WorkStealingQueueList.Add(workStealingQueue); } private void CleanUp() @@ -792,23 +649,15 @@ namespace System.Threading { if (null != workQueue) { - bool done = false; - while (!done) + IThreadPoolWorkItem cb; + while ((cb = workStealingQueue.LocalPop()) != null) { - IThreadPoolWorkItem cb = null; - if (workStealingQueue.LocalPop(out cb)) - { - Debug.Assert(null != cb); - workQueue.Enqueue(cb, true); - } - else - { - done = true; - } + Debug.Assert(null != cb); + workQueue.Enqueue(cb, forceGlobal: true); } } - ThreadPoolWorkQueue.allThreadQueues.Remove(workStealingQueue); + ThreadPoolWorkQueue.WorkStealingQueueList.Remove(workStealingQueue); } } @@ -845,8 +694,8 @@ namespace System.Threading internal sealed class QueueUserWorkItemCallback : IThreadPoolWorkItem { private WaitCallback callback; - private ExecutionContext context; - private Object state; + private readonly ExecutionContext context; + private readonly Object state; #if DEBUG private volatile int executed; @@ -883,7 +732,11 @@ namespace System.Threading try { if (context == null) - callback(state); + { + WaitCallback cb = callback; + callback = null; + cb(state); + } else ExecutionContext.Run(context, ccb, this); } @@ -894,22 +747,21 @@ namespace System.Threading } } - internal static ContextCallback ccb = new ContextCallback(WaitCallback_Context); + internal static readonly ContextCallback ccb = new ContextCallback(WaitCallback_Context); private static void WaitCallback_Context(Object state) { QueueUserWorkItemCallback obj = (QueueUserWorkItemCallback)state; - WaitCallback wc = obj.callback as WaitCallback; + WaitCallback wc = obj.callback; Debug.Assert(null != wc); wc(obj.state); } } - internal sealed class QueueUserWorkItemCallbackDefaultContext : IThreadPoolWorkItem { private WaitCallback callback; - private Object state; + private readonly Object state; #if DEBUG private volatile int executed; @@ -953,23 +805,172 @@ namespace System.Threading } } - internal static ContextCallback ccb = new ContextCallback(WaitCallback_Context); + internal static readonly ContextCallback ccb = new ContextCallback(WaitCallback_Context); private static void WaitCallback_Context(Object state) { QueueUserWorkItemCallbackDefaultContext obj = (QueueUserWorkItemCallbackDefaultContext)state; - WaitCallback wc = obj.callback as WaitCallback; + WaitCallback wc = obj.callback; Debug.Assert(null != wc); + obj.callback = null; wc(obj.state); } } + internal class _ThreadPoolWaitOrTimerCallback + { + private WaitOrTimerCallback _waitOrTimerCallback; + private ExecutionContext _executionContext; + private Object _state; + private static readonly ContextCallback _ccbt = new ContextCallback(WaitOrTimerCallback_Context_t); + private static readonly ContextCallback _ccbf = new ContextCallback(WaitOrTimerCallback_Context_f); + + internal _ThreadPoolWaitOrTimerCallback(WaitOrTimerCallback waitOrTimerCallback, Object state, bool flowExecutionContext) + { + _waitOrTimerCallback = waitOrTimerCallback; + _state = state; + + if (flowExecutionContext) + { + // capture the exection context + _executionContext = ExecutionContext.Capture(); + } + } + + private static void WaitOrTimerCallback_Context_t(Object state) => + WaitOrTimerCallback_Context(state, timedOut: true); + + private static void WaitOrTimerCallback_Context_f(Object state) => + WaitOrTimerCallback_Context(state, timedOut: false); + + private static void WaitOrTimerCallback_Context(Object state, bool timedOut) + { + _ThreadPoolWaitOrTimerCallback helper = (_ThreadPoolWaitOrTimerCallback)state; + helper._waitOrTimerCallback(helper._state, timedOut); + } + + // call back helper + internal static void PerformWaitOrTimerCallback(_ThreadPoolWaitOrTimerCallback helper, bool timedOut) + { + Debug.Assert(helper != null, "Null state passed to PerformWaitOrTimerCallback!"); + // call directly if it is an unsafe call OR EC flow is suppressed + if (helper._executionContext == null) + { + WaitOrTimerCallback callback = helper._waitOrTimerCallback; + callback(helper._state, timedOut); + } + else + { + ExecutionContext.Run(helper._executionContext, timedOut ? _ccbt : _ccbf, helper); + } + } + } + public static partial class ThreadPool { - public static bool QueueUserWorkItem(WaitCallback callBack) => - QueueUserWorkItem(callBack, null); + [CLSCompliant(false)] + public static RegisteredWaitHandle RegisterWaitForSingleObject( + WaitHandle waitObject, + WaitOrTimerCallback callBack, + Object state, + uint millisecondsTimeOutInterval, + bool executeOnlyOnce) + { + if (millisecondsTimeOutInterval > (uint)int.MaxValue && millisecondsTimeOutInterval != uint.MaxValue) + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeOutInterval), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); + return RegisterWaitForSingleObject(waitObject, callBack, state, millisecondsTimeOutInterval, executeOnlyOnce, true); + } - public static bool QueueUserWorkItem(WaitCallback callBack, object state) + [CLSCompliant(false)] + public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( + WaitHandle waitObject, + WaitOrTimerCallback callBack, + Object state, + uint millisecondsTimeOutInterval, + bool executeOnlyOnce) + { + if (millisecondsTimeOutInterval > (uint)int.MaxValue && millisecondsTimeOutInterval != uint.MaxValue) + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeOutInterval), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); + return RegisterWaitForSingleObject(waitObject, callBack, state, millisecondsTimeOutInterval, executeOnlyOnce, false); + } + + public static RegisteredWaitHandle RegisterWaitForSingleObject( + WaitHandle waitObject, + WaitOrTimerCallback callBack, + Object state, + int millisecondsTimeOutInterval, + bool executeOnlyOnce) + { + if (millisecondsTimeOutInterval < -1) + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeOutInterval), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); + return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)millisecondsTimeOutInterval, executeOnlyOnce, true); + } + + public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( + WaitHandle waitObject, + WaitOrTimerCallback callBack, + Object state, + int millisecondsTimeOutInterval, + bool executeOnlyOnce) + { + if (millisecondsTimeOutInterval < -1) + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeOutInterval), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); + return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)millisecondsTimeOutInterval, executeOnlyOnce, false); + } + + public static RegisteredWaitHandle RegisterWaitForSingleObject( + WaitHandle waitObject, + WaitOrTimerCallback callBack, + Object state, + long millisecondsTimeOutInterval, + bool executeOnlyOnce) + { + if (millisecondsTimeOutInterval < -1 || millisecondsTimeOutInterval > int.MaxValue) + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeOutInterval), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); + return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)millisecondsTimeOutInterval, executeOnlyOnce, true); + } + + public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( + WaitHandle waitObject, + WaitOrTimerCallback callBack, + Object state, + long millisecondsTimeOutInterval, + bool executeOnlyOnce) + { + if (millisecondsTimeOutInterval < -1 || millisecondsTimeOutInterval > int.MaxValue) + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeOutInterval), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); + return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)millisecondsTimeOutInterval, executeOnlyOnce, false); + } + + public static RegisteredWaitHandle RegisterWaitForSingleObject( + WaitHandle waitObject, + WaitOrTimerCallback callBack, + Object state, + TimeSpan timeout, + bool executeOnlyOnce) + { + int tm = WaitHandle.ToTimeoutMilliseconds(timeout); + return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)tm, executeOnlyOnce, true); + } + + public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( + WaitHandle waitObject, + WaitOrTimerCallback callBack, + Object state, + TimeSpan timeout, + bool executeOnlyOnce) + { + int tm = WaitHandle.ToTimeoutMilliseconds(timeout); + return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)tm, executeOnlyOnce, false); + } + + public static bool QueueUserWorkItem(WaitCallback callBack) => + QueueUserWorkItem(callBack, null, preferLocal: false); + + public static bool QueueUserWorkItem(WaitCallback callBack, object state) => + QueueUserWorkItem(callBack, state, preferLocal: false); + + public static bool QueueUserWorkItem(WaitCallback callBack, object state, bool preferLocal) { if (callBack == null) { @@ -982,7 +983,7 @@ namespace System.Threading new QueueUserWorkItemCallbackDefaultContext(callBack, state) : (IThreadPoolWorkItem)new QueueUserWorkItemCallback(callBack, state, context); - ThreadPoolGlobals.workQueue.Enqueue(tpcallBack, forceGlobal: true); + ThreadPoolGlobals.workQueue.Enqueue(tpcallBack, forceGlobal: !preferLocal); return true; } @@ -1007,55 +1008,35 @@ namespace System.Threading ThreadPoolGlobals.workQueue.Enqueue(workItem, forceGlobal); } - internal static void NotifyWorkItemProgress() - { - } - // This method tries to take the target callback out of the current thread's queue. internal static bool TryPopCustomWorkItem(IThreadPoolWorkItem workItem) { + Debug.Assert(null != workItem); return ThreadPoolGlobals.workQueue.LocalFindAndPop(workItem); } // Get all workitems. Called by TaskScheduler in its debugger hooks. internal static IEnumerable GetQueuedWorkItems() { - return EnumerateQueuedWorkItems(ThreadPoolWorkQueue.allThreadQueues.Current, ThreadPoolGlobals.workQueue.queueTail); - } - - internal static IEnumerable EnumerateQueuedWorkItems(ThreadPoolWorkQueue.WorkStealingQueue[] wsQueues, ThreadPoolWorkQueue.QueueSegment globalQueueTail) - { - if (wsQueues != null) + // Enumerate the global queue + foreach (IThreadPoolWorkItem workItem in ThreadPoolGlobals.workQueue.workItems) { - // First, enumerate all workitems in thread-local queues. - foreach (ThreadPoolWorkQueue.WorkStealingQueue wsq in wsQueues) - { - if (wsq != null && wsq.m_array != null) - { - IThreadPoolWorkItem[] items = wsq.m_array; - for (int i = 0; i < items.Length; i++) - { - IThreadPoolWorkItem item = items[i]; - if (item != null) - yield return item; - } - } - } + yield return workItem; } - if (globalQueueTail != null) + // Enumerate each local queue + foreach (ThreadPoolWorkQueue.WorkStealingQueue wsq in ThreadPoolWorkQueue.WorkStealingQueueList.Queues) { - // Now the global queue - for (ThreadPoolWorkQueue.QueueSegment segment = globalQueueTail; - segment != null; - segment = segment.Next) + if (wsq != null && wsq.m_array != null) { - IThreadPoolWorkItem[] items = segment.nodes; + IThreadPoolWorkItem[] items = wsq.m_array; for (int i = 0; i < items.Length; i++) { IThreadPoolWorkItem item = items[i]; if (item != null) + { yield return item; + } } } } @@ -1063,13 +1044,20 @@ namespace System.Threading internal static IEnumerable GetLocallyQueuedWorkItems() { - return EnumerateQueuedWorkItems(new ThreadPoolWorkQueue.WorkStealingQueue[] { ThreadPoolWorkQueueThreadLocals.Current.workStealingQueue }, null); + ThreadPoolWorkQueue.WorkStealingQueue wsq = ThreadPoolWorkQueueThreadLocals.threadLocals.workStealingQueue; + if (wsq != null && wsq.m_array != null) + { + IThreadPoolWorkItem[] items = wsq.m_array; + for (int i = 0; i < items.Length; i++) + { + IThreadPoolWorkItem item = items[i]; + if (item != null) + yield return item; + } + } } - internal static IEnumerable GetGloballyQueuedWorkItems() - { - return EnumerateQueuedWorkItems(null, ThreadPoolGlobals.workQueue.queueTail); - } + internal static IEnumerable GetGloballyQueuedWorkItems() => ThreadPoolGlobals.workQueue.workItems; private static object[] ToObjectArray(IEnumerable workitems) { @@ -1111,39 +1099,23 @@ namespace System.Threading [CLSCompliant(false)] unsafe public static bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) { - // OS doesn't signal handle, so do it here + // OS doesn't signal handle, so do it here (CoreCLR does this assignment in ThreadPoolNative::CorPostQueuedCompletionStatus) overlapped->InternalLow = (IntPtr)0; - // A quick-and-dirty implementation that runs the callback on the normal thread pool + // Both types of callbacks are executed on the same thread pool return UnsafeQueueUserWorkItem(NativeOverlappedCallback, (IntPtr)overlapped); } - internal static bool IsThreadPoolThread { get { return ThreadPoolWorkQueueThreadLocals.Current != null; } } - - internal static void RegisterWaitForSingleObject( - WaitHandle waitObject, - Action callBack, - Object state, - int millisecondsTimeOutInterval, - bool executeOnlyOnce) + [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Please use ThreadPool.BindHandle(SafeHandle) instead.", false)] + public static bool BindHandle(IntPtr osHandle) { - // - // This is just a quick-and-dirty implementation to make TaskFactory.FromAsync - // work for the few apps that are using it. A proper implementation would coalesce - // multiple waits onto a single thread, so that fewer machine resources would be - // consumed. - // - // Also, we're not returning a RegisteredWaitHandleObject, as in the real public - // version of this API, simply because the single consumer of this API doesn't - // need it. - // - - Debug.Assert(executeOnlyOnce); - - QueueUserWorkItem(_ => - { - bool timedOut = waitObject.WaitOne(millisecondsTimeOutInterval); - callBack(state, timedOut); - }); + throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // Replaced by ThreadPoolBoundHandle.BindHandle } + + public static bool BindHandle(SafeHandle osHandle) + { + throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // Replaced by ThreadPoolBoundHandle.BindHandle + } + + internal static bool IsThreadPoolThread { get { return ThreadPoolWorkQueueThreadLocals.threadLocals != null; } } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs new file mode 100644 index 0000000000..7a84894a4f --- /dev/null +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs @@ -0,0 +1,36 @@ +// 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 Internal.Runtime.Augments; + +namespace System.Threading +{ + /// + /// Ensures RuntimeThread.CurrentThread is initialized for a callback running on a thread pool thread. + /// If WinRT is enabled, also ensures the Windows Runtime is initialized during the execution of the callback. + /// + /// + /// This structure does not implement IDisposable to save on exception support, which callers do not need. + /// + internal struct ThreadPoolCallbackWrapper + { + private RuntimeThread _currentThread; + + public static ThreadPoolCallbackWrapper Enter() + { + return new ThreadPoolCallbackWrapper + { + _currentThread = RuntimeThread.InitializeThreadPoolThread(), + }; + } + + public void Exit(bool resetThread = true) + { + if (resetThread) + { + _currentThread.ResetThreadPoolThread(); + } + } + } +} diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/Timer.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/Timer.Windows.cs index 435777750e..7020e1a2ce 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/Timer.Windows.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/Timer.Windows.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Internal.Runtime.Augments; using System.Diagnostics; using System.Runtime.InteropServices; @@ -18,8 +17,9 @@ namespace System.Threading [NativeCallable(CallingConvention = CallingConvention.StdCall)] private static void TimerCallback(IntPtr instance, IntPtr context, IntPtr timer) { - RuntimeThread.InitializeThreadPoolThread(); + var wrapper = ThreadPoolCallbackWrapper.Enter(); Instance.FireNextTimers(); + wrapper.Exit(); } private unsafe void SetTimer(uint actualDuration) diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/Timer.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/Timer.cs index 6da95b55e7..fc284fd4e9 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/Timer.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/Timer.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Diagnostics.Contracts; namespace System.Threading { @@ -138,7 +137,19 @@ namespace System.Threading if (timer.m_period != Timeout.UnsignedInfinite) { timer.m_startTicks = nowTicks; - timer.m_dueTime = timer.m_period; + uint elapsedForNextDueTime = elapsed - timer.m_dueTime; + if (elapsedForNextDueTime < timer.m_period) + { + // Discount the extra time that has elapsed since the previous firing + // to prevent the timer ticks from drifting + timer.m_dueTime = timer.m_period - elapsedForNextDueTime; + } + else + { + // Enough time has elapsed to fire the timer yet again. The timer is not able to keep up + // with the short period, have it fire 1 ms from now to avoid spnning without delay. + timer.m_dueTime = 1; + } // // This is a repeating timer; schedule it to run again. @@ -498,7 +509,6 @@ namespace System.Threading throw new ArgumentOutOfRangeException(nameof(dueTime), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); if (period < -1) throw new ArgumentOutOfRangeException(nameof(period), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); - Contract.EndContractBlock(); TimerSetup(callback, state, (UInt32)dueTime, (UInt32)period); } @@ -545,7 +555,6 @@ namespace System.Threading throw new ArgumentOutOfRangeException(nameof(dueTime), SR.ArgumentOutOfRange_TimeoutTooLarge); if (period > MAX_SUPPORTED_TIMEOUT) throw new ArgumentOutOfRangeException(nameof(period), SR.ArgumentOutOfRange_PeriodTooLarge); - Contract.EndContractBlock(); TimerSetup(callback, state, (UInt32)dueTime, (UInt32)period); } @@ -566,7 +575,6 @@ namespace System.Threading { if (callback == null) throw new ArgumentNullException(nameof(TimerCallback)); - Contract.EndContractBlock(); _timer = new TimerHolder(new TimerQueueTimer(callback, state, dueTime, period)); } @@ -577,7 +585,6 @@ namespace System.Threading throw new ArgumentOutOfRangeException(nameof(dueTime), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); if (period < -1) throw new ArgumentOutOfRangeException(nameof(period), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); - Contract.EndContractBlock(); return _timer.m_timer.Change((UInt32)dueTime, (UInt32)period); } @@ -603,7 +610,6 @@ namespace System.Threading throw new ArgumentOutOfRangeException(nameof(dueTime), SR.ArgumentOutOfRange_TimeoutTooLarge); if (period > MAX_SUPPORTED_TIMEOUT) throw new ArgumentOutOfRangeException(nameof(period), SR.ArgumentOutOfRange_PeriodTooLarge); - Contract.EndContractBlock(); return _timer.m_timer.Change((UInt32)dueTime, (UInt32)period); } @@ -612,7 +618,6 @@ namespace System.Threading { if (notifyObject == null) throw new ArgumentNullException(nameof(notifyObject)); - Contract.EndContractBlock(); return _timer.Close(notifyObject); } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/Volatile.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/Volatile.cs index 5a63a88cfc..d9a009d731 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/Volatile.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/Volatile.cs @@ -7,6 +7,8 @@ using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Runtime; +using Internal.Runtime.CompilerServices; + namespace System.Threading { // diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitHandle.Unix.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitHandle.Unix.cs index b1053a911e..2d54218f35 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitHandle.Unix.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitHandle.Unix.cs @@ -9,16 +9,17 @@ namespace System.Threading { public abstract partial class WaitHandle { - private static bool WaitOneCore(IntPtr handle, int millisecondsTimeout) => - WaitSubsystem.Wait(handle, millisecondsTimeout); + private static bool WaitOneCore(IntPtr handle, int millisecondsTimeout, bool interruptible) => + WaitSubsystem.Wait(handle, millisecondsTimeout, interruptible); private static int WaitAnyCore( RuntimeThread currentThread, SafeWaitHandle[] safeWaitHandles, WaitHandle[] waitHandles, + int numWaitHandles, int millisecondsTimeout) { - return WaitSubsystem.Wait(currentThread, safeWaitHandles, waitHandles, false, millisecondsTimeout); + return WaitSubsystem.Wait(currentThread, safeWaitHandles, waitHandles, numWaitHandles, false, millisecondsTimeout); } private static bool WaitAllCore( @@ -27,7 +28,7 @@ namespace System.Threading WaitHandle[] waitHandles, int millisecondsTimeout) { - return WaitSubsystem.Wait(currentThread, safeWaitHandles, waitHandles, true, millisecondsTimeout) != WaitTimeout; + return WaitSubsystem.Wait(currentThread, safeWaitHandles, waitHandles, waitHandles.Length, true, millisecondsTimeout) != WaitTimeout; } private static bool SignalAndWaitCore(IntPtr handleToSignal, IntPtr handleToWaitOn, int millisecondsTimeout) diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs index 9bb6f47d85..bda696cca6 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs @@ -12,29 +12,32 @@ namespace System.Threading { public abstract partial class WaitHandle { - internal static unsafe int WaitForSingleObject(IntPtr handle, int millisecondsTimeout) + internal static unsafe int WaitForSingleObject(IntPtr handle, int millisecondsTimeout, bool interruptible) { - SynchronizationContext context = RuntimeThread.CurrentThread.SynchronizationContext; - bool useSyncContextWait = (context != null) && context.IsWaitNotificationRequired(); - - if (useSyncContextWait) + if (interruptible) { - var handles = new IntPtr[1] { handle }; - return context.Wait(handles, false, millisecondsTimeout); + SynchronizationContext context = RuntimeThread.CurrentThread.SynchronizationContext; + bool useSyncContextWait = (context != null) && context.IsWaitNotificationRequired(); + + if (useSyncContextWait) + { + var handles = new IntPtr[1] { handle }; + return context.Wait(handles, false, millisecondsTimeout); + } } - return WaitForMultipleObjectsIgnoringSyncContext(&handle, 1, false, millisecondsTimeout); + return WaitForMultipleObjectsIgnoringSyncContext(&handle, 1, false, millisecondsTimeout, interruptible); } internal static unsafe int WaitForMultipleObjectsIgnoringSyncContext(IntPtr[] handles, int numHandles, bool waitAll, int millisecondsTimeout) { fixed (IntPtr* pHandles = handles) { - return WaitForMultipleObjectsIgnoringSyncContext(pHandles, numHandles, waitAll, millisecondsTimeout); + return WaitForMultipleObjectsIgnoringSyncContext(pHandles, numHandles, waitAll, millisecondsTimeout, true); } } - private static unsafe int WaitForMultipleObjectsIgnoringSyncContext(IntPtr* pHandles, int numHandles, bool waitAll, int millisecondsTimeout) + private static unsafe int WaitForMultipleObjectsIgnoringSyncContext(IntPtr* pHandles, int numHandles, bool waitAll, int millisecondsTimeout, bool interruptible) { Debug.Assert(millisecondsTimeout >= -1); @@ -94,11 +97,11 @@ namespace System.Threading return result; } - private static bool WaitOneCore(IntPtr handle, int millisecondsTimeout) + private static bool WaitOneCore(IntPtr handle, int millisecondsTimeout, bool interruptible) { Debug.Assert(millisecondsTimeout >= -1); - int ret = WaitForSingleObject(handle, millisecondsTimeout); + int ret = WaitForSingleObject(handle, millisecondsTimeout, interruptible); if (ret == WaitAbandoned) { @@ -158,19 +161,20 @@ namespace System.Threading RuntimeThread currentThread, SafeWaitHandle[] safeWaitHandles, WaitHandle[] waitHandles, + int numWaitHandles, int millisecondsTimeout) { Debug.Assert(currentThread == RuntimeThread.CurrentThread); Debug.Assert(safeWaitHandles != null); - Debug.Assert(safeWaitHandles.Length >= waitHandles.Length); + Debug.Assert(safeWaitHandles.Length >= numWaitHandles); Debug.Assert(waitHandles != null); - Debug.Assert(waitHandles.Length > 0); - Debug.Assert(waitHandles.Length <= MaxWaitHandles); + Debug.Assert(numWaitHandles > 0); + Debug.Assert(numWaitHandles <= MaxWaitHandles); Debug.Assert(millisecondsTimeout >= -1); - int ret = WaitMultiple(currentThread, safeWaitHandles, waitHandles.Length, millisecondsTimeout, false /* waitany*/ ); + int ret = WaitMultiple(currentThread, safeWaitHandles, numWaitHandles, millisecondsTimeout, false /* waitany*/ ); - if ((WaitAbandoned <= ret) && (WaitAbandoned + waitHandles.Length > ret)) + if ((WaitAbandoned <= ret) && (WaitAbandoned + numWaitHandles > ret)) { int mutexIndex = ret - WaitAbandoned; if (0 <= mutexIndex && mutexIndex < waitHandles.Length) diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitHandle.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitHandle.cs index 4ae48a6914..ef7f47f5ee 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitHandle.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitHandle.cs @@ -13,7 +13,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; using System.Runtime.InteropServices; using Internal.Runtime.Augments; using Microsoft.Win32.SafeHandles; @@ -104,12 +103,11 @@ namespace System.Threading { throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); } - Contract.EndContractBlock(); return WaitOneCore(millisecondsTimeout); } - private bool WaitOneCore(int millisecondsTimeout) + private bool WaitOneCore(int millisecondsTimeout, bool interruptible = true) { Debug.Assert(millisecondsTimeout >= -1); @@ -125,7 +123,7 @@ namespace System.Threading waitHandle.DangerousAddRef(); try { - return WaitOneCore(waitHandle.DangerousGetHandle(), millisecondsTimeout); + return WaitOneCore(waitHandle.DangerousGetHandle(), millisecondsTimeout, interruptible); } finally { @@ -139,6 +137,8 @@ namespace System.Threading public virtual bool WaitOne(int millisecondsTimeout, bool exitContext) => WaitOne(millisecondsTimeout); public virtual bool WaitOne(TimeSpan timeout, bool exitContext) => WaitOne(timeout); + internal bool WaitOne(bool interruptible) => WaitOneCore(Timeout.Infinite, interruptible); + /// /// Obtains all of the corresponding safe wait handles and adds a ref to each. Since the /// property is publically modifiable, this makes sure that we add and release refs one the same set of safe wait @@ -147,19 +147,21 @@ namespace System.Threading private static SafeWaitHandle[] ObtainSafeWaitHandles( RuntimeThread currentThread, WaitHandle[] waitHandles, + int numWaitHandles, out SafeWaitHandle[] rentedSafeWaitHandles) { Debug.Assert(currentThread == RuntimeThread.CurrentThread); Debug.Assert(waitHandles != null); - Debug.Assert(waitHandles.Length > 0); - Debug.Assert(waitHandles.Length <= MaxWaitHandles); + Debug.Assert(numWaitHandles > 0); + Debug.Assert(numWaitHandles <= MaxWaitHandles); + Debug.Assert(numWaitHandles <= waitHandles.Length); - rentedSafeWaitHandles = currentThread.RentWaitedSafeWaitHandleArray(waitHandles.Length); - SafeWaitHandle[] safeWaitHandles = rentedSafeWaitHandles ?? new SafeWaitHandle[waitHandles.Length]; + rentedSafeWaitHandles = currentThread.RentWaitedSafeWaitHandleArray(numWaitHandles); + SafeWaitHandle[] safeWaitHandles = rentedSafeWaitHandles ?? new SafeWaitHandle[numWaitHandles]; bool success = false; try { - for (int i = 0; i < waitHandles.Length; ++i) + for (int i = 0; i < numWaitHandles; ++i) { WaitHandle waitHandle = waitHandles[i]; if (waitHandle == null) @@ -183,7 +185,7 @@ namespace System.Threading { if (!success) { - for (int i = 0; i < waitHandles.Length; ++i) + for (int i = 0; i < numWaitHandles; ++i) { SafeWaitHandle safeWaitHandle = safeWaitHandles[i]; if (safeWaitHandle == null) @@ -231,11 +233,10 @@ namespace System.Threading { throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); } - Contract.EndContractBlock(); RuntimeThread currentThread = RuntimeThread.CurrentThread; SafeWaitHandle[] rentedSafeWaitHandles; - SafeWaitHandle[] safeWaitHandles = ObtainSafeWaitHandles(currentThread, waitHandles, out rentedSafeWaitHandles); + SafeWaitHandle[] safeWaitHandles = ObtainSafeWaitHandles(currentThread, waitHandles, waitHandles.Length, out rentedSafeWaitHandles); try { return WaitAllCore(currentThread, safeWaitHandles, waitHandles, millisecondsTimeout); @@ -271,7 +272,9 @@ namespace System.Threading ** signalled or timeout milliseonds have elapsed. ========================================================================*/ - public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout) + public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout) => WaitAny(waitHandles, waitHandles?.Length ?? 0, millisecondsTimeout); + + internal static int WaitAny(WaitHandle[] waitHandles, int numWaitHandles, int millisecondsTimeout) { if (waitHandles == null) { @@ -289,18 +292,17 @@ namespace System.Threading { throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); } - Contract.EndContractBlock(); RuntimeThread currentThread = RuntimeThread.CurrentThread; SafeWaitHandle[] rentedSafeWaitHandles; - SafeWaitHandle[] safeWaitHandles = ObtainSafeWaitHandles(currentThread, waitHandles, out rentedSafeWaitHandles); + SafeWaitHandle[] safeWaitHandles = ObtainSafeWaitHandles(currentThread, waitHandles, numWaitHandles, out rentedSafeWaitHandles); try { - return WaitAnyCore(currentThread, safeWaitHandles, waitHandles, millisecondsTimeout); + return WaitAnyCore(currentThread, safeWaitHandles, waitHandles, numWaitHandles, millisecondsTimeout); } finally { - for (int i = 0; i < waitHandles.Length; ++i) + for (int i = 0; i < numWaitHandles; ++i) { safeWaitHandles[i].DangerousRelease(); safeWaitHandles[i] = null; @@ -353,7 +355,6 @@ namespace System.Threading throw new ObjectDisposedException(null, SR.ObjectDisposed_Generic); } - Contract.EndContractBlock(); safeWaitHandleToSignal.DangerousAddRef(); try @@ -405,7 +406,7 @@ namespace System.Threading internal static void ThrowInvalidHandleException() { var ex = new InvalidOperationException(SR.InvalidOperation_InvalidHandle); - ex.SetErrorCode(__HResults.ERROR_INVALID_HANDLE); + ex.SetErrorCode(HResults.E_HANDLE); throw ex; } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitHandleArray.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitHandleArray.cs index 0a3f675ed2..9f8ee32cf6 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitHandleArray.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitHandleArray.cs @@ -66,10 +66,16 @@ namespace System.Threading { Debug.Assert(_items != null); - EqualityComparer comparer = EqualityComparer.Default; for (int i = 0; i < _items.Length; ++i) { - Debug.Assert(comparer.Equals(_items[i], default(T))); + // Do not call EqualityComparer.Default here as it may call type loader. Type loader uses + // locks and we would end up with infinite recursion. + // Debug.Assert(EqualityComparer.Default.Equals(_items[i], default(T))); + + if (default(T) != null) + Debug.Assert(_items[i].Equals(default(T))); + else + Debug.Assert(_items[i] == null); } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitSubsystem.ThreadWaitInfo.Unix.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitSubsystem.ThreadWaitInfo.Unix.cs index 622797805d..6e43368282 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitSubsystem.ThreadWaitInfo.Unix.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitSubsystem.ThreadWaitInfo.Unix.cs @@ -64,6 +64,18 @@ namespace System.Threading /// private WaitHandleArray _waitedListNodes; + /// + /// Indicates whether the next wait should be interrupted. + /// + /// Synchronization: + /// - In most cases, reads and writes are synchronized with + /// - Sleep(nonzero) intentionally does not acquire , but it must acquire + /// to do the wait. To support this case, a pending interrupt is recorded while + /// and are locked, and the read and reset for Sleep(nonzero) are + /// done while is locked. + /// - Sleep(0) intentionally does not acquire any lock, so it uses an interlocked compare-exchange for the read and + /// reset, see + /// private int _isPendingInterrupt; //////////////////////////////////////////////////////////////// @@ -81,6 +93,7 @@ namespace System.Threading _thread = thread; _waitMonitor = new LowLevelMonitor(); + _waitSignalState = WaitSignalState.NotWaiting; _waitedObjectIndexThatSatisfiedWait = -1; _waitedObjects = new WaitHandleArray(elementInitializer: null); _waitedListNodes = new WaitHandleArray(i => new WaitedListNode(this, i)); @@ -88,6 +101,15 @@ namespace System.Threading public RuntimeThread Thread => _thread; + private bool IsWaiting + { + get + { + _waitMonitor.VerifyIsLocked(); + return _waitSignalState < WaitSignalState.NotWaiting; + } + } + /// /// Callers must ensure to clear the array after use. Once is called (followed /// by a call to , the array will be cleared automatically. @@ -114,7 +136,7 @@ namespace System.Threading /// /// The caller is expected to populate and pass in the number of objects filled /// - public void RegisterWait(int waitedCount, bool isWaitForAll) + public void RegisterWait(int waitedCount, bool prioritize, bool isWaitForAll) { s_lock.VerifyIsLocked(); Debug.Assert(_thread == RuntimeThread.CurrentThread); @@ -158,9 +180,19 @@ namespace System.Threading _isWaitForAll = isWaitForAll; _waitedCount = waitedCount; - for (int i = 0; i < waitedCount; ++i) + if (prioritize) { - waitedListNodes[i].RegisterWait(waitedObjects[i]); + for (int i = 0; i < waitedCount; ++i) + { + waitedListNodes[i].RegisterPrioritizedWait(waitedObjects[i]); + } + } + else + { + for (int i = 0; i < waitedCount; ++i) + { + waitedListNodes[i].RegisterWait(waitedObjects[i]); + } } } @@ -177,7 +209,7 @@ namespace System.Threading _waitedCount = 0; } - private int ProcessSignaledWaitState(WaitHandle[] waitHandlesForAbandon, out Exception exception) + private int ProcessSignaledWaitState(WaitHandle[] waitHandlesForAbandon) { s_lock.VerifyIsNotLocked(); _waitMonitor.VerifyIsLocked(); @@ -186,52 +218,53 @@ namespace System.Threading switch (_waitSignalState) { case WaitSignalState.Waiting: - exception = null; + case WaitSignalState.Waiting_Interruptible: return WaitHandle.WaitTimeout; - case WaitSignalState.Waiting_SignaledToSatisfyWait: + case WaitSignalState.NotWaiting_SignaledToSatisfyWait: { Debug.Assert(_waitedObjectIndexThatSatisfiedWait >= 0); int waitedObjectIndexThatSatisfiedWait = _waitedObjectIndexThatSatisfiedWait; _waitedObjectIndexThatSatisfiedWait = -1; - exception = null; return waitedObjectIndexThatSatisfiedWait; } - case WaitSignalState.Waiting_SignaledToSatisfyWaitWithAbandonedMutex: + case WaitSignalState.NotWaiting_SignaledToSatisfyWaitWithAbandonedMutex: Debug.Assert(_waitedObjectIndexThatSatisfiedWait >= 0); if (waitHandlesForAbandon == null) { _waitedObjectIndexThatSatisfiedWait = -1; - exception = new AbandonedMutexException(); + throw new AbandonedMutexException(); } else { int waitedObjectIndexThatSatisfiedWait = _waitedObjectIndexThatSatisfiedWait; _waitedObjectIndexThatSatisfiedWait = -1; - exception = + throw new AbandonedMutexException( waitedObjectIndexThatSatisfiedWait, waitHandlesForAbandon[waitedObjectIndexThatSatisfiedWait]); } - return 0; - case WaitSignalState.Waiting_SignaledToAbortWaitDueToMaximumMutexReacquireCount: + case WaitSignalState.NotWaiting_SignaledToAbortWaitDueToMaximumMutexReacquireCount: Debug.Assert(_waitedObjectIndexThatSatisfiedWait < 0); - exception = new OverflowException(SR.Overflow_MutexReacquireCount); - return 0; + throw new OverflowException(SR.Overflow_MutexReacquireCount); default: - Debug.Assert(_waitSignalState == WaitSignalState.Waiting_SignaledToInterruptWait); + Debug.Assert(_waitSignalState == WaitSignalState.NotWaiting_SignaledToInterruptWait); Debug.Assert(_waitedObjectIndexThatSatisfiedWait < 0); - exception = new ThreadInterruptedException(); - return 0; + throw new ThreadInterruptedException(); } } - public int Wait(int timeoutMilliseconds, WaitHandle[] waitHandlesForAbandon, bool isSleep) + public int Wait(int timeoutMilliseconds, bool interruptible, WaitHandle[] waitHandlesForAbandon, bool isSleep) { - if (!isSleep) + if (isSleep) + { + s_lock.VerifyIsNotLocked(); + Debug.Assert(waitHandlesForAbandon == null); + } + else { s_lock.VerifyIsLocked(); } @@ -254,54 +287,55 @@ namespace System.Threading Debug.Assert(_waitedObjectIndexThatSatisfiedWait < 0); Debug.Assert(_waitSignalState == WaitSignalState.NotWaiting); - /// A signaled state may be set only when the thread is in the - /// state - _waitSignalState = WaitSignalState.Waiting; + // A signaled state may be set only when the thread is in one of the following states + _waitSignalState = interruptible ? WaitSignalState.Waiting_Interruptible : WaitSignalState.Waiting; - int waitResult; - Exception exception; try { + if (isSleep && interruptible && CheckAndResetPendingInterrupt) + { + throw new ThreadInterruptedException(); + } + if (timeoutMilliseconds < 0) { do { _waitMonitor.Wait(); - } while (_waitSignalState == WaitSignalState.Waiting); + } while (IsWaiting); - waitResult = ProcessSignaledWaitState(waitHandlesForAbandon, out exception); - Debug.Assert(exception != null || waitResult != WaitHandle.WaitTimeout); + int waitResult = ProcessSignaledWaitState(waitHandlesForAbandon); + Debug.Assert(waitResult != WaitHandle.WaitTimeout); + return waitResult; } - else + + int elapsedMilliseconds = 0; + int startTimeMilliseconds = Environment.TickCount; + while (true) { - int elapsedMilliseconds = 0; - int startTimeMilliseconds = Environment.TickCount; - while (true) + bool monitorWaitResult = _waitMonitor.Wait(timeoutMilliseconds - elapsedMilliseconds); + + // It's possible for the wait to have timed out, but before the monitor could reacquire the lock, a + // signaler could have acquired it and signaled to satisfy the wait or interrupt the thread. Accept the + // signal and ignore the wait timeout. + int waitResult = ProcessSignaledWaitState(waitHandlesForAbandon); + if (waitResult != WaitHandle.WaitTimeout) { - bool monitorWaitResult = _waitMonitor.Wait(timeoutMilliseconds - elapsedMilliseconds); - - // It's possible for the wait to have timed out, but before the monitor could reacquire the lock, a - // signaler could have acquired it and signaled to satisfy the wait or interrupt the thread. Accept the - // signal and ignore the wait timeout. - waitResult = ProcessSignaledWaitState(waitHandlesForAbandon, out exception); - if (exception != null || waitResult != WaitHandle.WaitTimeout) - { - break; - } - - if (monitorWaitResult) - { - elapsedMilliseconds = Environment.TickCount - startTimeMilliseconds; - if (elapsedMilliseconds < timeoutMilliseconds) - { - continue; - } - } - - // Timeout - Debug.Assert(_waitedObjectIndexThatSatisfiedWait < 0); - break; + return waitResult; } + + if (monitorWaitResult) + { + elapsedMilliseconds = Environment.TickCount - startTimeMilliseconds; + if (elapsedMilliseconds < timeoutMilliseconds) + { + continue; + } + } + + // Timeout + Debug.Assert(_waitedObjectIndexThatSatisfiedWait < 0); + break; } } finally @@ -312,15 +346,6 @@ namespace System.Threading _thread.ClearWaitSleepJoinState(); } - if (exception != null) - { - throw exception; - } - if (waitResult != WaitHandle.WaitTimeout) - { - return waitResult; - } - /// Timeout. It's ok to read without acquiring here, because it /// is initially set by this thread, and another thread cannot unregister this thread's wait without first /// signaling this thread, in which case this thread wouldn't be timing out. @@ -328,20 +353,16 @@ namespace System.Threading if (!isSleep) { s_lock.Acquire(); - try - { - UnregisterWait(); - } - finally - { - s_lock.Release(); - } + UnregisterWait(); + s_lock.Release(); } - return waitResult; + return WaitHandle.WaitTimeout; } public static void UninterruptibleSleep0() { + s_lock.VerifyIsNotLocked(); + // On Unix, a thread waits on a condition variable. The timeout time will have already elapsed at the time // of the call. The documentation does not state whether the thread yields or does nothing before returning // an error, and in some cases, suggests that doing nothing is acceptable. The behavior could also be @@ -349,20 +370,27 @@ namespace System.Threading RuntimeThread.Yield(); } - public void Sleep(int timeoutMilliseconds) + public static void Sleep(int timeoutMilliseconds, bool interruptible) { s_lock.VerifyIsNotLocked(); - Debug.Assert(_thread == RuntimeThread.CurrentThread); - Debug.Assert(timeoutMilliseconds >= -1); if (timeoutMilliseconds == 0) { + if (interruptible && RuntimeThread.CurrentThread.WaitInfo.CheckAndResetPendingInterrupt_NotLocked) + { + throw new ThreadInterruptedException(); + } + UninterruptibleSleep0(); return; } - int waitResult = Wait(timeoutMilliseconds, waitHandlesForAbandon: null, isSleep: true); + int waitResult = + RuntimeThread + .CurrentThread + .WaitInfo + .Wait(timeoutMilliseconds, interruptible, waitHandlesForAbandon: null, isSleep: true); Debug.Assert(waitResult == WaitHandle.WaitTimeout); } @@ -404,7 +432,7 @@ namespace System.Threading // the thread can accept a signal. _waitMonitor.Acquire(); - if (_waitSignalState != WaitSignalState.Waiting) + if (!IsWaiting) { _waitMonitor.Release(); return false; @@ -421,15 +449,15 @@ namespace System.Threading Debug.Assert(_waitedObjectIndexThatSatisfiedWait < 0); if (wouldAnyMutexReacquireCountOverflow) { - _waitSignalState = WaitSignalState.Waiting_SignaledToAbortWaitDueToMaximumMutexReacquireCount; + _waitSignalState = WaitSignalState.NotWaiting_SignaledToAbortWaitDueToMaximumMutexReacquireCount; } else { _waitedObjectIndexThatSatisfiedWait = signaledWaitedObjectIndex; _waitSignalState = isAbandonedMutex - ? WaitSignalState.Waiting_SignaledToSatisfyWaitWithAbandonedMutex - : WaitSignalState.Waiting_SignaledToSatisfyWait; + ? WaitSignalState.NotWaiting_SignaledToSatisfyWaitWithAbandonedMutex + : WaitSignalState.NotWaiting_SignaledToSatisfyWait; } _waitMonitor.Signal_Release(); @@ -442,10 +470,10 @@ namespace System.Threading _waitMonitor.Acquire(); - if (_waitSignalState != WaitSignalState.Waiting) + if (_waitSignalState != WaitSignalState.Waiting_Interruptible) { - _waitMonitor.Release(); RecordPendingInterrupt(); + _waitMonitor.Release(); return; } @@ -455,13 +483,46 @@ namespace System.Threading } Debug.Assert(_waitedObjectIndexThatSatisfiedWait < 0); - _waitSignalState = WaitSignalState.Waiting_SignaledToInterruptWait; + _waitSignalState = WaitSignalState.NotWaiting_SignaledToInterruptWait; _waitMonitor.Signal_Release(); } - private void RecordPendingInterrupt() => Interlocked.Exchange(ref _isPendingInterrupt, 1); - public bool CheckAndResetPendingInterrupt => Interlocked.CompareExchange(ref _isPendingInterrupt, 0, 1) != 0; + private void RecordPendingInterrupt() + { + s_lock.VerifyIsLocked(); + _waitMonitor.VerifyIsLocked(); + + _isPendingInterrupt = 1; + } + + public bool CheckAndResetPendingInterrupt + { + get + { +#if DEBUG + Debug.Assert(s_lock.IsLocked || _waitMonitor.IsLocked); +#endif + + if (_isPendingInterrupt == 0) + { + return false; + } + _isPendingInterrupt = 0; + return true; + } + } + + private bool CheckAndResetPendingInterrupt_NotLocked + { + get + { + s_lock.VerifyIsNotLocked(); + _waitMonitor.VerifyIsNotLocked(); + + return Interlocked.CompareExchange(ref _isPendingInterrupt, 0, 1) != 0; + } + } public WaitableObject LockedMutexesHead { @@ -589,6 +650,29 @@ namespace System.Threading waitableObject.WaitersTail = this; } + public void RegisterPrioritizedWait(WaitableObject waitableObject) + { + s_lock.VerifyIsLocked(); + Debug.Assert(_waitInfo.Thread == RuntimeThread.CurrentThread); + + Debug.Assert(waitableObject != null); + + Debug.Assert(_previous == null); + Debug.Assert(_next == null); + + WaitedListNode head = waitableObject.WaitersHead; + if (head != null) + { + _next = head; + head._previous = this; + } + else + { + waitableObject.WaitersTail = this; + } + waitableObject.WaitersHead = this; + } + public void UnregisterWait(WaitableObject waitableObject) { s_lock.VerifyIsLocked(); @@ -623,12 +707,13 @@ namespace System.Threading private enum WaitSignalState : byte { - NotWaiting, Waiting, - Waiting_SignaledToSatisfyWait, - Waiting_SignaledToSatisfyWaitWithAbandonedMutex, - Waiting_SignaledToAbortWaitDueToMaximumMutexReacquireCount, - Waiting_SignaledToInterruptWait + Waiting_Interruptible, + NotWaiting, + NotWaiting_SignaledToSatisfyWait, + NotWaiting_SignaledToSatisfyWaitWithAbandonedMutex, + NotWaiting_SignaledToAbortWaitDueToMaximumMutexReacquireCount, + NotWaiting_SignaledToInterruptWait } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs index c4f1f90252..f082a5d1d4 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs @@ -171,7 +171,7 @@ namespace System.Threading /// by the thread. See . So, acquire the lock only after all /// possibilities for exceptions have been exhausted. ThreadWaitInfo waitInfo = RuntimeThread.CurrentThread.WaitInfo; - bool acquiredLock = waitableObject.Wait(waitInfo, 0); + bool acquiredLock = waitableObject.Wait(waitInfo, timeoutMilliseconds: 0, interruptible: false, prioritize: false); Debug.Assert(acquiredLock); return safeWaitHandle; } @@ -183,7 +183,13 @@ namespace System.Threading public static void SetEvent(IntPtr handle) { - WaitableObject waitableObject = HandleManager.FromHandle(handle); + SetEvent(HandleManager.FromHandle(handle)); + } + + public static void SetEvent(WaitableObject waitableObject) + { + Debug.Assert(waitableObject != null); + s_lock.Acquire(); try { @@ -197,7 +203,13 @@ namespace System.Threading public static void ResetEvent(IntPtr handle) { - WaitableObject waitableObject = HandleManager.FromHandle(handle); + ResetEvent(HandleManager.FromHandle(handle)); + } + + public static void ResetEvent(WaitableObject waitableObject) + { + Debug.Assert(waitableObject != null); + s_lock.Acquire(); try { @@ -212,8 +224,14 @@ namespace System.Threading public static int ReleaseSemaphore(IntPtr handle, int count) { Debug.Assert(count > 0); + return ReleaseSemaphore(HandleManager.FromHandle(handle), count); + } + + public static int ReleaseSemaphore(WaitableObject waitableObject, int count) + { + Debug.Assert(waitableObject != null); + Debug.Assert(count > 0); - WaitableObject waitableObject = HandleManager.FromHandle(handle); s_lock.Acquire(); try { @@ -227,7 +245,13 @@ namespace System.Threading public static void ReleaseMutex(IntPtr handle) { - WaitableObject waitableObject = HandleManager.FromHandle(handle); + ReleaseMutex(HandleManager.FromHandle(handle)); + } + + public static void ReleaseMutex(WaitableObject waitableObject) + { + Debug.Assert(waitableObject != null); + s_lock.Acquire(); try { @@ -239,45 +263,46 @@ namespace System.Threading } } - public static bool Wait(IntPtr handle, int timeoutMilliseconds) + public static bool Wait(IntPtr handle, int timeoutMilliseconds, bool interruptible) { Debug.Assert(timeoutMilliseconds >= -1); + return Wait(HandleManager.FromHandle(handle), timeoutMilliseconds, interruptible); + } - ThreadWaitInfo waitInfo = RuntimeThread.CurrentThread.WaitInfo; - if (waitInfo.CheckAndResetPendingInterrupt) - { - throw new ThreadInterruptedException(); - } + public static bool Wait( + WaitableObject waitableObject, + int timeoutMilliseconds, + bool interruptible = true, + bool prioritize = false) + { + Debug.Assert(waitableObject != null); + Debug.Assert(timeoutMilliseconds >= -1); - return HandleManager.FromHandle(handle).Wait(waitInfo, timeoutMilliseconds); + return waitableObject.Wait(RuntimeThread.CurrentThread.WaitInfo, timeoutMilliseconds, interruptible, prioritize); } public static int Wait( RuntimeThread currentThread, SafeWaitHandle[] safeWaitHandles, WaitHandle[] waitHandles, + int numWaitHandles, bool waitForAll, int timeoutMilliseconds) { Debug.Assert(currentThread == RuntimeThread.CurrentThread); Debug.Assert(safeWaitHandles != null); - Debug.Assert(safeWaitHandles.Length >= waitHandles.Length); - Debug.Assert(waitHandles.Length > 0); - Debug.Assert(waitHandles.Length <= WaitHandle.MaxWaitHandles); + Debug.Assert(numWaitHandles > 0); + Debug.Assert(numWaitHandles <= safeWaitHandles.Length); + Debug.Assert(numWaitHandles <= waitHandles.Length); + Debug.Assert(numWaitHandles <= WaitHandle.MaxWaitHandles); Debug.Assert(timeoutMilliseconds >= -1); ThreadWaitInfo waitInfo = currentThread.WaitInfo; - if (waitInfo.CheckAndResetPendingInterrupt) - { - throw new ThreadInterruptedException(); - } - - int count = waitHandles.Length; - WaitableObject[] waitableObjects = waitInfo.GetWaitedObjectArray(count); + WaitableObject[] waitableObjects = waitInfo.GetWaitedObjectArray(numWaitHandles); bool success = false; try { - for (int i = 0; i < count; ++i) + for (int i = 0; i < numWaitHandles; ++i) { Debug.Assert(safeWaitHandles[i] != null); WaitableObject waitableObject = HandleManager.FromHandle(safeWaitHandles[i].DangerousGetHandle()); @@ -304,20 +329,55 @@ namespace System.Threading { if (!success) { - for (int i = 0; i < count; ++i) + for (int i = 0; i < numWaitHandles; ++i) { waitableObjects[i] = null; } } } - if (count == 1) + if (numWaitHandles == 1) { WaitableObject waitableObject = waitableObjects[0]; waitableObjects[0] = null; - return waitableObject.Wait(waitInfo, timeoutMilliseconds) ? 0 : WaitHandle.WaitTimeout; + return + waitableObject.Wait(waitInfo, timeoutMilliseconds, interruptible: true, prioritize : false) + ? 0 + : WaitHandle.WaitTimeout; } + return + WaitableObject.Wait( + waitableObjects, + numWaitHandles, + waitForAll, + waitInfo, + timeoutMilliseconds, + interruptible: true, + prioritize: false, + waitHandlesForAbandon: waitHandles); + } + + public static int Wait( + RuntimeThread currentThread, + WaitableObject waitableObject0, + WaitableObject waitableObject1, + bool waitForAll, + int timeoutMilliseconds, + bool interruptible = true, + bool prioritize = false) + { + Debug.Assert(currentThread == RuntimeThread.CurrentThread); + Debug.Assert(waitableObject0 != null); + Debug.Assert(waitableObject1 != null); + Debug.Assert(waitableObject1 != waitableObject0); + Debug.Assert(timeoutMilliseconds >= -1); + + ThreadWaitInfo waitInfo = currentThread.WaitInfo; + int count = 2; + WaitableObject[] waitableObjects = waitInfo.GetWaitedObjectArray(count); + waitableObjects[0] = waitableObject0; + waitableObjects[1] = waitableObject1; return WaitableObject.Wait( waitableObjects, @@ -325,31 +385,50 @@ namespace System.Threading waitForAll, waitInfo, timeoutMilliseconds, - waitHandles); + interruptible, + prioritize, + waitHandlesForAbandon: null); } - public static bool SignalAndWait(IntPtr handleToSignal, IntPtr handleToWaitOn, int timeoutMilliseconds) + public static bool SignalAndWait( + IntPtr handleToSignal, + IntPtr handleToWaitOn, + int timeoutMilliseconds) { Debug.Assert(timeoutMilliseconds >= -1); + return + SignalAndWait( + HandleManager.FromHandle(handleToSignal), + HandleManager.FromHandle(handleToWaitOn), + timeoutMilliseconds); + } + + public static bool SignalAndWait( + WaitableObject waitableObjectToSignal, + WaitableObject waitableObjectToWaitOn, + int timeoutMilliseconds, + bool interruptible = true, + bool prioritize = false) + { + Debug.Assert(waitableObjectToSignal != null); + Debug.Assert(waitableObjectToWaitOn != null); + Debug.Assert(timeoutMilliseconds >= -1); + ThreadWaitInfo waitInfo = RuntimeThread.CurrentThread.WaitInfo; - - // A pending interrupt does not signal the specified handle - if (waitInfo.CheckAndResetPendingInterrupt) - { - throw new ThreadInterruptedException(); - } - - WaitableObject waitableObjectToSignal = HandleManager.FromHandle(handleToSignal); - WaitableObject waitableObjectToWaitOn = HandleManager.FromHandle(handleToWaitOn); - bool waitCalled = false; s_lock.Acquire(); try { + // A pending interrupt does not signal the specified handle + if (interruptible && waitInfo.CheckAndResetPendingInterrupt) + { + throw new ThreadInterruptedException(); + } + waitableObjectToSignal.Signal(1); waitCalled = true; - return waitableObjectToWaitOn.Wait_Locked(waitInfo, timeoutMilliseconds); + return waitableObjectToWaitOn.Wait_Locked(waitInfo, timeoutMilliseconds, interruptible, prioritize); } finally { @@ -370,17 +449,9 @@ namespace System.Threading ThreadWaitInfo.UninterruptibleSleep0(); } - public static void Sleep(int timeoutMilliseconds) + public static void Sleep(int timeoutMilliseconds, bool interruptible = true) { - Debug.Assert(timeoutMilliseconds >= -1); - - ThreadWaitInfo waitInfo = RuntimeThread.CurrentThread.WaitInfo; - if (waitInfo.CheckAndResetPendingInterrupt) - { - throw new ThreadInterruptedException(); - } - - waitInfo.Sleep(timeoutMilliseconds); + ThreadWaitInfo.Sleep(timeoutMilliseconds, interruptible); } public static void Interrupt(RuntimeThread thread) @@ -398,7 +469,6 @@ namespace System.Threading } } - // TODO: Call this public static void OnThreadExiting(RuntimeThread thread) { thread.WaitInfo.OnThreadExiting(); diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs index 1195b2719a..a52c34b365 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs @@ -198,8 +198,6 @@ namespace System.Threading s_lock.VerifyIsLocked(); bool isSignaled = _signalCount != 0; - Debug.Assert(!isSignaled || _waitersHead == null); - Debug.Assert(!isSignaled || _waitersTail == null); Debug.Assert(isSignaled || !IsMutex || _ownershipInfo.Thread != null); return isSignaled; } @@ -230,19 +228,36 @@ namespace System.Threading } } - public bool Wait(ThreadWaitInfo waitInfo, int timeoutMilliseconds) + public bool Wait(ThreadWaitInfo waitInfo, int timeoutMilliseconds, bool interruptible, bool prioritize) { + Debug.Assert(waitInfo != null); + Debug.Assert(waitInfo.Thread == RuntimeThread.CurrentThread); + + Debug.Assert(timeoutMilliseconds >= -1); + s_lock.Acquire(); - return Wait_Locked(waitInfo, timeoutMilliseconds); + + if (interruptible && waitInfo.CheckAndResetPendingInterrupt) + { + s_lock.Release(); + throw new ThreadInterruptedException(); + } + + return Wait_Locked(waitInfo, timeoutMilliseconds, interruptible, prioritize); } - public bool Wait_Locked(ThreadWaitInfo waitInfo, int timeoutMilliseconds) + /// + /// This function does not check for a pending thread interrupt. Callers are expected to do that soon after + /// acquiring . + /// + public bool Wait_Locked(ThreadWaitInfo waitInfo, int timeoutMilliseconds, bool interruptible, bool prioritize) { s_lock.VerifyIsLocked(); Debug.Assert(waitInfo != null); Debug.Assert(waitInfo.Thread == RuntimeThread.CurrentThread); Debug.Assert(timeoutMilliseconds >= -1); + Debug.Assert(!interruptible || !waitInfo.CheckAndResetPendingInterrupt); bool needToWait = false; try @@ -275,7 +290,7 @@ namespace System.Threading WaitableObject[] waitableObjects = waitInfo.GetWaitedObjectArray(1); waitableObjects[0] = this; - waitInfo.RegisterWait(1, isWaitForAll: false); + waitInfo.RegisterWait(1, prioritize, isWaitForAll: false); needToWait = true; } finally @@ -287,7 +302,12 @@ namespace System.Threading } } - return waitInfo.Wait(timeoutMilliseconds, waitHandlesForAbandon: null, isSleep: false) != WaitHandle.WaitTimeout; + return + waitInfo.Wait( + timeoutMilliseconds, + interruptible, + waitHandlesForAbandon: null, + isSleep: false) != WaitHandle.WaitTimeout; } public static int Wait( @@ -296,6 +316,8 @@ namespace System.Threading bool waitForAll, ThreadWaitInfo waitInfo, int timeoutMilliseconds, + bool interruptible, + bool prioritize, WaitHandle[] waitHandlesForAbandon) { s_lock.VerifyIsNotLocked(); @@ -311,6 +333,11 @@ namespace System.Threading s_lock.Acquire(); try { + if (interruptible && waitInfo.CheckAndResetPendingInterrupt) + { + throw new ThreadInterruptedException(); + } + if (!waitForAll) { // Check if any is already signaled @@ -419,7 +446,7 @@ namespace System.Threading } waitableObjects = null; // no need to clear this anymore, RegisterWait / Wait will take over from here - waitInfo.RegisterWait(count, waitForAll); + waitInfo.RegisterWait(count, prioritize, waitForAll); needToWait = true; } finally @@ -439,7 +466,7 @@ namespace System.Threading } } - return waitInfo.Wait(timeoutMilliseconds, waitHandlesForAbandon, isSleep: false); + return waitInfo.Wait(timeoutMilliseconds, interruptible, waitHandlesForAbandon, isSleep: false); } public static bool WouldWaitForAllBeSatisfiedOrAborted( @@ -450,6 +477,7 @@ namespace System.Threading ref bool wouldAnyMutexReacquireCountOverflow, ref bool isAnyAbandonedMutex) { + s_lock.VerifyIsLocked(); Debug.Assert(waitingThread != null); Debug.Assert(waitingThread != RuntimeThread.CurrentThread); Debug.Assert(waitedObjects != null); @@ -504,6 +532,7 @@ namespace System.Threading int waitedCount, int signaledWaitedObjectIndex) { + s_lock.VerifyIsLocked(); Debug.Assert(waitInfo != null); Debug.Assert(waitInfo.Thread != RuntimeThread.CurrentThread); Debug.Assert(waitedObjects != null); @@ -542,10 +571,12 @@ namespace System.Threading switch (_type) { case WaitableObjectType.ManualResetEvent: + Debug.Assert(count == 1); SignalManualResetEvent(); break; case WaitableObjectType.AutoResetEvent: + Debug.Assert(count == 1); SignalAutoResetEvent(); break; @@ -554,6 +585,7 @@ namespace System.Threading break; default: + Debug.Assert(count == 1); SignalMutex(); break; } @@ -663,9 +695,6 @@ namespace System.Threading if (oldSignalCount != 0) { - Debug.Assert(_waitersHead == null); - Debug.Assert(_waitersTail == null); - _signalCount = oldSignalCount + count; return oldSignalCount; } diff --git a/external/corert/src/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs b/external/corert/src/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs index 1b5f68da93..a859e3d46c 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Internal.Runtime.Augments; using Microsoft.Win32.SafeHandles; using System.Diagnostics; using System.Runtime.InteropServices; @@ -172,7 +171,7 @@ namespace System.Threading [NativeCallable(CallingConvention = CallingConvention.StdCall)] private static unsafe void OnNativeIOCompleted(IntPtr instance, IntPtr context, IntPtr overlappedPtr, uint ioResult, UIntPtr numberOfBytesTransferred, IntPtr ioPtr) { - RuntimeThread.InitializeThreadPoolThread(); + var wrapper = ThreadPoolCallbackWrapper.Enter(); Win32ThreadPoolNativeOverlapped* overlapped = (Win32ThreadPoolNativeOverlapped*)overlappedPtr; ThreadPoolBoundHandle boundHandle = overlapped->Data._boundHandle; @@ -182,6 +181,7 @@ namespace System.Threading boundHandle.Release(); Win32ThreadPoolNativeOverlapped.CompleteWithCallback(ioResult, (uint)numberOfBytesTransferred, overlapped); + wrapper.Exit(); } private bool AddRef() diff --git a/external/corert/src/System.Private.CoreLib/src/System/ThrowHelper.cs b/external/corert/src/System.Private.CoreLib/src/System/ThrowHelper.cs index ae1938cca4..ff7bb2b4b2 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -35,12 +35,14 @@ // multiple times for different instantiation. // +using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; namespace System { - [Pure] + [StackTraceHidden] internal static class ThrowHelper { internal static void ThrowArrayTypeMismatchException() @@ -63,33 +65,298 @@ namespace System throw new ArgumentOutOfRangeException(); } + internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument) + { + throw new ArgumentOutOfRangeException(GetArgumentName(argument)); + } + + private static ArgumentOutOfRangeException GetArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource) + { + return new ArgumentOutOfRangeException(GetArgumentName(argument), GetResourceString(resource)); + } + internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource) + { + throw GetArgumentOutOfRangeException(argument, resource); + } + internal static void ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index() + { + throw GetArgumentOutOfRangeException(ExceptionArgument.startIndex, + ExceptionResource.ArgumentOutOfRange_Index); + } + internal static void ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count() + { + throw GetArgumentOutOfRangeException(ExceptionArgument.count, + ExceptionResource.ArgumentOutOfRange_Count); + } + internal static void ThrowArgumentException_DestinationTooShort() { throw new ArgumentException(SR.Argument_DestinationTooShort); } + internal static void ThrowArgumentOutOfRange_IndexException() + { + throw GetArgumentOutOfRangeException(ExceptionArgument.index, + ExceptionResource.ArgumentOutOfRange_Index); + } + internal static void ThrowIndexArgumentOutOfRange_NeedNonNegNumException() + { + throw GetArgumentOutOfRangeException(ExceptionArgument.index, + ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + } + + private static ArgumentException GetWrongKeyTypeArgumentException(object key, Type targetType) + { + return new ArgumentException(SR.Format(SR.Arg_WrongType, key, targetType), nameof(key)); + } + internal static void ThrowWrongKeyTypeArgumentException(object key, Type targetType) + { + throw GetWrongKeyTypeArgumentException(key, targetType); + } + + private static ArgumentException GetWrongValueTypeArgumentException(object value, Type targetType) + { + return new ArgumentException(SR.Format(SR.Arg_WrongType, value, targetType), nameof(value)); + } + internal static void ThrowWrongValueTypeArgumentException(object value, Type targetType) + { + throw GetWrongValueTypeArgumentException(value, targetType); + } + + private static ArgumentException GetAddingDuplicateWithKeyArgumentException(object key) + { + return new ArgumentException(SR.Format(SR.Argument_AddingDuplicate, key)); + } + internal static void ThrowAddingDuplicateWithKeyArgumentException(object key) + { + throw GetAddingDuplicateWithKeyArgumentException(key); + } + + private static KeyNotFoundException GetKeyNotFoundException(object key) + { + throw new KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key.ToString())); + } + internal static void ThrowKeyNotFoundException(object key) + { + throw GetKeyNotFoundException(key); + } + + internal static void ThrowArgumentException(ExceptionResource resource) + { + throw new ArgumentException(GetResourceString(resource)); + } + + private static ArgumentException GetArgumentException(ExceptionResource resource, ExceptionArgument argument) + { + return new ArgumentException(GetResourceString(resource), GetArgumentName(argument)); + } + internal static void ThrowArgumentException(ExceptionResource resource, ExceptionArgument argument) + { + throw GetArgumentException(resource, argument); + } + + internal static void ThrowArgumentException_Argument_InvalidArrayType() + { + throw new ArgumentException(SR.Argument_InvalidArrayType); + } internal static void ThrowArgumentNullException(ExceptionArgument argument) { - throw GetArgumentNullException(argument); + throw new ArgumentNullException(GetArgumentName(argument)); } - private static ArgumentNullException GetArgumentNullException(ExceptionArgument argument) + + internal static void ThrowObjectDisposedException(string objectName, ExceptionResource resource) { - return new ArgumentNullException(GetArgumentName(argument)); + throw new ObjectDisposedException(objectName, GetResourceString(resource)); + } + + internal static void ThrowInvalidOperationException(ExceptionResource resource) + { + throw new InvalidOperationException(GetResourceString(resource)); + } + + internal static void ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion() + { + throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + } + + internal static void ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen() + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + + internal static void ThrowInvalidOperationException_InvalidOperation_EnumNotStarted() + { + throw new InvalidOperationException(SR.InvalidOperation_EnumNotStarted); + } + + internal static void ThrowInvalidOperationException_InvalidOperation_EnumEnded() + { + throw new InvalidOperationException(SR.InvalidOperation_EnumEnded); + } + + internal static void ThrowInvalidOperationException_InvalidOperation_NoValue() + { + throw new InvalidOperationException(SR.InvalidOperation_NoValue); + } + + internal static void ThrowSerializationException(ExceptionResource resource) + { + throw new SerializationException(GetResourceString(resource)); + } + + internal static void ThrowNotSupportedException() + { + throw new NotSupportedException(); + } + + internal static void ThrowNotSupportedException(ExceptionResource resource) + { + throw new NotSupportedException(GetResourceString(resource)); + } + + private static Exception GetArraySegmentCtorValidationFailedException(Array array, int offset, int count) + { + if (array == null) + return new ArgumentNullException(nameof(array)); + if (offset < 0) + return new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + if (count < 0) + return new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + + Debug.Assert(array.Length - offset < count); + return new ArgumentException(SR.Argument_InvalidOffLen); + } + internal static void ThrowArraySegmentCtorValidationFailedExceptions(Array array, int offset, int count) + { + throw GetArraySegmentCtorValidationFailedException(array, offset, count); + } + + // Allow nulls for reference types and Nullable, but not for value types. + // Aggressively inline so the jit evaluates the if in place and either drops the call altogether + // Or just leaves null test and call to the Non-returning ThrowHelper.ThrowArgumentNullException + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void IfNullAndNullsAreIllegalThenThrow(object value, ExceptionArgument argName) + { + // Note that default(T) is not equal to null for value types except when T is Nullable. + if (!(default(T) == null) && value == null) + ThrowHelper.ThrowArgumentNullException(argName); } private static string GetArgumentName(ExceptionArgument argument) { switch (argument) { + case ExceptionArgument.obj: + return "obj"; + case ExceptionArgument.dictionary: + return "dictionary"; case ExceptionArgument.array: return "array"; + case ExceptionArgument.info: + return "info"; + case ExceptionArgument.key: + return "key"; case ExceptionArgument.text: return "text"; case ExceptionArgument.values: return "values"; + case ExceptionArgument.value: + return "value"; + case ExceptionArgument.startIndex: + return "startIndex"; + case ExceptionArgument.task: + return "task"; + case ExceptionArgument.s: + return "s"; + case ExceptionArgument.input: + return "input"; + case ExceptionArgument.ownedMemory: + return "ownedMemory"; + case ExceptionArgument.list: + return "list"; + case ExceptionArgument.index: + return "index"; + case ExceptionArgument.capacity: + return "capacity"; + case ExceptionArgument.collection: + return "collection"; + case ExceptionArgument.item: + return "item"; + case ExceptionArgument.converter: + return "converter"; + case ExceptionArgument.match: + return "match"; + case ExceptionArgument.count: + return "count"; + case ExceptionArgument.action: + return "action"; + case ExceptionArgument.comparison: + return "comparison"; + case ExceptionArgument.exceptions: + return "exceptions"; + case ExceptionArgument.exception: + return "exception"; + case ExceptionArgument.pointer: + return "pointer"; + case ExceptionArgument.start: + return "start"; + case ExceptionArgument.format: + return "format"; + default: + Debug.Fail("The enum value is not defined, please check the ExceptionArgument Enum."); + return ""; + } + } + + private static string GetResourceString(ExceptionResource resource) + { + switch (resource) + { + case ExceptionResource.ArgumentOutOfRange_Index: + return SR.ArgumentOutOfRange_Index; + case ExceptionResource.ArgumentOutOfRange_Count: + return SR.ArgumentOutOfRange_Count; + case ExceptionResource.Arg_ArrayPlusOffTooSmall: + return SR.Arg_ArrayPlusOffTooSmall; + case ExceptionResource.NotSupported_ReadOnlyCollection: + return SR.NotSupported_ReadOnlyCollection; + case ExceptionResource.Arg_RankMultiDimNotSupported: + return SR.Arg_RankMultiDimNotSupported; + case ExceptionResource.Arg_NonZeroLowerBound: + return SR.Arg_NonZeroLowerBound; + case ExceptionResource.ArgumentOutOfRange_ListInsert: + return SR.ArgumentOutOfRange_ListInsert; + case ExceptionResource.ArgumentOutOfRange_NeedNonNegNum: + return SR.ArgumentOutOfRange_NeedNonNegNum; + case ExceptionResource.ArgumentOutOfRange_SmallCapacity: + return SR.ArgumentOutOfRange_SmallCapacity; + case ExceptionResource.Argument_InvalidOffLen: + return SR.Argument_InvalidOffLen; + case ExceptionResource.ArgumentOutOfRange_BiggerThanCollection: + return SR.ArgumentOutOfRange_BiggerThanCollection; + case ExceptionResource.Serialization_MissingKeys: + return SR.Serialization_MissingKeys; + case ExceptionResource.Serialization_NullKey: + return SR.Serialization_NullKey; + case ExceptionResource.NotSupported_KeyCollectionSet: + return SR.NotSupported_KeyCollectionSet; + case ExceptionResource.NotSupported_ValueCollectionSet: + return SR.NotSupported_ValueCollectionSet; + case ExceptionResource.InvalidOperation_NullArray: + return SR.InvalidOperation_NullArray; + case ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted: + return SR.TaskT_TransitionToFinal_AlreadyCompleted; + case ExceptionResource.TaskCompletionSourceT_TrySetException_NullException: + return SR.TaskCompletionSourceT_TrySetException_NullException; + case ExceptionResource.TaskCompletionSourceT_TrySetException_NoExceptions: + return SR.TaskCompletionSourceT_TrySetException_NoExceptions; + case ExceptionResource.Memory_ThrowIfDisposed: + return SR.Memory_ThrowIfDisposed; + case ExceptionResource.Memory_OutstandingReferences: + return SR.Memory_OutstandingReferences; default: Debug.Assert(false, - "The enum value is not defined, please check the ExceptionArgument Enum."); + "The enum value is not defined, please check the ExceptionResource Enum."); return ""; } } @@ -100,8 +367,61 @@ namespace System // internal enum ExceptionArgument { + obj, + dictionary, array, + info, + key, text, values, + value, + startIndex, + task, + s, + input, + ownedMemory, + list, + index, + capacity, + collection, + item, + converter, + match, + count, + action, + comparison, + exceptions, + exception, + pointer, + start, + format + } + + // + // The convention for this enum is using the resource name as the enum name + // + internal enum ExceptionResource + { + ArgumentOutOfRange_Index, + ArgumentOutOfRange_Count, + Arg_ArrayPlusOffTooSmall, + NotSupported_ReadOnlyCollection, + Arg_RankMultiDimNotSupported, + Arg_NonZeroLowerBound, + ArgumentOutOfRange_ListInsert, + ArgumentOutOfRange_NeedNonNegNum, + ArgumentOutOfRange_SmallCapacity, + Argument_InvalidOffLen, + ArgumentOutOfRange_BiggerThanCollection, + Serialization_MissingKeys, + Serialization_NullKey, + NotSupported_KeyCollectionSet, + NotSupported_ValueCollectionSet, + InvalidOperation_NullArray, + TaskT_TransitionToFinal_AlreadyCompleted, + TaskCompletionSourceT_TrySetException_NullException, + TaskCompletionSourceT_TrySetException_NoExceptions, + Memory_ThrowIfDisposed, + Memory_OutstandingReferences, } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs b/external/corert/src/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs index 2535c5d289..8e85f21ede 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs @@ -16,7 +16,6 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Globalization; using System.Runtime.CompilerServices; using System.Threading; @@ -875,7 +874,6 @@ namespace System { throw new ArgumentException(SR.Argument_TimeZoneInfoInvalidTZif, nameof(data)); } - Contract.EndContractBlock(); UtcOffset = new TimeSpan(0, 0, TZif_ToInt32(data, index + 00)); IsDst = (data[index + 4] != 0); AbbreviationIndex = data[index + 5]; @@ -899,7 +897,6 @@ namespace System { throw new ArgumentException("bad data", nameof(data)); } - Contract.EndContractBlock(); Magic = (uint)TZif_ToInt32(data, index + 00); diff --git a/external/corert/src/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs b/external/corert/src/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs index 0074f80ac8..cbdf985602 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs @@ -21,7 +21,6 @@ using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Globalization; using System.IO; using System.Runtime.CompilerServices; @@ -901,19 +900,20 @@ namespace System static unsafe private string TryGetLocalizedNameByNativeResource(string filePath, int resource) { using (SafeLibraryHandle handle = - Interop.mincore.LoadLibraryEx_SafeHandle(filePath, IntPtr.Zero, Interop.mincore.LOAD_LIBRARY_AS_DATAFILE)) + Interop.Kernel32.LoadLibraryEx(filePath, IntPtr.Zero, Interop.Kernel32.LOAD_LIBRARY_AS_DATAFILE)) { if (!handle.IsInvalid) { - StringBuilder localizedResource = StringBuilderCache.Acquire(Interop.mincore.LOAD_STRING_MAX_LENGTH); - localizedResource.Length = Interop.mincore.LOAD_STRING_MAX_LENGTH; + const int LoadStringMaxLength = 500; - int result = Interop.mincore.LoadString(handle, resource, - localizedResource, localizedResource.Length); + StringBuilder localizedResource = new StringBuilder(LoadStringMaxLength); + + int result = Interop.User32.LoadString(handle, resource, + localizedResource, LoadStringMaxLength); if (result != 0) { - return StringBuilderCache.GetStringAndRelease(localizedResource); + return localizedResource.ToString(); } } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/TimeZoneInfo.WinRT.cs b/external/corert/src/System.Private.CoreLib/src/System/TimeZoneInfo.WinRT.cs index 08431aea0c..faa851c709 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/TimeZoneInfo.WinRT.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/TimeZoneInfo.WinRT.cs @@ -20,7 +20,6 @@ using System.Diagnostics; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; -using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; diff --git a/external/corert/src/System.Private.CoreLib/src/System/TimeZoneInfo.cs.REMOVED.git-id b/external/corert/src/System.Private.CoreLib/src/System/TimeZoneInfo.cs.REMOVED.git-id index a0fc2bd079..12ed4e3eb2 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/TimeZoneInfo.cs.REMOVED.git-id +++ b/external/corert/src/System.Private.CoreLib/src/System/TimeZoneInfo.cs.REMOVED.git-id @@ -1 +1 @@ -1bc66894cc4e508daeed6f57e375e2d932049ca9 \ No newline at end of file +eee72972b436f576dd749d3f4aaaa7e088a4efc7 \ No newline at end of file diff --git a/external/corert/src/System.Private.CoreLib/src/System/Type.CoreRT.cs b/external/corert/src/System.Private.CoreLib/src/System/Type.CoreRT.cs index c5922bdf81..8c1ce16906 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Type.CoreRT.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Type.CoreRT.cs @@ -17,7 +17,7 @@ namespace System public bool IsInterface => (GetAttributeFlagsImpl() & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface; [Intrinsic] - public static Type GetTypeFromHandle(RuntimeTypeHandle handle) => ReflectionCoreNonPortable.GetTypeForRuntimeTypeHandle(handle); + public static Type GetTypeFromHandle(RuntimeTypeHandle handle) => RuntimeTypeUnifier.GetTypeForRuntimeTypeHandle(handle); [Intrinsic] public static Type GetType(string typeName) => GetType(typeName, throwOnError: false, ignoreCase: false); diff --git a/external/corert/src/System.Private.CoreLib/src/System/TypeLoadException.CoreRT.cs b/external/corert/src/System.Private.CoreLib/src/System/TypeLoadException.CoreRT.cs index 34795e367e..66908bb748 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/TypeLoadException.CoreRT.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/TypeLoadException.CoreRT.cs @@ -9,7 +9,7 @@ namespace System internal TypeLoadException(string message, string typeName) : base(message) { - HResult = __HResults.COR_E_TYPELOAD; + HResult = HResults.COR_E_TYPELOAD; _typeName = typeName; } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/TypeLoadException.cs b/external/corert/src/System.Private.CoreLib/src/System/TypeLoadException.cs index 664628049b..7f0e9c1eac 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/TypeLoadException.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/TypeLoadException.cs @@ -7,30 +7,31 @@ using System.Runtime.Serialization; namespace System { [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public partial class TypeLoadException : SystemException { public TypeLoadException() : base(SR.Arg_TypeLoadException) { - HResult = __HResults.COR_E_TYPELOAD; + HResult = HResults.COR_E_TYPELOAD; } public TypeLoadException(string message) : base(message) { - HResult = __HResults.COR_E_TYPELOAD; + HResult = HResults.COR_E_TYPELOAD; } public TypeLoadException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_TYPELOAD; + HResult = HResults.COR_E_TYPELOAD; } protected TypeLoadException(SerializationInfo info, StreamingContext context) : base(info, context) { - _typeName = info.GetString("TypeLoadClassName"); + // Ignoring serialization input } public override string Message @@ -56,9 +57,12 @@ namespace System public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); - info.AddValue("TypeLoadClassName", _typeName, typeof(string)); + info.AddValue("TypeLoadClassName", null, typeof(string)); + info.AddValue("TypeLoadAssemblyName", null, typeof(string)); + info.AddValue("TypeLoadMessageArg", null, typeof(string)); + info.AddValue("TypeLoadResourceID", 0, typeof(int)); } - private string _typeName; + private readonly string _typeName; } } diff --git a/external/corert/src/System.Private.CoreLib/src/System/TypedReference.cs b/external/corert/src/System.Private.CoreLib/src/System/TypedReference.cs index b8301cc14c..e30c781cce 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/TypedReference.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/TypedReference.cs @@ -7,13 +7,14 @@ using System.Runtime; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; +using Internal.Runtime.CompilerServices; using Internal.Reflection.Augments; namespace System { [CLSCompliant(false)] [StructLayout(LayoutKind.Sequential)] - public struct TypedReference + public ref struct TypedReference { // Do not change the ordering of these fields. The JIT has a dependency on this layout. private readonly ByReference _value; diff --git a/external/corert/src/System.Private.CoreLib/src/System/ValueType.cs b/external/corert/src/System.Private.CoreLib/src/System/ValueType.cs index a222466992..d75dd2692e 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/ValueType.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/ValueType.cs @@ -17,7 +17,8 @@ namespace System { // CONTRACT with Runtime // Place holder type for type hierarchy, Compiler/Runtime requires this class - + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public abstract class ValueType { public override String ToString() diff --git a/external/corert/src/System.Private.CoreLib/src/System/WeakReference.cs b/external/corert/src/System.Private.CoreLib/src/System/WeakReference.cs index e5fda01ec0..4996e94ee5 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/WeakReference.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/WeakReference.cs @@ -21,6 +21,7 @@ using Internal.Runtime.Augments; namespace System { [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class WeakReference : ISerializable { // If you fix bugs here, please fix them in WeakReference at the same time. @@ -57,8 +58,8 @@ namespace System throw new ArgumentNullException(nameof(info)); } - Object target = info.GetValue("TrackedObject", typeof(Object)); - bool trackResurrection = info.GetBoolean("TrackResurrection"); + Object target = info.GetValue("TrackedObject", typeof(Object)); // Do not rename (binary serialization) + bool trackResurrection = info.GetBoolean("TrackResurrection"); // Do not rename (binary serialization) m_IsLongReference = trackResurrection; m_handle = GCHandle.ToIntPtr(GCHandle.Alloc(target, trackResurrection ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak)); @@ -197,7 +198,7 @@ namespace System } else { - Debug.Assert(false, "WinRTInteropCallback is null"); + Debug.Fail("WinRTInteropCallback is null"); } #endif // ENABLE_WINRT return null; @@ -218,7 +219,7 @@ namespace System } else { - Debug.Assert(false, "WinRTInteropCallback is null"); + Debug.Fail("WinRTInteropCallback is null"); } #endif // ENABLE_WINRT } @@ -230,8 +231,8 @@ namespace System throw new ArgumentNullException(nameof(info)); } - info.AddValue("TrackedObject", Target, typeof(Object)); - info.AddValue("TrackResurrection", m_IsLongReference); + info.AddValue("TrackedObject", Target, typeof(Object)); // Do not rename (binary serialization) + info.AddValue("TrackResurrection", m_IsLongReference); // Do not rename (binary serialization) } // Free all system resources associated with this reference. diff --git a/external/corert/src/System.Private.CoreLib/src/System/WeakReferenceOfT.cs b/external/corert/src/System.Private.CoreLib/src/System/WeakReferenceOfT.cs index 41fd8e31db..e36cda5082 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/WeakReferenceOfT.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/WeakReferenceOfT.cs @@ -18,11 +18,13 @@ using System.Threading; using System.Diagnostics; using Internal.Runtime.Augments; +using Internal.Runtime.CompilerServices; namespace System { // This class is sealed to mitigate security issues caused by Object::MemberwiseClone. [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class WeakReference : ISerializable where T : class { // If you fix bugs here, please fix them in WeakReference at the same time. @@ -59,8 +61,8 @@ namespace System throw new ArgumentNullException(nameof(info)); } - T target = (T)info.GetValue("TrackedObject", typeof(T)); - bool trackResurrection = info.GetBoolean("TrackResurrection"); + T target = (T)info.GetValue("TrackedObject", typeof(T)); // Do not rename (binary serialization) + bool trackResurrection = info.GetBoolean("TrackResurrection"); // Do not rename (binary serialization) m_handle = (IntPtr)GCHandle.Alloc(target, trackResurrection ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak); @@ -142,7 +144,7 @@ namespace System } else { - Debug.Assert(false, "WinRTInteropCallback is null"); + Debug.Fail("WinRTInteropCallback is null"); } #endif // ENABLE_WINRT return null; @@ -161,7 +163,7 @@ namespace System callbacks.SetCOMWeakReferenceTarget(this, target); else { - Debug.Assert(false, "WinRTInteropCallback is null"); + Debug.Fail("WinRTInteropCallback is null"); } #endif // ENABLE_WINRT } @@ -190,8 +192,8 @@ namespace System throw new ArgumentNullException(nameof(info)); } - info.AddValue("TrackedObject", this.GetTarget(), typeof(T)); - info.AddValue("TrackResurrection", m_trackResurrection); + info.AddValue("TrackedObject", this.GetTarget(), typeof(T)); // Do not rename (binary serialization) + info.AddValue("TrackResurrection", m_trackResurrection); // Do not rename (binary serialization) } } } diff --git a/external/corert/src/System.Private.DeveloperExperience.Console/src/Internal/DeveloperExperience/DeveloperExperienceConsole.cs b/external/corert/src/System.Private.DeveloperExperience.Console/src/Internal/DeveloperExperience/DeveloperExperienceConsole.cs index 15845d4b8d..b9c11adf97 100644 --- a/external/corert/src/System.Private.DeveloperExperience.Console/src/Internal/DeveloperExperience/DeveloperExperienceConsole.cs +++ b/external/corert/src/System.Private.DeveloperExperience.Console/src/Internal/DeveloperExperience/DeveloperExperienceConsole.cs @@ -4,6 +4,8 @@ using System; using System.Diagnostics; +using System.Reflection; + using Internal.StackTraceGenerator; namespace Internal.DeveloperExperience @@ -29,6 +31,11 @@ namespace Internal.DeveloperExperience // we take whatever data StackTraceGenerator can get (none/partial/all). No reason to fall-back because the base-type // never finds anything. } + + public sealed override void TryGetILOffsetWithinMethod(IntPtr ip, out int ilOffset) + { + Internal.StackTraceGenerator.StackTraceGenerator.TryGetILOffsetWithinMethod(ip, out ilOffset); + } } } diff --git a/external/corert/src/System.Private.DeveloperExperience.Console/src/System.Private.DeveloperExperience.Console.csproj b/external/corert/src/System.Private.DeveloperExperience.Console/src/System.Private.DeveloperExperience.Console.csproj index 63e8a67c25..b0d772fe32 100644 --- a/external/corert/src/System.Private.DeveloperExperience.Console/src/System.Private.DeveloperExperience.Console.csproj +++ b/external/corert/src/System.Private.DeveloperExperience.Console/src/System.Private.DeveloperExperience.Console.csproj @@ -1,28 +1,23 @@ - - + System.Private.DeveloperExperience.Console 4.0.0.0 Library - {F9EF39E7-C8E4-4776-A952-FEF7A1FC2D3B} true + true - - - - + + false + true + - - false - - diff --git a/external/corert/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/Callbacks.CoreRT.cs b/external/corert/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/Callbacks.CoreRT.cs deleted file mode 100644 index 9e30dffc7d..0000000000 --- a/external/corert/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/Callbacks.CoreRT.cs +++ /dev/null @@ -1,91 +0,0 @@ -// 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 Internal.Runtime.Augments; -using Internal.NativeFormat; -using Internal.Runtime.TypeLoader; -using Internal.Reflection.Execution; -using System.Runtime.InteropServices; - -namespace Internal.Runtime.CompilerHelpers -{ - internal class Callbacks : InteropCallbacks - { - public override bool TryGetMarshallerDataForDelegate(RuntimeTypeHandle delegateTypeHandle, out McgPInvokeDelegateData data) - { - IntPtr openStub, closedStub, delegateCreationStub; - if (!TryGetMarshallersForDelegate(delegateTypeHandle, out openStub, out closedStub, out delegateCreationStub)) - { - data = default(McgPInvokeDelegateData); - return false; - } - - data = new global::System.Runtime.InteropServices.McgPInvokeDelegateData() - { - ReverseOpenStaticDelegateStub = openStub, - ReverseStub = closedStub, - ForwardDelegateCreationStub = delegateCreationStub - }; - return true; - } - - private static unsafe bool TryGetNativeReaderForBlob(NativeFormatModuleInfo module, ReflectionMapBlob blob, out NativeReader reader) - { - byte* pBlob; - uint cbBlob; - - if (module.TryFindBlob((int)blob, out pBlob, out cbBlob)) - { - reader = new NativeReader(pBlob, cbBlob); - return true; - } - - reader = default(NativeReader); - return false; - } - - private unsafe bool TryGetMarshallersForDelegate(RuntimeTypeHandle delegateTypeHandle, out IntPtr openStub, out IntPtr closedStub, out IntPtr delegateCreationStub) - { - int delegateHashcode = delegateTypeHandle.GetHashCode(); - openStub = IntPtr.Zero; - closedStub = IntPtr.Zero; - delegateCreationStub = IntPtr.Zero; - - foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules()) - { - NativeReader delegateMapReader; - if (TryGetNativeReaderForBlob(module, ReflectionMapBlob.DelegateMarshallingStubMap, out delegateMapReader)) - { - NativeParser delegateMapParser = new NativeParser(delegateMapReader, 0); - NativeHashtable delegateHashtable = new NativeHashtable(delegateMapParser); - - ExternalReferencesTable externalReferences = default(ExternalReferencesTable); - externalReferences.InitializeCommonFixupsTable(module); - - var lookup = delegateHashtable.Lookup(delegateHashcode); - NativeParser entryParser; - while (!(entryParser = lookup.GetNext()).IsNull) - { - RuntimeTypeHandle foundDelegateType = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); - if (foundDelegateType.Equals(delegateTypeHandle)) - { - byte* pOpen = (byte*)externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned()); - byte* pClose = (byte*)externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned()); - byte* pDelegateCreation = (byte*)externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned()); - openStub = (IntPtr)pOpen; - closedStub = (IntPtr)pClose; - delegateCreationStub = (IntPtr)pDelegateCreation; - return true; - } - } - } - } - - return false; - } - - } - -} diff --git a/external/corert/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs b/external/corert/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs index 0b85c75cf6..eaf08bb354 100644 --- a/external/corert/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs +++ b/external/corert/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs @@ -6,7 +6,9 @@ using System; using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +#if !CORECLR using Internal.Runtime.Augments; +#endif namespace Internal.Runtime.CompilerHelpers { @@ -18,11 +20,15 @@ namespace Internal.Runtime.CompilerHelpers { public static void InitializeLibrary() { -#if !CORERT +#if PROJECTN || CORECLR __vtable_IUnknown.Initialize(); McgModuleManager.Initialize(); #endif - RuntimeAugments.InitializeInteropLookups(new Callbacks()); + +#if !CORECLR + /// @TODO: enable this for Mcg on CoreCLR scenario + RuntimeAugments.InitializeInteropLookups(RuntimeInteropData.Instance); +#endif } } } diff --git a/external/corert/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.CoreRT.cs b/external/corert/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.CoreRT.cs new file mode 100644 index 0000000000..4de8b46035 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.CoreRT.cs @@ -0,0 +1,203 @@ +// 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 Internal.Runtime.Augments; +using Internal.NativeFormat; +using Internal.Runtime.TypeLoader; +using Internal.Reflection.Execution; +using System.Runtime.InteropServices; + +namespace Internal.Runtime.CompilerHelpers +{ + internal partial class RuntimeInteropData + { + public override bool TryGetMarshallerDataForDelegate(RuntimeTypeHandle delegateTypeHandle, out McgPInvokeDelegateData data) + { + IntPtr openStub, closedStub, delegateCreationStub; + if (!TryGetMarshallersForDelegate(delegateTypeHandle, out openStub, out closedStub, out delegateCreationStub)) + { + data = default(McgPInvokeDelegateData); + return false; + } + + data = new global::System.Runtime.InteropServices.McgPInvokeDelegateData() + { + ReverseOpenStaticDelegateStub = openStub, + ReverseStub = closedStub, + ForwardDelegateCreationStub = delegateCreationStub + }; + return true; + } + #region "Struct Data" + public override bool TryGetStructUnmarshalStub(RuntimeTypeHandle structureTypeHandle, out IntPtr unmarshalStub) + { + IntPtr marshalStub; + IntPtr destroyStub; + bool hasInvalidLayout; + int size; + return TryGetMarshallersForStruct(structureTypeHandle, out marshalStub, out unmarshalStub, out destroyStub, out hasInvalidLayout, out size); + } + + public override bool TryGetStructMarshalStub(RuntimeTypeHandle structureTypeHandle, out IntPtr marshalStub) + { + IntPtr unmarshalStub; + IntPtr destroyStub; + bool hasInvalidLayout; + int size; + return TryGetMarshallersForStruct(structureTypeHandle, out marshalStub, out unmarshalStub, out destroyStub, out hasInvalidLayout, out size); + } + + public override bool TryGetDestroyStructureStub(RuntimeTypeHandle structureTypeHandle, out IntPtr destroyStub, out bool hasInvalidLayout) + { + IntPtr marshalStub; + IntPtr unmarshalStub; + int size; + return TryGetMarshallersForStruct(structureTypeHandle, out marshalStub, out unmarshalStub, out destroyStub, out hasInvalidLayout, out size); + } + + public override bool TryGetStructUnsafeStructSize(RuntimeTypeHandle structureTypeHandle, out int size) + { + IntPtr marshalStub; + IntPtr unmarshalStub; + IntPtr destroyStub; + bool hasInvalidLayout; + return TryGetMarshallersForStruct(structureTypeHandle, out marshalStub, out unmarshalStub, out destroyStub, out hasInvalidLayout, out size); + } + + public override bool TryGetStructFieldOffset(RuntimeTypeHandle structureTypeHandle, string fieldName, out bool structExists, out uint offset) + { + ExternalReferencesTable externalReferences; + NativeParser entryParser; + structExists = false; + if (TryGetStructData(structureTypeHandle, out externalReferences, out entryParser)) + { + structExists = true; + // skip the first 4 IntPtrs(3 stubs and size) + entryParser.SkipInteger(); + entryParser.SkipInteger(); + entryParser.SkipInteger(); + entryParser.SkipInteger(); + + uint mask = entryParser.GetUnsigned(); + uint fieldCount = mask >> 1; + for (uint index = 0; index < fieldCount; index++) + { + string name = entryParser.GetString(); + offset = entryParser.GetUnsigned(); + if (name == fieldName) + { + return true; + } + } + } + offset = 0; + return false; + } + #endregion + + private static unsafe bool TryGetNativeReaderForBlob(NativeFormatModuleInfo module, ReflectionMapBlob blob, out NativeReader reader) + { + byte* pBlob; + uint cbBlob; + + if (module.TryFindBlob((int)blob, out pBlob, out cbBlob)) + { + reader = new NativeReader(pBlob, cbBlob); + return true; + } + + reader = default(NativeReader); + return false; + } + + private unsafe bool TryGetMarshallersForDelegate(RuntimeTypeHandle delegateTypeHandle, out IntPtr openStub, out IntPtr closedStub, out IntPtr delegateCreationStub) + { + int delegateHashcode = delegateTypeHandle.GetHashCode(); + openStub = IntPtr.Zero; + closedStub = IntPtr.Zero; + delegateCreationStub = IntPtr.Zero; + + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules()) + { + NativeReader delegateMapReader; + if (TryGetNativeReaderForBlob(module, ReflectionMapBlob.DelegateMarshallingStubMap, out delegateMapReader)) + { + NativeParser delegateMapParser = new NativeParser(delegateMapReader, 0); + NativeHashtable delegateHashtable = new NativeHashtable(delegateMapParser); + + ExternalReferencesTable externalReferences = default(ExternalReferencesTable); + externalReferences.InitializeCommonFixupsTable(module); + + var lookup = delegateHashtable.Lookup(delegateHashcode); + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + RuntimeTypeHandle foundDelegateType = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + if (foundDelegateType.Equals(delegateTypeHandle)) + { + openStub = externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned()); + closedStub = externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned()); + delegateCreationStub = externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned()); + return true; + } + } + } + } + return false; + } + + private unsafe bool TryGetStructData(RuntimeTypeHandle structTypeHandle, out ExternalReferencesTable externalReferences, out NativeParser entryParser) + { + int structHashcode = structTypeHandle.GetHashCode(); + externalReferences = default(ExternalReferencesTable); + entryParser = default(NativeParser); + foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules()) + { + NativeReader structMapReader; + if (TryGetNativeReaderForBlob(module, ReflectionMapBlob.StructMarshallingStubMap, out structMapReader)) + { + NativeParser structMapParser = new NativeParser(structMapReader, 0); + NativeHashtable structHashtable = new NativeHashtable(structMapParser); + + externalReferences.InitializeCommonFixupsTable(module); + + var lookup = structHashtable.Lookup(structHashcode); + while (!(entryParser = lookup.GetNext()).IsNull) + { + RuntimeTypeHandle foundStructType = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + if (foundStructType.Equals(structTypeHandle)) + { + return true; + } + } + } + } + return false; + } + + private unsafe bool TryGetMarshallersForStruct(RuntimeTypeHandle structTypeHandle, out IntPtr marshalStub, out IntPtr unmarshalStub, out IntPtr destroyStub, out bool hasInvalidLayout, out int size) + { + marshalStub = IntPtr.Zero; + unmarshalStub = IntPtr.Zero; + destroyStub = IntPtr.Zero; + hasInvalidLayout = true; + size = 0; + + ExternalReferencesTable externalReferences; + NativeParser entryParser; + if (TryGetStructData(structTypeHandle, out externalReferences, out entryParser)) + { + marshalStub = externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned()); + unmarshalStub = externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned()); + destroyStub = externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned()); + size = (int)entryParser.GetUnsigned(); + uint mask = entryParser.GetUnsigned(); + hasInvalidLayout = (mask & 0x1) == 1; + return true; + } + return false; + } + } +} diff --git a/external/corert/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.ProjectN.cs b/external/corert/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.ProjectN.cs new file mode 100644 index 0000000000..84678d1b97 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.ProjectN.cs @@ -0,0 +1,56 @@ +// 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 Internal.Runtime.Augments; +using Internal.NativeFormat; +using Internal.Runtime.TypeLoader; +using Internal.Reflection.Execution; +using System.Runtime.InteropServices; + +namespace Internal.Runtime.CompilerHelpers +{ + internal partial class RuntimeInteropData : InteropCallbacks + { + public override bool TryGetMarshallerDataForDelegate(RuntimeTypeHandle delegateTypeHandle, out McgPInvokeDelegateData data) + { + return McgModuleManager.GetPInvokeDelegateData(delegateTypeHandle, out data); + } + + #region "Struct Data" + public override bool TryGetStructUnmarshalStub(RuntimeTypeHandle structureTypeHandle, out IntPtr unmarshalStub) + { + return McgModuleManager.TryGetStructUnmarshalStub(structureTypeHandle, out unmarshalStub); + } + + public override bool TryGetStructMarshalStub(RuntimeTypeHandle structureTypeHandle, out IntPtr marshalStub) + { + return McgModuleManager.TryGetStructMarshalStub(structureTypeHandle, out marshalStub); + } + + public override bool TryGetDestroyStructureStub(RuntimeTypeHandle structureTypeHandle, out IntPtr destroyStructureStub, out bool hasInvalidLayout) + { + return McgModuleManager.TryGetDestroyStructureStub(structureTypeHandle, out destroyStructureStub, out hasInvalidLayout); + } + + public override bool TryGetStructFieldOffset(RuntimeTypeHandle structureTypeHandle, string fieldName, out bool structExists, out uint offset) + { + return McgModuleManager.TryGetStructFieldOffset(structureTypeHandle, fieldName, out structExists, out offset); + } + + public override bool TryGetStructUnsafeStructSize(RuntimeTypeHandle structureTypeHandle, out int size) + { + RuntimeTypeHandle unsafeStructType; + size = 0; + if (McgModuleManager.TryGetStructUnsafeStructType(structureTypeHandle, out unsafeStructType)) + { + size = unsafeStructType.GetValueTypeSize(); + return true; + } + return false; + } + #endregion + } +} diff --git a/external/corert/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/Callbacks.ProjectN.cs b/external/corert/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.cs similarity index 54% rename from external/corert/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/Callbacks.ProjectN.cs rename to external/corert/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.cs index 9705cba336..4daf4ad66b 100644 --- a/external/corert/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/Callbacks.ProjectN.cs +++ b/external/corert/src/System.Private.Interop/src/Internal/Runtime/CompilerHelpers/RuntimeInteropData.cs @@ -12,11 +12,19 @@ using System.Runtime.InteropServices; namespace Internal.Runtime.CompilerHelpers { - internal class Callbacks : InteropCallbacks + internal partial class RuntimeInteropData : InteropCallbacks { - public override bool TryGetMarshallerDataForDelegate(RuntimeTypeHandle delegateTypeHandle, out McgPInvokeDelegateData data) + private static RuntimeInteropData s_interopData; + public static RuntimeInteropData Instance { - return McgModuleManager.GetPInvokeDelegateData(delegateTypeHandle, out data); + get + { + if (s_interopData == null) + { + s_interopData = new RuntimeInteropData(); + } + return s_interopData; + } } } } diff --git a/external/corert/src/System.Private.Interop/src/Interop/Interop.PlatformNotSupported.cs b/external/corert/src/System.Private.Interop/src/Interop/Interop.PlatformNotSupported.cs index d3af3c1c26..265194093d 100644 --- a/external/corert/src/System.Private.Interop/src/Interop/Interop.PlatformNotSupported.cs +++ b/external/corert/src/System.Private.Interop/src/Interop/Interop.PlatformNotSupported.cs @@ -11,8 +11,7 @@ using System.Runtime.InteropServices; // are from McgMarshal , refactoring WinRT marshal API is TODO namespace System.Runtime.InteropServices { -#if CORECLR - +#if !ENABLE_MIN_WINRT public static partial class McgMarshal { @@ -60,6 +59,6 @@ namespace System.Runtime.InteropServices throw new PlatformNotSupportedException("StringToHStringForField"); } } -#endif +#endif // !ENABLE_MIN_WINRT } diff --git a/external/corert/src/System.Private.Interop/src/Interop/Interop.WinRT.Basic.cs b/external/corert/src/System.Private.Interop/src/Interop/Interop.WinRT.Basic.cs index b265e8c163..8989c2f721 100644 --- a/external/corert/src/System.Private.Interop/src/Interop/Interop.WinRT.Basic.cs +++ b/external/corert/src/System.Private.Interop/src/Interop/Interop.WinRT.Basic.cs @@ -67,6 +67,7 @@ namespace System.Runtime.InteropServices public static partial class ExternalInterop { +#if ENABLE_MIN_WINRT [DllImport(Libraries.CORE_WINRT)] [McgGeneratedNativeCallCodeAttribute] [MethodImplAttribute(MethodImplOptions.NoInlining)] @@ -80,6 +81,7 @@ namespace System.Runtime.InteropServices uint length, HSTRING_HEADER* phstringHeader, void* hstring); +#endif [DllImport(Libraries.CORE_COM)] [McgGeneratedNativeCallCodeAttribute] @@ -157,6 +159,7 @@ namespace System.Runtime.InteropServices } } +#if ENABLE_MIN_WINRT internal static unsafe void RoGetActivationFactory(string className, ref Guid iid, out IntPtr ppv) { fixed (char* unsafe_className = className) @@ -186,6 +189,7 @@ namespace System.Runtime.InteropServices } } } +#endif public static unsafe int CoGetContextToken(out IntPtr ppToken) { diff --git a/external/corert/src/System.Private.Interop/src/InteropExtensions/DebugAnnotations.cs b/external/corert/src/System.Private.Interop/src/InteropExtensions/DebugAnnotations.cs new file mode 100644 index 0000000000..ad8ad4f7f9 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/InteropExtensions/DebugAnnotations.cs @@ -0,0 +1,20 @@ +// 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. + +namespace System.Diagnostics +{ + /// + /// Annotations used by debugger + /// + public static class DebugAnnotations + { + /// + /// Informs debugger that previous line contains code that debugger needs to dive deeper inside. + /// + public static void PreviousCallContainsDebuggerStepInCode() + { + // This is a marker method and has no code in method body + } + } +} \ No newline at end of file diff --git a/external/corert/src/System.Private.Interop/src/InteropExtensions/DependencyReductionConditionallyDependentAttribute.cs b/external/corert/src/System.Private.Interop/src/InteropExtensions/DependencyReductionConditionallyDependentAttribute.cs new file mode 100644 index 0000000000..b75e4a770d --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/InteropExtensions/DependencyReductionConditionallyDependentAttribute.cs @@ -0,0 +1,16 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + // When applied to a type this custom attribute will cause the type to be necessary + // if dependencyType is necessary + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = true)] + public class DependencyReductionConditionallyDependentAttribute : Attribute + { + public DependencyReductionConditionallyDependentAttribute(System.Type dependencyType) + { + } + } +} diff --git a/external/corert/src/System.Private.Interop/src/InteropExtensions/DependencyReductionRootAttribute.cs b/external/corert/src/System.Private.Interop/src/InteropExtensions/DependencyReductionRootAttribute.cs new file mode 100644 index 0000000000..417cdb8417 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/InteropExtensions/DependencyReductionRootAttribute.cs @@ -0,0 +1,13 @@ +// 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. + +namespace System.Runtime.CompilerServices +{ + // When applied to a type this custom attribute will cause the type to become a new dependency + // reduction root. + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)] + public class DependencyReductionRootAttribute : Attribute + { + } +} diff --git a/external/corert/src/System.Private.Interop/src/InteropExtensions/DependencyReductionTypeRemoved.cs b/external/corert/src/System.Private.Interop/src/InteropExtensions/DependencyReductionTypeRemoved.cs new file mode 100644 index 0000000000..f6c2c7d0cd --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/InteropExtensions/DependencyReductionTypeRemoved.cs @@ -0,0 +1,23 @@ +// 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.Diagnostics; + +namespace System.Runtime.CompilerServices +{ + // This type should never be directly used. Dependency reduction + // can replace TypeHandles that should not be used with the TypeHandle + // for this type. + + // Rooted so we don't replace types with a different missing type + [DependencyReductionRoot] + public class DependencyReductionTypeRemoved + { + public DependencyReductionTypeRemoved() + { + Debug.Fail("A type that was removed by dependency reduction has been instantiated."); + throw new Exception("A type that was removed by dependency reduction has been instantiated."); + } + } +} diff --git a/external/corert/src/System.Private.Interop/src/InteropExtensions/EagerStaticClassConstructionAttribute.cs b/external/corert/src/System.Private.Interop/src/InteropExtensions/EagerStaticClassConstructionAttribute.cs new file mode 100644 index 0000000000..26e6484ee1 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/InteropExtensions/EagerStaticClassConstructionAttribute.cs @@ -0,0 +1,15 @@ +// 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.Runtime; +namespace System.Runtime.CompilerServices +{ + // When applied to a type this custom attribute will cause any static class constructor to be run eagerly + // at module load time rather than deferred till just before the class is used. + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)] + public class EagerStaticClassConstructionAttribute : Attribute + { + } +} diff --git a/external/corert/src/System.Private.Interop/src/InteropExtensions/ExplicitScopeAttribute.cs b/external/corert/src/System.Private.Interop/src/InteropExtensions/ExplicitScopeAttribute.cs new file mode 100644 index 0000000000..fc309169b6 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/InteropExtensions/ExplicitScopeAttribute.cs @@ -0,0 +1,28 @@ +// 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; + +namespace Internal.Reflection +{ + // + // This attribute is used to affect the generation of Project N metadata. When applied to a class, + // it will cause the metadata generator to emit the type into a user-specified scope (creating it if necessary.) + // + // For Fx, One use of this is to allow us to generate metadata for non-public types for private use by other FX components. + // By throwing it into a private scope that Reflection knows about, we can prevent Reflection's own consumers + // from seeing the internal type. + // For FX, it doesn't use(or reference) this attribute in this contact. Each FX assembly via a transform will inject a definition of this attribute and reference that one + + // For Interop, We use it to pass native winmd winrt type's winmd infomation to CreateMetadata Transform, so it knows where this + // native winmd winrt type originally comes from. + // For Interop, it will use this attribute in this contract during Mcg Transform. + [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Struct | AttributeTargets.Delegate, Inherited = false)] + public sealed class ExplicitScopeAttribute : Attribute + { + public ExplicitScopeAttribute(String assemblyIdentity) + { + } + } +} diff --git a/external/corert/src/System.Private.Interop/src/InteropExtensions/FixupRuntimeTypeHandle.cs b/external/corert/src/System.Private.Interop/src/InteropExtensions/FixupRuntimeTypeHandle.cs new file mode 100644 index 0000000000..9fa6a33611 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/InteropExtensions/FixupRuntimeTypeHandle.cs @@ -0,0 +1,30 @@ +// 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.Runtime.InteropServices; + +namespace Internal.Runtime.CompilerServices +{ + /// + /// FixupRuntimeTypeHandle is a do nothing class for consistency with ProjectN + /// + public unsafe struct FixupRuntimeTypeHandle + { + private RuntimeTypeHandle _handle; + + public FixupRuntimeTypeHandle(RuntimeTypeHandle runtimeTypeHandle) + { + this._handle = runtimeTypeHandle; + } + + public RuntimeTypeHandle RuntimeTypeHandle + { + get + { + return _handle; + } + } + } +} diff --git a/external/corert/src/System.Private.Interop/src/InteropExtensions/IActivationFactory.cs b/external/corert/src/System.Private.Interop/src/InteropExtensions/IActivationFactory.cs new file mode 100644 index 0000000000..955e991eda --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/InteropExtensions/IActivationFactory.cs @@ -0,0 +1,23 @@ +// 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.Runtime.InteropServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + + /// + /// This one is used by MCG as anchor type for the real IActivationFactory interface + /// + [Guid("00000035-0000-0000-C000-000000000046")] + public interface IActivationFactoryInternal + { + /// + /// Creates an new instance + /// + /// The IInspectable of the created object + object ActivateInstance(); + } +} diff --git a/external/corert/src/System.Private.Interop/src/InteropExtensions/InternalExtensions.cs b/external/corert/src/System.Private.Interop/src/InteropExtensions/InternalExtensions.cs new file mode 100644 index 0000000000..d5fe16588d --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/InteropExtensions/InternalExtensions.cs @@ -0,0 +1,26 @@ +// 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; + +namespace System.Runtime.InteropServices +{ + /// + /// Internal APIs available on ProjectN but not in ProjectK + /// + static class InternalExtensions + { + // Converts the DateTime instance into an OLE Automation compatible + // double date. + internal static double ToOADate(this DateTime dateTime) + { + throw new NotSupportedException("ToOADate"); + } + + internal static long DoubleDateToTicks(this double value) + { + throw new NotSupportedException("DoubleDateToTicks"); + } + } +} diff --git a/external/corert/src/System.Private.Interop/src/InteropExtensions/InteropExtensions.cs b/external/corert/src/System.Private.Interop/src/InteropExtensions/InteropExtensions.cs new file mode 100644 index 0000000000..93c46d95b2 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/InteropExtensions/InteropExtensions.cs @@ -0,0 +1,467 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Collections.Generic; +using System.Runtime; +using System.Reflection; + +namespace System.Runtime.InteropServices +{ + /// + /// Hooks for System.Private.Interop.dll code to access internal functionality in System.Private.CoreLib.dll. + /// Methods added to InteropExtensions should also be added to the System.Private.CoreLib.InteropServices contract + /// in order to be accessible from System.Private.Interop.dll. + /// + [CLSCompliant(false)] + public static class InteropExtensions + { + // Converts a managed DateTime to native OLE datetime + // Used by MCG marshalling code + public static double ToNativeOleDate(DateTime dateTime) + { + return dateTime.ToOADate(); + } + + // Converts native OLE datetime to managed DateTime + // Used by MCG marshalling code + public static DateTime FromNativeOleDate(double nativeOleDate) + { + return new DateTime(nativeOleDate.DoubleDateToTicks()); + } + + // Used by MCG's SafeHandle marshalling code to initialize a handle + public static void InitializeHandle(SafeHandle safeHandle, IntPtr win32Handle) + { + // We need private reflection here to access the handle field. + FieldInfo fieldInfo = safeHandle.GetType().GetField("handle", BindingFlags.NonPublic | BindingFlags.Instance); + fieldInfo.SetValue(safeHandle, win32Handle); + } + + // Used for methods in System.Private.Interop.dll that need to work from offsets on boxed structs + public unsafe static void PinObjectAndCall(Object obj, Action del) + { + throw new NotSupportedException("PinObjectAndCall"); + } + + public static void CopyToManaged(IntPtr source, Array destination, int startIndex, int length) + { + throw new NotSupportedException("CopyToManaged"); + } + + public static void CopyToNative(Array array, int startIndex, IntPtr destination, int length) + { + throw new NotSupportedException("CopyToNative"); + } + + public static int GetElementSize(this Array array) + { + throw new NotSupportedException("GetElementSize"); + } + + public static bool IsBlittable(this RuntimeTypeHandle handle) + { + throw new NotSupportedException("IsBlittable"); + } + + public static bool IsElementTypeBlittable(this Array array) + { + throw new NotSupportedException("IsElementTypeBlittable"); + } + + public static bool IsGenericType(this RuntimeTypeHandle handle) + { + throw new NotSupportedException("IsGenericType"); + } + + + public static bool IsClass(RuntimeTypeHandle handle) + { + return Type.GetTypeFromHandle(handle).GetTypeInfo().IsClass; + } + + public static IntPtr GetNativeFunctionPointer(this Delegate del) + { + throw new PlatformNotSupportedException("GetNativeFunctionPointer"); + } + public static IntPtr GetFunctionPointer(this Delegate del, out RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate) + { + // Note this work only for non-static methods , for static methods + // _methodPtr points to a stub that remove the this pointer and + // _methodPtrAux points actual pointer. + typeOfFirstParameterIfInstanceDelegate = default(RuntimeTypeHandle); + BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static; + FieldInfo field = del.GetType().GetField("_methodPtr", bindFlags); + return (IntPtr)field.GetValue(del); + } + + public static IntPtr GetRawFunctionPointerForOpenStaticDelegate(this Delegate del) + { + throw new NotSupportedException("GetRawFunctionPointerForOpenStaticDelegate"); + } + + public static IntPtr GetRawValue(this RuntimeTypeHandle handle) + { + throw new NotSupportedException("GetRawValue"); + } + + public static bool IsOfType(this Object obj, RuntimeTypeHandle handle) + { + return obj.GetType() == Type.GetTypeFromHandle(handle); + } + + public static bool IsNull(this RuntimeTypeHandle handle) + { + return handle.Equals(default(RuntimeTypeHandle)); + } + + public static Type GetTypeFromHandle(IntPtr typeHandle) + { + throw new NotSupportedException("GetTypeFromHandle(IntPtr)"); + } + + public static Type GetTypeFromHandle(RuntimeTypeHandle typeHandle) + { + return Type.GetTypeFromHandle(typeHandle); + } + + public static int GetValueTypeSize(this RuntimeTypeHandle handle) + { + throw new NotSupportedException("GetValueTypeSize"); + } + + public static bool IsValueType(this RuntimeTypeHandle handle) + { + return Type.GetTypeFromHandle(handle).GetTypeInfo().IsValueType; + } + + public static bool IsEnum(this RuntimeTypeHandle handle) + { + return Type.GetTypeFromHandle(handle).GetTypeInfo().IsEnum; + } + + public static bool AreTypesAssignable(RuntimeTypeHandle sourceHandle, RuntimeTypeHandle targetHandle) + { + Type srcType = Type.GetTypeFromHandle(sourceHandle); + Type targetType = Type.GetTypeFromHandle(targetHandle); + return targetType.IsAssignableFrom(srcType); + } + + public static unsafe void Memcpy(IntPtr destination, IntPtr source, int bytesToCopy) + { + throw new NotSupportedException("Memcpy"); + } + + public static bool RuntimeRegisterGcCalloutForGCStart(IntPtr pCalloutMethod) + { + //Nop + return true; + } + + public static bool RuntimeRegisterGcCalloutForGCEnd(IntPtr pCalloutMethod) + { + //Nop + return true; + } + + public static bool RuntimeRegisterGcCalloutForAfterMarkPhase(IntPtr pCalloutMethod) + { + //Nop + return true; + } + + public static bool RuntimeRegisterRefCountedHandleCallback(IntPtr pCalloutMethod, RuntimeTypeHandle pTypeFilter) + { + // Nop + return true; + } + + public static void RuntimeUnregisterRefCountedHandleCallback(IntPtr pCalloutMethod, RuntimeTypeHandle pTypeFilter) + { + //Nop + } + public static IntPtr RuntimeHandleAllocRefCounted(Object value) + { + return GCHandle.ToIntPtr( GCHandle.Alloc(value, GCHandleType.Normal)); + } + + public static void RuntimeHandleSet(IntPtr handle, Object value) + { + GCHandle gcHandle = GCHandle.FromIntPtr(handle); + gcHandle.Target = value; + } + + public static void RuntimeHandleFree(IntPtr handlePtr) + { + GCHandle handle = GCHandle.FromIntPtr(handlePtr); + handle.Free(); + } + + public static IntPtr RuntimeHandleAllocDependent(object primary, object secondary) + { + throw new NotSupportedException("RuntimeHandleAllocDependent"); + } + + public static bool RuntimeIsPromoted(object obj) + { + throw new NotSupportedException("RuntimeIsPromoted"); + } + + public static void RuntimeHandleSetDependentSecondary(IntPtr handle, Object secondary) + { + throw new NotSupportedException("RuntimeHandleSetDependentSecondary"); + } + + public static T UncheckedCast(object obj) where T : class + { + return obj as T; + } + + public static bool IsArray(RuntimeTypeHandle type) + { + throw new NotSupportedException("IsArray"); + } + + public static RuntimeTypeHandle GetArrayElementType(RuntimeTypeHandle arrayType) + { + throw new NotSupportedException("GetArrayElementType"); + } + + public static RuntimeTypeHandle GetTypeHandle(this object target) + { + Type type = target.GetType(); + return type.TypeHandle; + } + + public static bool IsInstanceOf(object obj, RuntimeTypeHandle typeHandle) + { + Type type = Type.GetTypeFromHandle(typeHandle); + return type.IsInstanceOfType(obj); + } + + public static bool IsInstanceOfClass(object obj, RuntimeTypeHandle classTypeHandle) + { + return obj.GetType() == Type.GetTypeFromHandle(classTypeHandle); + } + + public static bool IsInstanceOfInterface(object obj, RuntimeTypeHandle interfaceTypeHandle) + { + Type interfaceType = Type.GetTypeFromHandle(interfaceTypeHandle); + return TypeExtensions.IsInstanceOfType(interfaceType, obj); + } + + public static bool GuidEquals(ref Guid left, ref Guid right) + { + return left.Equals(right); + } + + public static bool ComparerEquals(T left, T right) + { + throw new NotSupportedException("ComparerEquals"); + } + + public static object RuntimeNewObject(RuntimeTypeHandle typeHnd) + { + Func getUninitializedObjectDelegate = + (Func) + typeof(string) + .GetTypeInfo() + .Assembly + .GetType("System.Runtime.Serialization.FormatterServices") + ?.GetMethod("GetUninitializedObject", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static) + ?.CreateDelegate(typeof(Func)); + + return getUninitializedObjectDelegate.Invoke(Type.GetTypeFromHandle(typeHnd)); + } + + internal static unsafe int wcslen(char* ptr) + { + char* end = ptr; + + // The following code is (somewhat surprisingly!) significantly faster than a naive loop, + // at least on x86 and the current jit. + + // First make sure our pointer is aligned on a dword boundary + while (((uint)end & 3) != 0 && *end != 0) + end++; + if (*end != 0) + { + // The loop condition below works because if "end[0] & end[1]" is non-zero, that means + // neither operand can have been zero. If is zero, we have to look at the operands individually, + // but we hope this going to fairly rare. + + // In general, it would be incorrect to access end[1] if we haven't made sure + // end[0] is non-zero. However, we know the ptr has been aligned by the loop above + // so end[0] and end[1] must be in the same page, so they're either both accessible, or both not. + + while ((end[0] & end[1]) != 0 || (end[0] != 0 && end[1] != 0)) + { + end += 2; + } + } + // finish up with the naive loop + for (; *end != 0; end++) + ; + + int count = (int)(end - ptr); + + return count; + } + + /// + /// Copy StringBuilder's contents to the char*, and appending a '\0' + /// The destination buffer must be big enough, and include space for '\0' + /// NOTE: There is no guarantee the destination pointer has enough size, but we have no choice + /// because the pointer might come from native code. + /// + /// + /// + public static unsafe void UnsafeCopyTo(this System.Text.StringBuilder stringBuilder, char* destination) + { + for (int i = 0; i < stringBuilder.Length; i++) + { + destination[i] = stringBuilder[i]; + } + destination[stringBuilder.Length] = '\0'; + } + + public static unsafe void ReplaceBuffer(this System.Text.StringBuilder stringBuilder, char* newBuffer) + { + // Mimic N stringbuilder replacebuffer behaviour.wcslen assume newBuffer to be '\0' terminated. + int len = wcslen(newBuffer); + stringBuilder.Clear(); + // the '+1' is for back-compat with desktop CLR in terms of length calculation because desktop + // CLR had '\0' + stringBuilder.EnsureCapacity(len + 1); + stringBuilder.Append(newBuffer, len); + } + + public static void ReplaceBuffer(this System.Text.StringBuilder stringBuilder, char[] newBuffer) + { + // mimic N stringbuilder replacebuffer behaviour. this's safe to do since we know the + // length of newBuffer. + stringBuilder.Clear(); + // the '+1' is for back-compat with desktop CLR in terms of length calculation because desktop + // CLR had '\0' + stringBuilder.EnsureCapacity(newBuffer.Length + 1); + stringBuilder.Append(newBuffer); + } + + public static char[] GetBuffer(this System.Text.StringBuilder stringBuilder, out int len) + { + return stringBuilder.GetBuffer(out len); + } + + public static IntPtr RuntimeHandleAllocVariable(Object value, uint type) + { + throw new NotSupportedException("RuntimeHandleAllocVariable"); + } + + public static uint RuntimeHandleGetVariableType(IntPtr handle) + { + throw new NotSupportedException("RuntimeHandleGetVariableType"); + } + + public static void RuntimeHandleSetVariableType(IntPtr handle, uint type) + { + throw new NotSupportedException("RuntimeHandleSetVariableType"); + } + + public static uint RuntimeHandleCompareExchangeVariableType(IntPtr handle, uint oldType, uint newType) + { + throw new NotSupportedException("RuntimeHandleCompareExchangeVariableType"); + } + + public static void SetExceptionErrorCode(Exception exception, int hr) + { + throw new NotSupportedException("SetExceptionErrorCode"); + } + + public static Exception CreateDataMisalignedException(string message) + { + return new DataMisalignedException(message); + } + + public static Delegate CreateDelegate(RuntimeTypeHandle typeHandleForDelegate, IntPtr ldftnResult, Object thisObject, bool isStatic, bool isVirtual, bool isOpen) + { + throw new NotSupportedException("CreateDelegate"); + } + + public enum VariableHandleType + { + WeakShort = 0x00000100, + WeakLong = 0x00000200, + Strong = 0x00000400, + Pinned = 0x00000800, + } + + public static void AddExceptionDataForRestrictedErrorInfo(Exception ex, string restrictedError, string restrictedErrorReference, string restrictedCapabilitySid, object restrictedErrorObject) + { + throw new NotSupportedException("AddExceptionDataForRestrictedErrorInfo"); + } + + public static bool TryGetRestrictedErrorObject(Exception ex, out object restrictedErrorObject) + { + throw new NotSupportedException("TryGetRestrictedErrorObject"); + } + + public static bool TryGetRestrictedErrorDetails(Exception ex, out string restrictedError, out string restrictedErrorReference, out string restrictedCapabilitySid) + { + throw new NotSupportedException("TryGetRestrictedErrorDetails"); + } + + public static TypeInitializationException CreateTypeInitializationException(string message) + { + return new TypeInitializationException(message,null); + } + + public unsafe static IntPtr GetObjectID(object obj) + { + throw new NotSupportedException("GetObjectID"); + } + + public static bool RhpETWShouldWalkCom() + { + throw new NotSupportedException("RhpETWShouldWalkCom"); + } + + public static void RhpETWLogLiveCom(int eventType, IntPtr CCWHandle, IntPtr objectID, IntPtr typeRawValue, IntPtr IUnknown, IntPtr VTable, Int32 comRefCount, Int32 jupiterRefCount, Int32 flags) + { + throw new NotSupportedException("RhpETWLogLiveCom"); + } + + public static bool SupportsReflection(this Type type) + { + return true; + } + + public static void SuppressReentrantWaits() + { + // Nop + } + + public static void RestoreReentrantWaits() + { + //Nop + } + + public static IntPtr GetCriticalHandle(CriticalHandle criticalHandle) + { + throw new NotSupportedException("GetCriticalHandle"); + } + + public static void SetCriticalHandle(CriticalHandle criticalHandle, IntPtr handle) + { + throw new NotSupportedException("SetCriticalHandle"); + } + } + + public class GCHelpers + { + public static void RhpSetThreadDoNotTriggerGC() { } + public static void RhpClearThreadDoNotTriggerGC() { } + } +} diff --git a/external/corert/src/System.Private.Interop/src/InteropExtensions/Lock.cs b/external/corert/src/System.Private.Interop/src/InteropExtensions/Lock.cs new file mode 100644 index 0000000000..d86c28f126 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/InteropExtensions/Lock.cs @@ -0,0 +1,25 @@ +// 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; + +namespace System.Threading +{ + /// + /// Simple wrapper around Monitor.Enter and Exit exposing interface as expected by + /// System.Private.InteropServices.__ComObject + /// + public class Lock + { + private object _lock = new object(); + public void Acquire() + { + Monitor.Enter(_lock); + } + public void Release() + { + Monitor.Exit(_lock); + } + } +} diff --git a/external/corert/src/System.Private.Interop/src/InteropExtensions/MetadataTransformedAttribute.cs b/external/corert/src/System.Private.Interop/src/InteropExtensions/MetadataTransformedAttribute.cs new file mode 100644 index 0000000000..b2e49840d6 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/InteropExtensions/MetadataTransformedAttribute.cs @@ -0,0 +1,43 @@ +// 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; + +namespace Internal.Reflection +{ + // This enumeration is a contract with Dependency Reducer ReducerEngine.cs, MdTransform\Metadata.cs, and MCG + [Flags] + public enum MetadataTransformation + { + None = 0x0, + OriginallyNotSealed = 0x1, // A method was originally unsealed, but a transform sealed it + OriginallyVirtual = 0x2, // A method was originally virtual, but a transform devirtualized it + OriginallySealed = 0x4, // A method was originally sealed, but a transform unsealed it + OriginallyNewSlot = 0x8, // A method was originally NewSlot + OriginallyAccessCheckedOnOverride = 0x10, // A method was originally AccessCheckedOnOverride (strict) + + OriginallyForeignObject = 0x20, // A class was originally marked as WindowsRuntime + OriginallyComObject = 0x40 + } + + /// + /// Indicates that a transform has changed metadata and has a flag for the state + /// reflection should show + /// + [System.Runtime.CompilerServices.DependencyReductionRoot] + [AttributeUsage( + AttributeTargets.Method | + AttributeTargets.Class | + AttributeTargets.Enum | + AttributeTargets.Interface | + AttributeTargets.Struct | + AttributeTargets.Delegate, + Inherited = false)] + public sealed class MetadataTransformedAttribute : Attribute + { + public MetadataTransformedAttribute(MetadataTransformation transformation) + { + } + } +} diff --git a/external/corert/src/System.Private.Interop/src/InteropExtensions/MissingInteropDataException.cs b/external/corert/src/System.Private.Interop/src/InteropExtensions/MissingInteropDataException.cs new file mode 100644 index 0000000000..fea2591a89 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/InteropExtensions/MissingInteropDataException.cs @@ -0,0 +1,23 @@ +// 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.Reflection; + +namespace System.Runtime.CompilerServices +{ + /// + /// Thrown when a manual marshalling method is called, but the type was not found + /// by static analysis or in the rd.xml file. + /// + public class MissingInteropDataException : Exception + { + public Type MissingType { get; private set; } + + public MissingInteropDataException(string resourceFormat, Type pertainantType) + : base() + { + MissingType = pertainantType; + } + } +} diff --git a/external/corert/src/System.Private.Interop/src/InteropExtensions/MissingMetadataException.cs b/external/corert/src/System.Private.Interop/src/InteropExtensions/MissingMetadataException.cs new file mode 100644 index 0000000000..8d7d8cfe90 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/InteropExtensions/MissingMetadataException.cs @@ -0,0 +1,27 @@ +// 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: MissingMetadataException +** +==============================================================*/ + +using global::System; + +namespace System.Reflection +{ + public sealed class MissingMetadataException : TypeAccessException + { + public MissingMetadataException() + { + } + + public MissingMetadataException(String message) + : base(message) + { + } + } +} diff --git a/external/corert/src/System.Private.Interop/src/InteropExtensions/NativeCallableAttribute.cs b/external/corert/src/System.Private.Interop/src/InteropExtensions/NativeCallableAttribute.cs new file mode 100644 index 0000000000..54b0c4f8d6 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/InteropExtensions/NativeCallableAttribute.cs @@ -0,0 +1,20 @@ +// 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; +namespace System.Runtime.InteropServices +{ + //BARTOK expects + [AttributeUsage(AttributeTargets.Method)] + public sealed class NativeCallableAttribute : Attribute + { + // Optional. If omitted, then the method is native callable, but no EAT is emitted. + public string EntryPoint; + // Optional. If omitted a default will be chosen by the compiler. + public CallingConvention CallingConvention; + public NativeCallableAttribute() + { + } + } +} diff --git a/external/corert/src/System.Private.Interop/src/InteropExtensions/PInvokeMarshal.cs b/external/corert/src/System.Private.Interop/src/InteropExtensions/PInvokeMarshal.cs new file mode 100644 index 0000000000..46da09e118 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/InteropExtensions/PInvokeMarshal.cs @@ -0,0 +1,208 @@ +// 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.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + /// + /// This PInvokeMarshal class should provide full public Marshal + /// implementation for all things related to P/Invoke marshalling + /// + [CLSCompliant(false)] + public sealed class PInvokeMarshal + { + public static void SaveLastWin32Error() + { + // nop + } + + public static void ClearLastWin32Error() + { + // nop + } + + public static int GetLastWin32Error() + { + return 0; + } + + public static void SetLastWin32Error(int errorCode) + { + // nop + } + + public static IntPtr GetStubForPInvokeDelegate(Delegate del) + { + return IntPtr.Zero; + } + + public static Delegate GetPInvokeDelegateForStub(IntPtr pStub, RuntimeTypeHandle delegateType) + { + return default(Delegate); + } + + public static IntPtr GetCurrentCalleeOpenStaticDelegateFunctionPointer() + { + return IntPtr.Zero; + } + + public static unsafe IntPtr MemAlloc(IntPtr cb) + { + return Marshal.AllocHGlobal(cb); + } + + public static void MemFree(IntPtr hglobal) + { + Marshal.FreeHGlobal(hglobal); + } + + public static unsafe IntPtr MemReAlloc(IntPtr pv, IntPtr cb) + { + return Marshal.ReAllocHGlobal(pv, cb); + } + + public static IntPtr CoTaskMemAlloc(UIntPtr bytes) + { + return Marshal.AllocCoTaskMem((int)bytes); + } + + public static void CoTaskMemFree(IntPtr allocatedMemory) + { + Marshal.FreeCoTaskMem(allocatedMemory); + } + + public static IntPtr CoTaskMemReAlloc(IntPtr pv, IntPtr cb) + { + return Marshal.ReAllocCoTaskMem(pv, (int)cb); + } + + public static T GetCurrentCalleeDelegate() where T : class // constraint can't be System.Delegate + { + return default(T); + } + + public static unsafe void StringBuilderToUnicodeString(System.Text.StringBuilder stringBuilder, ushort* destination) + { + // nop + } + + public static unsafe void UnicodeStringToStringBuilder(ushort* newBuffer, System.Text.StringBuilder stringBuilder) + { + // nop + } + + public static unsafe void StringBuilderToAnsiString(System.Text.StringBuilder stringBuilder, byte* pNative, + bool bestFit, bool throwOnUnmappableChar) + { + // nop + } + + public static unsafe void AnsiStringToStringBuilder(byte* newBuffer, System.Text.StringBuilder stringBuilder) + { + // nop + } + + public static unsafe string AnsiStringToString(byte* pchBuffer) + { + return default(string); + } + + public static unsafe byte* StringToAnsiString(string str, bool bestFit, bool throwOnUnmappableChar) + { + return default(byte*); + } + + public static unsafe void ByValWideCharArrayToAnsiCharArray(char[] managedArray, byte* pNative, int expectedCharCount, + bool bestFit, bool throwOnUnmappableChar) + { + // nop + } + + public static unsafe void ByValAnsiCharArrayToWideCharArray(byte* pNative, char[] managedArray) + { + // nop + } + + public static unsafe void WideCharArrayToAnsiCharArray(char[] managedArray, byte* pNative, bool bestFit, bool throwOnUnmappableChar) + { + // nop + } + + public static unsafe void AnsiCharArrayToWideCharArray(byte* pNative, char[] managedArray) + { + // nop + } + + public static unsafe byte WideCharToAnsiChar(char managedValue, bool bestFit, bool throwOnUnmappableChar) + { + return default(byte); + } + + public static unsafe char AnsiCharToWideChar(byte nativeValue) + { + return default(char); + } + + public static unsafe void StringToByValAnsiString(string str, byte* pNative, int charCount, bool bestFit, bool throwOnUnmappableChar, bool truncate = true) + { + // nop + } + + public static unsafe string ByValAnsiStringToString(byte* pchBuffer, int charCount) + { + return default(string); + } + + public static unsafe int ConvertMultiByteToWideChar(byte* buffer, int ansiLength, char* pWChar, int uniLength) + { + return default(int); + } + + public static unsafe int ConvertWideCharToMultiByte(char* wideCharStr, int wideCharLen, byte* multiByteStr, int multiByteLen) + { + return default(int); + } + + public static unsafe int ConvertWideCharToMultiByte(char* wideCharStr, + int wideCharLen, + byte* multiByteStr, + int multiByteLen, + uint flags, + IntPtr usedDefaultChar) + { + return default(int); + } + + public static unsafe int GetByteCount(char* wStr, int wideStrLen) + { + return default(int); + } + + unsafe public static int GetCharCount(byte* multiByteStr, int multiByteLen) + { + return default(int); + } + + public static unsafe int GetSystemMaxDBCSCharSize() + { + return default(int); + } + + public static unsafe String PtrToStringUni(IntPtr ptr, int len) + { + return default(String); + } + + public static unsafe String PtrToStringUni(IntPtr ptr) + { + return default(String); + } + + public static unsafe void CopyToNative(Array source, int startIndex, IntPtr destination, int length) + { + // nop + } + } +} diff --git a/external/corert/src/System.Private.Interop/src/InteropExtensions/PreInitializedAttribute.cs b/external/corert/src/System.Private.Interop/src/InteropExtensions/PreInitializedAttribute.cs new file mode 100644 index 0000000000..96c83a0e82 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/InteropExtensions/PreInitializedAttribute.cs @@ -0,0 +1,17 @@ +// 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.Threading; + +namespace System.Runtime.CompilerServices +{ + // This attribute should be placed on a static field by the code author to indicate that it is expected to + // be completely pre-initialized by the tool chain. The tool chain will produce an error if the field + // cannot be completely pre-initialized. + [AttributeUsage(AttributeTargets.Field)] + public sealed class PreInitializedAttribute : Attribute + { + } +} diff --git a/external/corert/src/System.Private.Interop/src/Readme.md b/external/corert/src/System.Private.Interop/src/Readme.md new file mode 100644 index 0000000000..62c2c01459 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/Readme.md @@ -0,0 +1,8 @@ +# System.Private.Interop.CoreCLR.csproj + +Builds the CoreCLR specific version of System.Private.Interop which along with MCG for CoreCLR provide WinRT marshalling support for CoreCLR/Mono. + + +# System.Private.Interop.Shared.projitems + +This includes all the compilation units required for building System.Private.Interop for .NET Native, CoreCLR, and CoreRT runtimes. diff --git a/external/corert/src/System.Private.Interop/src/Resources/Strings.resx b/external/corert/src/System.Private.Interop/src/Resources/Strings.resx index 3af5b510fd..a2d51ec829 100644 --- a/external/corert/src/System.Private.Interop/src/Resources/Strings.resx +++ b/external/corert/src/System.Private.Interop/src/Resources/Strings.resx @@ -363,4 +363,7 @@ Unable to find an entry point named '{0}' in DLL '{1}'. - + + 'Type '{0}' is not a delegate type. EventTokenTable may only be used with delegate types.' + + \ No newline at end of file diff --git a/external/corert/src/System.Private.Interop/src/Shared/ClassFactory.cs b/external/corert/src/System.Private.Interop/src/Shared/ClassFactory.cs new file mode 100644 index 0000000000..8de44d99e0 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/Shared/ClassFactory.cs @@ -0,0 +1,70 @@ +// 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. +// ---------------------------------------------------------------------------------- +// Interop library code +// --------------------------------------------------------------------------------- + +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Text; +using System.Runtime; +using Internal.NativeFormat; +using Internal.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + [Guid("4ffdd514-7dec-47cf-a0ad-4971868d8455")] + public unsafe class ClassFactory : IClassFactory + { + private McgModule parent; + private RuntimeTypeHandle classType; + + public ClassFactory(McgModule parent, RuntimeTypeHandle classType) + { + this.parent = parent; + this.classType = classType; + } + + public int CreateInstance(IntPtr pUnkOuter, Guid* riid, IntPtr* ppv) + { + if (pUnkOuter != IntPtr.Zero) + { + // We do not currently support COM aggregation + return Interop.COM.CLASS_E_NOAGGREGATION; + } + + RuntimeTypeHandle interfaceTypeHandle = parent.GetTypeFromGuid(ref *riid); + if (interfaceTypeHandle.Equals(default(RuntimeTypeHandle))) + { + return Interop.COM.E_NOINTERFACE; + } + else + { + object result = InteropExtensions.RuntimeNewObject(classType); + *ppv = McgMarshal.ObjectToComInterface(result, interfaceTypeHandle); + if (*ppv == IntPtr.Zero) + { + return Interop.COM.E_NOINTERFACE; + } + else + { + return Interop.COM.S_OK; + } + } + } + + public int LockServer (int fLock) + { + return Interop.COM.E_NOTIMPL; + } + } +} \ No newline at end of file diff --git a/external/corert/src/System.Private.Interop/src/Shared/ComCallableObject.cs b/external/corert/src/System.Private.Interop/src/Shared/ComCallableObject.cs index 7f5ef41d8e..04ad4e4873 100644 --- a/external/corert/src/System.Private.Interop/src/Shared/ComCallableObject.cs +++ b/external/corert/src/System.Private.Interop/src/Shared/ComCallableObject.cs @@ -23,7 +23,6 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Text; using System.Runtime; -using System.Diagnostics.Contracts; using Internal.NativeFormat; namespace System.Runtime.InteropServices @@ -91,6 +90,11 @@ namespace System.Runtime.InteropServices RuntimeTypeHandle m_interfaceType; // Refer to interface RuntimeTypeHandle // TODO: Define unqiue vtable for shared CCW instances, store McgInterfaceData pointer at negative offset +#if !CORECLR && ENABLE_WINRT + // for dynamic created stub, remove/delete stub during __interface_ccw destroy + int m_dynamicMethodStart; + int m_dynamicMethodEnd; // [m_dynamicMethodStart, m_dynamicMethodEnd) +#endif /// /// Cache for single memory block, perfect for calls like IDependencyProperty.SetValue (System.Object converts to CCW, passed to native code, then released). /// Last block not freed at shut-down. Consider more complicated caching when there are evidence for it @@ -107,11 +111,19 @@ namespace System.Runtime.InteropServices IntPtr vt = typeHandle.GetCcwVtable(); #if !CORECLR && ENABLE_WINRT + int dynamicMethodStart = 0; + int dynamicMethodEnd = 0; + if (vt == default(IntPtr) && McgModuleManager.UseDynamicInterop) { // TODO Design an interface, such as IMcgCCWData and each McgModule implements this interface // Dynamic has its own mcg module - vt = DynamicInteropHelpers.GetCCWVTable_NoThrow(managedCCW.TargetObject, typeHandle); + + vt = DynamicInteropHelpers.GetCCWVTable_NoThrow( + managedCCW.TargetObject, + typeHandle, + out dynamicMethodStart, + out dynamicMethodEnd); } #endif if (vt == default(IntPtr)) @@ -130,7 +142,10 @@ namespace System.Runtime.InteropServices pCcw->m_pVtable = vt.ToPointer(); pCcw->m_pNativeCCW = managedCCW.NativeCCW; pCcw->m_interfaceType = typeHandle; - +#if !CORECLR && ENABLE_WINRT + pCcw->m_dynamicMethodStart = dynamicMethodStart; + pCcw->m_dynamicMethodEnd = dynamicMethodEnd; +#endif managedCCW.NativeCCW->Link(pCcw); return pCcw; @@ -138,6 +153,14 @@ namespace System.Runtime.InteropServices internal static void Destroy(__interface_ccw* pInterfaceCCW) { +#if !CORECLR && ENABLE_WINRT + // Remove these dynamic thunk [m_dynamicMethodStart, m_dynamicMethodEnd) + for (int i = pInterfaceCCW->m_dynamicMethodStart; i < pInterfaceCCW->m_dynamicMethodEnd; i++) + { + System.IntPtr thunkAddress = ((System.IntPtr*)pInterfaceCCW->m_pVtable)[i]; + InteropCallInterceptor.FreeThunk(thunkAddress); + } +#endif McgComHelpers.CachedFree(pInterfaceCCW, ref s_cached_interface_ccw); } @@ -1086,7 +1109,7 @@ namespace System.Runtime.InteropServices internal static void InitRefCountedHandleCallback() { // TODO: -#if !CORERT +#if PROJECTN || CORECLR // // Register the callback to ref-counted handles // Inside this callback we'll determine whether the ref count handle to the target object @@ -1459,7 +1482,7 @@ namespace System.Runtime.InteropServices } } -#if !RHTESTCL && !CORECLR && !CORERT && ENABLE_WINRT +#if !RHTESTCL && PROJECTN && ENABLE_WINRT // TODO: Dynamic Boxing support // TODO: Optimize--it is possible that the following dynamic code is faster than above static code(CCWTemplate) // if we cann't find a interfaceType for giving guid, try to enumerate all interfaces implemented by this target object to see anyone matchs given guid diff --git a/external/corert/src/System.Private.Interop/src/Shared/ComException.cs b/external/corert/src/System.Private.Interop/src/Shared/ComException.cs index c2720c50c4..0d78808a81 100644 --- a/external/corert/src/System.Private.Interop/src/Shared/ComException.cs +++ b/external/corert/src/System.Private.Interop/src/Shared/ComException.cs @@ -20,8 +20,8 @@ using System.Globalization; namespace System.Runtime.InteropServices { // Exception for COM Interop errors where we don't recognize the HResult. - // [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class COMException : ExternalException { internal COMException(int hr) diff --git a/external/corert/src/System.Private.Interop/src/Shared/ComInterop.cs b/external/corert/src/System.Private.Interop/src/Shared/ComInterop.cs index b73effc41b..67439d8c4d 100644 --- a/external/corert/src/System.Private.Interop/src/Shared/ComInterop.cs +++ b/external/corert/src/System.Private.Interop/src/Shared/ComInterop.cs @@ -21,7 +21,6 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Text; using System.Runtime; -using System.Diagnostics.Contracts; using Internal.NativeFormat; namespace System.Runtime.InteropServices diff --git a/external/corert/src/System.Private.Interop/src/Shared/Dictionary.cs b/external/corert/src/System.Private.Interop/src/Shared/Dictionary.cs index dcd93fb269..d2750fa17e 100644 --- a/external/corert/src/System.Private.Interop/src/Shared/Dictionary.cs +++ b/external/corert/src/System.Private.Interop/src/Shared/Dictionary.cs @@ -6,7 +6,6 @@ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Threading; @@ -125,8 +124,6 @@ namespace System.Collections.Generic.Internal { get { - Contract.Ensures(Contract.Result() != null); - return new KeyCollection(this); } } @@ -135,8 +132,6 @@ namespace System.Collections.Generic.Internal { get { - Contract.Ensures(Contract.Result() != null); - return new ValueCollection(this); } } diff --git a/external/corert/src/System.Private.Interop/src/Shared/DictionaryBase.cs b/external/corert/src/System.Private.Interop/src/Shared/DictionaryBase.cs index 18337dfe51..fd2c3fb937 100644 --- a/external/corert/src/System.Private.Interop/src/Shared/DictionaryBase.cs +++ b/external/corert/src/System.Private.Interop/src/Shared/DictionaryBase.cs @@ -6,7 +6,6 @@ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace System.Collections.Generic.Internal diff --git a/external/corert/src/System.Private.Interop/src/Shared/HashSet.cs b/external/corert/src/System.Private.Interop/src/Shared/HashSet.cs index d115c3e554..83267003ef 100644 --- a/external/corert/src/System.Private.Interop/src/Shared/HashSet.cs +++ b/external/corert/src/System.Private.Interop/src/Shared/HashSet.cs @@ -6,7 +6,6 @@ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Threading; @@ -159,8 +158,6 @@ namespace System.Collections.Generic.Internal { get { - Contract.Ensures(Contract.Result() != null); - return new KeyCollection(this); } } diff --git a/external/corert/src/System.Private.Interop/src/Shared/IClassFactory.cs b/external/corert/src/System.Private.Interop/src/Shared/IClassFactory.cs new file mode 100644 index 0000000000..cf02d761a4 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/Shared/IClassFactory.cs @@ -0,0 +1,36 @@ +// 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. +// ---------------------------------------------------------------------------------- +// Interop library code +// --------------------------------------------------------------------------------- + +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Text; +using System.Runtime; +using Internal.NativeFormat; +using Internal.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + [ComImport] + [Guid("00000001-0000-0000-C000-000000000046")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public unsafe interface IClassFactory + { + [PreserveSig] + int CreateInstance(IntPtr pUnkOuter, Guid* riid, IntPtr* ppvObject); + + [PreserveSig] + int LockServer (int fLock); + } +} \ No newline at end of file diff --git a/external/corert/src/System.Private.Interop/src/Shared/Interop.Manual.cs b/external/corert/src/System.Private.Interop/src/Shared/Interop.Manual.cs index 38ee557c6b..fe0861c0db 100644 --- a/external/corert/src/System.Private.Interop/src/Shared/Interop.Manual.cs +++ b/external/corert/src/System.Private.Interop/src/Shared/Interop.Manual.cs @@ -85,6 +85,24 @@ partial class Interop internal int hr; } + [StructLayout(LayoutKind.Sequential)] + internal struct COSERVERINFO + { + internal int Reserved1; + internal IntPtr Name; + internal IntPtr AuthInfo; + internal int Reserved2; + } + + [Flags] + internal enum CLSCTX : int + { + CLSCTX_INPROC_SERVER = 0x1, + CLSCTX_LOCAL_SERVER = 0x4, + CLSCTX_REMOTE_SERVER = 0x10, + CLSCTX_SERVER = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER + } + [MethodImplAttribute(MethodImplOptions.NoInlining)] static unsafe internal string ConvertBSTRToString(IntPtr pBSTR) { @@ -144,6 +162,9 @@ partial class Interop internal const int TYPE_E_TYPEMISMATCH = unchecked((int)0x80028CA0); internal const int DISP_E_OVERFLOW = unchecked((int)0x8002000A); + internal const int CLASS_E_NOAGGREGATION = unchecked((int)0x80040110); + internal const int CLASS_E_CLASSNOTAVAILABLE = unchecked((int)0x80040111); + /// /// Error indicates that you are accessing a CCW whose target object has already been garbage /// collected while the CCW still has non-0 jupiter ref counts diff --git a/external/corert/src/System.Private.Interop/src/Shared/List.cs b/external/corert/src/System.Private.Interop/src/Shared/List.cs index d3b612c0f2..7f214a215f 100644 --- a/external/corert/src/System.Private.Interop/src/Shared/List.cs +++ b/external/corert/src/System.Private.Interop/src/Shared/List.cs @@ -6,7 +6,6 @@ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace System.Collections.Generic.Internal @@ -29,7 +28,6 @@ namespace System.Collections.Generic.Internal public List(int capacity) { if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); _items = new T[capacity]; } @@ -42,7 +40,6 @@ namespace System.Collections.Generic.Internal { if (collection == null) throw new ArgumentNullException(nameof(collection)); - Contract.EndContractBlock(); ICollection c = collection as ICollection; @@ -86,7 +83,6 @@ namespace System.Collections.Generic.Internal { get { - Contract.Ensures(Contract.Result() >= 0); return _items.Length; } set @@ -96,8 +92,6 @@ namespace System.Collections.Generic.Internal throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_SmallCapacity); } - Contract.EndContractBlock(); - if (value != _items.Length) { if (value > 0) @@ -119,7 +113,6 @@ namespace System.Collections.Generic.Internal { get { - Contract.Ensures(Contract.Result() >= 0); return _size; } } @@ -136,8 +129,6 @@ namespace System.Collections.Generic.Internal throw new ArgumentOutOfRangeException(); } - Contract.EndContractBlock(); - return _items[index]; } @@ -148,8 +139,6 @@ namespace System.Collections.Generic.Internal throw new ArgumentOutOfRangeException(); } - Contract.EndContractBlock(); - _items[index] = value; _version++; } @@ -224,7 +213,6 @@ namespace System.Collections.Generic.Internal { throw new ArgumentException(SR.Argument_InvalidOffLen); } - Contract.EndContractBlock(); // Delegate rest of error checking to Array.Copy. Array.Copy(_items, index, array, arrayIndex, count); @@ -268,9 +256,6 @@ namespace System.Collections.Generic.Internal // public int IndexOf(T item) { - Contract.Ensures(Contract.Result() >= -1); - Contract.Ensures(Contract.Result() < Count); - return Array.IndexOf(_items, item, 0, _size); } @@ -288,10 +273,6 @@ namespace System.Collections.Generic.Internal if (index > _size) throw new ArgumentOutOfRangeException(nameof(index)); - Contract.Ensures(Contract.Result() >= -1); - Contract.Ensures(Contract.Result() < Count); - Contract.EndContractBlock(); - return Array.IndexOf(_items, item, index, _size - index); } @@ -312,10 +293,6 @@ namespace System.Collections.Generic.Internal if (count < 0 || index > _size - count) throw new ArgumentOutOfRangeException(nameof(count)); - Contract.Ensures(Contract.Result() >= -1); - Contract.Ensures(Contract.Result() < Count); - Contract.EndContractBlock(); - return Array.IndexOf(_items, item, index, count); } @@ -330,7 +307,6 @@ namespace System.Collections.Generic.Internal { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); if (_size == _items.Length) EnsureCapacity(_size + 1); @@ -369,7 +345,6 @@ namespace System.Collections.Generic.Internal { throw new ArgumentOutOfRangeException(nameof(index)); } - Contract.EndContractBlock(); _size--; @@ -384,9 +359,6 @@ namespace System.Collections.Generic.Internal public T[] ToArray() { - Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().Length == Count); - T[] array = new T[_size]; Array.Copy(_items, 0, array, 0, _size); diff --git a/external/corert/src/System.Private.Interop/src/Shared/McgComHelpers.cs b/external/corert/src/System.Private.Interop/src/Shared/McgComHelpers.cs index 7812320113..90de9b3ea5 100644 --- a/external/corert/src/System.Private.Interop/src/Shared/McgComHelpers.cs +++ b/external/corert/src/System.Private.Interop/src/Shared/McgComHelpers.cs @@ -22,7 +22,6 @@ using System.Runtime.InteropServices; using System.Threading; using System.Text; using System.Runtime; -using System.Diagnostics.Contracts; using Internal.NativeFormat; using System.Runtime.CompilerServices; @@ -35,7 +34,7 @@ namespace System.Runtime.InteropServices /// internal static string GetRuntimeClassName(Object obj) { -#if ENABLE_WINRT +#if ENABLE_MIN_WINRT System.IntPtr pWinRTItf = default(IntPtr); try @@ -60,7 +59,7 @@ namespace System.Runtime.InteropServices /// internal static string GetRuntimeClassName(IntPtr pWinRTItf) { -#if ENABLE_WINRT +#if ENABLE_MIN_WINRT void* unsafe_hstring = null; try @@ -93,7 +92,7 @@ namespace System.Runtime.InteropServices Interop.COM.__IStream* pStreamNativePtr = (Interop.COM.__IStream*)(void*)pStream; UInt64 newPosition; - int hr = CalliIntrinsics.StdCall( + int hr = CalliIntrinsics.StdCall__int( pStreamNativePtr->vtbl->pfnSeek, pStreamNativePtr, 0UL, @@ -110,7 +109,7 @@ namespace System.Runtime.InteropServices Interop.COM.__IStream* pStreamNativePtr = (Interop.COM.__IStream*)(void*)pStream; UInt64 newPosition; - int hr = CalliIntrinsics.StdCall( + int hr = CalliIntrinsics.StdCall__int( pStreamNativePtr->vtbl->pfnSetSize, pStreamNativePtr, lSize, @@ -230,14 +229,6 @@ namespace System.Runtime.InteropServices } } - /// - /// Return true if the object is a RCW. False otherwise - /// - internal static bool IsComObject(object obj) - { - return (obj is __ComObject); - } - /// /// Unwrap if this is a managed wrapper /// Typically used in data binding @@ -619,7 +610,7 @@ namespace System.Runtime.InteropServices internal static __ComGenericInterfaceDispatcher CreateGenericComDispatcher(RuntimeTypeHandle genericDispatcherDef, RuntimeTypeHandle[] genericArguments, __ComObject comThisPointer) { -#if !RHTESTCL && !CORECLR && !CORERT +#if !RHTESTCL && PROJECTN Debug.Assert(genericDispatcherDef.IsGenericTypeDefinition()); Debug.Assert(genericArguments != null && genericArguments.Length > 0); diff --git a/external/corert/src/System.Private.Interop/src/Shared/McgData.cs b/external/corert/src/System.Private.Interop/src/Shared/McgData.cs index 40b9e9deb9..de7777904b 100644 --- a/external/corert/src/System.Private.Interop/src/Shared/McgData.cs +++ b/external/corert/src/System.Private.Interop/src/Shared/McgData.cs @@ -603,6 +603,11 @@ namespace System.Runtime.InteropServices /// hierarchy and also know which are the ones implemented by managed class /// public bool IsWinRTType; + + /// + /// The clsid of the COM class, or Guid.Empty if not available + /// + public Guid Clsid; } } diff --git a/external/corert/src/System.Private.Interop/src/Shared/McgHelpers.cs b/external/corert/src/System.Private.Interop/src/Shared/McgHelpers.cs index ddabcc53d8..d6d8caac6c 100644 --- a/external/corert/src/System.Private.Interop/src/Shared/McgHelpers.cs +++ b/external/corert/src/System.Private.Interop/src/Shared/McgHelpers.cs @@ -151,7 +151,7 @@ namespace System.Runtime.InteropServices break; default: - Debug.Assert(false, "IListThunk wrong oper"); + Debug.Fail("IListThunk wrong oper"); break; } @@ -213,7 +213,7 @@ namespace System.Runtime.InteropServices break; default: - Debug.Assert(false, "IListBlittableThunk wrong oper"); + Debug.Fail("IListBlittableThunk wrong oper"); break; } @@ -269,7 +269,7 @@ namespace System.Runtime.InteropServices break; default: - Debug.Assert(false, "IReadOnlyListThunk wrong oper"); + Debug.Fail("IReadOnlyListThunk wrong oper"); break; } @@ -311,7 +311,7 @@ namespace System.Runtime.InteropServices break; default: - Debug.Assert(false, "IReadOnlyListThunk wrong oper"); + Debug.Fail("IReadOnlyListThunk wrong oper"); break; } @@ -1700,7 +1700,7 @@ namespace System.Runtime.InteropServices *((uint*)pCount) = (uint)count; - InteropExtensions.CopyToNative(data, 0, pDest, count); + PInvokeMarshal.CopyToNative(data, 0, pDest, count); } catch (System.Exception hrExcep) { diff --git a/external/corert/src/System.Private.Interop/src/Shared/McgIntrinsics.cs b/external/corert/src/System.Private.Interop/src/Shared/McgIntrinsics.cs index cf87beaccc..6c18ea1471 100644 --- a/external/corert/src/System.Private.Interop/src/Shared/McgIntrinsics.cs +++ b/external/corert/src/System.Private.Interop/src/Shared/McgIntrinsics.cs @@ -21,7 +21,6 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Text; using System.Runtime; -using System.Diagnostics.Contracts; using Internal.NativeFormat; namespace System.Runtime.InteropServices @@ -187,10 +186,10 @@ namespace System.Runtime.InteropServices void * arg7) { // This method is implemented elsewhere in the toolchain - return 0; + return default(int); } - internal static T StdCall( + internal static int StdCall__int( System.IntPtr pfn, void* pComThis, ulong arg0, @@ -198,9 +197,9 @@ namespace System.Runtime.InteropServices void* arg2) { // This method is implemented elsewhere in the toolchain - return default(T); + return 0; } - internal static T StdCall( + internal static int StdCall__int( System.IntPtr pfn, void* pComThis, IntPtr arg0, @@ -210,50 +209,44 @@ namespace System.Runtime.InteropServices IntPtr arg4) { // This method is implemented elsewhere in the toolchain - return default(T); + return 0; } - internal static T StdCall( + internal static int StdCall__int( System.IntPtr pfn, uint arg0) { // This method is implemented elsewhere in the toolchain - return default(T); + return default(int); } - internal static T StdCall( + internal static int StdCall__int( System.IntPtr pfn, void* arg0) { // This method is implemented elsewhere in the toolchain - return default(T); + return default(int); } - internal static T StdCall( - System.IntPtr pfn, - void* arg0, - void* arg1) - { - // This method is implemented elsewhere in the toolchain - return default(T); - } - internal static T StdCall( + + internal static int StdCall__int( System.IntPtr pfn, void* arg0, uint arg1, void* arg2) { // This method is implemented elsewhere in the toolchain - return default(T); + return default(int); } - internal static T StdCall( + internal static int StdCall__int( System.IntPtr pfn, void* arg0, void* arg1, void* arg2) { // This method is implemented elsewhere in the toolchain - return default(T); + return default(int); } - internal static T StdCall( + + internal static int StdCall__int( System.IntPtr pfn, void* arg0, uint arg1, @@ -261,18 +254,19 @@ namespace System.Runtime.InteropServices void* arg3) { // This method is implemented elsewhere in the toolchain - return default(T); + return default(int); } - internal static T StdCall( + internal static int StdCall__int( System.IntPtr pfn, int hr, void* errorMsg, System.IntPtr pUnk) { // This method is implemented elsewhere in the toolchain - return default(T); + return default(int); } - internal static T StdCall( + + internal static int StdCall__int( System.IntPtr pfn, System.IntPtr pComThis, out System.IntPtr arg1, @@ -285,18 +279,19 @@ namespace System.Runtime.InteropServices arg3 = default(IntPtr); arg4 = default(IntPtr); arg2 = 0; - return default(T); + return default(int); } - internal static T StdCall( + internal static int StdCall__int( System.IntPtr pfn, System.IntPtr pComThis, out System.IntPtr arg) { // This method is implemented elsewhere in the toolchain arg = default(IntPtr); - return default(T); + return default(int); } - internal static T StdCall( + + internal static int StdCall__int( System.IntPtr pfn, System.IntPtr pComThis, System.Guid arg1, @@ -304,10 +299,10 @@ namespace System.Runtime.InteropServices { // This method is implemented elsewhere in the toolchain arg2 = default(IntPtr); - return default(T); + return default(int); } - internal static T StdCall( + internal static int StdCall__int( IntPtr pfn, void* pComThis, IntPtr piid, @@ -318,10 +313,10 @@ namespace System.Runtime.InteropServices IntPtr pclsid) { // This method is implemented elsewhere in the toolchain - return default(T); + return default(int); } - internal static T StdCall( + internal static int StdCall__int( IntPtr pfn, void* pComThis, IntPtr pStm, @@ -332,10 +327,10 @@ namespace System.Runtime.InteropServices int mshlflags) { // This method is implemented elsewhere in the toolchain - return default(T); + return default(int); } - internal static T StdCall( + internal static int StdCall__int( IntPtr pfn, void* pComThis, IntPtr pStm, @@ -343,25 +338,25 @@ namespace System.Runtime.InteropServices IntPtr ppvObj) { // This method is implemented elsewhere in the toolchain - return default(T); + return default(int); } - internal static T StdCall( + internal static int StdCall__int( IntPtr pfn, void* pComThis, IntPtr pStm) { // This method is implemented elsewhere in the toolchain - return default(T); + return default(int); } - internal static T StdCall( + internal static int StdCall__int( IntPtr pfn, void* pComThis, int dwReserved) { // This method is implemented elsewhere in the toolchain - return default(T); + return default(int); } private const MethodImplOptions InternalCall = (MethodImplOptions)0x1000; @@ -370,7 +365,7 @@ namespace System.Runtime.InteropServices // We need to call via an imported stub to do a stdcall without triggering GC // We can't use managed calli because the native target address could potentially satisfy a magic // bit check and causing the stub to believe it is a managed method which leads to crash. -#if !RHTESTCL && !CORECLR && !CORERT +#if !RHTESTCL && PROJECTN [MethodImplAttribute(InternalCall)] #if X86 [RuntimeImport("*", "@StdCallCOOP0@8")] @@ -492,7 +487,7 @@ namespace System.Runtime.InteropServices return default(T); } -#if ENABLE_WINRT +#if ENABLE_MIN_WINRT // For SharedCCW_IVector/SharedCCW_IVectorView internal static T Call(IntPtr pfn, object list, Toolbox.IList_Oper oper, int index, ref object item) { @@ -510,8 +505,7 @@ namespace System.Runtime.InteropServices return default(T); } -#if !RHTESTCL && !CORECLR - +#if ENABLE_WINRT // For SharedCcw_AsyncOperationCompletedHandler internal static T Call(IntPtr pfn, object handler, object asyncInfo, global::Windows.Foundation.AsyncStatus status) { @@ -523,7 +517,7 @@ namespace System.Runtime.InteropServices { return default(T); } -#endif // ENABLE_WINRT +#endif // ENABLE_MIN_WINRT // For ForwardDelegateCreationStub internal static Delegate Call__Delegate(System.IntPtr pfn, System.IntPtr pStub) { @@ -612,7 +606,7 @@ namespace System.Runtime.InteropServices IntPtr puArgErr); // IStream - internal delegate int AddrOfIStreamClone(IntPtr pComThis, out IntPtr ppstm); + internal delegate int AddrOfIStreamClone(IntPtr pComThis, IntPtr ppstm); internal delegate int AddrOfIStreamCopyTo(IntPtr pComThis, IntPtr pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten); internal delegate int AddrOfIStreamLockRegion(IntPtr pComThis, long libOffset, long cb, int dwLockType); internal delegate int AddrOfIStreamRead(IntPtr pComThis, IntPtr pv, int cb, IntPtr pcbRead); @@ -628,7 +622,7 @@ namespace System.Runtime.InteropServices #endif } -#if !CORECLR && ENABLE_WINRT +#if ENABLE_MIN_WINRT [McgIntrinsics] internal class WinRTAddrOfIntrinsics { @@ -636,7 +630,7 @@ namespace System.Runtime.InteropServices internal delegate int AddrOfGetIndexedProperty(System.IntPtr pComThis, HSTRING unsafe_name, TypeName unsafe_type, IntPtr __IntPtr__unsafe_customProperty); internal delegate int AddrOfTarget19(IntPtr p0, IntPtr p1, int p2); } -#endif // !CORECLR && ENABLE_WINRT +#endif // ENABLE_MIN_WINRT public delegate IntPtr AddrOfGetCCWVtable(); public delegate int AddrOfRelease(IntPtr pComThis); diff --git a/external/corert/src/System.Private.Interop/src/Shared/McgMarshal.cs b/external/corert/src/System.Private.Interop/src/Shared/McgMarshal.cs index 4e937b77bc..bf51b8d352 100644 --- a/external/corert/src/System.Private.Interop/src/Shared/McgMarshal.cs +++ b/external/corert/src/System.Private.Interop/src/Shared/McgMarshal.cs @@ -22,7 +22,6 @@ using System.Runtime.InteropServices; using System.Threading; using System.Text; using System.Runtime; -using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using Internal.NativeFormat; @@ -49,6 +48,11 @@ namespace System.Runtime.InteropServices PInvokeMarshal.SaveLastWin32Error(); } + public static void ClearLastWin32Error() + { + PInvokeMarshal.ClearLastWin32Error(); + } + public static bool GuidEquals(ref Guid left, ref Guid right) { return InteropExtensions.GuidEquals(ref left, ref right); @@ -73,15 +77,26 @@ namespace System.Runtime.InteropServices #endif } - public static bool IsCOMObject(Type type) + /// + /// Return true if the type is __COM or derived from __COM. False otherwise + /// + public static bool IsComObject(Type type) { #if RHTESTCL return false; #else - return type.GetTypeInfo().IsSubclassOf(typeof(__ComObject)); + return type == typeof(__ComObject) || type.GetTypeInfo().IsSubclassOf(typeof(__ComObject)); #endif } + /// + /// Return true if the object is a RCW. False otherwise + /// + internal static bool IsComObject(object obj) + { + return (obj is __ComObject); + } + public static T FastCast(object value) where T : class { // We have an assert here, to verify that a "real" cast would have succeeded. @@ -351,7 +366,7 @@ namespace System.Runtime.InteropServices } #endif -#if ENABLE_WINRT +#if ENABLE_MIN_WINRT [MethodImplAttribute(MethodImplOptions.NoInlining)] public static unsafe HSTRING StringToHString(string sourceString) @@ -392,11 +407,11 @@ namespace System.Runtime.InteropServices return hr; } } -#endif //ENABLE_WINRT +#endif //ENABLE_MIN_WINRT #endregion -#region COM marshalling + #region COM marshalling /// /// Explicit AddRef for RCWs @@ -687,10 +702,7 @@ namespace System.Runtime.InteropServices if (typeHnd.IsComClass()) { - Debug.Assert(obj == null || obj is __ComObject); - /// - /// This code path should be executed only for WinRT classes - /// + // This code path should be executed only for WinRT classes typeHnd = typeHnd.GetDefaultInterface(); Debug.Assert(!typeHnd.IsNull()); } @@ -867,7 +879,7 @@ namespace System.Runtime.InteropServices #endif } - public static unsafe IntPtr CreateInstanceFromApp(Guid clsid) + public static unsafe IntPtr CoCreateInstanceEx(Guid clsid, string server) { #if ENABLE_WINRT Interop.COM.MULTI_QI results; @@ -879,7 +891,26 @@ namespace System.Runtime.InteropServices results.pIID = new IntPtr(pIID); results.pItf = IntPtr.Zero; results.hr = 0; - int hr = ExternalInterop.CoCreateInstanceFromApp(pClsid, IntPtr.Zero, 0x15 /* (CLSCTX_SERVER) */, IntPtr.Zero, 1, pResults); + int hr; + + // if server name is specified, do remote server activation + if (!String.IsNullOrEmpty(server)) + { + Interop.COM.COSERVERINFO serverInfo; + fixed (char* pName = server) + { + serverInfo.Name = new IntPtr(pName); + IntPtr pServerInfo = new IntPtr(&serverInfo); + + hr = ExternalInterop.CoCreateInstanceFromApp(pClsid, IntPtr.Zero, (int)Interop.COM.CLSCTX.CLSCTX_REMOTE_SERVER, pServerInfo, 1, pResults); + } + + } + else + { + hr = ExternalInterop.CoCreateInstanceFromApp(pClsid, IntPtr.Zero, (int)Interop.COM.CLSCTX.CLSCTX_SERVER, IntPtr.Zero, 1, pResults); + } + if (hr < 0) { throw McgMarshal.GetExceptionForHR(hr, /*isWinRTScenario = */ false); @@ -888,16 +919,21 @@ namespace System.Runtime.InteropServices { throw McgMarshal.GetExceptionForHR(results.hr, /* isWinRTScenario = */ false); } - return results.pItf; + return results.pItf; } #else - throw new PlatformNotSupportedException("CreateInstanceFromApp"); + throw new PlatformNotSupportedException("CoCreateInstanceEx"); #endif + } -#endregion + public static unsafe IntPtr CoCreateInstanceEx(Guid clsid) + { + return CoCreateInstanceEx(clsid, string.Empty); + } + #endregion -#region Testing + #region Testing /// /// Internal-only method to allow testing of apartment teardown code @@ -945,7 +981,7 @@ namespace System.Runtime.InteropServices /// If so, it means that this exception was actually caused by a native exception in which case we do simply use the same /// message and stacktrace. /// b. If not, this is actually a managed exception and in this case we RoOriginateLanguageException with the msg, hresult and the IErrorInfo - /// aasociated with the managed exception. This helps us to retrieve the same exception in case it comes back to native. + /// associated with the managed exception. This helps us to retrieve the same exception in case it comes back to native. /// 2. On win8 and for classic COM scenarios. /// a. We create IErrorInfo for the given Exception object and SetErrorInfo with the given IErrorInfo. /// @@ -1036,7 +1072,7 @@ namespace System.Runtime.InteropServices IntPtr pResult = default(IntPtr); - int hr = CalliIntrinsics.StdCall( + int hr = CalliIntrinsics.StdCall__int( pIActivationFactoryInternal->pVtable->pfnActivateInstance, pIActivationFactoryInternal, &pResult @@ -1114,10 +1150,10 @@ namespace System.Runtime.InteropServices /// public static IntPtr GetCurrentCalleeOpenStaticDelegateFunctionPointer() { -#if RHTESTCL || CORECLR || CORERT - throw new NotSupportedException(); -#else +#if !RHTESTCL && PROJECTN return PInvokeMarshal.GetCurrentCalleeOpenStaticDelegateFunctionPointer(); +#else + throw new NotSupportedException(); #endif } @@ -1126,10 +1162,10 @@ namespace System.Runtime.InteropServices /// public static T GetCurrentCalleeDelegate() where T : class // constraint can't be System.Delegate { -#if RHTESTCL || CORECLR || CORERT - throw new NotSupportedException(); -#else +#if !RHTESTCL && PROJECTN return PInvokeMarshal.GetCurrentCalleeDelegate(); +#else + throw new NotSupportedException(); #endif } #endregion diff --git a/external/corert/src/System.Private.Interop/src/Shared/McgMethodNameAttribute.cs b/external/corert/src/System.Private.Interop/src/Shared/McgMethodNameAttribute.cs deleted file mode 100644 index 3fc99c4795..0000000000 --- a/external/corert/src/System.Private.Interop/src/Shared/McgMethodNameAttribute.cs +++ /dev/null @@ -1,22 +0,0 @@ -// 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; - -namespace System.Runtime.InteropServices -{ - /// - /// In the case of a WinRT name conflict, methods are renamed to something like the following: - /// IObservableVector`1.GetAt - /// C# obviously doesn't like this syntax. In MCG, we had to choose a different name, encode the real - /// name in this attribute, and later rename the method in a IL transform - /// - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] - public sealed class McgMethodNameAttribute : Attribute - { - public McgMethodNameAttribute(string realName) - { - } - } -} diff --git a/external/corert/src/System.Private.Interop/src/Shared/McgModule.cs b/external/corert/src/System.Private.Interop/src/Shared/McgModule.cs index 3c41f3f203..69bc6aa7af 100644 --- a/external/corert/src/System.Private.Interop/src/Shared/McgModule.cs +++ b/external/corert/src/System.Private.Interop/src/Shared/McgModule.cs @@ -25,7 +25,6 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Text; using System.Runtime; -using System.Diagnostics.Contracts; using Internal.NativeFormat; using Internal.Runtime.CompilerServices; @@ -143,7 +142,7 @@ namespace System.Runtime.InteropServices { if (intfHashSet.Add(new EquatableRuntimeTypeHandle(typeHnd), typeHnd.GetHashCode())) { - Debug.Assert(false, "Duplicate RuntimeTypeHandle found in m_interfaceData"); + Debug.Fail("Duplicate RuntimeTypeHandle found in m_interfaceData"); } } } @@ -160,7 +159,7 @@ namespace System.Runtime.InteropServices { if (classHashSet.Add(new EquatableRuntimeTypeHandle(typeHnd), typeHnd.GetHashCode())) { - Debug.Assert(false, "Duplicate RuntimeTypeHandle found in m_classData"); + Debug.Fail("Duplicate RuntimeTypeHandle found in m_classData"); } } } @@ -913,6 +912,32 @@ namespace System.Runtime.InteropServices mcgGenericArgumentMarshalInfo = default(McgGenericArgumentMarshalInfo); return false; } + + static Guid s_IID_IClassFactory = new Guid(0x00000001, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); + + public unsafe int DllGetClassObjectImpl(Guid rclsid, Guid riid, IntPtr* ppv) + { + if (riid != s_IID_IClassFactory) + { + // Make sure we generate a CCW for IClassFactory + // IntPtr dummy = Marshal.GetComInterfaceForObject(new ClassFactory(null, typeof(IClassFactory).TypeHandle), typeof(IClassFactory)); + // Marshal.Release(dummy); + return Interop.COM.E_NOINTERFACE; + } + + // TODO: build a index similar as McgModule.m_guidMap + for (int i = 0; i < this.m_ccwTemplateData.Length; i++) + { + if (this.m_ccwTemplateData[i].Clsid == rclsid) + { + ClassFactory classFactory = new ClassFactory(this, this.m_ccwTemplateData[i].ClassType); + *ppv = McgMarshal.ObjectToComInterface(classFactory, typeof(IClassFactory).TypeHandle); + return Interop.COM.S_OK; + } + } + return Interop.COM.CLASS_E_CLASSNOTAVAILABLE; + } + #if ENABLE_WINRT static Guid s_IID_IActivationFactory = new Guid(0x00000035, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); diff --git a/external/corert/src/System.Private.Interop/src/Shared/McgModuleManager.cs b/external/corert/src/System.Private.Interop/src/Shared/McgModuleManager.cs index 4cea22ae15..0c7856dcb7 100644 --- a/external/corert/src/System.Private.Interop/src/Shared/McgModuleManager.cs +++ b/external/corert/src/System.Private.Interop/src/Shared/McgModuleManager.cs @@ -24,7 +24,6 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Text; using System.Runtime; -using System.Diagnostics.Contracts; using Internal.NativeFormat; namespace System.Runtime.InteropServices @@ -681,7 +680,7 @@ namespace System.Runtime.InteropServices } } -#if !RHTESTCL && !CORECLR && !CORERT && ENABLE_WINRT +#if !RHTESTCL && PROJECTN && ENABLE_WINRT // Dynamic boxing support // TODO: Consider to use the field boxingStub for all projected reference types. // TODO: now it is only used for boxing "System.Uri". diff --git a/external/corert/src/System.Private.Interop/src/Shared/McgRemovedType.cs b/external/corert/src/System.Private.Interop/src/Shared/McgRemovedType.cs index f8cced9e1c..54cd2ec7a9 100644 --- a/external/corert/src/System.Private.Interop/src/Shared/McgRemovedType.cs +++ b/external/corert/src/System.Private.Interop/src/Shared/McgRemovedType.cs @@ -12,7 +12,7 @@ namespace System.Runtime.InteropServices { public McgRemovedType() { - Debug.Assert(false, "A type that was removed by MCG dependency reduction has been instantiated."); + Debug.Fail("A type that was removed by MCG dependency reduction has been instantiated."); throw new Exception(SR.Arg_RemovedTypeInstantiated); } } diff --git a/external/corert/src/System.Private.Interop/src/Shared/McgTypeHelpers.cs b/external/corert/src/System.Private.Interop/src/Shared/McgTypeHelpers.cs index 64e0681285..a6e80880c6 100644 --- a/external/corert/src/System.Private.Interop/src/Shared/McgTypeHelpers.cs +++ b/external/corert/src/System.Private.Interop/src/Shared/McgTypeHelpers.cs @@ -23,10 +23,14 @@ using System.Runtime.InteropServices; using System.Threading; using System.Text; using System.Runtime; -using System.Diagnostics.Contracts; using Internal.NativeFormat; using System.Runtime.CompilerServices; +#if !RHTESTCL && PROJECTN +using Internal.Runtime.Augments; +using Internal.Runtime.TypeLoader; +#endif + namespace System.Runtime.InteropServices { internal static class McgTypeHelpers @@ -181,6 +185,15 @@ namespace System.Runtime.InteropServices return false; } + public override bool Equals(Type o) + { + // + // We guarantee uniqueness in Mcg marshalling code + // + + return Object.ReferenceEquals(this, o); + } + public override int GetHashCode() { return _fullTypeName.GetHashCode(); @@ -308,7 +321,7 @@ namespace System.Runtime.InteropServices return; } -#if !RHTESTCL && !CORECLR && !CORERT && ENABLE_WINRT +#if !RHTESTCL && PROJECTN && ENABLE_WINRT if (McgModuleManager.UseDynamicInterop) { name = DynamicInteropTypeHelper.GetTypeName(typeHandle, out isWinRT); @@ -442,7 +455,7 @@ namespace System.Runtime.InteropServices bool isWinRT; Type type = McgModuleManager.GetTypeFromName(name, out isWinRT); -#if !RHTESTCL && !CORECLR && !CORERT +#if !RHTESTCL && PROJECTN if (type == null && McgModuleManager.UseDynamicInterop && nativeTypeKind == (int)TypeKind.Metadata) { type = DynamicInteropTypeHelper.GetTypeFromWinRTName(name, nativeTypeKind); @@ -822,10 +835,12 @@ namespace System.Runtime.InteropServices internal static bool IsComClass(this RuntimeTypeHandle handle) { + //From Interop point, Delegates aren't treated as Class #if CORECLR - return InteropExtensions.IsClass(handle); + return InteropExtensions.IsClass(handle) && + !InteropExtensions.AreTypesAssignable(handle, typeof(Delegate).TypeHandle); #else - return !InteropExtensions.IsInterface(handle) && + return !InteropExtensions.IsInterface(handle) && !handle.IsValueType() && !InteropExtensions.AreTypesAssignable(handle, typeof(Delegate).TypeHandle); #endif @@ -879,7 +894,7 @@ namespace System.Runtime.InteropServices return !McgModuleManager.GetInterfaceDataByIndex(moduleIndex, interfaceIndex).DynamicAdapterClassType.IsNull(); ; } -#if !RHTESTCL && !CORECLR && !CORERT && ENABLE_WINRT +#if !RHTESTCL && PROJECTN && ENABLE_WINRT if (McgModuleManager.UseDynamicInterop && interfaceType.IsGenericType()) return false; #endif @@ -933,26 +948,16 @@ namespace System.Runtime.InteropServices return default(IntPtr); } static IntPtr[] SharedCCWList = new IntPtr[] { -#if ENABLE_WINRT +#if ENABLE_MIN_WINRT SharedCcw_IVector.GetVtable(), SharedCcw_IVectorView.GetVtable(), SharedCcw_IIterable.GetVtable(), SharedCcw_IIterator.GetVtable(), - -#if RHTESTCL || CORECLR - default(IntPtr), -#else SharedCcw_AsyncOperationCompletedHandler.GetVtable(), -#endif SharedCcw_IVector_Blittable.GetVtable(), SharedCcw_IVectorView_Blittable.GetVtable(), - -#if RHTESTCL || CORECLR - default(IntPtr) -#else SharedCcw_IIterator_Blittable.GetVtable() -#endif -#endif //ENABLE_WINRT +#endif //ENABLE_MIN_WINRT }; internal static IntPtr GetCcwVtable(this RuntimeTypeHandle interfaceType) @@ -1097,6 +1102,13 @@ namespace System.Runtime.InteropServices return mcgGenericArgumentMarshalInfo.IteratorType; } +#if !RHTESTCL && PROJECTN && ENABLE_WINRT + if (McgModuleManager.UseDynamicInterop) + return DynamicInteropTypeHelper.ReplaceOpenGenericTypeInGenericInstantiation( + interfaceType, + typeof(global::Windows.Foundation.Collections.IIterator<>).TypeHandle + ); +#endif return default(RuntimeTypeHandle); } @@ -1108,6 +1120,16 @@ namespace System.Runtime.InteropServices return mcgGenericArgumentMarshalInfo.ElementClassType; } +#if !RHTESTCL && PROJECTN && ENABLE_WINRT + if (McgModuleManager.UseDynamicInterop) + { + RuntimeTypeHandle[] genericTypeArgumentHandles; + RuntimeAugments.GetGenericInstantiation(interfaceType, out genericTypeArgumentHandles); + + if(genericTypeArgumentHandles.Length == 1) + return genericTypeArgumentHandles[0]; + } +#endif return default(RuntimeTypeHandle); } @@ -1119,6 +1141,15 @@ namespace System.Runtime.InteropServices return mcgGenericArgumentMarshalInfo.ElementInterfaceType; } +#if !RHTESTCL && PROJECTN && ENABLE_WINRT + if (McgModuleManager.UseDynamicInterop) + { + RuntimeTypeHandle[] genericTypeArgumentHandles; + RuntimeAugments.GetGenericInstantiation(interfaceType, out genericTypeArgumentHandles); + if(genericTypeArgumentHandles.Length == 1) + return genericTypeArgumentHandles[0].GetDefaultInterface(); + } +#endif return default(RuntimeTypeHandle); } @@ -1130,6 +1161,13 @@ namespace System.Runtime.InteropServices return mcgGenericArgumentMarshalInfo.VectorViewType; } +#if !RHTESTCL && PROJECTN && ENABLE_WINRT + if (McgModuleManager.UseDynamicInterop) + return DynamicInteropTypeHelper.ReplaceOpenGenericTypeInGenericInstantiation( + interfaceType, + typeof(System.Collections.Generic.IReadOnlyList<>).TypeHandle + ); +#endif return default(RuntimeTypeHandle); } @@ -1141,6 +1179,13 @@ namespace System.Runtime.InteropServices return mcgGenericArgumentMarshalInfo.AsyncOperationType; } +#if !RHTESTCL && PROJECTN && ENABLE_WINRT + if (McgModuleManager.UseDynamicInterop) + return DynamicInteropTypeHelper.ReplaceOpenGenericTypeInGenericInstantiation( + interfaceType, + typeof(global::Windows.Foundation.IAsyncOperation<>).TypeHandle + ); +#endif return default(RuntimeTypeHandle); } @@ -1152,6 +1197,15 @@ namespace System.Runtime.InteropServices return (int)mcgGenericArgumentMarshalInfo.ElementSize; } +#if !RHTESTCL && PROJECTN && ENABLE_WINRT + if (McgModuleManager.UseDynamicInterop) + { + RuntimeTypeHandle[] genericTypeArgumentHandles; + RuntimeAugments.GetGenericInstantiation(interfaceType, out genericTypeArgumentHandles); + if (genericTypeArgumentHandles.Length == 1) + return genericTypeArgumentHandles[0].GetValueTypeSize(); + } +#endif return -1; } #endregion @@ -1166,7 +1220,7 @@ namespace System.Runtime.InteropServices string ccwRuntimeClassName; if (McgModuleManager.TryGetCCWRuntimeClassName(ccwType, out ccwRuntimeClassName)) return ccwRuntimeClassName; -#if !RHTESTCL && !CORECLR && !CORERT && ENABLE_WINRT +#if !RHTESTCL && PROJECTN && ENABLE_WINRT if (McgModuleManager.UseDynamicInterop) return DynamicInteropCCWTemplateHelper.GetCCWRuntimeClassName(ccwType); #endif @@ -1277,7 +1331,7 @@ namespace System.Runtime.InteropServices } } -#if !RHTESTCL && !CORECLR && !CORERT && ENABLE_WINRT +#if !RHTESTCL && PROJECTN && ENABLE_WINRT private static void GetIIDsImpl_dynamic(RuntimeTypeHandle typeHandle, System.Collections.Generic.Internal.List iids) { // Enumerate interfaces from itself and its baseclass @@ -1309,7 +1363,7 @@ namespace System.Runtime.InteropServices } // if there isn't any data about this type, just return empty list -#if !RHTESTCL && !CORECLR && !CORERT && ENABLE_WINRT +#if !RHTESTCL && PROJECTN && ENABLE_WINRT if (McgModuleManager.UseDynamicInterop) GetIIDsImpl_dynamic(ccwType, iids); #endif diff --git a/external/corert/src/System.Private.Interop/src/Shared/RCWWalker.cs b/external/corert/src/System.Private.Interop/src/Shared/RCWWalker.cs index 9f1507b3e2..b6a6d75d89 100644 --- a/external/corert/src/System.Private.Interop/src/Shared/RCWWalker.cs +++ b/external/corert/src/System.Private.Interop/src/Shared/RCWWalker.cs @@ -571,7 +571,7 @@ namespace System.Runtime.InteropServices private static unsafe void Initialize(__com_IJupiterObject* pJupiterObject) { IntPtr pGCManager; - int hr = CalliIntrinsics.StdCall(pJupiterObject->pVtable->pfnGetJupiterGCManager, pJupiterObject, &pGCManager); + int hr = CalliIntrinsics.StdCall__int(pJupiterObject->pVtable->pfnGetJupiterGCManager, pJupiterObject, &pGCManager); if (hr >= 0) { // disable warning for ref volatile @@ -599,7 +599,7 @@ namespace System.Runtime.InteropServices // AddRef on IGCManager // __com_IUnknown* pGCManagerUnk = (__com_IUnknown*)pGCManager; - CalliIntrinsics.StdCall(pGCManagerUnk->pVtable->pfnAddRef, pGCManager); + CalliIntrinsics.StdCall__int(pGCManagerUnk->pVtable->pfnAddRef, pGCManager); s_clrServices.pVtable = __vtable_ICLRServices.GetVtable(); @@ -609,7 +609,7 @@ namespace System.Runtime.InteropServices // Tell Jupiter that we are ready for tracking life time of objects and provide Jupiter with // our life time realted services through ICLRServices // - CalliIntrinsics.StdCall( + CalliIntrinsics.StdCall__int( pGCManager->pVTable->pfnSetCLRServices, pGCManager, pCLRServices @@ -656,7 +656,7 @@ namespace System.Runtime.InteropServices // Notify Jupiter that we've created a new RCW for this Jupiter object // To avoid surprises, we should notify them before we fire the first AfterAddRef // - CalliIntrinsics.StdCall(pJupiterObject->pVtable->pfnConnect, pJupiterObject); + CalliIntrinsics.StdCall__int(pJupiterObject->pVtable->pfnConnect, pJupiterObject); // // Tell Jupiter that we've done AddRef for IJupiterObject* and IUnknown* @@ -687,7 +687,7 @@ namespace System.Runtime.InteropServices // We should do this *after* we made a AddRef because we should never // be in a state where report refs > actual refs // - CalliIntrinsics.StdCall(pJupiterObject->pVtable->pfnAfterAddRef, pJupiterObject); + CalliIntrinsics.StdCall__int(pJupiterObject->pVtable->pfnAfterAddRef, pJupiterObject); } /// @@ -706,7 +706,7 @@ namespace System.Runtime.InteropServices __com_IJupiterObject* pJupiterObject = comObject.GetIJupiterObject_NoAddRef(); - CalliIntrinsics.StdCall(pJupiterObject->pVtable->pfnBeforeRelease, pJupiterObject); + CalliIntrinsics.StdCall__int(pJupiterObject->pVtable->pfnBeforeRelease, pJupiterObject); } /// diff --git a/external/corert/src/System.Private.Interop/src/Shared/StandardInterfaces.cs b/external/corert/src/System.Private.Interop/src/Shared/StandardInterfaces.cs index e623091d68..24c04ecce5 100644 --- a/external/corert/src/System.Private.Interop/src/Shared/StandardInterfaces.cs +++ b/external/corert/src/System.Private.Interop/src/Shared/StandardInterfaces.cs @@ -545,21 +545,22 @@ namespace System.Runtime.InteropServices HSTRING unsafe_name, IntPtr __IntPtr__unsafe_customProperty) { - void** unsafe_customProperty = (void**)__IntPtr__unsafe_customProperty; + // Initialize [out] parameters + *unsafe_customProperty = default(void*); object target = ComCallableObject.FromThisPointer(pComThis).TargetObject; string propertyName = McgMarshal.HStringToString(unsafe_name); try { global::Windows.UI.Xaml.Data.ICustomProperty property = ManagedGetCustomProperty(target, propertyName); - *unsafe_customProperty = (void*)McgComHelpers.ManagedObjectToComInterface( - property, - typeof(global::Windows.UI.Xaml.Data.ICustomProperty).TypeHandle); + *unsafe_customProperty = (void*)McgComHelpers.ManagedObjectToComInterface( + property, + typeof(global::Windows.UI.Xaml.Data.ICustomProperty).TypeHandle); } - catch (Exception ex) + catch (Exception) { - return McgMarshal.GetHRForExceptionWinRT(ex); + // Don't fail if property can't be found } return Interop.COM.S_OK; } @@ -608,9 +609,10 @@ namespace System.Runtime.InteropServices TypeName unsafe_type, IntPtr __IntPtr__unsafe_customProperty) { - //__com_Windows_UI_Xaml_Data__ICustomProperty** unsafe_customProperty = (__com_Windows_UI_Xaml_Data__ICustomProperty**)__IntPtr__unsafe_customProperty; void** unsafe_customProperty = (void**)__IntPtr__unsafe_customProperty; - + + // Initialize [out] parameters + *unsafe_customProperty = default(void*); try { object target = ComCallableObject.FromThisPointer(pComThis).TargetObject; @@ -622,9 +624,9 @@ namespace System.Runtime.InteropServices property, typeof(global::Windows.UI.Xaml.Data.ICustomProperty).TypeHandle); } - catch (Exception ex) + catch (Exception) { - return McgMarshal.GetHRForExceptionWinRT(ex); + // Don't fail if property can't be found - just return S_OK and NULL property } return Interop.COM.S_OK; @@ -712,7 +714,6 @@ namespace System.Runtime.InteropServices return null; } - [NativeCallable] static int GetStringRepresentation__STUB( System.IntPtr pComThis, @@ -768,7 +769,7 @@ namespace System.Runtime.InteropServices { TypeName* unsafe_value = (TypeName*)__IntPtr__unsafe_value; - + *unsafe_value = default(TypeName); int kind; try { @@ -784,9 +785,9 @@ namespace System.Runtime.InteropServices unsafe_value->Kind = (TypeKind)kind; } - catch (Exception ex) + catch (Exception) { - return McgMarshal.GetHRForExceptionWinRT(ex); + // Don't fail---Align with desktop behavior } return Interop.COM.S_OK; } @@ -1578,7 +1579,7 @@ namespace System.Runtime.InteropServices static unsafe int GetIMarshal(void **ppIMarshal) { -#if ENABLE_WINRT +#if ENABLE_MIN_WINRT void *pUnk = null; int hr = ExternalInterop.CoCreateFreeThreadedMarshaler(null, (void **)&pUnk); if (hr < 0) return hr; @@ -1611,7 +1612,7 @@ namespace System.Runtime.InteropServices int hr = GetIMarshal((void **)&pIMarshal); if (hr < 0) return hr; - return CalliIntrinsics.StdCall( + return CalliIntrinsics.StdCall__int( (*pIMarshal)->pfnGetUnmarshalClass, pIMarshal, piid, @@ -1644,7 +1645,7 @@ namespace System.Runtime.InteropServices int hr = GetIMarshal((void**)&pIMarshal); ; if (hr < 0) return hr; - return CalliIntrinsics.StdCall( + return CalliIntrinsics.StdCall__int( (*pIMarshal)->pfnGetMarshalSizeMax, pIMarshal, piid, @@ -1677,7 +1678,7 @@ namespace System.Runtime.InteropServices int hr = GetIMarshal((void**)&pIMarshal); if (hr < 0) return hr; - return CalliIntrinsics.StdCall( + return CalliIntrinsics.StdCall__int( (*pIMarshal)->pfnMarshalInterface, pIMarshal, pStm, @@ -1707,7 +1708,7 @@ namespace System.Runtime.InteropServices int hr = GetIMarshal((void**)&pIMarshal); if (hr < 0) return hr; - return CalliIntrinsics.StdCall( + return CalliIntrinsics.StdCall__int( (*pIMarshal)->pfnUnmarshalInterface, pIMarshal, pStm, @@ -1732,7 +1733,7 @@ namespace System.Runtime.InteropServices int hr = GetIMarshal((void**)&pIMarshal); if (hr < 0) return hr; - return CalliIntrinsics.StdCall( + return CalliIntrinsics.StdCall__int( (*pIMarshal)->pfnReleaseMarshalData, pIMarshal, pStm); @@ -1755,7 +1756,7 @@ namespace System.Runtime.InteropServices int hr = GetIMarshal((void**)&pIMarshal); if (hr < 0) return hr; - return CalliIntrinsics.StdCall( + return CalliIntrinsics.StdCall__int( (*pIMarshal)->pfnDisconnectObject, pIMarshal, dwReserved); @@ -1800,7 +1801,7 @@ namespace System.Runtime.InteropServices /// The IStream* internal static unsafe IntPtr CreateMemStm(ulong lSize) { -#if ENABLE_WINRT +#if ENABLE_MIN_WINRT __com_IStream* pIStream = (__com_IStream*)PInvokeMarshal.CoTaskMemAlloc(new UIntPtr((uint)sizeof(__com_IStream))); pIStream->pVtable = (__vtable_IStream*)__vtable_IStream.GetVtable(); pIStream->m_cbCurrent = 0; @@ -2076,7 +2077,7 @@ namespace System.Runtime.InteropServices #region Rest of IStream overrides that are not implemented [NativeCallable] - internal static int Clone(System.IntPtr pComThis, out IntPtr ppstm) + internal static int Clone(System.IntPtr pComThis, IntPtr ppstm) { ppstm = default(IntPtr); return Interop.COM.E_NOTIMPL; diff --git a/external/corert/src/System.Private.Interop/src/Shared/WindowsRuntimeMarshal.cs b/external/corert/src/System.Private.Interop/src/Shared/WindowsRuntimeMarshal.cs index 5cf39e92c6..50b8f5b88b 100644 --- a/external/corert/src/System.Private.Interop/src/Shared/WindowsRuntimeMarshal.cs +++ b/external/corert/src/System.Private.Interop/src/Shared/WindowsRuntimeMarshal.cs @@ -7,7 +7,6 @@ using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Security; using System.Runtime.CompilerServices; using System.Threading; @@ -32,7 +31,6 @@ namespace System.Runtime.InteropServices.WindowsRuntime throw new ArgumentNullException(nameof(addMethod)); if (removeMethod == null) throw new ArgumentNullException(nameof(removeMethod)); - Contract.EndContractBlock(); // Managed code allows adding a null event handler, the effect is a no-op. To match this behavior // for WinRT events, we simply ignore attempts to add null. @@ -64,7 +62,6 @@ namespace System.Runtime.InteropServices.WindowsRuntime { if (removeMethod == null) throw new ArgumentNullException(nameof(removeMethod)); - Contract.EndContractBlock(); // Managed code allows removing a null event handler, the effect is a no-op. To match this behavior // for WinRT events, we simply ignore attempts to remove null. @@ -90,7 +87,6 @@ namespace System.Runtime.InteropServices.WindowsRuntime { if (removeMethod == null) throw new ArgumentNullException(nameof(removeMethod)); - Contract.EndContractBlock(); // Delegate to managed event registration implementation or native event registration implementation // They have completely different implementation because native side has its own unique problem to solve - @@ -98,7 +94,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime // it would be more confusing and less-performant if we were to merge them together #if !RHTESTCL object target = removeMethod.Target; - if (target != null && target is __ComObject) + if (target == null || target is __ComObject) NativeOrStaticEventRegistrationImpl.RemoveAllEventHandlers(removeMethod); else #endif @@ -118,7 +114,8 @@ namespace System.Runtime.InteropServices.WindowsRuntime { ManagedEventRegistrationImpl.s_eventRegistrationsLock.Acquire(); - count += ManagedEventRegistrationImpl.s_eventRegistrations.GetKeys().Count; + foreach (var item in ManagedEventRegistrationImpl.s_eventRegistrations) + count++; } finally { @@ -674,6 +671,20 @@ namespace System.Runtime.InteropServices.WindowsRuntime return (object)comObject.BaseIUnknown_UnsafeNoAddRef; } + private static object FindEquivalentKeyUnsafe(ConditionalWeakTable registrationTable, object handler, out EventRegistrationTokenListWithCount tokens) + { + foreach (KeyValuePair item in registrationTable) + { + if (Object.Equals(item.Key, handler)) + { + tokens = item.Value; + return item.Key; + } + } + tokens = null; + return null; + } + internal static void AddEventHandler(Func addMethod, Action removeMethod, T handler) @@ -720,7 +731,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime // will be added into B's token list, but once we unsubscribe B, we might end up removing // the last token in C, and that may lead to crash. // - object key = registrationTokens.registrationTable.FindEquivalentKeyUnsafe(handler, out tokens); + object key = FindEquivalentKeyUnsafe(registrationTokens.registrationTable, handler, out tokens); if (key == null) { @@ -862,7 +873,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime // It actually doesn't matter which delegate - as long as it matches // Note that inside TryGetValueWithValueEquality we assumes that any delegate // with the same value equality would have the same hash code - object key = registrationTokens.registrationTable.FindEquivalentKeyUnsafe(handler, out tokens); + object key = FindEquivalentKeyUnsafe(registrationTokens.registrationTable, handler, out tokens); Debug.Assert((key != null && tokens != null) || (key == null && tokens == null), "key and tokens must be both null or non-null"); @@ -938,9 +949,9 @@ namespace System.Runtime.InteropServices.WindowsRuntime // Copy all tokens to tokensToRemove array which later we'll call removeMethod on // outside this lock - foreach (EventRegistrationTokenListWithCount tokens in registrationTokens.registrationTable.GetValues()) + foreach (KeyValuePair item in registrationTokens.registrationTable) { - tokens.CopyTo(tokensToRemove); + item.Value.CopyTo(tokensToRemove); } // Clear the table - at this point all event handlers are no longer in the cache diff --git a/external/corert/src/System.Private.Interop/src/Shared/__ComObject.cs.REMOVED.git-id b/external/corert/src/System.Private.Interop/src/Shared/__ComObject.cs.REMOVED.git-id index 57d8774d7a..618767a1ef 100644 --- a/external/corert/src/System.Private.Interop/src/Shared/__ComObject.cs.REMOVED.git-id +++ b/external/corert/src/System.Private.Interop/src/Shared/__ComObject.cs.REMOVED.git-id @@ -1 +1 @@ -ea151f4d6e35a59047f0b69a258c5d09f0c5e692 \ No newline at end of file +b68a81e0b13805a3945dadc0047f518b8d720231 \ No newline at end of file diff --git a/external/corert/src/System.Private.Interop/src/System.Private.Interop.CoreCLR.csproj b/external/corert/src/System.Private.Interop/src/System.Private.Interop.CoreCLR.csproj new file mode 100644 index 0000000000..443e811f70 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/System.Private.Interop.CoreCLR.csproj @@ -0,0 +1,76 @@ + + + + + System.Private.Interop + $(AssemblyName) + Library + {A85709C9-22D5-4704-8B7A-73751BB4386A} + true + TARGET_CORE_API_SET;CORECLR + + $(NoWarn);3021 + + MSFT + false + true + true + $(BaseOutputPath)$(OSPlatformConfig)\interop\coreclr + netstandard1.3 + true + + + + + 4.3.0 + + + 4.3.0 + + + 1.0.0 + + + + + + + + + + + + + + + + + + + + + + + System\Runtime\InteropServices\McgPInvokeData.cs + + + System\Runtime\InteropServices\McgGeneratedNativeCallCodeAttribute.cs + + + + + + + + + + + + + + + + + + + diff --git a/external/corert/src/System.Private.Interop/src/System.Private.Interop.Mono.csproj b/external/corert/src/System.Private.Interop/src/System.Private.Interop.Mono.csproj new file mode 100644 index 0000000000..efe03b78e3 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/System.Private.Interop.Mono.csproj @@ -0,0 +1,107 @@ + + + + + System.Private.Interop + $(AssemblyName) + Library + $(BaseOutputPath)$(OSPlatformConfig)\interop\mono + {A85709C9-22D5-4704-8B7A-73751BB4386A} + true + TARGET_CORE_API_SET;CORECLR; + + $(NoWarn);3021 + + MSFT + false + true + true + netstandard1.3 + true + + + + $(DefineConstants);ENABLE_MIN_WINRT + + + + + 4.3.0 + + + 4.3.0 + + + 1.0.0 + + + + + + + + + + + + + + + + + + + + + + + System\Runtime\InteropServices\McgPInvokeData.cs + + + System\Runtime\InteropServices\McgGeneratedNativeCallCodeAttribute.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/corert/src/System.Private.Interop/src/System.Private.Interop.Shared.projitems b/external/corert/src/System.Private.Interop/src/System.Private.Interop.Shared.projitems new file mode 100644 index 0000000000..394ff286f3 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/System.Private.Interop.Shared.projitems @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Internal\NativeFormat\NativeFormatReader.cs + + + Internal\NativeFormat\NativeFormatReader.Primitives.cs + + + Internal\NativeFormat\NativeFormatReader.String.cs + + + + + + + + + + + + + + + + + + diff --git a/external/corert/src/System.Private.Interop/src/System.Private.Interop.csproj b/external/corert/src/System.Private.Interop/src/System.Private.Interop.csproj index aa08ccddcd..03ca929280 100644 --- a/external/corert/src/System.Private.Interop/src/System.Private.Interop.csproj +++ b/external/corert/src/System.Private.Interop/src/System.Private.Interop.csproj @@ -1,90 +1,48 @@ - - + System.Private.Interop Library - {A85709C9-22D5-4704-8B7A-73751BB4386A} true $(DefineConstants);TARGET_CORE_API_SET $(NoWarn);3021 + + MSFT + true - - - - - false - + + false + true + - + + + + + + + + + + - - - - + - - - - Internal\NativeFormat\NativeFormatReader.cs - - - Internal\NativeFormat\NativeFormatReader.Primitives.cs - - - Internal\NativeFormat\NativeFormatReader.String.cs - Internal\Runtime\LowLevelStringConverter.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -101,15 +59,9 @@ - - - - - - @@ -119,11 +71,7 @@ - - - - @@ -132,25 +80,11 @@ - - - - - - - - - MetadataBlob.cs - - - System\Runtime\CompilerServices\__BlockReflectionAttribute.cs - - - + @@ -206,37 +140,60 @@ - + + + + - - - - - - - - - - - - - - + + + + MetadataBlob.cs + + + System\Runtime\CompilerServices\__BlockReflectionAttribute.cs + + + + System\NotImplemented.cs + - + + + + + + + + + - + + + + + + + + + + + + + + + Interop\Windows\mincore\Interop.MemAllocFree.cs @@ -247,11 +204,7 @@ Interop\Windows\mincore\Interop.MemAllocWithZeroInitializeNoThrow.cs - - - - - + Interop\Unix\Interop.Libraries.cs diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/ArrayWithOffset.cs b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/ArrayWithOffset.cs index 96997db757..5ee563ca61 100644 --- a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/ArrayWithOffset.cs +++ b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/ArrayWithOffset.cs @@ -83,7 +83,7 @@ namespace System.Runtime.InteropServices throw new ArgumentException(SR.Arg_NotIsomorphic); } - if (!arrayObj.IsElementTypeBlittable()) + if (!arrayObj.IsBlittable()) { throw new ArgumentException(SR.Arg_NotIsomorphic); } diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/ComWeakReferenceHelpers.cs b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/ComWeakReferenceHelpers.cs index a97198082e..a3910b67c8 100644 --- a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/ComWeakReferenceHelpers.cs +++ b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/ComWeakReferenceHelpers.cs @@ -134,7 +134,7 @@ namespace System.Runtime.InteropServices { // Now that we have the IWeakReferenceSource , we need to call the GetWeakReference method to get the corresponding IWeakReference __com_IWeakReferenceSource* pComWeakRefSource = (__com_IWeakReferenceSource*)pWeakRefSource; - int result = CalliIntrinsics.StdCall( + int result = CalliIntrinsics.StdCall__int( pComWeakRefSource->pVtable->pfnGetWeakReference, pWeakRefSource, out pWeakRef); diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/InvalidComObjectException.cs b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/InvalidComObjectException.cs index 43ead6772e..3d62cfc5ea 100644 --- a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/InvalidComObjectException.cs +++ b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/InvalidComObjectException.cs @@ -18,6 +18,7 @@ using System.Runtime.Serialization; namespace System.Runtime.InteropServices { [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class InvalidComObjectException : SystemException { public InvalidComObjectException() diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/InvalidOleVariantTypeException.cs b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/InvalidOleVariantTypeException.cs index 80f4ce3e34..d74ac6d388 100644 --- a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/InvalidOleVariantTypeException.cs +++ b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/InvalidOleVariantTypeException.cs @@ -20,6 +20,8 @@ using System.Runtime.Serialization; namespace System.Runtime.InteropServices { + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class InvalidOleVariantTypeException : SystemException { public InvalidOleVariantTypeException() diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/Marshal.cs b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/Marshal.cs index ec14c95e9e..f9e481abc3 100644 --- a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/Marshal.cs +++ b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/Marshal.cs @@ -12,7 +12,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; @@ -20,6 +19,7 @@ using System.Security; using System.Text; using System.Threading; using System.Runtime.InteropServices.ComTypes; +using Internal.Runtime.CompilerHelpers; namespace System.Runtime.InteropServices { @@ -98,28 +98,12 @@ namespace System.Runtime.InteropServices public static unsafe String PtrToStringUni(IntPtr ptr, int len) { - if (ptr == IntPtr.Zero) - throw new ArgumentNullException(nameof(ptr)); - if (len < 0) - throw new ArgumentException(nameof(len)); - - return new String((char*)ptr, 0, len); + return PInvokeMarshal.PtrToStringUni(ptr, len); } public static unsafe String PtrToStringUni(IntPtr ptr) { - if (IntPtr.Zero == ptr) - { - return null; - } - else if (IsWin32Atom(ptr)) - { - return null; - } - else - { - return new String((char*)ptr); - } + return PInvokeMarshal.PtrToStringUni(ptr); } public static String PtrToStringAuto(IntPtr ptr, int len) @@ -156,19 +140,16 @@ namespace System.Runtime.InteropServices if (structure == null) throw new ArgumentNullException(nameof(structure)); // we never had a check for generics here - Contract.EndContractBlock(); return SizeOfHelper(structure.GetType(), true); } - [Pure] public static int SizeOf(Type t) { if (t == null) throw new ArgumentNullException(nameof(t)); if (t.TypeHandle.IsGenericType() || t.TypeHandle.IsGenericTypeDefinition()) throw new ArgumentException(SR.Argument_NeedNonGenericType, nameof(t)); - Contract.EndContractBlock(); return SizeOfHelper(t, true); } @@ -177,10 +158,10 @@ namespace System.Runtime.InteropServices { RuntimeTypeHandle typeHandle = t.TypeHandle; - RuntimeTypeHandle unsafeStructType; - if (McgModuleManager.TryGetStructUnsafeStructType(typeHandle, out unsafeStructType)) + int size; + if (RuntimeInteropData.Instance.TryGetStructUnsafeStructSize(typeHandle, out size)) { - return unsafeStructType.GetValueTypeSize(); + return size; } if (!typeHandle.IsBlittable() && !typeHandle.IsValueType()) @@ -207,7 +188,6 @@ namespace System.Runtime.InteropServices if (t.TypeHandle.IsGenericType() || t.TypeHandle.IsGenericTypeDefinition()) throw new ArgumentException(SR.Argument_NeedNonGenericType, nameof(t)); - Contract.EndContractBlock(); return OffsetOfHelper(t, fieldName); } @@ -216,7 +196,7 @@ namespace System.Runtime.InteropServices { bool structExists; uint offset; - if (McgModuleManager.TryGetStructFieldOffset(t.TypeHandle, fieldName, out structExists, out offset)) + if (RuntimeInteropData.Instance.TryGetStructFieldOffset(t.TypeHandle, fieldName, out structExists, out offset)) { return new IntPtr(offset); } @@ -242,47 +222,42 @@ namespace System.Runtime.InteropServices //==================================================================== public static void Copy(int[] source, int startIndex, IntPtr destination, int length) { - CopyToNative(source, startIndex, destination, length); + PInvokeMarshal.CopyToNative(source, startIndex, destination, length); } public static void Copy(char[] source, int startIndex, IntPtr destination, int length) { - CopyToNative(source, startIndex, destination, length); + PInvokeMarshal.CopyToNative(source, startIndex, destination, length); } public static void Copy(short[] source, int startIndex, IntPtr destination, int length) { - CopyToNative(source, startIndex, destination, length); + PInvokeMarshal.CopyToNative(source, startIndex, destination, length); } public static void Copy(long[] source, int startIndex, IntPtr destination, int length) { - CopyToNative(source, startIndex, destination, length); + PInvokeMarshal.CopyToNative(source, startIndex, destination, length); } public static void Copy(float[] source, int startIndex, IntPtr destination, int length) { - CopyToNative(source, startIndex, destination, length); + PInvokeMarshal.CopyToNative(source, startIndex, destination, length); } public static void Copy(double[] source, int startIndex, IntPtr destination, int length) { - CopyToNative(source, startIndex, destination, length); + PInvokeMarshal.CopyToNative(source, startIndex, destination, length); } public static void Copy(byte[] source, int startIndex, IntPtr destination, int length) { - CopyToNative(source, startIndex, destination, length); + PInvokeMarshal.CopyToNative(source, startIndex, destination, length); } public static void Copy(IntPtr[] source, int startIndex, IntPtr destination, int length) { - CopyToNative(source, startIndex, destination, length); - } - - private static void CopyToNative(Array source, int startIndex, IntPtr destination, int length) - { - InteropExtensions.CopyToNative(source, startIndex, destination, length); + PInvokeMarshal.CopyToNative(source, startIndex, destination, length); } //==================================================================== @@ -290,50 +265,44 @@ namespace System.Runtime.InteropServices //==================================================================== public static void Copy(IntPtr source, int[] destination, int startIndex, int length) { - CopyToManaged(source, destination, startIndex, length); + PInvokeMarshal.CopyToManaged(source, destination, startIndex, length); } public static void Copy(IntPtr source, char[] destination, int startIndex, int length) { - CopyToManaged(source, destination, startIndex, length); + PInvokeMarshal.CopyToManaged(source, destination, startIndex, length); } public static void Copy(IntPtr source, short[] destination, int startIndex, int length) { - CopyToManaged(source, destination, startIndex, length); + PInvokeMarshal.CopyToManaged(source, destination, startIndex, length); } public static void Copy(IntPtr source, long[] destination, int startIndex, int length) { - CopyToManaged(source, destination, startIndex, length); + PInvokeMarshal.CopyToManaged(source, destination, startIndex, length); } public static void Copy(IntPtr source, float[] destination, int startIndex, int length) { - CopyToManaged(source, destination, startIndex, length); + PInvokeMarshal.CopyToManaged(source, destination, startIndex, length); } public static void Copy(IntPtr source, double[] destination, int startIndex, int length) { - CopyToManaged(source, destination, startIndex, length); + PInvokeMarshal.CopyToManaged(source, destination, startIndex, length); } public static void Copy(IntPtr source, byte[] destination, int startIndex, int length) { - CopyToManaged(source, destination, startIndex, length); + PInvokeMarshal.CopyToManaged(source, destination, startIndex, length); } public static void Copy(IntPtr source, IntPtr[] destination, int startIndex, int length) { - CopyToManaged(source, destination, startIndex, length); + PInvokeMarshal.CopyToManaged(source, destination, startIndex, length); } - private static void CopyToManaged(IntPtr source, Array destination, int startIndex, int length) - { - InteropExtensions.CopyToManaged(source, destination, startIndex, length); - } - - //==================================================================== // Read from memory //==================================================================== @@ -402,29 +371,23 @@ namespace System.Runtime.InteropServices public static IntPtr ReadIntPtr([MarshalAs(UnmanagedType.AsAny), In] Object ptr, int ofs) { -#if WIN32 - return (IntPtr)ReadInt32(ptr, ofs); -#else - return (IntPtr)ReadInt64(ptr, ofs); -#endif + if (IntPtr.Size == 4) + return (IntPtr)ReadInt32(ptr, ofs); + else + return (IntPtr)ReadInt64(ptr, ofs); } public static IntPtr ReadIntPtr(IntPtr ptr, int ofs) { -#if WIN32 - return (IntPtr)ReadInt32(ptr, ofs); -#else - return (IntPtr)ReadInt64(ptr, ofs); -#endif + if (IntPtr.Size == 4) + return (IntPtr)ReadInt32(ptr, ofs); + else + return (IntPtr)ReadInt64(ptr, ofs); } public static IntPtr ReadIntPtr(IntPtr ptr) { -#if WIN32 - return (IntPtr)ReadInt32(ptr, 0); -#else - return (IntPtr)ReadInt64(ptr, 0); -#endif + return ReadIntPtr(ptr, 0); } public static unsafe long ReadInt64(IntPtr ptr, int ofs) @@ -534,29 +497,24 @@ namespace System.Runtime.InteropServices public static void WriteIntPtr(IntPtr ptr, int ofs, IntPtr val) { -#if WIN32 - WriteInt32(ptr, ofs, (int)val); -#else - WriteInt64(ptr, ofs, (long)val); -#endif + if (IntPtr.Size == 4) + WriteInt32(ptr, ofs, (int)val); + else + WriteInt64(ptr, ofs, (long)val); } public static void WriteIntPtr([MarshalAs(UnmanagedType.AsAny), In, Out] Object ptr, int ofs, IntPtr val) { -#if WIN32 - WriteInt32(ptr, ofs, (int)val); -#else - WriteInt64(ptr, ofs, (long)val); -#endif + if (IntPtr.Size == 4) + WriteInt32(ptr, ofs, (int)val); + else + WriteInt64(ptr, ofs, (long)val); + } public static void WriteIntPtr(IntPtr ptr, IntPtr val) { -#if WIN32 - WriteInt32(ptr, 0, (int)val); -#else - WriteInt64(ptr, 0, (long)val); -#endif + WriteIntPtr(ptr, 0, val); } public static unsafe void WriteInt64(IntPtr ptr, int ofs, long val) @@ -828,7 +786,7 @@ namespace System.Runtime.InteropServices if (o == null) throw new ArgumentNullException(nameof(o), SR.Arg_InvalidHandle); - return McgComHelpers.IsComObject(o); + return McgMarshal.IsComObject(o); } public static unsafe IntPtr StringToCoTaskMemUni(String s) @@ -928,7 +886,6 @@ namespace System.Runtime.InteropServices { if (o == null) throw new ArgumentNullException(nameof(o)); - Contract.EndContractBlock(); __ComObject co = null; @@ -1096,7 +1053,7 @@ namespace System.Runtime.InteropServices } IntPtr unmarshalStub; - if (McgModuleManager.TryGetStructUnmarshalStub(structureTypeHandle, out unmarshalStub)) + if (RuntimeInteropData.Instance.TryGetStructUnmarshalStub(structureTypeHandle, out unmarshalStub)) { InteropExtensions.PinObjectAndCall(structure, unboxedStructPtr => @@ -1191,7 +1148,7 @@ namespace System.Runtime.InteropServices bool isBlittable = false; // whether Mcg treat this struct as blittable struct IntPtr marshalStub; - if (McgModuleManager.TryGetStructMarshalStub(structureTypeHandle, out marshalStub)) + if (RuntimeInteropData.Instance.TryGetStructMarshalStub(structureTypeHandle, out marshalStub)) { if (marshalStub != IntPtr.Zero) { @@ -1259,7 +1216,6 @@ namespace System.Runtime.InteropServices throw new ArgumentException(SR.Argument_MustHaveLayoutOrBeBlittable, structureTypeHandle.GetDisplayName()); } - Contract.EndContractBlock(); DestroyStructureHelper(ptr, structuretype); } @@ -1279,7 +1235,7 @@ namespace System.Runtime.InteropServices IntPtr destroyStructureStub; bool hasInvalidLayout; - if (McgModuleManager.TryGetDestroyStructureStub(structureTypeHandle, out destroyStructureStub, out hasInvalidLayout)) + if (RuntimeInteropData.Instance.TryGetDestroyStructureStub(structureTypeHandle, out destroyStructureStub, out hasInvalidLayout)) { if (hasInvalidLayout) throw new ArgumentException(SR.Argument_MustHaveLayoutOrBeBlittable, structureTypeHandle.GetDisplayName()); @@ -1328,7 +1284,6 @@ namespace System.Runtime.InteropServices if (t == null) throw new ArgumentNullException(nameof(t)); - Contract.EndContractBlock(); if (t.TypeHandle.IsGenericType() || t.TypeHandle.IsGenericTypeDefinition()) throw new ArgumentException(SR.Argument_NeedNonGenericType, nameof(t)); @@ -1360,7 +1315,6 @@ namespace System.Runtime.InteropServices if (obj != null && (obj.GetType().TypeHandle.IsGenericType() || obj.GetType().TypeHandle.IsGenericTypeDefinition())) throw new ArgumentException(SR.Argument_NeedNonGenericObject, nameof(obj)); - Contract.EndContractBlock(); Variant* pVariant = (Variant*)pDstNativeVariant; *pVariant = new Variant(obj); @@ -1380,7 +1334,6 @@ namespace System.Runtime.InteropServices // Obsolete if (pSrcNativeVariant == IntPtr.Zero) throw new ArgumentNullException(nameof(pSrcNativeVariant)); - Contract.EndContractBlock(); Variant* pNativeVar = (Variant*)pSrcNativeVariant; return pNativeVar->ToObject(); @@ -1398,7 +1351,6 @@ namespace System.Runtime.InteropServices if (cVars < 0) throw new ArgumentOutOfRangeException(nameof(cVars), SR.ArgumentOutOfRange_NeedNonNegNum); - Contract.EndContractBlock(); Object[] obj = new Object[cVars]; IntPtr aNativeVar = aSrcNativeVariant; @@ -1435,21 +1387,12 @@ namespace System.Runtime.InteropServices //==================================================================== public static IntPtr UnsafeAddrOfPinnedArrayElement(Array arr, int index) { - if (arr == null) - throw new ArgumentNullException(nameof(arr)); - - if (index < 0 || index >= arr.Length) - throw new ArgumentOutOfRangeException(nameof(index)); - Contract.EndContractBlock(); - - int offset = checked(index * arr.GetElementSize()); - - return arr.GetAddrOfPinnedArrayFromEETypeField() + offset; + return PInvokeMarshal.UnsafeAddrOfPinnedArrayElement(arr, index); } public static IntPtr UnsafeAddrOfPinnedArrayElement(T[] arr, int index) { - return UnsafeAddrOfPinnedArrayElement((Array)arr, index); + return PInvokeMarshal.UnsafeAddrOfPinnedArrayElement(arr, index); } //==================================================================== @@ -1476,9 +1419,7 @@ namespace System.Runtime.InteropServices #if ENABLE_WINRT public static Type GetTypeFromCLSID(Guid clsid) { - // @TODO - if this is something we recognize, create a strongly-typed RCW - // Otherwise, create a weakly typed RCW - throw new PlatformNotSupportedException(); + return Type.GetTypeFromCLSID(clsid); } //==================================================================== @@ -1616,7 +1557,6 @@ namespace System.Runtime.InteropServices { if (m == null) throw new ArgumentNullException(nameof(m)); - Contract.EndContractBlock(); // Note: This method is effectively a no-op in ahead-of-time compilation scenarios. In CoreCLR and Desktop, this will pre-generate // the P/Invoke, but everything is pre-generated in CoreRT. @@ -1626,7 +1566,6 @@ namespace System.Runtime.InteropServices { if (c == null) throw new ArgumentNullException(nameof(c)); - Contract.EndContractBlock(); MethodInfo[] mi = c.GetMethods(); if (mi != null) diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/MarshalAdapter.cs b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/MarshalAdapter.cs index 84b046a06e..599eca594e 100644 --- a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/MarshalAdapter.cs +++ b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/MarshalAdapter.cs @@ -11,7 +11,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Runtime.InteropServices.ComTypes; using System.Runtime.Versioning; diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/MarshalImpl.cs b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/MarshalImpl.cs index 973b305a70..20f08a9198 100644 --- a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/MarshalImpl.cs +++ b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/MarshalImpl.cs @@ -38,18 +38,18 @@ namespace System.Runtime.InteropServices { if (o == null) throw new ArgumentNullException(nameof(o)); - return McgMarshal.IsCOMObject(o.GetType()); + return McgMarshal.IsComObject(o); } public static int ReleaseComObject(object o) { if (o == null) throw new ArgumentNullException(nameof(o)); - return McgMarshal.Release(o as __ComObject); + return McgMarshal.Release(o as __ComObject); } public static int FinalReleaseComObject(object o) { - return McgMarshal.FinalReleaseComObject(o); + return McgMarshal.FinalReleaseComObject(o); } public static int QueryInterface(IntPtr pUnk, ref Guid iid, out IntPtr ppv) @@ -72,6 +72,5 @@ namespace System.Runtime.InteropServices { return McgMarshal.ComRelease(pUnk); } - } } diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/SEHException.cs b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/SEHException.cs index b199103a76..24f0f04558 100644 --- a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/SEHException.cs +++ b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/SEHException.cs @@ -18,6 +18,7 @@ using System.Runtime.Serialization; namespace System.Runtime.InteropServices { [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class SEHException : ExternalException { public SEHException() diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/SafeArrayRankMismatchException.cs b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/SafeArrayRankMismatchException.cs index cbb15a5131..1f295686e0 100644 --- a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/SafeArrayRankMismatchException.cs +++ b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/SafeArrayRankMismatchException.cs @@ -17,6 +17,7 @@ using System.Runtime.Serialization; namespace System.Runtime.InteropServices { [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class SafeArrayRankMismatchException : SystemException { public SafeArrayRankMismatchException() diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/SafeArrayTypeMismatchException.cs b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/SafeArrayTypeMismatchException.cs index 60ee7c20e4..24e95836c8 100644 --- a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/SafeArrayTypeMismatchException.cs +++ b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/SafeArrayTypeMismatchException.cs @@ -18,6 +18,7 @@ using System.Runtime.Serialization; namespace System.Runtime.InteropServices { [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class SafeArrayTypeMismatchException : SystemException { public SafeArrayTypeMismatchException() diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/CLRIKeyValuePairImpl.cs b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/CLRIKeyValuePairImpl.cs new file mode 100644 index 0000000000..9b027f3766 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/CLRIKeyValuePairImpl.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. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Windows.Foundation.Collections; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + [System.Runtime.CompilerServices.DependencyReductionRootAttribute] + [McgInternalTypeAttribute] + public sealed class CLRIKeyValuePairImpl : + global::System.Runtime.InteropServices.BoxedKeyValuePair, + IKeyValuePair, // Use IKeyValuePair from S.P.Interop instead of Windows.winmd to loose the dependency between S.P.Interop and Windows.winmd + __IUnboxInternal + { + private global::System.Collections.Generic.KeyValuePair _pair; + + public CLRIKeyValuePairImpl(ref global::System.Collections.Generic.KeyValuePair pair) + { + _pair = pair; + } + + public K get_Key() + { + return _pair.Key; + } + + [global::System.Runtime.InteropServices.McgAccessor(global::System.Runtime.InteropServices.McgAccessorKind.PropertyGet, "Value")] + public V get_Value() + { + return _pair.Value; + } + + public override object GetTarget() + { + return (object)_pair; + } + + /// + /// Called by public object McgModule.Box(object obj, int boxingIndex) after allocating instance + /// + /// KeyValuePair + /// IKeyValuePair instance + public override object Initialize(object pair) + { + _pair = (global::System.Collections.Generic.KeyValuePair)pair; + + return this; + } + + /// + /// Get unboxed value + /// This method is used by dynamic interop. + /// The reason for adding this instance method instead of using static Unbox is to avoid reflection + /// + /// native winrt object or our own boxed KeyValuePair + /// unboxed value as object + public object get_Value(object obj) + { + return Unbox(obj); + } + + /// + /// This method is called from ComInterop.cs in the Unboxing code. + /// + /// native winrt object or our own boxed KeyValuePair + /// unboxed value as object + public static object Unbox(object wrapper) + { + CLRIKeyValuePairImpl reference = wrapper as CLRIKeyValuePairImpl; + + if (reference != null) + { + return reference._pair; + } + else + { + // We could just have the native IKeyValuePair in which case we simply return wrapper as IKeyValuePair. + global::Windows.Foundation.Collections.IKeyValuePair iPair = wrapper as global::Windows.Foundation.Collections.IKeyValuePair; + return new System.Collections.Generic.KeyValuePair(iPair.get_Key(), iPair.get_Value()); + } + } + + public override string ToString() + { + return _pair.ToString(); + } + } + + [System.Runtime.CompilerServices.DependencyReductionRootAttribute] + [McgInternalTypeAttribute] + public sealed class CLRIKeyValuePairArrayImpl : global::System.Runtime.InteropServices.BoxedKeyValuePair + { + private object _pairs; + + /// + /// Called by public object McgModule.Box(object obj, int boxingIndex) after allocating instance + /// + /// KeyValuePair[] + /// IReferenceArrayImpl instance + public override object Initialize(object pairs) + { + _pairs = pairs; + + global::System.Collections.Generic.KeyValuePair[] unboxedPairArray = pairs as global::System.Collections.Generic.KeyValuePair[]; + + object[] boxedKeyValuePairs = new object[unboxedPairArray.Length]; + + for (int i = 0; i < unboxedPairArray.Length; i++) + { + boxedKeyValuePairs[i] = new CLRIKeyValuePairImpl(ref unboxedPairArray[i]); + } + + // Lets create the IReferenceArrayImpl of the type. + return new ReferenceArrayImpl(boxedKeyValuePairs, PropertyType.InspectableArray); + } + + public override object GetTarget() + { + return (object)_pairs; + } + } +} diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/EventRegistrationTokenTable.cs b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/EventRegistrationTokenTable.cs index d7515afd9b..c89fe9e292 100644 --- a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/EventRegistrationTokenTable.cs +++ b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/EventRegistrationTokenTable.cs @@ -24,12 +24,10 @@ namespace System.Runtime.InteropServices.WindowsRuntime { // T must be a delegate type, but we cannot constrain on being a delegate. Therefore, we'll do a // static check at construction time - /* if (!typeof(Delegate).IsAssignableFrom(typeof(T))) - { - throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EventTokenTableRequiresDelegate", typeof(T))); + { + throw new InvalidOperationException(SR.Format(SR.TypeNotDelegate,typeof(T))); } - */ } // The InvocationList property provides access to a delegate which will invoke every registered event handler @@ -279,14 +277,15 @@ namespace System.Runtime.InteropServices.WindowsRuntime where TKey : class where TValue : class { - TValue value; - TKey foundKey = table.FindEquivalentKeyUnsafe(key, out value); - if (foundKey == default(TKey)) + foreach (KeyValuePair item in table) { - value = callback(key); - table.Add(key, value); + if (Object.Equals(item.Key, key)) + return item.Value; + } + TValue value = callback(key); + table.Add(key, value); return value; } } diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/IPropertyValue.cs b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/IPropertyValue.cs new file mode 100644 index 0000000000..04ddbb128f --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/IPropertyValue.cs @@ -0,0 +1,59 @@ +// 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 Windows.Foundation; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + [ComImport] + [Guid("4bd682dd-7554-40e9-9a9b-82654ede7e62")] + [WindowsRuntimeImport] + public interface IPropertyValue + { + PropertyType get_Type(); + + bool IsNumericScalar + { + get; + } + + Byte GetUInt8(); + Int16 GetInt16(); + UInt16 GetUInt16(); + Int32 GetInt32(); + UInt32 GetUInt32(); + Int64 GetInt64(); + UInt64 GetUInt64(); + Single GetSingle(); + Double GetDouble(); + char GetChar16(); + Boolean GetBoolean(); + String GetString(); + Guid GetGuid(); + DateTimeOffset GetDateTime(); + TimeSpan GetTimeSpan(); + Point GetPoint(); + Size GetSize(); + Rect GetRect(); + void GetUInt8Array(out byte[] array); + void GetInt16Array(out Int16[] array); + void GetUInt16Array(out UInt16[] array); + void GetInt32Array(out Int32[] array); + void GetUInt32Array(out UInt32[] array); + void GetInt64Array(out Int64[] array); + void GetUInt64Array(out UInt64[] array); + void GetSingleArray(out Single[] array); + void GetDoubleArray(out Double[] array); + void GetChar16Array(out char[] array); + void GetBooleanArray(out Boolean[] array); + void GetStringArray(out String[] array); + void GetInspectableArray(out object[] array); + void GetGuidArray(out Guid[] array); + void GetDateTimeArray(out DateTimeOffset[] array); + void GetTimeSpanArray(out TimeSpan[] array); + void GetPointArray(out Point[] array); + void GetSizeArray(out Size[] array); + void GetRectArray(out Rect[] array); + } +} diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/IReadOnlyListAdapter.cs b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/IReadOnlyListAdapter.cs new file mode 100644 index 0000000000..bd082361a4 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/IReadOnlyListAdapter.cs @@ -0,0 +1,26 @@ +// 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.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + /// + /// IReadOnlyCollection is generic, so we can't invoke Count without knowing T. Instead, we + // introduce a new non-generic interface for our adapters, so we can have a non-generic helper + // for the call. + /// + public interface IReadOnlyCollectionAdapter + { + int Count { get; } + } + + public interface IReadOnlyListAdapter : IReadOnlyCollectionAdapter + { + T this[int index] { get; } + } +} diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/IReferenceArray.cs b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/IReferenceArray.cs new file mode 100644 index 0000000000..d10421bd6b --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/IReferenceArray.cs @@ -0,0 +1,15 @@ +// 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. + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + [ComImport] + [Guid("61c17707-2d65-11e0-9ae8-d48564015472")] + [WindowsRuntimeImport] + // T can be any WinRT-compatible type, including reference types. + public interface IReferenceArray : IPropertyValue + { + T[] get_Value(); + } +} diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/PropertyType.cs b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/PropertyType.cs new file mode 100644 index 0000000000..783383eb06 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/PropertyType.cs @@ -0,0 +1,51 @@ +// 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. + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + public enum PropertyType + { + Empty = 0, + UInt8 = 1, + Int16 = 2, + UInt16 = 3, + Int32 = 4, + UInt32 = 5, + Int64 = 6, + UInt64 = 7, + Single = 8, + Double = 9, + Char16 = 10, + Boolean = 11, + String = 12, + Inspectable = 13, + DateTime = 14, + TimeSpan = 15, + Guid = 16, + Point = 17, + Size = 18, + Rect = 19, + OtherType = 20, + UInt8Array = 1025, + Int16Array = 1026, + UInt16Array = 1027, + Int32Array = 1028, + UInt32Array = 1029, + Int64Array = 1030, + UInt64Array = 1031, + SingleArray = 1032, + DoubleArray = 1033, + Char16Array = 1034, + BooleanArray = 1035, + StringArray = 1036, + InspectableArray = 1037, + DateTimeArray = 1038, + TimeSpanArray = 1039, + GuidArray = 1040, + PointArray = 1041, + SizeArray = 1042, + RectArray = 1043, + OtherTypeArray = 1044 + } +} diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/PropertyValueImpl.cs b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/PropertyValueImpl.cs new file mode 100644 index 0000000000..d4df5c9ddd --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/PropertyValueImpl.cs @@ -0,0 +1,477 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.WindowsRuntime; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + [System.Runtime.CompilerServices.DependencyReductionRootAttribute] + [McgInternalTypeAttribute] + public class PropertyValueImpl : BoxedValue, IPropertyValue + { + internal PropertyValueImpl(object val, int type) : base(val, type) + { + } + + public PropertyType get_Type() + { + return (PropertyType)m_type; + } + + public bool IsNumericScalar + { + get + { + return IsNumericScalarImpl((PropertyType)m_type, m_data); + } + } + + public byte GetUInt8() + { + return CoerceScalarValue(PropertyType.UInt8); + } + + public short GetInt16() + { + return CoerceScalarValue(PropertyType.Int16); + } + + public ushort GetUInt16() + { + return CoerceScalarValue(PropertyType.UInt16); + } + + public int GetInt32() + { + return CoerceScalarValue(PropertyType.Int32); + } + + public uint GetUInt32() + { + return CoerceScalarValue(PropertyType.UInt32); + } + + public long GetInt64() + { + return CoerceScalarValue(PropertyType.Int64); + } + + public ulong GetUInt64() + { + return CoerceScalarValue(PropertyType.UInt64); + } + + public float GetSingle() + { + return CoerceScalarValue(PropertyType.Single); + } + + public double GetDouble() + { + return CoerceScalarValue(PropertyType.Double); + } + + public char GetChar16() + { + CheckType(PropertyType.Char16); + return (char)m_data; + } + + public bool GetBoolean() + { + CheckType(PropertyType.Boolean); + return (bool)m_data; + } + + public string GetString() + { + return CoerceScalarValue(PropertyType.String); + } + + public object GetInspectable() + { + CheckType(PropertyType.Inspectable); + return m_data; + } + + public System.Guid GetGuid() + { + return CoerceScalarValue(PropertyType.Guid); + } + + public System.DateTimeOffset GetDateTime() + { + CheckType(PropertyType.DateTime); + return (System.DateTimeOffset)m_data; + } + + public System.TimeSpan GetTimeSpan() + { + CheckType(PropertyType.TimeSpan); + return (System.TimeSpan)m_data; + } + + public global::Windows.Foundation.Point GetPoint() + { + CheckType(PropertyType.Point); + return (global::Windows.Foundation.Point)m_data; + } + + public global::Windows.Foundation.Size GetSize() + { + CheckType(PropertyType.Size); + return (global::Windows.Foundation.Size)m_data; + } + + public global::Windows.Foundation.Rect GetRect() + { + CheckType(PropertyType.Rect); + return (global::Windows.Foundation.Rect)m_data; + } + + public void GetUInt8Array(out byte[] array) + { + array = CoerceArrayValue(PropertyType.UInt8Array); + } + + public void GetInt16Array(out short[] array) + { + array = CoerceArrayValue(PropertyType.Int16Array); + } + + public void GetUInt16Array(out ushort[] array) + { + array = CoerceArrayValue(PropertyType.UInt16Array); + } + + public void GetInt32Array(out int[] array) + { + array = CoerceArrayValue(PropertyType.Int32Array); + } + + public void GetUInt32Array(out uint[] array) + { + array = CoerceArrayValue(PropertyType.UInt32Array); + } + + public void GetInt64Array(out long[] array) + { + array = CoerceArrayValue(PropertyType.Int64Array); + } + + public void GetUInt64Array(out ulong[] array) + { + array = CoerceArrayValue(PropertyType.UInt64Array); + } + + public void GetSingleArray(out float[] array) + { + array = CoerceArrayValue(PropertyType.SingleArray); + } + + public void GetDoubleArray(out double[] array) + { + array = CoerceArrayValue(PropertyType.DoubleArray); + } + + public void GetChar16Array(out char[] array) + { + CheckType(PropertyType.Char16Array); + array = (char[])m_data; + } + + public void GetBooleanArray(out bool[] array) + { + CheckType(PropertyType.BooleanArray); + array = (bool[])m_data; + } + + public void GetStringArray(out string[] array) + { + array = CoerceArrayValue(PropertyType.StringArray); + } + + public void GetInspectableArray(out object[] array) + { + CheckType(PropertyType.InspectableArray); + array = (object[])m_data; + } + + public void GetGuidArray(out System.Guid[] array) + { + array = CoerceArrayValue(PropertyType.GuidArray); + } + + public void GetDateTimeArray(out System.DateTimeOffset[] array) + { + CheckType(PropertyType.DateTimeArray); + array = (System.DateTimeOffset[])m_data; + } + + public void GetTimeSpanArray(out System.TimeSpan[] array) + { + CheckType(PropertyType.TimeSpanArray); + array = (System.TimeSpan[])m_data; + } + + public void GetPointArray(out global::Windows.Foundation.Point[] array) + { + CheckType(PropertyType.PointArray); + array = (global::Windows.Foundation.Point[])m_data; + } + + public void GetSizeArray(out global::Windows.Foundation.Size[] array) + { + CheckType(PropertyType.SizeArray); + array = (global::Windows.Foundation.Size[])m_data; + } + + public void GetRectArray(out global::Windows.Foundation.Rect[] array) + { + CheckType(PropertyType.RectArray); + array = (global::Windows.Foundation.Rect[])m_data; + } + + private T[] CoerceArrayValue(PropertyType unboxType) + { + // If we contain the type being looked for directly, then take the fast-path + if (m_type == (int)unboxType) + { + return (T[])m_data; + } + + // Make sure we have an array to begin with + System.Array dataArray = m_data as System.Array; + + if (dataArray == null) + { + throw CreateExceptionForInvalidCast((PropertyType)m_type, unboxType); + } + + // Array types are 1024 larger than their equivilent scalar counterpart + if ((m_type <= 1024) || ((int)unboxType <= 1024)) + { + throw CreateExceptionForInvalidCast((PropertyType)m_type, unboxType); + } + + PropertyType scalarType = (PropertyType)(m_type - 1024); + PropertyType unboxTypeT = unboxType - 1024; + + // If we do not have the correct array type, then we need to convert the array element-by-element + // to a new array of the requested type + T[] coercedArray = new T[dataArray.Length]; + + for (int i = 0; i < dataArray.Length; ++i) + { + coercedArray[i] = (T)CoerceScalarValue(scalarType, dataArray.GetValue(i), unboxTypeT); + } + + return coercedArray; + } + + [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private T CoerceScalarValue(PropertyType unboxType) + { + object result = m_data; + + // If we are just a boxed version of the requested type, then take the fast path out + if (m_type != (int)unboxType) + { + result = CoerceScalarValue((PropertyType)m_type, result, unboxType); + } + + return (T)result; + } + + static private object CoerceScalarValue(PropertyType type, object value, PropertyType unboxType) + { + // If the property type is neither one of the coercable numeric types nor IInspectable, we + // should not attempt coersion, even if the underlying value is technically convertable + if ((type == PropertyType.Guid) && (unboxType == PropertyType.String)) + { + // String <--> Guid is allowed + return ((System.Guid)value).ToString(); + } + else if ((type == PropertyType.String) && (unboxType == PropertyType.Guid)) + { + System.Guid result; + + if (System.Guid.TryParse((string)value, out result)) + { + return result; + } + } + else if (type == PropertyType.Inspectable) + { + // If the property type is IInspectable, and we have a nested IPropertyValue, then we need + // to pass along the request to coerce the value. + IPropertyValue ipv = value as IPropertyValue; + + if (ipv != null) + { + object result = ReferenceUtility.GetWellKnownScalar(ipv, unboxType); + + if (result != null) + { + return result; + } + + Debug.Assert( + false, + "T in coersion function wasn't understood as a type that can be coerced - make sure that CoerceScalarValue and NumericScalarTypes are in sync" + ); + } + } + else if (type == PropertyType.Boolean || type == PropertyType.Char16) + { + throw CreateExceptionForInvalidCoersion(type, value, unboxType, Interop.COM.TYPE_E_TYPEMISMATCH); + } + + // + // Let Convert handle all possible conversions - this include + // 1. string - which desktop code accidentally allowed + // 2. object (IInspectable) + // + try + { + switch (unboxType) + { + case PropertyType.UInt8: + return System.Convert.ToByte(value); + + case PropertyType.Int16: + return System.Convert.ToInt16(value); + + case PropertyType.UInt16: + return System.Convert.ToUInt16(value); + + case PropertyType.Int32: + return System.Convert.ToInt32(value); + + case PropertyType.UInt32: + return System.Convert.ToUInt32(value); + + case PropertyType.Int64: + return System.Convert.ToInt64(value); + + case PropertyType.UInt64: + return System.Convert.ToUInt64(value); + + case PropertyType.Single: + return System.Convert.ToSingle(value); + + case PropertyType.Double: + return System.Convert.ToDouble(value); + + default: + break; + } + } + catch (System.FormatException) + { + throw CreateExceptionForInvalidCoersion(type, value, unboxType, Interop.COM.TYPE_E_TYPEMISMATCH); + } + catch (System.InvalidCastException) + { + throw CreateExceptionForInvalidCoersion(type, value, unboxType, Interop.COM.TYPE_E_TYPEMISMATCH); + } + catch (System.OverflowException) + { + throw CreateExceptionForInvalidCoersion(type, value, unboxType, Interop.COM.DISP_E_OVERFLOW); + } + + throw CreateExceptionForInvalidCast(type, unboxType); + } + + private static bool IsNumericScalarImpl(PropertyType type, object data) + { + switch (type) + { + case PropertyType.UInt8: + case PropertyType.Int16: + case PropertyType.UInt16: + case PropertyType.Int32: + case PropertyType.UInt32: + case PropertyType.Int64: + case PropertyType.UInt64: + case PropertyType.Single: + case PropertyType.Double: + return true; + + default: + return McgMarshal.IsEnum(data); + } + } + + private void CheckType(PropertyType unboxType) + { + if (this.get_Type() != unboxType) + { + throw CreateExceptionForInvalidCast(this.get_Type(), unboxType); + } + } + + private static System.InvalidCastException CreateExceptionForInvalidCast( + PropertyType type, + PropertyType unboxType) + { + System.InvalidCastException ex = new System.InvalidCastException(SR.Format(SR.PropertyValue_InvalidCast, type, unboxType)); + McgMarshal.SetExceptionErrorCode(ex, Interop.COM.TYPE_E_TYPEMISMATCH); + return ex; + } + + private static System.InvalidCastException CreateExceptionForInvalidCoersion( + PropertyType type, + object value, + PropertyType unboxType, + int hr) + { + InvalidCastException ex = new InvalidCastException(SR.Format(SR.PropertyValue_InvalidCoersion, type, value, unboxType)); + McgMarshal.SetExceptionErrorCode(ex, hr); + return ex; + } + } + + internal class ReferenceUtility + { + internal static object GetWellKnownScalar(IPropertyValue ipv, PropertyType type) + { + switch (type) + { + case PropertyType.UInt8: + return ipv.GetUInt8(); + + case PropertyType.Int16: + return ipv.GetInt16(); + + case PropertyType.UInt16: + return ipv.GetUInt16(); + + case PropertyType.Int32: + return ipv.GetInt32(); + + case PropertyType.UInt32: + return ipv.GetUInt32(); + + case PropertyType.Int64: + return ipv.GetInt64(); + + case PropertyType.UInt64: + return ipv.GetUInt64(); + + case PropertyType.Single: + return ipv.GetSingle(); + + case PropertyType.Double: + return ipv.GetDouble(); + } + + Debug.Assert(false); + return null; + } + } +} diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/ReferenceArrayImpl.cs b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/ReferenceArrayImpl.cs new file mode 100644 index 0000000000..41d366a96f --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/ReferenceArrayImpl.cs @@ -0,0 +1,33 @@ +// 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. + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + /// + /// A managed wrapper of IReferenceArray + /// + [System.Runtime.CompilerServices.DependencyReductionRootAttribute] + [McgInternalTypeAttribute] + public sealed class ReferenceArrayImpl : ReferenceArrayImplBase, IReferenceArray + { + private T[] _value; + + public ReferenceArrayImpl(T[] obj, PropertyType type) : base(obj, type) + { + _value = obj; + m_unboxed = true; + } + + public T[] get_Value() + { + if (!m_unboxed) + { + _value = (T[])m_data; + m_unboxed = true; + } + + return _value; + } + } +} diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/ReferenceArrayImplBase.cs b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/ReferenceArrayImplBase.cs new file mode 100644 index 0000000000..3a71903b23 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/ReferenceArrayImplBase.cs @@ -0,0 +1,178 @@ +// 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. + + +namespace System.Runtime.InteropServices.WindowsRuntime +{ +#pragma warning disable 618 + [System.Runtime.CompilerServices.DependencyReductionRootAttribute] + [McgInternalTypeAttribute] + public class ReferenceArrayImplBase : + PropertyValueImpl, + System.Collections.IList, + System.Collections.Generic.IEnumerable, + System.Runtime.InteropServices.ICustomQueryInterface + { + // pinterface({faa585ea-6214-4217-afda-7f46de5869b3};cinterface(IInspectable)) + internal static System.Guid IID_IIterableOfObject = + new System.Guid(153846939, 24753, 21182, 0xA4, 0x4A, 0x6F, 0xE8, 0xE9, 0x33, 0xCB, 0xE4); + + private System.Collections.IList _list; + private System.Collections.Generic.IEnumerable _enumerableOfObject; + + public override void Initialize(object val, int type) + { + m_data = val; + m_type = (short)type; + + // This should not fail but I'm making a cast here anyway just in case + // we have a bug or there is a runtime failure + _list = (System.Collections.IList)val; + + // Not every array implements IEnumerable + _enumerableOfObject = val as System.Collections.Generic.IEnumerable; + } + + internal ReferenceArrayImplBase(object data, PropertyType type) : base(data, (int)type) + { + Initialize(data, (int)type); + } + + // + // Customize QI behavior: + // If this array type doesn't implement IEnumerable, reject IIterable + // + System.Runtime.InteropServices.CustomQueryInterfaceResult ICustomQueryInterface.GetInterface(ref System.Guid iid, out System.IntPtr ppv) + { + ppv = default(System.IntPtr); + + if (System.Runtime.InteropServices.McgMarshal.GuidEquals(ref iid, ref IID_IIterableOfObject)) + { + if (_enumerableOfObject == null) + { + // This array type doesn't actually support IEnumerable + // Reject the QI + return System.Runtime.InteropServices.CustomQueryInterfaceResult.Failed; + } + } + + return System.Runtime.InteropServices.CustomQueryInterfaceResult.NotHandled; + } + + // + // IEnumerable methods. Used by data-binding in Jupiter when you try to data bind + // against a managed array + // + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return ((System.Collections.IEnumerable)_list).GetEnumerator(); + } + + // + // IEnumerable methods. We need this because System.Array implement this (implicitly) if it is castable to object[] + // + System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() + { + return _enumerableOfObject.GetEnumerator(); + } + + // + // IList & ICollection methods. + // This enables two-way data binding and index access in Jupiter + // + object System.Collections.IList.this[int index] + { + get + { + return _list[index]; + } + + set + { + _list[index] = value; + } + } + + int System.Collections.IList.Add(object value) + { + return _list.Add(value); + } + + bool System.Collections.IList.Contains(object value) + { + return _list.Contains(value); + } + + void System.Collections.IList.Clear() + { + _list.Clear(); + } + + bool System.Collections.IList.IsReadOnly + { + get + { + return _list.IsReadOnly; + } + } + + bool System.Collections.IList.IsFixedSize + { + get + { + return _list.IsFixedSize; + } + } + + int System.Collections.IList.IndexOf(object value) + { + return _list.IndexOf(value); + } + + void System.Collections.IList.Insert(int index, object value) + { + _list.Insert(index, value); + } + + void System.Collections.IList.Remove(object value) + { + _list.Remove(value); + } + + void System.Collections.IList.RemoveAt(int index) + { + _list.RemoveAt(index); + } + + void System.Collections.ICollection.CopyTo(System.Array array, int index) + { + _list.CopyTo(array, index); + } + + int System.Collections.ICollection.Count + { + get + { + return _list.Count; + } + } + + object System.Collections.ICollection.SyncRoot + { + get + { + return _list.SyncRoot; + } + } + + bool System.Collections.ICollection.IsSynchronized + { + get + { + return _list.IsSynchronized; + } + } + } +#pragma warning restore 169 +} diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/ReferenceImpl.cs b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/ReferenceImpl.cs new file mode 100644 index 0000000000..e82b600204 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/ReferenceImpl.cs @@ -0,0 +1,40 @@ +// 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. + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + /// + /// A managed wrapper for IPropertyValue and IReference + /// + [System.Runtime.CompilerServices.DependencyReductionRootAttribute] + [McgInternalTypeAttribute] + public class ReferenceImpl : PropertyValueImpl, global::Windows.Foundation.IReference + { + private T m_value; + + public ReferenceImpl(T data, int type) + : base(data, type) + { + m_unboxed = true; + m_value = data; + } + + internal ReferenceImpl(T data, PropertyType type) + : base(data, (int)type) + { + m_unboxed = true; + m_value = data; + } + + public T get_Value() + { + if (!m_unboxed) + { + m_value = (T)m_data; + m_unboxed = true; + } + return m_value; + } + } +} diff --git a/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/__IUnboxInternal.cs b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/__IUnboxInternal.cs new file mode 100644 index 0000000000..1cad7be84d --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/System/Runtime/InteropServices/WindowsRuntime/__IUnboxInternal.cs @@ -0,0 +1,18 @@ +// 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.Runtime.InteropServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + /// + /// Interface to help get unboxed value from IReference/IReferenceArray/IKeyValuePair + /// + [System.Runtime.CompilerServices.DependencyReductionRootAttribute] + internal interface __IUnboxInternal + { + Object get_Value(Object obj); + } +} diff --git a/external/corert/src/System.Private.Interop/src/TypeForwarders.cs b/external/corert/src/System.Private.Interop/src/TypeForwarders.cs index badf604774..03271b59aa 100644 --- a/external/corert/src/System.Private.Interop/src/TypeForwarders.cs +++ b/external/corert/src/System.Private.Interop/src/TypeForwarders.cs @@ -1,14 +1,6 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. -[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.SafeBuffer))] -[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.MarshalAsAttribute))] -[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.UnmanagedType))] -[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.VarEnum))] -[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.CriticalHandle))] -[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.InAttribute))] -[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.DllNotFoundException))] -[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.EntryPointNotFoundException))] -[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.MarshalDirectiveException))] -[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.BestFitMappingAttribute))] \ No newline at end of file +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.GuidAttribute))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.InteropServices.HandleRef))] diff --git a/external/corert/src/System.Private.Interop/src/WinRT/ExceptionHelpers.cs b/external/corert/src/System.Private.Interop/src/WinRT/ExceptionHelpers.cs index 5970d99ecf..c023d5c06d 100644 --- a/external/corert/src/System.Private.Interop/src/WinRT/ExceptionHelpers.cs +++ b/external/corert/src/System.Private.Interop/src/WinRT/ExceptionHelpers.cs @@ -32,7 +32,7 @@ namespace System.Runtime.InteropServices { // Get the errorDetails associated with the restrictedErrorInfo. __com_IRestrictedErrorInfo* pComRestrictedErrorInfo = (__com_IRestrictedErrorInfo*)pRestrictedErrorInfo; - result = CalliIntrinsics.StdCall( + result = CalliIntrinsics.StdCall__int( pComRestrictedErrorInfo->pVtable->pfnGetErrorDetails, pRestrictedErrorInfo, out pErrDes, @@ -75,7 +75,7 @@ namespace System.Runtime.InteropServices try { __com_IRestrictedErrorInfo* pComRestrictedErrorInfo = (__com_IRestrictedErrorInfo*)pRestrictedErrorInfo; - int result = CalliIntrinsics.StdCall(pComRestrictedErrorInfo->pVtable->pfnGetReference, pRestrictedErrorInfo, out pReference); + int result = CalliIntrinsics.StdCall__int(pComRestrictedErrorInfo->pVtable->pfnGetReference, pRestrictedErrorInfo, out pReference); if (result >= 0) { errReference = Interop.COM.ConvertBSTRToString(pReference); @@ -564,7 +564,7 @@ namespace System.Runtime.InteropServices exception = new System.Security.VerificationException(); break; case __HResults.E_NOTIMPL: - exception = new NotImplementedException(); + exception = NotImplemented.ByDesign; break; case __HResults.E_OUTOFMEMORY: case __HResults.CTL_E_OUTOFMEMORY: @@ -795,7 +795,7 @@ namespace System.Runtime.InteropServices // We have an LanguageExceptionErrorInfo. IntPtr pUnk; __com_ILanguageExceptionErrorInfo* pComLanguageExceptionErrorInfo = (__com_ILanguageExceptionErrorInfo*)pLanguageExceptionErrorInfo; - int result = CalliIntrinsics.StdCall(pComLanguageExceptionErrorInfo->pVtable->pfnGetLanguageException, pLanguageExceptionErrorInfo, out pUnk); + int result = CalliIntrinsics.StdCall__int(pComLanguageExceptionErrorInfo->pVtable->pfnGetLanguageException, pLanguageExceptionErrorInfo, out pUnk); McgMarshal.ComSafeRelease(pLanguageExceptionErrorInfo); if (result >= 0 && pUnk != IntPtr.Zero) diff --git a/external/corert/src/System.Private.Interop/src/WinRT/Interop.WinRT.cs b/external/corert/src/System.Private.Interop/src/WinRT/Interop.WinRT.cs deleted file mode 100644 index 31a7f252ba..0000000000 --- a/external/corert/src/System.Private.Interop/src/WinRT/Interop.WinRT.cs +++ /dev/null @@ -1,95 +0,0 @@ -// 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.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Text; - -partial class Interop -{ - - internal unsafe partial class WinRT - { - // Converts a ASCII (code<0x7f) string to byte array - internal static byte[] AsciiStringToByteArray(string ascii) - { - byte[] ret = new byte[ascii.Length + 1]; - int index; - - for (index = 0; index < ascii.Length; index++) - { - ret[index] = (byte)ascii[index]; - } - - ret[index] = 0; - - return ret; - } - - internal static unsafe void RoGetActivationFactory(string className, ref Guid iid, out IntPtr ppv) - { - fixed (char* unsafe_className = className) - { - void* hstring_typeName = null; - - HSTRING_HEADER hstringHeader; - int hr = - WindowsCreateStringReference( - unsafe_className, (uint)className.Length, &hstringHeader, &hstring_typeName); - - if (hr < 0) - throw Marshal.GetExceptionForHR(hr); - - fixed (Guid* unsafe_iid = &iid) - { - fixed (void* unsafe_ppv = &ppv) - { - hr = ExternalInterop.RoGetActivationFactory( - hstring_typeName, - unsafe_iid, - unsafe_ppv); - - if (hr < 0) - throw Marshal.GetExceptionForHR(hr); - } - } - } - } - - [DllImport(Interop.CORE_WINRT_STRING)] - [McgGeneratedNativeCallCodeAttribute] - [MethodImplAttribute(MethodImplOptions.NoInlining)] - internal static extern unsafe int WindowsCreateString(char* sourceString, uint length, void* hstring); - - [DllImport(Interop.CORE_WINRT_STRING)] - [McgGeneratedNativeCallCodeAttribute] - [MethodImplAttribute(MethodImplOptions.NoInlining)] - internal static extern unsafe int WindowsCreateStringReference( - char* sourceString, uint length, HSTRING_HEADER* phstringHeader, void* hstring); - - [DllImport(Interop.CORE_WINRT_ERROR, PreserveSig = true)] - [McgGeneratedNativeCallCodeAttribute] - public static extern int GetRestrictedErrorInfo(out System.IntPtr pRestrictedErrorInfo); - - [DllImport(Interop.CORE_WINRT_ERROR, PreserveSig = true)] - [McgGeneratedNativeCallCodeAttribute] - [MethodImplAttribute(MethodImplOptions.NoInlining)] - public static extern int RoOriginateError(int hr, HSTRING hstring); - - [DllImport(Interop.CORE_WINRT_ERROR, PreserveSig = true)] - [McgGeneratedNativeCallCodeAttribute] - [MethodImplAttribute(MethodImplOptions.NoInlining)] - public static extern int SetRestrictedErrorInfo(System.IntPtr pRestrictedErrorInfo); - - [DllImport(Interop.CORE_WINRT_ERROR1, PreserveSig = true)] - [McgGeneratedNativeCallCodeAttribute] - internal static extern int RoOriginateLanguageException(int hr, HSTRING message, IntPtr pLanguageException); - - [DllImport(Interop.CORE_WINRT_ERROR1, PreserveSig = true)] - [McgGeneratedNativeCallCodeAttribute] - internal static extern int RoReportUnhandledError(IntPtr pRestrictedErrorInfo); - } - -} diff --git a/external/corert/src/System.Private.Interop/src/Windows/Foundation/Collections/IKeyValuePair.cs b/external/corert/src/System.Private.Interop/src/Windows/Foundation/Collections/IKeyValuePair.cs new file mode 100644 index 0000000000..88ba754372 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/Windows/Foundation/Collections/IKeyValuePair.cs @@ -0,0 +1,18 @@ +// 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.Runtime.InteropServices; +using System.Runtime.InteropServices.WindowsRuntime; + +namespace Windows.Foundation.Collections +{ + [ComImport] + [Guid("02b51929-c1c4-4a7e-8940-0312b5c18500")] + [WindowsRuntimeImport] + public unsafe interface IKeyValuePair + { + K get_Key(); + V get_Value(); + } +} diff --git a/external/corert/src/System.Private.Interop/src/Windows/Foundation/IReference.cs b/external/corert/src/System.Private.Interop/src/Windows/Foundation/IReference.cs new file mode 100644 index 0000000000..0533b01b03 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/Windows/Foundation/IReference.cs @@ -0,0 +1,13 @@ +// 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. + +namespace Windows.Foundation +{ + [global::System.Runtime.CompilerServices.DependencyReductionRootAttribute] + public interface IReference + { + [global::System.Runtime.InteropServices.McgAccessor(global::System.Runtime.InteropServices.McgAccessorKind.PropertyGet, "Value")] + T get_Value(); + } +} diff --git a/external/corert/src/System.Private.Interop/src/Windows/Foundation/Point.cs b/external/corert/src/System.Private.Interop/src/Windows/Foundation/Point.cs new file mode 100644 index 0000000000..aa96bbd369 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/Windows/Foundation/Point.cs @@ -0,0 +1,120 @@ +// 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.Globalization; +using System.Runtime.InteropServices; + +#if !FEATURE_SLJ_PROJECTION_COMPAT +#pragma warning disable 436 // Redefining types from Windows.Foundation +#endif // !FEATURE_SLJ_PROJECTION_COMPAT + +#if FEATURE_SLJ_PROJECTION_COMPAT +namespace System.Windows +#else // !FEATURE_SLJ_PROJECTION_COMPAT + + +namespace Windows.Foundation +#endif // FEATURE_SLJ_PROJECTION_COMPAT +{ + // + // Point is the managed projection of Windows.Foundation.Point. Any changes to the layout + // of this type must be exactly mirrored on the native WinRT side as well. + // + // Note that this type is owned by the Jupiter team. Please contact them before making any + // changes here. + // + [StructLayout(LayoutKind.Sequential)] + public struct Point : IFormattable + { + internal float _x; + internal float _y; + + public Point(double x, double y) + { + _x = (float)x; + _y = (float)y; + } + + public double X + { + get { return _x; } + set { _x = (float)value; } + } + + public double Y + { + get { return _y; } + set { _y = (float)value; } + } + + public override string ToString() + { + // Delegate to the internal method which implements all ToString calls. + return ConvertToString(null /* format string */, null /* format provider */); + } + + public string ToString(IFormatProvider provider) + { + // Delegate to the internal method which implements all ToString calls. + return ConvertToString(null /* format string */, provider); + } + + string IFormattable.ToString(string format, IFormatProvider provider) + { + // Delegate to the internal method which implements all ToString calls. + return ConvertToString(format, provider); + } + + private string ConvertToString(string format, IFormatProvider provider) + { + // Helper to get the numeric list separator for a given culture. + char separator = TokenizerHelper.GetNumericListSeparator(provider); + return String.Format(provider, + "{1:" + format + "}{0}{2:" + format + "}", + separator, + _x, + _y); + } + + public static bool operator ==(Point point1, Point point2) + { + return point1.X == point2.X && + point1.Y == point2.Y; + } + + public static bool operator !=(Point point1, Point point2) + { + return !(point1 == point2); + } + + public override bool Equals(object o) + { + if ((null == o) || !(o is Point)) + { + return false; + } + + Point value = (Point)o; + return (this == value); + } + + public bool Equals(Point value) + { + return (this == value); + } + + public override int GetHashCode() + { + // Perform field-by-field XOR of HashCodes + return X.GetHashCode() ^ + Y.GetHashCode(); + } + } +} + +#if !FEATURE_SLJ_PROJECTION_COMPAT +#pragma warning restore 436 +#endif // !FEATURE_SLJ_PROJECTION_COMPAT + diff --git a/external/corert/src/System.Private.Interop/src/Windows/Foundation/Rect.cs b/external/corert/src/System.Private.Interop/src/Windows/Foundation/Rect.cs new file mode 100644 index 0000000000..91e1ca46ff --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/Windows/Foundation/Rect.cs @@ -0,0 +1,367 @@ +// 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.Globalization; +using System.Runtime.InteropServices; + +#if !FEATURE_SLJ_PROJECTION_COMPAT +#pragma warning disable 436 // Redefining types from Windows.Foundation +#endif // !FEATURE_SLJ_PROJECTION_COMPAT + +#if FEATURE_SLJ_PROJECTION_COMPAT +namespace System.Windows +#else // !FEATURE_SLJ_PROJECTION_COMPAT + + +namespace Windows.Foundation +#endif // FEATURE_SLJ_PROJECTION_COMPAT +{ + // + // Rect is the managed projection of Windows.Foundation.Rect. Any changes to the layout + // of this type must be exactly mirrored on the native WinRT side as well. + // + // Note that this type is owned by the Jupiter team. Please contact them before making any + // changes here. + // + [StructLayout(LayoutKind.Sequential)] + public struct Rect : IFormattable + { + private float _x; + private float _y; + private float _width; + private float _height; + + private const double EmptyX = Double.PositiveInfinity; + private const double EmptyY = Double.PositiveInfinity; + private const double EmptyWidth = Double.NegativeInfinity; + private const double EmptyHeight = Double.NegativeInfinity; + + private readonly static Rect s_empty = CreateEmptyRect(); + + public Rect(double x, + double y, + double width, + double height) + { + if (width < 0) + throw new ArgumentException("width"); + if (height < 0) + throw new ArgumentException("height"); + + _x = (float)x; + _y = (float)y; + _width = (float)width; + _height = (float)height; + } + + public Rect(Point point1, + Point point2) + { + _x = (float)Math.Min(point1.X, point2.X); + _y = (float)Math.Min(point1.Y, point2.Y); + + _width = (float)Math.Max(Math.Max(point1.X, point2.X) - _x, 0); + _height = (float)Math.Max(Math.Max(point1.Y, point2.Y) - _y, 0); + } + + public Rect(Point location, Size size) + { + if (size.IsEmpty) + { + this = s_empty; + } + else + { + _x = (float)location.X; + _y = (float)location.Y; + _width = (float)size.Width; + _height = (float)size.Height; + } + } + + internal static Rect Create(double x, + double y, + double width, + double height) + { + if (x == EmptyX && y == EmptyY && width == EmptyWidth && height == EmptyHeight) + { + return Rect.Empty; + } + else + { + return new Rect(x, y, width, height); + } + } + + + public double X + { + get { return _x; } + set { _x = (float)value; } + } + + public double Y + { + get { return _y; } + set { _y = (float)value; } + } + + public double Width + { + get { return _width; } + set + { + if (value < 0) + throw new ArgumentException("Width"); + + _width = (float)value; + } + } + + public double Height + { + get { return _height; } + set + { + if (value < 0) + throw new ArgumentException("Height"); + + _height = (float)value; + } + } + + public double Left + { + get { return _x; } + } + + public double Top + { + get { return _y; } + } + + public double Right + { + get + { + if (IsEmpty) + { + return Double.NegativeInfinity; + } + + return _x + _width; + } + } + + public double Bottom + { + get + { + if (IsEmpty) + { + return Double.NegativeInfinity; + } + + return _y + _height; + } + } + + public static Rect Empty + { + get { return s_empty; } + } + + public bool IsEmpty + { + get { return _width < 0; } + } + + public bool Contains(Point point) + { + return ContainsInternal(point.X, point.Y); + } + + public void Intersect(Rect rect) + { + if (!this.IntersectsWith(rect)) + { + this = s_empty; + } + else + { + double left = Math.Max(X, rect.X); + double top = Math.Max(Y, rect.Y); + + // Max with 0 to prevent double weirdness from causing us to be (-epsilon..0) + Width = Math.Max(Math.Min(X + Width, rect.X + rect.Width) - left, 0); + Height = Math.Max(Math.Min(Y + Height, rect.Y + rect.Height) - top, 0); + + X = left; + Y = top; + } + } + + public void Union(Rect rect) + { + if (IsEmpty) + { + this = rect; + } + else if (!rect.IsEmpty) + { + double left = Math.Min(Left, rect.Left); + double top = Math.Min(Top, rect.Top); + + + // We need this check so that the math does not result in NaN + if ((rect.Width == Double.PositiveInfinity) || (Width == Double.PositiveInfinity)) + { + Width = Double.PositiveInfinity; + } + else + { + // Max with 0 to prevent double weirdness from causing us to be (-epsilon..0) + double maxRight = Math.Max(Right, rect.Right); + Width = Math.Max(maxRight - left, 0); + } + + // We need this check so that the math does not result in NaN + if ((rect.Height == Double.PositiveInfinity) || (Height == Double.PositiveInfinity)) + { + Height = Double.PositiveInfinity; + } + else + { + // Max with 0 to prevent double weirdness from causing us to be (-epsilon..0) + double maxBottom = Math.Max(Bottom, rect.Bottom); + Height = Math.Max(maxBottom - top, 0); + } + + X = left; + Y = top; + } + } + + public void Union(Point point) + { + Union(new Rect(point, point)); + } + + private bool ContainsInternal(double x, double y) + { + return ((x >= X) && (x - Width <= X) && + (y >= Y) && (y - Height <= Y)); + } + + internal bool IntersectsWith(Rect rect) + { + if (Width < 0 || rect.Width < 0) + { + return false; + } + + return (rect.X <= X + Width) && + (rect.X + rect.Width >= X) && + (rect.Y <= Y + Height) && + (rect.Y + rect.Height >= Y); + } + + private static Rect CreateEmptyRect() + { + Rect rect = new Rect(); + + // TODO: for consistency with width/height we should change these + // to assign directly to the backing fields. + rect.X = EmptyX; + rect.Y = EmptyY; + + // the width and height properties prevent assignment of + // negative numbers so assign directly to the backing fields. + rect._width = (float)EmptyWidth; + rect._height = (float)EmptyHeight; + + return rect; + } + + public override string ToString() + { + // Delegate to the internal method which implements all ToString calls. + return ConvertToString(null /* format string */, null /* format provider */); + } + + public string ToString(IFormatProvider provider) + { + // Delegate to the internal method which implements all ToString calls. + return ConvertToString(null /* format string */, provider); + } + + string IFormattable.ToString(string format, IFormatProvider provider) + { + // Delegate to the internal method which implements all ToString calls. + return ConvertToString(format, provider); + } + + internal string ConvertToString(string format, IFormatProvider provider) + { + if (IsEmpty) + { + return SR.DirectUI_Empty; + } + + // Helper to get the numeric list separator for a given culture. + char separator = TokenizerHelper.GetNumericListSeparator(provider); + return String.Format(provider, + "{1:" + format + "}{0}{2:" + format + "}{0}{3:" + format + "}{0}{4:" + format + "}", + separator, + _x, + _y, + _width, + _height); + } + + public bool Equals(Rect value) + { + return (this == value); + } + + public static bool operator ==(Rect rect1, Rect rect2) + { + return rect1.X == rect2.X && + rect1.Y == rect2.Y && + rect1.Width == rect2.Width && + rect1.Height == rect2.Height; + } + + public static bool operator !=(Rect rect1, Rect rect2) + { + return !(rect1 == rect2); + } + + public override bool Equals(object o) + { + if ((null == o) || !(o is Rect)) + { + return false; + } + + Rect value = (Rect)o; + return (this == value); + } + + public override int GetHashCode() + { + // Perform field-by-field XOR of HashCodes + return X.GetHashCode() ^ + Y.GetHashCode() ^ + Width.GetHashCode() ^ + Height.GetHashCode(); + } + } +} + +#if !FEATURE_SLJ_PROJECTION_COMPAT +#pragma warning restore 436 +#endif // !FEATURE_SLJ_PROJECTION_COMPAT diff --git a/external/corert/src/System.Private.Interop/src/Windows/Foundation/Size.cs b/external/corert/src/System.Private.Interop/src/Windows/Foundation/Size.cs new file mode 100644 index 0000000000..3e52f80366 --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/Windows/Foundation/Size.cs @@ -0,0 +1,159 @@ +// 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.Runtime.InteropServices; + +#if !FEATURE_SLJ_PROJECTION_COMPAT +#pragma warning disable 436 // Redefining types from Windows.Foundation +#endif // !FEATURE_SLJ_PROJECTION_COMPAT + +#if FEATURE_SLJ_PROJECTION_COMPAT +namespace System.Windows +#else // !FEATURE_SLJ_PROJECTION_COMPAT + + +namespace Windows.Foundation +#endif // FEATURE_SLJ_PROJECTION_COMPAT +{ + // + // Size is the managed projection of Windows.Foundation.Size. Any changes to the layout + // of this type must be exactly mirrored on the native WinRT side as well. + // + // Note that this type is owned by the Jupiter team. Please contact them before making any + // changes here. + // + [StructLayout(LayoutKind.Sequential)] + public struct Size + { + private float _width; + private float _height; + + private readonly static Size s_empty = CreateEmptySize(); + + public Size(double width, double height) + { + if (width < 0) + throw new ArgumentException("width"); + if (height < 0) + throw new ArgumentException("height"); + + _width = (float)width; + _height = (float)height; + } + + public double Width + { + get { return _width; } + set + { + if (value < 0) + throw new ArgumentException("Width"); + + _width = (float)value; + } + } + + public double Height + { + get { return _height; } + set + { + if (value < 0) + throw new ArgumentException("Height"); + + _height = (float)value; + } + } + + public static Size Empty + { + get { return s_empty; } + } + + + public bool IsEmpty + { + get { return Width < 0; } + } + + static private Size CreateEmptySize() + { + Size size = new Size(); + // We can't set these via the property setters because negatives widths + // are rejected in those APIs. + size._width = Single.NegativeInfinity; + size._height = Single.NegativeInfinity; + return size; + } + + public static bool operator ==(Size size1, Size size2) + { + return size1.Width == size2.Width && + size1.Height == size2.Height; + } + + public static bool operator !=(Size size1, Size size2) + { + return !(size1 == size2); + } + + public override bool Equals(object o) + { + if ((null == o) || !(o is Size)) + { + return false; + } + + Size value = (Size)o; + return Size.Equals(this, value); + } + + public bool Equals(Size value) + { + return Size.Equals(this, value); + } + + public override int GetHashCode() + { + if (IsEmpty) + { + return 0; + } + else + { + // Perform field-by-field XOR of HashCodes + return Width.GetHashCode() ^ + Height.GetHashCode(); + } + } + + private static bool Equals(Size size1, Size size2) + { + if (size1.IsEmpty) + { + return size2.IsEmpty; + } + else + { + return size1.Width.Equals(size2.Width) && + size1.Height.Equals(size2.Height); + } + } + + public override string ToString() + { + if (IsEmpty) + { + return "Empty"; + } + + return String.Format("{0},{1}", _width, _height); + } + } +} + +#if !FEATURE_SLJ_PROJECTION_COMPAT +#pragma warning restore 436 +#endif // !FEATURE_SLJ_PROJECTION_COMPAT diff --git a/external/corert/src/System.Private.Interop/src/Windows/Foundation/TokenizerHelper.cs b/external/corert/src/System.Private.Interop/src/Windows/Foundation/TokenizerHelper.cs new file mode 100644 index 0000000000..10242e5c8b --- /dev/null +++ b/external/corert/src/System.Private.Interop/src/Windows/Foundation/TokenizerHelper.cs @@ -0,0 +1,51 @@ +// 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.Globalization; +using System.Runtime.InteropServices; + +#if !FEATURE_SLJ_PROJECTION_COMPAT +#pragma warning disable 436 // Redefining types from Windows.Foundation +#endif // !FEATURE_SLJ_PROJECTION_COMPAT + +#if FEATURE_SLJ_PROJECTION_COMPAT +namespace System.Windows +#else // !FEATURE_SLJ_PROJECTION_COMPAT + + +namespace Windows.Foundation +#endif // FEATURE_SLJ_PROJECTION_COMPAT +{ + // + // Note that this type is owned by the Jupiter team. Please contact them before making any + // changes here. + // + internal static class TokenizerHelper + { + internal static char GetNumericListSeparator(IFormatProvider provider) + { + char numericSeparator = ','; + + // Get the NumberFormatInfo out of the provider, if possible + // If the IFormatProvider doesn't not contain a NumberFormatInfo, then + // this method returns the current culture's NumberFormatInfo. + NumberFormatInfo numberFormat = NumberFormatInfo.GetInstance(provider); + + // Is the decimal separator is the same as the list separator? + // If so, we use the ";". + if ((numberFormat.NumberDecimalSeparator.Length > 0) && (numericSeparator == numberFormat.NumberDecimalSeparator[0])) + { + numericSeparator = ';'; + } + + return numericSeparator; + } + } +} + +#if !FEATURE_SLJ_PROJECTION_COMPAT +#pragma warning restore 436 +#endif // !FEATURE_SLJ_PROJECTION_COMPAT + diff --git a/external/corert/src/System.Private.Jit/src/Internal/Runtime/JitSupport/ExternObjectSymbolNode.cs b/external/corert/src/System.Private.Jit/src/Internal/Runtime/JitSupport/ExternObjectSymbolNode.cs index 812618d50f..82374668c8 100644 --- a/external/corert/src/System.Private.Jit/src/Internal/Runtime/JitSupport/ExternObjectSymbolNode.cs +++ b/external/corert/src/System.Private.Jit/src/Internal/Runtime/JitSupport/ExternObjectSymbolNode.cs @@ -38,7 +38,7 @@ namespace Internal.Runtime.JitSupport /// /// Return a "GenericDictionaryCell" which can be used to get a pointer sized value - /// points to or is what this nodeis used with. + /// points to or is what this node is used with. /// public abstract GenericDictionaryCell GetDictionaryCell(); } diff --git a/external/corert/src/System.Private.Jit/src/Internal/Runtime/JitSupport/JitCompilation.cs b/external/corert/src/System.Private.Jit/src/Internal/Runtime/JitSupport/JitCompilation.cs index 52bc257014..e628978b9e 100644 --- a/external/corert/src/System.Private.Jit/src/Internal/Runtime/JitSupport/JitCompilation.cs +++ b/external/corert/src/System.Private.Jit/src/Internal/Runtime/JitSupport/JitCompilation.cs @@ -23,6 +23,7 @@ namespace ILCompiler _typeGetTypeMethodThunks = new TypeGetTypeMethodThunkCache(context.GetWellKnownType(WellKnownType.Object)); _methodILCache = new ILProvider(new PInvokeILProvider(new PInvokeILEmitterConfiguration(forceLazyResolution: true), null)); _nodeFactory = new NodeFactory(context); + _devirtualizationManager = new DevirtualizationManager(); } private readonly NodeFactory _nodeFactory; @@ -30,6 +31,7 @@ namespace ILCompiler protected readonly Logger _logger = Logger.Null; private readonly TypeGetTypeMethodThunkCache _typeGetTypeMethodThunks; private ILProvider _methodILCache; + private readonly DevirtualizationManager _devirtualizationManager; internal Logger Logger => _logger; @@ -97,6 +99,44 @@ namespace ILCompiler return intrinsicMethod; } + public bool HasFixedSlotVTable(TypeDesc type) + { + return true; + } + + public bool IsEffectivelySealed(TypeDesc type) + { + return _devirtualizationManager.IsEffectivelySealed(type); + } + + public bool IsEffectivelySealed(MethodDesc method) + { + return _devirtualizationManager.IsEffectivelySealed(method); + } + + public MethodDesc ResolveVirtualMethod(MethodDesc declMethod, TypeDesc implType) + { + return _devirtualizationManager.ResolveVirtualMethod(declMethod, implType); + } + + public bool NeedsRuntimeLookup(ReadyToRunHelperId lookupKind, object targetOfLookup) + { + // The current plan seem to be to copy paste from ILCompiler.Compilation, but that's not a sustainable plan + throw new NotImplementedException(); + } + + public ISymbolNode ComputeConstantLookup(ReadyToRunHelperId lookupKind, object targetOfLookup) + { + // The current plan seem to be to copy paste from ILCompiler.Compilation, but that's not a sustainable plan + throw new NotImplementedException(); + } + + public GenericDictionaryLookup ComputeGenericLookup(MethodDesc contextMethod, ReadyToRunHelperId lookupKind, object targetOfLookup) + { + // The current plan seem to be to copy paste from ILCompiler.Compilation, but that's not a sustainable plan + throw new NotImplementedException(); + } + public DelegateCreationInfo GetDelegateCtor(TypeDesc delegateType, MethodDesc target, bool followVirtualDispatch) { return DelegateCreationInfo.Create(delegateType, target, NodeFactory, followVirtualDispatch); diff --git a/external/corert/src/System.Private.Jit/src/Internal/Runtime/JitSupport/JitNodeFactory.cs b/external/corert/src/System.Private.Jit/src/Internal/Runtime/JitSupport/JitNodeFactory.cs index 01615055dd..989efecdfb 100644 --- a/external/corert/src/System.Private.Jit/src/Internal/Runtime/JitSupport/JitNodeFactory.cs +++ b/external/corert/src/System.Private.Jit/src/Internal/Runtime/JitSupport/JitNodeFactory.cs @@ -111,6 +111,7 @@ namespace ILCompiler.DependencyAnalysis public IMethodNode ShadowConcreteMethod(MethodDesc method, bool isUnboxingStub = false) { throw new NotImplementedException(); } internal IMethodNode StringAllocator(MethodDesc stringConstructor) { throw new NotImplementedException(); } public ISymbolNode ExternSymbol(string name) { throw new NotImplementedException(); } + public ISymbolNode ConstantUtf8String(string str) { throw new NotImplementedException(); } public IEETypeNode NecessaryTypeSymbol(TypeDesc type) { return ConstructedTypeSymbol(type); } internal JitInterfaceDispatchCellNode InterfaceDispatchCell(MethodDesc method) { throw new NotImplementedException(); } public ISymbolNode RuntimeMethodHandle(MethodDesc method) { throw new NotImplementedException(); } diff --git a/external/corert/src/System.Private.Jit/src/System.Private.Jit.csproj b/external/corert/src/System.Private.Jit/src/System.Private.Jit.csproj index fcf2cd590a..270c33469a 100644 --- a/external/corert/src/System.Private.Jit/src/System.Private.Jit.csproj +++ b/external/corert/src/System.Private.Jit/src/System.Private.Jit.csproj @@ -1,21 +1,16 @@ - - + System.Private.Jit 4.0.0.0 Library - {82CAE34B-8C3B-4D47-A809-CB2E22E0D36B} true - false TYPE_LOADER_IMPLEMENTATION;SUPPORTS_NATIVE_METADATA_TYPE_LOADING;$(DefineConstants) true true true + true - - - SUPPORT_JIT;$(DefineConstants) @@ -26,10 +21,11 @@ false + true - + @@ -70,9 +66,11 @@ + + @@ -100,10 +98,16 @@ + + + + + + @@ -119,14 +123,18 @@ + + + + @@ -137,9 +145,10 @@ + - \ No newline at end of file + diff --git a/external/corert/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ExecutionDomain.cs b/external/corert/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ExecutionDomain.cs index 17ad69dc16..3610291692 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ExecutionDomain.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ExecutionDomain.cs @@ -91,7 +91,9 @@ namespace Internal.Reflection.Core.Execution foreach (string defaultAssemblyName in defaultAssemblyNames) { RuntimeAssemblyName runtimeAssemblyName = AssemblyNameParser.Parse(defaultAssemblyName); - RuntimeAssembly defaultAssembly = RuntimeAssembly.GetRuntimeAssembly(runtimeAssemblyName); + RuntimeAssembly defaultAssembly = RuntimeAssembly.GetRuntimeAssemblyIfExists(runtimeAssemblyName); + if (defaultAssembly == null) + continue; Type resolvedType = defaultAssembly.GetTypeCore(coreTypeName, ignoreCase: ignoreCase); if (resolvedType != null) return resolvedType; @@ -175,17 +177,6 @@ namespace Internal.Reflection.Core.Execution } } - // - // Get or create a CustomAttributeData object for a specific type and arguments. - // - public CustomAttributeData GetCustomAttributeData(Type attributeType, IList constructorArguments, IList namedArguments) - { - if (!attributeType.IsRuntimeImplemented()) - throw new InvalidOperationException(); - RuntimeTypeInfo runtimeAttributeType = attributeType.CastToRuntimeTypeInfo(); - return new RuntimePseudoCustomAttributeData(runtimeAttributeType, constructorArguments, namedArguments); - } - //======================================================================================= // This group of methods jointly service the Type.GetTypeFromHandle() path. The caller // is responsible for analyzing the RuntimeTypeHandle to figure out which flavor to call. diff --git a/external/corert/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs b/external/corert/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs index e5344edf49..71e8080a25 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs @@ -43,13 +43,6 @@ namespace Internal.Reflection.Core.Execution public abstract bool IsReflectionBlocked(RuntimeTypeHandle typeHandle); public abstract string GetLastResortString(RuntimeTypeHandle typeHandle); - //============================================================================================== - // Default Value support. - //============================================================================================== - public abstract bool GetDefaultValueIfAny(MetadataReader reader, ParameterHandle parameterHandle, Type declaredType, IEnumerable customAttributes, out Object defaultValue); - public abstract bool GetDefaultValueIfAny(MetadataReader reader, FieldHandle fieldHandle, Type declaredType, IEnumerable customAttributes, out Object defaultValue); - public abstract bool GetDefaultValueIfAny(MetadataReader reader, PropertyHandle propertyHandle, Type declaredType, IEnumerable customAttributes, out Object defaultValue); - //============================================================================================== // Reflection Mapping Tables //============================================================================================== @@ -78,17 +71,6 @@ namespace Internal.Reflection.Core.Execution public abstract MethodInvoker TryGetMethodInvoker(RuntimeTypeHandle declaringTypeHandle, QMethodDefinition methodHandle, RuntimeTypeHandle[] genericMethodTypeArgumentHandles); public abstract FieldAccessor TryGetFieldAccessor(MetadataReader reader, RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle fieldTypeHandle, FieldHandle fieldHandle); - //============================================================================================== - // Pseudo Custom Attributes - //============================================================================================== - public abstract IEnumerable GetPseudoCustomAttributes(MetadataReader reader, ScopeDefinitionHandle scopeDefinitionHandle); - public abstract IEnumerable GetPseudoCustomAttributes(MetadataReader reader, TypeDefinitionHandle typeDefinitionHandle); - public abstract IEnumerable GetPseudoCustomAttributes(MetadataReader reader, MethodHandle methodHandle, TypeDefinitionHandle declaringTypeHandle); - public abstract IEnumerable GetPseudoCustomAttributes(MetadataReader reader, ParameterHandle parameterHandle, MethodHandle declaringMethodHandle); - public abstract IEnumerable GetPseudoCustomAttributes(MetadataReader reader, FieldHandle fieldHandle, TypeDefinitionHandle declaringTypeHandle); - public abstract IEnumerable GetPseudoCustomAttributes(MetadataReader reader, PropertyHandle propertyHandle, TypeDefinitionHandle declaringTypeHandle); - public abstract IEnumerable GetPseudoCustomAttributes(MetadataReader reader, EventHandle eventHandle, TypeDefinitionHandle declaringTypeHandle); - //============================================================================================== // RuntimeMethodHandle and RuntimeFieldHandle support. //============================================================================================== @@ -108,7 +90,6 @@ namespace Internal.Reflection.Core.Execution //============================================================================================== // Other //============================================================================================== - public abstract MethodInvoker GetSyntheticMethodInvoker(RuntimeTypeHandle thisType, RuntimeTypeHandle[] parameterTypes, InvokerOptions options, Func invoker); public abstract bool IsCOMObject(Type type); public abstract FieldAccessor CreateLiteralFieldAccessor(object value, RuntimeTypeHandle fieldTypeHandle); diff --git a/external/corert/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/InvokerOptions.cs b/external/corert/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/InvokerOptions.cs deleted file mode 100644 index 831a454f35..0000000000 --- a/external/corert/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/InvokerOptions.cs +++ /dev/null @@ -1,17 +0,0 @@ -// 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; - -namespace Internal.Reflection.Core.Execution -{ - [Flags] - public enum InvokerOptions - { - None = 0x00000000, - AllowNullThis = 0x00000001, // Don't raise an exception if the "thisObject" parameter to Invoker is null. - DontWrapException = 0x00000002, // Don't wrap target exceptions in TargetInvocationException. - } -} - diff --git a/external/corert/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/MethodInvoker.cs b/external/corert/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/MethodInvoker.cs index b367938129..efeb83909c 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/MethodInvoker.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/MethodInvoker.cs @@ -8,6 +8,8 @@ using System.Diagnostics; using System.Globalization; using System.Reflection.Runtime.General; +using Internal.Runtime.Augments; + namespace Internal.Reflection.Core.Execution { // @@ -22,15 +24,34 @@ namespace Internal.Reflection.Core.Execution public Object Invoke(Object thisObject, Object[] arguments, Binder binder, BindingFlags invokeAttr, CultureInfo cultureInfo) { BinderBundle binderBundle = binder.ToBinderBundle(invokeAttr, cultureInfo); - Object result = Invoke(thisObject, arguments, binderBundle); + bool wrapInTargetInvocationException = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0; + Object result = Invoke(thisObject, arguments, binderBundle, wrapInTargetInvocationException); System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); return result; } - public abstract Object Invoke(Object thisObject, Object[] arguments, BinderBundle binderBundle); + protected abstract Object Invoke(Object thisObject, Object[] arguments, BinderBundle binderBundle, bool wrapInTargetInvocationException); public abstract Delegate CreateDelegate(RuntimeTypeHandle delegateType, Object target, bool isStatic, bool isVirtual, bool isOpen); // This property is used to retrieve the target method pointer. It is used by the RuntimeMethodHandle.GetFunctionPointer API public abstract IntPtr LdFtnResult { get; } + + protected static void ValidateThis(object thisObject, RuntimeTypeHandle declaringTypeHandle) + { + if (thisObject == null) + throw new TargetException(SR.RFLCT_Targ_StatMethReqTarg); + + RuntimeTypeHandle srcTypeHandle = thisObject.GetType().TypeHandle; + if (RuntimeAugments.IsAssignableFrom(declaringTypeHandle, srcTypeHandle)) + return; + + if (RuntimeAugments.IsInterface(declaringTypeHandle)) + { + if (RuntimeAugments.IsInstanceOfInterface(thisObject, declaringTypeHandle)) + return; + } + + throw new TargetException(SR.RFLCT_Targ_ITargMismatch); + } } } diff --git a/external/corert/src/System.Private.Reflection.Core/src/Internal/Reflection/Tracing/ReflectionTrace.Internal.cs b/external/corert/src/System.Private.Reflection.Core/src/Internal/Reflection/Tracing/ReflectionTrace.Internal.cs index a1ff0daea1..71b02774a2 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/Internal/Reflection/Tracing/ReflectionTrace.Internal.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/Internal/Reflection/Tracing/ReflectionTrace.Internal.cs @@ -52,8 +52,7 @@ namespace Internal.Reflection.Tracing { try { - RuntimeAssembly runtimeAssembly = assembly as RuntimeAssembly; - if (runtimeAssembly == null) + if (!(assembly is RuntimeAssembly runtimeAssembly)) return null; return runtimeAssembly.RuntimeAssemblyName.FullName; } @@ -70,8 +69,7 @@ namespace Internal.Reflection.Tracing { try { - RuntimeCustomAttributeData runtimeCustomAttributeData = customAttributeData as RuntimeCustomAttributeData; - if (runtimeCustomAttributeData == null) + if (!(customAttributeData is RuntimeCustomAttributeData runtimeCustomAttributeData)) return null; return runtimeCustomAttributeData.AttributeType.NameString(); } @@ -88,8 +86,7 @@ namespace Internal.Reflection.Tracing { try { - ITraceableTypeMember traceableTypeMember = memberInfo as ITraceableTypeMember; - if (traceableTypeMember == null) + if (!(memberInfo is ITraceableTypeMember traceableTypeMember)) return null; return traceableTypeMember.ContainingType.NameString(); } @@ -106,13 +103,12 @@ namespace Internal.Reflection.Tracing { try { - TypeInfo typeInfo = memberInfo as TypeInfo; - if (typeInfo != null) + if (memberInfo is TypeInfo typeInfo) return typeInfo.AsType().NameString(); - ITraceableTypeMember traceableTypeMember = memberInfo as ITraceableTypeMember; - if (traceableTypeMember == null) + if (!(memberInfo is ITraceableTypeMember traceableTypeMember)) return null; + return traceableTypeMember.MemberName; } catch @@ -195,8 +191,7 @@ namespace Internal.Reflection.Tracing } else { - RuntimeNamedTypeInfo runtimeNamedTypeInfo = type.GetTypeInfo() as RuntimeNamedTypeInfo; - if (runtimeNamedTypeInfo == null) + if (!(type.GetTypeInfo() is RuntimeNamedTypeInfo runtimeNamedTypeInfo)) return null; return runtimeNamedTypeInfo.TraceableTypeName; diff --git a/external/corert/src/System.Private.Reflection.Core/src/Resources/Strings.resx b/external/corert/src/System.Private.Reflection.Core/src/Resources/Strings.resx index 3914eea1fa..4d76156999 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/Resources/Strings.resx +++ b/external/corert/src/System.Private.Reflection.Core/src/Resources/Strings.resx @@ -367,4 +367,22 @@ Cannot create a pointer to a byref: {0} + + Literal value was not found. + + + Cannot create boxed ByRef-like values. + + + Cannot instantiate a generic type on a byref-like type. + + + Non-static method requires a target. + + + Object does not match target type. + + + Nullable object must have a value. + diff --git a/external/corert/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.Experimental.csproj b/external/corert/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.Experimental.csproj index b7b298a973..75f14057af 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.Experimental.csproj +++ b/external/corert/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.Experimental.csproj @@ -1,8 +1,6 @@ - - + System.Private.Reflection.Core.Experimental - {50CDFB37-A390-499E-B3B6-690BE70AB706} true true diff --git a/external/corert/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj b/external/corert/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj index fc29604271..696f74096a 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj +++ b/external/corert/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj @@ -1,14 +1,13 @@ - - + System.Private.Reflection.Core - {999BB20D-A032-4836-A587-627D243C1668} 4.0.0.0 Library true + true ENABLE_REFLECTION_TRACE;$(DefineConstants) @@ -16,16 +15,14 @@ ECMA_METADATA_SUPPORT;$(DefineConstants) - - - - - false - + + false + true + @@ -35,7 +32,7 @@ - + @@ -117,12 +114,14 @@ + + @@ -130,6 +129,12 @@ + + + + + + @@ -147,6 +152,7 @@ + @@ -197,7 +203,6 @@ - @@ -253,8 +258,8 @@ System\Runtime\CompilerServices\DeveloperExperienceState.cs - - System\Runtime\CompilerServices\__BlockReflectionAttribute.cs + + System\Runtime\CompilerServices\__BlockAllReflectionAttribute.cs diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/ActivatorImplementation.cs b/external/corert/src/System.Private.Reflection.Core/src/System/ActivatorImplementation.cs index a3f3d4087a..35ce322e20 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/ActivatorImplementation.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/ActivatorImplementation.cs @@ -124,6 +124,9 @@ namespace System if (type.ContainsGenericParameters) throw new ArgumentException(SR.Format(SR.Acc_CreateGenericEx, type)); + if (type.IsByRefLike) + throw new NotSupportedException(SR.NotSupported_ByRefLike); + Type elementType = type; while (elementType.HasElementType) elementType = elementType.GetElementType(); diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/EcmaFormat/EcmaFormatRuntimeAssembly.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/EcmaFormat/EcmaFormatRuntimeAssembly.cs index 40f4322ccb..6badb26b97 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/EcmaFormat/EcmaFormatRuntimeAssembly.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/EcmaFormat/EcmaFormatRuntimeAssembly.cs @@ -96,6 +96,30 @@ namespace System.Reflection.Runtime.Assemblies.EcmaFormat } } + protected sealed override IEnumerable TypeForwardInfos + { + get + { + MetadataReader reader = MetadataReader; + foreach (ExportedTypeHandle exportedTypeHandle in reader.ExportedTypes) + { + ExportedType exportedType = reader.GetExportedType(exportedTypeHandle); + if (!exportedType.IsForwarder) + continue; + + EntityHandle implementation = exportedType.Implementation; + if (implementation.Kind != HandleKind.AssemblyReference) // This check also weeds out nested types. This is intentional. + continue; + RuntimeAssemblyName redirectedAssemblyName = ((AssemblyReferenceHandle)implementation).ToRuntimeAssemblyName(reader); + + string typeName = exportedType.Name.GetString(reader); + string namespaceName = exportedType.Namespace.GetString(reader); + + yield return new TypeForwardInfo(redirectedAssemblyName, namespaceName, typeName); + } + } + } + private unsafe struct InternalManifestResourceInfo { public bool Found; @@ -232,6 +256,14 @@ namespace System.Reflection.Runtime.Assemblies.EcmaFormat } } + public sealed override string ImageRuntimeVersion + { + get + { + return MetadataReader.MetadataVersion; + } + } + public sealed override Module ManifestModule { get @@ -256,6 +288,9 @@ namespace System.Reflection.Runtime.Assemblies.EcmaFormat public bool Equals(EcmaFormatRuntimeAssembly other) { + if (other == null) + return false; + return Object.ReferenceEquals(other.MetadataReader, MetadataReader); } diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.GetTypeCore.CaseSensitive.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.GetTypeCore.CaseSensitive.cs index eb54f91f8e..4e252ad0ea 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.GetTypeCore.CaseSensitive.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.GetTypeCore.CaseSensitive.cs @@ -42,7 +42,7 @@ namespace System.Reflection.Runtime.Assemblies.NativeFormat continue; // We've successfully drilled down the namespace chain. Now look for a top-level type matching the type name. - IEnumerable candidateTypes = namespaceDefinition.TypeDefinitions; + TypeDefinitionHandleCollection candidateTypes = namespaceDefinition.TypeDefinitions; foreach (TypeDefinitionHandle candidateType in candidateTypes) { TypeDefinition typeDefinition = candidateType.GetTypeDefinition(reader); @@ -51,7 +51,7 @@ namespace System.Reflection.Runtime.Assemblies.NativeFormat } // No match found in this assembly - see if there's a matching type forwarder. - IEnumerable candidateTypeForwarders = namespaceDefinition.TypeForwarders; + TypeForwarderHandleCollection candidateTypeForwarders = namespaceDefinition.TypeForwarders; foreach (TypeForwarderHandle typeForwarderHandle in candidateTypeForwarders) { TypeForwarder typeForwarder = typeForwarderHandle.GetTypeForwarder(reader); @@ -59,7 +59,7 @@ namespace System.Reflection.Runtime.Assemblies.NativeFormat { RuntimeAssemblyName redirectedAssemblyName = typeForwarder.Scope.ToRuntimeAssemblyName(reader); RuntimeAssembly redirectedAssembly = RuntimeAssembly.GetRuntimeAssemblyIfExists(redirectedAssemblyName); - if (redirectedAssemblyName == null) + if (redirectedAssembly == null) return null; return redirectedAssembly.GetTypeCoreCaseSensitive(fullName); } @@ -72,7 +72,7 @@ namespace System.Reflection.Runtime.Assemblies.NativeFormat private bool TryResolveNamespaceDefinitionCaseSensitive(MetadataReader reader, string[] namespaceParts, ScopeDefinitionHandle scopeDefinitionHandle, out NamespaceDefinition namespaceDefinition) { namespaceDefinition = scopeDefinitionHandle.GetScopeDefinition(reader).RootNamespaceDefinition.GetNamespaceDefinition(reader); - IEnumerable candidates = namespaceDefinition.NamespaceDefinitions; + NamespaceDefinitionHandleCollection candidates = namespaceDefinition.NamespaceDefinitions; int idx = namespaceParts.Length; while (idx-- != 0) { diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.cs index df317d3c65..e3d7e2ec7d 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.cs @@ -46,9 +46,6 @@ namespace System.Reflection.Runtime.Assemblies.NativeFormat { foreach (CustomAttributeData cad in RuntimeCustomAttributeData.GetCustomAttributes(scope.Reader, scope.ScopeDefinition.CustomAttributes)) yield return cad; - - foreach (CustomAttributeData cad in ReflectionCoreExecution.ExecutionEnvironment.GetPseudoCustomAttributes(scope.Reader, scope.Handle)) - yield return cad; } } } @@ -120,6 +117,37 @@ namespace System.Reflection.Runtime.Assemblies.NativeFormat } } + protected sealed override IEnumerable TypeForwardInfos + { + get + { + foreach (QScopeDefinition scope in AllScopes) + { + MetadataReader reader = scope.Reader; + ScopeDefinition scopeDefinition = scope.ScopeDefinition; + IEnumerable topLevelNamespaceHandles = new NamespaceDefinitionHandle[] { scopeDefinition.RootNamespaceDefinition }; + IEnumerable allNamespaceHandles = reader.GetTransitiveNamespaces(topLevelNamespaceHandles); + foreach (NamespaceDefinitionHandle namespaceHandle in allNamespaceHandles) + { + string namespaceName = null; + foreach (TypeForwarderHandle typeForwarderHandle in namespaceHandle.GetNamespaceDefinition(reader).TypeForwarders) + { + if (namespaceName == null) + { + namespaceName = namespaceHandle.ToNamespaceName(reader); + } + + TypeForwarder typeForwarder = typeForwarderHandle.GetTypeForwarder(reader); + string typeName = typeForwarder.Name.GetString(reader); + RuntimeAssemblyName redirectedAssemblyName = typeForwarder.Scope.ToRuntimeAssemblyName(reader); + + yield return new TypeForwardInfo(redirectedAssemblyName, namespaceName, typeName); + } + } + } + } + } + public sealed override ManifestResourceInfo GetManifestResourceInfo(String resourceName) { return ReflectionCoreExecution.ExecutionEnvironment.GetManifestResourceInfo(this, resourceName); @@ -135,6 +163,16 @@ namespace System.Reflection.Runtime.Assemblies.NativeFormat return ReflectionCoreExecution.ExecutionEnvironment.GetManifestResourceStream(this, name); } + public sealed override string ImageRuntimeVersion + { + get + { + // Needed to make RuntimeEnvironment.GetSystemVersion() work. Will not be correct always but anticipating most callers are not making + // actual decisions based on the value. + return "v4.0.30319"; + } + } + public sealed override Module ManifestModule { get diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/RuntimeAssembly.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/RuntimeAssembly.cs index 269574adad..353a3757cb 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/RuntimeAssembly.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/RuntimeAssembly.cs @@ -27,8 +27,7 @@ namespace System.Reflection.Runtime.Assemblies // // The runtime's implementation of an Assembly. // - [Serializable] - internal abstract partial class RuntimeAssembly : Assembly, IEquatable, ISerializable + internal abstract partial class RuntimeAssembly : Assembly, IEquatable { public bool Equals(RuntimeAssembly other) { @@ -126,6 +125,69 @@ namespace System.Reflection.Runtime.Assemblies return RuntimeAssemblyName.ToAssemblyName(); } + public sealed override Type[] GetForwardedTypes() + { + List types = new List(); + List exceptions = null; + + foreach (TypeForwardInfo typeForwardInfo in TypeForwardInfos) + { + string fullTypeName = typeForwardInfo.NamespaceName.Length == 0 ? typeForwardInfo.TypeName : typeForwardInfo.NamespaceName + "." + typeForwardInfo.TypeName; + RuntimeAssemblyName redirectedAssemblyName = typeForwardInfo.RedirectedAssemblyName; + + Type type = null; + RuntimeAssembly redirectedAssembly; + Exception exception = RuntimeAssembly.TryGetRuntimeAssembly(redirectedAssemblyName, out redirectedAssembly); + if (exception == null) + { + type = redirectedAssembly.GetTypeCore(fullTypeName, ignoreCase: false); // GetTypeCore() will follow any further type-forwards if needed. + if (type == null) + exception = Helpers.CreateTypeLoadException(fullTypeName.EscapeTypeNameIdentifier(), redirectedAssembly); + } + + Debug.Assert((type != null) != (exception != null)); // Exactly one of these must be non-null. + + if (type != null) + { + types.Add(type); + AddPublicNestedTypes(type, types); + } + else + { + if (exceptions == null) + { + exceptions = new List(); + } + exceptions.Add(exception); + } + } + + if (exceptions != null) + { + int numTypes = types.Count; + int numExceptions = exceptions.Count; + types.AddRange(new Type[numExceptions]); // add one null Type for each exception. + exceptions.InsertRange(0, new Exception[numTypes]); // align the Exceptions with the null Types. + throw new ReflectionTypeLoadException(types.ToArray(), exceptions.ToArray()); + } + + return types.ToArray(); + } + + /// + /// Intentionally excludes forwards to nested types. + /// + protected abstract IEnumerable TypeForwardInfos { get; } + + private static void AddPublicNestedTypes(Type type, List types) + { + foreach (Type nestedType in type.GetNestedTypes(BindingFlags.Public)) + { + types.Add(nestedType); + AddPublicNestedTypes(nestedType, types); + } + } + /// /// Helper routine for the more general Type.GetType() family of apis. /// @@ -149,6 +211,7 @@ namespace System.Reflection.Runtime.Assemblies public abstract override ManifestResourceInfo GetManifestResourceInfo(String resourceName); public abstract override String[] GetManifestResourceNames(); public abstract override Stream GetManifestResourceStream(String name); + public abstract override string ImageRuntimeVersion { get; } public abstract override bool Equals(Object obj); public abstract override int GetHashCode(); diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/MemberPolicies.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/MemberPolicies.cs index d807aa3835..84a09738c3 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/MemberPolicies.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/MemberPolicies.cs @@ -172,19 +172,15 @@ namespace System.Reflection.Runtime.BindingFlagSupport return true; } - if (t1.IsGenericParameter && t2.IsGenericParameter) + if (t1.IsGenericMethodParameter && t2.IsGenericMethodParameter) { - if (t1.DeclaringMethod != null && t2.DeclaringMethod != null) - { - // A generic method parameter. The DeclaringMethods will be different but we don't care about that - we can assume that - // the declaring method will be the method that declared the parameter's whose type we're testing. We only need to - // compare the positions. - return t1.GenericParameterPosition == t2.GenericParameterPosition; - } - return false; + // A generic method parameter. The DeclaringMethods will be different but we don't care about that - we can assume that + // the declaring method will be the method that declared the parameter's whose type we're testing. We only need to + // compare the positions. + return t1.GenericParameterPosition == t2.GenericParameterPosition; } - // If we got here, either t1 and t2 are different flavors of types or they are both simple named types. + // If we got here, either t1 and t2 are different flavors of types or they are both simple named types or both generic type parameters. // Either way, we can trust Reflection's result here. return false; } @@ -224,7 +220,7 @@ namespace System.Reflection.Runtime.BindingFlagSupport } else { - Debug.Assert(false, "Unknown MemberInfo type."); + Debug.Fail("Unknown MemberInfo type."); } } diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/Shared.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/Shared.cs index c92b8e729d..d8abe89fc7 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/Shared.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/BindingFlagSupport/Shared.cs @@ -128,7 +128,7 @@ namespace System.Reflection.Runtime.BindingFlagSupport for (int i = 0; i < parameterInfos.Length; i++) { // a null argument type implies a null arg which is always a perfect match - if ((object)argumentTypes[i] != null && !parameterInfos[i].ParameterType.Equals(argumentTypes[i])) + if ((object)argumentTypes[i] != null && !argumentTypes[i].MatchesParameterTypeExactly(parameterInfos[i])) return false; } } diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/CustomAttributes/NativeFormat/NativeFormatCustomAttributeData.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/CustomAttributes/NativeFormat/NativeFormatCustomAttributeData.cs index 847ef57ee6..9c266d3446 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/CustomAttributes/NativeFormat/NativeFormatCustomAttributeData.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/CustomAttributes/NativeFormat/NativeFormatCustomAttributeData.cs @@ -104,7 +104,7 @@ namespace System.Reflection.Runtime.CustomAttributes.NativeFormat internal sealed override IList GetConstructorArguments(bool throwIfMissingMetadata) { int index = 0; - LowLevelList lazyCtorTypeHandles = null; + Handle[] lazyCtorTypeHandles = null; LowLevelListWithIList customAttributeTypedArguments = new LowLevelListWithIList(); foreach (FixedArgumentHandle fixedArgumentHandle in _customAttribute.FixedArguments) @@ -120,7 +120,7 @@ namespace System.Reflection.Runtime.CustomAttributes.NativeFormat // parsing the constructor's signature to get the type info. if (lazyCtorTypeHandles == null) { - IEnumerable parameterTypeSignatureHandles; + HandleCollection parameterTypeSignatureHandles; HandleType handleType = _customAttribute.Constructor.HandleType; switch (handleType) { @@ -134,8 +134,7 @@ namespace System.Reflection.Runtime.CustomAttributes.NativeFormat default: throw new BadImageFormatException(); } - LowLevelList ctorTypeHandles = new LowLevelList(parameterTypeSignatureHandles); - lazyCtorTypeHandles = ctorTypeHandles; + lazyCtorTypeHandles = parameterTypeSignatureHandles.ToArray(); } Handle typeHandle = lazyCtorTypeHandles[index]; Exception exception = null; diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/CustomAttributes/RuntimeCustomAttributeData.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/CustomAttributes/RuntimeCustomAttributeData.cs index bbfc6fca82..64cdd09163 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/CustomAttributes/RuntimeCustomAttributeData.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/CustomAttributes/RuntimeCustomAttributeData.cs @@ -186,8 +186,7 @@ namespace System.Reflection.Runtime.CustomAttributes } // Handle the array case - IEnumerable enumerableValue = value as IEnumerable; - if (enumerableValue != null && !(value is String)) + if (value is IEnumerable enumerableValue && !(value is string)) { if (!argumentType.IsArray) throw new BadImageFormatException(); diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/CustomAttributes/RuntimePseudoCustomAttributeData.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/CustomAttributes/RuntimePseudoCustomAttributeData.cs index bc6bfb41a1..6734900d7e 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/CustomAttributes/RuntimePseudoCustomAttributeData.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/CustomAttributes/RuntimePseudoCustomAttributeData.cs @@ -17,7 +17,7 @@ namespace System.Reflection.Runtime.CustomAttributes // internal sealed class RuntimePseudoCustomAttributeData : RuntimeCustomAttributeData { - public RuntimePseudoCustomAttributeData(RuntimeTypeInfo attributeType, IList constructorArguments, IList namedArguments) + public RuntimePseudoCustomAttributeData(Type attributeType, IList constructorArguments, IList namedArguments) { _attributeType = attributeType; if (constructorArguments == null) @@ -74,7 +74,7 @@ namespace System.Reflection.Runtime.CustomAttributes // Equals/GetHashCode no need to override (they just implement reference equality but desktop never unified these things.) - private readonly RuntimeTypeInfo _attributeType; + private readonly Type _attributeType; private readonly ReadOnlyCollection _constructorArguments; private readonly ReadOnlyCollection _namedArguments; } diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/EcmaFormat/EcmaFormatRuntimeEventInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/EcmaFormat/EcmaFormatRuntimeEventInfo.cs index 87820be0ac..2baad4a655 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/EcmaFormat/EcmaFormatRuntimeEventInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/EcmaFormat/EcmaFormatRuntimeEventInfo.cs @@ -108,8 +108,7 @@ namespace System.Reflection.Runtime.EventInfos.EcmaFormat ReflectionTrace.EventInfo_CustomAttributes(this); #endif - foreach (CustomAttributeData cad in RuntimeCustomAttributeData.GetCustomAttributes(_reader, _event.GetCustomAttributes())) - yield return cad; + return RuntimeCustomAttributeData.GetCustomAttributes(_reader, _event.GetCustomAttributes()); } } @@ -118,8 +117,7 @@ namespace System.Reflection.Runtime.EventInfos.EcmaFormat if (other == null) throw new ArgumentNullException(nameof(other)); - EcmaFormatRuntimeEventInfo otherEvent = other as EcmaFormatRuntimeEventInfo; - if (otherEvent == null) + if (!(other is EcmaFormatRuntimeEventInfo otherEvent)) return false; if (!(_reader == otherEvent._reader)) return false; @@ -130,8 +128,7 @@ namespace System.Reflection.Runtime.EventInfos.EcmaFormat public sealed override bool Equals(Object obj) { - EcmaFormatRuntimeEventInfo other = obj as EcmaFormatRuntimeEventInfo; - if (other == null) + if (!(obj is EcmaFormatRuntimeEventInfo other)) return false; if (!(_reader == other._reader)) return false; diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/NativeFormat/NativeFormatRuntimeEventInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/NativeFormat/NativeFormatRuntimeEventInfo.cs index f738c4e3cc..05a946ef79 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/NativeFormat/NativeFormatRuntimeEventInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/NativeFormat/NativeFormatRuntimeEventInfo.cs @@ -104,10 +104,7 @@ namespace System.Reflection.Runtime.EventInfos.NativeFormat ReflectionTrace.EventInfo_CustomAttributes(this); #endif - foreach (CustomAttributeData cad in RuntimeCustomAttributeData.GetCustomAttributes(_reader, _event.CustomAttributes)) - yield return cad; - foreach (CustomAttributeData cad in ReflectionCoreExecution.ExecutionEnvironment.GetPseudoCustomAttributes(_reader, _eventHandle, _definingTypeInfo.TypeDefinitionHandle)) - yield return cad; + return RuntimeCustomAttributeData.GetCustomAttributes(_reader, _event.CustomAttributes); } } @@ -116,8 +113,7 @@ namespace System.Reflection.Runtime.EventInfos.NativeFormat if (other == null) throw new ArgumentNullException(nameof(other)); - NativeFormatRuntimeEventInfo otherEvent = other as NativeFormatRuntimeEventInfo; - if (otherEvent == null) + if (!(other is NativeFormatRuntimeEventInfo otherEvent)) return false; if (!(_reader == otherEvent._reader)) return false; @@ -130,8 +126,7 @@ namespace System.Reflection.Runtime.EventInfos.NativeFormat public sealed override bool Equals(Object obj) { - NativeFormatRuntimeEventInfo other = obj as NativeFormatRuntimeEventInfo; - if (other == null) + if (!(obj is NativeFormatRuntimeEventInfo other)) return false; if (!(_reader == other._reader)) return false; diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/RuntimeEventInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/RuntimeEventInfo.cs index 1e3492fe4a..edb8dc3542 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/RuntimeEventInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/RuntimeEventInfo.cs @@ -6,7 +6,6 @@ using System; using System.Reflection; using System.Diagnostics; using System.Collections.Generic; -using System.Runtime.Serialization; using System.Runtime.CompilerServices; using System.Reflection.Runtime.General; using System.Reflection.Runtime.TypeInfos; @@ -21,9 +20,8 @@ namespace System.Reflection.Runtime.EventInfos // // The runtime's implementation of EventInfo's // - [Serializable] [DebuggerDisplay("{_debugName}")] - internal abstract partial class RuntimeEventInfo : EventInfo, ISerializable, ITraceableTypeMember + internal abstract partial class RuntimeEventInfo : EventInfo, ITraceableTypeMember { protected RuntimeEventInfo(RuntimeTypeInfo contextTypeInfo, RuntimeTypeInfo reflectedType) { @@ -66,11 +64,6 @@ namespace System.Reflection.Runtime.EventInfos } } - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - throw new PlatformNotSupportedException(); - } - public sealed override MethodInfo[] GetOtherMethods(bool nonPublic) { throw new PlatformNotSupportedException(); diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/EcmaFormat/EcmaFormatRuntimeFieldInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/EcmaFormat/EcmaFormatRuntimeFieldInfo.cs index 3da17059cb..01b254971f 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/EcmaFormat/EcmaFormatRuntimeFieldInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/EcmaFormat/EcmaFormatRuntimeFieldInfo.cs @@ -12,6 +12,7 @@ using System.Runtime.CompilerServices; using System.Reflection.Runtime; using System.Reflection.Runtime.FieldInfos; using System.Reflection.Runtime.General; +using System.Reflection.Runtime.General.EcmaFormat; using System.Reflection.Runtime.TypeInfos; using System.Reflection.Runtime.TypeInfos.EcmaFormat; using System.Reflection.Runtime.CustomAttributes; @@ -63,28 +64,6 @@ namespace System.Reflection.Runtime.FieldInfos.EcmaFormat _field = _reader.GetFieldDefinition(fieldHandle); } - public sealed override IEnumerable CustomAttributes - { - get - { -#if ENABLE_REFLECTION_TRACE - if (ReflectionTrace.Enabled) - ReflectionTrace.FieldInfo_CustomAttributes(this); -#endif - - IEnumerable customAttributes = RuntimeCustomAttributeData.GetCustomAttributes(_reader, _field.GetCustomAttributes()); - foreach (CustomAttributeData cad in customAttributes) - yield return cad; - - if (_definingTypeInfo.IsExplicitLayout) - { - int offset = _field.GetOffset(); - CustomAttributeTypedArgument offsetArgument = new CustomAttributeTypedArgument(typeof(Int32), offset); - yield return ReflectionCoreExecution.ExecutionDomain.GetCustomAttributeData(typeof(System.Runtime.InteropServices.FieldOffsetAttribute), new CustomAttributeTypedArgument[] { offsetArgument }, null); - } - } - } - public sealed override FieldAttributes Attributes { get @@ -136,8 +115,7 @@ namespace System.Reflection.Runtime.FieldInfos.EcmaFormat if (other == null) throw new ArgumentNullException(nameof(other)); - EcmaFormatRuntimeFieldInfo otherField = other as EcmaFormatRuntimeFieldInfo; - if (otherField == null) + if (!(other is EcmaFormatRuntimeFieldInfo otherField)) return false; if (!(_reader == otherField._reader)) return false; @@ -148,8 +126,7 @@ namespace System.Reflection.Runtime.FieldInfos.EcmaFormat public sealed override bool Equals(Object obj) { - EcmaFormatRuntimeFieldInfo other = obj as EcmaFormatRuntimeFieldInfo; - if (other == null) + if (!(obj is EcmaFormatRuntimeFieldInfo other)) return false; if (!(_reader == other._reader)) return false; @@ -181,9 +158,9 @@ namespace System.Reflection.Runtime.FieldInfos.EcmaFormat public sealed override Type[] GetRequiredCustomModifiers() { throw new NotImplementedException(); } - protected sealed override bool TryGetDefaultValue(out object defaultValue) + protected sealed override bool GetDefaultValueIfAvailable(bool raw, out object defaultValue) { - return DefaultValueProcessing.GetDefaultValueIfAny(_reader, ref _field, this, out defaultValue); + return DefaultValueProcessing.GetDefaultValueIfAny(_reader, ref _field, this, raw, out defaultValue); } protected sealed override FieldAccessor TryGetFieldAccessor() @@ -203,6 +180,10 @@ namespace System.Reflection.Runtime.FieldInfos.EcmaFormat protected sealed override RuntimeTypeInfo DefiningType { get { return _definingTypeInfo; } } + protected sealed override IEnumerable TrueCustomAttributes => RuntimeCustomAttributeData.GetCustomAttributes(_reader, _field.GetCustomAttributes()); + + protected sealed override int ExplicitLayoutFieldOffsetData => _field.GetOffset(); + private readonly EcmaFormatRuntimeNamedTypeInfo _definingTypeInfo; private readonly FieldDefinitionHandle _fieldHandle; diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/NativeFormat/NativeFormatRuntimeFieldInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/NativeFormat/NativeFormatRuntimeFieldInfo.cs index ac03d6b99f..643d946044 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/NativeFormat/NativeFormatRuntimeFieldInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/NativeFormat/NativeFormatRuntimeFieldInfo.cs @@ -11,6 +11,7 @@ using System.Runtime.CompilerServices; using System.Reflection.Runtime.FieldInfos; using System.Reflection.Runtime.General; +using System.Reflection.Runtime.General.NativeFormat; using System.Reflection.Runtime.TypeInfos; using System.Reflection.Runtime.TypeInfos.NativeFormat; using System.Reflection.Runtime.CustomAttributes; @@ -60,23 +61,6 @@ namespace System.Reflection.Runtime.FieldInfos.NativeFormat _field = fieldHandle.GetField(_reader); } - public sealed override IEnumerable CustomAttributes - { - get - { -#if ENABLE_REFLECTION_TRACE - if (ReflectionTrace.Enabled) - ReflectionTrace.FieldInfo_CustomAttributes(this); -#endif - - IEnumerable customAttributes = RuntimeCustomAttributeData.GetCustomAttributes(_reader, _field.CustomAttributes); - foreach (CustomAttributeData cad in customAttributes) - yield return cad; - foreach (CustomAttributeData cad in ReflectionCoreExecution.ExecutionEnvironment.GetPseudoCustomAttributes(_reader, _fieldHandle, _definingTypeInfo.TypeDefinitionHandle)) - yield return cad; - } - } - public sealed override FieldAttributes Attributes { get @@ -117,8 +101,7 @@ namespace System.Reflection.Runtime.FieldInfos.NativeFormat if (other == null) throw new ArgumentNullException(nameof(other)); - NativeFormatRuntimeFieldInfo otherField = other as NativeFormatRuntimeFieldInfo; - if (otherField == null) + if (!(other is NativeFormatRuntimeFieldInfo otherField)) return false; if (!(_reader == otherField._reader)) return false; @@ -131,8 +114,7 @@ namespace System.Reflection.Runtime.FieldInfos.NativeFormat public sealed override bool Equals(Object obj) { - NativeFormatRuntimeFieldInfo other = obj as NativeFormatRuntimeFieldInfo; - if (other == null) + if (!(obj is NativeFormatRuntimeFieldInfo other)) return false; if (!(_reader == other._reader)) return false; @@ -160,14 +142,9 @@ namespace System.Reflection.Runtime.FieldInfos.NativeFormat } } - protected sealed override bool TryGetDefaultValue(out object defaultValue) + protected sealed override bool GetDefaultValueIfAvailable(bool raw, out object defaultValue) { - return ReflectionCoreExecution.ExecutionEnvironment.GetDefaultValueIfAny( - _reader, - _fieldHandle, - this.FieldType, - this.CustomAttributes, - out defaultValue); + return DefaultValueParser.GetDefaultValueIfAny(_reader, _field.DefaultValue, FieldType, CustomAttributes, raw, out defaultValue); } protected sealed override FieldAccessor TryGetFieldAccessor() @@ -186,6 +163,10 @@ namespace System.Reflection.Runtime.FieldInfos.NativeFormat protected sealed override RuntimeTypeInfo DefiningType { get { return _definingTypeInfo; } } + protected sealed override IEnumerable TrueCustomAttributes => RuntimeCustomAttributeData.GetCustomAttributes(_reader, _field.CustomAttributes); + + protected sealed override int ExplicitLayoutFieldOffsetData => (int)(_field.Offset); + private Handle FieldTypeHandle => _field.Signature.GetFieldSignature(_reader).Type; private readonly NativeFormatRuntimeNamedTypeInfo _definingTypeInfo; diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/RuntimeFieldInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/RuntimeFieldInfo.cs index fcd0fef184..392f6db9e3 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/RuntimeFieldInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/RuntimeFieldInfo.cs @@ -7,7 +7,7 @@ using System.Reflection; using System.Diagnostics; using System.Globalization; using System.Collections.Generic; -using System.Runtime.Serialization; +using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Reflection.Runtime.General; @@ -25,9 +25,8 @@ namespace System.Reflection.Runtime.FieldInfos // // The Runtime's implementation of fields. // - [Serializable] [DebuggerDisplay("{_debugName}")] - internal abstract partial class RuntimeFieldInfo : FieldInfo, ISerializable, ITraceableTypeMember + internal abstract partial class RuntimeFieldInfo : FieldInfo, ITraceableTypeMember { // // contextType - the type that supplies the type context (i.e. substitutions for generic parameters.) Though you @@ -52,6 +51,27 @@ namespace System.Reflection.Runtime.FieldInfos _reflectedType = reflectedType; } + public sealed override IEnumerable CustomAttributes + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.FieldInfo_CustomAttributes(this); +#endif + + foreach (CustomAttributeData cad in TrueCustomAttributes) + yield return cad; + + if (DeclaringType.IsExplicitLayout) + { + int offset = ExplicitLayoutFieldOffsetData; + CustomAttributeTypedArgument offsetArgument = new CustomAttributeTypedArgument(typeof(int), offset); + yield return new RuntimePseudoCustomAttributeData(typeof(FieldOffsetAttribute), new CustomAttributeTypedArgument[] { offsetArgument }, null); + } + } + } + public sealed override Type DeclaringType { get @@ -73,11 +93,6 @@ namespace System.Reflection.Runtime.FieldInfos } } - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - throw new PlatformNotSupportedException(); - } - public abstract override Type[] GetOptionalCustomModifiers(); public abstract override Type[] GetRequiredCustomModifiers(); @@ -176,8 +191,19 @@ namespace System.Reflection.Runtime.FieldInfos } } + public sealed override object GetRawConstantValue() + { + if (!IsLiteral) + throw new InvalidOperationException(); + + object defaultValue; + if (!GetDefaultValueIfAvailable(raw: true, defaultValue: out defaultValue)) + throw new BadImageFormatException(); // Field marked literal but has no default value. + + return defaultValue; + } + // Types that derive from RuntimeFieldInfo must implement the following public surface area members - public abstract override IEnumerable CustomAttributes { get; } public abstract override FieldAttributes Attributes { get; } public abstract override int MetadataToken { get; } public abstract override String ToString(); @@ -188,7 +214,7 @@ namespace System.Reflection.Runtime.FieldInfos /// /// Get the default value if exists for a field by parsing metadata. Return false if there is no default value. /// - protected abstract bool TryGetDefaultValue(out object defaultValue); + protected abstract bool GetDefaultValueIfAvailable(bool raw, out object defaultValue); /// /// Return a FieldAccessor object for accessing the value of a non-literal field. May rely on metadata to create correct accessor. @@ -208,7 +234,7 @@ namespace System.Reflection.Runtime.FieldInfos // For desktop compat, we return the metadata literal as is and do not attempt to convert or validate against the Field type. Object defaultValue; - if (!TryGetDefaultValue(out defaultValue)) + if (!GetDefaultValueIfAvailable(raw: false, defaultValue: out defaultValue)) { throw new BadImageFormatException(); // Field marked literal but has no default value. } @@ -253,6 +279,9 @@ namespace System.Reflection.Runtime.FieldInfos /// protected abstract RuntimeTypeInfo DefiningType { get; } + protected abstract IEnumerable TrueCustomAttributes { get; } + protected abstract int ExplicitLayoutFieldOffsetData { get; } + /// /// Returns the field offset (asserts and throws if not an instance field). Does not include the size of the object header. /// diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Dispensers.Ecma.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Dispensers.Ecma.cs index c7c3b82ac5..0c71073a76 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Dispensers.Ecma.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Dispensers.Ecma.cs @@ -68,9 +68,9 @@ namespace System.Reflection.Runtime.Assemblies.EcmaFormat public override bool Equals(Object obj) { - if (!(obj is EcmaRuntimeAssemblyKey)) + if (!(obj is EcmaRuntimeAssemblyKey other)) return false; - return Equals((EcmaRuntimeAssemblyKey)obj); + return Equals(other); } @@ -152,7 +152,7 @@ namespace System.Reflection.Runtime.ParameterInfos.EcmaFormat //----------------------------------------------------------------------------------------------------------- // ParameterInfos for MethodBase objects with Parameter metadata. //----------------------------------------------------------------------------------------------------------- - internal sealed partial class EcmaFormatMethodParameterInfo : RuntimeMethodParameterInfo + internal sealed partial class EcmaFormatMethodParameterInfo { internal static EcmaFormatMethodParameterInfo GetEcmaFormatMethodParameterInfo(MethodBase member, MethodDefinitionHandle methodHandle, int position, ParameterHandle parameterHandle, QSignatureTypeHandle qualifiedParameterType, TypeContext typeContext) { diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Dispensers.NativeFormat.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Dispensers.NativeFormat.cs index a24d4a0352..dfbd31cb85 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Dispensers.NativeFormat.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Dispensers.NativeFormat.cs @@ -85,9 +85,9 @@ namespace System.Reflection.Runtime.Assemblies.NativeFormat public override bool Equals(Object obj) { - if (!(obj is RuntimeAssemblyKey)) + if (!(obj is RuntimeAssemblyKey other)) return false; - return Equals((RuntimeAssemblyKey)obj); + return Equals(other); } @@ -176,7 +176,7 @@ namespace System.Reflection.Runtime.ParameterInfos.NativeFormat //----------------------------------------------------------------------------------------------------------- // ParameterInfos for MethodBase objects with Parameter metadata. //----------------------------------------------------------------------------------------------------------- - internal sealed partial class NativeFormatMethodParameterInfo : RuntimeMethodParameterInfo + internal sealed partial class NativeFormatMethodParameterInfo { internal static NativeFormatMethodParameterInfo GetNativeFormatMethodParameterInfo(MethodBase member, MethodHandle methodHandle, int position, ParameterHandle parameterHandle, QSignatureTypeHandle qualifiedParameterType, TypeContext typeContext) { @@ -194,7 +194,7 @@ namespace System.Reflection.Runtime.CustomAttributes //----------------------------------------------------------------------------------------------------------- internal abstract partial class RuntimeCustomAttributeData { - internal static IEnumerable GetCustomAttributes(MetadataReader reader, IEnumerable customAttributeHandles) + internal static IEnumerable GetCustomAttributes(MetadataReader reader, CustomAttributeHandleCollection customAttributeHandles) { foreach (CustomAttributeHandle customAttributeHandle in customAttributeHandles) yield return GetCustomAttributeData(reader, customAttributeHandle); diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Dispensers.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Dispensers.cs index 19201f5a1e..79eca88682 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Dispensers.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Dispensers.cs @@ -67,20 +67,30 @@ namespace System.Reflection.Runtime.Assemblies /// internal static RuntimeAssembly GetRuntimeAssemblyIfExists(RuntimeAssemblyName assemblyRefName) { - return s_assemblyRefNameToAssemblyDispenser.GetOrAdd(assemblyRefName); + object runtimeAssemblyOrException = s_assemblyRefNameToAssemblyDispenser.GetOrAdd(assemblyRefName); + if (runtimeAssemblyOrException is RuntimeAssembly runtimeAssembly) + return runtimeAssembly; + return null; } internal static Exception TryGetRuntimeAssembly(RuntimeAssemblyName assemblyRefName, out RuntimeAssembly result) { - result = GetRuntimeAssemblyIfExists(assemblyRefName); - if (result != null) + object runtimeAssemblyOrException = s_assemblyRefNameToAssemblyDispenser.GetOrAdd(assemblyRefName); + if (runtimeAssemblyOrException is RuntimeAssembly runtimeAssembly) + { + result = runtimeAssembly; return null; + } else - return new FileNotFoundException(SR.Format(SR.FileNotFound_AssemblyNotFound, assemblyRefName.FullName)); + { + result = null; + return (Exception)runtimeAssemblyOrException; + } } - private static readonly Dispenser s_assemblyRefNameToAssemblyDispenser = - DispenserFactory.CreateDispenser( + // The "object" here is either a RuntimeAssembly or an Exception. + private static readonly Dispenser s_assemblyRefNameToAssemblyDispenser = + DispenserFactory.CreateDispenser( DispenserScenario.AssemblyRefName_Assembly, delegate (RuntimeAssemblyName assemblyRefName) { @@ -88,7 +98,7 @@ namespace System.Reflection.Runtime.Assemblies AssemblyBindResult bindResult; Exception exception; if (!binder.Bind(assemblyRefName, out bindResult, out exception)) - return null; + return exception; return GetRuntimeAssembly(bindResult); } @@ -133,9 +143,9 @@ namespace System.Reflection.Runtime.MethodInfos //----------------------------------------------------------------------------------------------------------- internal sealed partial class RuntimeSyntheticConstructorInfo : RuntimeConstructorInfo { - internal static RuntimeSyntheticConstructorInfo GetRuntimeSyntheticConstructorInfo(SyntheticMethodId syntheticMethodId, RuntimeArrayTypeInfo declaringType, RuntimeTypeInfo[] runtimeParameterTypes, InvokerOptions options, Func invoker) + internal static RuntimeSyntheticConstructorInfo GetRuntimeSyntheticConstructorInfo(SyntheticMethodId syntheticMethodId, RuntimeArrayTypeInfo declaringType, RuntimeTypeInfo[] runtimeParameterTypes, InvokerOptions options, CustomMethodInvokerAction action) { - return new RuntimeSyntheticConstructorInfo(syntheticMethodId, declaringType, runtimeParameterTypes, options, invoker); + return new RuntimeSyntheticConstructorInfo(syntheticMethodId, declaringType, runtimeParameterTypes, options, action); } } @@ -179,9 +189,9 @@ namespace System.Reflection.Runtime.MethodInfos //----------------------------------------------------------------------------------------------------------- internal sealed partial class RuntimeSyntheticMethodInfo : RuntimeMethodInfo { - internal static RuntimeMethodInfo GetRuntimeSyntheticMethodInfo(SyntheticMethodId syntheticMethodId, String name, RuntimeArrayTypeInfo declaringType, RuntimeTypeInfo[] runtimeParameterTypes, RuntimeTypeInfo returnType, InvokerOptions options, Func invoker) + internal static RuntimeMethodInfo GetRuntimeSyntheticMethodInfo(SyntheticMethodId syntheticMethodId, String name, RuntimeArrayTypeInfo declaringType, RuntimeTypeInfo[] runtimeParameterTypes, RuntimeTypeInfo returnType, InvokerOptions options, CustomMethodInvokerAction action) { - return new RuntimeSyntheticMethodInfo(syntheticMethodId, name, declaringType, runtimeParameterTypes, returnType, options, invoker).WithDebugName(); + return new RuntimeSyntheticMethodInfo(syntheticMethodId, name, declaringType, runtimeParameterTypes, returnType, options, action).WithDebugName(); } } } diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/EcmaFormat/DefaultValueProcessing.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/EcmaFormat/DefaultValueProcessing.cs index f39a7c1eaa..50196d699b 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/EcmaFormat/DefaultValueProcessing.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/EcmaFormat/DefaultValueProcessing.cs @@ -6,46 +6,47 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Runtime.CompilerServices; +using System.Reflection.Runtime.General; using Internal.Reflection.Extensions.NonPortable; using System.Reflection.Metadata; -namespace System.Reflection.Runtime +namespace System.Reflection.Runtime.General.EcmaFormat { - public static class DefaultValueProcessing + internal static class DefaultValueProcessing { - public static bool GetDefaultValueIfAny(MetadataReader reader, ref FieldDefinition field, FieldInfo fieldInfo, out object defaultValue) + public static bool GetDefaultValueIfAny(MetadataReader reader, ref FieldDefinition field, FieldInfo fieldInfo, bool raw, out object defaultValue) { if (0 != (field.Attributes & FieldAttributes.HasDefault)) { - defaultValue = ConstantValueAsObject(field.GetDefaultValue(), reader); + defaultValue = ConstantValueAsObject(field.GetDefaultValue(), reader, fieldInfo.FieldType, raw); return true; } else { - return GetCustomAttributeDefaultValueIfAny(fieldInfo.CustomAttributes, out defaultValue); + return Helpers.GetCustomAttributeDefaultValueIfAny(fieldInfo.CustomAttributes, raw, out defaultValue); } } - public static bool GetDefaultValueIfAny(MetadataReader reader, ref Parameter parameter, ParameterInfo parameterInfo, out object defaultValue) + public static bool GetDefaultValueIfAny(MetadataReader reader, ref Parameter parameter, ParameterInfo parameterInfo, bool raw, out object defaultValue) { if (0 != (parameter.Attributes & ParameterAttributes.HasDefault)) { - defaultValue = ConstantValueAsObject(parameter.GetDefaultValue(), reader); + defaultValue = ConstantValueAsObject(parameter.GetDefaultValue(), reader, parameterInfo.ParameterType, raw); return true; } else { - return GetCustomAttributeDefaultValueIfAny(parameterInfo.CustomAttributes, out defaultValue); + return Helpers.GetCustomAttributeDefaultValueIfAny(parameterInfo.CustomAttributes, raw, out defaultValue); } } - public static bool GetDefaultValueIfAny(MetadataReader reader, ref PropertyDefinition property, PropertyInfo propertyInfo, out object defaultValue) + public static bool GetDefaultValueIfAny(MetadataReader reader, ref PropertyDefinition property, PropertyInfo propertyInfo, bool raw, out object defaultValue) { if (0 != (property.Attributes & PropertyAttributes.HasDefault)) { - defaultValue = ConstantValueAsObject(property.GetDefaultValue(), reader); + defaultValue = ConstantValueAsObject(property.GetDefaultValue(), reader, propertyInfo.PropertyType, raw); return true; } else @@ -54,33 +55,18 @@ namespace System.Reflection.Runtime defaultValue = null; return false; } - } - - private static bool GetCustomAttributeDefaultValueIfAny(IEnumerable customAttributes, out object defaultValue) - { - // Legacy: If there are multiple default value attribute, the desktop picks one at random (and so do we...) - foreach (CustomAttributeData cad in customAttributes) - { - Type attributeType = cad.AttributeType; - if (attributeType.IsSubclassOf(typeof(CustomConstantAttribute))) - { - CustomConstantAttribute customConstantAttribute = (CustomConstantAttribute)(cad.Instantiate()); - defaultValue = customConstantAttribute.Value; - return true; - } - if (attributeType.Equals(typeof(DecimalConstantAttribute))) - { - DecimalConstantAttribute decimalConstantAttribute = (DecimalConstantAttribute)(cad.Instantiate()); - defaultValue = decimalConstantAttribute.Value; - return true; - } - } - - defaultValue = null; - return false; } - public static object ConstantValueAsObject(ConstantHandle constantHandle, MetadataReader metadataReader) + private static object ConstantValueAsObject(ConstantHandle constantHandle, MetadataReader metadataReader, Type declaredType, bool raw) + { + object defaultValue = ConstantValueAsRawObject(constantHandle, metadataReader); + if ((!raw) && declaredType.IsEnum) + defaultValue = Enum.ToObject(declaredType, defaultValue); + return defaultValue; + } + + + private static object ConstantValueAsRawObject(ConstantHandle constantHandle, MetadataReader metadataReader) { if (constantHandle.IsNil) throw new BadImageFormatException(); @@ -148,4 +134,4 @@ namespace System.Reflection.Runtime throw new BadImageFormatException(); } } -} \ No newline at end of file +} diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Helpers.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Helpers.cs index 687e79b0f7..90943008c1 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Helpers.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/Helpers.cs @@ -15,8 +15,8 @@ using System.Reflection.Runtime.MethodInfos; using Internal.LowLevelLinq; using Internal.Runtime.Augments; +using Internal.Reflection.Augments; using Internal.Reflection.Core.Execution; -using Internal.Reflection.Core.NonPortable; using Internal.Reflection.Extensions.NonPortable; namespace System.Reflection.Runtime.General @@ -86,35 +86,6 @@ namespace System.Reflection.Runtime.General return new ReadOnlyCollection(enumeration.ToArray()); } - public static T[] ReadOnlyCollectionToArray(this IReadOnlyCollection collection) - { - int count = collection.Count; - T[] result = new T[count]; - int i = 0; - foreach (T element in collection) - { - result[i++] = element; - } - Debug.Assert(i == count); - return result; - } - - public static Array ReadOnlyCollectionToEnumArray(this IReadOnlyCollection collection, Type enumType) where T : struct - { - Debug.Assert(typeof(T).IsPrimitive); - Debug.Assert(enumType.IsEnum); - - int count = collection.Count; - T[] result = (T[])Array.CreateInstance(enumType, count); - int i = 0; - foreach (T element in collection) - { - result[i++] = element; - } - Debug.Assert(i == count); - return result; - } - public static MethodInfo FilterAccessor(this MethodInfo accessor, bool nonPublic) { if (nonPublic) @@ -124,18 +95,9 @@ namespace System.Reflection.Runtime.General return null; } - public static object ToRawValue(this object defaultValueOrLiteral) - { - Enum e = defaultValueOrLiteral as Enum; - if (e != null) - return RuntimeAugments.GetEnumValue(e); - return defaultValueOrLiteral; - } - public static Type GetTypeCore(this Assembly assembly, string name, bool ignoreCase) { - RuntimeAssembly runtimeAssembly = assembly as RuntimeAssembly; - if (runtimeAssembly != null) + if (assembly is RuntimeAssembly runtimeAssembly) { // Not a recursion - this one goes to the actual instance method on RuntimeAssembly. return runtimeAssembly.GetTypeCore(name, ignoreCase: ignoreCase); @@ -161,7 +123,7 @@ namespace System.Reflection.Runtime.General public static TypeLoadException CreateTypeLoadException(string typeName, string assemblyName) { string message = SR.Format(SR.TypeLoad_TypeNotFoundInAssembly, typeName, assemblyName); - return ReflectionCoreNonPortable.CreateTypeLoadException(message, typeName); + return ReflectionAugments.CreateTypeLoadException(message, typeName); } // Escape identifiers as described in "Specifying Fully Qualified Type Names" on msdn. @@ -232,5 +194,47 @@ namespace System.Reflection.Runtime.General attributes.CopyTo(result, 0); return result; } + + public static bool GetCustomAttributeDefaultValueIfAny(IEnumerable customAttributes, bool raw, out object defaultValue) + { + // Legacy: If there are multiple default value attribute, the desktop picks one at random (and so do we...) + foreach (CustomAttributeData cad in customAttributes) + { + Type attributeType = cad.AttributeType; + if (attributeType.IsSubclassOf(typeof(CustomConstantAttribute))) + { + if (raw) + { + foreach (CustomAttributeNamedArgument namedArgument in cad.NamedArguments) + { + if (namedArgument.MemberName.Equals("Value")) + { + defaultValue = namedArgument.TypedValue.Value; + return true; + } + } + defaultValue = null; + return false; + } + else + { + CustomConstantAttribute customConstantAttribute = (CustomConstantAttribute)(cad.Instantiate()); + defaultValue = customConstantAttribute.Value; + return true; + } + } + if (attributeType.Equals(typeof(DecimalConstantAttribute))) + { + // We should really do a non-instanting check if "raw == false" but given that we don't support + // reflection-only loads, there isn't an observable difference. + DecimalConstantAttribute decimalConstantAttribute = (DecimalConstantAttribute)(cad.Instantiate()); + defaultValue = decimalConstantAttribute.Value; + return true; + } + } + + defaultValue = null; + return false; + } } } diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/MetadataReaderExtensions.NativeFormat.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/MetadataReaderExtensions.NativeFormat.cs index bdc9c26df4..901d0f4baf 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/MetadataReaderExtensions.NativeFormat.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/MetadataReaderExtensions.NativeFormat.cs @@ -152,7 +152,7 @@ namespace System.Reflection.Runtime.General if (optional == modifiedType.IsOptional) { Type customModifier = modifiedType.ModifierType.Resolve(reader, typeContext); - customModifiers.Add(customModifier); + customModifiers.Insert(0, customModifier); } handle = modifiedType.Type; @@ -387,68 +387,72 @@ namespace System.Reflection.Runtime.General switch (handleType) { case HandleType.ConstantBooleanArray: - return handle.ToConstantBooleanArrayHandle(reader).GetConstantBooleanArray(reader).Value.ReadOnlyCollectionToArray(); + return handle.ToConstantBooleanArrayHandle(reader).GetConstantBooleanArray(reader).Value.ToArray(); case HandleType.ConstantCharArray: - return handle.ToConstantCharArrayHandle(reader).GetConstantCharArray(reader).Value.ReadOnlyCollectionToArray(); + return handle.ToConstantCharArrayHandle(reader).GetConstantCharArray(reader).Value.ToArray(); case HandleType.ConstantByteArray: - return handle.ToConstantByteArrayHandle(reader).GetConstantByteArray(reader).Value.ReadOnlyCollectionToArray(); + return handle.ToConstantByteArrayHandle(reader).GetConstantByteArray(reader).Value.ToArray(); case HandleType.ConstantSByteArray: - return handle.ToConstantSByteArrayHandle(reader).GetConstantSByteArray(reader).Value.ReadOnlyCollectionToArray(); + return handle.ToConstantSByteArrayHandle(reader).GetConstantSByteArray(reader).Value.ToArray(); case HandleType.ConstantInt16Array: - return handle.ToConstantInt16ArrayHandle(reader).GetConstantInt16Array(reader).Value.ReadOnlyCollectionToArray(); + return handle.ToConstantInt16ArrayHandle(reader).GetConstantInt16Array(reader).Value.ToArray(); case HandleType.ConstantUInt16Array: - return handle.ToConstantUInt16ArrayHandle(reader).GetConstantUInt16Array(reader).Value.ReadOnlyCollectionToArray(); + return handle.ToConstantUInt16ArrayHandle(reader).GetConstantUInt16Array(reader).Value.ToArray(); case HandleType.ConstantInt32Array: - return handle.ToConstantInt32ArrayHandle(reader).GetConstantInt32Array(reader).Value.ReadOnlyCollectionToArray(); + return handle.ToConstantInt32ArrayHandle(reader).GetConstantInt32Array(reader).Value.ToArray(); case HandleType.ConstantUInt32Array: - return handle.ToConstantUInt32ArrayHandle(reader).GetConstantUInt32Array(reader).Value.ReadOnlyCollectionToArray(); + return handle.ToConstantUInt32ArrayHandle(reader).GetConstantUInt32Array(reader).Value.ToArray(); case HandleType.ConstantInt64Array: - return handle.ToConstantInt64ArrayHandle(reader).GetConstantInt64Array(reader).Value.ReadOnlyCollectionToArray(); + return handle.ToConstantInt64ArrayHandle(reader).GetConstantInt64Array(reader).Value.ToArray(); case HandleType.ConstantUInt64Array: - return handle.ToConstantUInt64ArrayHandle(reader).GetConstantUInt64Array(reader).Value.ReadOnlyCollectionToArray(); + return handle.ToConstantUInt64ArrayHandle(reader).GetConstantUInt64Array(reader).Value.ToArray(); case HandleType.ConstantSingleArray: - return handle.ToConstantSingleArrayHandle(reader).GetConstantSingleArray(reader).Value.ReadOnlyCollectionToArray(); + return handle.ToConstantSingleArrayHandle(reader).GetConstantSingleArray(reader).Value.ToArray(); case HandleType.ConstantDoubleArray: - return handle.ToConstantDoubleArrayHandle(reader).GetConstantDoubleArray(reader).Value.ReadOnlyCollectionToArray(); + return handle.ToConstantDoubleArrayHandle(reader).GetConstantDoubleArray(reader).Value.ToArray(); case HandleType.ConstantEnumArray: return TryParseConstantEnumArray(handle.ToConstantEnumArrayHandle(reader), reader, out exception); case HandleType.ConstantStringArray: { - Handle[] constantHandles = handle.ToConstantStringArrayHandle(reader).GetConstantStringArray(reader).Value.ToArray(); - string[] elements = new string[constantHandles.Length]; - for (int i = 0; i < constantHandles.Length; i++) + HandleCollection constantHandles = handle.ToConstantStringArrayHandle(reader).GetConstantStringArray(reader).Value; + string[] elements = new string[constantHandles.Count]; + int i = 0; + foreach (Handle constantHandle in constantHandles) { object elementValue; - exception = constantHandles[i].TryParseConstantValue(reader, out elementValue); + exception = constantHandle.TryParseConstantValue(reader, out elementValue); if (exception != null) return null; elements[i] = (string)elementValue; + i++; } return elements; } case HandleType.ConstantHandleArray: { - Handle[] constantHandles = handle.ToConstantHandleArrayHandle(reader).GetConstantHandleArray(reader).Value.ToArray(); - object[] elements = new object[constantHandles.Length]; - for (int i = 0; i < constantHandles.Length; i++) + HandleCollection constantHandles = handle.ToConstantHandleArrayHandle(reader).GetConstantHandleArray(reader).Value; + object[] elements = new object[constantHandles.Count]; + int i = 0; + foreach (Handle constantHandle in constantHandles) { - exception = constantHandles[i].TryParseConstantValue(reader, out elements[i]); + exception = constantHandle.TryParseConstantValue(reader, out elements[i]); if (exception != null) return null; + i++; } return elements; } @@ -469,28 +473,28 @@ namespace System.Reflection.Runtime.General switch (enumArray.Value.HandleType) { case HandleType.ConstantByteArray: - return enumArray.Value.ToConstantByteArrayHandle(reader).GetConstantByteArray(reader).Value.ReadOnlyCollectionToEnumArray(elementType); + return enumArray.Value.ToConstantByteArrayHandle(reader).GetConstantByteArray(reader).Value.ToArray(elementType); case HandleType.ConstantSByteArray: - return enumArray.Value.ToConstantSByteArrayHandle(reader).GetConstantSByteArray(reader).Value.ReadOnlyCollectionToEnumArray(elementType); + return enumArray.Value.ToConstantSByteArrayHandle(reader).GetConstantSByteArray(reader).Value.ToArray(elementType); case HandleType.ConstantInt16Array: - return enumArray.Value.ToConstantInt16ArrayHandle(reader).GetConstantInt16Array(reader).Value.ReadOnlyCollectionToEnumArray(elementType); + return enumArray.Value.ToConstantInt16ArrayHandle(reader).GetConstantInt16Array(reader).Value.ToArray(elementType); case HandleType.ConstantUInt16Array: - return enumArray.Value.ToConstantUInt16ArrayHandle(reader).GetConstantUInt16Array(reader).Value.ReadOnlyCollectionToEnumArray(elementType); + return enumArray.Value.ToConstantUInt16ArrayHandle(reader).GetConstantUInt16Array(reader).Value.ToArray(elementType); case HandleType.ConstantInt32Array: - return enumArray.Value.ToConstantInt32ArrayHandle(reader).GetConstantInt32Array(reader).Value.ReadOnlyCollectionToEnumArray(elementType); + return enumArray.Value.ToConstantInt32ArrayHandle(reader).GetConstantInt32Array(reader).Value.ToArray(elementType); case HandleType.ConstantUInt32Array: - return enumArray.Value.ToConstantUInt32ArrayHandle(reader).GetConstantUInt32Array(reader).Value.ReadOnlyCollectionToEnumArray(elementType); + return enumArray.Value.ToConstantUInt32ArrayHandle(reader).GetConstantUInt32Array(reader).Value.ToArray(elementType); case HandleType.ConstantInt64Array: - return enumArray.Value.ToConstantInt64ArrayHandle(reader).GetConstantInt64Array(reader).Value.ReadOnlyCollectionToEnumArray(elementType); + return enumArray.Value.ToConstantInt64ArrayHandle(reader).GetConstantInt64Array(reader).Value.ToArray(elementType); case HandleType.ConstantUInt64Array: - return enumArray.Value.ToConstantUInt64ArrayHandle(reader).GetConstantUInt64Array(reader).Value.ReadOnlyCollectionToEnumArray(elementType); + return enumArray.Value.ToConstantUInt64ArrayHandle(reader).GetConstantUInt64Array(reader).Value.ToArray(elementType); default: throw new BadImageFormatException(); @@ -609,7 +613,7 @@ namespace System.Reflection.Runtime.General yield return namespaceHandle; NamespaceDefinition namespaceDefinition = namespaceHandle.GetNamespaceDefinition(reader); - foreach (NamespaceDefinitionHandle childNamespaceHandle in GetTransitiveNamespaces(reader, namespaceDefinition.NamespaceDefinitions)) + foreach (NamespaceDefinitionHandle childNamespaceHandle in GetTransitiveNamespaces(reader, namespaceDefinition.NamespaceDefinitions.AsEnumerable())) yield return childNamespaceHandle; } } @@ -641,7 +645,7 @@ namespace System.Reflection.Runtime.General yield return typeDefinitionHandle; - foreach (TypeDefinitionHandle nestedTypeDefinitionHandle in GetTransitiveTypes(reader, typeDefinition.NestedTypes, publicOnly)) + foreach (TypeDefinitionHandle nestedTypeDefinitionHandle in GetTransitiveTypes(reader, typeDefinition.NestedTypes.AsEnumerable(), publicOnly)) yield return nestedTypeDefinitionHandle; } } @@ -683,6 +687,259 @@ namespace System.Reflection.Runtime.General fullName.Append(typeName); return fullName.ToString(); } + + public static IEnumerable AsEnumerable(this NamespaceDefinitionHandleCollection collection) + { + foreach (NamespaceDefinitionHandle handle in collection) + yield return handle; + } + + public static IEnumerable AsEnumerable(this TypeDefinitionHandleCollection collection) + { + foreach (TypeDefinitionHandle handle in collection) + yield return handle; + } + + public static Handle[] ToArray(this HandleCollection collection) + { + int count = collection.Count; + Handle[] result = new Handle[count]; + int i = 0; + foreach (Handle element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + public static bool[] ToArray(this BooleanCollection collection) + { + int count = collection.Count; + bool[] result = new bool[count]; + int i = 0; + foreach (bool element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + public static char[] ToArray(this CharCollection collection) + { + int count = collection.Count; + char[] result = new char[count]; + int i = 0; + foreach (char element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + public static float[] ToArray(this SingleCollection collection) + { + int count = collection.Count; + float[] result = new float[count]; + int i = 0; + foreach (float element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + public static double[] ToArray(this DoubleCollection collection) + { + int count = collection.Count; + double[] result = new double[count]; + int i = 0; + foreach (double element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + public static byte[] ToArray(this ByteCollection collection, Type enumType = null) + { + int count = collection.Count; + byte[] result; + if (enumType != null) + { + Debug.Assert(enumType.IsEnum); + result = (byte[])Array.CreateInstance(enumType, count); + } + else + { + result = new byte[count]; + } + int i = 0; + foreach (byte element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + public static sbyte[] ToArray(this SByteCollection collection, Type enumType = null) + { + int count = collection.Count; + sbyte[] result; + if (enumType != null) + { + Debug.Assert(enumType.IsEnum); + result = (sbyte[])Array.CreateInstance(enumType, count); + } + else + { + result = new sbyte[count]; + } + int i = 0; + foreach (sbyte element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + public static ushort[] ToArray(this UInt16Collection collection, Type enumType = null) + { + int count = collection.Count; + ushort[] result; + if (enumType != null) + { + Debug.Assert(enumType.IsEnum); + result = (ushort[])Array.CreateInstance(enumType, count); + } + else + { + result = new ushort[count]; + } + int i = 0; + foreach (ushort element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + public static short[] ToArray(this Int16Collection collection, Type enumType = null) + { + int count = collection.Count; + short[] result; + if (enumType != null) + { + Debug.Assert(enumType.IsEnum); + result = (short[])Array.CreateInstance(enumType, count); + } + else + { + result = new short[count]; + } + int i = 0; + foreach (short element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + public static uint[] ToArray(this UInt32Collection collection, Type enumType = null) + { + int count = collection.Count; + uint[] result; + if (enumType != null) + { + Debug.Assert(enumType.IsEnum); + result = (uint[])Array.CreateInstance(enumType, count); + } + else + { + result = new uint[count]; + } + int i = 0; + foreach (uint element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + public static int[] ToArray(this Int32Collection collection, Type enumType = null) + { + int count = collection.Count; + int[] result; + if (enumType != null) + { + Debug.Assert(enumType.IsEnum); + result = (int[])Array.CreateInstance(enumType, count); + } + else + { + result = new int[count]; + } + int i = 0; + foreach (int element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + public static ulong[] ToArray(this UInt64Collection collection, Type enumType = null) + { + int count = collection.Count; + ulong[] result; + if (enumType != null) + { + Debug.Assert(enumType.IsEnum); + result = (ulong[])Array.CreateInstance(enumType, count); + } + else + { + result = new ulong[count]; + } + int i = 0; + foreach (ulong element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } + + public static long[] ToArray(this Int64Collection collection, Type enumType = null) + { + int count = collection.Count; + long[] result; + if (enumType != null) + { + Debug.Assert(enumType.IsEnum); + result = (long[])Array.CreateInstance(enumType, count); + } + else + { + result = new long[count]; + } + int i = 0; + foreach (long element in collection) + { + result[i++] = element; + } + Debug.Assert(i == count); + return result; + } } } diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/NativeFormat/DefaultValueParser.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/NativeFormat/DefaultValueParser.cs new file mode 100644 index 0000000000..e76be19145 --- /dev/null +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/NativeFormat/DefaultValueParser.cs @@ -0,0 +1,29 @@ +// 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.Collections.Generic; +using Internal.Metadata.NativeFormat; + +namespace System.Reflection.Runtime.General.NativeFormat +{ + internal static class DefaultValueParser + { + public static bool GetDefaultValueIfAny(MetadataReader reader, Handle constantHandle, Type declaredType, IEnumerable customAttributes, bool raw, out object defaultValue) + { + if (!(constantHandle.IsNull(reader))) + { + defaultValue = constantHandle.ParseConstantValue(reader); + if ((!raw) && declaredType.IsEnum) + defaultValue = Enum.ToObject(declaredType, defaultValue); + return true; + } + + if (Helpers.GetCustomAttributeDefaultValueIfAny(customAttributes, raw, out defaultValue)) + return true; + + defaultValue = null; + return false; + } + } +} diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/NonOverriddenApis.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/NonOverriddenApis.cs index 849d1fae97..2e5af22b97 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/NonOverriddenApis.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/NonOverriddenApis.cs @@ -147,6 +147,7 @@ namespace System.Reflection.Runtime.TypeInfos public sealed override bool IsInstanceOfType(object o) => base.IsInstanceOfType(o); public sealed override bool IsSerializable => base.IsSerializable; public sealed override bool IsEquivalentTo(Type other) => base.IsEquivalentTo(other); // Note: If we enable COM type equivalence, this is no longer the correct implementation. + public sealed override bool IsSignatureType => base.IsSignatureType; public sealed override IEnumerable DeclaredConstructors => base.DeclaredConstructors; public sealed override IEnumerable DeclaredEvents => base.DeclaredEvents; diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/QSignatureTypeHandle.NativeFormat.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/QSignatureTypeHandle.NativeFormat.cs index 670443d726..52f6228dea 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/QSignatureTypeHandle.NativeFormat.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/QSignatureTypeHandle.NativeFormat.cs @@ -20,11 +20,11 @@ namespace System.Reflection.Runtime.General { if (!skipCheck) { - if (!handle.IsTypeDefRefOrSpecHandle(reader)) + if (!handle.IsTypeDefRefSpecOrModifiedTypeHandle(reader)) throw new BadImageFormatException(); } - Debug.Assert(handle.IsTypeDefRefOrSpecHandle(reader)); + Debug.Assert(handle.IsTypeDefRefSpecOrModifiedTypeHandle(reader)); _reader = reader; _handle = handle; diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/QSignatureTypeHandle.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/QSignatureTypeHandle.cs index a2d426385c..e021b42605 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/QSignatureTypeHandle.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/QSignatureTypeHandle.cs @@ -36,10 +36,9 @@ namespace System.Reflection.Runtime.General { return _handle.TryResolve((global::Internal.Metadata.NativeFormat.MetadataReader)Reader, typeContext, ref exception); } - + #if ECMA_METADATA_SUPPORT - global::System.Reflection.Metadata.MetadataReader ecmaReader = Reader as global::System.Reflection.Metadata.MetadataReader; - if (ecmaReader != null) + if (Reader is global::System.Reflection.Metadata.MetadataReader ecmaReader) { return TryResolveSignature(typeContext, ref exception); } @@ -86,4 +85,4 @@ namespace System.Reflection.Runtime.General } } } -} \ No newline at end of file +} diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs index 8c643d9d82..96e76c1aae 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs @@ -30,11 +30,14 @@ namespace System.Reflection.Runtime.General { } - public sealed override Assembly Load(AssemblyName refName) + public sealed override Assembly Load(AssemblyName assemblyRef, bool throwOnFileNotFound) { - if (refName == null) - throw new ArgumentNullException("assemblyRef"); - return RuntimeAssembly.GetRuntimeAssembly(refName.ToRuntimeAssemblyName()); + if (assemblyRef == null) + throw new ArgumentNullException(nameof(assemblyRef)); + if (throwOnFileNotFound) + return RuntimeAssembly.GetRuntimeAssembly(assemblyRef.ToRuntimeAssemblyName()); + else + return RuntimeAssembly.GetRuntimeAssemblyIfExists(assemblyRef.ToRuntimeAssemblyName()); } public sealed override Assembly Load(byte[] rawAssembly, byte[] pdbSymbolStore) @@ -200,12 +203,10 @@ namespace System.Reflection.Runtime.General if (method == null) throw new ArgumentNullException(nameof(method)); - RuntimeTypeInfo runtimeDelegateType = type as RuntimeTypeInfo; - if (runtimeDelegateType == null) + if (!(type is RuntimeTypeInfo runtimeDelegateType)) throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); - RuntimeMethodInfo runtimeMethodInfo = method as RuntimeMethodInfo; - if (runtimeMethodInfo == null) + if (!(method is RuntimeMethodInfo runtimeMethodInfo)) throw new ArgumentException(SR.Argument_MustBeRuntimeMethodInfo, nameof(method)); if (!runtimeDelegateType.IsDelegate) @@ -231,8 +232,7 @@ namespace System.Reflection.Runtime.General if (method == null) throw new ArgumentNullException(nameof(method)); - RuntimeTypeInfo runtimeDelegateType = type as RuntimeTypeInfo; - if (runtimeDelegateType == null) + if (!(type is RuntimeTypeInfo runtimeDelegateType)) throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); if (!runtimeDelegateType.IsDelegate) throw new ArgumentException(SR.Arg_MustBeDelegate); @@ -260,12 +260,10 @@ namespace System.Reflection.Runtime.General if (method == null) throw new ArgumentNullException(nameof(method)); - RuntimeTypeInfo runtimeDelegateType = type as RuntimeTypeInfo; - if (runtimeDelegateType == null) + if (!(type is RuntimeTypeInfo runtimeDelegateType)) throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); - RuntimeTypeInfo runtimeContainingType = target as RuntimeTypeInfo; - if (runtimeContainingType == null) + if (!(target is RuntimeTypeInfo runtimeContainingType)) throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(target)); if (!runtimeDelegateType.IsDelegate) @@ -292,11 +290,11 @@ namespace System.Reflection.Runtime.General BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.ExactBinding; if (isStatic) { - bindingFlags |= BindingFlags.Static | BindingFlags.FlattenHierarchy; + bindingFlags |= BindingFlags.Static; } else { - bindingFlags |= BindingFlags.Instance; + bindingFlags |= BindingFlags.Instance | BindingFlags.DeclaredOnly; } if (ignoreCase) { @@ -310,14 +308,16 @@ namespace System.Reflection.Runtime.General { parameterTypes[i] = parameters[i].ParameterType; } - MethodInfo methodInfo = containingType.GetMethod(method, bindingFlags, null, parameterTypes, null); - if (methodInfo == null) - return null; - if (!methodInfo.ReturnType.Equals(invokeMethod.ReturnType)) - return null; + while (containingType != null) + { + MethodInfo methodInfo = containingType.GetMethod(method, 0, bindingFlags, null, parameterTypes, null); + if (methodInfo != null && methodInfo.ReturnType.Equals(invokeMethod.ReturnType)) + return (RuntimeMethodInfo)methodInfo; // This cast is safe since we already verified that containingType is runtime implemented. - return (RuntimeMethodInfo)methodInfo; // This cast is safe since we already verified that containingType is runtime implemented. + containingType = (RuntimeTypeInfo)(containingType.BaseType); + } + return null; } public sealed override Type GetTypeFromCLSID(Guid clsid, string server, bool throwOnError) @@ -338,7 +338,8 @@ namespace System.Reflection.Runtime.General case RuntimeConstructorInfo constructorInfo: return constructorInfo.LdFtnResult; default: - throw new PlatformNotSupportedException(); + Debug.Fail("RuntimeMethodHandle should only return a methodbase implemented by the runtime."); + throw new NotSupportedException(); } } @@ -361,8 +362,7 @@ namespace System.Reflection.Runtime.General Type targetType = target.GetType(); for (int i = 0; i < flds.Length; i++) { - RuntimeFieldInfo field = flds[i] as RuntimeFieldInfo; - if (field == null) + if (!(flds[i] is RuntimeFieldInfo field)) throw new ArgumentException(SR.Argument_MustBeRuntimeFieldInfo); if (field.IsInitOnly || field.IsStatic) throw new ArgumentException(SR.Argument_TypedReferenceInvalidField); @@ -387,5 +387,7 @@ namespace System.Reflection.Runtime.General } public sealed override Assembly[] GetLoadedAssemblies() => RuntimeAssembly.GetLoadedAssemblies(); + + public sealed override EnumInfo GetEnumInfo(Type type) => type.CastToRuntimeTypeInfo().EnumInfo; } } diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/RuntimeTypeHandleKey.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/RuntimeTypeHandleKey.cs index f8d40acdfa..2ae2efdc83 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/RuntimeTypeHandleKey.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/RuntimeTypeHandleKey.cs @@ -20,9 +20,9 @@ namespace System.Reflection.Runtime.General public override bool Equals(object obj) { - if (!(obj is RuntimeTypeHandleKey)) + if (!(obj is RuntimeTypeHandleKey other)) return false; - return Equals((RuntimeTypeHandleKey)obj); + return Equals(other); } public bool Equals(RuntimeTypeHandleKey other) diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ThunkedApis.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ThunkedApis.cs index c2cbaae586..7f09087644 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ThunkedApis.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ThunkedApis.cs @@ -8,15 +8,10 @@ // keep the main files less cluttered. // -using System; using System.IO; using System.Text; -using System.Reflection; using System.Diagnostics; using System.Globalization; -using System.Collections.Generic; -using System.Runtime.Serialization; -using System.Runtime.InteropServices; using System.Reflection.Runtime.General; using Internal.LowLevelLinq; @@ -68,7 +63,6 @@ namespace System.Reflection.Runtime.Assemblies public sealed override string CodeBase { get { throw new PlatformNotSupportedException(); } } public sealed override Assembly GetSatelliteAssembly(CultureInfo culture) { throw new PlatformNotSupportedException(); } public sealed override Assembly GetSatelliteAssembly(CultureInfo culture, Version version) { throw new PlatformNotSupportedException(); } - public sealed override string ImageRuntimeVersion { get { throw new PlatformNotSupportedException(); } } public sealed override AssemblyName[] GetReferencedAssemblies() { throw new PlatformNotSupportedException(); } public sealed override Module GetModule(string name) { throw new PlatformNotSupportedException(); } } @@ -97,21 +91,6 @@ namespace System.Reflection.Runtime.EventInfos } } -namespace System.Reflection.Runtime.FieldInfos -{ - internal abstract partial class RuntimeFieldInfo - { - public sealed override object GetRawConstantValue() - { - if (!IsLiteral) - throw new InvalidOperationException(); - - object value = GetValue(null); - return value.ToRawValue(); - } - } -} - namespace System.Reflection.Runtime.MethodInfos { internal abstract partial class RuntimeMethodInfo @@ -126,14 +105,6 @@ namespace System.Reflection.Runtime.MethodInfos } } -namespace System.Reflection.Runtime.ParameterInfos -{ - internal abstract partial class RuntimeParameterInfo - { - public sealed override object RawDefaultValue => DefaultValue.ToRawValue(); - } -} - namespace System.Reflection.Runtime.PropertyInfos { internal abstract partial class RuntimePropertyInfo @@ -157,8 +128,6 @@ namespace System.Reflection.Runtime.PropertyInfos accessors[index++] = setter; return accessors; } - - public sealed override object GetRawConstantValue() => GetConstantValue().ToRawValue(); } } diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeForwardInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeForwardInfo.cs new file mode 100644 index 0000000000..600eaaad49 --- /dev/null +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeForwardInfo.cs @@ -0,0 +1,26 @@ +// 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.Diagnostics; + +namespace System.Reflection.Runtime.General +{ + internal struct TypeForwardInfo + { + public TypeForwardInfo(RuntimeAssemblyName redirectedAssemblyName, string namespaceName, string typeName) + { + Debug.Assert(redirectedAssemblyName != null); + Debug.Assert(namespaceName != null); + Debug.Assert(typeName != null); + + RedirectedAssemblyName = redirectedAssemblyName; + NamespaceName = namespaceName; + TypeName = typeName; + } + + public RuntimeAssemblyName RedirectedAssemblyName { get; } + public string NamespaceName { get; } + public string TypeName { get; } + } +} diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeResolver.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeResolver.cs index 5f3bcd60a1..c1f053247d 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeResolver.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeResolver.cs @@ -38,10 +38,9 @@ namespace System.Reflection.Runtime.General { return global::Internal.Metadata.NativeFormat.Handle.FromIntToken(typeDefOrRefOrSpec.Handle).TryResolve((global::Internal.Metadata.NativeFormat.MetadataReader)typeDefOrRefOrSpec.Reader, typeContext, ref exception); } - + #if ECMA_METADATA_SUPPORT - global::System.Reflection.Metadata.MetadataReader ecmaReader = typeDefOrRefOrSpec.Reader as global::System.Reflection.Metadata.MetadataReader; - if (ecmaReader != null) + if (typeDefOrRefOrSpec.Reader is global::System.Reflection.Metadata.MetadataReader ecmaReader) return global::System.Reflection.Metadata.Ecma335.MetadataTokens.Handle(typeDefOrRefOrSpec.Handle).TryResolve(ecmaReader, typeContext, ref exception); #endif diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeUnifier.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeUnifier.cs index 02e2f22681..98d3380200 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeUnifier.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeUnifier.cs @@ -37,37 +37,31 @@ namespace System.Reflection.Runtime.General { internal static partial class TypeUnifier { - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static RuntimeTypeInfo GetArrayType(this RuntimeTypeInfo elementType) { - return elementType.GetArrayType(default(RuntimeTypeHandle)); + return RuntimeArrayTypeInfo.GetArrayTypeInfo(elementType, multiDim: false, rank: 1); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static RuntimeTypeInfo GetMultiDimArrayType(this RuntimeTypeInfo elementType, int rank) { - return elementType.GetMultiDimArrayType(rank, default(RuntimeTypeHandle)); + return RuntimeArrayTypeInfo.GetArrayTypeInfo(elementType, multiDim: true, rank: rank); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static RuntimeTypeInfo GetByRefType(this RuntimeTypeInfo targetType) { - return targetType.GetByRefType(default(RuntimeTypeHandle)); + return RuntimeByRefTypeInfo.GetByRefTypeInfo(targetType); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static RuntimeTypeInfo GetPointerType(this RuntimeTypeInfo targetType) { - return targetType.GetPointerType(default(RuntimeTypeHandle)); + return RuntimePointerTypeInfo.GetPointerTypeInfo(targetType); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static RuntimeTypeInfo GetConstructedGenericType(this RuntimeTypeInfo genericTypeDefinition, RuntimeTypeInfo[] genericTypeArguments) { - return genericTypeDefinition.GetConstructedGenericType(genericTypeArguments, default(RuntimeTypeHandle)); + return RuntimeConstructedGenericTypeInfo.GetRuntimeConstructedGenericTypeInfo(genericTypeDefinition, genericTypeArguments); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static RuntimeTypeInfo GetTypeForRuntimeTypeHandle(this RuntimeTypeHandle typeHandle) { Type type = Type.GetTypeFromHandle(typeHandle); @@ -80,31 +74,26 @@ namespace System.Reflection.Runtime.General // waste cycles looking up the handle again from the mapping tables.) //====================================================================================================== - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static RuntimeTypeInfo GetArrayType(this RuntimeTypeInfo elementType, RuntimeTypeHandle precomputedTypeHandle) { return RuntimeArrayTypeInfo.GetArrayTypeInfo(elementType, multiDim: false, rank: 1, precomputedTypeHandle: precomputedTypeHandle); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static RuntimeTypeInfo GetMultiDimArrayType(this RuntimeTypeInfo elementType, int rank, RuntimeTypeHandle precomputedTypeHandle) { return RuntimeArrayTypeInfo.GetArrayTypeInfo(elementType, multiDim: true, rank: rank, precomputedTypeHandle: precomputedTypeHandle); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static RuntimeTypeInfo GetPointerType(this RuntimeTypeInfo targetType, RuntimeTypeHandle precomputedTypeHandle) { return RuntimePointerTypeInfo.GetPointerTypeInfo(targetType, precomputedTypeHandle); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static RuntimeTypeInfo GetByRefType(this RuntimeTypeInfo targetType, RuntimeTypeHandle precomputedTypeHandle) { return RuntimeByRefTypeInfo.GetByRefTypeInfo(targetType, precomputedTypeHandle); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static RuntimeTypeInfo GetConstructedGenericType(this RuntimeTypeInfo genericTypeDefinition, RuntimeTypeInfo[] genericTypeArguments, RuntimeTypeHandle precomputedTypeHandle) { return RuntimeConstructedGenericTypeInfo.GetRuntimeConstructedGenericTypeInfo(genericTypeDefinition, genericTypeArguments, precomputedTypeHandle); @@ -194,12 +183,16 @@ namespace System.Reflection.Runtime.TypeInfos //----------------------------------------------------------------------------------------------------------- internal sealed partial class RuntimeArrayTypeInfo : RuntimeHasElementTypeInfo { + internal static RuntimeArrayTypeInfo GetArrayTypeInfo(RuntimeTypeInfo elementType, bool multiDim, int rank) + { + return GetArrayTypeInfo(elementType, multiDim, rank, GetRuntimeTypeHandleIfAny(elementType, multiDim, rank)); + } + internal static RuntimeArrayTypeInfo GetArrayTypeInfo(RuntimeTypeInfo elementType, bool multiDim, int rank, RuntimeTypeHandle precomputedTypeHandle) { Debug.Assert(multiDim || rank == 1); - RuntimeTypeHandle typeHandle = precomputedTypeHandle.IsNull() ? GetRuntimeTypeHandleIfAny(elementType, multiDim, rank) : precomputedTypeHandle; - UnificationKey key = new UnificationKey(elementType, typeHandle); + UnificationKey key = new UnificationKey(elementType, precomputedTypeHandle); RuntimeArrayTypeInfo type; if (!multiDim) type = ArrayTypeTable.Table.GetOrAdd(key); @@ -279,10 +272,8 @@ namespace System.Reflection.Runtime.TypeInfos { Debug.Assert(multiDim || rank == 1); - if (elementType.IsByRef) + if (elementType.IsByRef || elementType.IsByRefLike) throw new TypeLoadException(SR.Format(SR.ArgumentException_InvalidArrayElementType, elementType)); - if (elementType.IsGenericTypeDefinition) - throw new ArgumentException(SR.Format(SR.ArgumentException_InvalidArrayElementType, elementType)); // We only permit creating parameterized types if the pay-for-play policy specifically allows them *or* if the result // type would be an open type. @@ -296,10 +287,14 @@ namespace System.Reflection.Runtime.TypeInfos //----------------------------------------------------------------------------------------------------------- internal sealed partial class RuntimeByRefTypeInfo : RuntimeHasElementTypeInfo { + internal static RuntimeByRefTypeInfo GetByRefTypeInfo(RuntimeTypeInfo elementType) + { + return GetByRefTypeInfo(elementType, GetRuntimeTypeHandleIfAny(elementType)); + } + internal static RuntimeByRefTypeInfo GetByRefTypeInfo(RuntimeTypeInfo elementType, RuntimeTypeHandle precomputedTypeHandle) { - RuntimeTypeHandle typeHandle = precomputedTypeHandle.IsNull() ? GetRuntimeTypeHandleIfAny(elementType) : precomputedTypeHandle; - RuntimeByRefTypeInfo type = ByRefTypeTable.Table.GetOrAdd(new UnificationKey(elementType, typeHandle)); + RuntimeByRefTypeInfo type = ByRefTypeTable.Table.GetOrAdd(new UnificationKey(elementType, precomputedTypeHandle)); type.EstablishDebugName(); return type; } @@ -336,10 +331,14 @@ namespace System.Reflection.Runtime.TypeInfos //----------------------------------------------------------------------------------------------------------- internal sealed partial class RuntimePointerTypeInfo : RuntimeHasElementTypeInfo { + internal static RuntimePointerTypeInfo GetPointerTypeInfo(RuntimeTypeInfo elementType) + { + return GetPointerTypeInfo(elementType, precomputedTypeHandle: GetRuntimeTypeHandleIfAny(elementType)); + } + internal static RuntimePointerTypeInfo GetPointerTypeInfo(RuntimeTypeInfo elementType, RuntimeTypeHandle precomputedTypeHandle) { - RuntimeTypeHandle typeHandle = precomputedTypeHandle.IsNull() ? GetRuntimeTypeHandleIfAny(elementType) : precomputedTypeHandle; - RuntimePointerTypeInfo type = PointerTypeTable.Table.GetOrAdd(new UnificationKey(elementType, typeHandle)); + RuntimePointerTypeInfo type = PointerTypeTable.Table.GetOrAdd(new UnificationKey(elementType, precomputedTypeHandle)); type.EstablishDebugName(); return type; } @@ -376,10 +375,14 @@ namespace System.Reflection.Runtime.TypeInfos //----------------------------------------------------------------------------------------------------------- internal sealed partial class RuntimeConstructedGenericTypeInfo : RuntimeTypeInfo, IKeyedItem { + internal static RuntimeConstructedGenericTypeInfo GetRuntimeConstructedGenericTypeInfo(RuntimeTypeInfo genericTypeDefinition, RuntimeTypeInfo[] genericTypeArguments) + { + return GetRuntimeConstructedGenericTypeInfo(genericTypeDefinition, genericTypeArguments, precomputedTypeHandle: GetRuntimeTypeHandleIfAny(genericTypeDefinition, genericTypeArguments)); + } + internal static RuntimeConstructedGenericTypeInfo GetRuntimeConstructedGenericTypeInfo(RuntimeTypeInfo genericTypeDefinition, RuntimeTypeInfo[] genericTypeArguments, RuntimeTypeHandle precomputedTypeHandle) { - RuntimeTypeHandle typeHandle = precomputedTypeHandle.IsNull() ? GetRuntimeTypeHandleIfAny(genericTypeDefinition, genericTypeArguments) : precomputedTypeHandle; - UnificationKey key = new UnificationKey(genericTypeDefinition, genericTypeArguments, typeHandle); + UnificationKey key = new UnificationKey(genericTypeDefinition, genericTypeArguments, precomputedTypeHandle); RuntimeConstructedGenericTypeInfo typeInfo = ConstructedGenericTypeTable.Table.GetOrAdd(key); typeInfo.EstablishDebugName(); return typeInfo; diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodInvoker.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodInvoker.cs new file mode 100644 index 0000000000..ed124f01ac --- /dev/null +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodInvoker.cs @@ -0,0 +1,72 @@ +// 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.Diagnostics; + +using Internal.Runtime.Augments; +using Internal.Reflection.Core.Execution; + +namespace System.Reflection.Runtime.MethodInfos +{ + // + // Custom invoker for edge case scenarios not handled by the toolchain. Examples: Strings and Nullables. + // + internal sealed class CustomMethodInvoker : MethodInvoker + { + public CustomMethodInvoker(Type thisType, Type[] parameterTypes, InvokerOptions options, CustomMethodInvokerAction action) + { + _action = action; + _options = options; + _thisType = thisType; + _parameterTypes = parameterTypes; + } + + protected sealed override object Invoke(object thisObject, object[] arguments, BinderBundle binderBundle, bool wrapInTargetInvocationException) + { + Debug.Assert(arguments != null); + + // This does not handle optional parameters. None of the methods we use custom invocation for have them. + if (!(thisObject == null && 0 != (_options & InvokerOptions.AllowNullThis))) + ValidateThis(thisObject, _thisType.TypeHandle); + + if (arguments.Length != _parameterTypes.Length) + throw new TargetParameterCountException(); + + object[] convertedArguments = new object[arguments.Length]; + for (int i = 0; i < arguments.Length; i++) + { + convertedArguments[i] = RuntimeAugments.CheckArgument(arguments[i], _parameterTypes[i].TypeHandle, binderBundle); + } + object result; + try + { + result = _action(thisObject, convertedArguments, _thisType); + } + catch (Exception e) when (wrapInTargetInvocationException && ((_options & InvokerOptions.DontWrapException) == 0)) + { + throw new TargetInvocationException(e); + } + return result; + } + + public sealed override Delegate CreateDelegate(RuntimeTypeHandle delegateType, object target, bool isStatic, bool isVirtual, bool isOpen) + { + if (_thisType.IsConstructedGenericType && _thisType.GetGenericTypeDefinition() == CommonRuntimeTypes.Nullable) + { + // Desktop compat: MethodInfos to Nullable methods cannot be turned into delegates. + throw new ArgumentException(SR.Arg_DlgtTargMeth); + } + + throw new PlatformNotSupportedException(); + } + + public sealed override IntPtr LdFtnResult => throw new PlatformNotSupportedException(); + + private readonly InvokerOptions _options; + private readonly CustomMethodInvokerAction _action; + private readonly Type _thisType; + private readonly Type[] _parameterTypes; + } +} + diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodInvokerAction.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodInvokerAction.cs new file mode 100644 index 0000000000..cf7ca94e26 --- /dev/null +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodInvokerAction.cs @@ -0,0 +1,8 @@ +// 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. + +namespace System.Reflection.Runtime.MethodInfos +{ + internal delegate object CustomMethodInvokerAction(object thisObject, object[] args, Type thisType); +} diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodMapper.Nullable.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodMapper.Nullable.cs new file mode 100644 index 0000000000..a33e0f862b --- /dev/null +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodMapper.Nullable.cs @@ -0,0 +1,108 @@ +// 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.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Reflection.Runtime.General; + +namespace System.Reflection.Runtime.MethodInfos +{ + internal static partial class CustomMethodMapper + { + // + // Nullables are another edge case. + // + private static class NullableActions + { + public static Dictionary Map + { + get + { + if (s_lazyMap == null) + { + Dictionary map = new Dictionary(); + + Type type = CommonRuntimeTypes.Nullable; + Type theT = type.GetGenericTypeParameters()[0]; + + map.AddMethod(type, nameof(Nullable.ToString), Array.Empty(), + (object thisObject, object[] args, Type thisType) => + { + return thisObject == null ? string.Empty : thisObject.ToString(); + } + ); + + map.AddMethod(type, nameof(Nullable.Equals), new Type[] { CommonRuntimeTypes.Object }, + (object thisObject, object[] args, Type thisType) => + { + object other = args[0]; + if (thisObject == null) + return other == null; + if (other == null) + return false; + return thisObject.Equals(other); + } + ); + + map.AddMethod(type, nameof(Nullable.GetHashCode), Array.Empty(), + (object thisObject, object[] args, Type thisType) => + { + return thisObject == null ? 0 : thisObject.GetHashCode(); + } + ); + + map.AddConstructor(type, new Type[] { theT }, + (object thisObject, object[] args, Type thisType) => + { + return args[0]; + } + ); + + map.AddMethod(type, "get_" + nameof(Nullable.HasValue), Array.Empty(), + (object thisObject, object[] args, Type thisType) => + { + return thisObject != null; + } + ); + + map.AddMethod(type, "get_" + nameof(Nullable.Value), Array.Empty(), + (object thisObject, object[] args, Type thisType) => + { + if (thisObject == null) + throw new InvalidOperationException(SR.InvalidOperation_NoValue); + return thisObject; + } + ); + + map.AddMethod(type, nameof(Nullable.GetValueOrDefault), Array.Empty(), + (object thisObject, object[] args, Type thisType) => + { + if (thisObject == null) + return RuntimeHelpers.GetUninitializedObject(thisType.GenericTypeArguments[0]); + + return thisObject; + } + ); + + map.AddMethod(type, nameof(Nullable.GetValueOrDefault), new Type[] { theT }, + (object thisObject, object[] args, Type thisType) => + { + if (thisObject == null) + return args[0]; + return thisObject; + } + ); + + s_lazyMap = map; + } + + return s_lazyMap; + } + } + + private static volatile Dictionary s_lazyMap; + } + } +} + diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodMapper.String.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodMapper.String.cs new file mode 100644 index 0000000000..acd1dada3d --- /dev/null +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodMapper.String.cs @@ -0,0 +1,96 @@ +// 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.Text; +using System.Collections.Generic; + +namespace System.Reflection.Runtime.MethodInfos +{ + internal static partial class CustomMethodMapper + { + // + // String constructors require special casing down the stack, being the only variable-sized objects created via a constructor. + // + private static class StringActions + { + public static Dictionary Map + { + get + { + if (s_lazyMap == null) + { + Dictionary map = new Dictionary(); + + Type type = CommonRuntimeTypes.String; + + unsafe + { + map.AddConstructor(type, new Type[] { CommonRuntimeTypes.Char, CommonRuntimeTypes.Int32 }, + (object thisObject, object[] args, Type thisType) => + { + return new string((char)(args[0]), (int)(args[1])); + } + ); + + map.AddConstructor(type, new Type[] { typeof(char[]) }, + (object thisObject, object[] args, Type thisType) => + { + return new string((char[])(args[0])); + } + ); + + map.AddConstructor(type, new Type[] { typeof(char[]), CommonRuntimeTypes.Int32, CommonRuntimeTypes.Int32 }, + (object thisObject, object[] args, Type thisType) => + { + return new string((char[])(args[0]), (int)(args[1]), (int)(args[2])); + } + ); + + map.AddConstructor(type, new Type[] { typeof(char*) }, + (object thisObject, object[] args, Type thisType) => + { + return new string((char*)(IntPtr)(args[0])); + } + ); + + map.AddConstructor(type, new Type[] { typeof(char*), CommonRuntimeTypes.Int32, CommonRuntimeTypes.Int32 }, + (object thisObject, object[] args, Type thisType) => + { + return new string((char*)(IntPtr)(args[0]), (int)(args[1]), (int)(args[2])); + } + ); + + map.AddConstructor(type, new Type[] { typeof(sbyte*) }, + (object thisObject, object[] args, Type thisType) => + { + return new string((sbyte*)(IntPtr)(args[0])); + } + ); + + map.AddConstructor(type, new Type[] { typeof(sbyte*), CommonRuntimeTypes.Int32, CommonRuntimeTypes.Int32 }, + (object thisObject, object[] args, Type thisType) => + { + return new string((sbyte*)(IntPtr)(args[0]), (int)(args[1]), (int)(args[2])); + } + ); + + map.AddConstructor(type, new Type[] { typeof(sbyte*), CommonRuntimeTypes.Int32, CommonRuntimeTypes.Int32, typeof(Encoding) }, + (object thisObject, object[] args, Type thisType) => + { + return new string((sbyte*)(IntPtr)(args[0]), (int)(args[1]), (int)(args[2]), (Encoding)(args[3])); + } + ); + } + + s_lazyMap = map; + } + + return s_lazyMap; + } + } + private static volatile Dictionary s_lazyMap; + } + } +} + diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodMapper.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodMapper.cs new file mode 100644 index 0000000000..ce23069145 --- /dev/null +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/CustomMethodMapper.cs @@ -0,0 +1,71 @@ +// 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.Diagnostics; +using System.Collections.Generic; + +using Internal.Reflection.Core.Execution; + +namespace System.Reflection.Runtime.MethodInfos +{ + internal static partial class CustomMethodMapper + { + // + // Certain types and methods are edge-cases that require special handling. + // + public static MethodInvoker GetCustomMethodInvokerIfNeeded(this MethodBase methodBase) + { + Type declaringType = methodBase.DeclaringType; + bool isNullable = declaringType.IsConstructedGenericType && declaringType.GetGenericTypeDefinition() == CommonRuntimeTypes.Nullable; + + Dictionary map; + if (isNullable) + map = NullableActions.Map; + else if (declaringType == CommonRuntimeTypes.String) + map = StringActions.Map; + else + return null; + + if (!(map.TryGetValue(methodBase.MetadataDefinitionMethod, out CustomMethodInvokerAction action))) + return null; + + ParameterInfo[] parameterInfos = methodBase.GetParametersNoCopy(); + Type[] parameterTypes = new Type[parameterInfos.Length]; + for (int i = 0; i < parameterInfos.Length; i++) + { + parameterTypes[i] = parameterInfos[i].ParameterType; + } + + InvokerOptions options = (methodBase.IsStatic || methodBase is ConstructorInfo || isNullable) ? InvokerOptions.AllowNullThis : InvokerOptions.None; + return new CustomMethodInvoker(declaringType, parameterTypes, options, action); + } + + private static void AddConstructor(this Dictionary map, Type declaringType, Type[] parameterTypes, CustomMethodInvokerAction action) + { + map.AddMethod(declaringType, ConstructorInfo.ConstructorName, parameterTypes, action); + } + + private static void AddMethod(this Dictionary map, Type declaringType, string name, Type[] parameterTypes, CustomMethodInvokerAction action) + { + const BindingFlags bf = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly | BindingFlags.ExactBinding; + + MethodBase methodBase; + if (name == ConstructorInfo.ConstructorName) + { + methodBase = declaringType.GetConstructor(bf, null, parameterTypes, null); + } + else + { + methodBase = declaringType.GetMethod(name, 0, bf, null, parameterTypes, null); + } + + if (methodBase == null) + return; // If we got here, this specific member was not included in the metadata. + + Debug.Assert(methodBase == methodBase.MetadataDefinitionMethod); + map.Add(methodBase, action); + } + } +} + diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/EcmaFormat/EcmaFormatMethodCommon.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/EcmaFormat/EcmaFormatMethodCommon.cs index 8249cc3c78..44d5ed1f6c 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/EcmaFormat/EcmaFormatMethodCommon.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/EcmaFormat/EcmaFormatMethodCommon.cs @@ -31,13 +31,7 @@ namespace System.Reflection.Runtime.MethodInfos.EcmaFormat // internal struct EcmaFormatMethodCommon : IRuntimeMethodCommon, IEquatable { - public bool IsGenericMethodDefinition - { - get - { - return _method.GetGenericParameters().Count > 0; - } - } + public bool IsGenericMethodDefinition => GenericParameterCount != 0; public MethodInvoker GetUncachedMethodInvoker(RuntimeTypeInfo[] methodArguments, MemberInfo exceptionPertainant) { @@ -77,6 +71,8 @@ namespace System.Reflection.Runtime.MethodInfos.EcmaFormat } } + public int GenericParameterCount => _method.GetGenericParameters().Count; + public RuntimeTypeInfo[] GetGenericTypeParametersWithSpecifiedOwningMethod(RuntimeNamedMethodInfo owningMethod) { GenericParameterHandleCollection genericParameters = _method.GetGenericParameters(); @@ -161,19 +157,6 @@ namespace System.Reflection.Runtime.MethodInfos.EcmaFormat } } - public IEnumerable CustomAttributes - { - get - { - IEnumerable customAttributes = RuntimeCustomAttributeData.GetCustomAttributes(_reader, _method.GetCustomAttributes()); - foreach (CustomAttributeData cad in customAttributes) - yield return cad; - - if (0 != (_method.ImplAttributes & MethodImplAttributes.PreserveSig)) - yield return ReflectionCoreExecution.ExecutionDomain.GetCustomAttributeData(typeof(PreserveSigAttribute), null, null); - } - } - public RuntimeTypeInfo DeclaringType { get @@ -321,11 +304,13 @@ namespace System.Reflection.Runtime.MethodInfos.EcmaFormat return true; } + public IEnumerable TrueCustomAttributes => RuntimeCustomAttributeData.GetCustomAttributes(_reader, _method.GetCustomAttributes()); + public override bool Equals(Object obj) { - if (!(obj is EcmaFormatMethodCommon)) + if (!(obj is EcmaFormatMethodCommon other)) return false; - return Equals((EcmaFormatMethodCommon)obj); + return Equals(other); } public bool Equals(EcmaFormatMethodCommon other) diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/IRuntimeMethodCommon.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/IRuntimeMethodCommon.cs index cba2855425..68c458102c 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/IRuntimeMethodCommon.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/IRuntimeMethodCommon.cs @@ -26,7 +26,6 @@ namespace System.Reflection.Runtime.MethodInfos CallingConventions CallingConvention { get; } RuntimeTypeInfo ContextTypeInfo { get; } - IEnumerable CustomAttributes { get; } RuntimeTypeInfo DeclaringType { get; } RuntimeNamedTypeInfo DefiningTypeInfo { get; } MethodImplAttributes MethodImplementationFlags { get; } @@ -36,6 +35,7 @@ namespace System.Reflection.Runtime.MethodInfos /// Return an array of the types of the return value and parameter types. /// QSignatureTypeHandle[] QualifiedMethodSignature { get; } + IEnumerable TrueCustomAttributes { get; } /// /// Parse the metadata that describes parameters, and for each parameter for which there is specific metadata @@ -49,6 +49,7 @@ namespace System.Reflection.Runtime.MethodInfos MethodInvoker GetUncachedMethodInvoker(RuntimeTypeInfo[] methodArguments, MemberInfo exceptionPertainant); bool IsGenericMethodDefinition { get; } + int GenericParameterCount { get; } bool HasSameMetadataDefinitionAs(TRuntimeMethodCommon other); diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/InvokerOptions.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/InvokerOptions.cs new file mode 100644 index 0000000000..c5eb41ac63 --- /dev/null +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/InvokerOptions.cs @@ -0,0 +1,15 @@ +// 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. + +namespace System.Reflection.Runtime.MethodInfos +{ + [Flags] + internal enum InvokerOptions + { + None = 0x00000000, + AllowNullThis = 0x00000001, // Don't raise an exception if the "thisObject" parameter to Invoker is null. + DontWrapException = 0x00000002, // Don't wrap target exceptions in TargetInvocationException. + } +} + diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/NativeFormat/NativeFormatMethodCommon.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/NativeFormat/NativeFormatMethodCommon.cs index e44cf627bd..fb3bfbd488 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/NativeFormat/NativeFormatMethodCommon.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/NativeFormat/NativeFormatMethodCommon.cs @@ -2,9 +2,6 @@ // 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.Text; -using System.Reflection; using System.Diagnostics; using System.Collections.Generic; using System.Runtime; @@ -15,7 +12,6 @@ using System.Reflection.Runtime.ParameterInfos; using System.Reflection.Runtime.ParameterInfos.NativeFormat; using System.Reflection.Runtime.CustomAttributes; -using Internal.Reflection.Core; using Internal.Reflection.Core.Execution; using Internal.Runtime.CompilerServices; using Internal.Runtime.TypeLoader; @@ -28,14 +24,7 @@ namespace System.Reflection.Runtime.MethodInfos.NativeFormat // internal struct NativeFormatMethodCommon : IRuntimeMethodCommon, IEquatable { - public bool IsGenericMethodDefinition - { - get - { - Method method = MethodHandle.GetMethod(Reader); - return method.GenericParameters.GetEnumerator().MoveNext(); - } - } + public bool IsGenericMethodDefinition => GenericParameterCount != 0; public MethodInvoker GetUncachedMethodInvoker(RuntimeTypeInfo[] methodArguments, MemberInfo exceptionPertainant) { @@ -85,6 +74,8 @@ namespace System.Reflection.Runtime.MethodInfos.NativeFormat } } + public int GenericParameterCount => MethodHandle.GetMethod(Reader).GenericParameters.Count; + public RuntimeTypeInfo[] GetGenericTypeParametersWithSpecifiedOwningMethod(RuntimeNamedMethodInfo owningMethod) { Method method = MethodHandle.GetMethod(Reader); @@ -154,18 +145,6 @@ namespace System.Reflection.Runtime.MethodInfos.NativeFormat } } - public IEnumerable CustomAttributes - { - get - { - IEnumerable customAttributes = RuntimeCustomAttributeData.GetCustomAttributes(_reader, _method.CustomAttributes); - foreach (CustomAttributeData cad in customAttributes) - yield return cad; - foreach (CustomAttributeData cad in ReflectionCoreExecution.ExecutionEnvironment.GetPseudoCustomAttributes(_reader, _methodHandle, _definingTypeInfo.TypeDefinitionHandle)) - yield return cad; - } - } - public RuntimeTypeInfo DeclaringType { get @@ -322,11 +301,13 @@ namespace System.Reflection.Runtime.MethodInfos.NativeFormat return true; } + public IEnumerable TrueCustomAttributes => RuntimeCustomAttributeData.GetCustomAttributes(_reader, _method.CustomAttributes); + public override bool Equals(Object obj) { - if (!(obj is NativeFormatMethodCommon)) + if (!(obj is NativeFormatMethodCommon other)) return false; - return Equals((NativeFormatMethodCommon)obj); + return Equals(other); } public bool Equals(NativeFormatMethodCommon other) diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/OpenMethodInvoker.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/OpenMethodInvoker.cs index 4a5c891d3a..f29b790f56 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/OpenMethodInvoker.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/OpenMethodInvoker.cs @@ -15,7 +15,7 @@ namespace System.Reflection.Runtime.MethodInfos { internal sealed class OpenMethodInvoker : MethodInvoker { - public sealed override Object Invoke(Object thisObject, Object[] arguments, BinderBundle binderBundle) + protected sealed override Object Invoke(Object thisObject, Object[] arguments, BinderBundle binderBundle, bool wrapInTargetInvocationException) { throw new InvalidOperationException(SR.Arg_UnboundGenParam); } diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeClsIdNullaryConstructorInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeClsIdNullaryConstructorInfo.cs index e9f59b1feb..709d8926d7 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeClsIdNullaryConstructorInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeClsIdNullaryConstructorInfo.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Reflection.Runtime.General; using System.Reflection.Runtime.TypeInfos; using System.Reflection.Runtime.ParameterInfos; +using System.Runtime.InteropServices; using Internal.Reflection.Core.Execution; @@ -41,8 +42,7 @@ namespace System.Reflection.Runtime.MethodInfos public sealed override bool Equals(object obj) { - RuntimeCLSIDNullaryConstructorInfo other = obj as RuntimeCLSIDNullaryConstructorInfo; - if (other == null) + if (!(obj is RuntimeCLSIDNullaryConstructorInfo other)) return false; if (!(_declaringType.Equals(other._declaringType))) return false; @@ -58,7 +58,22 @@ namespace System.Reflection.Runtime.MethodInfos Guid clsid = _declaringType.GUID; string server = _declaringType.Server; - throw new NotImplementedException(); // TODO: https://github.com/dotnet/corert/issues/1764 - Make the call out to Interop to create an RCW from the supplied CLSID and server. + IntPtr pItf = IntPtr.Zero; + try + { + pItf = McgMarshal.CoCreateInstanceEx(clsid, server); + + // CoCreateInstanceEx will throw exception if it fails to + // create an instance. + Debug.Assert(pItf != IntPtr.Zero); + + return Marshal.GetObjectForIUnknown(pItf); + } + finally + { + if (pItf != IntPtr.Zero) + Marshal.Release(pItf); + } } public sealed override MethodBase MetadataDefinitionMethod { get { throw new NotSupportedException(); } } diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructedGenericMethodInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructedGenericMethodInfo.cs index cbab726c4a..c8e18b2987 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructedGenericMethodInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructedGenericMethodInfo.cs @@ -56,8 +56,7 @@ namespace System.Reflection.Runtime.MethodInfos public sealed override bool Equals(Object obj) { - RuntimeConstructedGenericMethodInfo other = obj as RuntimeConstructedGenericMethodInfo; - if (other == null) + if (!(obj is RuntimeConstructedGenericMethodInfo other)) return false; if (!_genericMethodDefinition.Equals(other._genericMethodDefinition)) return false; @@ -76,6 +75,8 @@ namespace System.Reflection.Runtime.MethodInfos return _genericMethodDefinition.GetHashCode(); } + public sealed override int GenericParameterCount => _genericMethodDefinition.GenericParameterCount; + public sealed override MethodInfo GetGenericMethodDefinition() { return _genericMethodDefinition; diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructorInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructorInfo.cs index a8d7d99fdd..c3a85bb08f 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructorInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructorInfo.cs @@ -7,7 +7,6 @@ using System.Reflection; using System.Diagnostics; using System.Globalization; using System.Collections.Generic; -using System.Runtime.Serialization; using System.Reflection.Runtime.General; using System.Reflection.Runtime.TypeInfos; using System.Reflection.Runtime.ParameterInfos; @@ -21,8 +20,7 @@ namespace System.Reflection.Runtime.MethodInfos // // The runtime's implementation of ConstructorInfo. // - [Serializable] - internal abstract partial class RuntimeConstructorInfo : ConstructorInfo, ISerializable + internal abstract partial class RuntimeConstructorInfo : ConstructorInfo { public abstract override MethodAttributes Attributes { get; } @@ -51,11 +49,6 @@ namespace System.Reflection.Runtime.MethodInfos throw new PlatformNotSupportedException(); } - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - throw new PlatformNotSupportedException(); - } - public sealed override ParameterInfo[] GetParameters() { #if ENABLE_REFLECTION_TRACE diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeDummyMethodInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeDummyMethodInfo.cs index 1423d21f7e..f841945f63 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeDummyMethodInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeDummyMethodInfo.cs @@ -29,6 +29,7 @@ namespace System.Reflection.Runtime.MethodInfos public sealed override bool IsConstructedGenericMethod { get { throw NotImplemented.ByDesign; } } public sealed override bool IsGenericMethod { get { throw NotImplemented.ByDesign; } } public sealed override bool IsGenericMethodDefinition { get { throw NotImplemented.ByDesign; } } + public sealed override int GenericParameterCount { get { throw NotImplemented.ByDesign; } } public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) { throw NotImplemented.ByDesign; } public sealed override MethodImplAttributes MethodImplementationFlags { get { throw NotImplemented.ByDesign; } } public sealed override Module Module { get { throw NotImplemented.ByDesign; } } diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeMethodInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeMethodInfo.cs index cdb8e54d27..33d73b16af 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeMethodInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeMethodInfo.cs @@ -7,7 +7,6 @@ using System.Reflection; using System.Diagnostics; using System.Globalization; using System.Collections.Generic; -using System.Runtime.Serialization; using System.Runtime.CompilerServices; using System.Reflection.Runtime.General; using System.Reflection.Runtime.TypeInfos; @@ -23,7 +22,7 @@ namespace System.Reflection.Runtime.MethodInfos // Abstract base class for RuntimeNamedMethodInfo, RuntimeConstructedGenericMethodInfo. // [DebuggerDisplay("{_debugName}")] - internal abstract partial class RuntimeMethodInfo : MethodInfo, ISerializable, ITraceableTypeMember + internal abstract partial class RuntimeMethodInfo : MethodInfo, ITraceableTypeMember { protected RuntimeMethodInfo() { @@ -87,8 +86,7 @@ namespace System.Reflection.Runtime.MethodInfos if (delegateType == null) throw new ArgumentNullException(nameof(delegateType)); - RuntimeTypeInfo runtimeDelegateType = delegateType as RuntimeTypeInfo; - if (runtimeDelegateType == null) + if (!(delegateType is RuntimeTypeInfo runtimeDelegateType)) throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(delegateType)); if (!runtimeDelegateType.IsDelegate) @@ -150,12 +148,9 @@ namespace System.Reflection.Runtime.MethodInfos return RuntimeGenericArgumentsOrParameters.CloneTypeArray(); } - public abstract override MethodInfo GetGenericMethodDefinition(); + public abstract override int GenericParameterCount { get; } - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - throw new PlatformNotSupportedException(); - } + public abstract override MethodInfo GetGenericMethodDefinition(); public sealed override MethodBody GetMethodBody() { @@ -382,8 +377,16 @@ namespace System.Reflection.Runtime.MethodInfos MethodInfo invokeMethod = runtimeDelegateType.GetInvokeMethod(); // Make sure the return type is assignment-compatible. - if (!IsAssignableFrom(executionEnvironment, invokeMethod.ReturnParameter.ParameterType, this.ReturnParameter.ParameterType)) + Type expectedReturnType = ReturnParameter.ParameterType; + Type actualReturnType = invokeMethod.ReturnParameter.ParameterType; + if (!IsAssignableFrom(executionEnvironment, actualReturnType, expectedReturnType)) return null; + if (expectedReturnType.IsValueType && !actualReturnType.IsValueType) + { + // For value type returning methods, conversions between enums and primitives are allowed (and screened by the above call to IsAssignableFrom) + // but conversions to Object or interfaces implemented by the value type are not. + return null; + } IList delegateParameters = invokeMethod.GetParametersNoCopy(); IList targetParameters = this.GetParametersNoCopy(); diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs index 03beae11c2..fe1e82f59d 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs @@ -6,9 +6,11 @@ using System; using System.Reflection; using System.Diagnostics; using System.Collections.Generic; +using System.Runtime.InteropServices; using System.Reflection.Runtime.General; using System.Reflection.Runtime.TypeInfos; using System.Reflection.Runtime.ParameterInfos; +using System.Reflection.Runtime.CustomAttributes; using Internal.Reflection.Core.Execution; @@ -80,7 +82,14 @@ namespace System.Reflection.Runtime.MethodInfos ReflectionTrace.MethodBase_CustomAttributes(this); #endif - return _common.CustomAttributes; + foreach (CustomAttributeData cad in _common.TrueCustomAttributes) + { + yield return cad; + } + + MethodImplAttributes implAttributes = _common.MethodImplementationFlags; + if (0 != (implAttributes & MethodImplAttributes.PreserveSig)) + yield return new RuntimePseudoCustomAttributeData(typeof(PreserveSigAttribute), null, null); } } @@ -115,6 +124,8 @@ namespace System.Reflection.Runtime.MethodInfos } } + public sealed override int GenericParameterCount => _common.GenericParameterCount; + public sealed override MethodInfo MakeGenericMethod(params Type[] typeArguments) { #if ENABLE_REFLECTION_TRACE @@ -129,13 +140,17 @@ namespace System.Reflection.Runtime.MethodInfos RuntimeTypeInfo[] genericTypeArguments = new RuntimeTypeInfo[typeArguments.Length]; for (int i = 0; i < typeArguments.Length; i++) { - if (typeArguments[i] == null) + Type typeArgument = typeArguments[i]; + if (typeArgument == null) throw new ArgumentNullException(); - if (!typeArguments[i].IsRuntimeImplemented()) + if (!typeArgument.IsRuntimeImplemented()) throw new ArgumentException(SR.Format(SR.Reflection_CustomReflectionObjectsNotSupported, typeArguments[i]), "typeArguments[" + i + "]"); // Not a runtime type. - genericTypeArguments[i] = typeArguments[i].CastToRuntimeTypeInfo(); + if (typeArgument.IsByRefLike) + throw new BadImageFormatException(SR.CannotUseByRefLikeTypeInInstantiation); + + genericTypeArguments[i] = typeArgument.CastToRuntimeTypeInfo(); } if (typeArguments.Length != GenericTypeParameters.Length) throw new ArgumentException(SR.Format(SR.Argument_NotEnoughGenArguments, typeArguments.Length, GenericTypeParameters.Length)); @@ -148,7 +163,7 @@ namespace System.Reflection.Runtime.MethodInfos { get { - return RuntimeNamedMethodInfo.GetRuntimeNamedMethodInfo(_common.RuntimeMethodCommonOfUninstantiatedMethod, _common.ContextTypeInfo); + return RuntimeNamedMethodInfo.GetRuntimeNamedMethodInfo(_common.RuntimeMethodCommonOfUninstantiatedMethod, _common.DefiningTypeInfo); } } @@ -195,12 +210,10 @@ namespace System.Reflection.Runtime.MethodInfos throw new ArgumentNullException(nameof(other)); // Do not rewrite as a call to IsConstructedGenericMethod - we haven't yet established that "other" is a runtime-implemented member yet! - RuntimeConstructedGenericMethodInfo otherConstructedGenericMethod = other as RuntimeConstructedGenericMethodInfo; - if (otherConstructedGenericMethod != null) + if (other is RuntimeConstructedGenericMethodInfo otherConstructedGenericMethod) other = otherConstructedGenericMethod.GetGenericMethodDefinition(); - RuntimeNamedMethodInfo otherMethod = other as RuntimeNamedMethodInfo; - if (otherMethod == null) + if (!(other is RuntimeNamedMethodInfo otherMethod)) return false; return _common.HasSameMetadataDefinitionAs(otherMethod._common); @@ -208,8 +221,7 @@ namespace System.Reflection.Runtime.MethodInfos public sealed override bool Equals(Object obj) { - RuntimeNamedMethodInfo other = obj as RuntimeNamedMethodInfo; - if (other == null) + if (!(obj is RuntimeNamedMethodInfo other)) return false; if (!_common.Equals(other._common)) return false; @@ -301,6 +313,10 @@ namespace System.Reflection.Runtime.MethodInfos { get { + MethodInvoker invoker = this.GetCustomMethodInvokerIfNeeded(); + if (invoker != null) + return invoker; + return GetUncachedMethodInvoker(Array.Empty(), this); } } diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimePlainConstructorInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimePlainConstructorInfo.cs index 0dbb40e002..860318a5c7 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimePlainConstructorInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimePlainConstructorInfo.cs @@ -71,7 +71,7 @@ namespace System.Reflection.Runtime.MethodInfos ReflectionTrace.MethodBase_CustomAttributes(this); #endif - return _common.CustomAttributes; + return _common.TrueCustomAttributes; } } @@ -150,16 +150,15 @@ namespace System.Reflection.Runtime.MethodInfos if (other == null) throw new ArgumentNullException(nameof(other)); - RuntimePlainConstructorInfo otherConstructor = other as RuntimePlainConstructorInfo; - if (otherConstructor == null) + if (!(other is RuntimePlainConstructorInfo otherConstructor)) return false; + return _common.HasSameMetadataDefinitionAs(otherConstructor._common); } public sealed override bool Equals(Object obj) { - RuntimePlainConstructorInfo other = obj as RuntimePlainConstructorInfo; - if (other == null) + if (!(obj is RuntimePlainConstructorInfo other)) return false; return _common.Equals(other._common); } @@ -195,6 +194,10 @@ namespace System.Reflection.Runtime.MethodInfos if (this.IsStatic) throw new MemberAccessException(SR.Acc_NotClassInit); + MethodInvoker invoker = this.GetCustomMethodInvokerIfNeeded(); + if (invoker != null) + return invoker; + return _common.GetUncachedMethodInvoker(Array.Empty(), this); } } diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticConstructorInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticConstructorInfo.cs index a0aa8a0aad..6431628f3e 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticConstructorInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticConstructorInfo.cs @@ -20,12 +20,12 @@ namespace System.Reflection.Runtime.MethodInfos // internal sealed partial class RuntimeSyntheticConstructorInfo : RuntimeConstructorInfo, IRuntimeMemberInfoWithNoMetadataDefinition { - private RuntimeSyntheticConstructorInfo(SyntheticMethodId syntheticMethodId, RuntimeArrayTypeInfo declaringType, RuntimeTypeInfo[] runtimeParameterTypes, InvokerOptions options, Func invoker) + private RuntimeSyntheticConstructorInfo(SyntheticMethodId syntheticMethodId, RuntimeArrayTypeInfo declaringType, RuntimeTypeInfo[] runtimeParameterTypes, InvokerOptions options, CustomMethodInvokerAction action) { _syntheticMethodId = syntheticMethodId; _declaringType = declaringType; _options = options; - _invoker = invoker; + _action = action; _runtimeParameterTypes = runtimeParameterTypes; } @@ -115,8 +115,7 @@ namespace System.Reflection.Runtime.MethodInfos public sealed override bool Equals(object obj) { - RuntimeSyntheticConstructorInfo other = obj as RuntimeSyntheticConstructorInfo; - if (other == null) + if (!(obj is RuntimeSyntheticConstructorInfo other)) return false; if (_syntheticMethodId != other._syntheticMethodId) return false; @@ -164,21 +163,7 @@ namespace System.Reflection.Runtime.MethodInfos } } - protected sealed override MethodInvoker UncachedMethodInvoker - { - get - { - RuntimeTypeInfo[] runtimeParameterTypes = _runtimeParameterTypes; - RuntimeTypeHandle[] runtimeParameterTypeHandles = new RuntimeTypeHandle[runtimeParameterTypes.Length]; - for (int i = 0; i < runtimeParameterTypes.Length; i++) - runtimeParameterTypeHandles[i] = runtimeParameterTypes[i].TypeHandle; - return ReflectionCoreExecution.ExecutionEnvironment.GetSyntheticMethodInvoker( - _declaringType.TypeHandle, - runtimeParameterTypeHandles, - _options, - _invoker); - } - } + protected sealed override MethodInvoker UncachedMethodInvoker => new CustomMethodInvoker(_declaringType, _runtimeParameterTypes, _options, _action); private volatile RuntimeParameterInfo[] _lazyParameters; @@ -186,6 +171,6 @@ namespace System.Reflection.Runtime.MethodInfos private readonly RuntimeArrayTypeInfo _declaringType; private readonly RuntimeTypeInfo[] _runtimeParameterTypes; private readonly InvokerOptions _options; - private readonly Func _invoker; + private readonly CustomMethodInvokerAction _action; } } diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticMethodInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticMethodInfo.cs index b4c30e7406..bbefb05b66 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticMethodInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticMethodInfo.cs @@ -19,13 +19,13 @@ namespace System.Reflection.Runtime.MethodInfos // internal sealed partial class RuntimeSyntheticMethodInfo : RuntimeMethodInfo, IRuntimeMemberInfoWithNoMetadataDefinition { - private RuntimeSyntheticMethodInfo(SyntheticMethodId syntheticMethodId, String name, RuntimeArrayTypeInfo declaringType, RuntimeTypeInfo[] parameterTypes, RuntimeTypeInfo returnType, InvokerOptions options, Func invoker) + private RuntimeSyntheticMethodInfo(SyntheticMethodId syntheticMethodId, String name, RuntimeArrayTypeInfo declaringType, RuntimeTypeInfo[] parameterTypes, RuntimeTypeInfo returnType, InvokerOptions options, CustomMethodInvokerAction action) { _syntheticMethodId = syntheticMethodId; _name = name; _declaringType = declaringType; _options = options; - _invoker = invoker; + _action = action; _runtimeParameterTypes = parameterTypes; _returnType = returnType; } @@ -65,8 +65,7 @@ namespace System.Reflection.Runtime.MethodInfos public sealed override bool Equals(Object obj) { - RuntimeSyntheticMethodInfo other = obj as RuntimeSyntheticMethodInfo; - if (other == null) + if (!(obj is RuntimeSyntheticMethodInfo other)) return false; if (_syntheticMethodId != other._syntheticMethodId) return false; @@ -109,6 +108,8 @@ namespace System.Reflection.Runtime.MethodInfos } } + public sealed override int GenericParameterCount => 0; + public sealed override MethodInfo MakeGenericMethod(params Type[] typeArguments) { throw new InvalidOperationException(SR.Format(SR.Arg_NotGenericMethodDefinition, this)); @@ -169,21 +170,7 @@ namespace System.Reflection.Runtime.MethodInfos } } - protected sealed override MethodInvoker UncachedMethodInvoker - { - get - { - RuntimeTypeInfo[] runtimeParameterTypes = _runtimeParameterTypes; - RuntimeTypeHandle[] runtimeParameterTypeHandles = new RuntimeTypeHandle[runtimeParameterTypes.Length]; - for (int i = 0; i < runtimeParameterTypes.Length; i++) - runtimeParameterTypeHandles[i] = runtimeParameterTypes[i].TypeHandle; - return ReflectionCoreExecution.ExecutionEnvironment.GetSyntheticMethodInvoker( - _declaringType.TypeHandle, - runtimeParameterTypeHandles, - _options, - _invoker); - } - } + protected sealed override MethodInvoker UncachedMethodInvoker => new CustomMethodInvoker(_declaringType, _runtimeParameterTypes, _options, _action); internal sealed override RuntimeTypeInfo[] RuntimeGenericArgumentsOrParameters { @@ -236,6 +223,6 @@ namespace System.Reflection.Runtime.MethodInfos private readonly RuntimeTypeInfo[] _runtimeParameterTypes; private readonly RuntimeTypeInfo _returnType; private readonly InvokerOptions _options; - private readonly Func _invoker; + private readonly CustomMethodInvokerAction _action; } } diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Modules/NativeFormat/NativeFormatRuntimeModule.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Modules/NativeFormat/NativeFormatRuntimeModule.cs index b646ce7bc7..e006776df3 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Modules/NativeFormat/NativeFormatRuntimeModule.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Modules/NativeFormat/NativeFormatRuntimeModule.cs @@ -58,7 +58,7 @@ namespace System.Reflection.Runtime.Modules.NativeFormat { get { - byte[] mvid = _assembly.Scope.ScopeDefinition.Mvid.ReadOnlyCollectionToArray(); + byte[] mvid = _assembly.Scope.ScopeDefinition.Mvid.ToArray(); if (mvid.Length == 0) return default(Guid); // Workaround for TFS 441076 - Module data not emitted for facade assemblies. return new Guid(mvid); diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Modules/RuntimeModule.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Modules/RuntimeModule.cs index 4fecb6e2b7..eff469c1b0 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Modules/RuntimeModule.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Modules/RuntimeModule.cs @@ -15,7 +15,6 @@ namespace System.Reflection.Runtime.Modules // Modules are quite meaningless in ProjectN but we have to keep up the appearances since they still exist in Win8P's surface area. // As far as ProjectN is concerned, each Assembly has one module. // - [Serializable] internal abstract partial class RuntimeModule : Module { protected RuntimeModule() @@ -36,10 +35,9 @@ namespace System.Reflection.Runtime.Modules public abstract override string Name { get; } - public sealed override bool Equals(object o) + public sealed override bool Equals(object obj) { - RuntimeModule other = o as RuntimeModule; - if (other == null) + if (!(obj is RuntimeModule other)) return false; return Assembly.Equals(other.Assembly); } diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/EcmaFormat/EcmaFormatMethodParameterInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/EcmaFormat/EcmaFormatMethodParameterInfo.cs index 20fa854231..71a59d3642 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/EcmaFormat/EcmaFormatMethodParameterInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/EcmaFormat/EcmaFormatMethodParameterInfo.cs @@ -7,6 +7,7 @@ using System.Reflection; using System.Diagnostics; using System.Collections.Generic; using System.Reflection.Runtime.General; +using System.Reflection.Runtime.General.EcmaFormat; using System.Reflection.Runtime.CustomAttributes; using System.Runtime.InteropServices; @@ -21,7 +22,7 @@ namespace System.Reflection.Runtime.ParameterInfos.EcmaFormat // // This implements ParameterInfo objects owned by MethodBase objects that have an associated Parameter metadata entity. // - internal sealed partial class EcmaFormatMethodParameterInfo : RuntimeMethodParameterInfo + internal sealed partial class EcmaFormatMethodParameterInfo : RuntimeFatMethodParameterInfo { private EcmaFormatMethodParameterInfo(MethodBase member, MethodDefinitionHandle methodHandle, int position, ParameterHandle parameterHandle, QSignatureTypeHandle qualifiedParameterTypeHandle, TypeContext typeContext) : base(member, position, qualifiedParameterTypeHandle, typeContext) @@ -48,40 +49,6 @@ namespace System.Reflection.Runtime.ParameterInfos.EcmaFormat } } - public sealed override IEnumerable CustomAttributes - { - get - { - IEnumerable customAttributes = RuntimeCustomAttributeData.GetCustomAttributes(this.Reader, _parameter.GetCustomAttributes()); - foreach (CustomAttributeData cad in customAttributes) - yield return cad; - - ParameterAttributes attributes = Attributes; - if (0 != (attributes & ParameterAttributes.In)) - yield return ReflectionCoreExecution.ExecutionDomain.GetCustomAttributeData(typeof(InAttribute), null, null); - if (0 != (attributes & ParameterAttributes.Out)) - yield return ReflectionCoreExecution.ExecutionDomain.GetCustomAttributeData(typeof(OutAttribute), null, null); - if (0 != (attributes & ParameterAttributes.Optional)) - yield return ReflectionCoreExecution.ExecutionDomain.GetCustomAttributeData(typeof(OptionalAttribute), null, null); - } - } - - public sealed override Object DefaultValue - { - get - { - return DefaultValueInfo.Item2; - } - } - - public sealed override bool HasDefaultValue - { - get - { - return DefaultValueInfo.Item1; - } - } - public sealed override String Name { get @@ -98,29 +65,15 @@ namespace System.Reflection.Runtime.ParameterInfos.EcmaFormat } } - private Tuple DefaultValueInfo - { - get - { - Tuple defaultValueInfo = _lazyDefaultValueInfo; - if (defaultValueInfo == null) - { - Object defaultValue; - bool hasDefaultValue = DefaultValueProcessing.GetDefaultValueIfAny(Reader, ref _parameter, this, out defaultValue); + protected sealed override IEnumerable TrueCustomAttributes => RuntimeCustomAttributeData.GetCustomAttributes(this.Reader, _parameter.GetCustomAttributes()); - if (!hasDefaultValue) - { - defaultValue = IsOptional ? (object)Missing.Value : (object)DBNull.Value; - } - defaultValueInfo = _lazyDefaultValueInfo = Tuple.Create(hasDefaultValue, defaultValue); - } - return defaultValueInfo; - } + protected sealed override bool GetDefaultValueIfAvailable(bool raw, out object defaultValue) + { + return DefaultValueProcessing.GetDefaultValueIfAny(Reader, ref _parameter, this, raw, out defaultValue); } private readonly MethodDefinitionHandle _methodHandle; private readonly ParameterHandle _parameterHandle; private Parameter _parameter; - private volatile Tuple _lazyDefaultValueInfo; } } diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/NativeFormat/NativeFormatMethodParameterInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/NativeFormat/NativeFormatMethodParameterInfo.cs index d1ca706dd1..8ed4f11b14 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/NativeFormat/NativeFormatMethodParameterInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/NativeFormat/NativeFormatMethodParameterInfo.cs @@ -7,6 +7,7 @@ using System.Reflection; using System.Diagnostics; using System.Collections.Generic; using System.Reflection.Runtime.General; +using System.Reflection.Runtime.General.NativeFormat; using System.Reflection.Runtime.CustomAttributes; using Internal.Reflection.Core; @@ -19,7 +20,7 @@ namespace System.Reflection.Runtime.ParameterInfos.NativeFormat // // This implements ParameterInfo objects owned by MethodBase objects that have an associated Parameter metadata entity. // - internal sealed partial class NativeFormatMethodParameterInfo : RuntimeMethodParameterInfo + internal sealed partial class NativeFormatMethodParameterInfo : RuntimeFatMethodParameterInfo { private NativeFormatMethodParameterInfo(MethodBase member, MethodHandle methodHandle, int position, ParameterHandle parameterHandle, QSignatureTypeHandle qualifiedParameterTypeHandle, TypeContext typeContext) : base(member, position, qualifiedParameterTypeHandle, typeContext) @@ -46,35 +47,6 @@ namespace System.Reflection.Runtime.ParameterInfos.NativeFormat } } - public sealed override IEnumerable CustomAttributes - { - get - { - IEnumerable customAttributes = RuntimeCustomAttributeData.GetCustomAttributes(this.Reader, _parameter.CustomAttributes); - foreach (CustomAttributeData cad in customAttributes) - yield return cad; - MethodHandle declaringMethodHandle = _methodHandle; - foreach (CustomAttributeData cad in ReflectionCoreExecution.ExecutionEnvironment.GetPseudoCustomAttributes(this.Reader, _parameterHandle, declaringMethodHandle)) - yield return cad; - } - } - - public sealed override Object DefaultValue - { - get - { - return DefaultValueInfo.Item2; - } - } - - public sealed override bool HasDefaultValue - { - get - { - return DefaultValueInfo.Item1; - } - } - public sealed override String Name { get @@ -90,34 +62,16 @@ namespace System.Reflection.Runtime.ParameterInfos.NativeFormat throw new InvalidOperationException(SR.NoMetadataTokenAvailable); } } - - private Tuple DefaultValueInfo + + protected sealed override IEnumerable TrueCustomAttributes => RuntimeCustomAttributeData.GetCustomAttributes(this.Reader, _parameter.CustomAttributes); + + protected sealed override bool GetDefaultValueIfAvailable(bool raw, out object defaultValue) { - get - { - Tuple defaultValueInfo = _lazyDefaultValueInfo; - if (defaultValueInfo == null) - { - Object defaultValue; - bool hasDefaultValue = ReflectionCoreExecution.ExecutionEnvironment.GetDefaultValueIfAny( - this.Reader, - _parameterHandle, - this.ParameterType, - this.CustomAttributes, - out defaultValue); - if (!hasDefaultValue) - { - defaultValue = IsOptional ? (object)Missing.Value : (object)DBNull.Value; - } - defaultValueInfo = _lazyDefaultValueInfo = Tuple.Create(hasDefaultValue, defaultValue); - } - return defaultValueInfo; - } + return DefaultValueParser.GetDefaultValueIfAny(Reader, _parameter.DefaultValue, ParameterType, CustomAttributes, raw, out defaultValue); } private readonly MethodHandle _methodHandle; private readonly ParameterHandle _parameterHandle; private readonly Parameter _parameter; - private volatile Tuple _lazyDefaultValueInfo; } } diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeFatMethodParameterInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeFatMethodParameterInfo.cs new file mode 100644 index 0000000000..57ce50467d --- /dev/null +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeFatMethodParameterInfo.cs @@ -0,0 +1,92 @@ +// 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.Diagnostics; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Reflection.Runtime.General; +using System.Reflection.Runtime.CustomAttributes; + +namespace System.Reflection.Runtime.ParameterInfos +{ + // + // This implements ParameterInfo objects owned by MethodBase objects that have associated Parameter metadata. (In practice, + // this means all non-return parameters since most such parameters have at least a name.) + // + internal abstract class RuntimeFatMethodParameterInfo : RuntimeMethodParameterInfo + { + protected RuntimeFatMethodParameterInfo(MethodBase member, int position, QSignatureTypeHandle qualifiedParameterTypeHandle, TypeContext typeContext) + : base(member, position, qualifiedParameterTypeHandle, typeContext) + { + } + + public sealed override IEnumerable CustomAttributes + { + get + { + foreach (CustomAttributeData cad in TrueCustomAttributes) + yield return cad; + + ParameterAttributes attributes = Attributes; + if (0 != (attributes & ParameterAttributes.In)) + yield return new RuntimePseudoCustomAttributeData(typeof(InAttribute), null, null); + if (0 != (attributes & ParameterAttributes.Out)) + yield return new RuntimePseudoCustomAttributeData(typeof(OutAttribute), null, null); + if (0 != (attributes & ParameterAttributes.Optional)) + yield return new RuntimePseudoCustomAttributeData(typeof(OptionalAttribute), null, null); + } + } + + protected abstract IEnumerable TrueCustomAttributes { get; } + + public sealed override bool HasDefaultValue => DefaultValueInfo.Item1; + public sealed override object DefaultValue => DefaultValueInfo.Item2; + + public sealed override object RawDefaultValue + { + get + { + Tuple rawDefaultValueInfo = _lazyRawDefaultValueInfo; + if (rawDefaultValueInfo == null) + { + object rawDefaultValue; + bool dontCare = GetDefaultValueOrSentinel(raw: true, defaultValue: out rawDefaultValue); + rawDefaultValueInfo = _lazyRawDefaultValueInfo = Tuple.Create(rawDefaultValue); + } + return rawDefaultValueInfo.Item1; + } + } + + protected abstract bool GetDefaultValueIfAvailable(bool raw, out object defaultValue); + + private Tuple DefaultValueInfo + { + get + { + Tuple defaultValueInfo = _lazyDefaultValueInfo; + if (defaultValueInfo == null) + { + object defaultValue; + bool hasDefaultValue = GetDefaultValueOrSentinel(raw: false, defaultValue: out defaultValue); + defaultValueInfo = _lazyDefaultValueInfo = Tuple.Create(hasDefaultValue, defaultValue); + } + return defaultValueInfo; + } + } + + private bool GetDefaultValueOrSentinel(bool raw, out object defaultValue) + { + bool hasDefaultValue = GetDefaultValueIfAvailable(raw, out defaultValue); + if (!hasDefaultValue) + { + defaultValue = IsOptional ? (object)Missing.Value : (object)DBNull.Value; + } + return hasDefaultValue; + } + + private volatile Tuple _lazyDefaultValueInfo; + private volatile Tuple _lazyRawDefaultValueInfo; + } +} + diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeParameterInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeParameterInfo.cs index 5d76cca3be..a17875b087 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeParameterInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeParameterInfo.cs @@ -6,15 +6,13 @@ using System; using System.Reflection; using System.Diagnostics; using System.Collections.Generic; -using System.Runtime.Serialization; namespace System.Reflection.Runtime.ParameterInfos { // // Abstract base for all ParameterInfo objects created by the Runtime. // - [Serializable] - internal abstract partial class RuntimeParameterInfo : ParameterInfo, ISerializable + internal abstract partial class RuntimeParameterInfo : ParameterInfo { protected RuntimeParameterInfo(MemberInfo member, int position) { @@ -25,11 +23,11 @@ namespace System.Reflection.Runtime.ParameterInfos public abstract override ParameterAttributes Attributes { get; } public abstract override IEnumerable CustomAttributes { get; } public abstract override Object DefaultValue { get; } + public abstract override object RawDefaultValue { get; } public sealed override bool Equals(Object obj) { - RuntimeParameterInfo other = obj as RuntimeParameterInfo; - if (other == null) + if (!(obj is RuntimeParameterInfo other)) return false; if (_position != other._position) return false; @@ -43,20 +41,6 @@ namespace System.Reflection.Runtime.ParameterInfos return _member.GetHashCode(); } - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - if (info == null) - throw new ArgumentNullException(nameof(info)); - - // Setting the ObjectType to ParameterInfo is partly tradition (CLR has always done this) and mostly because RuntimeParameterInfo - // is not discoverable via Reflection. - info.SetType(typeof(ParameterInfo)); - - // Compat note: Choosing not to serialize legacy fields such as "AttrsImpl" and "NameImpl" as they've been ignored by deserialization since CLR 4.0. - info.AddValue("MemberImpl", Member); - info.AddValue("PositionImpl", Position); - } - public abstract override Type[] GetOptionalCustomModifiers(); public abstract override Type[] GetRequiredCustomModifiers(); diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimePropertyIndexParameterInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimePropertyIndexParameterInfo.cs index c97c3d521b..dccd62727b 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimePropertyIndexParameterInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimePropertyIndexParameterInfo.cs @@ -46,6 +46,13 @@ namespace System.Reflection.Runtime.ParameterInfos } } + public sealed override object RawDefaultValue + { + get + { + return _backingParameter.RawDefaultValue; + } + } public sealed override Type[] GetOptionalCustomModifiers() { diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeSyntheticParameterInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeSyntheticParameterInfo.cs index e926052786..1cbcc4dd5a 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeSyntheticParameterInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeSyntheticParameterInfo.cs @@ -49,6 +49,14 @@ namespace System.Reflection.Runtime.ParameterInfos } } + public sealed override Object RawDefaultValue + { + get + { + return null; // Legacy: This is what the desktop returns. + } + } + public sealed override bool HasDefaultValue { get diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeThinMethodParameterInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeThinMethodParameterInfo.cs index 39ba3945c3..55ab899a84 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeThinMethodParameterInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/ParameterInfos/RuntimeThinMethodParameterInfo.cs @@ -49,6 +49,16 @@ namespace System.Reflection.Runtime.ParameterInfos } } + public sealed override object RawDefaultValue + { + get + { + // Returning "null" matches the desktop behavior, though this is inconsistent with the DBNull/Missing values + // returned by non-return ParameterInfo's without default values. + return null; + } + } + public sealed override bool HasDefaultValue { get diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/EcmaFormat/EcmaFormatRuntimePropertyInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/EcmaFormat/EcmaFormatRuntimePropertyInfo.cs index 1d57210f8a..f87a5168bd 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/EcmaFormat/EcmaFormatRuntimePropertyInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/EcmaFormat/EcmaFormatRuntimePropertyInfo.cs @@ -10,6 +10,7 @@ using System.Globalization; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Reflection.Runtime.General; +using System.Reflection.Runtime.General.EcmaFormat; using System.Reflection.Runtime.TypeInfos; using System.Reflection.Runtime.TypeInfos.EcmaFormat; using System.Reflection.Runtime.MethodInfos; @@ -78,8 +79,7 @@ namespace System.Reflection.Runtime.PropertyInfos.EcmaFormat ReflectionTrace.PropertyInfo_CustomAttributes(this); #endif - foreach (CustomAttributeData cad in RuntimeCustomAttributeData.GetCustomAttributes(_reader, _property.GetCustomAttributes())) - yield return cad; + return RuntimeCustomAttributeData.GetCustomAttributes(_reader, _property.GetCustomAttributes()); } } @@ -88,8 +88,7 @@ namespace System.Reflection.Runtime.PropertyInfos.EcmaFormat if (other == null) throw new ArgumentNullException(nameof(other)); - EcmaFormatRuntimePropertyInfo otherProperty = other as EcmaFormatRuntimePropertyInfo; - if (otherProperty == null) + if (!(other is EcmaFormatRuntimePropertyInfo otherProperty)) return false; if (!(_reader == otherProperty._reader)) return false; @@ -100,8 +99,7 @@ namespace System.Reflection.Runtime.PropertyInfos.EcmaFormat public sealed override bool Equals(Object obj) { - EcmaFormatRuntimePropertyInfo other = obj as EcmaFormatRuntimePropertyInfo; - if (other == null) + if (!(obj is EcmaFormatRuntimePropertyInfo other)) return false; if (!(_reader == other._reader)) return false; @@ -119,20 +117,6 @@ namespace System.Reflection.Runtime.PropertyInfos.EcmaFormat return _propertyHandle.GetHashCode(); } - public sealed override Object GetConstantValue() - { -#if ENABLE_REFLECTION_TRACE - if (ReflectionTrace.Enabled) - ReflectionTrace.PropertyInfo_GetConstantValue(this); -#endif - Object defaultValue; - if (!DefaultValueProcessing.GetDefaultValueIfAny(_reader, ref _property, this, out defaultValue)) - { - throw new InvalidOperationException(); - } - return defaultValue; - } - public sealed override int MetadataToken { get @@ -149,6 +133,11 @@ namespace System.Reflection.Runtime.PropertyInfos.EcmaFormat } } + protected sealed override bool GetDefaultValueIfAny(bool raw, out object defaultValue) + { + return DefaultValueProcessing.GetDefaultValueIfAny(_reader, ref _property, this, raw, out defaultValue); + } + protected sealed override RuntimeNamedMethodInfo GetPropertyMethod(PropertyMethodSemantics whichMethod) { MethodDefinitionHandle methodHandle; diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/NativeFormat/NativeFormatRuntimePropertyInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/NativeFormat/NativeFormatRuntimePropertyInfo.cs index 3cd353ef4f..98d24ba2c4 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/NativeFormat/NativeFormatRuntimePropertyInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/NativeFormat/NativeFormatRuntimePropertyInfo.cs @@ -10,6 +10,7 @@ using System.Globalization; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Reflection.Runtime.General; +using System.Reflection.Runtime.General.NativeFormat; using System.Reflection.Runtime.TypeInfos; using System.Reflection.Runtime.TypeInfos.NativeFormat; using System.Reflection.Runtime.MethodInfos; @@ -78,10 +79,7 @@ namespace System.Reflection.Runtime.PropertyInfos.NativeFormat ReflectionTrace.PropertyInfo_CustomAttributes(this); #endif - foreach (CustomAttributeData cad in RuntimeCustomAttributeData.GetCustomAttributes(_reader, _property.CustomAttributes)) - yield return cad; - foreach (CustomAttributeData cad in ReflectionCoreExecution.ExecutionEnvironment.GetPseudoCustomAttributes(_reader, _propertyHandle, _definingTypeInfo.TypeDefinitionHandle)) - yield return cad; + return RuntimeCustomAttributeData.GetCustomAttributes(_reader, _property.CustomAttributes); } } @@ -90,8 +88,7 @@ namespace System.Reflection.Runtime.PropertyInfos.NativeFormat if (other == null) throw new ArgumentNullException(nameof(other)); - NativeFormatRuntimePropertyInfo otherProperty = other as NativeFormatRuntimePropertyInfo; - if (otherProperty == null) + if (!(other is NativeFormatRuntimePropertyInfo otherProperty)) return false; if (!(_reader == otherProperty._reader)) return false; @@ -104,8 +101,7 @@ namespace System.Reflection.Runtime.PropertyInfos.NativeFormat public sealed override bool Equals(Object obj) { - NativeFormatRuntimePropertyInfo other = obj as NativeFormatRuntimePropertyInfo; - if (other == null) + if (!(obj is NativeFormatRuntimePropertyInfo other)) return false; if (!(_reader == other._reader)) return false; @@ -123,27 +119,6 @@ namespace System.Reflection.Runtime.PropertyInfos.NativeFormat return _propertyHandle.GetHashCode(); } - public sealed override Object GetConstantValue() - { -#if ENABLE_REFLECTION_TRACE - if (ReflectionTrace.Enabled) - ReflectionTrace.PropertyInfo_GetConstantValue(this); -#endif - - Object defaultValue; - if (!ReflectionCoreExecution.ExecutionEnvironment.GetDefaultValueIfAny( - _reader, - _propertyHandle, - this.PropertyType, - this.CustomAttributes, - out defaultValue)) - { - throw new InvalidOperationException(); - } - return defaultValue; - } - - public sealed override int MetadataToken { get @@ -160,6 +135,11 @@ namespace System.Reflection.Runtime.PropertyInfos.NativeFormat } } + protected sealed override bool GetDefaultValueIfAny(bool raw, out object defaultValue) + { + return DefaultValueParser.GetDefaultValueIfAny(_reader, _property.DefaultValue, PropertyType, CustomAttributes, raw, out defaultValue); + } + protected sealed override RuntimeNamedMethodInfo GetPropertyMethod(PropertyMethodSemantics whichMethod) { NativeFormatMethodSemanticsAttributes localMethodSemantics; diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/RuntimePropertyInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/RuntimePropertyInfo.cs index 5273427569..7691902beb 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/RuntimePropertyInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/RuntimePropertyInfo.cs @@ -8,7 +8,6 @@ using System.Reflection; using System.Diagnostics; using System.Globalization; using System.Collections.Generic; -using System.Runtime.Serialization; using System.Runtime.CompilerServices; using System.Reflection.Runtime.General; using System.Reflection.Runtime.TypeInfos; @@ -28,7 +27,7 @@ namespace System.Reflection.Runtime.PropertyInfos // The runtime's implementation of PropertyInfo's // [DebuggerDisplay("{_debugName}")] - internal abstract partial class RuntimePropertyInfo : PropertyInfo, ISerializable, ITraceableTypeMember + internal abstract partial class RuntimePropertyInfo : PropertyInfo, ITraceableTypeMember { // // propertyHandle - the "tkPropertyDef" that identifies the property. @@ -121,11 +120,6 @@ namespace System.Reflection.Runtime.PropertyInfos return result; } - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - throw new PlatformNotSupportedException(); - } - public sealed override MethodInfo GetMethod { get @@ -347,9 +341,13 @@ namespace System.Reflection.Runtime.PropertyInfos public abstract override IEnumerable CustomAttributes { get; } public abstract override bool Equals(Object obj); public abstract override int GetHashCode(); - public abstract override Object GetConstantValue(); public abstract override int MetadataToken { get; } + public sealed override object GetConstantValue() => GetConstantValue(raw: false); + public sealed override object GetRawConstantValue() => GetConstantValue(raw: true); + + protected abstract bool GetDefaultValueIfAny(bool raw, out object defaultValue); + /// /// Return a qualified handle that can be used to get the type of the property. /// @@ -381,6 +379,21 @@ namespace System.Reflection.Runtime.PropertyInfos protected readonly RuntimeTypeInfo ContextTypeInfo; protected readonly RuntimeTypeInfo _reflectedType; + private object GetConstantValue(bool raw) + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.PropertyInfo_GetConstantValue(this); +#endif + + object defaultValue; + if (!GetDefaultValueIfAny(raw, out defaultValue)) + { + throw new InvalidOperationException(SR.Arg_EnumLitValueNotFound); + } + return defaultValue; + } + private volatile MethodInvoker _lazyGetterInvoker = null; private volatile MethodInvoker _lazySetterInvoker = null; diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeGenericParameterTypeInfoForMethods.UnificationKey.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeGenericParameterTypeInfoForMethods.UnificationKey.cs index 930d14efb8..46a086829b 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeGenericParameterTypeInfoForMethods.UnificationKey.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeGenericParameterTypeInfoForMethods.UnificationKey.cs @@ -37,9 +37,9 @@ namespace System.Reflection.Runtime.TypeInfos.EcmaFormat public override bool Equals(object obj) { - if (!(obj is UnificationKey)) + if (!(obj is UnificationKey other)) return false; - return Equals((UnificationKey)obj); + return Equals(other); } public bool Equals(UnificationKey other) diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeGenericParameterTypeInfoForMethods.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeGenericParameterTypeInfoForMethods.cs index ec58e970e6..08f4973117 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeGenericParameterTypeInfoForMethods.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeGenericParameterTypeInfoForMethods.cs @@ -27,6 +27,9 @@ namespace System.Reflection.Runtime.TypeInfos.EcmaFormat _declaringRuntimeNamedMethodInfo = declaringRuntimeNamedMethodInfo; } + public sealed override bool IsGenericTypeParameter => false; + public sealed override bool IsGenericMethodParameter => true; + public sealed override MethodBase DeclaringMethod { get diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeGenericParameterTypeInfoForTypes.UnificationKey.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeGenericParameterTypeInfoForTypes.UnificationKey.cs index fd4e5903b9..2cc0f60b99 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeGenericParameterTypeInfoForTypes.UnificationKey.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeGenericParameterTypeInfoForTypes.UnificationKey.cs @@ -35,9 +35,9 @@ namespace System.Reflection.Runtime.TypeInfos.EcmaFormat public override bool Equals(object obj) { - if (!(obj is UnificationKey)) + if (!(obj is UnificationKey other)) return false; - return Equals((UnificationKey)obj); + return Equals(other); } public bool Equals(UnificationKey other) diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeGenericParameterTypeInfoForTypes.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeGenericParameterTypeInfoForTypes.cs index 2f14f6e270..d4d540cd96 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeGenericParameterTypeInfoForTypes.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeGenericParameterTypeInfoForTypes.cs @@ -23,6 +23,9 @@ namespace System.Reflection.Runtime.TypeInfos.EcmaFormat _declaringType = declaringType; } + public sealed override bool IsGenericTypeParameter => true; + public sealed override bool IsGenericMethodParameter => false; + public sealed override MethodBase DeclaringMethod { get diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeNamedTypeInfo.UnificationKey.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeNamedTypeInfo.UnificationKey.cs index 472d47da57..5b882317c8 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeNamedTypeInfo.UnificationKey.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeNamedTypeInfo.UnificationKey.cs @@ -42,9 +42,9 @@ namespace System.Reflection.Runtime.TypeInfos.EcmaFormat public override bool Equals(object obj) { - if (!(obj is UnificationKey)) + if (!(obj is UnificationKey other)) return false; - return Equals((UnificationKey)obj); + return Equals(other); } public bool Equals(UnificationKey other) diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeNamedTypeInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeNamedTypeInfo.cs index 0034bac7a2..b726e29941 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeNamedTypeInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/EcmaFormat/EcmaFormatRuntimeNamedTypeInfo.cs @@ -42,24 +42,6 @@ namespace System.Reflection.Runtime.TypeInfos.EcmaFormat } } - public sealed override IEnumerable CustomAttributes - { - get - { -#if ENABLE_REFLECTION_TRACE - if (ReflectionTrace.Enabled) - ReflectionTrace.TypeInfo_CustomAttributes(this); -#endif - - IEnumerable customAttributes = RuntimeCustomAttributeData.GetCustomAttributes(_reader, _typeDefinition.GetCustomAttributes()); - foreach (CustomAttributeData cad in customAttributes) - yield return cad; - - if (0 != (_typeDefinition.Attributes & TypeAttributes.Import)) - yield return ReflectionCoreExecution.ExecutionDomain.GetCustomAttributeData(typeof(ComImportAttribute), null, null); - } - } - protected sealed override Guid? ComputeGuidFromCustomAttributes() { // @@ -93,8 +75,7 @@ namespace System.Reflection.Runtime.TypeInfos.EcmaFormat if (firstArg.Value == null) continue; - string guidString = firstArg.Value as string; - if (guidString == null) + if (!(firstArg.Value is string guidString)) continue; return new Guid(guidString); @@ -218,6 +199,8 @@ namespace System.Reflection.Runtime.TypeInfos.EcmaFormat return name.EscapeTypeNameIdentifier(); } + protected sealed override IEnumerable TrueCustomAttributes => RuntimeCustomAttributeData.GetCustomAttributes(_reader, _typeDefinition.GetCustomAttributes()); + internal sealed override RuntimeTypeInfo[] RuntimeGenericTypeParameters { get diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForMethods.UnificationKey.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForMethods.UnificationKey.cs index dd40f7be7b..0363945258 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForMethods.UnificationKey.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForMethods.UnificationKey.cs @@ -37,9 +37,9 @@ namespace System.Reflection.Runtime.TypeInfos.NativeFormat public override bool Equals(object obj) { - if (!(obj is UnificationKey)) + if (!(obj is UnificationKey other)) return false; - return Equals((UnificationKey)obj); + return Equals(other); } public bool Equals(UnificationKey other) diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForMethods.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForMethods.cs index f237f5565b..42640f7af7 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForMethods.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForMethods.cs @@ -27,6 +27,9 @@ namespace System.Reflection.Runtime.TypeInfos.NativeFormat _declaringRuntimeNamedMethodInfo = declaringRuntimeNamedMethodInfo; } + public sealed override bool IsGenericTypeParameter => false; + public sealed override bool IsGenericMethodParameter => true; + public sealed override MethodBase DeclaringMethod { get diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForTypes.UnificationKey.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForTypes.UnificationKey.cs index 43da2ddaae..93b96b3552 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForTypes.UnificationKey.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForTypes.UnificationKey.cs @@ -35,9 +35,9 @@ namespace System.Reflection.Runtime.TypeInfos.NativeFormat public override bool Equals(object obj) { - if (!(obj is UnificationKey)) + if (!(obj is UnificationKey other)) return false; - return Equals((UnificationKey)obj); + return Equals(other); } public bool Equals(UnificationKey other) diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForTypes.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForTypes.cs index 2cbe729439..3cc9ab957e 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForTypes.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForTypes.cs @@ -23,6 +23,9 @@ namespace System.Reflection.Runtime.TypeInfos.NativeFormat _declaringType = declaringType; } + public sealed override bool IsGenericTypeParameter => true; + public sealed override bool IsGenericMethodParameter => false; + public sealed override MethodBase DeclaringMethod { get diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeNamedTypeInfo.UnificationKey.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeNamedTypeInfo.UnificationKey.cs index d81b7d9a70..02d63b2e52 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeNamedTypeInfo.UnificationKey.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeNamedTypeInfo.UnificationKey.cs @@ -42,9 +42,9 @@ namespace System.Reflection.Runtime.TypeInfos.NativeFormat public override bool Equals(object obj) { - if (!(obj is UnificationKey)) + if (!(obj is UnificationKey other)) return false; - return Equals((UnificationKey)obj); + return Equals(other); } public bool Equals(UnificationKey other) diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeNamedTypeInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeNamedTypeInfo.cs index 787e9370d6..382dde091d 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeNamedTypeInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeNamedTypeInfo.cs @@ -2,19 +2,14 @@ // 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.Text; using System.Reflection; using System.Diagnostics; using System.Collections.Generic; -using System.Collections.Concurrent; using System.Reflection.Runtime.Assemblies; using System.Reflection.Runtime.General; -using System.Reflection.Runtime.MethodInfos; -using System.Reflection.Runtime.TypeInfos; using System.Reflection.Runtime.CustomAttributes; -using Internal.Reflection.Core.Execution; using Internal.Reflection.Tracing; using Internal.Metadata.NativeFormat; @@ -46,25 +41,6 @@ namespace System.Reflection.Runtime.TypeInfos.NativeFormat } } - public sealed override IEnumerable CustomAttributes - { - get - { -#if ENABLE_REFLECTION_TRACE - if (ReflectionTrace.Enabled) - ReflectionTrace.TypeInfo_CustomAttributes(this); -#endif - - IEnumerable customAttributes = RuntimeCustomAttributeData.GetCustomAttributes(_reader, _typeDefinition.CustomAttributes); - foreach (CustomAttributeData cad in customAttributes) - yield return cad; - foreach (CustomAttributeData cad in ReflectionCoreExecution.ExecutionEnvironment.GetPseudoCustomAttributes(_reader, _typeDefinitionHandle)) - { - yield return cad; - } - } - } - protected sealed override Guid? ComputeGuidFromCustomAttributes() { // @@ -79,15 +55,14 @@ namespace System.Reflection.Runtime.TypeInfos.NativeFormat if (cah.IsCustomAttributeOfType(_reader, "System.Runtime.InteropServices", "GuidAttribute")) { CustomAttribute ca = cah.GetCustomAttribute(_reader); - IEnumerator fahEnumerator = ca.FixedArguments.GetEnumerator(); + FixedArgumentHandleCollection.Enumerator fahEnumerator = ca.FixedArguments.GetEnumerator(); if (!fahEnumerator.MoveNext()) continue; FixedArgumentHandle guidStringArgumentHandle = fahEnumerator.Current; if (fahEnumerator.MoveNext()) continue; FixedArgument guidStringArgument = guidStringArgumentHandle.GetFixedArgument(_reader); - String guidString = guidStringArgument.Value.ParseConstantValue(_reader) as String; - if (guidString == null) + if (!(guidStringArgument.Value.ParseConstantValue(_reader) is string guidString)) continue; return new Guid(guidString); } @@ -210,6 +185,8 @@ namespace System.Reflection.Runtime.TypeInfos.NativeFormat return name.EscapeTypeNameIdentifier(); } + protected sealed override IEnumerable TrueCustomAttributes => RuntimeCustomAttributeData.GetCustomAttributes(_reader, _typeDefinition.CustomAttributes); + internal sealed override RuntimeTypeInfo[] RuntimeGenericTypeParameters { get diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeArrayTypeInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeArrayTypeInfo.cs index e058d0cdd0..98db26c700 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeArrayTypeInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeArrayTypeInfo.cs @@ -73,7 +73,7 @@ namespace System.Reflection.Runtime.TypeInfos arrayType, ctorParameters, InvokerOptions.AllowNullThis | InvokerOptions.DontWrapException, - delegate (Object _this, Object[] args) + delegate (Object _this, Object[] args, Type thisType) { int[] lengths = new int[rank]; for (int i = 0; i < rank; i++) @@ -111,7 +111,7 @@ namespace System.Reflection.Runtime.TypeInfos arrayType, ctorParameters, InvokerOptions.AllowNullThis | InvokerOptions.DontWrapException, - delegate (Object _this, Object[] args) + delegate (Object _this, Object[] args, Type thisType) { int[] lengths = new int[args.Length]; for (int i = 0; i < args.Length; i++) @@ -147,7 +147,7 @@ namespace System.Reflection.Runtime.TypeInfos arrayType, ctorParameters, InvokerOptions.AllowNullThis | InvokerOptions.DontWrapException, - delegate (Object _this, Object[] args) + delegate (Object _this, Object[] args, Type thisType) { int[] lengths = new int[rank]; int[] lowerBounds = new int[rank]; @@ -185,7 +185,7 @@ namespace System.Reflection.Runtime.TypeInfos getParameters, elementType, InvokerOptions.None, - delegate (Object _this, Object[] args) + delegate (Object _this, Object[] args, Type thisType) { Array array = (Array)_this; int[] indices = new int[rank]; @@ -208,7 +208,7 @@ namespace System.Reflection.Runtime.TypeInfos setParameters, voidType, InvokerOptions.None, - delegate (Object _this, Object[] args) + delegate (Object _this, Object[] args, Type thisType) { Array array = (Array)_this; int[] indices = new int[rank]; @@ -232,7 +232,7 @@ namespace System.Reflection.Runtime.TypeInfos addressParameters, elementType.GetByRefType(), InvokerOptions.None, - delegate (Object _this, Object[] args) + delegate (Object _this, Object[] args, Type thisType) { throw new NotSupportedException(); } diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeClsIdTypeInfo.UnificationKey.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeClsIdTypeInfo.UnificationKey.cs index 5dc64ada2c..82657a0ea5 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeClsIdTypeInfo.UnificationKey.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeClsIdTypeInfo.UnificationKey.cs @@ -22,9 +22,9 @@ namespace System.Reflection.Runtime.TypeInfos public override bool Equals(object obj) { - if (!(obj is UnificationKey)) + if (!(obj is UnificationKey other)) return false; - return Equals((UnificationKey)obj); + return Equals(other); } public bool Equals(UnificationKey other) diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeClsIdTypeInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeClsIdTypeInfo.cs index b09238548f..6cc8532dcf 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeClsIdTypeInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeClsIdTypeInfo.cs @@ -9,6 +9,7 @@ using System.Reflection.Runtime.General; using System.Reflection.Runtime.MethodInfos; using System.Runtime.InteropServices; +using Internal.Reflection.Tracing; using Internal.Reflection.Core.Execution; namespace System.Reflection.Runtime.TypeInfos @@ -36,6 +37,19 @@ namespace System.Reflection.Runtime.TypeInfos public sealed override StructLayoutAttribute StructLayoutAttribute => BaseType.StructLayoutAttribute; public sealed override string ToString() => BaseType.ToString(); + public sealed override IEnumerable CustomAttributes + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_CustomAttributes(this); +#endif + + return Empty.Enumerable; + } + } + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) { if (other == null) diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeConstructedGenericTypeInfo.UnificationKey.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeConstructedGenericTypeInfo.UnificationKey.cs index 9d154a757c..f100f25ed5 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeConstructedGenericTypeInfo.UnificationKey.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeConstructedGenericTypeInfo.UnificationKey.cs @@ -39,9 +39,9 @@ namespace System.Reflection.Runtime.TypeInfos public override bool Equals(object obj) { - if (!(obj is UnificationKey)) + if (!(obj is UnificationKey other)) return false; - return Equals((UnificationKey)obj); + return Equals(other); } public bool Equals(UnificationKey other) diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeConstructedGenericTypeInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeConstructedGenericTypeInfo.cs index 3f2d8ad36c..9740554ec5 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeConstructedGenericTypeInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeConstructedGenericTypeInfo.cs @@ -39,6 +39,9 @@ namespace System.Reflection.Runtime.TypeInfos protected sealed override bool IsPointerImpl() => false; public sealed override bool IsConstructedGenericType => true; public sealed override bool IsGenericParameter => false; + public sealed override bool IsGenericTypeParameter => false; + public sealed override bool IsGenericMethodParameter => false; + public sealed override bool IsByRefLike => GenericTypeDefinitionTypeInfo.IsByRefLike; // // Implements IKeyedItem.PrepareKey. @@ -244,8 +247,7 @@ namespace System.Reflection.Runtime.TypeInfos get { RuntimeTypeInfo genericTypeDefinition = this.GenericTypeDefinitionTypeInfo; - RuntimeNamedTypeInfo genericTypeDefinitionNamedTypeInfo = genericTypeDefinition as RuntimeNamedTypeInfo; - if (genericTypeDefinitionNamedTypeInfo == null) + if (!(genericTypeDefinition is RuntimeNamedTypeInfo genericTypeDefinitionNamedTypeInfo)) throw ReflectionCoreExecution.ExecutionDomain.CreateMissingMetadataException(genericTypeDefinition); return genericTypeDefinitionNamedTypeInfo; } diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeGenericParameterTypeInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeGenericParameterTypeInfo.cs index fb030e856b..c5cae14b42 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeGenericParameterTypeInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeGenericParameterTypeInfo.cs @@ -35,6 +35,9 @@ namespace System.Reflection.Runtime.TypeInfos protected sealed override bool IsPointerImpl() => false; public sealed override bool IsConstructedGenericType => false; public sealed override bool IsGenericParameter => true; + public abstract override bool IsGenericTypeParameter { get; } + public abstract override bool IsGenericMethodParameter { get; } + public sealed override bool IsByRefLike => false; public sealed override Assembly Assembly { diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeHasElementTypeInfo.UnificationKey.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeHasElementTypeInfo.UnificationKey.cs index 3bee50f52b..a3aaa20ea9 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeHasElementTypeInfo.UnificationKey.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeHasElementTypeInfo.UnificationKey.cs @@ -37,9 +37,9 @@ namespace System.Reflection.Runtime.TypeInfos public override bool Equals(object obj) { - if (!(obj is UnificationKey)) + if (!(obj is UnificationKey other)) return false; - return Equals((UnificationKey)obj); + return Equals(other); } public bool Equals(UnificationKey other) diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeHasElementTypeInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeHasElementTypeInfo.cs index e60ec63139..be466e7e43 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeHasElementTypeInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeHasElementTypeInfo.cs @@ -36,6 +36,9 @@ namespace System.Reflection.Runtime.TypeInfos protected abstract override bool IsPointerImpl(); public sealed override bool IsConstructedGenericType => false; public sealed override bool IsGenericParameter => false; + public sealed override bool IsGenericTypeParameter => false; + public sealed override bool IsGenericMethodParameter => false; + public sealed override bool IsByRefLike => false; // // Implements IKeyedItem.PrepareKey. @@ -73,6 +76,19 @@ namespace System.Reflection.Runtime.TypeInfos } } + public sealed override IEnumerable CustomAttributes + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_CustomAttributes(this); +#endif + + return Empty.Enumerable; + } + } + public sealed override bool ContainsGenericParameters { get diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeNamedTypeInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeNamedTypeInfo.cs index da34e97142..6f40ed2674 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeNamedTypeInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeNamedTypeInfo.cs @@ -2,26 +2,15 @@ // 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.Text; -using System.Reflection; using System.Diagnostics; using System.Collections.Generic; using System.Collections.Concurrent; +using System.Runtime.InteropServices; using System.Reflection.Runtime.General; -using System.Reflection.Runtime.TypeInfos; -using System.Reflection.Runtime.Assemblies; using System.Reflection.Runtime.CustomAttributes; -using Internal.LowLevelLinq; -using Internal.Reflection.Core.Execution; - using Internal.Reflection.Tracing; -using CharSet = System.Runtime.InteropServices.CharSet; -using LayoutKind = System.Runtime.InteropServices.LayoutKind; -using StructLayoutAttribute = System.Runtime.InteropServices.StructLayoutAttribute; - namespace System.Reflection.Runtime.TypeInfos { // @@ -43,6 +32,23 @@ namespace System.Reflection.Runtime.TypeInfos } } + public sealed override IEnumerable CustomAttributes + { + get + { +#if ENABLE_REFLECTION_TRACE + if (ReflectionTrace.Enabled) + ReflectionTrace.TypeInfo_CustomAttributes(this); +#endif + + foreach (CustomAttributeData cad in TrueCustomAttributes) + yield return cad; + + if (0 != (Attributes & TypeAttributes.Import)) + yield return new RuntimePseudoCustomAttributeData(typeof(ComImportAttribute), null, null); + } + } + public bool Equals(RuntimeNamedTypeInfo other) { // RuntimeTypeInfo.Equals(object) is the one that encapsulates our unification strategy so defer to him. @@ -164,6 +170,8 @@ namespace System.Reflection.Runtime.TypeInfos } } + protected abstract IEnumerable TrueCustomAttributes { get; } + // // Returns the anchoring typedef that declares the members that this type wants returned by the Declared*** properties. // The Declared*** properties will project the anchoring typedef's members by overriding their DeclaringType property with "this" diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeDefinitionTypeInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeDefinitionTypeInfo.cs index 0ab216de22..d1d33416e3 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeDefinitionTypeInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeDefinitionTypeInfo.cs @@ -2,6 +2,11 @@ // 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.Runtime.CompilerServices; +using System.Reflection.Runtime.General; + +using Internal.Runtime.Augments; + namespace System.Reflection.Runtime.TypeInfos { // @@ -19,6 +24,25 @@ namespace System.Reflection.Runtime.TypeInfos protected sealed override bool IsPointerImpl() => false; public sealed override bool IsConstructedGenericType => false; public sealed override bool IsGenericParameter => false; + public sealed override bool IsGenericTypeParameter => false; + public sealed override bool IsGenericMethodParameter => false; + + public sealed override bool IsByRefLike + { + get + { + RuntimeTypeHandle typeHandle = InternalTypeHandleIfAvailable; + if (!typeHandle.IsNull()) + return RuntimeAugments.IsByRefLike(typeHandle); + + foreach (CustomAttributeData cad in CustomAttributes) + { + if (cad.AttributeType == typeof(IsByRefLikeAttribute)) + return true; + } + return false; + } + } // Left unsealed as RuntimeCLSIDTypeInfo has special behavior and needs to override. public override bool HasSameMetadataDefinitionAs(MemberInfo other) @@ -27,8 +51,7 @@ namespace System.Reflection.Runtime.TypeInfos throw new ArgumentNullException(nameof(other)); // Do not rewrite as a call to IsConstructedGenericType - we haven't yet established that "other" is a runtime-implemented member yet! - RuntimeConstructedGenericTypeInfo otherConstructedGenericType = other as RuntimeConstructedGenericTypeInfo; - if (otherConstructedGenericType != null) + if (other is RuntimeConstructedGenericTypeInfo otherConstructedGenericType) other = otherConstructedGenericType.GetGenericTypeDefinition(); // Unlike most other MemberInfo objects, types never get cloned due to containing generic types being instantiated. diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.BindingFlags.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.BindingFlags.cs index a5b1625188..25cc029875 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.BindingFlags.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.BindingFlags.cs @@ -54,6 +54,16 @@ namespace System.Reflection.Runtime.TypeInfos public sealed override MethodInfo[] GetMethods(BindingFlags bindingAttr) => Query(bindingAttr).ToArray(); protected sealed override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + return GetMethodImplCommon(name, GenericParameterCountAny, bindingAttr, binder, callConvention, types, modifiers); + } + + protected sealed override MethodInfo GetMethodImpl(string name, int genericParameterCount, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + return GetMethodImplCommon(name, genericParameterCount, bindingAttr, binder, callConvention, types, modifiers); + } + + private MethodInfo GetMethodImplCommon(string name, int genericParameterCount, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) { Debug.Assert(name != null); @@ -61,6 +71,7 @@ namespace System.Reflection.Runtime.TypeInfos if (types == null) { // Group #1: This group of api accept only a name and BindingFlags. The other parameters are hard-wired by the non-virtual api entrypoints. + Debug.Assert(genericParameterCount == GenericParameterCountAny); Debug.Assert(binder == null); Debug.Assert(callConvention == CallingConventions.Any); Debug.Assert(modifiers == null); @@ -73,6 +84,8 @@ namespace System.Reflection.Runtime.TypeInfos ListBuilder candidates = new ListBuilder(); foreach (MethodInfo candidate in queryResult) { + if (genericParameterCount != GenericParameterCountAny && genericParameterCount != candidate.GenericParameterCount) + continue; if (candidate.QualifiesBasedOnParameterCount(bindingAttr, callConvention, types)) candidates.Add(candidate); } @@ -186,6 +199,8 @@ namespace System.Reflection.Runtime.TypeInfos private TypeComponentsCache Cache => _lazyCache ?? (_lazyCache = new TypeComponentsCache(this)); private volatile TypeComponentsCache _lazyCache; + + private const int GenericParameterCountAny = -1; } } diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.TypeComponentsCache.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.TypeComponentsCache.cs index 9b031adfd9..4cc0ddfda7 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.TypeComponentsCache.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.TypeComponentsCache.cs @@ -6,9 +6,12 @@ using System.Threading; using System.Diagnostics; using System.Collections.Generic; using System.Collections.Concurrent; +using System.Reflection.Runtime.General; using System.Reflection.Runtime.BindingFlagSupport; -using Unsafe = System.Runtime.CompilerServices.Unsafe; +using Unsafe = Internal.Runtime.CompilerServices.Unsafe; + +using EnumInfo = Internal.Runtime.Augments.EnumInfo; namespace System.Reflection.Runtime.TypeInfos { @@ -71,6 +74,8 @@ namespace System.Reflection.Runtime.TypeInfos return Unsafe.As>(result); } + public EnumInfo EnumInfo => _lazyEnumInfo ?? (_lazyEnumInfo = new EnumInfo(_type)); + private static object[] CreatePerNameQueryCaches(RuntimeTypeInfo type, bool ignoreCase) { object[] perNameCaches = new object[MemberTypeIndex.Count]; @@ -97,6 +102,8 @@ namespace System.Reflection.Runtime.TypeInfos private readonly RuntimeTypeInfo _type; + private volatile EnumInfo _lazyEnumInfo; + // // Each PerName cache persists the results of a Type.Get(name, bindingFlags) for a particular MemberInfoType "M". // diff --git a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs index 91697acfdb..3304d97a56 100644 --- a/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs +++ b/external/corert/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs @@ -5,7 +5,6 @@ using System.Diagnostics; using System.Collections.Generic; using System.Runtime.CompilerServices; -using System.Runtime.Serialization; using System.Reflection.Runtime.General; using System.Reflection.Runtime.MethodInfos; @@ -13,6 +12,8 @@ using Internal.Reflection.Core.Execution; using Internal.Reflection.Tracing; using Internal.Reflection.Augments; +using EnumInfo = Internal.Runtime.Augments.EnumInfo; + using IRuntimeImplementedType = Internal.Reflection.Core.NonPortable.IRuntimeImplementedType; using StructLayoutAttribute = System.Runtime.InteropServices.StructLayoutAttribute; @@ -34,9 +35,8 @@ namespace System.Reflection.Runtime.TypeInfos // - Overrides many "NotImplemented" members in TypeInfo with abstracts so failure to implement // shows up as build error. // - [Serializable] [DebuggerDisplay("{_debugName}")] - internal abstract partial class RuntimeTypeInfo : TypeInfo, ISerializable, ITraceableTypeMember, ICloneable, IRuntimeImplementedType + internal abstract partial class RuntimeTypeInfo : TypeInfo, ITraceableTypeMember, ICloneable, IRuntimeImplementedType { protected RuntimeTypeInfo() { @@ -51,7 +51,11 @@ namespace System.Reflection.Runtime.TypeInfos protected abstract override bool IsByRefImpl(); protected abstract override bool IsPointerImpl(); public abstract override bool IsGenericParameter { get; } + public abstract override bool IsGenericTypeParameter { get; } + public abstract override bool IsGenericMethodParameter { get; } public abstract override bool IsConstructedGenericType { get; } + public abstract override bool IsByRefLike { get; } + public sealed override bool IsCollectible => false; public abstract override Assembly Assembly { get; } @@ -110,22 +114,7 @@ namespace System.Reflection.Runtime.TypeInfos public abstract override bool ContainsGenericParameters { get; } - // - // Left unsealed so that RuntimeNamedTypeInfo and RuntimeConstructedGenericTypeInfo and RuntimeGenericParameterTypeInfo can override. - // - public override IEnumerable CustomAttributes - { - get - { -#if ENABLE_REFLECTION_TRACE - if (ReflectionTrace.Enabled) - ReflectionTrace.TypeInfo_CustomAttributes(this); -#endif - - Debug.Assert(IsArray || IsByRef || IsPointer); - return Empty.Enumerable; - } - } + public abstract override IEnumerable CustomAttributes { get; } // // Left unsealed as generic parameter types must override. @@ -209,11 +198,6 @@ namespace System.Reflection.Runtime.TypeInfos throw new PlatformNotSupportedException(SR.PlatformNotSupported_InterfaceMap); } - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - throw new PlatformNotSupportedException(); - } - // // Implements the correct GUID behavior for all "constructed" types (i.e. returning an all-zero GUID.) Left unsealed // so that RuntimeNamedTypeInfo can override. @@ -449,24 +433,42 @@ namespace System.Reflection.Runtime.TypeInfos // In a pay-for-play world, this can cause needless MissingMetadataExceptions. There is no harm in creating // the Type object for an inconsistent generic type - no EEType will ever match it so any attempt to "invoke" it // will throw an exception. + bool foundSignatureType = false; RuntimeTypeInfo[] runtimeTypeArguments = new RuntimeTypeInfo[typeArguments.Length]; for (int i = 0; i < typeArguments.Length; i++) { - RuntimeTypeInfo runtimeTypeArgument = typeArguments[i] as RuntimeTypeInfo; + RuntimeTypeInfo runtimeTypeArgument = runtimeTypeArguments[i] = typeArguments[i] as RuntimeTypeInfo; if (runtimeTypeArgument == null) { if (typeArguments[i] == null) throw new ArgumentNullException(); + + if (typeArguments[i].IsSignatureType) + { + foundSignatureType = true; + } else + { throw new PlatformNotSupportedException(SR.PlatformNotSupported_MakeGenericType); // "PlatformNotSupported" because on desktop, passing in a foreign type is allowed and creates a RefEmit.TypeBuilder + } } + } + + if (foundSignatureType) + return ReflectionAugments.MakeGenericSignatureType(this, typeArguments); + + for (int i = 0; i < typeArguments.Length; i++) + { + RuntimeTypeInfo runtimeTypeArgument = runtimeTypeArguments[i]; // Desktop compatibility: Treat generic type definitions as a constructed generic type using the generic parameters as type arguments. if (runtimeTypeArgument.IsGenericTypeDefinition) - runtimeTypeArgument = runtimeTypeArgument.GetConstructedGenericType(runtimeTypeArgument.RuntimeGenericTypeParameters); + runtimeTypeArgument = runtimeTypeArguments[i] = runtimeTypeArgument.GetConstructedGenericType(runtimeTypeArgument.RuntimeGenericTypeParameters); - runtimeTypeArguments[i] = runtimeTypeArgument; + if (runtimeTypeArgument.IsByRefLike) + throw new TypeLoadException(SR.CannotUseByRefLikeTypeInInstantiation); } + return this.GetConstructedGenericType(runtimeTypeArguments); } @@ -613,6 +615,8 @@ namespace System.Reflection.Runtime.TypeInfos } } + internal EnumInfo EnumInfo => Cache.EnumInfo; + internal abstract Type InternalDeclaringType { get; } // diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/DefaultValueParser.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/DefaultValueParser.cs deleted file mode 100644 index 1720d2a352..0000000000 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/DefaultValueParser.cs +++ /dev/null @@ -1,117 +0,0 @@ -// 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 global::System; -using global::System.Reflection; -using global::System.Collections.Generic; -using global::System.Runtime.InteropServices; -using global::System.Runtime.CompilerServices; - -using global::Internal.Runtime.Augments; - -using global::Internal.Reflection.Core; -using global::Internal.Reflection.Core.Execution; -using global::Internal.Reflection.Extensions.NonPortable; - -using global::Internal.Metadata.NativeFormat; - -namespace Internal.Reflection.Execution -{ - internal static class DefaultValueParser - { - public static bool GetDefaultValueIfAny(MemberType memberType, MetadataReader reader, Handle constantHandle, Type declaredType, IEnumerable customAttributes, out Object defaultValue) - { - if (!(constantHandle.IsNull(reader))) - { - defaultValue = ParseMetadataConstant(reader, constantHandle); - if (declaredType.IsEnum) - defaultValue = Enum.ToObject(declaredType, defaultValue); - return true; - } - - if (memberType != MemberType.Property) // the attributes in question cannot be applied to properties. - { - // Legacy: If there are multiple default value attribute, the desktop picks one at random (and so do we...) - foreach (CustomAttributeData cad in customAttributes) - { - Type attributeType = cad.AttributeType; - if (attributeType.IsSubclassOf(typeof(CustomConstantAttribute))) - { - CustomConstantAttribute customConstantAttribute = (CustomConstantAttribute)(cad.Instantiate()); - defaultValue = customConstantAttribute.Value; - return true; - } - if (attributeType.Equals(typeof(DecimalConstantAttribute))) - { - DecimalConstantAttribute decimalConstantAttribute = (DecimalConstantAttribute)(cad.Instantiate()); - defaultValue = decimalConstantAttribute.Value; - return true; - } - } - } - - defaultValue = null; - return false; - } - - // Not all default value types are permitted in all scenarios. - public enum MemberType - { - Field = 1, - Property = 2, - Parameter = 3, - } - - private static Object ParseMetadataConstant(MetadataReader reader, Handle handle) - { - switch (handle.HandleType) - { - case HandleType.ConstantBooleanValue: - return handle.ToConstantBooleanValueHandle(reader).GetConstantBooleanValue(reader).Value; - - case HandleType.ConstantStringValue: - return handle.ToConstantStringValueHandle(reader).GetConstantStringValue(reader).Value; - - case HandleType.ConstantCharValue: - return handle.ToConstantCharValueHandle(reader).GetConstantCharValue(reader).Value; - - case HandleType.ConstantByteValue: - return handle.ToConstantByteValueHandle(reader).GetConstantByteValue(reader).Value; - - case HandleType.ConstantSByteValue: - return handle.ToConstantSByteValueHandle(reader).GetConstantSByteValue(reader).Value; - - case HandleType.ConstantInt16Value: - return handle.ToConstantInt16ValueHandle(reader).GetConstantInt16Value(reader).Value; - - case HandleType.ConstantUInt16Value: - return handle.ToConstantUInt16ValueHandle(reader).GetConstantUInt16Value(reader).Value; - - case HandleType.ConstantInt32Value: - return handle.ToConstantInt32ValueHandle(reader).GetConstantInt32Value(reader).Value; - - case HandleType.ConstantUInt32Value: - return handle.ToConstantUInt32ValueHandle(reader).GetConstantUInt32Value(reader).Value; - - case HandleType.ConstantInt64Value: - return handle.ToConstantInt64ValueHandle(reader).GetConstantInt64Value(reader).Value; - - case HandleType.ConstantUInt64Value: - return handle.ToConstantUInt64ValueHandle(reader).GetConstantUInt64Value(reader).Value; - - case HandleType.ConstantSingleValue: - return handle.ToConstantSingleValueHandle(reader).GetConstantSingleValue(reader).Value; - - case HandleType.ConstantDoubleValue: - return handle.ToConstantDoubleValueHandle(reader).GetConstantDoubleValue(reader).Value; - - case HandleType.ConstantReferenceValue: - return null; - - default: - throw new BadImageFormatException(); - } - } - } -} diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/EcmaFormatEnumInfoImplementation.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/EcmaFormatEnumInfoImplementation.cs deleted file mode 100644 index 74e53edb2b..0000000000 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/EcmaFormatEnumInfoImplementation.cs +++ /dev/null @@ -1,106 +0,0 @@ -// 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.Reflection; -using System.Collections.Generic; - -using Internal.Runtime.Augments; - -using Internal.Reflection.Core; -using Internal.Reflection.Core.Execution; - -using System.Reflection.Metadata; - -namespace Internal.Reflection.Execution -{ - internal sealed class EcmaFormatEnumInfoImplementation : EnumInfoImplementation - { - public EcmaFormatEnumInfoImplementation(Type enumType, MetadataReader reader, TypeDefinitionHandle typeDefinitionHandle) : base(enumType) - { - _reader = reader; - _typeDefinition = reader.GetTypeDefinition(typeDefinitionHandle); - } - - protected sealed override KeyValuePair[] ReadNamesAndValues() - { - LowLevelList> namesAndUnboxedValues = new LowLevelList>(); - MetadataReader reader = _reader; - foreach (FieldDefinitionHandle fieldHandle in _typeDefinition.GetFields()) - { - FieldDefinition field = reader.GetFieldDefinition(fieldHandle); - if (0 != (field.Attributes & FieldAttributes.Static)) - { - String name = reader.GetString(field.Name); - - if ((field.Attributes & FieldAttributes.HasDefault) != FieldAttributes.HasDefault) - throw new BadImageFormatException(); - - ConstantHandle valueHandle = field.GetDefaultValue(); - - ulong ulValue = ReadUnboxedEnumValue(reader, valueHandle); - namesAndUnboxedValues.Add(new KeyValuePair(name, ulValue)); - } - } - - return namesAndUnboxedValues.ToArray(); - } - - - // - // This returns the underlying enum values as "ulong" regardless of the actual underlying type. Signed integral types - // get sign-extended into the 64-bit value, unsigned types get zero-extended. - // - public static ulong ReadUnboxedEnumValue(MetadataReader metadataReader, ConstantHandle constantHandle) - { - if (constantHandle.IsNil) - throw new BadImageFormatException(); - - Constant constantValue = metadataReader.GetConstant(constantHandle); - - if (constantValue.Value.IsNil) - throw new BadImageFormatException(); - - BlobReader reader = metadataReader.GetBlobReader(constantValue.Value); - - switch (constantValue.TypeCode) - { - case ConstantTypeCode.Boolean: - return reader.ReadBoolean() ? 1UL : 0UL;; - - case ConstantTypeCode.Char: - return (ulong)(long)reader.ReadChar(); - - case ConstantTypeCode.SByte: - return (ulong)(long)reader.ReadSByte(); - - case ConstantTypeCode.Int16: - return (ulong)(long)reader.ReadInt16(); - - case ConstantTypeCode.Int32: - return (ulong)(long)reader.ReadInt32(); - - case ConstantTypeCode.Int64: - return (ulong)(long)reader.ReadInt64(); - - case ConstantTypeCode.Byte: - return (ulong)(long)reader.ReadByte(); - - case ConstantTypeCode.UInt16: - return (ulong)(long)reader.ReadUInt16(); - - case ConstantTypeCode.UInt32: - return (ulong)(long)reader.ReadUInt32(); - - case ConstantTypeCode.UInt64: - return (ulong)(long)reader.ReadUInt64(); - } - - throw new BadImageFormatException(); - } - - private readonly MetadataReader _reader; - private readonly TypeDefinition _typeDefinition; - } -} diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/EnumInfoImplementation.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/EnumInfoImplementation.cs deleted file mode 100644 index e52452507f..0000000000 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/EnumInfoImplementation.cs +++ /dev/null @@ -1,207 +0,0 @@ -// 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 global::System; -using global::System.Reflection; -using global::System.Collections.Generic; - -using global::Internal.Runtime.Augments; - -using global::Internal.Reflection.Core; -using global::Internal.Reflection.Core.Execution; - -using global::Internal.Metadata.NativeFormat; - -namespace Internal.Reflection.Execution -{ - internal abstract class EnumInfoImplementation : EnumInfo - { - protected EnumInfoImplementation(Type enumType) - { - _enumType = enumType; - } - - public sealed override Type UnderlyingType - { - get - { - return Enum.GetUnderlyingType(_enumType); - } - } - - public sealed override Array Values - { - get - { - if (_lazyValues == null) - { - RuntimeTypeHandle underlyingTypeHandle = Enum.GetUnderlyingType(_enumType).TypeHandle; - KeyValuePair[] namesAndValues = this.NamesAndValues; - int count = namesAndValues.Length; - if (underlyingTypeHandle.Equals(typeof(Boolean).TypeHandle)) - { - Boolean[] a = new Boolean[count]; - for (int i = 0; i < count; i++) - a[i] = namesAndValues[i].Value != 0 ? true : false; - _lazyValues = a; - } - else if (underlyingTypeHandle.Equals(typeof(Byte).TypeHandle)) - { - byte[] a = new byte[count]; - for (int i = 0; i < count; i++) - a[i] = unchecked((byte)(namesAndValues[i].Value)); - _lazyValues = a; - } - else if (underlyingTypeHandle.Equals(typeof(SByte).TypeHandle)) - { - sbyte[] a = new sbyte[count]; - for (int i = 0; i < count; i++) - a[i] = unchecked((sbyte)(namesAndValues[i].Value)); - _lazyValues = a; - } - else if (underlyingTypeHandle.Equals(typeof(UInt16).TypeHandle)) - { - ushort[] a = new ushort[count]; - for (int i = 0; i < count; i++) - a[i] = unchecked((ushort)(namesAndValues[i].Value)); - _lazyValues = a; - } - else if (underlyingTypeHandle.Equals(typeof(Int16).TypeHandle)) - { - short[] a = new short[count]; - for (int i = 0; i < count; i++) - a[i] = unchecked((short)(namesAndValues[i].Value)); - _lazyValues = a; - } - else if (underlyingTypeHandle.Equals(typeof(Char).TypeHandle)) - { - char[] a = new char[count]; - for (int i = 0; i < count; i++) - a[i] = unchecked((char)(namesAndValues[i].Value)); - _lazyValues = a; - } - else if (underlyingTypeHandle.Equals(typeof(UInt32).TypeHandle)) - { - uint[] a = new uint[count]; - for (int i = 0; i < count; i++) - a[i] = unchecked((uint)(namesAndValues[i].Value)); - _lazyValues = a; - } - else if (underlyingTypeHandle.Equals(typeof(Int32).TypeHandle)) - { - int[] a = new int[count]; - for (int i = 0; i < count; i++) - a[i] = unchecked((int)(namesAndValues[i].Value)); - _lazyValues = a; - } - else if (underlyingTypeHandle.Equals(typeof(UInt64).TypeHandle)) - { - ulong[] a = new ulong[count]; - for (int i = 0; i < count; i++) - a[i] = unchecked((ulong)(namesAndValues[i].Value)); - _lazyValues = a; - } - else if (underlyingTypeHandle.Equals(typeof(Int64).TypeHandle)) - { - long[] a = new long[count]; - for (int i = 0; i < count; i++) - a[i] = unchecked((long)(namesAndValues[i].Value)); - _lazyValues = a; - } - else - { - throw new NotSupportedException(); - } - } - return _lazyValues; - } - } - - protected abstract KeyValuePair[] ReadNamesAndValues(); - - // - // This returns the underlying enum values as "ulong" regardless of the actual underlying type. We first do a value-preserving - // cast to long, then sort it as a ulong. - // - public sealed override KeyValuePair[] NamesAndValues - { - get - { - if (_lazyNamesAndValues == null) - { - KeyValuePair[] sortedNamesAndUnboxedValues = ReadNamesAndValues(); - Array.Sort>(sortedNamesAndUnboxedValues, new NamesAndValueComparer()); - _lazyNamesAndValues = sortedNamesAndUnboxedValues; - } - return _lazyNamesAndValues; - } - } - - public sealed override bool HasFlagsAttribute - { - get - { - EnumInfoFlags flags = this.Flags; - return 0 != (flags & EnumInfoFlags.HasFlagsAttribute); - } - } - - // - // Sort comparer for NamesAndValues - // - private sealed class NamesAndValueComparer : IComparer> - { - public int Compare(KeyValuePair kv1, KeyValuePair kv2) - { - ulong x = kv1.Value; - ulong y = kv2.Value; - if (x < y) - return -1; - else if (x > y) - return 1; - else - return 0; - } - } - - private Type _enumType; - private volatile Array _lazyValues; - private volatile KeyValuePair[] _lazyNamesAndValues; - - private EnumInfoFlags Flags - { - get - { - if (_lazyEnumInfoFlags == 0) - { - EnumInfoFlags flags = EnumInfoFlags.Computed; - - Type flagsAttributeType = typeof(FlagsAttribute); - foreach (CustomAttributeData cad in _enumType.CustomAttributes) - { - if (cad.AttributeType.Equals(flagsAttributeType)) - { - flags |= EnumInfoFlags.HasFlagsAttribute; - break; - } - } - - _lazyEnumInfoFlags = flags; - } - return _lazyEnumInfoFlags; - } - } - - - - [Flags] - private enum EnumInfoFlags : uint - { - Computed = 0x00000001, // always set (to distinguish between computed and not computed) - HasFlagsAttribute = 0x00000002, - } - - private volatile EnumInfoFlags _lazyEnumInfoFlags; - } -} diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Interop.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Interop.cs index 0fb7029d99..a8f668627b 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Interop.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Interop.cs @@ -16,7 +16,7 @@ namespace Internal.Reflection.Execution { public sealed override bool IsCOMObject(Type type) { - return McgMarshal.IsCOMObject(type); + return McgMarshal.IsComObject(type); } } } diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.ManifestResources.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.ManifestResources.cs index 04c04f4cf0..5353283dc7 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.ManifestResources.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.ManifestResources.cs @@ -7,19 +7,13 @@ extern alias System_Private_CoreLib; using System; using System.IO; using System.Reflection; -using System.Diagnostics; using System.Collections.Generic; -using System.Runtime.InteropServices; using Internal.Runtime; using Internal.Runtime.Augments; using Internal.Runtime.TypeLoader; -using Internal.Metadata.NativeFormat; - -using Internal.Reflection.Core; using Internal.Reflection.Core.Execution; -using Internal.Reflection.Execution.MethodInvokers; using Internal.NativeFormat; namespace Internal.Reflection.Execution @@ -168,7 +162,11 @@ namespace Internal.Reflection.Execution String pathToRunningExe = RuntimeAugments.TryGetFullPathToMainApplication(); String directoryContainingRunningExe = System_Private_CoreLib::System.IO.Path.GetDirectoryName(pathToRunningExe); String fullName = System_Private_CoreLib::System.IO.Path.Combine(directoryContainingRunningExe, name); - return (Stream)RuntimeAugments.OpenFileIfExists(fullName); + + if (RuntimeAugments.FileExists(fullName)) + return new FileStream(fullName, FileMode.Open, FileAccess.Read, FileShare.Read); + else + return null; } /// diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs index c87ae2814e..cd873da358 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs @@ -10,20 +10,19 @@ using global::Internal.Runtime.Augments; using global::Internal.Runtime.CompilerServices; using global::Internal.Runtime.TypeLoader; -using global::Internal.Reflection.Core; using global::Internal.Reflection.Core.Execution; using global::Internal.Reflection.Execution.MethodInvokers; using global::Internal.Reflection.Execution.FieldAccessors; using global::Internal.Metadata.NativeFormat; -using global::System.Runtime.CompilerServices; using global::System.Runtime.InteropServices; using global::Internal.Runtime; using global::Internal.NativeFormat; using System.Reflection.Runtime.General; +using System.Threading; using CanonicalFormKind = global::Internal.TypeSystem.CanonicalFormKind; @@ -126,9 +125,14 @@ namespace Internal.Reflection.Execution { // For generic types, use the generic type definition runtimeTypeHandle = GetTypeDefinition(runtimeTypeHandle); - var moduleHandle = RuntimeAugments.GetModuleFromTypeHandle(runtimeTypeHandle); - NativeFormatModuleInfo module = ModuleList.Instance.GetModuleInfoByHandle(moduleHandle); + + //make sure the module is actually NativeFormatModuleInfo, if the module + //doesnt have reflection enabled it wont be a NativeFormatModuleInfo + if (!(ModuleList.Instance.TryGetModuleInfoByHandle(moduleHandle, out ModuleInfo untypedModuleInfo) && (untypedModuleInfo is NativeFormatModuleInfo module))) + { + return true; + } NativeReader blockedReflectionReader = GetNativeReaderForBlob(module, ReflectionMapBlob.BlockReflectionTypeMap); NativeParser blockedReflectionParser = new NativeParser(blockedReflectionReader, 0); @@ -360,37 +364,6 @@ namespace Internal.Reflection.Execution public sealed override MethodInvoker TryGetMethodInvoker(RuntimeTypeHandle declaringTypeHandle, QMethodDefinition methodHandle, RuntimeTypeHandle[] genericMethodTypeArgumentHandles) { - if (RuntimeAugments.IsNullable(declaringTypeHandle)) - return new NullableInstanceMethodInvoker(methodHandle.NativeFormatReader, methodHandle.NativeFormatHandle, declaringTypeHandle, null); - else if (declaringTypeHandle.Equals(typeof(String).TypeHandle)) - { - MetadataReader reader = methodHandle.NativeFormatReader; - MethodHandle nativeFormatHandle = methodHandle.NativeFormatHandle; - - Method method = nativeFormatHandle.GetMethod(reader); - MethodAttributes methodAttributes = method.Flags; - if (((method.Flags & MethodAttributes.MemberAccessMask) == MethodAttributes.Public) && - ((method.Flags & MethodAttributes.SpecialName) == MethodAttributes.SpecialName) && - (method.Name.GetConstantStringValue(reader).Value == ".ctor")) - { - return new StringConstructorMethodInvoker(reader, nativeFormatHandle); - } - } - else if (declaringTypeHandle.Equals(typeof(IntPtr).TypeHandle) || declaringTypeHandle.Equals(typeof(UIntPtr).TypeHandle)) - { - MetadataReader reader = methodHandle.NativeFormatReader; - MethodHandle nativeFormatHandle = methodHandle.NativeFormatHandle; - - Method method = nativeFormatHandle.GetMethod(reader); - MethodAttributes methodAttributes = method.Flags; - if (((method.Flags & MethodAttributes.MemberAccessMask) == MethodAttributes.Public) && - ((method.Flags & MethodAttributes.SpecialName) == MethodAttributes.SpecialName) && - (method.Name.GetConstantStringValue(reader).Value == ".ctor")) - { - return new IntPtrConstructorMethodInvoker(reader, nativeFormatHandle); - } - } - MethodBase methodInfo = ReflectionCoreExecution.ExecutionDomain.GetMethod(declaringTypeHandle, methodHandle, genericMethodTypeArgumentHandles); // Validate constraints first. This is potentially useless work if the method already exists, but it prevents bad @@ -700,10 +673,29 @@ namespace Internal.Reflection.Execution Debug.Assert(parameterTypeHandles.Length == byRefParameters.Length && byRefParameters.Length == forcedByRefParameters.Length); - bool isMethodOnStructure = RuntimeAugments.IsValueType(declaringType); + ThunkKind thunkKind; + if (methodBase.IsGenericMethod) + { + thunkKind = CallConverterThunk.ThunkKind.StandardToGenericInstantiating; + } + else if (RuntimeAugments.IsValueType(declaringType)) + { + // Unboxing instantiating stub + if (dictionary == IntPtr.Zero) + { + Debug.Assert(!methodBase.IsStatic); + thunkKind = CallConverterThunk.ThunkKind.StandardToGeneric; + } + else + thunkKind = CallConverterThunk.ThunkKind.StandardToGenericInstantiating; + } + else + { + thunkKind = CallConverterThunk.ThunkKind.StandardToGenericInstantiatingIfNotHasThis; + } return CallConverterThunk.MakeThunk( - (methodBase.IsGenericMethod || isMethodOnStructure ? ThunkKind.StandardToGenericInstantiating : ThunkKind.StandardToGenericInstantiatingIfNotHasThis), + thunkKind, methodEntrypoint, dictionary, !methodBase.IsStatic, @@ -713,7 +705,10 @@ namespace Internal.Reflection.Execution } else { - return FunctionPointerOps.GetGenericMethodFunctionPointer(methodEntrypoint, dictionary); + if (dictionary == IntPtr.Zero) + return methodEntrypoint; + else + return FunctionPointerOps.GetGenericMethodFunctionPointer(methodEntrypoint, dictionary); } } @@ -828,11 +823,21 @@ namespace Internal.Reflection.Execution // ldftn reverse lookup hash. Must be cleared and reset if the module list changes. (All sets to // this variable must happen under a lock) - private volatile KeyValuePair[] _ldftnReverseLookup = null; + private volatile KeyValuePair[] _ldftnReverseLookup_InvokeMap = null; + private volatile KeyValuePair[] _ldftnReverseLookup_ExactInstantiations = null; + private Func _computeLdFtnLookupInvokeMapInvokeMap = ComputeLdftnReverseLookup_InvokeMap; + private Func _computeLdFtnLookupExactInstantiations = ComputeLdftnReverseLookup_ExactInstantiations; - private KeyValuePair[] GetLdFtnReverseLookups() + /// + /// Initialize a lookup array of module to function pointer/parser offset pair arrays. Do so in a manner that will allow + /// future work which will invalidate the cache (by setting it to null) + /// + /// pointer to static which holds cache value. This is treated as a volatile variable + /// + /// + private KeyValuePair[] GetLdFtnReverseLookups_Helper(ref KeyValuePair[] ldftnReverseLookupStatic, Func lookupComputer) { - KeyValuePair[] ldFtnReverseLookup = _ldftnReverseLookup; + KeyValuePair[] ldFtnReverseLookup = Volatile.Read(ref ldftnReverseLookupStatic); if (ldFtnReverseLookup != null) return ldFtnReverseLookup; @@ -840,7 +845,7 @@ namespace Internal.Reflection.Execution { lock (this) { - ldFtnReverseLookup = _ldftnReverseLookup; + ldFtnReverseLookup = Volatile.Read(ref ldftnReverseLookupStatic); // double checked lock, safe due to use of volatile on s_ldftnReverseHashes if (ldFtnReverseLookup != null) @@ -857,30 +862,49 @@ namespace Internal.Reflection.Execution ldFtnReverseLookup = new KeyValuePair[size]; int index = 0; + bool restart = false; foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules()) { // If the module list changes during execution of this code, rebuild from scratch if (index >= ldFtnReverseLookup.Length) - continue; + { + restart = true; + break; + } - ldFtnReverseLookup[index] = new KeyValuePair(module, ComputeLdftnReverseLookupLookup(module)); + ldFtnReverseLookup[index] = new KeyValuePair(module, lookupComputer(module)); index++; } + if (restart) + continue; + // unless we need to repeat the module enumeration, only execute the body of this while loop once. break; } - _ldftnReverseLookup = ldFtnReverseLookup; + Volatile.Write(ref ldftnReverseLookupStatic, ldFtnReverseLookup); return ldFtnReverseLookup; } } } - internal unsafe bool TryGetMethodForOriginalLdFtnResult(IntPtr originalLdFtnResult, ref RuntimeTypeHandle declaringTypeHandle, out QMethodDefinition methodHandle, out RuntimeTypeHandle[] genericMethodTypeArgumentHandles) + private KeyValuePair[] GetLdFtnReverseLookups_InvokeMap() + { +#pragma warning disable 0420 // GetLdFtnReverseLookups_Helper treats its first parameter as volatile by using explicit Volatile operations + return GetLdFtnReverseLookups_Helper(ref _ldftnReverseLookup_InvokeMap, _computeLdFtnLookupInvokeMapInvokeMap); +#pragma warning restore 0420 + } + + private KeyValuePair[] GetLdFtnReverseLookups_ExactInstantations() + { +#pragma warning disable 0420 // GetLdFtnReverseLookups_Helper treats its first parameter as volatile by using explicit Volatile operations + return GetLdFtnReverseLookups_Helper(ref _ldftnReverseLookup_ExactInstantiations, _computeLdFtnLookupExactInstantiations); +#pragma warning restore 0420 + } + + internal unsafe void GetFunctionPointerAndInstantiationArgumentForOriginalLdFtnResult(IntPtr originalLdFtnResult, out IntPtr canonOriginalLdFtnResult, out IntPtr instantiationArgument) { - IntPtr canonOriginalLdFtnResult; - IntPtr instantiationArgument; if (FunctionPointerOps.IsGenericMethodPointer(originalLdFtnResult)) { GenericMethodDescriptor* realTargetData = FunctionPointerOps.ConvertToGenericDescriptor(originalLdFtnResult); @@ -903,12 +927,28 @@ namespace Internal.Reflection.Execution } else { - canonOriginalLdFtnResult = RuntimeAugments.GetCodeTarget(originalLdFtnResult); - instantiationArgument = IntPtr.Zero; + // The thunk could have been created by the TypeLoader as a dictionary slot for USG code + if (!CallConverterThunk.TryGetCallConversionTargetPointerAndInstantiatingArg(originalLdFtnResult, out canonOriginalLdFtnResult, out instantiationArgument)) + { + canonOriginalLdFtnResult = RuntimeAugments.GetCodeTarget(originalLdFtnResult); + instantiationArgument = IntPtr.Zero; + } } } + } - foreach (KeyValuePair perModuleLookup in GetLdFtnReverseLookups()) + internal bool TryGetMethodForOriginalLdFtnResult(IntPtr originalLdFtnResult, ref RuntimeTypeHandle declaringTypeHandle, out QMethodDefinition methodHandle, out RuntimeTypeHandle[] genericMethodTypeArgumentHandles) + { + GetFunctionPointerAndInstantiationArgumentForOriginalLdFtnResult(originalLdFtnResult, out IntPtr canonOriginalLdFtnResult, out IntPtr instantiationArgument); + + // Search TemplateMethodMap + if ((instantiationArgument != IntPtr.Zero) && TryGetMethodForOriginalLdFtnResult_GenericMethodWithInstantiationArgument(instantiationArgument, ref declaringTypeHandle, out methodHandle, out genericMethodTypeArgumentHandles)) + { + return true; + } + + // Search ExactInstantiationsMap + foreach (KeyValuePair perModuleLookup in GetLdFtnReverseLookups_ExactInstantations()) { int startIndex; int endIndex; @@ -918,7 +958,24 @@ namespace Internal.Reflection.Execution for (int curIndex = startIndex; curIndex <= endIndex; curIndex++) { uint parserOffset = perModuleLookup.Value.Data[curIndex].Offset; - if (TryGetMethodForOriginalLdFtnResult_Inner(perModuleLookup.Key, canonOriginalLdFtnResult, instantiationArgument, parserOffset, ref declaringTypeHandle, out methodHandle, out genericMethodTypeArgumentHandles)) + if (TryGetMethodForOriginalLdFtnResult_ExactInstantiation_Inner(perModuleLookup.Key, canonOriginalLdFtnResult, instantiationArgument, parserOffset, ref declaringTypeHandle, out methodHandle, out genericMethodTypeArgumentHandles)) + return true; + } + } + } + + // Search InvokeMap + foreach (KeyValuePair perModuleLookup in GetLdFtnReverseLookups_InvokeMap()) + { + int startIndex; + int endIndex; + + if (perModuleLookup.Value.TryGetOffsetsRange(canonOriginalLdFtnResult, out startIndex, out endIndex)) + { + for (int curIndex = startIndex; curIndex <= endIndex; curIndex++) + { + uint parserOffset = perModuleLookup.Value.Data[curIndex].Offset; + if (TryGetMethodForOriginalLdFtnResult_InvokeMap_Inner(perModuleLookup.Key, canonOriginalLdFtnResult, instantiationArgument, parserOffset, ref declaringTypeHandle, out methodHandle, out genericMethodTypeArgumentHandles)) return true; } } @@ -929,7 +986,7 @@ namespace Internal.Reflection.Execution return false; } - private FunctionPointersToOffsets ComputeLdftnReverseLookupLookup(NativeFormatModuleInfo mappingTableModule) + private static FunctionPointersToOffsets ComputeLdftnReverseLookup_InvokeMap(NativeFormatModuleInfo mappingTableModule) { FunctionPointersToOffsets functionPointerToOffsetInInvokeMap = new FunctionPointersToOffsets(); @@ -965,6 +1022,23 @@ namespace Internal.Reflection.Execution IntPtr entryMethodEntrypoint = externalReferences.GetFunctionPointerFromIndex(entryParser.GetUnsigned()); functionPointers.Add(new FunctionPointerOffsetPair(entryMethodEntrypoint, parserOffset)); + + // Add resolved stub targets to the reverse LdFtn lookup map for the purpose of reflection-based + // stack trace resolution - the reverse LdFtn lookup internally used by the reflection + // method resolution will work off an IP address on the stack which is an address + // within the actual method, not the stub. + IntPtr targetAddress = RuntimeAugments.GetCodeTarget(entryMethodEntrypoint); + if (targetAddress != IntPtr.Zero && targetAddress != entryMethodEntrypoint) + { + functionPointers.Add(new FunctionPointerOffsetPair(targetAddress, parserOffset)); + } + IntPtr targetAddress2; + if (TypeLoaderEnvironment.TryGetTargetOfUnboxingAndInstantiatingStub(entryMethodEntrypoint, out targetAddress2) && + targetAddress2 != entryMethodEntrypoint && + targetAddress2 != targetAddress) + { + functionPointers.Add(new FunctionPointerOffsetPair(targetAddress2, parserOffset)); + } } functionPointerToOffsetInInvokeMap.Data = functionPointers.ToArray(); @@ -973,7 +1047,7 @@ namespace Internal.Reflection.Execution return functionPointerToOffsetInInvokeMap; } - private unsafe bool TryGetMethodForOriginalLdFtnResult_Inner(NativeFormatModuleInfo mappingTableModule, IntPtr canonOriginalLdFtnResult, IntPtr instantiationArgument, uint parserOffset, ref RuntimeTypeHandle declaringTypeHandle, out QMethodDefinition methodHandle, out RuntimeTypeHandle[] genericMethodTypeArgumentHandles) + private unsafe bool TryGetMethodForOriginalLdFtnResult_InvokeMap_Inner(NativeFormatModuleInfo mappingTableModule, IntPtr canonOriginalLdFtnResult, IntPtr instantiationArgument, uint parserOffset, ref RuntimeTypeHandle declaringTypeHandle, out QMethodDefinition methodHandle, out RuntimeTypeHandle[] genericMethodTypeArgumentHandles) { methodHandle = default(QMethodDefinition); genericMethodTypeArgumentHandles = null; @@ -1008,7 +1082,13 @@ namespace Internal.Reflection.Execution if ((entryFlags & InvokeTableFlags.NeedsParameterInterpretation) == 0) entryParser.GetUnsigned(); // skip dynamic invoke cookie - Debug.Assert(entryMethodEntrypoint == canonOriginalLdFtnResult); +#if DEBUG + IntPtr targetAddress; + Debug.Assert(entryMethodEntrypoint == canonOriginalLdFtnResult || + RuntimeAugments.GetCodeTarget(entryMethodEntrypoint) == canonOriginalLdFtnResult || + TypeLoaderEnvironment.TryGetTargetOfUnboxingAndInstantiatingStub(entryMethodEntrypoint, out targetAddress) && + targetAddress == canonOriginalLdFtnResult); +#endif if ((entryFlags & InvokeTableFlags.RequiresInstArg) == 0 && declaringTypeHandle.IsNull()) declaringTypeHandle = externalReferences.GetRuntimeTypeHandleFromIndex(entryDeclaringTypeRaw); @@ -1068,6 +1148,119 @@ namespace Internal.Reflection.Execution return true; } + private static FunctionPointersToOffsets ComputeLdftnReverseLookup_ExactInstantiations(NativeFormatModuleInfo mappingTableModule) + { + FunctionPointersToOffsets functionPointerToOffsetInInvokeMap = new FunctionPointersToOffsets(); + + NativeReader methodTemplateMapReader; + if (!TryGetNativeReaderForBlob(mappingTableModule, ReflectionMapBlob.ExactMethodInstantiationsHashtable, out methodTemplateMapReader)) + { + return functionPointerToOffsetInInvokeMap; + } + + ExternalReferencesTable externalReferences = default(ExternalReferencesTable); + externalReferences.InitializeNativeReferences(mappingTableModule); + + NativeParser methodTemplateMapParser = new NativeParser(methodTemplateMapReader, 0); + NativeHashtable invokeHashtable = new NativeHashtable(methodTemplateMapParser); + + LowLevelList functionPointers = new LowLevelList(); + + var lookup = invokeHashtable.EnumerateAllEntries(); + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + uint parserOffset = entryParser.Offset; + + // Declaring Handle + entryParser.GetUnsigned(); + + // NameAndSig + entryParser.GetUnsigned(); + + // generic method arity + int parsedArity = (int)entryParser.GetSequenceCount(); + + for (int i = 0; i < parsedArity; i++) + { + entryParser.GetUnsigned(); + } + + IntPtr functionPointer = externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned()); + functionPointers.Add(new FunctionPointerOffsetPair(functionPointer, parserOffset)); + } + + functionPointerToOffsetInInvokeMap.Data = functionPointers.ToArray(); + Array.Sort(functionPointerToOffsetInInvokeMap.Data); + + return functionPointerToOffsetInInvokeMap; + } + + private unsafe bool TryGetMethodForOriginalLdFtnResult_ExactInstantiation_Inner(NativeFormatModuleInfo mappingTableModule, IntPtr canonOriginalLdFtnResult, IntPtr instantiationArgument, uint parserOffset, ref RuntimeTypeHandle declaringTypeHandle, out QMethodDefinition methodHandle, out RuntimeTypeHandle[] genericMethodTypeArgumentHandles) + { + methodHandle = default(QMethodDefinition); + genericMethodTypeArgumentHandles = null; + + if (instantiationArgument != IntPtr.Zero) + return false; + + NativeReader invokeMapReader; + if (!TryGetNativeReaderForBlob(mappingTableModule, ReflectionMapBlob.ExactMethodInstantiationsHashtable, out invokeMapReader)) + { + // This should have succeeded otherwise, how did we get a parser offset as an input parameter? + Debug.Assert(false); + return false; + } + + ExternalReferencesTable externalReferences = default(ExternalReferencesTable); + externalReferences.InitializeNativeReferences(mappingTableModule); + + NativeParser entryParser = new NativeParser(invokeMapReader, parserOffset); + + declaringTypeHandle = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + + // Hash table names / sigs are indirected through to the native layout info + MethodNameAndSignature nameAndSignature; + if (!TypeLoaderEnvironment.Instance.TryGetMethodNameAndSignatureFromNativeLayoutOffset(mappingTableModule.Handle, entryParser.GetUnsigned(), out nameAndSignature)) + return false; + + int parsedArity = (int)entryParser.GetSequenceCount(); + genericMethodTypeArgumentHandles = new RuntimeTypeHandle[parsedArity]; + + for (int i = 0; i < parsedArity; i++) + { + genericMethodTypeArgumentHandles[i] = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + } + + IntPtr functionPointer = externalReferences.GetIntPtrFromIndex(entryParser.GetUnsigned()); + if (functionPointer != canonOriginalLdFtnResult) + return false; + + if (TypeLoaderEnvironment.Instance.TryGetMetadataForTypeMethodNameAndSignature(declaringTypeHandle, nameAndSignature, out methodHandle)) + { + return true; + } + + return false; + } + + private unsafe bool TryGetMethodForOriginalLdFtnResult_GenericMethodWithInstantiationArgument(IntPtr instantiationArgument, ref RuntimeTypeHandle declaringTypeHandle, out QMethodDefinition methodHandle, out RuntimeTypeHandle[] genericMethodTypeArgumentHandles) + { + MethodNameAndSignature nameAndSig; + bool success = TypeLoaderEnvironment.Instance.TryGetGenericMethodComponents(instantiationArgument, out declaringTypeHandle, out nameAndSig, out genericMethodTypeArgumentHandles); + if (success) + { + if (TypeLoaderEnvironment.Instance.TryGetMetadataForTypeMethodNameAndSignature(declaringTypeHandle, nameAndSig, out methodHandle)) + { + return true; + } + } + + methodHandle = default(QMethodDefinition); + + return false; + } + public sealed override FieldAccessor TryGetFieldAccessor( MetadataReader metadataReader, RuntimeTypeHandle declaringTypeHandle, @@ -1094,8 +1287,11 @@ namespace Internal.Reflection.Execution return RuntimeAugments.IsValueType(fieldTypeHandle) ? (FieldAccessor)new ValueTypeFieldAccessorForInstanceFields( fieldAccessMetadata.Offset + fieldOffsetDelta, declaringTypeHandle, fieldTypeHandle) : - (FieldAccessor)new ReferenceTypeFieldAccessorForInstanceFields( - fieldAccessMetadata.Offset + fieldOffsetDelta, declaringTypeHandle, fieldTypeHandle); + RuntimeAugments.IsUnmanagedPointerType(fieldTypeHandle) ? + (FieldAccessor)new PointerTypeFieldAccessorForInstanceFields( + fieldAccessMetadata.Offset + fieldOffsetDelta, declaringTypeHandle, fieldTypeHandle) : + (FieldAccessor)new ReferenceTypeFieldAccessorForInstanceFields( + fieldAccessMetadata.Offset + fieldOffsetDelta, declaringTypeHandle, fieldTypeHandle); } case FieldTableFlags.Static: @@ -1117,7 +1313,12 @@ namespace Internal.Reflection.Execution else { Debug.Assert((fieldAccessMetadata.Flags & FieldTableFlags.IsUniversalCanonicalEntry) == 0); -#if CORERT +#if PROJECTN + // The fieldAccessMetadata.Offset value is not really a field offset, but a static field RVA. We'll use the + // field's address as a 'staticsBase', and just use a field offset of zero. + fieldOffset = 0; + staticsBase = TypeLoaderEnvironment.RvaToNonGenericStaticFieldAddress(fieldAccessMetadata.MappingTableModule, fieldAccessMetadata.Offset); +#else if (isGcStatic) { fieldOffset = fieldAccessMetadata.Offset; @@ -1130,11 +1331,6 @@ namespace Internal.Reflection.Execution fieldOffset = 0; staticsBase = fieldAccessMetadata.Cookie; } -#else - // The fieldAccessMetadata.Offset value is not really a field offset, but a static field RVA. We'll use the - // field's address as a 'staticsBase', and just use a field offset of zero. - fieldOffset = 0; - staticsBase = TypeLoaderEnvironment.RvaToNonGenericStaticFieldAddress(fieldAccessMetadata.MappingTableModule, fieldAccessMetadata.Offset); #endif } @@ -1142,29 +1338,33 @@ namespace Internal.Reflection.Execution return RuntimeAugments.IsValueType(fieldTypeHandle) ? (FieldAccessor)new ValueTypeFieldAccessorForStaticFields(cctorContext, staticsBase, fieldOffset, isGcStatic, fieldTypeHandle) : - (FieldAccessor)new ReferenceTypeFieldAccessorForStaticFields(cctorContext, staticsBase, fieldOffset, isGcStatic, fieldTypeHandle); + RuntimeAugments.IsUnmanagedPointerType(fieldTypeHandle) ? + (FieldAccessor)new PointerTypeFieldAccessorForStaticFields(cctorContext, staticsBase, fieldOffset, isGcStatic, fieldTypeHandle) : + (FieldAccessor)new ReferenceTypeFieldAccessorForStaticFields(cctorContext, staticsBase, fieldOffset, isGcStatic, fieldTypeHandle); } case FieldTableFlags.ThreadStatic: - if (fieldAccessMetadata.Cookie == IntPtr.Zero) { return RuntimeAugments.IsValueType(fieldTypeHandle) ? - (FieldAccessor)new ValueTypeFieldAccessorForUniversalThreadStaticFields( - TryGetStaticClassConstructionContext(declaringTypeHandle), - declaringTypeHandle, + (FieldAccessor)new ValueTypeFieldAccessorForThreadStaticFields( + TryGetStaticClassConstructionContext(declaringTypeHandle), + declaringTypeHandle, + (int)fieldAccessMetadata.Cookie, fieldAccessMetadata.Offset, fieldTypeHandle) : - (FieldAccessor)new ReferenceTypeFieldAccessorForUniversalThreadStaticFields( - TryGetStaticClassConstructionContext(declaringTypeHandle), - declaringTypeHandle, - fieldAccessMetadata.Offset, - fieldTypeHandle); - } - else - { - return RuntimeAugments.IsValueType(fieldTypeHandle) ? - (FieldAccessor)new ValueTypeFieldAccessorForThreadStaticFields(TryGetStaticClassConstructionContext(declaringTypeHandle), declaringTypeHandle, fieldAccessMetadata.Cookie, fieldTypeHandle) : - (FieldAccessor)new ReferenceTypeFieldAccessorForThreadStaticFields(TryGetStaticClassConstructionContext(declaringTypeHandle), declaringTypeHandle, fieldAccessMetadata.Cookie, fieldTypeHandle); + RuntimeAugments.IsUnmanagedPointerType(fieldTypeHandle) ? + (FieldAccessor)new PointerTypeFieldAccessorForThreadStaticFields( + TryGetStaticClassConstructionContext(declaringTypeHandle), + declaringTypeHandle, + (int)fieldAccessMetadata.Cookie, + fieldAccessMetadata.Offset, + fieldTypeHandle) : + (FieldAccessor)new ReferenceTypeFieldAccessorForThreadStaticFields( + TryGetStaticClassConstructionContext(declaringTypeHandle), + declaringTypeHandle, + (int)fieldAccessMetadata.Cookie, + fieldAccessMetadata.Offset, + fieldTypeHandle); } } @@ -1241,7 +1441,7 @@ namespace Internal.Reflection.Execution /// /// Locate the static constructor context given the runtime type handle (EEType) for the type in question. /// - /// EEtype of the type to look up + /// EEType of the type to look up internal unsafe IntPtr TryGetStaticClassConstructionContext(RuntimeTypeHandle typeHandle) { return TypeLoaderEnvironment.TryGetStaticClassConstructionContext(typeHandle); diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Runtime.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Runtime.cs index 8aeb9f6fe0..e9a7cbf736 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Runtime.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Runtime.cs @@ -3,17 +3,12 @@ // See the LICENSE file in the project root for more information. using System; -using System.Reflection; using System.Collections.Generic; -using System.Runtime.InteropServices; using Internal.Runtime.Augments; using Internal.Reflection.Core.Execution; using Internal.Reflection.Execution.FieldAccessors; -using Internal.Reflection.Execution.MethodInvokers; - -using Internal.Metadata.NativeFormat; namespace Internal.Reflection.Execution { @@ -61,91 +56,11 @@ namespace Internal.Reflection.Execution return RuntimeAugments.TryGetImplementedInterfaces(typeHandle); } - public sealed override MethodInvoker GetSyntheticMethodInvoker(RuntimeTypeHandle thisType, RuntimeTypeHandle[] parameterTypes, InvokerOptions options, Func invoker) - { - return new SyntheticMethodInvoker(thisType, parameterTypes, options, invoker); - } - public sealed override string GetLastResortString(RuntimeTypeHandle typeHandle) { return RuntimeAugments.GetLastResortString(typeHandle); } - //============================================================================================== - // Default Value support. - //============================================================================================== - public sealed override bool GetDefaultValueIfAny(MetadataReader reader, ParameterHandle parameterHandle, Type declaredType, IEnumerable customAttributes, out Object defaultValue) - { - Parameter parameter = parameterHandle.GetParameter(reader); - return DefaultValueParser.GetDefaultValueIfAny(DefaultValueParser.MemberType.Parameter, reader, parameter.DefaultValue, declaredType, customAttributes, out defaultValue); - } - - public sealed override bool GetDefaultValueIfAny(MetadataReader reader, FieldHandle fieldHandle, Type declaredType, IEnumerable customAttributes, out Object defaultValue) - { - Field field = fieldHandle.GetField(reader); - return DefaultValueParser.GetDefaultValueIfAny(DefaultValueParser.MemberType.Field, reader, field.DefaultValue, declaredType, customAttributes, out defaultValue); - } - - public sealed override bool GetDefaultValueIfAny(MetadataReader reader, PropertyHandle propertyHandle, Type declaredType, IEnumerable customAttributes, out Object defaultValue) - { - Property property = propertyHandle.GetProperty(reader); - return DefaultValueParser.GetDefaultValueIfAny(DefaultValueParser.MemberType.Property, reader, property.DefaultValue, declaredType, customAttributes, out defaultValue); - } - - //============================================================================================== - // Pseudo Custom Attributes - //============================================================================================== - public sealed override IEnumerable GetPseudoCustomAttributes(MetadataReader reader, ScopeDefinitionHandle scopeDefinitionHandle) - { - return Empty.Enumerable; - } - - public sealed override IEnumerable GetPseudoCustomAttributes(MetadataReader reader, TypeDefinitionHandle typeDefinitionHandle) - { - TypeAttributes attributes = typeDefinitionHandle.GetTypeDefinition(reader).Flags; - if (0 != (attributes & TypeAttributes.Import)) - yield return ReflectionCoreExecution.ExecutionDomain.GetCustomAttributeData(typeof(ComImportAttribute), null, null); - } - - public sealed override IEnumerable GetPseudoCustomAttributes(MetadataReader reader, MethodHandle methodHandle, TypeDefinitionHandle declaringTypeHandle) - { - MethodImplAttributes implAttributes = methodHandle.GetMethod(reader).ImplFlags; - if (0 != (implAttributes & MethodImplAttributes.PreserveSig)) - yield return ReflectionCoreExecution.ExecutionDomain.GetCustomAttributeData(typeof(PreserveSigAttribute), null, null); - } - - public sealed override IEnumerable GetPseudoCustomAttributes(MetadataReader reader, ParameterHandle parameterHandle, MethodHandle declaringMethodHandle) - { - ParameterAttributes attributes = parameterHandle.GetParameter(reader).Flags; - if (0 != (attributes & ParameterAttributes.In)) - yield return ReflectionCoreExecution.ExecutionDomain.GetCustomAttributeData(typeof(InAttribute), null, null); - if (0 != (attributes & ParameterAttributes.Out)) - yield return ReflectionCoreExecution.ExecutionDomain.GetCustomAttributeData(typeof(OutAttribute), null, null); - if (0 != (attributes & ParameterAttributes.Optional)) - yield return ReflectionCoreExecution.ExecutionDomain.GetCustomAttributeData(typeof(OptionalAttribute), null, null); - } - - public sealed override IEnumerable GetPseudoCustomAttributes(MetadataReader reader, FieldHandle fieldHandle, TypeDefinitionHandle declaringTypeHandle) - { - TypeAttributes layoutKind = declaringTypeHandle.GetTypeDefinition(reader).Flags & TypeAttributes.LayoutMask; - if (layoutKind == TypeAttributes.ExplicitLayout) - { - int offset = (int)(fieldHandle.GetField(reader).Offset); - CustomAttributeTypedArgument offsetArgument = new CustomAttributeTypedArgument(typeof(Int32), offset); - yield return ReflectionCoreExecution.ExecutionDomain.GetCustomAttributeData(typeof(FieldOffsetAttribute), new CustomAttributeTypedArgument[] { offsetArgument }, null); - } - } - - public sealed override IEnumerable GetPseudoCustomAttributes(MetadataReader reader, PropertyHandle propertyHandle, TypeDefinitionHandle declaringTypeHandle) - { - return Empty.Enumerable; - } - - public sealed override IEnumerable GetPseudoCustomAttributes(MetadataReader reader, EventHandle eventHandle, TypeDefinitionHandle declaringTypeHandle) - { - return Empty.Enumerable; - } - //============================================================================================== // Miscellaneous //============================================================================================== diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.cs index cf59bf959f..552b22098f 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.cs @@ -2,20 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using global::System; -using global::System.Reflection; -using global::System.Collections.Generic; -using global::System.Diagnostics; - -using global::Internal.Runtime.Augments; - -using global::Internal.Reflection.Core; using global::Internal.Reflection.Core.Execution; -using global::Internal.Metadata.NativeFormat; - -using ReflectionMapBlob = Internal.Runtime.ReflectionMapBlob; - namespace Internal.Reflection.Execution { internal sealed partial class ExecutionEnvironmentImplementation : ExecutionEnvironment diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/InstanceFieldAccessor.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/InstanceFieldAccessor.cs index e822c0ac77..07bbf52f35 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/InstanceFieldAccessor.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/InstanceFieldAccessor.cs @@ -2,24 +2,21 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using global::System; -using global::System.Threading; -using global::System.Reflection; -using global::System.Diagnostics; -using global::System.Collections.Generic; +using System; +using System.Reflection; -using global::Internal.Runtime.Augments; -using global::Internal.Reflection.Execution; -using global::Internal.Reflection.Core.Execution; +using Internal.Runtime.Augments; +using Internal.Reflection.Core.Execution; namespace Internal.Reflection.Execution.FieldAccessors { internal abstract class InstanceFieldAccessor : FieldAccessor { - public InstanceFieldAccessor(RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle fieldTypeHandle) + public InstanceFieldAccessor(RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle fieldTypeHandle, int offsetPlusHeader) { this.DeclaringTypeHandle = declaringTypeHandle; this.FieldTypeHandle = fieldTypeHandle; + this.OffsetPlusHeader = offsetPlusHeader; } public abstract override int Offset { get; } @@ -97,7 +94,8 @@ namespace Internal.Reflection.Execution.FieldAccessors protected abstract Object UncheckedGetField(Object obj); protected abstract void UncheckedSetField(Object obj, Object value); - protected RuntimeTypeHandle DeclaringTypeHandle { get; private set; } - protected RuntimeTypeHandle FieldTypeHandle { get; private set; } + protected int OffsetPlusHeader { get; } + protected RuntimeTypeHandle DeclaringTypeHandle { get; } + protected RuntimeTypeHandle FieldTypeHandle { get; } } } diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/LiteralFieldAccessor.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/LiteralFieldAccessor.cs index 4ea218494e..b9a6979ba9 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/LiteralFieldAccessor.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/LiteralFieldAccessor.cs @@ -4,7 +4,6 @@ using System; using System.Reflection; -using System.Diagnostics; namespace Internal.Reflection.Execution.FieldAccessors { diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/PointerTypeFieldAccessorForInstanceFields.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/PointerTypeFieldAccessorForInstanceFields.cs new file mode 100644 index 0000000000..ae6716f003 --- /dev/null +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/PointerTypeFieldAccessorForInstanceFields.cs @@ -0,0 +1,39 @@ +// 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 Internal.Runtime.Augments; + +namespace Internal.Reflection.Execution.FieldAccessors +{ + internal sealed class PointerTypeFieldAccessorForInstanceFields : InstanceFieldAccessor + { + public PointerTypeFieldAccessorForInstanceFields(int offsetPlusHeader, RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle fieldTypeHandle) + : base(declaringTypeHandle, fieldTypeHandle, offsetPlusHeader) + { + } + + public sealed override int Offset => OffsetPlusHeader - RuntimeAugments.ObjectHeaderSize; + + protected sealed override Object UncheckedGetField(Object obj) + { + return RuntimeAugments.LoadPointerTypeField(obj, OffsetPlusHeader, this.FieldTypeHandle); + } + + protected sealed override object UncheckedGetFieldDirectFromValueType(TypedReference typedReference) + { + return RuntimeAugments.LoadPointerTypeFieldValueFromValueType(typedReference, this.Offset, this.FieldTypeHandle); + } + + protected sealed override void UncheckedSetField(Object obj, Object value) + { + RuntimeAugments.StoreValueTypeField(obj, OffsetPlusHeader, value, typeof(IntPtr).TypeHandle); + } + + protected sealed override void UncheckedSetFieldDirectIntoValueType(TypedReference typedReference, object value) + { + RuntimeAugments.StoreValueTypeFieldValueIntoValueType(typedReference, this.Offset, value, typeof(IntPtr).TypeHandle); + } + } +} diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/PointerTypeFieldAccessorForStaticFields.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/PointerTypeFieldAccessorForStaticFields.cs new file mode 100644 index 0000000000..7f1efc19f2 --- /dev/null +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/PointerTypeFieldAccessorForStaticFields.cs @@ -0,0 +1,46 @@ +// 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 Internal.Runtime.Augments; + +namespace Internal.Reflection.Execution.FieldAccessors +{ + internal sealed class PointerTypeFieldAccessorForStaticFields : RegularStaticFieldAccessor + { + public PointerTypeFieldAccessorForStaticFields(IntPtr cctorContext, IntPtr staticsBase, int fieldOffset, bool isGcStatic, RuntimeTypeHandle fieldTypeHandle) + : base(cctorContext, staticsBase, fieldOffset, isGcStatic, fieldTypeHandle) + { + } + + unsafe protected sealed override Object GetFieldBypassCctor() + { +#if !PROJECTN + if (IsGcStatic) + { + // The _staticsBase variable points to a GC handle, which points at the GC statics base of the type. + // We need to perform a double indirection in a GC-safe manner. + object gcStaticsRegion = RuntimeAugments.LoadReferenceTypeField(*(IntPtr*)StaticsBase); + return RuntimeAugments.LoadPointerTypeField(gcStaticsRegion, FieldOffset, FieldTypeHandle); + } +#endif + return RuntimeAugments.LoadPointerTypeField(StaticsBase + FieldOffset, FieldTypeHandle); + } + + unsafe protected sealed override void UncheckedSetFieldBypassCctor(Object value) + { +#if !PROJECTN + if (IsGcStatic) + { + // The _staticsBase variable points to a GC handle, which points at the GC statics base of the type. + // We need to perform a double indirection in a GC-safe manner. + object gcStaticsRegion = RuntimeAugments.LoadReferenceTypeField(*(IntPtr*)StaticsBase); + RuntimeAugments.StoreValueTypeField(gcStaticsRegion, FieldOffset, value, typeof(IntPtr).TypeHandle); + return; + } +#endif + RuntimeAugments.StoreValueTypeField(StaticsBase + FieldOffset, value, typeof(IntPtr).TypeHandle); + } + } +} diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/PointerTypeFieldAccessorForThreadStaticFields.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/PointerTypeFieldAccessorForThreadStaticFields.cs new file mode 100644 index 0000000000..83c274334d --- /dev/null +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/PointerTypeFieldAccessorForThreadStaticFields.cs @@ -0,0 +1,29 @@ +// 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 Internal.Runtime.Augments; + +namespace Internal.Reflection.Execution.FieldAccessors +{ + internal sealed class PointerTypeFieldAccessorForThreadStaticFields : ThreadStaticFieldAccessor + { + public PointerTypeFieldAccessorForThreadStaticFields(IntPtr cctorContext, RuntimeTypeHandle declaringTypeHandle, int threadStaticsBlockOffset, int fieldOffset, RuntimeTypeHandle fieldTypeHandle) + : base(cctorContext, declaringTypeHandle, threadStaticsBlockOffset, fieldOffset, fieldTypeHandle) + { + } + + protected sealed override Object GetFieldBypassCctor() + { + IntPtr fieldAddress = RuntimeAugments.GetThreadStaticFieldAddress(DeclaringTypeHandle, ThreadStaticsBlockOffset, FieldOffset); + return RuntimeAugments.LoadPointerTypeField(fieldAddress, FieldTypeHandle); + } + + protected sealed override void UncheckedSetFieldBypassCctor(Object value) + { + IntPtr fieldAddress = RuntimeAugments.GetThreadStaticFieldAddress(DeclaringTypeHandle, ThreadStaticsBlockOffset, FieldOffset); + RuntimeAugments.StoreValueTypeField(fieldAddress, value, typeof(IntPtr).TypeHandle); + } + } +} diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForInstanceFields.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForInstanceFields.cs index b43718a472..004db4b2d6 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForInstanceFields.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForInstanceFields.cs @@ -2,33 +2,23 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using global::System; -using global::System.Threading; -using global::System.Reflection; -using global::System.Diagnostics; -using global::System.Collections.Generic; - -using global::Internal.Runtime.Augments; -using global::Internal.Reflection.Execution; -using global::Internal.Reflection.Core.Execution; +using System; +using Internal.Runtime.Augments; namespace Internal.Reflection.Execution.FieldAccessors { internal sealed class ReferenceTypeFieldAccessorForInstanceFields : InstanceFieldAccessor { - private int _offset; - - public ReferenceTypeFieldAccessorForInstanceFields(int offset, RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle fieldTypeHandle) - : base(declaringTypeHandle, fieldTypeHandle) + public ReferenceTypeFieldAccessorForInstanceFields(int offsetPlusHeader, RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle fieldTypeHandle) + : base(declaringTypeHandle, fieldTypeHandle, offsetPlusHeader) { - _offset = offset; } - public sealed override int Offset => _offset - RuntimeAugments.ObjectHeaderSize; + public sealed override int Offset => OffsetPlusHeader - RuntimeAugments.ObjectHeaderSize; protected sealed override Object UncheckedGetField(Object obj) { - return RuntimeAugments.LoadReferenceTypeField(obj, _offset); + return RuntimeAugments.LoadReferenceTypeField(obj, OffsetPlusHeader); } protected sealed override object UncheckedGetFieldDirectFromValueType(TypedReference typedReference) @@ -38,7 +28,7 @@ namespace Internal.Reflection.Execution.FieldAccessors protected sealed override void UncheckedSetField(Object obj, Object value) { - RuntimeAugments.StoreReferenceTypeField(obj, _offset, value); + RuntimeAugments.StoreReferenceTypeField(obj, OffsetPlusHeader, value); } protected sealed override void UncheckedSetFieldDirectIntoValueType(TypedReference typedReference, object value) diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForStaticFields.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForStaticFields.cs index cc6f5082b7..5abb9ac7b5 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForStaticFields.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForStaticFields.cs @@ -2,57 +2,45 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using global::System; -using global::System.Reflection; -using global::System.Runtime.CompilerServices; -using global::System.Runtime.InteropServices; - -using global::Internal.Runtime.Augments; +using System; +using Internal.Runtime.Augments; namespace Internal.Reflection.Execution.FieldAccessors { - internal sealed class ReferenceTypeFieldAccessorForStaticFields : WritableStaticFieldAccessor + internal sealed class ReferenceTypeFieldAccessorForStaticFields : RegularStaticFieldAccessor { - private IntPtr _staticsBase; - private bool _isGcStaticsBase; - private int _fieldOffset; - public ReferenceTypeFieldAccessorForStaticFields(IntPtr cctorContext, IntPtr staticsBase, int fieldOffset, bool isGcStatic, RuntimeTypeHandle fieldTypeHandle) - : base(cctorContext, fieldTypeHandle) + : base(cctorContext, staticsBase, fieldOffset, isGcStatic, fieldTypeHandle) { - _staticsBase = staticsBase; - _isGcStaticsBase = isGcStatic; - _fieldOffset = fieldOffset; } unsafe protected sealed override Object GetFieldBypassCctor() { -#if CORERT - if (_isGcStaticsBase) +#if !PROJECTN + if (IsGcStatic) { // The _staticsBase variable points to a GC handle, which points at the GC statics base of the type. // We need to perform a double indirection in a GC-safe manner. - object gcStaticsRegion = RuntimeAugments.LoadReferenceTypeField(*(IntPtr*)_staticsBase); - return RuntimeAugments.LoadReferenceTypeField(gcStaticsRegion, _fieldOffset); + object gcStaticsRegion = RuntimeAugments.LoadReferenceTypeField(*(IntPtr*)StaticsBase); + return RuntimeAugments.LoadReferenceTypeField(gcStaticsRegion, FieldOffset); } #endif - return RuntimeAugments.LoadReferenceTypeField(_staticsBase + _fieldOffset); + return RuntimeAugments.LoadReferenceTypeField(StaticsBase + FieldOffset); } unsafe protected sealed override void UncheckedSetFieldBypassCctor(Object value) { - -#if CORERT - if (_isGcStaticsBase) +#if !PROJECTN + if (IsGcStatic) { // The _staticsBase variable points to a GC handle, which points at the GC statics base of the type. // We need to perform a double indirection in a GC-safe manner. - object gcStaticsRegion = RuntimeAugments.LoadReferenceTypeField(*(IntPtr*)_staticsBase); - RuntimeAugments.StoreReferenceTypeField(gcStaticsRegion, _fieldOffset, value); + object gcStaticsRegion = RuntimeAugments.LoadReferenceTypeField(*(IntPtr*)StaticsBase); + RuntimeAugments.StoreReferenceTypeField(gcStaticsRegion, FieldOffset, value); return; } #endif - RuntimeAugments.StoreReferenceTypeField(_staticsBase + _fieldOffset, value); + RuntimeAugments.StoreReferenceTypeField(StaticsBase + FieldOffset, value); } } } diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForThreadStaticFields.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForThreadStaticFields.cs index 8d6a7758d0..287fe7571b 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForThreadStaticFields.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForThreadStaticFields.cs @@ -2,39 +2,27 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using global::System; -using global::System.Threading; -using global::System.Reflection; -using global::System.Diagnostics; -using global::System.Collections.Generic; - -using global::Internal.Runtime.Augments; -using global::Internal.Reflection.Execution; -using global::Internal.Reflection.Core.Execution; +using System; +using Internal.Runtime.Augments; namespace Internal.Reflection.Execution.FieldAccessors { - internal sealed class ReferenceTypeFieldAccessorForThreadStaticFields : WritableStaticFieldAccessor + internal sealed class ReferenceTypeFieldAccessorForThreadStaticFields : ThreadStaticFieldAccessor { - private IntPtr _cookie; - private RuntimeTypeHandle _declaringTypeHandle; - - public ReferenceTypeFieldAccessorForThreadStaticFields(IntPtr cctorContext, RuntimeTypeHandle declaringTypeHandle, IntPtr cookie, RuntimeTypeHandle fieldTypeHandle) - : base(cctorContext, fieldTypeHandle) + public ReferenceTypeFieldAccessorForThreadStaticFields(IntPtr cctorContext, RuntimeTypeHandle declaringTypeHandle, int threadStaticsBlockOffset, int fieldOffset, RuntimeTypeHandle fieldTypeHandle) + : base(cctorContext, declaringTypeHandle, threadStaticsBlockOffset, fieldOffset, fieldTypeHandle) { - _cookie = cookie; - _declaringTypeHandle = declaringTypeHandle; } protected sealed override Object GetFieldBypassCctor() { - IntPtr fieldAddress = RuntimeAugments.GetThreadStaticFieldAddress(_declaringTypeHandle, _cookie); + IntPtr fieldAddress = RuntimeAugments.GetThreadStaticFieldAddress(DeclaringTypeHandle, ThreadStaticsBlockOffset, FieldOffset); return RuntimeAugments.LoadReferenceTypeField(fieldAddress); } protected sealed override void UncheckedSetFieldBypassCctor(Object value) { - IntPtr fieldAddress = RuntimeAugments.GetThreadStaticFieldAddress(_declaringTypeHandle, _cookie); + IntPtr fieldAddress = RuntimeAugments.GetThreadStaticFieldAddress(DeclaringTypeHandle, ThreadStaticsBlockOffset, FieldOffset); RuntimeAugments.StoreReferenceTypeField(fieldAddress, value); } } diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForUniversalThreadStaticFields.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForUniversalThreadStaticFields.cs deleted file mode 100644 index a273eaead3..0000000000 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForUniversalThreadStaticFields.cs +++ /dev/null @@ -1,43 +0,0 @@ -// 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 global::System; -using global::System.Threading; -using global::System.Reflection; -using global::System.Diagnostics; -using global::System.Collections.Generic; - -using global::Internal.Runtime.Augments; -using global::Internal.Reflection.Execution; -using global::Internal.Reflection.Core.Execution; - -namespace Internal.Reflection.Execution.FieldAccessors -{ - internal sealed class ReferenceTypeFieldAccessorForUniversalThreadStaticFields : WritableStaticFieldAccessor - { - private int _fieldOffset; - private RuntimeTypeHandle _declaringTypeHandle; - - public ReferenceTypeFieldAccessorForUniversalThreadStaticFields(IntPtr cctorContext, RuntimeTypeHandle declaringTypeHandle, int fieldOffset, RuntimeTypeHandle fieldTypeHandle) - : base(cctorContext, fieldTypeHandle) - { - _fieldOffset = fieldOffset; - _declaringTypeHandle = declaringTypeHandle; - } - - protected sealed override Object GetFieldBypassCctor() - { - IntPtr tlsFieldsStartAddress = RuntimeAugments.GetThreadStaticFieldAddress(_declaringTypeHandle, IntPtr.Zero); - IntPtr fieldAddress = tlsFieldsStartAddress + _fieldOffset; - return RuntimeAugments.LoadReferenceTypeField(fieldAddress); - } - - protected sealed override void UncheckedSetFieldBypassCctor(Object value) - { - IntPtr tlsFieldsStartAddress = RuntimeAugments.GetThreadStaticFieldAddress(_declaringTypeHandle, IntPtr.Zero); - IntPtr fieldAddress = tlsFieldsStartAddress + _fieldOffset; - RuntimeAugments.StoreReferenceTypeField(fieldAddress, value); - } - } -} diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/RegularStaticFieldAccessor.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/RegularStaticFieldAccessor.cs new file mode 100644 index 0000000000..d4aa41f8dc --- /dev/null +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/RegularStaticFieldAccessor.cs @@ -0,0 +1,23 @@ +// 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; + +namespace Internal.Reflection.Execution.FieldAccessors +{ + internal abstract class RegularStaticFieldAccessor : WritableStaticFieldAccessor + { + protected RegularStaticFieldAccessor(IntPtr cctorContext, IntPtr staticsBase, int fieldOffset, bool isGcStatic, RuntimeTypeHandle fieldTypeHandle) + : base(cctorContext, fieldTypeHandle) + { + StaticsBase = staticsBase; + IsGcStatic = isGcStatic; + FieldOffset = fieldOffset; + } + + protected IntPtr StaticsBase { get; } + protected bool IsGcStatic { get; } + protected int FieldOffset { get; } + } +} diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/StaticFieldAccessor.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/StaticFieldAccessor.cs index f70236cb62..6b9f7dbc14 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/StaticFieldAccessor.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/StaticFieldAccessor.cs @@ -2,21 +2,18 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using global::System; -using global::System.Threading; -using global::System.Reflection; -using global::System.Diagnostics; -using global::System.Collections.Generic; +using System; +using System.Reflection; +using System.Diagnostics; -using global::Internal.Runtime.Augments; -using global::Internal.Reflection.Execution; -using global::Internal.Reflection.Core.Execution; +using Internal.Runtime.Augments; +using Internal.Reflection.Core.Execution; namespace Internal.Reflection.Execution.FieldAccessors { internal abstract class StaticFieldAccessor : FieldAccessor { - protected RuntimeTypeHandle FieldTypeHandle { get; private set; } + protected RuntimeTypeHandle FieldTypeHandle { get; } private IntPtr _cctorContext; diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ThreadStaticFieldAccessor.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ThreadStaticFieldAccessor.cs new file mode 100644 index 0000000000..4378fc9eb4 --- /dev/null +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ThreadStaticFieldAccessor.cs @@ -0,0 +1,23 @@ +// 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; + +namespace Internal.Reflection.Execution.FieldAccessors +{ + internal abstract class ThreadStaticFieldAccessor : WritableStaticFieldAccessor + { + protected ThreadStaticFieldAccessor(IntPtr cctorContext, RuntimeTypeHandle declaringTypeHandle, int threadStaticsBlockOffset, int fieldOffset, RuntimeTypeHandle fieldTypeHandle) + : base(cctorContext, fieldTypeHandle) + { + ThreadStaticsBlockOffset = threadStaticsBlockOffset; + FieldOffset = fieldOffset; + DeclaringTypeHandle = declaringTypeHandle; + } + + protected int ThreadStaticsBlockOffset { get; } + protected int FieldOffset { get; } + protected RuntimeTypeHandle DeclaringTypeHandle { get; } + } +} diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForInstanceFields.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForInstanceFields.cs index 4e399de7bb..c3b5cff6d9 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForInstanceFields.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForInstanceFields.cs @@ -2,33 +2,23 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using global::System; -using global::System.Threading; -using global::System.Reflection; -using global::System.Diagnostics; -using global::System.Collections.Generic; - -using global::Internal.Runtime.Augments; -using global::Internal.Reflection.Execution; -using global::Internal.Reflection.Core.Execution; +using System; +using Internal.Runtime.Augments; namespace Internal.Reflection.Execution.FieldAccessors { internal sealed class ValueTypeFieldAccessorForInstanceFields : InstanceFieldAccessor { - private int _offset; - - public ValueTypeFieldAccessorForInstanceFields(int offset, RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle fieldTypeHandle) - : base(declaringTypeHandle, fieldTypeHandle) + public ValueTypeFieldAccessorForInstanceFields(int offsetPlusHeader, RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle fieldTypeHandle) + : base(declaringTypeHandle, fieldTypeHandle, offsetPlusHeader) { - _offset = offset; } - public sealed override int Offset => _offset - RuntimeAugments.ObjectHeaderSize; + public sealed override int Offset => OffsetPlusHeader - RuntimeAugments.ObjectHeaderSize; protected sealed override Object UncheckedGetField(Object obj) { - return RuntimeAugments.LoadValueTypeField(obj, _offset, this.FieldTypeHandle); + return RuntimeAugments.LoadValueTypeField(obj, OffsetPlusHeader, this.FieldTypeHandle); } protected sealed override object UncheckedGetFieldDirectFromValueType(TypedReference typedReference) @@ -38,7 +28,7 @@ namespace Internal.Reflection.Execution.FieldAccessors protected sealed override void UncheckedSetField(Object obj, Object value) { - RuntimeAugments.StoreValueTypeField(obj, _offset, value, this.FieldTypeHandle); + RuntimeAugments.StoreValueTypeField(obj, OffsetPlusHeader, value, this.FieldTypeHandle); } protected sealed override void UncheckedSetFieldDirectIntoValueType(TypedReference typedReference, object value) diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForStaticFields.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForStaticFields.cs index 7440c1f186..bb54d91f98 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForStaticFields.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForStaticFields.cs @@ -2,56 +2,45 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using global::System; -using global::System.Reflection; -using global::System.Runtime.CompilerServices; - -using global::Internal.Runtime.Augments; +using System; +using Internal.Runtime.Augments; namespace Internal.Reflection.Execution.FieldAccessors { - internal sealed class ValueTypeFieldAccessorForStaticFields : WritableStaticFieldAccessor + internal sealed class ValueTypeFieldAccessorForStaticFields : RegularStaticFieldAccessor { - private IntPtr _staticsBase; - private bool _isGcStaticsBase; - private int _fieldOffset; - public ValueTypeFieldAccessorForStaticFields(IntPtr cctorContext, IntPtr staticsBase, int fieldOffset, bool isGcStatic, RuntimeTypeHandle fieldTypeHandle) - : base(cctorContext, fieldTypeHandle) + : base(cctorContext, staticsBase, fieldOffset, isGcStatic, fieldTypeHandle) { - _staticsBase = staticsBase; - _isGcStaticsBase = isGcStatic; - _fieldOffset = fieldOffset; } unsafe protected sealed override Object GetFieldBypassCctor() { -#if CORERT - if (_isGcStaticsBase) +#if !PROJECTN + if (IsGcStatic) { // The _staticsBase variable points to a GC handle, which points at the GC statics base of the type. // We need to perform a double indirection in a GC-safe manner. - object gcStaticsRegion = RuntimeAugments.LoadReferenceTypeField(*(IntPtr*)_staticsBase); - return RuntimeAugments.LoadValueTypeField(gcStaticsRegion, _fieldOffset, FieldTypeHandle); + object gcStaticsRegion = RuntimeAugments.LoadReferenceTypeField(*(IntPtr*)StaticsBase); + return RuntimeAugments.LoadValueTypeField(gcStaticsRegion, FieldOffset, FieldTypeHandle); } #endif - return RuntimeAugments.LoadValueTypeField(_staticsBase + _fieldOffset, FieldTypeHandle); + return RuntimeAugments.LoadValueTypeField(StaticsBase + FieldOffset, FieldTypeHandle); } unsafe protected sealed override void UncheckedSetFieldBypassCctor(Object value) { - -#if CORERT - if (_isGcStaticsBase) +#if !PROJECTN + if (IsGcStatic) { // The _staticsBase variable points to a GC handle, which points at the GC statics base of the type. // We need to perform a double indirection in a GC-safe manner. - object gcStaticsRegion = RuntimeAugments.LoadReferenceTypeField(*(IntPtr*)_staticsBase); - RuntimeAugments.StoreValueTypeField(gcStaticsRegion, _fieldOffset, value, FieldTypeHandle); + object gcStaticsRegion = RuntimeAugments.LoadReferenceTypeField(*(IntPtr*)StaticsBase); + RuntimeAugments.StoreValueTypeField(gcStaticsRegion, FieldOffset, value, FieldTypeHandle); return; } #endif - RuntimeAugments.StoreValueTypeField(_staticsBase + _fieldOffset, value, FieldTypeHandle); + RuntimeAugments.StoreValueTypeField(StaticsBase + FieldOffset, value, FieldTypeHandle); } } } diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForThreadStaticFields.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForThreadStaticFields.cs index 3c77fba295..6f98e043e2 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForThreadStaticFields.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForThreadStaticFields.cs @@ -2,41 +2,27 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using global::System; -using global::System.Threading; -using global::System.Reflection; -using global::System.Diagnostics; -using global::System.Collections.Generic; - -using global::Internal.Runtime.Augments; -using global::Internal.Reflection.Execution; -using global::Internal.Reflection.Core.Execution; - -using global::Internal.Metadata.NativeFormat; +using System; +using Internal.Runtime.Augments; namespace Internal.Reflection.Execution.FieldAccessors { - internal sealed class ValueTypeFieldAccessorForThreadStaticFields : WritableStaticFieldAccessor + internal sealed class ValueTypeFieldAccessorForThreadStaticFields : ThreadStaticFieldAccessor { - private IntPtr _cookie; - private RuntimeTypeHandle _declaringTypeHandle; - - public ValueTypeFieldAccessorForThreadStaticFields(IntPtr cctorContext, RuntimeTypeHandle declaringTypeHandle, IntPtr cookie, RuntimeTypeHandle fieldTypeHandle) - : base(cctorContext, fieldTypeHandle) + public ValueTypeFieldAccessorForThreadStaticFields(IntPtr cctorContext, RuntimeTypeHandle declaringTypeHandle, int threadStaticsBlockOffset, int fieldOffset, RuntimeTypeHandle fieldTypeHandle) + : base(cctorContext, declaringTypeHandle, threadStaticsBlockOffset, fieldOffset, fieldTypeHandle) { - _cookie = cookie; - _declaringTypeHandle = declaringTypeHandle; } protected sealed override Object GetFieldBypassCctor() { - IntPtr fieldAddress = RuntimeAugments.GetThreadStaticFieldAddress(_declaringTypeHandle, _cookie); + IntPtr fieldAddress = RuntimeAugments.GetThreadStaticFieldAddress(DeclaringTypeHandle, ThreadStaticsBlockOffset, FieldOffset); return RuntimeAugments.LoadValueTypeField(fieldAddress, FieldTypeHandle); } protected sealed override void UncheckedSetFieldBypassCctor(Object value) { - IntPtr fieldAddress = RuntimeAugments.GetThreadStaticFieldAddress(_declaringTypeHandle, _cookie); + IntPtr fieldAddress = RuntimeAugments.GetThreadStaticFieldAddress(DeclaringTypeHandle, ThreadStaticsBlockOffset, FieldOffset); RuntimeAugments.StoreValueTypeField(fieldAddress, value, FieldTypeHandle); } } diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForUniversalThreadStaticFields.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForUniversalThreadStaticFields.cs deleted file mode 100644 index 916854c960..0000000000 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForUniversalThreadStaticFields.cs +++ /dev/null @@ -1,45 +0,0 @@ -// 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 global::System; -using global::System.Threading; -using global::System.Reflection; -using global::System.Diagnostics; -using global::System.Collections.Generic; - -using global::Internal.Runtime.Augments; -using global::Internal.Reflection.Execution; -using global::Internal.Reflection.Core.Execution; - -using global::Internal.Metadata.NativeFormat; - -namespace Internal.Reflection.Execution.FieldAccessors -{ - internal sealed class ValueTypeFieldAccessorForUniversalThreadStaticFields : WritableStaticFieldAccessor - { - private int _fieldOffset; - private RuntimeTypeHandle _declaringTypeHandle; - - public ValueTypeFieldAccessorForUniversalThreadStaticFields(IntPtr cctorContext, RuntimeTypeHandle declaringTypeHandle, int fieldOffset, RuntimeTypeHandle fieldTypeHandle) - : base(cctorContext, fieldTypeHandle) - { - _fieldOffset = fieldOffset; - _declaringTypeHandle = declaringTypeHandle; - } - - protected sealed override Object GetFieldBypassCctor() - { - IntPtr tlsFieldsStartAddress = RuntimeAugments.GetThreadStaticFieldAddress(_declaringTypeHandle, IntPtr.Zero); - IntPtr fieldAddress = tlsFieldsStartAddress + _fieldOffset; - return RuntimeAugments.LoadValueTypeField(fieldAddress, FieldTypeHandle); - } - - protected sealed override void UncheckedSetFieldBypassCctor(Object value) - { - IntPtr tlsFieldsStartAddress = RuntimeAugments.GetThreadStaticFieldAddress(_declaringTypeHandle, IntPtr.Zero); - IntPtr fieldAddress = tlsFieldsStartAddress + _fieldOffset; - RuntimeAugments.StoreValueTypeField(fieldAddress, value, FieldTypeHandle); - } - } -} diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MetadataReaderExtensions.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MetadataReaderExtensions.cs index 7b8969f806..0d631cb018 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MetadataReaderExtensions.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MetadataReaderExtensions.cs @@ -3,14 +3,9 @@ // See the LICENSE file in the project root for more information. using global::System; -using global::System.Reflection; -using global::System.Collections.Generic; using global::Internal.Metadata.NativeFormat; -using global::Internal.Reflection.Core; -using global::Internal.Reflection.Core.Execution; - using Debug = System.Diagnostics.Debug; namespace Internal.Reflection.Execution diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/InstanceMethodInvoker.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/InstanceMethodInvoker.cs index 208ca22fe8..99ee62ea77 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/InstanceMethodInvoker.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/InstanceMethodInvoker.cs @@ -28,9 +28,9 @@ namespace Internal.Reflection.Execution.MethodInvokers } [DebuggerGuidedStepThroughAttribute] - public sealed override Object Invoke(Object thisObject, Object[] arguments, BinderBundle binderBundle) + protected sealed override Object Invoke(Object thisObject, Object[] arguments, BinderBundle binderBundle, bool wrapInTargetInvocationException) { - MethodInvokerUtils.ValidateThis(thisObject, _declaringTypeHandle); + ValidateThis(thisObject, _declaringTypeHandle); object result = RuntimeAugments.CallDynamicInvokeMethod( thisObject, MethodInvokeInfo.LdFtnResult, @@ -40,6 +40,7 @@ namespace Internal.Reflection.Execution.MethodInvokers MethodInvokeInfo.MethodInfo, arguments, binderBundle, + wrapInTargetInvocationException: wrapInTargetInvocationException, invokeMethodHelperIsThisCall: false, methodToCallIsThisCall: true); System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/IntPtrConstructorMethodInvoker.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/IntPtrConstructorMethodInvoker.cs deleted file mode 100644 index 51e101e42d..0000000000 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/IntPtrConstructorMethodInvoker.cs +++ /dev/null @@ -1,162 +0,0 @@ -// 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 global::System; -using global::System.Threading; -using global::System.Reflection; -using global::System.Diagnostics; -using global::System.Collections.Generic; - -using global::Internal.Runtime.Augments; -using global::Internal.Reflection.Execution; -using global::Internal.Reflection.Core.Execution; -using global::Internal.LowLevelLinq; - -using global::Internal.Metadata.NativeFormat; - -namespace Internal.Reflection.Execution.MethodInvokers -{ - // - // IntPtr and UIntPtr constructors are intrinsics and require special casing to invoke. - // - internal sealed class IntPtrConstructorMethodInvoker : MethodInvoker - { - public IntPtrConstructorMethodInvoker(MetadataReader reader, MethodHandle methodHandle) - { - // Since we control the definition of System.IntPtr, we only do enough analysis of the signature to disambiguate the constructors we support. - _id = IntPtrConstructorId.None; - Method method = methodHandle.GetMethod(reader); - Handle[] parameterTypeSignatureHandles = method.Signature.GetMethodSignature(reader).Parameters.ToArray(); - if (parameterTypeSignatureHandles.Length == 1) - { - Handle parameterTypeHandle = parameterTypeSignatureHandles[0]; - - // If any parameter is a pointer type, bail as we don't support Invokes on pointers. - if (parameterTypeHandle.HandleType != HandleType.TypeDefinition) - throw new PlatformNotSupportedException(SR.PlatformNotSupported_PointerArguments); - - TypeDefinition typeDefinition = parameterTypeHandle.ToTypeDefinitionHandle(reader).GetTypeDefinition(reader); - String name = typeDefinition.Name.GetString(reader); - switch (name) - { - case "Int32": - _id = IntPtrConstructorId.Int32; - break; - - case "Int64": - _id = IntPtrConstructorId.Int64; - break; - - case "UInt32": - _id = IntPtrConstructorId.UInt32; - break; - - case "UInt64": - _id = IntPtrConstructorId.UInt64; - break; - - default: - break; - } - } - } - - public sealed override Object Invoke(Object thisObject, Object[] arguments, BinderBundle binderBundle) - { - switch (_id) - { - case IntPtrConstructorId.Int32: - { - CheckArgumentCount(arguments, 1); - Int32 value = (Int32)(RuntimeAugments.CheckArgument(arguments[0], typeof(Int32).TypeHandle, binderBundle)); - try - { - return new IntPtr(value); - } - catch (Exception inner) - { - throw new TargetInvocationException(inner); - } - } - - case IntPtrConstructorId.Int64: - { - CheckArgumentCount(arguments, 1); - Int64 value = (Int64)(RuntimeAugments.CheckArgument(arguments[0], typeof(Int64).TypeHandle, binderBundle)); - try - { - return new IntPtr(value); - } - catch (Exception inner) - { - throw new TargetInvocationException(inner); - } - } - - case IntPtrConstructorId.UInt32: - { - CheckArgumentCount(arguments, 1); - UInt32 value = (UInt32)(RuntimeAugments.CheckArgument(arguments[0], typeof(UInt32).TypeHandle, binderBundle)); - try - { - return new UIntPtr(value); - } - catch (Exception inner) - { - throw new TargetInvocationException(inner); - } - } - - case IntPtrConstructorId.UInt64: - { - CheckArgumentCount(arguments, 1); - UInt64 value = (UInt64)(RuntimeAugments.CheckArgument(arguments[0], typeof(UInt64).TypeHandle, binderBundle)); - try - { - return new UIntPtr(value); - } - catch (Exception inner) - { - throw new TargetInvocationException(inner); - } - } - - default: - throw new InvalidOperationException(); - } - } - - public sealed override Delegate CreateDelegate(RuntimeTypeHandle delegateType, Object target, bool isStatic, bool isVirtual, bool isOpen) - { - Debug.Assert(false, "This code should be unreachable. ConstructorInfos do not expose a CreateDelegate()."); - throw NotImplemented.ByDesign; - } - - public sealed override IntPtr LdFtnResult - { - get - { - throw new PlatformNotSupportedException(); - } - } - - private void CheckArgumentCount(Object[] arguments, int expected) - { - if (arguments.Length != expected) - throw new TargetParameterCountException(); - } - - private enum IntPtrConstructorId - { - Int32 = 0, - UInt32 = 1, - Int64 = 2, - UInt64 = 3, - None = -1, - } - - private IntPtrConstructorId _id; - } -} - diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/MethodInvokerUtils.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/MethodInvokerUtils.cs deleted file mode 100644 index 7ad3ae47cd..0000000000 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/MethodInvokerUtils.cs +++ /dev/null @@ -1,35 +0,0 @@ -// 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 global::System; -using global::System.Threading; -using global::System.Reflection; -using global::System.Diagnostics; -using global::System.Collections.Generic; -using global::System.Runtime.CompilerServices; -using global::Internal.Runtime.Augments; - -namespace Internal.Reflection.Execution.MethodInvokers -{ - internal static class MethodInvokerUtils - { - public static void ValidateThis(Object thisObject, RuntimeTypeHandle declaringTypeHandle) - { - if (thisObject == null) - throw new TargetException(SR.RFLCT_Targ_StatMethReqTarg); - RuntimeTypeHandle srcTypeHandle = thisObject.GetType().TypeHandle; - if (RuntimeAugments.IsAssignableFrom(declaringTypeHandle, srcTypeHandle)) - return; - - if (RuntimeAugments.IsInterface(declaringTypeHandle)) - { - if (RuntimeAugments.IsInstanceOfInterface(thisObject, declaringTypeHandle)) - return; - } - - throw new TargetException(SR.RFLCT_Targ_ITargMismatch); - } - } -} - diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/MethodInvokerWithMethodInvokeInfo.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/MethodInvokerWithMethodInvokeInfo.cs index c8fdfe788e..48d12ab251 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/MethodInvokerWithMethodInvokeInfo.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/MethodInvokerWithMethodInvokeInfo.cs @@ -3,13 +3,9 @@ // See the LICENSE file in the project root for more information. using global::System; -using global::System.Threading; using global::System.Reflection; -using global::System.Diagnostics; -using global::System.Collections.Generic; using global::Internal.Runtime.Augments; -using global::Internal.Reflection.Execution; using global::Internal.Reflection.Core.Execution; using global::Internal.Metadata.NativeFormat; diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/NullableInstanceMethodInvoker.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/NullableInstanceMethodInvoker.cs deleted file mode 100644 index be7f54a354..0000000000 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/NullableInstanceMethodInvoker.cs +++ /dev/null @@ -1,198 +0,0 @@ -// 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 global::System; -using global::System.Threading; -using global::System.Reflection; -using global::System.Diagnostics; -using global::System.Collections.Generic; - -using global::Internal.Runtime.Augments; -using global::Internal.Reflection.Execution; -using global::Internal.Reflection.Core.Execution; - -using global::Internal.Metadata.NativeFormat; - -namespace Internal.Reflection.Execution.MethodInvokers -{ - // - // Special invoker for Nullable instance methods. This is disgusting beyond the pale but what do you expect when it comes to a hack-farm like Nullable<>. - // - // We cannot lean on Delegate.DynamicInvoke() here as you cannot take or invoke a delegate to a Nullable method even on the desktop. - // - // The desktop *does* allow MethodInfo.Invoke() on a Nullable, however. - // - // We go one step further and allow invoking on a Nullable method where the "this" is null (i.e. a boxed version of a Nullable where - // HasValue is false.) - // - // We could go and generate IL helper methods (for each possible T) just for the Nullable case but given the small number of - // methods and their trivial semantics, it's just easier for us to emulate the Nullable behavior ourselves. - // - internal sealed class NullableInstanceMethodInvoker : MethodInvoker - { - public NullableInstanceMethodInvoker(MetadataReader reader, MethodHandle methodHandle, RuntimeTypeHandle nullableTypeHandle, MethodInvokeInfo methodInvokeInfo) - { - _id = NullableMethodId.None; - _nullableTypeHandle = nullableTypeHandle; - Method method = methodHandle.GetMethod(reader); - if (MethodAttributes.Public == (method.Flags & MethodAttributes.MemberAccessMask)) - { - // Note: Since we control the definition of Nullable<>, we're not checking signatures here. - String name = method.Name.GetConstantStringValue(reader).Value; - switch (name) - { - case "GetType": - _id = NullableMethodId.GetType; - break; - - case "ToString": - _id = NullableMethodId.ToString; - break; - - case "Equals": - _id = NullableMethodId.Equals; - break; - - case "GetHashCode": - _id = NullableMethodId.GetHashCode; - break; - - case ".ctor": - _id = NullableMethodId.Ctor; - break; - - case "get_HasValue": - _id = NullableMethodId.get_HasValue; - break; - - case "get_Value": - _id = NullableMethodId.get_Value; - break; - - case "GetValueOrDefault": - IEnumerator parameters = method.Signature.GetMethodSignature(reader).Parameters.GetEnumerator(); - if (parameters.MoveNext()) - _id = NullableMethodId.GetValueOrDefault_1; - else - _id = NullableMethodId.GetValueOrDefault_0; - break; - - default: - break; - } - } - } - - public sealed override Object Invoke(Object thisObject, Object[] arguments, BinderBundle binderBundle) - { - Object value = thisObject; - bool hasValue = (thisObject != null); - switch (_id) - { - case NullableMethodId.GetType: - CheckArgumentCount(arguments, 0); - return value.GetType(); // Note: this throws a NullReferenceException if hasValue is false. Well so does the desktop. - - case NullableMethodId.ToString: - CheckArgumentCount(arguments, 0); - return hasValue ? value.ToString() : ""; - - case NullableMethodId.Equals: - { - CheckArgumentCount(arguments, 1); - Object other = arguments[0]; - if (!hasValue) - return other == null; - if (other == null) - return false; - return value.Equals(other); - } - - case NullableMethodId.GetHashCode: - CheckArgumentCount(arguments, 0); - return hasValue ? value.GetHashCode() : 0; - - case NullableMethodId.Ctor: - { - // Constructor case is tricky. Our implementation of NewObject() does not accept Nullable's so this is one of those cases - // where the constructor is responsible for both the allocation and initialization. Fortunately, we only have to return the boxed - // version of Nullable which conveniently happens to be equal to the value we were passed in. - CheckArgumentCount(arguments, 1); - RuntimeTypeHandle theT = RuntimeAugments.GetNullableType(_nullableTypeHandle); - Object argument = RuntimeAugments.CheckArgument(arguments[0], theT, binderBundle); - return argument; - } - - case NullableMethodId.get_HasValue: - CheckArgumentCount(arguments, 0); - return hasValue; - - case NullableMethodId.get_Value: - CheckArgumentCount(arguments, 0); - if (!hasValue) - throw new InvalidOperationException(SR.InvalidOperation_NoValue); - return value; - - case NullableMethodId.GetValueOrDefault_0: - { - CheckArgumentCount(arguments, 0); - if (hasValue) - return value; - RuntimeTypeHandle theT = RuntimeAugments.GetNullableType(_nullableTypeHandle); - return RuntimeAugments.NewObject(theT); - } - - case NullableMethodId.GetValueOrDefault_1: - { - CheckArgumentCount(arguments, 1); - RuntimeTypeHandle theT = RuntimeAugments.GetNullableType(_nullableTypeHandle); - Object defaultValue = RuntimeAugments.CheckArgument(arguments[0], theT, binderBundle); - return hasValue ? value : defaultValue; - } - - default: - throw new InvalidOperationException(); - } - } - - public sealed override Delegate CreateDelegate(RuntimeTypeHandle delegateType, Object target, bool isStatic, bool isVirtual, bool isOpen) - { - // Desktop compat: MethodInfos to Nullable methods cannot be turned into delegates. - throw new ArgumentException(SR.Arg_DlgtTargMeth); - } - - public sealed override IntPtr LdFtnResult - { - get - { - throw new PlatformNotSupportedException(); - } - } - - private void CheckArgumentCount(Object[] arguments, int expected) - { - if (arguments.Length != expected) - throw new TargetParameterCountException(); - } - - private enum NullableMethodId - { - GetType = 0, - ToString = 1, - Equals = 2, - GetHashCode = 3, - Ctor = 4, - get_HasValue = 5, - get_Value = 6, - GetValueOrDefault_0 = 7, - GetValueOrDefault_1 = 8, - - None = -1, - } - - private readonly NullableMethodId _id; - private readonly RuntimeTypeHandle _nullableTypeHandle; // type handle of a specific instantiation of Nullable<> - } -} - diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/StaticMethodInvoker.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/StaticMethodInvoker.cs index 182e344550..ca348728cf 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/StaticMethodInvoker.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/StaticMethodInvoker.cs @@ -25,7 +25,7 @@ namespace Internal.Reflection.Execution.MethodInvokers } [DebuggerGuidedStepThroughAttribute] - public sealed override Object Invoke(Object thisObject, Object[] arguments, BinderBundle binderBundle) + protected sealed override Object Invoke(Object thisObject, Object[] arguments, BinderBundle binderBundle, bool wrapInTargetInvocationException) { object result = RuntimeAugments.CallDynamicInvokeMethod( thisObject, @@ -36,6 +36,7 @@ namespace Internal.Reflection.Execution.MethodInvokers MethodInvokeInfo.MethodInfo, arguments, binderBundle, + wrapInTargetInvocationException: wrapInTargetInvocationException, invokeMethodHelperIsThisCall: false, methodToCallIsThisCall: false); System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/StringConstructorMethodInvoker.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/StringConstructorMethodInvoker.cs deleted file mode 100644 index 866e624cce..0000000000 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/StringConstructorMethodInvoker.cs +++ /dev/null @@ -1,127 +0,0 @@ -// 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 global::System; -using global::System.Threading; -using global::System.Reflection; -using global::System.Diagnostics; -using global::System.Collections.Generic; - -using global::Internal.Runtime.Augments; -using global::Internal.Reflection.Execution; -using global::Internal.Reflection.Core.Execution; - -using global::Internal.Metadata.NativeFormat; - -namespace Internal.Reflection.Execution.MethodInvokers -{ - // - // String constructors require special treatment from the compiler, and hence from Reflection invoke as well. - // - internal sealed class StringConstructorMethodInvoker : MethodInvoker - { - public StringConstructorMethodInvoker(MetadataReader reader, MethodHandle methodHandle) - { - // Since we control the definition of System.String, we only do enough analysis of the signature to disambiguate the constructors we support. - _id = StringConstructorId.None; - Method method = methodHandle.GetMethod(reader); - int parameterCount = 0; - foreach (Handle parameterTypeSignatureHandle in method.Signature.GetMethodSignature(reader).Parameters) - { - // If any parameter is a pointer type, bail as we don't support Invokes on pointers. - if (parameterTypeSignatureHandle.HandleType == HandleType.TypeSpecification) - { - TypeSpecification typeSpecification = parameterTypeSignatureHandle.ToTypeSpecificationHandle(reader).GetTypeSpecification(reader); - if (typeSpecification.Signature.HandleType == HandleType.PointerSignature) - return; - } - parameterCount++; - } - - switch (parameterCount) - { - case 1: - _id = StringConstructorId.CharArray; - break; - - case 2: - _id = StringConstructorId.Char_Int; - break; - - case 3: - _id = StringConstructorId.CharArray_Int_Int; - break; - - default: - break; - } - } - - public sealed override Object Invoke(Object thisObject, Object[] arguments, BinderBundle binderBundle) - { - switch (_id) - { - case StringConstructorId.CharArray: - { - CheckArgumentCount(arguments, 1); - char[] value = (char[])(RuntimeAugments.CheckArgument(arguments[0], typeof(char[]).TypeHandle, binderBundle)); - return new String(value); - } - - case StringConstructorId.Char_Int: - { - CheckArgumentCount(arguments, 2); - char c = (char)(RuntimeAugments.CheckArgument(arguments[0], typeof(char).TypeHandle, binderBundle)); - int count = (int)(RuntimeAugments.CheckArgument(arguments[1], typeof(int).TypeHandle, binderBundle)); - return new String(c, count); - } - - case StringConstructorId.CharArray_Int_Int: - { - CheckArgumentCount(arguments, 3); - char[] value = (char[])(RuntimeAugments.CheckArgument(arguments[0], typeof(char[]).TypeHandle, binderBundle)); - int startIndex = (int)(RuntimeAugments.CheckArgument(arguments[1], typeof(int).TypeHandle, binderBundle)); - int length = (int)(RuntimeAugments.CheckArgument(arguments[2], typeof(int).TypeHandle, binderBundle)); - return new String(value, startIndex, length); - } - - default: - throw new InvalidOperationException(); - } - } - - public sealed override Delegate CreateDelegate(RuntimeTypeHandle delegateType, Object target, bool isStatic, bool isVirtual, bool isOpen) - { - Debug.Assert(false, "This code should be unreachable. ConstructorInfos do not expose a CreateDelegate()."); - throw NotImplemented.ByDesign; - } - - public sealed override IntPtr LdFtnResult - { - get - { - throw new PlatformNotSupportedException(); - } - } - - private void CheckArgumentCount(Object[] arguments, int expected) - { - if (arguments.Length != expected) - throw new TargetParameterCountException(); - } - - - private enum StringConstructorId - { - CharArray = 0, // String(char[]) - Char_Int = 1, // String(char, int) - CharArray_Int_Int = 2, // String(char[], int, int) - - None = -1, - } - - private StringConstructorId _id; - } -} - diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/SyntheticMethodInvoker.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/SyntheticMethodInvoker.cs deleted file mode 100644 index 5409e342f7..0000000000 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/SyntheticMethodInvoker.cs +++ /dev/null @@ -1,78 +0,0 @@ -// 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 global::System; -using global::System.Threading; -using global::System.Reflection; -using global::System.Diagnostics; -using global::System.Collections.Generic; - -using global::Internal.Runtime.Augments; -using global::Internal.Reflection.Execution; -using global::Internal.Reflection.Core.Execution; - -namespace Internal.Reflection.Execution.MethodInvokers -{ - // - // Implements Invoke() for Get/Set methods on array. - // - internal sealed class SyntheticMethodInvoker : MethodInvoker - { - public SyntheticMethodInvoker(RuntimeTypeHandle thisType, RuntimeTypeHandle[] parameterTypes, InvokerOptions options, Func invoker) - { - _invoker = invoker; - _options = options; - _thisType = thisType; - _parameterTypes = parameterTypes; - } - - public override Object Invoke(Object thisObject, Object[] arguments, BinderBundle binderBundle) - { - //@todo: This does not handle optional parameters (nor does it need to as today we're only using it for three synthetic array methods.) - if (!(thisObject == null && 0 != (_options & InvokerOptions.AllowNullThis))) - MethodInvokerUtils.ValidateThis(thisObject, _thisType); - if (arguments == null) - arguments = Array.Empty(); - if (arguments.Length != _parameterTypes.Length) - throw new TargetParameterCountException(); - Object[] convertedArguments = new Object[arguments.Length]; - for (int i = 0; i < arguments.Length; i++) - { - convertedArguments[i] = RuntimeAugments.CheckArgument(arguments[i], _parameterTypes[i], binderBundle); - } - Object result; - try - { - result = _invoker(thisObject, convertedArguments); - } - catch (Exception e) - { - if (0 != (_options & InvokerOptions.DontWrapException)) - throw; - else - throw new TargetInvocationException(e); - } - return result; - } - - public override Delegate CreateDelegate(RuntimeTypeHandle delegateType, Object target, bool isStatic, bool isVirtual, bool isOpen) - { - throw new PlatformNotSupportedException(); - } - - public sealed override IntPtr LdFtnResult - { - get - { - throw new PlatformNotSupportedException(); - } - } - - private InvokerOptions _options; - private Func _invoker; - private RuntimeTypeHandle _thisType; - private RuntimeTypeHandle[] _parameterTypes; - } -} - diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/VirtualMethodInvoker.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/VirtualMethodInvoker.cs index c48f2f8523..fc700494a1 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/VirtualMethodInvoker.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/VirtualMethodInvoker.cs @@ -52,9 +52,9 @@ namespace Internal.Reflection.Execution.MethodInvokers } [DebuggerGuidedStepThroughAttribute] - public sealed override Object Invoke(Object thisObject, Object[] arguments, BinderBundle binderBundle) + protected sealed override Object Invoke(Object thisObject, Object[] arguments, BinderBundle binderBundle, bool wrapInTargetInvocationException) { - MethodInvokerUtils.ValidateThis(thisObject, _declaringTypeHandle); + ValidateThis(thisObject, _declaringTypeHandle); IntPtr resolvedVirtual = OpenMethodResolver.ResolveMethod(MethodInvokeInfo.VirtualResolveData, thisObject); @@ -67,6 +67,7 @@ namespace Internal.Reflection.Execution.MethodInvokers MethodInvokeInfo.MethodInfo, arguments, binderBundle, + wrapInTargetInvocationException: wrapInTargetInvocationException, invokeMethodHelperIsThisCall: false, methodToCallIsThisCall: true); System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/NativeFormatEnumInfoImplementation.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/NativeFormatEnumInfoImplementation.cs deleted file mode 100644 index dcea964a65..0000000000 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/NativeFormatEnumInfoImplementation.cs +++ /dev/null @@ -1,122 +0,0 @@ -// 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 global::System; -using global::System.Reflection; -using global::System.Collections.Generic; - -using global::Internal.Runtime.Augments; - -using global::Internal.Reflection.Core; -using global::Internal.Reflection.Core.Execution; - -using global::Internal.Metadata.NativeFormat; - -namespace Internal.Reflection.Execution -{ - internal sealed class NativeFormatEnumInfoImplementation : EnumInfoImplementation - { - public NativeFormatEnumInfoImplementation(Type enumType, MetadataReader reader, TypeDefinitionHandle typeDefinitionHandle) : base(enumType) - { - _reader = reader; - _typeDefinition = typeDefinitionHandle.GetTypeDefinition(reader); - } - - protected sealed override KeyValuePair[] ReadNamesAndValues() - { - LowLevelList> namesAndUnboxedValues = new LowLevelList>(); - MetadataReader reader = _reader; - foreach (FieldHandle fieldHandle in _typeDefinition.Fields) - { - Field field = fieldHandle.GetField(reader); - if (0 != (field.Flags & FieldAttributes.Static)) - { - String name = field.Name.GetString(reader); - Handle valueHandle = field.DefaultValue; - ulong lValue = ReadUnboxedEnumValue(reader, valueHandle); - namesAndUnboxedValues.Add(new KeyValuePair(name, lValue)); - } - } - - return namesAndUnboxedValues.ToArray(); - } - - // - // This returns the underlying enum values as "ulong" regardless of the actual underlying type. Signed integral types - // get sign-extended into the 64-bit value, unsigned types get zero-extended. - // - private static ulong ReadUnboxedEnumValue(MetadataReader reader, Handle valueHandle) - { - HandleType handleType = valueHandle.HandleType; - switch (handleType) - { - case HandleType.ConstantBooleanValue: - { - bool v = valueHandle.ToConstantBooleanValueHandle(reader).GetConstantBooleanValue(reader).Value; - return v ? 1UL : 0UL; - } - - case HandleType.ConstantCharValue: - { - char v = valueHandle.ToConstantCharValueHandle(reader).GetConstantCharValue(reader).Value; - return (ulong)(long)v; - } - - case HandleType.ConstantByteValue: - { - byte v = valueHandle.ToConstantByteValueHandle(reader).GetConstantByteValue(reader).Value; - return (ulong)(long)v; - } - - case HandleType.ConstantSByteValue: - { - sbyte v = valueHandle.ToConstantSByteValueHandle(reader).GetConstantSByteValue(reader).Value; - return (ulong)(long)v; - } - - case HandleType.ConstantUInt16Value: - { - UInt16 v = valueHandle.ToConstantUInt16ValueHandle(reader).GetConstantUInt16Value(reader).Value; - return (ulong)(long)v; - } - - case HandleType.ConstantInt16Value: - { - Int16 v = valueHandle.ToConstantInt16ValueHandle(reader).GetConstantInt16Value(reader).Value; - return (ulong)(long)v; - } - - case HandleType.ConstantUInt32Value: - { - UInt32 v = valueHandle.ToConstantUInt32ValueHandle(reader).GetConstantUInt32Value(reader).Value; - return (ulong)(long)v; - } - - case HandleType.ConstantInt32Value: - { - Int32 v = valueHandle.ToConstantInt32ValueHandle(reader).GetConstantInt32Value(reader).Value; - return (ulong)(long)v; - } - - case HandleType.ConstantUInt64Value: - { - UInt64 v = valueHandle.ToConstantUInt64ValueHandle(reader).GetConstantUInt64Value(reader).Value; - return (ulong)(long)v; - } - - case HandleType.ConstantInt64Value: - { - Int64 v = valueHandle.ToConstantInt64ValueHandle(reader).GetConstantInt64Value(reader).Value; - return (ulong)(long)v; - } - - default: - throw new BadImageFormatException(); - } - } - - private readonly MetadataReader _reader; - private readonly TypeDefinition _typeDefinition; - } -} diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/PayForPlayExperience/DiagnosticMappingTables.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/PayForPlayExperience/DiagnosticMappingTables.cs index 3e62bced97..06e521f15a 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/PayForPlayExperience/DiagnosticMappingTables.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/PayForPlayExperience/DiagnosticMappingTables.cs @@ -3,8 +3,6 @@ // See the LICENSE file in the project root for more information. using global::System; -using global::System.Reflection; -using global::System.Diagnostics; using global::System.Text; using global::System.Collections.Generic; @@ -12,9 +10,6 @@ using global::Internal.Metadata.NativeFormat; using global::Internal.Runtime.Augments; -using global::Internal.Reflection.Core; -using global::Internal.Reflection.Core.Execution; - using System.Reflection.Runtime.General; namespace Internal.Reflection.Execution.PayForPlayExperience diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/PayForPlayExperience/MissingMetadataExceptionCreator.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/PayForPlayExperience/MissingMetadataExceptionCreator.cs index 1e2f27ab3f..8d0a8420d3 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/PayForPlayExperience/MissingMetadataExceptionCreator.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/PayForPlayExperience/MissingMetadataExceptionCreator.cs @@ -8,11 +8,8 @@ using global::System.Reflection; using global::System.Diagnostics; using global::System.Collections.Generic; -using global::Internal.Metadata.NativeFormat; - using global::Internal.Runtime.Augments; -using global::Internal.Reflection.Core; using global::Internal.Reflection.Core.Execution; namespace Internal.Reflection.Execution.PayForPlayExperience diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionDomainSetupImplementation.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionDomainSetupImplementation.cs index 0dea9563cc..ba673fb73c 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionDomainSetupImplementation.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionDomainSetupImplementation.cs @@ -4,12 +4,8 @@ using global::System; using global::System.Reflection; -using global::System.Collections.Generic; - -using global::Internal.Metadata.NativeFormat; using global::Internal.Reflection.Core; -using global::Internal.Reflection.Core.Execution; using global::Internal.Reflection.Execution.PayForPlayExperience; namespace Internal.Reflection.Execution @@ -52,9 +48,8 @@ namespace Internal.Reflection.Execution { String resourceName = SR.Object_NotInvokable; - if (pertainant is MethodBase) + if (pertainant is MethodBase methodBase) { - MethodBase methodBase = (MethodBase)pertainant; resourceName = methodBase.IsConstructedGenericMethod ? SR.MakeGenericMethod_NoMetadata : SR.Object_NotInvokable; if (methodBase is ConstructorInfo) { diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs index bcbf538259..d2135fd086 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs @@ -26,12 +26,15 @@ using global::System; using global::System.Collections.Generic; using global::System.Reflection; -using global::System.Runtime.CompilerServices; +using global::System.Reflection.Runtime.General; using global::Internal.Runtime.Augments; using global::Internal.Reflection.Core; using global::Internal.Reflection.Core.Execution; +using global::Internal.Metadata.NativeFormat; + +using Debug = System.Diagnostics.Debug; namespace Internal.Reflection.Execution { @@ -88,6 +91,36 @@ namespace Internal.Reflection.Execution return ReflectionCoreExecution.ExecutionDomain.GetType(typeName, assemblyResolver, typeResolver, throwOnError, ignoreCase, defaultAssemblies); } + public static bool TryGetMethodMetadataFromStartAddress(IntPtr methodStartAddress, out MetadataReader reader, out TypeDefinitionHandle typeHandle, out MethodHandle methodHandle) + { + reader = null; + typeHandle = default(TypeDefinitionHandle); + methodHandle = default(MethodHandle); + + RuntimeTypeHandle declaringTypeHandle = default(RuntimeTypeHandle); + if (!ExecutionEnvironment.TryGetMethodForOriginalLdFtnResult(methodStartAddress, + ref declaringTypeHandle, out QMethodDefinition qMethodDefinition, out _)) + return false; + + if (!qMethodDefinition.IsNativeFormatMetadataBased) + return false; + + if (RuntimeAugments.IsGenericType(declaringTypeHandle)) + declaringTypeHandle = RuntimeAugments.GetGenericDefinition(declaringTypeHandle); + + if (!ExecutionEnvironment.TryGetMetadataForNamedType(declaringTypeHandle, out QTypeDefinition qTypeDefinition)) + return false; + + Debug.Assert(qTypeDefinition.IsNativeFormatMetadataBased); + Debug.Assert(qTypeDefinition.NativeFormatReader == qMethodDefinition.NativeFormatReader); + + reader = qTypeDefinition.NativeFormatReader; + typeHandle = qTypeDefinition.NativeFormatHandle; + methodHandle = qMethodDefinition.NativeFormatHandle; + + return true; + } + internal static ExecutionEnvironmentImplementation ExecutionEnvironment { get; private set; } internal static IList DefaultAssemblyNamesForGetType; diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecutionDomainCallbacksImplementation.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecutionDomainCallbacksImplementation.cs index cecab67cf5..1a004f50de 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecutionDomainCallbacksImplementation.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecutionDomainCallbacksImplementation.cs @@ -8,10 +8,7 @@ using System.Reflection; using System.Collections.Generic; using System.Runtime.InteropServices; -using Internal.Metadata.NativeFormat; - using Internal.Runtime.Augments; -using Internal.Runtime.TypeLoader; using Internal.Reflection.Core.Execution; using Internal.Reflection.Execution.PayForPlayExperience; @@ -96,32 +93,6 @@ namespace Internal.Reflection.Execution return _executionDomain.CreateMissingMetadataException(pertainant); } - public sealed override EnumInfo GetEnumInfoIfAvailable(Type enumType) - { - // Handle the weird case of an enum type nested under a generic type that makes the - // enum itself generic. - if (enumType.IsConstructedGenericType) - { - enumType = enumType.GetGenericTypeDefinition(); - } - - QTypeDefinition qTypeDefinition; - if (!ReflectionExecution.ExecutionEnvironment.TryGetMetadataForNamedType(enumType.TypeHandle, out qTypeDefinition)) - return null; - - if (qTypeDefinition.IsNativeFormatMetadataBased) - { - return new NativeFormatEnumInfoImplementation(enumType, qTypeDefinition.NativeFormatReader, qTypeDefinition.NativeFormatHandle); - } -#if ECMA_METADATA_SUPPORT - if (qTypeDefinition.IsEcmaFormatMetadataBased) - { - return new EcmaFormatEnumInfoImplementation(enumType, qTypeDefinition.EcmaFormatReader, qTypeDefinition.EcmaFormatHandle); - } -#endif - return null; - } - // This is called from the ToString() helper of a RuntimeType that does not have full metadata. // This helper makes a "best effort" to give the caller something better than "EETypePtr nnnnnnnnn". public sealed override String GetBetterDiagnosticInfoIfAvailable(RuntimeTypeHandle runtimeTypeHandle) @@ -129,7 +100,7 @@ namespace Internal.Reflection.Execution return Type.GetTypeFromHandle(runtimeTypeHandle).ToDisplayStringIfAvailable(null); } - public sealed override String GetMethodNameFromStartAddressIfAvailable(IntPtr methodStartAddress) + public sealed override MethodBase GetMethodBaseFromStartAddressIfAvailable(IntPtr methodStartAddress) { RuntimeTypeHandle declaringTypeHandle = default(RuntimeTypeHandle); QMethodDefinition methodHandle; @@ -140,94 +111,13 @@ namespace Internal.Reflection.Execution return null; } - MethodBase methodBase = ReflectionCoreExecution.ExecutionDomain.GetMethod( - declaringTypeHandle, methodHandle, genericMethodTypeArgumentHandles); - if (methodBase == null || string.IsNullOrEmpty(methodBase.Name)) - return null; - - // get type name - string typeName = string.Empty; - Type declaringType = Type.GetTypeFromHandle(declaringTypeHandle); - if (declaringType != null) - typeName = declaringType.ToDisplayStringIfAvailable(null); - if (string.IsNullOrEmpty(typeName)) - typeName = ""; - - StringBuilder fullMethodName = new StringBuilder(); - fullMethodName.Append(typeName); - fullMethodName.Append('.'); - fullMethodName.Append(methodBase.Name); - fullMethodName.Append('('); - - // get parameter list - ParameterInfo[] paramArr = methodBase.GetParametersNoCopy(); - for (int i = 0; i < paramArr.Length; ++i) + if (RuntimeAugments.IsGenericType(declaringTypeHandle)) { - if (i != 0) - fullMethodName.Append(", "); - - ParameterInfo param = paramArr[i]; - string paramTypeName = string.Empty; - if (param.ParameterType != null) - paramTypeName = param.ParameterType.ToDisplayStringIfAvailable(null); - if (string.IsNullOrEmpty(paramTypeName)) - paramTypeName = ""; - else - { - // remove namespace from param type-name - int idxSeparator = paramTypeName.IndexOf("."); - if (idxSeparator >= 0) - paramTypeName = paramTypeName.Remove(0, idxSeparator + 1); - } - - string paramName = param.Name; - if (string.IsNullOrEmpty(paramName)) - paramName = ""; - - fullMethodName.Append(paramTypeName); - fullMethodName.Append(' '); - fullMethodName.Append(paramName); - } - fullMethodName.Append(')'); - return fullMethodName.ToString(); - } - - private String GetTypeFullNameFromTypeRef(TypeReferenceHandle typeReferenceHandle, MetadataReader reader) - { - String s = ""; - - TypeReference typeReference = typeReferenceHandle.GetTypeReference(reader); - s = typeReference.TypeName.GetString(reader); - Handle parentHandle = typeReference.ParentNamespaceOrType; - HandleType parentHandleType = parentHandle.HandleType; - if (parentHandleType == HandleType.TypeReference) - { - String containingTypeName = GetTypeFullNameFromTypeRef(parentHandle.ToTypeReferenceHandle(reader), reader); - s = containingTypeName + "+" + s; - } - else if (parentHandleType == HandleType.NamespaceReference) - { - NamespaceReferenceHandle namespaceReferenceHandle = parentHandle.ToNamespaceReferenceHandle(reader); - for (;;) - { - NamespaceReference namespaceReference = namespaceReferenceHandle.GetNamespaceReference(reader); - String namespacePart = namespaceReference.Name.GetStringOrNull(reader); - if (namespacePart == null) - break; // Reached the root namespace. - s = namespacePart + "." + s; - if (namespaceReference.ParentScopeOrNamespace.HandleType != HandleType.NamespaceReference) - break; // Should have reached the root namespace first but this helper is for ToString() - better to - // return partial information than crash. - namespaceReferenceHandle = namespaceReference.ParentScopeOrNamespace.ToNamespaceReferenceHandle(reader); - } - } - else - { - // If we got here, the metadata is illegal but this helper is for ToString() - better to - // return something partial than throw. + declaringTypeHandle = RuntimeAugments.GetGenericDefinition(declaringTypeHandle); } - return s; + // We don't use the type argument handles as we want the uninstantiated method info + return ReflectionCoreExecution.ExecutionDomain.GetMethod(declaringTypeHandle, methodHandle, genericMethodTypeArgumentHandles: null); } public sealed override IntPtr TryGetStaticClassConstructionContext(RuntimeTypeHandle runtimeTypeHandle) @@ -346,13 +236,12 @@ namespace Internal.Reflection.Execution { defaultValue = null; - MethodBase methodInfo = defaultParametersContext as MethodBase; - if (methodInfo == null) + if (!(defaultParametersContext is MethodBase methodBase)) { return false; } - ParameterInfo parameterInfo = methodInfo.GetParametersNoCopy()[argIndex]; + ParameterInfo parameterInfo = methodBase.GetParametersNoCopy()[argIndex]; if (!parameterInfo.HasDefaultValue) { // If the parameter is optional, with no default value and we're asked for its default value, diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/TypeLoader/TypeCast.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/TypeLoader/TypeCast.cs index d7fc2b29cc..66305cdb7a 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/TypeLoader/TypeCast.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/TypeLoader/TypeCast.cs @@ -198,7 +198,7 @@ namespace Internal.Reflection.Execution break; default: - Debug.Assert(false, "unknown generic variance type"); + Debug.Fail("unknown generic variance type"); return false; } } diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Extensions/NonPortable/DelegateMethodInfoRetriever.cs b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Extensions/NonPortable/DelegateMethodInfoRetriever.cs index eccf479f69..09f29cf91f 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Extensions/NonPortable/DelegateMethodInfoRetriever.cs +++ b/external/corert/src/System.Private.Reflection.Execution/src/Internal/Reflection/Extensions/NonPortable/DelegateMethodInfoRetriever.cs @@ -11,7 +11,6 @@ using global::Internal.Runtime.CompilerServices; using global::Internal.Reflection.Execution; using global::Internal.Reflection.Core.Execution; -using global::Internal.Metadata.NativeFormat; using System.Reflection.Runtime.General; namespace Internal.Reflection.Extensions.NonPortable @@ -24,9 +23,16 @@ namespace Internal.Reflection.Extensions.NonPortable throw new ArgumentException(); Delegate[] invokeList = del.GetInvocationList(); del = invokeList[invokeList.Length - 1]; - RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate; - bool isOpenResolver; - IntPtr originalLdFtnResult = RuntimeAugments.GetDelegateLdFtnResult(del, out typeOfFirstParameterIfInstanceDelegate, out isOpenResolver); + IntPtr originalLdFtnResult = RuntimeAugments.GetDelegateLdFtnResult(del, out RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate, out bool isOpenResolver, out bool isInterpreterEntrypoint); + + if (isInterpreterEntrypoint) + { + // This is a special kind of delegate where the invoke method is "ObjectArrayThunk". Typically, + // this will be a delegate that points the the LINQ Expression interpreter. We could manufacture + // a MethodInfo based on the delegate's Invoke signature, but let's just throw for now. + throw new PlatformNotSupportedException(SR.DelegateGetMethodInfo_ObjectArrayDelegate); + } + if (originalLdFtnResult == (IntPtr)0) return null; @@ -61,7 +67,7 @@ namespace Internal.Reflection.Extensions.NonPortable RuntimeTypeHandle declaringTypeHandleIgnored; MethodNameAndSignature nameAndSignatureIgnored; if (!TypeLoaderEnvironment.Instance.TryGetRuntimeMethodHandleComponents(resolver->GVMMethodHandle, out declaringTypeHandleIgnored, out nameAndSignatureIgnored, out genericMethodTypeArgumentHandles)) - return null; + throw new MissingRuntimeArtifactException(SR.DelegateGetMethodInfo_NoInstantiation); } } } @@ -69,7 +75,15 @@ namespace Internal.Reflection.Extensions.NonPortable if (callTryGetMethod) { if (!ReflectionExecution.ExecutionEnvironment.TryGetMethodForOriginalLdFtnResult(originalLdFtnResult, ref typeOfFirstParameterIfInstanceDelegate, out methodHandle, out genericMethodTypeArgumentHandles)) - return null; + { + ReflectionExecution.ExecutionEnvironment.GetFunctionPointerAndInstantiationArgumentForOriginalLdFtnResult(originalLdFtnResult, out IntPtr ip, out IntPtr _); + + string methodDisplayString = RuntimeAugments.TryGetMethodDisplayStringFromIp(ip); + if (methodDisplayString == null) + throw new MissingRuntimeArtifactException(SR.DelegateGetMethodInfo_NoDynamic); + else + throw new MissingRuntimeArtifactException(SR.Format(SR.DelegateGetMethodInfo_NoDynamic_WithDisplayString, methodDisplayString)); + } } MethodBase methodBase = ReflectionCoreExecution.ExecutionDomain.GetMethod(typeOfFirstParameterIfInstanceDelegate, methodHandle, genericMethodTypeArgumentHandles); MethodInfo methodInfo = methodBase as MethodInfo; diff --git a/external/corert/src/System.Private.Reflection.Execution/src/Resources/Strings.resx b/external/corert/src/System.Private.Reflection.Execution/src/Resources/Strings.resx index 2024d9b80a..f03a830ef2 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/Resources/Strings.resx +++ b/external/corert/src/System.Private.Reflection.Execution/src/Resources/Strings.resx @@ -150,12 +150,6 @@ Passing or returning pointers through Invoke is not supported on this platform. - - Object does not match target type. - - - Non-static method requires a target. - Non-static field requires a target. @@ -228,4 +222,16 @@ Cannot set a constant field. + + Cannot retrieve a MethodInfo for this delegate because the method it targeted was not enabled for metadata using the Dynamic attribute. For more information, please visit http://go.microsoft.com/fwlink/?LinkID=616868 + + + Cannot retrieve a MethodInfo for this delegate because the method it targeted ({0}) was not enabled for metadata using the Dynamic attribute. For more information, please visit http://go.microsoft.com/fwlink/?LinkID=616868 + + + Cannot retrieve a MethodInfo for this delegate because the necessary generic instantiation was not metadata-enabled. For more information, please visit http://go.microsoft.com/fwlink/?LinkID=616868 + + + Cannot retrieve a MethodInfo for this delegate because the delegate target is an interpreted LINQ expression. + diff --git a/external/corert/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.Experimental.csproj b/external/corert/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.Experimental.csproj index b5580c0b4e..069aa60a9d 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.Experimental.csproj +++ b/external/corert/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.Experimental.csproj @@ -1,8 +1,6 @@ - - + System.Private.Reflection.Execution.Experimental - {B48AF2B7-7205-4449-9DFF-44936490E8A5} true true diff --git a/external/corert/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj b/external/corert/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj index a8d3699959..5b935ef320 100644 --- a/external/corert/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj +++ b/external/corert/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj @@ -1,22 +1,15 @@ - - + System.Private.Reflection.Execution - {306A4D48-0ACF-41C4-BBA0-BCDAD9253E2D} - System.Private.Reflection.Execution 4.0.0.0 Library - {306A4D48-0ACF-41C4-BBA0-BCDAD9253E2D} true + true - - - - ECMA_METADATA_SUPPORT;$(DefineConstants) @@ -26,7 +19,7 @@ - + @@ -53,7 +46,6 @@ - @@ -76,32 +68,27 @@ - - - + + + + - + - - - - - - @@ -138,8 +125,8 @@ System\Collections\Generic\Empty.cs - - System\Runtime\CompilerServices\__BlockReflectionAttribute.cs + + System\Runtime\CompilerServices\__BlockAllReflectionAttribute.cs diff --git a/external/corert/src/System.Private.Reflection.Metadata/src/System.Private.Reflection.Metadata.csproj b/external/corert/src/System.Private.Reflection.Metadata/src/System.Private.Reflection.Metadata.csproj index b02e524c03..d8935be43e 100644 --- a/external/corert/src/System.Private.Reflection.Metadata/src/System.Private.Reflection.Metadata.csproj +++ b/external/corert/src/System.Private.Reflection.Metadata/src/System.Private.Reflection.Metadata.csproj @@ -1,18 +1,13 @@ - - + System.Private.Reflection.Metadata 4.0.0.0 Library - {45A617DF-FEC7-59C8-FD0D-BD27938DC940} true + true - - - - @@ -35,8 +30,8 @@ - - System\Runtime\CompilerServices\__BlockReflectionAttribute.cs + + System\Runtime\CompilerServices\__BlockAllReflectionAttribute.cs diff --git a/external/corert/src/System.Private.Reflection.Metadata/tests/CollectionExtensions.cs b/external/corert/src/System.Private.Reflection.Metadata/tests/CollectionExtensions.cs new file mode 100644 index 0000000000..b96348e620 --- /dev/null +++ b/external/corert/src/System.Private.Reflection.Metadata/tests/CollectionExtensions.cs @@ -0,0 +1,73 @@ +// 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.Collections.Generic; +using System.Text; + +using Internal.Metadata.NativeFormat; + +using Debug = System.Diagnostics.Debug; + +namespace System.Private.Reflection.Metadata.Tests +{ + static class CollectionExtensions + { + public static ScopeDefinitionHandle Single(this ScopeDefinitionHandleCollection collection) + { + Debug.Assert(collection.Count == 1); + var enumerator = collection.GetEnumerator(); + bool hasNext = enumerator.MoveNext(); + Debug.Assert(hasNext); + var result = enumerator.Current; + Debug.Assert(!enumerator.MoveNext()); + return result; + } + + public static TypeDefinitionHandle Single(this TypeDefinitionHandleCollection collection) + { + Debug.Assert(collection.Count == 1); + var enumerator = collection.GetEnumerator(); + bool hasNext = enumerator.MoveNext(); + Debug.Assert(hasNext); + var result = enumerator.Current; + Debug.Assert(!enumerator.MoveNext()); + return result; + } + + public static NamespaceDefinitionHandle Single(this NamespaceDefinitionHandleCollection collection) + { + Debug.Assert(collection.Count == 1); + var enumerator = collection.GetEnumerator(); + bool hasNext = enumerator.MoveNext(); + Debug.Assert(hasNext); + var result = enumerator.Current; + Debug.Assert(!enumerator.MoveNext()); + return result; + } + + public static MethodHandle Single(this MethodHandleCollection collection) + { + Debug.Assert(collection.Count == 1); + var enumerator = collection.GetEnumerator(); + bool hasNext = enumerator.MoveNext(); + Debug.Assert(hasNext); + var result = enumerator.Current; + Debug.Assert(!enumerator.MoveNext()); + return result; + } + + public static IEnumerable AsEnumerable(this NamespaceDefinitionHandleCollection collection) + { + foreach (var element in collection) + yield return element; + } + + public static IEnumerable AsEnumerable(this TypeDefinitionHandleCollection collection) + { + foreach (var element in collection) + yield return element; + } + } +} diff --git a/external/corert/src/System.Private.Reflection.Metadata/tests/RoundTripTests.cs b/external/corert/src/System.Private.Reflection.Metadata/tests/RoundTripTests.cs index 3bb8406b67..4a4c336a5d 100644 --- a/external/corert/src/System.Private.Reflection.Metadata/tests/RoundTripTests.cs +++ b/external/corert/src/System.Private.Reflection.Metadata/tests/RoundTripTests.cs @@ -154,14 +154,14 @@ namespace System.Private.Reflection.Metadata.Tests // Validate the root namespace and type Reader.NamespaceDefinition rootNamespace = systemRuntimeScope.RootNamespaceDefinition.GetNamespaceDefinition(rd); - Assert.Equal(1, rootNamespace.TypeDefinitions.Count()); + Assert.Equal(1, rootNamespace.TypeDefinitions.Count); Reader.TypeDefinition moduleType = rootNamespace.TypeDefinitions.Single().GetTypeDefinition(rd); Assert.Equal("", moduleType.Name.GetConstantStringValue(rd).Value); - Assert.Equal(1, rootNamespace.NamespaceDefinitions.Count()); + Assert.Equal(1, rootNamespace.NamespaceDefinitions.Count); // Validate the System namespace Reader.NamespaceDefinition systemNamespace = rootNamespace.NamespaceDefinitions.Single().GetNamespaceDefinition(rd); - Assert.Equal(4, systemNamespace.TypeDefinitions.Count()); + Assert.Equal(4, systemNamespace.TypeDefinitions.Count); foreach (var typeHandle in systemNamespace.TypeDefinitions) { Reader.TypeDefinition type = typeHandle.GetTypeDefinition(rd); @@ -177,19 +177,19 @@ namespace System.Private.Reflection.Metadata.Tests { case "Object": Assert.Null(baseTypeName); - Assert.Equal(1, type.Methods.Count()); + Assert.Equal(1, type.Methods.Count); break; case "Void": Assert.Equal("ValueType", baseTypeName); - Assert.Equal(0, type.Methods.Count()); + Assert.Equal(0, type.Methods.Count); break; case "String": Assert.Equal("Object", baseTypeName); - Assert.Equal(1, type.Methods.Count()); + Assert.Equal(1, type.Methods.Count); break; case "ValueType": Assert.Equal("Object", baseTypeName); - Assert.Equal(0, type.Methods.Count()); + Assert.Equal(0, type.Methods.Count); break; default: throw new NotImplementedException(); @@ -214,7 +214,7 @@ namespace System.Private.Reflection.Metadata.Tests Reader.ScopeDefinition systemRuntimeScope = scopeHandle.GetScopeDefinition(rd); Reader.NamespaceDefinition rootNamespace = systemRuntimeScope.RootNamespaceDefinition.GetNamespaceDefinition(rd); Reader.NamespaceDefinition systemNamespace = - rootNamespace.NamespaceDefinitions.Single( + rootNamespace.NamespaceDefinitions.AsEnumerable().Single( ns => ns.GetNamespaceDefinition(rd).Name.StringEquals("System", rd) ).GetNamespaceDefinition(rd); @@ -222,10 +222,10 @@ namespace System.Private.Reflection.Metadata.Tests // Since both System.Object and System.String define a default constructor and the // records are structurally equivalent, there should only be one metadata record // representing a default .ctor in the blob. - Reader.TypeDefinition objectType = systemNamespace.TypeDefinitions.Single( + Reader.TypeDefinition objectType = systemNamespace.TypeDefinitions.AsEnumerable().Single( t => t.GetTypeDefinition(rd).Name.StringEquals("Object", rd) ).GetTypeDefinition(rd); - Reader.TypeDefinition stringType = systemNamespace.TypeDefinitions.Single( + Reader.TypeDefinition stringType = systemNamespace.TypeDefinitions.AsEnumerable().Single( t => t.GetTypeDefinition(rd).Name.StringEquals("String", rd) ).GetTypeDefinition(rd); diff --git a/external/corert/src/System.Private.Reflection.Metadata/tests/System.Private.Reflection.Metadata.Tests.csproj b/external/corert/src/System.Private.Reflection.Metadata/tests/System.Private.Reflection.Metadata.Tests.csproj index ed54ed5b59..17e5448997 100644 --- a/external/corert/src/System.Private.Reflection.Metadata/tests/System.Private.Reflection.Metadata.Tests.csproj +++ b/external/corert/src/System.Private.Reflection.Metadata/tests/System.Private.Reflection.Metadata.Tests.csproj @@ -2,20 +2,11 @@ - Debug - AnyCPU - {3A6D978E-156A-4BFF-B394-7F6F6BB22AE1} Library System.Private.Reflection.Metadata.Tests System.Private.Reflection.Metadata.Tests true - .NETStandard,Version=v1.3 - netcoreapp1.1 - - - - - + netstandard1.3 ..\..\Common\src\Internal\NativeFormat @@ -34,10 +25,13 @@ + + + $(XunitNetcoreExtensionsVersion) + + - {D66338D4-F9E4-4051-B302-232C6BFB6EF6} - ILCompiler.MetadataWriter writer @@ -51,11 +45,9 @@ TypeHashingAlgorithms.cs + - - - \ No newline at end of file diff --git a/external/corert/src/System.Private.Reflection.Metadata/tests/project.json b/external/corert/src/System.Private.Reflection.Metadata/tests/project.json deleted file mode 100644 index be5b24e24e..0000000000 --- a/external/corert/src/System.Private.Reflection.Metadata/tests/project.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "dependencies": { - "NETStandard.Library": "1.6.1", - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.DotNet.BuildTools.TestSuite": "1.0.0-prerelease-00807-03", - "test-runtime": { - "target": "project", - "exclude": "compile" - } - }, - "frameworks": { - "netstandard1.3": { } - }, - "supports": { - "coreFx.Test.netcoreapp1.1": { } - } -} diff --git a/external/corert/src/System.Private.StackTraceGenerator/src/Internal/Dia/DiaInterfaces.cs b/external/corert/src/System.Private.StackTraceGenerator/src/Internal/Dia/DiaInterfaces.cs index d64921df83..017395cae4 100644 --- a/external/corert/src/System.Private.StackTraceGenerator/src/Internal/Dia/DiaInterfaces.cs +++ b/external/corert/src/System.Private.StackTraceGenerator/src/Internal/Dia/DiaInterfaces.cs @@ -92,6 +92,18 @@ namespace Internal.StackGenerator.Dia enumLineNumbers = new IDiaEnumLineNumbers(_enumLineNumbers); return hr; } + + public int FindILOffsetsByRVA(int rva, int length, out IDiaEnumLineNumbers enumLineNumbers) + { + enumLineNumbers = null; + IntPtr _enumLineNumbers; + int hr = S.StdCall(GetVTableMember(46), Punk, rva, length, out _enumLineNumbers); + GC.KeepAlive(this); + if (hr != S_OK) + return hr; + enumLineNumbers = new IDiaEnumLineNumbers(_enumLineNumbers); + return hr; + } } internal sealed class IDiaEnumSymbols : ComInterface diff --git a/external/corert/src/System.Private.StackTraceGenerator/src/Internal/StackTraceGenerator/StackTraceGenerator.Unix.cs b/external/corert/src/System.Private.StackTraceGenerator/src/Internal/StackTraceGenerator/StackTraceGenerator.Unix.cs index ba5d6c32de..56670081ce 100644 --- a/external/corert/src/System.Private.StackTraceGenerator/src/Internal/StackTraceGenerator/StackTraceGenerator.Unix.cs +++ b/external/corert/src/System.Private.StackTraceGenerator/src/Internal/StackTraceGenerator/StackTraceGenerator.Unix.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics; namespace Internal.StackTraceGenerator { @@ -27,5 +28,14 @@ namespace Internal.StackTraceGenerator lineNumber = 0; columnNumber = 0; } + + /// + /// Makes reasonable effort to locate the IL offset within the current method. + /// + public static void TryGetILOffsetWithinMethod(IntPtr ip, out int ilOffset) + { + // CORERT-TODO: Implement StackTraceGenerator on Unix + ilOffset = StackFrame.OFFSET_UNKNOWN; + } } } diff --git a/external/corert/src/System.Private.StackTraceGenerator/src/Internal/StackTraceGenerator/StackTraceGenerator.Windows.cs b/external/corert/src/System.Private.StackTraceGenerator/src/Internal/StackTraceGenerator/StackTraceGenerator.Windows.cs index bab973781e..c6b7148512 100644 --- a/external/corert/src/System.Private.StackTraceGenerator/src/Internal/StackTraceGenerator/StackTraceGenerator.Windows.cs +++ b/external/corert/src/System.Private.StackTraceGenerator/src/Internal/StackTraceGenerator/StackTraceGenerator.Windows.cs @@ -3,8 +3,10 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Text; using System.Diagnostics; +using System.Reflection; using System.Runtime.InteropServices; using Internal.Runtime.Augments; using Internal.StackGenerator.Dia; @@ -13,11 +15,27 @@ namespace Internal.StackTraceGenerator { public static class StackTraceGenerator { + /// + /// Check the AppCompat switch 'Diagnostics.DisableDiaStackTraceResolution'. + /// This is used for testing of metadata-based stack trace resolution. + /// + private static bool IsDiaStackTraceResolutionDisabled() + { + bool disableDia = false; + AppContext.TryGetSwitch("Diagnostics.DisableDiaStackTraceResolution", out disableDia); + return disableDia; + } + // // Makes reasonable effort to construct one useful line of a stack trace. Returns null if it can't. // public static String CreateStackTraceString(IntPtr ip, bool includeFileInfo) { + if (IsDiaStackTraceResolutionDisabled()) + { + return null; + } + try { int hr; @@ -62,11 +80,33 @@ namespace Internal.StackTraceGenerator fileName = null; lineNumber = 0; columnNumber = 0; - int rva; - IDiaSession session = GetDiaSession(ip, out rva); - if (session == null) - return; - TryGetSourceLineInfo(session, rva, out fileName, out lineNumber, out columnNumber); + if (!IsDiaStackTraceResolutionDisabled()) + { + int rva; + IDiaSession session = GetDiaSession(ip, out rva); + if (session != null) + { + TryGetSourceLineInfo(session, rva, out fileName, out lineNumber, out columnNumber); + } + } + } + + /// + /// Makes reasonable effort to find the IL offset corresponding to the given address within a method. + /// Returns StackFrame.OFFSET_UNKNOWN if not available. + /// + public static void TryGetILOffsetWithinMethod(IntPtr ip, out int ilOffset) + { + ilOffset = StackFrame.OFFSET_UNKNOWN; + if (!IsDiaStackTraceResolutionDisabled()) + { + int rva; + IDiaSession session = GetDiaSession(ip, out rva); + if (session != null) + { + TryGetILOffsetInfo(session, rva, out ilOffset); + } + } } // @@ -197,6 +237,31 @@ namespace Internal.StackTraceGenerator } } + private static void TryGetILOffsetInfo(IDiaSession session, int rva, out int ilOffset) + { + IDiaEnumLineNumbers lineNumbers; + int hr = session.FindILOffsetsByRVA(rva, 1, out lineNumbers); + if (hr == S_OK) + { + int numLineNumbers; + hr = lineNumbers.Count(out numLineNumbers); + if (hr == S_OK && numLineNumbers > 0) + { + IDiaLineNumber ln; + hr = lineNumbers.Item(0, out ln); + if (hr == S_OK) + { + hr = ln.LineNumber(out ilOffset); + if (hr == S_OK) + { + return; + } + } + } + } + ilOffset = StackFrame.OFFSET_UNKNOWN; + } + // // Generate the " in :line " section. // @@ -521,33 +586,17 @@ namespace Internal.StackTraceGenerator if (s_loadedModules == null) { - // Lazily create the parallel arrays s_loadedModules and s_perModuleDebugInfo - int moduleCount = RuntimeAugments.GetLoadedOSModules(null); - - s_loadedModules = new IntPtr[moduleCount]; - s_perModuleDebugInfo = new IDiaSession[moduleCount]; - - // Actually read the module addresses into the array - RuntimeAugments.GetLoadedOSModules(s_loadedModules); + // Lazily create the map from module bases to debug info + s_loadedModules = new Dictionary(); } // Locate module index based on base address - int moduleIndex = s_loadedModules.Length; - do - { - if (--moduleIndex < 0) - { - return null; - } - } - while(s_loadedModules[moduleIndex] != moduleBase); - - IDiaSession diaSession = s_perModuleDebugInfo[moduleIndex]; - if (diaSession != null) + IDiaSession diaSession; + if (s_loadedModules.TryGetValue(moduleBase, out diaSession)) { return diaSession; } - + string modulePath = RuntimeAugments.TryGetFullPathToApplicationModule(moduleBase); if (modulePath == null) { @@ -580,13 +629,17 @@ namespace Internal.StackTraceGenerator return null; } - s_perModuleDebugInfo[moduleIndex] = diaSession; + s_loadedModules.Add(moduleBase, diaSession); return diaSession; } // CoCreateInstance is not in WindowsApp_Downlevel.lib and ExactSpelling = true is required // to force MCG to resolve it. - [DllImport("api-ms-win-core-com-l1-1-0.dll", ExactSpelling =true)] + // + // This api is a WACK violation but it cannot be changed to CoCreateInstanceApp() without breaking the stack generator altogether. + // The toolchain will not include this library in the dependency closure as long as (1) the program is being compiled as a store app and not a console .exe + // and (2) the /buildType switch passed to ILC is set to the "ret". + [DllImport("api-ms-win-core-com-l1-1-0.dll", ExactSpelling = true)] private static extern unsafe int CoCreateInstance(byte* rclsid, IntPtr pUnkOuter, int dwClsContext, byte* riid, out IntPtr ppv); private const int S_OK = 0; @@ -596,17 +649,7 @@ namespace Internal.StackTraceGenerator /// Loaded binary module addresses. /// [ThreadStatic] - private static IntPtr[] s_loadedModules; - - /// - /// DIA session COM interfaces for the individual native application modules. - /// The array is constructed upon the first call to GetDiaSession but the - /// COM interface instances are created lazily on demand. - /// This array is parallel to s_loadedModules - it has the same number of elements - /// and the corresponding entries have the same indices. - /// - [ThreadStatic] - private static IDiaSession[] s_perModuleDebugInfo; + private static Dictionary s_loadedModules; } } diff --git a/external/corert/src/System.Private.StackTraceGenerator/src/System.Private.StackTraceGenerator.csproj b/external/corert/src/System.Private.StackTraceGenerator/src/System.Private.StackTraceGenerator.csproj index 77e9568df0..7a33b2c813 100644 --- a/external/corert/src/System.Private.StackTraceGenerator/src/System.Private.StackTraceGenerator.csproj +++ b/external/corert/src/System.Private.StackTraceGenerator/src/System.Private.StackTraceGenerator.csproj @@ -1,18 +1,13 @@ - - + System.Private.StackTraceGenerator 4.0.0.0 Library - {35A616DA-EEC6-49C4-BD06-AD27938DC94B} - true + true + true - - - - @@ -32,8 +27,8 @@ System\Runtime\InteropServices\McgIntrinsicsAttribute.cs - - System\Runtime\CompilerServices\__BlockReflectionAttribute.cs + + System\Runtime\CompilerServices\__BlockAllReflectionAttribute.cs diff --git a/external/corert/src/System.Private.StackTraceMetadata/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs b/external/corert/src/System.Private.StackTraceMetadata/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs new file mode 100644 index 0000000000..0e4de34b0c --- /dev/null +++ b/external/corert/src/System.Private.StackTraceMetadata/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs @@ -0,0 +1,16 @@ +// 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; + +namespace Internal.Runtime.CompilerHelpers +{ + public class LibraryInitializer + { + public static void InitializeLibrary() + { + Internal.StackTraceMetadata.StackTraceMetadata.Initialize(); + } + } +} diff --git a/external/corert/src/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/MethodNameFormatter.cs b/external/corert/src/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/MethodNameFormatter.cs new file mode 100644 index 0000000000..2d3bb5a0a9 --- /dev/null +++ b/external/corert/src/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/MethodNameFormatter.cs @@ -0,0 +1,555 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Text; + +using Internal.Metadata.NativeFormat; + +namespace Internal.StackTraceMetadata +{ + class MethodNameFormatter + { + /// + /// Metadata reader used for the purpose of method name formatting. + /// + private readonly MetadataReader _metadataReader; + + /// + /// String builder used to construct formatted method name. + /// + private readonly StringBuilder _outputBuilder; + + /// + /// Represents the instatiation type context. + /// + private readonly SigTypeContext _typeContext; + + /// + /// Initialize the reader used for method name formatting. + /// + private MethodNameFormatter(MetadataReader metadataReader, SigTypeContext typeContext) + { + _metadataReader = metadataReader; + _outputBuilder = new StringBuilder(); + _typeContext = typeContext; + } + + public static string FormatMethodName(MetadataReader metadataReader, Handle methodHandle) + { + MethodNameFormatter formatter = new MethodNameFormatter(metadataReader, SigTypeContext.FromMethod(metadataReader, methodHandle)); + formatter.EmitMethodName(methodHandle); + return formatter._outputBuilder.ToString(); + } + + public static string FormatMethodName(MetadataReader metadataReader, TypeDefinitionHandle enclosingTypeHandle, MethodHandle methodHandle) + { + MethodNameFormatter formatter = new MethodNameFormatter(metadataReader, SigTypeContext.FromMethod(metadataReader, enclosingTypeHandle, methodHandle)); + + Method method = metadataReader.GetMethod(methodHandle); + MethodSignature methodSignature = metadataReader.GetMethodSignature(method.Signature); + formatter.EmitTypeName(enclosingTypeHandle, namespaceQualified: true); + formatter._outputBuilder.Append('.'); + formatter.EmitString(method.Name); + + bool first = true; + foreach (GenericParameterHandle handle in method.GenericParameters) + { + if (first) + { + first = false; + formatter._outputBuilder.Append('['); + } + else + { + formatter._outputBuilder.Append(", "); + } + formatter.EmitTypeName(handle, namespaceQualified: false); + } + if (!first) + { + formatter._outputBuilder.Append(']'); + } + + formatter.EmitMethodParameters(methodSignature); + + return formatter._outputBuilder.ToString(); + } + + /// + /// Emit a given method signature to a specified string builder. + /// + /// Method reference or instantiation token + private void EmitMethodName(Handle methodHandle) + { + switch (methodHandle.HandleType) + { + case HandleType.MemberReference: + EmitMethodReferenceName(methodHandle.ToMemberReferenceHandle(_metadataReader)); + break; + + case HandleType.MethodInstantiation: + EmitMethodInstantiationName(methodHandle.ToMethodInstantiationHandle(_metadataReader)); + break; + + case HandleType.QualifiedMethod: + EmitMethodDefinitionName(methodHandle.ToQualifiedMethodHandle(_metadataReader)); + break; + + default: + Debug.Assert(false); + _outputBuilder.Append("???"); + break; + } + } + + /// + /// Emit method reference to the output string builder. + /// + /// Member reference handle + private void EmitMethodReferenceName(MemberReferenceHandle memberRefHandle) + { + MemberReference methodRef = _metadataReader.GetMemberReference(memberRefHandle); + MethodSignature methodSignature; + EmitContainingTypeAndMethodName(methodRef, out methodSignature); + EmitMethodParameters(methodSignature); + } + + /// + /// Emit generic method instantiation to the output string builder. + /// + /// Method instantiation handle + private void EmitMethodInstantiationName(MethodInstantiationHandle methodInstHandle) + { + MethodInstantiation methodInst = _metadataReader.GetMethodInstantiation(methodInstHandle); + MemberReferenceHandle methodRefHandle = methodInst.Method.ToMemberReferenceHandle(_metadataReader); + MemberReference methodRef = methodRefHandle.GetMemberReference(_metadataReader); + MethodSignature methodSignature; + EmitContainingTypeAndMethodName(methodRef, out methodSignature); + EmitGenericArguments(methodInst.GenericTypeArguments); + EmitMethodParameters(methodSignature); + } + + private void EmitMethodDefinitionName(QualifiedMethodHandle qualifiedMethodHandle) + { + QualifiedMethod qualifiedMethod = _metadataReader.GetQualifiedMethod(qualifiedMethodHandle); + Method method = _metadataReader.GetMethod(qualifiedMethod.Method); + MethodSignature methodSignature = _metadataReader.GetMethodSignature(method.Signature); + EmitTypeName(qualifiedMethod.EnclosingType, namespaceQualified: true); + _outputBuilder.Append('.'); + EmitString(method.Name); + EmitMethodParameters(methodSignature); + } + + /// + /// Emit containing type and method name and extract the method signature from a method reference. + /// + /// Method reference to format + /// Output method signature + private void EmitContainingTypeAndMethodName(MemberReference methodRef, out MethodSignature methodSignature) + { + methodSignature = _metadataReader.GetMethodSignature(methodRef.Signature.ToMethodSignatureHandle(_metadataReader)); + EmitTypeName(methodRef.Parent, namespaceQualified: true); + _outputBuilder.Append("."); + EmitString(methodRef.Name); + } + + /// + /// Emit parenthesized method argument type list. + /// + /// Method signature to use for parameter formatting + private void EmitMethodParameters(MethodSignature methodSignature) + { + _outputBuilder.Append("("); + EmitTypeVector(methodSignature.Parameters); + _outputBuilder.Append(")"); + } + + /// + /// Emit comma-separated list of type names into the output string builder. + /// + /// Enumeration of type handles to output + private void EmitTypeVector(HandleCollection typeVector) + { + bool first = true; + foreach (Handle handle in typeVector) + { + if (first) + { + first = false; + } + else + { + _outputBuilder.Append(", "); + } + EmitTypeName(handle, namespaceQualified: false); + } + } + + /// + /// Emit the name of a given type to the output string builder. + /// + /// Type handle to format + /// When set to true, include namespace information + private void EmitTypeName(Handle typeHandle, bool namespaceQualified) + { + switch (typeHandle.HandleType) + { + case HandleType.TypeReference: + EmitTypeReferenceName(typeHandle.ToTypeReferenceHandle(_metadataReader), namespaceQualified); + break; + + case HandleType.TypeSpecification: + EmitTypeSpecificationName(typeHandle.ToTypeSpecificationHandle(_metadataReader), namespaceQualified); + break; + + case HandleType.TypeInstantiationSignature: + EmitTypeInstantiationName(typeHandle.ToTypeInstantiationSignatureHandle(_metadataReader), namespaceQualified); + break; + + case HandleType.SZArraySignature: + EmitSZArrayTypeName(typeHandle.ToSZArraySignatureHandle(_metadataReader), namespaceQualified); + break; + + case HandleType.ArraySignature: + EmitArrayTypeName(typeHandle.ToArraySignatureHandle(_metadataReader), namespaceQualified); + break; + + case HandleType.PointerSignature: + EmitPointerTypeName(typeHandle.ToPointerSignatureHandle(_metadataReader)); + break; + + case HandleType.ByReferenceSignature: + EmitByRefTypeName(typeHandle.ToByReferenceSignatureHandle(_metadataReader)); + break; + + case HandleType.TypeDefinition: + EmitTypeDefinitionName(typeHandle.ToTypeDefinitionHandle(_metadataReader), namespaceQualified); + break; + + case HandleType.TypeVariableSignature: + EmitTypeName(_typeContext.GetTypeVariable(typeHandle.ToTypeVariableSignatureHandle(_metadataReader).GetTypeVariableSignature(_metadataReader).Number), namespaceQualified); + break; + + case HandleType.MethodTypeVariableSignature: + EmitTypeName(_typeContext.GetMethodVariable(typeHandle.ToMethodTypeVariableSignatureHandle(_metadataReader).GetMethodTypeVariableSignature(_metadataReader).Number), namespaceQualified); + break; + + case HandleType.GenericParameter: + EmitString(typeHandle.ToGenericParameterHandle(_metadataReader).GetGenericParameter(_metadataReader).Name); + break; + + default: + Debug.Assert(false); + _outputBuilder.Append("???"); + break; + } + } + + /// + /// Emit namespace reference. + /// + /// Namespace reference handle + private void EmitNamespaceReferenceName(NamespaceReferenceHandle namespaceRefHandle) + { + NamespaceReference namespaceRef = _metadataReader.GetNamespaceReference(namespaceRefHandle); + if (!namespaceRef.ParentScopeOrNamespace.IsNull(_metadataReader) && + namespaceRef.ParentScopeOrNamespace.HandleType == HandleType.NamespaceReference) + { + int charsWritten = _outputBuilder.Length; + EmitNamespaceReferenceName(namespaceRef.ParentScopeOrNamespace.ToNamespaceReferenceHandle(_metadataReader)); + if (_outputBuilder.Length - charsWritten > 0) + _outputBuilder.Append('.'); + } + EmitString(namespaceRef.Name); + } + + private void EmitNamespaceDefinitionName(NamespaceDefinitionHandle namespaceDefHandle) + { + NamespaceDefinition namespaceDef = _metadataReader.GetNamespaceDefinition(namespaceDefHandle); + if (!namespaceDef.ParentScopeOrNamespace.IsNull(_metadataReader) && + namespaceDef.ParentScopeOrNamespace.HandleType == HandleType.NamespaceDefinition) + { + int charsWritten = _outputBuilder.Length; + EmitNamespaceDefinitionName(namespaceDef.ParentScopeOrNamespace.ToNamespaceDefinitionHandle(_metadataReader)); + if (_outputBuilder.Length - charsWritten > 0) + _outputBuilder.Append('.'); + } + EmitString(namespaceDef.Name); + } + + /// + /// Emit type reference. + /// + /// Type reference handle + /// When set to true, include namespace information + private void EmitTypeReferenceName(TypeReferenceHandle typeRefHandle, bool namespaceQualified) + { + TypeReference typeRef = _metadataReader.GetTypeReference(typeRefHandle); + if (!typeRef.ParentNamespaceOrType.IsNull(_metadataReader)) + { + if (typeRef.ParentNamespaceOrType.HandleType != HandleType.NamespaceReference) + { + // Nested type + EmitTypeName(typeRef.ParentNamespaceOrType, namespaceQualified); + _outputBuilder.Append('.'); + } + else if (namespaceQualified) + { + int charsWritten = _outputBuilder.Length; + EmitNamespaceReferenceName(typeRef.ParentNamespaceOrType.ToNamespaceReferenceHandle(_metadataReader)); + if (_outputBuilder.Length - charsWritten > 0) + _outputBuilder.Append('.'); + } + } + EmitString(typeRef.TypeName); + } + + private void EmitTypeDefinitionName(TypeDefinitionHandle typeDefHandle, bool namespaceQualified) + { + TypeDefinition typeDef = _metadataReader.GetTypeDefinition(typeDefHandle); + if (!typeDef.EnclosingType.IsNull(_metadataReader)) + { + // Nested type + EmitTypeName(typeDef.EnclosingType, namespaceQualified); + _outputBuilder.Append('.'); + } + else if (namespaceQualified) + { + int charsWritten = _outputBuilder.Length; + EmitNamespaceDefinitionName(typeDef.NamespaceDefinition); + if (_outputBuilder.Length - charsWritten > 0) + _outputBuilder.Append('.'); + } + EmitString(typeDef.Name); + } + + /// + /// Emit an arbitrary type specification. + /// + /// Type specification handle + /// When set to true, include namespace information + private void EmitTypeSpecificationName(TypeSpecificationHandle typeSpecHandle, bool namespaceQualified) + { + TypeSpecification typeSpec = _metadataReader.GetTypeSpecification(typeSpecHandle); + EmitTypeName(typeSpec.Signature, namespaceQualified); + } + + /// + /// Emit generic instantiation type. + /// + /// Instantiated type specification signature handle + /// When set to true, include namespace information + private void EmitTypeInstantiationName(TypeInstantiationSignatureHandle typeInstHandle, bool namespaceQualified) + { + TypeInstantiationSignature typeInst = _metadataReader.GetTypeInstantiationSignature(typeInstHandle); + EmitTypeName(typeInst.GenericType, namespaceQualified); + EmitGenericArguments(typeInst.GenericTypeArguments); + } + + /// + /// Emit SZArray (single-dimensional array with zero lower bound) type. + /// + /// SZArray type specification signature handle + /// When set to true, include namespace information + private void EmitSZArrayTypeName(SZArraySignatureHandle szArraySigHandle, bool namespaceQualified) + { + SZArraySignature szArraySig = _metadataReader.GetSZArraySignature(szArraySigHandle); + EmitTypeName(szArraySig.ElementType, namespaceQualified); + _outputBuilder.Append("[]"); + } + + /// + /// Emit multi-dimensional array type. + /// + /// Multi-dimensional array type specification signature handle + /// When set to true, include namespace information + private void EmitArrayTypeName(ArraySignatureHandle arraySigHandle, bool namespaceQualified) + { + ArraySignature arraySig = _metadataReader.GetArraySignature(arraySigHandle); + EmitTypeName(arraySig.ElementType, namespaceQualified); + _outputBuilder.Append('['); + if (arraySig.Rank > 1) + { + _outputBuilder.Append(',', arraySig.Rank - 1); + } + else + { + _outputBuilder.Append('*'); + } + _outputBuilder.Append(']'); + } + + /// + /// Emit pointer type. + /// + /// Pointer type specification signature handle + private void EmitPointerTypeName(PointerSignatureHandle pointerSigHandle) + { + PointerSignature pointerSig = _metadataReader.GetPointerSignature(pointerSigHandle); + EmitTypeName(pointerSig.Type, namespaceQualified: false); + _outputBuilder.Append('*'); + } + + /// + /// Emit by-reference type. + /// + /// ByReference type specification signature handle + private void EmitByRefTypeName(ByReferenceSignatureHandle byRefSigHandle) + { + ByReferenceSignature byRefSig = _metadataReader.GetByReferenceSignature(byRefSigHandle); + EmitTypeName(byRefSig.Type, namespaceQualified: false); + _outputBuilder.Append('&'); + } + + /// + /// Emit angle-bracketed list of type / method generic arguments. + /// + /// Collection of generic argument type handles + private void EmitGenericArguments(HandleCollection genericArguments) + { + _outputBuilder.Append('['); + EmitTypeVector(genericArguments); + _outputBuilder.Append(']'); + } + + /// + /// Emit a string (represented by a serialized ConstantStringValue) to the output string builder. + /// + /// Constant string value token (offset within stack trace native metadata) + private void EmitString(ConstantStringValueHandle stringHandle) + { + _outputBuilder.Append(_metadataReader.GetConstantStringValue(stringHandle).Value); + } + + private struct SigTypeContext + { + private readonly object _typeContext; + private readonly object _methodContext; + + public SigTypeContext(object typeContext, object methodContext) + { + _typeContext = typeContext; + _methodContext = methodContext; + } + + public static Handle GetHandleAt(HandleCollection collection, int index) + { + int currentIndex = 0; + + foreach (var currentArg in collection) + { + if (currentIndex == index) + return currentArg; + currentIndex++; + } + + Debug.Assert(false); + return default(Handle); + } + + public static Handle GetHandleAt(GenericParameterHandleCollection collection, int index) + { + int currentIndex = 0; + + foreach (var currentArg in collection) + { + if (currentIndex == index) + return currentArg; + currentIndex++; + } + + Debug.Assert(false); + return default(Handle); + } + + public Handle GetTypeVariable(int index) + { + return _typeContext is GenericParameterHandleCollection ? + GetHandleAt((GenericParameterHandleCollection)_typeContext, index) : + GetHandleAt((HandleCollection)_typeContext, index); + } + + public Handle GetMethodVariable(int index) + { + return _typeContext is GenericParameterHandleCollection ? + GetHandleAt((GenericParameterHandleCollection)_methodContext, index) : + GetHandleAt((HandleCollection)_methodContext, index); + } + + private static object GetTypeContext(MetadataReader metadataReader, Handle handle) + { + switch (handle.HandleType) + { + case HandleType.MemberReference: + MemberReference memberRef = handle.ToMemberReferenceHandle(metadataReader).GetMemberReference(metadataReader); + return GetTypeContext(metadataReader, memberRef.Parent); + + case HandleType.QualifiedMethod: + QualifiedMethod qualifiedMethod = handle.ToQualifiedMethodHandle(metadataReader).GetQualifiedMethod(metadataReader); + return GetTypeContext(metadataReader, qualifiedMethod.EnclosingType); + + case HandleType.TypeDefinition: + TypeDefinition typeDef = handle.ToTypeDefinitionHandle(metadataReader).GetTypeDefinition(metadataReader); + return typeDef.GenericParameters; + + case HandleType.TypeReference: + return default(HandleCollection); + + case HandleType.TypeSpecification: + TypeSpecification typeSpec = handle.ToTypeSpecificationHandle(metadataReader).GetTypeSpecification(metadataReader); + if (typeSpec.Signature.HandleType != HandleType.TypeInstantiationSignature) + { + Debug.Assert(false); + return default(HandleCollection); + } + return typeSpec.Signature.ToTypeInstantiationSignatureHandle(metadataReader).GetTypeInstantiationSignature(metadataReader).GenericTypeArguments; + + default: + Debug.Assert(false); + return default(HandleCollection); + } + } + + public static SigTypeContext FromMethod(MetadataReader metadataReader, Handle methodHandle) + { + object typeContext; + object methodContext; + + switch (methodHandle.HandleType) + { + case HandleType.MemberReference: + typeContext = GetTypeContext(metadataReader, methodHandle); + methodContext = default(HandleCollection); + break; + + case HandleType.MethodInstantiation: + MethodInstantiation methodInst = methodHandle.ToMethodInstantiationHandle(metadataReader).GetMethodInstantiation(metadataReader); + typeContext = GetTypeContext(metadataReader, methodInst.Method); + methodContext = methodInst.GenericTypeArguments; + break; + + case HandleType.QualifiedMethod: + QualifiedMethod qualifiedMethod = methodHandle.ToQualifiedMethodHandle(metadataReader).GetQualifiedMethod(metadataReader); + typeContext = GetTypeContext(metadataReader, qualifiedMethod.EnclosingType); + methodContext = qualifiedMethod.Method.GetMethod(metadataReader).GenericParameters; + break; + default: + Debug.Assert(false); + return default(SigTypeContext); + } + + return new SigTypeContext(typeContext, methodContext); + } + + public static SigTypeContext FromMethod(MetadataReader metadataReader, TypeDefinitionHandle enclosingTypeHandle, MethodHandle methodHandle) + { + Method method = metadataReader.GetMethod(methodHandle); + return new SigTypeContext(GetTypeContext(metadataReader, enclosingTypeHandle), method.GenericParameters); + } + } + } +} diff --git a/external/corert/src/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/StackTraceMetadata.cs b/external/corert/src/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/StackTraceMetadata.cs new file mode 100644 index 0000000000..1396b2893e --- /dev/null +++ b/external/corert/src/System.Private.StackTraceMetadata/src/Internal/StackTraceMetadata/StackTraceMetadata.cs @@ -0,0 +1,260 @@ +// 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.Collections.Generic; +using System.Runtime; + +using Internal.Metadata.NativeFormat; +using Internal.Runtime; +using Internal.Runtime.Augments; +using Internal.Runtime.TypeLoader; +using Internal.TypeSystem; + +using ReflectionExecution = Internal.Reflection.Execution.ReflectionExecution; + +#if BIT64 +using nint = System.Int64; +using nuint = System.UInt64; +#else +using nint = System.Int32; +using nuint = System.UInt32; +#endif + +namespace Internal.StackTraceMetadata +{ + /// + /// This helper class is used to resolve non-reflectable method names using a special + /// compiler-generated metadata blob to enhance quality of exception call stacks + /// in situations where symbol information is not available. + /// + internal static class StackTraceMetadata + { + /// + /// Module address-keyed map of per-module method name resolvers. + /// + static PerModuleMethodNameResolverHashtable _perModuleMethodNameResolverHashtable; + + /// + /// Eager startup initialization of stack trace metadata support creates + /// the per-module method name resolver hashtable and registers the runtime augment + /// for metadata-based stack trace resolution. + /// + internal static void Initialize() + { + _perModuleMethodNameResolverHashtable = new PerModuleMethodNameResolverHashtable(); + RuntimeAugments.InitializeStackTraceMetadataSupport(new StackTraceMetadataCallbacksImpl()); + } + + /// + /// Locate the containing module for a method and try to resolve its name based on start address. + /// + public static string GetMethodNameFromStartAddressIfAvailable(IntPtr methodStartAddress) + { + IntPtr moduleStartAddress = RuntimeAugments.GetOSModuleFromPointer(methodStartAddress); + int rva = (int)((nuint)methodStartAddress - (nuint)moduleStartAddress); + foreach (TypeManagerHandle handle in ModuleList.Enumerate()) + { + if (handle.OsModuleBase == moduleStartAddress) + { + string name = _perModuleMethodNameResolverHashtable.GetOrCreateValue(handle.GetIntPtrUNSAFE()).GetMethodNameFromRvaIfAvailable(rva); + if (name != null) + return name; + } + } + + // We haven't found information in the stack trace metadata tables, but maybe reflection will have this + if (ReflectionExecution.TryGetMethodMetadataFromStartAddress(methodStartAddress, + out MetadataReader reader, + out TypeDefinitionHandle typeHandle, + out MethodHandle methodHandle)) + { + return MethodNameFormatter.FormatMethodName(reader, typeHandle, methodHandle); + } + + return null; + } + + /// + /// This hashtable supports mapping from module start addresses to per-module method name resolvers. + /// + private sealed class PerModuleMethodNameResolverHashtable : LockFreeReaderHashtable + { + /// + /// Given a key, compute a hash code. This function must be thread safe. + /// + protected override int GetKeyHashCode(IntPtr key) + { + return key.GetHashCode(); + } + + /// + /// Given a value, compute a hash code which would be identical to the hash code + /// for a key which should look up this value. This function must be thread safe. + /// This function must also not cause additional hashtable adds. + /// + protected override int GetValueHashCode(PerModuleMethodNameResolver value) + { + return GetKeyHashCode(value.ModuleAddress); + } + + /// + /// Compare a key and value. If the key refers to this value, return true. + /// This function must be thread safe. + /// + protected override bool CompareKeyToValue(IntPtr key, PerModuleMethodNameResolver value) + { + return key == value.ModuleAddress; + } + + /// + /// Compare a value with another value. Return true if values are equal. + /// This function must be thread safe. + /// + protected override bool CompareValueToValue(PerModuleMethodNameResolver value1, PerModuleMethodNameResolver value2) + { + return value1.ModuleAddress == value2.ModuleAddress; + } + + /// + /// Create a new value from a key. Must be threadsafe. Value may or may not be added + /// to collection. Return value must not be null. + /// + protected override PerModuleMethodNameResolver CreateValueFromKey(IntPtr key) + { + return new PerModuleMethodNameResolver(key); + } + } + + /// + /// Implementation of stack trace metadata callbacks. + /// + private sealed class StackTraceMetadataCallbacksImpl : StackTraceMetadataCallbacks + { + public override string TryGetMethodNameFromStartAddress(IntPtr methodStartAddress) + { + return GetMethodNameFromStartAddressIfAvailable(methodStartAddress); + } + } + + /// + /// Method name resolver for a single binary module + /// + private sealed class PerModuleMethodNameResolver + { + /// + /// Start address of the module in question. + /// + private readonly IntPtr _moduleAddress; + + /// + /// Dictionary mapping method RVA's to tokens within the metadata blob. + /// + private readonly Dictionary _methodRvaToTokenMap; + + /// + /// Metadata reader for the stack trace metadata. + /// + private readonly MetadataReader _metadataReader; + + /// + /// Publicly exposed module address property. + /// + public IntPtr ModuleAddress { get { return _moduleAddress; } } + + /// + /// Construct the per-module resolver by looking up the necessary blobs. + /// + public unsafe PerModuleMethodNameResolver(IntPtr moduleAddress) + { + _moduleAddress = moduleAddress; + + TypeManagerHandle handle = new TypeManagerHandle(moduleAddress); + ModuleInfo moduleInfo; + if (!ModuleList.Instance.TryGetModuleInfoByHandle(handle, out moduleInfo)) + { + // Module not found + return; + } + + NativeFormatModuleInfo nativeFormatModuleInfo = moduleInfo as NativeFormatModuleInfo; + if (nativeFormatModuleInfo == null) + { + // It is not a native format module + return; + } + + byte *metadataBlob; + uint metadataBlobSize; + + byte *rvaToTokenMapBlob; + uint rvaToTokenMapBlobSize; + + if (nativeFormatModuleInfo.TryFindBlob( +#if PROJECTN + (int)ReflectionMapBlob.BlobIdStackTraceEmbeddedMetadata, +#else + (int)ReflectionMapBlob.EmbeddedMetadata, +#endif + out metadataBlob, + out metadataBlobSize) && + nativeFormatModuleInfo.TryFindBlob( + (int)ReflectionMapBlob.BlobIdStackTraceMethodRvaToTokenMapping, + out rvaToTokenMapBlob, + out rvaToTokenMapBlobSize)) + { + _metadataReader = new MetadataReader(new IntPtr(metadataBlob), (int)metadataBlobSize); + + // RVA to token map consists of pairs of integers (method RVA - token) + int rvaToTokenMapEntryCount = (int)(rvaToTokenMapBlobSize / (2 * sizeof(int))); + _methodRvaToTokenMap = new Dictionary(rvaToTokenMapEntryCount); + PopulateRvaToTokenMap(handle, (int *)rvaToTokenMapBlob, rvaToTokenMapEntryCount); + } + } + + /// + /// Construct the dictionary mapping method RVAs to stack trace metadata tokens + /// within a single binary module. + /// + /// List of RVA - token pairs + /// Number of the RVA - token pairs in the list + private unsafe void PopulateRvaToTokenMap(TypeManagerHandle handle, int *rvaToTokenMap, int entryCount) + { + for (int entryIndex = 0; entryIndex < entryCount; entryIndex++) + { +#if PROJECTN + int methodRva = rvaToTokenMap[2 * entryIndex + 0]; +#else + int* pRelPtr32 = &rvaToTokenMap[2 * entryIndex + 0]; + IntPtr pointer = (IntPtr)((byte*)pRelPtr32 + *pRelPtr32); + int methodRva = (int)((nuint)pointer - (nuint)handle.OsModuleBase); +#endif + int token = rvaToTokenMap[2 * entryIndex + 1]; + _methodRvaToTokenMap[methodRva] = token; + } + } + + /// + /// Try to resolve method name based on its address using the stack trace metadata + /// + public string GetMethodNameFromRvaIfAvailable(int rva) + { + if (_methodRvaToTokenMap == null) + { + // No stack trace metadata for this module + return null; + } + + int rawToken; + if (!_methodRvaToTokenMap.TryGetValue(rva, out rawToken)) + { + // Method RVA not found in the map + return null; + } + + return MethodNameFormatter.FormatMethodName(_metadataReader, Handle.FromIntToken(rawToken)); + } + } + } +} diff --git a/external/corert/src/System.Private.StackTraceMetadata/src/System.Private.StackTraceMetadata.csproj b/external/corert/src/System.Private.StackTraceMetadata/src/System.Private.StackTraceMetadata.csproj new file mode 100644 index 0000000000..799bc65e03 --- /dev/null +++ b/external/corert/src/System.Private.StackTraceMetadata/src/System.Private.StackTraceMetadata.csproj @@ -0,0 +1,31 @@ + + + + System.Private.StackTraceMetadata + 4.0.0.0 + Library + true + true + + + + + + + + + + + + + + + Internal\Runtime\MetadataBlob.cs + + + System\Runtime\CompilerServices\__BlockAllReflectionAttribute.cs + + + + + diff --git a/external/corert/src/System.Private.Threading/src/System.Private.Threading.csproj b/external/corert/src/System.Private.Threading/src/System.Private.Threading.csproj index 0022008341..d18767bcbc 100644 --- a/external/corert/src/System.Private.Threading/src/System.Private.Threading.csproj +++ b/external/corert/src/System.Private.Threading/src/System.Private.Threading.csproj @@ -1,19 +1,13 @@ - - + System.Private.Threading 4.0.0.0 Library - {AD10E5A4-FBA5-4135-9BA4-008723F319B8} true - true + true - - - - diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.Ecma.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.Ecma.cs index 2be089f9a7..c6342da015 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.Ecma.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.Ecma.cs @@ -6,9 +6,11 @@ using System; using System.Reflection; using System.Diagnostics; using System.Collections.Generic; +using System.IO; using Internal.Reflection.Core; using Internal.Runtime.TypeLoader; +using Internal.Runtime.Augments; using System.Reflection.Runtime.General; @@ -52,7 +54,7 @@ namespace Internal.Reflection.Execution MetadataReader reader = pe.GetMetadataReader(); // 2. Create AssemblyName from MetadataReader - RuntimeAssemblyName runtimeAssemblyName = reader.GetAssemblyDefinition().ToRuntimeAssemblyName(reader); + RuntimeAssemblyName runtimeAssemblyName = reader.GetAssemblyDefinition().ToRuntimeAssemblyName(reader).CanonicalizePublicKeyToken(); lock(s_ecmaLoadedAssemblies) { @@ -84,14 +86,14 @@ namespace Internal.Reflection.Execution } } - partial void BindEcmaAssemblyName(RuntimeAssemblyName refName, ref AssemblyBindResult result, ref Exception exception, ref bool foundMatch) + partial void BindEcmaAssemblyName(RuntimeAssemblyName refName, ref AssemblyBindResult result, ref Exception exception, ref Exception preferredException, ref bool foundMatch) { lock(s_ecmaLoadedAssemblies) { for (int i = 0; i < s_ecmaLoadedAssemblies.Count; i++) { PEInfo info = s_ecmaLoadedAssemblies[i]; - if (AssemblyNameMatches(refName, info.Name)) + if (AssemblyNameMatches(refName, info.Name, ref preferredException)) { if (foundMatch) { @@ -99,13 +101,124 @@ namespace Internal.Reflection.Execution return; } - foundMatch = true; result.EcmaMetadataReader = info.Reader; + foundMatch = result.EcmaMetadataReader != null; + + // For failed matches, we will never be able to succeed, so return now + if (!foundMatch) + return; + } + } + + if (!foundMatch) + { + try + { + // Not found in already loaded list, attempt to source assembly from disk + foreach (string filePath in FilePathsForAssembly(refName)) + { + FileStream ownedFileStream = null; + PEReader ownedPEReader = null; + try + { + if (!RuntimeAugments.FileExists(filePath)) + continue; + + try + { + ownedFileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); + } + catch (System.IO.IOException) + { + // Failure to open a file is not fundamentally an assembly load error, but it does indicate this file cannot be used + continue; + } + + ownedPEReader = new PEReader(ownedFileStream); + // FileStream ownership transferred to ownedPEReader + ownedFileStream = null; + + if (!ownedPEReader.HasMetadata) + continue; + + MetadataReader reader = ownedPEReader.GetMetadataReader(); + // Create AssemblyName from MetadataReader + RuntimeAssemblyName runtimeAssemblyName = reader.GetAssemblyDefinition().ToRuntimeAssemblyName(reader).CanonicalizePublicKeyToken(); + + // If assembly name doesn't match, it isn't the one we're looking for. Continue to look for more assemblies + if (!AssemblyNameMatches(refName, runtimeAssemblyName, ref preferredException)) + continue; + + // This is the one we are looking for, add it to the list of loaded assemblies + PEInfo peinfo = new PEInfo(runtimeAssemblyName, reader, ownedPEReader); + + s_ecmaLoadedAssemblies.Add(peinfo); + + // At this point the PE reader is no longer owned by this code, but is owned by the s_ecmaLoadedAssemblies list + PEReader pe = ownedPEReader; + ownedPEReader = null; + + ModuleList moduleList = ModuleList.Instance; + ModuleInfo newModuleInfo = new EcmaModuleInfo(moduleList.SystemModule.Handle, pe, reader); + moduleList.RegisterModule(newModuleInfo); + + foundMatch = true; + result.EcmaMetadataReader = peinfo.Reader; + break; + } + finally + { + if (ownedFileStream != null) + ownedFileStream.Dispose(); + + if (ownedPEReader != null) + ownedPEReader.Dispose(); + } + } + } + catch (System.IO.IOException) + { } + catch (System.ArgumentException) + { } + catch (System.BadImageFormatException badImageFormat) + { + exception = badImageFormat; + } + + // Cache missed lookups + if (!foundMatch) + { + PEInfo peinfo = new PEInfo(refName, null, null); + s_ecmaLoadedAssemblies.Add(peinfo); } } } } + public IEnumerable FilePathsForAssembly(RuntimeAssemblyName refName) + { + // Check for illegal characters in file name + if (refName.Name.IndexOfAny(Path.GetInvalidFileNameChars()) != -1) + yield break; + + // Implement simple probing for assembly in application base directory and culture specific directory + string probingDirectory = AppDomain.CurrentDomain.BaseDirectory; + string cultureQualifiedDirectory = probingDirectory; + + if (!String.IsNullOrEmpty(refName.CultureName)) + { + cultureQualifiedDirectory = Path.Combine(probingDirectory, refName.CultureName); + } + else + { + // Loading non-resource dlls not yet supported + yield break; + } + + // Attach assembly name + yield return Path.Combine(cultureQualifiedDirectory, refName.Name + ".dll"); + } + partial void InsertEcmaLoadedAssemblies(List loadedAssemblies) { lock (s_ecmaLoadedAssemblies) @@ -113,6 +226,9 @@ namespace Internal.Reflection.Execution for (int i = 0; i < s_ecmaLoadedAssemblies.Count; i++) { PEInfo info = s_ecmaLoadedAssemblies[i]; + if (info.Reader == null) + continue; + AssemblyBindResult result = default(AssemblyBindResult); result.EcmaMetadataReader = info.Reader; loadedAssemblies.Add(result); diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs index e17a26c13c..701160534d 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs @@ -35,7 +35,7 @@ namespace Internal.Reflection.Execution public static AssemblyBinderImplementation Instance { get; } = new AssemblyBinderImplementation(); partial void BindEcmaByteArray(byte[] rawAssembly, byte[] rawSymbolStore, ref AssemblyBindResult bindResult, ref Exception exception, ref bool? result); - partial void BindEcmaAssemblyName(RuntimeAssemblyName refName, ref AssemblyBindResult result, ref Exception exception, ref bool resultBoolean); + partial void BindEcmaAssemblyName(RuntimeAssemblyName refName, ref AssemblyBindResult result, ref Exception exception, ref Exception preferredException, ref bool resultBoolean); partial void InsertEcmaLoadedAssemblies(List loadedAssemblies); public sealed override bool Bind(byte[] rawAssembly, byte[] rawSymbolStore, out AssemblyBindResult bindResult, out Exception exception) @@ -59,6 +59,8 @@ namespace Internal.Reflection.Execution result = default(AssemblyBindResult); exception = null; + Exception preferredException = null; + refName = refName.CanonicalizePublicKeyToken(); // At least one real-world app calls Type.GetType() for "char" using the assembly name "mscorlib". To accomodate this, @@ -80,7 +82,7 @@ namespace Internal.Reflection.Execution } else { - nameMatches = AssemblyNameMatches(refName, group.Key); + nameMatches = AssemblyNameMatches(refName, group.Key, ref preferredException); } if (nameMatches) @@ -100,13 +102,13 @@ namespace Internal.Reflection.Execution } } - BindEcmaAssemblyName(refName, ref result, ref exception, ref foundMatch); + BindEcmaAssemblyName(refName, ref result, ref exception, ref preferredException, ref foundMatch); if (exception != null) return false; if (!foundMatch) { - exception = new IOException(SR.Format(SR.FileNotFound_AssemblyNotFound, refName.FullName)); + exception = preferredException ?? new FileNotFoundException(SR.Format(SR.FileNotFound_AssemblyNotFound, refName.FullName)); return false; } @@ -159,7 +161,7 @@ namespace Internal.Reflection.Execution // // Encapsulates the assembly ref->def matching policy. // - private bool AssemblyNameMatches(RuntimeAssemblyName refName, RuntimeAssemblyName defName) + private bool AssemblyNameMatches(RuntimeAssemblyName refName, RuntimeAssemblyName defName, ref Exception preferredException) { // // The defName came from trusted metadata so it should be fully specified. @@ -175,9 +177,11 @@ namespace Internal.Reflection.Execution if (refName.Version != null) { - int compareResult = refName.Version.CompareTo(defName.Version); - if (compareResult > 0) + if (!AssemblyVersionMatches(refVersion: refName.Version, defVersion: defName.Version)) + { + preferredException = new FileLoadException(SR.Format(SR.FileLoadException_RefDefMismatch, refName.FullName, defName.Version, refName.Version)); return false; + } } if (refName.CultureName != null) @@ -206,6 +210,33 @@ namespace Internal.Reflection.Execution return true; } + private static bool AssemblyVersionMatches(Version refVersion, Version defVersion) + { + if (defVersion.Major < refVersion.Major) + return false; + if (defVersion.Major > refVersion.Major) + return true; + + if (defVersion.Minor < refVersion.Minor) + return false; + if (defVersion.Minor > refVersion.Minor) + return true; + + if (refVersion.Build == -1) + return true; + if (defVersion.Build < refVersion.Build) + return false; + if (defVersion.Build > refVersion.Build) + return true; + + if (refVersion.Revision == -1) + return true; + if (defVersion.Revision < refVersion.Revision) + return false; + + return true; + } + /// /// This callback gets called whenever a module gets registered. It adds the metadata reader /// for the new module to the available scopes. The lock in ExecutionEnvironmentImplementation ensures diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs index 1f8eba9ccf..c09e3848ad 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs @@ -4,6 +4,7 @@ using System; +using Internal.Runtime.DebuggerSupport; using Internal.Runtime.TypeLoader; namespace Internal.Runtime.CompilerHelpers @@ -13,7 +14,6 @@ namespace Internal.Runtime.CompilerHelpers public static void InitializeLibrary() { TypeLoaderEnvironment.Initialize(); - DebugFuncEval.Initialize(); } } } diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/DebuggerSupport/DebuggerSupport.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/DebuggerSupport/DebuggerSupport.cs new file mode 100644 index 0000000000..d7ba1cdfbb --- /dev/null +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/DebuggerSupport/DebuggerSupport.cs @@ -0,0 +1,157 @@ +// 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 Internal.NativeFormat; +using Internal.Runtime.Augments; +using Internal.Runtime.CallInterceptor; +using Internal.Runtime.CompilerServices; +using Internal.Runtime.TypeLoader; +using Internal.TypeSystem; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +// The following definitions are required for interop with the VS Debugger +// Prior to making any changes to these, please reach out to the VS Debugger +// team to make sure that your changes are not going to prevent the debugger +// from working. +namespace Internal.Runtime.DebuggerSupport +{ + public class LowLevelNativeFormatReader + { + private readonly NativeReader _nativeReader; + private uint _offset; + + public unsafe LowLevelNativeFormatReader(byte* buffer, uint bufferSize) + { + _nativeReader = new NativeReader(buffer, bufferSize); + _offset = 0; + } + + public uint GetUnsigned() + { + uint value; + _offset = _nativeReader.DecodeUnsigned(_offset, out value); + return value; + } + + public ulong GetUnsignedLong() + { + ulong value; + _offset = _nativeReader.DecodeUnsignedLong(_offset, out value); + return value; + } + + public RuntimeSignature CreateRuntimeSignature() + { + return RuntimeSignature.CreateFromNativeLayoutSignatureForDebugger(_offset); + } + + internal NativeReader InternalReader + { + get + { + return _nativeReader; + } + } + + internal uint Offset + { + get + { + return _offset; + } + + set + { + this._offset = value; + } + } + } + + public class TypeSystemHelper + { + public static unsafe IntPtr GetVirtualMethodFunctionPointer(IntPtr thisPointer, uint virtualMethodSlot) + { + // The first pointer in the object is a pointer to the EEType object + EEType* eeType = *(EEType**)thisPointer; + + // The vtable of the object can be found at the end of EEType object + IntPtr* vtable = eeType->GetVTableStartAddress(); + + // Indexing the vtable to find out the actual function entry point + IntPtr entryPoint = vtable[virtualMethodSlot]; + + return entryPoint; + } + + public static unsafe bool IsValueType(RuntimeTypeHandle runtimeTypeHandle) + { + return runtimeTypeHandle.ToEETypePtr()->IsValueType; + } + + public static unsafe IntPtr GetInterfaceDispatchFunctionPointer(IntPtr thisPointer, RuntimeTypeHandle interfaceType, uint virtualMethodSlot) + { + object instance = Unsafe.As(ref thisPointer); + return RuntimeAugments.ResolveDispatch(instance, interfaceType, (int)virtualMethodSlot); + } + + public static bool CallingConverterDataFromMethodSignature(LowLevelNativeFormatReader reader, + ulong[] externalReferences, + out bool hasThis, + out TypeDesc[] parameters, + out bool[] paramsWithDependentLayout) + { + + if (externalReferences == null) + { + throw new ArgumentNullException(nameof(externalReferences)); + } + + TypeSystemContext typeSystemContext = TypeSystemContextFactory.Create(); + bool result = TypeLoaderEnvironment.Instance.GetCallingConverterDataFromMethodSignature_NativeLayout_Common( + typeSystemContext, + reader.CreateRuntimeSignature(), + Instantiation.Empty, + Instantiation.Empty, + out hasThis, + out parameters, + out paramsWithDependentLayout, + reader.InternalReader, + externalReferences); + TypeSystemContextFactory.Recycle(typeSystemContext); + return result; + } + + public static RuntimeTypeHandle GetConstructedRuntimeTypeHandle(LowLevelNativeFormatReader reader, ulong[] externalReferences) + { + if (externalReferences == null) + { + throw new ArgumentNullException(nameof(externalReferences)); + } + + TypeDesc objectTypeDesc = GetConstructedType(reader, externalReferences); + return objectTypeDesc.GetRuntimeTypeHandle(); + } + + private static TypeDesc GetConstructedType(LowLevelNativeFormatReader reader, ulong[] externalReferences) + { + NativeLayoutInfoLoadContext nativeLayoutContext = new NativeLayoutInfoLoadContext(); + TypeSystemContext typeSystemContext = TypeSystemContextFactory.Create(); + nativeLayoutContext._module = null; + nativeLayoutContext._typeSystemContext = typeSystemContext; + nativeLayoutContext._typeArgumentHandles = Instantiation.Empty; + nativeLayoutContext._methodArgumentHandles = Instantiation.Empty; + nativeLayoutContext._debuggerPreparedExternalReferences = externalReferences; + + TypeDesc objectTypeDesc = null; + NativeParser parser = new NativeParser(reader.InternalReader, reader.Offset); + objectTypeDesc = TypeLoaderEnvironment.Instance.GetConstructedTypeFromParserAndNativeLayoutContext( + ref parser, + nativeLayoutContext); + TypeSystemContextFactory.Recycle(typeSystemContext); + reader.Offset = parser.Offset; + return objectTypeDesc; + } + } +} diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.CallConversionInfo.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.CallConversionInfo.cs index e00b44a229..cd554332c6 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.CallConversionInfo.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.CallConversionInfo.cs @@ -66,6 +66,8 @@ namespace Internal.Runtime.TypeLoader case ThunkKind.StandardToStandardInstantiating: return "StandardToStandardInstantiating"; case ThunkKind.StandardToGenericInstantiating: return "StandardToGenericInstantiating"; case ThunkKind.StandardToGenericInstantiatingIfNotHasThis: return "StandardToGenericInstantiatingIfNotHasThis"; + case ThunkKind.StandardToGenericPassthruInstantiating: return "StandardToGenericPassthruInstantiating"; + case ThunkKind.StandardToGenericPassthruInstantiatingIfNotHasThis: return "StandardToGenericPassthruInstantiatingIfNotHasThis"; case ThunkKind.StandardToGeneric: return "StandardToGeneric"; case ThunkKind.GenericToStandard: return "GenericToStandard"; case ThunkKind.StandardUnboxing: return "StandardUnboxing"; @@ -120,7 +122,7 @@ namespace Internal.Runtime.TypeLoader } } - Debug.Assert(false, "UNREACHABLE"); + Debug.Fail("UNREACHABLE"); return false; } @@ -499,6 +501,25 @@ namespace Internal.Runtime.TypeLoader } } + public bool HasKnownTargetPointerAndInstantiatingArgument + { + get + { + // If the target method pointer and/or dictionary are passed as arguments to the converter, they are + // considered unknown. + // Similarly, delegate thunks and reflection DynamicInvoke thunks do not have any target pointer or + // dictionary pointers stored in their CallConversionInfo structures. + if (CallerHasExtraParameterWhichIsFunctionTarget || IsDelegateThunk || IsAnyDynamicInvokerThunk || _targetFunctionPointer == IntPtr.Zero) + return false; + + if (_instantiatingArg != IntPtr.Zero) + return true; + + // Null instantiating arguments are considered known values for non-instantiating stubs. + return _thunkKind == ThunkKind.StandardToGeneric || _thunkKind == ThunkKind.GenericToStandard || _thunkKind == ThunkKind.StandardUnboxing; + } + } + public bool CalleeHasParamType { get diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.CallConversionParameters.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.CallConversionParameters.cs index 67e27009ab..d05988797c 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.CallConversionParameters.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.CallConversionParameters.cs @@ -38,6 +38,8 @@ #define ENREGISTERED_RETURNTYPE_MAXSIZE #define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE #define ENREGISTERED_PARAMTYPE_MAXSIZE +#elif WASM +#define _TARGET_WASM_ #else #error Unknown architecture! #endif @@ -63,17 +65,16 @@ namespace Internal.Runtime.TypeLoader { internal class GCHandleContainer { - internal GCHandle _thisPtrHandle; - internal GCHandle _dynamicInvokeArgHandle; - internal GCHandle _returnObjectHandle; + internal UnsafeGCHandle _thisPtrHandle; + internal UnsafeGCHandle _dynamicInvokeArgHandle; + internal UnsafeGCHandle _returnObjectHandle; internal GCHandleContainer() { - // Allocations of pinned gc handles done only once during the lifetime of a thread. - // An empty string is used as the initial pinned object reference. - _thisPtrHandle = GCHandle.Alloc("", GCHandleType.Pinned); - _dynamicInvokeArgHandle = GCHandle.Alloc("", GCHandleType.Pinned); - _returnObjectHandle = GCHandle.Alloc("", GCHandleType.Pinned); + // Allocations of pinned gc handles done only once during the lifetime of a thread + _thisPtrHandle = UnsafeGCHandle.Alloc(null, GCHandleType.Pinned); + _dynamicInvokeArgHandle = UnsafeGCHandle.Alloc(null, GCHandleType.Pinned); + _returnObjectHandle = UnsafeGCHandle.Alloc(null, GCHandleType.Pinned); } ~GCHandleContainer() @@ -307,11 +308,11 @@ namespace Internal.Runtime.TypeLoader internal void ResetPinnedObjects() { - // Reset all pinned gchandles to an empty string. - // Freeing of gchandles is done in the destructor of GCHandleContainer when the thread dies - s_pinnedGCHandles._thisPtrHandle.Target = ""; - s_pinnedGCHandles._returnObjectHandle.Target = ""; - s_pinnedGCHandles._dynamicInvokeArgHandle.Target = ""; + // Reset all pinned gchandles to null. + // Freeing of gchandles is done in the destructor of GCHandleContainer when the thread dies. + s_pinnedGCHandles._thisPtrHandle.Target = null; + s_pinnedGCHandles._dynamicInvokeArgHandle.Target = null; + s_pinnedGCHandles._returnObjectHandle.Target = null; } private bool UpdateCalleeFunctionPointer(IntPtr newFunctionPointer) @@ -379,10 +380,11 @@ namespace Internal.Runtime.TypeLoader byte* pSrc = _callerTransitionBlock + ofsCaller; - TypeHandle thValueType; - _callerArgs.GetArgType(out thValueType); + TypeHandle thArgType; + _callerArgs.GetArgType(out thArgType); + Debug.Assert(!thArgType.IsNull()); - if (thValueType.IsNull()) + if (!thArgType.IsValueType()) { Debug.Assert(_callerArgs.GetArgSize() == IntPtr.Size); @@ -396,7 +398,7 @@ namespace Internal.Runtime.TypeLoader } else { - RuntimeTypeHandle argEEType = thValueType.GetRuntimeTypeHandle(); + RuntimeTypeHandle argEEType = thArgType.GetRuntimeTypeHandle(); if (_callerArgs.IsArgPassedByRef()) { @@ -417,7 +419,7 @@ namespace Internal.Runtime.TypeLoader Debug.Assert(_conversionInfo.IsClosedStaticDelegate && !_delegateData.Equals(default(DelegateData))); Debug.Assert(_delegateData._helperObject != null); s_pinnedGCHandles._thisPtrHandle.Target = _delegateData._helperObject; - return RuntimeAugments.GetRawAddrOfPinnedObject((IntPtr)s_pinnedGCHandles._thisPtrHandle); + return s_pinnedGCHandles._thisPtrHandle.GetRawTargetAddress(); } } @@ -472,7 +474,7 @@ namespace Internal.Runtime.TypeLoader s_pinnedGCHandles._thisPtrHandle.Target = _delegateData._helperObject; } - thisPointer = (void*)RuntimeAugments.GetRawAddrOfPinnedObject((IntPtr)s_pinnedGCHandles._thisPtrHandle); + thisPointer = (void*)s_pinnedGCHandles._thisPtrHandle.GetRawTargetAddress(); } else { @@ -507,7 +509,7 @@ namespace Internal.Runtime.TypeLoader // We'll need to create a return buffer, or assign into the return buffer when the actual call completes. if (_calleeArgs.HasRetBuffArg()) { - TypeHandle thValueType; + TypeHandle thRetType; bool forceByRefUnused; void* callerRetBuffer = null; @@ -517,17 +519,18 @@ namespace Internal.Runtime.TypeLoader // value, of the same type as the return value type handle in the callee's arguments. Debug.Assert(!_callerArgs.HasRetBuffArg()); - CorElementType returnType = _calleeArgs.GetReturnType(out thValueType, out forceByRefUnused); - RuntimeTypeHandle returnValueType = thValueType.IsNull() ? typeof(object).TypeHandle : thValueType.GetRuntimeTypeHandle(); + CorElementType returnType = _calleeArgs.GetReturnType(out thRetType, out forceByRefUnused); + Debug.Assert(!thRetType.IsNull()); + RuntimeTypeHandle returnValueType = thRetType.IsValueType() ? thRetType.GetRuntimeTypeHandle() : typeof(object).TypeHandle; s_pinnedGCHandles._returnObjectHandle.Target = RuntimeAugments.RawNewObject(returnValueType); // The transition block has a space reserved for storing return buffer data. This is protected conservatively. // Copy the address of the allocated object to the protected memory to be able to safely unpin it. callerRetBuffer = _callerTransitionBlock + TransitionBlock.GetOffsetOfReturnValuesBlock(); - *((void**)callerRetBuffer) = (void*)RuntimeAugments.GetRawAddrOfPinnedObject((IntPtr)s_pinnedGCHandles._returnObjectHandle); + *((void**)callerRetBuffer) = (void*)s_pinnedGCHandles._returnObjectHandle.GetRawTargetAddress(); // Unpin the allocated object (it's now protected in the caller's conservatively reported memory space) - s_pinnedGCHandles._returnObjectHandle.Target = ""; + s_pinnedGCHandles._returnObjectHandle.Target = null; // Point the callerRetBuffer to the begining of the actual object's data (skipping the EETypePtr slot) callerRetBuffer = (void*)(new IntPtr(*((void**)callerRetBuffer)) + IntPtr.Size); @@ -538,8 +541,8 @@ namespace Internal.Runtime.TypeLoader callerRetBuffer = _callerTransitionBlock + TransitionBlock.GetOffsetOfReturnValuesBlock(); // Make sure buffer is nulled out, and setup the return buffer location. - CorElementType returnType = _callerArgs.GetReturnType(out thValueType, out forceByRefUnused); - int returnSize = TypeHandle.GetElemSize(returnType, thValueType); + CorElementType returnType = _callerArgs.GetReturnType(out thRetType, out forceByRefUnused); + int returnSize = TypeHandle.GetElemSize(returnType, thRetType); CallConverterThunk.memzeroPointerAligned((byte*)callerRetBuffer, returnSize); } @@ -630,9 +633,31 @@ namespace Internal.Runtime.TypeLoader Func targetDelegate = _delegateData._helperObject as Func; Debug.Assert(targetDelegate != null); - s_pinnedGCHandles._returnObjectHandle.Target = targetDelegate(arguments ?? Array.Empty()); + object result = targetDelegate(arguments ?? Array.Empty()); - return RuntimeAugments.GetRawAddrOfPinnedObject((IntPtr)s_pinnedGCHandles._returnObjectHandle); + TypeHandle thArgType; + bool forceByRefUnused; + _calleeArgs.GetReturnType(out thArgType, out forceByRefUnused); + Debug.Assert(!thArgType.IsNull()); + + unsafe + { + if (thArgType.IsValueType() && thArgType.GetRuntimeTypeHandle().ToEETypePtr()->IsNullable) + { + object nullableObj = RuntimeAugments.RawNewObject(thArgType.GetRuntimeTypeHandle()); + s_pinnedGCHandles._returnObjectHandle.Target = nullableObj; + if (result != null) + { + RuntimeAugments.StoreValueTypeField(ref RuntimeAugments.GetRawData(nullableObj), result, thArgType.GetRuntimeTypeHandle()); + } + } + else + { + s_pinnedGCHandles._returnObjectHandle.Target = result; + } + } + + return s_pinnedGCHandles._returnObjectHandle.GetRawTargetAddress(); } internal IntPtr GetArgSetupStateDataPointer() diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.cs index 3b0393cd42..b9b8a9cff2 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.cs @@ -38,6 +38,8 @@ #define ENREGISTERED_RETURNTYPE_MAXSIZE #define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE #define ENREGISTERED_PARAMTYPE_MAXSIZE +#elif WASM +#define _TARGET_WASM_ #else #error Unknown architecture! #endif @@ -63,7 +65,7 @@ namespace Internal.Runtime.TypeLoader { private static LowLevelList s_allocatedThunks = new LowLevelList(); - private static object s_thunkPoolHeap; + private static volatile object s_thunkPoolHeap; internal static IntPtr CommonInputThunkStub = IntPtr.Zero; #if CALLDESCR_FPARGREGSARERETURNREGS @@ -116,8 +118,13 @@ namespace Internal.Runtime.TypeLoader private extern static unsafe void CallingConventionConverter_SpecifyCommonStubData(IntPtr commonStubData); #endif - static unsafe CallConverterThunk() + private static bool s_callConverterThunk = CallConverterThunk_LazyCctor(); + + private static unsafe bool CallConverterThunk_LazyCctor() { +#if PLATFORM_UNIX + // TODO +#else CallingConventionConverter_GetStubs(out ReturnVoidReturnThunk, out ReturnIntegerPointReturnThunk, out CommonInputThunkStub #if CALLDESCR_FPARGREGSARERETURNREGS #else @@ -131,7 +138,10 @@ namespace Internal.Runtime.TypeLoader { CallingConventionConverter_SpecifyCommonStubData((IntPtr)commonStubData); } -#endif +#endif //_TARGET_ARM_ + +#endif // PLATFORM_UNIX + return true; } internal static bool GetByRefIndicatorAtIndex(int index, bool[] lookup) @@ -222,7 +232,7 @@ namespace Internal.Runtime.TypeLoader lock (s_allocatedThunks) { - if (callConversionInfo < s_allocatedThunks.Count) + if (callConversionInfo < s_allocatedThunks.Count && s_allocatedThunks[callConversionInfo] != IntPtr.Zero) return s_allocatedThunks[callConversionInfo]; if (s_thunkPoolHeap == null) @@ -237,8 +247,13 @@ namespace Internal.Runtime.TypeLoader fixed (CallingConventionConverter_CommonCallingStub_PointerData* commonStubData = &s_commonStubData) { RuntimeAugments.SetThunkData(s_thunkPoolHeap, thunk, new IntPtr(callConversionInfo), new IntPtr(commonStubData)); - Debug.Assert(callConversionInfo == s_allocatedThunks.Count); - s_allocatedThunks.Add(thunk); + + if (callConversionInfo >= s_allocatedThunks.Count) + { + s_allocatedThunks.Expand(count: callConversionInfo + 1); + } + Debug.Assert(s_allocatedThunks[callConversionInfo] == IntPtr.Zero); + s_allocatedThunks[callConversionInfo] = thunk; } } @@ -250,11 +265,8 @@ namespace Internal.Runtime.TypeLoader if (thunkKind == ReversePinvokeThunk) { // Special unsupported thunk kind. Similar behavior to the thunks generated by the delegate ILTransform for this thunk kind - - RuntimeTypeHandle thDummy; - bool isOpenResolverDummy; Action throwNotSupportedException = () => { throw new NotSupportedException(); }; - return RuntimeAugments.GetDelegateLdFtnResult(throwNotSupportedException, out thDummy, out isOpenResolverDummy); + return RuntimeAugments.GetDelegateLdFtnResult(throwNotSupportedException, out RuntimeTypeHandle _, out bool _, out bool _); } RuntimeTypeHandle delegateType = RuntimeAugments.GetRuntimeTypeHandleFromObjectReference(delegateObject); @@ -306,7 +318,8 @@ namespace Internal.Runtime.TypeLoader { IntPtr callConversionId; IntPtr commonStubDataPtr; - if (!RuntimeAugments.TryGetThunkData(s_thunkPoolHeap, potentialStub, out callConversionId, out commonStubDataPtr)) + object thunkPoolHeap = s_thunkPoolHeap; + if (thunkPoolHeap == null || !RuntimeAugments.TryGetThunkData(thunkPoolHeap, potentialStub, out callConversionId, out commonStubDataPtr)) { // This isn't a call conversion stub nonUnboxingMethod = IntPtr.Zero; @@ -379,6 +392,28 @@ namespace Internal.Runtime.TypeLoader } } + public static unsafe bool TryGetCallConversionTargetPointerAndInstantiatingArg(IntPtr potentialStub, out IntPtr methodTarget, out IntPtr instantiatingArg) + { + methodTarget = instantiatingArg = IntPtr.Zero; + + IntPtr callConversionId; + IntPtr commonStubDataPtr; + object thunkPoolHeap = s_thunkPoolHeap; + if (thunkPoolHeap == null || !RuntimeAugments.TryGetThunkData(thunkPoolHeap, potentialStub, out callConversionId, out commonStubDataPtr)) + { + // This isn't a call conversion stub + return false; + } + + CallConversionInfo conversionInfo = CallConversionInfo.GetConverter(callConversionId.ToInt32()); + if (!conversionInfo.HasKnownTargetPointerAndInstantiatingArgument) + return false; + + methodTarget = conversionInfo.TargetFunctionPointer; + instantiatingArg = conversionInfo.InstantiatingStubArgument; + return true; + } + // This struct shares a layout with CallDescrData in the MRT codebase. internal unsafe struct CallDescrData { @@ -578,7 +613,8 @@ namespace Internal.Runtime.TypeLoader int ofsCallee; int ofsCaller; TypeHandle thDummy; - TypeHandle thValueType; + TypeHandle thArgType; + TypeHandle thRetType; IntPtr argPtr; #if CALLDESCR_FPARGREGS FloatArgumentRegisters* pFloatArgumentRegisters = null; @@ -655,7 +691,8 @@ namespace Internal.Runtime.TypeLoader InvokeUtils.DynamicInvokeParamLookupType paramLookupType; RuntimeTypeHandle argumentRuntimeTypeHandle; - CorElementType argType = conversionParams._calleeArgs.GetArgType(out thValueType); + CorElementType argType = conversionParams._calleeArgs.GetArgType(out thArgType); + Debug.Assert(!thArgType.IsNull()); if (argType == CorElementType.ELEMENT_TYPE_BYREF) { @@ -667,19 +704,20 @@ namespace Internal.Runtime.TypeLoader } else { - argumentRuntimeTypeHandle = (thValueType.IsNull() ? typeof(object).TypeHandle : thValueType.GetRuntimeTypeHandle()); + // We need to check the exact type handle of the argument being passed during reflection invoke scenarios. + argumentRuntimeTypeHandle = thArgType.GetRuntimeTypeHandle(); } object invokeParam = InvokeUtils.DynamicInvokeParamHelperCore( argumentRuntimeTypeHandle, out paramLookupType, out index, - conversionParams._calleeArgs.IsArgPassedByRef() ? InvokeUtils.DynamicInvokeParamType.Ref : InvokeUtils.DynamicInvokeParamType.In); + argType == CorElementType.ELEMENT_TYPE_BYREF ? InvokeUtils.DynamicInvokeParamType.Ref : InvokeUtils.DynamicInvokeParamType.In); if (paramLookupType == InvokeUtils.DynamicInvokeParamLookupType.ValuetypeObjectReturned) { CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.Target = invokeParam; - argPtr = RuntimeAugments.GetRawAddrOfPinnedObject((IntPtr)CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle) + IntPtr.Size; + argPtr = CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.GetRawTargetAddress() + IntPtr.Size; } else { @@ -687,7 +725,7 @@ namespace Internal.Runtime.TypeLoader Debug.Assert((invokeParam is object[]) && index < ((object[])invokeParam).Length); CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.Target = ((object[])invokeParam)[index]; - pinnedResultObject = RuntimeAugments.GetRawAddrOfPinnedObject((IntPtr)CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle); + pinnedResultObject = CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.GetRawTargetAddress(); if (conversionParams._calleeArgs.IsArgPassedByRef()) { @@ -704,16 +742,15 @@ namespace Internal.Runtime.TypeLoader { // Input byref parameter has a null value conversionParams._dynamicInvokeByRefObjectArgs[index] = new DynamicInvokeByRefArgObjectWrapper(); - CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.Target = conversionParams._dynamicInvokeByRefObjectArgs[index]; - argPtr = RuntimeAugments.GetRawAddrOfPinnedObject((IntPtr)CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle) + IntPtr.Size; } else { // Input byref parameter has a non-null value conversionParams._dynamicInvokeByRefObjectArgs[index] = new DynamicInvokeByRefArgObjectWrapper { _object = conversionParams._dynamicInvokeParams[index] }; - CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.Target = conversionParams._dynamicInvokeByRefObjectArgs[index]; - argPtr = RuntimeAugments.GetRawAddrOfPinnedObject((IntPtr)CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle) + IntPtr.Size; } + + CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.Target = conversionParams._dynamicInvokeByRefObjectArgs[index]; + argPtr = CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.GetRawTargetAddress() + IntPtr.Size; } else { @@ -754,9 +791,10 @@ namespace Internal.Runtime.TypeLoader argumentsAsObjectArray = argumentsAsObjectArray ?? new object[conversionParams._callerArgs.NumFixedArgs()]; - conversionParams._callerArgs.GetArgType(out thValueType); + conversionParams._callerArgs.GetArgType(out thArgType); + Debug.Assert(!thArgType.IsNull()); - if (thValueType.IsNull()) + if (!thArgType.IsValueType()) { Debug.Assert(!isCallerArgPassedByRef); Debug.Assert(conversionParams._callerArgs.GetArgSize() == IntPtr.Size); @@ -766,11 +804,11 @@ namespace Internal.Runtime.TypeLoader { if (isCallerArgPassedByRef) { - argumentsAsObjectArray[arg] = RuntimeAugments.Box(thValueType.GetRuntimeTypeHandle(), new IntPtr(*((void**)pSrc))); + argumentsAsObjectArray[arg] = RuntimeAugments.Box(thArgType.GetRuntimeTypeHandle(), new IntPtr(*((void**)pSrc))); } else { - argumentsAsObjectArray[arg] = RuntimeAugments.Box(thValueType.GetRuntimeTypeHandle(), new IntPtr(pSrc)); + argumentsAsObjectArray[arg] = RuntimeAugments.Box(thArgType.GetRuntimeTypeHandle(), new IntPtr(pSrc)); } } } @@ -779,34 +817,14 @@ namespace Internal.Runtime.TypeLoader if (isCalleeArgPassedByRef == isCallerArgPassedByRef) { // Argument copies without adjusting calling convention. - switch (stackSizeCallee) + if (isCalleeArgPassedByRef) { - case 1: - case 2: - case 4: - *((int*)pDest) = *((int*)pSrc); - break; - - case 8: - *((long*)pDest) = *((long*)pSrc); - break; - - default: - if (isCalleeArgPassedByRef) - { - // even though this argument is passed by value, the actual calling convention - // passes a pointer to the value of the argument. - Debug.Assert(isCallerArgPassedByRef); - // Copy the pointer from the incoming arguments to the outgoing arguments. - *((void**)pDest) = *((void**)pSrc); - } - else - { - // In this case, the valuetype is passed directly on the stack, even though it is - // a non-integral size. - Buffer.MemoryCopy(pSrc, pDest, stackSizeCallee, stackSizeCallee); - } - break; + *((IntPtr*)pDest) = *(IntPtr*)pSrc; + } + else + { + CorElementType argElemType = conversionParams._calleeArgs.GetArgType(out thArgType); + ExtendingCopy_NoWriteBarrier(pSrc, pDest, argElemType, stackSizeCaller); } } else @@ -821,7 +839,9 @@ namespace Internal.Runtime.TypeLoader else { // Copy into the destination the data pointed at by the pointer in the source(caller) data. - Buffer.MemoryCopy(*(byte**)pSrc, pDest, stackSizeCaller, stackSizeCaller); + byte* pRealSrc = *(byte**)pSrc; + CorElementType argElemType = conversionParams._calleeArgs.GetArgType(out thArgType); + ExtendingCopy_NoWriteBarrier(pRealSrc, pDest, argElemType, stackSizeCaller); } } @@ -838,7 +858,7 @@ namespace Internal.Runtime.TypeLoader { // The calleeTransitionBlock is GC-protected, so we can now safely unpin the return value of DynamicInvokeParamHelperCore, // since we just copied it to the callee TB. - CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.Target = ""; + CallConversionParameters.s_pinnedGCHandles._dynamicInvokeArgHandle.Target = null; } arg++; @@ -956,16 +976,17 @@ namespace Internal.Runtime.TypeLoader // the target method called by the delegate. Use the callee's ArgIterator instead to get the return type info if (conversionParams._conversionInfo.IsAnyDynamicInvokerThunk) { - returnType = conversionParams._calleeArgs.GetReturnType(out thValueType, out forceByRefUnused); + returnType = conversionParams._calleeArgs.GetReturnType(out thRetType, out forceByRefUnused); } else { - returnType = conversionParams._callerArgs.GetReturnType(out thValueType, out forceByRefUnused); + returnType = conversionParams._callerArgs.GetReturnType(out thRetType, out forceByRefUnused); } - int returnSize = TypeHandle.GetElemSize(returnType, thValueType); + Debug.Assert(!thRetType.IsNull()); + int returnSize = TypeHandle.GetElemSize(returnType, thRetType); // Unbox result of object array delegate call - if (conversionParams._conversionInfo.IsObjectArrayDelegateThunk && !thValueType.IsNull() && pinnedResultObject != IntPtr.Zero) + if (conversionParams._conversionInfo.IsObjectArrayDelegateThunk && thRetType.IsValueType() && pinnedResultObject != IntPtr.Zero) pinnedResultObject += IntPtr.Size; // Process return values @@ -995,7 +1016,7 @@ namespace Internal.Runtime.TypeLoader bool useGCSafeCopy = false; - if ((returnType == CorElementType.ELEMENT_TYPE_CLASS) || !thValueType.IsNull()) + if ((returnType == CorElementType.ELEMENT_TYPE_CLASS) || thRetType.IsValueType()) { // The GC Safe copy assumes that memory pointers are pointer-aligned and copy length is a multiple of pointer-size if (isPointerAligned(incomingRetBufPointer) && isPointerAligned(sourceBuffer) && (returnSize % sizeof(IntPtr) == 0)) @@ -1068,7 +1089,7 @@ namespace Internal.Runtime.TypeLoader if (conversionParams._conversionInfo.IsObjectArrayDelegateThunk) { - if (!thValueType.IsNull()) + if (thRetType.IsValueType()) { returnValueToCopy = (void*)pinnedResultObject; #if _TARGET_X86_ @@ -1077,14 +1098,11 @@ namespace Internal.Runtime.TypeLoader if (returnValueToCopy == null) { // object array delegate thunk result is a null object. We'll fill the return buffer with 'returnSize' zeros in that case - memzeroPointer((byte*)(&((TransitionBlock*)callerTransitionBlock)->m_returnBlock), returnSize); + memzeroPointer((byte*)(&((TransitionBlock*)callerTransitionBlock)->m_returnBlock), sizeof(ReturnBlock)); } else { - if (isPointerAligned(&((TransitionBlock*)callerTransitionBlock)->m_returnBlock) && isPointerAligned(returnValueToCopy) && (returnSize % sizeof(IntPtr) == 0)) - RuntimeAugments.BulkMoveWithWriteBarrier(new IntPtr(&((TransitionBlock*)callerTransitionBlock)->m_returnBlock), new IntPtr(returnValueToCopy), returnSize); - else - Buffer.MemoryCopy(returnValueToCopy, &((TransitionBlock*)callerTransitionBlock)->m_returnBlock, returnSize, returnSize); + ExtendingCopy_WriteBarrier(returnValueToCopy, &((TransitionBlock*)callerTransitionBlock)->m_returnBlock, returnType, returnSize); } #endif } @@ -1096,18 +1114,30 @@ namespace Internal.Runtime.TypeLoader #endif } } - else if (conversionParams._conversionInfo.IsAnyDynamicInvokerThunk && !thValueType.IsNull()) + else if (conversionParams._conversionInfo.IsAnyDynamicInvokerThunk && thRetType.IsValueType()) { Debug.Assert(returnValueToCopy != null); - if (!conversionParams._callerArgs.HasRetBuffArg() && conversionParams._calleeArgs.HasRetBuffArg()) - returnValueToCopy = (void*)(new IntPtr(*((void**)returnValueToCopy)) + IntPtr.Size); + if (conversionParams._calleeArgs.GetReturnType(out thDummy, out dummyBool) == CorElementType.ELEMENT_TYPE_VOID) + { + // Invokers returning void need to return a null object + returnValueToCopy = null; + } + else + { + if (!conversionParams._callerArgs.HasRetBuffArg() && conversionParams._calleeArgs.HasRetBuffArg()) + returnValueToCopy = (void*)(new IntPtr(*((void**)returnValueToCopy)) + IntPtr.Size); - // Need to box value type before returning it - object returnValue = RuntimeAugments.Box(thValueType.GetRuntimeTypeHandle(), new IntPtr(returnValueToCopy)); - CallConversionParameters.s_pinnedGCHandles._returnObjectHandle.Target = returnValue; - pinnedResultObject = RuntimeAugments.GetRawAddrOfPinnedObject((IntPtr)CallConversionParameters.s_pinnedGCHandles._returnObjectHandle); - returnValueToCopy = (void*)&pinnedResultObject; + // Need to box value type before returning it + object returnValue = RuntimeAugments.Box(thRetType.GetRuntimeTypeHandle(), new IntPtr(returnValueToCopy)); + CallConversionParameters.s_pinnedGCHandles._returnObjectHandle.Target = returnValue; + pinnedResultObject = CallConversionParameters.s_pinnedGCHandles._returnObjectHandle.GetRawTargetAddress(); + returnValueToCopy = (void*)&pinnedResultObject; + } + // Since we've changed the returnValueToCopy here, we need to update the idea of what we are returning + returnType = CorElementType.ELEMENT_TYPE_OBJECT; + thRetType = default(TypeHandle); + returnSize = TypeHandle.GetElemSize(returnType, thRetType); #if _TARGET_X86_ ((TransitionBlock*)callerTransitionBlock)->m_returnBlock.returnValue = pinnedResultObject; @@ -1126,7 +1156,7 @@ namespace Internal.Runtime.TypeLoader #if CALLDESCR_FPARGREGSARERETURNREGS Debug.Assert(fpReturnSize <= sizeof(FloatArgumentRegisters)); - memzeroPointerAligned(calleeTransitionBlock + TransitionBlock.GetOffsetOfFloatArgumentRegisters(), sizeof(FloatArgumentRegisters)); + memzeroPointerAligned(callerTransitionBlock + TransitionBlock.GetOffsetOfFloatArgumentRegisters(), sizeof(FloatArgumentRegisters)); if (returnValueToCopy == null) { // object array delegate thunk result is a null object. We'll fill the return buffer with 'returnSize' zeros in that case @@ -1151,9 +1181,13 @@ namespace Internal.Runtime.TypeLoader #if _TARGET_X86_ SetupCallerActualReturnData(callerTransitionBlock); t_NonArgRegisterReturnSpace = ((TransitionBlock*)callerTransitionBlock)->m_returnBlock; +#elif _TARGET_WASM_ + throw new NotImplementedException(); #else #error Platform not implemented #endif + +#if !_TARGET_WASM_ if (fpReturnSize == 4) { conversionParams._invokeReturnValue = ReturnFloatingPointReturn4Thunk; @@ -1163,6 +1197,7 @@ namespace Internal.Runtime.TypeLoader conversionParams._invokeReturnValue = ReturnFloatingPointReturn8Thunk; } return; +#endif // !_TARGET_WASM_ #endif } @@ -1173,35 +1208,158 @@ namespace Internal.Runtime.TypeLoader return; #else // If we reach here, we are returning value in the integer registers. - if (conversionParams._conversionInfo.IsObjectArrayDelegateThunk && (!thValueType.IsNull())) + if (returnValueToCopy == null) { - if (returnValueToCopy == null) - { - // object array delegate thunk result is a null object. We'll fill the return buffer with 'returnSize' zeros in that case - memzeroPointer(callerTransitionBlock + TransitionBlock.GetOffsetOfArgumentRegisters(), returnSize); - } - else - { - if (isPointerAligned(callerTransitionBlock + TransitionBlock.GetOffsetOfArgumentRegisters()) && isPointerAligned(returnValueToCopy) && (returnSize % sizeof(IntPtr) == 0)) - RuntimeAugments.BulkMoveWithWriteBarrier(new IntPtr(callerTransitionBlock + TransitionBlock.GetOffsetOfArgumentRegisters()), new IntPtr(returnValueToCopy), returnSize); - else - Buffer.MemoryCopy(returnValueToCopy, callerTransitionBlock + TransitionBlock.GetOffsetOfArgumentRegisters(), returnSize, returnSize); - } + // Return result is a null object. We'll fill the return buffer with 'returnSize' zeros in that case + memzeroPointer(callerTransitionBlock + TransitionBlock.GetOffsetOfArgumentRegisters(), returnSize); } else { - Debug.Assert(returnValueToCopy != null); - - Buffer.MemoryCopy(returnValueToCopy, - callerTransitionBlock + TransitionBlock.GetOffsetOfArgumentRegisters(), - ArchitectureConstants.ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE_PRIMITIVE, - ArchitectureConstants.ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE_PRIMITIVE); + ExtendingCopy_WriteBarrier(returnValueToCopy, callerTransitionBlock + TransitionBlock.GetOffsetOfArgumentRegisters(), returnType, returnSize); } conversionParams._invokeReturnValue = ReturnIntegerPointReturnThunk; #endif } } + // + // Converting by-ref values to non-by-ref form requires the converter to be capable of taking a pointer to a small integer + // value anywhere in memory and then copying the referenced value into an ABI-compliant pointer-sized "slot" which + // faithfully communicates the value. In such cases, the argument slot prepared by the converter must conform to all + // sign/zero-extension rules mandated by the ABI. + // + // ARM32 requires all less-than-pointer-sized values to be sign/zero-extended when they are placed into pointer-sized + // slots (i.e., requires "producer-oriented" sign/zero-extension). x86/amd64 do not have this requirement (i.e., the + // unused high bytes of the pointer-sized slot are ignored by the consumer and are allowed to take on any value); however + // to reduce the need for ever more #ifs in this file, this behavior will not be #if'd away. (Its not wrong, its just unnecessary) + // + private static unsafe void ExtendingCopy_WriteBarrier(void* pSrcVoid, void* pDestVoid, CorElementType type, int typeSize) + { + byte* pSrc = (byte*)pSrcVoid; + byte* pDest = (byte*)pDestVoid; + + if (SignExtendType(type)) + SignExtend(pSrc, pDest, typeSize); + else if (ZeroExtendType(type)) + ZeroExtend(pSrc, pDest, typeSize); + else if (isPointerAligned(pSrc) && isPointerAligned(pDest) && (typeSize % sizeof(IntPtr) == 0)) + RuntimeAugments.BulkMoveWithWriteBarrier(new IntPtr(pDest), new IntPtr(pSrc), typeSize); + else + Buffer.MemoryCopy(pSrc, pDest, typeSize, typeSize); + } + + // + // Converting by-ref values to non-by-ref form requires the converter to be capable of taking a pointer to a small integer + // value anywhere in memory and then copying the referenced value into an ABI-compliant pointer-sized "slot" which + // faithfully communicates the value. In such cases, the argument slot prepared by the converter must conform to all + // sign/zero-extension rules mandated by the ABI. + // + // ARM32 requires all less-than-pointer-sized values to be sign/zero-extended when they are placed into pointer-sized + // slots (i.e., requires "producer-oriented" sign/zero-extension). x86/amd64 do not have this requirement (i.e., the + // unused high bytes of the pointer-sized slot are ignored by the consumer and are allowed to take on any value); however + // to reduce the need for ever more #ifs in this file, this behavior will not be #if'd away. (Its not wrong, its just unnecessary) + // + private static unsafe void ExtendingCopy_NoWriteBarrier(void* pSrcVoid, void* pDestVoid, CorElementType type, int typeSize) + { + byte* pSrc = (byte*)pSrcVoid; + byte* pDest = (byte*)pDestVoid; + + if (SignExtendType(type)) + SignExtend(pSrc, pDest, typeSize); + else if (ZeroExtendType(type)) + ZeroExtend(pSrc, pDest, typeSize); + else + Buffer.MemoryCopy(pSrc, pDest, typeSize, typeSize); + } + + private static bool SignExtendType(CorElementType type) + { + switch (type) + { + case CorElementType.ELEMENT_TYPE_I1: + case CorElementType.ELEMENT_TYPE_I2: +#if BIT64 + case CorElementType.ELEMENT_TYPE_I4: +#endif + return true; + + } + + return false; + } + + private static bool ZeroExtendType(CorElementType type) + { + switch (type) + { + case CorElementType.ELEMENT_TYPE_U1: + case CorElementType.ELEMENT_TYPE_BOOLEAN: + case CorElementType.ELEMENT_TYPE_CHAR: + case CorElementType.ELEMENT_TYPE_U2: +#if BIT64 + case CorElementType.ELEMENT_TYPE_U4: +#endif + return true; + + } + + return false; + } + + internal static unsafe void SignExtend(void* pSrcVoid, void* pDestVoid, int size) + { + byte* pSrc = (byte*)pSrcVoid; + byte* pDest = (byte*)pDestVoid; + + switch (size) + { + case 1: + *((IntPtr*)pDest) = new IntPtr(*(sbyte*)pSrc); + break; + + case 2: + *((IntPtr*)pDest) = new IntPtr(*(short*)pSrc); + break; + +#if BIT64 + // On 64 bit platforms, a 32 bit parameter may require truncation/extension + case 4: + *((IntPtr*)pDest) = new IntPtr(*(int*)pSrc); + break; +#endif + default: + Debug.Fail("Should only be called for sizes where sign extension is a meaningful concept"); + break; + } + } + + internal static unsafe void ZeroExtend(void* pSrcVoid, void* pDestVoid, int size) + { + byte* pSrc = (byte*)pSrcVoid; + byte* pDest = (byte*)pDestVoid; + + switch (size) + { + case 1: + *((UIntPtr*)pDest) = new UIntPtr(*(byte*)pSrc); + break; + + case 2: + *((UIntPtr*)pDest) = new UIntPtr(*(ushort*)pSrc); + break; + +#if BIT64 + // On 64 bit platforms, a 32 bit parameter may require truncation/extension + case 4: + *((UIntPtr*)pDest) = new UIntPtr(*(uint*)pSrc); + break; +#endif + default: + Debug.Fail("Should only be called for sizes where sign extension is a meaningful concept"); + break; + } + } + #if CALLINGCONVENTION_CALLEE_POPS private static unsafe void SetupCallerPopArgument(byte* callerTransitionBlock, ArgIterator callerArgs) { diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallInterceptor.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallInterceptor.cs index f7b090965f..b26dd803f5 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallInterceptor.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallInterceptor.cs @@ -38,6 +38,8 @@ #define ENREGISTERED_RETURNTYPE_MAXSIZE #define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE #define ENREGISTERED_PARAMTYPE_MAXSIZE +#elif WASM +#define _TARGET_WASM_ #else #error Unknown architecture! #endif @@ -440,8 +442,9 @@ namespace Internal.Runtime.CallInterceptor if (ofsCallee < 0) needsFloatArgs = true; - TypeHandle dummyTypeHandle; - if (calleeArgs.IsArgPassedByRef() && calleeArgs.GetArgType(out dummyTypeHandle) != CorElementType.ELEMENT_TYPE_BYREF) + TypeHandle argTypeHandle; + CorElementType argType = calleeArgs.GetArgType(out argTypeHandle); + if (calleeArgs.IsArgPassedByRef() && argType != CorElementType.ELEMENT_TYPE_BYREF) { callConversionOps.Add(new CallConversionOperation( CallConversionOperation.OpCode.COPY_X_BYTES_FROM_LOCALBLOCK_Y_OFFSET_Z_IN_LOCALBLOCK_TO_OFFSET_W_IN_TRANSITION_BLOCK, @@ -456,16 +459,69 @@ namespace Internal.Runtime.CallInterceptor } else { - callConversionOps.Add(new CallConversionOperation( - CallConversionOperation.OpCode.COPY_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK, - calleeArgs.GetArgSize(), - CallConversionInterpreter.ArgBlock, - i, - ofsCallee -#if CCCONVERTER_TRACE - , "Arg #" + i.LowLevelToString() + // + // Converting by-ref values to non-by-ref form requires the converter to be capable of taking a pointer to a small integer + // value anywhere in memory and then copying the referenced value into an ABI-compliant pointer-sized "slot" which + // faithfully communicates the value. In such cases, the argument slot prepared by the converter must conform to all + // sign/zero-extension rules mandated by the ABI. + // + // ARM32 requires all less-than-pointer-sized values to be sign/zero-extended when they are placed into pointer-sized + // slots (i.e., requires "producer-oriented" sign/zero-extension). x86/amd64 do not have this requirement (i.e., the + // unused high bytes of the pointer-sized slot are ignored by the consumer and are allowed to take on any value); however + // to reduce the need for ever more #ifs in this file, this behavior will not be #if'd away. (Its not wrong, its just unnecessary) + // + + switch (argType) + { + case CorElementType.ELEMENT_TYPE_I1: + case CorElementType.ELEMENT_TYPE_I2: +#if BIT64 + case CorElementType.ELEMENT_TYPE_I4: #endif - )); + callConversionOps.Add(new CallConversionOperation( + CallConversionOperation.OpCode.SIGNEXTEND_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK, + calleeArgs.GetArgSize(), + CallConversionInterpreter.ArgBlock, + i, + ofsCallee +#if CCCONVERTER_TRACE + , "Arg #" + i.LowLevelToString() +#endif + )); + break; + + case CorElementType.ELEMENT_TYPE_U1: + case CorElementType.ELEMENT_TYPE_BOOLEAN: + case CorElementType.ELEMENT_TYPE_U2: + case CorElementType.ELEMENT_TYPE_CHAR: +#if BIT64 + case CorElementType.ELEMENT_TYPE_U4: +#endif + callConversionOps.Add(new CallConversionOperation( + CallConversionOperation.OpCode.ZEROEXTEND_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK, + calleeArgs.GetArgSize(), + CallConversionInterpreter.ArgBlock, + i, + ofsCallee +#if CCCONVERTER_TRACE + , "Arg #" + i.LowLevelToString() +#endif + )); + break; + + default: + callConversionOps.Add(new CallConversionOperation( + CallConversionOperation.OpCode.COPY_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK, + calleeArgs.GetArgSize(), + CallConversionInterpreter.ArgBlock, + i, + ofsCallee +#if CCCONVERTER_TRACE + , "Arg #" + i.LowLevelToString() +#endif + )); + break; + } } } @@ -559,6 +615,12 @@ namespace Internal.Runtime.CallInterceptor case OpCode.COPY_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK: s = "COPY_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK"; break; + case OpCode.ZEROEXTEND_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK: + s = "ZEROEXTEND_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK"; + break; + case OpCode.SIGNEXTEND_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK: + s = "SIGNEXTEND_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK"; + break; case OpCode.COPY_X_BYTES_TO_LOCALBLOCK_Y_POINTER_Z_FROM_OFFSET_W_IN_TRANSITION_BLOCK: s = "COPY_X_BYTES_TO_LOCALBLOCK_Y_POINTER_Z_FROM_OFFSET_W_IN_TRANSITION_BLOCK"; break; @@ -583,6 +645,12 @@ namespace Internal.Runtime.CallInterceptor case OpCode.RETURN_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z: s = "RETURN_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z"; break; + case OpCode.RETURN_SIGNEXTENDED_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z: + s = "RETURN_SIGNEXTENDED_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z"; + break; + case OpCode.RETURN_ZEROEXTENDED_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z: + s = "RETURN_ZEROEXTENDED_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z"; + break; case OpCode.CALL_DESCR_MANAGED_WITH_RETBUF_AS_LOCALBLOCK_X_POINTER_Y_STACKSLOTS_Z_FPCALLINFO_W: s = "CALL_DESCR_MANAGED_WITH_RETBUF_AS_LOCALBLOCK_X_POINTER_Y_STACKSLOTS_Z_FPCALLINFO_W"; break; @@ -622,7 +690,7 @@ namespace Internal.Runtime.CallInterceptor } #else - public CallConversionOperation(OpCode op, int X, int Y, int Z, int W) + public CallConversionOperation(OpCode op, int X, int Y, int Z, int W) { this.Op = op; this.X = X; @@ -672,6 +740,8 @@ namespace Internal.Runtime.CallInterceptor SET_LOCALBLOCK_X_POINTER_Y_TO_OFFSET_Z_IN_LOCALBLOCK, COPY_X_BYTES_FROM_LOCALBLOCK_Y_OFFSET_Z_IN_LOCALBLOCK_TO_OFFSET_W_IN_TRANSITION_BLOCK, COPY_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK, + SIGNEXTEND_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK, + ZEROEXTEND_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK, COPY_X_BYTES_TO_LOCALBLOCK_Y_POINTER_Z_FROM_OFFSET_W_IN_TRANSITION_BLOCK, COPY_X_BYTES_TO_LOCALBLOCK_Y_OFFSET_Z_IN_LOCALBLOCK_FROM_OFFSET_W_IN_TRANSITION_BLOCK, CALL_INTERCEPTOR, @@ -680,6 +750,8 @@ namespace Internal.Runtime.CallInterceptor RETURN_RETBUF_FROM_OFFSET_X_IN_TRANSITION_BLOCK, RETURN_FLOATINGPOINT_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z, RETURN_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z, + RETURN_SIGNEXTENDED_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z, + RETURN_ZEROEXTENDED_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z, CALL_DESCR_MANAGED_WITH_RETBUF_AS_LOCALBLOCK_X_POINTER_Y_STACKSLOTS_Z_FPCALLINFO_W, CALL_DESCR_NATIVE_WITH_RETBUF_AS_LOCALBLOCK_X_POINTER_Y_STACKSLOTS_Z_FPCALLINFO_W, COPY_X_BYTES_FROM_RETBUF_TO_LOCALBLOCK_Y_POINTER_Z, @@ -827,6 +899,30 @@ namespace Internal.Runtime.CallInterceptor } break; + case CallConversionOperation.OpCode.SIGNEXTEND_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK: + { + void* pSrc = locals.GetLocalBlock(op.Y).GetRawMemoryPointer()[op.Z].ToPointer(); + void* pDst = locals.TransitionBlockPtr + op.W; + CallConverterThunk.SignExtend(pSrc, pDst, op.X); + +#if CCCONVERTER_TRACE + CallingConventionConverterLogger.WriteLine(" -> SignExtend " + op.X.LowLevelToString() + " bytes from [" + new IntPtr(pSrc).LowLevelToString() + "] to [" + new IntPtr(pDst).LowLevelToString() + "]"); +#endif + } + break; + + case CallConversionOperation.OpCode.ZEROEXTEND_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK: + { + void* pSrc = locals.GetLocalBlock(op.Y).GetRawMemoryPointer()[op.Z].ToPointer(); + void* pDst = locals.TransitionBlockPtr + op.W; + CallConverterThunk.ZeroExtend(pSrc, pDst, op.X); + +#if CCCONVERTER_TRACE + CallingConventionConverterLogger.WriteLine(" -> ZeroExtend " + op.X.LowLevelToString() + " bytes from [" + new IntPtr(pSrc).LowLevelToString() + "] to [" + new IntPtr(pDst).LowLevelToString() + "]"); +#endif + } + break; + case CallConversionOperation.OpCode.COPY_X_BYTES_TO_LOCALBLOCK_Y_OFFSET_Z_IN_LOCALBLOCK_FROM_OFFSET_W_IN_TRANSITION_BLOCK: { void* pSrc = locals.TransitionBlockPtr + op.W; @@ -922,6 +1018,49 @@ namespace Internal.Runtime.CallInterceptor #endif } break; + + case CallConversionOperation.OpCode.RETURN_SIGNEXTENDED_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z: + { +#if X86 + CallConverterThunk.SetupCallerActualReturnData(locals.TransitionBlockPtr); + fixed (ReturnBlock* retBlk = &CallConverterThunk.t_NonArgRegisterReturnSpace) + { + CallConverterThunk.SignExtend(locals.GetLocalBlock(op.X).GetRawMemoryPointer()[op.Y].ToPointer(), retBlk, op.Z); + } + locals.IntPtrReturnVal = CallConverterThunk.ReturnIntegerPointReturnThunk; +#else + byte* returnBlock = locals.TransitionBlockPtr + TransitionBlock.GetOffsetOfArgumentRegisters(); + CallConverterThunk.SignExtend(locals.GetLocalBlock(op.X).GetRawMemoryPointer()[op.Y].ToPointer(), returnBlock, op.Z); + locals.IntPtrReturnVal = CallConverterThunk.ReturnIntegerPointReturnThunk; +#endif + +#if CCCONVERTER_TRACE + CallingConventionConverterLogger.WriteLine(" -> SignExtend " + op.Z.LowLevelToString() + " bytes from [" + new IntPtr(locals.GetLocalBlock(op.X).GetRawMemoryPointer()[op.Y].ToPointer()).LowLevelToString() + "] to return block"); +#endif + } + break; + + case CallConversionOperation.OpCode.RETURN_ZEROEXTENDED_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z: + { +#if X86 + CallConverterThunk.SetupCallerActualReturnData(locals.TransitionBlockPtr); + fixed (ReturnBlock* retBlk = &CallConverterThunk.t_NonArgRegisterReturnSpace) + { + CallConverterThunk.ZeroExtend(locals.GetLocalBlock(op.X).GetRawMemoryPointer()[op.Y].ToPointer(), retBlk, op.Z); + } + locals.IntPtrReturnVal = CallConverterThunk.ReturnIntegerPointReturnThunk; +#else + byte* returnBlock = locals.TransitionBlockPtr + TransitionBlock.GetOffsetOfArgumentRegisters(); + CallConverterThunk.ZeroExtend(locals.GetLocalBlock(op.X).GetRawMemoryPointer()[op.Y].ToPointer(), returnBlock, op.Z); + locals.IntPtrReturnVal = CallConverterThunk.ReturnIntegerPointReturnThunk; +#endif + +#if CCCONVERTER_TRACE + CallingConventionConverterLogger.WriteLine(" -> ZeroExtend " + op.Z.LowLevelToString() + " bytes from [" + new IntPtr(locals.GetLocalBlock(op.X).GetRawMemoryPointer()[op.Y].ToPointer()).LowLevelToString() + "] to return block"); +#endif + } + break; + case CallConversionOperation.OpCode.RETURN_FLOATINGPOINT_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z: { #if CALLDESCR_FPARGREGSARERETURNREGS @@ -1142,28 +1281,41 @@ namespace Internal.Runtime.CallInterceptor /// public void FreeThunk() { - if (_thunkAddress != IntPtr.Zero) + FreeThunk(_thunkAddress); + _thunkAddress = IntPtr.Zero; + _id = 0; + } + + /// + /// Free the specified thunk. Once this is called, the old thunk address is invalid. + /// + /// + public static void FreeThunk(IntPtr thunkAddress) + { + if (thunkAddress != IntPtr.Zero) { lock (s_callInterceptors) { - if (_thunkAddress != IntPtr.Zero) + if (thunkAddress != IntPtr.Zero) { - RuntimeAugments.FreeThunk(s_thunkPoolHeap, _thunkAddress); - - _thunkAddress = IntPtr.Zero; - - s_callInterceptors[_id] = null; - if (s_countFreeCallInterceptorId == s_freeCallInterceptorIds.Count) + IntPtr context; + if (RuntimeAugments.TryGetThunkData(s_thunkPoolHeap, thunkAddress, out context, out _)) { - s_freeCallInterceptorIds.Add(_id); - } - else - { - s_freeCallInterceptorIds[s_countFreeCallInterceptorId] = _id; - } + int id = context.ToInt32(); + s_callInterceptors[id] = null; + if (s_countFreeCallInterceptorId == s_freeCallInterceptorIds.Count) + { + s_freeCallInterceptorIds.Add(id); + } + else + { + s_freeCallInterceptorIds[s_countFreeCallInterceptorId] = id; + } - s_countFreeCallInterceptorId++; - _id = 0; + s_countFreeCallInterceptorId++; + + RuntimeAugments.FreeThunk(s_thunkPoolHeap, thunkAddress); + } } } } @@ -1343,7 +1495,41 @@ namespace Internal.Runtime.CallInterceptor } else { - callConversionOps.Add(new CallConversionOperation(CallConversionOperation.OpCode.RETURN_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z, CallConversionInterpreter.ArgBlock, 0, checked((int)returnType.GetSize()))); + // + // Converting by-ref values to non-by-ref form requires the converter to be capable of taking a pointer to a small integer + // value anywhere in memory and then copying the referenced value into an ABI-compliant pointer-sized "slot" which + // faithfully communicates the value. In such cases, the argument slot prepared by the converter must conform to all + // sign/zero-extension rules mandated by the ABI. + // + // ARM32 requires all less-than-pointer-sized values to be sign/zero-extended when they are placed into pointer-sized + // slots (i.e., requires "producer-oriented" sign/zero-extension). x86/amd64 do not have this requirement (i.e., the + // unused high bytes of the pointer-sized slot are ignored by the consumer and are allowed to take on any value); however + // to reduce the need for ever more #ifs in this file, this behavior will not be #if'd away. (Its not wrong, its just unnecessary) + // + switch (returnType.GetCorElementType()) + { + case CorElementType.ELEMENT_TYPE_I1: + case CorElementType.ELEMENT_TYPE_I2: +#if BIT64 + case CorElementType.ELEMENT_TYPE_I4: +#endif + callConversionOps.Add(new CallConversionOperation(CallConversionOperation.OpCode.RETURN_SIGNEXTENDED_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z, CallConversionInterpreter.ArgBlock, 0, checked((int)returnType.GetSize()))); + break; + + case CorElementType.ELEMENT_TYPE_U1: + case CorElementType.ELEMENT_TYPE_BOOLEAN: + case CorElementType.ELEMENT_TYPE_U2: + case CorElementType.ELEMENT_TYPE_CHAR: +#if BIT64 + case CorElementType.ELEMENT_TYPE_U4: +#endif + callConversionOps.Add(new CallConversionOperation(CallConversionOperation.OpCode.RETURN_ZEROEXTENDED_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z, CallConversionInterpreter.ArgBlock, 0, checked((int)returnType.GetSize()))); + break; + + default: + callConversionOps.Add(new CallConversionOperation(CallConversionOperation.OpCode.RETURN_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z, CallConversionInterpreter.ArgBlock, 0, checked((int)returnType.GetSize()))); + break; + } } Debug.Assert(callConversionOps[0].Op == CallConversionOperation.OpCode.ALLOC_X_LOCALBLOCK_BYTES_FOR_BLOCK_Y); diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallingConventions.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallingConventions.cs index 6265741b9a..66dc46496c 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallingConventions.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallingConventions.cs @@ -37,6 +37,8 @@ #define ENREGISTERED_RETURNTYPE_MAXSIZE #define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE #define ENREGISTERED_PARAMTYPE_MAXSIZE +#elif WASM +#define _TARGET_WASM_ #else #error Unknown architecture! #endif @@ -128,7 +130,7 @@ namespace Internal.Runtime.CallConverter } public bool IsHFA() { -#if !ARM +#if !ARM && !ARM64 return false; #else if (_isByRef) @@ -141,14 +143,17 @@ namespace Internal.Runtime.CallConverter public CorElementType GetHFAType() { + Debug.Assert(IsHFA()); #if ARM if (RequiresAlign8()) { return CorElementType.ELEMENT_TYPE_R8; } -#endif -#if ARM64 - Debug.Assert(false); // HFA8 detection not yet implemented for this platform +#elif ARM64 + if (_eeType->FieldAlignmentRequirement == IntPtr.Size) + { + return CorElementType.ELEMENT_TYPE_R8; + } #endif return CorElementType.ELEMENT_TYPE_R4; } @@ -271,8 +276,12 @@ namespace Internal.Runtime.CallConverter public int m_idxStack; // First stack slot used (or -1) public int m_cStack; // Count of stack slots used (or 0) +#if _TARGET_ARM64_ + public bool m_isSinglePrecision; // For determining if HFA is single or double precision +#endif + #if _TARGET_ARM_ - public bool m_fRequires64BitAlignment; // True if the argument should always be aligned (in registers or on the stack + public bool m_fRequires64BitAlignment; // True if the argument should always be aligned (in registers or on the stack #endif // Initialize to represent a non-placed argument (no register or stack slots referenced). @@ -284,6 +293,11 @@ namespace Internal.Runtime.CallConverter m_cGenReg = 0; m_idxStack = -1; m_cStack = 0; + +#if _TARGET_ARM64_ + m_isSinglePrecision = false; +#endif + #if _TARGET_ARM_ m_fRequires64BitAlignment = false; #endif @@ -343,14 +357,10 @@ namespace Internal.Runtime.CallConverter public int NumFixedArgs() { return _parameterTypes != null ? _parameterTypes.Length : 0; } // Argument iteration. - public CorElementType GetArgumentType(int argNum, out TypeHandle thValueType) + public CorElementType GetArgumentType(int argNum, out TypeHandle thArgType) { - thValueType = _parameterTypes[argNum]; - CorElementType returnValue = thValueType.GetCorElementType(); - if (!thValueType.IsValueType()) - { - thValueType = default(TypeHandle); - } + thArgType = _parameterTypes[argNum]; + CorElementType returnValue = thArgType.GetCorElementType(); return returnValue; } @@ -361,15 +371,10 @@ namespace Internal.Runtime.CallConverter default(TypeHandle); } - public CorElementType GetReturnType(out TypeHandle thValueType) + public CorElementType GetReturnType(out TypeHandle thRetType) { - thValueType = _returnType; - CorElementType returnValue = thValueType.GetCorElementType(); - if (!thValueType.IsValueType()) - { - thValueType = default(TypeHandle); - } - return returnValue; + thRetType = _returnType; + return thRetType.GetCorElementType(); } #if CCCONVERTER_TRACE @@ -413,13 +418,13 @@ namespace Internal.Runtime.CallConverter public int NumFixedArgs() { return _argData.NumFixedArgs() + (_extraFunctionPointerArg ? 1 : 0) + (_extraObjectFirstArg ? 1 : 0); } // Argument iteration. - public CorElementType GetArgumentType(int argNum, out TypeHandle thValueType, out bool forceByRefReturn) + public CorElementType GetArgumentType(int argNum, out TypeHandle thArgType, out bool forceByRefReturn) { forceByRefReturn = false; if (_extraObjectFirstArg && argNum == 0) { - thValueType = default(TypeHandle); + thArgType = new TypeHandle(false, typeof(object).TypeHandle); return CorElementType.ELEMENT_TYPE_CLASS; } @@ -431,20 +436,21 @@ namespace Internal.Runtime.CallConverter if (_extraFunctionPointerArg && argNum == _argData.NumFixedArgs()) { - thValueType = default(TypeHandle); + thArgType = new TypeHandle(false, typeof(IntPtr).TypeHandle); return CorElementType.ELEMENT_TYPE_I; } - return _argData.GetArgumentType(argNum, out thValueType); + + return _argData.GetArgumentType(argNum, out thArgType); } - public CorElementType GetReturnType(out TypeHandle thValueType, out bool forceByRefReturn) + public CorElementType GetReturnType(out TypeHandle thRetType, out bool forceByRefReturn) { if (_forcedByRefParams != null && _forcedByRefParams.Length > 0) forceByRefReturn = _forcedByRefParams[0]; else forceByRefReturn = false; - return _argData.GetReturnType(out thValueType); + return _argData.GetReturnType(out thRetType); } #if CCCONVERTER_TRACE @@ -569,7 +575,7 @@ namespace Internal.Runtime.CallConverter // // typ: the signature type //========================================================================= - private static bool IsArgumentInRegister(ref int pNumRegistersUsed, CorElementType typ, TypeHandle thValueType) + private static bool IsArgumentInRegister(ref int pNumRegistersUsed, CorElementType typ, TypeHandle thArgType) { // LIMITED_METHOD_CONTRACT; if ((pNumRegistersUsed) < ArchitectureConstants.NUM_ARGUMENT_REGISTERS) @@ -600,7 +606,7 @@ namespace Internal.Runtime.CallConverter case CorElementType.ELEMENT_TYPE_VALUETYPE: { // On ProjectN valuetypes of integral size are passed enregistered - int structSize = TypeHandle.GetElemSize(typ, thValueType); + int structSize = TypeHandle.GetElemSize(typ, thArgType); switch (structSize) { case 1: @@ -720,17 +726,20 @@ namespace Internal.Runtime.CallConverter #if _TARGET_X86_ // x86 is special as always - // ret += this.HasThis() ? offsetof(ArgumentRegisters, EDX) : offsetof(ArgumentRegisters, ECX); // DESKTOP BEHAVIOR ret += this.HasThis() ? ArgumentRegisters.GetOffsetOfEdx() : ArgumentRegisters.GetOffsetOfEcx(); int ret = TransitionBlock.GetOffsetOfArgs(); #else // RetBuf arg is in the first argument register by default int ret = TransitionBlock.GetOffsetOfArgumentRegisters(); +#if _TARGET_ARM64_ + ret += ArgumentRegisters.GetOffsetOfx8(); +#else // But if there is a this pointer, push it to the second. if (this.HasThis()) ret += IntPtr.Size; -#endif +#endif // _TARGET_ARM64_ +#endif // _TARGET_X86_ return ret; } @@ -753,7 +762,7 @@ namespace Internal.Runtime.CallConverter ret += IntPtr.Size; } - if (this.HasRetBuffArg()) + if (this.HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) { ret += IntPtr.Size; } @@ -792,7 +801,7 @@ namespace Internal.Runtime.CallConverter ret += IntPtr.Size; } - if (this.HasRetBuffArg()) + if (this.HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) { ret += IntPtr.Size; } @@ -822,7 +831,7 @@ namespace Internal.Runtime.CallConverter if (this.HasThis()) numRegistersUsed++; - if (this.HasRetBuffArg()) + if (this.HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) { #if !_TARGET_X86_ numRegistersUsed++; @@ -866,7 +875,7 @@ namespace Internal.Runtime.CallConverter case CallingConvention.ManagedStatic: case CallingConvention.ManagedInstance: _numRegistersUsed = numRegistersUsed; - // DESKTOP BEHAVIOR m_curOfs = (int)(TransitionBlock.GetOffsetOfArgs() + SizeOfArgStack()); + // DESKTOP BEHAVIOR _curOfs = (int)(TransitionBlock.GetOffsetOfArgs() + SizeOfArgStack()); _curOfs = (int)(TransitionBlock.GetOffsetOfArgs() + initialArgOffset); break; @@ -875,16 +884,16 @@ namespace Internal.Runtime.CallConverter break; } #else - m_numRegistersUsed = numRegistersUsed; -// DESKTOP BEHAVIOR m_curOfs = (int)(TransitionBlock.GetOffsetOfArgs() + SizeOfArgStack()); - m_curOfs = (int)(TransitionBlock.GetOffsetOfArgs() + initialArgOffset); + _numRegistersUsed = numRegistersUsed; +// DESKTOP BEHAVIOR _curOfs = (int)(TransitionBlock.GetOffsetOfArgs() + SizeOfArgStack()); + _curOfs = (int)(TransitionBlock.GetOffsetOfArgs() + initialArgOffset); #endif #elif _TARGET_AMD64_ #if UNIX_AMD64_ABI - m_idxGenReg = numRegistersUsed; - m_idxStack = 0; - m_idxFPReg = 0; + _idxGenReg = numRegistersUsed; + _idxStack = 0; + _idxFPReg = 0; #else _curOfs = TransitionBlock.GetOffsetOfArgs() + numRegistersUsed * IntPtr.Size; #endif @@ -898,35 +907,48 @@ namespace Internal.Runtime.CallConverter _idxStack = 0; _idxFPReg = 0; +#elif _TARGET_WASM_ + throw new NotImplementedException(); #else PORTABILITY_ASSERT("ArgIterator::GetNextOffset"); #endif +#if !_TARGET_WASM_ _argNum = (_skipFirstArg ? 1 : 0); _ITERATION_STARTED = true; +#endif // !_TARGET_WASM_ } if (_argNum >= this.NumFixedArgs()) return TransitionBlock.InvalidOffset; - TypeHandle thValueType; - CorElementType argType = this.GetArgumentType(_argNum, out thValueType, out _argForceByRef); + CorElementType argType = this.GetArgumentType(_argNum, out _argTypeHandle, out _argForceByRef); _argTypeHandleOfByRefParam = (argType == CorElementType.ELEMENT_TYPE_BYREF ? _argData.GetByRefArgumentType(_argNum) : default(TypeHandle)); _argNum++; - int argSize = TypeHandle.GetElemSize(argType, thValueType); + int argSize = TypeHandle.GetElemSize(argType, _argTypeHandle); + +#if _TARGET_ARM64_ + // NOT DESKTOP BEHAVIOR: The S and D registers overlap, and the UniversalTransitionThunk copies D registers to the transition blocks. We'll need + // to work with the D registers here as well. + if (argType == CorElementType.ELEMENT_TYPE_VALUETYPE && _argTypeHandle.IsHFA() && _argTypeHandle.GetHFAType() == CorElementType.ELEMENT_TYPE_R4) + { + argSize *= 2; + } +#endif _argType = argType; _argSize = argSize; - _argTypeHandle = thValueType; argType = _argForceByRef ? CorElementType.ELEMENT_TYPE_BYREF : argType; argSize = _argForceByRef ? IntPtr.Size : argSize; +#pragma warning disable 219,168 // Unused local int argOfs; +#pragma warning restore 219,168 #if _TARGET_X86_ #if FEATURE_INTERPRETER @@ -937,13 +959,13 @@ namespace Internal.Runtime.CallConverter return argOfs; } #endif - if (IsArgumentInRegister(ref _numRegistersUsed, argType, thValueType)) + if (IsArgumentInRegister(ref _numRegistersUsed, argType, _argTypeHandle)) { return TransitionBlock.GetOffsetOfArgumentRegisters() + (ArchitectureConstants.NUM_ARGUMENT_REGISTERS - _numRegistersUsed) * IntPtr.Size; } - // DESKTOP BEHAVIOR m_curOfs -= ArchitectureConstants.StackElemSize(argSize); - // DESKTOP BEHAVIOR return m_curOfs; + // DESKTOP BEHAVIOR _curOfs -= ArchitectureConstants.StackElemSize(argSize); + // DESKTOP BEHAVIOR return _curOfs; argOfs = _curOfs; _curOfs += ArchitectureConstants.StackElemSize(argSize); Debug.Assert(argOfs >= TransitionBlock.GetOffsetOfArgs()); @@ -967,7 +989,7 @@ namespace Internal.Runtime.CallConverter case CorElementType.ELEMENT_TYPE_VALUETYPE: { - // UNIXTODO: Passing of structs, HFAs. For now, use the Windows convention. + // UNIXTODO: FEATURE_UNIX_AMD64_STRUCT_PASSING: Passing of structs, HFAs. For now, use the Windows convention. argSize = IntPtr.Size; break; } @@ -1063,11 +1085,11 @@ namespace Internal.Runtime.CallConverter { // Value type case: extract the alignment requirement, note that this has to handle // the interop "native value types". - fRequiresAlign64Bit = thValueType.RequiresAlign8(); + fRequiresAlign64Bit = _argTypeHandle.RequiresAlign8(); // Handle HFAs: packed structures of 1-4 floats or doubles that are passed in FP argument // registers if possible. - if (thValueType.IsHFA()) + if (_argTypeHandle.IsHFA()) fFloatingPoint = true; break; @@ -1213,10 +1235,11 @@ namespace Internal.Runtime.CallConverter { // Handle HFAs: packed structures of 2-4 floats or doubles that are passed in FP argument // registers if possible. - if (thValueType.IsHFA()) + if (_argTypeHandle.IsHFA()) { - CorElementType type = thValueType.GetHFAType(); - cFPRegs = (type == CorElementType.ELEMENT_TYPE_R4) ? (argSize / sizeof(float)) : (argSize / sizeof(double)); + CorElementType type = _argTypeHandle.GetHFAType(); + // DESKTOP BEHAVIOR cFPRegs = (type == CorElementType.ELEMENT_TYPE_R4) ? (argSize / sizeof(float)) : (argSize / sizeof(double)); + cFPRegs = argSize / sizeof(double); } else { @@ -1267,6 +1290,8 @@ namespace Internal.Runtime.CallConverter argOfs = TransitionBlock.GetOffsetOfArgs() + _idxStack * 8; _idxStack += cArgSlots; return argOfs; +#elif _TARGET_WASM_ + throw new NotImplementedException(); #else #error PORTABILITY_ASSERT("ArgIterator::GetNextOffset"); #endif @@ -1309,7 +1334,7 @@ namespace Internal.Runtime.CallConverter if (this.HasThis()) numRegistersUsed++; - if (this.HasRetBuffArg()) + if (this.HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) { // DESKTOP BEHAVIOR numRegistersUsed++; // On ProjectN ret buff arg is passed on the call stack as the top stack arg @@ -1351,15 +1376,15 @@ namespace Internal.Runtime.CallConverter int nArgs = this.NumFixedArgs(); for (int i = (_skipFirstArg ? 1 : 0); i < nArgs; i++) { - TypeHandle thValueType; + TypeHandle thArgType; bool argForcedToBeByref; - CorElementType type = this.GetArgumentType(i, out thValueType, out argForcedToBeByref); + CorElementType type = this.GetArgumentType(i, out thArgType, out argForcedToBeByref); if (argForcedToBeByref) type = CorElementType.ELEMENT_TYPE_BYREF; - if (!IsArgumentInRegister(ref numRegistersUsed, type, thValueType)) + if (!IsArgumentInRegister(ref numRegistersUsed, type, thArgType)) { - int structSize = TypeHandle.GetElemSize(type, thValueType); + int structSize = TypeHandle.GetElemSize(type, thArgType); nSizeOfArgStack += ArchitectureConstants.StackElemSize(structSize); @@ -1505,7 +1530,11 @@ namespace Internal.Runtime.CallConverter if (!_argTypeHandle.IsNull() && _argTypeHandle.IsHFA()) { CorElementType type = _argTypeHandle.GetHFAType(); - pLoc->m_cFloatReg = (type == CorElementType.ELEMENT_TYPE_R4) ? GetArgSize() / sizeof(float) : GetArgSize() / sizeof(double); + bool isFloatType = (type == CorElementType.ELEMENT_TYPE_R4); + + // DESKTOP BEHAVIOR pLoc->m_cFloatReg = isFloatType ? GetArgSize() / sizeof(float) : GetArgSize() / sizeof(double); + pLoc->m_cFloatReg = GetArgSize() / sizeof(double); + pLoc->m_isSinglePrecision = isFloatType; } else { @@ -1530,7 +1559,7 @@ namespace Internal.Runtime.CallConverter } else { - pLoc->m_idxStack = TransitionBlock.GetArgumentIndexFromOffset(argOffset) - 8; + pLoc->m_idxStack = TransitionBlock.GetStackArgumentIndexFromOffset(argOffset); pLoc->m_cStack = cSlots; } } @@ -1542,6 +1571,14 @@ namespace Internal.Runtime.CallConverter { // LIMITED_METHOD_CONTRACT; + if (argOffset == TransitionBlock.StructInRegsOffset) + { + // We always already have argLocDesc for structs passed in registers, we + // compute it in the GetNextOffset for those since it is always needed. + Debug.Assert(false); + return; + } + pLoc->Init(); if (TransitionBlock.IsFloatArgumentRegisterOffset(argOffset)) @@ -1568,7 +1605,7 @@ namespace Internal.Runtime.CallConverter pLoc->m_cStack = cSlots; } } -#endif // _TARGET_ARM64_ && UNIX_AMD64_ABI +#endif // _TARGET_AMD64_ && UNIX_AMD64_ABI private int _nSizeOfArgStack; // Cached value of SizeOfArgStack @@ -1588,9 +1625,9 @@ namespace Internal.Runtime.CallConverter #if _TARGET_AMD64_ #if UNIX_AMD64_ABI - int m_idxGenReg; - int m_idxStack; - int m_idxFPReg; + int _idxGenReg; + int _idxStack; + int _idxFPReg; #else private int _curOfs; // Current position of the stack iterator #endif @@ -1642,7 +1679,7 @@ namespace Internal.Runtime.CallConverter // RETURN_FP_SIZE_SHIFT = 8, // The rest of the flags is cached value of GetFPReturnSize // }; - internal static void ComputeReturnValueTreatment(CorElementType type, TypeHandle thValueType, bool isVarArgMethod, out bool usesRetBuffer, out uint fpReturnSize) + internal static void ComputeReturnValueTreatment(CorElementType type, TypeHandle thRetType, bool isVarArgMethod, out bool usesRetBuffer, out uint fpReturnSize) { usesRetBuffer = false; @@ -1671,22 +1708,29 @@ namespace Internal.Runtime.CallConverter case CorElementType.ELEMENT_TYPE_VALUETYPE: #if ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE { - Debug.Assert(!thValueType.IsNull()); + Debug.Assert(!thRetType.IsNull() && thRetType.IsValueType()); #if FEATURE_HFA - if (thValueType.IsHFA() && !isVarArgMethod) + if (thRetType.IsHFA() && !isVarArgMethod) { - CorElementType hfaType = thValueType.GetHFAType(); + CorElementType hfaType = thRetType.GetHFAType(); +#if _TARGET_ARM64_ + // DESKTOP BEHAVIOR fpReturnSize = (hfaType == CorElementType.ELEMENT_TYPE_R4) ? (4 * (uint)sizeof(float)) : (4 * (uint)sizeof(double)); + // S and D registers overlap. Since we copy D registers in the UniversalTransitionThunk, we'll + // thread floats like doubles during copying. + fpReturnSize = 4 * (uint)sizeof(double); +#else fpReturnSize = (hfaType == CorElementType.ELEMENT_TYPE_R4) ? (4 * (uint)sizeof(float)) : (4 * (uint)sizeof(double)); +#endif break; } #endif - uint size = thValueType.GetSize(); + uint size = thRetType.GetSize(); #if _TARGET_X86_ || _TARGET_AMD64_ // Return value types of size which are not powers of 2 using a RetBuffArg @@ -1713,12 +1757,12 @@ namespace Internal.Runtime.CallConverter private void ComputeReturnFlags() { - TypeHandle thValueType; - CorElementType type = this.GetReturnType(out thValueType, out _RETURN_HAS_RET_BUFFER); + TypeHandle thRetType; + CorElementType type = this.GetReturnType(out thRetType, out _RETURN_HAS_RET_BUFFER); if (!_RETURN_HAS_RET_BUFFER) { - ComputeReturnValueTreatment(type, thValueType, this.IsVarArg(), out _RETURN_HAS_RET_BUFFER, out _fpReturnSize); + ComputeReturnValueTreatment(type, thRetType, this.IsVarArg(), out _RETURN_HAS_RET_BUFFER, out _fpReturnSize); } _RETURN_FLAGS_COMPUTED = true; @@ -1745,5 +1789,15 @@ namespace Internal.Runtime.CallConverter Debug.Assert(0 == (alignment & (alignment - 1))); return 0 == (val.ToInt64() & (alignment - 1)); } + + public static bool IsRetBuffPassedAsFirstArg() + { + // WRAPPER_NO_CONTRACT; +#if !_TARGET_ARM64_ + return true; +#else + return false; +#endif + } }; } diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ConstrainedCallSupport.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ConstrainedCallSupport.cs index e24353fae9..f00b66e76e 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ConstrainedCallSupport.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ConstrainedCallSupport.cs @@ -2,16 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. - -// The function signature of the following architectures is know for the constrained call helper functions -#if ARM -#elif X86 -#elif AMD64 -#elif ARM64 -#else -#error Unknown architecture! -#endif - using System; using System.Collections.Generic; using System.Text; @@ -34,9 +24,8 @@ namespace Internal.Runtime.TypeLoader private delegate IntPtr ResolveCallOnReferenceTypeDel(ref object thisPtr, IntPtr callDescIntPtr); private delegate IntPtr ResolveCallOnValueTypeDel(IntPtr thisPtr, IntPtr callDescIntPtr); #endif - private delegate IntPtr RuntimeCacheFuncSignatureDel(IntPtr context, IntPtr callDescIntPtr, object contextObject, out IntPtr auxResult); -#if !CORERT +#if PROJECTN [DllImport("*", ExactSpelling = true, EntryPoint = "ConstrainedCallSupport_GetStubs")] private extern static unsafe void ConstrainedCallSupport_GetStubs(out IntPtr constrainedCallSupport_DerefThisAndCall_CommonCallingStub, out IntPtr constrainedCallSupport_DirectConstrainedCall_CommonCallingStub); #endif @@ -54,7 +43,7 @@ namespace Internal.Runtime.TypeLoader static ConstrainedCallSupport() { // TODO: export this unmanaged API in CoreRT -#if !CORERT +#if PROJECTN ConstrainedCallSupport_GetStubs(out s_constrainedCallSupport_DerefThisAndCall_CommonCallingStub, out s_constrainedCallSupport_DirectConstrainedCall_CommonCallingStub); #else @@ -110,9 +99,9 @@ namespace Internal.Runtime.TypeLoader private static IntPtr s_boxAndToStringFuncPtr; private static IntPtr s_boxAndGetHashCodeFuncPtr; private static IntPtr s_boxAndEqualsFuncPtr; - private static int s_resolveCallOnReferenceTypeCacheMissFunc; private static LowLevelDictionary> s_nonGenericConstrainedCallDescs = new LowLevelDictionary>(); + private static LowLevelDictionary> s_nonGenericConstrainedCallDescsDirect = new LowLevelDictionary>(); public static unsafe IntPtr GetDirectConstrainedCallPtr(RuntimeTypeHandle constraintType, RuntimeTypeHandle constrainedMethodType, int constrainedMethodSlot) { @@ -139,14 +128,16 @@ namespace Internal.Runtime.TypeLoader public static unsafe IntPtr Get(RuntimeTypeHandle constraintType, RuntimeTypeHandle constrainedMethodType, int constrainedMethodSlot, bool directConstrainedCall = false) { - lock (s_nonGenericConstrainedCallDescs) + LowLevelDictionary> nonGenericConstrainedCallDescsDirect = directConstrainedCall ? s_nonGenericConstrainedCallDescsDirect : s_nonGenericConstrainedCallDescs; + + lock (nonGenericConstrainedCallDescsDirect) { // Get list of constrained call descs associated with a given type LowLevelList associatedCallDescs; - if (!s_nonGenericConstrainedCallDescs.TryGetValue(constraintType, out associatedCallDescs)) + if (!nonGenericConstrainedCallDescsDirect.TryGetValue(constraintType, out associatedCallDescs)) { associatedCallDescs = new LowLevelList(); - s_nonGenericConstrainedCallDescs.Add(constraintType, associatedCallDescs); + nonGenericConstrainedCallDescsDirect.Add(constraintType, associatedCallDescs); } // Perform linear scan of associated call descs to see if one matches @@ -219,8 +210,6 @@ namespace Internal.Runtime.TypeLoader s_boxAndToStringFuncPtr = Intrinsics.AddrOf((BoxAndCallDel)BoxAndToString); s_boxAndGetHashCodeFuncPtr = Intrinsics.AddrOf((BoxAndCallDel)BoxAndGetHashCode); s_boxAndEqualsFuncPtr = Intrinsics.AddrOf((BoxAndCallDel2)BoxAndEquals); - s_resolveCallOnReferenceTypeCacheMissFunc = RuntimeAugments.RegisterResolutionFunctionWithRuntimeCache( - Intrinsics.AddrOf((RuntimeCacheFuncSignatureDel)ResolveCallOnReferenceTypeCacheMiss)); } #if ARM @@ -229,16 +218,13 @@ namespace Internal.Runtime.TypeLoader private static unsafe IntPtr ResolveCallOnReferenceType(ref object thisPtr, IntPtr callDescIntPtr) #endif { - IntPtr ignoredAuxResult; - return RuntimeAugments.RuntimeCacheLookup(thisPtr.GetType().TypeHandle.ToIntPtr(), callDescIntPtr, s_resolveCallOnReferenceTypeCacheMissFunc, thisPtr, out ignoredAuxResult); - } - - private static unsafe IntPtr ResolveCallOnReferenceTypeCacheMiss(IntPtr context, IntPtr callDescIntPtr, object contextObject, out IntPtr auxResult) - { - auxResult = IntPtr.Zero; - NonGenericConstrainedCallDesc* callDesc = (NonGenericConstrainedCallDesc*)callDescIntPtr; - IntPtr target = RuntimeAugments.ResolveDispatch(contextObject, callDesc->_constrainedMethodType, callDesc->_constrainedMethodSlot); - return GetThunkThatDereferencesThisPointerAndTailCallsTarget(target); + return RuntimeAugments.RuntimeCacheLookup(thisPtr.GetType().TypeHandle.ToIntPtr(), callDescIntPtr, + (IntPtr context, IntPtr callDescPtr, object contextObject, ref IntPtr auxResult) => + { + NonGenericConstrainedCallDesc* callDesc = (NonGenericConstrainedCallDesc*)callDescPtr; + IntPtr target = RuntimeAugments.ResolveDispatch(contextObject, callDesc->_constrainedMethodType, callDesc->_constrainedMethodSlot); + return GetThunkThatDereferencesThisPointerAndTailCallsTarget(target); + }, thisPtr, out _); } // Resolve a constrained call in case where the call is an MDIL constrained call directly through a function pointer located in the generic dictionary @@ -394,7 +380,6 @@ namespace Internal.Runtime.TypeLoader private RuntimeMethodHandle _constrainedMethod; private static IntPtr s_resolveCallOnReferenceTypeFuncPtr; - private static int s_resolveCallOnReferenceTypeCacheMissFunc; private static IntPtr s_resolveCallOnValueTypeFuncPtr; private static LowLevelDictionary> s_genericConstrainedCallDescs = new LowLevelDictionary>(); @@ -460,8 +445,6 @@ namespace Internal.Runtime.TypeLoader { s_resolveCallOnReferenceTypeFuncPtr = Intrinsics.AddrOf((ResolveCallOnReferenceTypeDel)ResolveCallOnReferenceType); s_resolveCallOnValueTypeFuncPtr = Intrinsics.AddrOf((ResolveCallOnValueTypeDel)ResolveCallOnValueType); - s_resolveCallOnReferenceTypeCacheMissFunc = RuntimeAugments.RegisterResolutionFunctionWithRuntimeCache( - Intrinsics.AddrOf((RuntimeCacheFuncSignatureDel)ResolveCallOnReferenceTypeCacheMiss)); } #if ARM @@ -470,29 +453,26 @@ namespace Internal.Runtime.TypeLoader private static unsafe IntPtr ResolveCallOnReferenceType(ref object thisPtr, IntPtr callDescIntPtr) #endif { - IntPtr ignoredAuxResult; - return RuntimeAugments.RuntimeCacheLookup(thisPtr.GetType().TypeHandle.ToIntPtr(), callDescIntPtr, s_resolveCallOnReferenceTypeCacheMissFunc, thisPtr, out ignoredAuxResult); - } + return RuntimeAugments.RuntimeCacheLookup(thisPtr.GetType().TypeHandle.ToIntPtr(), callDescIntPtr, + (IntPtr context, IntPtr callDescPtr, object contextObject, ref IntPtr auxResult) => + { + // Perform a normal GVM dispatch, then change the function pointer to dereference the this pointer. + GenericConstrainedCallDesc* callDesc = (GenericConstrainedCallDesc*)callDescPtr; + IntPtr target = RuntimeAugments.GVMLookupForSlot(contextObject.GetType().TypeHandle, callDesc->_constrainedMethod); - private static unsafe IntPtr ResolveCallOnReferenceTypeCacheMiss(IntPtr context, IntPtr callDescIntPtr, object contextObject, out IntPtr auxResult) - { - auxResult = IntPtr.Zero; + if (FunctionPointerOps.IsGenericMethodPointer(target)) + { + GenericMethodDescriptor* genMethodDesc = FunctionPointerOps.ConvertToGenericDescriptor(target); + IntPtr actualCodeTarget = GetThunkThatDereferencesThisPointerAndTailCallsTarget(genMethodDesc->MethodFunctionPointer); - // Perform a normal GVM dispatch, then change the function pointer to dereference the this pointer. - GenericConstrainedCallDesc* callDesc = (GenericConstrainedCallDesc*)callDescIntPtr; - IntPtr target = RuntimeAugments.GVMLookupForSlot(contextObject.GetType().TypeHandle, callDesc->_constrainedMethod); - - if (FunctionPointerOps.IsGenericMethodPointer(target)) - { - GenericMethodDescriptor* genMethodDesc = FunctionPointerOps.ConvertToGenericDescriptor(target); - IntPtr actualCodeTarget = GetThunkThatDereferencesThisPointerAndTailCallsTarget(genMethodDesc->MethodFunctionPointer); - - return FunctionPointerOps.GetGenericMethodFunctionPointer(actualCodeTarget, genMethodDesc->InstantiationArgument); - } - else - { - return GetThunkThatDereferencesThisPointerAndTailCallsTarget(target); - } + return FunctionPointerOps.GetGenericMethodFunctionPointer(actualCodeTarget, genMethodDesc->InstantiationArgument); + } + else + { + return GetThunkThatDereferencesThisPointerAndTailCallsTarget(target); + } + }, + thisPtr, out _); } #if ARM diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/DebugFuncEval.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/DebugFuncEval.cs deleted file mode 100644 index d3d1162be9..0000000000 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/DebugFuncEval.cs +++ /dev/null @@ -1,207 +0,0 @@ -// 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.Runtime; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -using Internal.NativeFormat; -using Internal.Runtime.Augments; -using Internal.Runtime.CallInterceptor; -using Internal.Runtime.CompilerServices; -using Internal.TypeSystem; - -namespace Internal.Runtime.TypeLoader -{ - [McgIntrinsics] - internal static class AddrofIntrinsics - { - // This method is implemented elsewhere in the toolchain - internal static IntPtr AddrOf(T ftn) { throw new PlatformNotSupportedException(); } - } - - internal class DebugFuncEval - { - private static void HighLevelDebugFuncEvalHelperWithVariables(ref TypesAndValues param, ref LocalVariableSet arguments) - { - for (int i = 0; i < param.parameterValues.Length; i++) - { - arguments.SetVar(i + 1, param.parameterValues[i]); - } - - // Obtain the target method address from the runtime - IntPtr targetAddress = RuntimeAugments.RhpGetFuncEvalTargetAddress(); - - LocalVariableType[] returnAndArgumentTypes = new LocalVariableType[param.types.Length]; - for (int i = 0; i < returnAndArgumentTypes.Length; i++) - { - returnAndArgumentTypes[i] = new LocalVariableType(param.types[i], false, false); - } - - // Hard coding static here - DynamicCallSignature dynamicCallSignature = new DynamicCallSignature(Internal.Runtime.CallConverter.CallingConvention.ManagedStatic, returnAndArgumentTypes, returnAndArgumentTypes.Length); - - // Invoke the target method - Internal.Runtime.CallInterceptor.CallInterceptor.MakeDynamicCall(targetAddress, dynamicCallSignature, arguments); - - unsafe - { - // Box the return - IntPtr input = arguments.GetAddressOfVarData(0); - object returnValue = RuntimeAugments.RhBoxAny(input, (IntPtr)param.types[0].ToEETypePtr()); - GCHandle returnValueHandle = GCHandle.Alloc(returnValue); - IntPtr returnValueHandlePointer = GCHandle.ToIntPtr(returnValueHandle); - uint identifier = RuntimeAugments.RhpRecordDebuggeeInitiatedHandle(returnValueHandlePointer); - - // Signal to the debugger the func eval completes - FuncEvalCompleteCommand* funcEvalCompleteCommand = stackalloc FuncEvalCompleteCommand[1]; - funcEvalCompleteCommand->commandCode = 0; - funcEvalCompleteCommand->returnAddress = (long)returnValueHandlePointer; - IntPtr funcEvalCompleteCommandPointer = new IntPtr(funcEvalCompleteCommand); - RuntimeAugments.RhpSendCustomEventToDebugger(funcEvalCompleteCommandPointer, Unsafe.SizeOf()); - } - - // debugger magic will make sure this function never returns, instead control will be transferred back to the point where the FuncEval begins - } - - [StructLayout(LayoutKind.Explicit, Size=16)] - struct WriteParameterCommand - { - [FieldOffset(0)] - public int commandCode; - [FieldOffset(4)] - public int unused; - [FieldOffset(8)] - public long bufferAddress; - } - - [StructLayout(LayoutKind.Explicit, Size=16)] - struct FuncEvalCompleteCommand - { - [FieldOffset(0)] - public int commandCode; - [FieldOffset(4)] - public int unused; - [FieldOffset(8)] - public long returnAddress; - } - - struct TypesAndValues - { - public RuntimeTypeHandle[] types; - // TODO: We should support arguments of *any* type - public int[] parameterValues; - } - - private static void HighLevelDebugFuncEvalHelper() - { - uint parameterBufferSize = RuntimeAugments.RhpGetFuncEvalParameterBufferSize(); - - IntPtr writeParameterCommandPointer; - IntPtr debuggerBufferPointer; - unsafe - { - byte* debuggerBufferRawPointer = stackalloc byte[(int)parameterBufferSize]; - debuggerBufferPointer = new IntPtr(debuggerBufferRawPointer); - - WriteParameterCommand writeParameterCommand = new WriteParameterCommand - { - commandCode = 1, - bufferAddress = debuggerBufferPointer.ToInt64() - }; - - writeParameterCommandPointer = new IntPtr(&writeParameterCommand); - - RuntimeAugments.RhpSendCustomEventToDebugger(writeParameterCommandPointer, Unsafe.SizeOf()); - - // .. debugger magic ... the debuggerBuffer will be filled with parameter data - - TypesAndValues typesAndValues = new TypesAndValues(); - - uint trash; - uint parameterCount; - uint parameterValue; - uint eeTypeCount; - ulong eeType; - uint offset = 0; - - NativeReader reader = new NativeReader(debuggerBufferRawPointer, parameterBufferSize); - offset = reader.DecodeUnsigned(offset, out trash); // The VertexSequence always generate a length, I don't really need it. - offset = reader.DecodeUnsigned(offset, out parameterCount); - - typesAndValues.parameterValues = new int[parameterCount]; - for (int i = 0; i < parameterCount; i++) - { - offset = reader.DecodeUnsigned(offset, out parameterValue); - typesAndValues.parameterValues[i] = (int)parameterValue; - } - offset = reader.DecodeUnsigned(offset, out eeTypeCount); - for (int i = 0; i < eeTypeCount; i++) - { - // TODO: Stuff these eeType values into the external reference table - offset = reader.DecodeUnsignedLong(offset, out eeType); - } - - TypeSystemContext typeSystemContext = TypeSystemContextFactory.Create(); - bool hasThis; - TypeDesc[] parameters; - bool[] parametersWithGenericDependentLayout; - bool result = TypeLoaderEnvironment.Instance.GetCallingConverterDataFromMethodSignature_NativeLayout_Debugger(typeSystemContext, RuntimeSignature.CreateFromNativeLayoutSignatureForDebugger(offset), Instantiation.Empty, Instantiation.Empty, out hasThis, out parameters, out parametersWithGenericDependentLayout, reader); - - typesAndValues.types = new RuntimeTypeHandle[parameters.Length]; - - bool needToDynamicallyLoadTypes = false; - for (int i = 0; i < typesAndValues.types.Length; i++) - { - if (!parameters[i].RetrieveRuntimeTypeHandleIfPossible()) - { - needToDynamicallyLoadTypes = true; - break; - } - - typesAndValues.types[i] = parameters[i].GetRuntimeTypeHandle(); - } - - if (needToDynamicallyLoadTypes) - { - TypeLoaderEnvironment.Instance.RunUnderTypeLoaderLock(() => - { - typeSystemContext.FlushTypeBuilderStates(); - - GenericDictionaryCell[] cells = new GenericDictionaryCell[parameters.Length]; - for (int i = 0; i < cells.Length; i++) - { - cells[i] = GenericDictionaryCell.CreateTypeHandleCell(parameters[i]); - } - IntPtr[] eetypePointers; - TypeBuilder.ResolveMultipleCells(cells, out eetypePointers); - - for (int i = 0; i < parameters.Length; i++) - { - typesAndValues.types[i] = ((EEType*)eetypePointers[i])->ToRuntimeTypeHandle(); - } - }); - } - - TypeSystemContextFactory.Recycle(typeSystemContext); - - LocalVariableType[] argumentTypes = new LocalVariableType[parameters.Length]; - for (int i = 0; i < parameters.Length; i++) - { - // TODO: What these false really means? Need to make sure our format contains those information - argumentTypes[i] = new LocalVariableType(typesAndValues.types[i], false, false); - } - - LocalVariableSet.SetupArbitraryLocalVariableSet(HighLevelDebugFuncEvalHelperWithVariables, ref typesAndValues, argumentTypes); - } - } - - public static void Initialize() - { - // We needed this function only because the McgIntrinsics attribute cannot be applied on the static constructor - RuntimeAugments.RhpSetHighLevelDebugFuncEvalHelper(AddrofIntrinsics.AddrOf(HighLevelDebugFuncEvalHelper)); - } - } -} diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs index 5ca8d36b05..d6106a5ee3 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs @@ -120,12 +120,12 @@ namespace Internal.Runtime.TypeLoader public static IntPtr AllocateMemory(int cbBytes) { - return InteropExtensions.MemAlloc(new UIntPtr((uint)cbBytes)); + return PInvokeMarshal.MemAlloc(new IntPtr(cbBytes)); } public static void FreeMemory(IntPtr memoryPtrToFree) { - InteropExtensions.MemFree(memoryPtrToFree); + PInvokeMarshal.MemFree(memoryPtrToFree); } } @@ -186,9 +186,10 @@ namespace Internal.Runtime.TypeLoader if (pTemplateEEType != null) { - valueTypeFieldPaddingEncoded = EEType.ComputeValueTypeFieldPaddingFieldValue( + valueTypeFieldPaddingEncoded = EETypeBuilderHelpers.ComputeValueTypeFieldPaddingFieldValue( pTemplateEEType->ValueTypeFieldPadding, - (uint)pTemplateEEType->FieldAlignmentRequirement); + (uint)pTemplateEEType->FieldAlignmentRequirement, + IntPtr.Size); baseSize = (int)pTemplateEEType->BaseSize; isValueType = pTemplateEEType->IsValueType; hasFinalizer = pTemplateEEType->IsFinalizable; @@ -269,7 +270,7 @@ namespace Internal.Runtime.TypeLoader // Add Object type pointer field to base size baseSize += IntPtr.Size; - valueTypeFieldPaddingEncoded = (uint)EEType.ComputeValueTypeFieldPaddingFieldValue(cbValueTypeFieldPadding, (uint)state.FieldAlignment.Value); + valueTypeFieldPaddingEncoded = (uint)EETypeBuilderHelpers.ComputeValueTypeFieldPaddingFieldValue(cbValueTypeFieldPadding, (uint)state.FieldAlignment.Value, IntPtr.Size); } // Minimum base size is 3 pointers, and requires us to bump the size of an empty class type @@ -296,12 +297,9 @@ namespace Internal.Runtime.TypeLoader UInt32 rareFlags = optionalFields.GetFieldValue(EETypeOptionalFieldTag.RareFlags, 0); rareFlags |= (uint)EETypeRareFlags.IsDynamicTypeFlag; // Set the IsDynamicTypeFlag rareFlags &= ~(uint)EETypeRareFlags.NullableTypeViaIATFlag; // Remove the NullableTypeViaIATFlag flag - rareFlags &= ~(uint)EETypeRareFlags.HasSealedVTableEntriesFlag;// Remove the HasSealedVTableEntriesFlag - // we'll set IsDynamicTypeWithSealedVTableEntriesFlag instead - // Set the IsDynamicTypeWithSealedVTableEntriesFlag if needed if (state.NumSealedVTableEntries > 0) - rareFlags |= (uint)EETypeRareFlags.IsDynamicTypeWithSealedVTableEntriesFlag; + rareFlags |= (uint)EETypeRareFlags.HasSealedVTableEntriesFlag; if (requiresDynamicDispatchMap) rareFlags |= (uint)EETypeRareFlags.HasDynamicallyAllocatedDispatchMapFlag; @@ -320,7 +318,9 @@ namespace Internal.Runtime.TypeLoader rareFlags |= (uint)EETypeRareFlags.RequiresAlign8Flag; else rareFlags &= ~(uint)EETypeRareFlags.RequiresAlign8Flag; +#endif +#if ARM || ARM64 if (state.IsHFA) rareFlags |= (uint)EETypeRareFlags.IsHFAFlag; else @@ -341,6 +341,24 @@ namespace Internal.Runtime.TypeLoader else rareFlags &= ~(uint)EETypeRareFlags.IsByRefLikeFlag; + if (isNullable) + { + rareFlags |= (uint)EETypeRareFlags.IsNullableFlag; + uint nullableValueOffset = state.NullableValueOffset; + + // The stored offset is never zero (Nullable has a boolean there indicating whether the value is valid). + // If the real offset is one, then the field isn't set. Otherwise the offset is encoded - 1 to save space. + if (nullableValueOffset == 1) + optionalFields.ClearField(EETypeOptionalFieldTag.NullableValueOffset); + else + optionalFields.SetFieldValue(EETypeOptionalFieldTag.NullableValueOffset, checked(nullableValueOffset - 1)); + } + else + { + rareFlags &= ~(uint)EETypeRareFlags.IsNullableFlag; + optionalFields.ClearField(EETypeOptionalFieldTag.NullableValueOffset); + } + rareFlags |= (uint)EETypeRareFlags.HasDynamicModuleFlag; optionalFields.SetFieldValue(EETypeOptionalFieldTag.RareFlags, rareFlags); @@ -430,7 +448,7 @@ namespace Internal.Runtime.TypeLoader // GCDesc data in *reverse* order for instance GCDescs (subtracts 4 from the pointer values at each iteration). // - For the first GCDesc, we use (pEEType - 4) to point to the first 4-byte integer directly preceeding the EEType // - For the second GCDesc, given that the state.NonUniversalInstanceGCDesc already points to the first byte preceeding the template EEType, we - // subtract 3 to point to the first 4-byte integer directly preceeding the template EEtype + // subtract 3 to point to the first 4-byte integer directly preceeding the template EEType TestGCDescsForEquality(new IntPtr((byte*)pEEType - 4), state.NonUniversalInstanceGCDesc - 3, cbGCDesc, true); } #endif @@ -441,7 +459,7 @@ namespace Internal.Runtime.TypeLoader pEEType->OptionalFieldsPtr = (byte*)pEEType + cbEEType; optionalFields.WriteToEEType(pEEType, cbOptionalFieldsSize); -#if CORERT +#if !PROJECTN pEEType->PointerToTypeManager = PermanentAllocatedMemoryBlobs.GetPointerToIntPtr(moduleInfo.Handle.GetIntPtrUNSAFE()); #endif pEEType->DynamicModule = dynamicModulePtr; @@ -1008,6 +1026,8 @@ namespace Internal.Runtime.TypeLoader CreateEETypeWorker(typeof(void*).TypeHandle.ToEETypePtr(), hashCodeOfNewType, 0, false, state); Debug.Assert(!state.HalfBakedRuntimeTypeHandle.IsNull()); + TypeLoaderLogger.WriteLine("Allocated new POINTER type " + pointerType.ToString() + " with hashcode value = 0x" + hashCodeOfNewType.LowLevelToString() + " with eetype = " + state.HalfBakedRuntimeTypeHandle.ToIntPtr().LowLevelToString()); + state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->RelatedParameterType = pointeeTypeHandle.ToEETypePtr(); return state.HalfBakedRuntimeTypeHandle; @@ -1022,6 +1042,8 @@ namespace Internal.Runtime.TypeLoader CreateEETypeWorker(typeof(void*).TypeHandle.ToEETypePtr(), hashCodeOfNewType, 0, false, state); Debug.Assert(!state.HalfBakedRuntimeTypeHandle.IsNull()); + TypeLoaderLogger.WriteLine("Allocated new BYREF type " + byRefType.ToString() + " with hashcode value = 0x" + hashCodeOfNewType.LowLevelToString() + " with eetype = " + state.HalfBakedRuntimeTypeHandle.ToIntPtr().LowLevelToString()); + state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->RelatedParameterType = pointeeTypeHandle.ToEETypePtr(); // We used a pointer as a template. We need to make this a byref. @@ -1085,13 +1107,23 @@ namespace Internal.Runtime.TypeLoader return state.HalfBakedRuntimeTypeHandle; } - public static IntPtr GetDictionary(EEType* pEEType) + public static int GetDictionaryOffsetInEEtype(EEType* pEEType) { // Dictionary slot is the first vtable slot EEType* pBaseType = pEEType->BaseType; int dictionarySlot = (pBaseType == null ? 0 : pBaseType->NumVtableSlots); - return *(IntPtr*)((byte*)pEEType + sizeof(EEType) + dictionarySlot * IntPtr.Size); + return sizeof(EEType) + dictionarySlot * IntPtr.Size; + } + + public static IntPtr GetDictionaryAtOffset(EEType* pEEType, int offset) + { + return *(IntPtr*)((byte*)pEEType + offset); + } + + public static IntPtr GetDictionary(EEType* pEEType) + { + return GetDictionaryAtOffset(pEEType, GetDictionaryOffsetInEEtype(pEEType)); } public static int GetDictionarySlotInVTable(TypeDesc type) @@ -1120,5 +1152,24 @@ namespace Internal.Runtime.TypeLoader typeWithDictionary = null; return -1; } + + public static EEType* GetBaseEETypeForDictionaryPtr(EEType* pEEType, IntPtr dictionaryPtr) + { + // Look for the exact base type that owns the dictionary + IntPtr curDictPtr = GetDictionary(pEEType); + EEType* pBaseEEType = pEEType; + + while (curDictPtr != dictionaryPtr) + { + pBaseEEType = pBaseEEType->BaseType; + Debug.Assert(pBaseEEType != null); + // Since in multifile scenario, the base type's dictionary may end up having + // a copy in each module, therefore the lookup of the right base type should be + // based on the dictionary pointer in the current EEtype, instead of the base EEtype. + curDictPtr = GetDictionaryAtOffset(pEEType, EETypeCreator.GetDictionaryOffsetInEEtype(pBaseEEType)); + } + + return pBaseEEType; + } } } diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ExternalReferencesTable.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ExternalReferencesTable.cs index 53c878cb1f..3c6a9d382a 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ExternalReferencesTable.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ExternalReferencesTable.cs @@ -17,35 +17,34 @@ namespace Internal.Runtime.TypeLoader private IntPtr _elements; private uint _elementsCount; private TypeManagerHandle _moduleHandle; - private bool isDebuggerPrepared; + private ulong[] debuggerPreparedExternalReferences; - public bool IsInitialized() { return isDebuggerPrepared || !_moduleHandle.IsNull; } + public bool IsInitialized() { return (debuggerPreparedExternalReferences != null) || !_moduleHandle.IsNull; } private unsafe bool Initialize(NativeFormatModuleInfo module, ReflectionMapBlob blobId) { - if (module == null) - { - isDebuggerPrepared = true; - } - else - { - _moduleHandle = module.Handle; + _moduleHandle = module.Handle; - byte* pBlob; - uint cbBlob; - if (!module.TryFindBlob(blobId, out pBlob, out cbBlob)) - { - _elements = IntPtr.Zero; - _elementsCount = 0; - return false; - } - - _elements = (IntPtr)pBlob; - _elementsCount = (uint)(cbBlob / sizeof(TableElement)); + byte* pBlob; + uint cbBlob; + if (!module.TryFindBlob(blobId, out pBlob, out cbBlob)) + { + _elements = IntPtr.Zero; + _elementsCount = 0; + return false; } + + _elements = (IntPtr)pBlob; + _elementsCount = (uint)(cbBlob / sizeof(TableElement)); + return true; } + public void InitializeDebuggerReference(ulong[] debuggerPreparedExternalReferences) + { + this.debuggerPreparedExternalReferences = debuggerPreparedExternalReferences; + } + /// /// Initialize ExternalReferencesTable using the NativeReferences metadata blob on a given module. /// @@ -78,30 +77,23 @@ namespace Internal.Runtime.TypeLoader unsafe public uint GetRvaFromIndex(uint index) { -#if CORERT - // The usage of this API will need to go away since this is not fully portable - // and we'll not be able to support this for CppCodegen. - throw new PlatformNotSupportedException(); -#else +#if PROJECTN Debug.Assert(!_moduleHandle.IsNull); if (index >= _elementsCount) throw new BadImageFormatException(); return ((TableElement*)_elements)[index]; +#else + // The usage of this API will need to go away since this is not fully portable + // and we'll not be able to support this for CppCodegen. + throw new PlatformNotSupportedException(); #endif } unsafe public IntPtr GetIntPtrFromIndex(uint index) { -#if CORERT - if (index >= _elementsCount) - throw new BadImageFormatException(); - - // TODO: indirection through IAT - int* pRelPtr32 = &((int*)_elements)[index]; - return (IntPtr)((byte*)pRelPtr32 + *pRelPtr32); -#else +#if PROJECTN uint rva = GetRvaFromIndex(index); if ((rva & IndirectionConstants.RVAPointsToIndirection) != 0) { @@ -112,19 +104,19 @@ namespace Internal.Runtime.TypeLoader { return (IntPtr)(_moduleHandle.ConvertRVAToPointer(rva)); } -#endif - } - - unsafe public IntPtr GetFunctionPointerFromIndex(uint index) - { -#if CORERT +#else if (index >= _elementsCount) throw new BadImageFormatException(); // TODO: indirection through IAT int* pRelPtr32 = &((int*)_elements)[index]; return (IntPtr)((byte*)pRelPtr32 + *pRelPtr32); -#else +#endif + } + + unsafe public IntPtr GetFunctionPointerFromIndex(uint index) + { +#if PROJECTN uint rva = GetRvaFromIndex(index); if ((rva & DynamicInvokeMapEntry.IsImportMethodFlag) == DynamicInvokeMapEntry.IsImportMethodFlag) @@ -135,17 +127,26 @@ namespace Internal.Runtime.TypeLoader { return (IntPtr)(_moduleHandle.ConvertRVAToPointer(rva)); } +#else + if (index >= _elementsCount) + throw new BadImageFormatException(); + + // TODO: indirection through IAT + int* pRelPtr32 = &((int*)_elements)[index]; + return (IntPtr)((byte*)pRelPtr32 + *pRelPtr32); #endif } public RuntimeTypeHandle GetRuntimeTypeHandleFromIndex(uint index) { - if (isDebuggerPrepared) + if (this.debuggerPreparedExternalReferences == null) { - return typeof(int).TypeHandle; + return RuntimeAugments.CreateRuntimeTypeHandle(GetIntPtrFromIndex(index)); } - - return RuntimeAugments.CreateRuntimeTypeHandle(GetIntPtrFromIndex(index)); + else + { + return RuntimeAugments.CreateRuntimeTypeHandle((IntPtr)this.debuggerPreparedExternalReferences[index]); + } } public IntPtr GetGenericDictionaryFromIndex(uint index) @@ -153,7 +154,7 @@ namespace Internal.Runtime.TypeLoader return GetIntPtrFromIndex(index); } -#if CORERT +#if !PROJECTN unsafe public IntPtr GetFieldAddressFromIndex(uint index) { if (index >= _elementsCount) diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/LowLevelStringConverter.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/LowLevelStringConverter.cs index 105aa56599..37be9c7999 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/LowLevelStringConverter.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/LowLevelStringConverter.cs @@ -29,12 +29,12 @@ namespace System public static string ToStringInvariant(this byte arg) { - return ((uint)arg).LowLevelToString(); + return arg.LowLevelToString(); } public static string ToStringInvariant(this ushort arg) { - return ((uint)arg).LowLevelToString(); + return arg.LowLevelToString(); } public static string ToStringInvariant(this ulong arg) @@ -65,9 +65,16 @@ namespace Internal.Runtime.TypeLoader { private const string HexDigits = "0123456789ABCDEF"; - public static string LowLevelToString(this int arg) + private static string LowLevelToString(ulong arg, int shift) { - return ((uint)arg).LowLevelToString(); + StringBuilder sb = new StringBuilder(16); + while (shift > 0) + { + shift -= 4; + int digit = (int)((arg >> shift) & 0xF); + sb.Append(HexDigits[digit]); + } + return sb.ToString(); } public static string LowLevelToString(this LayoutInt arg) @@ -78,48 +85,34 @@ namespace Internal.Runtime.TypeLoader return ((uint)arg.AsInt).LowLevelToString(); } + public static string LowLevelToString(this byte arg) + { + return LowLevelToString((ulong)arg, 4 * 2); + } + + public static string LowLevelToString(this ushort arg) + { + return LowLevelToString((ulong)arg, 4 * 4); + } + + public static string LowLevelToString(this int arg) + { + return ((uint)arg).LowLevelToString(); + } + public static string LowLevelToString(this uint arg) { - StringBuilder sb = new StringBuilder(8); - int shift = 4 * 8; - while (shift > 0) - { - shift -= 4; - int digit = (int)((arg >> shift) & 0xF); - sb.Append(HexDigits[digit]); - } - - return sb.ToString(); + return LowLevelToString((ulong)arg, 4 * 8); } public static string LowLevelToString(this ulong arg) { - StringBuilder sb = new StringBuilder(16); - int shift = 4 * 16; - while (shift > 0) - { - shift -= 4; - int digit = (int)((arg >> shift) & 0xF); - sb.Append(HexDigits[digit]); - } - - return sb.ToString(); + return LowLevelToString((ulong)arg, 4 * 16); } public static string LowLevelToString(this IntPtr arg) { - StringBuilder sb = new StringBuilder(IntPtr.Size * 4); - ulong num = (ulong)arg; - - int shift = IntPtr.Size * 8; - while (shift > 0) - { - shift -= 4; - int digit = (int)((num >> shift) & 0xF); - sb.Append(HexDigits[digit]); - } - - return sb.ToString(); + return LowLevelToString((ulong)arg, IntPtr.Size * 8); } public static string LowLevelToString(this RuntimeTypeHandle rtth) @@ -149,20 +142,12 @@ namespace Internal.Runtime.TypeLoader } // Fallback implementation when no metadata available - string prefix = "EEType:0x"; + return LowLevelToStringRawEETypeAddress(rtth); + } - StringBuilder sb = new StringBuilder(prefix.Length + IntPtr.Size * 4); - ulong num = (ulong)rtth.ToIntPtr(); - - int shift = IntPtr.Size * 8; - while (shift > 0) - { - shift -= 4; - int digit = (int)((num >> shift) & 0xF); - sb.Append(HexDigits[digit]); - } - - return sb.ToString(); + public static string LowLevelToStringRawEETypeAddress(this RuntimeTypeHandle rtth) + { + return "EEType:0x" + LowLevelToString(rtth.ToIntPtr()); } } } diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataNameExtensions.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataNameExtensions.cs index 0ad42cd812..aea7ab44fb 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataNameExtensions.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataNameExtensions.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Text; using System.Reflection; using System.Collections.Generic; using Debug = System.Diagnostics.Debug; @@ -41,6 +42,11 @@ namespace Internal.Runtime.TypeLoader return handle.ToPointerSignatureHandle(reader).GetFullName(reader); case HandleType.ByReferenceSignature: return handle.ToByReferenceSignatureHandle(reader).GetFullName(reader); + + case HandleType.ScopeDefinition: + return handle.ToScopeDefinitionHandle(reader).GetFullName(reader); + case HandleType.ScopeReference: + return handle.ToScopeReferenceHandle(reader).GetFullName(reader); } return null; } @@ -108,15 +114,23 @@ namespace Internal.Runtime.TypeLoader return name + "<" + argsString + ">"; } - public static string GetFullName(this TypeDefinitionHandle typeDefHandle, MetadataReader reader) + public static void GetFullName(this TypeDefinitionHandle typeDefHandle, MetadataReader reader, out string name, out string enclosing, out string nspace) { var typeDef = typeDefHandle.GetTypeDefinition(reader); Debug.Assert(!typeDef.Name.IsNull(reader)); - var name = typeDef.Name.GetConstantStringValue(reader).Value; - var enclosing = typeDef.EnclosingType.IsNull(reader) ? null : typeDef.EnclosingType.GetFullName(reader); - var nspace = typeDef.NamespaceDefinition.IsNull(reader) ? null : typeDef.NamespaceDefinition.GetFullName(reader); + name = typeDef.Name.GetConstantStringValue(reader).Value; + enclosing = typeDef.EnclosingType.IsNull(reader) ? null : typeDef.EnclosingType.GetFullName(reader); + nspace = typeDef.NamespaceDefinition.IsNull(reader) ? null : typeDef.NamespaceDefinition.GetFullName(reader); + } + + public static string GetFullName(this TypeDefinitionHandle typeDefHandle, MetadataReader reader) + { + string name; + string enclosing; + string nspace; + typeDefHandle.GetFullName(reader, out name, out enclosing, out nspace); if (enclosing != null && name != null) return enclosing + "+" + name; @@ -126,6 +140,36 @@ namespace Internal.Runtime.TypeLoader return name; } + public static string GetContainingModuleName(this TypeDefinitionHandle typeDefHandle, MetadataReader reader) + { + var typeDef = typeDefHandle.GetTypeDefinition(reader); + + Handle currentHandle = !typeDef.EnclosingType.IsNull(reader) ? (Handle)typeDef.EnclosingType : (Handle)typeDef.NamespaceDefinition; + Debug.Assert(!currentHandle.IsNull(reader)); + + while (!currentHandle.IsNull(reader)) + { + switch (currentHandle.HandleType) + { + case HandleType.TypeDefinition: + typeDef = currentHandle.ToTypeDefinitionHandle(reader).GetTypeDefinition(reader); + currentHandle = !typeDef.EnclosingType.IsNull(reader) ? (Handle)typeDef.EnclosingType : (Handle)typeDef.NamespaceDefinition; + break; + + case HandleType.NamespaceDefinition: + currentHandle = currentHandle.ToNamespaceDefinitionHandle(reader).GetNamespaceDefinition(reader).ParentScopeOrNamespace; + break; + + case HandleType.ScopeDefinition: + return currentHandle.GetFullName(reader); + + default: + return "?"; + } + } + + return "?"; + } public static string GetFullName(this NamespaceDefinitionHandle namespaceHandle, MetadataReader reader) { var nspace = namespaceHandle.GetNamespaceDefinition(reader); @@ -142,15 +186,23 @@ namespace Internal.Runtime.TypeLoader return name; } - public static string GetFullName(this TypeReferenceHandle typeRefHandle, MetadataReader reader) + public static void GetFullName(this TypeReferenceHandle typeRefHandle, MetadataReader reader, out string name, out string enclosing, out string nspace) { var typeRef = typeRefHandle.GetTypeReference(reader); Debug.Assert(!typeRef.TypeName.IsNull(reader)); - var name = typeRef.TypeName.GetConstantStringValue(reader).Value; - var enclosing = typeRef.ParentNamespaceOrType.HandleType == HandleType.TypeReference ? typeRef.ParentNamespaceOrType.GetFullName(reader) : null; - var nspace = typeRef.ParentNamespaceOrType.HandleType == HandleType.NamespaceReference ? typeRef.ParentNamespaceOrType.GetFullName(reader) : null; + name = typeRef.TypeName.GetConstantStringValue(reader).Value; + enclosing = typeRef.ParentNamespaceOrType.HandleType == HandleType.TypeReference ? typeRef.ParentNamespaceOrType.GetFullName(reader) : null; + nspace = typeRef.ParentNamespaceOrType.HandleType == HandleType.NamespaceReference ? typeRef.ParentNamespaceOrType.GetFullName(reader) : null; + } + + public static string GetFullName(this TypeReferenceHandle typeRefHandle, MetadataReader reader) + { + string name; + string enclosing; + string nspace; + typeRefHandle.GetFullName(reader, out name, out enclosing, out nspace); if (enclosing != null && name != null) return enclosing + "+" + name; @@ -160,6 +212,33 @@ namespace Internal.Runtime.TypeLoader return name; } + public static string GetContainingModuleName(this TypeReferenceHandle typeRefHandle, MetadataReader reader) + { + var typeRef = typeRefHandle.GetTypeReference(reader); + + Handle currentHandle = typeRef.ParentNamespaceOrType; + Debug.Assert(!currentHandle.IsNull(reader)); + + while (!currentHandle.IsNull(reader)) + { + switch(currentHandle.HandleType) + { + case HandleType.TypeReference: + case HandleType.NamespaceReference: + currentHandle = typeRef.ParentNamespaceOrType; + break; + + case HandleType.ScopeReference: + return currentHandle.GetFullName(reader); + + default: + return "?"; + } + } + + return "?"; + } + public static string GetFullName(this NamespaceReferenceHandle namespaceHandle, MetadataReader reader) { var nspace = namespaceHandle.GetNamespaceReference(reader); @@ -175,5 +254,63 @@ namespace Internal.Runtime.TypeLoader return name; } + + public static string GetFullName(this ScopeDefinitionHandle scopeDefHandle, MetadataReader reader) + { + var scopeDef = scopeDefHandle.GetScopeDefinition(reader); + + Debug.Assert(!scopeDef.Name.IsNull(reader)); + + var assemblyName = new AssemblyName + { + Name = scopeDef.Name.GetConstantStringValue(reader).Value, + CultureName = scopeDef.Culture.IsNull(reader) ? null : scopeDef.Culture.GetConstantStringValue(reader).Value, + Version = new Version(scopeDef.MajorVersion, scopeDef.MinorVersion, scopeDef.BuildNumber, scopeDef.RevisionNumber) + }; + + if (scopeDef.PublicKey.Count > 0) + { + var pkt = new byte[scopeDef.PublicKey.Count]; + int index = 0; + foreach (var b in scopeDef.PublicKey) + pkt[index++] = b; + assemblyName.SetPublicKeyToken(pkt); + } + else + { + assemblyName.SetPublicKeyToken(Array.Empty()); + } + + return assemblyName.FullName; + } + + public static string GetFullName(this ScopeReferenceHandle scopeRefHandle, MetadataReader reader) + { + var scopeRef = scopeRefHandle.GetScopeReference(reader); + + Debug.Assert(!scopeRef.Name.IsNull(reader)); + + var assemblyName = new AssemblyName + { + Name = scopeRef.Name.GetConstantStringValue(reader).Value, + CultureName = scopeRef.Culture.IsNull(reader) ? null : scopeRef.Culture.GetConstantStringValue(reader).Value, + Version = new Version(scopeRef.MajorVersion, scopeRef.MinorVersion, scopeRef.BuildNumber, scopeRef.RevisionNumber) + }; + + if (scopeRef.PublicKeyOrToken.Count > 0) + { + var pkt = new byte[scopeRef.PublicKeyOrToken.Count]; + int index = 0; + foreach (var b in scopeRef.PublicKeyOrToken) + pkt[index++] = b; + assemblyName.SetPublicKeyToken(pkt); + } + else + { + assemblyName.SetPublicKeyToken(Array.Empty()); + } + + return assemblyName.FullName; + } } } diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataReaderExtensions.Ecma.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataReaderExtensions.Ecma.cs index 8899b34485..ebd30963a9 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataReaderExtensions.Ecma.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataReaderExtensions.Ecma.cs @@ -79,13 +79,17 @@ namespace System.Reflection.Runtime.General int contentType = ((int)assemblyFlags) & 0x00000E00; assemblyNameFlags |= (AssemblyNameFlags)contentType; - byte[] publicKeyOrTokenByteArray = null; + byte[] publicKeyOrTokenByteArray; if (!publicKeyOrToken.IsNil) { ImmutableArray publicKeyOrTokenBlob = reader.GetBlobContent(publicKeyOrToken); publicKeyOrTokenByteArray = new byte[publicKeyOrTokenBlob.Length]; publicKeyOrTokenBlob.CopyTo(publicKeyOrTokenByteArray); } + else + { + publicKeyOrTokenByteArray = Array.Empty(); + } return new RuntimeAssemblyName( name.GetString(reader), diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataReaderExtensions.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataReaderExtensions.cs index 2666ad79da..772796ec92 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataReaderExtensions.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataReaderExtensions.cs @@ -77,6 +77,15 @@ namespace System.Reflection.Runtime.General handleType == HandleType.TypeSpecification; } + public static bool IsTypeDefRefSpecOrModifiedTypeHandle(this Handle handle, MetadataReader reader) + { + HandleType handleType = handle.HandleType; + return handleType == HandleType.TypeDefinition || + handleType == HandleType.TypeReference || + handleType == HandleType.TypeSpecification || + handleType == HandleType.ModifiedType; + } + public static RuntimeAssemblyName ToRuntimeAssemblyName(this ScopeDefinitionHandle scopeDefinitionHandle, MetadataReader reader) { ScopeDefinition scopeDefinition = scopeDefinitionHandle.GetScopeDefinition(reader); @@ -117,7 +126,7 @@ namespace System.Reflection.Runtime.General ushort buildNumber, ushort revisionNumber, ConstantStringValueHandle culture, - IEnumerable publicKeyOrToken, + ByteCollection publicKeyOrToken, global::Internal.Metadata.NativeFormat.AssemblyFlags assemblyFlags) { AssemblyNameFlags assemblyNameFlags = AssemblyNameFlags.None; diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ModuleList.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ModuleList.cs index b6f6d746d9..f4721db26d 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ModuleList.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ModuleList.cs @@ -654,6 +654,17 @@ namespace Internal.Runtime.TypeLoader } } + /// + /// This helper method is called at the end of StartupCodeTrigger during main app build + /// to refresh the module list after all modules have been registered in StartupCodeHelpers. + /// +#if PROJECTN + public static void RegisterEagerModules() + { + TypeLoaderEnvironment.Instance.ModuleList.RegisterNewModules(ModuleType.Eager); + } +#endif + /// /// Register all modules which were added (Registered) to the runtime and are not already registered with the TypeLoader. /// diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutFieldAlgorithm.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutFieldAlgorithm.cs index 846bee9ca1..5f7c58637f 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutFieldAlgorithm.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutFieldAlgorithm.cs @@ -37,23 +37,6 @@ namespace Internal.Runtime.TypeLoader } } - public unsafe override bool ComputeIsByRefLike(DefType type) - { - if (type.IsTemplateCanonical()) - { - return type.ComputeTemplate().RuntimeTypeHandle.ToEETypePtr()->IsByRefLike; - } - else - { - if (type.RetrieveRuntimeTypeHandleIfPossible()) - { - return type.RuntimeTypeHandle.ToEETypePtr()->IsByRefLike; - } - - throw new NotImplementedException(); - } - } - public override ComputedInstanceFieldLayout ComputeInstanceLayout(DefType type, InstanceLayoutKind layoutKind) { if (!type.IsTemplateUniversal() && (layoutKind == InstanceLayoutKind.TypeOnly)) @@ -445,65 +428,101 @@ namespace Internal.Runtime.TypeLoader alignment = fieldDefType.InstanceFieldAlignment; } - public override DefType ComputeHomogeneousFloatAggregateElementType(DefType type) + public unsafe override DefType ComputeHomogeneousFloatAggregateElementType(DefType type) { - if (!type.IsValueType) - return null; - - // Once this is done, the NativeLayoutFields on the type are initialized - EnsureFieldLayoutLoadedForGenericType((DefType)type); - Debug.Assert(type.NativeLayoutFields != null); - - // Empty types are not HFA - if (type.NativeLayoutFields.Length == 0) - return null; - - DefType currentHfaElementType = null; - - for (int i = 0; i < type.NativeLayoutFields.Length; i++) + if (type.Context.Target.Architecture == TargetArchitecture.ARM) { - TypeDesc fieldType = type.NativeLayoutFields[i].FieldType; - if (type.NativeLayoutFields[i].FieldStorage != NativeFormat.FieldStorage.Instance) - continue; - - DefType fieldDefType = fieldType as DefType; - - // HFA types cannot contain non-HFA types - if (fieldDefType == null || !fieldDefType.IsHfa) + if (!type.IsValueType) return null; - Debug.Assert(fieldDefType.HfaElementType != null); + // There is no reason to compute the entire field layout for the HFA type/flag if + // the template type is not a universal generic type (information stored in rare flags on the EEType) + TypeDesc templateType = type.ComputeTemplate(false); + if (templateType != null && !templateType.IsCanonicalSubtype(CanonicalFormKind.Universal)) + { + EEType* pEETemplate = templateType.GetRuntimeTypeHandle().ToEETypePtr(); + if (!pEETemplate->IsHFA) + return null; + if (pEETemplate->RequiresAlign8) + return type.Context.GetWellKnownType(WellKnownType.Double); + else + return type.Context.GetWellKnownType(WellKnownType.Single); + } + + // Once this is done, the NativeLayoutFields on the type are initialized + EnsureFieldLayoutLoadedForGenericType((DefType)type); + Debug.Assert(type.NativeLayoutFields != null); + + // Empty types are not HFA + if (type.NativeLayoutFields.Length == 0) + return null; + + DefType currentHfaElementType = null; + + for (int i = 0; i < type.NativeLayoutFields.Length; i++) + { + TypeDesc fieldType = type.NativeLayoutFields[i].FieldType; + if (type.NativeLayoutFields[i].FieldStorage != NativeFormat.FieldStorage.Instance) + continue; + + DefType fieldDefType = fieldType as DefType; + + // HFA types cannot contain non-HFA types + if (fieldDefType == null || !fieldDefType.IsHfa) + return null; + + Debug.Assert(fieldDefType.HfaElementType != null); + + if (currentHfaElementType == null) + currentHfaElementType = fieldDefType.HfaElementType; + else if (currentHfaElementType != fieldDefType.HfaElementType) + return null; // If the field doesn't have the same HFA type as the one we've looked at before, the type cannot be HFA + } + + // If we didn't find any instance fields, then this can't be an HFA type if (currentHfaElementType == null) - currentHfaElementType = fieldDefType.HfaElementType; - else if (currentHfaElementType != fieldDefType.HfaElementType) - return null; // If the field doesn't have the same HFA type as the one we've looked at before, the type cannot be HFA + return null; + + // Note that we check the total size, but do not perform any checks on number of fields: + // - Type of fields can be HFA valuetype itself + // - Managed C++ HFA valuetypes have just one of type float to signal that + // the valuetype is HFA and explicitly specified size + int maxSize = currentHfaElementType.InstanceFieldSize.AsInt * currentHfaElementType.Context.Target.MaximumHfaElementCount; + if (type.InstanceFieldSize.AsInt > maxSize) + return null; + + return currentHfaElementType; } + else + { + Debug.Assert( + type.Context.Target.Architecture == TargetArchitecture.X86 || + type.Context.Target.Architecture == TargetArchitecture.X64); - // If we didn't find any instance fields, then this can't be an HFA type - if (currentHfaElementType == null) return null; - - // Note that we check the total size, but do not perform any checks on number of fields: - // - Type of fields can be HFA valuetype itself - // - Managed C++ HFA valuetypes have just one of type float to signal that - // the valuetype is HFA and explicitly specified size - int maxSize = currentHfaElementType.InstanceFieldSize.AsInt * currentHfaElementType.Context.Target.MaximumHfaElementCount; - if (type.InstanceFieldSize.AsInt > maxSize) - return null; - - return currentHfaElementType; + } } public override ValueTypeShapeCharacteristics ComputeValueTypeShapeCharacteristics(DefType type) { - if (!type.IsValueType || - ComputeHomogeneousFloatAggregateElementType(type) == null) + if (type.Context.Target.Architecture == TargetArchitecture.ARM) { + if (!type.IsValueType || ComputeHomogeneousFloatAggregateElementType(type) == null) + { + return ValueTypeShapeCharacteristics.None; + } + + return ValueTypeShapeCharacteristics.HomogenousFloatAggregate; + } + else + { + Debug.Assert( + type.Context.Target.Architecture == TargetArchitecture.X86 || + type.Context.Target.Architecture == TargetArchitecture.X64); + return ValueTypeShapeCharacteristics.None; } - - return ValueTypeShapeCharacteristics.HomogenousFloatAggregate; } } } diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutInfoLoadContext.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutInfoLoadContext.cs index 78f29bfa5c..f070b0066a 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutInfoLoadContext.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutInfoLoadContext.cs @@ -29,6 +29,7 @@ namespace Internal.Runtime.TypeLoader private ExternalReferencesTable _externalReferencesLookup; public Instantiation _typeArgumentHandles; public Instantiation _methodArgumentHandles; + public ulong[] _debuggerPreparedExternalReferences; private TypeDesc GetInstantiationType(ref NativeParser parser, uint arity) { @@ -66,8 +67,15 @@ namespace Internal.Runtime.TypeLoader { if (!_externalReferencesLookup.IsInitialized()) { - bool success = _externalReferencesLookup.InitializeNativeReferences(_module); - Debug.Assert(success); + if (this._debuggerPreparedExternalReferences == null) + { + bool success = _externalReferencesLookup.InitializeNativeReferences(_module); + Debug.Assert(success); + } + else + { + _externalReferencesLookup.InitializeDebuggerReference(this._debuggerPreparedExternalReferences); + } } } @@ -151,7 +159,7 @@ namespace Internal.Runtime.TypeLoader return _typeSystemContext.GetWellKnownType((WellKnownType)data); case TypeSignatureKind.FunctionPointer: - Debug.Assert(false, "NYI!"); + Debug.Fail("NYI!"); parser.ThrowBadImageFormatException(); return null; diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NoMetadataFieldLayoutAlgorithm.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NoMetadataFieldLayoutAlgorithm.cs index 405329912b..adff035ae7 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NoMetadataFieldLayoutAlgorithm.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NoMetadataFieldLayoutAlgorithm.cs @@ -23,11 +23,6 @@ namespace Internal.Runtime.TypeLoader return type.RuntimeTypeHandle.ToEETypePtr()->HasGCPointers; } - public unsafe override bool ComputeIsByRefLike(DefType type) - { - return type.RuntimeTypeHandle.ToEETypePtr()->IsByRefLike; - } - /// /// Reads the minimal information about type layout encoded in the /// EEType. That doesn't include field information. @@ -114,13 +109,11 @@ namespace Internal.Runtime.TypeLoader } else { - // We must delegate to algorithms that can work off of a sort of metadata - if (type.HasNativeLayout) - return s_nativeLayoutFieldAlgorithm.ComputeHomogeneousFloatAggregateElementType(type); - else if (type is MetadataType) - return _metadataFieldLayoutAlgorithm.ComputeHomogeneousFloatAggregateElementType(type); - else - return null; // If there isn't any form of metadata, it can't matter... as HFA is not part of the ABI except on ARM + Debug.Assert( + type.Context.Target.Architecture == TargetArchitecture.X86 || + type.Context.Target.Architecture == TargetArchitecture.X64); + + return null; } } @@ -142,13 +135,11 @@ namespace Internal.Runtime.TypeLoader } else { - // We must delegate to algorithms that can work off of a sort of metadata - if (type.HasNativeLayout) - return s_nativeLayoutFieldAlgorithm.ComputeValueTypeShapeCharacteristics(type); - else if (type is MetadataType) - return _metadataFieldLayoutAlgorithm.ComputeValueTypeShapeCharacteristics(type); - else - return ValueTypeShapeCharacteristics.None; // If there isn't any form of metadata, it can't matter... as HFA is not part of the ABI except on ARM + Debug.Assert( + type.Context.Target.Architecture == TargetArchitecture.X86 || + type.Context.Target.Architecture == TargetArchitecture.X64); + + return ValueTypeShapeCharacteristics.None; } } } diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/PermanentAllocatedMemoryBlobs.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/PermanentAllocatedMemoryBlobs.cs index 4e8781617d..a55f50d252 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/PermanentAllocatedMemoryBlobs.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/PermanentAllocatedMemoryBlobs.cs @@ -19,9 +19,8 @@ namespace Internal.Runtime.TypeLoader { public sealed partial class PermanentAllocatedMemoryBlobs { - // Various functions in the type loader need to create permanent pointers for use by thread static lookup, or other purposes. + // Various functions in the type loader need to create permanent pointers for various purposes. - private static PermanentlyAllocatedMemoryRegions_ThreadStaticFieldOffsets s_threadStaticFieldCookies = new PermanentlyAllocatedMemoryRegions_ThreadStaticFieldOffsets(); private static PermanentlyAllocatedMemoryRegions_Uint_In_IntPtr s_uintCellValues = new PermanentlyAllocatedMemoryRegions_Uint_In_IntPtr(); private static PermanentlyAllocatedMemoryRegions_IntPtr_In_IntPtr s_pointerIndirectionCellValues = new PermanentlyAllocatedMemoryRegions_IntPtr_In_IntPtr(); @@ -69,57 +68,6 @@ namespace Internal.Runtime.TypeLoader } } - public struct ThreadStaticFieldOffsets : IEquatable - { - public uint StartingOffsetInTlsBlock; // Offset in the TLS block containing the thread static fields of a given type - public uint FieldOffset; // Offset of a thread static field from the start of its containing type's TLS fields block - // (in other words, the address of a field is 'TLS block + StartingOffsetInTlsBlock + FieldOffset') - - public override int GetHashCode() - { - return (int)(StartingOffsetInTlsBlock ^ FieldOffset << 8); - } - - public override bool Equals(object obj) - { - if (obj is ThreadStaticFieldOffsets) - { - return Equals((ThreadStaticFieldOffsets)obj); - } - return false; - } - - public bool Equals(ThreadStaticFieldOffsets other) - { - if (StartingOffsetInTlsBlock != other.StartingOffsetInTlsBlock) - return false; - - return FieldOffset == other.FieldOffset; - } - } - - private class PermanentlyAllocatedMemoryRegions_ThreadStaticFieldOffsets - { - private LowLevelDictionary _allocatedBlocks = new LowLevelDictionary(); - private Lock _lock = new Lock(); - - public unsafe IntPtr GetMemoryBlockForValue(ThreadStaticFieldOffsets value) - { - using (LockHolder.Hold(_lock)) - { - IntPtr result; - if (_allocatedBlocks.TryGetValue(value, out result)) - { - return result; - } - result = MemoryHelpers.AllocateMemory(sizeof(ThreadStaticFieldOffsets)); - *(ThreadStaticFieldOffsets*)(result.ToPointer()) = value; - _allocatedBlocks.Add(value, result); - return result; - } - } - } - public static IntPtr GetPointerToUInt(uint value) { return s_uintCellValues.GetMemoryBlockForValue(value); @@ -129,10 +77,5 @@ namespace Internal.Runtime.TypeLoader { return s_pointerIndirectionCellValues.GetMemoryBlockForValue(value); } - - public static IntPtr GetPointerToThreadStaticFieldOffsets(ThreadStaticFieldOffsets value) - { - return s_threadStaticFieldCookies.GetMemoryBlockForValue(value); - } } } diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/CallingConvention.h b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/CallingConvention.h index aea9596658..b707b4bc8c 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/CallingConvention.h +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ReferenceSource/CallingConvention.h @@ -13,6 +13,8 @@ #ifndef __CALLING_CONVENTION_INCLUDED #define __CALLING_CONVENTION_INCLUDED +BOOL IsRetBuffPassedAsFirstArg(); + // Describes how a single argument is laid out in registers and/or stack locations when given as an input to a // managed method as part of a larger signature. // @@ -32,14 +34,25 @@ // and possibly on to the stack as well. struct ArgLocDesc { - int m_idxFloatReg; // First floating point register used (or -1) - int m_cFloatReg; // Count of floating point registers used (or 0) + int m_idxFloatReg; // First floating point register used (or -1) + int m_cFloatReg; // Count of floating point registers used (or 0) - int m_idxGenReg; // First general register used (or -1) - int m_cGenReg; // Count of general registers used (or 0) + int m_idxGenReg; // First general register used (or -1) + int m_cGenReg; // Count of general registers used (or 0) - int m_idxStack; // First stack slot used (or -1) - int m_cStack; // Count of stack slots used (or 0) + int m_idxStack; // First stack slot used (or -1) + int m_cStack; // Count of stack slots used (or 0) + +#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + + EEClass* m_eeClass; // For structs passed in register, it points to the EEClass of the struct + +#endif // UNIX_AMD64_ABI && FEATURE_UNIX_AMD64_STRUCT_PASSING + +#if defined(_TARGET_ARM64_) + bool m_isSinglePrecision; // For determining if HFA is single or double + // precision +#endif // defined(_TARGET_ARM64_) #if defined(_TARGET_ARM_) BOOL m_fRequires64BitAlignment; // True if the argument should always be aligned (in registers or on the stack @@ -61,6 +74,12 @@ struct ArgLocDesc m_cStack = 0; #if defined(_TARGET_ARM_) m_fRequires64BitAlignment = FALSE; +#endif +#if defined(_TARGET_ARM64_) + m_isSinglePrecision = FALSE; +#endif // defined(_TARGET_ARM64_) +#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + m_eeClass = NULL; #endif } }; @@ -102,6 +121,7 @@ struct TransitionBlock }; }; ArgumentRegisters m_argumentRegisters; + TADDR padding; // Keep size of TransitionBlock as multiple of 16-byte. Simplifies code in PROLOG_WITH_TRANSITION_BLOCK #else PORTABILITY_ASSERT("TransitionBlock"); #endif @@ -137,9 +157,13 @@ struct TransitionBlock { LIMITED_METHOD_CONTRACT; +#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + return offset >= sizeof(TransitionBlock); +#else int ofsArgRegs = GetOffsetOfArgumentRegisters(); return offset >= (int) (ofsArgRegs + ARGUMENTREGISTERS_SIZE); +#endif } static BOOL IsArgumentRegisterOffset(int offset) @@ -155,14 +179,45 @@ struct TransitionBlock static UINT GetArgumentIndexFromOffset(int offset) { LIMITED_METHOD_CONTRACT; + +#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + _ASSERTE(offset != TransitionBlock::StructInRegsOffset); +#endif return (offset - GetOffsetOfArgumentRegisters()) / sizeof(TADDR); } + + static UINT GetStackArgumentIndexFromOffset(int offset) + { + LIMITED_METHOD_CONTRACT; + + return (offset - TransitionBlock::GetOffsetOfArgs()) / STACK_ELEM_SIZE; + } + #endif #ifdef CALLDESCR_FPARGREGS static BOOL IsFloatArgumentRegisterOffset(int offset) { LIMITED_METHOD_CONTRACT; +#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + return (offset != TransitionBlock::StructInRegsOffset) && (offset < 0); +#else + return offset < 0; +#endif + } + + // Check if an argument has floating point register, that means that it is + // either a floating point argument or a struct passed in registers that + // has a floating point member. + static BOOL HasFloatRegister(int offset, ArgLocDesc* argLocDescForStructInRegs) + { + LIMITED_METHOD_CONTRACT; + #if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + if (offset == TransitionBlock::StructInRegsOffset) + { + return argLocDescForStructInRegs->m_cFloatReg > 0; + } + #endif return offset < 0; } @@ -171,7 +226,7 @@ struct TransitionBlock LIMITED_METHOD_CONTRACT; return -GetNegSpaceSize(); } -#endif +#endif // CALLDESCR_FPARGREGS static int GetOffsetOfCalleeSavedRegisters() { @@ -193,6 +248,11 @@ struct TransitionBlock } static const int InvalidOffset = -1; +#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + // Special offset value to represent struct passed in registers. Such a struct can span both + // general purpose and floating point registers, so it can have two different offsets. + static const int StructInRegsOffset = -2; +#endif }; //----------------------------------------------------------------------- @@ -339,11 +399,16 @@ public: { LIMITED_METHOD_CONTRACT; +#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING + // No arguments are passed by reference on AMD64 on Unix + return FALSE; +#else // If the size is bigger than ENREGISTERED_PARAM_TYPE_MAXSIZE, or if the size is NOT a power of 2, then // the argument is passed by reference. return (size > ENREGISTERED_PARAMTYPE_MAXSIZE) || ((size & (size-1)) != 0); +#endif } -#endif +#endif // _TARGET_AMD64_ // This overload should be used for varargs only. static BOOL IsVarArgPassedByRef(size_t size) @@ -351,7 +416,13 @@ public: LIMITED_METHOD_CONTRACT; #ifdef _TARGET_AMD64_ +#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING + PORTABILITY_ASSERT("ArgIteratorTemplate::IsVarArgPassedByRef"); + return FALSE; +#else // FEATURE_UNIX_AMD64_STRUCT_PASSING return IsArgPassedByRef(size); +#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING + #else return (size > ENREGISTERED_PARAMTYPE_MAXSIZE); #endif @@ -367,7 +438,7 @@ public: if (m_argType == ELEMENT_TYPE_VALUETYPE) { _ASSERTE(!m_argTypeHandle.IsNull()); - return ((m_argSize > ENREGISTERED_PARAMTYPE_MAXSIZE) && (!m_argTypeHandle.IsHFA() || IsVarArg())); + return ((m_argSize > ENREGISTERED_PARAMTYPE_MAXSIZE) && (!m_argTypeHandle.IsHFA() || this->IsVarArg())); } return FALSE; #else @@ -425,6 +496,15 @@ public: void GetVASigCookieLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetVASigCookieOffset(), pLoc); } #endif // !_TARGET_X86_ + ArgLocDesc* GetArgLocDescForStructInRegs() + { +#if (defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)) || defined (_TARGET_ARM64_) + return m_hasArgLocDescForStructInRegs ? &m_argLocDescForStructInRegs : NULL; +#else + return NULL; +#endif + } + #ifdef _TARGET_ARM_ // Get layout information for the argument that the ArgIterator is currently visiting. void GetArgLoc(int argOffset, ArgLocDesc *pLoc) @@ -462,7 +542,7 @@ public: } else { - pLoc->m_idxStack = TransitionBlock::GetArgumentIndexFromOffset(argOffset) - 4; + pLoc->m_idxStack = TransitionBlock::GetStackArgumentIndexFromOffset(argOffset); pLoc->m_cStack = cSlots; } } @@ -484,7 +564,10 @@ public: if (!m_argTypeHandle.IsNull() && m_argTypeHandle.IsHFA()) { CorElementType type = m_argTypeHandle.GetHFAType(); - pLoc->m_cFloatReg = (type == ELEMENT_TYPE_R4)? GetArgSize()/sizeof(float): GetArgSize()/sizeof(double); + bool isFloatType = (type == ELEMENT_TYPE_R4); + + pLoc->m_cFloatReg = isFloatType ? GetArgSize()/sizeof(float): GetArgSize()/sizeof(double); + pLoc->m_isSinglePrecision = isFloatType; } else { @@ -508,7 +591,7 @@ public: } else { - pLoc->m_idxStack = TransitionBlock::GetArgumentIndexFromOffset(argOffset) - 8; + pLoc->m_idxStack = TransitionBlock::GetStackArgumentIndexFromOffset(argOffset); pLoc->m_cStack = cSlots; } } @@ -516,37 +599,46 @@ public: #if defined(_TARGET_AMD64_) && defined(UNIX_AMD64_ABI) // Get layout information for the argument that the ArgIterator is currently visiting. - void GetArgLoc(int argOffset, ArgLocDesc *pLoc) + void GetArgLoc(int argOffset, ArgLocDesc* pLoc) { LIMITED_METHOD_CONTRACT; +#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + if (m_hasArgLocDescForStructInRegs) + { + *pLoc = m_argLocDescForStructInRegs; + return; + } +#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING + + if (argOffset == TransitionBlock::StructInRegsOffset) + { + // We always already have argLocDesc for structs passed in registers, we + // compute it in the GetNextOffset for those since it is always needed. + _ASSERTE(false); + return; + } + pLoc->Init(); if (TransitionBlock::IsFloatArgumentRegisterOffset(argOffset)) { - // Dividing by 8 as size of each register in FloatArgumentRegisters is 8 bytes. - pLoc->m_idxFloatReg = (argOffset - TransitionBlock::GetOffsetOfFloatArgumentRegisters()) / 8; - - // UNIXTODO: Passing of structs, HFAs. For now, use the Windows convention. + // Dividing by 16 as size of each register in FloatArgumentRegisters is 16 bytes. + pLoc->m_idxFloatReg = (argOffset - TransitionBlock::GetOffsetOfFloatArgumentRegisters()) / 16; pLoc->m_cFloatReg = 1; - return; } - - // UNIXTODO: Passing of structs, HFAs. For now, use the Windows convention. - int cSlots = 1; - - if (!TransitionBlock::IsStackArgumentOffset(argOffset)) + else if (!TransitionBlock::IsStackArgumentOffset(argOffset)) { pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(argOffset); - pLoc->m_cGenReg = cSlots; - } + pLoc->m_cGenReg = 1; + } else { - pLoc->m_idxStack = (argOffset - TransitionBlock::GetOffsetOfArgs()) / 8; - pLoc->m_cStack = cSlots; + pLoc->m_idxStack = TransitionBlock::GetStackArgumentIndexFromOffset(argOffset); + pLoc->m_cStack = (GetArgSize() + STACK_ELEM_SIZE - 1) / STACK_ELEM_SIZE; } } -#endif // _TARGET_ARM64_ && UNIX_AMD64_ABI +#endif // _TARGET_AMD64_ && UNIX_AMD64_ABI protected: DWORD m_dwFlags; // Cached flags @@ -558,6 +650,10 @@ protected: CorElementType m_argType; int m_argSize; TypeHandle m_argTypeHandle; +#if (defined(_TARGET_AMD64_) && defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)) || defined(_TARGET_ARM64_) + ArgLocDesc m_argLocDescForStructInRegs; + bool m_hasArgLocDescForStructInRegs; +#endif // _TARGET_AMD64_ && UNIX_AMD64_ABI && FEATURE_UNIX_AMD64_STRUCT_PASSING #ifdef _TARGET_X86_ int m_curOfs; // Current position of the stack iterator @@ -566,9 +662,12 @@ protected: #ifdef _TARGET_AMD64_ #ifdef UNIX_AMD64_ABI - int m_idxGenReg; - int m_idxStack; - int m_idxFPReg; + int m_idxGenReg; // Next general register to be assigned a value + int m_idxStack; // Next stack slot to be assigned a value + int m_idxFPReg; // Next floating point register to be assigned a value +#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING + bool m_fArgInRegisters; // Indicates that the current argument is stored in registers +#endif #else int m_curOfs; // Current position of the stack iterator #endif @@ -649,6 +748,8 @@ int ArgIteratorTemplate::GetRetBuffArgOffset() #if _TARGET_X86_ // x86 is special as always ret += this->HasThis() ? offsetof(ArgumentRegisters, EDX) : offsetof(ArgumentRegisters, ECX); +#elif _TARGET_ARM64_ + ret += (int) offsetof(ArgumentRegisters, x[8]); #else if (this->HasThis()) ret += sizeof(void *); @@ -676,7 +777,7 @@ int ArgIteratorTemplate::GetVASigCookieOffset() ret += sizeof(void*); } - if (this->HasRetBuffArg()) + if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) { ret += sizeof(void*); } @@ -729,7 +830,7 @@ int ArgIteratorTemplate::GetParamTypeArgOffset() ret += sizeof(void*); } - if (this->HasRetBuffArg()) + if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) { ret += sizeof(void*); } @@ -760,7 +861,7 @@ int ArgIteratorTemplate::GetNextOffset() if (this->HasThis()) numRegistersUsed++; - if (this->HasRetBuffArg()) + if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) numRegistersUsed++; _ASSERTE(!this->IsVarArg() || !this->HasParamType()); @@ -842,6 +943,10 @@ int ArgIteratorTemplate::GetNextOffset() m_argSize = argSize; m_argTypeHandle = thValueType; +#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + m_hasArgLocDescForStructInRegs = false; +#endif + #ifdef _TARGET_X86_ #ifdef FEATURE_INTERPRETER if (m_fUnmanagedCallConv) @@ -861,7 +966,12 @@ int ArgIteratorTemplate::GetNextOffset() return m_curOfs; #elif defined(_TARGET_AMD64_) #ifdef UNIX_AMD64_ABI + + m_fArgInRegisters = true; + int cFPRegs = 0; + int cGenRegs = 0; + int cbArg = StackElemSize(argSize); switch (argType) { @@ -878,42 +988,90 @@ int ArgIteratorTemplate::GetNextOffset() case ELEMENT_TYPE_VALUETYPE: { - // UNIXTODO: Passing of structs, HFAs. For now, use the Windows convention. - argSize = sizeof(TADDR); +#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING + MethodTable *pMT = m_argTypeHandle.AsMethodTable(); + if (pMT->IsRegPassedStruct()) + { + EEClass* eeClass = pMT->GetClass(); + cGenRegs = 0; + for (int i = 0; i < eeClass->GetNumberEightBytes(); i++) + { + switch (eeClass->GetEightByteClassification(i)) + { + case SystemVClassificationTypeInteger: + case SystemVClassificationTypeIntegerReference: + case SystemVClassificationTypeIntegerByRef: + cGenRegs++; + break; + case SystemVClassificationTypeSSE: + cFPRegs++; + break; + default: + _ASSERTE(false); + break; + } + } + + // Check if we have enough registers available for the struct passing + if ((cFPRegs + m_idxFPReg <= NUM_FLOAT_ARGUMENT_REGISTERS) && (cGenRegs + m_idxGenReg) <= NUM_ARGUMENT_REGISTERS) + { + m_argLocDescForStructInRegs.Init(); + m_argLocDescForStructInRegs.m_cGenReg = cGenRegs; + m_argLocDescForStructInRegs.m_cFloatReg = cFPRegs; + m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg; + m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg; + m_argLocDescForStructInRegs.m_eeClass = eeClass; + + m_hasArgLocDescForStructInRegs = true; + + m_idxGenReg += cGenRegs; + m_idxFPReg += cFPRegs; + + return TransitionBlock::StructInRegsOffset; + } + } + + // Set the register counts to indicate that this argument will not be passed in registers + cFPRegs = 0; + cGenRegs = 0; + +#else // FEATURE_UNIX_AMD64_STRUCT_PASSING + argSize = sizeof(TADDR); +#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING + break; } default: + cGenRegs = cbArg / 8; // GP reg size break; } - int cbArg = StackElemSize(argSize); + if ((cFPRegs > 0) && (cFPRegs + m_idxFPReg <= NUM_FLOAT_ARGUMENT_REGISTERS)) + { + int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 16; + m_idxFPReg += cFPRegs; + return argOfs; + } + else if ((cGenRegs > 0) && (m_idxGenReg + cGenRegs <= NUM_ARGUMENT_REGISTERS)) + { + int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8; + m_idxGenReg += cGenRegs; + return argOfs; + } + +#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + m_fArgInRegisters = false; +#endif + + int argOfs = TransitionBlock::GetOffsetOfArgs() + m_idxStack * STACK_ELEM_SIZE; + int cArgSlots = cbArg / STACK_ELEM_SIZE; - - if (cFPRegs>0) - { - if (cFPRegs + m_idxFPReg <= 8) - { - int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8; - m_idxFPReg += cFPRegs; - return argOfs; - } - } - else - { - if (m_idxGenReg + cArgSlots <= 6) - { - int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8; - m_idxGenReg += cArgSlots; - return argOfs; - } - } - - int argOfs = TransitionBlock::GetOffsetOfArgs() + m_idxStack * 8; m_idxStack += cArgSlots; + return argOfs; #else - // Each argument takes exactly one slot on AMD64 + // Each argument takes exactly one slot on AMD64 on Windows int argOfs = m_curOfs; m_curOfs += sizeof(void *); return argOfs; @@ -952,10 +1110,14 @@ int ArgIteratorTemplate::GetNextOffset() // the interop "native value types". fRequiresAlign64Bit = thValueType.RequiresAlign8(); +#ifdef FEATURE_HFA // Handle HFAs: packed structures of 1-4 floats or doubles that are passed in FP argument // registers if possible. if (thValueType.IsHFA()) + { fFloatingPoint = true; + } +#endif break; } @@ -977,6 +1139,7 @@ int ArgIteratorTemplate::GetNextOffset() // Ignore floating point argument placement in registers if we're dealing with a vararg function (the ABI // specifies this so that vararg processing on the callee side is simplified). +#ifndef ARM_SOFTFP if (fFloatingPoint && !this->IsVarArg()) { // Handle floating point (primitive) arguments. @@ -1029,6 +1192,7 @@ int ArgIteratorTemplate::GetNextOffset() return argOfs; } +#endif // ARM_SOFTFP // // Handle the non-floating point case. @@ -1104,7 +1268,17 @@ int ArgIteratorTemplate::GetNextOffset() if (thValueType.IsHFA()) { CorElementType type = thValueType.GetHFAType(); + bool isFloatType = (type == ELEMENT_TYPE_R4); + cFPRegs = (type == ELEMENT_TYPE_R4)? (argSize/sizeof(float)): (argSize/sizeof(double)); + + m_argLocDescForStructInRegs.Init(); + m_argLocDescForStructInRegs.m_cFloatReg = cFPRegs; + m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg; + + m_argLocDescForStructInRegs.m_isSinglePrecision = isFloatType; + + m_hasArgLocDescForStructInRegs = true; } else { @@ -1190,11 +1364,15 @@ void ArgIteratorTemplate::ComputeReturnFlags() break; case ELEMENT_TYPE_R4: +#ifndef ARM_SOFTFP flags |= sizeof(float) << RETURN_FP_SIZE_SHIFT; +#endif break; case ELEMENT_TYPE_R8: +#ifndef ARM_SOFTFP flags |= sizeof(double) << RETURN_FP_SIZE_SHIFT; +#endif break; case ELEMENT_TYPE_VALUETYPE: @@ -1202,6 +1380,40 @@ void ArgIteratorTemplate::ComputeReturnFlags() { _ASSERTE(!thValueType.IsNull()); +#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + MethodTable *pMT = thValueType.AsMethodTable(); + if (pMT->IsRegPassedStruct()) + { + EEClass* eeClass = pMT->GetClass(); + + if (eeClass->GetNumberEightBytes() == 1) + { + // Structs occupying just one eightbyte are treated as int / double + if (eeClass->GetEightByteClassification(0) == SystemVClassificationTypeSSE) + { + flags |= sizeof(double) << RETURN_FP_SIZE_SHIFT; + } + } + else + { + // Size of the struct is 16 bytes + flags |= (16 << RETURN_FP_SIZE_SHIFT); + // The lowest two bits of the size encode the order of the int and SSE fields + if (eeClass->GetEightByteClassification(0) == SystemVClassificationTypeSSE) + { + flags |= (1 << RETURN_FP_SIZE_SHIFT); + } + + if (eeClass->GetEightByteClassification(1) == SystemVClassificationTypeSSE) + { + flags |= (2 << RETURN_FP_SIZE_SHIFT); + } + } + + break; + } +#else // UNIX_AMD64_ABI && FEATURE_UNIX_AMD64_STRUCT_PASSING + #ifdef FEATURE_HFA if (thValueType.IsHFA() && !this->IsVarArg()) { @@ -1228,6 +1440,7 @@ void ArgIteratorTemplate::ComputeReturnFlags() if (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE) break; +#endif // UNIX_AMD64_ABI && FEATURE_UNIX_AMD64_STRUCT_PASSING } #endif // ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE @@ -1269,7 +1482,7 @@ void ArgIteratorTemplate::ForceSigWalk() if (this->HasThis()) numRegistersUsed++; - if (this->HasRetBuffArg()) + if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) numRegistersUsed++; if (this->IsVarArg()) @@ -1281,6 +1494,7 @@ void ArgIteratorTemplate::ForceSigWalk() #ifdef FEATURE_INTERPRETER BYTE callconv = CallConv(); switch (callconv) + { case IMAGE_CEE_CS_CALLCONV_C: case IMAGE_CEE_CS_CALLCONV_STDCALL: numRegistersUsed = NUM_ARGUMENT_REGISTERS; @@ -1315,11 +1529,7 @@ void ArgIteratorTemplate::ForceSigWalk() _ASSERTE(!FORBIDGC_LOADER_USE_ENABLED()); CONTRACT_VIOLATION(ThrowsViolation); #endif -#ifdef BINDER - IfFailThrow(COR_E_NOTSUPPORTED); -#else COMPlusThrow(kNotSupportedException); -#endif } #endif } @@ -1346,27 +1556,37 @@ void ArgIteratorTemplate::ForceSigWalk() int maxOffset = TransitionBlock::GetOffsetOfArgs(); - int ofs; + int ofs; while (TransitionBlock::InvalidOffset != (ofs = GetNextOffset())) { int stackElemSize; #ifdef _TARGET_AMD64_ +#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING + if (m_fArgInRegisters) + { + // Arguments passed in registers don't consume any stack + continue; + } + + stackElemSize = StackElemSize(GetArgSize()); +#else // FEATURE_UNIX_AMD64_STRUCT_PASSING // All stack arguments take just one stack slot on AMD64 because of arguments bigger // than a stack slot are passed by reference. stackElemSize = STACK_ELEM_SIZE; -#else +#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING +#else // _TARGET_AMD64_ stackElemSize = StackElemSize(GetArgSize()); #if defined(ENREGISTERED_PARAMTYPE_MAXSIZE) if (IsArgPassedByRef()) stackElemSize = STACK_ELEM_SIZE; #endif -#endif +#endif // _TARGET_AMD64_ int endOfs = ofs + stackElemSize; if (endOfs > maxOffset) { -#if !defined(DACCESS_COMPILE) && !defined(BINDER) +#if !defined(DACCESS_COMPILE) if (endOfs > MAX_ARG_SIZE) { #ifdef _DEBUG @@ -1504,4 +1724,25 @@ inline BOOL HasRetBuffArg(MetaSig * pSig) return argit.HasRetBuffArg(); } +#ifdef UNIX_X86_ABI +// For UNIX_X86_ABI and unmanaged function, we always need RetBuf if the return type is VALUETYPE +inline BOOL HasRetBuffArgUnmanagedFixup(MetaSig * pSig) +{ + WRAPPER_NO_CONTRACT; + // We cannot just pSig->GetReturnType() here since it will return ELEMENT_TYPE_VALUETYPE for enums + CorElementType type = pSig->GetRetTypeHandleThrowing().GetVerifierCorElementType(); + return type == ELEMENT_TYPE_VALUETYPE; +} +#endif + +inline BOOL IsRetBuffPassedAsFirstArg() +{ + WRAPPER_NO_CONTRACT; +#ifndef _TARGET_ARM64_ + return TRUE; +#else + return FALSE; +#endif +} + #endif // __CALLING_CONVENTION_INCLUDED diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/SerializedDebugData.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/SerializedDebugData.cs index 48549ac57e..03a64feab0 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/SerializedDebugData.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/SerializedDebugData.cs @@ -236,7 +236,7 @@ namespace Internal.Runtime.TypeLoader if (availableSize < requiredSize) throw new OutOfMemoryException(); } - InteropExtensions.CopyToNative(src, 0, dst, src.Length); + PInvokeMarshal.CopyToNative(src, 0, dst, src.Length); UpdatePhysicalBufferUsedSize(); // make sure that used physical buffer size is updated } } diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs deleted file mode 100644 index 830e4dfaef..0000000000 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs +++ /dev/null @@ -1,2123 +0,0 @@ -// 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.Collections.Generic; -using System.Diagnostics; -using System.Reflection; -using System.Runtime; -using System.Text; - -using System.Reflection.Runtime.General; - -using Internal.Runtime.Augments; -using Internal.Runtime.CompilerServices; - -using Internal.Metadata.NativeFormat; -using Internal.NativeFormat; -using Internal.TypeSystem; -using Internal.TypeSystem.NativeFormat; -using Internal.TypeSystem.NoMetadata; - -namespace Internal.Runtime.TypeLoader -{ - using DynamicGenericsRegistrationData = TypeLoaderEnvironment.DynamicGenericsRegistrationData; - using GenericTypeEntry = TypeLoaderEnvironment.GenericTypeEntry; - using TypeEntryToRegister = TypeLoaderEnvironment.TypeEntryToRegister; - using GenericMethodEntry = TypeLoaderEnvironment.GenericMethodEntry; - using HandleBasedGenericTypeLookup = TypeLoaderEnvironment.HandleBasedGenericTypeLookup; - using DefTypeBasedGenericTypeLookup = TypeLoaderEnvironment.DefTypeBasedGenericTypeLookup; - using HandleBasedGenericMethodLookup = TypeLoaderEnvironment.HandleBasedGenericMethodLookup; - using MethodDescBasedGenericMethodLookup = TypeLoaderEnvironment.MethodDescBasedGenericMethodLookup; - using ThunkKind = CallConverterThunk.ThunkKind; - using VTableSlotMapper = TypeBuilderState.VTableSlotMapper; - - internal static class LowLevelListExtensions - { - public static void Expand(this LowLevelList list, int count) - { - if (list.Capacity < count) - list.Capacity = count; - - while (list.Count < count) - list.Add(default(T)); - } - - public static bool HasSetBits(this LowLevelList list) - { - for (int index = 0; index < list.Count; index++) - { - if (list[index]) - return true; - } - - return false; - } - } - - [Flags] - internal enum FieldLoadState - { - None = 0, - Instance = 1, - Statics = 2, - } - - public static class TypeBuilderApi - { - public static void ResolveMultipleCells(GenericDictionaryCell [] cells, out IntPtr[] fixups) - { - TypeBuilder.ResolveMultipleCells(cells, out fixups); - } - } - - - internal class TypeBuilder - { - public TypeBuilder() - { - TypeLoaderEnvironment.Instance.VerifyTypeLoaderLockHeld(); - } - - private const int MinimumValueTypeSize = 0x1; - - /// - /// The StaticClassConstructionContext for a type is encoded in the negative space - /// of the NonGCStatic fields of a type. - /// - public static unsafe readonly int ClassConstructorOffset = -sizeof(System.Runtime.CompilerServices.StaticClassConstructionContext); - - private LowLevelList _typesThatNeedTypeHandles = new LowLevelList(); - - private LowLevelList _methodsThatNeedDictionaries = new LowLevelList(); - - private LowLevelList _typesThatNeedPreparation = null; - - private Object _epoch = new Object(); - -#if DEBUG - private bool _finalTypeBuilding = false; -#endif - - // Helper exception to abort type building if we do not find the generic type template - internal class MissingTemplateException : Exception - { - } - - - private bool CheckAllHandlesValidForMethod(MethodDesc method) - { - if (!method.OwningType.RetrieveRuntimeTypeHandleIfPossible()) - return false; - - for (int i = 0; i < method.Instantiation.Length; i++) - if (!method.Instantiation[i].RetrieveRuntimeTypeHandleIfPossible()) - return false; - - return true; - } - - internal bool RetrieveExactFunctionPointerIfPossible(MethodDesc method, out IntPtr result) - { - result = IntPtr.Zero; - - if (!method.IsNonSharableMethod || !CheckAllHandlesValidForMethod(method)) - return false; - - RuntimeTypeHandle[] genMethodArgs = method.Instantiation.Length > 0 ? new RuntimeTypeHandle[method.Instantiation.Length] : Empty.Array; - for (int i = 0; i < method.Instantiation.Length; i++) - genMethodArgs[i] = method.Instantiation[i].RuntimeTypeHandle; - - return TypeLoaderEnvironment.Instance.TryLookupExactMethodPointerForComponents(method.OwningType.RuntimeTypeHandle, method.NameAndSignature, genMethodArgs, out result); - } - - internal bool RetrieveMethodDictionaryIfPossible(InstantiatedMethod method) - { - if (method.RuntimeMethodDictionary != IntPtr.Zero) - return true; - - bool allHandlesValid = CheckAllHandlesValidForMethod(method); - - TypeLoaderLogger.WriteLine("Looking for method dictionary for method " + method.ToString() + " ... " + (allHandlesValid ? "(All type arg handles valid)" : "")); - - IntPtr methodDictionary; - - if ((allHandlesValid && TypeLoaderEnvironment.Instance.TryLookupGenericMethodDictionaryForComponents(new HandleBasedGenericMethodLookup(method), out methodDictionary)) || - (!allHandlesValid && TypeLoaderEnvironment.Instance.TryLookupGenericMethodDictionaryForComponents(new MethodDescBasedGenericMethodLookup(method), out methodDictionary))) - { - TypeLoaderLogger.WriteLine("Found DICT = " + methodDictionary.LowLevelToString() + " for method " + method.ToString()); - method.AssociateWithRuntimeMethodDictionary(methodDictionary); - return true; - } - - return false; - } - - /// - /// Register the type for preparation. The preparation will be done once the current type is prepared. - /// This is the prefered way to get a dependent type prepared because of it avoids issues with cycles and recursion. - /// - public void RegisterForPreparation(TypeDesc type) - { - TypeLoaderLogger.WriteLine("Register for preparation " + type.ToString() + " ..."); - - // If this type has type handle, do nothing and return - if (type.RetrieveRuntimeTypeHandleIfPossible()) - return; - - var state = type.GetOrCreateTypeBuilderState(); - - // If this type was already inspected, do nothing and return. - if (state.NeedsTypeHandle) - return; - - state.NeedsTypeHandle = true; - - if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) - return; - - if (_typesThatNeedPreparation == null) - _typesThatNeedPreparation = new LowLevelList(); - - _typesThatNeedPreparation.Add(type); - } - - /// - /// Collects all dependencies that need to be created in order to create - /// the method that was passed in. - /// - public void PrepareMethod(MethodDesc method) - { - TypeLoaderLogger.WriteLine("Preparing method " + method.ToString() + " ..."); - - RegisterForPreparation(method.OwningType); - - if (method.Instantiation.Length == 0) - return; - - InstantiatedMethod genericMethod = (InstantiatedMethod)method; - - if (RetrieveMethodDictionaryIfPossible(genericMethod)) - return; - - // If this method was already inspected, do nothing and return - if (genericMethod.NeedsDictionary) - return; - - genericMethod.NeedsDictionary = true; - - if (genericMethod.IsCanonicalMethod(CanonicalFormKind.Any)) - return; - - _methodsThatNeedDictionaries.Add(genericMethod); - - foreach (var type in genericMethod.Instantiation) - RegisterForPreparation(type); - - ParseNativeLayoutInfo(genericMethod); - } - - private void InsertIntoNeedsTypeHandleList(TypeBuilderState state, TypeDesc type) - { - if ((type is DefType) || (type is ArrayType) || (type is PointerType) || (type is ByRefType)) - { - _typesThatNeedTypeHandles.Add(type); - } - } - - /// - /// Collects all dependencies that need to be created in order to create - /// the type that was passed in. - /// - internal void PrepareType(TypeDesc type) - { - TypeLoaderLogger.WriteLine("Preparing type " + type.ToString() + " ..."); - - TypeBuilderState state = type.GetTypeBuilderStateIfExist(); - bool hasTypeHandle = type.RetrieveRuntimeTypeHandleIfPossible(); - - // If this type has type handle, do nothing and return unless we should prepare even in the presence of a type handle - if (hasTypeHandle) - return; - - if (state == null) - state = type.GetOrCreateTypeBuilderState(); - - // If this type was already prepared, do nothing unless we are re-preparing it for the purpose of loading the field layout - if (state.HasBeenPrepared) - { - return; - } - - state.HasBeenPrepared = true; - state.NeedsTypeHandle = true; - - if (!hasTypeHandle) - { - InsertIntoNeedsTypeHandleList(state, type); - } - - bool noExtraPreparation = false; // Set this to true for types which don't need other types to be prepared. I.e GenericTypeDefinitions - - if (type is DefType) - { - DefType typeAsDefType = (DefType)type; - - if (typeAsDefType.HasInstantiation) - { - if (typeAsDefType.IsTypeDefinition) - { - noExtraPreparation = true; - } - else - { - // This call to ComputeTemplate will find the native layout info for the type, and the template - // For metadata loaded types, a template will not exist, but we may find the NativeLayout describing the generic dictionary - typeAsDefType.ComputeTemplate(state, false); - - Debug.Assert(state.TemplateType == null || (state.TemplateType is DefType && !state.TemplateType.RuntimeTypeHandle.IsNull())); - - // Collect dependencies - - // We need the instantiation arguments to register a generic type - foreach (var instArg in typeAsDefType.Instantiation) - RegisterForPreparation(instArg); - - // We need the type definition to register a generic type - if (type.GetTypeDefinition() is MetadataType) - RegisterForPreparation(type.GetTypeDefinition()); - - ParseNativeLayoutInfo(state, type); - } - } - - if (!noExtraPreparation) - state.PrepareStaticGCLayout(); - } - else if (type is ParameterizedType) - { - PrepareType(((ParameterizedType)type).ParameterType); - - if (type is ArrayType) - { - ArrayType typeAsArrayType = (ArrayType)type; - - if (typeAsArrayType.IsSzArray && !typeAsArrayType.ElementType.IsPointer) - { - typeAsArrayType.ComputeTemplate(state); - Debug.Assert(state.TemplateType != null && state.TemplateType is ArrayType && !state.TemplateType.RuntimeTypeHandle.IsNull()); - - ParseNativeLayoutInfo(state, type); - } - else - { - Debug.Assert(typeAsArrayType.IsMdArray || typeAsArrayType.ElementType.IsPointer); - } - - // Assert that non-valuetypes are considered to have pointer size - Debug.Assert(typeAsArrayType.ParameterType.IsValueType || state.ComponentSize == IntPtr.Size); - } - } - else - { - Debug.Assert(false); - } - - // Need to prepare the base type first since it is used to compute interfaces - if (!noExtraPreparation) - { - PrepareBaseTypeAndDictionaries(type); - PrepareRuntimeInterfaces(type); - - TypeLoaderLogger.WriteLine("Layout for type " + type.ToString() + " complete." + - " IsHFA = " + (state.IsHFA ? "true" : "false") + - " Type size = " + (state.TypeSize.HasValue ? state.TypeSize.Value.LowLevelToString() : "UNDEF") + - " Fields size = " + (state.UnalignedTypeSize.HasValue ? state.UnalignedTypeSize.Value.LowLevelToString() : "UNDEF") + - " Type alignment = " + (state.FieldAlignment.HasValue ? state.FieldAlignment.Value.LowLevelToString() : "UNDEF")); - - if (state.TemplateType != null && state.TemplateType.IsCanonicalSubtype(CanonicalFormKind.Universal)) - { - state.VTableSlotsMapping = new VTableSlotMapper(state.TemplateType.RuntimeTypeHandle.GetNumVtableSlots()); - ComputeVTableLayout(type, state.TemplateType, state); - } - } - } - - /// - /// Recursively triggers preparation for a type's runtime interfaces - /// - private void PrepareRuntimeInterfaces(TypeDesc type) - { - // Prepare all the interfaces that might be used. (This can be a superset of the - // interfaces explicitly in the NativeLayout.) - foreach (DefType interfaceType in type.RuntimeInterfaces) - { - PrepareType(interfaceType); - } - } - - /// - /// Triggers preparation for a type's base types - /// - private void PrepareBaseTypeAndDictionaries(TypeDesc type) - { - DefType baseType = type.BaseType; - if (baseType == null) - return; - - PrepareType(baseType); - } - - private void ProcessTypesNeedingPreparation() - { - // Process the pending types - while (_typesThatNeedPreparation != null) - { - var pendingTypes = _typesThatNeedPreparation; - _typesThatNeedPreparation = null; - - for (int i = 0; i < pendingTypes.Count; i++) - PrepareType(pendingTypes[i]); - } - } - - private GenericDictionaryCell[] GetGenericMethodDictionaryCellsForMetadataBasedLoad(InstantiatedMethod method, InstantiatedMethod nonTemplateMethod) - { -#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING - uint r2rNativeLayoutInfoToken; - GenericDictionaryCell[] cells = null; - NativeFormatModuleInfo r2rNativeLayoutModuleInfo; - - if ((new TemplateLocator()).TryGetMetadataNativeLayout(nonTemplateMethod, out r2rNativeLayoutModuleInfo, out r2rNativeLayoutInfoToken)) - { - // ReadyToRun dictionary parsing - NativeReader readyToRunReader = TypeLoaderEnvironment.Instance.GetNativeLayoutInfoReader(r2rNativeLayoutModuleInfo.Handle); - var readyToRunInfoParser = new NativeParser(readyToRunReader, r2rNativeLayoutInfoToken); - - // A null readyToRunInfoParser is a valid situation to end up in - // This can happen if either we have exact code for a method, or if - // we are going to use the universal generic implementation. - // In both of those cases, we do not have any generic dictionary cells - // to put into the dictionary - if (!readyToRunInfoParser.IsNull) - { - NativeFormatMetadataUnit nativeMetadataUnit = method.Context.ResolveMetadataUnit(r2rNativeLayoutModuleInfo); - FixupCellMetadataResolver resolver = new FixupCellMetadataResolver(nativeMetadataUnit, nonTemplateMethod); - cells = GenericDictionaryCell.BuildDictionaryFromMetadataTokensAndContext(this, readyToRunInfoParser, nativeMetadataUnit, resolver); - } - } - - return cells; -#else - return null; -#endif - } - - internal void ParseNativeLayoutInfo(InstantiatedMethod method) - { - TypeLoaderLogger.WriteLine("Parsing NativeLayoutInfo for method " + ToString() + " ..."); - - Debug.Assert(method.Dictionary == null); - - InstantiatedMethod nonTemplateMethod = method; - - // Templates are always non-unboxing stubs - if (method.UnboxingStub) - { - // Strip unboxing stub, note the first parameter which is false - nonTemplateMethod = (InstantiatedMethod)method.Context.ResolveGenericMethodInstantiation(false, (DefType)method.OwningType, method.NameAndSignature, method.Instantiation, IntPtr.Zero, false); - } - - uint nativeLayoutInfoToken; - NativeFormatModuleInfo nativeLayoutModule; - MethodDesc templateMethod = (new TemplateLocator()).TryGetGenericMethodTemplate(nonTemplateMethod, out nativeLayoutModule, out nativeLayoutInfoToken); - - // If the templateMethod found in the static image is missing or universal, see if the R2R layout - // can provide something more specific. - if ((templateMethod == null) || templateMethod.IsCanonicalMethod(CanonicalFormKind.Universal)) - { - GenericDictionaryCell[] cells = GetGenericMethodDictionaryCellsForMetadataBasedLoad(method, nonTemplateMethod); - - if (cells != null) - { - method.SetGenericDictionary(new GenericMethodDictionary(cells)); - return; - } - - if (templateMethod == null) - { - // In this case we were looking for the r2r template to create the dictionary, but - // there isn't one. This implies that we don't need a Canon specific dictionary - // so just generate something empty - method.SetGenericDictionary(new GenericMethodDictionary(Array.Empty())); - return; - } - } - - // Ensure that if this method is non-shareable from a normal canonical perspective, then - // its template MUST be a universal canonical template method - Debug.Assert(!method.IsNonSharableMethod || (method.IsNonSharableMethod && templateMethod.IsCanonicalMethod(CanonicalFormKind.Universal))); - - NativeReader nativeLayoutInfoReader = TypeLoaderEnvironment.Instance.GetNativeLayoutInfoReader(nativeLayoutModule.Handle); - - var methodInfoParser = new NativeParser(nativeLayoutInfoReader, nativeLayoutInfoToken); - var context = new NativeLayoutInfoLoadContext - { - _typeSystemContext = method.Context, - _typeArgumentHandles = method.OwningType.Instantiation, - _methodArgumentHandles = method.Instantiation, - _module = nativeLayoutModule - }; - - BagElementKind kind; - while ((kind = methodInfoParser.GetBagElementKind()) != BagElementKind.End) - { - switch (kind) - { - case BagElementKind.DictionaryLayout: - TypeLoaderLogger.WriteLine("Found BagElementKind.DictionaryLayout"); - method.SetGenericDictionary(new GenericMethodDictionary(GenericDictionaryCell.BuildDictionary(this, context, methodInfoParser.GetParserFromRelativeOffset()))); - break; - - default: - Debug.Assert(false, "Unexpected BagElementKind for generic method with name " + method.NameAndSignature.Name + "! Only BagElementKind.DictionaryLayout should appear."); - throw new BadImageFormatException(); - } - } - - if (method.Dictionary == null) - method.SetGenericDictionary(new GenericMethodDictionary(Array.Empty())); - } - - internal void ParseNativeLayoutInfo(TypeBuilderState state, TypeDesc type) - { - TypeLoaderLogger.WriteLine("Parsing NativeLayoutInfo for type " + type.ToString() + " ..."); - - bool isTemplateUniversalCanon = false; - if (state.TemplateType != null) - { - isTemplateUniversalCanon = state.TemplateType.IsCanonicalSubtype(CanonicalFormKind.Universal); - } - - // If we found the universal template, see if there is a ReadyToRun dictionary description available. - // If so, use that, otherwise, run down the template type loader path with the universal template - if ((state.TemplateType == null) || isTemplateUniversalCanon) - { - // CanonAlike types do not get dictionaries - if ((state.TemplateType == null) && (type.IsConstructedOverType(type.Context.CanonAlikeTypeArray))) - return; - - // ReadyToRun case - Native Layout is just the dictionary - NativeParser readyToRunInfoParser = state.GetParserForReadyToRunNativeLayoutInfo(); - GenericDictionaryCell[] cells = null; - - // A null readyToRunInfoParser is a valid situation to end up in - // This can happen if either we have exact code for the method on a type, or if - // we are going to use the universal generic implementation. - // In both of those cases, we do not have any generic dictionary cells - // to put into the dictionary - if (!readyToRunInfoParser.IsNull) - { -#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING - NativeFormatMetadataUnit nativeMetadataUnit = type.Context.ResolveMetadataUnit(state.R2RNativeLayoutInfo.Module); - FixupCellMetadataResolver resolver = new FixupCellMetadataResolver(nativeMetadataUnit, type); - cells = GenericDictionaryCell.BuildDictionaryFromMetadataTokensAndContext(this, readyToRunInfoParser, nativeMetadataUnit, resolver); -#endif - } - state.Dictionary = cells != null ? new GenericTypeDictionary(cells) : null; - - if (state.TemplateType == null) - return; - } - - NativeParser typeInfoParser = state.GetParserForNativeLayoutInfo(); - NativeLayoutInfoLoadContext context = state.NativeLayoutInfo.LoadContext; - - NativeParser baseTypeParser = new NativeParser(); - - int nonGcDataSize = 0; - int gcDataSize = 0; - int threadDataSize = 0; - bool staticSizesMeaningful = (type is DefType) // Is type permitted to have static fields - && !isTemplateUniversalCanon; // Non-universal templates always specify their statics sizes - // if the size can be greater than 0 - - int baseTypeSize = 0; - bool checkBaseTypeSize = false; - - BagElementKind kind; - while ((kind = typeInfoParser.GetBagElementKind()) != BagElementKind.End) - { - switch (kind) - { - case BagElementKind.BaseType: - TypeLoaderLogger.WriteLine("Found BagElementKind.BaseType"); - Debug.Assert(baseTypeParser.IsNull); - baseTypeParser = typeInfoParser.GetParserFromRelativeOffset(); - break; - - case BagElementKind.BaseTypeSize: - TypeLoaderLogger.WriteLine("Found BagElementKind.BaseTypeSize"); - Debug.Assert(state.TemplateType.IsCanonicalSubtype(CanonicalFormKind.Universal)); - baseTypeSize = checked((int)typeInfoParser.GetUnsigned()); - break; - - case BagElementKind.ImplementedInterfaces: - TypeLoaderLogger.WriteLine("Found BagElementKind.ImplementedInterfaces"); - // Interface handling is done entirely in NativeLayoutInterfacesAlgorithm - typeInfoParser.GetUnsigned(); - break; - - case BagElementKind.TypeFlags: - { - TypeLoaderLogger.WriteLine("Found BagElementKind.TypeFlags"); - Internal.NativeFormat.TypeFlags flags = (Internal.NativeFormat.TypeFlags)typeInfoParser.GetUnsigned(); - Debug.Assert(state.HasStaticConstructor == ((flags & Internal.NativeFormat.TypeFlags.HasClassConstructor) != 0)); - } - break; - - case BagElementKind.ClassConstructorPointer: - TypeLoaderLogger.WriteLine("Found BagElementKind.ClassConstructorPointer"); - state.ClassConstructorPointer = context.GetGCStaticInfo(typeInfoParser.GetUnsigned()); - break; - - case BagElementKind.NonGcStaticDataSize: - TypeLoaderLogger.WriteLine("Found BagElementKind.NonGcStaticDataSize"); - // Use checked typecast to int to ensure there aren't any overflows/truncations (size value used in allocation of memory later) - nonGcDataSize = checked((int)typeInfoParser.GetUnsigned()); - Debug.Assert(staticSizesMeaningful); - break; - - case BagElementKind.GcStaticDataSize: - TypeLoaderLogger.WriteLine("Found BagElementKind.GcStaticDataSize"); - // Use checked typecast to int to ensure there aren't any overflows/truncations (size value used in allocation of memory later) - gcDataSize = checked((int)typeInfoParser.GetUnsigned()); - Debug.Assert(staticSizesMeaningful); - break; - - case BagElementKind.ThreadStaticDataSize: - TypeLoaderLogger.WriteLine("Found BagElementKind.ThreadStaticDataSize"); - // Use checked typecast to int to ensure there aren't any overflows/truncations (size value used in allocation of memory later) - threadDataSize = checked((int)typeInfoParser.GetUnsigned()); - Debug.Assert(staticSizesMeaningful); - break; - - case BagElementKind.GcStaticDesc: - TypeLoaderLogger.WriteLine("Found BagElementKind.GcStaticDesc"); - state.GcStaticDesc = context.GetGCStaticInfo(typeInfoParser.GetUnsigned()); - break; - - case BagElementKind.ThreadStaticDesc: - TypeLoaderLogger.WriteLine("Found BagElementKind.ThreadStaticDesc"); - state.ThreadStaticDesc = context.GetGCStaticInfo(typeInfoParser.GetUnsigned()); - break; - - case BagElementKind.GenericVarianceInfo: - TypeLoaderLogger.WriteLine("Found BagElementKind.GenericVarianceInfo"); - NativeParser varianceInfoParser = typeInfoParser.GetParserFromRelativeOffset(); - state.GenericVarianceFlags = new int[varianceInfoParser.GetSequenceCount()]; - for (int i = 0; i < state.GenericVarianceFlags.Length; i++) - state.GenericVarianceFlags[i] = checked((int)varianceInfoParser.GetUnsigned()); - break; - - case BagElementKind.FieldLayout: - TypeLoaderLogger.WriteLine("Found BagElementKind.FieldLayout"); - typeInfoParser.SkipInteger(); // Handled in type layout algorithm - break; - - case BagElementKind.VTableMethodSignatures: - TypeLoaderLogger.WriteLine("Found BagElementKind.VTableMethodSignatures"); - ParseVTableMethodSignatures(state, context, typeInfoParser.GetParserFromRelativeOffset()); - break; - - case BagElementKind.SealedVTableEntries: - TypeLoaderLogger.WriteLine("Found BagElementKind.SealedVTableEntries"); - state.NumSealedVTableEntries = typeInfoParser.GetUnsigned(); - break; - - case BagElementKind.DictionaryLayout: - TypeLoaderLogger.WriteLine("Found BagElementKind.DictionaryLayout"); - Debug.Assert(!isTemplateUniversalCanon, "Universal template nativelayout do not have DictionaryLayout"); - - if (type.IsConstructedOverType(type.Context.CanonAlikeTypeArray)) - { - TypeLoaderLogger.WriteLine("Type is CanonAlike, skip generation of dictionary"); - typeInfoParser.SkipInteger(); - break; - } - - Debug.Assert(state.Dictionary == null); - if (!state.TemplateType.RetrieveRuntimeTypeHandleIfPossible()) - { - TypeLoaderLogger.WriteLine("ERROR: failed to get type handle for template type " + state.TemplateType.ToString()); - throw new TypeBuilder.MissingTemplateException(); - } - state.Dictionary = new GenericTypeDictionary(GenericDictionaryCell.BuildDictionary(this, context, typeInfoParser.GetParserFromRelativeOffset())); - break; - - default: - TypeLoaderLogger.WriteLine("Found unknown BagElementKind: " + ((int)kind).LowLevelToString()); - typeInfoParser.SkipInteger(); - break; - } - } - - if (staticSizesMeaningful) - { - Debug.Assert((state.NonGcDataSize + (state.HasStaticConstructor ? TypeBuilder.ClassConstructorOffset : 0)) == nonGcDataSize); - Debug.Assert(state.GcDataSize == gcDataSize); - Debug.Assert(state.ThreadDataSize == threadDataSize); - } - -#if GENERICS_FORCE_USG - if (isTemplateUniversalCanon && type.CanShareNormalGenericCode()) - { - // Even in the GENERICS_FORCE_USG stress mode today, codegen will generate calls to normal-canonical target methods whenever possible. - // Given that we use universal template types to build the dynamic EETypes, these dynamic types will end up with NULL dictionary - // entries, causing the normal-canonical code sharing to fail. - // To fix this problem, we will load the generic dictionary from the non-universal template type, and build a generic dictionary out of - // it for the dynamic type, and store that dictionary pointer in the dynamic EEtype's structure. - TypeBuilderState tempState = new TypeBuilderState(); - tempState.NativeLayoutInfo = new NativeLayoutInfo(); - state.NonUniversalTemplateType = tempState.TemplateType = type.Context.TemplateLookup.TryGetNonUniversalTypeTemplate(type, ref tempState.NativeLayoutInfo); - if (tempState.TemplateType != null) - { - Debug.Assert(!tempState.TemplateType.IsCanonicalSubtype(CanonicalFormKind.UniversalCanonLookup)); - NativeParser nonUniversalTypeInfoParser = GetNativeLayoutInfoParser(type, ref tempState.NativeLayoutInfo); - NativeParser dictionaryLayoutParser = nonUniversalTypeInfoParser.GetParserForBagElementKind(BagElementKind.DictionaryLayout); - if (!dictionaryLayoutParser.IsNull) - state.Dictionary = new GenericTypeDictionary(GenericDictionaryCell.BuildDictionary(this, context, dictionaryLayoutParser)); - - // Get the non-universal GCDesc pointers, so we can compare them the ones we will dynamically construct for the type - // and verify they are equal (This is an easy and predictable way of validation for the GCDescs creation logic in the stress mode) - GetNonUniversalGCDescPointers(type, state, tempState); - } - } -#endif - type.ParseBaseType(context, baseTypeParser); - - // Assert that parsed base type size matches the BaseTypeSize that we calculated. - Debug.Assert(!checkBaseTypeSize || state.BaseTypeSize == baseTypeSize); - } - - private void ParseVTableMethodSignatures(TypeBuilderState state, NativeLayoutInfoLoadContext nativeLayoutInfoLoadContext, NativeParser methodSignaturesParser) - { - TypeDesc type = state.TypeBeingBuilt; - if (methodSignaturesParser.IsNull) - return; - - // Processing vtable method signatures is only meaningful in the context of universal generics only - Debug.Assert(state.TemplateType != null && state.TemplateType.IsCanonicalSubtype(CanonicalFormKind.Universal)); - - uint numSignatures = methodSignaturesParser.GetUnsigned(); - - state.VTableMethodSignatures = new TypeBuilderState.VTableLayoutInfo[numSignatures]; - - for (int i = 0; i < numSignatures; i++) - { - state.VTableMethodSignatures[i] = new TypeBuilderState.VTableLayoutInfo(); - - uint slot = methodSignaturesParser.GetUnsigned(); - state.VTableMethodSignatures[i].VTableSlot = (slot >> 1); - if ((slot & 1) == 1) - { - state.VTableMethodSignatures[i].IsSealedVTableSlot = true; - state.NumSealedVTableMethodSignatures++; - } - - NativeParser sigParser = methodSignaturesParser.GetParserFromRelativeOffset(); - state.VTableMethodSignatures[i].MethodSignature = RuntimeSignature.CreateFromNativeLayoutSignature(nativeLayoutInfoLoadContext._module.Handle, sigParser.Offset); - } - } - - private unsafe void ComputeVTableLayout(TypeDesc currentType, TypeDesc currentTemplateType, TypeBuilderState targetTypeState) - { - TypeDesc baseType = GetBaseTypeThatIsCorrectForMDArrays(currentType); - TypeDesc baseTemplateType = GetBaseTypeUsingRuntimeTypeHandle(currentTemplateType); - - Debug.Assert((baseType == null && baseTemplateType == null) || (baseType != null && baseTemplateType != null)); - - // Compute the vtable layout for the current type starting with base types first - if (baseType != null) - ComputeVTableLayout(baseType, baseTemplateType, targetTypeState); - - currentTemplateType.RetrieveRuntimeTypeHandleIfPossible(); - Debug.Assert(!currentTemplateType.RuntimeTypeHandle.IsNull()); - Debug.Assert(baseTemplateType == null || !baseTemplateType.RuntimeTypeHandle.IsNull()); - - // The m_usNumVtableSlots field on EETypes includes the count of vtable slots of the base type, - // so make sure we don't count that twice! - int currentVtableIndex = baseTemplateType == null ? 0 : baseTemplateType.RuntimeTypeHandle.GetNumVtableSlots(); - - IntPtr dictionarySlotInVtable = IntPtr.Zero; - - if (currentType.IsGeneric()) - { - if (!currentType.CanShareNormalGenericCode() && currentTemplateType.IsCanonicalSubtype(CanonicalFormKind.Universal)) - { - // We are building a type that cannot share code with normal canonical types, so the type has to have - // the same vtable layout as non-shared generics, meaning no dictionary pointer in the vtable. - // We use universal canonical template types to build such types. Universal canonical types have 'NULL' - // dictionary pointers in their vtables, so we'll start copying the vtable entries right after that - // dictionary slot (dictionaries are accessed/used at runtime in a different way, not through the vtable - // dictionary pointer for such types). - currentVtableIndex++; - } - else if (currentType.CanShareNormalGenericCode()) - { - // In the case of a normal canonical type in their base class hierarchy, - // we need to keep track of its dictionary slot in the vtable mapping, and try to - // copy its value values directly from its template type vtable. - // Two possible cases: - // 1) The template type is a normal canonical type. In this case, the dictionary value - // in the vtable slot of the template is NULL, but that's ok because this case is - // correctly handled anyways by the FinishBaseTypeAndDictionaries() API. - // 2) The template type is NOT a canonical type. In this case, the dictionary value - // in the vtable slot of the template is not null, and we keep track of it in the - // VTableSlotsMapping so we can copy it to the dynamic type after creation. - // This corner case is not handled by FinishBaseTypeAndDictionaries(), so we track it - // here. - // Examples: - // 1) Derived : Base, instantiated over [int,string] - // 2) Derived<__Universal> : BaseClass, and BaseClass : BaseBaseClass - // 3) Derived<__Universal> : BaseClass - Debug.Assert(currentTemplateType != null && !currentTemplateType.RuntimeTypeHandle.IsNull()); - - IntPtr* pTemplateVtable = (IntPtr*)((byte*)(currentTemplateType.RuntimeTypeHandle.ToEETypePtr()) + sizeof(EEType)); - Debug.Assert( - (pTemplateVtable[currentVtableIndex] == IntPtr.Zero && currentTemplateType.IsCanonicalSubtype(CanonicalFormKind.Any)) || - (pTemplateVtable[currentVtableIndex] != IntPtr.Zero && !currentTemplateType.IsCanonicalSubtype(CanonicalFormKind.Any))); - dictionarySlotInVtable = pTemplateVtable[currentVtableIndex]; - } - } - else if (currentType is ArrayType) - { - if (currentTemplateType.IsCanonicalSubtype(CanonicalFormKind.Universal)) - { - TypeDesc canonicalElementType = currentType.Context.ConvertToCanon(((ArrayType)currentType).ElementType, CanonicalFormKind.Specific); - bool quickIsNotCanonical = canonicalElementType == ((ArrayType)currentType).ElementType; - - Debug.Assert(quickIsNotCanonical == !canonicalElementType.IsCanonicalSubtype(CanonicalFormKind.Any)); - - if (quickIsNotCanonical) - { - // We are building a type that cannot share code with normal canonical types, so the type has to have - // the same vtable layout as non-shared generics, meaning no dictionary pointer in the vtable. - // We use universal canonical template types to build such types. Universal canonical types have 'NULL' - // dictionary pointers in their vtables, so we'll start copying the vtable entries right after that - // dictionary slot (dictionaries are accessed/used at runtime in a different way, not through the vtable - // dictionary pointer for such types). - currentVtableIndex++; - } - } - } - - // Map vtable entries from target type's template type - int numVtableSlotsOnCurrentTemplateType = currentTemplateType.RuntimeTypeHandle.GetNumVtableSlots(); - for (; currentVtableIndex < numVtableSlotsOnCurrentTemplateType; currentVtableIndex++) - { - targetTypeState.VTableSlotsMapping.AddMapping( - currentVtableIndex, - targetTypeState.VTableSlotsMapping.NumSlotMappings, - dictionarySlotInVtable); - - // Reset dictionarySlotInVtable (only one dictionary slot in vtable per type) - dictionarySlotInVtable = IntPtr.Zero; - } - - // Sanity check: vtable of the dynamic type should be equal or smaller than the vtable of the template type - Debug.Assert(targetTypeState.VTableSlotsMapping.NumSlotMappings <= numVtableSlotsOnCurrentTemplateType); - } - - /// - /// Wraps information about how a type is laid out into one package. Types may have been laid out by - /// TypeBuilder (which means they have a gc bitfield), or they could be types that were laid out by NUTC - /// (which means we only have a GCDesc for them). This struct wraps both of those possibilities into - /// one package to be able to write that layout to another bitfield we are constructing. (This is for - /// struct fields.) - /// - internal unsafe struct GCLayout - { - private LowLevelList _bitfield; - private unsafe void* _gcdesc; - private int _size; - private bool _isReferenceTypeGCLayout; - - public static GCLayout None { get { return new GCLayout(); } } - public static GCLayout SingleReference { get; } = new GCLayout(new LowLevelList(new bool[1] { true }), false); - - public bool IsNone { get { return _bitfield == null && _gcdesc == null; } } - - public GCLayout(LowLevelList bitfield, bool isReferenceTypeGCLayout) - { - Debug.Assert(bitfield != null); - - _bitfield = bitfield; - _gcdesc = null; - _size = 0; - _isReferenceTypeGCLayout = isReferenceTypeGCLayout; - } - - public GCLayout(RuntimeTypeHandle rtth) - { - EEType* eeType = rtth.ToEETypePtr(); - Debug.Assert(eeType != null); - - _bitfield = null; - _isReferenceTypeGCLayout = false; // This field is only used for the LowLevelList path - _gcdesc = eeType->HasGCPointers ? (void**)eeType - 1 : null; - _size = (int)eeType->BaseSize; - } - - /// - /// Writes this layout to the given bitfield. - /// - /// The bitfield to write a layout to (may be null, at which - /// point it will be created and assigned). - /// The offset at which we need to write the bitfield. - public void WriteToBitfield(LowLevelList bitfield, int offset) - { - if (bitfield == null) - throw new ArgumentNullException(nameof(bitfield)); - - if (IsNone) - return; - - // Ensure exactly one of these two are set. - Debug.Assert(_gcdesc != null ^ _bitfield != null); - - if (_bitfield != null) - MergeBitfields(bitfield, offset); - else - WriteGCDescToBitfield(bitfield, offset); - } - - private unsafe void WriteGCDescToBitfield(LowLevelList bitfield, int offset) - { - int startIndex = offset / IntPtr.Size; - - void** ptr = (void**)_gcdesc; - Debug.Assert(_gcdesc != null); - - // Number of series - int count = (int)*ptr-- - 1; - Debug.Assert(count >= 0); - - // Ensure capacity for the values we are about to write - int capacity = startIndex + _size / IntPtr.Size - 2; - bitfield.Expand(capacity); - - while (count-- >= 0) - { - int offs = (int)*ptr-- / IntPtr.Size - 1; - int len = ((int)*ptr-- + _size) / IntPtr.Size; - - Debug.Assert(len > 0); - Debug.Assert(offs >= 0); - - for (int i = 0; i < len; i++) - bitfield[startIndex + offs + i] = true; - } - } - - private void MergeBitfields(LowLevelList outputBitfield, int offset) - { - int startIndex = offset / IntPtr.Size; - - // These routines represent the GC layout after the EEType pointer - // in an object, but the LowLevelList bitfield logically contains - // the EETypepointer if it is describing a reference type. So, skip the - // first value. - int itemsToSkip = _isReferenceTypeGCLayout ? 1 : 0; - - // Assert that we only skip a non-reported pointer. - Debug.Assert(itemsToSkip == 0 || _bitfield[0] == false); - - // Ensure capacity for the values we are about to write - int capacity = startIndex + _bitfield.Count - itemsToSkip; - outputBitfield.Expand(capacity); - - - for (int i = itemsToSkip; i < _bitfield.Count; i++) - { - // We should never overwrite a TRUE value in the table. - Debug.Assert(!outputBitfield[startIndex + i - itemsToSkip] || _bitfield[i]); - - outputBitfield[startIndex + i - itemsToSkip] = _bitfield[i]; - } - } - } - -#if GENERICS_FORCE_USG - private unsafe void GetNonUniversalGCDescPointers(TypeDesc type, TypeBuilderState state, TypeBuilderState tempNonUniversalState) - { - NativeParser nonUniversalTypeInfoParser = GetNativeLayoutInfoParser(type, ref tempNonUniversalState.NativeLayoutInfo); - NativeLayoutInfoLoadContext context = tempNonUniversalState.NativeLayoutInfo.LoadContext; - - uint beginOffset = nonUniversalTypeInfoParser.Offset; - uint? staticGCDescId = nonUniversalTypeInfoParser.GetUnsignedForBagElementKind(BagElementKind.GcStaticDesc); - - nonUniversalTypeInfoParser.Offset = beginOffset; - uint? threadStaticGCDescId = nonUniversalTypeInfoParser.GetUnsignedForBagElementKind(BagElementKind.ThreadStaticDesc); - - if(staticGCDescId.HasValue) - state.NonUniversalStaticGCDesc = context.GetStaticInfo(staticGCDescId.Value); - - if (threadStaticGCDescId.HasValue) - state.NonUniversalThreadStaticGCDesc = context.GetStaticInfo(threadStaticGCDescId.Value); - - state.NonUniversalInstanceGCDescSize = RuntimeAugments.GetGCDescSize(tempNonUniversalState.TemplateType.RuntimeTypeHandle); - if (state.NonUniversalInstanceGCDescSize > 0) - state.NonUniversalInstanceGCDesc = new IntPtr(((byte*)tempNonUniversalState.TemplateType.RuntimeTypeHandle.ToIntPtr().ToPointer()) - 1); - } -#endif - - private unsafe void AllocateRuntimeType(TypeDesc type) - { - TypeBuilderState state = type.GetTypeBuilderState(); - - Debug.Assert(type is DefType || type is ArrayType || type is PointerType || type is ByRefType); - - if (state.ThreadDataSize != 0) - state.ThreadStaticOffset = TypeLoaderEnvironment.Instance.GetNextThreadStaticsOffsetValue(); - - RuntimeTypeHandle rtt = EETypeCreator.CreateEEType(type, state); - - if (state.ThreadDataSize != 0) - TypeLoaderEnvironment.Instance.RegisterDynamicThreadStaticsInfo(state.HalfBakedRuntimeTypeHandle, state.ThreadStaticOffset, state.ThreadDataSize); - - TypeLoaderLogger.WriteLine("Allocated new type " + type.ToString() + " with hashcode value = 0x" + type.GetHashCode().LowLevelToString() + " with eetype = " + rtt.ToIntPtr().LowLevelToString() + " of size " + rtt.ToEETypePtr()->BaseSize.LowLevelToString()); - } - - private void AllocateRuntimeMethodDictionary(InstantiatedMethod method) - { - Debug.Assert(method.RuntimeMethodDictionary == IntPtr.Zero && method.Dictionary != null); - - IntPtr rmd = method.Dictionary.Allocate(); - method.AssociateWithRuntimeMethodDictionary(rmd); - - TypeLoaderLogger.WriteLine("Allocated new method dictionary for method " + method.ToString() + " @ " + rmd.LowLevelToString()); - } - - private unsafe void FinishVTableCallingConverterThunks(TypeDesc type, TypeBuilderState state) - { - Debug.Assert(state.TemplateType.IsCanonicalSubtype(CanonicalFormKind.Universal)); - - if (state.VTableMethodSignatures == null || state.VTableMethodSignatures.Length == 0) - return; - - int numVtableSlots = GetRuntimeTypeHandle(type).GetNumVtableSlots(); - IntPtr* vtableCells = (IntPtr*)((byte*)GetRuntimeTypeHandle(type).ToIntPtr() + sizeof(EEType)); - Debug.Assert((state.VTableMethodSignatures.Length - state.NumSealedVTableMethodSignatures) <= numVtableSlots); - - // Generic context - RuntimeTypeHandle[] typeArgs = Empty.Array; - RuntimeTypeHandle[] methodArgs = Empty.Array; // No GVMs in vtables - - if (type is DefType) - typeArgs = GetRuntimeTypeHandles(((DefType)type).Instantiation); - else if (type is ArrayType) - typeArgs = GetRuntimeTypeHandles(new Instantiation(new TypeDesc[] { ((ArrayType)type).ElementType })); - - for (int i = 0; i < state.VTableMethodSignatures.Length; i++) - { - int vtableSlotInDynamicType = -1; - if (!state.VTableMethodSignatures[i].IsSealedVTableSlot) - { - vtableSlotInDynamicType = state.VTableSlotsMapping.GetVTableSlotInTargetType((int)state.VTableMethodSignatures[i].VTableSlot); - Debug.Assert(vtableSlotInDynamicType != -1); - } - - IntPtr originalFunctionPointerFromVTable = state.VTableMethodSignatures[i].IsSealedVTableSlot ? - ((IntPtr*)state.HalfBakedSealedVTable)[state.VTableMethodSignatures[i].VTableSlot] : - vtableCells[vtableSlotInDynamicType]; - - IntPtr thunkPtr = CallConverterThunk.MakeThunk( - ThunkKind.StandardToGeneric, - originalFunctionPointerFromVTable, - state.VTableMethodSignatures[i].MethodSignature, - IntPtr.Zero, // No instantiating arg for non-generic instance methods - typeArgs, - Empty.Array); // No GVMs in vtables, no no method args - - if (state.VTableMethodSignatures[i].IsSealedVTableSlot) - { - // Patch the sealed vtable entry to point to the calling converter thunk - Debug.Assert(state.VTableMethodSignatures[i].VTableSlot < state.NumSealedVTableEntries && state.HalfBakedSealedVTable != IntPtr.Zero); - ((IntPtr*)state.HalfBakedSealedVTable)[state.VTableMethodSignatures[i].VTableSlot] = thunkPtr; - } - else - { - // Patch the vtable entry to point to the calling converter thunk - Debug.Assert(vtableSlotInDynamicType < numVtableSlots && vtableCells != null); - vtableCells[vtableSlotInDynamicType] = thunkPtr; - } - } - } - - // - // Returns either the registered type handle or half-baked type handle. This method should be only called - // during final phase of type building. - // - public RuntimeTypeHandle GetRuntimeTypeHandle(TypeDesc type) - { -#if DEBUG - Debug.Assert(_finalTypeBuilding); -#endif - - var rtth = type.RuntimeTypeHandle; - if (!rtth.IsNull()) - return rtth; - - rtth = type.GetTypeBuilderState().HalfBakedRuntimeTypeHandle; - Debug.Assert(!rtth.IsNull()); - return rtth; - } - - public RuntimeTypeHandle[] GetRuntimeTypeHandles(Instantiation types) - { - if (types.Length == 0) - return Array.Empty(); - - RuntimeTypeHandle[] result = new RuntimeTypeHandle[types.Length]; - for (int i = 0; i < types.Length; i++) - result[i] = GetRuntimeTypeHandle(types[i]); - return result; - } - - public static DefType GetBaseTypeUsingRuntimeTypeHandle(TypeDesc type) - { - type.RetrieveRuntimeTypeHandleIfPossible(); - unsafe - { - RuntimeTypeHandle thBaseTypeTemplate = type.RuntimeTypeHandle.ToEETypePtr()->BaseType->ToRuntimeTypeHandle(); - if (thBaseTypeTemplate.IsNull()) - return null; - - return (DefType)type.Context.ResolveRuntimeTypeHandle(thBaseTypeTemplate); - } - } - - public static DefType GetBaseTypeThatIsCorrectForMDArrays(TypeDesc type) - { - if (type.BaseType == type.Context.GetWellKnownType(WellKnownType.Array)) - { - // Use the type from the template, the metadata we have will be inaccurate for multidimensional - // arrays, as we hide the MDArray infrastructure from the metadata. - TypeDesc template = type.ComputeTemplate(false); - return GetBaseTypeUsingRuntimeTypeHandle(template ?? type); - } - - return type.BaseType; - } - - private void FinishInterfaces(TypeDesc type, TypeBuilderState state) - { - DefType[] interfaces = state.RuntimeInterfaces; - if (interfaces != null) - { - for (int i = 0; i < interfaces.Length; i++) - { - state.HalfBakedRuntimeTypeHandle.SetInterface(i, GetRuntimeTypeHandle(interfaces[i])); - } - } - } - - unsafe private void FinishTypeDictionary(TypeDesc type, TypeBuilderState state) - { - if (state.Dictionary != null) - { - // First, update the dictionary slot in the type's vtable to point to the created dictionary when applicable - Debug.Assert(state.HalfBakedDictionary != IntPtr.Zero); - - int dictionarySlot = EETypeCreator.GetDictionarySlotInVTable(type); - if (dictionarySlot >= 0) - { - state.HalfBakedRuntimeTypeHandle.SetDictionary(dictionarySlot, state.HalfBakedDictionary); - } - else - { - // Dictionary shouldn't be in the vtable of the type - Debug.Assert(!type.CanShareNormalGenericCode()); - } - - TypeLoaderLogger.WriteLine("Setting dictionary entries for type " + type.ToString() + " @ " + state.HalfBakedDictionary.LowLevelToString()); - state.Dictionary.Finish(this); - } - } - - unsafe private void FinishMethodDictionary(InstantiatedMethod method) - { - Debug.Assert(method.Dictionary != null); - - TypeLoaderLogger.WriteLine("Setting dictionary entries for method " + method.ToString() + " @ " + method.RuntimeMethodDictionary.LowLevelToString()); - method.Dictionary.Finish(this); - } - - unsafe private void FinishClassConstructor(TypeDesc type, TypeBuilderState state) - { - if (!state.HasStaticConstructor) - return; - - IntPtr canonicalClassConstructorFunctionPointer = IntPtr.Zero; // Pointer to canonical static method to serve as cctor - IntPtr exactClassConstructorFunctionPointer = IntPtr.Zero; // Exact pointer. Takes priority over canonical pointer - - if (state.TemplateType == null) - { - if (!type.HasInstantiation) - { - // Non-Generic ReadyToRun types in their current state already have their static field region setup - // with the class constructor initialized. - return; - } - else - { - // For generic types, we need to do the metadata lookup and then resolve to a function pointer. - MethodDesc staticConstructor = type.GetStaticConstructor(); - IntPtr staticCctor; - IntPtr unused1; - TypeLoaderEnvironment.MethodAddressType addressType; - if (!TypeLoaderEnvironment.TryGetMethodAddressFromMethodDesc(staticConstructor, out staticCctor, out unused1, out addressType)) - { - Environment.FailFast("Unable to find class constructor method address for type:" + type.ToString()); - } - Debug.Assert(unused1 == IntPtr.Zero); - - switch (addressType) - { - case TypeLoaderEnvironment.MethodAddressType.Exact: - // If we have an exact match, put it in the slot directly - // and return as we don't want to make this into a fat function pointer - exactClassConstructorFunctionPointer = staticCctor; - break; - - case TypeLoaderEnvironment.MethodAddressType.Canonical: - case TypeLoaderEnvironment.MethodAddressType.UniversalCanonical: - // If we have a canonical method, setup for generating a fat function pointer - canonicalClassConstructorFunctionPointer = staticCctor; - break; - - default: - Environment.FailFast("Invalid MethodAddressType during ClassConstructor discovery"); - return; - } - } - } - else if (state.ClassConstructorPointer.HasValue) - { - canonicalClassConstructorFunctionPointer = state.ClassConstructorPointer.Value; - } - else - { - // Lookup the non-GC static data for the template type, and use the class constructor context offset to locate the class constructor's - // fat pointer within the non-GC static data. - IntPtr templateTypeStaticData = TypeLoaderEnvironment.Instance.TryGetNonGcStaticFieldData(GetRuntimeTypeHandle(state.TemplateType)); - Debug.Assert(templateTypeStaticData != IntPtr.Zero); - IntPtr* templateTypeClassConstructorSlotPointer = (IntPtr*)((byte*)*((IntPtr*)templateTypeStaticData) + ClassConstructorOffset); - IntPtr templateTypeClassConstructorFatFunctionPointer = templateTypeClassConstructorFatFunctionPointer = *templateTypeClassConstructorSlotPointer; - - // Crack the fat function pointer into the raw class constructor method pointer and the generic type dictionary. - Debug.Assert(FunctionPointerOps.IsGenericMethodPointer(templateTypeClassConstructorFatFunctionPointer)); - GenericMethodDescriptor* templateTypeGenericMethodDescriptor = FunctionPointerOps.ConvertToGenericDescriptor(templateTypeClassConstructorFatFunctionPointer); - Debug.Assert(templateTypeGenericMethodDescriptor != null); - canonicalClassConstructorFunctionPointer = templateTypeGenericMethodDescriptor->MethodFunctionPointer; - } - - IntPtr generatedTypeStaticData = GetRuntimeTypeHandle(type).ToEETypePtr()->DynamicNonGcStaticsData; - IntPtr* generatedTypeClassConstructorSlotPointer = (IntPtr*)((byte*)*((IntPtr*)generatedTypeStaticData) + ClassConstructorOffset); - - if (exactClassConstructorFunctionPointer != IntPtr.Zero) - { - // We have an exact pointer, not a canonical match - // Just set the pointer and return. No need for a fat pointer - *generatedTypeClassConstructorSlotPointer = exactClassConstructorFunctionPointer; - return; - } - - // If we reach here, classConstructorFunctionPointer points at a canonical method, that needs to be converted into - // a fat function pointer so that the calli in the ClassConstructorRunner will work properly - Debug.Assert(canonicalClassConstructorFunctionPointer != IntPtr.Zero); - - // Use the template type's class constructor method pointer and this type's generic type dictionary to generate a new fat pointer, - // and save that fat pointer back to this type's class constructor context offset within the non-GC static data. - IntPtr instantiationArgument = GetRuntimeTypeHandle(type).ToIntPtr(); - IntPtr generatedTypeClassConstructorFatFunctionPointer = FunctionPointerOps.GetGenericMethodFunctionPointer(canonicalClassConstructorFunctionPointer, instantiationArgument); - *generatedTypeClassConstructorSlotPointer = generatedTypeClassConstructorFatFunctionPointer; - } - - private void CopyDictionaryFromTypeToAppropriateSlotInDerivedType(TypeDesc baseType, TypeBuilderState derivedTypeState) - { - var baseTypeState = baseType.GetOrCreateTypeBuilderState(); - - if (baseTypeState.HasDictionaryInVTable) - { - RuntimeTypeHandle baseTypeHandle = GetRuntimeTypeHandle(baseType); - - // If the basetype is currently being created by the TypeBuilder, we need to get its dictionary pointer from the - // TypeBuilder state (at this point, the dictionary has not yet been set on the baseTypeHandle). If - // the basetype is not a dynamic type, or has previously been dynamically allocated in the past, the TypeBuilder - // state will have a null dictionary pointer, in which case we need to read it directly from the basetype's vtable - IntPtr dictionaryEntry = baseTypeState.HalfBakedDictionary; - if (dictionaryEntry == IntPtr.Zero) - dictionaryEntry = baseTypeHandle.GetDictionary(); - Debug.Assert(dictionaryEntry != IntPtr.Zero); - - // Compute the vtable slot for the dictionary entry to set - int dictionarySlot = EETypeCreator.GetDictionarySlotInVTable(baseType); - Debug.Assert(dictionarySlot >= 0); - - derivedTypeState.HalfBakedRuntimeTypeHandle.SetDictionary(dictionarySlot, dictionaryEntry); - TypeLoaderLogger.WriteLine("Setting basetype " + baseType.ToString() + " dictionary on type " + derivedTypeState.TypeBeingBuilt.ToString()); - } - } - - private void FinishBaseTypeAndDictionaries(TypeDesc type, TypeBuilderState state) - { - DefType baseType = GetBaseTypeThatIsCorrectForMDArrays(type); - state.HalfBakedRuntimeTypeHandle.SetBaseType(baseType == null ? default(RuntimeTypeHandle) : GetRuntimeTypeHandle(baseType)); - - if (baseType == null) - return; - - // Update every dictionary in type hierarchy with copy from base type - while (baseType != null) - { - CopyDictionaryFromTypeToAppropriateSlotInDerivedType(baseType, state); - baseType = baseType.BaseType; - } - } - - private void FinishRuntimeType(TypeDesc type) - { - TypeLoaderLogger.WriteLine("Finishing type " + type.ToString() + " ..."); - - var state = type.GetTypeBuilderState(); - - if (type is DefType) - { - DefType typeAsDefType = (DefType)type; - - if (type.HasInstantiation) - { - // Type definitions don't need any further finishing once created by the EETypeCreator - if (type.IsTypeDefinition) - return; - - state.HalfBakedRuntimeTypeHandle.SetGenericDefinition(GetRuntimeTypeHandle(typeAsDefType.GetTypeDefinition())); - Instantiation instantiation = typeAsDefType.Instantiation; - for (int argIndex = 0; argIndex < instantiation.Length; argIndex++) - state.HalfBakedRuntimeTypeHandle.SetGenericArgument(argIndex, GetRuntimeTypeHandle(instantiation[argIndex])); - } - - FinishBaseTypeAndDictionaries(type, state); - - FinishInterfaces(type, state); - - FinishTypeDictionary(type, state); - - FinishClassConstructor(type, state); - - // For types that were allocated from universal canonical templates, patch their vtables with - // pointers to calling convention conversion thunks - if (state.TemplateType != null && state.TemplateType.IsCanonicalSubtype(CanonicalFormKind.Universal)) - FinishVTableCallingConverterThunks(type, state); - - if (RuntimeAugments.IsNullable(state.HalfBakedRuntimeTypeHandle)) - { - Debug.Assert(typeAsDefType.Instantiation.Length == 1); - state.HalfBakedRuntimeTypeHandle.SetNullableType(GetRuntimeTypeHandle(typeAsDefType.Instantiation[0])); - } - } - else if (type is ParameterizedType) - { - if (type is ArrayType) - { - ArrayType typeAsSzArrayType = (ArrayType)type; - - state.HalfBakedRuntimeTypeHandle.SetRelatedParameterType(GetRuntimeTypeHandle(typeAsSzArrayType.ElementType)); - - state.HalfBakedRuntimeTypeHandle.SetComponentSize(state.ComponentSize.Value); - - FinishInterfaces(type, state); - - if (typeAsSzArrayType.IsSzArray && !typeAsSzArrayType.ElementType.IsPointer) - { - FinishTypeDictionary(type, state); - - // For types that were allocated from universal canonical templates, patch their vtables with - // pointers to calling convention conversion thunks - if (state.TemplateType != null && state.TemplateType.IsCanonicalSubtype(CanonicalFormKind.Universal)) - FinishVTableCallingConverterThunks(type, state); - } - } - else if (type is PointerType) - { - state.HalfBakedRuntimeTypeHandle.SetRelatedParameterType(GetRuntimeTypeHandle(((PointerType)type).ParameterType)); - - // Nothing else to do for pointer types - } - else if (type is ByRefType) - { - state.HalfBakedRuntimeTypeHandle.SetRelatedParameterType(GetRuntimeTypeHandle(((ByRefType)type).ParameterType)); - - // We used a pointer type for the template because they're similar enough. Adjust this to be a ByRef. - unsafe { Debug.Assert(state.HalfBakedRuntimeTypeHandle.ToEETypePtr()->ParameterizedTypeShape == ParameterizedTypeShapeConstants.Pointer); } - state.HalfBakedRuntimeTypeHandle.SetParameterizedTypeShape(ParameterizedTypeShapeConstants.ByRef); - } - } - else - { - Debug.Assert(false); - } - } - - private IEnumerable TypesToRegister() - { - for (int i = 0; i < _typesThatNeedTypeHandles.Count; i++) - { - DefType typeAsDefType = _typesThatNeedTypeHandles[i] as DefType; - if (typeAsDefType == null) - continue; - - if (typeAsDefType.HasInstantiation && !typeAsDefType.IsTypeDefinition) - { - yield return new TypeEntryToRegister - { - GenericTypeEntry = new GenericTypeEntry - { - _genericTypeDefinitionHandle = GetRuntimeTypeHandle(typeAsDefType.GetTypeDefinition()), - _genericTypeArgumentHandles = GetRuntimeTypeHandles(typeAsDefType.Instantiation), - _instantiatedTypeHandle = typeAsDefType.GetTypeBuilderState().HalfBakedRuntimeTypeHandle - } - }; - } - else - { - yield return new TypeEntryToRegister - { - MetadataDefinitionType = (MetadataType)typeAsDefType - }; - } - } - } - - private IEnumerable MethodsToRegister() - { - for (int i = 0; i < _methodsThatNeedDictionaries.Count; i++) - { - InstantiatedMethod method = _methodsThatNeedDictionaries[i]; - yield return new GenericMethodEntry - { - _declaringTypeHandle = GetRuntimeTypeHandle(method.OwningType), - _genericMethodArgumentHandles = GetRuntimeTypeHandles(method.Instantiation), - _methodNameAndSignature = method.NameAndSignature, - _methodDictionary = method.RuntimeMethodDictionary - }; - } - } - - private void RegisterGenericTypesAndMethods() - { - int typesToRegisterCount = 0; - for (int i = 0; i < _typesThatNeedTypeHandles.Count; i++) - { - DefType typeAsDefType; - if ((typeAsDefType = _typesThatNeedTypeHandles[i] as DefType) != null) - typesToRegisterCount++; - } - - DynamicGenericsRegistrationData registrationData = new DynamicGenericsRegistrationData - { - TypesToRegisterCount = typesToRegisterCount, - TypesToRegister = (typesToRegisterCount != 0) ? TypesToRegister() : null, - MethodsToRegisterCount = _methodsThatNeedDictionaries.Count, - MethodsToRegister = (_methodsThatNeedDictionaries.Count != 0) ? MethodsToRegister() : null, - }; - TypeLoaderEnvironment.Instance.RegisterDynamicGenericTypesAndMethods(registrationData); - } - - /// - /// Publish generic type / method information to the data buffer read by the debugger. This supports - /// debugging dynamically created types / methods - /// - private void RegisterDebugDataForTypesAndMethods() - { - for (int i = 0; i < _typesThatNeedTypeHandles.Count; i++) - { - DefType typeAsDefType; - if ((typeAsDefType = _typesThatNeedTypeHandles[i] as DefType) != null) - { - SerializedDebugData.RegisterDebugDataForType(this, typeAsDefType, typeAsDefType.GetTypeBuilderState()); - } - } - - for (int i = 0; i < _methodsThatNeedDictionaries.Count; i++) - { - SerializedDebugData.RegisterDebugDataForMethod(this, _methodsThatNeedDictionaries[i]); - } - } - - private void FinishTypeAndMethodBuilding() - { - // Once we start allocating EETypes and dictionaries, the only accepted failure is OOM. - // TODO: Error handling - on retry, restart where we failed last time? The current implementation is leaking on OOM. - -#if DEBUG - _finalTypeBuilding = true; -#endif - - // At this point we know all types that need EETypes. Allocate all EETypes so that we can start building - // their contents. - for (int i = 0; i < _typesThatNeedTypeHandles.Count; i++) - { - AllocateRuntimeType(_typesThatNeedTypeHandles[i]); - } - - for (int i = 0; i < _methodsThatNeedDictionaries.Count; i++) - { - AllocateRuntimeMethodDictionary(_methodsThatNeedDictionaries[i]); - } - - // Do not add more type phases here. Instead, read the required information from the TypeDesc or TypeBuilderState. - - // Fill in content of all EETypes - for (int i = 0; i < _typesThatNeedTypeHandles.Count; i++) - { - FinishRuntimeType(_typesThatNeedTypeHandles[i]); - } - - for (int i = 0; i < _methodsThatNeedDictionaries.Count; i++) - { - FinishMethodDictionary(_methodsThatNeedDictionaries[i]); - } - - RegisterDebugDataForTypesAndMethods(); - - int newArrayTypesCount = 0; - int newPointerTypesCount = 0; - int newByRefTypesCount = 0; - int[] mdArrayNewTypesCount = null; - - for (int i = 0; i < _typesThatNeedTypeHandles.Count; i++) - { - ParameterizedType typeAsParameterizedType = _typesThatNeedTypeHandles[i] as ParameterizedType; - if (typeAsParameterizedType == null) - continue; - - if (typeAsParameterizedType.IsSzArray) - newArrayTypesCount++; - else if (typeAsParameterizedType.IsPointer) - newPointerTypesCount++; - else if (typeAsParameterizedType.IsByRef) - newByRefTypesCount++; - else if (typeAsParameterizedType.IsMdArray) - { - if (mdArrayNewTypesCount == null) - mdArrayNewTypesCount = new int[MDArray.MaxRank + 1]; - mdArrayNewTypesCount[((ArrayType)typeAsParameterizedType).Rank]++; - } - } - // Reserve space in array/pointer cache's so that the actual adding can be fault-free. - var szArrayCache = TypeSystemContext.GetArrayTypesCache(false, -1); - szArrayCache.Reserve(szArrayCache.Count + newArrayTypesCount); - - // - if (mdArrayNewTypesCount != null) - { - for (int i = 0; i < mdArrayNewTypesCount.Length; i++) - { - if (mdArrayNewTypesCount[i] == 0) - continue; - - var mdArrayCache = TypeSystemContext.GetArrayTypesCache(true, i); - mdArrayCache.Reserve(mdArrayCache.Count + mdArrayNewTypesCount[i]); - } - } - - TypeSystemContext.PointerTypesCache.Reserve(TypeSystemContext.PointerTypesCache.Count + newPointerTypesCount); - TypeSystemContext.ByRefTypesCache.Reserve(TypeSystemContext.ByRefTypesCache.Count + newByRefTypesCount); - - // Finally, register all generic types and methods atomically with the runtime - RegisterGenericTypesAndMethods(); - - - for (int i = 0; i < _typesThatNeedTypeHandles.Count; i++) - { - _typesThatNeedTypeHandles[i].SetRuntimeTypeHandleUnsafe(_typesThatNeedTypeHandles[i].GetTypeBuilderState().HalfBakedRuntimeTypeHandle); - - TypeLoaderLogger.WriteLine("Successfully Registered type " + _typesThatNeedTypeHandles[i].ToString() + "."); - } - - // Save all constructed array and pointer types to the types cache - for (int i = 0; i < _typesThatNeedTypeHandles.Count; i++) - { - ParameterizedType typeAsParameterizedType = _typesThatNeedTypeHandles[i] as ParameterizedType; - if (typeAsParameterizedType == null) - continue; - - Debug.Assert(!typeAsParameterizedType.RuntimeTypeHandle.IsNull()); - Debug.Assert(!typeAsParameterizedType.ParameterType.RuntimeTypeHandle.IsNull()); - - if (typeAsParameterizedType.IsMdArray) - TypeSystemContext.GetArrayTypesCache(true, ((ArrayType)typeAsParameterizedType).Rank).AddOrGetExisting(typeAsParameterizedType.RuntimeTypeHandle); - else if (typeAsParameterizedType.IsSzArray) - TypeSystemContext.GetArrayTypesCache(false, -1).AddOrGetExisting(typeAsParameterizedType.RuntimeTypeHandle); - else if (typeAsParameterizedType.IsByRef) - { - unsafe - { - Debug.Assert(typeAsParameterizedType.RuntimeTypeHandle.ToEETypePtr()->IsByRefType); - } - TypeSystemContext.ByRefTypesCache.AddOrGetExisting(typeAsParameterizedType.RuntimeTypeHandle); - } - else - { - Debug.Assert(typeAsParameterizedType is PointerType); - unsafe - { - Debug.Assert(typeAsParameterizedType.RuntimeTypeHandle.ToEETypePtr()->IsPointerType); - } - TypeSystemContext.PointerTypesCache.AddOrGetExisting(typeAsParameterizedType.RuntimeTypeHandle); - } - } - } - - internal void BuildType(TypeDesc type) - { - TypeLoaderLogger.WriteLine("Dynamically allocating new type for " + type.ToString()); - - // Construct a new type along with all the dependencies that are needed to create interface lists, - // generic dictionaries, etc. - - // Start by collecting all dependencies we need to create in order to create this type. - PrepareType(type); - - // Process the pending types - ProcessTypesNeedingPreparation(); - - FinishTypeAndMethodBuilding(); - } - - internal bool TryComputeFieldOffset(DefType declaringType, uint fieldOrdinal, out int fieldOffset) - { - fieldOffset = int.MinValue; - - TypeLoaderLogger.WriteLine("Computing offset of field #" + fieldOrdinal.LowLevelToString() + " on type " + declaringType.ToString()); - - // Get the computed field offset result - LayoutInt layoutFieldOffset = declaringType.GetFieldByNativeLayoutOrdinal(fieldOrdinal).Offset; - if (layoutFieldOffset.IsIndeterminate) - { - fieldOffset = 0; - return false; - } - fieldOffset = layoutFieldOffset.AsInt; - return true; - } - - private void BuildMethod(InstantiatedMethod method) - { - TypeLoaderLogger.WriteLine("Dynamically allocating new method instantiation for " + method.ToString()); - - // Start by collecting all dependencies we need to create in order to create this method. - PrepareMethod(method); - - // Process the pending types - ProcessTypesNeedingPreparation(); - - FinishTypeAndMethodBuilding(); - } - - private static DefType GetExactDeclaringType(DefType srcDefType, DefType dstDefType) - { - while (srcDefType != null) - { - if (srcDefType.HasSameTypeDefinition(dstDefType)) - return srcDefType; - - srcDefType = srcDefType.BaseType; - } - - Debug.Assert(false); - return null; - } - - // - // This method is used by the lazy generic lookup. It resolves the signature of the runtime artifact in the given instantiation context. - // - private unsafe IntPtr BuildGenericLookupTarget(TypeSystemContext typeSystemContext, IntPtr context, IntPtr signature, out IntPtr auxResult) - { - TypeLoaderLogger.WriteLine("BuildGenericLookupTarget for " + context.LowLevelToString() + "/" + signature.LowLevelToString()); - - TypeManagerHandle typeManager; - NativeReader reader; - uint offset; - -#if !CORERT - // If the system module is compiled with as a type manager, all modules are compiled as such - if (ModuleList.Instance.SystemModule.Handle.IsTypeManager) -#endif - { - // The first is a pointer that points to the TypeManager indirection cell. - // The second is the offset into the native layout info blob in that TypeManager, where the native signature is encoded. - IntPtr** lazySignature = (IntPtr**)signature.ToPointer(); - typeManager = new TypeManagerHandle(lazySignature[0][0]); - offset = checked((uint)new IntPtr(lazySignature[1]).ToInt32()); - reader = TypeLoaderEnvironment.Instance.GetNativeLayoutInfoReader(typeManager); - } -#if !CORERT - else - { - IntPtr moduleHandle = RuntimeAugments.GetOSModuleFromPointer(signature); - typeManager = new TypeManagerHandle(moduleHandle); - reader = TypeLoaderEnvironment.Instance.GetNativeLayoutInfoReader(typeManager); - offset = reader.AddressToOffset(signature); - } -#endif - - NativeParser parser = new NativeParser(reader, offset); - - GenericContextKind contextKind = (GenericContextKind)parser.GetUnsigned(); - - NativeFormatModuleInfo moduleInfo = ModuleList.Instance.GetModuleInfoByHandle(typeManager); - - NativeLayoutInfoLoadContext nlilContext = new NativeLayoutInfoLoadContext(); - nlilContext._module = moduleInfo; - nlilContext._typeSystemContext = typeSystemContext; - -#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING - NativeFormatMetadataUnit metadataUnit = null; - - if (moduleInfo.ModuleType == ModuleType.ReadyToRun) - metadataUnit = typeSystemContext.ResolveMetadataUnit(moduleInfo); -#endif - - if ((contextKind & GenericContextKind.FromMethodHiddenArg) != 0) - { - RuntimeTypeHandle declaringTypeHandle; - MethodNameAndSignature nameAndSignature; - RuntimeTypeHandle[] genericMethodArgHandles; - bool success = TypeLoaderEnvironment.Instance.TryGetGenericMethodComponents(context, out declaringTypeHandle, out nameAndSignature, out genericMethodArgHandles); - Debug.Assert(success); - - if (RuntimeAugments.IsGenericType(declaringTypeHandle)) - { - DefType declaringType = (DefType)typeSystemContext.ResolveRuntimeTypeHandle(declaringTypeHandle); - nlilContext._typeArgumentHandles = declaringType.Instantiation; - } - - nlilContext._methodArgumentHandles = typeSystemContext.ResolveRuntimeTypeHandles(genericMethodArgHandles); - } - else - { - TypeDesc typeContext = typeSystemContext.ResolveRuntimeTypeHandle(RuntimeAugments.CreateRuntimeTypeHandle(context)); - - if (typeContext is DefType) - { - nlilContext._typeArgumentHandles = ((DefType)typeContext).Instantiation; - } - else if (typeContext is ArrayType) - { - nlilContext._typeArgumentHandles = new Instantiation(new TypeDesc[] { ((ArrayType)typeContext).ElementType }); - } - else - { - Debug.Assert(false); - } - - if ((contextKind & GenericContextKind.HasDeclaringType) != 0) - { - // No need to deal with arrays - arrays can't have declaring type - - TypeDesc declaringType; - - if (moduleInfo.ModuleType == ModuleType.Eager) - { - declaringType = nlilContext.GetType(ref parser); - } - else - { - Debug.Assert(moduleInfo.ModuleType == ModuleType.ReadyToRun); -#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING - uint typeToken = parser.GetUnsigned(); - declaringType = metadataUnit.GetType(((int)typeToken).AsHandle()); -#else - Environment.FailFast("Ready to Run module type?"); - declaringType = null; -#endif - } - - DefType actualContext = GetExactDeclaringType((DefType)typeContext, (DefType)declaringType); - - nlilContext._typeArgumentHandles = actualContext.Instantiation; - } - } - - if ((contextKind & GenericContextKind.NeedsUSGContext) != 0) - { - IntPtr genericDictionary; - auxResult = IntPtr.Zero; - - // There is a cache in place so that this function doesn't get called much, but we still need a registration store, - // so we don't leak allocated contexts - if (TypeLoaderEnvironment.Instance.TryLookupConstructedLazyDictionaryForContext(context, signature, out genericDictionary)) - { - return genericDictionary; - } - - GenericTypeDictionary ucgDict; - - if (moduleInfo.ModuleType == ModuleType.Eager) - { - ucgDict = new GenericTypeDictionary(GenericDictionaryCell.BuildDictionary(this, nlilContext, parser)); - } - else - { -#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING - Debug.Assert(moduleInfo.ModuleType == ModuleType.ReadyToRun); - FixupCellMetadataResolver metadataResolver = new FixupCellMetadataResolver(metadataUnit, nlilContext); - ucgDict = new GenericTypeDictionary(GenericDictionaryCell.BuildDictionaryFromMetadataTokensAndContext(this, parser, metadataUnit, metadataResolver)); -#else - Environment.FailFast("Ready to Run module type?"); - ucgDict = null; -#endif - } - genericDictionary = ucgDict.Allocate(); - - // Process the pending types - ProcessTypesNeedingPreparation(); - - FinishTypeAndMethodBuilding(); - - ucgDict.Finish(this); - - TypeLoaderEnvironment.Instance.RegisterConstructedLazyDictionaryForContext(context, signature, genericDictionary); - return genericDictionary; - } - else - { - GenericDictionaryCell cell; - - if (moduleInfo.ModuleType == ModuleType.Eager) - { - cell = GenericDictionaryCell.ParseAndCreateCell( - nlilContext, - ref parser); - } - else - { - Debug.Assert(moduleInfo.ModuleType == ModuleType.ReadyToRun); -#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING - MetadataFixupKind fixupKind = (MetadataFixupKind)parser.GetUInt8(); - Internal.Metadata.NativeFormat.Handle token = parser.GetUnsigned().AsHandle(); - Internal.Metadata.NativeFormat.Handle token2 = default(Internal.Metadata.NativeFormat.Handle); - - switch (fixupKind) - { - case MetadataFixupKind.GenericConstrainedMethod: - case MetadataFixupKind.NonGenericConstrainedMethod: - case MetadataFixupKind.NonGenericDirectConstrainedMethod: - token2 = parser.GetUnsigned().AsHandle(); - break; - } - - FixupCellMetadataResolver resolver = new FixupCellMetadataResolver(metadataUnit, nlilContext); - cell = GenericDictionaryCell.CreateCellFromFixupKindAndToken(fixupKind, resolver, token, token2); -#else - Environment.FailFast("Ready to Run module type?"); - cell = null; -#endif - } - - cell.Prepare(this); - - // Process the pending types - ProcessTypesNeedingPreparation(); - - FinishTypeAndMethodBuilding(); - - IntPtr dictionaryCell = cell.CreateLazyLookupCell(this, out auxResult); - - return dictionaryCell; - } - } - - public static bool TryBuildGenericType(RuntimeTypeHandle genericTypeDefinitionHandle, RuntimeTypeHandle[] genericTypeArgumentHandles, out RuntimeTypeHandle runtimeTypeHandle) - { - Debug.Assert(!genericTypeDefinitionHandle.IsNull() && genericTypeArgumentHandles != null && genericTypeArgumentHandles.Length > 0); - - try - { - TypeSystemContext context = TypeSystemContextFactory.Create(); - - DefType genericDef = (DefType)context.ResolveRuntimeTypeHandle(genericTypeDefinitionHandle); - Instantiation genericArgs = context.ResolveRuntimeTypeHandles(genericTypeArgumentHandles); - DefType typeBeingLoaded = context.ResolveGenericInstantiation(genericDef, genericArgs); - - new TypeBuilder().BuildType(typeBeingLoaded); - - runtimeTypeHandle = typeBeingLoaded.RuntimeTypeHandle; - Debug.Assert(!runtimeTypeHandle.IsNull()); - - // Recycle the context only if we succesfully built the type. The state may be partially initialized otherwise. - TypeSystemContextFactory.Recycle(context); - - return true; - } - catch (MissingTemplateException) - { - runtimeTypeHandle = default(RuntimeTypeHandle); - return false; - } - } - - public static bool TryBuildArrayType(RuntimeTypeHandle elementTypeHandle, bool isMdArray, int rank, out RuntimeTypeHandle arrayTypeHandle) - { - try - { - TypeSystemContext context = TypeSystemContextFactory.Create(); - - TypeDesc elementType = context.ResolveRuntimeTypeHandle(elementTypeHandle); - ArrayType arrayType = (ArrayType)context.GetArrayType(elementType, !isMdArray ? -1 : rank); - - new TypeBuilder().BuildType(arrayType); - - arrayTypeHandle = arrayType.RuntimeTypeHandle; - Debug.Assert(!arrayTypeHandle.IsNull()); - - // Recycle the context only if we succesfully built the type. The state may be partially initialized otherwise. - TypeSystemContextFactory.Recycle(context); - - return true; - } - catch (MissingTemplateException) - { - arrayTypeHandle = default(RuntimeTypeHandle); - return false; - } - } - - public static bool TryBuildPointerType(RuntimeTypeHandle pointeeTypeHandle, out RuntimeTypeHandle pointerTypeHandle) - { - if (!TypeSystemContext.PointerTypesCache.TryGetValue(pointeeTypeHandle, out pointerTypeHandle)) - { - TypeSystemContext context = TypeSystemContextFactory.Create(); - TypeDesc pointerType = context.GetPointerType(context.ResolveRuntimeTypeHandle(pointeeTypeHandle)); - pointerTypeHandle = EETypeCreator.CreatePointerEEType((uint)pointerType.GetHashCode(), pointeeTypeHandle, pointerType); - unsafe - { - Debug.Assert(pointerTypeHandle.ToEETypePtr()->IsPointerType); - } - TypeSystemContext.PointerTypesCache.AddOrGetExisting(pointerTypeHandle); - - // Recycle the context only if we succesfully built the type. The state may be partially initialized otherwise. - TypeSystemContextFactory.Recycle(context); - } - - return true; - } - - public static bool TryBuildByRefType(RuntimeTypeHandle pointeeTypeHandle, out RuntimeTypeHandle byRefTypeHandle) - { - if (!TypeSystemContext.ByRefTypesCache.TryGetValue(pointeeTypeHandle, out byRefTypeHandle)) - { - TypeSystemContext context = TypeSystemContextFactory.Create(); - TypeDesc byRefType = context.GetByRefType(context.ResolveRuntimeTypeHandle(pointeeTypeHandle)); - byRefTypeHandle = EETypeCreator.CreateByRefEEType((uint)byRefType.GetHashCode(), pointeeTypeHandle, byRefType); - unsafe - { - Debug.Assert(byRefTypeHandle.ToEETypePtr()->IsByRefType); - } - TypeSystemContext.ByRefTypesCache.AddOrGetExisting(byRefTypeHandle); - - // Recycle the context only if we succesfully built the type. The state may be partially initialized otherwise. - TypeSystemContextFactory.Recycle(context); - } - - return true; - } - - public static bool TryBuildGenericMethod(RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle[] genericMethodArgHandles, MethodNameAndSignature methodNameAndSignature, out IntPtr methodDictionary) - { - TypeSystemContext context = TypeSystemContextFactory.Create(); - - DefType declaringType = (DefType)context.ResolveRuntimeTypeHandle(declaringTypeHandle); - InstantiatedMethod methodBeingLoaded = (InstantiatedMethod)context.ResolveGenericMethodInstantiation(false, declaringType, methodNameAndSignature, context.ResolveRuntimeTypeHandles(genericMethodArgHandles), IntPtr.Zero, false); - - bool success = TryBuildGenericMethod(methodBeingLoaded, out methodDictionary); - - // Recycle the context only if we succesfully built the method. The state may be partially initialized otherwise. - if (success) - TypeSystemContextFactory.Recycle(context); - - return success; - } - - internal static bool TryBuildGenericMethod(InstantiatedMethod methodBeingLoaded, out IntPtr methodDictionary) - { - try - { - new TypeBuilder().BuildMethod(methodBeingLoaded); - - methodDictionary = methodBeingLoaded.RuntimeMethodDictionary; - Debug.Assert(methodDictionary != IntPtr.Zero); - - return true; - } - catch (MissingTemplateException) - { - methodDictionary = IntPtr.Zero; - return false; - } - } - - private void ResolveSingleCell_Worker(GenericDictionaryCell cell, out IntPtr fixupResolution) - { - cell.Prepare(this); - - // Process the pending types - ProcessTypesNeedingPreparation(); - FinishTypeAndMethodBuilding(); - - // At this stage the pointer we need is accessible via a call to Create on the prepared cell - fixupResolution = cell.Create(this); - } - - private void ResolveMultipleCells_Worker(GenericDictionaryCell[] cells, out IntPtr[] fixups) - { - foreach (var cell in cells) - { - cell.Prepare(this); - } - - // Process the pending types - ProcessTypesNeedingPreparation(); - FinishTypeAndMethodBuilding(); - - // At this stage the pointer we need is accessible via a call to Create on the prepared cell - fixups = new IntPtr[cells.Length]; - for (int i = 0; i < fixups.Length; i++) - fixups[i] = cells[i].Create(this); - } - -#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING - private void ResolveSingleMetadataFixup(NativeFormatMetadataUnit module, Handle token, MetadataFixupKind fixupKind, out IntPtr fixupResolution) - { - FixupCellMetadataResolver metadata = new FixupCellMetadataResolver(module); - - // Allocate a cell object to represent the fixup, and prepare it - GenericDictionaryCell cell = GenericDictionaryCell.CreateCellFromFixupKindAndToken(fixupKind, metadata, token, default(Handle)); - ResolveSingleCell_Worker(cell, out fixupResolution); - } - - public static bool TryResolveSingleMetadataFixup(NativeFormatModuleInfo module, int metadataToken, MetadataFixupKind fixupKind, out IntPtr fixupResolution) - { - TypeSystemContext context = TypeSystemContextFactory.Create(); - - NativeFormatMetadataUnit metadataUnit = context.ResolveMetadataUnit(module); - new TypeBuilder().ResolveSingleMetadataFixup(metadataUnit, metadataToken.AsHandle(), fixupKind, out fixupResolution); - - TypeSystemContextFactory.Recycle(context); - - return true; - } - - public static void ResolveSingleTypeDefinition(QTypeDefinition qTypeDefinition, out IntPtr typeHandle) - { - TypeSystemContext context = TypeSystemContextFactory.Create(); - - TypeDesc type = context.GetTypeDescFromQHandle(qTypeDefinition); - GenericDictionaryCell cell = GenericDictionaryCell.CreateTypeHandleCell(type); - - new TypeBuilder().ResolveSingleCell_Worker(cell, out typeHandle); - - TypeSystemContextFactory.Recycle(context); - } -#endif - - internal static void ResolveSingleCell(GenericDictionaryCell cell, out IntPtr fixupResolution) - { - new TypeBuilder().ResolveSingleCell_Worker(cell, out fixupResolution); - } - - public static void ResolveMultipleCells(GenericDictionaryCell [] cells, out IntPtr[] fixups) - { - new TypeBuilder().ResolveMultipleCells_Worker(cells, out fixups); - } - - public static IntPtr BuildGenericLookupTarget(IntPtr typeContext, IntPtr signature, out IntPtr auxResult) - { - try - { - TypeSystemContext context = TypeSystemContextFactory.Create(); - - IntPtr ret = new TypeBuilder().BuildGenericLookupTarget(context, typeContext, signature, out auxResult); - - TypeSystemContextFactory.Recycle(context); - - return ret; - } - catch (MissingTemplateException e) - { - // This should not ever happen. The static compiler should ensure that the templates are always - // available for types and methods referenced by lazy dictionary lookups - Environment.FailFast("MissingTemplateException thrown during lazy generic lookup", e); - - auxResult = IntPtr.Zero; - return IntPtr.Zero; - } - } - - public static bool TryGetFieldOffset(RuntimeTypeHandle declaringTypeHandle, uint fieldOrdinal, out int fieldOffset) - { - try - { - TypeSystemContext context = TypeSystemContextFactory.Create(); - - DefType declaringType = (DefType)context.ResolveRuntimeTypeHandle(declaringTypeHandle); - Debug.Assert(declaringType.HasInstantiation); - - bool success = new TypeBuilder().TryComputeFieldOffset(declaringType, fieldOrdinal, out fieldOffset); - - TypeSystemContextFactory.Recycle(context); - - return success; - } - catch (MissingTemplateException) - { - fieldOffset = int.MinValue; - return false; - } - } - - internal static bool TryGetDelegateInvokeMethodSignature(RuntimeTypeHandle delegateTypeHandle, out RuntimeSignature signature) - { - signature = default(RuntimeSignature); - bool success = false; - - TypeSystemContext context = TypeSystemContextFactory.Create(); - - DefType delegateType = (DefType)context.ResolveRuntimeTypeHandle(delegateTypeHandle); - Debug.Assert(delegateType.HasInstantiation); - - NativeLayoutInfoLoadContext loadContext; - NativeLayoutInfo universalLayoutInfo; - NativeParser parser = delegateType.GetOrCreateTypeBuilderState().GetParserForUniversalNativeLayoutInfo(out loadContext, out universalLayoutInfo); - if (!parser.IsNull) - { - NativeParser sigParser = parser.GetParserForBagElementKind(BagElementKind.DelegateInvokeSignature); - if (!sigParser.IsNull) - { - signature = RuntimeSignature.CreateFromNativeLayoutSignature(universalLayoutInfo.Module.Handle, sigParser.Offset); - success = true; - } - } - - TypeSystemContextFactory.Recycle(context); - - return success; - } - } -} diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs.REMOVED.git-id b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs.REMOVED.git-id new file mode 100644 index 0000000000..bfbd678e46 --- /dev/null +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs.REMOVED.git-id @@ -0,0 +1 @@ +315e0f9d274a7b10441556ff7c0b929a52724831 \ No newline at end of file diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilderState.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilderState.cs index cd0142b0fd..709a17f069 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilderState.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilderState.cs @@ -115,6 +115,17 @@ namespace Internal.Runtime.TypeLoader { if (!_templateComputed) { + // Multidimensional arrays and szarrays of pointers don't implement generic interfaces and are special cases. They use + // typeof(object[,]) as their template. + if (TypeBeingBuilt.IsMdArray || (TypeBeingBuilt.IsSzArray && ((ArrayType)TypeBeingBuilt).ElementType.IsPointer)) + { + _templateType = TypeBeingBuilt.Context.ResolveRuntimeTypeHandle(typeof(object[,]).TypeHandle); + _templateTypeLoaderNativeLayout = false; + _nativeLayoutComputed = _nativeLayoutTokenComputed = _templateComputed = true; + + return _templateType; + } + // Locate the template type and native layout info _templateType = TypeBeingBuilt.Context.TemplateLookup.TryGetTypeTemplate(TypeBeingBuilt, ref _nativeLayoutInfo); Debug.Assert(_templateType == null || !_templateType.RuntimeTypeHandle.IsNull()); @@ -372,25 +383,25 @@ namespace Internal.Runtime.TypeLoader } else { - // This should only happen for non-universal templates - Debug.Assert(TypeBeingBuilt.IsTemplateCanonical()); - - // Canonical template type loader case unsafe { - return templateType.GetRuntimeTypeHandle().ToEETypePtr()->NumVtableSlots; + if (TypeBeingBuilt.IsMdArray || (TypeBeingBuilt.IsSzArray && ((ArrayType)TypeBeingBuilt).ElementType.IsPointer)) + { + // MDArray types and pointer arrays have the same vtable as the System.Array type they "derive" from. + // They do not implement the generic interfaces that make this interesting for normal arrays. + return TypeBeingBuilt.BaseType.GetRuntimeTypeHandle().ToEETypePtr()->NumVtableSlots; + } + else + { + // This should only happen for non-universal templates + Debug.Assert(TypeBeingBuilt.IsTemplateCanonical()); + + // Canonical template type loader case + return templateType.GetRuntimeTypeHandle().ToEETypePtr()->NumVtableSlots; + } } } } - else if (TypeBeingBuilt.IsMdArray || (TypeBeingBuilt.IsSzArray && ((ArrayType)TypeBeingBuilt).ElementType.IsPointer)) - { - // MDArray types and pointer arrays have the same vtable as the System.Array type they "derive" from. - // They do not implement the generic interfaces that make this interesting for normal arrays. - unsafe - { - return TypeBeingBuilt.BaseType.GetRuntimeTypeHandle().ToEETypePtr()->NumVtableSlots; - } - } else { // Metadata based type loading. @@ -996,7 +1007,12 @@ namespace Internal.Runtime.TypeLoader { if (arrayType.ElementType is DefType) { - return checked((ushort)((DefType)arrayType.ElementType).InstanceFieldSize.AsInt); + uint size = (uint)((DefType)arrayType.ElementType).InstanceFieldSize.AsInt; + + if (size > ArrayTypesConstants.MaxSizeForValueClassInArray && arrayType.ElementType.IsValueType) + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadValueClassTooLarge, arrayType.ElementType); + + return checked((ushort)size); } else { @@ -1010,6 +1026,45 @@ namespace Internal.Runtime.TypeLoader } } + public uint NullableValueOffset + { + get + { + if (!TypeBeingBuilt.IsNullable) + return 0; + + if (TypeBeingBuilt.IsTemplateCanonical()) + { + // Pull the GC Desc from the canonical instantiation + TypeDesc templateType = TypeBeingBuilt.ComputeTemplate(); + bool success = templateType.RetrieveRuntimeTypeHandleIfPossible(); + Debug.Assert(success); + unsafe + { + return templateType.RuntimeTypeHandle.ToEETypePtr()->NullableValueOffset; + } + } + else + { + int fieldCount = 0; + uint nullableValueOffset = 0; + + foreach (FieldDesc f in GetFieldsForGCLayout()) + { + if (fieldCount == 1) + { + nullableValueOffset = checked((uint)f.Offset.AsInt); + } + fieldCount++; + } + + // Nullable only has two fields. HasValue and Value + Debug.Assert(fieldCount == 2); + return nullableValueOffset; + } + } + } + public bool IsHFA { get diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.FieldAccess.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.FieldAccess.cs index 2f0884e85d..2cd5b9156a 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.FieldAccess.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.FieldAccess.cs @@ -48,10 +48,18 @@ namespace Internal.Runtime.TypeLoader public int Offset; } + [StructLayout(LayoutKind.Sequential)] + struct ThreadStaticFieldOffsets + { + public uint StartingOffsetInTlsBlock; // Offset in the TLS block containing the thread static fields of a given type + public uint FieldOffset; // Offset of a thread static field from the start of its containing type's TLS fields block + // (in other words, the address of a field is 'TLS block + StartingOffsetInTlsBlock + FieldOffset') + } + public sealed partial class TypeLoaderEnvironment { /// - /// Try to look up field acccess info for given canon in metadata blobs for all available modules. + /// Try to look up field access info for given canon in metadata blobs for all available modules. /// /// Metadata reader for the declaring type /// Declaring type for the method @@ -110,7 +118,7 @@ namespace Internal.Runtime.TypeLoader /// Canonical form to use /// Output - metadata information for field accessor construction /// true when found, false otherwise - private static bool TryGetFieldAccessMetadataFromFieldAccessMap( + private unsafe static bool TryGetFieldAccessMetadataFromFieldAccessMap( MetadataReader metadataReader, RuntimeTypeHandle declaringTypeHandle, FieldHandle fieldHandle, @@ -184,13 +192,13 @@ namespace Internal.Runtime.TypeLoader continue; } - int cookieOrOffsetOrOrdinal = (int)entryParser.GetUnsigned(); - int fieldOffset; + int fieldOffset = -1; + int threadStaticsStartOffset = -1; IntPtr fieldAddressCookie = IntPtr.Zero; if (canonFormKind == CanonicalFormKind.Universal) { - if (!TypeLoaderEnvironment.Instance.TryGetFieldOffset(declaringTypeHandle, (uint)cookieOrOffsetOrOrdinal, out fieldOffset)) + if (!TypeLoaderEnvironment.Instance.TryGetFieldOffset(declaringTypeHandle, entryParser.GetUnsigned() /* field ordinal */, out fieldOffset)) { Debug.Assert(false); return false; @@ -198,19 +206,46 @@ namespace Internal.Runtime.TypeLoader } else { - if ((entryFlags & FieldTableFlags.FieldOffsetEncodedDirectly) != 0) - fieldOffset = cookieOrOffsetOrOrdinal; + if ((entryFlags & FieldTableFlags.StorageClass) == FieldTableFlags.ThreadStatic) + { + if ((entryFlags & FieldTableFlags.FieldOffsetEncodedDirectly) != 0) + { + if ((entryFlags & FieldTableFlags.IsAnyCanonicalEntry) == 0) + { + int rvaToThreadStaticFieldOffsets = (int)externalReferences.GetRvaFromIndex(entryParser.GetUnsigned()); + fieldAddressCookie = RvaToNonGenericStaticFieldAddress(mappingTableModule.Handle, rvaToThreadStaticFieldOffsets); + threadStaticsStartOffset = *(int*)fieldAddressCookie.ToPointer(); + } + fieldOffset = (int)entryParser.GetUnsigned(); + } + else + { + int rvaToThreadStaticFieldOffsets = (int)externalReferences.GetRvaFromIndex(entryParser.GetUnsigned()); + fieldAddressCookie = RvaToNonGenericStaticFieldAddress(mappingTableModule.Handle, rvaToThreadStaticFieldOffsets); + ThreadStaticFieldOffsets* pThreadStaticFieldOffsets = (ThreadStaticFieldOffsets*)fieldAddressCookie.ToPointer(); + + threadStaticsStartOffset = (int)pThreadStaticFieldOffsets->StartingOffsetInTlsBlock; + fieldOffset = (int)pThreadStaticFieldOffsets->FieldOffset; + } + } else { -#if CORERT - fieldOffset = 0; - fieldAddressCookie = externalReferences.GetFieldAddressFromIndex((uint)cookieOrOffsetOrOrdinal); - - if((entryFlags & FieldTableFlags.IsGcSection) != 0) + if ((entryFlags & FieldTableFlags.FieldOffsetEncodedDirectly) != 0) + { fieldOffset = (int)entryParser.GetUnsigned(); + } + else + { +#if PROJECTN + fieldOffset = (int)externalReferences.GetRvaFromIndex(entryParser.GetUnsigned()); #else - fieldOffset = (int)externalReferences.GetRvaFromIndex((uint)cookieOrOffsetOrOrdinal); + fieldOffset = 0; + fieldAddressCookie = externalReferences.GetFieldAddressFromIndex(entryParser.GetUnsigned()); + + if((entryFlags & FieldTableFlags.IsGcSection) != 0) + fieldOffset = (int)entryParser.GetUnsigned(); #endif + } } } @@ -218,26 +253,13 @@ namespace Internal.Runtime.TypeLoader { // TODO: CoreRT support - if (canonFormKind != CanonicalFormKind.Universal) - { - fieldAddressCookie = RvaToNonGenericStaticFieldAddress(mappingTableModule.Handle, fieldOffset); - } - if (!entryDeclaringTypeHandle.Equals(declaringTypeHandle)) { - // In this case we didn't find an exact match, but we did find a canonically equivalent match - // We might be in the dynamic type case, or the canonically equivalent, but not the same case. - - if (!RuntimeAugments.IsDynamicType(declaringTypeHandle)) - { - int offsetToCreateCookieFor = fieldOffset; - // We're working with a statically generated type, but we didn't find an exact match in the tables - if (canonFormKind != CanonicalFormKind.Universal) - offsetToCreateCookieFor = checked((int)TypeLoaderEnvironment.GetThreadStaticTypeOffsetFromThreadStaticCookie(fieldAddressCookie)); - - fieldAddressCookie = TypeLoaderEnvironment.Instance.TryGetThreadStaticFieldOffsetCookieForTypeAndFieldOffset(declaringTypeHandle, checked((uint)offsetToCreateCookieFor)); - } + if (!TypeLoaderEnvironment.Instance.TryGetThreadStaticStartOffset(declaringTypeHandle, out threadStaticsStartOffset)) + return false; } + + fieldAddressCookie = new IntPtr(threadStaticsStartOffset); } fieldAccessMetadata.MappingTableModule = mappingTableModule.Handle; diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.GVMResolution.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.GVMResolution.cs index d3dcb2a934..268d1cc326 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.GVMResolution.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.GVMResolution.cs @@ -21,12 +21,50 @@ namespace Internal.Runtime.TypeLoader { public sealed partial class TypeLoaderEnvironment { +#if GVM_RESOLUTION_TRACE + private string GetTypeNameDebug(RuntimeTypeHandle rtth) + { + string result; + + if (RuntimeAugments.IsGenericType(rtth)) + { + RuntimeTypeHandle[] typeArgumentsHandles; + RuntimeTypeHandle openTypeDef = RuntimeAugments.GetGenericInstantiation(rtth, out typeArgumentsHandles); ; + result = GetTypeNameDebug(openTypeDef) + "<"; + for (int i = 0; i < typeArgumentsHandles.Length; i++) + result += (i == 0 ? "" : ",") + GetTypeNameDebug(typeArgumentsHandles[i]); + return result + ">"; + } + else + { + System.Reflection.Runtime.General.QTypeDefinition qTypeDefinition; + + // Check if we have metadata. + if (Instance.TryGetMetadataForNamedType(rtth, out qTypeDefinition)) + return qTypeDefinition.NativeFormatHandle.GetFullName(qTypeDefinition.NativeFormatReader); + } + + result = "EEType:0x"; + ulong num = (ulong)RuntimeAugments.GetPointerFromTypeHandle(rtth); + + int shift = IntPtr.Size * 8; + const string HexDigits = "0123456789ABCDEF"; + while (shift > 0) + { + shift -= 4; + int digit = (int)((num >> shift) & 0xF); + result += HexDigits[digit]; + } + return result; + } +#endif + public bool TryGetGenericVirtualTargetForTypeAndSlot(RuntimeTypeHandle targetHandle, ref RuntimeTypeHandle declaringType, RuntimeTypeHandle[] genericArguments, ref string methodName, ref RuntimeSignature methodSignature, out IntPtr methodPointer, out IntPtr dictionaryPointer, out bool slotUpdated) { MethodNameAndSignature methodNameAndSignature = new MethodNameAndSignature(methodName, methodSignature); -#if REFLECTION_EXECUTION_TRACE - ReflectionExecutionLogger.WriteLine("GVM resolution starting for " + GetTypeNameDebug(declaringType) + "." + methodNameAndSignature.Name + "(...) on a target of type " + GetTypeNameDebug(targetHandle) + " ..."); +#if GVM_RESOLUTION_TRACE + Debug.WriteLine("GVM resolution starting for " + GetTypeNameDebug(declaringType) + "." + methodNameAndSignature.Name + "(...) on a target of type " + GetTypeNameDebug(targetHandle) + " ..."); #endif if (RuntimeAugments.IsInterface(declaringType)) @@ -83,14 +121,19 @@ namespace Internal.Runtime.TypeLoader { uint numTargetImplementations = entryParser.GetUnsigned(); +#if GVM_RESOLUTION_TRACE + Debug.WriteLine(" :: Declaring type = " + GetTypeNameDebug(declaringType)); + Debug.WriteLine(" :: Target type = " + GetTypeNameDebug(openTargetTypeHandle)); +#endif + for (uint j = 0; j < numTargetImplementations; j++) { uint nameAndSigToken = extRefs.GetExternalNativeLayoutOffset(entryParser.GetUnsigned()); MethodNameAndSignature targetMethodNameAndSignature = GetMethodNameAndSignatureFromNativeReader(nativeLayoutReader, module.Handle, nameAndSigToken); RuntimeTypeHandle targetTypeHandle = extRefs.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); -#if REFLECTION_EXECUTION_TRACE - ReflectionExecutionLogger.WriteLine(" Searching for GVM implementation on targe type = " + GetTypeNameDebug(targetTypeHandle)); +#if GVM_RESOLUTION_TRACE + Debug.WriteLine(" Searching for GVM implementation on targe type = " + GetTypeNameDebug(targetTypeHandle)); #endif uint numIfaceImpls = entryParser.GetUnsigned(); @@ -99,6 +142,10 @@ namespace Internal.Runtime.TypeLoader { RuntimeTypeHandle implementingTypeHandle = extRefs.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); +#if GVM_RESOLUTION_TRACE + Debug.WriteLine(" -> Current implementing type = " + GetTypeNameDebug(implementingTypeHandle)); +#endif + uint numIfaceSigs = entryParser.GetUnsigned(); if (!openTargetTypeHandle.Equals(implementingTypeHandle)) @@ -118,13 +165,16 @@ namespace Internal.Runtime.TypeLoader if (TypeLoaderEnvironment.Instance.GetTypeFromSignatureAndContext(ref ifaceSigParser, module.Handle, targetTypeInstantiation, null, out currentIfaceTypeHandle)) { +#if GVM_RESOLUTION_TRACE + Debug.WriteLine(" -> Current interface on type = " + GetTypeNameDebug(currentIfaceTypeHandle)); +#endif Debug.Assert(!currentIfaceTypeHandle.IsNull()); if ((!variantDispatch && declaringType.Equals(currentIfaceTypeHandle)) || (variantDispatch && RuntimeAugments.IsAssignableFrom(declaringType, currentIfaceTypeHandle))) { -#if REFLECTION_EXECUTION_TRACE - ReflectionExecutionLogger.WriteLine(" " + (declaringType.Equals(currentIfaceTypeHandle) ? "Exact" : "Variant-compatible") + " match found on this target type!"); +#if GVM_RESOLUTION_TRACE + Debug.WriteLine(" " + (declaringType.Equals(currentIfaceTypeHandle) ? "Exact" : "Variant-compatible") + " match found on this target type!"); #endif // We found the GVM slot target for the input interface GVM call, so let's update the interface GVM slot and return success to the caller declaringType = targetTypeHandle; @@ -197,8 +247,8 @@ namespace Internal.Runtime.TypeLoader RuntimeTypeHandle[] targetTypeInstantiation; openTargetTypeHandle = GetOpenTypeDefinition(targetTypeHandle, out targetTypeInstantiation); -#if REFLECTION_EXECUTION_TRACE - ReflectionExecutionLogger.WriteLine("INTERFACE GVM call = " + GetTypeNameDebug(declaringType) + "." + methodNameAndSignature.Name); +#if GVM_RESOLUTION_TRACE + Debug.WriteLine("INTERFACE GVM call = " + GetTypeNameDebug(declaringType) + "." + methodNameAndSignature.Name); #endif foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules(RuntimeAugments.GetModuleFromTypeHandle(openTargetTypeHandle))) @@ -427,8 +477,8 @@ namespace Internal.Runtime.TypeLoader int hashCode = openCallingTypeHandle.GetHashCode(); hashCode = ((hashCode << 13) ^ hashCode) ^ openTargetTypeHandle.GetHashCode(); -#if REFLECTION_EXECUTION_TRACE - ReflectionExecutionLogger.WriteLine("GVM Target Resolution = " + GetTypeNameDebug(targetTypeHandle) + "." + callingMethodNameAndSignature.Name); +#if GVM_RESOLUTION_TRACE + Debug.WriteLine("GVM Target Resolution = " + GetTypeNameDebug(targetTypeHandle) + "." + callingMethodNameAndSignature.Name); #endif foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules(RuntimeAugments.GetModuleFromTypeHandle(openTargetTypeHandle))) diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs index 4b5e4ca47e..3c6ecf1073 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs @@ -256,9 +256,17 @@ namespace Internal.Runtime.TypeLoader RuntimeFieldHandleInfo* fieldData = *(RuntimeFieldHandleInfo**)&runtimeFieldHandle; RuntimeSignature signature; -#if !CORERT +#if PROJECTN // If the system module is compiled with as a type manager, all modules are compiled as such - if (ModuleList.Instance.SystemModule.Handle.IsTypeManager) + if (!ModuleList.Instance.SystemModule.Handle.IsTypeManager) + { + IntPtr moduleHandle = RuntimeAugments.GetOSModuleFromPointer(fieldData->NativeLayoutInfoSignature); + + signature = RuntimeSignature.CreateFromNativeLayoutSignature( + new TypeManagerHandle(moduleHandle), + GetNativeLayoutInfoReader(new TypeManagerHandle(moduleHandle)).AddressToOffset(fieldData->NativeLayoutInfoSignature)); + } + else #endif { // The native layout info signature is a pair. @@ -270,16 +278,6 @@ namespace Internal.Runtime.TypeLoader new TypeManagerHandle(*(IntPtr*)nativeLayoutInfoSignatureData[0]), (uint)nativeLayoutInfoSignatureData[1].ToInt32()); } -#if !CORERT - else - { - IntPtr moduleHandle = RuntimeAugments.GetOSModuleFromPointer(fieldData->NativeLayoutInfoSignature); - - signature = RuntimeSignature.CreateFromNativeLayoutSignature( - new TypeManagerHandle(moduleHandle), - GetNativeLayoutInfoReader(new TypeManagerHandle(moduleHandle)).AddressToOffset(fieldData->NativeLayoutInfoSignature)); - } -#endif RuntimeSignature remainingSignature; if (!GetTypeFromSignatureAndContext(signature, null, null, out declaringTypeHandle, out remainingSignature)) @@ -425,9 +423,17 @@ namespace Internal.Runtime.TypeLoader RuntimeMethodHandleInfo* methodData = *(RuntimeMethodHandleInfo**)&runtimeMethodHandle; RuntimeSignature signature; -#if !CORERT +#if PROJECTN // If the system module is compiled with as a type manager, all modules are compiled as such - if (ModuleList.Instance.SystemModule.Handle.IsTypeManager) + if (!ModuleList.Instance.SystemModule.Handle.IsTypeManager) + { + IntPtr moduleHandle = RuntimeAugments.GetOSModuleFromPointer(methodData->NativeLayoutInfoSignature); + + signature = RuntimeSignature.CreateFromNativeLayoutSignature( + new TypeManagerHandle(moduleHandle), + GetNativeLayoutInfoReader(new TypeManagerHandle(moduleHandle)).AddressToOffset(methodData->NativeLayoutInfoSignature)); + } + else #endif { // The native layout info signature is a pair. @@ -439,16 +445,6 @@ namespace Internal.Runtime.TypeLoader new TypeManagerHandle(*(IntPtr*)nativeLayoutInfoSignatureData[0]), (uint)nativeLayoutInfoSignatureData[1].ToInt32()); } -#if !CORERT - else - { - IntPtr moduleHandle = RuntimeAugments.GetOSModuleFromPointer(methodData->NativeLayoutInfoSignature); - - signature = RuntimeSignature.CreateFromNativeLayoutSignature( - new TypeManagerHandle(moduleHandle), - GetNativeLayoutInfoReader(new TypeManagerHandle(moduleHandle)).AddressToOffset(methodData->NativeLayoutInfoSignature)); - } -#endif RuntimeSignature remainingSignature; return GetMethodFromSignatureAndContext(signature, null, null, out declaringTypeHandle, out nameAndSignature, out genericMethodArgs, out remainingSignature); diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs index 734eba6c02..bde93220d8 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs @@ -446,7 +446,7 @@ namespace Internal.Runtime.TypeLoader /// /// Locate the static constructor context given the runtime type handle (EEType) for the type in question. /// - /// EEtype of the type to look up + /// EEType of the type to look up public static unsafe IntPtr TryGetStaticClassConstructionContext(RuntimeTypeHandle typeHandle) { if (RuntimeAugments.HasCctor(typeHandle)) @@ -847,14 +847,14 @@ namespace Internal.Runtime.TypeLoader // done at runtime. When working with the CoreRT ABI, the correct slot numbers will be written to the map, // and no adjustments will be performed at runtime. // - -#if CORERT + +#if PROJECTN CanonicallyEquivalentEntryLocator canonHelper = new CanonicallyEquivalentEntryLocator( - methodHandleDeclaringType, + Instance.GetTypeDefinition(methodHandleDeclaringType), CanonicalFormKind.Specific); #else CanonicallyEquivalentEntryLocator canonHelper = new CanonicallyEquivalentEntryLocator( - Instance.GetTypeDefinition(methodHandleDeclaringType), + methodHandleDeclaringType, CanonicalFormKind.Specific); #endif @@ -931,7 +931,7 @@ namespace Internal.Runtime.TypeLoader { uint slot = entryParser.GetUnsigned(); -#if !CORERT +#if PROJECTN RuntimeTypeHandle searchForSharedGenericTypesInParentHierarchy = declaringTypeOfVirtualInvoke; while (!searchForSharedGenericTypesInParentHierarchy.IsNull()) { @@ -1680,11 +1680,14 @@ namespace Internal.Runtime.TypeLoader return true; } - // Generic non-shareable method: check the method instantiation arguments + // Generic non-shareable method or abstract methods: check for the canonical equivalency of the method + // instantiation arguments that we read from the entry if (((_flags & InvokeTableFlags.RequiresInstArg) == 0) || !_hasEntryPoint) - return _lookupMethodInfo.CompareMethodInstantiation(_methodInstantiation); + return _lookupMethodInfo.CanInstantiationsShareCode(_methodInstantiation, _canonFormKind); - // Generic shareable method: check for canonical equivalency of the method instantiation arguments + // Generic shareable method: check for canonical equivalency of the method instantiation arguments. + // The method instantiation arguments are extracted from the generic dictionary pointer that we read from the entry. + Debug.Assert(_entryDictionary != IntPtr.Zero); return GetNameAndSignatureAndMethodInstantiation() && _lookupMethodInfo.CanInstantiationsShareCode(_entryMethodInstantiation, _canonFormKind); } @@ -1746,7 +1749,7 @@ namespace Internal.Runtime.TypeLoader if ((_flags & InvokeTableFlags.IsUniversalCanonicalEntry) != 0) { // _nameAndSignature should have been read from the InvokeMap entry directly! - Debug.Assert(false, "Universal canonical entries do NOT have dictionary entries!"); + Debug.Fail("Universal canonical entries do NOT have dictionary entries!"); return false; } diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.SignatureParsing.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.SignatureParsing.cs index 3dcdb8b926..7666e88408 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.SignatureParsing.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.SignatureParsing.cs @@ -275,17 +275,30 @@ namespace Internal.Runtime.TypeLoader internal bool GetCallingConverterDataFromMethodSignature_NativeLayout(TypeSystemContext context, RuntimeSignature methodSig, Instantiation typeInstantiation, Instantiation methodInstantiation, out bool hasThis, out TypeDesc[] parameters, out bool[] parametersWithGenericDependentLayout) { - return GetCallingConverterDataFromMethodSignature_NativeLayout_Common(context, methodSig, typeInstantiation, methodInstantiation, out hasThis, out parameters, out parametersWithGenericDependentLayout, null); + return GetCallingConverterDataFromMethodSignature_NativeLayout_Common( + context, + methodSig, + typeInstantiation, + methodInstantiation, + out hasThis, + out parameters, + out parametersWithGenericDependentLayout, + null, + null); } - internal bool GetCallingConverterDataFromMethodSignature_NativeLayout_Debugger(TypeSystemContext context, RuntimeSignature methodSig, Instantiation typeInstantiation, Instantiation methodInstantiation, out bool hasThis, out TypeDesc[] parameters, out bool[] parametersWithGenericDependentLayout, NativeReader nativeReader) + internal bool GetCallingConverterDataFromMethodSignature_NativeLayout_Common( + TypeSystemContext context, + RuntimeSignature methodSig, + Instantiation typeInstantiation, + Instantiation methodInstantiation, + out bool hasThis, + out TypeDesc[] parameters, + out bool[] parametersWithGenericDependentLayout, + NativeReader nativeReader, + ulong[] debuggerPreparedExternalReferences) { - return GetCallingConverterDataFromMethodSignature_NativeLayout_Common(context, methodSig, typeInstantiation, methodInstantiation, out hasThis, out parameters, out parametersWithGenericDependentLayout, nativeReader); - } - - internal bool GetCallingConverterDataFromMethodSignature_NativeLayout_Common(TypeSystemContext context, RuntimeSignature methodSig, Instantiation typeInstantiation, Instantiation methodInstantiation, out bool hasThis, out TypeDesc[] parameters, out bool[] parametersWithGenericDependentLayout, NativeReader nativeReader) - { - bool isNotDebuggerCall = nativeReader == null; + bool isNotDebuggerCall = debuggerPreparedExternalReferences == null ; hasThis = false; parameters = null; @@ -295,9 +308,19 @@ namespace Internal.Runtime.TypeLoader nativeLayoutContext._typeSystemContext = context; nativeLayoutContext._typeArgumentHandles = typeInstantiation; nativeLayoutContext._methodArgumentHandles = methodInstantiation; + nativeLayoutContext._debuggerPreparedExternalReferences = debuggerPreparedExternalReferences; - NativeReader reader = isNotDebuggerCall ? GetNativeLayoutInfoReader(methodSig) : nativeReader; - NativeFormatModuleInfo module = isNotDebuggerCall ? ModuleList.Instance.GetModuleInfoByHandle(new TypeManagerHandle(methodSig.ModuleHandle)) : null; + NativeFormatModuleInfo module = null; + NativeReader reader = null; + if (isNotDebuggerCall) + { + reader = GetNativeLayoutInfoReader(methodSig); + module = ModuleList.Instance.GetModuleInfoByHandle(new TypeManagerHandle(methodSig.ModuleHandle)); + } + else + { + reader = nativeReader; + } NativeParser parser = new NativeParser(reader, methodSig.NativeLayoutOffset); MethodCallingConvention callingConvention = (MethodCallingConvention)parser.GetUnsigned(); @@ -650,7 +673,7 @@ namespace Internal.Runtime.TypeLoader case TypeSignatureKind.Lookback: { // Recursion above better have removed all lookbacks - Debug.Assert(false, "Unexpected lookback type"); + Debug.Fail("Unexpected lookback type"); return false; } diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.StaticsLookup.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.StaticsLookup.cs index dfca3feaf2..dedadaf92a 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.StaticsLookup.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.StaticsLookup.cs @@ -354,32 +354,26 @@ namespace Internal.Runtime.TypeLoader } - public unsafe IntPtr TryGetThreadStaticFieldOffsetCookieForTypeAndFieldOffset(RuntimeTypeHandle runtimeTypeHandle, uint fieldOffset) + public unsafe bool TryGetThreadStaticStartOffset(RuntimeTypeHandle runtimeTypeHandle, out int threadStaticsStartOffset) { - var cookieData = new PermanentAllocatedMemoryBlobs.ThreadStaticFieldOffsets(); + threadStaticsStartOffset = -1; if (runtimeTypeHandle.IsDynamicType()) { - cookieData.StartingOffsetInTlsBlock = 0; - cookieData.FieldOffset = fieldOffset; + // Specific TLS storage is allocated for each dynamic type. There is no starting offset since it's not a + // TLS storage block shared by multiple types. + threadStaticsStartOffset = 0; + return true; } else { IntPtr ptrToTlsOffset = TryGetTlsOffsetDictionaryCellForStaticType(runtimeTypeHandle); if (ptrToTlsOffset == IntPtr.Zero) - return IntPtr.Zero; + return false; - uint tlsOffset = *(uint*)ptrToTlsOffset; - cookieData.StartingOffsetInTlsBlock = tlsOffset; - cookieData.FieldOffset = fieldOffset; + threadStaticsStartOffset = *(int*)ptrToTlsOffset; + return true; } - - return PermanentAllocatedMemoryBlobs.GetPointerToThreadStaticFieldOffsets(cookieData); - } - - public static unsafe uint GetThreadStaticTypeOffsetFromThreadStaticCookie(IntPtr threadStaticFieldCookie) - { - return ((PermanentAllocatedMemoryBlobs.ThreadStaticFieldOffsets*)threadStaticFieldCookie.ToPointer())->FieldOffset; } private IntPtr TryGetTlsIndexDictionaryCellForStaticType(RuntimeTypeHandle runtimeTypeHandle) diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs index 1c5442ca2f..30f13e5d04 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs @@ -76,6 +76,21 @@ namespace Internal.Runtime.TypeLoader return TypeLoaderEnvironment.ConvertUnboxingFunctionPointerToUnderlyingNonUnboxingPointer(unboxingFunctionPointer, declaringType); } + public override bool TryGetPointerTypeForTargetType(RuntimeTypeHandle pointeeTypeHandle, out RuntimeTypeHandle pointerTypeHandle) + { + return TypeLoaderEnvironment.Instance.TryGetPointerTypeForTargetType(pointeeTypeHandle, out pointerTypeHandle); + } + + public override bool TryGetArrayTypeForElementType(RuntimeTypeHandle elementTypeHandle, bool isMdArray, int rank, out RuntimeTypeHandle arrayTypeHandle) + { + return TypeLoaderEnvironment.Instance.TryGetArrayTypeForElementType(elementTypeHandle, isMdArray, rank, out arrayTypeHandle); + } + + public override IntPtr UpdateFloatingDictionary(IntPtr context, IntPtr dictionaryPtr) + { + return TypeLoaderEnvironment.Instance.UpdateFloatingDictionary(context, dictionaryPtr); + } + /// /// Register a new runtime-allocated code thunk in the diagnostic stream. /// @@ -205,7 +220,7 @@ namespace Internal.Runtime.TypeLoader return !type.RuntimeTypeHandle.IsNull(); } - private TypeDesc GetConstructedTypeFromParserAndNativeLayoutContext(ref NativeParser parser, NativeLayoutInfoLoadContext nativeLayoutContext) + internal TypeDesc GetConstructedTypeFromParserAndNativeLayoutContext(ref NativeParser parser, NativeLayoutInfoLoadContext nativeLayoutContext) { TypeDesc parsedType = nativeLayoutContext.GetType(ref parser); if (parsedType == null) @@ -503,6 +518,67 @@ namespace Internal.Runtime.TypeLoader } } + public unsafe IntPtr UpdateFloatingDictionary(IntPtr context, IntPtr dictionaryPtr) + { + IntPtr newFloatingDictionary; + bool isNewlyAllocatedDictionary; + bool isTypeContext = context != dictionaryPtr; + + if (isTypeContext) + { + // Look for the exact base type that owns the dictionary. We may be having + // a virtual method run on a derived type and the generic lookup are performed + // on the base type's dictionary. + EEType* pEEType = (EEType*)context.ToPointer(); + context = (IntPtr)EETypeCreator.GetBaseEETypeForDictionaryPtr(pEEType, dictionaryPtr); + } + + using (LockHolder.Hold(_typeLoaderLock)) + { + // Check if some other thread already allocated a floating dictionary and updated the fixed portion + if(*(IntPtr*)dictionaryPtr != IntPtr.Zero) + return *(IntPtr*)dictionaryPtr; + + try + { + if (t_isReentrant) + Environment.FailFast("Reentrant update to floating dictionary"); + t_isReentrant = true; + + newFloatingDictionary = TypeBuilder.TryBuildFloatingDictionary(context, isTypeContext, dictionaryPtr, out isNewlyAllocatedDictionary); + + t_isReentrant = false; + } + catch + { + // Catch and rethrow any exceptions instead of using finally block. Otherwise, filters that are run during + // the first pass of exception unwind may hit the re-entrancy fail fast above. + + // TODO: Convert this to filter for better diagnostics once we switch to Roslyn + + t_isReentrant = false; + throw; + } + } + + if (newFloatingDictionary == IntPtr.Zero) + { + Environment.FailFast("Unable to update floating dictionary"); + return IntPtr.Zero; + } + + // The pointer to the floating dictionary is the first slot of the fixed dictionary. + if (Interlocked.CompareExchange(ref *(IntPtr*)dictionaryPtr, newFloatingDictionary, IntPtr.Zero) != IntPtr.Zero) + { + // Some other thread beat us and updated the pointer to the floating dictionary. + // Free the one allocated by the current thread + if (isNewlyAllocatedDictionary) + MemoryHelpers.FreeMemory(newFloatingDictionary); + } + + return *(IntPtr*)dictionaryPtr; + } + public bool CanInstantiationsShareCode(RuntimeTypeHandle[] genericArgHandles1, RuntimeTypeHandle[] genericArgHandles2, CanonicalFormKind kind) { if (genericArgHandles1.Length != genericArgHandles2.Length) @@ -589,7 +665,14 @@ namespace Internal.Runtime.TypeLoader public static unsafe bool TryGetTargetOfUnboxingAndInstantiatingStub(IntPtr maybeInstantiatingAndUnboxingStub, out IntPtr targetMethod) { - targetMethod = IntPtr.Zero; + targetMethod = RuntimeAugments.GetTargetOfUnboxingAndInstantiatingStub(maybeInstantiatingAndUnboxingStub); + if (targetMethod != IntPtr.Zero) + { + return true; + } + + // TODO: The rest of the code in this function is specific to ProjectN only. When we kill the binder, get rid of this + // linear search code (the only API that should be used for the lookup is the one above) // Get module IntPtr associatedModule = RuntimeAugments.GetOSModuleFromPointer(maybeInstantiatingAndUnboxingStub); @@ -598,6 +681,15 @@ namespace Internal.Runtime.TypeLoader return false; } + // Module having a type manager means we are not in ProjectN mode. Bail out earlier. + foreach (TypeManagerHandle handle in ModuleList.Enumerate()) + { + if (handle.OsModuleBase == associatedModule && handle.IsTypeManager) + { + return false; + } + } + // Get UnboxingAndInstantiatingTable UnboxingAndInstantiatingStubMapEntry* pBlob; uint cbBlob; diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderTypeSystemContext.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderTypeSystemContext.cs index 537e00bae9..3b9b0bcd1f 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderTypeSystemContext.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderTypeSystemContext.cs @@ -191,7 +191,7 @@ namespace Internal.Runtime.TypeLoader default: if (throwIfNotFound) - throw new NotImplementedException(); + throw new TypeLoadException(); else return null; } diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemContextFactory.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemContextFactory.cs index 56959bd5ec..d454e5c7cd 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemContextFactory.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemContextFactory.cs @@ -41,6 +41,8 @@ namespace Internal.Runtime.TypeLoader TargetArchitecture.X86, #elif AMD64 TargetArchitecture.X64, +#elif WASM + TargetArchitecture.Wasm32, #else #error Unknown architecture #endif diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/genericdictionarycell.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/genericdictionarycell.cs index afe6066c7a..1f04052f8e 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/genericdictionarycell.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/genericdictionarycell.cs @@ -440,10 +440,39 @@ namespace Internal.Runtime.TypeLoader { internal TypeDesc ContainingType; internal uint VTableSlot; + private TypeDesc ContainingTypeTemplate; internal unsafe override void Prepare(TypeBuilder builder) { builder.RegisterForPreparation(ContainingType); + ContainingTypeTemplate = ContainingType.ComputeTemplate(); + } + + // + // This helper function will traverse the hierarchy of the containing type of this vtable offset cell, in parallel with + // the hierarchy of its template type. + // When traversing the template type hierarchy, we have 2 possibilities for the base type: + // - Fully universal canonical. USG types always have a dictionary slot, so if the dynamically created type does not share + // normal canonical code, we subtract 1 from the vtable offset (the dynamic type does not have a dictionary slot in that case) + // - Exact non-canonical type. In that case, we do not need to make any changes to the vtable offset (the binder/ILCompiler + // would have written the correct vtable offset, taking in the account the existance or non-existance of a dictionary slot. + // + private void AdjustVtableSlot(TypeDesc currentType, TypeDesc currentTemplateType, ref int vtableSlot) + { + TypeDesc baseType = currentType.BaseType; + TypeDesc baseTemplateType = TypeBuilder.GetBaseTypeUsingRuntimeTypeHandle(currentTemplateType); + + Debug.Assert((baseType == null && baseTemplateType == null) || (baseType != null && baseTemplateType != null)); + + // Compute the vtable layout for the current type starting with base types first + if (baseType != null) + AdjustVtableSlot(baseType, baseTemplateType, ref vtableSlot); + + if (currentType.IsGeneric()) + { + if (!currentType.CanShareNormalGenericCode() && currentTemplateType.IsCanonicalSubtype(CanonicalFormKind.Universal)) + vtableSlot--; + } } internal override unsafe IntPtr Create(TypeBuilder builder) @@ -457,21 +486,11 @@ namespace Internal.Runtime.TypeLoader #endif int result = (int)VTableSlot; - DefType currentType = (DefType)ContainingType; - while (currentType != null) - { - if (currentType.HasInstantiation) - { - // Check if the current type can share code with normal canonical - // generic types. If not, then the vtable layout will not have a - // slot for a dictionary pointer, and we need to adjust the slot number - if (!currentType.CanShareNormalGenericCode()) - result--; - } - - currentType = currentType.BaseType; - } + // Check if the current type can share code with normal canonical + // generic types. If not, then the vtable layout will not have a + // slot for a dictionary pointer, and we need to adjust the slot number + AdjustVtableSlot(ContainingType, ContainingTypeTemplate, ref result); Debug.Assert(result >= 0); return (IntPtr)(sizeof(EEType) + result * IntPtr.Size); @@ -712,7 +731,7 @@ namespace Internal.Runtime.TypeLoader } case TypeLoaderEnvironment.MethodAddressType.UniversalCanonical: { - if (Method.IsCanonicalMethod(CanonicalFormKind.Specific) && + if (Method.IsCanonicalMethod(CanonicalFormKind.Universal) && !NeedsDictionaryParameterToCallCanonicalVersion(Method) && !UniversalGenericParameterLayout.MethodSignatureHasVarsNeedingCallingConventionConverter( Method.GetTypicalMethodDefinition().Signature)) @@ -737,11 +756,13 @@ namespace Internal.Runtime.TypeLoader bool methodRequestedIsCanonical = Method.IsCanonicalMethod(CanonicalFormKind.Specific); MethodDesc canonAlikeForm; +#if PROJECTN if (methodRequestedIsCanonical) { canonAlikeForm = Method.ReplaceTypesInConstructionOfMethod(Method.Context.CanonTypeArray, Method.Context.CanonAlikeTypeArray); } else +#endif { canonAlikeForm = Method; } @@ -852,7 +873,7 @@ namespace Internal.Runtime.TypeLoader return BuildCallingConventionConverter(builder, Method.UsgFunctionPointer, methodDictionary, true); } - Debug.Assert(false, "UNREACHABLE"); + Debug.Fail("UNREACHABLE"); return IntPtr.Zero; } @@ -870,15 +891,25 @@ namespace Internal.Runtime.TypeLoader Debug.Assert(!MethodSignature.IsNativeLayoutSignature || (MethodSignature.NativeLayoutSignature() != IntPtr.Zero)); - CallConverterThunk.ThunkKind thunkKind; + CallConverterThunk.ThunkKind thunkKind = default(CallConverterThunk.ThunkKind); if (usgConverter) { if (genericMethod || containingTypeIsValueType) { - if (dictionary == IntPtr.Zero) - thunkKind = CallConverterThunk.ThunkKind.StandardToGenericPassthruInstantiating; + if (Method.UnboxingStub) + { + if (dictionary == IntPtr.Zero) + Environment.FailFast("Need standard to generic non-instantiating unboxing stub thunk kind"); + else + thunkKind = CallConverterThunk.ThunkKind.StandardUnboxingAndInstantiatingGeneric; + } else - thunkKind = CallConverterThunk.ThunkKind.StandardToGenericInstantiating; + { + if (dictionary == IntPtr.Zero) + thunkKind = CallConverterThunk.ThunkKind.StandardToGenericPassthruInstantiating; + else + thunkKind = CallConverterThunk.ThunkKind.StandardToGenericInstantiating; + } } else { @@ -1061,11 +1092,73 @@ namespace Internal.Runtime.TypeLoader for (uint i = 0; i < count; i++) { - GenericDictionaryCell cell = ParseAndCreateCell(nativeLayoutInfoLoadContext, ref parser); - cell.Prepare(typeBuilder); - dictionary[i] = cell; + TypeLoaderLogger.WriteLine(" -> DictionaryCell[" + i.LowLevelToString() + "] = "); + + dictionary[i] = ParseAndCreateCell(nativeLayoutInfoLoadContext, ref parser); } + for (uint i = 0; i < count; i++) + dictionary[i].Prepare(typeBuilder); + + return dictionary; + } + + internal static unsafe GenericDictionaryCell[] BuildFloatingDictionary(TypeBuilder typeBuilder, NativeLayoutInfoLoadContext nativeLayoutInfoLoadContext, NativeParser parser, out int floatingVersionCellIndex, out int floatingVersionInLayout) + { + // + // The format of a dictionary that has a floating portion is as follows: + // + // "Fixed" portion: + // - First slot is a pointer to the first cell of the "floating" portion + // - Followed by N various dictionary lookup cells + // "Floating" portion: + // - Cell containing the version number of the floating portion + // - Followed by N various dictionary lookup cells + // + + floatingVersionCellIndex = floatingVersionInLayout = -1; + + uint count = parser.GetSequenceCount(); + Debug.Assert(count > 1); + + GenericDictionaryCell cell = ParseAndCreateCell(nativeLayoutInfoLoadContext, ref parser); + if (!(cell is PointerToOtherDictionarySlotCell)) + { + // This is not a dictionary layout that has a floating portion + Debug.Fail("Unreachable: we should never reach here if the target dictionary does not have a floating layout"); + return null; + } + + PointerToOtherDictionarySlotCell pointerToCell = (PointerToOtherDictionarySlotCell)cell; + floatingVersionCellIndex = (int)pointerToCell.OtherDictionarySlot; + Debug.Assert(count > pointerToCell.OtherDictionarySlot); + + GenericDictionaryCell[] dictionary = new GenericDictionaryCell[count - pointerToCell.OtherDictionarySlot]; + + for (uint i = 1; i < pointerToCell.OtherDictionarySlot; i++) + { + // Parse and discard the fixed dictionary cells. We only need to build the cells of the floating portion + ParseAndCreateCell(nativeLayoutInfoLoadContext, ref parser); + } + + for (uint i = pointerToCell.OtherDictionarySlot; i < count; i++) + { + TypeLoaderLogger.WriteLine(" -> FloatingDictionaryCell[" + (i - pointerToCell.OtherDictionarySlot).LowLevelToString() + "] (" + i.LowLevelToString() + " in all) = "); + + cell = ParseAndCreateCell(nativeLayoutInfoLoadContext, ref parser); + + if ((i == pointerToCell.OtherDictionarySlot) && (cell is IntPtrCell)) + { + // The first cell in the floating portion should always be the version number + floatingVersionInLayout = (int)((IntPtrCell)cell).Value; + } + + dictionary[i - pointerToCell.OtherDictionarySlot] = cell; + } + + for (uint i = pointerToCell.OtherDictionarySlot; i < count; i++) + dictionary[i - pointerToCell.OtherDictionarySlot].Prepare(typeBuilder); + return dictionary; } @@ -1805,15 +1898,7 @@ namespace Internal.Runtime.TypeLoader NativeParser sigParser = parser.GetParserFromRelativeOffset(); RuntimeSignature signature = RuntimeSignature.CreateFromNativeLayoutSignature(nativeLayoutInfoLoadContext._module.Handle, sigParser.Offset); -#if TYPE_LOADER_TRACE - TypeLoaderLogger.WriteLine("CallingConventionConverter on: "); - TypeLoaderLogger.WriteLine(" -> Flags: " + ((int)flags).LowLevelToString()); - TypeLoaderLogger.WriteLine(" -> Signature: " + signature.NativeLayoutSignature().LowLevelToString()); - for (int i = 0; !nativeLayoutInfoLoadContext._typeArgumentHandles.IsNull && i < nativeLayoutInfoLoadContext._typeArgumentHandles.Length; i++) - TypeLoaderLogger.WriteLine(" -> TypeArg[" + i.LowLevelToString() + "]: " + nativeLayoutInfoLoadContext._typeArgumentHandles[i]); - for (int i = 0; !nativeLayoutInfoLoadContext._methodArgumentHandles.IsNull && i < nativeLayoutInfoLoadContext._methodArgumentHandles.Length; i++) - TypeLoaderLogger.WriteLine(" -> MethodArg[" + i.LowLevelToString() + "]: " + nativeLayoutInfoLoadContext._methodArgumentHandles[i]); -#endif + TypeLoaderLogger.WriteLine("CallingConventionConverter: Flags=" + ((int)flags).LowLevelToString() + " Signature=" + signature.NativeLayoutSignature().LowLevelToString()); cell = new CallingConventionConverterCell { @@ -1834,6 +1919,7 @@ namespace Internal.Runtime.TypeLoader { OtherDictionarySlot = parser.GetUnsigned() }; + TypeLoaderLogger.WriteLine("PointerToOtherSlot: " + ((PointerToOtherDictionarySlotCell)cell).OtherDictionarySlot.LowLevelToString()); break; case FixupSignatureKind.IntValue: @@ -1841,6 +1927,7 @@ namespace Internal.Runtime.TypeLoader { Value = new IntPtr((int)parser.GetUnsigned()) }; + TypeLoaderLogger.WriteLine("IntValue: " + ((IntPtrCell)cell).Value.LowLevelToString()); break; default: diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/ArrayMethod.Runtime.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/ArrayMethod.Runtime.cs index 76619a2172..1be93ce111 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/ArrayMethod.Runtime.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/ArrayMethod.Runtime.cs @@ -15,7 +15,7 @@ namespace Internal.TypeSystem get { // TODO Eventually implement via working with a RuntimeMethod that refers to the actual implementation. - throw new global::System.NotImplementedException(); + throw NotImplemented.ActiveIssue("https://github.com/dotnet/corert/issues/3772"); } } } diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/CanonTypes.Runtime.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/CanonTypes.Runtime.cs index b7f4f5bc5d..ce05945b45 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/CanonTypes.Runtime.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/CanonTypes.Runtime.cs @@ -5,6 +5,7 @@ using System; +using Internal.Runtime; using Internal.Runtime.Augments; namespace Internal.TypeSystem diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/DefType.Runtime.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/DefType.Runtime.cs index 0e1f664b30..cdcd3eaa5b 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/DefType.Runtime.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/DefType.Runtime.cs @@ -15,13 +15,6 @@ namespace Internal.TypeSystem { internal static readonly LayoutInt MaximumAlignmentPossible = new LayoutInt(8); - public override IEnumerable GetFields() - { - // TODO davidwr! This isn't right for types with metadata, although it will work for now. - // Right now it serves to verify that none of our loading paths for the template type loader use it. - throw new NotImplementedException(); - } - internal IEnumerable GetDiagnosticFields() { if (HasNativeLayout) diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/ExceptionTypeNameFormatter.Runtime.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/ExceptionTypeNameFormatter.Runtime.cs new file mode 100644 index 0000000000..06f8d87011 --- /dev/null +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/ExceptionTypeNameFormatter.Runtime.cs @@ -0,0 +1,25 @@ +// 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. + +namespace Internal.TypeSystem +{ + partial class ExceptionTypeNameFormatter + { + private string GetTypeName(DefType type) + { + if (type is NoMetadata.NoMetadataType) + return ((NoMetadata.NoMetadataType)type).DiagnosticName; + + return type.Name; + } + + private string GetTypeNamespace(DefType type) + { + if (type is NoMetadata.NoMetadataType) + return ((NoMetadata.NoMetadataType)type).DiagnosticNamespace; + + return type.Namespace; + } + } +} diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/MethodForRuntimeDeterminedType.Runtime.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/MethodForRuntimeDeterminedType.Runtime.cs new file mode 100644 index 0000000000..ff296685f0 --- /dev/null +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/MethodForRuntimeDeterminedType.Runtime.cs @@ -0,0 +1,21 @@ +// 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 Internal.Runtime.CompilerServices; + +namespace Internal.TypeSystem +{ + public sealed partial class MethodForRuntimeDeterminedType : MethodDesc + { + public override MethodNameAndSignature NameAndSignature + { + get + { + return _typicalMethodDef.NameAndSignature; + } + } + } +} diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeMethodDesc.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeMethodDesc.cs index 0d4c8354b2..67e18c507a 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeMethodDesc.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeMethodDesc.cs @@ -75,7 +75,7 @@ namespace Internal.TypeSystem.NoMetadata { get { - throw new NotImplementedException(); + throw new NotSupportedException(); } } diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeNoMetadataType.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeNoMetadataType.cs index 1f5425b0ce..15bcb28bb0 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeNoMetadataType.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeNoMetadataType.cs @@ -4,13 +4,16 @@ using System; +using System.Text; +using System.Reflection.Runtime.General; +using Internal.NativeFormat; using Internal.TypeSystem; using Internal.Runtime; using Internal.Runtime.Augments; using Internal.Runtime.TypeLoader; -using System.Text; +using Internal.Metadata.NativeFormat; + using Debug = System.Diagnostics.Debug; -using Internal.NativeFormat; namespace Internal.TypeSystem.NoMetadata { @@ -28,7 +31,8 @@ namespace Internal.TypeSystem.NoMetadata private RuntimeTypeHandle _genericTypeDefinition; private DefType _genericTypeDefinitionAsDefType; private Instantiation _instantiation; - private bool _baseTypeCached; + + // "_baseType == this" means "base type was not initialized yet" private DefType _baseType; public NoMetadataType(TypeSystemContext context, RuntimeTypeHandle genericTypeDefinition, DefType genericTypeDefinitionAsDefType, Instantiation instantiation, int hashcode) @@ -49,6 +53,9 @@ namespace Internal.TypeSystem.NoMetadata Debug.Assert(((_instantiation.Length > 0) && _genericTypeDefinition.ToEETypePtr()->IsGenericTypeDefinition) || ((_instantiation.Length == 0) && !_genericTypeDefinition.ToEETypePtr()->IsGenericTypeDefinition)); } + + // Base type is not initialized + _baseType = this; } public override int GetHashCode() @@ -68,7 +75,8 @@ namespace Internal.TypeSystem.NoMetadata { get { - if (_baseTypeCached) + // _baseType == this means we didn't initialize it yet + if (_baseType != this) return _baseType; if (RetrieveRuntimeTypeHandleIfPossible()) @@ -96,7 +104,7 @@ namespace Internal.TypeSystem.NoMetadata NativeParser baseTypeParser = typeInfoParser.GetParserForBagElementKind(BagElementKind.BaseType); ParseBaseType(state.NativeLayoutInfo.LoadContext, baseTypeParser); - Debug.Assert(_baseTypeCached); + Debug.Assert(_baseType != this); return _baseType; } } @@ -121,9 +129,8 @@ namespace Internal.TypeSystem.NoMetadata /// public void SetBaseType(DefType baseType) { - Debug.Assert(!_baseTypeCached || _baseType == baseType); + Debug.Assert(_baseType == this || _baseType == baseType); _baseType = baseType; - _baseTypeCached = true; } protected override TypeFlags ComputeTypeFlags(TypeFlags mask) @@ -192,6 +199,20 @@ namespace Internal.TypeSystem.NoMetadata } } + if ((mask & TypeFlags.IsByRefLikeComputed) != 0) + { + flags |= TypeFlags.IsByRefLikeComputed; + + unsafe + { + EEType* eetype = _genericTypeDefinition.ToEETypePtr(); + if (eetype->IsByRefLike) + { + flags |= TypeFlags.IsByRefLike; + } + } + } + return flags; } @@ -280,6 +301,68 @@ namespace Internal.TypeSystem.NoMetadata } } + private void GetTypeNameHelper(out string name, out string nsName, out string assemblyName) + { + TypeReferenceHandle typeRefHandle; + QTypeDefinition qTypeDefinition; + MetadataReader reader; + + RuntimeTypeHandle genericDefinitionHandle = GetTypeDefinition().GetRuntimeTypeHandle(); + Debug.Assert(!genericDefinitionHandle.IsNull()); + + string enclosingDummy; + + // Try to get the name from metadata + if (TypeLoaderEnvironment.Instance.TryGetMetadataForNamedType(genericDefinitionHandle, out qTypeDefinition)) + { + TypeDefinitionHandle typeDefHandle = qTypeDefinition.NativeFormatHandle; + typeDefHandle.GetFullName(qTypeDefinition.NativeFormatReader, out name, out enclosingDummy, out nsName); + assemblyName = typeDefHandle.GetContainingModuleName(qTypeDefinition.NativeFormatReader); + } + // Try to get the name from diagnostic metadata + else if (TypeLoaderEnvironment.TryGetTypeReferenceForNamedType(genericDefinitionHandle, out reader, out typeRefHandle)) + { + typeRefHandle.GetFullName(reader, out name, out enclosingDummy, out nsName); + assemblyName = typeRefHandle.GetContainingModuleName(reader); + } + else + { + name = genericDefinitionHandle.LowLevelToStringRawEETypeAddress(); + nsName = ""; + assemblyName = "?"; + } + } + + public string DiagnosticNamespace + { + get + { + string name, nsName, assemblyName; + GetTypeNameHelper(out name, out nsName, out assemblyName); + return nsName; + } + } + + public string DiagnosticName + { + get + { + string name, nsName, assemblyName; + GetTypeNameHelper(out name, out nsName, out assemblyName); + return name; + } + } + + public string DiagnosticModuleName + { + get + { + string name, nsName, assemblyName; + GetTypeNameHelper(out name, out nsName, out assemblyName); + return assemblyName; + } + } + #if DEBUG private string _cachedToString = null; diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/ThrowHelper.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/ThrowHelper.cs new file mode 100644 index 0000000000..8c2027caff --- /dev/null +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/ThrowHelper.cs @@ -0,0 +1,64 @@ +// 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 CoreLibThrow = Internal.Runtime.CompilerHelpers.ThrowHelpers; + +namespace Internal.TypeSystem +{ + // This implementation forwards to the throw helpers targeted by the compiler in CoreLib. + // That way we can share the exception string resources. + public static partial class ThrowHelper + { + private static void ThrowTypeLoadException(ExceptionStringID id, string typeName, string assemblyName, string messageArg) + { + CoreLibThrow.ThrowTypeLoadExceptionWithArgument(id, typeName, assemblyName, messageArg); + } + + private static void ThrowTypeLoadException(ExceptionStringID id, string typeName, string assemblyName) + { + CoreLibThrow.ThrowTypeLoadException(id, typeName, assemblyName); + } + + public static void ThrowMissingMethodException(TypeDesc owningType, string methodName, MethodSignature signature) + { + CoreLibThrow.ThrowMissingMethodException(ExceptionStringID.MissingMethod, Format.Method(owningType, methodName, signature)); + } + + public static void ThrowMissingFieldException(TypeDesc owningType, string fieldName) + { + CoreLibThrow.ThrowMissingFieldException(ExceptionStringID.MissingField, Format.Field(owningType, fieldName)); + } + + public static void ThrowFileNotFoundException(ExceptionStringID id, string fileName) + { + CoreLibThrow.ThrowFileNotFoundException(id, fileName); + } + + public static void ThrowInvalidProgramException() + { + CoreLibThrow.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramDefault); + } + + public static void ThrowInvalidProgramException(ExceptionStringID id, MethodDesc method) + { + CoreLibThrow.ThrowInvalidProgramExceptionWithArgument(id, Format.Method(method)); + } + + public static void ThrowBadImageFormatException() + { + CoreLibThrow.ThrowBadImageFormatException(ExceptionStringID.BadImageFormatGeneric); + } + + private static partial class Format + { + public static string OwningModule(TypeDesc type) + { + if (type is NoMetadata.NoMetadataType) + return ((NoMetadata.NoMetadataType)type).DiagnosticModuleName; + + return Module((type as MetadataType)?.Module); + } + } + } +} diff --git a/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/TypeSystemContext.Runtime.cs b/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/TypeSystemContext.Runtime.cs index e318a1109d..81eda7cca5 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/TypeSystemContext.Runtime.cs +++ b/external/corert/src/System.Private.TypeLoader/src/Internal/TypeSystem/TypeSystemContext.Runtime.cs @@ -56,7 +56,7 @@ namespace Internal.TypeSystem protected override RuntimeTypeHandle CreateValueFromKey(RuntimeTypeHandle key) { - throw new NotImplementedException(); + throw new NotSupportedException(); } protected override int GetKeyHashCode(RuntimeTypeHandle key) @@ -647,6 +647,7 @@ namespace Internal.TypeSystem _typesToFlushTypeSystemStateFrom = null; } +#if PROJECTN private TypeDesc _canonAlikeType; public TypeDesc CanonAlikeType @@ -681,6 +682,7 @@ namespace Internal.TypeSystem return _canonTypeArray; } } +#endif #if ECMA_METADATA_SUPPORT public class ModuleToEcmaModuleHashtable : LockFreeReaderHashtable diff --git a/external/corert/src/System.Private.TypeLoader/src/Resources/Strings.resx b/external/corert/src/System.Private.TypeLoader/src/Resources/Strings.resx index 02df9452b6..949e8adc2f 100644 --- a/external/corert/src/System.Private.TypeLoader/src/Resources/Strings.resx +++ b/external/corert/src/System.Private.TypeLoader/src/Resources/Strings.resx @@ -126,4 +126,7 @@ Cannot load assembly '{0}'. No metadata found for this assembly. - \ No newline at end of file + + Cannot load assembly '{0}'. The assembly exists but its version {1} is lower than the requested version {2}. + + diff --git a/external/corert/src/System.Private.TypeLoader/src/System.Private.TypeLoader.Experimental.csproj b/external/corert/src/System.Private.TypeLoader/src/System.Private.TypeLoader.Experimental.csproj index 888d519310..530409f48b 100644 --- a/external/corert/src/System.Private.TypeLoader/src/System.Private.TypeLoader.Experimental.csproj +++ b/external/corert/src/System.Private.TypeLoader/src/System.Private.TypeLoader.Experimental.csproj @@ -1,8 +1,6 @@ - - + System.Private.TypeLoader.Experimental - {9D3328F2-6D2E-4064-82AE-57EED73872B2} true true diff --git a/external/corert/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj b/external/corert/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj index 5491c32c6d..06eb54493d 100644 --- a/external/corert/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj +++ b/external/corert/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj @@ -1,19 +1,24 @@ - - + System.Private.TypeLoader - {F36495F7-8CF5-474D-A92D-40317AE2439C} + + + + + + + + + 4.0.0.0 Library true TYPE_LOADER_IMPLEMENTATION;$(DefineConstants) + true - - - ECMA_METADATA_SUPPORT;$(DefineConstants) true @@ -33,18 +38,20 @@ CCCONVERTER_TRACE;$(DefineConstants) TYPE_LOADER_TRACE;$(DefineConstants) GENERICS_FORCE_USG;$(DefineConstants) + GVM_RESOLUTION_TRACE;$(DefineConstants) TYPE_SYSTEM_SINGLE_THREADED;$(DefineConstants) - - false - - + + + false + true + @@ -58,6 +65,7 @@ + @@ -70,8 +78,10 @@ - + + + @@ -80,6 +90,7 @@ + @@ -142,6 +153,9 @@ Internal\TypeSystem\Interop\MetadataType.Interop.cs + + Internal\TypeSystem\Canon\CanonTypes.Interop.cs + Internal\TypeSystem\Interop\InstantiatedType.Interop.cs @@ -264,6 +278,9 @@ Internal\TypeSystem\ByRefType.cs + + + Internal\TypeSystem\CastingHelper.cs Internal\TypeSystem\ConstructedTypeRewritingHelpers.cs @@ -376,8 +393,8 @@ Internal\TypeSystem\TypeSystemEntity.cs - - Internal\TypeSystem\TypeSystemException.cs + + Internal\TypeSystem\ThrowHelper.Common.cs Internal\TypeSystem\TypeSystemHelpers.cs @@ -388,6 +405,9 @@ LockFreeReaderHashtableOfPointers.cs + + Internal\TypeSystem\Utilities\ExceptionTypeNameFormatter.cs + TypeNameFormatter.cs @@ -398,6 +418,7 @@ Internal\TypeSystem\WellKnownType.cs + @@ -405,7 +426,6 @@ - @@ -461,6 +481,7 @@ + @@ -468,6 +489,7 @@ + @@ -475,8 +497,8 @@ NativeFormat\MetadataExtensions.cs - - System\Runtime\CompilerServices\__BlockReflectionAttribute.cs + + System\Runtime\CompilerServices\__BlockAllReflectionAttribute.cs @@ -508,5 +530,19 @@ NativeFormat\NativeFormatType.MethodImpls.cs + + + Internal\TypeSystem\TypeDesc.ToString.cs + + + Internal\TypeSystem\MethodDesc.ToString.cs + + + Internal\TypeSystem\FieldDesc.ToString.cs + + + Utilities\DebugNameFormatter.cs + + diff --git a/external/corert/src/Test.CoreLib/src/System/Object.cs b/external/corert/src/Test.CoreLib/src/System/Object.cs index 60777622ef..f402679ff1 100644 --- a/external/corert/src/Test.CoreLib/src/System/Object.cs +++ b/external/corert/src/Test.CoreLib/src/System/Object.cs @@ -17,6 +17,8 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Internal.Runtime.CompilerServices; + // TODO: remove when m_pEEType becomes EETypePtr using EEType = Internal.Runtime.EEType; using ObjHeader = Internal.Runtime.ObjHeader; diff --git a/external/corert/src/Test.CoreLib/src/System/RuntimeExceptionHelpers.cs b/external/corert/src/Test.CoreLib/src/System/RuntimeExceptionHelpers.cs index f30bb06475..7f57a67d0b 100644 --- a/external/corert/src/Test.CoreLib/src/System/RuntimeExceptionHelpers.cs +++ b/external/corert/src/Test.CoreLib/src/System/RuntimeExceptionHelpers.cs @@ -115,5 +115,10 @@ namespace System if (ex == null) FailFast("Exceptions must derive from the System.Exception class"); } + + [RuntimeExport("OnFirstChanceException")] + internal static void OnFirstChanceException(object e) + { + } } } diff --git a/external/corert/src/Test.CoreLib/src/System/RuntimeTypeHandle.cs b/external/corert/src/Test.CoreLib/src/System/RuntimeTypeHandle.cs new file mode 100644 index 0000000000..9292f899e4 --- /dev/null +++ b/external/corert/src/Test.CoreLib/src/System/RuntimeTypeHandle.cs @@ -0,0 +1,39 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System +{ + [StructLayout(LayoutKind.Sequential)] + public struct RuntimeTypeHandle + { + private EETypePtr _pEEType; + + internal RuntimeTypeHandle(EETypePtr pEEType) + { + _pEEType = pEEType; + } + + [Intrinsic] + internal static unsafe IntPtr GetValueInternal(RuntimeTypeHandle handle) + { + return (IntPtr)handle._pEEType.ToPointer(); + } + } +} + +namespace Internal.Runtime.CompilerHelpers +{ + // Needed by the compiler to lower LDTOKEN + internal static class LdTokenHelpers + { + private static RuntimeTypeHandle GetRuntimeTypeHandle(IntPtr pEEType) + { + return new RuntimeTypeHandle(new EETypePtr(pEEType)); + } + } +} diff --git a/external/corert/src/Test.CoreLib/src/System/Type.cs b/external/corert/src/Test.CoreLib/src/System/Type.cs new file mode 100644 index 0000000000..eeed98f492 --- /dev/null +++ b/external/corert/src/Test.CoreLib/src/System/Type.cs @@ -0,0 +1,30 @@ +// 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. + +// System.Type is only defined to support C# typeof. We shouldn't have it here since the semantic +// is not very compatible. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System +{ + public class Type + { + private readonly RuntimeTypeHandle _typeHandle; + + private Type(RuntimeTypeHandle typeHandle) + { + _typeHandle = typeHandle; + } + + public RuntimeTypeHandle TypeHandle => _typeHandle; + + public static Type GetTypeFromHandle(RuntimeTypeHandle rh) + { + return new Type(rh); + } + } +} diff --git a/external/corert/src/Test.CoreLib/src/Test.CoreLib.csproj b/external/corert/src/Test.CoreLib/src/Test.CoreLib.csproj index 7322f6afe5..a7588cad92 100644 --- a/external/corert/src/Test.CoreLib/src/Test.CoreLib.csproj +++ b/external/corert/src/Test.CoreLib/src/Test.CoreLib.csproj @@ -1,26 +1,12 @@ - - + - {6BD96201-7F81-4627-BB41-7A2CCB09082F} Library Test.CoreLib true - true true false - - $(MSBuildThisFileDirectory)/Documentation - - - - - - - - - - true + false FEATURE_GC_STRESS;$(DefineConstants) @@ -97,7 +83,8 @@ - + + @@ -169,9 +156,6 @@ System\RuntimeHandles.cs - - System\RuntimeTypeHandle.cs - System\Runtime\CompilerServices\EagerStaticClassConstructionAttribute.cs @@ -187,11 +171,8 @@ System\Runtime\CompilerServices\MethodImplAttribute.cs - - Runtime.Base\src\System\Runtime\CompilerServices\StackOnlyAttribute.cs - - - System\Runtime\CompilerServices\Unsafe.cs + + Runtime.Base\src\System\Runtime\CompilerServices\IsByRefLikeAttribute.cs System\EETypePtr.cs @@ -244,6 +225,9 @@ System\Void.cs + + Internal\Runtime\CompilerServices\Unsafe.cs + @@ -251,6 +235,8 @@ + + Internal\Runtime\TypeManagerHandle.cs diff --git a/external/corert/src/dir.props b/external/corert/src/dir.props index c79dd80eb7..ccd463c570 100644 --- a/external/corert/src/dir.props +++ b/external/corert/src/dir.props @@ -1,3 +1,15 @@ - + + + + + Debug;Release + AnyCPU;x64;x86;arm;arm64 + + + + $(BaseOutputPath)$(OSPlatformConfig)/sdk + true + + diff --git a/external/corert/src/dir.targets b/external/corert/src/dir.targets index 878c5a7ede..34ca873ae0 100644 --- a/external/corert/src/dir.targets +++ b/external/corert/src/dir.targets @@ -1,18 +1,3 @@ - - - true - - + - - - - - - - diff --git a/external/corert/src/dirs.proj b/external/corert/src/dirs.proj index 643c81a6d1..2356eef0de 100644 --- a/external/corert/src/dirs.proj +++ b/external/corert/src/dirs.proj @@ -1,4 +1,4 @@ - + @@ -6,36 +6,30 @@ - - + + + + + + + + + + + + + + + - - - - - - - - $(TraversalBuildDependsOn); - BuildPackages; - - - - - - EnsureBuildToolsRuntime; - $(TraversalBuildDependsOn) - - - diff --git a/external/corert/src/packaging/netcoreapp/project.json b/external/corert/src/packaging/netcoreapp/project.json deleted file mode 100644 index 1758e62edd..0000000000 --- a/external/corert/src/packaging/netcoreapp/project.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "frameworks": { - "netcoreapp2.0": { - "dependencies": { - "Microsoft.Private.CoreFx.NETCoreApp": "4.4.0-preview1-25218-01", - - "System.Memory": "4.4.0-preview1-25218-01", - "System.Runtime.CompilerServices.Unsafe": "4.4.0-preview1-25218-01", - - "Microsoft.NETCore.Native": "2.0.0-beta-25021-03" - } - } - }, - "runtimes": { - "win7-x64": { }, - "osx.10.10-x64": { }, - "ubuntu.14.04-x64": { }, - "ubuntu.16.04-x64": { } - } -} diff --git a/external/corert/src/packaging/packages.targets b/external/corert/src/packaging/packages.targets deleted file mode 100644 index 5ed71cd974..0000000000 --- a/external/corert/src/packaging/packages.targets +++ /dev/null @@ -1,359 +0,0 @@ - - - - - - prerelease - 1.0.6-$(ToolchainMilestone)-00003 - $(ToolchainVersion) - - Microsoft.DotNet.ILCompiler - - .Debug - Microsoft.DotNet.ILCompiler.SDK$(RuntimeSdkPackageNameSuffix) - - - $([System.Guid]::NewGuid().ToString().Replace('-', '').Substring(0, 8)) - - $(ToolchainPackageName).$(BuildUid) - $(RuntimeSdkPackageName).$(BuildUid) - - lib - lib - a - - 4.4.0-preview1-25218-01 - osx-x64 - $(NuPkgRid) - - 2.1.0-preview1-25324-01 - osx.10.12-x64 - $(NuPkgRid) - - 2.0.0-beta-25021-03 - - 1.0.18-prerelease-00002 - ubuntu.14.04-x64 - $(NuPkgRid) - - - - - - - - - - - - ]]> - - - - - - - ]]> - - - - - - - - - ]]> - - - - - - - - - ]]> - - - - - - - ]]> - - - - - - ]]> - - - - - - - - - - - - - - - - - - - - - - - - - ]]> - - - ]]> - - - - ]]> - - - ]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ]]> - - - - - - - ]]> - - - - - - - - - - ]]> - - - - - 1 - $(ToolchainVersion) - Microsoft .NET Native Toolchain - Provides the toolchain to compile managed code to native. - @(ILCompilerBinPlace -> '%(Text)', '') - - - - - - - - - - ]]> - - - - - 1 - $(RuntimeSdkVersion) - Microsoft .NET Native Runtime SDK - Provides the runtime and link libraries to compile managed code to native. - @(ILCompilerSdkBinPlace -> '%(Text)', '') - ]]> - - - - - - %(Stage) - %(Identity) - %(Version) - toolchain.$(NuPkgRid).%(Identity) - %(Version) - %(Title) - Microsoft - Microsoft - http://go.microsoft.com/fwlink/?LinkId=329770 - https://github.com/dotnet/corert - http://go.microsoft.com/fwlink/?LinkID=288859 - true - %(Description) - Initial release - Copyright ©%3B Microsoft Corporation - - %(Dependencies) - ]]> - - %(Files) - - - - - %(Stage) - $(ProductPackageDir)/%(RedirPackage).runtime.json - - - $(ProductPackageDir)/%(RedirPackage).nuspec - - - - %(RedirPackage) - %(NuSpecPackageMetadata) - - - - - -]]> - - $(ProductPackageDir)/%(PlatformPackage).nuspec - - - - %(PlatformPackage) - %(NuSpecPackageMetadata) - - - %(PlatformPkgFiles) - - -]]> - - - - - - - - - - - - - - - - - - - - - - - $(ProductPackageDir)stage1/ - $(ProductPackageDir)publish1/ - $(Stage1ProjectDir)project.json - - - - - - - - - - - diff --git a/external/corert/src/packaging/project.json b/external/corert/src/packaging/project.json deleted file mode 100644 index b6098fb4b0..0000000000 --- a/external/corert/src/packaging/project.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "dependencies": { - "Microsoft.NETCore.Jit": "2.1.0-preview1-25324-01", - "Microsoft.DotNet.ObjectWriter": "1.0.18-prerelease-00002" - }, - "frameworks": { - "dnxcore50": { } - }, - "runtimes": { - "win7-x64": { }, - "osx.10.10-x64": { }, - "osx.10.12-x64": { }, - "ubuntu.14.04-x64": { }, - "ubuntu.16.04-x64": { } - } -} diff --git a/external/corert/src/packaging/uap/project.json b/external/corert/src/packaging/uap/project.json deleted file mode 100644 index 81a7db8afd..0000000000 --- a/external/corert/src/packaging/uap/project.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "frameworks": { - "uap10.1": { - "dependencies": { - "runtime.win10-x64-aot.Microsoft.Private.CoreFx.UAP": "4.4.0-preview1-25218-01" - } - } - }, - - "runtimes": { - "win10-x64-aot": {}, - } -} diff --git a/external/corert/tests/CoreCLR.issues.targets b/external/corert/tests/CoreCLR.issues.targets index dbfc551f5f..cababdc896 100644 --- a/external/corert/tests/CoreCLR.issues.targets +++ b/external/corert/tests/CoreCLR.issues.targets @@ -10,6 +10,8 @@ + + @@ -36,155 +38,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -192,6 +49,9 @@ + + + @@ -288,20 +148,9 @@ - - - - - - - - - - - @@ -439,6 +288,8 @@ + + @@ -526,6 +377,10 @@ + + + + @@ -568,6 +423,7 @@ + @@ -599,10 +455,6 @@ - - - - @@ -645,10 +497,6 @@ - - - - @@ -723,17 +571,6 @@ - - - - - - - - - - - @@ -809,6 +646,8 @@ + + diff --git a/external/corert/tests/CoreCLR/Test.csproj b/external/corert/tests/CoreCLR/Test.csproj index a7348fe804..5447b57480 100644 --- a/external/corert/tests/CoreCLR/Test.csproj +++ b/external/corert/tests/CoreCLR/Test.csproj @@ -1,4 +1,4 @@ - + $(TestFileName) @@ -12,9 +12,10 @@ - - - + + + + diff --git a/external/corert/tests/CoreCLR/build-and-run-test.cmd b/external/corert/tests/CoreCLR/build-and-run-test.cmd index ea17108992..9b0feacc5a 100644 --- a/external/corert/tests/CoreCLR/build-and-run-test.cmd +++ b/external/corert/tests/CoreCLR/build-and-run-test.cmd @@ -20,13 +20,6 @@ set TestFileName=%~n2 copy /Y %~dp0\Test.csproj %TestFolder% -:: -:: Clear Core_Root before invoking MSBuild. This is important, because we need it set to the CoreCLR -:: test payload's CoreCLR folder while running the test infrastructure, but when msbuild launches -:: the CoreRT CoreCLR to run ILC.exe, Core_Root affects the binder and we probe for assemblies in -:: the wrong place. -set CORE_ROOT= - :: :: The CoreCLR test system configures the VS environment as 32-bit by default, :: so override if we're doing a 64-bit test run @@ -35,8 +28,8 @@ if "%CoreRT_BuildArch%" == "x64" ( call "%VS140COMNTOOLS%\..\..\VC\bin\amd64\vcvars64.bat" ) -echo msbuild /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" "/p:RepoLocalBuild=true" "/p:FrameworkLibPath=%~dp0..\..\bin\Product\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\lib" "/p:FrameworkObjPath=%~dp0..\..\bin\obj\Product\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\Framework" /p:DisableFrameworkLibGeneration=true %TestFolder%\Test.csproj -msbuild /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" "/p:RepoLocalBuild=true" "/p:FrameworkLibPath=%~dp0..\..\bin\Product\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\lib" "/p:FrameworkObjPath=%~dp0..\..\bin\obj\Product\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\Framework" /p:DisableFrameworkLibGeneration=true %TestFolder%\Test.csproj +echo msbuild /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" "/p:RepoLocalBuild=true" "/p:FrameworkLibPath=%~dp0..\..\bin\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\lib" "/p:FrameworkObjPath=%~dp0..\..\bin\obj\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\Framework" /p:DisableFrameworkLibGeneration=true %TestFolder%\Test.csproj +msbuild /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" "/p:RepoLocalBuild=true" "/p:FrameworkLibPath=%~dp0..\..\bin\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\lib" "/p:FrameworkObjPath=%~dp0..\..\bin\obj\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\Framework" /p:DisableFrameworkLibGeneration=true %TestFolder%\Test.csproj if errorlevel 1 ( set TestExitCode=!ERRORLEVEL! goto :Cleanup diff --git a/external/corert/tests/CoreCLR/dependencies.props b/external/corert/tests/CoreCLR/dependencies.props index 1b1591815c..eb179beec7 100644 --- a/external/corert/tests/CoreCLR/dependencies.props +++ b/external/corert/tests/CoreCLR/dependencies.props @@ -1,4 +1,4 @@ - + 9f9f1cf6e6c4a969e8d0158936427b9896300035 diff --git a/external/corert/tests/CoreCLR/runtest/dir.props b/external/corert/tests/CoreCLR/runtest/dir.props index 0953bb6b1b..a472120904 100644 --- a/external/corert/tests/CoreCLR/runtest/dir.props +++ b/external/corert/tests/CoreCLR/runtest/dir.props @@ -1,4 +1,4 @@ - + - - true - - false @@ -29,41 +21,18 @@ $(ProjectDir)..\..\..\packages\ $(ProjectDir)..\Tools\ $(ToolsDir)dotnetcli/ - $(ToolsDir)net46/ true - - - $(PackagesDir)NuGet.exe - $(SourceDir)NuGet.Config - -ConfigFile "$(NuGetConfigFile)" - - "$(NuGetToolPath)" - $(NugetRestoreCommand) install - - $(NugetRestoreCommand) -OutputDirectory "$(PackagesDir.TrimEnd('\\'))" - $(NugetRestoreCommand) $(NuGetConfigCommandLine) - $(NugetRestoreCommand) -Verbosity detailed - mono $(NuGetRestoreCommand) - - - - - - - - - - - - - - + + + + + + diff --git a/external/corert/tests/CoreCLR/runtest/dir.targets b/external/corert/tests/CoreCLR/runtest/dir.targets index f4d8e2b9cd..077610d0f4 100644 --- a/external/corert/tests/CoreCLR/runtest/dir.targets +++ b/external/corert/tests/CoreCLR/runtest/dir.targets @@ -1,5 +1,4 @@ - - + diff --git a/external/corert/tests/CoreCLR/runtest/publishdependency.targets b/external/corert/tests/CoreCLR/runtest/publishdependency.targets index c5269de18b..4aee8d8fd2 100644 --- a/external/corert/tests/CoreCLR/runtest/publishdependency.targets +++ b/external/corert/tests/CoreCLR/runtest/publishdependency.targets @@ -1,5 +1,4 @@ - - + diff --git a/external/corert/tests/CoreCLR/runtest/runtest.cmd b/external/corert/tests/CoreCLR/runtest/runtest.cmd index fafbe74826..928f6057c7 100644 --- a/external/corert/tests/CoreCLR/runtest/runtest.cmd +++ b/external/corert/tests/CoreCLR/runtest/runtest.cmd @@ -7,10 +7,8 @@ set __BuildType=Debug set __BuildOS=Windows_NT :: Default to highest Visual Studio version available -set __VSVersion=vs2015 - -if defined VS120COMNTOOLS set __VSVersion=vs2013 -if defined VS140COMNTOOLS set __VSVersion=vs2015 +set __VSVersion=vs2017 +set __VSProductVersion=150 :: Define a prefix for most output progress messages that come from this script. That makes :: it easier to see where these are coming from. Note that there is a trailing space here. @@ -48,9 +46,6 @@ if /i "%1" == "debug" (set __BuildType=Debug&shift&goto Arg_Loop if /i "%1" == "release" (set __BuildType=Release&shift&goto Arg_Loop) if /i "%1" == "checked" (set __BuildType=Checked&shift&goto Arg_Loop) -if /i "%1" == "vs2013" (set __VSVersion=%1&shift&goto Arg_Loop) -if /i "%1" == "vs2015" (set __VSVersion=%1&shift&goto Arg_Loop) - if /i "%1" == "SkipWrapperGeneration" (set __SkipWrapperGeneration=true&shift&goto Arg_Loop) if /i "%1" == "Exclude" (set __Exclude=%2&shift&shift&goto Arg_Loop) if /i "%1" == "Exclude0" (set __Exclude0=%2&shift&shift&goto Arg_Loop) @@ -96,31 +91,15 @@ if not defined XunitTestReportDirBase set XunitTestReportDirBase=%XunitTestBinB if not exist %__LogsDir% md %__LogsDir% -set __VSProductVersion= -if /i "%__VSVersion%" == "vs2013" set __VSProductVersion=120 -if /i "%__VSVersion%" == "vs2015" set __VSProductVersion=140 - :: Check presence of VS if not defined VS%__VSProductVersion%COMNTOOLS goto NoVS set __VSToolsRoot=!VS%__VSProductVersion%COMNTOOLS! if %__VSToolsRoot:~-1%==\ set "__VSToolsRoot=%__VSToolsRoot:~0,-1%" -:: Does VS really exist? -if not exist "%__VSToolsRoot%\..\IDE\devenv.exe" goto NoVS -if not exist "%__VSToolsRoot%\..\..\VC\vcvarsall.bat" goto NoVS -if not exist "%__VSToolsRoot%\VsDevCmd.bat" goto NoVS +set _msbuildexe="%VSINSTALLDIR%\MSBuild\15.0\Bin\MSBuild.exe" -if /i "%__VSVersion%" =="vs2015" goto MSBuild14 -set _msbuildexe="%ProgramFiles(x86)%\MSBuild\12.0\Bin\MSBuild.exe" -if not exist %_msbuildexe% set _msbuildexe="%ProgramFiles%\MSBuild\12.0\Bin\MSBuild.exe" -if not exist %_msbuildexe% set _msbuildexe="%ProgramFiles(x86)%\MSBuild\14.0\Bin\MSBuild.exe" -goto :CheckMSBuild14 -:MSBuild14 -set _msbuildexe="%ProgramFiles(x86)%\MSBuild\14.0\Bin\MSBuild.exe" -:CheckMSBuild14 -if not exist %_msbuildexe% set _msbuildexe="%ProgramFiles%\MSBuild\14.0\Bin\MSBuild.exe" -if not exist %_msbuildexe% echo Error: Could not find MSBuild.exe. Please see https://github.com/dotnet/coreclr/blob/master/Documentation/project-docs/developer-guide.md for build instructions. && exit /b 1 +if not exist !_msbuildexe! (echo Error: Could not find MSBuild.exe. Please see https://github.com/dotnet/corert/blob/master/Documentation/prerequisites-for-building.md for build instructions. && exit /b 1) :: Set the environment for the build- VS cmd prompt echo %__MsgPrefix%Using environment: "%__VSToolsRoot%\VsDevCmd.bat" @@ -192,11 +171,6 @@ if defined __GenerateLayoutOnly ( exit /b 1 ) -if not exist %CORE_ROOT%\coreclr.dll ( - echo %__MsgPrefix%Error: Ensure you have done a successful build of the Product and %CORE_ROOT% contains runtime binaries. - exit /b 1 -) - ::Check if the test Binaries are built if not exist %XunitTestBinBase% ( echo %__MsgPrefix%Error: Ensure the Test Binaries are built and are present at %XunitTestBinBase%. @@ -378,6 +352,6 @@ echo CORE_ROOT The path to the runtime exit /b 1 :NoVS -echo Visual Studio 2013+ ^(Community is free^) is a prerequisite to build this repository. +echo Visual Studio 2017 ^(Community is free^) is a prerequisite to build this repository. echo See: https://github.com/dotnet/coreclr/blob/master/Documentation/project-docs/developer-guide.md#prerequisites exit /b 1 diff --git a/external/corert/tests/CoreCLR/runtest/runtest.proj b/external/corert/tests/CoreCLR/runtest/runtest.proj index f73824537a..2a9902d827 100644 --- a/external/corert/tests/CoreCLR/runtest/runtest.proj +++ b/external/corert/tests/CoreCLR/runtest/runtest.proj @@ -1,5 +1,4 @@ - - + @@ -66,30 +65,23 @@ $(_XunitEpilog) - + Debug AnyCPU $(XunitWrapper) - 2.0 - {95DFC527-4DC1-495E-97D7-E94EE1F7140D} Library .NETFramework v4.5 + net45 true true - Properties - 512 - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} ..\..\ - 7a9bfb7d BuildOnly true - %24(TestWrappersPackagesConfigFileDirectory)project.json - %24(TestWrappersPackagesConfigFileDirectory)project.lock.json + %24(IntermediateOutputRootPath)\XUnitTooling $(ReferencePath);$(XunitTestBinBase)\Common\Desktop.Coreclr.TestWrapper\Desktop.Coreclr.TestWrapper @@ -110,13 +102,13 @@ $(_XunitEpilog) - - %24(TestWrappersPackagesConfigFileDirectory)project.json - %24(TestWrappersPackagesConfigFileDirectory)project.lock.json - - $(XunitTestBinBase)\$(Category)\ + $(XunitTestBinBase)\$(Category)\ + + + <_TargetFrameworkDirectories /> + <_FullFrameworkReferenceAssemblyPaths /> ]]> diff --git a/external/corert/tests/CoreCLR/runtest/runtest.sh b/external/corert/tests/CoreCLR/runtest/runtest.sh index d5f672417d..1ea3d52b62 100755 --- a/external/corert/tests/CoreCLR/runtest/runtest.sh +++ b/external/corert/tests/CoreCLR/runtest/runtest.sh @@ -209,8 +209,7 @@ function xunit_output_end { ((errorCount = 1)) fi - echo '' >>"$xunitOutputPath" - echo '' >>"$xunitOutputPath" + echo '' >"$xunitOutputPath" local line @@ -559,7 +558,7 @@ function set_up_core_dump_generation { # Include memory in private and shared file-backed mappings in the dump. # This ensures that we can see disassembly from our shared libraries when # inspecting the contents of the dump. See 'man core' for details. - echo 0x3F > /proc/self/coredump_filter + echo -n 0x3F > /proc/self/coredump_filter fi fi } diff --git a/external/corert/tests/CoreCLR/runtest/src/Common/Coreclr.TestWrapper/Coreclr.TestWrapper.csproj b/external/corert/tests/CoreCLR/runtest/src/Common/Coreclr.TestWrapper/Coreclr.TestWrapper.csproj index eb9a4142a8..3af4f3cf0e 100644 --- a/external/corert/tests/CoreCLR/runtest/src/Common/Coreclr.TestWrapper/Coreclr.TestWrapper.csproj +++ b/external/corert/tests/CoreCLR/runtest/src/Common/Coreclr.TestWrapper/Coreclr.TestWrapper.csproj @@ -1,5 +1,4 @@ - - + Debug diff --git a/external/corert/tests/CoreCLR/runtest/src/TestWrappersConfig/XUnitTooling.depproj b/external/corert/tests/CoreCLR/runtest/src/TestWrappersConfig/XUnitTooling.depproj index 525833ecd5..701486648e 100644 --- a/external/corert/tests/CoreCLR/runtest/src/TestWrappersConfig/XUnitTooling.depproj +++ b/external/corert/tests/CoreCLR/runtest/src/TestWrappersConfig/XUnitTooling.depproj @@ -1,29 +1,23 @@ - - - + - 4.0.0.0 - Library - $(AotPackageReferencePath) - true - true - project.json - project.lock.json + net45 + 2.2.0-beta2-build3300 - + + $(XunitPackageVersion) + + + $(XunitPackageVersion) + + + $(XunitPackageVersion) + + + $(XunitPackageVersion) + - - - .NETCore,Version=v5.0 - win8-aot - - \ No newline at end of file + diff --git a/external/corert/tests/CoreCLR/runtest/src/TestWrappersConfig/project.json b/external/corert/tests/CoreCLR/runtest/src/TestWrappersConfig/project.json deleted file mode 100644 index b9534119a9..0000000000 --- a/external/corert/tests/CoreCLR/runtest/src/TestWrappersConfig/project.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "dependencies": { - "xunit": "2.2.0-beta2-build3300", - "xunit.assert": "2.2.0-beta2-build3300", - "xunit.core": "2.2.0-beta2-build3300", - "xunit.runner.msbuild": "2.2.0-beta2-build3300" - }, - "frameworks": { - "netcoreapp1.0": { - "imports": [ - "dnxcore50", - "portable-net45+win8" - ] - }, - "net45": { - "imports": "portable-net45+win8" - } - }, - "runtimes": { - "win7-x86": {}, - "win7-x64": {}, - "ubuntu.14.04-x64": {}, - "osx.10.10-x64": {}, - "centos.7-x64": {}, - "rhel.7-x64": {}, - "debian.8-x64": {} - } -} diff --git a/external/corert/tests/CoreCLR/runtest/src/dir.common.props b/external/corert/tests/CoreCLR/runtest/src/dir.common.props index d5de5794d0..8d13477a1b 100644 --- a/external/corert/tests/CoreCLR/runtest/src/dir.common.props +++ b/external/corert/tests/CoreCLR/runtest/src/dir.common.props @@ -1,4 +1,4 @@ - + diff --git a/external/corert/tests/CoreCLR/runtest/src/dir.props b/external/corert/tests/CoreCLR/runtest/src/dir.props index 454db91dc4..5a67e6a839 100644 --- a/external/corert/tests/CoreCLR/runtest/src/dir.props +++ b/external/corert/tests/CoreCLR/runtest/src/dir.props @@ -1,4 +1,4 @@ - + @@ -11,8 +11,6 @@ 78,162,164,168,169,219,251,252,414,429,642,649,652,675,1691,1717,1718,3001,3002,3003,3005,3008 false true - - $(MSBuildThisFileDirectory)TestWrappersConfig\ @@ -81,9 +79,4 @@ 2 - - - $(SourceDir)Common\test_dependencies\project.json - $(SourceDir)Common\test_dependencies\project.lock.json - diff --git a/external/corert/tests/CoreCLR/runtest/src/dir.targets b/external/corert/tests/CoreCLR/runtest/src/dir.targets index 0d27268079..a767ae9ae2 100644 --- a/external/corert/tests/CoreCLR/runtest/src/dir.targets +++ b/external/corert/tests/CoreCLR/runtest/src/dir.targets @@ -1,4 +1,4 @@ - + SharedLibrary @@ -50,11 +50,6 @@ <_CLRTestNeedsProjectToRun Condition=" '$(_CLRTestNeedsToRun)' and '!$(_CLRTestBuildsExecutable)' ">true - - $(SourceDir)Common/empty/project.json - $(SourceDir)Common/empty/project.lock.json - - @@ -63,11 +58,7 @@ false - - - true - - + diff --git a/external/corert/tests/CoreCLR/runtest/tests.targets b/external/corert/tests/CoreCLR/runtest/tests.targets index d7a890f5e3..180cdc68fd 100644 --- a/external/corert/tests/CoreCLR/runtest/tests.targets +++ b/external/corert/tests/CoreCLR/runtest/tests.targets @@ -1,5 +1,4 @@ - - + diff --git a/external/corert/tests/CoreCLR/runtest/xunitwrapper.targets b/external/corert/tests/CoreCLR/runtest/xunitwrapper.targets index 3b343bd891..655fc04292 100644 --- a/external/corert/tests/CoreCLR/runtest/xunitwrapper.targets +++ b/external/corert/tests/CoreCLR/runtest/xunitwrapper.targets @@ -1,4 +1,3 @@ - @@ -6,13 +5,13 @@ - diff --git a/external/corert/tests/Test.Common.targets b/external/corert/tests/Test.Common.targets deleted file mode 100644 index 9bdc4440ca..0000000000 --- a/external/corert/tests/Test.Common.targets +++ /dev/null @@ -1,13 +0,0 @@ - - - - false - false - true - .NETStandard,Version=v1.6 - - - - - - \ No newline at end of file diff --git a/external/corert/tests/coredump_handling.sh b/external/corert/tests/coredump_handling.sh index e5814009fa..edf5e10bdc 100644 --- a/external/corert/tests/coredump_handling.sh +++ b/external/corert/tests/coredump_handling.sh @@ -24,7 +24,7 @@ function set_up_core_dump_generation { # Include memory in private and shared file-backed mappings in the dump. # This ensures that we can see disassembly from our shared libraries when # inspecting the contents of the dump. See 'man core' for details. - echo 0x3F > /proc/self/coredump_filter + echo -n 0x3F > /proc/self/coredump_filter fi fi } diff --git a/external/corert/tests/runtest.cmd b/external/corert/tests/runtest.cmd index 762b24bcfa..0c23b18a74 100644 --- a/external/corert/tests/runtest.cmd +++ b/external/corert/tests/runtest.cmd @@ -1,4 +1,4 @@ -@echo off +@if not defined _echo @echo off setlocal EnableDelayedExpansion set ThisScript=%0 @@ -14,19 +14,13 @@ set CoreRT_CoreCLRTargetsFile= set CoreRT_TestLogFileName=testresults.xml set CoreRT_TestName=* -:: Default to highest Visual Studio version available -set CoreRT_VSVersion=vs2015 -if defined VS150COMNTOOLS set CoreRT_VSVersion=vs2017 - :ArgLoop if "%1" == "" goto :ArgsDone if /i "%1" == "/?" goto :Usage if /i "%1" == "x64" (set CoreRT_BuildArch=x64&&shift&goto ArgLoop) if /i "%1" == "x86" (set CoreRT_BuildArch=x86&&shift&goto ArgLoop) if /i "%1" == "arm" (set CoreRT_BuildArch=arm&&shift&goto ArgLoop) - -if /i "%1" == "vs2017" (set CoreRT_VSVersion=vs2017&shift&goto Arg_Loop) -if /i "%1" == "vs2015" (set CoreRT_VSVersion=vs2015&shift&goto Arg_Loop) +if /i "%1" == "wasm" (set CoreRT_BuildArch=wasm&&set CoreRT_BuildOS=WebAssembly&&set CoreRT_TestCompileMode=wasm&&shift&goto ArgLoop) if /i "%1" == "debug" (set CoreRT_BuildType=Debug&shift&goto ArgLoop) if /i "%1" == "release" (set CoreRT_BuildType=Release&shift&goto ArgLoop) @@ -60,21 +54,24 @@ if /i "%1" == "/test" (set CoreRT_TestName=%2&shift&shift&goto ArgLoop) if /i "%1" == "/runtest" (set CoreRT_TestRun=%2&shift&shift&goto ArgLoop) if /i "%1" == "/dotnetclipath" (set CoreRT_CliDir=%2&shift&shift&goto ArgLoop) if /i "%1" == "/multimodule" (set CoreRT_MultiFileConfiguration=MultiModule&shift&goto ArgLoop) +if /i "%1" == "/determinism" (set CoreRT_DeterminismMode=true&shift&goto ArgLoop) echo Invalid command line argument: %1 goto :Usage :Usage echo %ThisScript% [arch] [flavor] [/mode] [/runtest] [/coreclr ^] -echo arch : x64 / x86 / arm +echo arch : x64 / x86 / arm / wasm echo flavor : debug / release -echo /mode : Optionally restrict to a single code generator. Specify cpp/ryujit. Default: both +echo /mode : Optionally restrict to a single code generator. Specify cpp/ryujit/wasm. Default: all echo /test : Run a single test by folder name (ie, BasicThreading) echo /runtest : Should just compile or run compiled binary? Specify: true/false. Default: true. echo /coreclr : Download and run the CoreCLR repo tests echo /coreclrsingletest ^ echo : Run a single CoreCLR repo test echo /multimodule : Compile the framework as a .lib and link tests against it (only supports ryujit) +echo /determinism : Compile the test twice with randomized dependency node mark stack to validate +echo compiler determinism in multi-threaded compilation. echo. echo --- CoreCLR Subset --- echo Top200 : Runs broad coverage / CI validation (~200 tests). @@ -85,6 +82,9 @@ exit /b 2 :ArgsDone +set CoreRT_HostArch=%CoreRT_BuildArch% +if /i "%CoreRT_BuildArch%"=="wasm" (set CoreRT_HostArch=x64) + if /i "%CoreRT_TestCompileMode%"=="jit" ( set CoreRT_TestCompileMode=ryujit ) @@ -110,13 +110,17 @@ if NOT "%CoreRT_MultiFileConfiguration%" == "" ( set __LogDir=%CoreRT_TestRoot%\..\bin\Logs\%__BuildStr%\tests -:: VS2017 changed the location of vcvarsall.bat. -if /i "%__VSVersion%" == "vs2017" ( - call "!VS150COMNTOOLS!\..\..\VC\Auxiliary\Build\vcvarsall.bat" %CoreRT_BuildArch% -) else ( - call "!VS140COMNTOOLS!\..\..\VC\vcvarsall.bat" %CoreRT_BuildArch% +if defined VisualStudioVersion goto :RunVCVars + +set _VSWHERE="%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" +if exist %_VSWHERE% ( + for /f "usebackq tokens=*" %%i in (`%_VSWHERE% -latest -prerelease -property installationPath`) do set _VSCOMNTOOLS=%%i\Common7\Tools ) +call "%_VSCOMNTOOLS%\VsDevCmd.bat" +:RunVCVars + +call "!VS150COMNTOOLS!\..\..\VC\Auxiliary\Build\vcvarsall.bat" %CoreRT_HostArch% if "%CoreRT_RunCoreCLRTests%"=="true" goto :TestExtRepo if /i "%__BuildType%"=="Debug" ( @@ -125,12 +129,15 @@ if /i "%__BuildType%"=="Debug" ( set __LinkLibs=msvcrt.lib ) +if not exist "!__CoreRTTestBinDir!" (mkdir !__CoreRTTestBinDir!) echo. > %__CoreRTTestBinDir%\testResults.tmp set /a __CppTotalTests=0 set /a __CppPassedTests=0 set /a __JitTotalTests=0 set /a __JitPassedTests=0 +set /a __WasmTotalTests=0 +set /a __WasmPassedTests=0 for /f "delims=" %%a in ('dir /s /aD /b %CoreRT_TestRoot%\src\%CoreRT_TestName%') do ( set __SourceFolder=%%a set __SourceFileName=%%~na @@ -144,27 +151,40 @@ for /f "delims=" %%a in ('dir /s /aD /b %CoreRT_TestRoot%\src\%CoreRT_TestName%' ) if NOT "!__SourceFileProj!" == "" ( if /i not "%CoreRT_TestCompileMode%" == "cpp" ( - set __Mode=Jit - call :CompileFile !__SourceFolder! !__SourceFileName! !__SourceFileProj! %__LogDir%\!__RelativePath! - set /a __JitTotalTests=!__JitTotalTests!+1 + if /i not "%CoreRT_TestCompileMode%" == "wasm" ( + if not exist "!__SourceFolder!\no_ryujit" ( + set __Mode=Jit + call :CompileFile !__SourceFolder! !__SourceFileName! !__SourceFileProj! %__LogDir%\!__RelativePath! + set /a __JitTotalTests=!__JitTotalTests!+1 + ) + ) ) if /i not "%CoreRT_TestCompileMode%" == "ryujit" ( - if not exist "!__SourceFolder!\no_cpp" ( - set __Mode=Cpp - call :CompileFile !__SourceFolder! !__SourceFileName! !__SourceFileProj! %__LogDir%\!__RelativePath! - set /a __CppTotalTests=!__CppTotalTests!+1 + if /i not "%CoreRT_TestCompileMode%" == "wasm" ( + if not exist "!__SourceFolder!\no_cpp" ( + set __Mode=Cpp + call :CompileFile !__SourceFolder! !__SourceFileName! !__SourceFileProj! %__LogDir%\!__RelativePath! + set /a __CppTotalTests=!__CppTotalTests!+1 + ) + ) + if /i not "%CoreRT_TestCompileMode%" == "cpp" ( + if exist "!__SourceFolder!\wasm" ( + set __Mode=wasm + call :CompileFile !__SourceFolder! !__SourceFileName! !__SourceFileProj! %__LogDir%\!__RelativePath! + set /a __WasmTotalTests=!__WasmTotalTests!+1 + ) ) ) ) ) set /a __CppFailedTests=%__CppTotalTests%-%__CppPassedTests% set /a __JitFailedTests=%__JitTotalTests%-%__JitPassedTests% -set /a __TotalTests=%__JitTotalTests%+%__CppTotalTests% -set /a __PassedTests=%__JitPassedTests%+%__CppPassedTests% -set /a __FailedTests=%__JitFailedTests%+%__CppFailedTests% +set /a __WasmFailedTests=%__WasmTotalTests%-%__WasmPassedTests% +set /a __TotalTests=%__JitTotalTests%+%__CppTotalTests%+%__WasmTotalTests% +set /a __PassedTests=%__JitPassedTests%+%__CppPassedTests%+%__WasmPassedTests% +set /a __FailedTests=%__JitFailedTests%+%__CppFailedTests%+%__WasmFailedTests% -echo ^ > %__CoreRTTestBinDir%\%CoreRT_TestLogFileName% -echo ^ >> %__CoreRTTestBinDir%\%CoreRT_TestLogFileName% +echo ^ > %__CoreRTTestBinDir%\%CoreRT_TestLogFileName% echo ^ >> %__CoreRTTestBinDir%\%CoreRT_TestLogFileName% echo ^ >> %__CoreRTTestBinDir%\%CoreRT_TestLogFileName% type %__CoreRTTestBinDir%\testResults.tmp >> %__CoreRTTestBinDir%\%CoreRT_TestLogFileName% @@ -175,6 +195,7 @@ echo ^ >> %__CoreRTTestBinDir%\%CoreRT_TestLogFileName% echo. set __JitStatusPassed=1 set __CppStatusPassed=1 +set __WasmStatusPassed=1 if /i not "%CoreRT_TestCompileMode%" == "cpp" ( set __JitStatusPassed=0 @@ -190,8 +211,17 @@ if /i not "%CoreRT_TestCompileMode%" == "ryujit" ( call :PassFail !__CppStatusPassed! "CPP - TOTAL: %__CppTotalTests% PASSED: %__CppPassedTests%" ) + +if /i not "%CoreRT_TestCompileMode%" == "ryujit" ( + set __WasmStatusPassed=0 + if %__WasmTotalTests% EQU %__WasmPassedTests% (set __WasmStatusPassed=1) + if %__WasmTotalTests% EQU 0 (set __WasmStatusPassed=1) + call :PassFail !__WasmStatusPassed! "WASM - TOTAL: %__WasmTotalTests% PASSED: %__WasmPassedTests%" +) + if not !__JitStatusPassed! EQU 1 (exit /b 1) if not !__CppStatusPassed! EQU 1 (exit /b 1) +if not !__WasmStatusPassed! EQU 1 (exit /b 1) exit /b 0 :PassFail @@ -227,29 +257,62 @@ goto :eof if /i "%CoreRT_BuildType%" == "debug" ( set extraArgs=!extraArgs! /p:UseDebugCrt=true ) + ) else if /i "%__Mode%" == "wasm" ( + set extraArgs=!extraArgs! /p:NativeCodeGen=wasm ) else ( if "%CoreRT_MultiFileConfiguration%" == "MultiModule" ( set extraArgs=!extraArgs! "/p:IlcMultiModule=true" ) ) - echo msbuild /m /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" "/p:Platform=%CoreRT_BuildArch%" "/p:RepoLocalBuild=true" "/p:FrameworkLibPath=%~dp0..\bin\Product\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\lib" "/p:FrameworkObjPath=%~dp0..\bin\obj\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\Framework" !extraArgs! !__SourceFileProj! - echo. - msbuild /m /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" "/p:Platform=%CoreRT_BuildArch%" "/p:RepoLocalBuild=true" "/p:FrameworkLibPath=%~dp0..\bin\Product\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\lib" "/p:FrameworkObjPath=%~dp0..\bin\obj\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\Framework" !extraArgs! !__SourceFileProj! - endlocal + if "%CoreRT_DeterminismMode%"=="true" ( + set /a CoreRT_DeterminismSeed=%RANDOM%*32768+%RANDOM% + echo Running determinism baseline scenario with seed !CoreRT_DeterminismSeed! - set __SavedErrorLevel=%ErrorLevel% - if "%CoreRT_TestRun%"=="false" (goto :SkipTestRun) - - if "%__SavedErrorLevel%"=="0" ( + echo msbuild /m /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" "/p:OSGroup=%CoreRT_BuildOS%" "/p:Platform=%CoreRT_BuildArch%" "/p:RepoLocalBuild=true" "/p:FrameworkLibPath=%~dp0..\bin\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\lib" "/p:FrameworkObjPath=%~dp0..\bin\obj\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\Framework" "/p:IlcGenerateMapFile=true" !extraArgs! !__SourceFileProj! echo. - echo Running test !__SourceFileName! - call !__SourceFile!.cmd !__SourceFolder!\bin\%CoreRT_BuildType%\%CoreRT_BuildArch%\native !__SourceFileName!.exe + msbuild /m /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" "/p:OSGroup=%CoreRT_BuildOS%" "/p:Platform=%CoreRT_BuildArch%" "/p:RepoLocalBuild=true" "/p:FrameworkLibPath=%~dp0..\bin\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\lib" "/p:FrameworkObjPath=%~dp0..\bin\obj\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\Framework" "/p:IlcGenerateMapFile=true" !extraArgs! !__SourceFileProj! + set __SavedErrorLevel=!ErrorLevel! + if not "!__SavedErrorLevel!"=="0" (goto :SkipTestRun) + + REM Back up the map file and delete the obj file so the MSBuild targets won't skip ILC target + rename !__SourceFolder!\obj\%CoreRT_BuildType%\%CoreRT_BuildArch%\native\!__SourceFileName!.map.xml !__SourceFileName!.baseline.map.xml + del !__SourceFolder!\obj\%CoreRT_BuildType%\%CoreRT_BuildArch%\native\!__SourceFileName!.obj + + set /a CoreRT_DeterminismSeed=%RANDOM%*32768+%RANDOM% + + echo Running determinism comparison scenario with seed !CoreRT_DeterminismSeed! + echo msbuild /m /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" "/p:OSGroup=%CoreRT_BuildOS%" "/p:Platform=%CoreRT_BuildArch%" "/p:RepoLocalBuild=true" "/p:FrameworkLibPath=%~dp0..\bin\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\lib" "/p:FrameworkObjPath=%~dp0..\bin\obj\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\Framework" "/p:IlcGenerateMapFile=true" !extraArgs! !__SourceFileProj! + echo. + msbuild /m /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" "/p:OSGroup=%CoreRT_BuildOS%" "/p:Platform=%CoreRT_BuildArch%" "/p:RepoLocalBuild=true" "/p:FrameworkLibPath=%~dp0..\bin\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\lib" "/p:FrameworkObjPath=%~dp0..\bin\obj\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\Framework" "/p:IlcGenerateMapFile=true" !extraArgs! !__SourceFileProj! + endlocal + set __SavedErrorLevel=!ErrorLevel! + if not "!__SavedErrorLevel!"=="0" (goto :SkipTestRun) + + fc !__SourceFolder!\obj\%CoreRT_BuildType%\%CoreRT_BuildArch%\native\!__SourceFileName!.baseline.map.xml !__SourceFolder!\obj\%CoreRT_BuildType%\%CoreRT_BuildArch%\native\!__SourceFileName!.map.xml + set __SavedErrorLevel=!ErrorLevel! + + ) else ( + echo msbuild /m /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" "/p:OSGroup=%CoreRT_BuildOS%" "/p:Platform=%CoreRT_BuildArch%" "/p:RepoLocalBuild=true" "/p:FrameworkLibPath=%~dp0..\bin\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\lib" "/p:FrameworkObjPath=%~dp0..\bin\obj\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\Framework" !extraArgs! !__SourceFileProj! + echo. + msbuild /m /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" "/p:OSGroup=%CoreRT_BuildOS%" "/p:Platform=%CoreRT_BuildArch%" "/p:RepoLocalBuild=true" "/p:FrameworkLibPath=%~dp0..\bin\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\lib" "/p:FrameworkObjPath=%~dp0..\bin\obj\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\Framework" !extraArgs! !__SourceFileProj! + endlocal + + set __SavedErrorLevel=!ErrorLevel! + if "%CoreRT_TestRun%"=="false" (goto :SkipTestRun) + if "%__Mode%" == "wasm" (goto :SkipTestRun) + + if "%__SavedErrorLevel%"=="0" ( + echo. + echo Running test !__SourceFileName! + call !__SourceFile!.cmd !__SourceFolder!\bin\%CoreRT_BuildType%\%CoreRT_BuildArch%\native !__SourceFileName!.exe + set __SavedErrorLevel=!ErrorLevel! + ) ) :SkipTestRun - if "%__SavedErrorLevel%"=="0" ( + if "!__SavedErrorLevel!"=="0" ( set /a __%__Mode%PassedTests=!__%__Mode%PassedTests!+1 echo ^ >> %__CoreRTTestBinDir%\testResults.tmp ) ELSE ( @@ -288,7 +351,7 @@ goto :eof set INIT_TESTS_LOG=%~dp0..\init-tests.log echo Restoring tests (this may take a few minutes).. echo Installing '%TESTS_REMOTE_URL%' to '%TESTS_LOCAL_ZIP%' >> "%INIT_TESTS_LOG%" - powershell -NoProfile -ExecutionPolicy unrestricted -Command "$retryCount = 0; $success = $false; do { try { (New-Object Net.WebClient).DownloadFile('%TESTS_REMOTE_URL%', '%TESTS_LOCAL_ZIP%'); $success = $true; } catch { if ($retryCount -ge 6) { throw; } else { $retryCount++; Start-Sleep -Seconds (5 * $retryCount); } } } while ($success -eq $false); Add-Type -Assembly 'System.IO.Compression.FileSystem' -ErrorVariable AddTypeErrors; if ($AddTypeErrors.Count -eq 0) { [System.IO.Compression.ZipFile]::ExtractToDirectory('%TESTS_LOCAL_ZIP%', '%CoreRT_TestExtRepo%') } else { (New-Object -com shell.application).namespace('%CoreRT_TestExtRepo%').CopyHere((new-object -com shell.application).namespace('%TESTS_LOCAL_ZIP%').Items(),16) }" >> "%INIT_TESTS_LOG%" + powershell -NoProfile -ExecutionPolicy unrestricted -Command "$retryCount = 0; $success = $false; do { try { [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12; (New-Object Net.WebClient).DownloadFile('%TESTS_REMOTE_URL%', '%TESTS_LOCAL_ZIP%'); $success = $true; } catch { if ($retryCount -ge 6) { throw; } else { $retryCount++; Start-Sleep -Seconds (5 * $retryCount); } } } while ($success -eq $false); Add-Type -Assembly 'System.IO.Compression.FileSystem' -ErrorVariable AddTypeErrors; if ($AddTypeErrors.Count -eq 0) { [System.IO.Compression.ZipFile]::ExtractToDirectory('%TESTS_LOCAL_ZIP%', '%CoreRT_TestExtRepo%') } else { (New-Object -com shell.application).namespace('%CoreRT_TestExtRepo%').CopyHere((new-object -com shell.application).namespace('%TESTS_LOCAL_ZIP%').Items(),16) }" >> "%INIT_TESTS_LOG%" if errorlevel 1 ( echo ERROR: Could not download CoreCLR tests correctly. See '%INIT_TESTS_LOG%' for more details. 1>&2 exit /b 1 @@ -320,18 +383,16 @@ goto :eof set IlcMultiModule=true REM Pre-compile shared framework assembly echo Compiling framework library - echo msbuild /m /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" "/p:Platform=%CoreRT_BuildArch%" "/p:RepoLocalBuild=true" "/p:FrameworkLibPath=%CoreRT_TestRoot%..\bin\Product\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\lib" "/p:FrameworkObjPath=%~dp0..\bin\obj\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\Framework" /t:CreateLib %CoreRT_TestRoot%\..\src\BuildIntegration\BuildFrameworkNativeObjects.proj - msbuild /m /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" "/p:Platform=%CoreRT_BuildArch%" "/p:RepoLocalBuild=true" "/p:FrameworkLibPath=%CoreRT_TestRoot%..\bin\Product\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\lib" "/p:FrameworkObjPath=%~dp0..\bin\obj\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\Framework" /t:CreateLib %CoreRT_TestRoot%\..\src\BuildIntegration\BuildFrameworkNativeObjects.proj + echo msbuild /m /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" "/p:OSGroup=%CoreRT_BuildOS%" "/p:Platform=%CoreRT_BuildArch%" "/p:RepoLocalBuild=true" "/p:FrameworkLibPath=%CoreRT_TestRoot%..\bin\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\lib" "/p:FrameworkObjPath=%~dp0..\bin\obj\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\Framework" /t:CreateLib %CoreRT_TestRoot%\..\src\BuildIntegration\BuildFrameworkNativeObjects.proj + msbuild /m /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" "/p:OSGroup=%CoreRT_BuildOS%" "/p:Platform=%CoreRT_BuildArch%" "/p:RepoLocalBuild=true" "/p:FrameworkLibPath=%CoreRT_TestRoot%..\bin\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\lib" "/p:FrameworkObjPath=%~dp0..\bin\obj\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\Framework" /t:CreateLib %CoreRT_TestRoot%\..\src\BuildIntegration\BuildFrameworkNativeObjects.proj ) echo. set CLRCustomTestLauncher=%CoreRT_TestRoot%\CoreCLR\build-and-run-test.cmd set XunitTestBinBase=!CoreRT_TestExtRepo! - set CORE_ROOT=%CoreRT_TestRoot%\..\Tools\dotnetcli\shared\Microsoft.NETCore.App\1.0.0 - echo CORE_ROOT IS NOW %CORE_ROOT% pushd %CoreRT_TestRoot%\CoreCLR\runtest - - msbuild "/p:RepoLocalBuild=true" src\TestWrappersConfig\XUnitTooling.depproj + + "%CoreRT_CliDir%\dotnet.exe" msbuild /t:Restore /p:RepoLocalBuild=true src\TestWrappersConfig\XUnitTooling.depproj if errorlevel 1 ( exit /b 1 ) diff --git a/external/corert/tests/runtest.sh b/external/corert/tests/runtest.sh index 94942284d4..0144371de4 100755 --- a/external/corert/tests/runtest.sh +++ b/external/corert/tests/runtest.sh @@ -55,8 +55,8 @@ run_test_dir() local __msbuild_dir=${CoreRT_TestRoot}/../Tools - echo ${__msbuild_dir}/dotnetcli/dotnet ${__msbuild_dir}/MSBuild.dll /ds /m /p:IlcPath=${CoreRT_ToolchainDir} /p:Configuration=${CoreRT_BuildType} /p:Platform=${CoreRT_BuildArch} /p:RepoLocalBuild=true "/p:FrameworkLibPath=${CoreRT_TestRoot}/../bin/Product/${CoreRT_BuildOS}.${CoreRT_BuildArch}.${CoreRT_BuildType}/lib" "/p:FrameworkObjPath=${CoreRT_TestRoot}/../bin/obj/${CoreRT_BuildOS}.${CoreRT_BuildArch}.${CoreRT_BuildType}/Framework" ${__extra_args} "${__extra_cxxflags}" "${__extra_linkflags}" ${__dir_path}/${__filename}.csproj - ${__msbuild_dir}/dotnetcli/dotnet ${__msbuild_dir}/MSBuild.dll /ds /m /p:IlcPath=${CoreRT_ToolchainDir} /p:Configuration=${CoreRT_BuildType} /p:Platform=${CoreRT_BuildArch} /p:RepoLocalBuild=true "/p:FrameworkLibPath=${CoreRT_TestRoot}/../bin/Product/${CoreRT_BuildOS}.${CoreRT_BuildArch}.${CoreRT_BuildType}/lib" "/p:FrameworkObjPath=${CoreRT_TestRoot}/../bin/obj/${CoreRT_BuildOS}.${CoreRT_BuildArch}.${CoreRT_BuildType}/Framework" ${__extra_args} "${__extra_cxxflags}" "${__extra_linkflags}" ${__dir_path}/${__filename}.csproj + echo ${__msbuild_dir}/dotnetcli/dotnet ${__msbuild_dir}/MSBuild.dll /ds /m /p:IlcPath=${CoreRT_ToolchainDir} /p:Configuration=${CoreRT_BuildType} /p:Platform=${CoreRT_BuildArch} /p:RepoLocalBuild=true "/p:FrameworkLibPath=${CoreRT_TestRoot}/../bin/${CoreRT_BuildOS}.${CoreRT_BuildArch}.${CoreRT_BuildType}/lib" "/p:FrameworkObjPath=${CoreRT_TestRoot}/../bin/obj/${CoreRT_BuildOS}.${CoreRT_BuildArch}.${CoreRT_BuildType}/Framework" ${__extra_args} "${__extra_cxxflags}" "${__extra_linkflags}" ${__dir_path}/${__filename}.csproj + ${__msbuild_dir}/dotnetcli/dotnet ${__msbuild_dir}/MSBuild.dll /ds /m /p:IlcPath=${CoreRT_ToolchainDir} /p:Configuration=${CoreRT_BuildType} /p:Platform=${CoreRT_BuildArch} /p:OSGroup=${CoreRT_BuildOS} /p:RepoLocalBuild=true "/p:FrameworkLibPath=${CoreRT_TestRoot}/../bin/${CoreRT_BuildOS}.${CoreRT_BuildArch}.${CoreRT_BuildType}/lib" "/p:FrameworkObjPath=${CoreRT_TestRoot}/../bin/obj/${CoreRT_BuildOS}.${CoreRT_BuildArch}.${CoreRT_BuildType}/Framework" ${__extra_args} "${__extra_cxxflags}" "${__extra_linkflags}" ${__dir_path}/${__filename}.csproj local __exitcode=$? @@ -135,7 +135,6 @@ run_coreclr_tests() fi XunitTestBinBase=${CoreRT_TestExtRepo} - CORE_ROOT=${CoreRT_TestRoot}/CoreCLR/runtest pushd ${CoreRT_TestRoot}/CoreCLR/runtest export CoreRT_TestRoot @@ -153,8 +152,8 @@ run_coreclr_tests() CoreRT_TestSelectionArg= fi - echo ./runtest.sh --testRootDir=${CoreRT_TestExtRepo} --coreOverlayDir=${CoreRT_TestRoot}/CoreCLR ${CoreRT_TestSelectionArg} --logdir=$__LogDir - ./runtest.sh --testRootDir=${CoreRT_TestExtRepo} --coreOverlayDir=${CoreRT_TestRoot}/CoreCLR ${CoreRT_TestSelectionArg} --logdir=$__LogDir + echo ./runtest.sh --testRootDir=${CoreRT_TestExtRepo} --coreOverlayDir=${CoreRT_TestRoot}/CoreCLR ${CoreRT_TestSelectionArg} --logdir=$__LogDir --disableEventLogging + ./runtest.sh --testRootDir=${CoreRT_TestExtRepo} --coreOverlayDir=${CoreRT_TestRoot}/CoreCLR ${CoreRT_TestSelectionArg} --logdir=$__LogDir --disableEventLogging } CoreRT_TestRoot="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -267,10 +266,10 @@ if [ ${CoreRT_CrossBuild} != 0 ]; then source $ROOTFS_DIR/etc/os-release fi if [ "$ID" = "tizen" ]; then - CoreRT_CrossCXXFlags="${CoreRT_CrossCXXFlags} -isystem ${CoreRT_CrossRootFS}/usr/lib/gcc/armv7l-tizen-linux-gnueabi/4.9.2/include/c++ ` - `-isystem ${CoreRT_CrossRootFS}//usr/lib/gcc/armv7l-tizen-linux-gnueabi/4.9.2/include/c++/armv7l-tizen-linux-gnueabi ` + TIZEN_TOOLCHAIN="armv7l-tizen-linux-gnueabi/6.2.1" + CoreRT_CrossCXXFlags="${CoreRT_CrossCXXFlags} -isystem ${CoreRT_CrossRootFS}/usr/lib/gcc/${TIZEN_TOOLCHAIN}/include/c++ ` + `-isystem ${CoreRT_CrossRootFS}//usr/lib/gcc/${TIZEN_TOOLCHAIN}/include/c++/armv7l-tizen-linux-gnueabi ` `-isystem ${CoreRT_CrossRootFS}/armel/usr/include" - TIZEN_TOOLCHAIN="armv7l-tizen-linux-gnueabi/4.9.2" CoreRT_CrossLinkerFlags="${CoreRT_CrossLinkerFlags} -B${CoreRT_CrossRootFS}/usr/lib/gcc/${TIZEN_TOOLCHAIN} ` `-L${CoreRT_CrossRootFS}/usr/lib/gcc/${TIZEN_TOOLCHAIN}" else @@ -321,6 +320,10 @@ __CppPassedTests=0 __JitTotalTests=0 __JitPassedTests=0 + +if [ ! -d ${__CoreRTTestBinDir} ]; then + mkdir -p ${__CoreRTTestBinDir} +fi echo > ${__CoreRTTestBinDir}/testResults.tmp __BuildOsLowcase=$(echo "${CoreRT_BuildOS}" | tr '[:upper:]' '[:lower:]') @@ -352,8 +355,7 @@ else __TestResultsLog=${__CoreRTTestBinDir}/testResults.xml fi -echo "" > ${__TestResultsLog} -echo "" >> ${__TestResultsLog} +echo "" > ${__TestResultsLog} echo "" >> ${__TestResultsLog} echo "" >> ${__TestResultsLog} cat "${__CoreRTTestBinDir}/testResults.tmp" >> ${__TestResultsLog} diff --git a/external/corert/tests/src/Simple/BasicThreading/BasicThreading.cmd b/external/corert/tests/src/Simple/BasicThreading/BasicThreading.cmd index 2167b8ecfe..8712cf5757 100644 --- a/external/corert/tests/src/Simple/BasicThreading/BasicThreading.cmd +++ b/external/corert/tests/src/Simple/BasicThreading/BasicThreading.cmd @@ -1,5 +1,8 @@ @echo off setlocal + +rem Enable Server GC for this test +set RH_UseServerGC=1 "%1\%2" set ErrorCode=%ERRORLEVEL% IF "%ErrorCode%"=="100" ( diff --git a/external/corert/tests/src/Simple/BasicThreading/BasicThreading.csproj b/external/corert/tests/src/Simple/BasicThreading/BasicThreading.csproj index 0948ddf81c..45ebce05d6 100644 --- a/external/corert/tests/src/Simple/BasicThreading/BasicThreading.csproj +++ b/external/corert/tests/src/Simple/BasicThreading/BasicThreading.csproj @@ -1,4 +1,4 @@ - + diff --git a/external/corert/tests/src/Simple/BasicThreading/BasicThreading.sh b/external/corert/tests/src/Simple/BasicThreading/BasicThreading.sh index 6640f629fc..91ec7c7507 100755 --- a/external/corert/tests/src/Simple/BasicThreading/BasicThreading.sh +++ b/external/corert/tests/src/Simple/BasicThreading/BasicThreading.sh @@ -1,4 +1,7 @@ #!/usr/bin/env bash + +# Enable Server GC for this test +export RH_UseServerGC=1 $1/$2 if [ $? == 100 ]; then echo pass diff --git a/external/corert/tests/src/Simple/BuildMultiModuleLibraries/BuildMultiModuleLibraries.csproj b/external/corert/tests/src/Simple/BuildMultiModuleLibraries/BuildMultiModuleLibraries.csproj index 519139cd58..37c79aa141 100644 --- a/external/corert/tests/src/Simple/BuildMultiModuleLibraries/BuildMultiModuleLibraries.csproj +++ b/external/corert/tests/src/Simple/BuildMultiModuleLibraries/BuildMultiModuleLibraries.csproj @@ -1,4 +1,4 @@ - + diff --git a/external/corert/tests/src/Simple/Delegates/Delegates.csproj b/external/corert/tests/src/Simple/Delegates/Delegates.csproj index 0948ddf81c..45ebce05d6 100644 --- a/external/corert/tests/src/Simple/Delegates/Delegates.csproj +++ b/external/corert/tests/src/Simple/Delegates/Delegates.csproj @@ -1,4 +1,4 @@ - + diff --git a/external/corert/tests/src/Simple/Exceptions/Exceptions.csproj b/external/corert/tests/src/Simple/Exceptions/Exceptions.csproj index 0948ddf81c..45ebce05d6 100644 --- a/external/corert/tests/src/Simple/Exceptions/Exceptions.csproj +++ b/external/corert/tests/src/Simple/Exceptions/Exceptions.csproj @@ -1,4 +1,4 @@ - + diff --git a/external/corert/tests/src/Simple/Generics/Generics.cs b/external/corert/tests/src/Simple/Generics/Generics.cs index b2c041c94d..3b5386b343 100644 --- a/external/corert/tests/src/Simple/Generics/Generics.cs +++ b/external/corert/tests/src/Simple/Generics/Generics.cs @@ -30,6 +30,7 @@ class Program TestGvmDelegates.Run(); TestGvmDependencies.Run(); TestFieldAccess.Run(); + TestNativeLayoutGeneration.Run(); return 100; } @@ -716,6 +717,9 @@ class Program public virtual string IFaceMethod1(T t) { return "BaseClass.IFaceMethod1"; } public virtual string IFaceGVMethod1(T t, U u) { return "BaseClass.IFaceGVMethod1"; } + + [MethodImpl(MethodImplOptions.NoInlining)] + public virtual string VirtualButNotUsedVirtuallyMethod(T t) { return "BaseClass.VirtualButNotUsedVirtuallyMethod"; } } public class DerivedClass1 : BaseClass, IFace @@ -728,6 +732,12 @@ class Program public new virtual string GVMethod3(T t, U u) { return "DerivedClass1.GVMethod3"; } public override string IFaceMethod1(T t) { return "DerivedClass1.IFaceMethod1"; } + + public string UseVirtualButNotUsedVirtuallyMethod(T t) + { + // Calling through base produces a `call` instead of `callvirt` instruction. + return base.VirtualButNotUsedVirtuallyMethod(t); + } } public class DerivedClass2 : DerivedClass1, IFace @@ -801,6 +811,7 @@ class Program new DerivedClass1().GVMethod2("string", "string2"); new DerivedClass1().GVMethod3("string", "string2"); new DerivedClass1().GVMethod4("string", "string2"); + new DerivedClass1().UseVirtualButNotUsedVirtuallyMethod("string"); new DerivedClass2().Method1("string"); new DerivedClass2().Method2("string"); new DerivedClass2().Method3("string"); @@ -809,13 +820,15 @@ class Program new DerivedClass2().GVMethod2("string", "string2"); new DerivedClass2().GVMethod3("string", "string2"); new DerivedClass2().GVMethod4("string", "string2"); - ((IFace)new BaseClass()).IFaceMethod1("string"); + Func> f = () => new BaseClass(); // Hack to prevent devirtualization + f().IFaceMethod1("string"); ((IFace)new BaseClass()).IFaceGVMethod1("string1", "string2"); MethodInfo m1 = typeof(BaseClass).GetTypeInfo().GetDeclaredMethod("Method1"); MethodInfo m2 = typeof(BaseClass).GetTypeInfo().GetDeclaredMethod("Method2"); MethodInfo m3 = typeof(BaseClass).GetTypeInfo().GetDeclaredMethod("Method3"); MethodInfo m4 = typeof(BaseClass).GetTypeInfo().GetDeclaredMethod("Method4"); + MethodInfo unusedMethod = typeof(BaseClass).GetTypeInfo().GetDeclaredMethod("VirtualButNotUsedVirtuallyMethod"); MethodInfo gvm1 = typeof(BaseClass).GetTypeInfo().GetDeclaredMethod("GVMethod1").MakeGenericMethod(typeof(string)); MethodInfo gvm2 = typeof(BaseClass).GetTypeInfo().GetDeclaredMethod("GVMethod2").MakeGenericMethod(typeof(string)); MethodInfo gvm3 = typeof(BaseClass).GetTypeInfo().GetDeclaredMethod("GVMethod3").MakeGenericMethod(typeof(string)); @@ -824,6 +837,7 @@ class Program Verify("BaseClass.Method2", m2.Invoke(new BaseClass(), new[] { "" })); Verify("BaseClass.Method3", m3.Invoke(new BaseClass(), new[] { "" })); Verify("BaseClass.Method4", m4.Invoke(new BaseClass(), new[] { "" })); + Verify("BaseClass.VirtualButNotUsedVirtuallyMethod", unusedMethod.Invoke(new BaseClass(), new[] { "" })); Verify("DerivedClass1.Method1", m1.Invoke(new DerivedClass1(), new[] { "" })); Verify("DerivedClass1.Method2", m2.Invoke(new DerivedClass1(), new[] { "" })); Verify("BaseClass.Method3", m3.Invoke(new DerivedClass1(), new[] { "" })); @@ -963,36 +977,50 @@ class Program class TestConstrainedMethodCalls { + class Atom1 { } + class Atom2 { } + interface IFoo { - void Frob(); + bool Frob(object o); } struct Foo : IFoo { public int FrobbedValue; - public void Frob() + public bool Frob(object o) { FrobbedValue = 12345; + return o is T[,,]; } } [MethodImpl(MethodImplOptions.NoInlining)] - static void DoFrob(ref T t) where T : IFoo + static bool DoFrob(ref T t, object o) where T : IFoo { // Perform a constrained interface call from shared code. // This should have been resolved to a direct call at compile time. - t.Frob(); + return t.Frob(o); } public static void Run() { - var foo = new Foo(); - DoFrob, object>(ref foo); + var foo1 = new Foo(); + bool result = DoFrob, Atom1>(ref foo1, new Atom1[0,0,0]); // If the FrobbedValue doesn't change when we frob, we must have done box+interface call. - if (foo.FrobbedValue != 12345) + if (foo1.FrobbedValue != 12345) + throw new Exception(); + + // Also check we passed the right generic context to Foo.Frob + if (!result) + throw new Exception(); + + // Also check dependency analysis: + // EEType for Atom2[,,] that we'll check for was never allocated. + var foo2 = new Foo(); + if (DoFrob, Atom2>(ref foo2, new object())) throw new Exception(); } } @@ -1128,6 +1156,21 @@ class Program string IMethod1(T t1, T t2); } + interface ICovariant + { + string ICovariantGVM(); + } + + public interface IBar + { + U IBarGVMethod(Func arg); + } + + public interface IFace + { + string IFaceGVMethod1(T t, U u); + } + class Base : IFoo, IFoo { public virtual string GMethod1(T t1, T t2) { return "Base.GMethod1<" + typeof(T) + ">(" + t1 + "," + t2 + ")"; } @@ -1175,6 +1218,36 @@ class Program public string IMethod1(T t1, T t2) { return "MyStruct3.IMethod1<" + typeof(T) + ">(" + t1 + "," + t2 + ")"; } } + public class AnotherBaseClass + { + public virtual string IFaceMethod1(T t) { return "AnotherBaseClass.IFaceMethod1"; } + public virtual string IFaceGVMethod1(T t, U u) { return "AnotherBaseClass.IFaceGVMethod1"; } + } + + public class AnotherDerivedClass : AnotherBaseClass, IFace + { + } + + public class BarImplementor : IBar + { + public virtual U IBarGVMethod(Func arg) { return arg(123); } + } + + public class Yahoo + { + public virtual U YahooGVM(Func arg) { return default(U); } + } + + public class YahooDerived : Yahoo + { + public override U YahooGVM(Func arg) { return arg(456); } + } + + public class Covariant : ICovariant + { + public string ICovariantGVM() { return String.Format("Covariant<{0}>.ICovariantGVM<{1}>", typeof(T).Name, typeof(U).Name); } + } + static string s_GMethod1; static string s_IFooString; static string s_IFooObject; @@ -1328,6 +1401,20 @@ class Program Console.WriteLine("===================="); } + { + string res = ((IFace)new AnotherDerivedClass()).IFaceGVMethod1("string1", "string2"); + WriteLineWithVerification("AnotherBaseClass.IFaceGVMethod1", res); + + res = ((IBar)new BarImplementor()).IBarGVMethod((i) => "BarImplementor:" + i.ToString()); + WriteLineWithVerification("BarImplementor:123", res); + + Yahoo y = new YahooDerived(); + WriteLineWithVerification("YahooDerived:456", y.YahooGVM((i) => "YahooDerived:" + i.ToString())); + + ICovariant cov = new Covariant(); + WriteLineWithVerification("Covariant.ICovariantGVM", cov.ICovariantGVM()); + } + if (s_NumErrors != 0) throw new Exception(); } @@ -1952,4 +2039,53 @@ class Program throw new Exception(s_NumErrors + " errors!"); } } + + // Regression test for https://github.com/dotnet/corert/issues/3659 + class TestNativeLayoutGeneration + { +#pragma warning disable 649 // s_ref was never assigned + private static object s_ref; +#pragma warning restore 649 + + class Used + { + [MethodImpl(MethodImplOptions.NoInlining)] + public virtual string DoStuff() + { + return "Used"; + } + } + + class Unused : Used + { + [MethodImpl(MethodImplOptions.NoInlining)] + public override string DoStuff() + { + return "Unused " + typeof(T).ToString(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void Blagh() + { + } + } + + public static void Run() + { + new Used().DoStuff(); + + try + { + // Call an instance method on something we never allocated, but overrides a used virtual. + // This asserted the compiler when trying to build a template for Unused<__Canon>. + ((Unused)s_ref).Blagh(); + } + catch (NullReferenceException) + { + return; + } + + throw new Exception(); + } + } } diff --git a/external/corert/tests/src/Simple/Generics/Generics.csproj b/external/corert/tests/src/Simple/Generics/Generics.csproj index 0948ddf81c..45ebce05d6 100644 --- a/external/corert/tests/src/Simple/Generics/Generics.csproj +++ b/external/corert/tests/src/Simple/Generics/Generics.csproj @@ -1,4 +1,4 @@ - + diff --git a/external/corert/tests/src/Simple/Hello/Hello.csproj b/external/corert/tests/src/Simple/Hello/Hello.csproj index 0948ddf81c..45ebce05d6 100644 --- a/external/corert/tests/src/Simple/Hello/Hello.csproj +++ b/external/corert/tests/src/Simple/Hello/Hello.csproj @@ -1,4 +1,4 @@ - + diff --git a/external/corert/tests/src/Simple/HelloWasm/CpObj.il b/external/corert/tests/src/Simple/HelloWasm/CpObj.il new file mode 100644 index 0000000000..ca6444dee5 --- /dev/null +++ b/external/corert/tests/src/Simple/HelloWasm/CpObj.il @@ -0,0 +1,34 @@ +// 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. + +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) + .ver 4:0:0:0 +} + +.assembly extern System.Private.CoreLib +{ + .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) + .ver 4:0:0:0 +} + +.assembly CpObj { } + +.class public CpObj.CpObjTest +{ + .method public hidebysig static void CpObj(valuetype CpObj.TestValue& dest, valuetype CpObj.TestValue& src) cil managed + { + .maxstack 8 + ldarg.0 + ldarg.1 + cpobj CpObj.TestValue + ret + } +} + +.class public value sealed CpObj.TestValue +{ + .field public int32 Field; +} \ No newline at end of file diff --git a/external/corert/tests/src/Simple/HelloWasm/CpObj.ilproj b/external/corert/tests/src/Simple/HelloWasm/CpObj.ilproj new file mode 100644 index 0000000000..e21390e95e --- /dev/null +++ b/external/corert/tests/src/Simple/HelloWasm/CpObj.ilproj @@ -0,0 +1,20 @@ + + + netstandard2.0 + Library + portable + $(MSBuildProjectDirectory)\bin\$(Configuration)\$(Platform)\ + $(MSBuildProjectDirectory)\obj\$(Configuration)\$(Platform)\ + + + + + + + + $(MicrosoftNETCoreAppPackageVersion) + + + + + diff --git a/external/corert/tests/src/Simple/HelloWasm/HelloWasm.cmd b/external/corert/tests/src/Simple/HelloWasm/HelloWasm.cmd new file mode 100644 index 0000000000..286a11ffc3 --- /dev/null +++ b/external/corert/tests/src/Simple/HelloWasm/HelloWasm.cmd @@ -0,0 +1,15 @@ +@echo off +setlocal +set ErrorCode=100 +for /f "usebackq delims=;" %%F in (`"%1\%2" world`) do ( + if "%%F"=="Hello world" set ErrorCode=0 +) +IF "%ErrorCode%"=="0" ( + echo %~n0: pass + EXIT /b 0 +) ELSE ( + echo %~n0: fail - %ErrorCode% + EXIT /b 1 +) +endlocal + diff --git a/external/corert/tests/src/Simple/HelloWasm/HelloWasm.csproj b/external/corert/tests/src/Simple/HelloWasm/HelloWasm.csproj new file mode 100644 index 0000000000..b767a5dbd5 --- /dev/null +++ b/external/corert/tests/src/Simple/HelloWasm/HelloWasm.csproj @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/external/corert/tests/src/Simple/HelloWasm/HelloWasm.sh b/external/corert/tests/src/Simple/HelloWasm/HelloWasm.sh new file mode 100644 index 0000000000..f3e52649a2 --- /dev/null +++ b/external/corert/tests/src/Simple/HelloWasm/HelloWasm.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +dir=$1 +file=$2 +cmp=`$dir/$file world | tr '\n' ';'` +if [[ $cmp = "Hello world;" ]]; then + echo pass + exit 0 +else + echo fail + exit 1 +fi diff --git a/external/corert/tests/src/Simple/HelloWasm/Program.cs b/external/corert/tests/src/Simple/HelloWasm/Program.cs new file mode 100644 index 0000000000..25886330ca --- /dev/null +++ b/external/corert/tests/src/Simple/HelloWasm/Program.cs @@ -0,0 +1,252 @@ +// 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.Runtime.InteropServices; +using CpObj; + +internal static class Program +{ + private static int staticInt; + [ThreadStatic] + private static int threadStaticInt; + private static unsafe void Main(string[] args) + { + Add(1, 2); + int tempInt = 0; + (*(&tempInt)) = 9; + if(tempInt == 9) + { + PrintLine("Hello from C#!"); + } + + TestClass tempObj = new TestDerivedClass(1337); + tempObj.TestMethod("Hello"); + tempObj.TestVirtualMethod("Hello"); + tempObj.TestVirtualMethod2("Hello"); + + TwoByteStr str = new TwoByteStr() { first = 1, second = 2 }; + TwoByteStr str2 = new TwoByteStr() { first = 3, second = 4 }; + *(&str) = str2; + str2 = *(&str); + + if (str2.second == 4) + { + PrintLine("value type int field test: Ok."); + } + + staticInt = 5; + if (staticInt == 5) + { + PrintLine("static int field test: Ok."); + } + + if(threadStaticInt == 0) + { + PrintLine("thread static int initial value field test: Ok."); + } + + threadStaticInt = 9; + if(threadStaticInt == 9) + { + PrintLine("thread static int field test: Ok."); + } + + var boxedInt = (object)tempInt; + if(((int)boxedInt) == 9) + { + PrintLine("box test: Ok."); + } + + var boxedStruct = (object)new BoxStubTest { Value = "Boxed Stub Test: Ok." }; + PrintLine(boxedStruct.ToString()); + + var not = Not(0xFFFFFFFF) == 0x00000000; + if (not) + { + PrintLine("not test: Ok."); + } + + var negInt = Neg(42) == -42; + if (negInt) + { + PrintLine("negInt test: Ok."); + } + + var shiftLeft = ShiftLeft(1, 2) == 4; + if (shiftLeft) + { + PrintLine("shiftLeft test: Ok."); + } + + var shiftRight = ShiftRight(4, 2) == 1; + if (shiftRight) + { + PrintLine("shiftRight test: Ok."); + } + + var unsignedShift = UnsignedShift(0xFFFFFFFFu, 4) == 0x0FFFFFFFu; + if (unsignedShift) + { + PrintLine("unsignedShift test: Ok."); + } + + var switchTest0 = SwitchOp(5, 5, 0); + if (switchTest0 == 10) + { + PrintLine("SwitchOp0 test: Ok."); + } + + var switchTest1 = SwitchOp(5, 5, 1); + if (switchTest1 == 25) + { + PrintLine("SwitchOp1 test: Ok."); + } + + var switchTestDefault = SwitchOp(5, 5, 20); + if (switchTestDefault == 0) + { + PrintLine("SwitchOpDefault test: Ok."); + } + + var cpObjTestA = new TestValue { Field = 1234 }; + var cpObjTestB = new TestValue { Field = 5678 }; + CpObjTest.CpObj(ref cpObjTestB, ref cpObjTestA); + if (cpObjTestB.Field == 1234) + { + PrintLine("CpObj test: Ok."); + } + } + + private static unsafe void PrintString(string s) + { + int length = s.Length; + fixed (char* curChar = s) + { + for (int i = 0; i < length; i++) + { + TwoByteStr curCharStr = new TwoByteStr(); + curCharStr.first = (byte)(*(curChar + i)); + printf((byte*)&curCharStr, null); + } + } + } + + public static void PrintLine(string s) + { + PrintString(s); + PrintString("\n"); + } + + private static int Add(int a, int b) + { + return a + b; + } + + private static uint Not(uint a) + { + return ~a; + } + + private static int Neg(int a) + { + return -a; + } + + private static int ShiftLeft(int a, int b) + { + return a << b; + } + + private static int ShiftRight(int a, int b) + { + return a >> b; + } + + private static uint UnsignedShift(uint a, int b) + { + return a >> b; + } + + private static int SwitchOp(int a, int b, int mode) + { + switch(mode) + { + case 0: + return a + b; + case 1: + return a * b; + case 2: + return a / b; + case 3: + return a - b; + default: + return 0; + } + } + + [DllImport("*")] + private static unsafe extern int printf(byte* str, byte* unused); +} + +public struct TwoByteStr +{ + public byte first; + public byte second; +} + +public struct BoxStubTest +{ + public string Value; + public override string ToString() + { + return Value; + } +} + +public class TestClass +{ + public string TestString {get; set;} + + public TestClass(int number) + { + if(number != 1337) + throw new Exception(); + } + + public void TestMethod(string str) + { + TestString = str; + if (TestString == str) + Program.PrintLine("Instance method call test: Ok."); + } + public virtual void TestVirtualMethod(string str) + { + Program.PrintLine("Virtual Slot Test: Ok If second"); + } + + public virtual void TestVirtualMethod2(string str) + { + Program.PrintLine("Virtual Slot Test 2: Ok"); + } +} + +public class TestDerivedClass : TestClass +{ + public TestDerivedClass(int number) : base(number) + { + + } + public override void TestVirtualMethod(string str) + { + Program.PrintLine("Virtual Slot Test: Ok"); + base.TestVirtualMethod(str); + } + + public override string ToString() + { + throw new Exception(); + } +} + diff --git a/external/corert/tests/src/Simple/HelloWasm/no_cpp b/external/corert/tests/src/Simple/HelloWasm/no_cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/external/corert/tests/src/Simple/HelloWasm/no_ryujit b/external/corert/tests/src/Simple/HelloWasm/no_ryujit new file mode 100644 index 0000000000..e69de29bb2 diff --git a/external/corert/tests/src/Simple/HelloWasm/no_unix b/external/corert/tests/src/Simple/HelloWasm/no_unix new file mode 100644 index 0000000000..e6c22996cd --- /dev/null +++ b/external/corert/tests/src/Simple/HelloWasm/no_unix @@ -0,0 +1 @@ +Doesn't work on OSX. \ No newline at end of file diff --git a/external/corert/tests/src/Simple/HelloWasm/wasm b/external/corert/tests/src/Simple/HelloWasm/wasm new file mode 100644 index 0000000000..e69de29bb2 diff --git a/external/corert/tests/src/Simple/Interfaces/Interfaces.cs b/external/corert/tests/src/Simple/Interfaces/Interfaces.cs index 98ae127a85..2bca941bb5 100644 --- a/external/corert/tests/src/Simple/Interfaces/Interfaces.cs +++ b/external/corert/tests/src/Simple/Interfaces/Interfaces.cs @@ -5,6 +5,7 @@ using System; using System.Text; using System.Collections.Generic; +using System.Runtime.CompilerServices; public class BringUpTest { @@ -28,6 +29,9 @@ public class BringUpTest if (TestSpecialArrayInterfaces() == Fail) return Fail; + if (TestIterfaceCallOptimization() == Fail) + return Fail; + return Pass; } @@ -359,4 +363,34 @@ public class BringUpTest } #endregion + + #region Interface call optimization tests + + public interface ISomeInterface + { + int SomeValue { get; } + } + + public abstract class SomeAbstractBaseClass : ISomeInterface + { + public abstract int SomeValue { get; } + } + + public class SomeClass : SomeAbstractBaseClass + { + public override int SomeValue + { + [MethodImpl(MethodImplOptions.NoInlining)] + get { return 14; } + } + } + + private static int TestIterfaceCallOptimization() + { + ISomeInterface test = new SomeClass(); + int v = test.SomeValue; + return (v == 14) ? Pass : Fail; + } + + #endregion } diff --git a/external/corert/tests/src/Simple/Interfaces/Interfaces.csproj b/external/corert/tests/src/Simple/Interfaces/Interfaces.csproj index 0948ddf81c..45ebce05d6 100644 --- a/external/corert/tests/src/Simple/Interfaces/Interfaces.csproj +++ b/external/corert/tests/src/Simple/Interfaces/Interfaces.csproj @@ -1,4 +1,4 @@ - + diff --git a/external/corert/tests/src/Simple/MultiModule/Library.csproj b/external/corert/tests/src/Simple/MultiModule/Library.csproj index e79b376302..5b5bacfaf5 100644 --- a/external/corert/tests/src/Simple/MultiModule/Library.csproj +++ b/external/corert/tests/src/Simple/MultiModule/Library.csproj @@ -1,4 +1,4 @@ - + Library true diff --git a/external/corert/tests/src/Simple/MultiModule/MultiModule.csproj b/external/corert/tests/src/Simple/MultiModule/MultiModule.csproj index d4027e9e4a..cb2cc8ee0d 100644 --- a/external/corert/tests/src/Simple/MultiModule/MultiModule.csproj +++ b/external/corert/tests/src/Simple/MultiModule/MultiModule.csproj @@ -1,4 +1,4 @@ - + true diff --git a/external/corert/tests/src/Simple/PInvoke/PInvoke.cs b/external/corert/tests/src/Simple/PInvoke/PInvoke.cs index fef465840a..3024b84eb6 100644 --- a/external/corert/tests/src/Simple/PInvoke/PInvoke.cs +++ b/external/corert/tests/src/Simple/PInvoke/PInvoke.cs @@ -181,6 +181,43 @@ namespace PInvokeTests [DllImport("*", CallingConvention = CallingConvention.StdCall)] static extern bool InlineArrayTest(ref InlineArrayStruct ias, ref InlineUnicodeStruct ius); + [UnmanagedFunctionPointer(CallingConvention.Cdecl, SetLastError = true)] + public unsafe delegate void SetLastErrorFuncDelegate(int errorCode); + + [DllImport("*", CallingConvention = CallingConvention.StdCall)] + internal static extern IntPtr GetFunctionPointer(); + + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + internal unsafe struct InlineString + { + internal uint size; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + internal string name; + } + + [DllImport("*", CallingConvention = CallingConvention.StdCall)] + static extern bool InlineStringTest(ref InlineString ias); + + internal delegate int Callback0(); + internal delegate int Callback1(); + internal delegate int Callback2(); + + [DllImport("*")] + internal static extern bool RegisterCallbacks(ref Callbacks callbacks); + + [StructLayout(LayoutKind.Sequential)] + internal struct Callbacks + { + public Callback0 callback0; + public Callback1 callback1; + public Callback2 callback2; + } + + public static int callbackFunc0() { return 0; } + public static int callbackFunc1() { return 1; } + public static int callbackFunc2() { return 2; } + public static int Main(string[] args) { TestBlittableType(); @@ -460,6 +497,12 @@ namespace PInvokeTests Delegate_String ds = new Delegate_String((new ClosedDelegateCLass()).GetString); ThrowIfNotEquals(true, ReversePInvoke_String(ds), "Delegate marshalling failed."); + + IntPtr procAddress = GetFunctionPointer(); + SetLastErrorFuncDelegate funcDelegate = + Marshal.GetDelegateForFunctionPointer(procAddress); + funcDelegate(0x204); + ThrowIfNotEquals(0x204, Marshal.GetLastWin32Error(), "Not match"); } static int Sum(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) @@ -519,9 +562,20 @@ namespace PInvokeTests public ExplicitStruct f2; } + + [StructLayout(LayoutKind.Explicit)] + public struct TestStruct2 + { + [FieldOffset(0)] + public int f1; + + [FieldOffset(8)] + public bool f2; + } private static void TestStruct() { +#if !CODEGEN_CPP Console.WriteLine("Testing Structs"); SequentialStruct ss = new SequentialStruct(); ss.f0 = 100; @@ -568,6 +622,10 @@ namespace PInvokeTests } ThrowIfNotEquals(true, StructTest_Array(ssa, ssa.Length), "Array of struct marshalling failed"); + InlineString ils = new InlineString(); + InlineStringTest(ref ils); + ThrowIfNotEquals("Hello World!", ils.name, "Inline string marshalling failed"); + InlineArrayStruct ias = new InlineArrayStruct(); ias.inlineArray = new short[128]; @@ -581,7 +639,24 @@ namespace PInvokeTests InlineUnicodeStruct ius = new InlineUnicodeStruct(); ius.inlineString = "Hello World"; -#if !CODEGEN_CPP + + TestStruct2 ts = new TestStruct2() { f1 = 100, f2 = true}; + int size = Marshal.SizeOf(ts); + IntPtr memory = Marshal.AllocHGlobal(size); + try + { + Marshal.StructureToPtr(ts, memory, false); + TestStruct2 ts2 = Marshal.PtrToStructure(memory); + ThrowIfNotEquals(true, ts2.f1 == 100 && ts2.f2 == true, "Struct marshalling Marshal API failed"); + + IntPtr offset = Marshal.OffsetOf("f2"); + ThrowIfNotEquals(new IntPtr(8), offset, "Struct marshalling OffsetOf failed."); + } + finally + { + Marshal.FreeHGlobal(memory); + } + ThrowIfNotEquals(true, InlineArrayTest(ref ias, ref ius), "inline array marshalling failed"); bool pass = true; for (short i = 0; i < 128; i++) @@ -610,6 +685,12 @@ namespace PInvokeTests pass = true; } ThrowIfNotEquals(true, pass, "Struct marshalling scenario6 failed."); + + Callbacks callbacks = new Callbacks(); + callbacks.callback0 = new Callback0(callbackFunc0); + callbacks.callback1 = new Callback1(callbackFunc1); + callbacks.callback2 = new Callback2(callbackFunc2); + ThrowIfNotEquals(true, RegisterCallbacks(ref callbacks), "Scenario 7: Struct with delegate marshalling failed"); #endif } } diff --git a/external/corert/tests/src/Simple/PInvoke/PInvoke.csproj b/external/corert/tests/src/Simple/PInvoke/PInvoke.csproj index 13f71a6483..05f571f84e 100644 --- a/external/corert/tests/src/Simple/PInvoke/PInvoke.csproj +++ b/external/corert/tests/src/Simple/PInvoke/PInvoke.csproj @@ -1,4 +1,4 @@ - + diff --git a/external/corert/tests/src/Simple/PInvoke/PInvokeNative.cpp b/external/corert/tests/src/Simple/PInvoke/PInvokeNative.cpp index 40f62ddded..90ea1dbff8 100644 --- a/external/corert/tests/src/Simple/PInvoke/PInvokeNative.cpp +++ b/external/corert/tests/src/Simple/PInvoke/PInvokeNative.cpp @@ -147,12 +147,13 @@ DLL_EXPORT int __stdcall VerifyAnsiString(char *val) return CompareAnsiString(val, "Hello World"); } -void CopyAnsiString(char *dst, char *src) +void CopyAnsiString(char *dst, const char *src) { if (src == NULL || dst == NULL) return; - char *p = dst, *q = src; + const char *q = src; + char *p = dst; while (*q) { *p++ = *q++; @@ -470,7 +471,7 @@ DLL_EXPORT void __stdcall StructTest_ByRef(NativeSequentialStruct *nss) nss->b++; char *p = nss->str; - while (*p != NULL) + while (*p != '\0') { *p = *p + 1; p++; @@ -616,6 +617,41 @@ DLL_EXPORT bool __stdcall IsNULL(void *a) return a == NULL; } +DLL_EXPORT void __cdecl SetLastErrorFunc(int errorCode) +{ +#ifdef Windows_NT + SetLastError(errorCode); +#else + errno = errorCode; +#endif +} +DLL_EXPORT void* __stdcall GetFunctionPointer() +{ + return (void*)&SetLastErrorFunc; +} + +typedef struct { + int c; + char inlineString[260]; +} inlineString; + +DLL_EXPORT bool __stdcall InlineStringTest(inlineString* p) +{ + CopyAnsiString(p->inlineString, "Hello World!"); + return true; +} +struct Callbacks +{ + int(__stdcall *callback0) (void); + int(__stdcall *callback1) (void); + int(__stdcall *callback2) (void); +}; + +DLL_EXPORT bool __stdcall RegisterCallbacks(Callbacks *callbacks) +{ + return callbacks->callback0() == 0 && callbacks->callback1() == 1 && callbacks->callback2() == 2; +} + #if (_MSC_VER >= 1400) // Check MSC version #pragma warning(pop) // Renable previous depreciations -#endif \ No newline at end of file +#endif diff --git a/external/corert/tests/src/Simple/PreInitData/PreInitData.cs b/external/corert/tests/src/Simple/PreInitData/PreInitData.cs index 8dbd8e4406..bbf460fd89 100644 --- a/external/corert/tests/src/Simple/PreInitData/PreInitData.cs +++ b/external/corert/tests/src/Simple/PreInitData/PreInitData.cs @@ -4,6 +4,10 @@ using System; using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using Internal.Runtime.CompilerServices; + +#region Place holder types for internal System.Private.CoreLib types namespace System.Runtime.CompilerServices { @@ -20,23 +24,135 @@ namespace System.Runtime.CompilerServices } } + + [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)] + class TypeHandleFixupAttribute: Attribute + { + public TypeHandleFixupAttribute(int offset, Type fixupType) + { + } + } + + [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)] + class MethodAddrFixupAttribute: Attribute + { + public MethodAddrFixupAttribute(int offset, Type fixupType, string methodName) + { + } + } +} + + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Method)] + public sealed class NativeCallableAttribute : Attribute + { + public string EntryPoint; + + public CallingConvention CallingConvention; + + public NativeCallableAttribute() + { + } + } +} + +#endregion + +namespace System.Runtime.InteropServices +{ + [AttributeUsage((System.AttributeTargets.Method | System.AttributeTargets.Class))] + internal class McgIntrinsicsAttribute : Attribute + { + } + + [McgIntrinsics] + internal static class AddrofIntrinsics + { + // This method is implemented elsewhere in the toolchain + internal static IntPtr AddrOf(T ftn) { throw new PlatformNotSupportedException(); } + } } class Details { - private static IntPtr PreInitializedField_DataBlob = IntPtr.Zero; + private static IntPtr PreInitializedInt32Field_DataBlob; + + [TypeHandleFixupAttribute(0, typeof(IntPtr))] + private static IntPtr PreInitializedIntField_DataBlob; + +#if BIT64 + [TypeHandleFixupAttribute(0, typeof(RuntimeTypeHandle))] + [TypeHandleFixupAttribute(16, typeof(int))] + [TypeHandleFixupAttribute(24, typeof(short))] + [TypeHandleFixupAttribute(32, typeof(long))] + [TypeHandleFixupAttribute(40, typeof(string))] +#else + [TypeHandleFixupAttribute(0, typeof(RuntimeTypeHandle))] + [TypeHandleFixupAttribute(8, typeof(int))] + [TypeHandleFixupAttribute(12, typeof(short))] + [TypeHandleFixupAttribute(16, typeof(long))] + [TypeHandleFixupAttribute(20, typeof(string))] +#endif + private static IntPtr PreInitializedTypeField_DataBlob; + +#if BIT64 + [TypeHandleFixupAttribute(0, typeof(IntPtr))] + [MethodAddrFixupAttribute(16, typeof(NativeMethods), "Func1")] + [MethodAddrFixupAttribute(24, typeof(NativeMethods), "Func2")] +#else + [TypeHandleFixupAttribute(0, typeof(IntPtr))] + [MethodAddrFixupAttribute(8, typeof(NativeMethods), "Func1")] + [MethodAddrFixupAttribute(12, typeof(NativeMethods), "Func2")] +#endif + private static IntPtr PreInitializedMethodTypeField_DataBlob; +} + +static class NativeMethods +{ + [NativeCallable] + internal static void Func1(int a) + { + } + + [NativeCallable] + internal static void Func2(float b) + { + } +} + +class PreInitData +{ + internal static string StaticStringFieldBefore = "BEFORE"; + + // + // Reference type fields + // + [PreInitialized] + [InitDataBlob(typeof(Details), "PreInitializedIntField_DataBlob")] + internal static int[] PreInitializedIntField; + + [PreInitialized] + [InitDataBlob(typeof(Details), "PreInitializedTypeField_DataBlob")] + internal static RuntimeTypeHandle[] PreInitializedTypeField; + + [PreInitialized] + [InitDataBlob(typeof(Details), "PreInitializedMethodField_DataBlob")] + internal static IntPtr[] PreInitializedMethodField; + + // + // Primitive type fields + // + [PreInitialized] + [InitDataBlob(typeof(Details), "PreInitializedInt32Field_DataBlob")] + internal static int PreInitializedInt32Field; // = 0x12345678 + + internal static string StaticStringFieldAfter = "AFTER"; } public class PreInitDataTest { - static int[] StaticIntArrayField = new int[] { 5, 6, 7, 8 }; - - [System.Runtime.CompilerServices.PreInitialized] - [System.Runtime.CompilerServices.InitDataBlob(typeof(Details), "PreInitializedField_DataBlob")] - static int[] PreInitializedField = new int[] { 1, 2, 3, 4 }; - - static string StaticStringField = "ABCDE"; - const int Pass = 100; const int Fail = -1; @@ -44,12 +160,30 @@ public class PreInitDataTest { int result = Pass; - if (!TestPreInitData()) + if (!TestPreInitPrimitiveData()) + { + Console.WriteLine("Failed"); + result = Fail; + } + + if (!TestPreInitIntData()) { Console.WriteLine("Failed"); result = Fail; } + if (!TestPreInitTypeData()) + { + Console.WriteLine("Failed"); + result = Fail; + } + + if (!TestPreInitMethodData()) + { + Console.WriteLine("Failed"); + result = Fail; + } + // Make sure PreInitializedField works with other statics if (!TestOtherStatics()) { @@ -60,30 +194,69 @@ public class PreInitDataTest return result; } - static bool TestPreInitData() + static bool TestPreInitPrimitiveData() { - Console.WriteLine("Testing preinitialized array..."); + Console.WriteLine("Testing preinitialized primitive data..."); - for (int i = 0; i < PreInitializedField.Length; ++i) + if (PreInitData.PreInitializedInt32Field != 0x12345678) + return false; + + return true; + } + + static bool TestPreInitIntData() + { + Console.WriteLine("Testing preinitialized int array..."); + + for (int i = 0; i < PreInitData.PreInitializedIntField.Length; ++i) { - if (PreInitializedField[i] != i + 1) + if (PreInitData.PreInitializedIntField[i] != i + 1) return false; } return true; } + static bool TestPreInitTypeData() + { + Console.WriteLine("Testing preinitialized type array..."); + + if (!PreInitData.PreInitializedTypeField[0].Equals(typeof(int).TypeHandle)) + return false; + if (!PreInitData.PreInitializedTypeField[1].Equals(typeof(short).TypeHandle)) + return false; + if (!PreInitData.PreInitializedTypeField[2].Equals(typeof(long).TypeHandle)) + return false; + if (!PreInitData.PreInitializedTypeField[3].Equals(typeof(string).TypeHandle)) + return false; + + return true; + } + + public delegate void Func1Proc(int a); + public delegate void Func2Proc(float a); + + static bool TestPreInitMethodData() + { + Console.WriteLine("Testing preinitialized method array..."); + + if (PreInitData.PreInitializedMethodField[0] != System.Runtime.InteropServices.AddrofIntrinsics.AddrOf(NativeMethods.Func1)) + return false; + + if (PreInitData.PreInitializedMethodField[1] != System.Runtime.InteropServices.AddrofIntrinsics.AddrOf(NativeMethods.Func2)) + return false; + + return true; + } + static bool TestOtherStatics() { Console.WriteLine("Testing other statics work well with preinitialized data in the same type..."); - for (int i = 0; i < StaticIntArrayField.Length; ++i) - { - if (StaticIntArrayField[i] != i + 5) - return false; - } + if (PreInitData.StaticStringFieldBefore != "BEFORE") + return false; - if (StaticStringField != "ABCDE") + if (PreInitData.StaticStringFieldAfter != "AFTER") return false; return true; diff --git a/external/corert/tests/src/Simple/PreInitData/PreInitData.il b/external/corert/tests/src/Simple/PreInitData/PreInitData.il deleted file mode 100644 index cb60cfb70f..0000000000 --- a/external/corert/tests/src/Simple/PreInitData/PreInitData.il +++ /dev/null @@ -1,347 +0,0 @@ -// 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. - -// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 -// Copyright (c) Microsoft Corporation. All rights reserved. - -// Metadata version: v4.0.30319 -.assembly extern mscorlib -{ - .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. - .ver 4:0:0:0 -} -.assembly PreInitData -{ - .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) - .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx - 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. - - // --- The following custom attribute is added automatically, do not uncomment ------- - // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) - - .hash algorithm 0x00008004 - .ver 0:0:0:0 -} -.module PreInitData.exe -// MVID: {916169DC-051D-4529-A01D-E629C1FB5BFB} -.imagebase 0x00400000 -.file alignment 0x00000200 -.stackreserve 0x00100000 -.subsystem 0x0003 // WINDOWS_CUI -.corflags 0x00000001 // ILONLY -// Image base: 0x001E0000 - - -// =============== CLASS MEMBERS DECLARATION =================== - -.class private auto ansi beforefieldinit Details - extends [mscorlib]System.Object -{ - .class explicit ansi sealed nested private '__PreInitData__Size=16' - extends [mscorlib]System.ValueType - { - .pack 1 - .size 16 - } // end of class '__PreInitData__Size=16' - - .field private static initonly valuetype Details/'__PreInitData__Size=16' PreInitializedField_DataBlob at I_00002A28 - .method public hidebysig specialname rtspecialname - instance void .ctor() cil managed - { - // Code size 8 (0x8) - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: nop - IL_0007: ret - } // end of method Details::.ctor -} // end of class Details - -.class public auto ansi beforefieldinit PreInitDataTest - extends [mscorlib]System.Object -{ - .field private static int32[] StaticIntArrayField - .field private static int32[] PreInitializedField - .custom instance void System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type, - string) = ( 01 00 07 44 65 74 61 69 6C 73 1C 50 72 65 49 6E // ...Details.PreIn - 69 74 69 61 6C 69 7A 65 64 46 69 65 6C 64 5F 44 // itializedField_D - 61 74 61 42 6C 6F 62 00 00 ) // ataBlob.. - .field private static string StaticStringField - .field private static literal int32 Pass = int32(0x00000064) - .field private static literal int32 Fail = int32(0xFFFFFFFF) - .method public hidebysig static int32 Main(string[] args) cil managed - { - .entrypoint - // Code size 64 (0x40) - .maxstack 2 - .locals init (int32 V_0, - bool V_1, - bool V_2, - int32 V_3) - IL_0000: nop - IL_0001: ldc.i4.s 100 - IL_0003: stloc.0 - IL_0004: call bool PreInitDataTest::TestPreInitData() - IL_0009: ldc.i4.0 - IL_000a: ceq - IL_000c: stloc.1 - IL_000d: ldloc.1 - IL_000e: brfalse.s IL_001f - - IL_0010: nop - IL_0011: ldstr "Failed" - IL_0016: call void [mscorlib]System.Console::WriteLine(string) - IL_001b: nop - IL_001c: ldc.i4.m1 - IL_001d: stloc.0 - IL_001e: nop - IL_001f: call bool PreInitDataTest::TestOtherStatics() - IL_0024: ldc.i4.0 - IL_0025: ceq - IL_0027: stloc.2 - IL_0028: ldloc.2 - IL_0029: brfalse.s IL_003a - - IL_002b: nop - IL_002c: ldstr "Failed" - IL_0031: call void [mscorlib]System.Console::WriteLine(string) - IL_0036: nop - IL_0037: ldc.i4.m1 - IL_0038: stloc.0 - IL_0039: nop - IL_003a: ldloc.0 - IL_003b: stloc.3 - IL_003c: br.s IL_003e - - IL_003e: ldloc.3 - IL_003f: ret - } // end of method PreInitDataTest::Main - - .method private hidebysig static bool TestPreInitData() cil managed - { - // Code size 65 (0x41) - .maxstack 3 - .locals init (int32 V_0, - bool V_1, - bool V_2, - bool V_3) - IL_0000: nop - IL_0001: ldstr "Testing preinitialized array..." - IL_0006: call void [mscorlib]System.Console::WriteLine(string) - IL_000b: nop - IL_000c: ldc.i4.0 - IL_000d: stloc.0 - IL_000e: br.s IL_002d - - IL_0010: nop - IL_0011: ldsfld int32[] PreInitDataTest::PreInitializedField - IL_0016: ldloc.0 - IL_0017: ldelem.i4 - IL_0018: ldloc.0 - IL_0019: ldc.i4.1 - IL_001a: add - IL_001b: ceq - IL_001d: ldc.i4.0 - IL_001e: ceq - IL_0020: stloc.1 - IL_0021: ldloc.1 - IL_0022: brfalse.s IL_0028 - - IL_0024: ldc.i4.0 - IL_0025: stloc.2 - IL_0026: br.s IL_003f - - IL_0028: nop - IL_0029: ldloc.0 - IL_002a: ldc.i4.1 - IL_002b: add - IL_002c: stloc.0 - IL_002d: ldloc.0 - IL_002e: ldsfld int32[] PreInitDataTest::PreInitializedField - IL_0033: ldlen - IL_0034: conv.i4 - IL_0035: clt - IL_0037: stloc.3 - IL_0038: ldloc.3 - IL_0039: brtrue.s IL_0010 - - IL_003b: ldc.i4.1 - IL_003c: stloc.2 - IL_003d: br.s IL_003f - - IL_003f: ldloc.2 - IL_0040: ret - } // end of method PreInitDataTest::TestPreInitData - - .method private hidebysig static bool TestOtherStatics() cil managed - { - // Code size 90 (0x5a) - .maxstack 3 - .locals init (int32 V_0, - bool V_1, - bool V_2, - bool V_3, - bool V_4) - IL_0000: nop - IL_0001: ldstr "Testing other statics work well with preinitialize" - + "d data in the same type..." - IL_0006: call void [mscorlib]System.Console::WriteLine(string) - IL_000b: nop - IL_000c: ldc.i4.0 - IL_000d: stloc.0 - IL_000e: br.s IL_002d - - IL_0010: nop - IL_0011: ldsfld int32[] PreInitDataTest::StaticIntArrayField - IL_0016: ldloc.0 - IL_0017: ldelem.i4 - IL_0018: ldloc.0 - IL_0019: ldc.i4.5 - IL_001a: add - IL_001b: ceq - IL_001d: ldc.i4.0 - IL_001e: ceq - IL_0020: stloc.1 - IL_0021: ldloc.1 - IL_0022: brfalse.s IL_0028 - - IL_0024: ldc.i4.0 - IL_0025: stloc.2 - IL_0026: br.s IL_0058 - - IL_0028: nop - IL_0029: ldloc.0 - IL_002a: ldc.i4.1 - IL_002b: add - IL_002c: stloc.0 - IL_002d: ldloc.0 - IL_002e: ldsfld int32[] PreInitDataTest::StaticIntArrayField - IL_0033: ldlen - IL_0034: conv.i4 - IL_0035: clt - IL_0037: stloc.3 - IL_0038: ldloc.3 - IL_0039: brtrue.s IL_0010 - - IL_003b: ldsfld string PreInitDataTest::StaticStringField - IL_0040: ldstr "ABCDE" - IL_0045: call bool [mscorlib]System.String::op_Inequality(string, - string) - IL_004a: stloc.s V_4 - IL_004c: ldloc.s V_4 - IL_004e: brfalse.s IL_0054 - - IL_0050: ldc.i4.0 - IL_0051: stloc.2 - IL_0052: br.s IL_0058 - - IL_0054: ldc.i4.1 - IL_0055: stloc.2 - IL_0056: br.s IL_0058 - - IL_0058: ldloc.2 - IL_0059: ret - } // end of method PreInitDataTest::TestOtherStatics - - .method public hidebysig specialname rtspecialname - instance void .ctor() cil managed - { - // Code size 8 (0x8) - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: nop - IL_0007: ret - } // end of method PreInitDataTest::.ctor - - .method private hidebysig specialname rtspecialname static - void .cctor() cil managed - { - // Code size 55 (0x37) - .maxstack 8 - IL_0000: ldc.i4.4 - IL_0001: newarr [mscorlib]System.Int32 - IL_0006: dup - IL_0007: ldtoken field valuetype ''/'__StaticArrayInitTypeSize=16' ''::AB434F2ED820934995191E4C1A3C24AB41FF2E2C - IL_000c: call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, - valuetype [mscorlib]System.RuntimeFieldHandle) - IL_0011: stsfld int32[] PreInitDataTest::StaticIntArrayField -/* - IL_0016: ldc.i4.4 - IL_0017: newarr [mscorlib]System.Int32 - IL_001c: dup - IL_001d: ldtoken field valuetype ''/'__StaticArrayInitTypeSize=16' ''::'1456763F890A84558F99AFA687C36B9037697848' - IL_0022: call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, - valuetype [mscorlib]System.RuntimeFieldHandle) - IL_0027: stsfld int32[] PreInitDataTest::PreInitializedField -*/ - IL_002c: ldstr "ABCDE" - IL_0031: stsfld string PreInitDataTest::StaticStringField - IL_0036: ret - } // end of method PreInitDataTest::.cctor - -} // end of class PreInitDataTest - -.class private auto ansi beforefieldinit System.Runtime.CompilerServices.PreInitializedAttribute - extends [mscorlib]System.Attribute -{ - .custom instance void [mscorlib]System.AttributeUsageAttribute::.ctor(valuetype [mscorlib]System.AttributeTargets) = ( 01 00 00 01 00 00 01 00 54 02 0D 41 6C 6C 6F 77 // ........T..Allow - 4D 75 6C 74 69 70 6C 65 00 ) // Multiple. - .method public hidebysig specialname rtspecialname - instance void .ctor() cil managed - { - // Code size 8 (0x8) - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Attribute::.ctor() - IL_0006: nop - IL_0007: ret - } // end of method PreInitializedAttribute::.ctor - -} // end of class System.Runtime.CompilerServices.PreInitializedAttribute - -.class private auto ansi beforefieldinit System.Runtime.CompilerServices.InitDataBlobAttribute - extends [mscorlib]System.Attribute -{ - .custom instance void [mscorlib]System.AttributeUsageAttribute::.ctor(valuetype [mscorlib]System.AttributeTargets) = ( 01 00 00 01 00 00 01 00 54 02 0D 41 6C 6C 6F 77 // ........T..Allow - 4D 75 6C 74 69 70 6C 65 00 ) // Multiple. - .method public hidebysig specialname rtspecialname - instance void .ctor(class [mscorlib]System.Type 'type', - string fieldName) cil managed - { - // Code size 9 (0x9) - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Attribute::.ctor() - IL_0006: nop - IL_0007: nop - IL_0008: ret - } // end of method InitDataBlobAttribute::.ctor - -} // end of class System.Runtime.CompilerServices.InitDataBlobAttribute - -.class private auto ansi sealed '' - extends [mscorlib]System.Object -{ - .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - .class explicit ansi sealed nested private '__StaticArrayInitTypeSize=16' - extends [mscorlib]System.ValueType - { - .pack 1 - .size 16 - } // end of class '__StaticArrayInitTypeSize=16' - - .field static assembly initonly valuetype ''/'__StaticArrayInitTypeSize=16' AB434F2ED820934995191E4C1A3C24AB41FF2E2C at I_00002A38 -} // end of class '' - - -// ============================================================= - -.data cil I_00002A28 = bytearray ( - 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00) -.data cil I_00002A38 = bytearray ( - 05 00 00 00 06 00 00 00 07 00 00 00 08 00 00 00) -// *********** DISASSEMBLY COMPLETE *********************** -// WARNING: Created Win32 resource file PreInitData.res diff --git a/external/corert/tests/src/Simple/PreInitData/PreInitData.ilproj b/external/corert/tests/src/Simple/PreInitData/PreInitData.ilproj index cffe3a3f93..6455ed8e37 100644 --- a/external/corert/tests/src/Simple/PreInitData/PreInitData.ilproj +++ b/external/corert/tests/src/Simple/PreInitData/PreInitData.ilproj @@ -1,6 +1,13 @@ - + - + + + + + + + + diff --git a/external/corert/tests/src/Simple/PreInitData/PreInitData32.il b/external/corert/tests/src/Simple/PreInitData/PreInitData32.il new file mode 100644 index 0000000000..8ca5eecd4d --- /dev/null +++ b/external/corert/tests/src/Simple/PreInitData/PreInitData32.il @@ -0,0 +1,194 @@ +// 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. + +// ============================================================= +// PreInitialized data +// ============================================================= + +.class private auto ansi beforefieldinit Details + extends [mscorlib]System.Object +{ + .field private static valuetype ''/'__StaticDataSize=4' PreInitializedInt32Field_DataBlob at Data_Int32 + + .field private static valuetype ''/'__StaticArrayInitTypeSize=24' PreInitializedField_DataBlob at Data_IntArray + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, + class [mscorlib]System.Type) = ( 01 00 00 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In + 74 33 32 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t32, mscorlib, V + 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0, + 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral, + 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken= + 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089 + 00 00 ) // 9.. + + .field private static valuetype ''/'__StaticArrayInitTypeSize=24' PreInitializedTypeField_DataBlob at Data_TypeArray + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, + class [mscorlib]System.Type) = ( 01 00 14 00 00 00 5A 53 79 73 74 65 6D 2E 53 74 // ......ZSystem.St + 72 69 6E 67 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 // ring, mscorlib, + 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C // Version=4.0.0.0, + 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C // Culture=neutral + 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E // , PublicKeyToken + 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 // =b77a5c561934e08 + 39 00 00 ) // 9.. + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, + class [mscorlib]System.Type) = ( 01 00 10 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In + 74 36 34 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t64, mscorlib, V + 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0, + 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral, + 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken= + 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089 + 00 00 ) + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, + class [mscorlib]System.Type) = ( 01 00 0C 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In + 74 31 36 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t16, mscorlib, V + 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0, + 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral, + 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken= + 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089 + 00 00 ) + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, + class [mscorlib]System.Type) = ( 01 08 00 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In + 74 33 32 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t32, mscorlib, V + 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0, + 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral, + 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken= + 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089 + 00 00 ) + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, + class [mscorlib]System.Type) = ( 01 00 00 00 00 00 6B 53 79 73 74 65 6D 2E 52 75 // ......kSystem.Ru + 6E 74 69 6D 65 54 79 70 65 48 61 6E 64 6C 65 2C // ntimeTypeHandle, + 20 53 79 73 74 65 6D 2E 52 75 6E 74 69 6D 65 2C // System.Runtime, + 20 56 65 72 73 69 6F 6E 3D 34 2E 32 2E 30 2E 30 // Version=4.2.0.0 + 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 // , Culture=neutra + 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 // l, PublicKeyToke + 6E 3D 62 30 33 66 35 66 37 66 31 31 64 35 30 61 // n=b03f5f7f11d50a + 33 61 00 00 ) // 3a.. + + .field private static valuetype ''/'__StaticArrayInitTypeSize=16' PreInitializedMethodField_DataBlob at Data_MethodArray + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, + class [mscorlib]System.Type) = ( 01 00 00 00 00 00 5A 53 79 73 74 65 6D 2E 49 6E // ......ZSystem.In + 74 50 74 72 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 // tPtr, mscorlib, + 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C // Version=4.0.0.0, + 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C // Culture=neutral + 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E // , PublicKeyToken + 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 // =b77a5c561934e08 + 39 00 00 ) + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.MethodAddrFixupAttribute::.ctor(int32, + class [mscorlib]System.Type, + string) = ( 01 00 08 00 00 00 0D 4E 61 74 69 76 65 4D 65 74 // .......NativeMet + 68 6F 64 73 05 46 75 6E 63 31 00 00 ) // hods.Func1.. + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.MethodAddrFixupAttribute::.ctor(int32, + class [mscorlib]System.Type, + string) = ( 01 00 0C 00 00 00 0D 4E 61 74 69 76 65 4D 65 74 // .......NativeMet + 68 6F 64 73 05 46 75 6E 63 32 00 00 ) // hods.Func2.. +} // end of class Details + +.class public auto ansi beforefieldinit PreInitData +{ + .field private static string StaticStringFieldBefore + + //=================================================================== + // preinitialized fields + .field private static int32[] PreInitializedIntField + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type, + string) = ( 01 00 07 44 65 74 61 69 6C 73 1C 50 72 65 49 6E // ...Details.PreIn + 69 74 69 61 6C 69 7A 65 64 46 69 65 6C 64 5F 44 // itializedField_D + 61 74 61 42 6C 6F 62 00 00 ) // ataBlob.. + + .field private static class [System.Private.CoreLib]Internal.Runtime.CompilerServices.FixupRuntimeTypeHandle[] PreInitializedTypeField + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type, + string) = ( 01 00 07 44 65 74 61 69 6C 73 20 50 72 65 49 6E // ...Details.PreIn + 69 74 69 61 6C 69 7A 65 64 54 79 70 65 46 69 65 // itializedTypeFie + 6C 64 5F 44 61 74 61 42 6C 6F 62 00 00 ) // ld_DataBlob.. + + .field static assembly native int[] PreInitializedMethodField + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type, + string) = ( 01 00 07 44 65 74 61 69 6C 73 22 50 72 65 49 6E // ...Details.PreIn + 69 74 69 61 6C 69 7A 65 64 4D 65 74 68 6F 64 46 // itializedMethodF + 69 65 6C 64 5F 44 61 74 61 42 6C 6F 62 00 00 ) // ield_DataBlob.. + + .field static assembly int32 PreInitializedInt32Field + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type, + string) = ( 01 00 07 44 65 74 61 69 6C 73 21 50 72 65 49 6E // ...Details!PreIn + 69 74 69 61 6C 69 7A 65 64 49 6E 74 33 32 46 69 // itializedInt32Fi + 65 6C 64 5F 44 61 74 61 42 6C 6F 62 00 00 ) // eld_DataBlob.. + + //=================================================================== + + .field private static string StaticStringFieldAfter + + .method private hidebysig specialname rtspecialname static + void .cctor() cil managed + { + // Code size 21 (0x15) + .maxstack 8 + IL_0000: ldstr "BEFORE" + IL_0005: stsfld string PreInitData::StaticStringFieldBefore + IL_000a: ldstr "AFTER" + IL_000f: stsfld string PreInitData::StaticStringFieldAfter + IL_0014: ret + } // end of method PreInitData::.cctor +} // end of class PreInitData + +.class private auto ansi '' + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .class explicit ansi sealed nested private '__StaticDataSize=4' + extends [mscorlib]System.ValueType + { + .pack 1 + .size 4 + } // end of class '__StaticDataSize=4' + .class explicit ansi sealed nested private '__StaticArrayInitTypeSize=24' + extends [mscorlib]System.ValueType + { + .pack 1 + .size 24 + } // end of class '__StaticArrayInitTypeSize=24' + .class explicit ansi sealed nested private '__StaticArrayInitTypeSize=16' + extends [mscorlib]System.ValueType + { + .pack 1 + .size 16 + } // end of class '__StaticArrayInitTypeSize=16' +} // end of class '' + +.class private abstract auto ansi sealed beforefieldinit NativeMethods + extends [mscorlib]System.Object +{ + .method assembly hidebysig static void + Func1(int32 a) cil managed + { + .custom instance void [System.Private.CoreLib]System.Runtime.InteropServices.NativeCallableAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method NativeMethods::Func1 + + .method assembly hidebysig static void + Func2(float32 b) cil managed + { + .custom instance void [System.Private.CoreLib]System.Runtime.InteropServices.NativeCallableAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method NativeMethods::Func2 + +} // end of class NativeMethods + +// ============================================================= +// Data blob +// ============================================================= + +.data cil Data_IntArray = bytearray ( 00 00 00 00 04 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00) +.data cil Data_TypeArray = bytearray ( 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00) +.data cil Data_MethodArray = bytearray ( 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 ) +.data cil Data_Int32 = bytearray ( 78 56 34 12 ) + diff --git a/external/corert/tests/src/Simple/PreInitData/PreInitData64.il b/external/corert/tests/src/Simple/PreInitData/PreInitData64.il new file mode 100644 index 0000000000..32e73bf870 --- /dev/null +++ b/external/corert/tests/src/Simple/PreInitData/PreInitData64.il @@ -0,0 +1,192 @@ +// 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. + +// ============================================================= +// PreInitialized data +// ============================================================= + +.class private auto ansi beforefieldinit Details + extends [mscorlib]System.Object +{ + .field private static valuetype ''/'__StaticDataSize=4' PreInitializedInt32Field_DataBlob at Data_Int32 + .field private static valuetype ''/'__StaticArrayInitTypeSize=32' PreInitializedField_DataBlob at Data_IntArray + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, + class [mscorlib]System.Type) = ( 01 00 00 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In + 74 33 32 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t32, mscorlib, V + 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0, + 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral, + 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken= + 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089 + 00 00 ) // 9.. + + .field private static valuetype ''/'__StaticArrayInitTypeSize=48' PreInitializedTypeField_DataBlob at Data_TypeArray + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, + class [mscorlib]System.Type) = ( 01 00 28 00 00 00 5A 53 79 73 74 65 6D 2E 53 74 // ......ZSystem.St + 72 69 6E 67 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 // ring, mscorlib, + 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C // Version=4.0.0.0, + 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C // Culture=neutral + 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E // , PublicKeyToken + 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 // =b77a5c561934e08 + 39 00 00 ) // 9.. + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, + class [mscorlib]System.Type) = ( 01 00 20 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In + 74 36 34 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t64, mscorlib, V + 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0, + 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral, + 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken= + 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089 + 00 00 ) + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, + class [mscorlib]System.Type) = ( 01 00 18 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In + 74 31 36 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t16, mscorlib, V + 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0, + 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral, + 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken= + 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089 + 00 00 ) + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, + class [mscorlib]System.Type) = ( 01 00 10 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In + 74 33 32 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t32, mscorlib, V + 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0, + 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral, + 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken= + 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089 + 00 00 ) + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, + class [mscorlib]System.Type) = ( 01 00 00 00 00 00 6B 53 79 73 74 65 6D 2E 52 75 // ......kSystem.Ru + 6E 74 69 6D 65 54 79 70 65 48 61 6E 64 6C 65 2C // ntimeTypeHandle, + 20 53 79 73 74 65 6D 2E 52 75 6E 74 69 6D 65 2C // System.Runtime, + 20 56 65 72 73 69 6F 6E 3D 34 2E 32 2E 30 2E 30 // Version=4.2.0.0 + 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 // , Culture=neutra + 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 // l, PublicKeyToke + 6E 3D 62 30 33 66 35 66 37 66 31 31 64 35 30 61 // n=b03f5f7f11d50a + 33 61 00 00 ) // 3a.. + + .field private static valuetype ''/'__StaticArrayInitTypeSize=32' PreInitializedMethodField_DataBlob at Data_MethodArray + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32, + class [mscorlib]System.Type) = ( 01 00 00 00 00 00 5A 53 79 73 74 65 6D 2E 49 6E // ......ZSystem.In + 74 50 74 72 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 // tPtr, mscorlib, + 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C // Version=4.0.0.0, + 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C // Culture=neutral + 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E // , PublicKeyToken + 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 // =b77a5c561934e08 + 39 00 00 ) + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.MethodAddrFixupAttribute::.ctor(int32, + class [mscorlib]System.Type, + string) = ( 01 00 10 00 00 00 0D 4E 61 74 69 76 65 4D 65 74 // .......NativeMet + 68 6F 64 73 05 46 75 6E 63 31 00 00 ) // hods.Func1.. + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.MethodAddrFixupAttribute::.ctor(int32, + class [mscorlib]System.Type, + string) = ( 01 00 18 00 00 00 0D 4E 61 74 69 76 65 4D 65 74 // .......NativeMet + 68 6F 64 73 05 46 75 6E 63 32 00 00 ) // hods.Func2.. +} // end of class Details + +.class public auto ansi beforefieldinit PreInitData +{ + .field private static string StaticStringFieldBefore + + //=================================================================== + // preinitialized fields + .field private static int32[] PreInitializedIntField + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type, + string) = ( 01 00 07 44 65 74 61 69 6C 73 1C 50 72 65 49 6E // ...Details.PreIn + 69 74 69 61 6C 69 7A 65 64 46 69 65 6C 64 5F 44 // itializedField_D + 61 74 61 42 6C 6F 62 00 00 ) // ataBlob.. + + .field private static class [System.Private.CoreLib]Internal.Runtime.CompilerServices.FixupRuntimeTypeHandle[] PreInitializedTypeField + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type, + string) = ( 01 00 07 44 65 74 61 69 6C 73 20 50 72 65 49 6E // ...Details.PreIn + 69 74 69 61 6C 69 7A 65 64 54 79 70 65 46 69 65 // itializedTypeFie + 6C 64 5F 44 61 74 61 42 6C 6F 62 00 00 ) // ld_DataBlob.. + + .field static assembly native int[] PreInitializedMethodField + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type, + string) = ( 01 00 07 44 65 74 61 69 6C 73 22 50 72 65 49 6E // ...Details.PreIn + 69 74 69 61 6C 69 7A 65 64 4D 65 74 68 6F 64 46 // itializedMethodF + 69 65 6C 64 5F 44 61 74 61 42 6C 6F 62 00 00 ) // ield_DataBlob.. + + .field static assembly int32 PreInitializedInt32Field + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type, + string) = ( 01 00 07 44 65 74 61 69 6C 73 21 50 72 65 49 6E // ...Details!PreIn + 69 74 69 61 6C 69 7A 65 64 49 6E 74 33 32 46 69 // itializedInt32Fi + 65 6C 64 5F 44 61 74 61 42 6C 6F 62 00 00 ) // eld_DataBlob.. + + //=================================================================== + + .field private static string StaticStringFieldAfter + + .method private hidebysig specialname rtspecialname static + void .cctor() cil managed + { + // Code size 21 (0x15) + .maxstack 8 + IL_0000: ldstr "BEFORE" + IL_0005: stsfld string PreInitData::StaticStringFieldBefore + IL_000a: ldstr "AFTER" + IL_000f: stsfld string PreInitData::StaticStringFieldAfter + IL_0014: ret + } // end of method PreInitData::.cctor +} // end of class PreInitData + +.class private auto ansi '' + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .class explicit ansi sealed nested private '__StaticDataSize=4' + extends [mscorlib]System.ValueType + { + .pack 1 + .size 4 + } // end of class '__StaticDataSize=4' + .class explicit ansi sealed nested private '__StaticArrayInitTypeSize=32' + extends [mscorlib]System.ValueType + { + .pack 1 + .size 32 + } // end of class '__StaticArrayInitTypeSize=32' + .class explicit ansi sealed nested private '__StaticArrayInitTypeSize=48' + extends [mscorlib]System.ValueType + { + .pack 1 + .size 48 + } // end of class '__StaticArrayInitTypeSize=16' +} // end of class '' + +.class private abstract auto ansi sealed beforefieldinit NativeMethods + extends [mscorlib]System.Object +{ + .method assembly hidebysig static void + Func1(int32 a) cil managed + { + .custom instance void [System.Private.CoreLib]System.Runtime.InteropServices.NativeCallableAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method NativeMethods::Func1 + + .method assembly hidebysig static void + Func2(float32 b) cil managed + { + .custom instance void [System.Private.CoreLib]System.Runtime.InteropServices.NativeCallableAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method NativeMethods::Func2 + +} // end of class NativeMethods + +// ============================================================= +// Data blob +// ============================================================= + +.data cil Data_IntArray = bytearray ( 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00) +.data cil Data_TypeArray = bytearray ( 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00) +.data cil Data_MethodArray = bytearray ( 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00) +.data cil Data_Int32 = bytearray ( 78 56 34 12 ) diff --git a/external/corert/tests/src/Simple/PreInitData/PreInitDataTest.il b/external/corert/tests/src/Simple/PreInitData/PreInitDataTest.il new file mode 100644 index 0000000000..1f0ad1ed97 --- /dev/null +++ b/external/corert/tests/src/Simple/PreInitData/PreInitDataTest.il @@ -0,0 +1,508 @@ +// 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. + +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} + +.assembly extern System.Private.CoreLib +{ + .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: + .ver 4:0:0:0 +} + +.assembly PreInitData +{ + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} + +.module PreInitData.exe +.imagebase 0x00400000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY +// Image base: 0x024E0000 + +// ============================================================= +// Intrinsics +// ============================================================= + +.class private auto ansi beforefieldinit System.Runtime.InteropServices.McgIntrinsicsAttribute + extends [mscorlib]System.Attribute +{ + .custom instance void [mscorlib]System.AttributeUsageAttribute::.ctor(valuetype [mscorlib]System.AttributeTargets) = ( 01 00 44 00 00 00 00 00 ) // ..D..... + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Attribute::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method McgIntrinsicsAttribute::.ctor + +} // end of class System.Runtime.InteropServices.McgIntrinsicsAttribute + +.class private abstract auto ansi sealed beforefieldinit System.Runtime.InteropServices.AddrofIntrinsics + extends [mscorlib]System.Object +{ + .custom instance void System.Runtime.InteropServices.McgIntrinsicsAttribute::.ctor() = ( 01 00 00 00 ) + .method assembly hidebysig static native int + AddrOf(!!T ftn) cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: nop + IL_0001: newobj instance void [mscorlib]System.PlatformNotSupportedException::.ctor() + IL_0006: throw + } // end of method AddrofIntrinsics::AddrOf + +} // end of class System.Runtime.InteropServices.AddrofIntrinsics + +// ============================================================= +// Test code +// ============================================================= + +.class public auto ansi beforefieldinit PreInitDataTest + extends [mscorlib]System.Object +{ +.class auto ansi sealed nested public Func1Proc + extends [mscorlib]System.MulticastDelegate + { + .method public hidebysig specialname rtspecialname + instance void .ctor(object 'object', + native int 'method') runtime managed + { + } // end of method Func1Proc::.ctor + + .method public hidebysig newslot virtual + instance void Invoke(int32 a) runtime managed + { + } // end of method Func1Proc::Invoke + + .method public hidebysig newslot virtual + instance class [mscorlib]System.IAsyncResult + BeginInvoke(int32 a, + class [mscorlib]System.AsyncCallback callback, + object 'object') runtime managed + { + } // end of method Func1Proc::BeginInvoke + + .method public hidebysig newslot virtual + instance void EndInvoke(class [mscorlib]System.IAsyncResult result) runtime managed + { + } // end of method Func1Proc::EndInvoke + + } // end of class Func1Proc + + .class auto ansi sealed nested public Func2Proc + extends [mscorlib]System.MulticastDelegate + { + .method public hidebysig specialname rtspecialname + instance void .ctor(object 'object', + native int 'method') runtime managed + { + } // end of method Func2Proc::.ctor + + .method public hidebysig newslot virtual + instance void Invoke(float32 a) runtime managed + { + } // end of method Func2Proc::Invoke + + .method public hidebysig newslot virtual + instance class [mscorlib]System.IAsyncResult + BeginInvoke(float32 a, + class [mscorlib]System.AsyncCallback callback, + object 'object') runtime managed + { + } // end of method Func2Proc::BeginInvoke + + .method public hidebysig newslot virtual + instance void EndInvoke(class [mscorlib]System.IAsyncResult result) runtime managed + { + } // end of method Func2Proc::EndInvoke + + } // end of class Func2Proc + + .field private static literal int32 Pass = int32(0x00000064) + .field private static literal int32 Fail = int32(0xFFFFFFFF) + .method public hidebysig static int32 Main(string[] args) cil managed + { + .entrypoint + // Code size 151 (0x97) + .maxstack 2 + .locals init (int32 V_0, + bool V_1, + bool V_2, + bool V_3, + bool V_4, + bool V_5, + int32 V_6) + IL_0000: nop + IL_0001: ldc.i4.s 100 + IL_0003: stloc.0 + IL_0004: call bool PreInitDataTest::TestPreInitPrimitiveData() + IL_0009: ldc.i4.0 + IL_000a: ceq + IL_000c: stloc.1 + IL_000d: ldloc.1 + IL_000e: brfalse.s IL_001f + + IL_0010: nop + IL_0011: ldstr "Failed" + IL_0016: call void [mscorlib]System.Console::WriteLine(string) + IL_001b: nop + IL_001c: ldc.i4.m1 + IL_001d: stloc.0 + IL_001e: nop + IL_001f: call bool PreInitDataTest::TestPreInitIntData() + IL_0024: ldc.i4.0 + IL_0025: ceq + IL_0027: stloc.2 + IL_0028: ldloc.2 + IL_0029: brfalse.s IL_003a + + IL_002b: nop + IL_002c: ldstr "Failed" + IL_0031: call void [mscorlib]System.Console::WriteLine(string) + IL_0036: nop + IL_0037: ldc.i4.m1 + IL_0038: stloc.0 + IL_0039: nop + IL_003a: call bool PreInitDataTest::TestPreInitTypeData() + IL_003f: ldc.i4.0 + IL_0040: ceq + IL_0042: stloc.3 + IL_0043: ldloc.3 + IL_0044: brfalse.s IL_0055 + + IL_0046: nop + IL_0047: ldstr "Failed" + IL_004c: call void [mscorlib]System.Console::WriteLine(string) + IL_0051: nop + IL_0052: ldc.i4.m1 + IL_0053: stloc.0 + IL_0054: nop + IL_0055: call bool PreInitDataTest::TestPreInitMethodData() + IL_005a: ldc.i4.0 + IL_005b: ceq + IL_005d: stloc.s V_4 + IL_005f: ldloc.s V_4 + IL_0061: brfalse.s IL_0072 + + IL_0063: nop + IL_0064: ldstr "Failed" + IL_0069: call void [mscorlib]System.Console::WriteLine(string) + IL_006e: nop + IL_006f: ldc.i4.m1 + IL_0070: stloc.0 + IL_0071: nop + IL_0072: call bool PreInitDataTest::TestOtherStatics() + IL_0077: ldc.i4.0 + IL_0078: ceq + IL_007a: stloc.s V_5 + IL_007c: ldloc.s V_5 + IL_007e: brfalse.s IL_008f + + IL_0080: nop + IL_0081: ldstr "Failed" + IL_0086: call void [mscorlib]System.Console::WriteLine(string) + IL_008b: nop + IL_008c: ldc.i4.m1 + IL_008d: stloc.0 + IL_008e: nop + IL_008f: ldloc.0 + IL_0090: stloc.s V_6 + IL_0092: br.s IL_0094 + + IL_0094: ldloc.s V_6 + IL_0096: ret + } // end of method PreInitDataTest::Main + + .method private hidebysig static bool TestPreInitPrimitiveData() cil managed + { + // Code size 41 (0x29) + .maxstack 2 + .locals init (bool V_0, + bool V_1) + IL_0000: nop + IL_0001: ldstr "Testing preinitialized primitive data..." + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldsfld int32 PreInitData::PreInitializedInt32Field + IL_0011: ldc.i4 0x12345678 + IL_0016: ceq + IL_0018: ldc.i4.0 + IL_0019: ceq + IL_001b: stloc.0 + IL_001c: ldloc.0 + IL_001d: brfalse.s IL_0023 + + IL_001f: ldc.i4.0 + IL_0020: stloc.1 + IL_0021: br.s IL_0027 + + IL_0023: ldc.i4.1 + IL_0024: stloc.1 + IL_0025: br.s IL_0027 + + IL_0027: ldloc.1 + IL_0028: ret + } // end of method PreInitDataTest::TestPreInitPrimitiveData + + .method private hidebysig static bool TestPreInitIntData() cil managed + { + // Code size 65 (0x41) + .maxstack 3 + .locals init (int32 V_0, + bool V_1, + bool V_2, + bool V_3) + IL_0000: nop + IL_0001: ldstr "Testing preinitialized int array..." + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldc.i4.0 + IL_000d: stloc.0 + IL_000e: br.s IL_002d + + IL_0010: nop + IL_0011: ldsfld int32[] PreInitData::PreInitializedIntField + IL_0016: ldloc.0 + IL_0017: ldelem.i4 + IL_0018: ldloc.0 + IL_0019: ldc.i4.1 + IL_001a: add + IL_001b: ceq + IL_001d: ldc.i4.0 + IL_001e: ceq + IL_0020: stloc.1 + IL_0021: ldloc.1 + IL_0022: brfalse.s IL_0028 + + IL_0024: ldc.i4.0 + IL_0025: stloc.2 + IL_0026: br.s IL_003f + + IL_0028: nop + IL_0029: ldloc.0 + IL_002a: ldc.i4.1 + IL_002b: add + IL_002c: stloc.0 + IL_002d: ldloc.0 + IL_002e: ldsfld int32[] PreInitData::PreInitializedIntField + IL_0033: ldlen + IL_0034: conv.i4 + IL_0035: clt + IL_0037: stloc.3 + IL_0038: ldloc.3 + IL_0039: brtrue.s IL_0010 + + IL_003b: ldc.i4.1 + IL_003c: stloc.2 + IL_003d: br.s IL_003f + + IL_003f: ldloc.2 + IL_0040: ret + } // end of method PreInitDataTest::TestPreInitIntData + +.method private hidebysig static bool TestPreInitTypeData() cil managed +{ + // Code size 191 (0xbf) + .maxstack 2 + .locals init (bool V_0, + bool V_1, + bool V_2, + bool V_3, + bool V_4) + IL_0000: nop + IL_0001: ldstr "Testing preinitialized type array..." + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldsfld valuetype [mscorlib]System.RuntimeTypeHandle[] PreInitData::PreInitializedTypeField + IL_0011: ldc.i4.0 + IL_0012: ldelema [mscorlib]System.RuntimeTypeHandle + IL_0017: ldtoken [mscorlib]System.Int32 + IL_001c: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_0021: callvirt instance valuetype [mscorlib]System.RuntimeTypeHandle [mscorlib]System.Type::get_TypeHandle() + IL_0026: call instance bool [mscorlib]System.RuntimeTypeHandle::Equals(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_002b: ldc.i4.0 + IL_002c: ceq + IL_002e: stloc.0 + IL_002f: ldloc.0 + IL_0030: brfalse.s IL_0039 + IL_0032: ldc.i4.0 + IL_0033: stloc.1 + IL_0034: br IL_00bd + IL_0039: ldsfld valuetype [mscorlib]System.RuntimeTypeHandle[] PreInitData::PreInitializedTypeField + IL_003e: ldc.i4.1 + IL_003f: ldelema [mscorlib]System.RuntimeTypeHandle + IL_0044: ldtoken [mscorlib]System.Int16 + IL_0049: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_004e: callvirt instance valuetype [mscorlib]System.RuntimeTypeHandle [mscorlib]System.Type::get_TypeHandle() + IL_0053: call instance bool [mscorlib]System.RuntimeTypeHandle::Equals(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_0058: ldc.i4.0 + IL_0059: ceq + IL_005b: stloc.2 + IL_005c: ldloc.2 + IL_005d: brfalse.s IL_0063 + IL_005f: ldc.i4.0 + IL_0060: stloc.1 + IL_0061: br.s IL_00bd + IL_0063: ldsfld valuetype [mscorlib]System.RuntimeTypeHandle[] PreInitData::PreInitializedTypeField + IL_0068: ldc.i4.2 + IL_0069: ldelema [mscorlib]System.RuntimeTypeHandle + IL_006e: ldtoken [mscorlib]System.Int64 + IL_0073: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_0078: callvirt instance valuetype [mscorlib]System.RuntimeTypeHandle [mscorlib]System.Type::get_TypeHandle() + IL_007d: call instance bool [mscorlib]System.RuntimeTypeHandle::Equals(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_0082: ldc.i4.0 + IL_0083: ceq + IL_0085: stloc.3 + IL_0086: ldloc.3 + IL_0087: brfalse.s IL_008d + IL_0089: ldc.i4.0 + IL_008a: stloc.1 + IL_008b: br.s IL_00bd + IL_008d: ldsfld valuetype [mscorlib]System.RuntimeTypeHandle[] PreInitData::PreInitializedTypeField + IL_0092: ldc.i4.3 + IL_0093: ldelema [mscorlib]System.RuntimeTypeHandle + IL_0098: ldtoken [mscorlib]System.String + IL_009d: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_00a2: callvirt instance valuetype [mscorlib]System.RuntimeTypeHandle [mscorlib]System.Type::get_TypeHandle() + IL_00a7: call instance bool [mscorlib]System.RuntimeTypeHandle::Equals(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_00ac: ldc.i4.0 + IL_00ad: ceq + IL_00af: stloc.s V_4 + IL_00b1: ldloc.s V_4 + IL_00b3: brfalse.s IL_00b9 + IL_00b5: ldc.i4.0 + IL_00b6: stloc.1 + IL_00b7: br.s IL_00bd + IL_00b9: ldc.i4.1 + IL_00ba: stloc.1 + IL_00bb: br.s IL_00bd + IL_00bd: ldloc.1 + IL_00be: ret +} // end of method PreInitDataTest::TestPreInitTypeData + + + .method private hidebysig static bool TestPreInitMethodData() cil managed + { + // Code size 92 (0x5c) + .maxstack 3 + .locals init (bool V_0, + bool V_1, + bool V_2) + IL_0000: nop + IL_0001: ldstr "Testing preinitialized method array..." + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldsfld native int[] PreInitData::PreInitializedMethodField + IL_0011: ldc.i4.0 + IL_0012: ldelem.i + IL_0013: ldnull + IL_0014: ldftn void NativeMethods::Func1(int32) + IL_001a: newobj instance void PreInitDataTest/Func1Proc::.ctor(object, + native int) + IL_001f: call native int System.Runtime.InteropServices.AddrofIntrinsics::AddrOf(!!0) + IL_0024: call bool [mscorlib]System.IntPtr::op_Inequality(native int, + native int) + IL_0029: stloc.0 + IL_002a: ldloc.0 + IL_002b: brfalse.s IL_0031 + + IL_002d: ldc.i4.0 + IL_002e: stloc.1 + IL_002f: br.s IL_005a + + IL_0031: ldsfld native int[] PreInitData::PreInitializedMethodField + IL_0036: ldc.i4.1 + IL_0037: ldelem.i + IL_0038: ldnull + IL_0039: ldftn void NativeMethods::Func2(float32) + IL_003f: newobj instance void PreInitDataTest/Func2Proc::.ctor(object, + native int) + IL_0044: call native int System.Runtime.InteropServices.AddrofIntrinsics::AddrOf(!!0) + IL_0049: call bool [mscorlib]System.IntPtr::op_Inequality(native int, + native int) + IL_004e: stloc.2 + IL_004f: ldloc.2 + IL_0050: brfalse.s IL_0056 + + IL_0052: ldc.i4.0 + IL_0053: stloc.1 + IL_0054: br.s IL_005a + + IL_0056: ldc.i4.1 + IL_0057: stloc.1 + IL_0058: br.s IL_005a + + IL_005a: ldloc.1 + IL_005b: ret + } // end of method PreInitDataTest::TestPreInitMethodData + + + .method private hidebysig static bool TestOtherStatics() cil managed + { + // Code size 64 (0x40) + .maxstack 2 + .locals init (bool V_0, + bool V_1, + bool V_2) + IL_0000: nop + IL_0001: ldstr "Testing other statics work well with preinitialize" + + "d data in the same type..." + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldsfld string PreInitData::StaticStringFieldBefore + IL_0011: ldstr "BEFORE" + IL_0016: call bool [mscorlib]System.String::op_Inequality(string, + string) + IL_001b: stloc.0 + IL_001c: ldloc.0 + IL_001d: brfalse.s IL_0023 + + IL_001f: ldc.i4.0 + IL_0020: stloc.1 + IL_0021: br.s IL_003e + + IL_0023: ldsfld string PreInitData::StaticStringFieldAfter + IL_0028: ldstr "AFTER" + IL_002d: call bool [mscorlib]System.String::op_Inequality(string, + string) + IL_0032: stloc.2 + IL_0033: ldloc.2 + IL_0034: brfalse.s IL_003a + + IL_0036: ldc.i4.0 + IL_0037: stloc.1 + IL_0038: br.s IL_003e + + IL_003a: ldc.i4.1 + IL_003b: stloc.1 + IL_003c: br.s IL_003e + + IL_003e: ldloc.1 + IL_003f: ret + } // end of method PreInitDataTest::TestOtherStatics + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method PreInitDataTest::.ctor + +} // end of class PreInitDataTest + diff --git a/external/corert/tests/src/Simple/Reflection/Reflection.cs b/external/corert/tests/src/Simple/Reflection/Reflection.cs index 79b1375609..4454e9415f 100644 --- a/external/corert/tests/src/Simple/Reflection/Reflection.cs +++ b/external/corert/tests/src/Simple/Reflection/Reflection.cs @@ -2,278 +2,520 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if MULTIMODULE_BUILD && !DEBUG +// Some tests won't work if we're using optimizing codegen, but scanner doesn't run. +// This currently happens in optimized multi-obj builds. +#define OPTIMIZED_MODE_WITHOUT_SCANNER +#endif + using System; -using System.Globalization; -using System.Reflection; using System.Runtime.CompilerServices; +using System.Reflection; -public class ReflectionTest +[assembly: TestAssembly] +[module: TestModule] + +internal class ReflectionTest { - const int Pass = 100; - const int Fail = -1; - - public static int Main() + private static int Main() { - CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + // Things I would like to test, but we don't fully support yet: + // * Interface method is reflectable if we statically called it through a constrained call + // * Delegate Invoke method is reflectable if we statically called it - if (TestNames() == Fail) - return Fail; + // + // Tests for dependency graph in the compiler + // +#if !OPTIMIZED_MODE_WITHOUT_SCANNER + TestContainment.Run(); + TestInterfaceMethod.Run(); + TestByRefLikeTypeMethod.Run(); +#endif + TestAttributeInheritance.Run(); + TestStringConstructor.Run(); + TestAssemblyAndModuleAttributes.Run(); + TestAttributeExpressions.Run(); + TestParameterAttributes.Run(); - if (TestUnification() == Fail) - return Fail; + // + // Mostly functionality tests + // + TestCreateDelegate.Run(); + TestInstanceFields.Run(); + TestReflectionInvoke.Run(); - if (TestTypeOf() == Fail) - return Fail; - - if (TestGenericComposition() == Fail) - return Fail; - - if (TestReflectionInvoke() == Fail) - return Fail; - - if (TestReflectionFieldAccess() == Fail) - return Fail; - - if (TestCreateDelegate() == Fail) - return Fail; - - return Pass; + return 100; } - private static int TestNames() + class TestReflectionInvoke { - string hello = "Hello"; - - Type stringType = hello.GetType(); - - if (stringType.FullName != "System.String") + internal class InvokeTests { - Console.WriteLine("Bad name"); - return Fail; + private string _world = "world"; + + public InvokeTests() { } + + public InvokeTests(string message) { _world = message; } + +#if OPTIMIZED_MODE_WITHOUT_SCANNER + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] +#endif + public static string GetHello(string name) + { + return "Hello " + name; + } + +#if OPTIMIZED_MODE_WITHOUT_SCANNER + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] +#endif + public static void GetHelloByRef(string name, out string result) + { + result = "Hello " + name; + } + +#if OPTIMIZED_MODE_WITHOUT_SCANNER + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] +#endif + public static string GetHelloGeneric(T obj) + { + return "Hello " + obj; + } + + +#if OPTIMIZED_MODE_WITHOUT_SCANNER + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] +#endif + public string GetHelloInstance() + { + return "Hello " + _world; + } } - return Pass; - } - - private static int TestUnification() - { - Console.WriteLine("Testing unification"); - - // ReflectionTest type doesn't have an EEType and is metadata only. - Type programType = Type.GetType("ReflectionTest"); - TypeInfo programTypeInfo = programType.GetTypeInfo(); - - Type programBaseType = programTypeInfo.BaseType; - - Type objectType = (new Object()).GetType(); - - if (!objectType.Equals(programBaseType)) + public static void Run() { - Console.WriteLine("Unification failed"); - return Fail; - } + Console.WriteLine(nameof(TestReflectionInvoke)); - return Pass; - } + // Ensure things we reflect on are in the static callgraph + if (string.Empty.Length > 0) + { + new InvokeTests().ToString(); + InvokeTests.GetHello(null); + InvokeTests.GetHelloGeneric(0); + InvokeTests.GetHelloGeneric(0); + string unused; + InvokeTests.GetHelloByRef(null, out unused); + unused.ToString(); + } - private static int TestTypeOf() - { - Console.WriteLine("Testing typeof()"); + { + MethodInfo helloMethod = typeof(InvokeTests).GetTypeInfo().GetDeclaredMethod("GetHello"); + string result = (string)helloMethod.Invoke(null, new object[] { "world" }); + if (result != "Hello world") + throw new Exception(); + } - Type intType = typeof(int); + { + MethodInfo helloGenericMethod = typeof(InvokeTests).GetTypeInfo().GetDeclaredMethod("GetHelloGeneric").MakeGenericMethod(typeof(int)); + string result = (string)helloGenericMethod.Invoke(null, new object[] { 12345 }); + if (result != "Hello 12345") + throw new Exception(); + } - if (intType.FullName != "System.Int32") - { - Console.WriteLine("Bad name"); - return Fail; - } - - if (12.GetType() != typeof(int)) - { - Console.WriteLine("Bad compare"); - return Fail; - } - - // This type only has a limited EEType (without a vtable) because it's not constructed. - if (typeof(UnallocatedType).FullName != "UnallocatedType") - { - return Fail; - } - - if (typeof(int) != typeof(int)) - { - Console.WriteLine("Bad compare"); - return Fail; - } - - return Pass; - } - - private static int TestGenericComposition() - { - Console.WriteLine("Testing generic composition"); - - Type nullableOfIntType = typeof(int?); - - string fullName = nullableOfIntType.FullName; - if (fullName.Contains("System.Nullable`1") && fullName.Contains("System.Int32")) - return Pass; - - return Fail; - } - - internal class InvokeTests - { - private string _world = "world"; - - public InvokeTests() { } - - public InvokeTests(string message) { _world = message; } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static string GetHello(string name) - { - return "Hello " + name; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static void GetHelloByRef(string name, out string result) - { - result = "Hello " + name; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static string GetHelloGeneric(T obj) - { - return "Hello " + obj; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public string GetHelloInstance() - { - return "Hello " + _world; + { + MethodInfo helloByRefMethod = typeof(InvokeTests).GetTypeInfo().GetDeclaredMethod("GetHelloByRef"); + object[] args = new object[] { "world", null }; + helloByRefMethod.Invoke(null, args); + if ((string)args[1] != "Hello world") + throw new Exception(); + } } } - private static int TestReflectionInvoke() + class TestInstanceFields { - Console.WriteLine("Testing reflection invoke"); - - // Dummy code to make sure the reflection targets are compiled. - if (String.Empty.Length > 0) + public class FieldInvokeSample { - new InvokeTests().ToString(); - InvokeTests.GetHello(null); - InvokeTests.GetHelloGeneric(0); - InvokeTests.GetHelloGeneric(0); - string unused; - InvokeTests.GetHelloByRef(null, out unused); - unused.ToString(); + public String InstanceField; } + public static void Run() { - MethodInfo helloMethod = typeof(InvokeTests).GetTypeInfo().GetDeclaredMethod("GetHello"); - string result = (string)helloMethod.Invoke(null, new object[] { "world" }); - if (result != "Hello world") - return Fail; - } + Console.WriteLine(nameof(TestInstanceFields)); - { - MethodInfo helloGenericMethod = typeof(InvokeTests).GetTypeInfo().GetDeclaredMethod("GetHelloGeneric").MakeGenericMethod(typeof(int)); - string result = (string)helloGenericMethod.Invoke(null, new object[] { 12345 }); - if (result != "Hello 12345") - return Fail; - } + TypeInfo ti = typeof(FieldInvokeSample).GetTypeInfo(); - { - MethodInfo helloGenericMethod = typeof(InvokeTests).GetTypeInfo().GetDeclaredMethod("GetHelloGeneric").MakeGenericMethod(typeof(double)); - string result = (string)helloGenericMethod.Invoke(null, new object[] { 3.14 }); - if (result != "Hello 3.14") - return Fail; - } - - { - MethodInfo helloByRefMethod = typeof(InvokeTests).GetTypeInfo().GetDeclaredMethod("GetHelloByRef"); - object[] args = new object[] { "world", null }; - helloByRefMethod.Invoke(null, args); - if ((string)args[1] != "Hello world") - return Fail; - } - - return Pass; - } - - public class FieldInvokeSample - { - public String InstanceField; - } - - private static int TestReflectionFieldAccess() - { - Console.WriteLine("Testing reflection field access"); - - if (string.Empty.Length > 0) - { - new FieldInvokeSample().ToString(); - } - - TypeInfo ti = typeof(FieldInvokeSample).GetTypeInfo(); - { FieldInfo instanceField = ti.GetDeclaredField("InstanceField"); FieldInvokeSample obj = new FieldInvokeSample(); String value = (String)(instanceField.GetValue(obj)); if (value != null) - return Fail; + throw new Exception(); obj.InstanceField = "Hi!"; value = (String)(instanceField.GetValue(obj)); if (value != "Hi!") - return Fail; + throw new Exception(); instanceField.SetValue(obj, "Bye!"); if (obj.InstanceField != "Bye!") - return Fail; + throw new Exception(); value = (String)(instanceField.GetValue(obj)); if (value != "Bye!") - return Fail; - - return Pass; + throw new Exception(); } } - delegate string GetHelloInstanceDelegate(InvokeTests o); - - private static int TestCreateDelegate() + class TestCreateDelegate { - Console.WriteLine("Testing MethodInfo.CreateDelegate"); - - // Dummy code to make sure the reflection targets are compiled. - if (String.Empty.Length > 0) + internal class Greeter { - new InvokeTests().GetHelloInstance(); - GetHelloInstanceDelegate d = null; - Func d2 = d.Invoke; - d = d2.Invoke; + private string _who; + + public Greeter(string who) { _who = who; } + +#if OPTIMIZED_MODE_WITHOUT_SCANNER + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] +#endif + public string Greet() + { + return "Hello " + _who; + } } - TypeInfo ti = typeof(InvokeTests).GetTypeInfo(); - MethodInfo mi = ti.GetDeclaredMethod("GetHelloInstance"); - { - var d = (GetHelloInstanceDelegate)mi.CreateDelegate(typeof(GetHelloInstanceDelegate)); - if (d(new InvokeTests("mom")) != "Hello mom") - return Fail; - } + delegate string GetHelloInstanceDelegate(Greeter o); + public static void Run() { - var d = (Func)mi.CreateDelegate(typeof(Func)); - if (d(new InvokeTests("pop")) != "Hello pop") - return Fail; - } + Console.WriteLine(nameof(TestCreateDelegate)); - return Pass; + // Ensure things we reflect on are in the static callgraph + if (string.Empty.Length > 0) + { + new Greeter(null).Greet(); + GetHelloInstanceDelegate d = null; + Func d2 = d.Invoke; + d = d2.Invoke; + } + + TypeInfo ti = typeof(Greeter).GetTypeInfo(); + MethodInfo mi = ti.GetDeclaredMethod(nameof(Greeter.Greet)); + { + var d = (GetHelloInstanceDelegate)mi.CreateDelegate(typeof(GetHelloInstanceDelegate)); + if (d(new Greeter("mom")) != "Hello mom") + throw new Exception(); + } + + { + var d = (Func)mi.CreateDelegate(typeof(Func)); + if (d(new Greeter("pop")) != "Hello pop") + throw new Exception(); + } + } } + + class TestParameterAttributes + { +#if OPTIMIZED_MODE_WITHOUT_SCANNER + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] +#endif + public static bool Method([Parameter] ParameterType parameter) + { + return parameter == null; + } + + public class ParameterType { } + + class ParameterAttribute : Attribute + { + public ParameterAttribute([CallerMemberName] string memberName = null) + { + MemberName = memberName; + } + + public string MemberName { get; } + } + + public static void Run() + { + Console.WriteLine(nameof(TestParameterAttributes)); + + // Ensure things we reflect on are in the static callgraph + if (string.Empty.Length > 0) + { + Method(null); + } + + MethodInfo method = typeof(TestParameterAttributes).GetMethod(nameof(Method)); + + var attribute = method.GetParameters()[0].GetCustomAttribute(); + if (attribute.MemberName != nameof(Method)) + throw new Exception(); + } + } + + class TestAttributeExpressions + { + struct FirstNeverUsedType { } + + struct SecondNeverUsedType { } + + class Gen { } + + class TypeAttribute : Attribute + { + public Type SomeType { get; set; } + + public TypeAttribute() { } + public TypeAttribute(Type someType) + { + SomeType = someType; + } + } + + enum MyEnum { } + + class EnumArrayAttribute : Attribute + { + public MyEnum[] EnumArray; + } + + [Type(typeof(FirstNeverUsedType*[,]))] + class Holder1 { } + + [Type(SomeType = typeof(Gen))] + class Holder2 { } + + [EnumArray(EnumArray = new MyEnum[] { 0 })] + class Holder3 { } + + public static void Run() + { + Console.WriteLine(nameof(TestAttributeExpressions)); + + TypeAttribute attr1 = typeof(Holder1).GetCustomAttribute(); + if (attr1.SomeType.ToString() != "ReflectionTest+TestAttributeExpressions+FirstNeverUsedType*[,]") + throw new Exception(); + + TypeAttribute attr2 = typeof(Holder2).GetCustomAttribute(); + if (attr2.SomeType.ToString() != "ReflectionTest+TestAttributeExpressions+Gen`1[ReflectionTest+TestAttributeExpressions+SecondNeverUsedType]") + throw new Exception(); + + EnumArrayAttribute attr3 = typeof(Holder3).GetCustomAttribute(); + if (attr3.EnumArray[0] != 0) + throw new Exception(); + } + } + + class TestAssemblyAndModuleAttributes + { + public static void Run() + { + Console.WriteLine(nameof(TestAssemblyAndModuleAttributes)); + + // Also tests GetExecutingAssembly + var assAttr = Assembly.GetExecutingAssembly().GetCustomAttribute(); + if (assAttr == null) + throw new Exception(); + + // Also tests GetEntryAssembly + var modAttr = Assembly.GetEntryAssembly().ManifestModule.GetCustomAttribute(); + if (modAttr == null) + throw new Exception(); + } + } + + class TestStringConstructor + { + public static void Run() + { + Console.WriteLine(nameof(TestStringConstructor)); + + // Ensure things we reflect on are in the static callgraph + if (string.Empty.Length > 0) + { + new string(new char[] { }, 0, 0); + } + + ConstructorInfo ctor = typeof(string).GetConstructor(new Type[] { typeof(char[]), typeof(int), typeof(int) }); + object str = ctor.Invoke(new object[] { new char[] { 'a' }, 0, 1 }); + if ((string)str != "a") + throw new Exception(); + } + } + + class TestAttributeInheritance + { + class BaseAttribute : Attribute + { + public string Field; + public int Property { get; set; } + } + + class DerivedAttribute : BaseAttribute { } + + [Derived(Field = "Hello", Property = 100)] + class TestType { } + + public static void Run() + { + Console.WriteLine(nameof(TestAttributeInheritance)); + + DerivedAttribute attr = typeof(TestType).GetCustomAttribute(); + if (attr.Field != "Hello" || attr.Property != 100) + throw new Exception(); + } + } + + class TestByRefLikeTypeMethod + { + ref struct ByRefLike + { + public readonly int Value; + + public ByRefLike(int value) + { + Value = value; + } + + public override string ToString() + { + return Value.ToString(); + } + } + + delegate string ToStringDelegate(ref ByRefLike thisObj); + + public static void Run() + { + Console.WriteLine(nameof(TestByRefLikeTypeMethod)); + + // Ensure things we reflect on are in the static callgraph + if (string.Empty.Length > 0) + { + default(ByRefLike).ToString(); + ToStringDelegate s = null; + s = s.Invoke; + } + + Type byRefLikeType = GetTestType(nameof(TestByRefLikeTypeMethod), nameof(ByRefLike)); + MethodInfo toStringMethod = byRefLikeType.GetMethod("ToString"); + var toString = (ToStringDelegate)toStringMethod.CreateDelegate(typeof(ToStringDelegate)); + + ByRefLike foo = new ByRefLike(123); + if (toString(ref foo) != "123") + throw new Exception(); + } + } + + class TestInterfaceMethod + { + interface IFoo + { + string Frob(int x); + } + + class Foo : IFoo + { + public string Frob(int x) + { + return x.ToString(); + } + } + + public static void Run() + { + Console.WriteLine(nameof(TestInterfaceMethod)); + + // Ensure things we reflect on are in the static callgraph + if (string.Empty.Length > 0) + { + ((IFoo)new Foo()).Frob(1); + } + + object result = InvokeTestMethod(typeof(IFoo), "Frob", new Foo(), 42); + if ((string)result != "42") + throw new Exception(); + } + } + + class TestContainment + { + class NeverUsedContainerType + { + public class UsedNestedType + { + public static int CallMe() + { + return 42; + } + } + } + + + public static void Run() + { + Console.WriteLine(nameof(TestContainment)); + + // Ensure things we reflect on are in the static callgraph + if (string.Empty.Length > 0) + { + NeverUsedContainerType.UsedNestedType.CallMe(); + } + + Type neverUsedContainerType = GetTestType(nameof(TestContainment), nameof(NeverUsedContainerType)); + Type usedNestedType = neverUsedContainerType.GetNestedType(nameof(NeverUsedContainerType.UsedNestedType)); + + // Since we called CallMe, it has reflection metadata and it is invokable + object o = InvokeTestMethod(usedNestedType, nameof(NeverUsedContainerType.UsedNestedType.CallMe)); + if ((int)o != 42) + throw new Exception(); + + // We can get a type handle for the nested type (the invoke mapping table needs it) + if (!HasTypeHandle(usedNestedType)) + throw new Exception($"{nameof(NeverUsedContainerType.UsedNestedType)} should have an EEType"); + + // But the containing type doesn't need an EEType + if (HasTypeHandle(neverUsedContainerType)) + throw new Exception($"{nameof(NeverUsedContainerType)} should not have an EEType"); + } + } + + #region Helpers + + private static Type GetTestType(string testName, string typeName) + { + string fullTypeName = $"{nameof(ReflectionTest)}+{testName}+{typeName}"; + Type result = Type.GetType(fullTypeName); + if (result == null) + throw new Exception($"'{fullTypeName}' could not be located"); + return result; + } + + private static object InvokeTestMethod(Type type, string methodName, object thisObj = null, params object[] param) + { + MethodInfo method = type.GetMethod(methodName); + if (method == null) + throw new Exception($"Method '{methodName}' not found on type {type}"); + + return method.Invoke(thisObj, param); + } + + private static bool HasTypeHandle(Type type) + { + try + { + RuntimeTypeHandle typeHandle = type.TypeHandle; + } + catch (Exception) + { + return false; + } + return true; + } + + #endregion } -class UnallocatedType { } +class TestAssemblyAttribute : Attribute { } +class TestModuleAttribute : Attribute { } diff --git a/external/corert/tests/src/Simple/Reflection/Reflection.csproj b/external/corert/tests/src/Simple/Reflection/Reflection.csproj index 0948ddf81c..45ebce05d6 100644 --- a/external/corert/tests/src/Simple/Reflection/Reflection.csproj +++ b/external/corert/tests/src/Simple/Reflection/Reflection.csproj @@ -1,4 +1,4 @@ - + diff --git a/external/corert/tests/src/Simple/SimpleTest.targets b/external/corert/tests/src/Simple/SimpleTest.targets index 6168776ed4..aae9489711 100644 --- a/external/corert/tests/src/Simple/SimpleTest.targets +++ b/external/corert/tests/src/Simple/SimpleTest.targets @@ -1,19 +1,30 @@ - + Exe $(MSBuildProjectDirectory)\bin\$(Configuration)\$(Platform)\ $(MSBuildProjectDirectory)\obj\$(Configuration)\$(Platform)\ + portable - $(MSBuildThisFileDirectory)\project.json - $(MSBuildThisFileDirectory)\project.lock.json false false true - .NETStandard,Version=v1.6 + .NETCoreApp + netcoreapp2.0 + + + + + $(MicrosoftNETCoreAppPackageVersion) + + + + + + $(IntermediateOutputRootPath)\repro - + <_ResolvedProjectReferencePaths Remove="@(ObjFiles)" /> @@ -33,15 +44,19 @@ - - + + + MULTIMODULE_BUILD;$(DefineConstants) + + + diff --git a/external/corert/tests/src/Simple/Threading/Threading.cs b/external/corert/tests/src/Simple/Threading/Threading.cs index 658b6fd206..99d6b610ed 100644 --- a/external/corert/tests/src/Simple/Threading/Threading.cs +++ b/external/corert/tests/src/Simple/Threading/Threading.cs @@ -4,7 +4,9 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Threading; +using System.Threading.Tasks; // TODO: Move these tests to CoreFX once they can be run on CoreRT @@ -14,6 +16,9 @@ internal static class Runner public static int Main() { + Console.WriteLine(" WaitSubsystemTests.DoubleSetOnEventWithTimedOutWaiterShouldNotStayInWaitersList"); + WaitSubsystemTests.DoubleSetOnEventWithTimedOutWaiterShouldNotStayInWaitersList(); + Console.WriteLine(" WaitSubsystemTests.ManualResetEventTest"); WaitSubsystemTests.ManualResetEventTest(); @@ -33,6 +38,80 @@ internal static class Runner //Console.WriteLine(" WaitSubsystemTests.MutexMaximumReacquireCountTest"); //WaitSubsystemTests.MutexMaximumReacquireCountTest(); + Console.WriteLine(" ThreadPoolTests.RunProcessorCountItemsInParallel"); + ThreadPoolTests.RunProcessorCountItemsInParallel(); + + Console.WriteLine(" ThreadPoolTests.RunMoreThanMaxJobsMakesOneJobWaitForStarvationDetection"); + ThreadPoolTests.RunMoreThanMaxJobsMakesOneJobWaitForStarvationDetection(); + + Console.WriteLine(" ThreadPoolTests.ThreadPoolCanPickUpOneJobWhenThreadIsAvailable"); + ThreadPoolTests.ThreadPoolCanPickUpOneJobWhenThreadIsAvailable(); + + Console.WriteLine(" ThreadPoolTests.ThreadPoolCanPickUpMultipleJobsWhenThreadsAreAvailable"); + ThreadPoolTests.ThreadPoolCanPickUpMultipleJobsWhenThreadsAreAvailable(); + + // This test takes a long time to run (min 42 seconds sleeping). Enable for manual testing. + // Console.WriteLine(" ThreadPoolTests.RunJobsAfterThreadTimeout"); + // ThreadPoolTests.RunJobsAfterThreadTimeout(); + + Console.WriteLine(" ThreadPoolTests.WorkQueueDepletionTest"); + ThreadPoolTests.WorkQueueDepletionTest(); + + Console.WriteLine(" ThreadPoolTests.WorkerThreadStateReset"); + ThreadPoolTests.WorkerThreadStateReset(); + + // This test is not applicable (and will not pass) on Windows since it uses the Windows OS-provided thread pool. + // Console.WriteLine(" ThreadPoolTests.SettingMinThreadsWillCreateThreadsUpToMinimum"); + // ThreadPoolTests.SettingMinThreadsWillCreateThreadsUpToMinimum(); + + Console.WriteLine(" WaitThreadTests.SignalingRegisteredHandleCallsCalback"); + WaitThreadTests.SignalingRegisteredHandleCallsCalback(); + + Console.WriteLine(" WaitThreadTests.TimingOutRegisteredHandleCallsCallback"); + WaitThreadTests.TimingOutRegisteredHandleCallsCallback(); + + Console.WriteLine(" WaitThreadTests.UnregisteringBeforeSignalingDoesNotCallCallback"); + WaitThreadTests.UnregisteringBeforeSignalingDoesNotCallCallback(); + + Console.WriteLine(" WaitThreadTests.RepeatingWaitFiresUntilUnregistered"); + WaitThreadTests.RepeatingWaitFiresUntilUnregistered(); + + Console.WriteLine(" WaitThreadTests.UnregisterEventSignaledWhenUnregistered"); + WaitThreadTests.UnregisterEventSignaledWhenUnregistered(); + + Console.WriteLine(" WaitThreadTests.CanRegisterMoreThan64Waits"); + WaitThreadTests.CanRegisterMoreThan64Waits(); + + Console.WriteLine(" WaitThreadTests.StateIsPasssedThroughToCallback"); + WaitThreadTests.StateIsPasssedThroughToCallback(); + + + // This test takes a long time to run. Enable for manual testing. + // Console.WriteLine(" WaitThreadTests.WaitWithLongerTimeoutThanWaitThreadCanStillTimeout"); + // WaitThreadTests.WaitWithLongerTimeoutThanWaitThreadCanStillTimeout(); + + Console.WriteLine(" WaitThreadTests.UnregisterCallbackIsNotCalledAfterCallbackFinishesIfAnotherCallbackOnSameWaitRunning"); + WaitThreadTests.UnregisterCallbackIsNotCalledAfterCallbackFinishesIfAnotherCallbackOnSameWaitRunning(); + + Console.WriteLine(" WaitThreadTests.CallingUnregisterOnAutomaticallyUnregisteredHandleReturnsTrue"); + WaitThreadTests.CallingUnregisterOnAutomaticallyUnregisteredHandleReturnsTrue(); + + Console.WriteLine(" WaitThreadTests.EventSetAfterUnregisterNotObservedOnWaitThread"); + WaitThreadTests.EventSetAfterUnregisterNotObservedOnWaitThread(); + + Console.WriteLine(" WaitThreadTests.BlockingUnregister"); + WaitThreadTests.BlockingUnregister(); + + Console.WriteLine(" WaitThreadTests.CanDisposeEventAfterUnblockingUnregister"); + WaitThreadTests.CanDisposeEventAfterUnblockingUnregister(); + + Console.WriteLine(" WaitThreadTests.UnregisterEventSignaledWhenUnregisteredEvenIfAutoUnregistered"); + WaitThreadTests.UnregisterEventSignaledWhenUnregisteredEvenIfAutoUnregistered(); + + + Console.WriteLine(" WaitThreadTests.BlockingUnregisterBlocksEvenIfCallbackExecuting"); + WaitThreadTests.BlockingUnregisterBlocksEvenIfCallbackExecuting(); + return Pass; } } @@ -390,6 +469,28 @@ internal static class WaitSubsystemTests } } + // There is a race condition between a timed out WaitOne and a Set call not clearing the waiters list + // in the wait subsystem (Unix only). More information can be found at + // https://github.com/dotnet/corert/issues/3616 and https://github.com/dotnet/corert/pull/3782. + [Fact] + public static void DoubleSetOnEventWithTimedOutWaiterShouldNotStayInWaitersList() + { + AutoResetEvent threadStartedEvent = new AutoResetEvent(false); + AutoResetEvent resetEvent = new AutoResetEvent(false); + Thread thread = new Thread(() => { + threadStartedEvent.Set(); + Thread.Sleep(50); + resetEvent.Set(); + resetEvent.Set(); + }); + + thread.IsBackground = true; + thread.Start(); + threadStartedEvent.WaitOne(ThreadTestHelpers.UnexpectedTimeoutMilliseconds); + resetEvent.WaitOne(50); + thread.Join(ThreadTestHelpers.UnexpectedTimeoutMilliseconds); + } + [Fact] public static void MutexTest() { @@ -733,11 +834,502 @@ internal static class WaitSubsystemTests } } +internal static class ThreadPoolTests +{ + [Fact] + public static void RunProcessorCountItemsInParallel() + { + int count = 0; + AutoResetEvent e0 = new AutoResetEvent(false); + for(int i = 0; i < Environment.ProcessorCount; ++i) + { + ThreadPool.QueueUserWorkItem( _ => { + if(Interlocked.Increment(ref count) == Environment.ProcessorCount) + { + e0.Set(); + } + }); + } + e0.CheckedWait(); + // Run the test again to make sure we can reuse the threads. + count = 0; + for(int i = 0; i < Environment.ProcessorCount; ++i) + { + ThreadPool.QueueUserWorkItem( _ => { + if(Interlocked.Increment(ref count) == Environment.ProcessorCount) + { + e0.Set(); + } + }); + } + e0.CheckedWait(); + } + + [Fact] + public static void RunMoreThanMaxJobsMakesOneJobWaitForStarvationDetection() + { + ManualResetEvent e0 = new ManualResetEvent(false); + AutoResetEvent jobsQueued = new AutoResetEvent(false); + int count = 0; + AutoResetEvent e1 = new AutoResetEvent(false); + for(int i = 0; i < Environment.ProcessorCount; ++i) + { + ThreadPool.QueueUserWorkItem( _ => { + if(Interlocked.Increment(ref count) == Environment.ProcessorCount) + { + jobsQueued.Set(); + } + e0.CheckedWait(); + }); + } + jobsQueued.CheckedWait(); + ThreadPool.QueueUserWorkItem( _ => e1.Set()); + Thread.Sleep(500); // Sleep for the gate thread delay to wait for starvation + e1.CheckedWait(); + e0.Set(); + } + + [Fact] + public static void ThreadPoolCanPickUpOneJobWhenThreadIsAvailable() + { + ManualResetEvent e0 = new ManualResetEvent(false); + AutoResetEvent jobsQueued = new AutoResetEvent(false); + AutoResetEvent testJobCompleted = new AutoResetEvent(false); + int count = 0; + + for(int i = 0; i < Environment.ProcessorCount - 1; ++i) + { + ThreadPool.QueueUserWorkItem( _ => { + if(Interlocked.Increment(ref count) == Environment.ProcessorCount - 1) + { + jobsQueued.Set(); + } + e0.CheckedWait(); + }); + } + jobsQueued.CheckedWait(); + ThreadPool.QueueUserWorkItem( _ => testJobCompleted.Set()); + testJobCompleted.CheckedWait(); + e0.Set(); + } + + [Fact] + public static void ThreadPoolCanPickUpMultipleJobsWhenThreadsAreAvailable() + { + ManualResetEvent e0 = new ManualResetEvent(false); + AutoResetEvent jobsQueued = new AutoResetEvent(false); + AutoResetEvent testJobCompleted = new AutoResetEvent(false); + int count = 0; + + for(int i = 0; i < Environment.ProcessorCount - 1; ++i) + { + ThreadPool.QueueUserWorkItem( _ => { + if(Interlocked.Increment(ref count) == Environment.ProcessorCount - 1) + { + jobsQueued.Set(); + } + e0.CheckedWait(); + }); + } + jobsQueued.CheckedWait(); + int testJobsCount = 0; + int maxCount = 5; + void Job(object _) + { + if(Interlocked.Increment(ref testJobsCount) != maxCount) + { + ThreadPool.QueueUserWorkItem(Job); + } + else + { + testJobCompleted.Set(); + } + } + ThreadPool.QueueUserWorkItem(Job); + testJobCompleted.CheckedWait(); + e0.Set(); + } + + private static WaitCallback CreateRecursiveJob(int jobCount, int targetJobCount, AutoResetEvent testJobCompleted) + { + return _ => + { + if (jobCount == targetJobCount) + { + testJobCompleted.Set(); + } + else + { + ThreadPool.QueueUserWorkItem(CreateRecursiveJob(jobCount + 1, targetJobCount, testJobCompleted)); + } + }; + } + + [Fact] + [OuterLoop] + public static void RunJobsAfterThreadTimeout() + { + ManualResetEvent e0 = new ManualResetEvent(false); + AutoResetEvent jobsQueued = new AutoResetEvent(false); + AutoResetEvent testJobCompleted = new AutoResetEvent(false); + int count = 0; + + for(int i = 0; i < Environment.ProcessorCount - 1; ++i) + { + ThreadPool.QueueUserWorkItem( _ => { + if(Interlocked.Increment(ref count) == Environment.ProcessorCount - 1) + { + jobsQueued.Set(); + } + e0.CheckedWait(); + }); + } + jobsQueued.CheckedWait(); + ThreadPool.QueueUserWorkItem( _ => testJobCompleted.Set()); + testJobCompleted.CheckedWait(); + Console.Write("Sleeping to time out thread\n"); + Thread.Sleep(21000); + ThreadPool.QueueUserWorkItem( _ => testJobCompleted.Set()); + testJobCompleted.CheckedWait(); + e0.Set(); + Console.Write("Sleeping to time out all threads\n"); + Thread.Sleep(21000); + ThreadPool.QueueUserWorkItem( _ => testJobCompleted.Set()); + testJobCompleted.CheckedWait(); + } + + [Fact] + public static void WorkQueueDepletionTest() + { + ManualResetEvent e0 = new ManualResetEvent(false); + int numLocalScheduled = 1; + int numGlobalScheduled = 1; + int numToSchedule = Environment.ProcessorCount * 64; + int numCompleted = 0; + object syncRoot = new object(); + void ThreadLocalJob() + { + if(Interlocked.Increment(ref numLocalScheduled) <= numToSchedule) + { + Task.Factory.StartNew(ThreadLocalJob); + } + if(Interlocked.Increment(ref numLocalScheduled) <= numToSchedule) + { + Task.Factory.StartNew(ThreadLocalJob); + } + if (Interlocked.Increment(ref numCompleted) == numToSchedule * 2) + { + e0.Set(); + } + } + void GlobalJob(object _) + { + if(Interlocked.Increment(ref numGlobalScheduled) <= numToSchedule) + { + ThreadPool.QueueUserWorkItem(GlobalJob); + } + if(Interlocked.Increment(ref numGlobalScheduled) <= numToSchedule) + { + ThreadPool.QueueUserWorkItem(GlobalJob); + } + if (Interlocked.Increment(ref numCompleted) == numToSchedule * 2) + { + e0.Set(); + } + } + Task.Factory.StartNew(ThreadLocalJob); + ThreadPool.QueueUserWorkItem(GlobalJob); + e0.CheckedWait(); + } + + [Fact] + public static void WorkerThreadStateReset() + { + var cultureInfo = new CultureInfo("pt-BR"); + var expectedCultureInfo = CultureInfo.CurrentCulture; + var expectedUICultureInfo = CultureInfo.CurrentUICulture; + int count = 0; + AutoResetEvent e0 = new AutoResetEvent(false); + for(int i = 0; i < Environment.ProcessorCount; ++i) + { + ThreadPool.QueueUserWorkItem( _ => { + CultureInfo.CurrentCulture = cultureInfo; + CultureInfo.CurrentUICulture = cultureInfo; + Thread.CurrentThread.Priority = ThreadPriority.AboveNormal; + if(Interlocked.Increment(ref count) == Environment.ProcessorCount) + { + e0.Set(); + } + }); + } + e0.CheckedWait(); + // Run the test again to make sure we can reuse the threads. + count = 0; + for(int i = 0; i < Environment.ProcessorCount; ++i) + { + ThreadPool.QueueUserWorkItem( _ => { + Assert.Equal(expectedCultureInfo, CultureInfo.CurrentCulture); + Assert.Equal(expectedUICultureInfo, CultureInfo.CurrentUICulture); + Assert.Equal(ThreadPriority.Normal, Thread.CurrentThread.Priority); + if(Interlocked.Increment(ref count) == Environment.ProcessorCount) + { + e0.Set(); + } + }); + } + e0.CheckedWait(); + } + + [Fact] + public static void SettingMinThreadsWillCreateThreadsUpToMinimum() + { + ThreadPool.GetMinThreads(out int minThreads, out int unusedMin); + ThreadPool.GetMaxThreads(out int maxThreads, out int unusedMax); + try + { + ManualResetEvent e0 = new ManualResetEvent(false); + AutoResetEvent jobsQueued = new AutoResetEvent(false); + int count = 0; + ThreadPool.SetMaxThreads(minThreads, unusedMax); + for(int i = 0; i < minThreads + 1; ++i) + { + ThreadPool.QueueUserWorkItem( _ => { + if(Interlocked.Increment(ref count) == minThreads + 1) + { + jobsQueued.Set(); + } + e0.CheckedWait(); + }); + } + Assert.False(jobsQueued.WaitOne(ThreadTestHelpers.ExpectedTimeoutMilliseconds)); + Assert.True(ThreadPool.SetMaxThreads(minThreads + 1, unusedMax)); + Assert.True(ThreadPool.SetMinThreads(minThreads + 1, unusedMin)); + + jobsQueued.CheckedWait(); + + e0.Set(); + } + finally + { + ThreadPool.SetMinThreads(minThreads, unusedMin); + ThreadPool.SetMaxThreads(maxThreads, unusedMax); + } + } +} + +internal static class WaitThreadTests +{ + private const int WaitThreadTimeoutTimeMs = 20000; + + [Fact] + public static void SignalingRegisteredHandleCallsCalback() + { + var e0 = new AutoResetEvent(false); + var e1 = new AutoResetEvent(false); + ThreadPool.RegisterWaitForSingleObject(e0, (_, timedOut) => + { + if(!timedOut) + { + e1.Set(); + } + }, null, ThreadTestHelpers.UnexpectedTimeoutMilliseconds, true); + e0.Set(); + e1.CheckedWait(); + } + + [Fact] + public static void TimingOutRegisteredHandleCallsCallback() + { + var e0 = new AutoResetEvent(false); + var e1 = new AutoResetEvent(false); + ThreadPool.RegisterWaitForSingleObject(e0, (_, timedOut) => + { + if(timedOut) + { + e1.Set(); + } + }, null, ThreadTestHelpers.ExpectedTimeoutMilliseconds, true); + e1.CheckedWait(); + } + + [Fact] + public static void UnregisteringBeforeSignalingDoesNotCallCallback() + { + var e0 = new AutoResetEvent(false); + var e1 = new AutoResetEvent(false); + var registeredWaitHandle = ThreadPool.RegisterWaitForSingleObject(e0, (_, __) => + { + e1.Set(); + }, null, ThreadTestHelpers.UnexpectedTimeoutMilliseconds, true); + registeredWaitHandle.Unregister(null); + Assert.False(e1.WaitOne(ThreadTestHelpers.ExpectedTimeoutMilliseconds)); + } + + [Fact] + public static void RepeatingWaitFiresUntilUnregistered() + { + var e0 = new AutoResetEvent(false); + var e1 = new AutoResetEvent(false); + var registered = ThreadPool.RegisterWaitForSingleObject(e0, (_, __) => + { + e1.Set(); + }, null, ThreadTestHelpers.UnexpectedTimeoutMilliseconds, false); + for (int i = 0; i < 4; ++i) + { + e0.Set(); + e1.CheckedWait(); + } + registered.Unregister(null); + e0.Set(); + Assert.False(e1.WaitOne(ThreadTestHelpers.ExpectedTimeoutMilliseconds)); + } + + [Fact] + public static void UnregisterEventSignaledWhenUnregistered() + { + var e0 = new AutoResetEvent(false); + var e1 = new AutoResetEvent(false); + var registered = ThreadPool.RegisterWaitForSingleObject(e0, (_, __) => {}, null, ThreadTestHelpers.UnexpectedTimeoutMilliseconds, true); + registered.Unregister(e1); + e1.CheckedWait(); + } + + [Fact] + public static void CanRegisterMoreThan64Waits() + { + RegisteredWaitHandle[] handles = new RegisteredWaitHandle[65]; + for(int i = 0; i < 65; ++i) { + handles[i] = ThreadPool.RegisterWaitForSingleObject(new AutoResetEvent(false), (_, __) => {}, null, -1, true); + } + for(int i = 0; i < 65; ++i) { + handles[i].Unregister(null); + } + } + + [Fact] + public static void StateIsPasssedThroughToCallback() + { + object state = new object(); + AutoResetEvent e0 = new AutoResetEvent(false); + ThreadPool.RegisterWaitForSingleObject(new AutoResetEvent(true), (callbackState, _) => + { + if(state == callbackState) + { + e0.Set(); + } + }, state, 0, true); + e0.CheckedWait(); + } + + [Fact] + [OuterLoop] + public static void WaitWithLongerTimeoutThanWaitThreadCanStillTimeout() + { + AutoResetEvent e0 = new AutoResetEvent(false); + ThreadPool.RegisterWaitForSingleObject(new AutoResetEvent(false), (_, __) => e0.Set(), null, WaitThreadTimeoutTimeMs + 1000, true); + Thread.Sleep(WaitThreadTimeoutTimeMs); + e0.CheckedWait(); + } + + [Fact] + public static void UnregisterCallbackIsNotCalledAfterCallbackFinishesIfAnotherCallbackOnSameWaitRunning() + { + AutoResetEvent e0 = new AutoResetEvent(false); + AutoResetEvent e1 = new AutoResetEvent(false); + AutoResetEvent e2 = new AutoResetEvent(false); + RegisteredWaitHandle handle = ThreadPool.RegisterWaitForSingleObject(e0, (_, __) => + { + e2.WaitOne(ThreadTestHelpers.UnexpectedTimeoutMilliseconds); + }, null, ThreadTestHelpers.UnexpectedTimeoutMilliseconds, false); + e0.Set(); + Thread.Sleep(50); + e0.Set(); + Thread.Sleep(50); + handle.Unregister(e1); + Assert.False(e1.WaitOne(ThreadTestHelpers.ExpectedTimeoutMilliseconds)); + e2.Set(); + } + + [Fact] + public static void CallingUnregisterOnAutomaticallyUnregisteredHandleReturnsTrue() + { + AutoResetEvent e0 = new AutoResetEvent(false); + RegisteredWaitHandle handle = ThreadPool.RegisterWaitForSingleObject(e0, (_, __) => {}, null, ThreadTestHelpers.UnexpectedTimeoutMilliseconds, true); + e0.Set(); + Thread.Sleep(ThreadTestHelpers.ExpectedTimeoutMilliseconds); + Assert.True(handle.Unregister(null)); + } + + [Fact] + public static void EventSetAfterUnregisterNotObservedOnWaitThread() + { + AutoResetEvent e0 = new AutoResetEvent(false); + RegisteredWaitHandle handle = ThreadPool.RegisterWaitForSingleObject(e0, (_, __) => {}, null, ThreadTestHelpers.UnexpectedTimeoutMilliseconds, true); + handle.Unregister(null); + e0.Set(); + e0.CheckedWait(); + } + + [Fact] + public static void BlockingUnregister() + { + RegisteredWaitHandle handle = ThreadPool.RegisterWaitForSingleObject(new AutoResetEvent(false), (_, __) => {}, null, ThreadTestHelpers.UnexpectedTimeoutMilliseconds, true); + handle.Unregister(new InvalidWaitHandle()); + } + + [Fact] + public static void CanDisposeEventAfterUnblockingUnregister() + { + using(var e0 = new AutoResetEvent(false)) + { + RegisteredWaitHandle handle = ThreadPool.RegisterWaitForSingleObject(e0, (_, __) => {}, null, ThreadTestHelpers.UnexpectedTimeoutMilliseconds, true); + handle.Unregister(null); + } + } + + [Fact] + public static void UnregisterEventSignaledWhenUnregisteredEvenIfAutoUnregistered() + { + var e0 = new AutoResetEvent(false); + RegisteredWaitHandle handle = ThreadPool.RegisterWaitForSingleObject(e0, (_, __) => {}, null, ThreadTestHelpers.UnexpectedTimeoutMilliseconds, true); + e0.Set(); + Thread.Sleep(50); // Ensure the callback has happened + var e1 = new AutoResetEvent(false); + handle.Unregister(e1); + e1.CheckedWait(); + } + + [Fact] + public static void BlockingUnregisterBlocksEvenIfCallbackExecuting() + { + bool callbackComplete = false; + var e0 = new AutoResetEvent(false); + RegisteredWaitHandle handle = ThreadPool.RegisterWaitForSingleObject(e0, (_, __) => + { + Thread.Sleep(300); + callbackComplete = true; + }, null, ThreadTestHelpers.UnexpectedTimeoutMilliseconds, true); + e0.Set(); + Thread.Sleep(100); // Give the wait thread time to process removals. + handle.Unregister(new InvalidWaitHandle()); + Assert.True(callbackComplete); + } +} + internal static class ThreadTestHelpers { public const int ExpectedTimeoutMilliseconds = 50; public const int ExpectedMeasurableTimeoutMilliseconds = 500; public const int UnexpectedTimeoutMilliseconds = 1000 * 30; + + public static void CheckedWait(this WaitHandle wh) + { + Assert.True(wh.WaitOne(UnexpectedTimeoutMilliseconds)); + } +} + +internal sealed class InvalidWaitHandle : WaitHandle +{ } internal sealed class Stopwatch diff --git a/external/corert/tests/src/Simple/Threading/Threading.csproj b/external/corert/tests/src/Simple/Threading/Threading.csproj index 0948ddf81c..45ebce05d6 100644 --- a/external/corert/tests/src/Simple/Threading/Threading.csproj +++ b/external/corert/tests/src/Simple/Threading/Threading.csproj @@ -1,4 +1,4 @@ - + diff --git a/external/corert/tests/src/Simple/project.json b/external/corert/tests/src/Simple/project.json deleted file mode 100644 index 59dba3394c..0000000000 --- a/external/corert/tests/src/Simple/project.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "dependencies": { - "NETStandard.Library": "1.6.1", - "System.Threading.Thread": "4.4.0-beta-24906-01" - }, - "frameworks": { - "netstandard1.6": {} - } -} diff --git a/external/corert/tests/testenv.cmd b/external/corert/tests/testenv.cmd index dd797b8933..28b0cc35c2 100644 --- a/external/corert/tests/testenv.cmd +++ b/external/corert/tests/testenv.cmd @@ -1,4 +1,4 @@ -@echo off +@if not defined _echo @echo off :Arg_Loop if "%1" == "" goto :ArgsDone @@ -6,6 +6,7 @@ if /i "%1" == "/?" goto :Usage if /i "%1" == "x64" (set CoreRT_BuildArch=x64&&shift&goto Arg_Loop) if /i "%1" == "x86" (set CoreRT_BuildArch=x86&&shift&goto Arg_Loop) if /i "%1" == "arm" (set CoreRT_BuildArch=arm&&shift&goto Arg_Loop) +if /i "%1" == "wasm" (set CoreRT_BuildArch=wasm&&shift&goto Arg_Loop) if /i "%1" == "dbg" (set CoreRT_BuildType=Debug&shift&goto Arg_Loop) if /i "%1" == "debug" (set CoreRT_BuildType=Debug&shift&goto Arg_Loop) @@ -16,10 +17,12 @@ if /i "%1" == "release" (set CoreRT_BuildType=Release&shift&goto Arg_Loop) set CoreRT_BuildOS=Windows_NT -set CoreRT_ToolchainDir=%~dp0\..\bin\Product\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType%\packaging\publish1 +if /i "%CoreRT_BuildArch%" == "wasm" (set CoreRT_BuildOS=WebAssembly) + +set CoreRT_ToolchainDir=%~dp0\..\bin\%CoreRT_BuildOS%.%CoreRT_BuildArch%.%CoreRT_BuildType% exit /b 0 :Usage -echo %0 [arch: x64/x86/arm] [flavor: debug/release] +echo %0 [arch: x64/x86/arm/wasm] [flavor: debug/release] exit /b 1 diff --git a/external/corert/tests/testenv.sh b/external/corert/tests/testenv.sh index f324fba421..69a636bcae 100755 --- a/external/corert/tests/testenv.sh +++ b/external/corert/tests/testenv.sh @@ -87,4 +87,4 @@ export CoreRT_BuildOS __ScriptDir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) -export CoreRT_ToolchainDir=${__ScriptDir}/../bin/Product/${CoreRT_BuildOS}.${CoreRT_BuildArch}.${CoreRT_BuildType}/packaging/publish1 +export CoreRT_ToolchainDir=${__ScriptDir}/../bin/${CoreRT_BuildOS}.${CoreRT_BuildArch}.${CoreRT_BuildType} diff --git a/mcs/build/common/Consts.cs b/mcs/build/common/Consts.cs index d6ee1c65d3..57c36a8c8d 100644 --- a/mcs/build/common/Consts.cs +++ b/mcs/build/common/Consts.cs @@ -34,7 +34,7 @@ static class Consts // Use these assembly version constants to make code more maintainable. // - public const string MonoVersion = "5.10.0.53"; + public const string MonoVersion = "5.10.0.69"; public const string MonoCompany = "Mono development team"; public const string MonoProduct = "Mono Common Language Infrastructure"; public const string MonoCopyright = "(c) Various Mono authors"; diff --git a/mcs/class/Microsoft.CSharp/Makefile b/mcs/class/Microsoft.CSharp/Makefile index 436a89179c..a9e79c9571 100644 --- a/mcs/class/Microsoft.CSharp/Makefile +++ b/mcs/class/Microsoft.CSharp/Makefile @@ -8,7 +8,7 @@ LIB_REFS = System.Core System KEYFILE = ../msfinal.pub LIB_MCS_FLAGS = -XTEST_LIB_REFS = Microsoft.CSharp System.Core Facades/System.Threading.Tasks Facades/System.Runtime.InteropServices.RuntimeInformation +XTEST_LIB_REFS = Microsoft.CSharp System.Core System Facades/System.Threading.Tasks Facades/System.Runtime.InteropServices.RuntimeInformation RESX_RESOURCE_STRING = ../../../external/corefx/src/Microsoft.CSharp/src/Resources/Strings.resx diff --git a/mcs/class/Microsoft.CSharp/Microsoft.CSharp.dll.sources b/mcs/class/Microsoft.CSharp/Microsoft.CSharp.dll.sources index c8f519a950..c29e417d6c 100644 --- a/mcs/class/Microsoft.CSharp/Microsoft.CSharp.dll.sources +++ b/mcs/class/Microsoft.CSharp/Microsoft.CSharp.dll.sources @@ -34,135 +34,17 @@ corefx/SR.cs ../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ExpressionTreeCallRewriter.cs ../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ICSharpBinder.cs ../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ICSharpInvokeOrInvokeMemberBinder.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ResetBindException.cs ../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinder.cs ../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinderException.cs ../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinderExtensions.cs ../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/RuntimeBinderInternalCompilerException.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Binding/Better.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Binding/ErrorReporting.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/BindingContext.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/BindingFlag.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/BinOpArgInfo.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/BinOpKind.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/BinOpSig.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/CandidateFunctionMember.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ConstVal.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Conversion.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Conversions.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/COperators.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Declarations/AggregateDeclaration.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExplicitConversion.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExpressionBinder.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExpressionKind.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/EXPRExtensions.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ExprFactory.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/EXPRFLAG.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/FundamentalTypes.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/GlobalSymbolContext.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/GroupToArgsBinder.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/GroupToArgsBinderResult.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/ImplicitConversion.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MemberLookup.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MemberLookupResults.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodIterator.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodKind.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodTypeInferrer.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Nullable.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/NullableLift.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Operators.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/PredefinedMembers.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/SemanticChecker.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/SubstitutionContext.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/AggregateSymbol.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/AssemblyQualifiedNamespaceSymbol.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/EventSymbol.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/FieldSymbol.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/IndexerSymbol.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/LocalVariableSymbol.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/MethodOrPropertySymbol.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/MethodSymbol.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/NamespaceOrAggregateSymbol.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/NamespaceSymbol.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/ParentSymbol.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/PropertySymbol.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/Scope.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/Symbol.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolKind.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolLoader.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolManagerBase.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolMask.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymbolTable.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/SymFactory.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/TypeParameterSymbol.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/VariableSymbol.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/ArrayIndex.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/ArrayInitialization.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Assignment.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/BinaryOperator.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Block.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/BoundAnonymousFunction.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Call.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Cast.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Class.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/CompoundOperator.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Concatenate.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Constant.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/EXPR.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/ExpressionIterator.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/ExprOperator.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/ExprWithType.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Field.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/FieldInfo.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/HoistedLocal.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/IExprWithArgs.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/IExprWithObject.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/List.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/LocalVariable.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/MemberGroup.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/MethodInfo.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/NamedArgumentSpecification.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Property.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/PropertyInfo.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Return.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Statement.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Temporary.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/TypeOf.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/UnaryOperator.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/UserDefinedConversion.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/UserDefinedLogicalOperator.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Visitors/ExpressionTreeRewriter.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Visitors/ExprVisitorBase.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/ZeroInitialize.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/TypeBind.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/AggregateType.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/ArgumentListType.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/ArrayType.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/ErrorType.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/MethodGroupType.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/NullableType.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/NullType.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/ParameterModifierType.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/PointerType.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/PredefinedTypes.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/Type.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeArray.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeFactory.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeKind.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeManager.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeParameterType.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/TypeTable.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/VoidType.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/UnaOpSig.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/UtilityTypeExtensions.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/WithType.cs +../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/*.cs +../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Binding/*.cs +../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Declarations/*.cs +../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/*.cs +../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/*.cs +../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Tree/Visitors/*.cs +../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Types/*.cs ../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/SpecialNames.cs ../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/SymbolTable.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/NameManager.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/Names.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/NameTable.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/Operators.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/PredefinedName.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/PredefinedType.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/TokenFacts.cs -../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/TokenKind.cs +../../../external/corefx/src/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Syntax/*.cs diff --git a/mcs/class/Microsoft.CSharp/corefx/SR.cs b/mcs/class/Microsoft.CSharp/corefx/SR.cs index 7d50ecad45..41761f8980 100644 --- a/mcs/class/Microsoft.CSharp/corefx/SR.cs +++ b/mcs/class/Microsoft.CSharp/corefx/SR.cs @@ -5,15 +5,9 @@ partial class SR { public const string InternalCompilerError = "An unexpected exception occurred while binding a dynamic operation"; - public const string BindRequireArguments = "Cannot bind call with no calling object"; - public const string BindCallFailedOverloadResolution = "Overload resolution failed"; - public const string BindBinaryOperatorRequireTwoArguments = "Binary operators must be invoked with two arguments"; - public const string BindUnaryOperatorRequireOneArgument = "Unary operators must be invoked with one argument"; public const string BindPropertyFailedMethodGroup = "The name '{0}' is bound to a method and cannot be used like a property"; public const string BindPropertyFailedEvent = "The event '{0}' can only appear on the left hand side of +"; public const string BindInvokeFailedNonDelegate = "Cannot invoke a non-delegate type"; - public const string BindBinaryAssignmentRequireTwoArguments = "Binary operators cannot be invoked with one argument"; - public const string BindBinaryAssignmentFailedNullReference = "Cannot perform member assignment on a null reference"; public const string NullReferenceOnMemberException = "Cannot perform runtime binding on a null reference"; public const string BindCallToConditionalMethod = "Cannot dynamically invoke method '{0}' because it has a Conditional attribute"; public const string BindToVoidMethodButExpectResult = "Cannot implicitly convert type 'void' to 'object'"; @@ -32,18 +26,13 @@ partial class SR public const string ObjectRequired = "An object reference is required for the non-static field, method, or property '{0}'"; public const string AmbigCall = "The call is ambiguous between the following methods or properties: '{0}' and '{1}'"; public const string BadAccess = "'{0}' is inaccessible due to its protection level"; - public const string MethDelegateMismatch = "No overload for '{0}' matches delegate '{1}'"; public const string AssgLvalueExpected = "The left-hand side of an assignment must be a variable, property or indexer"; public const string NoConstructors = "The type '{0}' has no constructors defined"; - public const string BadDelegateConstructor = "The delegate '{0}' does not have a valid constructor"; public const string PropertyLacksGet = "The property or indexer '{0}' cannot be used in this context because it lacks the get accessor"; public const string ObjectProhibited = "Member '{0}' cannot be accessed with an instance reference; qualify it with a type name instead"; public const string AssgReadonly = "A readonly field cannot be assigned to (except in a constructor or a variable initializer)"; - public const string RefReadonly = "A readonly field cannot be passed ref or out (except in a constructor)"; public const string AssgReadonlyStatic = "A static readonly field cannot be assigned to (except in a static constructor or a variable initializer)"; - public const string RefReadonlyStatic = "A static readonly field cannot be passed ref or out (except in a static constructor)"; public const string AssgReadonlyProp = "Property or indexer '{0}' cannot be assigned to -- it is read only"; - public const string RefProperty = "A property or indexer may not be passed as an out or ref parameter"; public const string UnsafeNeeded = "Dynamic calls cannot be used in conjunction with pointers"; public const string BadBoolOp = "In order to be applicable as a short circuit operator a user-defined logical operator ('{0}') must have the same return type as the type of its 2 parameters"; public const string MustHaveOpTF = "The type ('{0}') must contain declarations of operator true and operator false"; @@ -53,7 +42,6 @@ partial class SR public const string InaccessibleGetter = "The property or indexer '{0}' cannot be used in this context because the get accessor is inaccessible"; public const string InaccessibleSetter = "The property or indexer '{0}' cannot be used in this context because the set accessor is inaccessible"; public const string BadArity = "Using the generic {1} '{0}' requires '{2}' type arguments"; - public const string BadTypeArgument = "The type '{0}' may not be used as a type argument"; public const string TypeArgsNotAllowed = "The {1} '{0}' cannot be used with type arguments"; public const string HasNoTypeVars = "The non-generic {1} '{0}' cannot be used with type arguments"; public const string NewConstraintNotSatisfied = "'{2}' must be a non-abstract type with a public parameterless constructor in order to use it as parameter '{1}' in the generic type or method '{0}'"; @@ -61,39 +49,26 @@ partial class SR public const string GenericConstraintNotSatisfiedNullableEnum = "The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'."; public const string GenericConstraintNotSatisfiedNullableInterface = "The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The nullable type '{3}' does not satisfy the constraint of '{1}'. Nullable types can not satisfy any interface constraints."; public const string GenericConstraintNotSatisfiedValType = "The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. There is no boxing conversion from '{3}' to '{1}'."; - public const string TypeVarCantBeNull = "Cannot convert null to type parameter '{0}' because it could be a non-nullable value type. Consider using 'default({0})' instead."; - public const string BadRetType = "'{1} {0}' has the wrong return type"; public const string CantInferMethTypeArgs = "The type arguments for method '{0}' cannot be inferred from the usage. Try specifying the type arguments explicitly."; - public const string MethGrpToNonDel = "Cannot convert method group '{0}' to non-delegate type '{1}'. Did you intend to invoke the method?"; public const string RefConstraintNotSatisfied = "The type '{2}' must be a reference type in order to use it as parameter '{1}' in the generic type or method '{0}'"; public const string ValConstraintNotSatisfied = "The type '{2}' must be a non-nullable value type in order to use it as parameter '{1}' in the generic type or method '{0}'"; public const string AmbigUDConv = "Ambiguous user defined conversions '{0}' and '{1}' when converting from '{2}' to '{3}'"; public const string BindToBogus = "'{0}' is not supported by the language"; public const string CantCallSpecialMethod = "'{0}': cannot explicitly call operator or accessor"; public const string ConvertToStaticClass = "Cannot convert to static type '{0}'"; - public const string GenericArgIsStaticClass = "'{0}': static types cannot be used as type arguments"; public const string IncrementLvalueExpected = "The operand of an increment or decrement operator must be a variable, property or indexer"; - public const string BadArgCount = "No overload for method '{0}' takes '{1}' arguments"; + public const string BadArgCount = "No overload for method '{0}' takes {1} arguments"; public const string BadArgTypes = "The best overloaded method match for '{0}' has some invalid arguments"; - public const string RefLvalueExpected = "A ref or out argument must be an assignable variable"; public const string BadProtectedAccess = "Cannot access protected member '{0}' via a qualifier of type '{1}'; the qualifier must be of type '{2}' (or derived from it)"; public const string BindToBogusProp2 = "Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor methods '{1}' or '{2}'"; public const string BindToBogusProp1 = "Property, indexer, or event '{0}' is not supported by the language; try directly calling accessor method '{1}'"; public const string BadDelArgCount = "Delegate '{0}' does not take '{1}' arguments"; public const string BadDelArgTypes = "Delegate '{0}' has some invalid arguments"; - public const string AssgReadonlyLocal = "Cannot assign to '{0}' because it is read-only"; - public const string RefReadonlyLocal = "Cannot pass '{0}' as a ref or out argument because it is read-only"; public const string ReturnNotLValue = "Cannot modify the return value of '{0}' because it is not a variable"; public const string AssgReadonly2 = "Members of readonly field '{0}' cannot be modified (except in a constructor or a variable initializer)"; - public const string RefReadonly2 = "Members of readonly field '{0}' cannot be passed ref or out (except in a constructor)"; public const string AssgReadonlyStatic2 = "Fields of static readonly field '{0}' cannot be assigned to (except in a static constructor or a variable initializer)"; - public const string RefReadonlyStatic2 = "Fields of static readonly field '{0}' cannot be passed ref or out (except in a static constructor)"; - public const string AssgReadonlyLocalCause = "Cannot assign to '{0}' because it is a '{1}'"; - public const string RefReadonlyLocalCause = "Cannot pass '{0}' as a ref or out argument because it is a '{1}'"; - public const string DelegateOnNullable = "Cannot bind delegate to '{0}' because it is a member of 'System.Nullable'"; public const string BadCtorArgCount = "'{0}' does not contain a constructor that takes '{1}' arguments"; public const string NonInvocableMemberCalled = "Non-invocable member '{0}' cannot be used like a method."; - public const string NamedArgumentSpecificationBeforeFixedArgument = "Named argument specifications must appear after all fixed arguments have been specified"; public const string BadNamedArgument = "The best overload for '{0}' does not have a parameter named '{1}'"; public const string BadNamedArgumentForDelegateInvoke = "The delegate '{0}' does not have a parameter named '{1}'"; public const string DuplicateNamedArgument = "Named argument '{0}' cannot be specified multiple times"; @@ -101,4 +76,5 @@ partial class SR public const string TypeArgumentRequiredForStaticCall = "The first argument to dynamically-bound static or constructor call must be a Type"; public const string DynamicArgumentNeedsValue = "The runtime binder cannot bind a metaobject without a value"; public const string BindingNameCollision = "More than one type in the binding has the same full name."; + public const string BadNonTrailingNamedArgument = "Named argument '{0}' is used out-of-position but is followed by an unnamed argument"; } diff --git a/mcs/class/System.Core/corefx/SR.cs b/mcs/class/System.Core/corefx/SR.cs index e2a8867a1e..7554ca1a25 100644 --- a/mcs/class/System.Core/corefx/SR.cs +++ b/mcs/class/System.Core/corefx/SR.cs @@ -15,7 +15,6 @@ partial class SR public const string BoundsCannotBeLessThanOne = "Bounds count cannot be less than 1"; public const string TypeMustNotBeByRef = "Type must not be ByRef"; public const string TypeMustNotBePointer = "Type must not be a pointer type"; - public const string TypeDoesNotHaveConstructorForTheSignature = "Type doesn't have constructor with a given signature"; public const string SetterMustBeVoid = "Setter should have void type."; public const string PropertyTypeMustMatchGetter = "Property type must match the value type of getter"; public const string PropertyTypeMustMatchSetter = "Property type must match the value type of setter"; @@ -88,7 +87,6 @@ partial class SR public const string PropertyDoesNotHaveAccessor = "The property '{0}' has no 'get' or 'set' accessors"; public const string NotAMemberOfType = "'{0}' is not a member of type '{1}'"; public const string NotAMemberOfAnyType = "'{0}' is not a member of any type"; - public const string ExpressionNotSupportedForType = "The expression '{0}' is not supported for type '{1}'"; public const string UnsupportedExpressionType = "The expression type '{0}' is not supported"; public const string ParameterExpressionNotValidAsDelegate = "ParameterExpression of type '{0}' cannot be used for delegate parameter of type '{1}'"; public const string PropertyNotDefinedForType = "Property '{0}' is not defined for type '{1}'"; @@ -104,12 +102,9 @@ partial class SR public const string ElementInitializerMethodWithZeroArgs = "Element initializer method must have at least 1 parameter"; public const string ElementInitializerMethodStatic = "Element initializer method must be an instance method"; public const string TypeNotIEnumerable = "Type '{0}' is not IEnumerable"; - public const string UnexpectedCoalesceOperator = "Unexpected coalesce operator."; - public const string InvalidCast = "Cannot cast from type '{0}' to type '{1}"; public const string UnhandledBinary = "Unhandled binary: {0}"; public const string UnhandledBinding = "Unhandled binding "; public const string UnhandledBindingType = "Unhandled Binding Type: {0}"; - public const string UnhandledConvert = "Unhandled convert: {0}"; public const string UnhandledUnary = "Unhandled unary: {0}"; public const string UnknownBindingType = "Unknown binding type"; public const string UserDefinedOpMustHaveConsistentTypes = "The user-defined operator method '{1}' for operator '{0}' must have identical parameter and return types."; @@ -131,11 +126,9 @@ partial class SR public const string ControlCannotEnterTry = "Control cannot enter a try block."; public const string ControlCannotEnterExpression = "Control cannot enter an expression--only statements can be jumped into."; public const string NonLocalJumpWithValue = "Cannot jump to non-local label '{0}' with a value. Only jumps to labels defined in outer blocks can pass values."; - public const string ExtensionNotReduced = "Extension should have been reduced."; public const string CannotCompileConstant = "CompileToMethod cannot compile constant '{0}' because it is a non-trivial value, such as a live object. Instead, create an expression tree that can construct this value."; public const string CannotCompileDynamic = "Dynamic expressions are not supported by CompileToMethod. Instead, create an expression tree that uses System.Runtime.CompilerServices.CallSite."; public const string InvalidLvalue = "Invalid lvalue for assignment: {0}."; - public const string UnknownLiftType = "unknown lift type: '{0}'."; public const string UndefinedVariable = "variable '{0}' of type '{1}' referenced from scope '{2}', but it is not defined"; public const string CannotCloseOverByRef = "Cannot close over byref parameter '{0}' referenced in lambda '{1}'"; public const string UnexpectedVarArgsCall = "Unexpected VarArgs call to method '{0}'"; @@ -162,7 +155,6 @@ partial class SR public const string IncorrectNumberOfMethodCallArguments = "Incorrect number of arguments supplied for call to method '{0}'"; public const string IncorrectNumberOfLambdaArguments = "Incorrect number of arguments supplied for lambda invocation"; public const string IncorrectNumberOfConstructorArguments = "Incorrect number of arguments for constructor"; - public const string OperatorNotImplementedForType = "The operator '{0}' is not implemented for type '{1}'"; public const string NonStaticConstructorRequired = "The constructor should not be static"; public const string NonAbstractConstructorRequired = "Can't compile a NewExpression with a constructor declared on an abstract class"; public const string FirstArgumentMustBeCallSite = "First argument of delegate must be CallSite"; @@ -181,6 +173,7 @@ partial class SR public const string CollectionReadOnly = "Collection is read-only."; public const string KeyDoesNotExistInExpando = "The specified key '{0}' does not exist in the ExpandoObject."; public const string SameKeyExistsInExpando = "An element with the same key '{0}' already exists in the ExpandoObject."; + public const string Arg_KeyNotFoundWithKey = "The given key '{0}' was not present in the dictionary."; public const string EmptyEnumerable = "Enumeration yielded no results"; public const string MoreThanOneElement = "Sequence contains more than one element"; public const string MoreThanOneMatch = "Sequence contains more than one matching element"; diff --git a/mcs/class/System.Data/Makefile b/mcs/class/System.Data/Makefile index 671eda1e8b..6d80a3b531 100644 --- a/mcs/class/System.Data/Makefile +++ b/mcs/class/System.Data/Makefile @@ -21,6 +21,16 @@ else LIB_REFS += System.EnterpriseServices System.Configuration endif +ifdef MONO_FEATURE_APPLETLS +LIB_MCS_FLAGS += -d:MONO_FEATURE_APPLETLS +endif + +ifndef PROFILE_DISABLE_BTLS +ifdef HAVE_BTLS +LIB_MCS_FLAGS += -d:MONO_FEATURE_BTLS +endif +endif + TXT_RESOURCE_STRINGS = ../referencesource/System.Data/system.data.txt RESX_RESOURCE_STRING = \ ../../../external/corefx/src/System.Data.Common/src/Resources/Strings.resx \ @@ -31,6 +41,8 @@ TEST_LIB_REFS = System.Core Mono.Data.Sqlite TEST_MCS_FLAGS = $(LIB_MCS_FLAGS) -nowarn:618,169,612,219,168 TEST_NUNITLITE_APP_CONFIG_GLOBAL=Test/test-config-file + +USE_XTEST_REMOTE_EXECUTOR = YES XTEST_LIB_REFS = System System.Core System.Xml Facades/System.Text.Encoding.CodePages Facades/System.Threading.Tasks Facades/System.Runtime.InteropServices.RuntimeInformation TEST_MONO_PATH = . diff --git a/mcs/class/System.Data/System.Data.SqlClient/SqlCommand.platformnotsupported.cs b/mcs/class/System.Data/System.Data.SqlClient/SqlCommand.platformnotsupported.cs index 2d2da0ae8f..eef722c499 100644 --- a/mcs/class/System.Data/System.Data.SqlClient/SqlCommand.platformnotsupported.cs +++ b/mcs/class/System.Data/System.Data.SqlClient/SqlCommand.platformnotsupported.cs @@ -199,6 +199,9 @@ namespace System.Data.SqlClient internal void OnReturnValue(SqlReturnValue rec) => throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); + internal void OnReturnValue(SqlReturnValue rec, TdsParserStateObject stateObj) + => throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); + internal string BuildParamList(TdsParser parser, SqlParameterCollection parameters) => throw new PlatformNotSupportedException (EXCEPTION_MESSAGE); diff --git a/mcs/class/System.Data/corefx.common.sources b/mcs/class/System.Data/corefx.common.sources index f53a861c04..b249f0d81a 100644 --- a/mcs/class/System.Data/corefx.common.sources +++ b/mcs/class/System.Data/corefx.common.sources @@ -13,14 +13,11 @@ System.Data.SqlClient/SqlCredential.cs System.Data.SqlClient/SQLDebugging.cs corefx/Odbc/AdapterUtil.cs corefx/Odbc/Res.cs -corefx/DbConnection.cs corefx/DbConnectionOptions.cs corefx/DiagnosticListener.cs corefx/SqlClientFactory.cs corefx/SqlCommand.cs corefx/SqlException.cs -corefx/SqlMetaData.cs -corefx/SqlParameter.cs corefx/SqlConnection.cs corefx/SqlConnectionStringBuilder.cs corefx/SqlParameterCollection.cs @@ -361,8 +358,10 @@ corefx/SqlDependencyUtils.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/ProviderBase/DbReferenceCollection.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/ProviderBase/DbMetaDataFactory.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/ProviderBase/TimeoutTimer.cs +../../../external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlNorm.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlMetaData.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlNotificationRequest.cs +../../../external/corefx/src/System.Data.SqlClient/src/System/Data/Sql/SqlSer.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlDelegatedTransaction.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlClientMetaDataCollectionNames.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/ApplicationIntent.cs @@ -395,14 +394,14 @@ corefx/SqlDependencyUtils.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlDataReader.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlEnums.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlError.cs -../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/sqlmetadatafactory.cs +../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlMetadataFactory_.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlErrorCollection.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlException.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlInfoMessageEvent.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlInfoMessageEventHandler.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlInternalConnection.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlInternalConnectionTds.cs -../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/sqlinternaltransaction.cs +../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlInternalTransaction.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlParameter.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlParameterCollection.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlParameterCollectionHelper.cs @@ -456,6 +455,7 @@ corefx/SqlDependencyUtils.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SNI/SspiClientContextStatus.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SNI/SSRP.cs ../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/TdsParserStateObjectManaged.cs +../../../external/corefx/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlUdtInfo.cs ../../../external/corefx/src/Common/src/System/Net/ContextFlagsPal.cs ../../../external/corefx/src/Common/src/System/Net/SecurityStatusPal.cs diff --git a/mcs/class/System.Data/corefx/Odbc/AdapterUtil.cs b/mcs/class/System.Data/corefx/Odbc/AdapterUtil.cs index ca6c2edb4b..26c8639c86 100644 --- a/mcs/class/System.Data/corefx/Odbc/AdapterUtil.cs +++ b/mcs/class/System.Data/corefx/Odbc/AdapterUtil.cs @@ -82,11 +82,6 @@ namespace System.Data.Common return InvalidOperation(SR.GetString(SR.ADP_OffsetOutOfRangeException)); } - internal static ArgumentException InvalidDataType(TypeCode typecode) - { - return Argument(SR.GetString(SR.ADP_InvalidDataType, typecode.ToString())); - } - static internal InvalidOperationException QuotePrefixNotSet(string method) { return InvalidOperation(Res.GetString(Res.ADP_QuotePrefixNotSet, method)); @@ -104,11 +99,6 @@ namespace System.Data.Common return ADP.InvalidOperation(SR.GetString(SR.ADP_InvalidDataDirectory)); } - internal static ArgumentException UnknownDataTypeCode(Type dataType, TypeCode typeCode) - { - return Argument(SR.GetString(SR.ADP_UnknownDataTypeCode, ((int)typeCode).ToString(CultureInfo.InvariantCulture), dataType.FullName)); - } - internal static void EscapeSpecialCharacters(string unescapedString, StringBuilder escapedString) { // note special characters list is from character escapes @@ -137,29 +127,6 @@ namespace System.Data.Common return (IntPtr)checked(pbase.ToInt64() + offset); } - static internal ArgumentOutOfRangeException NotSupportedUserDefinedTypeSerializationFormat(Microsoft.SqlServer.Server.Format value, string method) { - return ADP.NotSupportedEnumerationValue(typeof(Microsoft.SqlServer.Server.Format), value.ToString(), method); - } - - static internal ArgumentOutOfRangeException InvalidUserDefinedTypeSerializationFormat(Microsoft.SqlServer.Server.Format value) { -#if DEBUG - switch(value) { - case Microsoft.SqlServer.Server.Format.Unknown: - case Microsoft.SqlServer.Server.Format.Native: - case Microsoft.SqlServer.Server.Format.UserDefined: - Debug.Assert(false, "valid UserDefinedTypeSerializationFormat " + value.ToString()); - break; - } -#endif - return InvalidEnumerationValue(typeof(Microsoft.SqlServer.Server.Format), (int) value); - } - - static internal ArgumentOutOfRangeException ArgumentOutOfRange(string message, string parameterName, object value) { - ArgumentOutOfRangeException e = new ArgumentOutOfRangeException(parameterName, value, message); - TraceExceptionAsReturnValue(e); - return e; - } - static internal Exception InvalidXMLBadVersion() { return Argument(Res.GetString(Res.ADP_InvalidXMLBadVersion)); } diff --git a/mcs/class/System.Data/corefx/SR.cs b/mcs/class/System.Data/corefx/SR.cs index 17fbd64338..cdf509de4f 100644 --- a/mcs/class/System.Data/corefx/SR.cs +++ b/mcs/class/System.Data/corefx/SR.cs @@ -458,6 +458,11 @@ partial class SR public const string DataColumns_RemoveExpression = "Cannot remove this column, because it is part of an expression: {0} = {1}."; public const string DataRow_RowInsertTwice = "The rowOrder value={0} has been found twice for table named '{1}'."; public const string Xml_ElementTypeNotFound = "Cannot find ElementType name='{0}'."; + public const string ADP_DbProviderFactories_InvariantNameNotFound = "The specified invariant name '{0}' wasn't found in the list of registered .NET Data Providers."; + public const string ADP_DbProviderFactories_NoInstance = "The requested .NET Data Provider's implementation does not have an Instance field of a System.Data.Common.DbProviderFactory derived type."; + public const string ADP_DbProviderFactories_FactoryNotLoadable = "The registered .NET Data Provider's DbProviderFactory implementation type '{0}' couldn't be loaded."; + public const string ADP_DbProviderFactories_NoAssemblyQualifiedName = "The missing .NET Data Provider's assembly qualified name is required."; + public const string ADP_DbProviderFactories_NotAFactoryType = "The type '{0}' doesn't inherit from DbProviderFactory."; public const string ADP_ConnectionAlreadyOpen = "The connection was not closed. {0}"; public const string ADP_InternalConnectionError = "Internal DbConnection Error: {0}"; public const string ADP_InvalidOffsetValue = "Invalid parameter Offset value '{0}'. The value must be greater than or equal to 0."; @@ -475,7 +480,7 @@ partial class SR public const string Arg_RankMultiDimNotSupported = "Only single dimensional arrays are supported for the requested action."; public const string Arg_RemoveArgNotFound = "Cannot remove the specified item because it was not found in the specified Collection."; public const string ArgumentOutOfRange_NeedNonNegNum = "Non-negative number required."; - public const string ADP_DeriveParametersNotSupported = "{0} DeriveParameters only supports CommandType.StoredProcedure, not CommandType.{1}."; + public const string ADP_DeriveParametersNotSupported = "{0} DeriveParameters only supports CommandType.StoredProcedure, not CommandType. {1}."; public const string ADP_NoStoredProcedureExists = "The stored procedure '{0}' doesn't exist."; public const string ADP_MissingConnectionOptionValue = "Use of key '{0}' requires the key '{1}' to be present."; public const string ADP_InvalidConnectionOptionValueLength = "The value's length for key '{0}' exceeds it's limit of '{1}'."; @@ -505,6 +510,7 @@ partial class SR public const string ADP_NonSequentialColumnAccess = "Invalid attempt to read from column ordinal '{0}'. With CommandBehavior.SequentialAccess, you may only read from column ordinal '{1}' or greater."; public const string ADP_InvalidDataType = "The parameter data type of {0} is invalid."; public const string ADP_UnknownDataType = "No mapping exists from object type {0} to a known managed provider native type."; + public const string ADP_UnknownDataTypeCode = "Unable to handle an unknown TypeCode {0} returned by Type {1}."; public const string ADP_DbTypeNotSupported = "No mapping exists from DbType {0} to a known {1}."; public const string ADP_VersionDoesNotSupportDataType = "The version of SQL Server in use does not support datatype '{0}'."; public const string ADP_ParameterValueOutOfRange = "Parameter value '{0}' is out of range."; @@ -524,6 +530,7 @@ partial class SR public const string SQL_EncryptionNotSupportedByClient = "The instance of SQL Server you attempted to connect to requires encryption but this machine does not support it."; public const string SQL_EncryptionNotSupportedByServer = "The instance of SQL Server you attempted to connect to does not support encryption."; public const string SQL_InvalidSQLServerVersionUnknown = "Unsupported SQL Server version. The .Net Framework SqlClient Data Provider can only be used with SQL Server versions 7.0 and later."; + public const string SQL_CannotCreateNormalizer = "Cannot create normalizer for '{0}'."; public const string SQL_CannotModifyPropertyAsyncOperationInProgress = "{0} cannot be changed while async operation is in progress."; public const string SQL_InstanceFailure = "Instance failure."; public const string SQL_InvalidPartnerConfiguration = "Server {0}, database {1} is not configured for database mirroring."; @@ -609,6 +616,8 @@ partial class SR public const string SqlMisc_StreamErrorMessage = "An error occurred while reading."; public const string SqlMisc_TruncationMaxDataMessage = "Data returned is larger than 2Gb in size. Use SequentialAccess command behavior in order to get all of the data."; public const string SqlMisc_SubclassMustOverride = "Subclass did not override a required method."; + public const string SqlUdtReason_NoUdtAttribute = "no UDT attribute"; + public const string SQLUDT_InvalidSqlType = "Specified type is not registered on the target server. {0}."; public const string Sql_InternalError = "Internal Error"; public const string ADP_OperationAborted = "Operation aborted."; public const string ADP_OperationAbortedExceptionMessage = "Operation aborted due to an exception (see InnerException for details)."; @@ -639,6 +648,13 @@ partial class SR public const string SqlProvider_DuplicateSortOrdinal = "The sort ordinal {0} was specified twice."; public const string SqlProvider_MissingSortOrdinal = "The sort ordinal {0} was not specified."; public const string SqlProvider_SortOrdinalGreaterThanFieldCount = "The sort ordinal {0} on field {1} exceeds the total number of fields."; + public const string SQLUDT_MaxByteSizeValue = "range: 0-8000"; + public const string SQLUDT_Unexpected = "unexpected error encountered in SqlClient data provider. {0}"; + public const string SQLUDT_UnexpectedUdtTypeName = "UdtTypeName property must be set only for UDT parameters."; + public const string SQLUDT_InvalidUdtTypeName = "UdtTypeName property must be set for UDT parameters."; + public const string SqlUdt_InvalidUdtMessage = "'{0}' is an invalid user defined type, reason: {1}."; + public const string SQL_UDTTypeName = "SqlParameter.UdtTypeName is an invalid multipart name"; + public const string SQL_InvalidUdt3PartNameFormat = "Invalid 3 part name format for UdtTypeName."; public const string IEnumerableOfSqlDataRecordHasNoRows = "There are no records in the SqlDataRecord enumeration. To send a table-valued parameter with no rows, use a null reference for the value instead."; public const string SNI_ERROR_1 = "I/O Error detected in read/write operation"; public const string SNI_ERROR_2 = "Connection was terminated"; @@ -787,7 +803,6 @@ partial class SR public const string ADP_DatabaseNameTooLong = "The argument is too long."; public const string ADP_DataReaderNoData = "No data exists for the row/column."; public const string ADP_NumericToDecimalOverflow = "The numerical value is too large to fit into a 96 bit decimal."; - public const string ADP_UnknownDataTypeCode = "Unable to handle an unknown TypeCode {0} returned by Type {1}."; public const string ADP_DbRecordReadOnly = "'{0}' cannot be called when the record is read only."; public const string ADP_OffsetOutOfRangeException = "Offset must refer to a location within the value."; public const string ODBC_GetSchemaRestrictionRequired = "The ODBC managed provider requires that the TABLE_NAME restriction be specified and non-null for the GetSchema indexes collection."; diff --git a/mcs/class/System.Data/corefx/SqlCommand.cs b/mcs/class/System.Data/corefx/SqlCommand.cs index c5f0c100c8..ea1606bddd 100644 --- a/mcs/class/System.Data/corefx/SqlCommand.cs +++ b/mcs/class/System.Data/corefx/SqlCommand.cs @@ -18,9 +18,6 @@ namespace System.Data.SqlClient set => throw new NotImplementedException(); } - [System.Security.Permissions.HostProtectionAttribute(ExternalThreading=true)] - public IAsyncResult BeginExecuteNonQuery() => BeginExecuteNonQuery(null, null); - [System.Security.Permissions.HostProtectionAttribute(ExternalThreading=true)] public IAsyncResult BeginExecuteXmlReader() => BeginExecuteXmlReader(null, null); diff --git a/mcs/class/System.Data/corefx/SqlMetaData.cs b/mcs/class/System.Data/corefx/SqlMetaData.cs deleted file mode 100644 index dcf7c48da8..0000000000 --- a/mcs/class/System.Data/corefx/SqlMetaData.cs +++ /dev/null @@ -1,21 +0,0 @@ -// 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.Data; - -namespace Microsoft.SqlServer.Server -{ - partial class SqlMetaData - { - public SqlMetaData (string name, SqlDbType dbType, Type userDefinedType) : - this (name, dbType, -1, 0, 0, 0, System.Data.SqlTypes.SqlCompareOptions.None, userDefinedType) { } - - - [MonoTODO] - public System.Data.DbType DbType { - get => throw new NotImplementedException(); - } - } -} diff --git a/mcs/class/System.Data/corefx/SqlParameter.cs b/mcs/class/System.Data/corefx/SqlParameter.cs deleted file mode 100644 index 5e0e88f2f6..0000000000 --- a/mcs/class/System.Data/corefx/SqlParameter.cs +++ /dev/null @@ -1,21 +0,0 @@ -// 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.Collections; -using System.Data; -using System.Data.Common; - -namespace System.Data.SqlClient -{ - partial class SqlParameter - { - [MonoTODO] - public string UdtTypeName - { - get => throw new NotImplementedException(); - set => throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/mcs/class/System.Data/darwin_net_4_x_System.Data.dll.sources b/mcs/class/System.Data/darwin_net_4_x_System.Data.dll.sources index 87d9b19fbf..eaef7f67fe 100644 --- a/mcs/class/System.Data/darwin_net_4_x_System.Data.dll.sources +++ b/mcs/class/System.Data/darwin_net_4_x_System.Data.dll.sources @@ -1,4 +1,4 @@ #include corefx.unix.sources #include net_4_x_System.Data.dll.sources -../../../external/corefx/src/System.Data.Odbc/src/Common/System/Data/Common/ExternDll.Osx.cs +../../../external/corefx/src/Common/src/Interop/OSX/Interop.Libraries.cs diff --git a/mcs/class/System.Data/linux_net_4_x_System.Data.dll.sources b/mcs/class/System.Data/linux_net_4_x_System.Data.dll.sources index be2e110dc6..0f50583418 100644 --- a/mcs/class/System.Data/linux_net_4_x_System.Data.dll.sources +++ b/mcs/class/System.Data/linux_net_4_x_System.Data.dll.sources @@ -1,4 +1,4 @@ #include corefx.unix.sources #include net_4_x_System.Data.dll.sources -../../../external/corefx/src/System.Data.Odbc/src/Common/System/Data/Common/ExternDll.Linux.cs +../../../external/corefx/src/Common/src/Interop/Linux/Interop.Libraries.cs diff --git a/mcs/class/System.Data/net_4_x_System.Data.dll.sources b/mcs/class/System.Data/net_4_x_System.Data.dll.sources index d29ed19116..1a105c614d 100644 --- a/mcs/class/System.Data/net_4_x_System.Data.dll.sources +++ b/mcs/class/System.Data/net_4_x_System.Data.dll.sources @@ -24,7 +24,8 @@ System.Data.SqlClient/SQLDebugging.cs corefx/Odbc/OdbcFactory.cs corefx/Odbc/DbConnectionStringCommon.cs ../referencesource/System.Data/System/Data/Odbc/OdbcPermission.cs -../../../external/corefx/src/System.Data.Odbc/src/Properties/SecurityTransparent.cs + +../../../external/corefx/src/Common/src/Interop/Interop.Odbc.cs ../../../external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/DbDataRecord.cs ../../../external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/Odbc32.cs ../../../external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcCommand.cs @@ -61,7 +62,6 @@ corefx/Odbc/DbConnectionStringCommon.cs ../../../external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcType.cs ../../../external/corefx/src/System.Data.Odbc/src/System/Data/Odbc/OdbcUtils.cs ../../../external/corefx/src/System.Data.Odbc/src/Common/System/HResults.cs -../../../external/corefx/src/System.Data.Odbc/src/Common/System/Data/Common/UnsafeNativeMethods.cs # OLEDB corefx/OleDbStubs.cs diff --git a/mcs/class/System.Data/win32_net_4_x_System.Data.dll.sources b/mcs/class/System.Data/win32_net_4_x_System.Data.dll.sources index ad1fa67b76..cdc6bae93d 100644 --- a/mcs/class/System.Data/win32_net_4_x_System.Data.dll.sources +++ b/mcs/class/System.Data/win32_net_4_x_System.Data.dll.sources @@ -46,5 +46,7 @@ ../../../external/corefx/src/Common/src/System/Net/SecurityStatusAdapterPal.Windows.cs ../../../external/corefx/src/Common/src/System/Collections/Generic/BidirectionalDictionary.cs +../../../external/corefx/src/Common/src/Interop/Windows/SChannel/Interop.SecPkgContext_ApplicationProtocol.cs + # ODBC: -../../../external/corefx/src/System.Data.Odbc/src/Common/System/Data/Common/ExternDll.Windows.cs +../../../external/corefx/src/Common/src/Interop/Windows/Interop.Libraries.cs diff --git a/mcs/class/System.IO.Compression/AssemblyInfo.cs b/mcs/class/System.IO.Compression/AssemblyInfo.cs index d89e9e473b..4c1ba11367 100644 --- a/mcs/class/System.IO.Compression/AssemblyInfo.cs +++ b/mcs/class/System.IO.Compression/AssemblyInfo.cs @@ -36,3 +36,4 @@ using System.Reflection; [assembly: AssemblyInformationalVersion (Consts.FxFileVersion)] [assembly: AssemblyFileVersion (Consts.FxFileVersion)] [assembly: AssemblyDelaySign (true)] +[assembly: CLSCompliant (true)] diff --git a/mcs/class/System.Numerics/Makefile b/mcs/class/System.Numerics/Makefile index 6b0a5d52a0..fcbdfcee40 100644 --- a/mcs/class/System.Numerics/Makefile +++ b/mcs/class/System.Numerics/Makefile @@ -13,7 +13,8 @@ LIBRARY_WARN_AS_ERROR = yes RESX_RESOURCE_STRING = \ ../../../external/corefx/src/System.Runtime.Numerics/src/Resources/Strings.resx \ - ../../../external/corefx/src/System.Numerics.Vectors/src/Resources/Strings.resx + ../../../external/corefx/src/System.Numerics.Vectors/src/Resources/Strings.resx \ + ../../../external/corefx/src/System.Buffers/src/Resources/Strings.resx EXTRA_DISTFILES = diff --git a/mcs/class/System.Numerics/System.Numerics.dll.sources b/mcs/class/System.Numerics/System.Numerics.dll.sources index 919debfb84..298e720a2f 100644 --- a/mcs/class/System.Numerics/System.Numerics.dll.sources +++ b/mcs/class/System.Numerics/System.Numerics.dll.sources @@ -6,6 +6,14 @@ corefx/Vector.cs ../../../external/corefx/src/Common/src/System/Globalization/FormatProvider.Number.cs ../../../external/corefx/src/Common/src/System/Numerics/Hashing/HashHelpers.cs +../../../external/corefx/src/Common/src/System/Text/ValueStringBuilder.cs + +# ValueStringBuilder.cs dependency +../../../external/corefx/src/System.Buffers/src/System/Buffers/ArrayPool.cs +../../../external/corefx/src/System.Buffers/src/System/Buffers/ArrayPoolEventSource.cs +../../../external/corefx/src/System.Buffers/src/System/Buffers/DefaultArrayPool.cs +../../../external/corefx/src/System.Buffers/src/System/Buffers/DefaultArrayPoolBucket.cs +../../../external/corefx/src/System.Buffers/src/System/Buffers/Utilities.cs ../../../external/corefx/src/System.Runtime.Numerics/src/System/Globalization/FormatProvider.BigInteger.cs ../../../external/corefx/src/System.Runtime.Numerics/src/System/Globalization/FormatProvider.NumberBuffer.cs diff --git a/mcs/class/System.Numerics/corefx/SR.cs b/mcs/class/System.Numerics/corefx/SR.cs index e4590b7131..07a5cb163d 100644 --- a/mcs/class/System.Numerics/corefx/SR.cs +++ b/mcs/class/System.Numerics/corefx/SR.cs @@ -22,4 +22,6 @@ partial class SR public const string Arg_ElementsInSourceIsGreaterThanDestination = "Number of elements in source vector is greater than the destination array"; public const string Arg_NullArgumentNullRef = "The method was called with a null array argument."; public const string Arg_TypeNotSupported = "Specified type is not supported"; + public const string ArgumentException_BufferNotFromPool = "The buffer is not associated with this pool and may not be returned to it."; + public const string Overflow_Negative_Unsigned = "Negative values do not have an unsigned representation."; } diff --git a/mcs/class/System/common.sources b/mcs/class/System/common.sources index 418336ceef..4d688c4322 100644 --- a/mcs/class/System/common.sources +++ b/mcs/class/System/common.sources @@ -870,9 +870,10 @@ corefx/SR.cs ../../../external/corefx/src/Common/src/System/Net/SecurityProtocol.cs ../../../external/corefx/src/Common/src/System/Net/UriScheme.cs +../../../external/corefx/src/Common/src/System/Net/WebSockets/ManagedWebSocket.cs + ../../../external/corefx/src/Common/src/System/Net/Logging/NetEventSource.Common.cs -../../../external/corefx/src/Common/src/System/Net/WebSockets/ManagedWebSocket.cs ../../../external/corefx/src/Common/src/System/Net/WebSockets/WebSocketValidate.cs ../../../external/corefx/src/System.Runtime/src/System/Collections/Generic/ISet.cs @@ -907,6 +908,7 @@ corefx/SR.cs ../../../external/corefx/src/System.Net.ServicePoint/src/System/Net/SecurityProtocolType.cs +../../../external/corefx/src/System.Net.WebSockets/src/System/Net/WebSockets/ValueWebSocketReceiveResult.cs ../../../external/corefx/src/System.Net.WebSockets/src/System/Net/WebSockets/WebSocket.cs ../../../external/corefx/src/System.Net.WebSockets/src/System/Net/WebSockets/WebSocketCloseStatus.cs ../../../external/corefx/src/System.Net.WebSockets/src/System/Net/WebSockets/WebSocketContext.cs @@ -920,6 +922,8 @@ corefx/SR.cs ../../../external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/ClientWebSocketOptions.cs ../../../external/corefx/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs +../../../external/corefx/src/System.Net.WebSockets.WebSocketProtocol/src/System/Net/WebSockets/ManagedWebSocket.netstandard.cs + ../../../external/corefx/src/System.Private.Uri/src/System/UriBuilder.cs ../../../external/corefx/src/System.Runtime.Extensions/src/System/CodeDom/Compiler/IndentedTextWriter.cs diff --git a/mcs/class/System/corefx/SR.cs b/mcs/class/System/corefx/SR.cs index 6af448f15e..a7ade18f15 100644 --- a/mcs/class/System/corefx/SR.cs +++ b/mcs/class/System/corefx/SR.cs @@ -45,6 +45,7 @@ partial class SR public const string PartitionerStatic_CanNotCallGetEnumeratorAfterSourceHasBeenDisposed = "Can not call GetEnumerator on partitions after the source enumerable is disposed"; public const string PartitionerStatic_CurrentCalledBeforeMoveNext = "MoveNext must be called at least once before calling Current."; public const string ConcurrentBag_Enumerator_EnumerationNotStartedOrAlreadyFinished = "Enumeration has either not started or has already finished."; + public const string Arg_KeyNotFoundWithKey = "The given key '{0}' was not present in the dictionary."; public const string Arg_NonZeroLowerBound = "The lower bound of target array must be zero."; public const string Arg_WrongType = "The value '{0}' is not of type '{1}' and cannot be used in this generic collection."; public const string Arg_ArrayPlusOffTooSmall = "Destination array is not long enough to copy all the items in the collection. Check array index and length."; @@ -80,8 +81,6 @@ partial class SR public const string Serialization_MissingKeys = "The keys for this dictionary are missing."; public const string Serialization_MissingValues = "The values for this dictionary are missing."; public const string ArgumentException_BufferNotFromPool = "The buffer is not associated with this pool and may not be returned to it."; - public const string ArgumentOutOfRange_IndexCountBuffer = "Index and count must refer to a location within the buffer."; - public const string Argument_InvalidCharSequenceNoIndex = "String contains invalid Unicode code points."; public const string net_uri_BadAuthority = "Invalid URI: The Authority/Host could not be parsed."; public const string net_uri_BadAuthorityTerminator = "Invalid URI: The Authority/Host cannot end with a backslash character ('\\\\')."; public const string net_uri_BadFormat = "Invalid URI: The format of the URI could not be determined."; @@ -125,7 +124,6 @@ partial class SR public const string ArgumentOutOfRange_Timeout = "The timeout must be greater than or equal to -1."; public const string ArgumentOutOfRange_WriteTimeout = "The timeout must be either a positive number or -1."; public const string IndexOutOfRange_IORaceCondition = "Probable I/O race condition detected while copying memory. The I/O package is not thread safe by default. In multithreaded applications, a stream must be accessed in a thread-safe way, such as a thread-safe wrapper returned by TextReader's or TextWriter's Synchronized methods. This also applies to classes like StreamWriter and StreamReader."; - public const string IO_BindHandleFailed = "BindHandle for ThreadPool failed on this handle."; public const string IO_OperationAborted = "The I/O operation has been aborted because of either a thread exit or an application request."; public const string NotSupported_UnseekableStream = "Stream does not support seeking."; public const string ObjectDisposed_StreamClosed = "Can not access a closed Stream."; @@ -140,6 +138,9 @@ partial class SR public const string IO_SharingViolation_NoFileName = "The process cannot access the port because it is being used by another process."; public const string IO_SharingViolation_File = "The process cannot access the port '{0}' because it is being used by another process."; public const string UnauthorizedAccess_IODenied_Path = "Access to the port '{0}' is denied."; + public const string PlatformNotSupported_IOPorts = "System.IO.Ports is currently only supported on Windows."; + public const string PlatformNotSupported_SerialPort_GetPortNames = "Enumeration of serial port names is not supported on the current platform."; + public const string IO_PathTooLong_Path = "The specified port name '{0}' is too long. The port name must be less than 260 characters."; public const string net_log_listener_delegate_exception = "Sending 500 response, AuthenticationSchemeSelectorDelegate threw an exception: {0}."; public const string net_log_listener_unsupported_authentication_scheme = "Received a request with an unsupported authentication scheme, Authorization:{0} SupportedSchemes:{1}."; public const string net_log_listener_unmatched_authentication_scheme = "Received a request with an unmatched or no authentication scheme. AuthenticationSchemes:{0}, Authorization:{1}."; @@ -149,6 +150,7 @@ partial class SR public const string net_listener_detach_error = "Can't detach Url group from request queue. Status code: {0}."; public const string net_listener_scheme = "Only Uri prefixes starting with 'http://' or 'https://' are supported."; public const string net_listener_host = "Only Uri prefixes with a valid hostname are supported."; + public const string net_listener_not_supported = "The request is not supported."; public const string net_listener_mustcall = "Please call the {0} method before calling this method."; public const string net_listener_slash = "Only Uri prefixes ending in '/' are allowed."; public const string net_listener_already = "Failed to listen on prefix '{0}' because it conflicts with an existing registration on the machine."; @@ -224,11 +226,8 @@ partial class SR public const string net_invalid_enum = "The specified value is not valid in the '{0}' enumeration."; public const string net_auth_message_not_encrypted = "Protocol error: A received message contains a valid signature but it was not encrypted as required by the effective Protection Level."; public const string SSPIInvalidHandleType = "'{0}' is not a supported handle type."; - public const string net_cannot_change_after_headers = "Cannot be changed after headers are sent."; - public const string offset_out_of_range = "Offset exceeds the length of buffer."; public const string net_io_operation_aborted = "I/O operation aborted: '{0}'."; public const string net_invalid_path = "Invalid path."; - public const string net_no_client_certificate = "Client certificate not found."; public const string net_listener_auth_errors = "Authentication errors."; public const string net_listener_close = "Listener closed."; public const string net_invalid_port = "Invalid port in prefix."; diff --git a/mcs/class/corlib/Makefile b/mcs/class/corlib/Makefile index 910f7b672f..4e7a250bf5 100644 --- a/mcs/class/corlib/Makefile +++ b/mcs/class/corlib/Makefile @@ -93,8 +93,10 @@ ifdef MONO_FEATURE_APPLE_X509 LIB_MCS_FLAGS += -d:MONO_FEATURE_APPLE_X509 endif +COREFX_FLAGS := -d:FEATURE_PORTABLE_SPAN + WARNING_ABOUT_DISABLED_WARNING=1635 -LOCAL_MCS_FLAGS = -unsafe -nostdlib -nowarn:612,618,3001,3002,3003,$(WARNING_ABOUT_DISABLED_WARNING) -d:INSIDE_CORLIB,MONO_CULTURE_DATA -d:LIBC $(REFERENCE_SOURCES_FLAGS) +LOCAL_MCS_FLAGS = -unsafe -nostdlib -nowarn:612,618,3001,3002,3003,$(WARNING_ABOUT_DISABLED_WARNING) -d:INSIDE_CORLIB,MONO_CULTURE_DATA -d:LIBC $(REFERENCE_SOURCES_FLAGS) $(COREFX_FLAGS) DEFAULT_REFERENCES = TEST_LIB_REFS = System.Core System diff --git a/mcs/class/corlib/ReferenceSources/String.cs b/mcs/class/corlib/ReferenceSources/String.cs index 40707aa779..a4a50c8eb4 100644 --- a/mcs/class/corlib/ReferenceSources/String.cs +++ b/mcs/class/corlib/ReferenceSources/String.cs @@ -41,12 +41,25 @@ namespace System { partial class String { + [MethodImplAttribute (MethodImplOptions.InternalCall)] + public extern String (ReadOnlySpan value); + public int Length { get { return m_stringLength; } } + public unsafe static implicit operator ReadOnlySpan (String value) + { + if (value == null) + return default; + + fixed (void* start = &value.m_firstChar) + return new ReadOnlySpan (start, value.Length); + } + + internal static unsafe int CompareOrdinalUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB) { if (strA == null) { @@ -743,5 +756,17 @@ namespace System // GetString () is called even when length == 0 return enc.GetString (bytes); } + + unsafe String CreateString (ReadOnlySpan value) + { + if (value.Length == 0) + return Empty; + + String result = FastAllocateString (value.Length); + fixed (char *dest = result, ptr = &value.DangerousGetPinnableReference ()) + wstrcpy (dest, ptr, value.Length); + + return result; + } } } \ No newline at end of file diff --git a/mcs/class/corlib/coreclr/SorterArray.cs b/mcs/class/corlib/coreclr/SorterArray.cs index da5086b01e..d8184f811d 100644 --- a/mcs/class/corlib/coreclr/SorterArray.cs +++ b/mcs/class/corlib/coreclr/SorterArray.cs @@ -65,7 +65,7 @@ namespace System try { - IntroSort(left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2(keys.Length)); + IntroSort(left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(keys.Length)); } catch (IndexOutOfRangeException) { @@ -271,7 +271,7 @@ namespace System try { - IntroSort(left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2(keys.Length)); + IntroSort(left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(keys.Length)); } catch (IndexOutOfRangeException) { diff --git a/mcs/class/corlib/corefx/SR.cs b/mcs/class/corlib/corefx/SR.cs deleted file mode 100644 index bf0de71551..0000000000 --- a/mcs/class/corlib/corefx/SR.cs +++ /dev/null @@ -1,828 +0,0 @@ -// -// This file was generated by resx2sr tool -// - -partial class SR -{ - public const string Arg_AccessException = "Cannot access member."; - public const string Arg_AccessViolationException = "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."; - public const string Arg_ApplicationException = "Error in the application."; - public const string Arg_ArgumentException = "Value does not fall within the expected range."; - public const string Arg_ArgumentOutOfRangeException = "Specified argument was out of the range of valid values."; - public const string Arg_ArithmeticException = "Overflow or underflow in the arithmetic operation."; - public const string Arg_ArrayPlusOffTooSmall = "Destination array is not long enough to copy all the items in the collection. Check array index and length."; - public const string Arg_ArrayTypeMismatchException = "Attempted to access an element as a type incompatible with the array."; - public const string Arg_ArrayZeroError = "Array must not be of length zero."; - public const string Arg_BadImageFormatException = "Format of the executable (.exe) or library (.dll) is invalid."; - public const string Arg_BogusIComparer = "Unable to sort because the IComparer.Compare() method returns inconsistent results. Either a value does not compare equal to itself, or one value repeatedly compared to another value yields different results. IComparer: '{0}'."; - public const string Arg_CannotBeNaN = "TimeSpan does not accept floating point Not-a-Number values."; - public const string Arg_CannotHaveNegativeValue = "String cannot contain a minus sign if the base is not 10."; - public const string Arg_CopyNonBlittableArray = "Arrays must contain only blittable data in order to be copied to unmanaged memory."; - public const string Arg_CopyOutOfRange = "Requested range extends past the end of the array."; - public const string Arg_CryptographyException = "Error occurred during a cryptographic operation."; - public const string Arg_DataMisalignedException = "A datatype misalignment was detected in a load or store instruction."; - public const string Arg_DateTimeRange = "Combination of arguments to the DateTime constructor is out of the legal range."; - public const string Arg_DirectoryNotFoundException = "Attempted to access a path that is not on the disk."; - public const string Arg_DecBitCtor = "Decimal byte array constructor requires an array of length four containing valid decimal bytes."; - public const string Arg_DivideByZero = "Attempted to divide by zero."; - public const string Arg_DlgtNullInst = "Delegate to an instance method cannot have null 'this'."; - public const string Arg_DlgtTypeMis = "Delegates must be of the same type."; - public const string Arg_DuplicateWaitObjectException = "Duplicate objects in argument."; - public const string Arg_EnumAndObjectMustBeSameType = "Object must be the same type as the enum. The type passed in was '{0}'; the enum type was '{1}'."; - public const string Arg_EntryPointNotFoundException = "Entry point was not found."; - public const string Arg_EntryPointNotFoundExceptionParameterized = "Unable to find an entry point named '{0}' in DLL '{1}'."; - public const string Arg_EnumIllegalVal = "Illegal enum value: {0}."; - public const string Arg_ExecutionEngineException = "Internal error in the runtime."; - public const string Arg_ExternalException = "External component has thrown an exception."; - public const string Arg_FieldAccessException = "Attempted to access a field that is not accessible by the caller."; - public const string Arg_FormatException = "One of the identified items was in an invalid format."; - public const string Arg_GuidArrayCtor = "Byte array for GUID must be exactly {0} bytes long."; - public const string Arg_HexStyleNotSupported = "The number style AllowHexSpecifier is not supported on floating point data types."; - public const string Arg_HTCapacityOverflow = "Hashtable's capacity overflowed and went negative. Check load factor, capacity and the current size of the table."; - public const string Arg_IndexOutOfRangeException = "Index was outside the bounds of the array."; - public const string Arg_InsufficientExecutionStackException = "Insufficient stack to continue executing the program safely. This can happen from having too many functions on the call stack or function on the stack using too much stack space."; - public const string Arg_InvalidBase = "Invalid Base."; - public const string Arg_InvalidCastException = "Specified cast is not valid."; - public const string Arg_InvalidHexStyle = "With the AllowHexSpecifier bit set in the enum bit field, the only other valid bits that can be combined into the enum value must be a subset of those in HexNumber."; - public const string Arg_InvalidOperationException = "Operation is not valid due to the current state of the object."; - public const string Arg_OleAutDateInvalid = " Not a legal OleAut date."; - public const string Arg_OleAutDateScale = "OleAut date did not convert to a DateTime correctly."; - public const string Arg_InvalidRuntimeTypeHandle = "Invalid RuntimeTypeHandle."; - public const string Arg_IOException = "I/O error occurred."; - public const string Arg_KeyNotFound = "The given key was not present in the dictionary."; - public const string Arg_LongerThanSrcString = "Source string was not long enough. Check sourceIndex and count."; - public const string Arg_LowerBoundsMustMatch = "The arrays' lower bounds must be identical."; - public const string Arg_MissingFieldException = "Attempted to access a non-existing field."; - public const string Arg_MethodAccessException = "Attempt to access the method failed."; - public const string Arg_MissingMemberException = "Attempted to access a missing member."; - public const string Arg_MissingMethodException = "Attempted to access a missing method."; - public const string Arg_MulticastNotSupportedException = "Attempted to add multiple callbacks to a delegate that does not support multicast."; - public const string Arg_MustBeBoolean = "Object must be of type Boolean."; - public const string Arg_MustBeByte = "Object must be of type Byte."; - public const string Arg_MustBeChar = "Object must be of type Char."; - public const string Arg_MustBeDateTime = "Object must be of type DateTime."; - public const string Arg_MustBeDateTimeOffset = "Object must be of type DateTimeOffset."; - public const string Arg_MustBeDecimal = "Object must be of type Decimal."; - public const string Arg_MustBeDouble = "Object must be of type Double."; - public const string Arg_MustBeEnum = "Type provided must be an Enum."; - public const string Arg_MustBeGuid = "Object must be of type GUID."; - public const string Arg_MustBeInt16 = "Object must be of type Int16."; - public const string Arg_MustBeInt32 = "Object must be of type Int32."; - public const string Arg_MustBeInt64 = "Object must be of type Int64."; - public const string Arg_MustBePrimArray = "Object must be an array of primitives."; - public const string Arg_MustBeSByte = "Object must be of type SByte."; - public const string Arg_MustBeSingle = "Object must be of type Single."; - public const string Arg_MustBeStatic = "Method must be a static method."; - public const string Arg_MustBeString = "Object must be of type String."; - public const string Arg_MustBeStringPtrNotAtom = "The pointer passed in as a String must not be in the bottom 64K of the process's address space."; - public const string Arg_MustBeTimeSpan = "Object must be of type TimeSpan."; - public const string Arg_MustBeUInt16 = "Object must be of type UInt16."; - public const string Arg_MustBeUInt32 = "Object must be of type UInt32."; - public const string Arg_MustBeUInt64 = "Object must be of type UInt64."; - public const string Arg_MustBeVersion = "Object must be of type Version."; - public const string Arg_NeedAtLeast1Rank = "Must provide at least one rank."; - public const string Arg_Need2DArray = "Array was not a two-dimensional array."; - public const string Arg_Need3DArray = "Array was not a three-dimensional array."; - public const string Arg_NegativeArgCount = "Argument count must not be negative."; - public const string Arg_NotFiniteNumberException = "Arg_NotFiniteNumberException = Number encountered was not a finite quantity."; - public const string Arg_NonZeroLowerBound = "The lower bound of target array must be zero."; - public const string Arg_NotGenericParameter = "Method may only be called on a Type for which Type.IsGenericParameter is true."; - public const string Arg_NotImplementedException = "The method or operation is not implemented."; - public const string Arg_NotSupportedException = "Specified method is not supported."; - public const string Arg_NotSupportedNonZeroLowerBound = "Arrays with non-zero lower bounds are not supported."; - public const string Arg_NullReferenceException = "Object reference not set to an instance of an object."; - public const string Arg_ObjObjEx = "Object of type '{0}' cannot be converted to type '{1}'."; - public const string Arg_OverflowException = "Arithmetic operation resulted in an overflow."; - public const string Arg_OutOfMemoryException = "Insufficient memory to continue the execution of the program."; - public const string Arg_PlatformNotSupported = "Operation is not supported on this platform."; - public const string Arg_ParamName_Name = "Parameter name: {0}"; - public const string Arg_PathIllegal = "The path is not of a legal form."; - public const string Arg_PathIllegalUNC = "The UNC path should be of the form \\\\\\\\server\\\\share."; - public const string Arg_RankException = "Attempted to operate on an array with the incorrect number of dimensions."; - public const string Arg_RankIndices = "Indices length does not match the array rank."; - public const string Arg_RankMultiDimNotSupported = "Only single dimensional arrays are supported for the requested action."; - public const string Arg_RanksAndBounds = "Number of lengths and lowerBounds must match."; - public const string Arg_RegGetOverflowBug = "RegistryKey.GetValue does not allow a String that has a length greater than Int32.MaxValue."; - public const string Arg_RegKeyNotFound = "The specified registry key does not exist."; - public const string Arg_SecurityException = "Security error."; - public const string Arg_StackOverflowException = "Operation caused a stack overflow."; - public const string Arg_SynchronizationLockException = "Object synchronization method was called from an unsynchronized block of code."; - public const string Arg_SystemException = "System error."; - public const string Arg_TargetInvocationException = "Exception has been thrown by the target of an invocation."; - public const string Arg_TargetParameterCountException = "Number of parameters specified does not match the expected number."; - public const string Arg_DefaultValueMissingException = "Missing parameter does not have a default value."; - public const string Arg_ThreadStartException = "Thread failed to start."; - public const string Arg_ThreadStateException = "Thread was in an invalid state for the operation being executed."; - public const string Arg_TimeoutException = "The operation has timed out."; - public const string Arg_TypeAccessException = "Attempt to access the type failed."; - public const string Arg_TypeLoadException = "Failure has occurred while loading a type."; - public const string Arg_UnauthorizedAccessException = "Attempted to perform an unauthorized operation."; - public const string Arg_VersionString = "Version string portion was too short or too long."; - public const string Arg_WrongType = "The value '{0}' is not of type '{1}' and cannot be used in this generic collection."; - public const string Argument_AbsolutePathRequired = "Absolute path information is required."; - public const string Argument_AddingDuplicate = "An item with the same key has already been added. Key: {0}"; - public const string Argument_AddingDuplicate__ = "Item has already been added. Key in dictionary: '{0}' Key being added: '{1}'"; - public const string Argument_AdjustmentRulesNoNulls = "The AdjustmentRule array cannot contain null elements."; - public const string Argument_AdjustmentRulesOutOfOrder = "The elements of the AdjustmentRule array must be in chronological order and must not overlap."; - public const string Argument_BadFormatSpecifier = "Format specifier was invalid."; - public const string Argument_CodepageNotSupported = "{0} is not a supported code page."; - public const string Argument_CompareOptionOrdinal = "CompareOption.Ordinal cannot be used with other options."; - public const string Argument_ConflictingDateTimeRoundtripStyles = "The DateTimeStyles value RoundtripKind cannot be used with the values AssumeLocal, AssumeUniversal or AdjustToUniversal."; - public const string Argument_ConflictingDateTimeStyles = "The DateTimeStyles values AssumeLocal and AssumeUniversal cannot be used together."; - public const string Argument_ConversionOverflow = "Conversion buffer overflow."; - public const string Argument_ConvertMismatch = "The conversion could not be completed because the supplied DateTime did not have the Kind property set correctly. For example, when the Kind property is DateTimeKind.Local, the source time zone must be TimeZoneInfo.Local."; - public const string Argument_CultureInvalidIdentifier = "{0} is an invalid culture identifier."; - public const string Argument_CultureIetfNotSupported = "Culture IETF Name {0} is not a recognized IETF name."; - public const string Argument_CultureIsNeutral = "Culture ID {0} (0x{0:X4}) is a neutral culture; a region cannot be created from it."; - public const string Argument_CultureNotSupported = "Culture is not supported."; - public const string Argument_CustomCultureCannotBePassedByNumber = "Customized cultures cannot be passed by LCID, only by name."; - public const string Argument_DateTimeBadBinaryData = "The binary data must result in a DateTime with ticks between DateTime.MinValue.Ticks and DateTime.MaxValue.Ticks."; - public const string Argument_DateTimeHasTicks = "The supplied DateTime must have the Year, Month, and Day properties set to 1. The time cannot be specified more precisely than whole milliseconds."; - public const string Argument_DateTimeHasTimeOfDay = "The supplied DateTime includes a TimeOfDay setting. This is not supported."; - public const string Argument_DateTimeIsInvalid = "The supplied DateTime represents an invalid time. For example, when the clock is adjusted forward, any time in the period that is skipped is invalid."; - public const string Argument_DateTimeIsNotAmbiguous = "The supplied DateTime is not in an ambiguous time range."; - public const string Argument_DateTimeKindMustBeUnspecified = "The supplied DateTime must have the Kind property set to DateTimeKind.Unspecified."; - public const string Argument_DateTimeOffsetInvalidDateTimeStyles = "The DateTimeStyles value 'NoCurrentDateDefault' is not allowed when parsing DateTimeOffset."; - public const string Argument_DateTimeOffsetIsNotAmbiguous = "The supplied DateTimeOffset is not in an ambiguous time range."; - public const string Argument_EmptyDecString = "Decimal separator cannot be the empty string."; - public const string Argument_EmptyName = "Empty name is not legal."; - public const string Argument_EmptyWaithandleArray = "Waithandle array may not be empty."; - public const string Argument_EncoderFallbackNotEmpty = "Must complete Convert() operation or call Encoder.Reset() before calling GetBytes() or GetByteCount(). Encoder '{0}' fallback '{1}'."; - public const string Argument_EncodingConversionOverflowBytes = "The output byte buffer is too small to contain the encoded data, encoding '{0}' fallback '{1}'."; - public const string Argument_EncodingConversionOverflowChars = "The output char buffer is too small to contain the decoded characters, encoding '{0}' fallback '{1}'."; - public const string Argument_EncodingNotSupported = "'{0}' is not a supported encoding name. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method."; - public const string Argument_EnumTypeDoesNotMatch = "The argument type, '{0}', is not the same as the enum type '{1}'."; - public const string Argument_FallbackBufferNotEmpty = "Cannot change fallback when buffer is not empty. Previous Convert() call left data in the fallback buffer."; - public const string Argument_IdnBadLabelSize = "IDN labels must be between 1 and 63 characters long."; - public const string Argument_IdnBadPunycode = "Invalid IDN encoded string."; - public const string Argument_IdnIllegalName = "Decoded string is not a valid IDN name."; - public const string Argument_ImplementIComparable = "At least one object must implement IComparable."; - public const string Argument_InvalidArgumentForComparison = "Type of argument is not compatible with the generic comparer."; - public const string Argument_InvalidArrayLength = "Length of the array must be {0}."; - public const string Argument_InvalidArrayType = "Target array type is not compatible with the type of items in the collection."; - public const string Argument_InvalidCalendar = "Not a valid calendar for the given culture."; - public const string Argument_InvalidCharSequence = "Invalid Unicode code point found at index {0}."; - public const string Argument_InvalidCharSequenceNoIndex = "String contains invalid Unicode code points."; - public const string Argument_InvalidCodePageBytesIndex = "Unable to translate bytes {0} at index {1} from specified code page to Unicode."; - public const string Argument_InvalidCodePageConversionIndex = "Unable to translate Unicode character \\\\u{0:X4} at index {1} to specified code page."; - public const string Argument_InvalidCultureName = "Culture name '{0}' is not supported."; - public const string Argument_InvalidDateTimeKind = "Invalid DateTimeKind value."; - public const string Argument_InvalidDateTimeStyles = "An undefined DateTimeStyles value is being used."; - public const string Argument_InvalidDigitSubstitution = "The DigitSubstitution property must be of a valid member of the DigitShapes enumeration. Valid entries include Context, NativeNational or None."; - public const string Argument_InvalidEnumValue = "The value '{0}' is not valid for this usage of the type {1}."; - public const string Argument_InvalidFlag = "Value of flags is invalid."; - public const string Argument_InvalidGroupSize = "Every element in the value array should be between one and nine, except for the last element, which can be zero."; - public const string Argument_InvalidHighSurrogate = "Found a high surrogate char without a following low surrogate at index: {0}. The input may not be in this encoding, or may not contain valid Unicode (UTF-16) characters."; - public const string Argument_InvalidId = "The specified ID parameter '{0}' is not supported."; - public const string Argument_InvalidLowSurrogate = "Found a low surrogate char without a preceding high surrogate at index: {0}. The input may not be in this encoding, or may not contain valid Unicode (UTF-16) characters."; - public const string Argument_InvalidNativeDigitCount = "The NativeDigits array must contain exactly ten members."; - public const string Argument_InvalidNativeDigitValue = "Each member of the NativeDigits array must be a single text element (one or more UTF16 code points) with a Unicode Nd (Number, Decimal Digit) property indicating it is a digit."; - public const string Argument_InvalidNeutralRegionName = "The region name {0} should not correspond to neutral culture; a specific culture name is required."; - public const string Argument_InvalidNormalizationForm = "Invalid normalization form."; - public const string Argument_InvalidNumberStyles = "An undefined NumberStyles value is being used."; - public const string Argument_InvalidOffLen = "Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection."; - public const string Argument_InvalidPathChars = "Illegal characters in path."; - public const string Argument_InvalidREG_TZI_FORMAT = "The REG_TZI_FORMAT structure is corrupt."; - public const string Argument_InvalidResourceCultureName = "The given culture name '{0}' cannot be used to locate a resource file. Resource filenames must consist of only letters, numbers, hyphens or underscores."; - public const string Argument_InvalidSerializedString = "The specified serialized string '{0}' is not supported."; - public const string Argument_InvalidTimeSpanStyles = "An undefined TimeSpanStyles value is being used."; - public const string Argument_MustBeFalse = "Argument must be initialized to false"; - public const string Argument_NoEra = "No Era was supplied."; - public const string Argument_NoRegionInvariantCulture = "There is no region associated with the Invariant Culture (Culture ID: 0x7F)."; - public const string Argument_NotIsomorphic = "Object contains non-primitive or non-blittable data."; - public const string Argument_OffsetLocalMismatch = "The UTC Offset of the local dateTime parameter does not match the offset argument."; - public const string Argument_OffsetPrecision = "Offset must be specified in whole minutes."; - public const string Argument_OffsetOutOfRange = "Offset must be within plus or minus 14 hours."; - public const string Argument_OffsetUtcMismatch = "The UTC Offset for Utc DateTime instances must be 0."; - public const string Argument_OneOfCulturesNotSupported = "Culture name {0} or {1} is not supported."; - public const string Argument_OnlyMscorlib = "Only mscorlib's assembly is valid."; - public const string Argument_OutOfOrderDateTimes = "The DateStart property must come before the DateEnd property."; - public const string ArgumentOutOfRange_HugeArrayNotSupported = "Arrays larger than 2GB are not supported."; - public const string ArgumentOutOfRange_Index = "Index was out of range. Must be non-negative and less than the size of the collection."; - public const string ArgumentOutOfRange_Length = "The specified length exceeds maximum capacity of SecureString."; - public const string ArgumentOutOfRange_LengthTooLarge = "The specified length exceeds the maximum value of {0}."; - public const string ArgumentOutOfRange_NeedNonNegNum = "Non-negative number required."; - public const string ArgumentOutOfRange_NeedNonNegNumRequired = "Non-negative number required."; - public const string Argument_PathFormatNotSupported = "The given path's format is not supported."; - public const string Argument_RecursiveFallback = "Recursive fallback not allowed for character \\\\u{0:X4}."; - public const string Argument_RecursiveFallbackBytes = "Recursive fallback not allowed for bytes {0}."; - public const string Argument_ResultCalendarRange = "The result is out of the supported range for this calendar. The result should be between {0} (Gregorian date) and {1} (Gregorian date), inclusive."; - public const string Argument_SemaphoreInitialMaximum = "The initial count for the semaphore must be greater than or equal to zero and less than the maximum count."; - public const string Argument_TimeSpanHasSeconds = "The TimeSpan parameter cannot be specified more precisely than whole minutes."; - public const string Argument_TimeZoneNotFound = "The time zone ID '{0}' was not found on the local computer."; - public const string Argument_TimeZoneInfoBadTZif = "The tzfile does not begin with the magic characters 'TZif'. Please verify that the file is not corrupt."; - public const string Argument_TimeZoneInfoInvalidTZif = "The TZif data structure is corrupt."; - public const string Argument_ToExclusiveLessThanFromExclusive = "fromInclusive must be less than or equal to toExclusive."; - public const string Argument_TransitionTimesAreIdentical = "The DaylightTransitionStart property must not equal the DaylightTransitionEnd property."; - public const string Argument_UTCOutOfRange = "The UTC time represented when the offset is applied must be between year 0 and 10,000."; - public const string Argument_WaitHandleNameTooLong = "The name can be no more than {0} characters in length."; - public const string ArgumentException_OtherNotArrayOfCorrectLength = "Object is not a array with the same number of elements as the array to compare it to."; - public const string ArgumentException_TupleIncorrectType = "Argument must be of type {0}."; - public const string ArgumentException_TupleLastArgumentNotATuple = "The last element of an eight element tuple must be a Tuple."; - public const string ArgumentException_ValueTupleIncorrectType = "Argument must be of type {0}."; - public const string ArgumentException_ValueTupleLastArgumentNotAValueTuple = "The last element of an eight element ValueTuple must be a ValueTuple."; - public const string ArgumentNull_Array = "Array cannot be null."; - public const string ArgumentNull_ArrayElement = "At least one element in the specified array was null."; - public const string ArgumentNull_ArrayValue = "Found a null value within an array."; - public const string ArgumentNull_Generic = "Value cannot be null."; - public const string ArgumentNull_Key = "Key cannot be null."; - public const string ArgumentNull_Obj = "Object cannot be null."; - public const string ArgumentNull_String = "String reference not set to an instance of a String."; - public const string ArgumentNull_Type = "Type cannot be null."; - public const string ArgumentNull_Waithandles = "The waitHandles parameter cannot be null."; - public const string ArgumentNull_WithParamName = "Parameter '{0}' cannot be null."; - public const string ArgumentOutOfRange_AddValue = "Value to add was out of range."; - public const string ArgumentOutOfRange_ActualValue = "Actual value was {0}."; - public const string ArgumentOutOfRange_BadYearMonthDay = "Year, Month, and Day parameters describe an un-representable DateTime."; - public const string ArgumentOutOfRange_BadHourMinuteSecond = "Hour, Minute, and Second parameters describe an un-representable DateTime."; - public const string ArgumentOutOfRange_BiggerThanCollection = "Must be less than or equal to the size of the collection."; - public const string ArgumentOutOfRange_Bounds_Lower_Upper = "Argument must be between {0} and {1}."; - public const string ArgumentOutOfRange_CalendarRange = "Specified time is not supported in this calendar. It should be between {0} (Gregorian date) and {1} (Gregorian date), inclusive."; - public const string ArgumentOutOfRange_Capacity = "Capacity exceeds maximum capacity."; - public const string ArgumentOutOfRange_Count = "Count must be positive and count must refer to a location within the string/array/collection."; - public const string ArgumentOutOfRange_DateArithmetic = "The added or subtracted value results in an un-representable DateTime."; - public const string ArgumentOutOfRange_DateTimeBadMonths = "Months value must be between +/-120000."; - public const string ArgumentOutOfRange_DateTimeBadTicks = "Ticks must be between DateTime.MinValue.Ticks and DateTime.MaxValue.Ticks."; - public const string ArgumentOutOfRange_DateTimeBadYears = "Years value must be between +/-10000."; - public const string ArgumentOutOfRange_Day = "Day must be between 1 and {0} for month {1}."; - public const string ArgumentOutOfRange_DayOfWeek = "The DayOfWeek enumeration must be in the range 0 through 6."; - public const string ArgumentOutOfRange_DayParam = "The Day parameter must be in the range 1 through 31."; - public const string ArgumentOutOfRange_DecimalRound = "Decimal can only round to between 0 and 28 digits of precision."; - public const string ArgumentOutOfRange_DecimalScale = "Decimal's scale value must be between 0 and 28, inclusive."; - public const string ArgumentOutOfRange_EndIndexStartIndex = "endIndex cannot be greater than startIndex."; - public const string ArgumentOutOfRange_Enum = "Enum value was out of legal range."; - public const string ArgumentOutOfRange_Era = "Time value was out of era range."; - public const string ArgumentOutOfRange_FileTimeInvalid = "Not a valid Win32 FileTime."; - public const string ArgumentOutOfRange_GenericPositive = "Value must be positive."; - public const string ArgumentOutOfRange_GetByteCountOverflow = "Too many characters. The resulting number of bytes is larger than what can be returned as an int."; - public const string ArgumentOutOfRange_GetCharCountOverflow = "Too many bytes. The resulting number of chars is larger than what can be returned as an int."; - public const string ArgumentOutOfRange_IndexCount = "Index and count must refer to a location within the string."; - public const string ArgumentOutOfRange_IndexCountBuffer = "Index and count must refer to a location within the buffer."; - public const string ArgumentOutOfRange_IndexLength = "Index and length must refer to a location within the string."; - public const string ArgumentOutOfRange_IndexString = "Index was out of range. Must be non-negative and less than the length of the string."; - public const string ArgumentOutOfRange_InvalidEraValue = "Era value was not valid."; - public const string ArgumentOutOfRange_InvalidHighSurrogate = "A valid high surrogate character is between 0xd800 and 0xdbff, inclusive."; - public const string ArgumentOutOfRange_InvalidLowSurrogate = "A valid low surrogate character is between 0xdc00 and 0xdfff, inclusive."; - public const string ArgumentOutOfRange_InvalidUTF32 = "A valid UTF32 value is between 0x000000 and 0x10ffff, inclusive, and should not include surrogate codepoint values (0x00d800 ~ 0x00dfff)."; - public const string ArgumentOutOfRange_LengthGreaterThanCapacity = "The length cannot be greater than the capacity."; - public const string ArgumentOutOfRange_ListInsert = "Index must be within the bounds of the List."; - public const string ArgumentOutOfRange_ListItem = "Index was out of range. Must be non-negative and less than the size of the list."; - public const string ArgumentOutOfRange_ListRemoveAt = "Index was out of range. Must be non-negative and less than the size of the list."; - public const string ArgumentOutOfRange_Month = "Month must be between one and twelve."; - public const string ArgumentOutOfRange_MonthParam = "The Month parameter must be in the range 1 through 12."; - public const string ArgumentOutOfRange_MustBeNonNegInt32 = "Value must be non-negative and less than or equal to Int32.MaxValue."; - public const string ArgumentOutOfRange_MustBeNonNegNum = "'{0}' must be non-negative."; - public const string ArgumentOutOfRange_MustBePositive = "'{0}' must be greater than zero."; - public const string ArgumentOutOfRange_NeedNonNegOrNegative1 = "Number must be either non-negative and less than or equal to Int32.MaxValue or -1."; - public const string ArgumentOutOfRange_NeedPosNum = "Positive number required."; - public const string ArgumentOutOfRange_NegativeCapacity = "Capacity must be positive."; - public const string ArgumentOutOfRange_NegativeCount = "Count cannot be less than zero."; - public const string ArgumentOutOfRange_NegativeLength = "Length cannot be less than zero."; - public const string ArgumentOutOfRange_OffsetLength = "Offset and length must refer to a position in the string."; - public const string ArgumentOutOfRange_OffsetOut = "Either offset did not refer to a position in the string, or there is an insufficient length of destination character array."; - public const string ArgumentOutOfRange_PartialWCHAR = "Pointer startIndex and length do not refer to a valid string."; - public const string ArgumentOutOfRange_Range = "Valid values are between {0} and {1}, inclusive."; - public const string ArgumentOutOfRange_RoundingDigits = "Rounding digits must be between 0 and 15, inclusive."; - public const string ArgumentOutOfRange_SmallCapacity = "capacity was less than the current size."; - public const string ArgumentOutOfRange_SmallMaxCapacity = "MaxCapacity must be one or greater."; - public const string ArgumentOutOfRange_StartIndex = "StartIndex cannot be less than zero."; - public const string ArgumentOutOfRange_StartIndexLargerThanLength = "startIndex cannot be larger than length of string."; - public const string ArgumentOutOfRange_StartIndexLessThanLength = "startIndex must be less than length of string."; - public const string ArgumentOutOfRange_UtcOffset = "The TimeSpan parameter must be within plus or minus 14.0 hours."; - public const string ArgumentOutOfRange_UtcOffsetAndDaylightDelta = "The sum of the BaseUtcOffset and DaylightDelta properties must within plus or minus 14.0 hours."; - public const string ArgumentOutOfRange_Version = "Version's parameters must be greater than or equal to zero."; - public const string ArgumentOutOfRange_Week = "The Week parameter must be in the range 1 through 5."; - public const string ArgumentOutOfRange_Year = "Year must be between 1 and 9999."; - public const string Arithmetic_NaN = "Function does not accept floating point Not-a-Number values."; - public const string ArrayTypeMismatch_CantAssignType = "Source array type cannot be assigned to destination array type."; - public const string BadImageFormatException_CouldNotLoadFileOrAssembly = "Could not load file or assembly '{0}'. An attempt was made to load a program with an incorrect format."; - public const string CollectionCorrupted = "A prior operation on this collection was interrupted by an exception. Collection's state is no longer trusted."; - public const string Exception_EndOfInnerExceptionStack = "--- End of inner exception stack trace ---"; - public const string Exception_WasThrown = "Exception of type '{0}' was thrown."; - public const string Format_BadBase64Char = "The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters."; - public const string Format_BadBase64CharArrayLength = "Invalid length for a Base-64 char array or string."; - public const string Format_BadBoolean = "String was not recognized as a valid Boolean."; - public const string Format_BadFormatSpecifier = "Format specifier was invalid."; - public const string Format_BadQuote = "Cannot find a matching quote character for the character '{0}'."; - public const string Format_EmptyInputString = "Input string was either empty or contained only whitespace."; - public const string Format_GuidHexPrefix = "Expected hex 0x in '{0}'."; - public const string Format_GuidInvLen = "Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)."; - public const string Format_GuidInvalidChar = "Guid string should only contain hexadecimal characters."; - public const string Format_GuidBrace = "Expected {0xdddddddd, etc}."; - public const string Format_GuidComma = "Could not find a comma, or the length between the previous token and the comma was zero (i.e., '0x,'etc.)."; - public const string Format_GuidBraceAfterLastNumber = "Could not find a brace, or the length between the previous token and the brace was zero (i.e., '0x,'etc.)."; - public const string Format_GuidDashes = "Dashes are in the wrong position for GUID parsing."; - public const string Format_GuidEndBrace = "Could not find the ending brace."; - public const string Format_ExtraJunkAtEnd = "Additional non-parsable characters are at the end of the string."; - public const string Format_GuidUnrecognized = "Unrecognized Guid format."; - public const string Format_IndexOutOfRange = "Index (zero based) must be greater than or equal to zero and less than the size of the argument list."; - public const string Format_InvalidGuidFormatSpecification = "Format String can be only 'D', 'd', 'N', 'n', 'P', 'p', 'B', 'b', 'X' or 'x'."; - public const string Format_InvalidString = "Input string was not in a correct format."; - public const string Format_NeedSingleChar = "String must be exactly one character long."; - public const string Format_NoParsibleDigits = "Could not find any recognizable digits."; - public const string Format_BadTimeSpan = "String was not recognized as a valid TimeSpan."; - public const string InsufficientMemory_MemFailPoint = "Insufficient available memory to meet the expected demands of an operation at this time. Please try again later."; - public const string InsufficientMemory_MemFailPoint_TooBig = "Insufficient memory to meet the expected demands of an operation, and this system is likely to never satisfy this request. If this is a 32 bit system, consider booting in 3 GB mode."; - public const string InsufficientMemory_MemFailPoint_VAFrag = "Insufficient available memory to meet the expected demands of an operation at this time, possibly due to virtual address space fragmentation. Please try again later."; - public const string InvalidCast_CannotCastNullToValueType = "Null object cannot be converted to a value type."; - public const string InvalidCast_DownCastArrayElement = "At least one element in the source array could not be cast down to the destination array type."; - public const string InvalidCast_FromTo = "Invalid cast from '{0}' to '{1}'."; - public const string InvalidCast_IConvertible = "Object must implement IConvertible."; - public const string InvalidCast_StoreArrayElement = "Object cannot be stored in an array of this type."; - public const string InvalidOperation_Calling = "WinRT Interop has already been initialized and cannot be initialized again."; - public const string InvalidOperation_DateTimeParsing = "Internal Error in DateTime and Calendar operations."; - public const string InvalidOperation_EnumEnded = "Enumeration already finished."; - public const string InvalidOperation_EnumFailedVersion = "Collection was modified; enumeration operation may not execute."; - public const string InvalidOperation_EnumNotStarted = "Enumeration has not started. Call MoveNext."; - public const string InvalidOperation_EnumOpCantHappen = "Enumeration has either not started or has already finished."; - public const string InvalidOperation_HandleIsNotInitialized = "Handle is not initialized."; - public const string InvalidOperation_IComparerFailed = "Failed to compare two elements in the array."; - public const string InvalidOperation_NoValue = "Nullable object must have a value."; - public const string InvalidOperation_NullArray = "The underlying array is null."; - public const string InvalidOperation_Overlapped_Pack = "Cannot pack a packed Overlapped again."; - public const string InvalidOperation_ReadOnly = "Instance is read-only."; - public const string InvalidOperation_ThreadWrongThreadStart = "The thread was created with a ThreadStart delegate that does not accept a parameter."; - public const string InvalidOperation_UnknownEnumType = "Unknown enum type."; - public const string InvalidOperation_WriteOnce = "This property has already been set and cannot be modified."; - public const string InvalidOperation_ArrayCreateInstance_NotARuntimeType = "Array.CreateInstance() can only accept Type objects created by the runtime."; - public const string InvalidOperation_TooEarly = "Internal Error: This operation cannot be invoked in an eager class constructor."; - public const string InvalidOperation_NullContext = "Cannot call Set on a null context"; - public const string InvalidOperation_CannotUseAFCOtherThread = "AsyncFlowControl object must be used on the thread where it was created."; - public const string InvalidOperation_CannotRestoreUnsupressedFlow = "Cannot restore context flow when it is not suppressed."; - public const string InvalidOperation_CannotSupressFlowMultipleTimes = "Context flow is already suppressed."; - public const string InvalidOperation_CannotUseAFCMultiple = "AsyncFlowControl object can be used only once to call Undo()."; - public const string InvalidOperation_AsyncFlowCtrlCtxMismatch = "AsyncFlowControl objects can be used to restore flow only on a Context that had its flow suppressed."; - public const string InvalidProgram_Default = "Common Language Runtime detected an invalid program."; - public const string InvalidProgram_Specific = "Common Language Runtime detected an invalid program. The body of method '{0}' is invalid."; - public const string InvalidProgram_Vararg = "Method '{0}' has a variable argument list. Variable argument lists are not supported in .NET Core."; - public const string InvalidProgram_CallVirtFinalize = "Object.Finalize() can not be called directly. It is only callable by the runtime."; - public const string InvalidTimeZone_InvalidRegistryData = "The time zone ID '{0}' was found on the local computer, but the registry information was corrupt."; - public const string IO_FileExists_Name = "The file '{0}' already exists."; - public const string IO_FileName_Name = "File name: '{0}'"; - public const string IO_FileNotFound = "Unable to find the specified file."; - public const string IO_FileNotFound_FileName = "Could not load file or assembly '{0}'. The system cannot find the file specified."; - public const string IO_FileLoad = "Could not load the specified file."; - public const string IO_FileLoad_FileName = "Could not load the file '{0}'."; - public const string IO_PathNotFound_NoPathName = "Could not find a part of the path."; - public const string IO_PathNotFound_Path = "Could not find a part of the path '{0}'."; - public const string IO_PathTooLong = "The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters."; - public const string IO_SharingViolation_File = "The process cannot access the file '{0}' because it is being used by another process."; - public const string IO_SharingViolation_NoFileName = "The process cannot access the file because it is being used by another process."; - public const string IO_AlreadyExists_Name = "Cannot create '{0}' because a file or directory with the same name already exists."; - public const string UnauthorizedAccess_IODenied_NoPathName = "Access to the path is denied."; - public const string UnauthorizedAccess_IODenied_Path = "Access to the path '{0}' is denied."; - public const string Lazy_CreateValue_NoParameterlessCtorForT = "The lazily-initialized type does not have a public, parameterless constructor."; - public const string Lazy_ctor_ModeInvalid = "The mode argument specifies an invalid value."; - public const string Lazy_StaticInit_InvalidOperation = "ValueFactory returned null."; - public const string Lazy_ToString_ValueNotCreated = "Value is not created."; - public const string Lazy_Value_RecursiveCallsToValue = "ValueFactory attempted to access the Value property of this instance."; - public const string MissingConstructor_Name = "Constructor on type '{0}' not found."; - public const string MustUseCCRewrite = "An assembly (probably '{1}') must be rewritten using the code contracts binary rewriter (CCRewrite) because it is calling Contract.{0} and the CONTRACTS_FULL symbol is defined. Remove any explicit definitions of the CONTRACTS_FULL symbol from your project and rebuild. CCRewrite can be downloaded from http://go.microsoft.com/fwlink/?LinkID=169180. \\r\\nAfter the rewriter is installed, it can be enabled in Visual Studio from the project's Properties page on the Code Contracts pane. Ensure that 'Perform Runtime Contract Checking' is enabled, which will define CONTRACTS_FULL."; - public const string NotSupported_FixedSizeCollection = "Collection was of a fixed size."; - public const string NotSupported_MaxWaitHandles = "The number of WaitHandles must be less than or equal to 64."; - public const string NotSupported_NoCodepageData = "No data is available for encoding {0}. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method."; - public const string NotSupported_ReadOnlyCollection = "Collection is read-only."; - public const string NotSupported_StringComparison = "The string comparison type passed in is currently not supported."; - public const string NotSupported_VoidArray = "Arrays of System.Void are not supported."; - public const string NotSupported_ByRefLike = "Cannot create boxed ByRef-like values."; - public const string NotSupported_Type = "Type is not supported."; - public const string NotSupported_WaitAllSTAThread = "WaitAll for multiple handles on a STA thread is not supported."; - public const string ObjectDisposed_Generic = "Cannot access a disposed object."; - public const string ObjectDisposed_ObjectName_Name = "Object name: '{0}'."; - public const string Overflow_Byte = "Value was either too large or too small for an unsigned byte."; - public const string Overflow_Char = "Value was either too large or too small for a character."; - public const string Overflow_Decimal = "Value was either too large or too small for a Decimal."; - public const string Overflow_Double = "Value was either too large or too small for a Double."; - public const string Overflow_TimeSpanElementTooLarge = "The TimeSpan could not be parsed because at least one of the numeric components is out of range or contains too many digits."; - public const string Overflow_Duration = "The duration cannot be returned for TimeSpan.MinValue because the absolute value of TimeSpan.MinValue exceeds the value of TimeSpan.MaxValue."; - public const string Overflow_Int16 = "Value was either too large or too small for an Int16."; - public const string Overflow_Int32 = "Value was either too large or too small for an Int32."; - public const string Overflow_Int64 = "Value was either too large or too small for an Int64."; - public const string Overflow_NegateTwosCompNum = "Negating the minimum value of a twos complement number is invalid."; - public const string Overflow_NegativeUnsigned = "The string was being parsed as an unsigned number and could not have a negative sign."; - public const string Overflow_SByte = "Value was either too large or too small for a signed byte."; - public const string Overflow_Single = "Value was either too large or too small for a Single."; - public const string Overflow_TimeSpanTooLong = "TimeSpan overflowed because the duration is too long."; - public const string Overflow_UInt16 = "Value was either too large or too small for a UInt16."; - public const string Overflow_UInt32 = "Value was either too large or too small for a UInt32."; - public const string Overflow_UInt64 = "Value was either too large or too small for a UInt64."; - public const string Rank_MultiDimNotSupported = "Only single dimension arrays are supported here."; - public const string RuntimeWrappedException = "An object that does not derive from System.Exception has been wrapped in a RuntimeWrappedException."; - public const string SpinWait_SpinUntil_ArgumentNull = "The condition argument is null."; - public const string Serialization_CorruptField = "The value of the field '{0}' is invalid. The serialized data is corrupt."; - public const string Serialization_InvalidData = "An error occurred while deserializing the object. The serialized data is corrupt."; - public const string Serialization_InvalidEscapeSequence = "The serialized data contained an invalid escape sequence '\\\\{0}'."; - public const string Serialization_InvalidType = "Only system-provided types can be passed to the GetUninitializedObject method. '{0}' is not a valid instance of a type."; - public const string SpinWait_SpinUntil_TimeoutWrong = "The timeout must represent a value between -1 and Int32.MaxValue, inclusive."; - public const string Threading_AbandonedMutexException = "The wait completed due to an abandoned mutex."; - public const string Threading_SemaphoreFullException = "Adding the specified count to the semaphore would cause it to exceed its maximum count."; - public const string Threading_ThreadInterrupted = "Thread was interrupted from a waiting state."; - public const string Threading_WaitHandleCannotBeOpenedException = "No handle of the given name exists."; - public const string Threading_WaitHandleCannotBeOpenedException_InvalidHandle = "A WaitHandle with system-wide name '{0}' cannot be created. A WaitHandle of a different type might have the same name."; - public const string TimeZoneNotFound_MissingRegistryData = "The time zone ID '{0}' was not found on the local computer."; - public const string TypeInitialization_Default = "Type constructor threw an exception."; - public const string TypeInitialization_Type = "The type initializer for '{0}' threw an exception."; - public const string TypeInitialization_Type_NoTypeAvailable = "A type initializer threw an exception. To determine which type, inspect the InnerException's StackTrace property."; - public const string Verification_Exception = "Operation could destabilize the runtime."; - public const string Arg_EnumFormatUnderlyingTypeAndObjectMustBeSameType = "Enum underlying type and the object must be same type or object. Type passed in was '{0}'; the enum underlying type was '{1}'."; - public const string Format_InvalidEnumFormatSpecification = "Format String can be only 'G', 'g', 'X', 'x', 'F', 'f', 'D' or 'd'."; - public const string Arg_MustBeEnumBaseTypeOrEnum = "The value passed in must be an enum base or an underlying type for an enum, such as an Int32."; - public const string Arg_EnumUnderlyingTypeAndObjectMustBeSameType = "Enum underlying type and the object must be same type or object must be a String. Type passed in was '{0}'; the enum underlying type was '{1}'."; - public const string Arg_MustBeType = "Type must be a type provided by the runtime."; - public const string Arg_MustContainEnumInfo = "Must specify valid information for parsing in the string."; - public const string Arg_EnumValueNotFound = "Requested value '{0}' was not found."; - public const string Argument_StringZeroLength = "String cannot be of zero length."; - public const string Argument_StringFirstCharIsZero = "The first char in the string is the null character."; - public const string Argument_LongEnvVarValue = "Environment variable name or value is too long."; - public const string Argument_IllegalEnvVarName = "Environment variable name cannot contain equal character."; - public const string AssumptionFailed = "Assumption failed."; - public const string AssumptionFailed_Cnd = "Assumption failed: {0}"; - public const string AssertionFailed = "Assertion failed."; - public const string AssertionFailed_Cnd = "Assertion failed: {0}"; - public const string PreconditionFailed = "Precondition failed."; - public const string PreconditionFailed_Cnd = "Precondition failed: {0}"; - public const string PostconditionFailed = "Postcondition failed."; - public const string PostconditionFailed_Cnd = "Postcondition failed: {0}"; - public const string PostconditionOnExceptionFailed = "Postcondition failed after throwing an exception."; - public const string PostconditionOnExceptionFailed_Cnd = "Postcondition failed after throwing an exception: {0}"; - public const string InvariantFailed = "Invariant failed."; - public const string InvariantFailed_Cnd = "Invariant failed: {0}"; - public const string MissingEncodingNameResource = "Could not find a resource entry for the encoding codepage '{0} - {1}'"; - public const string Globalization_cp_1200 = "Unicode"; - public const string Globalization_cp_1201 = "Unicode (Big-Endian)"; - public const string Globalization_cp_12000 = "Unicode (UTF-32)"; - public const string Globalization_cp_12001 = "Unicode (UTF-32 Big-Endian)"; - public const string Globalization_cp_20127 = "US-ASCII"; - public const string Globalization_cp_28591 = "Western European (ISO)"; - public const string Globalization_cp_65000 = "Unicode (UTF-7)"; - public const string Globalization_cp_65001 = "Unicode (UTF-8)"; - public const string DebugAssertBanner = "---- DEBUG ASSERTION FAILED ----"; - public const string DebugAssertLongMessage = "---- Assert Long Message ----"; - public const string DebugAssertShortMessage = "---- Assert Short Message ----"; - public const string InvalidCast_Empty = "Object cannot be cast to Empty."; - public const string Arg_UnknownTypeCode = "Unknown TypeCode value."; - public const string Format_BadDatePattern = "Could not determine the order of year, month, and date from '{0}'."; - public const string Format_BadDateTime = "String was not recognized as a valid DateTime."; - public const string Format_BadDateTimeCalendar = "The DateTime represented by the string is not supported in calendar {0}."; - public const string Format_BadDayOfWeek = "String was not recognized as a valid DateTime because the day of week was incorrect."; - public const string Format_DateOutOfRange = "The DateTime represented by the string is out of range."; - public const string Format_MissingIncompleteDate = "There must be at least a partial date with a year present in the input."; - public const string Format_OffsetOutOfRange = "The time zone offset must be within plus or minus 14 hours."; - public const string Format_RepeatDateTimePattern = "DateTime pattern '{0}' appears more than once with different values."; - public const string Format_UnknowDateTimeWord = "The string was not recognized as a valid DateTime. There is an unknown word starting at index {0}."; - public const string Format_UTCOutOfRange = "The UTC representation of the date falls outside the year range 1-9999."; - public const string RFLCT_Ambiguous = "Ambiguous match found."; - public const string AggregateException_ctor_DefaultMessage = "One or more errors occurred."; - public const string AggregateException_ctor_InnerExceptionNull = "An element of innerExceptions was null."; - public const string AggregateException_DeserializationFailure = "The serialization stream contains no inner exceptions."; - public const string AggregateException_InnerException = "(Inner Exception #{0}) "; // {entry.Item3} - public const string ArgumentOutOfRange_TimeoutTooLarge = "Time-out interval must be less than 2^32-2."; - public const string ArgumentOutOfRange_PeriodTooLarge = "Period must be less than 2^32-2."; - public const string TaskScheduler_FromCurrentSynchronizationContext_NoCurrent = "The current SynchronizationContext may not be used as a TaskScheduler."; - public const string TaskScheduler_ExecuteTask_WrongTaskScheduler = "ExecuteTask may not be called for a task which was previously queued to a different TaskScheduler."; - public const string TaskScheduler_InconsistentStateAfterTryExecuteTaskInline = "The TryExecuteTaskInline call to the underlying scheduler succeeded, but the task body was not invoked."; - public const string TaskSchedulerException_ctor_DefaultMessage = "An exception was thrown by a TaskScheduler."; - public const string Task_MultiTaskContinuation_FireOptions = "It is invalid to exclude specific continuation kinds for continuations off of multiple tasks."; - public const string Task_ContinueWith_ESandLR = "The specified TaskContinuationOptions combined LongRunning and ExecuteSynchronously. Synchronous continuations should not be long running."; - public const string Task_MultiTaskContinuation_EmptyTaskList = "The tasks argument contains no tasks."; - public const string Task_MultiTaskContinuation_NullTask = "The tasks argument included a null value."; - public const string Task_FromAsync_PreferFairness = "It is invalid to specify TaskCreationOptions.PreferFairness in calls to FromAsync."; - public const string Task_FromAsync_LongRunning = "It is invalid to specify TaskCreationOptions.LongRunning in calls to FromAsync."; - public const string AsyncMethodBuilder_InstanceNotInitialized = "The builder was not properly initialized."; - public const string TaskT_TransitionToFinal_AlreadyCompleted = "An attempt was made to transition a task to a final state when it had already completed."; - public const string TaskT_DebuggerNoResult = "{Not yet computed}"; - public const string OperationCanceled = "The operation was canceled."; - public const string CancellationToken_CreateLinkedToken_TokensIsEmpty = "No tokens were supplied."; - public const string CancellationTokenSource_Disposed = "The CancellationTokenSource has been disposed."; - public const string CancellationToken_SourceDisposed = "The CancellationTokenSource associated with this CancellationToken has been disposed."; - public const string TaskExceptionHolder_UnknownExceptionType = "(Internal)Expected an Exception or an IEnumerable"; - public const string TaskExceptionHolder_UnhandledException = "A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread."; - public const string Task_Delay_InvalidMillisecondsDelay = "The value needs to be either -1 (signifying an infinite timeout), 0 or a positive integer."; - public const string Task_Delay_InvalidDelay = "The value needs to translate in milliseconds to -1 (signifying an infinite timeout), 0 or a positive integer less than or equal to Int32.MaxValue."; - public const string Task_Dispose_NotCompleted = "A task may only be disposed if it is in a completion state (RanToCompletion, Faulted or Canceled)."; - public const string Task_WaitMulti_NullTask = "The tasks array included at least one null element."; - public const string Task_ContinueWith_NotOnAnything = "The specified TaskContinuationOptions excluded all continuation kinds."; - public const string Task_RunSynchronously_AlreadyStarted = "RunSynchronously may not be called on a task that was already started."; - public const string Task_ThrowIfDisposed = "The task has been disposed."; - public const string Task_RunSynchronously_TaskCompleted = "RunSynchronously may not be called on a task that has already completed."; - public const string Task_RunSynchronously_Promise = "RunSynchronously may not be called on a task not bound to a delegate, such as the task returned from an asynchronous method."; - public const string Task_RunSynchronously_Continuation = "RunSynchronously may not be called on a continuation task."; - public const string Task_Start_AlreadyStarted = "Start may not be called on a task that was already started."; - public const string Task_Start_ContinuationTask = "Start may not be called on a continuation task."; - public const string Task_Start_Promise = "Start may not be called on a promise-style task."; - public const string Task_Start_TaskCompleted = "Start may not be called on a task that has completed."; - public const string TaskCanceledException_ctor_DefaultMessage = "A task was canceled."; - public const string TaskCompletionSourceT_TrySetException_NoExceptions = "The exceptions collection was empty."; - public const string TaskCompletionSourceT_TrySetException_NullException = "The exceptions collection included at least one null element."; - public const string Argument_MinMaxValue = "'{0}' cannot be greater than {1}."; - public const string ExecutionContext_ExceptionInAsyncLocalNotification = "An exception was not handled in an AsyncLocal notification callback."; - public const string InvalidOperation_WrongAsyncResultOrEndCalledMultiple = "Either the IAsyncResult object did not come from the corresponding async method on this type, or the End method was called multiple times with the same IAsyncResult."; - public const string SpinLock_IsHeldByCurrentThread = "Thread tracking is disabled."; - public const string SpinLock_TryEnter_LockRecursionException = "The calling thread already holds the lock."; - public const string SpinLock_Exit_SynchronizationLockException = "The calling thread does not hold the lock."; - public const string SpinLock_TryReliableEnter_ArgumentException = "The tookLock argument must be set to false before calling this method."; - public const string SpinLock_TryEnter_ArgumentOutOfRange = "The timeout must be a value between -1 and Int32.MaxValue, inclusive."; - public const string ManualResetEventSlim_Disposed = "The event has been disposed."; - public const string ManualResetEventSlim_ctor_SpinCountOutOfRange = "The spinCount argument must be in the range 0 to {0}, inclusive."; - public const string ManualResetEventSlim_ctor_TooManyWaiters = "There are too many threads currently waiting on the event. A maximum of {0} waiting threads are supported."; - public const string InvalidOperation_SendNotSupportedOnWindowsRTSynchronizationContext = "Send is not supported in the Windows Runtime SynchronizationContext"; - public const string InvalidOperation_SetData_OnlyOnce = "SetData can only be used to set the value of a given name once."; - public const string SemaphoreSlim_Disposed = "The semaphore has been disposed."; - public const string SemaphoreSlim_Release_CountWrong = "The releaseCount argument must be greater than zero."; - public const string SemaphoreSlim_Wait_TimeoutWrong = "The timeout must represent a value between -1 and Int32.MaxValue, inclusive."; - public const string SemaphoreSlim_ctor_MaxCountWrong = "The maximumCount argument must be a positive number. If a maximum is not required, use the constructor without a maxCount parameter."; - public const string SemaphoreSlim_ctor_InitialCountWrong = "The initialCount argument must be non-negative and less than or equal to the maximumCount."; - public const string ThreadLocal_ValuesNotAvailable = "The ThreadLocal object is not tracking values. To use the Values property, use a ThreadLocal constructor that accepts the trackAllValues parameter and set the parameter to true."; - public const string ThreadLocal_Value_RecursiveCallsToValue = "ValueFactory attempted to access the Value property of this instance."; - public const string ThreadLocal_Disposed = "The ThreadLocal object has been disposed."; - public const string LockRecursionException_WriteAfterReadNotAllowed = "Write lock may not be acquired with read lock held. This pattern is prone to deadlocks. Please ensure that read locks are released before taking a write lock. If an upgrade is necessary, use an upgrade lock in place of the read lock."; - public const string LockRecursionException_RecursiveWriteNotAllowed = "Recursive write lock acquisitions not allowed in this mode."; - public const string LockRecursionException_ReadAfterWriteNotAllowed = "A read lock may not be acquired with the write lock held in this mode."; - public const string LockRecursionException_RecursiveUpgradeNotAllowed = "Recursive upgradeable lock acquisitions not allowed in this mode."; - public const string LockRecursionException_RecursiveReadNotAllowed = "Recursive read lock acquisitions not allowed in this mode."; - public const string SynchronizationLockException_IncorrectDispose = "The lock is being disposed while still being used. It either is being held by a thread and/or has active waiters waiting to acquire the lock."; - public const string SynchronizationLockException_MisMatchedWrite = "The write lock is being released without being held."; - public const string LockRecursionException_UpgradeAfterReadNotAllowed = "Upgradeable lock may not be acquired with read lock held."; - public const string LockRecursionException_UpgradeAfterWriteNotAllowed = "Upgradeable lock may not be acquired with write lock held in this mode. Acquiring Upgradeable lock gives the ability to read along with an option to upgrade to a writer."; - public const string SynchronizationLockException_MisMatchedUpgrade = "The upgradeable lock is being released without being held."; - public const string SynchronizationLockException_MisMatchedRead = "The read lock is being released without being held."; - public const string InvalidOperation_TimeoutsNotSupported = "Timeouts are not supported on this stream."; - public const string NotSupported_UnreadableStream = "Stream does not support reading."; - public const string NotSupported_UnwritableStream = "Stream does not support writing."; - public const string ObjectDisposed_StreamClosed = "Cannot access a closed Stream."; - public const string NotSupported_SubclassOverride = "Derived classes must provide an implementation."; - public const string InvalidOperation_NoPublicRemoveMethod = "Cannot remove the event handler since no public remove method exists for the event."; - public const string InvalidOperation_NoPublicAddMethod = "Cannot add the event handler since no public add method exists for the event."; - public const string SerializationException = "Serialization error."; - public const string Serialization_NotFound = "Member '{0}' was not found."; - public const string Serialization_OptionalFieldVersionValue = "Version value must be positive."; - public const string Serialization_SameNameTwice = "Cannot add the same member twice to a SerializationInfo object."; - public const string NotSupported_AbstractNonCLS = "This non-CLS method is not implemented."; - public const string NotSupported_NoTypeInfo = "Cannot resolve {0} to a TypeInfo object."; - public const string Arg_CustomAttributeFormatException = "Binary format of the specified custom attribute was invalid."; - public const string Argument_InvalidMemberForNamedArgument = "The member must be either a field or a property."; - public const string Arg_InvalidFilterCriteriaException = "Specified filter criteria was invalid."; - public const string Arg_ParmArraySize = "Must specify one or more parameters."; - public const string Arg_MustBePointer = "Type must be a Pointer."; - public const string Arg_InvalidHandle = "Invalid handle."; - public const string Argument_InvalidEnum = "The Enum type should contain one and only one instance field."; - public const string Argument_MustHaveAttributeBaseClass = "Type passed in must be derived from System.Attribute or System.Attribute itself."; - public const string InvalidFilterCriteriaException_CritString = "A String must be provided for the filter criteria."; - public const string InvalidFilterCriteriaException_CritInt = "An Int32 must be provided for the filter criteria."; - public const string InvalidOperation_NotSupportedOnWinRTEvent = "Adding or removing event handlers dynamically is not supported on WinRT events."; - public const string PlatformNotSupported_ReflectionOnly = "ReflectionOnly loading is not supported on this platform."; - public const string PlatformNotSupported_OSXFileLocking = "Locking/unlocking file regions is not supported on this platform. Use FileShare on the entire file instead."; - public const string MissingMember_Name = "Member '{0}' not found."; - public const string MissingMethod_Name = "Method '{0}' not found."; - public const string MissingField_Name = "Field '{0}' not found."; - public const string Format_StringZeroLength = "String cannot have zero length."; - public const string Security_CannotReadRegistryData = "The time zone ID '{0}' was found on the local computer, but the application does not have permission to read the registry information."; - public const string Security_InvalidAssemblyPublicKey = "Invalid assembly public key."; - public const string Security_RegistryPermission = "Requested registry access is not allowed."; - public const string ClassLoad_General = "Could not load type '{0}' from assembly '{1}'."; - public const string ClassLoad_RankTooLarge = "'{0}' from assembly '{1}' has too many dimensions."; - public const string ClassLoad_ExplicitGeneric = "Could not load type '{0}' from assembly '{1}' because generic types cannot have explicit layout."; - public const string ClassLoad_BadFormat = "Could not load type '{0}' from assembly '{1}' because the format is invalid."; - public const string ClassLoad_ValueClassTooLarge = "Array of type '{0}' from assembly '{1}' cannot be created because base value type is too large."; - public const string ClassLoad_ExplicitLayout = "Could not load type '{0}' from assembly '{1}' because it contains an object field at offset '{2}' that is incorrectly aligned or overlapped by a non-object field."; - public const string EE_MissingMethod = "Method not found: '{0}'."; - public const string EE_MissingField = "Field not found: '{0}'."; - public const string UnauthorizedAccess_RegistryKeyGeneric_Key = "Access to the registry key '{0}' is denied."; - public const string UnknownError_Num = "Unknown error '{0}'."; - public const string Argument_NeedStructWithNoRefs = "The specified Type must be a struct containing no references."; - public const string ArgumentNull_Buffer = "Buffer cannot be null."; - public const string ArgumentOutOfRange_AddressSpace = "The number of bytes cannot exceed the virtual address space on a 32 bit machine."; - public const string ArgumentOutOfRange_UIntPtrMaxMinusOne = "The length of the buffer must be less than the maximum UIntPtr value for your platform."; - public const string Arg_BufferTooSmall = "Not enough space available in the buffer."; - public const string InvalidOperation_MustCallInitialize = "You must call Initialize on this object instance before using it."; - public const string ArgumentException_BufferNotFromPool = "The buffer is not associated with this pool and may not be returned to it."; - public const string Argument_InvalidSafeBufferOffLen = "Offset and length were greater than the size of the SafeBuffer."; - public const string Argument_InvalidSeekOrigin = "Invalid seek origin."; - public const string Argument_NotEnoughBytesToRead = "There are not enough bytes remaining in the accessor to read at this position."; - public const string Argument_NotEnoughBytesToWrite = "There are not enough bytes remaining in the accessor to write at this position."; - public const string Argument_OffsetAndCapacityOutOfBounds = "Offset and capacity were greater than the size of the view."; - public const string ArgumentOutOfRange_UnmanagedMemStreamLength = "UnmanagedMemoryStream length must be non-negative and less than 2^63 - 1 - baseAddress."; - public const string Argument_UnmanagedMemAccessorWrapAround = "The UnmanagedMemoryAccessor capacity and offset would wrap around the high end of the address space."; - public const string ArgumentOutOfRange_StreamLength = "Stream length must be non-negative and less than 2^31 - 1 - origin."; - public const string ArgumentOutOfRange_UnmanagedMemStreamWrapAround = "The UnmanagedMemoryStream capacity would wrap around the high end of the address space."; - public const string InvalidOperation_CalledTwice = "The method cannot be called twice on the same instance."; - public const string IO_FixedCapacity = "Unable to expand length of this stream beyond its capacity."; - public const string IO_SeekBeforeBegin = "An attempt was made to move the position before the beginning of the stream."; - public const string IO_StreamTooLong = "Stream was too long."; - public const string Arg_BadDecimal = "Read an invalid decimal value from the buffer."; - public const string NotSupported_Reading = "Accessor does not support reading."; - public const string NotSupported_UmsSafeBuffer = "This operation is not supported for an UnmanagedMemoryStream created from a SafeBuffer."; - public const string NotSupported_Writing = "Accessor does not support writing."; - public const string NotSupported_UnseekableStream = "Stream does not support seeking."; - public const string IndexOutOfRange_UMSPosition = "Unmanaged memory stream position was beyond the capacity of the stream."; - public const string ObjectDisposed_StreamIsClosed = "Cannot access a closed Stream."; - public const string ObjectDisposed_ViewAccessorClosed = "Cannot access a closed accessor."; - public const string ArgumentOutOfRange_PositionLessThanCapacityRequired = "The position may not be greater or equal to the capacity of the accessor."; - public const string IO_EOF_ReadBeyondEOF = "Unable to read beyond the end of the stream."; - public const string Arg_EndOfStreamException = "Attempted to read past the end of the stream."; - public const string ObjectDisposed_FileClosed = "Cannot access a closed file."; - public const string Arg_InvalidSearchPattern = "Search pattern cannot contain \\\"..\\\" to move up directories and can be contained only internally in file/directory names, as in \\\"a..b\\\"."; - public const string ArgumentOutOfRange_FileLengthTooBig = "Specified file length was too large for the file system."; - public const string Argument_InvalidHandle = "'handle' has been disposed or is an invalid handle."; - public const string Argument_AlreadyBoundOrSyncHandle = "'handle' has already been bound to the thread pool, or was not opened for asynchronous I/O."; - public const string Argument_PreAllocatedAlreadyAllocated = "'preAllocated' is already in use."; - public const string Argument_NativeOverlappedAlreadyFree = "'overlapped' has already been freed."; - public const string Argument_NativeOverlappedWrongBoundHandle = "'overlapped' was not allocated by this ThreadPoolBoundHandle instance."; - public const string Arg_HandleNotAsync = "Handle does not support asynchronous operations. The parameters to the FileStream constructor may need to be changed to indicate that the handle was opened synchronously (that is, it was not opened for overlapped I/O)."; - public const string ArgumentNull_Path = "Path cannot be null."; - public const string Argument_EmptyPath = "Empty path name is not legal."; - public const string Argument_InvalidFileModeAndAccessCombo = "Combining FileMode: {0} with FileAccess: {1} is invalid."; - public const string Argument_InvalidAppendMode = "Append access can be requested only in write-only mode."; - public const string IO_UnknownFileName = "[Unknown]"; - public const string IO_FileStreamHandlePosition = "The OS handle's position is not what FileStream expected. Do not use a handle simultaneously in one FileStream and in Win32 code or another FileStream. This may cause data loss."; - public const string NotSupported_FileStreamOnNonFiles = "FileStream was asked to open a device that was not a file. For support for devices like 'com1:' or 'lpt1:', call CreateFile, then use the FileStream constructors that take an OS handle as an IntPtr."; - public const string IO_BindHandleFailed = "BindHandle for ThreadPool failed on this handle."; - public const string Arg_HandleNotSync = "Handle does not support synchronous operations. The parameters to the FileStream constructor may need to be changed to indicate that the handle was opened asynchronously (that is, it was opened explicitly for overlapped I/O)."; - public const string IO_SetLengthAppendTruncate = "Unable to truncate data that previously existed in a file opened in Append mode."; - public const string IO_SeekAppendOverwrite = "Unable seek backward to overwrite data that previously existed in a file opened in Append mode."; - public const string IO_FileTooLongOrHandleNotSync = "IO operation will not work. Most likely the file will become too long or the handle was not opened to support synchronous IO operations."; - public const string IndexOutOfRange_IORaceCondition = "Probable I/O race condition detected while copying memory. The I/O package is not thread safe by default. In multithreaded applications, a stream must be accessed in a thread-safe way, such as a thread-safe wrapper returned by TextReader's or TextWriter's Synchronized methods. This also applies to classes like StreamWriter and StreamReader."; - public const string Arg_ResourceFileUnsupportedVersion = "The ResourceReader class does not know how to read this version of .resources files."; - public const string Resources_StreamNotValid = "Stream is not a valid resource file."; - public const string BadImageFormat_ResourcesHeaderCorrupted = "Corrupt .resources file. Unable to read resources from this file because of invalid header information. Try regenerating the .resources file."; - public const string Argument_StreamNotReadable = "Stream was not readable."; - public const string BadImageFormat_NegativeStringLength = "Corrupt .resources file. String length must be non-negative."; - public const string BadImageFormat_ResourcesNameInvalidOffset = "Corrupt .resources file. The Invalid offset into name section is ."; - public const string BadImageFormat_TypeMismatch = "Corrupt .resources file. The specified type doesn't match the available data in the stream."; - public const string BadImageFormat_ResourceNameCorrupted_NameIndex = "Corrupt .resources file. The resource name for name index that extends past the end of the stream is "; - public const string BadImageFormat_ResourcesDataInvalidOffset = "Corrupt .resources file. Invalid offset into data section is "; - public const string Format_Bad7BitInt32 = "Too many bytes in what should have been a 7 bit encoded Int32."; - public const string BadImageFormat_InvalidType = "Corrupt .resources file. The specified type doesn't exist."; - public const string ResourceReaderIsClosed = "ResourceReader is closed."; - public const string Arg_MissingManifestResourceException = "Unable to find manifest resource."; - public const string Serialization_MissingKeys = "The keys for this dictionary are missing."; - public const string Serialization_NullKey = "One of the serialized keys is null."; - public const string NotSupported_KeyCollectionSet = "Mutating a key collection derived from a dictionary is not allowed."; - public const string NotSupported_ValueCollectionSet = "Mutating a value collection derived from a dictionary is not allowed."; - public const string IO_IO_StreamTooLong = "Stream was too long."; - public const string UnauthorizedAccess_MemStreamBuffer = "MemoryStream's internal buffer cannot be accessed."; - public const string NotSupported_MemStreamNotExpandable = "Memory stream is not expandable."; - public const string IO_IO_SeekBeforeBegin = "An attempt was made to move the position before the beginning of the stream."; - public const string ArgumentNull_Stream = "Stream cannot be null."; - public const string IO_IO_InvalidStringLen_Len = "BinaryReader encountered an invalid string length of {0} characters."; - public const string ArgumentOutOfRange_BinaryReaderFillBuffer = "The number of bytes requested does not fit into BinaryReader's internal buffer."; - public const string Serialization_InsufficientDeserializationState = "Insufficient state to deserialize the object. Missing field '{0}'."; - public const string NotSupported_UnitySerHolder = "The UnitySerializationHolder object is designed to transmit information about other types and is not serializable itself."; - public const string Serialization_UnableToFindModule = "The given module {0} cannot be found within the assembly {1}."; - public const string Argument_InvalidUnity = "Invalid Unity type."; - public const string InvalidOperation_InvalidHandle = "The handle is invalid."; - public const string PlatformNotSupported_NamedSynchronizationPrimitives = "The named version of this synchronization primitive is not supported on this platform."; - public const string InvalidOperation_EmptyQueue = "Queue empty."; - public const string Overflow_MutexReacquireCount = "The current thread attempted to reacquire a mutex that has reached its maximum acquire count."; - public const string Serialization_InsufficientState = "Insufficient state to return the real object."; - public const string Serialization_UnknownMember = "Cannot get the member '{0}'."; - public const string Serialization_NullSignature = "The method signature cannot be null."; - public const string Serialization_MemberTypeNotRecognized = "Unknown member type."; - public const string Serialization_BadParameterInfo = "Non existent ParameterInfo. Position bigger than member's parameters length."; - public const string Serialization_NoParameterInfo = "Serialized member does not have a ParameterInfo."; - public const string ArgumentNull_Assembly = "Assembly cannot be null."; - public const string Arg_InvalidNeutralResourcesLanguage_Asm_Culture = "The NeutralResourcesLanguageAttribute on the assembly \"{0}\" specifies an invalid culture name: \"{1}\"."; - public const string Arg_InvalidNeutralResourcesLanguage_FallbackLoc = "The NeutralResourcesLanguageAttribute specifies an invalid or unrecognized ultimate resource fallback location: \"{0}\"."; - public const string Arg_InvalidSatelliteContract_Asm_Ver = "Satellite contract version attribute on the assembly '{0}' specifies an invalid version: {1}."; - public const string Arg_ResMgrNotResSet = "Type parameter must refer to a subclass of ResourceSet."; - public const string BadImageFormat_ResourceNameCorrupted = "Corrupt .resources file. A resource name extends past the end of the stream."; - public const string BadImageFormat_ResourcesNameTooLong = "Corrupt .resources file. Resource name extends past the end of the file."; - public const string InvalidOperation_ResMgrBadResSet_Type = "'{0}': ResourceSet derived classes must provide a constructor that takes a String file name and a constructor that takes a Stream."; - public const string InvalidOperation_ResourceNotStream_Name = "Resource '{0}' was not a Stream - call GetObject instead."; - public const string MissingManifestResource_MultipleBlobs = "A case-insensitive lookup for resource file \"{0}\" in assembly \"{1}\" found multiple entries. Remove the duplicates or specify the exact case."; - public const string MissingManifestResource_NoNeutralAsm = "Could not find any resources appropriate for the specified culture or the neutral culture. Make sure \"{0}\" was correctly embedded or linked into assembly \"{1}\" at compile time, or that all the satellite assemblies required are loadable and fully signed."; - public const string MissingManifestResource_NoNeutralDisk = "Could not find any resources appropriate for the specified culture (or the neutral culture) on disk."; - public const string MissingManifestResource_NoPRIresources = "Unable to open Package Resource Index."; - public const string MissingManifestResource_ResWFileNotLoaded = "Unable to load resources for resource file \"{0}\" in package \"{1}\"."; - public const string MissingSatelliteAssembly_Culture_Name = "The satellite assembly named \"{1}\" for fallback culture \"{0}\" either could not be found or could not be loaded. This is generally a setup problem. Please consider reinstalling or repairing the application."; - public const string MissingSatelliteAssembly_Default = "Resource lookup fell back to the ultimate fallback resources in a satellite assembly, but that satellite either was not found or could not be loaded. Please consider reinstalling or repairing the application."; - public const string NotSupported_ObsoleteResourcesFile = "Found an obsolete .resources file in assembly '{0}'. Rebuild that .resources file then rebuild that assembly."; - public const string NotSupported_ResourceObjectSerialization = "Cannot read resources that depend on serialization."; - public const string ObjectDisposed_ResourceSet = "Cannot access a closed resource set."; - public const string Arg_ResourceNameNotExist = "The specified resource name \"{0}\" does not exist in the resource file."; - public const string BadImageFormat_ResourceDataLengthInvalid = "Corrupt .resources file. The specified data length '{0}' is not a valid position in the stream."; - public const string BadImageFormat_ResourcesIndexTooLong = "Corrupt .resources file. String for name index '{0}' extends past the end of the file."; - public const string InvalidOperation_ResourceNotString_Name = "Resource '{0}' was not a String - call GetObject instead."; - public const string InvalidOperation_ResourceNotString_Type = "Resource was of type '{0}' instead of String - call GetObject instead."; - public const string NotSupported_WrongResourceReader_Type = "This .resources file should not be read with this reader. The resource reader type is \"{0}\"."; - public const string Arg_MustBeDelegate = "Type must derive from Delegate."; - public const string NotSupported_GlobalMethodSerialization = "Serialization of global methods (including implicit serialization via the use of asynchronous delegates) is not supported."; - public const string NotSupported_DelegateSerHolderSerial = "DelegateSerializationHolder objects are designed to represent a delegate during serialization and are not serializable themselves."; - public const string DelegateSer_InsufficientMetadata = "The delegate cannot be serialized properly due to missing metadata for the target method."; - public const string Argument_NoUninitializedStrings = "Uninitialized Strings cannot be created."; - public const string ArgumentOutOfRangeException_NoGCRegionSizeTooLarge = "totalSize is too large. For more information about setting the maximum size, see \\\"Latency Modes\\\" in http://go.microsoft.com/fwlink/?LinkId=522706."; - public const string InvalidOperationException_AlreadyInNoGCRegion = "The NoGCRegion mode was already in progress."; - public const string InvalidOperationException_NoGCRegionAllocationExceeded = "Allocated memory exceeds specified memory for NoGCRegion mode."; - public const string InvalidOperationException_NoGCRegionInduced = "Garbage collection was induced in NoGCRegion mode."; - public const string InvalidOperationException_NoGCRegionNotInProgress = "NoGCRegion mode must be set."; - public const string InvalidOperationException_SetLatencyModeNoGC = "The NoGCRegion mode is in progress. End it and then set a different mode."; - public const string InvalidOperation_NotWithConcurrentGC = "This API is not available when the concurrent GC is enabled."; - public const string ThreadState_AlreadyStarted = "Thread is running or terminated; it cannot restart."; - public const string ThreadState_Dead_Priority = "Thread is dead; priority cannot be accessed."; - public const string ThreadState_Dead_State = "Thread is dead; state cannot be accessed."; - public const string ThreadState_NotStarted = "Thread has not been started."; - public const string ThreadState_SetPriorityFailed = "Unable to set thread priority."; - public const string Serialization_InvalidFieldState = "Object fields may not be properly initialized."; - public const string Acc_CreateAbst = "Cannot create an abstract class."; - public const string Acc_CreateGeneric = "Cannot create a type for which Type.ContainsGenericParameters is true."; - public const string Argument_InvalidValue = "Value was invalid."; - public const string NotSupported_ManagedActivation = "Cannot create uninitialized instances of types requiring managed activation."; - public const string PlatformNotSupported_ResourceManager_ResWFileUnsupportedMethod = "ResourceManager method '{0}' is not supported when reading from .resw resource files."; - public const string PlatformNotSupported_ResourceManager_ResWFileUnsupportedProperty = "ResourceManager property '{0}' is not supported when reading from .resw resource files."; - public const string Serialization_NonSerType = "Type '{0}' in Assembly '{1}' is not marked as serializable."; - public const string InvalidCast_DBNull = "Object cannot be cast to DBNull."; - public const string NotSupported_NYI = "This feature is not currently implemented."; - public const string Delegate_GarbageCollected = "The corresponding delegate has been garbage collected. Please make sure the delegate is still referenced by managed code when you are using the marshalled native function pointer."; - public const string Arg_AmbiguousMatchException = "Ambiguous match found."; - public const string NotSupported_ChangeType = "ChangeType operation is not supported."; - public const string Arg_EmptyArray = "Array may not be empty."; - public const string MissingMember = "Member not found."; - public const string MissingField = "Field not found."; - public const string InvalidCast_FromDBNull = "Object cannot be cast from DBNull to other types."; - public const string NotSupported_DBNullSerial = "Only one DBNull instance may exist, and calls to DBNull deserialization methods are not allowed."; - public const string Serialization_StringBuilderCapacity = "The serialized Capacity property of StringBuilder must be positive, less than or equal to MaxCapacity and greater than or equal to the String length."; - public const string Serialization_StringBuilderMaxCapacity = "The serialized MaxCapacity property of StringBuilder must be positive and greater than or equal to the String length."; - public const string PlatformNotSupported_Remoting = "Remoting is not supported on this platform."; - public const string PlatformNotSupported_StrongNameSigning = "Strong-name signing is not supported on this platform."; - public const string Serialization_MissingDateTimeData = "Invalid serialized DateTime data. Unable to find 'ticks' or 'dateData'."; - public const string Serialization_DateTimeTicksOutOfRange = "Invalid serialized DateTime data. Ticks must be between DateTime.MinValue.Ticks and DateTime.MaxValue.Ticks."; - public const string Arg_InvalidANSIString = "The ANSI string passed in could not be converted from the default ANSI code page to Unicode."; - public const string Arg_ExpectedNulTermination = "The value passed was not NUL terminated."; - public const string PlatformNotSupported_ArgIterator = "ArgIterator is not supported on this platform."; - public const string Arg_TypeUnloadedException = "Type had been unloaded."; - public const string Overflow_Currency = "Value was either too large or too small for a Currency."; - public const string PlatformNotSupported_SecureBinarySerialization = "Secure binary serialization is not supported on this platform."; - public const string Serialization_InvalidPtrValue = "An IntPtr or UIntPtr with an eight byte value cannot be deserialized on a machine with a four byte word size."; - public const string EventSource_ListenerNotFound = "Listener not found."; - public const string EventSource_ToString = "EventSource({0}, {1})"; - public const string EventSource_ImplementGetMetadata = "Please implement the GetMetadata method in your derived class"; - public const string EventSource_NeedGuid = "The Guid of an EventSource must be non zero."; - public const string EventSource_NeedName = "The name of an EventSource must not be null."; - public const string EventSource_NeedDescriptors = "The descriptor of an EventSource must be non-null."; - public const string EventSource_NeedManifest = "The manifest of an EventSource must be non-null."; - public const string EventSource_EventSourceGuidInUse = "An instance of EventSource with Guid {0} already exists."; - public const string EventSource_ListenerWriteFailure = "An error occurred when writing to a listener."; - public const string EventSource_NoManifest = "A manifest could not be generated for this EventSource because it contains one or more ill-formed event methods."; - public const string Argument_StreamNotWritable = "Stream was not writable."; - public const string Arg_SurrogatesNotAllowedAsSingleChar = "Unicode surrogate characters must be written out as pairs together in the same call, not individually. Consider passing in a character array instead."; - public const string CustomAttributeFormat_InvalidFieldFail = "'{0}' field specified was not found."; - public const string CustomAttributeFormat_InvalidPropertyFail = "'{0}' property specified was not found."; - public const string NotSupported_CannotCallEqualsOnSpan = "Equals() on Span and ReadOnlySpan is not supported. Use operator== instead."; - public const string NotSupported_CannotCallGetHashCodeOnSpan = "GetHashCode() on Span and ReadOnlySpan is not supported."; - public const string Argument_DestinationTooShort = "Destination is too short."; - public const string Argument_InvalidTypeWithPointersNotSupported = "Cannot use type '{0}'. Only value types without pointers or references are supported."; - public const string ArrayTypeMismatch_ConstrainedCopy = "Array.ConstrainedCopy will only work on array types that are provably compatible, without any form of boxing, unboxing, widening, or casting of each array element. Change the array types (i.e., copy a Derived[] to a Base[]), or use a mitigation strategy in the CER for Array.Copy's less powerful reliability contract, such as cloning the array or throwing away the potentially corrupt destination array."; - public const string Arg_DllNotFoundException = "Dll was not found."; - public const string Arg_DllNotFoundExceptionParameterized = "Unable to load DLL '{0}': The specified module could not be found."; - public const string WrongSizeArrayInNStruct = "Type could not be marshaled because the length of an embedded array instance does not match the declared length in the layout."; - public const string Arg_InteropMarshalUnmappableChar = "Cannot marshal: Encountered unmappable character."; - public const string Arg_MarshalDirectiveException = "Marshaling directives are invalid."; - public const string BlockingCollection_Add_ConcurrentCompleteAdd = "CompleteAdding may not be used concurrently with additions to the collection."; - public const string BlockingCollection_Add_Failed = "The underlying collection didn't accept the item."; - public const string BlockingCollection_CantAddAnyWhenCompleted = "At least one of the specified collections is marked as complete with regards to additions."; - public const string BlockingCollection_CantTakeAnyWhenAllDone = "All collections are marked as complete with regards to additions."; - public const string BlockingCollection_CantTakeWhenDone = "The collection argument is empty and has been marked as complete with regards to additions."; - public const string BlockingCollection_Completed = "The collection has been marked as complete with regards to additions."; - public const string BlockingCollection_CopyTo_IncorrectType = "The array argument is of the incorrect type."; - public const string BlockingCollection_CopyTo_MultiDim = "The array argument is multidimensional."; - public const string BlockingCollection_CopyTo_NonNegative = "The index argument must be greater than or equal zero."; - public const string Collection_CopyTo_TooManyElems = "The number of elements in the collection is greater than the available space from index to the end of the destination array."; - public const string BlockingCollection_ctor_BoundedCapacityRange = "The boundedCapacity argument must be positive."; - public const string BlockingCollection_ctor_CountMoreThanCapacity = "The collection argument contains more items than are allowed by the boundedCapacity."; - public const string BlockingCollection_Disposed = "The collection has been disposed."; - public const string BlockingCollection_Take_CollectionModified = "The underlying collection was modified from outside of the BlockingCollection."; - public const string BlockingCollection_TimeoutInvalid = "The specified timeout must represent a value between -1 and {0}, inclusive."; - public const string BlockingCollection_ValidateCollectionsArray_DispElems = "The collections argument contains at least one disposed element."; - public const string BlockingCollection_ValidateCollectionsArray_LargeSize = "The collections length is greater than the supported range for 32 bit machine."; - public const string BlockingCollection_ValidateCollectionsArray_NullElems = "The collections argument contains at least one null element."; - public const string BlockingCollection_ValidateCollectionsArray_ZeroSize = "The collections argument is a zero-length array."; - public const string Common_OperationCanceled = "The operation was canceled."; - public const string ConcurrentBag_Ctor_ArgumentNullException = "The collection argument is null."; - public const string ConcurrentBag_CopyTo_ArgumentNullException = "The array argument is null."; - public const string Collection_CopyTo_ArgumentOutOfRangeException = "The index argument must be greater than or equal zero."; - public const string ConcurrentCollection_SyncRoot_NotSupported = "The SyncRoot property may not be used for the synchronization of concurrent collections."; - public const string ConcurrentDictionary_ArrayIncorrectType = "The array is multidimensional, or the type parameter for the set cannot be cast automatically to the type of the destination array."; - public const string ConcurrentDictionary_SourceContainsDuplicateKeys = "The source argument contains duplicate keys."; - public const string ConcurrentDictionary_ConcurrencyLevelMustBePositive = "The concurrencyLevel argument must be positive."; - public const string ConcurrentDictionary_CapacityMustNotBeNegative = "The capacity argument must be greater than or equal to zero."; - public const string ConcurrentDictionary_IndexIsNegative = "The index argument is less than zero."; - public const string ConcurrentDictionary_ArrayNotLargeEnough = "The index is equal to or greater than the length of the array, or the number of elements in the dictionary is greater than the available space from index to the end of the destination array."; - public const string ConcurrentDictionary_KeyAlreadyExisted = "The key already existed in the dictionary."; - public const string ConcurrentDictionary_ItemKeyIsNull = "TKey is a reference type and item.Key is null."; - public const string ConcurrentDictionary_TypeOfKeyIncorrect = "The key was of an incorrect type for this dictionary."; - public const string ConcurrentDictionary_TypeOfValueIncorrect = "The value was of an incorrect type for this dictionary."; - public const string ConcurrentStack_PushPopRange_CountOutOfRange = "The count argument must be greater than or equal to zero."; - public const string ConcurrentStack_PushPopRange_InvalidCount = "The sum of the startIndex and count arguments must be less than or equal to the collection's Count."; - public const string ConcurrentStack_PushPopRange_StartOutOfRange = "The startIndex argument must be greater than or equal to zero."; - public const string Partitioner_DynamicPartitionsNotSupported = "Dynamic partitions are not supported by this partitioner."; - public const string PartitionerStatic_CanNotCallGetEnumeratorAfterSourceHasBeenDisposed = "Can not call GetEnumerator on partitions after the source enumerable is disposed"; - public const string PartitionerStatic_CurrentCalledBeforeMoveNext = "MoveNext must be called at least once before calling Current."; - public const string ConcurrentBag_Enumerator_EnumerationNotStartedOrAlreadyFinished = "Enumeration has either not started or has already finished."; - public const string ArrayTypeMustBeExactMatch = "The array type must be exactly {0}."; - public const string CannotCallEqualsOnSpan = "Equals() on Span and ReadOnlySpan is not supported. Use operator== instead."; - public const string CannotCallGetHashCodeOnSpan = "GetHashCode() on Span and ReadOnlySpan is not supported."; - public const string Argument_EmptyValue = "Value cannot be empty."; - public const string PlatformNotSupported_RuntimeInformation = "RuntimeInformation is not supported for Portable Class Libraries."; - public const string MemoryDisposed = "Memory has been disposed."; - public const string OutstandingReferences = "Release all references before disposing this instance."; -} diff --git a/mcs/class/corlib/corefx/SR.cs.REMOVED.git-id b/mcs/class/corlib/corefx/SR.cs.REMOVED.git-id new file mode 100644 index 0000000000..baf66a0144 --- /dev/null +++ b/mcs/class/corlib/corefx/SR.cs.REMOVED.git-id @@ -0,0 +1 @@ +9ac00eca4c5e8b4a2e61d5826df2437d0ceb119b \ No newline at end of file diff --git a/mcs/class/corlib/corert/RuntimeThread.cs b/mcs/class/corlib/corert/RuntimeThread.cs index 72810fade3..a296d8cbc2 100644 --- a/mcs/class/corlib/corert/RuntimeThread.cs +++ b/mcs/class/corlib/corert/RuntimeThread.cs @@ -1,9 +1,14 @@ namespace Internal.Runtime.Augments { - class RuntimeThread - { - public static void InitializeThreadPoolThread () - { - } - } + class RuntimeThread + { + public static RuntimeThread InitializeThreadPoolThread () + { + return default; + } + + public void ResetThreadPoolThread () + { + } + } } \ No newline at end of file diff --git a/mcs/class/corlib/corert/Stream.cs b/mcs/class/corlib/corert/Stream.cs index 632cd8e3c6..80eb3c22e4 100644 --- a/mcs/class/corlib/corert/Stream.cs +++ b/mcs/class/corlib/corert/Stream.cs @@ -10,17 +10,20 @@ namespace System.IO throw new NotImplementedException (); } - public virtual void Write(ReadOnlySpan source) + public virtual void Write (ReadOnlySpan source) { throw new NotImplementedException (); } public virtual ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default(CancellationToken)) { + if (destination.TryGetArray (out ArraySegment array)) + return new ValueTask (ReadAsync (array.Array, array.Offset, array.Count, cancellationToken)); + throw new NotImplementedException (); } - public virtual Task WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken = default(CancellationToken)) + public virtual Task WriteAsync (ReadOnlyMemory source, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException (); } diff --git a/mcs/class/corlib/corlib.dll.sources b/mcs/class/corlib/corlib.dll.sources index fa940530ad..93879941ca 100644 --- a/mcs/class/corlib/corlib.dll.sources +++ b/mcs/class/corlib/corlib.dll.sources @@ -1636,37 +1636,45 @@ corert/ThreadPoolBoundHandle.cs ../../../external/corert/src/Runtime.Base/src/System/Runtime/InteropServices/NativeCallableAttribute.cs -../../../external/corert/src/System.Private.CoreLib/shared/System/TupleExtensions.cs -../../../external/corert/src/System.Private.CoreLib/shared/System/ValueTuple.cs - -../../../external/corert/src/System.Private.CoreLib/shared/System/Collections/DictionaryEntry.cs - -../../../external/corert/src/System.Private.CoreLib/shared/System/Collections/Generic/KeyValuePair.cs - ../../../external/corert/src/System.Private.CoreLib/shared/System/Diagnostics/Debug.cs -../../../external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/IsReadOnlyAttribute.cs -../../../external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/IsByRefLikeAttribute.cs -../../../external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ITuple.cs -../../../external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/RuntimeFeature.cs -../../../external/corert/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/TupleElementNamesAttribute.cs - ../../../external/corert/src/System.Private.CoreLib/shared/System/Threading/DeferredDisposableLifetime.cs - ../../../external/corert/src/System.Private.CoreLib/src/System/Array.cs -../../../external/corert/src/System.Private.CoreLib/src/System/Tuple.cs ../../../external/corert/src/System.Private.CoreLib/src/System/Collections/LowLevelComparer.cs -../../../external/corert/src/System.Private.CoreLib/src/System/Collections/ObjectEqualityComparer.cs ../../../external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.cs -../../../external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/IDictionaryDebugView.cs -../../../external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs -../../../external/corert/src/System.Private.CoreLib/src/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs + +../../../external/corert/src/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs corefx/SR.cs +../../../external/corefx/src/Common/src/CoreLib/System/Memory.cs +../../../external/corefx/src/Common/src/CoreLib/System/MemoryDebugView.cs +../../../external/corefx/src/Common/src/CoreLib/System/ReadOnlyMemory.cs +../../../external/corefx/src/Common/src/CoreLib/System/Tuple.cs +../../../external/corefx/src/Common/src/CoreLib/System/TupleExtensions.cs +../../../external/corefx/src/Common/src/CoreLib/System/ValueTuple.cs + +../../../external/corefx/src/Common/src/CoreLib/System/Buffers/IRetainable.cs +../../../external/corefx/src/Common/src/CoreLib/System/Buffers/MemoryHandle.cs +../../../external/corefx/src/Common/src/CoreLib/System/Buffers/OwnedMemory.cs + +../../../external/corefx/src/Common/src/CoreLib/System/Collections/DictionaryEntry.cs +../../../external/corefx/src/Common/src/CoreLib/System/Collections/Generic/Dictionary.cs +../../../external/corefx/src/Common/src/CoreLib/System/Collections/Generic/IDictionaryDebugView.cs +../../../external/corefx/src/Common/src/CoreLib/System/Collections/Generic/KeyValuePair.cs + +../../../external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IntrinsicAttribute.cs +../../../external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IsReadOnlyAttribute.cs +../../../external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/IsByRefLikeAttribute.cs +../../../external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/ITuple.cs +../../../external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/RuntimeFeature.cs +../../../external/corefx/src/Common/src/CoreLib/System/Runtime/CompilerServices/TupleElementNamesAttribute.cs + +../../../external/corefx/src/Common/src/CoreLib/System/Runtime/InteropServices/MemoryMarshal.cs + ../../../external/corefx/src/Common/src/System/Collections/Generic/ReferenceEqualityComparer.cs ../../../external/corefx/src/System.Collections/src/System/Collections/Generic/CollectionExtensions.cs @@ -1680,16 +1688,14 @@ corefx/SR.cs ../../../external/corefx/src/System.Collections.Concurrent/src/System/Collections/Concurrent/Partitioner.cs ../../../external/corefx/src/System.Collections.Concurrent/src/System/Collections/Concurrent/PartitionerStatic.cs -../../../external/corefx/src/System.Memory/src/System/Memory.cs -../../../external/corefx/src/System.Memory/src/System/MemoryDebugView.cs ../../../external/corefx/src/System.Memory/src/System/Pinnable.cs -../../../external/corefx/src/System.Memory/src/System/ReadOnlyMemory.cs ../../../external/corefx/src/System.Memory/src/System/ReadOnlySpan.cs ../../../external/corefx/src/System.Memory/src/System/Span.cs ../../../external/corefx/src/System.Memory/src/System/SpanDebugView.cs -../../../external/corefx/src/System.Memory/src/System/SpanExtensions.cs -../../../external/corefx/src/System.Memory/src/System/SpanExtensions.Portable.cs +../../../external/corefx/src/System.Memory/src/System/MemoryExtensions.cs +../../../external/corefx/src/System.Memory/src/System/MemoryExtensions.Portable.cs ../../../external/corefx/src/System.Memory/src/System/SpanHelpers.cs +../../../external/corefx/src/System.Memory/src/System/SpanHelpers.BinarySearch.cs ../../../external/corefx/src/System.Memory/src/System/SpanHelpers.Clear.cs ../../../external/corefx/src/System.Memory/src/System/SpanHelpers.byte.cs ../../../external/corefx/src/System.Memory/src/System/SpanHelpers.T.cs diff --git a/mcs/class/lib/monolite-darwin/1051000001/Mono.Security.dll.REMOVED.git-id b/mcs/class/lib/monolite-darwin/1051000001/Mono.Security.dll.REMOVED.git-id index 503ebeadf7..71214bec84 100644 --- a/mcs/class/lib/monolite-darwin/1051000001/Mono.Security.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-darwin/1051000001/Mono.Security.dll.REMOVED.git-id @@ -1 +1 @@ -69a26e51725fcb244bd327ae285ebfbaced12674 \ No newline at end of file +6a12bbf6b6b494794344c6f5dbe585de0e5c4e9a \ No newline at end of file diff --git a/mcs/class/lib/monolite-darwin/1051000001/System.Configuration.dll.REMOVED.git-id b/mcs/class/lib/monolite-darwin/1051000001/System.Configuration.dll.REMOVED.git-id index 3b647fd8bf..9da941e2c7 100644 --- a/mcs/class/lib/monolite-darwin/1051000001/System.Configuration.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-darwin/1051000001/System.Configuration.dll.REMOVED.git-id @@ -1 +1 @@ -1f0c41b5d95d80c338214ae556cbb2dba670b961 \ No newline at end of file +ab9de6de6fdf886eee4177e261571f49ac7d95d4 \ No newline at end of file diff --git a/mcs/class/lib/monolite-darwin/1051000001/System.Core.dll.REMOVED.git-id b/mcs/class/lib/monolite-darwin/1051000001/System.Core.dll.REMOVED.git-id index b7a3a0ccca..921512ce2c 100644 --- a/mcs/class/lib/monolite-darwin/1051000001/System.Core.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-darwin/1051000001/System.Core.dll.REMOVED.git-id @@ -1 +1 @@ -f1e04651a1e3a4ed7d03eb975048e561e7ba66f9 \ No newline at end of file +58f852aeaa8ba5cd15d31ea427832860f3ce486a \ No newline at end of file diff --git a/mcs/class/lib/monolite-darwin/1051000001/System.Numerics.dll.REMOVED.git-id b/mcs/class/lib/monolite-darwin/1051000001/System.Numerics.dll.REMOVED.git-id index 3a200bf9b0..6540c5c1e9 100644 --- a/mcs/class/lib/monolite-darwin/1051000001/System.Numerics.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-darwin/1051000001/System.Numerics.dll.REMOVED.git-id @@ -1 +1 @@ -d385675bcf0f3ea6b08749692960d500809e5b40 \ No newline at end of file +12e34db1e270c4a5878c1fa53d0d867bea22606a \ No newline at end of file diff --git a/mcs/class/lib/monolite-darwin/1051000001/System.Security.dll.REMOVED.git-id b/mcs/class/lib/monolite-darwin/1051000001/System.Security.dll.REMOVED.git-id index cf2d99ac88..70b41aa741 100644 --- a/mcs/class/lib/monolite-darwin/1051000001/System.Security.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-darwin/1051000001/System.Security.dll.REMOVED.git-id @@ -1 +1 @@ -55612be79fd6ddb7cd581dbcb4f8f2e4fb68161d \ No newline at end of file +ade40d1fab96b3435c4dde44ea9e11bf697460d0 \ No newline at end of file diff --git a/mcs/class/lib/monolite-darwin/1051000001/System.Xml.dll.REMOVED.git-id b/mcs/class/lib/monolite-darwin/1051000001/System.Xml.dll.REMOVED.git-id index 8ad5d21c65..c2274c0270 100644 --- a/mcs/class/lib/monolite-darwin/1051000001/System.Xml.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-darwin/1051000001/System.Xml.dll.REMOVED.git-id @@ -1 +1 @@ -f4ac134b907d347ef37ba6b4e9e32cbea9edca82 \ No newline at end of file +b9a167340545a1ef4092db9539ebc85dae3a2430 \ No newline at end of file diff --git a/mcs/class/lib/monolite-darwin/1051000001/System.dll.REMOVED.git-id b/mcs/class/lib/monolite-darwin/1051000001/System.dll.REMOVED.git-id index f1b652c72a..d4ea265019 100644 --- a/mcs/class/lib/monolite-darwin/1051000001/System.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-darwin/1051000001/System.dll.REMOVED.git-id @@ -1 +1 @@ -fc0f5e575e3adcaf8cb897b729bcb48359ad7c1f \ No newline at end of file +7b8696a9592252dafa94d4a38f88fa5e993f747e \ No newline at end of file diff --git a/mcs/class/lib/monolite-darwin/1051000001/mcs.exe.REMOVED.git-id b/mcs/class/lib/monolite-darwin/1051000001/mcs.exe.REMOVED.git-id index e81909c95a..501cc38240 100644 --- a/mcs/class/lib/monolite-darwin/1051000001/mcs.exe.REMOVED.git-id +++ b/mcs/class/lib/monolite-darwin/1051000001/mcs.exe.REMOVED.git-id @@ -1 +1 @@ -e7d216667a2c332f9da392fb3a8ba348d560af69 \ No newline at end of file +40243be7f09fee217d4b7571bcb4919e6c75c2de \ No newline at end of file diff --git a/mcs/class/lib/monolite-darwin/1051000001/mscorlib.dll.REMOVED.git-id b/mcs/class/lib/monolite-darwin/1051000001/mscorlib.dll.REMOVED.git-id index 8c23c9794a..2be11d1095 100644 --- a/mcs/class/lib/monolite-darwin/1051000001/mscorlib.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-darwin/1051000001/mscorlib.dll.REMOVED.git-id @@ -1 +1 @@ -1934598cb36dd4e7224ab02f751aad9917a348cf \ No newline at end of file +1472389e53b7376dce7e6d0a8cbbe98df0866fef \ No newline at end of file diff --git a/mcs/class/lib/monolite-linux/1051000001/Mono.Security.dll.REMOVED.git-id b/mcs/class/lib/monolite-linux/1051000001/Mono.Security.dll.REMOVED.git-id index 503ebeadf7..71214bec84 100644 --- a/mcs/class/lib/monolite-linux/1051000001/Mono.Security.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-linux/1051000001/Mono.Security.dll.REMOVED.git-id @@ -1 +1 @@ -69a26e51725fcb244bd327ae285ebfbaced12674 \ No newline at end of file +6a12bbf6b6b494794344c6f5dbe585de0e5c4e9a \ No newline at end of file diff --git a/mcs/class/lib/monolite-linux/1051000001/System.Configuration.dll.REMOVED.git-id b/mcs/class/lib/monolite-linux/1051000001/System.Configuration.dll.REMOVED.git-id index 3b647fd8bf..9da941e2c7 100644 --- a/mcs/class/lib/monolite-linux/1051000001/System.Configuration.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-linux/1051000001/System.Configuration.dll.REMOVED.git-id @@ -1 +1 @@ -1f0c41b5d95d80c338214ae556cbb2dba670b961 \ No newline at end of file +ab9de6de6fdf886eee4177e261571f49ac7d95d4 \ No newline at end of file diff --git a/mcs/class/lib/monolite-linux/1051000001/System.Core.dll.REMOVED.git-id b/mcs/class/lib/monolite-linux/1051000001/System.Core.dll.REMOVED.git-id index b7a3a0ccca..921512ce2c 100644 --- a/mcs/class/lib/monolite-linux/1051000001/System.Core.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-linux/1051000001/System.Core.dll.REMOVED.git-id @@ -1 +1 @@ -f1e04651a1e3a4ed7d03eb975048e561e7ba66f9 \ No newline at end of file +58f852aeaa8ba5cd15d31ea427832860f3ce486a \ No newline at end of file diff --git a/mcs/class/lib/monolite-linux/1051000001/System.Numerics.dll.REMOVED.git-id b/mcs/class/lib/monolite-linux/1051000001/System.Numerics.dll.REMOVED.git-id index 3a200bf9b0..6540c5c1e9 100644 --- a/mcs/class/lib/monolite-linux/1051000001/System.Numerics.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-linux/1051000001/System.Numerics.dll.REMOVED.git-id @@ -1 +1 @@ -d385675bcf0f3ea6b08749692960d500809e5b40 \ No newline at end of file +12e34db1e270c4a5878c1fa53d0d867bea22606a \ No newline at end of file diff --git a/mcs/class/lib/monolite-linux/1051000001/System.Security.dll.REMOVED.git-id b/mcs/class/lib/monolite-linux/1051000001/System.Security.dll.REMOVED.git-id index cf2d99ac88..70b41aa741 100644 --- a/mcs/class/lib/monolite-linux/1051000001/System.Security.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-linux/1051000001/System.Security.dll.REMOVED.git-id @@ -1 +1 @@ -55612be79fd6ddb7cd581dbcb4f8f2e4fb68161d \ No newline at end of file +ade40d1fab96b3435c4dde44ea9e11bf697460d0 \ No newline at end of file diff --git a/mcs/class/lib/monolite-linux/1051000001/System.Xml.dll.REMOVED.git-id b/mcs/class/lib/monolite-linux/1051000001/System.Xml.dll.REMOVED.git-id index 8ad5d21c65..c2274c0270 100644 --- a/mcs/class/lib/monolite-linux/1051000001/System.Xml.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-linux/1051000001/System.Xml.dll.REMOVED.git-id @@ -1 +1 @@ -f4ac134b907d347ef37ba6b4e9e32cbea9edca82 \ No newline at end of file +b9a167340545a1ef4092db9539ebc85dae3a2430 \ No newline at end of file diff --git a/mcs/class/lib/monolite-linux/1051000001/System.dll.REMOVED.git-id b/mcs/class/lib/monolite-linux/1051000001/System.dll.REMOVED.git-id index f1b652c72a..d4ea265019 100644 --- a/mcs/class/lib/monolite-linux/1051000001/System.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-linux/1051000001/System.dll.REMOVED.git-id @@ -1 +1 @@ -fc0f5e575e3adcaf8cb897b729bcb48359ad7c1f \ No newline at end of file +7b8696a9592252dafa94d4a38f88fa5e993f747e \ No newline at end of file diff --git a/mcs/class/lib/monolite-linux/1051000001/mcs.exe.REMOVED.git-id b/mcs/class/lib/monolite-linux/1051000001/mcs.exe.REMOVED.git-id index e81909c95a..501cc38240 100644 --- a/mcs/class/lib/monolite-linux/1051000001/mcs.exe.REMOVED.git-id +++ b/mcs/class/lib/monolite-linux/1051000001/mcs.exe.REMOVED.git-id @@ -1 +1 @@ -e7d216667a2c332f9da392fb3a8ba348d560af69 \ No newline at end of file +40243be7f09fee217d4b7571bcb4919e6c75c2de \ No newline at end of file diff --git a/mcs/class/lib/monolite-linux/1051000001/mscorlib.dll.REMOVED.git-id b/mcs/class/lib/monolite-linux/1051000001/mscorlib.dll.REMOVED.git-id index 8c23c9794a..2be11d1095 100644 --- a/mcs/class/lib/monolite-linux/1051000001/mscorlib.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-linux/1051000001/mscorlib.dll.REMOVED.git-id @@ -1 +1 @@ -1934598cb36dd4e7224ab02f751aad9917a348cf \ No newline at end of file +1472389e53b7376dce7e6d0a8cbbe98df0866fef \ No newline at end of file diff --git a/mcs/class/lib/monolite-win32/1051000001/Mono.Security.dll.REMOVED.git-id b/mcs/class/lib/monolite-win32/1051000001/Mono.Security.dll.REMOVED.git-id index 503ebeadf7..71214bec84 100644 --- a/mcs/class/lib/monolite-win32/1051000001/Mono.Security.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-win32/1051000001/Mono.Security.dll.REMOVED.git-id @@ -1 +1 @@ -69a26e51725fcb244bd327ae285ebfbaced12674 \ No newline at end of file +6a12bbf6b6b494794344c6f5dbe585de0e5c4e9a \ No newline at end of file diff --git a/mcs/class/lib/monolite-win32/1051000001/System.Configuration.dll.REMOVED.git-id b/mcs/class/lib/monolite-win32/1051000001/System.Configuration.dll.REMOVED.git-id index 3b647fd8bf..9da941e2c7 100644 --- a/mcs/class/lib/monolite-win32/1051000001/System.Configuration.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-win32/1051000001/System.Configuration.dll.REMOVED.git-id @@ -1 +1 @@ -1f0c41b5d95d80c338214ae556cbb2dba670b961 \ No newline at end of file +ab9de6de6fdf886eee4177e261571f49ac7d95d4 \ No newline at end of file diff --git a/mcs/class/lib/monolite-win32/1051000001/System.Core.dll.REMOVED.git-id b/mcs/class/lib/monolite-win32/1051000001/System.Core.dll.REMOVED.git-id index b7a3a0ccca..921512ce2c 100644 --- a/mcs/class/lib/monolite-win32/1051000001/System.Core.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-win32/1051000001/System.Core.dll.REMOVED.git-id @@ -1 +1 @@ -f1e04651a1e3a4ed7d03eb975048e561e7ba66f9 \ No newline at end of file +58f852aeaa8ba5cd15d31ea427832860f3ce486a \ No newline at end of file diff --git a/mcs/class/lib/monolite-win32/1051000001/System.Numerics.dll.REMOVED.git-id b/mcs/class/lib/monolite-win32/1051000001/System.Numerics.dll.REMOVED.git-id index 3a200bf9b0..6540c5c1e9 100644 --- a/mcs/class/lib/monolite-win32/1051000001/System.Numerics.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-win32/1051000001/System.Numerics.dll.REMOVED.git-id @@ -1 +1 @@ -d385675bcf0f3ea6b08749692960d500809e5b40 \ No newline at end of file +12e34db1e270c4a5878c1fa53d0d867bea22606a \ No newline at end of file diff --git a/mcs/class/lib/monolite-win32/1051000001/System.Security.dll.REMOVED.git-id b/mcs/class/lib/monolite-win32/1051000001/System.Security.dll.REMOVED.git-id index cf2d99ac88..70b41aa741 100644 --- a/mcs/class/lib/monolite-win32/1051000001/System.Security.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-win32/1051000001/System.Security.dll.REMOVED.git-id @@ -1 +1 @@ -55612be79fd6ddb7cd581dbcb4f8f2e4fb68161d \ No newline at end of file +ade40d1fab96b3435c4dde44ea9e11bf697460d0 \ No newline at end of file diff --git a/mcs/class/lib/monolite-win32/1051000001/System.Xml.dll.REMOVED.git-id b/mcs/class/lib/monolite-win32/1051000001/System.Xml.dll.REMOVED.git-id index 8ad5d21c65..c2274c0270 100644 --- a/mcs/class/lib/monolite-win32/1051000001/System.Xml.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-win32/1051000001/System.Xml.dll.REMOVED.git-id @@ -1 +1 @@ -f4ac134b907d347ef37ba6b4e9e32cbea9edca82 \ No newline at end of file +b9a167340545a1ef4092db9539ebc85dae3a2430 \ No newline at end of file diff --git a/mcs/class/lib/monolite-win32/1051000001/System.dll.REMOVED.git-id b/mcs/class/lib/monolite-win32/1051000001/System.dll.REMOVED.git-id index f1b652c72a..d4ea265019 100644 --- a/mcs/class/lib/monolite-win32/1051000001/System.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-win32/1051000001/System.dll.REMOVED.git-id @@ -1 +1 @@ -fc0f5e575e3adcaf8cb897b729bcb48359ad7c1f \ No newline at end of file +7b8696a9592252dafa94d4a38f88fa5e993f747e \ No newline at end of file diff --git a/mcs/class/lib/monolite-win32/1051000001/mcs.exe.REMOVED.git-id b/mcs/class/lib/monolite-win32/1051000001/mcs.exe.REMOVED.git-id index e81909c95a..501cc38240 100644 --- a/mcs/class/lib/monolite-win32/1051000001/mcs.exe.REMOVED.git-id +++ b/mcs/class/lib/monolite-win32/1051000001/mcs.exe.REMOVED.git-id @@ -1 +1 @@ -e7d216667a2c332f9da392fb3a8ba348d560af69 \ No newline at end of file +40243be7f09fee217d4b7571bcb4919e6c75c2de \ No newline at end of file diff --git a/mcs/class/lib/monolite-win32/1051000001/mscorlib.dll.REMOVED.git-id b/mcs/class/lib/monolite-win32/1051000001/mscorlib.dll.REMOVED.git-id index 8c23c9794a..2be11d1095 100644 --- a/mcs/class/lib/monolite-win32/1051000001/mscorlib.dll.REMOVED.git-id +++ b/mcs/class/lib/monolite-win32/1051000001/mscorlib.dll.REMOVED.git-id @@ -1 +1 @@ -1934598cb36dd4e7224ab02f751aad9917a348cf \ No newline at end of file +1472389e53b7376dce7e6d0a8cbbe98df0866fef \ No newline at end of file diff --git a/mcs/class/referencesource/mscorlib/system/bitconverter.cs b/mcs/class/referencesource/mscorlib/system/bitconverter.cs index 49d20cd669..5c1e45160c 100644 --- a/mcs/class/referencesource/mscorlib/system/bitconverter.cs +++ b/mcs/class/referencesource/mscorlib/system/bitconverter.cs @@ -472,7 +472,18 @@ namespace System { // If we ever run on big endian machines, produce two versions where endianness is specified. Contract.Assert(IsLittleEndian, "This method is implemented assuming little endian with an ambiguous spec."); return *((double*)&value); - } + } + +#if MONO + // Converts a Span into an int + public static int ToInt32(ReadOnlySpan value) + { + if (value.Length < sizeof(int)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned(ref value.DangerousGetPinnableReference()); + } +#endif + } diff --git a/mcs/class/referencesource/mscorlib/system/collections/generic/list.cs b/mcs/class/referencesource/mscorlib/system/collections/generic/list.cs index 44210ced5e..c67e7732f9 100644 --- a/mcs/class/referencesource/mscorlib/system/collections/generic/list.cs +++ b/mcs/class/referencesource/mscorlib/system/collections/generic/list.cs @@ -178,7 +178,7 @@ namespace System.Collections.Generic { get { // Following trick can reduce the range check by one if ((uint) index >= (uint)_size) { - ThrowHelper.ThrowArgumentOutOfRangeException(); + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); } Contract.EndContractBlock(); #if MONO @@ -190,7 +190,7 @@ namespace System.Collections.Generic { set { if ((uint) index >= (uint)_size) { - ThrowHelper.ThrowArgumentOutOfRangeException(); + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); } Contract.EndContractBlock(); _items[index] = value; @@ -883,7 +883,7 @@ namespace System.Collections.Generic { // public void RemoveAt(int index) { if ((uint)index >= (uint)_size) { - ThrowHelper.ThrowArgumentOutOfRangeException(); + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); } Contract.EndContractBlock(); _size--; diff --git a/mcs/class/referencesource/mscorlib/system/int32.cs b/mcs/class/referencesource/mscorlib/system/int32.cs index a07f6e89c6..8e38b23294 100644 --- a/mcs/class/referencesource/mscorlib/system/int32.cs +++ b/mcs/class/referencesource/mscorlib/system/int32.cs @@ -388,5 +388,11 @@ namespace System { /// } ///#endif // #if GENERICS_WORK +#if MONO + public bool TryFormat(Span destination, out int charsWritten, System.ReadOnlySpan format = default, System.IFormatProvider provider = null) + { + throw new NotImplementedException (); + } +#endif } } diff --git a/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/RuntimeWrappedException.cs b/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/RuntimeWrappedException.cs index 14f68188d0..e747c5b92e 100644 --- a/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/RuntimeWrappedException.cs +++ b/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/RuntimeWrappedException.cs @@ -23,7 +23,10 @@ namespace System.Runtime.CompilerServices { [Serializable] public sealed class RuntimeWrappedException : Exception { - private RuntimeWrappedException(Object thrownObject) +#if MONO + public +#endif + RuntimeWrappedException(Object thrownObject) : base(Environment.GetResourceString("RuntimeWrappedException")) { SetErrorCode(System.__HResults.COR_E_RUNTIMEWRAPPED); m_wrappedException = thrownObject; diff --git a/mcs/class/referencesource/mscorlib/system/text/encoding.cs b/mcs/class/referencesource/mscorlib/system/text/encoding.cs index 3180f250c2..bd1b75f72c 100644 --- a/mcs/class/referencesource/mscorlib/system/text/encoding.cs +++ b/mcs/class/referencesource/mscorlib/system/text/encoding.cs @@ -1359,6 +1359,16 @@ namespace System.Text return String.CreateStringFromEncoding(bytes, byteCount, this); } +#if MONO + public unsafe string GetString(ReadOnlySpan bytes) + { + fixed (byte* bytesPtr = &bytes.DangerousGetPinnableReference()) + { + return GetString(bytesPtr, bytes.Length); + } + } +#endif + // Returns the code page identifier of this encoding. The returned value is // an integer between 0 and 65535 if the encoding has a code page // identifier, or -1 if the encoding does not represent a code page. diff --git a/mcs/class/referencesource/mscorlib/system/throwhelper.cs b/mcs/class/referencesource/mscorlib/system/throwhelper.cs index 0eea324a96..34f3d69db3 100644 --- a/mcs/class/referencesource/mscorlib/system/throwhelper.cs +++ b/mcs/class/referencesource/mscorlib/system/throwhelper.cs @@ -49,12 +49,15 @@ namespace System { using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Diagnostics.Contracts; + using System.Collections.Generic; [Pure] internal static partial class ThrowHelper { +#if !MONO internal static void ThrowArgumentOutOfRangeException() { ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index); } +#endif internal static void ThrowWrongKeyTypeArgumentException(object key, Type targetType) { throw new ArgumentException(Environment.GetResourceString("Arg_WrongType", key, targetType), "key"); @@ -121,6 +124,68 @@ namespace System { throw new ObjectDisposedException(objectName, Environment.GetResourceString(GetResourceName(resource))); } +#if MONO + internal static void ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion() + { + throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + } + + internal static void ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen() + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + + internal static void ThrowInvalidOperationException_InvalidOperation_EnumNotStarted() + { + throw new InvalidOperationException(SR.InvalidOperation_EnumNotStarted); + } + + internal static void ThrowInvalidOperationException_InvalidOperation_EnumEnded() + { + throw new InvalidOperationException(SR.InvalidOperation_EnumEnded); + } + + private static ArgumentOutOfRangeException GetArgumentOutOfRangeException(ExceptionArgument argument, string resource) + { + return new ArgumentOutOfRangeException(GetArgumentName(argument), resource); + } + + internal static void ThrowArgumentOutOfRange_IndexException() + { + throw GetArgumentOutOfRangeException(ExceptionArgument.index, + SR.ArgumentOutOfRange_Index); + } + + internal static void ThrowIndexArgumentOutOfRange_NeedNonNegNumException() + { + throw GetArgumentOutOfRangeException(ExceptionArgument.index, + SR.ArgumentOutOfRange_NeedNonNegNum); + } + + internal static void ThrowArgumentException_Argument_InvalidArrayType() + { + throw new ArgumentException(SR.Argument_InvalidArrayType); + } + + private static ArgumentException GetAddingDuplicateWithKeyArgumentException(object key) + { + return new ArgumentException(SR.Format(SR.Argument_AddingDuplicate, key)); + } + internal static void ThrowAddingDuplicateWithKeyArgumentException(object key) + { + throw GetAddingDuplicateWithKeyArgumentException(key); + } + + private static KeyNotFoundException GetKeyNotFoundException(object key) + { + throw new KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key.ToString())); + } + internal static void ThrowKeyNotFoundException(object key) + { + throw GetKeyNotFoundException(key); + } +#endif + // Allow nulls for reference types and Nullable, but not for value types. internal static void IfNullAndNullsAreIllegalThenThrow(object value, ExceptionArgument argName) { // Note that default(T) is not equal to null for value types except when T is Nullable. @@ -475,6 +540,9 @@ namespace System { pointer, ownedMemory, text, + length, + comparer, + comparable #endif } diff --git a/mcs/class/referencesource/mscorlib/system/type.cs b/mcs/class/referencesource/mscorlib/system/type.cs index ef7472f501..e5611108ff 100644 --- a/mcs/class/referencesource/mscorlib/system/type.cs +++ b/mcs/class/referencesource/mscorlib/system/type.cs @@ -1894,6 +1894,8 @@ namespace System { #if MONO public virtual bool IsSZArray { get { throw new NotImplementedException (); } } + + public virtual bool IsCollectible => true; #endif } diff --git a/mcs/errors/cs0029-42.cs b/mcs/errors/cs0029-42.cs new file mode 100644 index 0000000000..c7671000c7 --- /dev/null +++ b/mcs/errors/cs0029-42.cs @@ -0,0 +1,10 @@ +// CS0029: Cannot implicitly convert type `string' to `int' +// Line: 8 + +class C +{ + void Exists (int _) + { + _ = "2"; + } +} \ No newline at end of file diff --git a/mcs/errors/cs0103-18.cs b/mcs/errors/cs0103-18.cs new file mode 100644 index 0000000000..8cec755d23 --- /dev/null +++ b/mcs/errors/cs0103-18.cs @@ -0,0 +1,10 @@ +// CS0103: The name `_' does not exist in the current context +// Line: 8 + +class C +{ + void Test () + { + _.ToString (); + } +} \ No newline at end of file diff --git a/mcs/errors/cs1502-11.cs b/mcs/errors/cs1502-11.cs deleted file mode 100644 index 82dcb3a2c1..0000000000 --- a/mcs/errors/cs1502-11.cs +++ /dev/null @@ -1,11 +0,0 @@ -// CS1502: The best overloaded method match for `string.String(char*)' has some invalid arguments -// Line: 8 - -class C -{ - static string Prop { - get { - return new string ("s"); - } - } -} diff --git a/mcs/errors/cs1644-60.cs b/mcs/errors/cs1644-60.cs new file mode 100644 index 0000000000..ca9547bc56 --- /dev/null +++ b/mcs/errors/cs1644-60.cs @@ -0,0 +1,11 @@ +// CS1644: Feature `discards' cannot be used because it is not part of the C# 6.0 language specification +// Line: 9 +// Compiler options: -langversion:6 + +class X +{ + int Test () + { + _ = 2; + } +} diff --git a/mcs/errors/cs8183.cs b/mcs/errors/cs8183.cs new file mode 100644 index 0000000000..f9e9004b73 --- /dev/null +++ b/mcs/errors/cs8183.cs @@ -0,0 +1,11 @@ +// CS8183: Cannot infer the type of implicitly-typed discard +// Line: 9 +// Compiler options: -langversion:7.2 + +class X +{ + public static void Main () + { + _ = default; + } +} \ No newline at end of file diff --git a/mcs/errors/cs8184.cs b/mcs/errors/cs8184.cs new file mode 100644 index 0000000000..19a4685d7b --- /dev/null +++ b/mcs/errors/cs8184.cs @@ -0,0 +1,10 @@ +// CS8184: A deconstruction cannot mix declarations and expressions on the left-hand-side +// Line: 8 + +class X +{ + public static void Main () + { + (int a, b) = (1, 2); + } +} \ No newline at end of file diff --git a/mcs/errors/cs8207.cs b/mcs/errors/cs8207.cs new file mode 100644 index 0000000000..31090948b4 --- /dev/null +++ b/mcs/errors/cs8207.cs @@ -0,0 +1,19 @@ +// CS8207: An expression tree cannot contain a discard +// Line: 11 + +using System; +using System.Linq.Expressions; + +class X +{ + void Test () + { + Expression> e = () => TryGetValue (out _); + } + + bool TryGetValue (out int arg) + { + arg = 3; + return true; + } +} diff --git a/mcs/errors/cs8209.cs b/mcs/errors/cs8209.cs new file mode 100644 index 0000000000..3a46a206c8 --- /dev/null +++ b/mcs/errors/cs8209.cs @@ -0,0 +1,10 @@ +// CS8209: Cannot assign void to a discard +// Line: 8 + +class C +{ + public static void Main () + { + _ = Main (); + } +} \ No newline at end of file diff --git a/mcs/errors/cs8323.cs b/mcs/errors/cs8323.cs new file mode 100644 index 0000000000..c6c9309ec0 --- /dev/null +++ b/mcs/errors/cs8323.cs @@ -0,0 +1,15 @@ +// CS8323: Named argument `str' is used out of position but is followed by positional argument +// Line: 9 +// Compiler options: -langversion:7.2 + +class X +{ + public static void Main () + { + Test (str: "", ""); + } + + static void Test (int arg, string str) + { + } +} \ No newline at end of file diff --git a/mcs/errors/cs8324.cs b/mcs/errors/cs8324.cs new file mode 100644 index 0000000000..8a0be1aefb --- /dev/null +++ b/mcs/errors/cs8324.cs @@ -0,0 +1,12 @@ +// CS8324: Named argument specifications must appear after all fixed arguments have been specified in a dynamic invocation +// Line: 10 +// Compiler options: -langversion:7.2 + +class C +{ + void M () + { + dynamic d = new object (); + d.M (arg: 1, ""); + } +} \ No newline at end of file diff --git a/mcs/mcs/convert.cs b/mcs/mcs/convert.cs index f4012fbd22..ae153fc49e 100644 --- a/mcs/mcs/convert.cs +++ b/mcs/mcs/convert.cs @@ -819,7 +819,7 @@ namespace Mono.CSharp { if (expr_type == target_type) return true; - if (expr_type == InternalType.ThrowExpr) + if (expr_type == InternalType.ThrowExpr || expr_type == InternalType.DefaultType) return target_type.Kind != MemberKind.InternalCompilerType; if (target_type.IsNullableType) diff --git a/mcs/mcs/cs-parser.jay.REMOVED.git-id b/mcs/mcs/cs-parser.jay.REMOVED.git-id index 5ef91724c4..65e97dae30 100644 --- a/mcs/mcs/cs-parser.jay.REMOVED.git-id +++ b/mcs/mcs/cs-parser.jay.REMOVED.git-id @@ -1 +1 @@ -e2e85431d997383f904cd014a958d6d8310f1915 \ No newline at end of file +b27caaa0982fd67a1d7894558a1ad7e1bc5164da \ No newline at end of file diff --git a/mcs/mcs/cs-tokenizer.cs b/mcs/mcs/cs-tokenizer.cs index 5872a76246..37edb5c122 100644 --- a/mcs/mcs/cs-tokenizer.cs +++ b/mcs/mcs/cs-tokenizer.cs @@ -718,8 +718,11 @@ namespace Mono.CSharp case Token.DEFAULT: switch (peek_token ()) { case Token.COLON: - token (); - res = Token.DEFAULT_COLON; + // Special case: foo == null ? default : 1; + if (current_token != Token.INTERR) { + token (); + res = Token.DEFAULT_COLON; + } break; case Token.OPEN_PARENS: case Token.OPEN_PARENS_CAST: @@ -1402,6 +1405,7 @@ namespace Mono.CSharp case Token.NEW: case Token.INTERPOLATED_STRING: case Token.THROW: + case Token.DEFAULT_COLON: next_token = Token.INTERR; break; diff --git a/mcs/mcs/ecore.cs.REMOVED.git-id b/mcs/mcs/ecore.cs.REMOVED.git-id index f1b9b81096..a622fef689 100644 --- a/mcs/mcs/ecore.cs.REMOVED.git-id +++ b/mcs/mcs/ecore.cs.REMOVED.git-id @@ -1 +1 @@ -5e2e7f2436fd66421df911b794e620b8c8789969 \ No newline at end of file +b6a508cffc61cadc2e4ab73e7cc62af1fc2793d6 \ No newline at end of file diff --git a/mcs/mcs/expression.cs.REMOVED.git-id b/mcs/mcs/expression.cs.REMOVED.git-id index 000c2a102e..6096802326 100644 --- a/mcs/mcs/expression.cs.REMOVED.git-id +++ b/mcs/mcs/expression.cs.REMOVED.git-id @@ -1 +1 @@ -74d15942be865d75be6494f7125eba6633ef6317 \ No newline at end of file +cf27d94416e563d7cd096b31566b356ef3f05640 \ No newline at end of file diff --git a/mcs/mcs/membercache.cs b/mcs/mcs/membercache.cs index eebf71b844..ca05d2d7af 100644 --- a/mcs/mcs/membercache.cs +++ b/mcs/mcs/membercache.cs @@ -309,7 +309,6 @@ namespace Mono.CSharp { // if (!BuiltinTypeSpec.IsPrimitiveType (dt) || dt.BuiltinType == BuiltinTypeSpec.Type.Char) { switch (dt.BuiltinType) { - case BuiltinTypeSpec.Type.String: case BuiltinTypeSpec.Type.Delegate: case BuiltinTypeSpec.Type.MulticastDelegate: break; @@ -317,6 +316,9 @@ namespace Mono.CSharp { if (name == Operator.GetMetadataName (Operator.OpType.Implicit) || name == Operator.GetMetadataName (Operator.OpType.Explicit)) { state |= StateFlags.HasConversionOperator; } else { + if (dt.BuiltinType == BuiltinTypeSpec.Type.String) + break; + state |= StateFlags.HasUserOperator; } diff --git a/mcs/mcs/tuples.cs b/mcs/mcs/tuples.cs index bb7faf734d..901efdc954 100644 --- a/mcs/mcs/tuples.cs +++ b/mcs/mcs/tuples.cs @@ -432,7 +432,7 @@ namespace Mono.CSharp { Expression source; List targetExprs; - List variablesToInfer; + List variables; Expression instance; public TupleDeconstruct (List targetExprs, Expression source, Location loc) @@ -442,10 +442,11 @@ namespace Mono.CSharp this.loc = loc; } - public TupleDeconstruct (List targetExprs, List variables, Expression source, Location loc) - : this (targetExprs, source, loc) + public TupleDeconstruct (List variables, Expression source, Location loc) { - this.variablesToInfer = variables; + this.source = source; + this.variables = variables; + this.loc = loc; } public override Expression CreateExpressionTree (ResolveContext ec) @@ -469,9 +470,18 @@ namespace Mono.CSharp var src_type = src.Type; if (src_type.IsTupleType) { - if (src_type.Arity != targetExprs.Count) { + int target_count; + + if (targetExprs == null) { + target_count = variables.Count; + targetExprs = new List (target_count); + } else { + target_count = targetExprs.Count; + } + + if (src_type.Arity != target_count) { rc.Report.Error (8132, loc, "Cannot deconstruct a tuple of `{0}' elements into `{1}' variables", - src_type.Arity.ToString (), targetExprs.Count.ToString ()); + src_type.Arity.ToString (CultureInfo.InvariantCulture), target_count.ToString (CultureInfo.InvariantCulture)); return null; } @@ -482,27 +492,44 @@ namespace Mono.CSharp instance = expr_variable.CreateReferenceExpression (rc, loc); } - for (int i = 0; i < targetExprs.Count; ++i) { + for (int i = 0; i < target_count; ++i) { var tle = src_type.TypeArguments [i]; - var lv = variablesToInfer? [i]; - if (lv != null) { - if (InternalType.HasNoType (tle)) { - rc.Report.Error (8130, Location, "Cannot infer the type of implicitly-typed deconstruction variable `{0}'", lv.Name); - lv.Type = InternalType.ErrorType; + if (variables != null) { + var variable = variables [i].Variable; + + if (variable.Type == InternalType.Discard) { + variables [i] = null; + targetExprs.Add (EmptyExpressionStatement.Instance); continue; } - lv.Type = tle; - lv.PrepareAssignmentAnalysis ((BlockContext) rc); - } + var variable_type = variables [i].TypeExpression; + targetExprs.Add (new LocalVariableReference (variable, variable.Location)); + + if (variable_type is VarExpr) { + if (InternalType.HasNoType (tle)) { + rc.Report.Error (8130, Location, "Cannot infer the type of implicitly-typed deconstruction variable `{0}'", variable.Name); + tle = InternalType.ErrorType; + } + + variable.Type = tle; + } else { + variable.Type = variable_type.ResolveAsType (rc); + } + + variable.PrepareAssignmentAnalysis ((BlockContext)rc); + } var element_src = tupleLiteral == null ? new MemberAccess (instance, NamedTupleSpec.GetElementPropertyName (i)) : tupleLiteral.Elements [i].Expr; targetExprs [i] = new SimpleAssign (targetExprs [i], element_src).Resolve (rc); } eclass = ExprClass.Value; + + // TODO: The type is same only if there is no target element conversion + // var res = (/*byte*/ b, /*short*/ s) = (2, 4); type = src.Type; return this; } @@ -527,11 +554,24 @@ namespace Mono.CSharp public override void Emit (EmitContext ec) { - throw new NotImplementedException (); + if (instance != null) + ((ExpressionStatement)source).EmitStatement (ec); + + foreach (ExpressionStatement expr in targetExprs) + expr.Emit (ec); + + var ctor = MemberCache.FindMember (type, MemberFilter.Constructor (null), BindingRestriction.DeclaredOnly | BindingRestriction.InstanceOnly) as MethodSpec; + ec.Emit (OpCodes.Newobj, ctor); } public override void EmitStatement (EmitContext ec) { + if (variables != null) { + foreach (var lv in variables) { + lv?.Variable.CreateBuilder (ec); + } + } + if (instance != null) ((ExpressionStatement) source).EmitStatement (ec); @@ -549,9 +589,6 @@ namespace Mono.CSharp if (leave_copy) throw new NotImplementedException (); - foreach (var lv in variablesToInfer) - lv.CreateBuilder (ec); - EmitStatement (ec); } @@ -563,11 +600,11 @@ namespace Mono.CSharp public void SetGeneratedFieldAssigned (FlowAnalysisContext fc) { - if (variablesToInfer == null) + if (variables == null) return; - foreach (var lv in variablesToInfer) - fc.SetVariableAssigned (lv.VariableInfo); + foreach (var lv in variables) + fc.SetVariableAssigned (lv.Variable.VariableInfo); } } } \ No newline at end of file diff --git a/mcs/mcs/typespec.cs b/mcs/mcs/typespec.cs index 4627af2103..5b47547ca9 100644 --- a/mcs/mcs/typespec.cs +++ b/mcs/mcs/typespec.cs @@ -1463,6 +1463,29 @@ namespace Mono.CSharp class InternalType : TypeSpec, ITypeDefinition { + sealed class InternalTypeAssembly : IAssemblyDefinition + { + public static readonly InternalTypeAssembly Instance = new InternalTypeAssembly (); + + public string FullName => throw new NotImplementedException (); + + public bool IsCLSCompliant => false; + + public bool IsMissing => false; + + public string Name => throw new NotImplementedException (); + + public byte [] GetPublicKeyToken () + { + throw new NotImplementedException (); + } + + public bool IsFriendAssemblyTo (IAssemblyDefinition assembly) + { + return false; + } + } + public static readonly InternalType AnonymousMethod = new InternalType ("anonymous method"); public static readonly InternalType Arglist = new InternalType ("__arglist"); public static readonly InternalType MethodGroup = new InternalType ("method group"); @@ -1473,6 +1496,7 @@ namespace Mono.CSharp public static readonly InternalType VarOutType = new InternalType ("var out"); public static readonly InternalType ThrowExpr = new InternalType ("throw expression"); public static readonly InternalType DefaultType = new InternalType ("default"); + public static readonly InternalType Discard = new InternalType ("discard"); readonly string name; @@ -1497,7 +1521,7 @@ namespace Mono.CSharp IAssemblyDefinition ITypeDefinition.DeclaringAssembly { get { - throw new NotImplementedException (); + return InternalTypeAssembly.Instance; } } diff --git a/mcs/tests/gtest-409.cs b/mcs/tests/gtest-409.cs index 8db59d7e48..606ae3685d 100644 --- a/mcs/tests/gtest-409.cs +++ b/mcs/tests/gtest-409.cs @@ -1,3 +1,4 @@ +// Compiler options: -langversion:latest using System; // @@ -175,6 +176,11 @@ public class ConditionalParsing var x = args ?.2f : -.2f; } + void Test_23 (string args) + { + var x = args == null ? default : 1; + } + static void Helper (T arg) { } diff --git a/mcs/tests/known-issues-interp-net_4_x b/mcs/tests/known-issues-interp-net_4_x index b697c5e0a4..9139c646a0 100644 --- a/mcs/tests/known-issues-interp-net_4_x +++ b/mcs/tests/known-issues-interp-net_4_x @@ -15,6 +15,7 @@ gtest-etree-09.cs test-269.cs test-270.cs test-399.cs +test-404.cs test-704.cs test-811.cs test-async-17.cs diff --git a/mcs/tests/test-default-02.cs b/mcs/tests/test-default-02.cs index 225da9b6d2..0039782e5f 100644 --- a/mcs/tests/test-default-02.cs +++ b/mcs/tests/test-default-02.cs @@ -2,20 +2,32 @@ class C { - static void Main() - { - M (default, 1); + static void Main() + { + M (default, 1); - M2 (default); - M2 (null); - } + M2 (default); + M2 (null); + + var res = Test (default); + } - static void M (T x, T y) - { - } + static void M (T x, T y) + { + } + + static void M2 (params object[] x) + { + } + + static byte[] Test (S x) + { + return null; + } +} + +struct S +{ - static void M2 (params object[] x) - { - } } \ No newline at end of file diff --git a/mcs/tests/test-discards-01.cs b/mcs/tests/test-discards-01.cs new file mode 100644 index 0000000000..54cf29779c --- /dev/null +++ b/mcs/tests/test-discards-01.cs @@ -0,0 +1,36 @@ +using System; + +class X +{ + public static void Main () + { + string s = null; + + _ = 1; + { + char _ = '4'; + } + + _ = TestValue (); + + _ = _ = s; + + byte k1; + var s1 = (k1, _) = (1, s); + + Func l1 = () => _ = (_, _) = (1, s); + + TryGetValue (out _); + } + + static bool TryGetValue (out int arg) + { + arg = 3; + return true; + } + + static int TestValue () + { + return 4; + } +} \ No newline at end of file diff --git a/mcs/tests/test-tuple-09.cs b/mcs/tests/test-tuple-09.cs new file mode 100644 index 0000000000..3f15cae6cf --- /dev/null +++ b/mcs/tests/test-tuple-09.cs @@ -0,0 +1,19 @@ +using System; + +class TupleDeconstructionDeclaration +{ + public static int Main () + { + (string s, long l) = GetValues (); + (var vs, var vl) = GetValues (); + (object o, var vl2) = GetValues (); + (string ds, _) = GetValues (); + + return 0; + } + + static (string, long) GetValues () + { + return ("a", 3); + } +} \ No newline at end of file diff --git a/mcs/tests/ver-il-net_4_x.xml.REMOVED.git-id b/mcs/tests/ver-il-net_4_x.xml.REMOVED.git-id index 84c6f4981f..9db18b7cbf 100644 --- a/mcs/tests/ver-il-net_4_x.xml.REMOVED.git-id +++ b/mcs/tests/ver-il-net_4_x.xml.REMOVED.git-id @@ -1 +1 @@ -5adff6190605aca793479b15f0d101080c53ab9a \ No newline at end of file +53b3539833656acb93be1ac56ce37dd74ae46aea \ No newline at end of file diff --git a/mono/metadata/class.c.REMOVED.git-id b/mono/metadata/class.c.REMOVED.git-id index 5f605911d3..d882cb65b8 100644 --- a/mono/metadata/class.c.REMOVED.git-id +++ b/mono/metadata/class.c.REMOVED.git-id @@ -1 +1 @@ -0be27681ecbae4d86e37587094491cd49768b9f3 \ No newline at end of file +7b32f0215e2c03fa50e84914f489cb6c407c599a \ No newline at end of file diff --git a/mono/metadata/icall-def.h b/mono/metadata/icall-def.h index c7aa0b5766..0f4c52fa55 100644 --- a/mono/metadata/icall-def.h +++ b/mono/metadata/icall-def.h @@ -857,7 +857,8 @@ ICALL(SECMAN_2, "get_SecurityEnabled", ves_icall_System_Security_SecurityManager ICALL(SECMAN_3, "set_SecurityEnabled", ves_icall_System_Security_SecurityManager_set_SecurityEnabled) ICALL_TYPE(STRING, "System.String", STRING_1) -ICALL(STRING_1, ".ctor(char*)", ves_icall_System_String_ctor_RedirectToCreateString) +ICALL(STRING_1, ".ctor(System.ReadOnlySpan`1)", ves_icall_System_String_ctor_RedirectToCreateString) +ICALL(STRING_1a, ".ctor(char*)", ves_icall_System_String_ctor_RedirectToCreateString) ICALL(STRING_2, ".ctor(char*,int,int)", ves_icall_System_String_ctor_RedirectToCreateString) ICALL(STRING_3, ".ctor(char,int)", ves_icall_System_String_ctor_RedirectToCreateString) ICALL(STRING_4, ".ctor(char[])", ves_icall_System_String_ctor_RedirectToCreateString) diff --git a/mono/mini/objects.cs b/mono/mini/objects.cs index 8c251d5a70..e0cea4e8ba 100644 --- a/mono/mini/objects.cs +++ b/mono/mini/objects.cs @@ -1,6 +1,7 @@ using System; using System.Text; using System.Reflection; +using System.Collections.Generic; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; @@ -787,6 +788,11 @@ class Tests { } if (!ok) return 12; + + object arr = new int [10]; + if (arr is IList) + return 13; + return 0; } diff --git a/mono/mini/version.h b/mono/mini/version.h index 449746e7fb..d97e1befc0 100644 --- a/mono/mini/version.h +++ b/mono/mini/version.h @@ -1 +1 @@ -#define FULL_VERSION "explicit/e131c9c" +#define FULL_VERSION "explicit/f9011ff" diff --git a/mono/sgen/sgen-gchandles.c b/mono/sgen/sgen-gchandles.c index 7be066fb24..0010fc38e4 100644 --- a/mono/sgen/sgen-gchandles.c +++ b/mono/sgen/sgen-gchandles.c @@ -407,7 +407,7 @@ scan_for_weak (gpointer hidden, GCHandleType handle_type, int max_generation, gp GCObject *obj = (GCObject *)MONO_GC_REVEAL_POINTER (hidden, is_weak); /* If the object is dead we free the gc handle */ - if (!sgen_is_object_alive (obj)) + if (!sgen_is_object_alive_for_current_gen (obj)) return NULL; /* Relocate it */ @@ -421,7 +421,7 @@ scan_for_weak (gpointer hidden, GCHandleType handle_type, int max_generation, gp GCObject *field = *addr; /* if the object in the weak field is alive, we relocate it */ - if (field && sgen_is_object_alive (field)) + if (field && sgen_is_object_alive_for_current_gen (field)) ctx->ops->copy_or_mark_object (addr, ctx->queue); else *addr = NULL; diff --git a/po/mcs/de.gmo b/po/mcs/de.gmo index 13ae6ad21e..ab5e2c685a 100644 Binary files a/po/mcs/de.gmo and b/po/mcs/de.gmo differ diff --git a/po/mcs/de.po.REMOVED.git-id b/po/mcs/de.po.REMOVED.git-id index 8185d17313..8a08ab1c3b 100644 --- a/po/mcs/de.po.REMOVED.git-id +++ b/po/mcs/de.po.REMOVED.git-id @@ -1 +1 @@ -b2ba14b509031f8910c6ae655404bf3b0de120ea \ No newline at end of file +02b3109a0620a69e120719c428c56cd3f149b12d \ No newline at end of file diff --git a/po/mcs/es.gmo b/po/mcs/es.gmo index 99fc51fc68..dad6cead10 100644 Binary files a/po/mcs/es.gmo and b/po/mcs/es.gmo differ diff --git a/po/mcs/es.po.REMOVED.git-id b/po/mcs/es.po.REMOVED.git-id index b712e5e630..7d270de6bc 100644 --- a/po/mcs/es.po.REMOVED.git-id +++ b/po/mcs/es.po.REMOVED.git-id @@ -1 +1 @@ -5d270fad827ce819f2d41ce325c4c829099ea4f4 \ No newline at end of file +7f6cadf095d7630d8ddeae9112e012632d3544dd \ No newline at end of file diff --git a/po/mcs/ja.gmo b/po/mcs/ja.gmo index 5448bacaf5..71ca8ac39c 100644 Binary files a/po/mcs/ja.gmo and b/po/mcs/ja.gmo differ diff --git a/po/mcs/ja.po.REMOVED.git-id b/po/mcs/ja.po.REMOVED.git-id index 2b96993dae..777cbc34ab 100644 --- a/po/mcs/ja.po.REMOVED.git-id +++ b/po/mcs/ja.po.REMOVED.git-id @@ -1 +1 @@ -da9f42cb78d9e7d8d5f0ef93262660db42b11923 \ No newline at end of file +ae149beb3b2be30fc6bc6aa7ce4fe3a63f869ab9 \ No newline at end of file diff --git a/po/mcs/mcs.pot b/po/mcs/mcs.pot index a83006ca8f..8e2a39509d 100644 --- a/po/mcs/mcs.pot +++ b/po/mcs/mcs.pot @@ -6,9 +6,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: mono 5.10.0.53\n" +"Project-Id-Version: mono 5.10.0.69\n" "Report-Msgid-Bugs-To: http://www.mono-project.com/Bugs\n" -"POT-Creation-Date: 2018-01-28 18:45+0000\n" +"POT-Creation-Date: 2018-01-29 18:42+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -34,7 +34,7 @@ msgid "" "mismatch" msgstr "" -#: mcs/mcs/anonymous.cs:1059 mcs/mcs/ecore.cs:5941 +#: mcs/mcs/anonymous.cs:1059 mcs/mcs/ecore.cs:5948 #, csharp-format msgid "Delegate `{0}' does not take `{1}' arguments" msgstr "" @@ -482,146 +482,146 @@ msgstr "" msgid "`{0}' is obsolete: `{1}'" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:842 +#: mcs/mcs/cs-tokenizer.cs:845 msgid "" "The `partial' modifier can be used only immediately before `class', " "`struct', `interface', or `void' keyword" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:1717 mcs/mcs/cs-tokenizer.cs:1780 +#: mcs/mcs/cs-tokenizer.cs:1721 mcs/mcs/cs-tokenizer.cs:1784 msgid "Invalid number" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:1995 +#: mcs/mcs/cs-tokenizer.cs:1999 #, csharp-format msgid "Unrecognized escape sequence `\\{0}'" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:2014 +#: mcs/mcs/cs-tokenizer.cs:2018 msgid "Unrecognized escape sequence" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:2279 +#: mcs/mcs/cs-tokenizer.cs:2283 msgid "Filename, single-line comment or end-of-line expected" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:2327 +#: mcs/mcs/cs-tokenizer.cs:2331 msgid "Missing identifier to pre-processor directive" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:2337 mcs/mcs/cs-tokenizer.cs:2341 +#: mcs/mcs/cs-tokenizer.cs:2341 mcs/mcs/cs-tokenizer.cs:2345 #, csharp-format msgid "Identifier expected: {0}" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:2941 +#: mcs/mcs/cs-tokenizer.cs:2945 msgid "Integral constant is too large" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:2946 +#: mcs/mcs/cs-tokenizer.cs:2950 msgid "Invalid preprocessor directive" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:2953 +#: mcs/mcs/cs-tokenizer.cs:2957 #, csharp-format msgid "Unexpected processor directive ({0})" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:2959 +#: mcs/mcs/cs-tokenizer.cs:2963 msgid "" "Cannot define or undefine preprocessor symbols after first token in file" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:2965 +#: mcs/mcs/cs-tokenizer.cs:2969 msgid "" "Preprocessor directives must appear as the first non-whitespace character on " "a line" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:2970 +#: mcs/mcs/cs-tokenizer.cs:2974 msgid "Single-line comment or end-of-line expected" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:3015 mcs/mcs/cs-tokenizer.cs:4253 +#: mcs/mcs/cs-tokenizer.cs:3019 mcs/mcs/cs-tokenizer.cs:4257 msgid "Expected `#endif' directive" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:3048 mcs/mcs/cs-tokenizer.cs:3069 -#: mcs/mcs/cs-tokenizer.cs:3100 mcs/mcs/cs-tokenizer.cs:4251 +#: mcs/mcs/cs-tokenizer.cs:3052 mcs/mcs/cs-tokenizer.cs:3073 +#: mcs/mcs/cs-tokenizer.cs:3104 mcs/mcs/cs-tokenizer.cs:4255 msgid "#endregion directive expected" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:3149 +#: mcs/mcs/cs-tokenizer.cs:3153 msgid "Wrong preprocessor directive" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:3161 +#: mcs/mcs/cs-tokenizer.cs:3165 #, csharp-format msgid "#error: '{0}'" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:3179 +#: mcs/mcs/cs-tokenizer.cs:3183 msgid "The line number specified for #line directive is missing or invalid" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:3233 mcs/mcs/cs-tokenizer.cs:3953 +#: mcs/mcs/cs-tokenizer.cs:3237 mcs/mcs/cs-tokenizer.cs:3957 msgid "Newline in constant" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:3260 +#: mcs/mcs/cs-tokenizer.cs:3264 msgid "Unterminated string literal" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:3304 mcs/mcs/cs-tokenizer.cs:3335 +#: mcs/mcs/cs-tokenizer.cs:3308 mcs/mcs/cs-tokenizer.cs:3339 #, csharp-format msgid "Unexpected character `\\{0}'" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:3351 +#: mcs/mcs/cs-tokenizer.cs:3355 msgid "Identifier too long (limit is 512 chars)" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:3698 +#: mcs/mcs/cs-tokenizer.cs:3702 msgid "A single-line comment may not be used in an interpolated string" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:3768 +#: mcs/mcs/cs-tokenizer.cs:3772 msgid "End-of-file found, '*/' expected" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:3865 +#: mcs/mcs/cs-tokenizer.cs:3869 msgid "Missing close delimiter `}' for interpolated expression" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:3886 +#: mcs/mcs/cs-tokenizer.cs:3890 msgid "Keyword, identifier, or string expected after verbatim specifier: @" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:3923 +#: mcs/mcs/cs-tokenizer.cs:3927 #, csharp-format msgid "Unexpected character `{0}'" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:3948 +#: mcs/mcs/cs-tokenizer.cs:3952 msgid "Empty character literal" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:3969 +#: mcs/mcs/cs-tokenizer.cs:3973 msgid "Too many characters in character literal" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:4079 mcs/mcs/cs-tokenizer.cs:4142 +#: mcs/mcs/cs-tokenizer.cs:4083 mcs/mcs/cs-tokenizer.cs:4146 #, csharp-format msgid "" "A `{0}' character may only be escaped by doubling `{0}{0}' in an " "interpolated string" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:4120 +#: mcs/mcs/cs-tokenizer.cs:4124 msgid "Empty interpolated expression format specifier" msgstr "" -#: mcs/mcs/cs-tokenizer.cs:4122 +#: mcs/mcs/cs-tokenizer.cs:4126 msgid "" "A interpolated expression format specifier may not contain trailing " "whitespace" @@ -1394,7 +1394,7 @@ msgid "" "(are you missing a cast?)" msgstr "" -#: mcs/mcs/ecore.cs:332 mcs/mcs/ecore.cs:4184 +#: mcs/mcs/ecore.cs:332 mcs/mcs/ecore.cs:4191 #, csharp-format msgid "Cannot implicitly convert type `{0}' to `{1}'" msgstr "" @@ -1428,8 +1428,8 @@ msgstr "" msgid "Internal compiler error: {0}" msgstr "" -#: mcs/mcs/ecore.cs:605 mcs/mcs/expression.cs:1932 mcs/mcs/expression.cs:8149 -#: mcs/mcs/expression.cs:8157 +#: mcs/mcs/ecore.cs:605 mcs/mcs/expression.cs:1932 mcs/mcs/expression.cs:8146 +#: mcs/mcs/expression.cs:8154 msgid "A constant value is expected" msgstr "" @@ -1511,160 +1511,160 @@ msgid "" "renaming the local variable when it hides the member `{1}'" msgstr "" -#: mcs/mcs/ecore.cs:2962 +#: mcs/mcs/ecore.cs:2969 #, csharp-format msgid "A local variable `{0}' cannot be used before it is declared" msgstr "" -#: mcs/mcs/ecore.cs:3115 +#: mcs/mcs/ecore.cs:3122 msgid "System.Void cannot be used from C#. Consider using `void'" msgstr "" -#: mcs/mcs/ecore.cs:3277 +#: mcs/mcs/ecore.cs:3284 #, csharp-format msgid "" "The type or namespace name `{0}' could not be found in the global namespace. " "Are you missing {1} assembly reference?" msgstr "" -#: mcs/mcs/ecore.cs:3281 +#: mcs/mcs/ecore.cs:3288 #, csharp-format msgid "" "The type or namespace name `{0}' does not exist in the namespace `{1}'. Are " "you missing {2} assembly reference?" msgstr "" -#: mcs/mcs/ecore.cs:3537 +#: mcs/mcs/ecore.cs:3544 #, csharp-format msgid "Cannot call an abstract base member `{0}'" msgstr "" -#: mcs/mcs/ecore.cs:3544 +#: mcs/mcs/ecore.cs:3551 #, csharp-format msgid "" "Cannot access protected member `{0}' via a qualifier of type `{1}'. The " "qualifier must be of type `{2}' or derived from it" msgstr "" -#: mcs/mcs/ecore.cs:3577 +#: mcs/mcs/ecore.cs:3584 #, csharp-format msgid "" "Fields of static readonly field `{0}' cannot be assigned to (except in a " "static constructor or a variable initializer)" msgstr "" -#: mcs/mcs/ecore.cs:3580 +#: mcs/mcs/ecore.cs:3587 #, csharp-format msgid "" "Members of readonly field `{0}' cannot be modified (except in a constructor " "or a variable initializer)" msgstr "" -#: mcs/mcs/ecore.cs:3589 +#: mcs/mcs/ecore.cs:3596 #, csharp-format msgid "" "Members of value type `{0}' cannot be assigned using a property `{1}' object " "initializer" msgstr "" -#: mcs/mcs/ecore.cs:3593 +#: mcs/mcs/ecore.cs:3600 #, csharp-format msgid "" "Cannot modify a value type return value of `{0}'. Consider storing the value " "in a temporary variable" msgstr "" -#: mcs/mcs/ecore.cs:3606 +#: mcs/mcs/ecore.cs:3613 #, csharp-format msgid "Cannot assign to members of `{0}' because it is a `{1}'" msgstr "" -#: mcs/mcs/ecore.cs:3629 +#: mcs/mcs/ecore.cs:3636 #, csharp-format msgid "" "Static member `{0}' cannot be accessed with an instance reference, qualify " "it with a type name instead" msgstr "" -#: mcs/mcs/ecore.cs:3644 +#: mcs/mcs/ecore.cs:3651 #, csharp-format msgid "" "A field initializer cannot reference the nonstatic field, method, or " "property `{0}'" msgstr "" -#: mcs/mcs/ecore.cs:3650 +#: mcs/mcs/ecore.cs:3657 msgid "Constructor initializer cannot access primary constructor parameters" msgstr "" -#: mcs/mcs/ecore.cs:3652 +#: mcs/mcs/ecore.cs:3659 #, csharp-format msgid "" "An object reference is required to access primary constructor parameter `{0}'" msgstr "" -#: mcs/mcs/ecore.cs:3657 +#: mcs/mcs/ecore.cs:3664 #, csharp-format msgid "An object reference is required to access non-static member `{0}'" msgstr "" -#: mcs/mcs/ecore.cs:3668 +#: mcs/mcs/ecore.cs:3675 #, csharp-format msgid "" "Cannot access a nonstatic member of outer type `{0}' via nested type `{1}'" msgstr "" -#: mcs/mcs/ecore.cs:3696 +#: mcs/mcs/ecore.cs:3703 msgid "Cannot modify the result of an unboxing conversion" msgstr "" -#: mcs/mcs/ecore.cs:3825 +#: mcs/mcs/ecore.cs:3832 msgid "An argument to nameof operator cannot be extension method group" msgstr "" -#: mcs/mcs/ecore.cs:3883 +#: mcs/mcs/ecore.cs:3890 #, csharp-format msgid "" "Type `{0}' does not contain a member `{1}' and the best extension method " "overload `{2}' requires an instance of type `{3}'" msgstr "" -#: mcs/mcs/ecore.cs:3887 +#: mcs/mcs/ecore.cs:3894 #, csharp-format msgid "" "Type `{0}' does not contain a member `{1}' and the best extension method " "overload `{2}' has some invalid arguments" msgstr "" -#: mcs/mcs/ecore.cs:4035 +#: mcs/mcs/ecore.cs:4042 msgid "An expression tree cannot contain an expression with method group" msgstr "" -#: mcs/mcs/ecore.cs:4041 +#: mcs/mcs/ecore.cs:4048 msgid "" "Partial methods with only a defining declaration or removed conditional " "methods cannot be used in an expression tree" msgstr "" -#: mcs/mcs/ecore.cs:4100 +#: mcs/mcs/ecore.cs:4107 #, csharp-format msgid "" "Cannot convert method group `{0}' to non-delegate type `{1}'. Consider using " "parentheses to invoke the method" msgstr "" -#: mcs/mcs/ecore.cs:4245 +#: mcs/mcs/ecore.cs:4252 msgid "" "An argument to nameof operator cannot be method group with type arguments" msgstr "" -#: mcs/mcs/ecore.cs:5004 +#: mcs/mcs/ecore.cs:5011 #, csharp-format msgid "" "The type `{0}' does not contain a constructor that takes `{1}' arguments" msgstr "" -#: mcs/mcs/ecore.cs:5725 +#: mcs/mcs/ecore.cs:5732 #, csharp-format msgid "" "Type `{0}' does not contain a member `{1}' and the best extension method " @@ -1672,212 +1672,212 @@ msgid "" "without the extension method syntax" msgstr "" -#: mcs/mcs/ecore.cs:5764 +#: mcs/mcs/ecore.cs:5771 #, csharp-format msgid "" "The call is ambiguous between the following methods or properties: `{0}' and " "`{1}'" msgstr "" -#: mcs/mcs/ecore.cs:5825 +#: mcs/mcs/ecore.cs:5832 #, csharp-format msgid "" "The best overloaded collection initalizer method `{0}' cannot have `ref' or " "`out' modifier" msgstr "" -#: mcs/mcs/ecore.cs:5829 +#: mcs/mcs/ecore.cs:5836 #, csharp-format msgid "" "The best overloaded collection initalizer method `{0}' has some invalid " "arguments" msgstr "" -#: mcs/mcs/ecore.cs:5832 +#: mcs/mcs/ecore.cs:5839 #, csharp-format msgid "Delegate `{0}' has some invalid arguments" msgstr "" -#: mcs/mcs/ecore.cs:5836 +#: mcs/mcs/ecore.cs:5843 #, csharp-format msgid "The best overloaded method match for `{0}' has some invalid arguments" msgstr "" -#: mcs/mcs/ecore.cs:5845 +#: mcs/mcs/ecore.cs:5852 #, csharp-format msgid "" "Argument `#{0}' does not require `{1}' modifier. Consider removing `{1}' " "modifier" msgstr "" -#: mcs/mcs/ecore.cs:5848 +#: mcs/mcs/ecore.cs:5855 #, csharp-format msgid "Argument `#{0}' is missing `{1}' modifier" msgstr "" -#: mcs/mcs/ecore.cs:5865 +#: mcs/mcs/ecore.cs:5872 #, csharp-format msgid "Argument `#{0}' cannot convert `{1}' expression to type `{2}'" msgstr "" -#: mcs/mcs/ecore.cs:5917 +#: mcs/mcs/ecore.cs:5924 #, csharp-format msgid "" "The type arguments for method `{0}' cannot be inferred from the usage. Try " "specifying the type arguments explicitly" msgstr "" -#: mcs/mcs/ecore.cs:5946 +#: mcs/mcs/ecore.cs:5953 #, csharp-format msgid "No overload for method `{0}' takes `{1}' arguments" msgstr "" -#: mcs/mcs/ecore.cs:6057 +#: mcs/mcs/ecore.cs:6069 #, csharp-format msgid "The delegate `{0}' does not contain a parameter named `{1}'" msgstr "" -#: mcs/mcs/ecore.cs:6062 +#: mcs/mcs/ecore.cs:6074 #, csharp-format msgid "" "The best overloaded method match for `{0}' does not contain a parameter " "named `{1}'" msgstr "" -#: mcs/mcs/ecore.cs:6073 +#: mcs/mcs/ecore.cs:6085 #, csharp-format msgid "" "Named argument `{0}' is used out of position but is followed by positional " "argument" msgstr "" -#: mcs/mcs/ecore.cs:6077 +#: mcs/mcs/ecore.cs:6089 #, csharp-format msgid "" "Named argument `{0}' cannot be used for a parameter which has positional " "argument specified" msgstr "" -#: mcs/mcs/ecore.cs:6462 +#: mcs/mcs/ecore.cs:6474 msgid "" "You cannot use fixed size buffers contained in unfixed expressions. Try " "using the fixed statement" msgstr "" -#: mcs/mcs/ecore.cs:6467 +#: mcs/mcs/ecore.cs:6479 #, csharp-format msgid "`{0}': Fixed size buffers can only be accessed through locals or fields" msgstr "" -#: mcs/mcs/ecore.cs:6552 +#: mcs/mcs/ecore.cs:6564 #, csharp-format msgid "" "A static readonly field `{0}' cannot be passed ref or out (except in a " "static constructor)" msgstr "" -#: mcs/mcs/ecore.cs:6555 +#: mcs/mcs/ecore.cs:6567 #, csharp-format msgid "" "A readonly field `{0}' cannot be passed ref or out (except in a constructor)" msgstr "" -#: mcs/mcs/ecore.cs:6569 +#: mcs/mcs/ecore.cs:6581 #, csharp-format msgid "" "Fields of static readonly field `{0}' cannot be passed ref or out (except in " "a static constructor)" msgstr "" -#: mcs/mcs/ecore.cs:6572 +#: mcs/mcs/ecore.cs:6584 #, csharp-format msgid "" "Members of readonly field `{0}' cannot be passed ref or out (except in a " "constructor)" msgstr "" -#: mcs/mcs/ecore.cs:6579 +#: mcs/mcs/ecore.cs:6591 #, csharp-format msgid "" "A static readonly field `{0}' cannot be assigned to (except in a static " "constructor or a variable initializer)" msgstr "" -#: mcs/mcs/ecore.cs:6582 +#: mcs/mcs/ecore.cs:6594 #, csharp-format msgid "" "A readonly field `{0}' cannot be assigned to (except in a constructor or a " "variable initializer)" msgstr "" -#: mcs/mcs/ecore.cs:6649 +#: mcs/mcs/ecore.cs:6661 #, csharp-format msgid "Use of possibly unassigned field `{0}'" msgstr "" -#: mcs/mcs/ecore.cs:7065 +#: mcs/mcs/ecore.cs:7077 #, csharp-format msgid "Property or event `{0}' is not supported by the C# language" msgstr "" -#: mcs/mcs/ecore.cs:7185 +#: mcs/mcs/ecore.cs:7197 #, csharp-format msgid "Use of possibly unassigned auto-implemented property `{0}'" msgstr "" -#: mcs/mcs/ecore.cs:7389 +#: mcs/mcs/ecore.cs:7401 #, csharp-format msgid "A range variable `{0}' may not be passes as `ref' or `out' parameter" msgstr "" -#: mcs/mcs/ecore.cs:7417 +#: mcs/mcs/ecore.cs:7429 #, csharp-format msgid "Property or indexer `{0}' cannot be assigned to (it is read-only)" msgstr "" -#: mcs/mcs/ecore.cs:7425 +#: mcs/mcs/ecore.cs:7437 #, csharp-format msgid "" "The property or indexer `{0}' cannot be used in this context because the set " "accessor is inaccessible" msgstr "" -#: mcs/mcs/ecore.cs:7505 +#: mcs/mcs/ecore.cs:7517 #, csharp-format msgid "" "The property or indexer `{0}' cannot be used in this context because it " "lacks the `get' accessor" msgstr "" -#: mcs/mcs/ecore.cs:7512 +#: mcs/mcs/ecore.cs:7524 #, csharp-format msgid "" "The property or indexer `{0}' cannot be used in this context because the get " "accessor is inaccessible" msgstr "" -#: mcs/mcs/ecore.cs:7694 +#: mcs/mcs/ecore.cs:7706 #, csharp-format msgid "" "The event `{0}' can only appear on the left hand side of `+=' or `-=' " "operator" msgstr "" -#: mcs/mcs/ecore.cs:7698 +#: mcs/mcs/ecore.cs:7710 #, csharp-format msgid "" "The event `{0}' can only appear on the left hand side of += or -= when used " "outside of the type `{1}'" msgstr "" -#: mcs/mcs/ecore.cs:7876 +#: mcs/mcs/ecore.cs:7888 #, csharp-format msgid "" "An implicitly typed local variable declaration cannot be initialized with " "`{0}'" msgstr "" -#: mcs/mcs/ecore.cs:7891 +#: mcs/mcs/ecore.cs:7903 msgid "" "The contextual keyword `var' may only appear within a local variable " "declaration" @@ -2012,20 +2012,20 @@ msgid "" "The `default value' operator cannot be applied to an operand of a static type" msgstr "" -#: mcs/mcs/expression.cs:3468 -#, csharp-format -msgid "Operator `{0}' cannot be applied to operand `default'" -msgstr "" - -#: mcs/mcs/expression.cs:3472 +#: mcs/mcs/expression.cs:3467 #, csharp-format msgid "Operator `{0}' cannot be applied to operands of type `{1}' and `{2}'" msgstr "" -#: mcs/mcs/expression.cs:4279 +#: mcs/mcs/expression.cs:4274 msgid "To cast a negative value, you must enclose the value in parentheses" msgstr "" +#: mcs/mcs/expression.cs:4323 +#, csharp-format +msgid "Operator `{0}' cannot be applied to operand `default'" +msgstr "" + #: mcs/mcs/expression.cs:4328 #, csharp-format msgid "Operator `{0}' is ambiguous on operands `default' and `default'" @@ -2102,170 +2102,170 @@ msgstr "" msgid "Use of unassigned out parameter `{0}'" msgstr "" -#: mcs/mcs/expression.cs:7216 +#: mcs/mcs/expression.cs:7215 msgid "The syntax `var (...)' as an lvalue is reserved" msgstr "" -#: mcs/mcs/expression.cs:7299 +#: mcs/mcs/expression.cs:7296 #, csharp-format msgid "Cannot invoke a non-delegate type `{0}'" msgstr "" -#: mcs/mcs/expression.cs:7310 +#: mcs/mcs/expression.cs:7307 #, csharp-format msgid "The member `{0}' cannot be used as method or delegate" msgstr "" -#: mcs/mcs/expression.cs:7332 +#: mcs/mcs/expression.cs:7329 msgid "" "Do not directly call your base class Finalize method. It is called " "automatically from your destructor" msgstr "" -#: mcs/mcs/expression.cs:7334 +#: mcs/mcs/expression.cs:7331 msgid "" "Destructors and object.Finalize cannot be called directly. Consider calling " "IDisposable.Dispose if available" msgstr "" -#: mcs/mcs/expression.cs:7367 +#: mcs/mcs/expression.cs:7364 #, csharp-format msgid "" "The base call to method `{0}' cannot be dynamically dispatched. Consider " "casting the dynamic arguments or eliminating the base access" msgstr "" -#: mcs/mcs/expression.cs:7462 +#: mcs/mcs/expression.cs:7459 #, csharp-format msgid "`{0}': cannot explicitly call operator or accessor" msgstr "" -#: mcs/mcs/expression.cs:7653 +#: mcs/mcs/expression.cs:7650 msgid "" "Tuple type cannot be used in an object creation expression. Use a tuple " "literal expression instead." msgstr "" -#: mcs/mcs/expression.cs:7663 +#: mcs/mcs/expression.cs:7660 #, csharp-format msgid "Unsafe type `{0}' cannot be used in an object creation expression" msgstr "" -#: mcs/mcs/expression.cs:7686 +#: mcs/mcs/expression.cs:7683 #, csharp-format msgid "" "Cannot create an instance of the variable type `{0}' because it does not " "have the new() constraint" msgstr "" -#: mcs/mcs/expression.cs:7692 +#: mcs/mcs/expression.cs:7689 #, csharp-format msgid "" "`{0}': cannot provide arguments when creating an instance of a variable type" msgstr "" -#: mcs/mcs/expression.cs:7701 +#: mcs/mcs/expression.cs:7698 #, csharp-format msgid "Cannot create an instance of the static class `{0}'" msgstr "" -#: mcs/mcs/expression.cs:7713 +#: mcs/mcs/expression.cs:7710 #, csharp-format msgid "Cannot create an instance of the abstract class or interface `{0}'" msgstr "" -#: mcs/mcs/expression.cs:7998 +#: mcs/mcs/expression.cs:7995 msgid "" "An implicitly typed local variable declarator cannot use an array initializer" msgstr "" -#: mcs/mcs/expression.cs:8163 mcs/mcs/expression.cs:8188 +#: mcs/mcs/expression.cs:8160 mcs/mcs/expression.cs:8185 #, csharp-format msgid "An array initializer of length `{0}' was expected" msgstr "" -#: mcs/mcs/expression.cs:8179 +#: mcs/mcs/expression.cs:8176 msgid "" "Array initializers can only be used in a variable or field initializer. Try " "using a new expression instead" msgstr "" -#: mcs/mcs/expression.cs:8196 +#: mcs/mcs/expression.cs:8193 msgid "A nested array initializer was expected" msgstr "" -#: mcs/mcs/expression.cs:8243 +#: mcs/mcs/expression.cs:8240 msgid "An expression tree cannot contain a multidimensional array initializer" msgstr "" -#: mcs/mcs/expression.cs:8279 +#: mcs/mcs/expression.cs:8276 msgid "Cannot create an array with a negative size" msgstr "" -#: mcs/mcs/expression.cs:8381 +#: mcs/mcs/expression.cs:8378 msgid "" "Can only use array initializer expressions to assign to array types. Try " "using a new expression instead" msgstr "" -#: mcs/mcs/expression.cs:8822 +#: mcs/mcs/expression.cs:8819 msgid "" "The type of an implicitly typed array cannot be inferred from the " "initializer. Try specifying array type explicitly" msgstr "" -#: mcs/mcs/expression.cs:8977 +#: mcs/mcs/expression.cs:8974 msgid "" "The `this' object cannot be used before all of its fields are assigned to" msgstr "" -#: mcs/mcs/expression.cs:8983 +#: mcs/mcs/expression.cs:8980 msgid "" "Keyword `this' is not valid in a static property, static method, or static " "field initializer" msgstr "" -#: mcs/mcs/expression.cs:8986 +#: mcs/mcs/expression.cs:8983 msgid "" "Anonymous methods inside structs cannot access instance members of `this'. " "Consider copying `this' to a local variable outside the anonymous method and " "using the local instead" msgstr "" -#: mcs/mcs/expression.cs:8989 +#: mcs/mcs/expression.cs:8986 msgid "Keyword `this' is not available in the current context" msgstr "" -#: mcs/mcs/expression.cs:9065 +#: mcs/mcs/expression.cs:9062 msgid "Cannot take the address of `this' because it is read-only" msgstr "" -#: mcs/mcs/expression.cs:9067 +#: mcs/mcs/expression.cs:9064 msgid "Cannot pass `this' as a ref or out argument because it is read-only" msgstr "" -#: mcs/mcs/expression.cs:9069 +#: mcs/mcs/expression.cs:9066 msgid "Cannot assign to `this' because it is read-only" msgstr "" -#: mcs/mcs/expression.cs:9137 +#: mcs/mcs/expression.cs:9134 msgid "The __arglist construct is valid only within a variable argument method" msgstr "" -#: mcs/mcs/expression.cs:9198 +#: mcs/mcs/expression.cs:9195 msgid "An expression tree cannot contain a method with variable arguments" msgstr "" -#: mcs/mcs/expression.cs:9472 +#: mcs/mcs/expression.cs:9469 msgid "The typeof operator cannot be used on the dynamic type" msgstr "" -#: mcs/mcs/expression.cs:9513 +#: mcs/mcs/expression.cs:9510 #, csharp-format msgid "`{0}': an attribute argument cannot use type parameters" msgstr "" -#: mcs/mcs/expression.cs:9728 +#: mcs/mcs/expression.cs:9725 #, csharp-format msgid "" "`{0}' does not have a predefined size, therefore sizeof can only be used in " @@ -2273,194 +2273,206 @@ msgid "" "SizeOf)" msgstr "" -#: mcs/mcs/expression.cs:9793 +#: mcs/mcs/expression.cs:9790 #, csharp-format msgid "Alias `{0}' not found" msgstr "" -#: mcs/mcs/expression.cs:9834 +#: mcs/mcs/expression.cs:9831 msgid "" "The namespace alias qualifier `::' cannot be used to invoke a method. " "Consider using `.' instead" msgstr "" -#: mcs/mcs/expression.cs:9924 +#: mcs/mcs/expression.cs:9921 msgid "Cannot perform member binding on `null' value" msgstr "" -#: mcs/mcs/expression.cs:10091 +#: mcs/mcs/expression.cs:10088 #, csharp-format msgid "" "`{0}': cannot reference a type through an expression. Consider using `{1}' " "instead" msgstr "" -#: mcs/mcs/expression.cs:10170 +#: mcs/mcs/expression.cs:10167 #, csharp-format msgid "A nested type cannot be specified through a type parameter `{0}'" msgstr "" -#: mcs/mcs/expression.cs:10178 +#: mcs/mcs/expression.cs:10175 #, csharp-format msgid "" "Alias `{0}' cannot be used with `::' since it denotes a type. Consider " "replacing `::' with `.'" msgstr "" -#: mcs/mcs/expression.cs:10247 +#: mcs/mcs/expression.cs:10244 #, csharp-format msgid "The nested type `{0}' does not exist in the type `{1}'" msgstr "" -#: mcs/mcs/expression.cs:10271 +#: mcs/mcs/expression.cs:10268 #, csharp-format msgid "" "Type `{0}' does not contain a definition for `{1}' and no extension method " "`{1}' of type `{0}' could be found. Are you missing {2}?" msgstr "" -#: mcs/mcs/expression.cs:10563 +#: mcs/mcs/expression.cs:10560 #, csharp-format msgid "Cannot apply indexing with [] to an expression of type `{0}'" msgstr "" -#: mcs/mcs/expression.cs:10700 +#: mcs/mcs/expression.cs:10697 #, csharp-format msgid "Wrong number of indexes `{0}' inside [], expected `{1}'" msgstr "" -#: mcs/mcs/expression.cs:11138 +#: mcs/mcs/expression.cs:11135 msgid "" "The indexer base access cannot be dynamically dispatched. Consider casting " "the dynamic arguments or eliminating the base access" msgstr "" -#: mcs/mcs/expression.cs:11237 +#: mcs/mcs/expression.cs:11234 msgid "An expression tree may not contain a base access" msgstr "" -#: mcs/mcs/expression.cs:11255 +#: mcs/mcs/expression.cs:11252 msgid "Keyword `base' is not available in a static method" msgstr "" -#: mcs/mcs/expression.cs:11257 +#: mcs/mcs/expression.cs:11254 msgid "Keyword `base' is not available in the current context" msgstr "" -#: mcs/mcs/expression.cs:11295 +#: mcs/mcs/expression.cs:11292 msgid "" "A property, indexer or dynamic member access may not be passed as `ref' or " "`out' parameter" msgstr "" -#: mcs/mcs/expression.cs:11644 +#: mcs/mcs/expression.cs:11641 #, csharp-format msgid "Array elements cannot be of type `{0}'" msgstr "" -#: mcs/mcs/expression.cs:11647 +#: mcs/mcs/expression.cs:11644 #, csharp-format msgid "Array elements cannot be of static type `{0}'" msgstr "" -#: mcs/mcs/expression.cs:11856 +#: mcs/mcs/expression.cs:11853 msgid "Cannot use a negative size with stackalloc" msgstr "" -#: mcs/mcs/expression.cs:11860 +#: mcs/mcs/expression.cs:11857 msgid "Cannot use stackalloc in finally or catch" msgstr "" -#: mcs/mcs/expression.cs:12019 +#: mcs/mcs/expression.cs:12016 #, csharp-format msgid "" "Member `{0}' cannot be initialized. An object initializer may only be used " "for fields, or properties" msgstr "" -#: mcs/mcs/expression.cs:12027 +#: mcs/mcs/expression.cs:12024 #, csharp-format msgid "" "Static field or property `{0}' cannot be assigned in an object initializer" msgstr "" -#: mcs/mcs/expression.cs:12098 +#: mcs/mcs/expression.cs:12095 msgid "" "An expression tree cannot contain a collection initializer with extension " "method" msgstr "" -#: mcs/mcs/expression.cs:12136 +#: mcs/mcs/expression.cs:12133 msgid "Expression tree cannot contain a dictionary initializer" msgstr "" -#: mcs/mcs/expression.cs:12261 +#: mcs/mcs/expression.cs:12258 #, csharp-format msgid "" "A field or property `{0}' cannot be initialized with a collection object " "initializer because type `{1}' does not implement `{2}' interface" msgstr "" -#: mcs/mcs/expression.cs:12272 +#: mcs/mcs/expression.cs:12269 #, csharp-format msgid "Inconsistent `{0}' member declaration" msgstr "" -#: mcs/mcs/expression.cs:12280 +#: mcs/mcs/expression.cs:12277 #, csharp-format msgid "" "An object initializer includes more than one member `{0}' initialization" msgstr "" -#: mcs/mcs/expression.cs:12298 +#: mcs/mcs/expression.cs:12295 #, csharp-format msgid "Cannot initialize object of type `{0}' with a collection initializer" msgstr "" -#: mcs/mcs/expression.cs:12443 +#: mcs/mcs/expression.cs:12440 msgid "" "Object and collection initializers cannot be used to instantiate a delegate" msgstr "" -#: mcs/mcs/expression.cs:12662 +#: mcs/mcs/expression.cs:12659 msgid "Anonymous types cannot be used in this expression" msgstr "" -#: mcs/mcs/expression.cs:12756 +#: mcs/mcs/expression.cs:12753 #, csharp-format msgid "An anonymous type property `{0}' cannot be initialized with `{1}'" msgstr "" -#: mcs/mcs/expression.cs:12996 +#: mcs/mcs/expression.cs:12993 msgid "An expression tree cannot not contain a throw expression" msgstr "" -#: mcs/mcs/expression.cs:13067 +#: mcs/mcs/expression.cs:13064 msgid "" "An expression cannot be used in this context because it may not be returned " "by reference" msgstr "" -#: mcs/mcs/expression.cs:13092 +#: mcs/mcs/expression.cs:13089 #, csharp-format msgid "" "The expression must be of type `{0}' because it is being assigned by " "reference" msgstr "" -#: mcs/mcs/expression.cs:13124 +#: mcs/mcs/expression.cs:13121 msgid "" "An expression tree lambda cannot contain a call to a method, property, or " "indexer that returns by reference" msgstr "" -#: mcs/mcs/expression.cs:13169 +#: mcs/mcs/expression.cs:13166 #, csharp-format msgid "" "`await' cannot be used in an expression containing a call to `{0}' because " "it returns by reference" msgstr "" +#: mcs/mcs/expression.cs:13221 +msgid "An expression tree cannot contain a discard" +msgstr "" + +#: mcs/mcs/expression.cs:13235 +msgid "Cannot infer the type of implicitly-typed discard" +msgstr "" + +#: mcs/mcs/expression.cs:13241 +msgid "Cannot assign void to a discard" +msgstr "" + #: mcs/mcs/field.cs:74 msgid "" "The modifier 'abstract' is not valid on fields. Try using a property instead" @@ -2875,69 +2887,69 @@ msgid "" "suffix `{1}' to create a literal of this type" msgstr "" -#: mcs/mcs/membercache.cs:1489 +#: mcs/mcs/membercache.cs:1491 msgid "" "A partial method declaration and partial method implementation cannot differ " "on use of `params' modifier" msgstr "" -#: mcs/mcs/membercache.cs:1492 +#: mcs/mcs/membercache.cs:1494 msgid "" "A partial method declaration and partial method implementation must be both " "an extension method or neither" msgstr "" -#: mcs/mcs/membercache.cs:1496 +#: mcs/mcs/membercache.cs:1498 #, csharp-format msgid "" "Overloaded contructor `{0}' cannot differ on use of parameter modifiers only" msgstr "" -#: mcs/mcs/membercache.cs:1500 +#: mcs/mcs/membercache.cs:1502 #, csharp-format msgid "" "Overloaded method `{0}' cannot differ on use of parameter modifiers only" msgstr "" -#: mcs/mcs/membercache.cs:1509 +#: mcs/mcs/membercache.cs:1511 msgid "" "A partial method declaration and partial method implementation must both use " "the same tuple element names" msgstr "" -#: mcs/mcs/membercache.cs:1538 +#: mcs/mcs/membercache.cs:1540 msgid "" "A partial method declaration and partial method implementation must be both " "`static' or neither" msgstr "" -#: mcs/mcs/membercache.cs:1544 +#: mcs/mcs/membercache.cs:1546 msgid "" "A partial method declaration and partial method implementation must be both " "`unsafe' or neither" msgstr "" -#: mcs/mcs/membercache.cs:1552 +#: mcs/mcs/membercache.cs:1554 #, csharp-format msgid "A partial method `{0}' declaration is already defined" msgstr "" -#: mcs/mcs/membercache.cs:1556 +#: mcs/mcs/membercache.cs:1558 #, csharp-format msgid "A partial method `{0}' implementation is already defined" msgstr "" -#: mcs/mcs/membercache.cs:1567 mcs/mcs/property.cs:81 +#: mcs/mcs/membercache.cs:1569 mcs/mcs/property.cs:81 #, csharp-format msgid "A member `{0}' is already reserved" msgstr "" -#: mcs/mcs/membercache.cs:1578 +#: mcs/mcs/membercache.cs:1580 #, csharp-format msgid "Duplicate user-defined conversion in type `{0}'" msgstr "" -#: mcs/mcs/membercache.cs:1584 +#: mcs/mcs/membercache.cs:1586 #, csharp-format msgid "" "A member `{0}' is already defined. Rename this member or use different " diff --git a/po/mcs/pt_BR.gmo b/po/mcs/pt_BR.gmo index 5fa129d264..703668eb38 100644 Binary files a/po/mcs/pt_BR.gmo and b/po/mcs/pt_BR.gmo differ diff --git a/po/mcs/pt_BR.po.REMOVED.git-id b/po/mcs/pt_BR.po.REMOVED.git-id index c2c0813bec..577ef4e0e9 100644 --- a/po/mcs/pt_BR.po.REMOVED.git-id +++ b/po/mcs/pt_BR.po.REMOVED.git-id @@ -1 +1 @@ -9a234da93ccd638d8dff3a2c834df336c0f0f5d1 \ No newline at end of file +b77f75d96ef033158cd1d1e10a7b3ae15ac3c526 \ No newline at end of file